From afbe5bd3ff31a58199d523e502ab1c1a3ed7c0fb Mon Sep 17 00:00:00 2001 From: Baudouin Chauviere Date: Sun, 9 Dec 2018 15:45:09 -0700 Subject: [PATCH] need abc_with_bb_support for ace compilation --- Makefile | 3 +- abc_with_bb_support/FOR_CYGWIN/libSupport.c | 193 + .../JAMIESON_TESTS/logsyn_techmap4.script | 8 + .../logsyn_techmap4_mini.script | 17 + .../JAMIESON_TESTS/lut_lib4.txt | 6 + .../JAMIESON_TESTS/mini_example1.4.blif | 79 + .../JAMIESON_TESTS/mini_example1.blif | 56 + .../JAMIESON_TESTS/mini_example2.4.blif | 62 + .../JAMIESON_TESTS/mini_example2.blif | 40 + .../JAMIESON_TESTS/sample.blif | 11 + .../JAMIESON_TESTS/sample2.blif | 24 + .../JAMIESON_TESTS/sample3.blif | 13 + .../JAMIESON_TESTS/sample4.blif | 19 + abc_with_bb_support/Makefile | 82 + abc_with_bb_support/abc.dsp | 2819 ++++ abc_with_bb_support/abc.dsw | 29 + abc_with_bb_support/abc.ncb | Bin 0 -> 5114880 bytes abc_with_bb_support/abc.rc | 174 + abc_with_bb_support/abc.suo | Bin 0 -> 4608 bytes abc_with_bb_support/abc.vcproj | 12366 ++++++++++++++++ abc_with_bb_support/abclib.dsp | 2336 +++ abc_with_bb_support/abclib.dsw | 29 + abc_with_bb_support/abctestlib.dsp | 102 + abc_with_bb_support/abctestlib.dsw | 29 + abc_with_bb_support/accum.blif | 88 + abc_with_bb_support/accum.v | 16 + abc_with_bb_support/copyright.txt | 18 + abc_with_bb_support/demo.c | 181 + abc_with_bb_support/depends.sh | 13 + abc_with_bb_support/readme | 26 + abc_with_bb_support/regtest.script | 18 + abc_with_bb_support/regtest_output.txt | 96 + abc_with_bb_support/simple.blif | 29 + abc_with_bb_support/simple.v | 23 + abc_with_bb_support/src/aig/aig/aig.h | 498 + abc_with_bb_support/src/aig/aig/aigCheck.c | 162 + abc_with_bb_support/src/aig/aig/aigDfs.c | 631 + abc_with_bb_support/src/aig/aig/aigFanout.c | 189 + abc_with_bb_support/src/aig/aig/aigMan.c | 414 + abc_with_bb_support/src/aig/aig/aigMem.c | 598 + abc_with_bb_support/src/aig/aig/aigMffc.c | 297 + abc_with_bb_support/src/aig/aig/aigObj.c | 416 + abc_with_bb_support/src/aig/aig/aigOper.c | 449 + abc_with_bb_support/src/aig/aig/aigOrder.c | 171 + abc_with_bb_support/src/aig/aig/aigPart.c | 928 ++ abc_with_bb_support/src/aig/aig/aigRepr.c | 414 + abc_with_bb_support/src/aig/aig/aigSeq.c | 500 + abc_with_bb_support/src/aig/aig/aigShow.c | 356 + abc_with_bb_support/src/aig/aig/aigTable.c | 269 + abc_with_bb_support/src/aig/aig/aigTiming.c | 351 + abc_with_bb_support/src/aig/aig/aigTruth.c | 98 + abc_with_bb_support/src/aig/aig/aigUtil.c | 732 + abc_with_bb_support/src/aig/aig/aigWin.c | 184 + abc_with_bb_support/src/aig/aig/aig_.c | 48 + abc_with_bb_support/src/aig/aig/module.make | 17 + abc_with_bb_support/src/aig/bdc/bdc.h | 73 + abc_with_bb_support/src/aig/bdc/bdcCore.c | 189 + abc_with_bb_support/src/aig/bdc/bdcDec.c | 461 + abc_with_bb_support/src/aig/bdc/bdcInt.h | 150 + abc_with_bb_support/src/aig/bdc/bdcTable.c | 140 + abc_with_bb_support/src/aig/bdc/bdc_.c | 49 + abc_with_bb_support/src/aig/bdc/module.make | 4 + abc_with_bb_support/src/aig/cnf/cnf.h | 162 + abc_with_bb_support/src/aig/cnf/cnfCore.c | 185 + abc_with_bb_support/src/aig/cnf/cnfCut.c | 371 + abc_with_bb_support/src/aig/cnf/cnfData.c | 4784 ++++++ abc_with_bb_support/src/aig/cnf/cnfMan.c | 202 + abc_with_bb_support/src/aig/cnf/cnfMap.c | 356 + abc_with_bb_support/src/aig/cnf/cnfPost.c | 233 + abc_with_bb_support/src/aig/cnf/cnfUtil.c | 188 + abc_with_bb_support/src/aig/cnf/cnfWrite.c | 443 + abc_with_bb_support/src/aig/cnf/cnf_.c | 48 + abc_with_bb_support/src/aig/cnf/module.make | 8 + abc_with_bb_support/src/aig/csw/csw.h | 65 + abc_with_bb_support/src/aig/csw/cswCore.c | 94 + abc_with_bb_support/src/aig/csw/cswCut.c | 602 + abc_with_bb_support/src/aig/csw/cswInt.h | 157 + abc_with_bb_support/src/aig/csw/cswMan.c | 125 + abc_with_bb_support/src/aig/csw/cswTable.c | 161 + abc_with_bb_support/src/aig/csw/csw_.c | 48 + abc_with_bb_support/src/aig/csw/module.make | 4 + abc_with_bb_support/src/aig/dar/dar.h | 101 + abc_with_bb_support/src/aig/dar/darBalance.c | 395 + abc_with_bb_support/src/aig/dar/darCore.c | 244 + abc_with_bb_support/src/aig/dar/darCut.c | 698 + abc_with_bb_support/src/aig/dar/darData.c | 11287 ++++++++++++++ abc_with_bb_support/src/aig/dar/darInt.h | 161 + abc_with_bb_support/src/aig/dar/darLib.c | 980 ++ abc_with_bb_support/src/aig/dar/darMan.c | 132 + abc_with_bb_support/src/aig/dar/darRefact.c | 591 + abc_with_bb_support/src/aig/dar/darResub.c | 48 + abc_with_bb_support/src/aig/dar/darScript.c | 204 + abc_with_bb_support/src/aig/dar/darTruth.c | 353 + abc_with_bb_support/src/aig/dar/dar_.c | 48 + abc_with_bb_support/src/aig/dar/module.make | 10 + abc_with_bb_support/src/aig/deco/deco.h | 703 + abc_with_bb_support/src/aig/deco/module.make | 1 + abc_with_bb_support/src/aig/ec/module.make | 1 + abc_with_bb_support/src/aig/fra/fra.h | 238 + abc_with_bb_support/src/aig/fra/fraCec.c | 48 + abc_with_bb_support/src/aig/fra/fraClass.c | 570 + abc_with_bb_support/src/aig/fra/fraCnf.c | 286 + abc_with_bb_support/src/aig/fra/fraCore.c | 323 + abc_with_bb_support/src/aig/fra/fraInd.c | 327 + abc_with_bb_support/src/aig/fra/fraMan.c | 301 + abc_with_bb_support/src/aig/fra/fraSat.c | 337 + abc_with_bb_support/src/aig/fra/fraSec.c | 95 + abc_with_bb_support/src/aig/fra/fraSim.c | 755 + abc_with_bb_support/src/aig/fra/fra_.c | 48 + abc_with_bb_support/src/aig/fra/module.make | 9 + abc_with_bb_support/src/aig/hop/cudd2.c | 355 + abc_with_bb_support/src/aig/hop/cudd2.h | 82 + abc_with_bb_support/src/aig/hop/hop.h | 345 + abc_with_bb_support/src/aig/hop/hopBalance.c | 391 + abc_with_bb_support/src/aig/hop/hopCheck.c | 110 + abc_with_bb_support/src/aig/hop/hopDfs.c | 399 + abc_with_bb_support/src/aig/hop/hopMan.c | 164 + abc_with_bb_support/src/aig/hop/hopMem.c | 115 + abc_with_bb_support/src/aig/hop/hopObj.c | 271 + abc_with_bb_support/src/aig/hop/hopOper.c | 373 + abc_with_bb_support/src/aig/hop/hopTable.c | 262 + abc_with_bb_support/src/aig/hop/hopUtil.c | 572 + abc_with_bb_support/src/aig/hop/hop_.c | 48 + abc_with_bb_support/src/aig/hop/module.make | 9 + abc_with_bb_support/src/aig/ivy/attr.h | 414 + abc_with_bb_support/src/aig/ivy/ivy.h | 556 + abc_with_bb_support/src/aig/ivy/ivyBalance.c | 404 + abc_with_bb_support/src/aig/ivy/ivyCanon.c | 144 + abc_with_bb_support/src/aig/ivy/ivyCheck.c | 273 + abc_with_bb_support/src/aig/ivy/ivyCut.c | 989 ++ abc_with_bb_support/src/aig/ivy/ivyCutTrav.c | 473 + abc_with_bb_support/src/aig/ivy/ivyDfs.c | 493 + abc_with_bb_support/src/aig/ivy/ivyDsd.c | 819 + abc_with_bb_support/src/aig/ivy/ivyFanout.c | 309 + abc_with_bb_support/src/aig/ivy/ivyFastMap.c | 1593 ++ abc_with_bb_support/src/aig/ivy/ivyFraig.c | 2756 ++++ abc_with_bb_support/src/aig/ivy/ivyHaig.c | 530 + abc_with_bb_support/src/aig/ivy/ivyMan.c | 546 + abc_with_bb_support/src/aig/ivy/ivyMem.c | 116 + abc_with_bb_support/src/aig/ivy/ivyMulti.c | 301 + abc_with_bb_support/src/aig/ivy/ivyMulti8.c | 427 + abc_with_bb_support/src/aig/ivy/ivyObj.c | 476 + abc_with_bb_support/src/aig/ivy/ivyOper.c | 293 + abc_with_bb_support/src/aig/ivy/ivyResyn.c | 196 + abc_with_bb_support/src/aig/ivy/ivyRwr.c | 609 + abc_with_bb_support/src/aig/ivy/ivyRwrAlg.c | 408 + abc_with_bb_support/src/aig/ivy/ivySeq.c | 1134 ++ abc_with_bb_support/src/aig/ivy/ivyShow.c | 338 + abc_with_bb_support/src/aig/ivy/ivyTable.c | 301 + abc_with_bb_support/src/aig/ivy/ivyUtil.c | 818 + abc_with_bb_support/src/aig/ivy/ivy_.c | 48 + abc_with_bb_support/src/aig/ivy/module.make | 22 + abc_with_bb_support/src/aig/kit/kit.h | 544 + abc_with_bb_support/src/aig/kit/kitBdd.c | 231 + abc_with_bb_support/src/aig/kit/kitDsd.c | 2185 +++ abc_with_bb_support/src/aig/kit/kitFactor.c | 338 + abc_with_bb_support/src/aig/kit/kitGraph.c | 397 + abc_with_bb_support/src/aig/kit/kitHop.c | 115 + abc_with_bb_support/src/aig/kit/kitIsop.c | 325 + abc_with_bb_support/src/aig/kit/kitSop.c | 570 + abc_with_bb_support/src/aig/kit/kitTruth.c | 1640 ++ abc_with_bb_support/src/aig/kit/kit_.c | 48 + abc_with_bb_support/src/aig/kit/module.make | 8 + abc_with_bb_support/src/aig/mem/mem.c | 604 + abc_with_bb_support/src/aig/mem/mem.h | 72 + abc_with_bb_support/src/aig/mem/module.make | 1 + abc_with_bb_support/src/aig/rwt/module.make | 3 + abc_with_bb_support/src/aig/rwt/rwt.h | 155 + abc_with_bb_support/src/aig/rwt/rwtDec.c | 150 + abc_with_bb_support/src/aig/rwt/rwtMan.c | 358 + abc_with_bb_support/src/aig/rwt/rwtUtil.c | 665 + abc_with_bb_support/src/base/abc/abc.h | 942 ++ abc_with_bb_support/src/base/abc/abcAig.c | 1476 ++ abc_with_bb_support/src/base/abc/abcBlifMv.c | 970 ++ abc_with_bb_support/src/base/abc/abcCheck.c | 943 ++ abc_with_bb_support/src/base/abc/abcDfs.c | 1264 ++ abc_with_bb_support/src/base/abc/abcFanio.c | 297 + abc_with_bb_support/src/base/abc/abcFunc.c | 1154 ++ abc_with_bb_support/src/base/abc/abcHie.c | 492 + abc_with_bb_support/src/base/abc/abcInt.h | 52 + abc_with_bb_support/src/base/abc/abcLatch.c | 219 + abc_with_bb_support/src/base/abc/abcLib.c | 455 + abc_with_bb_support/src/base/abc/abcMinBase.c | 256 + abc_with_bb_support/src/base/abc/abcNames.c | 467 + abc_with_bb_support/src/base/abc/abcNetlist.c | 411 + abc_with_bb_support/src/base/abc/abcNtk.c | 1094 ++ abc_with_bb_support/src/base/abc/abcObj.c | 991 ++ abc_with_bb_support/src/base/abc/abcRefs.c | 452 + abc_with_bb_support/src/base/abc/abcShow.c | 318 + abc_with_bb_support/src/base/abc/abcSop.c | 1075 ++ abc_with_bb_support/src/base/abc/abcUtil.c | 1737 +++ abc_with_bb_support/src/base/abc/abc_.c | 47 + abc_with_bb_support/src/base/abc/module.make | 18 + abc_with_bb_support/src/base/abci/abc.c | 12160 +++++++++++++++ abc_with_bb_support/src/base/abci/abcAttach.c | 404 + abc_with_bb_support/src/base/abci/abcAuto.c | 239 + .../src/base/abci/abcBalance.c | 613 + abc_with_bb_support/src/base/abci/abcBmc.c | 115 + abc_with_bb_support/src/base/abci/abcCas.c | 111 + abc_with_bb_support/src/base/abci/abcClpBdd.c | 162 + abc_with_bb_support/src/base/abci/abcClpSop.c | 53 + abc_with_bb_support/src/base/abci/abcCut.c | 620 + abc_with_bb_support/src/base/abci/abcDar.c | 1017 ++ abc_with_bb_support/src/base/abci/abcDebug.c | 208 + abc_with_bb_support/src/base/abci/abcDress.c | 209 + abc_with_bb_support/src/base/abci/abcDsd.c | 551 + .../src/base/abci/abcEspresso.c | 250 + .../src/base/abci/abcExtract.c | 51 + abc_with_bb_support/src/base/abci/abcFpga.c | 278 + .../src/base/abci/abcFpgaFast.c | 190 + abc_with_bb_support/src/base/abci/abcFraig.c | 816 + abc_with_bb_support/src/base/abci/abcFxu.c | 260 + abc_with_bb_support/src/base/abci/abcGen.c | 511 + abc_with_bb_support/src/base/abci/abcHaig.c | 726 + abc_with_bb_support/src/base/abci/abcIf.c | 501 + abc_with_bb_support/src/base/abci/abcIvy.c | 1101 ++ abc_with_bb_support/src/base/abci/abcLut.c | 786 + abc_with_bb_support/src/base/abci/abcMap.c | 657 + .../src/base/abci/abcMeasure.c | 478 + abc_with_bb_support/src/base/abci/abcMini.c | 153 + abc_with_bb_support/src/base/abci/abcMiter.c | 1138 ++ abc_with_bb_support/src/base/abci/abcMulti.c | 643 + abc_with_bb_support/src/base/abci/abcMv.c | 369 + abc_with_bb_support/src/base/abci/abcNtbdd.c | 582 + abc_with_bb_support/src/base/abci/abcOdc.c | 1134 ++ abc_with_bb_support/src/base/abci/abcOrder.c | 131 + abc_with_bb_support/src/base/abci/abcPart.c | 891 ++ abc_with_bb_support/src/base/abci/abcPlace.c | 255 + abc_with_bb_support/src/base/abci/abcPrint.c | 949 ++ abc_with_bb_support/src/base/abci/abcProve.c | 343 + abc_with_bb_support/src/base/abci/abcQbf.c | 260 + abc_with_bb_support/src/base/abci/abcQuant.c | 419 + abc_with_bb_support/src/base/abci/abcRec.c | 1173 ++ abc_with_bb_support/src/base/abci/abcReconv.c | 762 + .../src/base/abci/abcRefactor.c | 379 + abc_with_bb_support/src/base/abci/abcRenode.c | 311 + .../src/base/abci/abcReorder.c | 100 + .../src/base/abci/abcRestruct.c | 1496 ++ abc_with_bb_support/src/base/abci/abcResub.c | 1951 +++ .../src/base/abci/abcRewrite.c | 414 + abc_with_bb_support/src/base/abci/abcRr.c | 999 ++ abc_with_bb_support/src/base/abci/abcSat.c | 884 ++ abc_with_bb_support/src/base/abci/abcStrash.c | 477 + abc_with_bb_support/src/base/abci/abcSweep.c | 948 ++ abc_with_bb_support/src/base/abci/abcSymm.c | 229 + abc_with_bb_support/src/base/abci/abcTiming.c | 905 ++ abc_with_bb_support/src/base/abci/abcUnate.c | 155 + .../src/base/abci/abcUnreach.c | 349 + abc_with_bb_support/src/base/abci/abcVerify.c | 1010 ++ abc_with_bb_support/src/base/abci/abcXsim.c | 207 + abc_with_bb_support/src/base/abci/abc_.c | 48 + abc_with_bb_support/src/base/abci/abc_new.h | 23 + abc_with_bb_support/src/base/abci/module.make | 55 + abc_with_bb_support/src/base/abci/xaaaa.c | 155 + abc_with_bb_support/src/base/cmd/cmd.c | 1674 +++ abc_with_bb_support/src/base/cmd/cmd.h | 73 + abc_with_bb_support/src/base/cmd/cmdAlias.c | 120 + abc_with_bb_support/src/base/cmd/cmdApi.c | 104 + abc_with_bb_support/src/base/cmd/cmdFlag.c | 104 + abc_with_bb_support/src/base/cmd/cmdHist.c | 55 + abc_with_bb_support/src/base/cmd/cmdInt.h | 83 + abc_with_bb_support/src/base/cmd/cmdUtils.c | 649 + abc_with_bb_support/src/base/cmd/module.make | 6 + .../src/base/func/funcBlifMv.c | 62 + abc_with_bb_support/src/base/io/io.c | 1893 +++ abc_with_bb_support/src/base/io/io.h | 146 + abc_with_bb_support/src/base/io/ioInt.h | 49 + abc_with_bb_support/src/base/io/ioReadAiger.c | 278 + abc_with_bb_support/src/base/io/ioReadBaf.c | 171 + abc_with_bb_support/src/base/io/ioReadBench.c | 277 + abc_with_bb_support/src/base/io/ioReadBlif.c | 1131 ++ .../src/base/io/ioReadBlifAig.c | 1013 ++ .../src/base/io/ioReadBlifMv.c | 1721 +++ abc_with_bb_support/src/base/io/ioReadDsd.c | 308 + abc_with_bb_support/src/base/io/ioReadEdif.c | 235 + abc_with_bb_support/src/base/io/ioReadEqn.c | 239 + abc_with_bb_support/src/base/io/ioReadPla.c | 250 + .../src/base/io/ioReadVerilog.c | 90 + abc_with_bb_support/src/base/io/ioUtil.c | 752 + .../src/base/io/ioWriteAiger.c | 284 + abc_with_bb_support/src/base/io/ioWriteBaf.c | 168 + .../src/base/io/ioWriteBench.c | 335 + abc_with_bb_support/src/base/io/ioWriteBlif.c | 656 + .../src/base/io/ioWriteBlifMv.c | 519 + abc_with_bb_support/src/base/io/ioWriteCnf.c | 115 + abc_with_bb_support/src/base/io/ioWriteDot.c | 809 + abc_with_bb_support/src/base/io/ioWriteEqn.c | 252 + abc_with_bb_support/src/base/io/ioWriteGml.c | 116 + abc_with_bb_support/src/base/io/ioWriteList.c | 288 + abc_with_bb_support/src/base/io/ioWritePla.c | 197 + .../src/base/io/ioWriteVerilog.c | 636 + .../src/base/io/ioWriteVerilog.zip | Bin 0 -> 3662 bytes abc_with_bb_support/src/base/io/io_.c | 48 + abc_with_bb_support/src/base/io/module.make | 25 + .../src/base/main/libSupport.c | 196 + abc_with_bb_support/src/base/main/main.c | 317 + abc_with_bb_support/src/base/main/main.h | 122 + abc_with_bb_support/src/base/main/mainFrame.c | 502 + abc_with_bb_support/src/base/main/mainInit.c | 100 + abc_with_bb_support/src/base/main/mainInt.h | 110 + abc_with_bb_support/src/base/main/mainUtils.c | 239 + abc_with_bb_support/src/base/main/module.make | 5 + abc_with_bb_support/src/base/seq/module.make | 14 + abc_with_bb_support/src/base/seq/seq.h | 101 + abc_with_bb_support/src/base/seq/seqAigCore.c | 977 ++ abc_with_bb_support/src/base/seq/seqAigIter.c | 268 + abc_with_bb_support/src/base/seq/seqCreate.c | 482 + .../src/base/seq/seqFpgaCore.c | 643 + .../src/base/seq/seqFpgaIter.c | 270 + abc_with_bb_support/src/base/seq/seqInt.h | 256 + abc_with_bb_support/src/base/seq/seqLatch.c | 223 + abc_with_bb_support/src/base/seq/seqMan.c | 133 + abc_with_bb_support/src/base/seq/seqMapCore.c | 652 + abc_with_bb_support/src/base/seq/seqMapIter.c | 623 + .../src/base/seq/seqMaxMeanCycle.c | 567 + abc_with_bb_support/src/base/seq/seqRetCore.c | 492 + abc_with_bb_support/src/base/seq/seqRetIter.c | 403 + abc_with_bb_support/src/base/seq/seqShare.c | 388 + abc_with_bb_support/src/base/seq/seqUtil.c | 597 + abc_with_bb_support/src/base/temp.c | 83 + abc_with_bb_support/src/base/ver/module.make | 4 + abc_with_bb_support/src/base/ver/ver.h | 118 + abc_with_bb_support/src/base/ver/verCore.c | 2839 ++++ abc_with_bb_support/src/base/ver/verCore.zip | Bin 0 -> 14624 bytes abc_with_bb_support/src/base/ver/verFormula.c | 474 + abc_with_bb_support/src/base/ver/verParse.c | 117 + abc_with_bb_support/src/base/ver/verStream.c | 440 + abc_with_bb_support/src/base/ver/verWords.c | 48 + abc_with_bb_support/src/base/ver/ver_.c | 48 + abc_with_bb_support/src/bdd/cas/cas.h | 62 + abc_with_bb_support/src/bdd/cas/casCore.c | 1263 ++ abc_with_bb_support/src/bdd/cas/casDec.c | 508 + abc_with_bb_support/src/bdd/cas/module.make | 3 + abc_with_bb_support/src/bdd/cudd/cuBdd.make | 41 + abc_with_bb_support/src/bdd/cudd/cudd.h | 959 ++ abc_with_bb_support/src/bdd/cudd/cudd.make | 42 + abc_with_bb_support/src/bdd/cudd/cuddAPI.c | 4409 ++++++ abc_with_bb_support/src/bdd/cudd/cuddAddAbs.c | 566 + .../src/bdd/cudd/cuddAddApply.c | 917 ++ .../src/bdd/cudd/cuddAddFind.c | 283 + abc_with_bb_support/src/bdd/cudd/cuddAddInv.c | 172 + abc_with_bb_support/src/bdd/cudd/cuddAddIte.c | 613 + abc_with_bb_support/src/bdd/cudd/cuddAddNeg.c | 262 + .../src/bdd/cudd/cuddAddWalsh.c | 364 + abc_with_bb_support/src/bdd/cudd/cuddAndAbs.c | 306 + abc_with_bb_support/src/bdd/cudd/cuddAnneal.c | 788 + abc_with_bb_support/src/bdd/cudd/cuddApa.c | 930 ++ abc_with_bb_support/src/bdd/cudd/cuddApprox.c | 2192 +++ abc_with_bb_support/src/bdd/cudd/cuddBddAbs.c | 689 + .../src/bdd/cudd/cuddBddCorr.c | 481 + abc_with_bb_support/src/bdd/cudd/cuddBddIte.c | 1254 ++ abc_with_bb_support/src/bdd/cudd/cuddBridge.c | 981 ++ abc_with_bb_support/src/bdd/cudd/cuddCache.c | 1023 ++ abc_with_bb_support/src/bdd/cudd/cuddCheck.c | 851 ++ abc_with_bb_support/src/bdd/cudd/cuddClip.c | 531 + abc_with_bb_support/src/bdd/cudd/cuddCof.c | 300 + .../src/bdd/cudd/cuddCompose.c | 1722 +++ abc_with_bb_support/src/bdd/cudd/cuddDecomp.c | 2150 +++ abc_with_bb_support/src/bdd/cudd/cuddEssent.c | 279 + abc_with_bb_support/src/bdd/cudd/cuddExact.c | 1004 ++ abc_with_bb_support/src/bdd/cudd/cuddExport.c | 1289 ++ abc_with_bb_support/src/bdd/cudd/cuddGenCof.c | 1968 +++ .../src/bdd/cudd/cuddGenetic.c | 921 ++ abc_with_bb_support/src/bdd/cudd/cuddGroup.c | 2142 +++ .../src/bdd/cudd/cuddHarwell.c | 541 + abc_with_bb_support/src/bdd/cudd/cuddInit.c | 283 + abc_with_bb_support/src/bdd/cudd/cuddInt.h | 1133 ++ .../src/bdd/cudd/cuddInteract.c | 402 + abc_with_bb_support/src/bdd/cudd/cuddLCache.c | 1428 ++ abc_with_bb_support/src/bdd/cudd/cuddLevelQ.c | 533 + abc_with_bb_support/src/bdd/cudd/cuddLinear.c | 1333 ++ .../src/bdd/cudd/cuddLiteral.c | 237 + .../src/bdd/cudd/cuddMatMult.c | 680 + .../src/bdd/cudd/cuddPriority.c | 1475 ++ abc_with_bb_support/src/bdd/cudd/cuddRead.c | 490 + abc_with_bb_support/src/bdd/cudd/cuddRef.c | 781 + .../src/bdd/cudd/cuddReorder.c | 2090 +++ abc_with_bb_support/src/bdd/cudd/cuddSat.c | 1305 ++ abc_with_bb_support/src/bdd/cudd/cuddSign.c | 292 + abc_with_bb_support/src/bdd/cudd/cuddSolve.c | 339 + abc_with_bb_support/src/bdd/cudd/cuddSplit.c | 657 + .../src/bdd/cudd/cuddSubsetHB.c | 1311 ++ .../src/bdd/cudd/cuddSubsetSP.c | 1624 ++ .../src/bdd/cudd/cuddSymmetry.c | 1668 +++ abc_with_bb_support/src/bdd/cudd/cuddTable.c | 3141 ++++ abc_with_bb_support/src/bdd/cudd/cuddUtil.c | 3633 +++++ abc_with_bb_support/src/bdd/cudd/cuddWindow.c | 997 ++ .../src/bdd/cudd/cuddZddCount.c | 324 + .../src/bdd/cudd/cuddZddFuncs.c | 1603 ++ .../src/bdd/cudd/cuddZddGroup.c | 1317 ++ .../src/bdd/cudd/cuddZddIsop.c | 885 ++ abc_with_bb_support/src/bdd/cudd/cuddZddLin.c | 939 ++ .../src/bdd/cudd/cuddZddMisc.c | 252 + .../src/bdd/cudd/cuddZddPort.c | 354 + .../src/bdd/cudd/cuddZddReord.c | 1633 ++ .../src/bdd/cudd/cuddZddSetop.c | 1137 ++ .../src/bdd/cudd/cuddZddSymm.c | 1677 +++ .../src/bdd/cudd/cuddZddUtil.c | 1021 ++ abc_with_bb_support/src/bdd/cudd/module.make | 61 + abc_with_bb_support/src/bdd/cudd/r7x8.1.mat | 53 + abc_with_bb_support/src/bdd/cudd/testcudd.c | 988 ++ abc_with_bb_support/src/bdd/dsd/dsd.h | 129 + abc_with_bb_support/src/bdd/dsd/dsdApi.c | 97 + abc_with_bb_support/src/bdd/dsd/dsdCheck.c | 314 + abc_with_bb_support/src/bdd/dsd/dsdInt.h | 91 + abc_with_bb_support/src/bdd/dsd/dsdLocal.c | 337 + abc_with_bb_support/src/bdd/dsd/dsdMan.c | 114 + abc_with_bb_support/src/bdd/dsd/dsdProc.c | 1617 ++ abc_with_bb_support/src/bdd/dsd/dsdTree.c | 1068 ++ abc_with_bb_support/src/bdd/dsd/module.make | 6 + abc_with_bb_support/src/bdd/epd/epd.c | 1314 ++ abc_with_bb_support/src/bdd/epd/epd.h | 160 + abc_with_bb_support/src/bdd/epd/module.make | 1 + abc_with_bb_support/src/bdd/mtr/module.make | 2 + abc_with_bb_support/src/bdd/mtr/mtr.h | 173 + abc_with_bb_support/src/bdd/mtr/mtrBasic.c | 426 + abc_with_bb_support/src/bdd/mtr/mtrGroup.c | 690 + abc_with_bb_support/src/bdd/mtr/mtrInt.h | 65 + abc_with_bb_support/src/bdd/parse/module.make | 3 + abc_with_bb_support/src/bdd/parse/parse.h | 54 + abc_with_bb_support/src/bdd/parse/parseCore.c | 504 + abc_with_bb_support/src/bdd/parse/parseEqn.c | 349 + abc_with_bb_support/src/bdd/parse/parseInt.h | 74 + .../src/bdd/parse/parseStack.c | 243 + abc_with_bb_support/src/bdd/reo/module.make | 7 + abc_with_bb_support/src/bdd/reo/reo.h | 232 + abc_with_bb_support/src/bdd/reo/reoApi.c | 289 + abc_with_bb_support/src/bdd/reo/reoCore.c | 438 + abc_with_bb_support/src/bdd/reo/reoProfile.c | 365 + abc_with_bb_support/src/bdd/reo/reoSift.c | 341 + abc_with_bb_support/src/bdd/reo/reoSwap.c | 898 ++ abc_with_bb_support/src/bdd/reo/reoTest.c | 251 + abc_with_bb_support/src/bdd/reo/reoTransfer.c | 199 + abc_with_bb_support/src/bdd/reo/reoUnits.c | 184 + abc_with_bb_support/src/generic.c | 47 + abc_with_bb_support/src/generic.h | 59 + abc_with_bb_support/src/map/fpga/fpga.c | 283 + abc_with_bb_support/src/map/fpga/fpga.h | 172 + abc_with_bb_support/src/map/fpga/fpgaCore.c | 188 + abc_with_bb_support/src/map/fpga/fpgaCreate.c | 580 + abc_with_bb_support/src/map/fpga/fpgaCut.c | 1159 ++ .../src/map/fpga/fpgaCutUtils.c | 470 + abc_with_bb_support/src/map/fpga/fpgaFanout.c | 141 + .../src/map/fpga/fpgaGENERIC.c | 46 + abc_with_bb_support/src/map/fpga/fpgaInt.h | 388 + abc_with_bb_support/src/map/fpga/fpgaLib.c | 249 + abc_with_bb_support/src/map/fpga/fpgaMatch.c | 794 + abc_with_bb_support/src/map/fpga/fpgaSwitch.c | 151 + abc_with_bb_support/src/map/fpga/fpgaTime.c | 262 + abc_with_bb_support/src/map/fpga/fpgaTruth.c | 166 + abc_with_bb_support/src/map/fpga/fpgaUtils.c | 986 ++ abc_with_bb_support/src/map/fpga/fpgaVec.c | 408 + abc_with_bb_support/src/map/fpga/module.make | 13 + abc_with_bb_support/src/map/if/if.h | 386 + abc_with_bb_support/src/map/if/ifCore.c | 146 + abc_with_bb_support/src/map/if/ifCut.c | 820 + abc_with_bb_support/src/map/if/ifMan.c | 570 + abc_with_bb_support/src/map/if/ifMap.c | 300 + abc_with_bb_support/src/map/if/ifReduce.c | 574 + abc_with_bb_support/src/map/if/ifSeq.c | 405 + abc_with_bb_support/src/map/if/ifTime.c | 221 + abc_with_bb_support/src/map/if/ifTruth.c | 102 + abc_with_bb_support/src/map/if/ifUtil.c | 454 + abc_with_bb_support/src/map/if/if_.c | 47 + abc_with_bb_support/src/map/if/module.make | 9 + abc_with_bb_support/src/map/mapper/mapper.c | 176 + abc_with_bb_support/src/map/mapper/mapper.h | 195 + .../src/map/mapper/mapperCanon.c | 271 + .../src/map/mapper/mapperCore.c | 228 + .../src/map/mapper/mapperCreate.c | 600 + .../src/map/mapper/mapperCut.c | 1168 ++ .../src/map/mapper/mapperCutUtils.c | 273 + .../src/map/mapper/mapperFanout.c | 141 + .../src/map/mapper/mapperGENERIC.c | 46 + .../src/map/mapper/mapperInt.h | 477 + .../src/map/mapper/mapperLib.c | 231 + .../src/map/mapper/mapperMatch.c | 596 + .../src/map/mapper/mapperRefs.c | 557 + .../src/map/mapper/mapperSuper.c | 449 + .../src/map/mapper/mapperSwitch.c | 223 + .../src/map/mapper/mapperTable.c | 402 + .../src/map/mapper/mapperTime.c | 510 + .../src/map/mapper/mapperTree.c | 818 + .../src/map/mapper/mapperTruth.c | 310 + .../src/map/mapper/mapperUtils.c | 1154 ++ .../src/map/mapper/mapperVec.c | 318 + .../src/map/mapper/module.make | 18 + abc_with_bb_support/src/map/mio/mio.c | 269 + abc_with_bb_support/src/map/mio/mio.h | 150 + abc_with_bb_support/src/map/mio/mioApi.c | 172 + abc_with_bb_support/src/map/mio/mioFunc.c | 268 + abc_with_bb_support/src/map/mio/mioGENERIC.c | 46 + abc_with_bb_support/src/map/mio/mioInt.h | 125 + abc_with_bb_support/src/map/mio/mioRead.c | 582 + abc_with_bb_support/src/map/mio/mioUtils.c | 531 + abc_with_bb_support/src/map/mio/module.make | 5 + abc_with_bb_support/src/map/super/module.make | 4 + abc_with_bb_support/src/map/super/super.c | 319 + abc_with_bb_support/src/map/super/super.h | 60 + abc_with_bb_support/src/map/super/superAnd.c | 696 + .../src/map/super/superGENERIC.c | 46 + abc_with_bb_support/src/map/super/superGate.c | 1324 ++ abc_with_bb_support/src/map/super/superInt.h | 62 + .../src/map/super/superWrite.c | 76 + .../src/misc/espresso/cofactor.c | 382 + abc_with_bb_support/src/misc/espresso/cols.c | 314 + abc_with_bb_support/src/misc/espresso/compl.c | 680 + .../src/misc/espresso/contain.c | 441 + .../src/misc/espresso/cubehack.c | 138 + .../src/misc/espresso/cubestr.c | 152 + abc_with_bb_support/src/misc/espresso/cvrin.c | 810 + abc_with_bb_support/src/misc/espresso/cvrm.c | 539 + .../src/misc/espresso/cvrmisc.c | 142 + .../src/misc/espresso/cvrout.c | 609 + .../src/misc/espresso/dominate.c | 98 + abc_with_bb_support/src/misc/espresso/equiv.c | 94 + .../src/misc/espresso/espresso.c | 139 + .../src/misc/espresso/espresso.h | 782 + abc_with_bb_support/src/misc/espresso/essen.c | 179 + abc_with_bb_support/src/misc/espresso/exact.c | 181 + .../src/misc/espresso/expand.c | 693 + abc_with_bb_support/src/misc/espresso/gasp.c | 228 + .../src/misc/espresso/gimpel.c | 106 + .../src/misc/espresso/globals.c | 76 + abc_with_bb_support/src/misc/espresso/hack.c | 641 + abc_with_bb_support/src/misc/espresso/indep.c | 134 + abc_with_bb_support/src/misc/espresso/irred.c | 440 + abc_with_bb_support/src/misc/espresso/main.c | 746 + abc_with_bb_support/src/misc/espresso/main.h | 122 + abc_with_bb_support/src/misc/espresso/map.c | 115 + .../src/misc/espresso/matrix.c | 574 + .../src/misc/espresso/mincov.c | 378 + .../src/misc/espresso/mincov.h | 11 + .../src/misc/espresso/mincov_int.h | 55 + .../src/misc/espresso/module.make | 39 + abc_with_bb_support/src/misc/espresso/opo.c | 624 + abc_with_bb_support/src/misc/espresso/pair.c | 675 + abc_with_bb_support/src/misc/espresso/part.c | 122 + .../src/misc/espresso/primes.c | 170 + .../src/misc/espresso/reduce.c | 258 + abc_with_bb_support/src/misc/espresso/rows.c | 314 + abc_with_bb_support/src/misc/espresso/set.c | 820 + abc_with_bb_support/src/misc/espresso/setc.c | 483 + abc_with_bb_support/src/misc/espresso/sharp.c | 247 + .../src/misc/espresso/sminterf.c | 44 + .../src/misc/espresso/solution.c | 114 + .../src/misc/espresso/sparse.c | 146 + .../src/misc/espresso/sparse.h | 135 + .../src/misc/espresso/sparse_int.h | 121 + abc_with_bb_support/src/misc/espresso/unate.c | 441 + .../src/misc/espresso/util_old.h | 301 + .../src/misc/espresso/verify.c | 193 + abc_with_bb_support/src/misc/extra/extra.h | 626 + .../src/misc/extra/extraBddAuto.c | 1558 ++ .../src/misc/extra/extraBddCas.c | 1230 ++ .../src/misc/extra/extraBddKmap.c | 783 + .../src/misc/extra/extraBddMisc.c | 1614 ++ .../src/misc/extra/extraBddSymm.c | 1469 ++ .../src/misc/extra/extraBddUnate.c | 641 + .../src/misc/extra/extraUtilBitMatrix.c | 415 + .../src/misc/extra/extraUtilCanon.c | 701 + .../src/misc/extra/extraUtilFile.c | 495 + .../src/misc/extra/extraUtilMemory.c | 625 + .../src/misc/extra/extraUtilMisc.c | 2235 +++ .../src/misc/extra/extraUtilProgress.c | 176 + .../src/misc/extra/extraUtilReader.c | 383 + .../src/misc/extra/extraUtilTruth.c | 1148 ++ .../src/misc/extra/extraUtilUtil.c | 330 + .../src/misc/extra/module.make | 15 + abc_with_bb_support/src/misc/hash/hash.h | 65 + abc_with_bb_support/src/misc/hash/hashFlt.h | 330 + abc_with_bb_support/src/misc/hash/hashInt.h | 293 + abc_with_bb_support/src/misc/hash/hashPtr.h | 331 + abc_with_bb_support/src/misc/hash/module.make | 1 + abc_with_bb_support/src/misc/mvc/module.make | 16 + abc_with_bb_support/src/misc/mvc/mvc.c | 46 + abc_with_bb_support/src/misc/mvc/mvc.h | 732 + abc_with_bb_support/src/misc/mvc/mvcApi.c | 233 + abc_with_bb_support/src/misc/mvc/mvcCompare.c | 369 + abc_with_bb_support/src/misc/mvc/mvcContain.c | 173 + abc_with_bb_support/src/misc/mvc/mvcCover.c | 251 + abc_with_bb_support/src/misc/mvc/mvcCube.c | 175 + abc_with_bb_support/src/misc/mvc/mvcDivide.c | 436 + abc_with_bb_support/src/misc/mvc/mvcDivisor.c | 90 + abc_with_bb_support/src/misc/mvc/mvcList.c | 362 + abc_with_bb_support/src/misc/mvc/mvcLits.c | 345 + abc_with_bb_support/src/misc/mvc/mvcMan.c | 77 + abc_with_bb_support/src/misc/mvc/mvcOpAlg.c | 163 + abc_with_bb_support/src/misc/mvc/mvcOpBool.c | 151 + abc_with_bb_support/src/misc/mvc/mvcPrint.c | 220 + abc_with_bb_support/src/misc/mvc/mvcSort.c | 141 + abc_with_bb_support/src/misc/mvc/mvcUtils.c | 868 ++ abc_with_bb_support/src/misc/nm/module.make | 2 + abc_with_bb_support/src/misc/nm/nm.h | 92 + abc_with_bb_support/src/misc/nm/nmApi.c | 272 + abc_with_bb_support/src/misc/nm/nmInt.h | 91 + abc_with_bb_support/src/misc/nm/nmTable.c | 340 + abc_with_bb_support/src/misc/st/module.make | 2 + abc_with_bb_support/src/misc/st/st.c | 624 + abc_with_bb_support/src/misc/st/st.h | 96 + abc_with_bb_support/src/misc/st/stmm.c | 687 + abc_with_bb_support/src/misc/st/stmm.h | 127 + abc_with_bb_support/src/misc/util/leaks.h | 30 + abc_with_bb_support/src/misc/util/module.make | 1 + .../src/misc/util/stdlib_hack.h | 471 + abc_with_bb_support/src/misc/util/util_hack.h | 95 + ...5-Removed-an-unnecessary-dereference.patch | 34 + abc_with_bb_support/src/misc/vec/module.make | 1 + abc_with_bb_support/src/misc/vec/vec.h | 68 + abc_with_bb_support/src/misc/vec/vecAtt.h | 391 + abc_with_bb_support/src/misc/vec/vecFlt.h | 665 + abc_with_bb_support/src/misc/vec/vecInt.h | 852 ++ abc_with_bb_support/src/misc/vec/vecPtr.h | 762 + abc_with_bb_support/src/misc/vec/vecStr.h | 562 + abc_with_bb_support/src/misc/vec/vecVec.h | 356 + abc_with_bb_support/src/opt/cut/abcCut.c | 492 + abc_with_bb_support/src/opt/cut/cut.h | 165 + abc_with_bb_support/src/opt/cut/cutApi.c | 197 + abc_with_bb_support/src/opt/cut/cutCut.c | 359 + abc_with_bb_support/src/opt/cut/cutExpand.c | 184 + abc_with_bb_support/src/opt/cut/cutInt.h | 157 + abc_with_bb_support/src/opt/cut/cutList.h | 207 + abc_with_bb_support/src/opt/cut/cutMan.c | 326 + abc_with_bb_support/src/opt/cut/cutMerge.c | 657 + abc_with_bb_support/src/opt/cut/cutNode.c | 992 ++ abc_with_bb_support/src/opt/cut/cutOracle.c | 428 + abc_with_bb_support/src/opt/cut/cutPre22.c | 988 ++ abc_with_bb_support/src/opt/cut/cutSeq.c | 227 + abc_with_bb_support/src/opt/cut/cutTruth.c | 226 + abc_with_bb_support/src/opt/cut/module.make | 9 + abc_with_bb_support/src/opt/dec/dec.h | 719 + abc_with_bb_support/src/opt/dec/decAbc.c | 305 + abc_with_bb_support/src/opt/dec/decFactor.c | 392 + abc_with_bb_support/src/opt/dec/decMan.c | 83 + abc_with_bb_support/src/opt/dec/decPrint.c | 284 + abc_with_bb_support/src/opt/dec/decUtil.c | 134 + abc_with_bb_support/src/opt/dec/module.make | 5 + abc_with_bb_support/src/opt/fxu/fxu.c | 252 + abc_with_bb_support/src/opt/fxu/fxu.h | 92 + abc_with_bb_support/src/opt/fxu/fxuCreate.c | 418 + abc_with_bb_support/src/opt/fxu/fxuHeapD.c | 445 + abc_with_bb_support/src/opt/fxu/fxuHeapS.c | 444 + abc_with_bb_support/src/opt/fxu/fxuInt.h | 537 + abc_with_bb_support/src/opt/fxu/fxuList.c | 522 + abc_with_bb_support/src/opt/fxu/fxuMatrix.c | 374 + abc_with_bb_support/src/opt/fxu/fxuPair.c | 555 + abc_with_bb_support/src/opt/fxu/fxuPrint.c | 195 + abc_with_bb_support/src/opt/fxu/fxuReduce.c | 204 + abc_with_bb_support/src/opt/fxu/fxuSelect.c | 603 + abc_with_bb_support/src/opt/fxu/fxuSingle.c | 166 + abc_with_bb_support/src/opt/fxu/fxuUpdate.c | 803 + abc_with_bb_support/src/opt/fxu/module.make | 12 + abc_with_bb_support/src/opt/lpk/lpk.h | 83 + abc_with_bb_support/src/opt/lpk/lpkCore.c | 468 + abc_with_bb_support/src/opt/lpk/lpkCut.c | 604 + abc_with_bb_support/src/opt/lpk/lpkInt.h | 165 + abc_with_bb_support/src/opt/lpk/lpkMan.c | 98 + abc_with_bb_support/src/opt/lpk/lpkMap.c | 205 + abc_with_bb_support/src/opt/lpk/lpkMulti.c | 495 + abc_with_bb_support/src/opt/lpk/lpkMux.c | 244 + abc_with_bb_support/src/opt/lpk/lpkSets.c | 440 + abc_with_bb_support/src/opt/lpk/lpk_.c | 48 + abc_with_bb_support/src/opt/lpk/module.make | 7 + abc_with_bb_support/src/opt/res/module.make | 7 + abc_with_bb_support/src/opt/res/res.h | 75 + abc_with_bb_support/src/opt/res/resCore.c | 415 + abc_with_bb_support/src/opt/res/resDivs.c | 285 + abc_with_bb_support/src/opt/res/resFilter.c | 434 + abc_with_bb_support/src/opt/res/resInt.h | 137 + abc_with_bb_support/src/opt/res/resSat.c | 407 + abc_with_bb_support/src/opt/res/resSim.c | 790 + abc_with_bb_support/src/opt/res/resSim_old.c | 521 + abc_with_bb_support/src/opt/res/resStrash.c | 117 + abc_with_bb_support/src/opt/res/resWin.c | 485 + abc_with_bb_support/src/opt/res/res_.c | 50 + abc_with_bb_support/src/opt/ret/module.make | 8 + abc_with_bb_support/src/opt/ret/retArea.c | 540 + abc_with_bb_support/src/opt/ret/retCore.c | 132 + abc_with_bb_support/src/opt/ret/retDelay.c | 305 + abc_with_bb_support/src/opt/ret/retFlow.c | 783 + abc_with_bb_support/src/opt/ret/retIncrem.c | 464 + abc_with_bb_support/src/opt/ret/retInit.c | 349 + abc_with_bb_support/src/opt/ret/retInt.h | 80 + abc_with_bb_support/src/opt/ret/retLvalue.c | 395 + abc_with_bb_support/src/opt/ret/ret_.c | 48 + abc_with_bb_support/src/opt/rwr/module.make | 7 + abc_with_bb_support/src/opt/rwr/rwr.h | 169 + abc_with_bb_support/src/opt/rwr/rwrDec.c | 150 + abc_with_bb_support/src/opt/rwr/rwrEva.c | 587 + abc_with_bb_support/src/opt/rwr/rwrExp.c | 333 + abc_with_bb_support/src/opt/rwr/rwrLib.c | 362 + abc_with_bb_support/src/opt/rwr/rwrMan.c | 318 + abc_with_bb_support/src/opt/rwr/rwrPrint.c | 266 + abc_with_bb_support/src/opt/rwr/rwrTemp.c | 121 + abc_with_bb_support/src/opt/rwr/rwrUtil.c | 659 + abc_with_bb_support/src/opt/sim/module.make | 10 + abc_with_bb_support/src/opt/sim/sim.h | 233 + abc_with_bb_support/src/opt/sim/simMan.c | 288 + abc_with_bb_support/src/opt/sim/simSat.c | 48 + abc_with_bb_support/src/opt/sim/simSeq.c | 171 + abc_with_bb_support/src/opt/sim/simSupp.c | 597 + abc_with_bb_support/src/opt/sim/simSwitch.c | 107 + abc_with_bb_support/src/opt/sim/simSym.c | 142 + abc_with_bb_support/src/opt/sim/simSymSat.c | 199 + abc_with_bb_support/src/opt/sim/simSymSim.c | 173 + abc_with_bb_support/src/opt/sim/simSymStr.c | 488 + abc_with_bb_support/src/opt/sim/simUtils.c | 711 + abc_with_bb_support/src/phys/place/Makefile | 30 + abc_with_bb_support/src/phys/place/README | 50 + abc_with_bb_support/src/phys/place/hpwl | 57 + .../src/phys/place/libhmetis.h | 31 + .../src/phys/place/module.make | 10 + .../src/phys/place/place_base.c | 345 + .../src/phys/place/place_base.h | 137 + .../src/phys/place/place_bin.c | 277 + .../src/phys/place/place_genqp.c | 309 + .../src/phys/place/place_gordian.c | 160 + .../src/phys/place/place_gordian.h | 78 + .../src/phys/place/place_inc.c | 106 + abc_with_bb_support/src/phys/place/place_io.c | 94 + .../src/phys/place/place_legalize.c | 23 + .../src/phys/place/place_pads.c | 141 + .../src/phys/place/place_partition.c | 1135 ++ .../src/phys/place/place_qpsolver.c | 1270 ++ .../src/phys/place/place_qpsolver.h | 140 + .../src/phys/place/place_test.c | 360 + abc_with_bb_support/src/sat/bsat/module.make | 6 + abc_with_bb_support/src/sat/bsat/satInter.c | 991 ++ abc_with_bb_support/src/sat/bsat/satMem.c | 527 + abc_with_bb_support/src/sat/bsat/satMem.h | 78 + abc_with_bb_support/src/sat/bsat/satSolver.c | 1354 ++ abc_with_bb_support/src/sat/bsat/satSolver.h | 189 + abc_with_bb_support/src/sat/bsat/satStore.c | 437 + abc_with_bb_support/src/sat/bsat/satStore.h | 137 + abc_with_bb_support/src/sat/bsat/satTrace.c | 109 + abc_with_bb_support/src/sat/bsat/satUtil.c | 232 + abc_with_bb_support/src/sat/bsat/satVec.h | 83 + abc_with_bb_support/src/sat/csat/csat_apis.c | 769 + abc_with_bb_support/src/sat/csat/csat_apis.h | 222 + abc_with_bb_support/src/sat/csat/module.make | 1 + abc_with_bb_support/src/sat/fraig/fraig.h | 267 + abc_with_bb_support/src/sat/fraig/fraigApi.c | 297 + .../src/sat/fraig/fraigCanon.c | 218 + .../src/sat/fraig/fraigChoice.c | 241 + .../src/sat/fraig/fraigFanout.c | 175 + abc_with_bb_support/src/sat/fraig/fraigFeed.c | 909 ++ abc_with_bb_support/src/sat/fraig/fraigInt.h | 451 + abc_with_bb_support/src/sat/fraig/fraigMan.c | 540 + abc_with_bb_support/src/sat/fraig/fraigMem.c | 246 + abc_with_bb_support/src/sat/fraig/fraigNode.c | 313 + .../src/sat/fraig/fraigPrime.c | 144 + abc_with_bb_support/src/sat/fraig/fraigSat.c | 1455 ++ .../src/sat/fraig/fraigTable.c | 657 + abc_with_bb_support/src/sat/fraig/fraigUtil.c | 1034 ++ abc_with_bb_support/src/sat/fraig/fraigVec.c | 545 + abc_with_bb_support/src/sat/fraig/module.make | 12 + abc_with_bb_support/src/sat/msat/module.make | 13 + abc_with_bb_support/src/sat/msat/msat.h | 172 + .../src/sat/msat/msatActivity.c | 160 + abc_with_bb_support/src/sat/msat/msatClause.c | 529 + .../src/sat/msat/msatClauseVec.c | 232 + abc_with_bb_support/src/sat/msat/msatInt.h | 306 + abc_with_bb_support/src/sat/msat/msatMem.c | 529 + abc_with_bb_support/src/sat/msat/msatOrderH.c | 405 + abc_with_bb_support/src/sat/msat/msatOrderJ.c | 472 + abc_with_bb_support/src/sat/msat/msatQueue.c | 157 + abc_with_bb_support/src/sat/msat/msatRead.c | 268 + .../src/sat/msat/msatSolverApi.c | 500 + .../src/sat/msat/msatSolverCore.c | 212 + .../src/sat/msat/msatSolverIo.c | 177 + .../src/sat/msat/msatSolverSearch.c | 629 + abc_with_bb_support/src/sat/msat/msatSort.c | 173 + abc_with_bb_support/src/sat/msat/msatVec.c | 495 + abc_with_bb_support/src/sat/proof/pr.c | 1263 ++ abc_with_bb_support/src/sat/proof/pr.h | 65 + abc_with_bb_support/src/sat/proof/stats.txt | 66 + abc_with_bb_support/todo.txt | 26 + 777 files changed, 372098 insertions(+), 2 deletions(-) create mode 100644 abc_with_bb_support/FOR_CYGWIN/libSupport.c create mode 100644 abc_with_bb_support/JAMIESON_TESTS/logsyn_techmap4.script create mode 100644 abc_with_bb_support/JAMIESON_TESTS/logsyn_techmap4_mini.script create mode 100644 abc_with_bb_support/JAMIESON_TESTS/lut_lib4.txt create mode 100644 abc_with_bb_support/JAMIESON_TESTS/mini_example1.4.blif create mode 100644 abc_with_bb_support/JAMIESON_TESTS/mini_example1.blif create mode 100644 abc_with_bb_support/JAMIESON_TESTS/mini_example2.4.blif create mode 100644 abc_with_bb_support/JAMIESON_TESTS/mini_example2.blif create mode 100644 abc_with_bb_support/JAMIESON_TESTS/sample.blif create mode 100644 abc_with_bb_support/JAMIESON_TESTS/sample2.blif create mode 100644 abc_with_bb_support/JAMIESON_TESTS/sample3.blif create mode 100644 abc_with_bb_support/JAMIESON_TESTS/sample4.blif create mode 100644 abc_with_bb_support/Makefile create mode 100644 abc_with_bb_support/abc.dsp create mode 100644 abc_with_bb_support/abc.dsw create mode 100644 abc_with_bb_support/abc.ncb create mode 100644 abc_with_bb_support/abc.rc create mode 100644 abc_with_bb_support/abc.suo create mode 100644 abc_with_bb_support/abc.vcproj create mode 100644 abc_with_bb_support/abclib.dsp create mode 100644 abc_with_bb_support/abclib.dsw create mode 100644 abc_with_bb_support/abctestlib.dsp create mode 100644 abc_with_bb_support/abctestlib.dsw create mode 100644 abc_with_bb_support/accum.blif create mode 100644 abc_with_bb_support/accum.v create mode 100644 abc_with_bb_support/copyright.txt create mode 100644 abc_with_bb_support/demo.c create mode 100644 abc_with_bb_support/depends.sh create mode 100644 abc_with_bb_support/readme create mode 100644 abc_with_bb_support/regtest.script create mode 100644 abc_with_bb_support/regtest_output.txt create mode 100644 abc_with_bb_support/simple.blif create mode 100644 abc_with_bb_support/simple.v create mode 100644 abc_with_bb_support/src/aig/aig/aig.h create mode 100644 abc_with_bb_support/src/aig/aig/aigCheck.c create mode 100644 abc_with_bb_support/src/aig/aig/aigDfs.c create mode 100644 abc_with_bb_support/src/aig/aig/aigFanout.c create mode 100644 abc_with_bb_support/src/aig/aig/aigMan.c create mode 100644 abc_with_bb_support/src/aig/aig/aigMem.c create mode 100644 abc_with_bb_support/src/aig/aig/aigMffc.c create mode 100644 abc_with_bb_support/src/aig/aig/aigObj.c create mode 100644 abc_with_bb_support/src/aig/aig/aigOper.c create mode 100644 abc_with_bb_support/src/aig/aig/aigOrder.c create mode 100644 abc_with_bb_support/src/aig/aig/aigPart.c create mode 100644 abc_with_bb_support/src/aig/aig/aigRepr.c create mode 100644 abc_with_bb_support/src/aig/aig/aigSeq.c create mode 100644 abc_with_bb_support/src/aig/aig/aigShow.c create mode 100644 abc_with_bb_support/src/aig/aig/aigTable.c create mode 100644 abc_with_bb_support/src/aig/aig/aigTiming.c create mode 100644 abc_with_bb_support/src/aig/aig/aigTruth.c create mode 100644 abc_with_bb_support/src/aig/aig/aigUtil.c create mode 100644 abc_with_bb_support/src/aig/aig/aigWin.c create mode 100644 abc_with_bb_support/src/aig/aig/aig_.c create mode 100644 abc_with_bb_support/src/aig/aig/module.make create mode 100644 abc_with_bb_support/src/aig/bdc/bdc.h create mode 100644 abc_with_bb_support/src/aig/bdc/bdcCore.c create mode 100644 abc_with_bb_support/src/aig/bdc/bdcDec.c create mode 100644 abc_with_bb_support/src/aig/bdc/bdcInt.h create mode 100644 abc_with_bb_support/src/aig/bdc/bdcTable.c create mode 100644 abc_with_bb_support/src/aig/bdc/bdc_.c create mode 100644 abc_with_bb_support/src/aig/bdc/module.make create mode 100644 abc_with_bb_support/src/aig/cnf/cnf.h create mode 100644 abc_with_bb_support/src/aig/cnf/cnfCore.c create mode 100644 abc_with_bb_support/src/aig/cnf/cnfCut.c create mode 100644 abc_with_bb_support/src/aig/cnf/cnfData.c create mode 100644 abc_with_bb_support/src/aig/cnf/cnfMan.c create mode 100644 abc_with_bb_support/src/aig/cnf/cnfMap.c create mode 100644 abc_with_bb_support/src/aig/cnf/cnfPost.c create mode 100644 abc_with_bb_support/src/aig/cnf/cnfUtil.c create mode 100644 abc_with_bb_support/src/aig/cnf/cnfWrite.c create mode 100644 abc_with_bb_support/src/aig/cnf/cnf_.c create mode 100644 abc_with_bb_support/src/aig/cnf/module.make create mode 100644 abc_with_bb_support/src/aig/csw/csw.h create mode 100644 abc_with_bb_support/src/aig/csw/cswCore.c create mode 100644 abc_with_bb_support/src/aig/csw/cswCut.c create mode 100644 abc_with_bb_support/src/aig/csw/cswInt.h create mode 100644 abc_with_bb_support/src/aig/csw/cswMan.c create mode 100644 abc_with_bb_support/src/aig/csw/cswTable.c create mode 100644 abc_with_bb_support/src/aig/csw/csw_.c create mode 100644 abc_with_bb_support/src/aig/csw/module.make create mode 100644 abc_with_bb_support/src/aig/dar/dar.h create mode 100644 abc_with_bb_support/src/aig/dar/darBalance.c create mode 100644 abc_with_bb_support/src/aig/dar/darCore.c create mode 100644 abc_with_bb_support/src/aig/dar/darCut.c create mode 100644 abc_with_bb_support/src/aig/dar/darData.c create mode 100644 abc_with_bb_support/src/aig/dar/darInt.h create mode 100644 abc_with_bb_support/src/aig/dar/darLib.c create mode 100644 abc_with_bb_support/src/aig/dar/darMan.c create mode 100644 abc_with_bb_support/src/aig/dar/darRefact.c create mode 100644 abc_with_bb_support/src/aig/dar/darResub.c create mode 100644 abc_with_bb_support/src/aig/dar/darScript.c create mode 100644 abc_with_bb_support/src/aig/dar/darTruth.c create mode 100644 abc_with_bb_support/src/aig/dar/dar_.c create mode 100644 abc_with_bb_support/src/aig/dar/module.make create mode 100644 abc_with_bb_support/src/aig/deco/deco.h create mode 100644 abc_with_bb_support/src/aig/deco/module.make create mode 100644 abc_with_bb_support/src/aig/ec/module.make create mode 100644 abc_with_bb_support/src/aig/fra/fra.h create mode 100644 abc_with_bb_support/src/aig/fra/fraCec.c create mode 100644 abc_with_bb_support/src/aig/fra/fraClass.c create mode 100644 abc_with_bb_support/src/aig/fra/fraCnf.c create mode 100644 abc_with_bb_support/src/aig/fra/fraCore.c create mode 100644 abc_with_bb_support/src/aig/fra/fraInd.c create mode 100644 abc_with_bb_support/src/aig/fra/fraMan.c create mode 100644 abc_with_bb_support/src/aig/fra/fraSat.c create mode 100644 abc_with_bb_support/src/aig/fra/fraSec.c create mode 100644 abc_with_bb_support/src/aig/fra/fraSim.c create mode 100644 abc_with_bb_support/src/aig/fra/fra_.c create mode 100644 abc_with_bb_support/src/aig/fra/module.make create mode 100644 abc_with_bb_support/src/aig/hop/cudd2.c create mode 100644 abc_with_bb_support/src/aig/hop/cudd2.h create mode 100644 abc_with_bb_support/src/aig/hop/hop.h create mode 100644 abc_with_bb_support/src/aig/hop/hopBalance.c create mode 100644 abc_with_bb_support/src/aig/hop/hopCheck.c create mode 100644 abc_with_bb_support/src/aig/hop/hopDfs.c create mode 100644 abc_with_bb_support/src/aig/hop/hopMan.c create mode 100644 abc_with_bb_support/src/aig/hop/hopMem.c create mode 100644 abc_with_bb_support/src/aig/hop/hopObj.c create mode 100644 abc_with_bb_support/src/aig/hop/hopOper.c create mode 100644 abc_with_bb_support/src/aig/hop/hopTable.c create mode 100644 abc_with_bb_support/src/aig/hop/hopUtil.c create mode 100644 abc_with_bb_support/src/aig/hop/hop_.c create mode 100644 abc_with_bb_support/src/aig/hop/module.make create mode 100644 abc_with_bb_support/src/aig/ivy/attr.h create mode 100644 abc_with_bb_support/src/aig/ivy/ivy.h create mode 100644 abc_with_bb_support/src/aig/ivy/ivyBalance.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyCanon.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyCheck.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyCut.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyCutTrav.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyDfs.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyDsd.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyFanout.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyFastMap.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyFraig.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyHaig.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyMan.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyMem.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyMulti.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyMulti8.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyObj.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyOper.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyResyn.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyRwr.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyRwrAlg.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivySeq.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyShow.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyTable.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivyUtil.c create mode 100644 abc_with_bb_support/src/aig/ivy/ivy_.c create mode 100644 abc_with_bb_support/src/aig/ivy/module.make create mode 100644 abc_with_bb_support/src/aig/kit/kit.h create mode 100644 abc_with_bb_support/src/aig/kit/kitBdd.c create mode 100644 abc_with_bb_support/src/aig/kit/kitDsd.c create mode 100644 abc_with_bb_support/src/aig/kit/kitFactor.c create mode 100644 abc_with_bb_support/src/aig/kit/kitGraph.c create mode 100644 abc_with_bb_support/src/aig/kit/kitHop.c create mode 100644 abc_with_bb_support/src/aig/kit/kitIsop.c create mode 100644 abc_with_bb_support/src/aig/kit/kitSop.c create mode 100644 abc_with_bb_support/src/aig/kit/kitTruth.c create mode 100644 abc_with_bb_support/src/aig/kit/kit_.c create mode 100644 abc_with_bb_support/src/aig/kit/module.make create mode 100644 abc_with_bb_support/src/aig/mem/mem.c create mode 100644 abc_with_bb_support/src/aig/mem/mem.h create mode 100644 abc_with_bb_support/src/aig/mem/module.make create mode 100644 abc_with_bb_support/src/aig/rwt/module.make create mode 100644 abc_with_bb_support/src/aig/rwt/rwt.h create mode 100644 abc_with_bb_support/src/aig/rwt/rwtDec.c create mode 100644 abc_with_bb_support/src/aig/rwt/rwtMan.c create mode 100644 abc_with_bb_support/src/aig/rwt/rwtUtil.c create mode 100644 abc_with_bb_support/src/base/abc/abc.h create mode 100644 abc_with_bb_support/src/base/abc/abcAig.c create mode 100644 abc_with_bb_support/src/base/abc/abcBlifMv.c create mode 100644 abc_with_bb_support/src/base/abc/abcCheck.c create mode 100644 abc_with_bb_support/src/base/abc/abcDfs.c create mode 100644 abc_with_bb_support/src/base/abc/abcFanio.c create mode 100644 abc_with_bb_support/src/base/abc/abcFunc.c create mode 100644 abc_with_bb_support/src/base/abc/abcHie.c create mode 100644 abc_with_bb_support/src/base/abc/abcInt.h create mode 100644 abc_with_bb_support/src/base/abc/abcLatch.c create mode 100644 abc_with_bb_support/src/base/abc/abcLib.c create mode 100644 abc_with_bb_support/src/base/abc/abcMinBase.c create mode 100644 abc_with_bb_support/src/base/abc/abcNames.c create mode 100644 abc_with_bb_support/src/base/abc/abcNetlist.c create mode 100644 abc_with_bb_support/src/base/abc/abcNtk.c create mode 100644 abc_with_bb_support/src/base/abc/abcObj.c create mode 100644 abc_with_bb_support/src/base/abc/abcRefs.c create mode 100644 abc_with_bb_support/src/base/abc/abcShow.c create mode 100644 abc_with_bb_support/src/base/abc/abcSop.c create mode 100644 abc_with_bb_support/src/base/abc/abcUtil.c create mode 100644 abc_with_bb_support/src/base/abc/abc_.c create mode 100644 abc_with_bb_support/src/base/abc/module.make create mode 100644 abc_with_bb_support/src/base/abci/abc.c create mode 100644 abc_with_bb_support/src/base/abci/abcAttach.c create mode 100644 abc_with_bb_support/src/base/abci/abcAuto.c create mode 100644 abc_with_bb_support/src/base/abci/abcBalance.c create mode 100644 abc_with_bb_support/src/base/abci/abcBmc.c create mode 100644 abc_with_bb_support/src/base/abci/abcCas.c create mode 100644 abc_with_bb_support/src/base/abci/abcClpBdd.c create mode 100644 abc_with_bb_support/src/base/abci/abcClpSop.c create mode 100644 abc_with_bb_support/src/base/abci/abcCut.c create mode 100644 abc_with_bb_support/src/base/abci/abcDar.c create mode 100644 abc_with_bb_support/src/base/abci/abcDebug.c create mode 100644 abc_with_bb_support/src/base/abci/abcDress.c create mode 100644 abc_with_bb_support/src/base/abci/abcDsd.c create mode 100644 abc_with_bb_support/src/base/abci/abcEspresso.c create mode 100644 abc_with_bb_support/src/base/abci/abcExtract.c create mode 100644 abc_with_bb_support/src/base/abci/abcFpga.c create mode 100644 abc_with_bb_support/src/base/abci/abcFpgaFast.c create mode 100644 abc_with_bb_support/src/base/abci/abcFraig.c create mode 100644 abc_with_bb_support/src/base/abci/abcFxu.c create mode 100644 abc_with_bb_support/src/base/abci/abcGen.c create mode 100644 abc_with_bb_support/src/base/abci/abcHaig.c create mode 100644 abc_with_bb_support/src/base/abci/abcIf.c create mode 100644 abc_with_bb_support/src/base/abci/abcIvy.c create mode 100644 abc_with_bb_support/src/base/abci/abcLut.c create mode 100644 abc_with_bb_support/src/base/abci/abcMap.c create mode 100644 abc_with_bb_support/src/base/abci/abcMeasure.c create mode 100644 abc_with_bb_support/src/base/abci/abcMini.c create mode 100644 abc_with_bb_support/src/base/abci/abcMiter.c create mode 100644 abc_with_bb_support/src/base/abci/abcMulti.c create mode 100644 abc_with_bb_support/src/base/abci/abcMv.c create mode 100644 abc_with_bb_support/src/base/abci/abcNtbdd.c create mode 100644 abc_with_bb_support/src/base/abci/abcOdc.c create mode 100644 abc_with_bb_support/src/base/abci/abcOrder.c create mode 100644 abc_with_bb_support/src/base/abci/abcPart.c create mode 100644 abc_with_bb_support/src/base/abci/abcPlace.c create mode 100644 abc_with_bb_support/src/base/abci/abcPrint.c create mode 100644 abc_with_bb_support/src/base/abci/abcProve.c create mode 100644 abc_with_bb_support/src/base/abci/abcQbf.c create mode 100644 abc_with_bb_support/src/base/abci/abcQuant.c create mode 100644 abc_with_bb_support/src/base/abci/abcRec.c create mode 100644 abc_with_bb_support/src/base/abci/abcReconv.c create mode 100644 abc_with_bb_support/src/base/abci/abcRefactor.c create mode 100644 abc_with_bb_support/src/base/abci/abcRenode.c create mode 100644 abc_with_bb_support/src/base/abci/abcReorder.c create mode 100644 abc_with_bb_support/src/base/abci/abcRestruct.c create mode 100644 abc_with_bb_support/src/base/abci/abcResub.c create mode 100644 abc_with_bb_support/src/base/abci/abcRewrite.c create mode 100644 abc_with_bb_support/src/base/abci/abcRr.c create mode 100644 abc_with_bb_support/src/base/abci/abcSat.c create mode 100644 abc_with_bb_support/src/base/abci/abcStrash.c create mode 100644 abc_with_bb_support/src/base/abci/abcSweep.c create mode 100644 abc_with_bb_support/src/base/abci/abcSymm.c create mode 100644 abc_with_bb_support/src/base/abci/abcTiming.c create mode 100644 abc_with_bb_support/src/base/abci/abcUnate.c create mode 100644 abc_with_bb_support/src/base/abci/abcUnreach.c create mode 100644 abc_with_bb_support/src/base/abci/abcVerify.c create mode 100644 abc_with_bb_support/src/base/abci/abcXsim.c create mode 100644 abc_with_bb_support/src/base/abci/abc_.c create mode 100644 abc_with_bb_support/src/base/abci/abc_new.h create mode 100644 abc_with_bb_support/src/base/abci/module.make create mode 100644 abc_with_bb_support/src/base/abci/xaaaa.c create mode 100644 abc_with_bb_support/src/base/cmd/cmd.c create mode 100644 abc_with_bb_support/src/base/cmd/cmd.h create mode 100644 abc_with_bb_support/src/base/cmd/cmdAlias.c create mode 100644 abc_with_bb_support/src/base/cmd/cmdApi.c create mode 100644 abc_with_bb_support/src/base/cmd/cmdFlag.c create mode 100644 abc_with_bb_support/src/base/cmd/cmdHist.c create mode 100644 abc_with_bb_support/src/base/cmd/cmdInt.h create mode 100644 abc_with_bb_support/src/base/cmd/cmdUtils.c create mode 100644 abc_with_bb_support/src/base/cmd/module.make create mode 100644 abc_with_bb_support/src/base/func/funcBlifMv.c create mode 100644 abc_with_bb_support/src/base/io/io.c create mode 100644 abc_with_bb_support/src/base/io/io.h create mode 100644 abc_with_bb_support/src/base/io/ioInt.h create mode 100644 abc_with_bb_support/src/base/io/ioReadAiger.c create mode 100644 abc_with_bb_support/src/base/io/ioReadBaf.c create mode 100644 abc_with_bb_support/src/base/io/ioReadBench.c create mode 100644 abc_with_bb_support/src/base/io/ioReadBlif.c create mode 100644 abc_with_bb_support/src/base/io/ioReadBlifAig.c create mode 100644 abc_with_bb_support/src/base/io/ioReadBlifMv.c create mode 100644 abc_with_bb_support/src/base/io/ioReadDsd.c create mode 100644 abc_with_bb_support/src/base/io/ioReadEdif.c create mode 100644 abc_with_bb_support/src/base/io/ioReadEqn.c create mode 100644 abc_with_bb_support/src/base/io/ioReadPla.c create mode 100644 abc_with_bb_support/src/base/io/ioReadVerilog.c create mode 100644 abc_with_bb_support/src/base/io/ioUtil.c create mode 100644 abc_with_bb_support/src/base/io/ioWriteAiger.c create mode 100644 abc_with_bb_support/src/base/io/ioWriteBaf.c create mode 100644 abc_with_bb_support/src/base/io/ioWriteBench.c create mode 100644 abc_with_bb_support/src/base/io/ioWriteBlif.c create mode 100644 abc_with_bb_support/src/base/io/ioWriteBlifMv.c create mode 100644 abc_with_bb_support/src/base/io/ioWriteCnf.c create mode 100644 abc_with_bb_support/src/base/io/ioWriteDot.c create mode 100644 abc_with_bb_support/src/base/io/ioWriteEqn.c create mode 100644 abc_with_bb_support/src/base/io/ioWriteGml.c create mode 100644 abc_with_bb_support/src/base/io/ioWriteList.c create mode 100644 abc_with_bb_support/src/base/io/ioWritePla.c create mode 100644 abc_with_bb_support/src/base/io/ioWriteVerilog.c create mode 100644 abc_with_bb_support/src/base/io/ioWriteVerilog.zip create mode 100644 abc_with_bb_support/src/base/io/io_.c create mode 100644 abc_with_bb_support/src/base/io/module.make create mode 100644 abc_with_bb_support/src/base/main/libSupport.c create mode 100644 abc_with_bb_support/src/base/main/main.c create mode 100644 abc_with_bb_support/src/base/main/main.h create mode 100644 abc_with_bb_support/src/base/main/mainFrame.c create mode 100644 abc_with_bb_support/src/base/main/mainInit.c create mode 100644 abc_with_bb_support/src/base/main/mainInt.h create mode 100644 abc_with_bb_support/src/base/main/mainUtils.c create mode 100644 abc_with_bb_support/src/base/main/module.make create mode 100644 abc_with_bb_support/src/base/seq/module.make create mode 100644 abc_with_bb_support/src/base/seq/seq.h create mode 100644 abc_with_bb_support/src/base/seq/seqAigCore.c create mode 100644 abc_with_bb_support/src/base/seq/seqAigIter.c create mode 100644 abc_with_bb_support/src/base/seq/seqCreate.c create mode 100644 abc_with_bb_support/src/base/seq/seqFpgaCore.c create mode 100644 abc_with_bb_support/src/base/seq/seqFpgaIter.c create mode 100644 abc_with_bb_support/src/base/seq/seqInt.h create mode 100644 abc_with_bb_support/src/base/seq/seqLatch.c create mode 100644 abc_with_bb_support/src/base/seq/seqMan.c create mode 100644 abc_with_bb_support/src/base/seq/seqMapCore.c create mode 100644 abc_with_bb_support/src/base/seq/seqMapIter.c create mode 100644 abc_with_bb_support/src/base/seq/seqMaxMeanCycle.c create mode 100644 abc_with_bb_support/src/base/seq/seqRetCore.c create mode 100644 abc_with_bb_support/src/base/seq/seqRetIter.c create mode 100644 abc_with_bb_support/src/base/seq/seqShare.c create mode 100644 abc_with_bb_support/src/base/seq/seqUtil.c create mode 100644 abc_with_bb_support/src/base/temp.c create mode 100644 abc_with_bb_support/src/base/ver/module.make create mode 100644 abc_with_bb_support/src/base/ver/ver.h create mode 100644 abc_with_bb_support/src/base/ver/verCore.c create mode 100644 abc_with_bb_support/src/base/ver/verCore.zip create mode 100644 abc_with_bb_support/src/base/ver/verFormula.c create mode 100644 abc_with_bb_support/src/base/ver/verParse.c create mode 100644 abc_with_bb_support/src/base/ver/verStream.c create mode 100644 abc_with_bb_support/src/base/ver/verWords.c create mode 100644 abc_with_bb_support/src/base/ver/ver_.c create mode 100644 abc_with_bb_support/src/bdd/cas/cas.h create mode 100644 abc_with_bb_support/src/bdd/cas/casCore.c create mode 100644 abc_with_bb_support/src/bdd/cas/casDec.c create mode 100644 abc_with_bb_support/src/bdd/cas/module.make create mode 100644 abc_with_bb_support/src/bdd/cudd/cuBdd.make create mode 100644 abc_with_bb_support/src/bdd/cudd/cudd.h create mode 100644 abc_with_bb_support/src/bdd/cudd/cudd.make create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddAPI.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddAddAbs.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddAddApply.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddAddFind.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddAddInv.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddAddIte.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddAddNeg.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddAddWalsh.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddAndAbs.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddAnneal.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddApa.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddApprox.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddBddAbs.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddBddCorr.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddBddIte.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddBridge.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddCache.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddCheck.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddClip.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddCof.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddCompose.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddDecomp.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddEssent.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddExact.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddExport.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddGenCof.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddGenetic.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddGroup.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddHarwell.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddInit.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddInt.h create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddInteract.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddLCache.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddLevelQ.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddLinear.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddLiteral.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddMatMult.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddPriority.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddRead.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddRef.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddReorder.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddSat.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddSign.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddSolve.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddSplit.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddSubsetHB.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddSubsetSP.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddSymmetry.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddTable.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddUtil.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddWindow.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddZddCount.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddZddFuncs.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddZddGroup.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddZddIsop.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddZddLin.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddZddMisc.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddZddPort.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddZddReord.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddZddSetop.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddZddSymm.c create mode 100644 abc_with_bb_support/src/bdd/cudd/cuddZddUtil.c create mode 100644 abc_with_bb_support/src/bdd/cudd/module.make create mode 100644 abc_with_bb_support/src/bdd/cudd/r7x8.1.mat create mode 100644 abc_with_bb_support/src/bdd/cudd/testcudd.c create mode 100644 abc_with_bb_support/src/bdd/dsd/dsd.h create mode 100644 abc_with_bb_support/src/bdd/dsd/dsdApi.c create mode 100644 abc_with_bb_support/src/bdd/dsd/dsdCheck.c create mode 100644 abc_with_bb_support/src/bdd/dsd/dsdInt.h create mode 100644 abc_with_bb_support/src/bdd/dsd/dsdLocal.c create mode 100644 abc_with_bb_support/src/bdd/dsd/dsdMan.c create mode 100644 abc_with_bb_support/src/bdd/dsd/dsdProc.c create mode 100644 abc_with_bb_support/src/bdd/dsd/dsdTree.c create mode 100644 abc_with_bb_support/src/bdd/dsd/module.make create mode 100644 abc_with_bb_support/src/bdd/epd/epd.c create mode 100644 abc_with_bb_support/src/bdd/epd/epd.h create mode 100644 abc_with_bb_support/src/bdd/epd/module.make create mode 100644 abc_with_bb_support/src/bdd/mtr/module.make create mode 100644 abc_with_bb_support/src/bdd/mtr/mtr.h create mode 100644 abc_with_bb_support/src/bdd/mtr/mtrBasic.c create mode 100644 abc_with_bb_support/src/bdd/mtr/mtrGroup.c create mode 100644 abc_with_bb_support/src/bdd/mtr/mtrInt.h create mode 100644 abc_with_bb_support/src/bdd/parse/module.make create mode 100644 abc_with_bb_support/src/bdd/parse/parse.h create mode 100644 abc_with_bb_support/src/bdd/parse/parseCore.c create mode 100644 abc_with_bb_support/src/bdd/parse/parseEqn.c create mode 100644 abc_with_bb_support/src/bdd/parse/parseInt.h create mode 100644 abc_with_bb_support/src/bdd/parse/parseStack.c create mode 100644 abc_with_bb_support/src/bdd/reo/module.make create mode 100644 abc_with_bb_support/src/bdd/reo/reo.h create mode 100644 abc_with_bb_support/src/bdd/reo/reoApi.c create mode 100644 abc_with_bb_support/src/bdd/reo/reoCore.c create mode 100644 abc_with_bb_support/src/bdd/reo/reoProfile.c create mode 100644 abc_with_bb_support/src/bdd/reo/reoSift.c create mode 100644 abc_with_bb_support/src/bdd/reo/reoSwap.c create mode 100644 abc_with_bb_support/src/bdd/reo/reoTest.c create mode 100644 abc_with_bb_support/src/bdd/reo/reoTransfer.c create mode 100644 abc_with_bb_support/src/bdd/reo/reoUnits.c create mode 100644 abc_with_bb_support/src/generic.c create mode 100644 abc_with_bb_support/src/generic.h create mode 100644 abc_with_bb_support/src/map/fpga/fpga.c create mode 100644 abc_with_bb_support/src/map/fpga/fpga.h create mode 100644 abc_with_bb_support/src/map/fpga/fpgaCore.c create mode 100644 abc_with_bb_support/src/map/fpga/fpgaCreate.c create mode 100644 abc_with_bb_support/src/map/fpga/fpgaCut.c create mode 100644 abc_with_bb_support/src/map/fpga/fpgaCutUtils.c create mode 100644 abc_with_bb_support/src/map/fpga/fpgaFanout.c create mode 100644 abc_with_bb_support/src/map/fpga/fpgaGENERIC.c create mode 100644 abc_with_bb_support/src/map/fpga/fpgaInt.h create mode 100644 abc_with_bb_support/src/map/fpga/fpgaLib.c create mode 100644 abc_with_bb_support/src/map/fpga/fpgaMatch.c create mode 100644 abc_with_bb_support/src/map/fpga/fpgaSwitch.c create mode 100644 abc_with_bb_support/src/map/fpga/fpgaTime.c create mode 100644 abc_with_bb_support/src/map/fpga/fpgaTruth.c create mode 100644 abc_with_bb_support/src/map/fpga/fpgaUtils.c create mode 100644 abc_with_bb_support/src/map/fpga/fpgaVec.c create mode 100644 abc_with_bb_support/src/map/fpga/module.make create mode 100644 abc_with_bb_support/src/map/if/if.h create mode 100644 abc_with_bb_support/src/map/if/ifCore.c create mode 100644 abc_with_bb_support/src/map/if/ifCut.c create mode 100644 abc_with_bb_support/src/map/if/ifMan.c create mode 100644 abc_with_bb_support/src/map/if/ifMap.c create mode 100644 abc_with_bb_support/src/map/if/ifReduce.c create mode 100644 abc_with_bb_support/src/map/if/ifSeq.c create mode 100644 abc_with_bb_support/src/map/if/ifTime.c create mode 100644 abc_with_bb_support/src/map/if/ifTruth.c create mode 100644 abc_with_bb_support/src/map/if/ifUtil.c create mode 100644 abc_with_bb_support/src/map/if/if_.c create mode 100644 abc_with_bb_support/src/map/if/module.make create mode 100644 abc_with_bb_support/src/map/mapper/mapper.c create mode 100644 abc_with_bb_support/src/map/mapper/mapper.h create mode 100644 abc_with_bb_support/src/map/mapper/mapperCanon.c create mode 100644 abc_with_bb_support/src/map/mapper/mapperCore.c create mode 100644 abc_with_bb_support/src/map/mapper/mapperCreate.c create mode 100644 abc_with_bb_support/src/map/mapper/mapperCut.c create mode 100644 abc_with_bb_support/src/map/mapper/mapperCutUtils.c create mode 100644 abc_with_bb_support/src/map/mapper/mapperFanout.c create mode 100644 abc_with_bb_support/src/map/mapper/mapperGENERIC.c create mode 100644 abc_with_bb_support/src/map/mapper/mapperInt.h create mode 100644 abc_with_bb_support/src/map/mapper/mapperLib.c create mode 100644 abc_with_bb_support/src/map/mapper/mapperMatch.c create mode 100644 abc_with_bb_support/src/map/mapper/mapperRefs.c create mode 100644 abc_with_bb_support/src/map/mapper/mapperSuper.c create mode 100644 abc_with_bb_support/src/map/mapper/mapperSwitch.c create mode 100644 abc_with_bb_support/src/map/mapper/mapperTable.c create mode 100644 abc_with_bb_support/src/map/mapper/mapperTime.c create mode 100644 abc_with_bb_support/src/map/mapper/mapperTree.c create mode 100644 abc_with_bb_support/src/map/mapper/mapperTruth.c create mode 100644 abc_with_bb_support/src/map/mapper/mapperUtils.c create mode 100644 abc_with_bb_support/src/map/mapper/mapperVec.c create mode 100644 abc_with_bb_support/src/map/mapper/module.make create mode 100644 abc_with_bb_support/src/map/mio/mio.c create mode 100644 abc_with_bb_support/src/map/mio/mio.h create mode 100644 abc_with_bb_support/src/map/mio/mioApi.c create mode 100644 abc_with_bb_support/src/map/mio/mioFunc.c create mode 100644 abc_with_bb_support/src/map/mio/mioGENERIC.c create mode 100644 abc_with_bb_support/src/map/mio/mioInt.h create mode 100644 abc_with_bb_support/src/map/mio/mioRead.c create mode 100644 abc_with_bb_support/src/map/mio/mioUtils.c create mode 100644 abc_with_bb_support/src/map/mio/module.make create mode 100644 abc_with_bb_support/src/map/super/module.make create mode 100644 abc_with_bb_support/src/map/super/super.c create mode 100644 abc_with_bb_support/src/map/super/super.h create mode 100644 abc_with_bb_support/src/map/super/superAnd.c create mode 100644 abc_with_bb_support/src/map/super/superGENERIC.c create mode 100644 abc_with_bb_support/src/map/super/superGate.c create mode 100644 abc_with_bb_support/src/map/super/superInt.h create mode 100644 abc_with_bb_support/src/map/super/superWrite.c create mode 100644 abc_with_bb_support/src/misc/espresso/cofactor.c create mode 100644 abc_with_bb_support/src/misc/espresso/cols.c create mode 100644 abc_with_bb_support/src/misc/espresso/compl.c create mode 100644 abc_with_bb_support/src/misc/espresso/contain.c create mode 100644 abc_with_bb_support/src/misc/espresso/cubehack.c create mode 100644 abc_with_bb_support/src/misc/espresso/cubestr.c create mode 100644 abc_with_bb_support/src/misc/espresso/cvrin.c create mode 100644 abc_with_bb_support/src/misc/espresso/cvrm.c create mode 100644 abc_with_bb_support/src/misc/espresso/cvrmisc.c create mode 100644 abc_with_bb_support/src/misc/espresso/cvrout.c create mode 100644 abc_with_bb_support/src/misc/espresso/dominate.c create mode 100644 abc_with_bb_support/src/misc/espresso/equiv.c create mode 100644 abc_with_bb_support/src/misc/espresso/espresso.c create mode 100644 abc_with_bb_support/src/misc/espresso/espresso.h create mode 100644 abc_with_bb_support/src/misc/espresso/essen.c create mode 100644 abc_with_bb_support/src/misc/espresso/exact.c create mode 100644 abc_with_bb_support/src/misc/espresso/expand.c create mode 100644 abc_with_bb_support/src/misc/espresso/gasp.c create mode 100644 abc_with_bb_support/src/misc/espresso/gimpel.c create mode 100644 abc_with_bb_support/src/misc/espresso/globals.c create mode 100644 abc_with_bb_support/src/misc/espresso/hack.c create mode 100644 abc_with_bb_support/src/misc/espresso/indep.c create mode 100644 abc_with_bb_support/src/misc/espresso/irred.c create mode 100644 abc_with_bb_support/src/misc/espresso/main.c create mode 100644 abc_with_bb_support/src/misc/espresso/main.h create mode 100644 abc_with_bb_support/src/misc/espresso/map.c create mode 100644 abc_with_bb_support/src/misc/espresso/matrix.c create mode 100644 abc_with_bb_support/src/misc/espresso/mincov.c create mode 100644 abc_with_bb_support/src/misc/espresso/mincov.h create mode 100644 abc_with_bb_support/src/misc/espresso/mincov_int.h create mode 100644 abc_with_bb_support/src/misc/espresso/module.make create mode 100644 abc_with_bb_support/src/misc/espresso/opo.c create mode 100644 abc_with_bb_support/src/misc/espresso/pair.c create mode 100644 abc_with_bb_support/src/misc/espresso/part.c create mode 100644 abc_with_bb_support/src/misc/espresso/primes.c create mode 100644 abc_with_bb_support/src/misc/espresso/reduce.c create mode 100644 abc_with_bb_support/src/misc/espresso/rows.c create mode 100644 abc_with_bb_support/src/misc/espresso/set.c create mode 100644 abc_with_bb_support/src/misc/espresso/setc.c create mode 100644 abc_with_bb_support/src/misc/espresso/sharp.c create mode 100644 abc_with_bb_support/src/misc/espresso/sminterf.c create mode 100644 abc_with_bb_support/src/misc/espresso/solution.c create mode 100644 abc_with_bb_support/src/misc/espresso/sparse.c create mode 100644 abc_with_bb_support/src/misc/espresso/sparse.h create mode 100644 abc_with_bb_support/src/misc/espresso/sparse_int.h create mode 100644 abc_with_bb_support/src/misc/espresso/unate.c create mode 100644 abc_with_bb_support/src/misc/espresso/util_old.h create mode 100644 abc_with_bb_support/src/misc/espresso/verify.c create mode 100644 abc_with_bb_support/src/misc/extra/extra.h create mode 100644 abc_with_bb_support/src/misc/extra/extraBddAuto.c create mode 100644 abc_with_bb_support/src/misc/extra/extraBddCas.c create mode 100644 abc_with_bb_support/src/misc/extra/extraBddKmap.c create mode 100644 abc_with_bb_support/src/misc/extra/extraBddMisc.c create mode 100644 abc_with_bb_support/src/misc/extra/extraBddSymm.c create mode 100644 abc_with_bb_support/src/misc/extra/extraBddUnate.c create mode 100644 abc_with_bb_support/src/misc/extra/extraUtilBitMatrix.c create mode 100644 abc_with_bb_support/src/misc/extra/extraUtilCanon.c create mode 100644 abc_with_bb_support/src/misc/extra/extraUtilFile.c create mode 100644 abc_with_bb_support/src/misc/extra/extraUtilMemory.c create mode 100644 abc_with_bb_support/src/misc/extra/extraUtilMisc.c create mode 100644 abc_with_bb_support/src/misc/extra/extraUtilProgress.c create mode 100644 abc_with_bb_support/src/misc/extra/extraUtilReader.c create mode 100644 abc_with_bb_support/src/misc/extra/extraUtilTruth.c create mode 100644 abc_with_bb_support/src/misc/extra/extraUtilUtil.c create mode 100644 abc_with_bb_support/src/misc/extra/module.make create mode 100644 abc_with_bb_support/src/misc/hash/hash.h create mode 100644 abc_with_bb_support/src/misc/hash/hashFlt.h create mode 100644 abc_with_bb_support/src/misc/hash/hashInt.h create mode 100644 abc_with_bb_support/src/misc/hash/hashPtr.h create mode 100644 abc_with_bb_support/src/misc/hash/module.make create mode 100644 abc_with_bb_support/src/misc/mvc/module.make create mode 100644 abc_with_bb_support/src/misc/mvc/mvc.c create mode 100644 abc_with_bb_support/src/misc/mvc/mvc.h create mode 100644 abc_with_bb_support/src/misc/mvc/mvcApi.c create mode 100644 abc_with_bb_support/src/misc/mvc/mvcCompare.c create mode 100644 abc_with_bb_support/src/misc/mvc/mvcContain.c create mode 100644 abc_with_bb_support/src/misc/mvc/mvcCover.c create mode 100644 abc_with_bb_support/src/misc/mvc/mvcCube.c create mode 100644 abc_with_bb_support/src/misc/mvc/mvcDivide.c create mode 100644 abc_with_bb_support/src/misc/mvc/mvcDivisor.c create mode 100644 abc_with_bb_support/src/misc/mvc/mvcList.c create mode 100644 abc_with_bb_support/src/misc/mvc/mvcLits.c create mode 100644 abc_with_bb_support/src/misc/mvc/mvcMan.c create mode 100644 abc_with_bb_support/src/misc/mvc/mvcOpAlg.c create mode 100644 abc_with_bb_support/src/misc/mvc/mvcOpBool.c create mode 100644 abc_with_bb_support/src/misc/mvc/mvcPrint.c create mode 100644 abc_with_bb_support/src/misc/mvc/mvcSort.c create mode 100644 abc_with_bb_support/src/misc/mvc/mvcUtils.c create mode 100644 abc_with_bb_support/src/misc/nm/module.make create mode 100644 abc_with_bb_support/src/misc/nm/nm.h create mode 100644 abc_with_bb_support/src/misc/nm/nmApi.c create mode 100644 abc_with_bb_support/src/misc/nm/nmInt.h create mode 100644 abc_with_bb_support/src/misc/nm/nmTable.c create mode 100644 abc_with_bb_support/src/misc/st/module.make create mode 100644 abc_with_bb_support/src/misc/st/st.c create mode 100644 abc_with_bb_support/src/misc/st/st.h create mode 100644 abc_with_bb_support/src/misc/st/stmm.c create mode 100644 abc_with_bb_support/src/misc/st/stmm.h create mode 100644 abc_with_bb_support/src/misc/util/leaks.h create mode 100644 abc_with_bb_support/src/misc/util/module.make create mode 100644 abc_with_bb_support/src/misc/util/stdlib_hack.h create mode 100644 abc_with_bb_support/src/misc/util/util_hack.h create mode 100644 abc_with_bb_support/src/misc/vec/0005-Removed-an-unnecessary-dereference.patch create mode 100644 abc_with_bb_support/src/misc/vec/module.make create mode 100644 abc_with_bb_support/src/misc/vec/vec.h create mode 100644 abc_with_bb_support/src/misc/vec/vecAtt.h create mode 100644 abc_with_bb_support/src/misc/vec/vecFlt.h create mode 100644 abc_with_bb_support/src/misc/vec/vecInt.h create mode 100644 abc_with_bb_support/src/misc/vec/vecPtr.h create mode 100644 abc_with_bb_support/src/misc/vec/vecStr.h create mode 100644 abc_with_bb_support/src/misc/vec/vecVec.h create mode 100644 abc_with_bb_support/src/opt/cut/abcCut.c create mode 100644 abc_with_bb_support/src/opt/cut/cut.h create mode 100644 abc_with_bb_support/src/opt/cut/cutApi.c create mode 100644 abc_with_bb_support/src/opt/cut/cutCut.c create mode 100644 abc_with_bb_support/src/opt/cut/cutExpand.c create mode 100644 abc_with_bb_support/src/opt/cut/cutInt.h create mode 100644 abc_with_bb_support/src/opt/cut/cutList.h create mode 100644 abc_with_bb_support/src/opt/cut/cutMan.c create mode 100644 abc_with_bb_support/src/opt/cut/cutMerge.c create mode 100644 abc_with_bb_support/src/opt/cut/cutNode.c create mode 100644 abc_with_bb_support/src/opt/cut/cutOracle.c create mode 100644 abc_with_bb_support/src/opt/cut/cutPre22.c create mode 100644 abc_with_bb_support/src/opt/cut/cutSeq.c create mode 100644 abc_with_bb_support/src/opt/cut/cutTruth.c create mode 100644 abc_with_bb_support/src/opt/cut/module.make create mode 100644 abc_with_bb_support/src/opt/dec/dec.h create mode 100644 abc_with_bb_support/src/opt/dec/decAbc.c create mode 100644 abc_with_bb_support/src/opt/dec/decFactor.c create mode 100644 abc_with_bb_support/src/opt/dec/decMan.c create mode 100644 abc_with_bb_support/src/opt/dec/decPrint.c create mode 100644 abc_with_bb_support/src/opt/dec/decUtil.c create mode 100644 abc_with_bb_support/src/opt/dec/module.make create mode 100644 abc_with_bb_support/src/opt/fxu/fxu.c create mode 100644 abc_with_bb_support/src/opt/fxu/fxu.h create mode 100644 abc_with_bb_support/src/opt/fxu/fxuCreate.c create mode 100644 abc_with_bb_support/src/opt/fxu/fxuHeapD.c create mode 100644 abc_with_bb_support/src/opt/fxu/fxuHeapS.c create mode 100644 abc_with_bb_support/src/opt/fxu/fxuInt.h create mode 100644 abc_with_bb_support/src/opt/fxu/fxuList.c create mode 100644 abc_with_bb_support/src/opt/fxu/fxuMatrix.c create mode 100644 abc_with_bb_support/src/opt/fxu/fxuPair.c create mode 100644 abc_with_bb_support/src/opt/fxu/fxuPrint.c create mode 100644 abc_with_bb_support/src/opt/fxu/fxuReduce.c create mode 100644 abc_with_bb_support/src/opt/fxu/fxuSelect.c create mode 100644 abc_with_bb_support/src/opt/fxu/fxuSingle.c create mode 100644 abc_with_bb_support/src/opt/fxu/fxuUpdate.c create mode 100644 abc_with_bb_support/src/opt/fxu/module.make create mode 100644 abc_with_bb_support/src/opt/lpk/lpk.h create mode 100644 abc_with_bb_support/src/opt/lpk/lpkCore.c create mode 100644 abc_with_bb_support/src/opt/lpk/lpkCut.c create mode 100644 abc_with_bb_support/src/opt/lpk/lpkInt.h create mode 100644 abc_with_bb_support/src/opt/lpk/lpkMan.c create mode 100644 abc_with_bb_support/src/opt/lpk/lpkMap.c create mode 100644 abc_with_bb_support/src/opt/lpk/lpkMulti.c create mode 100644 abc_with_bb_support/src/opt/lpk/lpkMux.c create mode 100644 abc_with_bb_support/src/opt/lpk/lpkSets.c create mode 100644 abc_with_bb_support/src/opt/lpk/lpk_.c create mode 100644 abc_with_bb_support/src/opt/lpk/module.make create mode 100644 abc_with_bb_support/src/opt/res/module.make create mode 100644 abc_with_bb_support/src/opt/res/res.h create mode 100644 abc_with_bb_support/src/opt/res/resCore.c create mode 100644 abc_with_bb_support/src/opt/res/resDivs.c create mode 100644 abc_with_bb_support/src/opt/res/resFilter.c create mode 100644 abc_with_bb_support/src/opt/res/resInt.h create mode 100644 abc_with_bb_support/src/opt/res/resSat.c create mode 100644 abc_with_bb_support/src/opt/res/resSim.c create mode 100644 abc_with_bb_support/src/opt/res/resSim_old.c create mode 100644 abc_with_bb_support/src/opt/res/resStrash.c create mode 100644 abc_with_bb_support/src/opt/res/resWin.c create mode 100644 abc_with_bb_support/src/opt/res/res_.c create mode 100644 abc_with_bb_support/src/opt/ret/module.make create mode 100644 abc_with_bb_support/src/opt/ret/retArea.c create mode 100644 abc_with_bb_support/src/opt/ret/retCore.c create mode 100644 abc_with_bb_support/src/opt/ret/retDelay.c create mode 100644 abc_with_bb_support/src/opt/ret/retFlow.c create mode 100644 abc_with_bb_support/src/opt/ret/retIncrem.c create mode 100644 abc_with_bb_support/src/opt/ret/retInit.c create mode 100644 abc_with_bb_support/src/opt/ret/retInt.h create mode 100644 abc_with_bb_support/src/opt/ret/retLvalue.c create mode 100644 abc_with_bb_support/src/opt/ret/ret_.c create mode 100644 abc_with_bb_support/src/opt/rwr/module.make create mode 100644 abc_with_bb_support/src/opt/rwr/rwr.h create mode 100644 abc_with_bb_support/src/opt/rwr/rwrDec.c create mode 100644 abc_with_bb_support/src/opt/rwr/rwrEva.c create mode 100644 abc_with_bb_support/src/opt/rwr/rwrExp.c create mode 100644 abc_with_bb_support/src/opt/rwr/rwrLib.c create mode 100644 abc_with_bb_support/src/opt/rwr/rwrMan.c create mode 100644 abc_with_bb_support/src/opt/rwr/rwrPrint.c create mode 100644 abc_with_bb_support/src/opt/rwr/rwrTemp.c create mode 100644 abc_with_bb_support/src/opt/rwr/rwrUtil.c create mode 100644 abc_with_bb_support/src/opt/sim/module.make create mode 100644 abc_with_bb_support/src/opt/sim/sim.h create mode 100644 abc_with_bb_support/src/opt/sim/simMan.c create mode 100644 abc_with_bb_support/src/opt/sim/simSat.c create mode 100644 abc_with_bb_support/src/opt/sim/simSeq.c create mode 100644 abc_with_bb_support/src/opt/sim/simSupp.c create mode 100644 abc_with_bb_support/src/opt/sim/simSwitch.c create mode 100644 abc_with_bb_support/src/opt/sim/simSym.c create mode 100644 abc_with_bb_support/src/opt/sim/simSymSat.c create mode 100644 abc_with_bb_support/src/opt/sim/simSymSim.c create mode 100644 abc_with_bb_support/src/opt/sim/simSymStr.c create mode 100644 abc_with_bb_support/src/opt/sim/simUtils.c create mode 100644 abc_with_bb_support/src/phys/place/Makefile create mode 100644 abc_with_bb_support/src/phys/place/README create mode 100644 abc_with_bb_support/src/phys/place/hpwl create mode 100644 abc_with_bb_support/src/phys/place/libhmetis.h create mode 100644 abc_with_bb_support/src/phys/place/module.make create mode 100644 abc_with_bb_support/src/phys/place/place_base.c create mode 100644 abc_with_bb_support/src/phys/place/place_base.h create mode 100644 abc_with_bb_support/src/phys/place/place_bin.c create mode 100644 abc_with_bb_support/src/phys/place/place_genqp.c create mode 100644 abc_with_bb_support/src/phys/place/place_gordian.c create mode 100644 abc_with_bb_support/src/phys/place/place_gordian.h create mode 100644 abc_with_bb_support/src/phys/place/place_inc.c create mode 100644 abc_with_bb_support/src/phys/place/place_io.c create mode 100644 abc_with_bb_support/src/phys/place/place_legalize.c create mode 100644 abc_with_bb_support/src/phys/place/place_pads.c create mode 100644 abc_with_bb_support/src/phys/place/place_partition.c create mode 100644 abc_with_bb_support/src/phys/place/place_qpsolver.c create mode 100644 abc_with_bb_support/src/phys/place/place_qpsolver.h create mode 100644 abc_with_bb_support/src/phys/place/place_test.c create mode 100644 abc_with_bb_support/src/sat/bsat/module.make create mode 100644 abc_with_bb_support/src/sat/bsat/satInter.c create mode 100644 abc_with_bb_support/src/sat/bsat/satMem.c create mode 100644 abc_with_bb_support/src/sat/bsat/satMem.h create mode 100644 abc_with_bb_support/src/sat/bsat/satSolver.c create mode 100644 abc_with_bb_support/src/sat/bsat/satSolver.h create mode 100644 abc_with_bb_support/src/sat/bsat/satStore.c create mode 100644 abc_with_bb_support/src/sat/bsat/satStore.h create mode 100644 abc_with_bb_support/src/sat/bsat/satTrace.c create mode 100644 abc_with_bb_support/src/sat/bsat/satUtil.c create mode 100644 abc_with_bb_support/src/sat/bsat/satVec.h create mode 100644 abc_with_bb_support/src/sat/csat/csat_apis.c create mode 100644 abc_with_bb_support/src/sat/csat/csat_apis.h create mode 100644 abc_with_bb_support/src/sat/csat/module.make create mode 100644 abc_with_bb_support/src/sat/fraig/fraig.h create mode 100644 abc_with_bb_support/src/sat/fraig/fraigApi.c create mode 100644 abc_with_bb_support/src/sat/fraig/fraigCanon.c create mode 100644 abc_with_bb_support/src/sat/fraig/fraigChoice.c create mode 100644 abc_with_bb_support/src/sat/fraig/fraigFanout.c create mode 100644 abc_with_bb_support/src/sat/fraig/fraigFeed.c create mode 100644 abc_with_bb_support/src/sat/fraig/fraigInt.h create mode 100644 abc_with_bb_support/src/sat/fraig/fraigMan.c create mode 100644 abc_with_bb_support/src/sat/fraig/fraigMem.c create mode 100644 abc_with_bb_support/src/sat/fraig/fraigNode.c create mode 100644 abc_with_bb_support/src/sat/fraig/fraigPrime.c create mode 100644 abc_with_bb_support/src/sat/fraig/fraigSat.c create mode 100644 abc_with_bb_support/src/sat/fraig/fraigTable.c create mode 100644 abc_with_bb_support/src/sat/fraig/fraigUtil.c create mode 100644 abc_with_bb_support/src/sat/fraig/fraigVec.c create mode 100644 abc_with_bb_support/src/sat/fraig/module.make create mode 100644 abc_with_bb_support/src/sat/msat/module.make create mode 100644 abc_with_bb_support/src/sat/msat/msat.h create mode 100644 abc_with_bb_support/src/sat/msat/msatActivity.c create mode 100644 abc_with_bb_support/src/sat/msat/msatClause.c create mode 100644 abc_with_bb_support/src/sat/msat/msatClauseVec.c create mode 100644 abc_with_bb_support/src/sat/msat/msatInt.h create mode 100644 abc_with_bb_support/src/sat/msat/msatMem.c create mode 100644 abc_with_bb_support/src/sat/msat/msatOrderH.c create mode 100644 abc_with_bb_support/src/sat/msat/msatOrderJ.c create mode 100644 abc_with_bb_support/src/sat/msat/msatQueue.c create mode 100644 abc_with_bb_support/src/sat/msat/msatRead.c create mode 100644 abc_with_bb_support/src/sat/msat/msatSolverApi.c create mode 100644 abc_with_bb_support/src/sat/msat/msatSolverCore.c create mode 100644 abc_with_bb_support/src/sat/msat/msatSolverIo.c create mode 100644 abc_with_bb_support/src/sat/msat/msatSolverSearch.c create mode 100644 abc_with_bb_support/src/sat/msat/msatSort.c create mode 100644 abc_with_bb_support/src/sat/msat/msatVec.c create mode 100644 abc_with_bb_support/src/sat/proof/pr.c create mode 100644 abc_with_bb_support/src/sat/proof/pr.h create mode 100644 abc_with_bb_support/src/sat/proof/stats.txt create mode 100644 abc_with_bb_support/todo.txt diff --git a/Makefile b/Makefile index e6d831477..6f74eae7f 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ # Makefile to build CAD tools in OpenFPGA inspired by Verilog-to-Routing (VTR) Framework # ########################################################################################## -SUBDIRS = abc_with_bb_support ace2 vpr7_x2p yosys +SUBDIRS = ace2 vpr7_x2p yosys all: notifications subdirs @@ -27,7 +27,6 @@ packages: @ cd vpr && make packages clean: - @ cd abc_with_bb_support && make clean @ cd ace2 && make clean @ cd vpr7_x2p && make clean @ cd yosys && make clean diff --git a/abc_with_bb_support/FOR_CYGWIN/libSupport.c b/abc_with_bb_support/FOR_CYGWIN/libSupport.c new file mode 100644 index 000000000..e4a5ca63e --- /dev/null +++ b/abc_with_bb_support/FOR_CYGWIN/libSupport.c @@ -0,0 +1,193 @@ +/**CFile**************************************************************** + + FileName [libSupport.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [The main package.] + + Synopsis [Support for external libaries.] + + Author [Mike Case] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: libSupport.c,v 1.1 2005/09/06 19:59:51 casem Exp $] + +***********************************************************************/ + +#include "mainInt.h" +#include +#include + +#ifndef WIN32 +# include +# include +# include +#endif + +#define MAX_LIBS 256 +static void* libHandles[MAX_LIBS+1]; // will be null terminated + +typedef void (*lib_init_end_func) (Abc_Frame_t * pAbc); + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This will find all the ABC library extensions in the current directory and load them all. +//////////////////////////////////////////////////////////////////////////////////////////////////// +void open_libs() { + int curr_lib = 0; + +#ifdef WIN32 +// printf("Warning: open_libs WIN32 not implemented.\n"); +#else + DIR* dirp; + struct dirent* dp; + char *env, *init_p, *p; + int done; + + env = getenv ("ABC_LIB_PATH"); + if (env == NULL) { +// printf("Warning: ABC_LIB_PATH not defined. Looking into the current directory.\n"); + init_p = malloc (2*sizeof(char)); + init_p[0]='.'; init_p[1] = 0; + } else { + init_p = malloc ((strlen(env)+1)*sizeof(char)); + strcpy (init_p, env); + } + + // Extract directories and read libraries + done = 0; + p = init_p; + while (!done) { + char *endp = strchr (p,':'); + if (endp == NULL) done = 1; // last directory in the list + else *endp = 0; // end of string + + dirp = opendir(p); + if (dirp == NULL) { +// printf("Warning: directory in ABC_LIB_PATH does not exist (%s).\n", p); + continue; + } + + while ((dp = readdir(dirp)) != NULL) { + if ((strncmp("libabc_", dp->d_name, 7) == 0) && + (strcmp(".so", dp->d_name + strlen(dp->d_name) - 3) == 0)) { + + // make sure we don't overflow the handle array + if (curr_lib >= MAX_LIBS) { + printf("Warning: maximum number of ABC libraries (%d) exceeded. Not loading %s.\n", + MAX_LIBS, + dp->d_name); + } + + // attempt to load it + else { + char* szPrefixed = malloc((strlen(dp->d_name) + strlen(p) + 2) * + sizeof(char)); + sprintf(szPrefixed, "%s/", p); + strcat(szPrefixed, dp->d_name); + libHandles[curr_lib] = dlopen(szPrefixed, RTLD_NOW); + + // did the load succeed? + if (libHandles[curr_lib] != 0) { + printf("Loaded ABC library: %s (Abc library extension #%d)\n", szPrefixed, curr_lib); + curr_lib++; + } else { + printf("Warning: failed to load ABC library %s:\n\t%s\n", szPrefixed, dlerror()); + } + + free(szPrefixed); + } + } + } + closedir(dirp); + p = endp+1; + } + + free(init_p); +#endif + + // null terminate the list of handles + libHandles[curr_lib] = 0; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This will close all open ABC library extensions +//////////////////////////////////////////////////////////////////////////////////////////////////// +void close_libs() { +#ifdef WIN32 + printf("Warning: close_libs WIN32 not implemented.\n"); +#else + int i; + for (i = 0; libHandles[i] != 0; i++) { + if (dlclose(libHandles[i]) != 0) { + printf("Warning: failed to close library %d\n", i); + } + libHandles[i] = 0; + } +#endif +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This will get a pointer to a function inside of an open library +//////////////////////////////////////////////////////////////////////////////////////////////////// +void* get_fnct_ptr(int lib_num, char* sym_name) { +#ifdef WIN32 + printf("Warning: get_fnct_ptr WIN32 not implemented.\n"); + return 0; +#else + return dlsym(libHandles[lib_num], sym_name); +#endif +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This will call an initialization function in every open library. +//////////////////////////////////////////////////////////////////////////////////////////////////// +void call_inits(Abc_Frame_t* pAbc) { + int i; + lib_init_end_func init_func; + for (i = 0; libHandles[i] != 0; i++) { + init_func = (lib_init_end_func) get_fnct_ptr(i, "abc_init"); + if (init_func == 0) { + printf("Warning: Failed to initialize library %d.\n", i); + } else { + (*init_func)(pAbc); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This will call a shutdown function in every open library. +//////////////////////////////////////////////////////////////////////////////////////////////////// +void call_ends(Abc_Frame_t* pAbc) { + int i; + lib_init_end_func end_func; + for (i = 0; libHandles[i] != 0; i++) { + end_func = (lib_init_end_func) get_fnct_ptr(i, "abc_end"); + if (end_func == 0) { + printf("Warning: Failed to end library %d.\n", i); + } else { + (*end_func)(pAbc); + } + } +} + +void Libs_Init(Abc_Frame_t * pAbc) +{ + open_libs(); + call_inits(pAbc); +} + +void Libs_End(Abc_Frame_t * pAbc) +{ + call_ends(pAbc); + + // It's good practice to close our libraries at this point, but this can mess up any backtrace printed by Valgind. + // close_libs(); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/JAMIESON_TESTS/logsyn_techmap4.script b/abc_with_bb_support/JAMIESON_TESTS/logsyn_techmap4.script new file mode 100644 index 000000000..09464ab59 --- /dev/null +++ b/abc_with_bb_support/JAMIESON_TESTS/logsyn_techmap4.script @@ -0,0 +1,8 @@ +read JAMIESON_TESTS/ava.blif; +resyn2; +read_lut JAMIESON_TESTS/lut_lib4.txt; +fpga; +cec; +write_hie JAMIESON_TESTS/ava.blif JAMIESON_TESTS/ava.4.blif; + +time diff --git a/abc_with_bb_support/JAMIESON_TESTS/logsyn_techmap4_mini.script b/abc_with_bb_support/JAMIESON_TESTS/logsyn_techmap4_mini.script new file mode 100644 index 000000000..92f6b6ea9 --- /dev/null +++ b/abc_with_bb_support/JAMIESON_TESTS/logsyn_techmap4_mini.script @@ -0,0 +1,17 @@ +read JAMIESON_TESTS/mini_example1.blif; +resyn2; +resyn2rs; +read_lut JAMIESON_TESTS/lut_lib4.txt; +fpga; +cec; +write_hie JAMIESON_TESTS/mini_example1.blif JAMIESON_TESTS/mini_example1.4.blif + +read JAMIESON_TESTS/mini_example2.blif; +resyn2; +resyn2rs; +read_lut JAMIESON_TESTS/lut_lib4.txt; +fpga; +cec; +write_hie JAMIESON_TESTS/mini_example2.blif JAMIESON_TESTS/mini_example2.4.blif + +time diff --git a/abc_with_bb_support/JAMIESON_TESTS/lut_lib4.txt b/abc_with_bb_support/JAMIESON_TESTS/lut_lib4.txt new file mode 100644 index 000000000..286b54d47 --- /dev/null +++ b/abc_with_bb_support/JAMIESON_TESTS/lut_lib4.txt @@ -0,0 +1,6 @@ +# The area/delay of k-variable LUTs: +# k area delay +1 1.00 1.00 +2 1.00 1.00 +3 1.00 1.00 +4 1.00 1.00 diff --git a/abc_with_bb_support/JAMIESON_TESTS/mini_example1.4.blif b/abc_with_bb_support/JAMIESON_TESTS/mini_example1.4.blif new file mode 100644 index 000000000..0a1a0b587 --- /dev/null +++ b/abc_with_bb_support/JAMIESON_TESTS/mini_example1.4.blif @@ -0,0 +1,79 @@ +# Benchmark "test" written by ABC on Tue Aug 28 16:53:47 2007 +.model test +.inputs a b c d e f g h i j k l m n o clk +.outputs A0 B0 C0 D0 E0 + +.latch n53 D0 0 +.latch n57 E0 0 + + +.subckt mult_M1 a0=a a1=b a2=c a3=d b0=e b1=f b2=g b3=h c0=m0 c1=m1 c2=m2 c3=m3 c4=m4 c5=m5 c6=m6 c7=m7 +.subckt mult_M2 a0=a a1=b a2=c a3=d b0=i b1=j b2=k b3=l c0=m20 c1=m21 c2=m22 c3=m23 c4=m24 c5=m25 c6=m26 c7=m27 + +.names m0 m1 m20 m21 A0 +0011 1 +1111 1 +.names m24 m26 n58_1 B0 +000 1 +.names m22 n59 n57 n68 n58_1 +001- 0 +---1 0 +.names m23 m25 m27 n60 n59 +0111 0 +101- 0 +1100 0 +.names n61 n62 n63 n64 n60 +110- 0 +-0-1 0 +.names a b e f n61 +0110 1 +.names c d n62 +10 1 +.names g h n63 +01 1 +.names e f g n64 +011 1 +.names o n60 n66 n67 n57 +10-1 1 +111- 1 +.names a b m n66 +1-0 1 +-00 1 +.names a b m n n67 +0110 1 +1-11 1 +-011 1 +.names m25 m27 n60 n68 +110 1 +.names m3 n60 n71 n72 n70 +10-1 1 +-01- 1 +.names m6 m7 n71 +01 1 +.names m2 m4 n72 +00 1 +.names m3 m5 n71 n72 n73 +0111 1 +1011 1 +.names m4 m5 n60 n71 n74 +0101 1 +.names n57 n70 n73 n74 C0 +0--0 0 +-000 0 +.names m n53 +1 1 +.end + + +.model mult_M1 +.inputs a0 a1 a2 a3 b0 b1 b2 b3 +.outputs c0 c1 c2 c3 c4 c5 c6 c7 +.blackbox +.end + + +.model mult_M2 +.inputs a0 a1 a2 a3 b0 b1 b2 b3 +.outputs c0 c1 c2 c3 c4 c5 c6 c7 +.blackbox +.end diff --git a/abc_with_bb_support/JAMIESON_TESTS/mini_example1.blif b/abc_with_bb_support/JAMIESON_TESTS/mini_example1.blif new file mode 100644 index 000000000..32bec4090 --- /dev/null +++ b/abc_with_bb_support/JAMIESON_TESTS/mini_example1.blif @@ -0,0 +1,56 @@ +.model test +.inputs a b c d e f g h i j k l m n o clk +.outputs A0 B0 C0 D0 E0 + +.subckt mult_M1 a0=a a1=b a2=c a3=d b0=e b1=f b2=g b3=h c0=m0 c1=m1 c2=m2 c3=m3 c4=m4 c5=m5 c6=m6 c7=m7 +.subckt mult_M2 a0=a a1=b a2=c a3=d b0=i b1=j b2=k b3=l c0=m20 c1=m21 c2=m22 c3=m23 c4=m24 c5=m25 c6=m26 c7=m27 + +.latch m D0 re clk 0 +.latch o4 E0 re clk 0 + +.names a b o1 +01 1 +.names c d o2 +10 1 +.names e f g h o1 o2 o3 +100011 1 +101-11 1 +011--0 1 +.names o3 o1 m n o o4 +11101 1 +10111 1 +000-1 1 +.names m0 m1 m20 m21 A0 +1111 1 +0011 1 +.names o3 o4 m22 m23 m24 m25 m26 m27 B0 +11010101 1 +01010001 1 +11010001 1 +11010100 1 +11000101 1 +01000101 1 +1---0101 1 +.names o3 o4 m2 m3 m4 m5 m6 m7 C0 +1--10101 1 +01010001 1 +11----01 1 +11010--- 1 +01000101 1 +01000101 1 +1---0101 1 + + +.end + +.model mult_M1 +.inputs a0 a1 a2 a3 b0 b1 b2 b3 +.outputs c0 c1 c2 c3 c4 c5 c6 c7 +.blackbox +.end + +.model mult_M2 +.inputs a0 a1 a2 a3 b0 b1 b2 b3 +.outputs c0 c1 c2 c3 c4 c5 c6 c7 +.blackbox +.end diff --git a/abc_with_bb_support/JAMIESON_TESTS/mini_example2.4.blif b/abc_with_bb_support/JAMIESON_TESTS/mini_example2.4.blif new file mode 100644 index 000000000..dff265f54 --- /dev/null +++ b/abc_with_bb_support/JAMIESON_TESTS/mini_example2.4.blif @@ -0,0 +1,62 @@ +# Benchmark "test" written by ABC on Tue Aug 28 16:53:48 2007 +.model test +.inputs a b c clk d e f g h i j k l m m0 m1 m2 m20 m21 m22 m23 m24 m25 m26 \ + m27 m3 m4 m5 m6 m7 n o +.outputs A0 B0 C0 D0 E0 + +.latch n74 D0 0 +.latch n78 E0 0 + +.names m0 m1 m20 m21 A0 +0011 1 +1111 1 +.names m24 m26 n46 B0 +000 1 +.names m22 n47 n78 n56 n46 +001- 0 +---1 0 +.names m23 m25 m27 n48 n47 +0111 0 +101- 0 +1100 0 +.names n49 n50 n51 n52 n48 +110- 0 +-0-1 0 +.names a b e f n49 +0110 1 +.names c d n50 +10 1 +.names g h n51 +01 1 +.names e f g n52 +011 1 +.names o n48 n54 n55 n78 +10-1 1 +111- 1 +.names a b m n54 +1-0 1 +-00 1 +.names a b m n n55 +0110 1 +1-11 1 +-011 1 +.names m25 m27 n48 n56 +110 1 +.names m3 n48 n59 n60 n58 +10-1 1 +-01- 1 +.names m6 m7 n59 +01 1 +.names m2 m4 n60 +00 1 +.names m3 m5 n59 n60 n61 +0111 1 +1011 1 +.names m4 m5 n48 n59 n62 +0101 1 +.names n78 n58 n61 n62 C0 +0--0 0 +-000 0 +.names m n74 +1 1 +.end diff --git a/abc_with_bb_support/JAMIESON_TESTS/mini_example2.blif b/abc_with_bb_support/JAMIESON_TESTS/mini_example2.blif new file mode 100644 index 000000000..6851d3d26 --- /dev/null +++ b/abc_with_bb_support/JAMIESON_TESTS/mini_example2.blif @@ -0,0 +1,40 @@ +.model test +.inputs a b c d e f g h i j k l m n o clk m0 m1 m2 m3 m4 m5 m6 m7 m20 m21 m22 m23 m24 m25 m26 m27 +.outputs A0 B0 C0 D0 E0 + +.latch m D0 re clk 0 +.latch o4 E0 re clk 0 + +.names a b o1 +01 1 +.names c d o2 +10 1 +.names e f g h o1 o2 o3 +100011 1 +101-11 1 +011--0 1 +.names o3 o1 m n o o4 +11101 1 +10111 1 +000-1 1 +.names m0 m1 m20 m21 A0 +1111 1 +0011 1 +.names o3 o4 m22 m23 m24 m25 m26 m27 B0 +11010101 1 +01010001 1 +11010001 1 +11010100 1 +11000101 1 +01000101 1 +1---0101 1 +.names o3 o4 m2 m3 m4 m5 m6 m7 C0 +1--10101 1 +01010001 1 +11----01 1 +11010--- 1 +01000101 1 +01000101 1 +1---0101 1 + +.end diff --git a/abc_with_bb_support/JAMIESON_TESTS/sample.blif b/abc_with_bb_support/JAMIESON_TESTS/sample.blif new file mode 100644 index 000000000..5da2c59aa --- /dev/null +++ b/abc_with_bb_support/JAMIESON_TESTS/sample.blif @@ -0,0 +1,11 @@ +.model sample +.inputs a00 a01 b00 b01 +.outputs y00 y01 + +.names a00 a01 y00 +11 1 +.names b00 b01 y01 +10 1 + +.end + diff --git a/abc_with_bb_support/JAMIESON_TESTS/sample2.blif b/abc_with_bb_support/JAMIESON_TESTS/sample2.blif new file mode 100644 index 000000000..7a4366fe3 --- /dev/null +++ b/abc_with_bb_support/JAMIESON_TESTS/sample2.blif @@ -0,0 +1,24 @@ +.model Adder02 +.inputs a00 a01 b00 b01 +.outputs y00 y01 y02 + +.subckt FA a=a00 b=b00 cin=c s=y00 cout=00 +.subckt FA a=a01 b=b01 cin=00 s=y01 cout=y02 +.names c +0 +.end + +.model FA +.inputs a b cin +.outputs s cout +.names a b k +10 1 +01 1 +.names k cin s +10 1 +01 1 +.names a b cin cout +11- 1 +1-1 1 +-11 1 +.end diff --git a/abc_with_bb_support/JAMIESON_TESTS/sample3.blif b/abc_with_bb_support/JAMIESON_TESTS/sample3.blif new file mode 100644 index 000000000..db3ee0a1e --- /dev/null +++ b/abc_with_bb_support/JAMIESON_TESTS/sample3.blif @@ -0,0 +1,13 @@ +.model Adder02 +.inputs a00 a01 +.outputs y00 + +.subckt FA a=a00 b=a01 cout=y00 +.end + +.model FA +.inputs a b +.outputs cout +.names a b cout +11 1 +.end diff --git a/abc_with_bb_support/JAMIESON_TESTS/sample4.blif b/abc_with_bb_support/JAMIESON_TESTS/sample4.blif new file mode 100644 index 000000000..03e2d4917 --- /dev/null +++ b/abc_with_bb_support/JAMIESON_TESTS/sample4.blif @@ -0,0 +1,19 @@ +.model test +.inputs A0 A1 B0 B1 start c +.outputs C0 C1 C2 + +.subckt FA a=A0 b=A1 c=start carryout=CARRY1 sum=C0d +.subckt FA a=A1 b=B1 c=CARRY1 carryout=C2d sum=C1d + +.latch C0d C0 re c 0 +.latch C1d C1 re c 0 +.latch C2d C2 re c 0 + +.end + +.model FA +.inputs a b c +.outputs carryout sum +# test +.blackbox extra words +.end diff --git a/abc_with_bb_support/Makefile b/abc_with_bb_support/Makefile new file mode 100644 index 000000000..86e1de407 --- /dev/null +++ b/abc_with_bb_support/Makefile @@ -0,0 +1,82 @@ + +CC := gcc-4.9 +CXX := g++-4.9 +LD := g++-4.9 +CP := cp + +PROG := abc + +MODULES := src/base/abc src/base/abci src/base/cmd \ + src/base/io src/base/main src/base/ver \ + src/aig/ivy src/aig/hop src/aig/rwt src/aig/deco \ + src/aig/mem src/aig/dar src/aig/fra src/aig/cnf \ + src/aig/csw src/aig/ec src/aig/aig src/aig/kit \ + src/aig/bdc \ + src/bdd/cudd src/bdd/dsd src/bdd/epd src/bdd/mtr \ + src/bdd/parse src/bdd/reo src/bdd/cas \ + src/map/fpga src/map/mapper src/map/mio \ + src/map/super src/map/if \ + src/misc/extra src/misc/mvc src/misc/st src/misc/util \ + src/misc/espresso src/misc/nm src/misc/vec \ + src/misc/hash \ + src/opt/cut src/opt/dec src/opt/fxu src/opt/rwr \ + src/opt/sim src/opt/ret src/opt/res src/opt/lpk \ + src/sat/bsat src/sat/csat src/sat/msat src/sat/fraig \ + src/phys/place + +default: $(PROG) + +#OPTFLAGS := -DNDEBUG -O3 +OPTFLAGS := -g -O + +CFLAGS += -w $(OPTFLAGS) $(patsubst %, -I%, $(MODULES)) +CFLAGS += -MD -MP +CXXFLAGS += $(CFLAGS) + +#LIBS := -ldl -rdynamic -ltermcap +LIBS := -ldl -rdynamic + +SRC := +GARBAGE := core core.* *.stackdump ./tags $(PROG) + +.PHONY: tags clean docs + +include $(patsubst %, %/module.make, $(MODULES)) + +OBJ := \ + $(patsubst %.cc, %.o, $(filter %.cc, $(SRC))) \ + $(patsubst %.c, %.o, $(filter %.c, $(SRC))) \ + $(patsubst %.y, %.o, $(filter %.y, $(SRC))) + +DEP := $(OBJ:.o=.d) + +# implicit rules +# Jason Luu Oct 26: Not necessary due to -MP -MD +#%.d: %.c +# ./depends.sh $(CC) `dirname $*.c` $(CFLAGS) $*.c > $@ +# +#%.d: %.cc +# ./depends.sh $(CXX) `dirname $*.cc` $(CXXFLAGS) $(CFLAGS) $*.cc > $@ +# +#-include $(DEP) + +# Actual targets + +depend: $(DEP) + +clean: + rm -rf $(PROG) $(OBJ) $(GARBAGE) $(OBJ:.o=.d) + +tags: + ctags -R . + +$(PROG): $(OBJ) lib$(PROG).a + $(LD) -o $@ $^ $(LIBS) + +lib$(PROG).a: $(OBJ) + ar rv $@ $? + ranlib $@ + +docs: + doxygen doxygen.conf + diff --git a/abc_with_bb_support/abc.dsp b/abc_with_bb_support/abc.dsp new file mode 100644 index 000000000..1492884eb --- /dev/null +++ b/abc_with_bb_support/abc.dsp @@ -0,0 +1,2819 @@ +# Microsoft Developer Studio Project File - Name="abc" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=abc - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "abc.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "abc.mak" CFG="abc - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "abc - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "abc - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "abc - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "src\base\abc" /I "src\base\abci" /I "src\base\abcs" /I "src\base\seq" /I "src\base\cmd" /I "src\base\io" /I "src\base\main" /I "src\bdd\cudd" /I "src\bdd\epd" /I "src\bdd\mtr" /I "src\bdd\parse" /I "src\bdd\dsd" /I "src\bdd\reo" /I "src\sop\ft" /I "src\sat\asat" /I "src\sat\bsat" /I "src\sat\msat" /I "src\sat\fraig" /I "src\opt\cut" /I "src\opt\dec" /I "src\opt\fxu" /I "src\opt\sim" /I "src\opt\rwr" /I "src\opt\res" /I "src\opt\lpk" /I "src\map\fpga" /I "src\map\if" /I "src\map\mapper" /I "src\map\mio" /I "src\map\super" /I "src\misc\extra" /I "src\misc\st" /I "src\misc\mvc" /I "src\misc\util" /I "src\misc\npn" /I "src\misc\vec" /I "src\misc\espresso" /I "src\misc\nm" /I "src\misc\hash" /I "src\aig\ivy" /I "src\aig\hop" /I "src\aig\rwt" /I "src\aig\deco" /I "src\aig\mem" /I "src\aig\aig" /I "src\aig\dar" /I "src\aig\cnf" /I "src\aig\fra" /I "src\aig\kit" /I "src\aig\bdc" /I "src\temp\esop" /I "src\phys\place" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "__STDC__" /FR /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /profile /machine:I386 /out:"_TEST/abc.exe" + +!ELSEIF "$(CFG)" == "abc - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "src\base\abc" /I "src\base\abci" /I "src\base\abcs" /I "src\base\seq" /I "src\base\cmd" /I "src\base\io" /I "src\base\main" /I "src\bdd\cudd" /I "src\bdd\epd" /I "src\bdd\mtr" /I "src\bdd\parse" /I "src\bdd\dsd" /I "src\bdd\reo" /I "src\sop\ft" /I "src\sat\asat" /I "src\sat\bsat" /I "src\sat\msat" /I "src\sat\fraig" /I "src\opt\cut" /I "src\opt\dec" /I "src\opt\fxu" /I "src\opt\sim" /I "src\opt\rwr" /I "src\opt\res" /I "src\opt\lpk" /I "src\map\fpga" /I "src\map\if" /I "src\map\mapper" /I "src\map\mio" /I "src\map\super" /I "src\misc\extra" /I "src\misc\st" /I "src\misc\mvc" /I "src\misc\util" /I "src\misc\npn" /I "src\misc\vec" /I "src\misc\espresso" /I "src\misc\nm" /I "src\misc\hash" /I "src\aig\ivy" /I "src\aig\hop" /I "src\aig\rwt" /I "src\aig\deco" /I "src\aig\mem" /I "src\aig\aig" /I "src\aig\dar" /I "src\aig\cnf" /I "src\aig\fra" /I "src\aig\kit" /I "src\aig\bdc" /I "src\temp\esop" /I "src\phys\place" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "__STDC__" /FR /YX /FD /GZ /c +# SUBTRACT CPP /X +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"_TEST/abc.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "abc - Win32 Release" +# Name "abc - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Group "base" + +# PROP Default_Filter "" +# Begin Group "abc" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\base\abc\abc.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcAig.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcBlifMv.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcCheck.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcDfs.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcFanio.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcFunc.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcHie.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcLatch.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcLib.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcMinBase.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcNames.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcNetlist.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcNtk.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcObj.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcRefs.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcShow.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcSop.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcUtil.c +# End Source File +# End Group +# Begin Group "abci" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\base\abci\abc.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcAttach.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcAuto.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcBalance.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcBmc.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcCas.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcClpBdd.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcClpSop.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcCut.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcDar.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcDebug.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcDress.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcDsd.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcEspresso.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcExtract.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcFpga.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcFpgaFast.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcFraig.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcFxu.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcGen.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcHaig.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcIf.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcIvy.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcLut.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcMap.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcMeasure.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcMini.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcMiter.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcMulti.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcMv.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcNtbdd.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcOdc.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcOrder.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcPart.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcPlace.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcPrint.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcProve.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcQbf.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcQuant.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcRec.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcReconv.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcRefactor.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcRenode.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcReorder.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcRestruct.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcResub.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcRewrite.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcRr.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcSat.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcStrash.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcSweep.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcSymm.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcTiming.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcUnate.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcUnreach.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcVerify.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcXsim.c +# End Source File +# End Group +# Begin Group "cmd" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\base\cmd\cmd.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\cmd\cmd.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\cmd\cmdAlias.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\cmd\cmdApi.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\cmd\cmdFlag.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\cmd\cmdHist.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\cmd\cmdInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\cmd\cmdUtils.c +# End Source File +# End Group +# Begin Group "io" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\base\io\io.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\io.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioReadAiger.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioReadBaf.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioReadBench.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioReadBlif.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioReadBlifAig.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioReadBlifMv.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioReadDsd.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioReadEdif.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioReadEqn.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioReadPla.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioReadVerilog.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioUtil.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteAiger.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteBaf.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteBench.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteBlif.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteBlifMv.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteCnf.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteDot.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteEqn.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteGml.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteList.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWritePla.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteVerilog.c +# End Source File +# End Group +# Begin Group "main" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\base\main\libSupport.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\main\main.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\main\main.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\main\mainFrame.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\main\mainInit.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\main\mainInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\main\mainUtils.c +# End Source File +# End Group +# Begin Group "ver" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\base\ver\ver.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\ver\verCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\ver\verFormula.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\ver\verParse.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\ver\verStream.c +# End Source File +# End Group +# Begin Group "func" + +# PROP Default_Filter "" +# End Group +# End Group +# Begin Group "bdd" + +# PROP Default_Filter "" +# Begin Group "cudd" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\bdd\cudd\cudd.h +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddAddAbs.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddAddApply.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddAddFind.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddAddInv.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddAddIte.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddAddNeg.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddAddWalsh.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddAndAbs.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddAnneal.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddApa.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddAPI.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddApprox.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddBddAbs.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddBddCorr.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddBddIte.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddBridge.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddCache.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddCheck.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddClip.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddCof.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddCompose.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddDecomp.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddEssent.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddExact.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddExport.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddGenCof.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddGenetic.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddGroup.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddHarwell.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddInit.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddInteract.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddLCache.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddLevelQ.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddLinear.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddLiteral.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddMatMult.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddPriority.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddRead.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddRef.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddReorder.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddSat.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddSign.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddSolve.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddSplit.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddSubsetHB.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddSubsetSP.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddSymmetry.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddTable.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddUtil.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddWindow.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddCount.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddFuncs.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddGroup.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddIsop.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddLin.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddMisc.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddPort.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddReord.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddSetop.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddSymm.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddUtil.c +# End Source File +# End Group +# Begin Group "epd" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\bdd\epd\epd.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\epd\epd.h +# End Source File +# End Group +# Begin Group "mtr" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\bdd\mtr\mtr.h +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\mtr\mtrBasic.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\mtr\mtrGroup.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\mtr\mtrInt.h +# End Source File +# End Group +# Begin Group "parse" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\bdd\parse\parse.h +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\parse\parseCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\parse\parseEqn.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\parse\parseInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\parse\parseStack.c +# End Source File +# End Group +# Begin Group "dsd" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\bdd\dsd\dsd.h +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\dsd\dsdApi.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\dsd\dsdCheck.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\dsd\dsdInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\dsd\dsdLocal.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\dsd\dsdMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\dsd\dsdProc.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\dsd\dsdTree.c +# End Source File +# End Group +# Begin Group "reo" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\bdd\reo\reo.h +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\reo\reoApi.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\reo\reoCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\reo\reoProfile.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\reo\reoSift.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\reo\reoSwap.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\reo\reoTest.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\reo\reoTransfer.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\reo\reoUnits.c +# End Source File +# End Group +# Begin Group "cas" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\bdd\cas\cas.h +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cas\casCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cas\casDec.c +# End Source File +# End Group +# End Group +# Begin Group "sat" + +# PROP Default_Filter "" +# Begin Group "msat" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\sat\msat\msat.h +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatActivity.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatClause.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatClauseVec.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatMem.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatOrderH.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatQueue.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatRead.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatSolverApi.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatSolverCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatSolverIo.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatSolverSearch.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatSort.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatVec.c +# End Source File +# End Group +# Begin Group "fraig" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\sat\fraig\fraig.h +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigApi.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigCanon.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigChoice.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigFanout.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigFeed.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigMem.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigNode.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigPrime.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigSat.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigTable.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigUtil.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigVec.c +# End Source File +# End Group +# Begin Group "csat" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\sat\csat\csat_apis.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\csat\csat_apis.h +# End Source File +# End Group +# Begin Group "bsat" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\sat\bsat\satInter.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\bsat\satMem.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\bsat\satMem.h +# End Source File +# Begin Source File + +SOURCE=.\src\sat\bsat\satSolver.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\bsat\satSolver.h +# End Source File +# Begin Source File + +SOURCE=.\src\sat\bsat\satStore.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\bsat\satStore.h +# End Source File +# Begin Source File + +SOURCE=.\src\sat\bsat\satTrace.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\bsat\satUtil.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\bsat\satVec.h +# End Source File +# End Group +# Begin Group "proof" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\sat\proof\pr.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\proof\pr.h +# End Source File +# End Group +# End Group +# Begin Group "opt" + +# PROP Default_Filter "" +# Begin Group "fxu" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\opt\fxu\fxu.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxu.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuCreate.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuHeapD.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuHeapS.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuList.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuMatrix.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuPair.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuPrint.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuReduce.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuSelect.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuSingle.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuUpdate.c +# End Source File +# End Group +# Begin Group "rwr" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\opt\rwr\rwr.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\rwr\rwrDec.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\rwr\rwrEva.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\rwr\rwrExp.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\rwr\rwrLib.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\rwr\rwrMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\rwr\rwrPrint.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\rwr\rwrTemp.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\rwr\rwrUtil.c +# End Source File +# End Group +# Begin Group "cut" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\opt\cut\cut.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutApi.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutCut.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutExpand.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutList.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutMerge.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutNode.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutOracle.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutPre22.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutSeq.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutTruth.c +# End Source File +# End Group +# Begin Group "dec" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\opt\dec\dec.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\dec\decAbc.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\dec\decFactor.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\dec\decMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\dec\decPrint.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\dec\decUtil.c +# End Source File +# End Group +# Begin Group "sim" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\opt\sim\sim.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\sim\simMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\sim\simSat.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\sim\simSeq.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\sim\simSupp.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\sim\simSwitch.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\sim\simSym.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\sim\simSymSat.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\sim\simSymSim.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\sim\simSymStr.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\sim\simUtils.c +# End Source File +# End Group +# Begin Group "ret" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\opt\ret\retArea.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\ret\retCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\ret\retDelay.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\ret\retFlow.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\ret\retIncrem.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\ret\retInit.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\ret\retInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\ret\retLvalue.c +# End Source File +# End Group +# Begin Group "res" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\opt\res\res.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\res\resCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\res\resDivs.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\res\resFilter.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\res\resInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\res\resSat.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\res\resSim.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\res\resStrash.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\res\resWin.c +# End Source File +# End Group +# Begin Group "lpk" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\opt\lpk\lpk.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\lpk\lpkCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\lpk\lpkCut.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\lpk\lpkInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\lpk\lpkMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\lpk\lpkMap.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\lpk\lpkMulti.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\lpk\lpkMux.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\lpk\lpkSets.c +# End Source File +# End Group +# End Group +# Begin Group "map" + +# PROP Default_Filter "" +# Begin Group "fpga" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\map\fpga\fpga.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpga.h +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaCreate.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaCut.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaCutUtils.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaFanout.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaLib.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaMatch.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaSwitch.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaTime.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaTruth.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaUtils.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaVec.c +# End Source File +# End Group +# Begin Group "mapper" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\map\mapper\mapper.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapper.h +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperCanon.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperCreate.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperCut.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperCutUtils.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperFanout.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperLib.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperMatch.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperRefs.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperSuper.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperSwitch.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperTable.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperTime.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperTree.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperTruth.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperUtils.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperVec.c +# End Source File +# End Group +# Begin Group "mio" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\map\mio\mio.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mio\mio.h +# End Source File +# Begin Source File + +SOURCE=.\src\map\mio\mioApi.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mio\mioFunc.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mio\mioInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\map\mio\mioRead.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mio\mioUtils.c +# End Source File +# End Group +# Begin Group "super" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\map\super\super.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\super\super.h +# End Source File +# Begin Source File + +SOURCE=.\src\map\super\superAnd.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\super\superGate.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\super\superInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\map\super\superWrite.c +# End Source File +# End Group +# Begin Group "if" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\map\if\if.h +# End Source File +# Begin Source File + +SOURCE=.\src\map\if\ifCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\if\ifCut.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\if\ifMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\if\ifMap.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\if\ifReduce.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\if\ifSeq.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\if\ifTime.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\if\ifTruth.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\if\ifUtil.c +# End Source File +# End Group +# End Group +# Begin Group "misc" + +# PROP Default_Filter "" +# Begin Group "extra" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\misc\extra\extra.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraBddAuto.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraBddCas.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraBddKmap.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraBddMisc.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraBddSymm.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraBddUnate.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraUtilBitMatrix.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraUtilCanon.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraUtilFile.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraUtilMemory.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraUtilMisc.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraUtilProgress.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraUtilReader.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraUtilTruth.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraUtilUtil.c +# End Source File +# End Group +# Begin Group "st" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\misc\st\st.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\st\st.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\st\stmm.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\st\stmm.h +# End Source File +# End Group +# Begin Group "mvc" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\misc\mvc\mvc.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvc.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcApi.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcCompare.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcContain.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcCover.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcCube.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcDivide.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcDivisor.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcList.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcLits.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcOpAlg.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcOpBool.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcPrint.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcSort.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcUtils.c +# End Source File +# End Group +# Begin Group "vec" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\misc\vec\vec.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\vec\vecAtt.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\vec\vecFlt.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\vec\vecInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\vec\vecPtr.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\vec\vecStr.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\vec\vecVec.h +# End Source File +# End Group +# Begin Group "espresso" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\misc\espresso\cofactor.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\cols.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\compl.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\contain.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\cubehack.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\cubestr.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\cvrin.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\cvrm.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\cvrmisc.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\cvrout.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\dominate.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\equiv.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\espresso.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\espresso.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\essen.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\exact.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\expand.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\gasp.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\gimpel.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\globals.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\hack.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\indep.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\irred.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\map.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\matrix.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\mincov.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\mincov.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\mincov_int.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\opo.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\pair.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\part.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\primes.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\reduce.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\rows.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\set.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\setc.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\sharp.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\sminterf.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\solution.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\sparse.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\sparse.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\sparse_int.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\unate.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\verify.c +# End Source File +# End Group +# Begin Group "util" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\misc\util\util_hack.h +# End Source File +# End Group +# Begin Group "nm" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\misc\nm\nm.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\nm\nmApi.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\nm\nmInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\nm\nmTable.c +# End Source File +# End Group +# Begin Group "hash" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\misc\hash\hash.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\hash\hashFlt.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\hash\hashInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\hash\hashPtr.h +# End Source File +# End Group +# End Group +# Begin Group "phys" + +# PROP Default_Filter "" +# Begin Group "place" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\phys\place\libhmetis.h +# End Source File +# Begin Source File + +SOURCE=.\src\phys\place\place_base.c +# End Source File +# Begin Source File + +SOURCE=.\src\phys\place\place_base.h +# End Source File +# Begin Source File + +SOURCE=.\src\phys\place\place_bin.c +# End Source File +# Begin Source File + +SOURCE=.\src\phys\place\place_genqp.c +# End Source File +# Begin Source File + +SOURCE=.\src\phys\place\place_gordian.c +# End Source File +# Begin Source File + +SOURCE=.\src\phys\place\place_gordian.h +# End Source File +# Begin Source File + +SOURCE=.\src\phys\place\place_legalize.c +# End Source File +# Begin Source File + +SOURCE=.\src\phys\place\place_pads.c +# End Source File +# Begin Source File + +SOURCE=.\src\phys\place\place_partition.c +# End Source File +# Begin Source File + +SOURCE=.\src\phys\place\place_qpsolver.c +# End Source File +# Begin Source File + +SOURCE=.\src\phys\place\place_qpsolver.h +# End Source File +# End Group +# End Group +# Begin Group "ai" + +# PROP Default_Filter "" +# Begin Group "hop" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\aig\hop\hop.h +# End Source File +# Begin Source File + +SOURCE=.\src\aig\hop\hopBalance.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\hop\hopCheck.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\hop\hopDfs.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\hop\hopMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\hop\hopMem.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\hop\hopObj.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\hop\hopOper.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\hop\hopTable.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\hop\hopUtil.c +# End Source File +# End Group +# Begin Group "ivy" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\aig\ivy\ivy.h +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyBalance.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyCanon.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyCheck.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyCut.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyCutTrav.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyDfs.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyDsd.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyFanout.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyFastMap.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyFraig.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyHaig.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyMem.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyMulti.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyObj.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyOper.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyResyn.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyRwr.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivySeq.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyShow.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyTable.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyUtil.c +# End Source File +# End Group +# Begin Group "rwt" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\aig\rwt\rwt.h +# End Source File +# Begin Source File + +SOURCE=.\src\aig\rwt\rwtDec.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\rwt\rwtMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\rwt\rwtUtil.c +# End Source File +# End Group +# Begin Group "deco" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\aig\deco\deco.h +# End Source File +# End Group +# Begin Group "mem" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\aig\mem\mem.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\mem\mem.h +# End Source File +# End Group +# Begin Group "ec" + +# PROP Default_Filter "" +# End Group +# Begin Group "dar" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\aig\dar\dar.h +# End Source File +# Begin Source File + +SOURCE=.\src\aig\dar\darBalance.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\dar\darCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\dar\darCut.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\dar\darData.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\dar\darInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\aig\dar\darLib.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\dar\darMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\dar\darRefact.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\dar\darResub.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\dar\darScript.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\dar\darTruth.c +# End Source File +# End Group +# Begin Group "fra" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\aig\fra\fra.h +# End Source File +# Begin Source File + +SOURCE=.\src\aig\fra\fraCec.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\fra\fraClass.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\fra\fraCnf.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\fra\fraCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\fra\fraInd.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\fra\fraMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\fra\fraSat.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\fra\fraSec.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\fra\fraSim.c +# End Source File +# End Group +# Begin Group "cnf" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\aig\cnf\cnf.h +# End Source File +# Begin Source File + +SOURCE=.\src\aig\cnf\cnfCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\cnf\cnfCut.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\cnf\cnfData.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\cnf\cnfMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\cnf\cnfMap.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\cnf\cnfPost.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\cnf\cnfUtil.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\cnf\cnfWrite.c +# End Source File +# End Group +# Begin Group "csw" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\aig\csw\csw.h +# End Source File +# Begin Source File + +SOURCE=.\src\aig\csw\cswCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\csw\cswCut.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\csw\cswInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\aig\csw\cswMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\csw\cswTable.c +# End Source File +# End Group +# Begin Group "kit" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\aig\kit\kit.h +# End Source File +# Begin Source File + +SOURCE=.\src\aig\kit\kitBdd.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\kit\kitDsd.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\kit\kitFactor.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\kit\kitGraph.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\kit\kitHop.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\kit\kitIsop.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\kit\kitSop.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\kit\kitTruth.c +# End Source File +# End Group +# Begin Group "bdc" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\aig\bdc\bdc.h +# End Source File +# Begin Source File + +SOURCE=.\src\aig\bdc\bdcCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\bdc\bdcDec.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\bdc\bdcInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\aig\bdc\bdcTable.c +# End Source File +# End Group +# Begin Group "aig" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\aig\aig\aig.h +# End Source File +# Begin Source File + +SOURCE=.\src\aig\aig\aigCheck.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\aig\aigDfs.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\aig\aigFanout.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\aig\aigMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\aig\aigMem.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\aig\aigMffc.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\aig\aigObj.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\aig\aigOper.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\aig\aigOrder.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\aig\aigPart.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\aig\aigRepr.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\aig\aigSeq.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\aig\aigShow.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\aig\aigTable.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\aig\aigTiming.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\aig\aigTruth.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\aig\aigUtil.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\aig\aigWin.c +# End Source File +# End Group +# End Group +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/abc_with_bb_support/abc.dsw b/abc_with_bb_support/abc.dsw new file mode 100644 index 000000000..146413a59 --- /dev/null +++ b/abc_with_bb_support/abc.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "abc"=.\abc.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/abc_with_bb_support/abc.ncb b/abc_with_bb_support/abc.ncb new file mode 100644 index 0000000000000000000000000000000000000000..b3a81db2c71e70454bd16e6111dc102bb2850e8b GIT binary patch literal 5114880 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 n_Mq;` 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

JnNBO9`f$!6q^tSsI>y!>tj@-gFO};!i^#h= z*~E9PZr)Blqy@e~p7@S;{+?+6Ty`_^=hTJsEIl$asn*$xkW=n68Relw=X8{&Vg`0X zpY=}QBhsVYLOQMZO)E96sIe60>OkL;zuKedVHa`9AMDM{p>MOsmA2=TNMp1TV{~t> z`ZhWTF+NY?I@8AGJ&emIxK=SDzhq2mrK%N39o8Sx4e`pq{{E_gziQzBp&F31LlRqIC4UR>^<;{Z#{h)xiIZ8n7|H z-9CRsll%U6?f<_~k~et%68Xt}x$Wmamcb=&IX4&G3IAqrtz%e=u|wkAIT>7QQFa59 z@!(@JxVIzjoE!nZ91TJGUT;U-g7q}#6f^WJi1@2yQb*Yg&hm}FpNLEE+Fnl8n? zT|npfaB$ftrTG3z#x;mrgD=YD=K#Yi#d6@GFus{t+_ibTIl)qHlQR_zZn# z!yjN(gFQk9?`HUotk5`}g}j~Sr?=r#&nwn-o9B@CBT}XJ0ZyKh*H!kd*Nyq**Mq*9gNqxg+_1PzFEV@cJg-$~GW9 z%P8Nk|JD0 zx4rkj^SuP`*ZE7mN4694ZCBx#$kpbPVxmbg)n)ojdfx%s+rZQBX?pUTac-Z;_2c*d z=(k5{tF>t57>Zc*m5ug|rZ4s@x+F=hC?4Ph@>1epKgpVR4JWZ)Vjc1Y`VU8h=!)^~ zXh|EOFK80)AGeHUMWNF}$3{Kk0XEp<;d{1x=9)@W2uw(I8pXB8}d5b-Nmk`u7}XD=vsnZqH3ou^>YF1 zvP)Q%&5NFhSK75IelK2c*N3cUf4A$8SR+WadK~dv=0=Kbr97MxpPIZBa`sFt&k{l} zil0hS`-I`~b^NZ9d`2yQ8GRKUNuEWe-K5&z`X?@p?@3bogE_IzUy940NS+^dE6L06 zu`Cbt{xcnlr&zWJDq~lRbfMHoM6KAbye*N9fpliF!=E2bjc;M?_+k7aKG%l{}Z^&i9lrtZ%4iNW-Q1LF?O2ockMyHSMunL+mWT za4cw#i>9yxthcAu22GtbLn`q==w+{~mX5GAFBk1W^a0r)Q~>GYmQnw!(0+S8)Oy>6 z_$2-`{)zJcfeuo-C`FP|T6e{>k{3fyn2Ek@OsM5Kw9b4^&1r^GS$>K&=UJ(ed&1p} z&aQQ52@K+Xt)kX*YvFIdB2VoTp_N>NP7ycTk5q& zv0c(=1_PoKi#@YFLVTPCkv!3;26WORwSBk)5-Bb%A~usQMD8O zUGKQs6aH?1ORXRjn_{VXWK*vCqSyN+KXUu>KpfuPe98!IzAaq|Md7Gu8ZSKu_ju;btm>r>XGV`AH|<=eQG^XeX*8t zs=7&U^!n0(YKQD^$*IYV$r!rAF}6{MtKP> z@ED#Mo-j)foi2YBFD^PAtyDpOs$9M72 zHdwHv9O+ozqP|!OPACakZyb>v&l`2u!fYMJbJx}ONYlPCAU=+sxUGkrilyrX@zhLD znHA5apWc$`G54UcelqYf^_=J9b@XEGs`?)E1-4ti1)ip!R5IBe4ZG*jX|LKdX_2(# zYQ@zV>xS;w8>BsL1Xgs%Cnps5xbu<=l4f;l|9RrNw;Ja`^C4A3+ZjOCaKRZd<*7Nd8$3d`lx2sma%** ziSI6UIr?7esQ{c7wHAvug}aa9e#I?X&2+i-cy2wTJss&9;eD;`PPX>C*xKmMFvn!e z>uX(-hc9hivm8+=kBj=nW8zEVh4G>6e$Qm@Iz3NyNOXPRXcO>Mr8SQ7w=9-jiJqK8 zzn>qSOJAAFKFck-_(e3Q7N1aR6K2a~l;?td;USm6Hrxxy~b(bLh*<=)LdIV}GKTR$~8B*Y5Pj&RCJ0m&{F;o6U&IafEq~)tG9=du1`A z)*l}AL4!R5E1{>^H_f4~?#JHZrR@E$F-sEB9~%EHuD`|CM(?C_sJnY@Wu}xX#VJ{xmhOMYh&Y?Wc*^EpHE`hnrIH{&x~*>Gr(bR^;zr% z$nr$>KNVl92{WT_@uyL1YiB`puMnQktvy(D@RX@42= z{uAsSKTq3#jvV?G?f(Z^Mi57KsTfyA?w7@}_PiT2>ub*MOnr8P6ZYVeg>~<^kJ*hJ zN`H~f-6&QB$1}FP<=x4Qk-yQ0&gZ&-mBRG+LO8<=X3|UOQ}fKW`2y1ifj(nxUEw> za>X^fa&_bC#jbll?8!%Rjp7=eoNQJs7r-&6a$RILEHmMkvQoK)6^(3E?lyk85?;B+ z>_{}Tr1Hsfq*-ys^TWu!ESf{Vmp0`CH#F)+eOyQ>*O331-1Xso7nA1dL^>Arix%+l z6B1dE_*4{yP3Pz)hdwmcJ!$#_^%_QO=s&+kf2-qr=bGr-6a3xixbX_FLtacfKVmKY zn6>qCTHE{Ec!oBYt-$ZJwk)oaI7f?DqP?qFi&v-38zX_M_c~^M*^lU6{Zf$~m5WOv zS&#@Fz-W_>LFHKB@<;~rclXk}Rs-9{VV3)6dH=95!q&zYhtiH?&s+8&Fh)j37kJm5 z%-K`uxt|0c)tUZwE`4Yjz2hhNQCC{>0$S@Zc%h9f^H#1FT4k%**X-o9c4HM$?g3l( zX>VO;-+sg9B1i1FvYd0P6DeNYzDU%v-q2{5M0Gyi>?9??@5zi@Swu+6-U*rNC}g5m%?xk1)JRXr%E90lJY$KFMzZ#M3@7Hy*kr4g;lx(j^HW5nspJl`=%~Ow7hT1yg#BSM)l}CeE$M;F?jS zGdUUj_6#m!pZJe@2tTzIcIC^oU!@Z*UnLyy@4e+5p}lOK3jN?&C7SJ?hLJ8O{Q zbq#+NNdo;=CY?PEAA$ue@k=uJK8AO;^Ki{Fczwg`TQts^;Bm-jW5XA41{v#X;lYnc zbHlwKkzXX&2YN}k(tFM3U&8~v*Ux)D#q!OU>Y}9|)%__!mnvU;>2E3BBz~L8XFi{l zUb@!w{yFe^(z;=q-h7L%S-T7$>Y z8^|NT51ea~myOwx`X@bzUMann`1NsE>`QN@`S)$?taoD#r;|JSO*#Ta;?!JxHeO}Z z99(BJv$PY6DX^0HQGV*a#X|Zg^SYvEALiZat(s-N;+M`y`WGO{st0JqR5h!>Bjfwvtn&-W6#jwG!Q#b&<~6=*d^fNylh7=UEm< z9Z5}Nupw{qG32|V3G^}Hhnc;RVwh{BDg6SLUJcE%RNuOgevjxTep9{3zrj`1o-ENN z^ZA-rd+EfEX7%=XNbHU-bqMu*A~l^#9#fgCWgT3`QjwpGO3d<#*V@oDF`dxF^fG%a zy=O0UvT6^FDy3S@G?xoZ52TtF7g6iAHO;uv)T-fIT5Tu%sT@om23xSCTqTy4N_ytG{@^hmes^0l3{$Spwf5=yL z!7T5jxsorN-Ki((tr}2MZLy;4WLm3%w5_~`oM|VPF0fXaZR_H>oICbX^wLOL5#LL? z+UY`-TAB(Bv*8z>(6qEdv=knxH`U&;s^Qf&ishe{2J#lQkrL+4Y zw9{bf;dncnGL`u5OQXlkCyaXh>(r^n{d;&Xk}r@ST}f)R645v2GK$i(j!ju1j5L4K zYV`sUI`-mfhmL=sohCgW3qs{+8ucIxTHQZHt7(oWs~+_C_8Qt$Ql{#? zF*4)`w{KLMx2Ubu1J7g+DdkBESSh3&Nty?`zpc}o*w|N;`W`m+eZSO7UEES!@?BbU zUvfN^e5PkGr7I$fP6Ar*j^)pf^0wvvJE`5f^+N8Cy09C3_IV16z`O5OP0_E%WH(>n|4T*i`-}U zs`?F|uT?F^o_Kw2P6pIx9dzGT|HSRCG4-lv$GP-8Q*EPY8a+zM^~(wF?Ql2Rod>nj zd4<_;GpWz#yXL9am=QO)G~QlUi{GJVTf5QW2_kzj6MbtME&Xzh8>g1w?r7d1|9(Q} zS)^aJ@AbA%ClA(3-7!$=t=*7tm^z~$X&}9II3+&a$@{5()r?%3vOSEZM`@kzM)GdG z;ToikYeG(6Wma16*6>X<1jcCMldic+3Fg+Xmb+KpbF796PrF;ytQ|0%P zrexUH(f*a@+QqzGOKNFKJ+uj@C*=d|XrzPDoPim~Y1k>~)n=O}?h(3CX{Z}=;!#(# za5ax~wTSj!?@Qg1os7=zLdKiQR+=*vKkdBjzH2DiRQFc6s}$)Ju}b@(8+)%$qav${ zQpMi%=5X3Z{aLv$LUy?jS>$;)CDT4S4WJY{TkH*gmqy*-+z>Ucyi7rgdFOpk$^PmF zUz74TEs06{s0B`Urk?h0wy~&p)~0sOu%4+=uGFt_OSx0szTrx6-Y(Uud4FOvnTQllJ;$(&9&MeYtcP+u_x57BWcTPioN|RHuD|SxpH?Crr{2x+PfCve(P|Hn`pXEB1$rzLJ64S6L{Dr%LDjOwrH>GBmR9;fm&b-lS$ znOo4ybVB#C@XKKLBBj$WyO|fks?ibrqB?KO8y1qhUkz2$KKH(rAGzN9RsT z+gx$SRWGWQn~P)T`4p5drJ`q2pKVLsos>~M;(UJnJmlqM_t-!`Qk&!5^E&LEv?^6z zhT6&D)1kg0BS$sFZ=Cr%rp3tzw_;JFOMin!}hq>cTQx_czc{d z#oq+3?Qs|&fAZVAJzgZ3@i~rvv-MNk@Y9HHv^`$OaCz(49#@q+xaim&@Op;-&0-CU zbEfUGm*Jz~4cn))x8Wn{q6 z@S}(qwLRX#@FU%kJK*~pu9#)p)3-GIaH5KBkGC>>0G3#2Gc|t0ct61KLrwoZK7$`< zcz^67u)ELT2N~WEpAzimsndjX+8f>*ogI3}On*4UaK$>qOHziuv*A6NNu7IA>k^+% zu8ZM&JN1|L;sc(~6uKIIk*yoAt1 zF zul3F!kNb7Z@8P!;E5rJy3ZDb{9R1(?D6L_9&iiksljcL|Dc?8!2mdeRw^~nnn)F_E zp!00mPq`JW6Zt#}{o!x1+TgwnpA;AERnA*uCj4IoIcB4qPS1?^Ho^5}Z0N3yn-#E$ zu5Ve7t+g3Zzh3XQ2JC&e5>am`n8v7&Ij9(UiZFaM++rY{WDs0NG3W*d|Hnh1mpnZT zZa5rHDb4aoIPc$#FN=a*zO!VFGl|(s(QPK%Jf}HO^W%JW{biB( zGAqSbut1fsUhm!db=F6+$w^}w%h$D!t*BLk{9C-w+T;V*fzC`}7{k4&qifwt>LTX* zk)*dW)W&4DGL-xcw;#+&bvWN*Yupv62OtSngho#;W+nMhpnuIBQ(U*=So-}?B*3=e z_orOvfa2BHNyZR`^3gd^tZ}!8yT7|~(2B?I&&uIoPE4!rRqv{E)wk+e^{hHp{i<$N zuc}kkr|MGms5(^rspeE`sxj4;kIL=WQL3G9Lf($yw?|6&aw{g_i^g@7(?-}T><*Rs zM@yHk4+oRdbs^U;yPdRdz@+(b( z_a7lW!ej2)P=+_%7vb(*S1OcdgUu~^wwDw4Q@MAA=RFuwKb)Mr8t#u)f2MBKLw)+} zX4LlUrONKWK&8G@o%|@JVXT5Dg?B#ZZsWl=pz^lVie4kn=k4h`9q2tBtrj(#?$~d3 z>?dlKYpuWEN-mTSS@S4@luE1eK1Th$_)q=kxRAg0yian7rZBzl-?i7j>$iUF-D>ar zcdgctep`n3rsel#&$iXqOqWfd`0wZH-{nOzh}ylMyDZd<#*$VpLMhJp*69oEBskWeJ?G#cP0=NB~ly)2!`%ogiNqcgV zAaq|F%iX4-K3dUJb`5C^gMvmHE z?OrdWJRJVNI+XK4ldSZF_Ln4m>0W!LlF)R20diS`@Z@pK8h3|0O>%dKG;TGiO0&YA z@Uk`TM@pvmwqV4yXQb^Po*oHLJv}@<&OIESzSHd=a=+SEhbr$h_r#w5?QOBax;fKZ zJ_ygB=guj9`_19`SBkZDMcC;cAJAuP-KkW|n)Ir>o^EXT^;!2yxO>ZO3U}*l#VfjU z?tmiCZC1FYAs0t5YrGTg2h#VB4mld@t`B!-x_8K%>h2*pMBPBEwjCZQPIb40yR%%v zTUA>re|w(gpx%9j$)rl9F5cTD&+5J*r>{;5&z@$hd(qzFE(v*m(Jf}Vp_P6MT3wvS zbFb^&)d%}?K>ry|uT_N7?eDG*xz|X2J-l~{`+@Rh^Q_gQ=tsNBfo8m$Q`|l^$(!;n zn~uRDpHI8JYWo)6gE{@v@RmW$!&P`reaYwh?Lhyw+W?I;4=E2wIY2u4H|&rnt!$L1 z-tM`Or*mA7kf-_JfBpSc1OM}CKt0-Rr~mWk{~xbrxcTAN$i$vGSD%|3GPt+`o+y)2 z;1^|Z$p`FiB^w!bhiC9z4Sxuqh*)Rs%A1S)zTj?#kHu1*X!IF;cf%VbBfu}%$d^E0 z-S8hcX~zEF58H4U9e?Zbc!(67Nr!>U-^%z^@X8r{55pJ6i@|kvFil^22KFcLCWUQr z2KRcV^Ed^7C2t1zdZSbEL52;;79I`xloW&n$ypR+flNNVUZ+<)09+>(Q+k#{{56W( zgU6YCwlTa4UX}1)kV(huT@w7Fu?EfH2OIsS=tq+Nvi!F6I~x8L{>hlTYValGvy0&? z@xq5+?+m@yf82<@3SLk%ct4{bZ|D54&EQ_&a2%E+%(xl+2%{em5e~qO&*0K`$!B3# z@ZlMJu;I<{97?={O#KWqyjHX)c>fIjNW*0T@7%!|TzVcww~D|OVakQ_o?`fV;^SIeRs+e$>Sldm}_3W(iTN?Wz{Y}sO@4A<+DNR3p*TY>nB>d=E=llFM9D# zP|ac0b1M>~G(_v!(frKfa%+BQZkmMF#oiK5jjL4lCpVr;Ub5@u0w zoYq+ri|aEUT!Tb>Pj^+~(NB4KPFA+!VnW}F#=*B>`|jhUWNzP_><{vMYh@nGCvnbD z^Oh(p=iXzQ5ar*Kt$4w2OL>I1HqNBG0g-r}?rt<|f+M->frP5-Ff1ChuccMqt+t2j z*XD{om}@>d;B$QC`pAQS;DdjXU1wwKH_a}2kG~ji47U^SwF(|tisHt^j?mSf z>tN3Q48UK_I8Gtz@@KfF*~!-ni9>J^=lQO~`|y_JXS;squvoQRO}pygyZxn58@ERT zpu4xonT<~47|W0G9L_ve!{@NBI=M!#_?r4CDIst0p;jJp zvTSHct*kaLmrrvR=^cEt>RKOt$n|}+*{&Zrt=!bEW}LH=myK5Vq&XmN6CY?N?E2a% zyZ)R?UxY6lUH9R!cs2fVbiHV2<30-K<35R7(F4l!)-Cv|(B=JAID93P;8by`q%>FA zWLG@oPJ~i2NMq`;3+0r5`-?c$D4(oq`JTxzP7EswArqv-n|dNHtrnkd^MvlUI(b3t zq2F2I%5#VG7IRD=c^hk}M_6&8e`2-r61u(hoErKVU9XSwRu)^4T2Y-y(eRpK=`lV! z%lsnF!2b4D+WSs=cY&)UmrkYC8MJWC$fV0}r>8 zlB><%^gKJiC;3MCTpvqf(%sl(A$J;$&mdm8g=J1 zSATM~Y8kq4(2LAsT6V#gxtXk|AI8)5qsj2%@jAm@`L?WKm5)}W+uP3h%WLd(s8zSB zSJkQNQ*~Ju3uo12GiSBV)4Z3R< zeFP?6aC3B5w1{XK_eJMZPtx)(LDWazwK4Ni z^b{xOUyNU}v%qD+ccJgJz%E$U+NI)y;zKzhx`em?lvK}2)=7lJnxpblrC>79(LJ89#)iLkT!Q|pMeUuO%{gzaZj|QQM5jO0Zi+7?d+u}Tp;OJP z!*j`t)bne}2TAIOv)I(+ixas}dg?3vp;CmC6}E*m|-+bOVn zHu-y^c3j6CthU_1nEIGLu+>_XM|rkHt|UI`YtZ&}X?>-Gx9MCpMv2CfT4pUH;U(ts z@9CvKQ~Q;fld5yo%xQI~H-4XJy{5Y#ianUriX_9T*EB~Tcm|xHJfpn}BYjl#DEZLb zb29l9MdPHoama&g+>S#65obJ)F{>CQM?f(bE}~fMx1$Am#B}D%u(EnF?7(a=E-g!< z-|TFC?ZBn?APTWO*K)$yIP^o&N?Q8q5UFJov)K=k&X1>DyNYq`M$B!!n8Sv}Be+hD z$8pJH#)_dv-8cNNF_ zP314e`qgM{3#a-ZSUaUUcdZ?i#whm$Ev&KX+^qW}A6JAZE%_|I`Zef-bmjQQ=;@$o-9 z)im7nh@KVPRanWi{~K#>=naR@j9);74U}CVl=s&KJNtTl(!SKQL?lUdg|SI9Vq{59{V{S$-9G z=?w1mj<@9(f&Yj;A}v=Pqo0t!7X0}PzK7v8@+X2X&ft3+ULwB>_{|x7Kf^cWeulN5 zm%+W>@$KBl;3G14Q=?ymLIIotfc z_Ri2BW%wXGlCd+G!H+S#EuK->srr>KA)TRymrNRiE53A!k1+gQ;-qo( zJNV|x;3pYAmDm#4;%9KLM;sHM4n8M?pKA0+5od*dn8C*z-WlIB^dcnUG(Qs!Z)Sd6 zCuQg-8D2!Z3)(k>pJR9_V#y)RXYlh3|DI@B#BIsoQw?7qZ3N#ZgI{R)3(>3KIs=mC z=VHT`5_yYJlcB%N@Y|71IXQxSpVD7$_)N}ZVyBy_=c^2#$oXBO$5-K>&@R^+K7v!h zXmm38^@jIFBEqnDvSXYkt%f6aZ!|1C54 zorW)A-<*Dx!50~RGnVO`_|D+>8a|aBRnG7K!9Sr~4;X&5+3AkT)aPQuJFr$l8zrU_ z=$9JafSpxjy+;@uOR$6Dlf9R2v(d=N9l+-s_8l89^S{g1O~0kj=)MGLbb{_MJ|p?0 zSb9z=OM5=QU&d!PpX~dx_ib8upSH@Ad=8|~XK}rw+|k(8@oB_o$M4zal(zg9tnbcQ zsqo(HcRl+oxZd+R|K9|U`jG!O^C>Y3zqWk#;3MC&dTt!|7xKB0kMz&NRMvlWulFxG z(9^Bu8NE;M`k4F8A&s<*qTPv&jOu(E@oB>M^u2n=zq-%X-=EzdL|XC#oUNBMA3L`F zpFJ;{EdBrL{XZVtzR#rP{a;NdOaHI(nJoA3>PMOH>4a9VH#!W)>RQGI|G7T-E^u)s z1=kM!oisG^-JoBN&A-eqT9LoRn)-Ftnp&%FB+AK8tW|TlQn}Wnyhpk+b~3&S(}$wY z6-Bk9N1yj=;d^uU=T_&|UT&wSIrp<~ZH0uvp2GGSb{?ZN1T=yeTTnPq?T`SXJ+oQ~YEm zBkZI0h|epGgSR4jI3Le$S62Fa#GA>TuZY!IET6)H6 z!yak~Hg6-ac07esOfzhi{TL@U<&{7_38eFE5w(gAjSjQZ_Gh63xgxp}jm*O6aUz;2 zqS{C7F#Z^AiOR;i5T~zhT#s@|D=gn@gW_ZGXLl+d#b)3a?L$_GWm#o*w5v0{Kt3VI zb>})T#Ik}XVHzkLXUly zYdyX8Q~K3E=(*CCsDH(1S$4{4-czgejpi{Us3qh#Vjfew?yJ&|hvq!R%6KS!Lg+9e zHp!E+p6i%A%S@oVRt5P^R{t1`=lDzF=>_>Skc_+zKL=OiGvKc1ig+}oxPHxp>RxzE01L9xQ0q*E^QD z3G@ZTY^47!`QK3ZB!w*?Dos!88}3Q}-s1|#(~rhlFS@a?BE0v5+_@k7kvu7?o4>E1 z7|?oFqvoLe3ffEh`4#j8=}g79w$Sb+bB;2rC2Qn{=Hz2WwSOWO#rMUh=Q`vE(U*VC z%TKcM_+(z^@%|WDQrDQ19KHyxbfMT}L=(cWlK|EAiCB%ZcUzb}TJB-YtVG zK7q%=S9r$;IO8Yi7`G7j@#mkIe52_GyxOlcLtS!199wc3J6qiu4j*W$(THPoMedpGxf?gR4hQBEs!wRTTN!#UKc z>S^!1&RuJKZkw%I(kfb&>-WY5MO!|PyXnj&mslIjLvO2gSjw#8eI(T7@WXt3bb@)iIXAkTc}M*14(6V_qNA8u#xS!?WoEgOKKG&d z+x(0^ryf_9KDVp&x@z<|^*3>+LCFwygO1~p@8Pr2wVu!3>oj%|XP{fZmHm`E;q5ET z$M71s`&eeCsmx59nU8+T)n+zom~X_4)I8sUS?Pd$8)l~V`GfNv@?Dsr`sEMJAI3a2 zD1S_TaQ@hQNqC0ps&?*xTo-zF-`s%Qkenh)tFB7tyE2|Ca5s>W433Y*`{=1~^l^9| zodI9JjOfQ###a$3ZaO)*IH#4ao-3J@q+g2rG_X13>0<7q9rq8Jf4*bpNtknVo?Lqu z-Q03|2o*J*S6%2riUp$wOVw5Fl zBYE#GILH0*Y1H$C+(zrsTj7*h3VG8XopVDP=b@Th= zkI2_5OfTG5Q0!)v?ZdciQk8Kv9+_hCpGun2=%`F3^74+QHKK2q4xDvX1m>RD^JHzn zJA6~SqkVYt;T#$D#@#|smt}%sE;9Qfr zW-|X>Q@9)2CCq`#3Xd0-GZRYdqPjjRmL5uX)pMh0DajQb*fTGaR4I%?Q=!?oG%JP5 z_>$5}p(b-h?MP9F#p8O!6AKp=E-qYOkXB5o%+B4%m{KZbd7>6O{L)|Oml4rX8~v<& z61@EwM(B)qMy?CvTV7;E@oX#|qn`D$MfJk`wSL@JXHL^B>gP0R7Dc}-FWrm!>e6WU z!p@aEy2>BJ2fY5 zkMc4zI*dJOjnh}yQC<&k`3&yzy-7EIg(E$Q<=Qkax_hnIn{C^usBjtWrr5-4;jy{# zjPj|-f*0q$&;1BT-I}YF?+xcXJU`6lt1AnSGul-*n(u!5!Plc&vXaFauMriQuUoa^ zv35#J{s-F9imLw`a&wqR=jEhpR!)W#nN@P<6I&>6u+@2Em*(wB<% z-kdbTs-0JuUGoL$36+Kx zJ@fM8p?PCysDYEIgVWsvYIQDkx-T@9-dZgtE83G3#_5uM>c&hjPo(^jYs<$6+ami zcz@|)pJ*xfmX=U;y;D>jzM$T*GgfWYlYNs$$&F!jRzgCmQK(fI6v}=toJCyalRtd9 z?$14tdkCp-ZSI-eC%j*6pjaBSY!1FYzKwL0veGHys$GyBh2A>TmHpWBh}v+_0~zz( zkYNu+=6{iVsh*o-+u9y~d(v*}i9u%Tpf)^)^}tCcH!fl(tYeZyDnE>1R@HT0K@o%1 zuSU35d3grWebZd?f~*>p{@b~C8S6Xci|D0Fe|anm2ZUDEm~D_o|Be*;bJQvxg3Yo@ zbX{E5xPCKo+kxEnCATZ{&+v@;X(N2$H+QmpP-`8E6m~dk{}b?VcUCkR3y3MK_J78= z^@zf`h4bk>X*}U(?xIb;M%qWuz(JI=D@fgnrL8%!r`xGmzIY&hn7;4?>riP* zmG=g=XP~>ei9C_$DOu(;Eu8faPw`Z7WZ|RTYDSz^5!WIoY0V+&Pb&?{d>4e4|J2re zxx&eO7dP;HIx1)tm@CLj5!L8wQ}-PiGhOJf-HTAkT2}ZG z1+jWdX{6WPxTIG|y&D~zoW9ZbU`dwREmFxQvwG(%;YL<3ljji=wFpb)qrk{A<^i-djhTBV6O54Z=)wH@&9B}hA%Dd0 zO5?|TY5HAP?mDuboR0nJQcfX$%POcDE1!|9crIt1vy64*2G);dI1A8%)#6cH16lPb zQh{P}OeYe-9Yl9{k5ZMQ&$mt5vBnw9%H|x_GxIG5zzY6d2}eH-o<2QyWA0wA)m-aX z)s)KDW}W#8tICb}-B>?PX0<3kOe*n>Xv!|x&6jw!>lrxNqpYV_asIJq;Lx8$+VN6} zI>*}c)Lo6dc1v}4TVDIJx~oul{cWFa18RFR5|Gd``QPjKyRn7I@A($01rPPQ2gMsu zPA+jPoT3d#sv88ql-Jsm!@2C!Nz1?CJ6GaKPybo|yZrQu9$o25kSn!kja!XE2PGX6dt9w*I>=Il9@}bs`)CbKmI0+9)9{C^7kF` zW?ba=y~PWOft}z%@56&D@(NGq|_~yD0Dnyh&v69)`=GFJmQx_cmO9 z>evs@;D;Jsg%uIwDT5C%Twd`Q%^Ca%!@qSu@&EoA9MO!ww}@SbeL)64#_*@{ZGe4x z1|MShBiL6n>N5Cn!|x!WMS4; z8GNka-apb^8GM4_-Vf5v8T?Gcy}zR?GWaCJy}u*zm-M|%SNx6PLY~XuOk4b&#Kmq* z23Njy)^#-h*2&;A4fpZ%?BPevRRMus>#Xox!g)yf0SC?6+s|>kRk)f=*BZhmwVOzJ^ z#}ytm+}qQ|q!yF2m~~FfY5bV>jgdiPS}mr=W4tZI)No8~#?)Ml&ykoO5aYikrsv1> z^BBEQOz(~9sWH7WrU%CKwix^Tm~lUUDE1b7p5*f?pHKK~;p3Q4(%%l^zAqo)!~O)n z@?T(2fGh3i`CoPbx__VhZ}`YYApI>%C;JTHWrs(uh)?=mX{67jzh$3G??2w`+`&AT z(q^B_=0o?{wEr{rX&LnFk-TFhpTF^$#OGv;7lg_7Ci}mBe~Ly`T~6oz8-Bl@@3XmI z$mc)8ALVzYeasX7nY6wpf5Tk%m9M{*{H}a`P~_|FNY3qT?uu__dUI;h8Q`d=TV(UR}eC}aBR&iC{)DExNIzQjG@{y9E>=W}Sw$h{f9 z?}J^CzRMricwR658uO{_o{ATI?(elWNM-*qCY8|d@s@NS<5YKjoGeLnH{NNC>iLTJ zz?K{*bXKGpS$8Lz%#q`6Iu>DvvEtGR#f0yziTyV^76&iKw}#|nz5CGJhyC2k(YGYo zG;WUGy$yDMBVx^8;zRAtCyH`6#B1LEDLsa5Z z&CquicmlA`YVM5#`BZlt(mDxv(bj0=<}xr)qH zD%S!uPLITo$8E`@Wc#Mb@cp2S1p&k!4iAL`mZ8DGU zPBJN0cgIF+`uR5>>xQ{$JW~qP*HdF6qpd_ErI@@2#2fidIn)k`WKKQ(t((T3a#+Q5 zDK(=4IWEEX1!1pwDLSLaW7)Bk4$sPqmw@nO%3ML|Wab2Q(#?;K=xqFMUrM?T!q! z($?f%(LUm)6xX+{WxyP93T4lsy68kc->28ir!HDh59(i%axAT@?pl~6 zrN6nSMmESrTfYudPXx#&xzDR=p1+g=={B2|HJ&66lPcY)C|Rm}|e*-!+c9g}at< z|B&8+PHPI${moWox~mdtU9P(Yv2>ogJI(wi`@4=jtGBcycJ-y)m*KuE`{vr$QeMYK zQ>dK;&QOm_Ya%^&x+_yW((jI!P*R zaRHrvy3lr{uVp8334BA*FZU-e(h@d-bLiA>f5wtxYMyA%O|~e*(~NgqLI1F|Bh+uX z8qpK)`kXX3$yQ}+h$Qp@>7B~GxWTs3Ebb-FDeH>U1K0F@V?90UL#_>hhsu^xSYkFOt5gPz&NgGb*q4y@Rhr5d@QvQ zq7|pwVyx*d)Qi{K=#<6~5{HdUJEq)X8ce3Ai71d?@qE$l}PQid*X2Xz%rD&$)wo zRNB2=lVVI!i!}--$5Y73!gv$!FTr>1K3|8&s_X4s-O?ZZxD_X@N-Cn*FZ7X`*2g3X zEA64S#@5|<(?;m7U35N+c8}?Nuu9d67_=wGlF!x3dm)!gceE4t-K}<1H>wxaigcpN zW7^`WPxvO0&f#k0t)sO!v(Llq8uHP`bT)cQeB(YwL8`TE82zJo%xk<}8_$MAY1FR3 z@{I@nfB2`iaYZf@|HJ$L8Hq-Fd)%*AeyzPD?$7@$Y_=or&;LBxVn^J^5vhF8j<}B_ zGO+WGxQ`<;;^-Z5A6IYjz#Z}GjsfG^AG0G~!*+EpJ#|OCrrCxxm}3y#*c@`EyO0y_icIozW=Ze%%>WG+dq^+3(5F zYm}qaZqyoj!vlRy!yhCj0ecM@`nra{)bN*v0T1+E4|+clRGpifp>JUH`!rk$z2Sl0 z>pSlwsul8XhQ6iI&udT}dcy;~*5mAub2&F9Lw~U0H5y#SiwqC+(uXKYtJd3NGxR+S zUs=CA^o9p|>5*80a51xH=m!|yu>N8MXu|`&*E=pE+5)?i8Tw<5e)fK~p*K9xk1+f$ z(=Q&9p+CX!PW#PZfp2)AA7i*;RU_$T=ub0z^S-U1H$2c!H2e-^BP6U0{n;Mh_tSj? z9_Y_Ae1Z89Dw(05Zg`u0Z-CzLK!1thw`0kKe&XzH{f%W1e~<0c5PHJ{{WXT)#%XEi zKFZMFVEBRi+_O)>1N~gXy}$ogGW2?$cHFxW^o9reg@(&p3bJK}Ui|}0(Y@!i4o5p3lGcXXsfn^H+WE!=X1k(6dzJZyp!_X6RQL{@PyUpf^0wKW+G2H<$k}&(ObM z_$7O--7DaM{_lq0j9m@=HbeiW;dS>q2YSN;{W`E@W*$n!mMR@pxcb^RX4~Ad6NBKRGFNFvCpAGkM720L!|1kWA zdNcN5CeG03;8SQPEdJ;o8G6Ec@ONpw&+0j6ct{_YiTqv5g(XLZo(Y$~?)A=v-ta(= z%8b8jxSacS(zgCt!|>Ae4uszDKwro3tBEm)EmDSlFT-E1`v;89@IYVRa33#XU537i z;a%#kgx>H#zrW#%H_yIShQ5vAW$Lzq-ta)*-f$mZ;?WF!C&S;XBS-Fr2YQY(@i&VL zc|Akl$M8Gqyj&;Xf&MVVXJRLUel$aWl;IQW+yK4dfqt;zmtnog+A2ej`iH;Tbw)#P zc%VPt@JsQ};@rs@`jZX+wDzvh8y@I2C8EKx{gb0I^b-tUQv03SSl9^<^xX0{gNxN! zhJK3S(`zq;-ta&_&2aChzhQ>{V#5!reFpS~2l`otPe;mRzbHe0wc#6!nm})OpugU5 zANM29(BEwM;-a^UkYR-f`dbb6@d3U#Yg_-k)9~>{cR+7=puflP3-DZvRFI*6(C}78 z$3t&;pnt@0MPA49CPV+Y;Xl>d4|>A`{Yt}qe3H8}^lJ_8Q0vQD%!a}P{qu(VxFvHk z^sgBHNzLZa8y@K2Fx zDw`0b4G;7>pG^R|FYr3v1aA0XomiE!&g@<3%%ii{vE@m z=f$s1hW-P?FRr+R1()H0{$s=ae%;&*{pW`FsW=&W!vp=-h7WN=NPk9#{(Hk~R_p-1 z;eq}q!+ji~2^sp|3}0Pg7w8QS^l?JM=(q;+@2CuY3B$)#co>zM;eo!i;Xdxr5gGdO zhJU~7DCi9j^py?Uj8AFWXjKg-ta)*-*6uvX?=$N2*azE?+Lx(fqszTioD?5iy8W%hOgeG zH1viC`jLivyM|>M`q74u*=5Nt?0X9j^zv`Piq+yTEzHo5H++v>hC*+6pg+@aZ)Y(( zLw}Cp@0Qygdcy<#1%~^0P17>;7a4v4ZY!ke!k(}-sG?hz5GFAxl{J5vgpt=`B`MRj~{h#hW>uT`WLjdlte2{riS{`;qrD^dA{sq4cTH8y@IC zGu+#uJe#5a%JAts=b<+|(0^yRw{uyVq2FS7Zs#+1rWG>z`PFc5FLQf_KFX=RW|Z1e zinGHR`hwvduu?$I%+T*__|B!yhTiaypIr<;m^~o0(;52R41aZ}-*yUkps#9pd)FTN z(HZ($hTpi;8t4rV^z{sHXAy9Y%Fyp)__rlzLT`AWZ)o^IW_QyyL*LBsvr2wclHC{K zfxeaD-ae&8hQ6)gMJ2~TZ+M{ZV7Rw)shgqiV)$z%DnM^|pzmq;|Ha;0xJhw!+r#bd z?%vg2jSlV(!=Qr=79_a4y9D>a-QC^Y2{1Se?g5hE{;gBp$>n|T{rv?WPd!gUovH4w zu0H4Nz1LoA?*v~)`NQ?qnDMs4qeAdvez>1n%y>aCM-X>+|DXAm!Hn~T4RJj)o_znt z%y@#o^Yn-7n=|9<1Rn&j?_@lAeQRbs!2`PX!}T4Q@s@&xxSknLUf-1&PXV(9sOb;a z_hQDs^XuVyW;}U)e`Y+vOIrWK^+TBPqhR;4Sj>3x`jO0dZt#kcqx<3dvCQ~b{tjHv zj3=+3#Ed8SP=kNCK9dlZNNZ+RtfJu{xXekn7a1FRnK zMt-<{6*Inw_XPiE#*^2tXT}qK=Su!?{T60CgEtNTXU3D)?_|ak{IBQ_*Y9J-1-vx; zpBYbHf0!AMfpG+`!w=V=V8%D1!bWmDdHq>tJj(DEzIFSb`E!vOuZJ3{u)kzHdHq#p zJi(KC{KNIPm~jzPr->O)UVo1nPw=fS{BZqaW;_e^6Hzzkhxh-38Bg@t+x^4!e=*|= zP{S1V^oQ%;G2@B;flGh5{xdV)0r@8ES3g|O!tX~iQvv*U@(&?vgMC2Qjl>>wb>?d@f|Z{G10rpTW<0kk~tau7#g#;pbZTxfcHM zrGD-QKlg*5`@zrs;OBn8W@@nhTnj(f!q2tvb1jfs%s=PB|9_uBYhaip=T1^TO`xba ztot3T6>^rs@2_qd``wEg**Gh1D`~4>t8J@mYj5jgn`E0}n`K*RTWvdRJ8yepduIC! zjnH^@k3DW*8d(|H9yuSm7~x0t(L&Lp(aO<|(azC1(X~-ujE)tG6^S*CwTg|8Esw2^ zZHXO>9ge+@8R_4BiMwb=cc6FCgoj7&Vgf5K`<92G^yDljYNw>}+kmKz+h(26e%BI? z9D#dVdqe*H{(cn62azj_p(oC4ao67x`m&b64#Dohxxucese~jYODdELbwan$E36`{ zD;z2tC7UQ)C|fMMB)chlBqIvC0=Oz5$|1@pN)y!)wGx?Rlq{F*7g-zGa9llEwh320 zk+J1GxlHbomyuVMx0QF4_m(e_Uy$FHb1Yno#GsvQlcUzBGzgWMb!II2ow}oxRY!z({Yz+n{D))|)Oq9%!ER|f6+>$(z{3U5HBJr(W zIDF2a^PlR_X_~)ugl9#*Zy%P6y~0j`l1H9QWy>hbKXs$aYb$EU(^Q_sldww)ND4{1 zNR;>_a^+CbDUotm;@=$&8w^TZN8ZmVDJkhLQPxlB&!$M1OIPCfBqjML^7n#JY_%1d zh9qB!Z*2nYkGx|R{hBrj$@8fj*E^m;g8|Z;V)rsL>19QB9T}mPo!d`a-?RY zNu*h1Kx9~?0A^XSXr*YqXnoAHdC}F;)6uihOVRt$htcm*QS5F?;?r~K>+6^5PvX-F z^=y&qpIXZm1SD@t?yofZK8}zQ)+jlf$Vy?$y%&?u$6;<{zF05uL=4g#(p)&YN_T@E zN#@iw;XUDVNRkwy^3IyhSQ4j@3_nK*7VV|8>@;utH#bvPLk0e ze+jbAuoiF#Ux1v|V0~b&Wrkoe~6!{X#9UT{)5Iq)s9eo=eRw+@B z?7aQCy>V=0?C+S0o$0Y!tgII=3TIgJcm55Jzg*2N|D#JK_$GcrV!=} zxsxn9bR0Yw{IeXoaaDotDJM4{w*a>=`ecqn6`sl5UA+Cg1E^2uV`{P*nfh*7f@^{Y zg2w_IRx{Ehk-snZH817V~(sbcb}W zbf5IF^n~=JbPo25MY83xHL|rbz1$+V%7@B-laG>*mrs#TmCuyVlh2pS6l#S=(Mr)? z(LqsFnXasbU$Lq3H`O%NHO%_ksxq2#8mU&LRcm9~JlYg(0c{a&G3|8iTKg5vw>wqv2>%< zs4}XJ1F(*U87~`e7;hRon!1^~n@*X2H=Q@d%(=|D%@@p9%va4Bmd2J}EWcXXSlU_+ zT8>$cTh3T6ST0(wT5eggE!DBt*Rd_Z9>2==6?;3^UeDgh-q_yU-rC;AZgw~vPRA;& z(G89-5GQh+Eu3wg?VO#RJ)AwA{V=13IGekhyIZ*1;Yjfm@)Y)z@RaqG^ECIi_O|gZ z@vZc&@~!u6@on|_{2_nXANS|+=k@Qx9)7@o)PKr<+Fy~XLRF<|Q}w9^R1>NN)skvQ zb*8#dJ*j?Ff9gr#Mc`%NMDT3zT(D{=Jya`nE|e9z6sjGrA8rtqVy93?8eo5D8aaSj zd@OPr7U@FdO5|qbR^(pfapVcs(&tDzc8rW@W6bJbqitZNIz_uj`$YRtjg>+VQX@y>15bsAPg1q zmkOu|+C=^!xnJ{OYaU>iC%eL0J6ZQsjI8{X%-W}!)laHwgId74#xZEhYbt2cG)Hw$ zb${s|d`_(Wj)60GSPYAK20G;BK&`-Ks41%AHs$W&s(HfQn&hoGF48?v0OIcDmQ+Zz*Q*~FJR=L%U z)N|Cg)J{z&%@)m9O&{$ktxuP(o2Wab3+RvPX+u}TeM4vCNn;b!9+S&F!hF=sw^Xrg zwZyF5tlO;Lt)*?#Y=78N?HlbF-(V zx$n_>)4i9y8ecVErjPGm>LZ-ke66?y=kiYCgzfTA5oIKMS{B+cbY>EoI{rHpkv-k`7r%wz7@DI*w^v+Om{oDvBODi; z#Y}%75DAsS{KBG`>Hop6M01;QCU$ceCg_<{-UAS zN%n~j!}EMC`hq>BmAJjQn|Pjhp?H&cyLhkoGWM8f;#cCgV!1>u(Mi%J^(9RuZ6#wR z6W|w5mh6xmk{pqol3amzd=LJyP^yq-NE=IANJn8;dLVr&{Znd?xn)&l>9Tt8*$2a8 zA0s;=J1IK{ul<{hCl|=$@&fXr*h`zkYws-ICO;}a1t0b;{FkqCzap%N!-q*%)Kz3C z1}cW*7^@hkn22MNVzOcncDwV6D~fEzSL}Lfr9o*|7QpV;OgU9K9edzvd2l z@9d!Ns_v!kubzorbd7qWdYgK;`l|W{cGSPsAJyN~MvYBl*Elu#HPtjVH0`m=4%Up+ zjMuEkZhKO5PLrj%rg?{b*R2iUNMZIK5(!k+R?$||_R#jxj?qrSUbIAeOnU-*^Aqh$ z?Hlb!L{x5_PnTC$P*)wh^{=`%x=y;zx^B90*tr+#mg&~&j$sGCsr#b)ru(jA>)m>v z-meen`{@Vkr|4(u59$wNcmJf98A67rfi{#hlry9nnq#-WVz`SPKVXa*+ZsC>I~yk% zryJ)Q_Zp8Fj~b5|kHZo)#5!tX8g3eGnq=Bx+6B9C*YwEr-1N8U4UV^_ckrBTW{25j zE^aPku4t}eu4%3dpL~dU80<%;dA513d6D^``7mtBJ@XSZ58_(*7B`}>3YN;SEyEBM zt+#B3jd^cjTZ4$lV%7?X$2wZOz~+p#PO@&YZh`H&XuW3rjM(hE)ri=vC?em|ww8!{ zCm`ybX4{LH_c0>gKWsL{yXCMKrP_O97g_^Lb;^Fxo@KvmzhxIX#15$=?#Sz??Wha; z)yC1$(cLi>k(0mo59HYahMa-4QNalC+SQ#y4{v(w@9I(^Onj-WH-Eayyhra6~6 zSL4|3+=1Pn#1wm-`<#dWjVw+&Kf@~OTvk^hS20&jSB9%6EaXJjc38=$u2-;>8n@AH zb9>w++-2P5+!^l1j91pdJqi|dj(eedC+z4M_XYPeSktd=uE*yIc`|S`_Vj^mT?Om9 z*K^!+2R2sa)p{#>t9xs})(-ZL^p3(Y+B*TqB<~FGZ0~&U67NM=;m6)Tysy1VpT_6) zg?u@Dm3>uUmz(%n`a1X~_@=@_ANHN}o%en6as7OMQGc4h8jkAz+BoX^8~dC3Tlm}h zXZaVxj$iWM@Mq(=?SFvdk^hDNPyZYLN57Y%s327Y$9(D#b)L$?af!N)<0f^VdPF^= zUQtwl4pa%G2O0#L1X=_-1jYv@!6#S}SQXe1*dDkNxDNl|W8iy05OfAT!Q840HOLLgLYj~{R4h~~)GX9G)GxFb{=|LQrZ1tQ;WFWJ;jZD{ z;ep{1;Z^W4&V(<7uZG_<5$m_GBjUpmW}?>eh*_&dx~8E)><=I%Z(|>EMCly#FnSa{o}NZ$ z(lhA!^aVPLz66iv3H<^QxGb)WyW;+M1^71&;!WanZ$LW(TXg~zaZaJ5@)>juK9}sO zd=a(WF9BJ(f{Go9o*lQCIx(c)#(m%@57EQ=F}hekWj$y80dMpbD)*3{1#g(19`6yU zeq?<@UEVL~Li~;C%gknT*k~ut=CcLKYDW@On@n^#P@{S>HV!ta`fEZJBrHXy@(ZbP z;zF%T58KQ3u_<+@DCGq)0tlrtS^;o?Z7{uytn+WSShrPaYjPst8 z=xm4=&=M7ZcNvMwj+_l(`PdH~kCUhM;?netD3Ob1Brv##&+K6`A(S*@XzGXSgjRCT2@LY+9*V542 zFu}0eu-8yG`MkPdf4!FU%%;1p0UaQI)>L1X?^^OH=G52KFVUaV6HYOC#}rf&YAGO$ zZE{u=bMa3R(W{cPQLH$7n|mRiDe=HM3;PKt!0*@~JSbe4{ERH82l_Sg>-@9+7al_u z27IUw3{8ruP!hr~1y}@rBdJP5Dn#dEYNDs00#ANmT!r8j7QrVMV-;tW0MES?@S(E! zrlj9TMbsv$jLIvdB2YC}bs#o1p&Y5js?Dmys*8RO8K`00fT?iPnDq;O?WWL4HHQ|f zWwOdmd+63WqAE;h)Vt}*RJ`ed4wJp0ZtMdEWj|Jb%)UXa!HCm_qBq2FrZ&qcXnDsn z-F7Abo0`m;0&Vp)=(lH}s>>|s(B~qan9o`O1^%LB7xiU`ELWf+!fI4qSc}RI>rp*o z6Dm|pWG`U)N`%nCjbJVvIDI$^P@g4>=^0{V@Rxe1K~C_Ojoc$Bpl;=5@Y?eh@wNhu zdCLnDEG8Qu zn+^ul?5!uq7JI-s++4b)r-`Z)DP4;O#s+T8%+<*8qIFaYmERX%|G}|SM4AM zp*gI*fc59lg>_YQ8M@KBOxag(F`tmtZhEl`2*Vrto73NbY{J;u`+k6fQ!McrvYZbrB#ySKRyxgWbmU}jPZh4-1m3eow1*!;6^ouh973e?BT#;eB{Vh1Aoh#lT+4Dt41M53M~Ml((CLF`EOv}xFh z_v82?9i5C4cOnKGjaZVzn7b@fu=kO7@#>@cuKG#(sru=9U2^y1!VA?U_dJ4%k&1n6 zyle_0#3*9H#Z3GsMZA}Y@h?* zqdg*uO^76VFmb{OLynEu+cl_6%Cm#KA#-k^m{4U0CcQBs% zFvdeCzB!li%PTWJ`DP38$0Lj{-i7hQw=h2VS1a+qOESLq6vppPpA@K`~!TTd#Um19N2``WMb#svQS_#~oAm;=-UjyH+D3g^a z7A=nB2=WnU;o}i+F3R|7#9NcWKhpv2fRKz;Oj%Bup=_z_ubie_qP(qqsSK&|tGcKL zsdT=C%}b@z=r@!I=OZ=9pXubzaZE#pjg ze$Abj$x6%-jl?XmNQnNIyuLZ=;Xx6Mc}>og=oeC)cQk2TZwT)TUm}WEiY5ihVvZBL zy$SK<(d68IX7kv~!sXOUwolex)q98J#nD)9V|@)EBsf(Z0zy+n;Z9Xy=! zI66x+;p3db@l{F`>f|1}uuN>fwJdNg$kbSw?0M)#x(&TZhX6mAAleG_;I=4^iU zY8`pYVAr;z6T5&f;#Y@NYs{YryEc!15|-^MpDWM^bm$k^2i=P{!@BJgNMPH{!mhAt zgN0XN)t(4_uxoil17O*vh~B`isl@YP(N>DDz^2_5t6hzWwk)rzE38?6)k#>h%PJ?VSyE;HM?kR1Z&pLv=i3sxXBJ{7B&xsH5+d}1bcSQ%&{meDoZ6; zv^tiJuxI-$A=t9~)=scy1CSTpZ#{^-sL&=xMzpf6iftln+Cup2A8em&JD$R-eRNcWRcq$_0*fYc6^2bK@9G7sHq5mHcI}Ak87$jZ zS1H)Gs_xOSZByO5VBLO~g7((R4pg@vmg zJps#hBkG1-%N<(+tF|*n!=@FWFT$4Hrsc3^#&|{8vzqaBUtrC!Zhpt2L%BhY7sy); zWU}jujsb0Yulk@G0<>hFdA@lKgIHVzX7R!vWn#%$h$h$5>tGW}79kE_rn-KS{tTjw zXL_AsT@vXnN=>DS-|oz$x0g(G4^0JXP!C>0Ti^ss zyjy@Vv$4}E;2lH|@3-~$1ll~`zY1Q$K|ddeb8h5#22h`YI&Vi29)iJc3?9VODi5!q zc4#G@*fDqpmqX$3AnZrva~(jRrs*^F`++>k4eVqdrGsIeVNmiOKX?Wuv;Q*S@_P8C zG?4jv!eKxMC&7%Mg4r9hIc8zy|aqB0FJu^ag3lYmMG2qj?i~?}1v1#3jYG zfm)6f_oDaGX#2!i{_DaA!oRRnszj6ZgYexUfMtDyRgw8^%=aAX&rgk^6ai9oitKT7 zS*6(C0I8`)&eoy_lRefN_fEWxY~W=t3^rqJ7(QII}j zSsC*o>T8hHy&<15loeukbG62|TVLG&n43U;hZ@_8QjkZxkc7HkGC{Tfj=x$Eur z{r1=PF7$4?3+4&=#C+6vN*;KPGjf-kshi1rKa0%#&BLT7DpYsunb_OUv9GbUoCqf; zI*ims_mS>E#}}dR%|6a$&Q0Lrq`xf5Y}DsA<4yw}zJMM3D4k*4T*vkCMQpzdH zS;||=2g-noR&`YMP#smBQQ6cUb%wgJx{rE>dainh`ieSRZP2(h?KGV=>oi+6Z#CaE z-L-wSN42N5E}dUjO;<}dRyRp^RCikE(g*bi^vCo;L)_5J(9Lk!@W9Z<*u}WVc*@wo z)YP=ywAW-cyUl~mBh3fQ$IL8?z*62))w0gA%@VNE)(+P0){WNf)=yTJt%$9RZM-eh z_Q>|aR@PqGzS_RYE^x>kwH);wQyg<0j~#zH$~vn!KRH>hg0AAO9D75Fcx!nxy%)S!yb7Pzm+GtT z8}FO$`{)z+8~SJZm-wIiJyeLgOZfxwz_`HFz~#WLfFtM)HV?K5t`F`E77djSO$+S_ z9SZ5gMZ)F7E5jSZ@50~1xvb>8rFfu8NnB zSB{gqH)M8o#r$b!B~`)5*<@CJb~*17%rFx7r$oy~2SsN_Nk=!bM@$CxI2UorHee19 ze54W@c}*s(Fng?T9DE%}cN2FP z_Z5$o97Pu5jO3yu9od0Nim8fO@T#*Fw-pcJTicZmr3-n1mdbX@F3MvJ$K?wer?&@6 zKS47MJ~heq6Er?un*l_AiFTQG4ZQ5{T9!@#FT0zrJ3Q^Rx^?ij*?NwiufJj_V=QY- z1%5u)IM28kzIHKFaZ_1SL*V6;O;b(NOml&o6Mo5O0BbfNaw{! z@W-dSX1M0Lo&jU#c_i?{L*B4A2Ym4f-ifdaGhrE)de?e4cq_wO?C9$bpZq+2sms0_ zK#FVo>tF}{1-7D{zbkz72mXh^f?om&{^VyiZ}4o*EB)cSm3!VDxgq z^1(EC?h}KPg45x@b3^=)5J>LO(6G?>(5%qx&|>)W+%PY!2wTF|urFK`IPS>sxbV2- zK3Eu>jb_UEU<-VS@2{dS6Zc5&p`|kOeZmTq6J%l72y^CO!#6}h|H3N=>c zbmZQ?sPZD$wgy?X{F>6>N({!@Jc+DXZshCJkt3UjRrv%fGB1-0>&)c9Zh}qm7TK?U z$a9rMMvJVa(dhR$eC7+&!}!D{dH8+j|QKxuz<}GEGK)={hn?4&;`7g$j&{hG+(wq&;({h+z-7EeMOrzd05G;iLO`=XOPM05=lpHg7_S*kdGLKOoI!# zg$v=tZ@JB$m%ad>k?_uP=xga0>(A<6>h*@g)G_K5RlO{m=_m6Bz7rRipGDjNM;fPCaw-mFsT#GK+DvW3u>+s@o#F(lS4#M00Z$yrRe9vJT6##u0WuRq$be*VNWCv| z)|RDao6XhX7uCYVT+LpKzPr!aFW3gu3d2D=YXaz6X63f91F2_vSC* zFXM0GXY=o%$GZi+O8tU>pa%L)P7@py92Q&=yc9TvK4DlG7v>V?5tbEJ7mgH;70wXO z67CVc5xx^jMa4xWMZbz>isp%yh*pYrh<1rCi40<^*e#~SQE^UjK5=1jHE|8`0Pz~} zI`IMV1MwsA2XR!AQ<6_oSdu2GCg~{IDA^);A@NFsQd*i@T0mM?MJuS_a-jTkSmX?*3wUo`5Es?E~t(WbS9gy9ZndEkPNFI@=%16k@ z$S27&<#Xf<IW`^dl=BVb5MxoVety;S_hqj@1xOSv=mUg}Ny7spAp7w!Otc&Y%>Wb(Z z>ze3#>1ONZ>Ne^w>#ph^=tTNb`ttfTeGPpNeJ}ky{SEyc{UiNz{Tux|y~J!#{0$!rY@$Q zra`75rUj<6ri-SVrfd_>oWq>gT*zF++{!%3JjJ}me93&>e9L^>{M>A_I4n6W4J}PA ztt{;=11*CsQ!Ga;CoR8QE?I6^?pXe|ys=2FrLASHb*v+;qpY*7XRW_m?^`uCoh@KX zx7D*XwKcc(v@N%-vF)(!vYoY|HKW~VH`}B32KFZQmiBh`F7|HrDfXlGkb zqMp*83Z7I?ny0DfH_vF#G|vvtF3%~?OV6L4&mM=@<<0GF>22rj>+SE|?7i!~@BQSp z`<%XfzWTm~zFxlNzBRs0zU{ufzWu&zpT{5YNBz0{`TPa_J^VZTd;N#}C;Vsq=lwjY zGF6?bM>V3FQ?03vR5z**HISN3eWEG^Dh0X+<^>i64hEhF{tUbhybXwhv0yw{E!ZpA zCpas3I(R;KD|kC-3)KkK2{jBg4lN5^2we%?3f&F`!u7(9!tKKy!xO{D!zaTx!itDC zVg_89Bhon1JkmbWF)}!EIC3I#E|L|w9=RQP7ok#Mq5VPN4rFOMf*ob zMVCbHMju6AMftG;v7)iku?n#&v1+l=vHh_lu~V_%W0zxBV_LcxU7D^)SE1ACdURvD zIo*ctNDrU~(VOUd^aJ`GZHe3BdE&L>b>m&*yMV$G)S0mG-`Et$Y38!;O2$~lz||#v zCWhkglM!D<5LGQgG<6NJRDHxxBE(Kh5I0pt%yb;_QftIYLc~dz5hL|Od^8uakpgj% z05Ops@z7UUGekl)5e0owILyXfM@r?_y%~-@WF2poJ z#50!>%j`iMvmG(aV8kyh#4hU)w{$?v^2u5R(aIx4DytEt)Ix;v7}3cmXFiFoJiCDvrDC06BjOK_giXyHkf{5ZBqKVmvBn~5r zScC}TJfes4@m+{SNpw&Qws9@&#ZY)XBs$m}+#kFWe9Cxkfe_D|@TXb^hQga7{!~F7 zQ|k-=D$n{XBlY9SXjk?NAfM#Z7`-3aFc@7pm{8DNT3HE)8EhP@ch9}0D{gaVPEg`vHngQ26Llc5*# zk7EpLfQM}`>@iRt8o9h8o@zK6ds=v!f(lALca>_IsVPt-@_hD+N$qlS2eVkk*$#@S zFpsmvtiqvG+zpxYzi?Y|yMUcIle>U>dT`=-j0B!GRknjc)1Jvv@%+h^H}L$Q8B|7% zv8M4f#u#=hxLkf@JM$n?ZEqHVF{MJD(uHWUq$3rpiur6NlaPx2jNnxZBby_CMtD(k zG+(qrbVPJ^ba|BY4k2;OOYR+e*VvcXBKkCNKC;5~!Xn5moRX#{GjugA>B*-!jhmjF z7dLTV&kY7+y73aKEs;zEVRN=XKDGy9>czmZ4r`P-&4eCA%sZ?QVUjgsoVBjTD}{{1^G>RV@nfDCb%!R zEh>gFTn+i~U+n#XS!CLCAPe1)+Ji$7D4E=CY1Tzn29MNBC3gU$$zG#jx^>`e6S{Xq zz9pE-X9ZWlI_(7(=15H*>>y;VjMr?&d?H!Aoyc__GF@g+7?HUWGK1-nmf(90kK{?} zqb`A+wHfn8Y}Tb|BJU$o#Cx4VR$QFZG^W{4=SU7cq`_Ac`K_J-pUsT;jOfQ(x{*F|HWHr zo>U(FH+v<;Q{w+*uQdN(?3E?ZCK2|E54t46UfBgr5@D}YgdV90bVxn_gT3I!n7i!`MyQUD^Y?`DE!7=~U@7WaFnxXGmvCXGv!#m7fV6XM+Ed$kd;M zlH!u|s`Lif_Mf2e{DR#5H)tzZGPaB(BbZ-Ju$G#Dx75zk2`bngmfn_rK%O%#(=9Wg ziJfJcZJ7hhvDvZqG8TpG79C}iz zp1f0gmXurjHfQHlu%4ouse24OzDZuBWD!zTqi?~O76D=6ei^b6~K&56KHp{m|laO!OqLXaU7hwr@H65KXAOzy}{8A zOuB?8yU8#a&z4xy45t>)lKAa;*X zQeXvD3AaQBAu~+6Z+bBHKU^D|#4d42oD!GBE%AWkn1j(`7lYEejHJAzlBABLk>pqC zT-rgO-Br>J`s*H&o|0aYK9Vy0#J60cJ%WGz=ns~0B>r;5NGj`+yUPO(_jmr0WPCb? zLt~zhCsc~N7QZnw+hnOSx_;v7>C$spwWKQnd2MT!lAU29JzL1x2v(F`)SWN+DmCtG zTk?Ar52u2Y*AU-xN_d+larI2>26(Wznmma?@OQ_F*2CYuB^rn^@_rK;HT%E^r2i)J z;_SiDFcpGI=`yb+vCa66WK!+sh(CQR`CMV1&Z>}g-Lq$^znwdAMEKLAPmBIR$ z@o!TVC_XOQJ3>w2g+gKnR1rp~8|FaG@YS6G)xt<`OXv*JpddI;Z3q+tmw#ZGaPfOZ zegTty8tn%!ejw%#xwpE`-<)@yq}vWT8_UYYc6**;4U!o(0N-Ua_^Na9ZLW!Kh;E9W zCVzXPTW^*0&{jo+(WmGLL7w$1?$a=v>C=;76w>;*K8HT1K9@eXK94@HK1H8TpI={q z$*mA}MJ+I&>w`nyMBf~0zBcfgJ25$zF<_NX2D4!y_!o=8ja~|l`EvaV=>1l~t6r02 zDxL;6<1Bd7zw6I485+XVc&Y!3$<)X&|8)kv!C+Wr-)7%#-+}!6A^TP2=-)t5m*F_@w4!4dDpXv2R4Lw>w-l5;B5>YJPwp%lz^-iBWM zE_n0zoe!K3p-0RMW@RaGE7QOVuM1vyGcdzD&|T>6bT7IuJqvrncI$ zKAKdTCeJz_vOnttq^|@yYlfbWl;LS|XS#;0|KC{GgoiRtGF`GrvQP4-Brl%Q3VlZM zxvxZ>i5qzCG_K)rO7J!c!EupHRgyaP0g%*ZLHqa4vhXI&__plJG2Tm;Gc4FttRK3wHPX7MWq4HV`&!4QS z#)ubvh-}Ee#faT}Nnbu3$P*|YXdLJr@S$H0C=-YS>w`WSIrXEw9t@^{L1rmqpf?jXd#6GmV_E z2zlTc$o=||?>%J;A-ns+UKlyuD^3q`w-Qed-&WUfD>qx!73mqEFk+nx#xfO{s6Q(4WZU) z3cXINK$}2^K*xX~S&_npU9yK{T7|@Wen3np7MEdAml5Ith<+boTindv#oi5$;9mA? z_6N3+W8#2t#wo?A0ET-5PD4&3PGfYpoxquh4!1iwhd75hM>sDz?>QehA2|VTjGG%( z2g-5FLpxiMTZ!ABJCr*L{P(%sdEEIxI2Ljjaj$Y8a-VTubKh{^a^G>^b93|Z^UCtd zfgx0ZSCLnVm&zN$8_65bn*g5BB;I7+6y6@*Mc!53UEV$3ecl7!L*65vfp6oxp&HJ` zFT^hlZEjJ1F@Ae~7k(dpUnorb^Vje<^SAJ~@gMPD@L%%(+u3}HT&g_b}fNk6s-4Wdt>BTN_ zM4Te7iws|LaT{?*aZm9;@gVVF;Gsjs!^FeIBfy;cO*{(7=NR!=@fPt;@j>w+@nP{F z;=jZn#h=8V#Sux0q_(7?q`9P%r1O7S#NLu!l5>)4lADrj$phdWPbDuUudp-xCHY(O zTJlEnR^pMyr1_<#qz$A^q^+bKq#ggu*X=1?B3&=tB|R#ABz-RZTlzuz@&E8*xw7iA z+Oqnxrm|MD)~Nl`R@P3|Ue-}IPqtEaOm<^hy zo)cb3A$f6mIeBGy8vK!}@@n$x@*48t@`>`<@*VQM@D5)r=Xrmac7^%oq%u~!)EKn>|EK)31EKw{~ zEK@93tWc~}TvS|B+*LePJX1VZXq6_VPw7`u%IeAv%09}z(0KP(4p0tM4pI(Q4pFXE zZdUG89#Ec8o>ZPzzE?_BXkVi;s7k2HsVb{#s_Ls6s2Zt8t0t=Es+OuYs5YuLtG1|a zs9veQsRU}FTBH`MCF*?Y!s-(0a_aKx3hIjLPU`O9Jq=V3SC3GSRIgKS2LEZV`mp+l z`l$Mi`m>s&;c9pqzNVn2lBTw%j;5}ro+d+6Uo%uQN;63_Q!`65TQf&ZCGMj7Z=Yik>68*6{jHqkcKHq$oOj?+%j&P2V871|BjjoMAx&Dv|) zXW9hv#nDN0Qk_gE*C}-QbVYT=bj5Wob?tP$b$xVwb&GW?benXWbz5}V@GcV!39(+H zN1{VtQeRm=KtB{dl;h^D=;jrPofn$^#L&mtVys@gWnz6dE29VKo;M28@b&Pe5gN##* zGmQ(3%Z)3HtBk9SSB)i26-|{)siw}Rk*2YxnWlxNMZhPQn3kHBna-H*o1U0nn7*2@ zS(rs;u~}k{n@gI@nN!Wx%(cuJ<|gLW<~HWG=5|0@JD59~XPB3o*P7Rv*PFA<56pj< zUzlH-Uzxp@uqBrzH>wupwWL_`SsGYcT1HsLTBcg2{a4&J*K*i$&T`%I(elm0v&yXU z{}rbhtd*?QtaYvRtQn|y)WF)%I^H_fI>$QKI?p=ay1;tLdei#A`Ve^M6YEo}+2*n# z`G!hJ5nI$&)7IM7$=1u(8?~nT+4|d-+4kFx+0NO1N42R7z`L_-mu%l{4!hqTv(u@kUF5vWe?0N0=?JexR?E~$@?PKjz?bCqvO}EbgDt^ZPm;G z0BJ7eDD5cYXy@qc=;avX80;A0SmQY4IPbWC%0pR>OODHqZw|H7;IukjPTCoF=5Xe8 z=5n@jc6APLu5hk(o^#%G-uho2(IeFSdE$KPG`Jit$`x>hT(w*cTuoigT)(;|yJoo- zxfZ*Yy0ToCTvuE=x7qD*``lqSTH3l}ZrUApH*hy`w{mxJcXfAjcX#)2&vP$zFLN(< zpL1V!UvXb`e|HN!3XjsG^5plF^3?Fu@ig!>^ECJT3N*i^rRE z-k09D-p}4I-mhLiM69`d`F#a^1%1tYt$m$+y?uRreSQ6W{e8=P8+}K8r+rs^w|#ef zcYXK3*m&T3=zHX&{5k!_{Wbj=-~=}FckmDN5AqN85AhF0Wr^Yb5&n_>J^thVEB?Fw zC*TYI<^SRrQxZx_$teY;q*AH6R5Pjr)tg#KEvMFl)3Fbkv@29Lb(eZf{Y`NLyZ}EL zk&FK$Cf5Y21!@O+1bPBL?Tu8Q4rIs`LeY_sH{=WX zLsTdb3Wh?V>Y-ml14AQ2lfY?Q9$Ep0@~Y74(D~5A(D#rqtPbnL!7#|m;Yc_dt{d(i z?i(H)9u=Mxo{ab@Gdw*!BYZBL6}}n170wRd4&Mpi4c`OH_d)n!SRQdjs7TI8u1IcF zy~-O&i8P9Ii}Z;Mi3~;EtKpFmk&%%@k+YF&kp~e?REs)I`ltalI!wrsSfbXbEozTC zqRwdTXp?BWXwT@<=rZI?Rzz1uS4CH&j?>!cy6F1ohUmuVrs$ohASR57V&a%2CXLBr z@|Yr~jOCA&iq($QkBy0qjg3Rqwh6I`u}QIgv6HdvWM)A}qnrzEq)jwjNV+Ipfv!n6 zqFd7=Xp(c-OmCsL{vZDTe)<{xihfUjpg+={cp#oPo)XU&ZxnA9?-uVK?-3`RD2XrG zkL6+4bd*OFK;m8p6k7YCIwE~}$X|;UKb}nvIyi5G@QYT^dxY z{lKGJ7xl;D(1Mj`eC-Lb70}^qg^u+c{B09`K8dU>VwGpF4em^0ABVvuFL)=xYd?gl z0;izIyN0>~FU>mc3vHr`6oqQAmulp@oW!#+yY%^CN!No;Nfea zfM{m`$uojp3P7tH=RXis5T-z%O8T~u*;WNA{k+OJ%YT#C2UDI6mi#O*Zky$l%bFKm^;WtV}+ zJ{KJJDo&QGIJoP3z*{d5&iY>P)x+SbuLDnA2afs`uNK_&>ENX=!FC-76a7}e3l93u zU}368l6{PJvTV!vso{1TY}wr@i)I7o!Ojp7Fa$={`u zfpm{0Pq`<{&K_$cJ)+6kcvc?va?g5b@yIKRsb!({pA-MSj#?8rl9d>33!Q?(^xLSj@ur>|ebCYC7b~URSn~t7J zVhP2;Ij#gmjJ$G{Jw{hTv`up2ZxL+XDmyfGZ1BU}#xn(6J~v8csz) zP3<uWCFb#5t1%X>s5eSks3}?{-AQ0pk|MMLQuU~-mOz@BYM59Ru zM)D3-Q9WalvID*WIh(*}30~p35v=QjuOGv5b)!rH2VTyrJEU?Zm>BHM?CT7+k&DxV zvx0LAAysqkKCYJ6g13&x=GWy<=bz`x1YHF?5Tx}K-WEngBSkFnV(|^JK{8dMl1`9% zW#bUuca?vaH&7hJV=Aqjt$e7At9q)=s=Vr7)brH0)ox7}%{I+$R(bd%Sy}`;|N58R%K>dFau5YkRMHb-o(D89t$ZsXt8R2uurP z2YkV{!Ck>Jp~ImH;Y}#fP%W|{QYLyK8jfv<<)W|A>Udhb!$VX%!!!Ax+FFTPMWjYi zqB;>rPr8AUr&|lvN$RReuWEAEm*r;j93PUn-Ca6!H)HMKBNao>t=)5_outq9zZo(Mxo$jI%RRr~? zawk#By3lLn{sGn8#7;bg8d}mdle}j|)Oz__Zo{XOJNW|L%j~3Yk zdDsSPMLw?|yFZ)s6D4<&hI%uf6luwC_YvQYr!eE&l~APNJ;~Kgff$}m{<{n^6K8=l z!pUNdMXj)-l4Fu%$Q-@ZarDK&cJBwi`%oMS&O1ST_k#2O1*~^&m>W(o-wFPEHaoGZ zJ0PFeOE3`myt#rjYvL(wSEhLqtMwmF{A&1=uW^tbvE-g>qAt#1RT{p{5S%T5IynWZ zfzgB9~8q@a0g;g2&=CQ#6pg>v=~6tihi${vD3 zHYb#^o1us`LkWA+Yl8Z9K62!1{bi{9P`Ta@1fgs_9IOO2Yjvnt1yHZFY3(9R$PhqBH z1+!0+s?2{&Ovs~mM~?ielr6K$Lb5`z*X?A#!Cp_3?S>V6F5|*pE94aHb#-}rp!a>{ ztAO3#m4CwgKB8)gGzx(#02OM??wQU_yC<0xQA zgxA)})E~KrrC_z~G95NuV^~UJGs?M|DRX|X@Hzl4=UQYIGdO7}$k)$C9$v>(uFh>O z0hL$-=(M|7`&frTDK;N!?Jd^5)>GiDJz;8D^KDAhON&6cUD8&`))=a>e$a1EvCXw@ z1xqoDVJW_|>A+IVV=s>i4mIo@!N!__oYWy?qX_Fw;|L&gQra;CRRk70b}-z(OOEH5 zD{mb_W|rhcc4{gzPA|Z={ooR~J-|y!qryX~y9O9>-Q880`eY5gy_nr-pLYtBB(r_H zpd)$c`;MJS?sqaf(+K}u>`W{DTbZ3n0p7rLN*&mO`VI$z*Mmg86%3UKRS0DulHVUX z9=aZigi9m4TQ%GUd&~6jy72b!5%|py!k?H*U%ZHcsqvLJk_ygOtwqyCgAhEEY>;;?s$-W!OtL$1Y+wF(ZGr zk0yP#$ttLe3R^nO{N!0<7N1>3H$S<1u4kpQL$o-#L)$QGe#5_seIopVX<)g|(u=Ws z5$p3_lbfjpNc@y)+8Q{1gWpG1Q9Y*^+&%KDe8@zu(x-qa@mn%K+>xbce=}=f`%FPWs-z zv1s-PcP#0lXBpODmmtrrIx`>EB>mi?Km^v@09z7&`pKpv5%~ZA>cUM#h1QvD;!&SL zjn->PzdD5atT{QkfJJme4BP{V#4<#}D}YPfKsep&{9G4k#xz9H(}8K6Lo9tB$Ogm}0vULR zoe@`e1nE z@(qzV3##PVsH3_F9{)vDQN0eoUy2H!A!45Z{KO5l zyB8?R7)0;mfTalJLb(X6#LkH1y8>7Fig^Az(3K1*;2S{Yyb~2r4=|3e@#F(~BYz+<`t2j~e@<}}cNv(Q3YQ1cY!HGt7H z0ygjqkeWF_2<8E=xdoiyHc%TUwDWFYH=TePbb)Gm4eFb2MQrjKwN1YvLdno}MJ?05 z+J4AtA4VP1(}-9EItOZ)`gIg=o^;>~wUO-}jrygN5XY_Jw2AS#oNM4Yq|xYAMJ8OMPxLGou2LWf-zHA$-?dYTBd zX)92U?a03SQInLm#*u|@hnl3_5mn6q3bhUB#|~uWKcFTl#JkXB7eY!^aY}?5UPKrLi3-7?EMN;=#(DfItx=(KC19%+3jxs z517^~U@3nA*-ArAfT~b!e?V1HmWvJDc7D_qE$%7-ysIy8mi}M?Y(Z_&J&5NXp}Oeb zi0X=>u4s981t4K#fxwIh9<~QK%s!xEFM!6p0@=xpnxbJ(1Q=O=U^4@Ol&u9qvmSWa zec&_?ftqPiQ$PSLMXyBQ7d!_B1ksn`2W~@4=^i=u5Gt!fMLi<2MNNEb5uZrFp`ldN)D2vAW5R+ zEICM)C_8*E!#RE_YwGd-dJbsk*wl zYSpUM@C%wRcYatWx2d&VY`OCt~XZ8FwVCAq9K0Y_0Rr)i$iQhuo zvG`1o^L($OBD+*r*#o=M) zLwM^vh1P5m`07kuBn>oxwV-*bQ>4&UHxl7pc;-C4Om~{)@w$za%M=dvf!W>e|t&wmQYPOpb?#CPWi-aY;J=<#-?O)Qr|7xRlQWB;p z95FC3)hAi$^_w2Qsdb>o??2&rg_?&n1-JP6c9V!jcti8O{Tu4WNt09;%Zu?zI^p=N z3(QN<39Uq~XH9v2(lt^~{we7dUjyk9{e1MS=nd!rz2eVj1MFWqp*4^i+tkSVWY&e^ zGvIR8Z&0(l5t9HlyMoYlK9;|B{x6#tcmarKaYh`m3Oj)rm3~Wq#qJ>ZUlZ=UqMJ`W#ElJy?t>7_wOv*CwxV$M% z9(YBr7QH5G=4@&69nIgd(6@zq`Y|~#nH~4{^fj%uroP=%u&Flx?+X|2ew#}v<07IpAuQU@QHX$0!vi!z<@HNmzf%4WJh2VygwXiu9mQx4xZ zQPx~A#}Y5|cM^W=afUqJ&4i^AP4su8B5Lc&(PF0VgC6gb!^=JW+n0`{S9^N0zmc|o zx;%L%9&lRXe_OL`%}#vKrQ`?jm!90$kvqZwx^w#ENNuL^06WmWh$n;hG(++{$x9@E zJo$@W|3XjxFCrc?1JsTc5^jLUk@`{ll1@wcOX}(Hc)c*gi3bm6$pfFy?`C}=#}n{^ zd@T21Xes6v{tI`Fx#H}D=N`O(kwLu*^}*<%-!b}Wap5I}mlYmz*5&?2Xj`^U*+<%z zOH-ClRXx>^4DNZ|nucGlXI{w#kH8<;zx+}G_ylg6V2IB-vGD!-agx8hKBEyl{LW4@ z1ZT|cPKfWIYCiuIK+ZgX>tb2ZET$fE7+VktBU49akE{usrFwDWr%L+Ju<@A=trCU_Vt*rp>xX)o&lEY{a}^;37i#WPM$w`d9=B^ zCw~@g>%l43p>6$niovidzn1a-Oy8j#`7I_1%8|k-N8U#}>tw;JXlErU^tIpfOjFt( z$?-EvFy{J|C^I*~x9J|XTTqotoi1&SJ8o4AdRBduy`~ zQF{!m%?hCQc)P_}Pt+p=tFvXOO9qx_Cs3aZtk067PI;#VT65Ge)JyNQO1lC5aeS6(^-yco)@f5vdkrkq-bD>Iuu`+A#l~l;)&;fN zJFV3=qK+HC#ad$2dhcbm)*ZFs_$}8?pmrQ?y_OyI;W?KtY+A&a(@sDuLHJGirGt%Pjh@}SLfyFFYFv|hq(;`XB@6K)rm8Eu-|ZR47ubwm5Oz&dU& zS~;|k3+&@gqP3%K3A0 zqurVg9o`hQGvl+xyNp)n-R$wIq8)lSo4hq>nQFVdNVHGyw9RXXwyL(zn~4@{xQ*VI zXuZZ~r&krN*?(%Q_d9wjL-u-QVjfdAd;QSz4cYB&MeEnu?p;CqH$MBlShR=3ZTQBc zZ5-I~y@B>|V9R$EZRC(WUkh?a9;*S8w2=a6mRd9VtGxgTHC;0V~>$=$&IN ztY24Lcm5=@q{*G85WdD#Bh3omHp+wVraT+5!f#&@-$Z#K=@e*DxP9KxS7ti69+oa> zlk~^;I%cKVjp-;X{HORWAH(-lk|mwu+d)H24m&-|9B&0w9A9CXl&CdyEo@^woO@*M zqRa*ck*ftF&~ug&U!L3LH8D#~+q|o=2cFB;yqlmg;T>p+ z5o#Qr_C?nD7o7fV3wrZ*;%fp&@?OVuLwfacKL(55B+I@H%i|YFFeiP!oH+|bec^1P zod3zrsp9iEnIs;MlS`DxNn0q&TWVxUS^;BHqr8TRqq^s0jPki~g{f)F=1+r`G5wn4 ziYgpcM#e06iMp9JVN9}^M`J3-)QlMzGdyNo%$qU0W8RHPgZ(f!^u$>3-@R6n))PSg z{{BsYe^cN;Oo2i)eTp%aZ{3nd5(}9yK`R)8vL&^N1-C5upn1atOZKcGPTjgO9WcSd zSaI6z7V<|!!8RGzPqV-I^nHNYs|+gPFpf#T46%3gyML` z;@1)r{B$bL>*3e6THwE<;TZku<(T!H^p-aFJVexzC`id z!b{@&SH^5nJdf~F;z#wc;(3KX0xt!|e5rUo;bmkr@NbIe7hV=$x5D*j3#CH=;pOnn zEBJj-yrA&%_zo6qLKJ6V9gS~eRlvNi;)R7jW*)=5z2fxc6pgQFRm6Od;!NLYd|9h9 z=F=2s`bL{7m@sNWai(uHzQR=v^KFVVeWT%>uR7+373Y`cqw&SA8kk>Joar778^^~n z-;)LTKSrL5NTz!<^y0ifTNE!PJiF+jUvZqzS0vLt8kUgEzY);ON8mjj0iALL`qv`x zutlIQ5rM}r0)8zb(DxL9m=V|_0-FB_j0lf_#die0#v6erGy>yTBJkyg2-q%0z@j+< zClz6`nTT!16su-U-dcEXdU<{)Yn}iLmbq@i)=)exGfoDqXPjgRw|3xMC&kI7pf;b6w!rJu@^v+?f!*=X@H$tyz^iE<- z`ioBYU(qmiSv$B7DMc^VxHrhHe8wp?lTw3HE8H{3I+)TZ#>lEjSwpF{ly;Ol8h-nY zQZu>JlyVt!pWG3BSk3%%fhaj6J%eR5P``RNXZ2QVl7sd6c$NPEsl$Ii6B> zbJ_2?xcS9PCCp!5DrG)=+nr%alhyC9ym{J7kE5`4$9+WJAlzSjd1p{+XAb(k6qa(H ztR=#~zyDqe94?Gh_3!zFZnttnHY(26hk+N$Tlk-+I7fWIKMN*f#wpHTGV~f_f|nx2 z*;0aaI3`%DE6!ea^mk*z)2n!T;lG++G0(1eCgC?=$By@s;&>?V`vsFRKNSjH7vHNG zn}W%h^NRC*2(N*d@E%Z{?>FoLlQF9mXW3&g{^UpePgI=m6JvhB1doJ@mlFOxtn}gO zLh-V~ufb;Cm{`Rt2>;G}hk0SeD+&MBe2aM|#j6Vc2H(@fldm}6Q}8p330GY(ln!-- ze`UVH{Ji2%2)~N&w!$Nn;!g>`f)R^oKP%oy_?OUYz;~+RO@&{E)&k!KP@MHHgC62b z_zE;ius*$(VzRj}d+fS3*}_#)vS;k#~ljuc-i{0MsH;VDb;<-$M2w`TDU zQ+%cH!|-p9ZxJf~s_+l+?O$jf6kjL&5VTv!?fFCLwo&-|@S26MNhtoB@Pn`$fR|Xs z-w=Ml;MHZ8D*m?c{boPrGZfz|d>`~l#*9|{J>l<|cQEg-IIjmaHB87?iZdOJ*@X$C z-xO!MW268kq?_WruQ+o|sCOvNd}GWGOvYqZ{Jik(W;^Cd6u&6^P4T_=b-qx#T^7F0 zY{UGp;(QMA#9>0cRdGJ&sA*y{W|`tFN8o1`6Y`JZET`zfjrXCC;@5?5hPP6rkK!z6 zp=ZK`=T7lIgl~k0VU*8`v)sp6C`_BDtB{?jWCBXIoIVM6Ul@x;Q{;>)w} zh?O^#K1qeI!8d7*xuAGT;j7JR@b?slgE9P8VS+bg#iNC<#20QcGEVV~!e243fR9!j zl@9z?V8Rzu6wfYvIs8Rq%#Px@g})4s#~8(_II{rm6efJ>S#iFykn=G?->i6X;Y-XC zu2vkw1Mypo3FTEDEco|z8R3iYEnk!>ilf?%-%FT~s}+wGzQ8O1U!`~@;q&3Y8UBYA zuPS^V{0MT?6c+q(YY3lT5J{w<9LVHp1`od@78#Q=;Dc(T% zi}0U^a)ycPkK0)IOnl84<+I|=gwMcNos8M9cuV2a&2;eB6>lwk8orE-R8_p4@Tq1h z_*lg|3ZH`Ux_G}U-bMIid{YynAr$W}e3JNetExDLD&RK}6VgXj%Jc>Km=g7!BT7JT{)5kAh013#Z;Xr>eNFMP!bjt4 z()jY6;x7mvC0_{`qxdA@BW3j3vx-j@J^~)DF;+1L7Gk8`oOqgWr?)5(;!)&jp>^qm z_`=$r_Oqcj|Fy;>}z#<_&LwH?`X~#Gb#b*kyhY_{VKjMahO zNb&i?onET9;tPZ~#|Ssv7sX!^?(|;oD85km)9|PXeTL$Tggd=j>SCeuSuEV?;mRq# zM7Y!2bx?e%aHs#9uJ|(HPG6Wabbk}SEZpg_l9T)Ut2w_yYkWRgF7Y3Rr%LFnLOhB* z8?;z80`B+YxQqXmKcBJ4Co3er({G(s{1xF&zx9{mD}_7#Ru&Y>%x~P!D&dd9mk7Qx zulQ==PVd!H@mGaAz1L91*9ea_vDnWN#n%dV`mjBUuM_U{VHXu&FT9d?$xQfA=sY(F zcY3m%if#a|Qd^k>II_a*V`!kzx|EpmTfG+(3Q zZ;|*;5Bj2tulYC?f2+iI`qVxuzUG}&{B087>17+J_?mNUuJQTp4T+x(c?;z^%Q43z z-x5B^3<9sC_)g&i;n%>J%8Kt2KEMnBFRA!D!uy)O-~|=mC;U0{9C#MR4+!srZx^DS ztN44ud*jQ;##9TPf8;^oS!6VGS;s~1_`dLT;w?7FLN6$?*>1HKu-Cs zY}R~_c^bT=x%|b?D3jHCF2`Wq!wA7PTz&>2*oOP#@wq+Em+OMK!SX*{AM88WK73i9 zdlyvr^tn#+%fYc2cM;AdxMrqjAP4(r9P(q>b{`sC+?MIJ9^36jIE`=_A>d43ZX2X& zz>9UYrZNKKH^RIFLagmYhFToopG{24mi`C ztg&Ljeu8rkww;JL;pfTYb6>$V2HOPJ9-PN+Z2KX?mk5k=H#wr4H5m~YhufBbTnmB6 zVIJafc+PFX`Xfw22>4f!|3J`j|Bmr`f;h=h$>ef5%xm34=D9IWdjxJ1zMKj9BiM(Q z|H-~}99~zjpMN6%lWlpfK^hM5=g#Gokhk7r`4rX#GiS93XXF( zSwEk5lXD||nTGL&AzwHiJ`dtn!nQg*j^)nx*k7>S-R9ia7=#rFdlAkfJc;|ozA7rp zqK|~+fc=Iz!MeOXoZP_ANBTMAGEjF`4Iu*Iet3eb2HTkgkUxS}^K*IobIpTz=d;C% zec}Dfwlt*|MJr6{YjXtkZA#V5k(^FyXGUCc`#lOxCENX6KhJy(HJ;msx++TBOo4(Z zIUt>bj+Hep(qe{fMoObayF_WTv^yz{m6j}}7o^QkX_ja`DIGWb)&ZqDhF?0M)XdOE zE+M3es7qXj7Qbc!8+DlHP=8qhgJOX(r=mcPO(rkp=sW9heJ>y~vl*6Ud3V|{P{YF&d+vl@xE^=P!m z$D$2C9w|3L(qc01=~UFGrlT%36L)nM>QHl_eVGT1%>rn77C|$#1lpu!&_FGR*6I~# zx>i9uwgwutbr0`aQnh#rm73enV3dPj8c zls+-tktV#u<>V-zi*}rAPDpA`T5srwpJ#RuTI@s6d{f`fH6NM! z&2Gn}T`+x75HWq23wZC@uHl-#qIsnBlJvr@JSgt2IO* zw)7lPdf%{*nbIvY5M7&;4j7JWrc}lZ^!sZrZE>64>$K(uSTj}Jqu_6kZCKI!f$I4v4x#u{ff z#FfHJ<0SUWbL|b$@Nyp|QSwn+LE2mT=PCVS?(_Glf%Kh_y=mWssyCA=m5)zp<< zSF*!mmq6*dSO?#BLlrEX}UpwiR^tF>+HkW*=8%zCFVz6u` z2khBsk3?&v$$pP5MPJ2j21*YjrLAJ?K-sLJy4iw3AKDC)b!lm{2PZ@hI|?DBG$8-0OiIck%mT|J17|fL=Y3 z#CLk*iHb)Gcl!LLiZj2bL}^Rsf4?Jf&Pl87Vh-_Qxs1l-0AsODxOxj)AL_) z=`Z@;bixCDZz4RGkqC(qq7afGBt?Kl^F3@(+1|Z~w&-<)sGb;CRO`+Vw25iwslytS z!ofPWVWV+A)exAE?l*Z*b7F2`X;BEdBo*!m@)X_yEM+K_G8{`n=^;Ze$dpQ(STB{A zx++;c!>i_gSg&Tzbo;DlYWTPnO!v!fpXCg{55%oXnA~1^#FX(;S;_BipE+>4lpYhi zPv+UOXs?t<$<{oJ+e0NoucVAq(mds*$4qlCRR;gR=a;f9uERZ_me<;Wm^@^}V)SpD!nSaDvD zs~KWEh~guJyMDE1ijNWQ`qgSE{(|sc=sCj|9u=qE2Q3kL;6Fz3>B67I*Qs%eit~Bk zcRRb||Mya%^Pexgn_)U#Qhc%SXUsF0A5?s~@UEsS=C3QhN_ZE1oe`}B#n%e&YUjK|9YbTqp=!Xd5hnD5Do(pO`qXZS|E1_> zFfp#)@V0Q*FFQHJqsY^sCB)}IxUV-j?&4=hdBu7|QPdlDNqkp7h*f;Ia92NQsrVk@ zu6{5`@ppu~`oTQK_X>CQgRP416YlBU&8GPN*or}zoswed{>_=r>dq;O|fG*|Ic!kt~wM#WDHclG1< z6+a`~*%e(<{1f5zMT2xx@lS<2dn11FH+Wvo3U}kq>XZ9))12SE3F4oV_^zH^O2yYa ze<*(Bd5NC~H8bo&@e9Ip8@?y`)m*>-$j^l5gqJIgLu9!IE& zPz#|pLLG#<2=$~MI^{u(W!j222j=Wk(kTafO-{{P7`_q)LFGuX$ySdgy4zW;8S=kh-GpB1{EQ<$@#p3A&0 z_6-LzuQ!nY!g?pt)rDuLBP=!h^Bp-2*Io<>Nv z%k^cl{!|Si0wFzmU%te3pp?K;Hr+vIKQj16cRutO4!BWR$ zj6sIAbV?IUSG=4VV>tBLQ{zU|4KNS;BRnB355}Eg20!4=h5c@El|@9?SWrv z=TceJ{(4Jk$5=TG%V)NK3WxR4<@U#^E4`VKh|v^1o1AF3x!zw%3G*zD!aWT)Y%x$8 zXSVsZB@Js=WR0Z0NvVg_@F|TjpZR#@jP>!L;q_8y!xj$XO)`(~ao5)bM`n3Xn#UaU z(j$g-JF>=7yQI`r>eZC`NlSy$Na+WqG(p}1lyaJf;`YZkIqzr@+}m2|+#bhB`A^D` znfzWAw<>Q6`Xkmeo9F4iDfyJXF?F|1{S)a-;B zZ5N#LGpIRtM{TsV+iujaIe`9`c&Ds9Q3E&@6V=&)S`LJE>i!d^o$vT zYvC2t!O>Y8rCb!XQ?@u6V~SzhnbKUtZ<|vZY52aSRLGpf6*1?qXLpjc|H#K0euaYh zf_#Xa-6Yd$i(?Pt?bX;{-{K2S4^dg($=o)#+2!|MO4=J_&l7Q<^N=2TF5HJCtvf1{l^b?}JnX z^(N*A<^-Js_OA4;9fmYZ(BwM;{t39q~twqa|N>-$T) zmeTVktKUyea~@J6Fqh+av_(iM-s6!-wQ+`ZFx*_+dwpImXZHOEd2@ncE0{+eVCa{a z(pA%^evR8nYOo; zoNfJ;xpEW{{{8*`G6nd>vOtf*oXq~kCHT*$cw*shoKdvmNrf*o3o*Z0CA6Ou!rl0z z?-Wlh+>JB(RB=Alv^<)J|GkRi^^Bj>JFZncvv8+(oT+$L;ZE=PyyDq~JH2BE#d8UF z#cjM^;e0fN-ae%&vGb;Z7f!O7Vwlnhm>Gt7SH)Wh zcY4GIinkW-^o3OwZztU83ri{9QMl6==2o0~ExzXl;y+Sx>eB+f;I&GjbY^`s&{yW8YVHuuyWIWW*kA<#xuW&bh>SM*<74G~7ZBd;1HhyQUIsPXrPCXgz z7@Oh0mEzP_u^qtY<}t-j3U}kbvMWygP@reEic?R+HUXc@Phvyoe^I#8vu;!TOW|&u z+7iXT7XFmz;U+46O}NuP^;7&O;Z9%GM)4cMoqnsf;=c=b`moZ9-xTijXIT|DxD?j2 z>)=1R;t7O1{oJn=Lf0E9-0AZ!DUPNleop`Qq2kGfJAL7sil-9p^oz?BM`a#AH|}Yc z;`a-890PSJhqdtiqjsGy!>x(|^YZFKF1d`K~+`ygpC!i{w6jHi=&VK38Cm zsN!qBN5#)B@$;Mfh`&a~*L<;xpF`s3Gx-pIl8UeS2o*o4#LsK^p6sFGYu+?;y^%02 z#?R@q>naX2L;QTdx#BQD!q4f;ODhgz75tpuy{O_vg*!cc4#kTLcY6Iaia#ve^#eQ| zx~|9)!kyoyryLjkB`VPP`TleiI=i9U;Fy}jn4d@Z9w9+rv?8#M%S9n4-ijQAc_24{ z+yNoMPPDr)=kgc?+Nm$cJQ_D=OFYXRQ4i<(;|N;j{sVpu>jIhQ5F9sKeE0}M&hs<+k9DZZ-973>s6EgP^ET-fAr9lr?HmqiG+57sk1 zf^*VxGsF$JmjB5$=>2fKMz9}l+X?$+p5S@#K2SIMygx54&xgELEhlIKouOL(60%;t z5w^@RF?xOEz}uJ8g)Qgw%RIO6JX9zwZfw|cLoDla?GU!yH*9%q*!FX=yc!`$3yu#u zsFpeYM_a1_)vQqb+p!~*Ev6Gq45vnplhU_j-wu4Li=)&;ror}mY;GZcl76^ zHpug6ZCGEV)J9q)l!h9X;oOGSL9FkQwlu7{QtD{f)}b`QuoX#ZmKp4?wz9Nn$zC$S zSG|{*s(x)*!}mYeR*{wnrT&I(6-omQTRD`TH*9ZGni*%Q%JxzlTu!4Y}jJv+Chf83raJ@ zf{r=9GV%dO!X$+3p1o_eCzaQGyC%H{TB*Ehr&V=VP90|G>e-&> z^>d=`fMb_9QSZQsdWl8S4&u2rHLZNkX^qo!=iARv`@yvnMBhYdQQUZ&AlDX0^M-3W zn)=W^QCcFcA4&}k+t-wO8fri&jW*PsP#PCE3MQ1}1ATIP!V|*04QIsjH!;h0-WPT@|I-ak^!W)M875w5n<7?_m?eRwY?; zL;VD$zJ{72N|OwAZIl+8p?h4(%yD0=v65ytY(r71W7yiK^o*gtiqbS0Tf_U`3wr9& zNLP-j;hLw+mAGS3n?yE7+7*-v7}k_2l{6p4I;nvvbHQy}Ic}uJ7{eMq<2`EFrlM3s zTHBPKHq<9j>SEZEp$4&l^wIE{VQR2FlmxUO?khEIp>b`j!E?(}W|^0&iS~o6d)(NK zG19Uj%P(z4O0^AJvy`4R)W=ZjWvEf1^sJ#yiV}K;e9y&HLwyceZ}W=37tKWHLDo5L z#KUN5=aA(!Y@a~E~B)mPotAVmo@iM~O+P2`u6^|9(#8JS1!mC>}Url?(R|>CYtARIB-1Q4owJZYaD!x(TSFw1Fno5dq5nkEiwQe3#e7o>U z7O!WMP4QjAD_T@dOqAm92#>X~;6E|(eERPfUcuspZ{9%S1@_Sgg(tR&!B;8%fpC1p z9ej@BM}#M|3Be~S{;}``HUaox#ZL&2uo2+TDt<<|k;+6n#m@?N{+XXr{4?Rs4|6re zzYy;HFc((*ityhJ_4hG~yZ(aT%x~an75`4+J3q_`75_oF^TT`%nJY-Q>%yJC<8 zN&LU1ILoQP-*RadPQE_{QSfv9J4He~iaa5_WW0&B!d*Gwxr;v-g*5HcTfsgZmyY<( zAMZ59@hE^h|Gi5UXVH=n-Z!AKHJcS@QIycUfeAKVq4P=1S42Xz7x});9O6;#)v*jd z1w8lX>9~u(&+oq@&eOfRo!{CMibF(E=ltAWR-Esr)bK#@3ikh-;(VW_##nxq-$@aP z>A?N-{g)b}eOG~JRGjb2)EFDQ8oZ$5e7~kPuVTV`R&ldtJ{heAoDnP%d zIN$fFy+7gS6leV)wf8?fMse0BQj0$Z^yY-pGcoHQsYM%taZVu~MV-fP%c9c4;b04cq`$%&2I3+inCsoOMF2Xb3ySo!kxeC z9~5W3EtmL-XZQ&; zXeXY5ZUN)u$bJ5C+{KUf`-kVmG<_t#v+KyC_;bRY9Y|@#+5X87U26~QzrN!AggZNv zj*6qZ0`Z+)%D18OOpLA{a5ui^Vu(kPSAkx-GxjgspTFZS{$Rg%PP~yA(`e}-@ z%b|+z-(Rjcx*outJ-~Lw2Mc%h10N|qM7XmzxT5$_;m$tcmg2*NJ9~!ID2#X?d7i_C zJNt+1ijNTP>?KMlK2o@|uc)Q?DB;c?<7vf53wPt>dMiFgxU&Npr}$Xm&TeF$;^Txn zJCk*aj~DLjQuZkRf^cWYazgP5!kyjASBg&*-U{zBjMq_ol5l5NlNyC}kZzNOJ3E{_ zicb;l>~_j1K2^B0^Qo)&G~v!JsGZ`|g*$)30~DVj+}RgRR(z&#H-7O`797V%nZZ%XH@(p;ci@S0mT;zcXoZTiZ2rG>;Ruq ze6et6H`rD2CBmJZ;Yh`o3U}j}XDPl+xU*M$Me&z~yK&NQDZX5|^K<^O;wywZd&oH{J&jzCpONYkf`ejl!KB?0brD67I(9pHqCZaA!~Zlj5%lclNiD zs5}Mv>UH7ny8sy!-y+=E@fJ{gt8i!cTVC;P!kwM){?K!m_zmIC&gV(QH%?EOguFY( z0G303-s$6?F%e1M6z=SQwkp0|xEo))QSrBgJG-D)72hG;*##|8e5Y_{7c^J#w}m^q zpi``@yZ3nHJ{O;ZZOq$>9~55P7RP+8;vWhxYKvmNK=F@-7qW#gAEEe3;rVTT%zG;S ziSRr&59W;(KQBC|&53!e;unQyw`>;{Rs2igS#4I#V-){dcxIa!^VEu86Q03lz}zVQ zlkjL8jrmWt@#oX$XW{8=I?TUN{JQY8HZA7I75_ze8k+|5!;1eVJf%&E`2ofM6rRGS zzE&coqR)CqR$n?Y_4Er~b7^Vf)btV-Ut7j6(?bxgDRM z?Ky}y7hxX4e1ruEFCi>MScI?`VF|)ggk=aXBP>T)f$$2#N`zGis}WvBSc9+@VI9JH zgbfHA5jG)gLD-704dD%hHxafYyoIm>VJE`d2)ht=BkVzV2VpP5K7{=U2N2#xcn{$q z!utq^5I#USjPN1C5rmHrjv{=Fa17x%!U=?v2&WJ}LHHEmEW$a2^9UEjR_q;AC_hA? z{Y@a#?k+w#zBn8sdM<2x9WOrlN5r?Nu%#t3JF9K_}N)>!U=5S$Cy3@p=TC^)wu zKF>ek>k(%M!hVDk2p188cu9ta);$3E?|6B{(bvZ9YlUscfAK7PqJ+C1t8W3ceF=xDBxeAuU?`8OMa30*A_d0x8$8U}OF)jZS7UUV7 ze<JP*p%5$YfW%X%HTmIIK1)qiW z3>K}?3Z?*3^^f}M&S8$z`P#<&sY0A3*-@4$7Aa22tS``K2C6M+?IY4g5|r- z2O(cEFinE+GWPY?73WQV$X%Jq=I@*6$hN2>02Wx6z{v;}~burhRY-2nu ze%3cLTMxBEF zWt9Gunkc0dwjQ>l)Lpa(+*?Ix6Y4$gVvdo%ZLV)&+1ro=QZ;YAx(zj#$I+kK+_E2$ zaS~g$04U|R>`SFo!v@|9cB6Oe8T&Q%Lf*-A#W8ri@uscq>d$eX^a*o)UHjTm=K-Bw zyDy?Y=#u%_pVd6`8sc-?kKu#$LC~a-2S_aTU_NMLYz~_XeatQV7VzBekFdzl`vH&8 z(_Tj9J`z$l25p}lXcy%|n<+2aQw7l0D(okoxtnXho( zIf?PqLt-CiQ`^|5;{G#o#c$~GrDyA>V9C$`Rt}tK13;T;FVY9&$;FUE&zHH{N z`)RrIwzfTNYFu9J4y&FXumtJ_>!CidIO=Pw_-kEb*7+xCx?%4+um5+k8==&|(jtda zdrO-pN_{Q09LytSkP6?J0+v=3TmxL$Nlj3EyF67Htc>ExqUXU>^tT*7sN`5QfEs$1xmL>Ge9Y=4SdaGg=Kr5w78k%pNr<^1245V zwQtL7v{_=@wl+GNo`(4q0OoFfqkvy2;Kbf}ey!je}r1^jLSzg)mP&s@)Z z&z#S^fAeoklAtUJJqh$2kr!tnmKQiRT5Qmo!{yP$Queq~$CWriX%n9NTzObKt{iOW zufLv|<)xN}b^^TiFAXguDE%n5Z#5J-n?oO+gn&qnb%nXsZW{LNzD~w7q_&L zqSVCF`i#;uHVS!`F?(VU<9z;|XlQl8^*@Nc45e5*&99wge)nhB!<6yIUxlq{iNKf_ z4Q(?heJ@r`l%BA(^P<$;j`UkMHSc(-ouPFD*M1>(K9nk3TDDQ@XxHE94A;=#-eY^o29iZ++O%;)F53 zF|^sCbkofA_o#rS-4)s6mKJ1`p0qb3+&dVak8o`n<;_jIA+C+Ev{<9m(C&Z2J)wp1 zzGFE|T`bp>Gm-x23k>aA$i5a!G)na?eW~+2SvIeaaUN-?bxQ$_9H(GZ3VfX?sbjxBb;$$@3`dzcA@hg6FdRBi(%(j&h6d zgJ2xX6K0#gw=+zCfBx^8ZAc#;wHE3OyHWe0%zDgjjA%&=xwX_0g4zwL1ajmSaU)q8 z8fpxAHbu>FzyApaRT5!m#eS4)``EAi+PUHnn027Qx6m+HbZ>^S7US(Vn^7)OI$(PH zRA^v+^3t=SFXh^FmKJ7|Dp=aAQL1F=3!Zt>-7ngD@Or93KexaieX3c2YbX5>Ih0yu zZdu5-_v^=*_C9U)n&puAoE-5b+f763Bkuo-l#sN=$5Pw3JfXlm3c2nAvf8l^%__c3PYtBigzpgdH5G#TVS##^!=Q*EwQ^#Vx)QJ2OhZ ziDsHoW?SQacRyL02mXtm5(^Aoe-&sJ-}8H#73XiLS={K5+9s`E+soAVX|@$51})dP z{~KaEN~x)(RW7COc96fWMrJ!Crd3;%Wr=NBzpqh-7GI3}m8tC6XhW+v#&vZl+FUY5 zZM!z+&Zno)Rrgc|r9U;(j9u7#3yFKFgnIcCwwphbCru%LPgaOE8{=M+eg;ZK?YBsO zrU&0DeW7(A-4o~6Xth`zaD8QHrx*Esq7T^b^Bu8RRcdNfW@gLUU&iMOR$I{xF=h@mWOYd4% zANktDPjP;dcHzDDjHO)!ucH#|By#xc9BMAc?WtBqxBdf^gtdIyuQwAR@tyR(JdfWQ zTKjSNk}m;hCCcL`u*dyAXPf)|)(fRCg=^DTj$>ho1pPensw+L%lg2YDYlfS@{MPf$ zPMi@b%S+m9bNi?5RFsyagOJAT0V2&{Q~Rrb+79qB8(Z4Db8Qb>5qX5$)PeSYkKg}H z!+tm3@kemiu9yOz4+Hu4_iqaPn*#q^Q=kT(TW^=<$JMxg_lFgal9PA+?%5PiF5LCI z-=}zL;jZ63Qt@=cUBCMe)k4QbV-7#(ulqB_p>D&^`S(7oII0`?x$lMSRy><<*B`uD z@f^b4ccW)3o?E!{J3UO+BzX)SNYVM3C>eJj&>P z+}GzSzUHS~eDQ};UgEocaq6naK5xy72<(@k9GMILUs2;;0hu~`GA{2`ap=>4%V!W; zA%5_U;xDm$-Ny@L?t?P-5efY_kHcl|gUgu_0-w(LAU}e@^9p3f50`n4k0T!U9lnnH zZHIN#wMXZ6=QbYl0)+Jl^tphhlF}(ZYCe41)uBpB+lWt0R@}$@81-5Z{vZoOf5|(< zcXbTD8dn^-_hICC=0@BHtYs~k@6Y^L_lRL?^Bv0;BXbVpz|zx8g$#2ISw6#jOR2Eo zlR&As`OHfX8$M-ZB@CZiN~H~-cuHjr?=_{YhPjGTHp6^Psi5Q>N->7d5~X5>PdTMW z49f>fWkj==5|X~w;QBZszYLC40;$96DTt+)AoEHel69BSr^5Z@lN`_V$>ztWb2~mw zTwB2K`KR=dVTnSi6vDs1e^cP!6!=e4;8)t?IXhVY{U7&ziPxN+p1dEZOQ7fIJosM~ z;!)&L(17wiC*0f9I_}~pZ|3X_BVfPEr#lMIaskX^6i+PNy+2DU&QdfArDq|`>nff^ zxO+dhQJg(nQ7B8Jf|F^~!sXrCbzBWT+=l znk7U?gb<047$FKF2|`kYWb*!*j(1Ceap-?XXo7IJe%*lcy~4UvHQ7J=DjzdEe()yv z7&1$~Lgs5P6*u?cWXQ_P8-x!6Q~<$8+MjJpaj%U-%{# zuAcv7zPzZ1{m{-hc>a_5=fAz;eEyU9`tu;g`TQsI&;Ja?`TQqCji)f?YZT}6pUgl1 zZ!6B{KNwU|NLK3oX>wU)T4@Hep7Ki|H)9=vAm0J5xTBa!d?5NfZ}|X zlZk&K^nti^lX4`D@PAg0=x_9~oM4%<0%gfogk%4pObN;s&RM>&4Hhb2YQ%Z2FJ@BV zN^x&-f0!pIRgEiMs)}ZptYlm{Q(UeoD4QPe+Z2o|Q>q%~Ay=kUH6{G|qH$$P)i{l9 ziMTSQu;inl9LnyuLH*fFMdC_6*S5k5{rmfmQs7B$?d+Y?x*h$0<^RL^`FN>2a+xoW z*h+vW340l6jN(ooamii+@2NPSXY?J~FTmR<&gT&Qk@g~ZeZ~1KKo@O42QRC*(??ve z7r^r=?(`Ao?RoI~6nFZFbM_o~BE_@G{?FR8;Mcq1&tI3*M|^5O1v;s?(?^`KXTaZ9 z-034u+tc7{6nAgnQ}z^i(suas`)4_aJ=>E&=i7$3duC496F>{pepr5^WVFY@hbr#$ z3&-p+@al?Jl=w&N5%5P8uPXeoJq%t@@fyNEupfXwpm=TJhwLHngo-~Q{C)dA`1jA? z&tF#~;Ro$OpbLt(5dNNh5B!MYt%Sd8-vwW%czfXo>;dozigyvd-|hz=sCYNw`|LjO z4vO~_zSr&re?sv-!r!s)fX6D{Pxv0Y2fVQ21BLIlyTLOnK3MoJy9+#-;=_c$ZQlmJ z*%g02eMSo3X?Fr$R(y=`9d-x!DaFSNf6KlFzESb1!r!!Sg3nfbhVX5c<9tRbK1=vk zyA`~P;`4-Wv0K0!DZWtn>-Kf<@`^7J{+i`DrL>AK7rxnU2EW+_fBt$`3EyNl0ezwP zTHzb*M(~dn-ynRwT@SuP@y){5*>&J+6~`+RzqNKP_)Cg!7rw@>0e?a9UBX|ruYwO$ ze6R4;b~Sh##orUY(yj!5T=B!gSJ)NcB^3Wi_{;WX@H~ohe7Z5q>@x5N6hA3^sa*=5 zQ1MTMFR@F&f9#AupPmge||3UbC zJ0HBW;^@4@Z=Rh8-azpi!spmI;1w1BL-=ev8@!0(w}j8Kv%s?`9)V@}j$SKG{yj ze1+mM!YA2Dn9o-{xA2K}BIeT-FCct^oq+iS#fu7m!M=cbf5l;9jNf=W9`hcGmk~bB zj>Ej8;<3WV+Oe27SG=RFGv*AATU_yM!kvGkoQmfZPU}19 z7ZlGU-1#?(QarzKT2w>-(k7Iig@hl+2uaxYDPC0gG4W^fsp1a_cYaL{DPB^z^Mmr1 z;-!Tw#w%S^>93U_{7#wlJ!xbyqcPx0!)ou8O4iq{nG{K_;} zypC|^ho-XP^@TfsHiZ;#DBStKxli$?!uP|*592{l7zFvYrEuq8=WE5=2;YlQuCNkS zyuI)}u+lQ-J;gf-cYc80RJ^P3U1k^JuTi|a@SPZ43*W$s_Y}SZ_Hpnvt9T#bZ(&p} z-l~fC6TTfIcJVe*e4y|*#Sc~o#Rm&_eyW-%K1{gtYgJqEk;0uHtj81|Bi#ATDyH~& z;jf$5aXvW|pD6q_*q1^luJ{z;n+@yl$rYa=e53fsicoyE@C~qEGv-$m-obsGFMK^l zH^U-P@kPSdVLSuc7m67UT5n-wTg5u}HVqSov~#xg_LCpm`{Ac0LKgYw0Ul;D|f$}N-i*RQTltu9y!ks-(D#d>l?(Bgg6#q@Qvj@7~ zB9sol3wL_A_XZR745iP5!rk`?Gbx@$xci=A z5yi6#cmD9IDjp-;`NM0ecsAkAA6^f|vkQ0r@J1+}L%92{;~d3v3U}XuT&s94;e9a{ z7~cj{JhyOuGsBo;isupDAKwwd=pV)N3U}YPWFtPvKly|^e`EJ6o?p20H&#IL0>YiY zu}2jzDBSrQtFL$=;m+S!XT=K(A8v-@JO?RWM7aAdXhF6I-TN!@Vd2hi^aF}NBHa0b zPN;Y};m)t~_iU8={lp4)ewsg3ys~iTkNJS&j|+d;K8#(AQoOeC0vKTct%Bn9h3CZx zVT_hiyrFQ88$~@w@utGFi(jRO6mKcq`BloUcpKr)uhIy-_l>`#m#_wfRm`=HEytW)c_4=(RV2<%UfL%xi_ z^9p31SGde`{2lSQ@9=fpZ#0y*MG$CzS^@JGw4lU(>OyXX&zB{Rz>f#yJ+-Z=8H?`|-h&UrI>w!d5XcK4uSeK}z-#CF z?-3F}X{{Nz;rT>^=Rqiqz~x|n!MW=5zL(5n@w&s~&{s)@>liVG#xVVoFx{TVv%)cG zjd09v2&4S58^qQ736B>nGp%{9>Ci!t z8=*8pz}iC&j@1t`@9kL3=O6^f;5PubWBC9=aD6=PMJxwtpk>Cn=GWa)+dMcKc@u%h z*ZT?N$FPp+Far6A`>hqW+!V{qdQVoh3vV7Vyd@(8sMnkvranFve6IG4%wy^4?V9&bRr9SFg(;)@rs z3yAlhS2Kh}SW?MM4~+}{?+hvHcd z&O1E6XddiapJRCZ;2F`+LBPJkad{ttWo}FP1~|t!=wrnvGmR5Xzw_P%jOWKVy6n_t z+XL7(NM~}Ev)3Mk8wt;!!+Z<^_rd35He{X$msvIk`w!%xT%eDld&%!&KQ9$T9L#mx z_^vTLZG$v7Gj1d}f0W*} zBd!>cn%EKO3Pz(CetO@7)gX1xTq=d0q%@W-bH<-&q>l*F28@oUJpyTGJED`5##{C! zBnO*|1o;TwXO5%C)p^kR5iQ~ChJAnBX0PcDN&KPF_MJTr{B9;E_EyY7kJMW9 zR;`n7pRk{cePG%%S}vq;UYpNP58D!$O2U_0;8TG<+UTKejHUjbSMmsY<8~PKUhvq{ z4f~xat&kpeO6Sd#xHOy6*{z)~p3`X8&)ya1b!`^xwWt;1Hq|Zd)F>^rvhOA9$lE9}Q8FPJY{!}4YztQ6^yjBAG4C;fR2HMFxJYhh^tNNK%g zA2y|r%;@|akpL?JOw=4xE&@&RXk0br7U;vHB`;&2wd@n5w9a(LzL+X2VVio$O5YQf z=D}Ao`>7e{kYUdXEtJcdCFo&W4!_86p{MqHLkkbaXl{4;Yg%AqeV*N8*}uYTDgz(Q zTan`IH{3w&YS}ABX_jHXE~Tx8mJgIZ5#1T3T=s34 z8t`s+ffXO^`xd|!_n>_nhK4+LD_F?Vo|$n^+i{tlzYzLN>jxVt-oq#yhw0kW6oh9d z+5#}{2Zq)T-0ulIPiNpuo^DBL|1(YlG+x8Jxp;viO}%yyi?dz zHnpWU%9F5gf89S@T`es^xxbg~!ej2dpSJ9MW4Tfq-o3XV*ZgR*_;l!F*b~Y4GYoA6 zDD5y}jRUC|o_bnHlD=pcdTG98ZyVV|($B~gses-F+60o0wH^I69kJBcl2x>{)S}eU z(x#BoI7_QZO4BUuw<#U8?4e|iE|2GXJJOLB1YEOCY$Pb{G1Q_`%4}(0NolI3B`T#w zmbSB$R$5y3Q@Us;V6QySmhf9%(_ir*!(L>jkbY8}50D^m`d@$M_>&iu2pW z9`W@t?&+d-c)X$R_yuzfhOv>@Llcx>8%zw!_!^e|o;+SLQ^a3kKf^w4vYBFSMd@2Z z`!Grk?Rx*l8faHUP%Bxrla8bne& zkDGm2)4|!|AkLfEhIVN@m$QaF50tXn1vo3_2wD(KH}oAwUc%D0fzoV4>k2+=3vund za#}@k%|Y`iByRgLo`^3EdsoOSSlSa(YHDfeOKFVdSOiMDEJuq_I&Rr7j)KY*#ptkV zcn3AdyRtoczWSkd#A%*P%!%|3w;CP^*Th>Z*^D~a>$sm=@%+DmE8UK_0U8 zM#4{06g=8B5r3w%)odjb?0!7_OSteHo`(s>l> zO08Rf>wj2|{@dB@6EWuigSJ;Fx z5nGUh-a#JPhjA#JI8KEV$FVR6y^p*_>u27Np*AJ%Io|-i@aE)~MGD*WD1Z30hkWeT zwzI!+d1R!e*S4T+6Wb-kTm!F|w5#GCH;V-|r6IPFe-2)^v%R#ow0%c zw?Q&Ckoj&t#uA;yc&o)e_g*rK<4V^HUV2abyzvRyD5diccn(WhsFB#^ z*-?@@w5;NBUlkiVO1n)BbL?PR3Id+WF=XQHDSFZDZum_IErmqfKcQM9l8Ba#(f*tj^OZzEe zJ4Lp{&>E4_yN33nl+H+xBc<$ilz%D%P}}yyVWN2_u_r= zJjb?*F8|rH$cb+x+L^I-x)LSB1}Pi(HmGH32g!XM5DRfi&svU!p|sZi67BModwP=C zq&AuOoMTDyxICdHec97rEWxqQ{z}YaD zK8t6D^-bEw@z^gLS|jqDUYFWgG2|rP**16@H~RcA$#SF=W4~!R_K?ySb{=Yy%*nL% znqlBqO`h<;uU#zOxJ`d^*D}h|Qiyvk3d^B0kol|@H=p8pIfs#j7wkNo9nYZ++E_{L znYeto%=5P3>1+=FyuN1I`W*DBS%$dW^F)+0^f1TwOf{65AEC@V4nNU1&^P<4rG*sF zy11cj7|;4qJOg}BzbP#hT2it^*@$;jK9ne>ENz0h#Zj@&rj)?a&YV&co7}%QXWBz? zDRUeeBCc&^Ii82o97_u<))0!Bfwi7s%!0+u(ZA7 zwlB(P14_@>ojyG#TaFwfdriF9F~yq8`-YZ7L$OpOtR}J?>Glfpeeh0VE150i;7xN5 z`6U6~46KbiFVMl;zB%3!d{6N0(7}6v?qWHvfp3a#7$-qr(DVcyv}gFn;M*d2Q@FPT z-wu2)@cx9}12Zu$iEZOK82`kU(?X21T8yz-OE7M0DaLdy!+5V1s9o?)!MDUZj9=rM zVl&3c@oljcEz@iu z`ry;rJ;+16H|~kKj+}B6wf%AaZ17b#QX9*Wc$C&y zjuoba5yH@}Q95R6uT1F;839RYhwb3cW3LUoG<|M4{)TJ&+Qw1tDyVVd=t$DH>=GD7 zr-1aW_Z`hz+OK$iLaD+Lx4eQ>(o*9%KyLY}opIW|8|LAjJ%7-hS3dhEu9xe3hy^*N zed0L@qZs0}IMm<-btqSVT7W#f5ITTGNTtPwmbSFQrG+kQ)~;^Ne9eh=!L%8sJuz*K zX@{)!L9}nCPAJd|G3PVyv!2D8*1PiL@#*7}@DrW|mdq^c*&?<_^k0syVrB} zRyb%6;0OY?E!etX`+_YDwlUbsU^|18t0x3C1-3hOp&rS$2U{Q9_wCOH zN0D%Z2}hrB;^-8PT;Zq|rW@NPoT!sy8-=ZuyR}oOmvU_t*Ir?ZB{T(JLO6%E3;o_+ zLOacQx!2DheO>34iTYgnrl&TyQdpC^0Aas&*eT_yoqGCZzJ2U>_nV(B5&zk}zCqsSOHg{6N5N}t+u37yo> zw)FLf;WE0BYrET)amRk;s`JO(3T^o}(WZI@Pa$iCH6L57lc^QgHQ_pyrS@moQ>FyyWvrOJm4Yxm&7kQTu$|>T-U+}eT9k>O7VRR5Bd#7 z3BIr4K_8+;g70T|(4Xj?;A~2eo}h1We1g|AJm_c4O7QxI2Yrq!6Z`9ekF4L=}ctznmaDJoC2QfUBL%_p1ZV$xxccB~(wD94)wwDu}Vj`Y!Ufa3^FE%`! z*Yslmv&& zxBXs)`6KR5oc77(e;KE}VLkf@CwU?53G4TlxN<09E3Eu`QQ~^odcZuy2b&G5h`g>3F3RGm1(5j$Mqqk z4Bu-2Jiw`!%h%6Uj=lzNcYJ!UAvf6} z3wx2O`teB%r}0N&`Z$eH^7S?PUA|s+=9S)4?v)yiCQ9eNj4n-M_*PD1RS2)Ouzb=S z+VZ!h1pfDxfLfsodWK=zra$itbikfv@i2}q(Smie@Vlam#xCAmVk)xvR_bZ|wSfQ6 zIX>sp69QgNJw$z4^X1kF9_Ws`PV?(V#(e#I;FCrado&@<0RpCGyFEF z_=v~wdl_Ea)#mxq01VajoMPZ6#jt_$uz zdzOdScP@$H%VIr{H%Q}F*oO)GB<`O*Q+g*J&S>8kyTY}|4+jqY9;WoRjL&m_Z+&-Hh_#WbX$daJ!O&!S9kXEbKeLL&OuqzP4OUvGNJ&e1&o;HJPY8fCf*)HK|umEVYJQ~c{)dG@aM z#Ll5?94@BDB~?(9c6|^#8=9NPy9wBkISc#U_tH)eVVA2C#I@o%(rx%bRFzKquFcHb zgU}NQ_h;6yuae4q=PW-@wuO6rMR>E9t;E%LhhvlF0^ZOLw`SOGSi+o$ZSsZga<`nu zRsrmb$TyNj#+v>P(yp%$bZ;_9=(%$76&#Rl{Vwp0)cRGPdy$?Oo$NfSPqwo)g67cy z)Qc|VZ6N!vGu?dnS7Em>U&VLy{p{+G%|7vlbm{Dxll)7uG)K6TIC@GsxYPIokGe-I zhwo0eeY}d(Lp^q|QB3J=;UuM^bxCb#p~38N4tFE42y_hm>9OQ`0{Nbab@us`hc1<) zI1J0#8$I}okmpwJSbVBEjSQ-z+xWh;*Di`RR|H;*9N)p)s;@3^zQ$VGr}^2w=9eL! zu{L(7l!^$q-nF6)2F|2sH?t!AxvGA*l2)PCx|;eC^|1hpOzX*q-VJ4^eF~LB9XL>D z)9%Jb{^WAkYE7_n^Cfy+Ke_E`_Zn#wOAR=jf5uPcSbA$i8})OCS?vsD59MS^<8*Wx zr@Cpe-F3l+V#r5r(yH>j&>eJ=275);d(}gkJLn+G}@T=@p>MHZu zR?d&Q#^gY$Dq!<7x45;;Mq}l&rj?_{a^*ABH;WZ1a%9)V5yr3n@_Q>$nca z&78)L@eGy(I!{^rz!tnMc>=ZP3$30OxoJF8SuJoU#&S?8zeoy037+^#@GW=S$Ch!d zJMnKiVOBk6C=xJ7W9exrW%sc4uwDF;I6fU?vx8>YJ>WBWYcEfcLe)o2IK`6iYsZ*$ zgzX)xJjDG~+p1R5u1;q$7+OOOw}N*y2dnkg=G(okJ@luaYD5VwS>=9$FN(GGtJ?)L z>|xY+**Ig8TYJm3e=6i@y3qp~Q|h|ut_RjQ)te8Y%_!B=*}V(<>9P!_RM&+os1|V_ zVTbtJC@+=KLq=_=7WTnfWj}vPEW1VSZhC=Y`kIlaz{pe0_hScFZDbGHx5~LX-%`$7 zVK1u-Im+9~MQ#~mhhiV-I>+>Mq^BN2%Npb#k2R%n@bMKPTyH-sj(WX~nh7nZKl?#M zvgL0}3H)1?fHlg^==nhZ|1_N^MS-4Izm)BcqxrvpK9EIR)P$sI*X4MmS2a^0ADCfd38=PKM3llN8}wG!TYBmt8u>37Mj5qGwbJ)4zEB<2KlD<-5RvFKSp!= zQ`a)4<03HCo9eW_8_ZWV!kx)1b0uq{zwtzKQgxmUv{Xtu_CvRJPDuGQM%0z=M)IMz z4rCluTK>jw#W%urj`2-)Gobe#Ejb&!zRM+CR=TyvhcU2+YqBDg&Bg3tNY_?lt8%Nn zok!l3lXu)5JW0MVLF5hVPzFIZbUB=D2F`i`rOC%w?v#k8;o)qSVy4Jf1>ow1=b`Yl7v$ zPOdBSmcryEA4>YolH9#{SNO_&@=*8V;BP}7UW%o4kkPWrStH`p2`Igd=2TBQjXJ%U zIdBbmdLV{x?kWai+Pt8s`gSXmhJ@Fms;4pgwVNl@6eL=e=Ve-gUh8TI~i3d`a)&Zh_v;F zzZF{OGRkabQm@6NLr+Fg!>_oHV!k@L+I&&1ZUSGM&wTwF-+G5uE6Gabx3QZ!Cd|#D zFWB3$4lf|x^?6?@m}q%gNK4jU_#2j=58XJPsU7tc~Bop)VogwsjP7>OBudn+yu9rHx*@uP_RfNE0Ofb55F>T^CO6y%(#WX0g)6I`Pg)ODtFH-{v+5+X65B#=| zlkjwA{sv@2$(P|VJWC_~_T+;8NWwQYygM>05s%^97#{d`4<_L|8D7V?BD~=-e0ReG zKkv#Uyz-;nan@GO@ECrG;eq~|lY}2-_;A*_)T8hieyrh}(_cBiP<+4sIMG%)A2Iby zpE3_q0lH28eHh6qU*%m5oWl0Q32Vpio;Zcm?_o!JcNc!eyY?9GPUKlLqzk5TPvcGf z>9C6ZT@$~TrYn7x$Nd7~bnc0C8`!J zvTB$KD)^UqPLpwC{L=jc?e`!O=TCUGImNdJ=l<03f2U_D)CX?RTj9;)-Jucd>eNp) z^G>#v`3)s}#!Y^4jD3|CFHv-fT2^&>hjwmts$UZ~oNEa!cY`mNwaW%%6G$7K3OT96 zeUms(wRMsj)EILSa&cFg6wh)uA*N-b>z{@69qpxOnbViW%)Z0jsl@#)oQ`)%uV((c zA|)qDpk>gBq9m32UT}JE^g}Y~>1ed6YZ7b?kpXRtJWUJV64}Z}qb$BO{$BL2QP%JJ zt70nZ`FH;jVjAU6fQNLN^(&QOHA{y^OZ}>z)yukO<*6a%=M)$81HSfMGf|H!kON?DrERO2NpD1Sb?f~5q^7T16tK%E`?r=UNk9r}ctg;jRD-LQg{xR?${e1Ku5aO5edqQef4Z6=x!KpQe(*DN9q!LGZ3oG(K16=s^k4WdeTA%IwpX@q z)-mgpb;*v%24$Px2uCp~iT-X zy^oeuDXWp~ok=fzXr?UlMVV$QrFu`Dm6**Y4+`JFzZkiq)#;H-D0N9$ zZ6f~#-+*#am?8cPTDN>{on#dC-6rtnhWVF#)sosd8i{Dt&OED~`*#ZTaSf;TIhp#o zKN(}5^qQ|#o^$D)*T#A~*-c*?zSGq07e|=JM!70KeQ3$sAPs%9U+%Zf>Sm&26~j%G z=hjTQO4SkB;A{xj-LW)|%yQme5BJyD0((12BdR2hrU#ztM#nE5=jsrPTHikOs^+x% z=lrXFbWGc6)M6;%j&AfG!S|ER@_M6&Q$JKMMDv39v<^5gu2iH6u!yzmRjgRHqi%NO zSzh1YI?2<9vJ9F&FChoNf3|BZoyKrg-^8zWzLfqe`NUQ3KI%?6muAp+^d_sL>@50& ze4CuKaP@{-)@bUpj$aq^_=xRi^oh^ZLXT#IIE4`V+DOvNTDwl19}?vSM_H~W(*wl0 zo@rW6K_2`nTKf&O=Vkt3_PDRixEf}md^F8xh_WN8^gUYep8D`yOF@`cE-{rYnoRl-Q|1G^;}UA&jPwC34u*){CWn5q%=a7t6HW3uT3%(S`k8k6e3(ck1X z8q{S}6|Jh27rT7t<`Md0t97BaANIAgGvIt_G?O-)Bq={54V%zW*@myFB~{_<{2O9> znMbR;oK|9VxB{%Og*4LpQgi-Q7|Dd%V zwVCSKp0tmF+3mcjy8O^a<{pgi>H*5}`Eji8W@`hXOBlPflGohXl>FaAX?;fRY(pzA zW$)>#l9qfEXv=~Sg+aJH23T<=w-2Z$X#uv~G2qi(whmUtNJ+mnA3$!~eS-WvL9@m*W8y?oZ)d`== z9n%T<3+vT<&2z+!oY#p<`TsXu%M5!v>q^{#IECrNUGMzfv!d&~tuG%(n)__RR_?{S zeC?e51BX{q1-gdW-u?o15!C0j2iVm(X&P4=Ksn{yvrg_a=o#g_A#+8rZr;XSd`TFW zhQn=C&h%Dw);Z#GD!v+UPy0L3aiVByFw!^SJom0(N3e6NeQeF!!`SIQmhoe(leUE3 zleFb9r!uFsNJo8GA@UvW`bS?wH|vi9YvXQ@z80=NX;8T8t}{N3$ogh8xRY(kS%239 zG|(@_#b2?%`EOt= zIHhBldwz$IqLy%GgKwm}>f%sR!-!2QGJU-*{L^DyS8^OmzM1h?6~A^$RBB^(%=1>2 zGEjL|CwIDv$%QVhg7o%4ax#e;RSKH9AJ&GEx0(B3W=^9v0Pj!jfAx8IL-lvYtnh8g zA73#iyxrRAY$(NZgzFybS}7gNiAO?*x-&K_g|Y4i&im7omZ4|htHGBJi{+>G(2RYC z{*=uB-aq3ZZPxz--)rtOq-!(0h@a?ZUQ4cGe^#3%5L6w#J;4M2Z!cDi&b3c)Q81!G zWEZDNf{O;Bd{`Y3TY^J2@V7^{2lx&N9>)u41Z1SIDNT>&W@hKC8u(ulyq4j+XS;(x zo#1s1-z}3}pL-L0Kf|kLRl#pb@cM@Dn(YdHd4g*^VNI9GZqS?rZ)*6?+0Nh-6TFS# zRkAAJqY}KG;X7qJf%i!8j)qsxDucI9@Gge$knI4zcY+^m`1Zzs-8R8{8oph&9pQh5 zzm(TwU&FVBLgzg41Rr2{C1`i_Y7%^q;oD@gxAby?A7yw&C?3u+N$^pIm(R+B-=5&d z8D1_c2Yy9@pJ;d?D}c{U@KX)X_+wWy!B01wE)RZUf=@C0SN|(n9iHGb4FARd0)BXc z&oTUG|1)@x1fOU4PyQ$H&I!K2@E`q;;B69ok>P*$e+O@z;D0py2mb?j{RF?(@b8T; zUMInCGW~_~{AG^vK@^ zC}&O+O7M3KU+>q0_fK#JbpF=)b>QMH=lNZ4_^19;p#2m4OT&Xb;k^?4JHvxL;jI(= zXT#T;J>l<}NAmdDdgU+J4PKkz8A0He`VY9jk>C{!4|aoBCHOXm2fM+G6MTEagWcdO z6C9yM{({|LQ7UfJ@K@owuog}5W`@7wU*Z0Gg10g} z*dJb%;0GE065I&JnFQ}(c(6ZwRf2ahJlG!=6_uw45flD`{o!#5-qY}4f4F~w_c1)! z8E&88u=M%U*)33u34Vm(!On1v1V75~U}t#S1RrU5uqXWO75s?v?-;{_J>gYQA~~-8 zCWpT8W89xl!jCt61?L1Hub?VG-QISNOV5L zJ=}jpmnu)EiWb=pvnRYZ!LK$v*b{y|!LKts*b{y%!6D}OTk4l`zb(Q4WO%SMe074~ zVR*1JJTJlTHayr9o|NDZ8orowA2=;5!5=pKHh&xUJ_-J);lZA8%LHF(_${{2SwF#_ zFg(~9u94u+8XoKn?~vf?SJHyLfqRzVuNxlh46jSt;cCOLgof}&K047?cB%&7aarr>GuNfncx)+-`VdD-a5g9 zy~>^7;5k<>!9}0Jnd1yVsO1FT(eNEP9gwF9E;4?z~4=9(b@2%;3+!yQi6+)r)P2I52H$g*D`z?B+%(i3BI@CTXS{`9=@LSpGO7JFzm-FSo2PSxP!wYa|(e6(0 zR)%Aj1iWQ}w>8}J2c4GS?G1Ofu+uradAsdo_^-$ovx}JET@C*Q?lAQBrKRD!8~!t= ziLhIf;Jpn0363)3c7pda{6{RTFq$R!;fDX6JxqFKf*)!45A1ufcbedmTX44jhaJcS zA8xo#IAL!x!ABXc^OK;T6MT%}Un5P;-hP4~XZTl0pF<}n_&CGAgkQ}%Ji$*gd=tE9 z>;xwGDTaT6E(vsRf}du%&T^wwC-@}8KZgs7on9t`yk5>Ud;^wj*d0mmX@;*ymx2={ z6MTl@pTRSRPEPPyhOaX@*XI)a9K&@w1LsF3_&mcuLCTGt=LDZ`xXz6z;Xi~YjvQKo|H1GN+y~&tC-{|yi`$OeZGvB8xXydw6t4uo z-tb`mR(y}VJZ~~w@_Y1)1i#hrVEc(8x_P=c>8T<3Oha$AD0GF%#6oMx2Z zPZ=KU+|Et#=L`?_Y)?<{7Yz^gY<1RpUOulHuG2)IixT_|!-GBBE(!j&;lZA5!vz1U z;m^2dh-dEvf6wq>=XTcw|IqMY=eAOUe`0vBXX_Juz2Q0)1-kv>()52}_$v0qoLisZ zUl|_k+{zwAo?qFSWCr0vW+lPDH++R#0lqxJe>6PUxxFjFWk*q`*=qfITY?8cwP5Gg zc#Dy)C@03ENkX9Sn1q*oLY)-#Apg%waM=mexwKk0pPb;b?c*B6!G6@_obNjA6Tc%btm9>SjiG1b1IG!3)L zve&njodZR_5?uEBwz6|q17AE3FMEAk*;!HO$|T`suWu{P?YV{TzLemy*SD3ODaAZh zn$8Nc*S8h&wwDopbtzs^_%`f4UX$Z7{Q(c*KZ^0cP5NcuZyP%=)VUuLT=xC8Vdq@@ zl5HTqm41aUGCbIW-7CRs8h#afp2%A#crC+&UD-`qJjC*^ZFoo5k?@@f53chfwidoU z9BAPm#qevevs%aS4z2_E+X=q6;q6^}@D~!iuHnHh>!S(2kKy55j(Z}`cPq&5-}dY= zv?87XNqU4=g$pfwaSV^8wryk&uqvG8mf$xexa$MgLc5WOz6~q(doQQFtvk9f=~Scj2*J1w4cw6YKpe+DT&zAM6v(N^sdHtc5nj zOyapd!J8UB3+)U>rvz_i_-ynd&?isu=7!I4#xJ}+v`>9kb`EPHH+>G_>m+zf!zBR@ zpDMvy8LpE$k+n(i)`kbWe`h4P>?PJR8Gp_ZN$|FY2fKpJO3SUnL57EOTC{VNmvcMA z>$!S-w`M6`QFv4KhmOqgSk3_t;g`qnI-gZ;Z{dS|+UFCzgW)kyGj4A2x zoPHmc__a5zedBX+6PqCFTyuPV0`kg;tlRT4O_Nf$3{ydEK)Wx)I ze=6^Dm#r&>N%5x!VCRZw`8(5Oai+KOH(w@her(I=JHZPL*$!(ne8Grtb zH}Okm=+gw#xoJ6$AaP099RIDb!X!JS?=D`FU@7n6g-!-uk@L2A5;9Cps?q zJrDooxSMhJB)EQmf%~}(uHVA9fqu&4{I6ooc(*GqFXvhKm8Y~WE(N<4w+tt{pnCo+ z{`?))jqvoH-@~?oW>I?6chfeh_o@-59xg2d(Qpcv=d)Lt-)d)hy~~E^b2#N~1mVWv zrr`2-U%>x9PR~C}@V%I~8{x|0zZbqNp697-o6qsWpkQ;i4d2MIG~Fs!wXHlKW#h@) zpyJ78;!Y&4S-8AC>YcKF*`rRsm;3X2{b%)&xA**=G);H&-IYmR(y(j5e~Rg2Kw=2lxow2{?Uc4)^>!@~cf~3|jO%&sE1Od8Y5DeOm9OZ6^Pn=S9C?=DiPN zy!p7YJ#G?|J{p>+ETsrS-4; ztp@uT_Z_ant-t+S>6${`58!#8-aKvkuAX=0newZAs0}Pie&2&%zm<+BxxbbCR(Q?p z`MdwjuW#q!|GWJE%6HQ+%0r%p)Srg=AMxjHO!cGp)8Bdh>AQOVTfg<}P>B6?v7G-` zq%n{4ezr?9{gZUHlahM#$~ErvA#{yWxgDiq1FbRh<{{TXSAR z?iydzALJL_8mF-NZ^fDVm0yL+zm@wHM&D8&PQSI5%}aeN|Mq|OI~~8v`t|KDd?TmP z<(J)DwMWHY_L;(|9jCT%%l=lJ>2v-5EnB($_?DifZVcGz5kHl5$eqh?%`X>oue5?) ze%ubY%D5ZA{uO(>_ry3qUsn1(`P1KNI^=&bz9WC}Y1kNNleVl|Lzu5)IF;8wb6fps zb6dUA(^mMZ;5Bji_jCKa`ET7*o4bWaXfKT2!WBmzPaeLQxU>N`X0~_N;Frx@*~`5- z;;D^X6U2#O6{hpz(s%WHPMP0Nmihf(wW)jUIl-Q8YwB)!;#VD(zdhJYuEH~&vju(tt|{hD`8|L+(7tIv?*Etln1ZYyR(;Y5@t)ERE9 zo!2-4TWgX+QEc1#o3-zVPrIv<-4WWs*F84qCVYvk>eqe;Vh}#Sc9rG(i<6v^d^dPW zipy6$lODi6__Sje>_7xd5D&6rxr*J)VC6w}dSuUS7Uh&%caeP_*>GCO_H?*{g`Uf? z(j&=l#lK4?Jp%bw`w^sCzUOTpUA}jnG?0~(KVlO`yVFA7aJlW;r;u8$f@FAZyLO|0 z?Z^=3Y?oWHJ<&Z37ilZr%fJ4Zmz=R;`n#VUOJPosnE@;BTl?8Zh4MSvtu*`2O2M(5 zKp}ZNz4wrpgtUCWdRenAhwmO=5t;nGGVSf^$ttJaYx!Ow*OZe@o}6 z-i_GI*8Dn*e1&r^x8-DvHe4nX!jp~2NJ%bQv3=_$At&E$UNU|1-R&ivE8hoZ2UouP zOKjgxwLLq1p`DWrH0i}vX`fH6 z`XJZI_u(4MC7BR~`^2P^<@?ku<;r)L*N&TdOHn?O66PVbIsV(N={)v#r+ujcMp2K zWJnb91Cx@J@6TTHq-r~tAw?<~TcIy`NiWOyUWx78OT2cLo&u{EPl42#t1fTwB zdU-g5Aho)ZTX{@-p{%LsjIWR2A%;`UR37Uo5oz0gVfv{mo4=VJ=nu#|{fL~@&&XE& zs)5UAzJT0U(7>&TR*;fuIrNkwP;Hb5JC zj6cp!kR9(QFpf#8F<9Y}b)Xx~2G>#~StS)KiCIb0uAq!0g}Vw#-`B8f_Bs;dvR?JE zm#n$u(AOi=z5#jn&yj`K^#wBY(hk*Vmg`A>i=4kM=>dF?j({%d5a^PQfv%s>P59YM z^FsR6+o3_SGukV=qw!K5Etzmhk7mzI@;R!hznTP_e9lYuQ@)#ht||6{mmY$!@4YlL zV@BbtX41LcR&QD<*YW$KZQICBmr!3A>}AZ;`R%lX10UXH)o?|d>*sNMP57TLYi=j-Fjr;zP7yz zgAVSD?Ip7$-|Oy4wi=a^ORx{8opp^cjle%)wD{6B_L6GY4y+?Rr-ARl z)yu9yoJ~52*7~#G6wB*MlYmpK>z!n(<-5PcauM({WyP@AOO9N=M}4kox7xRe_3?@SA^N^H`@oe6cMPcy=VHIjti9UKEDJWr zB%k&^w%oMCuRI^%raOwyV(;ZTMBhZmq-@yZCOf8YzirZt@_lRasVb#ytiMC^zz;3r3)h8+g@5H@}X-I)4NT!N9-xvWx4&~U}sB`LCXDQ zND38YCV$4Sr|B<}ZB;Bk*)ExUGraWFGiBU$r8opPo+es#!CG0_y50h_&NqWln zxs&X-e1p8SO5}UZYajc3eEYMDxq{s2ga*-dFP3R%;nYmcv-!-?fRX-K&ii$iWlNoiA?uc_(ap&% ze170RwAn#3!#bN8G%ILE2=jr?UC=C`(Lap%(o{1oHZ@;P98^#3ZOGpI3S z7jzS=#u-5rT~Ssw*7Hp!!KgBOImou+Tj%C_$w><>cxi^o_f?6t;7w*Nc=ORr_J38| zi)p71c}ZjHoi|NBSiY}K(@wr$ymUF`+atTSMmUvWJnt%!r#bC%2{9g%3b_JXthkgu-KHM*bk(l!+K zqdy?_kt3WmxrF`2-%<1*M{LReXfCdAHXWbAYF(+_I?-5?+fr#YW)%Ju3QO~m$z+3V z!v3c8C=`1yuN`mI{j8FiOuB%2w#jQ(QobsgECWjiyP;9$$JvTbXYWsh@1l{XCmEH7 z!+rk#>(GKz@m3;p`r5_`iZJDn>Na+6yT{?X@}<#SC^PbJu#`7pG> zjn)PwS*d(nhD7C&)QUkf zrt@Uvo8WGU-#$*?X6>#tX*82`k^K83RU}CT`7cFoQ`!!C^GDNHlW&EW2Aq7WymWEp z`^ih2S-$Nu=|{^~In#LpY5{{{{qIm>`?w#pS+HSR$+xujDJg5c^_=OSsN5?V%d0fnA>Ot8m)RkRrjUV20>fvyr$v4 z-PDZ>KG7o-3VSR*nlH_sAq)svNY0LX$3%zF*l(D^Mwft`!=4 zX`t!d8@+TQ<$JH+{*OYG^U_$7uacMUs(jd%Cal_d zd)E#71_LN7*>N7>-!2z&yObHYX3Tj#Bp<}&49&Gfs^!oJe+xpiBC0oD3 z+LkCM(N3j&F@5p(Y?!WQ9Fe|Q;zKQeoKSH008Y`v6!pUU+GZa|P$1MLZ(p_B+4K3^mTG$ix!tJSrhLk{aJ5^Hc zsp zhkmSaVlmn>H`3$uwTgawR#`d`K%u|zI>SJ|@62+LLY#`kRShruy&As`X8zEXQ{ulm z(@x>ZQ;ZHeiTytO(qod2lk}gY8zsFd=~PMIO1fBKj@B$KE|K^|;uMKjByN$eFk|O) zcIm7wE|U02`3Y`H$>wcUI(w$iM@IA~9YN_2O1Ds4CSBr6>b$oK=rjH{$4OkLz;_bw zDR7?x2P&-Ow3-ubq-$rttJ!5KV!bHK$@_Bc$CdVvz@HAM@SSHibsSm98$tI|`hmQm&D>MbdszjXe*k0B~$rY8!v75(emIG3+1u>?HDvM`5y7N#8mB?-SU~U z{AeX|%T{52?jCuLOJg5A-~#)?eCxbU-jQ#wOuAF5fk)^? zYL$T=-U<3t+Jg%7p_>!u#?@XY)d|0`^q^G-q?}iZhS;3>tDv7>P6TW?Cx9ZLi zM^~qEQP@UiH%F;$%{hL#m7F8p?Zl;YH^ufwPrfqP&cjnojkubXSSV>;VxeTP8)6np z!q@cP+T+95y13=jLy5E^ueptqwsuyYzT5?xEw@+Fqr_f`(j&_^AxDd5gcw>9!=jkB zn(zT+sd$K;t*6yYBeWjdk|bH_iApg8jT>%r>23KGeNgEu^xK z%kAQHaXQ^JhHtML|)rUXYv}D97wh>38pllbkrw#Xq$E9&c9Fgj@ zQt8}|#fsz$PaVJGmz@(N$w}Or1G6$XOpX^9m_t3G_NL= zj4ubmlgY=Ib~c*mtHGv%bUPc+FV!SKDpGlj317rOqPf^TBUxYELnjyXQ7 zWQ0+U=g0STO1aMCR9r3C)z57lj4}JNXSsY#(L1VNl~^@KkkL-uV7;{fu3q_A(x;V- z4=vnxjH3!&ozz}#KMuM- z8!^;Qk0q>1a1wPfwPd{L>2^b!MBnOe_O;}@+;xbhFy3uLqJ*7Pasu~BJl6@_LMN8Y z6;sSEsd6AKxT!Wvh-OzC7)ze=d7`CL|Ixekt;d8st99rs?C{31{954JP)Z@bspL+l zU5kQ0)>6N~PLNk^{J~}jm8?qc=LBWPnMv7#G zqq&XvD#g*H`~>RY6dUcU*f$#Rv^{0jlTrCFzAme5YU9&(4{=}Waw`ER zxIN*kDc6f|TmH6`z?KsDcP|0;R`(n8|3{4L;5Pqjyh78SnWl6;6@9-BT3+LGZey-~ z39Y*yq3hY0qxko;kwv3iJw~4cY?ckUz7l*q-14`j1h$mG|Ia0$-sgU!|3B80C!66x zFC6ihl5s&jniJr%qajQ-+)|2%^+HQ5yCD0U;FT==GWfhm6(sl$hD$C4>D>em>xG3L z=B^u@;1Egt4fb=uk5BL-!@D8@hn({){>AaDj^WR^IzYE3_`Zf;z>XO9^AcRV2(5`{ z@V_1LXE7cfOTxY*d&v=x>z5{m2R=yMBz$wjfARGx2g76dR)!amJh-+>f56MNHayr7 z+&#hDB>1yG@gs(Bo8T3a?{+l&V>^%8@c7+sh6g)>-!by$`RZZ#bJ*`;&qjC*kMJRX zVI3!(iroI6csdmxaBc+auTyau{tLMayNr7Qos0B$`uv+yyjxNx%(MJ{4fpOp!JAG9 z-|xWv7s4q{rDxiB^c871WuGgJdVVh7>(2ACztjAre#@Kkhs)EgcpitBbprAH&tTIj z&0`|fJj^sSXc$n}*O#nH2e?n0kyg;r$fO^Nb+(@D#~tf;Y!T+kzOEsSPj8L!XLz*{ z&CPqlyE%*NFY$M|Y)9n4_Rm(bs#4gc?CPCeJ%l|D%gOg5({uJ7;aN}C7T5Dl_2owF zXq@IhjNdxM`j+sJ67_I>tz{;wO~W(Uc~H-lO`jqso|>#n9Lx&g8T!a`>~R0$`(-=R zS!q_Tb@sRCg6~9ITZ(R&OqyRoQtopsUr7>HE1%}9HzYwWE6I|kc%WqU*#vwf>%gfo zeVwgODMdwm^$_IEwBl5}ha!bE&F?@dbjrr;7-$rw@;R(jeT=N#81kz$U0^Fr<)J0} z`LbzoG4_S8NB+JUyT`Bk0()hLW3}fb%3O7F6msDwVaZ85*0cQsF_uoQMNH9hKZ%fv zdA!p&DPOAdfBPd^&4sab} znIZN1d?>w>-3PIS^nkZGhSk{RoZEJ{>(DBE?+>PTqFOtg*hggZSi|bqAkKr^A=Bs~ zE`wUkG;1>p`L@~!G9+1Ut)w|+zg3CVxfhUBkj$ydsV}zJuBHV|_EW4BX8Ajii>yYD zhLDrP)`z$bqvrmcw7tgcBP$I%ntY;0T9r&Avkfa>8=EEMTjnLjpcLj`I4JvABOAyr z$hmjfi(Pd5s?C~kXgX8x;<65*zsUB;cx;l{ z$oq-0{I7Q3vxlg>jB}EDRGTh>i*m4=M1OG9e!r}LmUEm&8jneFbYN9o6!{J7eGx56 zB{sxzwIa5!o;Euw?u91xsP{&?+CI04cN)DVw}{uoH#J!UNd&0%HFC1A-jz%3MjWFt z*4rogtz(-#%^wndv;1-RR0~Hj_DrFzFZ8#?XU%;->O;M(1za#$nH&#)8sV_cPIJ5v4j_v zSga4VBT7P}z#-J+>wbgJxnLSsaz57tH;XST&KHr#m=$BX)4jmE`mSsh@1KePrP4jg z&2Jvs=pEkXL$KuGgeh)Z!BCIXBNd)bh^;6 zKi#qQ_d28KI?8#Y7cHeQ7nE?{no$bjI++nfw*2&bfIB#jS(+u9+WeqVMK+`LyseWJ zKc(VTY&s6gzKU`uTV;3E2qD&c(Q^v>ObJ)6efDFl)2?o!zk)hd47XZo7pTcf{2FTV zs3%ayCvi!ZVmf1Y&ZV2=K8k&HU?v-OYDGN=JKcBA#HZ7U*Nj?i%-Av554Evk84}5# z`7GOt@*a?VPARD*)oMSFc}!>SvHsH7TaKcxC2u~NQAayD>KE$s8g~bp9XjQ%na??P zP2APIsZf)weQ3^+{kb|Io6oo%=rx-EdeSNnqZTh9)<4h^ud{M)#<=z>qnfN|{NgLn zdiwD_S#TYeiG!!q_a?N_iTL+G`j*?4zbz%Or3C&TC;{v2o2~Q1{(t+SJlPBn>;L(z zr#8oTbqtlRdf*G_9Jwe*P?a-b8PV@98-6zjsqcQSSr*^jF&w#f<{6&sJ1#4H#lZgJ zxuv*K)9%W`67V0B@Y=gTLb;%G_%4`T8eZvOWl+$0w|$>4#UbnXdnEfigWw=B6N&=A zv!>x!Wjc?%TY~Rn_>^n`c(VkrXZXNO=h@dy@Bx{{ej4BwvRZJ`_Dxv_zicU(8oe!*@b@9Np*y59|Eb zkeNeUJ;4W9_-ovg+|N(&L55Fcm4K{&f)6#k0d#m+Kfm6Cb9Zn$w@UotmmlIhsO7$7 zPj>M#?;gOVvLae*w|&sN-ZK;hM83rw~5~hR~LUCzxJQSFUZT` z6Mm<>28BC<@TbrB?)doLf93Z_yr=S-%e&&m|3JAvMJE!~Fxx$Z+-yVtQl=em(d!qY z7gvBc_I-A4;i+Q+H8sdB;LH%38#+Ckd>@30X##G~LN{8IP_1*$iT>vcFBe{}9cb;1 zXsER+oKRRWlv8O4*ML>oM9%$w4IQY-=+Av+`dqtU<$X{#ExRV$y3n?8P2rJ3pQ8hn zHWI1EDuv0_1O0IqGN3IB;fuGr>ZZ*gTI`8zb@o@Tj^vH5i>`;#(~&sz^hWIc-i74p z*<(VgJG%|Hh9}z;_AJya)GBN^GQ_#8@I_$*?Np_(tI5_%vcHX=P&Lq}eOwbtO>s_x zetd@%G+~{rl4#>5>>T2llFiN3*0qP)z=?`nf+pFnS#dTrJ3G4}+p*BCaBX32L3#=r zpEe~r13`(rO8f zd=UGwz1WREf;Gobccz=+&Zd^n$4>Q4NMqklX*}d4#s4;1Y-^}{hurdR=r`@N7QQ8l3ldw0+Uemp;I2tX-sY|BdO<|3cETs52?+kk!zjKY$u-L``+% z>Xsc$`yHAcMXepfH7+}W)_it0leRE7o1a}k%f2DIF}saxNp>f#e8)oNLY2aD_F2{@mT)`wM11>7V($`raE*dECE{5;l08dpzhV98 z(`;iV&W2X%9kMamIoYjQyTZ`I*uoPG{WfaEntrA51pGmTYft(QD~u}4=9*s+Cqy;; zMRp>RD{8xMXYXY1XOaNWuW9t2r?W3&+xRrQKS|s7a8UK`QSSaY`W))oKNaG8r7(SM z;FU~)zjnKC$hvh#c73*8p<`jc3xej6TEaAI1GiH5?=aF-H_nIBJ0)iXK2rA(XSLiw8(I<;|KNqwx1J*7=S^ReQ+#@$w^vu9`#?F!=y6ANb+ zE-0)ZC(jn%pcQ>t82xYZAf~9c4kW(@7q&TUC{&UP7YQw0=`b#KPC0+ zvRwY?=}afOsNbyO=^NSG%s|TH``KBs?B357tqS?Osc;q}wp!mIl;fWmiE8+>XhA|s z=h}Ak7}eiFu5;nnF`-Y*gv+DTg&ts3s#eHPb9v0h<&Wvx73m9V8xv`{x~8LndzoKG z%heoQ@#j#|-(-EE1r@hu;)|fIRO;gjPt#(`;af+EsODZ_WLK!AZf}3atk5Tx7v87M zi+0vKtqOf(o__YPl)NpCnvF&;>+MIfT7@4+hVbh$QKx$IX5Z=l;5(tvsmvTIStjM; zvFz2PrF=$P`6AntjpKdAd%rute;((4jr{fF?6btxr{${uhFM=c6Rk6vTT5yO?_>(I zfo6cu+|gx5j828lg=vhfl8sdiZE0U0XP2H8zNiuA7y6gJKDuyzL0pv%_!|(nB>BQz z8)l*bU4L zk7Q3%yB}xe3)?YA)GD-MTvIB$F{)n4+z@63@ne+U7R>a|We5FT96C5!_Py5`jsEK2 zCLOM zL9wT-%9h4f^{qdd5m8T{Df}U(Xj3+5L`cINdUg}9b&9^X6S=Jw>B3%al&>{9gqlHb z-_vXLqj)CzN>DBezfYlep%$^L#Sf#7#A7>eewh2(6^<&5pl_U5IJGdTFtc#pQQ@6# zZbL!4SK7C20EICTuGuwQ*E4#*##si^$Jzyc-$X81N4%BV-bl&I0`t!7;MAks2XY-r zJx`(Buj0CvI({TuK@FELY)k!K$#r#M8P}uq+UE~WC0V%*UzR-em+X_Q_PZ3G zEWA*7yRfdHU9ieLn*_Zxn_2BTD4mCx+ukreIBBx&40Y4cALRd0E0o0Yf>w|!la0PS zWg<>(C8&?Og?$PKL4EXK{_b6PGSYc93)&-7xgE+F_+;f!9va~eq}GH#l5JBsF!t%A z3Y$I(H0f~K{?U|eix^Lj!UMEbeRW;-1EZC`+8p{#eR3qD)qLNHHm%(&)yqejD2Vxm zd=G1kpF!R0Yqc4TXVBx-7H5&y1O3=XLvA+t8MFwEr48U1cZIt-m_4xYtd;M8Pc!vo z=aR^^vS% zLAzYa(T-1wJs8Vc2 zi$5V=m(2{RT~)ZOgx;u1n-AsjMt0ffVUFCD*|?L<#o8xR84pD}`|+%O;o3E!7i{Dd zl<(rDbrp1WtHSYxx%91@=viMB*1(NY$<)gvgH{LM7)Gqm&@?>X--#yVy-@zz zJ$VO;|8HI=qcqAIL(dO`hMxuHeiON@T-d2lwb0j6e_i2vVtyRTT<3+TCl<37Ig0BB z%2ssvy{sVjHcEVdC=9J4pBWxz^-?O}S?0JysA1(vtJJ%B@0r+c=4P+b3P~)h8|hHq z!-|CM1UtX06YGs*Sz}zlT4Gn$54{REvr^bpSVj*IqqUs@7V|tX>m2)RC3Lk29ibHVW;Ln4-FZ|nh~qwS?#`KEKob!l~%hS`)De6?NBLwBmIlnt#Lg$ zyRZ(QM%Olse0>@JM;FFXv$G55GSlb#QD3@!%Z0oSrT=JUb!+YLdm|?<{pA1Q?}|Ct z=)bx<%*l=2a7OH}_75p~zc4SG`GKm1mBv1PRh{u7gm~DWr|y$m#a%N%vq8|9_-k<+xg+&Y8KC|LSi!E`ET{ zZ@Gm3qL6Yt>?2%^fj^+|B$Up$U^{1*k5r*l8UgIzgs@^V~UL-8rj;lJqQ94|IJ*sarup5vk@ z#4(=D{|^$ph2g<2-s=h8%J5)E@16ud$nY6%2KSp1ytCnwP-9;s!Mhti&G>((CwSPW z2zH>xCU_qUAM8dAN$~!Lp9xQteGtZ{JYRhH-x@ z!A~}vwI}zd6Z}-e2b(O4R$H#f_N^*RFuXAwW6lUF#VZQmiZ!mfsNq4rGvXn9b^1|z zM632=Nqd@w?}S_d{HX+=Xn1Ehwe-^jS9xuPHfk5{hb8zV!zFVBwUgkJ4G(q$MZ@Ij zKf`e8$wGOT;uVEgLJ~vm%kY@~fQRstWBkRWU+th0t7x^~c?mwn@L)Id$^@Tkc(A{D zcY;qdyfu2N?5iZW+HocLS#7w#li<@0ZwtSHaw|=Lg&BqidzX?`jrpx8d{<_BwMWBa zz5*V?e--0zMZV6q@WH;}R_v>)zJ$*-JlHp^p5U_#5B3cYNO1M@U5&;-jyb{S7#{2! z_D^t)BfG+@>&Jaug3mQP*f*S>;O811>>FN^;2OVnMH*-T_uCTuJi~*1!{rHnzTv^X zq4>J7+{#~Mc(9{)cgRd;o$o^myN~+Pp5Mg7sUKF6R}c5@FJL-% zUg5sO706>{T#D7>x875j_W#5$x#ZspBbnzQI-eb4dTr-0R?2ytwAv^EBu9v0!ILT-d#3{CPYLDZ3})Gkq`r zuKH^i_FePxRs0I8xIV(^ef>TkC%a_%H)IDO_p6Ljzw%a(_QPV!E#;oSzZbtJwhm6V z6N>rW=``;;aM#{yDhqxXn7(mje0Cn+SW3DT=1JD)x?eOilx_OnIKs=mK<8QB?KdXa z7SMV9BWa`Etewhs?&U7ud!6Jo^Yad~{nFioueH-Y>sDYb;DembJiW-q!3;!a3TO_H ztXgAc5Uo;1!C5+!wb)cPX?r*c8 z-W%q`yOM9VQDX94Y^NW~ce4AWN{H(u{2Ezz1T7|glgOv?HxusBJkl<1$c6UBOWmC@ zO^2D3w^mk~F%?6&^1aV=(bvzYB88h~bfSFojZ&5G5|_(mt0XUV ztM&-rxx{r^8GK!wc2O0!h0}UbzDw=wU-?dV&r{zjedT|s6XhiQOn2px;jNRMI9S3i zaoa}D*U2R-y~$4NJ3Z~`W|Lx3+1;$2o$N$Cg>PJnPqnSu5bY=QEIWUWC8)Kfp;q>x zzQolW9vVKFoR2U90$96hw&tvvz4-9 z=5Wz5;yE_5{u%P7wx084;Px_l=qu-#9*KNyoOa|?GqSQf)V&Z>Im6}rmf0mb`Rdb} z>-0U@3M5~p3Rs`%6*LaWgCMS%{t znicJ;cS3IoBMDRuv#%=lgKd%J-Pg(!}N4dkW{;$56))(kzubz~z z^LI$_@`guyw+Svjp-#wH$p5eC>3KYo-^thcA18Pf!xzABM>Z+JHNxfV|HTO|&Rf3z z-y>OF%E0TD`kIC2n^AfzK;lY0VNnC;DL`2*8dF>Jn#|1`u}6*wS0XW_z1y%_Fodbg~cE2ZEG%aMPa?#((tf; zd#n_%D7*qRy4HJ!$8}M_L-+%zN3DOVv;Jvi;kA3qdM3fOZmYnYsPFen@HU2rb>Qd( zZ)-9S9L5n}L z^X;LW^-{?D74_iWF2N5mJgisyC%EFR!<^TP``83mymi=*=*@k4 zf-BxS&`^E3U!LIN6V-|9(mNAe@zy~XLSx&L39fkSFtdwa^lpOpF+8kGzfEv`uZ|1r z&u!5;ReMu8XdPUKRr?XTC%C>>2N{Mz+*>BNzE_9Sv>>-!p9I%BxQ@-g>{})H;f8Z6 z822+1e4ychA9PWITRLo)3VvmRD;;&1&xdn=G{KdQI?V7RxWAs@n#1d`G8xHzeS#|; zb)X_famN@-NjVHPJn+SKO7No$Kbjpb`lrT?(BI4J{W|PjPUbGkD#wp8JlLE68{q*q*In!R-@0q9t8Zw%`!M&izti-U_3NG0yt5&`m#>+(hH935!Rp*E z-&ktnPn3Ck+#$HZIIW)#BtPOyh|iI(iBtbr!saf;|EvDfh^Op2`)x4U_0!s0_W8uK zOn++~uD+vjO>}it&f34_vp^#=%d>V9%IeB#HY?!Q>0D@-(sub8tCxHw-OXi;pTZW} z`dPjfCXX!N!A?|xd>6RmyXPwkwRB+<+$Ft&?{xQW40mA(-7l@y<$HuQwRDm$R+>8V zoqPq*%5{w~9p-M1zN4JxeZ@Y<)&invH2-RDK8`P*W@luH)2(&blMHmqo$_`@$y$3d z{<@T!VrgzGhD!L-ccp842G;`jE4@?U4s{2{6!djxMc*LzQuK{-qAnC}mJ|IU-)tvJ zO}+r7-`BzI1GiX`q`MeMcIFpfZtWVc&@MH+oL7UgShE$#LWJ@RzcFkH4Z;AbIhPZNBM z;g2HEi(Rk;KhALJHSm=L*E&a*tswE;T?u}&;nIeHYD(}4hRePS6jp+tZumn;64O2t z{0zfo6NYjF&-pmh3}1reBITCgXB&QpyMue%1fOI0V&qym?=->BHT+LVrm|j0@cD+{ zip(k}<0SY3!*Ag{8_M&s()3(n_)X}!u`W&U%M8B}8B1gY6Z}fUuSY5t8@UO7t>M?X z>$pFh;0h-Vn``)gSAy$%i~wAe^P|*nQQ%wMYTvz zzY`yUUFXWmXR&eJvHzCfDwkpx*5~D(C=Gv`;bDDVEx}d3#m2Aap5UsFVpidzW4a~y z?S>!2sjlpeB)ICe*wzWOlLS{gDCU&dvE0v1@H-7Zp7VJry#!xs_&83$;r!k}nc#ODu3b6w$rAj}hL7ij8Km12{2s$k;lx_jMNgKN&%K79inSTeu}JXy3=e#S z772d8;aUqJ1)Jax7#{c$$0WG=NwKX+*yl=c^{Zm`A}4XbB*E1Wi=ltj&lV@R`fV}$ zF=ujLmEh{<#q2Xq;r^Ed*SJv3-qlp@UnIE3kz#h-)L#ovm6pQ_!-Krl?g{>w;nUr8 z?gu8g#Tvj`C|SCAh}@V(9+4+&@e3XAKX0@Lv-AIm3h8T$QIw%jbE+&tt!s@hZVz zFg)<(+b6i@r()Lb7jPet;F`~hSy5fc{kQ~w+3>JWFg3w7Uly~fx`_Lt1lRmp%--(B z-0w(m&Bw*8YZh{UBEdC(7qe2*xcHX@*L+{h`sGsYn-cs@!-HI;f2On?L^l+}^UyeY zR_Qoa;cdf%T-bzCyrS?naCrxDH$0AS0T1D;#Q4WCzWv3*2R?6|1Yd1<;PbXj@V^=^ z*=Ke{6Z{>+1OIn)g1>8ckV~4H;O`k8_`jDX`1^(j{_kB0zQ*vt|9vXKKQKJVCB2*A z9~vI`!e1u%M}`N!u=H5+`C_f%Vc%j{DK0fXO0zk#*+@VLkL4Ng5WeEGp}x+bJU_PZ zVc%lU1pmbFuy4^M!9O)T>{}d~;Oh(z`xe6!{4>MDzQq{{zTWVIY(HjEf^RUKGo-lR zp5UJw-rlw6zAC{t8s5Qm;QnTUe_?n>q^&qdG{H9+9^?=?#>nPPmBW{Yhy9PL3I3Jg zVgI9Hf`4szkVEXA;NKV?_Cf<`y$(wt}n`eYk1Cg{ulYdiR@Sgr!j*Ym{H>y zoTLnXSw;)UkgUkq4awN~$Y}i;I~^IkiHseLjNOV1iMfo^#xge$cLZ(_?nvBV+z{MQ z+)=n;xZ$`FxRJO~xY4+yabs}D;Eu%|hZ~DK5qA>qWZZb%DY#Q{6L6>DCgM)VO~OsV zO~p;aorRl@n}ItUHxoAtHybwxcMfhY?p)kF+rsBPa^yKm8=}=qJ9*JZI z^Y`8&+-EqIubjTu>yjW>D8H4T!b7krkKa5P!e~FE(@DX73$Eu1`_J|{^7#H&+_U8& z{YtyikURZOvHaUR6JGw$Ay9Kn{`7Z_|K44DCpR!2cEe@3s#s|3wt_Z*|9E!}s~P#; zwOy3$@%3U%8p%pit8T5xpJ8?XJiLNe;dboEnoFT)xt6T1zdm#GGG+v9sz3w#om&ezL5&22@8!wSlAH!iu zDf-5{8Tbl>JB>B$O~&ic`_H)CcI-iJYz%vfTOWPXZ4In&H#%{-A;hVip9ptgv8~1x!!@=O zu^YZ{-h(XEKI&Fb@+;xWsLi}Wi+PO_egm%8+k9JR-F!g)wM(%l@97N0{>*pEX{g20 z-0Dk zcer~o*3negE5>!MaTXQV8uwOwI?WA?PtP;{nV!CERuNPp9f=VNxqc`grSWa!Cg{se znC;iZSk7|oV=QxxyQo;+F*!wrRLjk+!G`v?Uo7#Xtz2%4?_T0=i#58;-4@&V^G+J4 zim$ypC4TKxcX52C^6~dZ9#=%KXfdlilO7JlI`{ao=iuSH1IrD_1-0Hj3YJ zjeDH(75;9?uFGXkG?((A9i@#it^w}xn35@ObWF*7<8tZkhm1cd-z&!1lN&Pq_+DaCM$@q6Wa&-PRIk8$Pk<#B4%5I;(7PM3ID z>S?;f2MaAQv_Xw7VSLdTvxK^P&sG7-c_UT@T9GNujp-vg1wiMYr2U~;EQNE7cdE2K z?|vN?N>1@g6ISTS?p9(_o_gE&b>2uhekrZ$57K>AxOT4MsPMK@Gd}j&*6yxY1AW{p z(RY}u7|ZJj(|*<49o$3lDeJWObewV1^z>@uBg%J&aWdt5*m$z?J!xEL`Cc{twtR0n zoi-9$@*O4C=(`x7PtUJ64xxNE8ShoT74C-kl!gv~rp)6>o;l*(hG z)lL0aj&t0&=)1tUn0ot9wnrLLvBX^+bJ*FP7DHZWd|ZWm1Y1j?99P=u8p1l2>~}75 z$Hek%<@StUA8q|m`wI&DzRT^)b~T={ut#0P6=C#I`^it!c+q&ZdcMTC-SR!=c;L?-o#5LT9{9OB4>I4s+0O95 z|80@rl?@O4;+hG*v*Cfi>=V42;U{3*4E{2sWgfrssq@>%@&C01uW5MTcP~ruy$uiY ztvZPyk4LkFsHS81uM_Wbyn*3kOy0A1f;Tlh@aqo|9?Q9z;X(fHaVV&WR}@}}b^l22 zTBm7WVJG+_%?%&!hI3z&;4KpTn*?u};9Ili%;Ra5;MEhnb%HlY@HU2reT#Mp-ZsGp zCHO%Ju61+%UGbADF*>6!JpO@(2mYYW%*pX14G;Xp9TR+*;er3C6R7g=s=rE6j&)Olk2XB; zJw;u)*dC5HJn&E7D#a@bZwim*A?}99{uuDUhg}-W4XMb=V=R2wCs>u>YOh$o}d%WR+@4sJytH1q!?7at=7R9#jT~z}^ zR&pF*L_h>250V*>oHLS>1VM7nIVngK5y>bZh$IOjA}WfaBBCOSA}T5(W>ge#-~WGA z^~{njXP^C?bH8)%{h0Tu>Gl5J>gww1>dI?X0>+GH<5*pBzMlygM`FFvR`CJC{WyMq z#Rm%a>*%K|K1jG9$6u!SVBvlof4kyCg!^&)!-{i$DItvGpH_UBa6gWJQE`@c31J*R z4f@_uI*btR$MN$jK2o?JuP>wcDB*s*zP93{g>#=s=qD;ZM))JJ29JK0;$wwBY97UL zisIvhFNZaFW0omCUib>^ZGbk1;+%I%kXAfeAc{{EzS^wD@vP#LgtM*8v6$p^m@M4S z*XBBq%%=!nC*uqi6`v~H&-*r4e46m}ut5WTbH%3%_w&sY6rUmd3G)PwOBA0e+|Ork zQGAy0Ct+s^^`PR+uL;tcH|CV$%&!Tu3k1fc6n{YYcGzlyB`~xpqx>*O_|vc!gngkD zpDTP1Y~`Uhp!kEr_hQ!%tV>j!^HB-tGhoQe97Xxy;uFP=0q2(z!g~Khia#XWkApv} zIP(*2Z`HwZr{c^v2|?d~v*Ig-`*Ha-imw&!$MGLk{Bhxa+@H2#)B5r)1)pDnNyG>1 z{S@CI+|M^mQ+%UvKR+=>aX!}s%viBr8l?DU;eK9(efp?e<#SCytGOJGec?aiTZET@ z7CF}GEB=)5JD_2Xaj@iYVz&zSdK-P2IsARaZWHe9oVWAsEZQq~{!}%Onjnd#>$+zQL<=`T_=v^U?f|~~y z+sGz`6UmYPckp|3U8V`guQ>*8(Du7_TsJEiT{I3n;VjlTz~urP|0}XEj!d&6;aCR8 z3UK5#{>JI>qcF+pz;BdZJda_MZ^V1WBlF2mDU;*Z2!5KuQSX7jxA_+`{YQQ%AH9n8 z_fudO;d#uphVKlkBjm{Sfjk6m(n+kx!#&a_0I$U{?mgx38;;|UY<1yKDwu-uwmV3r zFn^vAJtdANaLx3CQqIs%Wy6*qrB;S*Qc4}oF+2fEQ_K%Rnqx}g%483lX1RR${Sbh7Hl(V6P$d$ueGGsYg6JrkiNU4n9YoW9+hO5(WE1dZ>&f~Qv znN}fAZOq3(>T0eApWSdTEax)8=N%|L87fQl+NcTYB1E=e+3ICaWwBuknqjt+XG>{? z=@ss2jbVG1thtG-O13s7k^jlM8TNiC^%Q&ml!lm+`Tdv&?_(>fKGH`F`+1ZG8`dn8 z?lSDlQR;8l+oLo{`jwQ1nKgk8H#Wp(p850@AKueazQ@8}3CwC&H;Gv7{;)ZploCh6 zxsy%WaPIu1J;EoN$c}lU)QWuewUI7$%{xiw|C}U6md<-gPm(d~XNHC_*P1L?+CjF+ z?8lQ~e7M*5qq2|GW>Y08ovMU0mYYHdIeMDWXnGryGRbGpq!gW=B(YD;s|=OV^%RhL zqVL%oBmR%0Nx)wom39K9dNRtL7E(D_mf%z7hgKyvqMgKE1@D*ko5!2BAy%u+NK}nH zyQ7qy^w9&Yu=VEl;A4q-ElAIp*+sqmQ=g-lrhPwIbAQ9O1jDIj*eaq_ zS4Ok(V&OhHpVCTZRA{BTJ?=3|y?z)W=#MXYVdS%0Nm z=IpNI>)(TthJJhEeAXzu?tLaZB+_muO-3X=!R$%*cOpo$&F&EXRx>e3>r7+^vA2x= zGSz$>&xNxdFpO=GVys&WYkq(=git~F6Mo5a__=4ia zg%=Pzh^!}~bYQ*>wlgFZ;fQr3Mh#yN1Ken{+sK1g1B|}=cba1NQg7^AGwwfhjVxy{qwi?z&?iiJox2zhR^Tx$L|;6*lvyF=sa>Q8c7j%HbVC(dWvj!(Y;w3!Ryio7PTdOOZ7Dsd`~7EB2_pKVdD z8T9QLuClwfhOH+${k#gV!|}k1hyC3Z!u4x_GB+}2MGW6tO0}iklO9qW;=pT`z%?qM zY-SBi`}p0FO&DLE&$ohS6*DY9DAhARBN_R7A^2b}qRe(H+oB9NuiP2$p)}saMEK$k z5sjrq#8iyO-SeJ#?cye~OIrkYNBc!S38r*Dmn; zz&AxX8O>X;?Z$Vh4*pkA+_wvSJ%6j>eA}aXKC9vxh5PZr6pCjN?#BneMR^@vm-V?p zNs);EClzPB*=nqg|F0>|a-45}HT-`@@pxirR#(OUHHx#|W)Gh22-aWGby;uI;$|iM zAE7wwciMepJEE)NtcR)TQvv^*DbD(n<@=rZUtMuF#kk{adHiQj$i(~hPJ(c6@2Ft1 z|J0i2Q2u#7b|RGaLK@{?^UEmdqVO3Hmd}Oo{{nfKH`4sH_b>K0ct6+y0UuvqHpVmS z1jqW6a%>@MCKj|Z7D79TWc)r~`Qcu9{LXQouJ9j?@9e?%!*D#0GSB-EM~2PsJdfXh zg5y{g>uZaCKxR1lxmIRe3gb71ckOQst0sQy0GAzOe2has$dlk6f@2$iZKb!K_kD_* z@)VIicyfHslqyILq*Ptr0ZO&a>N9Ma(O)%;Q}7v(7nWQ`sj}olN>wGNQmQ7;oUMV| z!iWp|#$O)r*Di1P#8@8|LjLgk)Ko$^eC{bh3nL_7^D{>=yz++c9;I4_Z!M)d@_Z>3 zGR#SoZZmx6DAhE4D=FP&_`Xp}lraOoxyje`*Ws8DW0-){VNF{R-&R28eOH&?m}mG) zX5Te++x$&$llQDE};bIi@=a|Ahzt z`H9g=VSUf?pL?J_jQQ2oa68~$fn!}j{ww5cm>Z40w<`kug@)D~YT);2t%TorZWA1% zJ!7wB(KWcAtCm@UMg7B}82y`%aV(bnNFmJa#((Rj7tFTbz4VH?9jV9iu_Wq^rlu6q zn0=PHW*KIQ_nIei_>y!u!byWh3(NJP7-yY`ke8WUxB5^LOd6cWP|Bjjg(gu5rL64n z(a>4>+x#kP^Yj~sf_xDJwA@Ka`jCY#Cj0MQ%55v3otUsnCvpUkwVXzgZLc6&PLQC zcvaTovK2B!Mqj z5K1{ybc(;uA(JUQaE@Z|8gN^TS>fgX^%SAA-Eh zR9uK_tq7^d75Fb5 z%Msd*zZ)rVBL)6*DZqEw{7wB|>!WMA*LzJh3unYjztr-rX1O*P|JR_FiRypWDI6h1 z6=p{9%?$oIL&O=JBM}evIf@=S$w1vXtSd}gr`Jjhh9NzaDl(k$twl=3Pr7J@#H}hA%l)lRpz`7Tf(oHdc*~u(K zDyPA>IF!775VHY@+dAkIyl8gd7y2uM>kKpnLLBFrbZAxZitJ^wg~3{my}xFN4XuPf ziu2g+D~CT)W2uvNhn^>eY^(dxWMS^*g@_%ml=UEWTq)tPqGfTNES2#M{~$x<7w!98-_ z-A>H^zGz~@-N70m!sb)iDRn*1=ich;pj+|@&gWGpL!bG6)MsmO6{ZvSzx4Iyc0-LC z)|-=Y&Mc|-*5J&I@VOmp@D9kkLBIKsrqRxF`Q**u9mh$|U_DMh3D6xnGS%mdCP~^g z`3!frrF5 z%71ZU?0q^I{)GqsoDateY?(jrr2HocKVstHUwH7(c>?yZ*e#=X}ZhZFG zy;x%SyiN6vuOZ$rX>B^2#a8U?$1w_^rBemu#I`kQ6XV0?C@vJBFLet@hdBk4DcV2fj zv^}3RQ|%J_u+58<;&{w4>?wQBe2dRd^7-BTZm!5Dtz{bWlh?+EXRy=M!K=eI^v~w1 z$%1ry(K1!O?B!c_eX;NPWt`P$W30y=@dIsTj2+QN1wS8|@8rXM*#D5v z6@z_}zTbF z?o~|55cl7tmB~AM)|^CIF!g!wTWv}gjTxoCtUhjzm|!O(Y`Zo&ULDa(Azbv#P98cAuH zLDDiUeqv~2O`<)5Rx8i0m9#s1Kie12nrxWt(@viQO>JBB9e-9I)7Nf#*N+(WH3LFU zEsATTgwM-}Q%bxw+1w(uaT$jP@P>VDxkDE7Pks|`x+2f_L|&g@e-0^q#bo)#|9%X) z@n_`2tA_9K&d`VJYqB7G_Dhb6Er?GHpYRkszlF96@+hx-+N`w)P!=4t9q}Yc3LxIc z%gGySd{w(8|t>vC^8D zGpmQO#YX5gb;nrQSQ+_czvWT1P5I8f2}=f_oA0jrtBp3SBbWwxaBe5VnwhaEV@Kji zF%|>x1XiE%y{0^PJE~xmjXnp!&!n?H6x3b8H{WvZqRu$8m&e(?#xfSTMMkC>_B{J_ z==TgVb?m)UeQ)kZr0C;P%J~v5#tlKLF$VQbbGzehAFs{kc#xLZpAd6iX9da*+A5fW z(&G`k!m?+{>ohW#FGhFS0Z)nPy-NDGyYVj6LIZ%l-ZN+Wc`4Bp49{|+*@E|o{=1^( z-&gvk1@Uxwwd2V5AK*Ry4)1PC8*Aqx@mWgdwKJrwj!J7^UhhUJ#QXU+^hn+|--h_l zFc}bHdYt(to(IdLA0=P;GLCsGJDx1x)_YK%&~E05qM*A zYK!dQE=HRZxGGE6yhx*!c=kt;M&)r;`n_UVW>rU@iTBAT%cnZS^g#~LWQz1mi@;C3 zxkdU;8&IC@G<{G$^+!!G0d>?2)F$(g_N(pMq%_!MH(Qn)tOfUnT<_E3xP2G<)qibM zf8o=8fJuGDOXwem+?e0oh4=3qzA**5pw~77z2XJoU3|$7p6F*Cm~KBwy}%kF%0En# zAMqCP+_zx`;uF{v+Wrrpn`oE5IZ8IB!DPhofX#%wOP}jt$sr%!)&(Ii&oND8Cw8Xb zSb0hbxYu{>GL$_$o2hTz=b^;yE3HfVy~XrL-MSt1{&CCEaI&}XUYy4D<{^CcteLMj zAQyfPnYnPYo!ZO42W$`YfEA86(E{Mrez&)V^7a$+jr5y2GRD&27UYaos8^3j=zZ`c zCgX}++s3=$zIacW74}Jd=hy{((*DO=$nmFEESC_eJId@dWFA@4A) zZ4HU7ZkALm%Ld@R8iMj^2JG>(bXqU(@m}1|8z|!v(b{N&dzyg~l<)O$oQ-JU+?DXT z0X6C~i1!h(Y{U4b#T`w7@0oTMJ`dm=&J^8>GuZdfkNjO3Wmiq9Cp+P-zE@gW12OJ6 zTFOY~i$_swZ%4V7fcvY8HcxZp!G3rOkKj2>2&KwnrgND2cnW!Y0%FB?n0Iu<#GpJ# zZ7U$=Y(Y)Hd8hF^Q@9I83b>!&hAIBpv(2{QYhMRUhQDQ~qt@C|csKd(UAD2v!8f7x z!X60oT0CmE36}jGpYt{$UvL~O6Mkcxp8bHla+i0a7U1qa-ElXpEy~~?vOXQvTr6WQ zhjt@Qz43P=1^ydSfbV!TZqM9Kt2iU@Kb7LFJK0_vhX1SsBmcfX@9lH2ZjU(MbO-B-nX2={h1zE>R8BmTTyjnj%V9jU!@H~zn=_1ZL0jD91w7;HBk7{}yZQhth;f_^vL zr;70!DPKfOV+V52_qqQ6SGCoZqcY^B$$FW3y zkM1{;83tvBNqzmvzoU&LFTgqAW~x`Sxx!`%#*32r9*EQQ+)_X1pQj8o=^ z<6axZ;Lvz9*^i+_2=dhEILQCT^|&nCSWdDPjyW{huZ?nlv`ATJ!6)jeNPl{e;aDAi z?}1va<__Ot?`1Ad@;-+qX=GA=Ga6ItZTR*uq{4>pDbFv0Qio*-X+^`_PpPKi`$Vas zlwg$F8rB(uBX~8-zk_Q|g)$mTfet^ zZ}cYn$=y=xl`J_>=&Pk(Qy$tI(Uazmra>hVvFQG;n*L>rnv8M`# zB{+R!Jyy7j0j5X}-|qFAO4I`8+$Y<;jFpcKOFW)e&P1b4jd2IjXj2z6E5rhOtfOLJ zo1+HOk*kSWcKft0YgisJlv+|#QR-xF31@an8jI>?Sg-O|Skr^Vp3XhcZN3+pwA48r zYCZ*JnCypFN_zgev-s9HSDiI6Q$s&+s9`C~&~7v5)p@VQ|)(zwsUh$X!sonB^)% zt06Tmufdf7(O6O&>GSdTuCiX6A+p?!ECTS!MtLgAPcP#Rc_+Pbl_4hT>tWBmq|{(t z%`oqlRu-lHhV2zfJq%lFlm^OL2hZ4gpw!W@{YB|M!y1IrU8V&Z4ElL5#4o%Pu4-U= zkiG^dcr17F5NqXCSOD_kXtbK3%{Z~_fDQ*5{{;whWvw!7&{_nME!e#!?_ZLbc zm!Y+P3C}X#&kOqg!cmO01|H_I*xQcQ1>LRuGq)Oy3>?IN;lY1aV)TvKKd7VpqY%R1 z0I_Q&Jov|O3I2S$B0>50{fnli8-5iY{KwO~^ubX_pl;+m6YJP>uo-jcRn%1{QCGno zfV=iP^+}ds;`1baI}dm5clsaE#+qqy{@h6Bb)va8Ewe5E)hGUan2vP`GcQGYrbjVq z$#F}LT+-6`dB_ijB#8~HhA3Vh+O@Qtk8!IQ_zibG#{_JLT{ zWyq|hSORi|4*gV?9ZREYawYEg-~BJ;&57)OtxO!QG#vH!XjoR6j8UW~u#R&l);lc; zv8!mJk!cOAWsMEjecx;kXPp#nTGp3iuqtILR-l|QABI%xENgriMxt0&NCRmoQfLfPXA|b4 zm_EDBzOz0h6HL)>d|WS>O-K!fc`W3O@6D}9WyU@)V#b*ghT7J=gt|IbLLH4OZ^Fp? zR#O%)2Y;P!dZ4AmH`m8(x~z_QAMsoCfv@ox)>bn#+|x5Mp2_=XS~fMsfAisB_oCB2 z<(Sv*L(C8r#G?hC8+($SO;_Zw30Tw4eMUL5c0cm#gIHA@Avrq9Kq9zi-0Rk1VD%H^0sG?;q0SkM@ZGT5y}ssH~#+LN`VFcRL|cmb7GlKA%Fd)U64`QslES+ zXBNvZ&3=OaP_Ht-qURTr0okaU;(UWp`p2XPFRM7qB$VGV>A-U+?%NS*V$y)eD$Y3( zywNeK!TrP#V!=--?%N$HV^V^@s(5DcpCTp&_%6kLdn5*b z*dL;wSHQa}ju#Vum)&LX_KN2d{)hVmypiJZ z!hd(agV$EPfbdK15_lEG3kv_u{RUoA@xsD?b-#iaQoM-pU)(R?af%lee$ib7&!Bj5 z;Xk{d!LR<1{9H;1|H=IX{k0qDeE~j3aX%h>)|~|(s(2&u|GE1dyszSHTVjt8 z_ZfH(#hVHL)O`xxLGc#C&$u(-EfjAh{1f*Hczwm&2tVykgR}OE%7MFuf9yU6ude*J z7ygm^2)vx)9fhBAr@)IS&hcvOGvYo3&!>1-;UBmUz%wb{UHD0N50>vi?KjMyn&s2Pp@YmdH;NumaBK%eND)>mnrwKpo4ucO;e1`B>+$-Qk(Fcms zd6w{(-OJ#gqn#7+`-LBJhrqii|8s=DwZiwhz2GhHz?V=jKPG&S+XG%i`Cl)5x7!UqQ>Eud;k(=} z@O;YuCgD%Jr@;>^|4$0v>2`u=QU0G2zQgSR->Cd=6TaPT2T!N`?-0JtZ3B0TKP`N# z+Y0{McgfFdxA3RjQ{dk!zE}7bw*~y1;`@X@>7E4tNb&u`H@nT??m;;#r_@79CQR{T}rkGsdghbewU_&T=^yr1H4 z2w&^gf_GH>sPHvz4R|ZXj|pGxR)aTI{J8K{ZWVYX#oraa!mR)=uJ{Sz%iVJDJc^$b z{-}EtJiX!{3SZ`yf&cMs@^k!1_#^HS@HZ7dE&O5kF!+AO&j?@QmVob4{4?PXxre|v zD}Gk^Vz(H4t>Rw_U*r~nFHroP@P%$6_;kh33t!+CfR9o9g7EonKKKa5zY{*s%>%zr z@gIag=pF>mz``iJr#}gw&q zn+2X(@hie-x|!gyivKBmhMNI?};CHyk{N;#q|cbHl(>DV|;UP&X9ZD4tXJ5H|$;5=LvH`+~X^{sz0j;O7;O z6F$fd0za*IKH&r1K=5OV#|t0e27n(_ynyiju0QxQiWd}qpSutIDaD~WiNAiXANXp; ziwN)Q`hw3>98;J0yVuNCFC)CC>j_>> z@p8iNarc0iRlL0L9kghz@k+wGxo+TB&L{h?BD{<10)9^MYQj6a z&fp&_o+!MN>jZvW@tVRrx{lzlDqc%?2iF1ofZ}z8w|DKqcPd^_csth)e52wGgx~G% z24A6gBjIgbTkr*nHxb^(wE>@|cr)RxU2E|1inkEn%C!Q&Q}I^9o4ID-WfjLTH~yNs zrr;$Nzgu_{*95$n;_Zbuc8$SvDc(_dBi9H#qvD-~H*^ibw_=PddcIwSH*gKWQ!4-6 zh1YlW!DlM}_Xw}&>Vf~`>*R2H39swwf?rg;kMKIK4)}S+`wFk^YJ-2S__xt@s4tm0e}<`xKufyppQ~-d*u2!YjIp;O!KjCcJ{H0NzCL z8N%;$cY@bZe3tO?t~_`p#qSrM;1a-#DLzMdIadxmpW+V+FYC&JXHa~;@G`Cp`0wZN zCDc0$g_m}v!M{{|vG7u^6!;;a zQ^hw4zt!CeUQO{Qg%@xIz>6yWl<-^JE#UbT-zGfX#e-*4e24J-Eq4k1#R`C~w=Wsc|Ir9_wKO{Um)&?8%gyOFV&jzg)j4vtvs_?8XEBHdij|k7= zvVhN4{0-rmU1snJiXRo8$z=i`ruZ@88C^#3K8ha~ezUt7yrbgp3cm@fn=vk=_zB?| zpqGPjA;nJ$Pw&!$bH*e}hYy9PbLqfKDgKf0v@R`pVZ~1iPvg>n$0>eBcxsm#obwk^ z_@4<+!v2%KtBuIYrbU&@4j!QJh&1b2Bal_!-5|3&(IT`2J*`f=vwU zKw^Kvakt_Z6yKmYo7lLby@(@c-$Fc7e5d#d<)2+)jG)?|ajZ`sFZ1oG$y1qgGNje^ zCo6WZufpE*55m8Oz6RcR#W`e~%AB{nk6DUC1pmhJKGrGD>!m`&miMtwamG89`PTA2 z-c_9UpUQk^c^}^@{;TlsE#vz>W|mEeKY4EG`SO-Y{h z?8i8U;=c=j&%TFaZN>i(egb+A7>7~(vherq`#26${EG0CmihMq#i190cz$3%z;T`8 ze+vK5GGFgg{2#(kSx%X}qqsr7$!$Ke%l)ohur2f%lD>-;(UK{o6qg%;3F01 z`xx-c){TMTmHxW3?ojCKHeGX``^3QTA&Oj3ne5&Fs*W%2x_F3=|inAPy zGyAw*h?&fr3*ocej5E(bV;1|ODb8{>&g_M@5Xw$I{@vxnwJOj2l7Gn*g z;w<;$%r5ApU>&#OtS92k(=-xpPVr%e_^@7yGdrQbh<3R0&w41%?0}{x)_f|?dMnOs zhh7tcP@MH#oY`hs5C5U=i}hlhq24mOvx>7GjWbU{H_VtbinHF0Gf!I9|L-ZzdO8mJ zQJcY!D9(C4&TN9d5%P)RYzM@dC+rj8yA{7h_(r=Cd>5a)FE>)Loe^iM+v-4D6=%C7 z;>VG%;GgS`Q3^q=%4N8Vy2`m30_TcwzG?wQnnO$PsQ0TFJ?;H(%|Wm03%n4sDp7bQn+bfj;x!cSpUhK}SA^Cc{Z|d)w?%x|kF5yp*L2{`6u(P&dYc}+ ztK#e@S2P)H2Jpd(*A{-0y$O7>;&p`IY;Oi%tax4F8Er;zRC`I~WIf>*4aZHn*3E=+ zqrPyzf9e~GH&A>SC0Q(!D z{87A#aIc5)i{czlNrWzRBly4Rl7H^JZZqMHv342#DaD%$_xrqZ{aSRtErff0j<=KB zjj=6-Uxo%d#@ksh`1FZwCH$B@2DFytaNzBPud!=@7Af9I_$s>!e3atdgs*^JH|D7o z?=E~9G$*loSn(dhAAwFK=C2jMNBB~^6ug(>J%!W80{SV6-z$8PT?F1z@&3Z++xg)2 z6(1yguAK|cHB=_t?@-|nKqD37V2TeHKHJU)FRA!Q;nZP;&aC32g-^HB!7s2e6~Z4Q zd@5{3VEj<=vBIZ7Cl>Q-ijNaM0otU{`&E3r@bPv$_!h+{2p?z1fv;42qVTbHEcimj zCkY>8$AHgJe6sM-b~N~C#is}#Wk-ScSA44Qk#;0_SH-6ZA7Mv;w^Dq%@ZokiczwlZ z2p?vLfmc<0rtqP5D0msgX9*u-hk)Ov_-x^W#m-Nh;`a+51dAx>Unu^7@PT$9cq+x` z2p<5=dGsIISPsu|uJFFFu7dhe@dt(Xv3{$KC^eT=7N1d)OY}FDt%Scz4?!e2?M}3GZgRfp1cLiSVwrEBH#q9~R!l zb^%|c_)_7WZD;W5ia#Q}lkEgPO7UgFJKB!m_bL9U@D8>Ecvr=j3vX}RgSSx4Il z4m!rEKE=O~PaYHA%r*o1Uh&6;H?>W{&ndoMcoXQ-L$6Ws4Z<7S#^4_+zEOB1+X(z! z#h(yP`!tw8Qhbx}dbS?;n~HB1Uf0$IKdAT?;k0*y`cCm}!fV(X;9bt(U%1~L!eebL zP$R{k7M{|k1h1_4Zs93x3h;7@?-3qjW5A0l{)})M)`8^)@YLbD`-EHkVLn>FA3*be)M+~KP;Rph|$hb{B_}6fsENV#orSC3DzEC-H77v3jYW;ZBR}s{(*36 z6`=jE_{YNE!+K||yHNZy;nWB~d38ED{$C1z-MkKdLhAa}a!?;=c($0Lzb<-%o^n5T6|E>D9-huiDnHx7++GH>q-;NYJAYXR-Efo6Jfn$6*$+fn|K+oi{(1k zMA#)?4xV1|jKclC@0UJK_McU_-_QOV#d8Y3A1mlF-=uh)@EKTJk9LCMw+QE|d-RtT zFC=`5nF79A@xsCfDctM*r&GL^aIg386t5%P>%m`2ZqLWo6W-Fa zM0~zbyn*l*q9eON@rJ_v{zr2aZzSCBXLPUPO@w=WuO8r0x!P2?-yg!6KaDg0fHT$^ z3x{$TR#cox0T&CG3NAHV8o0D@>EP1CWq`X0?q;}*aGBsT!)1ZX3YQHoJ6sO9oN&3} z^1vm)m4~|%t^!;|xJq!9;i|w@g{uZv1Fk0AU2wJFYQxoos}I)*t}$E_xE63N;ab7n z4c88?JzNL4j&Pmey2JH=y9cf(TrarZaD8m5J!k_yhxXw$N9ui%|92ga{WphqZM>+1 z#`s3px%PhkcYlwr|L-2Jy^sH??@_w_cO9d2xL&$*Z%RI|=t#XoevgjVJMUlde--~I zeWP%q?@@fOJ-!$6?X}0~yyzH(cfDiFr~UhMZTQjmD4gpZqx*_}KNaE?ogW<|f74$6 z`|CyLUoW25{vL%J@#`HUfB)|Bdf{Cgzw7zCmPhABJo^6c#_QkpA6+NPzyIp^?}mT9 z@U9Ify6^w$F}k0B_jql5uJ=94zt;;ty5H-aAMyX{eO`P1nebjk$Lq!Kdi;9vig*<6 zzdAvYen}Jm{kD%bxLJ&~FqLpT*zF5&L(J z*%0sia7E#G9WG{Q2Ry4#!<^JnuM;=L4so z*dx~Dg)0Nc?@>C`#`pGtkzecjIS-$?$wo01nm<~h&f z_&MBH;d`Mst*Hyw2JveL*AcD@++l>xINt$R60S7dD_>zu6CLWmvwPDLzjs79OW{_* zt%utJ_cYwIa4*8W26qhZeYn$bU%*{}`x#Dlb~tKH92|EmNQ7$vmj(CS21ka^c<|bh zy&c-hiCla5>4M3=kvLYwIhEn&1V)P`Tj93DG3}X;4hQxwj(k>M;g~ZAYz5#vrg*Sw)4ZD;P9w-8;p*i?K2q`~C7*!I_(C~dVj{N;`_5HiReo>5mn4g%hn7^3M znBU61kL$qkeqx}w&_Kx@m3$v$#(z;@kA)-In?XJmj%UM>agROe!_I*t{no;(oC?_-lX)c<2WUydX_8RDGhMck)w3X zakU+#9FFVTDc$F$obggdOC3tGFI+#&c2WAq_PgMvagG`@WNY0p^lcf39I!OY`Ehh_ zF)Lmb^W|J6M?V=I^=>GwbJXCabj5M3g3=h#>Y}vYalIO)G>%q{7}83ysrxDJGXuUo zZ(hLeKZjw}^G(=kcptVrPMb5Z9{i<@?52Q^#u#^QgE4Hblwe4kEcY}|1BoM4kHbC! zKOEiKAgd8P7t)LTJdzka1<>}d0Mt>p*~ zeTgwk$nhjfD#Y;>#mh+aXgV> zK7~<4j!pBdS~jwL@PMTr1X+Lg1n!GIC!+Vx)t02$9LGWVbaukN#7UVwA|GP8&YDu9 zFgfmY9EXpC)vmKoRP(r-~mZ6`|5aSC_vx_k4KXITx$qWJ4@ z8C|DT*K(ef(lJX7CrS@H>RwS=Uw+2=6_L8+Uiwk@TP+^mpB6>Vg>B-T;)n7@AMI7-C4SIs11)M%+$5PVm*)E}Uq zHjWxmlomSbgi<;pOA9HTbPK{;VPm+0h-|B+P86m2j@n(69&yxPrgYX(W1mv07_NDz zRKpd=eeSoKjgwoe)ah9JTQ%t#{OOr1Z3-rZJ@tTyNB+lrq?_kb6=>qHUa$ zc!xi+TrbVv3c0UBOqRMILaK5kj-zy3YfV3e9kpI5z2e%9!WcUw&K7fRLSe{FF@BaE ztA=x8rEzYoPL9Kh=6qNMy#N|$i=egkDl~16Ks)yhX!IV17Vt4>4j+fM@w?DaJ^`)f zlP)D}9HfF>gfuZ+ch4(TaMZw}G}W~WF`VnxV(2z2*c7~}{zJP#(TCQK@5lSg&m7pF zzteJbld0beR@9z1YtQ%)vsVz_+ZEkTB>|g?ECSvml&iNE2=UJ4UUr8yO zHN5GxoW}VQhO`H>B%G_``7JD01yb5?ucD4h4awiRziZy_`0zf($&4NSHnZb#cP!&p zptM|t(sB(-ORhWTuX!CcW+=6C)P1J(s-x8dN*@y`XTr(bTdt{~G{SNfCZ#9snGjNpqeT(2%Z_U)C=Ih*?Luj; z<+^dE<2kHS=URQz^p4gXD4lkZjXqz?A2P>P`{kv-eRq$a@#p5VT!X++dRVSzp)|!( zPk>SeM-4ej9UW~-Q2NBtiU+0d9OtPhO%siK-t{V2TYBDcewaLk<%$Us=|jgkQHHSvGojldGd?W!vZ_{MkMMNH zh#dxowbdMQpNBa1w4aCcecyf_-osjsmPQ!IPaW6TQEDoy04SxjZ-jHZ+c!e${AOv} zhQI#qxQdw4W=lPMN}C*K>UiaWcps=uLYmG||AT-3T}%bx2J80uA7p-Cx|<(h3u z+bp#rDK&KSLp&xrT9_gG%8f>4Q5_OzsMpB+^=8?diFVbv%gXg@kprmJ51JS7!7k6} zrzG#N{hHJ#+|%MH>g;2vQKCBKgw!guO8AlNkb(I>tmsH-xgvt``9o}NFjcpi7u@JG zK9)CGt{~&@qbzmqD3x~9Hl$S9)$HZZ{?ok}p8OMzbFK`hH&SqO2q&kd<{!@~=crps zsj;KBKc&Tv>qp8!ioxpb6wowE5ADJV&`7KVEygO)e5?j-$?ni3?g{PV-q2X?2d(J- z(D)t!E%8y%M4tg|{Z+7xun|@tet?CdpJ3JKA}k^O3hPOiU~%aWSZ})O9Be_Qh@lM| zUiFmYs!_(YE-aenMZRHM(Z5{}Li>ceJoNXorPeN`wysxr587Lv`j``}LIF#1uA?eoBCx&+W&Z1@Xl^J)&hj6o{ z{v^-2%~4aFQWr-X2$ZI~+PL{_kp8(H%{FD!ie?*nE}k4)fm}Djb5|Q$6k;l;u}Fb6nBK-?~|@Ri!lBQhS>c zRt&>`DoEUujk}orfDhJ1hdUYKCWWhAbzBL+bF#@=4N7BWbqwRW7PI_Zc|>^^)*I34 z1AR<$L&7^c7IWiVW5ZN`9C`miJ0!%bgXJ1X`tEH{A&iEQo(~_EJp8;AN*|U$ui$ey zd|pG{b|ie>K=0wr@Oewhq+=+T_&I@(xB2;gu$SuhZTk@A6Zh~*{+vM{f* z|M^OEZTM94W5q7(@Yz@aJm)X%bM6f0+vtliMzPbGho?fFDn8i?_BlH-gx=9| zr7O?xXQ?wuDW#+KJf*sh_6;a?aI{E5>3&CBdX!ScaFq_F5$5f19rn1m2A%oi5>}pH zwar5aEgWr~(DwtbQhZbkR0?q}VY%Xvzt*tSv8Hr~qgFVj1V?)xltwyQtf92RO+_m5 ze)7WhCiTBapS4rNIaM6joRW30T$N2}s-?aYrKc=4-6>UawAn)GLH7%m5>ZNTxsH!v zU%-2GI+P|HMVzAv1Qu?CZ5bm{@qa`8w z+u&{st(vqkT&2WVu*9V%34hOJsZ&cSt)tdCrQ(iu!6?1yXb+ez#tVNb3w|)UaOZp~ zzP_Se2KsO0x`%YA=xEc7?6{+~4oW>;tGj%P)UaG}N><-;Z8xRimez(St#-6?Md?{b zO&fVCHr{eoH|bDYHQe!8OG|xZ7aZ51QyOh&gr_^f(Jl?y3`dKQlv2lhkMw8E{dg)h zg&2Mzdz_%lFZ^||s}XVx+n%&?#_)HbHTAON znpXO8maDHRb+wxiD(`}04%DS5y~RZ~f4e)bGvx29P%~2pkiYM+w8}xLsiPecO6?pi zrBLeOXmgWNx|rn{QOpd<_wtx;+3Ndi55lMB?`HWV|AgWUxT7I;N;=w<;MsRNt{SB8k@EbwhLe1aSSRFpDecFOE67RP+V_Lh z!%|z5Y`3L;GJPILkBBw`NOQaR!8jLEWAy$w!o6J9OY*mgCO*W_x6o)uhrF+&Mir$@ zmb&_sGCNxNqV%|w4OleW_K>Ucby$AAd&TUv^_`ZNp~(hZS*KG zBE{Hoyt_S~EOQmd^EgI#T2}S*N{uil)EZa%5V4`}OQC(ydj3QHdM!y4rCups#YtWLfJOO-Ffy5(V5sJ-l#hS+tsOT+zsZ)rW2;h%He z0y}TJg}AqLT+`1qV(y?_Nc!C8XxWj{9!DKFO7ktP{ZjhPr3`tsFtmj^xaz8oHJdlp(o;hX2mf%k;G#a z=UW~5zs$838U4N{xrKYZhaVJ=Q~Yzq^C|wm;_<@0KE$hv7f^h+;su3!J&E;-7gl_k z;zfje{fRk>V+a&~o=;S~xbV@~iv#Uc#X0wc*0=DUibKsmiMLmr<85d`;DZ*Z;^h>t zsd#zCODbMLxYy^nP4P;K=Ty9kaIfc)M)7LG{l1lA{3SfcMB#ql%HOy)C-9nzf1!9S z;eJ2MQ;OG7{4K@n3HSS4zNB~q#rG=SNVwNW*`|0C#WyJ4O!1Y9w@`es;;n>xy_WkG zZ=?7m#qU;pl;Y?#;LnFMSn-a+{eGN16z{Bfd&Rpd&X${rm-j1GH{n@K790yD^VH<& zup2q1R6Y04D{$}s8N9!V$e5*J_o%!0uVeUpjw#+lcwN~)?t+$DTyq9pV$6s0T-om{ee;dX72={vY0~EhkxYy&qU-7=ey&nJLiuV)l?G`+z_a|K$&g4-{SjJ7%Ic z{ee$6`X40R@2{C#ade(=U9WFcR`DUiD`Vdv%xow=RCpEa{fyqL;=_bjm43)r#W6U8 z@V%ZB*YidBYJ~7i((1d6HGhGpCeMSt-tf4M@Q@Ckd;g0=_ziGhBgMbpPjs{5EbsD| z+wn2x1;tsO<}pR_!7QWVV}uujZ4=CVDo#DPJf=84NY4*_dh)vCgx>*MC3wDyj~8A- z)(DhW9E&>;PDy->`62m!Q?dTbgTgK&j;E7(YVramm$@b4AssyT{+ovITO&S`#D8ws zdP4tJ@yWuur!~gL6=yr304(yv;kZWeslxsKUe75$O*r?X!s;8vrwh+7GY-EfK0|ms z9xKLjSkCzJBzCrN&bwj0R`EH)y*^)Q#pem{iBVSc^%Y+zyt`q!607(_!u>wGKPBh4 z*rme#zPV=lYOksi{u$N7l=MmWZIM>sN$bYzS-9c8ZR-ydXtr(^u-B7gMr_I3P3;nL5S z%1;zVbmZ^w561CvC2tD9(GO+*_GFMR;&?g8cO6E2 z7hvWM?*rRG2n{1yY#CA-A$iw#D(DRK}iaW6UI} z_Z)uDjCZLS-o+G<<50qO!puF}VE!6x`hS`V|IqSt%sPgZXxYw*vh3e zz_5K!X|Q3>kJ1FgS!c$Xsm(PX?1S-_F|s!+@04-&pPsT-gzH6E^79#`L0Yi&OF0Wl z?rgXM@0k1l`VY@x|Ab)tp!5|yYza2FMYkJBVQNy!!mCX+t^}17jX${lu_Z8TK$J4KnO2Q5tI4o24|)updupvPlea&W*j2S?iJB zZP>4%G*EivlqQTmRV~jH4-7A4QssZMO+hdp4QRaydUR~q|_Ud@%vBJfGIkx0mqs z#@~$;xRCGSF~7O#XnyjrLYj$hTwQ)PQ`sW3@{6q`?!ep{|6f*||eLZg@3_+24eJg8I|qIKgUP7MuFq0dOxCU!kf!DISe?j4X+_JRtf1B{yXggwlMDe1+ zYhZK$>8v=%n>ee_dHvhl;a>>fkN=lP4+-N&aW;u_TnTH;WPNR0%z{S#IgZ8=*wXl4 zUvYkirA!%*s-Soo;ePz8u;OKf`|+;aiZe@My@`yEam{FS-2~x&JdLx{5of&F|KWXK zQ2b8ee!T9K;vC0>4iY|CC#85r;kThpiF~DaCET6+Hcy*^s|e0NAu^q|zBRMO-^qEjklW+R)%Kw`eGAbpXv z_{OKgH>sZVKpLX&-bmgB`Yb0t`Nr@H>^W70%wOu^YK`O_X4nm+^rD|KD6{wm_-hxy zv!+zQtWAnJpTB3P0&8V12dTBZZTz*2Vg964#H0v!P|%$1?ays(s-no?uMN$Xc;AQS zTg(sN@){@s_+c5qSS5n22-j#XIh^O-W>}_BLLWDm_gNfeB1<@)bvs5Rehp`}mV8dX z1x>ULq^RM$OLm9Z8jlfDNc!q55gASql!GkADVH{F4NC%^Rl>0BBCUnEvYe!R8%oFR zK`w@pnB_cq36$=CmKIYL(4{EV~dvmT!@ zhOkNNOF2(k6uF9J1?laEI@6R|m;_uOoobxrb4Vjo8zdD7%VmDpb8RT~4#T+vwGGP( zp3U;%|I|Om&-|qx_V(QlUBWN%zF)wc&eUK%!7^hZ%8Rvd&xkh0m1#tm6M=gC>I!9{lGN?)x3DDgVBmJRG`~(7q!N{tJtL-+$ST z@`>ftqoZM8=W%O(!|^M)<@I1!<}+(P#IXee*a`W_-+br!pA5GW#`pOB+VAJb_}>_I z7OY5(!Y%~)Kl+Vx%ZE|RB5BMDGZXa>Ym4R3T=}Y|A7%UuRy8}{44azZGfgT;H$lpa z@!iGdA*@hb1O26qNZqTnzKNBJ8SEo5zPvqaDpvPltTpS*i`LV1W=b_bzmNwr9Az-t zI}&Sg(}g(9Hw|%JUaNuW7o>)!UoFpKZPqJ(#pdrO1$6sjv4;bn5KI0z)Wk1K`Okh7 z!{$s2Tl1w&BCbL^zpo;mi$hpzOjU%%GylN)>$J8Q?u_@&R>^ZYd^%LZ$n$tScGk3G z%%ez`6<8zx@l~&}FbdySVWsF{u^n*Q#3J<=-dgBweq;)N;lr4MUt@7ihW#*7s!q7R zla-&@LC0g=CqECEg-FvC2$QRok3fUyZRihCqwxx?mhzL<4n(NDMkmu5pYQMT>GG7h zxwe;%noDQ>(`;mxUiQ+vX73d*bv8Nw^il_NDLk9q2!C&)KYOm(8=iUzjJw~0335J( z`%R6PlJiB4*FCLk)`YWWn8`Sc?W{aV?K)_63^%jPn(*`*o5|shQ`#dT#O0>%5g$rT z?EB@fUC|6hO7nVS%%TsdYs6<-2H{059L^3j!&AN+Iffc;)1fao*DOGqtr4pOFN;p- z+vZ)|(Pw67NPnhzOT5Yq`BAeoTy3pcj*^4V)yH`_LL2(KkI4h3N@$0rwQ(2xnWs!# z9{&uFVBPmeA+^)nmdG_J5DGOj9zbjo(CT1Hl*hcnW9Agz3TJ-_t=L_7nh7B#MUY~Qy;x1N)dtZe- zz50xQtD@AOVkRNq@*QEytwssKSs0cwk*@G=#P$!<6}gI6EP*~n1M@!K{j~U&2YsTN z$SHM^^SEw$1D<((q$5lIa>zgR(TZ+^Hhxd!nrV2y9zzVi4kgYG^L5CdO-y`vnq^H5 zgjd%L3HhL#lP6%#x5-nj!a}2zh0T$zI3*{+4MRVm?+v z*xEp_JDvL=$@*O613+Nox>>&)lK4~)(4COvXCpLkw`$1~SMf8#Tx@@3RMPvU81!f&r3-B00s zYHFO7U!as^eNIo0`spT{9?yy~T!4K0nYjz`W$1};H~wyO66pG$#%9{>02`u=(R z-?yv(dHmnE&zEx0_AmVi-_Fm0lH{-4&kL-Y4F93s&XR$4UB<)fsuqPb= znyol%Dy(;q{fLGs?)x2nKcX&*`+mnrI}+iyR2&r+{``JKl@&*^hCja_QGUhQN}x5J zVF*Vye+}`xMY!LO=rflb|ANB(enf{AzfHK`k7$SDMTPtQh}J01b)Q&4A^Q={R@{%b z`~8SUDelM5``iAAXAi|IN;vn~`@m}}UR8KM+Yh|7;)%lh*goKS6t6AZ?_ZQg@dm>E z{zVsTaym2??)NWxSMg@T{r*J<6mKQm?_ab*@pi(y+wO?Z!-{tl-pzIcpQLzK;azQ4 z@PUfoBi!#_L@WPMe(NpV?_bnFajqYwRm4sRhr13${s#*8`xg~de28$re-U>&i2M&1 z?)NW3J2i=q65h_XL-^k)K34eM_HOXE6`vs7?`yPM@hQSvLt_wDs}!Fh-0y32uj2O$ z_xl=gC1@1Sxx)RvMkN);P(1$pzD8LUe@J*U+1KduKXCEzUOXb)?_czl;;V%F{fpjH z{Bhxa|DyehZxY_nHbl@X72hV@?`t$y@u!8?v*_ZRp^EPjUdN&lXF4dpPk3#MPKK$V z_yOUyUQQ{Jik;wmiZw zrucWl6Kn!_cEx`ZUe1;SH;VrzysRw?e*P-{h4ev#7k_>~(vym#(ThL7FX%4CQ!w1H zkLY5>Qw#U|j!sfMy>P!zX+Om?3itb(c2Yc>@Zz>Oj?EO$CA^p|hGQ+o^9nC&i{e;b z@p$34+uLz0r1-7E{eGmsGIND=C@kFXUz%0<=lKZ17Q!(FxQkDe^*SYl=eD_V{1Ia- z5oh|+4g}--uHqGh`~5$ktcEY~GQOXhJO*w3KXKNxsPCx%!gc-?h5LQ3K2W@paKA6s zuZlDO#fYUHlvgTz@{F*m^DDy7oWh5XmF1bL2>%-WV$8cJURC&c84s+fID^cH5)$1M z(?Rj-!u>v4BNgZSmQk!+py#1@4dJwcgZWm)`95bf-{S)t;);d@{w(wK)7XL(uzqo^1LHCq&KB>Wj@&cjlv;*EvxgUx1) zjVj(m__O9&@ZS}0Dty1$51uvFrvu~JO!#x=Iq;H-Hy7^r^{S_M3*odOfR(d~w-kQR z90VVycq`$wCj^am#aScuc&xO;eH>sUW#`TPAf;Sf1-G2;k0~&{nZuk zBHZuu_PpXzV=`1@k`YzLPWi>p}mtl~r7({#re2)TFI ze6(<{pSw@{GRk+uSKd$&R;a>0lTg9gfr;Smp!A+lh zUo(Wy!k9nSVJJRRIIT9LRjK$a;eP+Xc8bpyKF`d9|Iv!yFMPh45B`Ya4+vj?Q9QI0 z6rUq}A;vpVo+v(7_#*U0&4nnw8)wFe1+O z67$=`!u@`awP2j$oy1)Ou_qiI(l7yx!6q1TGOM^t_F;P;|JQx!)W}bDM z=O#l)h!V}IG-(oLNRg7zolHsm-=DS5xvrxd_wV<6{{R2;Jg?V%op#@SK4+hO_OSNa z!&-aoA#MoakI3PV7(Ubu1wSW;KWcb9fAY#4{+QufNoM|=!yh+%1S`7O+RWik7#`2J zeEC;y;&`v*P{ZSS?~mm05r)Te+ONvt&l+CJm*V&Q9R9rF@toD^qxpCm|0u)TBL!g` z@(VX9{*vKQpR+>_f7$S;w|P0r^t#KGi?&Q4L7n0{f+c>xAo!I}g_dcRsEwt{bigPOBlkaJ_MT zaD8$8a2Mb%#9f5D7+a_dlL5) zZU}BDZWwMjZUpXW+%vdmanIpK;-1I7fO`=)3ilFjH11{GE4Wv2V{otGUdL&rX&mki z+<4rZxVLZ`bb?n5=AIU@tu}=}**Qu#opl*EGZ5<+=RFQsJF(+5JDkI@7rh z2Fu=6m_jlv6FnU+%Rd_vnI~RW2`}}u$)kp6(7j|%ZpT(uH9T>1>X;Pjtzw-Vb;b$;0 z6rPn^=FjReyZ=GyP0L7OOMD#5Om&gHo7Kx1Jl~z?e|6uSXQGkpxyo4YoCV%3_ujw5 zM-f)(6J2b;U*Z$*mR0voHoP-j_g9f_l}VP)Kl2~QJE8$S>xh3T=}{bc|I=W?Hz=(K zME#@5E2BQP(mIy1);s-q|2Etex%+2w_e$@-E2Dpx_ifASui{gBllyBMX z??%|{o%!5^sNGNKzowYeLmg)&YzFxul(yN`_7-zh9Bg7Lw+yB zW%uK9{$yn_KIYK4cR}ZobpwALC;J3Hq`zfX#rxXnZx4R^r2c!;-y1Y3=bcIEz0Mt6 zpZPzF&$2Kr)B8)9pk0Hz2R8)QXiD5aF)`=wNZ6P5_D=SfFL>O$lW@A9xfV-*hsFI6 zdl3CB#@hu9nWXWdk+#O2z-Y@X{3ZHsw6$wt2lJh!yaIeJx+dAIh5jVXX7l_YHhQ-w27wQAkma@okXF9Pdx? zCnEbf#h>a=GkcY7ktFs(`q&SN<%Rwtf3ffHFGKozITG6|y;k6rs)LCqrlo^lk0?ag zIez`m(RYdeeoGAZzR6nk^b&t*iVbyIuNPL+OJXKpJFmHbd?z|dh2^{4S8oupZ`}e~ zmaySYa}4=Tb@!ycu1@k4VNHLf9dGRf^r= zR&|ToH%FOvRNNmTJ-VSRj;g+9aUwx!-_2|Mt#=!_+tWOab93+|VDF;awVU7m*BIhM zlQAi5iz0o#U)^cXz;nSa#2V@LX$*rogC?f^0$-LzSH08BOSi8AzWx7DgWJ|!tg%>M z`P$O>Ti@A@-S3(cD$Y_~(mDB#^OBRwcaEJ_r;wsZ%?gAbW+PbnPWO_$%XgVSs*328 z6A8F93(^cpvnS20H0#ogOtUr3r}*DJ(kAQ-Q<$OT6#$|?WxzSM!wVhV`=OYY~C*V^Jrt0^?P{f9VpC& zel<;BzM5_XG^#Rq$941PLqDR$OE`hCjX(d~828go^Hqgz=}t&<{hgESRoF3JS_1NQ z_dD$svA0cfA#9A>sb$2jH))};!@T6?^0oJE8!`&RC$3ZLUpP+WGvYFB({g{*wM}#P zgnKacz3DW+SA3j{mtqf?M&2G^-Y2YKlt$xEI%#ESV=9S*?OWn(O;c{x9d32Z8l8Zk zcLHz8BCOt&PO#qA`&kUT`*Ot6#Fsuce)%P*bt$FcAd`a0x3)-c?+n+N6snX(-C7;d z^LB1o#q>_j4}3bzqE-@N0~-|p0@SU_*0BckPK@bMreDNG>iJS zx55LObJAB!Uo)+-&)wJfN)hUhtw}OLy}Q}N}=1G{UX@)2~09#$DS>=B;-Q_tF}W?*v~j^40dz z43h6+fB&SI1D&q7HBI-urX8>6Ul(b^ix*|u@Mrk$=;8FI7oAu!=4!ILni|#{ybcQrUZ=a!q)~aTjdf?~rRe!tUMJHD6;~cz z7`3dmMx+xdbk$B<^B~tS{rXp?J)yT-`8U!Yy@QvAyRd8A)HJXC*^e2&atb4a=sVX> z-4gTF$F$q^##QdCG>uD5UqINQUYbtw_4S!f`xZAQ?c1MpTJO@+#%@gdwM$*EG?pQ* zZkp0{rWc}ct^F9pY4S~To0`XWuW*~vR`#O%__>HpDbiMN>a5Oj zu7<)i@E=TzZ+3?w%BTGBVOx{bbDcgE%ji_NRP=Fb+}QZeb<6ZhvN}J=$nr4^?iVy%qCHX`4tBrD@R^@a$aA+MV&=Wxi8d^8?-NH)G1% z_)ckEJmqGmEu)T?E|=2X*-M99C8D#WwoBqWSGzXpI}f?VMej&oM(;HBIYfwlBa zh^Yj357N%#I7B)b3c0s$mHKM=ABMN;?-g=^hH}??)BJ z(PO@yi3=PI&r^uYy!1=tJJ4SS^(a*u&z$F_Mfq@f90#_ABzsda7L^O+tUqCbKcLi4Tbga(m3A_pGJbm*l9ho{=W_#kRQmabZfHN zi1d4EGbgD7x7v1IQano77v88^(irLt8*^(VU*BTiZ(8rI`~zu9y16sbmb%GFBTeCs z_tKq|uaDPiynM}^uVZW!W#L_Q)3PM(@DEZ zSbaY&^&Re~Q3930`my?~QT-X{{r%mzReF}{6Et#2ZN5L~Mb64Sk##T9TIaD?i!I%~k4N8;?n&~w6VEEM zC#e@a`U1-9G1mN_WW9eRW9!%5BtkF{wDK*)Dw<9p?280LcB?N#T5%=PiHCScd+LTE z4bVGd-CEvKJwA*q;3d}nb?tzll+r_Lu%1;F`Chhtemmf+VC7rcWck@&6dn4T*4A|B8 zbx6~mal1#Dzdv2HPluC*ca3jUaG#J9eeEHhKjZF!sPwf$r}G{}k>^^8^r93LN8A&$ zpi&Gq+@gu+Lsd2h` zbCFYvR9yAln6$R~I_>pQtty_XeC0ZKy*n~p1M1?Yr@0NH(v5ZXy6yN=Nhy?e#BEAT=W_U8Ow$dv>rZKxwdtA&@|7yl z@cmGn^6_yxkLc=Zru{<&+jFGZNo7lY2f{uC{b-M%V!E2z&2|YEV!JwwNJX=is)Ut0!#BX(R+Q~lSPfQWET;-9Px{JOXm(27#=ASF5++8^aQmWrvk z%$28{vTw&SXvnwkcR!_N)!j8%5@YP_ny0O8i0%ASKGmx3SG+4@d8y>J*H0;E;#Q`0 z^s*}q@p~8B&b?SdN3nD7`l6kB*A?x@JIbBfI`$%ptwK?GiOZ@^Do{S+-0}N;`To;I zd-jfTtspUdeHiXPfB$Edfa=Blp`VZa|F2bf@&`Qj{|9S`@&|kmYY(f-KnI1fzZE}Y z^&~kE&jHt3Jk}43-udHvdhB|$=ADICTX${>C#2z+vmgK8o(o^o@LfV>!W*83uWfj=19d?zyw>0~S}#e_7@mf2V0au)9ZUGE z+zvN<2s2mY&cf61EetOt81n@1EWfP{pWqi0zu{^4;|z~>*~;d^pJaGL-;3~ur{PaC zJdVqJF8rB>5Af3SF+2_5-tags`zKt2gc&`YYj|1Hr#Cze-^K8_uT0~x??3luZ$^IE zsnR$~zmf@MwrH}k)iAJx^%dbIS0f|D?Srd{%ff7nXXCg2C^&cJ8{6LNyNXZtBG&M` z?R`ESdB4K-?it5;C-D7k@5@lIdVVp_ZpP`H?mxryp1!U0%_GcbxIe?Pbnp0uchzv) z-Yft4^!#`45qFvG3TlwGnR-FiE5z{!x~iN~cv$!iTG)~MC7ihS725B=yDRvpfasu;VG*=h|v*{=NcRm|g7WmnIJ@M;% zi)~)`o$C$H5ie-$kMPZG&Hp%NO=mK1(%h(r@9AYZLTk$1N#pg*7s~mnem{Qza;Rbc zY5$x*I2^{g&_{<#%;}Y$hS;F`p0DWpi`mhsSDXHeK;f&c^IdyV4>Deum z4Ru02Qgl}67cQhM_DZVQRW;c!sh)I8Q>O1f=h~HyBcJj-tCp;q8P}`t3=f8f!lU7_ zFqE^aM}}88d2CLY7Z!)*TwimNb>PJ65=qIV^MSE8j`nkIh`y0w8DG%IV;J#|L_O8Ak!by)XV{O-^&H`^0PYbfepfsH1w)j{1uKcasns8|KSOS+&dsV#p5tUaJ_l%!F3KsZhtY1uP-}UdeC=naR<<@j}Ef-pm$#6{Lb^!Qu{P)NmH|DQkizOUm{Bo;t!|8GdlY{ z!`{?U=g@^Zil@T&6?d^p;h>Ceax52 zPHp%y=t?bA=b0a6l%_hrER0QQ>MY}-^{d77%KC=loox0gl!I7ngM8=k6s4~e&GgMu zi9&^BGP47f;+dg0Ilm&DTf}3k!edHb7xVmu@4@IQQjDx2He*iT1wF=3X_NW}b4gZ* zo56oIs#0xLb4|h-;WElVca2#^ZRH>2PA#Mf+*|9aNBN_GGe5p}}%U7S{~bG>7W8tz5WQ~Y#4)6b%HshmaKi|i@`uP#Ts-aFKWV>b#-tj5j= z?X8`Qh9VV-j=F~))Jku7djB9hAEL|AZh3g;_gLCb)0@1^>em7EgnG6&;d|`kt#o8l zDe}>7ZU~&REd23So~fVJ-0)t|5!NG$d#c=f_~%l3|028t9jK10d5y#6Lo7(0?0~0= z8_0Rb50lH0%d0%~sj*E5 z=yd`dz6`W>2G^OPe3DtN9A{Qo#DUAxcUJecp~P09=1;NSI)|^*(p}`|RgBbip#QzJ_R^Mh=@~!fpr>%AbB{|h4yTz36Nlq%kH)U~2E4h8(TpzJceGZxw57I&! z!LQoGKYN7BL#0GEm{d~txP2Jg$O@Bk-<=WI6^yzbfEq>V&$&lKO-e@f`vJY(Drl;N zzY|3C-Nd-9IPQu5B;&f`ydBJ9QwyG}ZB(V7-;dNE4i}Q$Drt^%A#E2^p8Xjo+(`-+ znsu6wiGL+~CfBfHFPVN>^hYW(9^8i(HONL`I{o_zKg7;{9qwQBtNa&!J!8;K^dY;@ z-VX|OLsL$6y@%^j##YmUR(6!H{_ygO#PJ)gNBJ5SMu*>uO8&`|u2+Z3l)LKcEAl3~ z)VNqPUX?p1#->#NyL;4kPr36@zkk{yN5lUsCY320#qd7kmhZ8{^D`XopMPjinprEp zDd7>QSH2H}w6f*v7&OmPy;P*E%K8h5O?Qpqz^(i+My?9gG@J!4-*;vwPC2@m@!$@m zUVd@*VQC#5&X`9dZuN0pd^>s#wb9Fi?1I)LtY&eV`H9X8xnh0o2gOT=rV({Dglm{_ zHfdutq&5cltDyH)T%S|!2XUQ2soxoPN09;y|*UP+$5(K zC!2EOLrsy@to7e`ot->5JP}@?Cw(_83e^+I*3|>`fKCQ7Qoq&JqJ|HHpSKQM;jt>2 zhKz>Z4!ftNzLXMI-y+^pk`#!K-T?P`iLgx!z ziFq)L)qFwH!8lTH1(&#r7W@otcNFQ2J=!9uD7N7r zpn)y^H&l8Kf=iykNUv+C#y5B78}IW~Nx8pqSKG+oW`7H=kJCKZiC?vs>Hd-6RQNy-YFa&^7ik2zk0Fl1$<+_hol0kNgtYb&sj z6BKY7;a(>t-xFHnp%ZCUPx~+ZxBhqDZAr^|h`HTdW^@baU;fEBel}w?wS+F=wv^9& zCc^qJ{^?DcWzNoLT>pC>#kD`;asBVuL5AD2MhR52GmuIJs(NEtpNJPl8E2;rh$a^Y2i8aG~@;c56vhDZ5d zcmutSFTDHVLOAj9acxHTXW{fr>pcI{`z$`KLFN4o`F=)2eXL(vK)ctqmU`CpqmzE2 zs;v()gdrrDQg-u;ESXPqghH>sO!DT(fW3CPHiQ4buz1N_{v5ZVLGg*XGel@lWG8=uT zyH|gU{I)kS4se>;iQ{G89A6)rib(wddtBcyIFTxk_FqWa!BrHLn8zcq2C zN7ne%cPhp6)08TmE2t$=R!pgAt96Jady%V78H)l`2Hn!~KFQjyO8zjDl~%>qhZ@!S z#r@ft_6WPmhS)sy^^_VGq^0tK*$U7%SMklPbT(S~WOKzvMfMPTlS9RN34PTgX-y76 z*ZFcPPoXh4Q^__T+()qI6o|;$L_9nIgMV1inv2`l-ny(kt z-G`KgO5%H`{!e-D%h#njCT^shiM~bIYx({rN^7&Lu_V$}wDozPYy4ik&+4WzC0ByC z@1?e$f^No}%|#*a63dsw@sr6$mDUf6WTD5ITr;NnPC{n#%r6No?XeiT!`IRGnd?wF z=60Ctkk;A~^6{U)|CGT0nu zTzHMAqyyg_{MuajGKTNSoC_Myg{R}-uZ!)oH$3gP87A@<pU-|#q&=$H#%!|?aqN`4Jb!yjyT97i0J3$H#0OG0K3 z#_%+J1H)5$ii|C^uTXkNS1{JtVH_i4el_O!v@qi5(#Pvw;q-j+%6PBmPjCG5XWNGT zclY_Y{;Pha<#E#bY%aZdOy#Tb%`yCTLz_p}bzIlmHHdkYuG{F}qSbA!-9_Fin-Q`P zaR}=ix|*6Kr#ZT=x{CJ{orxw~dsZ4cGOk}>*Fx5+7TdMNFYznw`pkdESpRco?O)op z&ad;`--m=|}rlnUPKLGdMt1gMCzc0_^elZR0&lkPrvRN zb3f9reqB4Cir;Erxs_H8<5x6}bn?s5=$;&p=JH9Vv#GOSyHY=Bk)jcKp^Y0PPaBA& z={j2pzQsye>pO#OY^O1eN<@oVuiyP}J6&V44<3@0&wbPOQdFXHj zZFn8D_oLtW+}K_RFeksAIl1~8QDZH?KR)F^6rR!U4CehGLg8oL5=UtR{6JRt@8we8 zrLXn3{Ey;Jl=?ipLo(Yz`0LUFb!iWqu4c4GU9D)5*^V^r+Yyy!m!z7yE~KUEl4W~c zw?LVa66=y~ny%NFVN9g8Mk$XZJyW2{Y4AT;X>3$PF|s+SJ(fqME1-FbX_si2c8!Be>5@c4*HlVLSEP^+DKV9qDCAnE(JxXK;zxhiLjQX$ z^Y7Z@pS8xnZ;Su6mMC8MKWl|?r|RFeKXJ+bS=$?%(%lQx3&s4OzyFlL|Hcw968w|# z|FqrVjfHXiFV4?slodzzI^W70DXtY6H2K4;;MeBD$9X~1pmWf><#4UWvsR6c3%kE_ zc$^o^^MWeyU$&26cMCt6ehvF)Ih>&;f0K}iv1h+5FQw_x`U#WnYxt>VU;KXX?7P(s7x!h1p2MZpBdKL8@Jn-eO~Y%!ZP=@si@&zvWf>DtZaF;8 z7bcpX-wrvvo`u({6gtLt^HQ3xMuy+WC!Y71w$m*Xh5hXvfS$|vNeoMLaX;>e4 z5*pO_p*d2XK0#79>H>M`$v?-{sGjV^>EoXzlv<2JT@(g}leyQZW&z`!t*qiBzTzqo zUe3jKNf^(aMpSp0*4DHzE4&lb2K9x>CQZ|<`ao)1b^>28ZG?B(t#%u7vB_LhO>a)2 zm-V>s(qBB|wtBTcw)=kw$4`alA1|yfsj5v1*`y&;(KZvfC$67Z2uvWp{ zQOxBf?^0Y3`pH<|YOWr>p(gh5<$YUfqZ9RUAyWF~j7UE5Yy4X3ra6+2SUY!z$HR~?8Z8*jLJlH7 zWwASPDl?sVzADn$J1C23L0m^^tr{dpQ(7mHR>{YN4))8^T5aW?2-&)z(wD9LHNjHW z+pfKTo|Ql4?r`Q4^H_cBKw9G1v$dU`q#7HB{)KGWPImj6_M4;`RYP@BGAukpZ9W%9 zg_lUx4C-_ia+~=NGVBg@LNh~Ut--q)Ce_0 zts+gfwRU2XaySaTTX7%du&mdJoqx#B>N1k+!+i7|zOq0w681=NKe$Ima zB_X-k#@R106Pd^SW)1CNaQFw5@dUIn3{A69l=r+K`Jrg}Msk0r%g$VC4*g|kE^YBg zkRH|8yTr2$id#hs-6`yfwEs?Gd4+aGJf^Xya$V{x7sUGi&{re%YUeMyQ$q(dd(^Y& zYp43>sAu&{s+AXr?*VeBC*lYfgzT)QuEk4syWN=O`DZyris#nlz!K-zR?&F`FTp&(`QsS#o3Nkh})6TZ$`)E z*lBJ}W@4A9(3E%+)v1YizbYqlA_J(V>l%kV^k-%Dxk&-xJNK%YsG>%EgVY6}eNM7ch5@ zE&O+1B1rG>7SNissD`wt7B;VKMXQ$9jApdGOl#(5>P+i$Pt(ek;uqn4Q{Y0gOvdyc zsb7|+ea(C{OrBATjcH56y|(yW z(_U2ns=4@`td0yu_w2!xLY`x%qY5&WoCp^=$&=ffI1vv+IqWjcZjVCwkzTRqAQhb~$1-${Gi7Ou1Jk(Ox%f1&9DD!;3IwxjVLe>OZ>d0Ak+|DzovKWtCyOW2jH z&?nh=S}B<5YM4Ht!ruecuJuR4F%)L0zhQm!J?f=xr!>}t#&4!xZiNfJP()>FCqMbS zg7y-s#2Zql>g}|`bBWci+QZPGxq!aY3VOXrpZL$;e@fv0@)9sI{*(EC`_?@910MB% zmce!Yh{yeZ=T$e{^n&7g!En}q(bLW0>MM~T!VA$~uT~sB$_v}j!_xcS#(#xz-9a2o zkLv=3M9bc>T==RA?s~Fzn&N5r z>V`+T<3qW0)--&BJDl)_r{Uv#KFS>j<>Iez;m6QRvbroh4d2-CsPA-HF8mROU%>1L zOCP!LEe$W;52yL{%44wy*)7hupZpFxmblu?lQplNiGMlnE8NAmV|SL{Y);?xI`0nM z1V?2Kt$DKSAAH#+&abn1v*y~Ff7|#@6cc1L!-2lS=6|VFstiJOS;kLa$ zn{?_~mfmge$51Ak(TgA7?XF@JcMCmW;OXod?PfO>C_q*ORVlULzA4Hugy|RUPl@~?mt`hPtBm;~KAS<`hS7Od7 z-zwX^rZ#gCdAb)(lh?`Fe02S_TTQfHk+z|csw{&aNggjCXVL~zF0xcB&BbZ0&iGc} zoa5fp+`4R(kn|}(vCHA({U8O2IqoJ^!;!4b zWNbK>l&QQJOi(`k(ipEo8uboRhNaYxPDfV#%wV@*Rhp#o*wU>+2Txzr__DEA>la?v zEEC9gaBeqPSyHDpM9Hh9%_WQds=W=2IomLT?CYgdqu3TwO(lJ1lS%TZNCjt;<|VwZ(0jB0OjlKD(fjQ5;qk}@FJS|((sHQ3DXp8!ddpVugHDUzHR{GgIZ@ zeMUbEeYR69zW*+kq4Z37AK_}99Ls8i@kW*GKEy44EB{@{&$5~3gKiwHTshe6z7=1= zcQ9W&G0lVIclw&jX$W#@eM6rblR!)dK;OfT*~{#(RKoUXJupwnrK?~=QPHhWlHKfbktUz}3+GRCFH`J%z7YDe& z&8J=HeO9ShaTQ*I_IIV%o-pC{(5sJlq@(xKlB;hv7uEY7hn{gvOFMR0^d^;BEf62O z+CNC_s>_MYgWo6r@-Ia?o}GYviKX^Y*SB0oS+%|9MhbVMn^iO~deLV#R%W|T;XE28 z9861-6{n~8hVI{jZ~f+W^OMPkQto_}l*(^$*QUP5tZ$6D7tebiidN}vB6Q@<+C5eS z+2=4U( z*K&B+I-gm9xzuNNUsky1wn_Z~DaFsS6P#5JqO6Ty$J*FzmSDs?s*wLSNO-kw{7R)* zLuxVCyF=1)7Dqj%sFi9SIh~fe$ZH3cVu;)$a*k{t71D30LsV3;sQqo&oH z=DG4LxUe{j+O4?DA}dAlm~4hN#Vpb2yOHm9nGs8E@xaAWA&a-3j+q{!b(R`Q&H;dIO*NJ88+HT-C(hP_x!uwF6YVakCRI zIFq*YEFrTUQN!sgm)edfQN~5|>h(hWMxm{KRs&Vd6~fQ8X4ZsW@@V>#GqGYPJ?krs zt43X*JE7z;Y40+VUVSd3Uuk~K#roq?%HRt-Xc&EZux8rQ}0 zsY_pb0c9|TUUmby&txAX{qFD!mGJS5`6Tg>{~ULnS)bA@UwTz{JFM@dZGI}N4eMB2 z+DdI`)HRhd)LQmR`i-^d&0I}QXKgM^XVa7?{lj(x>FIs&m6xE84~T0fBMp^Mb+6fu z@)FBofZ5g*&uT&4>N?VC50J`GwcO50zM=4uL-h2St<8tshP3rQ>MlqrubuTMS9?i1 z^tA;p+aJ`TOO2UPygpyN3!X?>4ke=_q9uH@CArLW)hLwkhTL z$GYb!t=Ph=P^Z$!-U?r846i&fr6W<&g-{Z&{^#$1sRT~b+kcqn$9Zo!Y+JmLV9veP zVq3hbs|vLGm~HVmKR>g7Jk;gU-5F{C}%5z67p549~_(|`9le1zdY zv3AU!MP|rZxs5dZM^=^5<<8+R8vcX-0sQS8KHBi_(Mp8ga`;%oHz5^3FCd3cH2gbu zHPUP6@TrDxM86YVjvPMIaLG)tTbje?8NR`906#2;vlPMKx3-R^nSYkAWrlxEU&!b) zhks(Yw5`#{Va$+)|IBddV#6PDI9(loYw1;)sps&o4PQgg;@pHB{+;1pFq%PMC5QiH z_-elz{DB<)tKrfFXV+y8|J`tnKpDm6@W8#+TUPOVZVoSH_)10q^xQdoJHtOkUc*X7 z4&TA>6^uNQp6BqL4gZ9(1Ty&?zPsTc`H%SBT&cL+Dj2?;K9zFI;Z+P@h8%`oK8NpX zxaKH~T5@=G!{0~$7rM&f2O7SV5h8pnhu1XxJ;pukM$h534VRuhcAs)M+miT`7QS4A+Pnt3o-vmErF)17Lqa4rhA9-+V?{j6ZTX0|@?P z;{hw`%;_^aJk@Y%yOP=*ex~7b{2YF#=5Vb;(<3l%aqiU|ey-u~FurzfSPnnW@L7z! z;r}_jhv75O$L1Ww9Nx$9w^f$^_+bAW zzhCF@n+?B%xflIi4!_gzn~+M!Co=V`F6oWq|rTsFZOL+0=o4DXD_JA6KezifDW--+L@Iee_)r?El-AIsry8Gao4 zD9$y{;ZqDhk~2!^A9J|MUDhR9@>?;7t6pi*j5HbJ=5Wy&Jq7C&aK*ig%YU)qd$V>& zIp=V-L&hPYUf6@T%Z?E7>0o`%PBwXe_NYO-3PeV_j?%;6duXl-mM z|98yc`x-9I3C49fe1F5W!x)krfEVWQ^9_&Z(C?VTyBi+Qt>0R{xSV?#uD$aP8!g3nU&AG* zp%vut3k}yUbb7ZOeu?34up&?S=kNiBk7Iqyxgj}xpy6X#5kc26hhJ;>YwSWn9+|^$ zFkEXWjEi#kEr!3!9xC`r4!^_jSJ+*}Dn$;z$MATr^QJwD^LwA+@f_&YIs8Gx>G0U^M=RsvoFfw zqYT&HMNW{(;V&D08+)4==j8C$4A-7V#yQ}w(B#>rUN>C3BhjoZ#!Cw?aIz7T;&J|! z;xYVb%yXO9k(N#%;Dn=kLQc;RGgktZyGMU z@z_}vbByUPEqn)7U9ZaUG@TKT;a^JQ=LDcKZ&~6B`el?vTT07~Tt=B=#BP@R^49 zMq8Tkc@Cdtcpo%C;kP;b9mD&gdxbru96sCde(aw_R$iR1QgaMH&z%RpjPTU2?7wU$ zd{1PS!pEoKr?{~FT*GA%9xdY>KF{z@t`qps96sOhbCKOKFU#TY8r~6^DCb?~@CAmS z!|E*KnH;{*@D7{-z&JU@NoT1=hD%qEc}Fo`T6kr2w$9A(w7w!9!>>!r=N;;6v4xN4 zi2s(umlz(;9p8PwNN)=Np5dpV1&v+n9KO`>(^+R`RGGuyH(Y0E(eLN*4-9W>``6DX zPJgLohHL*J^UY$swD1GaXE;8?(|kodhCe@ze-Y_iZsFs3;g{#|4-IebniJ2RIs7BT zwGNF%j2!;4;oASmJSc~MVt72id{z!$VR&mh+wbEX{;A8t~$*&F9ZcgM zzXpCL{kTix|Hbg3ZYb$LDTi-0e25zYesm6Z2mxxMpYjy=K{>pH;SZx@gN!hTZ)dnp zv|ybrmYbdLzP;g5&+K2%D*wCl$^ZIH^1$dWxM{fQxVLdLa5HgpaC33) zA6N8lBTn`b3c$+Y_D!F)=eIBJa-8(BviB86@7zy3+u|d5Rx0DftOdbWkQva|@_@F)1rYrU(A`@8V@II{2N) zvUF$Z`z!p<(y=Y3y2>!sN4_k#z2Cs|EI;{g-a)z^#2rn8`^SOL!hL|#J8N^{{_a_A z79RfUn^`&<5=Qlw9zf{SdcS_bl+}z6`(l`^wyB zVdYnxhwz)>+xj#5Z>9He*?k}UM(;UHZ9;KpVY2&toU$*Y`^#|(dmq2LAC7wq_byI& zhVA-|cNOaUqHqdR7c8F^+0V(^p5p7u^UHB6JC)}(8<;zPQ=G0_c=iA;!&Og{a`$Qj zdiN7BrA6)aAQsw>!2J%UXJxxGMKer6H>qq|Lrd;e!)#mh20 zdvDwOEFR-~={Ht?k8U*I$in91kzX{cv^-6ivAFDh+i-vN{$JtZt+&9>RQ@WrrNq4k z_jmYs!ixuhh z6(;j5Oe^A2oo3-RK6#dVg*$;~opHL)p6gp(S;YHy_t|&y;l=a%6W`T1JC9hdWDG0*ID+TnaQf~Xelt2$e0l#dgJO9q{%xP-!)5;Wct5LykMZk0Iqe6K+X(hf zoj*FIN3JBoggtNv;cC6ic{cFi2l>97u%@_^ai=dsLm0Qi6m$@9yWq;;I`b_1UiK@R z7R~hMnSSMxN$hd!8spbD;7{DVnc56t`X^Nf}5E0mmu5;xNbLLmw;b|KQo8v{^G%C zd=aJQ!Azw%t#OYq>+Pt?c_g-ekHa?9Y3K`Tt%hLkJey_7_mpeYJ?dIbH~UVKB9sDO z!t7)@vyzXTEI>)7r_k4WN!8_B&a0dOM@eK}s^vBwoJ*;|TN zb;?a;csW|KI;)4NE-BC{5)U&Yx14&NvQq$l8j_#)+ysB@w5X-`wO>^j!zAHY^ez<7 zw`grhM?!CY;ro@36eYdp0jX~FDW(sK)q=D%_V&$Z#Zb?(vsdz5y?cq5#*cgjp(hlw z2fi(w+!WA^E#}UNhC| zB9{Gh^jbC+S@_mY&Y#iXxr8!l7Du z1^LT3FZrqN*1A%ZfV2`6>Rm5=4Eac9TKa4Kh+N*Z%X&MWM;nLt8f`_Y(h9pu_hQ>$ zEoz-j`_Y*_(lVERv~2E4Z&w-{(&kv^B?Z#g-Y~hje4V{Cx8y4kPAF<`lRt`-($bk6 zg{RuXtu+^ge9&z0sx%~ztX`^`)GDBl8;_saLFd-KN_yV`W9 z=b40G&yVxcS(0yqpPsgmLu^0IRChzSJXbt&TKv)t=m0h*9(_p^BN?;aU*WEVT7`mt07`J8XqbzVTkVCi2ZQE$@=}qUAx^5?tl#(YnOLw6j2a z=3jDOr@8G7HAudwcc++STfR?BXG6X_yjJVv8|$UFBHvuo1uw)WIaqc{Ru*?NWc*Ij zws=J@_OL6asYp+iNta?Q8|o!d3t1AwQgB(x(DdXU)7p~nOWzSpbRTVYN{TJ~Jy&N+ zEL%zE_e%MQxR>Ob3h|an!u6&oY?#R)O8IsQlB|jQ z-DWxD^whK@W_ju8Da>ZSdr`YJeF(5k@G9xX>G^klVx`!}ZOgrtfq-t3!J ziKR5kHHCAi9-GrPK1a9g3xD(Vk(YdgCfnC&#chD&h>!hfoJNvneSMlU-2r@}2kBV| z{lYX7<@?poJSFz^vQsJBaq6>g=4+*RdIpv)2h#&Bp{$o89f|yDr7u~OV@Z}3&jeSS zw2F!)!5m!}6FUn!sC0!(vWV6PLt8q zpyVzH&=o%0O}NTGJtNZG7T3_(|nD*sfG6cOh)U<%4UA>asTqXQY?2W19_xA_0tL6yb z8ZG{|Sfn@~>n#^zk%pbH*kibxUEz;$_RiDn!IG|@;+bzd0>u+#Em#yM3oxR;t+aaQ zXCsKyP7QI~$aBSYUvh0y{`3NTSn@&TOZH8qBcXW5My(EflWB+2Tok&Sq2RK8EWbi(D^F-Xf?zFmUs z=*l{EpOvE{*7R@v>j5YY#4+MyabZ*M8KKt^>FZwoBGPq6Hw?)0Bl~UCpqL*V-O!6C>dNkoUUv2N7eE8LF5>DBHR#eFYFRhE%C4)K zn_O)+a0i(k+#8su=*%`9qOUnJY!hc`}4N9=JY^+IH!i_`uj7wJSg~6wOgKdudTBFRRQR`1$zyyFqCWSUkd82_Id_ ztn4?EU4l88EDvb6q0W#MSC!pwouYUo?NK{MqGco&L^ zD;3hLQl4*juTZ%;qfrqgDhFAwdzzTS!Rs+5>4#DzMY+d>G|<|5N^{$6A`itW} zwJfzI&C6tsd7YR3jN<8NI|t|9*@ktqRSz z>#@_eIdkV&ce3vwt<8meQP*m_v>!ulBVL=Q7MH^nC&&G|T00u$rRk|O&NAH|`8v4G zd`JCwKibzLrkkoe(a18>XjZ5VZZqYrveg`UbJ|NU_bsV;h1RTkqi+chD9K$MmB$ge zbnEn~q`oBHy=W3q!GrZ>{sv+E5I&(BhCrw!&;FvV!ro zUx0)|p|7;L*P1k)S`E{_8-@O-Z{ipJkZ*|Xi;!<2am6=2@Y3-R_LkSK zA^Cpr(yNm1T+>*VZ<*6xIQiHC$2>tTL^`tU@8#0I4Lw`pe`SoSXPV!L<81cQwNS2h z$1eFZ;t8U%h$wfH;Q~qz4klFx5;Y!OJ=@~Z{@1Tnx5djN-+*r&0LLke_PoR~>CLf- z%SzMD7T)ZIiKA%ksS)_l9KMU;lNg6!i!m2ZS;HS;*Ewq-x$sQ>`5TDMMfR!XaE;NB zreKu_dt|wI_A$H-GZtplx$yfNelTYTz$OmRF%8Ga15A{guLPa#Ep!h+U@H^Am2 zoD@8y>5<+feRx4@hOmam@Lu||>f7t_|JxS6xt0H^hSxQH#W6X&t>IDsa6}G2+weMA zd}Ng)ho573ZTd{~nR9q&!=v8gfE=#zE<0SfDE}PZ-SC>|6f$bc;nI^v!;lNfQV#EH zcn!1p(lCdsyktA~K>j}%+{N-Kb+O?Gpi@gHSonYGF4#pQpa;Tw~L@P?=1Z! z;Ne{Omki$!S`psxH2fIDqg}k)a^c4rK8{ntS?v{`hJVZOXcsTDS2v8nk_XS(g#7(! z?>^=?d$wVscUx)Hzwx`>q^KWt_T;D=Alq?g(y-U?n-4F0cV$j2j^pHe<9q)OdwTOYuj-DtJrh7Zz?X`OP56SqO^VR z`M=()E_B|2>MQS8-3ixws<#=$_3tpnpAVyZ^&7L8cJbq`O&0DO*Ww4VA9oTna^rM7 zsR6&ef<;w@3Q40Ru2B6Prupa_o`~1b1UV0VOtLXK=(ji{JJFpS4k{bNlr89uk7nm~ zE7-GO{oH8p_N_1*d&Wz`(y)msa6z(rQl4|(3zNf0lfq68KZi?qkJfL>V&AL{w!+S1 z4e!#uV@hS+NwTIV{t@GT$sdqB@mT!gEne0N70Z`cZtk{goRb{G+_F18-3{)f$78s& z5}m907L)a324Znvn$ze(Tq;b+TX`7sroR?gcT${{I z-cOb#A0|H}TkP7Jlq;xGP*~8Yph>|pTDj6tI%!^@5r)cek{9n(dF|r{CHEy&ku55W z)&a!D^rSK}iw&V2eYD=F5$2@jSfxOGS|RpzZzMC5`^v|4>9NVX$xq3B%O&fG)+ehH zyH2RD#)|Q|t`lc$_2#6gYj}P$d%2#$#`s9AY)`QDxP`0@Xa+3H>)(6w)EhVTnz{hO7x%KEhAtfWKIG3k_a zOL`{YiRJLz{W~rK2s)ttZt)5-I zzD5KZMbxp;no9UQfB0>&c6LZ6plzd>eZzvr1xFM#FKAhCRDtX}YEJucC`d{q4XFRC zph3O4*lQ%RJ-*sbd6*a8xAgA>Emuv-QiG~nt%+-vs}ZNZtFev7J-@q~j*Bg8ftyhx z&h149RYSigWBff$$3kE7p&xo*k~_?$wihIep}3!tpP{*aKST;zmAC>~ZdB;Wp&rZv(4@?UMG%~h&F;oRv@#{uzqiwV+bLgDHid%$XOm@u-?pN~Y4*IwU$d zP9?rFv`jLMl){3^KSmkiY`DV;uFtqWPrf()@Ea8BF)$>HjV*XH_SL5XHWFY;)AmdMu(1xB& zh9pCczMiu@zLd-1yYw0xX}?>OvesLac_`Ay3w|*ChXK5sX&kU|QYP(t4@(xMl;9KD zzE|z8cUNLOL!l+Bn!?q1Wu$*eDhvFZv}=WXE|E5jxYTUpl;V}*mb=3x_b-^9_L&P^ zLfMK|zNRM6u)69B*L{by>WxjL_P0b<6jj>loij`K!;%-Tk16700cev7DJoOZ&g0k$ znLs%z&ij&wD3@Vy?b#OVM(RWydv|ztO*n<(?&P|LK542dh8OV(y@A$WGv$jbk{457 z1z(fas^`bBqxHp>F~=d){3-fICw(XbmBKEGW{2|CviYxC_%-x{>#+mbh`y{heU|#E z%0bo?Raz&Y`*Tv*ks5B7JOa=CIXSW*v!^)BFAwJ>Po}(WMySj?gLtPZ^IV;z%J_%T zMmtuW&qbcoEA4xwIXDciG7_FJhc9o1yXZ`tO2|`WYqbr0=2S*G-AK(q_|0vm#W;la zHUgR9B=}F%*m{q2t~Z&u)}DW+?a9uS{=^eyL#zy1a=RfBDHryl$5o%JQy&h7N9d{> z8idy2cuqGuDV!Wm4QGY+p%eY_n{fK6Q2#<3mwgmI4XeU;)buX_#k(X)c7gsY*eFdr zqCPa=G-;6>2{*qQihnMtN3YqWAUmyQsDF}CrMTTBbk62@twyN)huOM^d~NerWj)|G+WwHoa zA2iy7lvU`eDd$wpU&D1BIn?<~I-~t9>bNX*T+XE0wW;AYNXyRSI-eRIh?Sd1xODQ! zRHTIKz0SHB$Mq(??FP#J6w0<bLqjVfX!Fs!D~4q*Qs5>~Lb*vyb-sC$c*! z>OK|vI|qH<+mLr0$oVAgL+8*VTurG+_B7r`Tr0vxMq6c)=E;@R;kC(4NmKR!io**3 zJv2_5B{ON=D*5Bw^+*$r4L9E(Yc+Dkk~DYq9+K152~BC^Lz32C#@6>_vNi2n;+eW9 zgwr|eY{Tb~hc|#$nie!GXi<>ap^Tcf7qB{_a4afe1X3*krtEE zpf#u3ZVYeg?swmn{$~?(t#BGCeGH9gj1l|Irx-zN;U&9<`0zI5#j#39p#$AywPh)n` zhIX@oRG&thxsM)Zqs;`g_No-O@eh$sQT|(`NTZW#NzLS7Moje?p@@^6NnI-U;@id% z)6zcKpO}`z$-Zgu3H!?BtJ63)3W~Y zX5BmL-CS$hGt-z0F7w|9jhJ-RNPb)&W8RToNhcsFzTZuo#V7%a{*m9xwQuRVeu^Z^AC z9qwP@xzI^yO20zO5oM@F4X52)l=2PDB_(T6JgtMYK~xIjlDD~w;5ouK_z&TqifI?P z*P|()nuLDH>>`WWw=#3kN{H4!K2Q6alF1PT!_(exmcJ-Xqh_|l;ZGE?dy0{`Y=~!L z@aa(Xuc3j>m(OP$s&SS^Zoegk1)rzA(eNC*5WT36AtAXkvveu>z1ZmskbZlQ>2jxm?1oHZH((@KZ(|(Xy&{{_ zL;SRGPBJNNRWBtvYwrNUU1zHwIwS6G=59L6V5ED2{#p8q(iE(M{pUt#w8)~{>GaOh z(7c8b*d6rGSxGa4VvMNK*N46oHL0|ZFKXYCdMHJa6Vxlv*&r&>8et`KlUrEJQYlm_ zxR;!XH*{eQ=36usD`WfZ6xLFDVXIE3cj+|4eQagq-qo?D+9kK7RMu1<_`m*7<>3BA z{x`KAPyT>M`QMrvP-$V5i>Oy|u6MWvXnqdYh#gCFp(FUn9IiDW>DHr$?(Waw;!tY+ z-}1>TaySizzj-GAJ3EKN0{MFdThi=V&EZuIf0z+ED|b1(n&E?>A7=Fj7N=jdDBaio z;LC62zcgR93_spY;rB+uhZH}=@cY9Kkdoo4+~P39n_v?izLX2!(C}Tu(R|YIH2mR) zH#WN@C*;C6H+(*tyo{8Er{P-}p4uhJg+JQxBkY{wBXZ%7H9X1%Yvsb9WVlXTWagC% zf4brIZ7f|b7rve0-@6?NZ+M!X4u(fL;ts{(3(he-$`M!4O49Ny)zR=MH(FSXmlnPQ zfmE)B$M{n`hHrCFFuO2&V<+WY3m@fSopX36!=qg7svO?g@F<^qAcvo4c$D+Kki)we z9_515a`^d%M>*p19NyLND0loehj%kP$}bCQ7N@_v;ZY8{R}SxCc$Ayg%Hcf?k8;-O z#pPDAm*G)PHa3U%Hawma^?VNRV|WESg)?A3Us{WSwe7&@GEn}3@m-)V39JMK)~W;J zjzCW&xyV{%8rti;gGGv_9whB z$$W&j;eHsu!t~Cz*k6Uydo_qddpSGczZj?QN^YZc-Ne0Qdy>Nme;^ko^Gg;czrOo4 zzuVp`O*7Kx%lZAY`z$U!%ly9*HcP`#j1y$@;m@#omqyv0!b*0x?Y;Kc?(kz;p5(#G zmEUatZ6VKWzikzMQ@8R{M#g)pEtylF%Y|JV#(r~qUkX&*zb!i5I=$oq zY`%`47vnsgc*gG%LtbF*TJKkZ&RR~4`eilUo^1U*Bpe!Ag%`qm;S;V>tm|!;3}VKs z6qfY8pN+o7ZZnju9My8#uX?a=fo|)EjIX{9-v;#-3O~L2=tEA6^5JjObPt_iFO&kk$sTF*#P*AHQrnUOLognPP0sY^9_ zgT2r%-p5z>>ho2Hy~DGiUFnQk&NOP-rHB&FFQSB1Z%0aaFMJ+W(O&hgPUg8irJtcr z@-mg#)r|aZK}%e7XswfI)=<|Cq9z`u7G9wS=CKx|eL_`v+QYOhwLAAkw9(&ZCz|&7 ze1;zW7q;f1l{c-u6+#RLg`-HH=(&BkHe7Et|BIcxt-9IXFQLs;w=dqr-0N(Suh#2S#;u^HrznqSQ=0lclt{#LMQ;_{nVf`ro_jDn z7#<3bgy(YWu$kQaj@(&u$x?QeHeRq@a?^Wp)c&OH zi+#FM-2J^DlzBX>F&)r}?m`J%0G(FlQqB+XHGBt3yAbh=J#TE6{zHB zI?w!M-nxjrXgAaP?}p3CqI7I)J(z3sV<*b5P_;vW{Um6e8I8ofU zF)7>_l+GionSN}7UIB(J)}FWRvdp-a+V$$xd}tlvK5M%M*)X^+iCaB5xs z!ZmhXYpr7t{NW|LCI(%T;S$r3zAUxt17z>3?fL?~u|70Nxy6{EQKV8^pAy~6YizBO zInD1Gc7`9Tg+D|*b9k#3_uFy*C~;5u19fy78%y?AH)MMBUFizba{UA<8L~1`!SS5L z9sK{p-g|&+Q6zoeJu^4Sh=8KRb4ZeN&N(Ada!!H?T@}enGzU}!K?G42bwxx)OelgF zP}EgWFo6PMc3m)|yuW|-%sC_L`t19C*K>XE_q^W2aAsB2-uQz&d1wx&AEHL9UbHTb~h*H z*ssxRw5F+d#|CY|$Cq)MvVr!R3FAxX(K^(tzX~-cQtBqIA)KQeEv|8UF%-VhdVg-u z*P5ngBMqVRUW|$37!{|}R~1w87Wn&0bZ(E*X6m`(^zY^H`5zc53$O>~%$zCjYgcd8ul>)pHQJ`4*TT>ksdDL8#SG<*)sxidCm;m2GiJX}J8_Kw%quN+P*b zVg*5bu?~mm`U0ev8E_%K)!|w)bh)bryixo1tP8Qs)wv}^)~yfGu*nIIYz&59pkNfoqGLJ)4!^Bzd>() z$9yM%Ci*~Evot#&+MZ}K_Y=^=A$VWjAb+4fz6h)=h5!8+&ft5h*I96wBs$(I+r1@R zy~FyBdf$Q~zMYqZk1bJUP@T#>P~~YVc788)pk5!y<#V{v6Jyxvet)o z?1z4SwZqF*Y-(fV*+oxQJ_Vv(v1BsW?SfMXcEpW@{#9;I`(>W@X&(w##AAWRz7^C> zG}{XveFI!tu{5;;tI`V3huor2+92K6p$}-r^)mPR!UYzAPfrFgraj1-(e!xq&IHl~ z>f4_Lb?Dhz!xZ(cLL)nhdD&Az`?LIvC$tv=Gpmtvp-v1jp>JjK>!gIdl8^bkm z-F|V8(R-C+Z`&h&KO^a5@v|sM))$+EtzsIx#3fPfu4cq|I=JXw|8>poF5}%le}7`& ze-Z}NJKb;e{C@vWH7$Ss2JZF$4>0=u7Vh`|v_WtATe#o<^E~_uxa~7hFGurlagtW2 z;Ofm9&t~v{m->t@aeDa!ac+2c6!Y(Rx~j@PM>9PuLmxj zN?*?4r5JhO$%4n}{knkHC+A6}uWIRcz^CCTsr1?hBiX$y|G!f&xBLwZJ`jHeF+9${ zxxxLui)T~m+ZenOmPgJRJWk)y;8~y%9bYPaH-jHxCK$ux^t}x3_2+k_()Ts^4vPc`~$f3t4jX?X}a_a+*kblBbaRY$B;frliaIJJAd$c*Heyo+xzr=Qm#fk zOPB2o?*G$gD&Or#rT^cB{a58&#PmeE?gY1m_j=rJ;k$qI>|WX`*;}yMCF`K~v&T1Y z_%wZ1I+4;)RyJe$1`c^zdme4W5myPl!a%Iqq)*F2X1=0^AIq1x7ivQbyf-PB5xzh_ z(73+KJWzZa+!#L_8H@{GS0CY77II;-OW|SHh%iR^fo9WxYw(33=uw7;qrxg!l_~W= zB2b(Y=Ta^#7ygZ0BzM$8bJZ?r7Iwi}PC8i4$*)JkU*n!-CbAbF>|eN@L0&9&PY550 z%X+)3u-{7rd)^2;_3+4VyFCdeD&Vz&eh z_fm9dcO(1A55Nx7xgJ2KeUN#EX4OwHC)#0km-Uk{yb7NTy_qHID}OU9C_x|M{oCRC zlEq5G?T6!AZL!-5H7MOucMO(d917chkujfSj+KTQa=^vFcipT z!Dzi7zRG}BpRf3SsJ$W;(4m~qO!8uPDcY;)>G7Onn}GhP=W2MDLXiYAIVCXb|?KpG`&=K8F>1?O{-_8b%bqX~7ug+q1%C z&%*m|f7~jc*f^(FDT~*N9l`g^Xw()@Xo6x_ZwJenjH-Q`(Q~E2Q|dMJoC2n~OSkqy zclkd5ycP3@#X(l+TTSI0-rCPG8>0Cd<3634)!3-4CkgCO7^gU%M{Yt9JU@U!UPt1zB(_eY+XlYA~hBis`CF&^}xqw{cCp zCAAHjGRy1|K13Uf!iL#CopE6NobX&|Oxi+W?^3rh_&KN6($eYsd2Nq_FggX#GT#RK zuyj%C7R*N8h^eh<&?2lIf1U4z((37rlc9>4rq|Qmvv9`~LP=f9x1oEIUZSfz-5~PO z)dth{sl61Ltf+Pmh1dP)%W*|0y_e?n*>Gfks7J_7jG`LlmN(kWZyu&jf3|pQCVGA&!9R&K?Cs+*nvb^u4KNBsMM|M*h>0sa5=B*BsvFUEp39dz7^%$em z0eZtHd@sW+ULLJZ;wXgmw*RL8YG=MVkbcoEZmIcJuMMtS%p1GAdjd&QYQ@r)Tf0De z8a!57udi)L+;D50=U9Tx*9%~c#lbv8C%k}<)s*v$9?6gXK*kw%zxOI z9WIeWN0MsQ`sJ`XEfs19gQ#Z@*GjPsY>B&v`YGk}U|RY+L9YnQ zUYoSoN#IoZHm0}sck`hj-DRvFN>UVVN;+})2Loy4MEM^7qOB)Q&bSM>^jLR-FJbBt z+BR1L`4BGe%O+gN-=YrT;*gCsl1rqQQ^_7S>p5S>pJ-iO?KVkV<;Zs8ku%I@PT%ts zl?_fbgP3pMf0PzE;sU6t5_-R?Q0+jDq0%IUjTpg)bBqq}r>|;`@eb4?AHYg4U9^1e z=$!^ZL-=tQ>|lq|_bxN*(9!VQx$xU7!#3bdB^&5AGa~7#XfQkG6am~^yZnlw4bq;6 z0z4Iue1z|ax3x#B*DaJrOs%p7$wWFa;rx1JpPj*(A9);$@5$4hc-tzh5ldk$sZ`lW zVCBMdYKbyD>g!?}TPEvh^(sMYlq0B6(~$qtQi-M%Pd#`AOEX zgfV+L*jvFEzXiGAeP~)<^KPN6_rqB=Uc4PkNFV3ONWUPVhf&rquE}-dRNXSHygEOmI$E2FjAmDYNt~i;%^g=7fGcm-SW?aCr?m?_fNad_54~*tallD-yH( z{qBO=8bE2y;xbk8flf>wdj0`jBnEt}n(5`D2Xk`eGvY-R{=JePE1}=1nc-=OnW8 zS4l>2{`vb81Ak)RzZV0i!zUB}C+darHI5p^9h-tnPLLPy_582#HAye-;=T7FQ>5VH zlFqH;z+YnuexkvB{Mm<6aOoge8#C?tohdjXC%-iu&YhitYYdjP+iL!oG?$b|DoZSm zIT$@t@N9#xV%C7xAO){%a7mV|fiM?L@~LKU?HpsSnS$3d_;R494 ztqp!VGib1vf_F6d5=Kz`tEAvwpSPG>ycs5QKjI2F}RQ0tND78&m{);aeFl~C-6xI_i=lre@fs}4esOiPEEn3 zXZBGfuxU%dXBzwpcLje3q~LQ5K93c6e3+!*N=Ix74pvK3@M{e2(3Pej$1x{PZG! zB=DCEegUhL`1DP|UorRubO`uUOu=6_xR0wVSw6|bFOA20aA6nwA2eSG1*Dfj_{pT){GGqMz%DIUME97t{{IFm+xK7R4ZDfl-A zAB}E-eVjFN%m1yxN1;za3z>rdXz-C}TJX=5g8yRh5hlY)6Pw^62=rh$2l~DgJdeSB zykKdWlk}Kq^7HY6FG|6WH@J@%JS+vr2!@}J7u+QUKiS}en8&a>l!D`sf!{z5=dx08 zt*2HJ37qs*-+^6q|eA2Qee*D=N(%sV6H9h|D6ugJQ{XUgRDR@tV`~5EW zAnBDvs3BQ@Ik5ceJs7-7t$`3J`LCEh>P)arlt4( z?kc3xr{Tp?>HAuGzaJ%EDt#KRnOu@T%U0mo?`!!&@VLHd_y?)<{Vl!UAM;WweHyMc zr6m6Wmi}~iI_-FWDt#KRxp$I&pr!Zgy7N=%({Qa4#Od=6vGi@x_A=8QW`HnbaoVu>E zgqUEr`|G8^?&G{=4?Hfu&bL1IdLyt`Irnn@$f@@mcV$luY2+)d{P%&Y%h`^z52xN; z48dLr5vKFed&*yHmPaNAZZ>}lToiw(!4r}c+)@ow$ptbu9Zjf zyxmdv`eu#E___nGHcY;gz7`GySK4W$OYZ-uJi=l69j#XURmA=nA z<+t~RkGd}x-wQtp%_n(1L%z!E_nuWdDxa4~w^PSwM>+zr7i{6uZq_6YBD-yW+L2^BrZzk7C{}Ue){Uz}9bs87xYt^evCwLqA z&F55km!|5YJe5YgK-bB)5*jT8-Kc%#1M_=uqjuDOxF^U%^+?V)x&MaqL=1DQa<=Cj z&N+$m7S7e2TR3sM;7&o{{%3q$5<3vVW?$g{DyHIt@a+zqaCBCqo^Tq2YH)ozoW2dC zgCvr!*e&)(4=QVVZ=tIQXI_7K3AUjVS%JHh=zQ9nHI12)tkE^IP9g$oR<6}>e`rqM z<20|S?CvzyQW~tb6B~)4Hcu36uFY(9b)9R}&g)a}cABLMcD3z^i?~wkrnP1paT0e` z%y<1vENrhvW2_nPbdr>bajENi*7_$D@{7r)i9^|imuLfnz`%hdM8;#S5-{f)m2~jA(M;@ zSFK{4-}$81F?R)%!r@}48K%-sb<*+ZYK>VSsMWfG&x0*Jc7{30QhHv^q($Xgg9r&D z&|7Hq*ORt6_5mZDG#P>s8!En9Y+4(^mYd#4SF2s(C8d?qPAkDyx@oQa)ks&Ot-qRN z_5*s_&1ue|t06Y~)7AM-`UqWJYnl>Wp&yCAyU1xzR$pL}B_wA^29bQy)x1#kjA`&p z(rDdmfUT_zCjX&eT5H={vmR|$7}^sdZ&l|UMX|Ec7p*3gdYQW%8khyOD!SG@+Bl&Z zQH-Y-#Y0(uy{>PEC16E9kyf#~w2n15&E!>&Nlr4UuBzs&|F^PvqhMp)+_=?NyY=jd z5p0ROJ_k1ZQQ!YW$IJKl>n@c4awa=@NZUQ9u3G8W+vm`X{_0Yjl?gV`=JC4vi)qaK zR~F`cMYA-)2H1MLu4bF&QCI6+p`yM=u5rDX>ZPf%_FiA2d+9lnT$R3})0|sZ<6Wir z3wN77MX<|Eqob=iPP$56t#x<*3spK#7!UR#UiEejHQT3hXm4cn&G-KD*VAFBx>NeB9>S~4idrSZB z2&b8uVAX9kT35YIo2;uxT;o0dYhB!wFa6c+PICr5WmPxlUFlB*Bd%QB7E|2p?*5H+ zPO~RHt?b^3uj-iwMX<4^^VHRR(|YUbQKvOvUEQ9uihG`u&ey{<%hA>8PV*^URdUIy z>!qf97VK`P)lOZlbUWhj*0mYAVBMW`GP)XVT4r4>bE}@sZGqD_{qp7hUyt(&p%DkdwYpSC^OuRadiYtwLAp-Sie7 zuU(wxxhDYA9`C=zqiLE;UIDF1$EEj1nYL6{({o}gifexzc=(EZAgD4Xg?>7g08AL>2P$_(^fBZwTko4 z-=7%x@56wo%>BmB-`oH1ECQwe2JY?uYc@2v+4F0JWd#e%1>*NK$c4)u15U#ZOl-+g z@PY>KkG&$jHU&S~;5D#opbw?s2-y4z24#R(Nx@4PoK-&H$EDy1U;M7M^|nJ;LpZNT z3(6S$Y`6e+$|-mSgKMuP{FFJ(4JEzLl1cpiz5B0^$eM=zU(7L2I3SfwoKX|sU4C&Ug|z4OKorm z=UQmk(i@fCHP8C*ZF4Vg@|I%{U^n@q`A*v!s5SLY4R)W)4o2_XYi;JQWQ$S=SS_qz z8Y36WKf)m7m5Y#MW#2l3z0F6kxVa5)7qU*tM_S@SV}qjDg4(h7%S~SD&@RWZfm&B} zJ{zfZs?%;_{&9CM$A#oN340gKjK68Qdw%Ei}v%!GqEXNN^O-|mf;=EW>9rMmtFd@CU3)aHFVKB>SG_Oqor$y z9ggO1FU4gjL2vwrG8M9mK5+>ss4KC=@^MKXzh=T}N^kuUJi8(lyX7;YyZcO(;fKYyb|MTkaiQC;U!!F&U*hQcBV) ztja*`WASfrx$OeKhTgE4a(GP1qsN<+XDdsIs^{Y1!z|y1?eG-a+4ZDcD(4m~thLAg zF6dQKy=dn)+VxGSBJuNiEmSE>cja@DoBg?$#io){{yv4@$;dN`F0LFUOR1a#<1f5~ zuNV1*{)~Ic?6Nk;w>;(6zFxJ+*--5Z7Sm99DSoeZ)+VL`#Yh$1$wENH0&SKAd#z*a%_eW`@+iC7us_~3 z&I70NyitbxYS5yNmi8rmr9b_8uN~Sm@{Rk+j>FKBs7cWpbTkILvThXCzk-8__N!6y zS{x1FjJ@EC-^X;>o*Hy<{bGErp?9liZ_SB_EPtem+L~CZAF|lU!qT8%1eTC0@fvI} zH?bpn{J&7mRa-noy%K1=Rl6004iYc@Z@9mF;-Bwy1t@{qdX}~F)o_vpK_Osj)7jR? zij#ws-N;tn|L!s_;)n zC;EOE!^O9y&qXxz@Pa=5u?F}41_!6oYd)?y{Q3NE@Ho9R-ipvot0DVz+BrojQEr`0 zvmr^T7&#Yb%&LmcWf1tAiu|isOG-7%EdgV?IycArj$#E}Z5|Z#g$9fal94qsO*j9> z*ErejOA07vBVlbM>_MhUJ_~7LDtSsmmi#@`w9C>h>yUQ&GSeDMXRJdyW9>%M$TWlZ z{FkLio}VKz*I?vp;p8ntr5x?#)mJ#GZu;&H)U%sQc9P9J+ z(+&yNBs&W`_&Z1Uh~_vhnaZTdBKq2mZVkFleN8y5K`n<+zr<5C(mqdx7u8O6n;*C7 z2=^gvqAy)dZZF1kFeYbj%hfqP;LmcRe0{5`Nr4*0Mxp&4XY!xm)14x3=xUa&UvOh( zk^g1++-Vxi2CZrPA?Wn|+vm`p=TTN^>8}LO2Pu#8xY~M-zET|gO?6Y$U$|@PTDtpU zO1RQVHdJqu$1{~hZFRlVen{nUz54n+t<(wU$&UE)FIY?`n72NNP{}0dRNtPea#ZmD*kU58oyHjvK{^!Xw z1^#IYu8|0DXyGZamlY{F;zWZlBd!gr&9CI$Q&F+b>Zvi=t&>O`8PB8`sh3AYw%dEQM$x0NbOYqT@BtlT19$;$LV_;yo}8;%jBld z*T>-AU&yhk?+!3{g{Uq07(C8rsKLEIkMA^|#N`}g@WWbmbq0^qpKEaM&tq>Y{RIZ! zh6g_93?8Sy*x=sJ^p;fmsRp0wHuBftar#*X_wwMnRQf9o-pk}cgU9KwH@KGvrB{{C zbcqYiXL53%mJj>ijg1Y@6~k3}+qGQlxzZ-ri7c6ZZ!_t(#_uK1cX0nc=YCH4l1|`X za(|$|zu%4x&VEiD%DH1WkH^4O-&HwJ;a)zhtMFIvCTSzmbVaUA()QuG(x1&=>47id z?*h(coXYEuo+o8W>Ycp%F7N))Gd2wVOWt+jvZue7z>>17PD%Oe#CiPDvjh(xQ@=D` zlKVe;_uoDHcewm_@VlWQ%>iId?9{ECsg|i>Q6M0F_fzBN7tv}>Emz3y}VdBb1}J!Cq96`&mV$n zVNoKfotcqjFNt*!=81Q(!@FGgu6d@``*nkQLE=yP4ZL&y0-`JI_xm>G%eHb*I~a^! zb8+xqP&zEhSCtc<{v{MT$hRMUh;BXi|S>$nT1%Bop3D@BXRvt!ww&NH4 z%jG3zO!cT1kysl?Eu+@aF&U5Lu9<$BA$APS+(6x7PDrcE+*jdi=yiqvdxD*C)jV94 zVU+kVjEEETSd1n4r+q4@jlY?LIlD=R;&bIg-Vko}##^+EJhS$Ww0G3E=!m!#K6j`_ z4d%lB;7eoCH1=S7FTQt%E5pCyVNkx}9t*d!kfc_qovEFv%;pVUhwT5B2RPw5zwPZV z!v5=e!PbxcmsU89jQVD$Oe-it^i_skzP*WkX!7A9E2pVg?pz05u17;!07^U!|A$($8WzZ(sI-?C1ZTu;dO}8{u)a~( z#ud>)aq^k!&S6jUXF-)vqq?5=HXW@f>vXiaI%);|c%o;Gqw7hfcPr4w)vZ+<;1hd! zOqDkUiT~kWLis5M6Y0z-p6ItJXCdUMeT-Ly%aC% zFrF>dd#hLpngo)LREF0=`6!XRQ=b{$Im~AlVgqtNTK4CI{9#A@tNs!ej{X{zBDT<| zjC8wd^)Fqv`R=ZhshcTK%C7+Iz@zRz%y+4L7)w%`PA^kLof}zwSV(`n)7?eh@`1Y6 zyeq7;7yKL@`;QccT%58)WT=X*;3epf)!sw?J-iEcyeGXn( z@1hrpCv?g5hQAHq7{oC$vmE?-+}DC{sns14Qf~BSf90a$ zF}~z$p#ssf)?2h#36DcL$qxp-~&tWjFKG=!M~~GRh7FWeeAINE^d#T7_I8Z z?Qt6ZQ1k`)VrfB2?~VCQ)}Zz==6#O;8y!cSJip3oM?w5{Xw6KqwNHir6ysSTP(1egxZ&mu`6mm*0J@;|lWy@q+abae@0~dgk|| zi72fkJxE?(G+vAbe_y~E8W>M-#tV)GBdX~;5e$MfZ@5~~JM=!~SOD6rMz5%wv6&E% zvc#~{dTIr(MHk{x;!STt9bedZq)|gcZ*4UZ-cRY!QV~lt4MG3 z@9q86=lFNGGkgcCQ}680*d!nN(#nb6H8YzIy~$(T5ys#CjP|>N5=5Su7T$^1>Pk>b ze(0krrBh2RfGal)N@TL36>07yqX^51yrD5Vsj22&dZ&bwb?`QKE+eBfd=-JImuPhQ zyQ>Io3^GYkvXUrdB;(a$`p=_`TJITk>}K>TO&?Kznn6E&i;-+6J?ps4@$i;z^u9!2 zm^Vl~F24~f%9}8%kx*LZQC1f5;%$8Q4eERV{(DpWwPn!VUpds8kK@%O_uHbz&p5;j z{$VZr6A`?V5{XU~v!fdRqwB#*`cp>9pQFOF!q?(*z6T{p0+r-h;Wv_}WUo9K)#C)H z_vDN)s}Z3=rctI7HS2EUl<%EGGDE592u7Lakb_3zWDHL8?iNA9xBW<*XuGd8eYW_J z+N(C>G`p_2yY653+|%?2wd=18p-H=z4zlUb8W&p!n(r!~VkSAPi}{&ik)8fKbiE2H z)ENIExV{w}KTcnH3K}~AE)T+ee}UR6f;+|VP<)cc;i^1-jIRQBs+rMxhp0%Uo)okU z)qgLD<>V>hZP1{`rlFY?SNN-C7Nu7;)4V~m1YtgJ)Dk&AiPU>A+(jMEBu#Osvm{s2 z#t%0-l@+^m_|kZ*&tDDAFN5kec5I^*qM9$@abDtR81%)Pj=Zgly1nc%4gX2ASZlt6 z@h73DXOI%MunNADS=7FxXi1Vv2G2sr;8DFC5!-WVNs@LeWQz`A=kPZ0EUcf7Y*sZm zEgm^`&_BL_CZ%%`56jMx=tlxk4=?N9WY+dR6u1v=bPyUmmbjNEMumt$UI9)~+s63y zk!+t@l3uzCzse)r2s=8Yk&$*sUV#$(Ano4m1@5#8G!@)z12-Rn86Cah`Mtr-O&kl% zOVCdG%xToHKgU2MvSsv+*N9hHfl~D5SZ_U{Q>F-Gj^x*5%xRL6Pv??z6ey0dq?W=# z7QAo*94(mWPw2V&OeDTXLh*d*nS{Yo?pMBg4H%R}ou58el4#S}$R}kp ziM7{wHv_wcy!bCt#Fz(|Lp~lp89t4_{ga4BS(^yeXJxdKCCb!nM?ST_WVF~2%LST? z%93bp&@86rX-J?mLs@GT<{fzwd5S~hw?=P2JCc`7%^&j-4_ozU|B07dW-;@7G@MR} z)y7_;Dw-*pQ4D190Yxj?2bKE>H3y+~jX0H{`BogQZOl;=WYvBkoN2V!;-9}iG4TIu z42bW$-_ien&;;_%O6&iJnjZei6kOv2YmRVj?7mWPZ|7SUi3A=qi~r($7})sLXWmQv z6w-$=oC!F;OOTdgc&rD`Hn{iO-7}TGoWa}T{SAG);BorX4etGR*Gr}Mdj4vRj6^C( zrLSh`#f{;Vsr0oBegt_shR6BWGq^Z7Gq_axh6dk&f5I3Zr*C3#Nr%pT4&O=Y+tT1u zu+NI&ar$-!&vIG(-H}S)+2GsZjWIk<-`(I|&;Mj9eIJAWA8~%kNn|6J2b;j7zwvwb(nU+>pXj0glWTob@fnkM zj=ukQc_d{`)*jUY;=~l zf{~=P9eeh@`04zK5)`nnpNRD6W#VI*5uFyEGtPf;w_BmUOkQQt3QB^LY~kg%tXLLQ z%(?o^x8&n!rJI7y^%3T$ZA0~H$pcyJnwk>)jeImeEN`}+Jp#!=%C{wZ$r3I96z2EW zFz1jAb3D&98@?I5zDX==&6kALQ^97Busv*1d4Gc6?h}KL$NIb!O-L&Q!qt^dvW>p* zAeP(m)~M_H*awO0Dn)&>NY`9(8v6~eu^oHf1M5>**}i9oZ2$M$aR6-24CW$j-Wte{ z^b5$@2ZAungGcjHM1|LUyaQf2yN2C!JV0tzI?_FMnJ>}j=Jiou9_dc~)x-L@oy?Ld z04tAXR8hU8AA1Im#AT7fYoei*^}FJIH#5uFc37fz3eI3Ya5gi7X%>_7&R{4Km#F5F zaI#TOD{w1XT7zYF#b87@DQvYl!3v_FVd8b9dg=J3$9R&u?l((ZeMR3mgZ!7_RdsUs z8EHgubE)?W)VKik>}r12h5x6Z!2>Q?A7H<%tq-VKP=P4SQ}|DA z^CVyA;{(~Ds}4S9ErT`n5z}(eDR?SPw@Rhxr7PV%ld z5Bu}g`WwN@U}}?>r@l6Clhcf>|95yJK&Mv$dTNY^*zVxAAN*^X%|YtGo0Qum^P;OST!q%+BXlC&!$jLL#VMwg zFtLZZh&-)Js>#oN+26F;IcBw94R|q^<`9k1k+d}n^v>`D*_rpW+3+NI)|GtkW_U!> zzc%FbuOor)UtZtUHtt`t!qd!CGq{FvUVYH^jEra2lWbnC_VW0w4oXz=%iT5X3n&zx z|FOqoNwDfQo{3E!{tDm@o>O!%IYVt-iZMNf%iGJ%Zc82O^!`vV<$iET3OA4mg=MTY;Sz>I=E+%@a>q&HWE4FYqM+D zm&d`SZZ#WA<@>C=Gf3hoj&?W2>p|T@c}iF6`snWm=2%#sMU-maAG*7?X#bSMEbvo~ zH4~-oK!58TB>MLoT{(J(`f*Q_l0*|F88iHdAvss?kAwdx3Zis5(rMpnw5#1xpFnM) z`5nIwniZA!h_t(*9{DH#)P1+cuPZA$#a`1uAIAT@yww*D9^LNCS2-9JCZ5o{n^dfl zmWNAT5^kdm>K`-MEpZ)uxG-Z};-MUhqQxgz+Nsc%jU$ZRw^DxXFHza@u+Hms-N#xv z%hES0a(J$LGzXIYWjgdZixJ=&zIZ+3z|C;PC1BtgsP#E`<2Iy#*Hd!9JF$lEBhxl3 zBJo#zI8Uf39EB+F$zf5XgVT@?r0J7>st&xhK767R{ZT%rb#!O68%pmS!I5xz^;Ag+ ziC6P}PF~H`BeH1qrqEH+CmY+y(FUI0i9WAdOtCRTF%E^jo83%GpNDUE3NK`w5=GB; z8%;j&vOyA|a5in*m=RSpajoqumtMegy_Ikp@p8{?){|0PeY^3Uy>4L4Sqq1nkEjL9 zVF%k*1oY?cPYnDYi~-R)T}uAWMC|;${r|a5A>J%M|Mz6vm{ADp0^nf`S5J~}iz^a% zJZ}`Q)S6c%{_m1Xf1<%7;x)wZIKBU{_bb;jm0nVZ{JMO{{|1lKpK5ULSFT1XJuIK! zU^LI{2M|0?5Bulm{mPY2rO!5aN#sDH0;JNHGq~3~{6c?@@sO{)!TtR9U@knr;CbN_ zW%+Ax|J@kw({CgHmNg~whbL9A^nM<`Cj~#<;C?>;&lFtpdtN-sm*%f)mYa|8o0r06 z@wadaE`3K{=k*R1Q}8MV_xg(v?Qe)giq#xvAgVdVEaPv`IN z!8FIbjb|%4|2wW9Az$eUbT(rSxP^P2Z*g{@KC5Q=aa3~A9pFr(cxkK;YO|KxEO;7- z#(*rwJRO>8XwE4PJ&7n!AGsIecd9bi^($!FL0yOEvbEPCf2gC`)wbM@71*rY#UZ`( zQx+}jX-0zQ*-5?CwD;Q>5p=wZ6`?;QE9^!Z(4kf6uaGPA*`ZbFWdGifK(p7TDn0$C zC3`4Tql+o!3}ia3q1~Wa=0hXihKff+USsSqIzB+)`09zD~7MZi`eHkngFTLPncE^4U zh5A@l-%u9Gs5(TW$Judw@EWsP)z)MCeJJZ2w|i!8KhH~jv}Y`f@{YE0Y29pF&ewd8 z`^psv#>Oct(UVl8%dm`EZ)HEa)D3fRy%SuEj%JYG?i|g=+?+k~g9D8fN>Lt73%iEn zy9(Ha4|ds8e5*7HBx_1u6&2<7UD1zf^odc>H_Sn5V_P9gKb3scU&=t6BMnEw&o|5% z8U>4Z<~f`85f6fr?#Y2Pu=kq;+BL6IsjibvUm@z~A2it zEQcpwE9P47!MX1NkvYI9J+WeH4 zxEInxWsCLz{b;=_OfS>7CX!ly_R>7cEPI_XSn-Yd%Qx`d{uqe&%E{J ziR4f^(8wDC*Nk~~jLSvzm@j!&#yFq&UIWt~DBVo=viWOcI5?``w1m&eGOQi#>%Acj zwl)#Z&~b~kXVM?;qDHX@8= z1@JJ2mpAwjwDYWM2p-G-)eY|D?Xju!bq&59Z4KVuQt2BT-0yGcol4)_;OiLYuryAk zZ*6eDPS!Y;zP-V3hV#)6Q|UV!Ja?T;^7a=``t>pS0FtbmtXoOu)%64I@V(5MRC2#B z3f&>jV^@10tEcieSu;!axM;1ZVr47>Gf4KF6*&_bUh7Cn9({q|#oiY4)%qW%(OOfI zzt)o;;Jr#v*;`V0mJR?{ih;Br`C0#bw=+f3+ zMIOt8e9*VJ;9FSd>~oUU>vJc|o;r*c9nhD`bKU^PwvEKo+Rhlihm=3TzccXf6Y(&T z%}xu&hU-qgBRPEDD&s9!_vjmn*miGF5f`pC!n$@yS0;O|X4qYZJv|@a;IxhfiMy47 zWNekHH(&kGeg2~#-AlTpK1or#Q`u^lU#vzJ8QxWwIo^!+_;kR4Q`W}{O$Q*2Giu*x|Ks)v$dh3(3; z&5g9k%HZSc{kKjGD(v@Hdx9I7_Urq30__Nw1l5?f8ps%bJKuT|$$29h>B3;NG%F|{ z5Wlc$Sj!IWHqo1X-D}`QUoC)L@`>krXeTGRQqSv!>g_5?iJZ1dqVev^S+^SD`ser7 zedl2x^_Spcj>*AP=;6lT&YUuixY>WHBkkS4q91e53XTD7`cf;W^{2dCElJUsq#sFP z4WSx;NY1Eano;plz4=a{RTR}>5K?+mN+Re!<2!1T;_gl?&F3P-D^0bqX*i8HRi{cZ z;7g>j}#3cr2tZqUO_rWc^^O z)4G7~YggfA$b0g+2`XM5+=*@CN{&^~v7`-^?XYVX6wRUCg_AvI2H1Lmp5Ngf3YPC- z2P4-d83nSD)rMdXnA`WVLchweg|cb?g!DP`tkr^9a$jaIv&rKw7t+(pAk#<#EbH7^ z?CX?gBKfRYibnX(UK0kds6X*Soc+WXL{YYEKP-$1WYV^8fEn~V{&B!W>d(UE1?c~Ti(N(di)RWIHXYTYwA?`UpDjV zD&_-9(XrpR2&URQAX@F|CQ@!)ZFX`vr6^35dczHD07YjCPy-z_h6DMe~eDLt6&ZHxPO9rGR56FN!O zPL2x_e-dMI=9P`YCgHLpKA%x;SWeGBA4Ly-rAaWSqoxGQJ*QgpBx zr&^;E{C%%k`wN>b^1~mFQC* zc=bkcIFjNrKjZesH0XQc1GLje!AUV^3xl$8U;5ESF(v*$Pi$|i=BlqSw$WD1MVG>s z?D|!&Bj&qTX-Y7Q-|l9Q^_3%a}WaQLJJZo#koyIcVzxo1H4RRrKb~(4cZ_ zniD~GLNGgS`&K5Y=?lfMnYh_JZxrNC(w)B2fOgeh%g6cFTl9w^SS-I!`^mOnc@D8P zZ&@7NO55K}I&oITlz$8QeizQV9}P$n<8=xYau?D@alXZ(QP>1n*AtThmhdt+L z2v71dbmBv?G92;}hmEP!+^-H@os^Mi))qV0?%+EuzY zs1zpqTSu7ud63-2wcY}c(#h=O`v1P4TFd>;{C{d&+B7R||NB++D@4{W1@Nf~$|Q9v?jb{L?nM`Af#f<9;*`_|_C0g5)NSnD%Br{GxzFUTGVc6_Gb<`1UdkWsl;HR>81!*h=Z*TA@ zlwXqxz>{|AWbi#^cibxl?`rTZ*c9TUHU&q-;I}e(40xdwytlz`#CtG4j9caA-_PI^ zkaO_$nSu{AxU|sD-I;<9Gk9|>>yTMe@G%BIE;E*{Z}3rQmlMT>FT?a|(W^!F}A$CMo#c2G?p2>*Xo(s-!{09XSg*5-)V3kM{#!wj!4OOYjQBtOTphUxV+TT72D;0_g#Z)CmX9) zDfoK^_i-2Nr{M1!+{az)l!AX?@CL|5@a+_Qx51lGNBl*l;Cl?-)K<%9rQjbLT>iGP zNl3vzGPw3!V^^Aj?=`smQxGpV1>a}zmaZj#-$=pt8@#n^&ENeg_{RqK@dgj4;0Fxu z;}aHcpNlu~o$@B}@HYkj)Zo$xp&LrUKQp*|8YB0n;GY}3GnQs(BU11$4DRD?PD;U< zrtw`L&-3aO{40aYGcx=<1^?RMK3?c!DfmAO?&FKTo`Qd4aP4QrYCQ%2r@?*v(qB{X zg9i8UQHylQ#m^yw`*^AqQ}AyM?&GgEPr<)4xaQsLZAihtH@J`QIxYqO!Qeg~?2Hur zM}zzLvA3k)KN;M|n_ZpTPxJk3aBr`(A{U-t@G97GoX%f^$NksCefs<2{72D$4_kU~ zuk%z2{)@rAz0RvC_^$@{`!Cj#8czdQi9X+1)U6dujy`RFV5baeK^nL?917Yvp?qm&Vign zh-F>=H}N~&aPCKNj^P~3c^2n5&heaQ+xknvJfSOHCU7PBtM!=lz1Avp-;A>h=h657 zF8w*Fv`1ej?+C8g5J~=uB{3CP`g!!Uf37v0dwKRG=YCGT^EKxY&J*+g=dc!}J^Edh zA<3%?&j)a(%kX>G`M}HNq)YIT)KT!m1N}a*-+Q){cT|_vsc-0h8hgjo2Cx1B{OJ5t zkL0_*cbz{UYhl@e8!^+bcfI}uR>i>n9lUt`Ag3+5katq=%DmHINn!X5_(J8I{9YuJo#oit-h=GE+&uUy?rp)dGWp09tBVSDs9JE#4e4W zr{7Jk(|PJT!Slbneu%vE?C4W#g(qDO;<;i?j-4C0-oy;7k>B6{^&5XbmHU_Ad0Lmu%irO_%f!fdD%>2t z9R3O)EC8%IQn4cTU5i#!n!u{T3}zhUgTA|IByP#%6V21pE<3scnK2J{L(n2+Wm@64 zLbGS3y*YY0nq1xMPQHo0wL{S)PvBV#^oedw#g1Uxf*=$;O@giWG8(fQHm^ipYDYMM7A={YN88fIE&dSnJ? zG-Ju|Y=-+eJQ6OCG?Ui-{_y+ohp>2b2yq#P?}FfwH@sG79u{tIyN`o2qjUJ4>e&xl zoRXQk@m&vBENGXhOC43~h0*e8MYJ-yCwlE(uUi}J$^|1cfBhj~?2?x#@3&g=yJUOK zA2henJVl4*XY&|?uBJqvg(2o}VI1xjWhueHHx>{TLmG&xrb&w)mJEn7JCkSQOMc z7!gxQUzZQvv~&!jp5{R}wB&EH*FrQ}kNni*p2CC8Dfn?HnyHkL_DD27Ci*@q%(c?p zK-rg3_PZ$e&*5Q8?V@(2m78NmCY^F(GH@`=#m%bxgTd_~CzhCfOK{*Ycj%T~{1=~q5Do%K=K z!dAiiNi}Tz2F2cjWSx2=>p-`#-)AAm%HTn~Zmh%G%2qJ^DwO>?s{&e={Me2!0_{=F z4$I(cr2-Vwg*`lHhJC{U(9GGkb4N6KZMZmG0`Bh$9}b_$=^0zXt@Ms<9B&%W+73m1 z5q^nZszb&}4%0pb@vCwQy{8!bEa7OSqwJ_0N4=;~)PzGmxXy@r!MFNEXGVRaG12(w zTzJ^|(Ph!B=;pZBFQsqYg-5-6qx+#!_5bIh=c5;+m*4}tqIcnw;*>f*h^g{mbP($C zw0V+I)~V**B+I;;RDjCl^{W>COX}lcs3BfVnsBtRqZPceZKfT*p1Q)ldqCB_@d-8% zzbS*w@6a&w4K_NXLu>aZL7(44<0oc{<3A+}Pd&9W^~kkVrnBYHpEpNl=Au9lRo_ZW z$eK@m;XbJ9MO&rUlTPp<(-?9c(K27r8#VegG5Qu&ivoAEav{BnxXJmhP1rVU7q$-< z;YIL))}9BK$Z6H}ncZ`K z_OE>wY9w96sPRFhS&+VPjpy7jU>0EWag?+~5O8GJG1Zbrc?cXh<6@0yOPBi*i zKb{T1wxK~r8Y9(Ylxqx4NFS$EliiY-%V~r;;+BxN#s=-B%LnEQ;Mqtzc46pAqu=); z>FD+SucG=Ht?3u!`X1vN?;5{oV7;V8)Po+yKidTcpq55l;OeUr;A~eIN zenlFn5|jxwTU0Hta?j=P^44&3c`+OVTzvKtaGuz1K6d4ZhnqW368jYth94LZlQiJguAzTs@&?H$@o4|pKjKjYiR?aAXoao^MIGs4$Ks=|7#&g=EltUEU!}zYOAIfo8mtLpV>Bg9% z@kFD^LdK7egKNVZ@$G(dcuTk-TnN9>sE|k$D;XKKF@hJtZ`-LH70q*^zPgq-MN_B5 z9Jk}go;q97>fKn|oETh6Th9zy#Qmu!yr6f+)>C-;UeEWJ zg1t{@(VxIwX=-yE@EgGAN+g`u8J9lg#SrgalBBhgq+V7q>XIo9menh7rR@`m?WAqq zUgAXfw`BH_8Lb8AyR+Qa;qy_lqjYs(I$_`=kx{iSpfodGF-D;a=@n|}H(j>LLRz)a z)0K?DuLtwFQwbt?Kw>em$|AAdMX#6ad^1>G2?akNZZ7HD_eUoy2i5mz8KBK3A1$#X zJ0>J`NZQzmbkP(ZF&;jUjFytp4o1E_@-CqMyCi&txsXcs59T0;BY8kPkvre^&4ZTo zNBIYq1=tI;`?d6}<1$@~dEV`14q5W6u788}C4F{%($Ai5MMh6>dhVNk#8c_+AL^;F zbu6tO!QLsGo-EC{uu=(4d0A*cd7c3cln74BZ025VI?gzWIA|NjI!P$SZM@$bbE)%O zYa727A(MO?+FA#1UE&6Wvd{pzNQ8d=Hjljvt8Hccrl2JBynyjRGDMp=7*zWgRLOY83+hQMZDgFDt;HI$8bN+jKqAqb8 z^s+V>mC?$Y-jrWl*++;@P78{&hoNHJqpO;~B+4e+2;pgQ_RsOI?$B~x@MYzImuG%nb|$Vv4vjtJSE&7Zs3V(*ca)s2EvlAH8XuahwrEz z^ODN*oh8gfR={_DU>j_)Cqr{5iqF_OIa;DYG9*b3i06m6MlnH;}J@u7q;l<*_ ziDwhdR^<6ysU8fpUZ<-IT)57UQLpElUeYtE?|OzH4;=HZgcI>3i^xn@vaB1sRkk!G}cBsUtuAM!z3R(Ui7 zQ<_!10PiXnT@<%ff0q^1&S>>ib(@Zl|6jvpkyh`0T~*U<;R(Y$_bThkG4^XsRZpk6 zAHqAB5tNVH^Wm^oM%JtKcy=%IFdc7WZ{v6FeCQf71JTih8GTP?AUZS$8OQuXhvck< z@KGIi+woUsAb(|suj5`j?qjxpA9E5NtL@Orpezh3In7B&+g#N19?d*8>s0($%`;cV zGtF<|1%sHktz@3IhZ$Kzo3UTZY-@j5ka^M>%!w{zCiEmTov)Z_FOSEDwr=>Nejj2r z>z((*+aBWB2=Cbj$CdA-Eny>SbuRPtQ!>l<_P%j5W9@xr)Gy`@y|Jt<5sc1gwN}#h*)EX-63I$3x7M`PE>qq2;ZkH> zt;MSc&O@s8daBz^_If{(Rfy>04E&kOGDT(B#oTS6`AOG1Gov}t+~}L=5VL^89KS>b zA}`Ihv>E+uvlvhw^O;)@k7<1y_y2eN)U)01*#8um!;>sqrw+vPrO6z`|Jf6XY*E~dH@3aIS+iyr3_EvWA3)Td*uVrTioWz&&y7<}XQxkXsOZ!rLkM#h- zyDdeN8NZ)~NlW+wW20Jgg8Pfh_pFzIi%~{Jts6j@&UeX{%CAx)`E_z-KJ#x@cb^^c z-@Jt9i8Wj?`h9EiPxel?a@sYi{9Bqgp)M9V(3eDI9cNal`ii{Q?_1|(K>2fT&nj6j zH=4Kgv^kI@8r-=#@dLZCQZ_f{^Ov|G?Eh0*y;qg`s($TFHj!>Zn5%BMQ=H`9yeT}K z%cyV>Qj+#aYX&DC-iGH|q3%J6Ch+_(q?iac*}qPUDA$SZgG>@Lp_=7dlUla}gFP(g zzT`d7a`*4wk!|ldzbZ`? z&Od*?W!&%d|H)T?r7Yk7g-a+Tx)}EQ&`GpJ43{jxTq0@-yj$m7xFm1qo(&5CZqyOo`kWd#(!U!)za+e3K@J}I1hM%6x{QRx#3*kc~kj7Q2g?sXJn1CQ*J&CZ~P7t zX@Hqj3Z8B7FU?Nw0pLmb#V;5y7(3zqDR?D=Z(`0&y;5)%VEA=lPKLcy3SP(HM_gmz zr={Qx4gL}Pn2DQ4-*PeDS{Qu3+ri)KQ}8whpMjS&#=aE1qru0c7jSMw3f|S=Jy_kw zqBaHZZtzO%JzZ))h4_X%FQZ@Tc)ablVW=_!D;w-XeT# z@cV<4!%ECQ_Js|R_L9i5Yr`3xyoR7FT9EO~yrhFlb{!TB3o_H_8LDqVD)guqhypHd zmLGkTv{C)T^TH0~Aj%K%-Bl*2fX==$mXLMu8(-i21au8X24mPqG6{{%47_7M6#V$1 z=ZU99<%!1BBN~F-)`-~!6o;1go3KYmFOOBh?z*{?CH?C_it}_nK1@C`uiLo)!7{#B zki|D^*mpYPxnd-{8!ln4u?YYEOR>Aw*B;67qxlUHsOE(~k{2DFwZiAwQLp~4@7_Wr zn<}0C3@2b;j-qUwhK2TIbab=Xcd|+SoOGL!(>IZWYCInte*~6~fM?ZPb?$854$imQ z-e~^FmSPvLm|FVg3E*v;Mbpws=u>VZbxG<`f%k=_8I=FM;NrLam>{eA+R@q3Qts6T zTZ0OW7J0a;n5Q_o`yiHGwRnuqx$bf| zlT|PI;gbaY8aq5bv4|sI(uT+2k)piQT8Le#_URClfM6D(3cWu9Y|W;?s(iCzzlj6I~K2J_e0<4qDcET z%kV6JcmllRB-Uq3p;fAaHmg(2G5Rp)I~T5TVR$j#EnlEFyhXe14nL;fe{UH6741l# zD1Vgrf#~DTr{#r-M($PGUHs=%;~d?Mb7-GuZr{>8SmSwhXupv=hG^OEP)lRE@G+1GXl z`#mq;=j(Soeh6O)-w3m#0Z|t8tu`(P4tIm2B<}a!Vb=G))N>_?wDq#8k6`k?mF*j7 zOceHgP=FE+hEf$#X(T=iH_|7S=O(zv3-oKv6eK?vz}H#9K%O#dnx~HUSqsrFyAF6s zeILD5-bnjTRr|T{*yiB{XnLB_Pm?HL@_hV-@uin`__9@kD}2hHetqo}aMKN}$U~;E zc_A&G#35I2lPsl{oNK-AQ|Om20Jfe$ce2$e_yqhyp2ATgCgJeR&DO^SL>v8YrZ3%c zG|hhGcrdt!D?zJ};_q;LQC(wK>ob2<->v0Jw4%Q6$ElTue?^p6t<+X(r@w_sO!>uO z5=VY{cqLd-?p@*D+AS*#RiWKK4izIycYSPx*DOO{-W1dVbCOw(fr?5)MOsJi1XtY^ zzC$}}&GCC%>oG23-VNmApbqrqkH%1iXj#W+q1J)QkXtQyv|3E-528r%QcwQ2gpN*EV5v?F^W$yr!zD_OQiI_Jd zY2g`2igR=PPgkQi?&5eCNkctRk&o0Dzk?gAZ5GDu_F|~@CSiF_ka&$c(<#oD%2owz zd`vADAcLzsRpFnbXcax{jCbk5Q0MHJ#=F{RTsB9Z5a*Ue(S%;z-&Tm!I{oQ4|6o6L z?Lec9BnL?_D{Z_vU?Yz9^s2sf-AgFb$I++14p08X<3m!`D|}BmiO=k&ZmQXLK|Z|w zs{Qn}e(;a+j1JoATZNRB;81nQuRao(q}C3UT=;sBUZoM}B_y%qW0|1{>#kaPNMy`s zk-c6)PB;a7nzHno28|2}?RW$jx zHk@G)R3sVr>}UX#q!xWK7{GT#C%sIX&`$gDa4oH;C$XKLr~WSnnuqI+kKD1;N~JuN zH5+;Nm$so2@DAWA559`?gP&(PHgmiHR!%_!-Hm!`%}SEb=iqmFkj>b05wY^#gWqPL z?{Sg#_ljfWV~wv;*c_>MFZ}t7P|=HoiFM3|G+s_`;W4AgyYljt?E3vWm>af7(JZXU z%faN}>jVDHQgDM^@PIWj7rrID)#PPGA5!V_$9|QIBaKyLympcFCfG2uE@kFf-_T~St&&Y+-iTTxHbTG?=X{KF#y)g;XZUz6mHDf* ziS8`9Rt)=5gYW@-3Q*xV;Q9euiBM`q zPAKgX8@lUq{N^un%U!1ze7|3ZZbG&Idb78Copx1zC0Rqw>gx0Rz!m7?r8;>&=wWPiR$14(_o=@ySD*RC3+~Gp8`9f!i)-D())=JolfC(LYmc?$ zDr@Yw@zi?IjzjLd_}$HLGEt9KLF6A~1DM_Dw308ssL$Jq_qRsx!qg}?F~-i9P8Lln zi~PIvaiUD+QOdB8;Gho}k{$kfykM&jNEWR{tzWTq+E$RZa^2+;&o6JgZ}Rx@Mq}dj z&_S}kFS$Nh-@i6zZHl-VjN;1U%^Ytm)4=ETIjd8nXx%Bal{7VlN%z|}O1F^4WVMcB z5j2jm+0)jlL<6;G&rZ~T3@!Iyj%Uwq=Gjw}CEAewMb}r*np7o`mRc}^{l#VGv zUQWCOa6MEdy~-i%XvI53R~@i3T!^i{*1m+_I+kA&-Sjngn3l6Ki9CeaFXFN`vUM+& zRbEPy7XQ*bXsT4bXxU^n>*XBVTa`r7Hk4u%6f=XFsqneL?T;zsS*V~L-%#5LUn=<_ z^TsJiXKVfG;QWNR91UD`u2k~UR=1Y)WR>?2E7adn&!TagWB zVZmH81xJwPcR2?VD;yvxXBPL^b#q{cn1Yuzxc7^&F9ok)aPR-%?TCFQt-|O_kIz?^OAhJ8Ql9t(Ab~Adl=mN zMR+b3p6O}u;chs8A4tJ_8C?Fv@!FMw_cplSSAA6q-pAm6fA*y*_?ZUx`?=3b!6gT3 zzTKby`=;Ri4DR=fi*vdxlLzw)o`;=L9r-&Lx@uWdqicAQ-p}J&a-_et-Xx6kNK(JV3f+7f_B8pj2K_rNRs0b(<%n236oO1y6{{GcttTk}E z&)MhR_s+TBd#>?~HG9_V(W66kbyanB)#MC(cm_T{10P}V+Sv0tw>9%#!Arwi zlK&{)JJR5ue_x(~pPqrQ&A>+)+{*zQGw{(F_*US_coiCB@V}YXd`AX8*5F8QjNDSyxd1LYmK|(V|KI(W#n{2SQHPgy`%-&hCZuX~;SJ5RU

E*7)U7NcO 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

UsDCQ*J%{x$o5w4RqVlQmj; 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

wQ-5K+Q=dL{)PF*sdOGJ?2T!;ZFW5$B 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ƔzHJ9hBqr+}3@86MtL)akz|ro;xF9uPO- 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ⅅ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⪻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%0DB=;Hdo>$K*pJ&OPN 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 literal 0 HcmV?d00001 diff --git a/abc_with_bb_support/abc.vcproj b/abc_with_bb_support/abc.vcproj new file mode 100644 index 000000000..ff18e2c7e --- /dev/null +++ b/abc_with_bb_support/abc.vcproj @@ -0,0 +1,12366 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/abc_with_bb_support/abclib.dsp b/abc_with_bb_support/abclib.dsp new file mode 100644 index 000000000..0152f1f11 --- /dev/null +++ b/abc_with_bb_support/abclib.dsp @@ -0,0 +1,2336 @@ +# Microsoft Developer Studio Project File - Name="abclib" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=abclib - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "abclib.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "abclib.mak" CFG="abclib - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "abclib - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "abclib - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "abclib - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "abclib___Win32_Release" +# PROP BASE Intermediate_Dir "abclib___Win32_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "abclib\ReleaseLib" +# PROP Intermediate_Dir "abclib\ReleaseLib" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "src\base\abc" /I "src\base\abci" /I "src\base\abcs" /I "src\base\seq" /I "src\base\cmd" /I "src\base\io" /I "src\base\main" /I "src\bdd\cudd" /I "src\bdd\epd" /I "src\bdd\mtr" /I "src\bdd\parse" /I "src\bdd\dsd" /I "src\bdd\reo" /I "src\sop\ft" /I "src\sat\asat" /I "src\sat\bsat" /I "src\sat\msat" /I "src\sat\fraig" /I "src\opt\cut" /I "src\opt\dec" /I "src\opt\fxu" /I "src\opt\sim" /I "src\opt\rwr" /I "src\opt\kit" /I "src\map\fpga" /I "src\map\if" /I "src\map\mapper" /I "src\map\mio" /I "src\map\super" /I "src\misc\extra" /I "src\misc\st" /I "src\misc\mvc" /I "src\misc\util" /I "src\misc\npn" /I "src\misc\vec" /I "src\misc\espresso" /I "src\misc\nm" /I "src\misc\hash" /I "src\aig\ivy" /I "src\aig\hop" /I "src\aig\rwt" /I "src\aig\deco" /I "src\aig\mem" /I "src\temp\esop" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "__STDC__" /D "HAVE_ASSERT_H" /FR /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"abclib\abclib_release.lib" + +!ELSEIF "$(CFG)" == "abclib - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "abclib___Win32_Debug" +# PROP BASE Intermediate_Dir "abclib___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "abclib\DebugLib" +# PROP Intermediate_Dir "abclib\DebugLib" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "src\base\abc" /I "src\base\abci" /I "src\base\abcs" /I "src\base\seq" /I "src\base\cmd" /I "src\base\io" /I "src\base\main" /I "src\bdd\cudd" /I "src\bdd\epd" /I "src\bdd\mtr" /I "src\bdd\parse" /I "src\bdd\dsd" /I "src\bdd\reo" /I "src\sop\ft" /I "src\sat\asat" /I "src\sat\bsat" /I "src\sat\msat" /I "src\sat\fraig" /I "src\opt\cut" /I "src\opt\dec" /I "src\opt\fxu" /I "src\opt\sim" /I "src\opt\rwr" /I "src\opt\kit" /I "src\map\fpga" /I "src\map\if" /I "src\map\mapper" /I "src\map\mio" /I "src\map\super" /I "src\misc\extra" /I "src\misc\st" /I "src\misc\mvc" /I "src\misc\util" /I "src\misc\npn" /I "src\misc\vec" /I "src\misc\espresso" /I "src\misc\nm" /I "src\misc\hash" /I "src\aig\ivy" /I "src\aig\hop" /I "src\aig\rwt" /I "src\aig\deco" /I "src\aig\mem" /I "src\temp\esop" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /D "__STDC__" /D "HAVE_ASSERT_H" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"abclib\abclib_debug.lib" + +!ENDIF + +# Begin Target + +# Name "abclib - Win32 Release" +# Name "abclib - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Group "base" + +# PROP Default_Filter "" +# Begin Group "abc" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\base\abc\abc.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcAig.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcCheck.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcDfs.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcFanio.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcFunc.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcLatch.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcLib.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcMinBase.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcNames.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcNetlist.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcNtk.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcObj.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcRefs.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcShow.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcSop.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abc\abcUtil.c +# End Source File +# End Group +# Begin Group "abci" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\base\abci\abc.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcAttach.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcAuto.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcBalance.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcBmc.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcClpBdd.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcClpSop.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcCut.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcDebug.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcDress.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcDsd.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcEspresso.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcExtract.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcFpga.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcFpgaFast.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcFraig.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcFxu.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcGen.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcIf.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcIvy.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcLut.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcMap.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcMini.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcMiter.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcMulti.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcMv.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcNtbdd.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcOrder.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcPrint.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcProve.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcReconv.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcRefactor.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcRenode.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcReorder.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcRestruct.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcResub.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcRewrite.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcRr.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcSat.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcStrash.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcSweep.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcSymm.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcTiming.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcUnate.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcUnreach.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcVerify.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\abci\abcXsim.c +# End Source File +# End Group +# Begin Group "cmd" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\base\cmd\cmd.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\cmd\cmd.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\cmd\cmdAlias.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\cmd\cmdApi.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\cmd\cmdFlag.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\cmd\cmdHist.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\cmd\cmdInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\cmd\cmdUtils.c +# End Source File +# End Group +# Begin Group "io" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\base\io\io.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\io.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioReadAiger.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioReadBaf.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioReadBench.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioReadBlif.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioReadBlifAig.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioReadEdif.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioReadEqn.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioReadPla.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioUtil.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteAiger.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteBaf.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteBench.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteBlif.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteCnf.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteDot.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteEqn.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteGml.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteList.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWritePla.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteVer.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\io\ioWriteVerAux.c +# End Source File +# End Group +# Begin Group "main" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\base\main\libSupport.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\main\main.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\main\main.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\main\mainFrame.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\main\mainInit.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\main\mainInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\main\mainUtils.c +# End Source File +# End Group +# Begin Group "ver" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\base\ver\ver.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\ver\verCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\ver\verFormula.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\ver\verParse.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\ver\verStream.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\ver\verWords.c +# End Source File +# End Group +# End Group +# Begin Group "bdd" + +# PROP Default_Filter "" +# Begin Group "cudd" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\bdd\cudd\cudd.h +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddAddAbs.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddAddApply.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddAddFind.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddAddInv.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddAddIte.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddAddNeg.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddAddWalsh.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddAndAbs.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddAnneal.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddApa.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddAPI.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddApprox.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddBddAbs.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddBddCorr.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddBddIte.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddBridge.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddCache.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddCheck.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddClip.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddCof.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddCompose.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddDecomp.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddEssent.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddExact.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddExport.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddGenCof.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddGenetic.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddGroup.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddHarwell.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddInit.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddInteract.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddLCache.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddLevelQ.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddLinear.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddLiteral.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddMatMult.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddPriority.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddRead.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddRef.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddReorder.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddSat.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddSign.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddSolve.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddSplit.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddSubsetHB.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddSubsetSP.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddSymmetry.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddTable.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddUtil.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddWindow.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddCount.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddFuncs.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddGroup.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddIsop.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddLin.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddMisc.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddPort.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddReord.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddSetop.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddSymm.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\cudd\cuddZddUtil.c +# End Source File +# End Group +# Begin Group "epd" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\bdd\epd\epd.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\epd\epd.h +# End Source File +# End Group +# Begin Group "mtr" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\bdd\mtr\mtr.h +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\mtr\mtrBasic.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\mtr\mtrGroup.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\mtr\mtrInt.h +# End Source File +# End Group +# Begin Group "parse" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\bdd\parse\parse.h +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\parse\parseCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\parse\parseEqn.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\parse\parseInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\parse\parseStack.c +# End Source File +# End Group +# Begin Group "dsd" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\bdd\dsd\dsd.h +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\dsd\dsdApi.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\dsd\dsdCheck.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\dsd\dsdInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\dsd\dsdLocal.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\dsd\dsdMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\dsd\dsdProc.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\dsd\dsdTree.c +# End Source File +# End Group +# Begin Group "reo" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\bdd\reo\reo.h +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\reo\reoApi.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\reo\reoCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\reo\reoProfile.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\reo\reoSift.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\reo\reoSwap.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\reo\reoTest.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\reo\reoTransfer.c +# End Source File +# Begin Source File + +SOURCE=.\src\bdd\reo\reoUnits.c +# End Source File +# End Group +# End Group +# Begin Group "sat" + +# PROP Default_Filter "" +# Begin Group "asat" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\sat\asat\added.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\asat\asatmem.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\asat\asatmem.h +# End Source File +# Begin Source File + +SOURCE=.\src\sat\asat\jfront.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\asat\solver.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\asat\solver.h +# End Source File +# Begin Source File + +SOURCE=.\src\sat\asat\solver_vec.h +# End Source File +# End Group +# Begin Group "msat" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\sat\msat\msat.h +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatActivity.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatClause.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatClauseVec.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatMem.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatOrderH.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatQueue.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatRead.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatSolverApi.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatSolverCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatSolverIo.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatSolverSearch.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatSort.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\msat\msatVec.c +# End Source File +# End Group +# Begin Group "fraig" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\sat\fraig\fraig.h +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigApi.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigCanon.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigChoice.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigFanout.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigFeed.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigMem.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigNode.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigPrime.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigSat.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigTable.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigUtil.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\fraig\fraigVec.c +# End Source File +# End Group +# Begin Group "csat" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\sat\csat\csat_apis.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\csat\csat_apis.h +# End Source File +# End Group +# Begin Group "bsat" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\sat\bsat\satMem.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\bsat\satMem.h +# End Source File +# Begin Source File + +SOURCE=.\src\sat\bsat\satSolver.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\bsat\satSolver.h +# End Source File +# Begin Source File + +SOURCE=.\src\sat\bsat\satUtil.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\bsat\satVec.h +# End Source File +# End Group +# End Group +# Begin Group "opt" + +# PROP Default_Filter "" +# Begin Group "fxu" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\opt\fxu\fxu.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxu.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuCreate.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuHeapD.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuHeapS.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuList.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuMatrix.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuPair.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuPrint.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuReduce.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuSelect.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuSingle.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\fxu\fxuUpdate.c +# End Source File +# End Group +# Begin Group "rwr" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\opt\rwr\rwr.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\rwr\rwrDec.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\rwr\rwrEva.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\rwr\rwrExp.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\rwr\rwrLib.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\rwr\rwrMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\rwr\rwrPrint.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\rwr\rwrTemp.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\rwr\rwrUtil.c +# End Source File +# End Group +# Begin Group "cut" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\opt\cut\cut.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutApi.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutCut.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutExpand.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutList.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutMerge.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutNode.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutOracle.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutPre22.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutSeq.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\cut\cutTruth.c +# End Source File +# End Group +# Begin Group "dec" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\opt\dec\dec.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\dec\decAbc.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\dec\decFactor.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\dec\decMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\dec\decPrint.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\dec\decUtil.c +# End Source File +# End Group +# Begin Group "sim" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\opt\sim\sim.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\sim\simMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\sim\simSat.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\sim\simSeq.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\sim\simSupp.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\sim\simSwitch.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\sim\simSym.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\sim\simSymSat.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\sim\simSymSim.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\sim\simSymStr.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\sim\simUtils.c +# End Source File +# End Group +# Begin Group "ret" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\opt\ret\retArea.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\ret\retCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\ret\retDelay.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\ret\retFlow.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\ret\retIncrem.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\ret\retInit.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\ret\retInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\ret\retLvalue.c +# End Source File +# End Group +# Begin Group "kit" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\opt\kit\kit.h +# End Source File +# Begin Source File + +SOURCE=.\src\opt\kit\kitBdd.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\kit\kitFactor.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\kit\kitGraph.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\kit\kitHop.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\kit\kitIsop.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\kit\kitSop.c +# End Source File +# Begin Source File + +SOURCE=.\src\opt\kit\kitTruth.c +# End Source File +# End Group +# End Group +# Begin Group "map" + +# PROP Default_Filter "" +# Begin Group "fpga" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\map\fpga\fpga.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpga.h +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaCreate.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaCut.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaCutUtils.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaFanout.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaLib.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaMatch.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaSwitch.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaTime.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaTruth.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaUtils.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\fpga\fpgaVec.c +# End Source File +# End Group +# Begin Group "mapper" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\map\mapper\mapper.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapper.h +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperCanon.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperCreate.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperCut.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperCutUtils.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperFanout.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperLib.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperMatch.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperRefs.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperSuper.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperSwitch.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperTable.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperTime.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperTree.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperTruth.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperUtils.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mapper\mapperVec.c +# End Source File +# End Group +# Begin Group "mio" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\map\mio\mio.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mio\mio.h +# End Source File +# Begin Source File + +SOURCE=.\src\map\mio\mioApi.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mio\mioFunc.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mio\mioInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\map\mio\mioRead.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\mio\mioUtils.c +# End Source File +# End Group +# Begin Group "super" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\map\super\super.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\super\super.h +# End Source File +# Begin Source File + +SOURCE=.\src\map\super\superAnd.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\super\superGate.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\super\superInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\map\super\superWrite.c +# End Source File +# End Group +# Begin Group "if" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\map\if\if.h +# End Source File +# Begin Source File + +SOURCE=.\src\map\if\ifCore.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\if\ifCut.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\if\ifMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\if\ifMap.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\if\ifPrepro.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\if\ifReduce.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\if\ifSeq.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\if\ifTime.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\if\ifTruth.c +# End Source File +# Begin Source File + +SOURCE=.\src\map\if\ifUtil.c +# End Source File +# End Group +# End Group +# Begin Group "misc" + +# PROP Default_Filter "" +# Begin Group "extra" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\misc\extra\extra.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraBddAuto.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraBddKmap.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraBddMisc.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraBddSymm.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraBddUnate.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraUtilBitMatrix.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraUtilCanon.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraUtilFile.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraUtilMemory.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraUtilMisc.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraUtilProgress.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraUtilReader.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraUtilTruth.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\extra\extraUtilUtil.c +# End Source File +# End Group +# Begin Group "st" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\misc\st\st.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\st\st.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\st\stmm.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\st\stmm.h +# End Source File +# End Group +# Begin Group "util" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\misc\util\util_hack.h +# End Source File +# End Group +# Begin Group "mvc" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\misc\mvc\mvc.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvc.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcApi.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcCompare.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcContain.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcCover.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcCube.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcDivide.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcDivisor.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcList.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcLits.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcOpAlg.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcOpBool.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcPrint.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcSort.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\mvc\mvcUtils.c +# End Source File +# End Group +# Begin Group "vec" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\misc\vec\vec.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\vec\vecAtt.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\vec\vecFlt.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\vec\vecInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\vec\vecPtr.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\vec\vecStr.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\vec\vecVec.h +# End Source File +# End Group +# Begin Group "espresso" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\misc\espresso\cofactor.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\cols.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\compl.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\contain.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\cubehack.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\cubestr.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\cvrin.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\cvrm.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\cvrmisc.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\cvrout.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\dominate.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\equiv.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\espresso.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\espresso.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\essen.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\exact.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\expand.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\gasp.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\gimpel.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\globals.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\hack.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\indep.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\irred.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\map.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\matrix.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\mincov.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\mincov.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\mincov_int.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\opo.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\pair.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\part.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\primes.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\reduce.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\rows.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\set.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\setc.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\sharp.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\sminterf.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\solution.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\sparse.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\sparse.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\sparse_int.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\unate.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\util_old.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\espresso\verify.c +# End Source File +# End Group +# Begin Group "nm" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\misc\nm\nm.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\nm\nmApi.c +# End Source File +# Begin Source File + +SOURCE=.\src\misc\nm\nmInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\nm\nmTable.c +# End Source File +# End Group +# Begin Group "hash" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\misc\hash\hash.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\hash\hashFlt.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\hash\hashInt.h +# End Source File +# Begin Source File + +SOURCE=.\src\misc\hash\hashPtr.h +# End Source File +# End Group +# End Group +# Begin Group "aig" + +# PROP Default_Filter "" +# Begin Group "hop" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\aig\hop\hop.h +# End Source File +# Begin Source File + +SOURCE=.\src\aig\hop\hopBalance.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\hop\hopCheck.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\hop\hopDfs.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\hop\hopMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\hop\hopMem.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\hop\hopObj.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\hop\hopOper.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\hop\hopTable.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\hop\hopUtil.c +# End Source File +# End Group +# Begin Group "ivy" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\aig\ivy\ivy.h +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyBalance.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyCanon.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyCheck.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyCut.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyCutTrav.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyDfs.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyDsd.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyFanout.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyFastMap.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyFraig.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyHaig.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyMem.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyMulti.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyObj.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyOper.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyResyn.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyRwr.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivySeq.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyShow.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyTable.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\ivy\ivyUtil.c +# End Source File +# End Group +# Begin Group "rwt" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\aig\rwt\rwt.h +# End Source File +# Begin Source File + +SOURCE=.\src\aig\rwt\rwtDec.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\rwt\rwtMan.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\rwt\rwtUtil.c +# End Source File +# End Group +# Begin Group "deco" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\aig\deco\deco.h +# End Source File +# End Group +# Begin Group "mem" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\aig\mem\mem.c +# End Source File +# Begin Source File + +SOURCE=.\src\aig\mem\mem.h +# End Source File +# End Group +# Begin Group "ec" + +# PROP Default_Filter "" +# End Group +# End Group +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# End Target +# End Project diff --git a/abc_with_bb_support/abclib.dsw b/abc_with_bb_support/abclib.dsw new file mode 100644 index 000000000..2e323b590 --- /dev/null +++ b/abc_with_bb_support/abclib.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "abclib"=.\abclib.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/abc_with_bb_support/abctestlib.dsp b/abc_with_bb_support/abctestlib.dsp new file mode 100644 index 000000000..3c8fdc4f3 --- /dev/null +++ b/abc_with_bb_support/abctestlib.dsp @@ -0,0 +1,102 @@ +# Microsoft Developer Studio Project File - Name="abctestlib" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=abctestlib - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "abctestlib.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "abctestlib.mak" CFG="abctestlib - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "abctestlib - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "abctestlib - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "abctestlib - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib C:\_projects\abc\abclib\abclib_release.lib /nologo /subsystem:console /machine:I386 /out:"_TEST/abctestlib.exe" + +!ELSEIF "$(CFG)" == "abctestlib - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib C:\_projects\abc\abclib\abclib_debug.lib /nologo /subsystem:console /debug /machine:I386 /out:"_TEST/abctestlib.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "abctestlib - Win32 Release" +# Name "abctestlib - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\demo.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/abc_with_bb_support/abctestlib.dsw b/abc_with_bb_support/abctestlib.dsw new file mode 100644 index 000000000..45048e1c2 --- /dev/null +++ b/abc_with_bb_support/abctestlib.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "abctestlib"=.\abctestlib.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/abc_with_bb_support/accum.blif b/abc_with_bb_support/accum.blif new file mode 100644 index 000000000..7c857cb3b --- /dev/null +++ b/abc_with_bb_support/accum.blif @@ -0,0 +1,88 @@ +.model accum +.inputs top^clock top^reset_n top^D~0 top^D~1 top^D~2 top^D~3 +.outputs top^Q~0 top^Q~1 top^Q~2 top^Q~3 + +.names gnd +.names hbpad +.names vcc +1 + +.names top^reset_n top^MULTI_PORT_MUX~0^LOGICAL_NOT~1 gnd top^ADD~2^ADDER_FUNC~15 top^MULTI_PORT_MUX~0^MUX_2~11 +1-1- 1 +-1-1 1 + +.latch top^MULTI_PORT_MUX~0^MUX_2~11 top^FF_NODE~3 re top^clock 0 + +.names gnd top^FF_NODE~3 top^D~0 top^ADD~2^ADDER_FUNC~15 +001 1 +010 1 +100 1 +111 1 + +.names gnd top^FF_NODE~3 top^D~0 top^ADD~2^CARRY_FUNC~16 +011 1 +100 1 +110 1 +111 1 + +.names top^ADD~2^CARRY_FUNC~16 top^FF_NODE~4 top^D~1 top^ADD~2^ADDER_FUNC~17 +001 1 +010 1 +100 1 +111 1 + +.names top^reset_n top^MULTI_PORT_MUX~0^LOGICAL_NOT~1 gnd top^ADD~2^ADDER_FUNC~17 top^MULTI_PORT_MUX~0^MUX_2~12 +1-1- 1 +-1-1 1 + +.latch top^MULTI_PORT_MUX~0^MUX_2~12 top^FF_NODE~4 re top^clock 0 + +.names top^ADD~2^CARRY_FUNC~16 top^FF_NODE~4 top^D~1 top^ADD~2^CARRY_FUNC~18 +011 1 +100 1 +110 1 +111 1 + +.names top^ADD~2^CARRY_FUNC~18 top^FF_NODE~5 top^D~2 top^ADD~2^ADDER_FUNC~19 +001 1 +010 1 +100 1 +111 1 + +.names top^reset_n top^MULTI_PORT_MUX~0^LOGICAL_NOT~1 gnd top^ADD~2^ADDER_FUNC~19 top^MULTI_PORT_MUX~0^MUX_2~13 +1-1- 1 +-1-1 1 + +.latch top^MULTI_PORT_MUX~0^MUX_2~13 top^FF_NODE~5 re top^clock 0 + +.names top^ADD~2^CARRY_FUNC~18 top^FF_NODE~5 top^D~2 top^ADD~2^CARRY_FUNC~20 +011 1 +100 1 +110 1 +111 1 + +.names top^ADD~2^CARRY_FUNC~20 top^FF_NODE~6 top^D~3 top^ADD~2^ADDER_FUNC~21 +001 1 +010 1 +100 1 +111 1 + +.names top^reset_n top^MULTI_PORT_MUX~0^LOGICAL_NOT~1 gnd top^ADD~2^ADDER_FUNC~21 top^MULTI_PORT_MUX~0^MUX_2~14 +1-1- 1 +-1-1 1 + +.latch top^MULTI_PORT_MUX~0^MUX_2~14 top^FF_NODE~6 re top^clock 0 + +.names top^reset_n top^MULTI_PORT_MUX~0^LOGICAL_NOT~1 +0 1 + +.names top^FF_NODE~3 top^Q~0 +1 1 +.names top^FF_NODE~4 top^Q~1 +1 1 +.names top^FF_NODE~5 top^Q~2 +1 1 +.names top^FF_NODE~6 top^Q~3 +1 1 +.end + diff --git a/abc_with_bb_support/accum.v b/abc_with_bb_support/accum.v new file mode 100644 index 000000000..b71bc4c87 --- /dev/null +++ b/abc_with_bb_support/accum.v @@ -0,0 +1,16 @@ +module accum (clock, reset_n, D, Q); +input clock, reset_n; +input [3:0] D; +output [3:0] Q; +reg [3:0] tmp; + + + always @(posedge clock) + begin + if (reset_n) + tmp <= 4'b0000; + else + tmp <= tmp + D; + end +assign Q = tmp; +endmodule \ No newline at end of file diff --git a/abc_with_bb_support/copyright.txt b/abc_with_bb_support/copyright.txt new file mode 100644 index 000000000..4afdc149c --- /dev/null +++ b/abc_with_bb_support/copyright.txt @@ -0,0 +1,18 @@ +Copyright (c) The Regents of the University of California. All rights reserved. + +Permission is hereby granted, without written agreement and without license or +royalty fees, to use, copy, modify, and distribute this software and its +documentation for any purpose, provided that the above copyright notice and +the following two paragraphs appear in all copies of this software. + +IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF +THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF +CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, +AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, +SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + diff --git a/abc_with_bb_support/demo.c b/abc_with_bb_support/demo.c new file mode 100644 index 000000000..9b0ef3340 --- /dev/null +++ b/abc_with_bb_support/demo.c @@ -0,0 +1,181 @@ +/**CFile**************************************************************** + + FileName [demo.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [ABC as a static library.] + + Synopsis [A demo program illustrating the use of ABC as a static library.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: demo.c,v 1.00 2005/11/14 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include +#include + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// procedures to start and stop the ABC framework +// (should be called before and after the ABC procedures are called) +extern void Abc_Start(); +extern void Abc_Stop(); + +// procedures to get the ABC framework and execute commands in it +extern void * Abc_FrameGetGlobalFrame(); +extern int Cmd_CommandExecute( void * pAbc, char * sCommand ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [The main() procedure.] + + Description [This procedure compiles into a stand-alone program for + DAG-aware rewriting of the AIGs. A BLIF or PLA file to be considered + for rewriting should be given as a command-line argument. Implementation + of the rewriting is inspired by the paper: Per Bjesse, Arne Boralv, + "DAG-aware circuit compression for formal verification", Proc. ICCAD 2004.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int main( int argc, char * argv[] ) +{ + // parameters + int fUseResyn2 = 0; + int fPrintStats = 1; + int fVerify = 1; + // variables + void * pAbc; + char * pFileName; + char Command[1000]; + int clkRead, clkResyn, clkVer, clk; + + ////////////////////////////////////////////////////////////////////////// + // get the input file name + if ( argc != 2 ) + { + printf( "Wrong number of command-line arguments.\n" ); + return 1; + } + pFileName = argv[1]; + + ////////////////////////////////////////////////////////////////////////// + // start the ABC framework + Abc_Start(); + pAbc = Abc_FrameGetGlobalFrame(); + +clk = clock(); + ////////////////////////////////////////////////////////////////////////// + // read the file + sprintf( Command, "read %s", pFileName ); + if ( Cmd_CommandExecute( pAbc, Command ) ) + { + fprintf( stdout, "Cannot execute command \"%s\".\n", Command ); + return 1; + } + + ////////////////////////////////////////////////////////////////////////// + // balance + sprintf( Command, "balance" ); + if ( Cmd_CommandExecute( pAbc, Command ) ) + { + fprintf( stdout, "Cannot execute command \"%s\".\n", Command ); + return 1; + } +clkRead = clock() - clk; + + ////////////////////////////////////////////////////////////////////////// + // print stats + if ( fPrintStats ) + { + sprintf( Command, "print_stats" ); + if ( Cmd_CommandExecute( pAbc, Command ) ) + { + fprintf( stdout, "Cannot execute command \"%s\".\n", Command ); + return 1; + } + } + +clk = clock(); + ////////////////////////////////////////////////////////////////////////// + // synthesize + if ( fUseResyn2 ) + { + sprintf( Command, "balance; rewrite -l; refactor -l; balance; rewrite -l; rewrite -lz; balance; refactor -lz; rewrite -lz; balance" ); + if ( Cmd_CommandExecute( pAbc, Command ) ) + { + fprintf( stdout, "Cannot execute command \"%s\".\n", Command ); + return 1; + } + } + else + { + sprintf( Command, "balance; rewrite -l; rewrite -lz; balance; rewrite -lz; balance" ); + if ( Cmd_CommandExecute( pAbc, Command ) ) + { + fprintf( stdout, "Cannot execute command \"%s\".\n", Command ); + return 1; + } + } +clkResyn = clock() - clk; + + ////////////////////////////////////////////////////////////////////////// + // print stats + if ( fPrintStats ) + { + sprintf( Command, "print_stats" ); + if ( Cmd_CommandExecute( pAbc, Command ) ) + { + fprintf( stdout, "Cannot execute command \"%s\".\n", Command ); + return 1; + } + } + + ////////////////////////////////////////////////////////////////////////// + // write the result in blif + sprintf( Command, "write_blif result.blif" ); + if ( Cmd_CommandExecute( pAbc, Command ) ) + { + fprintf( stdout, "Cannot execute command \"%s\".\n", Command ); + return 1; + } + + ////////////////////////////////////////////////////////////////////////// + // perform verification +clk = clock(); + if ( fVerify ) + { + sprintf( Command, "cec %s result.blif", pFileName ); + if ( Cmd_CommandExecute( pAbc, Command ) ) + { + fprintf( stdout, "Cannot execute command \"%s\".\n", Command ); + return 1; + } + } +clkVer = clock() - clk; + + printf( "Reading = %6.2f sec ", (float)(clkRead)/(float)(CLOCKS_PER_SEC) ); + printf( "Rewriting = %6.2f sec ", (float)(clkResyn)/(float)(CLOCKS_PER_SEC) ); + printf( "Verification = %6.2f sec\n", (float)(clkVer)/(float)(CLOCKS_PER_SEC) ); + + ////////////////////////////////////////////////////////////////////////// + // stop the ABC framework + Abc_Stop(); + return 0; +} + diff --git a/abc_with_bb_support/depends.sh b/abc_with_bb_support/depends.sh new file mode 100644 index 000000000..727e38cc8 --- /dev/null +++ b/abc_with_bb_support/depends.sh @@ -0,0 +1,13 @@ +#!/bin/sh +#echo "## Got: $*" +CC="$1" +DIR="$2" +shift 2 +case "$DIR" in + "" | ".") + $CC -MM -MG "$@" | sed -e 's@^\(.*\)\.o:@\1.d \1.o:@' + ;; + *) + $CC -MM -MG "$@" | sed -e "s@^\(.*\)\.o:@$DIR/\1.d $DIR/\1.o:@" + ;; +esac diff --git a/abc_with_bb_support/readme b/abc_with_bb_support/readme new file mode 100644 index 000000000..9c12c2591 --- /dev/null +++ b/abc_with_bb_support/readme @@ -0,0 +1,26 @@ +Often the code comes directly from a Windows computer. +The following steps may be needed to compile it on UNIX: + +>> dos2unix Makefile Makefile +>> dos2unix depends.sh depends.sh +>> chmod 755 depends.sh +>> make // on Solaris, try "gmake" + +If compiling as a static library, it is necessary to uncomment +#define _LIB in "src/abc/main/main.c" + +Several things to try if it does not compile on your platform: +- Try running all code (not only Makefile and depends.sh) through dos2unix +- Try the following actions: + (a) Remove flags from the libs line (LIBS :=) in Makefile + (b) Remove "src\base\main\libSupport.c" from "src\base\main\module.make" + (c) Comment calls to Libs_Init() and Libs_End() in "src\base\main\mainInit.c" +- Try linking with gcc (rather than g++) + For this replace "LD := g++" with "LD := gcc -lm" in Makefile +- If your Linux distributin does not have "readline", you may have problems + compiling ABC with gcc. Please try installing this library from + http://tiswww.case.edu/php/chet/readline/rltop.html + + +Finally, run regression test: +abc>>> so regtest.script diff --git a/abc_with_bb_support/regtest.script b/abc_with_bb_support/regtest.script new file mode 100644 index 000000000..bb07ad990 --- /dev/null +++ b/abc_with_bb_support/regtest.script @@ -0,0 +1,18 @@ +r examples/apex4.pla; resyn; if; cec; ps; clp; resyn; map; cec; ps +r examples/C2670.blif; st; w 1.aig; cec 1.aig +r examples/C2670.blif; st; short_names; w 1.bench; cec 1.bench +r examples/C2670.blif; st; short_names; ren -s; w 1.eqn; cec 1.eqn +r examples/C2670.blif; resyn2; if -K 8; cec; ps; u; map; cec; ps +r examples/frg2.blif; dsd; muxes; cec; ps; clp; share; resyn; map; cec; ps +r examples/frg2.blif; bdd; muxes; cec; ps; clp; st; ren -b; muxes; cec; ps +r examples/i10.blif; resyn2; fpga; cec; ps; u; map; cec; ps +r examples/i10.blif; choice; fpga; cec; ps; u; map; cec; ps +r examples/pj1.blif; st; if; cec; ps; u; map; cec; ps +r examples/s38417.blif; comb; w 1.blif; resyn; if; cec 1.blif; ps +r examples/s38417.blif; resyn; if; cec; ps; u; map; cec; ps +r examples/s38584.bench; resyn; ren -s; fx; if; cec; ps; u; map; cec; ps +r examples/s444.blif; b; esd -v; print_exdc; dsd; cec; ps +r examples/s444.blif; double; frames -F 5; w 1.blif; ffpga -K 8; cec 1.blif +r examples/s5378.blif; frames -F 5; cycle; w 1.blif; ps; ret; ps; sec 1.blif +r examples/s6669.blif; cycle; w 1.blif; ps; ret -M 3; resyn; ps; sec 1.blif +time diff --git a/abc_with_bb_support/regtest_output.txt b/abc_with_bb_support/regtest_output.txt new file mode 100644 index 000000000..93631e3eb --- /dev/null +++ b/abc_with_bb_support/regtest_output.txt @@ -0,0 +1,96 @@ +UC Berkeley, ABC 1.01 (compiled Dec 25 2006 17:15:00) +abc 01> so regtest.script +abc - > r examples/apex4.pla; resyn; if; cec; ps; clp; resyn; map; cec; ps +Networks are equivalent. +examples/apex4: i/o = 9/ 19 lat = 0 nd = 1172 aig = 4365 lev = 7 +The shared BDD size is 917 nodes. BDD construction time = 0.04 sec +A simple supergate library is derived from gate library "mcnc_temp.genlib". +Loaded 20 unique 5-input supergates from "mcnc_temp.super". Time = 0.02 sec +Networks are equivalent. +examples/apex4: i/o = 9/ 19 lat = 0 nd = 1734 aig = 2576 lev = 12 +abc - > r examples/C2670.blif; st; w 1.aig; cec 1.aig +Networks are equivalent after structural hashing. +abc - > r examples/C2670.blif; st; short_names; w 1.bench; cec 1.bench +Networks are equivalent after structural hashing. +abc - > r examples/C2670.blif; st; short_names; ren -s; w 1.eqn; cec 1.eqn +Networks are equivalent. +abc - > r examples/C2670.blif; resyn2; if -K 8; cec; ps; u; map; cec; ps +Networks are equivalent. +C2670.iscas : i/o = 233/ 140 lat = 0 nd = 120 aig = 1056 lev = 4 +Networks are equivalent. +C2670.iscas : i/o = 233/ 140 lat = 0 nd = 467 aig = 651 lev = 14 +abc - > r examples/frg2.blif; dsd; muxes; cec; ps; clp; share; resyn; map; cec; ps +Networks are equivalent. +frg2 : i/o = 143/ 139 lat = 0 nd = 1648 aig = 2268 lev = 18 +The shared BDD size is 1672 nodes. BDD construction time = 0.14 sec +Networks are equivalent. +frg2 : i/o = 143/ 139 lat = 0 nd = 533 aig = 778 lev = 8 +abc - > r examples/frg2.blif; bdd; muxes; cec; ps; clp; st; ren -b; muxes; cec; ps +Networks are equivalent. +frg2 : i/o = 143/ 139 lat = 0 nd = 2868 aig = 4221 lev = 38 +The shared BDD size is 1684 nodes. BDD construction time = 0.14 sec +Networks are equivalent. +frg2 : i/o = 143/ 139 lat = 0 nd = 2331 aig = 3180 lev = 20 +abc - > r examples/i10.blif; resyn2; fpga; cec; ps; u; map; cec; ps +Networks are equivalent. +i10 : i/o = 257/ 224 lat = 0 nd = 808 aig = 2630 lev = 12 +Networks are equivalent. +i10 : i/o = 257/ 224 lat = 0 nd = 1555 aig = 1980 lev = 24 +abc - > r examples/i10.blif; choice; fpga; cec; ps; u; map; cec; ps +Currently stored 3 networks with 5801 nodes will be fraiged. +Total fraiging time = 0.39 sec +Performing FPGA mapping with choices. +Networks are equivalent. +i10 : i/o = 257/ 224 lat = 0 nd = 798 aig = 2543 lev = 12 +Performing mapping with choices. +Networks are equivalent. +i10 : i/o = 257/ 224 lat = 0 nd = 1463 aig = 1993 lev = 23 +abc - > r examples/pj1.blif; st; if; cec; ps; u; map; cec; ps +Networks are equivalent after structural hashing. +exCombCkt : i/o = 1769/1063 lat = 0 nd = 5984 aig = 23156 lev = 52 +Networks are equivalent. +exCombCkt : i/o = 1769/1063 lat = 0 nd = 11474 aig = 16032 lev = 80 +abc - > r examples/s38417.blif; comb; w 1.blif; resyn; if; cec 1.blif; ps +examples/s38417.blif (line 14): Skipping directive ".wire_load_slope". +Networks are equivalent. +s38417 : i/o = 1664/1742 lat = 0 nd = 3479 aig = 10120 lev = 9 +abc - > r examples/s38417.blif; resyn; if; cec; ps; u; map; cec; ps +examples/s38417.blif (line 14): Skipping directive ".wire_load_slope". +examples/s38417.blif (line 14): Skipping directive ".wire_load_slope". +Networks are equivalent. +s38417 : i/o = 28/ 106 lat = 1636 nd = 3479 aig = 10120 lev = 9 +examples/s38417.blif (line 14): Skipping directive ".wire_load_slope". +Networks are equivalent. +s38417 : i/o = 28/ 106 lat = 1636 nd = 7189 aig = 8689 lev = 17 +abc - > r examples/s38584.bench; resyn; ren -s; fx; if; cec; ps; u; map; cec; ps +The network was strashed and balanced before FPGA mapping. +Networks are equivalent. +examples/s38584: i/o = 12/ 278 lat = 1452 nd = 4266 aig = 12569 lev = 10 +The network was strashed and balanced before mapping. +Networks are equivalent. +examples/s38584: i/o = 12/ 278 lat = 1452 nd = 8135 aig = 10674 lev = 18 +abc - > r examples/s444.blif; b; esd -v; print_exdc; dsd; cec; ps +The shared BDD size is 181 nodes. +BDD nodes in the transition relation before reordering 557. +BDD nodes in the transition relation after reordering 456. +Reachability analysis completed in 151 iterations. +The number of minterms in the reachable state set = 8865. +BDD nodes in the unreachable states before reordering 124. +BDD nodes in the unreachable states after reordering 113. +EXDC network statistics: +exdc : i/o = 21/ 21 lat = 0 nd = 21 cube = 86 lev = 2 +Networks are equivalent. +s444 : i/o = 3/ 6 lat = 21 nd = 82 aig = 176 lev = 7 +abc - > r examples/s444.blif; double; frames -F 5; w 1.blif; ffpga -K 8; cec 1.blif +Networks are equivalent after structural hashing. +abc - > r examples/s5378.blif; frames -F 5; cycle; w 1.blif; ps; ret; ps; sec 1.blif +s5378_5_frames: i/o = 175/ 245 lat = 164 and = 6629 (exor = 115) lev = 59 +s5378_5_frames: i/o = 175/ 245 lat = 182 nd = 6957 cube = 6956 lev = 50 +Networks are equivalent after framing. +abc - > r examples/s6669.blif; cycle; w 1.blif; ps; ret -M 3; resyn; ps; sec 1.blif +s6669 : i/o = 83/ 55 lat = 239 nd = 3080 cube = 3080 lev = 93 +s6669 : i/o = 83/ 55 lat = 183 and = 1915 (exor = 371) lev = 97 +Networks are equivalent after fraiging. +abc - > time +elapse: 44.07 seconds, total: 44.07 seconds +abc 150> \ No newline at end of file diff --git a/abc_with_bb_support/simple.blif b/abc_with_bb_support/simple.blif new file mode 100644 index 000000000..b01e3b00b --- /dev/null +++ b/abc_with_bb_support/simple.blif @@ -0,0 +1,29 @@ +.model simple +.inputs top^clock top^enable +.outputs top^value_out + +.names gnd +.names hbpad +.names vcc +1 + +.names top^LOGICAL_EQUAL~1^LOGICAL_AND~7 top^MULTI_PORT_MUX~0^LOGICAL_NOT~2 gnd top^FF_NODE~3 top^MULTI_PORT_MUX~0^MUX_2~5 +1-1- 1 +-1-1 1 + +.latch top^MULTI_PORT_MUX~0^MUX_2~5 top^FF_NODE~3 re top^clock 0 + +.names top^enable vcc top^LOGICAL_EQUAL~1^LOGICAL_XNOR~6^LOGICAL_XNOR~8 +00 1 +11 1 + +.names top^LOGICAL_EQUAL~1^LOGICAL_XNOR~6^LOGICAL_XNOR~8 top^LOGICAL_EQUAL~1^LOGICAL_AND~7 +1 1 + +.names top^LOGICAL_EQUAL~1^LOGICAL_AND~7 top^MULTI_PORT_MUX~0^LOGICAL_NOT~2 +0 1 + +.names top^FF_NODE~3 top^value_out +1 1 +.end + diff --git a/abc_with_bb_support/simple.v b/abc_with_bb_support/simple.v new file mode 100644 index 000000000..c2514898b --- /dev/null +++ b/abc_with_bb_support/simple.v @@ -0,0 +1,23 @@ +// Test enable circuitry + +module simple(clock, + enable, + value_out + ); + +input clock; +input enable; +reg temp; +output value_out; + +always @(posedge clock) +begin + if (enable == 1'b1) begin + temp <= 1'b0; + end +end + +assign value_out = temp; + +endmodule + diff --git a/abc_with_bb_support/src/aig/aig/aig.h b/abc_with_bb_support/src/aig/aig/aig.h new file mode 100644 index 000000000..4235fed2c --- /dev/null +++ b/abc_with_bb_support/src/aig/aig/aig.h @@ -0,0 +1,498 @@ +/**CFile**************************************************************** + + FileName [aig.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG package.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: aig.h,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __AIG_H__ +#define __AIG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +#include "vec.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Aig_Man_t_ Aig_Man_t; +typedef struct Aig_Obj_t_ Aig_Obj_t; +typedef struct Aig_MmFixed_t_ Aig_MmFixed_t; +typedef struct Aig_MmFlex_t_ Aig_MmFlex_t; +typedef struct Aig_MmStep_t_ Aig_MmStep_t; + +// object types +typedef enum { + AIG_OBJ_NONE, // 0: non-existent object + AIG_OBJ_CONST1, // 1: constant 1 + AIG_OBJ_PI, // 2: primary input + AIG_OBJ_PO, // 3: primary output + AIG_OBJ_BUF, // 4: buffer node + AIG_OBJ_AND, // 5: AND node + AIG_OBJ_EXOR, // 6: EXOR node + AIG_OBJ_LATCH, // 7: latch + AIG_OBJ_VOID // 8: unused object +} Aig_Type_t; + +// the AIG node +struct Aig_Obj_t_ // 8 words +{ + void * pData; // misc (cuts, copy, etc) + Aig_Obj_t * pNext; // strashing table + Aig_Obj_t * pFanin0; // fanin + Aig_Obj_t * pFanin1; // fanin + unsigned long Type : 3; // object type + unsigned long fPhase : 1; // value under 000...0 pattern + unsigned long fMarkA : 1; // multipurpose mask + unsigned long fMarkB : 1; // multipurpose mask + unsigned long nRefs : 26; // reference count + unsigned Level : 24; // the level of this node + unsigned nCuts : 8; // the number of cuts + int TravId; // unique ID of last traversal involving the node + int Id; // unique ID of the node +}; + +// the AIG manager +struct Aig_Man_t_ +{ + // AIG nodes + Vec_Ptr_t * vPis; // the array of PIs + Vec_Ptr_t * vPos; // the array of POs + Vec_Ptr_t * vObjs; // the array of all nodes (optional) + Vec_Ptr_t * vBufs; // the array of buffers + Aig_Obj_t * pConst1; // the constant 1 node + Aig_Obj_t Ghost; // the ghost node + int nRegs; // the number of registers (registers are last POs) + int nAsserts; // the number of asserts among POs (asserts are first POs) + // AIG node counters + int nObjs[AIG_OBJ_VOID];// the number of objects by type + int nCreated; // the number of created objects + int nDeleted; // the number of deleted objects + // structural hash table + Aig_Obj_t ** pTable; // structural hash table + int nTableSize; // structural hash table size + // representation of fanouts + int * pFanData; // the database to store fanout information + int nFansAlloc; // the size of fanout representation + Vec_Vec_t * vLevels; // used to update timing information + int nBufReplaces; // the number of times replacement led to a buffer + int nBufFixes; // the number of times buffers were propagated + int nBufMax; // the maximum number of buffers during computation + // topological order + unsigned * pOrderData; + int nOrderAlloc; + int iPrev; + int iNext; + int nAndTotal; + int nAndPrev; + // representatives + Aig_Obj_t ** pEquivs; // linked list of equivalent nodes (when choices are used) + Aig_Obj_t ** pReprs; // representatives of each node + int nReprsAlloc; // the number of allocated representatives + // various data members + Aig_MmFixed_t * pMemObjs; // memory manager for objects + Vec_Int_t * vLevelR; // the reverse level of the nodes + int nLevelMax; // maximum number of levels + void * pData; // the temporary data + int nTravIds; // the current traversal ID + int fCatchExor; // enables EXOR nodes + // timing statistics + int time1; + int time2; +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define AIG_MIN(a,b) (((a) < (b))? (a) : (b)) +#define AIG_MAX(a,b) (((a) > (b))? (a) : (b)) +#define AIG_ABS(a) (((a) >= 0)? (a) :-(a)) +#define AIG_INFINITY (100000000) + +#ifndef PRT +#define PRT(a,t) printf("%s = ", (a)); printf("%6.2f sec\n", (float)(t)/(float)(CLOCKS_PER_SEC)) +#endif + +static inline int Aig_BitWordNum( int nBits ) { return (nBits>>5) + ((nBits&31) > 0); } +static inline int Aig_TruthWordNum( int nVars ) { return nVars <= 5 ? 1 : (1 << (nVars - 5)); } +static inline int Aig_InfoHasBit( unsigned * p, int i ) { return (p[(i)>>5] & (1<<((i) & 31))) > 0; } +static inline void Aig_InfoSetBit( unsigned * p, int i ) { p[(i)>>5] |= (1<<((i) & 31)); } +static inline void Aig_InfoXorBit( unsigned * p, int i ) { p[(i)>>5] ^= (1<<((i) & 31)); } +static inline unsigned Aig_ObjCutSign( unsigned ObjId ) { return (1 << (ObjId & 31)); } + +static inline Aig_Obj_t * Aig_Regular( Aig_Obj_t * p ) { return (Aig_Obj_t *)((unsigned long)(p) & ~01); } +static inline Aig_Obj_t * Aig_Not( Aig_Obj_t * p ) { return (Aig_Obj_t *)((unsigned long)(p) ^ 01); } +static inline Aig_Obj_t * Aig_NotCond( Aig_Obj_t * p, int c ) { return (Aig_Obj_t *)((unsigned long)(p) ^ (c)); } +static inline int Aig_IsComplement( Aig_Obj_t * p ) { return (int )(((unsigned long)p) & 01); } + +static inline int Aig_ManPiNum( Aig_Man_t * p ) { return p->nObjs[AIG_OBJ_PI]; } +static inline int Aig_ManPoNum( Aig_Man_t * p ) { return p->nObjs[AIG_OBJ_PO]; } +static inline int Aig_ManBufNum( Aig_Man_t * p ) { return p->nObjs[AIG_OBJ_BUF]; } +static inline int Aig_ManAndNum( Aig_Man_t * p ) { return p->nObjs[AIG_OBJ_AND]; } +static inline int Aig_ManExorNum( Aig_Man_t * p ) { return p->nObjs[AIG_OBJ_EXOR]; } +static inline int Aig_ManLatchNum( Aig_Man_t * p ) { return p->nObjs[AIG_OBJ_LATCH]; } +static inline int Aig_ManNodeNum( Aig_Man_t * p ) { return p->nObjs[AIG_OBJ_AND]+p->nObjs[AIG_OBJ_EXOR]; } +static inline int Aig_ManGetCost( Aig_Man_t * p ) { return p->nObjs[AIG_OBJ_AND]+3*p->nObjs[AIG_OBJ_EXOR]; } +static inline int Aig_ManObjNum( Aig_Man_t * p ) { return p->nCreated - p->nDeleted; } +static inline int Aig_ManObjIdMax( Aig_Man_t * p ) { return Vec_PtrSize(p->vObjs); } +static inline int Aig_ManRegNum( Aig_Man_t * p ) { return p->nRegs; } + +static inline Aig_Obj_t * Aig_ManConst0( Aig_Man_t * p ) { return Aig_Not(p->pConst1); } +static inline Aig_Obj_t * Aig_ManConst1( Aig_Man_t * p ) { return p->pConst1; } +static inline Aig_Obj_t * Aig_ManGhost( Aig_Man_t * p ) { return &p->Ghost; } +static inline Aig_Obj_t * Aig_ManPi( Aig_Man_t * p, int i ) { return (Aig_Obj_t *)Vec_PtrEntry(p->vPis, i); } +static inline Aig_Obj_t * Aig_ManPo( Aig_Man_t * p, int i ) { return (Aig_Obj_t *)Vec_PtrEntry(p->vPos, i); } +static inline Aig_Obj_t * Aig_ManLo( Aig_Man_t * p, int i ) { return (Aig_Obj_t *)Vec_PtrEntry(p->vPis, Aig_ManPiNum(p)-Aig_ManRegNum(p)+i); } +static inline Aig_Obj_t * Aig_ManLi( Aig_Man_t * p, int i ) { return (Aig_Obj_t *)Vec_PtrEntry(p->vPos, Aig_ManPoNum(p)-Aig_ManRegNum(p)+i); } +static inline Aig_Obj_t * Aig_ManObj( Aig_Man_t * p, int i ) { return p->vObjs ? (Aig_Obj_t *)Vec_PtrEntry(p->vObjs, i) : NULL; } + +static inline Aig_Type_t Aig_ObjType( Aig_Obj_t * pObj ) { return (Aig_Type_t)pObj->Type; } +static inline int Aig_ObjIsNone( Aig_Obj_t * pObj ) { return pObj->Type == AIG_OBJ_NONE; } +static inline int Aig_ObjIsConst1( Aig_Obj_t * pObj ) { assert(!Aig_IsComplement(pObj)); return pObj->Type == AIG_OBJ_CONST1; } +static inline int Aig_ObjIsPi( Aig_Obj_t * pObj ) { return pObj->Type == AIG_OBJ_PI; } +static inline int Aig_ObjIsPo( Aig_Obj_t * pObj ) { return pObj->Type == AIG_OBJ_PO; } +static inline int Aig_ObjIsBuf( Aig_Obj_t * pObj ) { return pObj->Type == AIG_OBJ_BUF; } +static inline int Aig_ObjIsAnd( Aig_Obj_t * pObj ) { return pObj->Type == AIG_OBJ_AND; } +static inline int Aig_ObjIsExor( Aig_Obj_t * pObj ) { return pObj->Type == AIG_OBJ_EXOR; } +static inline int Aig_ObjIsLatch( Aig_Obj_t * pObj ) { return pObj->Type == AIG_OBJ_LATCH; } +static inline int Aig_ObjIsNode( Aig_Obj_t * pObj ) { return pObj->Type == AIG_OBJ_AND || pObj->Type == AIG_OBJ_EXOR; } +static inline int Aig_ObjIsTerm( Aig_Obj_t * pObj ) { return pObj->Type == AIG_OBJ_PI || pObj->Type == AIG_OBJ_PO || pObj->Type == AIG_OBJ_CONST1; } +static inline int Aig_ObjIsHash( Aig_Obj_t * pObj ) { return pObj->Type == AIG_OBJ_AND || pObj->Type == AIG_OBJ_EXOR || pObj->Type == AIG_OBJ_LATCH; } + +static inline int Aig_ObjIsMarkA( Aig_Obj_t * pObj ) { return pObj->fMarkA; } +static inline void Aig_ObjSetMarkA( Aig_Obj_t * pObj ) { pObj->fMarkA = 1; } +static inline void Aig_ObjClearMarkA( Aig_Obj_t * pObj ) { pObj->fMarkA = 0; } + +static inline void Aig_ObjSetTravId( Aig_Obj_t * pObj, int TravId ) { pObj->TravId = TravId; } +static inline void Aig_ObjSetTravIdCurrent( Aig_Man_t * p, Aig_Obj_t * pObj ) { pObj->TravId = p->nTravIds; } +static inline void Aig_ObjSetTravIdPrevious( Aig_Man_t * p, Aig_Obj_t * pObj ) { pObj->TravId = p->nTravIds - 1; } +static inline int Aig_ObjIsTravIdCurrent( Aig_Man_t * p, Aig_Obj_t * pObj ) { return (int)(pObj->TravId == p->nTravIds); } +static inline int Aig_ObjIsTravIdPrevious( Aig_Man_t * p, Aig_Obj_t * pObj ) { return (int)(pObj->TravId == p->nTravIds - 1); } + +static inline int Aig_ObjTravId( Aig_Obj_t * pObj ) { return (int)pObj->pData; } +static inline int Aig_ObjPhase( Aig_Obj_t * pObj ) { return pObj->fPhase; } +static inline int Aig_ObjPhaseReal( Aig_Obj_t * pObj ) { return pObj? Aig_Regular(pObj)->fPhase ^ Aig_IsComplement(pObj) : 1; } +static inline int Aig_ObjRefs( Aig_Obj_t * pObj ) { return pObj->nRefs; } +static inline void Aig_ObjRef( Aig_Obj_t * pObj ) { pObj->nRefs++; } +static inline void Aig_ObjDeref( Aig_Obj_t * pObj ) { assert( pObj->nRefs > 0 ); pObj->nRefs--; } +static inline void Aig_ObjClearRef( Aig_Obj_t * pObj ) { pObj->nRefs = 0; } +static inline int Aig_ObjFaninId0( Aig_Obj_t * pObj ) { return pObj->pFanin0? Aig_Regular(pObj->pFanin0)->Id : -1; } +static inline int Aig_ObjFaninId1( Aig_Obj_t * pObj ) { return pObj->pFanin1? Aig_Regular(pObj->pFanin1)->Id : -1; } +static inline int Aig_ObjFaninC0( Aig_Obj_t * pObj ) { return Aig_IsComplement(pObj->pFanin0); } +static inline int Aig_ObjFaninC1( Aig_Obj_t * pObj ) { return Aig_IsComplement(pObj->pFanin1); } +static inline Aig_Obj_t * Aig_ObjFanin0( Aig_Obj_t * pObj ) { return Aig_Regular(pObj->pFanin0); } +static inline Aig_Obj_t * Aig_ObjFanin1( Aig_Obj_t * pObj ) { return Aig_Regular(pObj->pFanin1); } +static inline Aig_Obj_t * Aig_ObjChild0( Aig_Obj_t * pObj ) { return pObj->pFanin0; } +static inline Aig_Obj_t * Aig_ObjChild1( Aig_Obj_t * pObj ) { return pObj->pFanin1; } +static inline Aig_Obj_t * Aig_ObjChild0Copy( Aig_Obj_t * pObj ) { assert( !Aig_IsComplement(pObj) ); return Aig_ObjFanin0(pObj)? Aig_NotCond((Aig_Obj_t *)Aig_ObjFanin0(pObj)->pData, Aig_ObjFaninC0(pObj)) : NULL; } +static inline Aig_Obj_t * Aig_ObjChild1Copy( Aig_Obj_t * pObj ) { assert( !Aig_IsComplement(pObj) ); return Aig_ObjFanin1(pObj)? Aig_NotCond((Aig_Obj_t *)Aig_ObjFanin1(pObj)->pData, Aig_ObjFaninC1(pObj)) : NULL; } +static inline int Aig_ObjLevel( Aig_Obj_t * pObj ) { return pObj->Level; } +static inline int Aig_ObjLevelNew( Aig_Obj_t * pObj ) { return Aig_ObjFanin1(pObj)? 1 + Aig_ObjIsExor(pObj) + AIG_MAX(Aig_ObjFanin0(pObj)->Level, Aig_ObjFanin1(pObj)->Level) : Aig_ObjFanin0(pObj)->Level; } +static inline void Aig_ObjClean( Aig_Obj_t * pObj ) { memset( pObj, 0, sizeof(Aig_Obj_t) ); } +static inline Aig_Obj_t * Aig_ObjFanout0( Aig_Man_t * p, Aig_Obj_t * pObj ) { assert(p->pFanData && pObj->Id < p->nFansAlloc); return Aig_ManObj(p, p->pFanData[5*pObj->Id] >> 1); } +static inline int Aig_ObjWhatFanin( Aig_Obj_t * pObj, Aig_Obj_t * pFanin ) +{ + if ( Aig_ObjFanin0(pObj) == pFanin ) return 0; + if ( Aig_ObjFanin1(pObj) == pFanin ) return 1; + assert(0); return -1; +} +static inline int Aig_ObjFanoutC( Aig_Obj_t * pObj, Aig_Obj_t * pFanout ) +{ + if ( Aig_ObjFanin0(pFanout) == pObj ) return Aig_ObjFaninC0(pObj); + if ( Aig_ObjFanin1(pFanout) == pObj ) return Aig_ObjFaninC1(pObj); + assert(0); return -1; +} + +// create the ghost of the new node +static inline Aig_Obj_t * Aig_ObjCreateGhost( Aig_Man_t * p, Aig_Obj_t * p0, Aig_Obj_t * p1, Aig_Type_t Type ) +{ + Aig_Obj_t * pGhost; + assert( Type != AIG_OBJ_AND || !Aig_ObjIsConst1(Aig_Regular(p0)) ); + assert( p1 == NULL || !Aig_ObjIsConst1(Aig_Regular(p1)) ); + assert( Type == AIG_OBJ_PI || Aig_Regular(p0) != Aig_Regular(p1) ); + pGhost = Aig_ManGhost(p); + pGhost->Type = Type; + if ( p1 == NULL || Aig_Regular(p0)->Id < Aig_Regular(p1)->Id ) + { + pGhost->pFanin0 = p0; + pGhost->pFanin1 = p1; + } + else + { + pGhost->pFanin0 = p1; + pGhost->pFanin1 = p0; + } + return pGhost; +} + +// internal memory manager +static inline Aig_Obj_t * Aig_ManFetchMemory( Aig_Man_t * p ) +{ + extern char * Aig_MmFixedEntryFetch( Aig_MmFixed_t * p ); + Aig_Obj_t * pTemp; + pTemp = (Aig_Obj_t *)Aig_MmFixedEntryFetch( p->pMemObjs ); + memset( pTemp, 0, sizeof(Aig_Obj_t) ); + Vec_PtrPush( p->vObjs, pTemp ); + pTemp->Id = p->nCreated++; + return pTemp; +} +static inline void Aig_ManRecycleMemory( Aig_Man_t * p, Aig_Obj_t * pEntry ) +{ + extern void Aig_MmFixedEntryRecycle( Aig_MmFixed_t * p, char * pEntry ); + assert( pEntry->nRefs == 0 ); + pEntry->Type = AIG_OBJ_NONE; // distinquishes a dead node from a live node + Aig_MmFixedEntryRecycle( p->pMemObjs, (char *)pEntry ); + p->nDeleted++; +} + + +//////////////////////////////////////////////////////////////////////// +/// ITERATORS /// +//////////////////////////////////////////////////////////////////////// + +// iterator over the primary inputs +#define Aig_ManForEachPi( p, pObj, i ) \ + Vec_PtrForEachEntry( p->vPis, pObj, i ) +// iterator over the primary outputs +#define Aig_ManForEachPo( p, pObj, i ) \ + Vec_PtrForEachEntry( p->vPos, pObj, i ) +// iterator over all objects, including those currently not used +#define Aig_ManForEachObj( p, pObj, i ) \ + Vec_PtrForEachEntry( p->vObjs, pObj, i ) if ( (pObj) == NULL ) {} else +// iterator over all nodes +#define Aig_ManForEachNode( p, pObj, i ) \ + Vec_PtrForEachEntry( p->vObjs, pObj, i ) if ( (pObj) == NULL || !Aig_ObjIsNode(pObj) ) {} else +// iterator over the nodes whose IDs are stored in the array +#define Aig_ManForEachNodeVec( p, vIds, pObj, i ) \ + for ( i = 0; i < Vec_IntSize(vIds) && ((pObj) = Aig_ManObj(p, Vec_IntEntry(vIds,i))); i++ ) +// iterator over the nodes in the topological order +#define Aig_ManForEachNodeInOrder( p, pObj ) \ + for ( assert(p->pOrderData), p->iPrev = 0, p->iNext = p->pOrderData[1]; \ + p->iNext && (((pObj) = Aig_ManObj(p, p->iNext)), 1); \ + p->iNext = p->pOrderData[2*p->iPrev+1] ) + +// these two procedures are only here for the use inside the iterator +static inline int Aig_ObjFanout0Int( Aig_Man_t * p, int ObjId ) { assert(ObjId < p->nFansAlloc); return p->pFanData[5*ObjId]; } +static inline int Aig_ObjFanoutNext( Aig_Man_t * p, int iFan ) { assert(iFan/2 < p->nFansAlloc); return p->pFanData[5*(iFan >> 1) + 3 + (iFan & 1)]; } +// iterator over the fanouts +#define Aig_ObjForEachFanout( p, pObj, pFanout, iFan, i ) \ + for ( assert(p->pFanData), i = 0; (i < (int)(pObj)->nRefs) && \ + (((iFan) = i? Aig_ObjFanoutNext(p, iFan) : Aig_ObjFanout0Int(p, pObj->Id)), 1) && \ + (((pFanout) = Aig_ManObj(p, iFan>>1)), 1); i++ ) + + +//////////////////////////////////////////////////////////////////////// +/// SEQUENTIAL ITERATORS /// +//////////////////////////////////////////////////////////////////////// + +// iterator over the primary inputs +#define Aig_ManForEachPiSeq( p, pObj, i ) \ + Vec_PtrForEachEntryStop( p->vPis, pObj, i, Aig_ManPiNum(p)-Aig_ManRegNum(p) ) +// iterator over the latch outputs +#define Aig_ManForEachLoSeq( p, pObj, i ) \ + Vec_PtrForEachEntryStart( p->vPis, pObj, i, Aig_ManPiNum(p)-Aig_ManRegNum(p) ) +// iterator over the primary outputs +#define Aig_ManForEachPoSeq( p, pObj, i ) \ + Vec_PtrForEachEntryStop( p->vPos, pObj, i, Aig_ManPoNum(p)-Aig_ManRegNum(p) ) +// iterator over the latch inputs +#define Aig_ManForEachLiSeq( p, pObj, i ) \ + Vec_PtrForEachEntryStart( p->vPos, pObj, i, Aig_ManPoNum(p)-Aig_ManRegNum(p) ) +// iterator over the latch input and outputs +#define Aig_ManForEachLiLoSeq( p, pObjLi, pObjLo, k ) \ + for ( k = 0; (k < Aig_ManRegNum(p)) && (((pObjLi) = Aig_ManLi(p, k)), 1) \ + && (((pObjLo)=Aig_ManLo(p, k)), 1); k++ ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== aigCheck.c ========================================================*/ +extern int Aig_ManCheck( Aig_Man_t * p ); +extern void Aig_ManCheckMarkA( Aig_Man_t * p ); +extern void Aig_ManCheckPhase( Aig_Man_t * p ); +/*=== aigDfs.c ==========================================================*/ +extern Vec_Ptr_t * Aig_ManDfs( Aig_Man_t * p ); +extern Vec_Ptr_t * Aig_ManDfsNodes( Aig_Man_t * p, Aig_Obj_t ** ppNodes, int nNodes ); +extern Vec_Ptr_t * Aig_ManDfsChoices( Aig_Man_t * p ); +extern Vec_Ptr_t * Aig_ManDfsReverse( Aig_Man_t * p ); +extern int Aig_ManCountLevels( Aig_Man_t * p ); +extern int Aig_DagSize( Aig_Obj_t * pObj ); +extern void Aig_ConeUnmark_rec( Aig_Obj_t * pObj ); +extern Aig_Obj_t * Aig_Transfer( Aig_Man_t * pSour, Aig_Man_t * pDest, Aig_Obj_t * pObj, int nVars ); +extern Aig_Obj_t * Aig_Compose( Aig_Man_t * p, Aig_Obj_t * pRoot, Aig_Obj_t * pFunc, int iVar ); +extern void Aig_ObjCollectCut( Aig_Obj_t * pRoot, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vNodes ); +extern int Aig_ObjCollectSuper( Aig_Obj_t * pObj, Vec_Ptr_t * vSuper ); +/*=== aigFanout.c ==========================================================*/ +extern void Aig_ObjAddFanout( Aig_Man_t * p, Aig_Obj_t * pObj, Aig_Obj_t * pFanout ); +extern void Aig_ObjRemoveFanout( Aig_Man_t * p, Aig_Obj_t * pObj, Aig_Obj_t * pFanout ); +extern void Aig_ManFanoutStart( Aig_Man_t * p ); +extern void Aig_ManFanoutStop( Aig_Man_t * p ); +/*=== aigMan.c ==========================================================*/ +extern Aig_Man_t * Aig_ManStart( int nNodesMax ); +extern Aig_Man_t * Aig_ManStartFrom( Aig_Man_t * p ); +extern Aig_Man_t * Aig_ManDup( Aig_Man_t * p, int fOrdered ); +extern Aig_Man_t * Aig_ManExtractMiter( Aig_Man_t * p, Aig_Obj_t * pNode1, Aig_Obj_t * pNode2 ); +extern void Aig_ManStop( Aig_Man_t * p ); +extern int Aig_ManCleanup( Aig_Man_t * p ); +extern int Aig_ManSeqCleanup( Aig_Man_t * p ); +extern void Aig_ManPrintStats( Aig_Man_t * p ); +/*=== aigMem.c ==========================================================*/ +extern void Aig_ManStartMemory( Aig_Man_t * p ); +extern void Aig_ManStopMemory( Aig_Man_t * p ); +/*=== aigMffc.c ==========================================================*/ +extern int Aig_NodeRef_rec( Aig_Obj_t * pNode, unsigned LevelMin ); +extern int Aig_NodeDeref_rec( Aig_Obj_t * pNode, unsigned LevelMin ); +extern int Aig_NodeMffsSupp( Aig_Man_t * p, Aig_Obj_t * pNode, int LevelMin, Vec_Ptr_t * vSupp ); +extern int Aig_NodeMffsLabel( Aig_Man_t * p, Aig_Obj_t * pNode ); +extern int Aig_NodeMffsLabelCut( Aig_Man_t * p, Aig_Obj_t * pNode, Vec_Ptr_t * vLeaves ); +extern int Aig_NodeMffsExtendCut( Aig_Man_t * p, Aig_Obj_t * pNode, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vResult ); +/*=== aigObj.c ==========================================================*/ +extern Aig_Obj_t * Aig_ObjCreatePi( Aig_Man_t * p ); +extern Aig_Obj_t * Aig_ObjCreatePo( Aig_Man_t * p, Aig_Obj_t * pDriver ); +extern Aig_Obj_t * Aig_ObjCreate( Aig_Man_t * p, Aig_Obj_t * pGhost ); +extern void Aig_ObjConnect( Aig_Man_t * p, Aig_Obj_t * pObj, Aig_Obj_t * pFan0, Aig_Obj_t * pFan1 ); +extern void Aig_ObjDisconnect( Aig_Man_t * p, Aig_Obj_t * pObj ); +extern void Aig_ObjDelete( Aig_Man_t * p, Aig_Obj_t * pObj ); +extern void Aig_ObjDelete_rec( Aig_Man_t * p, Aig_Obj_t * pObj, int fFreeTop ); +extern void Aig_ObjPatchFanin0( Aig_Man_t * p, Aig_Obj_t * pObj, Aig_Obj_t * pFaninNew ); +extern void Aig_ObjReplace( Aig_Man_t * p, Aig_Obj_t * pObjOld, Aig_Obj_t * pObjNew, int fNodesOnly, int fUpdateLevel ); +/*=== aigOper.c =========================================================*/ +extern Aig_Obj_t * Aig_IthVar( Aig_Man_t * p, int i ); +extern Aig_Obj_t * Aig_Oper( Aig_Man_t * p, Aig_Obj_t * p0, Aig_Obj_t * p1, Aig_Type_t Type ); +extern Aig_Obj_t * Aig_And( Aig_Man_t * p, Aig_Obj_t * p0, Aig_Obj_t * p1 ); +extern Aig_Obj_t * Aig_Latch( Aig_Man_t * p, Aig_Obj_t * pObj, int fInitOne ); +extern Aig_Obj_t * Aig_Or( Aig_Man_t * p, Aig_Obj_t * p0, Aig_Obj_t * p1 ); +extern Aig_Obj_t * Aig_Exor( Aig_Man_t * p, Aig_Obj_t * p0, Aig_Obj_t * p1 ); +extern Aig_Obj_t * Aig_Mux( Aig_Man_t * p, Aig_Obj_t * pC, Aig_Obj_t * p1, Aig_Obj_t * p0 ); +extern Aig_Obj_t * Aig_Maj( Aig_Man_t * p, Aig_Obj_t * pA, Aig_Obj_t * pB, Aig_Obj_t * pC ); +extern Aig_Obj_t * Aig_Miter( Aig_Man_t * p, Vec_Ptr_t * vPairs ); +extern Aig_Obj_t * Aig_MiterTwo( Aig_Man_t * p, Vec_Ptr_t * vNodes1, Vec_Ptr_t * vNodes2 ); +extern Aig_Obj_t * Aig_CreateAnd( Aig_Man_t * p, int nVars ); +extern Aig_Obj_t * Aig_CreateOr( Aig_Man_t * p, int nVars ); +extern Aig_Obj_t * Aig_CreateExor( Aig_Man_t * p, int nVars ); +/*=== aigOrder.c =========================================================*/ +extern void Aig_ManOrderStart( Aig_Man_t * p ); +extern void Aig_ManOrderStop( Aig_Man_t * p ); +extern void Aig_ObjOrderInsert( Aig_Man_t * p, int ObjId ); +extern void Aig_ObjOrderRemove( Aig_Man_t * p, int ObjId ); +extern void Aig_ObjOrderAdvance( Aig_Man_t * p ); +/*=== aigPart.c =========================================================*/ +extern Vec_Vec_t * Aig_ManSupports( Aig_Man_t * pMan ); +extern Vec_Vec_t * Aig_ManPartitionSmart( Aig_Man_t * p, int nPartSizeLimit, int fVerbose, Vec_Vec_t ** pvPartSupps ); +extern Vec_Vec_t * Aig_ManPartitionNaive( Aig_Man_t * p, int nPartSize ); +/*=== aigRepr.c =========================================================*/ +extern void Aig_ManReprStart( Aig_Man_t * p, int nIdMax ); +extern void Aig_ManReprStop( Aig_Man_t * p ); +extern void Aig_ObjCreateRepr( Aig_Man_t * p, Aig_Obj_t * pNode1, Aig_Obj_t * pNode2 ); +extern void Aig_ManTransferRepr( Aig_Man_t * pNew, Aig_Man_t * p ); +extern Aig_Man_t * Aig_ManDupRepr( Aig_Man_t * p ); +extern Aig_Man_t * Aig_ManRehash( Aig_Man_t * p ); +extern void Aig_ManCreateChoices( Aig_Man_t * p ); +/*=== aigSeq.c ========================================================*/ +extern int Aig_ManSeqStrash( Aig_Man_t * p, int nLatches, int * pInits ); +/*=== aigShow.c ========================================================*/ +extern void Aig_ManShow( Aig_Man_t * pMan, int fHaig, Vec_Ptr_t * vBold ); +/*=== aigTable.c ========================================================*/ +extern Aig_Obj_t * Aig_TableLookup( Aig_Man_t * p, Aig_Obj_t * pGhost ); +extern Aig_Obj_t * Aig_TableLookupTwo( Aig_Man_t * p, Aig_Obj_t * pFanin0, Aig_Obj_t * pFanin1 ); +extern void Aig_TableInsert( Aig_Man_t * p, Aig_Obj_t * pObj ); +extern void Aig_TableDelete( Aig_Man_t * p, Aig_Obj_t * pObj ); +extern int Aig_TableCountEntries( Aig_Man_t * p ); +extern void Aig_TableProfile( Aig_Man_t * p ); +/*=== aigTiming.c ========================================================*/ +extern void Aig_ObjClearReverseLevel( Aig_Man_t * p, Aig_Obj_t * pObj ); +extern int Aig_ObjRequiredLevel( Aig_Man_t * p, Aig_Obj_t * pObj ); +extern void Aig_ManStartReverseLevels( Aig_Man_t * p, int nMaxLevelIncrease ); +extern void Aig_ManStopReverseLevels( Aig_Man_t * p ); +extern void Aig_ManUpdateLevel( Aig_Man_t * p, Aig_Obj_t * pObjNew ); +extern void Aig_ManUpdateReverseLevel( Aig_Man_t * p, Aig_Obj_t * pObjNew ); +extern void Aig_ManVerifyLevel( Aig_Man_t * p ); +extern void Aig_ManVerifyReverseLevel( Aig_Man_t * p ); +/*=== aigTruth.c ========================================================*/ +extern unsigned * Aig_ManCutTruth( Aig_Obj_t * pRoot, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vNodes, Vec_Ptr_t * vTruthElem, Vec_Ptr_t * vTruthStore ); +/*=== aigUtil.c =========================================================*/ +extern unsigned Aig_PrimeCudd( unsigned p ); +extern void Aig_ManIncrementTravId( Aig_Man_t * p ); +extern int Aig_ManLevels( Aig_Man_t * p ); +extern void Aig_ManResetRefs( Aig_Man_t * p ); +extern void Aig_ManCleanMarkA( Aig_Man_t * p ); +extern void Aig_ManCleanMarkB( Aig_Man_t * p ); +extern void Aig_ManCleanData( Aig_Man_t * p ); +extern void Aig_ObjCleanData_rec( Aig_Obj_t * pObj ); +extern void Aig_ObjCollectMulti( Aig_Obj_t * pFunc, Vec_Ptr_t * vSuper ); +extern int Aig_ObjIsMuxType( Aig_Obj_t * pObj ); +extern int Aig_ObjRecognizeExor( Aig_Obj_t * pObj, Aig_Obj_t ** ppFan0, Aig_Obj_t ** ppFan1 ); +extern Aig_Obj_t * Aig_ObjRecognizeMux( Aig_Obj_t * pObj, Aig_Obj_t ** ppObjT, Aig_Obj_t ** ppObjE ); +extern Aig_Obj_t * Aig_ObjReal_rec( Aig_Obj_t * pObj ); +extern void Aig_ObjPrintEqn( FILE * pFile, Aig_Obj_t * pObj, Vec_Vec_t * vLevels, int Level ); +extern void Aig_ObjPrintVerilog( FILE * pFile, Aig_Obj_t * pObj, Vec_Vec_t * vLevels, int Level ); +extern void Aig_ObjPrintVerbose( Aig_Obj_t * pObj, int fHaig ); +extern void Aig_ManPrintVerbose( Aig_Man_t * p, int fHaig ); +extern void Aig_ManDump( Aig_Man_t * p ); +extern void Aig_ManDumpBlif( Aig_Man_t * p, char * pFileName ); +/*=== aigWin.c =========================================================*/ +extern void Aig_ManFindCut( Aig_Obj_t * pRoot, Vec_Ptr_t * vFront, Vec_Ptr_t * vVisited, int nSizeLimit, int nFanoutLimit ); + +/*=== aigMem.c ===========================================================*/ +// fixed-size-block memory manager +extern Aig_MmFixed_t * Aig_MmFixedStart( int nEntrySize, int nEntriesMax ); +extern void Aig_MmFixedStop( Aig_MmFixed_t * p, int fVerbose ); +extern char * Aig_MmFixedEntryFetch( Aig_MmFixed_t * p ); +extern void Aig_MmFixedEntryRecycle( Aig_MmFixed_t * p, char * pEntry ); +extern void Aig_MmFixedRestart( Aig_MmFixed_t * p ); +extern int Aig_MmFixedReadMemUsage( Aig_MmFixed_t * p ); +extern int Aig_MmFixedReadMaxEntriesUsed( Aig_MmFixed_t * p ); +// flexible-size-block memory manager +extern Aig_MmFlex_t * Aig_MmFlexStart(); +extern void Aig_MmFlexStop( Aig_MmFlex_t * p, int fVerbose ); +extern char * Aig_MmFlexEntryFetch( Aig_MmFlex_t * p, int nBytes ); +extern void Aig_MmFlexRestart( Aig_MmFlex_t * p ); +extern int Aig_MmFlexReadMemUsage( Aig_MmFlex_t * p ); +// hierarchical memory manager +extern Aig_MmStep_t * Aig_MmStepStart( int nSteps ); +extern void Aig_MmStepStop( Aig_MmStep_t * p, int fVerbose ); +extern char * Aig_MmStepEntryFetch( Aig_MmStep_t * p, int nBytes ); +extern void Aig_MmStepEntryRecycle( Aig_MmStep_t * p, char * pEntry, int nBytes ); +extern int Aig_MmStepReadMemUsage( Aig_MmStep_t * p ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/aig/aig/aigCheck.c b/abc_with_bb_support/src/aig/aig/aigCheck.c new file mode 100644 index 000000000..ff6f23f71 --- /dev/null +++ b/abc_with_bb_support/src/aig/aig/aigCheck.c @@ -0,0 +1,162 @@ +/**CFile**************************************************************** + + FileName [aigCheck.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG package.] + + Synopsis [AIG checking procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: aigCheck.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "aig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Checks the consistency of the AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ManCheck( Aig_Man_t * p ) +{ + Aig_Obj_t * pObj, * pObj2; + int i; + // check primary inputs + Aig_ManForEachPi( p, pObj, i ) + { + if ( Aig_ObjFanin0(pObj) || Aig_ObjFanin1(pObj) ) + { + printf( "Aig_ManCheck: The PI node \"%p\" has fanins.\n", pObj ); + return 0; + } + } + // check primary outputs + Aig_ManForEachPo( p, pObj, i ) + { + if ( !Aig_ObjFanin0(pObj) ) + { + printf( "Aig_ManCheck: The PO node \"%p\" has NULL fanin.\n", pObj ); + return 0; + } + if ( Aig_ObjFanin1(pObj) ) + { + printf( "Aig_ManCheck: The PO node \"%p\" has second fanin.\n", pObj ); + return 0; + } + } + // check internal nodes + Aig_ManForEachObj( p, pObj, i ) + { + if ( !Aig_ObjIsNode(pObj) ) + continue; + if ( !Aig_ObjFanin0(pObj) || !Aig_ObjFanin1(pObj) ) + { + printf( "Aig_ManCheck: The AIG has internal node \"%p\" with a NULL fanin.\n", pObj ); + return 0; + } + if ( Aig_ObjFanin0(pObj)->Id >= Aig_ObjFanin1(pObj)->Id ) + { + printf( "Aig_ManCheck: The AIG has node \"%p\" with a wrong ordering of fanins.\n", pObj ); + return 0; + } + pObj2 = Aig_TableLookup( p, pObj ); + if ( pObj2 != pObj ) + { + printf( "Aig_ManCheck: Node \"%p\" is not in the structural hashing table.\n", pObj ); + return 0; + } + } + // count the total number of nodes + if ( Aig_ManObjNum(p) != 1 + Aig_ManPiNum(p) + Aig_ManPoNum(p) + Aig_ManBufNum(p) + Aig_ManAndNum(p) + Aig_ManExorNum(p) + Aig_ManLatchNum(p) ) + { + printf( "Aig_ManCheck: The number of created nodes is wrong.\n" ); + printf( "C1 = %d. Pi = %d. Po = %d. Buf = %d. And = %d. Xor = %d. Lat = %d. Total = %d.\n", + 1, Aig_ManPiNum(p), Aig_ManPoNum(p), Aig_ManBufNum(p), Aig_ManAndNum(p), Aig_ManExorNum(p), Aig_ManLatchNum(p), + 1 + Aig_ManPiNum(p) + Aig_ManPoNum(p) + Aig_ManBufNum(p) + Aig_ManAndNum(p) + Aig_ManExorNum(p) + Aig_ManLatchNum(p) ); + printf( "Created = %d. Deleted = %d. Existing = %d.\n", + p->nCreated, p->nDeleted, p->nCreated - p->nDeleted ); + return 0; + } + // count the number of nodes in the table + if ( Aig_TableCountEntries(p) != Aig_ManAndNum(p) + Aig_ManExorNum(p) + Aig_ManLatchNum(p) ) + { + printf( "Aig_ManCheck: The number of nodes in the structural hashing table is wrong.\n" ); + printf( "Entries = %d. And = %d. Xor = %d. Lat = %d. Total = %d.\n", + Aig_TableCountEntries(p), Aig_ManAndNum(p), Aig_ManExorNum(p), Aig_ManLatchNum(p), + Aig_ManAndNum(p) + Aig_ManExorNum(p) + Aig_ManLatchNum(p) ); + + return 0; + } +// if ( !Aig_ManIsAcyclic(p) ) +// return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Checks if the markA is reset.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManCheckMarkA( Aig_Man_t * p ) +{ + Aig_Obj_t * pObj; + int i; + Aig_ManForEachObj( p, pObj, i ) + assert( pObj->fMarkA == 0 ); +} + +/**Function************************************************************* + + Synopsis [Checks the consistency of phase assignment.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManCheckPhase( Aig_Man_t * p ) +{ + Aig_Obj_t * pObj; + int i; + Aig_ManForEachObj( p, pObj, i ) + if ( Aig_ObjIsPi(pObj) ) + assert( (int)pObj->fPhase == 0 ); + else + assert( (int)pObj->fPhase == (Aig_ObjPhaseReal(Aig_ObjChild0(pObj)) & Aig_ObjPhaseReal(Aig_ObjChild1(pObj))) ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/aig/aigDfs.c b/abc_with_bb_support/src/aig/aig/aigDfs.c new file mode 100644 index 000000000..cdba18753 --- /dev/null +++ b/abc_with_bb_support/src/aig/aig/aigDfs.c @@ -0,0 +1,631 @@ +/**CFile**************************************************************** + + FileName [aigDfs.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG package.] + + Synopsis [DFS traversal procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: aigDfs.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "aig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Collects internal nodes in the DFS order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManDfs_rec( Aig_Man_t * p, Aig_Obj_t * pObj, Vec_Ptr_t * vNodes ) +{ + if ( pObj == NULL ) + return; + assert( !Aig_IsComplement(pObj) ); + if ( Aig_ObjIsTravIdCurrent(p, pObj) ) + return; + assert( Aig_ObjIsNode(pObj) || Aig_ObjIsBuf(pObj) ); + Aig_ManDfs_rec( p, Aig_ObjFanin0(pObj), vNodes ); + Aig_ManDfs_rec( p, Aig_ObjFanin1(pObj), vNodes ); + assert( !Aig_ObjIsTravIdCurrent(p, pObj) ); // loop detection + Aig_ObjSetTravIdCurrent(p, pObj); + Vec_PtrPush( vNodes, pObj ); +} + +/**Function************************************************************* + + Synopsis [Collects internal nodes in the DFS order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Aig_ManDfs( Aig_Man_t * p ) +{ + Vec_Ptr_t * vNodes; + Aig_Obj_t * pObj; + int i; + Aig_ManIncrementTravId( p ); + // mark constant and PIs + Aig_ObjSetTravIdCurrent( p, Aig_ManConst1(p) ); + Aig_ManForEachPi( p, pObj, i ) + Aig_ObjSetTravIdCurrent( p, pObj ); + // if there are latches, mark them + if ( Aig_ManLatchNum(p) > 0 ) + Aig_ManForEachObj( p, pObj, i ) + if ( Aig_ObjIsLatch(pObj) ) + Aig_ObjSetTravIdCurrent( p, pObj ); + // go through the nodes + vNodes = Vec_PtrAlloc( Aig_ManNodeNum(p) ); + Aig_ManForEachObj( p, pObj, i ) + if ( Aig_ObjIsNode(pObj) || Aig_ObjIsBuf(pObj) ) + Aig_ManDfs_rec( p, pObj, vNodes ); + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Collects internal nodes in the DFS order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Aig_ManDfsNodes( Aig_Man_t * p, Aig_Obj_t ** ppNodes, int nNodes ) +{ + Vec_Ptr_t * vNodes; + Aig_Obj_t * pObj; + int i; + assert( Aig_ManLatchNum(p) == 0 ); + Aig_ManIncrementTravId( p ); + // mark constant and PIs + Aig_ObjSetTravIdCurrent( p, Aig_ManConst1(p) ); + Aig_ManForEachPi( p, pObj, i ) + Aig_ObjSetTravIdCurrent( p, pObj ); + // go through the nodes + vNodes = Vec_PtrAlloc( Aig_ManNodeNum(p) ); + for ( i = 0; i < nNodes; i++ ) + Aig_ManDfs_rec( p, ppNodes[i], vNodes ); + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Collects internal nodes in the DFS order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManDfsChoices_rec( Aig_Man_t * p, Aig_Obj_t * pObj, Vec_Ptr_t * vNodes ) +{ + if ( pObj == NULL ) + return; + assert( !Aig_IsComplement(pObj) ); + if ( Aig_ObjIsTravIdCurrent(p, pObj) ) + return; + assert( Aig_ObjIsNode(pObj) ); + Aig_ManDfsChoices_rec( p, Aig_ObjFanin0(pObj), vNodes ); + Aig_ManDfsChoices_rec( p, Aig_ObjFanin1(pObj), vNodes ); + Aig_ManDfsChoices_rec( p, p->pEquivs[pObj->Id], vNodes ); + assert( !Aig_ObjIsTravIdCurrent(p, pObj) ); // loop detection + Aig_ObjSetTravIdCurrent(p, pObj); + Vec_PtrPush( vNodes, pObj ); +} + +/**Function************************************************************* + + Synopsis [Collects internal nodes in the DFS order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Aig_ManDfsChoices( Aig_Man_t * p ) +{ + Vec_Ptr_t * vNodes; + Aig_Obj_t * pObj; + int i; + assert( p->pEquivs != NULL ); + Aig_ManIncrementTravId( p ); + // mark constant and PIs + Aig_ObjSetTravIdCurrent( p, Aig_ManConst1(p) ); + Aig_ManForEachPi( p, pObj, i ) + Aig_ObjSetTravIdCurrent( p, pObj ); + // go through the nodes + vNodes = Vec_PtrAlloc( Aig_ManNodeNum(p) ); + Aig_ManForEachPo( p, pObj, i ) + Aig_ManDfsChoices_rec( p, Aig_ObjFanin0(pObj), vNodes ); + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Collects internal nodes in the reverse DFS order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManDfsReverse_rec( Aig_Man_t * p, Aig_Obj_t * pObj, Vec_Ptr_t * vNodes ) +{ + Aig_Obj_t * pFanout; + int iFanout, i; + assert( !Aig_IsComplement(pObj) ); + if ( Aig_ObjIsTravIdCurrent(p, pObj) ) + return; + assert( Aig_ObjIsNode(pObj) || Aig_ObjIsBuf(pObj) ); + Aig_ObjForEachFanout( p, pObj, pFanout, iFanout, i ) + Aig_ManDfsReverse_rec( p, pFanout, vNodes ); + assert( !Aig_ObjIsTravIdCurrent(p, pObj) ); // loop detection + Aig_ObjSetTravIdCurrent(p, pObj); + Vec_PtrPush( vNodes, pObj ); +} + +/**Function************************************************************* + + Synopsis [Collects internal nodes in the reverse DFS order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Aig_ManDfsReverse( Aig_Man_t * p ) +{ + Vec_Ptr_t * vNodes; + Aig_Obj_t * pObj; + int i; + Aig_ManIncrementTravId( p ); + // mark POs + Aig_ManForEachPo( p, pObj, i ) + Aig_ObjSetTravIdCurrent( p, pObj ); + // if there are latches, mark them + if ( Aig_ManLatchNum(p) > 0 ) + Aig_ManForEachObj( p, pObj, i ) + if ( Aig_ObjIsLatch(pObj) ) + Aig_ObjSetTravIdCurrent( p, pObj ); + // go through the nodes + vNodes = Vec_PtrAlloc( Aig_ManNodeNum(p) ); + Aig_ManForEachObj( p, pObj, i ) + if ( Aig_ObjIsNode(pObj) || Aig_ObjIsBuf(pObj) ) + Aig_ManDfsReverse_rec( p, pObj, vNodes ); + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Computes the max number of levels in the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ManCountLevels( Aig_Man_t * p ) +{ + Vec_Ptr_t * vNodes; + Aig_Obj_t * pObj; + int i, LevelsMax, Level0, Level1; + // initialize the levels + Aig_ManConst1(p)->pData = NULL; + Aig_ManForEachPi( p, pObj, i ) + pObj->pData = NULL; + // compute levels in a DFS order + vNodes = Aig_ManDfs( p ); + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + Level0 = (int)Aig_ObjFanin0(pObj)->pData; + Level1 = (int)Aig_ObjFanin1(pObj)->pData; + pObj->pData = (void *)(1 + Aig_ObjIsExor(pObj) + AIG_MAX(Level0, Level1)); + } + Vec_PtrFree( vNodes ); + // get levels of the POs + LevelsMax = 0; + Aig_ManForEachPo( p, pObj, i ) + LevelsMax = AIG_MAX( LevelsMax, (int)Aig_ObjFanin0(pObj)->pData ); + return LevelsMax; +} + +/**Function************************************************************* + + Synopsis [Counts the number of AIG nodes rooted at this cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ConeMark_rec( Aig_Obj_t * pObj ) +{ + assert( !Aig_IsComplement(pObj) ); + if ( !Aig_ObjIsNode(pObj) || Aig_ObjIsMarkA(pObj) ) + return; + Aig_ConeMark_rec( Aig_ObjFanin0(pObj) ); + Aig_ConeMark_rec( Aig_ObjFanin1(pObj) ); + assert( !Aig_ObjIsMarkA(pObj) ); // loop detection + Aig_ObjSetMarkA( pObj ); +} + +/**Function************************************************************* + + Synopsis [Counts the number of AIG nodes rooted at this cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ConeCleanAndMark_rec( Aig_Obj_t * pObj ) +{ + assert( !Aig_IsComplement(pObj) ); + if ( !Aig_ObjIsNode(pObj) || Aig_ObjIsMarkA(pObj) ) + return; + Aig_ConeCleanAndMark_rec( Aig_ObjFanin0(pObj) ); + Aig_ConeCleanAndMark_rec( Aig_ObjFanin1(pObj) ); + assert( !Aig_ObjIsMarkA(pObj) ); // loop detection + Aig_ObjSetMarkA( pObj ); + pObj->pData = NULL; +} + +/**Function************************************************************* + + Synopsis [Counts the number of AIG nodes rooted at this cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ConeCountAndMark_rec( Aig_Obj_t * pObj ) +{ + int Counter; + assert( !Aig_IsComplement(pObj) ); + if ( !Aig_ObjIsNode(pObj) || Aig_ObjIsMarkA(pObj) ) + return 0; + Counter = 1 + Aig_ConeCountAndMark_rec( Aig_ObjFanin0(pObj) ) + + Aig_ConeCountAndMark_rec( Aig_ObjFanin1(pObj) ); + assert( !Aig_ObjIsMarkA(pObj) ); // loop detection + Aig_ObjSetMarkA( pObj ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Counts the number of AIG nodes rooted at this cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ConeUnmark_rec( Aig_Obj_t * pObj ) +{ + assert( !Aig_IsComplement(pObj) ); + if ( !Aig_ObjIsNode(pObj) || !Aig_ObjIsMarkA(pObj) ) + return; + Aig_ConeUnmark_rec( Aig_ObjFanin0(pObj) ); + Aig_ConeUnmark_rec( Aig_ObjFanin1(pObj) ); + assert( Aig_ObjIsMarkA(pObj) ); // loop detection + Aig_ObjClearMarkA( pObj ); +} + +/**Function************************************************************* + + Synopsis [Counts the number of AIG nodes rooted at this cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_DagSize( Aig_Obj_t * pObj ) +{ + int Counter; + Counter = Aig_ConeCountAndMark_rec( Aig_Regular(pObj) ); + Aig_ConeUnmark_rec( Aig_Regular(pObj) ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Transfers the AIG from one manager into another.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_Transfer_rec( Aig_Man_t * pDest, Aig_Obj_t * pObj ) +{ + assert( !Aig_IsComplement(pObj) ); + if ( !Aig_ObjIsNode(pObj) || Aig_ObjIsMarkA(pObj) ) + return; + Aig_Transfer_rec( pDest, Aig_ObjFanin0(pObj) ); + Aig_Transfer_rec( pDest, Aig_ObjFanin1(pObj) ); + pObj->pData = Aig_And( pDest, Aig_ObjChild0Copy(pObj), Aig_ObjChild1Copy(pObj) ); + assert( !Aig_ObjIsMarkA(pObj) ); // loop detection + Aig_ObjSetMarkA( pObj ); +} + +/**Function************************************************************* + + Synopsis [Transfers the AIG from one manager into another.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_Transfer( Aig_Man_t * pSour, Aig_Man_t * pDest, Aig_Obj_t * pRoot, int nVars ) +{ + Aig_Obj_t * pObj; + int i; + // solve simple cases + if ( pSour == pDest ) + return pRoot; + if ( Aig_ObjIsConst1( Aig_Regular(pRoot) ) ) + return Aig_NotCond( Aig_ManConst1(pDest), Aig_IsComplement(pRoot) ); + // set the PI mapping + Aig_ManForEachPi( pSour, pObj, i ) + { + if ( i == nVars ) + break; + pObj->pData = Aig_IthVar(pDest, i); + } + // transfer and set markings + Aig_Transfer_rec( pDest, Aig_Regular(pRoot) ); + // clear the markings + Aig_ConeUnmark_rec( Aig_Regular(pRoot) ); + return Aig_NotCond( Aig_Regular(pRoot)->pData, Aig_IsComplement(pRoot) ); +} + +/**Function************************************************************* + + Synopsis [Composes the AIG (pRoot) with the function (pFunc) using PI var (iVar).] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_Compose_rec( Aig_Man_t * p, Aig_Obj_t * pObj, Aig_Obj_t * pFunc, Aig_Obj_t * pVar ) +{ + assert( !Aig_IsComplement(pObj) ); + if ( Aig_ObjIsMarkA(pObj) ) + return; + if ( Aig_ObjIsConst1(pObj) || Aig_ObjIsPi(pObj) ) + { + pObj->pData = pObj == pVar ? pFunc : pObj; + return; + } + Aig_Compose_rec( p, Aig_ObjFanin0(pObj), pFunc, pVar ); + Aig_Compose_rec( p, Aig_ObjFanin1(pObj), pFunc, pVar ); + pObj->pData = Aig_And( p, Aig_ObjChild0Copy(pObj), Aig_ObjChild1Copy(pObj) ); + assert( !Aig_ObjIsMarkA(pObj) ); // loop detection + Aig_ObjSetMarkA( pObj ); +} + +/**Function************************************************************* + + Synopsis [Composes the AIG (pRoot) with the function (pFunc) using PI var (iVar).] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_Compose( Aig_Man_t * p, Aig_Obj_t * pRoot, Aig_Obj_t * pFunc, int iVar ) +{ + // quit if the PI variable is not defined + if ( iVar >= Aig_ManPiNum(p) ) + { + printf( "Aig_Compose(): The PI variable %d is not defined.\n", iVar ); + return NULL; + } + // recursively perform composition + Aig_Compose_rec( p, Aig_Regular(pRoot), pFunc, Aig_ManPi(p, iVar) ); + // clear the markings + Aig_ConeUnmark_rec( Aig_Regular(pRoot) ); + return Aig_NotCond( Aig_Regular(pRoot)->pData, Aig_IsComplement(pRoot) ); +} + +/**Function************************************************************* + + Synopsis [Computes the internal nodes of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ObjCollectCut_rec( Aig_Obj_t * pNode, Vec_Ptr_t * vNodes ) +{ +// Aig_Obj_t * pFan0 = Aig_ObjFanin0(pNode); +// Aig_Obj_t * pFan1 = Aig_ObjFanin1(pNode); + if ( pNode->fMarkA ) + return; + pNode->fMarkA = 1; + assert( Aig_ObjIsNode(pNode) ); + Aig_ObjCollectCut_rec( Aig_ObjFanin0(pNode), vNodes ); + Aig_ObjCollectCut_rec( Aig_ObjFanin1(pNode), vNodes ); + Vec_PtrPush( vNodes, pNode ); +//printf( "added %d ", pNode->Id ); +} + +/**Function************************************************************* + + Synopsis [Computes the internal nodes of the cut.] + + Description [Does not include the leaves of the cut.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ObjCollectCut( Aig_Obj_t * pRoot, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vNodes ) +{ + Aig_Obj_t * pObj; + int i; + // collect and mark the leaves + Vec_PtrClear( vNodes ); + Vec_PtrForEachEntry( vLeaves, pObj, i ) + { + assert( pObj->fMarkA == 0 ); + pObj->fMarkA = 1; +// printf( "%d " , pObj->Id ); + } +//printf( "\n" ); + // collect and mark the nodes + Aig_ObjCollectCut_rec( pRoot, vNodes ); + // clean the nodes + Vec_PtrForEachEntry( vNodes, pObj, i ) + pObj->fMarkA = 0; + Vec_PtrForEachEntry( vLeaves, pObj, i ) + pObj->fMarkA = 0; +} + + +/**Function************************************************************* + + Synopsis [Collects the nodes of the supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ObjCollectSuper_rec( Aig_Obj_t * pRoot, Aig_Obj_t * pObj, Vec_Ptr_t * vSuper ) +{ + int RetValue1, RetValue2, i; + // check if the node is visited + if ( Aig_Regular(pObj)->fMarkA ) + { + // check if the node occurs in the same polarity + for ( i = 0; i < vSuper->nSize; i++ ) + if ( vSuper->pArray[i] == pObj ) + return 1; + // check if the node is present in the opposite polarity + for ( i = 0; i < vSuper->nSize; i++ ) + if ( vSuper->pArray[i] == Aig_Not(pObj) ) + return -1; + assert( 0 ); + return 0; + } + // if the new node is complemented or a PI, another gate begins + if ( pObj != pRoot && (Aig_IsComplement(pObj) || Aig_ObjType(pObj) != Aig_ObjType(pRoot) || Aig_ObjRefs(pObj) > 1) ) + { + Vec_PtrPush( vSuper, pObj ); + Aig_Regular(pObj)->fMarkA = 1; + return 0; + } + assert( !Aig_IsComplement(pObj) ); + assert( Aig_ObjIsNode(pObj) ); + // go through the branches + RetValue1 = Aig_ObjCollectSuper_rec( pRoot, Aig_ObjReal_rec( Aig_ObjChild0(pObj) ), vSuper ); + RetValue2 = Aig_ObjCollectSuper_rec( pRoot, Aig_ObjReal_rec( Aig_ObjChild1(pObj) ), vSuper ); + if ( RetValue1 == -1 || RetValue2 == -1 ) + return -1; + // return 1 if at least one branch has a duplicate + return RetValue1 || RetValue2; +} + +/**Function************************************************************* + + Synopsis [Collects the nodes of the supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ObjCollectSuper( Aig_Obj_t * pObj, Vec_Ptr_t * vSuper ) +{ + int RetValue, i; + assert( !Aig_IsComplement(pObj) ); + assert( Aig_ObjIsNode(pObj) ); + // collect the nodes in the implication supergate + Vec_PtrClear( vSuper ); + RetValue = Aig_ObjCollectSuper_rec( pObj, pObj, vSuper ); + assert( Vec_PtrSize(vSuper) > 1 ); + // unmark the visited nodes + Vec_PtrForEachEntry( vSuper, pObj, i ) + Aig_Regular(pObj)->fMarkA = 0; + // if we found the node and its complement in the same implication supergate, + // return empty set of nodes (meaning that we should use constant-0 node) + if ( RetValue == -1 ) + vSuper->nSize = 0; + return RetValue; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/aig/aigFanout.c b/abc_with_bb_support/src/aig/aig/aigFanout.c new file mode 100644 index 000000000..f3502fdf7 --- /dev/null +++ b/abc_with_bb_support/src/aig/aig/aigFanout.c @@ -0,0 +1,189 @@ +/**CFile**************************************************************** + + FileName [aigFanout.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG package.] + + Synopsis [Fanout manipulation.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: aigFanout.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "aig.h" + +// 0: first iFan +// 1: prev iFan0 +// 2: prev iFan1 +// 3: next iFan0 +// 4: next iFan1 + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static inline int Aig_FanoutCreate( int FanId, int Num ) { assert( Num < 2 ); return (FanId << 1) | Num; } +static inline int * Aig_FanoutObj( int * pData, int ObjId ) { return pData + 5*ObjId; } +static inline int * Aig_FanoutPrev( int * pData, int iFan ) { return pData + 5*(iFan >> 1) + 1 + (iFan & 1); } +static inline int * Aig_FanoutNext( int * pData, int iFan ) { return pData + 5*(iFan >> 1) + 3 + (iFan & 1); } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Create fanout for all objects in the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManFanoutStart( Aig_Man_t * p ) +{ + Aig_Obj_t * pObj; + int i; + assert( Aig_ManBufNum(p) == 0 ); + // allocate fanout datastructure + assert( p->pFanData == NULL ); + p->nFansAlloc = 2 * Aig_ManObjIdMax(p); + if ( p->nFansAlloc < (1<<12) ) + p->nFansAlloc = (1<<12); + p->pFanData = ALLOC( int, 5 * p->nFansAlloc ); + memset( p->pFanData, 0, sizeof(int) * 5 * p->nFansAlloc ); + // add fanouts for all objects + Aig_ManForEachObj( p, pObj, i ) + { + if ( Aig_ObjChild0(pObj) ) + Aig_ObjAddFanout( p, Aig_ObjFanin0(pObj), pObj ); + if ( Aig_ObjChild1(pObj) ) + Aig_ObjAddFanout( p, Aig_ObjFanin1(pObj), pObj ); + } +} + +/**Function************************************************************* + + Synopsis [Deletes fanout for all objects in the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManFanoutStop( Aig_Man_t * p ) +{ + assert( p->pFanData != NULL ); + FREE( p->pFanData ); + p->nFansAlloc = 0; +} + +/**Function************************************************************* + + Synopsis [Adds fanout (pFanout) of node (pObj).] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ObjAddFanout( Aig_Man_t * p, Aig_Obj_t * pObj, Aig_Obj_t * pFanout ) +{ + int iFan, * pFirst, * pPrevC, * pNextC, * pPrev, * pNext; + assert( p->pFanData ); + assert( !Aig_IsComplement(pObj) && !Aig_IsComplement(pFanout) ); + assert( pFanout->Id > 0 ); + if ( pObj->Id >= p->nFansAlloc || pFanout->Id >= p->nFansAlloc ) + { + int nFansAlloc = 2 * AIG_MAX( pObj->Id, pFanout->Id ); + p->pFanData = REALLOC( int, p->pFanData, 5 * nFansAlloc ); + memset( p->pFanData + 5 * p->nFansAlloc, 0, sizeof(int) * 5 * (nFansAlloc - p->nFansAlloc) ); + p->nFansAlloc = nFansAlloc; + } + assert( pObj->Id < p->nFansAlloc && pFanout->Id < p->nFansAlloc ); + iFan = Aig_FanoutCreate( pFanout->Id, Aig_ObjWhatFanin(pFanout, pObj) ); + pPrevC = Aig_FanoutPrev( p->pFanData, iFan ); + pNextC = Aig_FanoutNext( p->pFanData, iFan ); + pFirst = Aig_FanoutObj( p->pFanData, pObj->Id ); + if ( *pFirst == 0 ) + { + *pFirst = iFan; + *pPrevC = iFan; + *pNextC = iFan; + } + else + { + pPrev = Aig_FanoutPrev( p->pFanData, *pFirst ); + pNext = Aig_FanoutNext( p->pFanData, *pPrev ); + assert( *pNext == *pFirst ); + *pPrevC = *pPrev; + *pNextC = *pFirst; + *pPrev = iFan; + *pNext = iFan; + } +} + +/**Function************************************************************* + + Synopsis [Removes fanout (pFanout) of node (pObj).] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ObjRemoveFanout( Aig_Man_t * p, Aig_Obj_t * pObj, Aig_Obj_t * pFanout ) +{ + int iFan, * pFirst, * pPrevC, * pNextC, * pPrev, * pNext; + assert( p->pFanData && pObj->Id < p->nFansAlloc && pFanout->Id < p->nFansAlloc ); + assert( !Aig_IsComplement(pObj) && !Aig_IsComplement(pFanout) ); + assert( pFanout->Id > 0 ); + iFan = Aig_FanoutCreate( pFanout->Id, Aig_ObjWhatFanin(pFanout, pObj) ); + pPrevC = Aig_FanoutPrev( p->pFanData, iFan ); + pNextC = Aig_FanoutNext( p->pFanData, iFan ); + pPrev = Aig_FanoutPrev( p->pFanData, *pNextC ); + pNext = Aig_FanoutNext( p->pFanData, *pPrevC ); + assert( *pPrev == iFan ); + assert( *pNext == iFan ); + pFirst = Aig_FanoutObj( p->pFanData, pObj->Id ); + assert( *pFirst > 0 ); + if ( *pFirst == iFan ) + { + if ( *pNextC == iFan ) + { + *pFirst = 0; + *pPrev = 0; + *pNext = 0; + *pPrevC = 0; + *pNextC = 0; + return; + } + *pFirst = *pNextC; + } + *pPrev = *pPrevC; + *pNext = *pNextC; + *pPrevC = 0; + *pNextC = 0; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/aig/aigMan.c b/abc_with_bb_support/src/aig/aig/aigMan.c new file mode 100644 index 000000000..e3acd5f6a --- /dev/null +++ b/abc_with_bb_support/src/aig/aig/aigMan.c @@ -0,0 +1,414 @@ +/**CFile**************************************************************** + + FileName [aigMan.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG package.] + + Synopsis [AIG manager.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: aigMan.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "aig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the AIG manager.] + + Description [The argument of this procedure is a soft limit on the + the number of nodes, or 0 if the limit is unknown.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Man_t * Aig_ManStart( int nNodesMax ) +{ + Aig_Man_t * p; + if ( nNodesMax <= 0 ) + nNodesMax = 10007; + // start the manager + p = ALLOC( Aig_Man_t, 1 ); + memset( p, 0, sizeof(Aig_Man_t) ); + // perform initializations + p->nTravIds = 1; + p->fCatchExor = 0; + // allocate arrays for nodes + p->vPis = Vec_PtrAlloc( 100 ); + p->vPos = Vec_PtrAlloc( 100 ); + p->vObjs = Vec_PtrAlloc( 1000 ); + p->vBufs = Vec_PtrAlloc( 100 ); + // prepare the internal memory manager + p->pMemObjs = Aig_MmFixedStart( sizeof(Aig_Obj_t), nNodesMax ); + // create the constant node + p->pConst1 = Aig_ManFetchMemory( p ); + p->pConst1->Type = AIG_OBJ_CONST1; + p->pConst1->fPhase = 1; + p->nObjs[AIG_OBJ_CONST1]++; + // start the table + p->nTableSize = Aig_PrimeCudd( nNodesMax ); + p->pTable = ALLOC( Aig_Obj_t *, p->nTableSize ); + memset( p->pTable, 0, sizeof(Aig_Obj_t *) * p->nTableSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Duplicates the AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Man_t * Aig_ManStartFrom( Aig_Man_t * p ) +{ + Aig_Man_t * pNew; + Aig_Obj_t * pObj; + int i; + // create the new manager + pNew = Aig_ManStart( Aig_ManObjIdMax(p) + 1 ); + // create the PIs + Aig_ManConst1(p)->pData = Aig_ManConst1(pNew); + Aig_ManForEachPi( p, pObj, i ) + pObj->pData = Aig_ObjCreatePi(pNew); + return pNew; +} + +/**Function************************************************************* + + Synopsis [Duplicates the AIG manager recursively.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_ManDup_rec( Aig_Man_t * pNew, Aig_Man_t * p, Aig_Obj_t * pObj ) +{ + if ( pObj->pData ) + return pObj->pData; + Aig_ManDup_rec( pNew, p, Aig_ObjFanin0(pObj) ); + if ( Aig_ObjIsBuf(pObj) ) + return pObj->pData = Aig_ObjChild0Copy(pObj); + Aig_ManDup_rec( pNew, p, Aig_ObjFanin1(pObj) ); + return pObj->pData = Aig_And( pNew, Aig_ObjChild0Copy(pObj), Aig_ObjChild1Copy(pObj) ); +} + +/**Function************************************************************* + + Synopsis [Duplicates the AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Man_t * Aig_ManDup( Aig_Man_t * p, int fOrdered ) +{ + Aig_Man_t * pNew; + Aig_Obj_t * pObj; + int i; + // create the new manager + pNew = Aig_ManStart( Aig_ManObjIdMax(p) + 1 ); + pNew->nRegs = p->nRegs; + pNew->nAsserts = p->nAsserts; + // create the PIs + Aig_ManCleanData( p ); + Aig_ManConst1(p)->pData = Aig_ManConst1(pNew); + Aig_ManForEachPi( p, pObj, i ) + pObj->pData = Aig_ObjCreatePi(pNew); + // duplicate internal nodes + if ( fOrdered ) + { + Aig_ManForEachObj( p, pObj, i ) + if ( Aig_ObjIsBuf(pObj) ) + pObj->pData = Aig_ObjChild0Copy(pObj); + else if ( Aig_ObjIsNode(pObj) ) + pObj->pData = Aig_And( pNew, Aig_ObjChild0Copy(pObj), Aig_ObjChild1Copy(pObj) ); + } + else + { + Aig_ManForEachObj( p, pObj, i ) + if ( !Aig_ObjIsPo(pObj) ) + { + Aig_ManDup_rec( pNew, p, pObj ); + assert( pObj->Level == ((Aig_Obj_t*)pObj->pData)->Level ); + } + } + // add the POs + Aig_ManForEachPo( p, pObj, i ) + Aig_ObjCreatePo( pNew, Aig_ObjChild0Copy(pObj) ); + assert( Aig_ManBufNum(p) != 0 || Aig_ManNodeNum(p) == Aig_ManNodeNum(pNew) ); + // check the resulting network + if ( !Aig_ManCheck(pNew) ) + printf( "Aig_ManDup(): The check has failed.\n" ); + return pNew; +} + +/**Function************************************************************* + + Synopsis [Extracts the miter composed of XOR of the two nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Man_t * Aig_ManExtractMiter( Aig_Man_t * p, Aig_Obj_t * pNode1, Aig_Obj_t * pNode2 ) +{ + Aig_Man_t * pNew; + Aig_Obj_t * pObj; + int i; + // create the new manager + pNew = Aig_ManStart( Aig_ManObjIdMax(p) + 1 ); + // create the PIs + Aig_ManCleanData( p ); + Aig_ManConst1(p)->pData = Aig_ManConst1(pNew); + Aig_ManForEachPi( p, pObj, i ) + pObj->pData = Aig_ObjCreatePi(pNew); + // dump the nodes + Aig_ManDup_rec( pNew, p, pNode1 ); + Aig_ManDup_rec( pNew, p, pNode2 ); + // construct the EXOR + pObj = Aig_Exor( pNew, pNode1->pData, pNode2->pData ); + pObj = Aig_NotCond( pObj, Aig_Regular(pObj)->fPhase ^ Aig_IsComplement(pObj) ); + // add the PO + Aig_ObjCreatePo( pNew, pObj ); + // check the resulting network + if ( !Aig_ManCheck(pNew) ) + printf( "Aig_ManDup(): The check has failed.\n" ); + return pNew; +} + + +/**Function************************************************************* + + Synopsis [Stops the AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManStop( Aig_Man_t * p ) +{ + Aig_Obj_t * pObj; + int i; + // print time + if ( p->time1 ) { PRT( "time1", p->time1 ); } + if ( p->time2 ) { PRT( "time2", p->time2 ); } + // delete fanout + if ( p->pFanData ) + Aig_ManFanoutStop( p ); + // make sure the nodes have clean marks + Aig_ManForEachObj( p, pObj, i ) + assert( !pObj->fMarkA && !pObj->fMarkB ); +// Aig_TableProfile( p ); + Aig_MmFixedStop( p->pMemObjs, 0 ); + if ( p->vPis ) Vec_PtrFree( p->vPis ); + if ( p->vPos ) Vec_PtrFree( p->vPos ); + if ( p->vObjs ) Vec_PtrFree( p->vObjs ); + if ( p->vBufs ) Vec_PtrFree( p->vBufs ); + if ( p->vLevelR ) Vec_IntFree( p->vLevelR ); + if ( p->vLevels ) Vec_VecFree( p->vLevels ); + FREE( p->pReprs ); + FREE( p->pEquivs ); + free( p->pTable ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Returns the number of dangling nodes removed.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManSeqCleanup_rec( Aig_Man_t * p, Aig_Obj_t * pObj, Vec_Ptr_t * vNodes ) +{ + if ( Aig_ObjIsTravIdCurrent(p, pObj) ) + return; + Aig_ObjSetTravIdCurrent(p, pObj); + // collect latch input corresponding to unmarked PI (latch output) + if ( Aig_ObjIsPi(pObj) ) + { + Vec_PtrPush( vNodes, pObj->pNext ); + return; + } + if ( Aig_ObjIsPo(pObj) ) + { + Aig_ManSeqCleanup_rec( p, Aig_ObjFanin0(pObj), vNodes ); + return; + } + assert( Aig_ObjIsNode(pObj) ); + Aig_ManSeqCleanup_rec( p, Aig_ObjFanin0(pObj), vNodes ); + Aig_ManSeqCleanup_rec( p, Aig_ObjFanin1(pObj), vNodes ); +} + +/**Function************************************************************* + + Synopsis [Returns the number of dangling nodes removed.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ManSeqCleanup( Aig_Man_t * p ) +{ + Vec_Ptr_t * vNodes, * vCis, * vCos; + Aig_Obj_t * pObj, * pObjLi, * pObjLo; + int i, nTruePis, nTruePos; + assert( Aig_ManBufNum(p) == 0 ); + + // mark the PIs + Aig_ManIncrementTravId( p ); + Aig_ObjSetTravIdCurrent( p, Aig_ManConst1(p) ); + Aig_ManForEachPiSeq( p, pObj, i ) + Aig_ObjSetTravIdCurrent( p, pObj ); + + // prepare to collect nodes reachable from POs + vNodes = Vec_PtrAlloc( 100 ); + Aig_ManForEachPoSeq( p, pObj, i ) + Vec_PtrPush( vNodes, pObj ); + + // remember latch inputs in latch outputs + Aig_ManForEachLiLoSeq( p, pObjLi, pObjLo, i ) + pObjLo->pNext = pObjLi; + // mark the nodes reachable from these nodes + Vec_PtrForEachEntry( vNodes, pObj, i ) + Aig_ManSeqCleanup_rec( p, pObj, vNodes ); + assert( Vec_PtrSize(vNodes) <= Aig_ManPoNum(p) ); + // clean latch output pointers + Aig_ManForEachLiLoSeq( p, pObjLi, pObjLo, i ) + pObjLo->pNext = NULL; + + // if some latches are removed, update PIs/POs + if ( Vec_PtrSize(vNodes) < Aig_ManPoNum(p) ) + { + // collect new CIs/COs + vCis = Vec_PtrAlloc( Aig_ManPiNum(p) ); + Aig_ManForEachPi( p, pObj, i ) + if ( Aig_ObjIsTravIdCurrent(p, pObj) ) + Vec_PtrPush( vCis, pObj ); + vCos = Vec_PtrAlloc( Aig_ManPoNum(p) ); + Aig_ManForEachPo( p, pObj, i ) + if ( Aig_ObjIsTravIdCurrent(p, pObj) ) + Vec_PtrPush( vCos, pObj ); + else + Aig_ObjDisconnect( p, pObj ); + // remember the number of true PIs/POs + nTruePis = Aig_ManPiNum(p) - Aig_ManRegNum(p); + nTruePos = Aig_ManPoNum(p) - Aig_ManRegNum(p); + // set the new number of registers + p->nRegs -= Aig_ManPoNum(p) - Vec_PtrSize(vNodes); + // create new PIs/POs + assert( Vec_PtrSize(vCis) == nTruePis + p->nRegs ); + assert( Vec_PtrSize(vCos) == nTruePos + p->nRegs ); + Vec_PtrFree( p->vPis ); p->vPis = vCis; + Vec_PtrFree( p->vPos ); p->vPos = vCos; + p->nObjs[AIG_OBJ_PI] = Vec_PtrSize( p->vPis ); + p->nObjs[AIG_OBJ_PO] = Vec_PtrSize( p->vPos ); + } + Vec_PtrFree( vNodes ); + // remove dangling nodes + return Aig_ManCleanup( p ); +} + +/**Function************************************************************* + + Synopsis [Returns the number of dangling nodes removed.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ManCleanup( Aig_Man_t * p ) +{ + Vec_Ptr_t * vObjs; + Aig_Obj_t * pNode; + int i, nNodesOld; + nNodesOld = Aig_ManNodeNum(p); + // collect roots of dangling nodes + vObjs = Vec_PtrAlloc( 100 ); + Aig_ManForEachObj( p, pNode, i ) + if ( Aig_ObjIsNode(pNode) && Aig_ObjRefs(pNode) == 0 ) + Vec_PtrPush( vObjs, pNode ); + // recursively remove dangling nodes + Vec_PtrForEachEntry( vObjs, pNode, i ) + Aig_ObjDelete_rec( p, pNode, 1 ); + Vec_PtrFree( vObjs ); + return nNodesOld - Aig_ManNodeNum(p); +} + +/**Function************************************************************* + + Synopsis [Stops the AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManPrintStats( Aig_Man_t * p ) +{ + printf( "PI/PO/Lat = %5d/%5d/%5d ", Aig_ManPiNum(p), Aig_ManPoNum(p), Aig_ManLatchNum(p) ); + printf( "A = %7d. ", Aig_ManAndNum(p) ); + if ( Aig_ManExorNum(p) ) + printf( "X = %5d. ", Aig_ManExorNum(p) ); +// if ( Aig_ManBufNum(p) ) + printf( "B = %5d. ", Aig_ManBufNum(p) ); +// printf( "Cre = %6d. ", p->nCreated ); +// printf( "Del = %6d. ", p->nDeleted ); +// printf( "Lev = %3d. ", Aig_ManCountLevels(p) ); + printf( "Max = %7d. ", Aig_ManObjIdMax(p) ); + printf( "Lev = %3d. ", Aig_ManLevels(p) ); + printf( "\n" ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/aig/aigMem.c b/abc_with_bb_support/src/aig/aig/aigMem.c new file mode 100644 index 000000000..34d30c4bf --- /dev/null +++ b/abc_with_bb_support/src/aig/aig/aigMem.c @@ -0,0 +1,598 @@ +/**CFile**************************************************************** + + FileName [aigMem.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG package.] + + Synopsis [Memory managers.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: aigMem.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "aig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +struct Aig_MmFixed_t_ +{ + // information about individual entries + int nEntrySize; // the size of one entry + int nEntriesAlloc; // the total number of entries allocated + int nEntriesUsed; // the number of entries in use + int nEntriesMax; // the max number of entries in use + char * pEntriesFree; // the linked list of free entries + + // this is where the memory is stored + int nChunkSize; // the size of one chunk + int nChunksAlloc; // the maximum number of memory chunks + int nChunks; // the current number of memory chunks + char ** pChunks; // the allocated memory + + // statistics + int nMemoryUsed; // memory used in the allocated entries + int nMemoryAlloc; // memory allocated +}; + +struct Aig_MmFlex_t_ +{ + // information about individual entries + int nEntriesUsed; // the number of entries allocated + char * pCurrent; // the current pointer to free memory + char * pEnd; // the first entry outside the free memory + + // this is where the memory is stored + int nChunkSize; // the size of one chunk + int nChunksAlloc; // the maximum number of memory chunks + int nChunks; // the current number of memory chunks + char ** pChunks; // the allocated memory + + // statistics + int nMemoryUsed; // memory used in the allocated entries + int nMemoryAlloc; // memory allocated +}; + +struct Aig_MmStep_t_ +{ + int nMems; // the number of fixed memory managers employed + Aig_MmFixed_t ** pMems; // memory managers: 2^1 words, 2^2 words, etc + int nMapSize; // the size of the memory array + Aig_MmFixed_t ** pMap; // maps the number of bytes into its memory manager +}; + +#define ALLOC(type, num) ((type *) malloc(sizeof(type) * (num))) +#define FREE(obj) ((obj) ? (free((char *) (obj)), (obj) = 0) : 0) +#define REALLOC(type, obj, num) \ + ((obj) ? ((type *) realloc((char *)(obj), sizeof(type) * (num))) : \ + ((type *) malloc(sizeof(type) * (num)))) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates memory pieces of fixed size.] + + Description [The size of the chunk is computed as the minimum of + 1024 entries and 64K. Can only work with entry size at least 4 byte long.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_MmFixed_t * Aig_MmFixedStart( int nEntrySize, int nEntriesMax ) +{ + Aig_MmFixed_t * p; + + p = ALLOC( Aig_MmFixed_t, 1 ); + memset( p, 0, sizeof(Aig_MmFixed_t) ); + + p->nEntrySize = nEntrySize; + p->nEntriesAlloc = 0; + p->nEntriesUsed = 0; + p->pEntriesFree = NULL; + + p->nChunkSize = nEntriesMax / 8; + if ( p->nChunkSize < 8 ) + p->nChunkSize = 8; + + p->nChunksAlloc = 64; + p->nChunks = 0; + p->pChunks = ALLOC( char *, p->nChunksAlloc ); + + p->nMemoryUsed = 0; + p->nMemoryAlloc = 0; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_MmFixedStop( Aig_MmFixed_t * p, int fVerbose ) +{ + int i; + if ( p == NULL ) + return; + if ( fVerbose ) + { + printf( "Fixed memory manager: Entry = %5d. Chunk = %5d. Chunks used = %5d.\n", + p->nEntrySize, p->nChunkSize, p->nChunks ); + printf( " Entries used = %8d. Entries peak = %8d. Memory used = %8d. Memory alloc = %8d.\n", + p->nEntriesUsed, p->nEntriesMax, p->nEntrySize * p->nEntriesUsed, p->nMemoryAlloc ); + } + for ( i = 0; i < p->nChunks; i++ ) + free( p->pChunks[i] ); + free( p->pChunks ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Aig_MmFixedEntryFetch( Aig_MmFixed_t * p ) +{ + char * pTemp; + int i; + + // check if there are still free entries + if ( p->nEntriesUsed == p->nEntriesAlloc ) + { // need to allocate more entries + assert( p->pEntriesFree == NULL ); + if ( p->nChunks == p->nChunksAlloc ) + { + p->nChunksAlloc *= 2; + p->pChunks = REALLOC( char *, p->pChunks, p->nChunksAlloc ); + } + p->pEntriesFree = ALLOC( char, p->nEntrySize * p->nChunkSize ); + p->nMemoryAlloc += p->nEntrySize * p->nChunkSize; + // transform these entries into a linked list + pTemp = p->pEntriesFree; + for ( i = 1; i < p->nChunkSize; i++ ) + { + *((char **)pTemp) = pTemp + p->nEntrySize; + pTemp += p->nEntrySize; + } + // set the last link + *((char **)pTemp) = NULL; + // add the chunk to the chunk storage + p->pChunks[ p->nChunks++ ] = p->pEntriesFree; + // add to the number of entries allocated + p->nEntriesAlloc += p->nChunkSize; + } + // incrememt the counter of used entries + p->nEntriesUsed++; + if ( p->nEntriesMax < p->nEntriesUsed ) + p->nEntriesMax = p->nEntriesUsed; + // return the first entry in the free entry list + pTemp = p->pEntriesFree; + p->pEntriesFree = *((char **)pTemp); + return pTemp; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_MmFixedEntryRecycle( Aig_MmFixed_t * p, char * pEntry ) +{ + // decrement the counter of used entries + p->nEntriesUsed--; + // add the entry to the linked list of free entries + *((char **)pEntry) = p->pEntriesFree; + p->pEntriesFree = pEntry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [Relocates all the memory except the first chunk.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_MmFixedRestart( Aig_MmFixed_t * p ) +{ + int i; + char * pTemp; + if ( p->nChunks == 0 ) + return; + // deallocate all chunks except the first one + for ( i = 1; i < p->nChunks; i++ ) + free( p->pChunks[i] ); + p->nChunks = 1; + // transform these entries into a linked list + pTemp = p->pChunks[0]; + for ( i = 1; i < p->nChunkSize; i++ ) + { + *((char **)pTemp) = pTemp + p->nEntrySize; + pTemp += p->nEntrySize; + } + // set the last link + *((char **)pTemp) = NULL; + // set the free entry list + p->pEntriesFree = p->pChunks[0]; + // set the correct statistics + p->nMemoryAlloc = p->nEntrySize * p->nChunkSize; + p->nMemoryUsed = 0; + p->nEntriesAlloc = p->nChunkSize; + p->nEntriesUsed = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_MmFixedReadMemUsage( Aig_MmFixed_t * p ) +{ + return p->nMemoryAlloc; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_MmFixedReadMaxEntriesUsed( Aig_MmFixed_t * p ) +{ + return p->nEntriesMax; +} + + + +/**Function************************************************************* + + Synopsis [Allocates entries of flexible size.] + + Description [Can only work with entry size at least 4 byte long.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_MmFlex_t * Aig_MmFlexStart() +{ + Aig_MmFlex_t * p; + + p = ALLOC( Aig_MmFlex_t, 1 ); + memset( p, 0, sizeof(Aig_MmFlex_t) ); + + p->nEntriesUsed = 0; + p->pCurrent = NULL; + p->pEnd = NULL; + + p->nChunkSize = (1 << 18); + p->nChunksAlloc = 64; + p->nChunks = 0; + p->pChunks = ALLOC( char *, p->nChunksAlloc ); + + p->nMemoryUsed = 0; + p->nMemoryAlloc = 0; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_MmFlexStop( Aig_MmFlex_t * p, int fVerbose ) +{ + int i; + if ( p == NULL ) + return; + if ( fVerbose ) + { + printf( "Flexible memory manager: Chunk size = %d. Chunks used = %d.\n", + p->nChunkSize, p->nChunks ); + printf( " Entries used = %d. Memory used = %d. Memory alloc = %d.\n", + p->nEntriesUsed, p->nMemoryUsed, p->nMemoryAlloc ); + } + for ( i = 0; i < p->nChunks; i++ ) + free( p->pChunks[i] ); + free( p->pChunks ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Aig_MmFlexEntryFetch( Aig_MmFlex_t * p, int nBytes ) +{ + char * pTemp; + // check if there are still free entries + if ( p->pCurrent == NULL || p->pCurrent + nBytes > p->pEnd ) + { // need to allocate more entries + if ( p->nChunks == p->nChunksAlloc ) + { + p->nChunksAlloc *= 2; + p->pChunks = REALLOC( char *, p->pChunks, p->nChunksAlloc ); + } + if ( nBytes > p->nChunkSize ) + { + // resize the chunk size if more memory is requested than it can give + // (ideally, this should never happen) + p->nChunkSize = 2 * nBytes; + } + p->pCurrent = ALLOC( char, p->nChunkSize ); + p->pEnd = p->pCurrent + p->nChunkSize; + p->nMemoryAlloc += p->nChunkSize; + // add the chunk to the chunk storage + p->pChunks[ p->nChunks++ ] = p->pCurrent; + } + assert( p->pCurrent + nBytes <= p->pEnd ); + // increment the counter of used entries + p->nEntriesUsed++; + // keep track of the memory used + p->nMemoryUsed += nBytes; + // return the next entry + pTemp = p->pCurrent; + p->pCurrent += nBytes; + return pTemp; +} + +/**Function************************************************************* + + Synopsis [] + + Description [Relocates all the memory except the first chunk.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_MmFlexRestart( Aig_MmFlex_t * p ) +{ + int i; + if ( p->nChunks == 0 ) + return; + // deallocate all chunks except the first one + for ( i = 1; i < p->nChunks; i++ ) + free( p->pChunks[i] ); + p->nChunks = 1; + p->nMemoryAlloc = p->nChunkSize; + // transform these entries into a linked list + p->pCurrent = p->pChunks[0]; + p->pEnd = p->pCurrent + p->nChunkSize; + p->nEntriesUsed = 0; + p->nMemoryUsed = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_MmFlexReadMemUsage( Aig_MmFlex_t * p ) +{ + return p->nMemoryUsed; +} + + + + + +/**Function************************************************************* + + Synopsis [Starts the hierarchical memory manager.] + + Description [This manager can allocate entries of any size. + Iternally they are mapped into the entries with the number of bytes + equal to the power of 2. The smallest entry size is 8 bytes. The + next one is 16 bytes etc. So, if the user requests 6 bytes, he gets + 8 byte entry. If we asks for 25 bytes, he gets 32 byte entry etc. + The input parameters "nSteps" says how many fixed memory managers + are employed internally. Calling this procedure with nSteps equal + to 10 results in 10 hierarchically arranged internal memory managers, + which can allocate up to 4096 (1Kb) entries. Requests for larger + entries are handed over to malloc() and then free()ed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_MmStep_t * Aig_MmStepStart( int nSteps ) +{ + Aig_MmStep_t * p; + int i, k; + p = ALLOC( Aig_MmStep_t, 1 ); + memset( p, 0, sizeof(Aig_MmStep_t) ); + p->nMems = nSteps; + // start the fixed memory managers + p->pMems = ALLOC( Aig_MmFixed_t *, p->nMems ); + for ( i = 0; i < p->nMems; i++ ) + p->pMems[i] = Aig_MmFixedStart( (8<nMapSize = (4<nMems); + p->pMap = ALLOC( Aig_MmFixed_t *, p->nMapSize+1 ); + p->pMap[0] = NULL; + for ( k = 1; k <= 4; k++ ) + p->pMap[k] = p->pMems[0]; + for ( i = 0; i < p->nMems; i++ ) + for ( k = (4<pMap[k] = p->pMems[i]; +//for ( i = 1; i < 100; i ++ ) +//printf( "%10d: size = %10d\n", i, p->pMap[i]->nEntrySize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the memory manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_MmStepStop( Aig_MmStep_t * p, int fVerbose ) +{ + int i; + for ( i = 0; i < p->nMems; i++ ) + Aig_MmFixedStop( p->pMems[i], fVerbose ); +// if ( p->pLargeChunks ) +// { +// for ( i = 0; i < p->nLargeChunks; i++ ) +// free( p->pLargeChunks[i] ); +// free( p->pLargeChunks ); +// } + free( p->pMems ); + free( p->pMap ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Creates the entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Aig_MmStepEntryFetch( Aig_MmStep_t * p, int nBytes ) +{ + if ( nBytes == 0 ) + return NULL; + if ( nBytes > p->nMapSize ) + { +// printf( "Allocating %d bytes.\n", nBytes ); +/* + if ( p->nLargeChunks == p->nLargeChunksAlloc ) + { + if ( p->nLargeChunksAlloc == 0 ) + p->nLargeChunksAlloc = 5; + p->nLargeChunksAlloc *= 2; + p->pLargeChunks = REALLOC( char *, p->pLargeChunks, p->nLargeChunksAlloc ); + } + p->pLargeChunks[ p->nLargeChunks++ ] = ALLOC( char, nBytes ); + return p->pLargeChunks[ p->nLargeChunks - 1 ]; +*/ + return ALLOC( char, nBytes ); + } + return Aig_MmFixedEntryFetch( p->pMap[nBytes] ); +} + + +/**Function************************************************************* + + Synopsis [Recycles the entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_MmStepEntryRecycle( Aig_MmStep_t * p, char * pEntry, int nBytes ) +{ + if ( nBytes == 0 ) + return; + if ( nBytes > p->nMapSize ) + { + free( pEntry ); + return; + } + Aig_MmFixedEntryRecycle( p->pMap[nBytes], pEntry ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_MmStepReadMemUsage( Aig_MmStep_t * p ) +{ + int i, nMemTotal = 0; + for ( i = 0; i < p->nMems; i++ ) + nMemTotal += p->pMems[i]->nMemoryAlloc; + return nMemTotal; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/aig/aig/aigMffc.c b/abc_with_bb_support/src/aig/aig/aigMffc.c new file mode 100644 index 000000000..daeed2810 --- /dev/null +++ b/abc_with_bb_support/src/aig/aig/aigMffc.c @@ -0,0 +1,297 @@ +/**CFile**************************************************************** + + FileName [aigMffc.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG package.] + + Synopsis [Computation of MFFCs.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: aigMffc.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "aig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Dereferences the node's MFFC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_NodeDeref_rec( Aig_Obj_t * pNode, unsigned LevelMin ) +{ + Aig_Obj_t * pFanin; + int Counter = 0; + if ( Aig_ObjIsPi(pNode) ) + return 0; + // consider the first fanin + pFanin = Aig_ObjFanin0(pNode); + assert( pFanin->nRefs > 0 ); + if ( --pFanin->nRefs == 0 && (!LevelMin || pFanin->Level > LevelMin) ) + Counter += Aig_NodeDeref_rec( pFanin, LevelMin ); + // skip the buffer + if ( Aig_ObjIsBuf(pNode) ) + return Counter; + assert( Aig_ObjIsNode(pNode) ); + // consider the second fanin + pFanin = Aig_ObjFanin1(pNode); + assert( pFanin->nRefs > 0 ); + if ( --pFanin->nRefs == 0 && (!LevelMin || pFanin->Level > LevelMin) ) + Counter += Aig_NodeDeref_rec( pFanin, LevelMin ); + return Counter + 1; +} + +/**Function************************************************************* + + Synopsis [References the node's MFFC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_NodeRef_rec( Aig_Obj_t * pNode, unsigned LevelMin ) +{ + Aig_Obj_t * pFanin; + int Counter = 0; + if ( Aig_ObjIsPi(pNode) ) + return 0; + // consider the first fanin + pFanin = Aig_ObjFanin0(pNode); + if ( pFanin->nRefs++ == 0 && (!LevelMin || pFanin->Level > LevelMin) ) + Counter += Aig_NodeRef_rec( pFanin, LevelMin ); + // skip the buffer + if ( Aig_ObjIsBuf(pNode) ) + return Counter; + assert( Aig_ObjIsNode(pNode) ); + // consider the second fanin + pFanin = Aig_ObjFanin1(pNode); + if ( pFanin->nRefs++ == 0 && (!LevelMin || pFanin->Level > LevelMin) ) + Counter += Aig_NodeRef_rec( pFanin, LevelMin ); + return Counter + 1; +} + +/**Function************************************************************* + + Synopsis [References the node's MFFC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_NodeRefLabel_rec( Aig_Man_t * p, Aig_Obj_t * pNode, unsigned LevelMin ) +{ + Aig_Obj_t * pFanin; + int Counter = 0; + if ( Aig_ObjIsPi(pNode) ) + return 0; + Aig_ObjSetTravIdCurrent( p, pNode ); + // consider the first fanin + pFanin = Aig_ObjFanin0(pNode); + if ( pFanin->nRefs++ == 0 && (!LevelMin || pFanin->Level > LevelMin) ) + Counter += Aig_NodeRefLabel_rec( p, pFanin, LevelMin ); + if ( Aig_ObjIsBuf(pNode) ) + return Counter; + assert( Aig_ObjIsNode(pNode) ); + // consider the second fanin + pFanin = Aig_ObjFanin1(pNode); + if ( pFanin->nRefs++ == 0 && (!LevelMin || pFanin->Level > LevelMin) ) + Counter += Aig_NodeRefLabel_rec( p, pFanin, LevelMin ); + return Counter + 1; +} + +/**Function************************************************************* + + Synopsis [Collects the internal and boundary nodes in the derefed MFFC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_NodeMffsSupp_rec( Aig_Man_t * p, Aig_Obj_t * pNode, unsigned LevelMin, Vec_Ptr_t * vSupp, int fTopmost, Aig_Obj_t * pObjSkip ) +{ + // skip visited nodes + if ( Aig_ObjIsTravIdCurrent(p, pNode) ) + return; + Aig_ObjSetTravIdCurrent(p, pNode); + // add to the new support nodes + if ( !fTopmost && pNode != pObjSkip && (Aig_ObjIsPi(pNode) || pNode->nRefs > 0 || pNode->Level <= LevelMin) ) + { + if ( vSupp ) Vec_PtrPush( vSupp, pNode ); + return; + } + assert( Aig_ObjIsNode(pNode) ); + // recur on the children + Aig_NodeMffsSupp_rec( p, Aig_ObjFanin0(pNode), LevelMin, vSupp, 0, pObjSkip ); + Aig_NodeMffsSupp_rec( p, Aig_ObjFanin1(pNode), LevelMin, vSupp, 0, pObjSkip ); +} + +/**Function************************************************************* + + Synopsis [Collects the support of depth-limited MFFC.] + + Description [Returns the number of internal nodes in the MFFC.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_NodeMffsSupp( Aig_Man_t * p, Aig_Obj_t * pNode, int LevelMin, Vec_Ptr_t * vSupp ) +{ + int ConeSize1, ConeSize2; + assert( !Aig_IsComplement(pNode) ); + assert( Aig_ObjIsNode(pNode) ); + if ( vSupp ) Vec_PtrClear( vSupp ); + Aig_ManIncrementTravId( p ); + ConeSize1 = Aig_NodeDeref_rec( pNode, LevelMin ); + Aig_NodeMffsSupp_rec( p, pNode, LevelMin, vSupp, 1, NULL ); + ConeSize2 = Aig_NodeRef_rec( pNode, LevelMin ); + assert( ConeSize1 == ConeSize2 ); + assert( ConeSize1 > 0 ); + return ConeSize1; +} + +/**Function************************************************************* + + Synopsis [Labels the nodes in the MFFC.] + + Description [Returns the number of internal nodes in the MFFC.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_NodeMffsLabel( Aig_Man_t * p, Aig_Obj_t * pNode ) +{ + int ConeSize1, ConeSize2; + assert( !Aig_IsComplement(pNode) ); + assert( Aig_ObjIsNode(pNode) ); + Aig_ManIncrementTravId( p ); + ConeSize1 = Aig_NodeDeref_rec( pNode, 0 ); + ConeSize2 = Aig_NodeRefLabel_rec( p, pNode, 0 ); + assert( ConeSize1 == ConeSize2 ); + assert( ConeSize1 > 0 ); + return ConeSize1; +} + +/**Function************************************************************* + + Synopsis [Labels the nodes in the MFFC.] + + Description [Returns the number of internal nodes in the MFFC.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_NodeMffsLabelCut( Aig_Man_t * p, Aig_Obj_t * pNode, Vec_Ptr_t * vLeaves ) +{ + Aig_Obj_t * pObj; + int i, ConeSize1, ConeSize2; + assert( !Aig_IsComplement(pNode) ); + assert( Aig_ObjIsNode(pNode) ); + Aig_ManIncrementTravId( p ); + Vec_PtrForEachEntry( vLeaves, pObj, i ) + pObj->nRefs++; + ConeSize1 = Aig_NodeDeref_rec( pNode, 0 ); + ConeSize2 = Aig_NodeRefLabel_rec( p, pNode, 0 ); + Vec_PtrForEachEntry( vLeaves, pObj, i ) + pObj->nRefs--; + assert( ConeSize1 == ConeSize2 ); + assert( ConeSize1 > 0 ); + return ConeSize1; +} + +/**Function************************************************************* + + Synopsis [Expands the cut by adding the most closely related node.] + + Description [Returns 1 if the cut exists.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_NodeMffsExtendCut( Aig_Man_t * p, Aig_Obj_t * pNode, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vResult ) +{ + Aig_Obj_t * pObj, * pLeafBest; + int i, LevelMax, ConeSize1, ConeSize2, ConeCur1, ConeCur2, ConeBest; + // dereference the current cut + LevelMax = 0; + Vec_PtrForEachEntry( vLeaves, pObj, i ) + LevelMax = AIG_MAX( LevelMax, (int)pObj->Level ); + if ( LevelMax == 0 ) + return 0; + // dereference the cut + ConeSize1 = Aig_NodeDeref_rec( pNode, 0 ); + // try expanding each node in the boundary + ConeBest = AIG_INFINITY; + pLeafBest = NULL; + Vec_PtrForEachEntry( vLeaves, pObj, i ) + { + if ( (int)pObj->Level != LevelMax ) + continue; + ConeCur1 = Aig_NodeDeref_rec( pObj, 0 ); + if ( ConeBest > ConeCur1 ) + { + ConeBest = ConeCur1; + pLeafBest = pObj; + } + ConeCur2 = Aig_NodeRef_rec( pObj, 0 ); + assert( ConeCur1 == ConeCur2 ); + } + assert( pLeafBest != NULL ); + assert( Aig_ObjIsNode(pLeafBest) ); + // deref the best leaf + ConeCur1 = Aig_NodeDeref_rec( pLeafBest, 0 ); + // collect the cut nodes + Vec_PtrClear( vResult ); + Aig_ManIncrementTravId( p ); + Aig_NodeMffsSupp_rec( p, pNode, 0, vResult, 1, pLeafBest ); + // ref the nodes + ConeCur2 = Aig_NodeRef_rec( pLeafBest, 0 ); + assert( ConeCur1 == ConeCur2 ); + // ref the original node + ConeSize2 = Aig_NodeRef_rec( pNode, 0 ); + assert( ConeSize1 == ConeSize2 ); + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/aig/aigObj.c b/abc_with_bb_support/src/aig/aig/aigObj.c new file mode 100644 index 000000000..78f793661 --- /dev/null +++ b/abc_with_bb_support/src/aig/aig/aigObj.c @@ -0,0 +1,416 @@ +/**CFile**************************************************************** + + FileName [aigObj.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG package.] + + Synopsis [Adding/removing objects.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: aigObj.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "aig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Creates primary input.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_ObjCreatePi( Aig_Man_t * p ) +{ + Aig_Obj_t * pObj; + pObj = Aig_ManFetchMemory( p ); + pObj->Type = AIG_OBJ_PI; + Vec_PtrPush( p->vPis, pObj ); + p->nObjs[AIG_OBJ_PI]++; + return pObj; +} + +/**Function************************************************************* + + Synopsis [Creates primary output with the given driver.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_ObjCreatePo( Aig_Man_t * p, Aig_Obj_t * pDriver ) +{ + Aig_Obj_t * pObj; + pObj = Aig_ManFetchMemory( p ); + pObj->Type = AIG_OBJ_PO; + Vec_PtrPush( p->vPos, pObj ); + Aig_ObjConnect( p, pObj, pDriver, NULL ); + p->nObjs[AIG_OBJ_PO]++; + return pObj; +} + + +/**Function************************************************************* + + Synopsis [Create the new node assuming it does not exist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_ObjCreate( Aig_Man_t * p, Aig_Obj_t * pGhost ) +{ + Aig_Obj_t * pObj; + assert( !Aig_IsComplement(pGhost) ); + assert( Aig_ObjIsHash(pGhost) ); + assert( pGhost == &p->Ghost ); + // get memory for the new object + pObj = Aig_ManFetchMemory( p ); + pObj->Type = pGhost->Type; + // add connections + Aig_ObjConnect( p, pObj, pGhost->pFanin0, pGhost->pFanin1 ); + // update node counters of the manager + p->nObjs[Aig_ObjType(pObj)]++; + assert( pObj->pData == NULL ); + return pObj; +} + +/**Function************************************************************* + + Synopsis [Connect the object to the fanin.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ObjConnect( Aig_Man_t * p, Aig_Obj_t * pObj, Aig_Obj_t * pFan0, Aig_Obj_t * pFan1 ) +{ + assert( !Aig_IsComplement(pObj) ); + assert( !Aig_ObjIsPi(pObj) ); + // add the first fanin + pObj->pFanin0 = pFan0; + pObj->pFanin1 = pFan1; + // increment references of the fanins and add their fanouts + if ( pFan0 != NULL ) + { + assert( Aig_ObjFanin0(pObj)->Type > 0 ); + Aig_ObjRef( Aig_ObjFanin0(pObj) ); + if ( p->pFanData ) + Aig_ObjAddFanout( p, Aig_ObjFanin0(pObj), pObj ); + } + if ( pFan1 != NULL ) + { + assert( Aig_ObjFanin1(pObj)->Type > 0 ); + Aig_ObjRef( Aig_ObjFanin1(pObj) ); + if ( p->pFanData ) + Aig_ObjAddFanout( p, Aig_ObjFanin1(pObj), pObj ); + } + // set level and phase + pObj->Level = Aig_ObjLevelNew( pObj ); + pObj->fPhase = Aig_ObjPhaseReal(pFan0) & Aig_ObjPhaseReal(pFan1); + // add the node to the structural hash table + if ( Aig_ObjIsHash(pObj) ) + Aig_TableInsert( p, pObj ); + // add the node to the dynamically updated topological order + if ( p->pOrderData && Aig_ObjIsNode(pObj) ) + Aig_ObjOrderInsert( p, pObj->Id ); +} + +/**Function************************************************************* + + Synopsis [Disconnects the object from the fanins.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ObjDisconnect( Aig_Man_t * p, Aig_Obj_t * pObj ) +{ + assert( !Aig_IsComplement(pObj) ); + // remove connections + if ( pObj->pFanin0 != NULL ) + { + if ( p->pFanData ) + Aig_ObjRemoveFanout( p, Aig_ObjFanin0(pObj), pObj ); + Aig_ObjDeref(Aig_ObjFanin0(pObj)); + } + if ( pObj->pFanin1 != NULL ) + { + if ( p->pFanData ) + Aig_ObjRemoveFanout( p, Aig_ObjFanin1(pObj), pObj ); + Aig_ObjDeref(Aig_ObjFanin1(pObj)); + } + // remove the node from the structural hash table + if ( Aig_ObjIsHash(pObj) ) + Aig_TableDelete( p, pObj ); + // add the first fanin + pObj->pFanin0 = NULL; + pObj->pFanin1 = NULL; + // remove the node from the dynamically updated topological order + if ( p->pOrderData && Aig_ObjIsNode(pObj) ) + Aig_ObjOrderRemove( p, pObj->Id ); +} + +/**Function************************************************************* + + Synopsis [Deletes the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ObjDelete( Aig_Man_t * p, Aig_Obj_t * pObj ) +{ + assert( !Aig_IsComplement(pObj) ); + assert( !Aig_ObjIsTerm(pObj) ); + assert( Aig_ObjRefs(pObj) == 0 ); + if ( p->pFanData && Aig_ObjIsBuf(pObj) ) + Vec_PtrRemove( p->vBufs, pObj ); + p->nObjs[pObj->Type]--; + Vec_PtrWriteEntry( p->vObjs, pObj->Id, NULL ); + Aig_ManRecycleMemory( p, pObj ); +} + +/**Function************************************************************* + + Synopsis [Deletes the MFFC of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ObjDelete_rec( Aig_Man_t * p, Aig_Obj_t * pObj, int fFreeTop ) +{ + Aig_Obj_t * pFanin0, * pFanin1; + assert( !Aig_IsComplement(pObj) ); + if ( Aig_ObjIsConst1(pObj) || Aig_ObjIsPi(pObj) ) + return; + assert( !Aig_ObjIsPo(pObj) ); + pFanin0 = Aig_ObjFanin0(pObj); + pFanin1 = Aig_ObjFanin1(pObj); + Aig_ObjDisconnect( p, pObj ); + if ( fFreeTop ) + Aig_ObjDelete( p, pObj ); + if ( pFanin0 && !Aig_ObjIsNone(pFanin0) && Aig_ObjRefs(pFanin0) == 0 ) + Aig_ObjDelete_rec( p, pFanin0, 1 ); + if ( pFanin1 && !Aig_ObjIsNone(pFanin1) && Aig_ObjRefs(pFanin1) == 0 ) + Aig_ObjDelete_rec( p, pFanin1, 1 ); +} + +/**Function************************************************************* + + Synopsis [Replaces the first fanin of the node by the new fanin.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ObjPatchFanin0( Aig_Man_t * p, Aig_Obj_t * pObj, Aig_Obj_t * pFaninNew ) +{ + Aig_Obj_t * pFaninOld; + assert( !Aig_IsComplement(pObj) ); + assert( Aig_ObjIsPo(pObj) ); + pFaninOld = Aig_ObjFanin0(pObj); + // decrement ref and remove fanout + if ( p->pFanData ) + Aig_ObjRemoveFanout( p, pFaninOld, pObj ); + Aig_ObjDeref( pFaninOld ); + // update the fanin + pObj->pFanin0 = pFaninNew; + // increment ref and add fanout + if ( p->pFanData ) + Aig_ObjAddFanout( p, Aig_ObjFanin0(pObj), pObj ); + Aig_ObjRef( Aig_ObjFanin0(pObj) ); + // get rid of old fanin + if ( !Aig_ObjIsPi(pFaninOld) && !Aig_ObjIsConst1(pFaninOld) && Aig_ObjRefs(pFaninOld) == 0 ) + Aig_ObjDelete_rec( p, pFaninOld, 1 ); +} + +/**Function************************************************************* + + Synopsis [Replaces node with a buffer fanin by a node without them.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_NodeFixBufferFanins( Aig_Man_t * p, Aig_Obj_t * pObj, int fNodesOnly, int fUpdateLevel ) +{ + Aig_Obj_t * pFanReal0, * pFanReal1, * pResult; + p->nBufFixes++; + if ( Aig_ObjIsPo(pObj) ) + { + assert( Aig_ObjIsBuf(Aig_ObjFanin0(pObj)) ); + pFanReal0 = Aig_ObjReal_rec( Aig_ObjChild0(pObj) ); + assert( Aig_ObjPhaseReal(Aig_ObjChild0(pObj)) == Aig_ObjPhaseReal(pFanReal0) ); + Aig_ObjPatchFanin0( p, pObj, pFanReal0 ); + return; + } + assert( Aig_ObjIsNode(pObj) ); + assert( Aig_ObjIsBuf(Aig_ObjFanin0(pObj)) || Aig_ObjIsBuf(Aig_ObjFanin1(pObj)) ); + // get the real fanins + pFanReal0 = Aig_ObjReal_rec( Aig_ObjChild0(pObj) ); + pFanReal1 = Aig_ObjReal_rec( Aig_ObjChild1(pObj) ); + // get the new node + if ( Aig_ObjIsNode(pObj) ) + pResult = Aig_Oper( p, pFanReal0, pFanReal1, Aig_ObjType(pObj) ); +// else if ( Aig_ObjIsLatch(pObj) ) +// pResult = Aig_Latch( p, pFanReal0, Aig_ObjInit(pObj) ); + else + assert( 0 ); + // replace the node with buffer by the node without buffer + Aig_ObjReplace( p, pObj, pResult, fNodesOnly, fUpdateLevel ); +} + +/**Function************************************************************* + + Synopsis [Returns the number of dangling nodes removed.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ManPropagateBuffers( Aig_Man_t * p, int fNodesOnly, int fUpdateLevel ) +{ + Aig_Obj_t * pObj; + int nSteps; + assert( p->pFanData ); + for ( nSteps = 0; Vec_PtrSize(p->vBufs) > 0; nSteps++ ) + { + // get the node with a buffer fanin + for ( pObj = Vec_PtrEntryLast(p->vBufs); Aig_ObjIsBuf(pObj); pObj = Aig_ObjFanout0(p, pObj) ); + // replace this node by a node without buffer + Aig_NodeFixBufferFanins( p, pObj, fNodesOnly, fUpdateLevel ); + // stop if a cycle occured + if ( nSteps > 1000000 ) + { + printf( "Error: A cycle is encountered while propagating buffers.\n" ); + break; + } + } + return nSteps; +} + +/**Function************************************************************* + + Synopsis [Replaces one object by another.] + + Description [The new object (pObjNew) should be used instead of the old + object (pObjOld). If the new object is complemented or used, the buffer + is added and the new object remains in the manager; otherwise, the new + object is deleted.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ObjReplace( Aig_Man_t * p, Aig_Obj_t * pObjOld, Aig_Obj_t * pObjNew, int fNodesOnly, int fUpdateLevel ) +{ + Aig_Obj_t * pObjNewR = Aig_Regular(pObjNew); + // the object to be replaced cannot be complemented + assert( !Aig_IsComplement(pObjOld) ); + // the object to be replaced cannot be a terminal + assert( !Aig_ObjIsPi(pObjOld) && !Aig_ObjIsPo(pObjOld) ); + // the object to be used cannot be a buffer or a PO + assert( !Aig_ObjIsBuf(pObjNewR) && !Aig_ObjIsPo(pObjNewR) ); + // the object cannot be the same + assert( pObjOld != pObjNewR ); + // make sure object is not pointing to itself + assert( pObjOld != Aig_ObjFanin0(pObjNewR) ); + assert( pObjOld != Aig_ObjFanin1(pObjNewR) ); + // recursively delete the old node - but leave the object there + pObjNewR->nRefs++; + Aig_ObjDelete_rec( p, pObjOld, 0 ); + pObjNewR->nRefs--; + // if the new object is complemented or already used, create a buffer + p->nObjs[pObjOld->Type]--; + if ( Aig_IsComplement(pObjNew) || Aig_ObjRefs(pObjNew) > 0 || (fNodesOnly && !Aig_ObjIsNode(pObjNew)) ) + { + pObjOld->Type = AIG_OBJ_BUF; + Aig_ObjConnect( p, pObjOld, pObjNew, NULL ); + p->nBufReplaces++; + } + else + { + Aig_Obj_t * pFanin0 = pObjNew->pFanin0; + Aig_Obj_t * pFanin1 = pObjNew->pFanin1; + int LevelOld = pObjOld->Level; + pObjOld->Type = pObjNew->Type; + Aig_ObjDisconnect( p, pObjNew ); + Aig_ObjConnect( p, pObjOld, pFanin0, pFanin1 ); + // delete the new object + Aig_ObjDelete( p, pObjNew ); + // update levels + if ( p->pFanData ) + { + pObjOld->Level = LevelOld; + Aig_ManUpdateLevel( p, pObjOld ); + } + if ( fUpdateLevel ) + { + Aig_ObjClearReverseLevel( p, pObjOld ); + Aig_ManUpdateReverseLevel( p, pObjOld ); + } + } + p->nObjs[pObjOld->Type]++; + // store buffers if fanout is allocated + if ( p->pFanData && Aig_ObjIsBuf(pObjOld) ) + { + Vec_PtrPush( p->vBufs, pObjOld ); + p->nBufMax = AIG_MAX( p->nBufMax, Vec_PtrSize(p->vBufs) ); + Aig_ManPropagateBuffers( p, fNodesOnly, fUpdateLevel ); + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/aig/aigOper.c b/abc_with_bb_support/src/aig/aig/aigOper.c new file mode 100644 index 000000000..733db6e6a --- /dev/null +++ b/abc_with_bb_support/src/aig/aig/aigOper.c @@ -0,0 +1,449 @@ +/**CFile**************************************************************** + + FileName [aigOper.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG package.] + + Synopsis [AIG operations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: aigOper.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "aig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// procedure to detect an EXOR gate +static inline int Aig_ObjIsExorType( Aig_Obj_t * p0, Aig_Obj_t * p1, Aig_Obj_t ** ppFan0, Aig_Obj_t ** ppFan1 ) +{ + if ( !Aig_IsComplement(p0) || !Aig_IsComplement(p1) ) + return 0; + p0 = Aig_Regular(p0); + p1 = Aig_Regular(p1); + if ( !Aig_ObjIsAnd(p0) || !Aig_ObjIsAnd(p1) ) + return 0; + if ( Aig_ObjFanin0(p0) != Aig_ObjFanin0(p1) || Aig_ObjFanin1(p0) != Aig_ObjFanin1(p1) ) + return 0; + if ( Aig_ObjFaninC0(p0) == Aig_ObjFaninC0(p1) || Aig_ObjFaninC1(p0) == Aig_ObjFaninC1(p1) ) + return 0; + *ppFan0 = Aig_ObjChild0(p0); + *ppFan1 = Aig_ObjChild1(p0); + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Returns i-th elementary variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_IthVar( Aig_Man_t * p, int i ) +{ + int v; + for ( v = Aig_ManPiNum(p); v <= i; v++ ) + Aig_ObjCreatePi( p ); + assert( i < Vec_PtrSize(p->vPis) ); + return Aig_ManPi( p, i ); +} + +/**Function************************************************************* + + Synopsis [Perform one operation.] + + Description [The argument nodes can be complemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_Oper( Aig_Man_t * p, Aig_Obj_t * p0, Aig_Obj_t * p1, Aig_Type_t Type ) +{ + if ( Type == AIG_OBJ_AND ) + return Aig_And( p, p0, p1 ); + if ( Type == AIG_OBJ_EXOR ) + return Aig_Exor( p, p0, p1 ); + assert( 0 ); + return NULL; +} + +/**Function************************************************************* + + Synopsis [Creates the canonical form of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_CanonPair_rec( Aig_Man_t * p, Aig_Obj_t * pGhost ) +{ + Aig_Obj_t * pResult, * pLat0, * pLat1; + int fCompl0, fCompl1; + Aig_Type_t Type; + assert( Aig_ObjIsNode(pGhost) ); + // consider the case when the pair is canonical + if ( !Aig_ObjIsLatch(Aig_ObjFanin0(pGhost)) || !Aig_ObjIsLatch(Aig_ObjFanin1(pGhost)) ) + { + if ( pResult = Aig_TableLookup( p, pGhost ) ) + return pResult; + return Aig_ObjCreate( p, pGhost ); + } + /// remember the latches + pLat0 = Aig_ObjFanin0(pGhost); + pLat1 = Aig_ObjFanin1(pGhost); + // remember type and compls + Type = Aig_ObjType(pGhost); + fCompl0 = Aig_ObjFaninC0(pGhost); + fCompl1 = Aig_ObjFaninC1(pGhost); + // call recursively + pResult = Aig_Oper( p, Aig_NotCond(Aig_ObjChild0(pLat0), fCompl0), Aig_NotCond(Aig_ObjChild0(pLat1), fCompl1), Type ); + // build latch on top of this + return Aig_Latch( p, pResult, (Type == AIG_OBJ_AND)? fCompl0 & fCompl1 : fCompl0 ^ fCompl1 ); +} + +/**Function************************************************************* + + Synopsis [Performs canonicization step.] + + Description [The argument nodes can be complemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_And( Aig_Man_t * p, Aig_Obj_t * p0, Aig_Obj_t * p1 ) +{ + Aig_Obj_t * pGhost, * pResult; +// Aig_Obj_t * pFan0, * pFan1; + // check trivial cases + if ( p0 == p1 ) + return p0; + if ( p0 == Aig_Not(p1) ) + return Aig_Not(p->pConst1); + if ( Aig_Regular(p0) == p->pConst1 ) + return p0 == p->pConst1 ? p1 : Aig_Not(p->pConst1); + if ( Aig_Regular(p1) == p->pConst1 ) + return p1 == p->pConst1 ? p0 : Aig_Not(p->pConst1); + // check if it can be an EXOR gate +// if ( Aig_ObjIsExorType( p0, p1, &pFan0, &pFan1 ) ) +// return Aig_Exor( p, pFan0, pFan1 ); + pGhost = Aig_ObjCreateGhost( p, p0, p1, AIG_OBJ_AND ); + pResult = Aig_CanonPair_rec( p, pGhost ); + return pResult; +} + +/**Function************************************************************* + + Synopsis [Creates the canonical form of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_Latch( Aig_Man_t * p, Aig_Obj_t * pObj, int fInitOne ) +{ + Aig_Obj_t * pGhost, * pResult; + pGhost = Aig_ObjCreateGhost( p, Aig_NotCond(pObj, fInitOne), NULL, AIG_OBJ_LATCH ); + pResult = Aig_TableLookup( p, pGhost ); + if ( pResult == NULL ) + pResult = Aig_ObjCreate( p, pGhost ); + return Aig_NotCond( pResult, fInitOne ); +} + +/**Function************************************************************* + + Synopsis [Performs canonicization step.] + + Description [The argument nodes can be complemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_Exor( Aig_Man_t * p, Aig_Obj_t * p0, Aig_Obj_t * p1 ) +{ +/* + Aig_Obj_t * pGhost, * pResult; + // check trivial cases + if ( p0 == p1 ) + return Aig_Not(p->pConst1); + if ( p0 == Aig_Not(p1) ) + return p->pConst1; + if ( Aig_Regular(p0) == p->pConst1 ) + return Aig_NotCond( p1, p0 == p->pConst1 ); + if ( Aig_Regular(p1) == p->pConst1 ) + return Aig_NotCond( p0, p1 == p->pConst1 ); + // check the table + pGhost = Aig_ObjCreateGhost( p, p0, p1, AIG_OBJ_EXOR ); + if ( pResult = Aig_TableLookup( p, pGhost ) ) + return pResult; + return Aig_ObjCreate( p, pGhost ); +*/ + return Aig_Or( p, Aig_And(p, p0, Aig_Not(p1)), Aig_And(p, Aig_Not(p0), p1) ); +} + +/**Function************************************************************* + + Synopsis [Implements Boolean OR.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_Or( Aig_Man_t * p, Aig_Obj_t * p0, Aig_Obj_t * p1 ) +{ + return Aig_Not( Aig_And( p, Aig_Not(p0), Aig_Not(p1) ) ); +} + +/**Function************************************************************* + + Synopsis [Implements ITE operation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_Mux( Aig_Man_t * p, Aig_Obj_t * pC, Aig_Obj_t * p1, Aig_Obj_t * p0 ) +{ +/* + Aig_Obj_t * pTempA1, * pTempA2, * pTempB1, * pTempB2, * pTemp; + int Count0, Count1; + // consider trivial cases + if ( p0 == Aig_Not(p1) ) + return Aig_Exor( p, pC, p0 ); + // other cases can be added + // implement the first MUX (F = C * x1 + C' * x0) + + // check for constants here!!! + + pTempA1 = Aig_TableLookup( p, Aig_ObjCreateGhost(p, pC, p1, AIG_OBJ_AND) ); + pTempA2 = Aig_TableLookup( p, Aig_ObjCreateGhost(p, Aig_Not(pC), p0, AIG_OBJ_AND) ); + if ( pTempA1 && pTempA2 ) + { + pTemp = Aig_TableLookup( p, Aig_ObjCreateGhost(p, Aig_Not(pTempA1), Aig_Not(pTempA2), AIG_OBJ_AND) ); + if ( pTemp ) return Aig_Not(pTemp); + } + Count0 = (pTempA1 != NULL) + (pTempA2 != NULL); + // implement the second MUX (F' = C * x1' + C' * x0') + pTempB1 = Aig_TableLookup( p, Aig_ObjCreateGhost(p, pC, Aig_Not(p1), AIG_OBJ_AND) ); + pTempB2 = Aig_TableLookup( p, Aig_ObjCreateGhost(p, Aig_Not(pC), Aig_Not(p0), AIG_OBJ_AND) ); + if ( pTempB1 && pTempB2 ) + { + pTemp = Aig_TableLookup( p, Aig_ObjCreateGhost(p, Aig_Not(pTempB1), Aig_Not(pTempB2), AIG_OBJ_AND) ); + if ( pTemp ) return pTemp; + } + Count1 = (pTempB1 != NULL) + (pTempB2 != NULL); + // compare and decide which one to implement + if ( Count0 >= Count1 ) + { + pTempA1 = pTempA1? pTempA1 : Aig_And(p, pC, p1); + pTempA2 = pTempA2? pTempA2 : Aig_And(p, Aig_Not(pC), p0); + return Aig_Or( p, pTempA1, pTempA2 ); + } + pTempB1 = pTempB1? pTempB1 : Aig_And(p, pC, Aig_Not(p1)); + pTempB2 = pTempB2? pTempB2 : Aig_And(p, Aig_Not(pC), Aig_Not(p0)); + return Aig_Not( Aig_Or( p, pTempB1, pTempB2 ) ); +*/ + return Aig_Or( p, Aig_And(p, pC, p1), Aig_And(p, Aig_Not(pC), p0) ); +} + +/**Function************************************************************* + + Synopsis [Implements ITE operation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_Maj( Aig_Man_t * p, Aig_Obj_t * pA, Aig_Obj_t * pB, Aig_Obj_t * pC ) +{ + return Aig_Or( p, Aig_Or(p, Aig_And(p, pA, pB), Aig_And(p, pA, pC)), Aig_And(p, pB, pC) ); +} + +/**Function************************************************************* + + Synopsis [Constructs the well-balanced tree of gates.] + + Description [Disregards levels and possible logic sharing.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_Multi_rec( Aig_Man_t * p, Aig_Obj_t ** ppObjs, int nObjs, Aig_Type_t Type ) +{ + Aig_Obj_t * pObj1, * pObj2; + if ( nObjs == 1 ) + return ppObjs[0]; + pObj1 = Aig_Multi_rec( p, ppObjs, nObjs/2, Type ); + pObj2 = Aig_Multi_rec( p, ppObjs + nObjs/2, nObjs - nObjs/2, Type ); + return Aig_Oper( p, pObj1, pObj2, Type ); +} + +/**Function************************************************************* + + Synopsis [Old code.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_Multi( Aig_Man_t * p, Aig_Obj_t ** pArgs, int nArgs, Aig_Type_t Type ) +{ + assert( Type == AIG_OBJ_AND || Type == AIG_OBJ_EXOR ); + assert( nArgs > 0 ); + return Aig_Multi_rec( p, pArgs, nArgs, Type ); +} + +/**Function************************************************************* + + Synopsis [Implements the miter.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_Miter( Aig_Man_t * p, Vec_Ptr_t * vPairs ) +{ + int i; + assert( vPairs->nSize > 0 ); + assert( vPairs->nSize % 2 == 0 ); + for ( i = 0; i < vPairs->nSize; i += 2 ) + vPairs->pArray[i/2] = Aig_Not( Aig_Exor( p, vPairs->pArray[i], vPairs->pArray[i+1] ) ); + vPairs->nSize = vPairs->nSize/2; + return Aig_Not( Aig_Multi_rec( p, (Aig_Obj_t **)vPairs->pArray, vPairs->nSize, AIG_OBJ_AND ) ); +} + +/**Function************************************************************* + + Synopsis [Implements the miter.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_MiterTwo( Aig_Man_t * p, Vec_Ptr_t * vNodes1, Vec_Ptr_t * vNodes2 ) +{ + int i; + assert( vNodes1->nSize > 0 && vNodes1->nSize > 0 ); + assert( vNodes1->nSize == vNodes2->nSize ); + for ( i = 0; i < vNodes1->nSize; i++ ) + vNodes1->pArray[i] = Aig_Not( Aig_Exor( p, vNodes1->pArray[i], vNodes2->pArray[i] ) ); + return Aig_Not( Aig_Multi_rec( p, (Aig_Obj_t **)vNodes1->pArray, vNodes1->nSize, AIG_OBJ_AND ) ); +} + +/**Function************************************************************* + + Synopsis [Creates AND function with nVars inputs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_CreateAnd( Aig_Man_t * p, int nVars ) +{ + Aig_Obj_t * pFunc; + int i; + pFunc = Aig_ManConst1( p ); + for ( i = 0; i < nVars; i++ ) + pFunc = Aig_And( p, pFunc, Aig_IthVar(p, i) ); + return pFunc; +} + +/**Function************************************************************* + + Synopsis [Creates AND function with nVars inputs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_CreateOr( Aig_Man_t * p, int nVars ) +{ + Aig_Obj_t * pFunc; + int i; + pFunc = Aig_ManConst0( p ); + for ( i = 0; i < nVars; i++ ) + pFunc = Aig_Or( p, pFunc, Aig_IthVar(p, i) ); + return pFunc; +} + +/**Function************************************************************* + + Synopsis [Creates AND function with nVars inputs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_CreateExor( Aig_Man_t * p, int nVars ) +{ + Aig_Obj_t * pFunc; + int i; + pFunc = Aig_ManConst0( p ); + for ( i = 0; i < nVars; i++ ) + pFunc = Aig_Exor( p, pFunc, Aig_IthVar(p, i) ); + return pFunc; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/aig/aigOrder.c b/abc_with_bb_support/src/aig/aig/aigOrder.c new file mode 100644 index 000000000..a68daa45a --- /dev/null +++ b/abc_with_bb_support/src/aig/aig/aigOrder.c @@ -0,0 +1,171 @@ +/**CFile**************************************************************** + + FileName [aigOrder.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG package.] + + Synopsis [Dynamically updated topological order.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: aigOrder.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "aig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Initializes the order datastructure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManOrderStart( Aig_Man_t * p ) +{ + Aig_Obj_t * pObj; + int i; + assert( Aig_ManBufNum(p) == 0 ); + // allocate order datastructure + assert( p->pOrderData == NULL ); + p->nOrderAlloc = 2 * Aig_ManObjIdMax(p); + if ( p->nOrderAlloc < (1<<12) ) + p->nOrderAlloc = (1<<12); + p->pOrderData = ALLOC( unsigned, 2 * p->nOrderAlloc ); + memset( p->pOrderData, 0xFF, sizeof(unsigned) * 2 * p->nOrderAlloc ); + // add the constant node + p->pOrderData[0] = p->pOrderData[1] = 0; + p->iPrev = p->iNext = 0; + // add the internal nodes + Aig_ManForEachNode( p, pObj, i ) + Aig_ObjOrderInsert( p, pObj->Id ); +} + +/**Function************************************************************* + + Synopsis [Deletes the order datastructure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManOrderStop( Aig_Man_t * p ) +{ + assert( p->pOrderData ); + FREE( p->pOrderData ); + p->nOrderAlloc = 0; + p->iPrev = p->iNext = 0; +} + +/**Function************************************************************* + + Synopsis [Inserts an entry before iNext.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ObjOrderInsert( Aig_Man_t * p, int ObjId ) +{ + int iPrev; + assert( ObjId != 0 ); + assert( Aig_ObjIsNode( Aig_ManObj(p, ObjId) ) ); + if ( ObjId >= p->nOrderAlloc ) + { + int nOrderAlloc = 2 * ObjId; + p->pOrderData = REALLOC( unsigned, p->pOrderData, 2 * nOrderAlloc ); + memset( p->pOrderData + 2 * p->nOrderAlloc, 0xFF, sizeof(unsigned) * 2 * (nOrderAlloc - p->nOrderAlloc) ); + p->nOrderAlloc = nOrderAlloc; + } + assert( p->pOrderData[2*ObjId] == 0xFFFFFFFF ); // prev + assert( p->pOrderData[2*ObjId+1] == 0xFFFFFFFF ); // next + iPrev = p->pOrderData[2*p->iNext]; + assert( p->pOrderData[2*iPrev+1] == (unsigned)p->iNext ); + p->pOrderData[2*ObjId] = iPrev; + p->pOrderData[2*iPrev+1] = ObjId; + p->pOrderData[2*p->iNext] = ObjId; + p->pOrderData[2*ObjId+1] = p->iNext; + p->nAndTotal++; +} + +/**Function************************************************************* + + Synopsis [Removes the entry.] + + Description [If iPrev is removed, it slides backward. + If iNext is removed, it slides forward.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ObjOrderRemove( Aig_Man_t * p, int ObjId ) +{ + int iPrev, iNext; + assert( ObjId != 0 ); + assert( Aig_ObjIsNode( Aig_ManObj(p, ObjId) ) ); + iPrev = p->pOrderData[2*ObjId]; + iNext = p->pOrderData[2*ObjId+1]; + p->pOrderData[2*ObjId] = 0xFFFFFFFF; + p->pOrderData[2*ObjId+1] = 0xFFFFFFFF; + p->pOrderData[2*iNext] = iPrev; + p->pOrderData[2*iPrev+1] = iNext; + if ( p->iPrev == ObjId ) + { + p->nAndPrev--; + p->iPrev = iPrev; + } + if ( p->iNext == ObjId ) + p->iNext = iNext; + p->nAndTotal--; +} + +/**Function************************************************************* + + Synopsis [Advances the order forward.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ObjOrderAdvance( Aig_Man_t * p ) +{ + assert( p->pOrderData ); + assert( p->pOrderData[2*p->iPrev+1] == (unsigned)p->iNext ); + p->iPrev = p->iNext; + p->nAndPrev++; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/aig/aigPart.c b/abc_with_bb_support/src/aig/aig/aigPart.c new file mode 100644 index 000000000..03b39f000 --- /dev/null +++ b/abc_with_bb_support/src/aig/aig/aigPart.c @@ -0,0 +1,928 @@ +/**CFile**************************************************************** + + FileName [aigPart.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG package.] + + Synopsis [AIG partitioning package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: aigPart.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "aig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Part_Man_t_ Part_Man_t; +struct Part_Man_t_ +{ + int nChunkSize; // the size of one chunk of memory (~1 Mb) + int nStepSize; // the step size in saving memory (~64 bytes) + char * pFreeBuf; // the pointer to free memory + int nFreeSize; // the size of remaining free memory + Vec_Ptr_t * vMemory; // the memory allocated + Vec_Ptr_t * vFree; // the vector of free pieces of memory +}; + +typedef struct Part_One_t_ Part_One_t; +struct Part_One_t_ +{ + int nRefs; // the number of references + int nOuts; // the number of outputs + int nOutsAlloc; // the array size + int pOuts[0]; // the array of outputs +}; + +static inline int Part_SizeType( int nSize, int nStepSize ) { return nSize / nStepSize + ((nSize % nStepSize) > 0); } +static inline char * Part_OneNext( char * pPart ) { return *((char **)pPart); } +static inline void Part_OneSetNext( char * pPart, char * pNext ) { *((char **)pPart) = pNext; } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Start the memory manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Part_Man_t * Part_ManStart( int nChunkSize, int nStepSize ) +{ + Part_Man_t * p; + p = ALLOC( Part_Man_t, 1 ); + memset( p, 0, sizeof(Part_Man_t) ); + p->nChunkSize = nChunkSize; + p->nStepSize = nStepSize; + p->vMemory = Vec_PtrAlloc( 1000 ); + p->vFree = Vec_PtrAlloc( 1000 ); + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the memory manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Part_ManStop( Part_Man_t * p ) +{ + void * pMemory; + int i; + Vec_PtrForEachEntry( p->vMemory, pMemory, i ) + free( pMemory ); + Vec_PtrFree( p->vMemory ); + Vec_PtrFree( p->vFree ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Fetches the memory entry of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Part_ManFetch( Part_Man_t * p, int nSize ) +{ + int Type, nSizeReal; + char * pMemory; + assert( nSize > 0 ); + Type = Part_SizeType( nSize, p->nStepSize ); + Vec_PtrFillExtra( p->vFree, Type + 1, NULL ); + if ( pMemory = Vec_PtrEntry( p->vFree, Type ) ) + { + Vec_PtrWriteEntry( p->vFree, Type, Part_OneNext(pMemory) ); + return pMemory; + } + nSizeReal = p->nStepSize * Type; + if ( p->nFreeSize < nSizeReal ) + { + p->pFreeBuf = ALLOC( char, p->nChunkSize ); + p->nFreeSize = p->nChunkSize; + Vec_PtrPush( p->vMemory, p->pFreeBuf ); + } + assert( p->nFreeSize >= nSizeReal ); + pMemory = p->pFreeBuf; + p->pFreeBuf += nSizeReal; + p->nFreeSize -= nSizeReal; + return pMemory; +} + +/**Function************************************************************* + + Synopsis [Recycles the memory entry of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Part_ManRecycle( Part_Man_t * p, char * pMemory, int nSize ) +{ + int Type; + Type = Part_SizeType( nSize, p->nStepSize ); + Vec_PtrFillExtra( p->vFree, Type + 1, NULL ); + Part_OneSetNext( pMemory, Vec_PtrEntry(p->vFree, Type) ); + Vec_PtrWriteEntry( p->vFree, Type, pMemory ); +} + +/**Function************************************************************* + + Synopsis [Fetches the memory entry of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Part_One_t * Part_ManFetchEntry( Part_Man_t * p, int nWords, int nRefs ) +{ + Part_One_t * pPart; + pPart = (Part_One_t *)Part_ManFetch( p, sizeof(Part_One_t) + sizeof(int) * nWords ); + pPart->nRefs = nRefs; + pPart->nOuts = 0; + pPart->nOutsAlloc = nWords; + return pPart; +} + +/**Function************************************************************* + + Synopsis [Recycles the memory entry of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Part_ManRecycleEntry( Part_Man_t * p, Part_One_t * pEntry ) +{ + assert( pEntry->nOuts <= pEntry->nOutsAlloc ); + assert( pEntry->nOuts >= pEntry->nOutsAlloc/2 ); + Part_ManRecycle( p, (char *)pEntry, sizeof(Part_One_t) + sizeof(int) * pEntry->nOutsAlloc ); +} + +/**Function************************************************************* + + Synopsis [Merges two entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Part_One_t * Part_ManMergeEntry( Part_Man_t * pMan, Part_One_t * p1, Part_One_t * p2, int nRefs ) +{ + Part_One_t * p = Part_ManFetchEntry( pMan, p1->nOuts + p2->nOuts, nRefs ); + int * pBeg1 = p1->pOuts; + int * pBeg2 = p2->pOuts; + int * pBeg = p->pOuts; + int * pEnd1 = p1->pOuts + p1->nOuts; + int * pEnd2 = p2->pOuts + p2->nOuts; + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + *pBeg++ = *pBeg1++, pBeg2++; + else if ( *pBeg1 < *pBeg2 ) + *pBeg++ = *pBeg1++; + else + *pBeg++ = *pBeg2++; + } + while ( pBeg1 < pEnd1 ) + *pBeg++ = *pBeg1++; + while ( pBeg2 < pEnd2 ) + *pBeg++ = *pBeg2++; + p->nOuts = pBeg - p->pOuts; + assert( p->nOuts <= p->nOutsAlloc ); + assert( p->nOuts >= p1->nOuts ); + assert( p->nOuts >= p2->nOuts ); + return p; +} + +/**Function************************************************************* + + Synopsis [Tranfers the entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Part_ManTransferEntry( Part_One_t * p ) +{ + Vec_Int_t * vSupp; + int i; + vSupp = Vec_IntAlloc( p->nOuts ); + for ( i = 0; i < p->nOuts; i++ ) + Vec_IntPush( vSupp, p->pOuts[i] ); + return vSupp; +} + +/**Function************************************************************* + + Synopsis [Computes supports of the POs in the multi-output AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Vec_t * Aig_ManSupports( Aig_Man_t * pMan ) +{ + Vec_Vec_t * vSupps; + Vec_Int_t * vSupp; + Part_Man_t * p; + Part_One_t * pPart0, * pPart1; + Aig_Obj_t * pObj; + int i, iIns = 0, iOut = 0; + // start the support computation manager + p = Part_ManStart( 1 << 20, 1 << 6 ); + // consider objects in the topological order + vSupps = Vec_VecAlloc( Aig_ManPoNum(pMan) ); + Aig_ManCleanData(pMan); + Aig_ManForEachObj( pMan, pObj, i ) + { + if ( Aig_ObjIsNode(pObj) ) + { + pPart0 = Aig_ObjFanin0(pObj)->pData; + pPart1 = Aig_ObjFanin1(pObj)->pData; + pObj->pData = Part_ManMergeEntry( p, pPart0, pPart1, pObj->nRefs ); + assert( pPart0->nRefs > 0 ); + if ( --pPart0->nRefs == 0 ) + Part_ManRecycleEntry( p, pPart0 ); + assert( pPart1->nRefs > 0 ); + if ( --pPart1->nRefs == 0 ) + Part_ManRecycleEntry( p, pPart1 ); + continue; + } + if ( Aig_ObjIsPo(pObj) ) + { + pPart0 = Aig_ObjFanin0(pObj)->pData; + vSupp = Part_ManTransferEntry(pPart0); + Vec_IntPush( vSupp, iOut++ ); + Vec_PtrPush( (Vec_Ptr_t *)vSupps, vSupp ); + assert( pPart0->nRefs > 0 ); + if ( --pPart0->nRefs == 0 ) + Part_ManRecycleEntry( p, pPart0 ); + continue; + } + if ( Aig_ObjIsPi(pObj) ) + { + if ( pObj->nRefs ) + { + pPart0 = Part_ManFetchEntry( p, 1, pObj->nRefs ); + pPart0->pOuts[ pPart0->nOuts++ ] = iIns++; + pObj->pData = pPart0; + } + continue; + } + if ( Aig_ObjIsConst1(pObj) ) + { + if ( pObj->nRefs ) + pObj->pData = Part_ManFetchEntry( p, 0, pObj->nRefs ); + continue; + } + assert( 0 ); + } +printf( "Memory usage = %d Mb.\n", Vec_PtrSize(p->vMemory) * p->nChunkSize / (1<<20) ); + Part_ManStop( p ); + // sort supports by size + Vec_VecSort( vSupps, 1 ); +/* + Aig_ManForEachPo( pMan, pObj, i ) + printf( "%d ", Vec_IntSize( (Vec_Int_t *)Vec_VecEntry(vSupps, i) ) ); + printf( "\n" ); +*/ + return vSupps; +} + +/**Function************************************************************* + + Synopsis [Start char-bases support representation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Aig_ManSuppCharStart( Vec_Int_t * vOne, int nPis ) +{ + char * pBuffer; + int i, Entry; + pBuffer = ALLOC( char, nPis ); + memset( pBuffer, 0, sizeof(char) * nPis ); + Vec_IntForEachEntry( vOne, Entry, i ) + { + assert( Entry < nPis ); + pBuffer[Entry] = 1; + } + return pBuffer; +} + +/**Function************************************************************* + + Synopsis [Add to char-bases support representation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManSuppCharAdd( char * pBuffer, Vec_Int_t * vOne, int nPis ) +{ + int i, Entry; + Vec_IntForEachEntry( vOne, Entry, i ) + { + assert( Entry < nPis ); + pBuffer[Entry] = 1; + } +} + +/**Function************************************************************* + + Synopsis [Find the common variables using char-bases support representation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ManSuppCharCommon( char * pBuffer, Vec_Int_t * vOne ) +{ + int i, Entry, nCommon = 0; + Vec_IntForEachEntry( vOne, Entry, i ) + nCommon += pBuffer[Entry]; + return nCommon; +} + +/**Function************************************************************* + + Synopsis [Find the best partition.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ManPartitionSmartFindPart( Vec_Ptr_t * vPartSuppsAll, Vec_Ptr_t * vPartsAll, Vec_Ptr_t * vPartSuppsChar, int nPartSizeLimit, Vec_Int_t * vOne ) +{ + Vec_Int_t * vPartSupp, * vPart; + int Attract, Repulse, Value, ValueBest; + int i, nCommon, iBest; + iBest = -1; + ValueBest = 0; + Vec_PtrForEachEntry( vPartSuppsAll, vPartSupp, i ) + { + vPart = Vec_PtrEntry( vPartsAll, i ); + if ( nPartSizeLimit > 0 && Vec_IntSize(vPart) >= nPartSizeLimit ) + continue; +// nCommon = Vec_IntTwoCountCommon( vPartSupp, vOne ); + nCommon = Aig_ManSuppCharCommon( Vec_PtrEntry(vPartSuppsChar, i), vOne ); + if ( nCommon == 0 ) + continue; + if ( nCommon == Vec_IntSize(vOne) ) + return i; + Attract = 1000 * nCommon / Vec_IntSize(vOne); + if ( Vec_IntSize(vPartSupp) < 100 ) + Repulse = 1; + else + Repulse = 1+Extra_Base2Log(Vec_IntSize(vPartSupp)-100); + Value = Attract/Repulse; + if ( ValueBest < Value ) + { + ValueBest = Value; + iBest = i; + } + } + if ( ValueBest < 75 ) + return -1; + return iBest; +} + +/**Function************************************************************* + + Synopsis [Perform the smart partitioning.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManPartitionPrint( Aig_Man_t * p, Vec_Ptr_t * vPartsAll, Vec_Ptr_t * vPartSuppsAll ) +{ + Vec_Int_t * vOne; + int i, nOutputs, Counter; + + Counter = 0; + Vec_PtrForEachEntry( vPartSuppsAll, vOne, i ) + { + nOutputs = Vec_IntSize(Vec_PtrEntry(vPartsAll, i)); + printf( "%d=(%d,%d) ", i, Vec_IntSize(vOne), nOutputs ); + Counter += nOutputs; + if ( i == Vec_PtrSize(vPartsAll) - 1 ) + break; + } + assert( Counter == Aig_ManPoNum(p) ); + printf( "\nTotal = %d. Outputs = %d.\n", Counter, Aig_ManPoNum(p) ); +} + +/**Function************************************************************* + + Synopsis [Perform the smart partitioning.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManPartitionCompact( Vec_Ptr_t * vPartsAll, Vec_Ptr_t * vPartSuppsAll, int nPartSizeLimit ) +{ + Vec_Int_t * vOne, * vPart, * vPartSupp, * vTemp; + int i, iPart; + + if ( nPartSizeLimit == 0 ) + nPartSizeLimit = 200; + + // pack smaller partitions into larger blocks + iPart = 0; + vPart = vPartSupp = NULL; + Vec_PtrForEachEntry( vPartSuppsAll, vOne, i ) + { + if ( Vec_IntSize(vOne) < nPartSizeLimit ) + { + if ( vPartSupp == NULL ) + { + assert( vPart == NULL ); + vPartSupp = Vec_IntDup(vOne); + vPart = Vec_PtrEntry(vPartsAll, i); + } + else + { + vPartSupp = Vec_IntTwoMerge( vTemp = vPartSupp, vOne ); + Vec_IntFree( vTemp ); + vPart = Vec_IntTwoMerge( vTemp = vPart, Vec_PtrEntry(vPartsAll, i) ); + Vec_IntFree( vTemp ); + Vec_IntFree( Vec_PtrEntry(vPartsAll, i) ); + } + if ( Vec_IntSize(vPartSupp) < nPartSizeLimit ) + continue; + } + else + vPart = Vec_PtrEntry(vPartsAll, i); + // add the partition + Vec_PtrWriteEntry( vPartsAll, iPart, vPart ); + vPart = NULL; + if ( vPartSupp ) + { + Vec_IntFree( Vec_PtrEntry(vPartSuppsAll, iPart) ); + Vec_PtrWriteEntry( vPartSuppsAll, iPart, vPartSupp ); + vPartSupp = NULL; + } + iPart++; + } + // add the last one + if ( vPart ) + { + Vec_PtrWriteEntry( vPartsAll, iPart, vPart ); + vPart = NULL; + + assert( vPartSupp != NULL ); + Vec_IntFree( Vec_PtrEntry(vPartSuppsAll, iPart) ); + Vec_PtrWriteEntry( vPartSuppsAll, iPart, vPartSupp ); + vPartSupp = NULL; + iPart++; + } + Vec_PtrShrink( vPartsAll, iPart ); + Vec_PtrShrink( vPartsAll, iPart ); +} + +/**Function************************************************************* + + Synopsis [Perform the smart partitioning.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Vec_t * Aig_ManPartitionSmart( Aig_Man_t * p, int nPartSizeLimit, int fVerbose, Vec_Vec_t ** pvPartSupps ) +{ + Vec_Ptr_t * vPartSuppsChar; + Vec_Ptr_t * vSupps, * vPartsAll, * vPartsAll2, * vPartSuppsAll;//, * vPartPtr; + Vec_Int_t * vOne, * vPart, * vPartSupp, * vTemp; + int i, iPart, iOut, clk; + + // compute the supports for all outputs +clk = clock(); + vSupps = (Vec_Ptr_t *)Aig_ManSupports( p ); +if ( fVerbose ) +{ +PRT( "Supps", clock() - clk ); +} + // start char-based support representation + vPartSuppsChar = Vec_PtrAlloc( 1000 ); + + // create partitions +clk = clock(); + vPartsAll = Vec_PtrAlloc( 256 ); + vPartSuppsAll = Vec_PtrAlloc( 256 ); + Vec_PtrForEachEntry( vSupps, vOne, i ) + { + // get the output number + iOut = Vec_IntPop(vOne); + // find closely matching part + iPart = Aig_ManPartitionSmartFindPart( vPartSuppsAll, vPartsAll, vPartSuppsChar, nPartSizeLimit, vOne ); +//printf( "%4d->%4d ", i, iPart ); + if ( iPart == -1 ) + { + // create new partition + vPart = Vec_IntAlloc( 32 ); + Vec_IntPush( vPart, iOut ); + // create new partition support + vPartSupp = Vec_IntDup( vOne ); + // add this partition and its support + Vec_PtrPush( vPartsAll, vPart ); + Vec_PtrPush( vPartSuppsAll, vPartSupp ); + + Vec_PtrPush( vPartSuppsChar, Aig_ManSuppCharStart(vOne, Aig_ManPiNum(p)) ); + } + else + { + // add output to this partition + vPart = Vec_PtrEntry( vPartsAll, iPart ); + Vec_IntPush( vPart, iOut ); + // merge supports + vPartSupp = Vec_PtrEntry( vPartSuppsAll, iPart ); + vPartSupp = Vec_IntTwoMerge( vTemp = vPartSupp, vOne ); + Vec_IntFree( vTemp ); + // reinsert new support + Vec_PtrWriteEntry( vPartSuppsAll, iPart, vPartSupp ); + + Aig_ManSuppCharAdd( Vec_PtrEntry(vPartSuppsChar, iPart), vOne, Aig_ManPiNum(p) ); + } + } + + // stop char-based support representation + Vec_PtrForEachEntry( vPartSuppsChar, vTemp, i ) + free( vTemp ); + Vec_PtrFree( vPartSuppsChar ); + +//printf( "\n" ); +if ( fVerbose ) +{ +PRT( "Parts", clock() - clk ); +} + +clk = clock(); + // reorder partitions in the decreasing order of support sizes + // remember partition number in each partition support + Vec_PtrForEachEntry( vPartSuppsAll, vOne, i ) + Vec_IntPush( vOne, i ); + // sort the supports in the decreasing order + Vec_VecSort( (Vec_Vec_t *)vPartSuppsAll, 1 ); + // reproduce partitions + vPartsAll2 = Vec_PtrAlloc( 256 ); + Vec_PtrForEachEntry( vPartSuppsAll, vOne, i ) + Vec_PtrPush( vPartsAll2, Vec_PtrEntry(vPartsAll, Vec_IntPop(vOne)) ); + Vec_PtrFree( vPartsAll ); + vPartsAll = vPartsAll2; + + // compact small partitions +// Aig_ManPartitionPrint( p, vPartsAll, vPartSuppsAll ); + Aig_ManPartitionCompact( vPartsAll, vPartSuppsAll, nPartSizeLimit ); + if ( fVerbose ) +// Aig_ManPartitionPrint( p, vPartsAll, vPartSuppsAll ); + printf( "Created %d partitions.\n", Vec_PtrSize(vPartsAll) ); + +if ( fVerbose ) +{ +//PRT( "Comps", clock() - clk ); +} + + // cleanup + Vec_VecFree( (Vec_Vec_t *)vSupps ); + if ( pvPartSupps == NULL ) + Vec_VecFree( (Vec_Vec_t *)vPartSuppsAll ); + else + *pvPartSupps = (Vec_Vec_t *)vPartSuppsAll; +/* + // converts from intergers to nodes + Vec_PtrForEachEntry( vPartsAll, vPart, iPart ) + { + vPartPtr = Vec_PtrAlloc( Vec_IntSize(vPart) ); + Vec_IntForEachEntry( vPart, iOut, i ) + Vec_PtrPush( vPartPtr, Aig_ManPo(p, iOut) ); + Vec_IntFree( vPart ); + Vec_PtrWriteEntry( vPartsAll, iPart, vPartPtr ); + } +*/ + return (Vec_Vec_t *)vPartsAll; +} + +/**Function************************************************************* + + Synopsis [Perform the naive partitioning.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Vec_t * Aig_ManPartitionNaive( Aig_Man_t * p, int nPartSize ) +{ + Vec_Vec_t * vParts; + Aig_Obj_t * pObj; + int nParts, i; + nParts = (Aig_ManPoNum(p) / nPartSize) + ((Aig_ManPoNum(p) % nPartSize) > 0); + vParts = Vec_VecStart( nParts ); + Aig_ManForEachPo( p, pObj, i ) + Vec_VecPush( vParts, i / nPartSize, pObj ); + return vParts; +} + + + +/**Function************************************************************* + + Synopsis [Adds internal nodes in the topological order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_ManDupPart_rec( Aig_Man_t * pNew, Aig_Man_t * p, Aig_Obj_t * pObj ) +{ + assert( !Aig_IsComplement(pObj) ); + if ( Aig_ObjIsTravIdCurrent(p, pObj) ) + return pObj->pData; + assert( Aig_ObjIsNode(pObj) ); + Aig_ManDupPart_rec( pNew, p, Aig_ObjFanin0(pObj) ); + Aig_ManDupPart_rec( pNew, p, Aig_ObjFanin1(pObj) ); + pObj->pData = Aig_And( pNew, Aig_ObjChild0Copy(pObj), Aig_ObjChild1Copy(pObj) ); + assert( !Aig_ObjIsTravIdCurrent(p, pObj) ); // loop detection + Aig_ObjSetTravIdCurrent(p, pObj); + return pObj->pData; +} + +/**Function************************************************************* + + Synopsis [Adds internal nodes in the topological order.] + + Description [Returns the array of new outputs.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Aig_ManDupPart( Aig_Man_t * pNew, Aig_Man_t * p, Vec_Int_t * vPart, Vec_Int_t * vPartSupp, int fInverse ) +{ + Vec_Ptr_t * vOutputs; + Aig_Obj_t * pObj, * pObjNew; + int Entry, k; + // create the PIs + Aig_ManIncrementTravId( p ); + Aig_ObjSetTravIdCurrent( p, Aig_ManConst1(p) ); + Aig_ManConst1(p)->pData = Aig_ManConst1(pNew); + if ( !fInverse ) + { + Vec_IntForEachEntry( vPartSupp, Entry, k ) + { + pObj = Aig_ManPi( p, Entry ); + pObj->pData = Aig_ManPi( pNew, k ); + Aig_ObjSetTravIdCurrent( p, pObj ); + } + } + else + { + Vec_IntForEachEntry( vPartSupp, Entry, k ) + { + pObj = Aig_ManPi( p, k ); + pObj->pData = Aig_ManPi( pNew, Entry ); + Aig_ObjSetTravIdCurrent( p, pObj ); + } + } + // create the internal nodes + vOutputs = Vec_PtrAlloc( Vec_IntSize(vPart) ); + Vec_IntForEachEntry( vPart, Entry, k ) + { + pObj = Aig_ManPo( p, Entry ); + pObjNew = Aig_ManDupPart_rec( pNew, p, Aig_ObjFanin0(pObj) ); + pObjNew = Aig_NotCond( pObjNew, Aig_ObjFaninC0(pObj) ); + Vec_PtrPush( vOutputs, pObjNew ); + } + return vOutputs; +} + +/**Function************************************************************* + + Synopsis [Create partitioned miter of the two AIGs.] + + Description [Assumes that each output in the second AIG cannot have + more supp vars than the same output in the first AIG.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Aig_ManMiterPartitioned( Aig_Man_t * p1, Aig_Man_t * p2, int nPartSize ) +{ + Aig_Man_t * pNew; + Aig_Obj_t * pMiter; + Vec_Ptr_t * vMiters, * vNodes1, * vNodes2; + Vec_Vec_t * vParts, * vPartSupps; + Vec_Int_t * vPart, * vPartSupp; + int i, k; + // partition the first manager + vParts = Aig_ManPartitionSmart( p1, nPartSize, 0, &vPartSupps ); + // derive miters + vMiters = Vec_PtrAlloc( Vec_VecSize(vParts) ); + for ( i = 0; i < Vec_VecSize(vParts); i++ ) + { + // get partitions and their support + vPart = Vec_PtrEntry( (Vec_Ptr_t *)vParts, i ); + vPartSupp = Vec_PtrEntry( (Vec_Ptr_t *)vPartSupps, i ); + // create the new miter + pNew = Aig_ManStart( 1000 ); + // create the PIs + for ( k = 0; k < Vec_IntSize(vPartSupp); k++ ) + Aig_ObjCreatePi( pNew ); + // copy the components + vNodes1 = Aig_ManDupPart( pNew, p1, vPart, vPartSupp, 0 ); + vNodes2 = Aig_ManDupPart( pNew, p2, vPart, vPartSupp, 0 ); + // create the miter + pMiter = Aig_MiterTwo( pNew, vNodes1, vNodes2 ); + // create the output + Aig_ObjCreatePo( pNew, pMiter ); + // clean up + Aig_ManCleanup( pNew ); + Vec_PtrPush( vMiters, pNew ); + } + Vec_VecFree( vParts ); + Vec_VecFree( vPartSupps ); + return vMiters; +} + +/**Function************************************************************* + + Synopsis [Performs partitioned choice computation.] + + Description [Assumes that each output in the second AIG cannot have + more supp vars than the same output in the first AIG.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Man_t * Aig_ManChoicePartitioned( Vec_Ptr_t * vAigs, int nPartSize ) +{ + Aig_Man_t * pChoice, * pNew, * pAig, * p; + Aig_Obj_t * pObj; + Vec_Ptr_t * vMiters, * vNodes; + Vec_Vec_t * vParts, * vPartSupps; + Vec_Int_t * vPart, * vPartSupp; + int i, k, m; + + // partition the first AIG + assert( Vec_PtrSize(vAigs) > 1 ); + pAig = Vec_PtrEntry( vAigs, 0 ); + vParts = Aig_ManPartitionSmart( pAig, nPartSize, 0, &vPartSupps ); + + // derive the AIG partitions + vMiters = Vec_PtrAlloc( Vec_VecSize(vParts) ); + for ( i = 0; i < Vec_VecSize(vParts); i++ ) + { + // get partitions and their support + vPart = Vec_PtrEntry( (Vec_Ptr_t *)vParts, i ); + vPartSupp = Vec_PtrEntry( (Vec_Ptr_t *)vPartSupps, i ); + // create the new miter + pNew = Aig_ManStart( 1000 ); + // create the PIs + for ( k = 0; k < Vec_IntSize(vPartSupp); k++ ) + Aig_ObjCreatePi( pNew ); + // copy the components + Vec_PtrForEachEntry( vAigs, p, k ) + { + vNodes = Aig_ManDupPart( pNew, p, vPart, vPartSupp, 0 ); + Vec_PtrForEachEntry( vNodes, pObj, m ) + Aig_ObjCreatePo( pNew, pObj ); + Vec_PtrFree( vNodes ); + } + // add to the partitions + Vec_PtrPush( vMiters, pNew ); + } + + // perform choicing for each derived AIG + Vec_PtrForEachEntry( vMiters, pNew, i ) + { + extern Aig_Man_t * Fra_FraigChoice( Aig_Man_t * pManAig ); + pNew = Fra_FraigChoice( p = pNew ); + Vec_PtrWriteEntry( vMiters, i, pNew ); + Aig_ManStop( p ); + } + + // combine the resulting AIGs + pNew = Aig_ManStartFrom( pAig ); + Vec_PtrForEachEntry( vMiters, p, i ) + { + // get partitions and their support + vPart = Vec_PtrEntry( (Vec_Ptr_t *)vParts, i ); + vPartSupp = Vec_PtrEntry( (Vec_Ptr_t *)vPartSupps, i ); + // copy the component + vNodes = Aig_ManDupPart( pNew, p, vPart, vPartSupp, 1 ); + assert( Vec_PtrSize(vNodes) == Vec_VecSize(vParts) * Vec_PtrSize(vAigs) ); + // create choice node + for ( k = 0; k < Vec_VecSize(vParts); k++ ) + { + for ( m = 0; m < Vec_PtrSize(vAigs); m++ ) + { + pObj = Aig_ObjChild0( Vec_PtrEntry(vNodes, k * Vec_PtrSize(vAigs) + m) ); + break; + } + Aig_ObjCreatePo( pNew, pObj ); + } + Vec_PtrFree( vNodes ); + } + Vec_VecFree( vParts ); + Vec_VecFree( vPartSupps ); + + // trasfer representatives + Aig_ManReprStart( pNew, Aig_ManObjIdMax(pNew)+1 ); + Vec_PtrForEachEntry( vMiters, p, i ) + { + Aig_ManTransferRepr( pNew, p ); + Aig_ManStop( p ); + } + Vec_PtrFree( vMiters ); + + // derive the result of choicing + pChoice = Aig_ManRehash( pNew ); + if ( pChoice != pNew ) + Aig_ManStop( pNew ); + return pChoice; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/aig/aigRepr.c b/abc_with_bb_support/src/aig/aig/aigRepr.c new file mode 100644 index 000000000..ecb0fdaca --- /dev/null +++ b/abc_with_bb_support/src/aig/aig/aigRepr.c @@ -0,0 +1,414 @@ +/**CFile**************************************************************** + + FileName [aigRepr.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG package.] + + Synopsis [Handing node representatives.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: aigRepr.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "aig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the array of representatives.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManReprStart( Aig_Man_t * p, int nIdMax ) +{ + assert( Aig_ManBufNum(p) == 0 ); + assert( p->pReprs == NULL ); + p->nReprsAlloc = nIdMax; + p->pReprs = ALLOC( Aig_Obj_t *, p->nReprsAlloc ); + memset( p->pReprs, 0, sizeof(Aig_Obj_t *) * p->nReprsAlloc ); +} + +/**Function************************************************************* + + Synopsis [Stop the array of representatives.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManReprStop( Aig_Man_t * p ) +{ + assert( p->pReprs != NULL ); + FREE( p->pReprs ); + p->nReprsAlloc = 0; +} + +/**Function************************************************************* + + Synopsis [Set the representative.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ObjCreateRepr( Aig_Man_t * p, Aig_Obj_t * pNode1, Aig_Obj_t * pNode2 ) +{ + assert( p->pReprs != NULL ); + assert( !Aig_IsComplement(pNode1) ); + assert( !Aig_IsComplement(pNode2) ); + assert( pNode1->Id < p->nReprsAlloc ); + assert( pNode2->Id < p->nReprsAlloc ); + assert( pNode1->Id < pNode2->Id ); + p->pReprs[pNode2->Id] = pNode1; +} + +/**Function************************************************************* + + Synopsis [Set the representative.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Aig_ObjSetRepr( Aig_Man_t * p, Aig_Obj_t * pNode1, Aig_Obj_t * pNode2 ) +{ + assert( p->pReprs != NULL ); + assert( !Aig_IsComplement(pNode1) ); + assert( !Aig_IsComplement(pNode2) ); + assert( pNode1->Id < p->nReprsAlloc ); + assert( pNode2->Id < p->nReprsAlloc ); + if ( pNode1 == pNode2 ) + return; + if ( pNode1->Id < pNode2->Id ) + p->pReprs[pNode2->Id] = pNode1; + else + p->pReprs[pNode1->Id] = pNode2; +} + +/**Function************************************************************* + + Synopsis [Find representative.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Aig_Obj_t * Aig_ObjFindRepr( Aig_Man_t * p, Aig_Obj_t * pNode ) +{ + assert( p->pReprs != NULL ); + assert( !Aig_IsComplement(pNode) ); + assert( pNode->Id < p->nReprsAlloc ); + assert( !p->pReprs[pNode->Id] || p->pReprs[pNode->Id]->Id < pNode->Id ); + return p->pReprs[pNode->Id]; +} + +/**Function************************************************************* + + Synopsis [Clears the representative.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Aig_ObjClearRepr( Aig_Man_t * p, Aig_Obj_t * pNode ) +{ + assert( p->pReprs != NULL ); + assert( !Aig_IsComplement(pNode) ); + assert( pNode->Id < p->nReprsAlloc ); + p->pReprs[pNode->Id] = NULL; +} + +/**Function************************************************************* + + Synopsis [Find representative transitively.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Aig_Obj_t * Aig_ObjFindReprTrans( Aig_Man_t * p, Aig_Obj_t * pNode ) +{ + Aig_Obj_t * pPrev, * pRepr; + for ( pPrev = NULL, pRepr = Aig_ObjFindRepr(p, pNode); pRepr; pPrev = pRepr, pRepr = Aig_ObjFindRepr(p, pRepr) ); + return pPrev; +} + +/**Function************************************************************* + + Synopsis [Returns representatives of fanin in approapriate polarity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Aig_Obj_t * Aig_ObjRepr( Aig_Man_t * p, Aig_Obj_t * pObj ) +{ + Aig_Obj_t * pRepr; + if ( pRepr = Aig_ObjFindRepr(p, pObj) ) + return Aig_NotCond( pRepr->pData, pObj->fPhase ^ pRepr->fPhase ); + return pObj->pData; +} +static inline Aig_Obj_t * Aig_ObjChild0Repr( Aig_Man_t * p, Aig_Obj_t * pObj ) { return Aig_NotCond( Aig_ObjRepr(p, Aig_ObjFanin0(pObj)), Aig_ObjFaninC0(pObj) ); } +static inline Aig_Obj_t * Aig_ObjChild1Repr( Aig_Man_t * p, Aig_Obj_t * pObj ) { return Aig_NotCond( Aig_ObjRepr(p, Aig_ObjFanin1(pObj)), Aig_ObjFaninC1(pObj) ); } + +/**Function************************************************************* + + Synopsis [Duplicates AIG while substituting representatives.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManTransferRepr( Aig_Man_t * pNew, Aig_Man_t * p ) +{ + Aig_Obj_t * pObj, * pRepr; + int k; + assert( pNew->pReprs != NULL ); + // go through the nodes which have representatives + Aig_ManForEachObj( p, pObj, k ) + if ( pRepr = Aig_ObjFindRepr(p, pObj) ) + Aig_ObjSetRepr( pNew, Aig_Regular(pRepr->pData), Aig_Regular(pObj->pData) ); +} + +/**Function************************************************************* + + Synopsis [Duplicates AIG while substituting representatives.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Man_t * Aig_ManDupRepr( Aig_Man_t * p ) +{ + Aig_Man_t * pNew; + Aig_Obj_t * pObj, * pRepr; + int i; + // start the HOP package + pNew = Aig_ManStart( Aig_ManObjIdMax(p) + 1 ); + pNew->nRegs = p->nRegs; + Aig_ManReprStart( pNew, Aig_ManObjIdMax(p)+1 ); + // map the const and primary inputs + Aig_ManConst1(p)->pData = Aig_ManConst1(pNew); + Aig_ManForEachPi( p, pObj, i ) + pObj->pData = Aig_ObjCreatePi(pNew); + // map the internal nodes +//printf( "\n" ); + Aig_ManForEachNode( p, pObj, i ) + { + pObj->pData = Aig_And( pNew, Aig_ObjChild0Repr(p, pObj), Aig_ObjChild1Repr(p, pObj) ); + if ( pRepr = Aig_ObjFindRepr(p, pObj) ) // member of the class + { +//printf( "Using node %d for node %d.\n", pRepr->Id, pObj->Id ); + Aig_ObjSetRepr( pNew, Aig_Regular(pRepr->pData), Aig_Regular(pObj->pData) ); + } + } + // transfer the POs + Aig_ManForEachPo( p, pObj, i ) + Aig_ObjCreatePo( pNew, Aig_ObjChild0Repr(p, pObj) ); + // check the new manager + if ( !Aig_ManCheck(pNew) ) + printf( "Aig_ManDupRepr: Check has failed.\n" ); + return pNew; +} + +/**Function************************************************************* + + Synopsis [Transfer representatives and return the number of critical fanouts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ManRemapRepr( Aig_Man_t * p ) +{ + Aig_Obj_t * pObj, * pRepr; + int i, nFanouts = 0; + Aig_ManForEachNode( p, pObj, i ) + { + pRepr = Aig_ObjFindReprTrans( p, pObj ); + if ( pRepr == NULL ) + continue; + assert( pRepr->Id < pObj->Id ); + Aig_ObjSetRepr( p, pObj, pRepr ); + nFanouts += (pObj->nRefs > 0); + } + return nFanouts; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if pOld is in the TFI of pNew.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ObjCheckTfi_rec( Aig_Man_t * p, Aig_Obj_t * pNode, Aig_Obj_t * pOld ) +{ + // check the trivial cases + if ( pNode == NULL ) + return 0; +// if ( pNode->Id < pOld->Id ) // cannot use because of choices of pNode +// return 0; + if ( pNode == pOld ) + return 1; + // skip the visited node + if ( Aig_ObjIsTravIdCurrent( p, pNode ) ) + return 0; + Aig_ObjSetTravIdCurrent( p, pNode ); + // check the children + if ( Aig_ObjCheckTfi_rec( p, Aig_ObjFanin0(pNode), pOld ) ) + return 1; + if ( Aig_ObjCheckTfi_rec( p, Aig_ObjFanin1(pNode), pOld ) ) + return 1; + // check equivalent nodes + return Aig_ObjCheckTfi_rec( p, p->pEquivs[pNode->Id], pOld ); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if pOld is in the TFI of pNew.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ObjCheckTfi( Aig_Man_t * p, Aig_Obj_t * pNew, Aig_Obj_t * pOld ) +{ + assert( !Aig_IsComplement(pNew) ); + assert( !Aig_IsComplement(pOld) ); + Aig_ManIncrementTravId( p ); + return Aig_ObjCheckTfi_rec( p, pNew, pOld ); +} + +/**Function************************************************************* + + Synopsis [Iteratively rehashes the AIG.] + + Description [The input AIG is assumed to have representatives assigned.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Man_t * Aig_ManRehash( Aig_Man_t * p ) +{ + Aig_Man_t * pTemp; + assert( p->pReprs != NULL ); + while ( Aig_ManRemapRepr( p ) ) + { + p = Aig_ManDupRepr( pTemp = p ); + Aig_ManStop( pTemp ); + } + return p; +} + +/**Function************************************************************* + + Synopsis [Creates choices.] + + Description [The input AIG is assumed to have representatives assigned.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManCreateChoices( Aig_Man_t * p ) +{ + Aig_Obj_t * pObj, * pRepr; + int i; + assert( p->pReprs != NULL ); + // create equivalent nodes in the manager + assert( p->pEquivs == NULL ); + p->pEquivs = ALLOC( Aig_Obj_t *, Aig_ManObjIdMax(p) + 1 ); + memset( p->pEquivs, 0, sizeof(Aig_Obj_t *) * (Aig_ManObjIdMax(p) + 1) ); + // make the choice nodes + Aig_ManForEachNode( p, pObj, i ) + { + pRepr = Aig_ObjFindRepr( p, pObj ); + if ( pRepr == NULL ) + continue; + assert( pObj->nRefs == 0 ); + // skip constant and PI classes + if ( !Aig_ObjIsNode(pRepr) ) + { + Aig_ObjClearRepr( p, pObj ); + continue; + } + // skip choices with combinatinal loops + if ( Aig_ObjCheckTfi( p, pObj, pRepr ) ) + { + Aig_ObjClearRepr( p, pObj ); + continue; + } +//printf( "Node %d is represented by node %d.\n", pObj->Id, pRepr->Id ); + // add choice to the choice node + p->pEquivs[pObj->Id] = p->pEquivs[pRepr->Id]; + p->pEquivs[pRepr->Id] = pObj; + } +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/aig/aigSeq.c b/abc_with_bb_support/src/aig/aig/aigSeq.c new file mode 100644 index 000000000..79c28899d --- /dev/null +++ b/abc_with_bb_support/src/aig/aig/aigSeq.c @@ -0,0 +1,500 @@ +/**CFile**************************************************************** + + FileName [aigSeq.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG package.] + + Synopsis [Sequential strashing.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: aigSeq.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "aig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Converts combinational AIG manager into a sequential one.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManSeqStrashConvert( Aig_Man_t * p, int nLatches, int * pInits ) +{ + Aig_Obj_t * pObjLi, * pObjLo, * pLatch; + int i; + assert( Vec_PtrSize( p->vBufs ) == 0 ); + // collect the POs to be converted into latches + for ( i = 0; i < nLatches; i++ ) + { + // get the corresponding PI/PO pair + pObjLi = Aig_ManPo( p, Aig_ManPoNum(p) - nLatches + i ); + pObjLo = Aig_ManPi( p, Aig_ManPiNum(p) - nLatches + i ); + // create latch + pLatch = Aig_Latch( p, Aig_ObjChild0(pObjLi), pInits? pInits[i] : 0 ); + // recycle the old PO object + Aig_ObjDisconnect( p, pObjLi ); + Vec_PtrWriteEntry( p->vObjs, pObjLi->Id, NULL ); + Aig_ManRecycleMemory( p, pObjLi ); + // convert the corresponding PI to be a buffer and connect it to the latch + pObjLo->Type = AIG_OBJ_BUF; + Aig_ObjConnect( p, pObjLo, pLatch, NULL ); + // save the buffer +// Vec_PtrPush( p->vBufs, pObjLo ); + } + // shrink the arrays + Vec_PtrShrink( p->vPis, Aig_ManPiNum(p) - nLatches ); + Vec_PtrShrink( p->vPos, Aig_ManPoNum(p) - nLatches ); + // update the counters of different objects + p->nObjs[AIG_OBJ_PI] -= nLatches; + p->nObjs[AIG_OBJ_PO] -= nLatches; + p->nObjs[AIG_OBJ_BUF] += nLatches; +} + +/**Function************************************************************* + + Synopsis [Collects internal nodes in the DFS order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManDfsSeq_rec( Aig_Man_t * p, Aig_Obj_t * pObj, Vec_Ptr_t * vNodes ) +{ + assert( !Aig_IsComplement(pObj) ); + if ( pObj == NULL ) + return; + if ( Aig_ObjIsTravIdCurrent( p, pObj ) ) + return; + Aig_ObjSetTravIdCurrent( p, pObj ); + if ( Aig_ObjIsPi(pObj) || Aig_ObjIsConst1(pObj) ) + return; + Aig_ManDfsSeq_rec( p, Aig_ObjFanin0(pObj), vNodes ); + Aig_ManDfsSeq_rec( p, Aig_ObjFanin1(pObj), vNodes ); +// if ( (Aig_ObjFanin0(pObj) == NULL || Aig_ObjIsBuf(Aig_ObjFanin0(pObj))) && +// (Aig_ObjFanin1(pObj) == NULL || Aig_ObjIsBuf(Aig_ObjFanin1(pObj))) ) + Vec_PtrPush( vNodes, pObj ); +} + +/**Function************************************************************* + + Synopsis [Collects internal nodes in the DFS order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Aig_ManDfsSeq( Aig_Man_t * p ) +{ + Vec_Ptr_t * vNodes; + Aig_Obj_t * pObj; + int i; + Aig_ManIncrementTravId( p ); + vNodes = Vec_PtrAlloc( Aig_ManNodeNum(p) ); + Aig_ManForEachPo( p, pObj, i ) + Aig_ManDfsSeq_rec( p, Aig_ObjFanin0(pObj), vNodes ); + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Collects internal nodes in the DFS order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManDfsUnreach_rec( Aig_Man_t * p, Aig_Obj_t * pObj, Vec_Ptr_t * vNodes ) +{ + assert( !Aig_IsComplement(pObj) ); + if ( pObj == NULL ) + return; + if ( Aig_ObjIsTravIdPrevious(p, pObj) || Aig_ObjIsTravIdCurrent(p, pObj) ) + return; + Aig_ObjSetTravIdPrevious( p, pObj ); // assume unknown + Aig_ManDfsUnreach_rec( p, Aig_ObjFanin0(pObj), vNodes ); + Aig_ManDfsUnreach_rec( p, Aig_ObjFanin1(pObj), vNodes ); + if ( Aig_ObjIsTravIdPrevious(p, Aig_ObjFanin0(pObj)) && + (Aig_ObjFanin1(pObj) == NULL || Aig_ObjIsTravIdPrevious(p, Aig_ObjFanin1(pObj))) ) + Vec_PtrPush( vNodes, pObj ); + else + Aig_ObjSetTravIdCurrent( p, pObj ); +} + +/**Function************************************************************* + + Synopsis [Collects internal nodes unreachable from PIs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Aig_ManDfsUnreach( Aig_Man_t * p ) +{ + Vec_Ptr_t * vNodes; + Aig_Obj_t * pObj, * pFanin; + int i, k;//, RetValue; + // collect unreachable nodes + Aig_ManIncrementTravId( p ); + Aig_ManIncrementTravId( p ); + // mark the constant and PIs + Aig_ObjSetTravIdPrevious( p, Aig_ManConst1(p) ); + Aig_ManForEachPi( p, pObj, i ) + Aig_ObjSetTravIdCurrent( p, pObj ); + // curr marks visited nodes reachable from PIs + // prev marks visited nodes unreachable or unknown + + // collect the unreachable nodes + vNodes = Vec_PtrAlloc( 32 ); + Aig_ManForEachPo( p, pObj, i ) + Aig_ManDfsUnreach_rec( p, Aig_ObjFanin0(pObj), vNodes ); + + // refine resulting nodes + do + { + k = 0; + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + assert( Aig_ObjIsTravIdPrevious(p, pObj) ); + if ( Aig_ObjIsLatch(pObj) || Aig_ObjIsBuf(pObj) ) + { + pFanin = Aig_ObjFanin0(pObj); + assert( Aig_ObjIsTravIdPrevious(p, pFanin) || Aig_ObjIsTravIdCurrent(p, pFanin) ); + if ( Aig_ObjIsTravIdCurrent(p, pFanin) ) + { + Aig_ObjSetTravIdCurrent( p, pObj ); + continue; + } + } + else // AND gate + { + assert( Aig_ObjIsNode(pObj) ); + pFanin = Aig_ObjFanin0(pObj); + assert( Aig_ObjIsTravIdPrevious(p, pFanin) || Aig_ObjIsTravIdCurrent(p, pFanin) ); + if ( Aig_ObjIsTravIdCurrent(p, pFanin) ) + { + Aig_ObjSetTravIdCurrent( p, pObj ); + continue; + } + pFanin = Aig_ObjFanin1(pObj); + assert( Aig_ObjIsTravIdPrevious(p, pFanin) || Aig_ObjIsTravIdCurrent(p, pFanin) ); + if ( Aig_ObjIsTravIdCurrent(p, pFanin) ) + { + Aig_ObjSetTravIdCurrent( p, pObj ); + continue; + } + } + // write it back + Vec_PtrWriteEntry( vNodes, k++, pObj ); + } + Vec_PtrShrink( vNodes, k ); + } + while ( k < i ); + +// if ( Vec_PtrSize(vNodes) > 0 ) +// printf( "Found %d unreachable.\n", Vec_PtrSize(vNodes) ); + return vNodes; + +/* + // the resulting array contains all unreachable nodes except const 1 + if ( Vec_PtrSize(vNodes) == 0 ) + { + Vec_PtrFree( vNodes ); + return 0; + } + RetValue = Vec_PtrSize(vNodes); + + // mark these nodes + Aig_ManIncrementTravId( p ); + Vec_PtrForEachEntry( vNodes, pObj, i ) + Aig_ObjSetTravIdCurrent( p, pObj ); + Vec_PtrFree( vNodes ); + return RetValue; +*/ +} + + +/**Function************************************************************* + + Synopsis [Removes nodes that do not fanout into POs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ManRemoveUnmarked( Aig_Man_t * p ) +{ + Vec_Ptr_t * vNodes; + Aig_Obj_t * pObj; + int i, RetValue; + // collect unmarked nodes + vNodes = Vec_PtrAlloc( 100 ); + Aig_ManForEachObj( p, pObj, i ) + { + if ( Aig_ObjIsTerm(pObj) ) + continue; + if ( Aig_ObjIsTravIdCurrent(p, pObj) ) + continue; +//Aig_ObjPrintVerbose( pObj, 0 ); + Aig_ObjDisconnect( p, pObj ); + Vec_PtrPush( vNodes, pObj ); + } + if ( Vec_PtrSize(vNodes) == 0 ) + { + Vec_PtrFree( vNodes ); + return 0; + } + // remove the dangling objects + RetValue = Vec_PtrSize(vNodes); + Vec_PtrForEachEntry( vNodes, pObj, i ) + Aig_ObjDelete( p, pObj ); +// printf( "Removed %d dangling.\n", Vec_PtrSize(vNodes) ); + Vec_PtrFree( vNodes ); + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Rehashes the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ManSeqRehashOne( Aig_Man_t * p, Vec_Ptr_t * vNodes, Vec_Ptr_t * vUnreach ) +{ + Aig_Obj_t * pObj, * pObjNew, * pFanin0, * pFanin1; + int i, RetValue = 0, Counter = 0, Counter2 = 0; + + // mark the unreachable nodes + Aig_ManIncrementTravId( p ); + Vec_PtrForEachEntry( vUnreach, pObj, i ) + Aig_ObjSetTravIdCurrent(p, pObj); +/* + // count the number of unreachable object connections + // that is the number of unreachable objects connected to main objects + Aig_ManForEachObj( p, pObj, i ) + { + if ( Aig_ObjIsTravIdCurrent(p, pObj) ) + continue; + + pFanin0 = Aig_ObjFanin0(pObj); + if ( pFanin0 == NULL ) + continue; + if ( Aig_ObjIsTravIdCurrent(p, pFanin0) ) + pFanin0->fMarkA = 1; + + pFanin1 = Aig_ObjFanin1(pObj); + if ( pFanin1 == NULL ) + continue; + if ( Aig_ObjIsTravIdCurrent(p, pFanin1) ) + pFanin1->fMarkA = 1; + } + + // count the objects + Aig_ManForEachObj( p, pObj, i ) + Counter2 += pObj->fMarkA, pObj->fMarkA = 0; + printf( "Connections = %d.\n", Counter2 ); +*/ + + // go through the nodes while skipping unreachable + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + // skip nodes unreachable from the PIs + if ( Aig_ObjIsTravIdCurrent(p, pObj) ) + continue; + // process the node + if ( Aig_ObjIsPo(pObj) ) + { + if ( !Aig_ObjIsBuf(Aig_ObjFanin0(pObj)) ) + continue; + pFanin0 = Aig_ObjReal_rec( Aig_ObjChild0(pObj) ); + Aig_ObjPatchFanin0( p, pObj, pFanin0 ); + continue; + } + if ( Aig_ObjIsLatch(pObj) ) + { + if ( !Aig_ObjIsBuf(Aig_ObjFanin0(pObj)) ) + continue; + pObjNew = Aig_ObjReal_rec( Aig_ObjChild0(pObj) ); + pObjNew = Aig_Latch( p, pObjNew, 0 ); + Aig_ObjReplace( p, pObj, pObjNew, 1, 0 ); + RetValue = 1; + Counter++; + continue; + } + if ( Aig_ObjIsNode(pObj) ) + { + if ( !Aig_ObjIsBuf(Aig_ObjFanin0(pObj)) && !Aig_ObjIsBuf(Aig_ObjFanin1(pObj)) ) + continue; + pFanin0 = Aig_ObjReal_rec( Aig_ObjChild0(pObj) ); + pFanin1 = Aig_ObjReal_rec( Aig_ObjChild1(pObj) ); + pObjNew = Aig_And( p, pFanin0, pFanin1 ); + Aig_ObjReplace( p, pObj, pObjNew, 1, 0 ); + RetValue = 1; + Counter++; + continue; + } + } +// printf( "Rehashings = %d.\n", Counter++ ); + return RetValue; +} + +/**Function************************************************************* + + Synopsis [If AIG contains buffers, this procedure removes them.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManRemoveBuffers( Aig_Man_t * p ) +{ + Aig_Obj_t * pObj, * pObjNew, * pFanin0, * pFanin1; + int i; + if ( Aig_ManBufNum(p) == 0 ) + return; + Aig_ManForEachObj( p, pObj, i ) + { + if ( Aig_ObjIsPo(pObj) ) + { + if ( !Aig_ObjIsBuf(Aig_ObjFanin0(pObj)) ) + continue; + pFanin0 = Aig_ObjReal_rec( Aig_ObjChild0(pObj) ); + Aig_ObjPatchFanin0( p, pObj, pFanin0 ); + } + else if ( Aig_ObjIsLatch(pObj) ) + { + if ( !Aig_ObjIsBuf(Aig_ObjFanin0(pObj)) ) + continue; + pFanin0 = Aig_ObjReal_rec( Aig_ObjChild0(pObj) ); + pObjNew = Aig_Latch( p, pFanin0, 0 ); + Aig_ObjReplace( p, pObj, pObjNew, 0, 0 ); + } + else if ( Aig_ObjIsAnd(pObj) ) + { + if ( !Aig_ObjIsBuf(Aig_ObjFanin0(pObj)) && !Aig_ObjIsBuf(Aig_ObjFanin1(pObj)) ) + continue; + pFanin0 = Aig_ObjReal_rec( Aig_ObjChild0(pObj) ); + pFanin1 = Aig_ObjReal_rec( Aig_ObjChild1(pObj) ); + pObjNew = Aig_And( p, pFanin0, pFanin1 ); + Aig_ObjReplace( p, pObj, pObjNew, 0, 0 ); + } + } + assert( Aig_ManBufNum(p) == 0 ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ManSeqStrash( Aig_Man_t * p, int nLatches, int * pInits ) +{ + Vec_Ptr_t * vNodes, * vUnreach; +// Aig_Obj_t * pObj, * pFanin; +// int i; + int Iter, RetValue = 1; + + // create latches out of the additional PI/PO pairs + Aig_ManSeqStrashConvert( p, nLatches, pInits ); + + // iteratively rehash the network + for ( Iter = 0; RetValue; Iter++ ) + { +// Aig_ManPrintStats( p ); +/* + Aig_ManForEachObj( p, pObj, i ) + { + assert( pObj->Type > 0 ); + pFanin = Aig_ObjFanin0(pObj); + assert( pFanin == NULL || pFanin->Type > 0 ); + pFanin = Aig_ObjFanin1(pObj); + assert( pFanin == NULL || pFanin->Type > 0 ); + } +*/ + // mark nodes unreachable from the PIs + vUnreach = Aig_ManDfsUnreach( p ); + if ( Iter == 0 && Vec_PtrSize(vUnreach) > 0 ) + printf( "Unreachable objects = %d.\n", Vec_PtrSize(vUnreach) ); + // collect nodes reachable from the POs + vNodes = Aig_ManDfsSeq( p ); + // remove nodes unreachable from the POs + if ( Iter == 0 ) + Aig_ManRemoveUnmarked( p ); + // continue rehashing as long as there are changes + RetValue = Aig_ManSeqRehashOne( p, vNodes, vUnreach ); + Vec_PtrFree( vNodes ); + Vec_PtrFree( vUnreach ); + } + + // perform the final cleanup + Aig_ManIncrementTravId( p ); + vNodes = Aig_ManDfsSeq( p ); + Aig_ManRemoveUnmarked( p ); + Vec_PtrFree( vNodes ); + // remove buffers if they are left +// Aig_ManRemoveBuffers( p ); + + // clean up + if ( !Aig_ManCheck( p ) ) + { + printf( "Aig_ManSeqStrash: The network check has failed.\n" ); + return 0; + } + return 1; + +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/aig/aigShow.c b/abc_with_bb_support/src/aig/aig/aigShow.c new file mode 100644 index 000000000..3f0cbb38a --- /dev/null +++ b/abc_with_bb_support/src/aig/aig/aigShow.c @@ -0,0 +1,356 @@ +/**CFile**************************************************************** + + FileName [ivyShow.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [Visualization of HAIG.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyShow.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "aig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Writes the graph structure of AIG for DOT.] + + Description [Useful for graph visualization using tools such as GraphViz: + http://www.graphviz.org/] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_WriteDotAig( Aig_Man_t * pMan, char * pFileName, int fHaig, Vec_Ptr_t * vBold ) +{ + FILE * pFile; + Aig_Obj_t * pNode;//, * pTemp, * pPrev; + int LevelMax, Level, i; + + if ( Aig_ManNodeNum(pMan) > 200 ) + { + fprintf( stdout, "Cannot visualize AIG with more than 200 nodes.\n" ); + return; + } + if ( (pFile = fopen( pFileName, "w" )) == NULL ) + { + fprintf( stdout, "Cannot open the intermediate file \"%s\".\n", pFileName ); + return; + } + + // mark the nodes + if ( vBold ) + Vec_PtrForEachEntry( vBold, pNode, i ) + pNode->fMarkB = 1; + + // compute levels +// LevelMax = 1 + Aig_ManSetLevels( pMan, fHaig ); + LevelMax = 1 + Aig_ManLevels( pMan ); + Aig_ManForEachPo( pMan, pNode, i ) + pNode->Level = LevelMax; + + // write the DOT header + fprintf( pFile, "# %s\n", "AIG structure generated by IVY package" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "digraph AIG {\n" ); + fprintf( pFile, "size = \"7.5,10\";\n" ); +// fprintf( pFile, "ranksep = 0.5;\n" ); +// fprintf( pFile, "nodesep = 0.5;\n" ); + fprintf( pFile, "center = true;\n" ); +// fprintf( pFile, "orientation = landscape;\n" ); +// fprintf( pFile, "edge [fontsize = 10];\n" ); +// fprintf( pFile, "edge [dir = none];\n" ); + fprintf( pFile, "edge [dir = back];\n" ); + fprintf( pFile, "\n" ); + + // labels on the left of the picture + fprintf( pFile, "{\n" ); + fprintf( pFile, " node [shape = plaintext];\n" ); + fprintf( pFile, " edge [style = invis];\n" ); + fprintf( pFile, " LevelTitle1 [label=\"\"];\n" ); + fprintf( pFile, " LevelTitle2 [label=\"\"];\n" ); + // generate node names with labels + for ( Level = LevelMax; Level >= 0; Level-- ) + { + // the visible node name + fprintf( pFile, " Level%d", Level ); + fprintf( pFile, " [label = " ); + // label name + fprintf( pFile, "\"" ); + fprintf( pFile, "\"" ); + fprintf( pFile, "];\n" ); + } + + // genetate the sequence of visible/invisible nodes to mark levels + fprintf( pFile, " LevelTitle1 -> LevelTitle2 ->" ); + for ( Level = LevelMax; Level >= 0; Level-- ) + { + // the visible node name + fprintf( pFile, " Level%d", Level ); + // the connector + if ( Level != 0 ) + fprintf( pFile, " ->" ); + else + fprintf( pFile, ";" ); + } + fprintf( pFile, "\n" ); + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + + // generate title box on top + fprintf( pFile, "{\n" ); + fprintf( pFile, " rank = same;\n" ); + fprintf( pFile, " LevelTitle1;\n" ); + fprintf( pFile, " title1 [shape=plaintext,\n" ); + fprintf( pFile, " fontsize=20,\n" ); + fprintf( pFile, " fontname = \"Times-Roman\",\n" ); + fprintf( pFile, " label=\"" ); + fprintf( pFile, "%s", "AIG structure visualized by ABC" ); + fprintf( pFile, "\\n" ); + fprintf( pFile, "Benchmark \\\"%s\\\". ", "aig" ); + fprintf( pFile, "Time was %s. ", Extra_TimeStamp() ); + fprintf( pFile, "\"\n" ); + fprintf( pFile, " ];\n" ); + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + + // generate statistics box + fprintf( pFile, "{\n" ); + fprintf( pFile, " rank = same;\n" ); + fprintf( pFile, " LevelTitle2;\n" ); + fprintf( pFile, " title2 [shape=plaintext,\n" ); + fprintf( pFile, " fontsize=18,\n" ); + fprintf( pFile, " fontname = \"Times-Roman\",\n" ); + fprintf( pFile, " label=\"" ); + fprintf( pFile, "The set contains %d logic nodes and spans %d levels.", Aig_ManNodeNum(pMan), LevelMax ); + fprintf( pFile, "\\n" ); + fprintf( pFile, "\"\n" ); + fprintf( pFile, " ];\n" ); + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + + // generate the COs + fprintf( pFile, "{\n" ); + fprintf( pFile, " rank = same;\n" ); + // the labeling node of this level + fprintf( pFile, " Level%d;\n", LevelMax ); + // generate the CO nodes + Aig_ManForEachPo( pMan, pNode, i ) + { +/* + if ( fHaig || pNode->pEquiv == NULL ) + fprintf( pFile, " Node%d%s [label = \"%d%s\"", pNode->Id, + (Aig_ObjIsLatch(pNode)? "_in":""), pNode->Id, (Aig_ObjIsLatch(pNode)? "_in":"") ); + else + fprintf( pFile, " Node%d%s [label = \"%d%s(%d%s)\"", pNode->Id, + (Aig_ObjIsLatch(pNode)? "_in":""), pNode->Id, (Aig_ObjIsLatch(pNode)? "_in":""), + Aig_Regular(pNode->pEquiv)->Id, Aig_IsComplement(pNode->pEquiv)? "\'":"" ); +*/ + fprintf( pFile, " Node%d [label = \"%d\"", pNode->Id, pNode->Id ); + + fprintf( pFile, ", shape = %s", (Aig_ObjIsLatch(pNode)? "box":"invtriangle") ); + fprintf( pFile, ", color = coral, fillcolor = coral" ); + fprintf( pFile, "];\n" ); + } + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + + // generate nodes of each rank + for ( Level = LevelMax - 1; Level > 0; Level-- ) + { + fprintf( pFile, "{\n" ); + fprintf( pFile, " rank = same;\n" ); + // the labeling node of this level + fprintf( pFile, " Level%d;\n", Level ); + Aig_ManForEachObj( pMan, pNode, i ) + { + if ( (int)pNode->Level != Level ) + continue; +/* + if ( fHaig || pNode->pEquiv == NULL ) + fprintf( pFile, " Node%d [label = \"%d\"", pNode->Id, pNode->Id ); + else + fprintf( pFile, " Node%d [label = \"%d(%d%s)\"", pNode->Id, pNode->Id, + Aig_Regular(pNode->pEquiv)->Id, Aig_IsComplement(pNode->pEquiv)? "\'":"" ); +*/ + fprintf( pFile, " Node%d [label = \"%d\"", pNode->Id, pNode->Id ); + + fprintf( pFile, ", shape = ellipse" ); + if ( vBold && pNode->fMarkB ) + fprintf( pFile, ", style = filled" ); + fprintf( pFile, "];\n" ); + } + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + } + + // generate the CI nodes + fprintf( pFile, "{\n" ); + fprintf( pFile, " rank = same;\n" ); + // the labeling node of this level + fprintf( pFile, " Level%d;\n", 0 ); + // generate constant node + if ( Aig_ObjRefs(Aig_ManConst1(pMan)) > 0 ) + { + pNode = Aig_ManConst1(pMan); + // check if the costant node is present + fprintf( pFile, " Node%d [label = \"Const1\"", pNode->Id ); + fprintf( pFile, ", shape = ellipse" ); + fprintf( pFile, ", color = coral, fillcolor = coral" ); + fprintf( pFile, "];\n" ); + } + // generate the CI nodes + Aig_ManForEachPi( pMan, pNode, i ) + { +/* + if ( fHaig || pNode->pEquiv == NULL ) + fprintf( pFile, " Node%d%s [label = \"%d%s\"", pNode->Id, + (Aig_ObjIsLatch(pNode)? "_out":""), pNode->Id, (Aig_ObjIsLatch(pNode)? "_out":"") ); + else + fprintf( pFile, " Node%d%s [label = \"%d%s(%d%s)\"", pNode->Id, + (Aig_ObjIsLatch(pNode)? "_out":""), pNode->Id, (Aig_ObjIsLatch(pNode)? "_out":""), + Aig_Regular(pNode->pEquiv)->Id, Aig_IsComplement(pNode->pEquiv)? "\'":"" ); +*/ + fprintf( pFile, " Node%d [label = \"%d\"", pNode->Id, pNode->Id ); + + fprintf( pFile, ", shape = %s", (Aig_ObjIsLatch(pNode)? "box":"triangle") ); + fprintf( pFile, ", color = coral, fillcolor = coral" ); + fprintf( pFile, "];\n" ); + } + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + + // generate invisible edges from the square down + fprintf( pFile, "title1 -> title2 [style = invis];\n" ); + Aig_ManForEachPo( pMan, pNode, i ) + fprintf( pFile, "title2 -> Node%d%s [style = invis];\n", pNode->Id, (Aig_ObjIsLatch(pNode)? "_in":"") ); + + // generate edges + Aig_ManForEachObj( pMan, pNode, i ) + { + if ( !Aig_ObjIsNode(pNode) && !Aig_ObjIsPo(pNode) && !Aig_ObjIsBuf(pNode) ) + continue; + // generate the edge from this node to the next + fprintf( pFile, "Node%d%s", pNode->Id, (Aig_ObjIsLatch(pNode)? "_in":"") ); + fprintf( pFile, " -> " ); + fprintf( pFile, "Node%d%s", Aig_ObjFaninId0(pNode), (Aig_ObjIsLatch(Aig_ObjFanin0(pNode))? "_out":"") ); + fprintf( pFile, " [" ); + fprintf( pFile, "style = %s", Aig_ObjFaninC0(pNode)? "dotted" : "bold" ); +// if ( Aig_NtkIsSeq(pNode->pMan) && Seq_ObjFaninL0(pNode) > 0 ) +// fprintf( pFile, ", label = \"%s\"", Seq_ObjFaninGetInitPrintable(pNode,0) ); + fprintf( pFile, "]" ); + fprintf( pFile, ";\n" ); + if ( !Aig_ObjIsNode(pNode) ) + continue; + // generate the edge from this node to the next + fprintf( pFile, "Node%d", pNode->Id ); + fprintf( pFile, " -> " ); + fprintf( pFile, "Node%d%s", Aig_ObjFaninId1(pNode), (Aig_ObjIsLatch(Aig_ObjFanin1(pNode))? "_out":"") ); + fprintf( pFile, " [" ); + fprintf( pFile, "style = %s", Aig_ObjFaninC1(pNode)? "dotted" : "bold" ); +// if ( Aig_NtkIsSeq(pNode->pMan) && Seq_ObjFaninL1(pNode) > 0 ) +// fprintf( pFile, ", label = \"%s\"", Seq_ObjFaninGetInitPrintable(pNode,1) ); + fprintf( pFile, "]" ); + fprintf( pFile, ";\n" ); +/* + // generate the edges between the equivalent nodes + if ( fHaig && pNode->pEquiv && Aig_ObjRefs(pNode) > 0 ) + { + pPrev = pNode; + for ( pTemp = pNode->pEquiv; pTemp != pNode; pTemp = Aig_Regular(pTemp->pEquiv) ) + { + fprintf( pFile, "Node%d", pPrev->Id ); + fprintf( pFile, " -> " ); + fprintf( pFile, "Node%d", pTemp->Id ); + fprintf( pFile, " [style = %s]", Aig_IsComplement(pTemp->pEquiv)? "dotted" : "bold" ); + fprintf( pFile, ";\n" ); + pPrev = pTemp; + } + // connect the last node with the first + fprintf( pFile, "Node%d", pPrev->Id ); + fprintf( pFile, " -> " ); + fprintf( pFile, "Node%d", pNode->Id ); + fprintf( pFile, " [style = %s]", Aig_IsComplement(pPrev->pEquiv)? "dotted" : "bold" ); + fprintf( pFile, ";\n" ); + } +*/ + } + + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + fclose( pFile ); + + // unmark nodes + if ( vBold ) + Vec_PtrForEachEntry( vBold, pNode, i ) + pNode->fMarkB = 0; + + Aig_ManForEachPo( pMan, pNode, i ) + pNode->Level = Aig_ObjFanin0(pNode)->Level; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManShow( Aig_Man_t * pMan, int fHaig, Vec_Ptr_t * vBold ) +{ + extern void Abc_ShowFile( char * FileNameDot ); + static Counter = 0; + char FileNameDot[200]; + FILE * pFile; + // create the file name +// Aig_ShowGetFileName( pMan->pName, FileNameDot ); + sprintf( FileNameDot, "temp%02d.dot", Counter++ ); + // check that the file can be opened + if ( (pFile = fopen( FileNameDot, "w" )) == NULL ) + { + fprintf( stdout, "Cannot open the intermediate file \"%s\".\n", FileNameDot ); + return; + } + fclose( pFile ); + // generate the file + Aig_WriteDotAig( pMan, FileNameDot, fHaig, vBold ); + // visualize the file + Abc_ShowFile( FileNameDot ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/aig/aigTable.c b/abc_with_bb_support/src/aig/aig/aigTable.c new file mode 100644 index 000000000..601209a8a --- /dev/null +++ b/abc_with_bb_support/src/aig/aig/aigTable.c @@ -0,0 +1,269 @@ +/**CFile**************************************************************** + + FileName [aigTable.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG package.] + + Synopsis [Structural hashing table.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: aigTable.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "aig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// hashing the node +static unsigned long Aig_Hash( Aig_Obj_t * pObj, int TableSize ) +{ + unsigned long Key = Aig_ObjIsExor(pObj) * 1699; + Key ^= Aig_ObjFanin0(pObj)->Id * 7937; + Key ^= Aig_ObjFanin1(pObj)->Id * 2971; + Key ^= Aig_ObjFaninC0(pObj) * 911; + Key ^= Aig_ObjFaninC1(pObj) * 353; + return Key % TableSize; +} + +// returns the place where this node is stored (or should be stored) +static Aig_Obj_t ** Aig_TableFind( Aig_Man_t * p, Aig_Obj_t * pObj ) +{ + Aig_Obj_t ** ppEntry; + if ( Aig_ObjIsLatch(pObj) ) + { + assert( Aig_ObjChild0(pObj) && Aig_ObjChild1(pObj) == NULL ); + } + else + { + assert( Aig_ObjChild0(pObj) && Aig_ObjChild1(pObj) ); + assert( Aig_ObjFanin0(pObj)->Id < Aig_ObjFanin1(pObj)->Id ); + } + for ( ppEntry = p->pTable + Aig_Hash(pObj, p->nTableSize); *ppEntry; ppEntry = &(*ppEntry)->pNext ) + if ( *ppEntry == pObj ) + return ppEntry; + assert( *ppEntry == NULL ); + return ppEntry; +} + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Resizes the table.] + + Description [Typically this procedure should not be called.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_TableResize( Aig_Man_t * p ) +{ + Aig_Obj_t * pEntry, * pNext; + Aig_Obj_t ** pTableOld, ** ppPlace; + int nTableSizeOld, Counter, nEntries, i, clk; +clk = clock(); + // save the old table + pTableOld = p->pTable; + nTableSizeOld = p->nTableSize; + // get the new table + p->nTableSize = Aig_PrimeCudd( 2 * Aig_ManNodeNum(p) ); + p->pTable = ALLOC( Aig_Obj_t *, p->nTableSize ); + memset( p->pTable, 0, sizeof(Aig_Obj_t *) * p->nTableSize ); + // rehash the entries from the old table + Counter = 0; + for ( i = 0; i < nTableSizeOld; i++ ) + for ( pEntry = pTableOld[i], pNext = pEntry? pEntry->pNext : NULL; + pEntry; pEntry = pNext, pNext = pEntry? pEntry->pNext : NULL ) + { + // get the place where this entry goes in the table + ppPlace = Aig_TableFind( p, pEntry ); + assert( *ppPlace == NULL ); // should not be there + // add the entry to the list + *ppPlace = pEntry; + pEntry->pNext = NULL; + Counter++; + } + nEntries = Aig_ManNodeNum(p); + assert( Counter == nEntries ); + printf( "Increasing the structural table size from %6d to %6d. ", nTableSizeOld, p->nTableSize ); + PRT( "Time", clock() - clk ); + // replace the table and the parameters + free( pTableOld ); +} + +/**Function************************************************************* + + Synopsis [Checks if node with the given attributes is in the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_TableLookup( Aig_Man_t * p, Aig_Obj_t * pGhost ) +{ + Aig_Obj_t * pEntry; + assert( !Aig_IsComplement(pGhost) ); + if ( pGhost->Type == AIG_OBJ_LATCH ) + { + assert( Aig_ObjChild0(pGhost) && Aig_ObjChild1(pGhost) == NULL ); + if ( !Aig_ObjRefs(Aig_ObjFanin0(pGhost)) ) + return NULL; + } + else + { + assert( pGhost->Type == AIG_OBJ_AND ); + assert( Aig_ObjChild0(pGhost) && Aig_ObjChild1(pGhost) ); + assert( Aig_ObjFanin0(pGhost)->Id < Aig_ObjFanin1(pGhost)->Id ); + if ( !Aig_ObjRefs(Aig_ObjFanin0(pGhost)) || !Aig_ObjRefs(Aig_ObjFanin1(pGhost)) ) + return NULL; + } + for ( pEntry = p->pTable[Aig_Hash(pGhost, p->nTableSize)]; pEntry; pEntry = pEntry->pNext ) + { + if ( Aig_ObjChild0(pEntry) == Aig_ObjChild0(pGhost) && + Aig_ObjChild1(pEntry) == Aig_ObjChild1(pGhost) && + Aig_ObjType(pEntry) == Aig_ObjType(pGhost) ) + return pEntry; + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [Checks if node with the given attributes is in the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_TableLookupTwo( Aig_Man_t * p, Aig_Obj_t * pFanin0, Aig_Obj_t * pFanin1 ) +{ + Aig_Obj_t * pGhost; + // consider simple cases + if ( pFanin0 == pFanin1 ) + return pFanin0; + if ( pFanin0 == Aig_Not(pFanin1) ) + return Aig_ManConst0(p); + if ( Aig_Regular(pFanin0) == Aig_ManConst1(p) ) + return pFanin0 == Aig_ManConst1(p) ? pFanin1 : Aig_ManConst0(p); + if ( Aig_Regular(pFanin1) == Aig_ManConst1(p) ) + return pFanin1 == Aig_ManConst1(p) ? pFanin0 : Aig_ManConst0(p); + pGhost = Aig_ObjCreateGhost( p, pFanin0, pFanin1, AIG_OBJ_AND ); + return Aig_TableLookup( p, pGhost ); +} + +/**Function************************************************************* + + Synopsis [Adds the new node to the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_TableInsert( Aig_Man_t * p, Aig_Obj_t * pObj ) +{ + Aig_Obj_t ** ppPlace; + assert( !Aig_IsComplement(pObj) ); + assert( Aig_TableLookup(p, pObj) == NULL ); + if ( (pObj->Id & 0xFF) == 0 && 2 * p->nTableSize < Aig_ManNodeNum(p) ) + Aig_TableResize( p ); + ppPlace = Aig_TableFind( p, pObj ); + assert( *ppPlace == NULL ); + *ppPlace = pObj; +} + +/**Function************************************************************* + + Synopsis [Deletes the node from the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_TableDelete( Aig_Man_t * p, Aig_Obj_t * pObj ) +{ + Aig_Obj_t ** ppPlace; + assert( !Aig_IsComplement(pObj) ); + ppPlace = Aig_TableFind( p, pObj ); + assert( *ppPlace == pObj ); // node should be in the table + // remove the node + *ppPlace = pObj->pNext; + pObj->pNext = NULL; +} + +/**Function************************************************************* + + Synopsis [Count the number of nodes in the table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_TableCountEntries( Aig_Man_t * p ) +{ + Aig_Obj_t * pEntry; + int i, Counter = 0; + for ( i = 0; i < p->nTableSize; i++ ) + for ( pEntry = p->pTable[i]; pEntry; pEntry = pEntry->pNext ) + Counter++; + return Counter; +} + +/**Function******************************************************************** + + Synopsis [Profiles the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void Aig_TableProfile( Aig_Man_t * p ) +{ + Aig_Obj_t * pEntry; + int i, Counter; + for ( i = 0; i < p->nTableSize; i++ ) + { + Counter = 0; + for ( pEntry = p->pTable[i]; pEntry; pEntry = pEntry->pNext ) + Counter++; + if ( Counter ) + printf( "%d ", Counter ); + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/aig/aigTiming.c b/abc_with_bb_support/src/aig/aig/aigTiming.c new file mode 100644 index 000000000..01f01ccff --- /dev/null +++ b/abc_with_bb_support/src/aig/aig/aigTiming.c @@ -0,0 +1,351 @@ +/**CFile**************************************************************** + + FileName [aigTiming.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG package.] + + Synopsis [Incremental updating of direct/reverse AIG levels.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: aigTiming.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "aig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Returns the reverse level of the node.] + + Description [The reverse level is the level of the node in reverse + topological order, starting from the COs.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Aig_ObjReverseLevel( Aig_Man_t * p, Aig_Obj_t * pObj ) +{ + assert( p->vLevelR ); + Vec_IntFillExtra( p->vLevelR, pObj->Id + 1, 0 ); + return Vec_IntEntry(p->vLevelR, pObj->Id); +} + +/**Function************************************************************* + + Synopsis [Sets the reverse level of the node.] + + Description [The reverse level is the level of the node in reverse + topological order, starting from the COs.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Aig_ObjSetReverseLevel( Aig_Man_t * p, Aig_Obj_t * pObj, int LevelR ) +{ + assert( p->vLevelR ); + Vec_IntFillExtra( p->vLevelR, pObj->Id + 1, 0 ); + Vec_IntWriteEntry( p->vLevelR, pObj->Id, LevelR ); +} + +/**Function************************************************************* + + Synopsis [Resets reverse level of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ObjClearReverseLevel( Aig_Man_t * p, Aig_Obj_t * pObj ) +{ + Aig_ObjSetReverseLevel( p, pObj, 0 ); +} + +/**Function************************************************************* + + Synopsis [Returns required level of the node.] + + Description [Converts the reverse levels of the node into its required + level as follows: ReqLevel(Node) = MaxLevels(Ntk) + 1 - LevelR(Node).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ObjRequiredLevel( Aig_Man_t * p, Aig_Obj_t * pObj ) +{ + assert( p->vLevelR ); + return p->nLevelMax + 1 - Aig_ObjReverseLevel(p, pObj); +} + +/**Function************************************************************* + + Synopsis [Computes the reverse level of the node using its fanout levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ObjReverseLevelNew( Aig_Man_t * p, Aig_Obj_t * pObj ) +{ + Aig_Obj_t * pFanout; + int i, iFanout, LevelCur, Level = 0; + Aig_ObjForEachFanout( p, pObj, pFanout, iFanout, i ) + { + LevelCur = Aig_ObjReverseLevel( p, pFanout ); + Level = AIG_MAX( Level, LevelCur ); + } + return Level + 1; +} + +/**Function************************************************************* + + Synopsis [Prepares for the computation of required levels.] + + Description [This procedure should be called before the required times + are used. It starts internal data structures, which records the level + from the COs of the network nodes in reverse topologogical order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManStartReverseLevels( Aig_Man_t * p, int nMaxLevelIncrease ) +{ + Vec_Ptr_t * vNodes; + Aig_Obj_t * pObj; + int i; + assert( p->pFanData != NULL ); + assert( p->vLevelR == NULL ); + // remember the maximum number of direct levels + p->nLevelMax = Aig_ManLevels(p) + nMaxLevelIncrease; + // start the reverse levels + p->vLevelR = Vec_IntAlloc( 0 ); + Vec_IntFill( p->vLevelR, 1 + Aig_ManObjIdMax(p), 0 ); + // compute levels in reverse topological order + vNodes = Aig_ManDfsReverse( p ); + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + assert( pObj->fMarkA == 0 ); + Aig_ObjSetReverseLevel( p, pObj, Aig_ObjReverseLevelNew(p, pObj) ); + } + Vec_PtrFree( vNodes ); +} + +/**Function************************************************************* + + Synopsis [Cleans the data structures used to compute required levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManStopReverseLevels( Aig_Man_t * p ) +{ + assert( p->vLevelR != NULL ); + Vec_IntFree( p->vLevelR ); + p->vLevelR = NULL; + p->nLevelMax = 0; + +} + +/**Function************************************************************* + + Synopsis [Incrementally updates level of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManUpdateLevel( Aig_Man_t * p, Aig_Obj_t * pObjNew ) +{ + Aig_Obj_t * pFanout, * pTemp; + int iFanout, LevelOld, Lev, k, m; + assert( p->pFanData != NULL ); + assert( Aig_ObjIsNode(pObjNew) ); + // allocate level if needed + if ( p->vLevels == NULL ) + p->vLevels = Vec_VecAlloc( Aig_ManLevels(p) + 8 ); + // check if level has changed + LevelOld = Aig_ObjLevel(pObjNew); + if ( LevelOld == Aig_ObjLevelNew(pObjNew) ) + return; + // start the data structure for level update + // we cannot fail to visit a node when using this structure because the + // nodes are stored by their _old_ levels, which are assumed to be correct + Vec_VecClear( p->vLevels ); + Vec_VecPush( p->vLevels, LevelOld, pObjNew ); + pObjNew->fMarkA = 1; + // recursively update level + Vec_VecForEachEntryStart( p->vLevels, pTemp, Lev, k, LevelOld ) + { + pTemp->fMarkA = 0; + assert( Aig_ObjLevel(pTemp) == Lev ); + pTemp->Level = Aig_ObjLevelNew(pTemp); + // if the level did not change, no need to check the fanout levels + if ( Aig_ObjLevel(pTemp) == Lev ) + continue; + // schedule fanout for level update + Aig_ObjForEachFanout( p, pTemp, pFanout, iFanout, m ) + { + if ( Aig_ObjIsNode(pFanout) && !pFanout->fMarkA ) + { + assert( Aig_ObjLevel(pFanout) >= Lev ); + Vec_VecPush( p->vLevels, Aig_ObjLevel(pFanout), pFanout ); + pFanout->fMarkA = 1; + } + } + } +} + +/**Function************************************************************* + + Synopsis [Incrementally updates level of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManUpdateReverseLevel( Aig_Man_t * p, Aig_Obj_t * pObjNew ) +{ + Aig_Obj_t * pFanin, * pTemp; + int LevelOld, LevFanin, Lev, k; + assert( p->vLevelR != NULL ); + assert( Aig_ObjIsNode(pObjNew) ); + // allocate level if needed + if ( p->vLevels == NULL ) + p->vLevels = Vec_VecAlloc( Aig_ManLevels(p) + 8 ); + // check if level has changed + LevelOld = Aig_ObjReverseLevel(p, pObjNew); + if ( LevelOld == Aig_ObjReverseLevelNew(p, pObjNew) ) + return; + // start the data structure for level update + // we cannot fail to visit a node when using this structure because the + // nodes are stored by their _old_ levels, which are assumed to be correct + Vec_VecClear( p->vLevels ); + Vec_VecPush( p->vLevels, LevelOld, pObjNew ); + pObjNew->fMarkA = 1; + // recursively update level + Vec_VecForEachEntryStart( p->vLevels, pTemp, Lev, k, LevelOld ) + { + pTemp->fMarkA = 0; + LevelOld = Aig_ObjReverseLevel(p, pTemp); + assert( LevelOld == Lev ); + Aig_ObjSetReverseLevel( p, pTemp, Aig_ObjReverseLevelNew(p, pTemp) ); + // if the level did not change, to need to check the fanout levels + if ( Aig_ObjReverseLevel(p, pTemp) == Lev ) + continue; + // schedule fanins for level update + pFanin = Aig_ObjFanin0(pTemp); + if ( Aig_ObjIsNode(pFanin) && !pFanin->fMarkA ) + { + LevFanin = Aig_ObjReverseLevel( p, pFanin ); + assert( LevFanin >= Lev ); + Vec_VecPush( p->vLevels, LevFanin, pFanin ); + pFanin->fMarkA = 1; + } + pFanin = Aig_ObjFanin1(pTemp); + if ( Aig_ObjIsNode(pFanin) && !pFanin->fMarkA ) + { + LevFanin = Aig_ObjReverseLevel( p, pFanin ); + assert( LevFanin >= Lev ); + Vec_VecPush( p->vLevels, LevFanin, pFanin ); + pFanin->fMarkA = 1; + } + } +} + +/**Function************************************************************* + + Synopsis [Verifies direct level of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManVerifyLevel( Aig_Man_t * p ) +{ + Aig_Obj_t * pObj; + int i, Counter = 0; + assert( p->pFanData ); + Aig_ManForEachNode( p, pObj, i ) + if ( Aig_ObjLevel(pObj) != Aig_ObjLevelNew(pObj) ) + { + printf( "Level of node %6d should be %4d instead of %4d.\n", + pObj->Id, Aig_ObjLevelNew(pObj), Aig_ObjLevel(pObj) ); + Counter++; + } + if ( Counter ) + printf( "Levels of %d nodes are incorrect.\n", Counter ); +} + +/**Function************************************************************* + + Synopsis [Verifies reverse level of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManVerifyReverseLevel( Aig_Man_t * p ) +{ + Aig_Obj_t * pObj; + int i, Counter = 0; + assert( p->vLevelR ); + Aig_ManForEachNode( p, pObj, i ) + if ( Aig_ObjLevel(pObj) != Aig_ObjLevelNew(pObj) ) + { + printf( "Reverse level of node %6d should be %4d instead of %4d.\n", + pObj->Id, Aig_ObjReverseLevelNew(p, pObj), Aig_ObjReverseLevel(p, pObj) ); + Counter++; + } + if ( Counter ) + printf( "Reverse levels of %d nodes are incorrect.\n", Counter ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/aig/aigTruth.c b/abc_with_bb_support/src/aig/aig/aigTruth.c new file mode 100644 index 000000000..74a030b79 --- /dev/null +++ b/abc_with_bb_support/src/aig/aig/aigTruth.c @@ -0,0 +1,98 @@ +/**CFile**************************************************************** + + FileName [aigTruth.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG package.] + + Synopsis [Computes truth table for the cut.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: aigTruth.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "aig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes truth table of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned * Aig_ManCutTruthOne( Aig_Obj_t * pNode, unsigned * pTruth, int nWords ) +{ + unsigned * pTruth0, * pTruth1; + int i; + pTruth0 = Aig_ObjFanin0(pNode)->pData; + pTruth1 = Aig_ObjFanin1(pNode)->pData; + if ( Aig_ObjIsExor(pNode) ) + for ( i = 0; i < nWords; i++ ) + pTruth[i] = pTruth0[i] ^ pTruth1[i]; + else if ( !Aig_ObjFaninC0(pNode) && !Aig_ObjFaninC1(pNode) ) + for ( i = 0; i < nWords; i++ ) + pTruth[i] = pTruth0[i] & pTruth1[i]; + else if ( !Aig_ObjFaninC0(pNode) && Aig_ObjFaninC1(pNode) ) + for ( i = 0; i < nWords; i++ ) + pTruth[i] = pTruth0[i] & ~pTruth1[i]; + else if ( Aig_ObjFaninC0(pNode) && !Aig_ObjFaninC1(pNode) ) + for ( i = 0; i < nWords; i++ ) + pTruth[i] = ~pTruth0[i] & pTruth1[i]; + else // if ( Aig_ObjFaninC0(pNode) && Aig_ObjFaninC1(pNode) ) + for ( i = 0; i < nWords; i++ ) + pTruth[i] = ~pTruth0[i] & ~pTruth1[i]; + return pTruth; +} + +/**Function************************************************************* + + Synopsis [Computes truth table of the cut.] + + Description [The returned pointer should be used immediately.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned * Aig_ManCutTruth( Aig_Obj_t * pRoot, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vNodes, Vec_Ptr_t * vTruthElem, Vec_Ptr_t * vTruthStore ) +{ + Aig_Obj_t * pObj; + int i, nWords; + assert( Vec_PtrSize(vLeaves) <= Vec_PtrSize(vTruthElem) ); + assert( Vec_PtrSize(vNodes) <= Vec_PtrSize(vTruthStore) ); + assert( Vec_PtrSize(vNodes) == 0 || pRoot == Vec_PtrEntryLast(vNodes) ); + // assign elementary truth tables + Vec_PtrForEachEntry( vLeaves, pObj, i ) + pObj->pData = Vec_PtrEntry( vTruthElem, i ); + // compute truths for other nodes + nWords = Aig_TruthWordNum( Vec_PtrSize(vLeaves) ); + Vec_PtrForEachEntry( vNodes, pObj, i ) + pObj->pData = Aig_ManCutTruthOne( pObj, Vec_PtrEntry(vTruthStore, i), nWords ); + return pRoot->pData; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/aig/aigUtil.c b/abc_with_bb_support/src/aig/aig/aigUtil.c new file mode 100644 index 000000000..3b2807ea4 --- /dev/null +++ b/abc_with_bb_support/src/aig/aig/aigUtil.c @@ -0,0 +1,732 @@ +/**CFile**************************************************************** + + FileName [aigUtil.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG package.] + + Synopsis [Various procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: aigUtil.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "aig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function******************************************************************** + + Synopsis [Returns the next prime >= p.] + + Description [Copied from CUDD, for stand-aloneness.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +unsigned int Aig_PrimeCudd( unsigned int p ) +{ + int i,pn; + + p--; + do { + p++; + if (p&1) { + pn = 1; + i = 3; + while ((unsigned) (i * i) <= p) { + if (p % i == 0) { + pn = 0; + break; + } + i += 2; + } + } else { + pn = 0; + } + } while (!pn); + return(p); + +} /* end of Cudd_Prime */ + +/**Function************************************************************* + + Synopsis [Increments the current traversal ID of the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManIncrementTravId( Aig_Man_t * p ) +{ + if ( p->nTravIds >= (1<<30)-1 ) + Aig_ManCleanData( p ); + p->nTravIds++; +} + +/**Function************************************************************* + + Synopsis [Collect the latches.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ManLevels( Aig_Man_t * p ) +{ + Aig_Obj_t * pObj; + int i, LevelMax = 0; + Aig_ManForEachPo( p, pObj, i ) + LevelMax = AIG_MAX( LevelMax, (int)Aig_ObjFanin0(pObj)->Level ); + return LevelMax; +} + +/**Function************************************************************* + + Synopsis [Reset reference counters.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManResetRefs( Aig_Man_t * p ) +{ + Aig_Obj_t * pObj; + int i; + Aig_ManForEachObj( p, pObj, i ) + pObj->nRefs = 0; + Aig_ManForEachObj( p, pObj, i ) + { + if ( Aig_ObjFanin0(pObj) ) + Aig_ObjFanin0(pObj)->nRefs++; + if ( Aig_ObjFanin1(pObj) ) + Aig_ObjFanin1(pObj)->nRefs++; + } +} + +/**Function************************************************************* + + Synopsis [Cleans MarkB.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManCleanMarkA( Aig_Man_t * p ) +{ + Aig_Obj_t * pObj; + int i; + Aig_ManForEachObj( p, pObj, i ) + pObj->fMarkA = 0; +} + +/**Function************************************************************* + + Synopsis [Cleans MarkB.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManCleanMarkB( Aig_Man_t * p ) +{ + Aig_Obj_t * pObj; + int i; + Aig_ManForEachObj( p, pObj, i ) + pObj->fMarkB = 0; +} + +/**Function************************************************************* + + Synopsis [Cleans the data pointers for the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManCleanData( Aig_Man_t * p ) +{ + Aig_Obj_t * pObj; + int i; + Aig_ManForEachObj( p, pObj, i ) + pObj->pData = NULL; +} + +/**Function************************************************************* + + Synopsis [Recursively cleans the data pointers in the cone of the node.] + + Description [Applicable to small AIGs only because no caching is performed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ObjCleanData_rec( Aig_Obj_t * pObj ) +{ + assert( !Aig_IsComplement(pObj) ); + assert( !Aig_ObjIsPo(pObj) ); + if ( Aig_ObjIsAnd(pObj) ) + { + Aig_ObjCleanData_rec( Aig_ObjFanin0(pObj) ); + Aig_ObjCleanData_rec( Aig_ObjFanin1(pObj) ); + } + pObj->pData = NULL; +} + + +/**Function************************************************************* + + Synopsis [Detects multi-input gate rooted at this node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ObjCollectMulti_rec( Aig_Obj_t * pRoot, Aig_Obj_t * pObj, Vec_Ptr_t * vSuper ) +{ + if ( pRoot != pObj && (Aig_IsComplement(pObj) || Aig_ObjIsPi(pObj) || Aig_ObjType(pRoot) != Aig_ObjType(pObj)) ) + { + Vec_PtrPushUnique(vSuper, pObj); + return; + } + Aig_ObjCollectMulti_rec( pRoot, Aig_ObjChild0(pObj), vSuper ); + Aig_ObjCollectMulti_rec( pRoot, Aig_ObjChild1(pObj), vSuper ); +} + +/**Function************************************************************* + + Synopsis [Detects multi-input gate rooted at this node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ObjCollectMulti( Aig_Obj_t * pRoot, Vec_Ptr_t * vSuper ) +{ + assert( !Aig_IsComplement(pRoot) ); + Vec_PtrClear( vSuper ); + Aig_ObjCollectMulti_rec( pRoot, pRoot, vSuper ); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the node is the root of MUX or EXOR/NEXOR.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ObjIsMuxType( Aig_Obj_t * pNode ) +{ + Aig_Obj_t * pNode0, * pNode1; + // check that the node is regular + assert( !Aig_IsComplement(pNode) ); + // if the node is not AND, this is not MUX + if ( !Aig_ObjIsAnd(pNode) ) + return 0; + // if the children are not complemented, this is not MUX + if ( !Aig_ObjFaninC0(pNode) || !Aig_ObjFaninC1(pNode) ) + return 0; + // get children + pNode0 = Aig_ObjFanin0(pNode); + pNode1 = Aig_ObjFanin1(pNode); + // if the children are not ANDs, this is not MUX + if ( !Aig_ObjIsAnd(pNode0) || !Aig_ObjIsAnd(pNode1) ) + return 0; + // otherwise the node is MUX iff it has a pair of equal grandchildren + return (Aig_ObjFanin0(pNode0) == Aig_ObjFanin0(pNode1) && (Aig_ObjFaninC0(pNode0) ^ Aig_ObjFaninC0(pNode1))) || + (Aig_ObjFanin0(pNode0) == Aig_ObjFanin1(pNode1) && (Aig_ObjFaninC0(pNode0) ^ Aig_ObjFaninC1(pNode1))) || + (Aig_ObjFanin1(pNode0) == Aig_ObjFanin0(pNode1) && (Aig_ObjFaninC1(pNode0) ^ Aig_ObjFaninC0(pNode1))) || + (Aig_ObjFanin1(pNode0) == Aig_ObjFanin1(pNode1) && (Aig_ObjFaninC1(pNode0) ^ Aig_ObjFaninC1(pNode1))); +} + + +/**Function************************************************************* + + Synopsis [Recognizes what nodes are inputs of the EXOR.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ObjRecognizeExor( Aig_Obj_t * pObj, Aig_Obj_t ** ppFan0, Aig_Obj_t ** ppFan1 ) +{ + Aig_Obj_t * p0, * p1; + assert( !Aig_IsComplement(pObj) ); + if ( !Aig_ObjIsNode(pObj) ) + return 0; + if ( Aig_ObjIsExor(pObj) ) + { + *ppFan0 = Aig_ObjChild0(pObj); + *ppFan1 = Aig_ObjChild1(pObj); + return 1; + } + assert( Aig_ObjIsAnd(pObj) ); + p0 = Aig_ObjChild0(pObj); + p1 = Aig_ObjChild1(pObj); + if ( !Aig_IsComplement(p0) || !Aig_IsComplement(p1) ) + return 0; + p0 = Aig_Regular(p0); + p1 = Aig_Regular(p1); + if ( !Aig_ObjIsAnd(p0) || !Aig_ObjIsAnd(p1) ) + return 0; + if ( Aig_ObjFanin0(p0) != Aig_ObjFanin0(p1) || Aig_ObjFanin1(p0) != Aig_ObjFanin1(p1) ) + return 0; + if ( Aig_ObjFaninC0(p0) == Aig_ObjFaninC0(p1) || Aig_ObjFaninC1(p0) == Aig_ObjFaninC1(p1) ) + return 0; + *ppFan0 = Aig_ObjChild0(p0); + *ppFan1 = Aig_ObjChild1(p0); + return 1; +} + +/**Function************************************************************* + + Synopsis [Recognizes what nodes are control and data inputs of a MUX.] + + Description [If the node is a MUX, returns the control variable C. + Assigns nodes T and E to be the then and else variables of the MUX. + Node C is never complemented. Nodes T and E can be complemented. + This function also recognizes EXOR/NEXOR gates as MUXes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_ObjRecognizeMux( Aig_Obj_t * pNode, Aig_Obj_t ** ppNodeT, Aig_Obj_t ** ppNodeE ) +{ + Aig_Obj_t * pNode0, * pNode1; + assert( !Aig_IsComplement(pNode) ); + assert( Aig_ObjIsMuxType(pNode) ); + // get children + pNode0 = Aig_ObjFanin0(pNode); + pNode1 = Aig_ObjFanin1(pNode); + + // find the control variable + if ( Aig_ObjFanin1(pNode0) == Aig_ObjFanin1(pNode1) && (Aig_ObjFaninC1(pNode0) ^ Aig_ObjFaninC1(pNode1)) ) + { +// if ( Fraig_IsComplement(pNode1->p2) ) + if ( Aig_ObjFaninC1(pNode0) ) + { // pNode2->p2 is positive phase of C + *ppNodeT = Aig_Not(Aig_ObjChild0(pNode1));//pNode2->p1); + *ppNodeE = Aig_Not(Aig_ObjChild0(pNode0));//pNode1->p1); + return Aig_ObjChild1(pNode1);//pNode2->p2; + } + else + { // pNode1->p2 is positive phase of C + *ppNodeT = Aig_Not(Aig_ObjChild0(pNode0));//pNode1->p1); + *ppNodeE = Aig_Not(Aig_ObjChild0(pNode1));//pNode2->p1); + return Aig_ObjChild1(pNode0);//pNode1->p2; + } + } + else if ( Aig_ObjFanin0(pNode0) == Aig_ObjFanin0(pNode1) && (Aig_ObjFaninC0(pNode0) ^ Aig_ObjFaninC0(pNode1)) ) + { +// if ( Fraig_IsComplement(pNode1->p1) ) + if ( Aig_ObjFaninC0(pNode0) ) + { // pNode2->p1 is positive phase of C + *ppNodeT = Aig_Not(Aig_ObjChild1(pNode1));//pNode2->p2); + *ppNodeE = Aig_Not(Aig_ObjChild1(pNode0));//pNode1->p2); + return Aig_ObjChild0(pNode1);//pNode2->p1; + } + else + { // pNode1->p1 is positive phase of C + *ppNodeT = Aig_Not(Aig_ObjChild1(pNode0));//pNode1->p2); + *ppNodeE = Aig_Not(Aig_ObjChild1(pNode1));//pNode2->p2); + return Aig_ObjChild0(pNode0);//pNode1->p1; + } + } + else if ( Aig_ObjFanin0(pNode0) == Aig_ObjFanin1(pNode1) && (Aig_ObjFaninC0(pNode0) ^ Aig_ObjFaninC1(pNode1)) ) + { +// if ( Fraig_IsComplement(pNode1->p1) ) + if ( Aig_ObjFaninC0(pNode0) ) + { // pNode2->p2 is positive phase of C + *ppNodeT = Aig_Not(Aig_ObjChild0(pNode1));//pNode2->p1); + *ppNodeE = Aig_Not(Aig_ObjChild1(pNode0));//pNode1->p2); + return Aig_ObjChild1(pNode1);//pNode2->p2; + } + else + { // pNode1->p1 is positive phase of C + *ppNodeT = Aig_Not(Aig_ObjChild1(pNode0));//pNode1->p2); + *ppNodeE = Aig_Not(Aig_ObjChild0(pNode1));//pNode2->p1); + return Aig_ObjChild0(pNode0);//pNode1->p1; + } + } + else if ( Aig_ObjFanin1(pNode0) == Aig_ObjFanin0(pNode1) && (Aig_ObjFaninC1(pNode0) ^ Aig_ObjFaninC0(pNode1)) ) + { +// if ( Fraig_IsComplement(pNode1->p2) ) + if ( Aig_ObjFaninC1(pNode0) ) + { // pNode2->p1 is positive phase of C + *ppNodeT = Aig_Not(Aig_ObjChild1(pNode1));//pNode2->p2); + *ppNodeE = Aig_Not(Aig_ObjChild0(pNode0));//pNode1->p1); + return Aig_ObjChild0(pNode1);//pNode2->p1; + } + else + { // pNode1->p2 is positive phase of C + *ppNodeT = Aig_Not(Aig_ObjChild0(pNode0));//pNode1->p1); + *ppNodeE = Aig_Not(Aig_ObjChild1(pNode1));//pNode2->p2); + return Aig_ObjChild1(pNode0);//pNode1->p2; + } + } + assert( 0 ); // this is not MUX + return NULL; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Aig_ObjReal_rec( Aig_Obj_t * pObj ) +{ + Aig_Obj_t * pObjNew, * pObjR = Aig_Regular(pObj); + if ( !Aig_ObjIsBuf(pObjR) ) + return pObj; + pObjNew = Aig_ObjReal_rec( Aig_ObjChild0(pObjR) ); + return Aig_NotCond( pObjNew, Aig_IsComplement(pObj) ); +} + + +/**Function************************************************************* + + Synopsis [Prints Eqn formula for the AIG rooted at this node.] + + Description [The formula is in terms of PIs, which should have + their names assigned in pObj->pData fields.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ObjPrintEqn( FILE * pFile, Aig_Obj_t * pObj, Vec_Vec_t * vLevels, int Level ) +{ + Vec_Ptr_t * vSuper; + Aig_Obj_t * pFanin; + int fCompl, i; + // store the complemented attribute + fCompl = Aig_IsComplement(pObj); + pObj = Aig_Regular(pObj); + // constant case + if ( Aig_ObjIsConst1(pObj) ) + { + fprintf( pFile, "%d", !fCompl ); + return; + } + // PI case + if ( Aig_ObjIsPi(pObj) ) + { + fprintf( pFile, "%s%s", fCompl? "!" : "", pObj->pData ); + return; + } + // AND case + Vec_VecExpand( vLevels, Level ); + vSuper = Vec_VecEntry(vLevels, Level); + Aig_ObjCollectMulti( pObj, vSuper ); + fprintf( pFile, "%s", (Level==0? "" : "(") ); + Vec_PtrForEachEntry( vSuper, pFanin, i ) + { + Aig_ObjPrintEqn( pFile, Aig_NotCond(pFanin, fCompl), vLevels, Level+1 ); + if ( i < Vec_PtrSize(vSuper) - 1 ) + fprintf( pFile, " %s ", fCompl? "+" : "*" ); + } + fprintf( pFile, "%s", (Level==0? "" : ")") ); + return; +} + +/**Function************************************************************* + + Synopsis [Prints Verilog formula for the AIG rooted at this node.] + + Description [The formula is in terms of PIs, which should have + their names assigned in pObj->pData fields.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ObjPrintVerilog( FILE * pFile, Aig_Obj_t * pObj, Vec_Vec_t * vLevels, int Level ) +{ + Vec_Ptr_t * vSuper; + Aig_Obj_t * pFanin, * pFanin0, * pFanin1, * pFaninC; + int fCompl, i; + // store the complemented attribute + fCompl = Aig_IsComplement(pObj); + pObj = Aig_Regular(pObj); + // constant case + if ( Aig_ObjIsConst1(pObj) ) + { + fprintf( pFile, "1\'b%d", !fCompl ); + return; + } + // PI case + if ( Aig_ObjIsPi(pObj) ) + { + fprintf( pFile, "%s%s", fCompl? "~" : "", pObj->pData ); + return; + } + // EXOR case + if ( Aig_ObjIsExor(pObj) ) + { + Vec_VecExpand( vLevels, Level ); + vSuper = Vec_VecEntry( vLevels, Level ); + Aig_ObjCollectMulti( pObj, vSuper ); + fprintf( pFile, "%s", (Level==0? "" : "(") ); + Vec_PtrForEachEntry( vSuper, pFanin, i ) + { + Aig_ObjPrintVerilog( pFile, Aig_NotCond(pFanin, (fCompl && i==0)), vLevels, Level+1 ); + if ( i < Vec_PtrSize(vSuper) - 1 ) + fprintf( pFile, " ^ " ); + } + fprintf( pFile, "%s", (Level==0? "" : ")") ); + return; + } + // MUX case + if ( Aig_ObjIsMuxType(pObj) ) + { + if ( Aig_ObjRecognizeExor( pObj, &pFanin0, &pFanin1 ) ) + { + fprintf( pFile, "%s", (Level==0? "" : "(") ); + Aig_ObjPrintVerilog( pFile, Aig_NotCond(pFanin0, fCompl), vLevels, Level+1 ); + fprintf( pFile, " ^ " ); + Aig_ObjPrintVerilog( pFile, pFanin1, vLevels, Level+1 ); + fprintf( pFile, "%s", (Level==0? "" : ")") ); + } + else + { + pFaninC = Aig_ObjRecognizeMux( pObj, &pFanin1, &pFanin0 ); + fprintf( pFile, "%s", (Level==0? "" : "(") ); + Aig_ObjPrintVerilog( pFile, pFaninC, vLevels, Level+1 ); + fprintf( pFile, " ? " ); + Aig_ObjPrintVerilog( pFile, Aig_NotCond(pFanin1, fCompl), vLevels, Level+1 ); + fprintf( pFile, " : " ); + Aig_ObjPrintVerilog( pFile, Aig_NotCond(pFanin0, fCompl), vLevels, Level+1 ); + fprintf( pFile, "%s", (Level==0? "" : ")") ); + } + return; + } + // AND case + Vec_VecExpand( vLevels, Level ); + vSuper = Vec_VecEntry(vLevels, Level); + Aig_ObjCollectMulti( pObj, vSuper ); + fprintf( pFile, "%s", (Level==0? "" : "(") ); + Vec_PtrForEachEntry( vSuper, pFanin, i ) + { + Aig_ObjPrintVerilog( pFile, Aig_NotCond(pFanin, fCompl), vLevels, Level+1 ); + if ( i < Vec_PtrSize(vSuper) - 1 ) + fprintf( pFile, " %s ", fCompl? "|" : "&" ); + } + fprintf( pFile, "%s", (Level==0? "" : ")") ); + return; +} + + +/**Function************************************************************* + + Synopsis [Prints node in HAIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ObjPrintVerbose( Aig_Obj_t * pObj, int fHaig ) +{ + assert( !Aig_IsComplement(pObj) ); + printf( "Node %p : ", pObj ); + if ( Aig_ObjIsConst1(pObj) ) + printf( "constant 1" ); + else if ( Aig_ObjIsPi(pObj) ) + printf( "PI" ); + else + printf( "AND( %p%s, %p%s )", + Aig_ObjFanin0(pObj), (Aig_ObjFaninC0(pObj)? "\'" : " "), + Aig_ObjFanin1(pObj), (Aig_ObjFaninC1(pObj)? "\'" : " ") ); + printf( " (refs = %3d)", Aig_ObjRefs(pObj) ); +} + +/**Function************************************************************* + + Synopsis [Prints node in HAIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManPrintVerbose( Aig_Man_t * p, int fHaig ) +{ + Vec_Ptr_t * vNodes; + Aig_Obj_t * pObj; + int i; + printf( "PIs: " ); + Aig_ManForEachPi( p, pObj, i ) + printf( " %p", pObj ); + printf( "\n" ); + vNodes = Aig_ManDfs( p ); + Vec_PtrForEachEntry( vNodes, pObj, i ) + Aig_ObjPrintVerbose( pObj, fHaig ), printf( "\n" ); + printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [Write speculative miter for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManDump( Aig_Man_t * p ) +{ + static int Counter = 0; + char FileName[20]; + // dump the logic into a file + sprintf( FileName, "aigbug\\%03d.blif", ++Counter ); + Aig_ManDumpBlif( p, FileName ); + printf( "Intermediate AIG with %d nodes was written into file \"%s\".\n", Aig_ManNodeNum(p), FileName ); +} + +/**Function************************************************************* + + Synopsis [Writes the AIG into the BLIF file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManDumpBlif( Aig_Man_t * p, char * pFileName ) +{ + FILE * pFile; + Vec_Ptr_t * vNodes; + Aig_Obj_t * pObj, * pConst1 = NULL; + int i, nDigits, Counter = 0; + if ( Aig_ManPoNum(p) == 0 ) + { + printf( "Aig_ManDumpBlif(): AIG manager does not have POs.\n" ); + return; + } + // collect nodes in the DFS order + vNodes = Aig_ManDfs( p ); + // assign IDs to objects + Aig_ManConst1(p)->pData = (void *)Counter++; + Aig_ManForEachPi( p, pObj, i ) + pObj->pData = (void *)Counter++; + Aig_ManForEachPo( p, pObj, i ) + pObj->pData = (void *)Counter++; + Vec_PtrForEachEntry( vNodes, pObj, i ) + pObj->pData = (void *)Counter++; + nDigits = Extra_Base10Log( Counter ); + // write the file + pFile = fopen( pFileName, "w" ); + fprintf( pFile, "# BLIF file written by procedure Aig_ManDumpBlif() in ABC\n" ); + fprintf( pFile, "# http://www.eecs.berkeley.edu/~alanmi/abc/\n" ); + fprintf( pFile, ".model test\n" ); + // write PIs + fprintf( pFile, ".inputs" ); + Aig_ManForEachPi( p, pObj, i ) + fprintf( pFile, " n%0*d", nDigits, (int)pObj->pData ); + fprintf( pFile, "\n" ); + // write POs + fprintf( pFile, ".outputs" ); + Aig_ManForEachPo( p, pObj, i ) + fprintf( pFile, " n%0*d", nDigits, (int)pObj->pData ); + fprintf( pFile, "\n" ); + // write nodes + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + fprintf( pFile, ".names n%0*d n%0*d n%0*d\n", + nDigits, (int)Aig_ObjFanin0(pObj)->pData, + nDigits, (int)Aig_ObjFanin1(pObj)->pData, + nDigits, (int)pObj->pData ); + fprintf( pFile, "%d%d 1\n", !Aig_ObjFaninC0(pObj), !Aig_ObjFaninC1(pObj) ); + } + // write POs + Aig_ManForEachPo( p, pObj, i ) + { + if ( (int)pObj->pData == 1359 ) + { + int x = 0; + } + fprintf( pFile, ".names n%0*d n%0*d\n", + nDigits, (int)Aig_ObjFanin0(pObj)->pData, + nDigits, (int)pObj->pData ); + fprintf( pFile, "%d 1\n", !Aig_ObjFaninC0(pObj) ); + if ( Aig_ObjIsConst1(Aig_ObjFanin0(pObj)) ) + pConst1 = Aig_ManConst1(p); + } + if ( pConst1 ) + fprintf( pFile, ".names n%0*d\n 1\n", nDigits, (int)pConst1->pData ); + fprintf( pFile, ".end\n\n" ); + fclose( pFile ); + Vec_PtrFree( vNodes ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/aig/aigWin.c b/abc_with_bb_support/src/aig/aig/aigWin.c new file mode 100644 index 000000000..844f05ff0 --- /dev/null +++ b/abc_with_bb_support/src/aig/aig/aigWin.c @@ -0,0 +1,184 @@ +/**CFile**************************************************************** + + FileName [aigWin.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG package.] + + Synopsis [Window computation.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: aigWin.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "aig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Evaluate the cost of removing the node from the set of leaves.] + + Description [Returns the number of new leaves that will be brought in. + Returns large number if the node cannot be removed from the set of leaves.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Aig_NodeGetLeafCostOne( Aig_Obj_t * pNode, int nFanoutLimit ) +{ + int Cost; + // make sure the node is in the construction zone + assert( pNode->fMarkA ); + // cannot expand over the PI node + if ( Aig_ObjIsPi(pNode) ) + return 999; + // get the cost of the cone + Cost = (!Aig_ObjFanin0(pNode)->fMarkA) + (!Aig_ObjFanin1(pNode)->fMarkA); + // always accept if the number of leaves does not increase + if ( Cost < 2 ) + return Cost; + // skip nodes with many fanouts + if ( (int)pNode->nRefs > nFanoutLimit ) + return 999; + // return the number of nodes that will be on the leaves if this node is removed + return Cost; +} + +/**Function************************************************************* + + Synopsis [Builds reconvergence-driven cut by changing one leaf at a time.] + + Description [This procedure looks at the current leaves and tries to change + one leaf at a time in such a way that the cut grows as little as possible. + In evaluating the fanins, this procedure looks only at their immediate + predecessors (this is why it is called a one-level construction procedure).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ManFindCut_int( Vec_Ptr_t * vFront, Vec_Ptr_t * vVisited, int nSizeLimit, int nFanoutLimit ) +{ + Aig_Obj_t * pNode, * pFaninBest, * pNext; + int CostBest, CostCur, i; + // find the best fanin + CostBest = 100; + pFaninBest = NULL; +//printf( "Evaluating fanins of the cut:\n" ); + Vec_PtrForEachEntry( vFront, pNode, i ) + { + CostCur = Aig_NodeGetLeafCostOne( pNode, nFanoutLimit ); +//printf( " Fanin %s has cost %d.\n", Aig_ObjName(pNode), CostCur ); + if ( CostBest > CostCur || + (CostBest == CostCur && pNode->Level > pFaninBest->Level) ) + { + CostBest = CostCur; + pFaninBest = pNode; + } + if ( CostBest == 0 ) + break; + } + if ( pFaninBest == NULL ) + return 0; + assert( CostBest < 3 ); + if ( Vec_PtrSize(vFront) - 1 + CostBest > nSizeLimit ) + return 0; + assert( Aig_ObjIsNode(pFaninBest) ); + // remove the node from the array + Vec_PtrRemove( vFront, pFaninBest ); +//printf( "Removing fanin %s.\n", Aig_ObjName(pFaninBest) ); + + // add the left child to the fanins + pNext = Aig_ObjFanin0(pFaninBest); + if ( !pNext->fMarkA ) + { +//printf( "Adding fanin %s.\n", Aig_ObjName(pNext) ); + pNext->fMarkA = 1; + Vec_PtrPush( vFront, pNext ); + Vec_PtrPush( vVisited, pNext ); + } + // add the right child to the fanins + pNext = Aig_ObjFanin1(pFaninBest); + if ( !pNext->fMarkA ) + { +//printf( "Adding fanin %s.\n", Aig_ObjName(pNext) ); + pNext->fMarkA = 1; + Vec_PtrPush( vFront, pNext ); + Vec_PtrPush( vVisited, pNext ); + } + assert( Vec_PtrSize(vFront) <= nSizeLimit ); + // keep doing this + return 1; +} + +/**Function************************************************************* + + Synopsis [Computes one sequential cut of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManFindCut( Aig_Obj_t * pRoot, Vec_Ptr_t * vFront, Vec_Ptr_t * vVisited, int nSizeLimit, int nFanoutLimit ) +{ + Aig_Obj_t * pNode; + int i; + + assert( !Aig_IsComplement(pRoot) ); + assert( Aig_ObjIsNode(pRoot) ); + assert( Aig_ObjChild0(pRoot) ); + assert( Aig_ObjChild1(pRoot) ); + + // start the cut + Vec_PtrClear( vFront ); + Vec_PtrPush( vFront, Aig_ObjFanin0(pRoot) ); + Vec_PtrPush( vFront, Aig_ObjFanin1(pRoot) ); + + // start the visited nodes + Vec_PtrClear( vVisited ); + Vec_PtrPush( vVisited, pRoot ); + Vec_PtrPush( vVisited, Aig_ObjFanin0(pRoot) ); + Vec_PtrPush( vVisited, Aig_ObjFanin1(pRoot) ); + + // mark these nodes + assert( !pRoot->fMarkA ); + assert( !Aig_ObjFanin0(pRoot)->fMarkA ); + assert( !Aig_ObjFanin1(pRoot)->fMarkA ); + pRoot->fMarkA = 1; + Aig_ObjFanin0(pRoot)->fMarkA = 1; + Aig_ObjFanin1(pRoot)->fMarkA = 1; + + // compute the cut + while ( Aig_ManFindCut_int( vFront, vVisited, nSizeLimit, nFanoutLimit ) ); + assert( Vec_PtrSize(vFront) <= nSizeLimit ); + + // clean the visit markings + Vec_PtrForEachEntry( vVisited, pNode, i ) + pNode->fMarkA = 0; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/aig/aig_.c b/abc_with_bb_support/src/aig/aig/aig_.c new file mode 100644 index 000000000..fbd835f5b --- /dev/null +++ b/abc_with_bb_support/src/aig/aig/aig_.c @@ -0,0 +1,48 @@ +/**CFile**************************************************************** + + FileName [aig_.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: aig_.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "aig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/aig/module.make b/abc_with_bb_support/src/aig/aig/module.make new file mode 100644 index 000000000..be5afafa9 --- /dev/null +++ b/abc_with_bb_support/src/aig/aig/module.make @@ -0,0 +1,17 @@ +SRC += src/aig/aig/aigCheck.c \ + src/aig/aig/aigDfs.c \ + src/aig/aig/aigFanout.c \ + src/aig/aig/aigMan.c \ + src/aig/aig/aigMem.c \ + src/aig/aig/aigMffc.c \ + src/aig/aig/aigObj.c \ + src/aig/aig/aigOper.c \ + src/aig/aig/aigOrder.c \ + src/aig/aig/aigPart.c \ + src/aig/aig/aigRepr.c \ + src/aig/aig/aigSeq.c \ + src/aig/aig/aigTable.c \ + src/aig/aig/aigTiming.c \ + src/aig/aig/aigTruth.c \ + src/aig/aig/aigUtil.c \ + src/aig/aig/aigWin.c \ No newline at end of file diff --git a/abc_with_bb_support/src/aig/bdc/bdc.h b/abc_with_bb_support/src/aig/bdc/bdc.h new file mode 100644 index 000000000..c19c0b002 --- /dev/null +++ b/abc_with_bb_support/src/aig/bdc/bdc.h @@ -0,0 +1,73 @@ +/**CFile**************************************************************** + + FileName [bdc.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Truth-table-based bi-decomposition engine.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 30, 2007.] + + Revision [$Id: bdc.h,v 1.00 2007/01/30 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __BDC_H__ +#define __BDC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Bdc_Man_t_ Bdc_Man_t; +typedef struct Bdc_Par_t_ Bdc_Par_t; +struct Bdc_Par_t_ +{ + // general parameters + int nVarsMax; // the maximum support + int fVerbose; // enable basic stats + int fVeryVerbose; // enable detailed stats +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== bdcCore.c ==========================================================*/ +extern Bdc_Man_t * Bdc_ManAlloc( Bdc_Par_t * pPars ); +extern void Bdc_ManFree( Bdc_Man_t * p ); +extern int Bdc_ManDecompose( Bdc_Man_t * p, unsigned * puFunc, unsigned * puCare, int nVars, Vec_Ptr_t * vDivs, int nNodesLimit ); + + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/aig/bdc/bdcCore.c b/abc_with_bb_support/src/aig/bdc/bdcCore.c new file mode 100644 index 000000000..24fefd5f4 --- /dev/null +++ b/abc_with_bb_support/src/aig/bdc/bdcCore.c @@ -0,0 +1,189 @@ +/**CFile**************************************************************** + + FileName [bdcCore.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Truth-table-based bi-decomposition engine.] + + Synopsis [The gateway to bi-decomposition.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 30, 2007.] + + Revision [$Id: bdcCore.c,v 1.00 2007/01/30 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "bdcInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocate resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Bdc_Man_t * Bdc_ManAlloc( Bdc_Par_t * pPars ) +{ + Bdc_Man_t * p; + unsigned * pData; + int i, k, nBits; + p = ALLOC( Bdc_Man_t, 1 ); + memset( p, 0, sizeof(Bdc_Man_t) ); + assert( pPars->nVarsMax > 3 && pPars->nVarsMax < 16 ); + p->pPars = pPars; + p->nWords = Kit_TruthWordNum( pPars->nVarsMax ); + p->nDivsLimit = 200; + p->nNodesLimit = 0; // will be set later + // memory + p->vMemory = Vec_IntStart( 1 << 16 ); + // internal nodes + p->nNodesAlloc = 512; + p->pNodes = ALLOC( Bdc_Fun_t, p->nNodesAlloc ); + // set up hash table + p->nTableSize = (1 << p->pPars->nVarsMax); + p->pTable = ALLOC( Bdc_Fun_t *, p->nTableSize ); + memset( p->pTable, 0, sizeof(Bdc_Fun_t *) * p->nTableSize ); + p->vSpots = Vec_IntAlloc( 256 ); + // truth tables + p->vTruths = Vec_PtrAllocSimInfo( pPars->nVarsMax + 5, p->nWords ); + // set elementary truth tables + nBits = (1 << pPars->nVarsMax); + Kit_TruthFill( Vec_PtrEntry(p->vTruths, 0), p->nVars ); + for ( k = 0; k < pPars->nVarsMax; k++ ) + { + pData = Vec_PtrEntry( p->vTruths, k+1 ); + Kit_TruthClear( pData, p->nVars ); + for ( i = 0; i < nBits; i++ ) + if ( i & (1 << k) ) + pData[i>>5] |= (1 << (i&31)); + } + p->puTemp1 = Vec_PtrEntry( p->vTruths, pPars->nVarsMax + 1 ); + p->puTemp2 = Vec_PtrEntry( p->vTruths, pPars->nVarsMax + 2 ); + p->puTemp3 = Vec_PtrEntry( p->vTruths, pPars->nVarsMax + 3 ); + p->puTemp4 = Vec_PtrEntry( p->vTruths, pPars->nVarsMax + 4 ); + // start the internal ISFs + p->pIsfOL = &p->IsfOL; Bdc_IsfStart( p, p->pIsfOL ); + p->pIsfOR = &p->IsfOR; Bdc_IsfStart( p, p->pIsfOR ); + p->pIsfAL = &p->IsfAL; Bdc_IsfStart( p, p->pIsfAL ); + p->pIsfAR = &p->IsfAR; Bdc_IsfStart( p, p->pIsfAR ); + return p; +} + +/**Function************************************************************* + + Synopsis [Deallocate resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Bdc_ManFree( Bdc_Man_t * p ) +{ + Vec_IntFree( p->vMemory ); + Vec_IntFree( p->vSpots ); + Vec_PtrFree( p->vTruths ); + free( p->pNodes ); + free( p->pTable ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Clears the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Bdc_ManPrepare( Bdc_Man_t * p, Vec_Ptr_t * vDivs ) +{ + unsigned * puTruth; + Bdc_Fun_t * pNode; + int i; + Bdc_TableClear( p ); + Vec_IntClear( p->vMemory ); + // add constant 1 and elementary vars + p->nNodes = p->nNodesNew = 0; + for ( i = 0; i <= p->pPars->nVarsMax; i++ ) + { + pNode = Bdc_FunNew( p ); + pNode->Type = BDC_TYPE_PI; + pNode->puFunc = Vec_PtrEntry( p->vTruths, i ); + pNode->uSupp = i? (1 << (i-1)) : 0; + Bdc_TableAdd( p, pNode ); + } + // add the divisors + Vec_PtrForEachEntry( vDivs, puTruth, i ) + { + pNode = Bdc_FunNew( p ); + pNode->Type = BDC_TYPE_PI; + pNode->puFunc = puTruth; + pNode->uSupp = Kit_TruthSupport( puTruth, p->nVars ); + Bdc_TableAdd( p, pNode ); + if ( i == p->nDivsLimit ) + break; + } +} + +/**Function************************************************************* + + Synopsis [Performs decomposition of one function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Bdc_ManDecompose( Bdc_Man_t * p, unsigned * puFunc, unsigned * puCare, int nVars, Vec_Ptr_t * vDivs, int nNodesMax ) +{ + Bdc_Isf_t Isf, * pIsf = &Isf; + // set current manager parameters + p->nVars = nVars; + p->nWords = Kit_TruthWordNum( nVars ); + Bdc_ManPrepare( p, vDivs ); + p->nNodesLimit = (p->nNodes + nNodesMax < p->nNodesAlloc)? p->nNodes + nNodesMax : p->nNodesAlloc; + // copy the function + Bdc_IsfStart( p, pIsf ); + Bdc_IsfClean( pIsf ); + pIsf->uSupp = Kit_TruthSupport( puFunc, p->nVars ) | Kit_TruthSupport( puCare, p->nVars ); + Kit_TruthAnd( pIsf->puOn, puCare, puFunc, p->nVars ); + Kit_TruthSharp( pIsf->puOff, puCare, puFunc, p->nVars ); + // call decomposition + Bdc_SuppMinimize( p, pIsf ); + p->pRoot = Bdc_ManDecompose_rec( p, pIsf ); + if ( p->pRoot == NULL ) + return -1; + return p->nNodesNew; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/bdc/bdcDec.c b/abc_with_bb_support/src/aig/bdc/bdcDec.c new file mode 100644 index 000000000..643bf3f7b --- /dev/null +++ b/abc_with_bb_support/src/aig/bdc/bdcDec.c @@ -0,0 +1,461 @@ +/**CFile**************************************************************** + + FileName [bdcDec.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Truth-table-based bi-decomposition engine.] + + Synopsis [Decomposition procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 30, 2007.] + + Revision [$Id: bdcDec.c,v 1.00 2007/01/30 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "bdcInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Bdc_Type_t Bdc_DecomposeStep( Bdc_Man_t * p, Bdc_Isf_t * pIsf, Bdc_Isf_t * pIsfL, Bdc_Isf_t * pIsfR ); +static int Bdc_DecomposeUpdateRight( Bdc_Man_t * p, Bdc_Isf_t * pIsf, Bdc_Isf_t * pIsfL, Bdc_Isf_t * pIsfR, unsigned * puTruth, Bdc_Type_t Type ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs one step of bi-decomposition.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Bdc_Fun_t * Bdc_ManDecompose_rec( Bdc_Man_t * p, Bdc_Isf_t * pIsf ) +{ + Bdc_Fun_t * pFunc; + Bdc_Isf_t IsfL, * pIsfL = &IsfL; + Bdc_Isf_t IsfB, * pIsfR = &IsfB; + // check computed results + if ( pFunc = Bdc_TableLookup( p, pIsf ) ) + return pFunc; + // decide on the decomposition type + pFunc = Bdc_FunNew( p ); + if ( pFunc == NULL ) + return NULL; + pFunc->Type = Bdc_DecomposeStep( p, pIsf, pIsfL, pIsfR ); + // decompose the left branch + pFunc->pFan0 = Bdc_ManDecompose_rec( p, pIsfL ); + if ( pFunc->pFan0 == NULL ) + return NULL; + // decompose the right branch + if ( Bdc_DecomposeUpdateRight( p, pIsf, pIsfL, pIsfR, pFunc->pFan0->puFunc, pFunc->Type ) ) + { + p->nNodes--; + return pFunc->pFan0; + } + pFunc->pFan1 = Bdc_ManDecompose_rec( p, pIsfL ); + if ( pFunc->pFan1 == NULL ) + return NULL; + // compute the function of node + pFunc->puFunc = (unsigned *)Vec_IntFetch(p->vMemory, p->nWords); + if ( pFunc->Type == BDC_TYPE_AND ) + Kit_TruthAnd( pFunc->puFunc, pFunc->pFan0->puFunc, pFunc->pFan1->puFunc, p->nVars ); + else if ( pFunc->Type == BDC_TYPE_OR ) + Kit_TruthOr( pFunc->puFunc, pFunc->pFan0->puFunc, pFunc->pFan1->puFunc, p->nVars ); + else + assert( 0 ); + // verify correctness + assert( Bdc_TableCheckContainment(p, pIsf, pFunc->puFunc) ); + // convert from OR to AND + if ( pFunc->Type == BDC_TYPE_OR ) + { + pFunc->Type = BDC_TYPE_AND; + pFunc->pFan0 = Bdc_Not(pFunc->pFan0); + pFunc->pFan1 = Bdc_Not(pFunc->pFan1); + Kit_TruthNot( pFunc->puFunc, pFunc->puFunc, p->nVars ); + pFunc = Bdc_Not(pFunc); + } + Bdc_TableAdd( p, Bdc_Regular(pFunc) ); + return pFunc; +} + +/**Function************************************************************* + + Synopsis [Updates the ISF of the right after the left was decompoosed.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Bdc_DecomposeUpdateRight( Bdc_Man_t * p, Bdc_Isf_t * pIsf, Bdc_Isf_t * pIsfL, Bdc_Isf_t * pIsfR, unsigned * puTruth, Bdc_Type_t Type ) +{ + if ( Type == BDC_TYPE_OR ) + { +// Right.Q = bdd_appex( Q, CompSpecLeftF, bddop_diff, setRightRes ); +// Right.R = bdd_exist( R, setRightRes ); + +// if ( pR->Q ) Cudd_RecursiveDeref( dd, pR->Q ); +// if ( pR->R ) Cudd_RecursiveDeref( dd, pR->R ); +// pR->Q = Cudd_bddAndAbstract( dd, pF->Q, Cudd_Not(CompSpecF), pL->V ); Cudd_Ref( pR->Q ); +// pR->R = Cudd_bddExistAbstract( dd, pF->R, pL->V ); Cudd_Ref( pR->R ); + +// assert( pR->R != b0 ); +// return (int)( pR->Q == b0 ); + + Kit_TruthSharp( pIsfR->puOn, pIsf->puOn, puTruth, p->nVars ); + Kit_TruthExistSet( pIsfR->puOn, pIsfR->puOn, p->nVars, pIsfL->uSupp ); + Kit_TruthExistSet( pIsfR->puOff, pIsf->puOff, p->nVars, pIsfL->uSupp ); + assert( !Kit_TruthIsConst0(pIsfR->puOff, p->nVars) ); + return Kit_TruthIsConst0(pIsfR->puOn, p->nVars); + } + else if ( Type == BDC_TYPE_AND ) + { +// Right.R = bdd_appex( R, CompSpecLeftF, bddop_and, setRightRes ); +// Right.Q = bdd_exist( Q, setRightRes ); + +// if ( pR->Q ) Cudd_RecursiveDeref( dd, pR->Q ); +// if ( pR->R ) Cudd_RecursiveDeref( dd, pR->R ); +// pR->R = Cudd_bddAndAbstract( dd, pF->R, CompSpecF, pL->V ); Cudd_Ref( pR->R ); +// pR->Q = Cudd_bddExistAbstract( dd, pF->Q, pL->V ); Cudd_Ref( pR->Q ); + +// assert( pR->Q != b0 ); +// return (int)( pR->R == b0 ); + + Kit_TruthSharp( pIsfR->puOn, pIsf->puOn, puTruth, p->nVars ); + Kit_TruthExistSet( pIsfR->puOn, pIsfR->puOn, p->nVars, pIsfL->uSupp ); + Kit_TruthExistSet( pIsfR->puOff, pIsf->puOff, p->nVars, pIsfL->uSupp ); + assert( !Kit_TruthIsConst0(pIsfR->puOff, p->nVars) ); + return Kit_TruthIsConst0(pIsfR->puOn, p->nVars); + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Checks existence of OR-bidecomposition.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Bdc_DecomposeGetCost( Bdc_Man_t * p, int nLeftVars, int nRightVars ) +{ + assert( nLeftVars > 0 ); + assert( nRightVars > 0 ); + // compute the decomposition coefficient + if ( nLeftVars >= nRightVars ) + return BDC_SCALE * (p->nVars * nRightVars + nLeftVars); + else // if ( nLeftVars < nRightVars ) + return BDC_SCALE * (p->nVars * nLeftVars + nRightVars); +} + +/**Function************************************************************* + + Synopsis [Checks existence of weak OR-bidecomposition.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Bdc_DecomposeFindInitialVarSet( Bdc_Man_t * p, Bdc_Isf_t * pIsf, Bdc_Isf_t * pIsfL, Bdc_Isf_t * pIsfR ) +{ + char pVars[16]; + int v, nVars, Beg, End; + + assert( pIsfL->uSupp == 0 ); + assert( pIsfR->uSupp == 0 ); + + // fill in the variables + nVars = 0; + for ( v = 0; v < p->nVars; v++ ) + if ( pIsf->uSupp & (1 << v) ) + pVars[nVars++] = v; + + // try variable pairs + for ( Beg = 0; Beg < nVars; Beg++ ) + { + Kit_TruthExistNew( p->puTemp1, pIsf->puOff, p->nVars, pVars[Beg] ); + for ( End = nVars - 1; End > Beg; End-- ) + { + Kit_TruthExistNew( p->puTemp2, pIsf->puOff, p->nVars, pVars[End] ); + if ( Kit_TruthIsDisjoint3(pIsf->puOn, p->puTemp1, p->puTemp2, p->nVars) ) + { + pIsfL->uSupp = (1 << Beg); + pIsfR->uSupp = (1 << End); + pIsfL->Var = Beg; + pIsfR->Var = End; + return 1; + } + } + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Checks existence of weak OR-bidecomposition.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Bdc_DecomposeWeakOr( Bdc_Man_t * p, Bdc_Isf_t * pIsf, Bdc_Isf_t * pIsfL, Bdc_Isf_t * pIsfR ) +{ + int v, VarCost, VarBest, Cost, VarCostBest = 0; + + for ( v = 0; v < p->nVars; v++ ) + { + Kit_TruthExistNew( p->puTemp1, pIsf->puOff, p->nVars, v ); +// if ( (Q & !bdd_exist( R, VarSetXa )) != bddfalse ) +// Exist = Cudd_bddExistAbstract( dd, pF->R, Var ); Cudd_Ref( Exist ); +// if ( Cudd_bddIteConstant( dd, pF->Q, Cudd_Not(Exist), b0 ) != b0 ) + if ( !Kit_TruthIsImply( pIsf->puOn, p->puTemp1, p->nVars ) ) + { + // measure the cost of this variable +// VarCost = bdd_satcountset( bdd_forall( Q, VarSetXa ), VarCube ); + +// Univ = Cudd_bddUnivAbstract( dd, pF->Q, Var ); Cudd_Ref( Univ ); +// VarCost = Kit_TruthCountOnes( Univ, p->nVars ); +// Cudd_RecursiveDeref( dd, Univ ); + + Kit_TruthForallNew( p->puTemp2, pIsf->puOn, p->nVars, v ); + VarCost = Kit_TruthCountOnes( p->puTemp2, p->nVars ); + if ( VarCost == 0 ) + VarCost = 1; + if ( VarCostBest < VarCost ) + { + VarCostBest = VarCost; + VarBest = v; + } + } + } + + // derive the components for weak-bi-decomposition if the variable is found + if ( VarCostBest ) + { +// funQLeftRes = Q & bdd_exist( R, setRightORweak ); + +// Temp = Cudd_bddExistAbstract( dd, pF->R, VarBest ); Cudd_Ref( Temp ); +// pL->Q = Cudd_bddAnd( dd, pF->Q, Temp ); Cudd_Ref( pL->Q ); +// Cudd_RecursiveDeref( dd, Temp ); + + Kit_TruthExistNew( p->puTemp1, pIsf->puOff, p->nVars, VarBest ); + Kit_TruthAnd( pIsfL->puOn, pIsf->puOn, p->puTemp1, p->nVars ); + +// pL->R = pF->R; Cudd_Ref( pL->R ); +// pL->V = VarBest; Cudd_Ref( pL->V ); + Kit_TruthCopy( pIsfL->puOff, pIsf->puOff, p->nVars ); + pIsfL->Var = VarBest; + +// assert( pL->Q != b0 ); +// assert( pL->R != b0 ); +// assert( Cudd_bddIteConstant( dd, pL->Q, pL->R, b0 ) == b0 ); + + // express cost in percents of the covered boolean space + Cost = VarCostBest * BDC_SCALE / (1<nVars); + if ( Cost == 0 ) + Cost = 1; + return Cost; + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Checks existence of OR-bidecomposition.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Bdc_DecomposeOr( Bdc_Man_t * p, Bdc_Isf_t * pIsf, Bdc_Isf_t * pIsfL, Bdc_Isf_t * pIsfR ) +{ + unsigned uSuppRem; + int v, nLeftVars = 1, nRightVars = 1; + // clean the var sets + Bdc_IsfClean( pIsfL ); + Bdc_IsfClean( pIsfR ); + // find initial variable sets + if ( !Bdc_DecomposeFindInitialVarSet( p, pIsf, pIsfL, pIsfR ) ) + return Bdc_DecomposeWeakOr( p, pIsf, pIsfL, pIsfR ); + // prequantify the variables in the offset + Kit_TruthExistNew( p->puTemp1, pIsf->puOff, p->nVars, pIsfL->Var ); + Kit_TruthExistNew( p->puTemp2, pIsf->puOff, p->nVars, pIsfR->Var ); + // go through the remaining variables + uSuppRem = pIsf->uSupp & ~pIsfL->uSupp & ~pIsfR->uSupp; + assert( Kit_WordCountOnes(uSuppRem) > 0 ); + for ( v = 0; v < p->nVars; v++ ) + { + if ( (uSuppRem & (1 << v)) == 0 ) + continue; + // prequantify this variable + Kit_TruthExistNew( p->puTemp3, p->puTemp1, p->nVars, v ); + Kit_TruthExistNew( p->puTemp4, p->puTemp2, p->nVars, v ); + if ( nLeftVars < nRightVars ) + { +// if ( (Q & bdd_exist( pF->R, pL->V & VarNew ) & bdd_exist( pF->R, pR->V )) == bddfalse ) +// if ( VerifyORCondition( dd, pF->Q, pF->R, pL->V, pR->V, VarNew ) ) + if ( Kit_TruthIsDisjoint3(pIsf->puOn, p->puTemp3, p->puTemp2, p->nVars) ) + { +// pL->V &= VarNew; + pIsfL->uSupp |= (1 << v); + nLeftVars++; + } +// else if ( (Q & bdd_exist( pF->R, pR->V & VarNew ) & bdd_exist( pF->R, pL->V )) == bddfalse ) + else if ( Kit_TruthIsDisjoint3(pIsf->puOn, p->puTemp4, p->puTemp1, p->nVars) ) + { +// pR->V &= VarNew; + pIsfR->uSupp |= (1 << v); + nRightVars++; + } + } + else + { +// if ( (Q & bdd_exist( pF->R, pR->V & VarNew ) & bdd_exist( pF->R, pL->V )) == bddfalse ) + if ( Kit_TruthIsDisjoint3(pIsf->puOn, p->puTemp4, p->puTemp1, p->nVars) ) + { +// pR->V &= VarNew; + pIsfR->uSupp |= (1 << v); + nRightVars++; + } +// else if ( (Q & bdd_exist( pF->R, pL->V & VarNew ) & bdd_exist( pF->R, pR->V )) == bddfalse ) + else if ( Kit_TruthIsDisjoint3(pIsf->puOn, p->puTemp3, p->puTemp2, p->nVars) ) + { +// pL->V &= VarNew; + pIsfL->uSupp |= (1 << v); + nLeftVars++; + } + } + } + + // derive the functions Q and R for the left branch +// pL->Q = bdd_appex( pF->Q, bdd_exist( pF->R, pL->V ), bddop_and, pR->V ); +// pL->R = bdd_exist( pF->R, pR->V ); + +// Temp = Cudd_bddExistAbstract( dd, pF->R, pL->V ); Cudd_Ref( Temp ); +// pL->Q = Cudd_bddAndAbstract( dd, pF->Q, Temp, pR->V ); Cudd_Ref( pL->Q ); +// Cudd_RecursiveDeref( dd, Temp ); +// pL->R = Cudd_bddExistAbstract( dd, pF->R, pR->V ); Cudd_Ref( pL->R ); + + Kit_TruthAnd( pIsfL->puOn, pIsf->puOn, p->puTemp1, p->nVars ); + Kit_TruthExistSet( pIsfL->puOn, pIsfL->puOn, p->nVars, pIsfR->uSupp ); + Kit_TruthCopy( pIsfL->puOff, p->puTemp2, p->nVars ); + + // derive the functions Q and R for the right branch +// Temp = Cudd_bddExistAbstract( dd, pF->R, pR->V ); Cudd_Ref( Temp ); +// pR->Q = Cudd_bddAndAbstract( dd, pF->Q, Temp, pL->V ); Cudd_Ref( pR->Q ); +// Cudd_RecursiveDeref( dd, Temp ); +// pR->R = Cudd_bddExistAbstract( dd, pF->R, pL->V ); Cudd_Ref( pR->R ); + +/* + Kit_TruthAnd( pIsfR->puOn, pIsf->puOn, p->puTemp2, p->nVars ); + Kit_TruthExistSet( pIsfR->puOn, pIsfR->puOn, p->nVars, pIsfL->uSupp ); + Kit_TruthCopy( pIsfR->puOff, p->puTemp1, p->nVars ); +*/ + +// assert( pL->Q != b0 ); +// assert( pL->R != b0 ); +// assert( Cudd_bddIteConstant( dd, pL->Q, pL->R, b0 ) == b0 ); + assert( !Kit_TruthIsConst0(pIsfL->puOn, p->nVars) ); + assert( !Kit_TruthIsConst0(pIsfL->puOff, p->nVars) ); + assert( Kit_TruthIsDisjoint(pIsfL->puOn, pIsfL->puOff, p->nVars) ); + + return Bdc_DecomposeGetCost( p, nLeftVars, nRightVars ); +} + +/**Function************************************************************* + + Synopsis [Performs one step of bi-decomposition.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Bdc_Type_t Bdc_DecomposeStep( Bdc_Man_t * p, Bdc_Isf_t * pIsf, Bdc_Isf_t * pIsfL, Bdc_Isf_t * pIsfR ) +{ + int CostOr, CostAnd, CostOrL, CostOrR, CostAndL, CostAndR; + + Bdc_IsfClean( p->pIsfOL ); + Bdc_IsfClean( p->pIsfOR ); + Bdc_IsfClean( p->pIsfAL ); + Bdc_IsfClean( p->pIsfAR ); + + // perform OR decomposition + CostOr = Bdc_DecomposeOr( p, pIsf, p->pIsfOL, p->pIsfOR ); + + // perform AND decomposition + Bdc_IsfNot( pIsf ); + CostAnd = Bdc_DecomposeOr( p, pIsf, p->pIsfAL, p->pIsfAR ); + Bdc_IsfNot( pIsf ); + Bdc_IsfNot( p->pIsfAL ); + Bdc_IsfNot( p->pIsfAR ); + + // check the hash table + Bdc_SuppMinimize( p, p->pIsfOL ); + CostOrL = (Bdc_TableLookup(p, p->pIsfOL) != NULL); + Bdc_SuppMinimize( p, p->pIsfOR ); + CostOrR = (Bdc_TableLookup(p, p->pIsfOR) != NULL); + Bdc_SuppMinimize( p, p->pIsfAL ); + CostAndL = (Bdc_TableLookup(p, p->pIsfAL) != NULL); + Bdc_SuppMinimize( p, p->pIsfAR ); + CostAndR = (Bdc_TableLookup(p, p->pIsfAR) != NULL); + + // check if there is any reuse for the components + if ( CostOrL + CostOrR < CostAndL + CostAndR ) + { + Bdc_IsfCopy( pIsfL, p->pIsfOL ); + Bdc_IsfCopy( pIsfR, p->pIsfOR ); + return BDC_TYPE_OR; + } + if ( CostOrL + CostOrR > CostAndL + CostAndR ) + { + Bdc_IsfCopy( pIsfL, p->pIsfAL ); + Bdc_IsfCopy( pIsfR, p->pIsfAR ); + return BDC_TYPE_AND; + } + + // compare the two-component costs + if ( CostOr < CostAnd ) + { + Bdc_IsfCopy( pIsfL, p->pIsfOL ); + Bdc_IsfCopy( pIsfR, p->pIsfOR ); + return BDC_TYPE_OR; + } + return BDC_TYPE_AND; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/bdc/bdcInt.h b/abc_with_bb_support/src/aig/bdc/bdcInt.h new file mode 100644 index 000000000..3c7ea86a1 --- /dev/null +++ b/abc_with_bb_support/src/aig/bdc/bdcInt.h @@ -0,0 +1,150 @@ +/**CFile**************************************************************** + + FileName [bdcInt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Truth-table-based bi-decomposition engine.] + + Synopsis [Internal declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 15, 2007.] + + Revision [$Id: resInt.h,v 1.00 2007/01/15 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __BDC_INT_H__ +#define __BDC_INT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include "kit.h" +#include "bdc.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +#define BDC_SCALE 100 // value used to compute the cost + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +// network types +typedef enum { + BDC_TYPE_NONE = 0, // 0: unknown + BDC_TYPE_CONST1, // 1: constant 1 + BDC_TYPE_PI, // 2: primary input + BDC_TYPE_AND, // 4: AND-gate + BDC_TYPE_OR, // 5: OR-gate (temporary) + BDC_TYPE_XOR, // 6: XOR-gate + BDC_TYPE_MUX, // 7: MUX-gate + BDC_TYPE_OTHER // 8: unused +} Bdc_Type_t; + +typedef struct Bdc_Fun_t_ Bdc_Fun_t; +struct Bdc_Fun_t_ +{ + int Type; // Const1, PI, AND, XOR, MUX + Bdc_Fun_t * pFan0; // fanin of the given node + Bdc_Fun_t * pFan1; // fanin of the given node + Bdc_Fun_t * pFan2; // fanin of the given node + unsigned uSupp; // bit mask of current support + unsigned * puFunc; // the function of the node + Bdc_Fun_t * pNext; // next function with same support + void * pCopy; // the copy field +}; + +typedef struct Bdc_Isf_t_ Bdc_Isf_t; +struct Bdc_Isf_t_ +{ + int Var; // the first variable assigned + unsigned uSupp; // the current support + unsigned * puOn; // on-set + unsigned * puOff; // off-set +}; + +struct Bdc_Man_t_ +{ + // external parameters + Bdc_Par_t * pPars; // parameter set + int nVars; // the number of variables + int nWords; // the number of words + int nNodesLimit; // the limit on the number of new nodes + int nDivsLimit; // the limit on the number of divisors + // internal nodes + Bdc_Fun_t * pNodes; // storage for decomposition nodes + int nNodes; // the number of nodes used + int nNodesNew; // the number of nodes used + int nNodesAlloc; // the number of nodes allocated + Bdc_Fun_t * pRoot; // the root node + // resub candidates + Bdc_Fun_t ** pTable; // hash table of candidates + int nTableSize; // hash table size (1 << nVarsMax) + Vec_Int_t * vSpots; // the occupied spots in the table + // elementary truth tables + Vec_Ptr_t * vTruths; // for const 1 and elementary variables + unsigned * puTemp1; // temporary truth table + unsigned * puTemp2; // temporary truth table + unsigned * puTemp3; // temporary truth table + unsigned * puTemp4; // temporary truth table + // temporary ISFs + Bdc_Isf_t * pIsfOL, IsfOL; + Bdc_Isf_t * pIsfOR, IsfOR; + Bdc_Isf_t * pIsfAL, IsfAL; + Bdc_Isf_t * pIsfAR, IsfAR; + // internal memory manager + Vec_Int_t * vMemory; // memory for internal truth tables +}; + +// working with complemented attributes of objects +static inline int Bdc_IsComplement( Bdc_Fun_t * p ) { return (int)((unsigned long)p & (unsigned long)01); } +static inline Bdc_Fun_t * Bdc_Regular( Bdc_Fun_t * p ) { return (Bdc_Fun_t *)((unsigned long)p & ~(unsigned long)01); } +static inline Bdc_Fun_t * Bdc_Not( Bdc_Fun_t * p ) { return (Bdc_Fun_t *)((unsigned long)p ^ (unsigned long)01); } +static inline Bdc_Fun_t * Bdc_NotCond( Bdc_Fun_t * p, int c ) { return (Bdc_Fun_t *)((unsigned long)p ^ (unsigned long)(c!=0)); } + +static inline Bdc_Fun_t * Bdc_FunNew( Bdc_Man_t * p ) { Bdc_Fun_t * pRes; if ( p->nNodes == p->nNodesLimit ) return NULL; pRes = p->pNodes + p->nNodes++; memset( pRes, 0, sizeof(Bdc_Fun_t) ); p->nNodesNew++; return pRes; } +static inline void Bdc_IsfStart( Bdc_Man_t * p, Bdc_Isf_t * pF ) { pF->puOn = Vec_IntFetch( p->vMemory, p->nWords ); pF->puOff = Vec_IntFetch( p->vMemory, p->nWords ); } +static inline void Bdc_IsfClean( Bdc_Isf_t * p ) { p->uSupp = 0; p->Var = 0; } +static inline void Bdc_IsfCopy( Bdc_Isf_t * p, Bdc_Isf_t * q ) { Bdc_Isf_t T = *p; *p = *q; *q = T; } +static inline void Bdc_IsfNot( Bdc_Isf_t * p ) { unsigned * puT = p->puOn; p->puOn = p->puOff; p->puOff = puT; } + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== bdcDec.c ==========================================================*/ +extern Bdc_Fun_t * Bdc_ManDecompose_rec( Bdc_Man_t * p, Bdc_Isf_t * pIsf ); +/*=== bdcTable.c ==========================================================*/ +extern Bdc_Fun_t * Bdc_TableLookup( Bdc_Man_t * p, Bdc_Isf_t * pIsf ); +extern void Bdc_TableAdd( Bdc_Man_t * p, Bdc_Fun_t * pFunc ); +extern void Bdc_TableClear( Bdc_Man_t * p ); +extern void Bdc_SuppMinimize( Bdc_Man_t * p, Bdc_Isf_t * pIsf ); +extern int Bdc_TableCheckContainment( Bdc_Man_t * p, Bdc_Isf_t * pIsf, unsigned * puTruth ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/aig/bdc/bdcTable.c b/abc_with_bb_support/src/aig/bdc/bdcTable.c new file mode 100644 index 000000000..40abbcccd --- /dev/null +++ b/abc_with_bb_support/src/aig/bdc/bdcTable.c @@ -0,0 +1,140 @@ +/**CFile**************************************************************** + + FileName [bdcTable.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Truth-table-based bi-decomposition engine.] + + Synopsis [Hash table for intermediate nodes.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 30, 2007.] + + Revision [$Id: bdcTable.c,v 1.00 2007/01/30 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "bdcInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Minimizes the support of the ISF.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Bdc_SuppMinimize( Bdc_Man_t * p, Bdc_Isf_t * pIsf ) +{ + int v; + // go through the support variables + for ( v = 0; v < p->nVars; v++ ) + { + if ( (pIsf->uSupp & (1 << v)) == 0 ) + continue; + Kit_TruthExistNew( p->puTemp1, pIsf->puOn, p->nVars, v ); + Kit_TruthExistNew( p->puTemp2, pIsf->puOff, p->nVars, v ); + if ( !Kit_TruthIsDisjoint( p->puTemp1, p->puTemp2, p->nVars ) ) + continue; + // remove the variable + Kit_TruthCopy( pIsf->puOn, p->puTemp1, p->nVars ); + Kit_TruthCopy( pIsf->puOff, p->puTemp2, p->nVars ); + pIsf->uSupp &= ~(1 << v); + } +} + +/**Function************************************************************* + + Synopsis [Checks containment of the function in the ISF.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Bdc_TableCheckContainment( Bdc_Man_t * p, Bdc_Isf_t * pIsf, unsigned * puTruth ) +{ + return Kit_TruthIsImply( pIsf->puOn, puTruth, p->nVars ) && + Kit_TruthIsDisjoint( pIsf->puOff, puTruth, p->nVars ); +} + +/**Function************************************************************* + + Synopsis [Adds the new entry to the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Bdc_Fun_t * Bdc_TableLookup( Bdc_Man_t * p, Bdc_Isf_t * pIsf ) +{ + Bdc_Fun_t * pFunc; + for ( pFunc = p->pTable[pIsf->uSupp]; pFunc; pFunc = pFunc->pNext ) + if ( Bdc_TableCheckContainment( p, pIsf, pFunc->puFunc ) ) + return pFunc; + return NULL; +} + +/**Function************************************************************* + + Synopsis [Adds the new entry to the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Bdc_TableAdd( Bdc_Man_t * p, Bdc_Fun_t * pFunc ) +{ + if ( p->pTable[pFunc->uSupp] == NULL ) + Vec_IntPush( p->vSpots, pFunc->uSupp ); + pFunc->pNext = p->pTable[pFunc->uSupp]; + p->pTable[pFunc->uSupp] = pFunc; +} + +/**Function************************************************************* + + Synopsis [Adds the new entry to the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Bdc_TableClear( Bdc_Man_t * p ) +{ + int Spot, i; + Vec_IntForEachEntry( p->vSpots, Spot, i ) + p->pTable[Spot] = NULL; + Vec_IntClear( p->vSpots ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/bdc/bdc_.c b/abc_with_bb_support/src/aig/bdc/bdc_.c new file mode 100644 index 000000000..8951e5525 --- /dev/null +++ b/abc_with_bb_support/src/aig/bdc/bdc_.c @@ -0,0 +1,49 @@ +/**CFile**************************************************************** + + FileName [bdc_.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Truth-table-based bi-decomposition engine.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 30, 2007.] + + Revision [$Id: bdc_.c,v 1.00 2007/01/30 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "bdcInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/bdc/module.make b/abc_with_bb_support/src/aig/bdc/module.make new file mode 100644 index 000000000..140a4d5f9 --- /dev/null +++ b/abc_with_bb_support/src/aig/bdc/module.make @@ -0,0 +1,4 @@ +SRC += src/aig/bdc/bdcCore.c \ + src/aig/bdc/bdcDec.c \ + src/aig/bdc/bdcTable.c + diff --git a/abc_with_bb_support/src/aig/cnf/cnf.h b/abc_with_bb_support/src/aig/cnf/cnf.h new file mode 100644 index 000000000..dda183f89 --- /dev/null +++ b/abc_with_bb_support/src/aig/cnf/cnf.h @@ -0,0 +1,162 @@ +/**CFile**************************************************************** + + FileName [cnf.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: cnf.h,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __CNF_H__ +#define __CNF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +#include "vec.h" +#include "aig.h" +#include "darInt.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Cnf_Man_t_ Cnf_Man_t; +typedef struct Cnf_Dat_t_ Cnf_Dat_t; +typedef struct Cnf_Cut_t_ Cnf_Cut_t; + +// the CNF asserting outputs of AIG to be 1 +struct Cnf_Dat_t_ +{ + Aig_Man_t * pMan; // the AIG manager, for which CNF is computed + int nVars; // the number of variables + int nLiterals; // the number of CNF literals + int nClauses; // the number of CNF clauses + int ** pClauses; // the CNF clauses + int * pVarNums; // the number of CNF variable for each node ID (-1 if unused) +}; + +// the cut used to represent node in the AIG +struct Cnf_Cut_t_ +{ + char nFanins; // the number of leaves + char Cost; // the cost of this cut + short nWords; // the number of words in truth table + Vec_Int_t * vIsop[2]; // neg/pos ISOPs + int pFanins[0]; // the fanins (followed by the truth table) +}; + +// the CNF computation manager +struct Cnf_Man_t_ +{ + Aig_Man_t * pManAig; // the underlying AIG manager + char * pSopSizes; // sizes of SOPs for 4-variable functions + char ** pSops; // the SOPs for 4-variable functions + int aArea; // the area of the mapping + Aig_MmFlex_t * pMemCuts; // memory manager for cuts + int nMergeLimit; // the limit on the size of merged cut + unsigned * pTruths[4]; // temporary truth tables + Vec_Int_t * vMemory; // memory for intermediate ISOP representation + int timeCuts; + int timeMap; + int timeSave; +}; + + +static inline Dar_Cut_t * Dar_ObjBestCut( Aig_Obj_t * pObj ) { Dar_Cut_t * pCut; int i; Dar_ObjForEachCut( pObj, pCut, i ) if ( pCut->fBest ) return pCut; return NULL; } + +static inline int Cnf_CutSopCost( Cnf_Man_t * p, Dar_Cut_t * pCut ) { return p->pSopSizes[pCut->uTruth] + p->pSopSizes[0xFFFF & ~pCut->uTruth]; } + +static inline int Cnf_CutLeaveNum( Cnf_Cut_t * pCut ) { return pCut->nFanins; } +static inline int * Cnf_CutLeaves( Cnf_Cut_t * pCut ) { return pCut->pFanins; } +static inline unsigned * Cnf_CutTruth( Cnf_Cut_t * pCut ) { return (unsigned *)(pCut->pFanins + pCut->nFanins); } + +static inline Cnf_Cut_t * Cnf_ObjBestCut( Aig_Obj_t * pObj ) { return pObj->pData; } +static inline void Cnf_ObjSetBestCut( Aig_Obj_t * pObj, Cnf_Cut_t * pCut ) { pObj->pData = pCut; } + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// ITERATORS /// +//////////////////////////////////////////////////////////////////////// + +// iterator over leaves of the cut +#define Cnf_CutForEachLeaf( p, pCut, pLeaf, i ) \ + for ( i = 0; (i < (int)(pCut)->nFanins) && ((pLeaf) = Aig_ManObj(p, (pCut)->pFanins[i])); i++ ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== cnfCore.c ========================================================*/ +extern Cnf_Dat_t * Cnf_Derive( Aig_Man_t * pAig, int nOutputs ); +extern Cnf_Man_t * Cnf_ManRead(); +extern void Cnf_ClearMemory(); +/*=== cnfCut.c ========================================================*/ +extern Cnf_Cut_t * Cnf_CutCreate( Cnf_Man_t * p, Aig_Obj_t * pObj ); +extern void Cnf_CutPrint( Cnf_Cut_t * pCut ); +extern void Cnf_CutFree( Cnf_Cut_t * pCut ); +extern void Cnf_CutUpdateRefs( Cnf_Man_t * p, Cnf_Cut_t * pCut, Cnf_Cut_t * pCutFan, Cnf_Cut_t * pCutRes ); +extern Cnf_Cut_t * Cnf_CutCompose( Cnf_Man_t * p, Cnf_Cut_t * pCut, Cnf_Cut_t * pCutFan, int iFan ); +/*=== cnfData.c ========================================================*/ +extern void Cnf_ReadMsops( char ** ppSopSizes, char *** ppSops ); +/*=== cnfMan.c ========================================================*/ +extern Cnf_Man_t * Cnf_ManStart(); +extern void Cnf_ManStop( Cnf_Man_t * p ); +extern Vec_Int_t * Cnf_DataCollectPiSatNums( Cnf_Dat_t * pCnf, Aig_Man_t * p ); +extern void Cnf_DataFree( Cnf_Dat_t * p ); +extern void Cnf_DataWriteIntoFile( Cnf_Dat_t * p, char * pFileName, int fReadable ); +void * Cnf_DataWriteIntoSolver( Cnf_Dat_t * p ); +/*=== cnfMap.c ========================================================*/ +extern void Cnf_DeriveMapping( Cnf_Man_t * p ); +extern int Cnf_ManMapForCnf( Cnf_Man_t * p ); +/*=== cnfPost.c ========================================================*/ +extern void Cnf_ManTransferCuts( Cnf_Man_t * p ); +extern void Cnf_ManFreeCuts( Cnf_Man_t * p ); +extern void Cnf_ManPostprocess( Cnf_Man_t * p ); +/*=== cnfUtil.c ========================================================*/ +extern Vec_Ptr_t * Aig_ManScanMapping( Cnf_Man_t * p, int fCollect ); +extern Vec_Ptr_t * Cnf_ManScanMapping( Cnf_Man_t * p, int fCollect, int fPreorder ); +/*=== cnfWrite.c ========================================================*/ +extern void Cnf_SopConvertToVector( char * pSop, int nCubes, Vec_Int_t * vCover ); +extern Cnf_Dat_t * Cnf_ManWriteCnf( Cnf_Man_t * p, Vec_Ptr_t * vMapped, int nOutputs ); +extern Cnf_Dat_t * Cnf_DeriveSimple( Aig_Man_t * p, int nOutputs ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/aig/cnf/cnfCore.c b/abc_with_bb_support/src/aig/cnf/cnfCore.c new file mode 100644 index 000000000..faf5be23a --- /dev/null +++ b/abc_with_bb_support/src/aig/cnf/cnfCore.c @@ -0,0 +1,185 @@ +/**CFile**************************************************************** + + FileName [cnfCore.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG-to-CNF conversion.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: cnfCore.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cnf.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Cnf_Man_t * s_pManCnf = NULL; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Converts AIG into the SAT solver.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cnf_Dat_t * Cnf_Derive( Aig_Man_t * pAig, int nOutputs ) +{ + Cnf_Man_t * p; + Cnf_Dat_t * pCnf; + Vec_Ptr_t * vMapped; + Aig_MmFixed_t * pMemCuts; + int clk; + // allocate the CNF manager + if ( s_pManCnf == NULL ) + s_pManCnf = Cnf_ManStart(); + // connect the managers + p = s_pManCnf; + p->pManAig = pAig; + + // generate cuts for all nodes, assign cost, and find best cuts +clk = clock(); + pMemCuts = Dar_ManComputeCuts( pAig, 10 ); +p->timeCuts = clock() - clk; + + // find the mapping +clk = clock(); + Cnf_DeriveMapping( p ); +p->timeMap = clock() - clk; +// Aig_ManScanMapping( p, 1 ); + + // convert it into CNF +clk = clock(); + Cnf_ManTransferCuts( p ); + vMapped = Cnf_ManScanMapping( p, 1, 1 ); + pCnf = Cnf_ManWriteCnf( p, vMapped, nOutputs ); + Vec_PtrFree( vMapped ); + Aig_MmFixedStop( pMemCuts, 0 ); +p->timeSave = clock() - clk; + + // reset reference counters + Aig_ManResetRefs( pAig ); +//PRT( "Cuts ", p->timeCuts ); +//PRT( "Map ", p->timeMap ); +//PRT( "Saving ", p->timeSave ); + return pCnf; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cnf_Man_t * Cnf_ManRead() +{ + return s_pManCnf; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cnf_ClearMemory() +{ + if ( s_pManCnf == NULL ) + return; + Cnf_ManStop( s_pManCnf ); + s_pManCnf = NULL; +} + + +#if 0 + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cnf_Dat_t * Cnf_Derive_old( Aig_Man_t * pAig ) +{ +/* + // iteratively improve area flow + for ( i = 0; i < nIters; i++ ) + { +clk = clock(); + Cnf_ManScanMapping( p, 0 ); + Cnf_ManMapForCnf( p ); +PRT( "iter ", clock() - clk ); + } +*/ + // write the file + vMapped = Aig_ManScanMapping( p, 1 ); + Vec_PtrFree( vMapped ); + +clk = clock(); + Cnf_ManTransferCuts( p ); + + Cnf_ManPostprocess( p ); + Cnf_ManScanMapping( p, 0 ); +/* + Cnf_ManPostprocess( p ); + Cnf_ManScanMapping( p, 0 ); + Cnf_ManPostprocess( p ); + Cnf_ManScanMapping( p, 0 ); +*/ +PRT( "Ext ", clock() - clk ); + +/* + vMapped = Cnf_ManScanMapping( p, 1 ); + pCnf = Cnf_ManWriteCnf( p, vMapped ); + Vec_PtrFree( vMapped ); + + // clean up + Cnf_ManFreeCuts( p ); + Dar_ManCutsFree( pAig ); + return pCnf; +*/ + Aig_MmFixedStop( pMemCuts, 0 ); + return NULL; +} + +#endif + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/cnf/cnfCut.c b/abc_with_bb_support/src/aig/cnf/cnfCut.c new file mode 100644 index 000000000..3518958d4 --- /dev/null +++ b/abc_with_bb_support/src/aig/cnf/cnfCut.c @@ -0,0 +1,371 @@ +/**CFile**************************************************************** + + FileName [cnfCut.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG-to-CNF conversion.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: cnfCut.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cnf.h" +#include "kit.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates cut of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cnf_Cut_t * Cnf_CutAlloc( Cnf_Man_t * p, int nLeaves ) +{ + Cnf_Cut_t * pCut; + int nSize = sizeof(Cnf_Cut_t) + sizeof(int) * nLeaves + sizeof(unsigned) * Aig_TruthWordNum(nLeaves); + pCut = (Cnf_Cut_t *)Aig_MmFlexEntryFetch( p->pMemCuts, nSize ); + pCut->nFanins = nLeaves; + pCut->nWords = Aig_TruthWordNum(nLeaves); + pCut->vIsop[0] = pCut->vIsop[1] = NULL; + return pCut; +} + +/**Function************************************************************* + + Synopsis [Deallocates cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cnf_CutFree( Cnf_Cut_t * pCut ) +{ + if ( pCut->vIsop[0] ) + Vec_IntFree( pCut->vIsop[0] ); + if ( pCut->vIsop[1] ) + Vec_IntFree( pCut->vIsop[1] ); +} + +/**Function************************************************************* + + Synopsis [Creates cut for the given node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cnf_Cut_t * Cnf_CutCreate( Cnf_Man_t * p, Aig_Obj_t * pObj ) +{ + Dar_Cut_t * pCutBest; + Cnf_Cut_t * pCut; + unsigned * pTruth; + assert( Aig_ObjIsNode(pObj) ); + pCutBest = Dar_ObjBestCut( pObj ); + assert( pCutBest != NULL ); + assert( pCutBest->nLeaves <= 4 ); + pCut = Cnf_CutAlloc( p, pCutBest->nLeaves ); + memcpy( pCut->pFanins, pCutBest->pLeaves, sizeof(int) * pCutBest->nLeaves ); + pTruth = Cnf_CutTruth(pCut); + *pTruth = (pCutBest->uTruth << 16) | pCutBest->uTruth; + pCut->Cost = Cnf_CutSopCost( p, pCutBest ); + return pCut; +} + +/**Function************************************************************* + + Synopsis [Deallocates cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cnf_CutPrint( Cnf_Cut_t * pCut ) +{ + int i; + printf( "{" ); + for ( i = 0; i < pCut->nFanins; i++ ) + printf( "%d ", pCut->pFanins[i] ); + printf( " } " ); +} + +/**Function************************************************************* + + Synopsis [Allocates cut of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cnf_CutDeref( Cnf_Man_t * p, Cnf_Cut_t * pCut ) +{ + Aig_Obj_t * pObj; + int i; + Cnf_CutForEachLeaf( p->pManAig, pCut, pObj, i ) + { + assert( pObj->nRefs > 0 ); + pObj->nRefs--; + } +} + +/**Function************************************************************* + + Synopsis [Allocates cut of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cnf_CutRef( Cnf_Man_t * p, Cnf_Cut_t * pCut ) +{ + Aig_Obj_t * pObj; + int i; + Cnf_CutForEachLeaf( p->pManAig, pCut, pObj, i ) + { + pObj->nRefs++; + } +} + +/**Function************************************************************* + + Synopsis [Allocates cut of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cnf_CutUpdateRefs( Cnf_Man_t * p, Cnf_Cut_t * pCut, Cnf_Cut_t * pCutFan, Cnf_Cut_t * pCutRes ) +{ + Cnf_CutDeref( p, pCut ); + Cnf_CutDeref( p, pCutFan ); + Cnf_CutRef( p, pCutRes ); +} + +/**Function************************************************************* + + Synopsis [Merges two arrays of integers.] + + Description [Returns the number of items.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Cnf_CutMergeLeaves( Cnf_Cut_t * pCut, Cnf_Cut_t * pCutFan, int * pFanins ) +{ + int i, k, nFanins = 0; + for ( i = k = 0; i < pCut->nFanins && k < pCutFan->nFanins; ) + { + if ( pCut->pFanins[i] == pCutFan->pFanins[k] ) + pFanins[nFanins++] = pCut->pFanins[i], i++, k++; + else if ( pCut->pFanins[i] < pCutFan->pFanins[k] ) + pFanins[nFanins++] = pCut->pFanins[i], i++; + else + pFanins[nFanins++] = pCutFan->pFanins[k], k++; + } + for ( ; i < pCut->nFanins; i++ ) + pFanins[nFanins++] = pCut->pFanins[i]; + for ( ; k < pCutFan->nFanins; k++ ) + pFanins[nFanins++] = pCutFan->pFanins[k]; + return nFanins; +} + +/**Function************************************************************* + + Synopsis [Computes the stretching phase of the cut w.r.t. the merged cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Cnf_TruthPhase( Cnf_Cut_t * pCut, Cnf_Cut_t * pCut1 ) +{ + unsigned uPhase = 0; + int i, k; + for ( i = k = 0; i < pCut->nFanins; i++ ) + { + if ( k == pCut1->nFanins ) + break; + if ( pCut->pFanins[i] < pCut1->pFanins[k] ) + continue; + assert( pCut->pFanins[i] == pCut1->pFanins[k] ); + uPhase |= (1 << i); + k++; + } + return uPhase; +} + +/**Function************************************************************* + + Synopsis [Removes the fanin variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cnf_CutRemoveIthVar( Cnf_Cut_t * pCut, int iVar, int iFan ) +{ + int i; + assert( pCut->pFanins[iVar] == iFan ); + pCut->nFanins--; + for ( i = iVar; i < pCut->nFanins; i++ ) + pCut->pFanins[i] = pCut->pFanins[i+1]; +} + +/**Function************************************************************* + + Synopsis [Inserts the fanin variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cnf_CutInsertIthVar( Cnf_Cut_t * pCut, int iVar, int iFan ) +{ + int i; + for ( i = pCut->nFanins; i > iVar; i-- ) + pCut->pFanins[i] = pCut->pFanins[i-1]; + pCut->pFanins[iVar] = iFan; + pCut->nFanins++; +} + +/**Function************************************************************* + + Synopsis [Merges two cuts.] + + Description [Returns NULL of the cuts cannot be merged.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cnf_Cut_t * Cnf_CutCompose( Cnf_Man_t * p, Cnf_Cut_t * pCut, Cnf_Cut_t * pCutFan, int iFan ) +{ + Cnf_Cut_t * pCutRes; + static int pFanins[32]; + unsigned * pTruth, * pTruthFan, * pTruthRes; + unsigned * pTop = p->pTruths[0], * pFan = p->pTruths[2], * pTemp = p->pTruths[3]; + unsigned uPhase, uPhaseFan; + int i, iVar, nFanins, RetValue; + + // make sure the second cut is the fanin of the first + for ( iVar = 0; iVar < pCut->nFanins; iVar++ ) + if ( pCut->pFanins[iVar] == iFan ) + break; + assert( iVar < pCut->nFanins ); + // remove this variable + Cnf_CutRemoveIthVar( pCut, iVar, iFan ); + // merge leaves of the cuts + nFanins = Cnf_CutMergeLeaves( pCut, pCutFan, pFanins ); + if ( nFanins+1 > p->nMergeLimit ) + { + Cnf_CutInsertIthVar( pCut, iVar, iFan ); + return NULL; + } + // create new cut + pCutRes = Cnf_CutAlloc( p, nFanins ); + memcpy( pCutRes->pFanins, pFanins, sizeof(int) * nFanins ); + assert( pCutRes->nFanins <= pCut->nFanins + pCutFan->nFanins ); + + // derive its truth table + // get the truth tables in the composition space + pTruth = Cnf_CutTruth(pCut); + pTruthFan = Cnf_CutTruth(pCutFan); + pTruthRes = Cnf_CutTruth(pCutRes); + for ( i = 0; i < 2*pCutRes->nWords; i++ ) + pTop[i] = pTruth[i % pCut->nWords]; + for ( i = 0; i < pCutRes->nWords; i++ ) + pFan[i] = pTruthFan[i % pCutFan->nWords]; + // move the variable to the end + uPhase = Kit_BitMask( pCutRes->nFanins+1 ) & ~(1 << iVar); + Kit_TruthShrink( pTemp, pTop, pCutRes->nFanins, pCutRes->nFanins+1, uPhase, 1 ); + // compute the phases + uPhase = Cnf_TruthPhase( pCutRes, pCut ) | (1 << pCutRes->nFanins); + uPhaseFan = Cnf_TruthPhase( pCutRes, pCutFan ); + // permute truth-tables to the common support + Kit_TruthStretch( pTemp, pTop, pCut->nFanins+1, pCutRes->nFanins+1, uPhase, 1 ); + Kit_TruthStretch( pTemp, pFan, pCutFan->nFanins, pCutRes->nFanins, uPhaseFan, 1 ); + // perform Boolean operation + Kit_TruthMux( pTruthRes, pTop, pTop+pCutRes->nWords, pFan, pCutRes->nFanins ); + // return the cut to its original condition + Cnf_CutInsertIthVar( pCut, iVar, iFan ); + // consider the simple case + if ( pCutRes->nFanins < 5 ) + { + pCutRes->Cost = p->pSopSizes[0xFFFF & *pTruthRes] + p->pSopSizes[0xFFFF & ~*pTruthRes]; + return pCutRes; + } + + // derive ISOP for positive phase + RetValue = Kit_TruthIsop( pTruthRes, pCutRes->nFanins, p->vMemory, 0 ); + pCutRes->vIsop[1] = (RetValue == -1)? NULL : Vec_IntDup( p->vMemory ); + // derive ISOP for negative phase + Kit_TruthNot( pTruthRes, pTruthRes, pCutRes->nFanins ); + RetValue = Kit_TruthIsop( pTruthRes, pCutRes->nFanins, p->vMemory, 0 ); + pCutRes->vIsop[0] = (RetValue == -1)? NULL : Vec_IntDup( p->vMemory ); + Kit_TruthNot( pTruthRes, pTruthRes, pCutRes->nFanins ); + + // compute the cut cost + if ( pCutRes->vIsop[0] == NULL || pCutRes->vIsop[1] == NULL ) + pCutRes->Cost = 127; + else if ( Vec_IntSize(pCutRes->vIsop[0]) + Vec_IntSize(pCutRes->vIsop[1]) > 127 ) + pCutRes->Cost = 127; + else + pCutRes->Cost = Vec_IntSize(pCutRes->vIsop[0]) + Vec_IntSize(pCutRes->vIsop[1]); + return pCutRes; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/cnf/cnfData.c b/abc_with_bb_support/src/aig/cnf/cnfData.c new file mode 100644 index 000000000..87617c8bd --- /dev/null +++ b/abc_with_bb_support/src/aig/cnf/cnfData.c @@ -0,0 +1,4784 @@ +/**CFile**************************************************************** + + FileName [cnfData.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG-to-CNF conversion.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: cnfData.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cnf.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static char s_Data3[81] = "!#&()*+,-.0123456789:;<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZ[]abcdefghijklmnopqrstuvwxyz|"; + +static char * s_Data4[] = { +"! B a . 8 .B 8a K !K T Ta j 8j Tj s ( + (B +a (. +8 .B( +8a (K +K T( +T j( ", +"+j Tj( s+ E !E H Ha E. 8E H. H8 EK EK! HT HTa jE 8jE Hj sH d +d Hd g d. 8d ", +"Hd. g8 dK +dK Td gT dj +jd Hjd gs 2 !2 2B a2 5 58 5B 5a 2K 2K! T2 Ta2 5j 58", +"j 5T s5 ; +; ;B a; 5; > 5;B >a ;K +;K T; +T; ;j >j 5T; s> E2 !E2 H2 Ha2 5E ", +"58E H5 H58 EK2 !E2K HT2 TaH2 5jE 8j5E H5T sH5 d; +d; H; g; 5d >d H5d g> dK;", +" dK+; H;T gT; 5dj >jd H;j gs> N !N NB aN N. 8N .BN 8aN Q Q! QT Qa Qj Q8 QTj", +" sQ (N +N (BN +aN N.( +8N (BN. 8a+N Q( +Q QT( +QT Qj( +Q8 TjQ( s+Q W W! HW ", +"Wa W. W8 HW. H8W WQ Q!W Z Za Wj W8Q Zj sZ Wd +W HWd gW Wd. +W8 WdH. gW8 Qd ", +"+WQ Zd gZ Wjd +Wj Zdj gZs m m! mB am 5m 8m 5mB 5am Qm Q!m Tm Qam p p8 pT sp", +" m; +m m;B +ma 5m; >m m;5B >ma Q; +Qm Q;T +Tm p; p> pT; sp> Wm W!m Hm Hma 5", +"W 5W8 H5W H8m WQm WmQ! Zm Zam pW pW8 Zp sZp v v+ vH gv v5 v> vH5 gv> vQ v+Q", +" Zv gZv pv pv> Zpv y # & #B a& #. 8& .B# 8a& #K &K T# T& j# &j Tj# s& #( +&", +" #B( +a& #(. +8& #(.B 8a+& #(K +&K T#( +T& j#( +j& j#T( s+& #E &E H# H& #E.", +" 8&E H#. H8& EK# &EK HT# H&T j#E &jE Hj# sH& d# d& Hd# g& d#. 8d& d#H. g8& ", +"dK# d&K Td# gT& dj# d&j H#dj gs& #2 &2 2B# a&2 5# 5& 5B# 5a& 2K# &2K T#2 T&", +"2 5j# 5&j 5T# s5& ;# ;& ;#B a;& 5;# >& ;#5B >&a ;#K ;&K T;# T;& ;j# >&j 5#T", +"; s>& #E2 &E2 H#2 H&2 5#E 5&E H5# H5& #E2K EK&2 T#H2 H2T& j#5E 5E&j H#5T H5", +"s& d;# d&; H;# g;& 5d# >&d H#5d g>& ;#dK dK;& H#T; T;g& 5#dj d&>j H#;j >&gs", +" #N &N #BN a&N N.# 8&N #BN. aN8& Q# Q& QT# Qa& Qj# Q8& TjQ# sQ& #(N +&N #(N", +"B a&+N #(N. 8&+N .BN#( +8a&N Q#( +Q& T#Q( Q&+T j#Q( Q&+j QTj#( +Qs& W# W& H", +"W# H&W W#. W8& W#H. W8H& WQ# W&Q Z# Z& Wj# W&j Zj# sZ& Wd# +W& WdH# gW& d#W", +". 8dW& HWd#. W8g& Qd# Qd& Zd# gZ& W#dj W&+j djZ# Z&gs m# m& m#B am& 5m# 5&m", +" m#5B am5& Qm# Q&m Tm# Tm& p# p& pT# sp& m;# +m& ;#mB m&a; m;5# >m& 5m;#B a", +"m>& Q;# Q;& Q#T; T;Q& p;# p>& T;p# p&s> Wm# W&m Hm# Hm& 5W# 5W& H#5W 5WH& Q", +"mW# WQm& Zm# Z&m pW# p&W Zp# Zp& v# v& vH# gv& v5# v>& H5v# v&g> vQ# v&Q Zv", +"# Zv& pv# pv& Z#pv y& C !C D Da C. 8C D. D8 CK CK! DT DTa jC 8jC Dj sD C( +", +"C D( +D C(. +8C D(. +D8 CK( +CK DT( +DT jC( +jC Dj( s+D CE !CE HD HDa CE. 8", +"CE HD. H8D EKC !CEK HDT DTHa jCE jC8E HjD sHD dC +dC Dd gD dC. 8dC Dd. gD8 ", +"dKC dK+C DdT gDT djC +Cdj Ddj gsD C2 !C2 D2 Da2 5C 58C 5D 5D8 2KC !C2K DT2 ", +"T2Da 5jC 8j5C 5DT s5D ;C +;C D; +D; 5;C >C 5D; >D ;CK ;C+K D;T D;+T ;jC >jC", +" D;j s>D CE2 CE!2 HD2 H2Da 5CE 8C5E H5D 5DH8 CE2K !E2CK H2DT HDTa2 jC5E 58j", +"CE 5DHj H5sD d;C d;+C H;D gD; 5dC >Cd 5Dd g>D ;CdK +d;CK TdD; D;gT 5Cdj dj>", +"C Dd5T >Dgs CN !CN DN DaN N.C 8CN DN. D8N QC Q!C QD QDa QjC Q8C QDj sQD C(N", +" +CN D(N +DN C(N. 8C+N N.D( +ND8 QC( +QC QD( +QD jCQ( +CQ8 Q(Dj +QsD WC W!C", +" WD WDa WC. W8C WD. WD8 WQC WCQ! ZD ZDa WjC WCQ8 ZDj sZD WdC +WC WDd gWD dC", +"W. +CW8 W.Dd WDg8 QdC +CQd ZDd gZD WCdj +CWj DdZj gDsZ mC mC! Dm Dma 5mC 8m", +"C 5Dm D8m QmC mCQ! QDm DmQa pC p8C pD spD m;C +mC Dm; +Dm m;5C >mC 5mD; >Dm", +" Q;C +CQ; QD; QD+m p;C p>C pD; p>D WmC mCW! HmD DmWa 5WC 5CW8 5WD WD5a QmWC", +" Q!WmC ZDm DmZa pWC W8pC ZpD pDsZ vC v+C vD gvD v5C v>C vD5 v>D vQC +QvC Zv", +"D vDgZ pvC >Cpv pvD yD b b& bD c b. b8 bD. c8 bK bK& bT cT bj b8j bTj sc b(", +" b+ bD( c+ b(. b+8 D(b. c+8 bK( b+K bT( c+T bj( b+j b(Dj sc+ bE b&E bH cH b", +"E. b8E bH. cH8 bKE &EbK bHT cHT bjE bE&j bHj scH bd b+d bHd gc bd. b8d b.Dd", +" gc8 bdK bKd& bTd gcT bdj +jbd Hjbd gsc b2 b&2 bD2 c2 b5 b58 b5D c5 bK2 &2b", +"K bT2 c2T b5j 5&bj b5T sc5 b; b+; b;D c; b5; >b 5Db; c> b;K bK;& bT; c;T b;", +"j >bj 5Tb; c>s bE2 &Eb2 bH2 c2H b5E bE5& bH5 c5H EKb2 bK&E2 HTb2 HTc2 5jbE ", +"&jEb5 H5bT sHc5 bd; d&b; bH; gc; b5d >bd H5bd c>g dKb; d&;bK H;bT gTc; 5dbj", +" bd>j H;bj gsc> bN b&N bDN cN bN. b8N DNb. c8N bQ bQ& bQD cQ bQj bQ8 QDbj s", +"cQ b(N b+N D(bN c+N N.b( +8bN bD(N. +8cN bQ( b+Q b(QD cQ+ Qjb( +Qb8 bQDj( s", +"+cQ bW bW& bHW cW bW. bW8 b.WD cW8 bWQ W&bQ Zb Zc bWj W8bQ Zbj Zcs bWd b+W ", +"WDbd gcW Wdb. +Wb8 bHWd. g8cW bQd +WbQ Zbd Zcg Wjbd +Wbj bdZj gsZc bm bm& b", +"mD cm b5m b8m 5Dbm c5m bQm Q&bm bTm cQm pb pb8 pbD cp bm; b+m Dmb; cm+ 5mb;", +" >bm b5Dm; c>m bQ; +Qbm QDb; cQ; pb; p>b bTp; cp> bWm W&bm bHm cWm b5W 5Wb8", +" H5bW c5W WQbm bWQm& Zbm Zcm pbW bWp& Zpb cpZ vb vb+ vbH cv vb5 v>b bHv5 cv", +"> vbQ b+vQ Zvb cvZ pvb v>pb pvZb yc 0 !0 0B a0 1 18 1B 1a 0K !K0 T0 Ta0 1j ", +"18j 1T s1 0( +0 0B( +a0 1( 1+ 1(B 1+a 0(K +0K T0( +T0 1j( 1+j 1T( s1+ E0 !E", +"0 H0 Ha0 1E 18E 1H 1H8 EK0 !0EK HT0 TaH0 1jE 8j1E 1HT s1H d0 +d0 Hd0 g0 1d ", +"1+d 1Hd g1 dK0 dK+0 Td0 gT0 1dj +j1d 1Td gs1 02 !02 2B0 a02 15 158 15B 1a5 ", +"2K0 !02K T02 T2a0 15j 581j 1T5 s15 ;0 +;0 ;0B a;0 1; >1 1;B >1a ;0K ;0+K T;", +"0 +0T; 1;j >j1 1T; s>1 E02 E0!2 H02 a0H2 15E 581E 1H5 H51a E02K !E20K T0H2 ", +"HTa02 5j1E 158jE H51T 1Hs5 d;0 d;+0 H;0 g;0 1d5 >1d 1H; g>1 ;0dK +d;0K H0T;", +" T;g0 dj1; 1d>j H;1T s>g1 0N !0N 0BN a0N 1N 18N 1NB 1aN Q0 Q!0 QT0 Qa0 1Q 1", +"Q8 1QT s1Q 0(N +0N 0(NB a0+N 1(N 1+N (B1N +a1N Q0( +Q0 T0Q( +0Qa 1Q( 1+Q QT", +"1( 1+sQ W0 W!0 HW0 Wa0 1W 1W8 1HW 1Wa WQ0 W0Q! Z0 Za0 1WQ W81Q Z1 sZ1 Wd0 +", +"W0 WdH0 gW0 1Wd 1+W HW1d g1W Qd0 +0Qd Zd0 gZ0 1Qd +W1Q Z1d gZ1 m0 m0! m0B a", +"m0 1m 1m8 1mB 1am Qm0 m0Q! Tm0 Q0am p1 p18 p1T sp1 m;0 +m0 ;0mB +0am 1m; >m", +"1 m;1B 1a>m Q;0 +0Q; Q0T; +0Tm p1; p>1 1Tp; s1p> Wm0 m0W! Hm0 H0am 1W5 W81m", +" 1Hm H81m QmW0 Q!Wm0 Zm0 amZ0 p1W 1Wp8 Zp1 Z1sp v0 v+0 vH0 gv0 v1 v>1 v1H g", +"v1 vQ0 +Qv0 Zv0 g0Zv pv1 v1p> Zv1 y1 9 9& 9B 9a 91 : 91B :a 9K 9&K 9T 9Ta 9", +"j :j 9T1 s: 9( 9+ 9(B 9+a 91( :+ 1B9( :+a 9(K 9+K 9T( 9+T 9j( :+j 9(1T s:+ ", +"9E 9&E 9H 9Ha 91E :E 9H1 :H 9EK &E9K 9HT H&9T 9jE :jE 9Hj s:H 9d 9+d 9Hd g9", +" 9d1 :d 1H9d g: 9dK 9Kd& 9Td g9T 9dj :dj 1T9d g:s 92 9&2 92B 9a2 95 :5 95B ", +":5a 92K &29K 9T2 92T& 95j :5j 95T s:5 9; 9+; 9;B 9a; 95; :> 1B9; :>a 9;K 9K", +";& 9T; +T9; 9;j :>j 1T9; s:> 9E2 &E92 9H2 92H& 95E :5E 9H5 :H5 EK92 9&E2K H", +"T92 9HT&2 5j9E 5j:E H59T s5:H 9d; d&9; 9H; g9; 95d :>d 1H9; g:> dK9; d&;9K ", +"H;9T 9Tg; 5d9j >j:d H;9j s:g> 9N 9&N 9NB 9aN 91N :N 1B9N :aN 9Q 9Q& 9QT 9Qa", +" 9Q1 :Q 1Q9T s:Q 9(N 9+N (B9N +a9N 1(9N :+N 91(NB +a:N 9Q( 9+Q QT9( +Q9T 9(", +"1Q :Q+ 9Q1T( s+:Q 9W 9W& 9HW 9Wa 9W1 :W 1H9W :HW 9WQ W&9Q Z9 Z9a 9Wj :WQ Z9", +"1 Z: 9Wd 9+W HW9d g9W 1W9d :W+ 9H1Wd g:W 9Qd +W9Q Z9d gZ9 1Q9d :Qd 9dZ1 Z:g", +" 9m 9m& 9mB 9am 95m :m 1B9m :ma 9Qm Q&9m 9Tm Qa9m p9 :p p9T :ps 9m; 9+m m;9", +"B +m9a 1m9; :>m 95;mB :a>m 9Q; +Q9m Q;9T +T9m p9; :p> 9Tp; s>:p 9Wm W&9m 9H", +"m Hm9a 95W :W5 1H9m :Hm WQ9m 9WQm& Z9m 9aZm p9W :pW Zp9 Z:p v9 v9+ v9H gv9 ", +"v91 :v 9Hv1 :vg v9Q 9+vQ Zv9 Zvg9 pv9 :vp pvZ9 y: C0 !C0 D0 Da0 1C 18C 1D 1", +"D8 CK0 !0CK DT0 TaD0 1jC 8j1C 1DT s1D C0( +C0 D0( +D0 1C( 1+C 1D( 1+D 0(CK ", +"CK+0 T0D( DT+0 jC1( 1C+j 1(Dj 1+sD CE0 CE!0 HD0 DaH0 1CE 8C1E 1HD H81D C0EK", +" !E0CK DTH0 HDTa0 jC1E 18jCE Hj1D 1HsD dC0 dC+0 Dd0 gD0 1dC 1C8d 1Dd g1D CK", +"d0 +dC0K D0Td DTg0 1Cdj djC1+ Dd1T s1gD C02 C0!2 D02 D2a0 15C 581C 1D5 5D1a", +" C02K C02!K T2D0 DTa02 5j1C 158jC 5D1T 1Ds5 ;C0 ;C+0 D;0 +0D; 1;C >C1 1D; >", +"D1 CK;0 +;C0K D0T; a;0DT 1C;j 1j>C D;1T s1>D E0C2 C02!E D0H2 HDa02 5C1E 158", +"CE H51D 1H5D8 C02EK EK0!C2 HDT02 DTaH02 15jCE 58j1CE 1H5Dj s1H5D ;Cd0 +d;C0", +" H0D; D;g0 1C5d 1d>C H;1D >Dg1 dK;C0 dK;+C0 H;DT0 gDT;0 djC1; >j1dC 1H;Dj g", +"s>1D C0N C0!N D0N a0DN 1CN 8C1N 1DN 1ND8 QC0 !CQ0 QD0 DaQ0 1QC 1CQ8 1QD 1Qs", +"D 0(CN C0+N 0(DN D0+N C(1N +C1N D(1N 1N+D C0Q( QC+0 D0Q( +0QD QC1( 1C+Q 1(Q", +"D +Q1D WC0 !CW0 WD0 DaW0 1WC 1CW8 1WD WD1a QCW0 Q!WC0 ZD0 DaZ0 1CWj 1WQ8C Z", +"1D sDZ1 dCW0 WC+0 W0Dd WDg0 Wd1C 1C+W WD1d 1WgD dCQ0 +WQC0 DdZ0 ZDg0 1CQd 1", +"+WQC 1DZd gDZ1 mC0 !Cm0 Dm0 D0am 1mC 1C8m 1Dm D81m mCQ0 Q!mC0 Q0Dm am0QD p1", +"C 18pC pD1 s1pD ;Cm0 mC+0 m;D0 +0Dm m;1C 1m>C Dm1; 1D>m ;CQ0 +QmC0 Q0D; +QD", +"m0 1;pC p1>C 1Dp; p1>D mCW0 W!mC0 H0Dm am0WD 1C5W 1W58C Hm1D 1HmD8 WQmC0 Wm", +"CQ!0 DmZ0 ZDam0 1WpC p1W8C pDZ1 Zp1sD vC0 +Cv0 vD0 g0vD v1C >Cv1 v1D vDg1 Q", +"Cv0 v+QC0 Z0vD ZvDg0 pCv1 pv>1C v1pD y1D b9 b9& 9D c9 b1 :b b1D :c b9K 9&bK", +" bT9 c9T b1j :bj b1T s:c b9( b+9 9D( c9+ b1( :b+ b(1D :c+ 9(bK bK9+ b(9T 9+", +"cT b(9j b+:j b(1T s+:c b9E 9&bE bH9 c9H b1E :bE b1H :cH 9EbK b9&EK 9HbT 9Hc", +"T bE9j bj:E 9Hbj :Hsc bd9 9+bd 9Dd gc9 b1d :db 1Hbd g:c bK9d d&Kb9 9Tbd c9g", +"T 9dbj bd:j 1Tbd s:gc b92 9&b2 9D2 c29 b15 :b5 95D :c5 92bK b9&2K b29T 9Tc2", +" 95bj b5:j 95bT c5s: b;9 9+b; 9D; c;9 b1; :>b 1Db; c>: bK9; ;&Kb9 9Tb; 9Tc;", +" 9;bj :b>j 1Tb; s:c> 9Eb2 b9&E2 b29H 9Hc2 bE95 b5:E 9Hb5 c5:H b9E2K 9&EbK2 ", +"bH9T2 c29HT b15jE :b5jE b1H5T :cHs5 9db; d&;b9 9Hb; c;g9 95bd >b:d 1Hb; c>g", +": bd9;K bd9;&K bH;9T c;9gT b1d;j :>bdj b1HT; g:sc> b9N 9&bN 9DN c9N b1N :bN", +" bN1D :cN bQ9 b9Q& 9QD cQ9 b1Q :Qb 1QbT :cQ 9(bN bN9+ D(9N 9+cN 1(bN b+:N b", +"1D(N cN:+ b(9Q 9+bQ 9(QD 9+cQ b(1Q b+:Q b1QD( :+cQ bW9 b9W& 9WD cW9 b1W :Wb", +" 1HbW :cW 9WbQ bW9Q& Zb9 Zc9 9Wbj bW:Q Zb1 Z:c 9Wbd 9+bW WD9d cWg9 1Wbd b+:", +"W b1HWd :Wgc 9Qbd b+W9Q bdZ9 g9Zc 1Qbd bQ:d b1Zd Zcg: bm9 b9m& 9Dm cm9 b1m ", +":mb 1Dbm :cm 9Qbm bQ9m& 9Tbm 9Qcm pb9 :pb p9D cp: 9mb; 9+bm Dm9; 9+cm 1mb; ", +">b:m b1mD; :mc> 9Qb; b+Q9m QD9; 9Qc; b1p; pb:> 9Dp; c>:p 9Wbm bW9m& 9Hbm 9H", +"cm 95bW b5:W 1Hbm c5:W bW9Qm 9WQbm& bmZ9 cmZ9 b1pW pb:W pbZ9 cpZ: vb9 b+v9 ", +"v9D cv9 vb1 :vb b1vD :vc bQv9 vb9+Q vbZ9 Z9cv vbp9 pb:v vbZ1 y:c L !L LB aL", +" L. 8L .BL 8aL M M! TM aM Mj 8M TMj sM (L +L (BL +aL (L. +8L (L.B 8a+L M( +", +"M TM( +TM Mj( +M8 MjT( s+M EL !EL HL HaL EL. 8EL HL. H8L ME ME! HM HMa MjE ", +"8ME HMj sHM dL +dL HdL gL dL. 8dL dLH. g8L dM +Md HMd gM dMj 8dM dMHj gsM L", +"2 !L2 2BL aL2 5L 58L 5BL 5aL M2 M2! TM2 aM2 5M 5M8 5TM s5M ;L +;L ;LB a;L 5", +";L >L ;L5B >La M; +M; T;M aM; 5M; >M T;5M >Ms EL2 EL!2 HL2 aLH2 5EL 8E5L H5", +"L HL5a ME2 !EM2 HM2 H2aM 5ME 5E8M H5M H5sM d;L d;+L H;L g;L 5dL >Ld HL5d g>", +"L dM; +dM; HM; gM; 5dM >Md 5dHM >Mg LN !LN NBL aLN N.L 8LN LBN. aN8L QM Q!M", +" QTM QaM QMj Q8M TMQj sQM (LN +LN (LNB aL+N (LN. 8L+N (BLN. +8aLN QM( +QM T", +"MQ( Qa+M MjQ( Q8+M QTMj( +QsM WL W!L HWL WaL WL. W8L WLH. HLW8 WM WM! ZM Za", +"M WMj W8M ZMj sZM WdL +WL WdHL gWL dLW. +LW8 HWdL. W8gL WMd +WM ZdM gZM dMW", +"j W8+M dMZj sZgM mL mL! mLB amL 5mL 8mL mL5B 5Lam Mm Mm! TmM aMm pM p8M pTM", +" spM m;L +mL ;LmB +Lam m;5L >mL 5m;LB am>L Q;M +Mm MmT; Tm+M pM; >Mp T;pM s", +"p>M WmL mLW! HmL HLam 5WL 5LW8 HL5W HL8m WMm W!Mm ZMm aMZm pWM W8pM ZpM sMZ", +"p vL v+L vHL gvL v5L v>L H5vL >Lgv vM vM+ ZvM gvM pvM >Mv vMZp yM #L &L #BL", +" a&L #L. 8&L #L.B a&8L M# M& TM# T&M Mj# 8M& MjT# sM& #(L +&L #(LB a&+L (L#", +". 8&+L #L.(B +8a&L M#( +M& M#T( T&+M j#M( M&+j TMj#( +Ms& #EL &EL H#L H&L E", +"L#. &E8L #LH. 8&HL M#E M&E HM# HM& j#ME 8EM& MjH# HMs& d#L d&L d#HL g&L #Ld", +". 8Ld& Hd#L. 8&gL dM# dM& H#dM gM& Mjd# d&8M dMjH# sMg& #L2 &L2 #L2B &2aL 5", +"#L 5&L #B5L a&5L M#2 M&2 M#T2 T2M& 5M# 5M& TM5# 5Ms& ;#L ;&L #B;L aL;& ;#5L", +" >&L 5;#LB a&>L M;# M;& T#M; M;T& 5#M; >M& M;#5T sM>& EL#2 EL&2 #LH2 &LH2 #", +"E5L &E5L 5#HL HL5& #EM2 &EM2 M#H2 H2M& M#5E 5EM& H#5M 5MH& ;#dL dL;& ;#HL ;", +"&gL d#5L d&>L H5d#L g&>L d#M; M;d& H#M; M;g& 5#dM dM>& H5Md# >&gM #LN &LN #", +"LNB &LaN #LN. &L8N #LN.B 8a&LN QM# Q&M TMQ# aMQ& MjQ# 8MQ& QTMj# Q&sM (L#N ", +"&L+N #LN(B +a&LN #LN(. +8&LN N.#(BL 8a&+LN M#Q( Q&+M QTM#( +QTM& QMj#( +Q8M", +"& TMjQ#( s+QM& W#L W&L W#HL HLW& #LW. 8&WL HW#L. H8W&L WM# WM& ZM# Z&M MjW#", +" 8MW& MjZ# sMZ& d#WL +LW& HWd#L W&gL Wd#L. d&LW8 Wd#HL. gW8&L W#dM W&+M dMZ", +"# Z&gM dMjW# dM&W8 ZdMj# Z&Mgs m#L m&L #BmL aLm& m#5L 5Lm& 5m#LB am&5L Mm# ", +"Mm& T#Mm MmT& pM# p&M TMp# sMp& ;#mL +Lm& m;#LB am&+L 5m;#L m&>L m;#5BL >ma", +"&L Q#M; M;Q& Mm#T; aMm;& M;p# p&>M pTM;# >Mps& m#WL WLm& m#HL HLm& W#5L 5LW", +"& H5W#L H5&WL W#Mm MmW& MmZ# MmZ& WMp# WMp& pMZ# ZMp& v#L v&L H#vL gLv& 5#v", +"L >Lv& vH5#L gv>&L vM# vM& Z#vM vMZ& p#vM vMp& ZpvM# y&M U U! UD Ua U. U8 U", +"D. U8D UM U!M V Va Uj U8M Vj Vs U( U+ UD( U+D U(. U+8 D(U. +DU8 UM( U+M V( ", +"V+ Uj( U+j Vj( Vs+ UE U!E UH UHa UE. U8E UH. UH8 UME MEU! VH VHa UjE UE8M V", +"Hj VsH Ud U+d UHd gU Ud. U8d U.Dd gU8 UdM +MUd Vd gV Udj +jUd Vdj gVs U2 U!", +"2 UD2 Ua2 U5 U58 U5D U5a UM2 M2U! V2 V2a U5M 5MU8 V5 Vs5 U; U+; U;D Ua; U5;", +" >U 5DU; >UD U;M +MU; V; V+; U;j >MU V5; V> UE2 !EU2 UH2 HaU2 U5E 58UE UH5 ", +"H5U8 MEU2 U!ME2 V2H HaV2 UE5M U58ME V5H sHV5 Ud; +dU; UH; gU; U5d >Ud H5Ud ", +"g>U dMU; dM;U+ Vd; gV; 5dUj Ud>M V5d V>g UN U!N UDN UaN UN. U8N DNU. UND8 U", +"Q UQ! VQ VQa UQj UQ8 VQj VsQ U(N U+N D(UN UN+D N.U( +8UN UD(N. U+D8N UQ( U+", +"Q VQ( V+Q QjU( +QU8 QjV( sQV+ UW UW! UHW UWa UW. UW8 U.WD H8UW UWQ Q!UW VZ ", +"VZa UWj W8UQ VZj sZV UWd U+W WDUd gUW WdU. +WU8 UHWd. UWg8 UQd +WUQ VZd gVZ", +" WjUd +WUj ZjVd sZgV Um Um! UmD Uam U5m U8m 5DUm 5aUm UQm Q!Um Vm Vma pU pU", +"8 Vp Vps Um; U+m DmU; +DUm 5mU; >Um U5Dm; Ua>m UQ; +QUm Vm; V+m pU; p>U Vp;", +" V>p UWm W!Um UHm HmUa U5W 5WU8 H5UW H8Um WMUm Mm!UW VZm ZaVm pUW UWp8 VpZ ", +"sZVp vU vU+ vUH gvU vU5 v>U UHv5 v>gU vUQ U+vM Vv Vvg pvU v>pU Vvp yV bU U&", +" bUD cU bU. b8U UDb. cU8 bM bM& Vb Vc bMj b8M Vbj scV bU( b+U UDb( cU+ U(b.", +" U+b8 bUD(. U+c8 bM( b+M Vb( Vc+ b(Uj U+bj bjV( V+sc bUE U&E bHU cUH UEb. b", +"EU8 b.UH UHc8 bME bEM& VbH VcH bEUj bE8M bHVj sHVc bdU Ud& UHbd gcU b.Ud U8", +"bd bHUd. cUg8 bdM +Mbd Vdb gVc Udbj 8dbM bdVj scgV bU2 U&2 UDb2 c2U b5U U5&", +" bU5D c5U bM2 b2M& V2b Vc2 b5M 5Mb8 V5b c5V b;U U;& bUD; c;U U5b; >bU b5UD;", +" c>U bM; +Mb; V;b c;V U;bj >Mb b5V; V>c UEb2 &EU2 b2UH UHc2 bEU5 UE5& UHb5 ", +"UHc5 MEb2 M&Eb2 bHV2 cHV2 bE5M b5M8E bHV5 cHV5 Udb; d&U; UHb; c;gU U5bd bd>", +"U bH5Ud gUc> dMb; dM&b; bHV; c;gV 5dbM bd>M b5Vd c>gV bUN U&N UDbN cUN UNb.", +" bNU8 bUDN. U8cN bQU UQ& VbQ cQV UQbj UQb8 bQVj VscQ U(bN bNU+ bUD(N U+cN b", +"U(N. b+U8N UD(bN. cU+8N b(UQ U+bQ bQV( V+cQ bQUj( b+QU8 VbQj( cQVs+ bWU UW&", +" UHbW cWU b.UW UWb8 bHUW. UWc8 bWM WMU& ZbV ZcV UWbj W8bM VbZj VZsc UWbd U+", +"bW bHUWd cWgU bWUd. b+WU8 UHWbd. cWUg8 UQbd +WbM VdZb ZcgV bWMdj d&jUW ZbVd", +"j gVZsc bmU Um& bUDm cmU U5bm U8bm b5UDm U5cm bMm Q&Um Vmb cmV pbU pU& Vpb ", +"cpV Umb; U+bm bmUD; U+cm b5Um; bm>U U5Dbm; >Ucm UQb; +Mbm bQV; V+cm bMp; pb", +">M V;pb V>cp UWbm W&Um UHbm UHcm U5bW 5WU& bH5UW U5cW WMbm Mm&bW VmZb VZcm ", +"bWpU UWp& ZbVp VpZc vbU vU& bHvU cvU b5vU >bvU vbUH5 vUc> vbM b+vM Vvb cvV ", +"vbpU vb>M pbVv yVc k k! kB ak 1k 8k 1kB 1ak kM kM! Tk Tka l l8 lT ls k( +k ", +"k(B +ka 1k( 1+k k(1B +k1a kM( +kM Tk( +Tk l( l+ lT( ls+ kE kE! Hk Hka 1kE 8", +"kE 1Hk H8k kME M!kE HkT akHM lE l8E lH lsH dk +kd Hkd gk 1dk 8dk Hk1d g1k d", +"kM dk+M Tdk gkT ld l+d lHd gl k2 k2! k2B ak2 5k 5k8 5kB 5ak kM2 M!k2 Tk2 T2", +"ak l5 l58 l5T ls5 k; +k; k;B ak; 1;k >k 1Bk; >ka k;M k;+M Tk; T;+k l; l> l;", +"T l>s kE2 !Ek2 Hk2 H2ak 5kE 5E8k H5k 5aHk MEk2 kME!2 H2Tk ak2HM l5E 58lE lH", +"5 sHl5 dk; +dk; Hk; gk; 5dk >kd Hk1; >kg k;dM dkM+; TkH; Tkg; ld5 l>d lH; g", +"l> kN kN! kNB akN 1kN 8kN kN1B 1Nak Qk Qk! QkT Qak lQ lQ8 lQT lsQ k(N +kN (", +"BkN +Nak k(1N 1N+k 1k(NB akN1+ Qk( +Qk Q(Tk Qa+k lQ( l+Q QTl( s+lQ Wk Wk! H", +"kW Wak 1Wk W8k Hk1W W8Hk WkQ Q!Wk Zk Zka lW lW8 lZ lZs Wkd +Wk HWdk gkW Wk1", +"d +k1W 1HWdk 1Wgk Qdk Wk+Q Zkd gZk lWd l+W lZd glZ km km! kmB akm 1mk 8km 1", +"Bkm ak1m Qkm Q!km Tkm akTm lp lp8 lpT lsp km; +km m;kB ak+m km1; >km km;1B ", +"ak>m Qk; Qk+m TkQ; Tk+m l;p l>p pTl; spl> Wkm W!km Hkm akHm 5Wk W85k Hk1m W", +"a5k kmWM km!WM Zkm akZm lWp pWl8 lZp splZ vk vk+ vkH gvk v1k >kv 1Hvk gv>k ", +"vkQ +Qvk Zvk Zvgk lv lv> lvZ yl 9k k& 9kB 9ak 91k :k 1B9k :ak 9M 9M& 9Tk 9a", +"M l9 l: l9T ls: 9k( 9+k k(9B +k9a 1k9( :+k 91k(B +k:a 9M( 9+M 9(Tk +T9M l9(", +" l:+ 9Tl( s:l+ 9kE k&E 9Hk Hk& 1k9E :kE 91Hk :Hk 9ME 9EM& 9HM HM9a l9E l:E ", +"lH9 l:H 9dk dk& Hk9d g9k 91dk :dk 9H1dk g:k 9dM +M9d HM9d g9M ld9 l:d 9Hld ", +"gl: 9k2 k&2 k29B 92ak 95k :5k 9B5k 5a:k 9M2 92M& 92Tk 92aM l95 l:5 95lT l5s", +": 9;k k;& 9Bk; ak9; 5k9; >k: k;B95 :a>k 9M; +M9; Tk9; aM9; l;9 l>: 9Tl; s:l", +"> kE92 &Ek2 92Hk H2k& 9E5k 5k:E Hk95 H5:k ME92 k&EM2 92HM 9HM&2 95lE :5lE 9", +"Hl5 l5:H dk9; k;d& Hk9; 9;gk 5k9d :d>k 9H5dk >kg: dM9; dk&M; HM9; 9Mg; 95ld", +" :>ld 9Hl; g:l> 9kN k&N kN9B 9Nak 1k9N :kN 91kNB ak:N 9Qk Qk& Qk9T Qa9M l9Q", +" l:Q 9QlT :Qls k(9N 9N+k 9k(NB akN9+ 91k(N +k:N 1k(9NB :+akN 9(Qk +Q9M 9QTk", +"( 9+QTk 9Ql( :Ql+ l9QT( :Q+ls 9Wk Wk& Hk9W Wk9a 91Wk :Wk 9H1Wk Hk:W 9WM W&9", +"M Z9k Zk& lW9 l:W lZ9 Z:l Wk9d +k9W 9HWdk 9Wgk 9W1dk +W:k 1HW9dk :Wgk WM9d ", +"+W9M 9dZk Z9gk 9Wld :Wl+ Z9ld Z:gl 9mk km& 9Bkm ak9m 5k9m :mk kmB95 ak:m 9M", +"m kmQ& Tk9m aM9m p9l :pl lTp9 ls:p km9; +k9m km;9B akm9+ km;95 :m>k 95;kmB ", +":>akm Qk9; +M9m 9Q;Tk ak;9Q p9l; :pl> l;p9T l>ps: Wk9m kmW& Hk9m kmH& 5k9W ", +"5W:k 9H5Wk Hk:m WM9m km&WM 9MZm kmZ& p9lW lW:p p9lZ lZ:p v9k vk& 9Hvk v9gk ", +"95vk :vk v91Hk gk:v v9M 9+vM v9Zk v9gM lv9 lv: Z9lv yl: Uk U!k Dk Uak 1U 1U", +"8 1UD 1Ua UkM kMU! Vk Vak lU lU8 Vl lsV Uk( U+k Dk( +Dk 1U( 1U+ 1(Dk U+1D k", +"MU( Uk+M Vk( V+k lU( l+U Vl( l+V UkE kEU! UHk HkUa 1UE 1EU8 1UH UH1a kMUE U", +"!kME VHk HkVa lUE U8lE lHV VslH Udk +kUd Ddk gUk 1Ud U+1d UH1d g1U UkdM dkM", +"U+ Vdk gVk ldU U+ld ldV glV Uk2 k2U! Dk2 U2ak 1U5 5kU8 5Dk U51a kMU2 U!kM2 ", +"V2k akV2 lU5 U5l8 Vl5 V5ls U;k +kU; Dk; D;+k 1U; >kU U;1D >kD UkM; k;MU+ V;", +"k +kV; l;U l>U l;V V>l kEU2 U!kE2 U2Hk ak2UH 1EU5 1U58E U51H 1UH5a UkME2 kM", +"EU!2 HkV2 V2Hak U5lE lU58E V5lH lsVH5 dkU; dk;U+ HkU; U;gk U51d 1U>d UH1; g", +"U>k dkMU; U+dk;M HkV; V;gk U5ld >Uld V5ld V>gl UkN kNU! DkN UNak 1UN 1NU8 1", +"NDk 1NUa UQk U!Qk VQk QaVk lUQ UQl8 VlQ lQVs k(UN UN+k k(DN +NDk U(1N 1NU+ ", +"1UD(N 1U+DN U(Qk +kUQ QkV( +QVk UQl( U+lQ VQl( lQV+ UWk U!Wk WDk WkUa 1UW U", +"81W UH1W UW1a WkUQ UWQk! ZkV VaZk lWU UWl8 lZV VlsZ WkUd +kUW DdWk UWgk UW1", +"d U+1W 1UHWd 1UgW QkUd U+WQk VdZk ZkgV UWld U+lW VZld lZgV Umk U!km Dkm akU", +"m 1Um U81m Um1D Ua1m QkUm km!UQ Vmk akVm pUl l8pU Vpl lsVp kmU; +kUm kmD; D", +"k+m Um1; 1U>m 1UmD; Dk>m QkU; U+Qkm QkV; +kVm pUl; pUl> l;Vp Vpl> WkUm km!U", +"W HkUm akmUH U51W 1UW8m UH1m 1UH8m UWQkm UWQkm! VmZk ZkVam pUlW lWpU8 lZVp ", +"VplsZ vUk U+vk vDk vUgk v1U vU>k 1UvD v1gU UQvk vU+Qk Vvk gkVv lvU vUl> lvV", +" ylV t t& tD tc t1 t: t1D t:c tM tM& Vt tcV lt lt: ltV u t( t+ tD( tc+ t1( ", +"t:+ 1Dt( :ct+ tM( t+M Vt( t+V lt( lt+ l(Vt u+ tE t&E tH tcH t1E t:E tH1 t:H", +" tME M&tE tHV VctH ltE lEt: ltH uH td t+d tHd gt td1 t:d 1Htd gt: tdM +Mtd ", +"tdV gtV ltd t:ld tHld ug t2 t&2 tD2 tc2 t5 t:5 t5D tc5 tM2 M&t2 Vt2 V2tc lt", +"5 t5l: t5V u5 t; t+; t;D tc; t5; t> 1Dt; t>c t;M +Mt; t;V Vtc; lt; t>l Vlt;", +" u> tE2 &Et2 tH2 c2tH t5E :5tE tH5 :Ht5 MEt2 tM&E2 V2tH tcVH2 lEt5 t:5lE t5", +"lH uH5 td; d&t; tH; gt; t5d t>d 1Ht; gt> dMt; t+dM; Vdt; t;gV t5ld ldt> tHl", +"; ug> tN t&N tDN tcN t1N t:N 1DtN tN:c tQ tQ& tQV tcQ ltQ t:Q VltQ uQ t(N t", +"+N D(tN c+tN 1(tN :+tN t1D(N t:c+N tQ( t+Q VQt( cQt+ l(tQ tQl+ tQVl( u+Q tW", +" tW& tHW tcW tW1 t:W 1HtW :ctW tWQ WMt& tZ tZc ltW t:lW tZl uZ tWd t+W WDtd", +" gtW 1Wtd :Wt+ tH1Wd tWg: tQd +WtQ tZd gtZ tWld t+lW ldtZ uZg tm tm& tmD tc", +"m t5m t:m 1Dtm :ctm tQm Q&tm tmV cQtm tp tp: tpV up tm; t+m Dmt; cmt+ 1mt; ", +"t>m t5Dm; tmc> tQ; +Qtm Vmt; cQt; tp; tp> t;Vp up> tWm W&tm tHm cWtm t5W :W", +"t5 1Htm :Htm WMtm tWQm& tZm tmZc tpW tW:p tZp uZp tv tv+ tvH gtv tv1 tv: v1", +"tH :vgt tvQ vMt+ tZv cvtZ lvt t>lv tZlv yu ) !) )B a) ). 8) .B) 8a) )K !K) ", +"T) Ta) j) 8j) Tj) s) * +* *B a* *. 8* *B. 8*a *K +*K T* +T* *j +j* T*j s* E", +") !E) H) Ha) E). 8E) H). H8) EK) !)EK HT) TaH) jE) jE8) Hj) sH) d* +d* H* g", +"* d*. 8d* H*. g8* dK* dK+* H*T gT* dj* dj8* H*j gs* )2 !)2 2B) a)2 5) 58) 5", +"B) 5a) 2K) !)2K T)2 T2a) 5j) 8j5) 5T) s5) ;* +;* *B; a;* 5* >* 5*B >*a ;*K ", +";*+K T;* a;T* 5*j >*j 5T* s>* E)2 E)!2 H)2 a)H2 5E) 8E5) H5) H)5a E)2K !)2E", +"K T)H2 HTa)2 jE5) 58jE) H)5T H5s) d;* d;+* H;* g;* 5d* >*d H5* g>* ;*dK +d;", +"*K T;H* T;g* dj5* dj>* 5TH* s*g> )N !)N )BN a)N N.) 8)N )BN. aN8) Q) Q!) QT", +") Qa) Qj) Q8) TjQ) sQ) *N +*N *BN a*N *N. 8*N .B*N aN8* Q* +Q* Q*T Qa* Q*j ", +"Q8* QT*j s*Q W) W!) HW) Wa) W). W8) W)H. H)W8 WQ) W)Q! Z) Za) Wj) W)Q8 Zj) ", +"sZ) W* +W* H*W gW* W*. W8* H.W* W8g* W*Q W*+Q Z* gZ* W*j W*+j Z*j sZ* m) m)", +"! m)B am) 5m) 8m) m)5B 5)am Qm) m)Q! Tm) Q)am p) p8) pT) sp) m* +m* m*B am*", +" 5*m >m* 5m*B am>* Q;* Q*+m Tm* T*+m p* p>* p*T sp* Wm) m)W! Hm) H)am 5W) 5", +")W8 H)5W H)8m QmW) Q!Wm) Zm) amZ) pW) W8p) Zp) s)Zp v* v+* vH* gv* v5* v>* ", +"H5v* >*gv vQ* +Qv* Zv* Z*gv pv* v>p* Zp* y* , ,& ,B ,a ,. ,8 ,.B ,8a ,K ,&K", +" ,T ,Ta ,j ,8j ,Tj s, ,* - *B, -a ,*. -8 ,.*B -8a ,*K -K ,T* -T ,j* -j T*,j", +" s- ,E ,&E ,H ,Ha ,E. ,8E ,H. ,H8 ,EK &E,K ,HT H&,T ,jE ,E&j ,Hj s,H ,d -d ", +",Hd g- ,d. -8d ,.H* g-8 ,dK -dK ,Td g-T ,dj -jd H*,j s-g ,2 ,&2 ,2B ,a2 ,5 ", +",58 ,5B ,5a ,2K &2,K ,T2 ,2T& ,5j 5&,j ,5T s,5 ,; -; ,;B -a; ,5; -> *B,5 ->", +"a ,;K -;K ,T; -T; ,;j ->j 5T,; s-> ,E2 &E,2 ,H2 ,2H& ,5E ,E5& ,H5 H5,8 EK,2", +" ,&E2K HT,2 ,HT&2 5j,E &jE,5 H5,T ,Hs5 ,d; -d; ,H; g-; ,5d ->d H5,d g-> dK,", +"; dK-; H;,T g;-T 5d,j -d>j H;,j g>s- ,N ,&N ,NB ,aN ,N. ,8N .B,N 8a,N ,Q ,Q", +"& ,QT ,Qa ,Qj ,Q8 QT,j s,Q ,*N -N ,N*B -aN *N,. -8N *B,N. 8a-N ,Q* -Q Q*,T ", +"-QT Q*,j -Q8 ,QT*j s-Q ,W ,W& ,HW ,Wa ,W. ,W8 HW,. H8,W ,WQ W&,Q Z, Z&, ,Wj", +" W8,Q Z,j sZ, ,Wd -W H*,W g-W ,.W* -W8 ,HWd. g8-W ,Qd -WQ Z*, Z- W*,j -Wj ,", +"dZj Z-s ,m ,m& ,mB ,am ,5m ,8m 5m,B 5a,m ,Qm Q&,m ,Tm Qa,m p, p&, p,T sp, ,", +"m; -m *B,m -ma 5*,m ->m m*B,5 -a>m ,Q; -Qm Q;,T -Tm p*, -p ,Tp* -ps ,Wm W&,", +"m ,Hm Hm,a ,5W 5W,8 H5,W H8,m WQ,m ,WQm& Z,m ,aZm p,W ,Wp& Zp, Zps, v, -v v", +",H -vg v,5 -v> ,Hv5 g>-v v,Q -vQ Zv, Z-v pv, -pv pvZ, y- C) !C) D) Da) C). ", +"8C) D). D8) CK) !)CK DT) TaD) jC) jC8) Dj) sD) *C +*C D* +D* *C. 8*C D*. D8", +"* *CK *C+K D*T D*+T *jC +C*j D*j s*D CE) CE!) HD) DaH) E)C. CE8) D)H. H)D8 ", +"C)EK CE)!K DTH) HDTa) CEj) 8jCE) H)Dj HDs) d*C d*+C H*D gD* *Cd. d*8C H.D* ", +"D8g* *CdK +d*CK TdD* D*gT dC*j dj*+C D*Hj gDs* C)2 C)!2 D)2 D2a) 5C) 8C5) 5", +"D) 5)D8 C)2K !)2CK T2D) DTa)2 jC5) 58jC) 5)Dj 5Ds) ;*C ;*+C D;* a;D* 5*C >*", +"C 5D* >D* *C;K +;*CK T;D* a;*DT 5C*j *j>C D*5T >Ds* E)C2 CE)!2 D)H2 HDa)2 C", +"E5) 58CE) H)5D H5D8) CE)2K EKC!)2 HDT)2 DTaH)2 5jCE) 8jC5E) H5DT) sH5D) ;*d", +"C +d;*C D;H* D;g* d*5C d*>C 5DH* gD>* dK;*C dK;+*C H;DT* gDT;* dj*5C >*djC ", +"H5*Dj >D*gs C)N C)!N D)N a)DN C)N. C)8N N.D) 8)DN QC) !CQ) QD) DaQ) jCQ) 8C", +"Q) Q)Dj QDs) *CN *C+N D*N +ND* N.*C *C8N *ND. DN8* Q*C +CQ* QD* Q*+D QC*j Q", +"C8* DjQ* QDs* WC) !CW) WD) DaW) C)W. 8CW) D)W. W)D8 QCW) Q!WC) ZD) DaZ) jCW", +") W8QC) DjZ) ZDs) W*C +CW* WD* WDg* *CW. WC8* W.D* D8W* WCQ* +WQ*C Z*D gDZ*", +" WC*j +Wj*C D*Zj sDZ* mC) !Cm) Dm) D)am mC5) mC8) 5)Dm D)8m mCQ) Q!mC) Q)Dm", +" am)QD pC) 8Cp) pD) s)pD m*C +Cm* Dm* D*+m 5Cm* m*>C Dm5* Dm>* QCm* m*C+Q D", +"mQ* am*QD p*C >Cp* pD* pDs* mCW) W!mC) H)Dm am)WD WC5) 5W8C) 5)WD H8mD) WQm", +"C) WmCQ!) DmZ) ZDam) WCp) pW8C) Z)pD ZpDs) v*C +*vC vD* g*vD 5*vC v*>C 5Dv*", +" >*vD Q*vC v+Q*C vDZ* ZvDg* v*pC pv>*C pDZ* y*D ,b ,b& ,D c, ,b. ,8b ,D. c8", +", ,bK bK,& ,DT cT, ,jb b8,j ,Dj sc, b* -b ,D* c- b*. -b8 ,.D* c-8 b*K -bK b", +"T* c-T b*j -jb D*,j s-c ,bE b&,E ,Hb cH, bE,. ,Eb8 ,.bH ,Hc8 bK,E ,b&EK bH,", +"T ,HcT ,Ebj &jE,b bH,j cHs, ,db -bd ,Dd gc- ,.bd b8-d ,.Dd g8c- ,Kbd bd-K b", +"T,d -Tgc bd,j bd-j H*bj gcs- ,b2 b&,2 ,D2 c2, ,5b b5,8 ,5D c5, bK,2 ,b&2K ,", +"2bT ,Tc2 b5,j ,5b&j b5,T c5s, ,;b -b; ,D; c-; b5* ->b 5D,; c>- ,Kb; b;-K bT", +",; -Tc; b;,j >b-j 5Tb* c>s- bE,2 ,b&E2 ,2bH ,Hc2 ,Eb5 ,5b8E bH,5 ,Hc5 ,bE2K", +" bK&,E2 ,HbT2 c2H,T ,5bjE ,5b&jE ,H5bT s,Hc5 bd,; bd-; bH,; c;g- b5,d -d>b ", +"H5b* g-c> ,db;K -bd;K ,H;bT c-Tg; ,5dbj ->bdj ,H;bj s-gc> ,bN b&,N ,DN c,N ", +"bN,. ,Nb8 DN,. ,8cN ,Qb ,bQ& ,QD cQ, bQ,j bQ,8 QD,j cQs, b*N -bN ,ND* c-N *", +"Nb. b8-N ,D*N. -8cN bQ* -Qb QDb* c-Q Q*bj bQ-j ,QD*j cQs- ,Wb ,bW& ,WD cW, ", +",.bW bW,8 ,.WD ,Wc8 bW,Q ,WbQ& Zb, Zc, bW,j ,W8bQ ,DZj s,Zc bW* -Wb H*bW c-", +"W b.W* bW-8 ,WDd. c8-W bQ,d bW-Q Zb* Z-c W*bj bW-j b*Zj Zcs- ,mb ,bm& ,Dm c", +"m, b5,m b8,m 5D,m ,5cm bQ,m ,Qbm& bT,m ,Qcm pb, ,8pb pD, cp, bm* -mb Dm,; c", +"-m 5*bm >b-m ,5Dm* ->cm bQ,; bQ-m QD,; -Qcm pb* -pb ,Dp* cp- bW,m ,Wbm& bH,", +"m ,Hcm b5,W ,5Wb8 5W,D ,5cW ,WbQm bWQ,m& ,DZm cmZ, ,Wpb pbW,8 pbZ, Z,cp vb*", +" -vb vD, cv- ,5vb vb-> ,5vD -vc> ,Qvb vb-Q vbZ* cvZ- vbp* pb-v pbZ* yc- 0) ", +"!0) )B0 a0) 1) 18) 1)B 1a) 0)K 0)!K T0) a0T) 1j) 8j1) 1T) s1) *0 +*0 *B0 a*", +"0 1* 1+* 1*B 1a* *0K *0+K T*0 +0T* 1*j +j1* 1T* s*1 E0) E0!) H0) a0H) 1E) 8", +"E1) 1H) 1)H8 0)EK !E)0K T0H) HTa0) jE1) 18jE) 1)Hj 1Hs) d*0 d*+0 H*0 g*0 1d", +"* 8d1* 1H* g1* *0dK +d*0K H0T* T*g0 dj1* dj*1+ H*1T s*g1 0)2 0)!2 0)2B 0)a2", +" 15) 581) 5B1) 1)5a 0)2K 0)2!K 0)T2 Ta0)2 5j1) 158j) 1)5T 15s) ;*0 ;*+0 ;0*", +"B ;*a0 1;* >*1 *B1; 1a>* *0;K +;*0K ;*T0 a;*T0 ;j1* 1*>j 5T1* s1>* 0)E2 0)2", +"!E 0)H2 Ha0)2 5E1) 158E) 1)H5 1H58) 0)2EK EK0!)2 HT0)2 Ta0H)2 15jE) 58j1E) ", +"1H5T) s1H5) ;*d0 +d;*0 ;*H0 ;*g0 5d1* 1d>* H51* >*g1 dK;*0 dK;+*0 H;T*0 gT;", +"*0 dj*1; >*1dj 1H;T* s*1g> 0)N 0)!N 0)NB 0)aN 1)N 8)1N )B1N a)1N Q0) !0Q) T", +"0Q) a0Q) 1Q) 1)Q8 QT1) 1Qs) *0N *0+N 0B*N *0aN 1*N 1N8* 1N*B 1Na* Q*0 +0Q* ", +"Q0T* Q0a* 1Q* +Q1* Q*1T 1Qs* W0) !0W) W0H) a0W) 1W) 1)W8 HW1) 1)Wa Q0W) Q!W", +"0) Z0) a0Z) 1)Wj 1WQ8) Z1) s)Z1 W*0 +0W* H0W* W*g0 1W* +W1* H*1W 1Wg* W0Q* ", +"+WQ*0 Z*0 g0Z* W*1Q 1+WQ* Z1* Z1s* m0) !0m) )Bm0 m0a) 1m) 1)8m m)1B 1)am m0", +"Q) Q!m0) m0T) am0Q) p1) 18p) 1Tp) p1s) m*0 +0m* m0*B a0m* 1m* 1m>* *B1m am1", +"* Q0m* m*0+Q T0m* am*Q0 p*1 p1>* 1Tp* s1p* m0W) W!m0) m0H) am0H) 1)5W 1W58)", +" 1)Hm 1Hm8) WQm0) Wm0Q!) m0Z) Zam0) 1Wp) p1W8) p1Z) Zp1s) v*0 +*v0 H*v0 v*g", +"0 v1* >*v1 1Hv* g*v1 Q*v0 v+Q*0 v*Z0 Zv*g0 v1p* pv>1* p*Z1 y*1 ,9 ,9& ,9B ,", +"a9 ,1 :, ,1B :a, ,9K 9&,K ,T9 9T,a ,1j :j, ,1T s:, 9* -9 9*B -9a ,1* :- *B,", +"1 :-a 9*K -9K 9T* -T9 9*j :-j 1T9* s-: ,9E 9&,E ,H9 9H,a ,1E :,E ,1H :H, 9E", +",K ,9&EK 9H,T ,H9T& ,E9j ,j:E 9H,j :Hs, ,d9 -9d 9H* g-9 ,1d :-d 1H,d g:- ,K", +"9d 9d-K 9T,d -Tg9 9d,j -j:d 1T,d s-g: ,92 9&,2 92,B ,29a ,15 :5, ,B95 ,5:a ", +"92,K ,9&2K ,29T ,T9&2 95,j ,5:j 95,T :5s, ,;9 -9; *B9; 9a-; ,1; :-> *B95 :a", +"-> ,K9; 9;-K 9T,; 9T-; 9;,j -j:> 1T,; :-s> 9E,2 ,9&E2 ,29H ,H9&2 ,E95 ,5:E ", +"9H,5 ,H:5 ,9E2K 9&E,2K ,H9T2 9HT,a2 ,15jE :5j,E ,1H5T s,H:5 9d,; 9d-; 9H,; ", +"-9g; 95,d ->:d 1H,; :-g> ,d9;K -9d;K ,H;9T -T9g; ,1d;j :->dj ,1HT; g:s-> ,9", +"N 9&,N 9N,B ,N9a ,1N :,N 1N,B ,a:N ,Q9 ,9Q& 9Q,T 9Q,a ,1Q :Q, 1Q,T :Qs, 9*N", +" -9N 9N*B 9a-N ,N1* :-N ,1*NB -a:N 9Q* -Q9 Q*9T 9Q-T 1Q9* :-Q ,1QT* :Qs- ,W", +"9 ,9W& 9H,W 9W,a ,1W :W, 1H,W ,H:W 9W,Q ,W9Q& Z9, ,aZ9 9W,j ,W:Q Z1, Z:, 9W", +"* -W9 H*9W -Wg9 1W,d :-W ,1HW* :Wg- 9Q,d 9W-Q Z9* Z-9 1Q,d -W:Q ,1Z* Z:- ,m", +"9 ,9m& ,B9m 9a,m ,1m :m, ,B1m ,a:m 9Q,m ,Q9m& 9T,m ,Qa9m p9, :p, ,1pT s,:p ", +"9m* -m9 *B9m 9a-m 1m,; :-m ,1m*B :a-m 9Q,; 9Q-m Tm9* 9T-m p9* -p: 9Tp* :ps-", +" 9W,m ,W9m& 9H,m ,Hm9a 95,W ,5:W 1H,m ,H:m ,W9Qm 9WQ,m& ,mZ9 Z9a,m ,1pW p,:", +"W p9Z, Z,:p v9* -v9 ,Hv9 g9-v v1, :v- ,1vH -vg: ,Qv9 v9-Q v9Z* Z9-v v9p* -p", +":v p9Z* y:- C0) C0!) D0) a0D) 1C) 8C1) 1D) 1)D8 0)CK C0)!K T0D) DTa0) jC1) ", +"18jC) 1)Dj 1Ds) *C0 *C+0 D*0 +0D* 1*C 1C8* 1D* +D1* CK*0 +*C0K D0T* a*0DT 1", +"C*j *jC1+ D*1T 1Ds* E0C) C0)!E D0H) HDa0) CE1) 18CE) HD1) 1HD8) C0)EK EK)!C", +"0 HDT0) DTaH0) 1jCE) 8jC1E) 1HDT) s1HD) *Cd0 +d*C0 H0D* D*g0 d*1C 1+d*C H*1", +"D 1Dg* dK*C0 dK*+C0 H*DT0 gDT*0 dj*1C 1+d*jC 1H*Dj s*1gD 0)C2 C02!) 0)D2 Da", +"0)2 5C1) 158C) 1)5D 1D58) C02)K 2K!C0) DT0)2 Ta0D)2 15jC) 58j1C) 1D5T) s15D", +") *C;0 +;*C0 ;*D0 a;*D0 1C5* 1*>C 5D1* 1D>* ;*C0K ;*C+0K D;T*0 D;Ta*0 ;jC1*", +" >*1jC 1D;T* >D*s1 C02E) !E)C02 HD0)2 Da0H)2 15CE) 58C1E) 1H5D) H5D1a) EKC0", +")2 0)C2EK! DT0H)2 0)T2HDa 5jC1E) 5C1)8jE H5D1T) 1H5sD) d;*C0 d;*+C0 H;D*0 g", +"D;*0 1d5*C >*1dC 1H;D* >D*g1 ;*CdK0 dK+*;C0 D;TH*0 D;Tg*0 1d5*jC dj*>C1 H;j", +"1D* gs1>D* 0)CN C0N!) 0)DN Da0)N C)1N 18C)N D)1N 1D8)N C0Q) Q!C0) D0Q) QDa0", +") QC1) 1Q8C) 1)QD s1QD) C0*N +*C0N *0DN a*0DN *C1N 1+*CN 1ND* 1+D*N *CQ0 +Q", +"*C0 Q0D* +QD*0 1CQ* 1+Q*C QD1* s*1QD C0W) W!C0) D0W) WDa0) WC1) 1W8C) 1)WD ", +"1WD8) WQC0) WC0Q!) D0Z) ZDa0) 1WQC) W8C1Q) 1DZ) Z1Ds) *CW0 +W*C0 W0D* gWD*0", +" 1CW* 1+W*C WD1* g1DW* W*QC0 W*C+Q0 D*Z0 Z*Dg0 1W*QC *jC1+W 1DZ* Z1*gD C0m)", +" mC0!) m0D) am0D) mC1) 1m8C) 1)Dm 1Dm8) QmC0) mC0Q!) QDm0) QD0am) 1Cp) p18C", +") 1Dp) pD1s) *Cm0 m*C+0 D0m* am*D0 1Cm* >m1*C Dm1* >Dm1* m*CQ0 +QCm*0 QD;*0", +" am0QD* 1*pC p>1*C 1Dp* p>D1* WmC0) mC0W!) HmD0) WD0am) 1W5C) W8C1m) 1HmD) ", +"am)1WD QmCW0) QCW0m)! ZDm0) am0ZD) p1WC) 1W8pC) Zp1D) sZ1pD) *Cv0 v+*C0 D*v", +"0 gvD*0 1*vC v>1*C 1Dv* gv1D* vQ*C0 +Q*vC0 ZvD*0 gvDZ*0 pv1*C v>1p*C Zp*1D ", +"1Dy* ,b9 b9,& ,D9 c9, ,1b :b, ,1D :c, b9,K ,b9&K 9D,T ,Tc9 b1,j ,j:b b1,T :", +"cs, b*9 -b9 9D* c-9 b1* :-b 1Db* :c- bK9* b9-K 9Tb* c9-T 9*bj :b-j 1Tb* :cs", +"- b9,E ,b9&E 9H,D ,Hc9 ,Eb1 ,b:E b1,H cH:, ,b9EK b9&,EK ,H9bT c9H,T ,1bjE :", +"bj,E ,1HbT s,H:c 9db* bd-9 9Hb* c-g9 b1,d -b:d 1Hb* :cg- ,db9K -b9dK ,Dd9T ", +"c-9gT ,1dbj :-bdj ,1DTd g:sc- b9,2 ,b9&2 ,29D ,9c2 b1,5 ,5:b 95,D :,c5 ,b92", +"K b9&,2K ,D9T2 c29,T ,1b5j :b5,j ,1D5T s,5:c 9;b* b;-9 9D,; -9c; b1,; >b:- ", +"1D,; :-c> ,;b9K -b9;K ,D;9T c-9T; ,1;bj :->bj ,1DT; s-:c> ,b9E2 b9&,E2 ,H9b", +"2 c29,H ,1b5E :b5,E ,1Hb5 :cH,5 b9E,2K b9,&EK2 bH9,T2 ,H9c2T b15,jE ,5b:jE ", +"b1H,5T :cHs,5 ,db9; -b9d; ,H;9D c-9g; ,1db5 :->bd ,1Hb; g:c-> bd9,;K bd9-;K", +" 9H;bT* -T9gc; b1d,;j ->b:dj b1H,T; s-cg:> b9,N ,b9&N ,N9D ,9cN ,Nb1 ,b:N ,", +"N1D cN:, ,b9Q ,Qb9& 9Q,D ,Qc9 b1,Q ,Q:b 1Q,D :,cQ bN9* b9-N 9ND* -9cN bN1* ", +"-b:N ,1D*N cN:- 9Qb* bQ-9 QD9* c9-Q 1Qb* :b-Q ,1QD* c-:Q ,b9W ,Wb9& 9W,D ,W", +"c9 b1,W ,W:b 1W,D :,cW ,Wb9Q bW9,Q& ,DZ9 c9Z, ,1WbQ :Wb,Q ,1Zb :cZ, 9Wb* bW", +"-9 WD9* c9-W 1Wb* :b-W ,1WDd c-:W ,Qdb9 -Wb9Q b*Z9 c-Z9 ,1Qbd :-WbQ b1Z* :c", +"Z- ,b9m ,mb9& 9D,m ,mc9 b1,m ,m:b 1D,m :,cm ,Qb9m bQ9,m& ,QD9m cQ9,m ,1pb :", +"bp, ,1pD p,:c 9mb* bm-9 Dm9* c9-m 1mb* :b-m ,1Dm* c-:m ,Q;b9 -Qb9m ,QD9; c-", +"Q9m b1p* pb:- 9Dp* :c-p ,Wb9m bW9,m& ,Hm9D cW9,m ,1Wb5 :Wb,5 ,1Hbm :cW,5 bW", +"9,Qm WQ9m,b& Zb9,m Zc9,m pb9,W :pb,W Zpb,1 cpZ:, b*v9 -bv9 ,Dv9 v9c- ,1vb v", +"b:- ,1vD :c-v vb9,Q -vb9Q Zvb9* cvZ-9 pvb,1 :v-pb Zpb9* :cy- )L !)L )BL a)L", +" )L. 8)L )L.B a)8L M) M)! TM) aM) Mj) 8M) MjT) sM) *L +*L *BL a*L *L. 8*L .", +"B*L 8La* M* +M* T*M aM* M*j 8M* TM*j s*M E)L E)!L H)L a)HL )LE. E)8L )LH. 8", +")HL ME) !EM) HM) H)aM jEM) ME8) MjH) HMs) d*L d*+L H*L g*L *Ld. d*8L *LH. 8", +"*gL dM* +dM* HM* gM* M*dj dM8* M*Hj s*gM )L2 )L!2 )L2B )La2 5)L 8)5L )B5L a", +")5L M)2 !)M2 M)T2 M2a) 5M) 5)8M TM5) 5Ms) ;*L ;*+L ;L*B ;*aL 5*L >*L 5L*B a", +"*>L M;* +;M* M;T* M;a* 5M* >M* T*5M s*>M )LE2 !)2EL )LH2 Ha)L2 E)5L 58E)L 5", +")HL H58)L E)M2 ME)!2 M)H2 aM)H2 ME5) 5M8E) H)5M sH5M) ;*dL +d;*L ;*HL ;*gL ", +"d*5L d*>L HL5* g*>L d;M* dM;+* M;H* M;g* dM5* dM>* 5MH* >*gM )LN )L!N )LNB ", +")LaN )LN. )L8N .BL)N 8a)LN QM) M)Q! TMQ) Q)aM MjQ) Q)8M QTMj) QMs) *LN *L+N", +" NB*L *LaN N.*L *L8N *BLN. a*L8N Q*M Q*+M QTM* aMQ* QM*j 8MQ* M*jQT Q*sM W)", +"L !)WL W)HL a)WL )LW. 8)WL HW)L. H8W)L WM) M)W! ZM) aMZ) MjW) W)8M MjZ) ZMs", +") W*L +LW* HLW* W*gL *LW. WL8* H*WL. gW8*L WM* W*+M Z*M Z*gM M*Wj 8MW* M*Zj", +" sMZ* m)L !)mL )BmL m)aL m)5L m)8L 5m)LB am)5L Mm) M!m) T)Mm a)Mm pM) 8Mp) ", +"TMp) pMs) m*L +Lm* mL*B aLm* 5Lm* m*>L m*L5B >ma*L Mm* M*+m MmT* Mma* p*M p", +"*>M T*pM sMp* m)WL W!m)L m)HL am)HL W)5L 5W8)L H5W)L H8m)L W)Mm Mm)W! MmZ) ", +"aMmZ) WMp) pW8M) pMZ) ZpMs) v*L +*vL H*vL v*gL 5*vL v*>L vH5*L gv>*L vM* +M", +"v* vMZ* g*vM vMp* >*vM ZMp* y*M ,L ,&L ,LB ,aL ,L. ,8L .B,L 8a,L ,M ,M& ,TM", +" ,aM ,Mj ,8M TM,j s,M ,*L -L ,L*B -aL *L,. -8L *B,L. 8a-L ,M* -M T*,M -TM M", +"*,j -M8 M*j,T s-M ,EL &E,L ,HL ,LH& EL,. 8E,L HL,. ,LH8 ,ME ,EM& ,HM HM,a M", +"j,E ,E8M HM,j ,HsM ,dL -dL ,LH* g-L dL,. 8d-L ,HdL. -8gL ,dM -Md HM,d g-M d", +"M,j 8d-M ,HMdj gMs- ,L2 &L,2 2B,L aL,2 ,5L ,L5& 5B,L ,L5a ,M2 ,2M& TM,2 ,2a", +"M ,5M 5M,8 5T,M ,5sM ,;L -;L ;L,B a;-L ,L5* ->L ,5;LB -a>L ,M; -M; T;,M T;-", +"M 5M,; >M- ,5TM; >Ms- EL,2 ,&EL2 HL,2 ,HaL2 5E,L ,58EL ,LH5 ,H58L ME,2 M&E,", +"2 ,2HM ,HM&2 ,E5M ,5M8E H5,M s,H5M d;,L d;-L ,LH; -;gL ,L5d -d>L ,H5dL >Lg-", +" dM,; dM-; HM,; g;-M 5d,M -d>M ,H5dM g->M ,LN &L,N NB,L aL,N N.,L 8L,N ,LN.", +"B ,8aLN ,QM Q&,M QT,M Qa,M QM,j Q8,M ,QTMj ,QsM *L,N -LN *B,LN aL-N ,*LN. 8", +"L-N ,LN*B. -8aLN Q*,M -QM ,QTM* Qa-M M*j,Q Q8-M ,QTM*j sQ-M ,WL ,LW& HW,L ,", +"LWa WL,. ,LW8 ,HWL. ,H8WL ,WM W&,M Z,M ,aZM WM,j W8,M ,MZj sMZ, ,LW* -WL ,H", +"WdL gL-W ,WdL. W8-L H*L,W. -W8gL WM,d -WM ,dZM Z-M ,WMdj W8-M Z*M,j -MsZ ,m", +"L ,Lm& mL,B ,Lam 5m,L ,L8m ,5mLB ,5amL ,Mm M&,m Tm,M aM,m p,M ,8pM ,TpM sMp", +", ,Lm* -mL m*L,B am-L m*L,5 >L-m ,5Lm*B ->amL Q;,M -Mm Mm*,T Tm-M ,Mp* -pM ", +"p*T,M -Msp Wm,L m&L,W ,LHm ,HmaL ,L5W ,5W8L ,H5WL ,H8mL WM,m Mm&,W ,MZm Z&M", +",m ,WpM p&W,M ZMp, Zp&,M v,L -vL ,HvL gL-v ,5vL >L-v v,H5L -vg>L vM, -vM vM", +"Z, gM-v vMp, >M-v Zp*,M y-M U) U!) UD) Ua) U). U8) D)U. U)D8 UM) M)U! V) Va", +") Uj) U)8M Vj) Vs) U* U+* U*D Ua* U*. U8* U.D* D8U* U*M +MU* V* V+* U*j +jU", +"* V*j s*V UE) !EU) UH) HaU) E)U. 8EU) H)U. U)H8 MEU) U!ME) VH) HaV) jEU) U8", +"ME) HjV) VHs) Ud* +dU* UH* gU* d*U. 8dU* U.H* U8g* dMU* dM*U+ Vd* gV* djU* ", +"U+jd* H*Vj s*gV U)2 !)U2 D)U2 a)U2 U5) 58U) U)5D U)5a M)U2 U!M)2 V2) a)V2 U", +")5M U58M) V5) s)V5 U;* +;U* D;U* a;U* U5* >U* 5DU* Ua>* M;U* M;*U+ V;* a;V*", +" 5MU* U*>M V5* V>* E)U2 U!E)2 H)U2 UHa)2 5EU) U58E) U)H5 UH58) UME)2 ME)U!2", +" H)V2 V2Ha) U5ME) 5MEU8) H5V) VsH5) d;U* U+d;* H;U* U;g* 5dU* Ud>* H5U* >*g", +"U dM;U* U+dM;* H;V* g;V* U5dM* >MUd* H5V* V*g> U)N !)UN D)UN a)UN N.U) 8)UN", +" UD)N. U8D)N UQ) U)Q! VQ) QaV) QjU) U)Q8 QjV) VQs) U*N +*UN UND* UNa* *NU. ", +"UN8* U*DN. U8*DN UQ* +QU* V*Q +QV* Q*Uj Q8U* Q*Vj sQV* UW) W!U) U)WD U)Wa W", +")U. U)W8 UHW). UH8W) U)WM UWQ!) VZ) VaZ) U)Wj UW8Q) VjZ) s)VZ UW* +WU* H*UW", +" UWg* U.W* W8U* UH*W. gUW8* WMU* U+WQ* Z*V Z*gV W*Uj U+W*j ZjV* VZs* Um) m)", +"U! U)Dm U)am 5mU) U)8m U5Dm) U5am) U)Mm Mm)U! Vm) amV) pU) U8p) Vp) s)Vp Um", +"* +mU* DmU* amU* 5*Um Um>* U5*Dm >DmU* Q;U* Mm*U+ Vm* +mV* pU* >Up* Vp* s*V", +"p WmU) UWm!) U)Hm UHma) U)5W U5W8) UH5W) UH8m) Mm)UW UW)Mm! Z)Vm VZam) UWp)", +" pUW8) Z)Vp VpZs) vU* U+v* UHv* g*vU U5v* >*vU vUH5* gvU>* UQv* vU+Q* Vv* V", +"*gv vUp* >MvU* Z*Vp yV* ,U ,U& ,UD cU, ,U. ,U8 UD,. ,Uc8 ,UM U&,M V, Vc, ,U", +"j b8,M V,j s,V ,U* -U U*,D c-U ,.U* -U8 ,UD*. c8-U bM* -UM V*b V- U*,j -Uj ", +",jV* V-s ,UE ,EU& ,UH ,UcH UE,. ,EU8 ,.UH UH,8 ,EbM M&E,U V,H cHV, ,EUj ,U8", +"ME ,HVj sHV, ,Ud -Ud UH,d g-U ,.Ud U8-d ,UHd. g8-U bd,M bd-M Vd, V-g Ud,j U", +"d-j ,dVj s-gV ,U2 ,2U& UD,2 ,Uc2 ,U5 U5,8 U5,D ,Uc5 ,2bM M&2,U V,2 c2V, b5,", +"M ,U58M V5, V5s, ,U; -U; U;,D -Uc; U5,; ->U ,U5D; -Uc> bM,; bM-; V;, V-; U;", +",j -U>M ,5V; V-> UE,2 ,U&E2 ,2UH c2U,H ,EU5 ,U58E UH,5 c5U,H ,UME2 ,UEM&2 ,", +"HV2 VcH,2 ,U5ME M&E,U5 ,HV5 s,VH5 Ud,; Ud-; UH,; g;-U U5,d -d>U ,UH5d ->gU ", +",UdM; -UdM; ,HV; V;g- ,U5dM >M-bd ,5Vd g-V> ,UN ,NU& UD,N ,UcN UN,. ,NU8 ,U", +"DN. cU8,N ,UQ U&,Q V,Q V,cQ UQ,j UQ,8 ,QVj sQV, ,NU* -UN ,UD*N cN-U ,U*N. U", +"8-N U*N,D. c-U8N UQb* -UQ ,QV* V-Q ,UQ*j UQ-j V*bQj -QVs ,UW U&,W UH,W ,UcW", +" ,.UW UW,8 ,UHW. cWU,8 bW,M ,UWQ& Z,V VcZ, UW,j ,UWQ8 ZjV, VZs, UW,d -UW ,U", +"HW* -UcW ,UWd. UW-8 ,U.WD* c-WU8 UQ,d bW-M VdZ, Z-V ,UWdj UW-j Zb*Vj sZV- ,", +"Um U&,m Um,D ,Ucm U5,m U8,m ,U5Dm c5U,m bM,m Mm&,U Vm, V,cm pU, ,Up& Vp, s,", +"Vp Um,; -Um ,UmD; -Ucm ,U5m* >U-m U5*,Dm c>-Um UQ,; bM-m ,QV; V-m ,Up* -pU ", +"V*pb V-p UW,m ,UWm& UH,m cWU,m U5,W ,U5W8 ,UH5W c5W,U ,UWMm ,UWMm& VmZ, ZcV", +",m ,UpW pU&,W Z,Vp cpZV, vU, -vU ,UvD gU-v ,Uv5 v>-U vU,H5 cv->U ,UvM vb-M ", +"Vv, V-v vUp, pv-U V,pv yV- k) k)! k)B ak) 1k) 8k) k)1B 1)ak kM) M!k) Tk) T)", +"ak l) l8) lT) ls) k* +k* k*B ak* 1*k 8k* 1k*B ak1* k*M k*+M Tk* T*+k l* l+*", +" l*T ls* kE) !Ek) Hk) H)ak kE1) kE8) 1)Hk H)8k MEk) kME!) H)Tk ak)HM lE) 8E", +"l) lH) sHl) dk* +dk* Hk* gk* dk1* dk8* Hk1* 1*gk k*dM dkM+* TkH* Tkg* ld* 8", +"dl* lH* gl* k)2 !)k2 2Bk) k2a) 5k) 5)8k k)5B 5)ak M)k2 kM)!2 k)T2 ak)T2 l5)", +" 58l) 5Tl) s5l) k;* +;k* *Bk; k;a* 5k* >k* *B5k ak>* M;k* k;M+* k;T* ak;T* ", +"l;* l>* 5Tl* s>l* E)k2 kE)!2 k)H2 ak)H2 kE5) 5k8E) H)5k H5k8) kME)2 ME)k2! ", +"HkT)2 Hk)aM2 5El) l58E) H5l) lsH5) d;k* dk;+* k;H* k;g* dk5* dk>* 5kH* >*gk", +" dkM;* +kdM;* Hk;T* gkT;* 5dl* >*ld H5l* g>l* k)N !)kN )BkN k)aN k)1N kN8) ", +"1k)NB ak)1N Qk) k)Q! Q)Tk Q)ak lQ) Q8l) QTl) sQl) k*N +Nk* kN*B aNk* 1Nk* k", +"N8* k*N1B ak*1N Qk* Q*+k TkQ* akQ* l*Q +Ql* Q*lT lQs* Wk) k)W! H)Wk W)ak 1)", +"Wk W)8k 1HWk) 1Wak) W)Qk WkQ!) Zk) akZ) lW) W8l) lZ) l)sZ Wk* W*+k WkH* Wkg", +"* Wk1* 8kW* 1H*Wk g1Wk* QkW* +WkQ* Zk* Z*gk lW* +Wl* lZ* l*gZ km) k!m) m)kB", +" a)km 1)km 8)km km)1B akm1) Q)km km)Q! T)km akmQ) lp) p8l) pTl) l)sp km* k*", +"+m *Bkm kma* km1* km>* km*1B >kam* kmQ* km*+Q kmT* akmQ* l*p p>l* lTp* spl*", +" W)km km)W! H)km akmH) 5)Wk 5Wk8) 1Hmk) H8km) km)WM Wk)Mm! kmZ) Zkam) pWl) ", +"lWp8) l)Zp lZps) vk* +kv* Hkv* g*vk 1*vk >*vk v1Hk* >kgv* Qkv* vk+Q* vkZ* Z", +"vkg* lv* v>l* Zpl* yl* ,k ,k& ,kB ,ak ,1k :k, 1k,B ,a:k ,kM k&,M ,Tk 9a,M l", +", l:, l,T ls, ,k* -k *B,k -ka 1*,k :-k k*B,1 :a-k 9M* -kM Tk9* -Tk l*9 l- ,", +"Tl* l-s ,kE ,Ek& ,Hk Hk,a 1k,E ,k:E 1H,k ,H:k ,E9M k&E,M 9H,M ,HkT& l,E :,l", +"E lH, s,lH ,dk -kd Hk,d g-k 1d,k -k:d ,1Hdk :-gk 9d,M 9d-M HM9* -Tgk ld, l-", +"d ,Hld gl- ,k2 ,2k& k2,B ,2ak ,5k ,5:k ,B5k 5a,k ,29M k&2,M ,2Tk ,Tk&2 l,5 ", +":5l, ,5lT l5s, ,k; -k; ,Bk; ak-; 1;,k >k- ,1;kB -a>k 9M,; 9M-; Tk,; Tk-; l;", +", l-> ,Tl; s-l> kE,2 k&E,2 ,2Hk ,Hk&2 ,E5k :5k,E H5,k :H5,k ,kME2 ,kEM&2 ,H", +"kT2 ak2,HM ,5lE l:5,E ,Hl5 ls,H5 dk,; dk-; Hk,; g;-k 5d,k -d>k ,1Hk; g->k ,", +"dkM; -kdM; ,HkT; -Tkg; ,5ld ->ld ,Hl; g-l> ,kN ,Nk& kN,B ,Nak 1k,N ,k:N ,1k", +"NB :ak,N ,Qk Q&,k Qk,T Qa,k l,Q :Ql, ,QlT lQs, ,Nk* -kN k*N,B ak-N k*N,1 :N", +"-k ,1Nk*B :-akN Qk9* -Qk ,QkT* Qa-k ,Ql* l-Q l*9QT -Qls ,Wk W&,k Hk,W Wa,k ", +"1W,k ,W:k ,1HWk :HW,k 9W,M ,WkQ& Zk, ,aZk lW, :Wl, lZ, l,Z: Wk,d -Wk ,HkW* ", +"-Wgk ,1Wdk -k:W 1H*,Wk g:-Wk WM9* 9W-M ,dZk Z-k ,Wld l-W Z9l* l-Z ,km k&,m ", +",Bkm ak,m 1m,k ,k:m ,1mkB :ma,k 9M,m km&,Q Tk,m akm,Q l,p l,:p lTp, spl, km", +",; -km km*,B ak-m km*,1 -k:m kmB,1; :-mak Qk,; 9M-m km*,T Tk-m p9l* l-p l;,", +"pT ls-p Wk,m km&,W Hk,m akm,H 5W,k :W5,k ,1Hkm :Hm,k ,WkMm ,WkMm& ,kZm Zk&,", +"m p,lW :pl,W Zpl, lZ,:p vk, -vk ,Hvk gk-v ,1vk >k-v v1,Hk :v-gk ,Qvk v9-M v", +"kZ, Zk-v lv, l-v Zvl, yl- Uk) k)U! Dk) U)ak 1U) 1)U8 1)Dk 1)Ua kMU) U!kM) V", +"k) akV) lU) U8l) Vl) l)Vs U*k +kU* Dk* D*+k 1U* U+1* U*1D Ua1* UkM* k*MU+ V", +"*k +kV* l*U U+l* l*V Vls* kEU) U!kE) U)Hk ak)UH UE1) 1U8E) 1)UH 1UH8) UkME)", +" kMEU!) HkV) VHak) UEl) lU8E) VHl) lsVH) dkU* dk*U+ HkU* U*gk Ud1* 1U+d* UH", +"1* 1Ug* dkMU* U+dk*M HkV* V*gk Udl* l+Ud* Vdl* l*gV k)U2 U!k)2 k)D2 ak)U2 1", +")U5 1U58) 5)Dk 1Ua5) UkM)2 kM)U!2 k)V2 V2ak) U5l) lU58) l)V5 lsV5) k;U* k;*", +"U+ k;D* ak;U* U51* 1U>* Dk5* Dk>* k;MU* U+kM;* k;V* V+k;* U5l* >Ul* V5l* l*", +"V> UkE)2 kE)U!2 UHk)2 UH)ak2 1U5E) 5kEU8) 1UH5) ak)UH5 kMEU)2 !)k2UME V2Hk)", +" ak)V2H lU5E) U58lE) Vl5H) Vs5lH) dk;U* U+dk;* UH;k* gUk;* 1Ud5* >kUd* 1UH5", +"* >kgU* UdkM;* dkU;+M* Vdk;* gVk;* ldU5* l>Ud* lH;V* V>lg* k)UN U!k)N k)DN ", +"ak)UN U)1N 1U8)N 1UD)N 1Ua)N U)Qk UQk!) QkV) VQak) UQl) lUQ8) VQl) lsVQ) UN", +"k* k*NU+ DNk* ak*UN 1NU* 1U+*N 1U*DN 1Ua*N QkU* U+Qk* QkV* V+Qk* UQl* l+UQ*", +" lQV* s*VlQ U)Wk UWk!) W)Dk UWak) 1)UW 1UW8) 1UHW) 1UWa) UWQk) Wk)UQ! VkZ) ", +"ZkVa) UWl) lWU8) l)VZ lZVs) WkU* U+Wk* DkW* gUWk* UW1* 1U+W* 1UHW* g1UW* UW", +"*Qk +WkUQ* V*Zk gVZk* UWl* l+WU* VlZ* glZV* U)km km)U! D)km akmU) 1)Um 1Um8", +") 1UmD) 1Uam) km)UQ UQ)km! kmV) Vmak) l)pU pUl8) l)Vp Vpls) kmU* km*U+ kmD*", +" akmU* Um1* >kUm* km*1D >kDm* km*UQ U+Qkm* kmV* V+mk* pUl* l>pU* l*Vp V>lp*", +" km)UW UW)km! UHmk) ak)UHm 1UW5) km)UW8 1UHm) ak)U5W UWQkm) W)QkUm! ZkVm) V", +"maZk) lWpU) pU8lW) VplZ) lZsVp) U*vk vU+k* Dkv* gvUk* 1Uv* >kvU* v1UH* >kvD", +"* vUQk* U+Qvk* vkV* Vvgk* vUl* lv>U* l*Vv l*yV t, t&, tD, tc, t1, t:, ,1tD ", +"t,:c tM, ,Mt& Vt, V,tc lt, t:l, l,V u, t* t- t*D t-c t*1 t-: 1Dt* :ct- t*M ", +"t-M t*V V-t lt* l-t Vlt* u- t,E ,&tE tH, cHt, ,1tE :,tE ,1tH t,:H ,MtE tM&,", +"E V,tH tcV,H t,lE t:,lE tHl, uH, td* t-d tH* gt- ,1td :-td 1Ht* t-g: ,dtM -", +"Mtd Vdt* V-gt tdl* ldt- tHl* ug- t,2 ,&t2 ,Dt2 c2t, t5, :5t, ,5tD t,c5 ,Mt2", +" tM&,2 t,V2 tcV,2 t5l, t:5l, V5l, u5, t;* t-; ,Dt; c-t; t5* t>- 5Dt* c>t- ,", +"Mt; -Mt; V;t* t;V- t5l* t>l- V5t* u-> ,Et2 t&,E2 ,Ht2 tcH,2 ,5tE t:5,E ,Ht5", +" t:H,5 tM,E2 M&Et,2 tHV,2 Vc,tH2 t5,lE l:,t5E tH5l, ,Hu5 ,dt; -dt; ,Ht; t;g", +"- ,5td ->td H5t* g-t> tdM,; t-dM; tH;V* V-gt; t5dl* l-t>d tH5l* g-u> t,N ,&", +"tN ,DtN c,tN ,1tN :,tN t1D,N t:c,N tQ, ,Qt& V,tQ t,cQ tQl, t,:Q lQV, uQ, t*", +"N t-N D*tN tNc- 1*tN tN:- t*1DN t-:cN tQ* t-Q V*tQ tQV- tQl* tQl- tQ*Vl u-Q", +" tW, ,Wt& ,HtW t,cW ,1tW t,:W tH1,W t:H,W ,WtQ tWQ,& tZ, Z,tc tWl, t:Wl, l,", +"tZ uZ, tW* t-W H*tW tWg- 1Wt* :-tW tH*1W t-Wg: ,Qtd -WtQ tZ* tZ- tWl* lWt- ", +"l*tZ uZ- tm, ,mt& ,Dtm t,cm ,1tm t,:m t5D,m t:mc, ,Qtm tQm,& V,tm tcQ,m tp,", +" p,t: l,Vp up, tm* t-m Dmt* c-tm 1mt* :-tm t5*Dm t-mc> ,Qt; -Qtm Vmt* tmV- ", +"tp* tp- t*Vp up- ,Wtm tWm,& ,Htm tcW,m ,5tW t:W,5 tH5,W t:H,m tWQ,m Mm&tW, ", +"Z,tm tZc,m p,tW tp:,W Z,tp Zpu, tv* tv- vDt* t-cv v1t* t-:v tv1,H gtv:- vMt", +"* tQ-v t*Vv Z-tv l*tv tvl- l,Vv yu- F !F FB aF F. 8F .BF 8aF FK !KF TF TaF ", +"jF 8jF TjF sF (F +F (BF +aF (F. +8F (F.B 8a+F (FK +FK T(F +TF j(F +jF j(TF ", +"s+F G G! GH Ga G. G8 GH. G8H GK GK! GT GTa Gj G8j GTj sG Gd +G GHd gG Gd. +", +"G8 HdG. gG8 GdK +GK GTd gGT Gjd +Gj TdGj gsG F2 !F2 2BF aF2 5F 58F 5BF 5aF ", +"2KF !F2K TF2 T2aF 5jF 8j5F 5TF s5F ;F +;F ;FB a;F 5;F >F ;F5B >Fa ;FK ;F+K ", +"T;F +FT; ;jF >jF 5FT; s>F G2 G2! GH2 Ga2 G5 G58 G5H G5a G2K 2KG! GT2 TaG2 G", +"5j 58Gj G5T sG5 G; +G; G;H gG; G5d >G H5G; g>G G;K GK+; GT; GTg; G;j >Gj H;", +"Gj s>G FN !FN NBF aFN N.F 8FN FBN. aN8F QF Q!F QTF QaF QjF Q8F TjQF sQF (FN", +" +FN (FNB aF+N (FN. 8F+N .BN(F +8aFN Q(F +QF T(QF +FQa j(QF +FQ8 QTj(F +QsF", +" GW W!G GHW GaW GW. G8W HWG. W8Ga GQ GQ! ZG ZGa GQj GQ8 ZGj sZG GWd +GW HWG", +"d gGW WdG. G8+W GHWd. G8gW GQd +GQ ZGd gZG QdGj GQ+j GjZd sGgZ mF mF! mFB a", +"mF 5mF 8mF mF5B 5Fam QmF mFQ! TmF QFam pF p8F pTF spF m;F +mF ;FmB +Fam m;5", +"F >mF 5m;FB am>F Q;F +FQ; QFT; +FTm p;F p>F T;pF >Fsp Gm Gm! GmH Gam G5W G8", +"m H5Gm H8Gm GQm Q!Gm ZGm GaZm pG pG8 ZpG spG vG v+G vGH gvG vG5 v>G G5vH >G", +"gv vGQ +GvQ ZvG gGZv pvG p>G pGZv yG #F &F #BF a&F #F. 8&F #F.B a&8F #FK &F", +"K T#F T&F j#F &jF j#TF s&F #(F +&F #(FB a&+F (F#. 8&+F #BF(. +8a&F (F#K &F+", +"K #(TF +FT& #(jF +F&j Tj#(F +&sF G# G& GH# Ga& G#. G8& H#G. H8G& G#K G&K GT", +"# GT& Gj# G&j G#Hj sG& Gd# +G& HdG# gG& d#G. 8dG& GHd#. G8g& dKG# GKd& G#Td", +" GTg& G#dj G&+j dj#GT g&sG #F2 &F2 #F2B &2aF 5#F 5&F #B5F a&5F #F2K 2K&F #F", +"T2 &FT2 j#5F 5F&j T#5F 5&sF ;#F ;&F #B;F aF;& ;#5F >&F 5;#FB a&>F #F;K &F;K", +" ;#TF TF;& j#;F &j>F ;j#TF s&>F G#2 G&2 H#G2 G2H& G5# G5& G#H5 H5G& 2KG# &2", +"GK T#G2 G2T& 5jG# 5&Gj G#5T G5s& G;# G;& G#H; G;g& G#5d >G& G5Hd# gG>& ;#GK", +" GK;& G#T; T;G& G#;j G&>j G5Td# >&sG #FN &FN #FNB &FaN #FN. &F8N #FN.B 8a&F", +"N Q#F Q&F T#QF QFT& j#QF QF&j QTj#F Q&sF (F#N &F+N #FN(B +a&FN #FN(. +8&FN ", +"#BFN.( 8a&+FN #(QF +FQ& QT#(F +QT&F Qj#(F &jF+Q Tj#Q(F s+Q&F GW# G&W HWG# W", +"aG& W#G. W8G& GHW#. G8HW& GQ# GQ& ZG# Z&G G#Wj W&Gj GjZ# Z&sG WdG# G&+W GHW", +"d# G&gW GWd#. +GW8& HWdG#. gGW8& G#Qd G&+Q GdZ# gGZ& dj#GQ d&jGQ ZGdj# Z&Gg", +"s m#F m&F #BmF aFm& m#5F 5Fm& 5m#FB am&5F m#QF QFm& m#TF TFm& p#F p&F T#pF ", +"sFp& ;#mF +Fm& m;#FB am&+F 5m;#F m&>F m;#5BF >ma&F ;#QF QF;& Q;T#F +Tm&F ;#", +"pF >Fp& pT;#F p>&sF Gm# Gm& G#Hm HmG& G#5W 5WG& G5HW# G5aW& QmG# Q&Gm GmZ# ", +"GmZ& pG# pG& Z#pG pGZ& vG# v&G GHv# gGv& G5v# >Gv& vGH5# gv>G& GQv# GQv& vG", +"Z# ZGv& vGp# v&pG ZpvG# y&G I I! DI aI I. 8I DI. D8I IK IK! TI TIa Ij 8Ij D", +"jI sI I( +I DI( +DI I(. +I8 I(D. D8+I I(K +IK TI( +TI Ij( +Ij D(Ij sI+ GI G", +"I! J Ja GI. G8I J. J8 GIK IKG! JT JTa GjI 8IGj Jj sJ Id +GI Jd gJ Id. 8Id J", +"d. gJ8 IdK GK+I JTd gJT Idj Gj+I Jjd sJg I2 I2! DI2 aI2 5I 5I8 5DI 5aI I2K ", +"2KI! TI2 I2Ta 5Ij 58Ij 5TI sI5 I; +I; D;I aI; 5I; >I D;5I >DI I;K +KI; TI; ", +"T;+I I;j >Ij T;5I s>I GI2 I2G! J2 Ja2 G5I 5IG8 J5 J58 I2GK GI2!K JT2 TaJ2 5", +"IGj G58Ij J5T sJ5 G;I G;+I J; gJ; 5Id >GI J5d J> GKI; IdK+; J;T gTJ; I;Gj G", +"j>I J;j J>s IN IN! DIN aIN IN. 8IN IND. DN8I QI QI! QDI QaI QIj Q8I DjQI sI", +"Q I(N +IN I(DN +NaI N.I( +N8I DI(N. +D8IN QI( +QI Q(TI QD+I Q(Ij Q8+I Ij(QD", +" +QsI WI WI! JW JWa WI. W8I JW. J8W GQI Q!WI ZJ ZJa WIj Q8WI ZJj sJZ WId +W", +"I JWd gJW W.Id W8+I WdJ. gWJ8 QId GQ+I ZJd gZJ IdWj WI+j ZdJj gZsJ Im Im! D", +"mI aIm 5Im 8Im Dm5I 8IDm QIm Q!Im TIm aITm pI pI8 pDI spI Im; +Im ImD; Dm+I", +" 5mI; >Im Im;5D Dm>I QI; QI+m D;QI TI+m pI; p>I D;pI p>sI GmI W!Im Jm Jma 5", +"WI W85I J5W J8m QIGm Im!GQ ZJm ZaJm pGI G8pI Jp Jps vI vI+ Jv Jvg vI5 v>I J", +"v5 J>v vIQ +QvI JvZ gZJv pvI v>pI Jpv yJ bI I& bDI cI bI. b8I DIb. c8I bKI ", +"I&K bTI cTI bjI I&j TIbj scI bI( b+I DIb( c+I I(b. +Ib8 bDI(. +Ic8 I(bK bK+", +"I b(TI +TcI b(Ij +Ibj Ij(bT c+sI bG bG& Jb cJ bG. bG8 Jb. cJ8 bGK bKG& JbT ", +"cJT bGj G8bj Jjb sJc bGd b+G Jbd gJc b.Id +Gb8 bdJ. J8gc bKId bK+G bTJd gTc", +"J Gjbd +Gbj bdJj gcsJ bI2 I&2 DIb2 c2I b5I 5I& bD5I c5I I2bK &2IK b2TI TIc2", +" 5Ibj Ij5& 5IbT c5sI b;I I;& bDI; c;I 5Ib; >bI b5DI; c>I bKI; IK;& TIb; TIc", +"; I;bj bj>I I;jbT sIc> bG2 b2G& Jb2 cJ2 bG5 G5b8 J5b cJ5 G2bK I&2GK bTJ2 JT", +"c2 G5bj I&jG5 b5Jj c5sJ bG; +Gb; J;b cJ; G5bd >bG b5J; J>c bKG; I;&GK bTJ; ", +"cTJ; G;bj bG>j b;Jj c>sJ bIN I&N DIbN cIN INb. bN8I bDIN. 8IcN bQI QI& QIbT", +" cQI QIbj QIb8 bQDIj cQsI I(bN bN+I bDI(N +IcN bI(N. I&N+8 DI(bN. c+8IN b(Q", +"I +IbQ bQDI( +QcI Ij(bQ I&j+Q bQDIj( sI+cQ bGW WI& JbW cJW b.WI G8bW bWJ. J", +"8cW bGQ G&bQ ZJb ZcJ GQbj GQb8 JjZb ZcsJ WIbd +GbW bWJd cWgJ Id.bW b+GW8 Jb", +"Wd. cJWg8 GQbd +GbQ JbZd gJZc IdjbW IdjW& ZJbdj sJZgc bmI Im& bDIm cmI 5Ibm", +" 8Ibm b5DIm 5Icm QIbm ImQ& TIbm QIcm pbI pI& bTpI cpI Imb; +Ibm Im;bD +Icm ", +"Im;b5 bm>I b5DIm; >Icm QIb; I;Q& bQ;TI QIc; b;pI >bpI pbDI; pIc> bGm G&bm J", +"mb cJm G5bW G8bm b5Jm J5cW GQbm Im&GQ JmZb cmZJ pbG bGp& Jpb cpJ vbG vI& Jv", +"b cvJ bGv5 >bvI vbJ5 J>cv bGvQ QIv& ZbJv JvZc vbpG vIp& pbJv yJc 0F !0F 0BF", +" a0F 1F 18F 1FB 1aF 0FK 0F!K T0F a0TF 1jF 8j1F 1TF s1F 0(F +0F 0(FB a0+F 1(", +"F 1+F (B1F +a1F (F0K 0F+K 0(TF T0+F j(1F 1F+j T(1F 1+sF G0 G0! GH0 Ga0 1G 1", +"G8 1GH 1Ga G0K !KG0 GT0 TaG0 1Gj 18Gj 1GT sG1 Gd0 +G0 HdG0 gG0 1Gd 1+G GH1d", +" g1G dKG0 GK+0 G0Td GTg0 Gj1d +j1G GT1d sGg1 0F2 0F!2 0F2B 0Fa2 15F 581F 5B", +"1F 1F5a 0F2K 2K0!F 0FT2 Ta0F2 5j1F 158jF 1F5T 15sF ;0F ;0+F 0B;F ;0aF 1;F >", +"F1 ;F1B 1a>F 0F;K +;0FK ;0TF a;0TF 1F;j 1j>F 1FT; s1>F G02 !0G2 H0G2 a0G2 1", +"G5 15G8 G51H G51a 2KG0 G02!K T0G2 GTa02 15Gj 1G58j G51T 1Gs5 G;0 +0G; G0H; ", +"G;g0 1G; >G1 G;1H >Gg1 ;0GK +G;0K G0T; gGT;0 Gj1; 1G>j GT1; s1>G 0FN 0F!N 0", +"FNB 0FaN 1FN 8F1N NB1F aF1N Q0F !0QF T0QF a0QF 1QF 1FQ8 QT1F 1QsF (F0N 0F+N", +" 0B(FN +a0FN (F1N +F1N 1(FNB 1+aFN 0(QF Q0+F QT0(F +QT0F Q(1F 1F+Q 1QT(F s1", +"+QF GW0 G0W! HWG0 G0Wa 1GW G81W GH1W Ga1W GQ0 G0Q! ZG0 GaZ0 1GQ G81Q Z1G Z1", +"sG WdG0 GW+0 GHWd0 GWg0 GW1d +G1W 1GHWd 1GgW G0Qd +0GQ GdZ0 ZGg0 GQ1d +G1Q ", +"1GZd gGZ1 m0F !0mF 0BmF m0aF 1mF 1F8m mF1B 1Fam m0QF Q!m0F m0TF am0QF p1F 1", +"8pF 1TpF p1sF ;0mF m0+F m;0FB am0+F m;1F 1m>F 1m;FB >m1aF ;0QF +Qm0F Q;T0F ", +"+Tm0F 1;pF p1>F p1T;F p>1sF Gm0 m0G! G0Hm G0am 1Gm G81m Gm1H Ga1m QmG0 GQm!", +"0 GmZ0 ZGam0 pG1 1Gp8 pGZ1 s1pG vG0 +Gv0 GHv0 vGg0 v1G >Gv1 1GvH gGv1 GQv0 ", +"v+GQ0 vGZ0 ZvGg0 v1pG p1>G ZGv1 y1G 9F 9&F 9FB 9aF 91F :F 1B9F :aF 9FK &F9K", +" 9TF 9FT& 9jF :jF 9F1T s:F 9(F 9+F (B9F +a9F 1(9F :+F 91(FB +a:F (F9K +F9K ", +"T(9F 9F+T j(9F +j:F 9T1(F :+sF 9G 9G& 9GH 9Ga 9G1 :G 1G9H :GH 9GK 9KG& 9GT ", +"GT9a 9Gj :Gj 1G9T s:G 9Gd 9+G GH9d g9G 1G9d :G+ 9G1Hd g:G Gd9K 9K+G GT9d 9G", +"gT Gj9d +G:j 9GT1d sGg: 9F2 &F92 2B9F aF92 95F :5F 5B9F 5a:F 2K9F 9&F2K TF9", +"2 9TaF2 5j9F 5j:F 9F5T :5sF 9;F 9F;& ;F9B 9Fa; 9F1; :>F 95;FB :a>F ;F9K ;&F", +"9K 9FT; 9+T;F 9F;j :j>F 95T;F >Fs: 9G2 92G& GH92 92Ga 9G5 :G5 G59H G5:H G29", +"K 9G&2K 92GT 9GT&2 G59j G5:j G59T s5:G 9G; +G9; G;9H 9Gg; 1G9; :>G 9G5H; :G", +"g> 9KG; 9+G;K GT9; g9GT; G;9j >j:G 9G5Td :>sG 9FN &F9N NB9F aF9N 1F9N :FN 9", +"1FNB aF:N 9QF 9FQ& QT9F 9FQa 9F1Q :QF 9Q1TF sF:Q (F9N +F9N 9(FNB 9+aFN 91(F", +"N +F:N 1(F9NB :+aFN Q(9F 9F+Q 9QT(F 9+QTF 9Q1(F +Q:F 1Q(9TF :Q+sF 9GW G&9W ", +"GH9W Ga9W 1G9W :GW 9G1HW Ga:W 9GQ G&9Q Z9G 9GZ& 1G9Q :GQ 9GZ1 Z:G GW9d +G9W", +" 9GHWd 9GgW 9G1Wd +G:W 1GH9Wd gG:W GQ9d +G9Q 9GZd gGZ9 9GQ1d +G:Q Z91Gd :Gg", +"Z 9mF 9Fm& mF9B 9Fam 9F1m :mF 95mFB am:F Qm9F m&F9Q 9FTm 9QamF p9F :pF 9TpF", +" sF:p m;9F 9F+m 9m;FB 9+maF 95;mF >F:m 1mF9;B :>amF 9FQ; 9+QmF 9Q;TF 9+TmF ", +"9;pF >F:p p9T;F :ps>F 9Gm G&9m Gm9H Ga9m 1G9m :Gm 9G5Hm Ga:m GQ9m 9GQm& 9GZ", +"m Z9Gam p9G :pG pGZ9 sG:p v9G 9+vG 9GvH gGv9 9Gv1 :vG v91GH :Ggv 9GvQ v9+GQ", +" ZGv9 Zv9gG v9pG pv:G Zp9vG y:G I0 I0! DI0 aI0 1I 1I8 1DI 1aI I0K !KI0 TI0 ", +"T0aI 1Ij 18Ij 1TI sI1 I0( +I0 I0D( +0aI 1I( 1+I DI1( +D1I 0(IK I0+K I0T( +0", +"TI 1(Ij +j1I 1(TI 1+sI GI0 I0G! J0 Ja0 1GI G81I J1 J18 I0GK GI0!K JT0 TaJ0 ", +"Gj1I 1G8Ij J1T sJ1 Id0 +0Id Jd0 gJ0 1Id +G1I J1d gJ1 I0dK Id0+K TdJ0 JTg0 I", +"j1d Idj1+ 1TJd g1sJ I02 !0I2 I2D0 I2a0 1I5 158I 5D1I 5a1I 2KI0 I02!K I2T0 T", +"Ia02 15Ij 1I58j 5T1I 1Is5 I;0 +0I; D0I; a0I; 1I; >I1 D;1I 1D>I ;0IK I;0+K T", +"0I; +TI;0 Ij1; 1I>j TI1; s1>I I0G2 GI0!2 J02 a0J2 G51I 1G58I J15 1aJ5 GI02K", +" I02GK! T0J2 JTa02 1G5Ij G581Ij 1TJ5 s1J5 G0I; Id0+; J;0 g0J; G;1I 1G>I J1;", +" J>1 Id0;K +G0I;K T;J0 gJT;0 Idj1; >I1Gj 1TJ; J1s> I0N !0IN I0DN I0aN 1IN 1", +"N8I DI1N 1NaI QI0 I0Q! Q0TI Q0aI 1QI Q81I QD1I 1QsI 0(IN I0+N DI0(N +DI0N I", +"(1N 1N+I 1DI(N 1+DIN I0Q( +0QI QDI0( +QDI0 1(QI +Q1I 1QDI( sI1+Q WI0 I0W! J", +"W0 WaJ0 1WI W81I J1W 1WJ8 G0QI GQI!0 ZJ0 JaZ0 GQ1I 1GQ8I ZJ1 Z1sJ W0Id +0WI", +" WdJ0 JWg0 WI1d +W1I 1WJd gWJ1 Q0Id +GQI0 JdZ0 g0ZJ QI1d 1+GQI ZdJ1 ZJg1 Im", +"0 I!m0 D0Im a0Im 1Im 8I1m Dm1I aI1m Q0Im Im0Q! T0Im QaIm0 pI1 1Ip8 1DpI s1p", +"I m;I0 +0Im Im;D0 +DmI0 Im1; 1I>m 1DmI; >Dm1I Q0I; +QIm0 QD;I0 +TIm0 1Ip; p", +"1>I pD1I; p>D1I G0Im Im0W! Jm0 amJ0 Gm1I 1Gm8I J1m 1aJm Im0GQ GQ0Im! Z0Jm Z", +"Jam0 1GpI pG81I Jp1 J1sp vI0 +Iv0 Jv0 g0Jv v1I >Iv1 Jv1 g1Jv QIv0 vI+Q0 Z0J", +"v JvZg0 v1pI pvI>1 Z1Jv yJ1 9I 9I& 9DI c9I b1I :I 1D9I :cI 9IK 9KI& 9TI 9Tc", +"I 9Ij :Ij 1T9I s:I 9I( 9+I DI9( 9+cI b(1I :I+ b1DI( c+:I I(9K 9K+I 9(TI +T9", +"I 9(Ij +I:j b1TI( s+:I bG9 G&9I J9 cJ9 b1G :Gb J91 :J bK9G I&K9G J9T cTJ9 9", +"Gbj bG:j J9j :Js 9Id 9+bG J9d gJ9 1Gbd :Id b1Jd :Jg 9KId b+G9K 9TJd gTJ9 Id", +"9j Id:j 9dJj sJg: 9I2 92I& DI92 9Ic2 95I :I5 5D9I c5:I I29K I&29K 92TI c29T", +"I 5I9j 5I:j 5T9I s5:I 9I; +I9; D;9I 9Ic; 1Ib; :>I b1;DI :Ic> 9KI; I;&9K TI9", +"; c;9TI I;9j >j:I I;j9D :>sI b29G I&29G J92 c2J9 9Gb5 bG:5 J95 :J5 bG92K 9G", +"2I&K 9TJ2 cJ9T2 b1G5j :Gb5j 95Jj J5s: 9Gb; I;&9G J9; J9c; 1Gb; >b:G b1J; :J", +"> bG;9K I;K9+G 9TJ; cJ;9T Idj95 :>Gbj 9;Jj s:J> 9IN 9NI& DI9N 9IcN bN1I :IN", +" b1DIN cN:I 9QI Q&9I QD9I 9QcI 1Q9I :QI b1QTI :QsI I(9N 9N+I 9DI(N c9+IN b1", +"I(N +I:N 1D(9IN :c+IN 9(QI +Q9I 9QDI( cQ9+I b1QI( +Q:I Ij(9QD :cQ+I 9WI W&9", +"I J9W J9cW 1GbW :WI b1JW :JW 9GbQ bGQ9& ZJ9 cJZ9 1GbQ bG:Q J9Z1 Z:J WI9d +W", +"9I 9WJd gWJ9 b1GWd +W:I J91Wd :WgJ QI9d b+G9Q ZdJ9 ZJg9 Idj9W QI:d ZJ91d gZ", +":J 9Im I&9m Dm9I 9Icm 1Ibm :Im b1mDI cm:I QI9m Im&9Q TI9m cQ9Im p9I :pI 9Dp", +"I sI:p Im9; +I9m 9DmI; cm9+I b1mI; >I:m 1Dm9I; c>:Im QI9; I;&9Q 9QDI; cQ;9I", +" 9Ip; p>:I p9DI; cp:>I 9Gbm Im&9G J9m J9cm 1Gbm bG:m b1Jm :Jm bGQ9m 9GQIm& ", +"JmZ9 ZJ9cm b1pG pb:G Jp9 :Jp v9I 9+vI Jv9 g9Jv b1vI :vI vbJ1 :Jv 9QvI v9I+Q", +" Z9Jv cvZJ9 v9pI pv:I pvJ9 yJ: FL !FL LBF aFL FL. 8FL FL.B aF8L MF MF! TMF ", +"aMF MjF 8MF MjTF sMF (FL +FL (FLB aF+L FL(. 8F+L (BLF. +8aFL M(F +MF M(TF +", +"FaM j(MF +F8M TMj(F +MsF GL GL! GHL GaL GL. G8L HLG. GLH8 GM GM! GTM GaM GM", +"j G8M HMGj sGM GdL +GL HdGL gGL dLG. +LG8 GHdL. G8gL GMd +GM TdGM gMG dMGj ", +"G8+M dMjGT sGgM FL2 FL!2 FL2B FLa2 5FL 8F5L LB5F aF5L MF2 !FM2 MFT2 M2aF 5M", +"F 5F8M TM5F 5MsF ;FL ;F+L LB;F ;FaL ;F5L >FL 5;FLB aF>L M;F +FM; TFM; aFM; ", +"5FM; >MF M;F5T sM>F GL2 !LG2 HLG2 aLG2 G5L 58GL GLH5 GL5a GM2 M2G! G2HM G2a", +"M G5M 5MG8 H5GM G5sM G;L +LG; GLH; G;gL GL5d >GL G5HdL gG>L GM; G;+M HMG; G", +"Mg; 5dGM >MG G5TdM sG>M FLN FL!N FLNB FLaN FLN. FL8N .BLFN 8aFLN QMF MFQ! T", +"MQF QFaM MjQF QF8M QTMjF QMsF FL(N FL+N (BLFN +aFLN N.(FL +8FLN (BFN.L 8aF+", +"LN M(QF QM+F QTM(F aMF+Q QMj(F +Q8MF TMjQ(F s+QMF GWL GLW! HWGL GLWa WLG. G", +"LW8 GHWL. G8HWL GQM Q!GM ZGM GaZM WMGj W8GM GMZj ZGsM WdGL GW+L GHWdL GWgL ", +"GWdL. +GW8L HWdGL. gGW8L QdGM GQ+M GMZd ZGgM dMjGQ +GQ8M ZGdMj gZsGM mFL !F", +"mL LBmF mFaL mF5L mF8L 5mFLB amF5L MmF M!mF TFMm aFMm pMF 8MpF TMpF pMsF ;F", +"mL mF+L m;FLB amF+L 5m;FL mF>L m;F5BL >maFL QFM; +FMm MmFT; aMm+F M;pF pM>F", +" pTM;F >MpsF GmL mLG! GLHm GLam GL5W GL8m G5HWL G5aWL GMm G!Mm GMZm aMGm pG", +"M G8pM ZMpG sMpG vGL +GvL GHvL vGgL G5vL vG>L vGH5L gv>GL vMG +GvM ZGvM gGv", +"M vMpG pG>M ZpvGM yGM #FL &FL #FLB &FaL FL#. &F8L #L.FB 8a&FL M#F M&F M#TF ", +"TFM& j#MF 8FM& TMj#F M&sF (F#L &F+L (BL#F +a&FL #L.(F +8&FL (BF#L. 8a&+FL #", +"(MF +FM& TM#(F aMF+& Mj#(F M&F+j Mj#T(F s+M&F G#L G&L H#GL GLH& #LG. 8&GL G", +"H#L. G8H&L GM# GM& G#HM HMG& MjG# 8MG& GTMj# GMs& d#GL +LG& GHd#L G&gL Gd#L", +". d&LG8 Hd#GL. gG8&L G#dM G&+M dM#GT GMg& dMjG# dM&G8 GTMdj# gsGM& FL#2 FL&", +"2 #L2FB a&FL2 #F5L &F5L 5B#FL 5a&FL #FM2 &FM2 TM#F2 aMF&2 M#5F 5FM& 5TM#F s", +"5M&F #F;L &F;L ;#FLB a;&FL 5;#FL &F>L ;#F5BL >&aFL ;#MF MF;& M;#TF aM;&F M;", +"#5F M&>F 5T#M;F >Ms&F #LG2 &LG2 GH#L2 Ga&L2 5#GL GL5& G5H#L G5a&L M#G2 G2M&", +" GTM#2 GT&M2 G#5M 5MG& G5TM# sG5M& ;#GL GL;& G;H#L gG;&L G5d#L G&>L H5#G;L ", +"g>G&L G#M; M;G& GT;M# gMG;& G5Md# GM>& dM#G5T >MgG& FL#N FL&N #LNFB a&FLN #", +"LNF. 8&FLN .BL#FN a&F8LN M#QF QFM& QTM#F aMFQ& QMj#F M&FQ8 TMjQ#F sQM&F #LN", +"(F +&FLN #FN(BL a&F+LN #(FN.L 8&F+LN (FLBN.# 8a+&FLN QM#(F M&F+Q TM#Q(F +QT", +"M&F Mj#Q(F +Q8M&F #(TFQMj M&Fs+Q W#GL GLW& GHW#L GaW&L GW#L. G8W&L HW#GL. H", +"8WG&L G#WM WMG& GMZ# GMZ& GQMj# GQ8M& ZGMj# Z&GsM GWd#L d&LGW HWdG#L gGW&L ", +"Wd#GL. G8Wd&L WdG#HL. G8Wg&L dM#GQ dM&GQ ZGdM# Z&GgM GQMdj# GQ8dM& dMjZG# g", +"sGZ&M #FmL &FmL m#FLB am&FL 5m#FL m&F5L m#F5BL 5aFm&L m#MF MFm& Mm#TF aMm&F", +" M#pF M&pF pTM#F p&MsF m;#FL m&F+L ;#FmLB m&Fa;L m;#5FL >m&FL LBmF5;# am&>F", +"L Mm#;F Mm&+F Tm#M;F aMFQ;& pM;#F >Mp&F M;#pTF p&F>Ms m#GL GLm& GmH#L Gam&L", +" G5W#L G5&WL H5#GmL amLG5& G#Mm MmG& ZGMm# Z&GMm GMp# GMp& ZpGM# Zp&GM G#vL", +" G&vL vGH#L gvG&L vG5#L v>G&L G5Hv#L v&Gg>L GMv# GMv& ZvGM# Zv&GM pvGM# >Mv", +"G& pGMZv# GMy& UI U!I UDI UaI UI. U8I DIU. 8IUa IM IM! VI VaI UjI 8IM VIj s", +"IV UI( U+I DIU( +IUa I(U. +IU8 UDI(. U+D8I IM( +IM VI( V+I U(Ij +IUj IjV( V", +"+sI UG UG! JU JUa UG. UG8 JU. J8U UGM U!GM VJ VJa UGj G8Uj JjV sJV UGd U+G ", +"JUd gJU U.Id +GU8 UdJ. J8gU IdM +MUG VJd gVJ GjUd +GUj VdJj sJgV UI2 I2U! D", +"IU2 U2aI U5I 5IU8 UD5I 5IUa IM2 I!M2 V2I aIV2 5IM 8I5M V5I V5sI U;I +IU; UD", +"I; aIU; 5IU; >UI U5DI; Ua>I IM; I;+M V;I +IV; I;Uj >MI 5IV; V>I UG2 G2U! JU", +"2 UaJ2 UG5 G5U8 J5U U5J8 U2GM IM2G! VJ2 JaV2 G5Uj UG58M J5V VsJ5 UG; +GU; J", +";U J;gU G5Ud >UG U5J; J>U GMU; IdM+; J;V J;gV G;Uj UG>M V5J; J>V UIN INU! D", +"IUN UNaI INU. UN8I UDIN. U8IDN UQI Q!IM VQI QaVI QIUj QIU8 QIVj VQsI I(UN U", +"N+I UDI(N U+DIN UI(N. U+8IN DI(UN. +DIU8N U(QI +IUQ QIV( +QVI IM(Qj U+Q8I V", +"QIj( sIV+Q UGW U!WI JUW UWJa U.WI G8UW UWJ. UWJ8 UGQ Q!UG ZJV ZaVJ GQUj GQU", +"8 VZJj VZsJ WIUd +GUW UWJd JUgW Id.UW U+GW8 JUWd. gJUW8 GQUd +GUQ VdZJ ZJgV", +" IdMWj IdMW8 ZJVdj gVZsJ UmI U!Im UDIm aIUm 5IUm 8IUm U5DIm U5aIm IMm I!Mm ", +"VmI aIVm pUI U8pI VpI sIVp ImU; +IUm Im;UD U+maI Im;U5 Um>I U5DIm; >DmUI QI", +"U; IM+m QIV; +IVm U;pI pI>M pIV; pIV> UGm U!Gm JmU UaJm G5UW G8Um U5Jm U8Jm", +" GQUm IMmW! JmV JaVm pUG UGp8 JpV VpsJ vUG U+vI JvU gUJv UGv5 >UvI vUJ5 vUJ", +"> vIM +IvM VvJ JvgV vUpG vI>M VvJp yJV bUI U&I UDbI cUI UIb. 8IU& bUDI. U8c", +"I bMI IM& VbI VcI IMbj 8IbM bjVI VcsI UIb( +IU& bUDI( U+cI bUI(. b+U8I UDIb", +"(. cU+8I b(IM +IbM bIV( cIV+ IM(bj IM&+j VbIj( sIVc+ bGU UG& JbU cJU b.UG U", +"Gb8 bUJ. cUJ8 bGM GMU& VJb cJV UGbj G8bM VbJj VJsc UGbd U+bG bdJU cJgU Id.b", +"U b+GU8 JbUd. cJUg8 GMbd +GbM JbVd cJgV IdMbj IdMb8 JjVbd gVJsc UIb2 U2I& b", +"UDI2 UIc2 bU5I 5IU& b5UDI U5cI b2IM I2M& bIV2 cIV2 5IbM IM5& b5VI cIV5 bUI;", +" I;U& b;UDI U;cI b5UI; U&>I U5Db;I cU>I IMb; M;I& b;VI cIV; IM;b5 bM>I V5bI", +"; >IVc b2UG U2G& bUJ2 JUc2 UGb5 G5U& b5JU cUJ5 b2GM IM&G2 JbV2 V2cJ G5bM IM", +"&G5 JbV5 VJc5 UGb; G;U& b;JU cUJ; bG5Ud bG>U J5bUd >UcJ GMb; IdM;& JbV; VJc", +"; IdMb5 bG>M J5Vbd cJV> UIbN UNI& bUDIN UIcN bUIN. I&NU8 UDIbN. cU8IN QIbM ", +"QIU& bQVI VQcI bQUIj IM&Q8 VbQIj sIVcQ bUI(N I&NU+ UDIb(N cU+IN UI(bN. U+8I", +"&N UDbIN.( U+8cIN IM(bQ IM&+Q VbQI( cQV+I bQUIj( U+QI&j Ij(VbQ cQVsI+ UGbW ", +"G&UW bWJU JUcW bGUW. bG8UW JbUW. cJUW8 UGbQ GQU& VJZb VZcJ bGQUj IM&W8 ZJVb", +"j sJZVc bGUWd b+GUW JbUWd cJUgW bWUId. U+GbW8 bWUJd. gJUcW8 IdMbW IdMW& ZJV", +"bd gVZcJ bWMIdj bW8IdM JjVZbd sJZgVc bUIm ImU& bmUDI UmcI b5UIm Im&U5 U5Dbm", +"I c5UIm IMbm MmI& bmVI cIVm bMpI U&pI VbpI pIVc Im;bU Im&U+ UmDb;I cmU+I b5", +"UIm; >ImU& Imb;U5D c>UIm IMmb; IMmb+ VmbI; cQ;VI pbUI; >MbpI VpbI; cpV>I UG", +"bm GmU& bmJU cUJm bG5UW bG8Um J5bUW cJ5UW GMbm IMmG& JbVm VJcm bGpU UGp& pb", +"VJ cJVp bGvU UGv& JbvU vUcJ vbUG5 v>bUG JvbU5 cvJ>U bGvM IMv& vbVJ cJVv pvb", +"UG >MvbG JpVvb cJyV kF kF! kFB akF 1kF 8kF kF1B 1Fak kMF M!kF TkF TFak lF l", +"8F lTF lsF k(F +kF (BkF +Fak k(1F 1F+k 1k(FB akF1+ M(kF kM+F k(TF +FTk l(F ", +"l+F T(lF sFl+ Gk Gk! GkH Gak 1Gk G8k Gk1H Gk1a GkM kMG! GTk TkGa lG lG8 lGH", +" lsG Gkd +Gk GHdk gkG Gk1d +k1G 1GHdk 1Ggk dkGM Gk+M TdGk GTgk lGd l+G GTld", +" glG kF2 !Fk2 2BkF k2aF 5kF 5F8k kF5B 5Fak MFk2 kMF!2 kFT2 akFT2 l5F 58lF 5", +"TlF sFl5 k;F +Fk; ;FkB aFk; 1Fk; >kF k;F1B ak>F kFM; k;M+F TFk; ak;TF l;F l", +">F T;lF >Fls Gk2 k2G! G2Hk G2ak G5k 5kG8 H5Gk 5aGk kMG2 GkM!2 G2Tk GTka2 lG", +"5 G5l8 G5lH l5sG Gk; G;+k HkG; Gkg; Gk1; >kG 1G;Hk >Ggk k;GM +GkM; TkG; gkG", +"T; lG; l>G GTl; g>lG kFN !FkN NBkF kFaN kF1N kN8F 1kFNB akF1N QkF kFQ! QFTk", +" QFak lQF Q8lF QTlF sFlQ (FkN kF+N k(FNB akF+N 1k(FN 1+kFN k(F1NB 1+FakN k(", +"QF +FQk QkT(F +QkTF Q(lF +QlF lQT(F ls+QF GkW W!Gk GHWk WaGk Gk1W W8Gk 1GHW", +"k 1GaWk GQk Q!Gk ZkG GaZk lGW GQl8 lZG sZlG GWdk Gk+W GkHWd GkgW 1GWdk 1+GW", +"k GkH1Wd g1GWk QdGk GQ+k GkZd gGZk GQld +GlW ZGld lGgZ kmF k!mF mFkB aFkm 1", +"Fkm 8Fkm kmF1B akm1F QFkm kmFQ! TFkm akmQF lpF p8lF pTlF sFlp m;kF +Fkm km;", +"FB akm+F km;1F km>F 1mFk;B >kamF QFk; +QkmF Qk;TF ak;QF p;lF >Flp l;pTF l>p", +"sF Gkm G!km HkGm akGm Gk1m 8kGm 1GmHk akm1G QkGm GQkm! GkZm ZkGam lGp l8pG ", +"ZplG splG vkG +Gvk GkvH gGvk 1Gvk >Gvk v1GHk >kgvG GQvk vk+GQ ZGvk ZvkgG lv", +"G p>lG ZvlG ylG 9kF k&F kF9B 9Fak 1k9F :kF 91kFB ak:F 9MF 9FM& 9FTk 9FaM l9", +"F l:F 9TlF sFl: k(9F 9F+k 9k(FB akF9+ 91k(F +k:F 1k(9FB :+akF M(9F 9F+M 9Tk", +"(F 9+TkF 9(lF :+lF l9T(F ls:+F 9Gk Gk& Gk9H Gk9a 91Gk :Gk 9G1Hk Ga:k 9GM G&", +"9M GT9M Ga9M lG9 l:G 9GlH s:lG Gk9d +k9G 9GHdk 9Ggk 9G1dk +G:k 1GH9dk :Ggk ", +"GM9d +G9M 9GTdk 9GgM 9Gld :Gl+ lG9Td lGg: kF92 &Fk2 9kF2B akF92 9F5k 5k:F 9", +"5kFB :5akF MF92 k&FM2 9TkF2 9aMF2 95lF :5lF l95TF ls:5F 9Fk; kF;& k;F9B ak;", +"9F k;F95 :k>F 95Fk;B :>akF 9FM; k;&MF 9T;kF aM;9F 9;lF >Fl: l;9TF l>s:F 92G", +"k G2k& 9GHk2 9Gak2 Gk95 G5:k 9G5Hk :GH5k 92GM 9GM&2 9GTk2 9GaM2 9Gl5 l5:G l", +"G9H5 lsG:5 Gk9; k;G& 9G;Hk g9Gk; 9G5dk :G>k G5k9H; g:>Gk GM9; k;&GM 9GTk; g", +"9MG; 9Gl; :>lG lG;9H l>Gg: kF9N &FkN 9kFNB akF9N 91kFN kF:N 1kF9NB :akFN 9F", +"Qk QFk& 9QTkF 9QakF 9QlF lF:Q l9QTF :QFls 9k(FN k&F+N k(F9NB 9+FakN 1k(9FN ", +":+kFN 1k9(NBF akF:+N 9Qk(F 9+QkF Qk(9TF akF9+Q l9Q(F :Q+lF 9QTl(F ls+:QF Gk", +"9W WkG& 9GHWk 9GaWk 9G1Wk Gk:W 1GH9Wk :GHWk GQ9M QkG& 9GZk GkZ& 9GlW :GlW Z", +"9lG lGZ: 9GWdk dk&GW GkH9Wd g9GWk 1GW9dk :G+Wk 91GkHWd g:GWk 9GQdk dk&GQ Z9", +"Gdk Zk&gG lG9Qd :GQl+ lZ9Gd glZ:G 9Fkm kFm& kmF9B akm9F kmF95 km:F 95FkmB :", +"makF 9FMm km&QF 9TmkF aMm9F lFp9 lF:p p9lTF :plsF km;9F km&+F 9mFk;B akF9+m", +" 95;kmF :>mkF km1;9FB >ka:mF 9Q;kF k;&QF kmF9T; akF9Q; l;p9F l>p:F p9Tl;F :", +"psl>F Gk9m kmG& 9GmHk akm9G 9G5Wk Gk:m G5k9Hm :GmHk GM9m km&GQ Z9Gkm Zk&Gm ", +"p9lG lG:p Zp9lG :pGlZ 9Gvk Gkv& v9GHk gv9Gk v91Gk vk:G 9G5vkH :vGgk 9GvM v9", +"M+G Zv9Gk ZvkG& v9lG lG:v lvZ9G l:yG Ik Ik! DkI aIk 1UI 8Ik Dk1I Ua1I IkM I", +"!kM VIk aIVk lI lI8 lIV lsI Ik( +Ik D(Ik Dk+I 1(Ik U+1I Ik(1D 1U+aI kMI( Ik", +"+M IkV( +IVk lI( l+I VIl( sIl+ UGk U!Gk Jk Jka 1UG U81G J1U J8k IkGM IkMG! ", +"JkV VaJk lGU UGl8 lJ lJs Idk +kUG Jkd gJk UG1d U+1G 1UJd J1gU dkIM Idk+M Vd", +"Jk JkgV lId U+lG lJd glJ Ik2 I!k2 I2Dk I2ak 5Ik 8I5k Dk5I aI5k kMI2 IkM!2 I", +"kV2 V2aIk lI5 5Il8 V5lI l5sI Ik; I;+k IkD; Ika; U;1I >kI Ik;1D Dk>I k;IM Ik", +";+M IkV; V+Ik; lI; l>I V;lI lIV> U2Gk Ik2G! Jk2 akJ2 U51G 1UG58 J5k 5aJk Ik", +"MG2 Gk2IM! V2Jk JkVa2 UGl5 lGU58 lJ5 J5ls GkU; Idk+; Jk; J;gk UG1; 1U>G 1UJ", +"; J>k IdkM; +GkIM; V;Jk gVJk; UGl; >UlG lJ; J>l IkN I!kN DNIk aNIk 1NIk IN8", +"k IkN1D 1UaIN QIk Q!Ik QIVk aIQk lIQ Q8lI VQlI lQsI k(IN +NIk Ik(DN +DkIN I", +"k(1N 1U+IN 1D(IkN akN1+I Q(Ik QI+k VQIk( V+QIk QIl( +QlI VlQI( lsI+Q WIk W!", +"Ik JkW WaJk UG1W 8IWk 1UJW W8Jk GkUQ UGQk! ZJk ZaJk lWI W8lI lJZ lZsJ IdWk ", +"WI+k WkJd gWJk Idk1W IdkW8 J1UWd gJ1UW IdQk Idk+Q ZdJk ZJgk WIld +WlI ZJld ", +"gZlJ Ikm I!km IkDm Ikam Um1I Ik8m Ikm1D akm1I ImQk IkmQ! IkVm VmaIk lIp l8p", +"I lIVp splI kmI; Ik+m IkmD; akm+I Ikm1; Ik>m 1DmIk; >kDIm IkQ; Ikm+Q VmIk; ", +"V+mIk pIl; p>lI VplI; V>lpI GkUm IkmW! Jkm akJm UG1m IkmG8 1UJm 8kJm IkmGQ ", +"Ik!GMm JmZk ZJkam pUlG lGpU8 Jpl lsJp vIk +Ivk Jvk gkJv 1UvI vI>k v1Jk >kJv", +" QIvk vIk+Q ZkJv VvJgk lvI v>lI lvJ ylJ tI tI& tDI tcI t1I t:I 1DtI :Itc tI", +"M IMt& VtI tIVc ltI l:I VtlI uI tI( t+I DIt( c+tI 1It( :It+ t1DI( t:c+I IMt", +"( +ItM VIt( tIV+ tIl( t+lI ltVI( u+I tG tG& tJ tJc tG1 t:G tJ1 :Jt tGM GMt&", +" tJV VJtc ltG t:lG lJt uJ tGd t+G tJd gtJ 1Gtd :Gt+ J1td :Jgt GMtd +GtM VJt", +"d tJgV tGld t+lG ldtJ uJg tI2 I&t2 DIt2 c2tI t5I :It5 5DtI tIc5 IMt2 IM&t2 ", +"tIV2 tcVI2 t5lI l5:I tIV5 u5I t;I +It; D;tI tIc; 1It; t>I t5DI; >Itc IMt; t", +"+IM; tIV; tc;VI t;lI lIt> t5VI; u>I tG2 G&t2 tJ2 c2tJ tG5 :Gt5 tJ5 t5:J GMt", +"2 tGM&2 V2tJ tJVc2 t5lG t:Gl5 t5lJ uJ5 tG; +Gt; tJ; t;gJ 1Gt; t>G J1t; t>J ", +"GMt; t+GM; VJt; tJ;gV tGl; lGt> l;tJ uJ> tIN I&tN DItN cItN 1ItN tN:I t1DIN", +" t:cIN tQI QIt& VQtI tIcQ tQlI :QlI tQVlI uQI I(tN +ItN tDI(N tc+IN t1I(N t", +":+IN 1DIt(N :cIt+N QIt( +QtI tQVI( tcQ+I tQIl( t:Q+I Vl(tQI +QuI tGW G&tW t", +"JW cJtW 1GtW :GtW J1tW tW:J tGQ GQt& tZJ ZctJ tGlW :GtQ lJtZ uZJ WItd +GtW ", +"JWtd tWgJ tG1Wd t:G+W tJ1Wd gtJ:W GQtd +GtQ ZJtd gZtJ tGQld t:G+Q lJZtd gZu", +"J tmI Imt& DmtI tIcm 1Itm :Itm t5DIm t:mcI QItm tQIm& tIVm tcQIm tpI lI:p p", +"IVt upI Imt; +Itm tmDI; tcm+I t5Im; >Itm 1Dmt;I t>cIm QIt; t+QIm tQ;VI tcQI", +"; pIt; pIt> tpVI; p>uI tGm Gmt& tJm cJtm 1Gtm :Gtm J1tm tm:J GQtm tGQm& ZJt", +"m tJmZc tpG tG:p tpJ uJp tvG vIt+ tvJ tJcv v1tG tG:v t5Jv :Jtv vItQ tv+GQ t", +"JVv cvVtJ lGtv lI:v Jptv yuJ e e! eB ae e. 8e e.B 8ea eK eK! Te Tea ej 8ej ", +"Tej se e* +e *Be +ea e*. +e8 e.*B ae8* e*K +eK Te* +Te ej* +ej ejT* se+ Ge ", +"Ge! He Gae Ge. G8e He. H8e GeK eKG! GTe TeGa Gje 8eGj Hej sGe f f+ fH gf f.", +" f8 fH. gf8 fK f+K fT gfT fj f8j fTj sf e2 e2! e2B ae2 5e 5e8 5eB 5ae e2K 2", +"Ke! Te2 T2ae 5ej 58ej 5Te se5 e; +e; e;B ae; 5e; >e *B5e >ea e;K +Ke; Te; T", +";+e e;j >ej Te5* >es Ge2 e2G! He2 G2ae G5e 5eG8 H5e 5aHe e2GK Ge2!K G2Te ae", +"2GT 5eGj G58ej 5THe G5se f; f+; fH; gf; f5 f> f5H f>g f;K +;fK fT; g;fT f5j", +" f>j f5T sf> eN eN! eNB aeN eN. 8eN .BeN aN8e Qe Qe! QeT Qae Qej Q8e QTej s", +"eQ e*N +eN eN*B +Nae *Ne. +N8e *BeN. aeN8* Qe* +Qe TeQ* Qa+e ejQ* Q8+e ej*Q", +"T +Qse We We! HeW Wae We. W8e H.We W8He GQe Q!We Ze Zea Wej Q8We Zej sZe fW", +" f+W fHW gfW fW. f8W HWf. gWf8 fQ fQ+ Zf Zfg fQj fQ8 Zfj sfZ em em! emB aem", +" 5em 8em 5Bem ae8m Qem Q!em Tem aeTm pe pe8 peT spe em; +em *Bem ae+m em5* ", +">em em;5B ae>m Qe; Qe+m TeQ; Te+m pe; >ep Tep* sp>e Gme W!em Hem aeGm 5We W", +"85e 5WHe Wa5e QeGm em!GQ Zem aeZm pGe G8pe Zpe Zpse fv fv+ fvH gfv fv5 f>v ", +"vHf5 gvf> fvQ v+fQ Zfv gvZf fp fp> fpZ yf ,e e& ,eB ,ae ,e. ,8e e.,B 8e,a ,", +"eK e&K ,Te Te& ,je e&j Te,j se& ,e* -e ,e*B -ae e*,. -8e *Be,. 8e-a e*,K -e", +"K ,eT* -Te ,e*j -je ej*,T s-e ,G ,G& ,GH ,Ga ,G. ,G8 ,.He G8,H ,GK ,KG& ,GT", +" GT,a ,Gj G8,j GT,j sG, f, f- fH, gf- f,. f-8 ,Hf. f8g- f,K f-K fT, f-T fj,", +" f-j ,Hfj sf- ,e2 e&2 e2,B ,2ae ,5e 5e& ,B5e 5e,a e2,K &2eK ,2Te T2e& 5e,j ", +"ej5& 5e,T ,5se ,;e -e; ,Be; ae-; 5e,; >e- e;B,5 -a>e ,Ke; e;-K Te,; Te-; e;", +",j -j>e e;j,T >es- ,G2 ,2G& ,2He ,2Ga ,G5 G5,8 G5,H G5,a G2,K e&2GK ,2GT ,G", +"T&2 G5,j e&jG5 G5,T ,Gs5 f;, f-; ,Hf; g;f- f5, f>- ,Hf5 g-f> ,;fK fK-; ,Tf;", +" f;-T ,5fj ->fj ,5fT f>s- ,eN e&N eN,B ,Nae eN,. ,N8e ,eN.B aeN,8 ,Qe Qe& Q", +"e,T Qe,a Qe,j Qe,8 ,QTej ,Qse e*,N -eN *Be,N ae-N ,e*N. 8e-N ,eN*B. -8aeN ,", +"eQ* -Qe ,QTe* Qa-e ej*,Q Q8-e ,QTej* -Qse ,GW We& He,W Ga,W ,.We G8,W ,GHW.", +" ,G8Wa ,GQ G&,Q Ze, Ze& GQ,j GQ,8 ,GZj Zes, fW, f-W ,HfW -Wgf ,Wf. -Wf8 fHW", +",. f-Wg8 fQ, f-Q Zf, Z-f ,Wfj -Wfj fjZ, Z-sf ,me em& ,Bem ae,m 5e,m 8e,m em", +"B,5 aem,5 Qe,m emQ& Te,m emT& pe, pe& ,Tpe pes, em,; -me em;,B ae-m em;,5 -", +"m>e ,5em*B >e-am Qe,; Qe-m ,Q;Te Te-m ,;pe -pe peT,; se-p ,Gm G&,m Gm,H Ga,", +"m G5,W G8,m ,G5Hm em&H5 GQ,m em&GQ ,GZm emZ& pG, ,Gp& pGZ, pGs, fv, -vf fHv", +", gf-v v,f5 -vf> fvH,5 f>g-v v,fQ fQ-v fvZ, -vZf fp, fp- Z,fp yf- Ie Ie! De", +" Dea Ie. 8Ie De. D8e IeK I!eK DeT aITe Ije Ij8e Dej sIe I* +Ie De* +De I*. ", +"8I* D.I* D8+e I*K +KI* TI* De+T I*j I*+j I*Dj sI* GIe IeG! Je Jae IeG. GI8e", +" Je. J8e IeGK GIe!K JTe TeJa GIej IjeG8 Jje sJe fI f+I Jf gJf fI. f8I Jf. J", +"f8 fIK +IfK JfT fTgJ fjI +Ifj Jfj sfJ Ie2 I!e2 De2 I2ae 5Ie 8I5e 5De D85e e", +"2IK Ie2!K I2Te ae2TI Ij5e Ije58 De5T 5Dse I;e I;+e De; D;+e 5I* >eI De5* >e", +"D IKe; I;e+K TeD; ae;TI Ij5* I*>j TI5* sI>e IeG2 GIe!2 Je2 aeJ2 GI5e G58Ie ", +"J5e 5aJe GIe2K Ie2GK! TeJ2 JTae2 IjeG5 G58Ije 5TJe J5se fI; +If; Jf; J;gf f", +"5I f>I Jf5 J>f I;fK f+I;K fTJ; JfTg; 5Ifj >Ifj f5Jj J>sf IeN I!eN DeN DNae ", +"eNI. IN8e eND. DN8e QIe IeQ! QDe DeQa IjQe 8IQe DjQe QDse I*N +NI* DNI* +ND", +"e *NI. IN8* I*ND. +De8N QI* QI+e DeQ* QD+e IjQ* 8IQ* I*jQD QIs* WIe IeW! JW", +"e WaJe IeW. 8IWe WeJ. W8Je QIWe GQIe! ZJe JaZe IjWe GQ8Ie JjZe ZJse fWI +Wf", +"I JfW gWJf WIf. W8fI fWJ. JWf8 fQI +QfI ZfJ gJZf WIfj Q8fI JjZf ZfsJ Ime I!", +"em Dem aeDm Im5e Im8e Dm5e 8eDm ImQe ImeQ! DmQe aemQD pIe 8Ipe pDe pDse Im*", +" Im+e ImD* De+m Im5* Im>e Im*5D De>m ImQ* Im*+Q DeQ; ae;QD pI* pI>e Dep* pD", +">e ImWe ImeW! Jme aeJm WI5e G8mIe 5WJe 8eJm ImeGQ GQIem! JmZe ZJaem WIpe pG", +"8Ie Jpe seJp fvI f+vI Jvf gfJv vIf5 vIf> fvJ5 f>Jv vIfQ fv+QI JvZf ZfJgv fp", +"I pIf> Jpf yJf h h& hD hc h. h8 hD. hc8 hK h&K hT hcT hj h8j hTj hs h* h- h", +"D* hc- h*. h-8 D*h. c-h8 h*K h-K hT* h-T hj* h-j D*hj hs- hG hG& hJ hJc hG.", +" h8G hJ. hJ8 hGK G&hK hJT cJhT hjG G8hj hJj hsJ hf hf- hJf i hf. hf8 h.Jf i", +"8 hfK hKf- hfT iT hfj f-hj Jfhj is h2 h&2 hD2 hc2 h5 h58 h5D hc5 h2K &2hK h", +"T2 c2hT h5j 5&hj h5T hs5 h; h-; h;D hc; h5; h> 5Dh; h>c h;K -;hK hT; c;hT h", +";j h>j 5Th; hs> hG2 G&h2 hJ2 h2cJ h5G G5h8 hJ5 cJh5 G2hK hG&2K JTh2 cJTh2 G", +"5hj h5G&j J5hT h5sJ hf; f-h; hJ; i; hf5 h>f Jfh5 i> f;hK f-;hK J;hT iT; f5h", +"j hjf> J;hj is> hN h&N hDN hcN hN. h8N DNh. c8hN hQ hQ& hQD hcQ hQj hQ8 QDh", +"j hsQ h*N h-N D*hN hNc- *Nh. -8hN hD*N. c-8hN hQ* h-Q QDh* c-hQ Q*hj -Qh8 h", +"QD*j hQs- hW hW& hJW hcW hW. hW8 JWh. cWh8 hWQ GQh& hZ hZc hWj GQh8 hZj hZs", +" hfW h-W JfhW iW fWh. f8hW JfWh. iW8 hfQ f-hQ hZf iZ fQhj fQh8 hjZf iZs hm ", +"hm& hmD hcm h5m h8m 5Dhm c5hm hQm Q&hm hTm cQhm hp hp8 hpD hps hm; h-m Dmh;", +" c-hm 5*hm h>m h5Dm* hmc> hQ; -Qhm QDh; cQh; hp; hp- pDh; cph> hWm Gmh& hJm", +" cJhm h5W G8hm J5hW c5hW GQhm hWQm& hZm hmZc hpG pGh8 hZp cphZ hv hv- hvJ i", +"v hv5 hv> h5Jv iv> hvQ hQ-v hZv iZv hpf fph> Jphv yi e0 e0! e0B ae0 1e 1e8 ", +"1eB 1ae e0K !Ke0 Te0 T0ae 1ej 18ej 1Te se1 e*0 +e0 e0*B +0ae 1e* 1+e *B1e +", +"e1a *0eK e0+K e*T0 +0Te ej1* +j1e Te1* 1+se Ge0 e0G! He0 G0ae 1Ge G81e 1He ", +"Ga1e e0GK Ge0!K G0Te ae0GT Gj1e 1G8ej GT1e 1Gse f0 f+0 fH0 gf0 f1 f1+ f1H g", +"f1 f0K +0fK fT0 g0fT f1j 1+fj f1T sf1 e02 !0e2 2Be0 e2a0 1e5 158e 1B5e 5a1e", +" 2Ke0 e02!K e0T2 ae0T2 15ej 1e58j 5T1e 1es5 e;0 +0e; ;0eB a0e; 1e; >e1 1Be;", +" 1a>e ;0eK e;0+K T0e; ae;T0 ej1; 1e>j Te1; s1>e e0G2 Ge0!2 e0H2 ae0G2 G51e ", +"1G58e H51e 1Ga5e Ge02K e02GK! GTe02 GT0ae2 1G5ej G581ej 1GT5e se1G5 f;0 +;f", +"0 H;f0 f;g0 f15 f>1 1Hf5 g1f> ;0fK f+;0K T;f0 fT;g0 1;fj >jf1 1Tf5 f1s> e0N", +" !0eN 0BeN e0aN 1eN 1N8e eN1B 1Nae Qe0 e0Q! Q0Te Q0ae 1Qe Q81e Qe1T 1Qse *0", +"eN e0+N *Be0N ae0+N e*1N 1N+e 1e*NB 1+eaN e*Q0 +0Qe QeT*0 +QeT0 Qe1* +Q1e 1", +"QeT* se1+Q We0 e0W! H0We W0ae 1We W81e He1W Wa1e G0Qe GQe!0 Ze0 aeZ0 GQ1e 1", +"GQ8e Z1e Z1se fW0 +Wf0 HWf0 fWg0 f1W 1+fW 1HfW gWf1 fQ0 +Qf0 Zf0 g0Zf f1Q 1", +"+fQ Zf1 g1Zf em0 e!m0 m0eB a0em 1em 8e1m 1Bem ae1m Q0em em0Q! T0em aemQ0 pe", +"1 1ep8 1Tpe s1pe e0m* +0em em;0B aem+0 em1; 1e>m 1em*B >e1am Q0e; +Qem0 Qe;", +"T0 ae;Q0 1ep* p1>e pe1T; >eps1 G0em em0W! H0em aemG0 Gm1e 1Gm8e He1m aem1G ", +"em0GQ GQ0em! emZ0 Zeam0 1Gpe pG81e peZ1 Zpes1 fv0 v+f0 vHf0 g0fv fv1 v>f1 f", +"Hv1 fvg1 vQf0 fv+Q0 Z0fv Zfgv0 fp1 p>f1 Z1fp yf1 9e 9e& 9eB 9ae ,1e :e ,B1e", +" :ea 9eK 9Ke& 9Te Te9a 9ej :ej 1T9e s:e 9e* -9e *B9e 9a-e 1e9* :-e ,1e*B -a", +":e e*9K 9e-K Te9* 9T-e ej9* -j:e ,1Te* :-se ,G9 G&9e 9He 9G,a ,1G :Ge 1G,H ", +":He ,K9G e&K9G 9G,T ,GT9a 9G,j ,G:j 1G,T :Gse f9 f-9 f9H gf9 f91 :f ,1fH :f", +"g f9K fK-9 f9T fTg9 f9j :fj ,1fT sf: 9e2 92e& e29B 92ae 95e :e5 9B5e 5a:e e", +"29K e&29K 92Te 9Te&2 5e9j 5e:j 5T9e s5:e 9e; 9e-; 9Be; ae9; 1e,; >e: ,1;eB ", +":a>e 9Ke; -9e;K Te9; -T9e; e;9j >j:e e;j9T s:>e ,29G e&29G 92He ,Ga92 9G,5 ", +",G:5 H59e H5:e ,G92K 9G2e&K ,GT92 ae29GT ,1G5j :G5,j ,1G5T :Hes5 f9; -9f; 9", +"Hf; g;f9 f95 :f> 9Hf5 f>g: 9;fK f-9;K 9Tf; f-T9; 95fj fj:> 95fT s:f> 9eN 9N", +"e& eN9B 9Nae ,N1e :eN ,1eNB ae:N 9Qe Q&9e Qe9T Qa9e 1Q9e :Qe ,1QTe :Qse e*9", +"N 9e-N 9e*NB -9aeN ,1e*N -e:N 1eN9*B :-aeN Qe9* 9Q-e 9QeT* -Q9Te ,1Qe* -Q:e", +" 1Qe9T* s-:Qe 9We W&9e He9W Wa9e 1G,W :We ,1GHW He:W 9G,Q ,GQ9& Z9e 9aZe 1G", +",Q ,G:Q ,1Ze Z:e f9W -Wf9 9HfW gWf9 ,1fW :fW f91HW :Wgf f9Q -Qf9 Zf9 g9Zf ,", +"1fQ :fQ f9Z1 Z:f 9em e&9m 9Bem ae9m 1e,m :em ,1meB ae:m Qe9m em&9Q Te9m aem", +"9Q p9e :pe 9Tpe se:p em9; 9e-m 9em*B -m9ae ,1me; :m>e emB,1; :-mae Qe9; -Q9", +"em 9QeT; -Tm9e 9ep* >e:p p9eT; -ps:e 9G,m em&9G He9m aem9G 1G,m ,G:m ,1GHm ", +"He:m ,GQ9m 9GQem& 9eZm Z9eam ,1pG pG:e p9Ze Ze:p fv9 f9-v fHv9 fvg9 v9f1 :f", +"v fv91H gf:v v9fQ -vf9Q fvZ9 Zf9-v fp9 :fp Z9fp y:f Ie0 I!e0 De0 D0ae 1Ie 8", +"I1e 1De D81e e0IK Ie0!K D0Te ae0TI Ij1e Ije18 De1T 1Dse I*0 +0I* D0I* +0De ", +"1I* +I1e De1* +D1e *0IK I*0+K T0I* +DeT0 Ij1* I*j1+ TI1* 1Is* IeG0 GIe!0 Je", +"0 aeJ0 GI1e 1G8Ie J1e 1aJe GIe0K Ie0GK! TeJ0 JTae0 Ije1G 1G8Ije 1TJe J1se f", +"I0 +If0 Jf0 g0Jf f1I 1+fI Jf1 Jfg1 I0fK f+I0K J0fT JfTg0 1Ifj f1+Ij f1Jj J1", +"sf e0I2 Ie0!2 e0D2 ae0I2 5I1e 1I58e 5D1e 1De5a Ie02K e02IK! DeT02 TI0ae2 Ij", +"e15 5I81ej 1De5T sI15e I0e; I;e+0 D0e; ae;D0 5I1* 1I>e De1; 1D>e I;e0K +I0e", +";K De;T0 ae0TI; I;j1e >I1ej e;j1D sI*>1 GIe02 Ie0G2! e0J2 Jae02 1G5Ie G581I", +"e 1eJ5 J158e Ie0G2K !0I2GeK JTe02 ae0JT2 1G5Ije 8eGj1I5 J15Te sJ15e I;f0 f+", +"I;0 f;J0 Jf;g0 1If5 >If1 f1J5 J1f> fI;0K I;0f+K JfT;0 fT;gJ0 f15Ij f>1Ij Jf", +"15T J>sf1 e0IN Ie0!N e0DN ae0DN Ie1N 1I8eN 1NDe 1DeaN IeQ0 QIe!0 Q0De QDea0", +" QI1e 1QI8e QD1e sI1Qe *0IN I*0+N I*0DN +De0N 1NI* 1+IeN 1De*N 1+DeN Q0I* +", +"QIe0 QDe*0 +QDe0 QI1* 1+QI* 1QDI* sI*1Q IeW0 WIe!0 WeJ0 JWae0 WI1e 1WI8e 1W", +"Je J1W8e GQIe0 QI0We! JeZ0 ZJae0 1GQIe W8I1Qe J1Ze sJZ1e WIf0 f+WI0 fWJ0 Jf", +"Wg0 1WfI f1+WI JWf1 Jf1gW QIf0 fQ+I0 Z0Jf ZfJg0 1QfI f1Q+I JfZ1 Zf1sJ I0em ", +"Ime!0 D0em aemD0 Im1e 1Im8e De1m aem1D ImeQ0 QI0em! QDem0 ae0TIm 1Ipe pI81e", +" 1Dpe pDes1 I0m* Im*+0 Im*D0 am*I0 Im1* >I1em Im*1D >eD1m Im*Q0 Im0+Qe Im*T", +"0 ae0QD; 1Ip* >ep1I pDe1; sI*p1 ImeG0 WI0em! emJ0 Jmae0 1GmIe W8I1em 1eJm J", +"1m8e GQIem0 ImQeW!0 ZJem0 JmaZe0 pG1Ie 1WIpe8 peJ1 Jps1e f0vI fv+I0 J0fv Jv", +"gf0 vIf1 f>v1I fvJ1 J>fv1 fvQI0 vI+fQ0 ZfJv0 JvgZf0 pIf1 fp>1I J1fp Jfy1 h9", +" h9& h9D hc9 h1 h: h1D h:c h9K 9&hK hT9 c9hT h1j h:j h1T hs: h9* h-9 9Dh* h", +"9c- h1* h:- 1Dh* :ch- 9*hK -9hK 9Th* h9-T 9*hj :-hj 1Th* s-h: h9G 9Gh& hJ9 ", +"J9hc h1G h:G hJ1 :Jh 9GhK h9G&K J9hT cJ9hT 9Ghj :Ghj J9hj :Jhs hf9 f9h- Jf9", +" i9 hf1 :fh Jfh1 i: hKf9 f-9hK f9hT i9T f9hj hj:f f9Jj is: h92 9&h2 9Dh2 c2", +"h9 h15 h:5 95hD :ch5 92hK h9&2K 9Th2 hc9T2 95hj :5hj 95hT s:h5 h;9 -9h; 9Dh", +"; h9c; h1; h>: 1Dh; c>h: 9;hK h-9;K 9Th; hc;9T 9;hj :>hj 1Th; s:h> 9Gh2 h9G", +"&2 h2J9 cJ9h2 9Gh5 :Gh5 J9h5 h5:J h9G2K 9G&h2K J9Th2 hcTJ92 h1G5j h:G5j J95", +"hT hsJ:5 f9h; f-9h; J9h; i9; f9h5 :fh> J1h; i:> f9;hK h-;f9K Jf9T; 9Ti; f95", +"hj h>:fj Jf95T s:i> h9N 9&hN 9DhN c9hN h1N h:N 1DhN hN:c hQ9 9Qh& 9QhT h9cQ", +" h1Q h:Q 1QhT s:hQ 9*hN -9hN h9D*N c-9hN 1*hN hN:- h1D*N h:c-N 9Qh* h9-Q hQ", +"9D* c-Qh9 1Qh* :-hQ h1QD* h:Qs- hW9 9Wh& J9hW h9cW h1W h:W J1hW hW:J 9GhQ h", +"W9Q& hZ9 Z9hc 9Whj :GhQ hZ1 hZ: f9hW h9-W JWf9 i9W f1hW hW:f Jf91W i:W f9hQ", +" f-Qh9 Z9hf iZ9 f1hQ hQ:f h1Zf iZ: hm9 9mh& 9Dhm h9cm h1m h:m 1Dhm :chm 9Qh", +"m hQ9m& 9Thm hcQ9m hp9 hp: p9hT h:cp 9mh; h9-m hm9D; c-mh9 1mh; :-hm h1mD; ", +"h:mc> 9Qh; h-Q9m hQ;9D cQ;h9 p9h; h:-p hp9D; cp-h: 9Ghm hW9m& J9hm cJmh9 95", +"hW :Ghm J1hm hm:J hW9Qm 9GQhm& Z9hm hZc9m p9hW hW:p h1Jp Z:hp hv9 v9h- J9hv", +" iv9 hv1 hv: h1Jv i:v v9hQ hv-9Q Z9hv Zvi9 h1fp :fhp Zvh1 yi: eL eL! eLB ae", +"L eL. 8eL .BeL 8Lae Me Me! TeM aMe Mej 8Me TMej seM e*L +eL eL*B +Lae *Le. ", +"+L8e *BeL. aeL8* Me* +Me MeT* Te+M ejM* 8M+e Me*Tj +Mse GeL eLG! HeL GLae e", +"LG. GL8e eLH. HL8e GMe G!Me HMe aMHe MeGj 8eGM MeHj GMse fL f+L fHL gfL fL.", +" f8L HLf. gLf8 fM fM+ fTM gfM fMj f8M HMfj sfM eL2 !Le2 2BeL e2aL 5eL 5L8e ", +"eL5B 5Lae Me2 M!e2 T2Me M2ae 5Me 8M5e Te5M 5Mse e;L +Le; ;LeB aLe; 5Le; >eL", +" e;L5B ae>L Me; M;+e MeT; Mea; Me5* >Me Me;5T se>M eLG2 GeL!2 eLH2 aeLG2 GL", +"5e G58eL HL5e G5aeL G2Me Me2G! H2Me aMeG2 5eGM G5M8e 5MHe se5GM f;L +;fL H;", +"fL f;gL f5L f>L H5fL >Lgf fM; +Mf; HMf; g;fM f5M f>M H5fM >Msf eLN !LeN NBe", +"L eLaN N.eL eN8L eLN.B aeL8N QeM Q!Me QTMe aMQe QMej 8MQe MejQT QesM *LeN e", +"L+N *BeLN aeL+N e*LN. +e8LN eLN*B. 8eLa*N MeQ* Qe+M Me*QT aMe+Q Me*Qj +Qe8M", +" QeTM*j se+QM WeL eLW! HLWe WLae eLW. WL8e HeWL. H8eWL WMe W!Me ZeM aMZe Me", +"Wj 8MWe MeZj sMZe fWL +WfL HWfL fWgL WLf. W8fL fHWL. f8WgL fQM +WfM ZfM gMZ", +"f WMfj W8fM ZMfj fMsZ emL e!mL mLeB aLem 5Lem 8Lem emL5B aem5L Mem M!em MeT", +"m Meam peM 8Mpe TepM sMpe eLm* +Lem em;LB aem+L em;5L em>L 5eLm*B >eamL MeQ", +"; Me+m MemT; aMe+m Mep* pe>M peTM; >Mesp GLem emLW! HLem aemGL 5LWe G8meL H", +"5eWL H8emL MeGm MemW! MeZm ZeaMm GMpe pG8Me ZMpe ZpesM fvL v+fL vHfL gLfv v", +"5fL >Lfv fvH5L f>gvL fvM f+vM fMZv fvgM fpM >Mfp fMZp yfM ,eL e&L eL,B ,Lae", +" eL,. ,L8e ,eL.B aeL,8 ,Me Me& Te,M ae,M Me,j 8e,M Mej,T ,Mse e*,L -eL *Be,", +"L ae-L ,e*L. 8e-L ,eL*B. -8aeL ,eM* -Me Me*,T Te-M Me*,j 8M-e ,TMej* -Mse ,", +"GL ,LG& ,LHe ,LGa GL,. ,LG8 ,GHL. ,G8HL ,GM G&,M GT,M Ga,M GM,j G8,M ,GTMj ", +",GsM f,L f-L ,HfL gLf- ,Lf. -8fL fH,L. f-8gL fM, f-M ,HfM f-gM ,Mfj -Mf8 fT", +"M,j fMs- eL,2 &Le2 ,eL2B aeL,2 ,L5e 5Le& ,5eLB ,5aeL ,2Me M2e& Me2,T aMe,2 ", +"5e,M Me5& ,5TMe se5,M ,Le; e;-L e;L,B ae;-L e;L,5 -e>L ,5Le;B >e-aL Me,; Me", +"-; Me;,T -TMe; Me;,5 -M>e ,5TMe; s->Me GL,2 e&LG2 ,GHL2 ,GaL2 ,LG5 ,G58L ,G", +"5HL ,G5aL ,2GM Me&G2 ,GTM2 Me&H2 G5,M Me&G5 ,G5HM sG,5M ,;fL -;fL fH;,L f-;", +"gL ,5fL >Lf- f5H,L f>g-L ,Mf; f;-M fTM,; f-TM; ,5fM f->M f5T,M f>Ms- eL,N &", +"LeN ,eLNB aeL,N ,eLN. e&L8N eLN,.B ,8LaeN Qe,M MeQ& ,QTMe aMe,Q Mej,Q Me&Q8", +" ,QTMej seM,Q ,e*LN eL-N ,eL*BN -aeLN e*L,N. -8eLN NB*L,e. aeL-8N Me*,Q Qe-", +"M ,QTMe* -QTMe ,QMej* -Q8Me QMej,T* s-QMe ,LWe WLe& ,GHWL ,GaWL ,GWL. ,G8WL", +" HeL,W. aeL,W8 GQ,M MeW& ,GZM MeZ& ,GQMj Me&W8 ZeM,j Ze&sM ,WfL fL-W fHW,L ", +"f-WgL fW,L. f-W8L ,HWfL. gf8-WL ,WfM -WfM fMZ, fMZ- fQM,j f-Q8M ZfM,j sfZ-M", +" ,Lem eLm& emL,B aem,L emL,5 em&5L ,5LemB aeL,8m Me,m emM& Mem,T aMe,m ,Mpe", +" Mep& peT,M pe&sM em;,L em-L ,mLe;B -maeL ,5em*L >e-mL 5m*B,eL -ma>eL Mem,;", +" Me-m ,TmMe; -TmMe peM,; pe-M Me;p,T -psMe ,LGm em&GL ,GmHL em&HL ,G5WL ,G8", +"mL emL,H5 aeL,5W GM,m MemG& ZeM,m Ze&Mm ,GpM pG&,M Zpe,M ZpeM& fLv, fL-v fv", +"H,L -vgfL fv5,L f>-vL v,Hf5L -vgf>L v,fM fM-v Zfv,M -vMZf p,fM fM-p fpZ,M f", +"-yM Ue Ue! UeD Uae Ue. U8e U.De D8Ue UeM U!Me Ve Vea Uej 8MUe Vej seV Ue* U", +"+e DeU* +DUe U.I* +eU8 I*.UD U+eD8 IM* +MUe Ve* V+e I*Uj +eUj I*Vj V+se UGe", +" GeU! JUe UaJe GeU. G8Ue UeJ. U8Je GMUe IM!Ge VJe JaVe GjUe UG8Me VeJj VJse", +" fU fU+ JfU gfU fU. fU8 J.fU fUJ8 fUM U+fM Vf Vfg fUj U+fj Vfj sfV Ue2 e2U!", +" U2De U2ae U5e 5eU8 5DUe 5aUe U2Me IM2e! Ve2 aeV2 5MUe U5e8M V5e V5se Ue; +", +"eU; DeU; aeU; 5IU* >eU U5eD; Ua>e MeU; IM;+e Ve; +eV; IM5* Ue>M 5IV* V>e Ge", +"U2 UGe!2 UeJ2 JUae2 G5Ue UG58e U5Je J5U8e IM2Ge UG2Me! JeV2 VJae2 UG5Me G5M", +"U8e VeJ5 seVJ5 fU; U+f; fUJ; g;fU fU5 f>U fUJ5 gUf> U;fM fU+M; Vf; V;gf U5f", +"M fU>M Vf5 Vf> UeN eNU! UNDe UNae eNU. UN8e UeDN. U8eDN UQe Q!Ue VeQ QaVe Q", +"eUj Q8Ue QeVj sQVe UNI* UN+e I*NUD U+eDN I*NU. U+e8N DeNU*. aeNU8* QIU* +QU", +"e QIV* +QVe IM*Qj IM*Q8 VeQ*j sI*VQ UWe U!We UWJe WaUe U.We W8Ue JUWe. J8UW", +"e GQUe UGQe! ZeV ZaVe WeUj UGQ8e ZjVe VZse fUW U+fW JWfU gWfU UWf. UWf8 JfU", +"W. Jf8UW fUQ U+fQ VfZ ZfgV UWfj UQf8 fjVZ sZVf Uem U!em DeUm aeUm 5eUm 8eUm", +" U5eDm aemU5 QeUm IMme! Vem aeVm pUe U8pe Vpe seVp ImU* +eUm Im*UD aemU+ Im", +"*U5 Ue>m 5DeUm* >eUDm QeU; IMm+e QeV; +eVm Uep* pU>e pIV* >eVp GmUe UGme! U", +"eJm JmUae 5WUe UG8em J5UWe J8mUe IMmWe Im!WMe VeJm ZeVam UGpe pUG8e ZeVp Jp", +"Vse fvU f+vU fUJv fvgU vUf5 v>fU JvfU5 J>fvU vUfQ fvU+Q Vfv gfVv fpU p>fU f", +"pV yVf hU hU& hUD hcU hU. h8U UDh. cUh8 hM hM& Vh Vhc hMj h8M Vhj hsV hU* h", +"-U U*hD -Uhc U*h. -Uh8 hUD*. c-Uh8 hM* h-M Vh* V-h U*hj -Uhj V*hj V-hs hUG ", +"UGh& hJU hUcJ UGh. UGh8 JUh. hUJ8 hMG GMh& VhJ cJVh UGhj G8hM JjVh VhsJ hfU", +" f-U fUhJ iU h.fU fUh8 JfUh. iU8 hfM f-hM Vfh iV fUhj f8hM hjVf iVs hU2 U&h", +"2 UDh2 c2hU h5U U5h8 U5hD hUc5 hM2 M&h2 Vh2 h2Vc h5M 5Mh8 Vh5 c5Vh h;U -Uh;", +" U;hD hUc; U5h; h>U h5UD; >Uhc hM; -Mh; Vh; h;V- U;hj h>M V5h; h>V UGh2 hUG", +"&2 JUh2 cJUh2 UGh5 h5UG8 hUJ5 cJ5hU GMh2 hMG&2 h2VJ VhJc2 G5hM h5MG8 J5Vh V", +"h5sJ fUh; f;-U hUJ; iU; fUh5 fUh> Jf5hU i>U fMh; f-UM; h;Vf iV; f5hM hf>M h", +"5Vf iV> hUN U&hN UDhN cUhN UNh. U8hN hUDN. hcU8N hQU UQh& VhQ cQVh UQhj UQh", +"8 VQhj VshQ U*hN hN-U hUD*N c-UhN hU*N. -U8hN U*DhN. hc8-UN UQh* -UhQ V*hQ ", +"hQV- hQU*j -UQh8 VhQ*j V-hsQ hWU UWh& JUhW hUcW UWh. UWh8 hJUW. hJ8UW hWM W", +"Mh& hZV VhZc UWhj W8hM VZhj sZVh fUhW -UhW JfUhW iUW fUWh. f-UW8 hJWfU. UWi", +"8 fUhQ -UfQ VfhZ iZV fUQhj f-UWj VfZhj sZiV hmU Umh& UmhD hUcm U5hm U8hm h5", +"UDm hc5Um hMm Mmh& Vhm cmVh hpU pUh8 hpV Vphs Umh; -Uhm hmUD; c-mhU h5Um* >", +"Uhm U5*hmD h>cUm UQh; -Mhm Vmh; hmV- pUh; >Mhp h;Vp V-hp UGhm hWUm& hUJm cJ", +"mhU U5hW h5WU8 hJ5UW c5WhU GMhm hWMm& JmVh VhmZc pUhW hpUG8 VphZ cpJVh hvU ", +"fU-v vUhJ ivU vUh5 vUh> hvJU5 v>iU hvM hM-v hvV iVv hMfp >Mhv Vfhp yiV ke k", +"e! keB ake 1ek 8ke 1Bke ak1e keM M!ke Tke akTe le le8 leT lse ke* +ke *Bke ", +"ak+e ke1* +k1e ke*1B ake1+ Mek* ke+M keT* Tk+e le* l+e Tel* sel+ Gke G!ke H", +"ke akHe Gk1e 8eGk Hk1e 8kHe keGM keMG! TkHe akeGT lGe G8le lHe sGle fk fk+ ", +"fkH gfk f1k f8k 1Hfk f1gk fkM +kfM fTk fTgk lf lf+ lfH glf ke2 k!e2 e2kB k2", +"ae 5ke 8k5e 5Bke ak5e k2Me keM!2 T2ke akeT2 le5 5el8 5Tle l5se ke; k;+e kBe", +"; kea; ke1; >ke ke;1B ak>e Mek; ke;+M keT; akeT; le; l>e Tel; >els G2ke ke2", +"G! H2ke akeG2 5eGk G5k8e 5kHe akeG5 keMG2 Gk2Me! GTke2 ak2HMe G5le lG58e H5", +"le lsG5e fk; +kf; Hkf; g;fk f5k f>k H5fk gf>k k;fM fk+M; Tkf; fTkg; lf5 lf>", +" f5lH l>sf keN k!eN eNkB aNke 1Nke kN8e keN1B ake1N Qke Q!ke TkQe akQe leQ ", +"Q8le QelT lQse e*kN +Nke ke*NB ake+N ke*1N 1+ekN 1eNk*B akN1+e keQ* Qk+e Qk", +"eT* ake+Q Qel* +Qle leQT* lse+Q Wke W!ke WkHe akWe Wk1e 8kWe 1HeWk ake1W Qk", +"We GQke! Zke akZe lWe W8le lZe sZle fkW +Wfk HkfW gWfk 1Wfk W8fk f1HWk f8kg", +"W fQk +Qfk Zfk gkZf lfW fQl+ lfZ Zfgl kem k!em kBem keam ke1m ke8m kem1B ak", +"e1m kmQe kemQ! keTm akeTm lep l8pe lTpe sple emk; ke+m kem*B ake+m kem1; ke", +">m keB1m* >keam keQ; kem+Q kemT; akeQ; pel; p>le lepT; l>esp keGm kemW! keH", +"m akeGm Wk5e kemG8 kem1H ake5W kemGQ ke!GMm keZm Zkeam pGle lGp8e Zple lZes", +"p fvk f+vk fHvk fvgk v1fk fv>k fv1Hk f>kgv vkfQ fvk+Q fvZk Zfkgv lfp l>fp Z", +"flv ylf ,ke ke& ,Bke ak9e 1e,k :ek keB,1 ak:e 9Me Mek& Tk9e aM9e le9 l:e ,T", +"le s:le ke9* -ke ke*,B ak-e ke*,1 -k:e ,1ek*B :-ake Me9* 9M-e ,Tke* Tk-e 9e", +"l* l-e le9T* les- ,Gk G&,k Gk,H Ga,k 1G,k ,G:k ,1GHk Hk:e 9G,M ke&GM GT,k a", +"Me9G lG, :Gle ,GlH sGl, f9k f-k ,Hfk f-gk ,1fk :fk f91Hk gk:f f9M -kfM ,Tfk", +" f9gM lf9 lf: f9lH sfl- ,2ke k2e& ke2,B ake,2 5k9e 5k:e ,5keB :e5ak 92Me ke", +"&M2 ,Tke2 aMe92 ,5le l5:e le95T lse:5 ke,; ke-; ke;,B -kae; ke;,1 :e>k keB,", +"1; >k-ae Me9; -kMe; ke;,T -Tke; ,;le >el- le;,T l>es- ,2Gk ke&G2 ,GkH2 ke&H", +"2 G5,k :G5,k ,G5Hk :He5k ,GkM2 ke2GM& ,GTk2 ak2,GT ,Gl5 l:G,5 lG,H5 :Hel5 ,", +"kf; f;-k f9Hk; f-kg; ,5fk >k:f f95Hk f>kg: 9Mf; f-kM; f9Tk; f-Tk; f9l; l>:f", +" lf9H5 glf:> ,Nke kNe& keN,B ake,N keN,1 ke:N ,1NkeB :eakN Qk9e keQ& ,QkTe ", +"ake,Q ,Qle :Qle le9QT :Qels ke*,N ke-N 9eNk*B -kaeN ,1ek*N :-keN ke1*,NB -k", +"a:eN ,Qke* Qk-e Qke9T* -QkTe le9Q* -Qle 9Qel*T l-sQe Gk,W keW& ,GkHW ake,W ", +",1GWk Wk:e 1He,Wk :HeWk GQ,k ke&GQ ,GZk keZ& ,GlW :Wle Z9le leZ: ,Wfk -Wfk ", +"f9HWk f-kgW f91Wk fk:W ,1HfkW :fgWk ,Qfk -Qfk f9Zk f-Zk f9lW lW:f l,Zf Z:lf", +" ke,m emk& kem,B ake,m kem,1 ke:m keB,1m :emak Me9m kemQ& kem,T aMe9m p9le ", +"le:p lep,T :pels kem,; ke-m keB9m* -kmae ,1mke; :-mke ke,m1;B :ea-km ke;,Q ", +"-Qkem ,Tmke; -Tkem lep,; le-p leTp9* l-pse Gk,m kemG& kem,H akm,G ,1Gkm :Gm", +",k ,1Hkem :Hekm Mem9G ,GQkm& Z9ekm Zkem& pGl, :pGle lZep9 Z:elp v9fk fk-v f", +"v9Hk -vkgf fv95k fk:v f1Hvk, :fvgk v9fM -vkfQ Zf9vk Z-kfv l,fp :flv fp9lZ l", +"fy: Uek U!ke Dke akUe 1Ue U81e Ue1D Ua1e keIM IkMe! Vek akVe lIe U8le leV s", +"Ile Ik* +kUe IkD* Dk+e Ue1* U+1e Ik*1D ak*1I k*IM Ik*+M IkV* +kVe lI* U+le ", +"Vel* sIl* GkUe Ik!Ge Jke akJe UG1e 1UG8e 1UJe 8kJe IkMGe UGkMe! VeJk JkVae ", +"UGle lGU8e lJe lesJ fUk U+fk Jfk Jfgk f1U 1Uf8 f1Jk f1gU IkfM fU+kM Vfk gkV", +"f lfU fUl+ lfJ Vfgl U2ke Ik2e! D2ke akeU2 U51e 1Ue58 Dk5e akeU5 IkMe2 ke2IM", +"! keV2 Veka2 U5le lI58e V5le seVl5 keU; Ik;+e keD; akeU; Ue1; 1U>e Ik*5D Dk", +">e Ik;Me +IkMe; keV; V+ek; Uel; lI>e Vel; leV> Ik2Ge UG2ke! keJ2 Jkae2 1UG5", +"e G5kU8e 5kJe J5k8e UGkMe2 IkGMe2! JkVe2 VeaJk2 lGU5e UG5le8 J5le lJs5e U;f", +"k fU+k; fkJ; Jfkg; 1Uf5 fU>k f5Jk Jf>k fUkM; Ik;fM+ V;fk Vfgk; fUl; lIf> l;", +"Vf J>lf UNke IkNe! DNke akeUN 1NUe 1Ue8N 1UeDN 1UaeN QkUe UQek! QkVe VeQak ", +"UQle lIQ8e lQVe seVlQ INk* Ik*+N Ik*DN ak*IN Ik*1N Ik*8N IkN1D* akN1I* IkQ*", +" Ik*+Q VeQk* V+eQk QIl* l+IQe lI*VQ sI*lQ WkUe UWek! WkJe JkWae UW1e 1UW8e ", +"J1UWe J8kWe UGQke Ik!WMe JkZe ZJkae UWle lWI8e ZJle lJZse UWfk fU+Wk JWfk J", +"fkgW 1UfW f1U+W Jf1UW Jf8Wk UQfk fUQ+k ZkVf VfZgk fUlW lfU+W ZflJ glJZf keU", +"m Ikme! keDm akeUm Ue1m Ikm8e kem1D aem1U IkmQe Ik!Mem keVm Vemak pUle lIp8", +"e leVp Vpels kmI* Ikm+e IkmD* akmI* Ikm1* >kIem 1Dekm* >kDem IkmQ* U+Qkem V", +"emk; V+ekm pIl* l>Ipe Vpel; V>elp IkmWe Im!Wke keJm Jkmae 1UGem UG8kem J1mU", +"e J8kem UGQkem ImQkWe! ZJkem ZkaVem lGpUe lG8pUe leJp Jplse vUfk fvU+k fvJk", +" Jvkgf v1fU f>Uv1 Jv1fU J>kfv fvUQk fQ+vIk fkVv Vfvgk lIfp fpUl> Jplf lfyJ ", +"ht ht& htD htc ht1 h:t t1hD t:hc htM tMh& Vht tcVh lh lh: lhV uh ht* t-h hD", +"t* hct- t*h1 t-h: t*1hD h:tc- t*hM hMt- t*Vh Vht- lh* l-h l*Vh uh- htG h&tG", +" tJh hctJ tGh1 tGh: h1tJ tJh: tGhM tGMh& VhtJ tJVhc lhG lGh: lJh uJh tf tf-", +" tfJ it tf1 tf: J1tf it: tfM fMt- tfV iVt lfh tfl- lJtf ui ht2 t&h2 tDh2 h2", +"tc ht5 t:h5 hDt5 tch5 tMh2 htM&2 h2Vt Vhtc2 lh5 h5l: t5Vh uh5 ht; h;t- hDt;", +" tch; t5h; h>t t5*hD htc> t;hM t-hM; t;Vh V-ht; lh; h>l l;Vh uh> h2tG tG&h2", +" h2tJ tJhc2 tGh5 t:Gh5 h5tJ tJ5h: tGMh2 hM&tG2 tJVh2 VhctJ2 h5lG lh:G5 h5lJ", +" hJu5 tf; f-t; h;tJ it; tf5 tf> Jft5 it> fMt; tf-M; t;Vf t;iV l;tf h>lf hJl", +"; ui> htN t&hN tDhN hNtc t1hN hNt: ht1DN h:tcN htQ h&tQ tQVh tchQ lhQ hQl: ", +"VlhQ uhQ hNt* hNt- t*DhN t-hcN t*1hN h:t-N h1Dt*N t-ch:N t*hQ hQt- tQ*Vh V-", +"htQ hQl* hQl- lhVQ* h-uQ htW h&tW hWtJ tchW tWh1 t:hW tJh1W h:WtJ tGhQ tGQh", +"& hZt htZc lhW lWh: hZl uZh tfW hWt- JftW itW f1tW tW:f tfJ1W t:iW tfQ f-tQ", +" tZf iZt lWtf h-lW hZlf uiZ htm h&tm hDtm tchm t5hm t:hm ht5Dm h:mtc tQhm h", +"tQm& tmVh Vhmtc hpl h:tp Vplh uph tmh; hmt- tm*hD t-mhc t5*hm hmt> tmDh1; h", +">tcm tQh; t-Qhm Vhmt; V-mht l;hp hpl- lh;Vp hpu- tGhm tGmh& hmtJ tJmhc t5hW", +" h:Wt5 tJ5hW :Jmht tGQhm tG&hMm hmtZ hZtcm lGhp lhW:p tZhp hZup hvt t-hv tJ", +"hv itv h1tv tvh> hvtJ1 tvi: hQtv hvt-Q tZhv tZiv lvh hvl- hZlv uiy 3 !3 3B ", +"a3 3. 83 .B3 8a3 3K !K3 T3 Ta3 j3 8j3 Tj3 s3 3( +3 3B( +a3 3(. +83 3(.B 8a+", +"3 3(K +3K T3( +T3 j3( +j3 j3T( s+3 E3 !E3 H3 Ha3 E3. 8E3 H3. H83 EK3 !3EK H", +"T3 TaH3 jE3 jE83 Hj3 sH3 d3 +d3 Hd3 g3 d3. 8d3 d3H. g83 dK3 dK+3 Td3 gT3 dj", +"3 +3dj H3dj gs3 4 4! 4B 4a 45 48 45B 48a 4K 4K! 4T 4Ta 4j 48j 4T5 s4 4; 4+ ", +"4;B 4+a 45; >4 5;4B >4a 4;K 4+K 4T; 4+T 4j; >j4 T;4j s>4 4E 4E! 4H 4Ha 45E ", +"48E 4H5 4H8 4EK EK4! 4HT HT4a 4jE 8j4E 4Hj s4H 4d 4+d 4Hd g4 4d5 >4d H54d g", +">4 4dK +d4K 4Td g4T 4dj 4d>j H;4j gs4 3N !3N 3BN a3N N.3 83N 3BN. aN83 Q3 Q", +"!3 QT3 Qa3 Qj3 Q83 TjQ3 sQ3 3(N +3N 3(NB a3+N 3(N. 83+N 3(N.B +8a3N Q3( +Q3", +" T3Q( +3Qa j3Q( +3Q8 QTj3( +Qs3 W3 W!3 HW3 Wa3 W3. W83 W3H. H3W8 WQ3 W3Q! Z", +"3 Za3 Wj3 W3Q8 Zj3 sZ3 Wd3 +W3 WdH3 gW3 d3W. +3W8 HWd3. W8g3 Qd3 +3Qd Zd3 g", +"Z3 W3dj +3Wj djZ3 g3sZ 4m 4m! 4mB 4am 45m 48m 5m4B 8m4a 4Q 4Q! 4QT 4Qa p4 p", +"48 p4T sp4 4m; 4+m m;4B +m4a 5m4; >m4 45m;B 4a>m 4Q; 4+Q Q;4T +Q4T p4; p>4 ", +"4Tp; p>s4 4W 4W! 4HW 4Wa 4W5 4W8 H54W H84W 4WQ Q!4W Z4 Z4a p4W 4Wp8 Zp4 sZ4", +" v4 v4+ v4H gv4 v45 v>4 4Hv5 v>g4 v4Q 4+vQ Zv4 gZ4 pv4 p4v> pvZ4 y4 #3 &3 #", +"B3 a&3 #3. 8&3 #3.B a&83 #3K &3K T#3 T&3 j#3 &j3 j#T3 s&3 #3( +&3 #3(B a&+3", +" 3(#. 8&+3 .B3#( +8a&3 3(#K &3+K #3T( +3T& #3j( +3&j Tj#3( +&s3 #E3 &E3 H#3", +" H&3 E3#. &E83 #3H. 8&H3 #3EK EK&3 T#H3 H3T& #Ej3 jE&3 j#H3 H&s3 d#3 d&3 d#", +"H3 g&3 #3d. 83d& Hd#3. 8&g3 #3dK &3dK d#T3 T&g3 j#d3 d3&j dj#H3 s&g3 4# 4& ", +"4#B 4a& 45# 48& 5B4# 5a4& 4#K 4&K 4T# 4T& 4j# 4&j 4#5T s4& 4;# 4+& ;#4B a;4", +"& 5;4# >&4 45;#B 4a>& ;#4K 4K;& 4#T; +T4& 4#;j 4&>j ;j#4T >&s4 4#E 4&E 4H# ", +"4H& 5#4E 4E5& 4#H5 H54& EK4# &E4K HT4# H&4T j#4E 4E&j 4#Hj 4Hs& 4d# 4d& 4#H", +"; g4& 4#5d 4d>& 4H5d# >&g4 dK4# 4Kd& 4#Td 4Tg& 4#dj d&4j 4Hjd# g&s4 #3N &3N", +" #3NB &3aN #3N. &38N 3BN#. 8a&3N Q#3 Q&3 T#Q3 Q3T& j#Q3 Q3&j QTj#3 Q&s3 3(#", +"N &3+N 3(N#B +a&3N 3(N#. +8&3N 3B(N.# 8a&+3N #3Q( +3Q& QT#3( +QT&3 Qj#3( &j", +"3+Q Tj#Q3( s+Q&3 W#3 W&3 W#H3 H3W& #3W. 8&W3 HW#3. H8W&3 Q#W3 W3Q& Z#3 Z&3 ", +"j#W3 W3&j j#Z3 s3Z& d#W3 +3W& HWd#3 W&g3 Wd#3. d&3W8 Wd#H3. gW8&3 d#Q3 Q3d&", +" d#Z3 g3Z& dj#W3 d&jW3 Zdj#3 Z&3gs 4m# 4&m m#4B am4& 5m4# 8m4& 45m#B am&48 ", +"4Q# 4Q& 4#Tm Qa4& p4# p&4 4Tp# p&s4 m;4# +m4& 4m;#B am&4+ 45m;# 4&>m 5m;4#B", +" >m4a& 4#Q; +Q4& 4QT;# 4+QT& 4;p# p4>& p4T;# p>4s& 4W# 4W& 4#Hm Hm4& 4#5W 5", +"W4& 4H5W# 4H8W& WQ4# W&4Q Z4# Z4& 4Wp# 4Wp& p4Z# Z&s4 v4# v&4 4Hv# v&g4 45v", +"# v4>& v4H5# gv>4& 4Qv# 4Qv& v4Z# Z&g4 v4p# p4v& Zpv4# y&4 C3 !C3 D3 Da3 C3", +". 8C3 D3. D83 CK3 !3CK DT3 TaD3 jC3 jC83 Dj3 sD3 C3( +C3 D3( +D3 3(C. 8C+3 ", +"3(D. +3D8 3(CK CK+3 T3D( DT+3 C3j( jC+3 j3D( +Ds3 CE3 CE!3 HD3 DaH3 E3C. CE", +"83 D3H. H3D8 C3EK EKC!3 DTH3 HDTa3 CEj3 8jCE3 H3Dj HDs3 dC3 dC+3 Dd3 gD3 C3", +"d. dC83 d3D. D8g3 CKd3 +dC3K D3Td DTg3 jCd3 djC+3 D3dj sDg3 4C 4C! 4D 4Da 4", +"5C 48C 4D5 4D8 4CK CK4! 4DT DT4a 4jC 8j4C 4Dj s4D 4;C 4+C 4D; 4+D 5;4C >C4 ", +"45D; >D4 ;C4K +C4K D;4T +D4T 4C;j 4j>C D;4j >Ds4 4CE !C4E 4HD HD4a 5C4E 8C4", +"E H54D H84D EK4C 4CE!K HD4T 4HDTa jC4E 48jCE Hj4D 4HsD 4dC +d4C 4Dd g4D 4C5", +"d 4d>C 5D4d >Dg4 dK4C 4+dCK Dd4T 4DgT 4Cdj >j4dC Dd4j gDs4 C3N C3!N D3N a3D", +"N C3N. C38N N.D3 83DN QC3 !CQ3 QD3 DaQ3 jCQ3 8CQ3 Q3Dj QDs3 3(CN C3+N 3(DN ", +"D3+N C3N(. +8C3N D3(N. +D83N C3Q( QC+3 D3Q( +3QD QjC3( +Q8C3 QDj3( s+QD3 WC", +"3 !CW3 WD3 DaW3 C3W. 8CW3 D3W. W3D8 QCW3 Q!WC3 ZD3 DaZ3 jCW3 W8QC3 DjZ3 ZDs", +"3 dCW3 WC+3 W3Dd WDg3 WdC3. +W8C3 WDd3. gWD83 dCQ3 +WQC3 DdZ3 ZDg3 djCW3 +W", +"jC3 ZDdj3 gZsD3 4mC mC4! 4Dm Dm4a 5m4C 4C8m 45Dm Dm48 4QC 4CQ! 4QD QD4a p4C", +" 48pC pD4 pDs4 m;4C 4C+m 4mD; +m4D 45m;C 4m>C 4D5m; 4D>m 4CQ; 4C+Q Q;4D +Q4", +"D 4;pC p4>C 4Dp; p4>D 4WC W!4C 4WD WD4a 4C5W 4CW8 5W4D WD48 WQ4C 4WQ!C Z4D ", +"4DZa 4WpC p4W8C pDZ4 sDZ4 v4C 4+vC vD4 vDg4 45vC v4>C 4Dv5 v4>D 4QvC v4+QC ", +"vDZ4 gDZ4 v4pC pv>4C p4vD y4D b3 b&3 bD3 c3 b3. b83 D3b. c83 bK3 &3bK bT3 c", +"T3 bj3 b3&j b3Dj sc3 b3( b+3 D3b( c+3 3(b. +8b3 bD3(. +8c3 3(bK +3bK T3b( +", +"Tc3 j3b( b3+j bTj3( c+s3 bE3 &Eb3 bH3 cH3 E3b. 8Eb3 H3b. H8c3 EKb3 bK&E3 HT", +"b3 HTc3 jEb3 &jEb3 b3Hj cHs3 bd3 b3d& b3Dd gc3 d3b. b38d bHd3. c8g3 dKb3 d&", +"3bK b3Td cTg3 b3dj d&jb3 bHjd3 g3sc 4b 4b& 4bD c4 4b5 4b8 b54D c45 4bK bK4&", +" 4bT c4T 4bj b84j b54T sc4 4b; 4b+ b;4D c4+ 45b; >b4 4b5D; c>4 4Kb; 4Kb+ b;", +"4T 4+cT b;4j 4b>j 4bT;j s4c> 4bE b&4E 4bH c4H 4Eb5 4Eb8 b54H 4Hc5 bK4E 4b&E", +"K bH4T 4HcT 4Ebj 4b8jE bH4j sHc4 4bd b+4d bH4d gc4 b54d 4b>d 4bH5d g4c> 4Kb", +"d 4b+dK bT4d gTc4 bd4j >b4dj 4bHdj scg4 b3N &3bN D3bN c3N N.b3 83bN bD3N. 8", +"3cN bQ3 b3Q& b3QD cQ3 Qjb3 b3Q8 bQDj3 s3cQ 3(bN +3bN bD3(N +3cN b3(N. b+83N", +" D3(bN. c+83N Q3b( b3+Q bQD3( +Qc3 bQj3( b+Q83 QD3bj( cQ+s3 bW3 b3W& b3WD c", +"W3 W3b. b3W8 bHW3. W8c3 WQb3 bWQ&3 Zb3 Zc3 b3Wj bW8Q3 bjZ3 s3Zc Wdb3 b3+W b", +"HWd3 g3cW bWd3. b+W83 WD3bd. cW8g3 b3Qd b+WQ3 bdZ3 g3Zc bWjd3 b+Wj3 Zbdj3 Z", +"cgs3 4bm bm4& bm4D c4m 45bm bm48 4b5Dm 48cm 4bQ bQ4& bQ4D c4Q pb4 4bp& 4bpD", +" cp4 4mb; bm4+ 4bDm; 4+cm 4b5m; 4b>m b5m4D; >mc4 b;4Q b+4Q 4bQD; 4+cQ 4bp; ", +"p4>b pb4D; p>c4 4bW bW4& bH4W c4W b54W bW48 4bH5W 4Wc5 bW4Q 4bWQ& Z4b Zc4 4", +"bpW pb4W8 pbZ4 s4Zc vb4 4bv& 4bvD cv4 4bv5 v4>b vb4H5 v>c4 4bvQ vb4+Q vbZ4 ", +"g4Zc p4vb pvb>4 Zpbv4 yc4 6 6! 6B 6a 61 68 61B 68a 6K 6!K 6T 6Ta 6j 68j 6T1", +" s6 6( 6+ 6(B 6+a 61( 6+1 1(6B 1+6a 6(K 6+K 6T( 6+T 6j( 6+j 6(1T s6+ 6E 6!E", +" 6H 6Ha 61E 68E 6H1 6H8 6EK EK6! 6HT HT6a 6jE 8j6E 6Hj s6H 6d 6+d 6Hd g6 6d", +"1 68d 1H6d g61 6dK +d6K 6Td g6T 6dj +j6d 1T6d gs6 46 6!4 46B 4a6 7 78 7B 7a", +" 46K 4K6! 4T6 6T4a 7j 78j 7T 7s 6; 4+6 6;B 6a; 7; 7> 7B; 7>a 6;K 4K6+ 6T; 6", +"+4T 7;j 7>j 7T; 7s> 46E 4E6! 4H6 6H4a 7E 78E 7H 7H8 6E4K 6!4EK 6H4T 4H6Ta 7", +"jE 8j7E 7HT 7sH 4d6 6+4d 6H; g46 7d 7>d 7Hd g7 4K6d 4+6dK 6T4d 4Tg6 7dj >j7", +"d 7Td g7s 6N 6!N 6NB 6aN 61N 68N 1N6B 6N1a 6Q 6Q! 6QT 6Qa 6Q1 6Q8 1Q6T s6Q ", +"6(N 6+N (B6N +a6N 1(6N 6N1+ 61(NB 6+1aN 6Q( 6+Q QT6( +Q6T 6(1Q 1+6Q 6Q1T( 6", +"+sQ 6W 6W! 6HW 6Wa 6W1 6W8 1H6W 1W6a 6WQ Q!6W Z6 Z6a 6Wj W86Q Z61 sZ6 6Wd 6", +"+W HW6d g6W 1W6d 1+6W 6H1Wd 6Wg1 6Qd +W6Q Z6d gZ6 1Q6d +W6j 6dZ1 sZg6 6m 6m", +"! 6mB 6am 7m 78m 7Bm 7am 4Q6 Q!6m 6Tm 6Q4a 7p 7p8 7pT 7sp 6m; 6+m m;6B +m6a", +" 7m; 7>m m;7B >m7a 6Q; 6+4Q Q;6T +T6m 7p; 7p> pT7; 7ps> 4W6 6!4W 6Hm 6W4a 7", +"W 7W8 7HW 7Wa 6W4Q 4WQ6! Z46 4aZ6 7pW pW78 7Z 7Zs v6 v6+ v6H gv6 7v 7v> 7vH", +" g7v v6Q 6+vQ Zv6 Zvg6 7vp pv7> 7Zv y7 69 6& 69B 6a9 691 :6 916B :6a 69K 6&", +"K 6T9 6T& 6j9 :6j 9T6j s:6 69( 6+9 9(6B 9+6a 916( :6+ 691(B 6+:a 9(6K 6K9+ ", +"6(9T 9+6T 6(9j 6+:j 6T91( :6s+ 69E 6&E 6H9 6H& 916E :6E 691H :H6 9E6K &E6K ", +"9H6T H&6T 6E9j 6j:E 9H6j :Hs6 6d9 6d& 9H6d g69 691d :d6 6H91d g:6 6K9d 6Kd&", +" 9T6d 6Tg9 9d6j 6d:j 6Hj9d s:g6 49 49& 49B 49a 79 7: 7B9 7:a 49K 4K6& 49T 6", +"T4& 79j 7:j 7T9 7s: 49; 49+ 4B9; 9+4a 79; 7:> 9;7B :>7a 4K9; 4K9+ 9T6; 9+4T", +" 9;7j >j7: 9T7; 7:s> 49E 4E6& 49H 6H4& 79E 7:E 7H9 7:H 9E4K 49&EK 9H4T 49HT", +"& 9j7E :j7E 9H7T 7Hs: 49d 6d4& 9H4d g49 7d9 7:d 9H7d g7: 4K9d 49+dK 9T4d 49", +"gT 9d7j 7j:d 9T7d 7sg: 69N 6&N 9N6B 6N9a 916N :6N 691NB 6a:N 6Q9 6Q& 9Q6T 9", +"Q6a 9Q6j :Q6 6Q91T :Qs6 9(6N 6N9+ 69(NB 6+9aN 691(N 6+:N 91(6NB :6+aN 6(9Q ", +"9+6Q 6Q9T( 6+Q9T 6Q91( 6+:Q 9Q16T( :Q6s+ 6W9 6W& 9H6W 9W6a 691W :W6 6H91W 6", +"H:W 9W6Q W&6Q Z69 Z6& 9W6j 6W:Q 6jZ9 Z:6 9W6d 9+6W 6H9Wd 6Wg9 6W91d 6+:W 9H", +"16Wd :Wg6 9Q6d Qd6& 6dZ9 Z6g9 6Wj9d 6Q:d Z691d g6Z: 49m 6m& 4B9m 9a6m 79m 7", +":m 9m7B :m7a 49Q 6Q4& 9Q4T 9Q4a 7p9 :p7 p97T :p7s 9m6; 9+6m 49m;B 49+am 9m7", +"; :m7> 7B9m; 7:>am 9Q6; 9+4Q 49QT; 49+Qa 7;p9 7>:p 7p9T; 7p>s: 49W 6W4& 9H4", +"W 9W4a 7W9 7:W 9H7W :H7W 9W4Q 49WQ& Z49 49Z& p97W 7W:p 7Z9 7Z: v69 v6& 49vH", +" v6g9 7v9 :v7 v97H :vg7 49vQ 6Qv& v6Z9 Z4g9 p97v :p7v Z97v y7: 6C 6!C 6D 6D", +"a 61C 68C 6D1 6D8 6CK CK6! 6DT DT6a 6jC 8j6C 6Dj s6D 6C( 6+C 6D( 6+D 1C6( 6", +"C1+ 6(1D 1+6D CK6( +C6K DT6( +D6T jC6( 6C+j 6(Dj 6+sD 6CE !C6E 6HD HD6a 1C6", +"E 8C6E 1H6D H86D EK6C 6!CEK HD6T 6HDTa jC6E 68jCE Hj6D 6HsD 6dC +d6C 6Dd g6", +"D 6C1d 6C8d 1D6d 6Dg1 dK6C 6+dCK Dd6T 6DgT 6Cdj 6+jdC Dd6j gDs6 46C 4C6! 4D", +"6 6D4a 7C 78C 7D 7D8 6C4K 6!4CK 6D4T 4DT6a 7jC 8j7C 7DT 7sD 6;C 4C6+ 6D; 6+", +"4D 7;C 7>C 7D; 7>D ;C6K 4+6CK D;6T 4+D6T ;j7C 7j>C D;7T 7Ds> 6C4E 6!4CE 6H4", +"D 4H6Da 7CE 8C7E 7HD H87D 46CEK 4CE6!K 4H6DT 6HD4Ta jC7E 78jCE Hj7D sH7D 4C", +"6d 4+6dC 6D4d 4Dg6 7dC >C7d 7Dd g7D 4d6CK 6+C4dK 4Dd6T g46DT dj7C 7>djC Dd7", +"T 7Dgs 6CN !C6N 6DN Da6N 1C6N 8C6N 6N1D 6ND8 6QC 6CQ! 6QD QD6a 6C1Q 6CQ8 1Q", +"6D 6QsD C(6N +C6N D(6N 6N+D 61C(N 6+1CN 6D1(N 6+D1N QC6( 6C+Q 6(QD +Q6D 6Q1", +"C( 6+Q1C 6QD1( s6+QD 6WC W!6C 6WD WD6a 6C1W 6CW8 1W6D WD68 WQ6C 6WQ!C Z6D 6", +"DZa 6CWj 6W8QC 6DZ1 sDZ6 Wd6C 6C+W WD6d 6WgD 6W1dC 6+W1C 6WD1d g61WD 6CQd 6", +"+WQC 6DZd gDZ6 6WjdC 6+WjC Z61Dd gZ6sD 6mC mC6! 6Dm Dm6a 7mC 8m7C 7Dm D87m ", +"4C6Q 4Q6!C 6Q4D 4QD6a 7pC p87C 7pD 7Dsp m;6C 6C+m Dm6; +D6m m;7C 7m>C Dm7; ", +">m7D 6CQ; 4+Q6C QD6; 4+Q6D p;7C >C7p 7;pD p>7D 4C6W 4W6!C 6W4D 4WD6a 7WC W8", +"7C 7WD WD7a 4WQ6C 6WC4Q! 4DZ6 Z46Da pW7C 7pW8C 7ZD 7DsZ v6C 6+vC v6D vDg6 7", +"vC >C7v 7vD 7Dgv 6QvC v6+QC vDZ6 Zv6gD 7Cpv 7p>vC 7DZv y7D b6 b6& b6D c6 b6", +"1 :b6 6Db1 :c6 b6K bK6& b6T c6T b6j b6:j 6Dbj sc6 b6( b6+ b(6D c6+ 61b( b6:", +"+ b61D( :+c6 6(bK bK6+ b(6T 6+cT b(6j 6+bj b6T1( s+c6 b6E bE6& b6H c6H 61bE", +" b6:E 6Hb1 c6:H 6EbK b6&EK 6HbT 6HcT bE6j :b6jE 6Hbj sHc6 b6d 6+bd 6Hbd gc6", +" 6db1 b6:d b6H1d :cg6 bK6d b6+dK 6Tbd gTc6 6dbj :db6j b6Hdj scg6 4b6 b64& 4", +"9D c46 7b 7:b 7bD 7c 4Kb6 4b6&K b64T 49cT 7bj :b7j 7bT 7cs b6; b64+ 6Db; c6", +"; 7b; 7>b b;7D 7c> bK6; 4b+6K 6Tb; 6Tc; b;7j >j7b bT7; c>7s 4Eb6 4b6&E b64H", +" 49cH 7bE 7E:b 7bH 7cH 4b6EK b6E4&K 4bH6T c46HT bj7E 7:bjE bH7T 7Hsc b64d 4", +"b+6d 6Hb; c4g6 7bd :d7b bH7d g7c 4bd6K d&Kb6; 4bT6d c6;gT bd7j 7:dbj bT7d g", +"s7c b6N bN6& bN6D c6N 61bN b6:N b61DN cN:6 b6Q 6&bQ 6QbT c6Q 6Qb1 b6:Q b6Q1", +"D c6:Q 6(bN bN6+ b6D(N 6+cN b61(N :b6+N 6D(b1N :c6+N b(6Q 6+bQ b6QD( 6+cQ b", +"6Q1( :Qb6+ b6(1QD :cQ6+ b6W 6&bW 6HbW c6W 6Wb1 b6:W b6H1W c6:W 6WbQ b6WQ& Z", +"b6 Zc6 6Wbj :Wb6Q b6Z1 s6Zc 6Wbd 6+bW b6HWd cWg6 b6W1d :Wb6+ 6WDb1d g:c6W 6", +"Qbd b6+Qd b6Zd g6Zc b6Wdj :Qdb6 Zb61d Zc6g: b6m 6&bm 6Dbm c6m 7bm :m7b bm7D", +" 7cm b64Q 4bQ6& 6Tbm 49cQ 7pb 7b:p pb7D 7cp 6mb; 6+bm 49Dm; 6+cm bm7; >m7b ", +"7bDm; cm7> 6Qb; 4b+6Q 49QD; 6Qc; 7;pb p>7b 7pbD; 7pc> b64W 4bW6& 6Hbm 49cW ", +"7bW :W7b bH7W 7cW 4bW6Q b6W4Q& 4bZ6 c4Z6 pb7W 7pb:W 7Zb 7Zc vb6 b6v& 49vD c", +"v6 7vb 7b:v vb7H 7cv b6vQ vb6+Q vbZ6 Z6cv pv7b :vb7p Zv7b y7c 3L !3L 3BL a3", +"L 3L. 83L 3L.B a38L M3 M3! TM3 aM3 Mj3 8M3 MjT3 sM3 3(L +3L 3(LB a3+L (L3. ", +"83+L 3L.(B +8a3L M3( +M3 M3T( +3aM j3M( +38M TMj3( +Ms3 E3L E3!L H3L a3HL 3", +"LE. E38L 3LH. 83HL ME3 !EM3 HM3 H3aM jEM3 ME83 MjH3 HMs3 d3L d3+L d3HL g3L ", +"3Ld. d38L Hd3L. 83gL dM3 +3dM H3dM gM3 Mjd3 83dM dMjH3 sMg3 4L 4L! 4LB 4aL ", +"45L 48L 5B4L 4L5a 4M 4M! 4TM 4aM 4M5 48M 5T4M s4M 4;L 4+L ;L4B 4La; 5;4L >L", +"4 45;LB 4a>L 4M; 4+M T;4M +T4M M;4j >M4 4T5M; s4>M 4EL !E4L 4HL Ha4L 5E4L 8", +"E4L 4LH5 4LH8 4ME ME4! 4HM HM4a 4E5M 4E8M H54M 4HsM 4dL +d4L 4LH; g4L 4L5d ", +"4d>L 4H5dL >Lg4 4dM +M4d HM4d g4M 5d4M 4d>M 4HM5d g4>M 3LN 3L!N 3LNB 3LaN 3", +"LN. 3L8N 3L.NB 8a3LN QM3 M3Q! TMQ3 Q3aM MjQ3 Q38M QTMj3 QMs3 (L3N 3L+N 3(NL", +"B +a3LN 3L.(N +83LN .BL3(N 8a3+LN M3Q( QM+3 QTM3( aM3+Q QMj3( +Q8M3 TMjQ3( ", +"s+QM3 W3L !3WL W3HL a3WL 3LW. 83WL HW3L. H8W3L WM3 M3W! ZM3 aMZ3 MjW3 W38M ", +"MjZ3 ZMs3 d3WL W3+L HWd3L W3gL Wd3L. +W83L Wd3HL. gW83L W3dM +3WM dMZ3 ZMg3", +" dMjW3 +WM83 ZdMj3 gZsM3 4mL mL4! mL4B 4Lam 5m4L 4L8m 45mLB amL48 4QM Q!4M ", +"Tm4M Qa4M p4M 48pM 4TpM p4sM m;4L 4L+m 4m;LB amL4+ 45m;L 4m>L 5m;4LB >m4aL ", +"Q;4M +Q4M 4QTM; aMm4+ 4Mp; p4>M p4TM; >Mps4 4WL W!4L 4LHm 4LWa 4L5W 4LW8 4H", +"5WL 4H8WL 4WM 4!WM Z4M 4aZM 4WpM W84M p4ZM sMZ4 v4L 4+vL 4HvL v4gL 45vL v4>", +"L v4H5L gv>4L vM4 4+vM vMZ4 Z4gM p4vM v4>M Zpv4M y4M #3L &3L #3LB &3aL 3L#.", +" &38L 3L.#B 8a&3L M#3 M&3 M#T3 T3M& j#M3 83M& TMj#3 M&s3 3(#L &3+L (BL#3 +a", +"&3L 3L.#( +8&3L 3B(#L. 8a&+3L #3M( +3M& TM#3( aM3+& Mj#3( M&3+j Mj#T3( s+M&", +"3 E3#L E3&L #3HL &3HL 3L.#E 8&E3L H#3L. H8&3L #EM3 &EM3 M#H3 H3M& Mj#E3 M&E", +"83 HMj#3 sHM&3 #3dL &3dL Hd#3L &3gL d#3L. d&38L d#3HL. g8&3L M#d3 d3M& dM#H", +"3 M&g3 dMj#3 dM&83 HM#dj3 gsM&3 4#L 4&L #B4L a&4L 5#4L 4L5& 45#LB 48a&L 4M#", +" 4M& TM4# T&4M 4#5M 5M4& 4T5M# 4Ms& ;#4L 4L;& 4;#LB a;&4L 45;#L 4&>L 5;#4LB", +" >&4aL 4#M; +M4& M;#4T aM;4& M;#4j 4M>& 4T5M;# >Ms4& #E4L &E4L H#4L 4LH& 45", +"#EL 48&EL 4H5#L 4H8&L M#4E 4EM& 4#HM HM4& 4M5#E 48M&E 4HM5# s4HM& d#4L 4Ld&", +" 4Hd#L 4&gL 4d5#L >&4dL H5#4dL g>4&L 4#dM dM4& 4HMd# 4Mg& 4dM5# >M4d& dM#4H", +"j >Mg4& 3L#N 3L&N #LN3B a&3LN 3L.#N 8&3LN #L.3BN a&38LN M#Q3 Q3M& QTM#3 aM3", +"Q& QMj#3 M&3Q8 TMjQ#3 sQM&3 3(N#L +&3LN 3B(#LN a&3+LN #L.3(N 8&3+LN 3LNB#(.", +" 3L+N8a& QM#3( M&3+Q TM#Q3( +QTM&3 Mj#Q3( +Q8M&3 TMQj#3( M&3s+Q #3WL &3WL H", +"W#3L H&W3L W#3L. W8&3L W#3HL. W83H&L M#W3 W3M& M#Z3 M&Z3 WMj#3 W8M&3 ZMj#3 ", +"Z&Ms3 Wd#3L d&3WL Wd#H3L gW&3L d#3WL. W83d&L W3HLd#. W8&g3L dM#W3 dM&W3 ZdM", +"#3 Z&Mg3 WM#dj3 dM3W&j dMjZ#3 gsMZ&3 m#4L 4Lm& 4m#LB am&4L 45m#L m&L48 5m#4", +"LB 48am&L 4#Mm Q&4M Mm#4T aMm4& 4Mp# 4Mp& p4TM# p&4sM 4m;#L m&L4+ m;#4LB 4+", +"am&L 5m;4#L >m4&L mL4B5;# am&>L4 Mm#4; Mm&4+ 4QTM;# 4+TMm& p4M;# >Mp4& M;#p", +"4T p&4>Ms W#4L 4LW& 4HW#L 4H&WL 4W5#L 4W8&L H5#4WL m&L4H8 4#WM WM4& 4MZ# 4M", +"Z& p4WM# p&4WM Zp4M# Zp&4M 4#vL 4&vL v4H#L gv4&L v45#L v>4&L 4H5v#L v>&g4L ", +"4Mv# 4Mv& Zv4M# Zv&4M pv4M# >Mv4& pvMZ4# 4My& U3 U!3 UD3 Ua3 U3. U83 D3U. U", +"3D8 UM3 M3U! V3 Va3 Uj3 U38M Vj3 Vs3 U3( U+3 D3U( U3+D 3(U. +8U3 UD3(. U+D8", +"3 M3U( U3+M V3( V+3 j3U( U3+j j3V( s3V+ UE3 !EU3 UH3 HaU3 E3U. 8EU3 H3U. U3", +"H8 MEU3 U!ME3 VH3 HaV3 jEU3 U8ME3 HjV3 VHs3 Ud3 +dU3 U3Dd gU3 d3U. U38d UHd", +"3. U8g3 U3dM dM3U+ Vd3 gV3 U3dj U+jd3 djV3 g3Vs 4U 4U! 4UD 4Ua 4U5 4U8 U54D", +" U54a 4UM U!4M V4 V4a 4Uj U84M V45 s4V 4U; 4U+ U;4D U+4D 45U; >U4 4U5D; 4U>", +"D U;4M U+4M V4; V4+ U;4j 4U>M 4jV; V>4 4UE U!4E 4UH UH4a 4EU5 4EU8 U54H UH4", +"8 UM4E 4UM!E V4H 4HVa 4EUj 4U8ME 4HV5 sHV4 4Ud U+4d UH4d g4U U54d 4U>d 4UH5", +"d >Ug4 Ud4M 4U+dM V4d gV4 Ud4j >M4Ud 4dV5 g4V> U3N !3UN D3UN a3UN N.U3 83UN", +" UD3N. U8D3N UQ3 U3Q! VQ3 QaV3 QjU3 U3Q8 QjV3 VQs3 3(UN +3UN UD3(N U+D3N U3", +"(N. U+83N D3(UN. +D3U8N Q3U( U3+Q Q3V( +QV3 UQj3( U+Q83 VQj3( Vs+Q3 UW3 W!U", +"3 U3WD U3Wa W3U. U3W8 UHW3. UH8W3 U3WM UWQ!3 VZ3 VaZ3 U3Wj UW8Q3 VjZ3 s3VZ ", +"WdU3 U3+W UHWd3 UWg3 UWd3. U+W83 WD3Ud. gUW83 U3Qd U+WQ3 Z3Vd g3VZ UWjd3 U+", +"Wj3 VZdj3 gVZs3 4Um 4!Um Um4D Um4a 45Um Um48 4U5Dm 4U8Dm 4UQ Q!4U V4Q 4QVa ", +"pU4 4Up8 Vp4 s4Vp 4mU; Um4+ 4UDm; 4U+Dm 4U5m; 4U>m U5m4D; >Dm4U U;4Q U+4Q 4", +"QV; 4+Vm 4Up; p4>U p4V; p>V4 4UW 4!UW UH4W UW4a U54W UW48 4UH5W 4UHW8 UW4Q ", +"4UWQ! Z4V ZaV4 4UpW pU4W8 Z4Vp VZs4 vU4 4Uv+ 4UvD vUg4 4Uv5 v4>U vU4H5 gvU>", +"4 4UvM vU4+Q Vv4 g4Vv p4vU >Mv4U pvV4 yV4 bU3 U&3 UDb3 cU3 U3b. b3U8 bUD3. ", +"U8c3 bM3 b3M& Vb3 Vc3 b3Uj b38M bjV3 s3Vc U3b( b3U+ bUD3( U+c3 bU3(. b+U83 ", +"UD3b(. cU+83 M3b( b3+M b3V( c3V+ bMj3( b+M83 Vbj3( Vs+c3 UEb3 &EU3 b3UH UHc", +"3 bUE3. b8UE3 bHU3. cUH83 MEb3 M&Eb3 bHV3 VHc3 bMjE3 b8ME3 VbHj3 VsHc3 b3Ud", +" U3d& bHUd3 cUg3 bdU3. b8dU3 UH3bd. gcU83 b3dM dM&b3 bdV3 g3Vc bdMj3 d&jU3 ", +"Vdbj3 gVsc3 4bU 4U& bU4D c4U b54U b84U 4bU5D 4Uc5 4bM bM4& V4b c4V b54M b84", +"M 4bV5 Vsc4 b;4U b+4U 4bUD; 4Uc; 4bU5; 4b>U b5U4D; >Uc4 b;4M b+4M 4bV; V4c;", +" 4bM;j 4b>M V4b;j c4V> bU4E 4EU& bH4U 4UcH 4bU5E 4b8UE 4bHU5 c4UH5 4EbM 4bM", +"&E 4bVH cHV4 4bM5E 4b8ME V4bH5 c4VsH bd4U Ud4& 4bHUd c4gU 4bdU5 >b4Ud bH54U", +"d c>g4U bd4M dM&4b 4bVd c4gV 4bdUj >Mb4d V4db5 V>gc4 U3bN &3UN bUD3N U3cN b", +"U3N. b8U3N UD3bN. cU83N b3UQ U3Q& bQV3 VQc3 bQUj3 bQ8U3 VbQj3 cQVs3 bU3(N b", +"+U3N UD3b(N cU+3N U3(bN. U+3b8N D3UNb(. U+8c3N bQU3( b+QU3 VbQ3( cQV+3 UQ3b", +"j( &j3U+Q bQjV3( Vs+cQ3 b3UW U3W& bHUW3 UWc3 bWU3. bW8U3 UH3bW. cWU83 b3WM ", +"bWM&3 VbZ3 Z3Vc bWMj3 bW8M3 ZbVj3 ZcVs3 bWUd3 b+WU3 UHWbd3 cWUg3 UW3bd. d&3", +"UW8 W.DdbU3 gU8cW3 bWMd3 b+WM3 ZbVd3 gVZc3 dM3bWj dM3bW8 VdjZb3 ZcsgV3 bm4U", +" Um4& 4bUDm 4Ucm 4bU5m 4b8Um b5U4Dm c4U8m bQ4U UQ4& 4bVm V4cQ 4bpU 4Up& pbV", +"4 c4Vp 4bUm; 4b+Um bmU4D; c4U+m b5m4U; >b4Um 5mD;4bU c>4Um 4bQU; 4b+UQ V4bQ", +"; c4V+Q pb4U; >Mbp4 Vp4b; cpV>4 bW4U UW4& 4bHUW 4UcW 4bWU5 4bWU8 bH54UW c4W", +"U5 bW4M 4bWM& V4Zb VZc4 pb4UW pU&4W ZpbV4 cpZV4 4bvU 4Uv& vb4UH vUc4 vb4U5 ", +"v>b4U 4bHvU5 cv>4U 4bvM vbM4+ vbV4 c4Vv pvb4U >Mv4b Vvp4b c4yV 6k 6!k 6kB 6", +"ak 61k 68k 1k6B 8k6a 6M 6M! 6Tk 6aM l6 l68 l6T ls6 6k( 6+k k(6B +k6a 1k6( +", +"k68 61k(B 6+1ak 6M( 6+M 6(Tk +T6M l6( l+6 6Tl( s6l+ 6kE kE6! 6Hk Hk6a 1k6E ", +"6E8k 61Hk Hk68 6ME ME6! 6HM HM6a l6E 68lE lH6 s6lH 6dk +k6d Hk6d g6k 61dk 8", +"k6d 6H1dk 68gk 6dM +M6d HM6d g6M ld6 6+ld 6Hld gl6 4k 4k! 4kB 4ak 7k 78k 7B", +"k 7ak 4kM 6!4M 4Tk 6a4M 7l 7l8 7lT 7sl 4k; 4+k 4Bk; +k4a 7k; >k7 k;7B 7a>k ", +"6M; 6+4M Tk6; +T4k l;7 l>7 7Tl; 7sl> 4kE kE4! 4Hk Hk4a 7kE 8k7E 7Hk H87k 4E", +"6M 4kM!E 6H4M 4HkaM 7lE 78lE lH7 lH7s 4dk +k4d Hk4d g4k 7dk 7d>k Hk7d g7k 6", +"d4M 4+kdM HM6; 4Tgk ld7 7>ld 7Hld gl7 6kN kN6! kN6B 6Nak 1k6N 6N8k 61kNB ak", +"N68 6Qk Q!6M Qk6T Qa6M l6Q 6Ql8 6QlT lQs6 k(6N 6N+k 6k(NB akN6+ 61k(N 6+1kN", +" 1k(6NB 6+1akN 6(Qk +Q6M 6QTk( 6+QTk 6Ql( 6+lQ l6QT( ls6+Q 6Wk 6!Wk Hk6W Wk", +"6a 61Wk Wk68 6H1Wk 6H8Wk 6WM 6!WM Z6k 6aZk lW6 6Wl8 lZ6 s6lZ Wk6d +k6W 6HWd", +"k 6Wgk 6W1dk 6+W8k 1HW6dk g61Wk WM6d +W6M 6dZk Z6gk 6Wld 6+lW Z6ld g6lZ 4km", +" 6!km 4Bkm ak6m 7km 8k7m km7B ak7m 4Qk Q!4k Qk4T Qa4k 7lp l87p lp7T 7lsp km", +"6; +k6m km;4B akm4+ km7; 7k>m 7Bkm; 7>akm Qk6; +Q4k 4QkT; ak;4Q 7pl; 7pl> 7", +"lpT; l>p7s 4Wk 4!Wk Hk4W Wa4k 7Wk W87k Hk7W Wa7k 6W4M 4WkQ! Z4k 4aZk lW7 l8", +"7W 7Zl lZ7s v6k 4+vk 4Hvk v6gk 7vk >k7v vk7H gk7v v6M 6+vM v6Zk Z4gk lv7 7v", +"l> 7Zlv yl7 69k 6&k 9k6B ak6& 916k :6k 691kB 6a:k 6M9 6M& 9T6M 9a6M l69 l:6", +" 6Tl9 s6l: 9k6( +k6& 69k(B 6+9ak 691k( 6+:k 91k6(B :6+ak 6(9M 9+6M 6T9k( 6+", +"T9M 69l( :6l+ l69T( ls:6+ 9k6E 6Ek& 69Hk Hk6& 691kE 6k:E 6H91k 6H:k 6E9M 6E", +"M& 9H6M HM6& 69lE :6lE 6Hl9 l6:H 69dk dk6& 6H9dk 6&gk 6d91k 6d:k 9H16dk :6g", +"k 9d6M dM6& 6HM9d 6Mg9 6dl9 l6:d lH69d g6l: 49k 4k& 9k4B 9a4k 79k 7:k 9k7B ", +":a7k 49M 6M4& 9T4k 9a4M 7l9 l:7 l97T l:7s 9;4k 9+4k k;B49 ak;49 9;7k 7:>k 7", +"B9k; 7:>ak 9M6; 9+4M 49Tk; aM;49 79l; 7:l> 7l9T; l>7s: 9k4E 4Ek& 9H4k Hk4& ", +"9k7E :k7E 9H7k 7k:H 4E9M 49M&E 9H4M 49HaM 79lE lE7: l97H 7:lH 9d4k dk4& 49H", +"dk 49gk 9d7k 7k:d 7H9dk 7:gk 9d4M dk&4M 49HdM 49gM l97d 7:ld 7Tdl9 l:g7 9k6", +"N 6Nk& 69kNB akN6& 691kN 6k:N 91k6NB :6akN 9Q6M Qk6& 6Q9Tk 6Qa9M 6Ql9 l6:Q ", +"l69QT :Q6ls 69k(N k&N6+ 9k(6NB 6+9akN 91k6(N :6+kN 916k(BN akN:6+ 6Q9k( 6+Q", +"9M 9QT6M( 9+Q6aM l69Q( :Q6l+ 6Q9lT( ls6:Q+ 69Wk Wk6& 6H9Wk 6H&Wk 6W91k 6W:k", +" 9H16Wk :H6Wk 9W6M WM6& 6MZ9 6MZ& 6Wl9 l6:W l6Z9 Z6l: 6W9dk dk&6W 9HW6dk g6", +"9Wk 9W16dk :W6+k 691HWkd g:6Wk 6WM9d dk&6Q Z69dk Z6&gk lW69d :Qdl6 lZ69d gl", +"Z:6 9m4k km4& kmB49 akm49 9m7k 7k:m 7B9km 7:akm 9Q4k Qk4& 49QTk aMm49 p97l ", +"7l:p 7lp9T 7sl:p km;49 km&4+ 49mk;B 49+akm 79km; 7:>km km;7B9 :>m7ak 49Qk; ", +"k;&4Q 9Q;4Tk 49+aMm 7lp9; l>p7: l;p7T9 7spl>: 9W4k Wk4& 49HWk km&4H 9W7k 7k", +":W 7H9Wk 7:HWk 9W4M Mm&6W 49Zk 4kZ& l97W 7:lW Z97l l:7Z 49vk 4kv& v69Hk gv6", +"k& 79vk vk7: 7v9Hk :vkg7 49vM 6Mv& Zv69M Zv6k& v97l l:7v lvZ79 l:y7 6U 6U! ", +"6UD 6Ua 6U1 6U8 1U6D 1U6a 6UM U!6M V6 V6a l6U 6Ul8 Vl6 s6V 6U( 6U+ 6(Dk U+6", +"D 6(1U 1U6+ 6U1D( 6U+1D UM6( U+6M V6( V6+ 6Ul( 6Ul+ l(V6 V6l+ 6UE U!6E 6UH ", +"UH6a 6E1U 6EU8 1U6H UH68 UM6E 6UM!E V6H 6HVa 6UlE l6U8E V6lH sHV6 6Ud U+6d ", +"UH6d g6U 1U6d U86d 6UH1d 6Ug1 Ud6M 6U+dM V6d gV6 6Uld l+6Ud V6ld s6gV 4U6 6", +"!4U 4Dk 6U4a 7U 7U8 7UD 7Ua 6U4M 4U6M! V46 4aV6 7lU l87U 7V 7Vs 6U; 6U4+ U;", +"6D Ua6; 7U; 7>U U;7D 7D>k U;6M 4U+6M V6; 4+V6 7Ul; 7Ul> 7V; 7V> 4E6U 4U6!E ", +"6U4H 4UH6a 7UE U87E 7UH UH7a 4U6ME 6UE4M! 4HV6 V46Ha lE7U 7lU8E 7VH Vs7H 6U", +"4d 4U+6d UH6; 4Ug6 7Ud >U7d UH7d g7U 4Ud6M 6U+4dM 4dV6 V4g6 7Uld l>7Ud 7Vd ", +"g7V 6UN U!6N 6NDk 6NUa 6N1U 6NU8 6U1DN 6U8DN 6UQ Q!6U V6Q 6QVa 6UlQ UQ68 lQ", +"V6 sQV6 U(6N 6NU+ 6UD(N 6U+DN 6U1(N 6U+1N 1U(6DN 6UN1+D 6(UQ U+6Q 6QV( 6+VQ", +" l6UQ( l+6UQ Vl6Q( l+V6Q 6UW 6!UW UH6W UW6a 1U6W UW68 6UH1W 6UHW8 UW6Q 6UWQ", +"! Z6V ZaV6 6UlW lW6U8 VlZ6 VZs6 UW6d U+6W 6UHWd 6UgW 6UW1d 6U+1W 1UW6Dd g6U", +"1W UQ6d 6U+WM VdZ6 Z6gV lW6Ud l+W6U Z6Vld gV6lZ 6Um 6!Um Um6D Ua6m 7Um U87m", +" Um7D Ua7m 6U4Q 4UQ6! V6m 6aVm 7pU 78pU 7Vp Vp7s Um6; U+6m 4Dkm; 4+Dkm Um7;", +" >m7U 7UDm; 7>UDm UQ6; 4U+6Q 6QV; 6+Vm 7;pU p>7U V;7p 7pV> 6U4W 4UW6! UH6m ", +"4UW6a 7UW UW78 UH7W UW7a 4UW6Q Mm!6UW V4Z6 Z4V6a 7UlW 7pUW8 7ZV sZ7V v6U 6U", +"v+ 4Dvk v6gU 7vU v>7U vU7H gU7v 6UvM v6U+Q Vv6 g6Vv 7Ulv 7vUl> 7Vv y7V t6 t", +"6& t6D tc6 t61 t:6 6Dt1 c6t: t6M 6Mt& Vt6 c6V lt6 l6t: V6lt u6 t6( t+6 6Dt(", +" c6t+ 61t( :6t+ t61D( t:c6+ 6Mt( 6+tM t(V6 V6t+ t6l( l6t+ ltV6( u6+ t6E 6&t", +"E tH6 c6tH 61tE :6tE 6Ht1 t6:H 6MtE t6M&E V6tH cHV6 t6lE t:6lE l6tH u6H td6", +" 6+td 6Htd gt6 6dt1 t6:d tH61d t:g6 6dtM t+6dM V6td c6gV l6td t:dl6 tdVl6 u", +"g6 t4 t4& t4D tc4 7t 7t: 7tD 7ct t4M 4Mt& t4V Vtc4 7tl l:7t 7Vt u7 t4; t4+ ", +"4Dt; c4t+ 7t; t>7 7Dt; 7ct> 4Mt; 4+tM V4t; V4t+ l;7t 7tl> t;7V u7> t4E 4&tE", +" t4H c4tH 7tE tE7: 7tH tH7c 4MtE t4M&E V4tH tc4VH lE7t 7tl:E lH7t u7H t4d 4", +"+td 4Htd gt4 7td 7:td 7Htd g7t 4dtM t4+dM V4td t4gV ld7t 7tdl> td7V ug7 t6N", +" 6&tN 6DtN tNc6 61tN :6tN t61DN t:c6N tQ6 6Qt& V6tQ c6tQ l6tQ t6:Q tQVl6 u6", +"Q 6(tN 6+tN t6D(N tc6+N t61(N t:6+N 6D1t(N :c6t+N 6Qt( 6+tQ tQV6( tcQ6+ tQ6", +"l( t:Q6+ Vl6tQ( 6+uQ tW6 6Wt& 6HtW c6tW 6Wt1 t6:W tH61W t:H6W 6WtQ tW6Q& tZ", +"6 Z6tc l6tW t:Wl6 Z6lt uZ6 6Wtd 6+tW tH6Wd tWg6 tW61d t:W6+ 6WDtd1 gt:6W 6Q", +"td t+W6Q Z6td g6tZ tQdl6 t:Q6d tZl6d gZu6 t4m 4&tm 4Dtm c4tm 7tm 7:tm 7Dtm ", +"tm7c t4Q 4Qt& V4tQ c4tQ tp7 :p7t 7Vtp up7 6mt; 4+tm t4Dm; tc4+m 7mt; 7>tm 7", +"tDm; t>7cm 4Qt; 4+tQ t4VQ; tc4+Q 7pt; 7pt> tp7V; 7pu> t4W 4Wt& 4HtW c4tW 7t", +"W 7:tW 7HtW tW7c 4WtQ t4WQ& tZ4 t4Zc lW7t 7tW:p tZ7 uZ7 tv4 v6t+ v6tH g4tv ", +"tv7 7t:v tH7v tvg7 v6tQ tv4+Q t4Vv g4tZ 7tlv lvt7: 7Ztv yu7 < = =B5 =a> =K =+K =T =T+ =j =j> =T5 s= 4=", +"d =H5 g=> =dK +d=K =HT g=T =jd =d>j =Hj s=g =m 5m=B >m=a =Q =Q+ =QT =Qa p= p=> ", +"p=T s=p 4W< 4!W< H= vH=5 v>g= =Qv v+=Q Z= Z=g p=v v>p= Z=p y= ,<", +" <& ,= ,5=B ->=a =,K -=K =T, =T- =j, =j- ,5=T s=- ,4E ,E4& ,4H 4H,a ,E5< ,E", +"48 4H,5 4H,8 4E,K <&E4K 4H,T ,4HT& ,E4j <&j4E 4H,j ,4sH =d, -=d =H, g=- ,5=", +"d =d-> ,H=5 ->g= ,d=K -d=K ,H=T g-=T ,d=j =d-j ,H=j s-g= ,=-m =B5,m =a->m =Q, =Q- ,Q=T -Q=T p=, ", +"-p= p,=T -ps= ,4W 4&,W 4H,W 4W,a 4W,5 4W,8 ,4H5W -= =Hv,5 g=-v> v,=Q =Q-v Z=, Z", +"-= v=p, p=-v Z,p= y-= =C =D5 =D> =CK +C=K =DT +D=T ", +"=jC >C=j =Dj s=D C H5=D g>=D d", +"K=C =+dCK Dd=T gD=T dj=C =j>dC Hj=D gs=D C 5D=m >m=D =QC +Q=C =QD +Q=D p=C >Cp= p=D sp=D ", +"4CW< Cv= =5vD v>=D vQ=C =Qv+C Z=D =DgZ =C", +"pv p=v>C Zp=D y=D b< b<& ,D< c< b<. b8< ,.D< c<8 b ,5=D c=> =bK -b=K =bT c=T =", +"bj -j=b ,D=j s=c ,E4b <&E4b 4b,H ,4cH bE5< ,48bE H5b< H5c< ,4bEK 4bE<&K ,4H", +"bT c4H,T ,4jbE <&E4bj ,4Hbj c5b bH=5 c>g= bd=K", +" =b-dK bH=T gc=T bd=j =b>dj bH=j gsc= bm=b =b5Dm >=cm =bQ -Q=b ,Q=D c=Q p=b =b-p pb=", +"D cp= 4b,W =b =bvH5 v>c= vb=Q -v=bQ Z=b Z=c", +" pv=b -pv=b Zp=b yc= 6< 6!< 6= 7=B =a7 =6K 6+=K =T6 6+=T =j7 7>=j =T7 s=7 6", +"<4E 6!4=7d =H7 g7= 6d=K =6+dK 6H=T g6=T 7d=j 7>=d", +"j 7H=T s=g7 6m7= =m7B =m7a =Q6 6", +"+=Q 6Q=T 6Q=a p=7 7>p= 7p=T p=7s W<6m 7= =H7v", +" 7vg= v6=Q =Qv6+ Z=6 g6Z= p=7v 7p>v= 7Z= y7= ? ?& ?B ?a ?1 ?: ?B1 ?:a ?K ?&", +"K ?T ?Ta ?j ?:j ?T1 s? ?* ?- ?B* ?-a ?1* ?:- 1*?B :-?a ?*K ?-K ?T* ?-T ?j* ", +"?-j 1T?* s?- ?E ?&E ?H ?Ha ?1E ?:E ?H1 ?:H ?EK &E?K ?HT H&?T ?jE :j?E ?Hj s", +"?H ?d ?-d ?Hd g? ?d1 ?:d 1H?d g?: ?dK -d?K ?Td g?T ?dj :d?j 1T?d g?s ?4 ?4&", +" ?B4 ?a4 7? ?:7 7?B ?a7 ?4K 4&?K ?T4 4T?a ?j7 7:?j ?T7 s?7 ?= ?-= ?=B =a? ?", +"=7 A =B7? Aa ?=K =K?- =T? ?-=T =j? Aj ?T=j As ?4E 4&?E ?H4 4H?a 7?E ?E7: ?H", +"7 7:?H 4E?K ?4&EK 4H?T ?H4T& 7j?E ?:7jE 7H?T ?H7s ?=d -=?d =H? g?= ?d7 Ad 7", +"?=H Ag =K?d ?-=dK ?H=T =Tg? ?d=j Ajd ?H=j Ags ?N ?&N ?BN ?aN ?1N ?:N 1N?B :", +"a?N ?Q ?Q& ?QT ?Qa ?Q1 ?:Q 1Q?T s?Q ?*N ?-N *B?N -a?N 1*?N ?N:- ?B1*N ?:-aN", +" ?Q* ?-Q Q*?T -Q?T 1Q?* :-?Q ?Q1T* ?Qs- ?W ?W& ?HW ?Wa ?W1 ?:W 1H?W :H?W ?W", +"Q W&?Q ?Z ?Za ?Wj :W?Q ?Z1 s?Z ?Wd ?-W H*?W g?W 1W?d :-?W ?H1W* ?Wg: ?Qd -W", +"?Q ?Zd g?Z 1Q?d :Q?d Z1?d Z:g? ?m ?m& ?mB ?am ?m7 ?:m 7B?m 7a?m ?Q4 4Q?& ?T", +"m 4Q?a ?p ?p: ?pT s?p ?=m ?-m =B?m ?m=a 7=?m Am ?=7mB Aam =Q? ?-=Q ?Q=T ?Q=", +"a ?p= Ap =T?p Aps ?W4 4W?& ?Hm 4W?a ?W7 7:?W 7H?W 7W?a 4W?Q ?W4Q& ?Z4 Z4?a ", +"?pW ?W:p ?Zp 7Zs? ?v ?v- ?vH g?v ?v7 Av ?H7v Agv ?vQ ?Q-v ?Zv Z-?v ?pv Apv ", +"7Z?v yA 6C7= =D7 7>=D 6C=K =6+CK 6D=T =D6+T 7j=C 7>=jC 7D=T =D7s 46=dC 7H=D =Dg7 =6dCK 6+d", +"=CK =H6DT g=6DT 7=djC =j>7dC 7Dd=T g7s=D =mC =m7D 7>D=m 6Q=C =Q6+C 6Q=D =QD6+ =C7p 7p>=C 7p=D p=D7s =C =D7v 7vDg= =Qv6C v6+=QC Z6=D ", +"Z=g6D 7vp=C p=>7vC =D7Z =Dy7 ?b ?b& ?D ?c ?b1 ?:b ?D1 ?c: ?bK bK?& ?DT ?cT ", +"?jb :b?j ?Dj s?c ?b* ?-b ?D* ?c- b1?* ?b:- 1D?* :c?- b*?K -b?K bT?* c-?T b*", +"?j ?b-j D*?j ?cs- ?bE b&?E ?Hb ?cH b1?E :b?E b1?H :c?H bK?E ?b&EK bH?T cH?T", +" bj?E ?:bjE bH?j ?Hsc ?db -b?d ?Dd g?c b1?d ?b:d 1D?d ?cg: bd?K ?-bdK bT?d ", +"?Tgc bd?j ?:dbj Dd?j gs?c ?4b 4b?& ?D4 ?c4 7?b 7b?: ?D7 7c? 4b?K ?4b&K 4b?T", +" c4?T 7b?j ?:7bj 7b?T 7cs? =b? ?-=b =D? c=? =b7 Ab 7?=D Ac ?b=K ?-=bK ?D=T ", +"=T?c ?j=b Ajb ?D=j Asc 4b?E ?4b&E 4b?H c4?H ?E7b ?:7bE 7b?H ?H7c ?4bEK 4b&?", +"EK ?H4bT ?c4HT ?j7bE 7:b?jE ?H7bT s?7cH ?d=b ?-=bd ?H=b c=g? 7b?d Abd 7b=H ", +"Agc ?=dbK =b-?dK ?Dd=T c=?gT ?d7bj bdAj ?Hj=b gsAc ?bN b&?N ?DN ?cN b1?N :b", +"?N 1D?N ?N:c ?Qb bQ?& ?QD ?cQ b1?Q ?b:Q 1Q?D ?Qsc b*?N -b?N D*?N ?Nc- ?b1*N", +" ?:-bN ?D1*N ?c:-N bQ?* ?b-Q QD?* c-?Q ?Qb1* ?:Q-b ?QD1* ?cQs- ?Wb bW?& ?WD", +" ?cW b1?W ?b:W 1W?D :c?W bW?Q ?WbQ& ?Zb ?Zc bW?j ?:WbQ Zb?j ?cZ: bW?d ?b-W ", +"WD?d ?Wgc ?Wb1d ?:W-b ?WD1d ?cWg: bQ?d ?-WbQ Zb?d ?cZ- ?Wjbd ?:Qbd ?Zb1d g?", +"Zsc ?mb bm?& ?Dm ?cm 7b?m ?b:m 7D?m ?m7c 4b?Q ?Q4b& 4Q?D c4?Q ?pb pb?: ?pD ", +"?pc ?m=b ?b-m ?m=D ?mc= =m7b Abm ?D7=m Acm ?Q=b ?-Q=b ?Q=D =Q?c =b?p Apb =D", +"?p Apc 4b?W ?W4b& 4W?D c4?W 7b?W ?:W7b 7W?D ?W7c ?W4bQ 4bW?Q& Z4?D ?mZc pb?", +"W ?p:bW ?D7Z 7c?Z ?vb vb?- ?vD ?vc =b7v Avb ?D7v Acv vb?Q ?v-bQ =b?Z cv?Z p", +"b?v pvAb =b7Z yAc =L 5B=L >L=a =M =M+ =TM =aM =M5 >M= 5T", +"=M s=M L H5=L >Lg= =Md +M=d =HM g=M 5", +"d=M =d>M H5=M >Mg= L =B5mL =a>mL =QM +Q=M Tm=M Qa=M p=M >Mp= pT=M sp=M 4LW", +"< Lv= =Hv5L g=v>L =Mv =+vM Z=M =MgZ pv=M ", +"v>=M Zp=M y=M ,L-= =B5,L =a->L =M, =M- ,T=M -T=M ,5=M ->=M =T5,M =Ms- 4E,L ", +"<&E4L ,L4H ,4HaL ,45EL ,48EL ,4H5L ,4H8L ,E4M =dL =H5,L g=->L ,d=M =d-M ,H=M g-=M", +" =M5,d >M-=d =HM,5 g=Ms- =mL ,5m=BL ->m=aL ,Q=M -Q=M =QT,M =Q-aM p,=M =M-p p=T,M -ps=M ,", +"L4W =L v,5=HL -v>g=L v,=M =M-", +"v Z,=M =MZ- p=v,M -pv=M Z=p,M =My- U< U U5=D >U=D =U", +"M U+=M V= V=+ =Uj =U>M V=5 s=V 4EU< 4UU ", +"UH=5 g>=U Ud=M =U+dM V=H V=g Ud=j >M=Ud V5=H gVs= Um=U =U5Dm =U>Dm =UQ U+=Q V=", +"Q V+=Q p=U p>=U V=p Vps= U<4W 4UW=U =UvH5", +" g=Uv> vU=Q =Uv+Q Z=V gVZ= pv=U p=Uv> VpZ= yV= ,U< U<& U<,D c=U =bU5D =Uc> =bM -U=M V=b c=V ,U", +"=j =b>M V5=b V-s= ,E4U ,4U&E 4U,H c4U,H ,4U5E ,4U8E ,4UH5 c5Ud =bHU5", +" c=g>U bd=M =bM-d Vd=b gVc= =bM5d >M=bd V=bH5 c=Vgs ,NU< UN<& ,UDUm ,U5=Dm c=>Um ,U=Q -U=Q Vm=b =QV- pb=U =U-p =bV", +"p V=cp 4U,W = =U5vD, cv=>U v", +"b=M -vU=Q =bVv V-Z= p=bvU -pUv= Vv,p= c=yV k= 7B=k =", +"a>k =kM 6+=M =Tk 6a=M l= l=> l=T l=s 4Ek 7H=k >kg= 6d=M =k+dM 6H=M g6=M l=d >=ld l=H gl= m=k 7=mkB >k=am =Qk +Q=k Qk", +"=T Qa=k l=p p=l> lp=T lsp= W<4k =k 7v=", +"Hk g=k7v v6=M =Qkv+ Z=k =kgZ lv= v>l= l=Z yl= ?k ?k& ?Bk ?ak ?1k ?:k 1k?B :", +"a?k ?M ?M& ?Tk ?aM l? l?: l?T s?l ?k* ?-k k*?B -k?a 1*?k -k?: ?B1k* ?:-ak ?", +"M* ?-M Tk?* -T?M l?* l-? ?Tl* s?l- ?kE k&?E ?Hk Hk?a 1k?E :k?E 1H?k ?k:H ?M", +"E M&?E ?HM HM?a l?E lE?: l?H lHs? ?dk -k?d Hk?d g?k 1d?k ?k:d ?H1dk ?:gk ?d", +"M -M?d HM?d g?M l?d ?:ld ?Hld gl? ?4k 4k?& 4k?B 4a?k 7?k ?k7: ?k7B ?k7a ?M4", +" 4M?& 4T?M 4a?M l?7 7l?: 7l?T l?7s =k? =k- ?B=k ?a=k 7?=k Ak ?=7kB Aak =M? ", +"?-=M ?T=k ?a=M l=? Al =Tl? Als 4k?E ?4k&E 4H?k ?H4ak 7k?E ?:7kE ?k7H ?:H7k ", +"4M?E ?M4&E 4H?M ?HM4a lE7? l?7:E 7l?H l?H7s ?d=k =d-k ?H=k =kg? ?k7d Adk ?H", +"7=k Agk ?d=M ?-M=d ?H=M =Mg? ?=ld Ald =Hl? Alg ?kN k&?N kN?B ak?N 1k?N :k?N", +" ?B1kN ?:akN ?Qk Qk?& Qk?T Qa?M l?Q ?Ql: lQ?T ?Qls k*?N ?N-k ?Bk*N ?-akN ?1", +"k*N ?:-kN k*N?B1 :-k?aN Qk?* -Q?M ?QTk* ?-QTk ?Ql* ?Ql- l?QT* l-?sQ ?Wk Wk?", +"& Hk?W Wa?k 1W?k ?k:W ?H1Wk ?:HWk ?WM WM?& ?Zk Zk?a l?W ?:lW ?Zl l?Z: Wk?d ", +"-k?W ?HWdk ?Wgk ?W1dk ?:W-k 1H*?Wk g?:Wk WM?d -W?M Zk?d gk?Z ?Wld ?-lW ld?Z", +" ?Zgl ?mk km?& km?B ak?m 7k?m ?k:m ?m7kB ?:mak ?Mm Mm?& Tk?m aM?m ?pl l?:p ", +"lp?T ls?p ?m=k -k?m ?=mkB ?-mak ?=7km Akm 7=B?mk akAm ?Q=k -Q=k ?Tm=k ?-Tkm", +" ?pl= Alp l=?pT lsAp 4W?k ?W4k& Hk?m ?Hmak ?k7W ?:W7k ?H7Wk ?:Hkm 4W?M ?WM4", +"& Z4?M ?Z4ak lW?p l?W:p l?7Z s?Z7l ?vk =k-v vk?H gk?v vk7? Avk ?v7Hk gvAk ?", +"vM ?M-v =k?Z =kZ- lv? Alv ?Zlv yAl 6U< 6!U< Dk 7U=D =D>k 6U=M =U6+M V=6 V6=a l=U =Ul> l=V 7V", +"s= U=d 7U=H =Ug7 =U6dM", +" 6U+=Md V6=H =kgV ld=U l=>Ud =H7V V=gl 6NU< U=m =U7Dm 7Ua=m 6U=Q =UQ6+ V6=Q V=6+Q 7p=U l=p>U Vpl= V=pl> = 7vU=H g7Uv= =Uv6Q =U+v6M =", +"kVv Vv6g= =Ulv lv=>U Z=7V l=yV ?t ?t& ?tD ?ct ?t1 ?t: t1?D t:?c ?tM tM?& V?", +" V?c l?t ?tl: V?l u? ?t* t-? t*?D t-?c ?1t* ?:t- ?t1D* ?t:c- t*?M ?Mt- V?* ", +"V?- ?tl* l?t- l*V? u?- ?tE t&?E ?tH tc?H t1?E ?Et: ?1tH t:?H tM?E ?tM&E V?H", +" Vc?H lE?t ?t:lE lHV? u?H ?td ?dt- tH?d g?t ?1td t:?d ?tH1d ?tg: td?M ?td-M", +" V?d g?V ?tld l-?td ldV? u?g ?t4 ?&t4 t4?D t4?c 7t? ?:7t ?D7t 7t?c t4?M ?t4", +"M& V?4 c4V? l?7t ?t:7l 7V? u?7 t= t=- t=D t=c t=7 At =D7t Atc t=M =Mt- t=V ", +"V?c= l=t Alt 7Vt= Au ?Et4 ?t4&E t4?H ?tHc4 ?E7t ?t:7E ?H7t 7tH?c ?t4ME t4&?", +"ME V4?H V?c4H 7tl?E l?:7tE ?H7V ?Hu7 t=d -=td t=H gt= ?d7t Atd =H7t Agt td=", +"M t=-dM =HV? V=g? ldt= ltAd =Hlt Aug ?tN t&?N tD?N ?Ntc t1?N ?Nt: ?t1DN ?t:", +"cN ?tQ ?&tQ V?Q tc?Q ?Qlt t:?Q Vl?Q u?Q ?Nt* ?Nt- ?tD*N ?ct-N ?t1*N ?t:-N t", +"*1?DN t-:?cN t*?Q ?Qt- V*?Q ?QV- ?tQl* t-Ql? V?lQ* ?-uQ ?tW ?&tW tH?W tc?W ", +"?1tW t:?W ?tH1W ?cWt: tW?Q ?tWQ& ?ZV ?ctZ ?tlW l?Wt: l?tZ u?Z tW?d ?Wt- ?tH", +"W* ?Wgt ?tW1d t-W?: ?H1tW* g?t:W tQ?d t-W?Q ?dtZ ?Zgt l?Wtd l-W?t V?dlZ g?u", +"Z ?tm ?&tm tm?D tc?m ?m7t t:?m ?tm7D ?cm7t t4?Q ?tQ4& V?m cmV? ?pt ?t:p ?pV", +" u?p t=m ?mt- tm=D tmc= 7=tm Atm t=7Dm tcAm t=Q =Qt- =QV? ?mV- tp= Apt V=?p", +" Aup t4?W ?tW4& tH?m ?cWt4 ?W7t 7tW?: 7tH?W 7cW?t ?tW4Q ?W&t4Q ?mtZ V?mZc ?", +"Wtp ?pt:W tZ?p ?Zup ?vt t-?v =Htv ?vgt 7t?v Atv ?vt7H gtAv =Qtv ?vt-Q ?vV Z", +"-t= tp?v lvAt tZl= Auy 3F !3F 3BF a3F 3F. 83F 3F.B a38F 3FK 3F!K T3F a3TF j", +"3F j38F j3TF s3F 3(F +3F 3(FB a3+F (F3. 83+F 3F.(B +8a3F (F3K 3F+K 3(TF T3+", +"F 3(jF j3+F Tj3(F +3sF G3 G3! GH3 Ga3 G3. G83 H3G. G3H8 G3K !KG3 GT3 TaG3 G", +"j3 8jG3 G3Hj sG3 Gd3 +G3 HdG3 gG3 d3G. +3G8 GHd3. G8g3 dKG3 GK+3 G3Td GTg3 ", +"G3dj +3Gj dj3GT g3sG 4F 4F! 4FB 4aF 45F 48F 5B4F 4F5a 4FK !K4F 4TF Ta4F 4jF", +" 8j4F 4F5T s4F 4;F 4+F ;F4B 4Fa; 5;4F >F4 45;FB 4a>F ;F4K +F4K 4FT; 4F+T 4F", +";j 4j>F ;jF4T >Fs4 4G 4G! 4GH 4Ga 4G5 4G8 G54H G54a 4GK GK4! 4GT GT4a 4Gj G", +"84j G54T s4G 4Gd 4+G G;4H g4G G54d >G4 4GH5d >Gg4 4KG; 4K+G GT4d 4GgT G;4j ", +"4G>j 4GT5d sGg4 3FN 3F!N 3FNB 3FaN 3FN. 3F8N 3F.NB 8a3FN Q3F !3QF T3QF a3QF", +" j3QF 83QF QTj3F Q3sF (F3N 3F+N 3FN(B +a3FN 3F.(N +83FN N.(3BF 8a3+FN 3(QF ", +"Q3+F QT3(F +QT3F Qj3(F +Q83F Tj3Q(F s+Q3F GW3 G3W! HWG3 G3Wa W3G. G3W8 GHW3", +". G8HW3 GQ3 G3Q! ZG3 GaZ3 G3Wj G3Q8 GjZ3 ZGs3 WdG3 GW+3 GHWd3 GWg3 GWd3. +G", +"W83 HWdG3. gGW83 G3Qd +3GQ GdZ3 ZGg3 dj3GQ +GQ83 ZGdj3 gZsG3 4mF mF4! mF4B ", +"4Fam 5m4F 4F8m 45mFB amF48 4QF 4FQ! 4FTm 4FQa p4F 48pF 4TpF p4sF m;4F 4F+m ", +"4m;FB amF4+ 45m;F 4m>F 5m;4FB >m4aF 4FQ; 4F+Q 4QT;F 4+QTF 4;pF p4>F p4T;F p", +">4sF 4GW 4!Gm Gm4H Ga4W G54W G84W 4GH5W 4G8Hm 4GQ Q!4G Z4G 4GZa pG4 4Gp8 pG", +"Z4 Z4sG v4G 4+vG 4GvH v4gG 4Gv5 v4>G v4GH5 gv>4G 4GvQ +G4Q v4ZG gGZ4 v4pG p", +"4>G Zpv4G y4G #3F &3F #3FB &3aF 3F#. &38F 3F.#B 8a&3F 3F#K 3F&K #3TF &3TF #", +"3jF j3&F Tj#3F &3sF 3(#F &3+F 3BF#( +a&3F 3F.#( +8&3F #(F.B3 8a&+3F 3FK#( +", +"&3FK T#3(F +T&3F j#3(F &j3+F j#3T(F s+&3F G#3 G&3 H#G3 G3H& #3G. 8&G3 GH#3.", +" G8H&3 #3GK &3GK T#G3 G3T& j#G3 G3&j GTj#3 G&s3 d#G3 +3G& GHd#3 G&g3 Gd#3. ", +"d&3G8 Hd#G3. gG8&3 Gd#3K d&3GK GTd#3 gGT&3 dj#G3 d&jG3 GT#dj3 gsG&3 4#F 4&F", +" #B4F a&4F 5#4F 4F5& 45#FB 48a&F #F4K &F4K T#4F 4FT& j#4F 4F&j 4T5#F 4&sF ;", +"#4F 4F;& 4;#FB a;&4F 45;#F 4&>F 5;#4FB >&4aF 4;#FK ;&F4K 4T;#F 4+T&F ;j#4F ", +">&4jF 4T#;jF s>4&F 4G# 4G& GH4# Ga4& 4#G5 G54& 4GH5# 4G8H& G#4K 4KG& 4#GT G", +"T4& 4#Gj G&4j 4GT5# 4Gs& 4#G; +G4& 4GHd# 4Gg& 4G5d# 4G>& G5H4d# g>4G& 4Gd#K", +" 4+G&K 4GTd# g4GT& 4Gjd# >G4&j dj#4GT gs4G& 3F#N 3F&N 3FN#B a&3FN 3F.#N 8&3", +"FN .B3#FN a&38FN #3QF &3QF QT#3F Qa&3F Qj#3F &j3QF Tj#Q3F sQ&3F 3FN#( +&3FN", +" #BF3(N a&3+FN #3FN.( 8&3+FN 3F#N.B( 3F&N+8a Q#3(F +Q&3F T#3Q(F Q&3+TF j#3Q", +"(F +Q3&jF 3(jFQT# +Q&s3F W#G3 G3W& GHW#3 GaW&3 GW#3. G8W&3 HW#G3. H8WG&3 Q#", +"G3 G3Q& G#Z3 G&Z3 GQj#3 GQ8&3 ZGj#3 Z&Gs3 GWd#3 d&3GW HWdG#3 gGW&3 Wd#G3. G", +"8Wd&3 W#H3Gd. G8Wg&3 GQd#3 +GQ&3 ZGd#3 Z&Gg3 GQ#dj3 d&3GQ8 dj#ZG3 gsGZ&3 m#", +"4F 4Fm& 4m#FB am&4F 45m#F m&F48 5m#4FB 48am&F Q#4F 4FQ& 4QT#F 4Qa&F 4#pF 4&", +"pF p4T#F p&4sF 4m;#F m&F4+ m;#4FB 4+am&F 5m;4#F >m4&F 5B4Fm;# am&>F4 4Q;#F ", +"4+Q&F Q;#4TF a;F4Q& p4;#F p>4&F 4T;p#F s>4p&F 4#Gm Gm4& 4GHW# 4GaW& 4G5W# 4", +"G8W& G5H4W# G5a4W& 4#GQ GQ4& 4GZ# 4GZ& 4Gp# 4Gp& Zp4G# Zp&4G 4Gv# 4Gv& v4GH", +"# gv4G& v4G5# v>4G& 4GHv5# v>Gg4& v4GQ# v&4GQ Zv4G# Zv&4G pv4G# pv&4G pvGZ4", +"# 4Gy& I3 I3! DI3 aI3 I3. 8I3 I3D. D38I I3K !KI3 TI3 T3aI Ij3 83Ij D3Ij sI3", +" I3( +I3 I3D( +3aI 3(I. +38I DI3(. +D8I3 3(IK I3+K I3T( +3TI j3I( +3Ij Ij3D", +"( +Is3 GI3 I3G! J3 Ja3 I3G. G38I J3. J83 I3GK GI3!K JT3 TaJ3 G3Ij Ij3G8 Jj3", +" sJ3 Id3 +3Id Jd3 gJ3 d3I. 83Id d3J. g3J8 I3dK Id3+K TdJ3 JTg3 I3dj Idj+3 d", +"jJ3 g3sJ 4I 4I! 4DI 4aI 4I5 48I 5D4I 5a4I 4IK IK4! 4TI TI4a 4Ij 8I4j 5T4I s", +"4I 4I; 4+I D;4I +D4I 45I; >I4 4D5I; 4D>I 4KI; 4K+I T;4I +T4I I;4j 4I>j I;j4", +"D >Is4 4GI GI4! J4 J4a G54I G84I J45 J48 GI4K 4GI!K J4T 4TJa Gj4I 4G8Ij J4j", +" sJ4 4Id +G4I J4d gJ4 5I4d 4G>I 4dJ5 J>4 4KId 4+GIK 4TJ; gTJ4 Id4j >I4Gj 4d", +"Jj g4sJ I3N !3IN I3DN I3aN N.I3 IN83 DI3N. D8I3N QI3 I3Q! Q3TI Q3aI Q3Ij Q3", +"8I Ij3QD QIs3 3(IN I3+N DI3(N +DI3N I3(N. +I83N I3(DN. D83+IN I3Q( +3QI QDI", +"3( +QDI3 Ij3Q( +QI83 QD3Ij( sI+Q3 WI3 I3W! JW3 WaJ3 I3W. W38I W3J. W8J3 G3Q", +"I GQI!3 ZJ3 JaZ3 W3Ij GQ8I3 Z3Jj s3ZJ W3Id +3WI WdJ3 JWg3 Id3W. +WI83 JWd3.", +" gJW83 Q3Id +GQI3 JdZ3 g3ZJ IdjW3 +WIj3 ZJdj3 sJZg3 4Im 4!Im Dm4I am4I 45Im", +" 8m4I 4D5Im 4D8Im 4QI Q!4I QD4I Qa4I pI4 48pI 4DpI pIs4 4mI; +m4I Im;4D 4+D", +"Im Im;45 4I>m 4D5Im; >Dm4I Q;4I +Q4I 4QDI; 4+QTI 4Ip; p4>I pD4I; p>D4I 4WI ", +"4!WI J4W 4WJa 5W4I W84I 4WJ5 4WJ8 GQ4I 4GQI! ZJ4 ZaJ4 4GpI pG48I Jp4 s4Jp v", +"I4 4+vI Jv4 g4Jv 4Iv5 v4>I v4J5 v>J4 4QvI vI4+Q Z4Jv ZJg4 p4vI pvI>4 pvJ4 y", +"J4 bI3 I&3 DIb3 cI3 I3b. b38I bDI3. 8Ic3 I3bK &3IK b3TI TIc3 b3Ij I3&j Ij3b", +"T cIs3 I3b( b3+I bDI3( +Ic3 bI3(. I&3+8 DI3b(. c+8I3 bKI3( I&3+K bTI3( c+TI", +"3 Ij3b( I&j+3 bT3Ij( sI+c3 bG3 b3G& Jb3 cJ3 G3b. b3G8 b3J. c3J8 G3bK I&3GK ", +"bTJ3 JTc3 b3Gj I&jG3 bjJ3 s3cJ b3Id b3+G bdJ3 g3cJ Id3b. b+G83 Jbd3. cJ8g3 ", +"Id3bK b+G3K JbTd3 cJTg3 Idjb3 Idj&3 Jjbd3 sJgc3 4bI 4I& bD4I c4I b54I b84I ", +"4b5DI 48cI bK4I 4KI& bT4I 4TcI bj4I I&4j 4bT5I c4sI b;4I b+4I 4bDI; 4+cI 4b", +"5I; 4b>I b5D4I; >Ic4 I;K4b I;&4K 4bTI; c4+TI I;j4b >I4bj 4bTI;j c>s4I 4bG b", +"G4& J4b cJ4 b54G bG48 4bJ5 J4c5 4KbG 4bG&K 4bJT cTJ4 bG4j I&j4G 4bJj cJs4 b", +"G4d b+4G 4bJ; cJg4 4bG5d 4b>G J4b5d c4J> 4bGdK 4b+GK J4bTd cJ4gT Idj4b >bG4", +"j J4jbd J>sc4 I3bN &3IN bDI3N I3cN bI3N. I&38N DI3bN. c8I3N b3QI Q3I& bQDI3", +" QIc3 Ij3bQ I&jQ3 bQDIj3 sIQc3 bI3(N I&3+N DI3b(N c+I3N I3(bN. +I3b8N 3(INb", +"D. +I8c3N bQI3( b+QI3 QI3bT( cQ+I3 bQ3Ij( Ij3b+Q Qjb3TI( cQ+sI3 b3WI W3I& b", +"WJ3 JWc3 bGW3. bG8W3 JbW3. cJW83 b3GQ bGQ&3 JbZ3 Z3cJ bGQj3 I&jW3 ZJbj3 sJZ", +"c3 Id3bW b+GW3 JbWd3 cJWg3 bW3Id. Id3bW8 bWdJ3. gJ8cW3 bGQd3 b+GQ3 ZJbd3 Zc", +"Jg3 Id3bWj Id3bQ8 JjdZb3 ZcgsJ3 bm4I Im4& 4bDIm 4Icm 4b5Im Im&48 b5D4Im c45", +"Im bQ4I QI4& 4bQTI 4QcI 4bpI 4Ip& pb4TI pIc4 Im;4b Im&4+ 4bDIm; c4+Im 4b5Im", +"; >I4bm Imb;4D5 c>4Im 4bQI; I;&4Q bQ;4TI c4Q+I pb4I; p>b4I 4bTpI; cp>4I bG4", +"W WI4& 4bJm J4cW 4bG5W 4bGW8 J4b5W cJ45W bG4Q 4bGQ& J4Zb cJZ4 4bpG pbG48 pb", +"J4 c4Jp 4bvI 4Iv& vbJ4 c4Jv vb4G5 v>b4G Jv4b5 cvJ>4 vb4GQ vI&4Q ZJ4vb cvZJ4", +" pvb4G pvI4& Jpv4b cJy4 6F 6!F 6FB 6aF 61F 68F 1F6B 6F1a 6FK !K6F 6TF Ta6F ", +"6jF 8j6F 6F1T s6F 6(F 6+F (B6F +a6F 1(6F 6F1+ 61(FB 6+1aF (F6K +F6K T(6F 6F", +"+T j(6F 6F+j 6T1(F 6+sF 6G 6G! 6GH 6Ga 6G1 6G8 1G6H 1G6a 6GK GK6! 6GT GT6a ", +"6Gj G86j 1G6T s6G 6Gd 6+G GH6d g6G 1G6d 1+6G 6G1Hd 6Gg1 Gd6K 6K+G GT6d 6GgT", +" Gj6d +G6j 6GT1d sGg6 46F 4F6! 6F4B 4F6a 7F 78F 7BF 7aF 6F4K 6!4FK 4F6T 4T6", +"aF 7jF 8j7F 7TF 7sF 6;F 4F6+ ;F6B 6Fa; 7;F 7>F ;F7B >F7a ;F6K 4+6FK 6FT; 4+", +"T6F ;j7F 7j>F T;7F >F7s 4G6 6!4G 6G4H 6G4a 7G 7G8 7GH 7Ga 4K6G 4G6!K 6G4T 4", +"GT6a 7Gj G87j 7GT 7sG 6G; 6+4G G;6H 4Gg6 7Gd 7>G G;7H g7G 6KG; 4+G6K GT6; g", +"46GT G;7j >j7G GT7d sGg7 6FN !F6N NB6F aF6N 1F6N 8F6N 61FNB 68aFN 6QF 6FQ! ", +"QT6F 6FQa 6F1Q 6FQ8 6Q1TF 6QsF (F6N +F6N 6(FNB 6+aFN 61(FN 6+1FN 1(F6NB 1+F", +"6aN Q(6F 6F+Q 6QT(F 6+QTF 6Q1(F 6+Q1F 1Q(6TF s6+QF 6GW W!6G GH6W Ga6W 1G6W ", +"G86W 6G1HW 6G8Wa 6GQ Q!6G Z6G 6GZa 1G6Q GQ68 6GZ1 Z6sG GW6d +G6W 6GHWd 6GgW", +" 6G1Wd 6+G1W 1GH6Wd g61GW GQ6d +G6Q 6GZd gGZ6 6GQ1d 6+G1Q Z61Gd gZ6sG 6mF m", +"F6! mF6B 6Fam 7mF 8m7F mF7B am7F 4F6Q 4Q6!F 6FTm 4Qa6F 7pF p87F pT7F sF7p m", +";6F 6F+m 6m;FB 6+maF m;7F 7m>F 7Bm;F 7>amF 6FQ; 4+Q6F 6Q;TF 6+TmF p;7F >F7p", +" 7pT;F 7p>sF 6Gm 6!Gm Gm6H Ga6m 7GW G87W Gm7H Ga7W 6G4Q 4GQ6! 4GZ6 Z46Ga 7p", +"G 78pG 7ZG sG7Z v6G 6+vG 6GvH gGv6 7vG v>7G vG7H 7Ggv 6GvQ v6+GQ ZGv6 Zv6gG", +" pv7G p>7G 7GZv y7G 69F 6&F 9F6B 6F9a 916F :6F 691FB 6a:F 9F6K &F6K 6F9T 6F", +"T& 6F9j 6j:F 6T91F :6sF 9(6F 6F9+ 69(FB 6+9aF 691(F 6+:F 91(6FB :6+aF 69(FK", +" 6+9FK 6T9(F 6+T9F 6j9(F :6+jF 9T(6jF s:6+F 6G9 6G& 9G6H 9G6a 691G :G6 6G91", +"H 6G:H 6K9G 6KG& 9G6T GT6& 9G6j 6G:j 6GT9j :Gs6 9G6d 9+6G 6G9Hd 6Gg9 6G91d ", +"6+:G 9G16Hd :Gg6 6G9dK 6+G9K 6GT9d g69GT 6Gj9d :G6+j 9GT6dj g:s6G 49F 4F6& ", +"9F4B 4F9a 79F 7:F 9F7B :a7F 9F4K 49&FK 4F9T 49TaF 9j7F :j7F 9T7F sF7: 4F9; ", +"4F9+ 49;FB 49+aF 9;7F >F7: 7B9;F 7:>aF 49;FK 49+FK 49T;F 49+TF 79;jF 7:>jF ", +"7T9;F 7s:>F 49G 6G4& 9G4H 9G4a 7G9 7:G 9G7H :G7H 4K9G 49G&K 9G4T 49GT& 9G7j", +" 7j:G 9G7T 7:sG 9G4d 9+4G 49GH; 49gG 9G7d :>7G 7G9H; 7Gg: 49GdK 49+GK 49GTd", +" g49GT 7G9dj 7:G>j 7GT9d 7sGg: 9F6N &F6N 69FNB 6a9FN 691FN 6F:N 91F6NB :6aF", +"N 6F9Q 6FQ& 6Q9TF 6Qa9F 6Q91F 6Q:F 9Q16TF :Q6sF 69(FN 6+9FN 9(F6NB 9+F6aN 9", +"1(6FN :6+FN 916N(BF 6+a:FN 6Q9(F 6+Q9F 9Q(6TF 6+F9Qa 9Q(6jF :Q6+F QT1(69F s", +"6+:QF 9G6W G&6W 6G9HW 6Ga9W 6G91W 6G:W 9G16HW :G6Wa 9G6Q GQ6& 6GZ9 6GZ& 6GQ", +"9j 6G:Q Z691G :GZ6 6G9Wd 6+G9W 9GH6Wd g69GW 9G16Wd :G6+W 691HGWd g:6GW 6GQ9", +"d 6+G9Q Z69Gd Z6&gG 9GQ6dj :GQ6+ 6GjZ9d Z:g6G 4F9m 6Fm& 49mFB 49amF 9m7F 7F", +":m 7B9mF 7:amF 4F9Q 49Q&F 49QTF 49QaF 7Fp9 7F:p 7p9TF 7sp:F 49m;F 49+mF 9mF", +"6;B amF49+ 79m;F 7:>mF 9m;7BF :>m7aF 49Q;F 49+QF 4QF9T; a;F49Q 7p9;F 7p>:F ", +"p9;7TF :p>7sF 9G4W Gm6& 49GHm 49GWa 9G7W :G7W 7G9Hm 7:GHm 9G4Q 49GQ& 49ZG Z", +"49Ga p97G 7G:p Z97G 7GZ: 49vG 6Gv& v69GH gv69G v97G 7G:v 7v9GH :vGg7 v69GQ ", +"v6&GQ Zv69G Zv6G& 7pGv9 :vG7p 7Zv9G 7:yG 6I 6I! 6DI 6aI 6I1 68I 1D6I 1a6I 6", +"IK IK6! 6TI TI6a 6Ij 8I6j 1T6I s6I 6I( 6+I DI6( +D6I 6(1I 1+6I 6D1I( 6+D1I ", +"I(6K 6K+I 6(TI +T6I 6(Ij +I6j 6DjI( 6+sI 6GI GI6! J6 J6a 1G6I G86I J61 J68 ", +"GI6K 6GI!K J6T 6TJa Gj6I 6G8Ij J6j sJ6 6Id +G6I J6d gJ6 1I6d 8I6d 6dJ1 J6g1", +" 6KId 6+GIK 6TJd gTJ6 Id6j Idj6+ 6dJj g6sJ 4I6 6!4I 6D4I 6a4I 7I 7I8 7DI 7a", +"I 4K6I 4I6!K 6T4I 4TI6a 7Ij 8I7j 7TI 7sI 6I; 6+4I D;6I aI6; 7I; 7>I D;7I >D", +"7I 6KI; 4+I6K TI6; 4+T6I I;7j >j7I TI7; 7>sI 6G4I 4G6I! J46 4aJ6 7GI G87I 7", +"J 7J8 4G6IK 6GK4I! 4TJ6 J46Ta Gj7I 7G8Ij 7JT 7Js 6I4d 4+G6I J6; J4g6 7Id >G", +"7I 7Jd g7J 4Id6K I;K6+G 6TJ; gJ46T Id7j 7>GIj J;7T sJg7 6IN IN6! DI6N 6NaI ", +"6N1I 6N8I 6D1IN 6D8IN 6QI Q!6I QD6I Qa6I 1Q6I Q86I 6QD1I 6QsI I(6N 6N+I 6DI", +"(N 6+DIN 6I1(N 6+I1N 1D(6IN 6+N1aI 6(QI +Q6I 6QDI( 6+QTI 6QI1( 6+Q1I Ij(6QD", +" s6I+Q 6WI 6!WI J6W 6WJa 1W6I W86I 6WJ1 6WJ8 GQ6I 6GQI! ZJ6 ZaJ6 WI6j 6GQ8I", +" J6Z1 ZJs6 WI6d +W6I 6WJd gWJ6 6WI1d 6+W1I J61Wd gJ61W QI6d 6+GQI ZdJ6 ZJg6", +" Idj6W 6+WIj ZJ61d sJ6gZ 6Im 6!Im Dm6I aI6m 7Im 8I7m Dm7I aI7m 6Q4I 4QI6! T", +"I6m 4Qa6I 7pI 78pI pD7I 7psI Im6; +I6m 6DmI; 6+DIm Im7; >m7I 7DIm; 7>DIm QI", +"6; 4+Q6I 6QDI; 6+TIm 7;pI p>7I 7pDI; 7sIp> 6W4I 4WI6! J6m 6aJm 7WI W87I 7JW", +" J87W 4GQ6I Im!6GQ J4Z6 ZJ46a pG7I 7pG8I 7ZJ sJ7Z v6I 6+vI Jv6 g6Jv 7vI v>7", +"I 7Jv 7vJ> 6QvI v6I+Q Z6Jv Jv6gZ pv7I 7vIp> 7vJp yJ7 b6I 6I& 9D6I c6I 6Ib1 ", +":I6 b61DI c6:I bK6I 6KI& 6IbT 6TcI 6Ibj 6I:j b6T1I :Is6 b(6I 6Ib+ b6DI( 6+c", +"I b61I( 6+:I 6DIb1( :c6+I b6I(K b6+IK b6TI( c6+TI b6jI( :I6+j Ij(b6T s:I6+ ", +"b6G 6&bG J6b cJ6 6Gb1 b6:G b6J1 :J6 bK6G b6G&K b6JT cTJ6 6Gbj :Gb6j b6Jj s6", +":J 6Gbd 6+bG b6Jd cJg6 b6G1d 6I:d J6b1d g6:J b6GdK b6+GK J6bTd cJ6gT Idjb6 ", +":Id6j J6jbd sJ6g: 49I 6I4& 9D4I 49cI 7bI 7:I 9D7I 7cI 4K9I 49I&K 9T4I c46TI", +" bj7I 7j:I bT7I sI7c 6Ib; 9+4I 49DI; 6Ic; b;7I :>7I 7bDI; 7Ic> 49I;K I;&6K ", +"49TI; c6;TI 7bI;j 7:I>j 7bTI; 7sIc> b64G 4bG6& J49 J4c6 7bG :G7b 7Jb 7J: 4b", +"G6K I&K49G 49JT cJ46T bG7j 7:Gbj J97T sJ7c 6Gb; I;&6G 49J; J4g9 bG7d :I7d J", +"97d :Jg7 49IdK IdK49+ J49Td cJ6T; 7bGdj 7:Idj 7JbTd g7Js: bN6I 6NI& b6DIN 6", +"IcN b61IN 6I:N 6DIb1N :c6IN 6IbQ QI6& b6QTI 6QcI b6Q1I 6Q:I 6QIb1T :cQ6I b6", +"I(N b6+IN 9D(6IN c6+IN 6I(b1N :I6+N b(6D1IN c6+:IN b6QI( b6+QI bQ(6TI c6Q+I", +" Ij(b6Q :QI6+ b(6Q1TI sI+c6Q 6GbW WI6& b6JW J6cW b6G1W 6W:I J6b1W J6:W 6GbQ", +" b6GQ& J6Zb cJZ6 b6G1Q :GQb6 ZJ6b1 Z6:J b6GWd b6+WI J6bWd cJ6gW 6WIb1d :WI6", +"+ b6WJ1d :Jg6W b6GQd b6+GQ ZJ6bd Zc6gJ b6WIdj :QI6d ZbdJ6j Z:Jg6 6Ibm Im6& ", +"49DIm 6Icm bm7I :m7I 7bDIm cm7I 9Q4I Im&6Q 49QTI c4Q6I pb7I 7I:p 7pbTI 7Icp", +" 49Im; Im&6+ 9Dm6I; c6m+I 7bIm; 7:I>m 9Dm7I; 7c>Im 49QI; I;&6Q 6QIbT; c6QI;", +" 7pbI; :pI7> 7D;p9I 7cp>I 6Gbm Im&6G 49Jm J6cm bG7W :W7I J97W 7W:J 4bG6Q b6", +"G4Q& J4Z9 Zc4J6 pb7G :pG7b 7bJp Z:7J 49vI 6Iv& vbJ6 c6Jv vb7G 7I:v 7bJv :v7", +"J vb6GQ v6IQ& Jv6Zb cv6ZJ 7vbpG :vI7p Jp97v 7Jy: 3FL 3F!L 3FLB 3FaL FL3. 3F", +"8L 3L.FB 8a3FL M3F !3MF M3TF M3aF j3MF M38F TMj3F M3sF (F3L 3F+L 3FL(B +a3F", +"L 3L.(F +83FL (BL3F. 8a3+FL 3(MF M3+F TM3(F aM3+F Mj3(F +M83F Mj3T(F s+M3F ", +"G3L !3GL H3GL a3GL 3LG. 83GL GH3L. G8H3L GM3 M3G! G3HM G3aM MjG3 G38M GTMj3", +" GMs3 d3GL G3+L GHd3L G3gL Gd3L. +G83L Hd3GL. gG83L G3dM +3GM dM3GT GMg3 dM", +"jG3 +GM83 GTMdj3 gsGM3 4FL !F4L LB4F aF4L 5F4L 8F4L 45FLB 48aFL 4MF MF4! TM", +"4F 4FaM 4F5M 4F8M 4T5MF 4MsF ;F4L +F4L 4;FLB a;F4L 45;FL 4F>L 5;F4LB >F4aL ", +"4FM; 4F+M M;F4T aM;4F M;F4j 4M>F 4T5M;F >Ms4F 4GL GL4! GH4L 4LGa 4LG5 4LG8 ", +"4GH5L 4G8HL 4GM 4!GM GT4M Ga4M G54M G84M 4GT5M 4GsM 4LG; 4L+G 4GHdL 4GgL 4G", +"5dL 4G>L G5H4dL g>4GL GM4d +G4M 4GTdM 4GgM 4GM5d 4G>M G5T4dM >Mg4G FL3N 3FN", +"!L 3FNLB a3FLN 3L.FN 83FLN .BL3FN a3F8LN M3QF Q!M3F QTM3F aM3QF QMj3F Q8M3F", +" TMjQ3F sQM3F 3FN(L +3FLN (BL3FN a3F+LN N.(3FL 83F+LN (FLBN.3 3L+N8aF QM3(F", +" +QM3F TM3Q(F +Q3aMF Mj3Q(F Q83+MF 3(MFQTj +QMs3F W3GL W!G3L GHW3L GaW3L GW", +"3L. G8W3L HW3GL. W83GaL G3WM GQM!3 GMZ3 ZGaM3 GQMj3 GQ8M3 ZGMj3 sZGM3 GWd3L", +" +GW3L HWdG3L gGW3L Wd3GL. G83+WL W3HLGd. G8Wg3L dM3GQ +GQM3 ZGdM3 gZGM3 GQ", +"Mdj3 dM3GQ8 dMjZG3 sZGgM3 mF4L 4mF!L 4mFLB amF4L 45mFL 48mFL 5mF4LB 48FamL ", +"4FMm MmF4! MmF4T aMm4F 4MpF p48MF p4TMF sp4MF 4m;FL 4+mFL m;F4LB 4+FamL 5m;", +"4FL >m4FL mL4B5;F amF>L4 MmF4; 4+QMF 4QTM;F aMF4+Q p4M;F >Mp4F M;Fp4T sp4>M", +"F 4LGm 4GW!L 4GHWL 4GaWL 4G5WL 4G8WL G5H4WL amL4G8 GQ4M 4GQM! 4GZM Z4GaM 4G", +"pM pG48M Zp4GM sZ4GM 4GvL v4+GL v4GHL gv4GL v4G5L v>4GL 4GHv5L g4Lv>G 4GvM ", +"vM4+G Zv4GM gZ4GM pv4GM >Mv4G pvGZ4M 4GyM 3F#L 3F&L 3FL#B a&3FL 3L.#F 8&3FL", +" #L.3BF a&38FL #3MF &3MF TM#3F aM3&F Mj#3F M&38F Mj#T3F sM&3F 3FL#( +&3FL #", +"3(LBF a&3+FL #(F3L. 8&3+FL (FLB#3. 3F+L8a& M#3(F M&3+F M#3T(F +T3M&F j#3M(F", +" +M3&jF 3(MFTj# M&3s+F #3GL &3GL GH#3L Ga&3L G#3L. G8&3L H#3GL. H83G&L M#G3", +" G3M& GTM#3 GT&M3 GMj#3 G8M&3 HM#Gj3 sGM&3 Gd#3L d&3GL Hd#G3L gG&3L d#3GL. ", +"G83d&L H#G3dL. G8&g3L dM#G3 dM&G3 GT#dM3 gMG&3 GM#dj3 dM3G&j dMGjH#3 sG&gM3", +" #F4L &F4L 4#FLB 4a&FL 45#FL 48&FL 5B#4FL 5aF4&L M#4F 4FM& 4TM#F 4T&MF 4M5#", +"F 48M&F 5T#4MF s4M&F 4;#FL ;&F4L ;#F4LB 4aF;&L 5;#4FL >&4FL 5#4L;FB 4a&>FL ", +"M;#4F M;&4F 4T#M;F a;F4M& 4M#;jF >M4&F 5FM;4T# s4&>MF G#4L 4LG& 4GH#L 4Ga&L", +" 4G5#L 4G8&L G5#4HL 4GLH5& 4#GM GM4& 4GTM# 4GTM& 4GM5# 4G8M& 4G#H5M s4GM& 4", +"Gd#L 4+G&L G;#4HL g4G&L G5#4dL >G4&L HL5d4G# >G&g4L 4GMd# dM&4G dM#4GT g4MG", +"& dM#4Gj >MG4& 5#dM4GT >M&s4G 3FN#L &3FLN #LN3BF &3FaLN #LN3F. &3F8LN 3LNB#", +"F. 3F&N8aL QM#3F M&3QF TM#Q3F Qa3M&F Mj#Q3F Q83M&F TMQj#3F M&3sQF #(L3FN &3", +"F+LN (FLB#3N 3L+Na&F 3F#LN.( 3L+N8&F (BN.3F#L 8a+F3L&N M#3Q(F +Q3M&F 3(MFQT", +"# Q3aM+&F 3(MFQj# j3+FQ&M TjQ#3(MF M&s3+QF GW#3L G&W3L HW#G3L Wa3G&L W#3GL.", +" W83G&L W3HLG#. HLW&G83 GQM#3 GQ&M3 ZGM#3 Z&GM3 WM#Gj3 M&3GQ8 GMjZ#3 sGMZ&3", +" Wd#G3L G&3+WL W3HLGd# G&Wg3L W3GLd#. GW+38&L 3LH.WdG# GWg38&L GQ#dM3 dM3GQ", +"& dM#ZG3 gMGZ&3 Q#G3dMj W38M+G& dMZjG#3 sGgZM&3 4m#FL m&F4L m#F4LB 4aFm&L 5", +"m#4FL 48Fm&L mL4B5#F am5&4FL Mm#4F Mm&4F 4T#MmF aMF4Q& p4M#F p&4MF 4TMp#F s", +"4Mp&F m;#4FL 4+Fm&L mL4B;#F a;4&mFL 5#4Lm;F m&F>L4 LB;F5m4# am>&4FL 4Q#M;F ", +"M;F4Q& Q#M;4TF MmT&4+F M;#p4F p&4>MF M;p#4TF >&s4pMF 4GW#L 4G&WL Gm#4HL amL", +"4G& G5#4WL m&L4G8 W#4LG5H HL5&4GW 4GQM# Mm&4G Z4GM# Z4&GM pG4M# pG&4M pGMZ4", +"# Z4MpG& v4G#L v&4GL 4GHv#L v&Gg4L 4G5v#L >G4v&L 45v#GHL >&g4vGL vM4G# vM&4", +"G vMGZ4# Z4GvM& vM4pG# >M4pG& ZGvMp4# y&4GM UI3 I3U! DIU3 U3aI I3U. U38I UD", +"I3. U8ID3 IM3 I!M3 VI3 aIV3 U3Ij 83IM IjV3 VIs3 I3U( U3+I UDI3( U+DI3 UI3(.", +" U+8I3 DI3U(. +DIU83 M3I( +3IM I3V( +IV3 IM3j( U+jI3 VIj3( sIV+3 UG3 G3U! J", +"U3 UaJ3 G3U. U3G8 U3J. U8J3 U3GM IM3G! VJ3 VaJ3 U3Gj UG8M3 VjJ3 s3VJ U3Id U", +"3+G UdJ3 JUg3 Id3U. U+G83 JUd3. gJU83 I3dM IdM+3 J3Vd g3VJ IdMj3 IdM83 JjVd", +"3 gVJs3 4UI U!4I UD4I Ua4I U54I U84I 4U5DI 4U8aI 4IM 4!IM V4I 4aVI Uj4I 8I4", +"M 4IV5 V4sI U;4I U+4I 4UDI; 4U+aI 4U5I; 4U>I U5D4I; >I4Ua I;4M +I4M 4IV; 4+", +"VI IM;4j 4I>M V45I; >IV4 4UG 4!UG J4U 4UJa U54G UG48 4UJ5 4UJ8 UG4M 4UGM! J", +"4V JaV4 UG4j 4UG8M V4J5 VJs4 UG4d U+4G 4UJ; J4gU 4UG5d 4U>G J4U5d >UJ4 Id4M", +" IdM4+ V4J; J4gV IdM4j >MG4U J4V5d J4V> I3UN U!I3N UDI3N UaI3N UI3N. U8I3N ", +"DI3UN. 8I3UaN U3QI IM3Q! QIV3 VQaI3 IM3Qj UQ8I3 VQIj3 sIVQ3 UI3(N U+I3N DI3", +"U(N +I3UaN I3(UN. +I3U8N D3UNI(. +3UND8I IM3Q( U+QI3 VQI3( V+QI3 UQ3Ij( Ij3", +"U+Q Ij3VQ( V+QsI3 U3WI UGW!3 UWJ3 JUWa3 UGW3. UG8W3 JUW3. J8UW3 U3GQ UGQ!3 ", +"Z3VJ ZJVa3 UGQj3 UGQ83 ZJVj3 sJZV3 Id3UW U+GW3 JUWd3 gJUW3 UW3Id. Id3UW8 UW", +"dJ3. gUWJ83 IdMW3 U+GQ3 ZJVd3 gVZJ3 Id3UWj Id3UQ8 VZdJj3 sJZgV3 Um4I Im!4U ", +"4UDIm 4UaIm 4U5Im 4U8Im U5D4Im U8m4aI UQ4I IMm4! 4QVI V4QaI 4UpI pU48I pIV4", +" Vps4I Im;4U 4U+Im 4UDIm; U+m4aI 4U5Im; >I4Um ImU;4D5 4Ua>Im IMm4; IMm4+ V4", +"QI; V4+QI pU4I; >MIp4 Vp4I; V>p4I UG4W 4UGW! 4UJm J4UWa 4UG5W 4UGW8 J4U5W J", +"48UW UG4Q 4UGQ! VZJ4 ZJ4Va 4UpG pUG48 J4Vp JpVs4 4UvI vU4+G vUJ4 Jvg4U vU4G", +"5 v>U4G Jv4U5 J>v4U 4IvM vIM4+ J4Vv VvJg4 pvU4G >Mv4I JpVv4 J4yV UIb3 U3I& ", +"bUDI3 UIc3 bUI3. I&3U8 UDIb3. cU8I3 b3IM I3M& bIV3 VIc3 IM3bj IM&83 VbIj3 s", +"IVc3 bUI3( I&3U+ UDIb3( cU+I3 UI3b(. U+8I&3 3(U.bDI U+8cI3 IM3b( IM&+3 VbI3", +"( Vc+I3 bM3Ij( IM3b+j Ij3Vb( Vc+sI3 b3UG U3G& bUJ3 JUc3 bGU3. bG8U3 JbU3. c", +"JU83 b3GM IM&G3 VbJ3 c3VJ bGMj3 bG8M3 JjVb3 sJVc3 Id3bU b+GU3 JbUd3 cJUg3 U", +"G3bd. I&3U8d bdUJ3. gcUJ83 IdMb3 IdM&3 VJbd3 gVJc3 IM3bdj Id3b8M VdbJj3 sJc", +"gV3 bU4I U&4I 4bUDI 4UcI 4bU5I 4b8UI b5U4DI c4U5I bM4I IM4& 4bVI cIV4 4bM5I", +" IM&48 V4b5I c4VsI 4bUI; I;&4U b;U4DI c4U+I b5U4I; >I4U& 45b;UDI c>4UI IM;4", +"b IM;4& V4bI; c4V+I 4bMI;j >Mb4I I;jV4b V>c4I bG4U UG4& 4bJU cUJ4 4bGU5 4bG", +"U8 J4bU5 cJ4U5 bG4M IM&4G JbV4 VJc4 4bGUj 4bG8M J4Vb5 sJ4Vc 4bGUd 4b+UG J4b", +"Ud cJ4gU bG54Ud >bG4U 4bdJ5U J>c4U IdM4b IdM4& J4Vbd gV4cJ 4bMIdj >Mb4G J4b", +"V5d J>Vc4 bUI3N I&3UN UDIb3N cUI3N UI3bN. U83I&N D3UNbI. U8Ic3N IM3bQ IM&Q3", +" VbQI3 cQVI3 bQUIj3 IM3bQ8 Ij3VbQ cQVsI3 UI3b(N U+3I&N D3UNbI( U+Ic3N 3(U.b", +"IN b3U+8IN UDb.3(IN UIc3+8N bQ3IM( IM3b+Q bQIV3( V+IcQ3 Qjb3IM( b3+Q8IM IjV", +"3bQ( s+cQVI3 bGUW3 UG&W3 JbUW3 cJUW3 UG3bW. I&3UW8 bWUJ3. J8UcW3 bGQU3 IM&W", +"3 ZJVb3 ZcJV3 IM3bWj IM3bW8 JjVZb3 ZcVsJ3 bWUId3 Id3UW& bWUJd3 gJUcW3 Wdb3U", +"G. b3+GUW8 W3J.bdU gWJ8cU3 Id3bWM Id3UQ& VJdZb3 ZcJgV3 IdWjbM3 +WUjI&3 djJ3", +"ZbV g3sJZcV 4bUIm Im&4U bmU4DI c4UIm b5U4Im 4U8Im& 45bmUDI 4U8cmI IMm4b IMm", +"4& V4bQI c4VQI pb4IM pU&4I Vp4bI cpV4I 4bUIm; 4U+Im& Imb;4UD 4U+cmI Imb;4U5", +" Im&>U4 bU4I5mD; c4U>Im 4bQIM; 4b+IMm 4bQV;I c4IV+m IM;pb4 >I4pU& pbIV4; V>", +"4cpI 4bGUW 4UGW& J4bUW cJ4UW bG54UW bG84UW 4bWJ5U c4UJ8m 4bGUQ IM&4W ZJ4Vb ", +"Zc4VJ pbG4U pUG4& JpV4b cpJV4 vb4UG vU&4G Jv4bU cvJ4U 4bGvU5 >I4vU& vb4J5U ", +"J>4cvU vbM4G vIM4& VvJ4b cvVJ4 pb4vIM >I4vbM Vv4Jpb yJVc4 6kF kF6! kF6B 6Fa", +"k 1k6F 6F8k 61kFB akF68 6MF MF6! 6FTk 6FaM l6F 68lF 6TlF sFl6 k(6F 6F+k 6k(", +"FB akF6+ 61k(F 6+1kF 1k(6FB 6+1akF M(6F 6F+M 6Tk(F 6+TkF 6(lF 6+lF l6T(F ls", +"6+F 6Gk 6!Gk Gk6H Gk6a 61Gk Gk68 6G1Hk 6G8Hk 6GM 6!GM GT6M Ga6M lG6 6Gl8 6G", +"lH s6lG Gk6d +k6G 6GHdk 6Ggk 6G1dk 6+G8k 1GH6dk g61Gk GM6d +G6M 6GTdk 6GgM ", +"6Gld 6+lG lG6Td g6lG 4kF kF4! kF4B 4Fak 7kF 8k7F kF7B ak7F 4F6M 4kM!F 4FTk ", +"4TkaF 7lF 78lF lF7T sF7l 4Fk; 4F+k k;F4B ak;4F k;7F 7k>F 7Bk;F 7>akF 6FM; 4", +"+kMF 4Tk;F aM;6F 7;lF >F7l 7lT;F l>7sF 4Gk 4!Gk Gk4H Ga4k 7Gk G87k Gk7H Ga7", +"k 6G4M 4GkM! GT4k 4GTak lG7 l87G 7GlH lG7s Gk4d +G4k 4GkH; 4Ggk Gk7d 7G>k 7", +"GHdk 7Ggk GM6; 4+G6M 4GTdk g4kGT 7Gld 7>lG 7GTld lGg7 kF6N 6!kFN 6kFNB akF6", +"N 61kFN 68kFN 1kF6NB 68FakN 6FQk 6Qk!F 6QTkF 6QakF 6QlF l6Q8F l6QTF ls6QF 6", +"k(FN 6+kFN k(F6NB 6+FakN 1k(6FN +kF68N 1k6(NBF 6F1+akN 6Qk(F 6+QkF Qk(6TF a", +"kF6+Q l6Q(F l+6QF 6QTl(F s6Ql+F Gk6W 6GWk! 6GHWk 6GaWk 6G1Wk 6G8Wk 1GH6Wk G", +"8k6Wa GQ6M 6GQM! 6GZk Z6Gak 6GlW lG6W8 Z6lG lZs6G 6GWdk 6+GWk GkH6Wd g6GWk ", +"1GW6dk +Gk6W8 61GkHWd 6G8gkW 6GQdk 6+GWM Z6Gdk gZ6Gk lG6Qd l+G6W lZ6Gd glZ6", +"G 4Fkm kmF6! kmF4B akm4F km7F 78kmF 7BkmF 7akmF 4FQk 4Qk!F 4QkTF aMm6F lF7p", +" 7lp8F 7lpTF 7slpF km;4F 4+kmF 6mFk;B akF6+m 7km;F 7>mkF km;7BF >km7aF 4Qk;", +"F 4+QkF kmF6T; akF4+Q 7lp;F l>p7F l;p7TF 7spl>F Gk4W 4GkW! 4GkHm akm4G Gk7W", +" 7GW8k 7GHWk 7GaWk GQ4k 4GQk! 4GZk Z4kGa 7plG 7pGl8 lG7Z 7sGlZ 4Gvk v6+Gk v", +"6GHk gv6Gk vk7G 7>Gvk 7vGHk g7vGk 6GvM v6M+G Zv6Gk Z4kgG lG7v l>G7v lvZ7G l", +"Gy7 9k6F 6Fk& 69kFB akF6& 691kF 6k:F 91k6FB :6akF 6F9M 6FM& 6T9kF 6T&kF 69l", +"F :6lF l69TF ls:6F 69k(F k&F6+ 9k(6FB 6+9akF 91k6(F :6+kF 916k(BF akF:6+ 6M", +"9(F 6+M9F 9T(6MF k&F6+T l69(F l:6+F 6T9l(F s:6l+F 69Gk Gk6& 6G9Hk 6Gak& 6G9", +"1k 6G:k 9G16Hk :G6Hk 9G6M GM6& 6GT9M 6GTk& 6Gl9 l6:G lG69H lsG:6 6G9dk dk&6", +"G 9GH6dk g69Gk 9G16dk :G6+k 691HGkd g:6Gk 6GM9d dM&6G 9GT6dM g6M9G lG69d l:", +"G6+ 6GTld9 gl:6G 9k4F 4Fk& 49kFB 49akF 9k7F :k7F 7B9kF 7:akF 4F9M 49M&F 49T", +"kF 49aMF lF79 lF7: 7l9TF 7sl:F k;F49 k;&4F 49Fk;B akF49+ 79k;F 7:>kF k;F7B9", +" :>F7ak 49M;F M;&6F k;F49T aMF49+ 7l9;F l>7:F l;97TF 7s:l>F 9G4k Gk4& 49GHk", +" 49Gak 9G7k 7k:G 7G9Hk 7:GHk 9G4M 49GM& 49GHM 49GaM l97G 7:lG 7GTl9 7sGl: 4", +"9Gdk dk&4G 9G;4Hk g49Gk 7G9dk 7:G>k 9G;7Hk g7:Gk 49GdM M;&6G 9GT4dk g49GM l", +"G79d l>G7: 7G9lH; gl7:G 69kFN k&F6N 9kF6NB 6aFk&N 91k6FN :6kFN 916kNBF akF:", +"6N 6Q9kF 6Q&kF 9QT6MF akF6Q& l69QF :Q6lF 6Q9lTF ls6:QF 9k(6FN 6+Fk&N 9k6(NB", +"F ak6&+FN 916k(FN 6+k:FN NB1F9k6( ak:F6+N 9Q(6MF k&F6+Q QT6F9M( 6F+Q9aM 6Q9", +"l(F l+6:QF 69l(QTF l6:Qs+F 6G9Wk 6G&Wk 9GH6Wk Gak6W& 9G16Wk :G6Wk 691HGkW 6", +"Ga:Wk 6GQ9M 6GQk& Z69Gk Z6&Gk lG69W :GQl6 lZ69G Z:l6G 9GW6dk +Gk6W& 69HkGWd", +" 6G&gkW 691WGkd 6+G:Wk GH1d69Wk g6G:Wk 9GQ6dM 6GQdk& 6GMZ9d Z6Gg9M 6GQld9 :", +"G6l+W lG6Z9d Z:6glG kmF49 km&4F 49FkmB akF6m& 79kmF 7:kmF kmF7B9 :mk7aF 49Q", +"kF Mm&6F kmF49T akF49Q 7lp9F :pl7F p9l7TF :pl7sF 49mk;F kmF49+ km6;9FB ak6m", +";&F km;79F :mk7>F km7B9;F ak:m7>F k;F49Q k;F4Q& Tk9;4QF aM9m4+F l;97pF :p7l", +">F l97Tp;F >F7s:pl 49GWk km&4G 9Gm4Hk 49Gakm 7G9Wk 7:GWk 9Gm7Hk 7Gk:Hm 49GW", +"M Mm&6G Z49Gk Z4kG& 7pGl9 :pG7l 7Zl9G 7Z:lG v69Gk v6&Gk 49GvkH g4Gvk& 7v9Gk", +" :vG7k v9G7Hk g7G:vk v6M9G v6MG& Z4Gv9M Z4Gg9M lv79G lv:7G 7Z9lvG yl7:G 6UI", +" 6!Ik Dk6I Ua6I 1U6I U86I 6U1DI 6U8aI 6IM 6!IM V6I 6aVI lI6 68lI V6lI s6lI ", +"6(Ik U+6I Ik(6D 6U+aI Ik(61 6U+1I 6D1Ik( 1U+6aI 6(IM +I6M 6IV( 6+VI 6Il( 6+", +"lI Vl6I( lsI6+ 6UG 6!UG J6U 6UJa 1U6G UG68 6UJ1 6UJ8 UG6M 6UGM! J6V JaV6 6U", +"lG lG6U8 lJ6 s6lJ UG6d U+6G 6UJd J6gU Idk61 Idk68 J61Ud gJ61U Id6M IdM6+ Vd", +"J6 J6gV 6Ild l+G6U J6ld g6lJ 4Ik 4!Ik Dk4I aI4k 7UI U87I Dk7I Ua7I 6I4M 4Ik", +"M! 4IV6 V46aI lI7 l87I 7VI lI7s U;6I +I4k Ik;4D ak;4I U;7I 7I>k 7UDI; 7>UaI", +" IM6; IM;6+ 6IV; V4+6I 7Il; 7>lI V;7I 7IV> 6U4G 4UG6! J4k 4aJk 7UG UG78 7JU", +" J87U 4UG6M Ik!4GM V4J6 J4V6a 7UlG 7UGl8 7Jl sJ7V UG6; Idk4+ 4dJk J4gk UG7d", +" >U7G Jk7d >k7J Idk4M 4+kIdM V6J; gV4J6 7Ild l>G7U ld7J 7Jgl 6NIk IkN6! IkN", +"6D 6UaIN IkN61 6U8IN 6D1IkN akN68I UQ6I 6UQI! 6QVI V6QaI 6QlI lI6Q8 Vl6QI l", +"sI6Q Ik(6N 6U+IN 6D(IkN akN6+I 1U(6IN 6UN1+I 6(1UDIN 6NU+1aI 6UQI( 6U+QI V6", +"QI( V6+QI lI6Q( l+I6Q V6QlI( sIQV6+ UG6W 6UGW! 6UJW J6WUa 6UG1W 6UGW8 J61UW", +" J68UW UG6Q 6UGQ! J6Zk ZJ6Va 6WlI lWI68 Z6lJ lJZs6 Idk6W 6U+WI J6WUd gJ6UW ", +"1UW6Id 6W8Idk 6UWJ1d J6Wg1U Idk6Q 6U+GQ ZJ6Vd gV6ZJ lWI6d l+W6I lJZ6d glJZ6", +" Um6I Ikm6! Ikm4D akm4I Um7I 7U8Im 7UDIm 7UaIm QI4k IMm6! 6IVm V6maI 7plI 7", +"pU8I 7IVp 7sIVp Ikm6; Ikm4+ 6DmIk; 4+DIkm 7UIm; 7>UIm Ikm7D; >Im7Ua Ik;4Q I", +"Mm6+ V6mI; V6+Im 7pUI; l>I7p 7VpI; 7V>pI UG6m 4WIk! 4WJk J4kWa UG7W 7UGW8 J", +"k7W 7JUW8 IMm6G Ik!4GQ J4Zk ZJ4ak 7WlI 7pUG8 Jp7V Jpl7s 4Ivk v6U+G v6Jk Jv6", +"gU vU7G 7vU>G 7UJv J>k7v 6IvM v6I+M J6Vv Vv6gJ lI7v lvI7> 7Jlv 7Jyl t6I 6It", +"& 6DtI tIc6 6It1 t6:I t61DI t:c6I 6ItM IM6& tIV6 cIV6 t6lI l6:I ltV6I u6I 6", +"It( 6+tI t6DI( tc6+I t61I( t:6+I 6D1tI( :c6t+I t6IM( t+6IM Vt6I( t+V6I lt6I", +"( lt+6I Vt6lI( 6+uI tG6 6Gt& tJ6 c6tJ 6Gt1 t6:G t6J1 J6t: 6GtM tG6M& VtJ6 V", +"Jc6 l6tG t:Gl6 J6lt uJ6 6Gtd 6+tG J6td g6tJ tG61d t:G6+ tJ61d gtJ:6 tG6dM t", +"+G6M tdVJ6 gV6tJ ltG6d t+Gl6 lJt6d gJu6 t4I 4It& 4DtI tIc4 7tI 7It: tD7I 7I", +"tc 4ItM t4IM& tIV4 tc4VI lI7t 7:lI Vt7I u7I 4It; 4+tI t4DI; tc4+I 7It; 7It>", +" 7tDI; t>7cI t4IM; t4+IM t4VI; t4V+I 7tlI; l>I7t 7VtI; 7>uI t4G 4Gt& tJ4 cJ", +"t4 7tG 7:tG 7Jt tJ7c 4GtM t4GM& VJt4 tJ4Vc lG7t 7tGl: tJ7V uJ7 4Gtd 4+tG J4", +"td g4tJ 7Gtd 7>tG td7J 7Jgt t4GdM t4+GM tJ4Vd gt4VJ 7tGld t>G7l lJ;7t g7uJ ", +"6ItN t6I&N t6DIN tc6IN t61IN t:6IN 6D1tIN :I6tcN 6QtI tQ6I& tQV6I tcQ6I tQ6", +"lI t:Q6I Vl6tQI 6QuI t6I(N t+6IN 6DIt(N c6It+N 6I1t(N :I6t+N 61t(DIN t6:Ic+", +"N tQ6I( t+Q6I V6ItQ( t+Ic6Q lI6tQ( t+6:QI l6tQVI( u6+QI 6GtW tG6W& J6tW tcW", +"J6 tG61W t:G6W tJ61W :Jt6W 6GtQ tGQ6& Z6tJ Zc6tJ tGQl6 t:G6Q lJZt6 ZJu6 tG6", +"Wd t+G6W tJ6Wd gtJ6W 6WItd1 t+6:WI J61tWd :J6gtW tGQ6d t+G6Q tZJ6d gtZJ6 tW", +"6lId t+6:GQ tZ6lJd ug6ZJ 4Itm t4Im& t4DIm tc4Im 7Itm t:m7I 7tDIm 7ctIm 4QtI", +" t4QI& t4VQI tc4QI 7Itp :pI7t tp7VI 7puI t4Im; t4+Im 6Dmt;I t+Ic6m 7tIm; t>", +"7Im 7DItm; 7cIt>m t4QI; t4+QI tQIV6; t4IcQ; tp7I; tp>7I 7VItp; u7Ip> 4GtW t", +"4GW& J4tW tJ4cW 7GtW 7tG:W tW7J 7Jt:W 4GtQ t4GQ& ZJt4 tZ4cJ 7ptG tpG7: 7JtZ", +" 7ZuJ v6tG tv4+G t4Jv cv6tJ tG7v :vI7t 7Jtv g7tJv tv4GQ t4+GQ Vv6tJ cvVJ6 l", +"vt7G :vI7l lvJ7t yJu7 e= 5e=B =a>e =eK +e=K =Te +T=e =je =j>e 5T=e s=e 4G", +"< 4!G< 4He Ga4e G5< G84e H54e H84e 4KG< 4G= f5=H f>g= f=K f+=K =Tf gf=T =jf =jf> f5=T sf= m =B5em >e=am", +" =Qe +Q=e Qe=T Qa=e p=e >ep= pe=T sep= 4We 4!We Hf= =H", +"fv5 g=fv> =Qf =+fQ Z=f Zfg= fp= p=f> fpZ= yf= ,e =B5,e >e-=a ,e=K -e=K ,T=e =e-T ,j=e =", +"e-j =T5,e -=se ,4G 4&,G 4G,H 4G,a 4G,5 4G,8 ,4GH5 ,4GH8 ,K4G ,4G&K 4G,T ,4G", +"T& 4G,j <&j4G ,4GHj ,4sG f=, f-= fH=, f-g= =,f5 ->f= =Hf,5 g=f-> f,=K =Kf- ", +"=,fT f-=T =,fj f-=j =Hjf, f-s= e-=m emB=5, =a->em ,Q=e =e-Q =QT,e =Q-Te =ep, pe-= p=T,e -ps=e 4G,", +"W We4& ,4GHm -v= fv5=H, f>-g=v =,fQ f-=Q f=Z,", +" f-Z= p,f= f-p= fpZ=, f-y= I< I 5D=I =D>e =IK +I=K", +" =TI +T=I =Ij >I=j 5T=I s=I G<4I I J=5 J>= f", +"I=K =If+K J=T =TgJ fj=I f>=Ij J=j s=J Im=I =D5Im >eD=m =QI +Q=I QD=I Qa=I p", +"=I p>=I pD=I sp=I WI4e I=I v=J5 v>J= fQ=I =Qf+", +"I Z=J gZJ= =Ifp fp>=I Jp= yJ= h< h<& hD< hc< h<. h8< D= h5=D c=h> h=K =Kh- h=", +"T hc=T h=j =jh> h5=T hs= h4G 4Gh& hJ4 cJh4 4Gh5 4Gh8 J4h5 J4h8 4GhK h4G&K J", +"4hT cJ4hT 4Ghj h48Gj J4hj h4sJ h=f f-h= J=h i= f=h5 f>h= h5J= i>= =Khf h=f-", +"K hJ=T i=T hf=j h=jf> hJ=j is= h=hm h=5Dm c=h>m h=Q h-=Q hQ=D hc=Q hp= h=-p =", +"Dhp s=hp h4W 4Wh& J4hW c4hW 4Wh5 4Wh8 J5h= J=hv5 v>i= =Qhv h=Q-v hZ= iZ= h", +"=fp fp-h= Z=hp yi= 6e 6e! 6eB 6ae 6e1 68e 6B1e 1a6e 6eK eK6! 6Te Te6a 6ej 8", +"e6j 1T6e s6e 6e* 6+e *B6e +e6a 1e6* 1+6e 6e1*B 6+e1a e*6K 6K+e Te6* +T6e ej", +"6* +e6j 6Te1* 6+se 6G< 6!G< 6He Ga6e 1G< G86e 1H6e Ga1< 6KG< 6Ge7 =B7e 7a>e 6e=K =6+eK 6T=e =T6+e ", +"7e=j >j7e 7e=T >e7s 6G4e 4G6 7fH g7f =Kf6 f=6+", +"K f6=T g=f6T 7fj fj7> 7fT sf7 6eN eN6! eN6B 6Nae 6N1e 6N8e 6e1NB 68eaN 6Qe ", +"Q!6e Qe6T Qa6e 1Q6e Q86e 6Qe1T 6Qse e*6N 6N+e 6e*NB 6+eaN 6e1*N 6+e1N 1eN6*", +"B aeN68* Qe6* +Q6e 6QeT* 6+QTe 6Qe1* 6+Q1e 1Qe6T* s6e+Q 6We 6!We He6W Wa6e ", +"1W6e W86e 6He1W 6H8We GQ6e 6GQm7e 7=emB 7ae>m 6Q=e =Q6+e =Q6Te =Qa6e 7ep= 7p>e 7p", +"e=T 7sep= 6W4e 4We6! He6m aem6G 7We W87e He7W Wa7e 4GQ6e 7v fv7H gf7v f6=Q =Qf6+ fvZ6 Z", +"f6g= 7fp 7pf> 7Zf y7f ?e ?e& ?Be ?ae ?1e ?:e 1e?B :e?a ?eK e&?K ?Te Te?a ?j", +"e :e?j 1T?e s?e ?e* ?-e *B?e -a?e 1e?* :e?- ?B1e* ?:-ae e*?K -e?K Te?* ?e-T", +" ej?* ?e-j ?T1e* ?-se ?G ?G& ?GH ?Ga ?G1 ?:G 1G?H :G?H ?GK G&?K ?GT GT?a ?G", +"j :G?j 1G?T s?G ?f ?f- ?fH g?f ?f1 :f? f1?H :fg? ?fK ?Kf- ?fT ?Tgf ?fj ?j:f", +" f1?T s?f ?4e 4e?& 4e?B 4a?e 7?e 7:e ?B7e 7e?a 4e?K ?4e&K 4T?e ?T4ae 7e?j 7", +"j:e 7e?T 7?se ?=e =e?- ?B=e =e?a 7e?= Ae ?=7eB Aae ?e=K ?-=eK =e?T ?-T=e =e", +"?j Aje ?T7=e Ase ?G4 4G?& 4G?H 4G?a ?G7 7:?G 7G?H 7G?a 4G?K ?G4&K 4G?T ?GT4", +"a 7G?j ?:G7j 7G?T ?G7s ?f= f-?= =H?f ?fg= 7f? Af ?H7f Agf =K?f ?f-=K =T?f ?", +"fTg= =j?f Afj ?T7f Asf ?eN e&?N eN?B ae?N 1e?N ?N:e ?B1eN ?:aeN ?Qe Qe?& Qe", +"?T Qa?e 1Q?e :e?Q ?Q1Te ?Qse e*?N -e?N ?Be*N ?-aeN ?1e*N ?:-eN 1e*?BN :-e?a", +"N Qe?* ?e-Q ?QTe* ?-QTe ?Q1e* ?:Q-e 1Qe?T* s?-Qe ?GW G&?W He?W Ga?W 1G?W :G", +"?W ?G1HW ?:GWa ?GQ GQ?& ?ZG Ze?a 1G?Q :G?Q Z1?G sG?Z ?fW f-?W fH?W ?Wgf f1?", +"W ?W:f ?f1HW g?:fW ?fQ f-?Q ?Zf ?fZ- f1?Q ?Q:f ?jZf ?Zsf ?me em?& em?B ae?m", +" 7e?m :e?m ?m7eB ?:mae 4Q?e ?Q4e& Te?m ?Qa4e ?pe 7e:p pe?T se?p =e?m ?e-m ?", +"=emB ?-mae ?=7em Aem 7=e?mB aeAm =e?Q ?-Q=e ?Tm=e ?-Tem pe?= Ape ?p=Te spAe", +" ?Gm Gm?& Gm?H Ga?m 7G?W :G?m ?G7Hm ?:GHm 4G?Q ?GQ4& Z4?G ?Z4Ga ?pG ?G:p ?G", +"7Z sG?p ?vf -v?f fv?H gf?v 7f?v Afv ?v7fH gfAv =Q?f ?fQ-v ?fZ= g?Zfv ?pf Ap", +"f 7f?Z yAf 6I< 6!I< 6De De6a 1I< 8I6e 1D6e D86e 6KI< I=I 7D=I 7D>e 6I=K =I6+K 6T=I =TI6+ 7I=j 7>I=j 7T=I =I7s 4G6I< 4G6I 7Jf J>7f =If6K f6+=IK J6=T J=g6T fj7I 7", +"f>Ij =T7J 7Jsf 6NI< II=m 7De=m 7>Dem 6", +"Q=I =QI6+ =QD6I =Qa6I 7p=I p=I7> p=D7I s=I7p I 7vJ= J>=7v =Qf6I =Q+v6I Z6J= Z=Jg6 7Ifp 7fp>I Z=7", +"J 7Jyf h? h?& h?D ?ch h?1 h:? ?Dh1 ?ch: h?K ?Kh& h?T hT?c h?j ?:hj ?Dhj hs?", +" h?* h?- h*?D h-?c ?1h* ?:h- ?D1h* h:?c- ?Kh* ?Kh- h*?T ?-hT h*?j ?-hj ?Djh", +"* h?s- h?G h&?G ?J ?Jc ?Gh1 ?Gh: ?J1 ?J: ?KhG ?G&hK ?JT cJ?T ?Ghj ?:Ghj ?Jj", +" s?J ?fh h-?f ?Jf i? h1?f ?fh: J1?f i?: ?Khf ?fh-K hT?f i?T hj?f ?fjh: Jf?j", +" i?s h?4 ?4h& ?Dh4 h4?c 7h 7h: 7hD 7hc ?Kh4 h?4&K ?Th4 ?ch4T 7hj 7:hj 7hT h", +"s7 h=? ?-h= h?=D ?ch= 7h= Ah =D7h Ahc =Kh? h=?-K h?=T c=?hT =j7h Ahj =T7h A", +"hs ?Gh4 ?G4h& ?J4 c4?J 7hG 7Gh: ?J7 7c?J ?G4hK h4&?GK J4?T ?Jc4T 7Ghj 7h:Gj", +" hT7J 7Jhs ?fh= h=?f- ?J= i?= 7hf Ahf 7f?J Ai h=?fK ?f-h=K =T?J =Ti? hj7f h", +"fAj =j?J Ais h?N ?&hN hN?D hN?c ?1hN hN?: ?D1hN h:?cN h?Q h&?Q ?QhT hQ?c ?Q", +"h1 ?:hQ ?QDh1 hQs? ?*hN hN?- ?D*hN ?ch-N h?1*N h:?-N h1*?DN ?c-h:N h*?Q ?-h", +"Q ?QDh* ?cQh- h?Q1* h:Q?- ?Q*h1T hs?-Q h?W h&?W ?JW hW?c ?Wh1 ?:hW J1?W ?W:", +"J ?GhQ ?GQh& hZ? ?chZ ?Whj h:W?Q h1?Z s?hZ hW?f ?-hW Jf?W i?W ?fh1W h:W?f ?", +"Jf1W ?:iW hQ?f ?fQh- ?fhZ i?Z ?fQh1 :fQh? hZ?f1 s?iZ h?m h&?m ?Dhm hm?c 7hm", +" ?:hm 7Dhm hm7c ?Qh4 h?Q4& ?Thm ?cQh4 hp? h:?p hT?p s?hp ?=hm ?-hm ?Dmh= c=", +"?hm 7=hm Ahm 7h=Dm hcAm h?=Q h=Q?- h=Q?D c=Qh? h=?p Aph hp?=D hpAs ?Ghm ?Gm", +"h& ?Jm cJ?m 7hW 7:hW hW7J hW7c ?GQh4 ?G&h4Q h4?Z hZ?c4 hW?p hp?:G hZ7 7Zhs ", +"hv? h?-v ?Jv i?v hv7 Ahv 7Jhv Aiv hQ?v hv?-Q ?Zhv ?Ziv ?phv hpAf 7Zhv Aiy <", +"eL L =B5", +"eL >e=aL =Me +M=e Te=M aM=e 5M=e =M>e =T5Me se=M 4LG< 4GLf= =Hf5L g=f>L =Mf =+fM fT=M gf=M f5=M =Mf> =HMf5 =Msf eLe=mL em", +"L=B5 =am>eL Qe=M =Q+Me =QTMe =QaMe pe=M p=>Me p=TMe s=pMe 4LWe 4We!L 4HeWL ", +"4WaeL 4We5L 4W8eL v=L fv5=HL f>vg=L fv=M =Qf+M =MZf g=MZf =M", +"fp fp>=M fpZ=M =Myf e-=L ,5e=BL =a->eL ,M=e =e-M =TM,e =T-Me =M5,e >M-=e ,5T=Me s=-Me ,L", +"4G ,4G&L ,4GHL ,4GaL ,4G5L ,4G8L ,4LH5e <&LG5a 4G,M -=L f5,=HL f>-g=L =,fM f-", +"=M =HMf, g=Mf- =Mf,5 f>M-= =H,f5M sf=-M ,eL 5m=L,eB >m=a-eL =QM,e =Q-Me Mem=T, =Te-Mm", +" p=M,e -p=Me peT=M, s=M-pe ,4GWL L =,f5vHL >Lg=-vf =Qf,M -vMf= Z=f,M Z-=fM fp=,M fp-=M ", +"Z=Mfp, yf-=M Ue =U5De >eU=D =IM +I=M V=I V+=I Ue=j =I>M V5=I >eV= UG4e 4UG J5=U =UJ> fU=M =Uf+M J=V gVJ= fU=j f>U=M =jVf Vfs", +"= UNI< IeU=m U5e=D", +"m >Im=Ua UQ=I =UQ+I Ve=Q V=+QI pU=I p=U>e =IVp V>ep= UW4e 4UWe! UUv= J=vU5 J>=vU fU=Q =Uf+Q VfZ= J=VgZ =Ufp fpU>= V", +"=Jp J=yV hU< U h=U5D c=h>U h=M h-=M V=h h=V- h5=M >Mh= =jVh V=hs 4UhG h4UG& ", +"hUJ4 cJ4hU h4UG5 h48UG J5Uh= J=hU5 =Ui> hf=M h=Mf- h=Vf iV= h=Mf5 h", +">Mf= J=Vh5 s=iV U=Um h5U=Dm h>Uc=m hQ=U h=Q-U =QVh c=VhQ =Uhp -pUh= V=hp cp=V", +"h 4UhW h4WU& J=U hvUJ=5 iv>=U =Mhv h", +"vM-= V=hZ Z=iV fpUh= fp-=U Jp=Vh yVi= 6ek 6!ke 6Bke ak6e e 7=ekB 7ae>k 6M=e =k+Me Tk=e =Tk+e l=e >el= le=T", +" les= G<4k 4Gkk7f fk7H gk7f f6=M =kf+M fT=", +"k g=kfT lf7 l>7f =Hlf g7lf 6Nke k=em =km7eB 7am>ke Qk=e =Qk+e =QkTe =Qake lep= l>ep= l=pTe l=s", +"pe Wk4e k7v 7fvHk g7fvk fQ=k =Qf+", +"k =kZf Z=kgf 7flv lfp7> 7Zlf lfy7 ?ke ke?& ke?B ak?e 1e?k ?k:e ?B1ke ?:ake ", +"?Me Me?& Tk?e aM?e l?e ?:le ?Tle les? ke?* ?e-k ?Bke* ?-ake ?1ke* ?:-ke ke*", +"?B1 :-k?ae Me?* ?e-M ?Tke* ?-Tke ?el* ?-le l?Te* l-?se ?Gk Gk?& Gk?H Ga?k 1", +"G?k ?k:G ?G1Hk ?:GHk ?GM GM?& GT?M Ga?M l?G ?:lG ?GlH lGs? ?fk fk?- fk?H gk", +"?f ?1fk fk?: ?f1Hk g?:fk ?fM f-?M fT?M gM?f lf? ?fl- lH?f lfg? 4k?e ke4& ?B", +"4ke ?a4ke ?k7e 7k:e 7?keB ?a7ke 4M?e ?M4e& ?T4ke ?aM4e 7?le 7:le ?T7le 7sel", +"? ?k=e =e-k ?=ekB =a?ke ?=7ke Ake 7ek?=B akAe =e?M ?-M=e =T?ke ?aM=e ?=le A", +"le l=?Te lsAe 4G?k ?G4k& ?G4Hk ?Ga4k ?k7G ?:G7k ?G7Hk ?G7ak 4G?M ?GM4& ?GT4", +"k ?Ga4M 7l?G l?G7: l?G7H s?G7l =k?f f-=k ?f=Hk g=k?f fk7? Afk 7f?Hk gfAk =M", +"?f ?fM-= ?fT=k g?Mf= ?fl= Alf lf?=H glAf ke?N ke&?N ?BkeN ?akeN ?1keN ?:ekN", +" keN?B1 :ek?aN Qk?e ?Qke& ?QTke ?Qake ?Qle ?:Qle l?QTe s?lQe ?ke*N ?-keN ke", +"*?BN -ke?aN ke*?1N :ek?-N ke?B1*N ak:e?-N ?Qke* ?-Qke Qke?T* ?Qe-Tk l?Qe* l", +"-?Qe leQ?T* s?Ql-e Gk?W ?GWk& ?GHWk ?GaWk ?G1Wk ?:GWk 1He?Wk ?Hk:We GQ?M ?G", +"Qk& Zk?G ?ZGak ?GlW l?G:W lG?Z s?ZlG fk?W ?-Wfk ?fHWk g?fWk ?f1Wk :f?Wk f1H", +"?Wk :fWg?k fQ?M ?fQ-k ?MZf g?Zfk lW?f lf?:W ?Zlf gl?Zf ke?m ?mke& ?mkeB ?am", +"ke ?m7ke ?:mke 7ek?mB ?ak:em Me?m ?Mme& ?Tmke ?aMem le?p :pel? ?plTe s?ple ", +"?=ekm ?-mke =ke?mB ?ae-km 7=e?mk keAm =B?m7ek Akmae ?Mm=e ?-Mem ?Qe=Tk ?Qe=", +"ak l=?pe leAp ?pTl=e Apels Gk?m ?Gmk& ?GmHk ?Gakm ?G7Wk ?:Gkm ?Hk7We ?Gk7Wa", +" GM?m ?GMm& ?Z4Gk ?GaMm lG?p ?pGl: ?Zl7G s?plG fk?v -vk?f ?vfHk g?vfk ?v7fk", +" fvAk 7fH?vk Avkgf fv?M ?vMf- ?Zf=k g?vfM ?plf lfAp lv?Zf ylAf 6Ue 6!Ue Ue6", +"D Ua6e 1U6e U86e 6Ue1D 6U8De Ue6M Ik 7Ue=D 7>UD", +"e 6I=M =Ik+M V6=I V=6+I l=I =Il> =I7V >e7V 4UG6e I =U7J >kJ= =Uf6M =U+f6M =kVf J=Vg6 =Ulf lf7>U l=J 7Vsf 6", +"NUe 6Ue!N 6UeDN 6UaeN 6Ue1N 6U8eN IkN1D< akN1I< UQ6e 6UQe! 6QVe V6eQa QIl< ", +"lIUem 7Dm=Ik >Dm7U", +"e QI=k =QI+k V=6QI =QaIk lIp= l=Ip> V=plI V>e7p I J=k7v J>=vk =Uf6Q =U+f6Q ZJ=k J=kgZ =Ilv fpU7", +"> Jpl= l=yJ h?t h&?t ?Dht ht?c ?th1 ?th: ?t1hD ?t:hc h?M h&?M V?h Vh?c lh? ", +"l?h: lhV? u?h t*h? h?t- ?tDh* ?cht- ?t1h* ?t:h- h?1t*D ?cth:- h*?M ?-hM V*h", +"? h?V- h?l* h?l- V?lh* h?u- ?tG ?&tG ?Jt tJ?c ?1tG t:?G J1?t ?t:J ?GhM ?tGM", +"& ?JV cJV? h?lG lh?:G ?Jl u?J tf? t-?f tf?J i?t f1?t ?t:f ?Jtf1 ?ti: hM?f ?", +"fMt- V?f i?V ?flh lf?h: ?Jlf ui? ht4 h&t4 hDt4 tch4 7ht 7th: 7Dht ht7c ?Mh4", +" h?M4& h4V? V?hc4 7hl l:7h 7Vh u7h t=h t-h= ht=D htc= 7ht= Aht t=7hD htAc h", +"?=M h=Mt- h=V? c=Vh? l=h Alh V=7h Auh t4?G ?tG4& h4tJ tJ4?c ?G7t 7tGh: tJ7h", +" ?J7t: ?tG4M ?G&h4M J4V? ?JVc4 lG7h 7hl:G 7V?J ?Ju7 tf= f-t= t=J it= tf7 At", +"f 7Jtf Ait =Mtf tf=-M V?J= t=iV tfl= lfAh ?Jl= Aiu hN?t ?t&hN ?tDhN ?chtN ?", +"t1hN ?t:hN ht1?DN ?cth:N ?thQ ?tQh& hQV? V?hcQ hQl? lh?:Q V?lhQ h?uQ ?t*hN ", +"t-?hN h?Dt*N t-h?cN h?1t*N t-?h:N hDt*?1N hN?ct-: ?tQh* t-Qh? V?hQ* V?-hQ l", +"h?Q* l-h?Q lhQV?* uh-?Q ?thW ?tGW& ?WtJ ?cWtJ ?tG1W h:W?t ?Jt1W ?J:tW ?WhM ", +"?tGQ& tZ?J hZ?tc h?lW lhW?: lJ?Z hZu? ?Wtf t-W?f ?JtfW ?tiW tf?1W tf:?W tf1", +"?JW i?t:W ?Qtf tfQ?- tf?Z ?ZiV lhW?f lhW?- V?flZ i?uZ ?thm ?tmh& ?tmhD ?cmh", +"t hm7t h:m7t 7htDm 7hctm ?Mhm ?Mmh& hmV? V?mhc 7htp hpl?: 7Vhp hpu? hmt= t-", +"mh= t=hDm t=chm t=7hm htAm 7hDt=m Atmhc ht=Q t=Qh- V?mh= c=Qht hpl= hpAl l=", +"hVp upAh t4hW ?tGm& ?mtJ ?Jmtc hW7t 7hWt: ?J7tW ?J:tm ?tG4Q ?G&t4Q h4tZ V?m", +"cJ lW7h lhW7: tp?J hZu7 tf?v hv?t- tv?J ?vit 7htv hvAt hv7tJ itAv =Qtf hv?-", +"M t=hZ ?viV hvl= lvAh hZl= yAui O !O OB aO O. 8O .BO 8aO OK !KO TO TaO jO j", +"O8 jOT sO (O +O (BO +aO (O. +8O (O.B 8a+O (OK +OK T(O +TO jO( +jO T(jO s+O ", +"EO !EO HO HaO EO. 8EO HO. H8O EKO !OEK HTO TaHO jOE 8EjO HjO sHO dO +dO HdO", +" gO dO. 8dO dOH. g8O dKO dK+O TdO gTO djO jO8d jOTd gsO O2 !O2 2BO aO2 5O 5", +"8O 5BO 5aO 2KO !O2K TO2 T2aO jO5 58jO 5TO s5O ;O +;O ;OB a;O 5;O >O ;O5B >O", +"a ;OK ;O+K T;O +OT; ;jO >jO jOT; s>O EO2 EO!2 HO2 aOH2 5EO 8E5O H5O HO5a EO", +"2K !EO2K TOH2 HTaO2 5EjO jO58E jOH5 H5sO d;O d;+O H;O g;O 5dO >Od HO5d g>O ", +";OdK +d;OK HOT; T;gO jO5d dj>O jOH; >Ogs P P! PB aP P. 8P P.B 8Pa QP Q!P TP", +" QaP Pj Q8P TPj sP P( +P P(B +Pa P(. +P8 .BP( +8aP QP( +QP TP( +TP Pj( +Pj ", +"T(Pj sP+ WP W!P HP HPa WP. W8P HP. H8P WQP WPQ! ZP ZaP WjP 8PWj ZPj sZP dP ", +"+WP HPd gP dP. 8dP H.dP gP8 QdP Qd+P ZdP gZP dPj Wj+P dPZj gsP mP mP! mPB a", +"mP 5P 5P8 5PB 5aP QmP mPQ! TmP amTP pP p8P pTP spP ;P +mP ;PB a;P 5P; >P 5B", +";P >Pa Q;P Q;+P T;P Tm+P p;P >Pp T;pP >Ps WmP mPW! HmP amHP 5WP W85P H5P 5a", +"HP QmWP Q!WmP ZmP amZP pWP W8pP ZpP ZpsP vP v+P vHP gvP v5P >Pv H5vP >Pg vQ", +"P +QvP ZvP ZvgP pvP pv>P ZPpv yP #O &O #BO a&O #O. 8&O #O.B a&8O #OK &OK T#", +"O T&O jO# &jO T#jO s&O #(O +&O #(OB a&+O (O#. 8&+O (BO#. +8a&O (O#K &O+K #(", +"TO +OT& #(jO +&jO jOT#( +&sO #EO &EO H#O H&O EO#. &E8O #OH. 8&HO #OEK EK&O ", +"T#HO HOT& #EjO &EjO H#jO H&sO d#O d&O d#HO g&O #Od. 8Od& Hd#O. 8&gO #OdK &O", +"dK d#TO T&gO d#jO jOd& dj#HO gOs& #O2 &O2 #O2B &2aO 5#O 5&O #B5O a&5O #O2K ", +"2K&O #OT2 &OT2 5#jO jO5& T#5O 5&sO ;#O ;&O #B;O aO;& ;#5O >&O 5;#OB a&>O #O", +";K &O;K ;#TO TO;& ;#jO &j>O ;j#TO s&>O EO#2 EO&2 #OH2 &OH2 #E5O &E5O 5#HO H", +"O5& #O2EK &EO2K HT#O2 H&TO2 jO5#E &jE5O H5T#O sH5&O ;#dO dO;& ;#HO ;&gO d#5", +"O d&>O H5d#O g&>O dK;#O d&;OK H;T#O gT;&O dj#5O >&djO H;j#O gs>&O P# P& P#B", +" aP& P#. 8P& .BP# 8aP& QP# Q&P TP# TP& Pj# P&j T#Pj sP& P#( +P& #BP( +aP& #", +"(P. +8P& P#(.B aP&+8 P#Q( Q&+P P#T( T&+P j#P( P&+j Pj#T( +Ps& WP# W&P HP# H", +"P& P#W. 8PW& P#H. 8PH& QPW# WQP& ZP# Z&P W#Pj P&Wj PjZ# Z&sP dP# dP& H#dP g", +"P& P#d. d&8P dP#H. 8Pg& Q#dP dPQ& dPZ# Z&gP d#Pj P&dj dPjZ# g&sP mP# m&P P#", +"mB m&aP 5P# 5P& P#5B aP5& mPQ# QmP& mPT# m&TP pP# p&P TPp# p&sP ;P# ;P& PB;", +"# ;&aP 5#;P >P& ;P#5B aP>& Q#;P ;PQ& T#;P ;PT& ;Pp# p&>P pT;P# >&sP mPW# Wm", +"P& mPH# m&HP WP5# W&5P H#5P 5PH& WQmP# m&PWQ mPZ# m&ZP WPp# W&pP pPZ# ZPp& ", +"vP# v&P HPv# v&gP 5Pv# v&>P vH5P# >&gP QPv# Q&vP vPZ# ZPv& vPp# pPv& ZpvP# ", +"y&P CO !CO DO DaO CO. 8CO DO. D8O CKO !OCK DTO TaDO jOC 8CjO DjO sDO C(O +C", +"O D(O +DO (OC. 8C+O (OD. +OD8 (OCK CK+O T(DO DT+O C(jO +CjO D(jO +DsO CEO C", +"E!O HDO DaHO EOC. CE8O DOH. HOD8 COEK !EOCK DTHO HDTaO CEjO jO8CE HDjO HDsO", +" dCO dC+O DdO gDO COd. dC8O dOD. D8gO CKdO +dCOK DOTd DTgO dCjO djC+O jODd ", +"gOsD CO2 CO!2 DO2 D2aO 5CO 8C5O 5DO 5OD8 CO2K !C2OK T2DO DTaO2 5CjO jO58C j", +"O5D 5DsO ;CO ;C+O D;O +OD; ;C5O >CO 5OD; >DO CK;O +;COK DOT; a;ODT ;CjO jO>", +"C jOD; sD>O EOC2 !EOC2 DOH2 HDaO2 CE5O 58CEO HO5D H5D8O EKCO2 EK2!CO HDTO2 ", +"DTaHO2 jO5CE 58CjOE H5DjO sH5DO ;CdO +d;CO HOD; D;gO dC5O dC>O 5ODd gD>O dK", +";CO dK;+CO H;DTO gDT;O djC5O >jdCO H;jDO >DOgs PC PC! DP DPa PC. 8PC DP. D8", +"P QPC PCQ! QDP DPQa PjC QC8P DPj sPD PC( +PC DP( +DP C(P. +C8P P(D. D8+P PC", +"Q( QP+C Q(DP QD+P jCP( +CPj D(Pj +DsP WPC PCW! HPD DPWa PCW. WC8P H.DP D8HP", +" QPWC Q!WPC ZDP DPZa WCPj PjCW8 DPZj ZDsP dPC +CdP DdP gPD PCd. 8CdP D.dP D", +"8gP QCdP dPC+Q DdZP ZDgP dCPj dPj+C dPDj gDsP mPC P!mC DmP amDP 5PC 5C8P 5D", +"P D85P mPQC Q!mPC TmDP amPQD pPC 8PpC pDP pDsP ;PC +C;P D;P Dm+P 5C;P >PC D", +";5P >PD QC;P ;PC+Q DPQ; a;PQD ;PpC pP>C D;pP pD>P mPWC W!mPC DmHP amPWD WP5", +"C 5W8PC 5DHP H5PD8 WQmPC WmPQ!C DmZP ZDamP WPpC pW8PC ZPpD sPDZp vPC +PvC v", +"DP vDgP 5PvC vP>C 5DvP vD>P QPvC v+QPC ZPvD ZvDgP vPpC >PpvC pPvD yDP bO b&", +"O bDO cO bO. b8O DOb. c8O bKO &ObK bTO cTO bjO jOb8 jObT scO b(O b+O D(bO c", +"+O (Ob. +8bO bD(O. +8cO (ObK +ObK T(bO +TcO b(jO jOb+ bTj(O c+sO bEO &EbO b", +"HO cHO EOb. 8EbO HOb. H8cO EKbO bK&EO HTbO HTcO bEjO &jEbO jObH cHsO bdO bO", +"d& bODd gcO dOb. bO8d bHdO. c8gO dKbO d&ObK bOTd cTgO jObd d&jbO bHjdO gOsc", +" bO2 &Ob2 DOb2 c2O b5O bO5& bO5D c5O 2KbO bK&O2 TOb2 TOc2 jOb5 &jOb5 bO5T s", +"Oc5 b;O bO;& bOD; c;O 5;bO >bO b5D;O c>O ;ObK ;&ObK bOT; T;cO jOb; bj>O b5T", +";O >Osc EOb2 b&EO2 HOb2 HOc2 5EbO b58EO bOH5 H5cO bKEO2 &EObK2 bHTO2 c2HTO ", +"b5jEO b5E&jO bH5jO c5HsO d;bO d&;bO bOH; gOc; bO5d bd>O bH5dO >Ogc bd;OK bd", +"O;&K bH;TO c;TgO b5djO >bdjO bH;jO c>gsO bP bP& bPD cP bP. b8P b.DP cP8 bQP", +" Q&bP bTP cQP bPj Q8bP DPbj scP bP( b+P b(DP cP+ P(b. +Pb8 bPD(. +Pc8 QPb( ", +"+QbP b(TP +QcP b(Pj +Pbj bTPj( s+cP bWP W&bP bHP cWP WPb. W8bP b.HP H8cP WQ", +"bP bWQP& ZbP ZcP WjbP P&jbW bPZj sPZc bdP +WbP HPbd gcP b.dP 8dbP bHPd. g8c", +"P QdbP dP&bQ bdZP gPZc dPbj dP&bj ZbdPj scgP bmP m&bP DmbP cmP b5P 5Pb8 5Db", +"P c5P QmbP m&PbQ TmbP TmcP pbP b8pP bTpP cpP b;P +mbP D;bP c;P 5Pb; >Pb b5P", +"D; c>P Q;bP ;P&bQ T;bP Q;cP b;pP pb>P pbD;P >Pcp WmbP m&PbW HmbP HmcP 5WbP ", +"b5W8P H5bP H5cP bWQmP bWQm&P bmZP ZmcP bWpP pbW8P ZPpb cPZp vbP b+vP bHvP c", +"vP b5vP vb>P vbH5P >Pcv bQvP vb+QP ZPvb cPZv pPvb >Pbpv ZpbvP ycP 0O !0O 0B", +"O a0O 1O 18O 1OB 1aO 0OK 0O!K T0O a0TO jO1 18jO 1TO s1O 0(O +0O 0(OB a0+O 1", +"(O 1+O (B1O +a1O (O0K 0O+K 0(TO T0+O 1(jO jO1+ T(1O 1+sO E0O E0!O H0O a0HO ", +"1EO 8E1O 1HO 1OH8 0OEK !EO0K T0HO HTa0O 1EjO jO18E jO1H 1HsO d0O d0+O d0HO ", +"g0O 1dO 1O8d Hd1O g1O 0OdK +d0OK d0TO T0gO jO1d djO1+ 1OTd gOs1 0O2 0O!2 0O", +"2B 0Oa2 15O 581O 5B1O 1O5a 0O2K 2K0!O 0OT2 Ta0O2 15jO jO158 1O5T 15sO ;0O ;", +"0+O 0B;O ;0aO 1;O >O1 ;O1B 1a>O 0O;K +;0OK ;0TO a;0TO jO1; jO>1 1OT; s1>O 0", +"OE2 !EO02 0OH2 Ha0O2 5E1O 158EO 1OH5 1H58O EK0O2 2K0!EO HT0O2 Ta0HO2 jO15E ", +"158jOE 1H5jO s1H5O ;0dO +d;0O ;0HO ;0gO 1O5d 1d>O 1OH; >Og1 dK;0O dK;+0O H;", +"T0O gT;0O djO1; >j1dO 1H;jO gs>1O P0 P0! P0B aP0 1P 1P8 1PB 1aP QP0 P0Q! TP", +"0 Q0aP 1QP Q81P 1TP sP1 P0( +P0 0BP( +0aP 1P( 1+P P(1B +P1a P0Q( QP+0 P0T( ", +"+0TP 1(Pj +Q1P 1(TP 1+sP WP0 P0W! HP0 H0aP 1WP W81P 1HP H81P QPW0 Q!WP0 ZP0", +" aPZ0 Wj1P 1WQ8P Z1P Z1sP dP0 +0dP H0dP gP0 1dP +W1P HP1d g1P Q0dP dP0+Q dP", +"Z0 ZPg0 Qd1P dPj1+ 1dZP sPg1 mP0 P!m0 P0mB mPa0 1mP 8m1P 1B5P 5a1P mPQ0 Q!m", +"P0 mPT0 amPQ0 p1P 1Pp8 1TpP p1sP ;P0 +0;P PB;0 a0;P 1;P >P1 1B;P 1a>P Q0;P ", +";P0+Q T0;P a;PQ0 1;pP p1>P T;1P s1>P mPW0 W!mP0 mPH0 amPH0 5W1P 1W58P H51P ", +"1Hm8P WQmP0 WmPQ!0 mPZ0 ZamP0 1WpP p1W8P p1ZP sP1Zp vP0 +Pv0 HPv0 vPg0 v1P ", +"v1>P 1HvP g1>P QPv0 v+QP0 vPZ0 ZvPg0 pPv1 >Ppv1 ZPv1 y1P 9O 9&O 9OB 9aO 91O", +" :O 1B9O :aO 9OK &O9K 9TO 9OT& 9jO :jO jO9T s:O 9(O 9+O (B9O +a9O 1(9O :+O ", +"91(OB +a:O (O9K +O9K T(9O 9O+T 9(jO +j:O 9T1(O :+sO 9EO &E9O 9HO 9OH& 1E9O ", +":EO 9O1H :HO EK9O 9&EOK HT9O 9HTaO 9EjO jO:E jO9H sO:H 9dO 9Od& Hd9O g9O 9O", +"1d :dO 9H1dO g:O dK9O d&O9K 9OTd 9TgO jO9d dj:O 9HjdO gOs: 9O2 &O92 2B9O aO", +"92 95O :5O 5B9O 5a:O 2K9O 9&O2K TO92 9TaO2 jO95 jO:5 9O5T :5sO 9;O 9O;& ;O9", +"B 9Oa; 9O1; :>O 95;OB :a>O ;O9K ;&O9K 9OT; 9+T;O jO9; :j>O 95T;O >Os: EO92 ", +"9&EO2 HO92 9HaO2 5E9O 5E:O 9OH5 H5:O 9EO2K &EO92K 9HTO2 H&O9T2 95jEO :5jEO ", +"9H5jO :H5sO d;9O d&;9O 9OH; 9;gO 9O5d >O:d 9H5dO >Og: 9d;OK 9dO;&K 9H;TO g9", +"T;O 95djO :>djO 9H;jO g:s>O 9P 9P& 9PB 9aP 9P1 :P 1B9P :Pa 9QP Q&9P 9TP Qa9", +"P 9Pj :QP 1T9P s:P 9P( 9+P P(9B +P9a 9(1P :P+ 9P1(B +P:a QP9( +Q9P 9(TP +T9", +"P 9(Pj +Q:P 9TP1( s+:P 9WP W&9P 9HP HP9a 1W9P :WP 1H9P :HP WQ9P 9WQP& Z9P 9", +"aZP Wj9P Wj:P 9PZ1 Z:P 9dP +W9P HP9d g9P 1d9P :dP 9HP1d g:P Qd9P dP&9Q 9dZP", +" Z9gP dP9j Qd:P Z91dP gPZ: 9mP m&9P mP9B am9P 95P :mP 9B5P 5a:P Qm9P m&P9Q ", +"Tm9P 9QamP p9P :pP 9TpP sP:p 9;P +m9P 9B;P a;9P 1;9P >P: 95P;B :a>P Q;9P ;P", +"&9Q T;9P a;P9Q 9;pP >P:p p9T;P s:>P Wm9P m&P9W Hm9P 9HmaP 5W9P 5W:P H59P H5", +":P 9WQmP 9WQm&P 9mZP Z9amP 9WpP pW:P ZPp9 :PZp v9P 9+vP 9HvP v9gP 95vP :vP ", +"v91HP >Pg: 9QvP v9+QP ZPv9 Zv9gP pPv9 pv:P Zp9vP y:P C0O C0!O D0O a0DO 1CO ", +"8C1O 1DO 1OD8 0OCK C0O!K T0DO DTa0O 1CjO jO18C jO1D 1DsO 0(CO C0+O 0(DO D0+", +"O C(1O +C1O D(1O 1O+D C0O(K +C0OK DT0(O +DT0O jO1C( 1+jCO 1DT(O s1+DO E0CO ", +"C0O!E D0HO HDa0O CE1O 18CEO HD1O 1HD8O C0OEK EK0!CO HDT0O DTaH0O jO1CE 18Cj", +"OE 1HDjO s1HDO C0dO +dC0O d0DO D0gO dC1O 1+dCO 1ODd 1DgO dKC0O dKC+0O DdT0O", +" gDT0O djC1O 1+CdjO 1DdjO gs1DO 0OC2 C02!O 0OD2 Da0O2 5C1O 158CO 1O5D 1D58O", +" C02OK 2K0!CO DT0O2 Ta0DO2 jO15C 158jOC 1D5jO s15DO C0;O +;C0O ;0DO a;0DO ;", +"C1O 1C>O 1OD; 1D>O ;C0OK ;C0+OK D;T0O D;0+TO ;jC1O >j1CO 1D;jO >DOs1 C02EO ", +"!EOC02 HD0O2 Da0HO2 15CEO 58C1EO 1H5DO H5D1aO EK2C0O E02K!CO DT0HO2 DTH0aO2", +" 15CjOE 5E1O8jC H5D1TO 1H5sDO d;C0O d;C+0O H;D0O gD;0O 1d5CO >C1dO 1H;DO >D", +"Og1 ;C0dKO C0;O+dK Td0D;O D;Tg0O 1dC;jO djC>O1 djO1D; gs1>DO PC0 !CP0 DP0 D", +"0aP 1PC 1C8P 1DP D81P PCQ0 Q!PC0 Q0DP aP0QD 1CPj PjC18 QD1P 1DsP C0P( PC+0 ", +"P0D( +0DP PC1( 1C+P 1(DP +D1P QPC0( +QPC0 QDP0( +QDP0 PjC1( 1+QPC 1QDP( sP1", +"+D PCW0 W!PC0 H0DP aP0WD WP1C 1W8PC HP1D 1HPD8 WQPC0 WPCQ!0 DPZ0 ZDaP0 PjC1", +"W 1W8PjC 1DZP sP1ZD PCd0 dPC+0 D0dP DPg0 1CdP 1+WPC Dd1P 1DgP dPCQ0 +QCdP0 ", +"ZDdP0 gZDP0 dPj1C dPC1+Q Z1DdP gZ1DP PCm0 mPC!0 mPD0 amPD0 1C5P 1m8PC 5D1P ", +"1Dm8P QmPC0 mPCQ!0 QDmP0 QDmaP0 1PpC p18PC 1DpP sP1pD PC;0 ;PC+0 D0;P a;PD0", +" 1C;P 1P>C D;1P 1D>P ;PCQ0 +QC;P0 QD;P0 aP0QD; p1;PC >Pp1C pD1;P >Ps1D WmPC", +"0 mPCW!0 HmDP0 HmDaP0 1W5PC 5W81PC 1HmDP H8m1DP QmPWC0 QCW0mP! ZDmP0 amPZD0", +" p1WPC 1W8pPC Zp1DP Zp1sPD PCv0 v+PC0 DPv0 gvDP0 1PvC >Pv1C 1DvP >Pg1D vQPC", +"0 +QPvC0 ZvDP0 vDPgZ0 pv1PC pv1>PC Zv1DP 1DyP b9O 9&bO 9DO c9O b1O :bO bO1D", +" :cO 9ObK b9&OK bO9T 9TcO jOb1 bj:O jO9D sO:c 9(bO bO9+ D(9O 9+cO 1(bO b+:O", +" b1D(O c+:O b9(OK b+9OK bT9(O c9+TO b1j(O :b+jO b1T(O :c+sO 9EbO b9&EO bO9H", +" 9HcO 1EbO bE:O bO1H cH:O b9EOK 9&EbKO bH9TO c9HTO b1jEO :bjEO b1HjO :cHsO ", +"bO9d d&Ob9 9ODd c9gO bO1d bd:O b1HdO gO:c bd9OK 9+ObdK bTd9O gc9TO b1djO :d", +"bjO b1TdO g:scO 9Ob2 b9&O2 DO92 9Oc2 bO95 b5:O 9O5D :Oc5 b9O2K 9&ObK2 bT9O2", +" c29TO b15jO :b5jO b1T5O :c5sO bO9; ;&Ob9 9OD; 9;cO bO1; :b>O b1;DO >O:c b;", +"9OK 9+Ob;K bT;9O c;9TO b1;jO :>bjO b1T;O c>s:O b9EO2 9&EbO2 bH9O2 c29HO b15", +"EO :b5EO b1H5O :cH5O 9EObK2 EOb29&K 9HObT2 9HTc2O 95EbjO b5j:EO bHO95T s:Hc", +"5O bd9;O bd9;&O bH;9O c;9gO b1d5O :>bdO b1H;O g:c>O 9dOb;K d&b;9OK bHO9T; g", +"9Tc;O djOb1; >bj:dO djO95D c>sg:O bP9 b9P& 9DP cP9 b1P :Pb 1DbP :cP 9QbP bQ", +"9P& 9TbP 9QcP 9Pbj bQ:P 1TbP :csP b(9P 9+bP 9(DP 9+cP b(1P b+:P b1PD( :+cP ", +"bQ9P( b+Q9P bTP9( cQ9+P b1QP( :Qb+P b1TP( :cQ+P 9WbP bW9P& 9HbP 9HcP 1WbP b", +"W:P 1HbP cW:P bW9QP 9WQbP& bPZ9 cPZ9 b1WPj :WbPj b1ZP :PZc 9dbP dP&b9 Dd9P ", +"cPg9 1dbP bd:P b1HdP :cgP bQd9P 9+QbdP Zb9dP Zcg9P dPjb1 :QdbP Zb1dP g:PZc ", +"9mbP m&Pb9 Dm9P 9mcP 95bP b5:P 5D9P c5:P bQ9mP bQ9m&P bTm9P cQ9mP b1pP pb:P", +" 9DpP :Pcp 9;bP ;P&b9 D;9P 9;cP 1;bP >b:P b1;DP :c>P bQ;9P 9+Qb;P bT;9P cQ;", +"9P pb9;P :p>bP p9D;P cp:>P bW9mP bW9m&P bHm9P cW9mP b1W5P :Wb5P b1H5P :cW5P", +" 9WQbmP QmP&bW9 Zb9mP Zc9mP pb9WP :pbWP Zpb9P cpZ:P bPv9 vb9+P 9DvP v9cP b1", +"vP vb:P vb1HP :Pcv vb9QP b+Qv9P Zvb9P cvZ9P pvb9P :vbpP Zvb1P :cyP R R! RB ", +"Ra R. R8 R.B R8a RM RM! RT RTa Rj R8M RTj sR R( R+ R(B R+a R(. R+8 .BR( +8R", +"a RM( R+M RT( R+T Rj( R+j TjR( sR+ RE RE! RH RHa RE. R8E RH. RH8 RME MER! R", +"HT HMRa RjE RE8M RHj sRH Rd R+d RHd gR Rd. R8d HdR. gR8 RdM +MRd RTd gRT Rd", +"j +jRd HjRd gsR R2 R2! R2B Ra2 R5 R58 R5B R5a RM2 M2R! RT2 R2aM R5M 5MR8 R5", +"T sR5 R; R+; R;B Ra; R5; >R 5;RB >Ra R;M +MR; RT; +TR; R;j >MR 5TR; s>R RE2", +" !ER2 RH2 HaR2 R5E 58RE RH5 H5R8 MER2 RME!2 R2HM aM2RH RE5M R58ME H5RT RHs5", +" Rd; +dR; RH; gR; R5d >Rd H5Rd g>R dMR; dM;R+ HMR; RTg; 5dRj Rd>M H;Rj gR>M", +" RP RP! RPB RaP RP. R8P P.RB 8PRa S S! ST Sa Sj S8 STj Ss RP( R+P P(RB +PRa", +" P(R. +PR8 RP(.B R+8aP S( S+ ST( S+T Sj( S+8 TjS( Ss+ RW RW! RHW RWa RW. RW", +"8 R.HP H8RW SW S!W SZ SZa SWj S8W SZj SsZ RWd R+W HPRd gRW R.dP +WR8 dP.RH ", +"RWg8 Sd S+W SZd gS Sdj S8d ZdSj gSs Rm Rm! RmB Ram R5m R8m RB5P 5aRm Sm S!m", +" STm Sam Sp Sp8 SpT Ssp Rm; R+m RB;P +mRa 5PR; >PR ;PBR5 Ra>P S; S+m S;T Sa", +"; Sp; S> pTS; S>s RWm W!Rm RHm HmRa R5W 5WR8 H5RW H8Rm SWm W!Sm SZm ZaSm Sp", +"W pWS8 SZp SZsp vR vR+ vRH gvR vR5 v>R RHv5 gR>P Sv Sv+ SvZ gSv Svp S>v ZpS", +"v yS R# R& R#B Ra& R#. R8& .BR# 8aR& RM# R&M RT# RT& Rj# R&j TjR# sR& R#( R", +"+& #BR( +aR& #(R. +8R& R#(.B R+8a& M#R( +MR& T#R( +TR& j#R( +jR& RTj#( R+s&", +" R#E R&E RH# RH& #ER. 8&RE H#R. H8R& M#RE REM& R#HM HMR& j#RE RE&j R#Hj RHs", +"& Rd# Rd& HdR# gR& d#R. 8dR& RHd#. R8g& R#dM dMR& R#Td RTg& R#dj d&Rj RHjd#", +" g&sR R#2 R&2 2BR# a&R2 R5# R5& 5BR# 5aR& M#R2 R2M& T#R2 R2T& R#5M 5MR& R#5", +"T R5s& R;# R;& ;#RB a;R& 5;R# >R& R5;#B Ra>& R#M; M;R& R#T; T;R& R#;j R&>M ", +"R5T;# >&sR #ER2 &ER2 H#R2 R2H& 5#RE RE5& R#H5 H5R& RM#E2 M&ER2 RHT#2 RH&T2 ", +"R5M#E R5&ME RH5T# sRH5& d;R# d&R; R#H; R;g& R#5d Rd>& RH5d# >&gR dM;R# dM&R", +"; RH;T# gRT;& R5dM# >MRd& RH;j# >MgR& RP# R&P P#RB aPR& P#R. 8PR& RP#.B aP&", +"R8 S# S& ST# Sa& Sj# S8& TjS# Ss& P#R( +PR& RP#(B aP&R+ RP#(. R+8P& P#(R.B ", +"R+8aP& S#( S+& T#S( +TS& j#S( +jS& STj#( s+S& RW# RW& R#HP HPR& W#R. W8R& R", +"HW#. RH8W& SW# S&W SZ# SZ& WjS# W8S& ZjS# S&sZ R#dP +WR& dP#RH RWg& dP#R. d", +"P&R8 RH#dP. gRW8& Sd# Sd& ZdS# gS& djS# 8dS& SZdj# S&gs Rm# Rm& m#RB amR& R", +"#5P 5PR& R5m#B R5am& Sm# S&m TmS# TmS& Sp# Sp& pTS# S&sp R#;P +mR& ;P#RB a;", +"PR& ;P#R5 Rm>& R5#;PB >PRa& S;# S;& T;S# T;S& p;S# S>& SpT;# S&s> WmR# W&Rm", +" R#Hm HmR& R#5W 5WR& RH5W# RH5W& WmS# W&Sm ZmS# SmZ& pWS# SWp& S#Zp ZpS& vR", +"# vR& RHv# v&gR R5v# >Rv& vRH5# >PgR& Sv# Sv& S#Zv S&gv S#pv pvS& SZpv# yS&", +" RU RU! RD RDa RU. R8U RD. RD8 RUM U!RM VR VRa RjU U8Rj VRj sRV RU( R+U RD(", +" R+D U(R. U+R8 D(R. +DR8 UMR( RU+M VR( V+R R(Uj U+Rj RjV( V+sR RUE U!RE RHU", +" UHRa UER. REU8 R.UH UHR8 UMRE RUM!E VRH RHVa REUj R8UME RHVj VRsH RdU U+Rd", +" RDd gRU R.Ud U8Rd R.Dd RDg8 RUdM R+UdM VdR gVR UdRj R+jUd RdVj sRgV RU2 U!", +"R2 RD2 R2Ua R5U U5R8 R5D U5Ra UMR2 RUM!2 V2R RaV2 U5Rj R5U8M V5R V5sR R;U U", +"+R; RD; UaR; U5R; >RU 5DR; >RD RUM; R+UM; V;R R+V; U;Rj Rj>U R5V; V>R UER2 ", +"RUE!2 R2UH RHUa2 REU5 R5U8E UHR5 RH5U8 RUME2 U!MRE2 RHV2 V2RHa R5UME U58RjE", +" RHV5 VsHR5 UdR; R+Ud; UHR; RDg; U5Rd Rd>U 5DRd >RgU dM;RU R+UdM; RHV; V;gR", +" R5dUj >MRUd R5Vd gRV> UP UP! RDP UaP UP. U8P R.DP D8UP SU S!U SV SVa SUj S", +"8U SVj SsV UP( U+P R(DP +DUP P(U. +PU8 RDP(. R+D8P SU( S+U SV( SV+ UjS( U+S", +"8 VjS( VsS+ RWU R!UW RWD UWRa R.UW UWR8 R.WD H8UP SUW UWS! SZV VZSa UWSj UW", +"S8 SjVZ SVsZ UdP U+RW HPUd gUP U.dP 8dUP RWDd. U8gP SdU U+Sd SVd gSV UdSj U", +"8Sd SjVd SsgV RmU R!Um RDm UaRm U5P U8Rm 5DRm 5aUP SUm UmS! SVm VmSa SpU pU", +"S8 VpS VpSs U;P U+Rm DmR; +DRm 5PU; >PU R5D;P RD>P S;U U+S; SV; V+S; pUS; S", +">U S;Vp S>V UWRm RWUm! UHRm RHmUa U5RW R5WU8 H5UP RH8Um UWSm S!UWm VmSZ SZV", +"am SWpU SpUW8 SZVp SZpVs vRU R+vU vRD vRgU R5vU vU>P R5vD gU>P SvU vUS+ VvS", +" VvgS pUSv vUS> VpSv yVS Rb Rb& RbD cR Rb. Rb8 bDR. cR8 RbM bMR& VRb cRV Rb", +"j b8Rj RbVj scR Rb( Rb+ bDR( cR+ b(R. b+R8 RbD(. R+c8 R(bM bMR+ RbV( V+cR R", +"(bj b+Rj VRbj( s+cR RbE REU& RbH cRH bER. REb8 R.bH RHc8 REbM M&ERb RbVH VR", +"cH REbj Rb8ME bHRj sHcR Rbd b+Rd bHRd gcR R.bd b8Rd RbHd. g8cR bMRd dM&Rb R", +"bVd cRgV bdRj d&jRb VdRbj scgR Rb2 R2U& bDR2 cR2 Rb5 b5R8 b5RD cR5 R2bM M&2", +"Rb RbV2 V2cR b5Rj Rb58M RbV5 c5sR Rb; b+R; b;RD cR; b5R; >Rb Rb5D; c>R bMR;", +" M;&Rb RbV; V;cR b;Rj Rb>M V5Rb; sRc> bER2 Rb&E2 R2bH RHc2 REb5 Rb58E bHR5 ", +"RHc5 RbME2 RbEM&2 V2RbH cRVH2 Rb5ME M&ERb5 V5RbH cR5sH bdR; Rb+d; bHR; c;gR", +" b5Rd Rb>d RbH5d gRc> RbdM; RbdM;& VdRb; gVcR; Rb5dM >MbRd V5dRb V>gcR RbP ", +"UP& bPRD cRP R.bP bPR8 RbDP. R8cP Sb Sb& SVb Sc Sbj Sb8 VbSj Scs R(bP bPR+ ", +"RbDP( R+cP RbP(. Rb+8P bP(RD. cR+8P Sb( Sb+ VbS( Sc+ bjS( b+S8 SVbj( S+sc R", +"bW bWR& bHRW cRW R.bW bWR8 RbHW. RWc8 SbW bWS& SZb ScZ bWSj bWS8 SjZb ZcSs ", +"bWRd b+RW RbHdP cRgP RbWd. dP&U8 dP.RbH cRWg8 Sbd b+Sd ZbSd gSc bdSj b8Sd S", +"Zbdj gsSc Rbm bmR& bmRD cRm b5Rm b8Rm Rb5Dm R5cm Sbm bmS& VmSb Scm Spb pbS8", +" SbVp Scp bmR; b+Rm RbmD; R+cm Rb5;P Rb>P b5PRD; cR>P Sb; b+S; V;Sb Sc; pbS", +"; S>b SpbV; Sc> bWRm RbWm& bHRm RHcm b5RW Rb5W8 RbH5W R5cW bWSm SbWm& SmZb ", +"cmSZ SWpb SpbW8 ZpSb SZcp vRb Rbv& RbvD cvR Rbv5 >Rvb vRbH5 v>cR Svb vbS+ S", +"bVv Scv pvSb v>Sb VvpSb ycS Rk Rk! RkB Rak R1 R18 R1B R1a RkM kMR! RTk TkRa", +" lR lR8 lRT lsR Rk( R+k k(RB +kRa R1( R1+ 1(RB 1+Ra kMR( Rk+M R(Tk +kRT lR(", +" l+R RTl( sRl+ RkE kER! RHk HkRa R1E RE8k R1H 1HR8 kMRE RkM!E HkRT RHTak lR", +"E R8lE lHR sRlH Rdk +kRd HkRd gRk R1d 1+Rd 1HRd gR1 RkdM dkMR+ TkRd RTgk ld", +"R R+ld RHld glR Rk2 k2R! k2RB R2ak R15 5kR8 RB5k 1aR5 kMR2 RkM!2 R2Tk ak2RT", +" lR5 R5l8 R5lT l5sR R;k +kR; RBk; akR; R1; >kR RB1; R1>a RkM; k;MR+ TkR; ak", +";RT l;R l>R RTl; sRl> kER2 RkE!2 R2Hk ak2RH RE5k R158E 1HR5 R1H5a RkME2 kME", +"R2! RHTk2 RHTak2 R5lE lR58E RHl5 lsRH5 dkR; dk;R+ HkR; R;gk 1dR5 R1>d 1HR; ", +"gR>k dkMR; R+dk;M RH;Tk gRTk; R5ld >Rld RHl; gRl> kP kP! kPB akP R1P 8kP RB", +"1P 1PRa Sk S!k STk Sak Sl Sl8 SlT Ssl kP( +kP P(kB ak+P R(1P 1PR+ kP(1B akP", +"1+ Sk( S+k TkS( +TSk Sl( l+S STl( l+Ss RWk R!Wk HkP WkRa R1W 1WR8 1HRW 1WRa", +" SWk WkS! SZk ZkSa lWS S8lW lZS lZSs dkP +kRW dkHP gkP 1WRd 1+RW dkP1H R1gP", +" Sdk +WSk ZkSd gSk ldS S+lW SZld glS Rmk R!km RBkm akRm R1m 1mR8 RB1m 1aRm ", +"Skm kmS! TkSm akSm Slp lpS8 lTSp Slsp k;P +kRm kB;P k;aP 1mR; >kP k;P1B ak>", +"P S;k +kS; TkS; akS; l;S S>l lTS; Ssl> WkRm km!RW HkRm akmRH 1WR5 R1W8m 1HR", +"m akPH5 WkSm S!Wkm SmZk SZakm SplW SlpW8 SplZ SZpls vRk R+vk RHvk vRgk vR1 ", +"vR>k R1vH gk>P Svk vkS+ ZkSv gkSv lvS S>lv lZSv ylS R9 R9& R9B R9a R91 :R 9", +"1RB :Ra R9M 9MR& R9T 9TRa lR9 l:R R9lT s:R R9( R9+ 9(RB 9+Ra 91R( :R+ R91(B", +" R+:a R(9M 9MR+ R(9T 9+RT R9l( :Rl+ lR9T( s+:R R9E REk& R9H 9HRa 91RE :RE 9", +"HR1 :RH RE9M k&ERM 9HRT R9HT& R9lE lE:R R9lH :RlH R9d 9+Rd 9HRd gR9 9dR1 :R", +"d R9H1d g:R 9MRd dk&RM 9TRd R9gM R9ld :Rld lHR9d gRl: R92 R2k& 92RB R29a R9", +"5 :R5 RB95 R5:a R29M k&2RM R29T R9T&2 R9l5 l5:R 95RT s5:R R9; 9+R; RB9; 9aR", +"; 95R; :>R R95;B :a>R 9MR; k;&RM 9TR; aM;R9 R9l; :Rl> l;R9T :>sR 9ER2 k&ER2", +" R29H R9H&2 RE95 R5:E 9HR5 RH:5 R9ME2 R9EM&2 R9HT2 aM2R9H lR95E :R5lE lHR95", +" :RHl5 9dR; dk&R; 9HR; R9g; 95Rd >R:d R9H1; :>gR R9dM; R9dM;& R9HT; gR9T; l", +"dR95 l>:Rd lH;R9 g:Rl> R9P kP& RB9P 9PRa 9PR1 :RP kPB91 Ra:P S9 S9& S9T S9a", +" Sl9 S: lTS9 S:s R(9P 9PR+ kP(9B akP9+ kP(91 R+:P 9P(R1B :R+aP S9( S9+ 9TS(", +" 9+Sa l(S9 S:+ Sl9T( S+s: R9W 9WR& 9HRW 9WRa 9WR1 :RW R9H1W RH:W S9W 9WS& S", +"Z9 Z9Sa S9lW S:W S9lZ S:Z 9WRd 9+RW dkP9H R9gP dkP91 R+:W 9HPR1d :RgP S9d 9", +"+Sd Z9Sd gS9 S9ld S:d SZ9ld gS: R9m 9mR& RB9m 9aRm 95Rm :Rm R95mB Ra:m S9m ", +"9mS& 9TSm 9aSm Sp9 S:p STp9 :pSs 9mR; 9+Rm k;P9B ak;9P k;P95 :R>P k;B95P :>", +"Ram S9; 9+S; 9TS; 9aS; S9l; S:> Sp9T; s:S> 9WRm km&RW 9HRm km&RH 95RW R5:W ", +"R9H1m RH:m 9WSm S9Wm& SmZ9 SZ9am SWp9 :WSp ZpS9 SZ:p vR9 R9v& R9vH vRg9 R9v", +"1 :vR vR91H gR:v Sv9 v9S+ ZvS9 g9Sv S9lv S:v Sv9lZ y:S RUk U!Rk RDk DkRa R1", +"U 1UR8 R1D 1URa UkRM RUkM! VRk RaVk lRU R8lU VlR VlsR UkR( RU+k R(Dk +kRD R", +"(1U 1UR+ R(1D 1+RD RUkM( R+UkM RkV( R+Vk RUl( R+lU VRl( lRV+ UkRE RUk!E HkR", +"D RHUak RE1U R1U8E 1URH R1HU8 RUkME U!kRME RHVk VRHak RUlE lRU8E VRlH lsVRH", +" RUdk R+Udk DkRd RDgk 1URd R1+Ud 1DRd R1gU dkMRU R+UdkM RdVk VRgk RdlU l+RU", +"d lRVd VlgR UkR2 RUk!2 R2Dk ak2RD 1UR5 R1U58 1DR5 R1D5a RUkM2 U!kRM2 RkV2 V", +"2Rak R5lU lRU58 lRV5 lsVR5 RUk; R+Uk; DkR; ak;RD 1UR; R1>U 1DR; R1>D k;MRU ", +"R+Uk;M R;Vk V+Rk; R;lU lR>U lRV; >RVl RUkE2 U!kRE2 RHUk2 RHUak2 R1U5E 1U5R8", +"E R1HU5 1UHR5a UkMRE2 !ER2UkM V2RHk ak2VRH lRU5E R5Ul8E VlRH5 Vs5lHR dk;RU ", +"R+Udk; RH;Dk gRUk; R1dU5 >kRUd R1HU; >kgRD RdUk;M dkR;U+M VdRk; gVRk; ldRU5", +" l>RUd lH;VR V>lgR UPk U!kP DkP akUP 1UP U81P 1PRD Ua1P SUk U!Sk SVk VaSk S", +"lU lUS8 SlV SVls U(kP +kUP D(kP Dk+P 1(UP U+1P R1DP( R1+DP UkS( U+Sk VkS( S", +"kV+ SUl( lUS+ l(SV SVl+ WkUP kP!UW HkUP akPUH 1URW R1WU8 1WRD R1WUa UWSk S!", +"UWk ZkSV SZVak SUlW SlUW8 SVlZ SlVsZ dkUP dkPU+ dkDP UPgk Ud1P dkPU8 dkP1D ", +"1UgP UdSk S+UWk SkVd SVgk lUSd S8dlU SVld SlgV kmUP km!UP DkRm akmRD 1URm R", +"1mU8 1DRm akPU5 UmSk S!Ukm SkVm SVakm pUSl SlpU8 SlVp SlVsp k;UP k;PU+ k;DP", +" ak;UP U;1P 1U>P k;P1D Dk>P U;Sk S+Ukm SkV; SV+km lUS; >USl SVl; SlV> RWUkm", +" RWUkm! RHmDk RWDakm R1WU5 1UWR8m R1HUm 1UHR8m SUWkm km!SUW SZVkm ZkVSam Sl", +"pUW lWpS8U SlVZp VplSsZ UPvk vRU+k RDvk gvRDk R1vU >kvUP R1vD >kgUP SUvk Sv", +"U+k vkSV VvgSk vUSl lv>SU SlVv SlyV tR tR& tRD tcR tR1 t:R R1tD :cR tRM R&t", +"M VtR VtcR ltR :Rlt lRVt uR tR( t+R RDt( cRt+ R1t( :Rt+ tR1D( :+cR RMt( R+t", +"M VRt( tRV+ tRl( lRt+ ltVR( uR+ tRE R&tE tHR cRtH R1tE tE:R R1tH :RtH RMtE ", +"tRM&E VRtH tcVRH tRlE t:RlE lRtH uRH tdR R+td RHtd gtR R1td :Rtd tHR1d t:gR", +" RdtM t+RdM tRVd VtgR lRtd t:dlR tdVlR ugR tR2 R&t2 RDt2 c2tR t5R :Rt5 R5tD", +" cRt5 RMt2 tRM&2 tRV2 tcVR2 lRt5 t:Rl5 tRV5 uR5 t;R R+t; RDt; cRt; R1t; t>R", +" t5RD; :Rc> R;tM t+RM; tRV; tc;VR lRt; >Rlt t5VR; u>R REt2 tR&E2 RHt2 tcRH2", +" R5tE t:R5E RHt5 t:HR5 tRME2 M&EtR2 tHVR2 cRVtH2 t5RlE lt5:RE tH5lR RHu5 Rd", +"t; t+Rd; RHt; t;gR R5td >Rtd tH5Rd gRt> tdRM; dM&t;R tH;VR gtVR; t5dlR t>lR", +"d tH;lR g>uR tP tP& tPD tcP tP1 t:P 1DtP :ctP St St& StV Sct Stl S:t SVlt u", +"S tP( t+P DPt( cPt+ 1Pt( :Pt+ tP1D( t:Pc+ St( St+ S(Vt tcS+ l(St l+St SlVt(", +" uS+ tWR RWt& tHP cRtW R1tW :RtW 1HtP :HtP StW tWS& tZS tZSc lWSt tWS: lZSt", +" uZS tdP R+tW HPtd gtP 1dtP :dtP tHP1d t:gP Std t+Sd SdtZ gtS ldSt t:Sd Std", +"lZ ugS tmR Rmt& RDtm cRtm t5P :Rtm 5DtP cR:m Stm tmS& tmSV tmSc tpS tpS: Vp", +"St upS t;P R+tm D;tP c;tP 1;tP t>P t5PD; tc>P St; t+S; t;SV S;tc l;St S>t S", +"t;Vp u>S RWtm tWRm& RHtm tcWRm R5tW t:WR5 H5tP t:HRm SWtm StWm& tmSZ ScZtm ", +"tWSp S:ptW SZtp SZup tvR vRt+ vRtH gRtv vRt5 >Ptv tvR1H >Pgt tvS S+tv StVv ", +"tvgS Stlv tvS: lvVSt yuS )O !)O )BO a)O )O. 8)O )O.B a)8O )OK )O!K T)O a)TO", +" jO) 8)jO T)jO s)O *O +*O *BO a*O *O. 8*O .B*O 8Oa* *OK *O+K T*O +OT* *jO j", +"O8* jOT* s*O E)O E)!O H)O a)HO )OE. E)8O )OH. 8)HO )OEK !E)OK T)HO HTa)O E)", +"jO jO8E) H)jO H)sO d*O d*+O H*O g*O *Od. d*8O *OH. 8*gO *OdK +d*OK HOT* T*g", +"O d*jO dj*+O jOH* gOs* )O2 )O!2 )O2B )Oa2 5)O 8)5O )B5O a)5O )O2K !)2OK )OT", +"2 Ta)O2 5)jO jO58) T)5O 5)sO ;*O ;*+O ;O*B ;*aO 5*O >*O 5O*B a*>O *O;K +;*O", +"K ;*TO a;*TO jO5* *j>O 5OT* >Os* )OE2 !)2EO )OH2 Ha)O2 E)5O 58E)O 5)HO H58)", +"O EK)O2 !)OEK2 HT)O2 Ta)HO2 jO5E) 58EjO) H5T)O sH5)O ;*dO +d;*O ;*HO ;*gO d", +"*5O d*>O HO5* g*>O dK;*O dK;+*O H;T*O gT;*O dj*5O >*djO H5*jO s*Og> P) P)! ", +"P)B aP) P). 8P) .BP) 8)aP QP) P)Q! TP) Q)aP Pj) Q)8P T)Pj sP) P* +P* P*B aP", +"* P*. 8P* P.*B aP8* Q*P Q*+P TP* T*+P P*j 8PQ* PjT* sP* WP) P)W! HP) H)aP P", +")W. W)8P P)H. H)8P QPW) Q!WP) ZP) aPZ) W)Pj Pj)W8 PjZ) ZPs) W*P W*+P HP* gP", +"* W.P* 8PW* H.P* 8Pg* dPQ* +WQP* Z*P Z*gP P*Wj dPj8* P*Zj s*gP mP) P!m) P)m", +"B mPa) 5P) 5)8P P)5B 5)aP mPQ) Q!mP) mPT) amPQ) pP) 8Pp) TPp) pPs) m*P m*+P", +" *B;P m*aP 5P* >P* *B5P aP>* ;PQ* m*P+Q m*TP am*TP p*P p*>P TPp* s*>P mPW) ", +"W!mP) mPH) amPH) WP5) 5W8P) H)5P H5P8) WQmP) WmPQ!) mPZ) ZamP) WPp) pW8P) p", +"PZ) sP)Zp vP* +Pv* HPv* vPg* 5Pv* vP>* 5PH* >*gP Q*vP v+QP* vPZ* ZvPg* vPp*", +" >Ppv* ZPp* y*P ,O ,&O ,OB ,aO ,O. ,8O .B,O 8a,O ,OK &O,K ,TO ,OT& ,jO ,O&j", +" jO,T s,O ,*O -O ,O*B -aO *O,. -8O *B,O. 8a-O *O,K -OK ,OT* -TO ,O*j -jO *j", +"O,T s-O ,EO &E,O ,HO ,OH& EO,. 8E,O HO,. ,OH8 EK,O ,&EOK HT,O ,HTaO jO,E &j", +"E,O ,OHj ,HsO ,dO -dO ,OH* g-O dO,. 8d-O ,HdO. -8gO dK,O dK-O ,OTd gO-T ,Od", +"j dj-O ,HjdO gOs- ,O2 &O,2 2B,O aO,2 ,5O ,O5& 5B,O ,O5a 2K,O ,&O2K TO,2 ,Ta", +"O2 jO,5 &jO,5 ,O5T ,5sO ,;O -;O ;O,B a;-O ,O5* ->O ,5;OB -a>O ;O,K ;O-K ,OT", +"; T;-O ,O;j >O-j ,5T;O >Os- EO,2 ,&EO2 HO,2 ,HaO2 5E,O ,58EO ,OH5 ,H58O ,EO", +"2K &EO,2K ,HTO2 H&O,T2 ,5jEO ,5E&jO ,H5jO s,H5O d;,O d;-O ,OH; -;gO ,O5d -d", +">O ,H5dO >Og- ,d;OK -d;OK ,H;TO -T;gO ,5djO ->djO ,H;jO s-g>O ,P ,P& ,PB ,a", +"P ,P. ,8P P.,B 8P,a ,QP Q&,P ,TP Qa,P ,Pj Q8,P TP,j sP, ,P* -P *B,P -Pa ,.P", +"* -P8 P*.,B 8P-a Q*,P -QP T*,P -TP P*,j -Pj P*j,T s-P ,WP W&,P ,HP HP,a WP,", +". W8,P ,.HP H8,P WQ,P ,WQP& Z,P ,aZP Wj,P P&j,W ,PZj Z,sP ,dP -WP HP,d g-P ", +",.dP W8-P ,HPd. g8-P Qd,P Qd-P ,dZP Z-P dP,j Wj-P Z*P,j gPs- ,mP m&,P mP,B ", +"am,P ,5P 5P,8 ,B5P 5a,P Qm,P m&P,Q Tm,P ,QamP p,P ,8pP ,TpP p,sP ,;P -mP ,B", +";P am-P 5P,; >P- ,5P*B -a>P Q;,P Q;-P T;,P Tm-P ,;pP -pP p*T,P >Ps- Wm,P m&", +"P,W Hm,P ,HmaP 5W,P ,5W8P H5,P ,H58P ,WQmP ,WQm&P ,mZP Z&m,P ,WpP p&W,P ZPp", +", sP,Zp v,P -vP ,HvP gP-v ,5vP >P-v v,H5P g->P ,QvP vQ-P ZPv, -PZv pPv, pv-", +"P Zp*,P y-P C)O C)!O D)O a)DO )OC. C)8O )OD. 8)DO )OCK !CO)K T)DO DTa)O C)j", +"O jO8C) D)jO D)sO *CO *C+O D*O +OD* CO*. *C8O *OD. DO8* CK*O +*COK DOT* a*O", +"DT *CjO *jC+O jOD* D*sO E)CO CE)!O D)HO HDa)O C).EO 8CE)O HD)O. H8D)O CE)OK", +" EK)!CO HDT)O DTaH)O jOCE) 8CEjO) HjD)O sHD)O *CdO +d*CO HOD* D*gO d*CO. 8d", +"*CO H*DO. gD8*O dK*CO dK*+CO H*DTO gDT*O dj*CO 8dC*jO H*jDO s*DgO )OC2 !)2C", +"O )OD2 Da)O2 C)5O 58C)O D)5O 5D8)O 2KC)O CKO!)2 DT)O2 Ta)DO2 jO5C) 58CjO) 5", +"DT)O s5D)O *C;O +;*CO ;*DO a;*DO *C5O *C>O 5OD* D*>O ;*COK ;*C+OK D;T*O D;T", +"a*O ;jC*O >*jCO 5D*jO >D*sO CE)O2 !O2CE) HD)O2 Da)HO2 5CE)O 8CE5)O H5D)O 5D", +")H8O C)OEK2 C)!OEK2 DT)HO2 DTH)aO2 5CEjO) 5)jO8CE 5D)HjO H5Ds)O d;*CO d;*+C", +"O H;D*O gD;*O 5d*CO >*dCO H5*DO >D*gO ;*CdKO dK+*;CO D;TH*O D;Tg*O 5dC*jO d", +"j*>CO djO5D* gs*>DO PC) !CP) DP) D)aP C)P. PC8) P)D. D)8P PCQ) Q!PC) Q)DP a", +"P)QD jCP) PjC8) D)Pj DPs) P*C +CP* DP* D*+P *CP. 8CP* D.P* 8PD* QCP* P*C+Q ", +"DPQ* aP*QD PC*j P*j+C P*Dj DPs* PCW) W!PC) H)DP aP)WD WPC). W8PC) HPD). H8P", +"D) WQPC) WPCQ!) DPZ) ZDaP) PjCW) W8CPj) ZDPj) sPDZ) WCP* dPC+* DPH* DPg* dP", +"C*. W8*PC HP*D. gPD8* dPCQ* +WQP*C DPZ* Z*DgP dPj*C dPCQ8* Z*DPj sP*gD PCm)", +" mPC!) mPD) amPD) PC5) 5P8C) 5)DP 5DP8) QmPC) mPCQ!) QDmP) QDmaP) PCp) p8PC", +") DPp) sPDp) mCP* m*P+C m*DP am*DP 5CP* P*>C DP5* DP>* m*PQC +QmP*C QD;P* Q", +"D;aP* P*pC >Pp*C DPp* sP*pD WmPC) mPCW!) HmDP) HmDaP) 5WPC) W8C5P) H5PD) aP", +")5WD QmPWC) W)Q!mPC ZDmP) amPZD) pWPC) W8PpC) ZpDP) ZpDsP) P*vC v+P*C DPv* ", +"gvDP* v5P*C >Pv*C vD5P* >PgD* vQP*C P*Cv+Q ZvDP* gvDZ*P pvP*C pv*>PC Zp*DP ", +"DPy* ,bO b&,O ,DO c,O bO,. ,Ob8 DO,. ,8cO bK,O ,b&OK ,ObT ,TcO ,Obj &jO,b ,", +"ODj c,sO b*O -bO ,OD* c-O *Ob. b8-O ,D*O. -8cO *ObK bK-O bOT* cO-T jOb* bj-", +"O ,Dj*O sOc- bE,O ,b&EO ,ObH ,HcO ,bEO. ,8bEO ,HbO. cH8,O ,bEOK bK&,EO ,HbT", +"O cHT,O ,jbEO b8E,jO ,HjbO s,HcO ,Obd bd-O ,ODd gOc- ,dbO. -b8dO ,DdO. c-8g", +"O ,dbOK -bdOK ,DdTO c-TgO ,djbO -jbdO ,DdjO s-gcO bO,2 ,b&O2 DO,2 ,Oc2 ,Ob5", +" ,5b8O ,O5D ,5cO ,bO2K bK&,O2 ,DTO2 c2T,O ,5bjO ,5b&jO ,5DjO s,5cO ,Ob; b;-", +"O ,OD; -;cO bO5* -b>O ,5D;O >Oc- ,;bOK -b;OK ,D;TO c-T;O ,;jbO ->bjO ,D;jO ", +"s-c>O ,bEO2 b&E,O2 ,HbO2 c2H,O ,5bEO b5E,8O ,H5bO c5H,O bKE,O2 EO,2bK& bHO,", +"T2 ,HTc2O b5E,jO 5jbE,8O ,HOb5T c5Hs,O ,db;O -bd;O ,H;bO c-;gO ,5dbO ->bdO ", +",5DdO c>g-O bdO,;K bd;-OK ,HObT; -T;gcO djOb5* >bd-jO djO,5D c>gs-O ,Pb ,bP", +"& ,DP cP, ,.bP b8,P ,.DP ,8cP bQ,P ,QbP& bT,P ,QcP bP,j P&j,b DP,j cPs, bP*", +" -Pb DPb* c-P b.P* b8-P ,DP*. c8-P Q*bP bQ-P TPb* -QcP P*bj bP-j P*j,D c-sP", +" bW,P ,WbP& bH,P ,HcP ,WbP. ,W8bP ,HPb. cW8,P ,WbQP bWQ,P& ,DZP cPZ, ,WjbP ", +"bW8,Pj ZbP,j sP,Zc bd,P bW-P HPb* c-gP ,dPb. -Wb8P ,DdP. c-W8P ,QdbP -WbQP ", +"bPZ* cPZ- dPjb* -WjbP Zb*Pj s-PZc bm,P m&P,b Dm,P ,mcP b5,P ,5Pb8 5D,P ,5cP", +" ,QbmP ,Qbm&P ,QDmP cQm,P ,Ppb pb8,P ,DpP p,cP b;,P bm-P D;,P -mcP 5Pb* >b-", +"P ,5D;P c->P ,Q;bP -Qb;P ,QD;P c-Q;P bPp* pb-P pb*DP cP-p ,WbmP ,Wbm&P ,Hmb", +"P cWm,P ,5WbP b5W,8P ,H5bP c5W,P bWQ,mP QmP&,Wb Zbm,P ZcP,m pbW,P ,W8pbP Zp", +"b,P cpZ,P ,Pvb vb-P ,DvP cP-v vb5,P -v>bP vD,5P cv->P vbQ,P -vbQP Zvb,P cvZ", +"-P pvb,P -pvbP ZpbP* c-yP 0)O 0)!O 0)OB 0)aO 1)O 8)1O )B1O a)1O )O0K !K0)O ", +"0)TO Ta0)O 1)jO jO18) T)1O 1)sO *0O *0+O 0B*O *0aO 1*O 1O8* 1O*B 1Oa* 0O*K ", +"+*0OK *0TO a*0TO jO1* *jO1+ 1OT* 1*sO 0)EO !E)0O 0)HO Ha0)O E)1O 18E)O H)1O", +" 1H8)O EK0)O !0OEK) HT0)O Ta0H)O jO1E) 18EjO) 1HT)O s1H)O *0dO +d*0O *0HO *", +"0gO d*1O 1+d*O 1OH* 1*gO dK*0O dK*+0O H*T0O gT*0O dj*1O 1+d*jO 1H*jO s*1gO ", +")O02 0)2!O 0)2OB a0)O2 5)1O 158)O 15)OB 1a5)O 0)2OK !)O2K0 T0)O2 a0)TO2 jO1", +"5) 158jO) 1T5)O s15)O *0;O +;*0O *B;0O a;*0O 1O5* 1*>O 1;*OB >*1aO ;*0OK ;*", +"0+OK T;*0O T;0a*O ;jO1* >*1jO 1T;*O s*1>O 0)2EO !EO0)2 H0)O2 a0)HO2 15E)O 5", +"8E1)O 1H5)O H5)1aO E)O2K0 0)EO2K! T0)HO2 0)HOTa2 15EjO) 5)jO18E H5)1TO 1H5s", +")O d;*0O d;*+0O H;*0O g;*0O 1d5*O >*1dO 1H;*O g>1*O ;*0dKO dK+*;0O T;0H*O T", +";*g0O 1d5*jO dj*>O1 ;jO1H* g>1s*O P0) !0P) )BP0 P0a) 1P) 1)8P P)1B 1)aP P0Q", +") Q!P0) P0T) aP0Q) 1)Pj Pj)18 1)TP 1Ps) P*0 +0P* P0*B a0P* 1P* +P1* *B1P aP", +"1* Q0P* P*0+Q T0P* aP*Q0 Q*1P P*j1+ TP1* 1Ps* P0W) W!P0) P0H) aP0H) WP1) 1W", +"8P) 1)HP 1HP8) WQP0) WP0Q!) P0Z) ZaP0) Pj)1W 1W8Pj) 1PZ) sP1Z) W0P* dP0+* H", +"0P* P*g0 W*1P 1+WP* HP1* 1Pg* dP0Q* +WQP*0 P*Z0 Z*Pg0 dPj1* 1+WP*j 1PZ* sP*", +"g1 P0m) mP0!) mP0)B amP0) 1)5P 1m8P) 1mP)B 1amP) QmP0) mP0Q!) TmP0) Tm0aP) ", +"1Pp) p18P) p1TP) sP1p) m0P* m*P+0 m*P0B am*P0 5P1* 1P>* 1m*PB >P1a* m*PQ0 +", +"QmP*0 Tm*P0 am0TP* 1Pp* >Pp1* p*1TP sP*p1 WmP0) mP0W!) HmP0) Hm0aP) 1W5P) 5", +"W81P) 1HmP) am)1HP QmPW0) P!m0WQ) ZmP0) amPZ0) p1WP) 1W8pP) Zp1P) Zp1sP) P*", +"v0 v+P*0 vHP*0 gvP*0 1Pv* >Pv1* v1HP* >Pg1* vQP*0 P*0v+Q ZvP*0 gvPZ*0 pv1P*", +" pv1>P* Zp*1P 1Py* ,9O 9&,O 9O,B ,O9a ,1O :,O 1O,B ,a:O 9O,K ,9&OK ,O9T ,T9", +"aO ,O9j ,j:O ,O1T :,sO 9*O -9O 9O*B 9a-O ,O1* :-O ,1*OB -a:O *O9K 9O-K 9OT*", +" 9T-O jO9* :O-j ,1T*O sO:- 9E,O ,9&EO ,O9H ,H9aO 1E,O ,E:O ,O1H ,H:O ,9EOK ", +"9&E,OK ,H9TO 9HT,aO ,1jEO :j,EO ,1HjO s,H:O ,O9d 9d-O 9OH* -9gO ,O1d -d:O ,", +"1HdO gO:- ,d9OK -9dOK ,Td9O -T9gO ,1djO :-djO ,1TdO g:s-O 9O,2 ,9&O2 ,9O2B ", +",a9O2 ,O95 ,5:O ,15OB :5a,O ,9O2K 9&O,2K ,T9O2 9TO,a2 ,15jO :5j,O ,1T5O s,5", +":O ,O9; 9;-O ,;9OB -9a;O ,O1; >O:- ,1;OB :->aO ,;9OK -9;OK ,T;9O -T9;O ,1;j", +"O :->jO ,1T;O s-:>O ,9EO2 9&E,O2 ,H9O2 9HO,a2 ,15EO :5,EO ,1H5O :H5,O 9EO,2", +"K EO,29&K 9HO,T2 HO,29Ta 95E,jO ,5j:EO ,HO95T :H5s,O ,d9;O -9d;O ,H;9O g-9;", +"O ,1d5O :->dO ,1H;O g:->O 9dO,;K 9d;-OK ,HO9T; g9;-TO djO,1; ->j:dO ;jO,1H ", +"s->g:O ,P9 ,9P& ,B9P 9a,P ,1P :P, ,B1P ,a:P 9Q,P ,Q9P& 9T,P ,Qa9P 9P,j ,Q:P", +" 1T,P :Ps, 9P* -P9 *B9P 9a-P 1P9* :-P ,1P*B :a-P Q*9P 9Q-P TP9* 9T-P P*9j -", +"Q:P P*j9T :-sP 9W,P ,W9P& 9H,P ,HP9a 1W,P ,W:P 1H,P ,H:P ,W9QP 9WQ,P& ,PZ9 ", +"Z9a,P ,1WPj :WQ,P ,1ZP :PZ, 9d,P 9W-P HP9* -Pg9 1d,P -W:P ,1HdP :-gP ,Qd9P ", +"-W9QP 9PZ* -PZ9 dPj,1 :-WPj Z9*1P :PZ- 9m,P m&P,9 ,m9PB ,am9P 95,P ,5:P ,1m", +"PB :ma,P ,Q9mP ,Q9m&P ,Tm9P 9Tm,aP ,1pP p,:P p9T,P sP,:p 9;,P 9m-P ,;P9B -m", +"9aP 1;,P :->P ,1;PB :-maP ,Q;9P -Q9;P ,T;9P -Tm9P 9Pp* :P-p p9*TP -ps:P ,W9", +"mP ,W9m&P ,Hm9P 9Hm,aP ,1W5P :W5,P ,1H5P :Hm,P 9WQ,mP QmP&,W9 Z9m,P ,amZ9P ", +"p9W,P :pW,P Zp9,P Z:p,P ,Pv9 v9-P v9H,P -vg9P ,1vP :P-v v1,HP :v-gP v9Q,P -", +"v9QP Zv9,P Z-v9P pv9,P :v-pP Zp9P* :-yP 0)CO C0)!O 0)DO Da0)O C)1O 18C)O D)", +"1O 1D8)O C0)OK !K)C0O DT0)O Ta0D)O jO1C) 18CjO) 1DT)O s1D)O C0*O +*C0O *0DO", +" a*0DO *C1O 1+*CO 1OD* 1+D*O *C0OK *C0+OK D*T0O D*0+TO *jC1O 1+C*jO 1D*jO s", +"*1DO C0)EO !E)C0O HD0)O Da0H)O 1CE)O 8CE1)O 1HD)O H8)1DO EK)C0O 0)EOCK! DT0", +"H)O 0)HODTa 1CEjO) C)8O1jE Hj)1DO 1HDs)O d*C0O d*C+0O H*D0O gD*0O 1d*CO 8dC", +"1*O 1H*DO g1D*O *C0dKO C0dO+*K Td0D*O D*Tg0O 1dC*jO dj1*+CO djO1D* g1Ds*O C", +"02)O !)2C0O D0)O2 a0)DO2 15C)O 58C1)O 1D5)O 5D)1aO CKO0)2 0)C2!KO T0)DO2 0)", +"TODa2 15CjO) 5)jO18C 5D)1TO 1D5s)O ;*C0O ;*C+0O D;*0O D;0a*O 1;*CO >*1CO 1D", +";*O >D*1O *C0;OK C0;O+*K T;0D*O a;D*T0O 1;C*jO *jC>O1 ;jO1D* s*1>DO CEO0)2 ", +"0)C2!EO D0)HO2 0)HODa2 5CE1)O 5E1O8C) H5)1DO 5OD81H) 0)C2EKO C)!OE02K 0)HOD", +"T2 DaHO0)T2 5)jO1CE 5j1EC)8O H)Dj15O 15s)HDO ;*Cd0O C0;O+d* D;0H*O D;*g0O 5", +"dC1*O 1d*>CO 1HO5D* g1*>DO C0;OdK* dK+*C0;O T;H*D0O D*g0T;O dj1;*CO dj>*1CO", +" jOH;1D* >Dgs1*O C0P) PC0!) P0D) aP0D) PC1) 1P8C) 1)DP 1DP8) QPC0) PC0Q!) Q", +"DP0) QD0aP) PjC1) Q8C1P) 1QDP) sP1D) *CP0 P*C+0 D0P* aP*D0 1CP* 1+P*C DP1* ", +"aP*1D P*CQ0 +QCP*0 QD*P0 aP0QD* P*j1C P*C1+Q P*j1D sP*1D WPC0) PC0W!) HPD0)", +" WD0aP) 1WPC) W8C1P) 1HPD) aP)1WD QPCW0) QCW0P)! ZDP0) aP0ZD) 1WCPj) QC8P1W", +") Z1DP) Z1DsP) dPC*0 +WCP*0 HP*D0 gPD*0 1W*PC P*C1+W 1HPD* g1PD* QdCP*0 QCd", +"P+*0 Z*DP0 gPDZ*0 dPC1Q* WC*j1+P Z1*DP sPDZ1* mPC0) PC0m)! DmP0) Dm0aP) 1mP", +"C) 8mC1P) 1DmP) am)1DP mPCQ0) P!m0QC) Tm0DP) amDPQ0) p1PC) 1P8pC) pD1P) pD1", +"sP) m*PC0 +mCP*0 Dm*P0 am0DP* 1m*PC >P1*C 1DmP* >PD1* Q;CP*0 QCm*+P0 ;P0QD*", +" m*DP+Q0 p*1PC p*1>PC pD*1P sP1pD* mPCW0) P!m0WC) Dm0HP) amHPD0) 5WC1P) W)8", +"P1mC 1H)5DP H)8m1DP QCW0mP) !Cm)QPW0 DmPZ0) amZPD0) 1WPpC) 18pCWP) pDPZ1) s", +"1pDZP) vP*C0 P*Cv+0 vDP*0 gP*vD0 v1P*C v1*>PC v1DP* >D*g1P P*CvQ0 P*vC+Q0 v", +"DPZ*0 gDZ*vP0 v1Pp*C >*v1pPC Z1PpD* y*1DP b9,O ,b9&O ,O9D ,9cO ,Ob1 ,b:O ,O", +"1D c,:O ,b9OK b9&,OK ,D9TO c9T,O ,1bjO :bj,O ,1DjO s,O:c bO9* b9-O 9OD* -9c", +"O bO1* -b:O ,1D*O :Oc- b*9OK -b9OK bT*9O c-9TO b1*jO :-bjO b1T*O s-:cO ,b9E", +"O b9&,EO ,H9bO c9H,O ,1bEO :b,EO ,1HbO :cH,O b9E,OK b9,&EKO bH9,TO ,H9cTO b", +"1E,jO ,jb:EO ,1ObHj :cHs,O ,db9O -b9dO ,Dd9O c-9gO ,1dbO :-bdO ,1DdO g:c-O ", +"9dOb*K bd9-OK ,DO9Td -T9gcO djOb1* -jb:dO djO,1D s-cg:O ,b9O2 b9&,O2 ,D9O2 ", +"c29,O ,1b5O :b5,O ,1D5O :c5,O b9O,2K bO,29&K 9DO,T2 ,T9c2O b15,jO ,5b:jO ,1", +"Ob5T :c5s,O ,;b9O -b9;O ,D;9O c-9;O ,1;bO :->bO ,1D;O c>:-O 9;Ob*K b;9-OK ,", +"DO9T; -T9c;O ;jOb1* -jb:>O ;jO,1D c>:s-O b9E,O2 EO,2b9& 9HO,D2 ,H9c2O b1E,5", +"O ,5b:EO ,1ObH5 c5,:HO EO,2b9K 9&,KEOb2 HO,2bT9 ,9c2HTO 5E9O,jb ,b:EjO5 ,O1", +"Hb5T sHc5:,O bd9,;O bd9-;O ,HO9D; g-9c;O ,1Ob5d ->b:dO ,1ObH; c>-g:O ,Obd9;", +"K b9-OdK; ,OH;bT9 gTc;-9O ,O1db;j >j:d-bO 1H,;bTO >Og:s-c ,b9P ,P9b& 9D,P ,", +"Pc9 b1,P ,P:b 1D,P :,cP ,Qb9P bQ9,P& ,QD9P cQ9,P ,1QbP :Qb,P ,1QDP sP,:c 9P", +"b* bP-9 DP9* c9-P 1Pb* :b-P ,1DP* c-:P bQ*9P -Qb9P bTP9* c-Q9P P*jb1 :-QbP ", +"P*j9D s-P:c ,Wb9P bW9,P& ,HP9D cW9,P ,1WbP :Wb,P ,1HbP :cW,P bW9,QP WQP&,b9", +" Zb9,P Zc9,P b1W,Pj ,Wj:Pb Zb1,P Z:c,P ,dPb9 -Wb9P ,Dd9P c-W9P ,1dbP :-WbP ", +",1DdP g:Pc- 9QdbP* bQd-P9 Zb*9P Z-c9P b1WP*j :Wb-Pj Zb1P* Z:-cP ,mb9P bm9,P", +"& ,Dm9P cm9,P ,1mbP :mb,P ,1D5P :cm,P bQ9,mP QmP&,b9 9Dm,TP ,TmcP9 pb9,P :p", +"b,P p9D,P cp:,P ,;Pb9 -mb9P ,D;9P c-m9P ,1;bP :-mbP ,1D;P c>P:- 9Q;bP* bQ;-", +"P9 9QD,;P cm9-TP pb*9P -p:bP p9DP* cp-:P bW9,mP WmbP,9& 9Hm,DP ,HmcP9 b1W,5", +"P ,5W:Pb b1H,5P :H,c5P QmbP,W9 ,bP&WQ9m ,DmZ9P cm9Z,P ,1WpbP pbW:P, Z9PpD, ", +"Z:Pcp, vb9,P -vb9P v9D,P cv-9P vb1,P :v-bP v1D,P :vc-P bQ*v9P vb9-QP Z9PvD,", +" Z-9cvP pbPv1, -pb:vP ZbPp9* y:c-P R) R)! R)B Ra) R). R8) .BR) 8aR) RM) M)R", +"! RT) R)aM Rj) R)8M TjR) sR) R* R+* R*B Ra* R*. R8* R.*B 8*Ra R*M +MR* RT* ", +"+TR* R*j +jR* T*Rj sR* RE) !ER) RH) HaR) E)R. 8ER) H)R. R)H8 MER) RME!) R)H", +"M aM)RH jER) R8ME) R)Hj RHs) Rd* +dR* RH* gR* d*R. 8dR* R.H* R8g* dMR* dM*R", +"+ HMR* RTg* djR* R+jd* H*Rj s*gR R)2 !)R2 2BR) a)R2 R5) 58R) 5BR) R)5a M)R2", +" RM)!2 T)R2 aM)R2 R)5M R58M) R)5T R5s) R;* +;R* *BR; a;R* R5* >R* *BR5 Ra>*", +" M;R* M;*R+ T;R* aM;R* 5MR* R*>M 5TR* >Rs* E)R2 RE)!2 H)R2 RHa)2 5ER) R58E)", +" R)H5 RH58) RME)2 ME)R2! RHT)2 RH)aM2 R5ME) 5MER8) RH5T) sRH5) d;R* R+d;* H", +";R* R;g* 5dR* Rd>* H5R* >*gR dM;R* R+dM;* RH;T* gRT;* R5dM* >MRd* RH5T* >Mg", +"R* RP) P)R! P)RB R)aP P)R. R)8P RP).B aP)R8 S) S!) ST) Sa) Sj) S8) TjS) Ss)", +" R*P +PR* RP*B aPR* R.P* 8PR* P*.RB aP*R8 S* S+* S*T Sa* S*j S8* T*Sj Ss* R", +"W) W!R) R)HP R)Wa W)R. R)W8 RHW). RH8W) SW) W!S) SZ) ZaS) WjS) W8S) ZjS) s)", +"SZ RW* +WR* HPR* RWg* R.W* W8R* RH*W. gRW8* Sd* +WS* SZ* gS* W*Sj W8S* SjZ*", +" s*gS Rm) m)R! m)RB R)am R)5P R)8m R5m)B R5am) Sm) m)S! TmS) amS) Sp) p8S) ", +"pTS) s)Sp Rm* +mR* *BRm amR* 5PR* Rm>* R5*mB >PRa* S;* +mS* TmS* amS* Sp* S", +">* STp* s*S> WmR) RWm!) R)Hm RHma) R)5W R5W8) RH5W) RH8m) WmS) S!Wm) ZmS) S", +"Zam) pWS) SpW8) S)Zp SZps) vR* R+v* RHv* g*vR R5v* >*vR vRH5* >PgR* Sv* v+S", +"* ZvS* S*gv pvS* v>S* ZpS* yS* ,R ,R& ,RB ,Ra ,R. ,R8 RB,. R8,a ,RM R&,M ,R", +"T RT,a ,Rj R8,M RT,j sR, ,R* -R *B,R -Ra ,.R* -R8 ,R*.B R8-a R*,M -RM R*,T ", +"-RT R*,j -Rj ,RT*j s-R ,RE ,ER& ,RH RH,a RE,. ,ER8 ,.RH RH,8 RM,E M&E,R RH,", +"T ,RHT& ,ERj ,R8ME RH,j ,RsH ,Rd -Rd RH,d g-R ,.Rd R8-d ,RHd. g8-R Rd,M Rd-", +"M RT,d -RgM Rd,j Rd-j ,RHdj gRs- ,R2 ,2R& RB,2 ,2Ra ,R5 R5,8 RB,5 R5,a RM,2", +" M&2,R ,2RT ,RT&2 R5,M ,R58M R5,T ,Rs5 ,R; -R; RB,; Ra-; R5,; ->R ,R5*B -a>", +"R R;,M R;-M RT,; RT-; R;,j -R>M ,R5T; ->sR RE,2 ,R&E2 ,2RH ,RH&2 ,ER5 ,R58E", +" RH,5 ,RH5a ,RME2 ,REM&2 ,RHT2 aM2,RH ,R5ME M&E,R5 ,RH5T sR,H5 Rd,; Rd-; RH", +",; g;-R R5,d -d>R ,RH5d ->gR ,RdM; -RdM; ,RHT; -RTg; ,R5dM >M-Rd ,RH;j s-Rg", +"> ,RP R&,P RB,P Ra,P RP,. R8,P ,RP.B ,R8aP S, S&, S,T Sa, S,j S8, ,TSj Ss, ", +"R*,P -RP P*B,R Ra-P P*.,R R8-P ,R.P*B -R8aP S*, S- ,TS* S-T ,jS* S-8 S*T,j ", +"S-s ,RW R&,W RH,W RW,a ,.RW RW,8 ,RHW. ,RHW8 S,W ,WS& SZ, Z&S, ,WSj ,WS8 Sj", +"Z, s,SZ RW,d -RW ,RHW* -RgP ,RWd. RW-8 dP.,RH -RWg8 Sd, S-W Z*S, gS- ,dSj -", +"WS8 SZ*,j s-gS ,Rm R&,m RB,m Ra,m R5,m R8,m ,R5mB ,R5am S,m ,mS& ,TSm ,aSm ", +"Sp, p&S, STp, s,Sp Rm,; -Rm ,Rm*B Ra-m ,R5m* -R>P m*B,R5 >P-Ra S;, S-m ,TS;", +" -TS; p*S, S-p Sp*,T s-S> RW,m ,RWm& RH,m ,RHam R5,W ,R5W8 ,RH5W ,RH8m ,WSm", +" S&W,m SmZ, SZ&,m SWp, Sp&,W ZpS, Ss,Zp vR, -vR ,RvH gR-v ,Rv5 v>-R vR,H5 -", +"vRg> Sv, S-v ZvS, SvZ- pvS, Sv-p Sv,Zp y-S RU) U!R) RD) R)Ua U)R. R)U8 D)R.", +" R)D8 UMR) RUM!) VR) RaV) R)Uj R8UM) RjV) VRs) R*U U+R* RD* UaR* R.U* U8R* ", +"R.D* D8R* RUM* R+UM* V*R R+V* U*Rj R+jU* R*Vj V*sR UER) RUE!) R)UH RHUa) RU", +"E). R8UE) RHU). RH8U) RUME) U!MRE) RHV) VRHa) RjUE) U8ERj) VRHj) VsHR) UdR*", +" R+Ud* UHR* RDg* RdU*. R8dU* RH*U. gRU8* dM*RU R+UdM* RHV* V*gR RdjU* U8dR*", +"j VdR*j s*VgR U)R2 RU)!2 D)R2 RDa)2 R)U5 R5U8) R)5D R5D8) RUM)2 U!MR)2 R)V2", +" V2Ra) R5UM) U58Rj) R5V) Vs5R) U;R* R+U;* D;R* R+D;* U5R* R*>U 5DR* RD>* M;", +"*RU R+UM;* R;V* V+R;* R5*Uj >MRU* R5V* >RV* RUE)2 U!ER)2 RHU)2 UH)Ra2 R5UE)", +" U5ER8) RH5U) RH)U5a UMER)2 !ER)UM2 V2RH) RHaV2) U5ERj) REU85M) V5RH) sRHV5", +") RdU;* U+dR;* RH;U* gRU;* R5dU* >RUd* RH5U* g>RU* RdUM;* dMR;U+* VdR;* gVR", +";* U5dR*j Rdj>U* V5dR* V>gR* UP) P)U! R)DP U)aP P)U. U)8P RDP). RD8P) SU) U", +"!S) SV) VaS) UjS) U8S) VjS) s)SV UP* +PU* DPR* aPU* U.P* 8PU* RD*P. aP*U8 S", +"*U U+S* SV* V+S* U*Sj U8S* SjV* s*SV R)UW RWU!) R)WD RWDa) RWU). RW8U) RWD)", +". RWD8) UWS) S!UW) S)VZ SZVa) SUWj) S8UW) SZVj) SsZV) UWR* R+WU* HPU* UPg* ", +"RW*U. RW8U* RWD*. gUP8* UWS* S+UW* Z*SV S*gV SdU*j S8dU* SZ*Vj Ss*gV R)Um R", +"mU!) R)Dm RDma) U)5P R8mU) R5Dm) RD8m) UmS) S!Um) S)Vm SVam) S)pU SpU8) S)V", +"p SspV) UmR* R+mU* DmR* am*RD 5PU* UP>* R5Dm* >PUD* UmS* S+Um* VmS* SV+m* p", +"US* >US* S*Vp S*V> RWUm) UW)Rm! RHmU) am)RWD R5WU) R5)UW8 R5WD) aP)UH5 SUWm", +") UWmS!) SZVm) VZmSa) SpUW) pUWS8) SZpV) VpZSs) R*vU vRU+* RDv* gvRU* vRU5*", +" >PUv* vRD5* >PgU* vUS* SvU+* S*Vv VvgS* Sp*vU S>vU* VvpS* SVy* ,Rb R&,U ,R", +"D cR, ,.Rb Rb,8 ,.RD ,Rc8 Rb,M ,RbM& V,R V,cR Rb,j ,R8bM ,RVj cRs, Rb* -Rb ", +"R*,D c-R R.b* Rb-8 ,RD*. c8-R bMR* Rb-M ,RV* V-R b*Rj Rb-j V*R,j sRV- ,ERb ", +",Rb&E Rb,H ,RcH ,RbE. ,R8bE ,RHb. cRH,8 ,RbME ,RbM&E ,RVH cRV,H ,RjbE ,Rb&j", +"E V,RHj s,VRH Rb,d Rb-d RD,d c-gR ,Rdb. -Rb8d ,RDd. c-Rg8 ,RdbM -RbdM ,RVd ", +"gRV- ,Rdbj -Rjbd Vd,Rj V-gsR ,2Rb ,Rb&2 ,2RD ,Rc2 Rb,5 ,R5b8 R5,D ,Rc5 ,RbM", +"2 ,RbM&2 ,RV2 cRV,2 ,R5bM Rb5,8M ,RV5 s,VR5 Rb,; Rb-; RD,; -Rc; b5R* >R-U ,", +"R5D; ->cR ,R;bM -RbM; ,RV; V;-R ,R;bj >M-Rb V5*,R -RV> ,RbE2 R&E,U2 ,RHb2 c", +"RH,2 ,R5bE ,REU5& ,RHb5 cR5,H RbE,M2 bER2,M& V,RH2 V,HcR2 ,REb5M ,ER5b8M V5", +",RH s,HcR5 ,Rdb; -Rbd; ,RHb; c-Rg; ,R5bd ->Rbd ,R5Dd c>Rg- Rbd,M; Rbd-M; Vd", +",R; V-gR; Rb5,dM -Rb>Md V5d,R V->gR ,UP U&,P RD,P ,RcP ,.UP U8,P ,RDP. cR8,", +"P Sb, ,US& SV, Sc, ,USj ,US8 SjV, s,Sc bPR* -UP ,RDP* -RcP ,UP*. U8-P P*.,R", +"D c-R8P Sb* S-b V*Sb Sc- b*Sj -US8 SV*,j s-Sc Rb,W ,RWU& RW,D ,RcW ,RWb. ,R", +"Wb8 ,RWD. cRW,8 ,USW SbW,& ZbS, S,Zc SbW,j Sb8,W SZb,j Ss,Zc bWR* Rb-W ,RWD", +"d -RcW ,UdP. -RWb8 dP.,RD c-RW8 ,USd -USd ZbS* Z-Sc Sbd,j S-bW8 SZb*j gScs-", +" Rb,m ,RmU& RD,m ,Rcm U5,P ,R8bm ,R5Dm cR5,m ,USm Sbm,& VmS, cmS, pbS, Spb,", +"8 S,Vp S,cp bmR* Rb-m ,RDm* -Rcm ,U5;P -U>P R5D,;P c>R-m ,US; -US; V;S, S;V", +"- pbS* Sb-p Vp*Sb cpS- ,RWbm RW&,Um ,RHbm cRW,m ,R5bW Rb5,W8 ,R5WD cR5,W Sb", +"W,m ,UWS&m SZb,m ScZ,m Spb,W S8WpU, Vp,SZ ScpZ, ,Rvb vR-U ,RvD cR-v vRb,5 -", +"vR>b vRD,5 cvR-> vbS* Sb-v S,Vv cvS- Svbp* -pUSv Vv,Sp Scy- Rk) k)R! k)RB R", +")ak R1) R)8k 1)RB R)1a kMR) RkM!) R)Tk ak)RT lR) R8l) RTl) l)sR R*k +kR* Rk", +"*B akR* R1* 1+R* *BR1 1aR* RkM* k*MR+ TkR* ak*RT l*R R+l* RTl* sRl* kER) Rk", +"E!) R)Hk ak)RH 1ER) R18E) R)1H R1H8) RkME) kMER)! RHTk) RHTak) REl) lR8E) R", +"Hl) lsRH) dkR* dk*R+ HkR* R*gk 1dR* R1+d* 1HR* R1g* dkMR* R+dk*M RH*Tk gRTk", +"* Rdl* l+Rd* RHl* gRl* k)R2 Rk)!2 Rk)2B ak)R2 R)5k R158) R15)B R1a5) RkM)2 ", +"kM)R2! RTk)2 RT)ak2 R5l) lR58) lR5T) lsR5) k;R* k;*R+ k;*RB ak;R* 1;R* R1>*", +" R1;*B >kRa* k;MR* R+kM;* RT;k* RT;ak* R5l* >Rl* l;RT* l>sR* RkE)2 kE)R2! R", +"Hk)2 RH)ak2 R15E) 5kER8) R1H5) ak)RH5 kMER)2 !ER)kM2 Hk)RT2 akHMR)2 lR5E) R", +"58lE) lHR5) sR5lH) dk;R* R+dk;* RH;k* gRk;* R1d5* >kRd* R1H5* >kgR* RdkM;* ", +"dkR;+M* Hk;RT* RT;gk* ldR5* l>Rd* lH;R* gl>R* kP) k!P) P)kB a)kP R)1P 8)kP ", +"kP)1B akP1) Sk) k)S! TkS) akS) Sl) l)S8 STl) l)Ss kP* k*+P *BkP kPa* 1PR* k", +"P8* kP*1B akP1* S*k +kS* TkS* akS* l*S S+l* lTS* l*Ss R)Wk kP)W! H)kP akPH)", +" R)1W R1W8) R1HW) R1Wa) WkS) S!Wk) S)Zk SZak) SWl) Sl8W) l)SZ SslZ) WkR* dk", +"P+* kPH* kPg* 1WR* dkP8* kP*1H gR1W* WkS* S+Wk* ZkS* S*gk Sdl* S8dl* SZl* l", +"*gS R)km km)R! km)RB akmR) R)1m R1m8) R1m)B akP5) kmS) S!km) STkm) Sakm) l)", +"Sp Slp8) SlpT) Sslp) kmR* km*R+ km*RB akmR* 1mR* kP>* kP*5B >kPa* kmS* S+km", +"* S;Tk* Sa;k* Spl* l*S> Sp*lT Ss*l> km)RW RW)km! RHmk) ak)RHm R1W5) km)RW8 ", +"R1Hm) ak)R5W SWkm) km)S!W SZkm) ZkmSa) SlpW) lWpS8) SZpl) lZpSs) R*vk vR+k*", +" vRHk* gvRk* R1v* >kvR* vR1H* >kgP* vkS* Sv+k* SZ*vk gSvk* l*Sv lv>S* lvZS*", +" l*yS ,R9 R&,k RB,k R9,a ,R1 :R, RB,1 ,R:a R9,M ,R9M& R9,T ,RT9a l,R :Rl, ,", +"RlT sRl, R9* -R9 *BR9 R9-a R*,1 :-R ,R1*B :a-R 9MR* R9-M 9TR* R9-T ,Rl* l-R", +" l*R,T sRl- ,ER9 k&E,R R9,H ,RH9a ,ER1 ,R:E R1,H ,R:H ,R9ME ,R9M&E ,RH9T R9", +"H,aM ,RlE :R,lE ,RlH :RHl, R9,d R9-d 9HR* -Rg9 R1,d -R:d ,R1H* :-gR ,Rd9M -", +"R9dM ,RT9d -RTg9 ,Rld -Rld lH*,R gRl- ,2R9 k&2,R ,R92B ,Ra92 R9,5 ,R:5 ,R15", +"B :R5,a ,R9M2 ,R9M&2 ,RT92 ak2,RT ,Rl5 :R5l, l,R5T ls,R5 R9,; R9-; ,R;9B -R", +"9a; R1,; ->:R ,R1;B :-R>a ,R;9M -R9M; ,RT9; -RT9; ,Rl; -Rl> l;,RT s-Rl> ,R9", +"E2 ,REk&2 ,RH92 ak2,RH ,R15E :R5,E ,R1H5 :RH,5 R9E,M2 k&,MRE2 ,R29HM ,2RH9a", +"M l,R5E l,5:RE lH,R5 s,5:RH ,Rd9; -R9d; ,RH9; g-R9; ,R15d :-R>d ,R1H; g:R->", +" R9d,M; R9d-M; R9H,T; -R;g9M ld,R5 l->Rd lH;,R gl->R ,kP k&,P ,BkP ak,P R1,", +"P ,R:P ,R1PB :Ra,P S9, ,kS& ,TS9 ,aS9 l,S S:, lTS, l,Ss 9PR* -kP kP*,B ak-P", +" kP*,1 -R:P k*B,1P :-RaP S9* S-9 9TS* -TS9 S9l* l-S S9*lT s-S: R9,W kP&,W H", +"k,P akP,H R1,W ,R:W ,R1HP :RH,W ,WS9 S9W,& Z9S, SZ9,a S,lW :WS, SZl, S,Z: 9", +"WR* R9-W dkP,H -kgP dkP,1 -R:W ,1HdkP g:R-W ,dS9 -WS9 Z9S* g9S- Sdl, lWS- l", +"Z*S9 gSl- R9,m km&,R ,Rm9B akm,R R1,m ,R:m ,R1mB :Rm,a ,kSm S9m,& S9T,m S9a", +",m Spl, S,:p Sp9,T Ss,:p 9mR* R9-m k;P,B -Rm9a k;P,1 -R:m k;B,1P :-Ram ,kS;", +" -kS; S9;,T S-9Tm S;l, S>l- Sp9T* l-pSs ,RW9m ,RWkm& ,RH9m ,RHakm ,R15W :RW", +",5 ,R1Hm :RH,m S9W,m km&S,W SZ9,m SamZk, Sp9,W S:p,W lZ,Sp S:Zp, ,Rv9 vR-k ", +"vR9,H -vRg9 ,Rv1 :R-v vR1,H :vRg- v9S* S9-v Sv9Z* gS-v9 l,Sv S-lv lv,SZ l-y", +"S UkR) RUk!) R)Dk ak)RD R)1U R1U8) R)1D R1D8) RUkM) U!kRM) RkV) VRak) RUl) ", +"lRU8) VRl) lsVR) RUk* R+Uk* DkR* ak*RD 1UR* R1+U* 1DR* R1+D* k*MRU R+Uk*M R", +"*Vk V+Rk* R*lU l+RU* lRV* s*VlR RUkE) U!kRE) RHUk) RHUak) R1UE) 1UER8) R1HU", +") R1)UH8 UkMRE) !ER)UkM VRHk) ak)VRH lRUE) R8UlE) VlRH) Vs)lHR dk*RU R+Udk*", +" RH*Dk gRUk* R1dU* 1UdR8* R1HU* gR1U* RdUk*M dkR*U+M VdRk* gVRk* ldRU* R8dl", +"*U lH*VR glVR* RUk)2 U!kR)2 RDk)2 RD)ak2 R1U5) 1U5R8) R1D5) ak)R5D UkMR)2 !", +")k2RUM V2Rk) ak)V2R lRU5) R5Ul8) VlR5) V5Rls) k;*RU R+Uk;* RD;k* RD;ak* R1;", +"U* >kRU* R1D5* >kDR* R;Uk*M k;R*U+M V;Rk* ak;V*R l;RU* l>RU* l;VR* V>lR* Uk", +"ER)2 !ER)Uk2 Hk)RD2 R)UHak2 1UER5) RE1U58) R1)UH5 R)1HU5a E)R2UkM U!RME)k2 ", +"RHkV2) akV2RH) R5UlE) RUlE58) V5RlH) lRV5sH) RdUk;* dkR;U+* Hk;RD* RD;gk* 1", +"UdR5* R1d>U* 1UHR5* >D*gR1 dkR;U*M U+R*k;dM RH;V*k gRkV;* R5dl*U >RUld* lHR", +"V5* glRV>* U)kP kP)U! D)kP akPU) 1)UP 1UP8) R1DP) 1UaP) UkS) S!Uk) VkS) SVa", +"k) SUl) SlU8) l)SV SlVs) kPU* kP*U+ kPD* akPU* UP1* kP*U8 kP*1D aP*1U U*Sk ", +"S+Uk* SkV* SV+k* lUS* S8*lU SVl* Ss*Vl kP)UW UW)kP! RWDk) ak)RWD R1WU) kP)U", +"W8 R1WD) aP)1UH SUWk) UWkS!) SZVk) ZkVSa) SlUW) lWUS8) SlVZ) lZVSs) dkPU* U", +"+WkP* dkPD* gUPk* R1WU* 1U+RW* R1WD* g1UP* SdkU* U+WS*k SZ*Vk gSVk* lW*SU S", +"+UlW* lZ*SV glSV* km)UP Um)kP! RDmk) ak)RDm R1mU) km)U8P R1Dm) ak)U5P SUkm)", +" km)S!U SVkm) VmkSa) SlpU) pUlS8) SlVp) VplSs) km*UP U+mkP* km*RD R+Dkm* kP", +"*U5 >kPU* kP*5D >kDP* S;Uk* km*S+U SV;k* S+kVm* Sp*lU S>lU* Vp*Sl S>Vl* RWU", +"km) k!m)RWU km)RWD akRmUH) kP)U5W R)1WU8m kP)UH5 1HRmU8) km)SUW WmS)U!k Zkm", +"SV) akVmSZ) lWUSp) SplWU8) lZSVp) SlVpsZ) vRUk* kP*vU+ vRDk* gR*vDk vR1U* >", +"P*v1U vR1D* >D*vR1 SvUk* vU+S*k VvSk* gSkVv* lvSU* S>Ulv* lvVS* ylVS* tR, ,", +"Rt& ,RtD t,cR ,Rt1 t,:R R1,D :,cR ,RtM tRM,& tRV, tcV,R tRl, t:Rl, lRV, uR,", +" t*R t-R RDt* cRt- R1t* :Rt- t*R1D c-:R R*tM tR-M tRV* -RVt lRt* -Rlt t*VlR", +" u-R ,RtE tR&,E ,RtH tcR,H tR1,E t:R,E tHR,1 t:H,R tRM,E M&EtR, tHV,R cRVtH", +", ltR,E lt,:RE ltH,R ,RuH ,Rtd -Rtd RHt* gRt- tdR,1 t:d-R tH*R1 g:Rt- tdR,M", +" t-RdM tH*VR V-gtR ltd,R l-tRd tH*lR g-uR ,Rt2 tR&,2 tRD,2 tcR,2 ,Rt5 t:R,5", +" t5R,D tc5,R tRM,2 M&2tR, VtR,2 Vt,cR2 t5Rl, lt5:R, t5V,R ,Ru5 ,Rt; -Rt; t;", +"R,D tc;-R R5t* -Rt> t5*RD c>Rt- t;R,M t-RM; t;V,R V-tR; t5*lR l-t>R t5VR* -", +">uR tR,E2 ,R&tE2 tHR,2 cR,tH2 t5R,E :R,t5E tH5,R tH,cR5 ,RMtE2 M&tE,R2 V,Rt", +"H2 V2tc,RH l,Rt5E lRt5:,E tHRV5, uRH,5 tdR,; t-Rd; tH;,R gt-R; t5d,R t>-Rd ", +"tH5R* gt>-R ,Rdt;M -Rdt;M tHRV;, gtRV-; t5Rld, t>Rl-d tHRl;, u->gR tP, ,Pt&", +" ,DtP t,cP ,1tP t,:P tP1,D t:Pc, St, t&S, VtS, S,tc l,St S,t: SVl, uS, tP* ", +"t-P DPt* c-tP 1Pt* :-tP tP*1D t-P:c St* S-t t*SV StV- l*St Stl- St*Vl u-S ,", +"RtW tWR,& ,HtP tcW,R tWR,1 t:W,R tHP,1 t:H,P S,tW StW,& S,tZ ScZt, Stl,W S:", +"t,W lZ,St SZu, ,dtP -RtW HPt* gPt- tW*R1 t-W:R tHP1* gtP:- tWS* Sdt- S*tZ S", +"-gt Stdl* l-StW tZ*Sl gSu- ,Rtm tmR,& tmR,D tcm,R ,5tP t:m,R t5P,D tc5,P tm", +"S, Stm,& SV,tm Sct,m S,tp S:pt, Vp,St Spu, ,;tP -Rtm tm*RD t-mcR 5Pt* >Pt- ", +"t5PD* t>Pc- tmS* S;t- St;V* V-mSt S*tp tpS- tp*SV S-up tWR,m ,RWtm& tHm,R t", +"H,cRm t5W,R t5,:RW tH5,P tH,:Rm StW,m tWmS&, tZS,m tZmSc, tpS,W tpWS:, tZpS", +", uZ,Sp vRt* tP-v tvR,H cvRt- tvR,1 :vRt- tH5P* :vRc- S*tv tvS- Vv,St V-vSt", +" lvtS* l-vSt Vv,Sl y-uS X X! XB aX X. 8X X.B 8Xa XK XK! TX TXa Xj 8Xj TXj s", +"X X( +X X(B +Xa X(. +X8 .BX( +8aX X(K +XK TX( +TX Xj( +Xj T(Xj sX+ GX GX! H", +"X GaX GX. G8X HX. H8X GXK XKG! GTX TXGa GjX 8XGj HXj sGX dX +GX HXd gX dX. ", +"8dX H.dX gX8 dXK GK+X TdX gXT dXj Gj+X dXHj gsX X2 X2! X2B aX2 5X 5X8 5XB 5", +"aX X2K 2KX! TX2 T2aX 5Xj 58Xj 5TX sX5 X; +X; X;B aX; 5X; >X 5BX; >Xa X;K +K", +"X; TX; T;+X X;j >Xj T;5X >Xs GX2 X2G! HX2 G2aX G5X 5XG8 H5X 5aHX X2GK GX2!K", +" G2TX aX2GT 5XGj G58Xj 5THX G5sX G;X G;+X HX; gX; 5dX >XG 5dHX >Xg GKX; dXK", +"+; TXG; TXg; dj5X Gj>X Td5X gs>X XP XP! XPB aXP XP. 8XP P.XB aX8P QX QX! QX", +"T QaX QXj Q8X XjTP sXQ XP( +XP P(XB aX+P P(X. 8X+P XP(.B aXP+8 QX( +QX Q(TX", +" Qa+X Q(Xj Q8+X Xj(TP +QsX Y Y! YH Ya Y. Y8 YH. Y8H YQ YQ! YZ YZa Yj Y8Q YZ", +"j sY Yd Y+ YHd gY Yd. Y+8 HdY. gY8 YQd Y+Q YZd gYZ Yjd Y+j ZdYj sYg Xm Xm! ", +"XmB aXm 5Xm 8Xm 5BXm aX5P QXm Q!Xm TXm aXTm pX pX8 pXT spX Xm; +Xm XB;P aX+", +"m X;5P >Xm Xm;5B aX>P QX; QX+m TXQ; TX+m pX; >Xp TXp; sp>X Ym Ym! YHm Yam Y", +"5 Y58 Y5H Y5a YQm Q!Ym YZm ZaYm Yp Yp8 YpZ sYp Yv Yv+ YvH gYv Yv5 Y> vHY5 Y", +">g YvQ v+YQ YZv YZgv Ypv Y>p ZpYv yY X# X& X#B aX& X#. 8X& .BX# 8aX& X#K X&", +"K TX# TX& Xj# X&j T#Xj sX& X#( +X& #BX( +aX& #(X. +8X& X#(.B aX&+8 #(XK +KX", +"& X#T( T&+X j#X( X&+j Xj#T( +Xs& GX# G&X HX# HX& X#G. 8XG& X#H. 8XH& X#GK G", +"KX& G#TX TXG& G#Xj X&Gj H#Xj G&sX dX# dX& H#dX gX& X#d. d&8X dX#H. 8Xg& X#d", +"K dKX& T#dX TXg& d#Xj X&dj dXjH# g&sX X#2 X&2 2BX# X2a& 5X# 5X& X#5B aX5& 2", +"KX# &2XK X#T2 T2X& 5#Xj Xj5& 5#TX 5Xs& X;# X;& ;#XB X&a; 5#X; >X& X;#5B aX>", +"& ;#XK XK;& T#X; X;T& X#;j X&>j X;jT# >&sX X#G2 G2X& X#H2 H2X& G#5X 5XG& H#", +"5X 5XH& GX#2K X&2GK GTX#2 GT&X2 Xj#G5 X&jG5 G5TX# sX5G& G#X; X;G& H#X; X;g&", +" 5#dX G&>X H5Xd# >&gX dX#;K dX&;K GT;X# gXT;& dXj5# >XG&j X;jH# >Xgs& XP# X", +"&P P#XB X&aP P#X. X&8P XP#.B aXP8& QX# QX& Q#TX TXQ& Q#Xj 8XQ& Xj#TP QXs& P", +"#X( X&+P XP#(B aXP+& XP#(. X&P+8 P#(X.B +X8aP& X#Q( Q&+X QXT#( +QXT& Xj#Q( ", +"X&j+Q TX#Pj( sX+Q& Y# Y& YH# Ya& Y#. Y8& H#Y. H8Y& YQ# Y&Q YZ# YZ& Yj# Y&j ", +"ZjY# sY& Yd# Y+& HdY# gY& d#Y. 8dY& YHd#. g8Y& QdY# +QY& ZdY# Y&gZ djY# +jY", +"& YZdj# Y&gs Xm# Xm& m#XB X&am 5#Xm Xm5& Xm#5B aXm5& Q#Xm XmQ& T#Xm XmT& pX", +"# pX& TXp# p&sX X#;P X&+m Xm;#B aXm;& Xm;5# Xm>& 5X#;PB >Xam& Q#X; X;Q& QX;", +"T# aX;Q& X;p# p&>X pXT;# >Xps& Ym# Y&m HmY# HmY& Y5# Y5& H5Y# H5Y& QmY# Q&Y", +"m ZmY# YmZ& Yp# Yp& Y#Zp Y&sp Yv# Yv& vHY# Y&gv v5Y# Y>& YvH5# Y&g> vQY# YQ", +"v& Y#Zv ZvY& Y#pv pvY& YZvp# yY& IX IX! DX DXa IX. 8IX DX. D8X IXK I!XK DXT", +" aITX IjX Ij8X DXj sIX IX( +IX DX( +DX X(I. 8I+X X(D. D8+X X(IK IX+K D(TX D", +"X+T I(Xj Ij+X D(Xj +DsX GIX IXG! JX JaX IXG. GI8X JX. J8X IXGK GIX!K JTX TX", +"Ja GIXj IjXG8 JjX sJX IdX Id+X JdX gJX I.dX Id8X dXJ. J8gX IXdK IdX+K TdJX ", +"JTgX dXIj Idj+X dXJj gXsJ IX2 I!X2 DX2 I2aX 5IX 8I5X 5DX D85X X2IK IX2!K I2", +"TX aX2TI Ij5X IjX58 DX5T 5DsX I;X I;+X DX; D;+X I;5X >XI D;5X >XD IKX; I;X+", +"K TXD; aX;TI X;Ij Ij>X X;Dj sI>X IXG2 GIX!2 JX2 aXJ2 GI5X G58IX J5X 5aJX GI", +"X2K IX2GK! TXJ2 JTaX2 IjXG5 G58IjX 5TJX J5sX dXI; IdX+; J;X J;gX Id5X Id>X ", +"5dJX J>X IdX;K +GIX;K TXJ; gJTX; Idj5X >IjdX X;Jj >XsJ IP IP! DXP aIP IP. 8", +"IP D.IP 8IDP QIX Q!IP QDX DXQa IPj 8IQX DjQX sIP IP( +IP D(IP DX+P P(I. 8I+", +"P IP(D. +DX8P Q(IP QI+X Q(DX QD+X I(Pj IP+j IPjD( +IsP YI YI! YJ YJa YI. Y8", +"I YJ. YJ8 YQI QIY! YZJ ZJYa YjI Q8YI YJj sYJ YId Y+I YJd gYJ IdY. +IY8 JdY.", +" Y8gJ QIYd +QYI YdZJ YZgJ IdYj +IYj YdJj sJgY ImX I!Xm DXm aXDm 5IP 8I5P DX", +"5P 8XDm ImQX ImXQ! DmQX aXmQD pIX 8IpX pDX pDsX I;P Im+X I;DP DX+m I;5P >PI", +" I;P5D DX>P I;QX I;P+Q DXQ; aX;QD I;pX pI>X DXp; sI>P YIm ImY! YJm JmYa Y5I", +" 5IY8 YJ5 J5Y8 QIYm YQIm! JmYZ YZJam YpI pIY8 JpY JpsY YvI vIY+ JvY JvgY vI", +"Y5 Y>I Y5Jv Y>J YQvI Yv+QI YZJv gYZJv pIYv pIY> JvYp yJY bX bX& bXD cX bX. ", +"b8X b.DX cX8 bXK bKX& bTX cXT bXj 8Xbj DXbj scX bX( b+X b(DX cX+ X(b. +Xb8 ", +"bXD(. +Xc8 X(bK bK+X b(TX +TcX b(Xj +Xbj bTXj( s+cX bGX G&bX JbX cJX GXb. G", +"8bX bXJ. J8cX GXbK I&KGX bTJX JTcX GjbX I&jGX bXJj cJsX bdX +GbX bdJX gcX b", +".dX 8dbX JbdX. g8cX bKdX dX&bK TdbX gTcX dXbj IdjX& JjbdX scgX bX2 b2X& b2D", +"X cX2 b5X 5Xb8 5DbX c5X X2bK I&2XK b2TX TXc2 5Xbj I&j5X 5TbX c5sX bX; +Xb; ", +"DXb; cX; 5Xb; >Xb b5XD; c>X bKX; I;&XK TXb; TXc; X;bj bX>j X;jbT sc>X GXb2 ", +"I&2GX bXJ2 JXc2 G5bX bG58X b5JX J5cX bGX2K bG2X&K JbTX2 cJTX2 bG5Xj bG5X&j ", +"J5bTX sJc5X G;bX I;&dX bXJ; c;gX 5dbX bG>X J5bdX gc>X bG;XK IdKX;& J;bTX cJ", +";TX dXjb5 >XbGj J;jbX J>scX bXP IP& DXbP cXP b.IP 8IbP IP.bD 8IcP bQX Q&bX ", +"QDbX cQX QXbj Q8bX IPjbT cQsX b(IP +IbP IP(bD +IcP IP(b. IP&+8 DX(bP. cX+8P", +" b(QX +QbX bQXD( +QcX IPjb( IP&+j Ij(bTP sIPc+ Yb Yb& YJb Yc Yb. Yb8 JbY. Y", +"c8 YbQ bQY& YZb YcZ Ybj bQY8 ZbYj sYc Ybd Yb+ JbYd Ycg bdY. b+Y8 YJbd. Y8gc", +" bQYd b+YQ YdZb ZcgY bdYj b+Yj YZbdj gsYc bXm XmI& DXbm cXm 5IbP 8Xbm b5XDm", +" 5IcP QXbm Im&QX TXbm QXcm pbX b8pX bTpX cpX I;bP +Xbm I;PbD +Xcm I;Pb5 bX>", +"P 5DXb;P cX>P QXb; I;PQ& I;PbT QXc; bXp; pb>X pbXD; >Xcp Ybm bmY& JmYb Ycm ", +"Yb5 b5Y8 J5Yb Yc5 bQYm YbQm& YmZb cmYZ Ypb pbY8 YbJp Ycp Yvb vbY+ YbJv Ycv ", +"vbY5 Y>b YJ5vb Yc> YQvb Yvb+Q ZvYb YZcv pvYb p>Yb JpYvb yYc X0 X0! X0B aX0 ", +"1X 1X8 1XB 1aX X0K !KX0 TX0 T0aX 1Xj 18Xj 1TX sX1 X0( +X0 0BX( +0aX 1X( 1+X", +" X(1B +X1a 0(XK X0+K X0T( +0TX 1(Xj +j1X 1(TX 1+sX GX0 X0G! HX0 G0aX 1GX G8", +"1X 1HX Ga1X X0GK GX0!K G0TX aX0GT Gj1X 1G8Xj GT1X 1GsX dX0 +0dX H0dX gX0 1d", +"X +G1X HX1d g1X X0dK dX0+K T0dX TXg0 dj1X dXj1+ Td1X sXg1 X02 !0X2 2BX0 X2a", +"0 1X5 158X 1B5X 5a1X 2KX0 X02!K X0T2 aX0T2 15Xj 1X58j 5T1X 1Xs5 X;0 +0X; ;0", +"XB a0X; 1X; >X1 1BX; 1a>X ;0XK X;0+K T0X; aX;T0 Xj1; 1X>j TX1; s1>X X0G2 GX", +"0!2 X0H2 aX0G2 G51X 1G58X H51X 1Ga5X GX02K X02GK! GTX02 GT0aX2 1G5Xj G581Xj", +" 1GT5X sX1G5 G0X; dX0+; H0X; X;g0 G;1X 1G>X HX1; g1>X dX0;K +G0X;K GT;X0 gX", +"T;0 dXj1; >X1Gj X;j1H >Xgs1 XP0 X!P0 P0XB XPa0 1XP 8X1P XP1B aX1P QX0 X0Q! ", +"Q0TX Q0aX 1QX Q81X QX1T 1QsX P0X( XP+0 XP0(B aXP+0 XP1( +X1P 1XP(B 1+XaP X0", +"Q( +0QX QXT0( +QXT0 1(QX +Q1X 1QXT( sX1+Q Y0 Y0! YH0 Ya0 Y1 Y18 Y1H Y1a YQ0", +" Q!Y0 YZ0 ZaY0 Y1Q 1QY8 YZ1 sY1 Yd0 Y+0 HdY0 gY0 Y1d Y1+ 1HYd gY1 QdY0 +QY0", +" ZdY0 g0YZ 1QYd 1+Yj YdZ1 g1sY Xm0 X!m0 m0XB a0Xm 1Xm 8X1m 1BXm aX1m Q0Xm X", +"m0Q! T0Xm aXmQ0 pX1 1Xp8 1TpX s1pX X0;P +0Xm Xm;0B aXm+0 Xm1; 1X>P 1Xm;B >X", +"1am Q0X; +QXm0 QX;T0 aX;Q0 1Xp; p1>X pX1T; >Xps1 Ym0 m0Y! HmY0 amY0 Y15 1mY", +"8 1HY5 1aY5 QmY0 YQm!0 ZmY0 YZam0 Yp1 p1Y8 ZpY1 Y1sp Yv0 v+Y0 vHY0 g0Yv Yv1", +" Y>1 YHv1 g1Y> vQY0 Yv+Q0 Y0Zv gYZv0 pvY1 p>Y1 ZvY1 yY1 9X 9X& 9XB 9aX 9X1 ", +":X 1B9X :Xa 9XK 9KX& 9TX TX9a 9Xj :Xj 1T9X s:X 9X( 9+X X(9B +X9a 9(1X :X+ 9", +"X1(B +X:a X(9K 9K+X 9(TX +T9X 9(Xj +X:j 9TX1( s+:X 9GX G&9X 9HX Ga9X 1G9X :", +"GX 1H9X :HX GX9K X&K9G GT9X 9GTaX Gj9X Gj:X HX9j :GsX 9dX +G9X HX9d g9X 1d9", +"X :dX 9HX1d g:X 9KdX dX&9K Td9X 9TgX dX9j dX:j dXj9H s:gX 9X2 92X& X29B 92a", +"X 95X :X5 9B5X 5a:X X29K X&29K 92TX 9TX&2 5X9j 5X:j 5T9X s5:X 9X; +X9; 9BX;", +" aX9; 1X9; >X: 95X;B :a>X 9KX; X;&9K TX9; aX;9T X;9j >j:X X;j9T s:>X GX92 X", +"&29G 92HX 9GaX2 G59X G5:X H59X H5:X 9GX2K 9G2X&K 9GTX2 aX29GT 9G5Xj :G5Xj 9", +"G5TX :HXs5 G;9X dX&9; HX9; 9Xg; 5d9X :G>X 9H5dX >Xg: 9G;XK d&K9X; 9GTX; g9X", +"T; dXj95 :>GXj X;j9H g:Xs> 9XP X&9P XP9B aX9P 1X9P :XP 9X1PB aX:P 9QX Q&9X ", +"QX9T Qa9X 1Q9X :QX 9QX1T :QsX XP9( +X9P 9XP(B 9+XaP 9X1P( +X:P 1X(9PB :X+aP", +" 9(QX +Q9X 9QXT( 9+QTX 9QX1( +Q:X Xj(9TP :QXs+ Y9 Y9& Y9H Y9a Y91 Y: 9HY1 Y", +":H Y9Q 9QY& YZ9 Z9Ya Y9j Y:Q Z9Y1 sY: Y9d Y9+ 9HYd gY9 9dY1 Y:+ Y91Hd Y:g 9", +"QYd 9+YQ YdZ9 YZg9 9dYj :QY+ YZ91d gYZ: 9Xm X&9m 9BXm aX9m 1X9m :Xm 95XmB a", +"X:m QX9m Xm&9Q TX9m aXm9Q p9X :pX 9TpX sX:p Xm9; +X9m 9Xm;B aXm9+ 95X;P :X>", +"P X;B95P :Xm>a QX9; X;&9Q 9QXT; aX;9Q 9Xp; >X:p p9XT; :pXs> Y9m 9mY& 9HYm 9", +"aYm Y95 Y:5 9HY5 :HY5 9QYm Y9Qm& YmZ9 YZ9am Yp9 Y:p ZpY9 YpZ: Yv9 v9Y+ YHv9", +" Yvg9 v9Y1 Y:v Yv91H gY:v YQv9 Yv9+Q ZvY9 gY9Zv pvY9 Yp:v Yp9Zv yY: IX0 I!X", +"0 DX0 D0aX 1IX 8I1X 1DX D81X X0IK IX0!K D0TX aX0TI Ij1X IjX18 DX1T 1DsX X0I", +"( IX+0 X0D( +0DX IX1( +I1X 1(DX +D1X IX0(K +IX0K DXT0( +DXT0 IjX1( 1+IXj 1D", +"XT( sI1+X IXG0 GIX!0 JX0 aXJ0 GI1X 1G8IX J1X 1aJX GIX0K IX0GK! TXJ0 JTaX0 I", +"jX1G 1G8IjX 1TJX J1sX I0dX IdX+0 dXJ0 JXg0 Id1X 1+GIX 1dJX J1gX IdX0K +I0dX", +"K JTdX0 gJTX0 Idj1X 1+IdXj J1TdX sJg1X X0I2 IX0!2 X0D2 aX0I2 5I1X 1I58X 5D1", +"X 1DX5a IX02K X02IK! DXT02 TI0aX2 IjX15 5I81Xj 1DX5T sI15X I0X; I;X+0 D0X; ", +"aX;D0 I;1X 1I>X DX1; 1D>X I;X0K +I0X;K DX;T0 aX0TI; I;j1X >I1Xj X;j1D >Xs1D", +" GIX02 IX0G2! X0J2 JaX02 1G5IX G581IX 1XJ5 J158X IX0G2K I!X2G0K JTX02 aX0JT", +"2 1G5IjX Ij1XG58 J15TX sJ15X IdX;0 +GIX;0 X;J0 gJX;0 1G;IX >I1dX 1XJ; J1>X ", +"dX0I;K Id+X;0K J;TX0 gXTJ;0 1IdX;j Idj>X1 J1;TX J>s1X IP0 I!P0 D0IP a0IP 1I", +"P 8I1P DX1P aI1P Q0IP IP0Q! Q0DX QDXa0 QI1X IPj18 QD1X 1IsP P0I( +0IP IP0D(", +" +DXP0 1(IP +I1P 1DXP( 1+DIP IP0Q( +QIX0 QDX0( +QDX0 IPj1( IPj1+ 1QDX( sIP1", +"+ YI0 I0Y! YJ0 JaY0 Y1I 1IY8 YJ1 J1Y8 QIY0 YQI!0 Y0ZJ YZJa0 1QYI Y1Q8I ZJY1", +" Y1sJ IdY0 +IY0 JdY0 g0YJ 1IYd 1+YI YdJ1 YJg1 YQId0 Y+QI0 YZJd0 gYZJ0 Y1QId", +" Y1+QI YZ1Jd gY1sJ I0Xm ImX!0 D0Xm aXmD0 5I1P 1Im8X DX1m aXm1D ImXQ0 QI0Xm!", +" QDXm0 aX0TIm 1IpX pI81X 1DpX sIPp1 I0;P I;P+0 I;PD0 a;PI0 I;1P 1I>P I;P1D ", +">XD1m I;PQ0 Im0+QX I;PT0 aX0QD; pI1X; >Xp1I pDX1; sIP>1 ImY0 YIm!0 Y0Jm YJa", +"m0 1IY5 Y158I J1Y5 YJ15a YQIm0 Im0YQ! YZJm0 ZJmYa0 pIY1 Yp18I Y1Jp JpsY1 Y0", +"vI Yv+I0 Y0Jv gYJv0 vIY1 >IY1 Y1Jv Y1J> YvQI0 vIQY+0 YZvJ0 JvZgY0 Yv1pI Y>p", +"1I JpYv1 YJy1 bX9 I&9X 9DX cX9 b1X :IX 1DbX :cX bK9X I&K9X 9TbX 9TcX 9Xbj b", +"X:j 1TbX :csX b(9X 9+bX 9(DX 9+cX b(1X b+:X b1XD( :+cX bX9(K b+X9K bTX9( cX", +"9+T b1Xj( :I+Xj b1TX( :cXs+ 9GbX bG9X& J9X J9cX 1GbX bG:X b1JX :JX bG9XK bG", +"9X&K 9TJX cJ9TX b1GXj :GbXj 9XJj sX:J 9dbX dX&9I 9dJX cXg9 1dbX bd:X J91dX ", +"gX:J bdX9K IdK9+X J9TdX gJ9TX Idj9X :IdXj J9jdX g:XsJ b29X I&29X 92DX 9Xc2 ", +"95bX b5:X 5D9X c5:X bX92K 9I2X&K bTX92 cX9T2 b1X5j :I5Xj b1T5X :cXs5 9Xb; I", +";&9X DX9; 9Xc; 1Xb; :I>X b1XD; :c>X bX;9K I;K9+X bTX9; cX;9T I;j9X :>IXj X;", +"j9D c>Xs: bG9X2 bG9X&2 9XJ2 cJ9X2 b1G5X :Gb5X 95JX J5:X 9G2bXK bK9GX&2 J9TX", +"2 J9TcX2 9G5bXj bG5:Xj J95TX :Js5X bG;9X 9+GbX; 9XJ; cJ;9X b1GX; :>GbX J95d", +"X >X:J IdK9X; X;G&9IK J9;TX cXTJ9; b1GX;j >Ij:dX J9;Xj :J>sX 9IP I&9P DX9P ", +"9IcP 1IbP :IP b1XDP cX:P 9QbX IP&9Q QD9X 9QcX 1QbX bQ:X IPj9D :IsP 9(IP +I9", +"P 9DXP( cX9+P b1XP( +I:P b1DIP( :cX+P bQX9( b+Q9X 9QDX( cQX9+ IPj9( :QI+X I", +"P(b1T sIP:+ Yb9 9IY& YJ9 Yc9 Yb1 Y:b J9Y1 Y:J bQY9 Yb9Q& ZJY9 Y9Zc b1Yj :QY", +"b ZbY1 Z:Yc bdY9 b+Y9 YdJ9 g9Yc b1Yd :IY+ YJ91d gY:J Yb9Qd Yb+9Q YZb9d gY9Z", +"c Yb1Qd Y:b+Q YZb1d sY:gJ 9Xbm Im&9X DX9m 9Xcm 1Xbm bX:m b1XDm cX:m bQX9m 9", +"QIXm& bTX9m cQX9m b1pX pb:X 9DpX :Xcp I;9P I;P9+ I;P9D cXm9+ I;Pb1 :I>P 95D", +"I;P c>X:m I;P9Q 9+QI;P I;P9T cQX9; pbX9; :pI>X p9DX; cpX:> bmY9 Yb9m& JmY9 ", +"cmY9 b1Y5 :IY5 J9Y5 Y5:J Yb9Qm Im&Y9Q YZb9m YcZ9m pbY9 Yb:p Y9Jp cpY: vbY9 ", +"Yvb9+ Y9Jv Y9cv vbY1 Yb:v Jv9Y1 :JY> Yvb9Q Y9QvI& Jv9YZ YcvZ9 Ypbv9 :vIYp J", +"p9Yv Y:yJ RX RX! RXB RaX RX. R8X X.RB 8XRa XM XM! RTX aXM RjX 8XM TXRj sRX ", +"RX( R+X X(RB +XRa X(R. +XR8 RX(.B R+8aX XM( +XM R(TX +XRT R(Xj +XRj XM(Tj R", +"+sX RG RG! RGH RGa RG. RG8 R.HX G8RH RGM R!GM RGT GTRa RGj G8Rj GTRj sRG RG", +"d R+G HXRd gRG R.dX +GR8 dX.RH RGg8 dXM +MRG GTRd gXM GjRd +GRj dXMHj sRgX ", +"RX2 X2R! X2RB R2aX R5X 5XR8 RB5X 5XRa XM2 M!X2 R2TX X2aM 5XM 8X5M 5XRT R5sX", +" R;X +XR; RBX; aXR; 5XR; >XR X;BR5 Ra>X XM; X;+M TXR; XMa; X;Rj >XM XM;5T s", +"R>X RG2 G2R! R2HX R2Ga RG5 G5R8 G5RH G5Ra R2GM XM2G! R2GT aXMG2 G5Rj RG58M ", +"G5RT RGs5 RG; +GR; G;RH RGg; G5Rd >RG RG5H; gR>X GMR; dXM+; GTR; XMg; G;Rj ", +"RG>M dXMH5 gX>M RXP XPR! XPRB RXaP XPR. RX8P RXP.B aXPR8 SX S!X STX SaX SXj", +" S8X TXSj SsX XPR( RX+P RXP(B aXPR+ RXP(. R+8XP XP(R.B R+8aXP SX( S+X TXS( ", +"+TSX XjS( +XS8 STXj( S+sX YR YR! YRH YaR YR. Y8R RHY. RHY8 SY SY! SZY SYa S", +"Yj SY8 YjSZ sYS YRd Y+R RHYd gYR RdY. R+Y8 YRHd. Y8gR SYd SY+ YZSd gSY YjSd", +" Y+S8 SZYdj sYgS RmX R!Xm RBXm aXRm 5XRm 8XRm XmBR5 aXmR5 SXm XmS! TXSm aXS", +"m SpX pXS8 STpX sXSp XmR; +XRm Xm;RB aXmR+ Xm;R5 Rm>X R5X;PB >XRam S;X +XS;", +" TXS; aXS; pXS; S>X SpTX; >XSs YRm RmY! RHYm RaYm Y5R R5Y8 RHY5 R5Ya SYm Ym", +"S! SmYZ SmYa YpS S8Yp SZYp YpSs YvR vRY+ YHvR YvgR vRY5 Y>R YvRH5 gRY> SvY ", +"YvS+ YZSv SvgY YpSv S>Y SZpYv yYS RX# R&X X#RB aXR& X#R. 8XR& RX#.B aX&R8 X", +"M# XM& R#TX TXR& R#Xj X&Rj XM#Tj R&sX X#R( +XR& RX#(B aX&R+ RX#(. R+8X& X#(", +"R.B R+8aX& M#X( X&+M XM#T( aXM+& XM#j( XM&+j RT#Xj( sX+R& RG# RG& R#HX GaR&", +" G#R. G8R& RGH#. RG8H& R#GM GMR& R#GT GTR& R#Gj G&Rj RGTj# RGs& R#dX +GR& d", +"X#RH RGg& dX#R. dX&R8 RH#dX. gRG8& d#XM XMd& dXMH# XMg& dXMj# dXM&j dX#RHj ", +"gsRG& X#R2 R2X& RX#2B aX&R2 R#5X 5XR& R5X#B R5aX& M#X2 X2M& XM#T2 aXM&2 5#X", +"M XM5& R5TX# sX5R& R#X; X;R& X;#RB aX;R& X;#R5 R&>X R5#X;B >XRa& X#M; M;X& ", +"XM;T# aXM;& XM;5# XM>& X;#R5T >XsR& G#R2 R2G& RGH#2 RGa&2 R#G5 G5R& RG5H# R", +"G5H& XM#G2 XM&G2 RGT#2 XM&H2 RG5M# XM&G5 RG5T# sRG5& R#G; G;R& RG;H# gRG;& ", +"RG5d# RG>& dX#RH5 >XgR& dXM;# dXM;& XM;H# gXM;& dXM5# >XMG& dX#R5T >XgM& XP", +"R# RXP& RXP#B aXPR& RXP#. X&PR8 XP#R.B R8aX&P SX# S&X TXS# TXS& XjS# 8XS& S", +"TXj# S&sX RXP#( X&PR+ XP#R(B R+aX&P XP#R(. R+8X&P .BR(XP# aXR&+P8 X#S( +XS&", +" STX#( S+TX& SXj#( S+8X& Xj#ST( Ss+X& YR# Y&R RHY# RHY& R#Y. R8Y& YRH#. Y8R", +"H& SY# SY& S#YZ YZS& S#Yj Y8S& SZYj# S&sY RdY# R+Y& YRHd# Y&gR YRd#. Y+R8& ", +"RHdY#. gYR8& YdS# Y+S& SZYd# S&gY SYdj# SY+&j YZjSd# gSsY& R#Xm XmR& Xm#RB ", +"aXmR& Xm#R5 Xm&R5 R5#XmB R5aXm& XmS# XmS& STXm# SaXm& S#pX pXS& SpTX# Sp&sX", +" Xm;R# Xm&R+ Rm#X;B RamX;& R5X;P# >XRm& 5#XmR;B aXm>R& X;S# X;S& S;TX# Sa;X", +"& SpX;# S&>X pXTS;# S>sX& RmY# RmY& YRHm# YaRm& R5Y# R5Y& Y5RH# Y5aR& YmS# ", +"SmY& SZYm# SZ&Ym S#Yp S&Yp SZpY# Yp&Ss Y#vR vRY& YvRH# gYvR& YvR5# >RY& vRH", +"Y5# Y>gR& S#Yv YvS& YZvS# Sv&gY SvpY# S&Y> YpZSv# SYy& [ [! [D [a [. [8 [D.", +" [8D [M [!M V[ V[a [j [8M V[j [s [( [+ [D( [+D [(. [+8 D([. +D[8 [M( [+M V[", +"( V[+ [j( [+j V([j [s+ [G [!G [J [Ja [G. [8G [J. J8[ [GM GM[! VJ[ [aVJ [jG ", +"G8[j Jj[ [sJ [d [+G [Jd g[ [d. [8d J.[d g[8 [dM +G[M V[d g[V [dj +G[j [dJj ", +"g[s [2 [!2 [D2 [a2 [5 [58 [5D [5a [M2 M2[! V[2 V2[a [5M 5M[8 V[5 [s5 [; [+;", +" [;D [a; [5; [> 5D[; [>D [;M +M[; V[; [+V; [;j [>M [5V; V>[ [G2 G2[! [J2 J2", +"[a [5G G5[8 J5[ [5J8 GM[2 [!GM2 V2[J VJ[a2 G5[j [5G8M V[J5 J5[s [d; +G[; J;", +"[ g[; [5d [>G [5J; g[> GM[; [+GM; V[J; V;g[ G;[j [d>M [5Vd V>g[ [P [!P [DP ", +"[aP [P. [8P DP[. D8[P S[ S[! SV[ S[a S[j S[8 [jSV [sS [P( [+P DP[( +D[P P([", +". +P[8 [DP(. [+D8P S[( S[+ S(V[ V[S+ S([j [+S8 SV[j( S+[s [Y [Y! YJ[ Ya[ [Y", +". Y8[ Y.[J [JY8 S[Y S![Y ] ]a Yj[ Y8S[ ]j ]s [Yd Y+[ [dYJ g[Y Y.[d [+Y8 YJ[", +"d. Y8g[ S[d Y+S[ ]d ]g [dYj [+Yj ]jd ]gs [m [m! [mD [am [5m [8m 5D[m 5a[m S", +"[m S![m V[m [aVm [p [p8 [pV [ps [m; [+m Dm[; +D[m 5P[; [>m [5D;P [a>P S[; [", +"+S; V[S; [+Vm [p; S>[ V;[p [pV> [Ym Ym[! Jm[ [aJm Y5[ [5Y8 [JY5 [5Ya [mSY S", +"[Ym! ]m ]am [pY Y8[p ]p ]ps [v [v+ [vJ g[v [v5 Y>[ J5[v Y>g[ [vS S+[v ]v ]g", +"v [pv [pY> ]pv ]y [b [& [bD [c [b. [8b bD[. [c8 [bM [&M V[b [cV [jb [&j Vb[", +"j [sc [b( [+b bD[( [c+ b([. b+[8 [bD(. c+[8 bM[( b+[M [bV( V+[c bj[( b+[j V", +"[bj( [+sc [bG [&G [Jb [cJ bG[. bG[8 [bJ. J8[c bG[M GM[& JbV[ VJ[c bG[j G&[j", +" Jb[j [csJ [db [d& Jb[d g[c bd[. b8[d [Jbd. [8gc bd[M dM[& Vb[d [cgV bd[j d", +"&[j Jj[bd gs[c [b2 [&2 bD[2 [c2 [5b [5& b5[D [c5 bM[2 M&[2 [bV2 V2[c b5[j 5", +"M[& Vb[5 c5[s [;b [;& b;[D [c; b5[; [>b [5bD; [>c bM[; M;[& Vb[; V[c; b;[j ", +"[&>M V[5b; c>[s bG[2 G&[2 [bJ2 c2[J bG[5 G5[& Jb[5 J5[c [bGM2 [&GM2 VJ[b2 [", +"cJV2 [5bGM [5&GM J5V[b [sJc5 bG[; G;[& Jb[; c;g[ b5[d >b[d J5[bd c>g[ [dbM;", +" [d&M; J;V[b g[Vc; [5dbM [>bGM J;[bj J>V[c [bP [&P bP[D [cP bP[. b8[P [bDP.", +" [8cP S[b S[& V[Sb Sc[ [jSb [8Sb SV[bj [sSc bP[( b+[P [bDP( [+cP [bP(. [+b8", +"P bPD[(. [c+8P [bS( [+Sb SV[b( S+[c S[bj( S[+b8 V[jSb( [scS+ Yb[ Y&[ [JYb Y", +"c[ [bY. [8Yb YJ[b. Y8[c SYb YbS& ]b ]c YjSb YbS8 ]jb ]sc [dYb [+Yb YJ[bd Yc", +"g[ Yb[d. Yb+[8 [JdYb. g[Yc8 YbSd YbS+ ]bd ]gc S[dbj S[8bd bd]j gs]c [mb [m&", +" bm[D [cm b5[m b8[m [5bDm [5cm [mSb [mS& Vb[m cmS[ [pb [p& pbV[ cp[ bm[; b+", +"[m [mbD; [+cm [5b;P [&>P b5P[;D >P[c [;Sb [;S& SV;[b S;[c [;pb Sb[> [pVb; [", +">Sc [mYb [mY& Jb[m Jm[c [5Yb [5Y& YJ5[b Y5[c SmYb S[&Ym ]bm ]cm SbYp Y&[p ]", +"pb ]pc [vb [v& vb[J cv[ [5vb Yb[> [vJb5 [>Yc Sb[v S&[v ]vb ]cv pb[v SbY> pv", +"]b ]yc kX kX! kXB akX R1X 8kX RB1X 1XRa kXM M!kX TkX akTX lX lX8 lXT lsX kX", +"( +kX X(kB ak+X R(1X 1XR+ kX(1B akX1+ k(XM kX+M T(kX Tk+X lX( l+X TXl( sXl+", +" RGk R!Gk HkX GkRa R1G 1GR8 1GRH 1GRa kXGM kXMG! GkRT akXGT lGR RGl8 lHX sR", +"lG dkX +kRG dkHX gkX 1GRd 1+RG dkX1H R1gX kXdM dkX+M dkTX TkgX ldX R+lG HXl", +"d glX kX2 X!k2 X2kB k2aX 5kX 8k5X 5BkX ak5X k2XM kXM!2 T2kX akXT2 lX5 5Xl8 ", +"5TlX l5sX kX; k;+X kBX; kXa; 1XR; >kX kX;1B ak>X XMk; kX;+M kXT; akXT; lX; ", +"l>X TXl; >Xls R2Gk kX2G! H2kX akXG2 1GR5 R1G58 5kHX akXG5 kXMG2 Gk2XM! RGTk", +"2 ak2RGT RGl5 lGR58 H5lX lsGR5 GkR; dkX+; kXH; kXg; 1GR; R1>G dkXH5 gk>X dk", +"XM; +GkXM; dkXT; gkXT; RGl; lG>X HXl; >Xgl kXP X!kP XPkB kXaP kX1P kX8P kXP", +"1B akX1P SkX kXS! TkSX akSX lXS S8lX STlX lXSs XPk( kX+P kXP(B akX+P kXP1( ", +"R1+XP 1X(kPB 1+XakP kXS( +kSX STkX( S+TkX SXl( S+lX SlTX( Ssl+X Yk Yk! YkH ", +"Yak Y1R Y8k R1YH R1Ya SYk S!Yk YZk YkSa lY lY8 lYZ lYs Ykd Y+k HkYd gYk R1Y", +"d R1Y+ Y1RHd Y1gR YkSd YkS+ YdZk SYgk lYd lY+ YZld glY kXm X!km kBXm kXam 1", +"XRm kX8m kXm1B akX1m kXSm kXmS! kXTm SakXm lXp l8pX lTpX splX Xmk; kX+m kXm", +";B akX+m kXm1; kX>P kXB1;P >kXam kXS; S+kXm S;TkX Sa;kX S;lX lXS> lXpT; l>X", +"Ss Ykm kmY! HkYm akYm Y5k 5kY8 H5Yk 5aYk SmYk SYkm! YmZk SYakm lYp lpY8 lZY", +"p lsYp Yvk vkY+ YHvk Yvgk vRY1 Y>k Yv1RH >kgY YkSv SY+vk ZvYk gYkSv lYv lY>", +" YZlv ylY R9X kX& RB9X 9XRa 9XR1 :RX kXB91 Ra:X 9XM XMk& 9XRT aX9M lX9 l:X ", +"9TlX s:lX R(9X 9XR+ kX(9B akX9+ kX(91 R+:X 9X(R1B :R+aX 9(XM +X9M R9TX( aXM", +"9+ 9Xl( :Xl+ lX9T( lsX:+ R9G 9GR& 9GRH 9GRa 9GR1 :RG R9G1H RG:H 9MRG kX&GM ", +"9GRT aXM9G R9lG :RlG 9HlX :RsG 9GRd 9+RG dkX9H R9gX dkX91 R+:G 9HXR1d :RgX ", +"dX9M dkXM& dkX9T 9XgM 9dlX :dlX lHX9d lXg: R29X k2X& kX29B akX92 9XR5 R5:X ", +"R95XB :R5aX 92XM kX&M2 R9TX2 aXM92 95lX l5:X lX95T lsX:5 9XR; X;k& kX;9B ak", +"X9; kX;95 :R>X k;B95X :>RaX XM9; kX;M& kX;9T aXM9; 9Xl; l:>X lX;9T l>Xs: R2", +"9G kX&G2 R9GH2 kX&H2 9GR5 RG:5 R9GH5 :RGH5 R9GM2 kX2GM& R9GT2 aX29GM lGR95 ", +":RGl5 lHX95 :RGs5 9GR; dkX;& kX;9H gR9G; dkX95 >R:G 9G5RH; g:R>X dXM9; kX;d", +"M& XM;9H g9XM; lG;R9 l>G:R lHX9; glX:> kX9P X&kP kXP9B akX9P kXP91 kX:P 9X1", +"kPB :RaXP S9X 9XS& 9TSX 9aSX S9lX S:X Sl9TX sXS: kXP9( kX&+P 9X(kPB 9+XakP ", +"9X1kP( :R+XP kX9P1(B akX:P+ 9XS( 9+SX S9TX( S9+TX Sl9X( :XS+ lXTS9( S:s+X Y", +"9R Yk& R9YH R9Ya R9Y1 Y:R Y9R1H :RYa SY9 Y9S& YZS9 Y9Sa lY9 lY: Y9lZ S:sY R", +"9Yd R9Y+ Y9RHd Y9gR Y9R1d :RY+ R9HY1d gRY: Y9Sd Y9S+ SZ9Yd SYg9 Y9ld l+Y: l", +"YZ9d Y:gl 9XRm Xmk& kXm9B akX9m kXm95 Rm:X kXB95P :RmaX 9XSm S9Xm& S9TXm S9", +"aXm p9lX lX:p Sp9TX :pXSs kXm9; kXm9+ kXB9;P kX&a;P 95Xk;P :>RXm 1;9PkXB :X", +"a>kP 9XS; S9+Xm S9;TX S9aX; Sp9X; >XS: S9TlX; S:>sX R9Ym kmY& Y9RHm Y9aRm R", +"9Y5 :RY5 Y95RH Y:RH5 SmY9 SY9m& SZ9Ym SY9am S9Yp YpS: Yp9lZ lYs:p vRY9 vkY&", +" Yv9RH gY9vR Yv9R1 >kY: Y9HvR1 :vRgY YvS9 Sv9Y+ Sv9YZ gS9Yv Y9lv Y:lv lYvZ9", +" lYy: [k [!k [Dk [ak [1 [18 [1D [1a [kM kM[! V[k Vk[a [l [l8 [lV [sl [k( [+", +"k Dk[( +D[k [1( [1+ 1D[( 1+[a kM[( +k[M [kV( Vk[+ [l( [l+ l(V[ l+[s [Gk Gk[", +"! Jk[ [aJk [1G 1G[8 J1[ [1J8 Gk[M [!GkM V[Jk JkV[a [lG [8lG lJ[ lJ[s [dk +G", +"[k [dJk g[k [1d 1+[d [dJ1 g[1 dk[M [+GkM Vk[d V[gk [ld [+lG V[ld gl[ [k2 k2", +"[! Dk[2 ak[2 [15 5k[8 1D[5 1a[5 kM[2 [!kM2 [kV2 V[ak2 [l5 l5[8 V5[l [5ls [;", +"k +k[; Dk[; ak[; [1; [>1 1D[; [a>k k;[M [+kM; Vk[; V[+k; [l; [>l V[l; l>[s ", +"Gk[2 [!Gk2 [kJ2 Jk[a2 1G[5 [1G58 [1J5 J1[5a [GkM2 GkM[!2 JkV[2 V[aJk2 [5lG ", +"[lG58 J5[l [slJ5 Gk[; [+Gk; [;Jk [;gk 1G[; [d>k [1J; >kg[ [dkM; +Gk[;M Jk;V", +"[ g[Vk; [5ld lG[> J;[l [>gl [kP kP[! Dk[P ak[P [1P 1P[8 1D[P 1a[P S[k [!Sk ", +"SkV[ Sk[a [lS S8[l SV[l [lSs kP[( +k[P [DkP( [+DkP 1P[( 1+[P [1DP( [1+DP [k", +"S( Sk[+ SV[k( SV+[k l(S[ S[l+ SlV[( [slS+ Yk[ [!Yk YJk JkYa Y1[ [1Y8 J1Yk J", +"8Yk YkS[ S[Yk! ]k ]ak lY[ Y8[l ]l ]ls [dYk [+Yk YdJk YJgk [dY1 [1Y+ YJ1[d Y", +"1g[ Sk[d S[+Yk ]dk ]gk S[ld Y+[l ]ld ]lg [mk km[! Dk[m ak[m [1m 1m[8 1D[m 1", +"a[m Sk[m S[km! Vk[m S[akm [pl lp[8 [lVp ls[p km[; +k[m [mDk; [+mDk 1m[; [1>", +"P [1mD; [>1Dm Sk[; S[+km SV;[k S[ak; l;[p l>[p [plV; V>[Sl [mYk Yk[m! JmYk ", +"YJkam [1Y5 Y1[8m J5Yk YJ58k S[Ykm YkmS[! ]km ak]m [plY [plY8 ]lp ls]p [vk [", +"+vk YkJv gk[v [v1 >k[v J1[v g1[v vkS[ [vS+k ]vk gv]k lv[ [>lY ]lv ]yl [t [t", +"& [tD [ct [t1 [: tD[1 [:c [tM tM[& V[t V[tc [lt [:l V[lt u[ [t( t+[ [Dt( t+", +"[c t([1 [:+ [t1D( [+:c [Mt( tM[+ t(V[ V[t+ l([t l+[: [lVt( u[+ tG[ [&tG tJ[", +" [ctJ [1tG [:G [tJ1 :J[ [GtM tG[M& V[tJ tJV[c tG[l lG[: [ltJ uJ[ td[ [+tG [", +"Jtd g[t [1td [:d td[J1 g[: tM[d t+[GM V[td V[gt td[l ld[: lJ[td ug[ [t2 t2[", +"& [Dt2 c2[t t5[ [:5 tD[5 t5[c [Mt2 [tM&2 V2[t [cVt2 t5[l [5l: V[t5 u[5 t;[ ", +"[+t; tD[; t;[c [1t; t>[ t5[D; c>[: tM[; t+[M; V[t; [c;Vt t;[l l>[: [l;Vt u[", +"> [Gt2 tG[&2 t2[J tJ[c2 [5tG [5:G [Jt5 J5[: tG[M2 [&MtG2 tJV[2 [cVtJ2 tG5[l", +" [:lG5 lJ[t5 J5u[ [dt; t+[G; [Jt; t;g[ [5td tG[> tJ5[d t>g[ tG;[M tGM[;& tJ", +";V[ g[tV; [ldt5 [:dl> lJ;[t g[u> tP[ [&tP [DtP tP[c [1tP [:P tP[1D :P[c St[", +" [tS& V[St [cSt [lSt S:[ SlV[t u[S [Pt( [+tP tP[D( tcP[+ tP[1( [+:P [1DtP( ", +"[:c+P S([t t+S[ StV[( St+[c Stl[( S+[: [lVSt( S[u+ tY tY& tYJ tYc tY1 tY: Y", +"1tJ :JtY tYS S&tY ]t ]tc lYt tYS: ]lt ]u tYd tY+ YJtd gtY Y1td t+Y: tYJ1d Y", +":g[ SdtY S+tY ]td ]gt ldtY l+tY lt]d ]ug tm[ [&tm tD[m tm[c [1tm [:m t5[Dm ", +":m[c tmS[ S[&tm V[tm [cmSt tp[ [p: V[tp up[ [mt; [+tm tm[D; [cmt+ t5[;P >P[", +": tmD[1; [:mc> t;S[ St+[m St;V[ Sc;[t t;[p [pt> tp[V; S>u[ tYm Y&tm YJtm tm", +"Yc tY5 t5Y: YJt5 t5Yc tmSY tYSm& ]tm tc]m tpY Y:tp ]pt ]up tYv t+[v tJ[v tv", +"g[ t5[v tY> [v1tJ Yct> St[v [v&St ]tv gt]v tYlv tYS> lv]t ]yu Xe Xe! XeB aX", +"e Xe. 8Xe e.XB aX8e XeK X!eK TXe aXTe Xje Xj8e XjTe sXe X* +Xe X*B aX* X*. ", +"8X* X.*B aX8* X*K +KX* TX* TX+e X*j X*+j XjT* sX* GXe XeG! HXe aXHe XeG. GX", +"8e XeH. 8XHe XeGK GXe!K TXHe aXeGT GXej XjeG8 XjHe HXse fX f+X fHX gfX fX. ", +"f8X HXf. f8gX fXK +XfK fTX fTgX fjX +Xfj HXfj sfX Xe2 X!e2 e2XB X2ae 5Xe 8X", +"5e Xe5B aX5e e2XK Xe2!K XeT2 aXeT2 Xj5e Xje58 TX5e 5Xse X;e X;+e *BX; X;ae ", +"5X* >Xe *B5X aX>e XKe; X;e+K X;Te aX;Te Xj5* X*>j TX5* sX>e XeG2 GXe!2 XeH2", +" aXeG2 GX5e G58Xe 5XHe G5aXe GXe2K Xe2GK! GTXe2 GTXae2 XjeG5 G58Xje G5TXe s", +"X5He fX; +Xf; HXf; fXg; f5X f>X H5fX gf>X X;fK f+X;K TXf; fTXg; 5Xfj fj>X 5", +"TfX >Xsf eP eP! ePB aeP eP. 8eP P.eB ae8P QXe Q!eP TeP aXQe ePj 8XQe ejTP s", +"eP X*P +eP *BeP ae+P X.P* 8e+P X*P.B aX*8P QX* QX+e TXQ* Te+P XjQ* 8XQ* X*j", +"TP +esP Ye Ye! YHe Yae Ye. Y8e HeY. H8Ye YQe QeY! YZe ZeYa Yje Q8Ye ZeYj sY", +"e Yf Yf+ YfH gYf Yf. Yf8 fHY. Y8gf YfQ fQY+ ZfY ZfgY Yfj fQY8 YjZf sYf Xme ", +"X!em XBem Xmae 5eP 8e5P 5BeP ae5P XmQe XmeQ! XmTe aXmQe pXe 8Xpe TXpe pXse ", +"Xm* Xm+e *BXm Xma* Xm5* >eP Xm*5B ae>P XmQ* Xm*+Q XmT* aXmQ* pX* pX>e TXp* ", +"se>P Yem emY! HeYm aeYm Y5e 5eY8 H5Ye 5aYe QeYm YQem! YmZe YZaem Ype peY8 Z", +"eYp seYp Yfv fvY+ YHfv Yfgv Yf5 Y>f YHf5 f>gY fQYv Yfv+Q YvZf gYZfv fpY fpY", +"> ZfYp yYf ,X ,X& ,XB ,aX ,X. ,8X X.,B 8X,a ,XK ,KX& ,TX TX,a ,Xj 8X,j TX,j", +" sX, ,X* -X *B,X -Xa ,.X* -X8 X*.,B 8X-a ,KX* -XK T*,X -TX X*,j -Xj X*j,T s", +"-X ,GX G&,X ,HX Ga,X GX,. G8,X ,.HX H8,X GX,K X&K,G GT,X ,GTaX Gj,X X&j,G H", +"X,j ,GsX fX, f-X ,HfX g-X ,Xf. -Xf8 fHX,. g8-X ,XfK fK-X ,TfX -TgX ,Xfj -Xf", +"j fTX,j gXs- ,X2 ,2X& X2,B ,2aX ,5X 5X,8 ,B5X 5a,X X2,K X&2,K ,2TX ,TX&2 5X", +",j X&j,5 5T,X ,5sX ,X; -X; ,BX; aX-; 5X,; >X- ,5X*B -a>X ,KX; X;-K TX,; TX-", +"; X;,j -j>X X;j,T >Xs- GX,2 X&2,G ,2HX ,GaX2 G5,X ,G58X H5,X ,G5aX ,GX2K ,G", +"2X&K ,GTX2 aX2,GT ,G5Xj ,G5X&j ,G5TX sX,G5 ,Xf; f;-X HX,; g;-X ,5fX f->X f5", +"H,X g->X fX;,K f-X;K fTX,; f-TX; f5X,j f>-Xj f5T,X s-Xf> ,XP eP& ,BeP aX,P ", +",.eP 8X,P eP.,B aeP,8 ,QX Q&,X QX,T Qa,X QX,j Q8,X ePj,T ,QsX X*,P -XP X*P,", +"B aX-P X*P,. 8X-P ,X.P*B -X8aP Q*,X -QX ,QXT* Qa-X X*j,Q Q8-X ,TXP*j -QsX Y", +", Y&e Y,H Ya, Y,. Y8, ,HY. ,HY8 Y,Q ,QY& YZ, ZeY& Yj, ,QY8 Z,Yj sY, Yf, Y- ", +"fHY, Y-g f,Y. Y-8 YfH,. Y8g- fQY, Y-Q Y,Zf Y-Z fjY, Y-j YZ,fj sY- ,Xm X&,m ", +",BXm aX,m 5X,m 8X,m ,5XmB aXm,5 QX,m Xm&,Q TX,m aXm,Q pX, ,8pX ,TpX pXs, Xm", +",; -Xm Xm*,B aX-m Xm*,5 -X>P X;B,5P >X-am QX,; QX-m Xm*,T TX-m ,Xp* -pX pX*", +",T sX-p Y,m ,mY& ,HYm ,aYm Y5, ,5Y8 ,HY5 ,5Ya ,QYm Y&Q,m YmZ, YZ&,m Yp, peY", +"& ZpY, s,Yp Yv, Y-v YHv, -vgY f5Y, Y-> Yf5,H g-Y> YQv, -QYv ZvY, YZ-v Y,fp ", +"Y-p fpZY, yY- IXe I!Xe DXe aXDe XeI. IX8e XeD. 8XDe XeIK IXe!K TXDe aXeTI I", +"Xej IjX8e XjDe DXse I*X I*+X DX* DX+e I.X* I*8X D.X* 8XD* IKX* I*X+K TXD* a", +"X*TI X*Ij I*j+X X*Dj DXs* IXGe GIXe! JXe aXJe GIXe. G8IXe XeJ. 8XJe GIXeK I", +"XeGK! TXJe JTaXe IjXGe G8IXje XjJe JXse fIX +IfX JfX JfgX IXf. 8IfX fXJ. JX", +"f8 IXfK f+IXK JXfT JfTgX IjfX f8IXj JXfj JfsX XeI2 IXe!2 XeD2 aXeI2 IX5e 5I", +"8Xe DX5e 5DX8e IXe2K Xe2IK! DXTe2 DXTae2 IjX5e 5I8Xje 5DXTe sI5Xe X;I* I;X+", +"e X;De aX;De I*5X I*>X DX5* DX>e I;XeK +IXe;K DX;Te DX;+Te I;jX* >IjX* X;jD", +"e sI*>X GIXe2 IXeG2! XeJ2 JaXe2 G5IXe 5IXG8e 5XJe J58Xe IXeG2K I!X2GeK JTXe", +"2 aXeJT2 G5IXje 8eGj5IX J5TXe sJ5Xe I;fX f+IX; fXJ; JfXg; 5IfX fI>X JXf5 Jf", +">X fIX;K I;Xf+K JfTX; fTXgJ; f5IXj f>IXj Jf5TX J>sfX IPe I!eP DeP aeDP I.eP", +" IP8e D.eP 8eDP IPQe IPeQ! DXQe aePQD ePIj IPj8e ePDj DesP IP* IP+e IPD* De", +"+P I.P* IP8* IP*D. aP*8I IPQ* IP*+Q DXQ* aX*QD P*Ij IP*+j IP*Dj IPs* YIe Ie", +"Y! YJe JaYe IeY. 8IYe JeY. YeJ8 QIYe YQIe! ZeYJ YZJae IjYe Y8QIe YeJj YJse ", +"YfI f+YI YJf YJgf fIY. YIf8 Y.Jf JfY8 YIfQ Yf+QI YJZf gYZJf YIfj Yf8QI JfYj", +" YJsf emIP ImXe! XmDe aXmDe IP5e 5IP8e De5P aeP5D ImXQe QIXem! QDXem QDXaem", +" IPpe pI8eP DXpe sIPpe XmI* Im*+X XmD* aXmD* IP5* IP>e IP*5D De>P Im*QX +QI", +"Xm* Im*TX +QDXm* IPp* >XpI* DXp* sIP>e ImYe YIem! YeJm YJaem 5IYe Y58Ie YeJ", +"5 YJ58e YQIem ImeYQ! YZJem ZJeYam YIpe Yp8Ie peYJ JpsYe vIYf Yfv+I YfJv gYJ", +"fv YIf5 >IYf JfY5 YJf> YfvQI fvQY+I YZvJf ZfJgYv pIYf fpY>I YJfp YJyf hX hX", +"& hDX hcX hX. h8X DXh. cXh8 hXK X&hK hTX cXhT hjX 8Xhj DXhj hsX hX* h-X DXh", +"* c-X X*h. -Xh8 hDX*. c8-X X*hK hK-X TXh* -TcX X*hj -Xhj hTX*j sXh- hGX G&h", +"X hJX cXhJ GXh. G8hX JXh. hXJ8 GXhK hGX&K JThX cJThX GjhX h8GXj hXJj sXhJ h", +"fX -Xhf hXJf iX fXh. hXf8 JfXh. i8X fXhK f-XhK hXfT iTX hXfj f-jhX JfjhX is", +"X hX2 X&h2 DXh2 h2cX h5X 5Xh8 5DhX cXh5 X2hK hX&2K TXh2 cXTh2 5Xhj h58Xj 5T", +"hX sXh5 h;X -Xh; DXh; cXh; 5Xh; h>X h5DX; hc>X X;hK -X;hK TXh; c-XT; X;hj h", +"j>X h5TX; >Xhs GXh2 hGX&2 JXh2 cJXh2 G5hX h5G8X hXJ5 cJ5hX hGX2K X&2hGK hJT", +"X2 hJTcX2 h5GXj X&jh5G hJ5TX hsJ5X fXh; f-Xh; hXJ; iX; hXf5 hf>X Jf5hX i>X ", +"hfX;K hf;-XK hJ;TX TXi; hf5Xj h>fXj J;jhX >Xis hP hP& hPD hcP hP. h8P DPh. ", +"cPh8 hQX QXh& hTP cQhP hPj Q8hP DPhj hsP hP* h-P DPh* c-hP P*h. -Ph8 hPD*. ", +"c-X8P QXh* -QhP TPh* -QcX P*hj -Phj hTP*j hPs- hY hY& hYJ Ych hY. hY8 h.YJ ", +"h8Yc hYQ Y&hQ hZY YchZ hYj Y8hQ YZhj hsY hYf Y-h YJhf iY h.Yf h8Y- hYJf. iY", +"8 YfhQ hQY- ZfhY iZY Yfhj hjY- hYjZf isY hmX Xmh& DXhm cXhm h5P 5Ph8 5DhP c", +"5hP QXhm hQXm& TXhm cQXhm hpX pXh8 pDhP hPcp h;P -Xhm D;hP c;hP 5Ph; h>P h5", +"PD; hc>P QXh; -QXhm T;hP c-QXm pXh; >Xhp hpDX; >Phs hYm Y&hm YJhm hmYc hY5 ", +"Y5h8 YJh5 h5Yc YQhm hYQm& YZhm hZYcm hpY h8Yp hYJp sYhp hvY Y-hv JvhY iYv Y", +"fh5 h>Y hY5Jv iY> YvhQ hvY-Q YZhv YZiv hYfp Y-hp JpfhY yiY Xe0 X!e0 e0XB Xe", +"a0 1Xe 8X1e Xe1B aX1e e0XK Xe0!K XeT0 aXeT0 Xj1e Xje18 TX1e 1Xse X*0 +0X* X", +"0*B a0X* 1X* +X1e *B1X aX1* *0XK X*0+K T0X* aX*T0 Xj1* X*j1+ TX1* 1Xs* XeG0", +" GXe!0 XeH0 aXeG0 GX1e 1G8Xe HX1e 1GaXe GXe0K Xe0GK! GTXe0 GTXae0 Xje1G 1G8", +"Xje 1GTXe sX1He fX0 +Xf0 HXf0 fXg0 f1X 1+fX 1HfX f1gX X0fK f+X0K TXf0 fTXg0", +" 1Xfj f1+Xj 1TfX f1sX e0X2 Xe0!2 Xe02B aXe02 5X1e 1X58e 1X5eB 1aX5e Xe02K e", +"02XK! TXe02 TX0ae2 Xje15 5X81ej 1TX5e sX15e X0e; X;e+0 X;e0B aX;e0 5X1* 1X>", +"e 1X;*B >X1ae X;e0K +X0e;K TX;e0 aX0Te; X;j1e >X1ej 1TX5* sX*>1 GXe02 Xe0G2", +"! HXe02 HX0ae2 1G5Xe G581Xe 1HX5e H5X1ae Xe0G2K 2Ke!GX0 TX0He2 aXHeT02 1G5X", +"je 8eGj1X5 H5X1Te 1HXse5 X;f0 f+X;0 fHX;0 gfX;0 1Xf5 f1>X f1H5X f>g1X fX;0K", +" X;0f+K fTX;0 gX;fT0 f15Xj f>1Xj f1T5X sf>1X eP0 P!e0 P0eB a0eP 1eP 8e1P 1B", +"eP ae1P Q0eP eP0Q! T0eP aePQ0 QX1e ePj18 Te1P 1esP X0P* +0eP X*P0B aX*P0 X*", +"1P +e1P 1X*PB aX*1P Q0X* +QXe0 QX*T0 aX*Q0 QX1* ePj1+ 1QXT* sX*1Q Ye0 e0Y! ", +"HeY0 aeY0 Y1e 1eY8 1HYe 1aYe QeY0 YQe!0 Y0Ze YZae0 1QYe Y1Q8e ZeY1 Y1se Yf0", +" f+Y0 fHY0 g0Yf Yf1 f1Y+ YHf1 Yfg1 Y0fQ Yf+Q0 Y0Zf gYZf0 f1Yj Yf1+Q Y1Zf Y1", +"sf X0em Xme!0 Xme0B aXme0 5e1P 1Xm8e 1XmeB aXm1e XmeQ0 QX0em! TXme0 aX0Tem ", +"1Xpe pX18e pX1Te sePp1 X0m* Xm*+0 Xm*0B aXm*0 Xm1* 1e>P Xm*1B >eP1a Xm*Q0 X", +"m0+Qe Xm*T0 aX0Qe; 1Xp* >Xp1e pX*1T sX*p1 emY0 Yem!0 YHem0 Yaem0 1eY5 Y158e", +" Y1H5e Y1a5e YQem0 em0YQ! YZem0 ZemYa0 peY1 Yp18e YZ1pe sYp1e Y0fv Yfv+0 Yf", +"vH0 gYfv0 fvY1 Y1f> Yf1vH gY1f> YfvQ0 fvQY+0 YZvf0 ZfvgY0 Y1fp fpY>1 fpZY1 ", +"Yfy1 ,X9 X&9e ,B9X 9a,X ,1X :Xe ,B1X ,a:X ,K9X X&K9e 9T,X ,TX9a 9X,j ,X:j 1", +"T,X :Xse 9X* -X9 *B9X 9a-X 1X9* :-X ,1X*B :a-X 9KX* 9X-K TX9* 9T-X X*9j -j:", +"X X*j9T :-sX 9G,X ,G9X& 9H,X ,Ga9X 1G,X ,G:X 1H,X ,H:X ,G9XK ,G9X&K ,GT9X 9", +"GT,aX ,1GXj :GX,j ,1GTX sX,:G f9X -Xf9 9HfX f9gX ,1fX :fX f91HX gX:f 9XfK f", +"-9XK 9TfX f-T9X 9Xfj fj:X f9T1X sX:f ,29X X&29e ,X92B ,aX92 95,X ,5:X ,1X5B", +" :X5,a ,X92K 9X2e&K ,TX92 aX29Te ,1X5j :X5,j ,1T5X sX,:5 9X,; 9X-; ,X;9B -X", +"9a; 1X,; :->X ,1X;B :-X>a ,X;9K -X9;K ,TX9; -TX9; X;j,1 :-X>j ,1TX; s-X:> ,", +"G9X2 ,G9X&2 ,HX92 aX29He ,1G5X :G5,X ,1H5X :HX,5 9G2,XK X&9eG2K ,G29TX ,2GT", +"9aX 9G5,Xj ,G5:Xj 9G5,TX sX5:He 9Xf; f-9X; f9HX; gf9X; 95fX >X:f f95HX g:Xf", +"> f9X;K -X;f9K f9TX; f9;-TX f95Xj :f>Xj f95TX sf:>X 9eP e&9P 9BeP ae9P 1X,P", +" :eP ,1XPB ae:P 9Q,X eP&9Q Te9P aeP9Q 1Q,X ,Q:X ePj9T :esP X*9P 9X-P 9X*PB ", +"-X9aP ,1XP* -X:P X*B,1P :-XaP QX9* 9Q-X 9QXT* -QX9T X*j9Q -Q:X 9TXP*j s-X:Q", +" Y9e 9eY& ,HY9 ,aY9 Y1, Y:e ,1YH :HY, ,QY9 Y9Qe& Z9Y, YZ9,a ,1Yj :QY, Z1Y, ", +"seY: Yf9 Y-9 YHf9 g9Y- f9Y1 Y:f Yf91H gY:f fQY9 -QY9 Y9Zf Y9Z- f9Yj Yj:f Zf", +"9Y1 Y:sf 9X,m Xm&9e ,Xm9B aXm9e 1X,m ,X:m ,1XmB :Xm,a ,QX9m 9QXem& ,TX9m 9Q", +"a,Xm ,1pX pX:e p9X,T :pXse Xm9* 9X-m Xm*9B -Xm9a Xm*,1 :e>P XmB,1; :-Xam Xm", +"*9Q -QX9m Xm*9T -TX9m 9Xp* :X-p p9XT* -pXs: ,mY9 Y9em& Y9H,m Y9a,m ,1Y5 :eY", +"5 Y95,H Y:H,5 Y9Q,m em&Y9Q YZ9,m Y9mZe& p9Y, Y,:p Yp9Ze sY:pe fvY9 Y9-v Yf9", +"vH gY9-v f9Y5 :fY> Yf9H5 Y:gf> Yf9vQ Y-v9Q Zf9Yv Y-Zv9 Y9fp fpY: fp9YZ Y:yf", +" XeI0 IXe!0 XeD0 aXeD0 IX1e 1I8Xe DX1e 1DX8e IXe0K Xe0IK! DXTe0 DXTae0 IjX1", +"e 1I8Xje 1DXTe sI1Xe I0X* I*X+0 D0X* aX*D0 I*1X 1+IX* DX1* aX*1D I*X0K +I0X", +"*K DX*T0 aX0TI* I*j1X 1+IX*j X*j1D sI*1X GIXe0 IXeG0! XeJ0 JaXe0 1GIXe G8I1", +"Xe 1XJe J18Xe IXeG0K I!XKGe0 JTXe0 aXeJT0 1GIXje 8eGj1IX J1TXe sJ1Xe IXf0 f", +"+IX0 fXJ0 JfXg0 1IfX f1+IX JXf1 Jf1gX fIX0K +IXf0K JfTX0 fTXgJ0 f1IXj 1+Ifj", +"X Jf1TX sfJ1X IXe02 Xe0I2! DXe02 DX0ae2 1I5Xe 5I81Xe 1DX5e 5DX1ae Xe0I2K I!", +"X2e0K TX0De2 aXDeT02 1I5Xje 8X5e1Ij 5DX1Te 1DXse5 I;Xe0 +IXe;0 DX;e0 aX0De;", +" 1I;X* >I1X* 1DX5* >XD1e X;0I*K +KI;X*0 I;0TX* X;De+T0 1I;X*j I*j>X1 1DXe;j", +" >I1sX* IXeG02 I!X2Ge0 JXe02 aXeJ02 G5I1Xe 8X5e1GI J15Xe 1aXJ5e IXG0e2K IXG", +"02Ke! TXeJ02 aXJeT02 Ij1XG5e GX8e15Ij 1TXJ5e J15sXe fIX;0 I;Xf+0 JfX;0 gfXJ", +";0 f15IX f>1IX Jf15X J>f1X I;Xf0K I;fX+0K fTXJ;0 X;g0JfT I;jf1X f1I>Xj J1Xf", +"5T sf1J>X I0eP IPe!0 D0eP aePD0 IP1e 1IP8e De1P aeP1D IPeQ0 QI0eP! QDXe0 aX", +"0QDe IPj1e Q8I1eP ePj1D sIP1e I0P* IP*+0 IP*D0 aP*I0 IP1* IP*1+ IP*1D aP*1I", +" IP*Q0 IP0+Qe IP*T0 aX0QD* IP*1Q 1+QIP* IP*1T sIP1* IeY0 YIe!0 JeY0 YJae0 1", +"IYe Y18Ie YeJ1 YJ18e YQIe0 QIeY0! YZJe0 ZJeYa0 Y1QIe 1QIY8e YZ1Je sYJ1e fIY", +"0 Yf+I0 Y0Jf gYJf0 YIf1 Yf1+I JfY1 gY1Jf YfQI0 fQIY+0 YJfZ0 ZfJgY0 Yf1QI Y1", +"IfQ8 Zf1YJ sYfJ1 ImXe0 em0IP! DXme0 aX0Dem 1ImeP 8Im1eP 1DXem 1DXaem QIXem0", +" ImQXe0! Im0TeP aXQeIm0 pI1eP 1IPpe8 pDX1e sX1pDe Im*X0 Im0+eP Xm*D0 aX0Im*", +" Im*1X >eP1I Xm*1D >eD1P Im0QX* Im+XQe0 Im0TX* XmD*+Q0 pI*1X >I1pX* pDX1* >", +"I1seP YIem0 ImeY0! YJem0 JmeYa0 Y15Ie 1ImY8e YJ15e Y1eJ8m ImeYQ0 QmY0Ie! Ze", +"mYJ0 aeJmYZ0 Yp1Ie pI8Y1e JpY1e sY1Jpe YfvI0 fvIY+0 YJfv0 JvfgY0 Yf1vI Y>f1", +"I Jv1Yf Y>Jf1 fQIYv0 YQvIf+0 JvYZf0 gJZfYv0 fpY1I Y>1fpI JpfY1 yJYf1 h9X 9X", +"h& 9DhX h9cX h1X h:X 1DhX :Xhc 9XhK h9X&K 9ThX cX9hT 9Xhj :Xhj 1ThX sXh: 9X", +"h* h9-X DX9* c9-X 1Xh* :Xh- h1DX* c-:X h9X*K -X9hK hT9X* c-X9T h1X*j h:-Xj ", +"h1TX* s-Xh: 9GhX h9GX& hXJ9 cJ9hX 1GhX hG:X hXJ1 :XhJ h9GXK X&Kh9G J9ThX hJ", +"9cXT h1GXj h:GXj J9jhX hsJ:X hXf9 f-9hX JXf9 i9X hXf1 :Xhf Jf91X i:X f9XhK ", +"hf9-XK Jf9TX 9TiX f9jhX :fhXj Jf9Xj s:iX 9Xh2 h9X&2 h9DX2 cX9h2 95hX :Xh5 h", +"15DX h:c5X h9X2K X&2h9K hT9X2 hT9cX2 h15Xj h:5Xj h1T5X hs:5X 9Xh; -X9h; h;9", +"DX c-X9; 1Xh; >Xh: h1;DX c>Xh: h;9XK h;9-XK hT;9X cX9hT; h1;Xj h>:Xj h1TX; ", +"hs>:X h9GX2 X&2h9G J9Xh2 hJ9cX2 h1G5X h:G5X J95hX :Jh5X 9GXh2K X&h29GK hTXJ", +"92 h9cXJT2 9G5hjX h5G:Xj J9Xh5T :J5hsX f9Xh; hf9-X; Jf9X; 9Xi; f95hX h>:fX ", +"Jf95X >Xi: h;Xf9K h9-Xf;K J9XhT; i9;TX f9Xh;j :fXh>j J9Xf5T is:>X hP9 9Ph& ", +"9DhP h9cP h1P h:P 1DhP :chP 9QhP hQ9X& 9ThP cQXh9 9Phj :QhP 1ThP s:hP 9Ph* ", +"h9-P hP9D* c-X9P 1Ph* :-hP h1PD* h:Pc- hQ9X* -QXh9 hTP9* c-Q9X h1QX* h:Q-X ", +"h1TP* hsP:- hY9 h9Y& Y9hJ Y9hc hY1 Y:h YJh1 hY:J Y9hQ hY9Q& Y9hZ hZYc9 Y9hj", +" hQY: YZh1 Y:hZ Y9hf Y9h- JfY9 iY9 Yfh1 hY:f hY1Jf iY: hYf9Q Y-h9Q Zf9hY YZ", +"i9 hY1fQ Y:hfQ hZ1Yf sYi: 9Xhm hm9X& hm9DX cXmh9 95hP :Xhm h1mDX h:mcX hQ9X", +"m Xm&hQ9 hTm9X cX9hTm p9hP hP:p hp9DX cpXh: 9;hP -Xmh9 h;P9D c-X9m 1;hP >Ph", +": h1;DP h>P:c hQ;9X -Q9h;P hT;9P cQ9-Xm hp9X; -pXh: p9DX* cp-:X Y9hm hY9m& ", +"hYJ9m Ych9m Y9h5 h5Y: hY1J5 Y:Jh5 hY9Qm hQ9Y&m hZY9m Yc9hZm h1Yp Y:hp Jp9hY", +" hZ:Yp Y9hv hvY-9 Jv9hY Yvi9 Yvh1 Y:hv hv1YJ Y:iv hvY9Q Y-9hvQ hZvY9 iZYv9 ", +"fp9hY hv:Yp JpfY9 yYi: Re Re! ReB Rae Re. R8e e.RB 8eRa ReM R!Me RTe TeRa R", +"ej 8MRe TeRj sRe Re* R+e *BRe +eRa R.X* +eR8 X*.RB aX*R8 XM* +MRe TXR* +TRe", +" X*Rj +eRj XM*Tj R+se RGe GeR! RHe GaRe GeR. G8Re R.He H8Re GMRe XM!Ge GTRe", +" aXMHe GjRe RG8Me HeRj RGse fR fR+ fRH gfR fR. fR8 RHf. f8gR fRM R+fM fRT f", +"RgM fRj R+fj RHfj sfR Re2 e2R! e2RB R2ae R5e 5eR8 RB5e 5aRe R2Me XM2e! R2Te", +" aXMe2 5MRe R5e8M 5TRe R5se Re; +eR; RBe; aeR; 5XR* >eR R5e*B Ra>e MeR; XM;", +"+e TeR; aXMe; XM5* Re>M XM*5T sR>e GeR2 RGe!2 R2He RGae2 G5Re RG58e H5Re RG", +"5ae XM2Ge RG2Me! RGTe2 aX2HMe RG5Me G5MR8e RG5Te sRG5e fR; R+f; RHf; g;fR f", +"R5 f>R RHf5 gRf> R;fM fR+M; RTf; fRTg; R5fM fR>M R5fT sRf> ReP R!eP RBeP aP", +"Re R.eP 8PRe eP.RB aePR8 Se Se! SeT Sae Sej S8e TeSj Sse ePR* +PRe X*PRB aX", +"*RP X*PR. R+e8P Re.P*B R8eaP* Se* S+e TXS* +TSe X*Sj +eS8 SeT*j S+se YRe Re", +"Y! RHYe RaYe ReY. R8Ye YRHe. Y8RHe SYe YeS! SZe YaSe YjSe Y8Se SjZe seSZ Yf", +"R fRY+ YHfR YfgR Y.fR fRY8 YfRH. gYfR8 Sf Sf+ SfZ gSf Sfj Sf8 fjSZ sfS Rem ", +"R!em RBem aeRm 5eRm 8eRm R5emB aemR5 Sem emS! TeSm aeSm Spe peS8 STpe seSp ", +"XmR* +eRm Xm*RB aXmR* Xm*R5 Re>P XmBR5* >eRam Se; +eS; TeS; aeS; pXS* S>e S", +"peT; >eSs ReYm YRem! HeRm YaRem R5Ye Y5R8e Y5RHe Y5aRe YeSm SYem! SmZe SZea", +"m SeYp SpeY8 ZpSe SseYp fvR f+vR fHvR fvgR fRY5 fRY> Yf5RH f>RgY Sfv fvS+ Z", +"fSv gfSv Sfp Sf> SZfp yfS ,RX Re& RB,X Ra,X ,.Re R8,X ,RX.B ,R8aX ,XM X&,M ", +"RT,X TeR& Re,j 8X,M ,RTXj ,RsX R*,X -RX X*B,R Ra-X X*.,R R8-X ,R.X*B -R8aX ", +"X*,M -XM XM*,T RT-X XM*,j Re-j ,RTX*j -RsX ,RG R&,G RG,H RG,a ,.RG RG,8 ,RG", +"H. ,RGH8 RG,M XM&,G RG,T aXM,G RG,j e&jRG ,RGHj ,RsG fR, f-R ,RfH f-gR ,Rf.", +" -Rf8 fRH,. f-Rg8 ,RfM -RfM ,RfT -RfT ,Rfj -Rfj fRT,j f-sR ,2Re R2e& ,RX2B ", +",RaX2 R5,X 5eR& ,R5XB ,R5aX ,2XM XM&,2 ,RTX2 aXM,2 5X,M XM&,5 ,R5TX sX,R5 R", +"e,; Re-; ,R;XB -RaX; ,R5X; -R>X X;B,R5 >X-Ra XM,; XM-; XM;,T -RTX; XM;,5 -X", +">M ,RTX;j s-R>X ,2RG ,RG&2 ,RGH2 ,RGa2 RG,5 ,RG5& ,RGH5 ,RG5a ,RGM2 X&2,GM ", +",RGT2 aX2,GM ,RG5M ,G5XM& ,RG5T sRG,5 ,Rf; f;-R fRH,; f-Rg; ,Rf5 ->fR fR5,H", +" f>Rg- fRM,; f-RM; fRT,; f-RT; fR5,M f>R-M fR5,T sfR-> Re,P ePR& ePB,R aeP,", +"R eP.,R eP&R8 ,R.ePB ,R8aeP Se, Se& ,TSe ,aSe ,XSj ,8Se SeT,j Ses, X*P,R Re", +"-P ,RXP*B -RaeP ,RXP*. -R8eP ePR*,.B aeP-R8 ,XS* S-X SeT,* -TSe Se*,j -XS8 ", +"X*jS,T sXS- Y,R ,RY& ,RYH ,RYa ,RY. ,RY8 Y,RH. Y8,RH SY, Y&Se YZS, YaS, YjS", +", Y8S, SZe,j SYs, fRY, Y-R YfR,H gRY- YfR,. -RY8 fRHY,. Y-gR8 Sf, Sf- S,Zf ", +"Y-gS fjS, S8Y- SfZ,j S-sY Re,m emR& ,RmXB aXm,R ,R5Xm em&R5 XmB,R5 ,R5aXm ,", +"XSm emS& SeT,m Sae,m pXS, peS& Spe,T Ssep& Xm*,R Re-m XmB,R; -RmaX ,R5Xm* >", +"X-Rm Xm,;R5B >Xa-Rm ,XS; -XS; Se;,T S-TXm Spe,; >XS- S;TpX, -pXSs ,RYm Y&R,", +"m Y,RHm Ya,Rm ,RY5 Y5&,R Y5,RH Y5a,R SmY, SY&,m SZe,m SZem& S,Yp Yp&Se Yp,S", +"Z sY,Sp vRY, fR-v Yv,RH -vRgY Yf5,R -RY> Y5HvR, Y->gR YvS, SvY- Sv,Zf gSf-v", +" S,fp Y-S> SfpZ, Sfy- [e [!e [De [ae [e. [8e De[. D8[e [Me Me[! V[e [aVe [j", +"e 8M[e [jVe [se [* [+e [*D [a* [*. [8* D*[. D8[* [*M +M[* V[* [+Ve [*j +e[j", +" [jV* [s* [Ge Ge[! [Je Je[a Ge[. G8[e [eJ. Je[8 GM[e [!GMe [JVe VJ[ae Gj[e ", +"[8GMe Je[j [Jse [f [f+ [fJ g[f [f. [f8 J.[f J8[f [fM [+fM Vf[ Vfg[ [fj [+fj", +" Jj[f sf[ [e2 e2[! De[2 ae[2 [5e 5e[8 5D[e 5a[e Me[2 [!Me2 [eV2 V[ae2 5M[e ", +"[58Me [5Ve [5se [;e +e[; De[; ae[; [5* [>e 5D[* [a>e Me[; [+Me; [;Ve V[+e; ", +"5M[* [*>M [5V* >e[s Ge[2 [!Ge2 [eJ2 [Jae2 G5[e [5G8e Je[5 J5[8e [GMe2 Me2[!", +"G VJ[e2 [JaVe2 [5GMe G5M[8e J5V[e [sJ5e [f; f+[; J;[f [;gf [f5 [>f J5[f f>g", +"[ [;fM [f+M; V;[f Vfg[; [5fM >M[f V[f5 [>sf [eP eP[! De[P ae[P eP[. 8e[P [D", +"eP. [8DeP S[e [!Se SVe VeSa [jSe [8Se SjVe SVse [*P +e[P DP[* aP[* P*[. 8P[", +"* [*DP. [8*DP S[* [+Se V[S* V+Se [jS* [8S* SVe*j s*S[ [Ye Ye[! Ye[J Ye[a [e", +"Y. Ye[8 YJ[e. YJ8[e [YSe S[Ye! ]e ]ae Ye[j S[8Ye ]je ]se [fY Y+[f YJ[f [fgY", +" Y.[f Y8[f YJf[. g[Yf8 Sf[ S+[f ]f ]gf Yj[f S8[f ]fj ]sf [me em[! De[m ae[m", +" 5e[m 8e[m [5Dem [5aem [mSe S[em! VmSe SVeam [pe [8pe SeVp se[p [m* +e[m Dm", +"[* am[* 5P[* [m>e [5*Dm [>Dem [mS* S[+em VeS; SV+em [p* >e[p V*[p SV>e Ye[m", +" [Yem! Je[m Ya[em Ye[5 Y5[8e YJ5[e Y5[ae S[Yem [YmSe! ]em ae]m [Ype [pY8e ]", +"pe sp]e [vf [+fv [fJv gf[v Y5[f f>[v [vJf5 [>fgY [vSf [vfS+ ]fv gf]v fp[ [>", +"Sf ]pf ]yf [h [h& [hD [ch [h. h8[ [Dh. h8[c hM[ [&hM Vh[ [cVh hj[ [8hM V[hj", +" hs[ [h* [- hD[* [-c h.[* [-8 [hD*. [8c- [*hM [-M [hV* V-[ [*hj [-j Vh[*j [", +"s- [hG hG[& hJ[ hJ[c [Gh. hG[8 h.[J [Jh8 [GhM [hGM& V[hJ VhJ[c hG[j h8[GM [", +"Jhj hJ[s [fh [-f hJ[f i[ h.[f h8[f [fJh. i[8 hM[f fM[- [fVh iV[ hj[f fj[- V", +"f[hj i[s [h2 h2[& [Dh2 h2[c h5[ [5h8 hD[5 h5[c [Mh2 [h&M2 h2V[ Vh[c2 [5hM h", +"5[8M V[h5 h5[s h;[ [-; hD[; h;[c [5h; h>[ h5[D; c>[- [;hM [;-M V[h; V;[- [;", +"hj >M[- Vh5[; [sh> [Gh2 [hG&2 h2[J [cJh2 hG[5 h5[G8 [Jh5 [c5hJ [hGM2 [&GhM2", +" VhJ[2 [cJVh2 h5[GM h5G[&j Vh5[J hs[J5 h;[f [;f- [Jh; i[; h5[f f>[- [f5hJ i", +"[> [fhM; [-fM; Vf[h; V[i; [f5hM [-jf> Vf5[h V>i[ hP[ [&hP [DhP hP[c [Ph. [8", +"hP [hDP. [ch8P Sh Sh& ShV Shc Shj Sh8 SVhj hsS [*hP [-P [hDP* cP[- [h*P. [8", +"-P [*DhP. [-c8P Sh* Sh- S*Vh V-Sh S*hj h8S- ShV*j S-hs hY[ [hY& [JhY [chY h", +".[Y [hY8 hYJ[. hY8[c ShY S&hY ]h ]hc SYhj SYh8 ]hj ]hs [fhY Y-[ hYJ[f i[Y h", +"Y[f. Y8[- [fJhY. Y8i[ Shf Y-Sh ]hf ]i hjSf h8Sf hf]j ]is hm[ [&hm hD[m hm[c", +" [5hm [8hm h5[Dm [c5hm Shm S&hm SVhm hmSc hp[ h8[p VpSh Schp [mh; [-m hm[D;", +" cm[- h5[m* >P[- h5D[m* [-mc> Sh; hmS- SVh; h;Sc h;[p h>S [p*Vh Sch> [Yhm h", +"Y[m& [Jhm [cmhY [hY5 hY5[8 hY5[J Yc5[h SYhm ShYm& ]hm hc]m hY[p [p&hY ]ph h", +"p]s hv[ [-v hJ[v i[v h5[v [-Y> hv[J5 Y>i[ hvS S-hv ]hv ]iv Sfhp Sfh> hp]f ]", +"yi Rek R!ke RBke akRe R1e 1eR8 RB1e 1aRe XMke kXMe! TkRe akXTe lXe R8le RTl", +"e sRle kX* +kRe *BkX kXa* 1XR* 1+Re kX*1B akX1* XMk* kX*+M kXT* akXT* lX* R", +"+le TXl* sXl* GkRe kX!Ge HkRe akXHe 1GRe R1G8e 1HRe R1Gae kXMGe RGkMe! RGTk", +"e RGTake RGle lGR8e RHle lsGRe fRk R+fk RHfk fRgk fR1 R1f8 R1fH fRg1 kXfM f", +"R+kM RTfk fRTgk lfR fRl+ fRlH lXsf R2ke kX2e! kX2eB akXe2 1eR5 R1e58 R1e5B ", +"akX5e kXMe2 XM2ke! RTek2 ak2RTe R5le lX58e lX5Te lsX5e keR; kX;+e kX;*B akX", +"e; 1eR; R1>e kX*5B >kXae kX;Me +kXMe; kX;Te kX;aMe Rel; lX>e lX;Te l>Xse kX", +"2Ge RG2ke! RHek2 ak2RHe R1G5e G5kR8e R1H5e RG5ake RGkMe2 M!kXGe2 kX2HMe akR", +"eGT2 lGR5e RG5le8 lHX5e sX5lHe R;fk fR+k; fRHk; gfRk; R1f5 fR>k fR1H5 f>Rg1", +" fRkM; kX;fM+ fRTk; fT;gkX fRl; >Xlf lfRH5 glf>X keP k!eP kBeP keaP 1PRe ke", +"8P keP1B ake1P Sek keS! TkSe akSe leS S8le lTSe leSs X*kP ke+P kX*PB akXP* ", +"kX*1P kX*8P kXB1P* R1+aeP kXS* +kSe SeTk* S+eTk Sel* S+le lX*ST Ssel+ Yke k", +"eY! HkYe akYe R1Ye 8kYe Y1RHe Y1aRe YkSe SYke! ZkSe SZeak lYe Y8le SZle les", +"Y Yfk fkY+ YHfk Yfgk fRY1 f8Yk Yf1RH gY1fR Sfk fkS+ YkZf gkSf lYf l+Sf ZflY", +" gSlf keRm kXme! kXmeB akXem 1eRm kXm8e keP5B ake5P keSm Sekm! SeTkm Saekm ", +"Sple Spel8 SpelT Sselp Xmk* kXm+e kXm*B akXm* kXm1* ke>P kXB1m* >keaP keS; ", +"S+ekm Se;Tk Saek; Sel; leS> Se;lT S>els keYm Ykem! YkHem Yakem 5kYe Y5k8e Y", +"5kHe Y5ake SYkem YkmSe! SZekm SamZke leYp lYp8e lYZpe lYspe fvYk Yfkv+ Yfkv", +"H gYkfv f5Yk Yf>k Yf5Hk Y>kgf fkSv Sfv+k ZfkSv gSfvk Sflv S>lf lYfZp lYyf ,", +"kX 9eR& RB9e 9aRe R1,X :Re ,R1XB Ra:e 9X,M kX&,M 9TRe akX,T lX, :Rle ,TlX s", +"Xl, 9XR* -kX kX*,B ak-X kX*,1 -R:X k*B,1X :-RaX XM9* 9X-M kX*,T Tk-X ,Xl* l", +"-X lX*,T lXs- R9,G kX&,G 9HRe akX,G R1,G ,R:G ,R1HX RH:e ,RG9M ,GkXM& ,RG9T", +" ,GTakX ,RlG :RGle ,HlX sX,lG fR9 -Rf9 R9fH fRg9 ,Rf1 :fR fR91H gR:f R9fM f", +"-R9M R9fT f-R9T fRl, lX:f lfR,H g-lX ,2kX kX&,2 ,kX2B akX,2 95Re R5:e ,5kXB", +" :Re5a ,kXM2 kX2Me& ,TkX2 ak2,TX ,5lX :Rel5 lX,5T sX,l5 9eR; kX-; kX;,B -kX", +"a; kX;,1 :R>e kXB,1; :>Rae kX;,M -kXM; kX;,T -TkX; ,Xl; >Xl- lX;,T l-Xs> ,R", +"G92 ke2RG& ,HkX2 ak2,RG ,R1G5 :RG,5 ,H5kX :RH5e kX2,GM XMk&,G2 kX2,GT aX9M,", +"G2 lG,R5 :R5lG, lHX,5 sX5lG, R9f; f-R9; fR9H; fR9g; R9f5 :Rf> fR9H5 :fRg> f", +"R9M; f9;-XM fR9T; fR;-Tk lfR,5 lf:>X fR95T gl->X 9PRe ePk& keP,B akX,P keP,", +"1 Re:P kXB,1P :ReaP S9e 9eS& 9TSe 9aSe S9le S:e S9elT seS: kX*,P kX-P kXB9P", +"* -kXaP ,1XkP* :-ReP X*9PR1B :Xa-kP 9XS* -kSe S9eT* S-9TX S9el* lXS- S9TlX*", +" l-SsX Yk, ,kY& ,HYk ,aYk ,RY1 :RY, Y1,RH Y:R,H Y9Se SY9e& Z9Se SZ9ae lY, l", +"eY: YZl, l,sY fRY9 Y-k Yf9RH gkY- Yf9R1 Yk:f Y9HfR1 Y:gfR Sf9 S9Y- S9Zf g9S", +"f l,Sf lY- lYfZ9 sYl- 9eRm kXme& kXm,B akX,m kXm,1 Re:m kXB,1m :Ream 9eSm S", +"9em& S9eTm S9aem p9Se Se:p Sp9Te S:esp kXm,; kX-m kXB,;P -kXam ,R1Xm* :-RXm", +" Xm9*R1B :Ra>eP 9eS; S-9Xm S9eT; S9ae; Sp9X* >eS: S9Tle; -pXls ,kYm Yk&,m Y", +"k,Hm Yak,m ,5Yk Y:R,5 Y5k,H Y5a,k SY9,m SemYk& SZ9em S9mYa, l,Yp lY:pe lY,Z", +"p lYsp, vRf9 Yk-v Yvk,H Y-kgv Yf9R5 >kY- Y9HfR5 :vRgf fvS9 Sf-v9 Sf9Zv gSfv", +"9 S9fp Y-lv fp9SZ lYy- [ke ke[! Dk[e ak[e [1e 1e[8 1D[e 1a[e ke[M [!kMe [kV", +"e V[ake [le [8le V[le le[s [*k +k[* Dk[* ak[* [1* 1+[* 1D[* 1a[* k*[M [+kMe", +" Vk[* V[+ke [l* [+le V[l* l*[s Gk[e [!Gke [kJe Jk[ae 1G[e [1G8e Je[1 J1[8e ", +"[GkMe keM[!G JkV[e V[aJke [Gle [lG8e [Jle [slJe [fk [+fk Jk[f gk[f [f1 [1f8", +" J1[f g1[f [kfM [f+kM fkV[ Vfg[k lf[ l+[f [lVf g[lf ke[2 [!ke2 [Dke2 [ake2 ", +"1e[5 [158e [15De [1a5e [kMe2 keM[!2 V[ke2 [akVe2 [5le [l58e [lV5e [sl5e ke[", +"; [+ke; [;Dke [a;ke 1e[; [1>e [1;De [>1De [;kMe ke;[+M V[;ke Vek[a; [5l* >e", +"[l [l;Ve V>[le [Gke2 ke2[!G Jk[e2 [aeJk2 [1G5e G5k[8e J1[5e J1e[5a keM[G2 k", +"e[!GM2 V[eJk2 akVe[J2 [lG5e [5Gle8 lJ[5e lJ5[se [;fk [f+k; [fJk; g[fk; [1f5", +" >k[f [f1J5 J>k[f [fkM; [+kfM; Vf[k; g[kVf; l;[f [>lf Vf5[l J>l[f ke[P keP[", +"! keDP [akeP 1e[P [18eP [1DeP [1aeP [kSe S[ke! SkVe SVeak S[le S[8le SVle [", +"slSe kP[* [+keP [*DkP [a*kP 1P[* [1+eP [1*DP [1aP* Sk[* S[+ke SVek* SV+ke S", +"[l* [l+Se [l*SV [s*Sl [kYe Yk[e! YeJk YJkae Ye[1 Y1[8e YJ1[e YJ8ke S[Yke Yk", +"[Se! ]ke ak]e [Yle lY[8e ]le ls]e Yk[f Yfk[+ JfYk gYk[f Y1[f [f1Y+ [f1YJ g[", +"1Yf fkS[ Sf[+k ]fk gf]k [lSf lYf[+ ]lf gl]f ke[m [mke! [mDke [amke 1e[m [1m", +"8e [1mDe [1aem S[kem [mkSe! SVekm SakVem le[p [pl8e [plVe [psle km[* [+mke ", +"[m*Dk [amk* 1m[* [>1em [1mD* >kDeP S[;ke S+k[m* SVek; S+kVem l*[p [p*l> [p*", +"Vl V>eSl Yk[em [meYk! YJkem YaeJkm Y1[5e Y1e[8m YJ5ke Y1e[5a Yk[Sem ke[mSY!", +" ke]m ]akem [plYe lY8[pe le]p ]lpse fk[v [vf+k [vJfk g[vfk f1[v [v1f> [v1Jf", +" J>kYf [vfSk Sf+[vk fv]k ]gfvk [plf fp[l> lf]p yl]f ht[ [&ht hD[t ht[c h1[ ", +"[:h hD[1 [ch: [thM ht[M& V[ht Vh[tc lh[ lh[: [lVh u[h t*[ [-t tD[* [ct- [1t", +"* [:- t*[1D :c[- tM[* [t-M V[t* V[t- t*[l l-[ lhV[* u[- [htG tG[h& [Jht tJ[", +"hc hG[1 [h:G [Jh1 hJ[: tG[hM [h&tGM tJV[h Vh[tJc [hlG [:lhG hJ[l hJu[ tf[ [", +"-tf [ftJ i[t h1[f :f[ [f1tJ i[: [tfM [-tfM V[tf V[it [ltf [:lf lJh[f ui[ h2", +"[t ht[&2 ht[D2 [cht2 [ht5 h5[: t5[hD [:hc5 ht[M2 hM&[t2 Vh[t2 [ctVh2 h5[l [", +":lh5 lhV[5 h5u[ [ht; t;[- t;[hD [-tc; [1h; [:h> t5[D* [:-c> t;[hM [-tM; Vh;", +"[t V-[t; h;[l [>l- lh;V[ h>u[ tG[h2 [h&tG2 tJ[h2 [chtJ2 tG5[h [:hG5 tJ5[h :", +"J[h5 [h2tGM hG[&tM2 Vh[tJ2 h2tJ[cV lh[G5 lhG[:5 lJh[5 uh5[J t;[f [-tf; tJ;[", +"f t;i[ t5[f [>tf tf5[J t>i[ tf[M; tfM[-; tfV[; iV[t; lh;[f l-[f> Vf5[t i[u>", +" htP h&tP hDtP tchP tPh1 hP[: htP1D [:hcP Sht S&ht VhSt htSc Shl Sh: SlVh u", +"hS t*hP hPt- tP*hD [-tcP tP*h1 :P[- tPD[1* [:-cP S*ht St[- St*Vh V-[St l*Sh", +" Shl- ShlV* Shu- tYh Y&ht tJhY htYc h1tY hY[: hY1tJ tY:hJ tYSh ShtY& ]ht ht", +"]c lYh Y:Sh ]lh ]uh tYf tY- YJtf itY Y1tf Y:tf tYfJ1 tYi: Sft tYS- ]tf ]it ", +"Shlf tYl- lf]h ui] tmhP tm[h& tm[hD [cmht t5hP hm[: t5PhD [:mhc hmSt Shtm& ", +"VhmSt Shctm tpSh S:hp hplSV hpu[ t;hP tm[- tm[D* [-mtc t5[m* ht>P t5Dh;P [:", +"-cm h;St [-mSt Sh;Vt V-[tm l;Sh S:h> [p*Vt h>uS hmtY tYhm& tJmhY tYchm h5tY", +" [:mhY tY5hJ [:mhJ ShtYm tYmSh& ht]m ]htcm hplY [p:hY hp]l ]puh tYhv Y-tv h", +"v[tJ tYiv h1[v tYh> [v1hJ tYi> tvSh [-vSt hv]t ]tiv hvlY h>lY lv]h ]yui n n", +"! nB an n. 8n n.B 8na nK n!K Tn Tna nj 8nj Tnj sn n( +n n(B +na n(. +n8 .Bn", +"( +8an n(K +nK Tn( +Tn nj( +nj T(nj sn+ nE n!E Hn Hna nE. 8nE Hn. H8n nEK E", +"Kn! HnT HTan njE 8Enj Hnj snH dn +nd Hnd gn dn. 8dn H.dn gn8 dnK +Kdn Tdn g", +"nT dnj dn+j dnHj gsn 4n 4n! 4nB 4an 5n 48n 5nB 5an 4nK n!4K 4Tn Tn4a 4jn 8n", +"4j 5Tn s4n ;n 4+n ;nB a;n 5n; >n 5B;n >na ;nK 4K+n T;n +n4T ;nj >nj T;5n >n", +"s 4nE n!4E 4Hn Hn4a 5nE 4E8n H5n Hn48 nE4K 4nE!K Hn4T 4HTan 4Enj njE48 Hn4j", +" 4Hsn 4dn +n4d H;n g4n 5dn >nd 5dHn >ng 4Kdn dnK4+ T;Hn 4Tgn dn4j dn>j Td5n", +" gs>n Pn n!P PnB aPn Pn. 8Pn n.PB aP8n Qn Qn! QnT Qan Qnj Q8n PjTn sPn Pn( ", +"+Pn n(PB aP+n n(P. 8P+n Pn(.B aPn+8 Qn( +Qn Q(Tn Qa+n Q(nj Q8+n Pj(Tn +Qsn ", +"Wn Wn! HPn Wan Wn. W8n H.Wn W8Hn WnQ Q!Wn Zn Zna Wnj Q8Wn Znj sZn Wnd +Wn d", +"PHn gPn W.dn W8+n dP.Hn W8gn Qdn Wn+Q Znd gZn dnWj Wn+j dnZj gnsZ o o! oB o", +"a o5 o8 o5B o8a oQ o!Q oT oTa op op8 opT os o; o+ o;B o+a o5; o> 5;oB o>a o", +"Q; o+Q oT; o+T op; o>p pTo; os> oW o!W oH oHa o5W o8W oH5 oH8 oWQ Q!oW oZ o", +"Za opW pWo8 oZp osZ ov ov+ ovH go ov5 o>v vHo5 go> ovQ v+oQ oZv goZ opv pvo", +"> opZv yo n# &n n#B an& n#. 8n& .Bn# 8a&n n#K &nK Tn# T&n nj# &nj T#nj sn& ", +"n#( +n& #Bn( +a&n #(n. +8&n n#(.B an&+8 #(nK +K&n n#T( T&+n j#n( &n+j nj#T(", +" +ns& n#E &nE Hn# H&n #En. 8E&n n#H. 8nH& EKn# nK&E H#Tn T&Hn j#nE &Enj H#n", +"j H&sn dn# d&n H#dn gn& n#d. d&8n dn#H. 8ng& n#dK dK&n T#dn T&gn d#nj &ndj ", +"dnjH# g&sn 4n# 4&n n#4B an4& 5n# 5&n n#5B an5& n#4K 4K&n 4#Tn Tn4& 4#nj &n4", +"j 5#Tn 4&sn ;n# ;&n nB;# ;&an 5#;n >n& ;n#5B an>& n#;K ;K&n T#;n ;&Tn ;#nj ", +"&n>j ;njT# >&sn n#4E 4E&n 4#Hn Hn4& n#5E 5E&n H#5n 5&Hn 4n#EK &nE4K 4HTn# 4", +"H&Tn nj#4E &nj4E 4Hjn# snH4& 4#dn dn4& H#;n 4&gn 5#dn d&>n H5nd# >&gn dn#4K", +" d&n4K 4Tdn# g4T&n dnj4# >nd&j ;njH# >ngs& Pn# P&n n#PB P&an n#P. P&8n Pn#.", +"B aP&8n Qn# Q&n Q#Tn T&Qn Q#nj 8nQ& Pj#Tn Q&sn n#P( P&+n Pn#(B aP&+n Pn#(. ", +"P&n+8 n#(P.B +P8an& n#Q( Q&+n QnT#( +QnT& Pj#n( P&j+n TP#nj( sP+&n Wn# W&n ", +"H#Wn W&Hn n#W. 8nW& HPn#. H8P&n W#Qn Q&Wn Zn# Z&n W#nj &nWj njZ# Z&sn W#dn ", +"W&+n dP#Hn W&gn dP#n. dP&8n HP#dn. gP8&n Q#dn d&Qn dnZ# Z&gn dPjn# dP&nj Zn", +"dj# Z&ngs o# o& o#B oa& o5# o8& 5Bo# 5ao& oQ# o&Q oT# oT& op# op& pTo# os& ", +"o;# o+& ;#oB a;o& 5;o# o>& o5;#B >&oa Q;o# +Qo& T;o# +To& p;o# p>o& opT;# o", +"&s> oW# o&W oH# oH& 5Wo# 5Wo& H5o# H5o& WQo# W&oQ oZ# oZ& pWo# oWp& Z#op Z&", +"os ov# ov& vHo# go& v5o# v>o& ovH5# o&g> vQo# oQv& Z#ov Z&go o#pv pvo& oZpv", +"# yo& nC n!C Dn Dna nC. 8nC Dn. D8n nCK CKn! DnT DTan njC 8Cnj Dnj snD nC( ", +"+nC Dn( +Dn C(n. +C8n n(D. D8+n CKn( nC+K D(Tn Dn+T jCn( +Cnj D(nj +Dsn nCE", +" !CnE HnD HDan CEn. nC8E H.Dn D8Hn EKnC n!CEK HDTn HnDTa jCnE njC8E DnHj Hn", +"sD dnC +Cdn Ddn gnD nCd. 8Cdn D.dn D8gn nCdK dnC+K TdDn DngT dCnj dnj+C dnD", +"j gDsn 4nC n!4C 4Dn Dn4a 5nC 4C8n 5Dn D85n nC4K 4nC!K Dn4T 4DTan 4Cnj njC48", +" Dn4j 4Dsn ;nC 4C+n D;n +n4D 5C;n >nC D;5n >nD nC;K ;nC+K T;Dn a;nDT ;Cnj n", +"j>C ;nDj >Dsn nC4E 4nC!E Hn4D 4HDan nC5E 48nCE 5DHn 4H8Dn 4nCEK n!C4EK 4HDT", +"n HnD4Ta njC4E 48CnjE 4HjDn snH4D 4Cdn dnC4+ D;Hn 4Dgn 5Cdn dn>C Dd5n >Dgn ", +"dnC4K 4+CdnK 4DdTn g4DTn dnj4C >ndjC dnj4D >ngsD PnC PCn! DPn aPDn nCP. Pn8", +"C PnD. 8PDn QnC n!QC QDn DnQa QCnj QC8n DjQn QDsn nCP( Pn+C PnD( DP+n PnC(.", +" +P8nC DPn(. +DP8n nCQ( +CQn Q(Dn QD+n PjCn( +Qn8C QDnj( sP+Dn WnC n!WC WDn", +" DnWa nCW. WC8n W.Dn D8Wn WCQn WnQ!C ZnD DnZa WCnj W8nQC DnZj sDZn WCdn +CW", +"n DdWn WDgn dPCn. +Wn8C WDnd. gPD8n QCdn +WnQC DdZn gDZn dPjnC +WnjC ZnDdj ", +"gZnsD oC o!C oD oDa o5C o8C oD5 oD8 oQC Q!oC oDQ QDoa opC p8oC opD osD o;C ", +"o+C oD; o+D 5;oC o>C 5Do; o>D Q;oC +QoC QDo; +QoD p;oC >Cop o;pD oDs> oWC W", +"!oC oHD WDoa 5WoC W8oC H5oD H8oD WQoC o!WQC oZD ZDoa pWoC opW8C oDZp oDsZ o", +"vC v+oC ovD goD v5oC >Cov o5vD oDg> vQoC ov+QC oDZv oDgZ oCpv o>pvC pvoD yo", +"D bn bn& bnD cn bn. b8n b.Dn cn8 bnK bK&n bTn cnT bnj 8nbj Dnbj scn bn( b+n", +" b(Dn cn+ n(b. +nb8 bnD(. +nc8 n(bK bK+n b(Tn +Tcn b(nj +nbj bTnj( s+cn bnE", +" bE&n bHn cnH nEb. bE8n b.Hn H8cn nEbK &nEbK HnbT HncT bEnj &njbE Hnbj sHcn", +" bdn +nbd Hnbd gcn b.dn 8dbn bHnd. g8cn bKdn d&nbK Tdbn gTcn dnbj d&nbj dnj", +"bH gnsc 4bn bn4& bn4D c4n b5n bn48 5Dbn c5n 4Kbn &nK4b bn4T 4Tcn bn4j &nj4b", +" 5Tbn c4sn b;n bn4+ D;bn c;n 5nb; >nb b5nD; c>n bK;n ;&nbK T;bn T;cn ;nbj b", +"n>j ;njbT sc>n 4Ebn &nE4b bn4H 4Hcn bE5n 4b8nE H5bn H5cn 4bnEK 4bE&nK 4bHTn", +" c4HTn 4bjnE &nE4bj 4bHnj c5nsH bn4d d&n4b H;bn c4gn 5dbn bd>n bH5dn gc>n 4", +"bdnK d&Kb;n 4bTdn c;ngT dnj4b >nbdj ;njbH c>ngs bPn P&bn DPbn cPn Pnb. 8Pbn", +" bPDn. 8Pcn bQn Q&bn QDbn cQn Qnbj Q8bn bQnDj cQsn Pnb( +Pbn bPDn( +Pcn bPn", +"(. b+P8n DP(bn. cP+8n b(Qn +Qbn bQnD( +Qcn bQnj( b+Q8n Pj(bTn cQns+ bWn W&b", +"n HPbn cWn b.Wn W8bn bHPn. W8cn WnbQ bWnQ& Zbn Zcn Wnbj &njbW bnZj snZc Wnb", +"d +Wbn bHPdn cWgn bWnd. b+W8n dP.bHn cWng8 Qdbn d&nbQ bdZn gnZc dPjbn b+Wnj", +" Zbndj Zcngs ob ob& obD oc ob5 ob8 b5oD oc5 obQ bQo& obT ocQ opb pbo8 pboD ", +"osc ob; ob+ b;oD oc+ b5o; o>b ob5D; oc> bQo; b+oQ bTo; o+cQ o;pb p>ob opbD;", +" c>os obW bWo& obH ocH b5oW bWo8 bHo5 oHc5 bWoQ obWQ& oZb ocZ oWpb opbW8 op", +"Zb Zcos ovb vbo+ vboH goc o5vb v>ob ovbH5 c>go oQvb ovb+Q ovZb Zcgo pvob o>", +"bpv oZbpv yoc 6n 6!n 6nB 6an 1n 68n 1nB 1an 6nK n!6K 6Tn Tn6a 6jn 8n6j 1Tn ", +"s6n 6n( 6+n n(6B +n6a 1n( 1+n n(1B +n1a n(6K 6K+n 6(Tn +n6T 6(nj +n6j 1(Tn ", +"6+sn 6nE n!6E 6Hn Hn6a 1nE 6E8n 1Hn H81n nE6K 6!nEK Hn6T 6HTan 6Enj njE68 H", +"n6j 6Hsn 6dn +n6d Hn6d g6n 1dn +n1d Hn1d g1n 6Kdn dnK6+ Tn6d 6Tgn dn6j dnj6", +"+ Td1n s6gn 46n 4n6! 6n4B 46an 7n 78n 7Bn 7an 6n4K 6!4nK 46Tn 4T6an 7nj 8n7", +"j 7Tn 7sn 6;n +n6; 6B;n an6; 7;n >n7 ;n7B 7a>n 6K;n ;nK6+ Tn6; a;n6T ;n7j 7", +"n>j T;7n >n7s 6n4E 6!4nE 46Hn 4H6an 7nE 8n7E 7Hn H87n 46nEK 4nE6!K 4H6Tn 6H", +"T4an nj7E 78njE Hn7T 7Hsn dn6; 4+6dn Hn6; 6;gn 7dn 7d>n H;7n g7n dnK6; 4+6d", +"nK 4Td6n g46Tn dn7j 7>dnj Td7n gn7s 6P 6P! 6PB 6aP 6P1 68P 6B1P 1a6P 6QP Q!", +"6P 6TP Qa6P 6Pj Q86P 1T6P s6P 6P( 6+P P(6B +P6a 6(1P 1+6P 6P1(B 6+P1a 6(Qn ", +"+Q6P 6(TP +T6P 6(Pj +Q1n 6TP1( 6+sP 6WP 6!Wn 6HP HP6a 1Wn W86P 1H6P H86P Wn", +"6Q 6WQn! Z6P 6aZn Wn6j 6W8Qn Z1n Z6sP 6dP +W6P HP6d g6P 1d6P +W1n 6HP1d 68g", +"P Qd6P 6+WQn 6dZn Z6gP Qd1n dPj6+ 1dZn s6gP o6 o!6 o6B oa6 7o 7o8 7oB 7oa o", +"6Q 6Qo! oT6 6Qoa 7op o87p 7oT os7 o6; o+6 6;oB 6+oa 7o; o>7 o;7B 7ao> 6Qo; ", +"6+oQ 6To; 6+oT 7;op 7po> 7;oT o>7s o6W 6Wo! oH6 6Hoa 7oW o87W 7oH oH7a 6WoQ", +" o!6WQ oZ6 oaZ6 op7W 7opW8 7Zo os7Z ov6 v6o+ v6oH go6 7vo o>7v ov7H go7 oQv", +"6 ov6+Q ovZ6 g6oZ op7v 7p>ov oZ7v yo7 9n 6&n 9nB 9an 9n1 :n 1B9n :na 9nK 6K", +"&n 9Tn T&9n 9nj :nj 1T9n s:n 9n( 9+n n(9B +n9a 9(1n :n+ 9n1(B +n:a n(9K 9K+", +"n 9(Tn +T9n 9(nj +n:j 9Tn1( s+:n 9nE 6E&n 9Hn H&9n 9E1n :nE 1H9n :Hn nE9K &", +"nE6K Hn9T 6H&Tn 9Enj nj:E Hn9j :Hsn 9dn +n9d Hn9d g9n 1d9n :dn 9Hn1d g:n 9K", +"dn d&n6K Td9n 9Tgn dn9j dn:j dnj9H gns: 49n 9n4& 4B9n 9n4a 79n 7:n 9n7B :n7", +"a 4K9n &nK49 9n4T 49Tan 9n7j 7j:n 9T7n 7:sn 9;n 9n4+ 9B;n a;9n 9;7n >n: 7B9", +";n :a>n 9K;n ;&n6K T;9n a;n9T ;n9j >j:n 7T9;n s:>n 4E9n &nE49 9n4H 49Han 9n", +"7E 7E:n 9H7n :n7H 49nEK 49E&nK 49HTn 9Hn4T& 79njE 7:njE 7H9Tn 7:Hsn 9n4d d&", +"n49 H;9n 49gn 9d7n :d>n 7H9dn >ng: 49dnK d&K9;n 49Tdn g49Tn 7d9nj 7:dnj 7Td", +"9n g:n7s 6P9 6P& 6B9P 9a6P 1P9n :P6 6P19B 6a:P 9Qn Q&6P 9T6P Qa9n 9P6j :Qn ", +"6TP9j :Qsn 6(9P 9+6P 6P9(B 6+P9a 6P19( 6+:P 1P(9nB :P6+a 9(Qn +Q9n 6TP9( 6+", +"T9P 6Pj9( +Q:n Pj(9Tn :Qns+ 9Wn W&6P 9H6P HP6& 1W9n :Wn 6HP91 6H:P Wn9Q 6W&", +"Qn Z9n 6PZ& Wn9j Wn:Q 9nZ1 Z:n 9d6P +W9n 6HP9d 6Pg9 6dP91 6d:P 1HP9dn :Wgn ", +"Qd9n dP&6Q 9dZn Z9gn dPj9n Qd:n Z9n1d gnZ: o9 o9& o9B o9a 7o9 o: 7Bo9 o:a o", +"9Q 6Qo& o9T 6To& op9 o:p o97T os: o9; o9+ 9;oB 9+oa 7;o9 o:> 7o9;B oa:> 9Qo", +"; 9+oQ 9To; 9+oT o;p9 o>:p 7oT9; s:o> o9W 6Wo& o9H 6Ho& o97W o:W o97H o:H 9", +"WoQ o9WQ& oZ9 o9Z& oWp9 op:W Z97o Z:o ov9 v6o& v9oH go9 o97v :vo 7oHv9 go: ", +"oQv9 ov9+Q ovZ9 g9oZ pvo9 op:v oZ97v yo: 6nC n!6C 6Dn Dn6a 1nC 6C8n 1Dn D81", +"n nC6K 6!nCK Dn6T 6DTan 6Cnj njC68 Dn6j 6Dsn nC6( 6C+n 6(Dn +n6D nC1( 1C+n ", +"1(Dn +D1n 6nC(K 6+nCK 6DTn( 6+DTn njC6( 6+jnC 6Djn( sn+6D nC6E 6!nCE Hn6D 6", +"HDan nC1E 68nCE Hn1D 6H8Dn 6nCEK n!C6EK 6HDTn HnD6Ta njC6E 68CnjE 6HjDn snH", +"6D 6Cdn dnC6+ Dn6d 6Dgn 1Cdn 68dnC Dd1n 1Dgn dnC6K 6+CdnK 6DdTn g6DTn dnj6C", +" dnC6+j dnj6D gs6Dn 6n4C 6!4nC 46Dn 4D6an 7nC 8n7C 7Dn D87n 46nCK 4nC6!K 4D", +"T6n 6DT4an nj7C 78njC Dn7T 7Dsn 6C;n ;nC6+ Dn6; a;n6D ;n7C 7n>C D;7n 7D>n ;", +"nC6K 6+C;nK 6D;Tn +Dn6T; ;nj7C 7>jnC 7DT;n 7s>Dn 46nCE 4nC6!E 4H6Dn 6HD4an ", +"nC7E 78nCE Hn7D 7HD8n 6nC4EK 4n6!EKC 6HD4Tn 46anHDT 7njCE njC78E 7HDTn 7sHD", +"n dnC6; 4+6dnC 4Dd6n g46Dn dn7C 7>dnC Dd7n 7Dgn 6dC;nK dn6;+CK Ddn6T; 6D;gn", +"T 7dnjC >nj7dC 7DdTn g7sDn 6PC PC6! 6DP DP6a 6C1P 6C8P 1D6P D86P 6CQn 6QP!C", +" QD6P 6QDaP 6CPj 6Q8PC QD1n 6DsP PC6( 6C+P 6(DP +D6P 6P1C( 6+P1C 6DP1( 6+D1", +"P 6QPC( 6+QPC 6QDP( 6+QDP 6PjC( 6+PjC 6DPj( s6P+D 6CWn 6WP!C HP6D 6HPDa 1CW", +"n 6W8PC WD1n 6H8DP 6WQPC WnC6Q! 6DZn Z6DaP 6WjPC PjC6W8 1DZn Z1nsD 6CdP 6+W", +"PC Dd6P 6DgP 6dP1C 68dPC 6Dd1P g6P1D 6QdPC dPC6+Q Z6DdP gZ6DP dPj6C dPC6+j ", +"Z1nDd Z1ngD o6C 6!oC oD6 6Doa 7oC 7Co8 7oD oD7a 6QoC o!6QC 6QoD oD6Qa 7Cop ", +"7op8C op7D 7Dos 6;oC 6+oC 6Do; 6+oD o;7C >C7o 7;oD 7Do> o6Q;C o+6QC oD6Q; o", +"+D6Q 7op;C 7p>oC 7oDp; os7>D 6WoC o!6WC 6HoD oH6Da oW7C 7oW8C oH7D 7oHD8 o6", +"WQC 6WQo!C oDZ6 oZ6Da 7opWC op87WC 7DoZ osZ7D oCv6 ov6+C v6oD oDg6 7Cov 7vo", +">C ov7D 7Dgo ov6QC v6Qo+C Zv6oD goZ6D 7vopC o>p7vC 7vDoZ 7oyD b6n 6&bn 9Dn ", +"c6n b1n :nb 1Dbn :cn bK9n &nKb6 6Tbn 6Tcn 6jbn bn:j 1Tbn :csn b(9n 6+bn 9(D", +"n 6+cn b(1n b+:n b1nD( :+cn b6n(K b6+nK b6Tn( c6+Tn b6jn( :nb+j b1Tn( :cns+", +" bE9n &nEb6 6Hbn 6Hcn bE1n bn:E 1Hbn cn:H b6nEK b6E&nK b6HTn c6HTn b6jnE :n", +"bjE b6Hnj :cnsH 6dbn d&nb6 Dd9n c6gn 1dbn bd:n b1Hdn :cgn b6dnK dnKb6+ b6Td", +"n gc6Tn dnjb6 :dnbj dnj9D g:nsc bn49 4b6&n 9n4D 49cn 7bn :n7b bn7D 7cn 4b6n", +"K 4b6&nK 4bT9n c46Tn bn7j 7:bnj bT7n sn7c 6;bn ;&nb6 D;9n 6;cn b;7n 7b>n 7b", +"D;n >n7c b6;nK ;nKb6+ b6T;n c6;Tn 7b;nj 7>bnj 7bT;n c>n7s 4b6nE 4b6&nE 4bH9", +"n c46Hn bn7E 7:bnE bH7n cn7H bnE49K 4Eb6&nK bHn49T 49HcnT 7bnjE :nj7bE 7bHT", +"n 7csHn 4bd9n b6d;&n 49Ddn c6;gn bd7n 7:dbn 7bHdn gn7c dnKb6; dn4&b6K bTn49", +"d c4Tg9n 7bdnj 7bj:dn 7bTdn g7csn b6P 6&bP 6DbP c6P 6Pb1 b6:P b6P1D c6:P 6Q", +"bP b6QP& 6TbP 6QcP 6Pbj bQ:n b6T1P c6sP b(6P 6+bP b6PD( 6+cP b6P1( :Pb6+ b6", +"(1DP :cP6+ b6QP( b6+Qn b6TP( c6Q+P b6Pj( :Qnb+ Pj(b6T :cQ+n 6WbP b6WP& 6HbP", +" 6HcP 1Wbn bW:n b6H1P cW:n b6WQn 6W&bQn b6Zn c6Zn b6WPj :WnbQ b1Zn :cZn 6db", +"P dP&b6 b6HdP c6gP b6d1P :Wnb+ 6HPb1d g:Pc6 b6QdP b6QdP& Zb6dP Zc6gP dPjb6 ", +":Qdbn Zb1dn Z:ngc ob6 b6o& o9D oc6 7ob o:b ob7D 7co b6oQ ob6Q& b6oT o9cQ op", +"7b ob:p ob7T os7c b6o; b6o+ 9Do; o9c; 7;ob 7bo> 7obD; c>o: ob6Q; ob+6Q obT6", +"; oc6+Q 7obp; o:p>b 7obT; os:c> b6oW ob6W& b6oH o9cW ob7W ob:W ob7H 7Hoc ob", +"6WQ b6Wo&Q obZ6 Z6oc 7obpW o:pbW 7boZ oc7Z vbo9 ovb6+ v9oD g6oc ov7b ob:v 7", +"vboH 7cgo ovb6Q obQv6& oZbv6 cv6oZ 7vbop :vopb 7Zbov 7cyo Rn Rn! RnB Ran Rn", +". R8n n.RB 8nRa Mn Mn! RTn aMn Rjn 8Mn TnRj sRn Rn( R+n n(RB +nRa n(R. +nR8", +" Rn(.B R+8an Mn( +Mn R(Tn +nRT R(nj +nRj Mn(Tj R+sn RnE n!RE RHn HnRa nER. ", +"RE8n R.Hn HnR8 MnE MEn! HMn aMHn REnj 8EMn HnRj RHsn Rdn +nRd HnRd gRn R.dn", +" 8nRd dn.RH R8gn dMn dM+n TnRd gMn dnRj dM8n dMnHj sRgn R4 R4! R4B R4a R45 ", +"R48 RB5n 48Ra R4M 4!Mn R4T 4TRa R4j 48Rj 4TR5 sR4 R4; R4+ RB;n 4+Ra 5nR; >n", +"R ;nBR5 R4>a M;n 4MR+ 4TR; 4+RT 4jR; >Mn M;n5T sR>n R4E 4!RE R4H 4HRa RE5n ", +"RE48 4HR5 4HR8 RE4M MnE4! 4HRT aMn4H RE4j R48ME 4HRj R4sH R4d 4+Rd 4HRd gR4", +" 4dR5 R4>d R4H5d gR>n 4MRd dMn4+ 4TRd R4gM 4dRj dM>n dMnH5 gM>n RPn n!RP Pn", +"RB RPan PnR. RP8n RPn.B aPnR8 Sn S!n STn San Snj S8n TnSj Ssn PnR( RP+n RPn", +"(B aPnR+ RPn(. R+8Pn Pn(R.B R+8aPn Sn( S+n TnS( +TSn njS( +nS8 STnj( S+sn R", +"Wn R!Wn HnRW WnRa R.Wn WnR8 RHWn. RH8Wn SWn WnS! SZn ZnSa WnSj W8Sn SjZn sn", +"SZ WnRd +nRW RHWdn RWgn dP.Rn R+W8n RHWdn. gRW8n Sdn +WSn ZnSd gSn dnSj 8dS", +"n SZdnj gnSs oR o!R oRB oaR oR5 o8R R5oB R5oa So So! SoT Soa Sop So8 oTSp o", +"sS oR; o+R R;oB R+oa R5o; o>R oR5;B >Roa So; So+ oTS; o+Sa opS; S>o SpTo; o", +"sS> oRW RWo! oHR RHoa R5oW RWo8 RHo5 RHo8 SoW o!SW oZS SaoZ SWop SWo8 SpoZ ", +"oZSs ovR vRo+ vRoH goR o5vR vRo> ovRH5 gRo> Svo ovS+ oZSv goS opSv o>Sv SZp", +"ov yoS Rn# R&n n#RB anR& n#R. 8nR& Rn#.B an&R8 Mn# M&n R#Tn TnR& R#nj M&8n ", +"Mn#Tj R&sn n#R( +nR& Rn#(B an&R+ Rn#(. R+8&n n#(R.B R+8an& n#M( M&+n Mn#T( ", +"aMn+& Mn#j( M&n+j RT#nj( sn+R& n#RE RE&n R#Hn HnR& Rn#E. &nER8 RHn#. RH8&n ", +"n#ME ME&n H#Mn M&Hn Mn#jE M&n8E RHjn# snHR& R#dn dnR& dn#RH R&gn dn#R. d&nR", +"8 RH#dn. gR8&n d#Mn M&dn dMnH# M&gn dMnj# dM&8n dM#Hnj gsR&n R4# R4& 4#RB 4", +"aR& R#5n 48R& R45#B R48a& R#4M 4MR& R#4T 4TR& R#4j 4&Rj R4T5# R4s& R#;n 4+R", +"& ;n#RB a;nR& ;n#R5 R4>& R5#;nB >nRa& M#;n ;&Mn M;nT# aM;&n M;n5# M&>n M;#5", +"Tn >Mns& 4#RE RE4& R#4H 4HR& R45#E R48&E R4H5# R4H5& Mn#4E M&n4E R4HT# M&n4", +"H R4j#E M&n5E R4Hj# sR4H& R#4d 4dR& R4Hd# R4g& R4d5# >nRd& dn#RH5 >ngR& dMn", +"4# dM&R4 M;nH# gR4T& dMn5# >Mnd& dM#H5n >Mg&n PnR# RP&n RPn#B aP&Rn RPn#. P", +"&nR8 Pn#R.B R8aP&n Sn# S&n TnS# T&Sn njS# 8nS& STnj# S&sn RPn#( P&nR+ Pn#R(", +"B R+aP&n Pn#R(. R+8P&n .BR(Pn# aP8nR+& n#S( +nS& STn#( S+T&n Snj#( S+8&n nj", +"#ST( Ss+&n R#Wn WnR& RHWn# RH&Wn RWn#. RW8&n Hn#RW. H8nRW& WnS# W&Sn S#Zn Z", +"nS& SWnj# S8W&n SZnj# SZ&sn dP#Rn dP&Rn RHWdn# gRW&n RW#dn. RW8d&n dPHnR#. ", +"RW8gn& dnS# d&Sn SZdn# S&gn Sdnj# S8d&n ZnjSd# gSs&n oR# o&R R#oB Rao& R5o#", +" R5o& oR5#B o8Ra& So# So& S#oT oTS& S#op opS& SpTo# S&os R;o# R+o& oR;#B o+", +"Ra& oR5;# >Ro& R5;o#B o>Ra& o;S# o+S& S;To# Sa;o& Sp;o# S&o> opTS;# os>S& R", +"Wo# RWo& RHo# RHo& oR5W# o8RW& oHR5# oH8R& oWS# SWo& S#oZ S&oZ SpWo# Sp&oW ", +"SZpo# osZS& o#vR vRo& ovRH# o&gR ovR5# o>vR& vR5oH# go>R& S#ov ovS& SvoZ# S", +"&go Svop# Sv&o> oZpSv# Soy& Un Un! RDn Uan Un. U8n R.Dn D8Un UnM U!Mn Vn Vn", +"a Unj 8MUn Vnj snV Un( U+n R(Dn +DUn n(U. +nU8 RDn(. R+D8n U(Mn +MUn Vn( V+", +"n U(nj +nUj njV( V+sn UnE n!UE UHn HnUa nEU. UE8n U.Hn H8Un UEMn MnEU! VnH ", +"HnVa UEnj U8nME HnVj sHVn Udn +nUd HnUd gUn U.dn 8dUn RDdn. U8gn dMUn dMnU+", +" Vdn gVn dnUj dMnU8 dnVj sngV R4U 4!Un R4D 4URa U5n 4UR8 4DR5 4DR8 Un4M Mn!", +"4U V4R R4Va 4URj R48Uj V5n V4sR U;n 4UR+ 4DR; 4+RD 5nU; >nU R4D5; R4>D M;Un", +" M;nU+ V;n R4V+ ;nUj Un>M 5nV; V>n RE4U R4U!E 4URH R4HUa UE5n R48UE H5Un R4", +"HU8 MnE4U 4UEMn! R4VH V4RHa R4jUE MnE4U8 H5Vn snV4H 4URd R4+Ud 4DRd R4gU 5d", +"Un Ud>n R4D5d gU>n dMn4U 4U+dMn R4Vd V4gR dMnU5 >MnUd 5dVn >ngV UPn n!UP DP", +"Un aPUn PnU. 8PUn RDPn. RD8Pn SUn UnS! SVn VnSa UnSj U8Sn SjVn snSV PnU( +P", +"Un RDPn( R+DPn UPn(. U+P8n DP(Un. +DPU8n UnS( U+Sn S(Vn VnS+ SUnj( S+U8n SV", +"nj( SV+sn UWn U!Wn HPUn WaUn U.Wn W8Un RWDn. RWD8n UWSn S!UWn ZnV ZaVn WnUj", +" S8UWn ZjVn VZsn WnUd +WUn RWDdn UWgn UWnd. U+W8n dP.UHn gUP8n UdSn S+UWn V", +"dZn SVgn SdUnj S8dUn SVdnj gVnSs oU oU! oUD oUa oU5 oU8 R5oD RDo8 SoU S!oU ", +"oV oVa opU oUS8 oVp oVs oU; oU+ RDo; R+oD U5o; o>U oU5D; >RoD oUS; oUS+ oV;", +" oV+ o;pU oUS> V;op oV> oUW UWo! oUH UHoa U5oW UWo8 UHo5 UHo8 SWoU oUWS! oV", +"Z VZoa oWpU opUW8 VpoZ sZoV ovU vUo+ vRoD goU o5vU v>oU ovUH5 gUo> oUSv Svo", +"U+ oVv goV pvoU o>USv opVv yoV Rbn U&n bnRD cRn R.bn bnR8 RbDn. R8cn bMn M&", +"bn Vnb cnV bnRj 8Mbn bnVj cRsn R(bn bnR+ RbDn( R+cn Rbn(. Rb+8n bn(RD. cR+8", +"n b(Mn +Mbn bnV( V+cn Rbjn( &njU+ Vnbj( snVc+ REbn UE&n bnRH RHcn RbnE. Rb8", +"nE RbHn. cRH8n bEMn M&nbE bHVn cHVn RbjnE &njUE VnbHj snVcH bnRd d&Un RbHdn", +" cRgn Rbdn. d&nU8 dn.RbH gcR8n dMbn dM&bn bdVn cngV dMnbj dMnb8 Vdnbj gVnsc", +" R4b 4bR& 4bRD cR4 4bR5 4bR8 R4b5D R4c5 4MRb M&n4b R4Vb V4cR 4bRj M&nb5 b5V", +"n cRs4 4bR; 4bR+ R4bD; R4c; R4b5; R4>b b5nRD; cR>n M;bn M;&bn b;Vn V;cn M;n", +"b5 bM>n V5nb; cnV> RE4b R4b&E 4bRH R4cH R4b5E R4b8E R4bH5 cR4H5 R4bME M&ER4", +"b V4RbH cR4VH R4bjE M&Eb5n V5nbH cR4sH 4bRd d&n4U R4bH; cRg4 R4b5d >nbRd 4b", +"HR5d c>Rg4 dMn4b R4bdM& V4dRb gV4cR dMnb5 >Mbdn V5dbn V>ngc UPbn P&Un RbDPn", +" UPcn RbPn. Rb8Pn bPnRD. cR8Pn Sbn bnS& VnSb Scn bnSj b8Sn SVbnj snSc RbPn(", +" Rb+Pn bPnRD( cR+Pn UP(bn. U+Pb8n R(bPDn. U+Pcn8 bnS( b+Sn SVbn( cnS+ Sbnj(", +" Sb+8n VnjSb( Scs+n bnRW W&Un RbHWn RWcn RbWn. RbW8n Rb.WDn cRW8n bWSn SbW&", +"n ZnSb ZnSc SbWnj Sb8Wn SZbnj ZcnSs RbWdn dP&Un bWnRDd cRWgn dn.RbW b+nRW8 ", +"R.bHWnd cW8gUn bdSn Sb+Wn SZbdn gnSc Sbdnj Sb8dn SbjVdn gScsn obR oU& RboD ", +"ocR Rbo5 Rbo8 obR5D oUc5 Sob obS& oVb Sco opSb obS8 obVp osSc Rbo; Rbo+ obR", +"D; oUc; obR5; >Rob Rb5oD; cRo> obS; obS+ V;ob S+oc Spbo; Sbo> oVpb; ocS> Rb", +"oW UWo& RboH oUcW obR5W ob8RW obHR5 ocRH5 SWob oU&SW SboZ ZcoV SpboW Sb8oW ", +"oVZpb oVsZc vRob vUo& ovbRH gRoc ovbR5 o>bvR ob5vRD cvRo> ovSb Svbo+ obVv S", +"cgo Svbop S>bov oVvpb Scyo q q! qB qa q1 q8 q1B q8a qM q!M qT qTa lq q8l qT", +"l sq q( q+ q(B q+a q1( q+1 1(qB 1+qa qM( q+M qT( q+T lq( l+q l(qT sq+ qE q!", +"E qH qHa q1E q8E qH1 qH8 qME MEq! qHT HMqa lqE lEq8 lHq sqH qd q+d qHd gq q", +"d1 q8d 1Hqd gq1 qdM +Mqd qTd gqT ldq q+ld qHld glq q4 q!4 q4B qa4 7q 7q8 7q", +"B 7qa q4M 4Mq! qT4 4Tqa 7lq q87l 7qT sq7 q; q+4 q;B qa; 7q; q> 7Bq; q>a q;M", +" 4+qM qT; 4+qT l;q q>l qTl; sq> q4E 4Eq! qH4 4Hqa 7qE 7Eq8 7qH qH7a 4MqE q!", +"4ME 4HqT qH4aM lE7q 7lq8E 7qlH 7Hsq qd4 4+qd qH; gq4 7qd q>d qH7d gq7 4dqM ", +"q+4dM 4Tqd qTg4 7qld ldq> qHl; sqg7 qP q!P qPB qaP q1P q8P 1PqB 1aqP Sq Sq!", +" SqT Sqa Slq Sq8 qTSl sqS qP( q+P P(qB +Pqa 1Pq( 1+qP q1P(B q+1aP Sq( Sq+ S", +"(qT q+Sa l(Sq Sql+ SlqT( S+sq qW qW! qHW qWa qW1 qW8 1HqW 1Wqa SqW S!qW Zq ", +"Zqa lWq qWS8 Zql sqZ qWd q+W HPqd gqW 1Wqd 1+qW qH1dP qWg1 Sqd q+Sd Zqd gqZ", +" Sqld q+lW ldZq sqgS oq oq! oqB oqa 7oq oq8 7Boq oq7a Soq S!oq oqT oqSa r r", +"8 rT rs oq; oq+ oBq; q+oa q;7o q>o 7oq;B oaq> Sq; oqS+ qTS; q+oT r; r> r;T ", +"rs> oqW o!qW oqH qHoa 7qW qWo8 oq7H qHo8 qWSo SqWo! Zqo oaZq rW rW8 rZ rZs ", +"vq vq+ vqH goq vq7 vq> 7Hvq vqg7 vqS S+vq Zqv Zqgo rv rv> rZv yr q9 q& q9B ", +"qa9 q91 :q 91qB :qa q9M q&M qT9 qT& lq9 :ql l9qT sq: q9( q+9 9(qB 9+qa 91q(", +" :q+ q91(B :+qa 9Mq( 9+qM 9Tq( 9+qT q9l( l+:q qTl9( q+s: q9E q&E qH9 qH& 91", +"qE :qE 9Hq1 :qH 9MqE M&qE 9HqT HMq& q9lE lE:q l9qH lH:q qd9 qd& 9Hqd gq9 9d", +"q1 :qd qH91d gq: 9dqM dMq& 9Tqd qTg9 l9qd ld:q lHq9d g:sq q49 q&4 49qB 49qa", +" 7q9 :q7 q97B 7a:q 49qM 4Mq& 49qT 4Tq& l97q 7l:q 79qT :q7s q;9 q;& 9;qB 9aq", +"; 79q; q>: 7q9;B qa:> 9Mq; M;q& 9Tq; T;q& l9q; :ql> 7qT9; s:q> 49qE 4&qE 49", +"qH 4Hq& q97E 7E:q 79qH 7H:q q49ME q&4ME qH49T qH&4T 7lq9E :ql7E 7qHl9 :qH7s", +" 49qd 4dq& 9Hq; q;g9 79qd 7d:q 7qH9d g:q> qd49M qd&4M qH;9T gq49T 7qdl9 q>l", +":d 7qT9d glq7: q9P q&P 9PqB 9aqP 9Pq1 :qP q91PB qa:P Sq9 Sq& qTS9 qTS& lqS9", +" S:q Slq9T S:sq 9Pq( 9+qP q9P(B q+9aP q91P( q+:P 9P1q(B :q+aP q9S( q+S9 Sq9", +"T( Sq+9T Slq9( S+:q qTlS9( sq:S+ qW9 qW& 9HqW 9Wqa 9Wq1 :qW qH91W qH:W qWS9", +" qWS& Zq9 Zq& l9qW lW:q lqZ9 Zq: 9Wqd 9+qW qH9dP qWg9 qW19d q+:W 9HPqd1 gP:", +"q qdS9 qdS& qdZ9 g9Zq Sqdl9 Sd:q Zql9d S:gq oq9 oq& q9oB qao9 o97q o:q 7oq9", +"B oa:q So9 oqS& oTS9 o9Sa r9 r: r9T rs: q;o9 q+o9 oq9;B oq+9a 7oq9; q>o: o9", +";7qB o:q>a o9S; o9S+ Sq;9T Sqa9; r;9 r:> 9Tr; s:r> qWo9 qWo& qHo9 qHo& 79qW", +" 7W:q 7oHq9 oq:H SWo9 Sq&oW S9oZ oqZ& r9W r:W rZ9 rZ: vq9 vq& qHv9 g9vq v97", +"q :vq vq79H :vgq S9vq S&vq Z9vq Sog9 rv9 r:v Zvr9 yr: qU q!U qD qDa q1U q8U", +" qD1 qD8 qUM U!qM Vq Vqa lqU lUq8 Vql sqV qU( q+U qD( q+D 1Uq( 1Uq+ 1Dq( 1+", +"qD UMq( U+qM Vq( Vq+ qUl( lUq+ l(Vq l+Vq qUE U!qE qHU UHqa 1UqE U8qE 1UqH U", +"Hq8 UMqE q!UME VqH VHqa qUlE q8lUE lHVq qHVs qdU U+qd qDd gqU 1Uqd U8qd 1Dq", +"d qDg1 UdqM q+UdM Vqd gqV lUqd l+qUd ldVq gVsq q4U 4Uq! qD4 4Uqa 7qU q87U 7", +"qD qD7a 4UqM q!4UM Vq4 qaV4 lq7U 7lqU8 7Vq 7Vsq q;U 4Uq+ qD; 4+qD q;7U q>U ", +"q;7D q>D U;qM q+4UM Vq; q+V4 lUq; lq>U l;Vq V>q 4UqE q!4UE 4UqH qH4Ua qU7E ", +"7qU8E qH7U 7qHU8 q4UME 4UMq!E qHV4 Vq4Ha 7lqUE q8l7UE 7HVq sq7VH 4Uqd q+4Ud", +" 4Dqd qDg4 qd7U >Uqd qD7d gUq> qd4UM 4U+qdM qHV; g4Vq 7qdlU q>lUd 7dVq 7Vgq", +" qUP UPq! qDP UaqP 1UqP U8qP 1DqP D8qP SqU q!SU VqS SaVq lUSq SUq8 SlVq VqS", +"s UPq( U+qP DPq( +DqP q1UP( q+1UP qD1P( q+D1P qUS( SUq+ S(Vq S+Vq SlqU( Sq+", +"lU SlVq( Vq+Ss qWU UWq! qWD UWqa 1UqW UWq8 1WqD WDq8 SUqW SqUW! ZqV qaVZ lU", +"qW Sq8UW lZVq sZVq UWqd U+qW WDqd qWgU qW1Ud q+W1U qWD1d gq1UW SUqd Sq+UW S", +"dVq gVZq SqdlU Sq8Ud VqdlZ glqSV oqU q!oU oqD qDoa 7oU o87U oq7D oU7a oUSq ", +"SqUo! oVq oaVq rU rU8 rV rVs q;oU q+oU q;oD q+oD 7;oU 7Uo> 7oUD; oDq> SUq; ", +"Sq+oU S;Vq o+Vq r;U r>U rV; rV> qWoU oqUW! qHoU oqHUa oU7W 7oUW8 oU7H 7oUH8", +" SqUoW oUWSq! oVZq oVZqa rUW UWr8 rZV sZrV vqU q+vU vqD gUvq 7Uvq vUq> 7Dvq", +" 7ogU vUSq vqSU+ Vvq oVgq rvU v>rU rVv yrV tq tq& tqD cq tq1 :qt t1qD cq: t", +"qM tMq& Vqt cqV ltq lt:q ltVq uq tq( tq+ t(qD cq+ q1t( t+:q tq1D( q+:c qMt(", +" tMq+ t(Vq t+Vq l(tq tql+ Vqlt( uq+ tqE tEq& tqH cqH q1tE tE:q t1qH tH:q qM", +"tE tqM&E tHVq qHVc lEtq :qltE tqlH uqH tqd q+td qHtd gqt t1qd td:q tqH1d g:", +"cq tMqd tq+dM tdVq gVcq tqld :qdlt Vqdlt uqg tq4 q&t4 qDt4 cq4 7tq :q7t 7Dt", +"q 7cq q4tM tq4M& t4Vq c4Vq 7ltq 7tl:q Vq7t uq7 tq; q+t4 qDt; cq; 7qt; t>q t", +"q;7D cq> tMq; tq+4M t;Vq c;Vq tql; ltq> Vq;7t uq> q4tE tq4&E qHt4 qHc4 tE7q", +" 7tq:E 7qtH 7Hcq tq4ME q&Mt4E tqHV4 cqV4H 7tlqE :ql7tE 7tHVq 7quH qdt4 tq+4", +"d qHt; g4cq 7qtd tdq> 7tHqd t>gq tqd4M t4Mqd& Vqdt4 cq;gV 7tdlq t>qld 7Vdtq", +" gqu7 tqP q&tP qDtP cqP q1tP tP:q tq1DP :Pcq Stq S&tq VqSt Scq Sqlt :qSt Sl", +"Vtq uqS qPt( q+tP tqDP( q+cP tq1P( tq+:P qD1tP( cq:+P S(tq tqS+ StqV( S+cq ", +"Stlq( St+:q VqlSt( Squ+ tqW q&tW qHtW cqW t1qW tW:q tqH1W :Wcq tWSq Sq&tW t", +"Zq Zqc tqlW :qWSt ltZq uZq qWtd q+tW tqHdP gPcq tqW1d :qWt+ tW1qDd cqWg: Sd", +"tq St+qW tdZq Scgq Stdlq S:dtq tZqld gquZ ot ot& otD otc ot7 ot: 7Dot ot7c ", +"otS S&ot oVt otSc rt rt: rVt ur ot; ot+ oDt; t+oc 7ot; t>o ot7D; oct> S;ot ", +"S+ot t;oV S;cq rt; rt> t;rV ur> otW o&tW otH tHoc 7otW tWo: 7otH tHo: tWSo ", +"otSW& tZo octZ rtW t:rW rZt urZ tvo t+vq tHvq got 7tvq ot:v otH7v t>go Stvq", +" ot+Sv Zqtv tZgo rtv tvr: tZrv ury n= 5n=B =a>n =nK", +" +n=K =Tn +T=n =jn =j>n 5T=n s=n n H5=n >ng= dn=K =+dnK Hn=T gn=T dn=j >n=dj Hn=j gns=

= o5=B =ao> =Qo o+=Q =To o+=T p=o p=o> op=T s=o oW< Wo= =5oH o>g= ov=Q ov=+Q Z=o Z=go ovp= o>pv= p=oZ yo= ,n ,n& ,nB ,an ,", +"n. ,8n n.,B 8n,a ,nK ,K&n ,Tn T&,n ,nj 8n,j Tn,j sn, ,n* -n *B,n -na ,.n* -", +"n8 n*.,B 8n-a ,Kn* -nK T*,n -Tn n*,j -nj n*j,T s-n ,nE ,E&n ,Hn H&,n nE,. ,", +"E8n ,.Hn H8,n nE,K <&EnK Hn,T ,HnT& ,Enj <&jnE Hn,j ,Hsn ,dn -nd Hn,d g-n ,", +".dn 8d-n ,Hnd. g8-n ,Kdn dn-K Td,n -Tgn dn,j dn-j dnj,H gns- ,4n 4&,n 4B,n ", +"4a,n ,5n 48,n ,B5n 5a,n 4n,K <&K4n 4T,n ,4Tan 4j,n <&j5n 5T,n ,4sn =n, -=n ", +",n=B -n=a ,5=n >n- =B5,n -a>n ,n=K =K-n ,T=n -n=T ,n=j -n=j =T5,n >ns- 4n,E", +" <&E4n 4H,n ,4Han ,E5n ,48nE H5,n ,4H8n ,4nEK ,4E&nK ,4HTn 4H&,Tn ,4jnE n =H5,n g->n =dn,K -=dnK =HT,n ", +"g=-Tn =jd,n >n-dj =Hj,n s-ng= ,P< o=5,B ->oa o,=Q o-Q o,=T o-T o,p= o-p p=o,T os- o,W ,Wo& oH, ,Hoa ,", +"5oW ,Wo8 ,Ho5 ,Ho8 ,WoQ o&W,Q oZ, oaZ, oWp, op&,W opZ, s,oZ ov, o-v o,=H go", +"- o5v, -vo> ov,H5 g-o> oQv, -Qov ovZ, Z-o pvo, op-v oZ,p= yo- C 5D=n =D>n nC=K =+nCK Dn=T =D+Tn nj=C >n=jC Dn=j sn=D 4n=dC =H5Dn g=>Dn =d", +"nCK dnC=+K =HDTn g=DTn =jdnC =jd>nC =HjDn s=gDn Co= =5oD =Do", +"> oQ=C o=+QC oD=Q o+D=Q =Cop o>p=C op=D =Dos Wv=C ovD=5 go>=D ov=QC =Qvo+C =DoZ goZ=D p=ovC p=vo>C p=D", +"oZ =Dyo ,nb <&bn ,Dn cn =b5Dn >nc= bn=K =b-nK bT=n cn=T bn=j >n-bj =bT5n snc", +"= ,4bnE ,4b&nE ,4Hbn c4H,n ,5nbE <&Eb5n ,H5bn c5n-bd =bH5n c", +"=g>n =bdnK -nd=bK =bHTn c=gTn =bjdn >nd=bj =bHnj s=cgn b

=bo5D", +" c>o- ob=Q -Qob ob=T =Qoc op=b ob-p p=boD ocs= ,Wob obW<& ,Hob oHc< ob5,W o", +"b8,W obH,5 ocH,5 obW,Q bWb ovD,5 cv=o> ovb=Q o-vbQ =boZ ocZ- p=bov o-pvb Z=bop o", +"cy- 6n =n7B =n7a 6n=K =6+nK 6T=n =T6+n 7n=j 7>=nj =n7T 7=sn 46", +"=dn =n7H 7=gn =6dnK d", +"nK=6+ =H6Tn g=6Tn 7=dnj >n=7dj 7Td=n g7s=n 6P< 6!

=B7o 7o=a o6=Q o=6+Q =6o", +"T o+T=6 7op= 7o=p> 7o=T 7os= 6Wo< o!6W< 6Ho< oH6a< oW7< 7oW8< 7 7o=H 7og= ov=6Q =Qvo+6 o=Z6 goZ=6 7o=pv o>p7v= 7oZ= 7oy= ?n ?&n ?Bn ?an ", +"?1n ?:n 1n?B :n?a ?nK &n?K ?Tn T&?n ?jn :n?j 1T?n s?n ?n* ?-n n*?B -n?a 1n?", +"* :-n ?B1n* :a-n n*?K ?K-n Tn?* -n?T n*?j -n?j ?T1n* ?-sn ?nE &n?E ?Hn H&?n", +" 1n?E ?E:n 1H?n :n?H nE?K ?&nEK Hn?T ?HTan nj?E ?:njE Hn?j ?Hsn ?dn -n?d Hn", +"?d g?n 1d?n :n?d ?H1dn ?:gn dn?K ?-dnK Td?n ?Tgn dn?j ?:dnj ?Hjdn gns? ?4n ", +"4&?n 4n?B 4a?n 7?n :n7? ?n7B ?n7a 4n?K ?4&nK 4T?n ?T4an 7n?j ?:7nj ?n7T 7?s", +"n ?=n -n?= ?B=n =n?a =n7? An ?=7nB Aan ?n=K ?-=nK =n?T ?-T=n =n?j Ajn ?T7=n", +" Asn 4n?E ?4&nE 4H?n ?H4an 7n?E ?:7nE ?n7H ?:H7n ?4nEK &nE?4K ?H4Tn 4H&?Tn ", +"?j7nE 7:n?jE ?H7Tn s?7Hn =n?d ?-=dn =n?H ?=gn ?n7d Adn ?H7=n Agn ?=dnK -=n?", +"dK ?Td=n g?=Tn ?d7nj dnAj ?Hj=n gsAn ?P ?P& ?PB ?aP ?P1 ?:P 1P?B :P?a ?QP Q", +"&?P ?TP Qa?P ?Pj :Q?P 1T?P s?P ?P* ?-P P*?B -P?a 1P?* :-?P ?P1*B ?:P-a Qn?*", +" -Q?P TP?* -T?P P*?j -Q:n ?TP1* ?-sP ?WP W&?P ?HP HP?a 1W?P :W?P 1H?P :H?P ", +"Wn?Q ?WQP& ?ZP Z&?P Wn?j ?:WQn Z1?P sP?Z ?dP -W?P HP?d g?P 1d?P :d?P ?HP1d ", +"?:gP Qd?P ?-WQn Zn?d gP?Z dP?j ?:QdP ?Z1dP gPs? o? o?& o?B o?a o?7 o:? 7Bo?", +" ?ao: o?Q o&?Q o?T oT?a ?po o:?p 7o?T s?o o?= o-? =Bo? =ao? 7o?= Ao 7o=?B A", +"oa =Qo? ?Qo- =To? ?To- p=o? Aop o?Tp= Aos o?W o&?W o?H oH?a 7o?W ?Wo: 7o?H ", +"?Ho: oW?Q o?WQ& ?Zo ?aoZ op?W o:?pW o?7Z os?Z ?vo o-?v =Ho? go? o?7v Aov o?", +"H7v Aog ov?Q o-?vQ o?Z= ?Zgo op?v opAv ?Zo7v yAo =nC =n7D 7", +">D=n =6nCK 6+n=CK =D6Tn 6+D=Tn 7=njC >nj7=C =D7Tn s=7Dn 6n=7dC 7Dd=n g7=Dn ", +"dnC=6K dn=C6+K 6Dd=Tn g6D=Tn =jn7dC >j7d=nC 7Hn=Dj s=Dg7n 6C

C 7o=D o>D", +"7= o=6QC =Q6o+C =QDo6 o+6=QD 7o=pC p=7o>C p=D7o s=o7D o6W=7vC 7vDo= go7=D =Q6ovC =6o+vQ", +"C Z=o6D Z=6goD p=o7vC >C7vp=o 7Z=oD yo7=D ?bn bn?& ?Dn ?cn b1?n ?b:n 1D?n :", +"n?c bn?K ?b&nK bT?n cn?T bn?j ?:bnj Dn?j sn?c bn?* ?b-n Dn?* cn?- 1nb* :b-n", +" ?D1n* c-:n ?bn*K ?-bnK ?DTn* ?-Tcn ?jbn* ?-jbn ?Djn* s-n?c bn?E ?b&nE bH?n", +" cn?H ?b1nE ?:bnE ?Hb1n ?:Hcn ?bnEK &nE?bK ?HbTn ?cHTn ?jbnE :nb?jE ?Hjbn s", +"?cHn bd?n ?-bdn Dd?n gn?c ?d1bn ?:dbn ?Dd1n g:n?c ?dbnK -nb?dK ?DdTn g?cTn ", +"?djbn ?db-nj ?Ddnj g?scn 4b?n ?4b&n 4D?n ?4cn ?n7b ?:7bn ?n7D cn7? ?4bnK &n", +"K?4b ?D4Tn ?c4Tn ?j7bn 7:b?jn ?D7Tn s?7cn ?b=n ?-=bn =n?D cn?= =n7b Abn ?D7", +"=n Acn ?=nbK =bn?-K =bT?n c=?Tn =b7nj bnAj ?Dj=n scAn ?4bnE &nE?4b ?H4bn ?c", +"4Hn 7?bnE 7bn?:E ?H7bn 7c?Hn 4bn?EK &n?E4bK 4bH?Tn c4H?Tn 7bn?jE 7j?E:nb ?H", +"n7bT 7cHs?n ?=dbn =b-?dn ?Dd=n c=?gn ?d7bn bdAn ?D7dn gcAn =bn?dK =d-n?bK ?", +"Hn=bT g?Tc=n ?dn=bj Adnbj ?Hn=bj Agnsc ?Pb bP?& ?DP ?cP b1?P ?b:P 1D?P :c?P", +" bQ?P ?QbP& bT?P cQ?P bP?j ?:QbP DP?j sP?c bP?* ?b-P DP?* c-?P ?Pb1* ?:P-b ", +"?DP1* ?cP:- ?QbP* ?-QbP ?QDP* ?cQ-P ?Pjb* ?-Pbj ?DP*j s?Pc- bW?P ?WbP& bH?P", +" cW?P ?Wb1P ?:WbP ?HPb1 ?cW:P ?WbQn bWn?Q& Zb?P ?PZc ?WjbP ?Wb:Qn ?Zb1P s?Z", +"cP bd?P ?-WbP Dd?P gP?c ?dPb1 ?:dbP ?Dd1P g?P:c ?QdbP ?Wb-Qn ?ZbdP g?ZcP ?d", +"Pbj ?Wb-Pj ?DdPj g?scP o?b ?bo& o?D oc? 7bo? ob?: 7o?D ?co: ob?Q o?bQ& ob?T", +" ?Qoc ob?p o:?pb op?D ocs? =bo? ob?- =Do? ?co- 7o=b Aob o?D7= Aoc o?=bQ o-?", +"bQ o?D=Q oc?=Q p=bo? opAb ?pDo= osAc ob?W o?bW& ob?H ?Hoc ?W7ob o:?bW o?H7b", +" o:H?c o?bWQ obW?Q& ?DoZ oc?Z ?pobW ?pbo:W ?Zo7b s?oZc ob?v o-?vb ov?D ocg?", +" 7vbo? ovAb ?vD7o goAc ?vobQ ?vbo-Q ?Zo=b go?Zc ?pvob Aopvb ?pvoD yoAc R< R", +" R5=B >R=a =RM R+=M =RT R+=T =Rj =R>M R", +"5=T s=R 4R RH=5 g>=R Rd=M =R+dM", +" RH=T gR=T Rd=j >M=Rd RH=j gs=R R

o=5RB o>=Ra S= S=+ S=T S=a S=p S>= Sp=T s=S RWo< o!RW< RHo< o", +"HRa< 5WR< o8RW< oHR5< oH8R< oWS< S=R =RvH5 g=Ro> S=v v=S+ S=Z gS= p=Sv v>S= SZp= y=S ,", +"R< R<& RB,n Ra,n ,.R< R8,n ,R<.B ,R8a< ,Mn =R =R5,B >n-Ra ,R=M -R=M ,R=T -R=T ,R=j -R=j =RT,5 =Rs- ,ER4 ,R4&E ", +"R4,H ,R4H& ,R45E ,R48E ,R4H5 ,R4H8 ,R4ME n-Rd =RH,5 g=R-> =Rd,M =R-dM =RH,T g=", +"R-T =Rj,d >M-dn =RH,j s=Rg- R<,P o,5=RB o->Ra ", +"S=, S-o S,=T =TS- S,p= o-S> S=p,T S-s= ,RoW o&R,W ,RoH oH&,R o,R5W o8,RW oH", +",R5 oH8,R SWo, S<&oW S,oZ oZ&S< Sp<,W SpR =R5oH, go->R ovS, Svo- S,Z= S-go Sv,p= o-pSv S=Zp, S-yo Rn R5=D >R=D Un=M =RU+M V=R V+=R Rj=U >M=Un V5=R >nV= R4UnU=d =RHU5 g=R>U =RUdM d", +"Mn=U+ Vd=R =RgV =RjUd >nd=Uj V=RH5 V>ng= U

=RDo5 o>U=D S=U S+", +"=U S=V =aoV Sp=U =US> VpS= oVs= UWo< oUWUv= =Rv5D goU>= =USv S=vU+ oVZ= V=go p=USv S>=vU oVvp= S=yV ,Un bn =Rb5D =Rc> Rb=M =R-bM Vn=b =RV- Rb=j >M-bn V=R,5 >nV- ,R4bE <&ER", +"4b ,R4bH cR4,H ,U5nE <&ERb5 ,UH5n cR5H< n-Ud =RbH5 c=Rg> =RbdM =bd", +"-Mn V=R,H c=VgR =Rbdj >Md=Rb =RbHj c=Rgs bPR< ", +"oU ob5=RD oc->R S=b =bS- =boV Sc= Sp=b =bS> oV,p= Scs= ,UoW oU&,W ,UoH ocR,", +"H oU,5W oU8,W oUH,5 cR5W< Sb ob5=RH cv=>R =bSv o-USv SZ=b Z=Sc S=bpv o-", +"pvU oVvp, Scy= q< q!< q= =B7q =aq> q=M q+=M q=T q+=T l=q q", +">l= 7q=T sq= 4=qd 7q=H g=q> qd=M ", +"q=+dM qH=T =Tgq ldq= q>l=d lHq= l=gq q

7o=qB q>o=a S=q S+q= Sq=T Sq=a r= r>= r=T rs= oq= vq7=H vq>g= vqS= vq=S+ Zq= S=gq rv= v>r= rZ= ", +"yr= ?q ?q& ?qB ?qa ?q1 :q? q1?B ?a:q ?qM q&?M ?qT qT?a l?q l?:q qTl? s?q ?q", +"* -q ?Bq* -qa ?1q* -q: ?q1*B qa:- q*?M -qM q*?T -qT ?ql* l-q ?qTl* sq- ?qE ", +"?Eq& ?qH qH?a q1?E ?E:q ?1qH ?H:q qM?E ?qM&E qH?T ?qHT& lE?q :ql?E ?qlH ?Hs", +"q ?qd -qd qH?d g?q ?1qd ?d:q ?qH1d g:-q qd?M qd-M qT?d gM-q ?qld ld-q l?Hqd", +" gql- ?q4 ?4q& q4?B ?4qa ?q7 7?:q 7B?q 7q?a q4?M ?q4M& ?4qT ?qT4a 7l?q ?q7l", +": 7q?T ?q7s q=? -q= =B?q =a-q 7?q= Aq ?q7=B Aqa ?q=M =M-q ?q=T =T-q l?q= Al", +"q l=?qT Aqs q4?E ?q4&E ?4qH ?qH4a ?E7q ?q7:E 7q?H :qH7? ?q4ME q&4?ME ?qH4T ", +"?H4qT& ?q7lE l?7:qE l?H7q s?q7H ?dq= qd-= ?q=H g=-q 7q?d Aqd q=H7? Aqg ?qd=", +"M -q=dM q=H?T -qTg= l=?qd ldAq l=H?q glAq ?qP q&?P qP?B qa?P q1?P ?P:q ?q1P", +"B ?qa:P S? S?& S?T S?a S?l S?: ?TSl s?S q*?P -qP ?qP*B qa-P ?q1P* :P-q q*1?", +"PB -q:aP S?* S?- S*?T ?TS- l*S? S?l- S?lT* S-s? ?qW q&?W qH?W qW?a ?1qW ?W:", +"q ?qH1W :qH?W S?W S&?W S?Z ?aZq lWS? ?WS: l?Zq ?Zsq qW?d -qW ?qHW* gP-q ?qW", +"1d :W-q ?H1qW* -qWg: S?d ?WS- ?dZq g?S ldS? lW-q S?Zld s?gS o?q o&?q ?Boq o", +"q?a 7o?q o?:q ?q7oB o:?qa S?o S&o? ?TSo ?aSo r? r?: r?T r?s q=o? o-q o?q=B ", +"oa-q ?q7o= Aoq q=7o?B oqAa S?= o-S? =TS? =aS? r?= Ar =Tr? Ars oq?W ?qWo& oq", +"?H o?Hqa 7q?W o:?qW o?H7q o:H?q ?WSo S?oW& o?Zq S?Zoa r?W ?:rW r?Z s?rZ ?vq", +" -qv ?Hvq ?vgq ?q7v Aqv ?vq7H goAq S?v S-?v S=?Z S?go r?v Arv ?Zrv Ary qU< ", +"U 7q=D =Dq> qU=M q=U+M V=q =aVq lq=U q>l=U Vql= V=sq q4U=Ud q=H7U q>Dg= q=UdM q+U", +"=Md =HVq V=gq l=qUd l=Uq>d V=qlH V>qg= Uo=U q=D7o q>Do= Sq=U S=qU+ VqS= oV+q= r=U =Ur> rV= s=rV oqUW< qW=U vqD7= vq>=D vq=SU S=Uvq+ V=Zq Vv", +"qg= =Urv rv>=U Z=rV y=rV ?qt q&?t ?qD cq? ?1tq ?t:q ?1qD :q?c tq?M ?qtM& V?", +"q V?cq ?qlt ?t:lq l?Vq u?q tq* -qt qDt* cq- t1q* :qt- ?qD1* :c-q tMq* -Mtq ", +"t*Vq V-q tql* lt-q V?lq* uq- ?Etq ?qt&E tq?H ?Hcq ?qt1E ?t:qE ?qHt1 cq?:H ?", +"qtME tq&?ME ?HVq cq?VH l?qtE l?t:qE V?lqH ?quH tq?d td-q qHt* cqg? ?qdt1 -q", +"t:d ?qD1d cq-g: ?qdtM -qtdM ?dVq V?gq l?dtq l-qtd V?dlq g?uq t4?q ?qt4& ?4q", +"D c4?q ?q7t ?q7t: 7q?D ?q7c ?qt4M tq4?M& V4?q cq?V4 ?q7lt :ql7t? ?q7V ?qu7 ", +"t=q -qt= ?q=D cq= q=7t Aqt q=D7t Aqc tq=M -qt=M Vqt= V=cq ltq= ltAq V=q7t A", +"uq ?qt4E t4&?qE ?qHt4 cq?4H ?q7tE :q?7tE 7tH?q 7cq?H tq4?ME M&?Etq4 V?q4H V", +"?4cqH l?q7tE lE7t:q? 7V?qH u?q7H tdq= -qt=d tq=H t=gq 7tdq= tqAd t=H7q gqAt", +" t=qdM t=d-qM V?dq= cq=gV l=tqd Aqdlt V?d7q uqAg ?tP ?&tP tP?D ?Pcq ?1tP t:", +"?P ?qD1P cq?:P S?t S&?t S?V S?c l?St ?tS: SlV? u?S t*?P ?Pt- ?qDP* cP-q ?tP", +"1* -qt:P ?D1tP* cq-:P S*?t St-q S*V? cqS- St*l? l-qSt S?Vl* S?u- tq?W ?qWt&", +" tH?P ?Wcq ?qWt1 :qW?t ?qW1D cqW?: ?WSt S?tW& tZS? Sc?Z l?WSt S?:tW S?Zlt S", +"?uZ td?P tW-q ?qWDd ?tgP ?td1P -qWt: ?H1tdP cq-:W ?dSt -qWSt ?dSV S?gt S?dl", +"t -qWlt V?dSl g?uS ot? o&?t ?Dot ?cot o?7t ?to: ot?7D ot:?c otS? ot?S& oV? ", +"ocS? r?t ?tr: r?V ur? t=o ot- =Dot oct= 7ot= Aot t=o7D otAc S=t t=S- oVt= t", +"=Sc rt= Art t=rV Aru ?Wot ot?W& ?Hot otc?H ot?7W ot:?W otH7? ot:?H ot?SW S?", +"Wot& oV?Z oV?Zc ?trW r?t:W ?ZrV r?uZ ot?v o-tv =Hot t=go ?vq7t tvAo otH7= g", +"oAt tvS? ot-Sv tZS= ?vSc ?vrt rtAv ?vrV yAur Xn n!X XnB aXn Xn. 8Xn n.XB aX", +"8n XnK XKn! TXn aXTn Xjn Xj8n XjTn sXn Xn( +Xn n(XB aX+n n(X. 8X+n Xn(.B aX", +"n+8 n(XK Xn+K XnT( TX+n X(nj Xj+n XjnT( +Xsn Gn Gn! GnH Gan Gn. G8n G.Hn H8", +"Gn GnK n!GK GTn TnGa Gnj 8nGj HnGj sGn Gnd +Gn dXHn gXn G.dn G8+n dX.Hn G8g", +"n GKdn GK+n TdGn GTgn dnGj Gn+j dXjHn sGgn 4X 4X! 4XB 4aX 4X5 48X 4B5X 5a4X", +" 4XK XK4! 4TX TX4a 4Xj 8X4j 5T4X s4X 4X; 4+X 4BX; +X4a X;5n >X4 X;B5n 4a>X ", +"4KX; 4K+X T;4X +T4X X;4j 4X>j X;j4T s4>X 4GX 4!Gn 4HX Ga4X G5n G84X H54X H8", +"4X 4KGn 4GX!K GT4X 4GTaX Gn4j 4G8Xj HX4j 4GsX 4dX +G4X HX4d g4X 5d4X >nG 4H", +"X5d g4>X 4KdX 4+GXK Td4X 4TgX dX4j Gn>j dXj4H s4gX XPn XPn! PnXB XPan PnX. ", +"XP8n XPn.B aXP8n QXn n!QX TXQn aXQn XjQn 8XQn XjnTP QXsn PnX( XP+n XPn(B aX", +"P+n XPn(. +X8Pn Pn(X.B +X8aPn XnQ( QX+n QXnT( +QXTn XjnQ( +QX8n QXTnj( sX+Q", +"n Yn Yn! YHn Yan Yn. Y8n HnY. H8Yn YQn QnY! YZn ZnYa Yjn Q8Yn ZnYj sYn Ydn ", +"Y+n HnYd gYn dnY. +nY8 YHdn. Y8gn QdYn +QYn YdZn YZgn dnYj +nYj YZdnj gnsY ", +"oX o!X oXB oaX o5X o8X 5XoB 5aoX oQX QXo! oTX QaoX opX pXo8 pXoT osX oX; o+", +"X X;oB +Xoa 5Xo; o>X o5X;B oa>X QXo; +QoX TXo; +ToX o;pX op>X opTX; >Xos oY", +" oY! oYH Yao Y5o Y8o oHY5 oHY8 oYQ YQo! oZY YaoZ Ypo opY8 oZYp sYo Yvo Y+o ", +"oHYv goY ovY5 Y>o YvoH5 Y>go YQov YQo+ YvoZ oZgY opYv o>Yp YZvop yoY Xn# X&", +"n n#XB X&an n#X. X&8n Xn#.B aXn8& n#XK XK&n XnT# X&Tn X#nj &nXj XjnT# X&sn ", +"n#X( X&+n Xn#(B aXn+& Xn#(. X&n+8 n#(X.B +X8an& Xn#(K X&n+K TXn#( +TX&n Xjn", +"#( X&j+n TX#nj( sX+&n Gn# G&n G#Hn H&Gn n#G. 8nG& GnH#. G8nH& n#GK GK&n G#T", +"n T&Gn G#nj &nGj GTnj# G&sn G#dn G&+n dX#Hn G&gn dX#n. dX&8n HX#dn. gX8&n d", +"X#nK dX&nK GTnd# gXT&n dXjn# dX&nj dX#Hnj gsX&n 4X# 4X& X#4B aX4& 4#5X 5X4&", +" 4X5#B 48Xa& X#4K 4KX& 4#TX TX4& 4#Xj X&4j 4TX5# 4Xs& 4#X; +X4& X;#4B aX;4&", +" X;#5n 4X>& 5X#;nB >X4a& X;#4K X;&4K 4TX;# X;&4T X;j4# >X4&j X;#5Tn >Xs4& 4", +"#Gn G&4X 4#HX HX4& G#5n 5&Gn 4HX5# 4H8X& 4GX#K 4G&XK 4GTX# 4GTX& 4GjX# X&j4", +"G 4HXj# s4GX& 4#dX dX4& 4HXd# 4Xg& 4dX5# G&>n dX#H5n >Xg4& 4dX#K dX&4K 4TdX", +"# g4XT& dXj4# >nG&j dX#4Hj >nsG& PnX# XP&n XPn#B aXP&n XPn#. X&P8n Pn#X.B 8", +"XaP&n XnQ# X&Qn QXnT# QaX&n XjnQ# X&jQn QXTnj# sXQ&n XPn#( X&P+n Pn#X(B +Xa", +"P&n Pn#X(. +X8P&n .BX(Pn# aX+P8n& QXn#( +QX&n TX#Qn( QX&+Tn QX#nj( +QX&nj Q", +"#TnXj( +QXsn& Yn# Y&n HnY# H&Yn n#Y. 8nY& YHn#. Y8H&n QnY# Q&Yn Y#Zn ZnY& n", +"jY# &nYj YZnj# Y&sn dnY# +nY& YHdn# Y&gn Ydn#. Y+8&n dn#YH. gY8&n YQdn# Y+Q", +"&n YZdn# gYZ&n Yjdn# Y+j&n ZndYj# sYg&n oX# o&X X#oB aXo& 5Xo# 5Xo& o5X#B o", +"8aX& QXo# QXo& TXo# TXo& o#pX pXo& opTX# o&sX X;o# +Xo& oX;#B o+aX& o5X;# o", +"&>X X;#o5B o>aX& oQX;# o+QX& oTX;# o+TX& opX;# o>pX& pX;oT# os>X& oY# Y&o Y", +"#oH oHY& o5Y# o8Y& Y5oH# Y5ao& oQY# YQo& Y#oZ Y&oZ Y#op opY& YpoZ# Y&os Y#o", +"v ovY& YvoH# Y&go Yvo5# Y&o> ovHY5# goY>& YvoQ# Yv&oQ YZvo# goZY& Ypov# Yp&", +"o> oZvYp# Y&yo In In! DXn aIn In. 8In D.In 8IDn InK IKn! TIn aITn Inj Ij8n ", +"InDj sIn In( +In D(In DX+n n(I. 8I+n In(D. +DX8n n(IK +KIn T(In TI+n I(nj I", +"n+j InjD( +Isn GnI G!In Jn Jna G.In 8IGn Jn. J8n GKIn InKG! JnT TnJa InGj I", +"njG8 Jnj sJn Idn Gn+I Jnd gJn I.dn Id8n dnJ. J8gn dKIn Idn+K TdJn gTJn dnIj", +" Idn+j dnJj gnsJ 4IX 4!In 4DX DX4a 5In 8I4X 5D4X D84X 4KIn InK4! DX4T 4DXTa", +" In4j Inj48 DX4j 4DsX I;n +I4X D;4X +D4X I;5n >nI I;n5D 4D>X IK;n I;n+K I;T", +"n a;nTI ;nIj In>j I;n5T sI>n Gn4I In!4G J4X 4aJn 5IGn 4G8In J5n 48Jn InK4G ", +"4GKIn! 4TJn J4TaX Inj4G 4G8Inj 4XJj J4sX Id4X Idn4+ J;n J4gX Id5n Gn>I 5dJn", +" J>n Idn4K IdK4+X T;Jn gJ4TX Idn4j >nGIj ;nJj >nsJ IPn n!IP IPDn IPan PnI. ", +"IP8n IPnD. D8XPn QIn Q!In DXQn aIQn IjQn 8IQn IPjDn QIsn PnI( IP+n IPnD( +D", +"XPn IPn(. +IP8n DP(In. D8P+In Q(In QI+n QDXn( +QDIn IPjn( IPj+n IP(Dnj sIP+", +"n YIn InY! YJn JnYa InY. 8IYn Y.Jn JnY8 QIYn YQIn! ZJn ZaJn InYj Y8QIn JnYj", +" YJsn IdYn +IYn YdJn YJgn IdnY. Y+8In YJdn. gYJ8n IdQn Y+QIn ZdJn ZJgn YjId", +"n Y+jIn YJjdn sJngY oI oI! oDI oaI oI5 o8I 5DoI 5aoI oIQ QIo! oTI QaoI opI ", +"pIo8 pDoI osI oI; o+I DXo; +DoI 5Io; o>I oD5I; oD>X QIo; +QoI TIo; +ToI o;p", +"I p>oI opDI; sIo> oYI YIo! oJ oJa oIY5 oIY8 oJ5 oJ8 YQoI oYQI! oJZ oaZJ oIY", +"p Y8opI oJp oJs ovI oIY+ oJv goJ o5vI oIY> J5ov oJ> oQvI Y+oQI JvoZ gZoJ pv", +"oI o>IYp opJv yoJ bXn I&n DXbn cXn b.In 8Ibn In.bD 8Icn bKIn IK&n TIbn TIcn", +" Inbj &nIj InjbT cXsn b(In +Ibn In(bD +Icn In(b. I&n+8 DX(bn. cX+8n In(bK I", +"&n+K bTXn( cX+Tn Injb( I&n+j Ij(bTn sInc+ bGn G&bn Jnb cJn b.Gn G8bn bnJ. J", +"8cn bKGn I&nGK bTJn cTJn Gnbj I&nGj bnJj cJsn Gnbd +Gbn bdJn cJgn Idnb. Idn", +"b8 Jnbd. cJng8 IdnbK Idn&K JnbTd cJngT Idnbj Idn&j Jnjbd sJngc 4bX bX4& bX4", +"D c4X b54X b84X 4bX5D 48cX 4KbX I&n4K bT4X 4TcX bX4j I&n4j 4bT5X c4sX b;4X ", +"b+4X I;nbD 4+cX I;nb5 4b>X 5DXb;n c4>X I;nbK I;&nK I;nbT c4X+T I;nbj >Xb4j ", +"4bTX;j c>Xs4 bG4X I&n4G 4bJn J4cX G5bn I&nG5 b5Jn J5cn 4bGXK InK4G& J4bTX c", +"J4TX 4bGXj 4bGX&j J4jbX sJ4cX bd4X Idn4& b;Jn c4gX Idnb5 bG>n J5nbd cJ>n 4b", +"dXK IdK;&n J;nbT cJ;Tn dXj4b >nbGj J;nbj J>nsc IPbn P&In IPnbD IPcn IPnb. I", +"P&8n bPDIn. cX8Pn QIbn I&Qn bQXDn QIcn IPjbn IP&nj bTPInj sIPcn IPnb( IP&+n", +" bPDIn( cX+Pn bP(In. +IPb8n IPbnD(. +IPcn8 bQXn( I&n+Q IP(bTn cQX+n IP(bnj ", +"b+QInj InbjQD( sI+cQn Ybn bnY& JnYb Ycn bnY. b8Yn YJbn. cnY8 bQYn YbQ&n ZnY", +"b ZnYc bnYj Yb8Qn YZbnj snYc bdYn b+Yn YJbdn gnYc Ybdn. Yb+8n JndYb. Ycg8n ", +"YbQdn Yb+Qn YZbdn ZcngY Ybjdn Yb+nj YbdJnj sYcgn obI oI& bXoD ocI b5oI b8oI", +" ob5DX o8cX bQoI QIo& bToI oTcX pboI pIo& opbDX sIoc bXo; b+oI obDI; o+cX o", +"b5I; ob>X b5XoD; >Xoc obQI; ob+QI obTI; oc+QI opbI; o>bpI ob;pDX osc>X Ybo ", +"obY& oJb Yco obY5 obY8 J5ob Y5oc YQob YboQ& YboZ ZcoJ opYb Ypbo8 obJp osYc ", +"ovYb obY+ obJv Ycgo Yvbo5 Ybo> oJvb5 ocY> YvboQ Ybo+Q oJZvb ocZgY Ypbov Y>b", +"op oJpvb Ycyo 6X 6X! 6XB 6aX 6X1 68X 6B1X 1a6X 6XK XK6! 6TX TX6a 6Xj 8X6j 1", +"T6X s6X 6X( 6+X X(6B +X6a 6(1X 1+6X 6X1(B 6+X1a X(6K 6K+X 6(TX +T6X 6(Xj +X", +"6j 6TX1( 6+sX 6GX 6!Gn 6HX Ga6X 1Gn G86X 1H6X Ga1n 6KGn 6GX!K GT6X 6GTaX Gn", +"6j 6G8Xj GT1n 6GsX 6dX +G6X HX6d g6X 1d6X +G1n 6HX1d 68gX 6KdX 6+GXK Td6X 6", +"TgX dX6j dXj6+ dXj6H s6gX 4X6 6!4X 4B6X 6a4X 7X 7X8 7XB 7aX 4K6X 4X6!K 6T4X", +" 4TX6a 7Xj 8X7j 7TX 7sX 6X; 6+4X 6BX; aX6; 7X; >X7 X;7B 7a>X 6KX; 4+X6K TX6", +"; aX;6T X;7j >j7X TX7; >X7s 6G4X 4G6n! 6H4X 4Ga6X 7GX G87X 7HX Ga7X 4G6XK 6", +"GK4X! 4GT6X 6GT4aX Gn7j 7G8Xj GT7X 7GsX 6d4X 4+G6X HX6; 4Xg6 7dX 7G>X HX7d ", +"g7X 4dX6K X;K6+G 4Td6X g4X6T dX7j 7>GXj Td7X gX7s 6XP XP6! XP6B aX6P 1X6P 8", +"X6P 6X1PB 68XaP 6QX Q!6X QX6T Qa6X 1Q6X Q86X 6QX1T 6QsX XP6( +X6P 6XP(B 6+X", +"aP 6X1P( 6+X1P 1X(6PB 1+X6aP 6(QX +Q6X 6QXT( 6+QTX 6QX1( 6+Q1X Xj(6TP s6X+Q", +" Y6 Y6! Y6H Y6a Y61 Y68 6HY1 6HY8 Y6Q 6QY! YZ6 Z6Ya Y6j 6QY8 Z6Y1 sY6 Y6d Y", +"6+ 6HYd gY6 6dY1 6+Y1 Y61Hd Y6g1 6QYd 6+YQ YdZ6 YZg6 6dYj 6+Yj YZ61d g6sY o", +"6X 6Xo! 6XoB 6aoX 7oX o87X oX7B oa7X 6QoX o!6QX 6ToX oT6aX 7pX 78pX oT7X 7o", +"sX 6Xo; 6+oX o6X;B o+6aX oX7; 7o>X 7oX;B 7oa>X QX6; o+6QX oT6X; o+T6X 7;pX ", +"7p>X 7oTX; os7>X Y6o o!Y6 oHY6 oaY6 7Y 7Y8 7YH 7Ya o6YQ Y6oQ! Y6oZ YZ6oa 7Y", +"p Y87p 7ZY sY7 Yv6 o+Y6 YHv6 Yvg6 7Yv Y>7 7HYv g7Y YQv6 Yv6+Q ZvY6 gY6oZ 7v", +"Yp 7pY> YZ7v y7Y 6X9 6X& 6B9X 9a6X 1X9n :X6 6X19B 6a:X 6K9X 6KX& 9T6X TX6& ", +"9X6j 6X:j 6TX9j :Xs6 6(9X 9+6X 6X9(B 6+X9a 6X19( 6+:X 1X(9nB :X6+a 6X9(K 6+", +"X9K 6TX9( 6+T9X 6Xj9( :X6+j Xj(9Tn s:X6+ 9Gn G&6X 9H6X Ga9n 1G9n :Gn 6HX91 ", +"6H:X 9KGn 6G&XK GT9n 6GTX& Gn9j Gn:j 6HX9j :Gsn 9d6X +G9n 6HX9d 6Xg9 6dX91 ", +"6d:X 1HX9dn :Ggn 6dX9K dX&6K 6Td9X g6X9T dXj9n :Gn+j 9GTdnj g:Xs6 49X 6X4& ", +"4B9X 9a4X 7X9 7:X 9X7B :X7a 4K9X 49X&K 9T4X 49TaX 9X7j 7j:X 9T7X 7:sX 9X6; ", +"9+4X 49X;B aX;49 9X7; 7:>X 7X9;B 7:X>a 49X;K X;&6K 49TX; X;&6T 7X9;j 7:X>j ", +"7TX9; 7sX:> 9G4X 49GX& 9H4X 49GaX 9G7X :G7X 9H7X :H7X 49GXK X&K49G 49GTX 6G", +"T4X& 7G9Xj 7:GXj 7GT9X 7sG:X 9d4X dX&49 49HdX 49gX 9d7X :G>n 7HX9d 7:gX 49d", +"XK dXK49+ 49TdX g49TX 7dX9j 7:dXj 7Td9X g7Xs: 9X6P X&6P 6XP9B 6aX9P 6X19P 6", +"X:P 9X16PB :X6aP 9Q6X QX6& 6QX9T 6Qa9X 6QX9j 6Q:X 9TX6Pj :QXs6 6XP9( 6+X9P ", +"9X(6PB 9+X6aP 9X16P( :X6+P 6(9X1PB 6+X:Pa 6QX9( 6+Q9X 6Q(9TX 9+Q6TX Xj(9Qn ", +":QX6+ 6(QX9T1 sX+:Qn Y69 Y6& 6HY9 6HY& 9nY1 Y:6 Y691H :HY6 6QY9 6QY& Z6Y9 Z", +"6Y& 6jY9 :QY6 YZ69j s6Y: 6dY9 6+Y9 Y69Hd Y6g9 Y691d :dY6 9HnY1d g6Y: Y69Qd ", +"Y6+9Q YZ69d gY6Z9 Y6j9d Y:6+Q Y9dZ1n sY:g6 o9X 6Xo& 9XoB 9aoX o97X o:X 7o9X", +"B oa:X 9QoX o9QX& 9ToX o9TaX p97X 7X:p 7oT9X sXo: 9Xo; 9+oX o9X;B o9+aX 7o9", +"X; >Xo: o9;7XB o:>aX o9QX; o9+QX o9TX; o9+TX 7pX9; o:p>X 7T;p9X os:>X Y9o o", +"9Y& oHY9 o9Ya 7Y9 Y:o 7HY9 Yao: YQo9 Y6&oQ Y9oZ oZ9Ya Y97p Ypo: Y97Z Y:7Z o", +"vY9 o9Y+ Yv69H Y9go Y97v :v7Y 7Yv9H Y:go Yv69Q Yv6Q& oZ9Yv go9YZ Yp97v Y:op", +"v 7ZYv9 Y:yo 6IX 6!In 6DX DX6a 1In 8I6X 1D6X D86X 6KIn InK6! DX6T 6DXTa In6", +"j Inj68 DX6j 6DsX 6(In +I6X 6(DX +D6X 1(In +I1n 6DX1( 6+D1X In(6K 6+IXK 6DX", +"T( 6+DTX Inj6( Inj6+ 6DXj( sIn6+ Gn6I In!6G J6X 6aJn Gn1I 6G8In J1n 68Jn In", +"K6G 6GKIn! 6TJn J6TaX Inj6G 6G8Inj 6XJj J6sX Id6X Idn6+ 6dJn J6gX Id1n Idn6", +"8 1dJn J1gn Idn6K IdK6+X J6TdX gJ6TX Idn6j 6+GInj J6jdX sJ6gX 6I4X In!46 6D", +"4X 4DX6a 7IX 8I7X 7DX D87X InK46 6IK4X! 4DX6T 6TI4aX In7j 7I8Xj DX7T 7DsX I", +";6X I;n6+ DX6; aX;6D I;7X 7I>X DX7; 7D>X I;n6K I;K6+X I;n6T 6+D4TX 7IX;j 7>", +"IXj 7DXT; 7sI>X 4G6In 4G6In! 4XJ6 J46aX Gn7I 7G8In 7JX J87X 4G6InK 6!4GInK ", +"J46TX 4TXJ6a 7GIXj Inj7G8 Jn7T sX7J Idn6; 6+GI;n 6XJ; gJ46X Id7X 7>GIn J;7X", +" >X7J IdK6X; Id4X6+K J6;TX J4Tg6X 7IdXj >Ij7dX 7JTdX J>n7s 6IP 6!IP DX6P aI", +"6P 1I6P 8I6P 6DX1P 6D8IP QI6X 6QIn! QD6X 6QDaX QI1n IPj68 IPj6D 6IsP 6(IP +", +"I6P 6DXP( 6+DIP 6IP1( 6+I1P IP(1Dn 1+D6IP 6QIX( 6+QIP 6QDX( 6+QDX IPj6( IPj", +"6+ IP(6Dj sIP6+ Y6I 6IY! YJ6 J6Ya 6IY1 68YI J6Y1 J6Y8 6QYI Y6QI! ZJY6 YZ6Ja", +" 6IYj Y68QI J6Yj YJs6 6IYd 6+YI YdJ6 YJg6 Y61Id Y6+1I YJ61d gY6J1 Y6QId Y6+", +"QI YZ6Jd gY6ZJ Y6jId Y6+Ij YJ6dj sY6gJ oI6 6Io! 6DoI 6aoI 7oI o87I oD7I oa7", +"I 6QoI oIQ6! 6ToI oTI6a op7I 7oIp8 oT7I 7osI 6Io; 6+oI oD6I; o+D6I 7;oI 7Io", +"> 7oDI; o>D7I oIQ6; o+I6Q oTI6; o+T6I 7oIp; o>I7p 7oTI; osI7> oIY6 Y6oI! oJ", +"6 oaJ6 7YI 7IY8 7Jo Y87J Y6oQI oIQY6! Z6oJ oJZ6a 7IYp 7Yp8I oJ7Z 7JsY v6oI ", +"Yv6+I Y6Jv g6oJ 7IYv 7IY> 7voJ 7Jgo Yv6QI Y6Qo+I oJZv6 goJZ6 7vIYp Y>7pI oJ", +"p7v 7Jyo b6X 6&bX 6DbX c6X 6Xb1 :In b6X1D c6:X bK6X I&n6K 6TbX 6TcX 6Xbj In", +":j Inj9D :Isn b(6X 6+bX b6XD( 6+cX b6X1( +I:n b6(1DX :cX6+ b6X(K b6+XK b6TX", +"( c6X+T Inj9( :In+j In(b1T sIn:+ 6GbX I&n6G J9n J6cX 1Gbn bG:n b1Jn :Jn b6G", +"XK I&K9Gn 9TJn cJ6TX Inj9G :Gnbj 9nJj sn:J 6dbX Idn6& 9dJn c6gX Idnb1 Id:n ", +"J9n1d gn:J Idn9K IdK6X& J9nTd c6XgT Idn9j :Idnj J9ndj :Jngs b64X I&n49 9D4X", +" 49cX 7bX :I7X bX7D 7cX 4bX6K I&K49X 4bT6X c4X6T bX7j 7:IXj bT7X sX7c 6Xb; ", +"I;&6X I;n9D 6Xc; bX7; 7b>X 7bXD; >X7c I;n9K I;K6X& I;n9T c6XT; 7bX;j 7>bXj ", +"7bTX; 7cXs> 4bG6X 49GI&n 49Jn cJ46X bG7X 7:GbX J97X 7X:J InK49G 4KbG6X& J49", +"TX c4TJ9n 7bGXj 7bj:Gn 7JbTX 7Js:X Idn49 49+Idn 9;Jn cJ6X; bd7X 7:IdX 7JbdX", +" >n:J IdK49X dX4&b6K J9;Tn c4Tg6X 7bdXj >Ij:Gn 7bTdX J>ns: 6IbP IP6& b6XDP ", +"6IcP b6X1P 6I:P 6DXb1P :cX6P 6QbX IP&6Q b6QDX 6QcX IPjb6 QI:n b6TIPj sIP:n ", +"b6XP( IP&6+ IP(9Dn c6X+P IP(b1n :IP6+ b(6X1DP :I+c6P b6QX( b6+QX IP(b6T c6Q", +"+X IP(b6j :QI+n 6XbjQD( sI+:Qn Yb6 b6Y& J6Yb Yc6 b6Y1 :IY6 YJ6b1 Y6:J b6YQ ", +"Yb6Q& ZbY6 Y6Zc b6Yj Y:b6Q YZb6j s6Yc b6Yd b6Y+ YJ6bd g6Yc Yb61d Y:b6+ YbdJ", +"1n Y:Jg6 Yb6Qd Yb6+Q YZb6d Yc6gZ Yb6dj Yb6+j YbdZ1n sYcg6 o9I 6Io& 9DoI o9c", +"X ob7I o:I 7obDX 7Ioc 9QoI o9IQ& 9ToI oc6QI pb7X op:I 7obTI sIo: 9Io; 9+oI ", +"o9DI; oc6+I 7obI; :Io> 7D;o9I o:Ic> o9IQ; o9+QI o9TI; c6QX; 7pbX; o:Ip> 7b;", +"oTI os:>I obY6 Ybo6& oJ9 Y6oc 7Yb Ybo: Yb7J Yc7 Ybo6Q Y6QoI& Z9oJ oJ9Zc Yb7", +"p o:IYp Yb7Z Yc7Z vbY6 Yvb6+ o9Jv g9oJ Yb7v 7>Yb oJ97v Ycg7 Yvb6Q YbQo9+ oJ", +"9Zv oJ9gZ 7Ybpv :vIop oJpv9 Ycy7 RXn n!RX XnRB RXan XnR. RX8n RXn.B aXnR8 X", +"Mn n!XM XMTn XMan MnXj XM8n XMnTj XMsn XnR( RX+n RXn(B aXnR+ RXn(. R+8Xn Xn", +"(R.B R+8aXn X(Mn XM+n XMnT( aXM+n XMnj( R+jXn RTXnj( sX+Mn RGn R!Gn GnRH Gn", +"Ra R.Gn GnR8 RGHn. RG8Hn GMn G!Mn GnRT aMGn GnRj 8MGn RGTnj RGsn GnRd +nRG ", +"RGHdn RGgn dX.Rn R+G8n RGHdn. gRG8n dMGn GM+n dXMHn GMgn dXMnj dXM8n RGTdnj", +" gsRGn R4X 4!RX RB4X 4XRa 4XR5 4XR8 R45XB R48aX 4XM 4!XM 4XRT aX4M 4XRj 8X4", +"M R4T5X R4sX 4XR; 4XR+ X;BR4 aX;R4 R45X; R4>X R45X;B >XR4a X;4M +X4M XM;4T ", +"aXM4+ XM;4j 4X>M R4TX;j >XsR4 R4G 4!RG 4GRH 4GRa 4GR5 4GR8 R4GH5 R4GH8 4MRG", +" R4GM! 4GRT aXM4G 4GRj R4G8M R4GHj R4sG 4GRd 4+RG R4GH; R4gX R4G5d R4>G 4HX", +"R5d >XgR4 dX4M dXM4+ dXM4H 4XgM dXM4j GM>n R4HdXj >Xg4M XPRn RXPn! RXPnB aX", +"PRn RXPn. R8XPn XPnR.B R8XaPn SXn n!SX TXSn aXSn XjSn 8XSn STXnj SXsn RXPn(", +" R+XPn XPnR(B R+XaPn XPnR(. +XPR8n .BR(XPn aX+PR8n XnS( +XSn STXn( S+TXn SX", +"nj( S+8Xn XjnST( Ss+Xn YRn RnY! RHYn RaYn RnY. R8Yn YRHn. Y8RHn SYn YnS! Zn", +"SY SnYa SnYj SnY8 SZYnj SYsn RdYn R+Yn YRHdn YRgn YRdn. Y+R8n dn.YRH gYR8n ", +"YdSn SnY+ SZYdn SYgn SYdnj SY+8n YZnSdj gSsYn oRX RXo! RXoB RaoX R5oX R8oX ", +"oR5XB o8RaX SoX o!SX SXoT SXoa pXSo SXo8 SpToX sXSo R;oX R+oX oRX;B o+RaX o", +"R5X; oR>X X;BoR5 o>RaX oXS; SXo+ S;ToX Sa;oX SpXo; So>X opTS;X os>SX oYR YR", +"o! YRoH YRoa oRY5 YRo8 Y5oRH Y5aoR SoY S!oY SYoZ YaSo SoYp Y8So SZpoY SosY ", +"vRoY YRo+ YvoRH oYgR YvoR5 >RoY ovRY5H goY>R YvSo Y+So YZvSo SogY SvoYp SoY", +"> YpoSvZ SoyY XnR# RX&n RXn#B aXnR& RXn#. X&nR8 Xn#R.B R8aX&n X#Mn MnX& XMn", +"T# aXM&n XMnj# XM&8n RTXnj# sX&Mn RXn#( X&nR+ Xn#R(B R+aX&n Xn#R(. R+8X&n .", +"BR(Xn# aXR&+n8 XMn#( XM&+n TX#Mn( +TXM&n Mn#Xj( +XM&nj XMTnj#( XM&sn+ R#Gn ", +"GnR& RGHn# RGa&n RGn#. RG8&n Gn#RH. G8nRH& G#Mn M&Gn RGTn# aMnG& RGjn# M&nG", +"8 XM#Hnj sRG&n dX#Rn dX&Rn RGHdn# gRG&n RG#dn. RG8d&n dXHnR#. RG8gn& dXMn# ", +"dXM&n dX#HMn gXM&n dM#Gnj XM&dnj dXHjMn# sX&gMn R#4X 4XR& R4X#B R4aX& R45X#", +" R48X& 4X#R5B 4aXR5& 4#XM XM4& R4TX# aXM4& R4jX# XM&48 XM#5Tn sR4X& X;#R4 X", +";&R4 R4#X;B R4aX;& R45X;# >XR4& 5#X;R4B R4a>X& XM;4# XM;4& XM#T;n XM&a;n XM", +"#;nj >XM4& X;Rj4T# sX&>Mn R#4G 4GR& R4GH# R4GH& R4G5# R4G5& R4#H5X 4G8RH& R", +"4GM# XM&4G R4GT# XM&4H R4Gj# M&nG5 XM#4Hj sR4G& R4Gd# dX&R4 dX#R4H gR4G& dn", +"#RG5 >nGR& H#5nRG; >X&gR4 dXM4# dXM4& dX#R4T g4XM& dX#R4j >MG&n dX4MH5# sX&", +"gR4 RXPn# X&PRn XPnR#B RaXP&n XPnR#. R8XP&n .BP#RXn aXR&8Pn XnS# X&Sn STXn#", +" SaX&n SXnj# S8X&n XjnST# SsX&n XPnR#( R+XP&n XPRn#B( aXR&+Pn #(P.RXn 8X+PR", +"&n XnR(.BP# anR&8X+P SXn#( S+X&n TXnS#( +TXS&n XjnS#( X&jS+n XjSnT#( S+Xsn&", +" RnY# R&Yn YRHn# YaR&n YRn#. Y8R&n RHnY#. RH8Y&n YnS# SnY& SZYn# SZ&Yn SYnj", +"# SY8&n YjnSZ# sYS&n YRdn# Y+R&n dn#YRH gYR&n dn#YR. d&nY8R dnY#RH. gR8Y&n ", +"SYdn# SY+&n YZnSd# gSY&n YjnSd# S+nY&j dnYjSZ# sY&gSn RXo# R&oX oRX#B oaRX&", +" oR5X# o8RX& R5Xo#B R5ao&X oXS# SXo& oTXS# oT&SX SpXo# Sp&oX oTXSp# osSX& o", +"RX;# o+RX& X;#oRB aX;o&R X;#oR5 o>RX& X;o#R5B oaR>X& S;Xo# S;&oX oTXS;# S+X", +"oT& opXS;# S>oX& X;S#opT S>Xos& oRY# YRo& oYRH# YaoR& Y5oR# Y5&oR oHRY5# Y5", +"RoH& S#oY Y&So oZYS# oZ&SY YpoS# Yp&So oZSYp# sYoS& YvoR# Yv&oR oHRYv# goYR", +"& ovRY5# Y>oR& YRoHv5# Y>Rgo& SvoY# Sv&oY oZYSv# goSY& YpoSv# S>Yo& SZYpov#", +" yoYS& [n [!n [Dn [an [n. [8n Dn[. D8[n [Mn Mn[! V[n [aVn [jn 8M[n [jVn [sn", +" [n( [+n Dn[( +D[n n([. +n[8 [Dn(. [+D8n Mn[( +M[n [nV( [+Vn nj[( +n[j V[nj", +"( [+sn [Gn Gn[! Jn[ [aJn Gn[. G8[n [nJ. [8Jn GM[n [!GMn JnV JaVn Gn[j [8GMn", +" VnJj Jn[s [dn +G[n [dJn g[n dn[. 8d[n Jn[d. [8gn dM[n [+GMn VdJn JngV dn[j", +" [+jGn JnVdj gn[s [4 [4! [4D [4a [45 [48 4D[5 4D[8 [4M 4M[! V[4 [aV4 [4j 48", +"[j [4V5 [s4 [4; [4+ 4D[; 4+[a 5n[; [>4 [45D; [a>n 4M[; 4+[M [4V; [4V+ 4j[; ", +"[4>M V[4;j >n[s [4G 4G[! J4[ [aJ4 4G[5 4G[8 [4J5 [4J8 4G[M [4GM! V[J4 J4V[a", +" 4G[j [48GM V5Jn J4[s [4d 4+[d [4J; g[4 4d[5 [d>n J4[5d >ng[ 4d[M [4+GM V;J", +"n V[g4 4d[j [>4GM J4[dj g4[s [Pn n![P DP[n aP[n Pn[. 8P[n [DPn. [8DPn S[n [", +"!Sn VnS[ Sn[a Sn[j Sn[8 SV[nj S[sn Pn[( +P[n [DPn( [+DPn [Pn(. [+8Pn DPn[(.", +" +DP[8n [nS( Sn[+ SV[n( SV+[n S[nj( S[+8n VnjS[( [sS+n [Yn Yn[! [YJn Yn[a [", +"nY. Yn[8 YJ[n. YJ8[n Sn[Y S[Yn! ]n ]an Yn[j S[8Yn ]jn ]sn Yn[d Yn[+ YJ[dn [", +"Ygn [Ydn. Y+[8n [YdJn. g[Y8n Sn[d S[+Yn ]dn ]gn S[dnj S[8dn dn]j gs]n o[ o[", +"! o[D o[a o[5 o[8 [5oD [5oa o[S S!o[ oV[ Sao[ [po S8o[ [poV os[ o[; o[+ [;o", +"D [+oD o5[; [>o o[5D; oD[> S;o[ S+o[ V;o[ V[o+ [;op o[S> oVp[; [>oV o[Y o![", +"Y oJ[ Yao[ Y5o[ Y8o[ J5o[ J8o[ SYo[ o[YS! ]o ]oa o[Yp [poY8 ]op ]os [vo Y+o", +"[ [voJ go[ [5ov o[Y> oJ[v5 [>go o[Sv [voS+ ]ov ]og op[v [pvo> op]v ]yo [bn ", +"[&n bn[D [cn bn[. b8[n [bDn. [8cn bM[n M&[n [bVn V[cn bn[j &n[j V[bnj sn[c ", +"bn[( b+[n [bDn( [+cn [bn(. [+b8n bnD[(. [c+8n [bMn( [+bMn V[bn( [cV+n [jbn(", +" [+jbn [jbVn( [sc+n bG[n G&[n [bJn Jn[c [bGn. [8bGn Jn[b. [cJ8n GMbn [&GMn ", +"JbVn VJcn [jbGn [&jGn JnVbj [sJcn bd[n d&[n Jn[bd gn[c [dbn. [8dbn [dbJn. g", +"[c8n [dbMn [d&Mn JnVbd gVn[c [djbn [d&nj Jnb[dj g[scn [4b [4& 4b[D [c4 4b[5", +" 4b[8 [4b5D [4c5 4b[M 4M[& Vb[4 V[c4 4b[j 4&[j V[4b5 s4[c 4b[; 4b[+ [4bD; [", +"4c; [4b5; [&>n b5n[;D >n[c [4bM; [4+bM V[4b; [c4V+ [4jb; [>4bM V4b[;j V>[c4", +" 4b[G 4G[& Jb[4 J4[c [4bG5 [48bG J4[b5 [c4J5 [4bGM [4&GM J4V[b [c4VJ [4jbG ", +"[4&Gj J4[bj [s4cJ 4b[d 4d[& J4[bd g4[c [4db5 [>4bG J4b[5d J>n[c [4dbM [4dM&", +" J;Vbn g[4Vc [4dbj >MbGn J4b[dj J>Vcn bP[n P&[n [bDPn [Pcn [bPn. [8bPn bPD[", +"n. [c8Pn [bSn Sn[& SV[bn cnS[ S[bnj S[8bn V[nSbj [scSn [bPn( [+bPn bPD[n( [", +"c+Pn bPn[(. b+P[8n bD[(Pn. [+8cPn S[bn( S[+bn V[nSb( Sc[+n [jnSb( Sbn[+j nj", +"V(S[b Sc+[sn [bYn Yn[& YJ[bn [Ycn Yb[n. Yb8[n Jn[Yb. Yc[8n SnYb S[&Yn ]bn ]", +"cn SYbnj SYb8n bn]j sc]n Yb[dn Yb+[n Jn[Ybd g[Ycn [dnYb. Ybn[8d dnJ.Yb[ Yc8", +"g[n S[dbn S[d&n bd]n gc]n Sbn[dj SbnY+j ]jnbd ]cngs o[b o[& [boD oc[ [5ob [", +"5o& o[b5D c5o[ Sbo[ S&o[ V[ob o[Sc ob[p o&[p oVp[b [poc [;ob [+ob o[bD; c;o", +"[ o[b5; ob[> [5boD; [>oc S[;ob o[+Sb oV[b; oV+[c [pob; [p&o> [pboV; oV>[c Y", +"bo[ Y&o[ [Job o[Yc Ybo[5 o[8Yb oJ[b5 oJ8[c YbSo o[&SY ]ob ]oc [poYb [p&oY o", +"p]b os]c ob[v o&[v oJ[vb ocg[ [vob5 [v&o> [vboJ5 oJ>[c [voSb [v&So ov]b go]", +"c [pvob [pvo& ]opvb yo]c qX q!X qXB qaX q1X q8X 1XqB 1aqX qXM XMq! qTX TXqa", +" lXq q8lX qTlX sqX qX( q+X X(qB +Xqa 1Xq( 1+qX q1X(B q+1aX XMq( +XqM TXq( +", +"TqX qXl( q+lX lXqT( q+sX qG qG! qGH qGa qG1 qG8 1GqH 1Gqa qGM GMq! qGT GTqa", +" lGq q8lG qGlH sqG qGd q+G HXqd gqG 1Gqd 1+qG qG1Hd qGg1 GMqd +GqM GTqd qGg", +"M qGld q+lG lGqTd lGgq q4X 4Xq! 4XqB 4aqX 7qX q87X qX7B qa7X 4XqM q!4XM 4Tq", +"X qT4aX lX7 l87X 7TlX lX7s q;X 4+qX X;qB aXq; q;7X q>X 7qX;B qa>X XMq; q+4X", +"M TXq; q+T4X 7Xl; lXq> 7qTX; >Xsq qG4 4Gq! 4GqH 4Gqa 7qG q87G qG7H qG7a 4Gq", +"M qG4M! 4GqT qGT4a 7qlG 7qGl8 7HlX 7qsG qG; 4+qG G;qH qGg4 qG7d q>G 7qGH; >", +"Xgq GMq; q+G4M GTq; gq4GT 7dlX lGq> 7qGTd lXg7 qXP XPq! XPqB aXqP 1XqP 8XqP", +" q1XPB q8aXP SqX q!SX SXqT SXqa SqlX SXq8 SlqTX SqsX XPq( +XqP qXP(B q+aXP ", +"q1XP( q+1XP 1XPq(B 1+XqaP qXS( SXq+ SqTX( Sq+TX SlqX( Sq+lX lXTSq( sqS+X Yq", +" Yq! YqH Yqa Yq1 Yq8 qHY1 qHY8 YqS S!Yq ZqY SaYq lYq S8Yq ZqlY sqY Yqd Yq+ ", +"YHqd gqY qdY1 q+Y1 Yq1Hd g1Yq SdYq S+Yq qdYZ gYZq ldYq l+Yq lYZqd gqlY oqX ", +"oXq! qXoB oXqa oq7X oXq8 7oqXB 7oaqX SXoq SqXo! oXqT SqaoX rX r8X rTX rsX o", +"Xq; oXq+ oqX;B oq+aX 7oqX; oq>X oq;7XB q>oaX SXq; Sq+oX Sq;TX SqaX; r;X r>X", +" TXr; >Xrs Yqo o!Yq oHYq oqYa 7Yq Y87q 7HYq Ya7q SoYq YqoS! oZYq YqaoZ rY r", +"Y8 rZY rsY vqY Y+vq qHYv gYvq vq7Y Y>q vq7YH Y>gq YqSv vqYS+ YZvq goqSY rYv", +" rY> YZrv yrY q9X q&X 9XqB 9aqX 9Xq1 :qX q91XB qa:X 9XqM XMq& 9TqX TXq& q9l", +"X lX:q lXq9T sX:q 9Xq( 9+qX q9X(B q+9aX q91X( q+:X 9X1q(B :q+aX q9XM( q+9XM", +" qT9X( q+T9X lXq9( :ql+X qT9lX( sq:+X qG9 qG& 9GqH 9Gqa 9Gq1 :qG qG91H qG:H", +" 9GqM GMq& 9GqT GTq& l9qG lG:q lGq9H sG:q 9Gqd 9+qG qG9Hd qGg9 qG91d q+:G 9", +"HXqd1 gX:q qG9dM q+G9M qGT9d gq9GT lGq9d :qGl+ lG9qTd glq:G 49qX 4Xq& q49XB", +" qa49X q97X 7X:q 7q9XB 7qa:X 9X4M q&4XM qT49X qT&4X l97X 7:lX 7qT9X 7sX:q 9", +"Xq; X;q& q;9XB qa;9X 7q9X; >X:q q;97XB q>:aX q;9XM q;&XM qT;9X qT;X& lX79; ", +"q>l:X 7T9lX; sq>:X 49qG 4Gq& qG49H qGa49 79qG 7G:q 7qG9H :qG7H qG49M qG&4M ", +"qGT49 qGT4& 7qGl9 :qG7l 7qG9T sqG7: 9Gq; G;q& qG;9H gq49G 7qG9d :Gq> 7G9qH;", +" q>Gg: qG;9M qG;M& qGT9; g49XM 7dXl9 q>Gl: 7G9qTd q>Gs: 9XqP X&qP q9XPB qa9", +"XP q91XP qX:P 9X1qPB :qaXP q9SX SXq& Sq9TX Sqa9X Slq9X :XSq lXqS9T sq:SX q9", +"XP( q+9XP 9XPq(B 9+XqaP 9X1qP( :q+XP 91q(XPB q+a:XP Sq9X( Sq+9X qTXS9( S9Xq", +"+T lXqS9( S:q+X l9qTSX( S:+sqX Yq9 Yq& qHY9 qHY& q9Y1 Y:q Yq91H Ya:q S9Yq S", +"&Yq Y9Zq Y&Zq lqY9 YqS: lYZq9 Y:sq qdY9 q+Y9 Yq9Hd g9Yq Yq91d Y+:q qH9Y1d Y", +":gq SqdY9 Yq+S9 ZqY9d gqZY9 lYq9d lY:q+ Zq9lYd glY:q q9oX oXq& oq9XB oqa9X ", +"7oq9X oq:X oq97XB o:qaX SXo9 Sq&oX oqT9X S9aoX r9X r:X 9TrX s:rX oq9X; oq+9", +"X q;Xo9B o9Xqa; oq97X; o:q>X 7Boq9X; q>ao:X Sq;9X Sq;X& S9XqT; S9Xo+T 9Xr; ", +">Xr: r;X9T r>Xs: oqY9 oqY& Y9oqH Yqao9 Y97q Yqo: 7Yq9H o:HYq Y9So Yq&So oZ9", +"Yq Zq&oY rY9 rY: YZr9 sYr: Y9vq Y&vq vqY9H vq&gY vq7Y9 vqY: 7Y9vqH go:Yq vq", +"YS9 vq&SY ZqvY9 vq&YZ Yvr9 Y:rv rZYv9 rYy: [q [q! [qD [qa [q1 [q8 qD[1 qD[8", +" [qM [Mq! Vq[ [aVq [lq q8[l [lVq sq[ [q( [q+ [(qD q+[a [(q1 q+[1 [q1D( [q+1", +"D [(qM [Mq+ V([q [qV+ l([q [ql+ Vql[( [+sq [qG [!qG Jq Jqa qG[1 qG[8 Jq1 Jq", +"8 [MqG [qGM! JqV qaVJ [qlG [lqG8 Jql sqJ [qd q+[d Jqd gqJ qd[1 q8[d qdJ1 g1", +"Jq [Mqd [q+GM VdJq gVJq [qld [l+qG ldJq sqg[ [q4 q![4 qD[4 qa[4 7[ 7[8 7[D ", +"7[a q4[M [q4M! [qV4 Vq[4a 7[l [87l 7V[ 7[s [q; q+[4 qD[; qa[; 7[; 7[> [;7D ", +"7D[> [Mq; [q+4M [qV; Vq+[4 l;7[ l>7[ V;7[ V>7[ qG[4 [q4G! Jq4 qaJ4 7[G [87G", +" 7Jq 7aJq [q4GM qGM[4! J4Vq JqV4a lG7[ 7[lG8 Jq7V 7Jsq qG[; [q+4G Jq; g4Jq ", +"7[d 7G[> 7dJq g7[ [qd4M [4Mq+G J;Vq Jq;gV ld7[ [>l7G l;Jq 7Vg[ [qP [Pq! [Pq", +"D [Pqa [Pq1 [Pq8 [q1DP [q8DP Sq[ S![q S[Vq [qSa Sq[l [qS8 SlV[q Sq[s qP[( [", +"Pq+ [qDP( [q+DP [q1P( [q+1P qDP[1( [1Pq+D S([q [qS+ Sq[V( Vq+S[ Sq[l( [l+Sq", +" [lSVq( sq[S+ Yq[ [!Yq JqY YaJq [qY1 [qY8 Y1Jq Y8Jq S[Yq Sq[Y! ]q ]qa [lYq ", +"Yq8[l ]lq ]qs [dYq [qY+ qdYJ gYJq Y1[qd Yq+[1 JqY1d Jq8gY [qSd Yq+S[ ]qd ]q", +"g lYq[d lY+[q ld]q gl]q o[q o![q [qoD [qoa 7[o o87[ 7Do[ 7ao[ Sqo[ Sq[o! Vq", +"o[ oVq[a r[ r[8 rV[ r[s [;oq [qo+ o[qD; o[+qD [;7o o[q> 7[oD; [>o7D [qS; o[", +"+Sq oVq[; oV+[q r[; r[> V[r; V>r[ o[Yq Yqo[! oJq oaJq 7[Y Y87[ oJ7[ Ya7[ Sq", +"[oY o[SYq! ]oq oq]a r[Y Y8r[ ]r ]rs vq[ [+vq Jqv oJgq 7[v 7[Y> vq7J 7[go Sq", +"[v vq[S+ ]qv go]q r[v Y>r[ ]rv ]yr tq[ [q& qD[t cq[ [1tq [:q tq[1D [:cq tM[", +"q [Mq& V[tq [cVq tq[l [l:q Vql[t uq[ t([q [qt+ tq[D( [+cq tq[1( [+:q [1Dtq(", +" cq[:+ tq[M( tq+[M Vq[t( cqV[+ [lqt( [:lq+ [ltVq( [qu+ tqG [&qG Jqt cqJ t1q", +"G tG:q J1tq :Jq tMqG tqGM& VqtJ cJVq tqlG [:lqG ltJq uJq [qtd q+tG tdJq cqg", +"[ tqG1d [q:d Jqt1d :Jgq tqGdM tq+GM JqVtd cqJgV [ldtq [:dlq Jqltd gquJ t4[ ", +"[&t4 tD[4 t4[c 7[t 7[: [t7D 7[c tM[4 t4[M& V[t4 cqV[4 [l7t l:7[ V[7t u7[ [q", +"t; [4t+ tq;[D [qc; t;7[ 7[t> 7[tD; [>7c tq;[M t4[+M Vq;[t cq;V[ [l;7t [>l7t", +" 7V[t; 7[u> [4tG tqG4& t4Jq c4Jq tG7[ 7G[: tJ7[ :J7[ tqG4M t4MqG& JqVt4 cqJ", +"V4 7tG[l 7[:lG Jql7t 7Juq [4td tq+4G t;Jq t4g[ td7[ tGq> Jq;7t 7[gt tqGM; t", +"4M[d& Jq;Vt cqJV; 7[dlt [:d7l Jqlt; g7u[ [qtP [Pq& tq[DP [qcP tq[1P [q:P [q", +"1tPD cq[:P tqS[ [qS& Sq[Vt Sq[c Sq[lt Sq[: VqlSt[ Squ[ tq[P( tq+[P [qDtP( c", +"q[+P [q1tP( [:q+P t1qD[P( [:+cqP Sq[t( St+[q Vq[St( Scq[+ [lqSt( S:[q+ SlVq", +"[t( uS+[q tYq Y&tq JqtY Ycq Y1tq Yq[: JqtY1 cqY: YqSt Yq&St ]qt ]qc ltYq lY", +"t:q lt]q ]uq tdYq t+Yq JqtYd tYgq tYq1d [:dYq tY1Jqd g[:Yq StdYq tY+Sq tq]d", +" gq]t lYtqd [:dSq ]lqtd ]qug ot[ [qo& [toD o[cq ot7[ o:[ ot7[D oc7[ o[St ot", +"[S& V[ot oVt[c r[t r[: V[rt ur[ t;o[ t+o[ ot[D; otc[+ ot7[; [>ot 7[Dot; cq>", +"o[ ot[S; ot+S[ oVt[; oVt[+ t;r[ t>r[ rV[t; r[u> tYo Y&ot oJt otYc tY7 7[Y: ", +"7JtY 7[Yc SotY tYoS& ]ot ot]c rtY tYr: ]rt ur] vqtY Y+ot oJtv tYgo 7[tv 7Yt", +"> oJt7v tYg7 vq[St ot+SY tv]o go]t tYrv tYr> rt]v ]yur w w! wB wa w. w8 w.B", +" w8a wK w!K wT wTa wj w8j wTj sw w* w+ w*B w+a w*. w+8 *Bw. 8*wa w*K w+K wT", +"* w+T wj* w+j T*wj sw+ wG w!G wH wHa wG. w8G wH. wH8 wGK GKw! wHT GTwa wjG ", +"G8wj wHj swG fw fw+ fwH gw fw. fw8 f.wH gw8 fwK fKw+ fwT gwT fwj w+fj wHfj ", +"gws w4 w!4 w4B wa4 w5 w58 w5B w5a w4K 4Kw! wT4 4Twa w5j 48wj w5T sw4 w= w=+", +" w=B =aw w=5 w> =Bw5 w>a w=K =Kw+ =Tw w+=T =jw w>j w5=T sw> w4G 4Gw! wH4 4G", +"wa w5G 4Gw8 wH5 4Hw8 4GwK w!4GK 4GwT wH4Ta 4Gwj w5G8j 4Hwj wHs4 fw= w+f= =H", +"w gw= fw5 w>f wHf5 gw> =Kfw fw=+K fw=T =Tgw fw=j fjw> wH=j s=gw wP w!P wPB ", +"waP wP. w8P P.wB 8Pwa wQ wQ! wQT wQa wQj wQ8 TPwj swQ wP* w+P P*wB +Pwa P*w", +". +Pw8 wP*.B w+a8P wQ* w+Q Q*wT +QwT Q*wj +Qw8 wQT*j w+sP Yw Yw! YwH Ywa Yw", +". Yw8 Y.wH wHY8 YwQ Y!wQ Zw Zwa Ywj wQY8 Zwj sYw Yfw Yw+ wHYf gwY Y.fw fwY8", +" YfwH. Y8gw fwQ w+fQ Zwf gwZ fwYj w+Yj fjZw sYgw ow ow! owB owa ow5 ow8 oBw", +"5 w5oa owQ o!wQ owT wQoa pw pw8 pwT swo ow= ow+ =Bow ow=a w5o= w>o ow=5B oa", +"w> =Qw w+=Q ow=T w+oT pw= pw> =Tpw pws= Ywo o!Yw owH owYa Yw5 owY8 wHY5 wHo", +"8 wQoY YwoQ! Zwo oaZw pwY Y8pw Zwp osZw x x+ xH xg x5 x> xH5 xg> xQ x+Q xZ ", +"xZg xp xp> xZp yx w, w& w,B wa& w,. w8& ,.wB ,8wa w,K w&K wT, wT& wj, w&j ,", +"Twj sw& w*, -w *Bw, -wa ,*w. -w8 w*,.B -8wa ,*wK -wK ,Tw* -wT ,jw* -wj wT*,", +"j sw- wG, w&G wH, wH& ,Gw. ,Gw8 ,Hw. ,Hw8 ,GwK G&wK ,GwT GTw& ,Gwj G&wj ,Hw", +"j wHs, fw, -wf f,wH gw- w,f. f8-w fwH,. w8g- w,fK fK-w f,wT fT-w f,wj fj-w ", +"fwT,j s-gw w4, w&4 ,4wB ,4wa w5, w5& ,5wB ,5wa ,4wK 4&wK ,4wT 4Tw& ,4wj 4&w", +"j ,5wT w5s, w=, -w= w,=B =a-w =,w5 w>- w=5,B wa-> w,=K =K-w =,wT =T-w =,wj ", +"=j-w =Tw,5 s-w> ,4wG 4Gw& ,4wH 4Hw& ,Gw5 G5w& ,Hw5 H5w& w4G,K w&4GK wH4,T w", +"H&4T w5G,j w5&Gj wH5,T sw4,G =,fw f=-w =,wH -wg= f,w5 -wf> =Hw,5 g-w> fw=,K", +" -wf=K =Hw,T -wTg= fw5,j w>f-j =Hw,j gws-> wP, w&P ,PwB ,awP ,Pw. ,8wP wP,.", +"B w8a,P wQ, wQ& ,QwT ,Qwa ,Qwj ,Qw8 wQT,j wQs, ,Pw* -wP wP*,B wa-P wP*,. w8", +"-P P*.w,B -w8aP ,Qw* -wQ wQT,* wQ-T wQ*,j wQ-j P*jwT, sP-w Yw, Yw& wHY, wHY", +"& w,Y. w8Y& YwH,. Yw8,H wQY, wQY& Zw, Zw& wjY, w&Yj wjZ, s,Zw fwY, Y-w Yfw,", +"H Y-gw Yfw,. Y8-w fwHY,. gwY-8 f,wQ fQ-w fwZ, Z-w Yfjw, Yj-w Zwf,j Y-sw ow,", +" ow& w,oB wao& w5o, w5o& ow5,B ow8,a wQo, wQo& wTo, wTo& pw, pw& wTp, s,pw ", +"w=o, o-w ow=,B oa-w ow=,5 w>o- w=5o,B o-w>a =,wQ =Q-w owT=, oT-w w=p, -pw p", +"w=,T o-sw Y,o owY& oHY, oaY, o,Y5 o8Y, Yw5,H Yw5,a YQo, Yw&oQ Y,oZ owZ& Y,p", +"w Y&pw Z,pw Y,os x, x- x,H xg- x,5 x-> ,Hx5 g-x> x,Q x-Q xZ, xZ- xp, xp- Zp", +"x, yx- wI w!I wD wDa wI. w8I wD. wD8 wIK IKw! wDT TIwa wjI 8Iwj wDj swD wI*", +" w+I wD* w+D I*w. +Iw8 D*w. +Dw8 I*wK +IwK D*wT +DwT I*wj +Iwj D*wj w+sI wG", +"I GIw! Jw Jwa GIw. G8wI Jw. Jw8 GIwK w!GIK JwT JTwa GjwI w8GIj Jwj swJ fwI ", +"fIw+ Jwf gwJ wIf. fIw8 J.fw J8gw wIfK fw+IK fTJw wTgJ fIwj fw8Ij fwJj sJgw ", +"w4I 4Iw! wD4 4Dwa w5I 48wI w5D 4Dw8 4IwK w!4IK 4DwT wD4Ta 4Iwj w58Ij 4Dwj w", +"5sI =Iw w+=I =Dw w+=D w5=I w>I w5=D w>D wI=K =Iw+K wD=T =Dw+T wj=I >Iwj wD=", +"j =Dsw 4GwI w!4GI Jw4 waJ4 G5wI w5G8I Jw5 w5J8 w4GIK 4GIw!K wTJ4 Jw4Ta w5GI", +"j 4G8wjI w5Jj s4Jw fw=I =Ifw+ J=w J=gw fIw5 >Ifw fwJ5 J>w =IfwK fw+=IK =TJw", +" J=gwT =Ijfw w>fIj =jJw J>sw wIP IPw! wDP DPwa IPw. 8IwP DPw. D8wP wQI QIw!", +" wQD QDwa QIwj Q8wI QDwj wQsI IPw* +IwP DPw* +DwP IP*w. w+8IP wDP*. w+D8P Q", +"Iw* +QwI QDw* +QwD wQI*j w+Q8I wQD*j sw+QD YwI w!YI JwY YaJw wIY. YIw8 Y.Jw", +" Y8Jw YIwQ YwQI! ZwJ waZJ YIwj Yw8QI YjJw sJZw YIfw YIw+ YfJw JwgY YfwI. Yf", +"8wI YJfw. Jw8gY fIwQ Yw+QI JwZf gZJw YfjwI Yw+Ij JwjZf gwZsJ owI w!oI owD w", +"Doa w5oI w8oI w5oD wDo8 wQoI owQI! wQoD owDQa pwI w8pI pwD sIpw =Io o+=I ow", +"=D oa=I =5oI =Io> owD=5 oDw> oI=Q ow+QI oT=I ow+QD =Ipw pIw> =Dpw =Ios oIYw", +" YwoI! oJw oaJw YIw5 Yw58I Y5Jw o8Jw YwoQI oIQYw! oJZw oJZwa pIYw pwY8I Jpw", +" oJsw xI x+I xJ xJg x5I x>I xJ5 xJ> xQI +QxI xZJ gZxJ xpI p>xI xJp yxJ hw h", +"w& hwD cw hw. hw8 h.wD cw8 hwK hKw& hwT cwT hwj w8hj wDhj hsw hw* -wh h*wD ", +"cw- w*h. h8-w hwD*. w8c- w*hK hK-w h*wT hT-w h*wj hj-w hwT*j s-cw hwG hGw& ", +"Jwh cwJ wGh. hGw8 h.Jw h8Jw wGhK hwG&K hTJw wTcJ hGwj hw8Gj hjJw sJcw hfw h", +"f-w hfJw iw h.fw fwh8 Jwhf. iw8 hKfw -whfK fwhT iwT fwhj -wjhf Jwjhf isw hw", +"4 w&h4 wDh4 cw4 hw5 w5h8 wDh5 cw5 w4hK hw4&K wTh4 wTc4 w5hj hw5&j w5hT h4sw", +" h=w -wh= hw=D cw= w=h5 h>w h=w5D cw> =Khw h=-wK hw=T =Tcw hw=j hjw> h=Tw5 ", +"swh> w4hG hw4G& h4Jw c4Jw hGw5 hw5G8 h5Jw c5Jw hw4GK w&Gh4K Jwh4T cwJ4T hw5", +"Gj h4Gw&j Jw5hT cw5sJ fwh= h=f-w Jwh= iw= fwh5 hfw> J=hw5 iw> h=fwK -wfh=K ", +"J=hwT =Tiw h=jfw h>wfj J=jhw swi> hwP w&hP wDhP cwP wPh. w8hP hwDP. w8cP hw", +"Q w&hQ wQhT cwQ wQhj wQh8 hwQDj hQsw wPh* hP-w hwDP* cP-w hwP*. -wh8P wD*hP", +". cw-8P h*wQ hQ-w hwQD* cQ-w hwQ*j -wQh8 hQ*wDj cwQs- hYw Y&hw JwhY Ycw h.Y", +"w Ywh8 hYJw. Y8cw YwhQ hYwQ& hZw Zwc Ywhj hY8wQ hjZw swhZ Yfhw -whY hYJfw i", +"Yw hYfw. hY8-w JwfhY. Ywi8 fwhQ -wQhY hfZw iZw hYjfw Y-jhw hZwfj sYiw oh oh", +"& ohD ohc oh5 oh8 oDh5 h5oc ohQ o&hQ ohT hQoc hpo h8pw hTpw hso oh= oh- =Do", +"h c=oh o=h5 h>o oh=5D och> =Qoh hQo- =Toh =Qcw h=pw o-hp ohTp= ohs= ohY Y&o", +"h oJh ohYc Ywh5 Y8oh h5oJ h8oJ oYhQ ohYQ& hZo ochZ hYpw oh8Yp Zwhp oJhs xh ", +"xh- xJh ix xh5 xh> hJx5 ix> xhQ h-xQ xZh ixZ xph hpx- hZxp ixy w6 w!6 w6B w", +"a6 w1 w18 w1B w1a w6K 6!wK wT6 6Twa w1j 68wj w1T sw6 w6* w+6 6*wB 6+wa w1* ", +"w1+ 1*wB 1+wa 6*wK 6+wK 6Tw* 6+wT 6*wj 6+wj 1Tw* w1s* w6G 6Gw! wH6 6Gwa w1G", +" 6Gw8 w1H 6Hw8 6GwK w!6GK 6GwT wH6Ta 6Gwj w1G8j 6Hwj w1sG fw6 w+f6 wHf6 gw6", +" fw1 w1f8 wHf1 gw1 w6fK fw6+K wTf6 wTg6 w1fj fw1+j w1fT g6sw w46 6!w4 46wB ", +"4aw6 7w 7w8 7wB 7wa 46wK w!46K 4Tw6 wT46a 7wj 78wj 7wT sw7 w=6 =6w+ w6=B =6", +"wa 7w= w>7 =B7w =a7w w6=K w=6+K =6wT =Tw6+ =j7w wj7> =T7w 7sw> 4Gw6 w!46G 4", +"Hw6 wH46a 7wG w87G 7wH wH7a w46GK 4G6w!K wH46T 4GTwa6 wj7G 7wG8j wH7T sG7w ", +"w=f6 fw=6+ =6wH w=g6 7fw w>7f =H7w g7w fw=6K w=+f6K =Hw6T gw=6T fj7w w>7fj ", +"fw7T swg7 w6P 6Pw! 6PwB 6awP w1P 68wP 1PwB 1awP wQ6 6Qw! 6QwT 6Qwa w1Q 6Qw8", +" 1QwT w1sP 6Pw* 6+wP w6P*B w+6aP 1Pw* 1+wP w1P*B w1+aP 6Qw* 6+wQ wQ6T* w+Q6", +"T 1Qw* 1+wQ w1QT* sw6+Q Yw6 w!Y6 wHY6 waY6 Yw1 w1Y8 wHY1 w1Ya wQY6 Yw6Q! Zw", +"6 waZ6 w1Yj Yw1Q8 Zw1 s6Zw Yf6 f6Y+ YHf6 Yfg6 fwY1 f6Y8 Yf61H Ywg1 fQY6 Yf6", +"+Q Y6Zf g6Zw f6Yj Yf6+j fwZ1 g1Zw ow6 w!o6 w6oB o6wa 7wo o87w 7Bow ow7a o6w", +"Q ow6Q! o6wT owT6a pw7 w87p 7Tpw 7spw =6ow o6w+ ow=6B ow+6a ow7= o>7w 7o=wB", +" w>o7a =6wQ ow+6Q owT=6 ow+6T p=7w 7pw> pw7=T pw>7s owY6 Ywo6! o6wH Ywao6 7", +"Yw Y87w 7HYw Ya7w Ywo6Q owQY6! owZ6 Zwo6a pw7Y pw7Y8 7Zw 7Zsw x6 x6+ x6H xg", +"6 x7 x7> x7H xg7 x6Q 6+xQ xZ6 gZx6 xp7 7px> xZ7 yx7 ?w ?w& ?wB ?wa ?w1 :w ?", +"Bw1 :wa ?wK ?Kw& ?wT wT?a ?wj :wj w1?T s?w ?w* -w? w*?B ?a-w ?*w1 :w- ?w1*B", +" wa:- w*?K ?K-w ?*wT ?T-w ?*wj ?j-w ?wT1* s-:w ?wG w&?G ?wH wH?a w1?G :wG w", +"1?H :wH wG?K ?wG&K wH?T ?wHT& wj?G wj:G wH?j sG:w ?fw -w?f fw?H g?w f1?w :f", +"w ?wHf1 gw: ?Kfw ?f-wK fw?T ?Tgw fw?j fj:w ?fTw1 s?gw ?w4 ?4w& w4?B ?4wa 7w", +"? :w7 7B?w ?a7w w4?K ?w4&K ?4wT ?wT4a ?j7w wj7: ?T7w 7s:w ?w= ?=-w =B?w ?w=", +"a ?=7w Aw ?w=7B Awa =K?w ?w=-K ?w=T -wT?= ?w=j Awj 7wT?= Asw w4?G ?w4G& ?4w", +"H ?wH4a ?G7w 7G:w ?H7w 7H:w ?w4GK w&4?GK ?wH4T ?G4wT& ?wj7G :w7Gj 7wH?T :wH", +"7s fw?= ?w=f- ?w=H ?wg= 7w?f Afw 7wH?f Agw ?w=fK -w=?fK ?fTw= g?w=T ?fj7w f", +"wAj 7fT?w gwAs ?wP w&?P wP?B wa?P w1?P :wP ?w1PB wa:P ?wQ w&?Q wQ?T wQ?a w1", +"?Q :wQ ?wQ1T sP:w wP?* ?P-w ?wP*B ?-Pwa ?w1P* :P-w w1*?PB :w-aP ?*wQ ?Q-w ?", +"wQT* -wQ?T ?wQ1* :Q-w ?Q*w1T :wQs- ?Y ?Y& ?YH ?Ya ?Y1 ?Y: Y1?H ?HY: ?YQ Y&?", +"Q ?ZY ?aZw ?Yj ?QY: ?jZw s?Y ?Yf ?Y- Yf?H g?Y Y1?f :f?Y ?Yf1H Y:g? Yf?Q ?QY", +"- ?fZw ?Zgw Yf?j ?jY- ?ZYf1 sYg? o?w o&?w ?Bow ow?a o?7w :wo 7wo?B oa:w ow?", +"Q ?wQo& ow?T o?Twa ?pw :wp ?Tpw ?psw ow?= -wo? ?w=oB o-?wa ?w=7o Aow o?=7wB", +" owAa ?w=Q o-?wQ o?Tw= o-T?w ?wp= Apw ?pw=T swAo ?Yo Y&o? oY?H Yao? ?Y7 :w7", +"Y ?H7Y ?a7Y oY?Q ?YoQ& o?Zw ?ZoYa ?pY Y:?p 7Z?Y ?psY x? x?- x?H x?g x?7 Ax ", +"?Hx7 Axg x?Q ?-xQ x?Z g?xZ x?p Axp ?Zxp Axy w6I 6Iw! wD6 6Dwa w1I 68wI w1D ", +"6Dw8 6IwK w!6IK 6DwT wD6Ta 6Iwj w18Ij 6Dwj w1sI 6Iw* 6+wI 6Dw* 6+wD 1Iw* 1+", +"wI 1Dw* 1+wD w6I*K w+6IK wD6T* w+D6T w1I*j w1+Ij w1DT* sw6+D 6GwI w!6GI Jw6", +" waJ6 1GwI w1G8I Jw1 w1J8 w6GIK 6GIw!K wTJ6 Jw6Ta w1GIj 6G8wjI w1Jj s6Jw w6", +"fI fw6+I fwJ6 g6Jw fIw1 fw1+I fwJ1 g1Jw fw6IK w+If6K Jwf6T gwJ6T fw1Ij f6Iw", +"+j Jw1fT gwsJ6 4Iw6 w!46I 4Dw6 wD46a 7wI w87I 7wD wD7a w46IK 4I6w!K wD46T 4", +"TIwa6 wj7I 7w8Ij wD7T sI7w w6=I =Iw6+ =6wD =Dw6+ =I7w 7Iw> =D7w 7Dw> =Iw6K ", +"w+6=IK =Dw6T =D6w+T 7w=Ij w>7Ij 7wD=T w>D7s w46GI 4G6w!I w4J6 Jw64a wG7I 7w", +"G8I 7Jw 7aJw 4G6wIK 6!w4GIK Jw64T wT4J6a 7wGIj w8G7Ij 7TJw 7Jsw =Ifw6 fw6=I", +"+ w=J6 J=gw6 fw7I w>7fI 7wJ= 7Jgw fw6=IK =6w+fIK J=w6T gw6J=T 7fwIj 7fIw>j ", +"J=j7w J>w7s 6IwP w!6IP 6DwP wD6aP 1IwP w18IP 1DwP w1D8P 6QwI wQ6I! 6QwD wQD", +"6a 1QwI w1Q8I 1QwD sw6QD IP6* w+6IP wD6P* w+D6P w1IP* w1+IP w1DP* w1+DP wQ6", +"I* w+Q6I wQD6* w+Q6D w1QI* w1+QI w1QD* sIP6* w6YI Yw6I! Y6Jw YJ6wa YIw1 Yw1", +"8I Y1Jw Jw1Y8 Yw6QI wQIY6! Z6Jw ZwJ6a Yw1QI Y6IwQ8 Z1Jw sYwJ6 YIf6 Yf6+I Jf", +"Y6 gY6Jw Yf61I Yf68I Jw1Yf gw1YJ Yf6QI Y6Iw+Q Zf6Jw gwZJ6 Yf6Ij Y6IfQ8 Zw1J", +"f gwZJ1 w6oI ow6I! o6wD owD6a ow7I 7oIw8 ow7D 7wDo8 ow6QI wQ6oI! owD6Q oD6w", +"Qa 7Ipw pw78I 7Dpw osI7w =6oI ow+6I owD=6 ow+6D 7o=I w>o7I 7wDo= w>D7o =Qw6", +"I o+6=QI oTI=6 o+6=TI pw7=I pw>7I pwD7= osI7= Ywo6I owIY6! J6ow oJw6a 7IYw ", +"7Yw8I 7woJ oJ87w owQY6I o!Yw6QI oJZw6 Zw6oJa pw7YI 7Y8pwI Zw7J oJs7w x6I 6+", +"xI xJ6 gJx6 x7I 7>xI xJ7 g7xJ 6QxI x6+QI ZJx6 xZJg6 7pxI xp7>I 7ZxJ x7yJ h?", +"w w&h? ?wD cw? hw1 :wh w1?D :wc ?Khw ?w&hK ?whT ?Tcw ?whj hj:w w1hT :whs h*", +"?w h?-w ?*wD -w?c h*w1 -wh: ?wD1* :c-w ?w*hK -w?hK ?wDT* cw?-T ?wjh* :wh-j ", +"?wD*j :wcs- ?Ghw ?wGh& ?Jw cw?J hGw1 :Ghw h1Jw :Jw ?wGhK hw&?GK ?TJw cw?JT ", +"?wjhG :whGj ?jJw ?Jsw hw?f ?fh-w Jw?f i?w fwh1 hf:w Jw1?f i:w ?fhwK -wh?fK ", +"?fTJw ?wiT ?fjhw :fwhj ?Jjfw s?iw ?wh4 ?w4h& ?4wD c4?w 7hw :w7h ?D7w 7cw ?w", +"4hK h4&?wK ?wD4T cw?4T hj7w :w7hj hT7w 7hsw ?wh= ?w=h- ?w=D ?wc= 7wh= Ahw 7", +"wDh= Acw ?w=hK -w?h=K h=T?w cw=?T h=j7w hwAj 7hTw= hsAw ?w4hG hw4?G& J4?w c", +"w?J4 7Ghw :w7hG 7w?J :w7J hw4?GK hGw&?4K ?Jw4T ?J4cwT 7hwGj 7hG:wj ?J7wT hs", +"7Jw ?w=hf ?f-h=w ?wJ= ?wi= hf7w hfAw ?J7fw Aiw ?fwh=K =K?f-wh ?J=wT i?w=T 7", +"hfwj Ahjfw J=j?w isAw h?P h&?P ?DhP hP?c ?Ph1 hP:w ?wD1P :Pcw ?whQ ?wQh& ?T", +"hP ?Qcw ?Phj hQ:w ?wQ1D hPs? h*?P ?-hP ?wDP* cw?-P h?P1* :wh-P ?D*h1P :wc-P", +" ?wQh* -wQh? ?wQD* cwQ?- ?Pjh* :wQh- ?Q*w1D :wc-Q ?Yh Y&h? ?JY ?Yc h1?Y hY:", +"w Y1?J Y:?J hQ?Y ?YhQ& ?YhZ Yc?Z hj?Y :wQhY h1Zw ?JsY ?fhY h?Y- YJ?f i?Y hY", +"1?f ?Y:hf ?JYf1 ?Yi: ?fQhY ?Y-hQ hZ?Yf ?ZiY ?Yjhf :wQhf hZwf1 s?iY oh? o&h?", +" ?Doh ?coh 7ho oh: 7Doh oh7c hQo? oh?Q& hTo? ohc?Q oh?p :whp ?Dpw ohs? o?h=", +" h?o- oh?=D ohc?- 7oh= Aoh 7ho=D ohAc oh?=Q oh-?Q ohT?= oh-?T hpo?= hpAo oh", +"T7= hsAo oh?Y oh?Y& ?Jo oc?J 7hY 7hY: 7J?Y 7hYc oh?YQ ?YQoh& oJ?Z hZo?c ?Yh", +"p :wphY 7YhZ Yc?p x?h h?x- x?J ix? x7h Axh ?Jx7 Aix h?xQ x?h-Q hZx? x?iZ hp", +"x? xpAh hZx7 yAix wR w!R wRB waR wR. w8R R.wB R8wa wM wM! wTR waM wMj w8M R", +"Twj swR wR* w+R R*wB R+wa R*w. R+w8 wR*.B w+R8a wM* w+M RTw* R+wT R*wj R+wj", +" wTR*j w+sR wRG RGw! wHR RGwa RGw. RGw8 RHw. RHw8 wMG GMw! wHM GawM RGwj G8", +"wM RHwj wHsR fwR w+fR wHfR gwR wRf. w8fR fwRH. w8gR fwM w+fM wHfM gwM wMfj ", +"w8fM fwTRj gRsw wR4 R4w! R4wB R4wa w5R R4w8 R5wB R5wa wM4 4Mw! R4wT 4awM w5", +"M 48wM R5wT w5sR =Rw w+=R wR=B wa=R w5=R w>R =Rw5B >Rwa =Mw w+=M wT=R wa=M ", +"w5=M w>M =RTw5 >Msw R4wG w!R4G R4wH wHR4a RGw5 w5RG8 RHw5 wH5R8 4GwM wM4G! ", +"4HwM wHM4a G5wM w5MG8 H5wM swR4G =Rf =+fR fR=H =Rgw f5=R =Rf> =RfH5 gRw> fw", +"=M =Rf+M fR=T =Mgw fR=j fw>M =RfHj >Mgw wRP RPw! RPwB RawP RPw. R8wP wRP.B ", +"w8RaP Sw Sw! SwT Swa Swj Sw8 STwj swS R*wP R+wP wRP*B w+RaP wRP*. w+R8P P*.", +"wRB aP*w8R Sw* Sw+ wTS* w+Sa wjS* w+S8 SwT*j s*Sw YwR w!YR YRwH YRwa wRY. Y", +"Rw8 YwRH. Yw8RH SwY S!Yw ZwS SaZw YjSw YwS8 wjSZ SsZw fRYw YRw+ YfwRH YwgR ", +"YfwR. Yf8wR fRHYw. gwYR8 Sfw YwS+ ZwSf gwS fjSw fwS8 SfZwj swgS owR w!oR wR", +"oB oRwa oRw5 oRw8 owR5B ow8Ra Swo S!ow oTSw owSa pwS S8pw wTSp Sspw ow=R oR", +"w+ ow=RB ow+Ra ow=R5 >Row =R5owB w>oRa S=w owS+ =TSw =aSw pwS= S>w pw=ST S>", +"sw YRow YwoR! oRwH YwaoR YRw5 Yw5R8 Yw5RH Yw5Ra YwSo SwoY! oZSw SwaoZ YpSw ", +"pwYS8 SZpw sYoSw xR xR+ xRH xgR xR5 x>R RHx5 g>xR xS xS+ xZS xgS xpS x>S SZ", +"xp yxS wR, w&R ,RwB ,Rwa ,Rw. ,Rw8 wR,.B w8R,a wM, wM& ,RwT ,awM ,Rwj ,8wM ", +"wTR,j wMs, ,Rw* -wR wR*,B wa-R wR*,. w8-R ,R*w.B -wR8a ,Mw* -wM wTR,* wT-R ", +"wM*,j w8-M ,RTwj* sR-w ,RwG RGw& ,RwH RHw& wRG,. w8R,G wHR,. wH8,R ,GwM GMw", +"& ,HwM HMw& wMG,j w8M,G wHM,j swR,G wRf, fR-w fwR,H gR-w fwR,. -wfR8 wH,fR.", +" gw-R8 f,wM fM-w fwT,R gM-w fwM,j -wMf8 fR,wHj gws-R ,Rw4 R4w& wR4,B waR,4 ", +",Rw5 R5w& w5R,B w5a,R ,4wM 4Mw& wTR,4 wT&R4 ,5wM 5Mw& w5T,R swR,4 wR=, =R-w", +" =Rw,B -w=Ra =Rw,5 -Rw> w5,=RB w>-Ra =,wM =M-w =RTw, -wT=R =Rjw, >M-w =R,w5", +"T w>Ms- R4,G w&R4G wHR,4 wH&R4 w5R,G w5&RG wH5,R wH5R& wM4,G wM&4G wHM,4 wH", +"M4& w5M,G w5MG& wH5,M wH5M& =,fR f-=R =Rf,H g=R-w =Rf,5 w>f-R =R,wH5 gw>-R ", +"=Rf,M -wMf= =Rf,T gwM-= =Rf,j w>Mf- =R,f5T w>Mg- ,RwP R&wP wRP,B waR,P wRP,", +". w8R,P ,RPw.B ,R8waP Sw, Sw& wTS, wTS& wjS, w8S& SwT,j s,Sw wRP,* wR-P P*B", +"wR, -wRaP P*.wR, -wR8P ,RwBP*. w8R-Pa w*S, S-w SwT,* Sa-w Sw*,j S8-w wT*S,j", +" S-sw wRY, YRw& YwR,H Ywa,R YwR,. Yw8,R wHRY,. Y8RwH& YwS, YwS& S,Zw S&Zw S", +"Y,wj Sw8Y& ZwS,j sYwS& Yfw,R -RYw fwRY,H gwY-R fwRY,. Y-wR8 ,Hw.YfR Y-Rgw8 ", +"fwS, SwY- SfZw, S-gw Sfw,j Sf-w8 ZwjSf, gwSs- wRo, oRw& owR,B owa,R owR,5 o", +"w8,R w5Ro,B oaRw5& owS, owS& Swo,T Swao& S,pw S&pw pwS,T os,Sw ow=,R -Row =", +"R,owB o-wRa =R5ow, o-w>R =Bow,R5 w>Ro-a w=S, Swo- S=w,T o-TSw pw=S, pwS- S=", +"Tpw, os-Sw YRo, Yw&oR Y,oRH Yao,R Yw5,R Yw5R& Y5RoH, Y5Roa, Y,So Sw&oY oZ,S", +"w Zw&So pwYS, pw&SY ZwpS, os,SY x,R x-R ,RxH g-xR ,Rx5 ->xR x,RH5 xg->R xS,", +" x-S SZx, gSx- Spx, S-xp xZpS, y-xS [w [w! [wD [wa [w. [w8 w.[D [8wD [wM [!", +"wM Vw Vwa [wj [8wM Vwj swV [w* [w+ [*wD wD[+ w.[* w8[+ [wD*. [w+D8 [*wM wM[", +"+ Vw* Vw+ [*wj wj[+ wjV* s*Vw [wG wG[! Jw[ wa[J w.[G wG[8 J.[w [wJ8 [GwM [w", +"GM! VwJ waVJ wG[j [w8GM JjVw sJVw [fw w+[f [fJw g[w f.[w [wf8 Jw[f. w8g[ [w", +"fM [fw+M Vfw gwV [wfj [f8wM fjVw swg[ [w4 w![4 wD[4 wa[4 [w5 w8[4 [5wD wa[5", +" wM[4 [w4M! Vw4 waV4 wM[5 [w58M Vw5 s4Vw [= [=+ [=D [=a [=5 [>w [5=D =D[> [", +"=M [+=M Vw= =aVw [=j >M[= =jVw s=[ wG[4 [w4G! [wJ4 Jw[4a wG[5 [w5G8 [wJ5 Jw", +"5[8 [w4GM wMG[4! J4Vw VwJ4a [w5GM [4Gw8M J5Vw Vw5sJ [=f [+f= J=[ g[= f5[= f", +">[= J5[= [>gw =M[f [=f+M VwJ= V=g[ =j[f [>f=M Jj[= s=g[ [wP wP[! wP[D wP[a ", +"[Pw. wP[8 [wDP. [w8DP Sw[ S![w VwS SaVw wjS[ [wS8 wjSV SsVw wP[* wP[+ [wDP*", +" [w+DP [wP*. [w+8P [*PwD. [+PwD8 [wS* [wS+ S*Vw S+Vw S[*wj Sw+[8 VwS*j Vw+S", +"s Yw[ [!Yw [JYw [wYa Y.[w [wY8 Yw[J. Jw8[Y YwS[ Yw[S! ]w ]wa [wYj Sw8[Y ]wj", +" ]sw Yw[f [wY+ YJf[w Ywg[ Yw[f. [f8Yw [fYJw. g[wY8 [fSw Sw+[f ]fw ]gw [fjSw", +" Sf8[w fw]j gw]s o[w o![w [woD [woa w5o[ [wo8 o[w5D o[8wD o[Sw Swo[! oVw oa", +"Vw pw[ w8[p Vwp oVsw [=o o+[= =Do[ =ao[ [5o= o[w> [=o5D [>o=D S=[ S+[= VwS=", +" S[=a [p= [>S= pwV= VwS> Ywo[ Yw[o! Jwo[ oJ[wa [wY5 o[8Yw oJ[w5 oJ8[w Yw[So", +" o[YSw! ]ow ow]a Yw[p pw[Y8 ]pw sw]o x[ x[+ xJ[ xg[ x[5 x[> J5x[ g[x> x[S S", +"[x+ ]x ]xg xp[ S>x[ ]xp ]yx hw[ [w& wD[h cw[ h.[w [wh8 hw[D. w8[c hwM w&hM ", +"Vwh cwV [whj w8hM hjVw Vwhs [*hw [-w hw[D* [-cw hw[*. w8[- wD*[h. cw[-8 h*w", +"M hM-w V*hw V-w hwM*j [w-j Vwh*j V-sw hG[w wG[& [Jhw [cJw hw[G. hw8[G Jw[h.", +" cwJ[8 hGwM hwMG& VhJw cJVw hwMGj hw8GM VwJhj cwVsJ hw[f [f-w Jw[hf i[w [fh", +"w. [-fw8 [fhJw. [wi8 fwhM [-fwM hfVw iVw [fjhw [-jfw Vfwhj swiV h4[ w&[4 hD", +"[4 h4[c [wh5 [4h8 hw5[D [wc5 [4hM hwM4& h4Vw c4Vw [4hj hw58M h5Vw h4[s [=h ", +"[-= [h=D c=[ h5[= w>[- [=h5D [>cw hw=M =M[- h=Vw Vwc= hj[= =j[- Vw=h5 Vwh> ", +"hG[4 h4[G& [Jh4 cwJ[4 hw5[G h4[G8 Jw5[h cw5[J hwM4G h4GwM& VwJh4 cwVJ4 hw5G", +"M h4Gw8M Vw5hJ cwVJ5 [fh= f-[= hJ[= i[= [=hf5 [-=f> J=[h5 [>iw [=hfM [-=fM ", +"J=Vhw Vwi= [=jhf [-=fj J=[hj s=i[ [whP wP[& hw[DP [wcP hw[P. hw8[P hPD[w. c", +"w[8P Shw S&hw VwSh Scw hjSw h8Sw VwhSj Shsw hw[P* [w-P hPD[w* cw[-P hP*[w. ", +"[-w8P h*wD[P. [-8cwP S*hw Sw[- VwhS* cwS- Shw*j [-jSw Sh*Vwj V-wSs [hYw [wY", +"& hYJ[w Yw[c hY[w. hY8[w Jw[hY. Ycw[8 SwhY Sw&hY ]hw ]cw hYjSw Sh8Yw hw]j h", +"s]w hY[fw Yw[- Jw[hYf Ywi[ [fwhY. Y-[w8 h.Jw[fY i[Yw8 hfSw Shf-w hf]w ]iw S", +"hfwj [-jYw ]hfwj iw]s oh[ [ho& [hoD o[cw h5o[ h8o[ oh[5D ohc[5 Sho S&oh oVh", +" ohSc pwSh Soh8 oVhp oVhs [=oh o-[ oh[=D [-oc oh[=5 [>oh [=5ohD cw>o[ Sh= o", +"hS- V=Sh cwS= S=hp S=h> oVhp= Shs= o[hY oh[Y& hJo[ oJh[c oh[Y5 oh8[Y oJh[5 ", +"oJh[8 SohY ShoY& ]oh oh]c pw[hY oh8SY hp]o hs]o x[h x[- hJx[ ix[ h5x[ h>x[ ", +"xJ[h5 x[i> xhS Shx- ]xh ]ix hpx[ h>xS xp]h ix]y wq wq! wqB wqa wq1 wq8 q1wB", +" q8wa wqM q!wM wqT qTwa lw lw8 lwT lws wq* wq+ wBq* q+wa q*w1 q+w1 wq1*B wq", +"+1a q*wM q+wM q*wT q+wT lw* lw+ wTl* l+sw wqG w!qG wqH qGwa qGw1 qGw8 qHw1 ", +"qHw8 qGwM wqGM! qGwT wqHaM lwG w8lG lwH lGsw fq fq+ fqH gqf fq1 fq8 qHf1 g1", +"fq fqM q+fM fqT gMfq lfq l+fq lHfq glw wq4 w4q! q4wB w4qa 7wq w87q 7Bwq wq7", +"a q4wM wq4M! w4qT wqT4a lw7 w87l 7Tlw 7slw q=w w+q= =Bwq wq=a q=7w w>q 7wq=", +"B waq> wq=M q=w+M wq=T q=Tw+ lw= lw> =Tlw swl= w4qG wq4G! w4qH wqH4a wq7G 7", +"qGw8 wq7H 7wHq8 wq4GM qG4wM! wqH4T wT4qGa lG7w lw7G8 lH7w lws7G fq= q+f= =H", +"fq g=fq 7fq fq> 7Hfq 7fgq =Mfq fq=+M =Tfq fqTg= 7flw w>lf =Hlw g7lw wqP wPq", +"! qPwB wPqa q1wP wPq8 wq1PB wq8aP Swq S!wq wTSq wqSa lwS S8lw wTSl Sslw wPq", +"* wPq+ wqP*B wq+aP wq1P* wq+1P q*Pw1B w1Pqa* wqS* wqS+ Sq*wT Sw+qT l*Sw l+S", +"w lwST* lwsS+ Yqw q!Yw wHYq wqYa wqY1 wqY8 Yw1qH Yq8wH YqSw SwqY! Zqw waZq ", +"lYw Y8lw lwZ sYlw fqY Y+fq qHYf gYfq Y1fq Y8fq fqY1H fq8gY Sfq S+fq Zqf Sfg", +"q Sflw S8fq Zqlf gwlY oqw o!wq oBwq wqoa oq7w wqo8 7woqB 7waoq oqSw Swoq! w", +"qoT Swaoq rw rw8 rwT rsw owq= wqo+ q=owB q=aow 7o=wq oqw> q=o7wB w>qoa q=Sw", +" Sw+q= q=TSw S=awq rw= rw> =Trw swr> oqYw Yqow! wqoH Yqaow 7wYq Yq87w 7wHYq", +" 7Yawq SwoYq YqoSw! oqZw Zqwoa rYw Ywr8 rZw sYrw xq xq+ xqH xqg xq7 xq> 7qx", +"H gqx7 xqS Sqx+ xZq gqxZ xr xr> xrZ yxr ?qw wq& ?Bwq wq?a w1?q :wq ?qw1B wa", +":q ?wM w&?M wq?T wa?M lw? lw: ?Tlw s?lw q*?w -qw ?qw*B wa-q ?qw1* -q:w w1*?", +"qB :wq-a ?*wM ?M-w ?qTw* wq-T ?wl* l-w lw?T* swl- ?qG qGw& wq?H qG?a ?1qG ?", +"G:q ?qG1H wq:H wM?G ?qGM& wH?M ?qGT& ?qlG lG:w ?wlH lH:w fq? fq- ?Hfq fqg? ", +"f1?q :fq fq?1H :fgq ?Mfq fM-q ?Tfq ?wgM ?flw :flw fqTl? lwg? ?4wq w4q& ?qw4", +"B ?qaw4 ?q7w :q7w ?q7wB :wq7a ?4wM ?wM4& ?qTw4 ?wa4M l?7w l:7w 7wTl? lws7? ", +"?wq= q=-w ?w=qB -qw=a ?q7w= Aqw q=?7wB wqAa ?w=M -qw=M q=T?w -qTw= ?wl= Alw", +" lw=?T lwAs ?4qG ?qG4& ?qG4H ?qG4a 7q?G :wq7G 7wH?q :wH7q ?qG4M ?G4wM& ?qG4", +"T ?G4waM lw?7G lw:7G lwH7? :wH7l q=?f f-q= fq?=H fq-g= ?q7f Aqf 7fq?H gqAf ", +"fq?=M fq-=M fqT?= fq-=T lf?7w lfAq fqT7? glAw wq?P wPq& ?qwPB ?qawP ?qw1P w", +"q:P wq1?PB :wqaP S?w S&?w ?TSw ?aSw S?lw S:w lw?ST S?sw ?qwP* wq-P wq*?PB -", +"qwaP wq1?P* :wq-P ?Bwq1P* -qa:wP S*?w Sw-q S?wT* -qTSw lw?S* S-lw S?Tlw* l-", +"wSs ?Yq Y&?q ?HYq ?aYq Y1?q Yq:w ?Yq1H :wHYq S?Y S&?Y Zq?Y ?aSY lY? ?YS: ?Z", +"lY s?lY fq?Y Y-q fq?YH ?Ygq fq?Y1 fqY: ?Y1fqH g?Y:q S?f S-?Y Sf?Z S?gw S?lf", +" ?Yl- lY?Zf g?lY oq?w wqo& o?qwB o?awq ?q7ow oq:w o?q7wB :woqa o?Sw Sw&o? o", +"?TSw S?aow r?w r:w ?wrT s?rw ?w=oq oq-w q=wo?B o-qwa o?q7w= oqAw =B7wo?q Ao", +"awq ?wS= o-qSw S?=wT o-qwT ?wr= Arw r=T?w rsAw o?Yq Yq&o? o?HYq ?Yaoq ?q7Y ", +":woYq ?Y7qH :woqH So?Y S?Yo& S?ZoY S?Yoa r?Y ?Yr: ?ZrY s?rY x?q xq- ?qxH g?", +"xq ?qx7 Axq x?q7H xqAg x?S S?x- S?xZ g?xS xr? Axr r?xZ yxAr [qw [!wq wqD wa", +"[q [w1 w8[q wD[1 wa[1 wM[q [qwM! Vwq waVq lw[ w8[l lwV Vwsq [q* w+[q [*qD q", +"+wD [*w1 [1w+ [q*1D [q8D* [Mq* [q+wM [qV* wqV+ [ql* [wl+ l*Vw l+Vw qG[w [qw", +"G! Jqw waJq wG[1 [q8wG [wJ1 wqJ8 [qwGM wMG[q! VwJq JqVwa [wlG lw[G8 lwJ Jqs", +"w fq[ [+fq Jqf Jqgw [qf1 [qf8 J1fq J8fq [qfM fq[+M fqV Vfgq [lfq fq8[l Jqlf", +" g[lw [4wq [qw4! w4qD [qaw4 7[w w87[ [w7D [w7a [qw4M [4Mwq! wqV4 Vwq4a 7[lw", +" lw7[8 7Vw 7Vsw [=q [+q= [q=D [q=a 7[= w>7[ =D7[ =a7[ [q=M [=q+M Vq[= Vw=q+", +" l=[ [>lw Vwl= 7[s= [qw4G [4Gwq! wqJ4 Jqw4a [w7G 7[wG8 7wJq Jq87w [4GwqM [!", +"wq4GM JqVw4 Vw4Jqa lw7[G 7[Glw8 7Jlw lwJ7s [=fq fq[=+ Jq= J=gq 7[f [>7f Jq7", +"f 7[gw fq[=M [=+fqM V=Jq Jq=gV 7[lf fq>[l Jql= g[l= wP[q [qwP! wPqD [qawP w", +"P[1 [q8wP [w1DP [w1aP [qSw Sq[w! VqSw VwqSa [lSw Sw8[l SlVw lwVSs [Pq* [q+w", +"P [q*DP [qaP* [q*1P [q8P* w1PqD* w1P[a* [qS* Sw+[q VwqS* Vw+Sq lw[S* lw+S[ ", +"lwVS* Vw+Sl [qYw Yw[q! JwYq JqYwa [wY1 Yq8[w JqYw1 Jq8Yw Sq[Yw Yq[Sw! ]qw w", +"q]a Yw[l lYw[8 ]lw lw]s [fYq fq[Y+ YJfq JqfgY fq[Y1 fq8[Y JqfY1 JqfY8 Sq[f ", +"Sfq[+ ]qf gq]f lYf[q fq8S[ lf]q gl]w [qow o[qw! wqoD o[awq o[7w o[87w 7wDo[", +" 7[aow Sq[ow o[qSw! oqVw oVwqa r[w [wr8 rVw swrV q=o[ [=oq+ [=oqD [=aoq 7o[", +"= [>o7w 7[=oD w>qoD Sq[= S=[q+ oVwq= oVwq+ r[= [>rw Vwr= s=r[ Yw[oq o[wYq! ", +"oqJw oJqwa Yw7[ 7[Yw8 oJq7w oJqw8 YqoSw[ Sqo[Yw! oq]w ]oqwa Ywr[ r[Yw8 ]rw ", +"rs]w xq[ [qx+ xJq gqxJ x7[ 7[x> 7Jxq g7x[ Sqx[ xq[S+ ]xq xq]g xr[ r[x> ]xr ", +"yx]r z z& zD zc z1 z: z1D z:c zM zM& zV zVc zl zl: zlV uz z* z- z*D zc- z*1", +" z:- 1Dz* :cz- z*M z-M zV* zV- zl* zl- l*zV uz- zG zG& zJ zJc z1G z:G zJ1 z", +"J: zGM GMz& zJV cJzV zlG l:zG zlJ uzJ zf zf- zJf iz zf1 z:f Jfz1 iz: zfM f-", +"zM zVf izV zlf lfz: lfzJ uzi z4 z4& z4D zc4 z7 z7: z7D z7c z4M 4Mz& zV4 c4z", +"V zl7 l:z7 z7V uz7 z= z-= z=D zc= z7= Az =Dz7 Azc z=M =Mz- zV= c=zV zl= Azl", +" l=zV Azu z4G 4Gz& zJ4 cJz4 z7G 7:zG zJ7 7Jz: 4GzM z4GM& J4zV zJVc4 lGz7 zl", +"7:G 7Jzl zJu7 zf= f-z= zJ= iz= z7f Azf 7Jzf Azi =Mzf zf-=M J=zV zVi= lfz7 z", +"lAf l=zJ uzAi zP zP& zDP zcP z1P z:P 1DzP :czP zS zS& zVS zcS zlS z:S SlzV ", +"uzS z*P z-P DPz* c-zP 1Pz* :-zP z*1DP z:c-P zS* z-S SVz* Scz- l*zS l-zS zlV", +"S* z-uS zY zY& zJY zYc zY1 zY: YJz1 Y:zJ zYS SYz& ]z ]zc zlY lYz: ]zl uz] z", +"Yf zY- YJzf izY Yfz1 Y:zf zJYf1 zYi: zfS Sfz- ]zf ]zi lYzf lYz- zl]f ]zui z", +"o zo& zoD zoc zo7 zo: 7ozD 7czo zoS Soz& zoV Sczo zr zr: zrV uzr zo= zo- =D", +"zo ocz- 7oz= Azo zo7=D zoAc z=S S-zo S=zV Scz= zr= Azr rVz= uzAr zoY Y&zo z", +"oJ Yczo z7Y Y:zo 7Jzo Ycz7 SozY zoYS& ]zo zo]c zrY rYz: ]zr ]zur zx zx- zxJ", +" izx zx7 Azx xJz7 izAx zxS x-zS ]zx iz]x zxr zxAr zx]r | ", +NULL +}; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Prepares the data for MSOPs of 4-variable functions.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cnf_ReadMsops( char ** ppSopSizes, char *** ppSops ) +{ + unsigned uMasks[4][2] = { + { 0x5555, 0xAAAA }, + { 0x3333, 0xCCCC }, + { 0x0F0F, 0xF0F0 }, + { 0x00FF, 0xFF00 } + }; + char Map[256], * pPrev, * pMemory; + char * pSopSizes, ** pSops; + int i, k, b, Size; + + // map chars into their numbers + for ( i = 0; i < 256; i++ ) + Map[i] = -1; + for ( i = 0; i < 81; i++ ) + Map[s_Data3[i]] = i; + + // count the number of strings + for ( Size = 0; s_Data4[Size] && Size < 100000; Size++ ); + assert( Size < 100000 ); + + // allocate memory + pMemory = ALLOC( char, Size * 75 ); + // copy the array into memory + for ( i = 0; i < Size; i++ ) + for ( k = 0; k < 75; k++ ) + if ( s_Data4[i][k] == ' ' ) + pMemory[i*75+k] = -1; + else + pMemory[i*75+k] = Map[s_Data4[i][k]]; + + // set pointers and compute SOP sizes + pSopSizes = ALLOC( char, 65536 ); + pSops = ALLOC( char *, 65536 ); + pSopSizes[0] = 0; + pSops[0] = NULL; + pPrev = pMemory; + for ( k = 0, i = 1; i < 65536; k++ ) + if ( pMemory[k] == -1 ) + { + pSopSizes[i] = pMemory + k - pPrev; + pSops[i++] = pPrev; + pPrev = pMemory + k + 1; + } + *ppSopSizes = pSopSizes; + *ppSops = pSops; + + // verify the results - derive truth table from SOP + for ( i = 1; i < 65536; i++ ) + { + int uTruth = 0, uCube, Lit; + for ( k = 0; k < pSopSizes[i]; k++ ) + { + uCube = 0xFFFF; + Lit = pSops[i][k]; + for ( b = 3; b >= 0; b-- ) + { + if ( Lit % 3 == 0 ) + uCube &= uMasks[b][0]; + else if ( Lit % 3 == 1 ) + uCube &= uMasks[b][1]; + Lit = Lit / 3; + } + uTruth |= uCube; + } + assert( uTruth == i ); + } +} + +#if 0 + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ManDeriveCnfTest() +{ + int i, k, Lit; + printf( "\n" ); + for ( i = 80; i >= 0; i-- ) + { + Lit = i; + for ( k = 0; k < 4; k++ ) + { + if ( Lit % 3 == 0 ) + printf( "%c", 'A' + k ); + else if ( Lit % 3 == 1 ) + printf( "%c", 'a' + k ); + Lit = Lit / 3; + } + printf( "\n" ); + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ManDeriveCnfTest2() +{ + char s_Data3[81] = "!#&()*+,-.0123456789:;<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZ[]abcdefghijklmnopqrstuvwxyz|"; + + unsigned uMasks[4][2] = { + { 0x5555, 0xAAAA }, + { 0x3333, 0xCCCC }, + { 0x0F0F, 0xF0F0 }, + { 0x00FF, 0xFF00 } + }; + char Buffer[100], * pCur; + FILE * pFile; + int CountCur, Counter = 0, nLines = 0; + int pLines[1<<16] = {0}; + int pNums[1<<16] = {0}; + unsigned uTruth, uTruth2, uCube, cCube; + char * pSops[1<<16] = {0}; + char Sop[10]; + char Cube[4]; + int i, k; + + pFile = fopen( "cands2.txt", "r" ); + while ( fgets( Buffer, 100, pFile ) ) + { + if ( Buffer[0] == '0' ) + Extra_ReadHexadecimal( &uTruth2, Buffer+2, 4 ); + else + uTruth2 = 0xFFFFFF; + + // skip all chars till a-d or A-D + if ( Buffer[0] == '0' ) + for ( pCur = Buffer; *pCur != '\n'; pCur++ ) + { +// if ( *pCur >= 'a' && *pCur <= 'd' || *pCur >= 'A' && *pCur <= 'D' ) + if ( *pCur == ':' ) + { + pCur++; + break; + } + } + else + pCur = Buffer; + + + uTruth = 0; + CountCur = 0; + uCube = 0xFFFF; + for ( i = 0; i < 4; i++ ) + Cube[i] = 2; + + for ( ; *pCur; pCur++ ) + { + if ( *pCur == '+' || *pCur == '\n' ) + { + uTruth |= uCube; + uCube = 0xFFFF; + + // get the cube + cCube = 0; + for ( i = 0; i < 4; i++ ) + cCube = 3 * cCube + Cube[i]; + for ( i = 0; i < 4; i++ ) + Cube[i] = 2; + + assert( cCube >= 0 && cCube < 81 ); + Sop[CountCur] = cCube; + CountCur++; + if ( *pCur == '\n' ) + { + Sop[CountCur] = 0; + break; + } + } + else if ( *pCur >= 'a' && *pCur <= 'd' ) + { + uCube &= uMasks[*pCur-'a'][1]; + Cube[*pCur-'a'] = 1; + } + else if ( *pCur >= 'A' && *pCur <= 'D' ) + { + uCube &= uMasks[*pCur-'A'][0]; + Cube[*pCur-'A'] = 0; + } + } + assert( *pCur == '\n' ); + assert( uTruth2 == 0xFFFFFF || uTruth2 == uTruth ); + + Counter += CountCur; + pNums[uTruth] = CountCur; + pSops[uTruth] = ALLOC( char, CountCur ); + memcpy( pSops[uTruth], Sop, CountCur ); + pLines[nLines++] = Counter; + } + fclose( pFile ); + + printf( "Lines = %d. Counter = %d.\n", nLines, Counter ); + +/* + // write the number of cubes + for ( i = 0; i < 65536; i++ ) + printf( "%d,%d ", pNums[i], pNums[i] + pNums[0xffff & ~i] ); + printf( "\n" ); +*/ + + // write the number of cubes + Counter = 0; + for ( i = 1; i < 65536; i++ ) + { + CountCur = pNums[i]; + assert( CountCur > 0 ); + for ( k = 0; k <= CountCur; k++ ) + { + if ( k < CountCur ) + { + assert( pSops[i][k] >= 0 && pSops[i][k] < 81 ); + printf( "%c", s_Data3[pSops[i][k]] ); + } + else + printf( " " ); + if ( ++Counter == 75 ) + { + printf( "\",\n\"" ); + Counter = 0; + } + } + } + printf( "\n" ); + + return 1; +} + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/cnf/cnfMan.c b/abc_with_bb_support/src/aig/cnf/cnfMan.c new file mode 100644 index 000000000..5ba2941c4 --- /dev/null +++ b/abc_with_bb_support/src/aig/cnf/cnfMan.c @@ -0,0 +1,202 @@ +/**CFile**************************************************************** + + FileName [cnfMan.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG-to-CNF conversion.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: cnfMan.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cnf.h" +#include "satSolver.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static inline int Cnf_Lit2Var( int Lit ) { return (Lit & 1)? -(Lit >> 1)-1 : (Lit >> 1)+1; } +static inline int Cnf_Lit2Var2( int Lit ) { return (Lit & 1)? -(Lit >> 1) : (Lit >> 1); } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the fraiging manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cnf_Man_t * Cnf_ManStart() +{ + Cnf_Man_t * p; + int i; + // allocate the manager + p = ALLOC( Cnf_Man_t, 1 ); + memset( p, 0, sizeof(Cnf_Man_t) ); + // derive internal data structures + Cnf_ReadMsops( &p->pSopSizes, &p->pSops ); + // allocate memory manager for cuts + p->pMemCuts = Aig_MmFlexStart(); + p->nMergeLimit = 10; + // allocate temporary truth tables + p->pTruths[0] = ALLOC( unsigned, 4 * Aig_TruthWordNum(p->nMergeLimit) ); + for ( i = 1; i < 4; i++ ) + p->pTruths[i] = p->pTruths[i-1] + Aig_TruthWordNum(p->nMergeLimit); + p->vMemory = Vec_IntAlloc( 1 << 18 ); + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the fraiging manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cnf_ManStop( Cnf_Man_t * p ) +{ + Vec_IntFree( p->vMemory ); + free( p->pTruths[0] ); + Aig_MmFlexStop( p->pMemCuts, 0 ); + free( p->pSopSizes ); + free( p->pSops[1] ); + free( p->pSops ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Returns the array of CI IDs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Cnf_DataCollectPiSatNums( Cnf_Dat_t * pCnf, Aig_Man_t * p ) +{ + Vec_Int_t * vCiIds; + Aig_Obj_t * pObj; + int i; + vCiIds = Vec_IntAlloc( Aig_ManPiNum(p) ); + Aig_ManForEachPi( p, pObj, i ) + Vec_IntPush( vCiIds, pCnf->pVarNums[pObj->Id] ); + return vCiIds; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cnf_DataFree( Cnf_Dat_t * p ) +{ + if ( p == NULL ) + return; + free( p->pClauses[0] ); + free( p->pClauses ); + free( p->pVarNums ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Writes CNF into a file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cnf_DataWriteIntoFile( Cnf_Dat_t * p, char * pFileName, int fReadable ) +{ + FILE * pFile; + int * pLit, * pStop, i; + pFile = fopen( pFileName, "w" ); + if ( pFile == NULL ) + { + printf( "Cnf_WriteIntoFile(): Output file cannot be opened.\n" ); + return; + } + fprintf( pFile, "c Result of efficient AIG-to-CNF conversion using package CNF\n" ); + fprintf( pFile, "p %d %d\n", p->nVars, p->nClauses ); + for ( i = 0; i < p->nClauses; i++ ) + { + for ( pLit = p->pClauses[i], pStop = p->pClauses[i+1]; pLit < pStop; pLit++ ) + fprintf( pFile, "%d ", fReadable? Cnf_Lit2Var2(*pLit) : Cnf_Lit2Var(*pLit) ); + fprintf( pFile, "0\n" ); + } + fprintf( pFile, "\n" ); + fclose( pFile ); +} + +/**Function************************************************************* + + Synopsis [Writes CNF into a file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void * Cnf_DataWriteIntoSolver( Cnf_Dat_t * p ) +{ + sat_solver * pSat; + int i, status; + pSat = sat_solver_new(); + sat_solver_setnvars( pSat, p->nVars ); + for ( i = 0; i < p->nClauses; i++ ) + { + if ( !sat_solver_addclause( pSat, p->pClauses[i], p->pClauses[i+1] ) ) + { + sat_solver_delete( pSat ); + return NULL; + } + } + status = sat_solver_simplify(pSat); + if ( status == 0 ) + { + sat_solver_delete( pSat ); + return NULL; + } + return pSat; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/cnf/cnfMap.c b/abc_with_bb_support/src/aig/cnf/cnfMap.c new file mode 100644 index 000000000..ad252815a --- /dev/null +++ b/abc_with_bb_support/src/aig/cnf/cnfMap.c @@ -0,0 +1,356 @@ +/**CFile**************************************************************** + + FileName [cnfMap.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG-to-CNF conversion.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: cnfMap.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cnf.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes area flow of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cnf_CutAssignAreaFlow( Cnf_Man_t * p, Dar_Cut_t * pCut, int * pAreaFlows ) +{ + Aig_Obj_t * pLeaf; + int i; + pCut->Value = 0; + pCut->uSign = 100 * Cnf_CutSopCost( p, pCut ); + Dar_CutForEachLeaf( p->pManAig, pCut, pLeaf, i ) + { + pCut->Value += pLeaf->nRefs; + if ( !Aig_ObjIsNode(pLeaf) ) + continue; + assert( pLeaf->nRefs > 0 ); + pCut->uSign += pAreaFlows[pLeaf->Id] / (pLeaf->nRefs? pLeaf->nRefs : 1); + } +} + +/**Function************************************************************* + + Synopsis [Computes area flow of the supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Cnf_CutSuperAreaFlow( Vec_Ptr_t * vSuper, int * pAreaFlows ) +{ + Aig_Obj_t * pLeaf; + int i, nAreaFlow; + nAreaFlow = 100 * (Vec_PtrSize(vSuper) + 1); + Vec_PtrForEachEntry( vSuper, pLeaf, i ) + { + pLeaf = Aig_Regular(pLeaf); + if ( !Aig_ObjIsNode(pLeaf) ) + continue; + assert( pLeaf->nRefs > 0 ); + nAreaFlow += pAreaFlows[pLeaf->Id] / (pLeaf->nRefs? pLeaf->nRefs : 1); + } + return nAreaFlow; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cnf_DeriveMapping( Cnf_Man_t * p ) +{ + Vec_Ptr_t * vSuper; + Aig_Obj_t * pObj; + Dar_Cut_t * pCut, * pCutBest; + int i, k, AreaFlow, * pAreaFlows; + // allocate area flows + pAreaFlows = ALLOC( int, Aig_ManObjIdMax(p->pManAig) + 1 ); + memset( pAreaFlows, 0, sizeof(int) * (Aig_ManObjIdMax(p->pManAig) + 1) ); + // visit the nodes in the topological order and update their best cuts + vSuper = Vec_PtrAlloc( 100 ); + Aig_ManForEachNode( p->pManAig, pObj, i ) + { + // go through the cuts + pCutBest = NULL; + Dar_ObjForEachCut( pObj, pCut, k ) + { + pCut->fBest = 0; + if ( k == 0 ) + continue; + Cnf_CutAssignAreaFlow( p, pCut, pAreaFlows ); + if ( pCutBest == NULL || pCutBest->uSign > pCut->uSign || + (pCutBest->uSign == pCut->uSign && pCutBest->Value < pCut->Value) ) + pCutBest = pCut; + } + // check the big cut +// Aig_ObjCollectSuper( pObj, vSuper ); + // get the area flow of this cut +// AreaFlow = Cnf_CutSuperAreaFlow( vSuper, pAreaFlows ); + AreaFlow = AIG_INFINITY; + if ( AreaFlow >= (int)pCutBest->uSign ) + { + pAreaFlows[pObj->Id] = pCutBest->uSign; + pCutBest->fBest = 1; + } + else + { + pAreaFlows[pObj->Id] = AreaFlow; + pObj->fMarkB = 1; // mark the special node + } + } + Vec_PtrFree( vSuper ); + free( pAreaFlows ); + +/* + // compute the area of mapping + AreaFlow = 0; + Aig_ManForEachPo( p->pManAig, pObj, i ) + AreaFlow += Dar_ObjBestCut(Aig_ObjFanin0(pObj))->uSign / 100 / Aig_ObjFanin0(pObj)->nRefs; + printf( "Area of the network = %d.\n", AreaFlow ); +*/ +} + + + +#if 0 + +/**Function************************************************************* + + Synopsis [Computes area of the first level.] + + Description [The cut need to be derefed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_CutDeref( Aig_Man_t * p, Dar_Cut_t * pCut ) +{ + Aig_Obj_t * pLeaf; + int i; + Dar_CutForEachLeaf( p, pCut, pLeaf, i ) + { + assert( pLeaf->nRefs > 0 ); + if ( --pLeaf->nRefs > 0 || !Aig_ObjIsAnd(pLeaf) ) + continue; + Aig_CutDeref( p, Aig_ObjBestCut(pLeaf) ); + } +} + +/**Function************************************************************* + + Synopsis [Computes area of the first level.] + + Description [The cut need to be derefed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_CutRef( Aig_Man_t * p, Dar_Cut_t * pCut ) +{ + Aig_Obj_t * pLeaf; + int i, Area = pCut->Value; + Dar_CutForEachLeaf( p, pCut, pLeaf, i ) + { + assert( pLeaf->nRefs >= 0 ); + if ( pLeaf->nRefs++ > 0 || !Aig_ObjIsAnd(pLeaf) ) + continue; + Area += Aig_CutRef( p, Aig_ObjBestCut(pLeaf) ); + } + return Area; +} + +/**Function************************************************************* + + Synopsis [Computes exact area of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Cnf_CutArea( Aig_Man_t * p, Dar_Cut_t * pCut ) +{ + int Area; + Area = Aig_CutRef( p, pCut ); + Aig_CutDeref( p, pCut ); + return Area; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the second cut is better.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Cnf_CutCompare( Dar_Cut_t * pC0, Dar_Cut_t * pC1 ) +{ + if ( pC0->Area < pC1->Area - 0.0001 ) + return -1; + if ( pC0->Area > pC1->Area + 0.0001 ) // smaller area flow is better + return 1; +// if ( pC0->NoRefs < pC1->NoRefs ) +// return -1; +// if ( pC0->NoRefs > pC1->NoRefs ) // fewer non-referenced fanins is better +// return 1; +// if ( pC0->FanRefs / pC0->nLeaves > pC1->FanRefs / pC1->nLeaves ) +// return -1; +// if ( pC0->FanRefs / pC0->nLeaves < pC1->FanRefs / pC1->nLeaves ) +// return 1; // larger average fanin ref-counter is better +// return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Returns the cut with the smallest area flow.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dar_Cut_t * Cnf_ObjFindBestCut( Aig_Obj_t * pObj ) +{ + Dar_Cut_t * pCut, * pCutBest; + int i; + pCutBest = NULL; + Dar_ObjForEachCut( pObj, pCut, i ) + if ( pCutBest == NULL || Cnf_CutCompare(pCutBest, pCut) == 1 ) + pCutBest = pCut; + return pCutBest; +} + +/**Function************************************************************* + + Synopsis [Computes area flow of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cnf_CutAssignArea( Cnf_Man_t * p, Dar_Cut_t * pCut ) +{ + Aig_Obj_t * pLeaf; + int i; + pCut->Area = (float)pCut->Cost; + pCut->NoRefs = 0; + pCut->FanRefs = 0; + Dar_CutForEachLeaf( p->pManAig, pCut, pLeaf, i ) + { + if ( !Aig_ObjIsNode(pLeaf) ) + continue; + if ( pLeaf->nRefs == 0 ) + { + pCut->Area += Aig_ObjBestCut(pLeaf)->Cost; + pCut->NoRefs++; + } + else + { + if ( pCut->FanRefs + pLeaf->nRefs > 15 ) + pCut->FanRefs = 15; + else + pCut->FanRefs += pLeaf->nRefs; + } + } +} + +/**Function************************************************************* + + Synopsis [Performs one round of "area recovery" using exact local area.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Cnf_ManMapForCnf( Cnf_Man_t * p ) +{ + Aig_Obj_t * pObj; + Dar_Cut_t * pCut, * pCutBest; + int i, k; + // visit the nodes in the topological order and update their best cuts + Aig_ManForEachNode( p->pManAig, pObj, i ) + { + // find the old best cut + pCutBest = Aig_ObjBestCut(pObj); + Dar_ObjClearBestCut(pCutBest); + // if the node is used, dereference its cut + if ( pObj->nRefs ) + Aig_CutDeref( p->pManAig, pCutBest ); + + // evaluate the cuts of this node + Dar_ObjForEachCut( pObj, pCut, k ) +// Cnf_CutAssignAreaFlow( p, pCut ); + pCut->Area = (float)Cnf_CutArea( p->pManAig, pCut ); + + // find the new best cut + pCutBest = Cnf_ObjFindBestCut(pObj); + Dar_ObjSetBestCut( pCutBest ); + // if the node is used, reference its cut + if ( pObj->nRefs ) + Aig_CutRef( p->pManAig, pCutBest ); + } + return 1; +} + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/cnf/cnfPost.c b/abc_with_bb_support/src/aig/cnf/cnfPost.c new file mode 100644 index 000000000..d425554a1 --- /dev/null +++ b/abc_with_bb_support/src/aig/cnf/cnfPost.c @@ -0,0 +1,233 @@ +/**CFile**************************************************************** + + FileName [cnfPost.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG-to-CNF conversion.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: cnfPost.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cnf.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cnf_ManPostprocess_old( Cnf_Man_t * p ) +{ +// extern int Aig_ManLargeCutEval( Aig_Man_t * p, Aig_Obj_t * pRoot, Dar_Cut_t * pCutR, Dar_Cut_t * pCutL, int Leaf ); + int nNew, Gain, nGain = 0, nVars = 0; + + Aig_Obj_t * pObj, * pFan; + Dar_Cut_t * pCutBest, * pCut; + int i, k;//, a, b, Counter; + Aig_ManForEachObj( p->pManAig, pObj, i ) + { + if ( !Aig_ObjIsNode(pObj) ) + continue; + if ( pObj->nRefs == 0 ) + continue; +// pCutBest = Aig_ObjBestCut(pObj); + pCutBest = NULL; + + Dar_CutForEachLeaf( p->pManAig, pCutBest, pFan, k ) + { + if ( !Aig_ObjIsNode(pFan) ) + continue; + assert( pFan->nRefs != 0 ); + if ( pFan->nRefs != 1 ) + continue; +// pCut = Aig_ObjBestCut(pFan); + pCut = NULL; +/* + // find how many common variable they have + Counter = 0; + for ( a = 0; a < (int)pCut->nLeaves; a++ ) + { + for ( b = 0; b < (int)pCutBest->nLeaves; b++ ) + if ( pCut->pLeaves[a] == pCutBest->pLeaves[b] ) + break; + if ( b == (int)pCutBest->nLeaves ) + continue; + Counter++; + } + printf( "%d ", Counter ); +*/ + // find the new truth table after collapsing these two cuts + + +// nNew = Aig_ManLargeCutEval( p->pManAig, pObj, pCutBest, pCut, pFan->Id ); + nNew = 0; + + +// printf( "%d+%d=%d:%d(%d) ", pCutBest->Cost, pCut->Cost, +// pCutBest->Cost+pCut->Cost, nNew, pCutBest->Cost+pCut->Cost-nNew ); + + Gain = pCutBest->Value + pCut->Value - nNew; + if ( Gain > 0 ) + { + nGain += Gain; + nVars++; + } + } + } + printf( "Total gain = %d. Vars = %d.\n", nGain, nVars ); +} + +/**Function************************************************************* + + Synopsis [Transfers cuts of the mapped nodes into internal representation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cnf_ManTransferCuts( Cnf_Man_t * p ) +{ + Aig_Obj_t * pObj; + int i; + Aig_MmFlexRestart( p->pMemCuts ); + Aig_ManForEachObj( p->pManAig, pObj, i ) + { + if ( Aig_ObjIsNode(pObj) && pObj->nRefs > 0 ) + pObj->pData = Cnf_CutCreate( p, pObj ); + else + pObj->pData = NULL; + } +} + +/**Function************************************************************* + + Synopsis [Transfers cuts of the mapped nodes into internal representation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cnf_ManFreeCuts( Cnf_Man_t * p ) +{ + Aig_Obj_t * pObj; + int i; + Aig_ManForEachObj( p->pManAig, pObj, i ) + if ( pObj->pData ) + { + Cnf_CutFree( pObj->pData ); + pObj->pData = NULL; + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cnf_ManPostprocess( Cnf_Man_t * p ) +{ + Cnf_Cut_t * pCut, * pCutFan, * pCutRes; + Aig_Obj_t * pObj, * pFan; + int Order[16], Costs[16]; + int i, k, fChanges; + Aig_ManForEachNode( p->pManAig, pObj, i ) + { + if ( pObj->nRefs == 0 ) + continue; + pCut = Cnf_ObjBestCut(pObj); + + // sort fanins according to their size + Cnf_CutForEachLeaf( p->pManAig, pCut, pFan, k ) + { + Order[k] = k; + Costs[k] = Aig_ObjIsNode(pFan)? Cnf_ObjBestCut(pFan)->Cost : 0; + } + // sort the cuts by Weight + do { + int Temp; + fChanges = 0; + for ( k = 0; k < pCut->nFanins - 1; k++ ) + { + if ( Costs[Order[k]] <= Costs[Order[k+1]] ) + continue; + Temp = Order[k]; + Order[k] = Order[k+1]; + Order[k+1] = Temp; + fChanges = 1; + } + } while ( fChanges ); + + +// Cnf_CutForEachLeaf( p->pManAig, pCut, pFan, k ) + for ( k = 0; (k < (int)(pCut)->nFanins) && ((pFan) = Aig_ManObj(p->pManAig, (pCut)->pFanins[Order[k]])); k++ ) + { + if ( !Aig_ObjIsNode(pFan) ) + continue; + assert( pFan->nRefs != 0 ); + if ( pFan->nRefs != 1 ) + continue; + pCutFan = Cnf_ObjBestCut(pFan); + // try composing these two cuts +// Cnf_CutPrint( pCut ); + pCutRes = Cnf_CutCompose( p, pCut, pCutFan, pFan->Id ); +// Cnf_CutPrint( pCut ); +// printf( "\n" ); + // check if the cost if reduced + if ( pCutRes == NULL || pCutRes->Cost == 127 || pCutRes->Cost > pCut->Cost + pCutFan->Cost ) + { + if ( pCutRes ) + Cnf_CutFree( pCutRes ); + continue; + } + // update the cut + Cnf_ObjSetBestCut( pObj, pCutRes ); + Cnf_ObjSetBestCut( pFan, NULL ); + Cnf_CutUpdateRefs( p, pCut, pCutFan, pCutRes ); + assert( pFan->nRefs == 0 ); + Cnf_CutFree( pCut ); + Cnf_CutFree( pCutFan ); + break; + } + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/cnf/cnfUtil.c b/abc_with_bb_support/src/aig/cnf/cnfUtil.c new file mode 100644 index 000000000..63991e709 --- /dev/null +++ b/abc_with_bb_support/src/aig/cnf/cnfUtil.c @@ -0,0 +1,188 @@ +/**CFile**************************************************************** + + FileName [cnfUtil.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG-to-CNF conversion.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: cnfUtil.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cnf.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes area, references, and nodes used in the mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ManScanMapping_rec( Cnf_Man_t * p, Aig_Obj_t * pObj, Vec_Ptr_t * vMapped ) +{ + Aig_Obj_t * pLeaf; + Dar_Cut_t * pCutBest; + int aArea, i; + if ( pObj->nRefs++ || Aig_ObjIsPi(pObj) || Aig_ObjIsConst1(pObj) ) + return 0; + assert( Aig_ObjIsAnd(pObj) ); + // collect the node first to derive pre-order + if ( vMapped ) + Vec_PtrPush( vMapped, pObj ); + // visit the transitive fanin of the selected cut + if ( pObj->fMarkB ) + { + Vec_Ptr_t * vSuper = Vec_PtrAlloc( 100 ); + Aig_ObjCollectSuper( pObj, vSuper ); + aArea = Vec_PtrSize(vSuper) + 1; + Vec_PtrForEachEntry( vSuper, pLeaf, i ) + aArea += Aig_ManScanMapping_rec( p, Aig_Regular(pLeaf), vMapped ); + Vec_PtrFree( vSuper ); + //////////////////////////// + pObj->fMarkB = 1; + } + else + { + pCutBest = Dar_ObjBestCut( pObj ); + aArea = Cnf_CutSopCost( p, pCutBest ); + Dar_CutForEachLeaf( p->pManAig, pCutBest, pLeaf, i ) + aArea += Aig_ManScanMapping_rec( p, pLeaf, vMapped ); + } + return aArea; +} + +/**Function************************************************************* + + Synopsis [Computes area, references, and nodes used in the mapping.] + + Description [Collects the nodes in reverse topological order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Aig_ManScanMapping( Cnf_Man_t * p, int fCollect ) +{ + Vec_Ptr_t * vMapped = NULL; + Aig_Obj_t * pObj; + int i; + // clean all references + Aig_ManForEachObj( p->pManAig, pObj, i ) + pObj->nRefs = 0; + // allocate the array + if ( fCollect ) + vMapped = Vec_PtrAlloc( 1000 ); + // collect nodes reachable from POs in the DFS order through the best cuts + p->aArea = 0; + Aig_ManForEachPo( p->pManAig, pObj, i ) + p->aArea += Aig_ManScanMapping_rec( p, Aig_ObjFanin0(pObj), vMapped ); +// printf( "Variables = %6d. Clauses = %8d.\n", vMapped? Vec_PtrSize(vMapped) + Aig_ManPiNum(p->pManAig) + 1 : 0, p->aArea + 2 ); + return vMapped; +} + +/**Function************************************************************* + + Synopsis [Computes area, references, and nodes used in the mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Cnf_ManScanMapping_rec( Cnf_Man_t * p, Aig_Obj_t * pObj, Vec_Ptr_t * vMapped, int fPreorder ) +{ + Aig_Obj_t * pLeaf; + Cnf_Cut_t * pCutBest; + int aArea, i; + if ( pObj->nRefs++ || Aig_ObjIsPi(pObj) || Aig_ObjIsConst1(pObj) ) + return 0; + assert( Aig_ObjIsAnd(pObj) ); + assert( pObj->pData != NULL ); + // add the node to the mapping + if ( vMapped && fPreorder ) + Vec_PtrPush( vMapped, pObj ); + // visit the transitive fanin of the selected cut + if ( pObj->fMarkB ) + { + Vec_Ptr_t * vSuper = Vec_PtrAlloc( 100 ); + Aig_ObjCollectSuper( pObj, vSuper ); + aArea = Vec_PtrSize(vSuper) + 1; + Vec_PtrForEachEntry( vSuper, pLeaf, i ) + aArea += Cnf_ManScanMapping_rec( p, Aig_Regular(pLeaf), vMapped, fPreorder ); + Vec_PtrFree( vSuper ); + //////////////////////////// + pObj->fMarkB = 1; + } + else + { + pCutBest = pObj->pData; + assert( pCutBest->Cost < 127 ); + aArea = pCutBest->Cost; + Cnf_CutForEachLeaf( p->pManAig, pCutBest, pLeaf, i ) + aArea += Cnf_ManScanMapping_rec( p, pLeaf, vMapped, fPreorder ); + } + // add the node to the mapping + if ( vMapped && !fPreorder ) + Vec_PtrPush( vMapped, pObj ); + return aArea; +} + +/**Function************************************************************* + + Synopsis [Computes area, references, and nodes used in the mapping.] + + Description [Collects the nodes in reverse topological order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Cnf_ManScanMapping( Cnf_Man_t * p, int fCollect, int fPreorder ) +{ + Vec_Ptr_t * vMapped = NULL; + Aig_Obj_t * pObj; + int i; + // clean all references + Aig_ManForEachObj( p->pManAig, pObj, i ) + pObj->nRefs = 0; + // allocate the array + if ( fCollect ) + vMapped = Vec_PtrAlloc( 1000 ); + // collect nodes reachable from POs in the DFS order through the best cuts + p->aArea = 0; + Aig_ManForEachPo( p->pManAig, pObj, i ) + p->aArea += Cnf_ManScanMapping_rec( p, Aig_ObjFanin0(pObj), vMapped, fPreorder ); +// printf( "Variables = %6d. Clauses = %8d.\n", vMapped? Vec_PtrSize(vMapped) + Aig_ManPiNum(p->pManAig) + 1 : 0, p->aArea + 2 ); + return vMapped; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/cnf/cnfWrite.c b/abc_with_bb_support/src/aig/cnf/cnfWrite.c new file mode 100644 index 000000000..4bd23b431 --- /dev/null +++ b/abc_with_bb_support/src/aig/cnf/cnfWrite.c @@ -0,0 +1,443 @@ +/**CFile**************************************************************** + + FileName [cnfWrite.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG-to-CNF conversion.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: cnfWrite.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cnf.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Writes the cover into the array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cnf_SopConvertToVector( char * pSop, int nCubes, Vec_Int_t * vCover ) +{ + int Lits[4], Cube, iCube, i, b; + Vec_IntClear( vCover ); + for ( i = 0; i < nCubes; i++ ) + { + Cube = pSop[i]; + for ( b = 0; b < 4; b++ ) + { + if ( Cube % 3 == 0 ) + Lits[b] = 1; + else if ( Cube % 3 == 1 ) + Lits[b] = 2; + else + Lits[b] = 0; + Cube = Cube / 3; + } + iCube = 0; + for ( b = 0; b < 4; b++ ) + iCube = (iCube << 2) | Lits[b]; + Vec_IntPush( vCover, iCube ); + } +} + +/**Function************************************************************* + + Synopsis [Returns the number of literals in the SOP.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Cnf_SopCountLiterals( char * pSop, int nCubes ) +{ + int nLits = 0, Cube, i, b; + for ( i = 0; i < nCubes; i++ ) + { + Cube = pSop[i]; + for ( b = 0; b < 4; b++ ) + { + if ( Cube % 3 != 2 ) + nLits++; + Cube = Cube / 3; + } + } + return nLits; +} + +/**Function************************************************************* + + Synopsis [Returns the number of literals in the SOP.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Cnf_IsopCountLiterals( Vec_Int_t * vIsop, int nVars ) +{ + int nLits = 0, Cube, i, b; + Vec_IntForEachEntry( vIsop, Cube, i ) + { + for ( b = 0; b < nVars; b++ ) + { + if ( (Cube & 3) == 1 || (Cube & 3) == 2 ) + nLits++; + Cube >>= 2; + } + } + return nLits; +} + +/**Function************************************************************* + + Synopsis [Writes the cube and returns the number of literals in it.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Cnf_IsopWriteCube( int Cube, int nVars, int * pVars, int * pLiterals ) +{ + int nLits = nVars, b; + for ( b = 0; b < nVars; b++ ) + { + if ( (Cube & 3) == 1 ) // value 0 --> write positive literal + *pLiterals++ = 2 * pVars[b]; + else if ( (Cube & 3) == 2 ) // value 1 --> write negative literal + *pLiterals++ = 2 * pVars[b] + 1; + else + nLits--; + Cube >>= 2; + } + return nLits; +} + +/**Function************************************************************* + + Synopsis [Derives CNF for the mapping.] + + Description [The last argument shows the number of last outputs + of the manager, which will not be converted into clauses but the + new variables for which will be introduced.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cnf_Dat_t * Cnf_ManWriteCnf( Cnf_Man_t * p, Vec_Ptr_t * vMapped, int nOutputs ) +{ + Aig_Obj_t * pObj; + Cnf_Dat_t * pCnf; + Cnf_Cut_t * pCut; + Vec_Int_t * vCover, * vSopTemp; + int OutVar, PoVar, pVars[32], * pLits, ** pClas; + unsigned uTruth; + int i, k, nLiterals, nClauses, Cube, Number; + + // count the number of literals and clauses + nLiterals = 1 + Aig_ManPoNum( p->pManAig ) + 3 * nOutputs; + nClauses = 1 + Aig_ManPoNum( p->pManAig ) + nOutputs; + Vec_PtrForEachEntry( vMapped, pObj, i ) + { + assert( Aig_ObjIsNode(pObj) ); + pCut = Cnf_ObjBestCut( pObj ); + + // positive polarity of the cut + if ( pCut->nFanins < 5 ) + { + uTruth = 0xFFFF & *Cnf_CutTruth(pCut); + nLiterals += Cnf_SopCountLiterals( p->pSops[uTruth], p->pSopSizes[uTruth] ) + p->pSopSizes[uTruth]; + assert( p->pSopSizes[uTruth] >= 0 ); + nClauses += p->pSopSizes[uTruth]; + } + else + { + nLiterals += Cnf_IsopCountLiterals( pCut->vIsop[1], pCut->nFanins ) + Vec_IntSize(pCut->vIsop[1]); + nClauses += Vec_IntSize(pCut->vIsop[1]); + } + // negative polarity of the cut + if ( pCut->nFanins < 5 ) + { + uTruth = 0xFFFF & ~*Cnf_CutTruth(pCut); + nLiterals += Cnf_SopCountLiterals( p->pSops[uTruth], p->pSopSizes[uTruth] ) + p->pSopSizes[uTruth]; + assert( p->pSopSizes[uTruth] >= 0 ); + nClauses += p->pSopSizes[uTruth]; + } + else + { + nLiterals += Cnf_IsopCountLiterals( pCut->vIsop[0], pCut->nFanins ) + Vec_IntSize(pCut->vIsop[0]); + nClauses += Vec_IntSize(pCut->vIsop[0]); + } +//printf( "%d ", nClauses-(1 + Aig_ManPoNum( p->pManAig )) ); + } +//printf( "\n" ); + + // allocate CNF + pCnf = ALLOC( Cnf_Dat_t, 1 ); + memset( pCnf, 0, sizeof(Cnf_Dat_t) ); + pCnf->nLiterals = nLiterals; + pCnf->nClauses = nClauses; + pCnf->pClauses = ALLOC( int *, nClauses + 1 ); + pCnf->pClauses[0] = ALLOC( int, nLiterals ); + pCnf->pClauses[nClauses] = pCnf->pClauses[0] + nLiterals; + + // create room for variable numbers + pCnf->pVarNums = ALLOC( int, 1+Aig_ManObjIdMax(p->pManAig) ); + memset( pCnf->pVarNums, 0xff, sizeof(int) * (1+Aig_ManObjIdMax(p->pManAig)) ); + // assign variables to the last (nOutputs) POs + Number = 1; + if ( nOutputs ) + { + assert( nOutputs == Aig_ManRegNum(p->pManAig) ); + Aig_ManForEachLiSeq( p->pManAig, pObj, i ) + pCnf->pVarNums[pObj->Id] = Number++; + } + // assign variables to the internal nodes + Vec_PtrForEachEntry( vMapped, pObj, i ) + pCnf->pVarNums[pObj->Id] = Number++; + // assign variables to the PIs and constant node + Aig_ManForEachPi( p->pManAig, pObj, i ) + pCnf->pVarNums[pObj->Id] = Number++; + pCnf->pVarNums[Aig_ManConst1(p->pManAig)->Id] = Number++; + pCnf->nVars = Number; + + // assign the clauses + vSopTemp = Vec_IntAlloc( 1 << 16 ); + pLits = pCnf->pClauses[0]; + pClas = pCnf->pClauses; + Vec_PtrForEachEntry( vMapped, pObj, i ) + { + pCut = Cnf_ObjBestCut( pObj ); + + // save variables of this cut + OutVar = pCnf->pVarNums[ pObj->Id ]; + for ( k = 0; k < (int)pCut->nFanins; k++ ) + { + pVars[k] = pCnf->pVarNums[ pCut->pFanins[k] ]; + assert( pVars[k] <= Aig_ManObjIdMax(p->pManAig) ); + } + + // positive polarity of the cut + if ( pCut->nFanins < 5 ) + { + uTruth = 0xFFFF & *Cnf_CutTruth(pCut); + Cnf_SopConvertToVector( p->pSops[uTruth], p->pSopSizes[uTruth], vSopTemp ); + vCover = vSopTemp; + } + else + vCover = pCut->vIsop[1]; + Vec_IntForEachEntry( vCover, Cube, k ) + { + *pClas++ = pLits; + *pLits++ = 2 * OutVar; + pLits += Cnf_IsopWriteCube( Cube, pCut->nFanins, pVars, pLits ); + } + + // negative polarity of the cut + if ( pCut->nFanins < 5 ) + { + uTruth = 0xFFFF & ~*Cnf_CutTruth(pCut); + Cnf_SopConvertToVector( p->pSops[uTruth], p->pSopSizes[uTruth], vSopTemp ); + vCover = vSopTemp; + } + else + vCover = pCut->vIsop[0]; + Vec_IntForEachEntry( vCover, Cube, k ) + { + *pClas++ = pLits; + *pLits++ = 2 * OutVar + 1; + pLits += Cnf_IsopWriteCube( Cube, pCut->nFanins, pVars, pLits ); + } + } + Vec_IntFree( vSopTemp ); + + // write the constant literal + OutVar = pCnf->pVarNums[ Aig_ManConst1(p->pManAig)->Id ]; + assert( OutVar <= Aig_ManObjIdMax(p->pManAig) ); + *pClas++ = pLits; + *pLits++ = 2 * OutVar; + + // write the output literals + Aig_ManForEachPo( p->pManAig, pObj, i ) + { + OutVar = pCnf->pVarNums[ Aig_ObjFanin0(pObj)->Id ]; + if ( i < Aig_ManPoNum(p->pManAig) - nOutputs ) + { + *pClas++ = pLits; + *pLits++ = 2 * OutVar + Aig_ObjFaninC0(pObj); + } + else + { + PoVar = pCnf->pVarNums[ pObj->Id ]; + // first clause + *pClas++ = pLits; + *pLits++ = 2 * PoVar; + *pLits++ = 2 * OutVar + !Aig_ObjFaninC0(pObj); + // second clause + *pClas++ = pLits; + *pLits++ = 2 * PoVar + 1; + *pLits++ = 2 * OutVar + Aig_ObjFaninC0(pObj); + } + } + + // verify that the correct number of literals and clauses was written + assert( pLits - pCnf->pClauses[0] == nLiterals ); + assert( pClas - pCnf->pClauses == nClauses ); + return pCnf; +} + + +/**Function************************************************************* + + Synopsis [Derives a simple CNF for the AIG.] + + Description [The last argument shows the number of last outputs + of the manager, which will not be converted into clauses but the + new variables for which will be introduced.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cnf_Dat_t * Cnf_DeriveSimple( Aig_Man_t * p, int nOutputs ) +{ + Aig_Obj_t * pObj; + Cnf_Dat_t * pCnf; + int OutVar, PoVar, pVars[32], * pLits, ** pClas; + int i, nLiterals, nClauses, Number; + + // count the number of literals and clauses + nLiterals = 1 + 7 * Aig_ManNodeNum(p) + Aig_ManPoNum( p ) + 3 * nOutputs; + nClauses = 1 + 3 * Aig_ManNodeNum(p) + Aig_ManPoNum( p ) + nOutputs; + + // allocate CNF + pCnf = ALLOC( Cnf_Dat_t, 1 ); + memset( pCnf, 0, sizeof(Cnf_Dat_t) ); + pCnf->nLiterals = nLiterals; + pCnf->nClauses = nClauses; + pCnf->pClauses = ALLOC( int *, nClauses + 1 ); + pCnf->pClauses[0] = ALLOC( int, nLiterals ); + pCnf->pClauses[nClauses] = pCnf->pClauses[0] + nLiterals; + + // create room for variable numbers + pCnf->pVarNums = ALLOC( int, 1+Aig_ManObjIdMax(p) ); + memset( pCnf->pVarNums, 0xff, sizeof(int) * (1+Aig_ManObjIdMax(p)) ); + // assign variables to the last (nOutputs) POs + Number = 1; + if ( nOutputs ) + { + assert( nOutputs == Aig_ManRegNum(p) ); + Aig_ManForEachLiSeq( p, pObj, i ) + pCnf->pVarNums[pObj->Id] = Number++; + } + // assign variables to the internal nodes + Aig_ManForEachNode( p, pObj, i ) + pCnf->pVarNums[pObj->Id] = Number++; + // assign variables to the PIs and constant node + Aig_ManForEachPi( p, pObj, i ) + pCnf->pVarNums[pObj->Id] = Number++; + pCnf->pVarNums[Aig_ManConst1(p)->Id] = Number++; + pCnf->nVars = Number; +/* + // print CNF numbers + printf( "SAT numbers of each node:\n" ); + Aig_ManForEachObj( p, pObj, i ) + printf( "%d=%d ", pObj->Id, pCnf->pVarNums[pObj->Id] ); + printf( "\n" ); +*/ + // assign the clauses + pLits = pCnf->pClauses[0]; + pClas = pCnf->pClauses; + Aig_ManForEachNode( p, pObj, i ) + { + OutVar = pCnf->pVarNums[ pObj->Id ]; + pVars[0] = pCnf->pVarNums[ Aig_ObjFanin0(pObj)->Id ]; + pVars[1] = pCnf->pVarNums[ Aig_ObjFanin1(pObj)->Id ]; + + // positive phase + *pClas++ = pLits; + *pLits++ = 2 * OutVar; + *pLits++ = 2 * pVars[0] + !Aig_ObjFaninC0(pObj); + *pLits++ = 2 * pVars[1] + !Aig_ObjFaninC1(pObj); + // negative phase + *pClas++ = pLits; + *pLits++ = 2 * OutVar + 1; + *pLits++ = 2 * pVars[0] + Aig_ObjFaninC0(pObj); + *pClas++ = pLits; + *pLits++ = 2 * OutVar + 1; + *pLits++ = 2 * pVars[1] + Aig_ObjFaninC1(pObj); + } + + // write the constant literal + OutVar = pCnf->pVarNums[ Aig_ManConst1(p)->Id ]; + assert( OutVar <= Aig_ManObjIdMax(p) ); + *pClas++ = pLits; + *pLits++ = 2 * OutVar; + + // write the output literals + Aig_ManForEachPo( p, pObj, i ) + { + OutVar = pCnf->pVarNums[ Aig_ObjFanin0(pObj)->Id ]; + if ( i < Aig_ManPoNum(p) - nOutputs ) + { + *pClas++ = pLits; + *pLits++ = 2 * OutVar + Aig_ObjFaninC0(pObj); + } + else + { + PoVar = pCnf->pVarNums[ pObj->Id ]; + // first clause + *pClas++ = pLits; + *pLits++ = 2 * PoVar; + *pLits++ = 2 * OutVar + !Aig_ObjFaninC0(pObj); + // second clause + *pClas++ = pLits; + *pLits++ = 2 * PoVar + 1; + *pLits++ = 2 * OutVar + Aig_ObjFaninC0(pObj); + } + } + + // verify that the correct number of literals and clauses was written + assert( pLits - pCnf->pClauses[0] == nLiterals ); + assert( pClas - pCnf->pClauses == nClauses ); + return pCnf; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/cnf/cnf_.c b/abc_with_bb_support/src/aig/cnf/cnf_.c new file mode 100644 index 000000000..9255a4e68 --- /dev/null +++ b/abc_with_bb_support/src/aig/cnf/cnf_.c @@ -0,0 +1,48 @@ +/**CFile**************************************************************** + + FileName [cnf_.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG-to-CNF conversion.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: cnf_.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cnf.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/cnf/module.make b/abc_with_bb_support/src/aig/cnf/module.make new file mode 100644 index 000000000..f0430c23f --- /dev/null +++ b/abc_with_bb_support/src/aig/cnf/module.make @@ -0,0 +1,8 @@ +SRC += src/aig/cnf/cnfCore.c \ + src/aig/cnf/cnfCut.c \ + src/aig/cnf/cnfData.c \ + src/aig/cnf/cnfMan.c \ + src/aig/cnf/cnfMap.c \ + src/aig/cnf/cnfPost.c \ + src/aig/cnf/cnfUtil.c \ + src/aig/cnf/cnfWrite.c diff --git a/abc_with_bb_support/src/aig/csw/csw.h b/abc_with_bb_support/src/aig/csw/csw.h new file mode 100644 index 000000000..dfcfe8f43 --- /dev/null +++ b/abc_with_bb_support/src/aig/csw/csw.h @@ -0,0 +1,65 @@ +/**CFile**************************************************************** + + FileName [csw.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Cut sweeping.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - July 11, 2007.] + + Revision [$Id: csw.h,v 1.00 2007/07/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __CSW_H__ +#define __CSW_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// ITERATORS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== cnfCore.c ========================================================*/ +extern Aig_Man_t * Csw_Sweep( Aig_Man_t * pAig, int nCutsMax, int nLeafMax, int fVerbose ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/aig/csw/cswCore.c b/abc_with_bb_support/src/aig/csw/cswCore.c new file mode 100644 index 000000000..7e9fb67d1 --- /dev/null +++ b/abc_with_bb_support/src/aig/csw/cswCore.c @@ -0,0 +1,94 @@ +/**CFile**************************************************************** + + FileName [cswCore.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Cut sweeping.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - July 11, 2007.] + + Revision [$Id: cswCore.c,v 1.00 2007/07/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cswInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Man_t * Csw_Sweep( Aig_Man_t * pAig, int nCutsMax, int nLeafMax, int fVerbose ) +{ + Csw_Man_t * p; + Aig_Man_t * pRes; + Aig_Obj_t * pObj, * pObjNew, * pObjRes; + int i, clk; +clk = clock(); + // start the manager + p = Csw_ManStart( pAig, nCutsMax, nLeafMax, fVerbose ); + // set elementary cuts at the PIs + Aig_ManForEachPi( p->pManRes, pObj, i ) + { + Csw_ObjPrepareCuts( p, pObj, 1 ); + Csw_ObjAddRefs( p, pObj, Aig_ManPi(p->pManAig,i)->nRefs ); + } + // process the nodes + Aig_ManForEachNode( pAig, pObj, i ) + { + // create the new node + pObjNew = Aig_And( p->pManRes, Csw_ObjChild0Equiv(p, pObj), Csw_ObjChild1Equiv(p, pObj) ); + // check if this node can be represented using another node +// pObjRes = Csw_ObjSweep( p, Aig_Regular(pObjNew), pObj->nRefs > 1 ); +// pObjRes = Aig_NotCond( pObjRes, Aig_IsComplement(pObjNew) ); + // try recursively if resubsitution is used + do { + pObjRes = Csw_ObjSweep( p, Aig_Regular(pObjNew), pObj->nRefs > 1 ); + pObjRes = Aig_NotCond( pObjRes, Aig_IsComplement(pObjNew) ); + pObjNew = pObjRes; + } while ( Csw_ObjCuts(p, Aig_Regular(pObjNew)) == NULL && !Aig_ObjIsConst1(Aig_Regular(pObjNew)) ); + // save the resulting node + Csw_ObjSetEquiv( p, pObj, pObjRes ); + // add to the reference counter + Csw_ObjAddRefs( p, Aig_Regular(pObjRes), pObj->nRefs ); + } + // add the POs + Aig_ManForEachPo( pAig, pObj, i ) + Aig_ObjCreatePo( p->pManRes, Csw_ObjChild0Equiv(p, pObj) ); + // remove dangling nodes + Aig_ManCleanup( p->pManRes ); + // return the resulting manager +p->timeTotal = clock() - clk; +p->timeOther = p->timeTotal - p->timeCuts - p->timeHash; + pRes = p->pManRes; + Csw_ManStop( p ); + return pRes; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/csw/cswCut.c b/abc_with_bb_support/src/aig/csw/cswCut.c new file mode 100644 index 000000000..f971cdb01 --- /dev/null +++ b/abc_with_bb_support/src/aig/csw/cswCut.c @@ -0,0 +1,602 @@ +/**CFile**************************************************************** + + FileName [cswCut.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Cut sweeping.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - July 11, 2007.] + + Revision [$Id: cswCut.c,v 1.00 2007/07/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cswInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Compute the cost of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Csw_CutFindCost( Csw_Man_t * p, Csw_Cut_t * pCut ) +{ + Aig_Obj_t * pLeaf; + int i, Cost = 0; + assert( pCut->nFanins > 0 ); + Csw_CutForEachLeaf( p->pManRes, pCut, pLeaf, i ) + { +// Cost += pLeaf->nRefs; + Cost += Csw_ObjRefs( p, pLeaf ); +// printf( "%d ", pLeaf->nRefs ); + } +//printf( "\n" ); + return Cost * 100 / pCut->nFanins; +} + +/**Function************************************************************* + + Synopsis [Compute the cost of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline float Csw_CutFindCost2( Csw_Man_t * p, Csw_Cut_t * pCut ) +{ + Aig_Obj_t * pLeaf; + float Cost = 0.0; + int i; + assert( pCut->nFanins > 0 ); + Csw_CutForEachLeaf( p->pManRes, pCut, pLeaf, i ) + Cost += (float)1.0/pLeaf->nRefs; + return 1/Cost; +} + +/**Function************************************************************* + + Synopsis [Returns the next free cut to use.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Csw_Cut_t * Csw_CutFindFree( Csw_Man_t * p, Aig_Obj_t * pObj ) +{ + Csw_Cut_t * pCut, * pCutMax; + int i; + pCutMax = NULL; + Csw_ObjForEachCut( p, pObj, pCut, i ) + { + if ( pCut->nFanins == 0 ) + return pCut; + if ( pCutMax == NULL || pCutMax->Cost < pCut->Cost ) + pCutMax = pCut; + } + assert( pCutMax != NULL ); + pCutMax->nFanins = 0; + return pCutMax; +} + +/**Function************************************************************* + + Synopsis [Computes the stretching phase of the cut w.r.t. the merged cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Cut_TruthPhase( Csw_Cut_t * pCut, Csw_Cut_t * pCut1 ) +{ + unsigned uPhase = 0; + int i, k; + for ( i = k = 0; i < pCut->nFanins; i++ ) + { + if ( k == pCut1->nFanins ) + break; + if ( pCut->pFanins[i] < pCut1->pFanins[k] ) + continue; + assert( pCut->pFanins[i] == pCut1->pFanins[k] ); + uPhase |= (1 << i); + k++; + } + return uPhase; +} + +/**Function************************************************************* + + Synopsis [Performs truth table computation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned * Csw_CutComputeTruth( Csw_Man_t * p, Csw_Cut_t * pCut, Csw_Cut_t * pCut0, Csw_Cut_t * pCut1, int fCompl0, int fCompl1 ) +{ + // permute the first table + if ( fCompl0 ) + Kit_TruthNot( p->puTemp[0], Csw_CutTruth(pCut0), p->nLeafMax ); + else + Kit_TruthCopy( p->puTemp[0], Csw_CutTruth(pCut0), p->nLeafMax ); + Kit_TruthStretch( p->puTemp[2], p->puTemp[0], pCut0->nFanins, p->nLeafMax, Cut_TruthPhase(pCut, pCut0), 0 ); + // permute the second table + if ( fCompl1 ) + Kit_TruthNot( p->puTemp[1], Csw_CutTruth(pCut1), p->nLeafMax ); + else + Kit_TruthCopy( p->puTemp[1], Csw_CutTruth(pCut1), p->nLeafMax ); + Kit_TruthStretch( p->puTemp[3], p->puTemp[1], pCut1->nFanins, p->nLeafMax, Cut_TruthPhase(pCut, pCut1), 0 ); + // produce the resulting table + Kit_TruthAnd( Csw_CutTruth(pCut), p->puTemp[2], p->puTemp[3], p->nLeafMax ); +// assert( pCut->nFanins >= Kit_TruthSupportSize( Csw_CutTruth(pCut), p->nLeafMax ) ); + return Csw_CutTruth(pCut); +} + +/**Function************************************************************* + + Synopsis [Performs support minimization for the truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Csw_CutSupportMinimize( Csw_Man_t * p, Csw_Cut_t * pCut ) +{ + unsigned * pTruth; + int uSupp, nFansNew, i, k; + // get truth table + pTruth = Csw_CutTruth( pCut ); + // get support + uSupp = Kit_TruthSupport( pTruth, p->nLeafMax ); + // get the new support size + nFansNew = Kit_WordCountOnes( uSupp ); + // check if there are redundant variables + if ( nFansNew == pCut->nFanins ) + return nFansNew; + assert( nFansNew < pCut->nFanins ); + // minimize support + Kit_TruthShrink( p->puTemp[0], pTruth, nFansNew, p->nLeafMax, uSupp, 1 ); + for ( i = k = 0; i < pCut->nFanins; i++ ) + if ( uSupp & (1 << i) ) + pCut->pFanins[k++] = pCut->pFanins[i]; + assert( k == nFansNew ); + pCut->nFanins = nFansNew; +// assert( nFansNew == Kit_TruthSupportSize( pTruth, p->nLeafMax ) ); +//Extra_PrintBinary( stdout, pTruth, (1<nLeafMax) ); printf( "\n" ); + return nFansNew; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if pDom is contained in pCut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Csw_CutCheckDominance( Csw_Cut_t * pDom, Csw_Cut_t * pCut ) +{ + int i, k; + for ( i = 0; i < (int)pDom->nFanins; i++ ) + { + for ( k = 0; k < (int)pCut->nFanins; k++ ) + if ( pDom->pFanins[i] == pCut->pFanins[k] ) + break; + if ( k == (int)pCut->nFanins ) // node i in pDom is not contained in pCut + return 0; + } + // every node in pDom is contained in pCut + return 1; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the cut is contained.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Csw_CutFilter( Csw_Man_t * p, Aig_Obj_t * pObj, Csw_Cut_t * pCut ) +{ + Csw_Cut_t * pTemp; + int i; + // go through the cuts of the node + Csw_ObjForEachCut( p, pObj, pTemp, i ) + { + if ( pTemp->nFanins < 2 ) + continue; + if ( pTemp == pCut ) + continue; + if ( pTemp->nFanins > pCut->nFanins ) + { + // skip the non-contained cuts + if ( (pTemp->uSign & pCut->uSign) != pCut->uSign ) + continue; + // check containment seriously + if ( Csw_CutCheckDominance( pCut, pTemp ) ) + { + // remove contained cut + pTemp->nFanins = 0; + } + } + else + { + // skip the non-contained cuts + if ( (pTemp->uSign & pCut->uSign) != pTemp->uSign ) + continue; + // check containment seriously + if ( Csw_CutCheckDominance( pTemp, pCut ) ) + { + // remove the given + pCut->nFanins = 0; + return 1; + } + } + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Merges two cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Csw_CutMergeOrdered( Csw_Man_t * p, Csw_Cut_t * pC0, Csw_Cut_t * pC1, Csw_Cut_t * pC ) +{ + int i, k, c; + assert( pC0->nFanins >= pC1->nFanins ); + // the case of the largest cut sizes + if ( pC0->nFanins == p->nLeafMax && pC1->nFanins == p->nLeafMax ) + { + for ( i = 0; i < pC0->nFanins; i++ ) + if ( pC0->pFanins[i] != pC1->pFanins[i] ) + return 0; + for ( i = 0; i < pC0->nFanins; i++ ) + pC->pFanins[i] = pC0->pFanins[i]; + pC->nFanins = pC0->nFanins; + return 1; + } + // the case when one of the cuts is the largest + if ( pC0->nFanins == p->nLeafMax ) + { + for ( i = 0; i < pC1->nFanins; i++ ) + { + for ( k = pC0->nFanins - 1; k >= 0; k-- ) + if ( pC0->pFanins[k] == pC1->pFanins[i] ) + break; + if ( k == -1 ) // did not find + return 0; + } + for ( i = 0; i < pC0->nFanins; i++ ) + pC->pFanins[i] = pC0->pFanins[i]; + pC->nFanins = pC0->nFanins; + return 1; + } + + // compare two cuts with different numbers + i = k = 0; + for ( c = 0; c < p->nLeafMax; c++ ) + { + if ( k == pC1->nFanins ) + { + if ( i == pC0->nFanins ) + { + pC->nFanins = c; + return 1; + } + pC->pFanins[c] = pC0->pFanins[i++]; + continue; + } + if ( i == pC0->nFanins ) + { + if ( k == pC1->nFanins ) + { + pC->nFanins = c; + return 1; + } + pC->pFanins[c] = pC1->pFanins[k++]; + continue; + } + if ( pC0->pFanins[i] < pC1->pFanins[k] ) + { + pC->pFanins[c] = pC0->pFanins[i++]; + continue; + } + if ( pC0->pFanins[i] > pC1->pFanins[k] ) + { + pC->pFanins[c] = pC1->pFanins[k++]; + continue; + } + pC->pFanins[c] = pC0->pFanins[i++]; + k++; + } + if ( i < pC0->nFanins || k < pC1->nFanins ) + return 0; + pC->nFanins = c; + return 1; +} + +/**Function************************************************************* + + Synopsis [Prepares the object for FPGA mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Csw_CutMerge( Csw_Man_t * p, Csw_Cut_t * pCut0, Csw_Cut_t * pCut1, Csw_Cut_t * pCut ) +{ + assert( p->nLeafMax > 0 ); + // merge the nodes + if ( pCut0->nFanins < pCut1->nFanins ) + { + if ( !Csw_CutMergeOrdered( p, pCut1, pCut0, pCut ) ) + return 0; + } + else + { + if ( !Csw_CutMergeOrdered( p, pCut0, pCut1, pCut ) ) + return 0; + } + pCut->uSign = pCut0->uSign | pCut1->uSign; + return 1; +} + +/**Function************************************************************* + + Synopsis [Consider cut with more than 2 fanins having 2 true variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Csw_ObjTwoVarCut( Csw_Man_t * p, Csw_Cut_t * pCut ) +{ + Aig_Obj_t * pRes, * pIn0, * pIn1; + int nVars, uTruth, fCompl = 0; + assert( pCut->nFanins > 2 ); + // minimize support of this cut + nVars = Csw_CutSupportMinimize( p, pCut ); + assert( nVars == 2 ); + // get the fanins + pIn0 = Aig_ManObj( p->pManRes, pCut->pFanins[0] ); + pIn1 = Aig_ManObj( p->pManRes, pCut->pFanins[1] ); + // derive the truth table + uTruth = 0xF & *Csw_CutTruth(pCut); + if ( uTruth == 14 || uTruth == 13 || uTruth == 11 || uTruth == 7 ) + { + uTruth = 0xF & ~uTruth; + fCompl = 1; + } + // compute the result + pRes = NULL; + if ( uTruth == 1 ) // 0001 // 1110 14 + pRes = Aig_And( p->pManRes, Aig_Not(pIn0), Aig_Not(pIn1) ); + if ( uTruth == 2 ) // 0010 // 1101 13 + pRes = Aig_And( p->pManRes, pIn0 , Aig_Not(pIn1) ); + if ( uTruth == 4 ) // 0100 // 1011 11 + pRes = Aig_And( p->pManRes, Aig_Not(pIn0), pIn1 ); + if ( uTruth == 8 ) // 1000 // 0111 7 + pRes = Aig_And( p->pManRes, pIn0 , pIn1 ); + if ( pRes ) + pRes = Aig_NotCond( pRes, fCompl ); + return pRes; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Csw_Cut_t * Csw_ObjPrepareCuts( Csw_Man_t * p, Aig_Obj_t * pObj, int fTriv ) +{ + Csw_Cut_t * pCutSet, * pCut; + int i; + // create the cutset of the node + pCutSet = (Csw_Cut_t *)Aig_MmFixedEntryFetch( p->pMemCuts ); + Csw_ObjSetCuts( p, pObj, pCutSet ); + Csw_ObjForEachCut( p, pObj, pCut, i ) + { + pCut->nFanins = 0; + pCut->iNode = pObj->Id; + pCut->nCutSize = p->nCutSize; + pCut->nLeafMax = p->nLeafMax; + } + // add unit cut if needed + if ( fTriv ) + { + pCut = pCutSet; + pCut->Cost = 0; + pCut->iNode = pObj->Id; + pCut->nFanins = 1; + pCut->pFanins[0] = pObj->Id; + pCut->uSign = Aig_ObjCutSign( pObj->Id ); + memset( Csw_CutTruth(pCut), 0xAA, sizeof(unsigned) * p->nTruthWords ); + } + return pCutSet; +} + +/**Function************************************************************* + + Synopsis [Derives cuts for one node and sweeps this node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Csw_ObjSweep( Csw_Man_t * p, Aig_Obj_t * pObj, int fTriv ) +{ + int fUseResub = 1; + Csw_Cut_t * pCut0, * pCut1, * pCut, * pCutSet; + Aig_Obj_t * pFanin0 = Aig_ObjFanin0(pObj); + Aig_Obj_t * pFanin1 = Aig_ObjFanin1(pObj); + Aig_Obj_t * pObjNew; + unsigned * pTruth; + int i, k, nVars, nFanins, iVar, clk; + + assert( !Aig_IsComplement(pObj) ); + if ( !Aig_ObjIsNode(pObj) ) + return pObj; + if ( Csw_ObjCuts(p, pObj) ) + return pObj; + // the node is not processed yet + assert( Csw_ObjCuts(p, pObj) == NULL ); + assert( Aig_ObjIsNode(pObj) ); + + // set up the first cut + pCutSet = Csw_ObjPrepareCuts( p, pObj, fTriv ); + + // compute pair-wise cut combinations while checking table + Csw_ObjForEachCut( p, pFanin0, pCut0, i ) + if ( pCut0->nFanins > 0 ) + Csw_ObjForEachCut( p, pFanin1, pCut1, k ) + if ( pCut1->nFanins > 0 ) + { + // make sure K-feasible cut exists + if ( Kit_WordCountOnes(pCut0->uSign | pCut1->uSign) > p->nLeafMax ) + continue; + // get the next cut of this node + pCut = Csw_CutFindFree( p, pObj ); +clk = clock(); + // assemble the new cut + if ( !Csw_CutMerge( p, pCut0, pCut1, pCut ) ) + { + assert( pCut->nFanins == 0 ); + continue; + } + // check containment + if ( Csw_CutFilter( p, pObj, pCut ) ) + { + assert( pCut->nFanins == 0 ); + continue; + } + // create its truth table + pTruth = Csw_CutComputeTruth( p, pCut, pCut0, pCut1, Aig_ObjFaninC0(pObj), Aig_ObjFaninC1(pObj) ); + // support minimize the truth table + nFanins = pCut->nFanins; +// nVars = Csw_CutSupportMinimize( p, pCut ); // leads to quality degradation + nVars = Kit_TruthSupportSize( pTruth, p->nLeafMax ); +p->timeCuts += clock() - clk; + + // check for trivial truth tables + if ( nVars == 0 ) + { + p->nNodesTriv0++; + return Aig_NotCond( Aig_ManConst1(p->pManRes), !(pTruth[0] & 1) ); + } + if ( nVars == 1 ) + { + p->nNodesTriv1++; + iVar = Kit_WordFindFirstBit( Kit_TruthSupport(pTruth, p->nLeafMax) ); + assert( iVar < pCut->nFanins ); + return Aig_NotCond( Aig_ManObj(p->pManRes, pCut->pFanins[iVar]), (pTruth[0] & 1) ); + } + if ( nVars == 2 && nFanins > 2 && fUseResub ) + { + if ( pObjNew = Csw_ObjTwoVarCut( p, pCut ) ) + { + p->nNodesTriv2++; + return pObjNew; + } + } + + // check if an equivalent node with the same cut exists +clk = clock(); + pObjNew = pCut->nFanins > 2 ? Csw_TableCutLookup( p, pCut ) : NULL; +p->timeHash += clock() - clk; + if ( pObjNew ) + { + p->nNodesCuts++; + return pObjNew; + } + + // assign the cost + pCut->Cost = Csw_CutFindCost( p, pCut ); + assert( pCut->nFanins > 0 ); + assert( pCut->Cost > 0 ); + } + p->nNodesTried++; + + // load the resulting cuts into the table +clk = clock(); + Csw_ObjForEachCut( p, pObj, pCut, i ) + { + if ( pCut->nFanins > 2 ) + { + assert( pCut->Cost > 0 ); + Csw_TableCutInsert( p, pCut ); + } + } +p->timeHash += clock() - clk; + + // return the node if could not replace it + return pObj; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/csw/cswInt.h b/abc_with_bb_support/src/aig/csw/cswInt.h new file mode 100644 index 000000000..25e639784 --- /dev/null +++ b/abc_with_bb_support/src/aig/csw/cswInt.h @@ -0,0 +1,157 @@ +/**CFile**************************************************************** + + FileName [cswInt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Cut sweeping.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - July 11, 2007.] + + Revision [$Id: cswInt.h,v 1.00 2007/07/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __CSW_INT_H__ +#define __CSW_INT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +#include "aig.h" +#include "dar.h" +#include "kit.h" +#include "csw.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Csw_Man_t_ Csw_Man_t; +typedef struct Csw_Cut_t_ Csw_Cut_t; + +// the cut used to represent node in the AIG +struct Csw_Cut_t_ +{ + Csw_Cut_t * pNext; // the next cut in the table + int Cost; // the cost of the cut +// float Cost; // the cost of the cut + unsigned uSign; // cut signature + int iNode; // the node, for which it is the cut + short nCutSize; // the number of bytes in the cut + char nLeafMax; // the maximum number of fanins + char nFanins; // the current number of fanins + int pFanins[0]; // the fanins (followed by the truth table) +}; + +// the CNF computation manager +struct Csw_Man_t_ +{ + // AIG manager + Aig_Man_t * pManAig; // the input AIG manager + Aig_Man_t * pManRes; // the output AIG manager + Aig_Obj_t ** pEquiv; // the equivalent nodes in the resulting manager + Csw_Cut_t ** pCuts; // the cuts for each node in the output manager + int * pnRefs; // the number of references of each new node + // hash table for cuts + Csw_Cut_t ** pTable; // the table composed of cuts + int nTableSize; // the size of hash table + // parameters + int nCutsMax; // the max number of cuts at the node + int nLeafMax; // the max number of leaves of a cut + int fVerbose; // enables verbose output + // internal variables + int nCutSize; // the number of bytes needed to store one cut + int nTruthWords; // the number of truth table words + Aig_MmFixed_t * pMemCuts; // memory manager for cuts + unsigned * puTemp[4]; // used for the truth table computation + // statistics + int nNodesTriv0; // the number of trivial nodes + int nNodesTriv1; // the number of trivial nodes + int nNodesTriv2; // the number of trivial nodes + int nNodesCuts; // the number of rewritten nodes + int nNodesTried; // the number of nodes tried + int timeCuts; // time to compute the cut and its truth table + int timeHash; // time for hashing cuts + int timeOther; // other time + int timeTotal; // total time +}; + +static inline int Csw_CutLeaveNum( Csw_Cut_t * pCut ) { return pCut->nFanins; } +static inline int * Csw_CutLeaves( Csw_Cut_t * pCut ) { return pCut->pFanins; } +static inline unsigned * Csw_CutTruth( Csw_Cut_t * pCut ) { return (unsigned *)(pCut->pFanins + pCut->nLeafMax); } +static inline Csw_Cut_t * Csw_CutNext( Csw_Cut_t * pCut ) { return (Csw_Cut_t *)(((char *)pCut) + pCut->nCutSize); } + +static inline int Csw_ObjRefs( Csw_Man_t * p, Aig_Obj_t * pObj ) { return p->pnRefs[pObj->Id]; } +static inline void Csw_ObjAddRefs( Csw_Man_t * p, Aig_Obj_t * pObj, int nRefs ) { p->pnRefs[pObj->Id] += nRefs; } + +static inline Csw_Cut_t * Csw_ObjCuts( Csw_Man_t * p, Aig_Obj_t * pObj ) { return p->pCuts[pObj->Id]; } +static inline void Csw_ObjSetCuts( Csw_Man_t * p, Aig_Obj_t * pObj, Csw_Cut_t * pCuts ) { p->pCuts[pObj->Id] = pCuts; } + +static inline Aig_Obj_t * Csw_ObjEquiv( Csw_Man_t * p, Aig_Obj_t * pObj ) { return p->pEquiv[pObj->Id]; } +static inline void Csw_ObjSetEquiv( Csw_Man_t * p, Aig_Obj_t * pObj, Aig_Obj_t * pEquiv ) { p->pEquiv[pObj->Id] = pEquiv; } + +static inline Aig_Obj_t * Csw_ObjChild0Equiv( Csw_Man_t * p, Aig_Obj_t * pObj ) { assert( !Aig_IsComplement(pObj) ); return Aig_ObjFanin0(pObj)? Aig_NotCond(Csw_ObjEquiv(p, Aig_ObjFanin0(pObj)), Aig_ObjFaninC0(pObj)) : NULL; } +static inline Aig_Obj_t * Csw_ObjChild1Equiv( Csw_Man_t * p, Aig_Obj_t * pObj ) { assert( !Aig_IsComplement(pObj) ); return Aig_ObjFanin1(pObj)? Aig_NotCond(Csw_ObjEquiv(p, Aig_ObjFanin1(pObj)), Aig_ObjFaninC1(pObj)) : NULL; } + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// ITERATORS /// +//////////////////////////////////////////////////////////////////////// + +// iterator over cuts of the node +#define Csw_ObjForEachCut( p, pObj, pCut, i ) \ + for ( i = 0, pCut = Csw_ObjCuts(p, pObj); i < p->nCutsMax; i++, pCut = Csw_CutNext(pCut) ) +// iterator over leaves of the cut +#define Csw_CutForEachLeaf( p, pCut, pLeaf, i ) \ + for ( i = 0; (i < (int)(pCut)->nFanins) && ((pLeaf) = Aig_ManObj(p, (pCut)->pFanins[i])); i++ ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== cnfCut.c ========================================================*/ +extern Csw_Cut_t * Csw_ObjPrepareCuts( Csw_Man_t * p, Aig_Obj_t * pObj, int fTriv ); +extern Aig_Obj_t * Csw_ObjSweep( Csw_Man_t * p, Aig_Obj_t * pObj, int fTriv ); +/*=== cnfMan.c ========================================================*/ +extern Csw_Man_t * Csw_ManStart( Aig_Man_t * pMan, int nCutsMax, int nLeafMax, int fVerbose ); +extern void Csw_ManStop( Csw_Man_t * p ); +/*=== cnfTable.c ========================================================*/ +extern int Csw_TableCountCuts( Csw_Man_t * p ); +extern void Csw_TableCutInsert( Csw_Man_t * p, Csw_Cut_t * pCut ); +extern Aig_Obj_t * Csw_TableCutLookup( Csw_Man_t * p, Csw_Cut_t * pCut ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/aig/csw/cswMan.c b/abc_with_bb_support/src/aig/csw/cswMan.c new file mode 100644 index 000000000..a91b6a63d --- /dev/null +++ b/abc_with_bb_support/src/aig/csw/cswMan.c @@ -0,0 +1,125 @@ +/**CFile**************************************************************** + + FileName [cswMan.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Cut sweeping.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - July 11, 2007.] + + Revision [$Id: cswMan.c,v 1.00 2007/07/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cswInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the cut sweeping manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Csw_Man_t * Csw_ManStart( Aig_Man_t * pMan, int nCutsMax, int nLeafMax, int fVerbose ) +{ + Csw_Man_t * p; + Aig_Obj_t * pObj; + int i; + assert( nCutsMax >= 2 ); + assert( nLeafMax <= 16 ); + // allocate the fraiging manager + p = ALLOC( Csw_Man_t, 1 ); + memset( p, 0, sizeof(Csw_Man_t) ); + p->nCutsMax = nCutsMax; + p->nLeafMax = nLeafMax; + p->fVerbose = fVerbose; + p->pManAig = pMan; + // create the new manager + p->pManRes = Aig_ManStartFrom( pMan ); + assert( Aig_ManPiNum(p->pManAig) == Aig_ManPiNum(p->pManRes) ); + // allocate room for cuts and equivalent nodes + p->pnRefs = ALLOC( int, Aig_ManObjIdMax(pMan) + 1 ); + p->pEquiv = ALLOC( Aig_Obj_t *, Aig_ManObjIdMax(pMan) + 1 ); + p->pCuts = ALLOC( Csw_Cut_t *, Aig_ManObjIdMax(pMan) + 1 ); + memset( p->pCuts, 0, sizeof(Aig_Obj_t *) * (Aig_ManObjIdMax(pMan) + 1) ); + memset( p->pnRefs, 0, sizeof(int) * (Aig_ManObjIdMax(pMan) + 1) ); + // allocate memory manager + p->nTruthWords = Aig_TruthWordNum(nLeafMax); + p->nCutSize = sizeof(Csw_Cut_t) + sizeof(int) * nLeafMax + sizeof(unsigned) * p->nTruthWords; + p->pMemCuts = Aig_MmFixedStart( p->nCutSize * p->nCutsMax, 512 ); + // allocate hash table for cuts + p->nTableSize = Aig_PrimeCudd( Aig_ManNodeNum(pMan) * p->nCutsMax / 2 ); + p->pTable = ALLOC( Csw_Cut_t *, p->nTableSize ); + memset( p->pTable, 0, sizeof(Aig_Obj_t *) * p->nTableSize ); + // set the pointers to the available fraig nodes + Csw_ObjSetEquiv( p, Aig_ManConst1(p->pManAig), Aig_ManConst1(p->pManRes) ); + Aig_ManForEachPi( p->pManAig, pObj, i ) + Csw_ObjSetEquiv( p, pObj, Aig_ManPi(p->pManRes, i) ); + // room for temporary truth tables + p->puTemp[0] = ALLOC( unsigned, 4 * p->nTruthWords ); + p->puTemp[1] = p->puTemp[0] + p->nTruthWords; + p->puTemp[2] = p->puTemp[1] + p->nTruthWords; + p->puTemp[3] = p->puTemp[2] + p->nTruthWords; + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the fraiging manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Csw_ManStop( Csw_Man_t * p ) +{ + if ( p->fVerbose ) + { + int nNodesBeg = Aig_ManNodeNum(p->pManAig); + int nNodesEnd = Aig_ManNodeNum(p->pManRes); + printf( "Beg = %7d. End = %7d. (%6.2f %%) Try = %7d. Cuts = %8d.\n", + nNodesBeg, nNodesEnd, 100.0*(nNodesBeg-nNodesEnd)/nNodesBeg, + p->nNodesTried, Csw_TableCountCuts( p ) ); + printf( "Triv0 = %6d. Triv1 = %6d. Triv2 = %6d. Cut-replace = %6d.\n", + p->nNodesTriv0, p->nNodesTriv1, p->nNodesTriv2, p->nNodesCuts ); + PRTP( "Cuts ", p->timeCuts, p->timeTotal ); + PRTP( "Hashing ", p->timeHash, p->timeTotal ); + PRTP( "Other ", p->timeOther, p->timeTotal ); + PRTP( "TOTAL ", p->timeTotal, p->timeTotal ); + } + free( p->puTemp[0] ); + Aig_MmFixedStop( p->pMemCuts, 0 ); + free( p->pnRefs ); + free( p->pEquiv ); + free( p->pCuts ); + free( p->pTable ); + free( p ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/csw/cswTable.c b/abc_with_bb_support/src/aig/csw/cswTable.c new file mode 100644 index 000000000..ff411694b --- /dev/null +++ b/abc_with_bb_support/src/aig/csw/cswTable.c @@ -0,0 +1,161 @@ +/**CFile**************************************************************** + + FileName [cswTable.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Cut sweeping.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - July 11, 2007.] + + Revision [$Id: cswTable.c,v 1.00 2007/07/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cswInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes hash value of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Csw_CutHash( Csw_Cut_t * pCut ) +{ + static int s_FPrimes[128] = { + 1009, 1049, 1093, 1151, 1201, 1249, 1297, 1361, 1427, 1459, + 1499, 1559, 1607, 1657, 1709, 1759, 1823, 1877, 1933, 1997, + 2039, 2089, 2141, 2213, 2269, 2311, 2371, 2411, 2467, 2543, + 2609, 2663, 2699, 2741, 2797, 2851, 2909, 2969, 3037, 3089, + 3169, 3221, 3299, 3331, 3389, 3461, 3517, 3557, 3613, 3671, + 3719, 3779, 3847, 3907, 3943, 4013, 4073, 4129, 4201, 4243, + 4289, 4363, 4441, 4493, 4549, 4621, 4663, 4729, 4793, 4871, + 4933, 4973, 5021, 5087, 5153, 5227, 5281, 5351, 5417, 5471, + 5519, 5573, 5651, 5693, 5749, 5821, 5861, 5923, 6011, 6073, + 6131, 6199, 6257, 6301, 6353, 6397, 6481, 6563, 6619, 6689, + 6737, 6803, 6863, 6917, 6977, 7027, 7109, 7187, 7237, 7309, + 7393, 7477, 7523, 7561, 7607, 7681, 7727, 7817, 7877, 7933, + 8011, 8039, 8059, 8081, 8093, 8111, 8123, 8147 + }; + unsigned uHash; + int i; + assert( pCut->nFanins <= 16 ); + uHash = 0; + for ( i = 0; i < pCut->nFanins; i++ ) + uHash ^= pCut->pFanins[i] * s_FPrimes[i]; + return uHash; +} + +/**Function************************************************************* + + Synopsis [Returns the total number of cuts in the table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Csw_TableCountCuts( Csw_Man_t * p ) +{ + Csw_Cut_t * pEnt; + int i, Counter = 0; + for ( i = 0; i < p->nTableSize; i++ ) + for ( pEnt = p->pTable[i]; pEnt; pEnt = pEnt->pNext ) + Counter++; + return Counter; +} + +/**Function************************************************************* + + Synopsis [Adds the cut to the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Csw_TableCutInsert( Csw_Man_t * p, Csw_Cut_t * pCut ) +{ + int iEntry = Csw_CutHash(pCut) % p->nTableSize; + pCut->pNext = p->pTable[iEntry]; + p->pTable[iEntry] = pCut; +} + +/**Function************************************************************* + + Synopsis [Returns an equivalent node if it exists.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Csw_TableCutLookup( Csw_Man_t * p, Csw_Cut_t * pCut ) +{ + Aig_Obj_t * pRes = NULL; + Csw_Cut_t * pEnt; + unsigned * pTruthNew, * pTruthOld; + int iEntry = Csw_CutHash(pCut) % p->nTableSize; + for ( pEnt = p->pTable[iEntry]; pEnt; pEnt = pEnt->pNext ) + { + if ( pEnt->nFanins != pCut->nFanins ) + continue; + if ( pEnt->uSign != pCut->uSign ) + continue; + if ( memcmp( pEnt->pFanins, pCut->pFanins, sizeof(int) * pCut->nFanins ) ) + continue; + pTruthOld = Csw_CutTruth(pEnt); + pTruthNew = Csw_CutTruth(pCut); + if ( (pTruthOld[0] & 1) == (pTruthNew[0] & 1) ) + { + if ( Kit_TruthIsEqual( pTruthOld, pTruthNew, pCut->nFanins ) ) + { + pRes = Aig_ManObj( p->pManRes, pEnt->iNode ); + assert( pRes->fPhase == Aig_ManObj( p->pManRes, pCut->iNode )->fPhase ); + break; + } + } + else + { + if ( Kit_TruthIsOpposite( pTruthOld, pTruthNew, pCut->nFanins ) ) + { + pRes = Aig_Not( Aig_ManObj( p->pManRes, pEnt->iNode ) ); + assert( Aig_Regular(pRes)->fPhase != Aig_ManObj( p->pManRes, pCut->iNode )->fPhase ); + break; + } + } + } + return pRes; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/csw/csw_.c b/abc_with_bb_support/src/aig/csw/csw_.c new file mode 100644 index 000000000..f89e3f5ad --- /dev/null +++ b/abc_with_bb_support/src/aig/csw/csw_.c @@ -0,0 +1,48 @@ +/**CFile**************************************************************** + + FileName [csw_.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Cut sweeping.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - July 11, 2007.] + + Revision [$Id: csw_.c,v 1.00 2007/07/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cswInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/csw/module.make b/abc_with_bb_support/src/aig/csw/module.make new file mode 100644 index 000000000..e9cab5c5e --- /dev/null +++ b/abc_with_bb_support/src/aig/csw/module.make @@ -0,0 +1,4 @@ +SRC += src/aig/csw/cswCore.c \ + src/aig/csw/cswCut.c \ + src/aig/csw/cswMan.c \ + src/aig/csw/cswTable.c diff --git a/abc_with_bb_support/src/aig/dar/dar.h b/abc_with_bb_support/src/aig/dar/dar.h new file mode 100644 index 000000000..107055f2c --- /dev/null +++ b/abc_with_bb_support/src/aig/dar/dar.h @@ -0,0 +1,101 @@ +/**CFile**************************************************************** + + FileName [dar.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: dar.h,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __DAR_H__ +#define __DAR_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Dar_RwrPar_t_ Dar_RwrPar_t; +typedef struct Dar_RefPar_t_ Dar_RefPar_t; + +struct Dar_RwrPar_t_ +{ + int nCutsMax; // the maximum number of cuts to try + int nSubgMax; // the maximum number of subgraphs to try + int fFanout; // support fanout representation + int fUpdateLevel; // update level + int fUseZeros; // performs zero-cost replacement + int fVerbose; // enables verbose output + int fVeryVerbose; // enables very verbose output +}; + +struct Dar_RefPar_t_ +{ + int nMffcMin; // the min MFFC size for which refactoring is used + int nLeafMax; // the max number of leaves of a cut + int nCutsMax; // the max number of cuts to consider + int fExtend; // extends the cut below MFFC + int fUpdateLevel; // updates the level after each move + int fUseZeros; // perform zero-cost replacements + int fVerbose; // verbosity level + int fVeryVerbose; // enables very verbose output +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// ITERATORS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== darBalance.c ========================================================*/ +extern Aig_Man_t * Dar_ManBalance( Aig_Man_t * p, int fUpdateLevel ); +/*=== darCore.c ========================================================*/ +extern void Dar_ManDefaultRwrParams( Dar_RwrPar_t * pPars ); +extern int Dar_ManRewrite( Aig_Man_t * pAig, Dar_RwrPar_t * pPars ); +extern Aig_MmFixed_t * Dar_ManComputeCuts( Aig_Man_t * pAig, int nCutsMax ); +/*=== darRefact.c ========================================================*/ +extern void Dar_ManDefaultRefParams( Dar_RefPar_t * pPars ); +extern int Dar_ManRefactor( Aig_Man_t * pAig, Dar_RefPar_t * pPars ); +/*=== darScript.c ========================================================*/ +extern Aig_Man_t * Dar_ManRewriteDefault( Aig_Man_t * pAig ); +extern Aig_Man_t * Dar_ManCompress2( Aig_Man_t * pAig, int fBalance, int fUpdateLevel, int fVerbose ); +extern Aig_Man_t * Dar_ManRwsat( Aig_Man_t * pAig, int fBalance, int fVerbose ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/aig/dar/darBalance.c b/abc_with_bb_support/src/aig/dar/darBalance.c new file mode 100644 index 000000000..84f6aba14 --- /dev/null +++ b/abc_with_bb_support/src/aig/dar/darBalance.c @@ -0,0 +1,395 @@ +/**CFile**************************************************************** + + FileName [darBalance.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting.] + + Synopsis [Algebraic AIG balancing.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: darBalance.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "darInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Aig_Obj_t * Dar_Balance_rec( Aig_Man_t * pNew, Aig_Obj_t * pObj, Vec_Vec_t * vStore, int Level, int fUpdateLevel ); +static Vec_Ptr_t * Dar_BalanceCone( Aig_Obj_t * pObj, Vec_Vec_t * vStore, int Level ); +static int Dar_BalanceFindLeft( Vec_Ptr_t * vSuper ); +static void Dar_BalancePermute( Aig_Man_t * p, Vec_Ptr_t * vSuper, int LeftBound, int fExor ); +static void Dar_BalancePushUniqueOrderByLevel( Vec_Ptr_t * vStore, Aig_Obj_t * pObj ); +static Aig_Obj_t * Dar_BalanceBuildSuper( Aig_Man_t * p, Vec_Ptr_t * vSuper, Aig_Type_t Type, int fUpdateLevel ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs algebraic balancing of the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Man_t * Dar_ManBalance( Aig_Man_t * p, int fUpdateLevel ) +{ + Aig_Man_t * pNew; + Aig_Obj_t * pObj, * pDriver, * pObjNew; + Vec_Vec_t * vStore; + int i; + // create the new manager + pNew = Aig_ManStart( Aig_ManObjIdMax(p) + 1 ); + pNew->nRegs = p->nRegs; + pNew->nAsserts = p->nAsserts; + // map the PI nodes + Aig_ManCleanData( p ); + Aig_ManConst1(p)->pData = Aig_ManConst1(pNew); + Aig_ManForEachPi( p, pObj, i ) + pObj->pData = Aig_ObjCreatePi(pNew); + // balance the AIG + vStore = Vec_VecAlloc( 50 ); + Aig_ManForEachPo( p, pObj, i ) + { + pDriver = Aig_ObjReal_rec( Aig_ObjChild0(pObj) ); + pObjNew = Dar_Balance_rec( pNew, Aig_Regular(pDriver), vStore, 0, fUpdateLevel ); + pObjNew = Aig_NotCond( pObjNew, Aig_IsComplement(pDriver) ); + Aig_ObjCreatePo( pNew, pObjNew ); + } + Vec_VecFree( vStore ); + // remove dangling nodes + if ( i = Aig_ManCleanup( pNew ) ) + printf( "Cleanup after balancing removed %d dangling nodes.\n", i ); + // check the resulting AIG + if ( !Aig_ManCheck(pNew) ) + printf( "Dar_ManBalance(): The check has failed.\n" ); + return pNew; +} + +/**Function************************************************************* + + Synopsis [Returns the new node constructed.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Dar_Balance_rec( Aig_Man_t * pNew, Aig_Obj_t * pObjOld, Vec_Vec_t * vStore, int Level, int fUpdateLevel ) +{ + Aig_Obj_t * pObjNew; + Vec_Ptr_t * vSuper; + int i; + assert( !Aig_IsComplement(pObjOld) ); + assert( !Aig_ObjIsBuf(pObjOld) ); + // return if the result is known + if ( pObjOld->pData ) + return pObjOld->pData; + assert( Aig_ObjIsNode(pObjOld) ); + // get the implication supergate + vSuper = Dar_BalanceCone( pObjOld, vStore, Level ); + // check if supergate contains two nodes in the opposite polarity + if ( vSuper->nSize == 0 ) + return pObjOld->pData = Aig_ManConst0(pNew); + if ( Vec_PtrSize(vSuper) < 2 ) + printf( "BUG!\n" ); + // for each old node, derive the new well-balanced node + for ( i = 0; i < Vec_PtrSize(vSuper); i++ ) + { + pObjNew = Dar_Balance_rec( pNew, Aig_Regular(vSuper->pArray[i]), vStore, Level + 1, fUpdateLevel ); + vSuper->pArray[i] = Aig_NotCond( pObjNew, Aig_IsComplement(vSuper->pArray[i]) ); + } + // build the supergate + pObjNew = Dar_BalanceBuildSuper( pNew, vSuper, Aig_ObjType(pObjOld), fUpdateLevel ); + // make sure the balanced node is not assigned +// assert( pObjOld->Level >= Aig_Regular(pObjNew)->Level ); + assert( pObjOld->pData == NULL ); + return pObjOld->pData = pObjNew; +} + +/**Function************************************************************* + + Synopsis [Collects the nodes of the supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dar_BalanceCone_rec( Aig_Obj_t * pRoot, Aig_Obj_t * pObj, Vec_Ptr_t * vSuper ) +{ + int RetValue1, RetValue2, i; + // check if the node is visited + if ( Aig_Regular(pObj)->fMarkB ) + { + // check if the node occurs in the same polarity + for ( i = 0; i < vSuper->nSize; i++ ) + if ( vSuper->pArray[i] == pObj ) + return 1; + // check if the node is present in the opposite polarity + for ( i = 0; i < vSuper->nSize; i++ ) + if ( vSuper->pArray[i] == Aig_Not(pObj) ) + return -1; + assert( 0 ); + return 0; + } + // if the new node is complemented or a PI, another gate begins + if ( pObj != pRoot && (Aig_IsComplement(pObj) || Aig_ObjType(pObj) != Aig_ObjType(pRoot) || Aig_ObjRefs(pObj) > 1) ) + { + Vec_PtrPush( vSuper, pObj ); + Aig_Regular(pObj)->fMarkB = 1; + return 0; + } + assert( !Aig_IsComplement(pObj) ); + assert( Aig_ObjIsNode(pObj) ); + // go through the branches + RetValue1 = Dar_BalanceCone_rec( pRoot, Aig_ObjReal_rec( Aig_ObjChild0(pObj) ), vSuper ); + RetValue2 = Dar_BalanceCone_rec( pRoot, Aig_ObjReal_rec( Aig_ObjChild1(pObj) ), vSuper ); + if ( RetValue1 == -1 || RetValue2 == -1 ) + return -1; + // return 1 if at least one branch has a duplicate + return RetValue1 || RetValue2; +} + +/**Function************************************************************* + + Synopsis [Collects the nodes of the supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Dar_BalanceCone( Aig_Obj_t * pObj, Vec_Vec_t * vStore, int Level ) +{ + Vec_Ptr_t * vNodes; + int RetValue, i; + assert( !Aig_IsComplement(pObj) ); + // extend the storage + if ( Vec_VecSize( vStore ) <= Level ) + Vec_VecPush( vStore, Level, 0 ); + // get the temporary array of nodes + vNodes = Vec_VecEntry( vStore, Level ); + Vec_PtrClear( vNodes ); + // collect the nodes in the implication supergate + RetValue = Dar_BalanceCone_rec( pObj, pObj, vNodes ); + assert( vNodes->nSize > 1 ); + // unmark the visited nodes + Vec_PtrForEachEntry( vNodes, pObj, i ) + Aig_Regular(pObj)->fMarkB = 0; + // if we found the node and its complement in the same implication supergate, + // return empty set of nodes (meaning that we should use constant-0 node) + if ( RetValue == -1 ) + vNodes->nSize = 0; + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Procedure used for sorting the nodes in decreasing order of levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_NodeCompareLevelsDecrease( Aig_Obj_t ** pp1, Aig_Obj_t ** pp2 ) +{ + int Diff = Aig_ObjLevel(Aig_Regular(*pp1)) - Aig_ObjLevel(Aig_Regular(*pp2)); + if ( Diff > 0 ) + return -1; + if ( Diff < 0 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Builds implication supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Dar_BalanceBuildSuper( Aig_Man_t * p, Vec_Ptr_t * vSuper, Aig_Type_t Type, int fUpdateLevel ) +{ + Aig_Obj_t * pObj1, * pObj2; + int LeftBound; + assert( vSuper->nSize > 1 ); + // sort the new nodes by level in the decreasing order + Vec_PtrSort( vSuper, Aig_NodeCompareLevelsDecrease ); + // balance the nodes + while ( vSuper->nSize > 1 ) + { + // find the left bound on the node to be paired + LeftBound = (!fUpdateLevel)? 0 : Dar_BalanceFindLeft( vSuper ); + // find the node that can be shared (if no such node, randomize choice) + Dar_BalancePermute( p, vSuper, LeftBound, Type == AIG_OBJ_EXOR ); + // pull out the last two nodes + pObj1 = Vec_PtrPop(vSuper); + pObj2 = Vec_PtrPop(vSuper); + Dar_BalancePushUniqueOrderByLevel( vSuper, Aig_Oper(p, pObj1, pObj2, Type) ); + } + return Vec_PtrEntry(vSuper, 0); +} + +/**Function************************************************************* + + Synopsis [Finds the left bound on the next candidate to be paired.] + + Description [The nodes in the array are in the decreasing order of levels. + The last node in the array has the smallest level. By default it would be paired + with the next node on the left. However, it may be possible to pair it with some + other node on the left, in such a way that the new node is shared. This procedure + finds the index of the left-most node, which can be paired with the last node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dar_BalanceFindLeft( Vec_Ptr_t * vSuper ) +{ + Aig_Obj_t * pObjRight, * pObjLeft; + int Current; + // if two or less nodes, pair with the first + if ( Vec_PtrSize(vSuper) < 3 ) + return 0; + // set the pointer to the one before the last + Current = Vec_PtrSize(vSuper) - 2; + pObjRight = Vec_PtrEntry( vSuper, Current ); + // go through the nodes to the left of this one + for ( Current--; Current >= 0; Current-- ) + { + // get the next node on the left + pObjLeft = Vec_PtrEntry( vSuper, Current ); + // if the level of this node is different, quit the loop + if ( Aig_ObjLevel(Aig_Regular(pObjLeft)) != Aig_ObjLevel(Aig_Regular(pObjRight)) ) + break; + } + Current++; + // get the node, for which the equality holds + pObjLeft = Vec_PtrEntry( vSuper, Current ); + assert( Aig_ObjLevel(Aig_Regular(pObjLeft)) == Aig_ObjLevel(Aig_Regular(pObjRight)) ); + return Current; +} + +/**Function************************************************************* + + Synopsis [Moves closer to the end the node that is best for sharing.] + + Description [If there is no node with sharing, randomly chooses one of + the legal nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_BalancePermute( Aig_Man_t * p, Vec_Ptr_t * vSuper, int LeftBound, int fExor ) +{ + Aig_Obj_t * pObj1, * pObj2, * pObj3, * pGhost; + int RightBound, i; + // get the right bound + RightBound = Vec_PtrSize(vSuper) - 2; + assert( LeftBound <= RightBound ); + if ( LeftBound == RightBound ) + return; + // get the two last nodes + pObj1 = Vec_PtrEntry( vSuper, RightBound + 1 ); + pObj2 = Vec_PtrEntry( vSuper, RightBound ); + if ( Aig_Regular(pObj1) == p->pConst1 || Aig_Regular(pObj2) == p->pConst1 ) + return; + // find the first node that can be shared + for ( i = RightBound; i >= LeftBound; i-- ) + { + pObj3 = Vec_PtrEntry( vSuper, i ); + if ( Aig_Regular(pObj3) == p->pConst1 ) + { + Vec_PtrWriteEntry( vSuper, i, pObj2 ); + Vec_PtrWriteEntry( vSuper, RightBound, pObj3 ); + return; + } + pGhost = Aig_ObjCreateGhost( p, pObj1, pObj3, fExor? AIG_OBJ_EXOR : AIG_OBJ_AND ); + if ( Aig_TableLookup( p, pGhost ) ) + { + if ( pObj3 == pObj2 ) + return; + Vec_PtrWriteEntry( vSuper, i, pObj2 ); + Vec_PtrWriteEntry( vSuper, RightBound, pObj3 ); + return; + } + } +/* + // we did not find the node to share, randomize choice + { + int Choice = rand() % (RightBound - LeftBound + 1); + pObj3 = Vec_PtrEntry( vSuper, LeftBound + Choice ); + if ( pObj3 == pObj2 ) + return; + Vec_PtrWriteEntry( vSuper, LeftBound + Choice, pObj2 ); + Vec_PtrWriteEntry( vSuper, RightBound, pObj3 ); + } +*/ +} + +/**Function************************************************************* + + Synopsis [Inserts a new node in the order by levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_BalancePushUniqueOrderByLevel( Vec_Ptr_t * vStore, Aig_Obj_t * pObj ) +{ + Aig_Obj_t * pObj1, * pObj2; + int i; + if ( Vec_PtrPushUnique(vStore, pObj) ) + return; + // find the p of the node + for ( i = vStore->nSize-1; i > 0; i-- ) + { + pObj1 = vStore->pArray[i ]; + pObj2 = vStore->pArray[i-1]; + if ( Aig_ObjLevel(Aig_Regular(pObj1)) <= Aig_ObjLevel(Aig_Regular(pObj2)) ) + break; + vStore->pArray[i ] = pObj2; + vStore->pArray[i-1] = pObj1; + } +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/dar/darCore.c b/abc_with_bb_support/src/aig/dar/darCore.c new file mode 100644 index 000000000..662a912e9 --- /dev/null +++ b/abc_with_bb_support/src/aig/dar/darCore.c @@ -0,0 +1,244 @@ +/**CFile**************************************************************** + + FileName [darCore.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting.] + + Synopsis [Core of the rewriting package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: darCore.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "darInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Returns the structure with default assignment of parameters.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_ManDefaultRwrParams( Dar_RwrPar_t * pPars ) +{ + memset( pPars, 0, sizeof(Dar_RwrPar_t) ); + pPars->nCutsMax = 8; // 8 + pPars->nSubgMax = 5; // 5 is a "magic number" + pPars->fFanout = 1; + pPars->fUpdateLevel = 0; + pPars->fUseZeros = 0; + pPars->fVerbose = 0; + pPars->fVeryVerbose = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dar_ManRewrite( Aig_Man_t * pAig, Dar_RwrPar_t * pPars ) +{ + Dar_Man_t * p; + ProgressBar * pProgress; + Dar_Cut_t * pCut; + Aig_Obj_t * pObj, * pObjNew; + int i, k, nNodesOld, nNodeBefore, nNodeAfter, Required; + int clk = 0, clkStart; + // prepare the library + Dar_LibPrepare( pPars->nSubgMax ); + // create rewriting manager + p = Dar_ManStart( pAig, pPars ); + // remove dangling nodes + Aig_ManCleanup( pAig ); + // if updating levels is requested, start fanout and timing + if ( p->pPars->fFanout ) + Aig_ManFanoutStart( pAig ); + if ( p->pPars->fUpdateLevel ) + Aig_ManStartReverseLevels( pAig, 0 ); + // set elementary cuts for the PIs + Dar_ManCutsStart( p ); + // resynthesize each node once + clkStart = clock(); + p->nNodesInit = Aig_ManNodeNum(pAig); + nNodesOld = Vec_PtrSize( pAig->vObjs ); + + pProgress = Extra_ProgressBarStart( stdout, nNodesOld ); + Aig_ManForEachObj( pAig, pObj, i ) +// pProgress = Extra_ProgressBarStart( stdout, 100 ); +// Aig_ManOrderStart( pAig ); +// Aig_ManForEachNodeInOrder( pAig, pObj ) + { +// Extra_ProgressBarUpdate( pProgress, 100*pAig->nAndPrev/pAig->nAndTotal, NULL ); + + Extra_ProgressBarUpdate( pProgress, i, NULL ); + if ( !Aig_ObjIsNode(pObj) ) + continue; + if ( i > nNodesOld ) + break; + + // consider freeing the cuts +// if ( (i & 0xFFF) == 0 && Aig_MmFixedReadMemUsage(p->pMemCuts)/(1<<20) > 100 ) +// Dar_ManCutsStart( p ); + + // compute cuts for the node + p->nNodesTried++; +clk = clock(); + Dar_ObjComputeCuts_rec( p, pObj ); +p->timeCuts += clock() - clk; + + // check if there is a trivial cut + Dar_ObjForEachCut( pObj, pCut, k ) + if ( pCut->nLeaves == 0 || (pCut->nLeaves == 1 && pCut->pLeaves[0] != pObj->Id && Aig_ManObj(p->pAig, pCut->pLeaves[0])) ) + break; + if ( k < (int)pObj->nCuts ) + { + assert( pCut->nLeaves < 2 ); + if ( pCut->nLeaves == 0 ) // replace by constant + { + assert( pCut->uTruth == 0 || pCut->uTruth == 0xFFFF ); + pObjNew = Aig_NotCond( Aig_ManConst1(p->pAig), pCut->uTruth==0 ); + } + else + { + assert( pCut->uTruth == 0xAAAA || pCut->uTruth == 0x5555 ); + pObjNew = Aig_NotCond( Aig_ManObj(p->pAig, pCut->pLeaves[0]), pCut->uTruth==0x5555 ); + } + // remove the old cuts + Dar_ObjSetCuts( pObj, NULL ); + // replace the node + Aig_ObjReplace( pAig, pObj, pObjNew, 1, p->pPars->fUpdateLevel ); + continue; + } + + // evaluate the cuts + p->GainBest = -1; + Required = pAig->vLevelR? Aig_ObjRequiredLevel(pAig, pObj) : AIG_INFINITY; + Dar_ObjForEachCut( pObj, pCut, k ) + Dar_LibEval( p, pObj, pCut, Required ); + // check the best gain + if ( !(p->GainBest > 0 || (p->GainBest == 0 && p->pPars->fUseZeros)) ) + { +// Aig_ObjOrderAdvance( pAig ); + continue; + } + // remove the old cuts + Dar_ObjSetCuts( pObj, NULL ); + // if we end up here, a rewriting step is accepted + nNodeBefore = Aig_ManNodeNum( pAig ); + pObjNew = Dar_LibBuildBest( p ); // pObjNew can be complemented! + pObjNew = Aig_NotCond( pObjNew, Aig_ObjPhaseReal(pObjNew) ^ pObj->fPhase ); + assert( (int)Aig_Regular(pObjNew)->Level <= Required ); + // replace the node + Aig_ObjReplace( pAig, pObj, pObjNew, 1, p->pPars->fUpdateLevel ); + // compare the gains + nNodeAfter = Aig_ManNodeNum( pAig ); + assert( p->GainBest <= nNodeBefore - nNodeAfter ); + // count gains of this class + p->ClassGains[p->ClassBest] += nNodeBefore - nNodeAfter; + } +// Aig_ManOrderStop( pAig ); + +p->timeTotal = clock() - clkStart; +p->timeOther = p->timeTotal - p->timeCuts - p->timeEval; + + Extra_ProgressBarStop( pProgress ); + p->nCutMemUsed = Aig_MmFixedReadMemUsage(p->pMemCuts)/(1<<20); + Dar_ManCutsFree( p ); + // put the nodes into the DFS order and reassign their IDs +// Aig_NtkReassignIds( p ); + // fix the levels +// Aig_ManVerifyLevel( pAig ); + if ( p->pPars->fFanout ) + Aig_ManFanoutStop( pAig ); + if ( p->pPars->fUpdateLevel ) + { +// Aig_ManVerifyReverseLevel( pAig ); + Aig_ManStopReverseLevels( pAig ); + } + // stop the rewriting manager + Dar_ManStop( p ); + Aig_ManCheckPhase( pAig ); + // check + if ( !Aig_ManCheck( pAig ) ) + { + printf( "Aig_ManRewrite: The network check has failed.\n" ); + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_MmFixed_t * Dar_ManComputeCuts( Aig_Man_t * pAig, int nCutsMax ) +{ + Dar_Man_t * p; + Dar_RwrPar_t Pars, * pPars = &Pars; + Aig_Obj_t * pObj; + Aig_MmFixed_t * pMemCuts; + int i, nNodes, clk = 0, clkStart = clock(); + // remove dangling nodes + if ( nNodes = Aig_ManCleanup( pAig ) ) + { +// printf( "Removing %d nodes.\n", nNodes ); + } + // create default parameters + Dar_ManDefaultRwrParams( pPars ); + pPars->nCutsMax = nCutsMax; + // create rewriting manager + p = Dar_ManStart( pAig, pPars ); + // set elementary cuts for the PIs + Dar_ManCutsStart( p ); + // compute cuts for each nodes in the topological order + Aig_ManForEachNode( pAig, pObj, i ) + Dar_ObjComputeCuts( p, pObj ); + // free the cuts + pMemCuts = p->pMemCuts; + p->pMemCuts = NULL; +// Dar_ManCutsFree( p ); + // stop the rewriting manager + Dar_ManStop( p ); + return pMemCuts; +} + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/dar/darCut.c b/abc_with_bb_support/src/aig/dar/darCut.c new file mode 100644 index 000000000..de471c260 --- /dev/null +++ b/abc_with_bb_support/src/aig/dar/darCut.c @@ -0,0 +1,698 @@ +/**CFile**************************************************************** + + FileName [darCut.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting.] + + Synopsis [Computation of 4-input cuts.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: darCut.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "darInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Returns the number of 1s in the machine word.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Dar_WordCountOnes( unsigned uWord ) +{ + uWord = (uWord & 0x55555555) + ((uWord>>1) & 0x55555555); + uWord = (uWord & 0x33333333) + ((uWord>>2) & 0x33333333); + uWord = (uWord & 0x0F0F0F0F) + ((uWord>>4) & 0x0F0F0F0F); + uWord = (uWord & 0x00FF00FF) + ((uWord>>8) & 0x00FF00FF); + return (uWord & 0x0000FFFF) + (uWord>>16); +} + +/**Function************************************************************* + + Synopsis [Compute the cost of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Dar_CutFindValue( Dar_Man_t * p, Dar_Cut_t * pCut ) +{ + Aig_Obj_t * pLeaf; + int i, Value, nOnes; + assert( pCut->fUsed ); + Value = 0; + nOnes = 0; + Dar_CutForEachLeaf( p->pAig, pCut, pLeaf, i ) + { + if ( pLeaf == NULL ) + return 0; + assert( pLeaf != NULL ); + Value += pLeaf->nRefs; + nOnes += (pLeaf->nRefs == 1); + } + if ( pCut->nLeaves < 2 ) + return 1001; +// Value = Value * 100 / pCut->nLeaves; + if ( Value > 1000 ) + Value = 1000; + if ( nOnes > 3 ) + Value = 5 - nOnes; + return Value; +} + +/**Function************************************************************* + + Synopsis [Returns the next free cut to use.] + + Description [Uses the cut with the smallest value.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dar_Cut_t * Dar_CutFindFree( Dar_Man_t * p, Aig_Obj_t * pObj ) +{ + Dar_Cut_t * pCut, * pCutMax; + int i; + pCutMax = NULL; + Dar_ObjForEachCutAll( pObj, pCut, i ) + { + if ( pCut->fUsed == 0 ) + return pCut; + if ( pCut->nLeaves < 3 ) + continue; + if ( pCutMax == NULL || pCutMax->Value > pCut->Value ) + pCutMax = pCut; + } + if ( pCutMax == NULL ) + { + Dar_ObjForEachCutAll( pObj, pCut, i ) + { + if ( pCut->nLeaves < 2 ) + continue; + if ( pCutMax == NULL || pCutMax->Value > pCut->Value ) + pCutMax = pCut; + } + } + if ( pCutMax == NULL ) + { + Dar_ObjForEachCutAll( pObj, pCut, i ) + { + if ( pCutMax == NULL || pCutMax->Value > pCut->Value ) + pCutMax = pCut; + } + } + assert( pCutMax != NULL ); + pCutMax->fUsed = 0; + return pCutMax; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if pDom is contained in pCut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Dar_CutCheckDominance( Dar_Cut_t * pDom, Dar_Cut_t * pCut ) +{ + int i, k; + assert( pDom->fUsed && pCut->fUsed ); + for ( i = 0; i < (int)pDom->nLeaves; i++ ) + { + for ( k = 0; k < (int)pCut->nLeaves; k++ ) + if ( pDom->pLeaves[i] == pCut->pLeaves[k] ) + break; + if ( k == (int)pCut->nLeaves ) // node i in pDom is not contained in pCut + return 0; + } + // every node in pDom is contained in pCut + return 1; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the cut is contained.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Dar_CutFilter( Aig_Obj_t * pObj, Dar_Cut_t * pCut ) +{ + Dar_Cut_t * pTemp; + int i; + assert( pCut->fUsed ); + // go through the cuts of the node + Dar_ObjForEachCut( pObj, pTemp, i ) + { + if ( pTemp == pCut ) + continue; + if ( pTemp->nLeaves > pCut->nLeaves ) + { + // skip the non-contained cuts + if ( (pTemp->uSign & pCut->uSign) != pCut->uSign ) + continue; + // check containment seriously + if ( Dar_CutCheckDominance( pCut, pTemp ) ) + { + // remove contained cut + pTemp->fUsed = 0; + } + } + else + { + // skip the non-contained cuts + if ( (pTemp->uSign & pCut->uSign) != pTemp->uSign ) + continue; + // check containment seriously + if ( Dar_CutCheckDominance( pTemp, pCut ) ) + { + // remove the given cut + pCut->fUsed = 0; + return 1; + } + } + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Merges two cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Dar_CutMergeOrdered( Dar_Cut_t * pC, Dar_Cut_t * pC0, Dar_Cut_t * pC1 ) +{ + int i, k, c; + assert( pC0->nLeaves >= pC1->nLeaves ); + + // the case of the largest cut sizes + if ( pC0->nLeaves == 4 && pC1->nLeaves == 4 ) + { + if ( pC0->uSign != pC1->uSign ) + return 0; + for ( i = 0; i < (int)pC0->nLeaves; i++ ) + if ( pC0->pLeaves[i] != pC1->pLeaves[i] ) + return 0; + for ( i = 0; i < (int)pC0->nLeaves; i++ ) + pC->pLeaves[i] = pC0->pLeaves[i]; + pC->nLeaves = pC0->nLeaves; + return 1; + } + + // the case when one of the cuts is the largest + if ( pC0->nLeaves == 4 ) + { + if ( (pC0->uSign & pC1->uSign) != pC1->uSign ) + return 0; + for ( i = 0; i < (int)pC1->nLeaves; i++ ) + { + for ( k = (int)pC0->nLeaves - 1; k >= 0; k-- ) + if ( pC0->pLeaves[k] == pC1->pLeaves[i] ) + break; + if ( k == -1 ) // did not find + return 0; + } + for ( i = 0; i < (int)pC0->nLeaves; i++ ) + pC->pLeaves[i] = pC0->pLeaves[i]; + pC->nLeaves = pC0->nLeaves; + return 1; + } + + // compare two cuts with different numbers + i = k = 0; + for ( c = 0; c < 4; c++ ) + { + if ( k == (int)pC1->nLeaves ) + { + if ( i == (int)pC0->nLeaves ) + { + pC->nLeaves = c; + return 1; + } + pC->pLeaves[c] = pC0->pLeaves[i++]; + continue; + } + if ( i == (int)pC0->nLeaves ) + { + if ( k == (int)pC1->nLeaves ) + { + pC->nLeaves = c; + return 1; + } + pC->pLeaves[c] = pC1->pLeaves[k++]; + continue; + } + if ( pC0->pLeaves[i] < pC1->pLeaves[k] ) + { + pC->pLeaves[c] = pC0->pLeaves[i++]; + continue; + } + if ( pC0->pLeaves[i] > pC1->pLeaves[k] ) + { + pC->pLeaves[c] = pC1->pLeaves[k++]; + continue; + } + pC->pLeaves[c] = pC0->pLeaves[i++]; + k++; + } + if ( i < (int)pC0->nLeaves || k < (int)pC1->nLeaves ) + return 0; + pC->nLeaves = c; + return 1; +} + +/**Function************************************************************* + + Synopsis [Prepares the object for FPGA mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Dar_CutMerge( Dar_Cut_t * pCut, Dar_Cut_t * pCut0, Dar_Cut_t * pCut1 ) +{ + assert( !pCut->fUsed ); + // merge the nodes + if ( pCut0->nLeaves <= pCut1->nLeaves ) + { + if ( !Dar_CutMergeOrdered( pCut, pCut1, pCut0 ) ) + return 0; + } + else + { + if ( !Dar_CutMergeOrdered( pCut, pCut0, pCut1 ) ) + return 0; + } + pCut->uSign = pCut0->uSign | pCut1->uSign; + pCut->fUsed = 1; + return 1; +} + + +/**Function************************************************************* + + Synopsis [Computes the stretching phase of the cut w.r.t. the merged cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Dar_CutTruthPhase( Dar_Cut_t * pCut, Dar_Cut_t * pCut1 ) +{ + unsigned uPhase = 0; + int i, k; + for ( i = k = 0; i < (int)pCut->nLeaves; i++ ) + { + if ( k == (int)pCut1->nLeaves ) + break; + if ( pCut->pLeaves[i] < pCut1->pLeaves[k] ) + continue; + assert( pCut->pLeaves[i] == pCut1->pLeaves[k] ); + uPhase |= (1 << i); + k++; + } + return uPhase; +} + +/**Function************************************************************* + + Synopsis [Swaps two advancent variables of the truth table.] + + Description [Swaps variable iVar and iVar+1.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Dar_CutTruthSwapAdjacentVars( unsigned uTruth, int iVar ) +{ + assert( iVar >= 0 && iVar <= 2 ); + if ( iVar == 0 ) + return (uTruth & 0x99999999) | ((uTruth & 0x22222222) << 1) | ((uTruth & 0x44444444) >> 1); + if ( iVar == 1 ) + return (uTruth & 0xC3C3C3C3) | ((uTruth & 0x0C0C0C0C) << 2) | ((uTruth & 0x30303030) >> 2); + if ( iVar == 2 ) + return (uTruth & 0xF00FF00F) | ((uTruth & 0x00F000F0) << 4) | ((uTruth & 0x0F000F00) >> 4); + assert( 0 ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Expands the truth table according to the phase.] + + Description [The input and output truth tables are in pIn/pOut. The current number + of variables is nVars. The total number of variables in nVarsAll. The last argument + (Phase) contains shows where the variables should go.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Dar_CutTruthStretch( unsigned uTruth, int nVars, unsigned Phase ) +{ + int i, k, Var = nVars - 1; + for ( i = 3; i >= 0; i-- ) + if ( Phase & (1 << i) ) + { + for ( k = Var; k < i; k++ ) + uTruth = Dar_CutTruthSwapAdjacentVars( uTruth, k ); + Var--; + } + assert( Var == -1 ); + return uTruth; +} + +/**Function************************************************************* + + Synopsis [Shrinks the truth table according to the phase.] + + Description [The input and output truth tables are in pIn/pOut. The current number + of variables is nVars. The total number of variables in nVarsAll. The last argument + (Phase) contains shows what variables should remain.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Dar_CutTruthShrink( unsigned uTruth, int nVars, unsigned Phase ) +{ + int i, k, Var = 0; + for ( i = 0; i < 4; i++ ) + if ( Phase & (1 << i) ) + { + for ( k = i-1; k >= Var; k-- ) + uTruth = Dar_CutTruthSwapAdjacentVars( uTruth, k ); + Var++; + } + return uTruth; +} + +/**Function************************************************************* + + Synopsis [Performs truth table computation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Dar_CutTruth( Dar_Cut_t * pCut, Dar_Cut_t * pCut0, Dar_Cut_t * pCut1, int fCompl0, int fCompl1 ) +{ + unsigned uTruth0 = fCompl0 ? ~pCut0->uTruth : pCut0->uTruth; + unsigned uTruth1 = fCompl1 ? ~pCut1->uTruth : pCut1->uTruth; + uTruth0 = Dar_CutTruthStretch( uTruth0, pCut0->nLeaves, Dar_CutTruthPhase(pCut, pCut0) ); + uTruth1 = Dar_CutTruthStretch( uTruth1, pCut1->nLeaves, Dar_CutTruthPhase(pCut, pCut1) ); + return uTruth0 & uTruth1; +} + +/**Function************************************************************* + + Synopsis [Minimize support of the cut.] + + Description [Returns 1 if the node's support has changed] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Dar_CutSuppMinimize( Dar_Cut_t * pCut ) +{ + unsigned uMasks[4][2] = { + { 0x5555, 0xAAAA }, + { 0x3333, 0xCCCC }, + { 0x0F0F, 0xF0F0 }, + { 0x00FF, 0xFF00 } + }; + unsigned uPhase = 0, uTruth = 0xFFFF & pCut->uTruth; + int i, k, nLeaves; + assert( pCut->fUsed ); + // compute the truth support of the cut's function + nLeaves = pCut->nLeaves; + for ( i = 0; i < (int)pCut->nLeaves; i++ ) + if ( (uTruth & uMasks[i][0]) == ((uTruth & uMasks[i][1]) >> (1 << i)) ) + nLeaves--; + else + uPhase |= (1 << i); + if ( nLeaves == (int)pCut->nLeaves ) + return 0; + // shrink the truth table + uTruth = Dar_CutTruthShrink( uTruth, pCut->nLeaves, uPhase ); + pCut->uTruth = 0xFFFF & uTruth; + // update leaves and signature + pCut->uSign = 0; + for ( i = k = 0; i < (int)pCut->nLeaves; i++ ) + { + if ( !(uPhase & (1 << i)) ) + continue; + pCut->pLeaves[k++] = pCut->pLeaves[i]; + pCut->uSign |= Aig_ObjCutSign( pCut->pLeaves[i] ); + } + assert( k == nLeaves ); + pCut->nLeaves = nLeaves; + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_ManCutsFree( Dar_Man_t * p ) +{ + if ( p->pMemCuts == NULL ) + return; + Aig_MmFixedStop( p->pMemCuts, 0 ); + p->pMemCuts = NULL; +// Aig_ManCleanData( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dar_Cut_t * Dar_ObjPrepareCuts( Dar_Man_t * p, Aig_Obj_t * pObj ) +{ + Dar_Cut_t * pCutSet, * pCut; + int i; + assert( Dar_ObjCuts(pObj) == NULL ); + pObj->nCuts = p->pPars->nCutsMax; + // create the cutset of the node + pCutSet = (Dar_Cut_t *)Aig_MmFixedEntryFetch( p->pMemCuts ); + Dar_ObjSetCuts( pObj, pCutSet ); + Dar_ObjForEachCut( pObj, pCut, i ) + pCut->fUsed = 0; + // add unit cut if needed + pCut = pCutSet; + pCut->fUsed = 1; + if ( Aig_ObjIsConst1(pObj) ) + { + pCut->nLeaves = 0; + pCut->uSign = 0; + pCut->uTruth = 0xFFFF; + } + else + { + pCut->nLeaves = 1; + pCut->pLeaves[0] = pObj->Id; + pCut->uSign = Aig_ObjCutSign( pObj->Id ); + pCut->uTruth = 0xAAAA; + } + pCut->Value = Dar_CutFindValue( p, pCut ); + return pCutSet; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_ManCutsStart( Dar_Man_t * p ) +{ + Aig_Obj_t * pObj; + int i; + Aig_ManCleanData( p->pAig ); + Aig_MmFixedRestart( p->pMemCuts ); + Dar_ObjPrepareCuts( p, Aig_ManConst1(p->pAig) ); + Aig_ManForEachPi( p->pAig, pObj, i ) + Dar_ObjPrepareCuts( p, pObj ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dar_Cut_t * Dar_ObjComputeCuts( Dar_Man_t * p, Aig_Obj_t * pObj ) +{ + Aig_Obj_t * pFanin0 = Aig_ObjReal_rec( Aig_ObjChild0(pObj) ); + Aig_Obj_t * pFanin1 = Aig_ObjReal_rec( Aig_ObjChild1(pObj) ); + Aig_Obj_t * pFaninR0 = Aig_Regular(pFanin0); + Aig_Obj_t * pFaninR1 = Aig_Regular(pFanin1); + Dar_Cut_t * pCutSet, * pCut0, * pCut1, * pCut; + int i, k, RetValue; + + assert( !Aig_IsComplement(pObj) ); + assert( Aig_ObjIsNode(pObj) ); + assert( Dar_ObjCuts(pObj) == NULL ); + assert( Dar_ObjCuts(pFaninR0) != NULL ); + assert( Dar_ObjCuts(pFaninR1) != NULL ); + + // set up the first cut + pCutSet = Dar_ObjPrepareCuts( p, pObj ); + // make sure fanins cuts are computed + Dar_ObjForEachCut( pFaninR0, pCut0, i ) + Dar_ObjForEachCut( pFaninR1, pCut1, k ) + { + p->nCutsAll++; + // make sure K-feasible cut exists + if ( Dar_WordCountOnes(pCut0->uSign | pCut1->uSign) > 4 ) + continue; + // get the next cut of this node + pCut = Dar_CutFindFree( p, pObj ); + // create the new cut + if ( !Dar_CutMerge( pCut, pCut0, pCut1 ) ) + { + assert( !pCut->fUsed ); + continue; + } + p->nCutsTried++; + // check dominance + if ( Dar_CutFilter( pObj, pCut ) ) + { + assert( !pCut->fUsed ); + continue; + } + // compute truth table + pCut->uTruth = 0xFFFF & Dar_CutTruth( pCut, pCut0, pCut1, Aig_IsComplement(pFanin0), Aig_IsComplement(pFanin1) ); + + // minimize support of the cut + if ( Dar_CutSuppMinimize( pCut ) ) + { + RetValue = Dar_CutFilter( pObj, pCut ); + assert( !RetValue ); + } + + // assign the value of the cut + pCut->Value = Dar_CutFindValue( p, pCut ); + // if the cut contains removed node, do not use it + if ( pCut->Value == 0 ) + { + p->nCutsSkipped++; + pCut->fUsed = 0; + } + else if ( pCut->nLeaves < 2 ) + return pCutSet; + } + // count the number of nontrivial cuts cuts + Dar_ObjForEachCut( pObj, pCut, i ) + p->nCutsUsed += pCut->fUsed; + // discount trivial cut + p->nCutsUsed--; + return pCutSet; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dar_Cut_t * Dar_ObjComputeCuts_rec( Dar_Man_t * p, Aig_Obj_t * pObj ) +{ + if ( Dar_ObjCuts(pObj) ) + return Dar_ObjCuts(pObj); + if ( Aig_ObjIsBuf(pObj) ) + return Dar_ObjComputeCuts_rec( p, Aig_ObjFanin0(pObj) ); + Dar_ObjComputeCuts_rec( p, Aig_ObjFanin0(pObj) ); + Dar_ObjComputeCuts_rec( p, Aig_ObjFanin1(pObj) ); + return Dar_ObjComputeCuts( p, pObj ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/dar/darData.c b/abc_with_bb_support/src/aig/dar/darData.c new file mode 100644 index 000000000..c95a3f160 --- /dev/null +++ b/abc_with_bb_support/src/aig/dar/darData.c @@ -0,0 +1,11287 @@ +/**CFile**************************************************************** + + FileName [dar_.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting.] + + Synopsis [Storage for AIG subgraph data.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: dar_.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "darInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +const int s_nDataSize1 = 2*43906; +unsigned int s_Data1[2*43906] = { + 4, 6, 5, 7, 9, 11, 5, 6, 4, 7, 15, 17, + 2, 8, 4, 21, 3, 10, 5, 25, 7, 25, 9, 25, + 2, 10, 7, 33, 9, 33, 2, 14, 5, 39, 6, 39, + 17, 39, 3, 16, 7, 47, 33, 47, 2, 16, 4, 53, + 7, 53, 3, 14, 5, 59, 17, 59, 53, 59, 3, 11, + 7, 67, 4, 68, 5, 66, 6, 73, 33, 67, 7, 76, + 5, 77, 9, 77, 9, 66, 33, 85, 3, 9, 4, 88, + 7, 91, 88, 93, 6, 88, 21, 89, 11, 89, 21, 100, + 25, 101, 11, 88, 7, 107, 33, 107, 2, 11, 5, 113, + 7, 112, 30, 113, 25, 113, 5, 120, 7, 121, 9, 120, + 9, 113, 25, 128, 21, 129, 9, 112, 2, 9, 6, 137, + 5, 138, 11, 136, 3, 17, 7, 144, 113, 145, 15, 144, + 113, 151, 3, 15, 6, 155, 4, 156, 17, 154, 113, 161, + 2, 17, 7, 165, 5, 166, 155, 165, 15, 164, 2, 15, + 6, 174, 17, 174, 3, 13, 113, 181, 135, 181, 143, 181, + 2, 12, 9, 189, 181, 189, 9, 192, 2, 13, 85, 197, + 107, 197, 3, 12, 7, 203, 11, 203, 33, 203, 197, 203, + 9, 211, 3, 19, 7, 215, 33, 215, 173, 215, 179, 215, + 2, 18, 215, 225, 2, 19, 151, 229, 161, 229, 3, 18, + 113, 235, 229, 235, 2, 4, 25, 241, 9, 241, 15, 241, + 11, 241, 6, 240, 7, 240, 59, 253, 7, 241, 9, 257, + 6, 241, 253, 261, 3, 5, 251, 265, 6, 266, 241, 269, + 253, 265, 7, 272, 7, 273, 241, 277, 53, 265, 7, 280, + 248, 265, 9, 285, 11, 265, 241, 288, 9, 291, 17, 265, + 9, 265, 257, 265, 25, 299, 251, 298, 7, 264, 3, 305, + 5, 305, 128, 305, 101, 305, 299, 305, 241, 305, 7, 317, + 265, 319, 9, 305, 113, 322, 113, 305, 9, 326, 6, 264, + 241, 331, 17, 331, 53, 331, 253, 331, 7, 265, 59, 341, + 241, 343, 331, 341, 241, 346, 241, 347, 15, 341, 113, 353, + 241, 340, 7, 357, 265, 357, 59, 357, 331, 357, 256, 265, + 7, 367, 59, 367, 331, 367, 6, 265, 5, 375, 242, 375, + 316, 375, 25, 375, 241, 382, 305, 375, 241, 386, 241, 387, + 11, 375, 241, 375, 265, 394, 7, 397, 25, 394, 305, 394, + 251, 395, 257, 375, 241, 374, 260, 265, 241, 265, 251, 413, + 331, 413, 261, 413, 375, 413, 257, 413, 19, 413, 12, 413, + 11, 413, 9, 428, 9, 413, 11, 433, 11, 432, 15, 413, + 11, 412, 9, 441, 7, 413, 241, 445, 265, 445, 375, 445, + 409, 445, 411, 445, 6, 412, 241, 457, 445, 457, 241, 460, + 6, 413, 357, 465, 367, 465, 7, 412, 265, 471, 59, 471, + 331, 471, 465, 471, 241, 479, 2, 6, 353, 483, 322, 483, + 30, 483, 25, 483, 9, 490, 305, 483, 9, 494, 19, 483, + 413, 483, 12, 483, 244, 483, 241, 483, 9, 506, 9, 483, + 305, 510, 25, 510, 11, 510, 241, 510, 257, 510, 247, 483, + 11, 483, 2, 524, 4, 527, 5, 524, 3, 531, 9, 524, + 258, 483, 151, 483, 257, 483, 9, 540, 155, 483, 17, 545, + 235, 483, 181, 483, 161, 483, 4, 482, 2, 555, 5, 482, + 2, 559, 6, 559, 17, 559, 5, 483, 2, 567, 6, 566, + 241, 567, 483, 573, 9, 573, 9, 567, 4, 483, 6, 581, + 3, 582, 3, 7, 289, 587, 510, 587, 11, 591, 11, 590, + 516, 587, 413, 587, 483, 598, 498, 587, 500, 587, 19, 587, + 483, 606, 502, 587, 12, 587, 483, 612, 534, 587, 524, 587, + 9, 618, 288, 587, 305, 623, 25, 623, 11, 587, 265, 629, + 331, 631, 510, 628, 9, 628, 483, 636, 483, 628, 9, 640, + 265, 628, 305, 645, 25, 645, 375, 628, 265, 587, 11, 653, + 33, 655, 11, 652, 25, 659, 305, 659, 295, 587, 241, 587, + 9, 587, 4, 668, 2, 671, 3, 668, 5, 675, 524, 668, + 483, 668, 11, 681, 11, 680, 11, 668, 483, 686, 392, 587, + 165, 587, 375, 587, 17, 695, 11, 695, 11, 694, 113, 587, + 567, 587, 305, 705, 25, 705, 555, 704, 5, 586, 3, 713, + 7, 713, 623, 713, 645, 713, 659, 713, 128, 713, 394, 713, + 101, 713, 299, 713, 705, 713, 510, 713, 483, 713, 9, 736, + 9, 713, 113, 740, 483, 740, 241, 713, 375, 746, 375, 713, + 241, 750, 113, 713, 9, 754, 4, 586, 7, 759, 589, 759, + 33, 759, 5, 587, 7, 767, 2, 768, 47, 767, 483, 773, + 759, 767, 483, 777, 17, 767, 113, 781, 483, 781, 483, 766, + 47, 787, 759, 787, 17, 787, 566, 587, 47, 795, 759, 795, + 17, 795, 4, 587, 3, 803, 6, 805, 7, 802, 494, 803, + 490, 803, 736, 803, 25, 803, 483, 816, 305, 803, 483, 820, + 713, 803, 483, 824, 483, 803, 305, 828, 25, 828, 713, 828, + 555, 829, 483, 802, 580, 587, 483, 587, 413, 842, 19, 842, + 12, 842, 11, 843, 9, 851, 9, 842, 11, 855, 11, 854, + 11, 842, 9, 860, 5, 843, 803, 865, 9, 865, 839, 865, + 841, 865, 4, 842, 483, 875, 865, 875, 483, 878, 4, 843, + 787, 883, 795, 883, 5, 842, 587, 889, 47, 889, 759, 889, + 17, 889, 883, 889, 483, 899, 3, 4, 559, 903, 19, 903, + 12, 903, 842, 903, 587, 903, 483, 912, 165, 912, 9, 903, + 11, 918, 483, 903, 241, 923, 555, 923, 587, 922, 11, 922, + 15, 903, 11, 903, 9, 934, 483, 934, 524, 903, 692, 903, + 767, 903, 375, 945, 165, 903, 7, 949, 587, 948, 6, 902, + 3, 955, 7, 902, 787, 959, 795, 959, 889, 959, 767, 959, + 483, 967, 33, 959, 7, 903, 956, 973, 88, 973, 4, 973, + 3, 973, 955, 980, 9, 980, 3, 972, 4, 972, 955, 973, + 3, 990, 375, 973, 9, 973, 3, 996, 6, 903, 4, 1001, + 3, 1001, 3, 1000, 4, 1000, 2, 5, 955, 1011, 914, 1011, + 918, 1011, 11, 1017, 11, 1016, 912, 1011, 483, 1022, 920, 1011, + 906, 1011, 19, 1011, 903, 1030, 842, 1011, 903, 1034, 908, 1011, + 910, 1011, 12, 1011, 903, 1042, 922, 1011, 587, 1046, 928, 1011, + 936, 1011, 934, 1011, 9, 1054, 15, 1011, 11, 1011, 918, 1060, + 9, 1060, 903, 1064, 903, 1060, 9, 1068, 483, 1011, 912, 1072, + 903, 1072, 587, 1076, 587, 1072, 903, 1080, 155, 1072, 587, 1011, + 922, 1086, 483, 1086, 903, 1090, 903, 1086, 483, 1094, 11, 1087, + 9, 1099, 9, 1086, 11, 1103, 17, 1011, 9, 1011, 934, 1108, + 903, 1108, 11, 1113, 11, 1112, 11, 1108, 903, 1118, 587, 1108, + 11, 1123, 668, 1011, 11, 1127, 544, 1011, 155, 1011, 483, 1132, + 67, 1011, 375, 1137, 7, 1010, 2, 1141, 959, 1141, 85, 1141, + 107, 1141, 203, 1141, 67, 1141, 9, 1153, 215, 1141, 47, 1141, + 759, 1141, 6, 1010, 2, 1163, 5, 1163, 17, 1163, 7, 1011, + 2, 1171, 5, 1171, 2, 1170, 5, 1170, 1163, 1171, 5, 1180, + 2, 1181, 483, 1171, 903, 1170, 375, 1189, 972, 1011, 375, 1193, + 6, 1011, 2, 1197, 5, 1197, 2, 1196, 5, 1196, 1141, 1197, + 2, 1206, 5, 1207, 903, 1196, 1000, 1011, 903, 1011, 19, 1216, + 842, 1216, 12, 1216, 11, 1217, 9, 1225, 587, 1216, 483, 1228, + 11, 1216, 9, 1232, 9, 1216, 11, 1237, 11, 1236, 15, 1216, + 483, 1216, 587, 1244, 7, 1217, 59, 1249, 331, 1249, 1213, 1249, + 1215, 1249, 6, 1216, 1249, 1259, 6, 1217, 1189, 1263, 1193, 1263, + 7, 1216, 375, 1269, 1263, 1269, 3, 6, 413, 1275, 1216, 1275, + 241, 1275, 9, 1281, 11, 1283, 11, 1280, 9, 1287, 17, 1275, + 3, 1290, 5, 1293, 4, 1290, 2, 1297, 903, 1291, 1011, 1275, + 11, 1275, 241, 1304, 9, 1307, 248, 1275, 9, 1311, 973, 1275, + 9, 1315, 341, 1275, 803, 1319, 9, 1319, 5, 1274, 3, 1325, + 17, 1325, 357, 1325, 367, 1325, 471, 1325, 341, 1325, 241, 1337, + 1249, 1325, 253, 1325, 53, 1325, 5, 1275, 6, 1347, 2, 1348, + 918, 1347, 903, 1347, 9, 1354, 9, 1347, 903, 1358, 4, 1275, + 6, 1362, 265, 1363, 2, 7, 393, 1369, 334, 1369, 1325, 1369, + 1363, 1374, 17, 1374, 62, 1369, 331, 1369, 17, 1382, 59, 1369, + 17, 1386, 1328, 1369, 1304, 1369, 9, 1393, 17, 1369, 331, 1396, + 241, 1397, 59, 1396, 1325, 1396, 11, 1369, 1275, 1406, 9, 1409, + 265, 1369, 241, 1412, 11, 1413, 15, 1369, 5, 1418, 3, 1421, + 2, 1418, 4, 1425, 1011, 1419, 107, 1369, 67, 1369, 803, 1433, + 9, 1433, 1359, 1369, 203, 1369, 215, 1369, 85, 1369, 5, 1368, + 2, 1447, 7, 1447, 47, 1447, 85, 1447, 107, 1447, 203, 1447, + 67, 1447, 9, 1461, 215, 1447, 759, 1447, 959, 1447, 4, 1368, + 2, 1471, 7, 1471, 265, 1471, 7, 1476, 331, 1471, 59, 1471, + 1325, 1471, 5, 1369, 7, 1486, 803, 1487, 1275, 1486, 803, 1493, + 9, 1493, 1346, 1369, 803, 1499, 9, 1499, 4, 1369, 7, 1505, + 3, 1506, 1060, 1505, 11, 1505, 1011, 1512, 1011, 1505, 7, 1517, + 11, 1517, 11, 1516, 1347, 1505, 1275, 1504, 1362, 1369, 1275, 1369, + 413, 1531, 19, 1531, 12, 1531, 1216, 1531, 1011, 1531, 903, 1540, + 9, 1531, 11, 1545, 11, 1544, 11, 1531, 9, 1550, 903, 1531, + 1011, 1554, 11, 1530, 9, 1559, 5, 1531, 959, 1563, 47, 1563, + 759, 1563, 17, 1563, 1527, 1563, 1529, 1563, 4, 1530, 1563, 1577, + 4, 1531, 1493, 1581, 1499, 1581, 5, 1530, 803, 1587, 9, 1587, + 1581, 1587, 1, 974, 1, 975, 1, 982, 1, 983, 0, 204, + 0, 195, 0, 881, 0, 463, 0, 212, 0, 900, 0, 480, + 0, 194, 0, 880, 0, 462, 0, 213, 0, 901, 0, 481, + 0, 205, 0, 216, 0, 217, 1, 446, 0, 448, 0, 415, + 0, 421, 1, 92, 0, 92, 1, 94, 1645, 1647, 1, 93, + 88, 1650, 1645, 1653, 0, 93, 1, 95, 1657, 1659, 0, 108, + 0, 109, 1, 360, 0, 368, 0, 358, 0, 414, 1, 447, + 1637, 1675, 0, 449, 1635, 1679, 0, 191, 0, 877, 0, 473, + 1, 473, 0, 459, 1, 459, 0, 891, 0, 207, 0, 458, + 0, 876, 0, 190, 1, 458, 0, 472, 0, 890, 0, 206, + 1, 472, 1, 977, 1, 976, 0, 399, 0, 398, 0, 397, + 7, 1722, 0, 275, 0, 269, 241, 1728, 0, 270, 1, 279, + 0, 321, 0, 274, 0, 271, 1, 278, 0, 320, 1, 277, + 241, 1746, 0, 319, 265, 1750, 1, 276, 0, 318, 1, 926, + 0, 1518, 0, 950, 0, 1519, 0, 951, 1, 998, 1, 999, + 0, 575, 0, 1478, 0, 282, 0, 1479, 0, 283, 0, 369, + 0, 359, 1667, 1785, 0, 420, 1, 422, 1, 927, 1, 1421, + 5, 1794, 1419, 1797, 3, 1794, 1799, 1801, 0, 527, 2, 1804, + 525, 1807, 4, 1804, 1809, 1811, 1, 671, 4, 1814, 669, 1817, + 2, 1814, 1819, 1821, 0, 1293, 3, 1824, 1291, 1827, 5, 1824, + 1829, 1831, 1, 1425, 2, 1834, 1419, 1837, 4, 1834, 1839, 1841, + 0, 531, 5, 1844, 525, 1847, 3, 1844, 1849, 1851, 1, 675, + 3, 1854, 669, 1857, 5, 1854, 1859, 1861, 0, 1297, 4, 1864, + 1291, 1867, 2, 1864, 1869, 1871, 1, 1422, 0, 528, 1, 672, + 0, 1294, 1, 1426, 0, 532, 1, 676, 0, 1298, 1, 123, + 0, 79, 1, 993, 0, 124, 1, 80, 0, 1210, 0, 1184, + 0, 125, 1891, 1905, 1, 81, 1893, 1909, 1, 122, 1897, 1913, + 0, 78, 1899, 1917, 0, 1208, 0, 1182, 1, 992, 1, 361, + 1671, 1927, 0, 574, 1, 984, 1, 985, 1, 114, 9, 1937, + 0, 68, 1, 980, 955, 1942, 9, 1942, 0, 568, 9, 1949, + 1, 924, 1, 925, 0, 306, 375, 1957, 0, 308, 1413, 1961, + 0, 26, 1, 306, 1, 308, 1, 714, 0, 1142, 0, 1448, + 0, 1450, 1, 1450, 1413, 1979, 0, 1474, 0, 54, 0, 56, + 1, 1472, 0, 1326, 0, 60, 1, 60, 0, 1166, 0, 40, + 1, 956, 973, 2001, 973, 2000, 0, 556, 1, 22, 0, 1475, + 1, 1165, 1168, 2012, 1169, 2013, 2015, 2017, 1168, 2013, 1169, 2012, + 2021, 2023, 1, 1167, 2, 2027, 1197, 2029, 1997, 2031, 0, 57, + 1, 957, 0, 1451, 1980, 2039, 1979, 2039, 1413, 2042, 1413, 2039, + 1979, 2046, 1, 1449, 5, 2051, 1451, 2053, 1975, 2055, 1, 1451, + 1977, 2059, 1413, 2061, 0, 307, 1967, 2065, 375, 2067, 0, 309, + 1969, 2071, 1413, 2073, 0, 41, 1, 307, 1958, 2079, 1957, 2079, + 375, 2082, 375, 2079, 1957, 2086, 1, 309, 1962, 2091, 1961, 2091, + 1413, 2094, 1413, 2091, 1961, 2098, 1, 41, 2, 2103, 43, 2105, + 1999, 2107, 1, 1143, 5, 2111, 1171, 2113, 1973, 2115, 1, 561, + 564, 2118, 565, 2119, 2121, 2123, 564, 2119, 565, 2118, 2127, 2129, + 0, 91, 7, 2132, 0, 569, 1, 981, 9, 2139, 955, 2139, + 0, 69, 9, 2145, 1, 115, 0, 432, 0, 500, 0, 1245, + 0, 1237, 0, 501, 0, 433, 1, 1415, 1, 348, 1, 388, + 1, 826, 1, 126, 1, 1376, 1, 380, 1, 814, 1, 118, + 1, 302, 1, 710, 1, 102, 1, 402, 1, 834, 1, 130, + 1, 404, 1, 836, 1, 132, 1, 314, 1, 732, 1, 104, + 0, 1236, 0, 1244, 1, 1414, 0, 202, 1, 888, 1, 202, + 1603, 2215, 0, 106, 1, 106, 1663, 2221, 1, 794, 0, 366, + 0, 84, 1, 786, 1, 84, 1941, 2233, 1, 390, 1, 994, + 1, 450, 1, 105, 1, 733, 1, 405, 1, 315, 1, 837, + 1, 133, 1, 407, 1, 451, 1, 391, 1, 1383, 0, 905, + 1, 905, 0, 1477, 1, 1477, 1775, 2269, 6, 2269, 7, 2269, + 6, 2268, 2275, 2277, 1983, 2269, 7, 2268, 2273, 2283, 0, 281, + 1, 281, 1777, 2289, 6, 2289, 7, 2289, 6, 2288, 2295, 2297, + 1987, 2289, 7, 2288, 2293, 2303, 1, 1387, 1, 406, 2227, 2309, + 1, 995, 0, 461, 1, 461, 1621, 2317, 1699, 2317, 0, 1261, + 1, 1261, 0, 879, 0, 193, 1, 879, 1619, 2331, 1701, 2331, + 1, 193, 1617, 2337, 1703, 2337, 0, 1579, 0, 211, 9, 2344, + 1, 1579, 1, 211, 1623, 2351, 1711, 2351, 0, 479, 241, 2356, + 0, 899, 483, 2360, 0, 227, 1, 479, 1627, 2367, 1707, 2367, + 1, 899, 1625, 2373, 1709, 2373, 1, 227, 0, 1273, 0, 1593, + 0, 239, 1, 1273, 1, 1593, 1, 239, 0, 478, 2367, 2393, + 1, 478, 2359, 2397, 1615, 2397, 2357, 2397, 1687, 2397, 0, 1272, + 2387, 2407, 1, 1272, 2381, 2411, 0, 898, 2373, 2415, 0, 210, + 2351, 2419, 1, 898, 2363, 2423, 1613, 2423, 2361, 2423, 1695, 2423, + 1, 210, 2347, 2433, 1611, 2433, 2345, 2433, 1697, 2433, 0, 1592, + 2389, 2443, 0, 192, 9, 2446, 2337, 2449, 2337, 2447, 1, 1592, + 2383, 2455, 1, 192, 1605, 2459, 2329, 2459, 1683, 2459, 0, 460, + 241, 2466, 2317, 2469, 2317, 2467, 0, 878, 483, 2474, 2331, 2477, + 2331, 2475, 0, 238, 2391, 2483, 1, 460, 1609, 2487, 2315, 2487, + 1691, 2487, 1, 878, 1607, 2495, 2327, 2495, 1685, 2495, 1, 238, + 2385, 2503, 0, 1260, 2325, 2507, 0, 1578, 2349, 2511, 0, 226, + 2379, 2515, 1, 1260, 2323, 2519, 1, 1578, 2343, 2523, 1, 226, + 2365, 2527, 1, 349, 1, 127, 1, 827, 1, 389, 1, 119, + 1, 815, 1, 381, 1, 1377, 1, 131, 1, 835, 1, 303, + 1, 403, 1, 711, 1, 103, 1, 1366, 1, 694, 1, 702, + 9, 2563, 0, 572, 1, 572, 1931, 2569, 0, 1516, 1, 1516, + 1761, 2575, 7, 2575, 6, 2574, 2579, 2581, 6, 2575, 7, 2574, + 2585, 2587, 0, 944, 0, 948, 1, 578, 587, 2595, 1, 948, + 1763, 2599, 7, 2599, 6, 2598, 2603, 2605, 6, 2599, 7, 2598, + 2609, 2611, 1, 1186, 1, 996, 3, 2616, 1, 544, 1, 465, + 471, 2622, 1687, 2625, 1, 197, 203, 2628, 1697, 2631, 1, 883, + 889, 2634, 1695, 2637, 1, 181, 189, 2640, 1683, 2643, 0, 445, + 1693, 2647, 1713, 2647, 265, 2646, 1675, 2653, 2257, 2647, 375, 2647, + 457, 2647, 1, 445, 264, 2662, 241, 2662, 1679, 2667, 375, 2662, + 457, 2662, 1691, 2673, 1, 865, 875, 2676, 1685, 2679, 0, 215, + 7, 2682, 1, 215, 1633, 2687, 1, 1563, 0, 457, 241, 2692, + 2317, 2695, 1, 457, 241, 2698, 445, 2699, 445, 2698, 1691, 2705, + 0, 875, 483, 2708, 2331, 2711, 1, 875, 865, 2714, 1685, 2717, + 0, 189, 9, 2720, 2337, 2723, 1, 189, 181, 2726, 1683, 2729, + 0, 203, 7, 2732, 2215, 2735, 11, 2732, 2351, 2739, 0, 471, + 7, 2742, 1689, 2745, 265, 2742, 2367, 2749, 0, 889, 587, 2752, + 2373, 2755, 1, 203, 1629, 2759, 197, 2758, 1697, 2763, 1, 471, + 265, 2766, 2647, 2769, 465, 2766, 1687, 2773, 1, 889, 883, 2776, + 1695, 2779, 0, 107, 7, 2782, 2221, 2785, 1, 107, 1665, 2789, + 1, 795, 0, 367, 2255, 2795, 375, 2795, 7, 2794, 1, 367, + 265, 2802, 1783, 2805, 0, 85, 7, 2808, 2233, 2811, 0, 357, + 7, 2814, 1927, 2817, 1, 85, 2145, 2821, 1, 357, 265, 2824, + 1785, 2827, 1, 787, 0, 573, 483, 2832, 2569, 2835, 1, 579, + 1, 573, 1773, 2841, 1, 997, 1, 545, 1, 1187, 903, 2849, + 1, 1367, 0, 949, 7, 2854, 2599, 2857, 0, 945, 483, 2861, + 1, 949, 1767, 2865, 0, 1517, 7, 2868, 2575, 2871, 1, 1517, + 1765, 2875, 1, 703, 1, 695, 1, 332, 1, 266, 1731, 2885, + 1733, 2885, 1, 1012, 1, 30, 113, 2892, 0, 316, 265, 2896, + 375, 2897, 0, 272, 7, 2902, 1735, 2903, 1, 736, 803, 2908, + 1, 316, 1737, 2913, 375, 2912, 1, 272, 1727, 2919, 241, 2918, + 0, 1374, 2545, 2925, 1, 1374, 1363, 2928, 1, 1382, 0, 904, + 483, 2934, 2265, 2937, 1, 904, 0, 1476, 7, 2942, 2269, 2945, + 1, 1476, 1779, 2949, 2011, 2949, 0, 280, 7, 2954, 2289, 2957, + 1, 280, 1781, 2961, 2035, 2961, 1, 1386, 1, 99, 1, 991, + 0, 1181, 2, 2972, 0, 347, 2165, 2977, 1, 347, 1, 263, + 273, 2983, 272, 2982, 2985, 2987, 272, 2983, 273, 2982, 2991, 2993, + 0, 77, 0, 1207, 5, 2998, 1, 77, 5, 3002, 1917, 3005, + 9, 3003, 0, 121, 7, 3010, 1913, 3013, 9, 3011, 0, 387, + 2259, 3019, 1, 121, 9, 3023, 1, 825, 483, 3027, 1, 387, + 241, 3030, 241, 3031, 1, 98, 0, 1206, 2, 3038, 0, 386, + 2237, 3043, 3033, 3043, 1, 386, 241, 3048, 1, 824, 483, 3052, + 1, 990, 3, 3056, 0, 120, 3023, 3061, 9, 3063, 0, 1180, + 5, 3066, 1, 120, 3016, 3071, 5, 3070, 1897, 3075, 3013, 3075, + 3011, 3071, 9, 3080, 9, 3070, 9, 3071, 3011, 3086, 0, 76, + 3008, 3091, 7, 3090, 1899, 3095, 3005, 3095, 3003, 3091, 9, 3100, + 9, 3091, 3003, 3104, 0, 346, 2531, 3109, 1, 76, 2997, 3113, + 9, 3115, 1, 776, 1, 346, 241, 3120, 2977, 3123, 1, 21, + 2, 3127, 1275, 3129, 9, 3131, 4, 3126, 3133, 3135, 2, 3126, + 668, 3139, 3135, 3141, 587, 3139, 9, 3144, 3135, 3147, 9, 3139, + 587, 3150, 3135, 3153, 100, 3126, 129, 3126, 129, 3127, 100, 3127, + 89, 3127, 11, 3164, 11, 3127, 89, 3169, 89, 3168, 89, 3126, + 0, 25, 2243, 3177, 2547, 3177, 2539, 3177, 2533, 3177, 3024, 3177, + 3, 3177, 1369, 3189, 11, 3191, 5, 3176, 3193, 3195, 3, 3176, + 524, 3199, 3195, 3201, 483, 3199, 11, 3204, 3195, 3207, 11, 3199, + 483, 3210, 3195, 3213, 3023, 3177, 9, 3216, 101, 3177, 128, 3177, + 113, 3177, 9, 3224, 9, 3177, 3023, 3228, 113, 3228, 1, 33, + 5, 3234, 6, 3237, 2997, 3235, 9, 3241, 3177, 3235, 67, 3235, + 9, 3247, 67, 3234, 3239, 3251, 1, 25, 128, 3254, 101, 3254, + 113, 3255, 9, 3254, 113, 3262, 113, 3254, 9, 3266, 9, 3267, + 0, 33, 2759, 3273, 2789, 3273, 2821, 3273, 9, 3273, 67, 3272, + 9, 3283, 1, 251, 298, 3286, 395, 3286, 395, 3287, 298, 3287, + 257, 3287, 265, 3297, 265, 3296, 265, 3287, 257, 3302, 265, 3286, + 0, 305, 2249, 3309, 2553, 3309, 2543, 3309, 2537, 3309, 3034, 3309, + 3031, 3309, 241, 3320, 3235, 3309, 299, 3309, 394, 3309, 375, 3309, + 241, 3330, 241, 3309, 3031, 3334, 375, 3334, 241, 3308, 375, 3341, + 1, 331, 2647, 3345, 413, 3345, 241, 3344, 1369, 3344, 341, 3344, + 0, 253, 7, 3356, 265, 3356, 3345, 3357, 1, 555, 923, 3364, + 923, 3365, 704, 3364, 829, 3364, 829, 3365, 704, 3365, 0, 713, + 2245, 3379, 2549, 3379, 2541, 3379, 2535, 3379, 3028, 3379, 3027, 3379, + 483, 3390, 3235, 3379, 705, 3379, 828, 3379, 803, 3379, 483, 3400, + 483, 3379, 3027, 3404, 803, 3404, 1, 759, 3, 3410, 6, 3413, + 3273, 3411, 767, 3410, 3415, 3419, 0, 559, 0, 251, 413, 3424, + 413, 3425, 257, 3425, 413, 3431, 412, 3430, 3433, 3435, 412, 3431, + 413, 3430, 3439, 3441, 1, 305, 3, 3445, 375, 3447, 3, 3444, + 394, 3444, 299, 3444, 241, 3444, 375, 3456, 375, 3444, 241, 3460, + 1, 253, 265, 3464, 0, 555, 4, 3469, 15, 3471, 483, 3473, + 2, 3468, 3475, 3477, 4, 3468, 524, 3481, 3477, 3483, 11, 3481, + 483, 3486, 3477, 3489, 483, 3481, 11, 3492, 3477, 3495, 1, 713, + 5, 3499, 17, 3501, 587, 3503, 3, 3498, 3505, 3507, 5, 3498, + 668, 3511, 3507, 3513, 9, 3511, 587, 3516, 3507, 3519, 587, 3511, + 9, 3522, 3507, 3525, 828, 3498, 705, 3498, 483, 3498, 803, 3532, + 803, 3498, 483, 3536, 1, 559, 903, 3540, 1, 955, 980, 3544, + 3, 3544, 973, 3549, 973, 3548, 973, 3545, 2139, 3555, 1011, 3544, + 973, 3544, 3, 3560, 0, 1141, 2, 3564, 9, 3565, 3411, 3565, + 0, 1447, 9, 3573, 3411, 3573, 1, 1471, 4, 3579, 11, 3581, + 1369, 3583, 2, 3578, 3585, 3587, 4, 3578, 1418, 3591, 3587, 3593, + 15, 3591, 1369, 3596, 3587, 3599, 1369, 3591, 15, 3602, 3587, 3605, + 265, 3578, 2011, 3609, 0, 1325, 5, 3613, 9, 3615, 1275, 3617, + 3, 3612, 3619, 3621, 5, 3612, 1290, 3625, 3621, 3627, 17, 3625, + 1275, 3630, 3621, 3633, 1275, 3625, 17, 3636, 3621, 3639, 1, 53, + 265, 3642, 2035, 3645, 0, 59, 5, 3648, 1, 1141, 3177, 3653, + 3379, 3653, 3309, 3653, 0, 1163, 5, 3660, 1, 959, 3565, 3665, + 3273, 3665, 3573, 3665, 1, 1447, 3177, 3673, 3309, 3673, 3379, 3673, + 0, 1471, 7, 3680, 2269, 3683, 3345, 3681, 1, 1325, 1369, 3688, + 3357, 3689, 3681, 3689, 0, 39, 5, 3696, 1, 47, 3565, 3701, + 3573, 3701, 3273, 3701, 0, 53, 2, 3709, 587, 3711, 17, 3713, + 7, 3708, 2289, 3717, 4, 3708, 3715, 3721, 2, 3708, 1290, 3725, + 3721, 3727, 1275, 3725, 17, 3730, 3721, 3733, 17, 3725, 1275, 3736, + 3721, 3739, 3345, 3709, 3689, 3709, 1, 59, 3, 3747, 483, 3749, + 15, 3751, 5, 3746, 3753, 3755, 3, 3746, 1418, 3759, 3755, 3761, + 1369, 3759, 15, 3764, 3755, 3767, 15, 3759, 1369, 3770, 3755, 3773, + 1369, 3746, 3681, 3747, 3357, 3747, 3709, 3747, 1, 330, 1369, 3785, + 471, 3785, 367, 3785, 357, 3785, 341, 3785, 241, 3795, 0, 1162, + 17, 3799, 0, 758, 767, 3803, 0, 32, 2215, 3807, 2221, 3807, + 2233, 3807, 3249, 3807, 3246, 3807, 9, 3817, 3235, 3807, 67, 3820, + 9, 3823, 67, 3807, 3235, 3826, 9, 3829, 1, 758, 3807, 3833, + 1, 32, 3104, 3837, 3284, 3837, 3091, 3837, 9, 3842, 3283, 3837, + 9, 3846, 3273, 3837, 67, 3851, 9, 3853, 9, 3837, 3283, 3856, + 3091, 3856, 0, 1470, 3785, 3863, 0, 24, 2203, 3867, 3259, 3867, + 2191, 3867, 3257, 3867, 2179, 3867, 2895, 3867, 2171, 3867, 3265, 3867, + 3269, 3867, 3085, 3867, 3071, 3867, 9, 3889, 3267, 3867, 9, 3893, + 2893, 3867, 113, 3897, 3263, 3867, 113, 3901, 3255, 3867, 101, 3905, + 128, 3905, 113, 3905, 9, 3910, 9, 3905, 113, 3914, 3837, 3867, + 128, 3867, 101, 3867, 9, 3867, 113, 3924, 113, 3867, 9, 3928, + 1, 24, 3220, 3933, 3222, 3933, 3232, 3933, 3226, 3933, 3224, 3933, + 9, 3942, 3228, 3933, 113, 3946, 3177, 3933, 101, 3950, 128, 3950, + 113, 3950, 9, 3956, 9, 3950, 113, 3960, 128, 3933, 3177, 3964, + 101, 3933, 3177, 3968, 9, 3933, 3224, 3972, 3177, 3972, 113, 3976, + 113, 3972, 3177, 3980, 113, 3933, 3228, 3984, 3177, 3984, 9, 3988, + 9, 3984, 3177, 3992, 0, 712, 2201, 3997, 3531, 3997, 2189, 3997, + 3529, 3997, 2177, 3997, 2911, 3997, 2169, 3997, 3535, 3997, 3539, 3997, + 3055, 3997, 3053, 3997, 483, 4019, 3537, 3997, 483, 4023, 2909, 3997, + 803, 4027, 3533, 3997, 803, 4031, 3499, 3997, 705, 4035, 828, 4035, + 803, 4035, 483, 4040, 483, 4035, 803, 4044, 3837, 3997, 828, 3997, + 705, 3997, 483, 3997, 803, 4054, 803, 3997, 483, 4058, 0, 52, + 3785, 4063, 0, 304, 2199, 4067, 3455, 4067, 2187, 4067, 3453, 4067, + 2175, 4067, 2917, 4067, 2167, 4067, 3459, 4067, 3463, 4067, 3051, 4067, + 3049, 4067, 241, 4089, 3461, 4067, 241, 4093, 2913, 4067, 375, 4097, + 3457, 4067, 375, 4101, 3445, 4067, 299, 4105, 394, 4105, 375, 4105, + 241, 4110, 241, 4105, 375, 4114, 3837, 4067, 394, 4067, 299, 4067, + 241, 4067, 375, 4124, 375, 4067, 241, 4128, 0, 38, 17, 4133, + 0, 252, 265, 4137, 3785, 4137, 0, 558, 17, 4143, 1, 712, + 3396, 4147, 3398, 4147, 3408, 4147, 3402, 4147, 3400, 4147, 483, 4156, + 3404, 4147, 803, 4160, 3379, 4147, 705, 4164, 828, 4164, 803, 4164, + 483, 4170, 483, 4164, 803, 4174, 9, 4147, 828, 4147, 3379, 4180, + 705, 4147, 3379, 4184, 483, 4147, 3400, 4188, 3379, 4188, 803, 4192, + 803, 4188, 3379, 4196, 803, 4147, 3404, 4200, 3379, 4200, 483, 4204, + 483, 4200, 3379, 4208, 1, 304, 3326, 4213, 3328, 4213, 3338, 4213, + 3332, 4213, 3330, 4213, 241, 4222, 3334, 4213, 375, 4226, 3309, 4213, + 299, 4230, 394, 4230, 375, 4230, 241, 4236, 241, 4230, 375, 4240, + 9, 4213, 394, 4213, 3309, 4246, 299, 4213, 3309, 4250, 241, 4213, + 3330, 4254, 3309, 4254, 375, 4258, 375, 4254, 3309, 4262, 375, 4213, + 3334, 4266, 3309, 4266, 241, 4270, 241, 4266, 3309, 4274, 0, 1446, + 3833, 4279, 0, 1140, 3833, 4283, 1, 1446, 3867, 4287, 3997, 4287, + 4067, 4287, 1, 46, 4279, 4295, 3807, 4295, 4283, 4295, 1, 1140, + 3867, 4303, 4067, 4303, 3997, 4303, 1, 58, 1369, 4311, 4063, 4311, + 3863, 4311, 4137, 4311, 1, 958, 3807, 4321, 4279, 4321, 4283, 4321, + 1, 1324, 1369, 4329, 3863, 4329, 4063, 4329, 4137, 4329, 1, 214, + 1631, 4339, 2685, 4339, 0, 214, 1, 1562, 1, 444, 449, 4349, + 7, 4349, 265, 4353, 2647, 4355, 2658, 4349, 251, 4349, 2647, 4349, + 375, 4362, 375, 4349, 2647, 4366, 0, 444, 2701, 4371, 1689, 4371, + 1705, 4371, 447, 4371, 2665, 4371, 2241, 4371, 3785, 4371, 2671, 4371, + 265, 4371, 331, 4371, 2663, 4371, 375, 4393, 375, 4371, 1, 510, + 257, 4399, 1, 1058, 0, 1396, 0, 1406, 0, 628, 375, 4409, + 1, 652, 1, 288, 1, 267, 1741, 4417, 1, 1013, 1, 1375, + 1, 333, 2903, 4425, 3361, 4425, 0, 1375, 2931, 4431, 2173, 4431, + 1, 273, 2905, 4437, 1739, 4437, 6, 4436, 7, 4437, 4443, 4445, + 6, 4437, 3359, 4437, 7, 4436, 4449, 4453, 0, 273, 2883, 4457, + 2913, 4457, 3351, 4457, 3457, 4457, 2923, 4457, 1749, 4457, 1743, 4457, + 1, 31, 3224, 4473, 3177, 4473, 113, 4476, 113, 4473, 3177, 4480, + 1, 737, 3400, 4485, 3379, 4485, 803, 4488, 803, 4485, 3379, 4492, + 1, 317, 2903, 4497, 3361, 4497, 2900, 4497, 7, 4497, 265, 4505, + 2897, 4507, 1753, 4497, 1745, 4497, 3330, 4497, 2897, 4497, 375, 4516, + 3309, 4497, 375, 4520, 375, 4497, 2897, 4524, 3309, 4524, 0, 317, + 7, 4530, 2913, 4531, 375, 4535, 375, 4531, 1, 507, 1, 245, + 0, 1397, 1, 913, 0, 1407, 1, 653, 11, 4551, 1, 289, + 1, 629, 713, 4557, 25, 4557, 305, 4557, 0, 629, 375, 4565, + 0, 340, 3785, 4569, 4311, 4569, 4329, 4569, 4555, 4569, 0, 766, + 1413, 4579, 922, 4579, 903, 4579, 483, 4584, 483, 4579, 903, 4588, + 0, 66, 75, 4593, 7, 4593, 3113, 4593, 9, 4599, 3251, 4593, + 9, 4603, 375, 4593, 483, 4593, 9, 4593, 1, 340, 331, 4613, + 1, 766, 483, 4616, 2591, 4617, 483, 4621, 1, 66, 5, 4624, + 6, 4627, 3248, 4625, 1941, 4625, 9, 4633, 36, 4625, 3247, 4625, + 9, 4638, 9, 4624, 1941, 4643, 3807, 4643, 33, 4625, 9, 4648, + 9, 4625, 3247, 4652, 33, 4652, 3807, 4625, 9, 4659, 0, 1170, + 2615, 4663, 903, 4665, 0, 112, 3972, 4669, 3270, 4669, 3086, 4669, + 1938, 4669, 1937, 4669, 9, 4678, 2564, 4669, 3071, 4669, 9, 4684, + 3267, 4669, 9, 4688, 2563, 4669, 9, 4692, 9, 4669, 3267, 4696, + 3933, 4696, 1937, 4696, 3071, 4696, 2563, 4696, 3933, 4669, 9, 4708, + 1, 1170, 1413, 4713, 922, 4713, 483, 4713, 903, 4718, 903, 4713, + 483, 4722, 1, 112, 2, 4727, 7, 4729, 3261, 4727, 9, 4733, + 25, 4727, 903, 4727, 587, 4727, 9, 4727, 0, 972, 3059, 4745, + 1925, 4745, 3563, 4745, 3553, 4745, 3549, 4745, 3547, 4745, 2005, 4745, + 2001, 4745, 1595, 4745, 1945, 4745, 1599, 4745, 2239, 4745, 1717, 4745, + 2619, 4745, 1769, 4745, 1947, 4745, 1933, 4745, 1943, 4745, 955, 4781, + 9, 4781, 0, 802, 807, 4787, 375, 4787, 7, 4787, 805, 4793, + 265, 4793, 483, 4787, 0, 144, 483, 4801, 1, 972, 3551, 4805, + 2003, 4805, 375, 4805, 955, 4805, 9, 4805, 1011, 4805, 1, 802, + 713, 4819, 483, 4819, 1, 144, 0, 256, 2309, 4827, 4437, 4827, + 264, 4826, 299, 4833, 264, 4827, 367, 4837, 265, 4826, 2309, 4841, + 4805, 4827, 4613, 4827, 375, 4827, 510, 4827, 483, 4827, 9, 4852, + 9, 4827, 483, 4856, 0, 1504, 1, 256, 2798, 4863, 7, 4863, + 265, 4867, 264, 4862, 264, 4863, 2795, 4863, 375, 4874, 265, 4862, + 4873, 4879, 305, 4863, 713, 4863, 25, 4863, 375, 4863, 2795, 4888, + 265, 4863, 4871, 4893, 1, 164, 0, 374, 241, 4899, 0, 1346, + 9, 4903, 0, 154, 0, 1412, 2853, 4909, 2163, 4909, 0, 1418, + 1, 1346, 1, 154, 483, 4919, 1, 1412, 241, 4922, 1, 1290, + 4915, 4927, 1, 1280, 1, 246, 0, 566, 2596, 4935, 586, 4934, + 705, 4939, 586, 4935, 795, 4943, 2595, 4935, 587, 4946, 587, 4935, + 2595, 4950, 0, 1086, 375, 4955, 0, 922, 1759, 4959, 2265, 4959, + 3367, 4959, 4421, 4959, 1953, 4959, 1, 566, 5, 4971, 6, 4973, + 1950, 4971, 1949, 4971, 9, 4978, 586, 4970, 586, 4971, 587, 4970, + 4985, 4987, 4827, 4971, 9, 4971, 1949, 4992, 4745, 4971, 4569, 4971, + 587, 4971, 4983, 5001, 1, 1086, 9, 5005, 1, 922, 3369, 5009, + 555, 5009, 257, 5009, 1, 932, 0, 88, 3171, 5019, 10, 5018, + 101, 5023, 101, 5019, 10, 5019, 107, 5029, 0, 934, 375, 5033, + 0, 1108, 587, 5037, 1, 1362, 265, 5041, 1, 88, 93, 5045, + 1643, 5047, 93, 5044, 1645, 5051, 1645, 5045, 2135, 5045, 973, 5044, + 4745, 5059, 3163, 5045, 3173, 5045, 3167, 5045, 7, 5044, 10, 5044, + 10, 5045, 11, 5044, 3807, 5075, 5073, 5075, 33, 5045, 21, 5045, + 973, 5045, 4805, 5085, 3807, 5045, 4279, 5045, 4283, 5045, 4745, 5045, + 11, 5045, 5071, 5097, 1, 1304, 1, 1108, 973, 5103, 972, 5102, + 5105, 5107, 972, 5103, 973, 5102, 5111, 5113, 257, 5103, 0, 260, + 0, 580, 0, 136, 0, 668, 1, 260, 4139, 5127, 4138, 5127, + 264, 5126, 5131, 5133, 264, 5127, 4137, 5137, 265, 5126, 4137, 5141, + 5137, 5142, 5129, 5141, 5138, 5141, 5137, 5141, 4137, 5150, 265, 5127, + 4137, 5154, 5133, 5157, 5133, 5155, 4137, 5161, 4137, 5127, 265, 5165, + 5137, 5167, 265, 5164, 5133, 5171, 1, 580, 1, 136, 5019, 5177, + 1, 524, 5125, 5181, 1, 296, 5019, 5185, 0, 1413, 2559, 5189, + 2209, 5189, 4925, 5189, 0, 1419, 5181, 5197, 1, 295, 973, 5201, + 972, 5201, 49, 5201, 48, 5201, 972, 5200, 5203, 5211, 973, 5200, + 5205, 5215, 48, 5200, 5207, 5219, 49, 5200, 5209, 5223, 1, 1413, + 1417, 5227, 1, 1291, 5125, 5231, 5197, 5231, 0, 1087, 375, 5237, + 0, 923, 1793, 5241, 2941, 5241, 5012, 5241, 3543, 5241, 3559, 5241, + 2891, 5241, 5017, 5241, 1955, 5241, 555, 5241, 5009, 5258, 5009, 5241, + 555, 5262, 1, 1087, 5037, 5267, 1, 923, 555, 5270, 4959, 5273, + 241, 5270, 4959, 5277, 4959, 5271, 555, 5281, 1, 933, 4959, 5285, + 0, 935, 375, 5289, 0, 1109, 5005, 5293, 1, 935, 0, 669, + 4927, 5299, 1, 525, 4915, 5303, 5299, 5303, 1, 669, 1, 297, + 0, 341, 483, 5313, 3747, 5313, 3689, 5313, 3345, 5313, 4415, 5313, + 0, 257, 2255, 5325, 4888, 5325, 4881, 5325, 3467, 5325, 2919, 5325, + 4871, 5325, 4893, 5336, 4894, 5325, 7, 5324, 4863, 5343, 4872, 5325, + 4879, 5347, 4869, 5325, 4893, 5325, 4871, 5352, 5343, 5353, 3, 5325, + 5, 5358, 4862, 5360, 4863, 5361, 5363, 5365, 5343, 5364, 4862, 5361, + 4863, 5360, 5371, 5373, 5344, 5361, 5343, 5361, 4863, 5378, 5, 5325, + 3, 5382, 4862, 5384, 4863, 5385, 5387, 5389, 5343, 5388, 4862, 5385, + 4863, 5384, 5395, 5397, 5344, 5385, 5343, 5385, 4863, 5402, 264, 5325, + 4863, 5406, 4879, 5409, 5344, 5407, 5343, 5407, 4863, 5414, 4863, 5407, + 4871, 5419, 5343, 5418, 7, 5325, 265, 5425, 4863, 5427, 266, 5325, + 375, 5325, 4863, 5432, 510, 5325, 483, 5325, 9, 5438, 9, 5325, + 483, 5442, 251, 5325, 265, 5446, 265, 5447, 4892, 5325, 265, 5325, + 257, 5454, 1, 5457, 7, 5455, 5459, 5461, 0, 5461, 5457, 5465, + 251, 5454, 4863, 5454, 4863, 5325, 3, 5472, 5, 5474, 5, 5472, + 3, 5478, 375, 5472, 7, 5473, 5361, 5485, 5385, 5485, 5477, 5485, + 5481, 5485, 5407, 5485, 264, 5472, 4879, 5497, 5485, 5497, 264, 5473, + 4893, 5503, 265, 5472, 265, 5324, 4417, 5509, 251, 5509, 1, 341, + 5325, 5515, 331, 5514, 1, 257, 445, 5521, 265, 5522, 449, 5521, + 448, 5521, 414, 5521, 415, 5521, 421, 5521, 420, 5521, 253, 5521, + 265, 5538, 340, 5521, 341, 5521, 272, 5520, 241, 5546, 273, 5521, + 5549, 5551, 272, 5521, 273, 5520, 412, 5521, 251, 5559, 241, 5520, + 265, 5562, 5551, 5565, 272, 5563, 5557, 5569, 500, 5521, 432, 5521, + 501, 5521, 413, 5520, 5525, 5579, 5529, 5579, 5533, 5579, 5535, 5579, + 5555, 5579, 5541, 5579, 251, 5579, 5559, 5592, 5560, 5579, 5559, 5579, + 251, 5598, 5577, 5579, 433, 5521, 5579, 5605, 264, 5520, 5543, 5609, + 367, 5609, 264, 5521, 299, 5615, 412, 5520, 5527, 5619, 5531, 5619, + 5537, 5619, 5551, 5619, 5573, 5619, 5575, 5619, 413, 5521, 375, 5632, + 5619, 5635, 251, 5632, 5619, 5639, 5619, 5633, 251, 5643, 483, 5632, + 5619, 5647, 9, 5632, 5619, 5651, 7, 5521, 265, 5654, 5609, 5657, + 265, 5655, 5615, 5661, 2227, 5521, 375, 5665, 4841, 5521, 375, 5669, + 375, 5520, 2227, 5673, 4841, 5673, 4827, 5673, 375, 5521, 413, 5680, + 5619, 5683, 413, 5681, 5559, 5687, 483, 5521, 413, 5690, 5619, 5693, + 413, 5691, 5559, 5697, 9, 5521, 413, 5700, 5619, 5703, 413, 5701, + 5559, 5707, 251, 5521, 413, 5710, 5619, 5713, 413, 5711, 5559, 5717, + 265, 5521, 445, 5720, 5579, 5723, 253, 5720, 5579, 5727, 7, 5720, + 5609, 5731, 4827, 5521, 3, 5735, 5, 5736, 5, 5735, 3, 5740, + 375, 5735, 7, 5734, 5739, 5747, 5743, 5747, 264, 5735, 5747, 5753, + 265, 5735, 265, 5520, 2647, 5759, 5545, 5759, 1783, 5759, 241, 5758, + 5551, 5767, 0, 1171, 4714, 5771, 4716, 5771, 4720, 5771, 4724, 5771, + 2850, 5771, 1413, 5771, 4713, 5782, 922, 5771, 4713, 5786, 4718, 5771, + 903, 5790, 2849, 5771, 903, 5794, 483, 5771, 4722, 5798, 903, 5798, + 4713, 5802, 4713, 5798, 903, 5806, 4722, 5771, 483, 5810, 903, 5771, + 4718, 5814, 2849, 5814, 483, 5814, 4713, 5820, 4713, 5814, 483, 5824, + 4713, 5771, 1413, 5828, 922, 5828, 483, 5828, 903, 5834, 903, 5828, + 483, 5838, 0, 973, 1895, 5843, 2037, 5843, 1597, 5843, 1601, 5843, + 2142, 5843, 2313, 5843, 4810, 5843, 1715, 5843, 1771, 5843, 1935, 5843, + 2140, 5843, 2139, 5843, 955, 5866, 9, 5866, 375, 5843, 4805, 5872, + 483, 5843, 9, 5843, 2139, 5878, 955, 5843, 2139, 5882, 1011, 5843, + 4805, 5843, 375, 5888, 1, 1171, 3, 5892, 4663, 5895, 903, 5897, + 483, 5892, 4663, 5901, 903, 5903, 4663, 5893, 1413, 5907, 922, 5907, + 483, 5907, 903, 5912, 903, 5907, 483, 5916, 1, 973, 956, 5920, + 4745, 5923, 88, 5920, 4745, 5927, 5, 5920, 4745, 5931, 1011, 5933, + 3, 5920, 955, 5936, 4745, 5939, 9, 5936, 4745, 5943, 4745, 5937, + 955, 5947, 9, 5947, 375, 5920, 4745, 5953, 955, 5920, 3, 5956, + 4745, 5959, 5325, 5921, 9, 5920, 3, 5964, 4745, 5967, 4745, 5921, + 375, 5971, 0, 767, 903, 5974, 4617, 5977, 483, 5979, 4617, 5975, + 1413, 5983, 922, 5983, 903, 5983, 483, 5988, 483, 5983, 903, 5992, + 0, 567, 4989, 5997, 4983, 5997, 5001, 6000, 5002, 5997, 4984, 5997, + 4987, 6007, 5001, 5997, 4983, 6010, 3, 5997, 7, 6014, 4970, 6016, + 4971, 6017, 6019, 6021, 4970, 6017, 4971, 6016, 6025, 6027, 7, 5997, + 3, 6030, 4970, 6032, 4971, 6033, 6035, 6037, 4970, 6033, 4971, 6032, + 6041, 6043, 3, 5996, 7, 6047, 2, 5996, 4992, 6051, 6015, 6051, + 7, 6055, 4971, 6057, 9, 6051, 4971, 6060, 4971, 6051, 9, 6064, + 586, 5997, 4971, 6068, 4987, 6071, 4971, 6069, 4983, 6075, 2, 5997, + 6048, 6079, 4971, 6081, 7, 6079, 6047, 6084, 4971, 6087, 4971, 6085, + 6047, 6091, 6047, 6079, 7, 6094, 4971, 6097, 4992, 5997, 587, 6101, + 2839, 5997, 587, 6105, 9, 5997, 4971, 6108, 587, 6111, 4971, 5997, + 2, 6115, 7, 6117, 4975, 6117, 586, 6114, 4987, 6123, 5, 6114, + 6, 6127, 6117, 6129, 6119, 6127, 586, 6115, 5001, 6135, 9, 6114, + 587, 6139, 587, 6115, 9, 6143, 0, 67, 4636, 6147, 37, 6147, + 3008, 6147, 4656, 6147, 4650, 6147, 7, 6146, 4643, 6159, 2233, 6159, + 4625, 6159, 9, 6165, 5, 6147, 6, 6169, 36, 6147, 4625, 6172, + 3807, 6173, 4648, 6147, 9, 6178, 3003, 6147, 9, 6182, 33, 6146, + 6151, 6187, 3856, 6187, 3837, 6187, 9, 6192, 9, 6187, 3837, 6196, + 33, 6147, 4652, 6200, 3807, 6201, 9, 6205, 9, 6200, 4625, 6208, + 3807, 6209, 4625, 6200, 9, 6214, 4652, 6147, 33, 6218, 9, 6147, + 4648, 6222, 3003, 6222, 33, 6222, 4625, 6228, 3807, 6229, 33, 6223, + 3837, 6235, 4625, 6222, 33, 6238, 4625, 6147, 75, 6243, 4629, 6243, + 7, 6243, 4627, 6249, 6171, 6243, 375, 6243, 36, 6242, 33, 6242, + 9, 6258, 9, 6242, 33, 6262, 0, 89, 5079, 6267, 5071, 6267, + 5097, 6270, 5098, 6267, 5072, 6267, 5075, 6277, 5097, 6267, 5071, 6280, + 107, 6267, 5, 6267, 7, 6286, 5044, 6288, 5045, 6289, 6291, 6293, + 5044, 6289, 5045, 6288, 6297, 6299, 7, 6267, 5, 6302, 5044, 6304, + 5045, 6305, 6307, 6309, 5044, 6305, 5045, 6304, 6313, 6315, 5, 6266, + 7, 6318, 5045, 6321, 7, 6319, 7, 6266, 5, 6326, 5045, 6329, + 5075, 6327, 2221, 6327, 5045, 6327, 4, 6266, 6287, 6339, 7, 6341, + 5045, 6343, 10, 6266, 5075, 6347, 2221, 6347, 5045, 6347, 10, 6267, + 5097, 6355, 5045, 6354, 5075, 6359, 5045, 6355, 5071, 6363, 1274, 6267, + 4, 6267, 6324, 6369, 5045, 6371, 7, 6369, 6319, 6374, 5045, 6377, + 5044, 6374, 5045, 6375, 6319, 6383, 6381, 6383, 5044, 6375, 5045, 6374, + 6389, 6391, 6319, 6369, 7, 6394, 5045, 6397, 6, 6267, 3, 6400, + 3, 6267, 6, 6404, 21, 6267, 11, 6409, 5045, 6411, 5045, 6267, + 10, 6414, 5075, 6417, 10, 6415, 5097, 6421, 5311, 6267, 11, 6266, + 2969, 6427, 5082, 6427, 5045, 6427, 21, 6432, 21, 6427, 5045, 6436, + 1, 767, 4580, 6441, 4582, 6441, 4586, 6441, 4590, 6441, 2862, 6441, + 1413, 6441, 4579, 6452, 922, 6441, 4579, 6456, 4584, 6441, 483, 6460, + 2861, 6441, 483, 6464, 759, 6440, 903, 6441, 4588, 6470, 483, 6470, + 4579, 6474, 4579, 6470, 483, 6478, 4588, 6441, 903, 6482, 483, 6441, + 4584, 6486, 2861, 6486, 903, 6486, 4579, 6492, 4579, 6486, 903, 6496, + 4579, 6441, 1413, 6500, 922, 6500, 903, 6500, 483, 6506, 483, 6500, + 903, 6510, 1, 567, 5, 6514, 6, 6517, 4935, 6517, 2137, 6515, + 9, 6523, 14, 6515, 2, 6515, 563, 6529, 4935, 6531, 6519, 6529, + 4935, 6535, 7, 6529, 6520, 6539, 4935, 6539, 6517, 6542, 6517, 6539, + 4935, 6546, 1197, 6529, 4935, 6551, 6, 6515, 5, 6554, 5, 6515, + 6, 6558, 5325, 6515, 903, 6515, 9, 6514, 4950, 6567, 587, 6567, + 4935, 6570, 4935, 6567, 587, 6574, 241, 6515, 4950, 6515, 9, 6581, + 587, 6515, 9, 6585, 4935, 6587, 4935, 6584, 9, 6591, 4935, 6515, + 2, 6594, 7, 6597, 5, 6595, 6539, 6601, 6599, 6601, 6, 6601, + 6597, 6607, 9, 6595, 587, 6611, 587, 6594, 9, 6615, 5843, 6515, + 5313, 6515, 587, 6514, 1, 67, 4594, 6625, 4606, 6625, 3280, 6625, + 75, 6625, 4593, 6632, 2146, 6625, 2145, 6625, 9, 6638, 7, 6625, + 73, 6643, 4593, 6645, 375, 6625, 4593, 6648, 33, 6624, 4593, 6653, + 9, 6655, 9, 6625, 3273, 6658, 2145, 6658, 4593, 6625, 75, 6664, + 375, 6664, 36, 6665, 33, 6665, 9, 6672, 9, 6665, 33, 6676, + 3273, 6625, 9, 6680, 1, 89, 1657, 6685, 25, 6685, 3565, 6685, + 3573, 6685, 3273, 6685, 21, 6684, 483, 6685, 5843, 6685, 11, 6684, + 0, 1487, 10, 6705, 7, 6705, 5, 6708, 5, 6705, 7, 6712, + 0, 1347, 0, 113, 2149, 6719, 9, 6721, 4736, 6719, 9, 6725, + 3023, 6719, 9, 6729, 4740, 6719, 9, 6733, 2879, 6719, 9, 6737, + 587, 6719, 4727, 6740, 9, 6743, 25, 6719, 4727, 6746, 9, 6749, + 4727, 6719, 5, 6753, 6, 6755, 4731, 6755, 2, 6752, 7, 6761, + 6755, 6763, 6757, 6761, 25, 6752, 9, 6769, 587, 6752, 9, 6773, + 9, 6753, 25, 6777, 587, 6777, 3255, 6719, 9, 6783, 0, 137, + 1, 113, 31, 6788, 2, 6788, 7, 6793, 4669, 6793, 4697, 6789, + 14, 6788, 6, 6789, 6, 6788, 5, 6804, 11, 6788, 5, 6808, + 5, 6788, 6, 6813, 6, 6812, 4696, 6813, 9, 6813, 4669, 6820, + 4669, 6813, 9, 6824, 7, 6788, 6815, 6829, 15, 6788, 6803, 6833, + 8, 6788, 8, 6789, 4669, 6839, 1368, 6789, 5, 6789, 35, 6845, + 4669, 6847, 6795, 6845, 4669, 6851, 6, 6845, 6796, 6855, 4669, 6855, + 6793, 6858, 6793, 6855, 4669, 6862, 1171, 6845, 4669, 6867, 17, 6845, + 4669, 6871, 7, 6789, 6811, 6875, 6807, 6875, 6817, 6875, 6801, 6875, + 2, 6874, 2, 6789, 7, 6886, 30, 6788, 3867, 6891, 25, 6788, + 4696, 6895, 3867, 6895, 9, 6899, 9, 6894, 3867, 6903, 4669, 6895, + 9, 6906, 9, 6895, 4669, 6910, 587, 6788, 4696, 6915, 9, 6915, + 4669, 6918, 4669, 6915, 9, 6922, 25, 6789, 4473, 6927, 6791, 6927, + 3972, 6927, 3933, 6927, 9, 6934, 9, 6927, 3933, 6938, 4696, 6789, + 6837, 6943, 25, 6943, 587, 6943, 9, 6789, 6837, 6951, 4669, 6953, + 587, 6951, 4669, 6957, 25, 6951, 4669, 6961, 4669, 6950, 25, 6965, + 6837, 6965, 587, 6965, 4669, 6789, 5, 6972, 6, 6975, 2, 6973, + 6855, 6979, 6977, 6979, 7, 6979, 6975, 6985, 25, 6973, 9, 6989, + 587, 6973, 9, 6993, 9, 6972, 6837, 6997, 25, 6997, 587, 6997, + 9, 6973, 6839, 7005, 9, 6788, 6799, 7009, 6840, 7009, 6839, 7009, + 4669, 7014, 4669, 7009, 6839, 7018, 25, 7008, 3867, 7023, 25, 7009, + 3933, 7027, 1, 137, 6267, 7031, 0, 375, 413, 7034, 413, 7035, + 241, 7034, 3307, 7041, 2885, 7041, 0, 261, 0, 803, 16, 7049, + 7, 7049, 4, 7052, 4, 7049, 7, 7056, 0, 581, 0, 145, + 1, 375, 317, 7064, 265, 7065, 256, 7068, 256, 7069, 4901, 7065, + 4, 7065, 2, 7077, 265, 7077, 2, 7076, 4899, 7083, 2, 7065, + 265, 7087, 4, 7086, 4899, 7091, 2, 7064, 265, 7064, 3, 7064, + 7084, 7099, 7092, 7099, 7083, 7099, 4899, 7104, 7091, 7099, 4899, 7108, + 7075, 7099, 4899, 7099, 7083, 7114, 7091, 7114, 240, 7064, 240, 7065, + 265, 7123, 7, 7125, 7114, 7123, 7099, 7123, 4899, 7130, 4899, 7123, + 7099, 7134, 413, 7065, 7, 7138, 3, 7065, 7079, 7143, 4899, 7145, + 7081, 7143, 4899, 7149, 5, 7065, 7089, 7153, 4899, 7155, 445, 7065, + 4349, 7159, 7121, 7159, 367, 7065, 4863, 7165, 973, 7065, 4805, 7169, + 316, 7064, 4067, 7173, 973, 7064, 4745, 7177, 445, 7064, 4371, 7181, + 257, 7064, 4841, 7185, 2227, 7185, 4827, 7185, 7071, 7185, 305, 7064, + 4067, 7195, 241, 7197, 241, 7194, 4067, 7201, 587, 7064, 257, 7065, + 4863, 7207, 7073, 7207, 628, 7065, 587, 7065, 11, 7214, 11, 7065, + 587, 7218, 241, 7219, 305, 7065, 4497, 7225, 7067, 7225, 4254, 7225, + 4213, 7225, 241, 7232, 241, 7225, 4213, 7236, 4900, 7065, 7121, 7241, + 7095, 7241, 7097, 7241, 241, 7065, 7095, 7249, 4899, 7251, 7097, 7249, + 4899, 7255, 6, 7249, 7121, 7249, 4899, 7261, 4899, 7248, 7121, 7265, + 7095, 7265, 7097, 7265, 4899, 7065, 241, 7272, 7121, 7275, 7095, 7275, + 7097, 7275, 241, 7273, 7123, 7283, 241, 7064, 4371, 7287, 7141, 7287, + 7127, 7287, 7075, 7287, 7134, 7287, 7123, 7287, 4899, 7298, 4899, 7287, + 7123, 7302, 305, 7286, 4067, 7307, 305, 7287, 4213, 7311, 1, 261, + 413, 7315, 3357, 7315, 264, 7319, 265, 7318, 7321, 7323, 264, 7318, + 265, 7319, 7327, 7329, 1, 803, 737, 7332, 4799, 7333, 1274, 7332, + 6, 7333, 2, 7341, 2, 7340, 4787, 7345, 2, 7333, 6, 7348, + 4787, 7351, 2, 7332, 6, 7332, 3, 7356, 587, 7332, 3, 7360, + 3, 7332, 7346, 7365, 7352, 7365, 7345, 7365, 4787, 7370, 7351, 7365, + 4787, 7374, 6, 7365, 6, 7364, 7337, 7365, 4787, 7365, 7345, 7384, + 7351, 7384, 7, 7332, 7379, 7391, 1275, 7332, 7341, 7395, 482, 7332, + 482, 7333, 7384, 7401, 7365, 7401, 4787, 7404, 4787, 7401, 7365, 7408, + 3, 7333, 7343, 7413, 4787, 7415, 7, 7333, 7363, 7419, 7359, 7419, + 7381, 7419, 7339, 7419, 736, 7332, 3997, 7429, 713, 7332, 3997, 7433, + 483, 7435, 483, 7432, 3997, 7439, 11, 7333, 483, 7443, 713, 7333, + 4485, 7447, 7335, 7447, 4188, 7447, 4147, 7447, 483, 7454, 483, 7447, + 4147, 7458, 4798, 7333, 7399, 7463, 7355, 7463, 483, 7333, 7355, 7469, + 4787, 7471, 7399, 7469, 4787, 7475, 4787, 7468, 7399, 7479, 7355, 7479, + 4787, 7333, 483, 7484, 7399, 7487, 7355, 7487, 483, 7485, 7401, 7493, + 483, 7332, 7337, 7497, 7408, 7497, 7401, 7497, 4787, 7502, 4787, 7497, + 7401, 7506, 713, 7496, 3997, 7511, 713, 7497, 4147, 7515, 1, 145, + 586, 7519, 7, 7519, 3, 7522, 3, 7519, 7, 7526, 1, 155, + 483, 7530, 0, 175, 482, 7535, 6, 7535, 2, 7538, 2, 7535, + 6, 7542, 1, 1505, 1011, 7546, 6, 7549, 7, 7549, 6, 7548, + 7553, 7555, 7, 7548, 7551, 7559, 1, 1363, 8, 7563, 6, 7563, + 4, 7566, 4, 7563, 6, 7570, 1374, 7563, 4423, 7575, 1374, 7562, + 4431, 7579, 265, 7562, 5189, 7583, 1, 165, 903, 7586, 6, 7589, + 7, 7589, 6, 7588, 7593, 7595, 7, 7588, 7591, 7599, 1, 175, + 44, 7602, 45, 7603, 7605, 7607, 44, 7603, 45, 7602, 7611, 7613, + 0, 299, 3289, 7617, 2181, 7617, 3307, 7617, 2885, 7617, 1, 299, + 4371, 7627, 2801, 7627, 1669, 7627, 5461, 7627, 3295, 7627, 305, 7626, + 4067, 7639, 251, 7627, 305, 7627, 4213, 7645, 0, 705, 3371, 7649, + 2183, 7649, 0, 101, 3157, 7655, 2185, 7655, 3175, 7655, 6697, 7655, + 3037, 7655, 6685, 7655, 21, 7667, 1, 705, 3377, 7671, 713, 7670, + 3997, 7675, 555, 7671, 713, 7671, 4147, 7681, 1, 101, 3163, 7685, + 25, 7684, 3867, 7689, 21, 7685, 25, 7685, 3933, 7695, 0, 129, + 2253, 7699, 21, 7699, 1, 129, 21, 7704, 0, 395, 2247, 7709, + 4417, 7709, 265, 7709, 7, 7715, 251, 7709, 0, 829, 2251, 7721, + 555, 7721, 1, 395, 2647, 7727, 1719, 7727, 251, 7726, 1, 829, + 555, 7734, 0, 298, 265, 7739, 7, 7741, 7627, 7743, 2551, 7739, + 7642, 7739, 4417, 7739, 7627, 7739, 251, 7752, 251, 7739, 7627, 7756, + 1, 298, 2647, 7761, 1783, 7761, 251, 7760, 7617, 7767, 7617, 7761, + 251, 7771, 0, 704, 2555, 7775, 7678, 7775, 7671, 7775, 555, 7780, + 555, 7775, 7671, 7784, 0, 100, 2557, 7789, 7692, 7789, 2969, 7789, + 5082, 7789, 5045, 7789, 21, 7798, 7685, 7789, 21, 7802, 21, 7789, + 7685, 7806, 5045, 7806, 1, 704, 555, 7812, 7649, 7815, 7649, 7813, + 555, 7819, 1, 100, 21, 7822, 7655, 7825, 7655, 7823, 21, 7829, + 0, 128, 3159, 7833, 2197, 7833, 7707, 7833, 7705, 7833, 21, 7841, + 1, 128, 3161, 7845, 7702, 7845, 25, 7844, 3867, 7851, 21, 7845, + 7699, 7854, 7699, 7845, 21, 7858, 25, 7845, 3933, 7863, 0, 394, + 3291, 7867, 2193, 7867, 7733, 7867, 2885, 7867, 3307, 7867, 7727, 7867, + 251, 7879, 0, 828, 3373, 7883, 2195, 7883, 7737, 7883, 7735, 7883, + 555, 7891, 1, 394, 4371, 7895, 3293, 7895, 7718, 7895, 1721, 7895, + 1725, 7895, 7717, 7895, 305, 7894, 4067, 7909, 251, 7895, 7709, 7912, + 7709, 7895, 251, 7916, 305, 7895, 4213, 7921, 1, 828, 3375, 7925, + 7724, 7925, 713, 7924, 3997, 7931, 555, 7925, 7721, 7934, 7721, 7925, + 555, 7938, 713, 7925, 4147, 7943, 0, 8, 6, 7947, 3, 7948, + 1274, 7947, 113, 7947, 3, 7947, 0, 7957, 6, 7956, 0, 7956, + 9, 7963, 11, 7957, 587, 7947, 1, 10, 5081, 7971, 5026, 7971, + 7, 7971, 2, 7976, 1368, 7971, 5097, 7971, 931, 7971, 1417, 7971, + 939, 7971, 941, 7971, 272, 7971, 1476, 7971, 280, 7971, 949, 7971, + 1517, 7971, 4696, 7971, 149, 7971, 631, 7971, 970, 7971, 50, 7971, + 764, 7971, 357, 7971, 367, 7971, 471, 7971, 1249, 7971, 5019, 7971, + 101, 8022, 128, 7971, 101, 7971, 5019, 8028, 4669, 7971, 9, 8032, + 759, 7971, 33, 8036, 1471, 7971, 265, 8040, 959, 7971, 33, 8044, + 253, 7971, 265, 8048, 113, 7971, 9, 8052, 375, 7971, 88, 7971, + 89, 7971, 2, 7971, 1, 8063, 7, 8062, 1, 8062, 11, 8069, + 9, 8063, 3, 7971, 9, 8074, 33, 7971, 959, 8078, 47, 8078, + 759, 8078, 25, 7971, 305, 7971, 713, 7971, 2, 7970, 8059, 8093, + 8077, 8093, 8075, 8093, 9, 8099, 3867, 8093, 3997, 8093, 4067, 8093, + 3, 7970, 4696, 8109, 8061, 8109, 8072, 8109, 8063, 8109, 9, 8116, + 4669, 8109, 9, 8120, 9, 8109, 4669, 8124, 8063, 8124, 47, 7971, + 33, 8130, 53, 7971, 265, 8134, 265, 7971, 253, 8138, 1471, 8138, + 53, 8138, 587, 7971, 483, 7971, 9, 7971, 4669, 8150, 113, 8150, + 3, 8150, 8093, 8157, 3, 8151, 8063, 8161, 0, 10, 203, 8165, + 107, 8165, 5019, 8169, 85, 8165, 2215, 8165, 2221, 8165, 2233, 8165, + 5075, 8165, 4643, 8165, 5045, 8165, 25, 8185, 4551, 8165, 922, 8165, + 1413, 8165, 273, 8165, 281, 8165, 1477, 8165, 1516, 8165, 948, 8165, + 4625, 8165, 9, 8205, 759, 8165, 959, 8165, 67, 8165, 9, 8213, + 1505, 8165, 1011, 8216, 88, 8165, 33, 8221, 89, 8165, 5019, 8225, + 3, 8165, 33, 8229, 9, 8231, 8063, 8229, 9, 8228, 33, 8237, + 2, 8165, 9, 8241, 33, 8165, 1141, 8165, 1447, 8165, 2, 8164, + 2215, 8251, 2221, 8251, 5075, 8251, 2233, 8251, 4643, 8251, 8229, 8251, + 4625, 8251, 9, 8265, 4295, 8251, 3833, 8251, 4321, 8251, 5045, 8251, + 3, 8164, 8241, 8277, 8093, 8277, 3837, 8277, 4287, 8277, 4303, 8277, + 128, 8277, 101, 8277, 113, 8277, 9, 8292, 9, 8277, 113, 8296, + 47, 8165, 165, 8165, 903, 8302, 265, 8165, 587, 8165, 1011, 8165, + 1505, 8310, 1369, 8165, 483, 8165, 903, 8316, 903, 8165, 483, 8320, + 165, 8320, 9, 8165, 3, 8326, 33, 8329, 3, 8327, 113, 8333, + 0, 240, 4266, 8337, 4888, 8337, 4892, 8337, 4213, 8337, 375, 8344, + 4863, 8337, 375, 8348, 265, 8348, 261, 8337, 3335, 8337, 375, 8357, + 347, 8337, 386, 8337, 331, 8337, 305, 8337, 375, 8366, 340, 8337, + 331, 8371, 375, 8337, 4213, 8374, 4863, 8374, 305, 8374, 1325, 8337, + 59, 8337, 7, 8337, 265, 8387, 4213, 8389, 4863, 8389, 305, 8389, + 265, 8386, 331, 8397, 7, 8336, 5141, 8401, 5137, 8402, 5137, 8401, + 5141, 8406, 5150, 8401, 5154, 8401, 5133, 8413, 5161, 8401, 5127, 8401, + 265, 8419, 5137, 8421, 265, 8418, 5133, 8425, 265, 8401, 5127, 8429, + 5141, 8431, 5127, 8428, 5133, 8435, 3785, 8401, 4329, 8401, 4311, 8401, + 483, 8337, 9, 8337, 1275, 8337, 15, 8337, 265, 8337, 4863, 8452, + 257, 8452, 1, 8457, 6, 8452, 1, 8461, 241, 8462, 241, 8461, + 1, 8466, 7, 8452, 331, 8471, 7, 8453, 7895, 8475, 7287, 8475, + 8465, 8475, 8469, 8475, 7627, 8475, 8459, 8475, 0, 8475, 8457, 8489, + 375, 8475, 1, 264, 3293, 8495, 7718, 8495, 3295, 8495, 3305, 8495, + 3301, 8495, 5512, 8495, 5451, 8495, 7756, 8495, 7091, 8495, 4899, 8512, + 7083, 8495, 4899, 8516, 7092, 8495, 7084, 8495, 4696, 8495, 7075, 8495, + 7123, 8495, 4899, 8528, 7134, 8495, 445, 8495, 4349, 8535, 8386, 8495, + 331, 8539, 471, 8495, 367, 8495, 357, 8495, 631, 8495, 1249, 8495, + 628, 8495, 8387, 8495, 8401, 8495, 4137, 8495, 3863, 8495, 4063, 8495, + 8475, 8495, 4371, 8495, 4549, 8495, 4545, 8495, 407, 8495, 1417, 8495, + 665, 8495, 338, 8495, 1480, 8495, 336, 8495, 4745, 8495, 4669, 8495, + 9, 8586, 4899, 8495, 7091, 8590, 7083, 8590, 7123, 8590, 4569, 8495, + 7709, 8495, 251, 8600, 341, 8495, 4863, 8605, 241, 8605, 253, 8495, + 331, 8610, 251, 8495, 7709, 8614, 5509, 8614, 7739, 8614, 256, 8495, + 375, 8623, 7739, 8495, 251, 8626, 5509, 8495, 251, 8630, 257, 8495, + 305, 8635, 53, 8495, 331, 8638, 1471, 8495, 331, 8642, 6, 8495, + 4349, 8647, 4497, 8647, 413, 8647, 4213, 8647, 8109, 8647, 4147, 8647, + 3933, 8647, 445, 8647, 316, 8647, 4713, 8647, 4863, 8647, 8337, 8647, + 305, 8647, 241, 8672, 241, 8647, 305, 8676, 587, 8647, 8387, 8647, + 7, 8495, 3785, 8685, 4329, 8685, 4311, 8685, 8337, 8684, 331, 8693, + 331, 8685, 8337, 8697, 15, 8685, 241, 8684, 375, 8703, 331, 8495, + 253, 8706, 1471, 8706, 53, 8706, 305, 8495, 6, 8494, 8539, 8717, + 8693, 8717, 4371, 8717, 8623, 8717, 8703, 8717, 8685, 8717, 8337, 8729, + 241, 8729, 471, 8717, 367, 8717, 357, 8717, 1369, 8717, 341, 8717, + 241, 8743, 4569, 8717, 4137, 8717, 4063, 8717, 3863, 8717, 8401, 8717, + 7, 8494, 8389, 8757, 8555, 8757, 8374, 8757, 8670, 8757, 8635, 8757, + 8676, 8757, 8647, 8757, 8337, 8770, 241, 8770, 8337, 8757, 8647, 8776, + 375, 8776, 9, 8757, 375, 8757, 8337, 8784, 241, 8757, 1, 8789, + 8647, 8791, 8647, 8788, 587, 8495, 11, 8796, 11, 8495, 587, 8800, + 9, 8495, 4669, 8804, 1369, 8495, 17, 8495, 241, 8495, 1, 8813, + 7, 8815, 8717, 8817, 7, 8814, 8647, 8821, 7, 8812, 8717, 8825, + 375, 8825, 7, 8813, 1, 8830, 8647, 8833, 8647, 8831, 331, 8831, + 6, 8813, 8337, 8495, 7, 8842, 8717, 8845, 331, 8845, 483, 8843, + 9, 8843, 7, 8843, 8647, 8855, 375, 8855, 0, 264, 3299, 8861, + 5431, 8861, 5469, 8861, 5449, 8861, 4888, 8861, 4863, 8861, 375, 8872, + 629, 8861, 394, 8861, 299, 8861, 659, 8861, 623, 8861, 645, 8861, + 101, 8861, 705, 8861, 128, 8861, 828, 8861, 3345, 8861, 3445, 8861, + 375, 8899, 2981, 8861, 3049, 8861, 7195, 8861, 3461, 8861, 4613, 8861, + 331, 8910, 4614, 8861, 375, 8861, 4863, 8916, 241, 8916, 803, 8861, + 483, 8922, 113, 8861, 9, 8926, 256, 8861, 331, 8931, 5455, 8861, + 251, 8935, 7, 8861, 331, 8939, 241, 8941, 241, 8938, 331, 8945, + 331, 8861, 4613, 8948, 7, 8860, 3837, 8953, 8093, 8953, 394, 8953, + 299, 8953, 375, 8953, 241, 8962, 4303, 8953, 4287, 8953, 241, 8953, + 375, 8970, 483, 8861, 803, 8974, 9, 8861, 113, 8978, 17, 8861, + 241, 8861, 375, 8984, 7, 8984, 331, 8989, 7, 8985, 375, 8993, + 1, 240, 3342, 8997, 2900, 8997, 8672, 8997, 8743, 8997, 3795, 8997, + 8770, 8997, 8729, 8997, 2658, 8997, 7159, 8997, 2703, 8997, 2660, 8997, + 413, 8997, 8757, 8997, 8647, 9022, 8684, 8997, 8717, 9027, 8605, 8997, + 457, 8997, 2647, 9032, 8647, 8997, 305, 9036, 8757, 9036, 8916, 8997, + 257, 8997, 2647, 8997, 375, 9046, 457, 9046, 3341, 8997, 375, 9052, + 2897, 8997, 375, 9056, 305, 8997, 8647, 9060, 340, 8997, 8717, 9065, + 3785, 9065, 8495, 9065, 375, 8997, 3341, 9072, 2897, 9072, 2647, 9072, + 8861, 9072, 7, 8997, 413, 9082, 0, 9085, 1, 9083, 264, 9088, + 264, 9089, 8495, 9082, 8717, 9095, 8495, 9083, 8757, 9099, 305, 9099, + 265, 9083, 2897, 9105, 3341, 9105, 2647, 9105, 9087, 9105, 1, 9105, + 9085, 9115, 9093, 9105, 8861, 9105, 265, 9082, 8717, 9123, 3785, 9123, + 9091, 9123, 8495, 9123, 483, 8997, 9, 8997, 265, 8997, 7, 9136, + 8717, 9139, 3785, 9139, 8495, 9139, 7, 9137, 8647, 9147, 8495, 8997, + 7, 9150, 8717, 9153, 7, 9151, 8647, 9157, 8861, 8997, 375, 9160, + 0, 482, 6, 9165, 4, 9167, 803, 9165, 5, 9165, 587, 9173, + 9, 9173, 5, 9164, 17, 9179, 19, 9165, 12, 9165, 9, 9165, + 11, 9186, 11, 9165, 9, 9190, 1, 586, 4795, 9195, 807, 9195, + 4787, 9198, 4788, 9195, 4790, 9195, 8374, 9195, 8389, 9195, 4797, 9195, + 8086, 9195, 8090, 9195, 8251, 9195, 4283, 9195, 4279, 9195, 3807, 9195, + 1491, 9195, 631, 9195, 1160, 9195, 1466, 9195, 764, 9195, 357, 9195, + 367, 9195, 471, 9195, 1249, 9195, 8800, 9195, 8647, 9195, 4787, 9195, + 807, 9246, 375, 9246, 33, 9195, 759, 9252, 1141, 9195, 759, 9256, + 803, 9195, 375, 9195, 4787, 9262, 8337, 9262, 8165, 9195, 265, 9269, + 566, 9195, 567, 9195, 7971, 9195, 25, 9276, 713, 9276, 8495, 9195, + 11, 9282, 4, 9195, 483, 9287, 5, 9195, 17, 9291, 483, 9290, + 759, 9195, 1141, 9296, 1447, 9296, 33, 9296, 713, 9195, 7971, 9304, + 25, 9195, 7971, 9308, 8337, 9195, 375, 9312, 4, 9194, 9273, 9317, + 9295, 9317, 9291, 9317, 483, 9323, 4279, 9317, 3807, 9317, 4283, 9317, + 8251, 9317, 5, 9194, 9275, 9335, 9288, 9335, 9287, 9335, 483, 9340, + 8647, 9335, 9, 9335, 483, 9335, 9287, 9348, 1447, 9195, 759, 9352, + 11, 9195, 8495, 9357, 8495, 9356, 9, 9195, 1011, 9195, 483, 9195, + 5, 9366, 9317, 9369, 5, 9367, 9287, 9373, 0, 586, 7, 9377, + 4, 9378, 16, 9377, 8800, 9377, 273, 9377, 1477, 9377, 281, 9377, + 948, 9377, 1516, 9377, 750, 9377, 386, 9377, 347, 9377, 510, 9377, + 767, 9377, 375, 9377, 305, 9406, 713, 9406, 165, 9377, 903, 9412, + 8495, 9377, 11, 9416, 566, 9377, 759, 9421, 5, 9377, 759, 9425, + 483, 9427, 483, 9424, 759, 9431, 4, 9377, 0, 9435, 7, 9434, + 0, 9434, 587, 9441, 713, 9435, 713, 9377, 375, 9446, 305, 9377, + 375, 9450, 4, 9376, 767, 9455, 5, 9376, 3837, 9459, 8093, 9459, + 828, 9459, 705, 9459, 803, 9459, 483, 9468, 4287, 9459, 4303, 9459, + 483, 9459, 803, 9476, 1505, 9377, 1011, 9480, 11, 9377, 8495, 9484, + 903, 9377, 165, 9488, 9, 9377, 483, 9492, 1011, 9377, 1505, 9496, + 483, 9377, 9, 9500, 5, 9500, 759, 9505, 5, 9501, 803, 9509, + 1, 482, 6, 9513, 5, 9514, 14, 9513, 5, 9513, 1, 9521, + 6, 9520, 1, 9520, 483, 9527, 11, 9513, 0, 902, 524, 9533, + 1291, 9533, 767, 9533, 7, 9533, 3, 9541, 305, 9533, 375, 9544, + 386, 9533, 347, 9533, 375, 9533, 305, 9552, 483, 9533, 11, 9556, + 11, 9533, 483, 9560, 1, 1010, 5038, 9565, 5037, 9565, 587, 9568, + 668, 9565, 1419, 9565, 8982, 9565, 7, 9564, 3867, 9579, 8277, 9579, + 9459, 9579, 8953, 9579, 4067, 9579, 3997, 9579, 375, 9565, 483, 9565, + 9, 9565, 587, 9596, 587, 9565, 5037, 9600, 9, 9600, 17, 9565, + 8861, 9606, 8861, 9565, 7, 9611, 17, 9610, 0, 1010, 9535, 9617, + 9537, 9617, 9559, 9617, 9563, 9617, 5, 9617, 6, 9627, 7, 9627, + 5006, 9617, 5005, 9617, 9, 9634, 9362, 9617, 1290, 9617, 9533, 9641, + 525, 9617, 9533, 9645, 8804, 9617, 8150, 9617, 922, 9617, 1413, 9617, + 7971, 9617, 9, 9656, 6, 9617, 11, 9661, 9195, 9617, 9, 9664, + 6, 9616, 17, 9669, 7, 9616, 9195, 9673, 9317, 9673, 4321, 9673, + 3833, 9673, 4295, 9673, 5045, 9673, 19, 9617, 12, 9617, 11, 9617, + 9, 9690, 483, 9617, 903, 9694, 1275, 9617, 17, 9698, 9533, 9701, + 9, 9617, 5005, 9704, 9195, 9704, 7971, 9704, 8495, 9704, 11, 9704, + 17, 9617, 1275, 9716, 9533, 9719, 903, 9617, 483, 9722, 8495, 9617, + 9, 9726, 1, 902, 9573, 9731, 9575, 9731, 9599, 9731, 9605, 9731, + 1418, 9731, 9565, 9741, 669, 9731, 9565, 9745, 8386, 9731, 7, 9731, + 8337, 9750, 7, 9730, 9673, 9755, 8251, 9755, 4283, 9755, 3807, 9755, + 4279, 9755, 483, 9731, 15, 9731, 1369, 9768, 9565, 9771, 1369, 9731, + 15, 9774, 9565, 9777, 1011, 9731, 8337, 9731, 7, 9782, 9617, 9731, + 6, 9786, 7, 9786, 6, 9787, 9791, 9793, 7, 9787, 9789, 9797, + 0, 1274, 5, 9801, 1, 1368, 4904, 9805, 4903, 9805, 9, 9808, + 9484, 9805, 1517, 9805, 1476, 9805, 272, 9805, 280, 9805, 949, 9805, + 1197, 9805, 1471, 9805, 265, 9826, 253, 9805, 265, 9830, 53, 9805, + 265, 9834, 5, 9804, 3867, 9839, 8277, 9839, 8953, 9839, 9459, 9839, + 3997, 9839, 4067, 9839, 11, 9805, 9377, 9852, 9, 9805, 4903, 9856, + 265, 9805, 1471, 9860, 253, 9860, 53, 9860, 9377, 9805, 11, 9868, + 5, 9869, 0, 1368, 5075, 9875, 2221, 9875, 4643, 9875, 2233, 9875, + 2215, 9875, 4339, 9875, 7, 9875, 5, 9888, 4, 9889, 10, 9875, + 8714, 9875, 8800, 9875, 4625, 9875, 9, 9901, 47, 9875, 305, 9875, + 8495, 9906, 959, 9875, 5045, 9875, 4551, 9875, 1325, 9875, 59, 9875, + 331, 9875, 5, 9875, 0, 9923, 7, 9922, 0, 9922, 1369, 9929, + 9287, 9923, 4, 9875, 1011, 9935, 8495, 9875, 10, 9938, 10, 9939, + 305, 9938, 11, 9938, 9943, 9947, 11, 9939, 9941, 9951, 4, 9874, + 8495, 9955, 8717, 9955, 4329, 9955, 4311, 9955, 3785, 9955, 5, 9874, + 9195, 9967, 9755, 9967, 9317, 9967, 3833, 9967, 4295, 9967, 4321, 9967, + 5045, 9967, 759, 9875, 11, 9875, 8495, 9984, 17, 9875, 903, 9875, + 265, 9875, 15, 9875, 9195, 9875, 241, 9997, 1, 1274, 6,10001, + 4,10002, 4,10003, 8,10001, 5123,10001, 5,10011, 4,10010, +10013,10015, 4,10011, 5,10010,10019,10021, 341,10001, 4663,10001, + 903,10027, 1471,10001, 253,10001, 53,10001, 4,10001, 1,10037, + 6,10036, 1,10036, 1275,10043, 5,10001, 9935,10047, 8337,10001, + 9,10051, 5,10000, 8685,10055, 1369,10055, 8401,10055, 3863,10055, + 4137,10055, 4063,10055, 4569,10055, 9955,10055, 241,10001, 17,10001, + 1369,10001, 5,10077, 0, 14, 6,10081, 2,10082, 482,10081, + 2,10081, 0,10089, 6,10088, 0,10088, 15,10095, 2,10080, + 17,10099, 1369,10081, 17,10081, 1, 16, 7,10107, 3,10108, + 586,10107, 8306,10107, 1447,10107, 1141,10107, 9875,10107, 241,10121, + 265,10120, 9992,10107, 545,10107, 695,10107, 1382,10107, 1374,10107, + 1386,10107, 59,10107, 1369,10138, 154,10107, 483,10143, 1325,10107, + 1369,10146, 331,10107, 1369,10150, 3,10107, 1,10155, 7,10154, + 1,10154, 17,10161, 8241,10155, 15,10154, 483,10167, 3,10106, + 9673,10171, 8251,10171, 3807,10171, 4279,10171, 4283,10171, 9967,10171, + 33,10107, 1369,10107, 331,10186, 1325,10186, 59,10186, 1011,10107, + 265,10107, 8165,10196, 9875,10196, 1275,10107, 15,10107, 3,10204, + 483,10207, 3,10205, 1369,10211, 8165,10107, 265,10214, 0, 16, + 4,10219, 3,10220, 3,10221, 7,10219, 2,10226,10225,10229, + 2,10227,10223,10233, 1368,10219,10225,10237, 902,10219,10233,10241, + 59,10219, 1325,10219, 331,10219, 3,10219, 4,10250,10233,10253, + 2,10219, 7,10256,10225,10259, 903,10257, 8495,10219, 587,10265, +10001,10219, 903,10269, 2,10218, 8495,10273, 8717,10273,10055,10273, + 4311,10273, 4329,10273, 3785,10273, 265,10219, 1275,10219, 1, 14, + 8982,10291, 8861,10291, 17,10294, 4745,10291, 1011,10299, 53,10291, + 253,10291, 1471,10291, 2,10291, 3,10291, 483,10311,10257,10311, + 59,10291, 17,10316, 62,10291, 1325,10291, 17,10322, 331,10291, + 17,10326, 1328,10291, 334,10291, 393,10291, 1359,10291, 1031,10291, + 499,10291, 8337,10291, 483,10343, 9875,10291, 1011,10347, 3,10290, + 8685,10351, 1369,10351, 8401,10351, 9955,10351, 4063,10351, 4137,10351, + 3863,10351, 4569,10351,10273,10351, 241,10291, 1369,10291, 17,10291, + 8861,10374, 59,10374, 1325,10374, 331,10374, 1, 11, 340,10385, + 341,10385, 264,10385, 375,10391, 1010,10385, 264,10384, 341,10397, +10387,10397, 5,10385, 6,10402, 2,10402, 2,10403, 7,10385, + 265,10410,10397,10413, 265,10411,10391,10417, 5,10384, 29,10421, + 341,10421, 6,10421, 25,10427, 8262,10385, 9,10431, 8279,10385, + 9,10435, 88,10384, 3807,10439, 6347,10439, 6327,10439, 8251,10439, + 9875,10439, 8165,10439, 8229,10385, 8251,10452, 9,10455, 9,10453, + 8251,10459, 8241,10385, 33,10385, 6685,10465, 9,10465, 6267,10385, + 101,10471, 8240,10385, 8296,10475, 8277,10475, 9,10478, 9,10475, + 8277,10482, 8228,10385, 6719,10385, 9,10489, 265,10384,10389,10493, + 5313,10493, 8251,10385, 8229,10498, 9,10501, 483,10384, 5197,10505, + 5125,10505, 1275,10384, 1369,10385, 6685,10513, 9,10513, 1011,10385, + 265,10385, 7,10520,10397,10523, 2,10384,10423,10527,10428,10527, +10427,10527, 25,10532,10425,10527, 340,10527,10397,10539,10399,10527, +10392,10527, 375,10527,10391,10546, 7,10527, 265,10550,10397,10553, + 265,10551,10391,10557,10391,10527, 375,10560,10463,10527, 9,10565, +10487,10527, 9,10569,10519,10527, 6,10573, 7,10572,10575,10577, + 7,10573, 6,10572,10581,10583, 25,10527,10427,10586, 8165,10527, + 903,10527, 265,10527, 7,10594,10397,10597, 9,10527, 3,10384, +10482,10603, 5,10602, 6,10607, 5,10603, 6,10611,10475,10613, +10458,10603,10395,10603, 6,10619, 7,10618,10621,10623, 7,10619, + 6,10618,10627,10629,10407,10603, 6,10633, 7,10632,10635,10637, + 7,10633, 6,10632,10641,10643, 8326,10603,10453,10603, 9,10648, +10475,10603, 9,10652, 9875,10603, 9,10657,10521,10603, 375,10661, + 9,10602, 8251,10665, 3807,10665, 9875,10665, 8165,10665, 8165,10603, + 9,10674, 9,10675, 3807,10603, 9,10681, 9,10603,10475,10684, +10453,10684, 8165,10684, 8251,10603, 9,10693, 2,10385,10613,10697, + 8165,10699,10646,10697, 8327,10697,10690,10697,10676,10697, 5,10696, +10603,10711, 6,10713, 7,10712,10715,10717, 7,10713, 6,10712, +10721,10723, 8326,10697,10603,10726, 8251,10727,10674,10697, 9,10732, + 8165,10696,10613,10737,10705,10737, 8296,10737,10684,10737, 8277,10737, + 9,10746,10603,10737, 9,10750, 9,10737,10603,10754, 8277,10754, + 265,10697, 8165,10697,10684,10762, 8251,10763, 9,10767, 9,10762, +10603,10770, 8251,10771,10603,10762, 9,10776,10684,10697, 8165,10780, + 8165,10781, 9,10697,10674,10786, 8165,10786,10603,10790, 8251,10791, + 8165,10787,10603,10797, 8277,10797,10603,10786, 8165,10802, 8165,10803, +10603,10697, 8326,10808, 8165,10808, 9,10812, 8165,10809, 9,10817, + 9,10808, 8165,10820, 8165,10821, 67,10697,10427,10827, 3,10385, + 8225,10831, 8242,10831,10427,10831,10409,10831, 6,10839, 7,10838, +10841,10843, 7,10839, 6,10838,10847,10849, 7,10831,10421,10853, + 375,10831,10590,10831, 9,10859, 8165,10830,10527,10863, 9,10865, + 8165,10831,10527,10868, 9,10871, 9,10831, 8241,10874,10527,10831, + 8326,10879, 8165,10878, 9,10883, 8165,10879, 9,10886, 9,10879, + 8165,10890, 8165,10891, 8241,10831, 9,10896, 89,10384, 88,10385, + 8225,10903, 8165,10385, 264,10907, 1368,10906,10909,10911, 7,10906, + 2,10915, 2,10914,10909,10919, 265,10915, 5,10907, 3,10924, +10919,10927,10911,10927, 3,10925,10917,10933, 1369,10925, 1369,10906, + 265,10907,10939,10941, 2,10907,10937,10945, 8229,10945, 9,10949, +10831,10945, 9,10953, 3,10907, 5,10957, 6,10959,10786,10957, + 5,10956,10919,10965,10911,10965,10697,10957, 9,10970, 9,10957, +10697,10974, 2,10906,10961,10979,10613,10979, 8296,10979,10974,10979, +10684,10979, 7,10978,10927,10991,10965,10991,10909,10991, 8277,10979, + 9,10998,10957,10979, 9,11002,10603,10979, 9,11006, 9,10979, +10603,11010, 8277,11010,10957,11010, 3,10906,10923,11019,10945,11019, + 9,11023,10527,11019, 9,11027, 9,10384, 1369,11031, 33,11031, + 3,11030, 8251,11037, 3807,11037, 9875,11037, 8165,11037, 3,11031, + 8241,11047, 1, 9, 69,11051, 6625,11053, 981,11050, 980,11050, + 4745,11059, 7966,11050, 7967,11050, 4,11050, 7,11067, 1275,11067, + 7954,11051, 7967,11051,11063,11075, 120,11050, 3867,11079, 112,11050, +11073,11083, 66,11050, 8251,11087, 1941,11087, 6159,11087, 3807,11087, + 9875,11087, 8165,11087, 121,11050, 7966,11051,11065,11103, 7957,11051, + 11,11106, 1141,11051, 1447,11051, 67,11051, 7,11114,11087,11117, + 113,11051,11083,11121, 7947,11123, 7947,11120,11083,11127, 89,11051, + 8495,11131, 25,11050, 113,11134, 3867,11137, 3867,11135, 113,11141, + 567,11050, 4950,11145, 587,11145, 4935,11148, 4935,11145, 587,11152, + 973,11050, 3,11156, 4745,11159, 587,11051, 566,11162, 566,11163, + 567,11163,11165,11169, 567,11162,11167,11173, 1011,11051, 1369,11051, + 6625,11179, 7956,11051, 7955,11051, 33,11051, 6625,11187, 25,11051, +11101,11191, 3984,11191, 3023,11191, 3933,11191, 113,11198, 113,11191, + 3933,11202, 973,11051, 2139,11207,11057,11207, 567,11051, 4971,11213, + 587,11215, 257,11051, 483,11219, 483,11050, 257,11223, 1011,11050, + 973,11227, 972,11226,11229,11231, 972,11227, 973,11226,11235,11237, + 257,11227, 265,11050, 5019,11243, 903,11051, 7,11246, 2,11050, +11187,11251,11073,11251,11127,11251,11207,11251,11113,11251,11111,11251, +11075,11251,11179,11251,11121,11251, 7947,11269, 25,11251, 5019,11251, + 3,11050,11068,11276,11069,11277,11279,11281,11068,11277,11069,11276, +11285,11287,11185,11277, 973,11276, 4745,11293,11103,11277,11109,11277, +11249,11277, 10,11276, 10,11277, 11,11276, 8251,11307, 3807,11307, + 9875,11307,11305,11307, 8165,11307, 33,11277, 7971,11319, 8165,11277, + 25,11323, 7947,11277, 973,11277, 4805,11329, 3807,11277, 8251,11277, + 4279,11277, 4283,11277, 9673,11277, 9967,11277, 9875,11277, 4745,11277, + 11,11277,11303,11349, 7971,11349, 2,11051,11315,11355,11303,11355, +11349,11358,11350,11355,11304,11355,11307,11365,11349,11355,11303,11368, + 5,11355, 7,11372,11276,11374,11277,11375,11377,11379,11276,11375, +11277,11374,11383,11385, 7,11355, 5,11388,11276,11390,11277,11391, +11393,11395,11276,11391,11277,11390,11399,11401, 5,11354, 7,11404, +11277,11407, 7,11405, 7,11354,11087,11413, 5,11412,11277,11417, +11307,11413,11277,11413, 4,11354,11373,11425, 7,11427,11277,11429, + 10,11354,11087,11433,11307,11433,11277,11433, 10,11355,11349,11441, +11277,11440,11307,11445,11277,11441,11303,11449, 4,11355,11410,11453, +11277,11455, 7,11453,11405,11458,11277,11461,11276,11458,11277,11459, +11405,11467,11465,11467,11276,11459,11277,11458,11473,11475,11405,11453, + 7,11478,11277,11481, 1,11452, 1,11355, 4,11486, 67,11355, + 1275,11355, 9,11493,11485,11495,11489,11495, 7947,11355, 11,11501, +11277,11503,11277,11355, 10,11506,11307,11509, 10,11507,11349,11513, + 11,11354,11326,11517,11277,11517, 7947,11520, 7947,11517,11277,11524, + 3,11051, 8213,11529, 8052,11529, 7971,11529, 113,11534, 113,11529, + 7971,11538, 8241,11529, 7947,11528, 483,11529, 112,11051,11326,11549, +11277,11549, 7947,11552, 7947,11549,11277,11556, 67,11050,11187,11561, +11053,11561,11179,11561, 66,11051, 113,11050,11556,11571,11185,11571, +11549,11571, 7947,11576, 25,11570, 3867,11581, 7947,11571,11549,11584, + 25,11571, 3933,11589, 7947,11051, 113,11592,11083,11595,11251,11595, + 113,11593,11549,11601, 3,11593,11549,11605,11517,11605, 3,11592, + 11,11051, 8116,11613, 8099,11613,11272,11613,11561,11613,11491,11613, +11251,11613, 25,11624, 8074,11613, 8093,11629, 25,11613,11251,11632, + 8109,11613, 8063,11636, 8063,11613, 8109,11640, 7957,11612,11277,11645, + 6625,11613, 113,11613, 2,11613,11183,11653,11545,11653,11611,11653, +11529,11653, 7947,11661, 3,11613, 7971,11664, 8093,11667, 7971,11665, + 8109,11671, 2,11612,11605,11675,11326,11675,11277,11675, 7947,11680, + 7947,11675,11277,11684, 7971,11613, 3,11688, 8093,11691, 3,11689, + 8063,11695, 11,11050,11355,11699, 113,11701, 1369,11699,11251,11705, + 33,11699,11251,11709, 3,11698, 8251,11713, 3807,11713,11433,11713, +11413,11713, 9875,11713, 8165,11713, 3,11699, 8241,11727, 0, 11, + 8095,11731, 8112,11731, 6688,11731, 6285,11731, 203,11730, 2351,11741, + 8159,11731, 8162,11731, 8128,11731, 8114,11731, 8097,11731, 8118,11731, + 8100,11731, 5,11730, 1369,11759, 8116,11731, 9,11762, 8099,11731, + 9,11766, 181,11731, 98,11731, 5019,11773, 8058,11731, 8093,11777, + 8156,11731, 8093,11781, 8076,11731, 8093,11785, 30,11731, 8124,11731, + 8063,11790, 8072,11731, 8109,11794, 8063,11731, 8161,11798, 8124,11798, + 8109,11798, 9,11804, 9,11798, 8109,11808, 25,11731, 6685,11812, + 9,11812, 89,11731, 5019,11819, 21,11821, 21,11818, 5019,11825, + 2759,11731, 2789,11731, 2821,11731, 6658,11731, 6685,11731, 25,11836, + 4413,11731, 113,11731, 9,11842, 128,11731, 659,11731, 623,11731, + 645,11731, 705,11731, 299,11731, 828,11731, 394,11731, 241,11731, + 375,11862, 483,11731, 803,11866, 8074,11731, 8093,11871, 9,11873, + 9,11870, 8093,11877, 101,11731, 21,11731, 89,11882, 5019,11885, + 89,11883, 5045,11889, 8061,11731, 8109,11892, 803,11731, 483,11896, + 375,11731, 241,11900, 587,11901, 6625,11731, 9,11906, 6789,11731, + 2,11911, 6855,11913, 1369,11730, 587,11730, 375,11919, 8109,11731, + 8061,11922, 8072,11922, 8063,11922, 9,11928, 9,11922, 8063,11932, + 145,11731, 903,11730, 375,11939, 265,11731, 8161,11731, 8063,11944, + 2,11730, 8124,11949, 8804,11949, 3972,11949, 8150,11949, 8495,11949, + 9,11958, 7971,11949, 9,11962, 3933,11949, 9,11966, 9,11949, + 8109,11970, 8495,11970, 3933,11970, 7971,11970, 8109,11949, 9,11980, + 3,11730, 483,11985, 2,11731, 8075,11989, 9,11991, 3,11731, + 8151,11994, 5,11995, 6,11999, 0,11998, 0,11995, 5,12004, + 8150,11994, 8093,12009, 7971,11994, 8093,12013, 9,12015, 9,12012, + 8093,12019, 1369,11995, 11,12023,12003,12025,12007,12025, 7971,11995, + 8161,12031,11997,12031, 8124,12031, 8109,12031, 9,12038, 9,12031, + 8109,12042, 9,11994, 7971,12046, 8093,12049, 7971,12047, 8109,12053, + 89,11730, 5082,12057, 2969,12057, 5045,12057, 21,12062, 21,12057, + 5045,12066, 88,11731, 7971,12070, 8093,12073, 7971,12071, 8109,12077, + 8150,11731, 3,12080, 8093,12083, 3,12081, 8063,12087, 7971,11731, + 1368,12091, 264,12090,12093,12095, 5,12090, 3,12099, 3,12098, +12093,12103, 1369,12099, 7,12091, 2,12108,12103,12111,12095,12111, + 2,12109,12101,12117, 265,12109, 265,12090, 1369,12091,12123,12125, + 88,12090, 8093,12129, 88,12091, 8061,12133, 2,12091,12001,12137, + 7,12136,12103,12141,12095,12141, 9,12137, 3,12091, 8061,12149, + 8072,12149,12121,12149, 8063,12149, 9,12156, 9,12149, 8063,12160, + 2,12090,12107,12165,12149,12165, 9,12169, 3,12090, 5,12173, + 6,12175,12137,12177,12146,12173, 5,12172,12111,12183,12141,12183, +12093,12183, 8093,12173, 9,12191,12137,12173, 9,12194, 9,12172, + 8093,12199, 9,12173,12137,12202, 9,12090, 3,12206, 8093,12209, + 3,12207, 8063,12213, 9,11731, 8116,12216, 8099,12216, 8074,12216, + 8093,12223, 25,12216, 8109,12216, 8063,12228, 8063,12216, 8109,12232, + 6625,12216, 113,12216, 3,12216, 7971,12240, 8093,12243, 7971,12241, + 8109,12247, 7971,12216, 3,12250, 8093,12253, 3,12251, 8063,12257, + 0, 9, 192,12261, 2329,12263, 211,12261, 2419,12267, 211,12260, + 2433,12271, 192,12260, 2337,12275, 189,12260, 2337,12279, 4,12261, + 7,12282, 413,12260, 413,12261, 1216,12260, 1216,12261, 1086,12261, + 9617,12295, 137,12261, 587,12261, 1011,12300, 9617,12303, 1011,12301, + 9565,12307, 1011,12261, 9617,12311, 587,12313, 587,12310, 9617,12317, + 1011,12260, 5267,12321, 9600,12321, 9565,12321, 587,12326, 587,12321, + 9565,12330, 587,12260, 5231,12335, 5181,12335,10505,12335, 1275,12261, + 2,12260,10001,12345, 5,12347, 4,12346,12349,12351, 4,12347, + 5,12346,12355,12357, 3,12260,11243,12361,11251,12361, 5177,12361, + 5185,12361, 2,12261, 3,12261,11355,12373, 1, 265, 471,12376, + 2647,12379, 357,12376, 1785,12383, 586,12377, 375,12387, 586,12376, + 413,12377, 3,12377, 7,12394, 375,12397, 7,12395, 331,12401, + 483,12395, 15,12405, 5,12377, 17,12409, 587,12411, 3,12376, + 669,12415, 1418,12415,12413,12415, 1369,12415, 15,12422, 15,12415, + 1369,12426, 5,12376,12417,12431, 1419,12431,12415,12435, 668,12431, +12415,12439,12419,12431,12407,12431,12429,12431, 9,12431, 587,12448, +12415,12451,12425,12431, 587,12431, 9,12456,12415,12459, 6719,12377, + 9,12463, 7867,12377, 251,12467, 3357,12377, 3681,12377, 3709,12377, + 2647,12377, 5843,12377, 4407,12377,11917,12377, 305,12377, 713,12377, + 25,12377, 4405,12377, 7041,12377, 251,12493, 1471,12376, 2011,12497, + 53,12376, 2035,12501, 257,12377, 253,12376, 5325,12507, 251,12376, + 7867,12511, 7617,12511, 7041,12511, 7617,12377, 251,12519, 1363,12376, + 5189,12523, 483,12377, 9,12377, 8938,12377, 375,12531, 5313,12377, + 7035,12377, 7091,12537, 7083,12537, 7123,12537, 241,12537, 11,12376, + 5313,12547, 587,12376,12387,12551,11731,12551, 9,12376, 5019,12557, +12361,12557, 1369,12376, 11,12377, 587,12377,12391,12567, 7,12376, + 2,12571, 4,12572,12537,12575, 4,12571, 2,12578,12537,12581, +12545,12571, 240,12571,12537,12587, 8948,12571, 331,12571, 8861,12592, + 8861,12571, 331,12596, 4827,12571, 6,12377, 257,12603, 265,12605, + 241,12603, 1,12609,12607,12611, 7,12377, 3,12614, 375,12617, + 8861,12614, 375,12621, 375,12615, 8861,12625, 241,12614, 375,12629, + 257,12376, 2647,12633, 1783,12633, 256,12377, 375,12639, 8861,12377, + 261,12643, 260,12643, 256,12643, 6,12643, 241,12650, 7,12643, + 241,12654, 331,12655, 6,12642,12645,12661, 7,12642,12647,12665, +12653,12665, 375,12665, 241,12643, 6,12672,12665,12675, 7,12672, + 6,12673,12679,12681,12655,12681, 7,12673,12675,12687, 241,12377, + 7,12691, 331,12693, 7,12690, 375,12697, 8997,12691, 7,12701, + 241,12376, 4457,12705, 374,12705, 5189,12705, 265,12705, 6,12712, + 8386,12705, 6,12705, 265,12718, 7,12705, 8337,12722, 8337,12705, + 7,12726, 8861,12705, 6,12731, 7,12731, 6,12730,12735,12737, + 7,12730,12733,12741, 1, 241, 448,12745, 4349,12747, 445,12744, + 1679,12751, 457,12744, 4371,12755, 8461,12744, 8475,12759, 277,12744, + 4457,12763, 923,12744, 4959,12767, 8388,12744, 8389,12744, 4,12745, + 11,12775, 1369,12777, 2,12745, 1275,12781, 9,12783, 2,12744, + 1419,12787, 668,12787,12779,12787, 587,12787, 9,12794, 9,12787, + 587,12798, 4,12744,12789,12803, 669,12803,12787,12807, 1418,12803, +12787,12811,12791,12803,12785,12803,12801,12803, 15,12803, 1369,12820, +12787,12823,12797,12803, 1369,12803, 15,12828,12787,12831, 445,12745, + 265,12834, 4349,12837, 923,12745, 5009,12841, 8374,12745, 346,12745, + 2981,12847, 386,12745, 387,12745, 3049,12853, 347,12745, 8389,12745, +12771,12859, 1412,12745, 5227,12863, 346,12744, 2977,12867,12857,12867, + 386,12744, 4067,12873,12853,12873, 1412,12744, 5189,12879, 374,12744, +12845,12883, 387,12744, 3043,12887,12851,12887, 347,12744,12847,12893, + 272,12745, 4453,12897, 4437,12897, 1755,12897, 8388,12745, 8495,12905, +12773,12905, 8387,12745, 265,12911, 8861,12913, 265,12910, 8495,12917, + 253,12745, 59,12744, 1325,12744, 341,12745, 375,12745,12883,12929, + 8337,12931, 8337,12928,12883,12935, 4371,12745, 457,12939, 375,12939, + 4531,12745, 375,12945, 8366,12745, 375,12949, 305,12744, 4457,12953, + 375,12952, 4067,12957, 4067,12953, 375,12961, 331,12744, 4457,12965, +12927,12965, 8386,12745, 8495,12971, 331,12971, 1325,12971, 59,12971, + 265,12971, 8375,12745, 8495,12983, 305,12745,12887,12987, 4266,12987, + 3031,12987, 4213,12987, 375,12994, 8337,12986, 375,12999, 375,12987, + 4213,13002, 5325,12745, 7,13007, 5361,13009, 5385,13009, 5407,13009, + 1275,12744, 15,12744, 8475,12745, 375,13021, 8461,13021, 6,12744, + 8429,13027, 8428,13027, 4139,13027, 4138,13027, 3,13026, 5,13036, + 5,13026, 3,13040, 264,13026,13031,13045,13035,13045, 264,13027, + 8401,13051, 4137,13051, 265,13026, 8401,13057,13051,13058,13029,13057, +13052,13057, 4137,13057,13051,13066,13033,13057,13054,13057,13051,13057, + 8401,13074, 4137,13074, 4137,13027, 265,13081,13051,13083, 265,13080, +13045,13087, 341,13027, 265,13027, 8401,13092,13045,13095, 4137,13092, +13045,13099,13045,13093, 8401,13103, 4137,13103, 8401,13027, 265,13109, +13051,13111, 265,13108,13045,13115, 7,12744, 8605,13119, 8374,13119, + 8916,13119, 8389,13119, 412,13119, 241,13119, 265,13130, 264,13118, + 264,13119, 8452,13119, 8647,13119, 265,13118,13137,13143, 8861,13119, + 375,13146, 8337,13119, 375,13150, 265,13150, 305,13119, 713,13119, + 25,13119, 375,13119, 8861,13162, 8337,13162, 265,13119, 241,13168, +13135,13169, 8337,13168, 6,12745,12965,13177,13145,13177,13139,13177, + 8453,13177,13119,13185,12923,13177,12925,13177,13135,13177,13169,13192, +13172,13177,13136,13177,13143,13199,13175,13177,13155,13177,13169,13177, +13135,13206, 3,13177, 5,13210,13118,13212,13119,13213,13215,13217, +13118,13213,13119,13212,13221,13223, 5,13177, 3,13226,13118,13228, +13119,13229,13231,13233,13118,13229,13119,13228,13237,13239, 264,13177, +13119,13242,13143,13245,13119,13243,13135,13249, 413,13177, 8452,13177, + 8861,13255, 305,13177, 8337,13177, 265,13260, 8861,13263, 265,13261, + 8495,13267, 265,13177, 8337,13270, 8861,13273, 8861,13271, 8337,13277, +13119,13177, 264,13280,13143,13283, 264,13281,13169,13287, 265,13280, +13287,13291, 265,13281,13283,13295, 8387,13177, 264,13299, 265,13298, +13301,13303, 264,13298, 265,13299,13307,13309, 265,13176, 8842,13313, + 8495,13313, 8337,13316, 8337,13313, 8495,13320, 7,12745,13039,13325, +13043,13325,13045,13325, 241,13324, 0,13333, 8717,13325, 8495,13325, + 8337,13339, 305,13339, 4311,13325, 4329,13325, 3785,13325,10351,13325, +10055,13325, 8337,13324, 8495,13355, 331,13355, 1325,13355, 59,13355, + 265,13355, 59,13325, 8337,13367, 1325,13325, 8337,13371, 331,13325, + 8337,13375, 8861,13375, 265,13325,13051,13381,13335,13381, 5325,13381, + 1,13381,13333,13389, 8861,13381, 8337,13381,13027,13325, 264,13396, + 264,13397, 265,13396,13401,13403, 265,13397,13399,13407, 265,13324, + 8717,13411, 3785,13411,13045,13411, 8495,13411, 374,12745, 8842,13421, + 8495,13421, 8337,13424, 8337,13421, 8495,13428, 340,12745, 8717,13433, +13045,13433, 3785,13433, 8495,13433, 375,12744, 4371,13443,13428,13443, +12983,13443, 8475,13443,13421,13443, 8337,13452, 305,13442, 4067,13457, + 8337,13443,13421,13460, 305,13443, 4213,13465, 8452,12745, 8647,13469, + 7,13469, 8495,13473, 8337,12745, 375,13476,12883,13479, 375,13477, + 305,13483,13421,13483, 305,13476, 375,13489, 6,13477, 8453,13493, + 8452,13492,13495,13497, 8452,13493, 8453,13492,13501,13503, 7,13477, +13213,13507,13229,13507, 375,13507,13243,13507, 265,13507,13177,13517, + 6,13476, 3,13521, 5,13522,13507,13525, 5,13521, 3,13528, +13507,13531,13517,13521, 264,13521,13507,13537, 7,13476, 8495,13541, + 331,13541, 1325,13541, 59,13541, 265,13541, 265,13476, 8647,13553, + 7,13553, 8495,13557, 265,13477,13177,13561,13521,13561, 7,13561, + 265,12745, 445,13568, 4349,13571, 445,13569, 4371,13575, 8770,13569, + 8729,13569,13027,13569, 375,13583, 8684,13569, 8717,13587, 331,13569, +13177,13591, 8757,13569, 8647,13594, 8647,13569, 8757,13598, 8387,13568, + 8495,13603, 6,13569, 7,13569, 8647,13609, 8495,13608, 8717,13613, + 8495,13609, 8757,13617, 6,13568, 8842,13621, 8495,13621, 8337,13624, +13609,13621, 8337,13621, 8495,13630, 7,13568, 8717,13635, 3785,13635, +13045,13635, 8495,13635,13607,13635, 8337,13568, 8647,13647, 7,13647, + 8495,13651, 8495,13569, 6,13654, 7,13654, 8717,13659, 6,13655, +13659,13663, 7,13655,13657,13667, 8647,13667, 265,12744,13258,13673, +12845,13673,13091,13673,12935,13673,12927,13673,12929,13673, 8337,13685, +13479,13673,12911,13673,13260,13673,12859,13673, 4457,13673, 5189,13673, +13177,13673, 305,13700, 8337,13700, 305,13673,13177,13706, 8386,13673, + 7,13673, 8337,13712, 8861,13673, 6,13717, 7,13717, 6,13716, +13721,13723, 7,13716,13719,13727, 8337,13673,13177,13730, 7,13730, + 1, 587, 264,13737, 375,13739, 16,13737, 264,13736, 341,13745, + 7,13737, 4,13748, 4,13749, 3,13736, 717,13755, 341,13755, + 6,13755, 713,13761, 17,13755, 566,13736, 1108,13737, 9565,13769, +11731,13737, 265,13772,11942,13737, 3565,13737, 3573,13737, 3273,13737, + 375,13736, 113,13736, 4696,13787, 9,13787, 4669,13790, 4669,13787, + 9,13794, 1011,13737, 9565,13799, 9,13801, 9,13798, 9565,13805, + 241,13737, 9,13737, 1011,13810, 9565,13813, 1011,13811, 9617,13817, + 113,13737, 4727,13821, 9,13823, 7049,13737, 807,13827, 375,13827, + 265,13736,11731,13833, 1487,13737, 1011,13736, 5293,13839, 9704,13839, + 9617,13839, 9,13844, 9,13839, 9617,13848, 265,13737,11731,13852, + 4,13736,13757,13857,13762,13857,13761,13857, 713,13862,13759,13857, + 340,13857,13745,13869,13747,13857,13740,13857, 375,13857,13739,13876, + 7,13857, 265,13880,13745,13883, 265,13881,13739,13887,13739,13857, + 375,13890, 713,13857,13761,13894, 265,13857, 7,13898,13745,13901, + 483,13857, 5,13736,13853,13907, 375,13909, 483,13906, 4,13737, + 7,13914, 767,13915,13761,13919, 5,13737, 3,13923, 1,13924, + 1,13923, 3,13928, 17,13923, 587,13933,13927,13935,13931,13935, + 567,13736, 483,13736, 5,13942, 1, 483, 9174,13946, 9175,13946, + 2,13947, 7,13952, 2,13946, 7,13957, 6,13946, 9170,13947, + 9175,13947,13949,13965, 824,13946, 3997,13969, 802,13946,13963,13973, + 766,13946, 825,13946, 9174,13947,13951,13981, 803,13947,13973,13985, + 9165,13987, 9165,13984,13973,13991, 713,13946, 803,13994, 3997,13997, + 3997,13995, 803,14001, 1171,13946, 4663,14005, 903,14007, 155,13946, + 9172,13947, 9171,13947, 713,13947,13979,14017, 4200,14017, 3027,14017, + 4147,14017, 803,14024, 803,14017, 4147,14028, 1171,13947, 4722,14033, + 903,14033, 4713,14036, 4713,14033, 903,14040, 257,13947, 9,14045, + 9,13946, 257,14049, 903,13946, 257,14053, 11,13946, 5197,14057, + 5125,14057,12335,14057, 4,13946, 2,14065, 7,14067,14013,14069, + 5,13946,13958,14072,13959,14073,14075,14077,13958,14073,13959,14072, +14081,14083, 586,14072, 586,14073, 587,14072,14089,14091, 4827,14073, + 9,14073, 4569,14073, 4745,14073, 587,14073,14087,14103, 4,13947, +14093,14107,14087,14107,14103,14110,14104,14107,14088,14107,14091,14117, +14103,14107,14087,14120, 3,14107, 7,14124,14072,14126,14073,14127, +14129,14131,14072,14127,14073,14126,14135,14137, 7,14107, 3,14140, +14072,14142,14073,14143,14145,14147,14072,14143,14073,14142,14151,14153, + 3,14106, 7,14157, 2,14106,14125,14161, 7,14163,14073,14165, + 586,14107,14073,14168,14091,14171,14073,14169,14087,14175, 2,14107, +14158,14179,14073,14181, 7,14179,14157,14184,14073,14187,14073,14185, +14157,14191,14157,14179, 7,14194,14073,14197,14073,14107, 586,14200, +14091,14203, 586,14201,14103,14207, 5,13947,14069,14211, 9165,14213, + 6,14211, 9165,14210,14069,14219, 17,14211, 802,13947, 9165,14225, + 803,13946,14226,14229,14015,14229,14225,14229, 9165,14234, 713,14228, + 3997,14239, 9165,14229,14225,14242, 713,14229, 4147,14247, 9165,13947, + 803,14250,13973,14253, 803,14251,14225,14257, 4,14251, 2,14261, + 7,14263, 5,14250,14265,14267,14069,14267, 587,13947, 9340,14273, + 9323,14273, 9290,14273, 9317,14279, 9335,14273, 9287,14282, 9287,14273, + 9335,14286, 5,14273, 9195,14290, 9317,14293, 9195,14291, 9335,14297, + 9195,14273, 5,14300, 9317,14303, 5,14301, 9287,14307, 587,13946, + 5,14310, 0, 265, 446,14315, 4371,14317, 445,14314, 1675,14321, + 8725,14315, 8766,14315, 471,14314, 2367,14329, 8827,14315, 8836,14315, + 319,14314, 4497,14337, 1302,14315, 667,14315, 8794,14315, 8768,14315, + 8727,14315, 8774,14315, 8732,14315, 1011,14315, 1275,14354, 3,14315, + 1369,14359, 11,14361, 5,14315, 9,14365, 1275,14367, 3,14314, + 1291,14371, 524,14371,14369,14371, 483,14371, 11,14378, 11,14371, + 483,14382, 5,14314, 525,14387,14371,14389,14373,14387,14375,14387, + 1290,14387,14371,14397,14363,14387,14385,14387,14381,14387, 17,14387, + 1275,14406,14371,14409, 1275,14387, 17,14412,14371,14415, 8770,14315, + 241,14418, 8729,14315, 241,14422, 445,14315, 241,14426, 4371,14429, + 1303,14315, 666,14315, 8784,14315, 4266,14315, 8622,14315, 8717,14441, + 8824,14315, 8717,14445, 8702,14315, 8717,14449, 256,14314, 7185,14453, + 2309,14453, 5673,14453, 5521,14453, 375,14461, 4213,14315, 375,14464, + 316,14315, 4533,14469, 1757,14469, 8697,14315, 629,14315, 407,14315, + 5325,14479, 3121,14315, 3031,14315, 5519,14315, 3355,14315, 510,14315, + 8788,14315, 8647,14492, 8676,14315, 8757,14496, 8647,14315, 8831,14500, + 8788,14500, 8757,14500, 241,14506, 241,14500, 8757,14510, 331,14315, + 1325,14315, 59,14315, 5521,14315, 375,14521, 375,14315, 4213,14524, + 8757,14524, 53,14525, 1471,14525, 253,14525, 253,14314, 4425,14537, + 4497,14537, 483,14315, 9,14542, 9,14315, 483,14546, 8684,14315, + 8717,14551, 241,14553, 241,14550, 8717,14557, 331,14551, 251,14315, + 257,14563, 8495,14565, 8635,14315, 8757,14568, 5515,14315, 331,14573, + 8757,14315, 8635,14576, 8676,14576, 8647,14576, 241,14582, 375,14576, + 241,14576, 8647,14588, 1369,14314, 2853,14593, 15,14315, 1275,14315, + 1011,14598, 587,14315, 241,14602, 8831,14315, 8647,14606, 6,14314, +14341,14611,14343,14611,14601,14611,14357,14611, 8495,14611, 241,14611, + 7,14314,14433,14625,14435,14625,14605,14625, 8495,14625, 3785,14625, +10055,14625,10351,14625, 4329,14625, 4311,14625, 4555,14625,14073,14625, + 4971,14625, 8717,14625, 7,14315, 8813,14652,14623,14653, 8495,14657, + 4,14653, 2,14660,14620,14663, 8495,14663,14611,14666,14611,14663, + 8495,14670, 2,14653, 4,14674,14620,14677, 8495,14677,14611,14680, +14611,14677, 8495,14684, 240,14653,14620,14689, 8495,14689,14611,14692, +14611,14689, 8495,14696, 8812,14652, 8717,14701, 8495,14652, 8717,14705, + 241,14707, 241,14704, 8717,14711, 331,14705, 15,14653, 483,14717, + 1275,14653, 9,14721, 8495,14653, 8831,14725,14655,14725, 8788,14725, + 8757,14725, 241,14732, 241,14725, 8757,14736, 375,14653, 8647,14653, + 241,14652, 8647,14745, 8495,14744, 8717,14749, 8495,14745, 8757,14753, + 257,14314, 8614,14757, 4417,14757, 8495,14757, 251,14762, 251,14757, + 8495,14766, 256,14315, 8647,14771, 7207,14771, 5432,14771, 5325,14771, + 375,14778, 8495,14770, 8717,14783, 375,14771, 5325,14786, 8495,14771, + 8757,14791, 8812,14315, 6,14794, 7,14794, 8717,14799, 6,14795, +14799,14803, 7,14795,14797,14807, 8647,14807, 8495,14315, 261,14812, + 260,14812, 256,14812, 8717,14819, 256,14813, 8635,14823, 6,14813, +14815,14827, 7,14813,14817,14831, 8635,14831, 8676,14831, 8647,14831, + 241,14838, 375,14831, 241,14831, 8647,14844, 6,14812, 241,14848, +14831,14851, 7,14812, 8717,14855, 241,14857, 241,14854, 8717,14861, + 331,14855, 241,14812, 6,14866,14831,14869, 7,14866, 8717,14873, + 6,14867,14873,14877,14855,14877, 7,14867,14869,14883, 8647,14883, + 241,14315, 445,14888, 4371,14891, 445,14889, 4349,14895, 8770,14888, + 8729,14888, 587,14888,14625,14903, 8684,14888, 8717,14907, 8757,14888, + 8647,14910, 8387,14889, 8495,14915, 8647,14888, 8757,14918, 6,14889, + 8842,14923, 8495,14923, 8337,14926, 8337,14923, 8495,14930, 7,14889, + 8717,14935, 3785,14935, 8495,14935, 6,14888,14935,14943, 7,14888, + 8647,14947, 8495,14946, 8717,14951,14923,14947, 8495,14947, 8757,14957, + 8495,14888, 6,14960, 7,14960, 8717,14965, 6,14961,14965,14969, + 7,14961,14963,14973, 8647,14973, 8337,14889, 8647,14979, 7,14979, + 8495,14983,12745,14315, 375,14987, 241,14314, 9099,14991, 9036,14991, + 8647,14991, 8997,14996, 5521,14991, 375,15001, 375,14991, 9082,14991, + 8495,15007, 7,14991, 8495,15011, 8997,15013, 8997,15010, 8495,15017, + 8997,14991, 8647,15020, 7,15020, 8495,15025, 0, 241, 460,15029, + 2315,15031, 479,15029, 2393,15035, 479,15028, 2397,15039, 460,15028, + 2317,15043, 457,15028, 2317,15047, 269,15028, 2885,15051, 464,15029, + 16,15029, 413,15029, 6,15058, 4,15029, 7,15062, 7,15063, + 15,15063, 483,15069, 2,15029, 587,15073, 17,15075, 2,15028, + 525,15079, 1290,15079,15071,15079, 1275,15079, 17,15086, 17,15079, + 1275,15090, 4,15028, 1291,15095,15079,15097,15081,15095,15083,15095, + 524,15095,15079,15105,15077,15095,15093,15095,15089,15095, 11,15095, + 483,15114,15079,15117, 483,15095, 11,15120,15079,15123, 9072,15029, + 305,15127, 9105,15029, 305,15131, 9083,15029, 413,15135, 412,15134, +15137,15139, 412,15135, 413,15134,15143,15145, 3445,15029, 375,15149, +12526,15029,12528,15029, 251,15029, 375,15029, 305,15159, 8997,15161, + 8997,15158, 305,15165,12614,15029, 375,15169, 7065,15029, 265,15173, + 7259,15175, 305,15028, 9105,15179, 9072,15179, 375,15179, 8997,15184, + 8997,15179, 375,15188, 9082,15029, 375,15193, 265,15193, 5521,15029, + 375,15199, 265,15199, 483,15029,12377,15204, 9,15029,12377,15208, + 17,15029,13737,15029, 375,15215, 6,15028, 3,15219, 5,15220, +15193,15223, 5,15219, 3,15226,15193,15229,15197,15219,15057,15219, + 3,15235, 2,15234,15237,15239, 2,15235, 3,15234,15243,15245, +15065,15219, 3,15249, 2,15248,15251,15253, 2,15249, 3,15248, +15257,15259, 264,15219,15193,15263, 8997,15219, 7,15028, 4437,15269, +15213,15269, 3,15273, 2,15272,15275,15277, 2,15273, 3,15272, +15281,15283,12571,15269, 4805,15269, 4613,15269,14073,15269, 375,15269, + 510,15269, 4971,15269, 483,15269, 9,15300, 9,15269, 483,15304, + 6,15029, 9020,15309, 9021,15309,12393,15309,12392,15309, 8337,15309, + 265,15318, 8452,15308, 241,15322, 8453,15309,15325,15327, 8452,15309, + 8453,15308, 412,15309, 8997,15335, 241,15308, 265,15338,15327,15341, + 8452,15339,15333,15345,15067,15309, 3,15349, 2,15348,15351,15353, + 2,15349, 3,15348,15357,15359, 413,15308,15313,15363,15315,15363, +15331,15363,15321,15363, 8997,15363,15335,15372,15336,15363,15335,15363, + 8997,15378, 412,15308,15311,15383,15317,15383,15327,15383, 413,15309, +12377,15390,15383,15393, 8997,15390,15383,15397,15383,15391, 8997,15401, +12377,15309, 413,15404,15383,15407, 413,15405,15335,15411, 8997,15309, + 413,15414,15383,15417, 413,15415,15335,15421, 265,15309, 8337,15424, +15363,15427, 265,15308, 241,15430,15327,15433, 7,15029,15223,15437, + 8997,15439,15229,15437, 8997,15443,15263,15437, 8997,15447, 4,15436, +15219,15451, 3,15453, 2,15452,15455,15457, 2,15453, 3,15452, +15461,15463, 264,15436, 264,15437,12377,15436, 375,15471, 8997,15436, +15223,15475,15229,15475, 375,15475,15263,15475, 265,15475,15219,15485, + 59,15437, 1325,15437, 331,15437, 1275,15437, 9,15495, 15,15437, + 483,15499, 265,15437,15266,15503,15219,15503, 8997,15506,15467,15503, + 8997,15503,15219,15512, 305,15503, 265,15436,15469,15519, 331,15519, + 375,15028, 3307,15525, 2885,15525,12511,15525,12377,15525, 251,15533, + 340,15029, 331,15537, 9136,15029, 7,15541, 8997,15029, 375,15544, + 305,15547, 305,15545, 375,15551, 6,15545, 3,15555, 5,15556, + 5,15555, 3,15560, 264,15555, 7,15545, 8495,15567, 331,15567, + 1325,15567, 59,15567, 265,15567, 7,15544,15559,15579,15563,15579, +15223,15579,15229,15579, 375,15579,15263,15579,15565,15579, 265,15579, +15219,15595,15555,15595, 265,15544,15555,15601, 7,15601, 265,15545, + 8647,15607, 7,15607, 8495,15611, 265,15029, 7,15615, 375,15617, + 7,15614, 331,15621, 8997,15614, 7,15625,12377,15029,13421,15629, +13313,15629,13621,15629,14923,15629,12721,15629,12715,15629,12709,15629, + 375,15629,12705,15645, 7,15629, 331,15649, 483,15628, 9,15628, + 7,15628, 375,15657, 265,15028, 9036,15661, 9099,15661, 241,15661, + 6,15667,15629,15669, 8647,15661, 8997,15672, 5521,15661, 375,15677, + 9082,15661, 8495,15681, 375,15661, 7,15661, 8495,15687, 8997,15689, + 8997,15686, 8495,15693, 8997,15661, 8647,15696, 7,15696, 8495,15701, + 0, 587, 9319,15705, 9336,15705, 889,15704, 2373,15711, 9371,15705, + 9374,15705, 9350,15705, 9338,15705, 9321,15705, 9342,15705, 9324,15705, + 578,15705, 3,15705, 6,15730, 7,15705, 567,15735, 9195,15737, + 4,15735, 9366,15741, 483,15741, 9195,15744, 9195,15741, 483,15748, + 7,15704, 9340,15705, 483,15754, 9323,15705, 483,15758, 579,15705, + 9272,15705, 9317,15765, 9368,15705, 9317,15769, 9294,15705, 9317,15773, + 566,15704,15729,15777, 9348,15705, 9287,15780, 9288,15705, 9335,15784, + 9287,15705, 9373,15788, 9348,15788, 9335,15788, 483,15794, 483,15788, + 9335,15798, 567,15705, 9,15802,15777,15805,15777,15803, 9,15809, +12377,15705, 11,15813, 9,15705, 567,15816,15777,15819, 567,15817, + 9290,15705, 9317,15825, 483,15827, 483,15824, 9317,15831, 9275,15705, + 9335,15834, 375,15705, 11,15839, 11,15704, 375,15843, 9335,15705, + 9275,15846, 9288,15846, 9287,15846, 483,15852, 483,15846, 9287,15856, + 9,15704, 5231,15861, 5181,15861,10505,15861,14057,15861, 1011,15704, + 375,15871, 9373,15705, 9287,15874, 4,15704, 9262,15879, 375,15879, + 9195,15882, 7,15879, 265,15887, 9195,15889, 9195,15879, 375,15892, + 483,15879, 5,15704, 5,15705, 9367,15900, 9366,15900, 9317,15905, + 9195,15900, 9317,15909, 483,15911, 483,15908, 9317,15915, 9195,15901, + 9373,15919,15903,15919, 9348,15919, 9335,15919, 483,15926, 483,15919, + 9335,15930, 483,15900, 9195,15934, 9317,15937, 9195,15935, 9335,15941, + 567,15704,15763,15945, 9,15945, 566,15705,15823,15951,15948,15951, +15945,15951, 9,15956, 9195,15950, 9317,15961, 9195,15951, 9335,15965, + 9,15951,15945,15968, 9366,15705, 5,15972, 9317,15975, 5,15973, + 9287,15979, 9195,15705, 566,15982, 9317,15985, 566,15983, 9275,15989, + 5,15983, 9275,15993, 9288,15993, 9287,15993, 483,15998, 483,15993, + 9287,16002, 5,15982, 9317,16007, 483,16009, 483,16006, 9317,16013, + 483,15982, 5,16016, 9317,16019, 5,16017, 9287,16023, 483,15705, + 9340,16026, 9323,16026, 9290,16026, 9317,16033, 9335,16026, 9287,16036, + 9287,16026, 9335,16040, 5,16026, 9195,16044, 9317,16047, 9195,16045, + 9335,16051, 9195,16026, 5,16054, 9317,16057, 5,16055, 9287,16061, + 0, 483, 878,16065, 2327,16067, 899,16065, 2415,16071, 899,16064, + 2423,16075, 878,16064, 2331,16079, 875,16064, 2331,16083, 573,16064, + 2569,16087, 2,16064, 15,16091, 413,16064, 413,16065, 573,16065, + 2567,16099, 1216,16064, 1216,16065, 904,16065, 2263,16107, 6515,16065, + 5,16111, 6539,16113, 903,16064, 4421,16117, 5285,16117, 2265,16117, + 4,16064, 5,16064, 4,16065, 2,16129, 7,16131, 0,16130, + 0,16129, 2,16136, 15,16129, 483,16141,16135,16143,16139,16143, + 9513,16065, 5,16149,16133,16151, 4,16148, 2,16155, 7,16157, +16151,16159, 1, 1011, 996,16162, 14,16162, 5,16163, 6,16169, + 9617,16171, 2,16162, 9617,16175,16171,16176,16172,16175,16171,16175, + 9617,16182, 7,16175,16169,16187, 9617,16189, 5,16162,16186,16193, +16175,16193, 7,16196, 7,16197,16187,16193, 6,16192,16195,16205, +16199,16205, 7,16192,16203,16211,16175,16211, 3,16211,16162,16217, + 3,16193, 7,16221,16162,16222,16162,16221, 7,16226, 7,16227, +16205,16231, 6,16193,16201,16235,16214,16235,16175,16235,16211,16240, +16211,16235,16175,16244,16225,16235,16229,16235,16219,16235, 7,16193, +16175,16254,16205,16257,16205,16255,16175,16261, 9704,16163, 587,16265, + 5293,16163, 587,16269, 997,16162, 972,16162,16235,16275, 9,16275, +14652,16163, 973,16163,16273,16283,16278,16283, 9,16283,16275,16288, +16275,16283, 9,16292, 955,16162, 5241,16297, 1505,16162, 6,16301, + 7,16300,16303,16305, 7,16301, 6,16300,16309,16311, 67,16162, +16235,16315, 9,16163, 9617,16318, 587,16321, 587,16319, 9617,16325, + 15,16162, 257,16163, 9,16331, 9,16162, 973,16335,16275,16337, + 973,16334, 257,16335, 587,16162, 5293,16345, 9704,16345, 9617,16345, + 9,16350, 9,16345, 9617,16354, 6,16162, 5,16358, 7,16162, +16221,16362,16235,16365,16235,16363, 8647,16363, 903,16362,16235,16373, + 6,16163,16329,16377, 7,16163,16167,16381,16205,16381,16361,16381, + 8717,16381,14315,16380, 4311,16381, 4329,16381, 3785,16381,10351,16381, +10055,16381, 973,16162, 9,16402, 972,16163,16341,16407,16165,16407, +16405,16407,16403,16407, 9,16415, 9617,16163, 1368,16418, 14,16418, + 2,16418, 7,16425,16423,16427, 7,16424, 5,16418, 6,16433, +16431,16435,16421,16435, 6,16432,16427,16441, 2,16419,16435,16445, +16171,16445, 7,16445,16433,16451, 9,16418, 587,16455, 587,16419, + 9,16459, 6,16418, 5,16462,16427,16465, 7,16418, 2,16468, +16435,16471,14315,16163, 7,16474, 17,16475, 903,16162, 3,16480, + 6,16483, 6,16482, 5241,16481, 1274,16480, 15,16480, 1275,16480, + 6,16480, 3,16496, 5,16496, 7,16480,16485,16503,16235,16503, + 6,16481,16495,16509,16493,16509, 7,16481,16499,16515,16501,16515, +16487,16515,16205,16515,16361,16515,16491,16515,16167,16515, 1, 903, + 9542,16530, 9543,16530, 1274,16530, 3,16530, 9541,16539, 6,16538, + 7,16538, 5,16545,16530,16547, 5,16539, 7,16551,16530,16552, +16530,16551, 7,16556, 7,16557,16543,16561, 6,16539, 9533,16565, +16545,16565,16555,16565,16559,16565,16549,16565, 7,16539,16543,16577, + 4,16530,16576,16581,16543,16583,16539,16581, 7,16586,16543,16589, + 7,16587,16565,16593,16545,16581,16565,16596,16565,16581,16545,16600, +16568,16581,16579,16581, 7,16581,16539,16608,16543,16611,16539,16609, +16545,16615, 1170,16530,16565,16619, 9541,16531,16535,16623, 559,16530, + 5241,16627, 767,16530,16565,16631, 165,16530, 6,16635, 7,16634, +16637,16639, 7,16635, 6,16634,16643,16645, 9540,16531,16543,16649, +16533,16649,16539,16649,16537,16649, 1275,16530,16623,16659, 9533,16659, + 257,16531, 483,16665, 483,16530, 257,16669, 15,16530, 5241,16673, + 6,16530, 3,16676,16649,16679, 7,16530,16541,16683,16566,16683, +16551,16682,16565,16689,16565,16683, 9533,16692, 375,16683, 1011,16682, +16565,16699, 9533,16683,16565,16702,15269,16683, 4827,16683, 6,16531, +16662,16711,16659,16711, 9533,16714, 9533,16711,16659,16718, 7,16531, +16539,16723, 9533,16725,16537,16723, 9533,16729, 1007,16723,16543,16723, + 9533,16735,16679,16723, 9533,16739, 331,16723, 1325,16723, 59,16723, + 9533,16722,16543,16749,16539,16749,16679,16749,16537,16749, 9533,16531, + 1275,16759,16711,16761, 7,16759,16565,16765, 7,16758,16539,16769, +16543,16769,16679,16769,16537,16769,15029,16531, 7,16779, 1011,16530, + 5,16782, 6,16785, 6,16784, 5241,16783, 14,16782, 1275,16782, + 15,16782, 6,16782, 5,16798, 3,16798, 7,16782,16787,16805, +16565,16805, 6,16783,16797,16811,16795,16811, 7,16783,16801,16817, +16803,16817,16789,16817,16543,16817,16679,16817,16793,16817,16537,16817, + 1, 1369, 1010,16833, 16,16833, 2,16833, 5,16838, 5,16839, +16837,16843, 7,16833, 4,16846,16843,16849, 4,16847,16841,16853, +16835,16853, 2,16832, 11,16859,15900,16833, 331,16832, 59,16832, + 6717,16833, 9,16869, 1325,16832, 265,16832, 4,16833, 7,16876, +16843,16879, 2,16877, 1,16882, 1,16877, 2,16886, 11,16877, + 1369,16891,16885,16893,16889,16893, 5,16833, 2,16898,16853,16901, +15705,16898, 1275,16898, 9,16907, 1346,16833, 9,16911,15705,16833, + 11,16915, 5,16914, 1275,16833, 5,16921, 17,16923, 5,16920, + 9,16927, 1275,16832, 6717,16931, 9,16933, 1, 1275, 5814,16937, + 902,16937, 902,16936, 3,16937, 7,16944, 413,16937, 1216,16937, + 6787,16937, 5,16953, 4,16952,16955,16957, 4,16953, 5,16952, +16961,16963, 1505,16937, 5771,16937, 903,16968, 1011,16937, 903,16972, + 903,16936,16941,16977, 241,16936, 17,16936, 5299,16983, 4915,16983, + 11,16936, 903,16937, 5771,16990,16943,16991, 1011,16990,15208,16937, + 9,16937,15029,17000,15029,16937, 9,17004, 4,16936, 265,17009, + 5,16936, 4,16937, 1011,17015, 1369,16936, 6717,17019, 9,17021, + 1, 17, 4,17025, 6,17026, 113,17025, 1275,17024, 5299,17033, + 4915,17033, 241,17025, 2,17024, 3,17024,11731,17025, 265,17045, + 1, 15, 5886,17049, 5,17048, 483,17053, 413,17049, 1216,17049, + 165,17049, 5843,17049, 1011,17062,14315,17049, 17,17067, 903,17049, + 1011,17070, 1011,17048, 241,17048, 903,17048, 5241,17079, 1011,17049, + 5843,17082, 903,17082,15204,17049, 483,17049,15029,17090,15029,17049, + 483,17094, 3,17048, 483,17099, 2,17049, 3,17049, 5,17105, + 1,17106, 1,17105, 5,17110, 483,17105, 15,17115,17109,17117, +17113,17117, 1369,17105, 0, 1011, 2,17125, 7,17127, 9565,17129, + 5,17124, 9565,17133,17129,17134,17130,17133,17129,17133, 9565,17140, + 6,17133,17127,17145, 9565,17147, 9600,17125, 9,17151, 5267,17125, + 9,17155,16722,17125,10385,17125, 9,17161, 587,17125, 9565,17164, + 9,17167, 9,17165, 9565,17171, 1275,17125, 903,17175, 9,17125, + 375,17125, 587,17181, 9,17124, 5267,17185, 9600,17185, 9565,17185, + 587,17190, 587,17185, 9565,17194, 587,17124, 375,17199,13737,17125, + 9,17203, 6,17124,17176,17207,17175,17207, 903,17210, 903,17207, +17175,17214, 7,17124,10001,17219, 903,17221, 6,17125, 3,17224, +17219,17227, 903,17229, 7,17125, 88,17233, 89,17233, 265,17233, + 264,17232,17239,17241, 264,17233, 3,17233, 9,17246, 3,17232, +17237,17251, 9,17251, 2,17232,17235,17257,17249,17257,17247,17257, + 9,17263, 265,17232,17245,17267, 2,17233,17254,17271, 9,17271, +17251,17274,17251,17271, 9,17278,16531,17232, 9,17233, 3,17284, +17257,17287, 3,17285,17271,17291, 903,17233, 9565,17125, 1368,17297, + 14,17297, 2,17296, 7,17303, 2,17297, 7,17306, 7,17307, +17301,17311, 5,17297,17305,17315,17129,17315, 6,17314,17311,17321, + 6,17315,17309,17325,17303,17325,17299,17325, 587,17296, 9,17333, + 9,17297, 587,17337, 6,17297, 5,17340,17311,17343, 7,17297, + 2,17346,17325,17349,12377,17125, 9,17353,16531,17125, 6,17357, + 7,17357, 6,17356,17361,17363, 7,17356,17359,17367, 0, 903, + 767,17370, 4617,17373, 483,17375, 15,17371, 1011,17379, 767,17371, + 4588,17383, 483,17383, 4579,17386, 4579,17383, 483,17390, 375,17371, + 11,17395, 483,17370, 2265,17399, 4421,17399, 5285,17399, 11,17370, + 375,17407, 6,17370,17380,17411,17379,17411, 1011,17414, 1011,17411, +17379,17418, 7,17370, 3549,17423, 2001,17423, 8495,17423,10291,17423, + 1011,17431,14073,17423,11277,17423, 4971,17423, 5045,17423, 6,17371, + 5,17442,17423,17445, 1011,17447, 7,17371, 1011,17451, 0, 1369, + 2789,17455, 2821,17455, 2759,17455, 2687,17455, 6658,17455, 2,17455, + 6,17466, 6625,17455, 9,17470,12551,17455, 1347,17455,13808,17455, + 6685,17455,13833,17455, 4413,17455,17025,17455, 265,17487, 241,17486, +17038,17455, 903,17455, 9,17455, 6625,17496, 17,17454,12377,17501, + 11,17454,12377,17505, 803,17455, 15,17454, 5303,17511, 4927,17511, +17033,17511,16983,17511, 265,17454, 2853,17521,17082,17455, 11,17455, +12377,17526,12564,17455, 241,17455,13737,17532,17025,17532, 1011,17455, +17049,17538,12377,17455, 10,17543, 10,17542, 305,17543, 11,17543, +17547,17551, 11,17542,17545,17555,17049,17455, 1011,17558, 4,17454, + 5,17455,13737,17455, 241,17566, 0, 1275, 3,17570, 9,17573, + 4,17570, 5,17570, 9856,17579, 9805,17579, 9,17582, 9,17579, + 9805,17586, 5,17571, 3,17591, 0,17592, 0,17591, 3,17596, + 9,17591, 1275,17601,17595,17603,17599,17603, 17,17591, 9805,17609, + 1369,17590, 17,17613, 1486,17571, 17,17617, 1369,17571,17577,17621, + 9,17623, 5,17621, 9,17627, 5,17620, 17,17631, 0, 17, + 4,17634, 587,17637, 155,17635, 1011,17635, 1369,17634,12377,17645, +16990,17635, 587,17635,12377,17650,12566,17635, 903,17635,16937,17656, +12377,17635, 587,17660,16937,17635, 903,17664, 3,17634, 483,17669, + 2,17635, 4,17673, 0,17674, 0,17673, 4,17678, 587,17673, + 17,17683,17677,17685,17681,17685, 3,17635, 1369,17691, 0, 15, + 5,17695, 7,17696, 1369,17694, 5303,17701, 4927,17701,16983,17701, +17033,17701, 3,17694, 3,17695,10104,17712,10105,17713,17715,17717, +10104,17713,10105,17712,17721,17723, 1, 13, 113,17727, 0, 12, +17727,17731, 9,17732, 2,17735, 2,17734, 137,17732, 136,17732, + 2,17733,17741,17745, 3,17733,17739,17749,17743,17749, 2,17732, + 9,17754,17749,17757,17749,17755, 3,17732,17737,17763,17745,17763, + 0, 13, 1, 12, 1369,17771, 33,17771, 3,17770, 8251,17777, + 3807,17777, 9875,17777, 8165,17777, 3,17771, 8241,17787,17769,17771, + 9,17791, 2,17792, 2,17793, 137,17791, 136,17791, 2,17791, + 9,17802, 3,17791,17797,17807, 2,17790,17807,17811,17799,17811, + 3,17790,17805,17817,17795,17817,17803,17817,17801,17817, 1, 413, + 4,17826, 2,17826, 5,17826, 6,17833, 6,17832, 3,17826, + 6,17839, 6,17838, 2653,17827, 1637,17827,14537,17827,14321,17827, + 260,17826, 2903,17827, 1639,17827, 1641,17827, 3361,17827, 331,17826, +17521,17827, 4909,17827,14593,17827, 261,17826, 257,17826, 14,17826, + 1274,17826, 2159,17827, 2161,17827, 251,17827, 2647,17827, 331,17885, + 15,17826, 1275,17826,14652,17827, 6,17827,17829,17895,17831,17895, + 8997,17895,17863,17895,17871,17895,17889,17895,17891,17895, 7,17827, +17833,17911,17839,17911, 8495,17911, 413,17910, 0,17919,17837,17911, +17843,17911, 265,17911,17921,17927, 1,17927,17919,17931, 2647,17927, + 8717,17911, 3785,17911,17853,17911,17875,17911,17877,17911,14315,17910, + 261,17911, 7,17826,17835,17951,17841,17951, 8647,17951, 251,17951, + 6,17826, 5,17960,17911,17963, 3,17960,17911,17967,15029,17827, + 265,17971,17895,17973, 7,17971,14315,17827, 6,17978, 7,17978, + 6,17979,17983,17985, 7,17979,17981,17989, 0, 412, 9036,17993, + 9099,17993,17829,17993,17895,17998,17831,17993,17895,18002,17896,17993, +17898,17993,17913,17993,17915,17993, 446,17993, 4371,18015, 4379,17993, +17900,17993,17917,17993, 4360,17993, 3429,17993,17882,17993, 7039,17993, +17958,17993,17949,17993, 241,17993, 445,18036, 4371,18039, 445,18037, + 4349,18043,16097,17993,12289,17993, 8647,17993, 8997,18050, 4349,17993, + 251,18054,17951,17993, 251,18058, 251,17993, 4349,18062,17827,18062, +17951,18062,17895,17993,17829,18070,17831,18070, 8997,18070, 5521,17993, + 375,18079, 445,17993, 241,18082, 4371,18085, 3,18083, 2,18082, +18089,18091, 2,18083, 3,18082,18095,18097, 465,17993, 5,18101, + 4,18100,18103,18105, 4,18101, 5,18100,18109,18111, 9082,17993, + 8495,18115,17910,17993,17833,18119,17839,18119, 8495,18119, 375,17993, + 7,17993, 8495,18129, 8997,18131,17827,18129,17829,18135,17831,18135, + 8997,18135, 8997,18128, 8495,18143,17827,18128,17833,18147,17839,18147, + 8495,18147, 8997,17993, 8647,18154,17895,18154, 7,18154, 8495,18161, +17827,17993, 241,18164, 6,18167, 6,18166, 265,18165,17895,18173, + 261,18164, 375,18165, 260,18164, 251,18164, 6,18165,18177,18185, + 7,18165,18171,18189,18181,18189, 6,18164,18173,18195, 241,18194, +18189,18199,18189,18195, 7,18164,17833,18205,17839,18205,18169,18205, +18185,18205, 8495,18205, 1, 843, 0, 842,18217,18219, 483,18220, + 4,18223, 4,18222, 581,18220, 580,18220, 4,18221,18229,18233, + 5,18221,18227,18237,18231,18237, 4,18220, 483,18242,18237,18245, +18237,18243, 5,18220,18225,18251,18233,18251, 0, 413,15309,18257, + 265,18258,15363,18261,15424,18257,15363,18265,13375,18257,13381,18257, + 4888,18257,13162,18257, 251,18256, 375,18256, 265,18257,15309,18281, +15383,18283,15309,18280,15363,18287, 331,18257, 2663,18257, 251,18293, + 9,18256, 483,18256, 375,18257, 4863,18300,13119,18300, 4863,18257, + 375,18306,13119,18257, 375,18310, 6,18257, 264,18314, 7,18257, + 264,18318, 264,18319, 265,18319,18321,18325, 265,18318,18323,18329, + 8647,18319, 375,18319, 331,18319, 7,18256, 1689,18339, 1705,18339, + 2701,18339,18317,18339, 8717,18339,12755,18339, 7627,18339, 7895,18339, + 3785,18339,13443,18339, 7287,18339, 265,18339, 331,18339, 8495,18339, + 375,18339,12745,18339, 457,18371, 375,18371, 6,18256,18291,18377, +12745,18257,17895,18381, 8647,18381, 7,18381, 8495,18387, 1, 412, + 1679,18391, 448,18391, 4349,18395, 4351,18391, 4390,18391,18300,18391, + 4457,18391,18277,18391, 1673,18391,18279,18391, 1789,18391,18364,18391, +18335,18391, 3427,18391, 7037,18391, 265,18391, 445,18422, 4349,18425, + 445,18423, 4371,18429,16095,18391,12287,18391, 5189,18391,18299,18391, +18297,18391, 2153,18391, 2151,18391, 4371,18391, 331,18446,18339,18391, + 331,18450, 331,18391, 4371,18454,18339,18454, 445,18391, 265,18460, + 4349,18463, 5,18461, 4,18460,18467,18469, 4,18461, 5,18460, +18473,18475, 465,18391, 3,18479, 2,18478,18481,18483, 2,18479, + 3,18478,18487,18489, 8386,18391,18318,18391, 375,18391,18257,18496, + 7,18391, 265,18501,18257,18503, 8337,18500,18257,18500, 8337,18391, + 7,18510, 8861,18391, 6,18515, 7,18515, 6,18514,18519,18521, + 7,18514,18517,18525,18257,18391, 265,18528, 241,18529, 6,18532, + 6,18533, 375,18528, 261,18529, 260,18529, 251,18529, 6,18529, + 241,18546,18531,18547, 7,18529, 8495,18553,18537,18553, 6,18528, +18553,18559,18541,18559, 7,18528,18549,18565,18535,18565,18547,18565, +18543,18565, 0, 843, 1, 842, 5,18576,18575,18577, 483,18581, + 4,18582, 4,18583, 581,18581, 580,18581, 4,18581, 483,18592, + 5,18581,18587,18597, 4,18580,18597,18601,18589,18601, 5,18580, +18595,18607,18585,18607,18593,18607,18591,18607, 1, 1217,16117,18617, +12291,18617,16103,18617, 4959,18617,17399,18617, 2205,18617, 2207,18617, + 1269,18617, 5,18633, 4,18632,18635,18637, 4,18633, 5,18632, +18641,18643, 1259,18617, 2,18647, 3,18646,18649,18651, 3,18647, + 2,18646,18655,18657, 7,18617, 0, 1216, 9,18662,18617,18665, + 483,18662,18617,18669,18660,18663, 7,18663,18617,18674,18617,18663, + 6,18679, 7,18679, 6,18678,18683,18685, 7,18678,18681,18689, + 1, 1531, 4904,18693,17609,18693,17586,18693, 4903,18693, 9,18700, +17579,18693, 9,18704, 9,18693, 4903,18708,17579,18708, 5,18692, + 0, 1530,18693,18717, 4,18719, 5,18719, 4,18718,18723,18725, + 5,18718,18721,18729, 1, 19, 1369,18733, 3,18733, 3,18732, + 9875,18739, 0, 18,18733,18743, 2,18745, 3,18745, 2,18744, +18749,18751, 3,18744,18747,18755, 0, 1217,12293,18759,16105,18759, + 1269,18759, 2,18765, 3,18764,18767,18769, 3,18765, 2,18764, +18773,18775, 1259,18759, 5,18779, 4,18778,18781,18783, 4,18779, + 5,18778,18787,18789, 375,18759, 7,18759, 1, 1216, 4,18796, +18759,18799, 3,18796, 6,18803, 6,18802, 5,18796, 6,18809, + 6,18808, 2,18796,18759,18815, 5241,18797, 14,18796, 1274,18796, + 2157,18797, 2155,18797,18795,18797,18799,18829,18815,18829, 15,18796, + 1275,18796, 6,18796, 3,18838, 5,18838, 7,18796,18805,18845, +18811,18845,18794,18797,18803,18851,18809,18851, 6,18797,18800,18857, +18816,18857,18799,18857,18759,18862,18815,18857,18759,18866,18835,18857, +18837,18857,18759,18857,18799,18874,18815,18874, 7,18797,18841,18881, +18843,18881,18803,18881,18759,18887,18809,18881,18759,18891,18807,18881, +18813,18881,18821,18881,18823,18881,18759,18880,18803,18903,18809,18903, +18759,18797, 6,18909, 7,18909, 6,18908,18913,18915, 7,18908, +18803,18919,18809,18919,18911,18919, 0, 1531, 1, 1530, 6717,18929, + 9,18931,18927,18929, 4,18935, 5,18935, 4,18934,18939,18941, + 5,18934,18937,18945, 0, 19, 1, 18,18949,18951, 2,18953, + 3,18953, 2,18952,18957,18959, 3,18952,18955,18963, 0, 2, +11303,18967,11349,18968, 5071,18967, 5097,18972,11350,18967, 5098,18967, +11304,18967,11307,18981, 5072,18967, 5075,18985,11315,18967, 5079,18967, +13961,18967, 581,18993, 4,18992,18995,18997, 5,18992, 580,18993, +19001,19003, 5,18993, 4,18993, 483,19008,19001,19011, 483,18993, + 4,19014,19001,19017, 4,19015,19007,19021, 1181,18966, 1206,18966, + 3451,18967, 375,19029, 1967,18967, 375,19033, 567,18966, 4992,19037, + 9,19037, 4971,19040, 4971,19037, 9,19044, 1141,18966, 555,18966, + 981,18967,16129,18966,16143,19055, 9523,18967, 483,19059, 9525,19061, + 1007,18967, 1297,18966, 524,18966, 0,19069, 4,19070, 4,19069, + 0,19074, 97,18967,11067,18967, 1275,19080, 7947,19083, 1205,18967, + 571,18967,11070,18967, 7947,19091,10043,18967, 1275,19094,10041,19097, +10044,18967,10041,19101, 1206,18967, 3001,19105, 1901,19105, 30,18967, + 8165,19111, 8150,18967, 33,19115, 1181,18967, 3663,19119, 1997,19119, + 3069,19119, 1923,19119, 9769,18967, 3972,18967, 8124,18967, 9357,18967, + 9346,18967, 8782,18967, 4244,18967, 4178,18967, 9768,18967, 8804,18967, + 8245,18967, 8249,18967, 8247,18967, 6950,18967,12299,18967, 4652,18967, +10684,18967,14096,18967, 4992,18967, 203,18967,11731,19167,17771,18967, + 113,19171,14223,18967, 85,18967,11731,19177,11031,18967, 113,19181, + 107,18967,11731,19185,11699,18967, 113,19189, 8701,18967, 9293,18967, + 3933,18967, 9,19196, 8109,18967, 9,19200, 4213,18967, 9,19204, + 4147,18967, 9,19208, 8757,18967, 9,19212, 9335,18967, 9,19216, +10202,18967, 7947,19221,17637,19221, 578,18967, 4935,19227,11349,18967, +11303,19230, 5097,18967, 5071,19234, 8647,18967, 11,19239,10603,18967, + 375,19243,10427,19243,10609,19243, 7,19243,10607,19251, 9,19242, + 4625,18967, 375,19257, 75,19257, 4629,19257, 7,19257, 4627,19265, + 9,19256,11277,18967, 10,19270,11307,19273, 10,19271,11349,19277, + 483,19271, 5045,18967, 10,19282, 5075,19285, 10,19283, 5097,19289, + 483,19283, 6789,18967, 9,19294, 288,18967, 525,18967,19073,19301, +19077,19301, 1811,19301, 1877,19301, 5,19301,17563,19301, 4861,19301, + 1290,18967, 5,19317, 3721,19317, 1985,19317,17563,19317, 4861,19317, + 296,18967, 8861,19329, 12,18967, 8165,19333, 18,18967, 25,18967, + 9,19338, 8165,19341, 8165,19339, 9,19345, 7971,18967, 9,19348, + 33,19351, 33,19349, 9,19355, 47,18967,11731,19359,10107,18967, + 1275,19362, 7947,19365,17637,19365, 113,19363, 59,18967,17635,19373, +10291,18967, 165,19377, 375,18967, 510,18967, 11,18967, 8165,19385, + 9,19387, 265,19384, 9,19384, 8165,19393, 9,18967, 567,19397, + 4971,19399, 6,19397, 25,19396, 8165,19405, 7971,19396, 33,19409, + 3933,19396, 8109,19396, 4213,19396, 4147,19396, 9335,19396, 8757,19396, + 8495,19396, 6789,19396, 4625,19396,10603,19396, 4971,19396,14073,19396, + 567,19396, 4935,19437, 11,19397, 8109,19441, 3933,19441, 8495,19441, + 7971,19441, 265,19396, 8861,19451, 11,19396, 8165,19455, 483,19396, + 17,18967, 1275,19460, 4861,19463,17563,19463, 3721,19463, 1985,19463, + 15,19460, 15,18967, 9731,19474, 1011,19475, 483,19475, 17,19474, + 6515,18967, 483,19485, 6527,19487, 6557,19487, 6561,19487,14210,18967, + 17,19495, 4971,18967, 9,19498,14073,18967, 9,19502, 5921,18967, + 375,19507,10046,18967, 16,18966, 0,19513, 4,19514,19317,19517, +19463,19517, 4,19513, 0,19522,19317,19525,19463,19525, 8495,19513, +10351,19513, 4311,19513, 4329,19513, 8717,19513,10055,19513, 3785,19513, + 14,18966, 6,19545, 2,19546, 2,19545, 6,19550, 482,19545, + 17,19545, 10,18966,17777,19559, 2215,19559,11307,19559, 5075,19559, +11713,19559, 2221,19559,11087,19559,10439,19559,10665,19559, 4643,19559, +11037,19559, 2233,19559, 9195,19559,11277,19559, 5045,19559,19455,19559, +19333,19559,19393,19559,19385,19559, 9,19597, 9317,19559, 9755,19559, + 4321,19559,10171,19559, 4295,19559, 3833,19559,10603,19559, 9,19613, + 4625,19559, 9,19617, 8,18967, 11,19621, 14,18967, 9731,19625, + 17,19625, 11,18966, 8124,19631, 3972,19631, 8804,19631, 8150,19631, + 7971,19631, 9,19640, 3933,19631, 9,19644, 8109,19631, 9,19648, + 8495,19631, 9,19652, 9,19631, 8109,19656, 3933,19656, 8495,19656, + 7971,19656, 9,18966,10001,19667, 5,19669, 4,19668,19671,19673, + 4,19669, 5,19668,19677,19679, 16,18967, 7,19683, 10,18967, +11277,19686,11307,19689, 5045,19686, 5075,19693,11349,19687, 5097,19687, + 9195,19687,19656,19687,19441,19687,19631,19687, 9,19706,11277,19687, +11303,19711, 5045,19687, 5071,19715, 9,19687,19631,19718, 19,18967, + 13,18967,19631,19725,10385,18967, 101,19729, 128,19729, 9,19729, + 113,19734, 483,19729,10081,19739,11759,19739,10405,19739, 113,19729, + 9,19746, 67,18967,11613,19751,12216,19751, 9,19751,11731,19756, +11731,19751, 9,19760, 566,18967, 253,19765, 1471,19765, 53,19765, + 256,18967, 629,18967, 1413,18967, 1471,19777, 1346,18967, 340,18967, + 8165,19783, 567,18967, 4935,19787, 9,19789, 9,19786, 4935,19793, + 305,18967, 959,18967,11731,19799, 759,18967,11731,19803, 331,18967, +17635,19807, 1325,18967,17635,19811,10001,18967,10003,19815, 4,19817, + 4,19816,10007,19815, 580,19815,10006,19815, 483,19815, 4,19828, + 4,19829, 581,19815, 4,19815,10003,19836, 483,19836, 5,19815, +19819,19843,19833,19843, 4,19814,19835,19849,19823,19849,19843,19849, + 5,19814,19839,19857,19821,19857,19825,19857,19827,19857,19841,19857, +19831,19857,19837,19857, 9731,18967, 15,19872, 8495,18967, 483,19877, + 9,19876, 9195,18967, 113,19883,11731,19883, 241,18967, 7,19888, + 483,18967, 9,19892, 17,19893, 903,19897, 5,19892, 253,19901, + 1471,19901, 53,19901, 5,19893, 1505,19909, 903,18967, 7,19913, + 1275,18967,10043,19916,10041,19919,11067,19916, 7947,19923,17637,19917, +10107,19927, 0,19916, 4,19931, 0,19932,19917,19935, 7,19932, +19937,19939, 7,19931, 4,19942, 16,19931, 6,19916, 4,19949, + 6,19950,19917,19953, 1,19950,19955,19957, 1,19949, 4,19960, + 8,19916, 4,19965, 1,19966, 1,19965, 4,19970,10107,19916, + 7947,19975,17637,19975, 7947,19917,11067,19981,10107,19981, 9,19917, + 2009,19987, 3135,19987,19969,19987,19973,19987, 5231,19987,19957,19987, +19963,19987, 0,19987, 5175,19987,14065,19987, 17,19916,19525,20009, +19517,20009, 3721,20009, 1985,20009, 1,20009,19987,20019,20003,20009, + 4861,20009,17563,20009, 5,19916, 4,19917, 5,19917, 265,18967, +17635,20035, 5771,20035, 9,20035, 11,20034, 11,20035, 9,20034, + 8861,20047, 483,20035, 7,20034, 8165,20053, 7,20035, 8495,20057, + 587,18967, 7,20061, 4,20063, 4,20062, 16,20061, 17,20061, + 4,20060,20071,20073, 5,20060,20067,20077,20069,20077, 4,20061, + 7,20082,20077,20085, 5,20061,20065,20089,13947,18967, 5,20092, + 17,20095, 5,20093, 9,20099, 4,18966, 8770,20103, 8729,20103, + 8697,20103, 8784,20103, 4266,20103, 9262,20103, 2,20103, 7,20117, + 8684,20103, 8717,20121, 331,20121, 8647,20103, 8757,20126, 8757,20103, + 8647,20130, 375,20130, 4213,20103, 375,20136, 9750,20103,19687,20103, + 347,20103, 386,20103, 340,20103, 331,20149, 375,20103, 8757,20152, + 4213,20152, 9195,20152, 305,20152, 9,20103,10291,20103, 483,20165, + 59,20103, 15,20103, 9195,20103, 375,20172, 331,20103,10001,20103, + 9,20179, 305,20103, 375,20182, 7,20103, 8495,20186, 8717,20189, + 331,20189, 8495,20187, 8757,20195, 9731,20186, 265,20187, 8757,20201, + 4213,20201, 9195,20201, 305,20201, 265,20186, 331,20211, 8647,20187, + 1325,20103, 7,20102, 8495,20219,10351,20219, 4311,20219, 4329,20219, + 3785,20219,10055,20219, 8717,20219, 483,20103, 1275,20103, 265,20103, + 7,20239, 8495,20241, 375,20241, 7,20238, 331,20247, 9731,20103, + 7,20250, 8495,20103, 7,20254, 8717,20257, 331,20257, 483,20255, + 9,20255, 7,20255, 8647,20267, 375,20267, 5,18966, 9535,20273, + 9537,20273, 2,20273, 6,20278, 9559,20273, 482,20273, 9563,20273, + 9362,20273, 922,20273, 1413,20273, 19,20273, 12,20273, 8150,20273, + 8804,20273,19385,20273, 1290,20273, 9533,20305, 525,20273, 9533,20309, + 7971,20273, 9,20312, 11,20273, 9,20316, 17,20273, 1275,20320, + 9533,20323, 9,20273, 9195,20326, 11,20326, 7971,20326, 8495,20326, + 9195,20273, 9,20336, 6,20273, 5,20341, 2,20340, 5,20340, +18967,20347, 11,20341, 7,20273, 5,20353, 6,20272, 17,20357, + 7,20272, 9195,20361,11277,20361, 5045,20361, 9755,20361, 4295,20361, +10171,20361, 4321,20361, 3833,20361, 9317,20361, 483,20273, 903,20380, + 1275,20273, 17,20384, 9533,20387, 903,20273, 483,20390, 8495,20273, + 9,20394, 9731,20273, 6,20398, 7,20398, 6,20399,20403,20405, + 7,20399,20401,20409, 6,18966,19836,20413,19857,20415,19854,20413, +19871,20413,19815,20413, 4,20423,19843,20425, 4,20422,19857,20429, +19477,20413,19145,20413,19875,20413,19843,20413,19849,20438,19849,20413, +19843,20442,19718,20413,19473,20413,19483,20413,19725,20413, 19,20413, + 12,20413,19337,20413,19687,20413, 9,20460,19475,20413, 9731,20465, + 17,20465, 9,20413,19687,20470, 11,20470, 11,20413, 9,20476, + 8495,20413, 5,20413, 9,20483, 4,20413,19815,20487,19849,20489, +19815,20486,19857,20493, 5,20412, 17,20497, 7,18966,10439,20501, +11087,20501,10665,20501, 4643,20501,11307,20501, 5075,20501,17777,20501, + 2215,20501,18739,20501, 4339,20501,11713,20501, 2221,20501,11037,20501, + 2233,20501,19626,20501,19129,20501, 0,20501, 4,20534,19301,20537, +19317,20537,19463,20537,20009,20537,10036,20501,19843,20547, 8800,20501, + 8714,20501, 5045,20501,11277,20501, 4551,20501,19628,20501,19455,20501, +19393,20501,19723,20501,19333,20501, 305,20501, 8495,20570, 959,20501, +19625,20501, 9731,20576, 17,20576,10603,20501, 9,20583, 4625,20501, + 9,20587,19385,20501, 9,20591, 759,20501, 47,20501,10107,20501, + 241,20599, 265,20598,10196,20501, 11,20501, 8495,20606, 17,20501, +19625,20610, 59,20501,10291,20501, 1011,20617, 15,20501, 1325,20501, + 8495,20501, 10,20624, 10,20625, 305,20624, 11,20624,20629,20633, + 11,20625,20627,20637, 9731,20501,19625,20640, 4,20501, 7,20645, + 483,20647,18967,20649, 0,20644,19301,20653,19317,20653,19463,20653, +20009,20653,20651,20653, 7,20644,19916,20665,20653,20667, 1275,20665, +18967,20670,20653,20673,18967,20665, 1275,20676,20653,20679,10001,20644, +19843,20683, 1011,20645,10047,20645, 5,20501, 9287,20691, 331,20501, + 4,20500, 8495,20697, 4311,20697,10351,20697, 3785,20697, 4329,20697, + 8717,20697,10055,20697, 5,20500, 9195,20713,11277,20713, 5045,20713, + 4321,20713, 9755,20713, 4295,20713,10171,20713, 9317,20713, 3833,20713, + 903,20501, 265,20501,10107,20734, 9195,20501, 241,20739,10001,20501, + 4,20742,19843,20745, 5,20742,18967,20748, 4,20743,20751,20753, +19511,20753,19857,20753, 5,20743, 7,18967, 8810,20762, 8811,20762, + 2,20763, 1,20768,20483,20763, 8495,20773, 8800,20763,20273,20763, + 8495,20763, 11,20780, 8810,20763,20767,20785,10081,20763, 1011,20789, + 1010,20788,20791,20793, 1010,20789, 1011,20788,20797,20799, 566,20763, + 11,20763, 9731,20805, 8495,20805, 8495,20804, 265,20763,20482,20763, + 17,20815, 567,20763, 9195,20819, 9,20763, 8811,20763,20765,20825, + 241,20762, 265,20762, 8165,20831, 1011,20763, 375,20763, 4,20762, +20815,20839, 5,20762,11276,20842, 5044,20842,11277,20842,11276,20843, +20849,20851, 5045,20842, 5044,20843,20855,20857,20773,20843,20470,20843, +20103,20843,20413,20843, 9,20866, 9195,20843,11277,20843,20845,20873, + 5045,20843,20847,20877, 9,20843,20413,20880, 4,20763,20480,20885, + 7,20884,20843,20889, 9366,20885, 1,20885, 3,20894,20842,20896, +20843,20897,20899,20901,20889,20900,20842,20897,20843,20896,20907,20909, +20890,20897,20889,20897,20843,20914, 3,20885, 1,20918,20842,20920, +20843,20921,20923,20925,20889,20924,20842,20921,20843,20920,20931,20933, +20890,20921,20889,20921,20843,20938, 9195,20885, 483,20942,20866,20885, + 8495,20885,20413,20948, 483,20885, 9195,20952,20413,20885, 8495,20956, +20843,20956,20843,20885, 1,20962, 3,20964, 3,20962, 1,20968, + 7,20963,20897,20973,20921,20973,20967,20973,20971,20973,20413,20962, + 5,20763, 7,20985, 17,20985,20413,20989,20413,20984,20839,20993, + 17,20993, 483,20984,20839,20985, 1,21001, 3,21002, 3,21001, + 1,21006, 7,21000,21005,21011,21009,21011,20413,21001,20413,20763, + 5,21019,20885,21021, 9,21021, 5,21018,20839,21027, 17,21027, + 483,20763, 5,21032, 5,18967, 0,21037, 3,21038, 9357,21037, +20742,21037,20761,21045,20187,21037, 8495,21049,20501,21037,10001,21036, +20753,21055,20763,21037, 8495,21059,13947,21036, 17,21063, 256,21037, +20186,21037,20885,21037, 7,21071,20897,21073,20921,21073, 9,21037, + 1275,21079, 9533,21081,21041,21081, 17,21037, 9195,21087, 7971,21087, + 8495,21087, 483,21036, 253,21095, 1471,21095, 53,21095, 1275,21036, + 6,21036,20640,21105,20610,21105,21069,21105, 9731,21105,20501,21112, +20501,21105, 9731,21116, 17,21116, 17,21105,20501,21122, 7,21036, +11276,21126, 5044,21126,11277,21126,11276,21127,21133,21135, 5045,21126, + 5044,21127,21139,21141, 1,21127, 3,21144,18967,21147, 3,21127, + 1,21150,18967,21153,18967,21127,20470,21127,21049,21127,20413,21127, + 9,21162,20103,21127, 9195,21127,11277,21127,21129,21171, 5045,21127, +21131,21175, 9,21127,20413,21178, 6,21037,21149,21183,21155,21183, +20254,21183,19243,21183,19257,21183,18967,21183, 1,21183, 3,21196, +21126,21198,21127,21199,21201,21203,21126,21199,21127,21198,21207,21209, + 3,21183, 1,21212,21126,21214,21127,21215,21217,21219,21126,21215, +21127,21214,21223,21225, 8495,21183,20103,21228,19687,21183,20843,21183, +21166,21183, 11,21183,20501,21239,20103,21183, 8495,21242,21127,21242, +21127,21183,20103,21248,20763,21183, 9731,21253, 17,21253, 7,21037, +19625,21259, 15,21259,20413,21263,20103,21258,21105,21267, 241,21258, +21105,21259,20103,21273,20103,21037, 7,21277,21199,21279,21215,21279, +11277,21279, 5045,21279,21183,21279, 6,21276, 1,21291, 3,21292, +21279,21295, 3,21291, 1,21298,21279,21301, 7,21276,21105,21305, + 241,21037, 483,21309, 7,21308, 4,18967,21183,21315, 0,21314, +20309,21319, 524,21319,20273,21323,20305,21319, 1291,21319,20273,21329, +21081,21319,20323,21319,20387,21319, 11,21319, 483,21338,20273,21341, + 483,21319, 11,21344,20273,21347, 165,21315,17635,21315, 1011,21352, +17642,21315,20413,21315, 6,21359, 9,21315,20352,21315,11277,21365, + 5045,21365,20985,21315, 11,21315,10075,21315, 483,21315, 1347,21315, + 6,21314, 1,21381, 3,21382,21365,21385, 3,21381, 1,21388, +21365,21391,20273,21381, 11,21381, 7,21314, 6,21315, 17,21401, + 7,21315,21384,21404,21390,21404,21385,21405,20273,21411,21407,21411, +21391,21405,20273,21417,21409,21417,21385,21404,21384,21405,21423,21425, +21391,21404,21390,21405,21429,21431,11276,21404, 5044,21404,11276,21405, +11277,21404,21439,21441, 5044,21405, 5045,21404,21445,21447, 0,21405, + 4,21450, 4,21405, 0,21454, 8495,21405, 5045,21405,20273,21461, +21437,21461,11277,21405,20273,21467,21435,21467, 9,21405,20273,21404, +21385,21475,21391,21475,11277,21475, 5045,21475, 265,21405, 483,21405, +18967,21487,21453,21489,21457,21489,21381,21405,20273,21315, 6,21497, + 1,21499, 3,21500, 3,21499, 1,21504, 7,21496,21503,21509, +21507,21509,21385,21509,21391,21509,11277,21509, 5045,21509, 1011,21315, +17635,21522, 6,18967, 2,21526, 11,21529,20103,21527,20690,21527, + 9,21535,21259,21527, 9,21539, 17,21527, 4,21526, 11,21545, + 5,21526,20640,21549,21259,21549,20610,21549, 9731,21549,20501,21556, +20501,21549, 9731,21560, 17,21560, 17,21549,20501,21566, 4,21527, + 15,21571, 5,21527, 482,21575, 2,21575, 6,21578, 6,21575, + 2,21582, 9,21575,20501,21587,20501,21574, 9,21591,20501,21527, + 5,21595, 17,21597, 5,21594, 9,21601, 1, 3,19718,21604, +19719,21604,19402,21605,20962,21604,20973,21613,21248,21604,21272,21604, +21494,21604,20963,21604,21249,21604,21001,21604,21011,21627,21273,21604, +21495,21604,21178,21604,20880,21604,21179,21604,20881,21604,21049,21604, +20773,21604,21361,21604,15753,21605, 803,21649,14387,21605, 375,21653, + 9663,21604, 9177,21604,20351,21604,20485,21604,21360,21604, 990,21604, + 4745,21667,19685,21605,21087,21671,21156,21605,21087,21675,21317,21605, +21291,21604,21279,21681,21499,21604,21509,21685, 996,21604, 4745,21689, +21472,21604,21473,21604, 761,21605, 3419,21697, 6469,21697, 3119,21697, +21048,21604,20772,21604, 973,21604, 955,21708, 4745,21711, 9,21708, + 4745,21715, 4745,21709, 955,21719, 9,21719, 2077,21605, 7,21725, + 14,21724,21727,21729, 6,21725, 15,21724,21733,21735,20350,21604, +20484,21604, 9662,21604, 9176,21604,21380,21604,21104,21604,21126,21604, +21183,21751, 9,21751,20842,21604,20885,21757, 9,21757, 305,21604, +18967,21763, 375,21765, 955,21604, 973,21769, 4805,21771, 973,21768, + 4745,21775,17423,21769, 4745,21769, 713,21604,21404,21604,21381,21785, + 9,21785,20884,21604,21316,21605,21182,21604,20501,21795,21793,21795, +21405,21604,21381,21800, 9,21800,20885,21604,20843,21806,20890,21807, +20889,21807,20843,21812,20973,21807,21073,21807,20413,21807,20843,21807, +21757,21823,20889,21822,21183,21604,21127,21828,21279,21829,21679,21829, +20103,21829,21127,21829,21751,21839,21105,21604,21381,21604,21365,21845, +21405,21844,20413,21845,21475,21845,21405,21845,21785,21855,20273,21855, +21509,21845,20843,21604,20885,21862, 9,21862,21127,21604,21183,21868, +18967,21869,21183,21873, 9,21868, 1199,21605,13923,21604,13935,21881, + 9437,21605, 587,21885, 9439,21887, 1177,21605, 1421,21604, 668,21604, + 1,21895, 5,21896, 5,21895, 1,21900, 117,21605,11759,21605, + 1369,21906, 7971,21909,11760,21605, 7971,21913, 809,21605, 989,21605, + 9929,21605, 1369,21920, 9927,21923, 9930,21605, 9927,21927,19596,21605, + 9,21931,19556,21605,20170,21605,19707,21605, 9,21939,20956,21605, +21791,21943,21242,21605,21795,21947,20773,21605,21707,21951,21049,21605, +21705,21955,21373,21605,20464,21605,19557,21605,20499,21605,20359,21605, + 9181,21605, 9671,21605, 4145,21605, 3801,21605, 4135,21605,10101,21605, +20577,21605,19656,21605,19441,21605,19332,21605,19392,21605,19454,21605, +21059,21605,19440,21605,19657,21605, 4134,21605,10100,21605,20498,21605, +20358,21605,20142,21605, 9,22007,20576,21605,20864,21605, 9,22013, +21116,21605,21166,21605,21183,22019, 9,22019,21560,21605, 9180,21605, + 9670,21605, 3800,21605, 4144,21605,21026,21605,20839,22035,20814,21605, +20839,22039,20992,21605,20839,22043,21359,21605,21845,22047,21647,22047, +21053,21605,21795,22053,20779,21605, 8087,21605, 8091,21605, 8089,21605, +20303,21605, 9,22065,20465,21605,10786,21605,11131,21605, 4696,21605, +11970,21605,19722,21605,19333,21605,19393,21605,19455,21605,20351,21605, +21739,22087,20485,21605,21741,22091, 9663,21605,21743,22095, 9177,21605, +21745,22099,20350,21605,21661,22103,20484,21605,21663,22107, 9662,21605, +21657,22111, 9176,21605,21659,22115,10099,21605, 17,22119, 17,22118, + 4133,21605, 17,22125, 17,22124, 8251,21605, 9,22131, 3807,21605, + 9,22135, 13,21604,18967,22139, 19,21604,20501,22143, 9875,22143, + 12,21604, 8251,22149, 3807,22149,19559,22149,20501,22149, 9875,22149, + 8165,22149, 18,21604, 3799,21605, 17,22165, 17,22164, 4143,21605, + 17,22171, 17,22170, 4283,21605, 9,22177, 4279,21605, 9,22181, + 9669,21605, 17,22185, 17,22184, 9179,21605, 17,22191, 17,22190, + 9673,21605, 9,22197, 9967,21605, 9,22201,19624,21605,20501,22205, + 17,22205,19686,21605,22149,22211,19441,22211,19656,22211,21607,22211, +19631,22211, 9,22220, 9,22211,19631,22224,19559,21605,19385,22228, + 9,22231, 9,22229,19545,21605, 17,22236, 17,22237,10102,21605, + 7971,22243,17053,22243, 629,21605,21117,21605,21561,21605,20867,21605, +20885,22255,21167,21605,21183,22259,21548,21605, 17,22263,20501,22263, +21104,21605,21069,22269,21267,22269,21305,22269,21259,22269,20103,22277, + 17,22269,20501,22269,20838,21605,20985,22285,20842,21605,21865,22289, +21809,22289,21613,22289,21637,22289,21867,22289,20773,22289,20956,22289, +21863,22289,20885,22305, 9,22305,20885,22289,20413,22310,20413,22289, +20885,22314,21126,21605,21871,22319,21831,22319,21617,22319,21635,22319, +21877,22319,21049,22319,21242,22319,21869,22319,21183,22335, 9,22335, +21183,22319,20103,22340,20103,22319,21183,22344,21020,21605,20885,22349, +21278,21605,21183,22353,21052,21605,21829,22357,21127,22357,21358,21605, +21747,22363,21665,22363,21532,21605,20497,21605, 17,22371, 17,22370, +20361,21605, 9,22377,20713,21605, 9,22381,20357,21605, 17,22385, + 17,22384,21048,21605,21643,22391,20772,21605,21645,22395,21509,21605, +21381,22399,21499,22399, 9,22399,21475,21605,21381,22407, 9,22407, +21365,21605,21381,22413, 9,22413,11971,21605, 4697,21605, 8387,21605, + 9,22423,11949,21605, 11,22426, 5,22428, 6,22426, 5,22432, + 5,22426, 6,22436, 6,22437, 7,22426,22441,22443, 7,22427, +22435,22447,22439,22447,22431,22447, 14,22426,22447,22455, 6,22427, + 15,22426,22459,22461, 9,22426, 9,22427, 4669,21605, 11,22468, + 5,22470, 6,22468, 5,22474, 5,22468, 6,22478, 6,22479, + 7,22468,22483,22485, 7,22469,22477,22489,22481,22489,22473,22489, + 14,22468,22489,22497, 6,22469, 15,22468,22501,22503, 9,22468, + 9,22469,12345,21605, 5,22510, 4,22510, 12,22510, 13,22511, +22517,22519, 12,22511, 13,22510,22523,22525, 4,22511,22513,22529, + 5,22511,22515,22533, 5123,21605, 5,22536, 4,22536, 12,22536, + 13,22537,22543,22545, 12,22537, 13,22536,22549,22551, 4,22537, +22539,22555, 5,22537,22541,22559,10697,21605, 9,22562,19385,21605, +19559,22566, 9,22569, 9,22567,19559,22573,19397,21605, 6,22576, + 11,22577, 11,22576,19475,21605,20413,22584, 1406,21605, 1396,21605, + 525,21605, 903,22593, 934,21605, 9,22597, 1290,21605, 903,22601, + 669,21605,21899,22605,21903,22605, 1861,22605, 1887,22605, 4,22605, +17013,22605, 4917,22605,19687,21605,22139,22621,21609,22621, 9,22621, +20103,22620, 9,22629,19625,21605,20501,22632,19384,21605,21087,22637, +20326,22637, 9,22636,20273,22637, 9,22644, 9,22637,20273,22648, +19396,21605, 5,22652, 10,22652,19441,22657, 10,22653,19455,22661, + 11,22652,22661,22665, 11,22653,22657,22669,19474,21605,20413,22673, + 1418,21605, 4,22677, 3755,22677, 1995,22677,17013,22677, 4917,22677, + 12,21605,22139,22689,18967,22688, 18,21605,22143,22695,21252,21605, +21279,21605,21183,22701,21291,22701, 9,22701,20483,21605,20763,22708, +20187,21605,21036,22712,21069,22715,21036,22713,21049,22719,21037,22713, +21037,22712, 9,22713,20353,21605, 5,22729,21315,22729, 33,21605, + 8150,22735,17727,22735, 9,22735, 7971,22740, 7971,22735, 9,22744, +17025,22735, 8165,21605, 30,22751, 181,22751, 9,22751, 25,22756, + 25,22751, 9,22760, 145,22751, 53,21605,17049,22767,10219,21605, + 903,22771, 155,22771,10081,21605, 1369,22776, 7971,22779,17053,22779, + 257,21605, 9,22785, 1171,21605, 4722,22789, 4713,22789, 903,22792, +21315,22789, 903,22789, 4713,22798, 628,21605, 9,22803, 11,21605, + 766,22807, 767,22807,20984,22807,20762,22806,22813,22815,20985,22807, +20762,22807,22140,22807,19718,22807,19725,22807, 586,22806,22809,22829, + 5,22807,20763,22832,22815,22835,20763,22833,22821,22839, 587,22832, +22829,22843, 587,22833, 586,22807,22847,22849, 5515,22807,17043,22807, + 4825,22807,17727,22807, 8150,22807,22139,22807,18967,22862,19396,22806, +19397,22806,19396,22807,22869,22871,19397,22807,22867,22875,22653,22875, +20763,22807, 5,22880,22815,22883,19687,22807, 9,22886, 7971,22807, + 9,22890,19461,22807, 9,22807,19687,22896, 7971,22896,18967,22896, +18967,22807,22139,22904, 9,22904,20103,22905, 9,22911, 1369,22806, + 903,22806, 9,22917, 587,22807, 5,22920,22829,22923,18967,22806, +20326,22927,21087,22927, 9,22926,20273,22927, 9,22934, 9,22927, +20273,22938, 9,22806,18967,22942,20763,22806,22819,22947, 587,22806, +22811,22951, 9,22951, 17,21605,19722,22956,19723,22957,22959,22961, +19723,22956,19722,22957,22965,22967,19396,22956,19396,22957,19397,22956, +22973,22975,19545,22956,20497,22956,20357,22956, 4133,22956,10099,22956, + 3799,22956, 4143,22956, 9179,22956, 9669,22956,19475,22957,21315,22957, +19397,22957,22971,23001, 1369,22956, 1275,22956, 903,23007, 587,22957, + 903,22957, 15,21605,20103,23014,18967,23015,20501,23019, 1369,23014, + 4917,23023,17013,23023, 3755,23023, 1995,23023,18967,23014,20413,23033, + 9,21605,19631,23036,19384,23036,19384,23037,22567,23043,19385,23036, +10697,23036, 4669,23036,11949,23036, 11,23037,23047,23055,22567,23055, +18967,23055,18967,23036, 10,23062, 10,23063, 11,23062,23067,23069, + 11,23063,23065,23073, 11,23036,18967,23076, 7049,21605, 587,23081, + 7051,23083, 7055,23083, 7059,23083,17219,21605, 903,23091, 4663,21605, + 903,23095, 4745,21605, 955,23099, 9,23099, 4569,21605,17423,21605, + 955,23107, 9,23107,14625,21605, 5997,21605, 7,23114, 5,23114, + 6,23119,23117,23121, 6,23118, 6,23114, 5,23126, 7,23115, +23129,23131,23125,23131, 14,23114,23131,23137, 6,23115, 15,23114, +23141,23143,20482,21605,20762,23146,20773,23149,20762,23147,20815,23153, +20839,23147,21399,23147,19683,23147,20763,23147,20843,23163,23149,23163, +20763,23146,20839,23169,23153,23169,20186,21605,21036,23174,21049,23177, +21036,23175,21069,23181, 331,23175, 59,23175, 1325,23175, 265,23175, +20644,21605,20352,21605, 9660,21605, 8386,21605, 59,23199, 331,23199, + 1325,23199, 265,23199,21243,21605,21829,23209,20957,21605,21807,23213, +21272,21605,21631,23217,20962,21605, 9,23221,21248,21605,21625,23225, +21549,21605,20501,23228,21105,21605,20501,23232,20843,21605,21758,23237, +21623,23237,21760,23237,21641,23237,20885,23237,21757,23246,20889,23247, +21757,23237,20885,23252, 9,23252,20103,23236, 9,23259, 9,23237, +21757,23262,20885,23236, 9,23267,21127,21605,21752,23271,21625,23271, +21754,23271,21639,23271,18967,23270,21087,23281,21183,23271,21751,23284, +21751,23271,21183,23288, 9,23288,20103,23270,21183,23295, 9,23295, + 9,23271,21751,23300,21405,21605,21633,23305,21394,23305,21786,23305, +21381,23305,20273,23312,21785,23312,20326,23305,21087,23305,21695,23305, +21037,23305,21036,23304,23325,23327,21036,23305,21037,23304,23331,23333, +21788,23305,21785,23305,21381,23338, 9,23338, 9,23305,20273,23344, +21785,23344,20273,23305,21381,23350, 9,23350,21259,21605,21843,23357, +20885,21605,20763,23360, 5,23363,18967,23362, 6,23360,18967,23368, +18967,23360, 6,23372, 6,23373, 7,23360,23377,23379, 7,23361, +23365,23383, 4,23383,23363,23387,23371,23383,23375,23383,23367,23383, +21526,23360,23383,23397, 6,23361,21527,23360,23401,23403, 9,23361, +20843,23407,21791,23361,20413,23411,20413,23360,21791,23415,20843,23360, + 9,23419,21183,21605,21315,23422,21795,23425,21795,23423,20103,23429, +20103,23422,21795,23433,20763,23422,21249,21605,21617,23439,21001,21605, + 9,23443,21273,21605,21619,23447,21371,21605, 9,23451,21253,21605, + 8,21604,22363,23457,22053,23457,21983,23457,23039,23457,21985,23457, +22077,23457,22075,23457,22465,23457,22507,23457,23053,23457,23051,23457, +20501,23457,23037,23457,19631,23483,11949,23483, 4669,23483,18967,23457, + 10,21604,22870,23493,22869,23493,22867,23493,23041,23493,22583,23493, +21995,23493,22665,23493,21991,23493,19441,23493,22626,23493,21997,23493, +19656,23493,22908,23493,22902,23493,22419,23493,22421,23493,11970,23493, + 4696,23493,21987,23493,23069,23493,21989,23493,23079,23493,22643,23493, +22933,23493,22945,23493,22693,23493,22904,23493, 9,23546,22896,23493, +18967,23550,22621,23493, 9,23554,19396,23493,22807,23558, 8647,23493, +22637,23493, 9,23565,23077,23493,18967,23569,22689,23493,18967,23573, +22927,23493, 9,23577,22943,23493,18967,23581,22807,23493,19396,23584, +19396,23585,18967,23584, 9,23590, 9,23584,18967,23594,18967,23585, + 9,23598, 9,23585,18967,23602,19631,23493, 9,23606,18967,23493, +22896,23610,22807,23610, 9,23614, 9,23610,22807,23618, 9,23493, +11949,23622, 4669,23622,22621,23622,19631,23622,22904,23622,22807,23622, +18967,23634,18967,23622,22807,23638, 4669,23493, 9,23642,11949,23493, + 9,23646, 16,21604,22979,23651,21935,23651,22239,23651,22389,23651, +22375,23651,22001,23651,21999,23651,22005,23651,22003,23651,22123,23651, +22129,23651,22983,23651,22981,23651,22987,23651,22985,23651,22989,23651, +22995,23651,22993,23651,22991,23651,22169,23651,22029,23651,22027,23651, +22175,23651,22031,23651,22189,23651,22195,23651,22033,23651, 7,23651, + 3,23706, 3,23651, 7,23710, 586,23651,22957,23651,19545,23717, +20357,23717,20497,23717,10099,23717, 4133,23717, 3799,23717, 9669,23717, + 9179,23717, 4143,23717,19559,23651,20713,23651,20361,23651, 9967,23651, + 9673,23651, 4283,23651, 3807,23651, 8251,23651, 4279,23651, 14,21604, +23175,23755,23199,23755, 5,23755, 1,23760,22677,23763,23023,23763, + 1,23755, 5,23768,22677,23771,23023,23771,16381,23755,14625,23755, + 4569,23755,13325,23755, 8685,23755,19513,23755, 1369,23755, 17,23755, +20219,23755, 4063,23755,10273,23755,20697,23755, 4137,23755, 8401,23755, + 9955,23755, 3863,23755,19631,21605, 11,23808, 5,23810, 6,23808, + 5,23814, 5,23808, 6,23818, 6,23819, 7,23808,23823,23825, + 7,23809,23817,23829,23821,23829,23813,23829, 14,23808,23829,23837, + 6,23809, 15,23808,23841,23843, 9,23808,23457,23847, 9,23809, +19667,21605, 5,23852, 4,23852, 12,23852, 13,23853,23859,23861, + 12,23853, 13,23852,23865,23867, 4,23853,23855,23871, 5,23853, +23857,23875, 8,21605,23646,23879,23642,23879,23606,23879,23851,23879, +22467,23879,22509,23879,19384,23879,19631,23879,23493,23894,23493,23879, +11949,23898, 4669,23898,19631,23898,11949,23879,23493,23906, 4669,23879, +23493,23910,18967,23879, 11,23914, 11,23915,23493,23919, 11,23879, +18967,23922, 14,21605,18967,23926,20501,23929, 17,23929,18967,23927, +20413,23935, 16,21605,22241,23939,22387,23939,22373,23939,22121,23939, +22127,23939,22167,23939,22173,23939,22187,23939,22193,23939,19545,23939, +10099,23939, 4133,23939, 3799,23939, 4143,23939, 9669,23939, 9179,23939, +20357,23939,20497,23939,19625,23939,21549,23939,21105,23939, 11,21604, +23053,23983,23051,23983,23039,23983,21985,23983,22085,23983, 5,23982, + 6,23995,21983,23983,22077,23983,22075,23983,22081,23983,22083,23983, +22572,23983,23847,23983,22465,23983,22507,23983,22211,23983, 9,24017, +19396,23983,22567,23983, 9,24022, 8165,23983, 9,24027,23037,23983, +11949,24031, 4669,24031,19631,24031,19559,23983, 9,24039, 8251,23983, + 9,24043, 3807,23983, 9,24047, 9875,23983, 9,24051,20501,23983, + 9,24055, 9,23982, 8251,24059, 3807,24059,22211,24059,19559,24059, +20501,24059, 9875,24059, 8165,24059, 9,23983,22567,24074,18967,24074, +18967,23983,21183,24081,23997,24081, 375,24081, 7,24081,23995,24089, + 9,24080, 9,21604,22869,24095,23894,24095,21997,24095,21365,24095, +21475,24095,21509,24095,21405,24094,22047,24095,22357,24095,23919,24095, +21995,24095,22583,24095,23906,24095,23910,24095,21127,24094,22319,24125, +20843,24094,22289,24129,22419,24095,22421,24095,21279,24095, 973,24094, + 4745,24139,19559,24095,20361,24095,20713,24095, 9673,24095, 9967,24095, + 4283,24095, 4279,24095, 8251,24095, 3807,24095, 33,24095, 7971,24161, + 8165,24095, 25,24165, 9875,24095,20501,24095, 4745,24095,17423,24095, +21405,24095,20273,24177,21785,24177,19631,24095,23879,24182, 4669,24095, +23879,24186,11949,24095,23879,24190, 973,24095, 4805,24195,19687,24095, +23493,24199,23879,24095,19631,24202,11949,24202, 4669,24202,20413,24095, + 11,24094, 8251,24213, 3807,24213,22211,24213,19559,24213,20501,24213, + 9875,24213, 8165,24213,21127,24095,21751,24229,20843,24095,21757,24233, + 11,24095,19687,24237,23610,24237,23493,24237,18967,24242, 7971,24237, +18967,24237,23493,24248,18967,24095, 10,24252,24213,24255, 10,24253, +24237,24259, 483,24253, 10,21605,23893,24265,24020,24265,22581,24265, +22669,24265,19455,24265,23917,24265,19333,24265,23073,24265,19393,24265, +23060,24265,24092,24265,23925,24265,24078,24265,23651,24265, 9755,24265, + 9317,24265, 4321,24265, 3833,24265,10171,24265, 4295,24265,22149,24265, +18967,24307,24213,24265,18967,24311,24059,24265,18967,24315,21183,24265, +19396,24265,23983,24320,19559,24321, 922,24265, 1413,24265,19683,24265, +24074,24265,18967,24332,23055,24265,18967,24336,24080,24265, 9,24340, +20839,24265,21399,24265,19385,24265,23879,24349, 9,24349,23983,24265, +19396,24355,19396,24354,18967,24355, 9,24360, 9,24355,18967,24364, +18967,24354, 9,24368, 9,24354,18967,24372,18967,24264,19441,24377, +24213,24377,24059,24377,22149,24377,19656,24377,19631,24377, 9,24388, +23983,24377, 9,24393, 9,24377,19631,24396, 903,24265, 483,24400, + 483,24265, 903,24404,18967,24265,23055,24408,24074,24408,19559,24409, + 9,24415, 9,24408,23983,24418,19559,24419,23983,24408, 9,24424, + 9,24265,24080,24428,18967,24428,19559,24433,23983,24432,23983,24428, +18967,24438,18967,24429,19631,24443, 17,21604,21963,24447,23958,24447, +23972,24447,23974,24447,23960,24447,23962,24447,21979,24447,21977,24447, +21967,24447,21965,24447,23964,24447,23966,24447,23968,24447,23970,24447, +21971,24447,21969,24447,21975,24447,21973,24447,22807,24447,19545,24447, +23939,24486,20357,24447,23939,24490,20497,24447,23939,24494, 9669,24447, +23939,24498, 9179,24447,23939,24502, 3799,24447,23939,24506, 4143,24447, +23939,24510,10099,24447,23939,24514, 4133,24447,23939,24518,23939,24447, +19545,24522,20357,24522,20497,24522,10099,24522, 4133,24522, 3799,24522, + 9669,24522, 9179,24522, 4143,24522, 15,24446, 15,21604,23357,24545, +22713,24545,22423,24545, 8337,24545,20103,24545, 17,24544, 483,24545, + 19,21605,22163,24561,18967,24560, 13,21605,24080,24567,22149,24567, +23983,24567,18967,24572,18967,24567,23983,24576, 113,21605,10385,24581, +23879,24583, 9,24583,11731,21605, 67,24589,23879,24591, 9,24591, + 137,21605,12261,21605, 587,24599,10107,24601,11067,24601,12285,24601, + 1346,21605, 1505,24609, 256,21605, 8495,24613,10107,24613, 1486,21605, + 803,24619, 1504,21605,20029,24623,21103,24623,19781,24623, 973,21605, + 2845,24631, 4812,24631, 955,24631, 4805,24636, 2971,24631, 4814,24631, + 9,24631, 4805,24644, 4805,24631, 955,24648, 9,24648, 341,21605, + 803,24655,17049,24655,20884,21605,21820,24661,21807,24661,20413,24664, +20413,24661,21807,24668,21182,21605,21195,24673,21836,24673,21829,24673, +20103,24678,20103,24673,21829,24682,20984,21605,20839,24687,20413,24689, +20413,24686,20839,24693,21258,21605,23755,24697,21749,24697,21574,21605, + 9,24703,21404,21605,24109,24707,21803,24707,21849,24707,21621,24707, +21693,24707,21805,24707,21801,24707,21381,24721, 9,24721, 1141,21605, +17025,24727, 1447,21605,17025,24731, 253,21605,17049,24735, 1471,21605, +17049,24739,21018,21605, 5,24742,20839,24745, 5,24743,20885,24749, +21276,21605, 7,24753,20413,21605, 6,24756, 4,24759,18967,24760, +18967,24759, 4,24764, 4,24765,21314,24759,21570,24756,21571,24757, +24773,24775, 6,24757,21314,24778,21314,24779,20885,24757,24661,24785, +21571,24756,21570,24757,24789,24791,21315,24757,24783,24795,21381,24757, +20885,24756,21791,24801,19475,24756,20984,24756,20839,24807,21315,24756, +21747,24811,24781,24811,23457,24811, 5,24756,20762,24819,20762,24818, +24763,24819,24767,24819,20839,24819,21399,24819,24771,24819,19683,24819, +20763,24819,20843,24837,24823,24837,20763,24818,24821,24843,20839,24843, + 5,24757,24769,24849,20885,24849,24661,24849,20763,24756, 5,24856, +20839,24859, 5,24857,20885,24863,20103,21605,21183,24867,21127,24869, +24673,24869, 15,24866,21183,24866,21795,24877, 15,24867,19687,24866, + 9,24883,19625,24866,20843,24866, 9,24889,21549,24866,21127,24866, +21183,24895, 9,24895,21105,24866,20763,24866, 5,24903,21527,24866, + 9,24867,19687,24909,20843,24909,21127,24909, 6,24866,21036,24917, + 5,24917,18967,24920,18967,24917, 5,24924,21036,24916, 5,24916, +18967,24930,24881,24917,18967,24916, 5,24936, 7,24866,23755,24941, + 1325,24941, 331,24941, 59,24941, 265,24941, 7,24867,23929,24953, +22205,24953,24919,24953,24923,24953,24927,24953,24929,24953,24933,24953, +24939,24953,24905,24953, 4,24953,24903,24973,22269,24953,22263,24953, + 375,24953,21037,24953,24917,24983, 6,24867, 5,24986,24941,24989, +24901,24987,24893,24987,24887,24987,21037,24866,24987,24999, 7,24999, +20273,21605,21258,25004,21259,25005,25007,25009,21183,25005,21259,25004, +21258,25005,25015,25017, 375,25005, 6,25004, 7,25004, 7,25005, +25023,25027,21315,25027, 6,25005,25025,25033,20501,21605, 7,25036, + 4,25039, 4,25038,20885,25037, 7,25037,18967,25047, 5,25049, + 5,25048, 5,25047,18967,25054, 5,25046,21036,25047,21037,25037, +22269,25063,21105,25063, 16,25036, 10,25037, 803,25037, 903,25037, + 9,25037, 17,25036,19625,25036,21105,25036,21549,25036,21037,25036, +21829,25087,25059,25087,21127,25087,24095,25087,25071,25087, 11,25037, + 4,25036, 7,25100,25051,25101, 5,25036,25041,25107,18967,25106, +25099,25111, 5,25037,25103,25115,25043,25115,25087,25115, 7,25114, +25087,25123,25069,25115, 4,25037,25111,25129,25057,25129,25053,25129, +25061,25129,25079,25129, 9165,21605, 9168,25140, 9169,25141,25143,25145, + 9169,25140, 9168,25141,25149,25151, 8337,21605, 15,25155, 6,25155, + 5,25158, 7,25155, 375,25163, 6,25154,25157,25167, 7,25154, +25161,25171,23755,25171, 59,25171, 331,25171, 1325,25171, 265,25171, + 9617,21605, 9630,25184, 9631,25185,25187,25189, 9629,25185, 9631,25184, + 9630,25185,25195,25197, 375,25185, 6,25185, 7,25185, 6,25184, +25205,25207, 7,25184,25203,25211, 9875,21605, 7,25214, 4,25216, + 4,25217, 9893,25215, 16,25214, 803,25215, 9,25215, 17,25214, + 903,25215, 4,25215,25231,25235, 5,25215,25219,25239,25225,25239, + 4,25214, 7,25244,25239,25247, 5,25214,25221,25251, 843,21605, + 9,25255, 413,21605, 7,25259, 375,25259, 1530,21605, 9,25265, + 1216,21605, 7,25269,21497,21605, 7,25273,21277,21605, 7,25276, +21183,25279, 7,25277,21105,25283,21019,21605, 5,25286,20885,25289, + 5,25287,20839,25293, 265,21605, 7,25297, 7971,25299, 587,21605, + 11,25302, 9,25305, 9,25303, 11,25309, 5,25303,20501,25313, + 9875,25313, 903,21605, 9,25319, 11,25321, 11,25318, 9,25325, + 7,25319, 1141,25329, 33,25329, 1447,25329, 1011,25318, 7,25337, + 1275,21605, 9,25341, 1369,25343, 17,25340, 903,25347, 5,25340, + 1505,25351, 5,25341, 253,25355, 53,25355, 1471,25355, 1369,25340, + 9,25363, 1011,21605, 7,25367, 959,25369, 47,25369, 759,25369, + 903,25366, 7,25377, 1369,21605, 9929,25380, 9927,25383,11759,25380, + 7971,25387,17053,25381,10081,25391, 7,25380, 4,25394, 5,25395, + 7,25398,25381,25401, 0,25398,25403,25405, 4,25395, 0,25395, + 5,25410, 1,25380, 5,25415, 1,25416,25381,25419, 6,25416, +25421,25423, 6,25415, 5,25426, 14,25415, 10,25380, 5,25433, + 0,25434, 0,25433, 5,25438, 16,25380,10081,25380, 7971,25445, +17053,25445, 7971,25381,11759,25451,10081,25451, 17,25380, 11,25380, + 11,25381, 1965,25461, 3195,25461,25437,25461,25441,25461, 5197,25461, +25405,25461,25413,25461, 1,25461, 4579,25461,15899,25461, 15,25380, +23763,25483,23771,25483, 3755,25483, 1995,25483, 0,25483,25461,25493, +25477,25483, 4917,25483,17013,25483, 4,25380, 7,25502,20029,25503, +19781,25503,21103,25503, 5,25380,25409,25513, 803,25513, 4,25381, +20033,25519,25457,25519, 5,25381,25397,25525,25505,25525,25443,25525, +10219,25525, 959,25525, 47,25525, 759,25525, 1275,25380, 9,25541, + 241,21605, 9,25545, 483,25545, 7,25544, 8495,25551,10107,25551, +17571,21605, 7,25557, 4,25558, 4,25559, 16,25557, 17,25557, + 4,25556,25567,25569, 5,25556,25561,25573,25565,25573, 4,25557, + 7,25578,25573,25581, 5,25557,25563,25585,17371,21605,16711,25589, + 7,25589,14315,21605, 5,25594, 6,25596, 6,25597, 14,25594, + 15,25594, 6,25594, 5,25606, 7,25594,25601,25611, 6,25595, +25605,25615, 7,25595,25609,25619,25599,25619,25603,25619,15705,21605, + 767,25627, 483,25629, 4,25627, 483,25633,21527,21605,20103,25636, + 5,25637, 17,25641, 5,25636, 9,25645,21315,21605,21183,25648, +21795,25651,21183,25649,24673,25655,21747,25649,20413,25659,23457,25649, +20413,25663,20413,25648,23457,25667,21747,25667,21037,21605,21829,25673, +20501,25675,21127,25673,20501,25679,21105,25673, 11,25673,20501,25685, + 15,25673,20501,25672,24095,25691,21829,25691,21127,25691,20187,25672, +24095,25673,20501,25701, 6,25673, 7,25673, 6,25672,25707,25709, +20103,25709, 7,25672,21749,25715,23755,25715,25705,25715,20103,25672, + 7,25723,20763,21605,20482,25726,20839,25729,20483,25726,21183,25726, + 4,25727,23147,25737,24819,25737, 5,25727,23383,25743,20956,25743, +20773,25743,24953,25743,20885,25743,20413,25752,20413,25743,20885,25756, + 4,25726,21605,25761, 7,25763,25743,25765,24849,25761,20413,25761, + 5,25726,20839,25773,20413,25775,20413,25772,20839,25779,20413,25726, + 5,25782,20839,25785, 5,25783,20885,25789, 5,21604,25275,25793, +25003,25793,25725,25793,24755,25793,21049,25793,21242,25793,20773,25793, +20956,25793,25699,25793,22725,25793,22391,25793,24682,25793,23209,25793, +25712,25793,25733,25793,22711,25793,22395,25793,24668,25793,23213,25793, +25770,25793,25083,25793,23235,25793,22017,25793,25085,25793,23231,25793, +22025,25793,25087,25793,25691,25793,22357,25793,22047,25793,23455,25793, +15567,25793,12971,25793,13355,25793,13541,25793,22869,25793,22635,25793, +23021,25793,23894,25793,23919,25793,21997,25793,21995,25793,22583,25793, +23906,25793,23910,25793,22419,25793,22421,25793,23034,25793,19656,25793, +19441,25793,11970,25793, 4696,25793,22674,25793,22069,25793,25081,25793, +22011,25793, 9357,25793,25205,25793,25027,25793, 9356,25793,25171,25793, +24941,25793,23199,25793,23175,25793,20606,25793,25593,25793,19396,25793, +11131,25793,21059,25793,23018,25793,20501,25933,20057,25793,22633,25793, +20501,25939,21405,25793, 357,25793, 367,25793, 471,25793, 1249,25793, + 628,25793, 4569,25793,14625,25793, 4545,25793,13325,25793, 8337,25961, + 4549,25793,23229,25793,20501,25967,23233,25793,20501,25971,25706,25793, +20186,25793, 8386,25793, 9083,25793, 9484,25793, 9984,25793,20187,25793, + 8387,25793,19513,25793, 8401,25793,20219,25793, 4137,25793,10273,25793, + 4063,25793, 3863,25793,20697,25793, 9955,25793,20353,25793,25709,25793, +20103,26010,25715,25793,24673,25793,20103,26016,24697,25793,21183,25793, +20103,26022,21259,25793, 4745,25793,17423,25793, 9082,25793,22673,25793, +20413,26034,23033,25793,20413,26038,25551,25793,24613,25793,22249,25793, +20326,25793,21087,25793, 9704,25793,23879,25793,19631,26054,11949,26054, + 4669,26054,11949,25793,23879,26062, 9,26062, 4669,25793,23879,26068, + 9,26068,20805,25793,23015,25793,18967,26076,20501,26079,18967,26077, +20413,26083,19631,25793,23879,26086, 9,26086, 9377,25793, 11,26092, + 341,25793, 241,26097, 8165,25793, 256,25793, 257,25793, 11,25793, + 9195,26106,20501,26106, 587,26106, 9377,26106, 9875,26106,20763,26106, +20804,25793,10219,25793, 587,26123, 53,25793,20501,25793, 10,26128, + 10,26129, 11,26128,26133,26135, 11,26129,26131,26139,20413,25793, +20885,26142,24661,26142,25761,26142,22673,26142,23033,26142,20762,25793, +25761,25793,20413,26156,24661,25793,20413,26160,20885,25793,20413,26164, + 17,25793,20762,26168,20762,26169,20763,26169,26171,26175,20763,26168, +26173,26179, 9,25793,11949,26182, 4669,26182,19631,26182,18967,26182, +20273,26182, 9617,26182,20763,25793, 11,26196,25707,25793, 9875,25793, + 10,26202, 10,26203, 11,26202,26207,26209, 11,26203,26205,26213, + 253,25793, 6,25793,25273,26219,24753,26219,24999,26219,24953,26219, +25005,26219,25723,26219,25707,26219,25185,26219,25163,26219,25589,26219, +24081,26219, 4713,26219,16363,26219, 9195,26219, 4863,26219,13119,26219, + 8757,26219, 4213,26219,23493,26219, 8109,26219, 3933,26219, 4147,26219, + 9335,26219,20103,26219, 8337,26219,20273,26219, 8997,26219,25673,26219, + 241,26219,18967,26219, 11,26279, 11,26219, 587,26219, 8387,26219, +14653,26219,20187,26219, 7,25793, 8717,26293, 3785,26293,25673,26292, +23755,26293,10351,26293, 4311,26293, 4329,26293,10055,26293,20103,26292, + 8337,26292, 8997,26292, 15,26293,18967,26317,18967,26292, 241,26292, + 9195,25793, 11,26324, 1471,25793, 6,25792,23175,26331,25977,26331, +24941,26331,26311,26331,26299,26331,25975,26331,25715,26331,24697,26331, +26315,26331,23199,26331,25171,26331,25979,26331,26033,26331,26313,26331, +16381,26331,26323,26331,26103,26331,13325,26331, 8685,26331,26155,26331, +26321,26331,26293,26331,20103,26375,25673,26375, 8997,26375, 8337,26375, + 241,26375,18967,26375, 17,26331, 1369,26331, 4569,26331,19513,26331, + 4063,26331,10273,26331, 3863,26331,20697,26331, 9955,26331, 4137,26331, +20219,26331, 8401,26331,14625,26331, 7,25792,25987,26415,26266,26415, +26201,26415,26274,26415,26272,26415,25989,26415,25981,26415,26268,26415, +21604,26415,18967,26433,26219,26435, 8997,26415,26219,26438,26105,26415, +26276,26415, 8647,26415,19396,26415,26197,26415,26278,26415,26219,26415, +20103,26454,25673,26454, 8997,26454, 8337,26454, 241,26454,18967,26454, + 8337,26415,26219,26468,20103,26415,26219,26472, 9,26415,18967,26476, + 241,26415, 1,26481,26219,26483,26219,26480,25673,26415,26219,26488, +18967,26415, 9,26492,26219,26492, 587,25793, 11,26498, 1369,25793, +25673,25793,20501,26505, 6,26504, 7,26504,26331,26511, 6,26505, +26511,26515, 7,26505,26509,26519,26219,26519,18967,25793, 9,26524, +23015,26524,20501,26529, 483,26525, 6,26524, 7,26524,26331,26537, + 7,26525,26219,26541, 6,26525, 241,25793, 1,26547, 7,26549, +26331,26551, 7,26548,26219,26555, 7,26547, 1,26558,26219,26561, +26219,26559, 7,26546,26331,26567,20273,25793, 9,26570,14315,25793, + 8997,25793, 7,26576,26331,26579, 7,26577,26219,26583, 9617,25793, + 9,26586, 8337,25793, 7,26590,26331,26593, 483,26591, 9,26591, + 7,26591,26219,26601,20103,25793,21183,26604,24673,26604,25709,26604, + 7,26604,26331,26613, 483,26605, 9,26605, 7,26605,26219,26621, + 4,21604,21955,26625,24877,26625,23433,26625,21947,26625,21951,26625, +24801,26625,23415,26625,21943,26625,22282,26625,22251,26625,22266,26625, +22253,26625,24811,26625,25667,26625,22363,26625,22053,26625,21116,26625, +21560,26625,25735,26625,23437,26625,22699,26625,21253,26625,23930,26625, +22206,26625,24805,26625,23937,26625,22587,26625,23039,26625,23053,26625, +23051,26625,21961,26625,21981,26625,20465,26625,20576,26625,21985,26625, +21983,26625,22077,26625,22075,26625, 9573,26625, 9575,26625,23847,26625, +22465,26625,22507,26625, 3,26625, 7,26712, 9605,26625, 586,26625, + 9599,26625,19474,26625,20413,26723,20186,26625,20805,26625,22423,26625, +22713,26625,22205,26625,20501,26734,19625,26625,20501,26738,19687,26625, +23929,26625,20501,26744,23934,26625,20413,26749,22585,26625,20413,26753, + 8386,26625,23927,26625,18967,26759,20501,26761,18967,26758,20413,26765, +23037,26625,19631,26769,11949,26769, 4669,26769, 1418,26625, 9565,26777, + 669,26625, 9565,26781,22263,26625,20501,26784,22269,26625,20501,26788, +21549,26625,20501,26792,20843,26625,21105,26625,20501,26798,21127,26625, +23423,26625,20103,26805,23357,26625, 7971,26625, 1170,26625,25203,26813, +20501,26625,22269,26816,22263,26816,21105,26816,21549,26816,22205,26816, +23929,26816,19625,26816,20762,26625,26219,26833,23361,26625,20413,26837, + 11,26625, 1,26841,18967,26841, 15,26625,18967,26846,20413,26849, +18967,26847,20501,26853, 1369,26846, 9565,26857, 7,26625, 4,26861, + 3,26860, 4,26860,21605,26867,20103,26860, 8337,26860,18967,26860, +26219,26875, 1011,26860,25203,26879, 6,26625,21604,26882,20763,26883, + 6,26624,24811,26889,22053,26889,22363,26889,25667,26889,20501,26889, +25649,26889,20413,26901,18967,26889, 7,26624,24265,26907,19559,26907, + 9673,26907, 8251,26907, 3807,26907, 4283,26907,20361,26907, 4279,26907, +20713,26907, 9967,26907, 483,26625, 1369,26625, 15,26930, 9565,26933, +25649,26625,20413,26937,18967,26625, 15,26940,20413,26943,23927,26940, +20413,26947, 6,26940,21604,26950, 7,26940,26219,26955, 7,26941, +26953,26959,26885,26959,26331,26959,23755,26959, 6,26941, 1011,26625, + 7,26971,25207,26973,23197,26973, 7,26970,25203,26979,20103,26625, + 7,26982, 8337,26625, 7,26986, 9565,26625, 9617,26625, 6,26992, + 7,26992, 6,26993,26997,26999, 7,26993,26995,27003,20273,26625, + 6,27006, 7,27006, 6,27007,27011,27013, 7,27007,27009,27017, + 7,21604,25987,27021,26266,27021,22732,27021,22733,27021,25087,27021, +25691,27021,22357,27021,22047,27021,26027,27021,25989,27021,26268,27021, +20819,27021,20152,27021, 8374,27021, 8389,27021,20201,27021,20952,27021, + 1,27021, 4,27056, 4,27057, 3,27021, 4,27062, 902,27021, +19559,27021,20361,27021, 9673,27021,20713,27021, 9967,27021, 4283,27021, + 4279,27021, 8251,27021, 3807,27021,26100,27021,26106,27021,26128,27021, +26202,27021, 8647,27021,20326,27021,25239,27021,25115,27021, 8800,27021, +21087,27021, 357,27021, 367,27021, 471,27021, 1249,27021, 8086,27021, +19687,27021, 9704,27021,25742,27021,20885,27021, 483,27122, 1141,27021, + 375,27021,20103,27128, 8337,27128,25673,27021,20501,27135,26219,27021, +20103,27138, 8337,27138, 11,27139, 17,27139, 1447,27021,21127,27021, +20843,27021, 8165,27021,25793,27154, 265,27155, 33,27021, 567,27021, +22729,27021,21315,27164, 11,27021,25793,27169, 8495,27169,25793,27168, + 8495,27168,18967,27169,21037,27169,21497,27021,21277,27021,21276,27021, +21496,27021,20273,27021, 9,27190,21315,27190,20103,27021,26219,27196, + 375,27196,21037,27196,21314,27021,22729,27205,20273,27205,21036,27021, +25129,27211,20103,27211,21183,27021, 9,27021,20273,27218, 9617,27218, +21315,27021,22729,27224,20273,27224,21037,27021,20103,27230,25743,27021, + 25,27021, 7971,27236, 7971,27021, 25,27240, 9617,27021, 9,27244, + 8337,27021,26219,27248, 375,27248, 4,27021,25037,27255,25215,27255, + 3,27254, 1,27254,25727,27255, 483,27255,20691,27255, 9923,27255, +18967,27255,20103,27273,18967,27254,22729,27277,20273,27277, 5,27021, +27061,27283, 1,27283,25727,27282,18967,27283,22729,27291,20273,27291, + 17,27283,18967,27297,18967,27282,25129,27301,20103,27301,25793,27021, + 8165,27306, 11,27306,20501,27306, 9875,27306, 9731,27021, 8495,27021, + 11,27318, 4,27020, 1,27323,27283,27324,27286,27323,27289,27323, +27121,27323,24265,27323,27225,27323,27290,27323,27211,27323,27301,27323, +27283,27323, 1,27344,25727,27345,18967,27344,18967,27345,19559,27323, +18967,27323,27283,27356, 4283,27323, 9673,27323, 8251,27323, 3807,27323, +20361,27323, 9967,27323,20713,27323, 4279,27323, 5,27020,27059,27377, +27263,27377,27235,27377,27264,27377, 8647,27377,19396,27377,27231,27377, +27272,27377,27205,27377,27277,27377,27255,27377, 1,27399,25727,27398, +18967,27398,18967,27399,26219,27377, 9,27377,18967,27410,25727,27377, +27255,27414,18967,27377, 9,27418,27255,27418, 1011,27021,25727,27021, + 5,27426,27323,27429, 5,27427,27255,27433,18967,27021, 7,27437, +21605,27439, 4,27441, 4,27440, 4,27439,21605,27446, 4,27438, + 16,27437, 113,27437,11731,27437, 17,27437, 4,27436,22729,27461, +21605,27460,27459,27465,27377,27461,20273,27461, 5,27436,25129,27473, +27443,27473,27323,27473,20103,27473, 5,27437,25101,27483,23193,27483, + 7,27483,21605,27489,27461,27491,27449,27483,27445,27483,27461,27483, +27255,27483,21315,27483, 4,27437, 7,27504,27473,27505,27283,27505, +21037,27505, 483,27021,20885,27514,20501,27021, 241,27519,25793,27518, + 9875,27021, 241,27525,25793,27524,20413,27021, 6,21604,24811,27533, +25667,27533,22363,27533,22053,27533, 1,27533, 5,27542,22605,27545, +22677,27545,23023,27545,25483,27545,20644,27533,21359,27533,25793,27557, +12345,27533, 5,27561, 4,27560,27563,27565, 4,27561, 5,27560, +27569,27571, 5123,27533, 5,27575, 4,27574,27577,27579, 4,27575, + 5,27574,27583,27585,19667,27533, 5,27589, 4,27588,27591,27593, + 4,27589, 5,27588,27597,27599, 341,27533,21052,27533,25793,27605, +25649,27533,20413,27609,25673,27533,21183,27613,26861,27533, 9,27617, +26293,27533, 9,27621,17219,27533, 903,27625, 4663,27533, 903,27629, +23195,27533,21315,27633,10219,27533, 903,27637, 53,27533,25025,27533, +21315,27643,20103,27533, 9,27647,21314,27533,24795,27651,26142,27651, +20413,27651,25793,27656,25793,27651,20413,27660,21036,27533,25129,27665, +21259,27533,26625,27669, 17,27533,21315,27673,21037,27533,25793,27677, +20501,27679,20501,27676,25793,27683, 8337,27533, 9,27687, 1471,27533, + 5,27533, 6,27693, 587,27695,21605,27697, 1,27692,22605,27701, +22677,27701,23023,27701,25483,27701,27699,27701, 6,27692,25380,27713, +27701,27715, 1369,27713,21605,27718,27701,27721,21605,27713, 1369,27724, +27701,27727, 9,27693,18967,27692,25129,27733, 9935,27693,20645,27693, + 4,27533,20501,27740,26524,27741,20501,27745,25793,27741,18967,27748, +20501,27751,18967,27749,20413,27755,18967,27741,25793,27758,20501,27761, +20501,27759,25793,27765,18967,27740,26142,27769,20413,27769,25793,27772, +25793,27769,20413,27776,26940,27533,26969,27781, 253,27533, 4,27532, +24811,27787,22053,27787,22363,27787,25667,27787,20501,27787,25649,27787, +20413,27799,18967,27787, 5,27532,24941,27805,23175,27805,25715,27805, +24697,27805,25171,27805,23199,27805,26959,27805,16381,27805,13325,27805, + 8685,27805,26293,27805, 17,27805, 1369,27805, 4569,27805,19513,27805, + 4063,27805,10273,27805, 4137,27805,20219,27805, 8401,27805, 3863,27805, +14625,27805,20697,27805, 9955,27805, 241,27533,18967,27533,20486,27857, +20487,27857,20413,27857, 4,27862, 4,27863, 580,27857, 483,27857, + 4,27870, 4,27871, 581,27857, 4,27856,27861,27879,26142,27879, +27877,27879,20413,27879,25793,27886,25793,27879,20413,27890, 5,27856, +25129,27895,27859,27895,27865,27895,27869,27895,27873,27895, 5,27857, +25101,27907,27555,27907,23193,27907,27743,27907,27886,27907,27867,27907, +27875,27907,20413,27907,27879,27922,27879,27907,20413,27926, 4,27857, +20413,27930,27895,27933, 483,27930,27895,27937,27895,27931,20413,27941, + 1369,27533, 5,27945,20501,27533,21037,27949, 4,27948,27907,27953, + 5,27948,18967,27956,21037,27948,25793,27961, 4,27949,27959,27965, +27665,27965,27733,27965,27895,27965, 5,27949,27961,27975, 7,21605, + 1,27979, 2,27981, 5,27983, 5,27982, 1010,27981, 5,27981, + 2,27990, 3,27978, 9,27995, 4311,27979,10351,27979, 4329,27979, + 3785,27979,10055,27979, 8717,27979,26283,27979,26388,27979,23790,27979, +27828,27979,22143,27979,22149,27979,24213,27979,24059,27979, 331,27979, + 8337,28027,20103,28027,21036,27978,21049,28033,21242,28033,21183,28033, +20103,28038,20103,28033,21183,28042,26331,27979,20103,28047, 8337,28047, + 17,28046,21277,27978,21183,28055,27693,27979,18967,28059,25793,27979, +21127,28063,20843,28063,19687,28063, 8337,28063,20103,28063, 7971,28063, + 241,28063, 8495,27979, 241,28079, 8337,27978,26331,28083,27805,28083, + 1325,28083, 331,28083, 59,28083,23755,28083,25793,28083, 265,28083, +21037,27979,27665,28101,28033,28101, 11,28101,21315,27979, 1325,27979, + 8337,28111,20103,28111, 59,27979, 8337,28117,20103,28117,10291,27979, + 483,28123,20273,27978,27533,28127,21315,28129,20103,27978,26331,28133, +27805,28133,23755,28133, 331,28133, 1325,28133, 59,28133,25793,28133, + 265,28133,21105,27979, 11,27979,20103,28153, 8337,28153, 17,27979, +26331,28158, 8337,28159,20103,28159,27805,28158,23755,28158,27731,27979, +27692,27979, 9,28173,23755,27979,20103,28177, 8337,28177, 17,28176, +23983,27979, 9,28185, 15,27979, 3,28189,20273,28189, 9617,28189, +27805,27979,20103,28197, 8337,28197, 17,28196, 241,27978,25793,28205, + 8495,28205,10107,28205, 265,27979,20103,28213, 8337,28213, 4,27978, +27985,28219,21105,28219,21549,28219,19625,28219,28173,28219,18967,28229, + 5,27978,19397,28233,28060,28233,18966,28232,18966,28233, 9,28241, +27323,28233,23651,28233,26907,28233, 9755,28233, 9317,28233, 4321,28233, + 3833,28233,10171,28233, 4295,28233,28101,28233,21183,28233, 1413,28233, + 922,28233,27856,28233,28059,28233,18967,28272,19396,28233,28239,28277, +19683,28233,18967,28232,28235,28283,28242,28283,28241,28283, 9,28288, + 9,28283,28241,28292,27533,28233,18967,28296, 9,28233,18967,28300, +28239,28303,18967,28301,28241,28307,21399,28233,20839,28233, 483,28233, + 903,28314, 903,28233, 483,28318,18967,28233,28059,28322,28239,28323, + 9,28327,27533,28322, 9,28322,28239,28333, 4,27979,27987,28337, +27993,28337,28270,28337,28330,28337,28298,28337,27989,28337, 586,28337, + 3,28337, 7,28352, 7,28337, 3,28356,27856,28337,28233,28360, +21575,28337,28296,28337,18967,28366,27533,28337,28322,28370,18967,28370, +28233,28374,28233,28370,18967,28378,28322,28337,27533,28382,18967,28337, +28296,28386,27533,28386,28233,28390,28233,28386,27533,28394,28233,28337, +27856,28398,27533,28398,18967,28402,18967,28398,27533,28406, 5,27979, +27255,28411, 9287,28411,20885,28411,27533,28410,28219,28419,18967,28421, + 9,28419, 241,28411, 483,28427,28219,28411,27856,28431,27533,28431, +18967,28434,18967,28431,27533,28438,18967,28410,28219,28443, 17,28443, +21036,27979,21069,28449,21267,28449,21305,28449,28219,28449,21259,28449, +20103,28459, 17,28449,21037,27978,26331,28465,21749,28465,27805,28465, +23755,28465,28449,28465,25793,28465,10001,27979, 9,28479,27533,27979, +21496,28483,21277,28483,20885,28483,27931,28483, 9,28483, 9731,28483, +21037,28482,21037,28483,21315,28483,20273,28500,21036,28482,28499,28505, +21036,28483,28497,28509,20273,28483,21315,28512, 4,28483, 5,28483, +28386,28519,28337,28519,18967,28522,18967,28519,28337,28526, 17,28519, + 4,28482,28519,28533, 5,28482,28517,28537,28219,28537,18967,28541, + 9,28537, 1275,27979,20031,28547,20885,28547, 5,28547,18967,27979, +20103,28555, 5,28555,28337,28559, 9,28559, 5,28554,28219,28565, + 17,28565,21527,27979,18967,27978,28519,28573, 7,28573, 4,28576, + 4,28577,28553,28573,21605,28573, 5,28584,28581,28587, 5,28585, +28579,28591, 16,28573,28591,28595, 11,28573,20273,28573, 567,28573, + 5,28572, 4,28573, 7,28606,28591,28609,28483,28607,28547,28607, + 483,28607, 5,28573,21605,28618,28581,28621,20762,21605,20803,28625, + 7,28625, 4,28628,25743,28631, 4,28629,24687,28635,25773,28635, +21035,28625, 16,28625,25743,28643,20999,28625,20985,28625, 483,28649, + 11,28625,20273,28625, 567,28625, 4,28624,20985,28659, 5,28624, +20956,28663,20773,28663,20885,28663,20413,28668,20413,28663,20885,28672, +20482,28625,20839,28677, 4,28625, 7,28680,25743,28683,20843,28681, + 483,28681, 5,28625,21605,28690,28635,28693,20839,28691,20413,28697, +20413,28690,20839,28701,20413,28625, 5,28705,20885,28707, 5,28704, +20839,28711, 5,21605,21183,28715,18967,28716,24673,28719,21194,28715, +24673,28723,28573,28714,28581,28727,20620,28714,20621,28714, 9994,28714, + 9995,28714, 1,28715, 1,28714,26781,28741, 668,28741,26625,28745, +26777,28741, 1419,28741,26625,28751,26857,28741,26933,28741, 2,28741, + 9,28741, 587,28760,26625,28763, 587,28741, 9,28766,26625,28769, +21018,28714,20839,28773,19396,28715,22579,28777,21611,28777,19683,28715, +20413,28783,23490,28715,20413,28787,21526,28714,26816,28791,24953,28791, + 17,28791,20501,28791,26625,28798,26625,28791,20501,28802,20762,28714, +20773,28807,20956,28807,20885,28807,20413,28812,20413,28807,20885,28816, +27787,28715,18967,28821,20501,28823,18967,28820,20413,28827,27802,28715, +20413,28831,21019,28714,20885,28835,27856,28715,20413,28839,27931,28715, +20413,28843,26904,28715,20413,28847,26861,28715,18967,28851,20823,28715, +20645,28715, 9935,28715,21362,28715,21376,28715,27533,28715,18967,28865, +20501,28867,18967,28864,20413,28871, 9994,28715,28737,28875,20822,28715, +28807,28879,20763,28715,28835,28883,28816,28883,20866,28883,21021,28883, +20413,28883,28807,28892,20843,28892,28807,28883,20413,28898, 9,28899, +20843,28883,20413,28904, 9,28882,28807,28909, 803,28715, 11,28913, +27979,28715,18967,28917,20621,28715,28731,28921,20413,28714,21399,28925, +20839,28925,25737,28925,19683,28925,20763,28925,20843,28935,28807,28935, +20763,28924,20839,28941,25737,28715,20413,28945,20839,28715,20413,28949, +20620,28715,28733,28953,21399,28715,20413,28957, 483,28715,21315,28960, +26860,28715,28337,28715, 11,28967,23457,28715,18967,28970,20413,28973, +18967,28971,20501,28977, 9,28715,21315,28980,20763,28981,20763,28980, +28807,28987, 17,28715,11949,28991, 4669,28991,19631,28991,18967,28991, +20273,28991, 9617,28991, 9995,28715,28735,29005, 1275,28714, 1505,29009, +26889,28715,18967,29012,20413,29015,18967,29013,20501,29019, 1369,28714, + 803,29023, 587,28715, 1505,28715, 6,28714,26940,29031,20413,29033, +28965,29031,18967,29037,18967,29030,26816,29041,24953,29041, 17,29041, +20501,29041,26625,29048,26625,29041,20501,29052,26625,29031,18967,29056, +20413,29059,18967,29057,20501,29063,18967,29031,20413,29067,26625,29069, +26625,29066,20413,29073, 7,28714,19397,29077, 0,29077, 2,29080, +28991,29083, 2,29077, 0,29087, 0,29086,28991,29091,28999,29077, + 1,29076,29091,29097,29083,29097,29081,29097, 2,29103, 1,29077, +29089,29107, 2,29107,28852,29077, 0,29076, 2,29115,29107,29116, +29110,29115,29107,29115, 2,29122,18966,29076,18966,29077,29097,29129, +28991,29129, 9,29129,27323,29077,23651,29077,26907,29077, 9755,29077, + 9317,29077, 4321,29077, 3833,29077,10171,29077, 4295,29077,21183,29077, + 1413,29077, 922,29077,26940,29077,28851,29077,18967,29162,19396,29077, +29127,29167,19683,29077,18967,29076,29079,29173,29134,29173,29129,29173, + 9,29178, 9,29173,29129,29182,26625,29077,18967,29186,21399,29077, + 9,29077,18967,29192,29127,29195,18967,29193,29129,29199,20839,29077, + 483,29077, 903,29204, 903,29077, 483,29208,18967,29077,29115,29213, +28851,29212,29127,29213, 9,29219,26625,29212, 9,29212,29127,29225, + 6,28715,29083,29229,29097,29230,29091,29229,29097,29234,29098,29229, +29100,29229,29105,29229,29109,29229,29119,29229,29121,29229,29125,29229, +29111,29229,29115,29253,24907,29229,25639,29229,22369,29229,28600,29229, +28557,29229,28654,29229,22057,29229,29178,29229,29160,29229,19706,29229, +10737,29229,10475,29229,10979,29229,19597,29229,12137,29229,29219,29229, +29215,29229,29130,29229,29222,29229,29129,29229,29173,29294,29097,29294, +29188,29229,29097,29229,29091,29302,29083,29302,29129,29302, 1,29229, + 5,29310, 5,29229, 1,29314,29173,29229,29129,29318,19687,29229, +19631,29322,26940,29229,29077,29326,10697,29229, 8165,29331,19631,29229, +19687,29334,21575,29229,20273,29229,28573,29340,28625,29340,29186,29229, +18967,29346,19384,29229,19559,29351,28625,29229,20273,29354,28573,29229, +20273,29358, 11,29229,18967,29362,19559,29365,18967,29363,19631,29369, + 587,29229,21605,29373,29313,29375,29317,29375,26625,29229,29212,29380, +18967,29380,29077,29384,29077,29380,18967,29388,29212,29229,29127,29393, +29115,29393,26625,29392,18967,29229,29186,29400,29077,29401,29173,29405, +29097,29405,26625,29400,29077,29410, 11,29401,19687,29415, 11,29400, +19559,29419,29077,29400,29127,29423,26625,29422,29115,29423,29077,29229, +26940,29430,26625,29430,18967,29434,18967,29430,29127,29439,26625,29438, +29115,29439,18967,29431,29129,29447, 7,28715,28791,29451,29041,29451, +19625,29451,21549,29451,21105,29451,26625,29450,29031,29463,18967,29465, + 1275,29451, 903,29469,29031,29451,26940,29473,26625,29473,18967,29476, +18967,29473,26625,29480,20763,28714,28635,29485,28855,29485,20839,29485, +20413,29491,20413,29484,20839,29495, 9,29485,20762,28715,28941,29501, +29495,29501,28773,29501,20815,29501,20993,29501,21027,29501,28985,29501, +29498,29501,29485,29501,20413,29519, 9,29518,20985,29501,20413,29525, + 9,29501,29485,29528,21527,28714,29229,29533, 9,29533,26940,28715, +20413,29539, 9731,28715, 7,29543,26625,28715,20762,29547, 6,29547, + 7,29547,29400,29553,29229,29553,18967,29556,18967,29552,18967,29553, +29229,29562, 6,29546,29553,29567, 7,29546,29551,29571,29031,29571, +18967,29575,18967,29546,20413,29579,18967,29547,20501,29583, 6,29582, + 7,29582, 6,29583,29589,29591, 7,29583,29587,29595, 903,28715, + 7,29599,18967,28715,21183,29602,24673,29605,21183,29603,21795,29609, +23457,29602,20413,29613,27787,29602,20413,29617,27533,29602,20413,29621, +20413,29603,27787,29625,26889,29625,26625,29625,27533,29625,23457,29625, +26889,29602,20413,29637,26625,29602,20413,29641,21315,28715,21183,29645, +24673,29645,25709,29645, 7,29645, 483,29644, 9,29644,18967,28714, +27965,29659,27948,29659,26816,29659,26898,29659,27796,29659,24953,29659, +20119,29659,25129,29659,29653,29659,26887,29659,23480,29659,25099,29659, +21605,29659, 6,29684,29645,29687,29451,29659,28625,29691,28573,29691, +20187,29659,28625,29659,19683,29659,20839,29659,21399,29659,28573,29659, +27787,29659,20501,29708,20501,29659,27787,29712,26889,29712,26625,29712, +27533,29712,23457,29712,27533,29659,20501,29724, 17,29659, 6,29658, +26816,29731,24953,29731,29451,29731, 17,29731,20501,29731,26625,29740, +26625,29731,20501,29744, 7,29658,29697,29749,20103,29749,20186,29659, +29731,29755, 6,29659,29752,29759,24941,29759,23175,29759,29645,29759, +28133,29759,21605,29758,29645,29771,20103,29759,29749,29774,29077,29759, +29749,29759,20103,29780, 11,29759,20763,29759,26625,29787, 7,29659, +29731,29791,20103,29793,20103,29790,29731,29797,23457,29659,20501,29800, +26889,29659,20501,29804,20103,29659, 7,29809,29759,29811, 7,29808, +29731,29815,26625,29659,20501,29818, 6,29819, 7,29819, 6,29818, +29825,29827, 7,29818,29823,29831,21036,21605,27965,29835,27951,29835, +27948,29835,26816,29835,27796,29835,26898,29835,27216,29835,28151,29835, +28101,29835,26887,29835,21194,29835,24673,29857,24675,29835,21069,29835, +21267,29835,21305,29835,24953,29835,20119,29835,22723,29835,25129,29835, +25063,29835,23480,29835,21796,29835,21795,29835,20501,29882,25099,29835, +18967,29835,21183,29889,21795,29891,21183,29888,24673,29895,21259,29835, +20103,29899,21183,29835,27021,29902,18967,29902,24673,29907,20187,29835, +21127,29911,28573,29835,28625,29835,19683,29835,20839,29835,21399,29835, +27787,29835,20501,29924,27533,29835,20501,29928,27021,29835,21183,29932, +20501,29835,27787,29936,26889,29936,26625,29936,27533,29936,23457,29936, +21795,29936, 17,29835, 6,29834,26816,29953,21069,29953,21267,29953, +24953,29953,21305,29953,25063,29953,21259,29953,20103,29967, 17,29953, +20501,29953,26625,29972,26625,29953,20501,29976, 7,29834,21242,29981, +29911,29981,21049,29981,21183,29981,20103,29988,20103,29981,21183,29992, +20186,29835,29953,29997,21105,29997, 6,29835,28133,30003,21279,30003, +21166,30003,29992,30003,23175,30003,24941,30003,20103,30003,21127,30016, +29981,30016,21127,30003,20103,30022,29981,30003,20103,30026, 11,30003, +20763,30003,26625,30033, 7,29835,21105,30037,20103,30039,29953,30037, +20103,30043,20103,30036,29953,30047,21105,30047,23457,29835,20501,30052, +26889,29835,20501,30056,26625,29835,20501,30060, 6,30061, 7,30061, + 6,30060,30067,30069, 7,30060,30065,30073,20103,29835, 7,30077, +30003,30079,21183,30079, 7,30076,29953,30085,21105,30085, 4,21605, +26371,30091,26450,30091,26539,30091,26542,30091,27439,30090,27483,30101, +17048,30091,26496,30091,26452,30091,26373,30091,26466,30091,26386,30091, + 586,30091, 803,30117, 586,30090, 767,30121, 1,30091, 15,30124, + 2,30124, 7,30124,26219,30131, 7,30125,23755,30135,26331,30135, +27805,30135,25793,30135, 1,30090,26454,30145,26375,30145,26415,30145, +26219,30150,26219,30145,26415,30154,26292,30145,26331,30159,25793,30145, + 7,30162,26331,30165, 7,30163,26219,30169, 7,30145,25793,30173, +26415,30175,25793,30172,26331,30179, 3,30090, 767,30183,26454,30091, +18967,30186,26375,30091,18967,30190,28159,30091, 8684,30091, 8647,30091, +26154,30091,26331,30201,26534,30091,26541,30205,26536,30091,26331,30209, +26320,30091,26331,30213,19384,30091,20501,30217,19474,30091,20611,30091, + 9989,30091,20762,30090,20985,30227,27436,30091,20501,30231,27507,30231, +27451,30231,27453,30231,26492,30091,26219,30240,26278,30091,26415,30244, +26219,30091,26541,30248,26492,30248,26415,30248,18967,30254,18967,30248, +26415,30258,27021,30091,18967,30263,20413,30265,18967,30262,20501,30269, +21527,30091, 9,30273,21258,30091,21575,30091,20501,30279,20985,30091, +20501,30090,27907,30285,27483,30285, 340,30091, 9,30091, 483,30091, +26292,30091,26331,30297,18967,30299,18967,30296,26331,30303,20413,30091, +20763,30307,25793,30309,26197,30091,26415,30312, 375,30091,24095,30091, +18967,30319,20413,30321,18967,30318,20501,30325,24252,30091,20501,30329, + 15,30091, 1,30332,18967,30332, 11,30091,18967,30339,20413,30341, +18967,30338,20501,30345, 1369,30339, 9565,30349,28741,30349,30129,30349, +27979,30339,19631,30357,11949,30357, 4669,30357,26415,30091, 1,30365, +26219,30367,26197,30364,26278,30364,26219,30364,18967,30374,18967,30364, +26219,30378,18967,30365,26219,30383, 1369,30090,20029,30387,19781,30387, +21103,30387,26541,30091,26219,30394, 6,30090,23646,30399,23642,30399, +23606,30399,24349,30399,26086,30399,26062,30399,26068,30399,24583,30399, +24591,30399,19384,30399,24265,30419,25793,30399,19631,30422,11949,30422, + 4669,30422,11949,30399,23493,30430,25793,30430, 4669,30399,23493,30436, +25793,30436,19631,30399,23493,30442,25793,30442, 11,30399,18967,30448, +24265,30451,18967,30399, 11,30454,24265,30457, 11,30455,23493,30461, +25793,30461,23493,30399,11949,30466, 4669,30466,19631,30466, 7,30090, +21549,30475,21105,30475,19625,30475, 6,30091,26524,30483,26545,30485, +26524,30482,26541,30489, 7,30091,26525,30492,30455,30493,25793,30497, + 2,30493, 0,30500,30422,30503,25793,30503,30399,30506,30399,30503, +25793,30510, 0,30493, 2,30514,30422,30517,25793,30517,30399,30520, +30399,30517,25793,30524, 1,30492,26219,30529,18966,30493,30422,30533, +25793,30533,30399,30536,30399,30533,25793,30540, 8495,30492,21183,30493, + 8495,30493,26524,30492,26331,30551,21037,30492,25793,30492,26331,30557, +18967,30559,18967,30556,26331,30563, 265,30492, 265,30493,25793,30493, +26541,30571,30495,30571,26492,30571,26415,30571,18967,30578,18967,30571, +26415,30582,18967,30492,26219,30587,25793,30586,26331,30591,25793,30587, +26415,30595,20763,30090,26142,30599,24849,30599,25793,30599,20413,30604, +20413,30599,25793,30608,20762,30091,26219,30613,20885,30613,25793,30612, +26331,30619,25793,30613,26415,30623,26524,30091,20501,30627, 6,30626, +26541,30631, 7,30626,26331,30635, 6,30627,26537,30639,30635,30639, + 7,30627,30631,30645,26219,30645, 8495,30091, 7,30650,25793,30091, + 1,30655, 7,30656,26219,30659, 7,30657,26331,30663,20762,30654, +26331,30667,20762,30655,26197,30671,26219,30671, 6,30655, 7,30655, +26197,30679,26278,30679, 1,30678,26219,30685,26219,30679,18967,30688, +18967,30678,26219,30693,18967,30679,26219,30696, 6,30654,30679,30701, + 7,30654,26331,30705,18967,30707,30677,30705,18967,30704,26331,30713, +18967,30654,20501,30717, 6,30716, 7,30716,26331,30723, 6,30717, +30723,30727, 7,30717,30721,30731,26219,30731,18967,30655,20413,30737, + 7,30736,26219,30741, 7,30737,26331,30745, 265,30091, 0,30748, + 6,30751,21605,30752,21605,30751, 6,30756, 6,30757, 7,30748, +30755,30763,30759,30763, 7,30749,30761,30769,18967,30091,26454,30772, +26375,30772,26292,30772,26331,30779, 11,30772,20501,30783, 15,30772, +27021,30772,20501,30789,20501,30773,25793,30793,27021,30793,24095,30793, +26415,30772,26219,30800,20187,30773,25793,30805,26219,30772,26415,30808, +24095,30772,20501,30813, 6,30773,26604,30817,29645,30817,25793,30817, +20103,30822,20103,30817,25793,30826, 7,30773,26331,30831,27805,30831, +23755,30831,25793,30831, 6,30772,30831,30841, 7,30772,26219,30845, +25793,30844,26331,30849,30817,30845,25793,30845,26415,30855,25793,30772, +20501,30859, 6,30858, 7,30858,26331,30865, 6,30859,30865,30869, + 7,30859,30863,30873,26219,30873,20103,30773,26219,30879, 7,30879, +25793,30883,21037,30091, 6,30887, 7,30887, 6,30886,30891,30893, + 7,30886,30889,30897,18967,30090,27530,30901,26142,30901,26009,30901, +26270,30901,28571,30901,26454,30901,26375,30901,24210,30901,27459,30901, +26219,30901,20273,30920,26415,30920,20413,30901,25793,30926,27021,30926, +24095,30926,27021,30901,20413,30934,20985,30901,24095,30901,20413,30940, +20352,30901,25793,30945,26292,30901,26331,30949, 7,30901,25793,30953, +20273,30955,26415,30955,20273,30952,25793,30961,25793,30952,26331,30965, +26415,30901,26219,30968,20273,30901,26219,30972, 7,30972,25793,30977, +25793,30901,20413,30980, 7,30981,26219,30985, 7,30980,26331,30989, +21314,21605,27530,30993,26142,30993,26270,30993,26009,30993,26454,30993, +26375,30993,28571,30993,24795,30993,24799,30993,24210,30993,21850,30993, +21845,30993,20413,31016,27459,30993,18967,30993, 6,31023,29645,31025, +26219,30993,20273,31028,26415,31028,27021,30993,20413,31034,20413,30993, +25793,31038,27021,31038,24095,31038,21845,31038,20985,30993,24095,30993, +20413,31050,26292,30993,26331,31055,20352,30993,25793,31059, 7,30993, +25793,31063,20273,31065,26415,31065,25793,31062,26331,31071,20273,31062, +25793,31075,26415,30993,26219,31078,25793,30993,20413,31082, 7,31083, +26219,31087, 7,31082,26331,31091,20273,30993,26219,31094, 7,31094, +25793,31099, 6,21605,27347,31103,27401,31103,27378,31103,27327,31103, +27380,31103,27329,31103,27263,31103,27377,31116,27285,31103,27059,31103, +27377,31122,27194,31103,27203,31103,27215,31103,27208,31103,27228,31103, +27233,31103,27188,31103,27187,31103,27184,31103,27183,31103,27294,31103, +27275,31103,27305,31103,27280,31103,27481,31103,27470,31103,27341,31103, +27394,31103,27122,31103,27390,31103,27337,31103,26796,31103,26802,31103, +30939,31103,30229,31103,30616,31103,31049,31103,28681,31103,20843,31180, +28686,31103,22287,31103,28661,31103,28649,31103,27502,31103,27513,31103, +25030,31103,24983,31103,30283,31103,25683,31103,21058,31103,29835,31205, +21195,31103,29835,31209,25629,31103,13894,31103,13919,31103,27498,31103, +27509,31103,27930,31103,27895,31223,27857,31103, 4,31226,27895,31229, + 4,31227,27907,31233,27479,31103,27468,31103,27500,31103,27511,31103, +26941,31103, 9457,31103,30118,31103,30185,31103,30123,31103,30105,31103, +26742,31103, 9444,31103, 9435,31103, 713,31260,30751,31102,30763,31265, +30221,31103,19397,31102,28777,31271, 3119,31103, 3027,31103, 3805,31103, + 4820,31103, 6469,31103, 3419,31103,27422,31103,27359,31103,27392,31103, +27339,31103,27343,31103,27396,31103,27404,31103,27351,31103,27352,31103, +27407,31103, 9539,31103,24554,31103,29659,31102,29645,31311,17294,31103, +26843,31103,30335,31103,26845,31103,25689,31103,30337,31103,30787,31103, +24552,31103,25157,31103,24881,31103,28387,31103,28386,31102,31335,31337, +28387,31102,28386,31103,31341,31343,27287,31103,27323,31347,30127,31103, +30117,31103, 803,31352, 3,31103, 0,31356,27277,31103,20273,31360, +27377,31360,27461,31103,20273,31366,27483,31366,27377,31366,27205,31103, +20273,31374,27377,31374,27218,31103,27316,31103,27398,31103,18967,31384, +27345,31103,18967,31388, 413,31103,30292,31103,26986,31103,26982,31103, + 9287,31103,27224,31103,20273,31402,27323,31403,21276,31103,29835,31409, +21496,31103,27021,31412,27021,31413,21277,31103,27021,31418,27021,31419, +21183,31419, 6441,31103, 759,31427, 4403,31103,17075,31103,16329,31103, + 4919,31103,17099,31103, 9362,31103, 5309,31103, 4547,31103,27291,31103, +20273,31446,27323,31447,26810,31103,26990,31103, 777,31103, 824,31103, + 244,31103, 4933,31103, 5297,31103,17077,31103,13019,31103,12705,31103, +13673,31103,18391,31103,18617,31103,14597,31103,27210,31103,27323,31481, +27472,31103,27323,31485,27300,31103,27323,31489,26317,31103,26476,31103, + 9134,31103, 1216,31103,19687,31103,26625,31500,19397,31103,29659,31505, +29835,31505,22655,31505,23622,31103,27410,31103,22139,31103,22163,31103, +24543,31103,24557,31103, 305,31103,21036,31102,26816,31527,25063,31527, +31245,31527,21069,31527,21267,31527,21305,31527,24953,31527,21259,31527, +20103,31543, 17,31527,20501,31527,26625,31548,26625,31527,20501,31552, +26415,31103, 9,31556,26940,31103,29645,31103,29659,31563,27418,31103, +27255,31566,27272,31103,27377,31570,27255,31103,27483,31574,27418,31574, +27377,31574,18967,31580,18967,31574,27377,31584, 8446,31103, 155,31103, +17049,31103, 1369,31593, 8861,31103,26625,31103,21127,31598,20843,31598, +19687,31598, 8337,31598,20103,31598, 7971,31598, 9565,31598,18967,31598, + 9731,31103,27021,31616,30613,31103,20885,31620, 9617,31102,26973,31625, + 8337,31103,24545,31628,26625,31628, 9,31628, 4819,31103, 713,31636, +13857,31103, 713,31640, 4543,31103,21372,31103,21315,31103,27190,31648, +27483,31648,25027,31648,27021,31649,20273,31657,27377,31657,27021,31648, +20273,31662,27323,31663,20273,31648,27021,31668,27021,31669, 11,31648, +21037,31103,29835,31677,20103,31679,20763,31676,29835,31683,20103,31676, +29835,31687, 713,31103,13857,31690, 9435,31690, 4819,31690, 803,31690, + 25,31103, 7971,31103,26625,31702,20884,31103,29659,31707,29835,31707, +20984,31103,28659,31713,28625,31713,30227,31713,22285,31713,17233,31103, + 903,31722,30091,31103, 9,31726,20162,31103,20171,31103,25027,31103, +21315,31734,27190,31103,21315,31738,21127,31103,26625,31742,20843,31103, +26625,31746,28681,31746, 9565,31103,26625,31752, 8997,31103, 9,31756, + 766,31103, 9455,31761,30121,31761, 3803,31761, 759,31761, 9,31103, +27021,31770,30091,31770, 9195,31770, 241,31770,26415,31770, 8997,31770, +27377,31770,23493,31770, 8337,31770,20103,31770, 1011,31103, 903,31792, + 241,31103, 9,31796,27297,31103,27282,31103,27323,31803,18967,31805, +18967,31802,27323,31809, 17,31803,20103,31103,24545,31814,26625,31814, +21037,31814,29835,31821, 9,31814,21037,31815,25793,31827,20273,31103, +27224,31830,27291,31830,27205,31830,27277,31830,27461,31830,21315,31830, +27021,31842,27021,31843,27021,31830,21315,31848,27231,31103,27377,31852, + 803,31103,30117,31856, 713,31856,20885,31103,27021,31862,30613,31862, +23493,31103, 9,31868,24545,31103,20103,31872, 8337,31872, 17,31873, + 11,31103,28715,31881,19631,31883,11949,31883, 4669,31883,21315,31880, + 8451,31103,27377,31103,27059,31894,27263,31894,27205,31894,27231,31894, +27461,31894,27272,31894,27277,31894,27255,31894,18967,31910, 9,31894, +18967,31894,27255,31916, 903,31103,17233,31920, 1011,31920, 257,31103, +27483,31103,21315,31928,27461,31928,27255,31928, 4,31102,23646,31937, +23642,31937,23606,31937,24349,31937,26086,31937,26062,31937,26068,31937, +24583,31937,24591,31937,19384,31937,24265,31957,25793,31937,19631,31960, +11949,31960, 4669,31960,11949,31937,23493,31968,25793,31968, 4669,31937, +23493,31974,25793,31974,19631,31937,23493,31980,25793,31980, 11,31937, +18967,31986,24265,31989,18967,31937, 11,31992,24265,31995, 11,31993, +23493,31999,25793,31999,23493,31937,11949,32004, 4669,32004,19631,32004, + 5,31102,26940,32013,20413,32015,18967,32012,26816,32019,24953,32019, +31245,32019, 17,32019,20501,32019,26625,32028,26625,32019,20501,32032, +26625,32013,18967,32036,20413,32039,18967,32037,20501,32043,18967,32013, +20413,32047,26625,32049,26625,32046,20413,32053, 4,31103,27857,32057, +27879,32059,27857,32056,27895,32063,20763,32056,29835,32067,29659,32067, +28715,32057,29083,32073,29091,32073,19631,32073,11949,32073, 4669,32073, +29129,32073,18967,32073,29077,32087,18967,32056,23147,32091,24819,32091, +28925,32091,28715,32091,20413,32099, 5,31103,27437,32102,19397,32103, +21605,32103,20762,32108,20762,32109, 586,32108, 586,32109,31993,32103, +25793,32119, 2,32103,21605,32123, 0,32122,31960,32127,25793,32127, +31937,32130,31937,32127,25793,32134,32073,32127, 0,32103,21605,32141, + 2,32140,31960,32145,25793,32145,31937,32148,31937,32145,25793,32152, +32073,32145, 586,32140, 586,32141,21605,32102,18966,32102,18966,32103, +21605,32167, 7,32169, 9,32167,31960,32167,25793,32167,31937,32176, +31937,32167,25793,32180,32073,32167, 1,32103,32125,32187,31937,32189, + 3,32103,32143,32193,31937,32195,28573,32103,28625,32103,20843,32201, + 9195,32103,20501,32103,21183,32103,27436,32102,27323,32211, 9377,32103, + 713,32215, 9875,32103,19396,32103,32165,32221, 9,32103,18967,32224, +32165,32227,18967,32225,32167,32231,20763,32102,22285,32235,28659,32235, +28625,32235,32111,32235,30227,32235,30772,32103,20501,32247,27021,32102, +27323,32251,18967,32253,18967,32250,27323,32257, 17,32251, 1369,32103, + 903,32103, 9,32265, 587,32102, 9455,32269,32159,32269,32115,32269, + 3803,32269,30121,32269, 759,32269,25673,32103,20501,32283,20763,32103, +32113,32287,27021,32287,30613,32287, 587,32103,32161,32295,32117,32295, +30117,32295, 713,32295,27021,32103,27483,32305,32105,32305,27418,32305, +27377,32305,18967,32312,18967,32305,27377,32316,31992,32103,32163,32321, +18967,32103,32163,32325,31937,32327,32165,32325, 9,32331, 9,32324, +32165,32335,30091,32324,20501,32339,31937,32324,32163,32343,31937,32103, +18967,32346,32163,32349,30091,32103, 9,32353,18967,32352,20501,32357, +18967,32353,20413,32361,18967,32102,24953,32365,32171,32365,32107,32365, +32172,32365,32167,32365, 9,32374, 9,32365,32167,32378,27021,32364, +27323,32383,27021,32365,27377,32387,21037,31102,26604,32391,31561,32391, +31615,32391,29645,32391,25793,32391,20103,32400,20103,32391,25793,32404, +21314,31103,23147,32409,24819,32409,28925,32409,28715,32409,20413,32417, +21036,31103,25673,32421,20501,32423,25087,32421,25691,32421,22357,32421, +24953,32421,21049,32421,21242,32421,21183,32421,20103,32438,27021,32420, +27323,32443,20103,32421,21183,32446,27021,32421,27377,32451,27436,31103, + 5,32454,27323,32457, 5,32455,27255,32461, 9195,31103, 9,32464, +27021,31103,21496,32468,21277,32468,20885,32468, 9,32468, 9731,32468, +21037,32468,21315,32468,20273,32482,27323,32483,21037,32469,21315,32469, +27205,32491,21036,32468,27323,32495,32489,32495,21036,32469,27231,32501, +32481,32501,20273,32468,21315,32506, 4,32469, 5,32469,27231,32513, +27272,32513,27255,32513,18967,32518,18967,32513,27255,32522, 9,32513, + 4,32468,32513,32529, 5,32468,27323,32533,18967,32535,32511,32533, +18967,32532,27323,32541, 17,32533,18967,32468, 5,32546,27323,32549, + 5,32547,27255,32553, 587,31103,30091,32557, 803,32559, 5,32556, + 9455,32563, 3803,32563,30121,32563, 759,32563, 5,32557, 9435,32573, + 803,32573,18967,31103,32391,32579,26625,32581,27398,32578,27345,32578, +21605,32578, 7,32589, 4,32591, 4,32590, 4,32589, 7,32596, + 16,32589,27282,32578,27323,32603,26625,32578,32391,32607,27377,32578, +27255,32610,20483,32579,25793,32615,27255,32578,27377,32618,21183,32579, +26625,32623, 4,32579,26142,32627,24849,32627,21605,32627, 7,32633, +25793,32627,20413,32636,20413,32627,25793,32640, 5,32579,32593,32645, + 4,32578,24819,32649,23147,32649,28925,32649,28715,32649,20413,32657, + 5,32578,24953,32661,32595,32661,32599,32661,32635,32661,32601,32661, +27021,32660,27323,32673,27021,32661,27377,32677,27021,32578, 5,32680, +27323,32683, 5,32681,27255,32687,20763,31103,18967,32691, 5,32692, + 5,32693,21037,32690,29835,32699,29659,32691,29835,32691,21036,32691, +21037,32691,25793,32709,30091,32691,20885,32713, 4,32691,32697,32717, + 5,32691,18967,32720,28681,32721, 4,32690,32723,32727,32695,32727, +29659,32727,29835,32727,32707,32727, 5,32690,28625,32739,22285,32739, +28659,32739,30227,32739,32709,32739,18967,31102, 5,32750,26816,32753, +24953,32753,31245,32753, 17,32753,26625,32753,20501,32762,20501,32753, +26625,32766, 5,32751, 9,32771,30091,32771,20501,32775,21526,21605, + 5,32778,26816,32781,24953,32781, 17,32781,26625,32781,20501,32788, +20501,32781,26625,32792, 5,32779, 9,32797,30091,32797,20501,32801, +18967,21605,20355,32804,20354,32804,26415,32805,26219,32810,26454,32805, +26375,32805,21127,32805,21183,32818,21248,32805,21273,32805,20986,32805, +20987,32805,28109,32804,28108,32805,32831,32833,31676,32805,31527,32837, +28109,32805,28108,32804,32841,32843,20352,32805, 4,32847,22729,32849, +22731,32847,32809,32847,20486,32805,26292,32805,26331,32859,19622,32804, +19623,32805,32863,32865,19623,32804,19622,32805,32869,32871, 386,32805, + 347,32805,21396,32804,21397,32805,32879,32881,21546,32804,21547,32805, +32885,32887,21397,32804,21396,32805,32891,32893,21547,32804,21546,32805, +32897,32899,20477,32805,27673,32805,20476,32805,27672,32805,21127,32804, +21087,32911,29031,32804,31881,32805,21543,32805,21087,32805,21127,32921, +31880,32805,21542,32805,26219,32805,26415,32928,27283,32805,31103,32933, +20353,32805,32807,32937,20487,32805,21403,32804,21402,32805,32943,32945, +21573,32804,21572,32805,32949,32951,21403,32805,21402,32804,32955,32957, +21573,32805,21572,32804,32961,32963,20985,32804,32829,32967,21401,32804, +32056,32805,29450,32805,29041,32975,28791,32975,29731,32975,29659,32975, +20984,32805,21400,32805,32971,32987,32103,32805, 7,32990,32365,32993, +32057,32805,29451,32805,32915,32999,20984,32804,32827,33003,20985,32805, +31103,33007, 7,33006,33003,33011,21400,32804,21401,32805,33015,33017, +21183,32805,21127,33020,20413,32805, 4,33025, 4,33024, 11,33024, +27533,32805, 17,33032,20273,32805, 7,33037, 7,33036, 15,33037, +25793,32805, 7,33045,26219,33047, 7,33044,26331,33051,21527,32805, + 17,33054,31103,32805, 4,33059,21037,33059,32421,33063,21037,33058, +31527,33067, 4,33058, 11,33058,21037,32805,31103,33075,32391,33077, +31103,33074,31527,33081,28715,32805,29031,33085, 7,33085,29759,33089, +21527,33085,29229,33093, 7,33084,29041,33097,29731,33097,29659,33097, +28791,33097, 15,33085, 580,32805, 1170,32805, 256,32805, 629,32805, + 1413,32805, 922,32805, 581,32805, 1171,32805, 767,32805,31103,33125, + 375,32805, 305,33128, 510,32805, 18,32804, 12,32804,23493,33137, + 1291,32805, 525,32805, 13,32805,22807,33145,33137,33145, 19,32805, +33135,33151, 18,32805, 13,32804, 12,32805,33157,33159, 19,32804, +33155,33163, 16,32805,32421,33167,25743,33167,32365,33167,32661,33167, +27533,33167,21037,33167,31103,33179, 10,32805,19333,33183,33137,33183, +31648,33183,19455,33183,24237,33183,22896,33183,19393,33183,19385,33183, + 9,33199,22807,33183, 9,33202,20413,33183, 9,33183,22807,33208, +21315,33183,31103,33212,31103,33183,21315,33216, 14,32804,32847,33221, +33041,33221,24953,33221,32975,33221,33097,33221,26816,33221,33167,33221, +33111,33221, 17,33221,20501,33221,26625,33240,26625,33221,20501,33244, + 10,32804,22149,33249,19441,33249,24213,33249,24059,33249,19656,33249, + 9,33249,19631,33260,19631,33249, 9,33264,23983,33249, 9,33269, + 524,32805, 1290,32805, 11,32804,21087,33277,20326,33277,33183,33277, + 9,33283, 9,33276,23493,33287,33183,33287,23493,33277, 9,33293, +20273,33277, 9,33296, 9,33277,20273,33300, 9,32804, 11,33304, +23493,33307,33183,33307, 11,33305,24265,33313,33249,33313, 15,32804, +32937,33319,32999,33319,26142,33319,33123,33319,20413,33319,25793,33328, +20273,33319,25793,33319,20413,33334, 11,32805,24567,33339,33157,33339, +24428,33339,20413,33338,31103,33338,33260,33339,33249,33339, 9,33352, +24265,33339, 9,33356, 9,33339,24265,33360,33249,33360, 483,33338, + 9,32805,30090,33368,30090,33369,30091,33368,33373,33375, 483,33368, + 11,33369,20273,33381,30091,33369,33371,33385,20501,33385, 17,32805, +19474,33390,19475,33391,33393,33395,21314,33390,19474,33391,19475,33390, +33401,33403,21314,33391,21315,33390,33407,33409,27533,33390,21527,33390, +33319,33391, 1275,33390,21315,33391,33399,33421, 305,32805, 375,33424, + 1011,32805, 7,33428,33221,33431, 15,33429, 7,33429, 241,32805, + 7,33438, 483,32805, 4,33442, 903,33442, 9,33442, 4,33443, + 11,33442, 903,32805, 9,33455, 483,33454, 1275,32805, 17,33460, + 7,32805,20985,33464,33003,33467,20985,33465,32985,33471,25672,33465, +30772,33465,25673,33465,30773,33465,21036,33465,30090,33465,20273,33464, +33221,33487,25793,33464,26331,33491,28715,33464,29731,33495,29659,33495, +28791,33495,29041,33495,33221,33495,18967,33465, 5,33506,30091,33506, + 5,33507,33485,33513, 17,33507,21605,33465, 4,33518,33513,33521, +21037,33518, 4,33519,33509,33527,33483,33527, 11,33519,25793,33465, +26415,33535, 1011,33464,33221,33539, 241,33464, 8495,33465,32103,33465, +20273,33547,32165,33547,21037,33465,21604,33553,21605,33552,33555,33557, +21605,33553,21604,33552,33561,33563,28715,33465,20273,33567,29533,33567, +30091,33465,18967,33572,33221,33465,20273,33577,23927,33465,20273,33581, +29031,33465,20273,33585,32013,33465,20273,33589, 15,33465,29835,33465, +29659,33465,29077,33597,32102,33465, 4,33465,24687,33603,25773,33603, +21605,33602,33513,33609,31830,33603,32645,33603,29485,33603,22729,33603, +31103,33603,20273,33620,20273,33603,31103,33624, 5,33465,18967,33628, +33527,33631,31103,33628,20103,33629,31103,33637, 5,33464,33519,33641, + 7,33641,21605,33645,33603,33647,33620,33641,33547,33641,33557,33641, +33475,33641,33525,33641,33481,33641,31648,33641,20413,33641,33603,33641, +31103,33666,21315,33641,31103,33670,31103,33641,33603,33674,21315,33674, + 4,33464,33507,33681,25743,33681,33601,33681,32365,33681,32661,33681, +33635,33681,33575,33681,33477,33681,33511,33681,33479,33681,32421,33681, +33221,33681,27533,33681,33629,33681,31103,33709,21037,33681,31103,33713, +31103,33465, 5,33716,33681,33719, 5,33717,33603,33723, 6,32804, +33624,33727,33713,33727,33085,33727,29229,33733,33179,33727,21036,33727, +33681,33739,33167,33739,28714,33727,33567,33745,18967,33727, 5,33749, +33603,33751, 5,33748,33681,33755,33167,33755,20273,33727,33603,33760, +33603,33727,20273,33764,29451,33727, 17,33727, 5,33726,26816,33773, +33681,33773,24953,33773,33487,33773,33041,33773,33495,33773,33097,33773, +32847,33773,32975,33773,33167,33773,33111,33773,33539,33773,33431,33773, + 17,33773,33465,33773,20273,33803,26625,33773,20501,33806,20501,33773, +26625,33810, 5,33727,33603,33815,18967,33814,33681,33819,33167,33819, + 9,33815,33465,33815,20273,33827,30091,33815,20501,33831,33465,33727, +18967,33834, 4,33837, 4,33836,21315,33834,21314,33834, 4,33835, +33843,33847, 5,33835,33841,33851,33845,33851, 4,33834,18967,33856, +33851,33859,33851,33857, 5,33834,33839,33865,33847,33865, 5,32805, +27953,33871,27743,33871,27555,33871, 2,33870, 0,33870, 3,33870, + 1,33870,33609,33871,33521,33871,30285,33871,33485,33871,32717,33871, +25101,33871,33027,33871,33061,33871,23193,33871,33593,33871,33451,33871, +20413,33871,20885,33871,33603,33871,27533,33913,23015,33871,20501,33917, +27741,33871,20501,33921, 9,33871,30492,33871, 6,33871,33879,33929, +33881,33929,20273,33929, 7,33871,33883,33937,33885,33937,25793,33937, +32805,33936, 4,33945,21605,33937,33947,33949, 5,33949,33945,33953, +33603,33949,30091,33936,21527,33937,27533,33937,21315,33963, 7,33870, +31648,33967,20413,33967,31103,33967,21315,33972,21315,33967,31103,33976, +21315,33871,21605,33981,33929,33983,30091,33871,20501,33987, 6,33986, + 7,33986, 6,33987,33993,33995, 7,33987,33991,33999, 4,32804, +27530,34003,26142,34003,26270,34003,26009,34003,33879,34003,33929,34012, +33881,34003,33929,34016,33930,34003,33932,34003,33939,34003,33941,34003, +33506,34003,33681,34029,33683,34003,33934,34003,33943,34003,26454,34003, +26375,34003,33769,34003,32937,34003,32999,34003,33664,34003,32941,34003, +33908,34003,32997,34003,28571,34003,33970,34003,33961,34003,18967,34003, +33465,34062,33681,34065,33465,34063,33641,34069,33206,34003,33123,34003, +33121,34003,24210,34003,33413,34003,33415,34003,32903,34003,32909,34003, +33771,34003,32917,34003,32927,34003,33275,34003,33143,34003,27459,34003, +33419,34003,33463,34003,33035,34003,33057,34003,33517,34003,26219,34003, +20273,34110,26415,34110,24265,34003,28233,34003,29077,34003,33183,34003, +20413,34122,33641,34003,20413,34126,33967,34003,20413,34130,33391,34003, +27533,34135,20413,34003,25793,34138,27021,34138,33641,34138,33871,34138, +33967,34138,33183,34138,24095,34138,27021,34003,20413,34154,33929,34003, +33879,34158,33881,34158,20273,34158,20985,34003,31103,34167, 11,34003, +21605,34171,33465,34003,18967,34174,33681,34177,24095,34003,20413,34180, +26292,34003,26331,34185,20352,34003,25793,34189,33936,34003,33883,34193, +33885,34193,25793,34193, 7,34003,25793,34201,20273,34203,26415,34203, +33871,34201,33879,34209,33881,34209,20273,34209,25793,34200,26331,34217, +20273,34200,25793,34221,33871,34200,33883,34225,33885,34225,25793,34225, +26415,34003,26219,34232,20273,34003,26219,34236,33929,34236, 7,34236, +25793,34243,25793,34003,20413,34246, 7,34247,26219,34251, 7,34246, +26331,34255,33871,34003,18967,34258, 6,34261, 6,34260,21605,34259, +33929,34267,21527,34258,31103,34259,21526,34258,20413,34258, 6,34259, +34271,34279, 7,34259,34265,34283,34275,34283, 6,34258,34267,34289, +18967,34288,34283,34293,34283,34289, 7,34258,33883,34299,33885,34299, +34263,34299,34279,34299,25793,34299, 4,32805, 2,34311, 7,34313, +27669,34311,23357,34311,32468,34311,28483,34311,27978,34311,31746,34311, +31742,34311,31500,34311,20413,34310,23014,34311,31103,34310,21605,34311, + 7,34338, 7,34339,33773,34343,33221,34343, 15,34338,27021,34311, +31103,34350,28411,34311,33629,34311,20413,34357, 483,34310,31103,34311, +27021,34362,20843,34362,21127,34362,19687,34362,19687,34311,31103,34372, +20843,34311,31103,34376,21127,34311,31103,34380, 15,34311,21605,34384, + 375,34311,33727,34311, 9,34391, 7,34311,21605,34394,25793,34395, + 265,34395,31103,34395, 7,34310,33755,34405,33819,34405,33773,34405, +33739,34405,25743,34405,32661,34405,32421,34405,32365,34405,33221,34405, +27533,34405,21037,34405,33727,34427,31103,34427,21037,34311,33929,34433, +26219,34433, 7,34433,25793,34439,28715,34311,20413,34443, 5,32804, +27965,34447,27948,34447,26816,34447,27796,34447,26898,34447,20353,34447, +29451,34447,32847,34447,33487,34447,33041,34447,32975,34447,33495,34447, +33097,34447,34343,34447,34315,34447,33527,34447,33518,34447,33641,34481, +33643,34447,24953,34447,20119,34447,33706,34447,34362,34447,32727,34447, +25129,34447,34333,34447,33029,34447,34337,34447,33071,34447,32691,34447, +34424,34447,34403,34447,32067,34447,31707,34447,26887,34447,32857,34447, +32973,34447,21605,34447,33465,34522,33641,34525,33465,34523,33681,34529, +33176,34447,33111,34447,33109,34447,31505,34447,23480,34447,33347,34447, +33349,34447,32905,34447,32907,34447,32919,34447,32925,34447,33273,34447, +33141,34447,25099,34447,33539,34447,34361,34447,33445,34447,33431,34447, +33367,34447,33453,34447,33031,34447,33073,34447,33533,34447,20187,34447, +19683,34447,20839,34447,21399,34447,33167,34447,27533,34586,33681,34447, +27533,34590,34405,34447,27533,34594,28625,34447,28573,34447,33339,34447, +20413,34603, 1171,34447,27787,34447,20501,34608,27533,34447,20501,34612, +33681,34612,34405,34612,33167,34612,20501,34447,27787,34622,26889,34622, +26625,34622,27533,34622,23457,34622,34395,34447, 17,34447,33465,34447, +29077,34639,20273,34639,21605,34638,33641,34645,28233,34639,24265,34639, + 11,34639, 6,34446,26816,34655,34405,34655,33041,34655,33097,34655, +34343,34655,33681,34655,32847,34655,33487,34655,32975,34655,33495,34655, +24953,34655,33167,34655,33111,34655,33539,34655,33431,34655, 17,34655, +33465,34655,20273,34689,20501,34655,26625,34692,26625,34655,20501,34696, + 7,34446,34635,34701,34579,34701,34311,34701,20103,34701,20186,34447, +34655,34711,34394,34447,34655,34715,31103,34447,34311,34718, 6,34447, +34706,34723,34395,34723,33039,34723,33089,34723,34397,34723,34341,34723, +20273,34723,34708,34723,23175,34723,24941,34723,34325,34723,28133,34723, +33437,34723,20103,34723,34701,34750,34701,34723,34311,34754,20103,34754, + 11,34723,34311,34723,34701,34762,20763,34723,26625,34767, 7,34447, +21605,34771,34311,34773,34655,34771,34311,34777,20103,34777,20103,34770, +34655,34783,34311,34770,34655,34787,23457,34447,20501,34790,26889,34447, +20501,34794,20103,34447, 7,34799,34723,34801, 7,34798,34655,34805, +26625,34447,20501,34808, 6,34809, 7,34809, 6,34808,34815,34817, + 7,34808,34813,34821,34311,34447,21605,34824,18967,34825, 6,34828, + 6,34829,31103,34824,21527,34825,21526,34825,20413,34825, 6,34825, +18967,34842,34827,34843, 7,34825,34723,34849,25793,34849,34833,34849, + 6,34824,34849,34857,34837,34857, 7,34824,34655,34863,34845,34863, +34831,34863,34843,34863,34839,34863, 6,32805,34236,34875,34120,34875, +34433,34875,20962,34875,21001,34875,34118,34875,34355,34875,25648,34875, +21314,34874,34891,34893,25649,34875,33087,34875,21314,34875,34116,34875, +23017,34875,34335,34875,21937,34875,34170,34875,34173,34875,21959,34875, +21315,34874,34897,34917,34385,34875,34387,34875,24875,34875,33332,34875, +33435,34875,33043,34875,33107,34875,34349,34875,21605,34875,21315,34936, +34893,34939,21315,34937,34901,34943,20273,34875,34003,34946,33319,34946, +20885,34875,20843,34952,21315,34875,21605,34956,34893,34959,19475,34875, +26625,34963,24265,34875,34003,34966,29077,34875,34003,34970,28233,34875, +34003,34974,20843,34875,20885,34978, 11,34875,34003,34982,33319,34875, +20273,34986,34003,34875,29077,34990,20273,34990,28233,34990,24265,34990, + 11,34990,30993,34875,30901,34875,20984,34875,20839,35007, 5,34875, +20763,35011,20843,35013,20763,35010,20839,35017,20763,34875, 5,35020, +20839,35023, 5,35021,20885,35027, 7,32804,34736,35031,34459,35031, +34461,35031,29691,35031,29340,35031,28714,35031,33603,35043,21605,35031, + 5,35047,33681,35049,34405,35049,33167,35049, 5,35046,33603,35057, +34607,35031,34637,35031,33924,35031,34447,35031, 11,35031,32103,35031, +29835,35031,29659,35031,20273,35031,34723,35076,29229,35076,35011,35031, +34723,35031,20273,35084, 567,35031,29229,35031,20273,35090, 9,35031, +33871,35094,33871,35031, 9,35098, 4,35030, 5,35030,35083,35105, +34875,35105,35010,35031,35103,35111, 4,35031,35108,35115, 7,35114, +35049,35119,35105,35115,34875,35122,34875,35115,35105,35126, 483,35115, + 5,35031,21605,35132,33603,35135,35103,35133,34875,35139,34875,35132, +35103,35143,34875,35031,18967,35147, 4,35148, 4,35149,21315,35147, +21314,35147, 4,35147,18967,35158, 5,35147,35115,35163,35153,35163, + 4,35146,35163,35169,35155,35169, 5,35146,35103,35175,35161,35175, +35151,35175,35159,35175,35157,35175, 0, 4,14087,35187,14103,35188, + 4983,35187, 5001,35192,14104,35187, 5002,35187,14088,35187,14091,35201, + 4984,35187, 4987,35205,14093,35187, 4989,35187,28758,35187,29229,35213, +28741,35187, 2,35216,29229,35219,20501,35186,19463,35223,19317,35223, +19301,35223,20009,35223, 53,35186,19463,35233,20009,35233,19317,35233, +19053,35187, 989,35241, 1201,35187,18967,35245, 1203,35247,17673,35186, +17685,35251,21405,35186,21489,35255,19513,35186,19463,35259,20009,35259, +19317,35259,10157,35187, 17,35267,10159,35269,20343,35187,18967,35273, +20345,35275, 1179,35187, 527,35186,19301,35281,19916,35186, 4,35285, + 7,35286, 7,35285, 4,35290, 16,35285,19069,35186,19301,35297, + 1290,35186, 0,35301, 2,35302, 2,35301, 0,35306,18966,35301, +21530,35187,20501,35313, 1489,35187,16859,35187, 11,35318, 9875,35321, +21529,35187, 11,35324,20501,35327, 147,35187,16860,35187, 9875,35333, + 987,35187, 8069,35187, 11,35338, 8067,35341, 8070,35187, 8067,35345, +27519,35187,27525,35187,26322,35187,26566,35187,26102,35187,26276,35187, +26105,35187,33464,35187,29600,35187,32295,35187,31856,35187,25636,35187, +29229,35371, 9,35371,28555,35187,29229,35377, 9,35377,19301,35187, +20501,35383, 527,35383,19069,35383, 4266,35187, 8056,35187, 8147,35187, +20739,35187, 9997,35187, 8784,35187, 8622,35187, 8824,35187, 8702,35187, + 8697,35187, 8676,35187, 8635,35187,20828,35187,19772,35187,19890,35187, +19774,35187,19894,35187,19382,35187,19458,35187,32557,35187, 803,35429, +19463,35187,20501,35433, 53,35433,19513,35433,20009,35187, 53,35441, +20501,35441,19513,35441,19317,35187,20501,35449, 53,35449,19513,35449, +19914,35187,21066,35187,20575,35187,21312,35187,21270,35187, 694,35187, + 9377,35467,16920,35187, 9,35471,17621,35187, 9,35475, 4213,35187, + 375,35478, 8757,35187, 375,35482, 9530,35187, 9875,35487,16091,35487, +20501,35487,19751,35187, 53,35495, 253,35495, 117,35495, 71,35495, + 1471,35495, 1397,35187, 9592,35187,16514,35187,19380,35187,13836,35187, +20595,35187, 9911,35187, 9983,35187, 9905,35187,17566,35187,16816,35187, +17910,35187,18880,35187,14103,35187,14087,35532, 5001,35187, 4983,35536, +20597,35187,26219,35187, 241,35542, 587,35543, 8647,35187, 241,35548, + 587,35549,14073,35187, 586,35554,14091,35557, 586,35555,14103,35561, + 4971,35187, 586,35564, 4987,35567, 586,35565, 5001,35571, 1291,35187, +35305,35575,35309,35575, 1871,35575, 1889,35575, 3,35575,19667,35575, +12345,35575, 5123,35575,19067,35575,35311,35575,18967,35575, 1297,35597, +35301,35597,19917,35187,35289,35603,35293,35603,19939,35603,19945,35603, + 6,35603,19947,35603,35295,35603, 17,35603,19931,35619,35285,35619, + 1108,35187, 11,35625,22806,35187, 9,35629, 524,35187, 3,35633, + 3477,35633, 2007,35633,19051,35633,19667,35633,12345,35633, 5123,35633, +18967,35633, 555,35649, 713,35187, 1325,35187,17455,35655,10001,35187, + 1505,35659, 9513,35187, 11,35662, 9875,35665,16091,35665,20501,35665, +19396,35187, 483,35672,17455,35187,13737,35676, 803,35187,31103,35680, +31103,35187, 803,35684, 113,35187, 375,35187, 587,35691, 9195,35693, + 4213,35690, 7971,35690, 8757,35690, 587,35690, 9377,35703, 9565,35690, +18967,35690,19892,35187, 9,35710, 510,35187,18967,35714, 587,35187, + 375,35718, 9377,35721, 9377,35719, 375,35725, 483,35187,19396,35728, + 587,35729, 11,35728, 5123,35735,12345,35735, 3477,35735, 2007,35735, +19667,35735,19051,35735,18967,35735, 555,35749,18967,35728, 9,35752, + 9,35728,18967,35756, 1275,35187,16833,35760, 9,35763, 903,35761, + 9,35761,21575,35187,18967,35771,21577,35773,21581,35773,21585,35773, + 6685,35187, 7519,35187, 17,35783, 7521,35785, 7525,35785, 7529,35785, +21258,35187, 241,35792,16722,35187,32103,35187, 9,35799,10310,35187, + 8684,35187, 241,35804, 331,35805,26292,35187, 241,35810, 482,35186, + 2,35815, 0,35816,35633,35819,35735,35819, 0,35815, 2,35824, +35633,35827,35735,35827,35749,35815,35649,35815,18966,35815,35735,35837, +35633,35837, 1368,35186,25793,35843, 8495,35843,23755,35843,10055,35843, + 4329,35843, 4311,35843,26331,35843,27805,35843, 3785,35843, 8717,35843, +10351,35843, 586,35186,31761,35867,32563,35867,32269,35867, 7,35867, + 4,35874, 4,35867, 7,35878, 16,35867, 767,35867,31103,35885, + 482,35187, 587,35889, 1274,35187, 587,35186, 9262,35895, 375,35895, + 9195,35898, 7,35895, 265,35903, 9195,35905, 9195,35895, 375,35908, + 483,35895, 483,35186, 1369,35186,19463,35917,19301,35917,20009,35917, +19317,35917, 1368,35187, 7,35927, 586,35187,14073,35930,14091,35933, + 4971,35930, 4987,35937,31856,35931,32295,35931, 7971,35931,14073,35931, +14087,35947, 4971,35931, 4983,35951,31103,35931, 803,35954, 803,35931, +31103,35958, 483,35931, 1531,35187,13737,35187, 1487,35966,17455,35966, +16833,35187, 1196,35972, 1196,35973, 1197,35972,35977,35979, 1197,35973, +35975,35983, 1275,35972, 9,35987, 767,35187,31103,35991, 1487,35187, +13737,35994,16937,35187, 17,35999, 9377,36001,17573,36001,16947,36001, + 1347,35187,18967,36009, 555,36011, 251,36011, 1365,36011, 1351,36011, + 21,36011,20762,35187, 241,36022, 256,35187,25793,36026,25793,36027, + 8495,36026, 8495,36027,18967,36026,21037,36026, 972,35187, 9377,36041, + 629,35187,18967,36044, 340,35187, 9377,36049,27978,35187,28353,36053, +30003,36053,29759,36053,34723,36053,21183,36053, 2,36053,28337,36065, +28189,36065,28191,36053, 959,36053, 759,36053, 47,36053, 66,35187, + 89,35187, 7,36080, 973,35187,35895,36085,21527,35187,21605,36088, +29229,36091, 9,36091, 145,35187, 165,35187, 305,35187, 25,35187, +27533,35187, 1505,36105, 331,35187,17455,36109, 59,35187,17455,36113, +29599,35187, 7,36116,26546,35187, 7,36120, 8812,35187, 7,36124, +10291,35187, 3,36129, 3,36128, 9565,35187, 375,36134, 8495,35187, + 256,36138, 7,36139, 375,36143, 7,36138, 241,36146, 331,36147, + 241,36138, 7,36152, 7971,35187, 375,36156,25793,35187, 256,36160, + 7,36160, 241,36164, 241,36160, 7,36168,32805,35187, 7,36172, +19888,35187, 7,36176,18967,35187, 256,36180, 629,36180, 510,36180, + 375,36180, 483,36180, 9,36190, 9,36180, 483,36194, 7,36180, + 241,36198, 7,36181,25793,36203, 8495,36203, 241,36180, 7,36208, + 241,35187,26292,36212,26219,36212, 8684,36212, 8647,36212,20762,36212, +21258,36212, 7,36212,25793,36226,25793,36227, 8495,36226, 8495,36227, +18967,36226,21037,36226,25793,36212, 7,36240, 8495,36212, 7,36244, +18967,36212, 7,36248,21037,36212, 7,36252, 9,35187,19892,36256, + 1011,36256, 11,36261,18967,36256, 483,36264, 483,36256,18967,36268, + 903,35187, 7,36272, 9377,36275,19913,35187, 6,36279, 7,36279, + 6,36278,36283,36285, 7,36278,36281,36289, 15,35187,25381,36293, + 483,36293, 2,36293,28189,36299, 1011,35187,16710,36303,16711,36303, +16710,36302,36307,36309, 0,36303, 6,36312,16711,36302,36305,36317, + 9,36302, 11,36321, 1275,36302,36315,36325, 11,36303, 9,36329, + 6,36302, 3,36332, 6,36303, 0,36336,36325,36339, 7,36303, +25793,36343, 8495,36343,21605,35187,21037,36348,21105,36348,21527,36348, +29229,36355, 9,36355,21549,36348,19625,36348,21526,36348, 5,36364, + 922,36349, 918,36349, 1505,36349, 1419,36349, 668,36349, 483,36349, + 903,36378, 587,36349, 9,36382, 9,36349, 903,36386, 11,36387, + 587,36386, 15,36348, 11,36348, 9,36397, 6,36348,21036,36400, + 5,36400,18967,36404,18967,36400, 5,36408, 7,36348,30003,36413, +29759,36413,34723,36413,21183,36413, 959,36413, 47,36413, 759,36413, + 6,36349,36353,36429,36361,36429,36351,36429,36363,36429, 7,36349, +36403,36439,31527,36439,22269,36439,28791,36439,22263,36439,32781,36439, +36367,36439,21105,36439,21549,36439,29953,36439,29731,36439,34655,36439, +33773,36439,29835,36439,29659,36439,34447,36439,32753,36439,32019,36439, +29041,36439,23929,36439,22205,36439,19625,36439,33221,36439,36407,36439, +36411,36439,18967,36348, 5,36490, 6,36493,36413,36495, 6,36492, +36439,36499,36439,36493, 14,36490,36439,36505, 6,36491,36395,36509, + 6,36490, 5,36512,36439,36515, 903,36349, 7,36519, 9,36518, + 483,36518, 265,35187,17455,36527, 5843,36527, 483,36527, 587,36527, + 9,36527, 7,36526, 9377,36539, 7,36527, 11,35187, 8069,36544, + 8067,36547,16859,36544, 9875,36551,21529,36544,20501,36555,16091,36545, + 9513,36559, 7,36544, 2,36563, 7,36564,36545,36567, 1,36564, +36569,36571, 1,36563, 2,36574, 0,36544, 2,36579, 0,36580, +36545,36583, 6,36580,36585,36587, 6,36579, 2,36590, 482,36579, + 1368,36544, 2,36597, 1,36598, 1,36597, 2,36602,18966,36544, + 2,36607, 6,36608, 6,36607, 2,36612, 482,36607, 9513,36544, +20501,36619, 9875,36619,16091,36619,20501,36545,21529,36627, 9513,36627, + 9875,36545,16859,36633, 9513,36633, 1369,36545, 1989,36639, 3587,36639, +36601,36639,36605,36639, 5303,36639,36571,36639,36577,36639, 0,36639, + 4897,36639,17041,36639,18967,36545,20281,36661,20345,36661,36611,36661, +36615,36661,36587,36661,36593,36661, 7,36661,20285,36661,36595,36661, +36617,36661, 483,36661,20273,36683,36579,36683,36607,36683,21605,36544, + 9,36691, 483,36544,35819,36695,35827,36695, 3477,36695, 2007,36695, + 1,36695,36639,36705,36655,36695,19051,36695,19667,36695, 5123,36695, +12345,36695,35837,36695,18967,36695, 555,36721,35815,36721, 3,36544, + 3,36545, 375,36729,21308,35187, 7,36732,16481,35187, 7,36736, +21037,35187, 256,36740, 7,36740, 241,36744, 241,36740, 7,36748, +16531,35187, 7,36752, 1011,36753, 7,36757,18797,35187, 7,36760, +17827,35187, 7,36764,16783,35187, 7,36768, 2,35186,27138,36773, +28047,36773,26454,36773, 8770,36773,26375,36773, 8729,36773, 8697,36773, + 8784,36773,28197,36773, 4266,36773, 9262,36773,28111,36773,27128,36773, +28027,36773,28117,36773,28177,36773,31872,36773,28063,36773,31598,36773, +24545,36773,31103,36812, 8684,36773, 8717,36817, 331,36817,26292,36773, +26331,36823, 8647,36773, 8757,36826,26219,36773,27021,36830,26415,36830, +26415,36773,26219,36836, 8757,36773, 8647,36840, 375,36840, 4213,36773, + 375,36846,28153,36773,26860,36773,28213,36773,35931,36773,28159,36773, +31770,36773, 347,36773, 386,36773, 9750,36773,27978,36773,26331,36869, +27805,36869, 1325,36869, 331,36869, 59,36869,23755,36869,25793,36869, + 265,36869, 340,36773, 331,36887,31103,36773,24545,36890,26625,36890, + 9,36890, 375,36773, 8757,36898, 4213,36898, 9195,36898,27021,36898, + 305,36898, 483,36773, 9195,36773, 375,36912,10001,36773, 9,36917, + 1325,36773, 1275,36773, 331,36773,27533,36773, 9,36927, 305,36773, + 375,36930,10291,36773, 483,36935,27021,36773,26219,36938, 375,36938, + 7,36773,25793,36944,26331,36947, 8495,36944, 8717,36951, 331,36951, +25793,36945,26415,36957,27021,36957, 8495,36945, 8757,36963,26625,36944, + 9731,36944,21605,36945,24545,36971,26625,36971, 9,36971, 265,36945, + 8757,36979, 4213,36979,27021,36979, 9195,36979, 305,36979,21605,36944, +26331,36991,27805,36991,23755,36991, 59,36991, 331,36991, 1325,36991, +25793,36991, 265,36991, 265,36944, 331,37009, 8647,36945,26219,36945, + 59,36773, 7,36772,25793,37019, 8495,37019,23755,37019,10055,37019, + 4329,37019, 4311,37019, 3785,37019,27805,37019,10351,37019, 8717,37019, +26331,37019, 9,36773,31103,37042, 15,36773,31103,37047, 9731,36773, + 7,37050,21605,36773, 15,37055,31103,37057, 6,37055, 5,37060, + 7,37055,26219,37065, 375,37065, 6,37054,37057,37071, 7,37054, +26331,37075,37063,37075,27805,37075,23755,37075, 59,37075, 331,37075, + 1325,37075,25793,37075, 265,37075, 265,36773, 7,37095, 8495,37097, + 375,37097, 7,37094, 331,37103,26625,36773,31103,37106, 7,37106, + 8495,36773, 7,37112, 8717,37115, 331,37115, 9,37113, 483,37113, + 7,37113, 8647,37125, 375,37125,25793,36773, 7,37130,26331,37133, + 483,37131, 9,37131, 7,37131,26219,37141, 3,35186,20305,37145, + 9641,37145,20309,37145, 9645,37145, 4,37145, 7,37154,35603,37157, + 9719,37145, 16,37145,35603,37163,35619,37145,21081,37145,20323,37145, +20387,37145, 9701,37145,35719,37145, 524,37145,20273,37179, 9617,37179, + 1291,37145, 9617,37185,20273,37185, 305,37145, 375,37190, 386,37145, + 347,37145, 767,37145,31103,37199, 483,37145, 11,37202, 9617,37205, +20273,37205, 375,37145, 305,37210, 7,37145, 3,37215,18967,37217, +35187,37219, 4,37214,35603,37223,37221,37223, 3,37214,35187,37229, + 11,37145, 483,37232, 9617,37235,20273,37235, 6,35186,19975,37241, + 0,37241, 2,37244,35575,37247,35633,37247,35735,37247,36695,37247, +18966,37241,35735,37257,35633,37257,35575,37257,36695,37257,35749,37241, +35649,37241,36721,37241,35597,37241,19365,37241,19221,37241,19917,37241, +10107,37279, 587,37241, 3,37241, 2,37241, 6,37287, 17,37289, +35187,37291, 0,37286,35575,37295,35633,37295,35735,37295,36695,37295, +37293,37295, 6,37286,36544,37307,37295,37309, 11,37307,35187,37312, +37295,37315,35187,37307, 11,37318,37295,37321, 7,35186, 4,37325, + 3,37326, 3,37327,35927,37331, 902,37325,25525,37325, 1325,37325, +10001,37325, 903,37341, 1275,37325, 59,37325,25793,37325, 587,37349, + 8495,37325, 587,37353, 2,37325, 7,37356,37331,37359, 903,37357, +10311,37357, 3,37325, 4,37366,27533,37325, 903,37371, 331,37325, + 2,37324,25793,37377, 8495,37377,23755,37377, 4329,37377,10055,37377, +27805,37377, 3785,37377, 4311,37377,26331,37377, 8717,37377,10351,37377, +21605,37325, 903,37401, 155,37401, 265,37325, 7,35187, 5045,37408, + 805,37409, 9195,37413, 4,37408,37279,37417,19916,37417,37241,37421, + 1275,37417,18967,37424,37241,37427,18967,37417, 1275,37430,37241,37433, +26546,37408,32805,37408,29599,37408, 652,37409, 9195,37443, 8812,37408, +19888,37408,19913,37408,21308,37408,16481,37408,37285,37409,16783,37408, +17827,37408,18797,37408,26498,37409, 8796,37409, 8495,37408, 241,37468, + 331,37469,25793,37408, 241,37474,25793,37409, 241,37479, 587,37478, + 8495,37409, 241,37485, 587,37484,21037,37408, 241,37490, 88,37409, +37411,37495,16531,37408, 587,37409, 265,37500, 9195,37503,26625,37501, +25793,37501, 8495,37501,25793,37500, 8495,37500, 9731,37501, 265,37409, + 587,37518, 9195,37521, 4213,37519, 7971,37519, 8757,37519, 587,37519, + 9377,37531, 9565,37519, 89,37409, 5069,37537, 9195,37537,27021,37537, + 483,37409, 1275,37409, 9,37547,26503,37409, 8809,37409,18967,37408, + 241,37554, 241,37408,25793,37558,25793,37559, 8495,37558, 8495,37559, +18967,37558,21037,37558, 903,37408, 9377,37573,21605,37408,30003,37577, +29759,37577,34723,37577,21183,37577,37495,37577, 959,37577, 47,37577, + 759,37577, 265,37408, 9377,37595, 903,37409,31103,37598,31920,37409, +31103,37409, 903,37604, 375,37409, 2,37408,37331,37611,37495,37611, + 3,37408,14072,37616, 4970,37616,14072,37617,14073,37616,37623,37625, + 4971,37616, 4970,37617,37629,37631, 7,37617, 4,37635, 1,37616, +37537,37639, 9,37639,36773,37617,37537,37617, 7971,37617,14073,37617, +37619,37651, 4971,37617,37621,37655, 483,37617, 9,37617, 2,37409, +37369,37663,37329,37663,37637,37663, 7,37662,37617,37671,37335,37663, + 9362,37663,27218,37663,37642,37663,37639,37663, 9,37682,28189,37663, + 1,37663, 5,37688,37616,37690,37617,37691,37693,37695,37671,37694, +37616,37691,37617,37690,37701,37703,37672,37691,37671,37691,37617,37708, + 5,37663, 1,37712,37616,37714,37617,37715,37717,37719,37671,37718, +37616,37715,37617,37714,37725,37727,37672,37715,37671,37715,37617,37732, +28337,37663,37660,37663,27021,37663, 9,37740, 9195,37663, 9,37744, + 9,37663,37639,37748,27021,37748, 9195,37748,37617,37748,37241,37663, +37617,37663, 1,37760, 5,37762, 5,37760, 1,37766, 7,37761, +37691,37771,37715,37771,37765,37771,37769,37771, 9,37760, 3,37409, +37577,37783, 9,37785, 16,37783, 4,37783, 7,37790, 7,37783, + 4,37794,18967,37783,35187,37799,37789,37801,37793,37801,37797,37801, + 9,37782,37577,37809,37611,37809,37611,37783, 1,37815, 5,37816, + 5,37815, 1,37820, 7,37814,37819,37825,37823,37825, 9,37815, + 9,37409, 3,37832,37577,37835,37611,37835, 3,37833,37663,37841, + 3,35187,10104,37844,10105,37844, 0,37845, 5,37850, 4,37844, +36773,37855,10104,37845,37849,37859,10105,37845,37847,37863,36945,37845, +37855,37867,10291,37844,36944,37845, 265,37873,37663,37845, 7,37877, +37691,37879,37715,37879, 1369,37845, 11,37885, 9617,37887,37853,37887, +20273,37887, 11,37844, 6,37844, 7,37844,14072,37898, 4970,37898, +14073,37898,14072,37899,37905,37907, 4971,37898, 4970,37899,37911,37913, +35187,37899,36773,37899, 7971,37899,14073,37899,37901,37923, 4971,37899, +37903,37927, 483,37899, 6,37845,37856,37933,37855,37933,36773,37936, + 1,37933, 5,37940,37898,37942,37899,37943,37945,37947,37898,37943, +37899,37942,37951,37953, 5,37933, 1,37956,37898,37958,37899,37959, +37961,37963,37898,37959,37899,37958,37967,37969,36773,37933,37855,37972, +37899,37933, 7,37845, 265,37979,36773,37981,35187,37978, 2,37985, + 4,37979,37987,37989, 3,37989,37985,37993,37663,37989,36773,37978, + 265,37999,37897,37979,36773,37845, 4,38005,37933,38007, 7,38005, +37943,38011,37959,38011,14073,38011, 4971,38011, 6,38004, 1,38021, + 5,38022,38011,38025, 5,38021, 1,38028,38011,38031,38007,38021, + 7,38004, 265,38037,18967,37845, 4,38041,37933,38043, 256,38041, + 7,38041, 241,38048, 241,38041, 7,38052, 2,35187,29229,38057, +28741,38059,28741,38056,29229,38063, 0,38057, 0,38056,28714,38069, +37185,38069, 1290,38069,37145,38075,37179,38069, 525,38069,37145,38081, +37887,38069,37205,38069,37235,38069, 5,38069,21605,38090,21605,38069, + 5,38094, 1275,38069, 17,38098,37145,38101, 17,38069, 1275,38104, +37145,38107, 1505,38057,17455,38057, 903,38112,17494,38057, 483,38057, +37214,38057,14073,38121, 4971,38121,37783,38057, 587,38057,10373,38057, + 9,38057, 155,38057, 6,38056, 1,38137, 5,38138,38121,38141, + 5,38137, 1,38144,38121,38147,37145,38137, 587,38137, 7,38056, +37331,38155, 6,38057, 5,38158,18966,38159, 0,38159, 2,38164, + 2,38159, 0,38168, 1369,38159, 17,38159,35187,38175,38163,38177, +38167,38177,38171,38177, 7,38057,38141,38185,37145,38187,38147,38185, +37145,38191,34447,38185,28715,38185,38069,38197,38071,38185,29659,38185, +29835,38185,38093,38185,38097,38185, 4971,38185,37145,38211,14073,38185, +37145,38215,37145,38184,38141,38219,38147,38219,14073,38219, 4971,38219, +21605,38185, 903,38229,37145,38057, 4,38232, 6,38233, 1,38237, + 5,38238, 5,38237, 1,38242,38235,38237, 7,38233, 265,38249, + 7,38232,38241,38253,38245,38253,38141,38253,38147,38253,14073,38253, + 4971,38253, 903,38057,17455,38266, 6,35187, 4,38271, 1,38272, + 0,38270, 17,38277,37844,38278,37845,38279,38281,38283,37845,38278, +37844,38279,38287,38289, 1369,38271, 2,38270, 587,38295, 3,38270, + 2,38271, 1275,38301, 1, 5,35962,38304,35963,38304,37760,38304, +37771,38311,37976,38304,38002,38304,37761,38304,37977,38304,37815,38304, +37825,38323,38003,38304,37930,38304,37658,38304,37931,38304,37659,38304, + 77,38304, 3095,38337, 1917,38337, 120,38304, 3013,38343, 1897,38343, + 79,38305, 3113,38349, 125,38305, 3023,38353,35929,38305,37885,38357, +37916,38305,37885,38361,38021,38304,38011,38365,38237,38304,38253,38369, + 4597,38305, 6625,38373, 113,38304, 4696,38377, 9,38377, 4669,38380, + 4669,38377, 9,38384,37896,38304,37898,38304,37933,38391, 483,38391, +37616,38304,37663,38397, 483,38397, 59,38304,23023,38403,25483,38403, +22677,38403,27533,38304,23023,38411,22677,38411,22605,38411,25483,38411, +37663,38304,37617,38420,37672,38421,37671,38421,37617,38426,37771,38421, +37879,38421,37617,38421,38397,38435,37671,38434,37933,38304,37899,38440, +38011,38441,37899,38441,38391,38447,37897,38304,38137,38304,38121,38453, +38219,38453,38185,38453,37145,38459,38253,38453,37617,38304,37663,38464, + 483,38464,37899,38304,37933,38470, 483,38470, 979,38305,21605,38477, + 987,38479,21879,38305, 1205,38483,29229,38304,29375,38487,17105,38304, +17117,38491,23755,38304,23023,38495,22677,38495,25483,38495,26863,38305, +21605,38503,26865,38505,10091,38305, 15,38509,10093,38511, 1009,38305, + 675,38304,22605,38517, 1418,38304, 1,38521, 3,38522, 3,38521, + 1,38526,21604,38521,21895,38304,22605,38533,25380,38304, 5,38537, + 6,38538, 6,38537, 5,38542, 14,38537, 1365,38305,17573,38305, + 9,38550,10001,38553,27996,38305,27533,38557, 1203,38305,17574,38305, +10001,38563, 177,38305,27995,38305, 9,38568,27533,38571, 7963,38305, + 9,38574, 7961,38577, 7964,38305, 7961,38581, 120,38305, 77,38305, +37758,38305,37972,38305,37457,38305,37867,38305,35912,38305,35733,38305, +35732,38305,35913,38305,22605,38305,27533,38605, 675,38605,21895,38605, +36856,38305, 483,38613,37644,38305, 483,38617,37918,38305,37933,38621, + 483,38621,37177,38305, 483,38627, 4798,38305,15896,38305,23023,38305, + 59,38635,27533,38635,23755,38635,22677,38305,27533,38643, 59,38643, +23755,38643,25483,38305, 59,38651,27533,38651,23755,38651,25368,38305, +30290,38305,27127,38305,30762,38305,30566,38305, 843,38304,35187,38669, + 1531,38304, 842,38304, 1530,38304,35930,38305,38675,38679,38307,38679, + 9492,38305,10001,38685,13755,38685,27533,38685,24597,38305, 59,38693, + 331,38693, 97,38693, 141,38693, 1325,38693, 629,38305,37896,38305, +37610,38305,37783,38709,37616,38305,38467,38713,38423,38713,38311,38713, +38331,38713,38469,38713,38465,38713,37663,38725, 483,38725,37898,38305, +38473,38731,38443,38731,38315,38731,38329,38731,38475,38731,38471,38731, +37933,38743, 483,38743,37866,38305,37456,38305,17508,38305, 9257,38305, +27149,38305, 9353,38305, 9253,38305,38253,38305,38137,38763,38237,38763, + 483,38763,38219,38305,38137,38771, 483,38771,38121,38305,38137,38777, + 483,38777,15897,38305, 4799,38305,27161,38305,20187,38305, 483,38789, + 8387,38305, 483,38793, 9541,38305,15879,38305, 483,38798, 4787,38305, + 483,38802,16125,38305, 3,38806, 2,38806, 842,38806, 843,38807, +38813,38815, 842,38807, 843,38806,38819,38821, 2,38807,38809,38825, + 3,38807,38811,38829, 5121,38305, 3,38832, 2,38832, 842,38832, + 843,38833,38839,38841, 842,38833, 843,38832,38845,38847, 2,38833, +38835,38851, 3,38833,38837,38855,35729,38305, 587,38859, 587,38858, +19384,38305, 483,38865, 669,38305, 1011,38869, 1086,38305, 483,38873, + 1418,38305, 1011,38877,25381,38305,38541,38881,38545,38881,25423,38881, +25429,38881, 7,38881,25431,38881,38547,38881, 15,38881,25415,38897, +38537,38897, 1419,38305,38525,38903,38529,38903, 1801,38903, 1875,38903, + 2,38903,23983,38903,10603,38903, 4625,38903,21893,38903,38531,38903, +21605,38903, 1421,38925,38521,38925, 1108,38305, 9565,38931, 246,38305, + 483,38935, 294,38305, 8495,38939,35931,38305,38669,38943,38309,38943, + 483,38943,36773,38942, 483,38951,19396,38305,35718,38305,37885,38957, +37202,38957,37145,38957, 483,38962, 483,38957,37145,38966, 668,38305, + 2,38971, 3507,38971, 1971,38971,21783,38971,23983,38971,10603,38971, + 4625,38971,21605,38971, 713,38987,20060,38305, 483,38991, 713,38305, +16833,38995,38011,38305,37933,38999,38021,38999, 483,38999,37285,38305, +37409,39006,36945,38305,37845,39011,37845,39010, 483,39011,37215,38305, + 3,39019,38057,39019, 9377,38305, 9,39024,10001,39027,13755,39027, +27533,39027, 1471,38305,16937,39035, 9875,38305, 1011,39039, 1347,39039, + 555,38305,20763,38305, 566,39046, 566,39047, 567,39046,39051,39053, + 567,39047,39049,39057, 257,38305, 483,39061, 973,38305, 4816,39065, + 4805,39065, 1011,39068,38057,39065, 1011,39065, 4805,39074, 628,38305, + 483,39079, 1406,38305, 587,38305,23584,39085,24355,39085,37782,39085, +37408,39084,39091,39093,37783,39085,37408,39085,38670,39085, 3,39085, +37409,39102,39093,39105,37409,39103,39099,39109,38669,39085,35187,39112, +35728,39084,23493,39085,22807,39118,22807,39085,23493,39122,35729,39084, +35728,39085,39127,39129,35729,39085,39117,39133,37409,39085, 3,39136, +39093,39139, 483,39085,35187,39142,35187,39085,38669,39146, 483,39146, +36773,39147, 483,39153, 1011,39084, 483,39157, 9,39084, 4625,39161, +10603,39161, 3507,39161, 1971,39161,23983,39161,21783,39161,21605,39161, + 713,39175,18967,39084, 483,39179,35187,39084,37202,39183,37885,39183, +37145,39183, 483,39188, 483,39183,37145,39192,37409,39084,39097,39197, + 11,39084, 483,39201, 1369,38305,35964,39204,35965,39205,39207,39209, +35965,39204,35964,39205,39213,39215,35761,39205,38057,39205, 15,39204, + 1011,39223, 11,39205, 1011,39205, 11,39204, 1275,38305, 483,38305, +35895,39234, 4787,39234,15879,39234,21405,38305, 1011,39243, 483,39243, + 7535,38305, 15,39249, 7537,39251, 7541,39251, 7545,39251,28337,38305, +21605,39259,28351,39261,28355,39261,28359,39261, 6147,38305, 483,39269, +15269,38305, 483,39273,17423,38305, 1011,39277, 483,39277, 4827,38305, + 483,39283, 4745,38305, 1011,39287, 483,39287,17232,38305,30492,38305, +25793,39295, 265,39294, 4569,38305, 483,39301,14625,38305, 483,39305, + 7063,38305, 483,39309,37284,38305,37356,38305,17233,38305,30493,38305, + 483,39319,37973,38305,37759,38305,38002,38305,38327,39327,37760,38305, + 483,39331,37976,38305,38321,39335,37617,38305,38398,39339,38319,39339, +38400,39339,38335,39339,37663,39339,38397,39348,37671,39349,38397,39339, +37663,39354, 483,39354,36773,39338, 483,39361, 483,39339,38397,39364, +37663,39338, 483,39369,37899,38305,38392,39373,38321,39373,38394,39373, +38333,39373,35187,39372,37885,39383,37933,39373,38391,39386,38391,39373, +37933,39390, 483,39390,36773,39372,37933,39397, 483,39397, 483,39373, +38391,39402,38185,38305,38150,39407,38137,39407,37145,39410,37202,39407, +37885,39407, 483,39407,37145,39418,37145,39407,38137,39422, 483,39422, +37979,38305,38451,39429,37663,38305,37409,39432, 3,39435, 7,39433, +39437,39439, 2,39439,39435,39443, 483,39433,37617,39447,37241,39432, +37617,39432, 483,39453,37933,38305,36773,39456,37977,38305,38315,39461, +37815,38305, 483,39465,38003,38305,38317,39469,38127,38305, 483,39473, + 482,38304, 5,39477, 6,39478, 6,39477, 5,39482, 14,39477, + 586,38304,39122,39489,39128,39489,39127,39489,38863,39489,38601,39489, +38948,39489,38603,39489,39150,39489,39144,39489,38783,39489,38785,39489, + 3,39489, 1,39512,38971,39515,39161,39515, 1,39489, 3,39520, +38971,39523,39161,39523,39175,39489,38987,39489,21604,39489,39161,39533, +38971,39533,39146,39489, 483,39538,39142,39489,35187,39542,38943,39489, + 483,39546,35728,39489,39085,39550,22807,39489,39085,39554,26219,39489, + 8647,39489,19396,39489,39085,39489,22807,39564,35728,39564,35187,39564, + 483,39570, 483,39564,35187,39574,35895,39489, 9,39489,18967,39580, +18967,39489, 9,39584,35187,39489,39142,39588,39085,39588, 483,39592, + 483,39588,39085,39596, 483,39489,38943,39600,39146,39600,39085,39600, +35187,39606,35187,39600,39085,39610, 4787,39489,15879,39489, 1368,38304, + 3867,39619, 8277,39619, 3997,39619, 4067,39619, 9459,39619, 8953,39619, + 1274,38304,27979,39633,14625,39633, 4569,39633,16381,39633,13325,39633, +26293,39633, 8685,39633,35843,39633, 1369,39633,19513,39633, 3863,39633, +37019,39633, 9955,39633,37377,39633, 8401,39633,10273,39633,20697,39633, + 4137,39633,20219,39633, 4063,39633,35895,38305, 483,39674,35915,38305, + 3,39678, 2,39678, 842,39678, 843,39679,39685,39687, 842,39679, + 843,39678,39691,39693, 2,39679,39681,39697, 3,39679,39683,39701, + 482,38305,39616,39705,39614,39705,39578,39705,35718,39705,35895,39705, +39489,39714,39489,39705,15879,39718, 4787,39718,35895,39718,15879,39705, +39489,39726, 4787,39705,39489,39730,35187,39705, 587,39734, 587,39735, +39489,39739, 587,39705,35187,39742, 1368,38305, 587,38304,39241,39749, +39239,39749,39237,39749,38599,39749,38597,39749,38633,39749,38631,39749, +39677,39749,38801,39749,38805,39749,38679,39749, 483,39771,39235,39749, +15879,39775, 4787,39775,35895,39775, 483,39748,38679,39783, 483,38304, +38121,39787,38219,39787,38253,39787,37899,39786,38731,39795,37617,39786, +38713,39799,38011,39787,38185,39787,37145,39805,14625,39787, 4569,39787, +19396,39787,35931,39787,39489,39815,15269,39787, 4827,39787, 587,39786, +38679,39823,37899,39787,38391,39827,37617,39787,38397,39831,17423,39787, + 4745,39787, 9,39787,18967,39838,18967,39787, 9,39842, 587,39787, +39588,39847,39489,39847,35187,39850,35187,39847,39489,39854,35187,39787, + 586,39858,39823,39861, 586,39859,39847,39865, 586,38305,39713,39869, +38861,39869,39737,39869,39745,39869,38675,39869,35187,39879,39823,39869, +35187,39883,39783,39869,35187,39887, 1108,39869, 295,39869,35927,39869, +37611,39869,38155,39869,35719,39869,39705,39901,39749,39869,22807,39905, +35728,39905,35187,39905, 483,39910, 483,39905,35187,39914,35187,39868, +39823,39919,39783,39919,38675,39919,39749,39919, 483,39927, 1011,39869, + 9,39930, 9,39869, 1011,39934, 1369,38304, 1275,39938, 1275,38304, +39429,39943,23023,39943,22605,39943,25483,39943,22677,39943, 1369,39942, + 803,38305,17455,39956,13737,39957,39705,39961, 1505,38305,21605,39965, + 713,39967, 305,39967, 1489,39967, 1509,39967, 25,39967,15705,38305, + 767,39979,39705,39981,17455,38305, 15,39985, 9513,39987,16859,39987, +17469,39987, 803,39984, 154,38305, 165,39997, 374,38305, 11,40001, + 66,38305, 340,38305,30091,40006,27978,38305,39633,40011,23755,40011, +27805,40011,26331,40011,25793,40011, 8495,40011, 511,38305, 11,40025, + 935,38305, 483,40029, 1109,38305, 11,40033, 295,38305, 256,38305, + 375,40039, 9805,40039,20762,38305, 1141,40045, 1447,40045, 33,40045, + 88,38305,11251,40053, 5177,40053, 136,38305,36727,40059,37895,40059, +36079,40059, 144,38305, 113,40067, 341,38305, 113,40071,16937,40071, +27979,38305,38411,40077, 483,40077,39943,40077, 9565,40077, 113,38305, + 4727,40087, 9,40089,37662,38305,37241,40093,37932,38305,36773,40097, +37782,38305,37978,38305,39633,40103,38389,40103, 305,38305,16833,40109, + 25,38305,16833,40113,20501,38305, 1011,40117, 1347,40117, 253,38305, +16937,40123, 53,38305,16937,40127, 251,38305, 21,38305,38004,38305, + 7,40135,37241,38305, 6,40138, 2,40141,35187,40142,35187,40141, + 2,40146, 2,40147,38056,40141,37663,40138, 3,40138,40145,40157, +40149,40157,40153,40157, 3,40139,40151,40165,40093,40165,36773,38305, +37933,40171,37899,40173,37933,40170,35931,40170, 483,40179,37617,40170, + 483,40183,37899,40170,37933,40187, 483,40187,37409,40170, 3,40193, + 483,40171,35931,40197,37617,40197,37899,40197, 6,40170,37844,40205, + 3,40205,35187,40208,35187,40205, 3,40212, 7,40171,40207,40217, +40211,40217,40215,40217,40195,40217, 2,40217,40193,40227,37845,40217, +40205,40231,37845,40170, 7,40235,37325,38305, 7,40238, 2,40241, + 2,40240, 7,40239,35187,40247, 3,40249, 3,40248, 3,40247, +35187,40254, 3,40246,37844,40247, 1368,40238, 586,40239, 1369,40238, +37845,40238,40259,40269,40265,40269, 587,40239, 2,40238, 7,40276, +40251,40277, 3,40238,40243,40283,35187,40282,40275,40287, 3,40239, +40279,40291,40245,40291,40269,40291, 7,40290,40269,40299,40263,40291, + 2,40239,40287,40305,40257,40305,40253,40305,40261,40305,40267,40305, +10219,38305, 7,40316, 2,40318, 2,40319, 1368,40316, 1369,40316, + 2,40317,40327,40329, 3,40317,40321,40333,40325,40333, 2,40316, + 7,40338,40333,40341, 3,40316,40323,40345, 413,38305, 7,40349, + 375,40349, 1217,38305, 483,40355, 1216,38305, 7,40359, 412,38305, + 483,40363,36348,38305, 2,40366,36429,40369,38233,38305, 7,40373, +25296,38305, 7,40377, 265,38305,27978,40381,30492,40380, 17,40380, + 8495,40387, 6,40380, 11,40391, 7,40380,30091,40394, 7,40381, +27021,40399, 9195,40399,40391,40399,40001,40399,21605,40398, 6,40381, +40395,40411,40007,40411, 17,40411,21605,40381, 7,40418, 17,40419, + 241,40380, 483,40425,21605,40380, 7,40429,30091,40380, 7,40432, + 11,38305,18967,40436, 483,40439, 587,40436, 483,40443, 1369,40436, + 483,40437, 587,40449, 3,40436, 3,40437, 9875,40455,20501,40455, + 1011,38305, 483,40461, 587,40463, 587,40460, 483,40467, 9,40460, + 9565,40471, 7,40461, 959,40475, 759,40475, 47,40475, 903,40460, + 7,40483, 15,38305, 1369,40486, 1011,40489, 241,40486, 483,40493, + 3,40486, 165,40497, 3,40487, 253,40501, 1471,40501, 53,40501, +21605,38305,27533,40509, 15,40509,31103,40513, 1275,40509, 7,40508, +23755,40519,39633,40519,27805,40519,26331,40519,25793,40519, 8495,40519, + 7,40509,26219,40533, 305,40533, 25,40533, 713,40533, 265,40509, +31103,40543, 265,40508, 7,40547,25367,38305, 6,40551, 7,40551, + 6,40550,40555,40557, 7,40550,40553,40561, 17,38305,19917,40565, + 587,40565, 265,40564, 8495,40571, 265,40565, 8861,40575, 3,40564, + 113,40579, 3,40565,10109,40583,10108,40583,10108,40582,40585,40589, +10109,40582,40587,40593, 1141,40583, 1447,40583, 33,40583, 903,38305, + 7,40603, 1141,40605, 1447,40605, 33,40605, 1011,40602, 7,40613, +18967,38305, 1108,40617, 1072,40617, 165,40617, 1291,40617, 524,40617, + 9,40617, 1011,40628, 11,40616, 483,40633, 11,40617, 483,40636, + 483,40617, 11,40641, 1011,40640, 587,40641, 11,40640, 9,40616, + 587,40616, 483,40653, 7,40616, 1141,40657, 1447,40657, 33,40657, + 1011,40617,21183,40665, 7,40665, 483,40664, 9,40664, 241,38305, + 483,40675, 15,40674, 483,40679, 9,40675, 7,40674, 375,40685, + 9805,40685, 7,40675, 265,40691, 59,40691, 331,40691, 1325,40691, + 265,40674, 483,40701, 9,38305, 7963,40704, 7961,40707,27995,40704, +27533,40711,17573,40704,10001,40715,13755,40705, 9377,40719, 1,40704, + 3,40723, 1,40724,40705,40727, 7,40724,40729,40731, 7,40723, + 3,40734, 586,40723, 6,40704, 3,40741, 6,40742,40705,40745, + 0,40742,40747,40749, 0,40741, 3,40752,21604,40704, 3,40757, + 7,40758, 7,40757, 3,40762, 586,40757, 1274,40704, 3,40769, + 0,40770, 0,40769, 3,40774, 9377,40704,27533,40779,10001,40779, +13755,40779,10001,40705,17573,40787, 9377,40787,27533,40705,27995,40793, + 9377,40793,21605,40705,26715,40799,26865,40799,40761,40799,40765,40799, +40731,40799,40737,40799, 6,40799,26719,40799,40739,40799,40767,40799, + 587,40799,26625,40821,40723,40821,40757,40821, 1275,40705, 1991,40829, + 3621,40829,40773,40829,40777,40829, 5299,40829,40749,40829,40755,40829, + 1,40829, 4907,40829,17711,40829, 1011,40704, 9565,40851, 587,40704, +39515,40855,39523,40855, 3507,40855, 1971,40855, 0,40855,40829,40865, +40845,40855,21783,40855,23983,40855, 4625,40855,10603,40855,39533,40855, +21605,40855, 713,40881,39489,40881,18967,40704, 1011,40705,18967,40889, + 9617,40889, 2,40704,36727,40895,37895,40895,36079,40895, 3,40704, +11251,40903, 5177,40903, 2,40705,36729,40909, 3,40705,11355,40913, +30748,38305, 7,40916,17125,38305, 7,40920,30091,38305, 340,40924, + 7,40924,25793,40929, 265,40928, 1011,40925, 7,40935, 265,40925, + 7,40939, 265,40924, 7,40942,14315,38305,31103,40947, 375,40947, + 7,40947, 8495,40953,37845,38305,37897,40957, 1275,40957,36945,40956, + 6,40957, 7,40957, 6,40956,40967,40969,36773,40969, 7,40956, +38389,40975,39633,40975,40965,40975,36773,40956, 7,40983,37409,38305, +37285,40986, 113,40987, 2,40987,39313,40993,40157,40993, 3,40987, +39439,40999,40217,40999, 2,40986,38305,41005, 7,41007,40999,41009, +40165,41005,37241,41005, 3,40986, 3,38304,40375,41019,40237,41019, +40985,41019,40137,41019,40963,41019,39015,41019,38749,41019,40098,41019, +39323,41019,40972,41019,40989,41019,39009,41019,38751,41019,40094,41019, +39325,41019,41014,41019,15567,41019,12971,41019,13355,41019,13541,41019, +21049,41019,21242,41019,39127,41019,39714,41019,39739,41019,38603,41019, +38601,41019,38863,41019,39726,41019,39730,41019,20956,41019,20773,41019, +38783,41019,38785,41019,11970,41019,19441,41019,19656,41019, 4696,41019, + 5,41019, 6,41097,27169,41019,27168,41019,33465,41019,40011,41019, +30492,41019,40519,41019,39295,41019,30493,41019,40929,41019, 357,41019, + 367,41019, 471,41019, 1249,41019, 628,41019,20057,41019, 4569,41019, +14625,41019, 4545,41019,36203,41019,36343,41019,40953,41019,21183,41019, +20103,41142,13325,41019, 8337,41147, 4549,41019, 9357,41019, 9356,41019, +40966,41019,36026,41019,37558,41019,36226,41019,36944,41019, 8386,41019, +20186,41019, 9083,41019, 9484,41019,36027,41019,37559,41019,36227,41019, +36945,41019, 8387,41019,20187,41019,19513,41019,35843,41019,20219,41019, + 8401,41019,37019,41019,20697,41019, 4137,41019, 9955,41019, 3863,41019, + 4063,41019,37377,41019,10273,41019,21405,41019,20606,41019, 9984,41019, +37215,41019,40969,41019,36773,41218,40975,41019,40097,41019,36773,41224, +40103,41019,37979,41019,19396,41019,11131,41019,21059,41019, 9082,41019, +28205,41019,25551,41019,24613,41019,20326,41019,21087,41019,39705,41019, +35895,41250,15879,41250, 4787,41250,11949,41019, 9,41258, 4669,41019, + 9,41262,15879,41019,39705,41266, 4787,41019,39705,41270,20805,41019, +37501,41019,19631,41019, 9,41278,35895,41019,39705,41282, 9704,41019, +40387,41019,38939,41019,40033,41019,40571,41019,20804,41019, 341,41019, + 241,41299,27979,41019, 241,41303, 9377,41019, 11,41306, 256,41019, +35187,41310, 257,41019,35187,41315,20763,41019, 11,41318, 4745,41019, +17423,41019, 587,41019, 11,41326,37409,41326,37500,41019, 9875,41019, + 10,41334, 10,41335, 11,41334,41339,41341, 11,41335,41337,41345, + 1471,41019,37325,41019, 587,41351,37241,41019,40093,41354,41005,41354, +37408,41019, 241,41360,41005,41019,37241,41364,40093,41019,37241,41368, +20885,41019,20413,41372, 1369,41019,37409,41377,37409,41019, 241,41381, + 587,41380,40967,41019, 9195,41019, 11,41388,20413,41019,20885,41392, +10219,41019, 587,41397,20501,41019, 10,41400, 10,41401, 11,41400, +41405,41407, 11,41401,41403,41411, 253,41019, 6,41019,40373,41417, +40135,41417,40235,41417,40983,41417,40967,41417,27021,41417,40533,41417, +30091,41417, 4713,41417,16363,41417, 4863,41417,13119,41417,26415,41417, + 8757,41417,23493,41417,27377,41417, 4213,41417, 9195,41417,39489,41417, + 9335,41417, 4147,41417, 3933,41417, 8109,41417,36212,41417,36773,41417, +20103,41417, 8337,41417,37145,41417, 8997,41417,40957,41417, 241,41417, +35187,41478,18967,41417, 11,41483,35187,41417, 241,41486, 587,41487, + 587,41417, 8387,41417,20187,41417,14653,41417,36945,41417, 7,41019, +30091,41502,26331,41503, 8717,41503,23755,41503,27805,41503, 3785,41503, +40957,41502,36212,41502,39633,41503,10055,41503, 4329,41503, 4311,41503, +10351,41503,36773,41502,20103,41502, 8337,41502, 8997,41502, 1275,41503, + 15,41503,18967,41541,35187,41502, 241,41544, 241,41502,35187,41548, +27021,41019, 11,41552, 53,41019, 6,41018,41165,41559,41531,41559, +41517,41559,41157,41559,40975,41559,40103,41559,41537,41559,41169,41559, +41167,41559,41239,41559,41533,41559,41535,41559,40011,41559,27979,41559, +40519,41559,41549,41559,41311,41559,16381,41559,13325,41559,26293,41559, + 8685,41559,41361,41559,41545,41559,41503,41559,36773,41607,40957,41607, + 8997,41607,20103,41607, 8337,41607, 241,41607,35187,41607, 1369,41559, + 4569,41559,35843,41559,19513,41559, 3863,41559, 9955,41559, 4063,41559, +37377,41559,10273,41559,20697,41559, 4137,41559,37019,41559,20219,41559, + 8401,41559,14625,41559, 7,41018,41181,41653,41466,41653,41387,41653, +41476,41653,41474,41653,41185,41653,41183,41653,41171,41653,41468,41653, +41470,41653, 8997,41653,41417,41674,41315,41653,41478,41653,26219,41653, + 8647,41653,41381,41653,41486,41653,19396,41653,41417,41653,36773,41692, +40957,41692, 8997,41692,20103,41692, 8337,41692, 241,41692,35187,41692, +20103,41653,41417,41708, 8337,41653,41417,41712,36773,41653,41417,41716, + 483,41653, 9,41653,18967,41722, 241,41653, 1,41727,41417,41729, +41417,41726,18967,41653, 9,41734,40957,41653,41417,41738,35187,41653, +41417,41742, 11,41019,27021,41746, 587,41746, 9195,41746,20501,41746, + 9377,41746, 9875,41746,20763,41746, 9,41019, 4669,41762,11949,41762, +19631,41762,18967,41762,20273,41762, 9617,41762, 17,41019,20762,41776, +20762,41777,20763,41776,41781,41783,20763,41777,41779,41787,40957,41019, + 6,41790, 7,41790,41559,41795, 6,41791,41795,41799, 7,41791, +41793,41803,41417,41803, 9617,41019, 9,41808,36212,41019, 7,41812, +35187,41019, 256,41816, 7,41816, 241,41820,41559,41821, 7,41817, +41417,41827, 241,41816, 7,41830, 241,41019, 1,41835, 7,41837, +41559,41839, 7,41836,41417,41843,37408,41834, 7,41835, 1,41848, +41417,41851,41417,41849, 7,41834,41559,41857,35187,41856,35187,41834, + 7,41862,18967,41019, 483,41867, 9,41866,30091,41019, 7,41872, +14315,41019, 8997,41019, 7,41878,41559,41881, 7,41879,41417,41885, +20273,41019, 9,41888, 8337,41019, 7,41892,41559,41895, 9,41893, + 483,41893, 7,41893,41417,41903,20103,41019,21183,41906, 7,41906, +41559,41911, 9,41907, 483,41907, 7,41907,41417,41919,36773,41019, +40097,41922,40969,41922, 7,41922,41559,41929, 9,41923, 483,41923, + 7,41923,41417,41937, 2,38304,38595,41941,40177,41941,39459,41941, +38591,41941,38593,41941,40155,41941,39451,41941,38589,41941,39237,41941, +39241,41941,39239,41941,38599,41941,38597,41941,38633,41941,38631,41941, +26777,41941, 9741,41941,26781,41941, 9745,41941,39677,41941,38801,41941, +38805,41941, 5,41941, 6,41986,38881,41989, 9771,41941, 14,41941, +38881,41995,38897,41941,30349,41941,26857,41941,26933,41941, 9777,41941, +40077,41941,31598,41941,35931,41941,37519,41941,35690,41941,40651,41941, +38955,41941,40887,41941,39235,41941,35895,42025,15879,42025, 4787,42025, + 8982,41941, 668,41941,26625,42035, 9731,42035, 1419,41941, 9731,42041, +26625,42041,40471,41941,38931,41941,40037,41941,40851,41941,37617,41941, +37899,41941,39457,41941,36773,42059,39429,41941,27021,41941, 375,41941, +35187,42066,31103,41941,26625,42070, 9195,41941, 483,41941,39433,41941, +37241,42079, 587,41941, 1,42083,35187,42083, 9,42082, 9731,42089, +26625,42089, 6,41941, 2,42095,21605,42097,38305,42099, 5,42094, +38881,42103,42101,42103, 2,42094,38305,42109, 7,41940, 9459,42113, + 8953,42113, 3997,42113, 4067,42113, 8277,42113, 3867,42113, 9,41941, + 587,42126, 9731,42129,26625,42129, 17,41941, 8861,42134,26625,41941, +31103,42138, 8861,41941, 7,42143, 17,42142,35187,41941, 375,42148, + 9731,41941, 7,38304,39023,42155,41231,42155,25445,42155, 1,42155, + 2,42162, 3,42162,38903,42167,38971,42167,39161,42167,40855,42167, +18967,42167, 2,42163, 3,42163, 5,42155, 2,42182, 3,42182, + 2,42183,42187,42189, 3,42183,42185,42193,21604,42155,18967,42197, +39161,42197,38971,42197,38903,42197,40855,42197,39175,42155,38987,42155, +40881,42155,38925,42155,22779,42155,22243,42155,28063,42155,41303,42155, +31598,42155,25299,42155, 357,42155, 367,42155, 471,42155, 1249,42155, +35690,42155,37519,42155, 272,42155, 1476,42155, 280,42155, 949,42155, + 1517,42155,40387,42155,40571,42155,40033,42155,38939,42155,21087,42155, +20326,42155, 9704,42155, 9304,42155,35931,42155,40998,42155, 253,42155, + 265,42270, 375,42155,35187,42274,31103,42155,26625,42278,25381,42155, +10081,42283,41417,42155, 1369,42287,26625,42155,31103,42290, 9617,42155, + 9,42294, 53,42155, 265,42298,20273,42155, 9,42302,37899,42155, +37617,42155, 1471,42155, 265,42310, 587,42155,35187,42315,38233,42155, +38004,42155,36773,42155,37845,42322,38056,42155,39019,42327,37145,42327, +37844,42155,40305,42333,37933,42155, 483,42155,37845,42155,36773,42340, +40999,42155, 305,42155,21605,42347, 713,42155, 9195,42350,21605,42351, + 9195,42155, 713,42356, 2,42155, 1,42360, 8229,42361,40987,42361, +35187,42361,36773,42369,35187,42360,39019,42373,37145,42373, 3,42155, +42179,42379, 1,42379, 7,42379, 15,42385,38305,42387, 1,42378, +38903,42391,38971,42391,39161,42391,40855,42391,18967,42391,42389,42391, + 7,42378,40704,42405,42391,42407, 9,42405,38305,42410,42391,42413, +38305,42405, 9,42416,42391,42419,40987,42378, 1369,42379,35187,42378, +40305,42427, 25,42155,21605,42431, 2,42154, 1,42435,42379,42436, +42382,42435,42423,42435,42269,42435,42333,42435,42427,42435,42379,42435, + 1,42450,40987,42451,35187,42451, 3997,42435, 4067,42435, 9459,42435, + 8953,42435, 8277,42435, 3867,42435, 3,42154,42165,42471,42363,42471, +42345,42471,42366,42471,26219,42471, 8647,42471,42341,42471,42368,42471, +19396,42471,42361,42471, 1,42491,40987,42490,35187,42490,41417,42471, + 483,42471,18967,42471, 9,42502, 9,42471,18967,42506,40987,42471, +42361,42510,35187,42471,42361,42514,32805,42155, 265,42155, 253,42520, + 1471,42520, 53,42520,40987,42155, 3,42528,42435,42531, 3,42529, +42361,42535,35187,42155, 7,42539,38305,42541, 2,42543, 2,42542, + 2,42541,38305,42548, 2,42540, 1368,42539, 375,42538, 1369,42539, + 2,42538,39019,42561,38305,42560,42559,42565,37145,42561, 3,42538, +40305,42571,42545,42571,42435,42571, 3,42539,40277,42579,39315,42579, + 7,42579,38305,42585,42561,42587,42551,42579,42547,42579,42561,42579, +42361,42579, 2,42539, 7,42598,42571,42599,37845,42599, 9,42155, +20273,42606, 9617,42606, 6,38304,40509,42613,31103,42615,21604,42613, +40471,42613,40851,42613,40037,42613,38931,42613, 8982,42613,27979,42613, + 483,42631,41941,42631,40957,42613,37933,42637,41503,42613, 483,42641, + 8861,42613, 17,42644,17423,42613, 1011,42649, 4745,42613, 1011,42653, + 9875,42613, 1011,42657, 1471,42613,36773,42613, 483,42663,37844,42613, +40305,42667,37979,42613,41941,42671, 1369,42613,38057,42675, 331,42613, + 17,42678, 1325,42613, 17,42682, 334,42613, 1328,42613, 8337,42613, + 483,42691,20103,42613, 483,42695, 53,42613, 393,42613, 1359,42613, + 3,42613, 483,42705,35187,42704,40305,42709,10257,42705,37357,42705, + 2,42613, 62,42613, 59,42613, 17,42720,20501,42613, 1011,42725, + 253,42613, 3,42612,40975,42731,40103,42731,27979,42731,40519,42731, +40011,42731,16381,42731,13325,42731,26293,42731, 8685,42731,41503,42731, + 1369,42731, 4569,42731,35843,42731,19513,42731, 3863,42731, 9955,42731, +20697,42731, 4137,42731,37019,42731, 8401,42731,20219,42731, 4063,42731, +14625,42731,37377,42731,10273,42731,18967,42613, 165,42783, 241,42613, +35187,42613, 3,42788,40305,42791, 3,42789,40277,42795,39315,42795, + 17,42613, 8861,42800, 331,42800, 1325,42800, 59,42800, 7,38305, +25004,42810,25005,42810,25184,42810,25185,42810, 264,42811, 5,42811, + 0,42822, 3,42822, 1,42810, 4,42829,25004,42811,42815,42833, +25184,42811,42819,42837,25005,42811,42813,42841,25185,42811,42817,42845, +41493,42811,41622,42811,25367,42810,30748,42810,39650,42811,42752,42811, +38673,42811,38675,42811,39823,42811,39783,42811,27533,42811,26625,42869, + 331,42811,35187,42873,41559,42811, 1369,42876, 767,42811,18967,42881, +41019,42811,37899,42885,37617,42885,35931,42885,27021,42885,30091,42885, + 9195,42885,25793,42811,30091,42899,27021,42899,37845,42811,42667,42905, + 59,42811,35187,42909, 1325,42811,35187,42913,37897,42811,17125,42810, +30091,42810,25793,42921,41019,42921, 265,42920, 1369,42811,30091,42929, +41559,42928,20103,42929, 8337,42929,36773,42929,42731,42928,39633,42928, +42707,42811,42704,42811, 483,42947,39633,42811, 1369,42950,39749,42811, + 483,42955, 1275,42811, 265,42810,40411,42961,30091,42960,21605,42810, +26331,42967,25793,42967,23755,42967,27805,42967,39633,42967,42731,42967, +41559,42967,41019,42967, 8495,42967,42731,42811, 1369,42986, 241,42810, + 375,42991, 9805,42991,18967,42810, 1141,42997, 33,42997, 1447,42997, + 265,42811,35187,43005, 9377,43005, 9533,43005,37145,43005,21605,42811, +34311,43015,42619,43015,26625,43015,24545,43015, 9731,43015, 2,42810, + 3,42810, 0,43028,43005,43031, 295,43029, 1108,43029,35927,43029, +38155,43029,37611,43029, 9,43029, 1011,43044, 1011,43029, 9,43048, + 2,42811,42831,43053, 5,43053,42829,43057,36256,43053, 9,43053, +35187,43062,35187,43053, 9,43066, 3,42811, 5,43070,22807,43071, +37663,43071,42613,43070, 483,43079, 241,43071, 9,43083,37845,42810, +41559,43087,38389,43087,42731,43087,39633,43087,41019,43087,42613,42811, +32805,43099,37845,43098,37845,43099,37844,43098,43105,43107,37844,43099, +43103,43111, 3,43099, 1369,43115, 3,43098, 483,43119, 15,42811, +20273,43123, 9617,43123,35187,42810, 7,43129, 2,43130, 2,43131, +38305,43129, 3,43136,43135,43139, 3,43137,43133,43143, 1368,43129, +43143,43147, 59,43129, 1325,43129, 331,43129, 265,43129, 2,43129, + 7,43158,43143,43161, 3,43129,38305,43164,43135,43167,37408,38305, + 7,43171, 2,43172,40999,43175, 2,43173,40101,43179,41017,43179, + 1368,43171,40999,43185, 59,43171, 1325,43171, 331,43171, 265,43171, + 2,43170,37783,43197, 2,43171, 7,43200,40999,43203,37617,43201, + 3,43171,38305,43208,43179,43211, 3,38305,43129,43214,43135,43217, + 1,43214, 1011,43221,42041,43221, 1418,43221,41941,43227,42035,43221, + 669,43221,41941,43233,42089,43221,42129,43221, 1369,43221, 15,43240, +41941,43243, 15,43221, 1369,43246,41941,43249,11989,43215, 9,43253, +13737,43215, 9,43257,37357,43215,10257,43215,38118,43215,38132,43215, +37544,43215,37409,43215, 483,43270, 113,43215, 587,43275,37345,43215, +37241,43214,40993,43281,40993,43215,37241,43285, 9,43215,38057,43288, + 483,43215,38057,43292,37409,43292,10289,43215, 15,43214, 165,43301, + 11,43214, 9,43214,11251,43307, 5177,43307, 17,43214, 113,43313, + 11,43215, 165,43215, 7,43214, 0,43321, 4,43322, 4,43321, + 0,43326,35186,43321, 295,43321, 1108,43321,35927,43321,38155,43321, +37611,43321, 9,43321, 1011,43342, 1011,43321, 9,43346, 7,43215, +21604,43351, 1,43351, 3,43354, 3,43351, 1,43358, 15,43351, +38305,43363,43353,43365,43357,43365,43361,43365, 1011,43363,37409,43214, +43179,43375,41941,43215,37408,43379, 7,43379,35187,43382,35187,43379, + 6,43386, 7,43386, 6,43387,43391,43393, 7,43387,43389,43397, + 1011,43215,37408,43401, 7,43401,35187,43404,35187,43401, 7,43408, +38057,43215,40097,43413,40969,43413, 7,43413, 9,43412, 483,43412, +35187,43214,40305,43425,40275,43425,38305,43425, 6,43430,43413,43433, + 6,43425,38305,43436,43413,43439,41941,43425, 6,43443, 7,43443, + 6,43442,43447,43449, 7,43442,43445,43453, 1011,43425, 7,43457, +37844,38305,42336,43461,42917,43461,42905,43461,39013,43461,40305,43461, +40275,43461,37933,43461,42155,43474,42155,43461,37933,43478, 6,43460, +41941,43461, 6,43485, 7,43485, 6,43484,43489,43491, 7,43484, +43487,43495, 1011,43461, 7,43499, 2,38305,41603,43503,41686,43503, +41825,43503,41828,43503,42541,43502,42579,43513,16936,43503,36400,43503, +36439,43519,36348,43503, 6,43523,36413,43525, 6,43522,36439,43529, +36439,43523,41744,43503,41688,43503,41605,43503,41706,43503,41620,43503, + 1,43503, 1275,43544, 4,43544, 7,43544,41417,43551, 7,43545, +39633,43555,41559,43555,42731,43555,41019,43555, 1,43502,41692,43565, +41607,43565,41653,43565,41417,43570,41417,43565,41653,43574,41502,43565, +41559,43579,41019,43565, 7,43582,41559,43585, 7,43583,41417,43589, + 7,43565,41019,43593,41653,43595,41019,43592,41559,43599, 5,43502, +21605,43603,42810,43604,42811,43605,43607,43609,42811,43604,42810,43605, +43613,43615,41692,43503,35187,43618,41607,43503,35187,43622,41360,43503, +41559,43627,41820,43503,41559,43631,41544,43503,41559,43635,35760,43503, + 8315,43503,37408,43502,37783,43643,42538,43503,42601,43647,42553,43647, +42555,43647,41742,43503,41417,43654,41486,43503,41653,43658,41417,43503, +41827,43662,41742,43662,41653,43662,35187,43668,35187,43662,41653,43672, +37783,43503,37325,43502,42795,43679,42579,43679,41502,43503,41559,43685, +35187,43687,35187,43684,41559,43691,37241,43503,37409,43695,41019,43697, +41381,43503,41653,43700, 1275,43503, 1,43704,35187,43704, 9,43705, + 9731,43711,43221,43711,43549,43711,26625,43711, 587,43503,42811,43721, +35895,43723,15879,43723, 4787,43723,41653,43503, 1,43731,41417,43733, +41381,43730,41486,43730,41417,43730,35187,43740,35187,43730,41417,43744, + 9,43502,36727,43749,36079,43749,37895,43749,41827,43503,41417,43756, + 6,43502,39616,43761,39614,43761,39578,43761,39901,43761,41282,43761, +41266,43761,41270,43761,39961,43761,39981,43761,35718,43761,39869,43781, +41019,43761,35895,43784,15879,43784, 4787,43784,15879,43761,39489,43792, +41019,43792, 4787,43761,39489,43798,41019,43798,35895,43761,39489,43804, +41019,43804, 587,43761,35187,43810,39869,43813,35187,43761, 587,43816, +39869,43819, 587,43817,39489,43823,41019,43823,39489,43761,15879,43828, + 4787,43828,35895,43828, 7,43502, 6,43503,36348,43838,36439,43841, +36348,43839,36429,43845, 7,43503,41817,43848,43817,43849,41019,43853, + 4,43849, 0,43856,43784,43859,41019,43859,43761,43862,43761,43859, +41019,43866, 0,43849, 4,43870,43784,43873,41019,43873,43761,43876, +43761,43873,41019,43880, 1,43848,41417,43885,35186,43849,43784,43889, +41019,43889,43761,43892,43761,43889,41019,43896,41816,43848,41559,43901, +41019,43848,41559,43905,35187,43907,35187,43904,41559,43911,41019,43849, +41827,43915,43851,43915,41742,43915,41653,43915,35187,43922,35187,43915, +41653,43926,35187,43848,41417,43931,41019,43930,41559,43935,41019,43931, +41653,43939,37409,43502,41354,43943,40165,43943,41019,43943,37241,43948, +37241,43943,41019,43952,37408,43503,41417,43957,37663,43957,41019,43956, +41559,43963,41019,43957,41653,43967,41816,43503, 6,43970, 7,43970, +41559,43975, 6,43971,43975,43979, 7,43971,43973,43983,41417,43983, +41019,43503, 1,43989, 7,43990,41417,43993, 7,43991,41559,43997, +37408,43988,41559,44001,37408,43989,41381,44005, 7,43989,41381,44009, +41486,44009, 1,44008,41417,44015,41417,44009,35187,44018,35187,44009, +41417,44022, 7,43988,41559,44027,35187,44029,35187,44026,41559,44033, +35187,43988, 6,44036, 7,44036,41559,44041, 6,44037,44041,44045, + 7,44037,44039,44049,41417,44049,21605,43503, 7,44055, 265,44057, +35187,43503,41692,44060,41607,44060,41502,44060,41559,44067, 1275,44060, +41653,44060,41417,44072,36945,44061,41019,44077,41417,44060,41653,44080, + 6,44061,41922,44085,43413,44085,41019,44085,36773,44090,36773,44085, +41019,44094, 7,44061,41559,44099,42731,44099,39633,44099,41019,44099, + 6,44060,44099,44109, 7,44060,41417,44113,41019,44112,41559,44117, +44085,44113,41019,44113,41653,44123,41019,44060, 6,44126, 7,44126, +41559,44131, 6,44127,44131,44135, 7,44127,44129,44139,41417,44139, +36773,44061,41417,44145, 7,44145,41019,44149,35187,43502,41217,44153, +41472,44153,29229,44153,41099,44153,36429,44153,42559,44153,41417,44153, +37145,44166,37783,44153,37214,44153,41019,44173, 7,44153,41019,44177, +37145,44179,37145,44176,41019,44183,37145,44153,41417,44186, 7,44186, +41019,44191,38056,38305,41472,44195,41217,44195,41099,44195,29229,44195, +36429,44195,42559,44195,35187,44195, 6,44209,43413,44211,41417,44195, +37145,44214,37783,44195,37214,44195,41019,44221, 7,44195,41019,44225, +37145,44227,37145,44224,41019,44231,37145,44195,41417,44234, 7,44234, +41019,44239, 6,38305,42453,44243,42493,44243,42472,44243,42439,44243, +42474,44243,42441,44243,42363,44243,42471,44256,42381,44243,42165,44243, +42471,44262,42325,44243,42330,44243,42343,44243,42321,44243,42319,44243, +42371,44243,42376,44243,42568,44243,42447,44243,42484,44243,42054,44243, +42056,44243,44171,44243,43645,44243,43960,44243,44219,44243,43201,44243, +37617,44298,43206,44243,38711,44243,43199,44243,42605,44243,40231,44243, +43677,44243,40959,44243,10586,44243,10827,44243,42594,44243,42603,44243, +42577,44243,42596,44243, 6664,44243, 6243,44243, 9780,44242, 566,44242, + 566,44243, 567,44242,44337,44339,43517,44243, 9781,44242,42012,44243, +36303,44242,36303,44243,26970,44242,26971,44242,43639,44243, 4593,44243, + 6625,44358, 3113,44243, 3023,44243, 4736,44243, 6653,44243,42518,44243, + 3251,44243,42516,44243,42486,44243,42449,44243,42496,44243,42456,44243, +19257,44243,19243,44243,43425,44242,43413,44389,40511,44243,17452,44243, +42085,44243,43707,44243,42087,44243,40961,44243,43709,44243,44071,44243, +36099,44243,36098,44242,44409,44411,36099,44242,36098,44243,44415,44417, +43605,44243,43222,44242,43223,44243,44423,44425,43222,44243,43223,44242, +44429,44431,42199,44243,24081,44243,42181,44243,42383,44243,42435,44441, +42401,44243,42177,44243,43547,44243, 5,44242,42283,44451,25380,44451, +42155,44455, 1369,44451,21605,44458,42155,44461,21605,44451, 1369,44464, +42155,44467,42373,44243,37145,44470,42561,44243,37145,44474,42579,44474, +42327,44243,37145,44480,27514,44243,42490,44243,35187,44486,42451,44243, +35187,44490, 413,44243,32805,44243,42155,44496,28547,44243,42064,44243, +42138,44243,40517,44243,40543,44243,30294,44243,38004,44243,43461,44513, +38232,44243,42155,44517,38005,44243,42155,44521,30749,44243, 9780,44243, +44345,44527,26970,44243,44355,44531, 6625,44243, 4593,44534, 33,44535, + 9781,44243,44333,44541,25005,44243,26971,44243,44353,44547,25185,44243, +25589,44243,42074,44243,42152,44243,10831,44243, 77,44243, 120,44243, + 506,44243,38041,44243, 5101,44243,16989,44243,10511,44243, 4931,44243, + 5267,44243,16981,44243,13017,44243,12705,44243,13673,44243,18391,44243, +18617,44243,14599,44243, 9366,44243,36302,44243,18217,44243,42332,44243, +42435,44599,42570,44243,42435,44603,42426,44243,42435,44607,41539,44243, +41720,44243, 9132,44243, 1216,44243,36302,44242, 3,44618,44351,44621, +30748,44243,30748,44242,44525,44627,30749,44242,44625,44631,16931,44243, +35931,44243,41941,44636,39600,44243,42500,44243,18929,44243,38669,44243, +38677,44243,17019,44243,39941,44243,39955,44243,27021,44243, 483,44656, +41941,44656,37844,44242,44351,44663,41653,44243, 483,44666,43413,44243, +43425,44671,42514,44243,42361,44674,42368,44243,42471,44678,42361,44243, +42579,44682,42514,44682,42471,44682,35187,44688,35187,44682,42471,44692, +20234,44243, 8444,44243, 567,44243,44335,44701,16937,44243, 11,44705, + 1369,44705,26625,44243,41941,44710,41941,44243,37899,44714,37617,44714, +35931,44714,27021,44714,26625,44714, 9195,44714, 9731,44714,43957,44243, +37663,44730,20103,44243, 483,44734, 8337,44243, 483,44738, 9533,44243, + 4727,44243, 25,44744,10527,44243, 25,44748, 4541,44243,38128,44243, +38057,44243,42155,44757,37145,44759,37145,44756,42155,44763, 587,44756, +37845,44243,43461,44769,36773,44771,36773,44768,43461,44775, 25,44243, +10527,44778, 4727,44778, 113,44778, 9195,44243,41941,44786, 483,44786, +37782,44243,43197,44793,43643,44793,38709,44793,17451,44243, 1011,44800, +35688,44243,36910,44243,36923,44243,37899,44243,41941,44810,37617,44243, +41941,44814,43201,44814, 9731,44243,41941,44820, 8997,44243, 483,44824, + 66,44243, 33,44829, 483,44243,27021,44832,30091,44832, 241,44832, + 9195,44832,41653,44832, 8997,44832,42471,44832,39489,44832,20103,44832, + 8337,44832,36773,44832, 903,44243, 1011,44856,18967,44243, 567,44861, + 566,44860,44863,44865, 566,44861, 567,44860,44869,44871, 241,44243, + 483,44874,42425,44243,42378,44243,42435,44881,35187,44883,35187,44880, +42435,44887, 1369,44881,36773,44243,37845,44892,43461,44895, 483,44892, +37845,44893,41019,44901,37145,44243,42327,44904,42373,44904,42561,44904, +38057,44904,42155,44913,42341,44243,42471,44916,30091,44243, 483,44920, + 113,44243, 25,44924,35187,44924,17125,44243,37663,44243,43957,44932, +39489,44243, 483,44936,39943,44243, 1369,44941, 587,44243,43215,44945, +35895,44947,15879,44947, 4787,44947,38057,44944, 8449,44243,20237,44243, + 265,44242,40399,44961, 11,44961,42471,44243,42165,44966,42363,44966, +42341,44966,42368,44966,42361,44966,35187,44976, 483,44966,35187,44966, +42361,44982,20834,44243, 1011,44243,17451,44988,35187,44988, 903,44988, +20763,44988,20763,44243, 1011,44998, 257,44243,42579,44243,42561,45004, +42361,45004, 2,44242,39616,45011,39614,45011,39578,45011,39901,45011, +41282,45011,41266,45011,41270,45011,39961,45011,39981,45011,35718,45011, +39869,45031,41019,45011,35895,45034,15879,45034, 4787,45034,15879,45011, +39489,45042,41019,45042, 4787,45011,39489,45048,41019,45048,35895,45011, +39489,45054,41019,45054, 587,45011,35187,45060,39869,45063,35187,45011, + 587,45066,39869,45069, 587,45067,39489,45073,41019,45073,39489,45011, +15879,45078, 4787,45078,35895,45078, 3,44242,36302,45087,44349,45089, +35187,45086,44351,45093, 2,44243, 14,45097, 5,45097, 6,45100, + 6,45097, 5,45104,21605,45097,38305,45109,45099,45111,45103,45111, +45107,45111,43215,45097,43325,45119,43329,45119,35895,45119,15879,45119, + 4787,45119,43331,45119,35187,45119,43321,45133,35187,45096,39313,45137, +40157,45137,43281,45137,43215,45137,37241,45145, 3,44243,42539,45148, +38305,45149,37408,45152,37408,45153,45067,45149,41019,45159, 4,45149, + 0,45163,38305,45163, 0,45162,45034,45169,41019,45169,45011,45172, +45011,45169,41019,45176,45119,45169, 0,45149,38305,45183, 4,45182, +45034,45187,41019,45187,45011,45190,45011,45187,41019,45194,45119,45187, + 0,45148,38305,45148, 1,45148,45176,45205,45194,45205,45169,45205, +45011,45210,45187,45205,45011,45214,45159,45205,45011,45205,45169,45220, +45187,45220,35186,45148,35186,45149,38305,45229, 7,45231,45220,45229, +45205,45229,45011,45236,45034,45229,41019,45229,45011,45242,45011,45229, +45205,45246,41019,45246,45119,45229, 1,45149,45165,45255,45011,45257, +45167,45255,45011,45261, 5,45149,45185,45265,45011,45267,43171,45149, +37617,45271,42538,45148,42435,45275,37409,45148,38709,45279,43197,45279, +45155,45279,43643,45279,42155,45148,42435,45289,35187,45291,35187,45288, +42435,45295, 1369,45289, 1011,45149, 483,45301, 11,45148, 33,45305, +37409,45149,45157,45309,43957,45309, 11,45149, 25,45315,35187,45315, +42155,45149,42579,45321,45151,45321,42514,45321,42471,45321,35187,45328, +35187,45321,42471,45332,45066,45149,45201,45337,45203,45337,35187,45149, +45201,45343,45011,45345,45203,45343,45011,45349,45011,45342,45201,45353, +45203,45353,45011,45149,35187,45358,45201,45361,45203,45361,35187,45148, +40217,45367,45233,45367,42155,45366,42435,45373,42155,45367,42471,45377, +37845,44242,41922,45381,43413,45381,44595,45381,44993,45381,41019,45381, +36773,45390,36773,45381,41019,45394,38056,44243,39313,45399,40157,45399, +43281,45399,43215,45399,37241,45407,37844,44243,40217,45411,42155,45410, +42435,45415,42155,45411,42471,45419,42538,44243, 3,45422,42435,45425, + 3,45423,42361,45429,42155,44243,32805,45432,37845,45432,37845,45433, +37844,45432,42435,45441,45439,45441,37844,45433,42341,45447,45437,45447, + 3,45433,42341,45453,42368,45453,42361,45453,35187,45458,35187,45453, +42361,45462, 483,45453, 3,45432,42435,45469,35187,45471,35187,45468, +42435,45475, 1369,45469,35187,45432, 3,45480,42435,45483, 3,45481, +42361,45487, 11,44243, 8861,45491, 3,45490, 33,45495, 3,45491, + 113,45499, 331,45491, 1325,45491, 59,45491,35187,44243,42490,45508, +42451,45508,38305,45508, 7,45515, 2,45517, 2,45516, 2,45515, + 7,45522, 1368,45515, 1011,45508,45381,45529,42378,45508,42435,45533, +42471,45508,42361,45536,37285,45509,41019,45541,42361,45508,42471,45544, + 113,45508, 2,45509,41354,45551,40165,45551,38305,45551, 7,45557, +41019,45551,37241,45560,37241,45551,41019,45564, 3,45509,45519,45569, + 2,45508,40157,45573,39313,45573,43281,45573,43215,45573,37241,45581, + 3,45508,40217,45585,45521,45585,45525,45585,45559,45585,45527,45585, +42155,45584,42435,45597,42155,45585,42471,45601,42155,45508, 3,45604, +42435,45607, 3,45605,42361,45611,37409,44243,43503,45615,37663,45617, + 3,45615,43201,45621, 3,45614,38709,45625,43197,45625,43643,45625, +35187,44242, 3,45632,44351,45635,35187,38305,37214,45639, 2,45641, +39019,45643,39021,45641,37286,45639,35890,45638,35891,45639,45651,45653, +35891,45638,35890,45639,45657,45659,38152,45638,38153,45639,45663,45665, +38296,45638,38297,45639,45669,45671,38153,45638,38152,45639,45675,45677, +38297,45638,38296,45639,45681,45683,37283,45639,42675,45639,37282,45639, +42674,45639,37899,45638,37885,45695,38293,45639,37885,45639,37899,45701, +44945,45639,38292,45639,44944,45639,37287,45639,38173,45638,38172,45639, +45713,45715,38303,45638,38302,45639,45719,45721,38173,45639,38172,45638, +45725,45727,38303,45639,38302,45638,45731,45733,45096,45639,45149,45639, + 7,45738,45367,45741,45097,45639,37241,45639, 2,45747, 2,45746, + 587,45746,42613,45639, 1369,45754,38271,45639, 1369,45758,44243,45639, + 2,45763, 2,45762, 587,45762, 136,45639, 137,45639, 1530,45638, + 842,45638, 669,45639, 1419,45639, 843,45639,39085,45783,45777,45783, + 1531,45639,45775,45789, 1530,45639, 843,45638, 842,45639,45795,45797, + 1531,45638,45793,45801, 1368,45639,45411,45805,40999,45805,45367,45805, +45585,45805,42613,45805,37845,45805,44243,45817, 586,45639,39847,45821, +39142,45821,39085,45821, 483,45826,37241,45821, 483,45821,39085,45832, + 586,45638,38675,45837,39823,45837,39783,45837,39749,45837, 483,45845, + 1418,45639, 668,45639, 587,45638,37885,45853,37202,45853,37145,45853, + 483,45858, 483,45853,37145,45862, 587,45639,37241,45866,44243,45866, + 9,45866, 483,45639, 587,45875,37145,45877, 1369,45639,35760,45880, +35761,45881,45883,45885,35760,45881,35761,45880,45889,45891,42613,45880, +38271,45880, 15,45880, 9,45639, 2,45900, 2,45901, 587,45900, + 265,45639, 375,45909, 15,45639, 1369,45912, 7,45639,40956,45917, +44060,45917,40957,45917,44061,45917,43502,45917,37844,45917,35187,45917, + 3,45930,43503,45930, 3,45931,45927,45937, 1369,45931,38305,45917, + 2,45942,45937,45945,37845,45942, 2,45943,45933,45951,45929,45951, + 587,45943,45149,45917,37145,45959,45227,45959,37845,45917,38305,45964, +43503,45917,35187,45968, 375,45917,45148,45917, 2,45917,40101,45977, +41017,45977,38305,45976,45937,45983,44904,45977,45569,45977,43375,45977, +39019,45977,44243,45977,37145,45994,37145,45977,44243,45998, 3,45917, +35187,46002,45951,46005,44243,46002, 3,45916,45943,46011, 7,46011, +38305,46015,45977,46017,45994,46011,45959,46011,45967,46011,45919,46011, +45949,46011,45925,46011,37241,46011,45977,46011,44243,46034,44243,46011, +45977,46038, 2,45916,45931,46043,40999,46043,45975,46043,45367,46043, +45585,46043,46009,46043,45971,46043,45921,46043,45935,46043,45923,46043, +45411,46043,42613,46043,46003,46043,44243,46069,37845,46043,44243,46073, +44243,45917, 3,46076,46043,46079, 3,46077,45977,46083, 6,45638, +45998,46087,46073,46087,45817,46087,37844,46087,46043,46095,45805,46095, +35187,46087, 3,46101,45977,46103, 3,46100,46043,46107,45805,46107, +37145,46087,45977,46112,45977,46087,37145,46116, 3,46087,35187,46120, +46043,46123,45805,46123,45917,46121,37145,46129,45917,46087,35187,46132, + 2,46135, 2,46134,38057,46132,38056,46132, 2,46133,46141,46145, + 3,46133,46139,46149,46143,46149, 2,46132,35187,46154,46149,46157, +46149,46155, 3,46132,46137,46163,46145,46163, 3,45639, 5,46169, + 6,46171, 4,46168, 0,46168, 5,46168, 1,46168,45983,46169, +45945,46169,43679,46169,45927,46169,40277,46169,45749,46169,45765,46169, +39315,46169,45905,46169,37241,46169,45977,46169,42613,46203,43848,46169, + 256,46169, 6,46169,46175,46211,46177,46211,37145,46211, 7,46169, +46179,46219,46181,46219,41019,46219,45639,46218, 2,46227,38305,46219, +46229,46231, 3,46231,46227,46235,45977,46231,43503,46218, 241,46218, +38271,46219, 7,46168,37241,46247,38057,46169,38305,46251,46211,46253, +43503,46169, 6,46256, 7,46256, 6,46257,46261,46263, 7,46257, +46259,46267, 241,46169, 7,46270, 2,45638,41472,46275,41217,46275, +46175,46275,46211,46280,46177,46275,46211,46284,46212,46275,46214,46275, +46221,46275,46223,46275,46173,46275,45930,46275,46043,46299,46045,46275, +46216,46275,46225,46275,41099,46275,46032,46275,45711,46275,46200,46275, +45745,46275,46248,46275,46245,46275,35187,46275,45917,46322,46043,46325, +45917,46323,46011,46329,45830,46275,29229,46275,45773,46275,36429,46275, +45895,46275,45897,46275,45687,46275,45693,46275,45705,46275,45707,46275, +45849,46275,45779,46275,42559,46275,45899,46275,45915,46275,45757,46275, +45761,46275,45941,46275,41417,46275,37145,46368,45821,46275,37241,46372, +46011,46275,37241,46376,46247,46275,37241,46380,45881,46275,42613,46385, +37241,46275,46011,46388,46169,46388,46247,46388,45821,46388,46211,46275, +46175,46398,46177,46398,37145,46398,37783,46275,44243,46407,45917,46275, +35187,46410,46043,46413,37214,46275,41019,46417,46218,46275,46179,46421, +46181,46421,41019,46421, 7,46275,41019,46429,37145,46431,46169,46429, +46175,46435,46177,46435,37145,46435,37145,46428,41019,46443,46169,46428, +46179,46447,46181,46447,41019,46447,37145,46275,41417,46454,46211,46454, + 7,46454,41019,46461,46169,46275,35187,46464, 6,46467, 6,46466, +38305,46465,46211,46473,38271,46464,44243,46465,38270,46464,37241,46464, + 6,46465,46477,46485, 7,46465,46471,46489,46481,46489, 6,46464, +46473,46495,35187,46494,46489,46499,46489,46495, 7,46464,46179,46505, +46181,46505,46469,46505,46485,46505,41019,46505, 2,45639,42671,46517, +39429,46517,44814,46517,44810,46517,44636,46517,37241,46516,40077,46517, +44243,46516,46003,46517,37241,46535, 9,46516,44243,46517,37617,46540, +37899,46540,35931,46540,35931,46517,44243,46548,37617,46517,44243,46552, +37899,46517,44243,46556, 7,46517,44243,46561, 7,46516,46107,46565, +46123,46565,46095,46565,40999,46565,45585,46565,45411,46565,45367,46565, +42613,46565,37845,46565,46087,46583,44243,46583,37845,46517,46211,46589, +41417,46589, 7,46589,41019,46595, 3,45638,45951,46599,45942,46599, +46011,46603,46013,46599,46066,46599,46540,46599,40305,46599,46529,46599, +45751,46599,46533,46599,45767,46599,46580,46599,46563,46599,45649,46599, +45737,46599,38305,46599,45917,46630,46011,46633,45917,46631,46043,46637, +45814,46599,45771,46599,45869,46599,45871,46599,45689,46599,45691,46599, +45699,46599,45709,46599,45851,46599,45781,46599,40275,46599,46539,46599, +45903,46599,45873,46599,45907,46599,45753,46599,45769,46599,45957,46599, +45805,46599,42613,46676,46043,46599,42613,46680,46565,46599,42613,46684, +45867,46599,37241,46689,42613,46599,46043,46692,46565,46692,45805,46692, +45917,46599,38305,46700,46011,46703,46560,46599,44243,46599,46517,46708, + 7,46599,38305,46713,46517,46715,46517,46712,41941,46599, 6,46721, + 7,46721, 6,46720,46725,46727, 7,46720,46723,46731, 1011,46599, + 7,46735,46517,46599,38305,46738,35187,46739, 6,46742, 6,46743, +44243,46738,38271,46739,38270,46739,37241,46739, 6,46739,35187,46756, +46741,46757, 7,46739,41019,46763,46747,46763, 6,46738,46763,46769, +46751,46769, 7,46738,46759,46775,46745,46775,46757,46775,46753,46775, + 6,45639, 7,45638,43214,46787,45977,46789,38305,46787, 3,46793, +46043,46795,46565,46795,45805,46795, 3,46792,45977,46803, 59,46787, + 1325,46787, 331,46787, 265,46787, 2,46787, 7,46814,46795,46817, + 3,46787,38305,46820,45977,46823,46785,46787,35187,46827, 2,46828, + 2,46829,38057,46827,38056,46827, 2,46827,35187,46838, 3,46827, +46833,46843, 2,46826,46843,46847,46835,46847, 3,46826,46841,46853, +46831,46853,46839,46853,46837,46853, 0, 6,13135,46863,13169,46864, + 4871,46863, 4893,46868,13172,46863, 4894,46863,13136,46863,13143,46877, + 4872,46863, 4879,46881,13145,46863, 4881,46863,37417,46863,38056,46888, +38057,46889,46891,46893,38057,46888,38056,46889,46897,46899,17176,46863, +17380,46863,16950,46863,17058,46863,16948,46863,17056,46863,16974,46863, +17072,46863,16996,46863,17086,46863, 3348,46863,18291,46863, 7316,46863, + 8455,46863, 8343,46863, 8353,46863,13139,46863,13175,46863,13155,46863, +13517,46863,15197,46863,15485,46863,15512,46863,15595,46863,15203,46863, + 5757,46863, 5453,46863, 5471,46863, 5455,46863, 4863,46959, 5507,46863, +12569,46863,12387,46863,12551,46966,12552,46863,16995,46863,16941,46863, +16977,46974,16978,46863,17017,46862,17016,46862,19836,46863,19857,46985, +19815,46863, 4,46988,19857,46991, 4,46989,19843,46995,27930,46863, +27895,46999,27857,46863, 4,47002,27895,47005, 4,47003,27907,47009, +36303,46862,36325,47013,27128,46863, 9136,46863,27266,46863,27163,46863, +34310,46863,34447,47025,46516,46863,46599,47029,33454,46863,33368,46863, +28027,46863,28111,46863,28117,46863,26928,46863,24253,46863,30569,46863, +30316,46863,28213,46863,26525,46863, 9137,46863, 273,46863, 1477,46863, + 281,46863,17011,46863, 5043,46863,15503,46863, 8997,47066,24558,46863, +26575,46863,41877,46863, 120,46863, 77,46863,16977,46863,16941,47080, + 9262,46863,14813,46863,26605,46863,41923,46863, 8843,46863,37131,46863, +41907,46863,26591,46863,41893,46863,37113,46863,20255,46863, 9288,46863, + 8697,46863, 9275,46863, 8784,46863,19877,46863,41867,46863,20043,46863, +21376,46863,21485,46863,19787,46863,19391,46863,19299,46863,19271,46863, +19283,46863,17010,46863, 5042,46863,36518,46863,40664,46863,17017,46863, +46983,47143, 1516,46863, 948,46863,17016,46863,46981,47151,16972,46863, + 903,47154,17070,46863, 1011,47158,17175,46863, 903,47162,17379,46863, + 1011,47166, 4213,46863, 375,47170, 8757,46863, 375,47174, 4266,46863, +16865,46863,16873,46863,16867,46863,12642,46863, 8896,46863,14740,46863, + 8355,46863, 264,47193, 265,47192,47195,47197, 264,47192, 265,47193, +47201,47203,14515,46863,14517,46863,14519,46863,20201,46863,36979,46863, +20152,46863,36898,46863,20239,46863,37095,46863,21522,46863,38266,46863, +17082,46863, 903,47228,16990,46863,16943,47233, 1011,47232,35780,46863, +29644,46863,43412,46863,15628,46863, 9044,46863, 264,47247, 265,47246, +47249,47251, 264,47246, 265,47247,47255,47257,12484,46863,12486,46863, +12488,46863,12551,46863,12387,47266,12547,46863,12633,46863,12507,46863, +12497,46863,12501,46863,13381,46863,13169,46863,13135,47282, 4893,46863, + 4871,47286,12563,46863,27255,46863, 483,47292, 9287,46863, 483,47296, +13119,46863, 264,47300,13143,47303, 264,47301,13169,47307, 4863,46863, + 264,47310, 4879,47313, 264,47311, 4893,47317, 7315,46863, 413,47320, + 264,47320, 264,47321, 265,47320,47327,47329, 265,47321,47325,47333, +14653,46863, 375,47336,16875,46863,12504,46863,19478,46863,35766,46863, +20044,46863,36534,46863,12566,46863,12391,47353,33338,46863,34447,47357, +45866,46863,46599,47361,25545,46863,40675,46863, 1413,46863, 922,46863, + 1281,46863, 247,46863,36303,46863,38299,47377,37897,47377,36335,47377, +35893,47377,20035,46863, 11,47387, 11,47386,36527,46863, 587,47392, +24545,46863, 483,47396,35761,46863, 903,47400,19475,46863, 1011,47404, + 1086,46863, 934,46863, 1216,46863,17049,47412,16937,47412, 9766,46863, + 5519,46863, 8389,46863, 8861,46863, 3345,47424,12377,47424, 8337,46863, + 375,47430, 8374,46863, 8365,46863,20169,46863,36921,46863, 8453,46863, + 4863,47443,13119,47443, 9072,46863, 9105,46863, 305,46863,12377,47452, + 9731,46863, 483,47456, 8997,46863,15503,47460, 265,47460, 265,47461, + 257,47460, 264,47469, 265,47468,47471,47473, 264,47468, 265,47469, +47477,47479, 375,47460,15029,46863,12377,47484, 375,46863,27021,47488, +30091,47488, 9195,47488, 8757,47488, 4213,47488,14653,47488,20103,47488, +36773,47488, 8337,47488, 8997,47488, 241,47488, 6578,46863, 394,46863, + 828,46863, 128,46863, 299,46863, 705,46863, 101,46863,28715,46863, +21315,47526, 165,46863, 903,47530,43215,46863,38057,47534, 1505,46863, + 1011,47538,30091,46863, 375,47542, 113,46863, 25,47546, 9,47546, + 803,46863, 483,47552, 518,46863, 504,46863, 508,46863, 645,46863, + 623,46863, 659,46863, 510,46863, 241,47568, 506,46863, 9,47572, + 244,46863, 483,47576, 265,46863, 8997,47580, 5041,47580,17009,47580, + 8861,47581, 241,46863, 265,47591, 6515,47590, 375,47590, 510,47590, + 483,47590, 9,47600, 9,47590, 483,47604, 1011,46863,16990,47608, +17070,47608,17379,47608,40617,47608,16937,47608, 903,47618, 1505,47608, +17049,47608, 903,47624,21315,47608,19475,47608, 587,47608, 903,47608, +16937,47634,17049,47634, 903,46863,16937,47641,16977,47643,17082,47640, +16972,47640,17175,47640,32805,47640,36349,47640,17049,47640, 1011,47656, + 165,47640,16937,47640,16943,47663, 1011,47662,38057,47640,35761,47640, + 483,47640, 11,47640, 1011,47640,17049,47676,16937,47676,10527,47640, + 4727,47640, 6515,47640, 6564,46863, 6685,46863,35187,47690, 11,47691, + 6515,46863, 241,47696, 903,47696, 587,47697,10761,46863,10592,46863, + 4738,46863,13857,46863, 483,47710,10527,46863, 903,47714, 9,47714, + 3345,46863, 413,47720, 8861,47720, 1369,47721, 341,47721, 3747,46863, + 1369,47731, 3689,46863, 1369,47735,17000,46863,17090,46863, 2691,46863, + 2777,46863, 2793,46863, 2831,46863, 6486,46863, 4415,46863,10493,46863, +13941,46863,10901,46863, 4727,46863, 903,47760, 9,47760, 4819,46863, + 483,47766, 6703,46863, 6623,46863, 5759,46863, 7823,46863, 7813,46863, + 7761,46863, 7705,46863, 7735,46863, 7727,46863,13904,46863,10600,46863, + 4742,46863, 4822,46863, 2875,46863, 2865,46863, 2961,46863, 2949,46863, + 2919,46863, 3645,46863, 3609,46863, 3467,46863,17102,46863,17104,46863, + 1369,47815, 5041,46863, 265,47819, 265,47818, 4919,46863, 483,47824, +17100,46863,17009,46863, 265,47831, 265,47830,17099,46863, 483,47836, + 4920,46863, 6441,46863, 483,47842,17123,46863, 3353,46863, 3691,46863, +10154,46863, 3777,46863, 2847,46863, 2881,46863, 2933,46863, 2929,46863, + 2967,46863, 5271,46863, 4923,46863, 3355,46863, 3031,46863, 3121,46863, +20819,46863,37537,46863,20952,46863,37748,46863,20885,46863, 483,47884, +37663,46863, 9,47888,11529,46863, 1010,46862,23717,47895,24522,47895, +24447,47895,23939,47900,23939,47895,24447,47904,22956,47895,23651,47909, +21605,47895, 17,47913,23939,47915, 17,47912,23651,47919, 17,47895, +21605,47923,24447,47925,21605,47922,23651,47929, 264,46862,47135,47933, +47823,47933,47835,47933,47137,47933,47587,47933,47585,47933,47465,47933, +47583,47933,47019,47933,47581,47933,17009,47953, 5041,47953, 8997,47953, + 265,46862,47063,47961,47065,47961,47055,47961, 8997,47961, 5041,47961, +17009,47961, 8495,47961, 241,47961, 241,46862, 240,46863, 265,47981, + 1,47982, 0,47982, 5521,47983, 5520,47982,47989,47991, 5520,47983, + 5521,47982,47995,47997, 0,47983,47985,48001, 1,47983,47987,48005, + 264,46863,15193,48009,15475,48009,15579,48009,13119,48008,13143,48017, + 4863,48008, 4879,48021,47972,48009,47970,48009,47821,48009,47833,48009, +13507,48009,47968,48009,47467,48009, 53,48009, 1471,48009, 8997,48009, +47961,48042, 253,48009,17009,48009,47961,48048, 5041,48009,47961,48052, +15437,48009, 8997,48057,20501,48009,47961,48009,17009,48062, 5041,48062, + 8997,48062,20219,48009,37019,48009,19513,48009,35843,48009,20697,48009, +37377,48009,13119,48009,13135,48083, 4863,48009, 4871,48087,18967,48009, + 483,48091, 1369,48009, 9875,48009, 8401,48009, 9955,48009,10273,48009, + 4137,48009, 3863,48009, 4063,48009, 241,48009, 413,46863, 3345,48112, + 7315,48112,17049,48112,16937,48112,12377,46863, 8861,48122,15029,48122, + 305,48122, 713,48122, 25,48122, 587,48123,12387,48135, 11,48123, + 257,48123, 253,48123, 1471,48123, 53,48123, 1369,48123, 257,48122, + 587,48122,12391,48153, 257,46863, 9136,48156, 9137,48156, 264,48157, +48159,48163,17827,48156,48163,48167, 413,48156, 1,48170, 264,48156, + 8997,48175, 241,48175, 1,48179, 265,48157, 8997,48183,48175,48184, + 4425,48183,48161,48183,48176,48183,48175,48183, 8997,48194, 5579,48183, +17873,48183, 1791,48183,48173,48183,48181,48183, 265,48156, 8997,48208, +48163,48211,48163,48209, 8997,48215, 8997,48156, 265,48218,48163,48221, + 265,48219,48175,48225,12377,48156,36925,46863,20177,46863, 8383,46863, + 8385,46863, 88,46863, 11,48239,37409,48239, 566,46863,27021,48245, + 9195,48245,20501,48245, 9377,48245, 9875,48245, 587,48245,20763,48245, + 653,46863, 11,48261, 289,46863, 587,48265,36349,46863, 903,48268, +40617,46863, 1011,48272, 66,46863, 33,48277,20217,46863,37017,46863, +37845,46863,38057,46863, 903,48286,43215,48286, 155,48286,21315,46863, + 483,48294, 1011,48294,28715,48294, 1347,48294, 1347,46863,21315,48304, + 155,46863,38057,48308,21378,46863,38134,46863,20103,46863, 375,48316, +36773,46863, 375,48320, 713,46863,12377,48324, 25,46863, 113,48328, +12377,48328,26625,46863, 483,48334,10107,46863,38056,48338,38056,48339, +38057,48338,48343,48345,38057,48339,48341,48349, 3,48339, 3,48338, + 9195,46863, 375,48356,27021,46863, 375,48360, 19,46863, 59,48365, +32805,46863, 4,48368,34447,48371, 903,48368, 9,48368, 4,48369, +33871,48379, 11,48368,34447,48383,45639,46863, 2,48386,46599,48389, + 2,48387,46169,48393, 587,48386,46599,48397,36256,46863,35187,46863, + 6685,48402, 9,48402, 483,46863,27255,48408,26625,48408,24545,48408, + 9287,48408,21315,48408, 903,48408, 9731,48408,13857,48408, 803,48408, + 244,48408,17049,48408, 6441,48408, 4819,48408,17099,48408, 4919,48408, + 9,48408, 241,48440, 241,48408, 9,48444,20885,48408, 5,48408, +27021,48451, 9195,48451,20501,48451, 9377,48451, 9875,48451, 587,48451, +20763,48451, 9,46863,32805,48466,10527,48466, 113,48466, 506,48466, +16937,48466, 4727,48466, 483,48466, 241,48480, 241,48466, 483,48484, +37663,48466, 265,48467, 11,48491, 3,48466, 11,48495,37409,48495, +35187,48466,19917,46863,18967,48503, 5,48504, 5,48505,21036,48503, +21037,48503, 4,48503,48509,48515, 5,48503,18967,48518, 4,48502, +48521,48523,48507,48523,48511,48523, 5,48502,48513,48531, 587,46863, +12377,48535,12551,48537,45639,48535,46275,48541,12377,48534,12391,48545, +36527,48534,45639,48534,46599,48551, 265,48535,17827,48555, 1011,48534, + 11,46863,32805,48561,34003,48563,20035,48560,32805,48560,34447,48569, + 903,48560, 1275,48561, 9,48575, 3,48560, 33,48579, 3,48561, + 113,48583,16937,46863, 1216,48586, 413,48586,21036,48586,21037,48586, +21036,48587,48595,48597,21037,48587,48593,48601, 903,48587,16941,48605, + 1011,48586, 903,48608, 903,48586,16943,48613, 1011,48612, 9,48586, +17049,46863, 1216,48620, 413,48620, 903,48620, 1011,48626, 1011,48620, + 903,48630, 483,48620, 2,48620, 3,48620, 1369,48639, 2,48621, + 3,48621, 483,48645, 2,46862, 6,48649, 4,48651, 5,48649, + 9,48655,21604,48657,21604,48656,21605,48657,48661,48663,21605,48656, +48659,48667, 5,48648,23717,48671,24522,48671,24447,48671,23939,48676, +23939,48671,24447,48680,22956,48671,23651,48685, 17,48671,21605,48689, +24447,48691,21605,48688,23651,48695,21605,48671, 17,48699,23939,48701, + 17,48698,23651,48705, 19,48649, 12,48649, 9,48649, 11,48712, + 11,48649, 9,48716,21605,48649,48652,48720,48653,48721,48723,48725, +48653,48720,48652,48721,48729,48731, 3,46862, 5,48735, 4,46862, +19975,48739,19365,48739,19221,48739,19917,48739,10107,48747, 587,48739, + 5,46862,25451,48753,42283,48753,19739,48753, 0,48753, 3,48761, +25380,48753, 7971,48765,42155,48765, 164,48753,48353,48771,20763,48753, + 1011,48775, 1010,48774,48777,48779, 1010,48775, 1011,48774,48783,48785, + 2,48753, 17,48788,48353,48791, 2,48752,23717,48795,24522,48795, +23939,48795,24447,48800,24447,48795,23939,48804,22956,48795,23651,48809, +21605,48795, 17,48813,23939,48815, 17,48812,23651,48819, 17,48795, +21605,48823,24447,48825,21605,48822,23651,48829, 1369,48753,21605,48832, + 7971,48835,42155,48835,21605,48753, 1369,48840,42155,48843, 7971,48843, + 17,48753,37844,48848,48762,48848,48763,48849,48853,48855,37844,48849, +48763,48848,48762,48849,48861,48863,37845,48848,48859,48867,37845,48849, +48851,48871, 2,48849,48355,48875,47853,48875, 2,48848,48353,48881, + 5,46863, 374,48884, 374,48885, 375,48884,48889,48891,19381,48885, +19380,48884,48895,48897,19381,48884,19380,48885,48901,48903,20812,48884, +20813,48884,20812,48885,48909,48911, 1197,48885,21604,48915,21604,48914, +21605,48915,48919,48921,21605,48914,48917,48925, 375,48885,48887,48929, +20813,48885,48907,48933,20501,48885, 1011,48937,27021,48885, 483,48941, + 9195,48885, 483,48945, 9377,48885, 483,48949, 9875,48885, 1011,48953, + 587,48885, 483,48957, 1369,48885, 1011,48961,38057,48961,20763,48885, + 483,48967, 903,48885, 375,48971, 483,48884,27021,48975, 9195,48975, +20501,48975, 9377,48975, 9875,48975, 587,48975,20763,48975,18967,48885, + 483,48991, 165,48991, 2,48884,48961,48997, 3,48884,13118,49000, + 4862,49000,13118,49001,13119,49000,49007,49009, 4863,49000, 4862,49001, +49013,49015,15193,49001,15475,49001,15579,49001,13507,49001,20501,49001, + 253,49001,15437,49001, 8997,49031,20219,49001,37019,49001,19513,49001, +35843,49001,20697,49001,37377,49001,13119,49001,49003,49047, 4863,49001, +49005,49051, 241,49001, 9875,49001, 53,49001, 1471,49001, 8401,49001, + 9955,49001,10273,49001, 4137,49001, 3863,49001, 4063,49001, 1369,49001, +18967,49001, 483,49077, 2,48885, 1,49081, 7,49082,49000,49084, +49001,49085,49087,49089,49000,49085,49001,49084,49093,49095, 7,49081, + 1,49098,49000,49100,49001,49101,49103,49105,49000,49101,49001,49100, +49109,49111, 1,49080, 7,49115, 7,49080,49001,49119, 0,49080, +49083,49123, 7,49125,49001,49127, 0,49081,49116,49131,49001,49133, + 7,49131,49115,49136,49001,49139,49001,49137,49115,49143,49115,49131, + 7,49146,49001,49149,49001,49081, 3,48885,37357,49155, 165,49155, +10257,49155, 483,49155,35187,48885, 375,49165, 9,48885, 1,49168, + 3,49171,46863,49172,46863,49171, 3,49176, 3,49177,37845,49169, +37845,49168,37844,49168,49183,49187,37844,49169,49185,49191, 2,49168, +49175,49195,49179,49195, 2,49169,49181,49201, 3,46863,49171,49204, +49195,49207, 6,49205, 11,49211, 0,49205, 0,49204,40704,49217, +38305,49217, 9,49220, 9,49217,38305,49224, 6,49204,40705,49229, +49217,49231,49219,49229,18967,49229, 4,49236, 5,49236, 5,49237, + 4,49237,46863,49244,49241,49247,46863,49237, 4,49250,49241,49253, + 4,49251,49243,49257,49227,49229, 17,49229,49223,49229,37325,49205, + 903,49267,10107,49204,48875,49271,10219,49205, 903,49275,17049,49204, + 1369,49279, 11,49205, 25,49283, 9,49283, 17,49205, 903,49289, +21315,49289,37409,49205, 9,49295, 1011,49205, 375,49299, 9,49204, + 11,49303,37409,49303, 11,49204, 33,49309,35187,49205, 1505,49313, + 4,49204,49289,49317, 5,49204,13118,49320, 4862,49320,13118,49321, +13119,49320,49327,49329, 4863,49320, 4862,49321,49333,49335,15193,49321, +15475,49321,15579,49321,13507,49321,20501,49321, 253,49321,15437,49321, + 8997,49351,37019,49321,20219,49321,35843,49321,19513,49321,37377,49321, +20697,49321,13119,49321,49323,49367, 4863,49321,49325,49371, 241,49321, + 9875,49321, 1471,49321, 53,49321, 8401,49321,10273,49321, 9955,49321, + 4137,49321, 4063,49321, 3863,49321, 1369,49321,18967,49321, 483,49397, + 4,49205, 1,49401, 7,49402,49320,49404,49321,49405,49407,49409, +49320,49405,49321,49404,49413,49415, 7,49401, 1,49418,49320,49420, +49321,49421,49423,49425,49320,49421,49321,49420,49429,49431, 1,49400, + 7,49435, 7,49400,49321,49439, 0,49400,49403,49443, 7,49445, +49321,49447, 0,49401,49436,49451,49321,49453, 7,49451,49435,49456, +49321,49459,49321,49457,49435,49463,49435,49451, 7,49466,49321,49469, +49321,49401, 5,49205,20645,49475, 1505,49475, 9935,49475,18967,49205, + 7,49483, 4,49484, 4,49485, 16,49483, 17,49483, 4,49482, +49493,49495, 5,49482,49487,49499,49491,49499, 4,49483, 7,49504, +49499,49507, 5,49483,49489,49511, 2,46863, 6,49515, 17,49517, +35187,49519, 0,49515,38305,49523, 15,49525, 0,49514,36545,49529, +49521,49529, 15,49529, 6,49514,49531,49537,36544,49537,49529,49541, +49527,49537, 11,49537,35187,49546,49529,49549,35187,49537, 11,49552, +49529,49555,45639,49514,46599,49559,45639,49515,46275,49563,17125,49515, + 16,49567, 16,49566, 17,49567,49571,49573, 17,49566,49569,49577, +17049,49514, 4,49514, 7,49583, 1,49585, 265,49583, 1,49588, + 0,49588,49587,49589,49586,49588,49595,49597,49587,49588,49586,49589, +49601,49603, 0,49589,49591,49607, 1,49589,49593,49611, 4,49515, + 1011,49615, 1,49617, 0,49617, 1,49616,49621,49623, 0,49616, +49619,49627, 4,46863,27857,49630,27895,49633,19815,49630,19857,49637, +49237,49631,49239,49641,49237,49630,49241,49645,32805,49630,34447,49649, +32805,49631,34003,49653,27857,49631,27879,49657,19815,49631,19849,49661, + 2,49630, 7,49665, 1,49667, 265,49665, 1,49670, 0,49670, +49669,49671,49668,49670,49677,49679,49669,49670,49668,49671,49683,49685, + 0,49671,49673,49689, 1,49671,49675,49693, 2,49631, 903,49697, + 1,49699, 0,49699, 1,49698,49703,49705, 0,49698,49701,49709, + 1, 7, 377,49713,48110,49712,48111,49712,49152,49712,49472,49712, +49153,49712,49473,49712,49374,49712,49054,49712,49375,49712,49055,49712, + 704,49712, 100,49712,26502,49712,41376,49712, 8808,49712,47622,49713, +47660,49713,47146,49713,47148,49713,49059,49713,49379,49713,49158,49713, +49478,49713,49061,49713,49381,49713,48998,49713,49318,49713,47224,49713, +47226,49713,48041,49713,48039,49713,47058,49713,47060,49713,47220,49713, +47222,49713,48047,49713,47056,49713,47442,49713,49349,49713,49029,49713, +49121,49713,49441,49713,47628,49713,47668,49713,48962,49713,49290,49713, +22798,49713,39074,49713,31498,49713,44616,49713,31392,49713,44494,49713, +47540,49713,47532,49713,31794,49713,44858,49713,48298,49713,48288,49713, +31924,49713,44994,49713, 8652,49713, 8535,49713,13252,49713,43989,49712, +41417,49843,30655,49712,26219,49847,26503,49712,41377,49712, 8809,49712, +26547,49712,26219,49857,41835,49712,41417,49861, 273,49712,12897,49865, + 8813,49712, 8647,49869, 376,49713, 705,49712, 101,49712, 803,49712, + 483,49879,30091,49712,26219,49883, 113,49712, 9,49887,43503,49712, +41417,49891,43215,49712, 483,49895,43214,49712, 112,49712, 802,49712, +49320,49712,49401,49905, 241,49905,49000,49712,49081,49911, 241,49911, +49081,49712,49001,49916,49001,49917,49911,49921,49401,49712,49321,49924, +49321,49925,49905,49929,49001,49712,49081,49932, 241,49932,49321,49712, +49401,49938, 241,49938, 414,49713,18391,49945, 420,49713,18391,49949, + 449,49713,18391,49953, 8808,49713,49855,49957, 8453,49713,46863,49960, +20239,49713,46863,49964,37095,49713,46863,49968,47976,49713,47593,49713, +26502,49713,49851,49977,31921,49713,47592,49713, 8495,49983, 828,49713, +49903,49987, 128,49713,49901,49991, 705,49713,49737,49995, 101,49713, +49739,49999,47977,49713, 8495,50003,47488,49713,48122,49713,48480,49713, +48440,49713,47568,49713, 1344,49713, 1482,49713, 64,49713, 1484,49713, + 336,49713, 1480,49713, 254,49713, 1342,49713, 338,49713, 1123,49713, + 931,49713, 665,49713, 1417,49713, 1429,49713, 1301,49713, 1127,49713, + 941,49713, 523,49713, 1283,49713, 939,49713, 1103,49713, 171,49713, + 1525,49713, 407,49713, 1517,49713, 949,49713, 280,49713, 1476,49713, + 272,49713, 1017,49713, 1077,49713, 1047,49713, 1113,49713, 1245,49713, + 1237,49713, 501,49713, 433,49713, 929,49713, 591,49713, 915,49713, + 681,49713, 1555,49713, 911,49713, 1545,49713, 855,49713,41376,49713, +49853,50107, 4900,49713,14622,49713,48113,49713, 8997,50115, 1382,49713, + 695,49713,48286,49713, 903,50122,48294,49713, 1011,50126,26503,49713, +49741,50131,47530,49713, 903,50134,47538,49713, 1011,50138,49289,49713, +49317,50142, 903,50142,48961,49713,48997,50148, 1011,50148,21522,49713, +46863,50154,38266,49713,46863,50158, 394,49713, 299,49713,31792,49713, + 903,50166,44856,49713, 1011,50170,22789,49713, 903,50174,39065,49713, + 1011,50178, 1386,49713, 545,49713,41377,49713,49743,50187, 413,49712, + 8647,50191,18062,50191,17993,50191, 251,50196, 251,50191,17993,50200, +46863,50191, 412,49712,48008,49713,50207,50209,49717,50209, 341,50209, +40381,49713, 17,50217, 913,49713, 483,50221, 289,49713, 8809,49713, +49745,50227,49000,49713,49935,50231,49919,50231,49721,50231,49731,50231, +49937,50231,49933,50231,49081,50243, 241,50243,49320,49713,49941,50249, +49927,50249,49723,50249,49729,50249,49943,50249,49939,50249,49401,50261, + 241,50261, 1374,49713, 8672,49713,12534,49713,12921,49713, 264,50273, + 265,50272,50275,50277, 264,50272, 265,50273,50281,50283, 8605,49713, +26097,49713,41299,49713,26277,49713,41479,49713,26104,49713,41314,49713, + 1477,49713,46863,50300, 281,49713,46863,50304, 1516,49713,46863,50308, + 948,49713,46863,50312,44988,49713, 903,50316,31920,49713, 1011,50320, +43293,49713,49895,50325,15156,49713, 264,50329, 265,50328,50331,50333, + 264,50328, 265,50329,50337,50339, 8916,49713,14623,49713, 8495,50345, + 4901,49713, 8495,50349,14611,49713, 241,50352, 4899,49713, 241,50356, +15219,49713, 412,50360, 413,50361,50363,50365, 412,50361, 413,50360, +50369,50371, 5119,49713, 412,50374, 413,50375,50377,50379, 412,50375, + 413,50374,50383,50385,13177,49713, 413,50388, 264,50388, 264,50389, + 265,50388,50395,50397, 265,50389,50393,50401, 5313,49713,12377,50404, +47591,49713, 265,50409, 8861,50411, 265,50408, 8495,50415,47641,49713, +43292,49713,49899,50421,15158,49713,39228,49713,23012,49713,40676,49713, +25546,49713, 500,49713,18391,50435, 432,49713,18391,50439, 1244,49713, +18617,50443, 1236,49713,18617,50447, 1109,49713, 903,50451, 1412,49713, + 305,50455,44832,49713,31770,49713,25545,49713, 9,50462,40675,49713, + 483,50466, 935,49713, 483,50471, 1087,49713, 9,50475, 1413,49713, + 295,49713, 1290,49713, 903,50483, 1304,49713, 9,50487,48009,49713, +50191,50491,49719,50491, 241,50491,22957,49713, 903,50498,39205,49713, + 1011,50502,47608,49713, 1505,50506,21315,50506,47640,49713, 165,50512, +38057,50512, 510,49713,46863,50518, 922,49713, 1108,49713, 1280,49713, + 9,50527, 246,49713, 483,50531, 412,49713,47460,50535, 483,50535, + 9,50535, 8997,50535,46863,50542,46863,50535, 8997,50546, 1216,49713, +44243,50550,31103,50550, 483,50550,18617,50557, 9,50550,18617,50561, + 8677,49713, 305,49713, 241,50567,16163,50567, 8647,50566, 8495,49713, + 257,50574, 1141,49713,16531,50579, 253,49713, 59,50582, 1325,50582, + 331,50582, 265,50582, 8634,49713, 8611,49713,41349,49713,26127,49713, + 273,49713,12705,50601,13673,50601,18391,50601, 2923,50601,46863,50600, + 331,49713, 53,50612, 1471,50612, 253,50612, 1369,50612, 8861,49713, + 241,50623, 375,50625, 375,50622, 251,49713, 413,50631,17993,50633, + 413,50630,18391,50637,15029,50630, 264,50641, 265,50640,50643,50645, + 264,50640, 265,50641,50649,50651,39204,49713, 257,49713, 265,50657, +26625,50657,25793,50657,41019,50657, 8495,50657, 9731,50657,25793,50656, +41019,50656, 8495,50656, 265,50656, 4863,50677,12377,49713,46863,50680, + 5313,50680, 341,49713, 241,50687, 298,49713, 4863,50691, 704,49713, +49875,50695, 9195,50695, 100,49713,49877,50701, 395,49713, 829,49713, +49879,50707, 9195,50707, 129,49713,49887,50713, 89,49713,48884,50716, +48884,50717,48885,50717,50719,50723,48885,50716,50721,50727, 11,50716, + 567,49713,42155,50733,41941,50733, 7971,50733, 9565,50733, 587,50733, + 9377,50743, 587,50732, 9195,50747,38057,49713,47640,50750,46863,50750, + 903,50754, 903,50750,46863,50758,21315,49713,47608,50762,46863,50762, + 1011,50766, 1011,50762,46863,50770, 628,49713, 1396,49713, 241,50777, + 288,49713, 7971,50781, 265,49713,23584,50785,39564,50785,24355,50785, +39905,50785, 5520,50785, 256,50784,50795,50797, 5521,50785, 256,50785, +50204,50785, 10,50784, 1,50785, 257,50808,50797,50811, 257,50809, +50803,50815, 10,50785,50191,50785,46863,50820,47590,50784,23493,50785, +22807,50826,39489,50785,39085,50830,22807,50785,23493,50834,39085,50785, +39489,50838, 1087,50785, 935,50785,47591,50784, 8495,50847,47590,50785, +50847,50851, 4213,50785, 8757,50785,26415,50785,41653,50785,27377,50785, + 9335,50785,42471,50785, 8109,50785, 4147,50785, 3933,50785, 53,50784, + 1471,50784, 253,50784,47591,50785,50825,50881, 257,50785, 1,50884, +50797,50887,50797,50885, 4827,50885, 305,50785, 8861,50895, 8495,50785, + 331,50899,46863,50785,50191,50902, 241,50902, 241,50785,46863,50908, + 8337,50909,46863,50913, 1369,50784, 305,50917, 11,50785,50807,50921, + 8165,50921, 1369,50921, 241,50784,47460,50929, 483,50929, 9,50929, + 8997,50929,46863,50936,46863,50929, 8997,50940, 257,50784,50801,50945, +50803,50945, 4863,50945, 11,50784,50819,50953, 7971,50953, 1011,49713, +31920,50958,48294,50958,44856,50958,47538,50958,48961,50958,39065,50958, +21315,50958,46863,50972,31103,50958, 903,50976, 1505,50958,46863,50980, +44243,50958, 903,50984,39205,50958, 9,50958, 903,50958,31103,50992, +44243,50992,46863,50958,21315,50998, 1505,50998, 903,49713,44988,51004, +48286,51004,31792,51004,47530,51004,49289,51004,22789,51004,38057,51004, +46863,51018,44243,51004, 1011,51022, 165,51004,46863,51026,31103,51004, + 1011,51030,22957,51004, 483,51004, 1011,51004,44243,51038,31103,51038, +46863,51004,38057,51044, 165,51044, 241,49713,47961,51050, 4899,51050, +14611,51050, 375,51050,12377,51051, 375,51061, 8997,51051, 375,51065, + 265,51065,46863,51069, 1275,51050, 9,51073, 15,51050, 483,51077, + 265,51050,47460,51081, 483,51081, 9,51081, 8997,51081,46863,51088, +46863,51081, 8997,51092, 8647,49713, 413,51096, 305,51097, 11,51097, + 1369,51097, 341,51097, 305,51096,41417,49713, 11,51111, 1369,51111, +26219,49713, 11,51117, 1369,51117,32102,49713,27021,51123,45148,49713, + 11,51127, 1369,51127,31857,49713,44925,49713,32294,49713,45314,49713, +17565,49713,32103,49713, 241,51143, 587,51142,45149,49713, 11,51148, +49317,49713,49289,51152,48997,49713,48961,51156,49001,49713,49912,51161, +49725,51161,49914,51161,49735,51161,49081,51161,49911,51170,49911,51161, +49081,51174, 241,51174, 241,51161,49911,51180,49321,49713,49906,51185, +49727,51185,49908,51185,49733,51185,49401,51185,49905,51194,49905,51185, +49401,51198, 241,51198, 241,51185,49905,51204,49475,49713, 1505,51208, +49155,49713, 165,51212, 240,49712,51059,51217,50163,51217,50165,51217, +51051,51217, 375,51225, 264,49712,49715,51229,50838,51229,50834,51229, +50850,51229,50847,51229,50945,51229,49983,51229,50415,51229,50677,51229, +50691,51229,50496,51229,50003,51229,50906,51229,50910,51229,50345,51229, +50349,51229, 8374,51229, 8389,51229,50705,51229,20152,51229,36898,51229, +20201,51229,36979,51229,50902,51229, 241,51276,50908,51229,46863,51280, +50491,51229, 241,51284,47590,51229,50785,51288,22807,51229,50785,51292, +39085,51229,50785,51296,26219,51229,41417,51229,14524,51229,35690,51229, +50733,51229,37519,51229,50781,51229,50953,51229,50455,51229,50917,51229, +19396,51229, 8647,51229,20103,51229, 375,51324,36773,51229, 375,51328, + 8337,51229, 375,51332,50785,51229,39085,51336,22807,51336,47590,51336, +46863,51336, 241,51344, 241,51336,46863,51348,47488,51229,47961,51229, + 9,51229,18967,51356, 483,51229,35187,51229, 375,51362,18967,51229, + 9,51366,46863,51229,50908,51370,50785,51370, 241,51374, 241,51370, +50785,51378, 375,51370, 241,51229,50491,51384,50902,51384,50785,51384, +46863,51390,46863,51384,50785,51394, 375,51229, 8337,51398,20103,51398, +36773,51398,14315,51398,35187,51398,46863,51398,14315,51229, 375,51412, + 4899,51229,14611,51229, 1010,49712, 3867,51421, 8277,51421, 4067,51421, + 3997,51421, 8953,51421, 9459,51421, 902,49712,50321,51435,51031,51435, +24265,51435,28233,51435,29077,51435,51005,51435,31103,51447,19559,51435, + 4283,51435, 9673,51435, 4279,51435, 3807,51435,20361,51435,20713,51435, + 9967,51435, 8251,51435,47961,49713, 241,51468,47979,49713, 412,51472, + 413,51473,51475,51477, 412,51473, 413,51472,51481,51483, 240,49713, + 340,51487,51398,51487,51418,51487,51416,51487,51354,51487,47974,51487, +47589,51487, 8590,51487,12537,51487,14620,51487, 375,51487,51229,51508, + 7,51487, 265,51512, 265,51513,51229,51517,47580,51487, 8861,51521, +47961,51487,51229,51524, 8495,51524,51229,51487, 375,51530,14611,51530, + 4899,51530,47961,51530, 8495,51487, 4899,51540,47961,51540,14611,51540, +14611,51487,51229,51548, 8495,51548, 4899,51487,51229,51554, 8495,51554, +46863,51487, 265,51560, 8861,51563, 265,51561, 8495,51567,51229,51567, + 265,51487, 7,51572,15309,51573,15308,51572,51577,51579,15308,51573, +15309,51572,51583,51585,46863,51572, 8861,51589, 902,49713,31103,51593, +43053,51593,46863,51593, 1010,49713, 1505,51601, 918,51601, 9,51601, + 903,51606, 903,51601, 9,51610, 265,49712,49873,51615,50165,51615, +51059,51615,51057,51615,51055,51615,51053,51615,49975,51615,49973,51615, +50113,51615,50111,51615,50163,51615,51471,51615,50355,51615,50359,51615, +50209,51615, 241,51645, 8948,51615,50225,51615,50479,51615, 331,51615, + 8861,51654, 8861,51615, 331,51658, 4827,51615,15269,51615,51051,51615, + 375,51667,14611,51667, 4899,51667,47961,51667, 241,51614,50209,51677, + 241,49712,18300,51681, 8605,51681, 8916,51681,50945,51681,49321,51680, +50249,51691,49001,51680,50231,51695,50705,51681, 8374,51681,51508,51681, +50691,51681,47443,51681, 8389,51681,50677,51681,51517,51681, 8861,51681, + 375,51714,26219,51681,41417,51681, 305,51681,48009,51681,51229,51725, + 8647,51681, 8452,51681,46863,51731, 713,51681, 25,51681,18257,51681, + 375,51738, 8337,51681, 375,51742, 265,51742,46863,51747, 375,51681, +18257,51750, 8861,51750, 8337,51750,51487,51750,51487,51681, 375,51760, + 265,51680,50209,51765,49321,51681,49905,51769,49001,51681,49911,51773, + 265,51681,51370,51777,51229,51777,46863,51780,46863,51777,51229,51784, + 8337,51776,46863,51789,46863,51681, 264,51792,51765,51795, 264,51793, +51777,51799, 264,49713,51489,51803,51515,51803,51575,51803,51521,51803, +50411,51803,50659,51803,51563,51803, 8471,51803, 8397,51803, 8371,51803, +20149,51803,36887,51803,20211,51803,37009,51803,20247,51803,37103,51803, +51589,51803,50207,51803,46863,51839,51765,51803,46863,51843,51677,51803, +46863,51847,36773,51803,46863,51851,20103,51803,46863,51855, 8337,51803, +46863,51859, 4613,51803,12571,51803,36539,51803,37595,51803,36049,51803, +47581,51803,51487,51873,51615,51803,39085,51877,22807,51877,47590,51877, +46863,51877, 241,51884, 241,51877,46863,51888,46863,51802,51765,51893, +51677,51893,50207,51893,51615,51893, 241,51901, 341,51893, 1369,51803, + 341,51803,51487,51909, 8337,51909,20103,51909,36773,51909,35187,51909, +14315,51909,46863,51909,12377,51803, 375,51925, 1011,49712,26219,51929, +41417,51929, 8647,51929, 903,49712,49981,51937,51594,51937, 375,51937, +31103,51937,51593,51944,51593,51937,31103,51948,15269,51937, 4827,51937, + 1217,49713, 483,51957, 9,51957, 413,49713, 8647,51962,13177,51962, + 251,51962,18391,51969, 375,51962,18391,51973,44243,51962,31103,51962, +18391,51963, 251,51981, 483,51962,18391,51985, 9,51962,18391,51989, + 375,49713, 5,51992,51615,51995, 413,51993,17993,51999, 413,51992, +18391,52003, 241,51993,51487,52007,46863,51992, 241,51992,51217,52013, +51615,52013, 8861,51992,12377,51993,51487,52021,15029,51992,14315,49713, + 341,52027,51487,52029,15029,49713, 266,52032, 267,52032, 264,52033, +52035,52039, 465,52032,52039,52043, 413,52032, 6,52046, 264,52032, + 251,52051, 241,52051, 6,52055, 265,52033, 251,52059,52051,52060, + 8841,52059,52037,52059,52052,52059,52051,52059, 251,52070,15363,52059, +15055,52059,15061,52059,52049,52059,52057,52059, 265,52032, 251,52084, +52039,52087,52039,52085, 251,52091, 251,52032, 265,52094,52039,52097, + 265,52095,52051,52101, 375,52032, 8639,49713, 8643,49713,26217,49713, +41415,49713, 164,49713,51593,52115, 903,52115, 1504,49713,51601,52121, + 144,49713, 113,52125, 802,49713,49880,52129,49879,52129, 483,52132, + 9366,52129, 9195,52129, 483,52138, 483,52129,49879,52142, 9195,52142, +31103,52129, 112,49713,49888,52151,49887,52151, 9,52154, 9,52151, +49887,52158,44243,52151, 1486,49713, 803,52165,43214,49713,49896,52169, +49895,52169, 483,52172, 483,52169,49895,52176, 1073,49713, 903,52181, + 525,49713, 903,52185, 669,49713, 483,52189, 566,49713,51929,52193, +51615,52193,10107,52193,21036,49713, 17,52201, 88,49713,37611,52205, +36083,52205,38155,52205,35927,52205, 1346,49713, 1505,52215, 154,49713, + 483,52219,26329,49713,41557,49713, 165,49713,49155,52226,47640,52226, +46863,52226, 903,52232, 903,52226,46863,52236,43215,52226, 1505,49713, +49475,52242,47608,52242,46863,52242, 1011,52248, 1011,52242,46863,52252, +28715,52242, 803,49713,49903,52259, 483,52261, 483,52258,49903,52265, + 113,49713,49901,52269, 9,52271, 9,52268,49901,52275,28715,49713, + 1505,52278,43215,49713,49899,52283, 483,52285, 483,52282,49899,52289, + 165,52282,29028,49713,43318,49713,25793,49713, 257,52298, 713,49713, + 241,52303,16163,52303,41019,49713, 257,52308, 25,49713, 241,52313, +16163,52313, 1447,49713,16531,52319, 33,49713,16531,52323, 1471,49713, + 59,52326, 1325,52326, 331,52326, 265,52326, 53,49713, 1325,52336, + 59,52336, 331,52336, 265,52336, 1325,49713, 53,52346, 1471,52346, + 253,52346, 1369,52346, 59,49713, 1471,52356, 53,52356, 253,52356, + 1369,52356,48466,49713, 483,52366,48408,49713, 9,52370, 843,49713, + 9,52375, 1530,49713, 9,52379,40436,49713, 113,52383, 587,49713, +28714,52387,36212,52387,30091,52387, 567,52386, 9195,52395, 11,52386, +32103,52386,35187,52387, 241,52402, 241,52387,35187,52406, 4,52386, + 9366,52411, 9195,52411, 483,52414, 483,52411, 9195,52418,31103,52411, + 5,52387,21605,52424, 9195,52425,27021,52425,21605,52387, 11,52433, + 5,52432, 11,49713,32805,52439,19888,52439,19913,52439, 265,52439, +51803,52447,43503,52439, 1275,52438, 9,52453, 89,52438, 587,52438, + 265,52438,51229,52461, 7971,52461,45149,52438,18967,52439, 241,52468, + 241,52439,18967,52472, 2,52438, 9,52477,44243,52477, 3,52439, + 7971,52483,42155,52483, 2,52439,52205,52489,38305,52439, 265,52493, +38305,52438, 113,52497, 1369,49713, 331,52500, 59,52500,40674,52501, +38305,52501, 265,52509, 241,52508, 241,52501,38305,52514, 1325,52500, + 265,52500,51229,52521, 305,52521, 17,52500, 241,52527,38305,52500, +35187,52501, 265,52501, 331,52535,51803,52535, 4,52500,51601,52541, + 5,52500, 803,52545, 5,52501, 959,52549, 47,52549, 759,52549, + 4,52501, 1275,52500, 9,52559, 17,49713, 1275,52562, 903,52565, + 1369,52562, 241,52569, 2,52562, 903,52573,51593,52573, 3,52562, + 113,52579, 3,52563, 1141,52583, 1447,52583, 33,52583, 2,52563, + 265,52591,51803,52591,38305,49713, 1369,52596, 3,52596, 3,52597, + 8165,52603,20501,52603, 9875,52603, 11,52603, 11,52596, 113,52613, + 15,49713, 1010,52617, 1010,52616, 1011,52616,52619,52623, 1011,52617, +52621,52627, 241,52616, 483,52631, 3,52616, 483,52635, 3,52617, + 1369,52639, 1275,49713, 17,52642, 903,52645, 11,52642, 9,52649, + 241,52642, 9,52653, 9,52643, 1369,52657, 11,52657, 5,52642, + 1505,52663, 5,52643, 53,52667, 1471,52667, 253,52667, 1369,52642, + 9,52675,18967,49713, 241,52679, 9,52681, 5,52678, 17,52685, + 5,52679, 9,52689, 483,49713, 803,52693,52129,52695,43215,52693, +52169,52699, 1216,52693,18759,52703, 413,52693,17993,52707, 803,52692, +49903,52711,48466,52692,43215,52692,49899,52717,40675,52692, 1216,52692, +18617,52723, 413,52692,18391,52727,44243,52692,38305,52693,46169,52733, + 9,52692,46863,52736, 903,52692, 5,52692,10107,52743,51929,52743, +51615,52743, 5,52693,46863,52692, 9,52752, 9,49713, 113,52757, +52151,52759, 4,52756, 7,52763, 2,52765, 2,52764, 2,52763, + 7,52770, 1368,52763, 1216,52757,18759,52777, 413,52757,17993,52781, + 113,52756,49901,52785,48408,52756,25545,52756, 1216,52756,18617,52793, + 413,52756,18391,52797,31103,52756, 483,52756,46863,52802, 1011,52756, + 3,52756,52769,52809,52773,52809,37611,52809,38155,52809,35927,52809, +52489,52809,52775,52809, 3,52757,52767,52825,52151,52825,37663,52825, +52477,52825,31103,52825,46863,52756, 483,52836,31103,49713, 1216,52840, + 413,52840,21036,52840,21037,52840,21037,52841,52847,52851, 903,52841, +51593,52855, 1011,52840, 903,52858,21036,52841,52849,52863, 903,52840, +51435,52867, 1011,52866, 9,52840, 5,52840,27021,52875,44243,49713, + 1216,52878, 413,52878, 903,52878, 1011,52884, 1011,52878, 903,52888, + 483,52878, 3,52878, 11,52895, 1369,52895, 3,52879, 483,52901, +49205,49713, 5,52905,48885,49713, 113,52909, 3,52909, 3,49712, +20819,52915,50945,52915,51508,52915,37537,52915,36898,52915,20152,52915, + 8374,52915,50695,52915,50701,52915,50707,52915,50713,52915,50691,52915, +50705,52915,37748,52915,20952,52915,52395,52915,52457,52915,52142,52915, +52158,52915, 8389,52915,50677,52915,51517,52915,50747,52915,50731,52915, +52418,52915,52478,52915,36979,52915,20201,52915,26219,52915,26106,52915, +52129,52915, 483,52976,52411,52915, 483,52980,51123,52915,32103,52915, +52875,52915,31770,52915,42885,52915,20885,52915, 483,52994,40399,52915, +44714,52915, 357,52915, 367,52915, 471,52915, 1249,52915, 8800,52915, + 8647,52915,47488,52915, 8086,52915,19687,52915,19559,52915,20361,52915, + 9673,52915,20713,52915, 9967,52915, 4283,52915, 4279,52915, 3807,52915, + 8251,52915,41746,52915,41417,52915,44832,52915, 9704,52915,20326,52915, +21087,52915,52906,52915,48245,52915,48975,52915,48451,52915,52151,52915, + 9,53058,52477,52915, 9,53062,51487,52915, 375,53066,50785,52915, + 8165,52915, 265,53073, 1447,52915,38305,53077,21127,52915,20843,52915, +52425,52915,37663,52915, 9,53086, 1141,52915,38305,53091, 566,52915, + 567,52915,46863,53097,44243,52915,41941,53100, 483,53100, 375,52915, +51487,53106,20103,53106, 8337,53106,36773,53106,46863,53106,41941,52915, +44243,53118, 33,52915,38305,53123, 9617,52915, 9,53126, 8337,52915, + 375,53130,48884,52915, 1011,52915,48885,52915, 483,53139,52907,52915, + 25,52915, 7971,53144,41019,52915, 11,53148, 305,52915, 8495,52915, + 11,53154,20103,52915, 375,53158,20273,52915, 9,53162, 4,52915, +28411,53167,31103,53167,48408,53167,52905,53167, 483,53167,46863,53176, +20691,53167, 9923,53167,46863,53167, 483,53184, 5,52915,52905,53188, + 17,53189,18967,53193,46863,53188, 483,53188,25793,52915, 11,53200, + 713,52915, 7971,52915, 25,53206,36773,52915, 375,53210, 4,52914, +53191,53215,53051,53215,24265,53215,28233,53215,29077,53215,53135,53215, +53197,53215,53189,53215,52905,53231,46863,53231,19559,53215, 9673,53215, + 4283,53215,20361,53215, 9967,53215,20713,53215, 8251,53215, 4279,53215, + 3807,53215, 5,52914,53143,53255,53174,53255,50785,53255,53139,53255, +53184,53255,50733,53255,19396,53255,53167,53255,52905,53270,46863,53270, + 8647,53255,41417,53255,26219,53255, 9,53255,18967,53282, 483,53255, +18967,53255, 9,53288,52905,53255,53167,53292,46863,53255,53167,53296, + 11,52915,25793,53301,25793,53300, 8495,53301, 8495,53300,18967,53301, +41019,53301,41019,53300,21037,53301, 9,52915,52151,53318,37663,53318, +52477,53318,31103,53318,20273,53318, 9617,53318,52905,52915, 5,53332, +53215,53335, 5,53333,53167,53339, 9875,52915,35187,53343, 241,53343, +46863,52915, 375,53348, 5,53348,53215,53353, 5,53349,53167,53357, + 483,52915,52129,53360,20885,53360,52411,53360,44243,53360, 5,53361, + 5,53360,18967,52915,11731,53375, 113,53375,31103,52915, 9,53380, +20501,52915,35187,53385, 241,53385, 2,49712,52711,53391,52785,53391, +51059,53391,50165,53391,49995,53391,49999,53391,49987,53391,49991,53391, +50163,53391,52013,53391,52265,53391,52275,53391,17609,53391, 4904,53391, +17586,53391,52259,53391, 483,53423, 272,53391, 1476,53391, 280,53391, + 949,53391, 1517,53391,52269,53391, 9,53437, 9484,53391,51803,53391, +51051,53391, 375,53445, 4903,53391, 9,53448,17579,53391, 9,53452, +42991,53391,40039,53391,40685,53391, 1197,53391, 265,53391, 253,53464, + 1471,53464, 53,53464, 53,53391, 265,53472, 253,53391, 265,53476, + 5,53391, 1471,53391, 265,53482, 5,53390, 8953,53487, 9459,53487, + 4067,53487, 3997,53487, 8277,53487, 3867,53487, 11,53391, 9377,53500, + 9,53391, 4903,53504,17579,53504, 9377,53391, 11,53510, 5,53511, + 5,49712,48765,53517,48835,53517,48843,53517,25445,53517,22779,53517, +22243,53517, 7,53517, 2,53531,28063,53517,25299,53517,31598,53517, +53152,53517,50733,53517,35690,53517,37519,53517,50781,53517,52461,53517, +50953,53517, 272,53517, 1476,53517, 280,53517, 949,53517, 1517,53517, + 357,53517, 367,53517, 471,53517, 1249,53517, 9304,53517,53204,53517, +53144,53517,35931,53517,52912,53517,20326,53517,21087,53517,20273,53517, + 9,53586,50785,53517, 1369,53591,53391,53591,25381,53517,48753,53597, +10081,53597, 9704,53517, 53,53517, 265,53604,37899,53517,37617,53517, +52483,53517, 9617,53517, 9,53614, 253,53517, 265,53618,31103,53517, +26625,53622, 375,53517,35187,53626,26625,53517,31103,53630, 1471,53517, + 265,53634, 265,53517, 253,53638, 1471,53638, 53,53638,49204,53517, +49205,53517,52913,53517, 713,53517, 9195,53652,52915,53652,21605,53653, + 305,53517,52915,53660,21605,53661, 2,53517, 8229,53667,52909,53667, +46863,53667, 3,53517,52909,53674,53391,53675, 1369,53675,46863,53674, +52915,53517, 305,53684, 713,53684, 25,53684, 25,53517,52915,53692, +21605,53693, 9195,53517, 713,53698, 2,53516,53677,53703,53581,53703, +53647,53703,53683,53703,53675,53703,52909,53713,46863,53713, 4067,53703, + 3997,53703, 8953,53703, 9459,53703, 8277,53703, 3867,53703, 3,53516, +53651,53731,53670,53731,50785,53731,50733,53731,53649,53731,53672,53731, +19396,53731,53667,53731,52909,53746,46863,53746, 8647,53731,26219,53731, +41417,53731, 483,53731,18967,53731, 9,53760, 9,53731,18967,53764, +52909,53731,53667,53768,46863,53731,53667,53772, 587,53517,35187,53777, + 483,53517,52909,53517, 3,53782,53703,53785, 3,53783,53667,53789, +46863,53517, 3,53792,53703,53795, 3,53793,53667,53799, 9,53517, +20273,53802, 9617,53802,35187,53517, 375,53808, 4,49712,48747,53813, +37279,53813,19981,53813,24601,53813, 7,53813,21605,53823,53071,53813, +52743,53813,52193,53813,50225,53813, 8306,53813, 545,53813, 695,53813, + 1382,53813, 1374,53813, 1386,53813,51906,53813,53442,53813,53199,53813, +53373,53813,53095,53813,51803,53813, 1369,53856,53391,53856, 9992,53813, +20734,53813,19916,53813,48739,53867,37241,53867, 7947,53867,28205,53813, +24613,53813,25551,53813,53189,53813, 483,53881, 33,53813, 9875,53813, + 265,53886, 241,53887,20501,53813, 265,53892, 241,53893, 1141,53813, + 154,53813, 483,53901, 1447,53813, 331,53813, 1369,53906, 265,53813, + 8165,53910, 9875,53910,20501,53910, 1011,53813, 59,53813, 1369,53920, + 3,53813, 8241,53925, 15,53924, 483,53929,53391,53813,51803,53932, + 1325,53813, 1369,53936, 3,53812,24265,53941,28233,53941,29077,53941, + 9673,53941,19559,53941, 4283,53941,20361,53941, 8251,53941, 3807,53941, + 9967,53941,20713,53941, 4279,53941, 1369,53813, 331,53966, 1325,53966, + 59,53966,51803,53966,18967,53813, 1275,53976,48739,53979,37241,53979, + 7947,53979, 113,53977, 1275,53813,18967,53988,48739,53991, 7947,53991, +37241,53991, 8165,53813, 265,53998,46863,53813,38056,54002,38056,54003, +38057,54002,54007,54009,38057,54003,54005,54013, 15,53813, 3,54017, + 1369,54019, 3,54016, 483,54023, 5,49713, 375,54026,51615,54029, + 1368,54027, 7,54027, 2,54034, 2,54035,32805,54027,31103,54041, +20035,54027, 375,54045, 341,54027,18967,54049,12377,54027, 375,54053, +52915,54027,31103,54057,27021,54027,31103,54061,30091,54027,31103,54065, +31103,54026,27021,54069,52915,54069,18967,54027,47608,54075,46863,54075, + 1011,54078, 1011,54075,46863,54082,53924,54027,53825,54087, 483,54087, +51929,54027, 483,54093,51615,54027, 375,54097, 483,54097, 1369,54026, + 803,54103, 483,54026,53813,54107,10107,54107,51929,54107,51615,54107, + 1275,54026, 1505,54117,18967,54026, 17,54121, 1369,54027,51601,54125, + 2,54026,54078,54129,54075,54129,46863,54132,52541,54129, 1505,54129, + 918,54129,52121,54129,54125,54129, 903,54129, 9,54146, 9,54129, + 903,54150,46863,54129,54075,54154, 3,54026,54033,54159,54037,54159, +53966,54159,12571,54159, 4613,54159,51615,54159, 8337,54159,46863,54173, +52591,54159,53932,54159,53391,54159,53813,54180,53813,54159, 1369,54184, +53391,54184,36773,54159,46863,54191,20103,54159,46863,54195, 1369,54159, +53813,54198, 2,54027, 0,54202,54159,54205,46863,54207, 7,54202, +54159,54211, 265,54203, 9,54215,54159,54203, 9,54219,46863,54219, + 3,54027,48286,54225,54039,54225,54150,54225,52573,54225,52115,54225, +38057,54225,46863,54236,53813,54224,53825,54241, 483,54241, 9,54225, +54129,54246, 483,54225,53813,54251,54154,54225,46863,54225,38057,54256, +54129,54256,54129,54225, 9,54262,46863,54262,10107,54027, 483,54269, +53813,54027, 483,54273, 3,54273, 1369,54277,53391,54277, 3,54272, +53825,54283, 483,54283, 17,54027,20273,54289, 9617,54289, 3,49713, +16722,54295,16530,54294,54297,54299,16723,54295,16531,54294,54303,54305, +16530,54295, 1,54295,35187,54311, 17,54313, 7,54295,16531,54316, +54299,54319,16531,54317,54309,54323, 15,54317,38305,54327, 1,54294, +40705,54331,54329,54331, 17,54331, 7,54294,54333,54339,40704,54339, +54331,54343,49713,54339, 4,54346, 4,54339,49713,54350,54315,54339, + 9,54339,38305,54356,54331,54359,38305,54339, 9,54362,54331,54365, +52489,54295, 9,54369,36527,54295, 375,54373, 341,54295,12377,54295, + 9,54379,16531,54295, 7,54382,54299,54385,37611,54295, 9,54389, + 8321,54295,35927,54295, 9,54395,38155,54295, 9,54399,43503,54295, +44243,54403, 113,54295,38305,54406,40086,54295, 8241,54295,44243,54294, + 11,54415, 1369,54415, 1486,54295,35187,54295,47640,54423,46863,54423, + 903,54426, 903,54423,46863,54430,53480,54295,11731,54295,51615,54295, + 241,54295, 17,54294, 113,54443,38305,54294, 9,54294,37611,54449, +38155,54449,35927,54449,52489,54449, 15,54294, 483,54459, 11,54295, +44243,54463,38305,54295, 113,54466,38057,54295, 7,54471, 375,54471, + 4,54294,54426,54477,54423,54477,46863,54480,52573,54477,54377,54477, +52115,54477,43053,54477,46863,54477,54423,54492, 5,54294,54473,54497, + 6,54497,54471,54501,53966,54497,12571,54497, 4613,54497,51615,54497, + 8337,54497,46863,54513,52591,54497,53932,54497,53813,54497, 1369,54520, +53391,54520,53391,54497,53813,54526,20103,54497,46863,54531,36773,54497, +46863,54535, 1369,54497,53813,54538, 4,54295, 0,54542,54497,54545, +46863,54547, 5,54295,48294,54551,21315,54551,46863,54554,53391,54550, + 1369,54550,46863,54551,21315,54562,53391,54295, 5,54567,53813,54569, + 5,54566, 1369,54295, 9893,54575,52129,54575,54353,54575,54349,54575, +20885,54575,52411,54575,44243,54575, 5,54574, 5,54575,53813,54593, + 2,49713,53227,54597,53262,54597,53355,54597,53358,54597,53298,54597, +53264,54597,53229,54597,53274,54597,53234,54597,49168,54597, 1,54597, + 7,54597, 567,54621,52915,54623, 4,54621,54575,54627,53360,54627, +52915,54627, 483,54632, 483,54627,52915,54636, 9,54621, 1,54596, +36544,54643,35187,54643, 11,54646, 11,54643,35187,54650, 7,54596, +36545,54655,54643,54657,54645,54655,21605,54655,54653,54655,54649,54655, + 15,54655,53270,54597,46863,54670,53231,54597,46863,54674,49169,54597, +54097,54597,53134,54597,53215,54683,53352,54597,53215,54687,53196,54597, +53215,54691, 8311,54597,48884,54596,54617,54697,53731,54597,53881,54597, +54283,54597,54241,54597,54087,54597,36256,54597,53296,54597,53167,54712, +53184,54597,53255,54716,53167,54597,53357,54720,53296,54720,53255,54720, +46863,54726,46863,54720,53255,54730,48885,54597, 9,54734,54697,54737, +54697,54735, 9,54741,35187,54597, 9,54744, 9,54597,48885,54748, +54697,54751,48885,54749,35187,54748, 11,54749,52915,54759,53188,54597, +53215,54763,46863,54765,46863,54762,53215,54769,53813,54763,53139,54597, +53255,54774,44243,54597, 11,54779,54225,54597,53813,54783,51229,54597, + 265,54597,54027,54789,47961,54791,14611,54791, 4899,54791, 375,54791, + 17,54596, 903,54801,51593,54801,54477,54801,54225,54801, 11,54596, +53318,54811,52825,54811,52915,54811, 9,54816, 9,54811,52915,54820, +44243,54811,53255,54597,53139,54826,53184,54826,53167,54826,46863,54832, +46863,54826,53167,54836,53357,54597,53167,54840, 4,54596,51418,54845, +51416,54845,51398,54845,51354,54845,51909,54845, 340,54845,51803,54857, +51873,54845,47974,54845,47589,54845, 8590,54845,14620,54845,12537,54845, +52021,54845,52029,54845,53106,54845, 375,54845,51229,54878,52915,54878, + 0,54845, 6,54885, 7,54845, 265,54888,51803,54891, 265,54889, +51229,54895,52915,54895,47580,54845,51803,54901, 8861,54901, 8495,54845, +47961,54906, 4899,54906,14611,54906,52915,54845, 375,54914,14611,54845, +51229,54918, 8495,54918, 4899,54845,51229,54924, 8495,54924,47961,54845, +51229,54930, 8495,54930, 265,54845, 7,54936,51803,54939,54887,54937, +54886,54936,54943,54945,54887,54936,54886,54937,54949,54951,46863,54936, + 8861,54955,51803,54955,46863,54845, 265,54960,51803,54963, 8861,54963, + 265,54961,51229,54969, 8495,54969,51229,54845, 375,54974,14611,54974, + 4899,54974,47961,54974, 5,54596, 1505,54985, 918,54985,52541,54985, +52121,54985,54125,54985, 903,54985, 9,54996, 9,54985, 903,55000, + 5,54597,54889,55005,52915,55007,53349,55004, 6,55005,54914,55013, +52915,55013,54845,55016,54845,55013,52915,55020, 53,55005, 1471,55005, + 253,55005,52557,55005,53348,55004,53215,55033, 241,55005, 9,55037, +52915,55004,53215,55041,46863,55043,46863,55040,53215,55047,53813,55041, + 17,55005,35187,55053,52915,55005,53357,55057,55011,55057,53296,55057, +53255,55057,46863,55064,46863,55057,53255,55068,46863,55004,52915,55072, +53215,55075,52915,55073,53255,55079,48885,54596,54679,55083, 9,55083, +48884,54597,54755,55089,55086,55089,55083,55089, 9,55094,52915,55088, +53215,55099,52915,55089,53255,55103, 9,55089,55083,55106,53348,54597, + 5,55110,53215,55113, 5,55111,53167,55117,52915,54597,48884,55120, +53215,55123,48884,55121,53139,55127, 5,55121,53139,55131,53184,55131, +53167,55131,46863,55136,46863,55131,53167,55140, 5,55120,53215,55145, +46863,55147,46863,55144,53215,55151,53813,55145,46863,55120, 5,55156, +53215,55159, 5,55157,53167,55163,21605,54597,54027,55167,31103,55169, +46863,54597,53270,55172,53231,55172,53188,55172,53215,55179,53255,55172, +53167,55182,53167,55172,53255,55186, 5,55172,52915,55190,53215,55193, +52915,55191,53255,55197,52915,55172, 5,55200,53215,55203, 5,55201, +53167,55207, 4,49713,51398,55211, 375,55211,51229,55214,53709,55211, +53740,55211,51909,55211,53797,55211,53800,55211, 340,55211,51803,55229, +54339,55210,54575,55233,53774,55211,53742,55211,53711,55211,53750,55211, +53716,55211,54663,55211,53533,55211, 1368,55211,52205,55251,54449,55251, +52809,55251,54295,55251, 9,55259, 7,55211, 265,55263,51229,55265, + 89,55263,52915,55269, 265,55262,51803,55273, 2,55262,52205,55277, +52809,55277,54449,55277,54295,55277, 9,55285, 2,55263,52825,55289, +53318,55289, 9,55289,52915,55294,52915,55289, 9,55298,53746,55211, +46863,55302,53713,55211,46863,55306,31796,55211,25037,55211,25215,55211, +42338,55211,42076,55211,43053,55211,42959,55211,54575,55211,54339,55325, +54786,55211,51360,55211,53780,55211,52751,55211, 8148,55211, 9594,55211, +54377,55211,54439,55211,53758,55211,54700,55211,54826,55211,53286,55211, +53646,55211,53703,55353,53794,55211,53703,55357,53682,55211,53703,55361, +53371,55211, 842,55211, 9377,55367,54591,55211,54421,55211,54561,55211, +53681,55211,53255,55211,54597,55378, 483,55378,53679,55211,54573,55211, +55131,55211,54559,55211,54435,55211,19892,55211,53772,55211,53667,55396, +53672,55211,53731,55400,53667,55211,53799,55404,53772,55404,53731,55404, +46863,55410,46863,55404,53731,55414, 935,55211,20733,55211, 9991,55211, +41941,55211, 483,55424, 9565,55211, 483,55428, 1487,55211,54597,55211, +51229,55434, 53,55435, 1471,55435, 253,55435,53731,55434,53255,55434, +47546,55211, 66,55211, 1369,55451, 241,55211,31103,55454,18967,55211, + 483,55458, 483,55211,42155,55462,41941,55462,51229,55462,53517,55462, + 7971,55462, 9565,55462,53731,55462,53255,55462, 587,55462, 9377,55481, +18967,55462, 587,55463, 9195,55487,52915,55487,53674,55211,53703,55493, +46863,55495, 1369,55493,46863,55492,53703,55501,53391,55493,53649,55211, +53731,55506,31103,55211, 241,55510, 587,55511, 113,55211,46863,55516, +17455,55211,54551,55211, 1369,55523,53391,55523,51229,55211, 375,55528, +54597,55528, 483,55528, 1011,55211, 8165,55537, 9875,55537,20501,55537, + 11,55537, 265,55211, 7,55546,51803,55549,54295,55547,47961,55553, +14611,55553, 4899,55553, 375,55553, 1369,55210,54985,55563,51601,55563, +54129,55563, 587,55210, 9366,55571,54575,55571,53360,55571, 9195,55571, + 483,55578,52915,55571, 483,55582, 483,55571, 9195,55586,52915,55586, +31103,55571,53731,55211,53649,55594,53672,55594,53667,55594,46863,55600, + 483,55594,54597,55594,46863,55594,53667,55608, 587,55211, 9377,55613, + 483,55615, 483,55612, 9377,55619,21605,55211,21526,55622, 6,55623, +21527,55622,55627,55629, 7,55622, 7,55623,55625,55635, 6,55622, +18967,55638,55635,55641,18967,55622, 6,55645,55633,55647, 6,55644, +55635,55651, 1347,55211, 1369,55655,53799,55211,53667,55658, 2,55210, +51418,55663,51416,55663,51398,55663,51354,55663,51909,55663, 340,55663, +51803,55675,51873,55663,47974,55663,47589,55663, 8590,55663,14620,55663, +12537,55663,52021,55663,52029,55663,53106,55663, 375,55663,51229,55696, +52915,55696, 0,55663, 6,55703, 7,55663, 265,55706,51803,55709, + 265,55707,51229,55713,52915,55713,47580,55663,51803,55719, 8861,55719, + 8495,55663,47961,55724, 4899,55724,14611,55724,52915,55663, 375,55732, +14611,55663,51229,55736, 8495,55736, 4899,55663,51229,55742, 8495,55742, +47961,55663,51229,55748, 8495,55748, 265,55663, 7,55754,51803,55757, +55705,55755,55704,55754,55761,55763,55705,55754,55704,55755,55767,55769, +46863,55754, 8861,55773,51803,55773,46863,55663, 265,55778,51803,55781, + 8861,55781, 265,55779,51229,55787, 8495,55787,51229,55663, 375,55792, +14611,55792, 4899,55792,47961,55792, 3,55210,54801,55803,52573,55803, +52115,55803,43053,55803,46863,55803, 2,55211, 7,55814,52205,55817, +52809,55817,54449,55817,54295,55817, 9,55825,54295,55815,54501,55829, + 7,55829,54497,55833, 375,55829, 3,55211,55707,55839,52915,55841, +55706,55839,53793,55838,55779,55839, 6,55839,55732,55851,52915,55851, +55663,55854,55663,55851,52915,55858, 0,55851,55829,55851, 0,55850, +55663,55867, 0,55839, 6,55870,55663,55873, 0,55838,49713,55838, +55845,55879, 1,55838,55868,55883,55874,55883,55867,55883,55663,55888, +55873,55883,55663,55892,55849,55883,55663,55883,55867,55898,55873,55898, +46862,55839,55898,55905,55883,55905,55663,55908,55663,55905,55883,55912, + 1,55839,55863,55917,55663,55919, 7,55839,55663,55922,55879,55925, +55879,55923,55663,55929,52591,55839,53792,55838,53703,55935,43027,55839, +43837,55839,39747,55839,53517,55838,53703,55945,46863,55947, 1369,55945, +46863,55944,53703,55953,53391,55945, 1369,55839,18967,55959, 11,55838, + 1369,55963,18967,55839,46863,55967, 11,55839,46863,55971,53517,55839, +53799,55975,55847,55975,53772,55975,53731,55975,46863,55982,46863,55975, +53731,55986,55778,55839,55877,55991,46863,55839,55877,55995,55663,55997, +55663,55994,55877,56001,55663,55839, 7,56004,55879,56007,46863,56004, +55877,56011,46863,55838,53517,56014,53703,56017,53517,56015,53731,56021, +49204,55211,53517,56024,53703,56027,53517,56025,53731,56031,53792,55211, + 3,56034,53703,56037, 3,56035,53667,56041, 7971,55211, 483,56044, +42155,55211, 483,56048,53517,55211, 483,56052,49204,56052,53703,56057, +49204,56053,53649,56061, 3,56053,53649,56065,53672,56065,53667,56065, +46863,56070, 483,56065,54597,56065,46863,56065,53667,56078, 3,56052, +53703,56083,46863,56085, 1369,56083,46863,56082,53703,56091,53391,56083, +46863,56052, 3,56096,53703,56099, 3,56097,53667,56103, 11,55211, + 3,56106, 1369,56109, 3,56107, 483,56113,46863,55211,53746,56116, +53713,56116,53674,56116,53703,56123,53731,56116,53667,56126,53667,56116, +53731,56130, 113,56116, 3,56116,53517,56136,53703,56139,53517,56137, +53731,56143,53517,56116, 3,56146,53703,56149, 3,56147,53667,56153, +46863,49713,47982,56156,47983,56157,56159,56161,47983,56156,47982,56157, +56165,56167,49588,56156,49589,56157,56171,56173,49670,56156,49671,56157, +56177,56179,49589,56156,49588,56157,56183,56185,49671,56156,49670,56157, +56189,56191, 8453,56156, 273,56156,21522,56156,38266,56156, 1516,56156, + 948,56156,20239,56156,37095,56156, 1477,56156, 281,56156,49617,56156, +49616,56157,56215,56217,49699,56156,49698,56157,56221,56223,49617,56157, +49616,56156,56227,56229,49699,56157,49698,56156,56233,56235, 1216,56156, + 412,56156,12377,56156, 375,56156, 510,56156,38057,56156, 903,56248, + 165,56156, 903,56252,21315,56156, 1011,56256, 1505,56156, 1011,56260, + 413,56157,50785,56265,56241,56265, 1217,56157,56239,56271, 1216,56157, + 413,56156, 412,56157,56277,56279, 1217,56156,56275,56283, 264,56157, +51777,56287,50908,56287,50785,56287, 241,56292, 241,56287,50785,56296, + 375,56287, 264,56156,50207,56303,51765,56303,51677,56303,51615,56303, + 241,56311, 341,56303, 1011,56156, 1505,56316,21315,56316, 903,56156, + 165,56322,38057,56322, 483,56156, 9,56328, 9,56156, 483,56332, + 5,56157, 4,56156,56337,56339,46863,56340, 2,56343, 2,56342, +49515,56340,49514,56340, 2,56341,56349,56353, 3,56341,56347,56357, +56351,56357, 2,56340,46863,56362,56357,56365,56357,56363, 3,56340, +56345,56371,56353,56371, 3,56157, 375,56377, 2,56156,56337,56381, + 1,56383, 0,56382,56385,56387, 0,56383, 1,56382,56391,56393, +56377,56381,46863,56396, 4,56399, 4,56398,49631,56396,49630,56396, + 4,56397,56405,56409, 5,56397,56403,56413,56407,56413, 4,56396, +46863,56418,56413,56421,56413,56419, 5,56396,56401,56427,56409,56427, + 2,56157, 3,56156,56337,56435, 7,56437, 6,56436,56439,56441, + 6,56437, 7,56436,56445,56447,56433,56435,46863,56451, 4,56452, + 4,56453,49631,56451,49630,56451, 4,56451,46863,56462, 5,56451, +56457,56467, 4,56450,56467,56471,56459,56471, 5,56450,56465,56477, +56455,56477,56463,56477,56461,56477, 4,56157,56435,56487, 1,56489, + 0,56488,56491,56493, 0,56489, 1,56488,56497,56499,56381,56487, + 7,56503, 6,56502,56505,56507, 6,56503, 7,56502,56511,56513, + 5,56156,56487,56517,46863,56519, 2,56520, 2,56521,49515,56519, +49514,56519, 2,56519,46863,56530, 3,56519,56525,56535, 2,56518, +56535,56539,56527,56539, 3,56518,56533,56545,56523,56545,56531,56545, +56529,56545, 1, 2,11709,56555,11632,56555,11035,56555,10468,56555, +17775,56555,11788,56555,12226,56555,11816,56555,11770,56555,44558,56555, +42181,56555,44243,56576,44438,56555,10856,56555, 3447,56555, 375,56584, + 3448,56555,11705,56555,11033,56555,10516,56555,17773,56555,18735,56555, +10427,56555,10831,56600,10836,56555,10855,56555,16623,56555,16711,56555, + 9533,56610,16718,56555, 3555,56555, 5882,56555, 9261,56555, 483,56621, + 3699,56555, 15,56625, 6,56624,56627,56629, 14,56625, 7,56624, +56633,56635, 1999,56555, 15,56639, 6,56638,56641,56643, 14,56639, + 7,56638,56647,56649,29450,56555,29731,56653,29659,56653,28791,56653, +29041,56653,38067,56555, 5,56663,38185,56665,42155,56554,44243,56669, +42471,56670,44966,56669,42471,56669,44243,56676, 1471,56554,36639,56681, +15390,56555,15383,56685,28715,56555,21527,56689,29229,56691, 7,56689, +29759,56695, 7,56688,28791,56699,29041,56699,29731,56699,29659,56699, + 1005,56555,16877,56554,16893,56711, 9925,56555, 1369,56715, 9927,56717, +35336,56555, 989,56721,35278,56555, 1177,56725, 987,56555,35187,56728, + 989,56731,36563,56554,36639,56735, 671,56554,36597,56554,36639,56741, + 1418,56554, 1,56745, 4,56746, 4,56745, 1,56750, 147,56555, +35187,56754, 53,56757,17637,56555, 587,56760,10107,56763,35316,56555, + 1471,56767,35330,56555, 53,56771,17638,56555,10107,56775, 1489,56555, +35187,56778, 1471,56781, 1179,56555,35187,56784, 1177,56787,37229,56555, +35187,56790,37223,56793, 9441,56555, 587,56796, 9439,56799,37230,56555, +37223,56803, 9442,56555, 9439,56807,45432,56555,42435,56811, 30,56555, +11731,56814,45433,56555,56669,56819,43099,56555,52259,56555, 483,56825, +44742,56555,44904,56555,52613,56555,52383,56555,52497,56555,39231,56555, +40447,56555,39083,56555,38797,56555,39019,56555,39301,56555,39429,56555, +39305,56555, 386,56555, 347,56555,54492,56555,45508,56555,56116,56555, +55812,56555,40987,56555,52909,56555,51598,56555,50419,56555,56332,56555, +52836,56555,52366,56555,36639,56555, 1471,56881,36563,56881,36597,56881, + 181,56555,11731,56888,50579,56555,52319,56555,52323,56555,54436,56555, +54406,56555,52532,56555,52514,56555,55520,56555,44930,56555,39317,56555, +51141,56555,40123,56555,39228,56555,40676,56555,47366,56555,40682,56555, +52599,56555,50655,56555,52531,56555,36538,56555,36048,56555,37594,56555, +42918,56555,40922,56555,39292,56555,11111,56555,11113,56555,11187,56555, +17508,56555,17180,56555, 3280,56555, 3574,56555, 3568,56555,12216,56555, + 25,56958,11613,56555, 25,56962,48406,56555,48400,56555,48500,56555, +43292,56555,47534,56555,17494,56555,39065,56555,48961,56555,49162,56555, +17496,56555,11179,56555,47892,56555,11546,56555,11207,56555, 5878,56555, +52446,56555,35506,56555,39035,56555,40087,56555, 9,57003,10513,56555, + 9,57006,36102,56555, 253,57011, 53,57011, 117,57011, 1471,57011, + 7968,56555,53813,57021,10107,57021,11067,57021,37282,56555,53813,57029, +37417,57029,10107,57029,35652,56555, 253,57037, 1471,57037, 809,57037, + 53,57037,38293,56555,53813,57047,37417,57047,38275,57047,10107,57047, + 1397,56555,35187,57056,52439,56555, 265,57060,40127,56555,43318,56555, +48750,56555,53813,57069,10107,57069,36100,56555, 253,57075, 53,57075, + 1471,57075, 9404,56555, 483,57083, 8053,56555, 9,57087,10465,56555, + 9,57090,11936,56555,10119,56555,10185,56555,53885,56555,10117,56555, +45490,56555, 33,57105, 8212,56555, 9,57109,11812,56555, 9,57112, +53899,56555, 6719,56555, 11,57119, 5,57120, 6,57119, 5,57124, + 5,57119, 6,57128, 6,57129, 7,57119,57133,57135, 15,57119, + 6,57118,57139,57141, 14,57119, 7,57118,57131,57147,57127,57147, +57123,57147,57145,57147, 6787,56555, 5,57157, 4,57157, 12,57157, + 13,57156,57163,57165, 13,57157, 12,57156,57169,57171, 5,57156, +57161,57175, 4,57156,57159,57179,10831,56555, 375,57182,44243,57182, +10427,57182, 9,57183,11529,56555,46863,57192, 483,57192,17640,56555, +39226,56555,48572,56555,47410,56555,40568,56555,47374,56555,47674,56555, +17656,56555,47676,56555,47634,56555,48112,56555,47412,56555, 5814,56555, + 918,56555,36256,56555,46863,57226,48466,56555,49713,57230,35187,57230, + 11,57231,47608,56555, 903,57238,52756,56555,46863,57242,48620,56555, + 247,56555,46863,57248, 1419,56555,56749,57253,56753,57253,25519,57253, + 1841,57253, 1883,57253,22679,57253, 5,57253,22677,57267,14065,57253, + 5175,57253,40565,56555, 587,57274, 922,56555,51593,56555,46863,57280, +47640,56555, 11,57285, 1011,57284, 11,57284,35718,56555, 9439,57293, +37223,57293, 9381,57293,37157,57293,35877,57293,35881,57293, 9383,57293, +37163,57293,35883,57293, 17,57293,37145,57313, 9377,57313,35867,57313, +51051,56555, 375,57321,50959,56555, 9,57325,39205,56555, 10,57329, + 10,57328, 11,57329,57333,57335, 1011,57328, 11,57328,57331,57341, +48402,56555, 9,57344,48560,56555, 903,57348,36544,56555, 6,57353, +36661,57355,36675,57353, 8067,57353, 7979,57353, 7981,57353,39747,57353, +43837,57353,43027,57353, 1369,57353, 7971,57373, 934,56555,46863,57376, + 668,56555, 5,57381,22605,57383,22615,57381, 3135,57381, 2009,57381, +14065,57381, 5175,57381,53905,56555,55432,56555,10303,56555,42661,56555, + 25,56555,11613,57404,12216,57404, 9,57404,11731,57410,35187,57404, + 253,57415, 53,57415, 117,57415, 1471,57415,11731,57404, 9,57424, + 8165,56555, 67,57428, 9,57431, 7947,56555, 587,57434,53813,57437, +10107,57437,11067,57437,17049,56555,46863,57444, 165,56555,43215,57448, + 113,56555,54295,57452, 767,56555, 483,57457, 9377,57459, 9377,57456, + 483,57463, 803,56555,17455,57466, 375,56555,10831,57470, 3447,57470, + 305,57470,17125,57470, 510,56555, 11,56555, 33,57483,44243,57485, +44243,57482, 33,57489,39205,57482,47640,57482,46863,57482, 903,57496, +35187,57482,43027,57501,43837,57501, 8067,57501, 7979,57501,39747,57501, + 7981,57501, 1369,57501, 7971,57515, 903,57482,46863,57518, 9,56555, +10465,57522,11812,57522,10513,57522, 25,57522,11731,57530,56156,57522, +40675,57522, 3565,57522, 3573,57522, 3273,57522,11731,57522, 25,57544, +48402,57522,17455,57522, 5843,57522, 11,57523,46863,57555,49713,57522, +46863,57558, 903,57522,35187,57522,46863,57564,46863,57522,49713,57568, +35187,57568, 11,57569, 587,57522, 5175,57577,14065,57577, 3135,57577, + 2009,57577, 483,57522,55839,56555, 483,57589, 5771,56555, 903,57592, + 6705,56555, 1369,57597, 6707,57599, 6711,57599, 6715,57599, 3697,56555, + 16,57607, 16,57606, 17,57607,57611,57613, 17,57606,57609,57617, + 3709,56555, 8075,56555, 9,57623, 3273,56555, 9,57626, 3423,56555, + 16,57631, 16,57630, 17,57631,57635,57637, 17,57630,57633,57641, + 3661,56555, 16,57645, 16,57644, 17,57645,57649,57651, 17,57644, +57647,57655, 3681,56555, 3357,56555,53675,56555, 9,57663,42379,56555, + 9,57667, 3573,56555, 9,57670, 3565,56555, 9,57674,52878,56555, +15436,56555, 375,57681,54550,56555,17232,56555,38305,57686, 4935,56555, + 6,57691, 5,57692, 7,57691, 5,57691, 6,57698, 6,57699, +57697,57703, 6,57690, 15,57691,57707,57709, 7,57690,57695,57713, +57701,57713, 14,57691,57713,57719,55803,56555,46863,57722,16127,56555, + 6,57727, 5,57728, 7,57727, 5,57727, 6,57734, 6,57735, +57733,57739, 6,57726, 15,57727,57743,57745, 7,57726,57731,57749, +57737,57749, 14,57727,57749,57755,54477,56555,46863,57758,49155,56555, + 483,57762, 5843,56555, 955,57766, 9,57766, 5313,56555, 9802,56555, +48736,56555, 9434,56555, 483,57779,15309,56555, 413,57782,15383,57785, + 413,57783,15335,57789, 8,56554, 4,57793, 1,57794,57381,57797, +57577,57797, 1,57793, 4,57802,57381,57805,57577,57805, 10,56554, + 7,57811, 2,57812,57353,57815,57501,57815, 2,57811, 7,57820, +57353,57823,57501,57823,57515,57811,57373,57811, 1368,57811,57501,57833, +57353,57833, 3867,57811, 8277,57811, 3997,57811, 4067,57811, 9459,57811, + 8953,57811,57483,57811, 9,57811, 14,56555,38881,57855, 11,56554, +44778,57859,44243,57859, 25,57862,47640,57859, 25,57859,44243,57868, +48466,57859, 903,57859,46863,57874,46863,57859, 903,57878, 9,57878, + 9,57859,46863,57884, 9,56554,40903,57889,40053,57889,43307,57889, + 5019,57889,12361,57889, 16,56555,19625,57901,21105,57901,21549,57901, +17591,57901, 17,56554,36639,57911, 10,56555,57859,57915,11731,56555, + 30,57918, 181,57918,54295,57918, 9,57918, 25,57926, 145,57918, + 25,57918, 9,57932, 67,56555, 9,57937, 8165,57939, 8165,57936, + 9,57943,17635,56555, 155,57946, 903,57946, 145,56555,11731,57952, +17695,56555, 1369,57957, 7971,57959,53517,57959,17053,57959,17699,57959, +42155,57959, 155,56555,17635,57970,10305,56555,42699,56555,10307,56555, + 566,56555,53813,57981,10107,57981, 256,56555,42810,56555,17125,57988, + 1170,56555, 629,56555, 1413,56555,37408,56555,25793,57999,41019,57999, + 8495,57999, 265,57998, 1346,56555,48884,56555,27021,58011,52915,58011, + 9195,58011,20763,58011, 340,56555, 331,58021,35187,58020,38270,56555, + 766,56555,25519,58029, 802,56555, 9366,58033, 9195,58033, 483,58036, + 483,58033, 9195,58040,31103,58033,42729,56555, 257,56555,44243,58048, + 1487,56555,55211,58052,44243,56555,10831,58056,42181,58056,42155,58057, +42471,58063,42155,58056,42435,58067, 9533,58056,37145,58056, 11,58057, + 25,58075,35187,58056,17125,58056, 11,58056, 33,58083,49713,58056, + 257,58056,55211,56555,46863,58090,17455,58090, 1487,58090, 1197,56555, + 4,58099, 5,58098,58101,58103, 5,58099, 4,58098,58107,58109, + 1505,56555,45002,56555,42155,56555,42435,58117,44243,58119,44243,58116, +42435,58123, 305,56555, 375,58126,35187,58126, 253,58131, 1471,58131, + 53,58131, 713,56555,35187,58138, 253,58141, 1471,58141, 809,58141, + 53,58141,37241,56555, 587,58150,53813,58153,37417,58153,10107,58153, + 955,56555, 5843,58160,48739,56555, 587,58164,53813,58167,10107,58167, +40461,56555, 9,58173,52501,56555,35187,58176, 241,58176, 9801,56555, + 17,58182, 4,58183,58185,58187, 5,58183,57901,58191, 4,58182, + 7,58194,58191,58197, 5,58182,48735,56555, 17,58202, 4,58203, +58205,58207, 5,58203,57901,58211, 4,58202, 7,58214,58211,58217, + 5,58202, 9533,56555,16711,58222,44243,58222,37145,56555,44243,58228, + 8861,56555, 5,58233, 6,58235, 6,58234, 14,58233, 15,58233, + 6,58233, 5,58244, 7,58233,58237,58249, 6,58232,58243,58253, + 7,58232,58239,58257,58247,58257,58241,58257, 9377,56555, 767,58264, + 483,58267, 4,58264, 483,58271, 1217,56555, 483,58275, 413,56555, +15309,58278,15383,58281,15309,58279,15363,58285,46863,58278, 412,56555, + 483,58291, 1216,56555,46863,58294,56156,56555, 9,58298, 1011,56555, +39205,58302,47640,58302,46863,58302, 903,58308, 7,58302, 903,58302, +46863,58314,43215,58302, 7,58319,38305,56555,17232,58322, 7,58322, +17125,58326,17125,58322, 7,58330,49713,56555,48466,58334, 9,58334, +46863,58338,44243,58334,46863,58334, 9,58344, 241,56555,52501,58348, + 7,58348, 7,58349, 265,58355, 265,58348, 483,58359, 483,56555, +40675,58362,43215,58362,49155,58362,11529,58362, 903,58362, 9,58362, + 5,58362,53813,58377,10107,58377, 5,58363,55211,58383,40675,56555, + 7,58387, 9,58386, 483,58386,46863,58386,35187,56555,37229,58396, +37223,58399, 1177,58397, 1179,58403, 989,58397, 987,58407, 1179,58396, + 1177,58411, 987,58396, 989,58415, 1489,58396, 1471,58419, 147,58396, + 53,58423, 117,58397, 25,58427, 809,58397, 713,58431, 2,58396, + 7,58435, 2,58436,58397,58439, 5,58436,58441,58443, 5,58435, + 7,58446, 10,58435, 4,58396, 7,58453, 4,58454,58397,58457, + 3,58454,58459,58461, 3,58453, 7,58464, 586,58453, 1368,58396, + 7,58471, 5,58472, 5,58471, 7,58476, 10,58471, 16,58396, + 7,58483, 3,58484, 3,58483, 7,58488, 586,58483,44243,58396, +52501,58396, 340,58396,48466,58396, 1397,58396, 25,58396, 1471,58505, + 253,58505, 53,58505, 117,58505, 713,58396, 53,58515, 253,58515, + 1471,58515, 809,58515, 53,58397, 147,58525, 305,58525, 713,58525, + 25,58525, 1471,58397, 1489,58535, 305,58535, 25,58535, 713,58535, + 305,58396, 253,58545, 1471,58545, 53,58545, 253,58397, 305,58553, + 25,58553, 713,58553, 9,58396,46863,58560, 1369,58397, 9891,58565, + 9927,58565,58475,58565,58479,58565,58443,58565,58449,58565, 4,58565, + 9895,58565,19687,58565,20843,58565,21127,58565,58451,58565,58481,58565, + 11,58565, 9875,58593,58435,58593,58471,58593, 17,58397,10111,58601, +10159,58601,58487,58601,58491,58601,36729,58601,58461,58601,58467,58601, + 2,58601,10113,58601,58469,58601,58493,58601, 587,58601,10107,58625, +58453,58625,58483,58625,46863,58396, 9,58632, 11,58396,57815,58637, +57823,58637, 8067,58637, 7979,58637, 3,58637,58601,58647,58617,58637, + 7981,58637,39747,58637,43027,58637,43837,58637,57833,58637, 1369,58637, + 7971,58663,57811,58663, 587,58396,35877,58669,35881,58669, 9439,58669, + 9381,58669, 5,58669,58565,58679,58579,58669,37223,58669,37157,58669, + 9383,58669,37163,58669,35883,58669, 17,58669, 9377,58695,37145,58695, +35867,58695, 6,58396, 7,58396,25793,58705,41019,58705, 8495,58705, + 265,58704, 6,58397,36661,58715,58705,58715,57999,58715, 7,58397, +58703,58723,58027,58723, 265,58396, 6,58728, 7,58728, 6,58729, +58733,58735, 7,58729,58731,58739, 903,56555,17455,58742,17635,58742, +47608,58742, 5771,58742,48560,58742, 11,58743,46863,58755, 9,58742, + 483,58742,46863,58742, 11,58763, 1011,58762, 11,58762, 11,58742, +46863,58770, 7,58743, 8165,58775, 1011,58742,46863,58778,46863,56555, +21036,58782,21036,58783,21037,58782,58787,58789,21037,58783,58785,58793, +54477,58782,55211,58782,55803,58782,52756,58782,51593,58782,40675,58782, +43215,58782,36256,58782,11529,58782, 934,58782, 247,58782, 1216,58782, + 413,58782,17049,58782, 11,58783, 903,58825, 9,58825, 9,58782, +49713,58830,35187,58830, 11,58831, 1011,58782, 903,58838,35187,58782, + 9,58842, 903,58782, 11,58847, 1011,58846, 11,58846, 11,58782, + 903,58854, 5,58782,27021,58859,52915,58859, 9195,58859,20763,58859, +49713,58782, 9,58868, 1275,56555, 17,58872, 4,58872, 7,58876, + 5,58872, 4,58873,58875,58883, 5,58873,58879,58887,57901,58887, + 265,56555,37408,58892,52439,58892, 7,58892, 331,58899,35187,58898, + 7,58893, 375,58905, 241,58892, 483,58909,38305,58893, 375,58913, +35187,58892, 6,58916, 7,58916, 6,58917,58921,58923, 7,58917, +58919,58927, 587,56555, 9441,58930, 9439,58933,17637,58930,10107,58937, +37417,58931,37241,58941,11067,58931, 7947,58945, 7,58930, 4,58949, + 7,58950,58931,58953, 0,58950,58955,58957, 0,58949, 4,58960, +35186,58949, 1,58930, 4,58967, 1,58968,58931,58971, 6,58968, +58973,58975, 6,58967, 4,58978, 8,58967, 16,58930, 4,58985, + 0,58986, 0,58985, 4,58990,35186,58985,22807,58931,37241,58930, +10107,58999,53813,58999,37417,58999, 7947,58930,53813,59007,10107,59007, +11067,59007,10107,58931,17637,59015,48739,59015,37241,59015, 7947,59015, +48739,58930,53813,59025,10107,59025,53813,58931,48739,59031,37241,59031, + 7947,59031,40565,58930, 17,58931, 1985,59041, 3721,59041,58989,59041, +58993,59041, 5299,59041,58957,59041,58963,59041, 1,59041,35233,59041, +35917,59041, 4861,59041,17563,59041,58965,59041,58995,59041,35187,59041, + 53,59071,58949,59071,58985,59071,35187,58930,35881,59079,35877,59079, +37223,59079,37157,59079, 6,59079, 9439,59079, 9381,59079,37163,59079, + 9383,59079,35883,59079, 17,59079,37145,59101, 9377,59101,35867,59101, + 9,58930,57797,59109,57805,59109, 3135,59109, 2009,59109, 0,59109, +59041,59119,59057,59109, 5175,59109,14065,59109, 4,58930, 9366,59129, + 9195,59129, 483,59132, 483,59129, 9195,59136,31103,59129, 5,58930, +25519,59143, 5,58931,25503,59147,24623,59147,30387,59147,58565,59147, +22605,59147,36526,56555, 6,59158, 7,59158, 6,59159,59163,59165, + 7,59159,59161,59169,40920,56555, 7,59172,43400,56555, 7,59177, +16065,56555, 5,59181, 7,59182, 7,59183,56555,59186, 6,59182, +59189,59191, 6,59183,56555,59183, 7,59196,59191,59199, 7,59197, +59195,59203,15029,56555, 5,59206, 6,59209, 6,59208, 14,59206, + 15,59206, 6,59206, 5,59218, 7,59206,59211,59223, 375,59223, + 6,59207,59217,59229, 7,59207,59221,59233,59213,59233, 59,59233, +59215,59233, 331,59233, 1325,59233, 265,59233,43215,56555, 9,59249, + 483,59248,46863,59248, 165,59248, 1011,59248, 7,59259,17125,56555, + 4,59262, 7,59265, 1011,59266, 1011,59265, 7,59270, 7,59271, + 1170,59265, 5,59263,57993,59279,58313,59279,17129,59279,42811,59263, +42811,59262,44243,59262,42810,59262,59287,59293,42810,59263,59289,59297, + 375,59262, 6,59262,59269,59303,59273,59303,59277,59303, 7,59262, +38305,59310, 6,59263,59275,59315,59311,59315, 7,59263,59303,59321, +38305,59262, 7,59324,54295,56555,11731,59328, 113,59328, 5,59328, + 5,59329,57901,59337,17455,56555, 7,59341, 4,59342, 4,59343, + 16,59341, 17,59341,55211,59340, 803,59340, 903,59340, 9,59340, + 4,59340,59351,59361, 5,59340,59345,59365,59349,59365, 4,59341, + 7,59370,59365,59373, 5,59341,59347,59377, 4,56554,26454,59381, +26375,59381, 8672,59381, 8743,59381,41692,59381,41607,59381, 8770,59381, + 8729,59381, 3795,59381,47953,59381,48062,59381,47961,59381,48009,59404, +48009,59381,47961,59408,26219,59381,26415,59412,41417,59381,41653,59416, +44832,59381,31770,59381, 8757,59381, 8647,59424,41653,59381,41417,59428, +26415,59381,26219,59432, 8647,59381, 305,59436, 8757,59436, 8605,59381, +47580,59381,47933,59445, 8684,59381, 8717,59449, 8916,59381,44243,59381, + 483,59454,41502,59381,41559,59459, 340,59381, 8717,59463, 3785,59463, + 8495,59463, 375,59381, 8861,59470,46863,59470,47488,59381,26292,59381, +26331,59479, 9,59381,31103,59482, 305,59381, 8647,59486, 7,59381, + 1,59491, 264,59492, 264,59493,25793,59491,26415,59499,41019,59491, +41653,59503, 8495,59491, 305,59507, 8757,59507, 8495,59490, 8717,59513, +41019,59490,41559,59517, 265,59491,59497,59521, 8861,59521,46863,59521, +25793,59490,26331,59529, 265,59490, 8717,59533,59495,59533, 3785,59533, + 8495,59533,31103,59381, 9,59542, 483,59381,44243,59546,46863,59381, + 265,59551,48009,59553, 265,59550,47933,59557, 375,59550,41019,59381, + 7,59563,41417,59565, 7,59562,41559,59569, 265,59381,46863,59573, +47961,59575,46863,59572,47933,59579, 7,59573, 8647,59583, 7,59572, + 8717,59587, 3785,59587, 8495,59587,25793,59381, 7,59595,26219,59597, + 7,59594,26331,59601, 8495,59381, 7,59604, 8717,59607, 7,59605, + 8647,59611, 8861,59381, 375,59614, 5,56554,26777,59619, 9741,59619, + 9745,59619,26781,59619,30349,59619,26857,59619,26933,59619, 2,59619, + 7,59634,57353,59637,57501,59637,58637,59637, 6,59635, 9777,59619, + 1368,59619,57501,59649,57353,59649,58637,59649,57515,59619,57373,59619, +58663,59619, 9771,59619,40077,59619,31598,59619,55462,59619,50733,59619, +37519,59619,35690,59619, 8982,59619,55211,59619, 483,59678, 668,59619, + 9731,59683,26625,59683, 1419,59619, 9731,59689,26625,59689, 17,59619, + 8861,59694, 9,59619, 587,59698, 9731,59701,26625,59701, 375,59619, +35187,59706, 6,59619, 7,59619, 5,59713, 2,59712,57353,59717, +57501,59717,58637,59717, 5,59712,58396,59725,59717,59727,35187,59725, +56555,59730,59717,59733,56555,59725,35187,59736,59717,59739, 7,59618, + 3867,59743, 8277,59743, 3997,59743, 4067,59743, 9459,59743, 8953,59743, +31103,59619,26625,59756, 483,59619,55211,59760, 587,59619, 9,59764, + 9731,59767,26625,59767,35187,59619, 375,59772,26625,59619,31103,59776, + 8861,59619, 7,59781, 17,59780, 6,56554,36633,59787,36627,59787, + 1,59787, 4,59792,57253,59795,57381,59795,57577,59795,59109,59795, + 5,59792,38304,59787,39987,59787,36544,59787, 9875,59811,20501,59811, + 11,59787,35187,59816,20501,59819, 9875,59819, 5,59787, 1,59824, + 4,59787, 6,59829, 1369,59831,56555,59833, 1,59828,57253,59837, +57381,59837,57577,59837,59109,59837,59835,59837, 6,59828,58930,59849, +59837,59851, 587,59849,56555,59854,59837,59857,56555,59849, 587,59860, +59837,59863,35187,59787, 11,59866,20501,59869, 9875,59869, 7,56554, + 4904,59875,17586,59875,17609,59875,40685,59875,40039,59875,42991,59875, + 9484,59875, 272,59875, 1476,59875, 280,59875, 949,59875, 1517,59875, + 1197,59875, 4903,59875, 9,59902,17579,59875, 9,59906, 11,59875, + 9377,59910, 9,59875, 4903,59914,17579,59914, 253,59875, 265,59920, + 53,59875, 265,59924, 1471,59875, 265,59928, 5,59874, 3867,59933, + 8277,59933, 4067,59933, 3997,59933, 8953,59933, 9459,59933, 265,59875, + 253,59946, 1471,59946, 53,59946, 9377,59875, 11,59954, 5,59955, + 7,56555,59826,59960,59827,59961,59963,59965,59827,59960,59826,59961, +59969,59971,59804,59960,59805,59961,59975,59977,59805,59960,59804,59961, +59981,59983,21036,59960,14072,59960,14073,59960,14072,59961,59991,59993, +59183,59960,59191,59997,59183,59961,59185,60001, 4970,59960, 4971,59960, + 4970,59961,60007,60009,16169,59961, 9617,60013,39786,59961,59807,59961, +59806,59961,28715,59960,28791,60023,29731,60023,29659,60023,29041,60023, +21036,59961,21037,59960,60033,60035,38304,59961,59787,60038, 483,60038, +38304,59960,60019,60045,59787,60045, 483,60045,18967,59961, 5,60053, + 5,60052, 2,59961, 0,60059,56555,60061, 5,60063,59961,60065, + 7,60064,60067,60069,59960,60065, 6,60064,60073,60075, 1,59961, + 3,60079, 5,60081,56555,60082,56555,60081, 5,60086, 5,60087, + 1011,60079,56555,60093, 5,60079, 1,59960,21036,60099, 3,60099, +56555,60103, 5,60105, 5,60104, 5,60103,56555,60110, 5,60099, +18967,60114,18967,60099, 5,60118, 5,60119, 2,59960,59811,60125, +36545,60125,59787,60129,59819,60125,59869,60125, 15,60125,29659,59961, +29077,60139,39787,59961,60045,60143,32103,59961,41746,59961,26106,59961, + 8800,59961,36526,59960,40920,59960, 1163,59961,41019,59961, 11,60160, +35187,60161,59619,59961, 4971,59961,60005,60169,14073,59961,59989,60173, + 4555,59961, 559,59961, 39,59961, 8495,59961,35187,60183, 11,60182, +15029,59960, 375,60189,17125,59960,38305,60192, 11,59961,41019,60196, +25793,60196, 8495,60196, 265,59961, 3447,60205,10831,60205, 305,60205, +17125,60205,11731,60205, 567,59961,46863,60217, 15,59961, 3651,60221, + 1993,60221, 4903,60221,17579,60221, 241,59960,38305,59960,60017,60233, +60021,60233,60041,60233,60043,60233,60039,60233,59787,60243, 483,60243, +17125,60232, 1011,59960,59279,60251,21037,59961,59987,60255, 11,60255, +28715,59961,29533,60261,20273,60261, 9617,60261,35187,59960,25793,60269, +41019,60269, 8495,60269,58715,60269, 265,60268, 265,59960, 331,60281, +35187,60280,38305,59961,37845,60287,37844,60286,60289,60291,37844,60287, +37845,60286,60295,60297,60048,60287,59787,60287,60045,60302,60050,60287, +60045,60287,59787,60308, 483,60308, 483,60287,60045,60314, 1011,59961, +25793,59961,35187,60321, 11,60320, 4,59960,60091,60327,60107,60327, +60033,60327,60101,60327,59337,60327,58191,60327,58211,60327,60057,60327, +60117,60327,60121,60327,21549,60327,21105,60327,19625,60327,58887,60327, +17591,60327, 5,59960, 1,60359, 3,60361,60205,60363,60255,60359, + 4,59961,60085,60369,60089,60369,60113,60369,60109,60369,57685,60369, +59335,60369,58201,60369,58221,60369,57775,60369,57777,60369,60095,60369, +60055,60369,60123,60369,31103,60369,21575,60369,58881,60369,58009,60369, +17579,60369, 4903,60369,48408,60369,46863,60369, 483,60410, 483,60369, +46863,60414, 5,59961,18967,60418,60327,60421, 1368,60419, 2,60419, + 7,60426, 7,60419, 2,60430,59787,59961,38304,60435,60287,60437, +38304,60434,60233,60441, 483,59961,38304,60445,60287,60447,38304,60444, +60233,60451, 5,56555,60081,60454,60369,60457,60103,60454,60369,60461, + 1,60455, 3,60464, 1,60454,59491,60469,59490,60469,59381,60469, + 7,60474, 7,60475, 5,60469,59961,60481, 6,60468,60473,60485, +60477,60485, 7,60468,60471,60491,59381,60491, 6,60469,59381,60497, +60491,60498,60479,60497,60494,60497,60491,60497,59381,60506, 7,60469, +59381,60510,60485,60513,60485,60511,59381,60517, 2,60454,60261,60521, +60483,60521,53813,60455, 483,60527,52915,60455,46863,60531,59875,60455, + 9801,60454,60369,60537,48735,60454,60369,60541,10107,60455, 483,60545, + 9195,60455,46863,60549,54295,60454,60369,60553, 587,60455,17455,60557, +11731,60557, 9,60455, 8861,60563, 17,60455, 587,60567,26625,60569, + 9731,60569,60467,60569, 483,60454,53813,60577,10107,60577,20763,60455, +46863,60583,46863,60454,27021,60587,52915,60587, 9195,60587,20763,60587, + 1275,60454,60369,60597, 587,60454,25519,60601,27021,60455,46863,60605, + 6,60454,38881,60609,59381,60455,30091,60455,31103,60615, 4,56555, + 2,60619, 0,60621,56555,60623, 5,60625, 7,60621,60627,60629, + 1,60618,59689,60633, 1418,60633,59619,60637,59683,60633, 669,60633, +59619,60643,60569,60633,59701,60633,59767,60633, 15,60633, 1369,60652, +59619,60655, 1369,60633, 15,60658,59619,60661, 842,60619, 9377,60665, +59787,60619, 9377,60618, 483,60671, 587,60619, 483,60674, 9377,60677, + 9377,60675, 483,60681,21527,60619, 9,60685,31103,60619, 587,60689, + 587,60618, 9366,60693, 9195,60693, 483,60696, 483,60693, 9195,60700, +31103,60693, 483,60619, 587,60706, 9377,60709, 9377,60707, 587,60707, + 9195,60715, 7,60618,59337,60719,58191,60719,58211,60719,21105,60719, +21549,60719,19625,60719,58887,60719,17591,60719, 6,60619, 1,60737, + 4,60738, 4,60737, 1,60742, 1369,60737,56555,60747,60741,60749, +60745,60749, 7,60619, 2,60755,21183,60755, 9377,60755,59619,60619, +38305,60619, 3,60764, 2,60764,56555,60765, 6,60770,60769,60773, + 6,60765,56555,60776,60769,60779, 1011,60619,46862,60782,46862,60783, +46863,60783,60785,60789,46863,60782,60787,60793, 6,56555,36303,60797, +36302,60796,60799,60801,36303,60796,36302,60797,60805,60807,40946,60796, +40946,60797,60765,60796,60769,60815, 2,60797, 0,60818,40947,60796, +60813,60823,40947,60797,60811,60827,60765,60797,60767,60831,59381,60797, + 17,60797,35187,60837,20501,60839,60125,60839,60821,60839, 9875,60839, +35187,60796,58723,60849, 5,60796,38881,60853, 0, 3, 8169,60857, + 8028,60857, 8173,60857, 8214,60857, 8167,60857, 8154,60857, 8026,60857, + 8054,60857,17728,60857,60210,60857,60283,60857,58128,60857,58023,60857, +57476,60857,56854,60857,56856,60857,58901,60857,58906,60857,58122,60857, +58066,60857,56810,60857,56822,60857,16743,60857,16696,60857, 4810,60857, +16745,60857,16729,60857,16714,60857, 6648,60857, 2079,60857, 375,60916, + 2086,60857,16733,60857,16565,60857,16683,60924,16692,60857,16735,60857, +16739,60857,51942,60857,44534,60857,42383,60857,44243,60938,44440,60857, +16747,60857, 75,60857, 6625,60946, 6632,60857, 6645,60857,60079,60857, + 5,60955,60327,60957, 5,60954,60369,60961,60096,60857,60369,60965, + 1325,60856,40829,60969, 1173,60857,17591,60856,17603,60975,10039,60857, + 1275,60979,10041,60981,38560,60857, 1205,60985, 1203,60857,38305,60988, + 1205,60991,38514,60857, 1007,60995,40741,60856,40829,60999, 531,60856, + 1290,60856, 0,61005, 5,61006, 5,61005, 0,61010,40769,60856, +40829,61015,38548,60857, 1325,61019, 177,60857,38305,61022, 59,61025, +17053,60857, 483,61028,10081,61031, 1009,60857,38305,61034, 1007,61037, + 1365,60857,38305,61040, 1325,61043,38566,60857, 59,61047,17054,60857, +10081,61051,42109,60857,38305,61054,42103,61057, 9527,60857, 483,61060, + 9525,61063,42110,60857,42103,61067, 9528,60857, 9525,61071, 386,60857, +56555,61074, 8150,60857, 113,61078, 347,60857,56555,61082,58905,60857, + 375,61086,45432,60857,56555,61090,43099,60857,56555,61094,40829,60857, + 1325,61099,40741,61099,40769,61099,60535,60857, 9,61107,57916,60857, + 9,61111,57851,60857, 9,61115,17727,60857, 113,61118,57060,60857, + 8309,60857,60230,60857,57986,60857,57998,60857,60268,60857,58352,60857, +58704,60857,13820,60857,13772,60857,57994,60857,58056,60857,42155,61144, +36101,60857, 375,61149,37407,60857,47392,60857,36532,60857,36536,60857, +57284,60857,58760,60857,58762,60857,58846,60857,58372,60857,57278,60857, +57996,60857,57470,60857, 305,61174,60205,60857, 305,61178,58374,60857, +58830,60857,57586,60857,57480,60857,57230,60857,57568,60857,40684,60857, +36542,60857, 375,61197,42990,60857,40038,60857,37498,60857,36754,60857, +35796,60857, 8225,60857, 8242,60857, 128,60857, 7971,61214, 101,60857, + 7971,61218,37362,60857,48122,60857,12526,60857, 6698,60857,47690,60857, +38704,60857,11943,60857, 9,61235,12343,60857, 5,61239, 4,61238, +61241,61243, 4,61239, 5,61238,61247,61249,40132,60857, 331,61253, + 59,61253, 97,61253, 1325,61253, 8148,60857,48753,61263,10081,61263, +11759,61263,39044,60857, 331,61271, 1325,61271, 571,61271, 59,61271, +42338,60857,48753,61281,44451,61281,10081,61281,42959,60857,48753,61289, +44451,61289,42825,61289,10081,61289, 629,60857,56555,61298,38305,61298, + 289,60857, 587,61305,10287,60857,17660,60857,10262,60857,40130,60857, + 331,61315, 59,61315, 1325,61315,53780,60857,48753,61323,10081,61323, +60668,60857, 9,61329, 8213,60857, 9,61332,11813,60857, 9,61337, +17030,60857, 8211,60857, 8301,60857, 8209,60857,38110,60857, 8052,60857, + 9,61350,10464,60857, 9,61355,10527,60857, 9,61359, 4727,60857, + 9,61363,11989,60857, 11,61367, 5,61368, 6,61367, 5,61372, + 5,61367, 6,61376, 6,61377, 7,61367,61381,61383, 15,61367, + 6,61366,61387,61389, 14,61367, 7,61366,61379,61395,61375,61395, +61371,61395,61393,61395,12371,60857, 5,61405, 4,61405, 12,61405, + 13,61404,61411,61413, 13,61405, 12,61404,61417,61419, 5,61404, +61409,61423, 4,61404,61407,61427, 6625,60857,44243,61430, 375,61430, + 75,61430, 9,61431, 6685,60857, 483,61440,46863,61440,17060,60857, +35768,60857,50524,60857,52806,60857,50480,60857,36296,60857,50990,60857, + 510,60857,56555,61460,47608,60857,36293,60857, 483,61466, 295,60857, +49713,61470, 1291,60857,61009,61475,61013,61475,20033,61475, 1831,61475, + 1881,61475,19319,61475, 4,61475,19317,61489,15899,61475, 4579,61475, +48561,60857, 9,61497, 922,60857,56555,61500,51937,60857, 375,61504, +57915,60857,57859,61508, 9,61511,39234,60857,60853,61515,60609,61515, + 9525,61515,42103,61515, 9517,61515,41989,61515,39485,61515,39481,61515, +57855,61515, 9519,61515,41995,61515,39487,61515, 15,61515, 9513,61541, +41941,61541,39477,61541,50958,60857, 9,61548,35761,60857, 9,61552, +35719,60857, 375,61557,57482,60857,57852,61561,57811,61561, 9,61564, + 9,61561,57811,61568,57522,60857, 483,61572,46863,61572, 1072,60857, + 1108,60857,49713,61580, 524,60857, 4,61585,19301,61587,19311,61585, + 3195,61585, 1965,61585,15899,61585, 4579,61585,40704,60857, 7,61601, +40799,61603,40813,61601, 7961,61601, 7951,61601, 7953,61601,35893,61601, +37897,61601,38299,61601, 1275,61601, 7947,61621,52756,60857, 1011,61624, +10245,60857,37339,60857, 33,60857,10385,61632, 9,61635, 7971,60857, + 101,61638, 128,61638, 9,61638, 113,61644, 483,61638,48753,61649, +10081,61649,11759,61649, 113,61638, 9,61656, 21,60857,38305,61660, + 331,61663, 59,61663, 97,61663, 1325,61663,47640,60857,56555,61672, +17635,60857,12377,61676,11731,60857,13737,61680, 567,60857,46863,61685, +55211,60857,44243,60857, 6625,61690,42383,61690,58116,61690,42155,61690, +56555,61698,56555,61690,42155,61702, 1505,60857,38057,61706, 288,60857, +46863,61711,48466,60857,56555,61714, 11,60857, 340,61719, 341,61719, + 264,61718,61721,61725, 7,61719, 265,61728,61725,61731, 265,61729, + 264,61719,61735,61737,56555,61719,57859,61741, 9,61743, 483,61718, + 4579,61747,15899,61747, 3195,61747, 1965,61747, 265,61719, 7,61756, +61725,61759,56555,61718,57852,61763,57811,61763, 9,61766, 9,61763, +57811,61770, 265,61718,61723,61775,46863,61775, 9,60857, 8213,61780, + 8052,61780, 7971,61780, 113,61786,36527,61780,58782,61780,58362,61780, + 8241,61780, 113,61780, 7971,61798,35761,61780,50958,61780, 483,61780, +56555,61806, 1011,61780,49713,61810,38305,61780,38299,61815,37897,61815, + 7961,61815, 7951,61815,35893,61815, 7953,61815, 1275,61815, 7947,61829, +49713,61780, 1011,61832,56555,61780,46863,61836, 483,61836,46863,61780, +56555,61842, 7563,60857, 1275,61847, 7565,61849, 7569,61849, 7573,61849, + 7065,60857,35187,61857,10089,60857, 16,61861, 16,61860, 17,61861, +61865,61867, 17,61860,61863,61871,10257,60857, 903,61874, 8241,60857, + 9,61878, 3255,60857, 9,61883,48789,60857, 16,61887, 16,61886, + 17,61887,61891,61893, 17,61886,61889,61897,37357,60857, 903,61900, + 3499,60857, 9,61905, 3445,60857, 9,61909,38184,60857, 375,61913, +16722,60857,16543,61917,16679,61917,16537,61917,35187,61916,13922,60857, +13753,61927, 4805,60857, 375,61930,16683,60857,16565,61934, 375,61934, + 6515,60857, 9,61941,59824,60857,60369,61945, 9,61945, 9520,60857, + 9,61951,16711,60857,16659,61954, 8,60856, 3,61959, 6,61960, +61601,61963,61815,61963, 6,61959, 3,61968,61601,61971,61815,61971, +61829,61959,61621,61959, 1274,61959,61815,61981,61601,61981, 16,60856, + 10,60856, 5,61989, 0,61990,61585,61993,61747,61993, 0,61989, + 5,61998,61585,62001,61747,62001,57811,61989,59933,61989,59743,61989, + 8093,61989, 3837,61989,39619,61989,51421,61989,53703,61989,42435,61989, + 4287,61989, 4303,61989,53487,61989,42113,61989, 9839,61989, 9579,61989, + 9,61989, 113,62036, 128,61989, 101,61989, 113,61989, 9,62044, +57859,60857,57915,62048, 9,62051, 9,62049,57915,62055, 11,60856, + 483,62059, 9,60856,57889,62063, 5185,62063,11243,62063,12557,62063, +11251,62063, 5177,62063, 16,60857,57293,62077,35603,62077,58669,62077, +59079,62077,55005,62077, 17,60856,61685,62089, 483,62089, 15,60856, +40829,62095, 10,60857,21183,62099, 759,62099, 47,62099, 959,62099, + 203,62099, 107,62099, 85,62099, 67,62099, 9,62115, 113,60857, + 8150,62118,17727,62118,13737,62118, 9,62118, 7971,62126,17025,62118, + 7971,62118, 9,62132,10385,60857, 9,62137, 33,62139, 33,62136, + 9,62143, 165,60857,17049,62146,17025,60857, 1275,62151, 7947,62153, +48739,62153,17637,62153,17029,62153,37241,62153, 113,62150,17049,60857, + 165,62166,10247,60857,37347,60857,10249,60857,16659,60857,16711,62176, +37408,60857,56555,62180,16531,62180, 340,60857,51803,62187, 331,62187, + 1325,62187, 59,62187,52439,60857,56555,62196,52563,60857, 265,62201, + 1413,60857,56555,62204, 256,60857,56555,62208,38305,62208, 566,60857, +61987,62215, 9377,62215,37325,62215,10219,62215, 959,62215, 47,62215, + 759,62215, 587,62215,20763,62215,42810,60857,43073,62235,42827,62235, +58703,62235,60849,62235,58027,62235,42821,62235, 1325,62235, 59,62235, + 331,62235, 265,62235,42613,62235,56555,62257, 241,62234, 580,60857, +20033,62263,59143,62263,60601,62263,58029,62263,55210,60857,31103,62273, +37375,60857, 1347,60857,37409,60857, 375,62280, 375,60857,58905,62284, +16683,62284, 4805,62284,58126,62284, 6625,62284, 2079,62284,51937,62284, + 305,62284,56555,62300,56555,62284, 305,62304,37409,62284, 803,60857, +37608,60857, 305,60857,60205,62314,57470,62314, 375,62314,56555,62320, +56555,62314, 375,62324,58116,60857,44243,62328,58126,60857, 375,62332, +42155,60857,58056,62336,44243,62336,56555,62340, 483,62336,48753,62345, +44451,62345,10081,62345,56555,62336,44243,62352,53517,60857, 483,62356, +48753,62359,10081,62359, 251,60857,38305,62364, 331,62367, 1325,62367, + 59,62367, 555,60857,38305,62374, 331,62377, 1325,62377, 571,62377, + 59,62377,60612,60857, 7,62387,36273,60857, 7,62391,59787,60857, +60619,62394, 9,62397, 5,62394,60369,62401, 9,62401, 5,62395, +60327,62407,60719,62407,57901,62407, 17,62407,59381,60857,60455,62416, + 7,62419, 9513,60857, 5,62423, 17,62425, 5,62422, 9,62429, +58362,60857, 903,62432, 9,62432,58348,60857, 7,62438,58782,60857, + 903,62442, 9,62442,58396,60857, 7,62448,58742,60857, 9,62453, + 483,62452,46863,62452,60763,60857, 7,62461, 903,60857,58782,62464, +58362,62464,37357,62464,10257,62464, 483,62464,56555,62474,46863,62464, +56555,62478,56555,62464, 9,62483,46863,62482, 483,62482,38057,62464, + 7,62491, 265,60857,46863,62495, 11,62497, 11,62494,46863,62501, + 7,62494,51803,62505, 331,62505, 1325,62505, 59,62505, 7,62495, + 375,62515,35187,60857,59960,62518,16722,62518, 7,62518,56555,62524, +16531,62524,56555,62518, 7,62530,16531,62518, 7,62534,46863,60857, +36527,62538,58742,62538,57522,62538,12377,62538, 6685,62538, 1011,62538, + 903,62538,56555,62552, 9,62538,56555,62556,56555,62538, 903,62560, + 9,62560,36527,60857, 7,62567, 331,62569, 9,62566,46863,62566, + 483,62566, 7,62566, 375,62579,38305,60857,42109,62582,42103,62585, + 1205,62583, 1203,62589, 1007,62583, 1009,62593, 1203,62582, 1205,62597, + 1009,62582, 1007,62601, 177,62582, 59,62605, 1365,62582, 1325,62609, + 571,62583, 555,62613, 97,62583, 21,62617, 5,62582, 6,62621, + 5,62622,62583,62625, 2,62622,62627,62629, 2,62621, 6,62632, + 482,62621, 3,62582, 6,62639, 3,62640,62583,62643, 4,62640, +62645,62647, 4,62639, 6,62650, 8,62639, 14,62582, 6,62657, + 2,62658, 2,62657, 6,62662, 482,62657, 1274,62582, 6,62669, + 4,62670, 4,62669, 6,62674, 8,62669, 256,62582, 629,62582, + 555,62582, 59,62685, 331,62685, 1325,62685, 571,62685, 21,62582, + 1325,62695, 331,62695, 59,62695, 97,62695, 1325,62583, 1365,62705, + 251,62705, 21,62705, 555,62705, 59,62583, 177,62715, 251,62715, + 555,62715, 21,62715, 251,62582, 331,62725, 59,62725, 1325,62725, + 331,62583, 251,62733, 555,62733, 21,62733, 15,62583,10085,62741, +10093,62741,62661,62741,62665,62741,40909,62741,62629,62741,62635,62741, + 3,62741,10087,62741,62637,62741,62667,62741, 483,62741,10081,62765, +62621,62765,62657,62765, 1275,62583,10005,62773,10041,62773,62673,62773, +62677,62773,62647,62773,62653,62773, 5,62773,10009,62773,23879,62773, +31937,62773,30399,62773,62655,62773,62679,62773, 9,62773,10001,62801, +62639,62801,62669,62801, 483,62582,39485,62809,39481,62809, 9525,62809, + 9517,62809, 4,62809,62773,62819,62787,62809,42103,62809,41989,62809, + 9519,62809,41995,62809,57855,62809,60853,62809,60609,62809,39487,62809, + 15,62809, 9513,62841,41941,62841,39477,62841, 9,62582,61971,62849, +61963,62849, 7961,62849, 7951,62849, 2,62849,62741,62859,62757,62849, + 7953,62849,35893,62849,38299,62849,37897,62849,61981,62849, 1275,62849, + 7947,62875,61959,62875, 6,62582, 7,62582,58703,62883,58027,62883, +60849,62883, 59,62883, 1325,62883, 331,62883, 265,62883,42613,62883, +56555,62899, 241,62882, 7,62583,58715,62905,58056,62905,40799,62905, + 375,62905,56555,62905,44243,62914,44243,62905,56555,62918, 265,62583, + 375,62923,62881,62923, 241,62582, 6,62928, 7,62928, 6,62929, +62933,62935, 7,62929,62931,62939, 1011,60857,52756,62942,46863,62942, + 483,62942, 9,62942,49713,62950,49713,62942, 9,62954, 7,62943, +10107,62959,53813,62959,49713,60857,22807,62965, 1108,62964, 295,62964, + 9,62964, 1011,62972, 1011,62964, 9,62976, 4,62964,31103,62981, + 241,60857,59960,62984,42810,62984, 7,62984,56555,62990,38305,62990, +56555,62984, 7,62996,38305,62984, 6,63000, 7,63000, 6,63001, +63005,63007, 7,63001,63003,63011, 483,60857, 9527,63014, 9525,63017, +17053,63014,10081,63021,11759,63015, 7971,63025,44451,63015,42155,63029, + 0,63014, 5,63033, 0,63034,63015,63037, 7,63034,63039,63041, + 7,63033, 5,63044, 10,63033, 6,63014, 5,63051, 6,63052, +63015,63055, 1,63052,63057,63059, 1,63051, 5,63062,38304,63051, + 14,63014, 5,63069, 1,63070, 1,63069, 5,63074,38304,63069, +36527,63014,58742,63014,57522,63014,12377,63014, 6685,63014, 7971,63014, +48753,63091,10081,63091,11759,63091,42155,63014,10081,63099,48753,63099, +44451,63099,10081,63015,17053,63107,53517,63107,42155,63107, 7971,63107, +53517,63014,48753,63117,10081,63117,48753,63015,53517,63123, 7971,63123, +42155,63123,36293,63014, 17,63015, 11,63015, 9,63014,56555,63136, + 15,63015, 1995,63141, 3755,63141,63073,63141,63077,63141, 5303,63141, +63059,63141,63065,63141, 0,63141,38403,63141,39943,63141, 4917,63141, +17013,63141,63067,63141,63079,63141,38305,63141, 59,63171,63051,63171, +63069,63171, 903,63014,56555,63178, 1011,63014, 11,63014,61993,63185, +62001,63185, 3195,63185, 1965,63185, 1,63185,63141,63195,63157,63185, + 4579,63185,15899,63185,38305,63014,39481,63205,39485,63205,42103,63205, +41989,63205, 7,63205, 9525,63205, 9517,63205,41995,63205, 9519,63205, +57855,63205,60609,63205,60853,63205,39487,63205, 15,63205,41941,63233, + 9513,63233,39477,63233, 4,63014,20033,63241,59143,63241,60601,63241, +58029,63241,63133,63241, 5,63014,61987,63253, 9377,63253,37325,63253, +10219,63253, 959,63253, 47,63253, 759,63253, 587,63253,20763,63253, + 4,63015, 7,63272,63253,63275,20029,63273,59147,63273,21103,63273, +19781,63273,19301,63273,62773,63273, 5,63015, 803,63291,56555,63014, + 903,63294, 9,63294,40674,60857, 6,63300, 7,63300, 6,63301, +63305,63307, 7,63301,63303,63311,38266,60857, 7,63315,36752,60857, + 7,63318,16937,60857, 580,63322, 483,63322, 4,63327, 4,63326, +49631,63322,49630,63322, 581,63322, 4,63322, 483,63338,46863,63338, + 5,63322,63329,63345, 4,63323,63333,63349,63337,63349, 5,63323, +63335,63355,63325,63355,63331,63355,63341,63355,63343,63355,46863,63322, + 4,63366,63355,63369, 4,63367,63345,63373,38057,60857, 5,63376, + 6,63379, 6,63378, 14,63376, 15,63376, 1505,63376, 6,63376, + 5,63390, 7,63376,63381,63395, 375,63395, 6,63377,63387,63401, + 7,63377,63393,63405,63383,63405,38161,63405, 59,63405,63385,63405, + 331,63405, 1325,63405, 265,63405, 903,63376, 7,63423,16531,60857, + 1275,63427,16711,63429,37408,63426, 7,63426,16543,63435,16679,63435, +16537,63435,35187,63434, 7,63427,16565,63445,35187,63426, 7,63448, +12377,60857, 9,63453,46863,63452, 483,63452,17635,63452,13737,60857, + 113,63462,11731,63462, 5,63462,13753,63469, 5,63463,13751,63473, +13917,63473,13743,63473,60619,60857,59787,63480, 9,63483,60455,60857, +59381,63486, 7,63489, 5,60856,45491,63493,42800,63493,59694,63493, +10374,63493,60563,63493, 9606,63493,42134,63493, 629,63493, 705,63493, + 101,63493, 299,63493, 828,63493, 128,63493, 394,63493,48122,63493, + 803,63493, 483,63524,42613,63493, 17,63528,51992,63493, 375,63493, + 241,63534,49713,63534,10291,63493, 17,63540, 256,63493, 113,63493, + 9,63546, 17,63493,42613,63550,10291,63550,59619,63550, 9565,63550, +41941,63550, 9,63493, 113,63562, 7,63493,21605,63567, 241,63566, + 7,63492,59933,63573,59743,63573,57811,63573, 8093,63573, 3837,63573, +53703,63573, 4287,63573,42435,63573, 4303,63573,53487,63573, 9839,63573, +42113,63573, 9579,63573,39619,63573,51421,63573,31103,63493,46863,63493, +12377,63606, 483,63493, 803,63610,49713,63493, 375,63614,56555,63493, + 5,63619, 6,63621, 6,63620, 14,63619, 15,63619, 6,63618, +63629,63631, 7,63618,63625,63635,63627,63635, 7,63619,63623,63641, + 6,63619, 5,63644,63635,63647, 241,63493, 375,63650, 7,63650, +59619,63493, 7,63657, 17,63656,12377,63493,46863,63662, 9565,63493, + 7,63667, 17,63666,41941,63493, 7,63673, 17,63672, 4,60856, +20305,63679, 9641,63679, 9645,63679,20309,63679,21081,63679,20323,63679, +20387,63679, 3,63679, 6,63694,61601,63697,61815,63697,62849,63697, + 6,63695, 9701,63679, 1274,63679,61815,63709,61601,63709,62849,63709, +61829,63679,61621,63679,62875,63679, 9719,63679,43005,63679,58056,63679, + 386,63679, 347,63679, 767,63679,31103,63733, 524,63679, 9617,63737, +20273,63737, 1291,63679, 9617,63743,20273,63743, 305,63679, 375,63748, + 375,63679, 305,63752,44243,63679,56555,63756, 11,63679, 483,63760, + 9617,63763,20273,63763, 7,63679,38305,63769,56555,63771, 6,63679, + 4,63775, 3,63774,61601,63779,61815,63779,62849,63779, 4,63774, +62582,63787,63779,63789,38305,63787,60857,63792,63779,63795,60857,63787, +38305,63798,63779,63801, 7,63678,63253,63805,62215,63805, 483,63679, + 11,63810, 9617,63813,20273,63813,56555,63679,44243,63818, 7,60856, +40787,63823,40793,63823, 0,63823, 5,63828,61475,63831,61585,63831, +61747,63831,63185,63831,36001,63823,26106,63823,32103,63823,41746,63823, +43005,63823, 8800,63823,59910,63823,36539,63823,37595,63823,36049,63823, +63253,63823,62215,63823,61305,63823,48975,63823,48245,63823,48451,63823, +60707,63823, 386,63823, 347,63823, 510,63823,60755,63823, 9852,63823, +53500,63823,36041,63823,37573,63823,36275,63823, 375,63823, 305,63892, + 273,63823, 281,63823, 1477,63823, 1516,63823, 948,63823,40704,63823, +10001,63907,27533,63907, 8495,63823, 11,63912,48885,63823, 483,63917, +41019,63823, 11,63920, 567,63823,63493,63925, 165,63823, 903,63928, + 11,63823,25793,63932,41019,63932, 8495,63932,59875,63932, 9805,63932, +53391,63932,60618,63823, 483,63947, 9,63823, 483,63950,38305,63950, +27533,63955,10001,63955, 1505,63823, 1011,63960, 305,63823, 375,63964, + 4,63823,63610,63969,63291,63969,63493,63969, 483,63974, 483,63969, +63493,63978,56555,63968, 483,63983, 5,63823, 7,63987, 1275,63989, +60857,63991, 0,63986,61475,63995,61585,63995,61747,63995,63185,63995, +63993,63995, 7,63986,63014,64007,63995,64009, 483,64007,60857,64012, +63995,64015,60857,64007, 483,64018,63995,64021, 4,63822,63253,64025, +62215,64025, 5,63822,59743,64031,59933,64031,57811,64031, 8093,64031, + 3837,64031,42435,64031, 4303,64031,53703,64031, 4287,64031,42113,64031, + 9579,64031,53487,64031, 9839,64031,51421,64031,39619,64031,25793,63823, + 11,64062, 903,63823, 165,64066,38305,63823, 9,64070,27533,64073, +10001,64073, 1011,63823, 1505,64078,56555,63823, 4,64082, 483,64085, + 483,63823, 9,64088,59875,63823, 11,64092, 5,64093, 9805,63823, + 11,64098, 5,64099,53391,63823, 11,64104, 5,64105, 6,60856, +60454,64111,60369,64113, 5,64111,56555,64116,60369,64119,56555,64111, + 17,64122, 4,64122, 7,64126, 5,64122,60369,64131, 5,64123, +60327,64135,60719,64135,64129,64135,57901,64135, 4,64123,64125,64145, + 7,60857,16539,64149,16683,64151, 264,64149, 264,64148, 3,64149, +16530,64158,16530,64159, 1,64158,58396,64148,58348,64148,36527,64148, + 375,64171,40674,64148,36752,64148, 331,64149, 1325,64149, 59,64149, +38057,64148, 375,64185,16531,64148,64161,64189,16543,64189,16679,64189, +16537,64189,35187,64188, 17,64149, 241,64201,38305,64149,42383,64205, + 6625,64205,58116,64205,42155,64205,56555,64212,56555,64205,42155,64216, +16531,64149,64163,64221,16659,64221,48885,64149,56555,64227, 15,64149, + 483,64231,38305,64231,27533,64235,64165,64235,10001,64235,35187,64148, +56555,64242,16531,64242, 265,64148,64155,64249,51803,64249, 331,64249, + 1325,64249, 59,64249, 241,64148,56555,64260,38305,64260,38305,64148, +58703,64267,58027,64267,60849,64267, 59,64267, 1325,64267, 331,64267, + 265,64267,42613,64267,56555,64283, 241,64266, 265,64149,64157,64289, +41941,64289,51229,64289, 4213,64289, 8757,64289, 9565,64289,59619,64289, + 305,64289, 713,64289, 25,64289, 8165,64289, 9617,64289,20273,64289, + 4,64148,57293,64317,35603,64317,58669,64317,59079,64317,55005,64317, + 5,64148, 3,64329, 1,64331,64205,64333,21183,64329, 759,64329, + 47,64329, 959,64329, 4,64149,43053,64345,54985,64345,54129,64345, +51601,64345,58782,64345,46863,64345,56555,64356,56555,64345,46863,64360, + 5,64149, 803,64365, 1275,64149, 9,64369,56555,64148,25793,64373, + 8495,64373,41019,64373,35187,64372, 241,64372,59960,60857,25793,64385, +41019,64385, 8495,64385, 241,64384,35187,64384, 5,60857,60079,64396, +60369,64399, 0,64396,63743,64403, 1290,64403,63679,64407,63737,64403, + 525,64403,63679,64413,63763,64403,63813,64403, 17,64403, 1275,64420, +63679,64423, 1275,64403, 17,64426,63679,64429, 3,64396, 6,64433, + 759,64397, 483,64437,64025,64397, 483,64441,63823,64397, 483,64445, + 9377,64397, 483,64449, 9513,64396, 9,64453, 959,64397, 483,64457, +37325,64397, 483,64461, 47,64397, 483,64465,10219,64397, 483,64469, +59787,64396,60369,64473, 9,64473,13737,64396,13753,64479,49713,64397, +61987,64397, 483,64485, 9,64397,42613,64489,59619,64489,10291,64489, + 9565,64489,41941,64489, 17,64397, 113,64501,63805,64397, 483,64505, +20763,64397, 483,64509, 483,64396,64025,64513,63823,64513,61987,64513, +63805,64513, 9377,64513,37325,64513,10219,64513, 959,64513, 47,64513, + 759,64513, 587,64513,20763,64513, 587,64397, 483,64539, 7,64396, +21183,64543, 759,64543, 47,64543, 959,64543, 6,64397,56555,64553, + 4,64554, 5,64554, 5,64555,64557,64561, 4,64555,64559,64565, + 7,64397, 0,64569, 5,64570, 5,64569, 0,64574,21105,64569, +21549,64569,19625,64569, 1275,64569,60857,64585,64573,64587,64577,64587, +63679,64397,63705,64593,64435,64593,31103,64593, 375,64593, 7,64593, +64433,64603, 903,64397, 375,64607, 7,64607,35187,64397,60796,64612, +60797,64612,31103,64613,60797,64613,64615,64621, 375,64613,60796,64613, +64617,64627,56555,64397,59787,64631, 9,64633,56555,64396,59914,64637, +60221,64637,59875,64637, 9,64642,60369,64637, 9,64637,59875,64648, +60454,60857,59914,64653,60221,64653,59875,64653, 9,64658,60369,64653, + 9,64653,59875,64664, 4,60857, 8743,64669, 8605,64669, 8672,64669, + 8647,64669, 305,64676, 3795,64669,10025,64669,27603,64669,31524,64669, +31690,64669,31700,64669,63569,64669,46862,64669, 0,64669, 2,64696, + 6,64696, 6,64697,63604,64669,24655,64669,63534,64669, 8916,64669, +62092,64669,63133,64669,48408,64669, 713,64669,31103,64718, 305,64669, + 8647,64722,31103,64722, 25,64669,31103,64728, 340,64669, 8717,64733, + 3785,64733, 8495,64733, 483,64669,62089,64740,46863,64740, 375,64669, +63493,64746, 8861,64746,62089,64669, 483,64752, 15,64669, 483,64757, +20273,64759, 9617,64759,64403,64759,64699,64759,31103,64669, 305,64768, + 713,64768, 25,64768,63493,64768,49713,64769, 483,64668,20033,64781, +59143,64781,58029,64781,60601,64781,49713,64668,31103,64791,46863,64669, + 483,64794, 6,64668,64795,64799, 2,64801, 3,64800,64803,64805, + 3,64801, 2,64800,64809,64811, 7,64668,57293,64815,64695,64815, + 2,64819, 3,64818,64821,64823, 3,64819, 2,64818,64827,64829, +64701,64815, 2,64833, 3,64832,64835,64837, 3,64833, 2,64832, +64841,64843,35603,64815,59079,64815,58669,64815,55005,64815, 6,64669, + 0,64854,64815,64857, 2,64859, 3,64858,64861,64863, 3,64859, + 2,64858,64867,64869, 7,64669,60857,64873, 264,64874, 264,64875, + 1,64873, 264,64880, 264,64881,64703,64873, 2,64887, 3,64886, +64889,64891, 3,64887, 2,64886,64895,64897,25793,64873,41019,64873, + 8495,64873, 305,64905,59875,64873,53391,64873, 9805,64873,38305,64873, + 483,64915, 265,64872, 8717,64919,64883,64919,64877,64919, 3785,64919, + 8495,64919, 265,64873,64885,64931,64879,64931,63493,64931, 8861,64931, +46863,64931, 8861,64669, 375,64942,63493,64669,31103,64946, 375,64946, + 265,64669, 7,64952, 8717,64955, 3785,64955, 8495,64955, 7,64953, + 8647,64963,56555,64669,59381,64967, 7,64969,56555,64668,31103,64973, + 375,64973,59712,64973, 7,64973, 265,64981,59619,64980,59619,64973, + 7,64986,60618,60857,31103,64991, 375,64991,59712,64991, 7,64991, + 265,64999,59619,64998,59619,64991, 7,65004, 6,60857, 264,65009, +64746,65011, 375,65011,64669,65014,64931,65011,64669,65011, 375,65020, + 264,65008,64733,65025, 341,65025,64669,65029,64955,65025,64919,65025, + 3,65008, 341,65037,64669,65039,63907,65037,40705,65037,63823,65045, +64235,65037,63955,65037,64073,65037, 17,65037,64953,65009, 375,65057, + 4,65009, 1274,65061, 3,65061, 6,65064, 6,65061, 3,65068, +56555,65008,60796,60857,56555,60857, 386,65076,45432,65076, 347,65076, +43099,65076,60418,65077,60327,65087,59961,65077, 5,65091,60369,65093, + 5,65090,60327,65097, 18,65076, 12,65076, 1291,65076,48561,65076, + 525,65076,49630,65076, 580,65076, 1170,65076,37408,65076, 256,65076, +52439,65076, 629,65076, 1413,65076,47640,65076, 922,65076,49631,65076, + 581,65076, 1171,65076,44243,65076,42155,65136, 375,65076, 305,65140, +48466,65076, 510,65076, 13,65077,65103,65149, 19,65077,65101,65153, + 13,65076, 18,65077, 19,65076,65159,65161, 12,65077,65157,65165, +57915,65165, 14,65077,57901,65171,60719,65171,60327,65171,65115,65171, + 17,65171, 10,65077, 9,65183, 16,65076,65171,65187, 10,65076, +65165,65191,48560,65076, 524,65076, 1290,65076, 11,65076,65149,65201, +57852,65201,65184,65201,65183,65201, 9,65208,57811,65201, 9,65212, + 9,65201,65183,65216,57811,65216,46863,65200, 483,65200, 9,65076, + 483,65226,46863,65226, 17,65076, 1275,65232, 11,65077,65191,65237, + 9,65239,57915,65237, 9,65243, 9,65236,65191,65247,57915,65247, + 9,65077, 11,65253,65183,65255,57811,65255, 11,65252,65191,65261, +57915,65261, 15,65077,65233,65267,65135,65267, 305,65076, 375,65272, +42155,65076,44243,65276, 1011,65076, 7,65281, 7,65280,65171,65285, + 15,65281, 241,65076, 7,65290, 483,65076, 4,65295, 4,65294, + 903,65294, 9,65294, 11,65294, 903,65076, 9,65307,46863,65306, + 483,65306,35187,65076, 7,65314, 1275,65076, 17,65318,46863,65076, + 4,65323, 4,65322, 903,65322, 9,65322, 11,65322, 7,65077, + 9,65335, 11,65335, 567,65335, 4,65335, 483,65343, 5,65335, + 5,65334,65343,65349, 4,65334,65347,65353, 6,65076,65350,65357, +65355,65357,65289,65357,65349,65357,65343,65364,65343,65357,65349,65368, + 11,65357,65346,65357,65353,65375, 5,65357,65335,65379,65349,65381, +65335,65378,65353,65385,65335,65357, 4,65389, 5,65389,65343,65393, + 4,65388,65393,65397, 5,65388,65391,65401,65353,65401, 5,65077, +60629,65407,59960,65406,59960,65407,38185,65407,57993,65407,65115,65407, +65113,65407,65111,65407,59961,65406,65413,65425,60327,65425,65197,65407, +65195,65407,65105,65407,60251,65407,58313,65407,65299,65407,65327,65407, +65285,65407,65225,65407,65223,65407,65305,65407,65333,65407,56555,65407, + 7,65454, 7,65455,59961,65407,60359,65461,65411,65461, 11,65461, + 1171,65407,65335,65469,65335,65407,65187,65407,57901,65407,60327,65407, +60719,65407, 17,65407,65335,65483,59875,65483,64385,65407,64373,65407, + 6,65407,65459,65493,60755,65493,65283,65493, 11,65493, 7,65407, +56555,65502, 7,65406,65461,65507,65493,65507, 6,65406,65505,65513, +65457,65513,60719,65513,65413,65513,60327,65513,57901,65513,65187,65513, +65115,65513,65285,65513,65503,65513, 17,65513, 4,65076, 2,65537, + 7,65539,65407,65541,65510,65537,65533,65537,65507,65537,65493,65548, +31103,65537,65493,65537,65507,65554, 483,65536,65407,65559,46863,65536, +65407,65563, 375,65537, 15,65537,65357,65569, 7,65536,65513,65573, +65171,65573,65407,65573,59712,65537,65502,65537,65513,65583, 7,65537, +65493,65587,65407,65587,65507,65591, 265,65587,59619,65586,65407,65586, +65513,65599,59619,65537, 7,65602,65407,65537, 6,65607, 7,65607, +65493,65611, 6,65606,65611,65615, 7,65606,65609,65619,65513,65619, + 4,65077,65135,65625,65133,65625,65131,65625,65372,65625,65199,65625, +65109,65625,65107,65625,65235,65625,65321,65625, 11,65625,65357,65644, +65233,65625,65357,65625, 11,65650,65075,65625,65073,65625, 7,65625, +60455,65625, 7,65661, 5,65076, 2,65664,65625,65667, 1,65664, + 3,65664, 0,65664,65625,65675,59914,65665,60221,65665,65336,65665, +65297,65665,65325,65665,59875,65665, 9,65688,65659,65665,65667,65693, +65675,65693,60369,65665, 9,65665,65335,65700,59875,65700,65335,65665, + 9,65706,65658,65665,65671,65711,65673,65711, 6,65665,65668,65717, +65676,65717,65667,65717,65625,65722,65675,65717,65625,65726,65625,65717, +65667,65730,65675,65730, 7,65665,65671,65737,65625,65739,65673,65737, +65625,65743,65625,65736,65671,65747,65673,65747,65625,65665, 6,65753, + 7,65753, 6,65752,65757,65759, 7,65752,65671,65763,65673,65763, +65755,65763, 6,65077,60619,65771, 9,65773, 17,65771,65625,65777, +65537,65771, 9,65781, 5,65771,60369,65785, 9,65785, 5,65770, +65573,65791,60327,65791,60719,65791,57901,65791,65187,65791,65115,65791, +65285,65791, 17,65791, 7,65076,25793,65809,41019,65809, 8495,65809, + 1011,65808,65513,65817,65791,65817,65407,65817,65171,65817, 241,65808, +35187,65808, 15,65809,65665,65831,65407,65809, 11,65835, 4,65808, +65791,65839,65513,65839,65171,65839,65407,65839, 4,65809,65785,65849, +65665,65849,65771,65809, 4,65855, 5,65855, 4,65854,65859,65861, + 5,65854,65857,65865, 1, 4,49284,65869,49311,65869,48330,65869, +48279,65869,47548,65869,47076,65869,47078,65869,48581,65869,48584,65869, +54254,65869,54260,65869,54266,65869,54222,65869, 8672,65869, 8743,65869, + 3795,65869, 8605,65869, 8647,65869, 305,65904,27021,65868,31103,65909, +27377,65910,31894,65909,27377,65909,31103,65916, 21,65868,57577,65921, +59109,65921,19987,65921,57381,65921,59787,65868,57577,65931,57381,65931, +57253,65931,59109,65931,15390,65869,15383,65941,44768,65869,44663,65945, +48284,65869,37897,65949,44243,65869,37845,65953,45411,65955,37845,65952, +44663,65959,37845,65869,44243,65963,45381,65965,44243,65962,44663,65969, +46863,65962,37897,65973, 1175,65869,56555,65977, 1177,65979,49215,65869, + 9,65983,49229,65985,56709,65869, 1009,65989,54641,65869,54643,65993, +60737,65868,60749,65997,11355,65868,11495,66001,57793,65868,57577,66005, +57381,66005,59109,66005,59715,65869,56555,66013,59717,66015, 7959,65869, + 9,66019, 7961,66021,49534,65869,49537,66025,19086,65869, 1203,66029, +49529,65869, 15,66032,49537,66035,19064,65869, 1009,66039,54668,65869, +54643,66043, 1205,65869,18967,66046, 1203,66049,19949,65868,19987,66053, + 1425,65868,57253,66057, 668,65868, 1,66061, 2,66062, 2,66061, + 1,66066,56554,66061,58930,65868, 4,66073, 6,66074, 6,66073, + 4,66078, 8,66073,19965,65868,19987,66085,56745,65868,57253,66089, +60136,65869,59787,66093, 571,65869,18967,66096, 555,66099,16091,65869, + 15,66102, 9513,66105,19078,65869, 21,66109,54655,65869, 15,66112, +54643,66115, 1007,65869,18967,66118, 1009,66121,60125,65869, 15,66124, +59787,66127,19088,65869, 555,66131,16092,65869, 9513,66135, 97,65869, +18967,66138, 21,66141,20347,65869,18967,66144,20345,66147,10095,65869, + 15,66150,10093,66153,20348,65869,20345,66157,10096,65869,10093,66161, +32468,65869,27323,66165,33442,65869,48368,65869,32469,65869,65909,66173, +28483,65869,28479,65869,63604,65869,31690,65869,31700,65869,31524,65869, +31596,65869,32464,65869,53380,65869,25037,65869,24655,65869,25457,65869, +23005,65869,27603,65869,22591,65869,23105,65869,23357,65869,23113,65869, + 120,65869,46863,66212, 77,65869,46863,66216,54154,65869,54225,66220, +56328,65869,58298,65869,58868,65869,52752,65869,52370,65869,58344,65869, +57253,65869,59787,66237, 1425,66237,56745,66237,19987,65869, 21,66245, +19949,66245,19965,66245,25215,65869,48583,65869, 113,66254,25459,65869, +31920,65869,32263,65869,23099,65869,23107,65869,24631,65869,22915,65869, +22589,65869,65294,65869,65322,65869,63534,65869,54256,65869,54129,66280, +25305,65869,32295,65869,31796,65869,62092,65869,63133,65869,22785,65869, +22803,65869,22951,65869,24735,65869,23012,65869,25546,65869,47364,65869, +25548,65869,62442,65869,63294,65869,62432,65869,62560,65869,57577,65869, + 21,66319,59787,66319,57793,66319,57381,65869,59787,66327, 21,66327, +57793,66327,59109,65869, 21,66335,59787,66335,57793,66335,48578,65869, + 33,66343,48276,65869, 33,66347,49308,65869, 33,66351,59340,65869, +63135,65869,61464,65869,61580,65869,61578,65869,62946,65869,62950,65869, +62550,65869,61810,65869,63182,65869,62948,65869,57660,65869,57766,65869, +62060,65869,57772,65869,22767,65869,19810,65869, 251,66387, 555,66387, + 1365,66387, 21,66387, 9994,65869, 9513,66397,16859,66397,59787,66397, +20620,65869,59787,66405,21529,66405, 9513,66405,19372,65869, 251,66413, + 21,66413, 177,66413, 555,66413,20823,65869,59787,66423,21529,66423, +20771,66423, 9513,66423,57971,65869, 21,66433, 251,66433, 177,66433, + 159,66433, 555,66433,25303,65869, 11,66445,24739,65869,29028,65869, +57658,65869,57620,65869,48328,65869, 113,66456,19806,65869, 251,66461, + 555,66461, 21,66461, 8916,65869,54262,65869,46863,66470,54219,65869, +46863,66474,32556,65869, 759,66479, 7061,65869, 3,66483, 2,66483, + 842,66483, 843,66482,66489,66491, 843,66483, 842,66482,66495,66497, + 3,66482,66487,66501, 2,66482,66485,66505,47546,65869, 25,66508, +49283,65869, 25,66512,47600,65869,48444,65869,47572,65869,62146,65869, +17476,65869,23010,65869,63184,65869,61584,65869,48558,65869,47408,65869, +25460,65869,61474,65869,47372,65869,61746,65869,47632,65869,17538,65869, +47634,65869,47676,65869,48112,65869,47412,65869, 5886,65869,48095,65869, + 1072,65869,60857,66560,48408,65869,49713,66564, 241,66564, 587,66565, + 903,66564,48420,65869,61780,65869, 1011,66576,47640,65869, 1011,66580, + 483,66580,47590,65869, 483,66586,47672,65869,47368,65869, 1413,65869, +46863,66594,52692,65869,46863,66598,47370,65869, 922,65869,46863,66604, +49075,65869,49395,65869,48586,65869,10025,65869, 1281,65869,46863,66616, + 1291,65869,60857,66620, 669,65869,66065,66625,66069,66625,40909,66625, + 1821,66625, 1879,66625,38973,66625, 3,66625,38971,66639,57911,66625, +17041,66625, 4897,66625,56739,66625,66071,66625,56555,66625, 671,66653, +66061,66653,25381,65869, 11,66659, 11,66658,58931,65869,66077,66665, +66081,66665,58715,66665,58975,66665,58981,66665,59089,66665, 7,66665, +59079,66679,23879,66665,30399,66665,31937,66665,58983,66665,66083,66665, + 9,66665,58967,66693,66073,66693,58782,65869,49713,66698,60857,66698, + 11,66699, 1108,65869,60857,66706,62089,65869, 483,66710, 4802,65869, + 5314,65869,17670,65869,17693,65869,62059,65869, 483,66722,47608,65869, +60857,66726, 903,66726, 11,66727, 587,66726,19474,65869,10093,66737, +20345,66737,10085,66737,20281,66737,19549,66737,19553,66737,10087,66737, +20285,66737,19555,66737, 483,66737,20273,66757,10081,66757,19545,66757, +61718,65869, 483,66764,22957,65869, 903,66768, 587,66768,62538,65869, +56555,66774, 1011,66774, 288,65869, 9,66781,19916,65869, 7,66785, +35603,66787,35613,66785,10041,66785,10005,66785,10009,66785,23879,66785, +30399,66785,31937,66785, 9,66785,10001,66805,63014,65869,56555,66808, + 11,66809, 587,66809, 1011,66808, 11,66808,48534,65869, 1011,66820, + 1418,65869, 3,66825,38903,66827,38913,66825, 3587,66825, 1989,66825, +56681,66825,57911,66825,17041,66825, 4897,66825,56555,66825, 1471,66845, + 1086,65869,46863,66848, 524,65869,60857,66852, 1304,65869, 9,66857, + 1530,65869, 9,66861, 9195,65869,31103,66864,10031,65869,27641,65869, + 713,65869,31103,66872, 1447,65869,13737,66877, 9875,65869, 15,66880, + 9513,66883,16859,66883,59787,66883, 803,66881, 1325,65869,18967,66892, + 251,66895, 555,66895, 1365,66895, 21,66895,16937,65869,46863,66904, + 1505,65869,28715,66908, 5438,65869,49162,65869, 113,65869,48583,66916, +48328,66916, 25,66916,46863,66922,46863,66916, 25,66926, 375,65869, +63493,66930, 8861,66930,58334,65869,46863,66936,58362,65869,60857,66940, + 506,65869,46863,66944, 587,65869, 759,66949,31103,66951,31103,66948, + 759,66955,22957,66948,47608,66948,46863,66948, 1011,66962, 1011,66948, +46863,66966, 1369,65869, 15,66970, 4897,66973,17041,66973, 3587,66973, + 1989,66973,57911,66973,56681,66973,56555,66973, 1471,66987, 1275,66970, + 9,66991, 1275,65869,18967,66994,31937,66997,30399,66997,10041,66997, +10005,66997,23879,66997,10009,66997, 9,66997,10001,67011, 11,66994, + 9,67015, 9,66995, 11,67019, 1369,67019, 1369,66994, 9,67025, + 483,65869,32805,67028,56156,67028,65076,67028,25545,67028,62089,67028, +62942,67028,62059,67028,47590,67028,47640,67028,17669,67028, 5313,67028, + 4801,67028, 587,67029,46863,67055,61718,67028,49713,67028,46863,67060, + 1011,67028,60857,67064, 903,67028,46863,67068,46863,67028,49713,67072, + 587,67073, 241,67072, 903,67072,60857,67028,56555,67082, 587,67083, + 11,67083, 1011,67082, 11,67082, 11,67028,60857,67094, 5325,67028, +56555,67028,60857,67100, 241,67028,46863,67104, 4593,67028,11985,67028, + 5843,67028,49155,67028, 5876,65869,49155,65869, 483,67118, 6267,65869, + 9,67123, 6367,67125, 6403,67125, 6407,67125,60419,65869,56555,67133, +60425,67135,60429,67135,60433,67135, 5325,65869, 483,67142,55005,65869, + 9,67147, 5843,65869,56555,67150, 1011,67150, 483,67150,12023,65869, +11986,65869, 4608,65869,11985,65869, 483,67164, 3681,65869,56555,67168, + 3709,65869,56555,67172, 3357,65869,56555,67176,52840,65869, 4593,65869, + 483,67182,54202,65869,54159,67187,46863,67189, 4801,65869, 483,67192, +54129,65869,54256,67196,46863,67196,54225,67200,54225,67196,46863,67204, +17669,65869, 483,67208, 5313,65869,56555,67212, 483,67212,15309,65869, + 413,67218,15383,67221, 413,67219,15335,67225,54225,65869,54154,67228, +46863,67228,54129,67232,54129,67228,46863,67236, 1274,65868, 6,67241, + 4,67242,66785,67245,66997,67245, 4,67241, 6,67250,66785,67253, +66997,67253,67011,67241,66805,67241, 8,67241,66997,67263,66785,67263, + 1368,65868, 1,67269, 2,67270,66825,67273,66973,67273, 2,67269, + 1,67278,66825,67281,66973,67281,66987,67269,66845,67269,56554,67269, +66973,67291,66825,67291, 586,65868,24265,67297,28233,67297,29077,67297, +19559,67297, 8251,67297,20361,67297, 9673,67297,20713,67297, 4283,67297, + 9967,67297, 4279,67297, 3807,67297, 482,65869, 903,67323, 1,67325, + 0,67325, 1,67324,67329,67331, 0,67324,67327,67335, 587,65868, +31690,67339,31103,67339, 713,67342, 713,67339,31103,67346,48408,67339, +46863,67339, 483,67352, 483,67339,46863,67356, 483,65868,57577,67361, +57253,67361,19987,67361,59109,67361,57381,67361, 1369,65868, 1275,65868, +47953,67375,48062,67375,47961,67375,48009,67380,48009,67375,47961,67384, +47580,67375,47933,67389,46863,67375, 265,67393,48009,67395, 265,67392, +47933,67399, 265,67375,46863,67403,47961,67405,46863,67402,47933,67409, + 1368,65869,48009,67413,49321,67413,49001,67413, 265,67413,46863,67421, + 586,65869,58056,67425,58601,67425,40799,67425,44243,67425,56555,67432, +35927,67425,38155,67425,37611,67425, 375,67425, 510,67425, 9,67425, + 483,67446,56555,67425,44243,67450, 483,67425, 9,67454, 843,65869, + 9,67459,15705,65869, 9,67463,10001,67465,13755,67465,15733,67465, +27533,67465,17455,65869,56555,67474, 1347,67474, 1011,67474, 767,65869, +56555,67483, 1471,67485, 253,67485, 809,67485, 771,67485, 53,67485, + 1347,65869,17455,67496, 567,65869, 9,67501,10033,65869,27691,65869, +10035,65869,27978,65869,26331,67511,23755,67511,27805,67511, 256,65869, +25793,67519,59875,67519,41019,67519, 8495,67519,53391,67519, 9805,67519, + 972,65869,25793,67533,41019,67533, 8495,67533,20762,65869,58715,67541, + 375,67541, 154,65869,40909,67547,49204,65869, 11,67550, 33,67553, + 11,67551, 25,67557, 1369,67551,37409,67551, 340,65869,25793,67565, +41019,67565, 8717,67565, 8495,67565, 3785,67565,49514,65869, 66,65869, + 33,67579,46863,67581,46863,67578, 33,67585, 112,65869,44243,67589, +27785,65869, 257,65869,67373,67595,31103,67594,31103,65869,27021,67601, +27377,67603,27021,67600,27323,67607, 25,67600, 713,67600, 305,67600, +63493,67600, 8861,67600, 9195,67600,52915,67600, 1369,67601, 587,67601, + 713,67627, 903,67600, 241,67600, 587,67600, 759,67635,49713,67600, + 257,67600, 165,65869,60857,67642,31926,65869,27021,65869,27323,67649, +31103,67651,31103,67648,27323,67655,52915,65869,31103,67658, 305,65869, + 8647,67662,31103,67662, 25,65869,49283,67668,47546,67668,31103,67668, + 113,67668,46863,67676,46863,67668, 113,67680,20501,65869, 15,67684, +59787,67687,21529,67687, 9513,67687, 803,67685, 1141,65869,13737,67697, + 33,65869,13737,67701, 331,65869,18967,67704, 251,67707, 21,67707, + 555,67707, 59,65869,18967,67714, 251,67717, 21,67717, 177,67717, + 555,67717, 8861,65869,60796,67727,60797,67727,60796,67726,67731,67733, +31103,67726,60797,67726,67729,67739, 375,67726,63493,65869,60796,67745, +60797,67745,31103,67744,60797,67744,67747,67753,60796,67744,67749,67757, + 375,67744, 413,65869,15309,67762,15383,67765,15309,67763,15363,67769, +46863,67762,32805,65869, 9,67775, 483,67774,46863,67774, 1216,65869, +46863,67782,65076,65869, 9,67787, 483,67786,46863,67786,56156,65869, + 483,67794,56555,67794,56555,65869,56156,67800,63014,67800,62538,67800, + 3357,67800,17455,67800, 5313,67800, 5843,67800, 3681,67800, 3709,67800, +46863,67800,49713,67820,60857,67820, 11,67821,49713,67800,46863,67828, + 483,67800,60857,67832, 11,67801,46863,67837,60857,67800, 9,67841, + 483,67840,46863,67840,15029,67800, 7,67849, 903,65869,31103,67852, +22957,67852,47608,67852,48408,67852,46863,67852, 1011,67862, 483,67862, + 483,67852,46863,67868, 7,67852,25793,67873,41019,67873, 8495,67873, + 1011,67852,46863,67880,28715,67852, 7,67885,21605,65869, 15,67889, +31103,67891, 7,67888,26331,67895,23755,67895,27805,67895, 7,67889, +26219,67903,49713,65869,58782,67906,48408,67906, 483,67906,46863,67912, +31103,67906,56555,67906,46863,67918, 3,67907,31103,67923,46863,67906, + 483,67926,56555,67926, 241,65869,31103,67932,48408,67932,46863,67932, + 483,67938, 483,67932,46863,67942, 7,67932,25793,67947,59875,67947, +41019,67947, 8495,67947,53391,67947, 9805,67947,56555,67933, 7,67961, + 9,65869,25545,67964,62942,67964,60857,67964, 1011,67970, 1011,67964, +60857,67974,25545,65869, 7,67979, 483,67978, 9,67978,46863,67978, +46863,65869,37844,67988,37896,67989,37844,67989, 6,67994,37845,67988, +67993,67999,67997,67999,67995,67999,37897,67999,54262,67988,54219,67988, + 120,67988, 77,67988, 6,67989,37844,68017,37844,68016,67999,68021, +32805,67988,37845,67989,68019,68027,67991,68027,37899,68027,37897,67989, +67991,68035,54129,67988,54225,68038,52692,67988,58334,67988,65076,67988, +54225,67988,54129,68048,25545,67988, 66,67988, 33,68055,62942,67988, + 25,67988, 113,68060, 113,67988, 25,68064, 506,67988, 1086,67988, + 1281,67988, 1216,67988, 413,67988, 922,67988, 1413,67988,16937,67988, + 587,67989, 483,68085, 483,67988,49713,68088, 241,68088, 587,68089, + 903,68088, 241,67988, 483,68098, 903,67988, 1011,68102, 483,68102, + 1275,67989,56555,67988,49713,68110,60857,68110, 11,68111,60857,67988, +56555,68118, 1011,68118, 1011,67988,60857,68124, 903,68124, 11,68125, + 587,68124, 587,67988, 1011,68134, 2,67988, 3,67988, 11,68140, + 33,68143, 11,68141, 25,68147, 1369,68141,37409,68141, 2,67989, + 3,67989, 483,68157,49713,67988,56555,68160, 483,68160, 11,67988, + 3,68166, 33,68169, 3,68167, 113,68173, 15,65869,10095,68176, +10093,68179,54643,68177,54655,68183,49537,68177,49529,68187,54655,68176, +54643,68191,49529,68176,49537,68195,16091,68176, 9513,68199,60125,68176, +59787,68203,16859,68177, 9875,68207,21529,68177,20501,68211, 1,68176, + 2,68215, 1,68216,68177,68219, 7,68216,68221,68223, 7,68215, + 2,68226, 1368,68215, 6,68176, 2,68233, 6,68234,68177,68237, + 0,68234,68239,68241, 0,68233, 2,68244,18966,68233,56554,68176, + 2,68251, 7,68252, 7,68251, 2,68256, 1368,68251, 482,68176, + 2,68263, 0,68264, 0,68263, 2,68268,18966,68263,20501,68176, + 9513,68275,59787,68275,21529,68275, 9875,68176,59787,68283, 9513,68283, +16859,68283,59787,68177,60125,68291, 9875,68291,20501,68291, 9513,68177, +16091,68299,20501,68299, 9875,68299, 483,68177, 2007,68307, 3477,68307, +68267,68307,68271,68307, 5197,68307,68241,68307,68247,68307, 1,68307, +19051,68307,19667,68307, 5123,68307,12345,68307,68249,68307,68273,68307, +18967,68307, 555,68337,68233,68337,68263,68337,56555,68177,59637,68345, +59717,68345,68255,68345,68259,68345,68223,68345,68229,68345, 6,68345, +59649,68345,39747,68345,43837,68345,43027,68345,68231,68345,68261,68345, + 1369,68345,59619,68373,68215,68373,68251,68373,18967,68176,19553,68381, +19549,68381,20345,68381,20281,68381, 7,68381,68345,68391,68359,68381, +10093,68381,10085,68381,20285,68381,10087,68381,19555,68381, 483,68381, +20273,68407,10081,68407,19545,68407, 1369,68176,67281,68415,67273,68415, + 3587,68415, 1989,68415, 0,68415,68307,68425,68323,68415,56681,68415, +57911,68415, 4897,68415,17041,68415,67291,68415,56555,68415, 1471,68441, +67269,68441, 3,68176,40909,68447, 3,68177,40895,68451,40059,68451, +43749,68451,38903,68451,18967,65869,20347,68460,20345,68463, 1203,68461, + 1205,68467, 1009,68461, 1007,68471, 1205,68460, 1203,68475, 1007,68460, + 1009,68479, 97,68460, 21,68483, 571,68460, 555,68487, 177,68461, + 59,68491, 1365,68461, 1325,68495, 2,68460, 6,68499, 2,68500, +68461,68503, 5,68500,68505,68507, 5,68499, 6,68510, 14,68499, + 4,68460, 6,68517, 4,68518,68461,68521, 3,68518,68523,68525, + 3,68517, 6,68528, 1274,68517, 482,68460, 6,68535, 5,68536, + 5,68535, 6,68540, 14,68535, 8,68460, 6,68547, 3,68548, + 3,68547, 6,68552, 1274,68547, 256,68461, 629,68461, 1325,68460, + 21,68563, 251,68563, 555,68563, 1365,68563, 59,68460, 555,68573, + 251,68573, 21,68573, 177,68573, 555,68461, 571,68583, 331,68583, + 59,68583, 1325,68583, 21,68461, 97,68593, 331,68593, 1325,68593, + 59,68593, 331,68460, 251,68603, 555,68603, 21,68603, 251,68461, + 331,68611, 59,68611, 1325,68611, 9,68461, 7951,68619, 7961,68619, +68551,68619,68555,68619,68451,68619,68525,68619,68531,68619, 2,68619, + 7953,68619,35893,68619,38299,68619,37897,68619,68533,68619,68557,68619, + 1275,68619, 7947,68649,68517,68649,68547,68649, 483,68461, 9517,68657, + 9525,68657,68539,68657,68543,68657,20033,68657,68507,68657,68513,68657, + 4,68657, 9519,68657,57855,68657,60853,68657,60609,68657,68515,68657, +68545,68657, 15,68657, 9513,68687,68499,68687,68535,68687, 1275,68460, +67245,68695,67253,68695,10041,68695,10005,68695, 5,68695,68657,68705, +68673,68695,10009,68695,23879,68695,31937,68695,30399,68695,67263,68695, + 9,68695,10001,68721,67241,68721, 15,68460,19549,68727,19553,68727, +10093,68727,10085,68727, 3,68727,68619,68737,68635,68727,20345,68727, +20281,68727,10087,68727,20285,68727,19555,68727, 483,68727,10081,68753, +20273,68753,19545,68753, 7,68460,58715,68761, 375,68761, 7,68461, +58703,68767,58027,68767,60849,68767,68345,68767,35603,68767, 59,68767, + 1325,68767, 331,68767, 265,68767, 241,68766, 241,68461, 6,68789, + 7,68789, 6,68788,68793,68795, 7,68788,68791,68799, 265,68460, + 375,68803, 1011,65869,59960,68807,61780,68806,63014,68806,62538,68806, +17455,68806,47640,68806, 5843,68806,48534,68806, 483,68806,60857,68824, + 9,68806,60857,68828,46863,68806,60857,68832, 903,68832, 11,68833, + 587,68832, 587,68806,46863,68842, 11,68807,46863,68847, 7,68807, +56555,68850,63823,68851, 9377,68851, 903,68806,46863,68858,56555,68807, + 6,68863, 7,68863, 6,68862,68867,68869, 7,68862,68865,68873, +60857,68806, 7,68877, 483,68876,46863,68876, 9,68876,62942,65869, + 7,68887,46863,68886, 9,68886, 483,68886,60857,65869,58782,68896, +58362,68896,47608,68896, 1108,68896, 1072,68896, 165,68896, 524,68896, + 1291,68896, 11,68897, 483,68915, 9,68896, 1011,68918, 587,68897, + 483,68923,46863,68896,56555,68926, 1011,68926, 483,68896,56555,68932, + 587,68933, 11,68933, 1011,68932, 11,68932, 11,68896, 483,68944, + 7,68897, 1141,68949, 1447,68949, 33,68949,56555,68896, 9,68957, +46863,68956, 483,68956, 1011,68896, 7,68965, 9,68964, 483,68964, +46863,68964, 265,65869,16710,68974,44242,68974,16711,68974,16710,68975, +68981,68983,45086,68975,44242,68975, 3,68988,44243,68974,68987,68993, +68991,68993,60797,68975,60796,68974,68999,69001,60796,68975,60797,68974, +69005,69007, 3,68975,44242,69011,44242,69010,68993,69015, 6,69010, + 1274,68975,44243,68975,69013,69023,45087,68975,68979,69027,16711,68975, +68977,69031, 1275,68975, 4899,69035,14611,69035,47961,69035, 11,68974, + 9,69043,46863,68974,69019,69047,69021,69047, 9,68975, 11,69053, + 6,68974, 0,69056,69035,69059, 7,68974,25793,69063,41019,69063, + 8717,69063, 8495,69063, 3785,69063, 6,68975, 3,69074,69047,69077, + 7,68975, 8647,69081,21605,68975, 375,69085,18967,68974, 375,69089, + 11,65869,39085,69093,49204,69092, 33,69097, 803,69093,63014,69092, +25381,69092, 265,69092, 9,69107,60857,69092, 483,69110, 483,69092, +60857,69114, 1275,69092, 9,69119, 2,69092,44243,69123, 3,69092, + 33,69127,46863,69129,46863,69126, 33,69133, 3,69093,49283,69137, +47546,69137, 113,69137,46863,69142,46863,69137, 113,69146,46863,69092, + 3,69150, 33,69153, 3,69151, 113,69157,20034,65869, 375,69161, +48560,65869, 3,69164, 33,69167, 3,69165, 113,69171,59206,65869, + 7,69175,29598,65869, 7,69179,60455,65869, 256,69183, 375,69183, + 7,69183, 241,69188, 241,69183, 7,69192,15029,65869,56555,69196, + 7,69199,28715,65869, 256,69203, 1505,69202, 7,69203, 241,69208, + 241,69203, 7,69212, 903,69202, 7,69217,54027,65869, 375,69221, + 2,69220,54159,69225,46863,69227, 2,69221,54256,69231,46863,69231, +54225,69234,54225,69231,46863,69238,17635,65869, 7,69243, 2,69244, + 2,69245, 1368,69243, 1369,69243, 2,69242,69253,69255, 3,69242, +69247,69259,69251,69259, 2,69243, 7,69264,69259,69267, 3,69243, +69249,69271, 2,65868,41692,69275,41607,69275, 8672,69275, 8743,69275, +26454,69275,26375,69275, 8770,69275, 8729,69275, 3795,69275,47953,69275, +48062,69275,47961,69275,48009,69298,48009,69275,47961,69302,41417,69275, +41653,69306,26219,69275,26415,69310,31770,69275,44832,69275, 8757,69275, + 8647,69318,26415,69275,26219,69322,41653,69275,41417,69326, 8647,69275, + 305,69330, 8757,69330, 8605,69275,47580,69275,47933,69339, 8684,69275, + 8717,69343, 8916,69275,31103,69275, 9,69348,26292,69275,26331,69353, + 340,69275, 8717,69357, 3785,69357, 8495,69357, 375,69275, 8861,69364, +46863,69364,47488,69275,41502,69275,41559,69373, 483,69275,44243,69376, + 305,69275, 8647,69380, 7,69275, 1,69385, 264,69386, 264,69387, +41019,69385,41653,69393,25793,69385,26415,69397, 8495,69385, 305,69401, + 8757,69401, 8495,69384, 8717,69407,25793,69384,26331,69411, 265,69385, +69391,69415, 8861,69415,46863,69415,41019,69384,41559,69423, 265,69384, + 8717,69427,69389,69427, 3785,69427, 8495,69427,44243,69275, 483,69436, + 9,69275,31103,69440,46863,69275, 265,69445,48009,69447, 265,69444, +47933,69451, 375,69444,25793,69275, 7,69457,26219,69459, 7,69456, +26331,69463, 265,69275,46863,69467,47961,69469,46863,69466,47933,69473, + 7,69467, 8647,69477, 7,69466, 8717,69481, 3785,69481, 8495,69481, +41019,69275, 7,69489,41417,69491, 7,69488,41559,69495, 8495,69275, + 7,69498, 8717,69501, 7,69499, 8647,69505, 8861,69275, 375,69508, + 3,65868,21116,69513,21253,69513,21560,69513,20465,69513,20576,69513, +59683,69513,42035,69513, 9573,69513,59689,69513, 9575,69513,42041,69513, +43711,69513,42089,69513,42129,69513, 1,69513, 6,69543,68974,69544, +68975,69545,69547,69549,68974,69545,68975,69544,69553,69555, 4,69513, + 6,69558,66665,69561,66785,69561,66997,69561,68695,69561, 9599,69513, + 8,69513,66997,69573,66785,69573,66665,69573,68695,69573,67011,69513, +66805,69513,68721,69513,66693,69513,60569,69513,59701,69513,59767,69513, + 9605,69513,32468,69513,28483,69513,19625,69513,20501,69602,43015,69513, +44714,69513,19474,69513,20413,69611,20805,69513,20186,69513,21549,69513, +20501,69618,21105,69513,20501,69622, 1418,69513,59619,69627, 9565,69627, +41941,69627, 669,69513, 9565,69635,59619,69635,41941,69635,37501,69513, +31103,69513,27021,69644, 8386,69513,50657,69513,36944,69513,48408,69513, + 1369,69513, 15,69656, 9565,69659,41941,69659,59619,69659, 483,69513, +46863,69666,20501,69513,21105,69670,21549,69670,19625,69670, 6,69513, + 3,69679,56555,69681,65869,69683, 4,69678,66665,69687,66785,69687, +66997,69687,68695,69687,69685,69687, 3,69678,68460,69699,69687,69701, +18967,69699,65869,69704,69687,69707,65869,69699,18967,69710,69687,69713, + 7,69513,20103,69716, 8337,69716,36773,69716,27021,69513,31103,69724, + 7,69512,24265,69729,28233,69729,29077,69729,20361,69729, 9673,69729, + 4279,69729, 9967,69729,19559,69729, 4283,69729,20713,69729, 3807,69729, + 8251,69729,44243,69513,41941,69754,46863,69513, 483,69758, 15,69513, +18967,69763,20501,69765,18967,69762,20413,69769, 1369,69762, 9565,69773, +41941,69773,59619,69773, 8337,69513, 7,69780,18967,69513, 15,69784, +20413,69787, 1011,69513,44242,69790,44242,69791,44243,69790,69795,69797, +44243,69791,69793,69801,28715,69513, 7,69805,36773,69513, 7,69808, +41941,69513,44243,69812,20103,69513, 7,69816,20273,69513, 6,69820, + 7,69820, 6,69821,69825,69827, 7,69821,69823,69831, 9617,69513, + 6,69834, 7,69834, 6,69835,69839,69841, 7,69835,69837,69845, + 6,65868, 1,69849, 3,69850, 4,69849, 3,69855,67988,69856, +67989,69857,69859,69861,67988,69857,67989,69856,69865,69867,21604,69849, + 3,69849, 1,69872, 7,65868,59007,69877,58999,69877,59025,69877, +19981,69877,48747,69877,37279,69877,57047,69877,57029,69877,58153,69877, + 1,69877, 2,69896,66625,69899,66825,69899,66973,69899,68415,69899, +58167,69877,56554,69877,66973,69911,66825,69911,66625,69911,68415,69911, +66987,69877,66845,69877,68441,69877,66653,69877,24601,69877,57437,69877, +57021,69877,57069,69877,54107,69877,52743,69877,52193,69877,20734,69877, +25551,69877,24613,69877,28205,69877,20501,69877, 265,69950, 241,69951, + 1141,69877,56555,69957,60577,69877,62959,69877,58377,69877,57981,69877, +19916,69877, 7947,69969,48739,69969,37241,69969,58931,69877,48739,69977, + 7947,69977,37241,69977, 8306,69877, 33,69877,56555,69987, 1447,69877, +56555,69991, 9875,69877, 241,69995, 265,69994, 9992,69877, 545,69877, + 695,69877, 1382,69877, 1374,69877, 1386,69877, 154,69877, 483,70013, +60455,69877, 483,70017, 1369,69877, 331,70020, 1325,70020, 59,70020, + 1325,69877, 1369,70028, 1275,69877,18967,70032,48739,70035,37241,70035, + 7947,70035, 59,69877, 1369,70042, 2,69877, 7,70047, 9,70049, +65869,70051, 1,70046,66625,70055,66825,70055,66973,70055,68415,70055, +70053,70055, 7,70046,68176,70067,70055,70069, 15,70067,65869,70072, +70055,70075,65869,70067, 15,70078,70055,70081, 3,69877, 8241,70085, + 15,70084, 483,70089, 331,69877, 1369,70092, 3,69876,24265,70097, +28233,70097,29077,70097,19559,70097,20713,70097, 4283,70097,20361,70097, + 9673,70097, 4279,70097, 9967,70097, 8251,70097, 3807,70097,18967,69877, + 1275,70122,48739,70125,37241,70125, 7947,70125, 113,70123, 1011,69877, + 265,69877,20501,70136, 8165,70136, 9875,70136,46863,69877,38056,70144, +38056,70145,38057,70144,70149,70151,38057,70145,70147,70155, 15,69877, + 3,70159, 1369,70161, 3,70158, 483,70165,54027,69877, 483,70169, + 8165,69877, 265,70172, 7,65869,69874,70176,69875,70177,70179,70181, +69875,70176,69874,70177,70185,70187,69852,70176,69853,70177,70191,70193, +69853,70176,69852,70177,70197,70199,11276,70176,11277,70176,11276,70177, +70205,70207, 5044,70176, 5045,70176, 5044,70177,70213,70215,40583,70177, +40583,70176,40582,70176,70219,70223,40582,70177,70221,70227,24094,70177, +69871,70177,69870,70177,21604,70177,69849,70236, 9,70236,21604,70176, +70233,70243,69849,70243, 9,70243, 4,70177, 0,70250, 1,70177, + 264,70254, 264,70255,24095,70177,70243,70261,45149,70177,26498,70177, +41326,70177, 8796,70177,27533,70177, 9,70273,25793,70177,27021,70277, + 587,70276, 241,70277, 5045,70177,70211,70285,11277,70177,70203,70289, + 4551,70177,10001,70177, 9,70295, 8495,70177, 305,70299, 587,70298, + 241,70299, 88,70177, 1369,70307, 587,70177,25793,70310,41019,70310, + 8495,70310, 1369,70177,20103,70319,36773,70319, 8337,70319, 265,70177, +70259,70327,63493,70327, 8861,70327,46863,70327, 89,70177,46863,70337, + 483,70177,18967,70341,37241,70343,48739,70343,70253,70343, 7947,70343, + 1275,70177,44243,70353,21605,70176,70231,70357,70235,70357,70239,70357, +70241,70357,70237,70357,69849,70367, 9,70367,26331,70357,23755,70357, +27805,70357, 241,70176,25793,70379,59875,70379,41019,70379, 8495,70379, + 9805,70379,53391,70379, 903,70176,25793,70393, 8495,70393,41019,70393, +18967,70176,58715,70401, 375,70401, 265,70176,70257,70407,25793,70407, +41019,70407, 8717,70407, 8495,70407, 3785,70407,21605,70177,70246,70421, +69849,70421,70243,70424,70248,70421,70243,70421,69849,70430, 9,70430, +24545,70421, 9,70421,70243,70438, 903,70421, 241,70177,51487,70445, + 375,70177, 1369,70449,41019,70177, 587,70452, 241,70453, 2,70176, +48009,70459, 265,70459,46863,70463,49321,70459,49001,70459, 3,70176, +58056,70471,58601,70471,40799,70471,44243,70471,56555,70478,35927,70471, + 375,70471, 510,70471, 483,70471, 9,70488,38155,70471,37611,70471, +56555,70471,44243,70496, 9,70471, 483,70500, 2,70177,44243,70505, +48466,70505,47640,70505,46863,70505, 9,70512, 903,70512, 903,70505, +46863,70518, 9,70505,46863,70522,49155,70505, 3,70177, 1369,70529, + 9,70531,37663,70529, 9,70528, 1369,70537,18967,70529,44243,70541, +69849,70177,21604,70545,70421,70547,21604,70544,70357,70551, 9,70177, +21604,70555,70421,70557,21604,70554,70357,70561, 3,70554, 1369,70565, + 3,70555, 483,70569, 3,65869,60078,70572,60079,70572,13748,70573, +13736,70572,70579,70581,13736,70573,13737,70572,13749,70573,70587,70589, + 1,70573, 5,70592, 1,70572,69385,70597,69384,70597,69275,70597, + 7,70602, 7,70603,59961,70597, 6,70596,70601,70611,70605,70611, + 7,70596,70599,70617,69275,70617, 6,70597,69275,70623,70617,70624, +70607,70623,70620,70623,70617,70623,69275,70632,56555,70623, 7,70597, +69275,70638,70611,70641,70611,70639,69275,70645,48560,70572, 33,70649, +60230,70573,57986,70573,58352,70573, 8309,70573,13737,70573, 7,70660, +70581,70663,59960,70573,70575,70667,70611,70667, 241,70666, 256,70573, +56555,70674, 11,70573,48583,70679,48328,70679, 25,70679,46863,70684, +46863,70679, 25,70688,59961,70573,70577,70693, 483,70573, 15,70697, +41941,70699, 9565,70699,70595,70699,59619,70699, 1369,70573,46863,70709, +37409,70573,46863,70713,46863,70572, 11,70716, 33,70719, 1369,70717, + 11,70717, 25,70725,37409,70717, 15,70572,40909,70731, 11,70572, + 33,70735,46863,70737,46863,70734, 33,70741, 6,70572, 1,70744, +70667,70747, 5,70744, 7,70572,70609,70753,58056,70753,70636,70753, +70623,70753,56555,70760,40799,70753,58601,70753,44243,70753,56555,70768, +35927,70753, 375,70753, 510,70753, 483,70753, 9,70778,38155,70753, +37611,70753, 9,70753, 483,70786,56555,70753,70623,70790,44243,70790, + 6,70573,70731,70797,56555,70797, 7,70573,13737,70802,70581,70805, +13737,70803,70585,70809,70611,70803,56555,70813,70747,70803,56555,70817, +70751,70803,42613,70803,56555,70823,54497,70803,54159,70803,51803,70803, +58348,70802,10291,70803,56555,70835, 59,70803, 1325,70803, 331,70803, + 1275,70803, 9,70845,56555,70802,70611,70849,70747,70849, 241,70848, + 241,70802,56555,70856, 15,70803, 483,70861,69275,70573,58348,70573, + 7,70866,38057,70573, 7,70871,43503,70573,44243,70875,56555,70573, + 256,70878, 7,70878,70611,70883,70747,70883, 241,70882, 7,70879, +70623,70891, 241,70878, 7,70894, 241,70573,44242,70899,44242,70898, +44243,70899,70903,70905,59960,70898,44243,70898,70901,70911, 7,70898, +56555,70914,56555,70898, 7,70918, 2,65869, 1,70922,69635,70925, + 668,70925,69513,70929,69627,70925, 1419,70925,69513,70935,70699,70925, +69659,70925,69773,70925, 587,70925, 9,70944,69513,70947, 9,70925, + 587,70950,69513,70953,54027,70922,54159,70957,46863,70959,54027,70923, +54154,70963,46863,70963,54129,70966,54129,70963,46863,70970,44243,70923, + 11,70975,46863,70922, 11,70922,44243,70981, 6,70922, 0,70985, + 5,70986, 5,70985, 0,70991, 0,70990, 1,70984,70995,70997, +70989,70997,70987,70997, 5,71003, 1,70985,70993,71007, 5,71007, + 0,70984, 5,71013,71007,71014,71010,71013,71007,71013, 5,71020, + 903,70985, 1,71025, 0,71025, 1,71024,71029,71031, 0,71024, +71027,71035, 7,70922,48009,71039, 265,71039,46863,71043,49321,71039, +49001,71039, 7,70923,70989,71051,70997,71052,70995,71051,70997,71056, +70998,71051,71000,71051,71005,71051,71009,71051,71017,71051,71019,71051, +71023,71051,71011,71051,71013,71075,14219,71051,14013,71051,14267,71051, +16151,71051,70997,71051,70995,71086,70989,71086,56554,71051, 1,71051, + 2,71094, 2,71051, 1,71098,14211,71051, 9165,71103, 9,71051, +65869,71107,71093,71109,71097,71109,71101,71109,70985,71051,69513,70923, + 903,70923,46862,71120,46862,71121,46863,71121,71123,71127,46863,71120, +71125,71131, 6,65869, 1,71134,70803,71137,56555,71139, 4,71134, +69977,71143,58930,71143,69877,71147,69969,71143,19917,71143,69877,71153, +70343,71143,70035,71143,70125,71143, 587,71143,56555,71162,69877,71165, +56555,71143, 587,71168,69877,71171, 2,71134, 903,71175, 1,71177, + 0,71177, 1,71176,71181,71183, 0,71176,71179,71187, 2,71135, + 265,71191, 0,71192, 1,71192, 0,71193,71197,71199, 1,71193, +71195,71203, 3,71135, 8,71207, 4,71207, 6,71210, 6,71207, + 4,71214,56555,71207,65869,71219,71209,71221,71213,71221,71217,71221, + 0, 5,59619,71229,59961,71230,60166,71229, 1207,71228,19105,71237, + 1180,71228,19119,71241,60158,71228,60180,71228,60178,71228,60318,71228, +60167,71229, 1197,71228,59875,71255,53391,71255, 9805,71255,60159,71228, +60181,71228,60179,71228,38158,71229,44195,71269, 1196,71228,60319,71228, +28739,71229, 2,71277,29229,71279, 59,71228,60221,71283,63823,71228, +61747,71287,61585,71287,61475,71287,63185,71287, 39,71228,56555,71297, + 15,71299, 6,71298,71301,71303, 14,71299, 7,71298,71307,71309, + 1163,71228,19119,71313, 25,71228,61747,71317,63185,71317,25461,71317, +61585,71317,70985,71228,71086,71327,70997,71327,71051,71330,71051,71327, +70997,71334,38057,71229, 6,71338,44195,71341,60973,71229, 1179,71345, +49213,71229,49217,71349,54619,71229, 11,71353,54655,71355, 1003,71229, +60857,71359, 1007,71361,11995,71228,12025,71365,64569,71228,64587,71369, +61989,71228,61747,71373,63185,71373,61585,71373, 8065,71229, 11,71381, + 8067,71383,63777,71229,60857,71387,63779,71389,21918,71229, 987,71393, +54336,71229,54339,71397, 989,71229,21605,71400, 987,71403,21890,71229, + 1179,71407,49262,71229,49217,71411,54331,71229, 17,71414,54339,71417, +25395,71228,25461,71421, 1293,71228,61475,71425,25433,71228,25461,71429, +61005,71228,61475,71433,63014,71228, 5,71437, 7,71438, 7,71437, + 5,71442, 10,71437, 524,71228, 0,71449, 3,71450, 3,71449, + 0,71454,60856,71449,21904,71229, 25,71461, 809,71229,21605,71464, + 713,71467,13755,71229, 17,71470, 9377,71473,65054,71229,63823,71477, + 117,71229,21605,71480, 25,71483,21916,71229, 713,71487,13764,71229, + 9377,71491,65037,71229, 17,71494,63823,71497, 1177,71229,21605,71500, + 1179,71503,49229,71229, 17,71506,49217,71509,26867,71229,21605,71512, +26865,71515,10161,71229, 17,71518,10159,71521,26868,71229,26865,71525, +10162,71229,10159,71529, 1180,71229, 2975,71533, 1903,71533,19025,71533, +33464,71229,34655,71541,34447,71541,33221,71541,33773,71541, 1207,71229, +19027,71551, 3567,71551, 1973,71551, 3041,71551, 1921,71551,19049,71551, + 9824,71229,71273,71565,53462,71229,71273,71569,59900,71229,71273,71573, +60319,71229,71251,71577,60159,71229,71245,71581,60178,71229,71267,71585, +59901,71229,71255,71589,60158,71229,71263,71593,60180,71229,71265,71597, +25461,71229, 25,71601,25395,71601,25433,71601,61475,71229,63823,71609, + 1293,71609,61005,71609,65808,71229,65513,71617,65791,71617,65407,71617, +65171,71617,60835,71229,60318,71229,71275,71629, 9617,71629,19797,71229, + 375,71635,20735,71229,47386,71229,20040,71229,20050,71229,61747,71229, +63823,71647, 25,71647,61989,71647,63185,71229, 25,71655,63823,71655, +61989,71655,61585,71229,63823,71663, 25,71663,61989,71663,20056,71229, + 375,71671, 9825,71229,71255,71675,53463,71229,71255,71679,57562,71229, +58846,71229,58372,71229,58758,71229,58762,71229,57224,71229,57284,71229, +57278,71229,58760,71229,20686,71229,24738,71229, 305,71703, 713,71703, + 1489,71703, 25,71703,10074,71229, 9377,71713,17573,71713,63823,71713, +22766,71229, 305,71721, 25,71721, 147,71721, 713,71721,27672,71229, +63823,71731,27995,71731, 9377,71731,62147,71229, 25,71739, 305,71739, + 147,71739, 169,71739, 713,71739,31881,71229,63823,71751,27995,71751, +31359,71751, 9377,71751,60221,71229, 59,71761, 653,71229, 11,71765, + 9993,71229,17542,71229, 9936,71229,24734,71229, 305,71775, 713,71775, + 25,71775,60834,71229,21350,71229,16129,71229, 3,71787, 2,71787, + 842,71787, 843,71786,71793,71795, 843,71787, 842,71786,71799,71801, + 3,71786,71791,71805, 2,71786,71789,71809,16966,71229,19480,71229, +50522,71229,52740,71229,57380,71229,59108,71229,50478,71229,57252,71229, +19986,71229,51036,71229,57576,71229,58112,71229,58362,71229, 903,71836, +47640,71229,56555,71840,60179,71229,71249,71845,60181,71229,71247,71849, +63015,71229,71441,71853,71445,71853,62905,71853,63041,71853,63047,71853, +63215,71853, 6,71853,63205,71867,19687,71853,21127,71853,20843,71853, +63049,71853,71447,71853, 11,71853,63033,71881,71437,71881,19917,71229, + 9,71886, 525,71229,71453,71891,71457,71891,36729,71891, 1851,71891, + 1885,71891,35635,71891, 2,71891,35633,71905,62095,71891,17711,71891, + 4907,71891,61003,71891,71459,71891,60857,71891, 531,71919,71449,71919, + 1413,71229, 331,71925,49713,71924, 1419,71229,56555,71930,58782,71229, + 903,71934, 296,71229, 8861,71939, 1106,71229,18967,71943, 9617,71943, +24095,71229,46863,71949,57522,71229, 11,71953, 903,71952, 587,71952, +51004,71229, 483,71960,22956,71229,70471,71965,70753,71965,10159,71965, +26865,71965,10111,71965,26715,71965,23709,71965,23713,71965,67425,71965, +10113,71965,26719,71965,23715,71965, 587,71965,10107,71991,26625,71991, +23651,71991,19475,71229, 483,71998, 918,71229,56555,72002, 922,71229, +56555,72006,49713,72006, 668,71229,56555,72012, 1290,71229, 2,72017, +35575,72019,35585,72017, 3621,72017, 1991,72017,60969,72017,62095,72017, +17711,72017, 4907,72017,60857,72017, 1325,72037,58930,71229, 9,72040, +52692,71229, 903,72044,25380,71229, 6,72049,38881,72051,38891,72049, + 9927,72049, 9891,72049, 9895,72049,19687,72049,21127,72049,20843,72049, + 11,72049, 9875,72069, 1447,71229,15705,72073, 9917,71229,20615,71229, + 759,71229,17455,72081, 9195,71229, 1487,72085, 1471,71229,21605,72088, + 305,72091, 713,72091, 1489,72091, 25,72091, 9805,71229, 1197,72101, + 1275,72101, 9,72105, 1197,72100,71273,72109,10001,71229, 17,72112, + 9377,72115,17573,72115,63823,72115,17455,71229,12377,72122,59961,71229, +59619,72126, 1011,72126, 9617,72131, 1011,72127, 9565,72135,59619,72135, + 89,71229,17455,72141,60797,71229,59381,72144, 165,71229,21315,72148, + 587,71229,66971,72153,17455,72153,57522,72152, 9,72152,56555,72160, +56555,72152, 9,72164,60857,72153, 9,72169, 1369,71229,39085,72173, +21605,72172,20843,72177,21127,72177, 9927,72177, 9891,72177,19687,72177, + 9895,72177, 11,72177, 9875,72191, 1275,71229, 17,72194, 4907,72197, +17711,72197, 3621,72197, 1991,72197,62095,72197,60969,72197,60857,72197, + 1325,72211, 483,71229,20035,72214,58742,72214,19475,72214,51004,72214, +56555,72214, 903,72224, 903,72214,56555,72228,49713,72228,49713,72214, + 903,72234,55839,71229,46863,72239,65061,71229,60857,72243,65063,72245, +65067,72245,65071,72245, 7065,71229,18967,72253, 6789,71229, 11,72257, + 6843,72259, 6885,72259, 6889,72259,11277,71229,46863,72267, 9935,71229, + 1011,72270,20645,71229, 1011,72274, 3445,71229, 375,72279, 5045,71229, +46863,72283,17104,71229,48643,72287,12614,71229, 375,72291,69872,71229, +68155,72295,71116,71229,71013,72299,70985,71229,71013,72303,71051,72305, +71051,72302,71013,72309,71051,71229,70985,72313,70997,72315,70985,72312, +71013,72319, 482,71228,23717,72323,24522,72323,24447,72323,23939,72328, +23939,72323,24447,72332,22956,72323,23651,72337,21605,72323, 17,72341, +23939,72343, 17,72340,23651,72347, 17,72323,21605,72351,24447,72353, +21605,72350,23651,72357, 1368,71228, 7,72361, 5,72362,72049,72365, +72177,72365, 5,72361, 7,72370,72049,72373,72177,72373,72191,72361, +72069,72361, 10,72361,72177,72383,72049,72383,27021,72361,24095,72361, + 9195,72361,52915,72361,23651,72361,51435,72361,21605,72361, 9,72401, +67297,72361,70097,72361,69729,72361,11277,72361, 5045,72361,10171,72361, +53941,72361, 9755,72361,26907,72361, 4321,72361,27323,72361, 3833,72361, + 9317,72361, 4295,72361,53215,72361, 1274,71228, 3,72435, 0,72436, +72017,72439,72197,72439, 0,72435, 3,72444,72017,72447,72197,72447, +72211,72435,72037,72435,60856,72435,72197,72457,72017,72457, 586,71228, +39619,72463,51421,72463,57811,72463,59933,72463,59743,72463, 8093,72463, + 3837,72463,42435,72463, 4287,72463,53703,72463, 4303,72463,42113,72463, + 9839,72463,53487,72463, 9579,72463, 483,72463, 803,72494, 828,72463, + 705,72463, 803,72463, 483,72502, 482,71229,66737,72507,36661,72507, +62741,72507,68727,72507,68381,72507, 1274,71229, 587,71228,61747,72521, +25461,72521,61475,72521,63185,72521,61585,72521, 483,71228,56555,72533, + 6,72535, 5,72536, 7,72535, 5,72535, 6,72542, 6,72543, +72541,72547, 6,72534, 15,72535,72551,72553, 7,72534,72545,72557, +72539,72557, 14,72535,72557,72563, 1368,71229,55839,72567,51803,72567, +54159,72567,54497,72567, 265,72567, 1275,71228,59914,72579,60221,72579, +18708,72579, 9856,72579,53504,72579, 9805,72579, 9,72590,53391,72579, + 9,72594,59875,72579, 9,72598,18693,72579, 9,72602,60369,72579, + 9,72579,18693,72608, 9805,72608,59875,72608,53391,72608, 586,71229, + 33,72619, 1447,72619, 1141,72619, 889,72619, 795,72619, 787,72619, + 767,72619, 483,72633, 1505,71229,16937,72636,56555,72636, 581,71229, +60857,72643, 1325,72645, 331,72645, 571,72645, 585,72645, 59,72645, +16937,71229, 1505,72656,13947,71229, 2,72661,14217,72663, 11,72661, + 9875,72667,16091,72667,13955,72667,20501,72667, 9919,71229,20623,71229, + 9921,71229, 1196,71229,72103,72683,71256,72683,71258,72683,71260,72683, +71255,72683,59875,72692,53391,72692, 9805,72692,59875,72683,71255,72700, + 9805,72683,71255,72704,53391,72683,71255,72708, 154,71229,64148,71229, + 1325,72715, 59,72715, 331,72715, 265,72715,52501,71229, 265,72725, + 297,71229,18967,72729, 1107,71229,59960,71229,21105,72735,65513,72735, +65791,72735,65407,72735,21549,72735,19625,72735,65171,72735,59619,72735, + 15,72735, 88,71229,70177,72755,70176,72754,72757,72759,70176,72755, +70177,72754,72763,72765, 1141,72755, 1447,72755, 33,72755, 11,72755, +37409,72755, 972,71229, 59,72779, 1325,72779, 331,72779,27978,71229, +60796,71229,59381,72789,31102,71229,62905,72793,68761,72793,70401,72793, +67541,72793, 164,71229,36729,72803,68447,72803,70731,72803,67547,72803, +54596,71229,44243,72813,20695,71229, 155,71229,20763,71229, 375,72820, + 1197,71229,71273,72825,59875,72827, 9805,72827,53391,72827,53391,72824, +71273,72835, 9805,72824,71273,72839,59875,72824,71273,72843, 375,71229, +20763,72846,20836,71229, 1141,71229,15705,72853, 33,71229,15705,72857, +27021,71229, 1487,72861, 959,71229,17455,72865,52915,71229, 1487,72869, + 47,71229,17455,72873,59875,71229, 1197,72877,72683,72879, 1275,72877, + 9,72883, 1197,72876,71273,72887, 253,71229,21605,72890, 305,72893, + 25,72893, 713,72893,53391,71229, 1197,72901,72683,72903, 1275,72901, + 9,72907, 1197,72900,71273,72911, 53,71229,21605,72914, 305,72917, + 25,72917, 147,72917, 713,72917,27533,71229, 17,72926,63823,72929, +27995,72929, 9377,72929,30749,71229, 7,72937,26970,71229, 7,72941, +70864,71229, 7,72945,69849,71229, 3,72948,68155,72951, 3,72949, +67577,72955,68139,72955,70979,72955,69275,71229,70573,72962, 7,72965, +69513,71229, 1011,72968, 7,72971, 9780,71229, 7,72975,59381,71229, + 2,72979,72789,72981,60797,72978, 6,72978,56555,72986,72981,72989, +26625,71229, 1011,72992, 7,72995, 9731,71229, 1011,72998, 7,73001, +69790,71229, 7,73005, 13,71229,32805,71229, 7,73010,33221,73013, +34655,73013,33773,73013,34447,73013, 15,73011,34875,73023, 7,73011, +34723,73027, 19,71229, 59,73031, 18,71229,65076,71229, 7,73037, +65493,73039, 7,73036,65171,73043,65791,73043,65513,73043,65407,73043, + 15,73037,65357,73053, 12,71229,68974,71229, 375,73059,71119,71229, + 7,73063, 1011,71229,52387,73067,20762,73067,59961,73066, 9617,73073, +20645,73066, 9935,73066, 17,73066,18967,73081, 9617,73081, 6,73066, + 7,73067,18967,73088,26625,73066, 7,73093,18967,73067, 7,73096, + 17,73097, 9731,73066, 7,73103,69513,73066, 7,73107,21315,73066, + 7,73111, 15,71229,18967,73115, 17,73117, 3,73114, 3,73115, +72567,73123, 17,73114, 265,71229, 9,73128, 8861,73131,60857,73129, + 9,73135,65869,73128, 375,73139, 11,71229,60857,73143, 9,73145, + 1275,73143, 9,73149, 9,73142,60857,71229, 7,73154, 59,73157, + 1325,73157, 331,73157, 265,73157, 7,73155, 375,73167, 265,73155, + 375,73171,12377,73154, 7,73175,18967,71229, 7,73179,25793,73181, +41019,73181, 8495,73181,46863,71229,20035,73188,58742,73188, 903,73188, +56555,73194,56555,73188, 903,73198,20035,71229,21183,73203, 7,73203, + 331,73207, 483,73202,46863,73202, 9,73202, 7,73202, 375,73217, +49713,71229,39085,73221, 136,73220, 1197,73220, 137,73220, 922,73220, + 1413,73220, 1011,73220, 6,73234, 483,73220, 903,73238, 903,73220, + 483,73242, 2,73220, 9,73246,44243,73247, 3,73220, 5,73253, + 6,73255, 9,73253, 2,73221,73257,73261,73229,73261,73258,73261, +29229,73261,72683,73261,73237,73261,73087,73261, 9,73261,73253,73276, +73253,73261, 9,73280, 3,73221,73225,73285,73249,73285,73227,73285, +73247,73285, 9,73293, 9,73220, 2,73296,73285,73299, 2,73297, +73253,73303, 17,71229,10161,73306,10159,73309,54339,73307,54331,73313, +49217,73307,49229,73317,54331,73306,54339,73321,49229,73306,49217,73325, +65037,73306,63823,73329,13755,73306, 9377,73333,27995,73307,27533,73337, +17573,73307,10001,73341, 7,73306, 3,73345, 7,73346,73307,73349, + 1,73346,73351,73353, 1,73345, 3,73356,21604,73345, 0,73306, + 3,73363, 0,73364,73307,73367, 6,73364,73369,73371, 6,73363, + 3,73374, 1274,73363, 586,73306, 3,73381, 1,73382, 1,73381, + 3,73386,21604,73381,60856,73306, 3,73393, 6,73394, 6,73393, + 3,73398, 1274,73393,10001,73306,63823,73405, 9377,73405,17573,73405, +27533,73306, 9377,73413,63823,73413,27995,73413, 9377,73307,13755,73421, +27533,73421,10001,73421,63823,73307,65037,73429,10001,73429,27533,73429, +60857,73307,63697,73437,63779,73437,73397,73437,73401,73437,73371,73437, +73377,73437, 7,73437,63709,73437,35893,73437,37897,73437,38299,73437, +73379,73437,73403,73437, 1275,73437,63679,73465,73363,73465,73393,73465, + 587,73307, 1971,73473, 3507,73473,73385,73473,73389,73473, 5231,73473, +73353,73473,73359,73473, 0,73473,21783,73473,23983,73473, 4625,73473, +10603,73473,73361,73473,73391,73473,21605,73473, 713,73503,73345,73503, +73381,73503, 1011,73306,18967,73511, 9617,73511, 1275,73306,72439,73517, +72447,73517, 3621,73517, 1991,73517, 1,73517,73473,73527,73489,73517, +60969,73517,62095,73517, 4907,73517,17711,73517,72457,73517,60857,73517, + 1325,73543,72435,73543,21605,73306,23713,73549,23709,73549,26865,73549, +26715,73549, 6,73549,73437,73559,73451,73549,10159,73549,10111,73549, +26719,73549,10113,73549,67425,73549,70753,73549,70471,73549,23715,73549, + 587,73549,26625,73581,10107,73581,23651,73581, 1011,73307, 9565,73589, + 2,73306,36729,73593,68447,73593,67547,73593,70731,73593, 2,73307, +36727,73603,68451,73603,37895,73603,36079,73603,35575,73603, 15,73306, +21605,71229,26867,73616,26865,73619, 1179,73617, 1177,73623, 987,73617, + 989,73627, 1177,73616, 1179,73631, 989,73616, 987,73635, 809,73616, + 713,73639, 117,73616, 25,73643, 1489,73617, 1471,73647, 147,73617, + 53,73651,26219,73617, 5,73616, 7,73657, 5,73658,73617,73661, + 2,73658,73663,73665, 2,73657, 7,73668, 1368,73657, 3,73616, + 7,73675, 3,73676,73617,73679, 4,73676,73681,73683, 4,73675, + 7,73686, 16,73675, 10,73616, 7,73693, 2,73694, 2,73693, + 7,73698, 1368,73693, 586,73616, 7,73705, 4,73706, 4,73705, + 7,73710, 16,73705,44243,73617,48466,73617, 340,73617, 1397,73617, + 53,73616, 713,73725, 305,73725, 25,73725, 147,73725, 1471,73616, + 25,73735, 305,73735, 713,73735, 1489,73735, 25,73617, 117,73745, + 253,73745, 1471,73745, 53,73745, 713,73617, 809,73755, 253,73755, + 53,73755, 1471,73755, 253,73616, 305,73765, 25,73765, 713,73765, + 305,73617, 253,73773, 1471,73773, 53,73773, 9,73617,46863,73780, + 587,73617, 9381,73785, 9439,73785,73709,73785,73713,73785,25519,73785, +73683,73785,73689,73785, 5,73785, 9383,73785,62077,73785,64317,73785, +64815,73785,73691,73785,73715,73785, 17,73785, 9377,73815,73675,73815, +73705,73815, 11,73617, 7979,73823, 8067,73823,73697,73823,73701,73823, +73603,73823,73665,73823,73671,73823, 3,73823, 7981,73823,39747,73823, +43027,73823,43837,73823,73673,73823,73703,73823, 1369,73823, 7971,73853, +73657,73853,73693,73853, 17,73616,23709,73861,23713,73861,10159,73861, +10111,73861, 2,73861,73823,73871,73839,73861,26865,73861,26715,73861, +10113,73861,26719,73861,67425,73861,70471,73861,70753,73861,23715,73861, + 587,73861,10107,73893,26625,73893,23651,73893, 1369,73616,72365,73901, +72373,73901, 9927,73901, 9891,73901, 4,73901,73785,73911,73801,73901, + 9895,73901,19687,73901,20843,73901,21127,73901,72383,73901, 11,73901, + 9875,73927,72361,73927,46863,73617, 9,73932, 6,73616,62905,73937, +68761,73937,70401,73937,67541,73937, 7,73616, 6,73617,62883,73949, +68767,73949,64267,73949,62235,73949,38881,73949,73437,73949,73947,73949, +72787,73949, 7,73617,25793,73967,41019,73967, 8495,73967,73937,73967, +72793,73967, 265,73966, 265,73617, 6,73981, 7,73981, 6,73980, +73985,73987, 7,73980,73983,73991, 903,71229,64148,73995,57522,73994, +58782,73994,58362,73994,52501,73995,52692,73994,46863,73994,56555,74008, + 9,73994,56555,74012, 483,73994,56555,74016,49713,74016,49713,73994, + 483,74022, 15,73995, 483,74027, 7,73994, 59,74031, 1325,74031, + 331,74031, 7,73995,60857,74038,59875,74039, 375,74039, 9805,74039, +53391,74039,60857,73995, 6,74051, 7,74051, 6,74050,74055,74057, + 7,74050,74053,74061,56555,73994, 7,74065,46863,74064, 483,74064, + 9,74064,58742,71229, 7,74075, 9,74074,46863,74074, 483,74074, +56555,71229,43502,74085,21036,74085, 6,74089, 6,74088,43503,74085, + 6,74095, 6,74094,21037,74085,59634,74085,59635,74085, 6,74105, + 6,74104,21104,74085,59645,74085,43838,74085,21548,74085,21526,74085, + 5,74118,59644,74085,19624,74085,18967,74085, 5,74126, 6,74128, + 6,74129, 6,74126, 5,74134, 6,74127, 14,74126,38305,74085, + 2,74142,59619,74085, 2,74146, 5,74085,59961,74151, 6,74151, +21526,74150, 6,74150,18967,74158,18967,74150, 6,74162, 6,74163, + 2,74085,59619,74168,38305,74168, 14,74085,18967,74174,21527,74085, +74155,74179, 9,74179,19625,74085,21549,74085,43839,74085,21105,74085, +47640,74084, 922,74084, 918,74084, 1505,74084, 668,74084, 1419,74084, + 483,74084, 903,74204, 11,74085, 9,74209, 15,74085,74139,74213, +46863,74084, 903,74216, 9,74084, 11,74221, 903,74220, 587,74220, + 587,74084, 9,74228, 6,74084,74191,74233,74189,74233,74187,74233, +74113,74233,74087,74233,74101,74233,74103,74233,74171,74233,74169,74233, +59619,74251,74149,74233,72981,74233,74185,74233,74145,74233,74173,74233, +74213,74233,59381,74233, 7,74084,74093,74269,74099,74269,74123,74269, +74109,74269,74159,74269,74089,74269,74095,74269,74105,74269,74151,74269, +74111,74269,74115,74269,74117,74269,74157,74269,74121,74269,21105,74269, +21549,74269,65513,74269,65791,74269,65407,74269,74125,74269,19625,74269, +65171,74269,74175,74269,74131,74269,74129,74269,74137,74269,74177,74269, +74161,74269,74165,74269,74163,74269,74141,74269,59619,74269, 15,74269, + 6,74085,21036,74336,74269,74339,43503,74336,74269,74343,59635,74336, +74269,74347, 5,74336,74269,74351,18967,74350,74269,74355,18967,74336, + 5,74358,74269,74361, 7,74085,74091,74365,74097,74365,74107,74365, +74155,74365,21183,74365,65493,74365,74133,74365,74167,74365, 9,74365, + 959,74365, 47,74365, 759,74365, 903,74084, 7,74391, 483,74390, + 9,74390,46863,74390, 9,71229,20035,74400,58742,74400,58930,74400, +19917,74400, 903,74400,56555,74410, 265,74400, 8861,74415, 587,74400, +56555,74418,56555,74400, 11,74423, 903,74422, 587,74422, 265,74401, + 8495,74431, 11,74431, 3,74400,70177,74437,70176,74436,74439,74441, +70176,74437,70177,74436,74445,74447, 1141,74437, 1447,74437, 33,74437, + 11,74437,37409,74437, 3,74401, 113,74461, 11,74400,21522,71229, + 7,74467,63452,71229, 7,74471,13673,71229, 7,74475,17049,71229, + 3,74478,48643,74481, 2,74479, 3,74479,48637,74487,49581,74487, +47813,74487,21315,71229, 165,74494, 1011,74494, 7,74499,16163,71229, + 2,74503,72683,74505,73087,74505,16171,74505, 375,74503,64669,71229, + 7,74515, 265,74517,12377,71229, 256,74521,17455,74520, 7,74520, + 375,74527, 7,74521, 241,74530, 331,74531,60857,74520, 7,74537, + 241,74521, 7,74540,10385,71229, 375,74545,70899,71229, 7,74549, +18391,71229, 7,74553,18617,71229, 7,74557,12705,71229, 7,74561, +70573,71229,69275,74564, 7,74567, 3,71228,74332,74571,72750,74571, +71253,74571,72985,74571,72147,74571,71783,74571,71577,74571,67600,74571, +45491,74571,42800,74571,59710,74571,74365,74593,74269,74571,59619,74596, +72735,74571,59619,74600,59694,74571,60563,74571, 629,74571, 101,74571, + 705,74571, 299,74571, 128,74571, 828,74571, 394,74571,48122,74571, +70327,74571,66930,74571, 9606,74571,10374,74571, 113,74571, 9,74632, +74465,74571,73153,74571,73031,74571,73057,74571,73131,74571,71939,74571, +72733,74571,74415,74571,72145,74571,59381,74653,42134,74571,10291,74571, + 17,74658,51992,74571, 375,74571, 241,74664,65869,74664,49713,74664, +60796,74571,74213,74673, 256,74571, 803,74571, 483,74678, 483,74571, + 803,74682,31103,74571,65869,74686, 6,74571,59619,74690,74365,74693, +56555,74690,74213,74697, 7,74571, 241,74700, 7,74570,57811,74705, +59933,74705,59743,74705, 9839,74705, 9579,74705,39619,74705,51421,74705, + 4287,74705, 4303,74705, 8093,74705, 3837,74705,53487,74705,42113,74705, +42435,74705,53703,74705,42613,74571, 17,74736,46863,74571,12377,74740, + 9,74571, 113,74744,49713,74571, 375,74748, 17,74571,42613,74752, +59619,74752,10291,74752, 9565,74752,41941,74752, 9565,74571, 7,74765, + 17,74764,65869,74571,60796,74771,60797,74771,31103,74770,60797,74770, +74773,74779,60796,74770,74775,74783, 375,74770, 241,74571, 375,74788, + 7,74788,56555,74571, 5,74795,74269,74797,72735,74797, 6,74797, +74365,74803, 6,74796,74269,74807,72735,74807, 14,74795,72735,74813, +74269,74813, 15,74795, 6,74795, 5,74820,74269,74823,72735,74823, + 7,74795,74803,74829, 6,74794,74213,74833,74819,74833, 7,74794, +74807,74839,74823,74839,71229,74838,74807,74845,74823,74845,74797,74845, +74813,74845,74813,74839,12377,74571,46863,74856,41941,74571, 7,74861, + 17,74860,59619,74571,74269,74866,72735,74866, 6,74866,74365,74873, + 6,74867,74269,74877,72735,74877, 7,74867, 17,74866, 2,71228, +71233,74887,56555,74887,72129,74887,72127,74887,59619,74895,71235,74887, +72790,74887,71627,74887,74153,74887,74155,74887,74365,74906,74372,74887, +74158,74887,74269,74913,74279,74887,74350,74887,74269,74919,74353,74887, +74266,74887,74150,74887, 6,74927,74365,74929, 6,74926,74269,74933, +74269,74927,74287,74887,74336,74887, 5,74940,74269,74943,60261,74887, +71629,74887,63737,74887,37179,74887, 9535,74887,63743,74887, 9537,74887, +37185,74887,74264,74887,74174,74887,74269,74965,74315,74887,73073,74887, +37887,74887,37205,74887,37235,74887,74085,74887, 5,74978, 6,74981, +74365,74983, 6,74980,74269,74987,74269,74981, 6,74978, 5,74992, +74269,74995, 6,74979,74213,74999, 14,74978,74269,75003, 5,74887, +74336,75006,74269,75009,74085,75006, 6,75013,74365,75015, 6,75012, +74269,75019,74269,75013,74085,75007,74233,75025, 6,75006,72735,75029, +74085,75028,74269,75033,74269,75029,74085,75029,74233,75039, 7,75006, +71853,75043,72049,75043,72177,75043,73901,75043, 6,75007,74365,75053, + 7,75007,72131,74887,72753,74887,74335,74887, 9563,74887,74382,74887, + 10,74887,72177,75069,72049,75069,71853,75069,73901,75069, 14,74887, +74085,75079,74233,75081,74085,75078,74269,75085,72735,75079,74269,75079, +72191,74887,72069,74887,73927,74887,71881,74887,64759,74887,63763,74887, +63813,74887, 9559,74887,74213,74887,74233,75108,74365,74887,74155,75112, + 9,75112,43123,74887,42606,74887,53318,74887, 9362,74887,27218,74887, +28189,74887,74233,74887,59381,75130,74213,75130,72789,74887,59381,75136, +64289,74887,26182,74887,28991,74887, 8804,74887,54289,74887,53802,74887, + 8150,74887, 1290,74887,63679,75155, 9533,75155,37145,75155, 525,74887, + 9533,75163,63679,75163,37145,75163, 922,74887, 1413,74887,73615,74887, +73127,74887,73009,74887,73035,74887,73081,74887,71943,74887,72729,74887, +73511,74887,41762,74887, 7971,74887, 9,75192,53517,74887, 9,75196, + 483,74887, 903,75200, 9195,74887, 9,75204, 1275,74887, 17,75208, + 9533,75211,37145,75211,63679,75211,27021,74887, 9,75218, 7,74887, + 2,75223,60857,75225,71229,75227, 5,75222,71853,75231,72049,75231, +72177,75231,73901,75231,75229,75231, 2,75222,73616,75243,75231,75245, +21605,75243,71229,75248,75231,75251,71229,75243,21605,75254,75231,75257, + 6,74887,74150,75260,74269,75263,74085,75260, 5,75266,74269,75269, +56555,75261,71229,75272,74085,75261,74155,75277, 9,75277, 5,75260, +72735,75283,74085,75282,74269,75287,74269,75283,74085,75283,74233,75293, +75275,75283, 11,75261,21604,75299,21604,75298,21605,75299,75303,75305, +21605,75298,75301,75309,52915,74887, 9,75312, 6,74886,23717,75317, +24522,75317,24447,75317,23939,75322,23939,75317,24447,75326,22956,75317, +23651,75331, 17,75317,21605,75335,24447,75337,21605,75334,23651,75341, +21605,75317, 17,75345,23939,75347, 17,75344,23651,75351, 7,74886, +27021,75355,24095,75355, 9195,75355,52915,75355,21605,75355, 9,75365, +67297,75355,26907,75355, 9755,75355, 9317,75355, 3833,75355,23651,75355, +51435,75355,27323,75355, 4321,75355,69729,75355,53215,75355, 4295,75355, +70097,75355,53941,75355,10171,75355, 5045,75355,11277,75355,42155,74887, + 9,75402, 19,74887, 12,74887, 11,74887, 9,75410, 9,74887, +74365,75414,42155,75414,27021,75414,52915,75414, 9195,75414,25793,75414, +53517,75414, 7971,75414, 8495,75414,41019,75414, 11,75414, 17,74887, + 1275,75438, 9533,75441,37145,75441,63679,75441, 8495,74887, 9,75448, + 903,74887, 483,75452,21605,74887,26219,75457,42810,75456,75056,75456, +75057,75457,75463,75465,75053,75457,42810,75457,75057,75456,75056,75457, +75473,75475,42811,75456,75471,75479,42811,75457,75461,75483,44243,75457, + 375,75457, 6,75457, 7,75457,25793,75493, 6,75456,75493,75497, + 7,75456,75491,75501,41019,74887, 9,75504,59381,74887,72789,75508, +74233,75508,25793,74887, 9,75514,26625,74887, 6,75518, 7,75518, + 6,75519,75523,75525, 7,75519,75521,75529, 9731,74887, 6,75532, + 7,75532, 6,75533,75537,75539, 7,75533,75535,75543,69513,74887, + 6,75546, 7,75546, 6,75547,75551,75553, 7,75547,75549,75557, + 7,71228,73276,75561,27306,75561,73261,75561, 9,75566,24095,75561, +52603,75561,53910,75561,55537,75561,20053,75561,20831,75561,19783,75561, +64289,75561,27021,75561,25793,75586, 265,75587, 959,75561,60857,75593, +58775,75561, 5045,75561,11277,75561, 4551,75561,70136,75561,10196,75561, + 273,75561, 1477,75561, 281,75561, 948,75561, 1516,75561, 922,75561, + 1413,75561,52915,75561, 265,75623, 47,75561,60857,75627,25793,75561, +27021,75630, 9195,75561, 265,75635, 759,75561,60857,75639, 89,75561, + 5019,75643,60857,75643, 1505,75561, 1011,75648, 587,75561,60857,75653, +70573,75653, 1369,75561,43503,75659, 483,75561, 903,75662, 165,75561, + 903,75666, 1141,75561,18967,75671, 1447,75561,18967,75675, 2,75561, +61780,75679, 9,75679,60857,75682,54295,75679,70085,75679,53925,75679, +10155,75679,11529,75679,60857,75679, 9,75696, 3,75561,73221,75701, + 9,75703, 8063,75701,53667,75701,42361,75701, 33,75561,18967,75713, + 2,75560,27021,75717,24095,75717, 9195,75717,52915,75717,21605,75717, + 9,75727,67297,75717,23651,75717,51435,75717,27323,75717, 4321,75717, + 9755,75717,26907,75717, 9317,75717, 3833,75717,69729,75717,53941,75717, +10171,75717,70097,75717,53215,75717, 4295,75717, 5045,75717,11277,75717, + 3,75560,57811,75765,59933,75765,59743,75765,39619,75765,51421,75765, + 4287,75765, 4303,75765, 9839,75765, 9579,75765, 8093,75765, 3837,75765, +42113,75765,53487,75765,42435,75765,53703,75765, 265,75561,53813,75796, +69877,75796,10107,75796, 1011,75561, 1505,75804,54597,75805,21605,75561, + 9,75811, 145,75811, 903,75561, 165,75816, 483,75816,54295,75817, + 9,75561,73261,75824,69877,75561, 265,75828,10107,75561, 265,75832, +53813,75561, 265,75836, 6,71228,63091,75841,63099,75841,63117,75841, +25451,75841,53597,75841,42283,75841,61289,75841,61281,75841,62345,75841, + 0,75841, 3,75860,71891,75863,72017,75863,72197,75863,73517,75863, + 3,75861,62359,75841,60856,75841,72197,75877,72017,75877,71891,75877, +73517,75877,72211,75841,72037,75841,73543,75841,71919,75841,19739,75841, +61649,75841,61263,75841,61323,75841,20763,75841, 1011,75903, 1010,75902, +75905,75907, 1010,75903, 1011,75902,75911,75913,25380,75841, 7971,75917, +53517,75917,42155,75917,63015,75841,53517,75925, 7971,75925,42155,75925, + 1369,75841,21605,75932,53517,75935,42155,75935, 7971,75935, 3,75841, + 6,75943, 11,75945,71229,75947, 0,75942,71891,75951,72017,75951, +72197,75951,73517,75951,75949,75951, 6,75942,73306,75963,75951,75965, + 17,75963,71229,75968,75951,75971,71229,75963, 17,75974,75951,75977, + 2,75841,60857,75981, 16,75983, 16,75982, 17,75983,75987,75989, + 17,75982,75985,75993, 2,75840,23717,75997,24522,75997,23939,75997, +24447,76002,24447,75997,23939,76006,22956,75997,23651,76011,21605,75997, + 17,76015,23939,76017, 17,76014,23651,76021, 17,75997,21605,76025, +24447,76027,21605,76024,23651,76031,21605,75841, 1369,76034,53517,76037, +42155,76037, 7971,76037, 17,75841,37844,76044,75872,76044,75873,76045, +76049,76051,37844,76045,75873,76044,75872,76045,76057,76059,37845,76044, +76055,76063,37845,76045,76047,76067, 7,71229,74797,76071,74829,76073, + 264,76071, 5,76071, 3,76078, 3,76079, 5,76070,75925,76085, +63014,76085,75841,76089,75917,76085,25381,76085,75841,76095,75935,76085, +76037,76085, 483,76085,60857,76102,75841,76105,60857,76085, 483,76108, +75841,76111,32805,76070,34655,76115,33773,76115,34447,76115,33221,76115, +32805,76071,34003,76125,33319,76125,34447,76071,35031,76131,65407,76071, +65335,76135,65076,76070,65513,76139,65791,76139,65407,76139,65171,76139, +65076,76071,65625,76149,65267,76149,20035,76070, 375,76155,74794,76071, +59381,76159,74819,76159,74571,76071,56555,76165,59619,76167,56555,76164, +59381,76171,12377,76070, 375,76175,49204,76071, 1369,76071, 241,76181, +56555,76071,74571,76184,59381,76187,59381,76185,74571,76191,49205,76071, +65869,76195, 1275,76071, 903,76199,60857,76070, 59,76203, 1325,76203, + 331,76203, 265,76203, 903,76070, 59,76213, 1325,76213, 331,76213, +56555,76070,74877,76221,74866,76221,65513,76221,65791,76221,65407,76221, +74807,76221,74823,76221,74797,76221,21105,76221,75029,76221,75283,76221, +21549,76221,19625,76221,65171,76221,75079,76221,74813,76221,59619,76221, +74571,76254,74571,76221,59619,76258, 15,76221,74887,76263,21605,76070, +73949,76267, 265,76071,63823,76271,63679,76271,72619,76271, 9377,76271, + 9533,76271,37145,76271, 2,76070,76077,76285,76081,76285,55839,76285, +54159,76285,54497,76285,51803,76285,73123,76285,76179,76285,65869,76301, + 265,76285, 3,76070,76196,76307,67988,76307, 33,76307, 1447,76307, + 1141,76307,76271,76307,76195,76307,65869,76320, 9,76307,46863,76307, +65869,76326,65869,76307,76195,76330,46863,76330, 2,76071,76310,76337, +76328,76337,76334,76337,76083,76337, 10,76337, 5,76337, 7,76348, + 7,76337, 5,76352,55803,76337,54225,76337,54477,76337,51593,76337, +67988,76337,76307,76364,64345,76337,76324,76337,73121,76337,72713,76337, +76326,76337,65869,76376, 9,76337,76307,76380,60857,76337,71229,76385, +76347,76387,76351,76387,76355,76387, 903,76337,46863,76337,76330,76396, +65869,76396,76307,76400,76307,76396,65869,76404,76330,76337,46863,76408, +65869,76337,76326,76412,46863,76412,76307,76416,76307,76412,46863,76420, +76307,76337,67988,76424, 9,76424,46863,76424,65869,76430,65869,76424, +46863,76434, 3,76071, 5,76438,76285,76441, 113,76439,46863,76438, +76285,76447,65869,76449,76285,76439,67988,76453, 9,76453,46863,76453, +65869,76458,65869,76453,46863,76462, 15,76071,74485,76467,73261,76467, +46863,76071, 3,76472,76285,76475,65869,76477, 3,76473,76412,76481, +65869,76481,76337,76484,76337,76481,65869,76488,65869,76070, 375,76493, +70176,71229, 375,76497, 3,71229,70554,76500, 5,76501, 1,76505, +71229,76507, 2,76509,59961,76505,74887,76513, 6,76505,76511,76517, +73261,76517, 0,76500,75163,76523, 524,76523,74887,76527,75155,76523, + 1291,76523,74887,76533,75211,76523,75441,76523, 483,76523, 11,76540, +74887,76543, 11,76523, 483,76546,74887,76549, 33,76501, 9,76553, + 8147,76501,42315,76501,53777,76501,70555,76500,70176,76500, 9,76565, +43848,76501,70177,76501,76563,76571,76566,76571, 9,76571,76565,76576, +76565,76571, 9,76580, 1141,76501, 9,76585, 1447,76501, 9,76589, +69849,76500,68155,76593,17049,76500,48643,76597, 1369,76501, 803,76601, + 15,76500,76337,76605,37409,76501, 9,76609, 9,76500,70177,76613, +76565,76615,70177,76612, 1141,76613, 1447,76613, 33,76613, 11,76613, +37409,76613, 11,76501, 9,76631, 6,76500, 7,76500, 33,76637, + 1447,76637, 1141,76637,76271,76637, 6,76501,60856,76647, 0,76647, + 3,76650, 3,76647, 0,76654, 11,76647,71229,76659,76649,76661, +76653,76661,76657,76661, 7,76501,43503,76668,70177,76500, 9,76672, +70176,76501,76619,76677,76503,76677,76675,76677,76673,76677, 9,76685, +43503,76501, 7,76688, 17,76689, 1011,76501, 375,76695, 7,76695, +18967,76501, 4,76700, 5,76700,76071,76705, 6,76704, 6,76705, +76070,76701,76709,76713,76705,76713, 14,76700,76713,76719,71229,76701, + 7,76723,76711,76725, 7,76722,76709,76729,76705,76729,76719,76729, + 15,76723,76071,76701,76703,76739, 15,76700,76739,76743, 6,76700, + 5,76746,76729,76749,76713,76749, 7,76700,76707,76755,76711,76755, + 6,76701,76743,76761,76737,76761, 7,76701,76749,76767,76709,76767, +71229,76766,76749,76773,76709,76773,76705,76773,76719,76773,76719,76767, +65869,76500,68155,76785,68109,76785, 256,76785, 7,76785, 241,76792, + 241,76785, 7,76796,70572,71229,68155,76801,68109,76801, 256,76801, + 7,76801, 241,76808, 241,76801, 7,76812, 2,71229, 0,76817, + 4,76818, 0,76816, 2,76823, 5,76816,62284,76817,62186,76817, +64248,76817,62504,76817,64205,76817,61690,76817,52501,76817,70021,76817, +17496,76817,11179,76817,10187,76817,53967,76817,17455,76817, 9,76852, +16065,76817, 16,76857, 16,76856, 17,76857,76861,76863, 17,76856, +76859,76867,70802,76817, 340,76817,60857,76872,64148,76817, 265,76876, + 9,76817,17455,76880, 375,76817,60857,76884, 587,76817, 17,76889, +37145,76891, 9533,76891,76523,76891,76821,76891,63679,76891,44243,76817, +60857,76902,49713,76903, 17,76816,36729,76909,68447,76909,67547,76909, +70731,76909,49713,76816,44243,76919,54295,76817, 9,76923, 6,76816, +66737,76927,36661,76927,62741,76927,68381,76927,68727,76927, 7,76816, +54497,76939,55839,76939,54159,76939,51803,76939,73123,76939, 265,76939, + 7,76817,62494,76952,70573,76952, 265,76952,60857,76958,60857,76952, + 265,76962, 265,76953,60857,76967,62494,76817, 7,76970,43215,76817, + 9,76975, 265,76817,64148,76978, 483,76979, 7,76978,60857,76984, +60857,76978, 7,76988,60857,76817,42810,76993,42810,76992,42811,76993, +76997,76999, 375,76992, 340,76992,42811,76992,76995,77007,44243,76992, + 6,76992, 7,76992, 265,77014, 6,76993,77015,77019, 7,76993, +77013,77023, 265,76993, 7,77027, 265,76992, 7,77030,65869,76817, +69275,77035, 7,77037,70573,76817, 6,77041, 7,77041, 6,77040, +77045,77047, 7,77040,77043,77051,65869,76816,69716,77055, 7,77055, +69513,77058,69513,77055, 7,77062,70922,71229,69716,77067, 7,77067, +69513,77070,69513,77067, 7,77074, 6,71229,74890,77079,59619,77081, +38056,77078, 5,77084,76825,77079,76827,77089,55814,77078,55815,77079, +77093,77095,55815,77078,55814,77079,77099,77101, 1346,77078, 1346,77079, + 1347,77078,77107,77109,17126,77079,17125,77079, 2,77114,17127,77079, + 9565,77119,74169,77079,59619,77123,60621,77079,59619,77127,38056,77079, +38057,77078,44195,77133,58303,77079, 1010,77079, 1010,77078,77119,77141, +35187,77079, 2,77144, 0,77079, 4,77149, 2,77151,71229,77152, +71229,77151, 2,77156, 2,77157,76816,77151, 1011,77149,71229,77165, + 5,77079, 1,77168, 5,77078,38056,77173,77133,77175, 2,77172, +58302,77079,77141,77181, 9565,77181,59619,77181,20501,77079, 1011,77189, + 1010,77188,77191,77193, 1010,77189, 1011,77188,77197,77199, 567,77079, +21604,77203,21604,77202,21605,77203,77207,77209,21605,77202,77205,77213, + 1347,77079,77105,77217,74887,77079,56555,77220,59619,77223,56555,77221, +59381,77227, 9875,77079, 1011,77231, 1010,77230,77233,77235, 1010,77231, + 1011,77230,77239,77241,56555,77079,74887,77244,59619,77247,59619,77245, +74887,77251, 1011,77244, 9565,77255,77141,77255,59619,77255, 1011,77245, + 9617,77263,77139,77263,74887,77263, 587,77079,21605,77271,42155,77273, +53517,77273,76085,77273,77171,77273, 7971,77273, 1011,77078,77113,77285, +77117,77285,74505,77285,77131,77285,77137,77285,77147,77285,73261,77285, +38057,77079,77179,77301,77087,77301,41941,77301,76827,77301, 9565,77301, +59619,77301,77141,77301,56555,77078,72981,77317,75508,77317,74887,77317, +59381,77322,59381,77317,74887,77326,21605,77078,62905,77331,68761,77331, +67541,77331,70401,77331,73967,77331, 1011,77079,56555,77343,59381,77345, +56555,77342,59619,77349,77141,77349, 9565,77349, 2,77078,66737,77357, + 5,77356,77301,77361,62741,77357,36661,77357,68727,77357,68381,77357, + 3,77078,77161,77373, 2,77079,17125,77376,77285,77379,35187,77376, +77285,77383, 3,77079,77155,77387,77159,77387,77163,77387,77167,77387, + 11,77079,37844,77397,37844,77396,37845,77397,77401,77403,37845,77396, +77399,77407,65869,71229, 1530,77410, 842,77410,48535,77410, 669,77410, + 1419,77410,49514,77410, 136,77410,49515,77410, 137,77410, 843,77411, +77415,77431, 1531,77411,77413,77435, 843,77410, 1530,77411, 1531,77410, +77441,77443, 842,77411,77439,77447, 1418,77410,48534,77410, 668,77410, + 587,77410,46863,77456, 9,77456, 1369,77410, 15,77462, 9,77410, + 2,77467, 2,77466, 587,77466, 265,77410, 375,77475, 15,77410, + 1369,77478,46863,77410, 2,77483, 2,77482, 587,77482, 7,77411, + 59,77491, 1325,77491, 331,77491, 265,77491, 6,77410,77491,77501, + 2,77503, 3,77503, 2,77502,77507,77509, 3,77502,77505,77513, + 3,77411,67577,77517,77425,77517,77423,77517,68139,77517,70979,77517, +77455,77517,77453,77517,77421,77517,77471,77517,77487,77517,77461,77517, +77459,77517,77473,77517,77489,77517, 7,77517, 1011,77517, 7,77549, + 2,77410, 9,77552,77517,77555,46863,77552,77517,77559,69716,77553, +77546,77553, 7,77553,69513,77566,77517,77566,69513,77553, 7,77572, +77517,77553, 6,77577, 7,77577, 6,77576,77581,77583, 7,77576, +77579,77587, 2,77411,76517,77591,29229,77591,72789,77591,77429,77591, +77427,77591,72683,77591,74233,77591,77317,77591,77451,77591,77419,77591, +77417,77591,77285,77591,73087,77591,77465,77591,77481,77591, 7,77591, +70573,77591, 7,77625, 3,77410, 4,77628,77591,77631, 1,77628, + 5,77628, 0,77628,77591,77639, 5,77629, 6,77643,77591,77645, +68155,77629,77469,77629,77485,77629,68109,77629,77623,77629,77631,77657, +77639,77657, 256,77629,77622,77629,77635,77665,77637,77665, 6,77629, +77632,77671,77640,77671,77631,77671,77591,77676,77639,77671,77591,77680, +77591,77671,77631,77684,77639,77684, 7,77629,77635,77691,77591,77693, +77637,77691,77591,77697, 241,77690,77591,77690,77635,77703,77637,77703, + 241,77629, 7,77708,77591,77629, 6,77713, 7,77713, 6,77712, +77717,77719, 7,77712,77635,77723,77637,77723,77715,77723, 6,77411, + 7,77410, 375,77733,77731,77733, 2,77737, 3,77737, 2,77736, +77741,77743, 3,77736,77739,77747, 1, 6,18363,77751, 4389,77751, +18362,77751, 4388,77751,70572,77750,70667,77761,70849,77761,70803,77761, +56555,77767,70883,77761, 272,77751, 8997,77773, 4425,77773, 8452,77751, + 251,77779, 8841,77779, 8453,77751, 273,77751, 4139,77751, 8429,77751, + 4138,77751, 8428,77751,70879,77751,70797,77797,18257,77751, 465,77801, + 445,77751,17827,77805, 4371,77751, 265,77808,18339,77751, 265,77812, +64953,77751, 375,77817,14888,77751, 375,77821,13569,77751, 375,77825, + 4137,77751, 265,77828, 8401,77751, 265,77832, 413,77750,77835,77837, +77831,77837,77795,77837,77793,77837,77815,77837,77811,77837,77757,77837, +77759,77837, 241,77837, 1217,77750, 412,77750, 8401,77859, 4137,77859, +18339,77859, 4371,77859,77791,77859,77789,77859,77753,77859,77755,77859, + 1216,77750,40461,77751, 9,77879,59573,77751, 375,77883,69467,77751, + 375,77887, 7047,77751, 412,77891, 413,77890,77893,77895, 413,77891, + 412,77890,77899,77901,12615,77751, 375,77905,45880,77751,46275,77909, +33390,77751,34003,77913,77462,77751,77591,77917,65232,77751,65625,77921, + 923,77751,16481,77925,16783,77925,18797,77925,25367,77751, 9,77933, +68975,77751, 375,77937,62984,77751, 9,77941, 1412,77751,17827,77945, + 412,77751,77837,77949, 1216,77751, 9137,77751, 375,77955, 253,77751, + 9136,77959, 9137,77959,17826,77959, 1,77959, 413,77966,17827,77959, + 413,77959, 1,77972, 265,77958, 8997,77977, 4425,77977,77963,77977, +77965,77977,77969,77977,77975,77977, 265,77959, 8997,77990, 8997,77959, + 265,77994, 265,77995,12377,77959, 8337,77751, 266,78003, 267,78003, + 464,78003, 6,78003, 413,78010, 465,78003, 413,78003, 6,78016, + 265,78002, 251,78021, 8841,78021,78007,78021,78009,78021,78013,78021, +78019,78021, 265,78003, 251,78034, 251,78003, 265,78038, 265,78039, + 375,78003,38057,77751, 265,77751,18339,78048,77837,78051, 4371,78048, +77837,78055, 253,78048, 8997,78059, 4425,78059, 8337,78048, 251,78065, + 8841,78065,77837,78049, 8401,78071, 4137,78071,18339,78071, 4371,78071, + 4137,78048,77837,78081, 8401,78048,77837,78085, 241,78049, 8401,78089, + 4137,78089, 1369,78048,17827,78095, 241,78048, 1011,77751,56555,78101, +60369,78103, 241,77751, 413,78107,14315,78106, 375,78111,57522,78107, +67028,78107, 265,78107,60857,78106, 9,78121,56555,78107, 9,78124, +65869,78107, 483,78128, 483,78107,65869,78132, 9,78107,56555,78136, + 265,78106, 8685,77751, 375,78143, 264,77750,78066,78147,78060,78147, +78091,78147,78093,78147,18339,78147, 4371,78147,17911,78147,13433,78147, +13411,78147,13635,78147,77978,78147,78022,78147,77999,78147,78043,78147, +77774,78147,77780,78147,64733,78147,64955,78147,78021,78147, 251,78184, +77977,78147, 8997,78188,78065,78147, 251,78192,78059,78147, 8997,78196, +77779,78147, 251,78200,77773,78147, 8997,78204, 9139,78147, 9123,78147, + 9065,78147,14935,78147,64919,78147,59463,78147,69357,78147,59533,78147, +69427,78147,59587,78147,69481,78147,78088,78147, 8401,78231, 4137,78231, + 8997,78147,78059,78236,77977,78236,77773,78236, 251,78147,78065,78244, +78021,78244,77779,78244,27979,78147,26293,78147,41503,78147, 8685,78147, +78141,78147,77949,78147,78099,78147,78049,78147, 8337,78267, 251,78269, + 253,78267, 8997,78273, 241,78266, 8401,78277, 4137,78277, 241,78267, + 1369,78147, 241,78147, 6,78287,78065,78289,78021,78289,77779,78289, + 1,78287,78059,78297,77977,78297,77773,78297,78049,78286, 8401,78305, + 4137,78305,16381,78147,19513,78147,35843,78147,13325,78147,70407,78147, +69063,78147,67565,78147, 341,78147,64669,78325, 8997,78325,59381,78325, +69275,78325,65869,78325, 241,78325, 471,78147, 367,78147, 357,78147, + 8401,78147, 4137,78147,37019,78147, 4063,78147,20219,78147, 3863,78147, +20697,78147,37377,78147, 9955,78147,10273,78147,14625,78147, 4569,78147, + 265,77750,77785,78369, 251,78371,77787,78369, 8997,78375, 253,78369, + 8997,78379, 8337,78369, 251,78383, 241,78368, 8401,78387, 4137,78387, +77791,78387,77789,78387, 241,78369, 241,77750, 8428,78399,78147,78401, + 4138,78399,78147,78405, 4137,78399, 265,78409, 265,78408,78147,78413, + 8401,78399, 265,78417, 265,78416,78147,78421, 265,78398, 8401,78425, + 4137,78425,77791,78425,77789,78425, 265,78399, 8401,78434,78147,78437, + 4137,78434,78147,78441,78147,78435, 8401,78445, 4137,78445, 240,77751, + 265,78451, 1010,77751,28337,78455, 264,77751,78426,78459,78428,78459, +78419,78459,78411,78459,78388,78459,78390,78459,77860,78459,77862,78459, +77864,78459,77866,78459,18339,78459,77859,78480, 4371,78459,77859,78484, +78381,78459,78385,78459,13381,78459,77961,78459,78005,78459,77993,78459, +78037,78459,77997,78459,78041,78459,78015,78459,77971,78459,77807,78459, +77803,78459,64746,78459,78382,78459, 251,78517,78378,78459, 8997,78521, +78035,78459, 251,78525,77991,78459, 8997,78529, 9072,78459, 9105,78459, +59470,78459,69364,78459,59521,78459,69415,78459,64931,78459, 413,78459, +77859,78459, 8401,78548, 4137,78548,18339,78548, 4371,78548,78425,78459, + 8401,78558, 4137,78558,78387,78459, 8401,78564, 4137,78564, 8401,78459, +78425,78570,78387,78570,77859,78570, 4137,78459,78425,78578,78387,78578, +77859,78578, 8337,78459,78369,78586, 251,78589, 253,78459,78369,78592, + 8997,78595,78396,78459,78119,78459,78369,78459, 8337,78602, 251,78605, + 253,78602, 8997,78609, 241,78603, 8401,78613, 4137,78613, 241,78602, +69275,78459, 375,78620,59381,78459, 375,78624, 8997,78459, 375,78628, +64669,78459, 375,78632,70327,78459,65869,78459, 375,78638,66930,78459, + 375,78459,64669,78644, 8997,78644,59381,78644,69275,78644,65869,78644, + 241,78644, 241,78459,78369,78658, 375,78658, 394,78459, 299,78459, + 1217,77751, 413,77751,77859,78671,78369,78671, 375,78671,14315,77751, + 241,78678, 375,78681, 375,78679, 341,77751,12377,78687, 257,77751, + 375,78691,70572,77751,70693,78695,70800,78695,70797,78695,56555,78700, +56555,78695,70797,78704, 1346,77751, 9,78709, 1061,77751, 9,78713, + 525,77751, 9,78717,64396,77751, 9,78721,38056,77751,29229,78725, + 9,78725, 137,77751,56555,78731, 5,78733, 4,78732,78735,78737, + 4,78733, 5,78732,78741,78743,20103,77751, 375,78747, 1471,77751, +12377,78751,36773,77751, 375,78755, 53,77751,12377,78759,45639,77751, + 1369,78762,46275,78765,32805,77751, 17,78768,34003,78771,77410,77751, + 1369,78774,77591,78777,65076,77751, 17,78780,65625,78783, 1275,77751, + 5,78786, 9,78789, 5,78787, 17,78793,35187,77751, 1196,78797, + 6,78797, 1011,78800, 137,78797, 136,78797, 1197,78797, 1413,78797, + 922,78797, 1011,78797, 6,78814, 903,78797, 483,78818, 483,78797, + 903,78822, 2,78796,78805,78827,29229,78827,78799,78827,78817,78827, +78803,78827, 9,78827, 3,78796,78807,78841,78809,78841, 2,78797, +78841,78847, 9,78849, 9,78846,78841,78853, 3,78797, 5,78857, + 6,78859,78827,78861,78838,78857, 9,78857,78827,78866,78827,78857, + 9,78870, 9,78797, 2,78875,78857,78877, 3,78875, 2,78874, +78841,78883, 3,78874, 17,77751,65076,78889,65407,78891,32805,78889, +34447,78895,20035,78889,58742,78889,65076,78888,65625,78903,32805,78888, +34003,78907, 903,78889,56555,78910,56555,78889, 903,78914,65869,78889, + 2,78919, 3,78919, 2,78918, 3,78918,18967,77751, 295,78929, + 1108,78929, 1011,78929, 9,78934, 9,78929, 1011,78938, 483,78929, +18967,78943, 5,78944, 5,78945,21036,78943,21037,78943, 4,78943, +78949,78955, 5,78943,18967,78958, 4,78942,78961,78963,78947,78963, +78951,78963, 5,78942,78953,78971, 1369,77751,12376,78974,12376,78975, +12377,78974,78979,78981,77410,78975,77517,78985,45639,78975,46599,78989, +12377,78975,78977,78993,36527,78975,68806,78975,77410,78974,77591,79001, +45639,78974,46275,79005, 1011,78975,65869,79008,65869,78975, 1011,79012, + 265,78974,17827,79017,56555,78975, 4,79021, 5,79021, 4,79020, + 5,79020,60857,77751, 241,79030, 9,79033, 5,79030, 9,79037, + 5,79031, 17,79041,70177,77751,38056,79045,38056,79044,38057,79045, +79049,79051,38057,79044,79047,79055, 375,79045,27979,77751, 375,79061, + 2,77750,68283,79065,68275,79065,36633,79065,36627,79065,39987,79065, +66423,79065,66883,79065,66405,79065,66397,79065,67687,79065,78047,79065, + 9,79087,36544,79065, 9875,79091,20501,79091,68177,79065, 9875,79097, +20501,79097,64396,79065, 9,79103, 5,79065,60857,79106, 9,79109, +35187,79065, 11,79112,20501,79115, 9875,79115, 11,79065,35187,79120, + 9875,79123,20501,79123,60857,79065, 5,79129, 17,79131, 5,79128, + 9,79135, 3,77750,40779,79139,63907,79139,73421,79139,73429,79139, +64235,79139,67465,79139,63955,79139,39027,79139,38685,79139,64073,79139, + 6,79139, 4,79161,78728,79139,78838,79139,78827,79139, 9,79168, +78725,79139, 9,79172, 9934,79139,20644,79139, 4663,79139, 903,79181, +17219,79139, 903,79185,12345,79139, 5,79189, 4,79188,79191,79193, + 4,79189, 5,79188,79197,79199, 5123,79139, 5,79203, 4,79202, +79205,79207, 4,79203, 5,79202,79211,79213,19667,79139, 5,79217, + 4,79216,79219,79221, 4,79217, 5,79216,79225,79227,73306,79139, + 9377,79231,63823,79231,40705,79139, 9377,79237,63823,79237,21036,79139, +70177,79139, 9,79245, 341,79139,64669,79249,65869,79249, 253,79139, +65869,79255, 8337,79139, 9,79259, 241,79139, 1471,79139,65869,79265, +37325,79139, 903,79269,20103,79139, 9,79273,10219,79139, 903,79277, + 4,79139,20501,79280, 9875,79280, 5,79139, 6,79287,78827,79289, +78725,79289,18967,79286, 9935,79287,20645,79287,36773,79139, 9,79301, + 53,79139,65869,79305, 5,79138,27979,79309, 8685,79309,41503,79309, +26293,79309, 1369,79309,16381,79309,13325,79309,35843,79309,19513,79309, + 8401,79309, 4137,79309,14625,79309, 4569,79309,37019,79309, 4063,79309, + 3863,79309,37377,79309,20219,79309,10273,79309, 9955,79309,20697,79309, + 9,79139,78725,79352,78827,79352,35187,79139, 1505,79359, 17,79139, +71229,79362, 9377,79365,63823,79365,21315,79363,71229,79139, 17,79372, +63823,79375, 9377,79375,18967,79139,20413,79381, 4,79383, 4,79382, +20487,79381,20486,79381,79161,79381, 4,79393, 4,79392,79163,79381, + 580,79381,49630,79381,79162,79381,46863,79381, 4,79406, 4,79407, + 483,79381, 4,79412, 4,79413,49631,79381, 581,79381, 4,79381, +20413,79422,79161,79422,46863,79422, 483,79422, 5,79381,79385,79433, +79179,79433,79283,79433,79395,79433,79411,79433,79417,79433,20413,79433, + 4,79380,79389,79449,79446,79449,79421,79449,79419,79449,79399,79449, +79433,79449,20413,79460,20413,79449,79433,79464, 5,79380,79425,79469, +79387,79469,79391,79469,79427,79469,79397,79469,79401,79469,79403,79469, +79405,79469,79429,79469,79409,79469,79431,79469,79415,79469,79423,79469, +20413,79495, 1369,79139, 5,79499,27979,79139,65869,79503, 9,79503, +20501,79139, 4,79508,79433,79511, 5,79508,18967,79514,21037,79508, + 4,79509,79517,79521,79243,79521,79295,79521,79469,79521, 5,79509, +79519,79531, 9875,79139, 4,79534, 5,79534, 4,79535, 5,79535, + 4,77750, 3,79545, 5,77750,10256,79549,37356,79549, 4745,79549, + 1011,79555,17423,79549, 1011,79559,63550,79549,64489,79549,63493,79549, + 17,79566,70803,79549,56555,79571,74752,79549, 8982,79549,74571,79549, + 17,79578, 8861,79549, 17,79582,37844,79549,27979,79549, 483,79589, + 253,79549,56555,79593, 8337,79549, 483,79597, 241,79549, 1325,79549, + 17,79602, 331,79549, 17,79606, 1328,79549, 334,79549, 53,79549, +56555,79615,20501,79549, 1011,79619,36773,79549, 483,79623, 9875,79549, + 1011,79627, 1359,79549, 393,79549, 2,79549,37325,79634,10219,79634, + 3,79549, 483,79641,35187,79640,10257,79641,37357,79641, 62,79549, + 59,79549, 17,79652,20103,79549, 483,79657, 1471,79549,56555,79661, + 3,79548,27979,79665, 8685,79665,26293,79665,41503,79665, 1369,79665, +16381,79665,13325,79665,19513,79665,35843,79665, 8401,79665, 4137,79665, +14625,79665, 4569,79665,20219,79665, 3863,79665, 4063,79665,20697,79665, +37019,79665, 9955,79665,10273,79665,37377,79665,18967,79549, 165,79709, + 1369,79549,38057,79713,35187,79549, 3,79717, 3,79716, 17,79549, +63493,79722,74571,79722, 8861,79722, 1325,79722, 331,79722, 59,79722, +37325,79549, 2,79736, 3,79736, 2,79737, 3,79737,10219,79549, + 2,79746, 3,79746, 2,79747, 3,79747, 5,77751, 1196,79756, + 1196,79757, 1197,79756,79761,79763,21604,79757, 6,79757, 587,79769, +21605,79771, 1,79757, 3,79774, 3,79775,18967,79775, 483,79781, + 1,79756,25381,79785,63014,79785,79773,79785,60857,79785, 483,79792, + 483,79785,60857,79796, 6,79756,79787,79801,63015,79801,79785,79805, +25380,79801,79785,79809,79789,79801,79783,79801,79799,79801, 1369,79801, +21605,79818,79785,79821,79795,79801,21605,79801, 1369,79826,79785,79829, + 1216,79757, 413,79757, 1197,79757,79759,79837,79546,79757, 903,79757, +20735,79757, 9993,79757, 1275,79756, 9,79849,60857,79756, 9,79853, +56555,79757, 1505,79856,21605,79757,21376,79757, 483,79757,21315,79864, +58112,79757,21315,79757, 483,79870, 1505,79757,56555,79874, 2,79756, +79767,79879, 7,79881, 6,79880,79883,79885, 6,79881, 7,79880, +79889,79891,79777,79879, 7,79895, 6,79894,79897,79899, 6,79895, + 7,79894,79903,79905,28337,79879, 3,79756,79861,79911, 7,79913, + 6,79912,79915,79917, 6,79913, 7,79912,79921,79923, 2,79757, +79779,79927, 7,79929, 6,79928,79931,79933, 6,79929, 7,79928, +79937,79939, 3,79757, 1,79942,79879,79945, 7,79947, 6,79946, +79949,79951, 6,79947, 7,79946,79955,79957,79545,79942,79545,79757, + 2,79963, 3,79963, 2,79962, 3,79962, 9,79757,63493,79973, +74571,79973, 8861,79973, 1325,79973, 331,79973, 59,79973, 3,77751, +79112,79987, 9,79989,78847,79987, 9,79993, 1216,79987,12371,79987, + 5,79999, 4,79998,80001,80003, 4,79999, 5,79998,80007,80009, + 413,79987, 375,79987,60454,79987,60369,80017, 566,79987,35187,79987, +79065,80022, 9,80025,79106,79987, 1011,79987,37407,79987,10287,79987, +65869,79987, 165,80036,38132,79987, 9,79987,38057,80042,67642,79987, +38057,79987, 9,80048, 165,79987,65869,80052, 5,79986, 4,79987, + 5,79987,79065,80060,56555,80060,60369,80065, 483,80060,79065,79987, +35187,80070, 9,80073, 4,80071, 5,80071, 4,80070, 5,80070, +56555,79987,49630,80084,46863,80084, 4,80089, 4,80088, 581,80084, + 580,80084,49631,80084, 4,80084,46863,80100, 483,80100, 5,80084, +60369,80107,80091,80107, 4,80085,80095,80113,80099,80113, 5,80085, +60327,80119,60719,80119,80097,80119,80087,80119,57901,80119,80093,80119, +80103,80119,80105,80119, 483,80084, 4,80136,80119,80139, 4,80137, +80107,80143, 483,79987, 5,80146, 2,77751,79538,80151,79541,80153, +79514,80151,79521,80157,79024,80151,79027,80161,78958,80151,78963,80165, +79021,80151, 5,80168,79027,80171, 5,80169,79023,80175,78943,80151, + 5,80178,78963,80181, 5,80179,78955,80185, 1,80151, 7,80189, + 4,80191, 4,80190, 16,80189, 4,80189, 7,80198,28714,80151, + 17,80203,79286,80151,79541,80207,79521,80207,35187,80150,79289,80213, +29229,80213,79352,80213, 9,80213,79139,80220,79139,80213, 9,80224, + 9,80151, 4,80150, 265,80231, 5,80150,80193,80235,28337,80235, + 4,80151, 1011,80241, 5,80151,80195,80245,80201,80245,79534,80245, +79543,80251,79508,80245,79531,80255,79021,80244,79027,80259,78943,80244, +78963,80263,79021,80245,79029,80267,78943,80245,78971,80271,80197,80245, +28219,80245,30475,80245,23939,80245,79139,80244,79541,80283,79521,80283, +21605,80244, 17,80289,79139,80151,35187,80293, 9,80295, 4,80293, + 5,80293,79537,80301,79511,80301,79177,80301,79179,80301,79285,80301, +79283,80301, 4,80292,80301,80315, 5,80292,79541,80319,79521,80319, +80299,80319,21605,80151, 5,80326, 17,80329, 5,80327, 9,80333, + 4,77751,79750,80337,79753,80339,79740,80337,79743,80343,78922,80337, +78925,80347,78880,80337,78883,80351,78919,80337, 3,80354,78925,80357, + 3,80355,78921,80361,78875,80337, 3,80364,78883,80367, 3,80365, +78877,80371, 6,80337, 1369,80375,56555,80377, 1,80337,60857,80381, + 1275,80383, 1,80336,58931,80387,19916,80387,80379,80387,18967,80387, + 1275,80394, 1275,80387,18967,80398, 6,80336,19917,80403,80387,80405, +80389,80403,80391,80403,58930,80403,80387,80413,80385,80403,80401,80403, + 587,80403,56555,80420,80387,80423,80397,80403,56555,80403, 587,80428, +80387,80431,43214,80337,79640,80337,79753,80437,79743,80437, 2,80336, + 265,80443, 2,80337, 903,80447, 3,80337,67988,80450,67988,80451, +67989,80450,80455,80457,79746,80451,79755,80461,79736,80451,79745,80465, +78919,80450,78925,80469,78875,80450,78883,80473,78919,80451,78927,80477, +78875,80451,78887,80481,67989,80451,80453,80485,79549,80450,79753,80489, +79743,80489,38305,80450,79549,80337, 2,80497, 3,80497,79749,80501, +79739,80501,79551,80501,79553,80501,79639,80501,79637,80501, 2,80496, +80501,80515, 3,80496,79753,80519,79743,80519,80499,80519,38305,80337, + 3,80526, 3,80527, 0, 7, 1517,80532, 2575,80535, 949,80532, + 2599,80539, 1476,80532, 2269,80543, 280,80532, 2289,80547, 272,80532, + 4437,80551, 121,80532,38343,80555, 3075,80555, 1913,80555, 76,80532, + 3005,80563, 1899,80563,38337,80563, 123,80533, 3061,80571, 81,80533, + 2997,80575, 367,80532, 7627,80579, 357,80532, 1927,80583, 397,80532, + 7895,80587, 1516,80532, 948,80532, 107,80532, 2221,80595, 203,80532, + 2215,80599, 215,80532, 4339,80603, 91,80532, 5045,80607, 1477,80532, + 281,80532, 273,80532, 317,80532,14469,80617, 67,80532,11087,80621, + 4643,80621, 2233,80621, 4625,80621, 9,80629, 53,80532, 2289,80633, + 1471,80532, 2269,80637,80012,80533,77949,80641,79834,80533,77949,80645, +79996,80533,78669,80649,79832,80533,78669,80653,78546,80533,77949,80657, +78108,80533,78141,80661,77949,80661,77855,80533,77949,80667, 416,80533, +17993,80671, 418,80533,17993,80675, 447,80533,17993,80679, 295,80532, + 294,80532, 76,80533,38587,80687,26285,80533,26390,80533,26106,80533, + 121,80533,38585,80697,26502,80533,32269,80533,31761,80533,32563,80533, +31921,80533,31591,80533,78287,80533,78141,80713,78099,80713,77949,80713, +23788,80533,27830,80533,27947,80533,41746,80533,26216,80533,41414,80533, +26126,80533,41348,80533,26328,80533,41556,80533,29029,80533,43319,80533, + 203,80533, 2211,80745,60857,80745, 85,80533, 4593,80751, 2229,80751, +60857,80751,80229,80533,39823,80533,23013,80533,39229,80533,39783,80533, +38673,80533,38675,80533, 107,80533, 5019,80773, 2219,80773,60857,80773, +80228,80533,40956,80533,45411,80783,37899,80783,41493,80533, 1517,80533, + 2573,80791,80591,80791, 949,80533, 2593,80797,80593,80797, 280,80533, + 2287,80803,80613,80803, 1476,80533, 2267,80809,80611,80809, 272,80533, + 4457,80815,80615,80815, 8673,80533, 8681,80533, 8800,80533, 8742,80533, +41622,80533, 8740,80533, 8610,80533, 8642,80533, 8638,80533,39650,80533, +42752,80533, 8717,80533, 413,80843, 1369,80842, 341,80842, 3785,80533, + 413,80851, 1369,80850, 341,80850, 413,80532, 8717,80859, 3785,80859, +13443,80859, 7627,80859, 7287,80859, 7895,80859,12755,80859, 1689,80859, + 2701,80859, 1705,80859,77859,80859,78459,80880,78548,80859,78048,80859, +77837,80887,78071,80859,18454,80859,78147,80859,78459,80859,77859,80896, + 265,80859,77751,80900,77837,80903,77751,80901,77859,80907,18391,80859, + 331,80910, 331,80859,18391,80914, 8495,80859,77751,80859, 265,80920, +77837,80923,12745,80859, 375,80927, 457,80927, 375,80859,13977,80533, +80021,80533,13767,80533, 4347,80533, 2213,80533, 2225,80533, 2231,80533, +10079,80533,79501,80533,18715,80533,18579,80533,14313,80533,80149,80533, +13945,80533, 4987,80533,14091,80533, 4619,80533,80069,80533,13913,80533, + 4555,80533,16635,80533,16301,80533, 2575,80533, 2599,80533, 2289,80533, + 2269,80533, 4437,80533, 7549,80533, 7589,80533,13785,80533,80015,80533, +23755,80533, 1369,80996,39633,80533, 1369,81000,78284,80533, 7205,80533, + 2561,80533, 2261,80533, 4423,80533, 2307,80533,79318,80533,10058,80533, +79674,80533,26331,80533, 1369,81022,41559,80533, 1369,81026, 4329,80533, + 1369,81030, 4311,80533, 1369,81034,10354,80533, 3786,80533, 4330,80533, +27805,80533, 1369,81044,42731,80533, 1369,81048,10055,80533, 1369,81052, +10351,80533, 1369,81056, 4312,80533,78645,80533,78324,80533,78458,80533, + 375,81067, 3794,80533,78147,80533, 413,81073, 1369,81072, 341,81072, + 215,80533, 4345,81081,60857,81081,79309,80533, 1369,81086,79665,80533, + 1369,81090, 1477,80533,80543,81095, 281,80533,80547,81099, 1516,80533, +80535,81103, 948,80533,80539,81107,13443,80533,16669,80533,43293,80533, +26104,80533,41314,80533,26277,80533,41479,80533,13027,80533, 264,81124, + 264,81125, 413,81125, 265,81124,81129,81133, 265,81125,81127,81137, + 5127,80533, 264,81140, 264,81141, 413,81141, 265,81140,81145,81149, + 265,81141,81143,81153,15309,80533, 412,81157, 413,81156,81159,81161, + 413,81157, 412,81156,81165,81167,32294,80533,45314,80533,31857,80533, +44925,80533,14053,80533,22897,80533,39143,80533,78107,80533, 413,81184, +78141,81187,77949,81187, 413,81185,77859,81193, 1276,80533,17993,81197, + 438,80533,17993,81201, 1278,80533,18759,81205, 1242,80533,18759,81209, +22806,80533, 903,81213, 9,81213,39084,80533, 483,81219,39204,80533, +41941,81223,22956,80533,26625,81227, 1109,80533,35187,81231, 294,80533, +80683,81235,35187,81235, 1413,80533, 1471,81241, 295,80533,80685,81245, +39749,80533, 483,81249,24095,80533,35187,81253, 11,81252,39787,80533, + 241,81259, 903,81259, 587,81258,24236,80533,39846,80533,78459,80533, + 413,81270,77949,81273, 413,81271,77859,81277,39232,80533,46169,81281, + 510,80533, 11,81285, 1108,80533, 11,81289, 1216,80533,79987,81292, +78669,81295,79757,81292,78669,81299, 1275,81292,18759,81303, 15,81292, +18759,81307, 8808,80533,41376,80533, 7195,80533, 1141,80533,14315,81317, + 959,80533,17125,81321, 253,80533,25793,81324,41019,81324, 8495,81324, + 317,80533,15661,81333,14991,81333,17993,81333, 2899,81333, 273,80533, +80551,81343, 8634,80533, 8677,80533, 331,80533, 413,81351,18391,81353, + 413,81350,17993,81357, 8861,81351,63493,81351,74571,81351,15029,81351, + 8495,80533, 586,81368, 586,81369, 587,81368,81373,81375, 11,81368, + 253,81368, 1471,81368, 53,81368, 587,81369,81371,81387, 305,81369, + 713,81369, 25,81369, 1369,81368, 257,81369, 257,81368,14011,80533, + 257,80533,25793,81404,41019,81404, 8495,81404, 973,80533,20103,81413, + 8337,81413,36773,81413, 341,80533,30091,81421, 8717,81420,20103,81421, +36773,81421, 8337,81421, 3785,81420,78147,81420, 89,80533,10831,81437, +10903,81437, 5019,81437,60857,81437,17125,81437, 567,80533,63493,81449, +74571,81449, 8861,81449, 652,80533,60857,81457, 288,80533, 8861,81461, + 265,80533,78670,81465,77859,81467, 413,81465,77751,81471,77837,81473, +77751,81470,77859,81477,73154,81465,62059,81465,78107,81465,15029,81465, + 4593,81465,11985,81465,78459,81465,80057,81465,79911,81465, 8861,81465, +63493,81465,74571,81465,72152,81465,61718,81465,77751,81465, 413,81508, +77859,81511, 413,81509,77949,81515, 17,81464,35187,81519, 11,81465, +60857,81522, 587,81465,71229,81526,71229,81465,60857,81530, 11,81531, + 587,81530,60857,81465, 10,81539, 10,81538, 305,81539, 11,81539, +81543,81547,71229,81538, 11,81538,81541,81553, 587,81464,60857,81557, + 11,81464, 8861,81561, 1011,80533,50785,81565, 9,81564, 11,81569, + 11,81565, 9,81573,71229,81565,77387,81577, 903,80533,50959,81581, +45149,80533,35187,81585, 11,81584,32103,80533,27021,81591, 241,81591, + 903,81591, 587,81590, 7333,80533, 483,81601, 6789,80533, 9,81605, + 903,81605,14073,80533, 241,81611, 903,81611, 587,81610,11277,80533, +35187,81619, 11,81618,42707,80533, 3445,80533, 375,81627,10313,80533, +79643,80533, 2621,80533,14229,80533,11571,80533, 5045,80533,35187,81641, + 11,81640, 4971,80533, 241,81647, 903,81647, 587,81646, 7009,80533, + 7497,80533, 7287,80533, 7845,80533, 7925,80533, 7895,80533, 7685,80533, + 7671,80533, 7627,80533,14102,80533,11348,80533, 5096,80533, 5000,80533, + 7533,80533, 4617,80533, 483,81683,13907,80533, 483,81687, 7563,80533, + 264,81691, 264,81690, 265,81691,81695,81697, 265,81690,81693,81701, + 7531,80533, 483,81705,79634,80533,80501,81709,79719,81709,79640,80533, + 483,81715,42716,80533,42795,81719,42704,80533, 483,81723,10308,80533, +36131,81727,10310,80533, 483,81731, 5227,80533, 5009,80533, 3461,80533, + 2981,80533, 3049,80533,80061,80533, 483,81745, 240,80532,77859,81749, +78459,81750,78089,81749,78147,81755,78305,81749,78231,81749,78277,81749, +78445,81749,78564,81749,78613,81749,78558,81749,78548,81749,78071,81749, +78387,81749,78459,81776,78425,81749,78459,81780,78434,81749,78147,81785, +13103,81749, 5161,81749,13074,81749, 5150,81749,13057,81749,13051,81796, + 5141,81749, 5137,81800,13051,81749,13057,81804, 5137,81749, 5141,81808, +13092,81749,13045,81813, 5154,81749, 5133,81817,78048,81749,77837,81821, +78459,81749,78387,81824,78425,81824,77859,81824,25793,81749,41019,81749, + 8495,81749,78147,81749,48009,81749,49321,81749,49001,81749,78399,81749, + 265,81847,78459,81849, 265,81846,78147,81853, 265,81749,13027,81856, +13045,81859, 5127,81856, 5133,81863,13027,81857,13057,81867, 5127,81857, + 5141,81871,78399,81856,78147,81875,77751,81856,77837,81879,77751,81857, +78387,81883,78425,81883,77859,81883,77751,81749, 265,81890,77837,81893, +23755,81749,39633,81749, 3785,81749, 8717,81749,79309,81749,79665,81749, +10055,81749,10351,81749,27805,81749,42731,81749,26331,81749, 4329,81749, +41559,81749, 4311,81749, 5127,81749, 265,81925, 5137,81927, 265,81924, + 5133,81931,13027,81749, 265,81935,13051,81937, 265,81934,13045,81941, + 1010,80532,27021,81945,24095,81945,52915,81945, 9195,81945,81565,81945, + 9,81955,81569,81945,23651,81945,67297,81945,81289,81945,21605,81945, + 9,81967,51435,81945, 4321,81945, 9755,81945,11277,81945, 5045,81945, + 4295,81945, 3833,81945,69729,81945,26907,81945,70097,81945,27323,81945, +53941,81945,53215,81945,10171,81945, 9317,81945, 264,80532,51421,82001, +39619,82001,57811,82001,59743,82001,59933,82001, 8093,82001, 3837,82001, +53703,82001, 4303,82001,42435,82001, 4287,82001,53487,82001, 9579,82001, +42113,82001, 9839,82001, 241,82001, 375,82032, 394,82001, 299,82001, + 375,82001, 241,82040,78399,80533, 264,82044, 264,82045, 413,82045, + 265,82044,82049,82053, 265,82045,82047,82057, 240,80533,78397,82061, +78459,82063,78099,82061,78147,82066,78264,82061,78283,82061,78260,82061, +78262,82061,78661,82061,78599,82061,78619,82061,78601,82061,78675,82061, +77837,82061,77949,82088,78673,82061,77950,82061,78118,82061,78459,82097, +78670,82061,77859,82101,77949,82061,78147,82104,77837,82104,78141,82061, +78147,82110, 413,82061,77751,82115,77837,82117,77751,82114,77859,82121, +78147,82061,78099,82124,78141,82124,77949,82124,78107,82061, 265,82132, +78459,82135, 265,82133,78147,82139,77751,82061, 413,82142,77859,82145, + 413,82143,77949,82149, 265,82061,78107,82152,78459,82155,78107,82153, +78141,82159, 902,80533,37663,82163,37599,82163, 265,80532,25793,82169, +41019,82169,39787,82169,23755,82169,39633,82169, 8495,82169,78147,82169, + 8717,82169, 3785,82169,79309,82169,79665,82169,27805,82169,42731,82169, +10055,82169,10351,82169,26331,82169,41559,82169, 4329,82169, 4311,82169, + 4555,82169,14073,82169, 4971,82169,21605,82169,65869,82215,38305,82169, +56555,82219, 483,82219, 241,80532, 4437,82225, 7,82225, 265,82229, +39787,82225,51615,82225, 4613,82225,12571,82225,14073,82225, 4971,82225, + 375,82225, 510,82225,51937,82225,16683,82225, 4805,82225, 9,82225, + 483,82254, 483,82225, 9,82258,38305,82225, 483,82263, 1010,80533, +55211,82267,64345,82267,36256,82267,35187,82267, 9,82274, 9,82267, +35187,82278, 1011,80532,10001,82283, 903,82285,27533,82283, 903,82289, +79139,82283, 903,82293,82267,82283, 9,82296,82278,82283,81231,82283, +21605,82283, 903,82305, 9,82283,82267,82308, 903,80532, 3549,82313, + 2001,82313,21769,82313,10291,82313, 1011,82321,42613,82313, 1011,82325, +79549,82313, 1011,82329,24095,82313,25793,82313, 8495,82313, 5045,82313, +11277,82313,39787,82313,14073,82313, 4971,82313,21605,82313, 955,82349, + 9,82349,65869,82349,38305,82313, 1011,82357, 483,82357,41019,82313, + 264,80533, 53,82365, 1471,82365, 253,82365,72567,82365,76285,82365, +76939,82365,77751,82364, 375,82379, 471,82365, 367,82365, 357,82365, + 341,82365, 241,82389, 241,82365, 9,82393, 483,82393, 413,80533, +78106,82398,78107,82399,82401,82403,78459,82403,78106,82399,78107,82398, +82409,82411,78141,82411,77949,82411,79987,82398,77949,82419,79757,82398, +77949,82423,78459,82398,77949,82427, 331,82398,17993,82431, 261,82398, +17993,82435,77949,82399,78459,82439,17993,82399, 331,82443, 1275,82398, +17993,82447, 15,82398,17993,82451, 375,80533, 53,82455, 1471,82455, + 253,82455, 1369,82455, 11,82455, 261,80533, 413,82467,18391,82469, + 413,82466,17993,82473,15029,82467, 264,82477, 265,82476,82479,82481, + 264,82476, 265,82477,82485,82487,16531,80533, 483,82491,12745,80533, + 264,82495,13129,82495, 413,82495,13119,82501,15503,82495,50535,82495, +50929,82495,51081,82495,82231,82495,13171,82495,13133,82495, 264,82494, + 265,82495,82519,82521, 265,82494,82497,82525, 375,82495, 257,82495, + 264,82531, 265,82530,82533,82535, 264,82530, 265,82531,82539,82541, + 566,80533, 53,82545, 253,82545, 1471,82545, 88,80533, 1369,82553, + 1346,80533,55211,82557,28714,80533,28337,82561,60618,80533,60757,82565, + 3,82565,60755,82569, 154,80533,37663,82573,43214,80533,43053,82577, + 483,82577,43502,80533,80531,82583,46169,82583, 67,80533,11529,82589, +11569,82589, 4610,82589,61780,82589, 9,82589, 4593,82598,60857,82598, +60857,82589, 9,82604, 4593,82589, 9,82608,37845,80533,38305,82612, +45411,82615,37899,82615,79926,80533,79987,82621, 4,82623, 5,82622, +82625,82627, 5,82623, 4,82622,82631,82633,80058,80533,79757,82637, + 2,82639, 3,82638,82641,82643, 3,82639, 2,82638,82647,82649, +80244,80533, 17,82653, 1447,80533,14315,82657, 33,80533,14315,82661, + 759,80533,17125,82665, 47,80533,17125,82669, 1471,80533,41019,82672, +25793,82672, 8495,82672, 53,80533,25793,82680,41019,82680, 8495,82680, +25793,80533, 586,82688, 586,82689, 11,82688, 1369,82688, 587,82688, +82693,82699, 253,82688, 53,82688, 1471,82688, 587,82689,82691,82709, + 25,82689, 713,82689, 305,82689, 257,82688, 257,82689, 1325,80533, +74571,82723,63493,82723, 8861,82723,15029,82723,41019,80533, 586,82732, + 586,82733, 587,82732,82737,82739, 11,82732, 253,82732, 1471,82732, + 53,82732, 713,82733, 25,82733, 305,82733, 587,82733,82735,82757, + 257,82732, 1369,82732, 257,82733, 59,80533,63493,82767,74571,82767, + 8861,82767,15029,82767,79139,80533,80244,82777,80245,82777,80293,82777, + 4,82782,80319,82785, 4,82783,80301,82789,80298,82777,80319,82793, +21036,82777,21037,82777, 1216,82777, 413,82777,80245,82776,82779,82805, + 1011,82777, 903,82808,21037,82776,82797,82813,80244,82776,82781,82817, +21036,82776,82799,82821,65869,82777, 9,82777, 903,82777, 1011,82828, + 4,82777,80293,82833,80315,82835,80293,82832,80319,82839,79549,80533, +80450,82843,80451,82843,80497,82843, 2,82848,80519,82851, 2,82849, +80501,82855,80498,82843,80519,82859, 1216,82843, 413,82843,80451,82842, +82845,82867, 903,82843, 1011,82870,80450,82842,82847,82875,56555,82843, + 483,82843, 1011,82843, 903,82882, 2,82842,80501,82887,79719,82887, + 3,82842,80337,82892, 483,82893, 3,82843,82867,82899, 1369,82899, + 2,82843,82875,82905,82895,82905,80497,82905,80515,82911,80497,82904, +80519,82915,80519,82905,80437,82905,80489,82905,79587,82905,79721,82905, +79645,82905,80337,82842, 3,82930,82905,82933, 3,82931,82887,82937, +27533,80533, 1216,82941, 413,82941,21036,82941,21037,82941, 1011,82941, + 903,82950,21037,82940,82947,82955,21036,82940,82949,82959, 9,82941, +65869,82941, 903,82941, 1011,82966,42613,80533, 1216,82971, 413,82971, + 903,82971, 1011,82976, 483,82971,56555,82971, 1011,82971, 903,82984, + 2,82971,42667,82989,42791,82989,42709,82989, 3,82971, 1369,82997, + 2,82970,42795,83001, 3,82970, 483,83005,10001,80533, 1216,83009, + 413,83009,21036,83009,21037,83009,21036,83008,83017,83019, 1011,83009, + 903,83022,21037,83008,83015,83027,65869,83009, 9,83009, 903,83009, + 1011,83034,10291,80533, 1216,83039, 413,83039, 903,83039, 1011,83044, +56555,83039, 483,83039, 1011,83039, 903,83052, 2,83039,37871,83057, +36133,83057,35803,83057, 3,83039, 1369,83065, 2,83038,36131,83069, + 3,83038, 483,83073, 1369,80533,26331,83076,25793,83076,23755,83076, +27805,83076,40381,83077,41559,83076, 8717,83076,21036,83077,39633,83076, +78147,83076,79309,83076,10055,83076,42731,83076, 3785,83076,79665,83076, + 4329,83076,10351,83076, 4311,83076,38305,83076,41941,83115,41019,83076, + 8495,83076,38305,83077,41019,83123, 5,83077,18967,83126,18967,83077, + 11,83131, 5,83130, 17,80533,25367,83137,62984,83137,60857,83137, + 265,83143, 241,83142, 241,83137,60857,83148,21605,83136,26625,83153, + 265,83136,35187,83157,21605,83137,25793,83161,35187,83137, 265,83165, + 587,80533,62984,83169,62518,83169,68807,83169,36526,83169,17125,83169, + 265,83169,35187,83180,35187,83169,60857,83184, 265,83184,32103,83168, +38305,83168, 483,83193,39787,83168,14073,83168, 4971,83168, 265,83168, +60857,83203, 241,83169,60857,83206,60857,83169, 241,83210,35187,83210, + 11,80533,25793,83216,41019,83216, 8495,83216, 265,83217, 8495,83225, +45149,83216,21605,83216, 903,83231, 9,83231,24095,83216,11277,83216, + 5045,83216, 265,83216, 8861,83243, 3,83217, 375,83247,38305,80533, +76424,83251,54262,83251,54219,83251,76453,83251,42185,83251,42193,83260, +42194,83251,42191,83251,42193,83251,42185,83268,80450,83251,37844,83251, +37845,83251,44663,83277,38707,83277,43483,83277,37897,83277,80451,83251, +80450,83250,83287,83289, 120,83251, 77,83251,32805,83251,54225,83251, +54129,83298,76337,83251,76307,83302,76307,83251,76337,83306,54129,83251, +54225,83310, 113,83251, 25,83314, 506,83251, 1216,83251, 413,83251, + 1413,83251,37845,83250,45411,83327,83275,83327,37899,83327,37897,83250, +83275,83335,65076,83251,37844,83250, 6,83340,83277,83343,83277,83341, +80451,83250,83273,83349, 66,83251, 33,83353, 25,83251, 113,83356, + 1281,83251, 1086,83251, 922,83251, 483,83251, 241,83366, 587,83367, + 903,83366, 241,83251, 483,83374, 903,83251, 1011,83378, 483,83378, + 1369,83250,41941,83385, 587,83250, 483,83389, 587,83251, 1011,83392, + 1011,83251, 903,83396, 11,83397, 587,83396, 1275,83250,46169,83405, +60857,83251,56555,83408,56555,83251, 11,83413,60857,83412, 2,83250, +80531,83419,46169,83419, 3,83250,43053,83425, 483,83425, 2,83251, +80435,83431,80495,83431,43461,83431,80529,83431,46599,83431,43425,83431, + 3,83251,43837,83445,43027,83445, 11,83445, 25,83451,39747,83445, + 11,83444, 33,83457, 1369,83445, 11,83251, 3,83463, 113,83465, + 3,83462, 33,83469, 15,80533, 1216,83473,18617,83475, 413,83473, +18391,83479,25545,83473,62942,83473, 1216,83472,18759,83487, 413,83472, +17993,83491,60857,83473, 1011,83494,18967,83473, 17,83499, 1011,83473, +60857,83502, 3,83472,37663,83507, 3,83473,37611,83511,38155,83511, +35927,83511,21605,80533,27065,83519,27261,83519,21036,83519,21037,83519, +27067,83519,27255,83519,73221,83519,55211,83519,36256,83519,51593,83519, +55803,83519,64345,83519,54477,83519, 1216,83519, 413,83519,21037,83518, +83525,83551,21036,83518,83527,83555, 247,83519, 934,83519, 9,83519, +35187,83562, 11,83563, 1011,83519, 903,83568, 17,83518,26625,83573, + 11,83518, 903,83577, 9,83577, 11,83519, 903,83582, 903,83519, +27021,83587, 1011,83586, 11,83587, 11,83586,35187,83519, 9,83596, +65869,83519, 5,83518,28337,83603, 5,83519,27021,83607,52915,83607, + 9195,83607,20501,83607, 9875,83607,30475,83607,28219,83607,23939,83607, + 1275,80533,43214,83624,43214,83625,43215,83624,83629,83631, 1216,83625, +18617,83635, 413,83625,18391,83639,43215,83625,83627,83643,40675,83625, +73994,83625, 1216,83624,18759,83651, 413,83624,17993,83655,71229,83625, + 903,83658, 903,83625,71229,83662,38305,83624,46169,83667, 5,83624, +55211,83671, 5,83625,53813,83675,10107,83675,69877,83675, 483,80533, + 241,83683, 9,83685, 9,83682, 11,83689, 17,83683, 903,83693, + 11,83683, 9,83697, 5,83682, 53,83701, 253,83701, 1471,83701, + 5,83683, 1505,83709, 9,80533,80151,83712, 1011,83713,82267,83717, + 241,83713, 483,83721, 483,83712, 11,83725, 1011,83712,81945,83729, + 11,83729, 3,83712, 1369,83735, 3,83713, 483,83739,17025,80533, + 5,83742, 2,83745, 17,83746, 17,83745, 2,83750, 2,83751, + 164,83745,38057,83743,38057,83742,38056,83742,83759,83763,38056,83743, +83761,83767, 3,83742,83749,83771,83753,83771,83757,83771, 3,83743, +83755,83779,13737,80533, 375,83783,80151,80533, 9,83786,35187,83787, + 9,83791, 5,83787, 9,83795, 5,83786, 17,83799,79987,80533, + 413,83802,77949,83805, 413,83803,77859,83809, 1216,83802,78669,83813, + 1216,83803,77857,83817, 4,83803,79549,83821, 2,83823, 3,83822, +83825,83827, 3,83823, 2,83822,83831,83833, 4,83802,79757,83837, + 2,83839, 3,83838,83841,83843, 3,83839, 2,83838,83847,83849, +79757,80533, 413,83852,77949,83855, 413,83853,77859,83859, 1216,83852, +78669,83863, 1216,83853,77857,83867, 2,83853,79139,83871, 4,83873, + 5,83872,83875,83877, 5,83873, 4,83872,83881,83883, 2,83852, +79987,83887, 4,83889, 5,83888,83891,83893, 5,83889, 4,83888, +83897,83899, 3,80532,79231,83903,79237,83903,79365,83903,79375,83903, +73413,83903,73405,83903,40793,83903,40787,83903,71751,83903,36001,83903, +71731,83903,72115,83903,72929,83903,71713,83903, 7,83903, 4,83933, +83519,83935,32103,83903,26106,83903,41746,83903,43005,83903, 8800,83903, +64513,83903,63253,83903,62215,83903,59910,83903,41019,83903, 11,83956, +60755,83903,60707,83903, 9852,83903,48975,83903,48245,83903,48451,83903, +76271,83903,82267,83903, 9,83974,82278,83903,83157,83903,81231,83903, +81235,83903,81519,83903, 386,83903, 347,83903, 510,83903, 273,83903, + 281,83903, 1477,83903, 1516,83903, 948,83903, 165,83903, 903,84004, +40704,83903,79139,84009,27533,84009,10001,84009,73307,83903,79139,84017, +27533,84017,10001,84017,53500,83903,37595,83903,36049,83903,36539,83903, +68851,83903,36041,83903,37573,83903,36275,83903, 1505,83903, 1011,84040, +25793,83903, 11,84044,64397,83903, 483,84049, 8495,83903, 11,84052, +60618,83903, 483,84057, 375,83903, 305,84060, 903,83903, 165,84064, + 1011,83903, 1505,84068, 305,83903, 375,84072, 4,83903,56555,84076, + 483,84079, 5,83902,57811,84083,59743,84083,59933,84083, 9579,84083, + 9839,84083,51421,84083,39619,84083, 4303,84083, 4287,84083, 8093,84083, + 3837,84083,42113,84083,53487,84083,53703,84083,42435,84083,48885,83903, + 483,84115, 11,83903,25793,84118,41019,84118, 8495,84118,59875,84118, + 9805,84118,53391,84118, 9,83903,82267,84132, 483,84132,38305,84132, +79139,84139,10001,84139,27533,84139,38305,83903, 9,84146,79139,84149, +27533,84149,10001,84149, 9805,83903, 11,84156, 5,84157, 483,83903, + 9,84162,56555,83903, 4,84166, 483,84169,53391,83903, 11,84172, + 5,84173,59875,83903, 11,84178, 5,84179, 2,80532,11087,84185, +11307,84185,11713,84185,10439,84185, 2221,84185,10665,84185,11037,84185, + 2233,84185,17777,84185, 2215,84185,18739,84185, 4339,84185,79091,84185, +79097,84185, 5075,84185, 4643,84185,79115,84185,79123,84185,59811,84185, +36619,84185,68291,84185,68299,84185,24213,84185,24059,84185,22149,84185, +22143,84185,72667,84185,60839,84185,35665,84185,59819,84185,59869,84185, +35487,84185, 7,84185, 4,84251,27306,84185,24095,84185,53910,84185, +55537,84185,79280,84185,80301,84263,83607,84185,40455,84185,25313,84185, + 8714,84185,32103,84185,26106,84185, 8800,84185,41746,84185,81569,84185, +48009,84185,49001,84185,49321,84185, 4551,84185,81531,84185,81289,84185, +81245,84185,83729,84185,10196,84185,70136,84185,10603,84185, 9,84305, + 4625,84185, 9,84309,81565,84185, 9,84313,23983,84185, 9,84317, +76501,84185,68176,84185,79065,84323,59787,84323, 9513,84323,36545,84185, +79065,84331,59787,84331, 9513,84331, 759,84185,35187,84339,10107,84185, + 265,84342, 241,84343,69877,84185, 265,84348, 241,84349, 305,84185, + 8495,84354, 959,84185,35187,84359,28714,84185,77079,84185, 1011,84365, + 1010,84364,84367,84369, 1010,84365, 1011,84364,84373,84375, 47,84185, +35187,84379,48245,84185,48975,84185,48451,84185,11277,84185, 5045,84185, +52603,84185, 8495,84185, 10,84394, 10,84395, 305,84394, 11,84394, +84399,84403, 11,84395,84397,84407, 331,84185,71229,84411,79549,84185, + 1011,84415, 265,84185,53813,84418,71229,84419,10107,84418,69877,84418, +79757,84419, 903,84185,55211,84431,10291,84185, 1011,84435,25793,84185, + 10,84438, 10,84439,27021,84438, 11,84438,84443,84447, 11,84439, +84441,84451,42613,84185, 1011,84455, 1325,84185,71229,84459, 5,84185, +27255,84463, 9287,84463,53167,84463,21605,84462, 4,84185,79139,84472, +80301,84475,49475,84473,73066,84473,28715,84473, 1011,84473,71229,84484, +71229,84473, 1011,84488,27693,84473,10047,84473,79287,84473,41019,84185, + 10,84498, 10,84499, 11,84498,84503,84505, 11,84499,84501,84509, + 59,84185,71229,84513, 4,84184, 8495,84517,41019,84517,25793,84517, +49001,84517,49321,84517,48009,84517, 265,84517,78147,84517,23755,84517, +39633,84517, 3785,84517, 8717,84517,41559,84517, 4311,84517,79665,84517, +10351,84517,26331,84517,42731,84517, 4329,84517,79309,84517,27805,84517, +10055,84517, 5,84184,27021,84563,24095,84563, 9195,84563,52915,84563, +21605,84563, 9,84573,23651,84563, 9755,84563,51435,84563,67297,84563, + 4321,84563,69729,84563,26907,84563, 9317,84563,53215,84563,27323,84563, +53941,84563, 3833,84563,10171,84563,70097,84563, 4295,84563, 5045,84563, +11277,84563,53813,84185, 265,84610, 241,84611,48885,84185, 1011,84617, + 11,84185,25793,84620, 8495,84620,41019,84620, 17,84185,30091,84629, +65869,84185, 15,84632,79065,84635,59787,84635, 9513,84635, 803,84633, +38305,84185, 1011,84645, 1347,84645, 15,84185,28714,84650,28714,84651, +65869,84650,79065,84657, 9513,84657,59787,84657,28715,84650,84655,84665, +28715,84651,84653,84669, 9195,84185, 241,84673,35187,84673,21605,84185, +27255,84679, 7,84678, 4,84682, 4,84683,84253,84679, 16,84678, +55211,84679, 803,84679, 9,84679, 17,84678, 903,84679,65869,84679, + 4,84679,84699,84705, 5,84679,84685,84709,84691,84709,27021,84709, + 4,84678, 7,84716,84709,84719, 5,84678,84687,84723,52915,84185, +35187,84727, 241,84727,27021,84185,35187,84733, 241,84733,25793,84732, +79139,84185,80245,84740, 4,84740,80301,84745, 5,84740,80151,84748, + 4,84741,80319,84753,84751,84753,80207,84753,80283,84753, 5,84741, +84743,84763, 5,80532,80759,84767, 7,84767, 2,84770, 3,84770, +83251,84775, 2,84771,84776,84779,84775,84779,83251,84782,83251,84779, +84775,84786, 3,84771,84773,84791,83251,84793,27306,84767,24095,84767, +52603,84767,53910,84767,55537,84767,84438,84767,84394,84767,84498,84767, +81539,84767,25793,84767,27021,84814,84185,84814,64289,84767,84060,84767, +81569,84767,83729,84767,81245,84767,81289,84767,70136,84767,10196,84767, + 273,84767, 1477,84767, 281,84767, 948,84767, 1516,84767,81565,84767, + 9,84847, 1505,84767, 1011,84850, 5045,84767,11277,84767, 4551,84767, + 922,84767, 1413,84767,20831,84767,19783,84767,20053,84767,58775,84767, + 9195,84767, 265,84873, 47,84767,60857,84877, 165,84767, 903,84880, +41019,84767,84185,84884,27021,84767,25793,84888, 265,84889, 8495,84767, +84185,84894, 959,84767,60857,84899, 89,84767, 5019,84903,60857,84903, + 375,84767,83903,84908, 759,84767,60857,84913, 265,84767,53813,84916, +69877,84916,10107,84916, 1011,84767, 1505,84924,54597,84925, 903,84767, + 165,84930, 483,84930,54295,84931, 1447,84767,18967,84939, 1141,84767, +18967,84943, 2,84767,61780,84947, 9,84947,60857,84950,54295,84947, +70085,84947,11529,84947,10155,84947,53925,84947,60857,84947, 9,84964, + 3,84767, 8063,84969,42361,84969,53667,84969,84185,84767,41019,84976, + 8495,84976,25793,84976,83903,84767, 375,84984, 33,84767,18967,84989, + 2,84766,27021,84993,24095,84993, 9195,84993,52915,84993,21605,84993, + 9,85003,67297,84993, 9755,84993,51435,84993,23651,84993, 4321,84993, +26907,84993,69729,84993,10171,84993,53941,84993,70097,84993,53215,84993, + 9317,84993, 4295,84993,27323,84993, 3833,84993, 5045,84993,11277,84993, + 3,84766,57811,85041,59743,85041,59933,85041,51421,85041,39619,85041, + 4303,85041, 4287,85041, 9579,85041, 9839,85041, 8093,85041, 3837,85041, +53487,85041,42113,85041,53703,85041,42435,85041,52915,84767, 265,85073, + 587,84767,60857,85077,70573,85077, 1369,84767,43503,85083, 483,84767, + 903,85086,21605,84767, 9,85091, 145,85091,10107,84767, 265,85096, + 9,84767,53813,84767, 265,85102,69877,84767, 265,85106, 4,80532, +83715,85111,80781,85111,83789,85111, 7,85111, 2,85118,83511,85121, + 2,85119,82163,85125,83507,85125,82573,85125, 1368,85111,83511,85133, +25525,85111,84498,85111,84438,85111,84709,85111,79634,85111,80501,85145, +84394,85111,64513,85111,63253,85111,62215,85111,81539,85111,64397,85111, + 483,85159,83787,85111, 9,85163,43214,85111, 8495,85111,84185,85168, + 587,85169, 331,85111,60857,85175,79139,85111, 903,85179, 265,85111, +60857,85183,79987,85183,10001,85111, 903,85189,41019,85111,84185,85192, + 587,85193,27533,85111, 903,85199, 59,85111,60857,85203, 3,85111, + 4,85206,85125,85209,38305,85206, 2,85111, 7,85214,83511,85217, +79549,85214,80501,85221,49155,85215,62464,85215,43215,85215, 903,85215, +60857,85230,60857,85215, 903,85234,42705,85215,10311,85215,79641,85215, +84185,85111,25793,85244,41019,85244, 8495,85244,25793,85111,84185,85252, + 587,85253, 1325,85111,60857,85259, 2,85110, 8495,85263,25793,85263, +41019,85263,49321,85263,49001,85263,48009,85263, 265,85263,78147,85263, +39633,85263,23755,85263, 3785,85263, 8717,85263,26331,85263, 4329,85263, +79309,85263,10055,85263,41559,85263,27805,85263, 4311,85263,79665,85263, +42731,85263,10351,85263,49205,85111, 903,85309,21605,85111, 903,85313, + 155,85313, 1275,85111,43215,85319,77751,85111,38305,85111, 7,85324, + 2,85326, 2,85327, 1368,85324, 1369,85324, 2,85325,85335,85337, + 3,85325,85329,85341,85333,85341,84185,85341, 2,85324, 7,85348, +85341,85351, 3,85324,85331,85355,79549,85111,80451,85358, 2,85358, +80501,85363, 3,85358,80337,85366, 2,85359,80519,85371,85369,85371, +80437,85371,80489,85371, 3,85359,85361,85381, 5,80533,83932,85385, +83933,85385,64148,85385,64149,85385, 0,85385,56555,85395, 1369,85397, + 7,85385,83903,85400,60857,85400, 265,85401,83903,85407, 1275,85401, +60857,85411, 0,85384,63015,85415,25380,85415,85413,85415,21605,85415, + 1369,85422, 1369,85415,21605,85426, 7,85384,85417,85431,25381,85431, +85415,85435,63014,85431,85415,85439,85419,85431,85399,85431,85429,85431, +85425,85431, 483,85431,60857,85450,85415,85453,60857,85431, 483,85456, +85415,85459,54440,85385,83903,85385, 7,85464,27425,85385,53137,85385, + 9365,85385, 803,85385,60857,85474,62310,85385,85206,85385,85125,85481, + 241,85385, 483,85485,54295,85484,54295,85385, 241,85490, 483,85384, + 53,85495, 253,85495, 1471,85495, 1275,85384,55211,85503,21605,85384, +28337,85507, 1369,85385, 1011,85511,60857,85385, 7,85514, 803,85514, + 2,85384,85389,85521,85393,85521,55211,85521,64345,85521,84132,85521, +36256,85521,83903,85521, 9,85534, 9,85521,83903,85538,35187,85538, +35187,85521, 9,85544, 3,85384,85467,85549,85387,85549,85403,85549, +85401,85549,83903,85557,85391,85549,85517,85549,85405,85549, 53,85549, + 1471,85549, 253,85549,76285,85549,76939,85549,72567,85549, 241,85549, + 483,85579, 9,85579, 2,85385, 3,85385, 165,85587, 1108,85587, + 1072,85587,76337,85587, 1011,85587, 483,85596, 9,85596,85111,85586, +85125,85603, 9,85587, 1011,85606, 483,85587, 1011,85610,80151,85384, + 17,85615,85111,85385, 1368,85618, 7,85618, 2,85623, 2,85622, + 1369,85618, 2,85619,85629,85631, 3,85619,85627,85635,85121,85635, +85217,85635,85621,85635,85133,85635, 2,85618, 7,85646,85635,85649, + 3,85618,85625,85653,85125,85653,55211,85385, 265,85659, 17,85385, + 3,80533,73221,85665, 9,85667,78796,85665, 9,85671, 8139,85665, +53639,85665,42521,85665,85322,85665, 9,85681,55004,85665,85111,85665, +77751,85686, 9,85689,42613,85664, 483,85693,10291,85664, 483,85697, +79549,85664, 483,85701,85585,85665, 9,85705, 9,85664, 1369,85709, + 15,85664,37663,85713,85125,85713,38305,85664,43053,85719, 483,85719, + 17,85665, 903,85725,35187,85665,77751,85728, 9,85731, 4,85664, +85125,85735,37663,85735, 5,85664,76939,85741, 1471,85741, 53,85741, + 253,85741,76285,85741,72567,85741, 241,85741, 9,85755, 483,85755, + 5,85665, 1505,85761, 922,85761, 918,85761,54597,85760, 903,85761, + 9,85770, 483,85770, 483,85761, 903,85776, 9,85761, 903,85780, +77751,85760, 9,85785,79756,85665, 9,85789,84185,85665, 16,85792, + 7,85792, 4,85797, 4,85796, 17,85792, 4,85793,85803,85805, + 483,85805, 5,85793,85801,85811,85797,85811,85795,85811, 4,85792, + 7,85818,85811,85821, 5,85792,85799,85825,54597,85665, 11,85829, + 5,85828, 1369,85665, 9,85835,18967,85665, 4,85839, 483,85841, +77751,85665,35187,85844, 9,85847, 9,85845,85111,85851,85111,85844, + 9,85855, 5,85845, 17,85859, 5,85844, 9,85863,77751,85664, + 375,85867,79986,80533, 375,85871, 2,80533,80080,85875,80079,85877, +80298,85875,80319,85881,80293,85875, 4,85884,80319,85887, 4,85885, +80301,85891,11727,85875,11047,85875,17787,85875,18737,85875,78670,85875, +77859,85903,10874,85875,77952,85875,77857,85909, 7,85874,83903,85913, +85167,85875,85355,85875,85213,85875, 1216,85875,77751,85923,77877,85925, +77751,85922,77857,85929,62310,85875,64483,85875,68896,85875,62464,85875, +61780,85875,61688,85875,85159,85875,10831,85875, 9,85946, 413,85875, +77751,85951,77837,85953,77751,85950,77859,85957,74571,85875,17178,85875, +50959,85875,43400,85875,11177,85875,84077,85875,85913,85971,40675,85875, +55546,85875,54440,85875,69196,85875,17125,85875, 9,85982,15029,85875, +65869,85986,52169,85875,49155,85875,40453,85875,43305,85875,40005,85875, +84767,85875,77751,86001, 9,86003,73994,85875,42613,85874,42795,86009, +10291,85874,36131,86013,70135,85875,53919,85875,10195,85875,79842,85875, +79757,85875, 903,86024,63493,85875, 8861,85875,80058,85875,80079,86033, +54422,85875,11529,85875,54447,85875,52601,85875, 145,85875,35187,86044, +36096,85875,79601,85875,79549,85874,80501,86053,79719,86053,64396,85875, +85111,86059, 1011,85875,43215,86062, 241,85875,54295,86066,65869,85875, +60857,86070,15029,86070, 9,85875,10831,86076,60857,86076,17125,86076, +84076,85875, 483,86085, 803,85875,60857,86088, 903,85875,53813,86093, +60857,86092,10107,86093,69877,86093,79757,86092,71229,86092, 265,85875, +55211,86106,10371,85875,42787,85875,55211,85875,60857,86114, 265,86114, +38305,85874,46169,86121,80531,86121,72818,85875, 11,85875,43215,86128, +43316,85875,35187,85875,54295,86134, 145,86134,71229,85875, 903,86140, + 155,86140,43215,85875, 10,86147, 10,86146,85111,86147, 1011,86146, +84767,86147, 11,86147,86151,86159, 11,86146,86149,86163, 155,85875, +71229,86166, 5,85874,55211,86171,64345,86171,84132,86171,36256,86171, +83903,86171, 9,86180, 9,86171,83903,86184,35187,86184,35187,86171, + 9,86190, 4,85875,80070,86195,80077,86197,80293,86195,80315,86201, +80293,86194,80319,86205, 483,86195,83903,86209,80533,86194, 3,86213, + 7,86195,86215,86217, 2,86217,86213,86221,79987,86194,80079,86225, +83903,86194, 483,86229,77751,86195,79545,86233, 3,86235, 2,86234, +86237,86239, 2,86235, 3,86234,86243,86245,77751,86194,79549,86249, + 3,86251, 2,86250,86253,86255, 2,86251, 3,86250,86259,86261, + 5,85875,85914,86265,85913,86265,83903,86268,49713,86265,49712,86264, +86273,86275,49712,86265,49713,86264,86279,86281,53813,86265,64317,86265, +64815,86265,62077,86265,10107,86265,69877,86265, 17,86265,83903,86297, +35187,86297,60857,86264,85111,86303,65869,86265, 9,86307, 587,86265, +60857,86311,83903,86265,85913,86314,79757,85874,79987,86319, 4,86321, + 5,86320,86323,86325, 5,86321, 4,86320,86329,86331,80336,85875, +79549,86335, 3,86337, 2,86336,86339,86341, 2,86337, 3,86336, +86345,86347,83903,85875, 16,86351, 7,86351,86265,86355, 4,86354, + 4,86355, 17,86351, 4,86351, 7,86364, 5,86351,86361,86369, + 4,86350,86363,86373, 483,86373, 5,86350,86359,86379,86367,86379, +86355,86379,86353,86379,54295,85875, 241,86388,35187,86388,60857,85875, + 7,86395, 4,86396, 4,86397,86265,86397, 16,86395, 17,86395, + 803,86394,55211,86394, 903,86394,65869,86394, 9,86394, 4,86394, +86407,86419, 5,86394,86399,86423,86405,86423,85111,86423, 4,86395, + 7,86430,86423,86433, 5,86395,86401,86437,77751,85875, 413,86440, +77859,86443, 413,86441,77949,86447, 1216,86440,77857,86451, 1216,86441, +78669,86455, 4,86441,79757,86459, 3,86461, 2,86460,86463,86465, + 2,86461, 3,86460,86469,86471, 4,86440,79549,86475, 3,86477, + 2,86476,86479,86481, 2,86477, 3,86476,86485,86487,79987,85875, +80244,86491,80245,86491,80151,86491, 5,86496, 5,86497, 4,86491, +80083,86503,80029,86503,80063,86503,86501,86503, 5,86491,80151,86512, + 4,86490,80079,86517,86513,86517,86493,86517,86515,86517,86499,86517, + 5,86490,86495,86529,86503,86529,77751,85874,86529,86535,86503,86536, +86532,86535,86521,86535,86512,86535,86517,86545,86503,86535,86529,86548, +86491,86535, 5,86552,86517,86555, 5,86553,86503,86559,85663,86535, +85100,86535, 9,86535,84767,86566,84767,86535, 9,86570, 5,86535, +86491,86574,86517,86577,86491,86575,86529,86581,80150,80533,80315,86585, +80301,86586,80298,86585,80319,86591,80301,86585,80315,86594,80316,86585, +80325,86585,80293,86585, 4,86602,80319,86605, 4,86603,80301,86609, +85663,86585,85100,86585, 9,86585,84767,86616,84767,86585, 9,86620, + 4,86585,80293,86625,80315,86627,80293,86624,80319,86631, 4,80533, +79968,86635,79967,86637,80498,86635,80519,86641,80497,86635, 2,86644, +80519,86647, 2,86645,80501,86651,78670,86635,77859,86655,77952,86635, +77857,86659, 0,86635,21605,86663, 587,86665, 7,86635, 483,86669, +18967,86671, 0,86634,19917,86675,58930,86675,86673,86675,56555,86675, + 587,86682, 587,86675,56555,86686, 7,86634,58931,86691,86675,86693, +86677,86691,86679,86691,19916,86691,86675,86701,86265,86691,86667,86691, +86689,86691,86685,86691, 1275,86691,18967,86712,86675,86715,18967,86691, + 1275,86718,86675,86721,29023,86635,25513,86635,24619,86635,84363,86635, +84723,86635,84471,86635, 1216,86635,77751,86737,77877,86739,77751,86736, +77857,86743,63610,86635,63291,86635,84321,86635,86028,86635,86030,86635, +85960,86635,86437,86635,76631,86635,76601,86635,74682,86635, 8974,86635, + 413,86635,77751,86769,77837,86771,77751,86768,77859,86775,63493,86635, + 483,86778,85875,86778,29598,86635,25545,86635,59206,86635,15029,86635, +56555,86790,62942,86635,80030,86635,79987,86635, 1011,86798,74571,86635, +85875,86802, 483,86802, 8861,86635,85875,86808, 483,86808,79926,86635, +79967,86815,85875,86635, 8861,86818,63493,86818,74571,86818,79263,86635, +76816,86635,77387,86829,76500,86635,84185,86833, 11,86833, 903,86635, +28715,86838,56555,86635,15029,86842, 483,86635,63493,86846,74571,86846, + 8861,86846, 1011,86635,79987,86854,60857,86854,10073,86635,27855,86635, +62278,86635, 587,86635,28715,86866,29026,86635,60857,86635, 1011,86872, + 1347,86872,28715,86635, 11,86879,84185,86879, 903,86878, 587,86878, + 1347,86635,60857,86888, 2,86634, 4,86893, 7,86895, 3,86634, +85125,86899,37663,86899, 2,86635,79962,86905,79965,86907,80497,86905, +80515,86911,80497,86904,80519,86915,79757,86904,79967,86919,71229,86904, +77387,86923,77751,86905,79065,86927, 5,86929, 4,86928,86931,86933, + 4,86929, 5,86928,86937,86939,77751,86904,79139,86943, 5,86945, + 4,86944,86947,86949, 4,86945, 5,86944,86953,86955, 3,86635, +85217,86959,85121,86959,86897,86959,86217,86959,85133,86959, 6,86959, + 6,86958, 7,86958,86971,86975, 7,86959,86973,86979,60755,86959, +35927,86959,38155,86959,37611,86959,71229,86958,84185,86991, 11,86991, +79987,86634,79757,86997, 2,86999, 3,86998,87001,87003, 3,86999, + 2,86998,87007,87009,80150,86635,79139,87013, 5,87015, 4,87014, +87017,87019, 4,87015, 5,87014,87023,87025,84767,86635, 1368,87029, + 7,87029, 2,87032, 2,87033, 1369,87029, 2,87029, 7,87040, + 3,87029,87037,87045, 2,87028,87039,87049, 3,87028,87035,87053, +87043,87053,87031,87053,71229,86635, 7,87061, 2,87062, 2,87063, + 1368,87061, 1369,87061, 2,87060,77387,87073,87071,87073, 3,87060, +87065,87079,87069,87079,84185,87079, 11,87079, 2,87061,77373,87089, +76635,87089, 7,87088,87079,87095,72519,87089, 3,87061,87067,87101, +85875,87101,77751,86635, 413,87106,77859,87109, 413,87107,77949,87113, + 1216,87106,77857,87117, 1216,87107,78669,87121, 2,87107,79987,87125, + 5,87127, 4,87126,87129,87131, 4,87127, 5,87126,87135,87137, + 2,87106,79139,87141, 5,87143, 4,87142,87145,87147, 4,87143, + 5,87142,87151,87153,79757,86635,80450,87157,80451,87157,80337,87157, + 3,87162, 3,87163, 2,87157,79971,87169,79841,87169,79961,87169, +87167,87169, 3,87157,80337,87178, 2,87156,79967,87183,87179,87183, +87159,87183,87181,87183,87165,87183, 3,87156,87161,87195,87169,87195, +77751,86634,87195,87201,87169,87202,87198,87201,87187,87201,87178,87201, +87183,87211,87169,87201,87195,87214,87157,87201, 3,87218,87183,87221, + 3,87219,87169,87225, 3,87201,87157,87228,87183,87231,87157,87229, +87195,87235,80336,80533,80515,87239,80501,87240,80498,87239,80519,87245, +80501,87239,80515,87248,80516,87239,80525,87239,80497,87239, 2,87256, +80519,87259, 2,87257,80501,87263, 2,87239,80497,87267,80515,87269, +80497,87266,80519,87273,77751,80533,79962,87277, 2,87279,79971,87281, + 2,87278,79967,87285,80070,87277, 4,87289,80083,87291, 4,87288, +80079,87295,79968,87277,79967,87299,80080,87277,80079,87303,80498,87277, +80519,87307,80298,87277,80319,87311,80497,87277, 2,87314,80519,87317, + 2,87315,80501,87321,80293,87277, 4,87324,80319,87327, 4,87325, +80301,87331,81857,87277,78453,87277,80233,87277,80445,87277, 8453,87277, + 273,87277,84484,87277,85230,87277,84531,87277,85277,87277,21522,87277, + 1516,87277,38266,87277, 948,87277,20239,87277, 1477,87277,37095,87277, + 281,87277,84473,87277, 1011,87370,85215,87277, 903,87374,80242,87277, +80448,87277,80447,87277, 903,87382,80241,87277, 1011,87386,38057,87277, + 903,87390, 165,87277, 903,87394,21315,87277, 1011,87398, 1505,87277, + 1011,87402, 1216,87276, 412,87276, 413,87277,87409,87411, 1217,87277, +87407,87415, 413,87276, 1216,87277, 1217,87276,87421,87423, 412,87277, +87419,87427,12377,87277, 375,87277,57522,87277,67028,87277,67800,87277, + 510,87277, 240,87277, 265,87443,77750,87445,77751,87444,87447,87449, +77751,87445,77750,87444,87453,87455, 264,87277,84517,87459,85263,87459, +81749,87459, 341,87459, 264,87276, 375,87469, 241,87277,81464,87472, +81465,87473,87475,87477,81464,87473,81465,87472,87481,87483, 1011,87277, +84473,87486,80241,87486, 1505,87486,21315,87486, 903,87277,85215,87496, +80447,87496, 165,87496,38057,87496, 483,87277,65869,87506, 9,87506, + 9,87277,56555,87512, 483,87512,56555,87277, 9,87518,65869,87518, +65869,87277, 483,87524,56555,87524, 5,87277,84185,87531, 1011,87533, + 2,87531,80532,87536, 3,87531,85215,87541, 3,87530,87539,87545, +85263,87545,84517,87545,81749,87545, 2,87530,87533,87555, 4,87276, +87531,87559, 2,87561, 3,87561, 2,87560,87565,87567, 3,87560, +87563,87571, 3,87277,85111,87575, 903,87577,87559,87575, 0,87581, + 1,87580,87583,87585, 1,87581, 0,87580,87589,87591, 4,87575, +80532,87594, 5,87575,84473,87599, 5,87574,87597,87603,84517,87603, +85263,87603,81749,87603, 4,87574,87577,87613, 2,87276,85663,87617, +85100,87617, 9,87617,84767,87622,84767,87617, 9,87626,87575,87617, + 4,87631, 5,87631, 4,87630,87635,87637, 5,87630,87633,87641, + 2,87277,79962,87645,79965,87647,80497,87645,80515,87651,80497,87644, +80519,87655,79962,87644,79967,87659,87559,87645, 7,87663, 6,87662, +87665,87667, 6,87663, 7,87662,87671,87673, 4,87645, 1011,87677, +77751,87679,77750,87678,87681,87683,77750,87679,77751,87678,87687,87689, + 4,87644, 265,87693,77750,87695,77751,87694,87697,87699,77751,87695, +77750,87694,87703,87705, 3,87276, 375,87709,87645,87709, 4,87713, + 5,87713, 4,87712,87717,87719, 5,87712,87715,87723, 4,87277, +80070,87727,80077,87729,80293,87727,80315,87733,80293,87726,80319,87737, +80070,87726,80079,87741, 2,87727, 903,87745,77751,87747,77750,87746, +87749,87751,77750,87747,77751,87746,87755,87757, 2,87726, 265,87761, +77750,87763,77751,87762,87765,87767,77751,87763,77750,87762,87771,87773, + 5,87276,87575,87777, 7,87779, 6,87778,87781,87783, 6,87779, + 7,87778,87787,87789,87645,87777, 0,87793, 1,87792,87795,87797, + 1,87793, 0,87792,87801,87803,87727,87777, 2,87807, 3,87807, + 2,87806,87811,87813, 3,87806,87809,87817 +}; + +const int s_nDataSize2 = 24772; +unsigned int s_Data2[24772] = { + 3, 9, 5, 6, 119, 796, 636, 113, 449, 235, 239, 105, + 789, 96, 439, 624, 630, 230, 111, 110, 787, 786, 628, 627, + 116, 115, 436, 435, 227, 226, 93, 92, 792, 791, 100, 99, + 443, 442, 633, 632, 128, 183, 234, 178, 233, 85, 762, 203, + 32, 742, 356, 169, 152, 12, 672, 741, 127, 168, 671, 740, + 50, 352, 149, 64, 414, 197, 276, 751, 722, 396, 275, 434, + 721, 785, 274, 795, 720, 448, 273, 719, 474, 758, 718, 392, + 269, 748, 715, 400, 118, 794, 635, 81, 76, 750, 747, 597, + 595, 91, 433, 225, 74, 745, 680, 565, 268, 476, 271, 761, + 350, 678, 345, 567, 757, 471, 470, 563, 469, 561, 551, 461, + 465, 706, 650, 714, 261, 332, 641, 708, 676, 542, 260, 458, + 755, 314, 325, 658, 288, 349, 760, 654, 656, 564, 562, 644, + 552, 642, 550, 259, 252, 254, 322, 311, 329, 109, 784, 626, + 43, 395, 182, 55, 399, 186, 104, 447, 238, 41, 389, 173, + 175, 193, 485, 580, 579, 734, 25, 733, 528, 548, 310, 546, + 344, 526, 308, 560, 342, 525, 556, 558, 523, 524, 522, 520, + 307, 622, 623, 431, 618, 620, 429, 617, 305, 615, 519, 518, + 516, 778, 304, 250, 302, 776, 216, 218, 774, 215, 301, 300, + 514, 771, 513, 541, 298, 512, 297, 535, 321, 508, 510, 319, + 538, 539, 533, 507, 509, 296, 146, 705, 531, 537, 317, 557, + 341, 143, 697, 555, 544, 339, 613, 426, 217, 773, 619, 428, + 221, 780, 611, 610, 424, 609, 769, 423, 422, 768, 213, 625, + 783, 670, 732, 782, 578, 577, 731, 484, 387, 668, 669, 171, + 172, 375, 191, 575, 729, 482, 446, 667, 237, 574, 728, 481, + 398, 666, 185, 573, 727, 480, 394, 665, 181, 572, 695, 694, + 140, 738, 372, 246, 690, 689, 136, 244, 369, 767, 212, 766, + 702, 701, 367, 257, 700, 315, 726, 382, 59, 407, 190, 52, + 366, 157, 65, 417, 201, 63, 413, 194, 686, 692, 243, 248, + 699, 256, 150, 354, 365, 364, 353, 156, 685, 348, 242, 661, + 192, 409, 376, 378, 411, 164, 569, 717, 473, 391, 660, 177, + 189, 406, 374, 371, 405, 162, 200, 416, 363, 362, 415, 155, + 313, 324, 361, 360, 331, 312, 328, 381, 316, 330, 359, 323, + 4596,24359,41366,41344, 4857,37718, 4594,24357, 4846,37706, 4678,26650, + 4592,24355, 4844,37704, 4591,24354, 4843,37703, 8816,41900,22056,15422, + 7473, 8464,42932, 8814,41898, 6804,20483,12853, 8462,42930, 8809,42808, +21978,15306, 7385, 8456,42895, 8807,41327,21965,15246,15293, 7372, 8454, +42893, 3212,20457,36983, 3516, 6187, 6184,20453,29352,10563,10421, 2593, +20452, 6183, 5637, 2589, 6182, 2645, 2619, 2283,26212,20199, 2269,12513, + 2614, 2661,18113, 2288, 4777,35226, 4775, 4774, 4773,42536, 2198,32674, +32753, 8833, 8780,40027,43084,39938,43445, 8548, 8831,24157,26148,31156, +29057, 8503, 8771, 8777,24156,26147,36425,33823,40025,43073,43081,39936, +43443,22500,18803, 8829, 8770,40023,43072,39934,43438, 8546, 8827,24155, +26146,31154,29044, 8501, 8765,24153,26140,36424,33800,33820,40022,43066, +39933,43435,22498,18801,43065,40020,43434,39931,18800,22493, 8764, 8499, +26128,24151,33799,36411, 8826, 8544,26120,24146,29025,31141, 8762, 8824, +43063,40019,43432,39929, 2287, 2286,21235,26865,13688,26627,13207,20826, + 2106, 2073, 4667, 4378, 1966, 4054,13206,20825, 2159, 2168,18699,39853, +10355,39675, 2167,39852,13205,18520, 2158,39674,20824,20823,42653,42280, +18519,10116,21390,13926, 2032,42279, 1932,42652, 2166, 2157,42651, 5184, +42278, 5035, 2070,21389,20822, 4377,13925,13204, 5034, 5183,13924,21388, + 5033, 5182,39673,42650,39851,42277, 4221, 5142,18518,21387,18698,20821, + 4220, 4982,10115,13923,10354,13203,39850,42276,39672,42649,20820,18697, +42648,39671,42275,39849,18517,21386, 4376, 5032,13202,10353,13922,10114, + 4375, 5181,20819,18696,21385,18516, 4981, 5141,42274,39848,42647,39670, + 5031, 5180,42646, 5140,42273, 4980, 4219,21384,20818, 4374,13921,13201, + 5135, 5174,39653,42257,39831,42630, 5172, 5133,24141,26112,31138,29023, + 5026, 4969, 4975,24140,26111,36408,33796,39651,42249,42254,39829,42628, +21363,18686, 5139, 5179,39669,42272,39847,42645, 5178, 5138,18515,20817, +18695,21383, 5030, 4979,10113,13200,10352,13920,39668,42271,39846,42644, +21382,18694, 5170,24183, 5169,36516, 5168,39992, 5167,22753, 5166,21404, + 5165,39867, 5164,21361, 4045,26846, 4044,21215, 5162,39827, 5191,21359, + 5190,39825, 5189, 5160, 5159, 4043, 5157, 5024, 1891,42248, 1847,42621, + 4193, 4341,18472,20750, 1681,21357,10093,13145,13869, 2660, 7371, 2659, + 2658,13144,20749, 1846, 1890,39824,18682,39649,10344, 1872,39823,18507, +13143, 1889,39648,20748,20747,42620,42247,10107,18506,21356,13868, 1871, +42246, 1843,42619, 2205,26240,26211, 2200,24249,24232, 2478, 2517,36625, +34562,31491,29570, 2507, 2558,34076,37229,29433,31635, 8704, 8600,35491, +38460,30352,32396,28999, 8171, 8335,38314,35364,32268,30297,30134, 8698, + 8591,35488,38453,30345,32384,32389, 8166, 8333,38305,35357,32255,30292, + 2285, 7325, 1898,14098, 1896, 1895, 1894,39359,24288,41706,41800,20017, +37217,39357,36575,18022,26236,20013,24246,39355,39925,18013,18779,20001, +22482,39395,20196,39397,20208,40017,39923,20064,42324,12370,42658,19065, +18776,41387,41382, 9904,21392,10687,10412,41365,41360,18055,18053,39685, +20893,39857,18775,43056,43431,42334,18524,42660,22479,40016,39922,14502, +15946,39376,39374,20062,20060,21649,22478,39380,39378,12368,18702,43055, +43430,18057,39680, 9906,39855,39991,21351,22752,39816,39990,39815,22751, +21350,39639,39814,33754,42230,28989,42602,18468,18677,24118,26056,31087, +28988,10090,10312,10318,24117,26055,36340,33753,39637,20700,20705,39812, +18675,42228,42600,42219,42225,18464,42598,21348,39635,39810,13101,13106, +13844,26054,24116,33752,36339,20699,21346,26053,24115,28987,31086,42218, +42595,36338,39633,31085,39808,39667,39845,39666,39844,39665,39843,18693, +18692,10351,10350,18514,39842,13199,13198,10112,39664,20816,20815,10111, +18513,42270,42643,13919,21381,13918,42269,21380,42642,39806,21403,21344, +39866,39805,39865,21343,21402,39804,21342,21175,26830,39802,21173,26826, +21340,39346,24061,39338,32626,39333, 9843,39332,39331, 9814,39328,15936, +39183,11746,19744,25614,39182,39181,39180,19836,42268,11903,42641,17932, +17931,40961,40960, 9771,21379, 9770, 9769,40959,40958,17930,17929,39663, +20814,39841,17928,40957,40956,42267,18512,42640,19835,39179,39178,11902, +11901,39177,39176,19834,19833,19832,19831,39175,39174,11900,18691,40955, +40954,17927,39662, 9768,39840,39173,19830,11899,17926,40953,13197, 9767, +40952,20813, 9766,17925,40951,11898,19829,39172,11897,42266,19828,19827, +42639,11896,40950,21378,13917,39171,39170,39169,39168,14088, 3310, 3309, +35263, 2276,26305, 5245, 6267, 5248, 5445,10636,10624, 5401, 7751, 5390, + 5674, 2548, 5410, 3350, 3931, 3971, 3960, 6123,28071,22800, 8025,27598, +16338, 7478,22061,15427, 3847, 3840, 3822, 5835,26825,21172, 7148,26571, +13617, 6808,20693,13100, 3469, 3729, 3618, 6021,27993,22666, 7965,27534, +16152,16158, 7368,21963,15285,15291, 3513, 3757, 3655, 6026,28010,22688, + 7970,27539,16193, 7376,21969,15297, 3722, 3611,27986,22657,22659,16147, + 3608, 3610,25575,25569,25573,25568, 2720, 2722,23945,23941,23943,23940, + 3606,26233,26200, 2718,24244,24224, 3603,22654,16143, 2716,22466,15931, + 8720, 2547, 2499, 2498,35234, 8719,35524, 7050,35233, 6181,20028, 5622, +20027, 5610, 7023,23939,24648,23938,24484, 5952, 7920,22462,25567,27390, +15928,25566,27757, 5620, 7027,36388,33781,31116,29009, 5970, 7936,33795, +36407,29022,31137, 5612, 7025,24121,24653,24129,24494, 5960, 7922,26081, +27412,26074,27796, 5601, 7014, 6501, 5946,27753,22458, 7917,27387,15926, + 4317, 7284,21850,15156, 5825, 5940, 5824, 5794, 7123, 6732, 6038,28015, +22709, 7982,27551,16225, 7395,21983,15311, 8718, 5673, 7049, 8717,35523, +23937,21560,14272,15352,40871,22739,16272,23936,22733,16263,14776,40870, +21558,14265,23935,21474,14087,15148,40869,22445,15906,23934,41714,40868, +34075,23933,34079,40867,41730,23932,41537,31827,37396,23931,41448, 4494, +23930,41503,23929,26451,38349,32305,23928,24323,23927,40866,31785,37351, +23925,40858, 4472,23924,40862,23923,40853,38197,23920,23919,38475,23914, +23913,38152,23908,40840,28843,38481,23905,23904,23903,23902,23901,23900, +23899,23898,23897,23896,40839,40838,23895,23894,40837,40836,23893,40835, +23892,23891,40834,40833,23890,40832,23889,23888,40831,40830,23887,40829, +23886,23885,40828,40827,23884,23882,40823,40826,40822,23879,23878,40819, +40818,23877,23876,23922,23875,23874,32490,32499,23873,23872,32793,23871, +17197,23870,23869,29043,33819,24320,40817,29655,38507,24317,24309,28839, +33590,23867,23868,23865,40816,25558,25555,23866,40815,23860,23864,21472, +14085,40814,22439,15900,25548,23863,40812,23859,23856,40811,40805,40808, +23858,33581,33583,23854,23881,33580,33592,23853,23852,19635,33579,40804, +23848,23844,33560,33558,35258,40795,23843,33557,33556,35255,25392,23842, +33555,33554,23841,24131,24133,23789,17857,23787,18134,25390,26230,23785, +25387,26229,26199,23783,23782,23781,23780,23779,23778,24120,24128,23777, +17845,23775,27731,25366,18759,23762,23761,23760,23759,23758,23757,23851, +23847,40801,40803,23850,33457,33578,35260,23849,33572,23756,33456,23755, +25328,18750,10402,24219,24218,24217,24239,41600,41620,24216,24215,24238, +29171,33958,24221,24241,24214,24237,24213,24236,24223,24243,24231,24248, +24235,24212,41619,41599,24580,24740,24486,24650,24479,24643,24493,24652, +24536,24696,24535,24695,24534,24694,24533,24693,24640,40701,30096,38438, +24638,24477,35149,24692,24532,24054,24053,24052,24051,24050,23803,17878, +23801,25476,23799,23798,23797,33549, 3281, 2981,35267, 2427, 2429, 2425, + 2424, 2495, 7047, 7649, 2423,21521,14156, 2422,35247,23754,40705,23741, +40684,40700,23725, 4338,40674,23724,40673, 7842, 7748, 7750, 7746, 7745, + 7744, 7761, 7758, 7769, 7651, 7653, 7648, 7647, 7646, 7645,21520,14155, + 6300,21670,14601, 7644,35246, 8354,35392, 7811, 7809, 7813,37284,31745, + 7771,36483,31210, 7788, 7802,36473,31194, 4186, 4465, 4184,13064, 4181, + 4180, 4236, 4246,23721,39799,39630,23720,18459,23719,10083,18671,39628, +33435,13819,10309,39797,28701,21329,36039,30815,42588,42206,25299,25298, +13062,42586,20667,20672,42197,42203,23718,21332,13824,33434,42590,28700, +42208,30814,36038,25297,20676,13069,39022,22404,15866,17830,43413,13837, + 9689,43025,21338, 9687,17828,40683,11384,19518,39000,11387,42593,21639, +19521,42214,14460,40699,20689,13087,24049,24531,24691, 4190,23717,22449, +15912,25296,20692,13099,23753,22427,15895,25337,20666,13060,23716,22403, +15865,25288,20665,13059,13584, 4199, 4197,23711,19508,11363,25282,20745, +13140, 9063, 9396, 9184, 9168, 9338,38785,32799, 9254,23359,17393, 9336, +38782,32791, 9247,23353,17357, 9344,38793,32809, 9282,23387,17431, 9456, +38858,32878, 9094,23244,17141,23710,21540,14212,15278,40657,22649,16130, + 2758,21538,14208, 3347, 5771, 1853, 5082, 1788, 4966,42481,42234, 1708, +26963,42233,26591,42480, 1852, 3346, 3345,42479,37847, 1851,37846, 1835, +13635,42478,42232,37845,26590, 1785,10346, 1834,35043, 1833,37844,13634, + 6891,27206, 6890,42477, 6889,37843, 5951, 5949,22464,27759, 5930, 5929, +22774,28067, 5928, 5927,20495,26455, 5926, 5925,26249,26192, 5924,26307, + 5934, 5932,27724,22402, 5923, 6119, 5922, 7262,27717, 5920, 6917, 6277, + 5918, 5917, 5916, 5915, 5914, 5954, 6118, 6927,27234, 6888,42759,27205, +29027,43024,42739, 8746,29048, 8768,43069, 6886, 6887,27204,42738,28699, +43023, 8745, 8742,43021, 8741,43020, 8740,43019, 8784,43196, 8739,43018, + 8767,43068, 8523, 8744, 4957,42196, 5672,42195, 4956,42194,24230,42193, +33939,33938,24127,42192,33769,35199,24492,42191,35198,33768,24688,20662, +24528,20661,24048,41181, 4990, 4842,42304,37700, 5671, 5670,42303,37699, + 2546, 2545,37881,42519, 5669, 5668,37880,42518, 2544, 5667, 5666, 4137, + 5106, 5104, 5072, 5070, 5074, 5076, 5068, 5067, 5066, 5065, 5064, 5063, + 5100, 5096, 5095, 5094, 5084, 5062, 4634, 5061,42190,28698,42457,26562, + 4635, 5091,42302,26982,42517,26626, 4666, 5090,37879, 4989,13687,42301, +42516,37878,26981, 4665,10365, 4136,35060, 5089,42515, 4988,26625,42300, +42514,26624,26980, 4664,42299, 4135,26979, 5088, 4134, 4987, 4663,42298, +42513, 2163,37877,13686, 2162,42512, 2150,26623,37876,10364,35059,13685, + 2161,42297, 2142,37875, 2140,42511, 2149,42296, 5087, 4986, 1917,26978, +42295,26622,42510, 2148, 4848, 4862, 4827, 4826, 4841,37698, 4882,13463, +42509,42294,37697,26621, 4840,10189, 4881,34876, 4839,37696, 4880,13462, +37874,13684, 5086,37695, 4985,13461,42293,42508,37694,26977, 4838,10188, + 4879,34875,23729,34834,40698,40656,23709,34880,40655,41381,24211,34827, +41560,41559, 4874,34826, 4876,34861, 4892,34905, 4878,37693,13460, 4898, +37778,13508, 8353,35391, 8390,35436, 8581, 8534, 8602,38462, 8683,38525, +15448, 8641,38478, 8579,15277,38435,15138,33431,16652,33730,33429,34560, +29423,29381,40645,41866,33391,26227,33910,40643,18131,33513,11326,11531, +33511,28664,40769,33510,40787,33508,28642,40785,33751,18774,41232,27772, +33679,28943,12118,40634,40633,33677,28941,40629,40632,40628,33675,28936, +41598,41618,33605,33597,33360,33359,33358,33357,33787,39161,39321,39320, +36288,34527,26330,41849,18165,34522,26325,33349,34055,41845,18161,34536, +39160,38969,23693,41793,39327,39626,32091,42030,33307,32090, 4703,33453, +28723,25230,25229,33306,28623,26436,26446,33608,33305,40807,35522,19640, +33304,40825,35232,19645,33303,34053,33301,34044,34048,33298,41691,33296, +41683,41686,33295,33536,33540,33535,33292,33287,33282,33286,33935,33933, +33279,40631,33711,41180,33709,41173,35230,19918,33708,41172,35520,19917, +33707,41171,33577,28797,33278,28611,33892,29148,33886,29144,33277,28610, +33276,28609,33275,28608,33274,28607,33291,28620,33273,28606,43071,33822, +43437,35917,33272,28605,36118,33548,30917,35916,33484,35981,35979,30906, +28760,30775,33546,36116,33383,36083,28645,33367,28749,33482,33271,35915, +36081,30729,33270,28604,39412,30728,39470,35914,33309,33311,39410,39456, +18196,25216,28625,35913,39468,39505,20324,25215,30734,41748,35944,41830, +23695,23697,28638,39458,33331,39507,25232,25234,30736,33269,41797,41832, +18192,23675,35964,35966,41701,41752,20319,23674,35912,30727,41697,28603, +41792,33268,33267,28602,36620,34558,31487,35911,33425,33427,36622,28601, +37113,31476,28689,35910,31489,33266,34471,37010,34556,30813,36021,36023, +29384,34420,37214,28675,33409,33411,31478,37012,30791,33265,29426,35909, +37115,34422,36005,36007,34066,30726,34473,29386,35908,30725,34068,37210, +29428,33264,39067,39069,23701,30724,23703,35907,23673,39065,33386,25214, +23672,39063,28670,25213,36000,30777,40776,40768,25250,25252,33263,40764, +28600,40762,33262,28599,43013,30723,43400,35906,23671,21636,22383,25212, +14454,15824,15837,33740,28975,43052,33261,43429,36320,33739,28974,43051, +31074,43428,36319,24075,31084,21648,28986,22477,26012,36329,14493,33749, +15945,43398,43011,22377,15823,14439,21634,26052,24114,33366,33365,28644, +33480,28747,36111,33529,30902,36079,33473,33260,33259,33972,33258,33553, +33285,28618,41597,41617,33284,33970,33294,33539,33528,28778,11448,40591, +40590,33526,33525,33524,33523,39058,39057,23835,30901,23815,36110,33479, +25517,28746,25494,40753,40752,33538,28787,40610,40608,33522,33780,40589, +42861,23670,33778,35362,33256,35344,33254,33463,34043,29414,41685,41783, +34041,29411,26400,26365,34040,41682,34039,34052,29420,34038,29410,34037, +29409,34065,34064,29425,34036,29408,39406,31565,39466,37204,34456,39450, +18188,26395,37101,39499,20313,26360,41742,41824,24283,24274,29519,34552, +31485,41780,36616,41681,34035,29407,36615,34551,31484,37203,34455,29376, +37100,31472,34411,37003,34047,29418,41695,41790,34046,34050,34034,34073, +34032,33930,33941,29158,33932,29155,33929,29153,33798,41083,18043,35223, +35283,35285,35154,35225,35169,30109,40588,40587,35146,30088,22376,15822, +35144,30086,22374,15820,35142,30084,22372,15818,39529,39623,35148,35209, +39159,35167,39318,35262,30208,40586,40585,35254,30204,41614,41595,35257, +30206,40794,40799,35162,40709,18758,34825,35269,35266,10793,10619,35245, +10783,10561,35251,35243,35242,33724,33728,33722,33721,33968,34685,29738, +40704,40703,34727,29780,41380,41359,34710,29763,20739,40561,13138,40560, +34684,29737,20657,40559,13052,40558,33851,27196, 9902,42736,26993,36435, +42547,42321,12366,27711,36437,43010,33439,42468, 9680,42735, 9685,36043, +11382,33445,11374,42216,36041,43009,33849,37911, 9900,42734,35066,36431, +37907,33847,12364,27710,36433,43008,35081,12602,37746,35083,35007,31772, +37338,35022,35015,35047,35005,35004,35003,35002,35001,35000,10302,35071, +10368,35013,35012,35011,34999,10301,13577,37817,34998,42180,10300,28558, +17770,37820,19393,34996,13579,42450,37818,26546,35045,35058,42292,10363, +26976,10362,37873,13683,35057,13682,42507,37872,26620,35056,37692,10361, +13459,34874,37871,37691,35055,13681,10187,37870,34873,35054,37869,10360, +13680,35053,37868,33660,25733, 9805,40999,26975,36216,42506,42291,11876, +25732,36215,40998,33659,42505, 9804,40997, 9803,36214,11875,33658,11874, +42290,36213,40996,33657,37867, 9802,40995,35052,36212,37866,33656,11873, +25731,36211,40994,33655,37690, 9801,40993,34872,36210,37689,33654,11872, +25730,36209,40992,33496,34824,18484,18433,34860,34891,18526,34871,42289, +10186,26974,10185,37688,13458,34870,13457,42504,37687,26619,34869,37686, +10184,13456,34868,37685,34922,37771,10204,13501,34915,37764,35355,41246, +40557,24074,35361,40556,41290,23645,35390,35394,35388,35387,35423,35431, +35421,35420,35419,34424,34430,29390,34410,29375,34409,29374,34408,29373, +43003,34454,43397,37099,34419,34418,29383,34554,25244,41863,17813,34550, +27709, 2281,26243,26215, 2280, 2279,26242,26806,26214,26542,23643,17768, + 9617,19682,25590, 9615,11631,25602,17766,11650,19701,23641,24043,17975, + 9857,19915,25886, 9855,12116,25884,17973,12114,19913,24041,24525,18827, +24685,10438,18963,10587,18825,10436,24523,10585,24683,18961, 2443, 2442, +18824,26805,10435,26541, 2441,21154,13576, 9256,38787,32802, 9246,38781, +32790, 9253,38784,32798, 6490, 6682,39661,39839, 6676, 6675, 6674, 6673, + 6672,39158, 6697,23640, 6775, 6783,36532,31231, 6580, 6579,18960,26804, +10584,26540, 6578,21153,13575, 6857,38536,32501, 6867,38538,32503, 6855, +38534,32498, 6822,20490,12860, 7477,22060,15426, 6814,20485,12855, 7472, +22055,15421, 8988,38813,32831, 8677,38488, 8239,38346, 6280,21655, 6279, +21654, 6320,23639,23638,23637,23636,40555,23635, 6274, 6315,38953, 8751, + 8823, 6245,23632,23631,14039,20651,37335,31769,15808,34822,24166,24163, +37375,31808,23727,25314,39344,39342,23630,14031,25171,15799, 6241,42999, + 8753,42998, 6240,42997, 6239,42996, 6238, 7605, 7603,21633,24145,14431, +24150,23743,21632,14430, 7577, 7576,21645,23768,14481,23764, 6237, 6236, +21631,42614,14429,42241, 6235,21630,14428,24073,24072,24071,24070,41264, +24069,24066,24065,37331,31766,24064,26009,24063,21647,14491, 8775,43079, + 8774,43078, 8737,42995, 7643,21669,14595,23622,21711,14828, 7827, 7826, +21710,23621,14827,23620, 7824,21709,14826, 6347, 6349, 6351,38519,32485, + 6362,38530,32492, 6364,38532,32494, 6358,38528,32489,18365,24583,18606, +18598,18597,18596,42444,37811,24657,24497,24634,24469,35138,24040,24039, +24522,24682,24681,24521,24578,24738,39405,39465,41680,20283,41779,18147, +18187,41741,20312,39449,41823,39498,24520,24680,24679,24519,24038,24037, +39157,39156,40949,19826,40948,17924,23619,23846,24202,35241,41126,33720, +25977,35386,41125,33719,25976,33718,41124,24482,24646,23834,23814,19058, +10678,43037,36075,43422,30854,19057,10677,42994,35892,43396,30674,15796, +22352,24144,24149,23613,23612,19134,10762,42990,37248,43394,31694,18444, +10075,18466,10092,18432,10073,18431,10072,18552,10124,18550,10122,23611, +23610,24036,24035,24518,24678,24677,24517,18454,10080,23609,23608,22370, +22368,15817,18448,25149,25148,18744,18741,10391,20660,23752,23751,22426, +22425,15894,18430,25336,25335,18757,18756,10405,20648,24161,24159,22349, +22348,15794,18522,26155,26150,18733,18732,10388,20880,35161,40708,34821, +25334,18505,10106,18494,10104,23607,23606,19397,19395,11212,18488,25147, +25146,17776,17773, 9620,20742,18381,38855,18383,23136,18385,38407,17765, +38850,17764,23121,17763,38405,17762,43195,17839,43034,17985,42989,17984, +42731,28965,42744,31065,17998,28977,19979,31082,42988,19997,43054,18379, +37275,17761,42173,28551,26945,42439,42170,28550,26539,30673,42436,42437, +19380,26536,26537,17760,42307,28549,30672,19379,42537,17759,42176,28548, +26948,37814,17758,34994,19378,30671,42446,19377,37812,17757,42746,28547, +30670,19376,43059,18265,10019,18264,10018,42987,20311,43393,18186,18921, +18906,18919,18915,18807,18731,38831,18730,23104,18729,38403,18869,18878, +18890,18823, 1666, 1701, 1613,22346,27707, 5904,15787,15792,27365, 7899, +15124,15129,21836, 7255, 1669, 1704, 1616,22339,27700, 5897,15785,27358, + 7892,15122,21829, 7248, 1664, 1699, 1611,22772,28065, 6116,16309,27593, + 8020,15404,22040, 7459, 1663, 1698, 1610,21183,26835, 5820,13632,26587, + 7143,13137,20738, 6799, 1829, 1839,37855,42487, 1838, 1828,42486,37854, + 2028, 2063, 1963,40545,40543, 2030, 2065, 1965, 2026, 2061, 1961, 2025, + 2060, 1960, 2154, 2146,37897,42535, 2153, 2145,37896,42534, 1827, 1837, +37853,42485, 1662, 1697,21182,26834, 1622, 4117, 7502, 7272, 7274,27373, +27378,27730,27742,17756,17854, 7245,27355,27697,18094, 7287,27392,27763, + 6313, 7244,27354,27696, 6266, 7243,27353,27695, 6311, 7242,28038,27694, + 7421, 7241,27352,27693, 6335, 7267, 7266, 7265, 7494,23605,23604,25145, +25144,23603,25143, 7324, 7323, 7322, 7321, 7320,20812,13196, 7319, 7318, +21377,13916, 7317, 4373, 7238,26225,26196, 8973,23120,16979, 8991,23130, +16996, 7361, 7359,33574,27527,28795,27980,30929,36127,23669,25203,23595, +25135,23750,25342, 8946,23103,16963,18369,38399,17755,38397,18727,38395, +39660, 8200,39838, 8199, 8198, 8197, 8196,39155, 8195, 8452,38335,42884, + 8140, 8431,38284,42842, 8238, 8459,38345,42916,17754,17853,18767,18068, + 4795, 4925,37367,42056, 4484, 4737,42533,37895, 4794, 4924,37366,42055, + 4483, 4736,37894,42532, 5148, 4829,39792,26808, 5188, 4825,39864,26903, +39989,27146, 4804,37381, 4788,37328, 4856,37595, 4824,37717, 5146, 4855, +39788,26801, 6299,27264,27094, 4457, 7316, 4456,27091,27262, 6297, 4299, + 4454,27752,27386, 7280, 4453,27978,27525, 7357, 4452,28047,27577, 7432, + 4451,27140,27285, 6329, 4450,27692,27351, 7237,13569, 4475,27089,27259, + 6295, 6696,21031,13404,34709,29762, 4560,34755,29808, 4581,34683,29736, + 4540,33871,35166,33467,24062,23862,25554,23594,25134,23714,23713,25286, + 4462, 4464, 4447, 4446, 4445, 4444, 4443, 4442, 4441, 4490, 4488, 4440, + 4439,34673,29726, 4521,33234, 4460, 4438, 4471, 4714, 4116, 4473, 4716, + 4119, 4497, 4755, 4167, 4493, 4495, 4753, 4165, 4466, 4711, 4111, 6367, +21723,14912, 6860,21744,15033, 9259,23362,17407,23593, 6328,21692,14780, + 6339,21695,14794, 6324,21690,14774, 4486, 4739, 4149, 4482, 4735, 4147, +14023, 4480, 4733, 4145, 4479, 4732, 4144, 4793, 4923,37365,42054, 4807, +37432, 4864,37753, 4806, 4936,37431,42088, 7907,23592,42849,23591,42851, +23590,42847, 8458,42915, 4918, 4916, 4911, 4910, 4909, 4933, 4932, 4931, + 4908, 4907, 4914,33979,42019,18138,33978,33765,42018,18021,35195,35194, +42017,18787,33764, 4922, 4792,42053,37364, 4747,26177, 4701,26259, 4705, + 4704, 4700, 4699, 4698, 4724, 4726, 4741, 4152, 4707, 4109, 4697, 4101, + 4696, 4100, 4695, 4099, 4694, 4098, 4693, 4097, 4745, 4156, 4749, 4162, + 4751,25133,24475,35152,24229,33977,24126,33763,24491,35193,34428,29388, +37024,34477,31481,37194,37023,31480,42016,37193,42435,34476,18270,10027, +26730,20331,26939,18213,20344,12777,42015,18212,42434,20330,18798,10416, +26729,20025,26938,18796,21497,14105,42014,18038,42433,21501,18025, 9892, +26728,21500,26937,18037,20021,12308,42013,18795,42432,20024, 4927,42065, + 4906,42012, 4935,42087, 4159, 4161, 4096, 4095, 5108,35087, 5099,35070, + 5057,34992, 4094, 5661, 4092, 5336,14586,14141,11159,11144, 4091, 5658, + 4090, 5522,14874,14302,14990,14331, 5862, 8892,17350,17552, 4089, 4088, + 4087, 5339, 4103, 5225,14403,14016, 5549,35329,26162,10521,41333,26658, +38425,42315,42540,15112,26160,38424,41335,33653,25729, 9800,40991,26618, +36208,42288,42503,11871,25728,36207,40990, 4791, 4921,37363,42052, 4478, + 4731,21062,26749, 4143, 4142,37893,42531, 4053, 4052,21234,26864, 4790, + 4920,37362,42051, 4477, 4730,21061,26748, 2152, 2144,37892,42530, 2059, + 2024,21233,26863, 4141, 4051, 1959, 4140,35970,21026,19466,37325,19470, +37594,35973,20647,36795,37324,20288,21025,20446,20646,37216,37593,36757, +37592,20286,20645,20426,21024,37208,37323,36392,41275,36391,41274,36390, +41273,36562,26297,36140,27688,36146,27687,37256,36530,36566,21023,20236, +37322,20194,37591,36542,20644,36570,37273,38249,36136,30938,42243,33227, +42616,33585,35886,30656,42245,33587,42618,33226,35885,30655,43077,28533, +43442,33225,36062,30839,43032,28725,43420,33455,35884,30654,42323,28532, +42657,33224,35963,40621,35883,30653,28530,41592,37231,31646,37228,31634, +37227,31633,37226,31632,37225,31631,36387,31115,36386,31114,36385,31113, +36384,31112,36134,40810,36142,40821,36071,40723,42984,38423,38141,32157, +38140,32156,38139,32155,38149,27088,38109,42750,38108,42749,38107,42748, +38187,27117,38186,27116,38100,42743,38088,27686,38247,38185,27115,38191, +27124,38214,27132,38159,27072,38158,32171,38157,32170,38156,32169,38228, +27110,38222,32183,38091,32101,38138,27063,36315,41193,31056,36314,41192, +31055,36313,41191,31054,36317,41195,31058,36312,31053,36311,31052,36310, +31051,36488,36486,36503,36501,37279,38775,37277,23368,37238,21750,37281, +21729,38738,38749,38748,38747,38746,38867,37264,27685,37262,31730,42983, +29628,43392,34603,37271,21705,37267,27297,37266,21703,37261,21701,36206, +40989,40988,36205,36204,40987,11870,25727,37684,13455,36203,40986,11869, +25726,37865,13677,36202,40985,11868,36078,42982,36077,40791,11447,37590, +37321,37589,37320,37588,37319,37587,37318,37601,37727,37586,37585,38474, +27258,38427,42992,38441,43041,42981,38422,38491,38421,40661,15111,25290, +37828,13590,37018,41853,37017,41852,37016,41851,37014,41847,37022,41855, +36427,41329,37904,42542,20055,26153,42839,38280,36037,40659,42464,37830, +19498,25285,42838,38279,36429,41331,27347,21820,20057,26158,42837,38278, +37832,42467,37810,42431,37809,42430,37825,31965,37834,31981,37808,31952, +37807,31951,37806,31950,37805,31949,37804,31948,37917,37915,37803,37802, +37901,37900,37801,42429,37800,42428,37799,42427,37910,42544,37909,32040, +37903,32034,37891,42529,37361,42050,21232,26862,21060,26747,37890,42528, +42049,37360,21231,26861,26746,21059,37889,42527,21230,26860,37888,42526, +21229,26859,36250,41019,31021,36249,41018,31020,36248,41017,31019,40536, +36252,41021,31023,36246,41015,37359,42048,19815,25716,41014,36245,36244, +41013,42047,37358,19814,25715,41012,36243,36242,41011,37887,42525,19813, +25714,41010,36241,36240,41009,42524,37886,19812,25713,41008,36239,36238, +41007,25712,19811,19810,25711,41006,36237,38303,27404,38277,32219,32089, +31946,38295,32233,38293,32229,38301,32251,38313,32267,38312,32266,38311, +32265,38310,32264,38316,32270,38322,27497,38321,32275,38320,32274,38319, +32273,38348,32304,37330,37597,39790,37599,37315,37576,39863,37575,39988, +37574,37314,37380,21016,21068,20643,20887,37573,37716,37379,37715,39787, +37714,37313,37312,37393,37311,31761,37371,31804,37429,31832,37334,37333, +31768,37340,31774,37317,31763,37373,31806,37310,31760,37309,31759,37308, +31758,37307,31757,37306,31756,37305,31755,37342,31782,37304,31754,37395, +31826,37357,42046,21058,26745,37356,42045,21057,26744,37384,21073,20905, +37725,37383,42080,21072,26757,36539,31236,42240,33909,42613,33908,36552, +36554,41277,27230,41843,41861,41231,32116,41230,41229,41228,40619,41868, +26318,41870,26320,40616,41138,41136,41859,41151,41140,40535,21494,14101, +23589,22490,15957,40534,22327,15761,11105,12188,25952,21433,14012,41188, +42876,41187,42788,41186,41198,41197,41185,41184,41183,41190,22326,15760, +25107,21432,14011,40532,21476,14091,25705,22469,15935,40531,22471,15939, +25961,21478,14093,40530,40528,40529,40526,40524,40522,40521,40546,40520, +40525,40519,40517,40518,40515,40513,40511,40510,40516,40509,40544,40508, +40527,40507,41533,40506,41451,40505,41499,40504,26449,40503,25565,42936, +22325,41892,27294,40502,40500,40501,40498,40497,22354,40496,27209,40495, +40494,40493,40492,40491,40490,40489,40488,40487,40486,40485,40484,40483, +40482,40481,40480,40479,40478,40477,40476,40475,40474,40473,40472,40471, +40470,40844,40873,40842,40469,40468,40467,40731,40727,40726,41525,41516, +41491,41482,41440,41413,41439,41412,41515,41524,41481,41490,43735,22324, +15759,16624,28157,21431,14010,43733,32591,21430,14009,28150,22323,15758, +43855,22322,43755,43758,43761,43764,43754,43757,43760,43763,43720,43719, +43718,43717,43716,43715,40425,40428,21429,14008,23586,22321,15757,40421, +40427,40420,40539,21428,14007,25691,22320,15756,40538,40419,41106,41105, +41104,41103,41102,41101,41100,41099,41098,41097,41096,41095,41094,41093, +41092,41091,41624,41622,41170,41169,41155,41150,41149,25938,40466,41386, +41364,41377,41358,41385,41363,41376,41357,41362,41384,41356,41375,40682, +40681,40697,40696,40680,40695,42655,42309,42637,42264,42636,42263,42635, +42262,42612,42239,41840,41839,40760,40781,40751,40750,40749,40748,40747, +40746,40745,40744,40743,19610,11499,40767,40742,19609,11498,40922,40921, +40920,40919,19824,11893,40806,19637,40824,19642,43451,43494,43383,43382, +43381,43426,43425,43403,43406,43493,42869,43492,41082,43497,25564,43543, +26448,43380,27232,43418,27208,41130,41128,41123,41122,41121,19910,41120, +19909,41119,21668,14585,25832,19949,12173,41118,21519,14140,25831,19948, +12172,40717,21471,14084,25676,22424,15893,40716,21470,14083,23749,22423, +15892,40715,21469,14082,25333,20638,13037,40714,40713,21468,21467,14081, +18429,25332,25331,18755,18754,10404,20637,43096,43094,43089,43552,43379, +43378,43391,43377,43405,43402,43376,43412,43411,43410,43050,27771,43049, +27770,43154,43150,43147,43146,42787,42875,42786,42791,42879,42790,42878, +42785,42874,42784,42873,42783,42872,42797,42800,42887,42799,42886,42804, +42889,42806,42891,42796,42883,42795,42882,42794,42881,42756,42863,42773, +42771,42766,42151,26932,42150,26931,42149,42148,42147,43496,43152,43542, +43214,43417,43030,42146,42578,43375,42972,42145,26304,42144, 8714,42143, + 4292,42142,41168,42213,26958,42212,26957,42211,42592,19520,28500,11386, +33192,42172,26944,42175,26947,41865,40982,40981,36199,25725,40980,33652, +36198,41589,38420,41594,26258,28531,41575,30651,28499,28529,30616,41573, +28498,26256,41591,41588,26253,29448,41570,31341,29251,41587,37002,26222, +41569,34280,36861,41579,41583,42426,42003,42441,42021,42422,42001,42421, +42000,42420,41999,42419,41998,42418,41997,42549,42554,42417,42416,42415, +42414,42413,42461,42460,42466,42035,42463,42033,42157,42424,42141,42412, +40979,40978,25668,42855,26110,42918,25092,42853,42871,42934,22317,42082, +25091,41996,26252,41995,41994,42037,43149,43093,42770,43091,42768,42068, +26182,43088,42765,42067,41993,41992,41991,41990,41989,41988,41090,42493, +42031,42411,42455,41986,42410,42058,35228,41985,33976,41984,33762,41983, +35192,41982,42064,26720,26751,42079,26756,41556,41555,41554,41553,41552, +41551,41550,41549,41548,41547,28954,36303,29445,28595,30719,28949,31037, +31036,28948,29138,29146,42040,18503,28890,11603,42085,18546,29136,12591, +28992,34983,33191,33607,28497,28496,28495,30615,28494,30614,28598,30722, +28597,30721,28493,40722,28492,43040,28938,33362,33190,28940,28933,33521, +28882,33189,28885,28491,33576,28490,28489,34058,28488,43036,30613,28487, +33738,43048,30612,28486,33188,33589,30951,30611,36138,35850,37007,33187, +34415,37109,35849,34465,37206,33186,33354,35968,35848,34061,36115,33185, +33364,35978,35847,33545,36598,33184,33281,35919,35846,33986,37009,33183, +34417,37112,35845,34470,37005,33182,34413,37103,35844,34460,36002,33181, +33988,36600,35843,33389,36004,33180,34063,37213,35842,33408,35921,33179, +33533,36113,35841,33289,29190,34982,29185,31274,29184,33907,37001,33984, +34407,37098,36596,34453,37212,33407,18346,19496,28643,33544,28617,28614, +28485,28484,28483,28673,28481,28479,29406,31544,29413,33178,34458,29378, +33352,37000,34029,34406,37097,37202,34452,29424,33406,34469,29382,29417, +29281,29422,29317,29405,29250,28838,28836,37849,42483,28814,30940,28478, +28477,37842,42476,28476,30606,28475,28474,38452,43058,28473,30605,28472, +28471,42453,37823,28739,28734,38419,42971,29679,43208,29678,43207,31729, +29627,33177,43206,31728,29626,29677,29650,43205,38505,28776,28775,42970, +36999,33520,34405,37096,36109,34451,35977,33543,17815,19590,28786,28783, +28774,28772,28771,30898,28770,28769,42474,37840,29623,12817,34588,34600, +31712,31658,37250,37234,37236,37269,29630,29589,34609,34590,33919,33906, +33913,33905,42969,31273,29183,33904,31235,36538,33925,31246,36556,36588, +29160,33943,37105,34462,18195,20323,37111,34468,18199,20327,37192,34475, +34981,37798,37021,34427,37199,34486,34447,37041,37036,34442,37198,34485, +34446,37040,37035,34441,37197,34484,34445,37039,37034,34440,37196,34483, +34444,37038,37033,34439,29372,42968,31543,29404,33981,31196,30367,30203, +30366,41981,18427,30356,14108,42029,18442,30336,14049,43491,18868,41980, +32456,32455,30380,30282,32249,32248,37303,10546,37572,14501,20624,30273, +35009,30290,34980,30280,30107,30279,30106,42967,38418,34593,34595,19025, +34597,19027,34592,19023,35427,30962,17897,28470,17732,38480,19333,35429, +19649,30604,38482,28844,35458,30603,17731,29656,18373,38508,20466,35444, +19332,31721,38493,28469,35460,31660,18367,29663,18375,38516,20472,35448, +20459,31725,38495,29587,28963,35440,31716,18371,30078,18726,38490,21427, +35454,20463,32088,38502,29646,29661,38512,29659,38510,34979,37797,17730, +19331,34977,37793,37795,35338,30602,17729,30097,18746,38439,21463,35336, +19330,32099,38437,28468,29180,29178,35416,31261,18112,29162,38477,20192, +35434,31268,18127,29166,38486,20217,35328,31264,18119,29164,38417,20198, +35327,31092,18019,30124,18785,38416,21482,35326,20004,32123,38415,28995, +34436,37030,18144,20280,34399,36995,29581,31652,29460,31502,31466,29366, +29003,31106,30139,30601,32132,28467,30142,30600,32143,28466,31130,29012, +34426,37020,17728,19329,34393,36989,28465,30597,29451,31497,31451,29356, +29455,29447,31340,30077,32087,31494,29249,30178,30202,30177,30299,30201, +30200,34404,36998,18725,21426,34279,36860,29360,30138,38134,36982,29363, +30425,38670,36988,29362,36987,29359,36981,29848,31780,31779,37302,10167, +37571,13097,20623,29838,31753,31752,37378,10150,37713,13026,20886,29854, +29837,29836,29887,36236,41005,31801,32030,31800,32029,41004,36235,37355, +42044,31799,32028,31798,32027,29877,29972,42043,37354,31797,32026,31796, +32025,29876,29971,37885,42523,31795,32024,31794,32023,29875,29970,42522, +37884,31793,32022,31792,32021,29874,29969,29873,29968,31791,32020,31790, +32019,29872,29967,37442,31835,31838,29892,20945,13294,37758,10198,37441, +42091,31834,32051,31837,32054,29891,29979,29963,29965,29961,29949,29948, +29947,29946,29945,29976,29975,29974,29956,31972,31971,41977,33975,31945, +33761,31944,31943,35191,29944,31942,31941,42063,42090,32050,32053,29978, +37353,42042,31017,31016,31015,31014,28924,28923,37883,42521,31013,31012, +31011,31010,28922,28921,36234,41003,31009,31008,28920,31007,31006,28919, +36573,36559,30749,41750,39927,27061,36085,28663,39519,26343,39521,26345, +39517,38971,39061,30955,30953,28834,28832,26597, 9597,20771, 9596,30942, +28812,13649,13159,36568,29087,20211,36550,30974,39555,30976,31207,31202, +39566,31213,39568,31215,32332,32324,14975,14864,32331,30971, 9587,30784, +30885,11334,11536,31035,28947,31034,28946,31033,28945,31032,30680,30682, +31027,43145,27971,38105,36361,38104,36360,38103,36359,38102,36358,30596, +30904,30595,30731,30594,30858,30593,30922,30592,31569,30591,31279,41602, +28464,40779,28463,40729,28462,30788,30920,30787,30919,30589,32108,30587, +30851,40741,31218,31282,31217,33158,31244,33923,33157,31281,31243,33922, +31223,31649,31222,33156,34481,31229,33155,31648,34480,31228,31103,41708, +30586,41705,30751,34057,30585,34060,30837,33551,31227,34479,33921,31242, +30584,33388,31238,33912,30583,33405,34467,30582,30581,34464,31590,33351, +33917,31240,31277,33471,30580,33542,30579,35822,28461,24137,33154,24135, +30578,35821,28460,20315,33153,18190,30577,35820,28459,20322,33152,18194, +40766,40775,28458,28669,33151,33385,31272,30897,33903,31234,31271,34450, +33902,31233,31270,36595,29182,20310,33983,18185,30576,35819,28457,20059, +33150,18701,40773,28667,42406,40772,30889,33149,30856,33148,31111,42577, +31110,31109,12307,41976,20622,30843,33147,31877,31865,31864,31875,38137, +37570,31904,31542,30896,31541,34449,31540,37201,29403,20309,34026,18184, +31630,42576,31629,31628,12776,41975,20621,31903,43374,33146,31108,31627, +33356,33519,31045,33145,43156,38484,28456,28455,38443,43045,28454,28453, +31152,30572,38414,42966,29040,29047,43204,38501,29676,29645,30895,36108, +28768,23808,33518,23827,31710,42921,31727,29625,36586,36582,36581,36580, +36579,36584,40740,29152,33928,31288,36607,29197,20336,33993,18262,31287, +36606,29196,20335,33992,18261,31286,36605,29195,23570,33991,23569,31284, +36603,29194,20334,33990,18260,37259,22029,43144,27970,32398,33816,32358, +33967,32372,33144,32371,32472,32357,32356,32355,32375,32377,32470,33143, +32469,32454,31940,32488,14328,32491,14344,32497,14301,32500,14308,39361, +26101,32247,10545,39782,26792,32246,31751,14500,13025,32238,39552,32227, +32323,32235,32231,32312,42580,32225,14040,42025,20652,32253,32263,42575, +32262,32261,14104,41974,20620,32260,32259,32300,32243,41593,28452,30571, +31099,41604,28451,30570,28450,30569,28449,41607,29090,41606,29089,30841, +28727,31733,29666,31732,29665,31225,31499,31220,31266,30568,31496,30567, +31263,30566,32131,30565,31105,30564,31091,30563,32122,30562,28448,42539, +28447,37827,28446,32197,32191,32196,32190,31097,31096,31095,38514,20470, +31258,31251,33142,31249,33223,31256,31255,31254,30650,30561,41572,26255, +41586,29175,31260,32084,31062,28962,32185,25060,43143,27969,32154,32153, +32152,32151,21506,32150,21505,37792,38136,43087,27905,42764,27245,41135, +25798,38184,32179,32181,32177,32114,32083,31493,32129,32128,32127,41585, +29248,31771,10157,39784,26794,31750,10149,39862,26902,39987,27145,31749, +31778,13024,13096,31777,10166,39781,26791,33466,43424,35165,40725,33880, +43390,32468,43373,32475,32374,33139,32354,31789,32018,31005,31004,31788, +32017,31787,32016,31830,13286,31829,32048,32045,25059,31976,25310,31939, +26251,31947,31983,31938,31937,32043,12475,31936,14107,31974,14074,31959, +35141,31935,33974,31934,33760,31933,35190,31932,13023,32258,14103,31931, +13022,31930,13021,32223,14038,32221,32257,32014,32013,31992,11595,31990, +31988,31986,31985,31963,31929,37791,29943,21499,34974,18036,29942,34973, +31928,18794,37790,20023,31927,37789,29941,20329,34972,18211,31926,31970, +32047,31003,32903,32767,32894,32750,32590,32608,16656,32623,12282,11039, +32630,32627,16680,32582,16581,32576,32615,32614,32573,32572,32570,32568, +32651,32665,32650,33138,32656,33896,33137,32664,32655,33895,32654,33894, +34023,32564,32563,33517,32562,41669,32828,32827,17502,17501,32745,32744, +16798,16797,32825,17495,32917,17319,32824,32850,17318,32849,32783,17544, +32797,17565,32561,32560,32658,32646,32926,32914,32913,32559,32558,32690, +32795,17563,17381,32684,32777,17541,17317,32672,17200,32670,17194,32669, +32741,16962,32740,32739,32738,32789,32923,32737,32736,32766,32755,10229, +10284,10239,10165,10237,10159,10228,10148,10227,10147,10226,10283, 9863, + 9748,40145,10048,40165,10050,40167,32291,31050,30365,28953,30176,28952, +28951,30343,30129,32290,32168,30364,30175,30174,30363,30199,30379, 9665, +21011,36541,36756,35972,36365,20445, 9752, 9582, 9750, 9581, 9627, 9634, + 9580, 9579, 9578,11036, 9648,11282, 9577,11035, 9667,34545,34581,35202, +33772, 9889, 9885, 9884, 9883,17877,17837,17713,17879,18133,17856,17712, +18096,17858,18098,18135,17711, 9949, 9955, 9953, 9952, 9951,18129,18093, +10037, 9640,23566,23565, 9646,23564,20220,33136,41616, 9694, 9697,23563, +19433,33135,40607,23562,24496,33134,35221,24699,33133,24539,33132,24046, +41177,10234,10296,10225,10282, 9726,21010, 9717, 9716, 9715, 9714, 9713, +11524, 9728,17865,24209,33927,10733,10730,23561,19623,33131,40798,23560, +24148,33130,33815,10024,21009,23559,20317,33129,41789,23694,20321,33330, +41796,40707,35160,25330,18753,34859,10070,18483,13378,34808,10099,18426, +13436,34909,10126,18555,13494,10794,10495,10784,10441,10782,10434,10796, +10498,10799,10512,10801,10515,10281,10224,10280,10223,10290,10231,35069, +34971,10279,26303,10278,10277,24228,33937,24125,24490,33767,35197,24673, +13015,24513,13014,24030,41167,10777,10431,10306,10236,34976,10768,10408, +10359,10183,10358,10182, 9797, 9796, 9795, 9799, 9794, 9793,10191,10196, +10146,10145,34402,34382,34392,34391,34390,34389,17710,18023,26235,26237, +26202,26204,18125,18089,18105,18118,17709,18100,18018,17708,18784,18778, +17707,18012,18092,26221,26195,18091,18724,18111,37836,42470,21174,26829, +37838,42472,21177,26832,37857,42495, 9576, 9575,21216,26848,11031,11030, +36545,41564,20204,26213, 9574,11029,36549,41567,20210,26218,41566,26217, +10511,10644,10497,10634,10420,10555,10514,10653,10508,10637,10403,34807, +14078,18425,36535,41546,20191,26194,10473,10618,10430,10580,10480,10623, +10491,10625,30183,10532,40102,10560,10582,10554,10579,32289,32272,10562, +10590,10628,10770,10617,10776,10620,10632,10550,10549,10548,10655,10630, +10616, 9860, 9852, 9851, 9854,10530,20618, 5566,20617, 9573,20885, 9712, +20616, 4291, 4290, 4289, 4288, 4287, 4286, 4356, 4355, 4354,41343,41339, +40665, 6669, 6709,20614,13010, 6821,20611,13007, 7470,22053,15419, 6720, +21548,14238,30101,35158,15891,22422,29753,34700,13136,20737, 4549, 4518, +29770,34717, 4565,29796,34743, 4572,29734,34681, 4535,41611,18173,41613, +33786,35208,34535,18103, 4285,42625,32907,32906,32195,32189,32188,32194, + 4993,42313, 4949,42140,42312,20607,20879,42139,10304,20606,13058,42138, +10729,20605,16109,42137,42448,42178,10276,42491,42136,42490, 4948,42489, +10286,42409, 4954,42443,36593,42135, 4284,42134,10029,42133, 9940,20935, + 9939,23558,12616,42351,23557,20934,33126,42350, 9183, 4283,10121,18549, + 4282,40459, 4281, 4280,20604,42634,13003,42261, 4279,20603,13002, 5137, + 4978,20602,42260,13001,42633,42632,42259,20601,20600,13000,12999,18690, +10349,20599,20598,12998,18689, 4278,20597,12997,18511,10110,20596,20595, +12996,18510,40918,20594,12995,17923, 9765,40917,20593,40916,17922,40692, +40691,40690,40689, 4320, 4322,40418,40417, 4306,40416,42585,42574, 4973, +42252, 4972,42405,42201,20670,42404,42200,10316,20703,13104,42223,10315, +42403,10097,18481,20592,20591,18478,12994,13134,20735,18415,18413,20734, +10063, 4277,20590,12993, 4335,20733,13133,35135,30076,22307,15748, 4399, + 4401, 4276, 4275, 4274, 4273, 4272, 4271,29721,34668, 4515, 4304,40712, +21466,14080,23748,22421,15890,40415,40514,41545,41544, 6668, 8194,33785, +35207,34534,40424,21425,14006,23588,22306,15747,40423,40414,40413,21439, +14026,23556,22334,15779, 4372, 4369, 4368, 4367,35151,30093,22413,15879, +35150, 4709,42027,20654,31957,13047, 4743, 4692,42062,20878,31969,13057, +32452,16107,32464,32480,32369,18743,24473,20658,26549,18117,24227,20589, +26528,18017,24124,20588,26527,18783,24489,20587,26526,40412,10275,20877, +12992,42311,41973,20586,31925,12991,30092,30275,29355,29432,30137,30296, +29002,29008, 4318,17706,23555,20691,26570,40411,25557, 6570, 4334,29718, +34665,12990,20585, 8185, 4333,34541,40410,21144,13573,23554,21321,13811, + 4337, 4332, 4331,17775,23649,20740,26589,18110,24208,20732,26586, 4339, +17705,23553,20744,26593,10094,18475,20584,20583,18473,12989,13156,20767, +18411,18408,20766,10060, 4269,20582,12988, 4346,20765,13155,23552,23551, +20958,20950,18569,13299,10132,18561,20957,23550,18568,23549,10131,18560, +23548,20949,23547,13298,10128,18557,20955,20947,18566,13296,23546,20967, +13309, 4426, 4425,20966,23545,13308,23544, 4422,20964,13306,18123,20907, +18736,26927,18735,20656,18116,20581,18016,20580,18782,20579,18072,27682, +18077,20932,18075,26926,18074,20930,18070,20923, 7374,21967,15295, 6806, +20578,12987, 7475,22058,15424, 7355,21955,15281, 6829,20897,13255, 7482, +22065,15432,17704,26941,39072,22305,15746,25553,21424,14005, 4345, 4344, +20764,39836,13154,39658, 4330, 4329,20731,26878,13132,26640, 5176, 5028, +20763,39657,13153,39835, 4328, 4672,20730,26639,13131,26877,39834,39656, +20762,20761,13152,13151,26876,26638,20729,20728,13130,13129, 4343,21375, +13913, 4327,21249,13704,39129,20760,13150,25661,20727,13128, 4364,20803, +13187, 4385,20846,13227, 4366,17904,26925,17903,20775,18109,20759, 4352, + 4363,17703,20773, 7224,21817,15106, 7278,21845,15151, 6793,20758,13149, + 7453,22033,15389, 4419, 4418,18063,20916,18723,20909, 4414, 4413,17702, +20911, 7222,21815,15104, 7399,21987,15317, 7350,21950,15275, 4312, 4362, +17701,20681, 7220,21813,15100,23543, 7430,22016,15356, 7436,22020,15361, + 7409,22000,15333, 7391,21981,15309, 9607, 9609,20868,26645,26883, 9571, +21254, 9570, 9569, 9568,20862,26642,26881, 9605,21252, 9603, 9711, 9710, +20845,26634,26872, 9709,21244, 9708,32149,17742,27675,18764,26633,17850, +27739, 7293,27674,27803, 7219, 7264,27723,27722, 7263,17700,27691, 7218, +27690,27673, 7233, 4397, 4384, 4387, 4383,10544,26524,14097,37564,10164, +26523,13611,37563,37712,26522,26665,37562, 4854,37711, 4819,37561,10169, +26582,13623,37610,37603,26564, 4833,37657,23542,26552,23747,26521,10144, +26664,13559,37710,24179,26684, 4622,26520, 4642,26575, 4621, 4680,26519, +26657, 4620, 4010, 4619, 4009, 4618, 4008, 4617, 4007, 4616, 4006, 4615, + 4005, 4614, 4004, 4613, 4003, 4612, 4002, 4651, 4042, 4650, 4041, 4649, + 4040,17699,42365, 4999,42364,42338,26673,42337,26672,17698,26694,10370, +26693, 9943,26689, 9942,26688, 4611,26518, 4610,42500, 4609,37862, 4608, +26517,42499,26516,37861,26515, 4984,42285,42284,26514,10357,26513, 4837, +37681,37680,26512,10181,26511,40976,36197,36196,40975, 9792,26510, 4627, + 4019, 4677, 4066, 4629, 4023, 4679,26656, 9567,17697,26655,26157,26889, +26152, 9850,17972,26509,25868,26789,25867, 5989, 4001, 5982, 4017, 9724, + 9832, 9821, 4068, 4021, 4025, 4000, 3999, 3998, 3997, 3996, 3995, 3994, + 3993, 3992,18079,18763,17849,17696, 5676, 3991, 5978, 4076, 9819, 4640, + 4639,26788,26573,26828,26827, 4655, 4607,26604,26847,26850,26787, 4653, + 4606,26845,26508,26844,26786, 4072, 4071, 4070, 9601, 9599,13239,13706, + 9566, 9565,13247,13710, 9707, 9706,13224,13694,32148,17740,27672,18762, +26869,17848,27738,17695,27802,26341,38967,25052,26190,25051,26188,25050, +12633,25049,12628,25048,26338,25047,12682,25046,26280,25045,12771,26095, +26329,25111,12672,25228,25044,25043,25042,25041,25040,25039,25038,25037, +25270,25269,25980,25979,25036,25035,25034,25033,25032,25295,25294,25293, +25292,26167,26172,26166,26171,26165,26170,26164,26169,25309,25308,25307, +26176,26181,26175,26180,26174,26179,25031,25030,25029,25028,25027,25026, +25025,25024,25023,25022,25021,25020,25019,25018,25017,25016,25015,25014, +25013,25012,25011,25010,25009,25008,25238,25236,25264,25266,25543,25542, +25537,25539,26316,26327,26091,25226,26059,26061,26058,32176,38181,25804, +25806,25802,25801,26108,20020,26093,20015,25242,19468,26336,20349,26335, +20348,26334,20347,26332,20343,26323,20340,25391,25370,29835,25369,34970, +26099,31094,32126,31253,35415,38473,36287,38148,36286,36285,25935,27057, +25934,26098,25933,26374,25963,27671,27340,25960,25932,27255,27085,25931, +25959,27050,26097,25967,26186,26184,33117,33965,33116,28439,29173,33964, +29435,34082,33115,28438,33114,34081,26377,26419,25260,33911,33531,28780, +28785,33537,33915,25007,25006,25005,33469,33960,29416,34045,33300,28622, +33283,28616,29170,33957,33350,34056,26369,26402,25004,26005,25003,25341, +29168,33955,34022,33954,26394,26357,28189,22298,28165,28167,33113,33899, +33112,28437,29150,33898,28123,33901,33516,28767,28122,28121,25552,40394, +25551,25550,28436,28435,28641,29402,29401,34021,26373,34969,26372,25248, +19474,11313,19927,25892,12124,28434,28433,25826,25825,26224,25828,26930, +26924,25830,25824,26967,41117,19947,12165,25389,26063,26083,27025,21441, +27190,21629,27108,27071,27244,32175,38180,27074,27070,27069,27076,27059, +21496,27027,27670,27047,34611,35085,27135,27056,34968,27055,38179,35414, +38472,38147,38146,27084,27254,27083,27253,27052,27222,31101,36363,26296, +26288,26271,26287,26283,20247,26285,26264,26290,26273,26294,26277,26293, +26276,26292,26275,25344,28055,28057,27828,27726,27784,27403,27783,27402, +27715,29840,27669,29881,29192,42830,42130,43047,27769,42402,37788,43086, +42763,41134,27668,42752,41279,41836,28023,27667,27737,29834,27736,27904, +27496,27903,27495,38178,32174,28432,28862,29400,29399,29247,27982,27966, +27515,38471,32426,38145,32163,36284,31043,27984,36120,40793,28431,28430, +29046,29039,27762,35413,38470,27243,27242,27068,28429,28880,29398,27126, +27270,27100,26923,27010,27012,26951,26961,26969,26954,26922,26921,26920, +26919,26918,42210,42129,30264,27000,42552,37919,29189,28991,30289,26917, +42459,37899,26956,42401,37787,43142,26916,41838,27054,26915,26914,27261, +27093,27122,27137,27139,27143,26929,27252,27082,26987,26986,26985,26984, +26965,27045,42306,26262,26269,26268,25864,25862,19907,12099,25861,21133, +13558,26232,25284,21158,13589,36233,41002,41001,36232,25459,25467,25466, +25439,25438,25437,25463,25462,25478,25436,25435,25434,25433,25432,25431, +25430,25429,25448,26797,26913,25450,25428,26630,26868,25427,25455,19572, +11451,19575,25453,11454,25425,19564,11435,25422,25421,34547,36611,29498, +31483,40783,36087,27782,27902,27499,27501,27494,27493,27401,27721,27720, +27719,27519,27514,27513,27512,26737,26742,26739,26717,26716,26715,26714, +26713,26735,26734,26733,36534,41543,26558,22395,26507,22420,26675,22296, +26506,20726,26578,20695,26505,20577,20876,26654,30266,30082,29431,29354, +30295,30136,29007,29001,26504,26785,26503,26784,26502,26783,26501,26782, +26653,20576,26803,26818,26810,26781,26780,26779,26778,26777,26905,26776, +26775,26774,26773,26772,26814,27666,26771,27735,27975,27749,28027,28044, +28037,26796,26770,26831,21179,21176,13621,13619,26843,21132,13557,26822, +26821,26820,25684,19793,25679,19791,25660,19781,25659,26261,25658,32147, +25657,26231,25656,25655,25654,27734,25653,25682,27665,27767,25704,27766, +27664,25703,25707,25652,27718,20178,20182,19439,11301,19435,11297,19999, +12305,20245,12674,20249,12676,20253,12680,20252,12679,20251,12678,26247, +21597,21596,21595,21594,30157,35219,33813,29037,29457,34543,40385,40384, +40383,40625,19655,20477,20475,42960,43367,42673,42571,42959,43366,19653, +28426,33105,28425,33104,19651,28424,33103,41111,41110,41108,31886,28423, +28422,28421,28420,19639,11546,19644,11548,19622,19115,41179,41153,19536, +11398,21323,21148,37369,37702,31765,21152,21314,21131,37377,37709,31776, +21130,32245,21129,21313,21128,21312,21127,21311,21126,21401,21305,37295, +37560,31748,21304,21374,13912,20757,20756,13148,21373,21241,13693,20725, +20724,13127,21240,21372,13911,21371,19612,11504,28419,40382,40381,19823, +11892,39128,20755,39127,19822,19780,11781,25651,20723,25650,19779,19821, +11891,19820,19819,11890,11889,19818,41089,41088,19437,11299,19540,11402, +19531,11393,20034,12310,20037,12329,15739,22295,20036,12328,20290,12758, +20300,12769,20299,12768,20298,12767,32458,40797,41132,40630,41662,41707, +41690,41785,41661,41774,41660,41773,41700,41699,41795,41684,41688,41659, +41727,41811,41725,41657,28418,28417,20229,12982,20228,20575,20225,12655, +20222,12653,28416,26302,42400,37786,22294,15738,22293,15737,22292,15736, +22291,15735,22290,15734,22289,15733,22288,15732,22287,15731,34719,29772, +35227,30162,29728,34675,35140,30081,34689,29742,35134,30075,29774,34721, +35133,30074,35253,30198,35132,30073,28931,33671,35130,30071,22286,35177, +22285,22284,22353,22283,31910,29115,29113,31879,28415,28414,22438,15899, +22282,15730,22746,21563,37294,37559,31747,21562,22748,16285,22750,16288, +22415,15884,22281,15729,22280,15728,22392,15849,22463,15930,34658,29711, +35157,30100,29710,34657,35156,30099,22419,15889,31863,29036,29035,22651, +16133,22653,16140,22658,16151,19922,19920,19906,40380,19905,19904,41087, +41116,19968,12203,19966,12201,19946,12164,19945,12163,38940,25109,21310, +21125,20232,12661,20234,12663,20240,12667,20239,12666,20238,12665,20242, +12669,20305,12687,20304,12686,20303,12685,20307,12689,20176,12631,20175, +12630,20180,12635,32140,31127,32139,31126,32138,31125,32137,31124,20351, +20346,31463,31462,31456,31448,31447,31446,31445,31862,32146,31924,41972, +21504,21503,21578,21577,21576,21575,27661,21437,21457,21455,32173,21534, +21532,21530,27660,41713,41802,42860,42754,41724,41810,21525,14160,21523, +14158,21518,14134,21517,14133,21542,14214,22955,23406,23405,23404,23403, +22986,21686,14735,21651,14505,41289,41281,42958,43365,40371,40370,43076, +43441,21638,14457,21657,14512,21674,14605,21672,14603,21667,14579,21666, +14578,19579,11459,19601,11477,25419,25417,19561,35438,30308,41723,41809, +27202,27033,21034,27713,21008,27733,25368,21007,27659,21075,22411,15877, +34878,29879,41558,41577,40612,40614,41693,41787,22278,15727,34907,29889, +26560,26816,22277,15726,26551,26812,22394,15852,22364,15806,34804,29833, +26500,26769,22363,15805,21481,20207,22481,20203,20213,20206,20202,26499, +26768,20201,20270,12650,20269,41342,41338,41337,41341,40664,40663,41562, +41581,20573,12980,29751,34698,34655,29708,34696,29749,34653,29706,41610, +20570,20569,34533,35206,33784,20568,18102,20567,12979,42624,42623,23537, +23536,20566,12978,20565,12977,41086,41085,36592,12658,20564,41808,41374, +41353,41373,41352,41372,41351,41371,40369,40368,40367,40366,40365,40364, +42597,42570,42569,42627,20664,13056,20563,12976,20800,13184,20754,20753, +13147,20799,20842,13223,20722,20721,13126,20841,20798,13183,20797,20650, +13049,20562,12975,20561,12974,20560,12973,20559,12972,41979,31961,42023, +42061,41971,31968,41970,32451,41969,40363,20875,13250,31923,42060,26496, +26767,21448,14037,20720,13125,20719,13124,25860,25859,20718,20717,25966, +25556,20796,13182,20927,13282,20929,13284,20809,13192,20795,13181,20866, +13243,20840,13222,20852,13232,20839,13221,21150,21156,21136,21124,21123, +21122,21121,21120,21278,27658,21119,25367,27732,21118,28025,21138,21117, +13556,21116,13555,21115,13554,21114,13553,21263,21262,21261,25415,25413, +12352,14223,12821,14281,12823,14284,11140,14523,11132,16013,16393,16380, +14985,14869,16399,16386,15015,14892,11966,11104,10996,14459,11321,11539, +11466,10995,11332,11472,11534,10994,11346,16643,10993,16653,11530,11520, +11433,12281,12285,11348,11345,40140,39228,11990,14113,11989,15240,11988, +21492,20011,21491,20010,21490,20009,21489,20008,21488,20007,21512,20042, +34803,29832,23535,23699,24207,21511,20041,33102,11672,22276,35218,11555, +11551,41176,11366,35137,40362,33101,40361,40523,41542,21487,21370,35188, +33950,13910,33100,40360,40499,41541,21486,20006,35187,33949,11888,43364, +33099,43363,12766,42568,18668,12657,36591,33098,32353,11080,12046,12039, +12010,12036,12014,11189,11191,37683,42287,11099,11101,37864,42502,11089, +11091,11066,11068,11117,36201,40984,12614,42348,27657,22275,42347,37744, +12613,12600,42346,37743,12538,12537,33097,13266,23526,27656,22272,12536, +12510,15773,14398,15722,33962,15721,26417,15720,26663,40355,15797,15719, +15718,33946,33830,33433,15717,15716,15715,26691,16233,33096,33095,15713, +33094,37344,33868,37388,33875,20474,32309,33833,33437,33837,17996,33093, +33092,15709,15706,15921,40354,33812,23525,20040,15834,32487,32496,32776, +40353,40352,33091,33811,33810,33809,37293,33808,33807,33806,33805,32299, +32474,32388,33090,32352,17684,23524,27756,22461,27655,22255,17843,23772, +17841,23746,27728,22418,26585,20716,15700,15099,15888,26495,26494,20558, +27029,21447,27036,21462,16104,14669,17900,16103,12971,40351,31922,32450, +25572,19660,17683,23523,26493,20557,16102,15274,26492,20556,12088,12087, +12086,41166,23522,12131,12085,42128,12084,40627,35975,37785,42399,12082, +12113,12079,12078,12077,12076,42498,37860,12075,12074,42283,37679,12073, +12072,40974,36195,12071,12123,41772,41771,41770,41769,41782,36891,41799, +36967,41768,36859,41807,12968,41806,36986,41805,36985,41767,36858,41766, +12517,12514,37751,12606,37749,12604,11378,42546,37906,11431,11430,11429, +11446,11450,11428,11427,11426,21537,12242,20215,20189,20274,21113,20200, +20268,20272,22254,20265,20555,20264,20794,20263,20262,21369,20261,20260, +22253,20257,20715,20267,24168,23521,41348,40350,24206,41540,22356,21036, +22362,21005,21070,22252,21510,21004,21146,22251,21140,22330,21435,21317, +13366,18487,13365,15909,18447,15816,21112,22361,15804,18405,15803,13364, +14077,13363,13435,13809,13572,15699,13492,18554,15698,36471,36498,36547, +15137,14053,16177,14484,40139,39954,15239,14112,15238,14111,15697,15887, +15864,40711,21465,20752,15098,20937,15326,36469,20468,15273,14182,20554, +15272,16100,14326,14299,15021,14896,15013,14890,14961,14960,14959,14958, +14853,14768,14231,14499,14426,14030,14767,14665,14705,14195,14692,14187, +14663,14180,14730,14577,14729,14132,14728,12159,14727,14726,43440,43362, +18059,34607,17682,34605,18720,34602,34903,14772,14786,14269,15355,14267, +14784,16269,15351,16265,14868,14852,14851,14850,14849,14957,14889,25722, +33651,14576,14575,14574,26973,14573,35051,14572,26612,14571,34867,14570, +14569,14096,13552,23520,14062,23519,39795,23518,21316,22250,39531,20793, +14004,39655,14003,21368,14002,39833,14001,14000,13999,39126,19817,40119, +40138,14280,14283,14228,20553,21485,20792,21484,14237,33758,33757,39753, +14240,39752,33089,14247,34800,14246,33088,21111,21451,14222,14207,26584, +15097,16099,14131,25721,33650,14130,14129,14128,26972,14127,35050,14126, +26611,14125,34866,14124,14123,14052,12157,12155,12153,12182,12152,12151, +12150,26971,12149,35049,12148,26610,12147,34865,33649,25720,13454,12146, +16900,17343,16912,17380,16619,16568,23517,16689,16566,24234,16564,22248, +16724,24188,16723,33085,16729,33890,33084,24187,23516,33889,16728,33888, +34012,16559,23826,33515,41648,16558,17067,17181,16557,26220,18087,16720, +16810,16773,16772,20552,17681,16771,16556,18719,16956,16955,17537,17300, +17536,17299,17535,17534,17297,17295,17293,17292,17291,17290,17533,17388, +17377,12965,12964,13095,12963,41609,18172,34532,35205,33783,15477,15532, +17101,12962,32905,32193,32187,16767,12961,13055,41347,13067,12960,18502, +12959,12958,18545,12957,17199,14073,18441,14048,32032,31967,31921,21110, +21446,14036,18404,14035,17680,23511,18740,24471,13163,13660,26601,20777, +18115,24226,18015,24123,18781,24488,12956,13551,26652,20874,40347,13054, +13588,26487,20551,40346,40512,41539,33756,35186,33948,13180,19648,25562, +20465,26438,30161,30303,21450,27031,29353,29430,30135,30294,29000,29006, +21461,27035,36984,41804,12955,13550,37747,42357,12954,13549,40345,25560, +26486,13123,33952,13122,13121,25965,25559,15460,15514,17055,17772,23647, +17679,23510,18108,24205,18121,17678,18738,18114,18014,18780,12953,26651, +18061,18085,17677,18083,18718,18081,15279,16126,15095,15694,17676,17906, +18107,15149,15902,13263,15344,16259,15353,16267,15187,15955,15289,16156, +15127,15790,15093,15692,13548,13610,13596,23508,22329,23745,22417,15809, +34823,15691,34863,22242,24181,15690,15886,20714,13547,40796,35139,33827, +33803,15828,15687,41794,13546,42222,13545,13544,13587,16239,34799,16238, +33082,13543,13578,42408,37794,37816,42445,13657,42127,13656,13655,13654, +42398,37782,13764,42369,17675,42368,13763,42367,13761,17674,13760,15683, +13728,13727,13542,13541,13540,42497,13539,37859,13538,42282,13537,13536, +37678,13535,40973,36194,13534,15680,14206,14568,14122,13667,13586,20550, +13585,11809,11819,11779,11817,12121,11775,11797, 9551,34857,33081,33079, + 1870,10340, 1763,21210, 1607,32011, 1803,35041,30536,30534,19291,31901, +17673,29870,28404,28402, 1820,37989, 1577,29932, 1748,18661, 1887,13864, +35765,35763,10964,37629, 9549,34854,33077,33074, 1867,10337, 1760,21207, + 1604,32008, 1800,35038,30532,30529,19289,31898,17671,29867,28400,28397, + 1817,37986, 1574,29929, 1745,18658, 1884,13861,35761,35758,10962,37626, + 9546,33071,33068,33066,19054,18717,14385,19286,38275,38056,30331,33064, +30526,30524,19282,30522,17667,28391,28388,28386,32215,35749,35477,35586, +10674,17664,21625,22234,35746,35744,10957,35742, 9542,33055,33053,33050, +19051,18714,14382,19279,38272,38053,30328,33047,30516,30513,19277,30510, +17661,28384,28382,28379,32212,35739,35474,35583,10671,17657,21622,22231, +35737,35734,10955,35731,17797,34167,33045,33043,36731,34323,33345,31399, +18340,31381,19447,34342,35958,30508,19267,31584,17649,29534,33325,28371, +17808,36906,20409,29311,35939,29295,34185,36925,35717,35715,19461,36750, +17796,34166,33029,33027,36730,34322,33344,31398,18339,31380,19446,34341, +35957,30500,19259,31583,17641,29533,33324,28368,17807,36905,20408,29310, +35938,29294,34184,36924,35713,35711,19460,36749,43358,33025,33022,33020, + 7205,40213, 6413,24632, 7559,39912, 6227,33018,42727,30498,27180,30496, +24775,28366,40212,28363, 7556,35709, 6226,43355, 7202,27333, 6412,42724, +35706,35704,39911,35702,43361,33015,33058,33061, 7208,40209, 6409,24630, + 7562,39908, 6223,33013,42730,30519,27183,30493,24778,28361,40216,28394, + 7555,35699, 6230,43354, 7201,27332, 6416,42723,35755,35752,39915,35697, +17638,10331,34848,35032,30491, 1811,33011, 1568, 9531, 1739,19256, 1878, +35695,32002,21053,21201,18613,18652,33008,29923,17635, 1861,10944, 1754, +35692, 1598,28359, 1794,37620,37980,19253,13855,17632,34202,33647,33633, +36770,34359,33005,31436,18359,31419,19250,34375,35689,30992,19768,31615, +17920,29549,33004,28918,17631,36945,20439,29346,35688,29330,34219,36962, +36230,36193,19249,36789,17630,33377,33646,33632,36229,33003,30991,17919, +30769,19248,35687,19767,28655,28917,35994,36192,17889,10746,35613,35557, +30927, 8803,33565, 5749, 9746, 8073,19628, 8560,36125,32295,22558,21685, +18903,19091,33570,30376,17895, 8844,11544, 6969,36132, 6014,28802, 8448, +38196,38333,19633,14689,17888,10745,35612,35556,30926, 8802,33564, 5748, + 9745, 8072,19627, 8559,36124,32294,22557,21684,18902,19090,33569,30375, +17894, 8843,11543, 6968,36131, 6013,28801, 8447,38195,38332,19632,14688, +17887,17628,35611,35555,30925,30488,33563,33001, 9744, 9528,19626,19246, +36123,35685,22556,21683,18901,19089,33568,32999,17893,17626,11542,10941, +36130,35683,28800,28356,38194,38331,19631,19244,39563,10143,34967,34798, +30660,42077,33233,26992,33856,42320,31186,31181,39684,31861,24419,21003, +26998,18587,42331,29831,29068,29085,36449,39689,35890,24423,28540,39559, +37950,37553,42072,13360,33862,18638,33197,33201,35065,37723,32038,34797, +31175,18619,33846,21002,30630,37949,31192,19345,29074,17746,28509,34966, +36467,18586,29079,21066,29936,37552,38021,34889,35855,35859,36462,21109, +33210,18637,33451,33444,35020,37608,31979,34796,30643,18604,33205,21001, +30835,37948,30639,19516,28522,17835,28711,34965,35868,18585,28517,21046, +29911,37551,37970,34832,36049,36060,35864,21108,18010,35175,33221,33215, +31081,18584,33736,21859,37947,37550,29904,35353,36327,30648,19351,32120, +17752,30423,33747,28527,31920,38450,34964,34795,36337,18946,28984,21000, +35874,35879,19988,38641,17823,18358,28697,29563,30809,31435,33401,34358, +38685,38468,31614,31418,36017,36769,11343,12750, 9663,10013,33421,34218, +32162,32425,36788,36944,36033,36961,28685,29329,30799,31601,19492,20438, +17822,18357,28696,29562,30808,31434,33400,34357,38467,38684,31613,31417, +36016,36768,11342,12749, 9662,10012,33420,34217,32424,32161,36787,36943, +36032,36960,28684,29328,30798,31600,19491,20437,17821,18356,33685,33684, +30807,31433,33399,34356,36258,36257,31612,31416,36015,36767,19976,19975, +17962,17961,33419,34216,31042,31041,36786,36942,36031,36959,28683,29327, +36265,36264,19490,20436,10003,34321,34165,34340,17795,32160,11309,35238, +35956,35937,28636,34183,31582,38466,20424,31397,18329,29293,29532,35383, +30747,36729,33323,33343, 9657,38683,19459,30427,36748,36904,12740,36923, +10002,34320,34164,34339,17794,32423,11308,35382,35955,35936,28635,34182, +31581,38682,20423,31396,18328,29292,29531,35237,30746,36728,33322,33342, + 9656,38465,19458,30305,36747,36903,12739,36922,33683,34319,34163,34338, +17793,31040,19974,33715,35954,35935,33682,34181,31580,36256,31048,31395, +28956,29291,29530,33714,36263,36727,33321,33341,17960,36255,19457,28928, +36746,36902,36262,36921,43340,17624,32997,32995, 7188,40208, 6408,32993, + 7542,39907, 6222,24772,42710,35681,27167,19242,24766,17621,40196,32990, + 7554,27177, 6210,43353, 7200,35678, 6396,42722,35675,35673,39895,19240, +34525,23507,32987, 7309,32975, 7308,24977,23337,17054,17288,23183,40340, +24975,23336,17053,17287,23182,40338,40336,23181,17052,24973,23335,17286, +38810,32821,40605,39392,25222,38772,40603,39389,25224,32726,38771,32725, +40601,40599,23180,39383,17051,39386,25220,25218,23334,24199,17285,24192, +25279,25281,38770,38769,38961,32820,32724,32723,38959,38809,40652,40654, +38955,38957,41224,17050,25995,23333,41226,23179,25993,17284,23681,23679, +32973,39048,40641,13960,23505,13952,23503,13969,23500,23498,39722,23496, +39746,23493,39745,40067,31682,40070,31685,40072,31687,40066,31681,40065, +31680,40055,31673,39721, 9923,39744, 9934,39743, 9933,16510,32971, 2826, +28343, 2824,40334,40332,40331,40329,40327,40325,40323,40321, 6294,22565, +16042, 6271,22626,16092, 4266,22625,16091,10919,19223, 6624,10911,24964, + 7087,19217,24960, 5724, 2709, 3037, 3181, 4265,22622,16089, 4298,22620, +16087, 7349,21949,15271, 7347,21947,15269, 7345,21945,15267, 3206, 5752, +10899,17800,34171,33121,33125,36735,34327,33348,31403,18344,31385,19450, +34346,35961,30552,19305,31588,17694,29538,33328,28443,17811,36910,20413, +29315,35942,29299,34189,36929,35807,35803,19464,36754,17799,34170,33120, +33124,36734,34326,33347,31402,18343,31384,19449,34345,35960,30551,19304, +31587,17693,29537,33327,28442,17810,36909,20412,29314,35941,29298,34188, +36928,35806,35802,19463,36753,18636,34169,33119,33123,36733,34325,34794, +31401,18342,31383,20999,34344,37946,30550,19303,31586,17692,29536,34963, +28441,18583,36908,20411,29313,37549,29297,34187,36927,35805,35801,21107, +36752,34520,23692,17727,34206,33644,33630,36773,34362,33170,31439,18362, +31423,19328,34379,35834,30989,19765,31619,17917,29553,33166,28915,17723, +36949,20442,29350,35830,29333,34222,36965,36227,36190,19324,36793,17726, +34205,33423,33403,37945,34793,33169,31860,18635,31422,19327,34378,35833, +30811,19494,31618,17825,29552,33165,28687,17722,36948,21106,29349,35829, +29830,34962,37548,36019,36035,19323,36792,17725,34204,34961,34792,36772, +34361,33168,31438,18361,31421,19326,34377,35832,31859,21105,31617,18634, +29551,33164,29829,17721,36947,20441,29348,35828,29332,34221,36964,37944, +37547,19322,36791,17719,33381,33643,33629,36226,33162,30988,17916,30773, +19320,35826,19764,28659,28914,35998,36189,17718,33380,33494,33506,37943, +34791,33161,31858,18633,30772,19319,33379,35825,30915,19588,30771,17875, +28658,33160,28758,17717,35997,21104,28657,35824,29828,34960,37546,36106, +36096,19318,35996,34201,17619,33398,33418,34790,37942,31857,32969,31415, +18632,34374,19209,30806,35662,31611,19489,29548,17820,28682,32964,36941, +17615,29345,21103,29827,35647,37538,34959,36030,36014,36785,19204,38965, +38827,38964,38402,38963,38394,30483,30481,30479,30197,30173,30172,37558, +37533,33232,26991,33855,42319,26997,31853,42071,34785,30663,10141,42330, +20996,31185,37937,30659,42076,28539,39558,29067,34954,39683,18580,28536, +13358,39562,37532,24418,29823,36448,39688,35889,24422,37708,37640,36827, +41765,29940,37091,37531,38132,37530,29954,29939,29952,29938,18047,17687, +18179,17690,37648,36442,36300,26727,26710,26725,26709,36053,36296,36454, + 8812, 5812, 8435, 8792, 4905,43814,43311,43002,43286,42582,42845,42928, +42841,17738,17994,17736,17882,37784,37852,36298,36308,42834,26708,42866, +42924,42836,41896, 4061, 5984, 5996, 3392, 6655, 3665, 5991, 2355,11822, +11824,12297,12293,41271,41244, 9823, 9825,28555,30678,23629,25170,28972, +31072,24113,26051,28716,30818,23740,25326,28544,30669,23602,25142, 9678, + 9673,12176,11804, 6292,22567,16044,14145,14590, 2539, 5657,14143,14588, +14164,14610, 2549, 5675, 1621, 5475, 1922, 5500, 3097, 5374, 5384, 3103, + 1927, 5218, 5216, 1912, 5230, 1907, 5228, 1909, 1924, 5240, 1920, 5242, + 5251, 1915,16600,16633,10970,11111,10966,11285,11116,12195,12208,11050, +24332,28819,28826,30947,37995,11048,37653,24461,38605,24787,38432,11046, +28807,30934,11044,43887,43853,43884,43850,43879,43845,43876,43842,14147, +14592,11113,12199,17610,19104,17607,19103,35640,28333, 8578, 8135, 4818, + 4785, 8230, 8669, 8667, 8228, 8584, 8161, 8176, 8596, 8576, 8133, 6923, + 6164,36294, 8790,36293, 6921, 6162, 6901, 6157, 4816, 4783, 8178, 8598, + 6925, 6166, 8586, 8163, 6903, 6159,24751,12788,39418,38649,38634,36069, +38630,37529,39997,39585,40148,40037,40013,40113,39995,39678,40109,39587, +39677,39544,39583,40111,39582,38677,35816,35815,38676,38692,38648,32845, +30554,32330,30665,31742,31199,32322,32317,10868,10865,28325,28322,28318, +28315,18253,11572,11569,37502,37165,35655,35652,35793,36302,36292,18631, +34162,32968,32963,36726,34318,34789,31394,18338,31379,20998,34337,37941, +30485,19208,31579,17614,29529,34958,28341,18582,36901,20407,29309,37537, +29290,34180,36920,35646,35661,21102,36745,18630,18355,32967,32962,31856, +31432,34788,34355,34200,34373,31610,31414,37940,36766,19207,19203,17613, +17618,34957,34215,29547,29344,36784,36940,37536,36958,29826,29326,35645, +35660,21101,20435,18629,17873,32966,32961,31855,30913,34787,33504,33376, +33375,30768,30767,37939,36104,19206,19202,17612,17617,34956,33492,28654, +28653,35993,35992,37535,36094,29825,28756,35644,35659,21100,19586,35799, + 9353, 9466, 9351, 8511, 8467, 9356, 9355,38382,28878,28873,28860,28855, +36282,36277,35797,18255,27821, 2597,18244,18243, 2456, 2596,18258,20185, +18251,18250,18248, 2639,37181,37178,37131,37164,37190,37163,37162,37161, + 2628, 2484,37160,37130,37159,37189,37158,37107,35671,28353,30196,28348, +30171,28352,28351,30211,28330,32980,32985,32978,32983,28346,28350,28329, +32887,32733,36647,36641,39425,39436,39434,17467,17466,17465,11252,11231, +11249,11228,28577,30701,28571,30695,17464,36512,36527,32681,33885, 2803, +29143, 2801,33883, 2815,29141, 2814,17283,36510,23099, 8940,16953, 8939, +24190,24197,17374,36514,23332, 9222,17282, 9221,24186,24195,16899,36509, +17342,36508,16716,36507,23331, 9220,17281, 9219,23513,23515,16898,35774, +16770,35773,35812,32912, 9413, 9412,38826,38825,32843,32842,36520,32749, +36525,32722,36524,32765, 9315, 9314,38768,38767,32721,32720,36523,32902, +36522,32643,11922,11919,17550,32854,17500,32826,17196,32891,17326,32918, +14566,13036,13033,13045, 9827,20640,20632, 5980,21627, 6232,20630, 4294, +28502,30618, 3072, 3390, 3308, 3501, 3294, 3479, 3296, 3485, 3291, 3474, +24393,42188,24390,42185,10400,38621,10397,38618,38600,37957,38597,37954, + 3369, 3053, 2347, 2474, 2341, 2468, 3497, 3306, 3387, 3070, 3372, 3056, + 3462, 3288, 3460, 3286, 3367, 3051, 6898, 5577, 6896, 5575, 6912, 5608, + 3262,38172,27114, 3033,38221,27106, 3031, 2490,38144,27081, 2488, 9523, +38143,27080, 9895, 3361, 3413, 3411, 2340, 2338,19193,20045, 3436,16188, + 7760,16186,16190,16166, 7755,27548, 7979,27546, 7977,27554, 7985,27371, + 7905,27369, 7903,10218,34894,29094,29104,23572,40849,33705,33700, 5477, + 3365, 5504, 2343, 5409, 3495, 5442, 3385, 5436, 3375, 5376, 3454, 5430, + 3363, 5327, 2345, 5283, 2367,32622,30556,30756,31026,32607,30558,30783, +30872,30884, 6085, 1558, 5512, 1532, 5502, 1551, 6098, 1541,16642,32605, +32620,16677,16631,16675,14167,14613,10974,10972, 5387, 3106,12007,12006, +19884,19883,27888,27479,25821,25820,22532,21907,15995,15226,13355,13354, +20992,20991,27887,27478,16175,22682, 3640,22681, 3639,28006, 3745,36150, +14722,16168,31028,14779,14262, 5486, 3092,14788,14271, 5514, 2300,14739, +14218, 5444, 3337, 3130, 5407,14202,14718, 3108, 5389,14190,14695, 3105, + 5386,14188,14693,14733,14211, 5433, 3328, 3090, 5367,14184,14674, 2302, + 5285,14115,14519, 2320, 5325,14137,14582,10976,10978, 2315, 5308,14118, +14556,12287,16679,12187,12185,12167,12213,12211,12145,12210,12220,12171, +12005,19882,25918,25819,19938,12144,13353,20990,25795,12218,12143,11787, +16647,11802,11800,11791,11789,12169,12012,11287,12142,12004,11785,11783, +32625,32624,16645,11772,16644,11535,11333,11771,11770,11769,11768,37781, +39432,42704,17745,19044,28513,30325,30629,32526,33196,35580,33861,33845, +31191,31174,35858,38269,11123,14378, 9612,10668,33200,35471,29073,29078, +36461,36466,35854,38050,28508,30067,30634,32209,19344,21619,17744,37488, +28512,29885,30628,32037,33195,35064,33860,33844,31190,37929,35857,37722, +11122,29816, 9611,31846,33199,34888,29072,34947,36460,36465,35853,38020, +28507,29935,30633,31908,19343,34770,42124,18618,28511,29884,30627,39579, +33194,35063,33859,39542,31189,31173,41965,37721,26764,13467,24372,10194, +39541,34887,29071,29077,36459,41964,35852,38019,28506,42123,30632,31907, +39578,21065,35579,33450,33204,33209,10667, 9684,21618,19515,32208,30827, +35470,33443,30642,30638,32525,30834,30066,28710,28516,28521,38268,36059, +30324,28721,19043,17834,14377,11391,35867,35863,38049,36048,35019,33449, +33203,33208,31845, 9683,34769,19514,31883,30826,34831,33442,37928,30637, +31978,30833,29910,28709,34946,28520,37607,36058,29852,28720,37487,17833, +29815,11390,35866,35862,37969,36047,35018,33448,39540,33207,10162,24371, +21045,39577,31882,30825,34830,39539,30641,30636,39576,30832,42122,28708, +28515,28519,37606,41963,29851,28719,18603,42121,13429,26763,41962,35861, +37968,36046,18009,35174,33220,33214,31080,10542,33735,21858, 9872,32383, +19996,35352,36326,30647,19350,32119,17751,30422,33746,28526,18003,38449, +12303,30287,36336,18945,28983,15177,35873,35878,19987,38640,18008,35578, +33219,33213,31079,10666,33734,21857, 9871,32382,19995,35469,36325,30646, +19349,32524,17750,30421,33745,28525,18002,38448,12302,30323,36335,19042, +28982,15176,35872,35877,19986,38639,18007,35173,33218,39538,31078,10541, +39575,21856,24379,32381,39537,35351,36324,30645,19348,39574,17749,42120, +33744,28524,41961,38447,26910,30286,42119,18944,28981,15175,35871,41960, +19985,38638,18006,35172,33217,33212,31077,31844,33733,34768, 9870,32380, +19994,35350,36323,37927,19347,32118,17748,30420,33743,34945,18001,38446, +12301,30285,36334,37486,28980,29814,35870,35876,19984,38637,35881,43188, +19019,43115,19000,43043,18937,42904,19125,40860,41464,40864,41531,40856, +41497,27449,27858,26485,26484,18893,35399,18905,18871,36637, 8640,43490, + 8638, 8636, 8632,43488, 8630,27913, 6309,27643, 6199,27650, 6201,27648, +27317,15071,21781,27319,36291,30114,30113,18035,18034,18843,27630, 6194, +27198,26411,27628,27627,27626,17953,18752,18873,18877, 6850,14325,14323, +13562,27200,27195,26544,26498,35410,18842,18840, 7945, 4605,36290,29959, +29958,18839,26754,26753,18838,30779,28661,28542,28311,32951,38593,37485, +26198,38808,32719,26208,26206,26210,28413,12590,18544,11602,18501,10103, +18493,11601,18500, 7875, 7873,27185,26532,26483,27409,27411,26491,27792, +26530,26719,26489,26712,12815,27808,26482,27790,27795,27794,26070,27380, +30341,10472,28732,29134,29567,29569,30349,30351,29019,29021,27745,26481, +26480,25346,25339,25355,38807,32718,26068,26073,25349,25351,25348, 2714, +26479,25856,26478,25855,26477,25854,26067,26078,32125,19191,26065,26076, +26476,26475,26072,26080,26131,26136,32145,10040,37484,37483, 4063, 5977, +22224,15675,18722,38647,38592,36537,38591,26410,26474,26473,26416,26683, +27293,27228,26245,26662,35635,28328,28327,43141, 8634, 3698, 3417,43138, + 8621, 3713, 3441, 4213, 6544, 4218, 6524,40309,21826,15119,24933,20136, +12548,31819,24935,29131,37427,37426,37408,38126,37407,24931,24929,17280, +35772,17341,35771, 8954, 8953, 8972, 8971,32782,38780,32780, 9335,38778, + 9333,35811,32911,35810,32910,35809,32909, 9436, 9435, 9450, 9449, 8407, + 8256,38629, 8415, 8264, 8255, 8406, 8263, 8414,35185,30123,35217,30156, +35214,30153,35789,40137,14174,32950,32949,32948,32953,35923,39167,35925, +38697,30195,42782,38646,27407,27788,27787, 8189,35791,12362,12359,11671, +11668, 8208,38343, 8206,38341, 8147,38291, 8145,38289, 8143,38287,10851, +27142,10850,27121,10849,27044, 6334, 1523,11177,11175,15347,23665,25199, +23667,25201,41325,41317,41322,41314,12358,23576,40851,37545,38125,37544, +37543,37541,41764,40304,28309,28308,28307,43105,18991,28306,28304,28884, +35642,42567,41159, 9349,33613, 2854,28895, 2849,33611, 2852,28893, 2847, +13527,26472,11550,36307, 9348,13526,18492,33879,41480,33867,41510,41496, +41445,41530,41535,43810,43282,43307,37913,41501,41463,13758,43028,41495, +43007,41529,12582,18534,26682,12589,18543,33877,41478,33870,41514, 4208, + 6553, 4263,22580,16060,11642,19693, 6603,11623,25597, 7060,19674,25585, + 5684, 2676, 3005, 3140, 4055, 5988, 4207, 6550,20547, 5987,19189,20546, + 9831,13525,20545, 9723,40302,40300,40298,38633,31123,25354,25357,40313, +40315,40312,40311,19187, 2935, 2932, 2975, 2972, 2931, 2389, 2392, 2388, +38391,12812,38367,38390,38360,10845,23489, 9521,23488,23541,23486,10861, +35182,30120,10858,12326,35220,30158,12323, 3320, 3318, 3083, 2317,41305, +27826,27824,27810,30339,28303,30476,21529,28302,30475,21527,28301,30474, +35418,30472,24979,41370,28299,40542,28298,40374,28297,41301,28504,41303, +28296,40378,28295,40389, 9517,22223, 9515,22222,28294,30471,15673,28293, +30470,26087,23485,26085,23484,26089,23634,26143,23482,28291,30468,35840, +25163,24068,33717,30467,33713,31149,36306,36274,36305,33668,36280,36279, +33666,41175,33726,30753, 8805, 9754,35271,35600,11567,11566, 8646,35602, +11561, 8644,40541,43812,40373,43309,40377,40376,43284,40388, 5853,40387, +35409,30466, 8193,35407,30465, 8192,35381,30464, 8184,35379,30463,35570, +30461,43352,28290,30460,27624,28289,30459,28287,30457,39372,30625,39369, +30622,38899,31715,39351,30456,39349,30978,39353,31089,29622,31709,38885, +31720,38884,30455, 8191,38882,30961,39786,30454,31739,35838,35795, 9519, +24942,24946,10856,10860,36345,36353,36344,36355,36343,36351,27406,35181, +30119,35281,30226, 8360, 8356, 8330, 8358,39348,32112,36349,36348,36347, +35184,30122,28764,41298,25054,40688,35240,30453, 8794,35249,30966,28852, +35594,28858,28857,35592,35385,30452, 4903,35397,30969,11554,35412,30451, +24989,41350,37156,36374,38124,38073,32589,11073,42118,18242,16618,38061, +11079,42117,17959,36254,33491,33503,36103,33681,30912,17872,30766,19973, +36261,19585,31039,28755,35991,36093,33703,33698,37155,38123,36373,18241, +37154, 9818,20884,14498,26409,27227,26106,41758,37129,18218,12026,42159, +18240,12035,42116,35785,38881,32098,33691,33693,33689,22218,12043,13218, +10294,10292,12028,30542,30450,30538,30449,31151,30448,31161,30447,31163, +31165,10883,22217, 5329,42155,42974, 8736,42153, 4951,41300,31460,30446, +31458,30544,31450,30445,30548,30444,30546,30443,30850,30442,31129,30441, +31171,30440,31177,31167,35657,35781, 5119, 5121,43484,18837,42822,32959, +28339,32957,28337,10890,10889,35180,30118,10888,35116,30009,32955,28335, +32956,28336,10886,32142,30439,32107,30438,33826,29060,33828,29062,30848, +31159,32105,31158,18239,12112,42115,40294,22797,16335,11611,19666,23481, +11704,19724,23480,11710,19727,23478,11634,19685,23477,11722,19733,23476, +40292,21574,14298,14322,21593,40290,11726,19737,23475,11202,19384,23474, +11677,19713,23473,11674,11673,19710,23534,11205,19387,23472,11208,19390, +23471,11353,19502,23470,11011,19312,23469,11649,19700,23468,12449,20095, +23467,11007,19309,23466,11630,19681,23465,12445,20092,23464,16116,10909, +19216, 6507,10843,19185, 7796, 5508, 1553, 6102, 1544,11201,19383, 7799, +10841,19183, 6769,10917,10916,19222, 6757, 1919, 5507, 5506, 2337,11200, +19382, 7798, 3099, 5379, 5378, 3456,11204,19386, 7743, 3078, 5354,14173, +14650,11010,19311, 6602,10930,19231, 7795,10927,10929,19230, 7724,10926, +19228, 7741, 5302, 2336,11207,19389, 7616,10838,19181,41258,11641,19692, +41257,10836,19179,41256,16649,22930,25272,11033,19314,25535,11323,19482, +25545,11004,19307,25874,11315,19476,25895,12442,20090,25866,11468,19595, +25469,11456,19577,25457,32610,32609,16651,22932,25274,30785,30886,11537, +11335,11325,19484,25547,25471,19597,11470,16682,12289,16681,25058,22939, +16691,12141,11331,16598,30781,11329,11767,11766,11320,19480,25541,12003, +12140,11319,19479,41255,17819,18627,28695,29901,30805,31851,33397,34784, +34199,34372,31609,31413,36013,37935,11341,13776, 9661,10272,33417,34953, +29546,29343,36783,36939,36029,37525,28681,29821,30797,31919,19488,21087, +17818,18651,28694,29922,30804,31892,33396,34847,34198,34371,31608,31412, +36012,37979,11340,13854, 9660,10330,33416,35031,29545,29342,36782,36938, +36028,37619,28680,29861,30796,32001,19487,21199,35953,18354,28693,29561, +30803,31431,33395,34354,34197,34370,35934,31411,36011,36765,28634,12748, +30745,10011,33415,34214,33340,29341,36781,36937,36027,36957,28679,29325, +30795,31599,33320,20434,35951,18353,28692,29560,30802,31430,33394,34353, +34196,34369,35932,31410,36010,36764,28632,12747,30743,10010,33414,34213, +33338,29340,36780,36936,36026,36956,28678,29324,30794,31598,33318,20433, +18163,34524,18170,34539,38973,41703,29442,14761,14765,14758,14451,13068, +10317,20704,13105,42224,24299,30412,43809,43281,43306,38235,27349, 7889, +14291,14320,14307,14275,27292,32314,26432, 8142,38286, 8205,38340,32311, +26426,27542, 7973,24301,30414,29621,31708,38245, 2898,34620, 3233,38243, + 2890,34618, 3225,38241, 2909,34616, 3245, 2476, 2353,29613,31699,38239, + 2333,34614, 2311,38232, 2959,32947, 2997,38230, 2957,33238, 2995,32946, + 2920, 3256,38218,33236, 2918, 3254,38216,32945, 2913, 3249,38211,33141, + 2911, 3247,38209, 3284, 3458,28841,30957,38225, 2952,33595, 2990,32944, + 2906, 3242,38205,33111, 2889, 3224,38171,26471,33619, 2904, 3240,38203, +33617, 2902, 3238,38201,33615, 2900, 3236,38199, 9347,33603, 2896, 3231, +38189,33601, 2888, 3223,38170,33599, 2908, 3244,38207,40288, 5473,19177, + 6077,19175, 6083,40286, 5510,20671,42202,28668,40774,28870,35518,28876, +28875,35516, 3026, 2497,12321,35396,30968,43080, 8776,42253, 4974, 9563, +35780, 9562,35779, 9561,35778, 9560,35777,35621,35776,43187,43525,33087, +28410, 8381,40344, 6063,40349, 5471, 8656, 8665,35769,35619,43193,43529, +24924,24922,19170, 6052,40280, 5460,19169, 6094,40279, 5498,40284, 6048, +40278, 5456,35768, 9554,35767, 9553,42816,42903,33083,28406, 8214, 8219, +42821,42908,35276,30221,10834,40283, 5465,19173, 6057,40282, 5484,19172, + 6072, 4604,35480, 8688,38151, 7004,35486, 8696,38161, 7021,35484, 8694, +38154, 7019,11715,11706,15672,11667,11713,11702,15669,11664,11699,11717, +11708, 5397, 3116,14777,14260, 5481, 3113, 5447, 3389,14741,14220, 5446, + 3339, 5412, 3500, 3132, 5411,14204,14721, 7329,21927,15249, 5494, 2352, + 5372, 3448, 3101, 5382,14186,14691, 2327, 5344,14162,14608, 3077, 5353, +14172,14646,11648,19699, 6601,35482, 8692,38166, 7017, 5392, 3473, 3110, + 5391,14192,14699,23463,43692,43694,24920,38951,22867,16482,33249,28118, +16479,28586,28115,22864,30710,35901,40584,16114,11661,23661,38948,22861, +16476,43690,33246,28112,16473,43689,28583,28109,22858,30707,35898,40581, +25195,23462,43789,43767,24919,23461,43688,43686,24918,16464,28568,30692, +28588,33251,30712,24853,35903,24812,28590,33253,30714,24855,35905,24814, +28576,30700,28575,30699,28574,30698,28594,30718,28593,30717,28592,30716, +23460,26117,23459,26125,25062,24917,25064,24916,23615,26119,23618,26127, +24915,25159,24914,25161,23458,25068,23457,25070,25378,24913,25384,24912, +23580,24911,23578,24910,25380,25086,25386,25084,23456,23455,43698,43696, +43702,43700,24909,24908,23454,28127,23453,28131,28125,24907,28129,24906, +23452,25073,23451,25076,27214,24905,27040,24904,23584,24903,23582,24902, +27216,25090,27042,25088, 9897,23617,27212,23825,27038,23807,25508,25507, +25485,25484,38076,32635,24060,43752,24059,43747,43751,25989,43746,25988, +16663,38064,23708,23707,28163,28162,28160,28159,25277,25276,33688,11488, +11501,33695,11465,19593,41254,23833,23813,24901,25514,24900,25491,25516, +25493,23824,23806,25376,25506,25382,25483,25505,25482,23840,23819,25258, +25524,25255,25501,25521,25498,16662,40650,40648,12139,16661,17475,16660, +17494,16708,32634,16655,11765,16654,16668,16665,17463,25268,22928,16640, +16658,12002,16657,41253,22927,16639,27274,27104, 8615, 2949, 8724, 2967, +24899,24898, 5711, 3168,27087,27257,39591,39593,39778,39780,24897,24896, +25305,41442,41415,24895,24981,41405,41436,24894,40670,21283,13733,25304, +20144,12556,40669,23178,17049,25303,23330,17279,40668,22082,15459,25302, +21736,14943,40667,22103,15510,25301,21714,14841,31816,38978,12457,20101, +23723,12456,20100,23722,12455,20099,25853,25930,41147,41165,24893,25153, +25151,41527,41493,41518,41484,24892,24891,24985,24983,41512,41476,41523, +41489,24890,24889,29127,37418,38081,38944,38942,25926,25928,41143,41145, +41161,41163,24888,24887,25157,24316,25155,24308,26445,24886,26435,24885, +31153,30573, 8606,27241, 8708,27067,24884,11397,24883,19535, 5633,40721, + 8611,27268, 8716,27098,24882,13815,24881,21327,27278,27119,15862, 8485, +22401, 8532,24880,24879,27266,27096,13813, 5014,21325, 5150,24878,24877, +24305,25080,24314,25078,24876,26430,24875,26443,38381,12808,38372, 9614, +33240,28553,30852,30588,23810,25510,23829,25487,24874,25489,24873,25512, +28730,32109,30590, 8604,27239, 8706,27065,25072,11400,25075,19538,23818, +25523,23839,25500,25257,25497,25254,25520,25114,41379,26145,23677,25211, +24077,36418,36416,36452,21509,20039,35274,30219,12320,35212,30151,33801, +29031,33802,29032,12319,12317,12316,12325,33814,29038,29030,31148,29029, +31147,33804,29034,31169,31146,33818,29042,23805,25504,23823,25481,26115, +25480,26123,25503,25094,40694,30575,35818,25066,41355,14455,13070,10319, +20706,13107,42226,34316,10000,34335,34160,17805,17791,20405,11306,35931, +35950,34178,28631,31377,31577,31392,20421,29288,18326,29307,29527,36724, +30742,33337,33317,18336, 9654,19444,19455,36899,36743,36918,12737,34315, + 9999,34334,34159,17804,17790,20404,11305,35930,35949,34177,28630,31376, +31576,31391,20420,29287,18325,29306,29526,36723,30741,33336,33316,18335, + 9653,19443,19454,36898,36742,36917,12736,34314, 9998,34333,34158,34388, +36806,31455,29578,35929,35948,34387,28629,31375,31575,31644,20419,29577, +18324,29305,29525,36980,30740,33335,33315,29358,31643,36979,34229,36897, +36741,36916,12735,34530,23691,34519,23690,34518,23689,37417,37349,37500, +37481,37567,37337,37555,33628,33624,33374,33373, 9760, 9764,19763,19759, +31002,30998,33638,33642,30765,30764,30983,30987,28913,28909,28652,28651, +36225,36221,28904,28900,17915,17911,11887,11883,35990,35989,36188,36184, +34783,33502,33372,33371,10271, 9735,21086,19584,31918,30877,34952,33490, +30763,30762,31850,30911,29820,28754,28650,28649,37934,36102,29900,28792, +18626,17871,13775,11515,35988,35987,37524,36092,34846,33501,33370,33369, +10329, 9734,21198,19583,32000,30876,35030,33489,30761,30760,31891,30910, +29860,28753,28648,28647,37978,36101,29921,28791,18650,17870,13853,11514, +35986,35985,37618,36091,33500,33499,38464,38679, 9733, 9732,19582,19581, +30875,30874,33488,33487,30759,30758,30909,30908,28752,28751,32408,32159, +36100,36099,28790,28789,17869,17868,11513,11512,35984,35983,36090,36089, +20673,42204,28671,40777,43082, 8778,35236,31145,42255, 4976,12467,15666, +12584,18536,15665,18529,11600,18499,43684,43683,43682,43681,41521,41506, +41487,41472,28106,28105,41520,41486,41505,41471,28104,28103,12588,18542, +35566,30397,17734,29615,31701,29620,31707,11594,11580,11593,11592,13438, +40317,40319, 2675,11660,19708,41252, 6693,40409,12569,20157,40408,40672, +12563,20151,40276,40403,40406,12618,42353,20165,42669,40402,40405,40550, +40548,12621,42356,20168,42672,40274,40272,40554,40552,35279,30224,34631, +29684,40270,43213,40268,43541,40400,40397,24303,34628,29681,40399,43210, +40396,43538,32283,11690, 3331,32281,11697,11696,11695,11694,19722, 2733, +11692,19720, 2731, 6695, 6692,34708,29761,20865,13242,11180,11183,11179, +11182, 2831,15192,39417,31625,18280,19041,29473,30322,31309,32523,34248, +35577,34246,34106,31307,31515,36671,38267,12696,14376, 9964,10665,34104, +35468,29216,29471,36826,36669,36824,38048,29214,30065,31513,32207,20360, +21617,18318,37584,29511,29813,31361,31913,34295,34944,34300,34148,31356, +37965,36713,37480,12727,29846, 9992,31873,34152,34767,29271,34991,36876, +36717,36881,37926,29266,29895,31557,31843,20395,34820,42168,18576,29518, +29812,31369,39620,34308,34943,34307,39550,31368,31564,42011,37479,26800, +13352,24374,10138,39549,34766,29279,29517,36889,42010,36888,37925,29278, +42167,31563,31842,39619,20989,18315,18575,34990,34819,31355,31912,34299, +34942,34294,34151,31360,31556,36716,37478,21142,21022,18640,18594,34147, +34765,29265,29510,36880,36712,36875,37924,29270,29894,37964,37583,20398, +20988,39536,10155,34989,34818,31354,41959,34298,26909,34293,42114,31359, +31555,39573,31872,24378,21021,26908,18593,42113,29845,29264,29509,36879, +39572,36874,24377,29269,39535,37963,37582,41958,13391,39547,10137,34941, +34764,31365,42007,34304,26936,34303,42164,31364,31560,39617,31841,24384, +20987,26935,18574,42163,29811,29275,29514,36885,39616,36884,24383,29274, +39546,37923,37477,42006,13351,38766,32717,41738,39446,26352,38806,41818, +39493,26389,32819,38805,32818,38804,32817,41744,39452,26362,38765,41826, +39501,26397,32716,38764,32715,41740,41820,23329,39495,17278,39448,26391, +26354,23177,24271,17048,24282,41746,41828,23176,39503,17047,39454,26399, +26364,23328,24276,17277,24285,24278,26367,18312,19039,29506,30320,31351, +32521,34285,35575,34290,34140,31346,31552,36705,38265,12724,14374, 9989, +10663,34144,35466,29261,29502,36866,36709,36871,38046,29256,30063,31548, +32205,20392,21615,18311,37476,29505,29843,31350,31955,34284,34987,34289, +34139,31345,37922,36704,37580,12723,29810, 9988,31840,34143,34816,29260, +34940,36865,36708,36870,37961,29255,29907,31547,31870,20391,34763,42112, +18591,29504,29842,31349,39571,34283,34986,34288,39534,31344,31551,41957, +37579,26762,13389,24370,10153,39533,34815,29259,29501,36864,41956,36869, +37960,29254,42111,31546,31869,39570,21019,18310,18590,34939,34762,31348, +31954,34282,34985,34287,34138,31343,31550,36703,37578,21080,20986,18621, +18573,34142,34814,29258,29500,36863,36707,36868,37959,29253,29906,37921, +37475,20390,21018,39491,41816,18351,18625,29558,29899,31428,31849,34351, +34782,34367,34194,31408,31606,36762,37933,12745,13774,10008,10270,34211, +34951,29338,29543,36934,36778,36954,37523,29322,29819,31596,31917,20431, +21085,18350,18649,29557,29920,31427,31890,34350,34845,34366,34193,31407, +31605,36761,37977,12744,13852,10007,10328,34210,35029,29337,29542,36933, +36777,36953,37617,29321,29859,31595,31999,20430,21197,39509,41834,39497, +41822,39416,39422,36636,26312,40618,12761, 9997,34313,34157,34332,17789, +17803,11304,20403,35947,35928,28628,34176,31574,31374,20418,31390,18323, +29286,29524,29304,30739,36722,33314,33334, 9652,18334,19453,19442,36740, +36896,12734,36915, 9996,34312,34156,34331,17788,17802,11303,20402,35946, +35927,28627,34175,31573,31373,20417,31389,18322,29285,29523,29303,30738, +36721,33313,33333, 9651,18333,19452,19441,36739,36895,12733,36914,26314, +40623,12765,40108,39363,34244,18711,34242,34102, 9962,32204,20358,35465, +31511,10662,34100,21614,31305,38045,31303,20356,29212,18278,29210,35574, +36667,19038,29469,14373,18276,38264,12694,30319,36822,36665,36820,22228, +34292,18709,34297,34146, 9991,32202,20394,35463,31554,10660,34150,21612, +31353,38043,31358,20397,29263,18314,29268,35572,36711,19036,29508,14371, +18317,38262,12726,30317,36873,36715,36878,22226,26350,24269,27187,27023, +34349,33623,34365,34192,10006, 9763,20429,19758,31594,30997,34209,33641, +31406,31604,31426,30986,29320,28908,29336,29541,36760,36220,29556,28899, +18349,17910,12743,11882,36932,36776,36952,36183,34348,33627,34364,34191, +10005, 9759,20428,19762,31593,31001,34208,33637,31405,31603,31425,30982, +29319,28912,29335,29540,36759,36224,29555,28903,18348,17914,12742,11886, +36931,36775,36951,36187,34330,18612,34311,34174, 9995,31998,20416,35028, +31572,10327,34155,21196,31388,37616,31372,20401,29302,18332,29284,34844, +36738,18648,29522,13851,18321,37976,12732,29919,36913,36720,36894,21052, +34329,18579,34310,34173, 9994,31916,20415,34950,31571,10269,34154,21084, +31387,37522,31371,20400,29301,18331,29283,34781,36737,18624,29521,13773, +18320,37932,12731,29898,36912,36719,36893,20995,39415,36635,36645,36634, +18150,32346,18876,18918,24312,24295,43680,43679,43678,43677,26441,26422, +24311,28102,24294,28101,28100,26440,28099,26421,29619,31706,35564,30395, +24748,12785,38909,17715,34572,32943,34565,32942,33109,32941,33107,32940, +33462,32939,33464,33255,33791,32938,33839,32937,26996,33854,42329,33231, + 4860,26990, 4869,42075, 4812,31180, 4889,42318,30662,30658,42070,31184, +42110,29066,28535,28538, 4851,41955, 4868,29084, 4811,42109, 4886,26761, +41954,35888,26760,36447,33841,33229, 6909, 6154,33836,33460,36639,34574, +33128,35369,32936,35343,32935,35345,33257, 4929, 4853, 4801, 8174, 8594, +26661,35341,33835,20883, 4403,27646,33832,36456,29081,34567,33172,36633, +33793,33174,35371,33176,20032, 6170,31135, 7934,31122,21877,36400,15196, +38669,38458,32136,32394,36405, 8854,12314, 8759,21876, 7933,32393,31121, +38668,12313,36399,36404,32135, 8758,15195, 8853,38457,31134,20031, 7031, +27226,35360,33777, 6907, 6152,27545, 7976,27376, 7910,25374, 5783,40720, + 5590,40719,27247, 8608,27078, 8710,11395,25577,19533,25579,25372,34570, +33459,36372,38122,40107,18793,26105,38657, 5299, 6951, 5277, 6942, 5207, +35783,36420,35787,36422,35213,30152,35211,30150,35122,30023, 8388, 8378, + 8328,24307, 8365,36415,36414,35216,30155,26359,24273, 8422, 8428, 5279, + 6944, 5209,32415,39946, 7623, 5315,32412,39943, 7620, 5312,18875,32509, +16150, 2984,35426,30960,32517,16139,30869,11425,25477,25461,30880,11462, +30866,11422,39427,30599,35836,36440,36444,10217,34885,14438,42333,10393, +20892,14503,42335,10413,20894,10266,34883,20882, 5976,26660, 7452,40393, +22765,40392,42558,17686,24994,24996,38675,37474,26104,38673,37292,41297, +41296, 5257,41295,42384,35378,31144, 7315,41309,22694,41308,42557,18046, +26133,26138,42926,17689,25000,24998,26707,26706,11659,19707,23731,16164, +22675, 3628,22673, 3626,27999, 3736,16172,22678, 3635,22677, 3634,28001, + 3741,36160,14714,11656,36158,14704,11128,11130,11151,11166,11150,11165, +18238,11137,11136,11156,11171,11139,11158,11173,43676,43675,40426,40422, +23861,43674,43673,25549,11054,28809,30936,11052,11058,24334,28821,28828, +30949,37997,11056,37655,19527,24456,19529,24467,17847,28737,30847,28736, +30846,17852,24463,38607,24789,38434,22436,24452,22433,24449,18761,32104, +30104,18766, 9511,24629,23450,22216,19145,21808, 9502,24823, 9509,24627, + 9506,24624,23447,22213,10827,22210,19142,10825,27623,21805, 9499,24821, +12241,24413,11978,11977,24351,37674,12240,38014,12239,24346,11976,11975, +24408,38009,12238,37669,36177,23963,36172,23958,34838,10321,34735,29788, +34724,29777,29106,29096,34761,10289,12275,18486,39272,17971,34802,10265, +12256,24403,12237,12236,24341,37664,12254,38004,12252,24339,12235,12234, +24401,38002,12250,37662,36165,23951,36167,23953,34650,29703,34652,29705, +25917,25916,25915,25914,25913,25912,12270,24399,12269,12268,24337,37660, +12267,38000,36163,23949,34648,29701,39271,39270,39269,39268,39614, 9840, +39611, 9837,39311,39313,15815,18446,15664,18407,25958,25957,39326,39325, +15938,18403,11852,24415,11851,11850,24353,37676,11849,38016,11848,24410, +11847,11846,24348,37671,11845,38011,36179,23965,36174,23960,23948,36162, +34737,29790,34726,29779,23955,36169,25637,25636,25635,25634,39114,39113, +39112,39111,39110,39109,25665,25663,12587,18541,11879,18498,14047,18440, +25702,25701,39166,39165,14090,18402,39108,17936,11844,24405,11843,11842, +24343,37666,11841,38006,34806,10216,34670,29723,34760,10233,11867,24398, +11866,11865,24336,37659,11864,37999,34647,29700,10477, 2940,32467,16149, +32466,16148,22209,30149,27815,22206,30146,27818,21516,30358,29052,42781, + 5262,29055, 5650,32406,39962, 7642, 5292,32403,39959, 7639, 5289,30355, +31454,31143,27276,27113,15961, 8496,22495, 8542,26122,26114, 9817, 9816, +40359,40358,40357,38632,36068,38628,36066,25364, 5587,25362, 5585,27744, +35164,27741,32167,30182,43039, 8749,28286,30437,28285,30436,28557,30676, +23626,25167,28284,30435,15392,38911,22203,15663,28480,30608,28283,30434, +13472,38888,38891, 9556, 9558,32073,17462,28408,30540,28282,30433,28281, +30432,28546,30667,23599,25139,28280,30431,32743,17461,17460,16952,32742, +32889,17532,32841,17492,32823,28445,30560,26002,23327,17046,17276,23175, +41237,32840,32328,32320,26000,23174,17275,17045,23326,41235,16461,22851, +41251,16456,22848,41249,14757,14744,14494,14440,28961,31061,24084,26022, + 9874, 9881,41268,41241,15171,24103,26041,24102,26040,24101,26039,24100, +26038,24099,26037,41261,24098,26036,24096,26034,24095,26033,24094,26032, +28970,31070,24093,26031,17457,14547, 9868, 9879, 9866, 9877,28967,31067, +24111,26049,15173,41263,24108,26046,39341,22202,15662,28964,31064,24106, +26044,28960,31060,24082,26020, 9865, 9876,32852,17456,17548,32851,17455, +32816,32785,32916,17531,17044, 9586, 9584,32333,32325, 9591, 9589, 9595, + 9593,28279,30430,12843,38913,22201,15661,28482,30610,40679,23173,17043, +25319,23325,17274,40677,23324,17273,25317,23172,17042,32839,32327,32319, +13094,11464,19592,25465,14477,13090,14461,13088,11377,11369,28713,30829, +23737,25323,13425,39016,38994,39015,38993,39014,38992,39013,38991,39012, +38990, 9676, 9671,17454,28706,30823,28704,30821,11381,11373,13427,25313, +22200,15660,11379,11371,11376,11368,28703,30820,28278,30429,36332,17990, +14546,14542,14497,14496,22199,15659,17992,36342,13093,13092,22198,15658, +36055, 2866, 2829, 3638, 3744, 3499, 3633, 3740, 3484,11658, 3631, 3738, + 3477,11670,11655, 5719, 3176, 5341, 4133, 4211, 6542, 5449, 3341, 6652, + 3662, 3115, 5396,14196,14706, 3112, 5394,14194,14701,42953, 8732,11657, +19706, 7754, 3118, 5400, 5399, 3481,11676,19712, 7757,11654,19704, 7752, + 5403, 3483, 3120, 5402,14198,14713,27282,27129,15657, 8469,22197, 8525, +25605,25607, 5438, 3330, 5347, 2330, 4205, 6539, 4204, 6536, 4216, 6535, +18237,30015, 5335,42108, 5656,42107, 5334, 4130, 4202, 6534, 9809, 9807, +12024,12022,26008,26705,31206,13483,12498,43672,43671,41432,41401,28098, +41431,41400,28097,12417,12474,37392,41411,10446,37390,41407,43005,41462, +12490,13482,12473,13478,12472, 4325,13891,13909,41113,11025,11027,11024, +41934,41909,41932,41907,41930,41905,43557,43224,41928,41903,25633,26470, +39107,22052,15418,25818,26704,10264,10263,10262,10261, 9791, 9790, 2808, +12838,12830, 2417, 2470, 2512,26469,25852,26468,26467,26466,26465,12840, +41950,41925,41947,41922,41942,41917,41939,41914,15417,22051, 7469,12848, +12847,12835,26703,26702,26701,26700,26699,25611,31367,29516,42166, 4823, +42009, 4867,26799, 4810, 4873,34306,36887,42106,26907,41953,31562,29277, +34302,26934,42162, 4821,42005, 4866,31559, 4809, 4871,31363,29273,42105, +29513,41952,26759,36883,13383,12952,13474,12951,13042,12950,13020,12949, +14487,15161,13350,13349,20985,20984,27892,27483,13348,20983,25782,13347, +20982,25206,12948,12947,20544, 4262,12946,12945,26464,14072,26566,26463, +14046,26580,26462,18439, 4603,14071,13524,14070,18453,18438,17861, 4657, +26461,17863,13626,18471,14057, 4633,13523,14045,22196,21308,39164,39106, +39324,34716,29769,10260,10259,10258,10257,10256,10255,13793,13786,13790, +13783,10254,10253,10252,10251,42104, 9370,42103, 4943,42102, 8891,42101, + 4942,42100, 5521,42099, 4941,42098, 4940,42097, 5861,42096, 5548,42095, + 4939,42094, 4938,42093, 5224,39607, 6179,39604, 6176,39600, 5011,39597, + 5008,32103,30103,11072,11078,12111,13800,13797,39315,12034,12944,13018, +26606,13076,13075,13600,32387,18401,26557,32345,32365,14060,26556,14059, +26555,34072,34074,35363,33779,32386,32385,32344,32363,14015,13566,18400, +18399,32343,32360,41729,41726,14014,13565,32342,31867,18491,10102,13908, +18497,32463,16138,39646,39105,39267,39266,39104, 8395,38401, 6355,41721, + 2806,14942,14940,15509, 2419, 2472, 2515,15507,38567,30014,30070, 6192, +21609,14364,18236,30013, 5520, 4129,14974,15523,13346,13345,13381,13344, +13040,13343,14489,15163,24034,23979,13415,25700,25956,25911,18771,18770, +14056,10057,10087,17950,18452,18437,18457,10079,14044,17955,25910,25632, +25631,30112,25667,14089,14069,15937,24022,15654,15908,17948,18398,18397, +32341,15653,30111,12274,11878,12266,12265,12264,12263,11863,11862,11861, +11860,11840,11839,11838,11837,11836,11835,11834,11833,11974,11973,12233, +12232,11832,11831,11830,11829,12231,12230,11972,11971,12229,12228,12248, +12246,12260,12258,12227,12226,32071,25909,12471,23983,23975,13362,39031, +39034,23974,23973,24018,13370,24017,25987,23323,17041,17272,23171,41218, +41216,23170,17040,25985,23322,17271,16688,16705,41214,41212,41210,43228, +43561,41208,41207,43742,19608,11484,16702,22946,41206,41222,23193,17068, +25991,23345,17302,41220,32632,32631,32629,32628,24058,43750,43745,25983, +24057,43749,43744,25982,16694,16693,16711,11413,41203,43739,19605,11481, +16698,22943,41202,11444,12020,11198,11196, 2533, 5649,11154,11169,19364, +19373,23443,23442,25123,25132,17605,17604, 9495, 9494,11149,11164,19361, +19370,23441,23439,25120,25129,17603,17601, 9493, 9491,11276,19424,25193, +11263,28096,22842,19411,28093,16450,25186,22839,16447,14152,14598,14154, +14600,40573,43670,43669,40565,11273,19421,25190,11260,28090,22836,19408, +28087,16444,25183,22833,16441,40575,40567,11622,11639,19673,19690,23659, +23654,25584,25596,17786,17781, 9645, 9639,40577,40569,11148,11163,19360, +19369,23657,23652,25119,25128,17784,17779, 9643, 9637,11628,11646,19679, +19697,23437,23436,25589,25601,17599,17598, 9489, 9488,25173,21913,15232, +25175,22538,16001,12943,12942,20543,20542,27895,27486,11632,11651,19683, +19702,23644,23642,25591,25603,17769,17767, 9618, 9616,11621,11638,19672, +19689,23435,23433,25583,25595,17597,17595, 9487, 9485,24843,24802,27885, +27476,24840,24799,27882,27473,11280,11267,19428,19415,24847,24806,11243, +11222,11278,11265,19426,19413,24845,24804,11247,11226,11246,11225,11245, +11224, 9633, 9626, 9631, 9624, 9630, 9623, 9629,22193,22192, 9622,11609, +19664, 6702,11613,19668, 6646,11653,10814,19162, 2877,11724,19735, 6704, +11720,19731, 6648,12357,10807,19156, 2750,35666,14645,35664,14654,14724, +11679,19715, 6691,35668,14649,10928,10937,12356,22619, 3566,27955, 3703, +22617, 3564,27953, 3701, 3642, 3747, 3503, 3652, 3754, 3510, 3650, 3752, + 3508,12361,10908,19215, 2747,10907,19214, 2708,10905,19212, 2706,12355, +22624, 3568,27957, 3705, 3648, 3750, 3506,10913,19219, 2711,36153,14644, + 3409, 2351,16161,22670, 3623,22669, 3622,27996, 3733, 3621, 3732, 3472, +36155,14698, 5721, 3178,11637,11620,19688,19671,25594,25582, 6644, 2752, + 7104, 3068, 5757, 3211,22609, 3556,27948, 3691, 3647, 3749, 3505,11625, +19676, 2678,14720,14712,14697,12350,20053, 6708,10920,10912,19224,19218, +24965,24961, 6625, 2710, 7088, 3038, 5725, 3182,14643,11645,11627,19696, +19678,25600,25588, 6598, 2670, 7057, 3002, 5681, 3137,11643,11624,19694, +19675,25598,25586, 6604, 2677, 7061, 3006, 5685, 3141, 6665,11636,11619, +19687,19670,25593,25581, 6597, 2669, 7056, 3001, 5680, 3136, 7420, 8000, + 6079,22010,27569, 1609,15345,28036, 1696,16260,22730, 1661, 5493, 1546, + 6090, 1536,14782,14264, 5488, 3076,11352,19501, 6767,11351,19500, 6759, + 7423, 8002, 6081,22012,27571, 1593,15349,28040, 1694,16262,22732, 1659, +11360,11358, 4196, 6563, 4559, 2510, 5632, 6430, 6983, 5538, 5417, 3315, + 2532, 5648, 6454, 6991, 5552, 7418, 7998, 6076,22006,27567, 1615,15341, +28034, 1703,16258,22728, 1668, 7429, 8005, 6096,22015,27574, 1945,15354, +28043, 2010,16268,22736, 2045,11018, 7431, 8007, 6100,22017,27576, 1943, +15357,28046, 2008,16271,22738, 2043, 7354, 7956, 6008,21954,27522, 3450, +15280,27974, 3718,16127,22646, 3599,11022, 7356, 7958, 6010,21956,27524, + 3452,15282,27977, 3720,16129,22648, 3601, 3219, 5763, 3215, 5759, 3217, + 5761,12345, 7373,21966,15294,12347, 7375, 7969, 6025,21968,27538, 3512, +15296,28009, 3756,16192,22687, 3654, 6020, 3468, 7964, 3728,27533,27992, + 7367, 3617,21962,22665,15290,16157,11605,11607, 6022, 3470, 7966, 3730, +27535,27994, 7369, 3619,21964,22667,15292,16159, 7212, 7880, 5884,21812, +27338, 1956,15096,27654, 2021,15695,22246, 2056,15652, 7176, 7863, 5878, +21771,27307, 1958,15057,27622, 2023,15651,22191, 2058, 5882, 1979, 7878, + 2086,27336,27652, 7210, 2119,21810,22244,15094,15693,15650, 5877, 1981, + 7862, 2088,27306,27621, 7175, 2121,21770,22190,15056,15649, 5903, 1972, + 7898, 2079,27364,27706, 7254, 2112,21835,22345,15128,15791, 5905, 1970, + 7900, 2077,27366,27708, 7256, 2110,21837,22347,15130,15793, 7277, 7914, + 5937,21844,27383, 1947,15150,27748, 2012,15903,22442, 2047, 7279, 7916, + 5939,21846,27385, 1942,15152,27751, 2007,15905,22444, 2042,11579,15682, +15954,15648, 7174, 7861, 5876,21769,27305, 1951,15055,27620, 2016,15647, +22189, 2051, 7225, 7887, 5893,21818,27346, 1941,15107,27681, 2006,15745, +22304, 2041, 5965, 1995, 7927, 2103,27417,27801, 7292, 2136,21871,22489, +15188,15956, 5967, 1997, 7929, 2105,27419,27805, 7295, 2138,21873,22492, +15190,15959,15725,15646, 5875, 1974, 7860, 2081,27304,27619, 7173, 2114, +21768,22188,15054,15645, 5898, 1969, 7893, 2076,27359,27701, 7249, 2109, +21830,22340,15123,15786, 5963, 1993, 7925, 2101,27415,27799, 7290, 2134, +21869,22487,15186,15953, 5975, 2335, 1930, 5492, 3095, 5371, 1929, 5238, + 4396, 6018, 3466, 7962, 3726,27531,27990, 7365, 3615,21960,22663,15288, +16155,15644, 5874, 1989, 7859, 2097,27303,27618, 7172, 2130,21767,22187, +15053,15643, 5896, 1991, 7891, 2099,27357,27699, 7247, 2132,21828,22338, +15121,15784, 5901, 1987, 7896, 2095,27362,27704, 7252, 2128,21833,22343, +15126,15789,40964,40969,11764,11763,12001,12000,19881,19880,27891,27482, +25817,25816,22535,21910,15998,15229,11762,11761,19755,19754,27894,27485, +25630,25629,22537,21912,16000,15231,40905,40903, 9789, 9788,40901,40899, +40897,40896,40966,40971,40909,40907,40895,40894, 6556, 2584,10923,19227, + 7632, 5986, 2350, 6894, 5573, 5343, 2349, 6532, 2574, 6531, 2573,11135, + 5333, 2323, 6530, 2572, 6538, 2576, 6558, 2586, 6548, 2579, 6523, 2567, + 6552, 2581,42955,43330,42965,43372,42963,43370,43386,42977,43388,42979, +43328,42952,27399,27780,18424,10069,27918,27458,27867,27441,27850,27237, +18451,10078,26723,26534,25881,26460,25851,25613,25872,25850,25533,33674, +28935,33476,28743,34646,29699,15642,22186,10387,20541,25975,33672,28932, +25970,25973,25724, 6489, 6488,22185,16713,32639,32637,35179,30117,23822, +35131,30072,15714,22269,15641,22184,15819,22373,15848,22391,12098,15640, +22183,22249,16565,32569,32571, 9719,33478,28745,23832,25719,25969,25718, +34680,29733,14043,18436,14466,14392,25879,25878,39323,17980,39257,17970, +39317,17978,26434,29669,40064,43549,26428,39091,17935,39163,17943,39090, +17934,39625,18600,34033,34028,34031,32368,15883,33670,28930,41658,41677, +41679,39052,40789,10443,20697,10475,20687, 6684, 6686,10479,20540, 6681, + 6680,18396,10056,14100,18395,32340,15639,21550,16438,32542,32541,22717, +16437,32540,32539,15638,22182,35542,14642, 7787, 7786,10643,19009,10642, +24512,19008,24672,12068,19901,24016, 6774, 6773,15637,22181,34007,34006, +41647,41646,27847,27438,26554, 9787, 9786, 9785, 9784,43226,43559,43555, +43222,25849,25610,26004,26007,15109,14390,26698,25609,10775,34837,15977, +15208,22514,21889,27846,27437,15976,15207,27398,27397,21864,21863,22513, +21888,27779,27778,15181,15180,27845,27436,22476,22475,15944,15943,24033, +24032,23978,23977,27844,27435,22564,22563,16041,16040,27843,27434,21895, +21894,15214,15213,27842,27433,22520,22519,15983,15982,15206,21887,15205, +27865,21886,27456,27841,27432,21015,21014,13387,13386,27840,27431,20628, +20627,13030,13029,15204,21885,25773,12104,12103,11744,11743,12094,12096, +10779,34810, 9700,23972,23971,27861,27452,20981,20980,13342,13341,11739, +11738, 7942, 7940,10724,10721,23970,23969,23982,24015,23968,23967,24014, +23981,24013,24025,24027,24012,12070,27864,27455,22512,22511,15975,15974, +27863,27454,20636,20634,13035,13032,24511,24671,19132,19131,10760,10759, +10732,10735,24516,24676,19106,19108,10731,10734,24510,24670,19113,19112, +10741,10740,24509,24669,19062,19061,10684,10683,10781,34759,15212,21893, +25753, 7947,10429,10571,18816,18957,24508,24668,10426,10568,24505,18813, +18954,24665,27870,27461,21901,21898,15220,15217,27873,27464,22526,22523, +15989,15986,10439,10588,18828,18964,24526,24686,10437,10586,24524,18826, +18962,24684,11737,11736,11945,11944,10596,10595,11735,11734, 9849, 9848, +12067,12066,14142,14587, 9783, 9782,12065,12063,19900,19898,25848,25846, +24011,24009,17969,17967, 9847, 9845,27876,27467,20539,20538,12941,12940, +15223,21904,25776,12061,12060,14139,14584,12117,12115,19916,19914,25887, +25885,24044,24042,17976,17974, 9858, 9856,11432,19557,25411,28147,22914, +16602,41080,11934,19847,25742,28084,22830,16436,12222,12284,16672,12217, +12215,41025,43668,11931,19844,25739,28081,22827,16433,11412,41027,11312, +19473,25900,25247,19933,12130,41029,11109,19341,25898,25106,19931,12128, +12154,19940,25920,28156,22923,16635,12183,19958,25945,25943,19956,12181, +12158,19944,25924,25951,19964,12197,11798,19788,25675,25673,19786,11796, +11776,19772,25641,25639,19770,11774,24992,20537,12939,12938,20536,25208, +12059,11760,19753,25628,25627,19752,11759,11818,19804,25696,25688,19797, +11808,11820,19806,25698,25690,19799,11810,40597,40758,40595,40594,11733, +11449,11453,19574,25452,28145,22912,16597,12058,11816,19802,25694,25686, +19795,11806,40639,41075,40637,11758,16629,11108,12193,11999,19879,25908, +25815,19937,12138,12206,12205,12038,11757,19751,25785,25626,19870,11960, +12937,20535,25784,25002,20534,12936,12057,43728,43725,32603,11411,19550, +25402,28149,22917,16605,23999,25793,23996,25790,11938,19851,24003,11913, +11936,19849,24001,11917,11916,11915,12045,12042,13120,15165,12156,19942, +25922,25823,19886,12009,12122,19926,25891,25643,19774,11778,12125,19928, +25893,25625,19750,11756,11815,32602,30882,43667,40537,12120,19924,25889, +25693,19801,11814,11107,12033,12192,19962,25949,28154,22921,16628,12191, +19961,25948,12190,19960,25947,25839,19892,12032,12296,19991,26015,12292, +27396,21862,19981,27777,15179,26011,22474,15942,15399,12056,12846,12547, +17076,15466,12055,15522,39256,39255,39254,39253,39252,39251,39263,39265, +39250,39249,39248,39247,41047,41046,41059,41061,41051,41045,41043,41042, +41041,41040,41039,41038,41037,41036,41035,25752,22562,16039,25751,22518, +15981,25750,21013,13385,25749,20626,13028,13044,20642,25772,11943,12102, +11742,12092,41054,41053,41056,12137,41034,14685,12136,11996,25761,20979, +13340,11732,12162,25907,17091,15471,15526,39246,19972,12273,12354,17039, +15458,15506,25771,22510,15973,24007,19896,12053,12090,24029,19903,12089, +24006,19895,12052,24005,19894,12051,15407,12851,41065,41064,41063,41070, +41068,41049,12119,41032,25779,22529,15992,11924,12041,41078,41073,11731, +11311,19472,25246,28144,22911,16596,12050,25764,20533,12935,12049,12100, +19908,25863,12189,12110,11106,19340,25105,28153,22920,16627,12109,19912, +25883,12262,11859,17316,14861,11730,14973,14817,11729,12834,12408,39101, +39103,39089,39088,39087,39086,39093,39095,39085,39084,39125,39121,39124, +39120,40887,40886,40885,40884,40883,40882,40927,40893,40881,40880,40879, +11755,40878,14710,14683,11754,11753,25624,25623,14807,12832,39083,20489, +12859,39082,20052,12349,39081,21547,14236,39080,39079,43776,43805,39078, +40447,43732,17270,14840,14939,17396,14901,15027,12225,12244,40915,40914, +40913, 9781, 9780,11077,40876,40925,40891,40889,40936,40934,39151,39147, +39150,39146,11813,11812,40932,40930,40938,14394,11970,12224,40944,40943, +40942,11828,11827,40940,11826,40911,40947,39308,39307,39140,39139,11752, +19749,25622,25621,19748,11751,14389,39077,39076, 9722, 9721,12135,19936, +25814,25906,19878,11995,12934,13377,12967,13339,39117,39116,39149,39145, +39154,39153, 9830, 9829,13375,12933,13338,12970,40737,40739,40734,17453, +16185,16111,14676,12161,12134,12133,19935,25905,25813,19877,11994,12179, +11794,11750,11749,16595,11439,11438,19567,25441,15755,17269,15754,15753, +25531,25446,25408,19555,11420,25620,26459,25475,25474,25473,19599,11474, +25445,25444,19570,11442,25406,19553,11418,25399,19547,11408,16959,17482, +13265,13337,15636,39056,22178,15634,28773,30900,39099,39097,39119,39123, +39075,39074, 9703, 9705,17452,28766,30894,28763,30892,28762,30891,12175, +19951,25937,25837,19890,12018,40756,16626,11529,39135,39137,14641,11793, +19784,25671,11748,19747,25619,12932,20532,25424,12048,11528,11522,11747, +19746,25618,11780,19776,25645,12178,19954,25941,11436,19565,25426,11993, +11992,19876,19875,27860,27451,25812,25811,22516,21891,15979,15210,25844, +30888,11476,30863,11415,25810,12017,12016,19889,19888,27839,27430,25836, +25835,22509,21884,15972,15203,25834,25940,19953,25939,19544,25396,25395, +25617,19745,25616,19562,25420,25418,25647,19778,25649,19560,25416,25414, +25670,19783,25669,19543,25394,25393,34715,29768, 1823, 1805,12576,24366, + 1065, 1012,24432,38030,12599,37738,35076,34899,38032,37740,24173,22266, +24434,21419,27315,26681,20922,13278,20920,13276,29112,20926,13281, 1859, + 1842, 1150, 1140, 9932,39742, 9931,39741,24819,39740,34742,29795,13907, +18540, 1482, 1476,12573,24363,12596,37734, 1062, 1009,24428,38026,12611, +42344,12595,37733,35078,34901,38034,37742,24175,22274,24436,21423, 9930, +39739, 9929,39738, 9926,39729, 9925,39728,24831,39727,29130,37421,29129, +37420,29099,29109, 1469,36495,37425,37424,37423,12586,18539,12624,42360, +20171,42676, 1479, 1473,12626,20173,42362, 1481,42678, 1475, 8054, 9922, +39720,29125,37415,38037, 1149, 1139, 1152, 1142,12620,20167,42355, 1145, +42671, 1135,35453,39526,38500,38702,24594,22314,41882,29647,27291,27290, +27617,27314,27289,12795,30387,38366,30408,38358,38363,38365, 2930,38357, + 2387,37255,29642,37254,29641,37253,29640,19022,43201,30416,38370,27663, +37246,36144,37245,38299,40063,31679,40062,31678,31738,31737,31705,35446, +31723,24604,15633,41888,29654, 8044,31735,38376, 2386,35443,31719,35442, +31718,32516,16284,32479,16283,35456,39528,38504,38704,24596,22316,41884, +29649,24601,29658,41890,15385,37241,37244, 1421,40058,31676,40057,31675, +38385,12811,24756,12793,29602,30390,38389,38388,29600,29596,29618,31704, +34636, 1433,29689, 1438,43219,43551,24599,29653,41887,15383,24598,29652, +41886,15382, 1285, 1418, 8050,38379,12806,24754,12791,24750,12787,38378, +38384,12810,38375,38387, 1306, 1294, 1303, 1291,29617,31703,29612,31698, +34634, 1429,29687, 1436,43217,43548,34630, 1300,29683, 1288,43212,43540, +30964, 3798, 3778, 3800, 3780,35425,30959,32462,16137,32461,16136,36652, +34579,34586,36650,34577,34584,34085,29464,31470,29585,31656,31506,29370, +29462,31468,29583,31654,31504,29368,36644,21875, 7932,32392, 5254,31444, +20450,38667,12312,36398,36403,31443,31624,36800, 8757,12755, 8518,15194, + 8852,38456, 8493,31120,31133,36972,36799,36971,12754,32134,20030,31623, + 6169,20449, 7030, 8702, 8337,38763, 9313,41293, 8934,36480, 8701,43062, + 8761,18031, 8593, 8173,38803,36371,36370,36369,18030,18029,18028,27225, +41287,29017,38339, 8204,38308, 8169,34386,32391,34385,34228,31132, 7931, +33774, 8517,31642, 8756,34227, 5253,36397,31641,38666,35366,35201,38455, +33771,29576,36805, 6168,29575, 8492,36402, 8851,29015, 7029,36978,36804, +36977,30301, 8061,41292, 7297,38121,38120,36368,18792,38119,36491,38118, +38117,38116,31119,31118,41283,18027,41285,18033,27911,27641,42858,42857, +26103,35204, 5619,36383, 5616,36380,24771, 7553,40207, 6407,31301,24617, +34240,40205,34238,34098,31299,39906,36663, 7199,39905, 6221,43351, 7551, +34096, 6406,29208,43349,36818,36661,36816,42721,29206,27330,42720, 7197, +27172, 6220,24769, 7550,40204, 6404,31297,24616,34236,40203,34234,34094, +31295,39903,36659, 7196,39901, 6218,43348, 7549,34092, 6402,29204,43347, +36814,36657,36812,42718,29202,27329,42716, 7195,27170, 6216,34504,30404, +34501,30401,18155,41645, 6885, 6148,41642, 7307,41673,41778,22311,41672, +22818,41641,22547,41640,22332,41639,26348,26380,22545,41668,18159,41675, +38227, 2955,34005, 2993, 2916,38213,34004, 3252, 2894,38183,34025, 3229, + 2887,38169,34020, 3222, 2627, 2643,38824, 9409,41720, 9218,36476, 2626, +41719, 2595, 2647, 2634,23169,18235,22102,18234,18217,21766,22081,18233, +37188,18232,18231,18230,18216,21765,21923,18229,18215,36632,14274,35340, +34569,20455, 2653,31640,31453,36976,12760,36803, 2617, 8060, 8540, 8244, +38393, 6849,41718, 6440, 2625,36148, 8149,38297,41717, 3792,18228,18227, + 6920, 6161,18226,18225,18224,18223,21921,18222,18221,41757,26415,41756, +26414,20448, 2591,31622,31442,36970,12753,36798, 2464,18210,26408,26407, +41754,34549,35325,41666, 7314,41665,41776,22693,41664, 6432,42556,18178, +26697,26356,26393,34087,38762, 9312,41711, 8933,36479, 2483,41710, 2455, +38802,37153,37152,37151,18209,18208,21763,18207,29565,21760,37224, 2557, +18220,37221, 2554,36643,20333,36602,39439,31285,36604,39484,24264,40237, +40132,40235,40130,39442,31290,36609,41735,39478,24258,40093,40186,40088, +40181,18143,34398,34435,37029,20279,36994,18145,34400,34437,37031,20281, +36996,39483,24263,39482,24262,39486,24266,40091,40184,40086,40179,40083, +40176,40081,40174,40241,40136,40239,40134,34758,10627,34757,10559,39303, +39305,34812,10557,37291,38086,37290,38094,37289,38080,38614,37288,38662, +37287,38664,37301,38656,37473,38655,30133,20531,10622,20530,10525,18394, +10055,18422,10067,18393,10054,26458,26696,10410,13522,15751,22319,25877, +34645,29698,34644,29697,34660,29713,38660,37435,37440,34753,29806,34751, +29804,34672,29725,34643,29696,34642,29695,34656,29709,34662,29715,34730, +29783,34732,29785,34677,29730,34641,29694,34640,29693,34667,29720,34666, +29719,37327,38096,20808,13191,18392,18391,10053,20807,20851,13231,18390, +18389,10052,20850,20857,13235,18421,18419,10066,20855,18410,10062,18477, +10096,20791,13179,18409,18412,10061,20790,14025,18388,13571,18387,20836, +13215,18414,18417,10064,20835,13521,18416,34836,10553,34691,29744,13625, +18470,13520,18480,39295,39298,12931,20529,20572,10615,12981,20574,12930, +20528,12929,20527,12928,20526,32453,16108,27288,18450,10077, 5631,37822, + 5630,42452,42565,18490,10101,13290,20941,13178,20789,13214,20834,34706, +29759,34699,29752,13177,18474,20788,20787,18476,10095,13213,18479,20833, +20832,18482,10098,13176,18538,13175,18496,32478,16282,32460,16135,13212, +20831,13174,20786,13220,20838,13190,20806,13230,20849,32339,15631,32362, +15847,32337,15629,39186,39188,15802,22360,25843,39259,39261,15628,22173, +25858,39245,39244,12083,12081,10274,39719,40156,40256,40155,40255,24386, +15813,24636,13407,30271,30269, 6595, 6594,34679,29732,14042,18435, 6251, + 6249,13292,20943,34704,29757,34702,29755,34701,29754,13119,13118,18533, +32482,16287,10342,39718,40154,40254,40153,40253,24396,13488,24439,13440, +30194,30193,34714,29767,38659,37400,37405, 8700,30362,30361,30170,30169, +37413,34739,29792,39737,10247,30192,30191,34741,29794,13117,37729,13629, +42340,24441,13487,24438,13490,39736,10215,39748,10210,39731,10209,39726, +10220,39735,10208,39725,10245,39733,10222,39717,10213,20954,10594,10379, +39764,10378,39763,39762,10528,40246,40143,40245,40142,40220,40105,40219, +40104,18564,10135,20952,13301,18563,18571,10134,20960,10373,39756,18559, +10130,20948,13297,18558,18567,10129,20956,10205,34916,34923,37772,13502, +37765,10203,34914,34921,37770,13500,37763,39761,10377,40172,40079,40170, +40077,40152,40252,40151,40251,39766,10523,40161,40261,40160,40260,40233, +40128,40231,40126,34851, 9960,34232,34090,10334, 1864,21204, 1757,32005, + 1601,35035, 1797,31293,31509,31895,20354,29864,18274,29200,29467,37983, + 1814,29926, 1571,18655, 1742,13858, 1881,36810,36655,37623,12692,34934, +34778,34003, 7306,34002,29395,34001,34498,34011,34508,34000,34497,40229, +34492,41761,34931,34775,41968,34018,34514,40243,34516,42397, 1037,41633, + 1031,41632, 1048,42392, 1022,41635, 1050,42394, 1024,41760,41631, 1046, +42390, 1020,41629, 2954,41628, 2992,41627, 2915,41626, 3251,41650, 2892, +41652, 3227,41656, 2886,41654, 3221,34263, 9979,34254,34121, 9970, 936, +20366, 931,31521, 926,34112, 921,31315,31530,31324,20375,29222,18286, +29231,29479,36686, 916,29488, 911,18294, 906,12712, 901,36842,36677, +36833,12703,34381,31639,34224, 2650,34226, 2616,31621, 2599, 2652,36975, +34384,36802,29572,36797,29574,36969, 2482, 6384,37645,37150,37149,39645, +37635,37644,37632,37148,37147,37146,37145,37144,27910, 2624, 8314, 6262, +41716,37187,38260,37297,37347,42609,32086,38078,37639,34016, 7313,34015, +29397,34014,34512,37090, 2481, 2480, 2630, 2637, 2505, 6421,37518,37128, +37399,37127,37495,37126,37469,37124,37468,37513,37142,37123,37511,37462, +37299,37756,37434,37122,37141,37121,37140,39431,36631,37412,37404,37498, +37494,37472,10043,37461,37460,37467,10039,37458,37457,37647,37138,37137, +37120,37510,37517,37520,37174,37505,37439,29444,37119,37136,37135,37172, +37170,37118,37117, 2641, 2632,39414,26406,26405,27640,42564, 2623,37566, +37286,31638,12764,12757,41733, 2622, 2621,37527,37402,37492,10045,37455, +37454,37465,37185,37508,37437,37184,37183,26413,27645,42561,20297, 2612, +20295, 2610,35114, 2608,35112, 2606,12763,37557,39421,18930,22264,27005, +21417,13951,24817,38564,38257,38562,37452,38560,30007,38068,32735,32711, +38761,32710, 9311,38760, 9310,38072,32709,38071,32901,38070,32764,17268, +23321,24825, 9217,24780, 9216,17267,38060,16897,38059,17340,38058,17530, +38066,27635,27312, 7608,38801,32708,27786,32093,30095,22171,15627,38645, +30126,38644,38643, 5247, 5530,32815,38800,32814, 9381,38799, 9380,38075, +32813,27684,30333,18707,35295,27162,27153,35405,22170,39882,38555,24446, +22351,39919,38609,24465,35332,27193,35291,27160,27150,35403,42950, 8731, +42949, 8730,42948, 8729,42947, 8728,35377,32076,40005, 8482,40002, 8479, +35541,14640,35540,14639,14313,27249,27251,17038,23168,24827, 9024,24782, + 9023,17037,38063,27021,16563,38690,23265,21508,37451,38084,32336,32515, +15626,32520,15625,32511,15677,32508,15624,32529,16280,38571, 5200,32335, +15623,35539,14638,14662,14637,35552,14666, 8626, 8629,14668,14708,18932, +22271,27007,21421,13942,24829,30360,42780,35108,30005, 5647,35143,30085, + 5665,22165,18820, 5605, 5604,35347,32111,32506,15929, 6955, 5331,40061, +43546,42762,18769, 6937, 5272, 6936, 5271, 6935, 5270, 6933, 5268, 5629, +22268, 6939, 5274,22180,22164, 6946, 5281,25809,32240,18748, 8323, 8156, + 8316, 8158, 8320, 8153, 5580,35374,32097,35335,32096,35334,32095,32513, +15882,32367,15881,38759, 9309,43061, 8932,36478, 8059,38798,38115,38114, +38113,18791,18790,18789,30347,37642,37515,37450,38130,37755,38112,38111, +27909,27639,37569,38128, 3646,22685,16184, 6338, 1522,12640,12645,15371, + 6342, 1510,12642,12647,15373, 6782, 6781,14798,21699, 6344,14796,21697, + 6341, 7801, 1555, 7440,12375, 7443, 8012, 6107,22026,27582, 1592,15367, +28052, 1693,16277,22744, 1658, 1489, 7435,12373, 7437, 8010, 6105,22021, +27580, 1940,15362,28050, 2005,16275,22742, 2040, 7442,22025,15366, 7439, +22023,15364, 7426, 1562,12340,12333,12338,12331, 5804, 3214,12342,12335, + 6027, 3514, 7971, 3758,27540,28011, 7377, 3656,21970,22689,15298,16194, +15505,12533,15504,14938,16379,14982,16012,12532, 6752, 1497, 6749, 1494, +41395,41428,41393,41426,41397,41430, 7673, 2785,12396, 3807, 8862,12509, +12436,12393, 5370, 3094,12439,12401, 5365, 3088,14737,14216, 5440, 3335, + 3128, 5405,14200,14716, 3087, 5364,14181,14664, 7871,12398, 4411, 4409, +12435,20087, 6593, 3074, 5351,14171,14636,12448,20094, 6591,12546,15118, +21825, 7236,12545,20135, 6451,12407,12544,12406,12388,12506, 3804, 8859, +12522,12505, 7664, 2776, 7671, 2783, 7717, 2884,41403,41434, 6017, 3465, + 7961, 3725,27530,27989, 7364, 3614,21959,22662,15287,16154,15051,21757, + 7169,12568,20156, 6382,14937,21735, 6848,41461,41460,12427,20084, 2755, +31205,31204,16392,12489,12488,12426, 4417,13481, 2225, 7635, 2415,15621, + 7167, 7858, 5873,21755,27302, 2239,15049,27613, 2245,15620,22163, 2261, + 7304, 7938, 5973,21879,27421, 2241,15198,27830, 2247,15967,22504, 2264, + 7251, 7895, 5900,21832,27361, 2237,15125,27703, 2243,15788,22342, 2257, +16032,12416,20081, 7526,31201,12415,12414,15686,15952, 2667,15619, 7166, + 7857, 5872,21754,27301, 1949,15048,27612, 2014,15618,22162, 2049,12423, + 7223, 7885, 5891,21816,27344, 1939,15105,27679, 2004,15743,22302, 2039, +12404, 7351, 7953, 6005,21951,27517, 3446,15276,27968, 3715,16106,22638, + 3587, 2256,21296,12567,13748,20155, 2235, 2255,21295,12566,13747,20154, + 2234,12560,20148, 6450,41459,15243, 3788,41450,15617,41420,41458,12425, +20083, 2209,31821,12497,12496,15117,21824, 7235,12562,20150, 6449, 2227, + 2223,12487,20114, 4245,12485,20112, 4243,12875,20501, 4242,14245,41418, +41456,12559,20147, 2220, 6016, 3464, 7960, 3724,27529,27988, 7363, 3613, +21958,22661,15286,16153,12500,12432, 7405, 7990, 6044,21993,27559, 1591, +15324,28021, 1692,16231,22715, 1657,15316,41467, 2217,12430, 7400, 7988, + 6042,21988,27557, 1938,15318,28019, 2003,16229,22713, 2038, 7404,21992, +15323, 7402,21990,15321, 1457,21287,12552,13738,20140, 1453,41444,15320, +41469, 1463,15615,15612,16030,12413,20080, 1444,12412,20079, 1443,31824, +31818,12565,20153, 2233,12484,32070,12483,12482,13480,13746,21294, 2254, +12481,20111, 4241,12480,20110, 4240,12492,20116, 6512,12479,20109, 6380, + 2268,22161,15610,15609,22160, 2259,15966,22503, 2263,12420,12419,15685, +22239, 2251,12495,12558,20146, 2219,12554,20142, 1460,15103, 2216,41454, +12422,41409,41438,15245, 3790,41453,11020,40855,30973,11578,11577,11591, +11575,11589, 4564,11583,11582, 2666,11586,11585,12828,12826, 5829, 3832, +12874,12870,20498, 3812,12869,20497, 3811, 3831, 5828, 3830, 5827, 3834, + 5831,12877,12879,12873, 7468,22050,15416, 2531, 5823, 3818, 3836, 3843, +12884,20506, 3875, 3905, 5843, 3898, 5839, 3900, 5841,12888, 7474,22057, +15423,12890, 7476, 8024, 6122,22059,27597, 3926,15425,28070, 3966,16337, +22799, 3955, 6117, 1968, 8021, 2075,27594,28066, 7460, 2108,22041,22773, +15405,16310, 7454, 8017, 6112,22034,27590, 1937,15390,28062, 2002,16302, +22767, 2037, 6115, 1983, 8019, 2091,27592,28064, 7456, 2124,22037,22769, +15401,16306, 7450, 8015, 6110,22032,27588, 1954,15388,28060, 2019,16293, +22756, 2054, 6109, 1977, 8014, 2084,27587,28059, 7449, 2117,22031,22755, +15387,16292,12895, 7488, 8031, 6129,22071,27604, 1590,15438,28077, 1691, +16344,22806, 1656, 3810, 3826, 3829,12893, 7483, 8029, 6127,22066,27602, + 1936,15433,28075, 2001,16342,22804, 2036, 7487,22070,15437, 7485,22068, +15435, 5838, 3897, 3886, 3910, 3915, 3877, 3891, 3902, 3874, 3889, 3896, + 3873, 3888, 3895, 3879, 3893, 3904, 6124, 3932, 8026, 3972,27599,28072, + 7479, 3961,22062,22801,15428,16339, 3884, 3908, 3913, 3809, 3825, 3828, +12865,43305,43632,43303,43630,43300,43627,43299,43626,14287,43298,43625, +43296,43623,14936,21734, 6847,22080,14935,22079,19102,22101,19101,14934, +43294,14933,43621,14932,14931,15462,15475,15457,15456,14956,14950, 6852, + 6846, 6845,15485,14333,14993,14992,14981,15007,15006,15005,15022,15014, +15010,15009,15004,15025,14351,15024,15483,15020,14349,15012, 5524, 1639, +14997,14337,14991,14332,14980,14979,14978,14995,14335,14984, 5519, 1906, +14339,15000,14999,43316,43637,15003,15479,14306,43314,43635,15041,14354, +15040,15493,15044,14356,15043,15495, 6866,14914,21725, 6369,14916,21727, + 6371,12858,20488, 6820,12872,20500, 3817, 3953,22796,16334,12883,20505, + 3872, 3952,22795,16333, 3951,22794,16332, 3865,22785,16323,19032,19100, +38551,19030,19099,38548,30001,29999, 6915, 4625, 4602,35104,29997,15608, +22159,15607,22158,15821,22375, 3127, 3334,35145,30087, 3314, 3325,15606, +27585, 4632,15846,22390,40060,43545, 2385, 2530, 2543,40009,43415, 2964, +38234,30168,30190,40054,43537,30277,23167,19097,40033,43462,38797,32707, + 2929,30189,40458,23320,17266,25101,23166,17036,40446,23349,17310,25097, +23198,17075,38926,38925,38924,38923,39028,39026,38922,38921,38920,38919, +39041,39043,40452,40462,40454,40445,40444,40442,40449,32588,32587,32586, +40456,23339,17294,25099,23187,17062,32922,32788,16896,17339,32594,40441, +16617,25096,16879,23055,40439,16911,23063,40438,40437,23400,17527,16871, +23049,40436,40435,22906,16587,40434,22905,16586,16585,22904,40433,40432, +22903,16584,16868,23046,40465,16590,22909,40464,16609,16607,16594,43731, +43730,40431,40430,39039,39038,39037,39036,16616,38937,38936,39243,39241, +38935,38934,39292,39288,17173,16615,16752,16614,16613,16612,16744,16611, +16789,39239,39238,16852,17211,32900,32763,16623,16621,11076,11075,39237, +39236,39278,39277,39276,39275,11952,19862,25769,25748,19855,11942,32899, +32762,17499,17451,17325,25842,21445,14034,17484,16791,25870,23275,17187, +25841,23273,17185,25876,23263,17165,11948,19858,25763,13414,13413,39216, +39215,39197,39196,39291,39287,12478,12477,13373,13336,13335,13368,12031, +12030,39235,39234,12108,12107,39284,39283,11941,11940,19854,19853,27900, +27491,25747,25746,22542,21917,16005,15236,39233,39232,39203,39201,27879, +27470,27855,27446,27838,27429,25904,25903,25745,25955,25902,18666,42563, +25759,25755,25998,27775,25808,27637,27015,27615,41158,41157,27836,27427, +27049,27611,39207,39211,11951,11950,19861,19860,27835,27426,25768,25767, +22508,21883,15971,15202,25766,11956,11954,19866,19864,27875,27466,25778, +25775,22528,21903,15991,15222,39221,39219,39224,39223,39290,39286,39231, +39230,39281,39280,14930,14929,14847,14954,14316,21588, 3816,14319,21591, + 3882,14318,21590, 2882,14948,14946,14294,21570, 893,14296,21572, 964, + 8262, 8413, 8261, 8412, 8253, 8404, 3081, 5860, 3167, 5859, 3175, 8260, + 8411, 1395, 1411, 1414,21584,14311,14347,21606, 1403,14305,21581, 1409, +14342,21602, 3815, 8259, 8410, 8258, 8409, 8252, 8403, 1406, 1393, 1111, + 1117,14703,14648,14660, 8187, 8350, 3080, 2322, 5710, 3166, 5718, 3174, +11162,11147,19368,19359,25127,25118, 6642, 2749, 7102, 3062, 5755, 3209, +11161,11146,19367,19358,25126,25117, 6623, 2705, 7086, 3036, 5723, 3180, +14635,11168,11153,19372,19363,25131,25122, 6590, 2665, 7054, 2999, 5678, + 3134,11160,11145,19366,19357,25125,25116, 6600, 2674, 7059, 3004, 5683, + 3139,14894,16401,15605,15603,14362,15842,14927,16368,43326,42946,43324, +42944,43321,42941,42939,43319,43255,43588,43263,43596,43262,43595,14473, +21643, 6260,30231,32200,38696,30229,38545,30262,30167,38588,30018,30166, +30012, 3276, 3434,43569,43236,43566,43233,16329,22791, 6234,16050,22573, + 6247, 7829, 7736, 7585, 7869, 7600, 6756, 7794, 7740, 6576,14475, 6583, + 7597, 4175,14479, 4170,15601,22157, 6792,15140,15600,22156, 7261,14360, + 2881, 2805, 5527, 2986, 2945, 2937, 2928, 2927, 2384, 2977,38654,30261, + 8287, 8126, 8281, 8116, 830, 8286, 8125, 8285, 8124, 824, 827, 8303, + 8131, 8302, 8123, 8307, 8101, 8300, 8301, 8120, 8121, 8299, 8119, 8297, + 8118, 8295, 8104, 8292, 8129, 8306, 8103, 8308, 8106,30038,30035,38259, +30315, 8425, 8427,32057,14884,13417,15599,22155, 6511,15656,22195, 7821, + 6331, 6427,15712, 6424, 7414,15705,22257, 6844, 5887, 5799,16243,14926, +21733, 6841,15598,15965, 6473,22154,15597,15868,15596,15840,22386, 6843, +14445, 5911, 5635,14453,14444,14448,15830,15844,22388, 6840, 5913, 5628, +15833,15827,15826,14839, 7575,15595, 2872, 2741,14925,21732, 6839,15594, +22153, 2833,15593,22152, 2835,15592,22151, 2438,15591,22150, 2432, 2664, +15875,22409, 2445, 2717,15589,22148, 7839,16355,16374,15782,22336, 7823, +16357,22809, 7240,16207,22702, 7765,14756,14227,14755,14226,14754,14225, +16218,16217,43513,43174,43510,43171,16224, 6719,21546,14235,16216,22706, + 6725,14763,14230,16220,15588,22147, 7390,15933,22467, 7388,43505,43166, +43502,43163,16209,22704, 7767,15811,22366, 7394,16146,22656, 7387,16145, + 3605,22655,16144,15587,22146, 7231,15855,16035,15854,16034, 2839,15860, +22399, 2838,15859,22398, 7229,15586,22145, 7501,30128,38653,30132,38652, +38681,38651,30131,13334,16069,22590,16078, 6270,22599, 6269,16181,12927, +27932, 6914, 6960, 5414, 8057, 5957, 6506, 6505,18941,18940,10538,10537, +16179,13333,16170,13332,16388,15017, 7588, 6306, 5236, 5811, 5851, 5869, + 6740, 7127, 5798, 6468, 6996, 5564,14405,14018,14404,14017,15770, 6466, + 6994, 5562,14450,14447,15769,15768,15767,16373,22815, 7228,14407,14020, +15772,16122,22643, 7227,14409,14022,14443, 5547, 5223,32069,27917,16038, +22561,16037, 6291,22560, 6288,27915,15874,22408, 4435,15801,22359, 4459, + 6574,22144,15585, 6562,22358,15800, 6582,22406,15872,16011, 5655, 4128, +15584,22143, 4437, 5660, 4093,14634,14633,14632,14631,43483,18998,16048, +22571, 6258,43478,43131,43475,43128, 6707,21367,13906,16183,22684, 4239, +14678, 6689,21336,13835,14680,16124,13331, 5770, 5919,14672,14671, 5768, + 5868, 5766, 5907, 6561, 4189,15911,22448, 6838, 5943, 5627, 6442, 6987, + 5542,15703, 5594, 5416, 5598, 6075, 7011, 7997,28033,27566, 6496, 7417, +22727,22005,16257,15340,15583, 5600, 5867, 7013, 7854,27610,27300, 6498, + 7163,22142,21753,15582,15047, 5945, 5645, 6724, 7116, 5787, 5602, 5947, + 7015, 7918,27754,27388, 6502, 7285,22459,21851,15927,15157, 5597, 5962, + 7010, 7924,27798,27414, 6495, 7289,22486,21868,15951,15185,15455,16352, +15503,16351,16350,15513,22106, 7835,14844,21717, 7820,14886,21720, 7819, +15410,22044, 7818,14825,21708, 7817,14824,21707, 7816,16199,22692, 7815, +14883,14823,43587,43254,43586,43253, 7825, 2663,16362,22811, 1398,16367, +16366,43289,43616,43594,43261, 2414,16372,22814, 1155,16371,22813, 2421, +16359,43280,43613,43252,43585,14838,16365,16364,43278,43611,43275,43608, +43273,43606,43291,43618,15447,38524, 8682,15449,38526, 8684,43271,43604, +43270,43603,43598,43265,43259,43592,16370,43269,43602,43017,43408,15858, + 3596,22642,16121,15857,22397, 2837,14234,21545, 6718, 5782,14743,21688, + 6318,16198,16215,16304,16214,16197,16213,16206,22701, 7764,16205,22700, + 7763, 6717,21544,14233,16291,16212, 5810,43103,43458,40124,14170,40123, +14169,30188,30187,38695,30186,38694,30185,32066,32060,14630,14629,14628, + 3343, 7680,39970,32449, 5425, 7677,39967,32446, 5422, 8440, 8451, 3712, + 3440, 3696, 3415, 3589, 3585, 3711, 3439,14244,43101,43456, 6671,21079, +13519,15581,16237,43099,43454,43111,18997, 3710, 3438,43109,18995,43123, +43470,43120,43467,16101,22636, 3583,15932, 3604,16312,16142,14805, 5765, + 5233,43113,43460,16120, 3595,16119,22641, 3594,16118,22640, 3593,14803, +16047,22570, 3522,16046,22569, 3521, 6706,20785,13173, 6447,21556,14255, + 6445,22725,16252, 6333, 6444,14418,14028,14442,15337, 5596, 6067, 7009, + 7995,28031,27564, 6494, 7412,22724,22003,16251,15336, 6435,21554,14253, + 6438,22722,16249, 6434, 7416,14753,14387, 5540, 6065, 6985, 7993,28029, +27562, 6437, 7410,22721,22001,16248,15334,15301,21973, 7380, 6030, 5803, + 6029, 5775, 6743, 7129, 5801, 6731, 7122, 5793, 6727, 7118, 5789,14760, + 6032, 5777,14746, 7386,21979,15307, 5781, 5452, 6034, 5779, 6723, 7115, + 5786, 5795, 6039, 7124, 7983,28016,27552, 6733, 7396,22710,21984,16226, +15312,14752,14748, 5791, 6037, 7120, 7981,28014,27550, 6729, 7392,22708, +21982,16222,15310,43027,14465,21641, 6257,16327,22789, 6256,32237,15002, +14464,14463, 6589, 6484,21453,14051,16246,14469,14468,15689,15950, 6254, +15580, 5571, 5866, 7001, 7853,27609,27299, 6481, 7162,22141,21752,15579, +15046,14751,14471, 5569, 5889, 6999, 7883,27677,27342, 6479, 7221,22300, +21814,15741,15101,16326,22788, 3938,43577,43244,43574,43241, 6819,21366, +13905, 6818,20784,13172,16331,22793, 4238, 5813, 3987, 6796,21319,13807, + 5817, 4015, 5809, 4012, 6805,20713,13116, 6807, 7147, 5834,20783,26609, + 3845,13171,26853, 3838,13666,21222, 3820, 6790, 7138, 5808,20805,26616, + 1953,13189,26857, 2018,13674,21227, 2053, 5807, 1976, 7137, 2083,26636, +26874, 6789, 2116,20848,21247,13229,13701, 5821, 1967, 7144, 2074,26629, +26867, 6800, 2107,20830,21239,13211,13692, 6794, 7140, 5815,20782,26608, + 1935,13170,26852, 2000,13665,21221, 2035, 5819, 1985, 7142, 2093,26647, +26885, 6798, 2126,20870,21256,13245,13708,16325,22787, 3937, 3940, 3946, + 3921, 3959, 3970, 3930, 3950, 3964, 3924, 3936, 3944, 3919, 3949, 3963, + 3923, 3957, 3968, 3928, 6835, 7154, 5848,20903,26670, 1589,13261,26895, + 1690,13717,21268, 1655, 3935, 3943, 3918, 6830, 7152, 5846,20898,26668, + 1934,13256,26893, 1999,13715,21266, 2034, 6834,20902,13260, 6832,20900, +13258,16315,22777, 3856, 3855, 3861, 3850, 5836, 3848, 7149, 3841,26628, +26866, 6809, 3823,20829,21238,13210,13691, 3934, 3942, 3917,14837,21713, + 6353,22100,14836,14835,14846, 6851,21738,14952, 6837,21731,14924,15502, +15501,15516,15528,15540,14882,15536, 5864, 1638,16378, 5858, 4127, 5857, + 1905, 6854,21740,14967,14881,15534,14878,14906,15544,15546, 6862,21746, +15035, 6864,21748,15037,14867,14908,14897,14891,14888,14899,14875,14880, +16098,16095,22634,22631, 3578, 3575,22629, 3573,27960, 3708,22608, 3555, +27947, 3688,22606, 3553,27945, 3686,40118,14110,30165,38687,30164,32068, + 3274, 3432, 3268, 3426,27965,27963,14626, 5349,27940,35536,14625,35538, +14627,35535,14624,35534,14623, 6664, 6663, 4303,21444,14033, 6560,21443, +14032, 5738, 3196, 2528, 2527, 5644, 7042, 8345, 8183, 2526, 3323, 3317, + 5640, 7038, 6931, 5266, 5641, 7039, 2524, 3313, 7732,39979,32435, 5362, + 7729,39976,32432, 5359,22612, 3559,27951, 3694,22611, 3558,27950, 3693, + 7630,39953,32422, 5322, 7627,39950,32419, 5319,35533,14622,35532,14621, +35531,14620,35545,14653,35530,14619,35544,14652,10716,10713,10708,10705, +35527,14616,35529,14618,10710,10707,10593,10592, 2702, 2690,18867,18855, +10470,10458, 5741, 3199, 7099, 3049,24735,24575, 5733, 3191, 3272, 3430, + 5735, 3193, 5734, 3192, 7096, 3046,24732,24572, 5731, 3189, 7094, 3044, +24730,24570, 5728, 3186, 7091, 3041,24727,24567, 5715, 3172, 7083, 3029, +24724,24564, 3270, 3428, 2700, 2688,18865,18853,10468,10456,43482,18834, + 5709, 3165, 5704, 3161, 5626, 4113, 5855, 4085, 5662, 3986, 5701, 3158, + 5693, 3150, 7077, 3022, 7069, 3014,24716,24556,24708,24548, 6620, 2699, + 6612, 2687,18985,18864,18977,18852,10613,10467,10605,10455, 5697, 3154, + 5689, 3146, 7073, 3018,22604, 7065, 3010,24712,24552, 3551,24704,24544, + 6616, 2694, 6608, 2682,27943,18981,18859,18973,18847, 3684,10609,10462, +10601,10450,22603, 3550,27942, 3683, 2696, 2684,18861,18849,10464,10452, + 5698, 3155, 5690, 3147, 7074, 3019, 7066, 3011,24713,24553,24705,24545, + 6617, 2695, 6609, 2683,18982,18860,18974,18848,10610,10463,10602,10451, +28004, 3126, 3060, 3379, 3125, 3123, 3122, 3333, 3066, 3383, 3065, 3382, +10507,18914,10506, 2875,18913, 2874, 3304, 3493, 3302, 3491, 3301, 3490, + 2746, 2745,18889,18888,10490,10489, 2744, 2743,18887,18886,10488,10487, +14522, 5654, 2538,15766,15765,14397, 8138, 8310, 5559, 5546, 5545, 5222, + 5707, 5221, 5717, 5544, 5220,16422,16417,16419,43666,43661,43664,43659, +43656,43654,43652,43650,43643,43648,43641,43646, 1721, 7573, 1718, 7570, + 7711, 2859, 7706, 2844,16494, 7690, 2800, 7688, 2798, 7687, 2797, 7709, + 2857, 7704, 2842,16484, 7694, 2813,16502, 7685, 2795, 7714, 2864, 7684, + 2794,16491,16490, 7693, 2812, 7683, 2793,16534,16541,16532,16539,16547, +16552,16489,16551,32547,16545,32549,16550,16549,32544,16488,32584,32596, +16580,22899,28140,32583,38724,43714,22901,28142,38722,43712,16582,32581, +16671,16593,16569,16412,16411,16410, 7682, 2792, 7692, 2811,16524,16526, +16408,16407,16430,16406,16509,16506, 3645,22871,16497,16427,16426,22823, + 4558,16425,22822, 4556,16592,16500,16511,16465,16469,16467, 7701, 2822, + 7699, 2820, 7697, 2818,16504,16951,23098, 8930,32714,16751,16750, 4324, +16746,32898,32761, 9180,23289,17210, 6722,23032,16851,17209,23288, 9179, +17215,23293, 9187,16839,16989, 6471,23037,16857,16836,16987,16831,16984, +43775,43804, 4361, 9178,17191,23279, 9156,17164,23262, 9137,17189,23277, + 9154,16785,17184,23272, 9152,17183,23271, 9151,17163,23261, 9136,16793, +17260,23314, 9210,17259,23313, 9209,17258,17257,17256,17540,17520,16895, +16894,17384,16916,17373,17084,23204, 9040,17546, 4126, 8890, 8929,23097, +16950,16949,23096, 8928,16948,23095, 8927,16947,43871,43830,43869,43828, +17472, 6376,22977,16757,16765,22969, 7161,17449,16799,17459,32713,16749, +16748,17470,17447, 8952, 8951,16830,23015, 7448,16850,23031, 6788,32760, +17208,23287, 9177, 8970, 8969,16809,22996, 4555,16808,22995, 6317,16849, +23030, 7159,16829,23014, 6374,16848,23029, 7446,16828,23013, 6786,17207, +23286, 9176,16782,16847,23028, 7215,16827,23012, 6419,16779,17480,32732, +32754,32731,16784,17182,16768, 9189,43807,43778, 4268,17176,17161,43867, +43826,17160,43865,43824, 9208,23312,17030,17255,23160, 9017, 9207,23311, +17029,17254,23159, 9016, 9226,23343,17066,17298,23191, 9030,17028,17253, + 9206,23310,17252, 9205,23309,17251,17027,23158, 9015,17250,23308, 9204, +17026,23157, 9014,17249,23307, 9203,17248,23306, 9202,17025,23156, 9013, +17247,16867,17519,16893,16892,16891,16890,16902,16865,23045, 6470,16873, +16870,23048, 6378, 6190,23395,17522,16864,23044, 4525,16883,23059, 4509, +16914,23065, 4508, 9201,23305,17246, 9249,23355,17360,17420,23377, 9272, +17138,23241, 9091, 5871, 7856, 7524, 7165, 8042, 6140,16927,17143,23076, +23246, 7522, 9096,28180,28208, 8040, 9120, 6138, 8876,17436,23391, 9286, +17592,28242, 9307,23430,28276, 8912, 7520, 8038, 6136,17136,23239, 9089, +17430,23386, 9281,17586,28237, 9302,23425,28271, 8907, 7518, 8036, 6134, + 9269,23374,16924,17140,17417,23073,23243, 7516, 9093,28177,28205, 8034, + 9117, 6132, 8873,17372,16889, 9229,23348,17074,17309,23197, 9034,17073, +23196, 9033,16881,23057, 4517,16670,16574,22893,28134,32575,38716,43706, +22895,28136,38718,43708,16576,32577,32601,16573,22892,28133,16625,22919, +28152,16946,23094, 8925,16945,23093, 8924,38796,32704,17444,17443,17498, +17477,17504,17488,17490,17442,17441,16795, 3581,22981,16775,16774,22980, + 4507, 3580,23011,16826, 8943,23102,17179,16957,23268, 9147,32748,32893, + 3864,22994,16807,16806,22993, 4539, 2179,23010,16825,17509,16846,23027, + 2193, 2184,23020,16838,16855,23035, 2197, 2182,23018,16834,16811,22998, + 4506,16813,23000, 4524,16982,16819,43800,43771, 9166, 1673, 9200,23304, +17024,17245,23155, 9012, 9228,23347,17072,17308,23195, 9032, 9224,23341, +17064,17296,23189, 9028,32759,32897,17214,23292, 9186,17206,23285, 9175, + 3948,23026,16845, 3644,23025,16844,43803,43774, 9174, 2192,17159,23260, + 9135, 9134,23259,17158,17440,17514,17512,16860,23040, 2191,17000,23134, + 8995,16998,23132, 8993, 3591,23024,16843,17222,16994, 9192,23296,17218, + 9194,23298,17220,17507,16992,23127, 8987,16862,23042, 1328,23154,17244, +17023,17474,17243,17022,17497,17021,17486,17439,17496,17057,17093,17020, +17019,17117,17352,17371,17370,17376,17369,17390,17379,17392,17109, 8894, + 1637,17338, 8889, 1904,17356,17103,17438,17401,17126,17403,17128,17409, +23364, 9261,17411,23366, 9263, 8923,23092,16944, 8922,23091,16943, 9133, + 2862, 9144, 2791, 9142, 2810, 838,22973,16764,16761,22976, 840, 9131, + 2790,16736,16734,17324, 8978,16842,23023, 3814, 9107,23257,17154,17107, +23220, 9070, 9076,23226,17115, 8968, 8967,23119,16978,16803,22990, 3881, +16802,22989, 2880,32747,16805,22992, 1327,16816,23003, 1334, 9165, 8962, + 8961, 1344, 9163, 8966,23118,16977, 8964,23116,16975,17217,23295, 9191, + 9011,23153,17018,17017,23152, 9010,17120,23229, 9079,17082,23202, 9038, + 9062,23213,17099,32758,32757, 8984, 8982, 8977, 8976, 2171, 2170, 2178, +23009,16824,43802,43773, 9173, 2190, 9199,23303,17016,17242,23151, 9009, + 9198,23302,17015,17241,23150, 9008, 9196,23300,17239,23148,17238,17237, +17236,17235,17234,17233,17232,17231,17518,17517,17172,17337,17336,17335, +17334,17345,17205,32896,17333,32921,28257, 9056,43837, 9245, 9395,28250, + 9329,43796, 9392,28197, 9326,43902,28224, 9053,43895, 9242,43834, 9049, +28254, 9238, 9388,43793, 9322,28247, 9385,43899, 9319,28194,43892, 9046, +28221, 9235, 9232,23352,17089,17323,23207, 9043,17321,17320,32773,32680, +32679,32772,32685,32778,32683,32775,32792,32688,38823,32771,17516,17539, +17543,17204,23284, 9172,17013,23147,17012, 9006,23146, 9005,17011,17010, +23145,23144, 9004, 9003,17009,17007,23143,23141, 9002, 9000,17081,17080, +23201,23200, 9037, 9036,38830, 9416,32848, 9415,32847,38829, 9215,23319, +17035,17265,23165, 9022, 9213,23317,17033,17263,23163, 9020,17203,23283, + 9171,16878,23054, 2188,17529,23402, 1325,17526,23399, 9170,16876,23052, + 1324,17525,23398, 2187, 1177, 1188, 1160, 1185, 1167, 1170,32806,32697, + 1220, 1215, 1246, 1202, 1250, 1232,32811,32702,17134,23237, 9087,17427, +17583,23383,23422,28234,28268, 9278, 1184, 9299, 1187, 8904, 1176,16926, +23075, 1159,17142,28179, 1166,23245,28207, 1169, 9095, 9119, 8875, 9275, +23380,17423, 9277,23382,17426,17148,16931,23251,23080,28212,28184, 9101, + 1236, 9124, 1240, 8880, 1226,32808,32699,38792,38755,43819,43784, 9280, + 1245, 9301, 1249, 8906, 1231,23385,23083,28236,28187, 9343, 1254, 9363, + 1256, 8883, 1210,38860,38757,43860,43786, 9458, 1260, 9471, 1262, 8911, + 1219,17429,16934,28215,28270, 9127, 1208, 9376, 1258,32880,32701,43821, +43907, 9306, 1214, 9480, 1264,28241,28275, 9365, 1222, 9378, 1242,43862, +43909, 9473, 1228, 9482, 1252,17153,17585,23256,23424, 9106, 1197,32810, +32931,38794,38872, 9285, 1201,23390,23429, 9345, 1204,38864,38874, 9462, + 1206,17435,17591,32884,32933,17434,17590,23389,23428,28240,28274, 9284, + 1200, 9305, 1213, 8910, 1218,17433,17589,23388,23427,28239,28273, 9283, + 1199, 9304, 1212, 8909, 1217,17147,16930,23250,23079, 9100, 1235,28211, +28183, 9123, 1239, 8879, 1225,16933,23082, 1244,17152,28186, 1248,23255, +28214, 1230, 9105, 9126, 8882,17145,23248, 9098,17151,23254,17150, 9104, +23253, 9103,38863, 9461,32883, 9460,32882,38862, 1316, 1319, 1337, 1313, + 1340, 1322,17098,23212,17097, 9061,23211, 9060,38834, 9427,32857, 9426, +32856,38833,17367,17366,17365,17364,17363,16908,32794,32925,17425,17582, + 9448, 9447, 1380,17560,23409, 1373,38849, 9446,32872, 9444,32870,38847, +38841, 9434,32864, 9432,32862,38839,38837, 9430,32860, 9429,32859,38836, +32691,32796,17106,17105,23219,23218, 9069, 9068,38844, 9439,32867, 9438, +32866,38843, 1370, 1378, 1348, 1375, 1356, 1362,16910,23062, 2186, 1385, +23412,17568, 9252,23358,17387, 1382, 1390, 1353, 1387, 1359, 1365,32693, +32800,17114,23225,17113, 9075,23224, 9074,38853, 9453,32875, 9452,32874, +38852,17332,17331,17330,17329,16888,32787,32920,17432,17588,17358,17556, +17351,17553,17389,17570,17378,17562,17382,17564,17394,17572,17362,17558, + 9442, 9441, 9424, 9423, 1368, 1108,13330,13329,13400,13397,17078,17315, +17071,17307,15133,13770,13518,14434,15468,14972,15521,14863,13769,14436, +15135,13517,13768,14811,15395,13516,13767,14420,15116,13515,15518,14860, +15465,14965,13805,14816,15398,13568,17124,17405,17070,17314,15369,14792, +14790,15359,14821,15430,15491,14910,14770,15314,12926,13328,15520,14971, +13327,12925,13326,12924,13325,12923,15542,15031,13469,13253,14815,15397, +15464,14859,17349,16906,17347,16904,14873,16397,14871,16395,14532,13324, +14527,13323,14525,13393,14530,12922,14535,12984,14537,12921,14517,12920, +16382,14987,16384,14989,16022,13322,16017,13321,16015,13395,16020,12919, +16025,12986,16027,12918,16008,12917,15775,15777,14402,14400,17398,14510, +14903,14858,14508,14424,14819,16961,17305,14412,14855,13876,13883,13881, +13873,14422,14414,14416,14809,13946,13944,13878,13889,13887,15029,14923, +13451,14963,13840,13900,13831,13803,13842,13981,13779,13829,13959,13950, +13971,13964,13962,13967,16068,22589,16077, 4261,22598, 4260,16066,22587, +16075, 4259,22596, 4257,15256,21934,15263, 7336,21941, 7343,15254,21932, +15261, 7334,21939, 7341,12916,20525, 4255,13079,20683, 4314,12915,20524, + 4254,12914,20523, 4253,13081,20685, 4316,13320,20978, 4434,13419,21040, + 4468,13319,20977, 4433,13318,20976, 4432,13317,20975, 4431,12913,20522, + 4252,12912,20521, 4251,12911,20520, 4250, 4301,22781,16319,15412,22046, + 7464,12910,20519, 6816,12909,20518, 6634,12908,20517, 6661,12907,20516, + 6459,12906,20515, 6453,12905,20514, 6802,13316,20974, 6632,13315,20973, + 6637,13314,20972, 6628,13313,20971, 6457,16321,22783, 4249,16308,22771, + 4248,16203,22698, 6715,15914,22451, 6492,15305,21977, 7384,15303,21975, + 7382,15155,21849, 7283,15414,22048, 7466,13006,20610, 6813,15403,22039, + 7458,13009,20613, 6659,16201,22696, 6713,13403,21030, 6639, 8092, 8571, + 8574, 8095,38612,37449,38626,37448,38624,37446,38541,37444,14814,17313, +17312,12904,12903,12902,12901,13083,13012, 7107, 5350,42779,42777, 4169, + 4393,14834,14922, 4390, 4349, 4424, 4270, 4347,18969,43135,27930,27928, +27921, 4601, 4600, 4598,27511,27509,27504, 7785, 6772, 6679, 6487, 6688, + 8090, 8569,30259,35323,30254,35317,30252,35315, 8089, 8568,18967,43133, +30251,35314,30244,35307,30257,35321,30249,35312,15087,21797, 4505, 4504, + 4512, 4579, 4577, 4514,29692,34639,29691,34638,29707,34654,29717,34664, +29799,34746,29801,34748,29740,34687,13958, 6932, 5267, 6930, 5265, 4503, + 4502, 4501, 4520, 4519, 4551, 8363, 2934, 2926, 2974, 2970, 8370, 2925, + 2383, 2391, 2382, 8368, 8347,30250,35313, 8343, 7722, 7720,19096,19094, +10709,10706, 4500,22238,15684, 4499,22236,15681,13514,21078, 7566,12900, +20513, 6779,12899,20512, 6826,12898,20511, 6737,17122,23231, 9081, 9066, +23216,17102,17111,23222, 9072, 9026,23185,17056,17095,23209, 9058,12897, +20510, 7806,15550,22120, 7851, 7507,22090,15478,15533,22114, 7845,15538, +22116, 7847, 7499,22084,15461,15530,22111, 7841,15711,22261, 6742,15708, +22259, 7774, 7581,22380,15829,15836,22382, 7583,15923,22457, 7564, 7593, +22241,15688,15442,22075, 7492,15489,22096, 7513,15481,22092, 7509, 7837, +22108,15515,17005,23139, 8998, 8997,23138,17004,15500,22099, 7832, 7497, +22078,15454,15453,22077, 7496, 7831,22098,15499,15473,22087, 7504, 4529, +22140,15578,13822,15577, 4527,22139,15576,13817,15575,15724,13513,15574, +22138, 4179,15573,13583,14251,15710,15572,22137, 7776,14257,15571,16242, +15707,15570,22136, 6745,16254,15569,15832,15835,15702,15922,15568,22135, + 6500,15567,15925, 4188, 4538,22455,15920, 4537,22454,15919, 7591,22453, +15918,15917,13613,15916, 7595,22134,15566,14065,15565,17228,14833, 4173, +13188,20804, 4365,13228,20847, 4386,17227,13237,20859, 4389,13169,20781, + 4360,14099,13827, 4534,13603,21164, 7590,14068,14024,13570,13209,20828, + 4382,13512,13412,13411,17003,17226,15452,14832,15498,14921,17328,16887, +14866,16391,14521,13410,16377,14977,16010,13409,14396,13941,13885,13074, +20679, 4310,13073,20678, 4309, 4308,22776,16314,15300,21972, 7379,13072, + 4531,15564,22133, 6475,15870,15563,15825,13599,13511,15764, 6771, 6670, + 6678, 6486, 7784,13448,13897, 3266, 3424,42761,42776, 6929, 5264, 2924, + 2381,18833,43481, 8713, 2380, 2379, 2962,19078,42868,18806,43450, 2923, + 1016, 1058,35100,29992, 1044,35096,29988,15561,22131,35097,29989, 1040, + 2404, 1034, 1042, 1054, 1028, 5115, 5117,16064,22585,16073, 4297,22594, + 4296,27926, 4624, 6948, 5294,15252,21930,15259, 7332,21937, 7339,27507, + 8352,14813,14857,14970,14969,42775,13447,13896,13446,13895, 7110, 5369, + 2980, 8377, 7739, 7738,19111,19110,10739,10738,18987,43158, 2942, 8712, + 4563,13639,21187, 4554,13904, 4195, 4381,15070,21780, 4533,13903,14067, +13957,16938,16937,42132,42573,17002,17225,15451,14920,15497,14831,14919, +15409,22043, 7462,13305,20963, 6811,13304,20962, 6657,16196,22691, 6711, +13303,13986,13985,13984,17224,14918,14830,42689,42380,42688,42379, 4429, +13311,20969, 4428,13977,16936,13307,20965, 4423,13509,37779, 4899,13507, +37777, 4897,13983,42687,42378,42373,42682,13988,42377,42686,42691,42382, + 9078,23228,17119, 7511,22094,15487,15440,22073, 7490,13115,20712, 6824, +42802, 8376, 8375, 7849,22118,15548,13826,13445,13894,13114,13628, 2232, +21214,13743,13651,21291, 2250, 6477,21167,15144,13606,21841, 7270,13956, +21399, 2214, 2231,21355,13867,15143,21840, 7269, 6483,21334,13833,13955, + 4574,13113,13949, 4571, 4570,13731,21281, 4553,13168,13737,21286, 2230, +13742,21290, 2249,13954,21398, 2213,13948,21396, 2229,13112,20711, 6735, +13940,15265,21943,15066,21776,15085,21795,30243,35306,15560,22130, 4548, +29765,34712,29750,34697, 2378, 5130, 5127,43480,18832, 2377,35094,29986, + 2376,35090,29982,15558,22128,35091,29983, 2403,15077,21787,30235,35299, +30246,35309, 8342,13111,20710, 6777,13110,20709, 7804, 4550,13208,20827, + 4380,18680,42605, 4379,13646,18934,42986, 8312, 1778,42238,42611,13167, +20780, 4359, 7035, 5307,42741, 1710, 1626, 8327, 8326, 2375, 8340, 8325, + 7615, 7612,19074,19071,10696,10693, 4562,13166, 8224, 8659,19004,43178, +15330,21997, 8664, 8226, 2922, 2374, 8223, 8658, 5467, 955, 6059, 953, + 5464, 959, 6056, 957, 8661, 8663, 8218, 8221,14243,14242,16236,16235, +32298,32297, 8076, 6069,42820,42819, 8383,10641,19007,10640, 6755,19006, + 6754,15343,22008, 7135, 5491,42828, 2373, 8387, 8386, 7793, 7792,19130, +19129,10758,10757,19123,42907,43185,43523,42818,42906, 8672, 8675, 1503, + 6071, 5483, 1507,32302, 7134, 5490, 8080, 6089,19017,43192,43191,43528, +43190,43527,42827,42913, 8385, 7791, 7790,19128,19127,10756,10755,10651, +19016,10648, 6766,19013, 6763, 8233, 8236,42911,42825, 5497, 1549, 6093, + 1539, 5496, 1548, 6092, 1538,15069,21779,15332,21999,15083,21793,15090, +21800,15088,21798,15674,22221,15557,22127,15556,22126,15559,22129,15555, +22125,15078,21788,15080,21790,15076,21786,15554,22124,15562,22132,15553, +22123,15552,22122,15074,21784,15073,21783,15184,21867,15949,22485,15948, +22484,15988,15219,22525,21900,27872,27463,15985,15216,27869,22522,21897, +27460,27857,27448,27849,27440,25615,27633,27608,21778,15068,27834,27425, +27765,27606,15970,15201,22507,21882,27833,27424,15969,15200,27899,27898, +22541,22540,22506,21881,27490,27489,16004,16003,27832,27423,21916,21915, +15235,15234,27897,27488 +}; + + +const int s_nDataSize3 = 24772; +unsigned int s_Data3[24772] = { +//Output priorities (total = 24772): +10, 9, 12, 6, 11, 14, 4, 1, 7, 5, 2, 3, 13, 8, 0, +0, 1, 2, 219, 218, 76, 217, 216, 77, 1, 0, 74, 78, 79, 75, +70, 71, 72, 68, 73, 69, 310, 299, 296, 331, 297, 332, 114, 26, 298, +340, 330, 336, 338, 339, 62, 112, 328, 334, 329, 335, 113, 337, 327, 333, +115, 60, 27, 61, 63, 273, 215, 36, 213, 31, 35, 39, 319, 312, 320, +323, 324, 343, 157, 345, 28, 178, 32, 59, 269, 58, 309, 305, 275, 271, +308, 304, 34, 117, 181, 129, 154, 211, 30, 38, 177, 193, 55, 130, 268, +33, 303, 174, 29, 37, 119, 212, 143, 188, 66, 270, 141, 183, 194, 179, +126, 261, 190, 267, 344, 346, 272, 189, 173, 274, 51, 322, 321, 342, 315, +214, 311, 14, 57, 223, 348, 125, 176, 192, 317, 280, 314, 165, 160, 175, +326, 127, 17, 247, 172, 262, 5, 231, 99, 138, 124, 54, 301, 325, 167, +15, 43, 205, 47, 122, 147, 292, 295, 83, 106, 86, 87, 220, 131, 347, +293, 150, 209, 233, 67, 116, 148, 156, 171, 180, 191, 256, 195, 341, 128, +277, 291, 281, 289, 294, 313, 102, 46, 103, 207, 229, 236, 240, 224, 227, +249, 170, 49, 251, 258, 318, 133, 197, 278, 2, 56, 252, 265, 254, 20, +142, 151, 316, 164, 121, 162, 41, 107, 139, 203, 288, 302, 48, 52, 110, +53, 64, 4, 16, 284, 135, 307, 3, 40, 155, 202, 23, 93, 22, 92, +290, 65, 210, 50, 98, 208, 94, 123, 137, 187, 225, 7, 10, 120, 109, +263, 287, 186, 108, 152, 264, 90, 253, 96, 97, 136, 200, 118, 140, 300, +306, 185, 6, 11, 89, 259, 85, 101, 132, 196, 24, 45, 260, 244, 91, +18, 104, 232, 19, 44, 105, 184, 21, 84, 226, 250, 283, 279, 286, 158, +248, 257, 255, 80, 169, 13, 100, 81, 168, 199, 238, 9, 12, 82, 182, +201, 266, 146, 204, 230, 239, 241, 159, 221, 285, 134, 88, 242, 246, 8, +144, 222, 149, 161, 228, 237, 276, 95, 206, 282, 163, 198, 243, 235, 245, +42, 166, 25, 111, 145, 153, 234, 13, 0, 1, 8, 3, 11, 7, 9, +2, 12, 4, 6, 5, 10, 0, 61, 62, 0, 2, 3, 5, 6, 8, +15, 17, 1, 4, 7, 9, 10, 11, 12, 13, 14, 16, 18, 19, 20, +21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, +36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, +51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 63, 64, 65, 66, 67, +68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 4, 5, +2, 0, 1, 3, 7, 10, 8, 11, 6, 9, 97, 100, 22, 123, 124, +125, 98, 101, 40, 39, 66, 72, 75, 69, 68, 74, 21, 41, 67, 73, +77, 30, 71, 23, 106, 36, 54, 55, 63, 96, 32, 99, 58, 59, 65, +38, 107, 27, 17, 56, 57, 64, 70, 76, 29, 15, 31, 122, 102, 25, +48, 49, 50, 51, 52, 53, 26, 28, 103, 37, 127, 104, 120, 121, 24, +33, 46, 47, 62, 112, 113, 116, 105, 20, 13, 126, 128, 35, 34, 19, +42, 43, 44, 45, 60, 61, 18, 79, 84, 91, 16, 14, 82, 89, 94, +85, 119, 83, 88, 95, 118, 78, 90, 81, 86, 93, 12, 80, 87, 92, +108, 109, 110, 111, 114, 115, 117, 148, 542, 149, 147, 303, 531, 555, 573, +574, 532, 556, 150, 153, 155, 578, 304, 560, 536, 151, 154, 157, 534, 558, +576, 533, 538, 557, 575, 547, 551, 570, 305, 568, 306, 540, 541, 544, 546, +550, 553, 562, 564, 565, 571, 552, 554, 572, 535, 559, 577, 537, 561, 569, +539, 543, 545, 548, 549, 563, 566, 567, 308, 307, 152, 156, 158, 382, 144, +128, 145, 438, 446, 450, 458, 462, 470, 474, 482, 492, 500, 504, 510, 516, +522, 530, 204, 394, 528, 381, 397, 480, 498, 396, 400, 436, 444, 448, 456, +460, 468, 472, 490, 502, 508, 514, 520, 286, 388, 393, 294, 403, 273, 384, +385, 387, 391, 399, 408, 313, 317, 347, 354, 414, 419, 426, 25, 88, 127, +130, 198, 275, 336, 483, 126, 281, 283, 341, 432, 299, 325, 390, 402, 415, +416, 417, 418, 427, 428, 116, 124, 134, 142, 221, 229, 298, 479, 486, 497, +527, 24, 87, 227, 311, 315, 329, 353, 506, 512, 518, 30, 34, 41, 196, +300, 484, 48, 104, 186, 191, 264, 335, 343, 363, 234, 276, 441, 443, 453, +455, 465, 467, 477, 495, 525, 222, 269, 476, 494, 524, 395, 89, 140, 413, +420, 425, 481, 499, 505, 511, 517, 529, 200, 257, 327, 398, 401, 440, 445, +452, 457, 464, 469, 32, 36, 42, 47, 345, 365, 435, 442, 447, 454, 459, +466, 471, 478, 489, 496, 501, 507, 513, 519, 526, 202, 323, 146, 170, 177, +179, 248, 255, 368, 380, 206, 122, 132, 258, 279, 288, 296, 349, 375, 434, +46, 114, 165, 180, 201, 219, 243, 250, 297, 405, 429, 33, 38, 43, 172, +203, 208, 346, 392, 404, 410, 485, 488, 183, 216, 261, 348, 386, 430, 20, +26, 65, 100, 131, 162, 167, 174, 182, 188, 189, 194, 210, 218, 240, 252, +260, 266, 272, 284, 360, 372, 383, 423, 12, 17, 57, 92, 99, 129, 213, +215, 230, 231, 278, 291, 293, 339, 389, 406, 411, 422, 15, 60, 62, 97, +117, 119, 135, 199, 224, 225, 236, 245, 267, 302, 321, 326, 377, 407, 102, +137, 175, 274, 431, 412, 421, 424, 173, 238, 246, 251, 253, 262, 270, 282, +366, 370, 378, 437, 439, 449, 451, 461, 463, 473, 475, 491, 493, 503, 509, +515, 521, 523, 0, 1, 81, 82, 106, 160, 168, 184, 185, 192, 193, 263, +271, 277, 322, 358, 22, 28, 31, 40, 67, 98, 103, 105, 138, 254, 316, +319, 355, 328, 10, 14, 49, 52, 59, 70, 73, 90, 112, 133, 220, 228, +232, 233, 301, 309, 314, 331, 333, 337, 344, 351, 35, 37, 44, 163, 342, +409, 433, 487, 259, 27, 29, 39, 50, 161, 164, 176, 178, 181, 187, 190, +197, 205, 239, 241, 242, 324, 332, 359, 361, 362, 371, 373, 374, 6, 7, +45, 55, 56, 75, 77, 79, 80, 108, 109, 159, 166, 169, 171, 195, 217, +237, 247, 249, 256, 265, 268, 280, 334, 340, 350, 357, 367, 369, 379, 2, +3, 9, 18, 19, 21, 23, 53, 63, 64, 66, 68, 72, 76, 83, 84, +93, 95, 96, 101, 107, 111, 120, 121, 123, 125, 139, 141, 143, 212, 244, +290, 295, 310, 312, 318, 320, 330, 338, 352, 356, 4, 5, 8, 11, 13, +51, 54, 58, 61, 69, 71, 74, 78, 85, 86, 94, 110, 113, 115, 136, +207, 209, 211, 214, 223, 226, 235, 285, 287, 289, 292, 364, 376, 16, 91, +118, 0, 3, 19, 149, 96, 20, 150, 5, 1, 6, 4, 2, 147, 93, +91, 145, 148, 95, 146, 85, 55, 66, 76, 59, 73, 81, 51, 61, 64, +54, 58, 92, 94, 63, 173, 62, 86, 65, 75, 60, 74, 82, 56, 52, +53, 57, 171, 167, 160, 159, 170, 72, 141, 162, 172, 28, 68, 29, 140, +168, 27, 70, 126, 139, 69, 163, 89, 137, 166, 71, 169, 129, 142, 164, +116, 135, 143, 90, 67, 113, 121, 165, 161, 131, 123, 125, 133, 30, 7, +138, 10, 117, 119, 11, 120, 127, 134, 130, 132, 115, 124, 114, 136, 9, +118, 33, 77, 157, 158, 8, 12, 88, 155, 156, 34, 78, 99, 97, 128, +36, 44, 87, 35, 43, 15, 13, 17, 122, 21, 23, 25, 144, 37, 45, +47, 103, 32, 42, 80, 39, 49, 83, 108, 40, 50, 84, 38, 46, 48, +31, 41, 79, 18, 98, 22, 24, 26, 14, 16, 105, 107, 100, 152, 111, +109, 104, 101, 110, 112, 106, 153, 102, 151, 154, 0, 1, 3, 4, 2, +12, 13, 10, 11, 14, 9, 6, 5, 7, 8, 21, 24, 23, 22, 20, +19, 15, 16, 17, 18, 0, 1, 3, 2, 108, 109, 139, 153, 122, 96, +178, 97, 126, 95, 101, 100, 94, 174, 140, 172, 175, 173, 5, 36, 154, +88, 83, 80, 35, 161, 151, 19, 11, 6, 147, 143, 34, 176, 123, 37, +169, 170, 171, 163, 164, 165, 166, 167, 168, 155, 156, 157, 158, 159, 160, +82, 162, 177, 152, 81, 111, 7, 12, 20, 128, 4, 93, 21, 64, 13, +148, 60, 62, 118, 10, 18, 33, 144, 30, 48, 135, 149, 86, 114, 99, +124, 129, 130, 131, 61, 63, 65, 8, 14, 16, 22, 24, 26, 28, 73, +75, 77, 90, 56, 72, 74, 76, 78, 51, 53, 55, 57, 59, 67, 69, +71, 79, 89, 91, 50, 52, 54, 58, 66, 68, 70, 92, 107, 39, 41, +43, 45, 47, 110, 103, 105, 120, 145, 127, 38, 40, 42, 44, 46, 137, +102, 104, 106, 31, 32, 49, 9, 15, 17, 23, 29, 87, 25, 27, 150, +141, 142, 84, 85, 146, 98, 116, 119, 133, 115, 121, 136, 132, 125, 117, +112, 113, 138, 134, 2, 0, 1, 3, 8, 6, 7, 4, 5, 9, 10, +0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, +15, 16, 2, 1, 4, 8, 7, 9, 6, 0, 5, 3, 10, 109, 107, +108, 97, 105, 28, 30, 21, 81, 26, 32, 106, 20, 24, 35, 22, 34, +96, 104, 62, 27, 111, 11, 18, 23, 36, 83, 37, 12, 95, 17, 46, +56, 25, 40, 42, 87, 89, 14, 16, 13, 29, 103, 82, 110, 15, 19, +31, 33, 102, 38, 66, 67, 68, 69, 70, 71, 72, 85, 41, 115, 117, +91, 93, 94, 99, 101, 43, 73, 86, 88, 114, 116, 50, 52, 74, 76, +77, 78, 79, 39, 113, 44, 45, 47, 48, 49, 51, 53, 54, 55, 57, +58, 59, 60, 61, 63, 64, 65, 75, 80, 84, 90, 92, 98, 100, 112, +0, 1, 20, 17, 18, 12, 19, 16, 21, 15, 23, 14, 22, 13, 0, +6, 5, 11, 3, 9, 2, 8, 1, 7, 4, 10, 3, 4, 11, 14, +17, 32, 0, 1, 2, 5, 6, 7, 8, 9, 10, 12, 13, 15, 16, +18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 33, +34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 2, 3, 0, 1, +15, 13, 17, 21, 77, 76, 33, 30, 34, 110, 105, 114, 118, 31, 32, +35, 101, 100, 16, 131, 12, 14, 130, 135, 132, 112, 120, 116, 111, 115, +119, 79, 117, 113, 121, 20, 133, 80, 134, 78, 81, 44, 109, 49, 47, +124, 46, 28, 48, 126, 26, 87, 89, 90, 88, 6, 5, 7, 9, 11, +19, 36, 38, 40, 42, 86, 91, 18, 4, 10, 39, 8, 37, 41, 43, +45, 60, 127, 27, 61, 106, 129, 59, 108, 128, 53, 54, 57, 58, 62, +50, 29, 56, 63, 52, 55, 107, 122, 123, 125, 51, 64, 65, 66, 67, +68, 69, 70, 71, 72, 73, 74, 75, 82, 83, 84, 85, 92, 93, 94, +95, 96, 97, 98, 99, 102, 103, 104, 23, 25, 24, 22, 175, 174, 176, +162, 158, 165, 124, 166, 15, 155, 126, 160, 120, 121, 122, 123, 125, 127, +128, 129, 153, 157, 171, 152, 163, 170, 96, 97, 108, 0, 3, 6, 9, +12, 66, 69, 72, 75, 78, 81, 130, 131, 150, 151, 156, 159, 161, 168, +169, 173, 177, 154, 164, 167, 172, 1, 2, 4, 5, 7, 8, 10, 11, +13, 14, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, +29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, +44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, +59, 60, 61, 62, 63, 64, 65, 67, 68, 70, 71, 73, 74, 76, 77, +79, 80, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, +95, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 109, 110, 111, 112, +113, 114, 115, 116, 117, 118, 119, 132, 133, 134, 135, 136, 137, 138, 139, +140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 178, 179, 0, 1, 2, +8, 6, 7, 3, 5, 4, 0, 1305, 1308, 540, 545, 558, 563, 564, 569, +570, 575, 576, 581, 582, 587, 588, 593, 594, 599, 600, 605, 606, 611, 612, +617, 618, 623, 624, 629, 630, 635, 636, 641, 642, 647, 648, 653, 654, 657, +662, 663, 668, 669, 672, 677, 678, 681, 684, 687, 690, 0, 1, 2, 3, +4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, +19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, +34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, +49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, +64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, +79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, +94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, +109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, +124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, +139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, +154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, +169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, +184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, +199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, +214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, +229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, +244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, +259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, +274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, +289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, +304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, +319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, +334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, +349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, +364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, +379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, +394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, +409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, +424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, +439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, +454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, +469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, +484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, +499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, +514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, +529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 541, 542, 543, 544, +546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 559, 560, 561, +562, 565, 566, 567, 568, 571, 572, 573, 574, 577, 578, 579, 580, 583, 584, +585, 586, 589, 590, 591, 592, 595, 596, 597, 598, 601, 602, 603, 604, 607, +608, 609, 610, 613, 614, 615, 616, 619, 620, 621, 622, 625, 626, 627, 628, +631, 632, 633, 634, 637, 638, 639, 640, 643, 644, 645, 646, 649, 650, 651, +652, 655, 656, 658, 659, 660, 661, 664, 665, 666, 667, 670, 671, 673, 674, +675, 676, 679, 680, 682, 683, 685, 686, 688, 689, 691, 692, 693, 694, 695, +696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, +711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, +726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, +741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, +756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, +771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, +786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, +801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, +816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, +831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, +846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, +861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, +876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, +891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, +906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, +921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, +936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, +951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, +966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, +981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, +996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, +1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025, +1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, +1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, +1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, +1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, +1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, +1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1115, +1116, 1117, 1118, 1119, 1120, 1121, 1122, 1123, 1124, 1125, 1126, 1127, 1128, 1129, 1130, +1131, 1132, 1133, 1134, 1135, 1136, 1137, 1138, 1139, 1140, 1141, 1142, 1143, 1144, 1145, +1146, 1147, 1148, 1149, 1150, 1151, 1152, 1153, 1154, 1155, 1156, 1157, 1158, 1159, 1160, +1161, 1162, 1163, 1164, 1165, 1166, 1167, 1168, 1169, 1170, 1171, 1172, 1173, 1174, 1175, +1176, 1177, 1178, 1179, 1180, 1181, 1182, 1183, 1184, 1185, 1186, 1187, 1188, 1189, 1190, +1191, 1192, 1193, 1194, 1195, 1196, 1197, 1198, 1199, 1200, 1201, 1202, 1203, 1204, 1205, +1206, 1207, 1208, 1209, 1210, 1211, 1212, 1213, 1214, 1215, 1216, 1217, 1218, 1219, 1220, +1221, 1222, 1223, 1224, 1225, 1226, 1227, 1228, 1229, 1230, 1231, 1232, 1233, 1234, 1235, +1236, 1237, 1238, 1239, 1240, 1241, 1242, 1243, 1244, 1245, 1246, 1247, 1248, 1249, 1250, +1251, 1252, 1253, 1254, 1255, 1256, 1257, 1258, 1259, 1260, 1261, 1262, 1263, 1264, 1265, +1266, 1267, 1268, 1269, 1270, 1271, 1272, 1273, 1274, 1275, 1276, 1277, 1278, 1279, 1280, +1281, 1282, 1283, 1284, 1285, 1286, 1287, 1288, 1289, 1290, 1291, 1292, 1293, 1294, 1295, +1296, 1297, 1298, 1299, 1300, 1301, 1302, 1303, 1304, 1306, 1307, 1309, 1310, 158, 161, +164, 167, 170, 173, 74, 77, 86, 89, 0, 3, 12, 15, 24, 27, 36, +39, 48, 51, 60, 63, 110, 113, 122, 125, 134, 137, 146, 149, 76, 88, +156, 160, 162, 165, 169, 171, 2, 132, 175, 26, 38, 50, 62, 72, 83, +84, 95, 108, 120, 78, 90, 96, 104, 174, 14, 144, 176, 9, 21, 33, +45, 57, 69, 79, 81, 91, 93, 98, 106, 119, 131, 143, 155, 59, 71, +157, 159, 163, 166, 168, 172, 99, 101, 103, 105, 11, 35, 47, 73, 75, +80, 82, 85, 87, 92, 94, 97, 100, 102, 107, 117, 129, 141, 8, 20, +23, 32, 44, 56, 68, 114, 126, 138, 150, 153, 179, 186, 191, 198, 180, +192, 1, 4, 5, 6, 7, 10, 13, 16, 17, 18, 19, 22, 25, 28, +29, 30, 31, 34, 37, 40, 41, 42, 43, 46, 49, 52, 53, 54, 55, +58, 61, 64, 65, 66, 67, 70, 109, 111, 112, 115, 116, 118, 121, 123, +124, 127, 128, 130, 133, 135, 136, 139, 140, 142, 145, 147, 148, 151, 152, +154, 213, 214, 215, 216, 177, 189, 184, 196, 221, 222, 223, 224, 178, 181, +182, 183, 185, 187, 188, 190, 193, 194, 195, 197, 199, 200, 201, 202, 203, +204, 205, 206, 207, 208, 209, 210, 211, 212, 217, 218, 219, 220, 2, 4, +6, 8, 10, 12, 32, 34, 36, 46, 48, 52, 54, 56, 58, 60, 62, +64, 86, 87, 88, 105, 106, 107, 108, 109, 112, 113, 115, 117, 121, 1, +3, 5, 7, 9, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, +25, 26, 27, 28, 29, 30, 31, 33, 35, 37, 38, 39, 40, 41, 42, +43, 44, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 66, 67, +68, 71, 73, 75, 76, 78, 79, 80, 82, 83, 84, 92, 93, 94, 96, +97, 98, 99, 100, 101, 102, 103, 104, 110, 111, 114, 116, 118, 119, 120, +122, 124, 126, 127, 129, 131, 141, 143, 145, 146, 148, 150, 152, 153, 156, +166, 167, 170, 173, 174, 176, 180, 182, 184, 186, 0, 22, 24, 50, 69, +70, 72, 74, 77, 81, 85, 89, 90, 91, 95, 123, 125, 128, 130, 132, +133, 134, 135, 136, 137, 138, 139, 140, 142, 144, 147, 149, 151, 154, 155, +157, 158, 159, 160, 161, 162, 163, 164, 165, 168, 169, 171, 172, 175, 177, +178, 179, 181, 183, 185, 122, 85, 89, 92, 106, 118, 136, 148, 156, 164, +27, 35, 37, 38, 52, 57, 62, 67, 71, 83, 55, 65, 68, 121, 26, +53, 63, 66, 80, 82, 105, 114, 119, 123, 137, 143, 14, 25, 30, 31, +100, 28, 117, 108, 134, 3, 12, 34, 36, 39, 40, 45, 46, 51, 59, +60, 61, 73, 74, 75, 76, 77, 78, 84, 86, 87, 88, 90, 91, 93, +94, 95, 110, 112, 115, 116, 120, 127, 128, 130, 132, 141, 142, 152, 175, +58, 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 16, +17, 18, 19, 20, 21, 22, 23, 24, 29, 32, 33, 41, 42, 43, 44, +47, 48, 49, 50, 54, 56, 64, 69, 70, 72, 79, 81, 96, 97, 98, +99, 101, 102, 103, 104, 107, 109, 111, 113, 124, 125, 126, 129, 131, 133, +135, 138, 139, 140, 144, 145, 146, 147, 149, 150, 151, 153, 154, 155, 157, +158, 159, 160, 161, 162, 163, 165, 166, 167, 168, 169, 170, 171, 172, 173, +174, 176, 177, 23, 29, 5, 24, 25, 26, 27, 28, 30, 31, 0, 1, +2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, +18, 19, 20, 21, 22, 32, 83, 84, 96, 99, 100, 16, 85, 98, 18, +26, 27, 37, 40, 49, 52, 17, 51, 55, 57, 97, 8, 3, 19, 9, +50, 56, 58, 10, 53, 72, 82, 87, 89, 91, 14, 118, 129, 12, 81, +86, 88, 90, 130, 11, 20, 21, 22, 23, 24, 25, 28, 29, 30, 31, +32, 33, 34, 35, 36, 43, 67, 68, 69, 93, 95, 41, 45, 47, 4, +5, 6, 13, 38, 117, 128, 71, 0, 1, 2, 15, 39, 92, 94, 65, +70, 122, 123, 127, 7, 42, 44, 46, 48, 54, 59, 60, 61, 62, 63, +64, 66, 105, 106, 109, 110, 111, 112, 113, 114, 121, 124, 126, 73, 74, +75, 76, 77, 78, 79, 80, 101, 102, 103, 104, 107, 108, 115, 116, 119, +120, 125, 18, 24, 5, 7, 9, 13, 0, 1, 2, 3, 4, 6, 8, +10, 11, 12, 14, 15, 16, 17, 19, 20, 21, 22, 23, 25, 26, 27, +28, 29, 30, 38, 37, 3, 2, 39, 4, 40, 5, 109, 110, 111, 99, +107, 97, 105, 140, 141, 142, 0, 98, 106, 1, 100, 108, 34, 35, 36, +10, 11, 42, 46, 50, 54, 58, 62, 66, 70, 13, 17, 21, 30, 6, +88, 90, 96, 102, 113, 115, 121, 123, 125, 135, 137, 139, 9, 14, 18, +22, 33, 44, 48, 52, 56, 60, 64, 68, 72, 116, 126, 130, 43, 47, +51, 55, 59, 63, 67, 71, 85, 91, 93, 103, 80, 82, 83, 73, 75, +76, 119, 129, 133, 12, 16, 20, 31, 7, 41, 45, 49, 53, 57, 61, +65, 69, 117, 127, 131, 87, 89, 95, 101, 112, 114, 120, 122, 124, 134, +136, 138, 86, 92, 94, 104, 118, 128, 132, 79, 81, 84, 74, 77, 78, +8, 15, 19, 23, 32, 24, 25, 28, 26, 27, 29, 0, 1, 2, 3, +4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, +19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, +34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, +49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, +64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, +79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, +94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, +109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, +124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, +139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, +154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 34, 5, 6, 7, +8, 9, 10, 11, 12, 33, 47, 2, 4, 19, 20, 21, 36, 37, 38, +40, 41, 0, 1, 3, 13, 14, 15, 16, 17, 18, 22, 23, 24, 25, +26, 27, 28, 29, 30, 31, 32, 35, 39, 42, 43, 44, 45, 46, 48, +49, 50, 144, 145, 0, 141, 142, 143, 155, 154, 139, 140, 146, 147, 150, +151, 76, 82, 83, 84, 85, 86, 87, 158, 131, 77, 132, 159, 23, 24, +25, 26, 27, 28, 32, 33, 73, 79, 91, 95, 1, 3, 5, 12, 13, +14, 15, 16, 17, 18, 19, 20, 21, 22, 29, 30, 31, 34, 102, 104, +106, 108, 112, 113, 114, 115, 116, 117, 118, 119, 138, 37, 39, 41, 43, +48, 50, 52, 54, 2, 4, 6, 7, 8, 9, 10, 11, 36, 38, 40, +42, 44, 45, 46, 47, 56, 57, 58, 59, 60, 62, 64, 66, 68, 70, +72, 74, 75, 78, 80, 81, 88, 89, 90, 92, 93, 94, 96, 97, 98, +100, 103, 105, 107, 109, 137, 160, 161, 162, 35, 49, 51, 53, 55, 61, +63, 65, 67, 69, 71, 99, 101, 110, 111, 120, 121, 122, 123, 124, 125, +126, 127, 128, 129, 130, 133, 134, 135, 136, 148, 149, 152, 153, 156, 157, +4, 11, 5, 6, 7, 8, 10, 9, 12, 17, 18, 19, 21, 2, 3, +0, 1, 13, 14, 15, 16, 20, 28, 32, 34, 38, 40, 22, 23, 24, +25, 26, 27, 29, 30, 31, 33, 35, 36, 37, 39, 41, 73, 69, 95, +11, 72, 14, 12, 13, 93, 71, 74, 94, 87, 81, 86, 79, 60, 96, +98, 88, 59, 82, 97, 44, 46, 54, 68, 92, 100, 45, 53, 67, 43, +91, 99, 80, 89, 77, 1, 3, 9, 5, 6, 7, 8, 70, 28, 83, +22, 34, 42, 49, 51, 57, 17, 23, 29, 37, 63, 19, 25, 31, 39, +48, 50, 52, 56, 58, 62, 64, 66, 84, 76, 90, 78, 75, 85, 4, +0, 2, 10, 20, 26, 32, 36, 40, 15, 61, 16, 18, 24, 30, 35, +38, 47, 55, 65, 21, 27, 33, 41, 0, 1, 2, 3, 4, 5, 6, +7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, +22, 23, 24, 25, 26, 40, 42, 3, 1, 51, 5, 49, 48, 22, 24, +4, 2, 52, 50, 76, 78, 80, 94, 96, 98, 23, 47, 26, 27, 63, +64, 65, 70, 71, 21, 77, 79, 81, 17, 20, 33, 37, 41, 43, 57, +55, 56, 58, 59, 60, 61, 62, 18, 19, 25, 32, 36, 95, 97, 99, +0, 38, 39, 54, 67, 69, 73, 75, 85, 87, 91, 93, 53, 66, 84, +86, 90, 89, 6, 7, 11, 12, 13, 82, 83, 68, 72, 74, 92, 8, +9, 10, 14, 15, 16, 28, 29, 30, 31, 34, 35, 44, 45, 46, 88, +0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, +15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, +30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, +45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, +60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, +75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, +90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, +105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, +120, 121, 122, 123, 124, 125, 0, 1, 2, 3, 4, 5, 6, 7, 8, +9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, +24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, +39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, +54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, +69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, +84, 85, 86, 87, 88, 89, 90, 91, 92, 0, 1, 2, 3, 4, 5, +6, 7, 8, 1, 3, 43, 0, 2, 4, 5, 6, 7, 8, 9, 10, +11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, +26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, +41, 42, 44, 45, 46, 89, 90, 0, 1, 2, 3, 4, 5, 6, 7, +8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, +23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, +38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, +53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, +68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, +83, 84, 85, 86, 87, 88, 91, 92, 93, 94, 95, 96, 97, 98, 99, +100, 101, 102, 103, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, +11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, +26, 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, +9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, +24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, +39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, +54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, +69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 186, 189, 174, +185, 179, 180, 177, 181, 188, 191, 60, 63, 175, 183, 176, 184, 64, 61, +62, 65, 178, 182, 187, 190, 130, 142, 154, 166, 104, 110, 116, 13, 24, +36, 78, 122, 136, 148, 160, 172, 7, 18, 30, 84, 102, 108, 114, 105, +111, 117, 120, 123, 74, 86, 98, 107, 113, 119, 125, 1, 9, 20, 32, +44, 45, 55, 75, 99, 103, 109, 115, 0, 6, 19, 31, 42, 43, 54, +72, 73, 85, 96, 97, 137, 149, 161, 173, 2, 46, 47, 56, 76, 77, +100, 101, 127, 133, 139, 145, 151, 157, 163, 169, 8, 21, 33, 87, 106, +112, 118, 121, 124, 128, 129, 140, 141, 152, 153, 3, 4, 15, 26, 38, +48, 49, 50, 51, 57, 58, 66, 67, 68, 69, 80, 90, 91, 92, 93, +126, 131, 134, 138, 143, 146, 150, 155, 158, 162, 167, 170, 10, 11, 22, +23, 34, 35, 88, 89, 135, 147, 159, 171, 5, 12, 14, 16, 17, 25, +27, 28, 29, 37, 39, 40, 41, 52, 53, 59, 70, 71, 79, 81, 82, +83, 94, 95, 132, 144, 156, 164, 165, 168, 756, 759, 761, 104, 105, 640, +647, 700, 107, 757, 760, 102, 222, 224, 310, 420, 431, 441, 460, 468, 491, +494, 501, 558, 571, 654, 666, 683, 722, 7, 10, 31, 34, 60, 65, 162, +165, 178, 434, 467, 477, 480, 550, 569, 657, 671, 682, 718, 751, 13, 37, +54, 175, 181, 262, 265, 313, 385, 465, 551, 563, 641, 645, 701, 719, 253, +316, 333, 339, 364, 365, 369, 371, 436, 439, 461, 496, 499, 505, 514, 520, +522, 577, 585, 588, 598, 601, 608, 617, 622, 685, 727, 733, 223, 240, 191, +709, 92, 115, 144, 199, 248, 252, 290, 307, 338, 372, 382, 410, 475, 508, +513, 515, 527, 547, 566, 567, 572, 580, 587, 590, 595, 604, 606, 615, 619, +688, 716, 725, 730, 735, 739, 747, 11, 16, 22, 23, 35, 40, 46, 47, +52, 53, 59, 64, 88, 89, 94, 103, 118, 121, 124, 138, 143, 149, 163, +164, 179, 184, 190, 202, 205, 208, 225, 244, 245, 250, 259, 268, 280, 281, +284, 285, 291, 322, 328, 332, 340, 373, 379, 388, 400, 401, 404, 405, 411, +421, 426, 428, 429, 437, 442, 450, 455, 457, 469, 478, 481, 488, 497, 502, +529, 530, 536, 537, 540, 553, 564, 570, 625, 626, 630, 635, 637, 643, 655, +656, 668, 670, 678, 681, 691, 692, 699, 703, 704, 724, 738, 749, 750, 753, +560, 574, 723, 85, 331, 470, 101, 217, 633, 642, 697, 742, 745, 754, 19, +43, 48, 187, 324, 97, 218, 296, 416, 130, 137, 166, 226, 241, 271, 279, +319, 327, 391, 399, 440, 482, 142, 283, 325, 403, 443, 453, 485, 758, 0, +24, 67, 298, 336, 346, 464, 489, 504, 507, 510, 516, 518, 521, 523, 524, +535, 552, 559, 573, 576, 579, 582, 583, 591, 592, 594, 599, 602, 603, 607, +611, 614, 616, 620, 623, 632, 644, 649, 651, 661, 663, 667, 669, 684, 687, +696, 708, 720, 726, 729, 732, 736, 1, 2, 3, 4, 5, 6, 8, 9, +12, 14, 15, 17, 18, 20, 21, 25, 26, 27, 28, 29, 30, 32, 33, +36, 38, 39, 41, 42, 44, 45, 49, 50, 51, 55, 56, 57, 58, 61, +62, 63, 66, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, +80, 81, 82, 83, 84, 86, 87, 90, 91, 93, 95, 96, 98, 99, 100, +106, 108, 109, 110, 111, 112, 113, 114, 116, 117, 119, 120, 122, 123, 125, +126, 127, 128, 129, 131, 132, 133, 134, 135, 136, 139, 140, 141, 145, 146, +147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 167, +168, 169, 170, 171, 172, 173, 174, 176, 177, 180, 182, 183, 185, 186, 188, +189, 192, 193, 194, 195, 196, 197, 198, 200, 201, 203, 204, 206, 207, 209, +210, 211, 212, 213, 214, 215, 216, 219, 220, 221, 227, 228, 229, 230, 231, +232, 233, 234, 235, 236, 237, 238, 239, 242, 243, 246, 247, 249, 251, 254, +255, 256, 257, 258, 260, 261, 263, 264, 266, 267, 269, 270, 272, 273, 274, +275, 276, 277, 278, 282, 286, 287, 288, 289, 292, 293, 294, 295, 297, 299, +300, 301, 302, 303, 304, 305, 306, 308, 309, 311, 312, 314, 315, 317, 318, +320, 321, 323, 326, 329, 330, 334, 335, 337, 341, 342, 343, 344, 345, 347, +348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, +363, 366, 367, 368, 370, 374, 375, 376, 377, 378, 380, 381, 383, 384, 386, +387, 389, 390, 392, 393, 394, 395, 396, 397, 398, 402, 406, 407, 408, 409, +412, 413, 414, 415, 417, 418, 419, 422, 423, 424, 425, 427, 430, 432, 433, +435, 438, 444, 445, 446, 447, 448, 449, 451, 452, 454, 456, 458, 459, 462, +463, 466, 471, 472, 473, 474, 476, 479, 483, 484, 486, 487, 490, 492, 493, +495, 498, 500, 503, 506, 509, 511, 512, 517, 519, 525, 526, 528, 531, 532, +533, 534, 538, 539, 541, 542, 543, 544, 545, 546, 548, 549, 554, 555, 556, +557, 561, 562, 565, 568, 575, 578, 581, 584, 586, 589, 593, 596, 597, 600, +605, 609, 610, 612, 613, 618, 621, 624, 627, 628, 629, 631, 634, 636, 638, +639, 646, 648, 650, 652, 653, 658, 659, 660, 662, 664, 665, 672, 673, 674, +675, 676, 677, 679, 680, 686, 689, 690, 693, 694, 695, 698, 702, 705, 706, +707, 710, 711, 712, 713, 714, 715, 717, 721, 728, 731, 734, 737, 740, 741, +743, 744, 746, 748, 752, 755, 325, 326, 324, 15, 258, 103, 123, 3, 9, +124, 331, 14, 259, 16, 2, 8, 337, 102, 335, 340, 4, 10, 17, 120, +246, 247, 276, 333, 334, 33, 72, 128, 338, 5, 11, 76, 105, 127, 0, +13, 74, 73, 122, 1, 75, 7, 300, 336, 364, 6, 12, 228, 235, 263, +265, 270, 280, 291, 106, 297, 77, 135, 162, 169, 192, 199, 201, 214, 216, +225, 260, 262, 104, 306, 316, 339, 170, 200, 223, 341, 137, 160, 190, 203, +215, 218, 141, 150, 157, 171, 180, 187, 312, 313, 317, 39, 125, 143, 148, +158, 173, 178, 188, 308, 318, 107, 322, 362, 35, 91, 97, 109, 299, 230, +272, 293, 244, 256, 289, 323, 367, 38, 121, 302, 303, 311, 327, 245, 257, +261, 290, 305, 234, 264, 279, 34, 36, 79, 85, 115, 126, 298, 307, 309, +319, 328, 57, 63, 69, 198, 229, 271, 292, 82, 88, 90, 96, 108, 118, +304, 320, 329, 37, 41, 80, 86, 116, 301, 332, 139, 159, 164, 168, 189, +194, 205, 213, 217, 220, 222, 40, 136, 145, 167, 177, 197, 202, 212, 227, +18, 27, 48, 363, 237, 267, 282, 196, 240, 241, 252, 253, 285, 286, 142, +147, 152, 155, 156, 172, 175, 182, 185, 186, 310, 321, 330, 346, 347, 348, +355, 138, 163, 165, 193, 195, 204, 210, 219, 226, 131, 132, 208, 250, 251, +278, 314, 315, 357, 365, 140, 146, 179, 206, 211, 20, 29, 50, 161, 166, +191, 221, 224, 269, 368, 26, 44, 47, 58, 64, 70, 81, 87, 117, 144, +151, 153, 174, 181, 183, 93, 99, 111, 232, 274, 295, 59, 65, 71, 133, +134, 149, 154, 176, 184, 209, 350, 361, 239, 242, 254, 284, 287, 268, 56, +62, 68, 78, 84, 94, 100, 112, 114, 92, 98, 110, 238, 283, 55, 61, +67, 349, 236, 243, 255, 266, 281, 288, 19, 24, 28, 42, 45, 49, 129, +130, 207, 248, 249, 277, 354, 359, 360, 233, 275, 296, 352, 353, 22, 31, +52, 54, 60, 66, 83, 89, 95, 101, 113, 119, 342, 345, 366, 231, 273, +294, 25, 43, 46, 23, 32, 53, 343, 351, 358, 21, 30, 51, 344, 356, +0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, +15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, +30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, +45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, +60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, +75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, +90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, +105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, +120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, +135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, +150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, +165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, +180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, +195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, +210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, +225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, +240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, +255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, +270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, +285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, +300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, +315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, +330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, +345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, +360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, +375, 376, 377, 19, 29, 33, 45, 86, 88, 90, 92, 18, 27, 31, 47, +82, 103, 37, 41, 55, 87, 11, 35, 39, 43, 49, 51, 53, 57, 61, +63, 89, 91, 105, 109, 114, 20, 9, 13, 107, 28, 30, 32, 34, 46, +48, 78, 84, 133, 135, 93, 112, 134, 136, 36, 40, 62, 66, 80, 38, +42, 44, 50, 52, 54, 56, 58, 64, 127, 131, 137, 1, 15, 16, 21, +22, 102, 111, 113, 128, 132, 138, 23, 25, 59, 68, 70, 94, 69, 3, +4, 6, 7, 10, 104, 106, 108, 110, 121, 123, 67, 24, 26, 60, 122, +124, 72, 76, 81, 96, 85, 74, 98, 129, 83, 125, 0, 2, 5, 8, +12, 14, 17, 71, 73, 75, 77, 79, 95, 97, 99, 100, 126, 65, 130, +101, 118, 116, 120, 115, 117, 119, 219, 220, 221, 127, 131, 135, 139, 228, +229, 230, 231, 232, 233, 234, 235, 222, 223, 224, 225, 226, 227, 236, 237, +238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, +253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 275, +276, 277, 278, 9, 13, 17, 21, 25, 29, 36, 40, 44, 48, 49, 56, +60, 64, 68, 72, 76, 80, 84, 171, 176, 180, 184, 0, 1, 2, 3, +4, 5, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18, 19, 20, 22, +23, 24, 26, 27, 28, 30, 31, 32, 33, 34, 35, 37, 38, 39, 41, +42, 43, 45, 46, 47, 50, 51, 52, 53, 54, 55, 57, 58, 59, 61, +62, 63, 65, 66, 67, 69, 70, 71, 73, 74, 75, 77, 78, 79, 81, +82, 83, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, +98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, +113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 128, +129, 130, 132, 133, 134, 136, 137, 138, 140, 141, 142, 143, 144, 145, 146, +147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, +162, 163, 164, 165, 166, 167, 168, 169, 170, 172, 173, 174, 175, 177, 178, +179, 181, 182, 183, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, +196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, +211, 212, 213, 214, 215, 216, 217, 218, 267, 268, 269, 270, 271, 272, 273, +274, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 1, 2, +3, 4, 0, 8, 12, 11, 102, 127, 6, 5, 101, 7, 128, 4, 3, +10, 1, 2, 0, 9, 92, 90, 97, 114, 94, 95, 99, 112, 111, 123, +116, 118, 120, 121, 125, 98, 89, 91, 96, 93, 100, 113, 24, 34, 16, +36, 14, 22, 13, 21, 33, 18, 26, 30, 115, 117, 124, 119, 122, 126, +17, 25, 29, 104, 19, 27, 31, 106, 28, 20, 32, 15, 23, 35, 54, +56, 110, 51, 72, 88, 38, 40, 49, 58, 60, 64, 66, 74, 81, 105, +107, 37, 39, 50, 52, 57, 59, 63, 65, 71, 73, 82, 87, 42, 43, +45, 48, 61, 67, 70, 76, 77, 79, 83, 86, 53, 55, 41, 44, 46, +47, 62, 68, 69, 75, 78, 80, 84, 85, 109, 103, 108, 7, 8, 6, +9, 17, 10, 28, 26, 24, 50, 68, 4, 34, 5, 0, 23, 46, 2, +18, 33, 1, 85, 19, 58, 62, 70, 29, 31, 35, 3, 25, 27, 47, +48, 49, 51, 11, 90, 30, 32, 36, 40, 41, 42, 43, 44, 45, 12, +13, 14, 15, 16, 20, 21, 22, 37, 38, 39, 52, 53, 54, 55, 56, +57, 59, 60, 61, 63, 64, 65, 66, 67, 69, 71, 72, 73, 74, 75, +76, 77, 78, 79, 80, 81, 82, 83, 84, 86, 87, 88, 89, 88, 125, +101, 103, 124, 127, 134, 135, 25, 29, 37, 45, 165, 177, 163, 180, 138, +142, 113, 24, 28, 36, 40, 41, 44, 48, 49, 58, 59, 60, 61, 66, +67, 68, 69, 70, 71, 72, 73, 89, 92, 93, 145, 148, 149, 94, 105, +109, 151, 154, 161, 173, 96, 99, 100, 102, 104, 107, 108, 111, 114, 115, +116, 118, 119, 120, 122, 123, 126, 132, 133, 136, 137, 140, 141, 155, 156, +159, 160, 164, 169, 171, 172, 175, 176, 179, 181, 182, 183, 0, 1, 2, +3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, +18, 19, 20, 21, 22, 23, 26, 27, 30, 31, 32, 33, 34, 35, 38, +39, 42, 43, 46, 47, 50, 51, 52, 53, 54, 55, 56, 57, 62, 63, +64, 65, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, +87, 90, 91, 95, 97, 98, 106, 110, 112, 117, 121, 128, 129, 130, 131, +139, 143, 144, 146, 147, 150, 152, 153, 157, 158, 162, 166, 167, 168, 170, +174, 178, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, +13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, +28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 1, 2, 3, +4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, +19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, +34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, +49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, +64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, +79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, +94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, +109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, +124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, +139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, +154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, +169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, +184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, +199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, +214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, +229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, +244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, +259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, +274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, +289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, +304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, +319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, +334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, +349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, +364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, +379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, +394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, +409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, +424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, +439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, +454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, +469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, +484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, +499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, +514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, +529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, +544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, +559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, +574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, +589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, +604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, +619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, +634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, +649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, +664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, +679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, +694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, +709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, +724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, +739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, +754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, +769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, +784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, +799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, +814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, +829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, +844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, +859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, +874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, +889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, +904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, +919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, +934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, +949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, +964, 965, 966, 967, 968, 969, 970, 971, 306, 146, 158, 174, 216, 233, 27, +29, 121, 123, 125, 127, 144, 150, 152, 173, 188, 218, 295, 299, 307, 0, +1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, +16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 30, 31, 32, +33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, +48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, +63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, +78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, +93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, +108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 122, 124, +126, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, +142, 143, 145, 147, 148, 149, 151, 153, 154, 155, 156, 157, 159, 160, 161, +162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 175, 176, 177, 178, +179, 180, 181, 182, 183, 184, 185, 186, 187, 189, 190, 191, 192, 193, 194, +195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, +210, 211, 212, 213, 214, 215, 217, 219, 220, 221, 222, 223, 224, 225, 226, +227, 228, 229, 230, 231, 232, 234, 235, 236, 237, 238, 239, 240, 241, 242, +243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, +258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, +273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, +288, 289, 290, 291, 292, 293, 294, 296, 297, 298, 300, 301, 302, 303, 304, +305, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, +322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, +337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, +352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, +367, 368, 369, 370, 371, 372, 373, 374, 76, 106, 77, 107, 125, 115, 126, +60, 61, 71, 59, 96, 99, 110, 89, 70, 58, 25, 27, 33, 35, 86, +87, 72, 75, 79, 94, 112, 95, 100, 73, 74, 78, 101, 102, 105, 109, +88, 26, 32, 34, 103, 104, 108, 24, 97, 98, 113, 114, 44, 64, 69, +36, 38, 41, 43, 47, 54, 57, 63, 66, 111, 37, 39, 40, 42, 45, +46, 55, 56, 62, 65, 67, 68, 90, 92, 5, 11, 9, 84, 8, 4, +10, 2, 48, 50, 52, 81, 82, 0, 6, 12, 14, 85, 80, 83, 49, +51, 53, 91, 93, 118, 121, 124, 3, 17, 18, 21, 22, 29, 30, 1, +7, 13, 15, 116, 119, 122, 129, 16, 19, 20, 23, 28, 31, 117, 120, +123, 127, 128, 130, 3, 4, 5, 6, 25, 26, 27, 28, 29, 33, 41, +54, 61, 62, 99, 101, 105, 106, 107, 108, 133, 134, 0, 7, 30, 83, +86, 87, 88, 90, 91, 92, 93, 135, 136, 137, 1, 2, 31, 32, 34, +100, 102, 103, 104, 119, 132, 138, 139, 140, 16, 21, 22, 23, 24, 44, +51, 64, 65, 66, 67, 69, 71, 72, 73, 74, 75, 76, 8, 9, 10, +11, 12, 13, 14, 15, 35, 36, 42, 43, 46, 48, 49, 50, 52, 60, +63, 77, 78, 79, 80, 81, 82, 85, 89, 94, 95, 96, 97, 98, 109, +110, 111, 112, 113, 114, 115, 120, 121, 17, 18, 19, 20, 37, 38, 39, +40, 45, 47, 53, 55, 56, 57, 58, 59, 68, 70, 84, 116, 117, 118, +122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 141, 142, 143, 144, 145, +146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, +161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, +176, 177, 178, 179, 180, 181, 182, 49, 18, 16, 17, 42, 45, 15, 8, +46, 39, 6, 48, 7, 9, 10, 41, 44, 5, 40, 43, 30, 47, 51, +52, 25, 19, 20, 21, 22, 23, 24, 3, 4, 29, 31, 32, 33, 34, +35, 36, 37, 38, 71, 73, 11, 12, 13, 14, 50, 70, 0, 1, 2, +26, 27, 28, 60, 62, 69, 61, 63, 64, 65, 72, 75, 76, 54, 55, +56, 57, 58, 59, 66, 68, 81, 82, 83, 84, 86, 74, 53, 67, 77, +78, 79, 80, 85, 87, 88, 0, 1, 2, 3, 4, 5, 6, 7, 8, +9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, +24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, +39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, +54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, +69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, +84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, +99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, +114, 115, 116, 117, 118, 119, 120, 121, 122, 0, 1, 2, 3, 4, 5, +6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, +21, 100, 31, 35, 41, 33, 30, 34, 40, 82, 81, 103, 47, 32, 15, +99, 46, 9, 21, 8, 69, 65, 67, 14, 62, 57, 58, 60, 80, 56, +20, 11, 97, 10, 85, 86, 1, 22, 26, 36, 16, 17, 78, 79, 105, +108, 5, 24, 28, 38, 44, 45, 59, 61, 63, 70, 109, 111, 0, 4, +25, 29, 39, 50, 51, 54, 55, 71, 72, 73, 74, 75, 76, 77, 95, +96, 101, 102, 104, 110, 98, 18, 19, 83, 84, 89, 23, 27, 37, 42, +43, 48, 113, 2, 3, 6, 7, 12, 13, 49, 52, 53, 87, 88, 90, +91, 92, 93, 94, 106, 107, 112, 64, 66, 68, 5, 4, 0, 39, 48, +17, 8, 6, 7, 77, 78, 75, 76, 79, 80, 16, 15, 73, 56, 55, +74, 40, 41, 42, 60, 9, 10, 11, 12, 13, 14, 59, 43, 62, 1, +2, 3, 44, 61, 58, 68, 22, 23, 28, 35, 36, 45, 46, 47, 57, +67, 69, 70, 71, 19, 18, 20, 21, 24, 25, 26, 27, 29, 30, 31, +32, 33, 34, 37, 38, 49, 50, 51, 52, 53, 54, 63, 64, 65, 66, +72, 81, 82, 83, 84, 34, 44, 71, 32, 43, 30, 35, 72, 33, 11, +10, 39, 37, 13, 38, 42, 36, 12, 41, 31, 8, 9, 40, 50, 69, +55, 20, 24, 28, 135, 1, 5, 15, 136, 70, 46, 51, 114, 115, 45, +3, 7, 17, 113, 116, 54, 128, 129, 134, 138, 0, 4, 14, 26, 19, +23, 27, 48, 62, 63, 68, 123, 124, 125, 126, 131, 132, 29, 49, 59, +60, 66, 127, 130, 133, 86, 110, 119, 57, 58, 65, 83, 87, 103, 111, +122, 18, 22, 82, 102, 61, 64, 67, 21, 25, 52, 2, 6, 16, 75, +76, 85, 90, 95, 99, 100, 109, 120, 56, 73, 74, 77, 78, 79, 80, +81, 84, 88, 89, 91, 92, 93, 94, 96, 97, 98, 101, 104, 105, 106, +107, 108, 112, 117, 118, 121, 137, 47, 53, 4, 5, 0, 1, 2, 3, +7, 8, 6, 9, 10, 20, 25, 27, 18, 19, 21, 23, 24, 26, 28, +0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, +15, 16, 17, 22, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, +40, 41, 76, 96, 112, 109, 110, 94, 95, 49, 73, 56, 61, 65, 68, +102, 30, 37, 38, 40, 41, 44, 45, 47, 48, 55, 57, 59, 60, 79, +82, 88, 89, 90, 91, 92, 98, 100, 105, 108, 111, 0, 1, 2, 3, +4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, +19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 31, 32, 33, 34, +35, 36, 39, 42, 43, 46, 50, 51, 52, 53, 54, 58, 62, 63, 64, +66, 67, 69, 70, 71, 72, 74, 75, 77, 78, 80, 81, 83, 84, 85, +86, 87, 93, 97, 99, 101, 103, 104, 106, 107, 113, 91, 42, 46, 77, +26, 8, 78, 41, 43, 44, 47, 68, 5, 30, 45, 89, 90, 48, 49, +50, 51, 25, 76, 93, 14, 70, 88, 4, 23, 24, 29, 59, 63, 75, +79, 92, 97, 0, 1, 2, 6, 7, 15, 16, 17, 20, 21, 27, 31, +32, 33, 34, 35, 36, 62, 64, 67, 72, 61, 66, 71, 86, 94, 12, +74, 55, 58, 60, 81, 83, 3, 9, 10, 11, 13, 18, 19, 22, 28, +37, 38, 39, 40, 53, 56, 65, 69, 73, 82, 84, 85, 52, 54, 57, +80, 87, 95, 96, 1, 8, 10, 13, 12, 6, 2, 0, 11, 9, 5, +25, 7, 19, 43, 44, 45, 32, 33, 34, 50, 51, 52, 26, 22, 23, +3, 4, 28, 15, 36, 37, 49, 30, 31, 29, 14, 20, 21, 24, 27, +17, 39, 46, 16, 35, 53, 38, 40, 41, 42, 18, 47, 48, 0, 1, +2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, +17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, +32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, +47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, +62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, +77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, +92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, +107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, +122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, +0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, +15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 49, 310, 312, +314, 269, 271, 273, 275, 277, 279, 281, 297, 299, 0, 1, 2, 3, 4, +5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, +20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, +35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, +51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, +66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, +81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, +96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, +111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, +126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, +141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, +156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, +171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, +186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, +201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, +216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, +231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, +246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, +261, 262, 263, 264, 265, 266, 267, 268, 270, 272, 274, 276, 278, 280, 282, +283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 298, +300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 311, 313, 315, 316, 317, +318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, +333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, +348, 349, 350, 351, 352, 353, 354, 355, 179, 200, 161, 175, 243, 100, 104, +196, 198, 223, 227, 176, 203, 212, 215, 162, 163, 247, 160, 174, 216, 218, +158, 172, 96, 157, 166, 170, 197, 199, 217, 219, 159, 173, 178, 201, 99, +101, 103, 105, 177, 202, 213, 214, 98, 102, 92, 108, 112, 116, 120, 124, +128, 132, 184, 192, 208, 180, 188, 204, 239, 249, 221, 225, 154, 165, 169, +51, 53, 55, 57, 59, 63, 67, 71, 75, 79, 83, 87, 95, 119, 123, +127, 131, 138, 142, 146, 150, 183, 191, 207, 236, 241, 245, 250, 61, 65, +69, 73, 77, 81, 85, 89, 93, 109, 113, 117, 140, 144, 148, 152, 8, +9, 14, 15, 20, 21, 50, 52, 54, 56, 91, 107, 111, 115, 121, 125, +129, 133, 134, 135, 136, 137, 141, 145, 149, 153, 155, 164, 168, 181, 187, +189, 195, 205, 211, 58, 62, 66, 70, 74, 78, 82, 86, 118, 122, 126, +130, 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 16, 17, +18, 19, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, +35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, +60, 64, 68, 72, 76, 80, 84, 88, 139, 143, 147, 151, 186, 194, 210, +222, 226, 238, 242, 246, 248, 94, 97, 156, 167, 171, 228, 230, 90, 106, +110, 114, 220, 224, 237, 240, 244, 251, 182, 185, 190, 193, 206, 209, 229, +231, 232, 233, 234, 235, 24, 51, 54, 111, 25, 52, 55, 112, 13, 31, +34, 37, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, +14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 26, 27, 28, 29, 30, +32, 33, 35, 36, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, +49, 50, 53, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, +68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, +83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, +98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 113, 114, +115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, +130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 0, +1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, +16, 17, 8, 16, 18, 7, 26, 30, 34, 104, 14, 15, 17, 19, 22, +48, 68, 73, 75, 79, 11, 1, 6, 20, 28, 32, 36, 38, 40, 44, +98, 102, 103, 105, 106, 115, 2, 13, 21, 23, 49, 53, 54, 55, 56, +57, 58, 60, 61, 64, 66, 67, 69, 76, 83, 85, 95, 100, 10, 0, +3, 4, 5, 9, 12, 24, 25, 27, 29, 31, 33, 35, 37, 39, 41, +42, 43, 45, 46, 47, 50, 51, 52, 59, 62, 63, 65, 70, 71, 72, +74, 77, 78, 80, 81, 82, 84, 86, 87, 88, 89, 90, 91, 92, 93, +94, 96, 97, 99, 101, 107, 108, 109, 110, 111, 112, 113, 114, 116, 117, +118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 121, 120, 103, 101, 102, +86, 111, 124, 27, 44, 100, 10, 13, 82, 92, 98, 78, 88, 94, 42, +40, 9, 11, 12, 14, 80, 84, 90, 96, 45, 119, 122, 46, 48, 50, +52, 104, 106, 109, 110, 1, 4, 18, 25, 29, 32, 19, 34, 22, 5, +8, 38, 107, 39, 16, 17, 21, 26, 30, 33, 43, 87, 125, 41, 83, +93, 99, 2, 24, 28, 31, 35, 53, 55, 81, 91, 97, 105, 0, 3, +7, 6, 15, 20, 23, 36, 37, 47, 49, 51, 54, 56, 57, 58, 59, +60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, +75, 76, 77, 79, 85, 89, 95, 108, 112, 113, 114, 115, 116, 117, 118, +123, 161, 57, 60, 95, 169, 165, 171, 0, 160, 40, 168, 164, 39, 58, +59, 96, 1, 2, 50, 55, 64, 75, 85, 92, 97, 71, 77, 81, 87, +6, 16, 30, 44, 181, 170, 49, 56, 63, 76, 86, 91, 98, 174, 180, +5, 15, 29, 43, 72, 78, 82, 88, 113, 126, 194, 195, 198, 158, 159, +162, 163, 114, 125, 144, 145, 166, 167, 192, 193, 156, 47, 48, 51, 52, +53, 54, 61, 62, 65, 66, 83, 84, 89, 90, 93, 94, 99, 100, 101, +102, 103, 104, 130, 131, 132, 133, 134, 135, 152, 175, 176, 177, 67, 69, +73, 79, 105, 3, 4, 7, 8, 13, 14, 17, 18, 19, 20, 25, 26, +27, 28, 31, 32, 33, 34, 37, 38, 41, 42, 45, 46, 68, 70, 74, +80, 106, 117, 118, 142, 143, 178, 179, 138, 9, 140, 147, 148, 107, 109, +111, 139, 141, 146, 149, 182, 183, 184, 185, 190, 191, 115, 119, 121, 123, +137, 155, 157, 187, 189, 197, 10, 11, 12, 21, 22, 23, 24, 35, 36, +108, 110, 112, 116, 120, 122, 124, 127, 128, 129, 136, 150, 151, 153, 154, +173, 186, 188, 196, 172, 37, 36, 33, 32, 35, 34, 4, 2, 6, 0, +44, 45, 47, 46, 42, 43, 39, 41, 40, 38, 5, 7, 3, 1, 51, +17, 11, 15, 19, 9, 13, 10, 14, 18, 16, 23, 24, 27, 28, 31, +8, 12, 20, 22, 21, 25, 26, 29, 30, 49, 48, 50, 179, 176, 165, +178, 171, 175, 174, 177, 162, 168, 190, 188, 202, 200, 33, 75, 93, 51, +81, 87, 173, 164, 157, 114, 3, 6, 156, 15, 21, 27, 170, 167, 123, +63, 69, 39, 45, 57, 199, 211, 172, 208, 205, 214, 181, 184, 203, 196, +169, 198, 201, 160, 161, 137, 163, 125, 209, 119, 183, 215, 166, 207, 204, +213, 197, 191, 206, 210, 182, 212, 187, 185, 53, 83, 89, 192, 186, 193, +136, 180, 194, 130, 142, 154, 155, 148, 149, 131, 143, 116, 32, 74, 92, +122, 101, 107, 110, 113, 98, 104, 71, 65, 41, 47, 59, 2, 11, 14, +20, 26, 195, 189, 147, 150, 151, 145, 133, 127, 139, 126, 138, 132, 144, +117, 159, 52, 82, 88, 158, 120, 96, 102, 108, 99, 105, 111, 30, 72, +90, 8, 5, 9, 0, 124, 97, 103, 109, 12, 18, 24, 48, 78, 84, +66, 60, 70, 62, 35, 77, 95, 68, 42, 50, 80, 86, 17, 23, 29, +36, 54, 38, 44, 56, 152, 153, 64, 40, 58, 46, 128, 140, 129, 134, +135, 141, 146, 106, 100, 112, 121, 7, 4, 115, 118, 34, 61, 76, 94, +16, 22, 28, 37, 43, 55, 49, 79, 85, 67, 1, 13, 19, 25, 31, +73, 91, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, +12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, +27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, +42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, +57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, +72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, +87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, +102, 103, 104, 105, 106, 107, 108, 109, 110, 2, 5, 8, 11, 14, 17, +20, 23, 26, 29, 32, 35, 38, 41, 44, 47, 50, 53, 56, 59, 62, +65, 68, 0, 1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 18, +19, 21, 22, 24, 25, 27, 28, 30, 31, 33, 34, 36, 37, 39, 40, +42, 43, 45, 46, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, +64, 66, 67, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, +53, 52, 72, 71, 73, 70, 97, 98, 96, 99, 68, 80, 82, 74, 77, +78, 5, 22, 25, 41, 49, 1, 9, 17, 95, 103, 102, 104, 75, 76, +79, 66, 69, 93, 67, 94, 81, 83, 84, 85, 86, 87, 88, 89, 90, +91, 106, 107, 92, 100, 101, 105, 32, 34, 38, 12, 15, 54, 58, 62, +39, 14, 36, 57, 61, 65, 31, 35, 47, 29, 45, 42, 28, 44, 30, +46, 18, 16, 55, 59, 63, 40, 21, 26, 50, 3, 7, 11, 19, 0, +4, 8, 33, 37, 20, 23, 24, 27, 43, 48, 51, 2, 6, 10, 13, +56, 60, 64, 0, 2, 1, 11, 5, 6, 12, 3, 4, 9, 10, 14, +7, 8, 13, 21, 36, 18, 23, 24, 25, 26, 27, 28, 29, 30, 31, +32, 33, 34, 35, 37, 15, 16, 22, 17, 19, 20, 0, 1, 0, 1, +2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 48, 5, 34, 63, +4, 62, 50, 47, 49, 33, 35, 36, 29, 3, 6, 61, 64, 54, 58, +45, 32, 51, 56, 60, 70, 57, 40, 46, 11, 55, 59, 44, 53, 65, +72, 14, 15, 17, 20, 22, 27, 66, 68, 69, 71, 26, 19, 67, 1, +9, 12, 7, 25, 24, 28, 30, 31, 10, 8, 16, 18, 21, 23, 52, +0, 2, 41, 42, 43, 13, 37, 38, 39, 107, 15, 97, 100, 101, 105, +99, 33, 81, 79, 80, 34, 92, 95, 86, 48, 118, 41, 43, 119, 35, +36, 37, 75, 102, 103, 104, 23, 24, 25, 26, 31, 4, 17, 19, 21, +22, 27, 38, 39, 52, 56, 58, 64, 70, 72, 82, 47, 87, 51, 55, +57, 77, 78, 98, 116, 117, 28, 29, 30, 32, 40, 42, 5, 7, 9, +93, 96, 1, 2, 3, 6, 8, 10, 12, 54, 59, 65, 66, 68, 69, +71, 73, 74, 76, 83, 84, 89, 90, 106, 109, 111, 112, 114, 44, 45, +46, 49, 50, 11, 53, 16, 18, 20, 0, 13, 14, 60, 61, 62, 63, +67, 85, 88, 91, 94, 108, 110, 113, 115, 13, 4, 46, 47, 2, 6, +37, 38, 43, 51, 54, 55, 58, 67, 69, 0, 1, 3, 5, 7, 8, +9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, +25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 39, 40, 41, +42, 44, 45, 48, 49, 50, 52, 53, 56, 57, 59, 60, 61, 62, 63, +64, 65, 66, 68, 70, 71, 72, 73, 74, 75, 7, 4, 6, 5, 2, +3, 9, 8, 0, 1, 23, 22, 24, 25, 14, 26, 30, 34, 10, 11, +12, 13, 15, 16, 17, 18, 19, 20, 21, 27, 28, 29, 31, 32, 33, +35, 36, 37, 0, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, +25, 26, 27, 28, 29, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, +25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, +40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, +55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, +70, 71, 72, 73, 0, 1, 2, 3, 4, 4, 0, 1, 2, 3, 5, +6, 7, 8, 0, 1, 2, 3, 4, 5, 323, 139, 149, 165, 207, 234, +320, 296, 300, 20, 44, 45, 52, 138, 147, 189, 191, 192, 193, 210, 213, +215, 217, 226, 232, 236, 238, 293, 322, 325, 74, 122, 306, 150, 264, 4, +5, 197, 201, 117, 172, 188, 195, 203, 222, 244, 245, 265, 271, 291, 292, +332, 2, 18, 19, 22, 34, 46, 57, 82, 84, 85, 88, 89, 120, 130, +133, 134, 136, 137, 140, 142, 143, 145, 146, 148, 153, 159, 160, 161, 162, +166, 169, 173, 184, 185, 209, 211, 219, 228, 229, 231, 237, 240, 242, 243, +246, 247, 248, 251, 253, 254, 257, 286, 294, 298, 324, 335, 33, 98, 118, +200, 204, 303, 305, 327, 100, 196, 205, 275, 123, 126, 282, 0, 1, 3, +6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 21, 23, 24, +25, 26, 27, 28, 29, 30, 31, 32, 35, 36, 37, 38, 39, 40, 41, +42, 43, 47, 48, 49, 50, 51, 53, 54, 55, 56, 58, 59, 60, 61, +62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 75, 76, 77, +78, 79, 80, 81, 83, 86, 87, 90, 91, 92, 93, 94, 95, 96, 97, +99, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, +115, 116, 119, 121, 124, 125, 127, 128, 129, 131, 132, 135, 141, 144, 151, +152, 154, 155, 156, 157, 158, 163, 164, 167, 168, 170, 171, 174, 175, 176, +177, 178, 179, 180, 181, 182, 183, 186, 187, 190, 194, 198, 199, 202, 206, +208, 212, 214, 216, 218, 220, 221, 223, 224, 225, 227, 230, 233, 235, 239, +241, 249, 250, 252, 255, 256, 258, 259, 260, 261, 262, 263, 266, 267, 268, +269, 270, 272, 273, 274, 276, 277, 278, 279, 280, 281, 283, 284, 285, 287, +288, 289, 290, 295, 297, 299, 301, 302, 304, 307, 308, 309, 310, 311, 312, +313, 314, 315, 316, 317, 318, 319, 321, 326, 328, 329, 330, 331, 333, 334, +336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, +351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 0, 1, +2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, +17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, +32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, +47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, +62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, +77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, +92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, +107, 108, 109, 4, 5, 18, 20, 0, 1, 2, 3, 6, 7, 8, 9, +10, 11, 12, 13, 14, 15, 16, 17, 19, 21, 22, 23, 24, 25, 26, +0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, +25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, +40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, +55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, +70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, +85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, +100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, +115, 116, 117, 118, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, +11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, +26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, +41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, +56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, +71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, +86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, +101, 102, 103, 104, 105, 106, 107, 38, 40, 39, 65, 63, 66, 26, 28, +11, 20, 24, 32, 108, 8, 47, 45, 48, 49, 105, 109, 139, 75, 124, +18, 22, 30, 34, 111, 102, 114, 118, 77, 123, 133, 1, 5, 17, 46, +69, 73, 81, 29, 78, 101, 37, 50, 64, 9, 51, 55, 61, 76, 68, +72, 84, 2, 6, 14, 19, 23, 35, 67, 71, 79, 83, 107, 113, 115, +117, 137, 147, 36, 10, 27, 53, 57, 59, 135, 13, 103, 104, 126, 3, +7, 15, 41, 44, 54, 58, 60, 70, 74, 80, 82, 0, 4, 12, 16, +21, 25, 31, 33, 42, 43, 52, 56, 62, 85, 88, 91, 94, 106, 110, +112, 116, 119, 120, 121, 122, 125, 127, 128, 129, 130, 131, 132, 134, 136, +138, 140, 141, 142, 143, 144, 145, 146, 148, 89, 86, 87, 90, 92, 93, +95, 96, 97, 98, 99, 100, 0, 1, 2, 3, 4, 5, 6, 7, 8, +9, 10, 11, 12, 1, 2, 4, 32, 36, 38, 3, 34, 28, 31, 35, +37, 6, 12, 14, 19, 20, 22, 33, 39, 43, 53, 54, 55, 56, 57, +61, 67, 69, 70, 71, 30, 0, 5, 7, 8, 9, 10, 11, 13, 15, +16, 17, 18, 21, 23, 24, 25, 26, 27, 29, 40, 41, 42, 44, 45, +46, 47, 48, 49, 50, 51, 52, 58, 59, 60, 62, 63, 64, 65, 66, +68, 72, 16, 28, 34, 38, 42, 2, 14, 15, 18, 19, 26, 29, 30, +31, 32, 33, 40, 43, 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, +11, 12, 13, 17, 20, 21, 22, 23, 24, 25, 27, 35, 36, 37, 39, +41, 2, 0, 7, 1, 86, 5, 74, 21, 89, 85, 88, 3, 4, 6, +51, 23, 111, 70, 56, 90, 98, 27, 78, 53, 68, 80, 87, 116, 12, +25, 109, 114, 103, 108, 110, 9, 57, 62, 65, 91, 96, 99, 69, 71, +72, 73, 81, 49, 50, 52, 93, 101, 36, 55, 64, 125, 19, 20, 22, +106, 41, 8, 11, 13, 29, 34, 37, 54, 102, 107, 118, 123, 126, 14, +15, 16, 18, 82, 83, 84, 92, 97, 100, 104, 112, 17, 32, 121, 94, +24, 113, 76, 77, 79, 10, 61, 95, 26, 28, 30, 31, 35, 38, 39, +40, 46, 58, 59, 60, 63, 66, 67, 75, 105, 115, 117, 119, 120, 124, +127, 128, 33, 42, 43, 44, 45, 47, 48, 122, 0, 1, 2, 3, 4, +5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, +20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 2, 0, 1, 3, 4, +5, 6, 7, 8, 82, 11, 26, 19, 75, 6, 9, 47, 54, 66, 73, +80, 87, 0, 3, 4, 5, 18, 23, 24, 25, 44, 53, 62, 63, 65, +67, 68, 69, 70, 72, 76, 77, 86, 88, 89, 90, 91, 92, 93, 22, +10, 15, 21, 49, 58, 50, 59, 83, 1, 2, 7, 8, 12, 13, 14, +16, 17, 20, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, +39, 40, 41, 42, 43, 45, 46, 48, 51, 52, 55, 56, 57, 60, 61, +64, 71, 74, 78, 79, 81, 84, 85, 0, 1, 0, 1, 34, 2, 12, +13, 32, 37, 76, 3, 10, 11, 19, 20, 21, 22, 23, 27, 28, 29, +30, 35, 53, 55, 78, 4, 16, 17, 5, 6, 7, 8, 9, 14, 15, +18, 24, 25, 26, 31, 33, 36, 38, 39, 40, 41, 42, 43, 44, 45, +46, 47, 48, 49, 50, 51, 52, 54, 56, 57, 58, 59, 60, 61, 62, +63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 77, 79, +80, 81, 82, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, +12, 13, 14, 15, 5, 30, 31, 0, 1, 2, 3, 4, 6, 7, 8, +9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, +24, 25, 26, 27, 28, 29, 32, 33, 34, 35, 36, 37, 59, 0, 1, +2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, +17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, +32, 33, 34, 35, 36, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, +48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 60, 61, 62, 63, +64, 65, 36, 61, 20, 1, 19, 126, 127, 60, 56, 33, 34, 35, 21, +22, 52, 59, 67, 71, 72, 63, 65, 66, 32, 45, 57, 84, 91, 114, +125, 64, 0, 2, 3, 68, 70, 73, 26, 46, 53, 54, 55, 58, 115, +131, 74, 12, 96, 97, 99, 41, 42, 44, 49, 50, 75, 80, 83, 85, +103, 106, 107, 108, 109, 110, 111, 112, 113, 116, 117, 118, 119, 120, 124, +13, 62, 76, 77, 78, 86, 128, 129, 94, 23, 24, 25, 27, 28, 29, +30, 31, 37, 38, 39, 40, 43, 47, 48, 51, 4, 5, 6, 7, 8, +9, 10, 11, 14, 15, 16, 17, 18, 69, 79, 81, 82, 87, 88, 89, +90, 92, 93, 95, 98, 100, 101, 102, 104, 105, 121, 122, 123, 130, 132, +3, 2, 10, 11, 1, 4, 6, 8, 5, 7, 9, 0, 31, 30, 15, +43, 49, 53, 57, 45, 12, 13, 16, 18, 32, 34, 36, 14, 33, 35, +37, 21, 23, 17, 19, 20, 22, 24, 25, 26, 27, 28, 29, 42, 48, +52, 56, 38, 41, 44, 46, 50, 54, 39, 40, 47, 51, 55, 35, 36, +3, 5, 7, 9, 11, 2, 17, 30, 34, 14, 20, 23, 4, 6, 13, +16, 18, 19, 22, 24, 25, 31, 32, 33, 8, 10, 12, 15, 21, 0, +1, 26, 27, 28, 29, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, +25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, +40, 41, 42, 43, 44, 45, 46, 47, 5, 4, 6, 21, 0, 3, 18, +16, 15, 17, 9, 12, 20, 7, 14, 19, 8, 10, 11, 13, 1, 2, +167, 168, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, +13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, +28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, +43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, +58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, +73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, +88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, +103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, +118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, +133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, +148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, +163, 164, 165, 166, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, +2, 3, 7, 6, 8, 9, 16, 2, 18, 19, 28, 27, 24, 25, 26, +17, 3, 20, 22, 21, 23, 13, 0, 4, 14, 1, 5, 12, 10, 11, +15, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7, 12, 13, +14, 15, 16, 17, 18, 19, 0, 1, 2, 3, 4, 5, 6, 7, 8, +9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, +24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, +39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, +54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, +69, 70, 71, 72, 73, 18, 22, 28, 35, 53, 54, 57, 59, 0, 1, +2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, +17, 19, 20, 21, 23, 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, +36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, +51, 52, 55, 56, 58, 9, 13, 4, 6, 11, 15, 8, 12, 5, 7, +10, 14, 2, 3, 0, 1, 0, 3, 4, 7, 1, 2, 5, 6, 8, +9, 10, 0, 1, 2, 65, 64, 66, 52, 46, 49, 53, 59, 44, 48, +45, 57, 34, 25, 26, 27, 36, 56, 24, 43, 47, 0, 1, 2, 3, +4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, +19, 20, 21, 22, 23, 28, 29, 30, 31, 32, 33, 35, 37, 38, 39, +40, 41, 42, 50, 51, 54, 55, 58, 60, 61, 62, 63, 0, 1, 2, +3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, +18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, +33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, +48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, +63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, +78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, +93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, +108, 109, 110, 111, 112, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, +25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, +40, 41, 42, 43, 44, 45, 46, 47, 48, 0, 1, 2, 3, 4, 5, +6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, +21, 22, 23, 24, 25, 26, 0, 1, 2, 3, 4, 5, 6, 7, 8, +9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, +24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, +39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, +54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, +69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, +84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, +99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, +114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, +129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, +144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, +159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, +174, 175, 84, 86, 5, 16, 18, 21, 81, 82, 3, 6, 10, 12, 14, +22, 24, 26, 29, 39, 41, 42, 54, 56, 58, 60, 62, 75, 0, 1, +2, 4, 7, 8, 9, 11, 13, 15, 17, 19, 20, 23, 25, 27, 28, +30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45, 46, 47, +48, 49, 50, 51, 52, 53, 55, 57, 59, 61, 63, 64, 65, 66, 67, +68, 69, 70, 71, 72, 73, 74, 76, 77, 78, 79, 80, 83, 85, 0, +2, 4, 6, 14, 16, 18, 20, 8, 31, 10, 11, 24, 25, 28, 29, +32, 33, 36, 38, 40, 41, 44, 45, 9, 30, 1, 3, 5, 7, 12, +13, 15, 17, 19, 21, 22, 23, 26, 27, 34, 35, 37, 39, 42, 43, +46, 47, 27, 28, 35, 36, 45, 46, 13, 14, 59, 60, 0, 1, 2, +3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16, 17, 18, 19, +20, 21, 22, 23, 24, 25, 26, 29, 30, 31, 32, 33, 34, 37, 38, +39, 40, 41, 42, 43, 44, 47, 48, 49, 50, 51, 52, 53, 54, 55, +56, 57, 58, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, +73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, +88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, +103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, +118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, +133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, +148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, +163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, +178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, +193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, +208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, +223, 224, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, +13, 14, 15, 16, 17, 0, 4, 1, 2, 3, 7, 5, 9, 6, 8, +0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, +15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, +30, 31, 32, 33, 34, 35, 36, 37, 8, 11, 9, 10, 0, 1, 4, +5, 2, 3, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +10, 11, 12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, +12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, +27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, +42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, +57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, +72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, +87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, +102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, +117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, +132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, +147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, +162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, +177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, +192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, +207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, +222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, +237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, +252, 253, 254, 255, 256, 257, 0, 1, 2, 3, 4, 5, 6, 7, 8, +9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, +35, 1, 2, 20, 29, 32, 33, 34, 0, 3, 4, 5, 6, 7, 8, +9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, +25, 26, 27, 28, 30, 31, 36, 37, 38, 39, 40, 41, 42, 43, 44, +45, 46, 47, 48, 49, 50, 51, 52, 53, 0, 1, 2, 3, 4, 5, +6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, +21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, +36, 37, 38, 39, 40, 0, 1, 0, 1, 2, 3, 4, 5, 0, 1, +2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, +17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, +32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, +47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, +62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, +77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, +92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, +107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, +122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, +137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, +152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, +167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, +182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, +197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, +212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, +227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, +242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, +257, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, +14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, +29, 0, 1, 4, 27, 19, 21, 5, 18, 6, 8, 10, 11, 12, 14, +15, 16, 17, 20, 0, 1, 2, 3, 7, 9, 13, 22, 23, 24, 25, +26, 28, 29, 30, 31, 32, 33, 34, 35, 0, 1, 2, 3, 4, 0, +1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, +16, 17, 18, 19, 112, 110, 113, 111, 29, 37, 18, 93, 96, 97, 99, +98, 92, 100, 104, 105, 108, 101, 61, 62, 69, 77, 86, 70, 78, 85, +68, 76, 87, 60, 54, 55, 59, 30, 38, 73, 81, 90, 94, 19, 14, +16, 71, 79, 84, 50, 51, 57, 0, 2, 4, 6, 8, 10, 12, 20, +26, 34, 44, 64, 72, 80, 91, 40, 63, 13, 5, 7, 9, 11, 15, +1, 3, 39, 21, 48, 49, 56, 95, 74, 82, 89, 31, 25, 33, 45, +65, 43, 103, 106, 107, 109, 17, 52, 53, 58, 28, 36, 41, 23, 27, +35, 47, 42, 66, 22, 24, 32, 46, 67, 75, 83, 88, 102, 6, 7, +0, 1, 3, 5, 2, 4, 50, 48, 52, 54, 56, 2, 6, 14, 18, +22, 26, 30, 34, 49, 53, 55, 57, 58, 59, 60, 0, 1, 3, 4, +5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 19, 20, 21, 23, +24, 25, 27, 28, 29, 31, 32, 33, 35, 36, 37, 38, 39, 40, 41, +42, 43, 44, 45, 46, 47, 51, 23, 24, 25, 26, 16, 18, 20, 22, +15, 17, 19, 21, 6, 8, 10, 12, 14, 0, 2, 1, 3, 4, 5, +11, 7, 9, 13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, +11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, +26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, +41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, +56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, +71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, +86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, +101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, +116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, +131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 1, 0, +2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0, 1, +2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, +17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, +32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, +47, 48, 49, 50, 51, 52, 53, 54, 55, 0, 1, 2, 3, 4, 5, +6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 112, 114, 19, 61, 70, +18, 111, 3, 5, 6, 66, 82, 109, 110, 113, 58, 102, 104, 31, 35, +36, 37, 38, 71, 77, 86, 93, 95, 96, 108, 47, 25, 45, 46, 53, +55, 75, 40, 41, 42, 52, 60, 83, 84, 24, 26, 11, 12, 22, 23, +28, 30, 57, 63, 65, 69, 81, 90, 106, 32, 33, 34, 49, 50, 51, +72, 73, 78, 79, 87, 88, 94, 4, 7, 8, 9, 10, 20, 21, 29, +39, 43, 44, 48, 54, 56, 59, 62, 64, 67, 68, 74, 76, 80, 89, +91, 92, 100, 101, 115, 116, 16, 99, 13, 14, 15, 17, 27, 85, 97, +98, 103, 105, 107, 117, 7, 10, 19, 0, 1, 2, 3, 4, 5, 6, +8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 0, 1, 2, 3, 4, +5, 7, 17, 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, +13, 14, 15, 16, 18, 19, 20, 21, 22, 23, 0, 1, 0, 7, 11, +5, 6, 10, 3, 4, 1, 2, 8, 9, 12, 0, 1, 2, 3, 4, +0, 1, 2, 3, 4, 1, 3, 0, 2, 4, 5, 6, 7, 8, 9, +10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 1, 2, 3, 4, +5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, +20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, +35, 36, 0, 1, 75, 78, 76, 15, 79, 77, 8, 85, 14, 0, 4, +10, 81, 83, 84, 87, 12, 80, 82, 11, 9, 2, 6, 86, 36, 38, +37, 13, 1, 5, 3, 7, 49, 22, 24, 28, 32, 50, 58, 63, 66, +71, 23, 25, 29, 33, 47, 48, 51, 52, 53, 54, 46, 17, 19, 16, +20, 21, 26, 27, 30, 31, 34, 35, 39, 40, 41, 42, 43, 44, 45, +55, 56, 57, 59, 60, 61, 62, 64, 65, 67, 68, 69, 70, 72, 73, +74, 18, 11, 22, 1, 3, 5, 7, 9, 10, 13, 15, 16, 18, 20, +26, 28, 0, 2, 4, 6, 8, 12, 14, 17, 19, 21, 23, 24, 25, +27, 29, 47, 45, 43, 48, 42, 44, 49, 46, 33, 11, 27, 31, 37, +38, 63, 17, 18, 21, 23, 25, 29, 32, 35, 77, 83, 97, 0, 5, +9, 15, 16, 19, 39, 41, 53, 69, 20, 22, 24, 26, 28, 30, 34, +36, 76, 82, 96, 102, 103, 1, 3, 7, 13, 40, 51, 57, 61, 67, +71, 73, 75, 81, 87, 95, 101, 106, 107, 70, 2, 4, 6, 8, 10, +12, 14, 50, 52, 54, 55, 56, 58, 59, 60, 62, 64, 65, 66, 68, +72, 74, 78, 79, 80, 84, 85, 86, 88, 89, 90, 91, 92, 93, 94, +98, 99, 100, 104, 105, 108, 109, 0, 1, 46, 43, 47, 42, 3, 44, +5, 20, 4, 18, 24, 26, 30, 38, 34, 36, 7, 9, 22, 2, 45, +21, 40, 19, 35, 37, 39, 32, 12, 14, 28, 10, 16, 41, 25, 27, +31, 6, 8, 23, 33, 11, 17, 13, 15, 29, 52, 50, 53, 48, 49, +51, 0, 1, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, +6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, +21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, +36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, +51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, +66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, +81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, +96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, +111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, +126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, +141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, +156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, +171, 172, 173, 174, 175, 176, 177, 178, 179, 0, 1, 2, 3, 4, 5, +6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, +21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, +36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, +51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, +66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, +81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, +96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, +111, 112, 113, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, +12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, +27, 28, 29, 30, 31, 32, 33, 34, 35, 5, 15, 19, 27, 31, 35, +39, 43, 96, 104, 120, 121, 131, 0, 1, 2, 10, 12, 24, 25, 46, +47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 76, 77, 78, 79, 80, +81, 82, 83, 84, 85, 86, 87, 88, 90, 91, 93, 95, 97, 99, 101, +103, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, +119, 122, 123, 124, 125, 126, 127, 128, 129, 130, 132, 133, 134, 135, 136, +137, 138, 140, 144, 148, 152, 156, 160, 164, 3, 4, 6, 7, 8, 9, +11, 13, 14, 16, 17, 18, 20, 21, 22, 23, 26, 28, 29, 30, 32, +33, 34, 36, 37, 38, 40, 41, 42, 44, 45, 57, 58, 59, 60, 61, +62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 89, +92, 94, 98, 100, 102, 139, 141, 142, 143, 145, 146, 147, 149, 150, 151, +153, 154, 155, 157, 158, 159, 161, 162, 163, 165, 166, 36, 57, 60, 65, +68, 114, 135, 156, 161, 162, 165, 170, 176, 179, 182, 183, 188, 191, 194, +197, 198, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, +13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, +28, 29, 30, 31, 32, 33, 34, 35, 37, 38, 39, 40, 41, 42, 43, +44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 58, 59, +61, 62, 63, 64, 66, 67, 69, 70, 71, 72, 73, 74, 75, 76, 77, +78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, +93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, +108, 109, 110, 111, 112, 113, 115, 116, 117, 118, 119, 120, 121, 122, 123, +124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 136, 137, 138, 139, +140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, +155, 157, 158, 159, 160, 163, 164, 166, 167, 168, 169, 171, 172, 173, 174, +175, 177, 178, 180, 181, 184, 185, 186, 187, 189, 190, 192, 193, 195, 196, +199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, +214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, +229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, +244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, +259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, +274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, +289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, +304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, +319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, +334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, +349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, +364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, +379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, +394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, +409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, +424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, +439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, +454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, +469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, +484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, +499, 500, 501, 502, 503, 504, 505, 506, 0, 1, 2, 3, 4, 5, 6, +7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, +25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, +40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, +55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, +70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, +85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, +100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, +115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, +130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, +145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, +160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, +175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, +190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, +205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, +220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, +235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, +250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, +265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, +280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, +295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, +310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, +325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, +340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, +355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, +370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, +385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, +400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, +415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, +430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, +445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, +460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, +475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, +490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, +505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, +520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, +535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, +550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, +565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, +580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, +595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, +610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, +625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, +640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, +655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, +0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, +15, 16, 17, 18, 19, 20, 21, 0, 1, 2, 3, 4, 5, 6, 7, +8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, +23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, +38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, +53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, +68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, +83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, +98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, +113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, +128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, +143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, +158, 159, 160, 161, 162, 163, 164, 165, 166, 0, 1, 2, 3, 4, 5, +6, 7, 8, 9, 10, 11, 12, 13, 14, 0, 1, 2, 3, 4, 5, +0, 3, 5, 10, 16, 18, 94, 4, 6, 7, 8, 17, 37, 44, 48, +51, 56, 66, 69, 75, 79, 82, 84, 88, 98, 99, 102, 1, 2, 9, +11, 12, 13, 14, 15, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, +29, 30, 31, 32, 33, 34, 35, 36, 38, 39, 40, 41, 42, 43, 45, +46, 47, 49, 50, 52, 53, 54, 55, 57, 58, 59, 60, 61, 62, 63, +64, 65, 67, 68, 70, 71, 72, 73, 74, 76, 77, 78, 80, 81, 83, +85, 86, 87, 89, 90, 91, 92, 93, 95, 96, 97, 100, 101, 103, 104, +105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, +120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, +135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, +150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, +165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, +180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, +81, 18, 24, 33, 69, 83, 19, 25, 35, 71, 0, 1, 2, 3, 4, +5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 20, 21, +22, 23, 26, 27, 28, 29, 30, 31, 32, 34, 36, 37, 38, 39, 40, +41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, +56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 70, 72, +73, 74, 75, 76, 77, 78, 79, 80, 82, 84, 85, 86, 87, 88, 89, +90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, +105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, +120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, +135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, +150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, +165, 166, 167, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, +12, 13, 14, 15, 16, 17, 4, 5, 6, 31, 32, 39, 40, 41, 0, +1, 2, 3, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, +19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 33, 34, 35, +36, 37, 38, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, +54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, +69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, +84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, +99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, +114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, +129, 130, 131, 132, 133, 134, 135, 136, 0, 1, 3, 22, 23, 26, 34, +0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, +16, 17, 18, 19, 20, 21, 24, 25, 27, 28, 29, 30, 31, 32, 33, +35, 36, 37, 38, 39, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0, +1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, +16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, +31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, +46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, +61, 62, 63, 64, 65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, +25, 26, 27, 28, 29, 30, 31, 32, 33, 51, 0, 1, 2, 3, 4, +5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, +20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, +35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, +50, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, +66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 0, 1, 2, +3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, +18, 19, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, +13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, +28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 1, +2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, +17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, +32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, +47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, +62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, +77, 74, 75, 92, 93, 116, 143, 144, 149, 14, 15, 34, 35, 36, 37, +68, 69, 98, 99, 104, 105, 110, 111, 154, 155, 156, 157, 166, 167, 168, +169, 178, 179, 180, 181, 4, 5, 6, 7, 22, 23, 24, 25, 46, 47, +50, 51, 56, 57, 60, 61, 80, 82, 85, 87, 121, 122, 123, 124, 133, +134, 135, 136, 190, 191, 194, 197, 202, 203, 206, 207, 212, 213, 216, 217, +0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, +21, 26, 27, 28, 29, 30, 31, 32, 33, 38, 39, 40, 41, 42, 43, +44, 45, 48, 49, 52, 53, 54, 55, 58, 59, 62, 63, 64, 65, 66, +67, 70, 71, 72, 73, 76, 77, 78, 79, 81, 83, 84, 86, 88, 89, +90, 91, 94, 95, 96, 97, 100, 101, 102, 103, 106, 107, 108, 109, 112, +113, 114, 115, 117, 118, 119, 120, 125, 126, 127, 128, 129, 130, 131, 132, +137, 138, 139, 140, 141, 142, 145, 146, 147, 148, 150, 151, 152, 153, 158, +159, 160, 161, 162, 163, 164, 165, 170, 171, 172, 173, 174, 175, 176, 177, +182, 183, 184, 185, 186, 187, 188, 189, 192, 193, 195, 196, 198, 199, 200, +201, 204, 205, 208, 209, 210, 211, 214, 215, 218, 219, 220, 221, 0, 1, +2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, +17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, +32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 0, 1, 2, +3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, +18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, +33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, +48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, +63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, +78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, +93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, +108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, +123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, +138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, +153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, +168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, +183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, +198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, +213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, +228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, +243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, +258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, +273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, +288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, +303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, +318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, +333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, +348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, +363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, +0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, +15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, +30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, +45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 0, 1, 2, 3, +4, 5, 6, 7, 0, 1, 14, 15, 16, 17, 2, 3, 4, 5, 8, +9, 10, 11, 12, 13, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, +28, 29, 30, 31, 32, 33, 11, 2, 5, 10, 8, 4, 9, 1, 7, +3, 0, 6, 42, 45, 36, 39, 12, 16, 18, 22, 38, 41, 44, 47, +37, 40, 43, 46, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, +11, 13, 14, 15, 17, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, +30, 31, 32, 33, 34, 35, 0, 1, 2, 3, 4, 5, 6, 7, 8, +9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, +24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, +39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, +54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, +69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, +84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, +99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, +114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, +129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, +144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, +159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, +174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, +189, 190, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, +13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, +28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, +43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, +58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, +73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, +88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, +103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 4, 5, +6, 8, 1, 3, 0, 2, 7, 9, 0, 1, 2, 3, 4, 5, 6, +7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, +22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, +37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, +52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, +67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, +82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, +97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, +112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, +127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, +142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, +157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, +172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, +187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, +202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, +217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, +232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, +247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, +262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, +277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, +292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, +307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, +322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, +337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, +352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, +367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, +382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, +397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, +412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, +427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, +442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, +457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, +472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, +487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, +502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, +517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, +532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, +547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, +562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, +577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, +592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, +607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, +622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, +637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, +652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, +667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, +682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, +697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, +712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, +727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, +742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, +757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, +772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, +787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, +802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, +817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, +832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, +847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, +862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, +877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, +892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, +907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, +922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, +937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, +952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, +967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, +982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, +997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, +1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025, 1026, +1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, +1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, +1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, +1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, +1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 1101, +1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1115, 1116, +1117, 1118, 1119, 1120, 1121, 1122, 1123, 1124, 1125, 1126, 1127, 1128, 1129, 1130, 1131, +1132, 1133, 1134, 1135, 1136, 1137, 1138, 1139, 1140, 1141, 1142, 1143, 1144, 1145, 1146, +1147, 1148, 1149, 1150, 1151, 1152, 1153, 1154, 1155, 1156, 1157, 1158, 1159, 1160, 1161, +1162, 1163, 1164, 1165, 1166, 1167, 1168, 1169, 1170, 1171, 1172, 1173, 1174, 1175, 1176, +1177, 1178, 1179, 1180, 1181, 1182, 1183, 1184, 1185, 1186, 1187, 1188, 1189, 1190, 1191, +1192, 1193, 1194, 1195, 1196, 1197, 1198, 1199, 1200, 1201, 1202, 1203, 1204, 1205, 1206, +1207, 1208, 1209, 1210, 1211, 1212, 1213, 1214, 1215, 1216, 1217, 1218, 1219, 1220, 1221, +1222, 1223, 1224, 1225, 1226, 1227, 1228, 1229, 1230, 1231, 1232, 1233, 1234, 1235, 1236, +1237, 1238, 1239, 1240, 1241, 1242, 1243, 1244, 1245, 1246, 1247, 1248, 1249, 1250, 1251, +1252, 1253, 1254, 1255, 1256, 1257, 1258, 1259, 1260, 1261, 1262, 1263, 1264, 1265, 1266, +1267, 1268, 1269, 1270, 1271, 1272, 1273, 1274, 1275, 1276, 1277, 1278, 1279, 1280, 1281, +1282, 1283, 1284, 1285, 1286, 1287, 1288, 1289, 1290, 1291, 1292, 1293, 1294, 1295, 1296, +1297, 1298, 1299, 1300, 1301, 1302, 1303, 1304, 1305, 1306, 1307, 1308, 1309, 1310, 1311, +1312, 1313, 1314, 1315, 1316, 1317, 1318, 1319, 1320, 1321, 1322, 1323, 1324, 1325, 1326, +1327, 1328, 1329, 1330, 1331, 1332, 1333, 1334, 1335, 1336, 1337, 1338, 1339, 1340, 1341, +1342, 1343, 1344, 1345, 1346, 1347, 1348, 1349, 1350, 1351, 1352, 1353, 1354, 1355, 1356, +1357, 1358, 1359, 1360, 1361, 1362, 1363, 1364, 1365, 1366, 1367, 1368, 1369, 1370, 1371, +1372, 1373, 1374, 1375, 1376, 1377, 1378, 1379, 1380, 1381, 1382, 1383, 1384, 1385, 1386, +1387, 1388, 1389, 1390, 1391, 1392, 1393, 1394, 1395, 1396, 1397, 1398, 1399, 1400, 1401, +1402, 1403, 1404, 1405, 1406, 1407, 1408, 1409, 1410, 1411, 1412, 1413, 1414, 1415, 1416, +1417, 1418, 1419, 1420, 1421, 1422, 1423, 1424, 1425, 1426, 1427, 1428, 1429, 1430, 1431, +1432, 1433, 1434, 1435, 1436, 1437, 1438, 1439, 1440, 1441, 1442, 1443, 1444, 1445, 1446, +1447, 1448, 1449, 1450, 1451, 1452, 1453, 1454, 1455, 1456, 1457, 1458, 1459, 1460, 1461, +1462, 1463, 1464, 1465, 1466, 1467, 1468, 1469, 1470, 1471, 1472, 1473, 1474, 1475, 1476, +1477, 1478, 1479, 1480, 1481, 1482, 1483, 1484, 1485, 1486, 1487, 1488, 1489, 1490, 1491, +1492, 1493, 1494, 1495, 1496, 1497, 1498, 1499, 1500, 1501, 1502, 1503, 1504, 1505, 1506, +1507, 1508, 1509, 1510, 1511, 1512, 1513, 1514, 1515, 1516, 1517, 1518, 1519, 1520, 1521, +1522, 1523, 1524, 1525, 1526, 1527, 1528, 1529, 1530, 1531, 1532, 1533, 1534, 1535, 1536, +1537, 1538, 1539, 1540, 1541, 1542, 1543, 1544, 1545, 1546, 1547, 1548, 1549, 1550, 1551, +1552, 1553, 1554, 1555, 1556, 1557, 1558, 1559, 1560, 1561, 1562, 1563, 1564, 1565, 1566, +1567, 1568, 1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, 1578, 1579, 1580, 1581, +1582, 1583, 1584, 1585, 1586, 1587, 1588, 1589, 1590, 1591, 1592, 1593, 1594, 1595, 1596, +1597, 1598, 1599, 1600, 1601, 1602, 1603, 1604, 1605, 1606, 1607, 1608, 1609, 1610, 1611, +1612, 1613, 1614, 1615, 1616, 1617, 1618, 1619, 1620, 1621, 1622, 1623, 1624, 1625, 1626, +1627, 1628, 1629, 1630, 1631, 1632, 1633, 1634, 1635, 1636, 1637, 1638, 1639, 1640, 1641, +1642, 1643, 1644, 1645, 1646, 1647, 1648, 1649, 1650, 1651, 1652, 1653, 1654, 1655, 1656, +1657, 1658, 1659, 1660, 1661, 1662, 1663, 1664, 1665, 1666, 1667, 1668, 1669, 1670, 1671, +1672, 1673, 1674, 1675, 1676, 1677, 1678, 1679, 1680, 1681, 1682, 1683, 1684, 1685, 1686, +1687, 1688, 1689, 1690, 1691, 1692, 1693, 1694, 1695, 1696, 1697, 1698, 1699, 1700, 1701, +1702, 1703, 1704, 1705, 1706, 1707, 1708, 1709, 1710, 1711, 1712, 1713, 1714, 1715, 1716, +1717, 1718, 1719, 1720, 1721, 1722, 1723, 1724, 1725, 1726, 1727, 1728, 1729, 1730, 1731, +1732, 1733, 1734, 1735, 1736, 1737, 1738, 1739, 1740, 1741, 1742, 1743, 1744, 1745, 1746, +1747, 1748, 1749, 1750, 1751, 1752, 1753, 1754, 1755, 1756, 1757, 1758, 1759, 1760, 1761, +1762, 1763, 1764, 1765, 1766, 1767, 1768, 1769, 1770, 1771, 1772, 1773, 1774, 1775, 1776, +1777, 1778, 1779, 1780, 1781, 1782, 1783, 1784, 1785, 1786, 1787, 1788, 1789, 1790, 1791, +1792, 1793, 1794, 1795, 1796, 1797, 1798, 1799, 1800, 1801, 1802, 1803, 1804, 1805, 1806, +1807, 1808, 1809, 1810, 1811, 1812, 1813, 1814, 1815, 1816, 1817, 1818, 1819, 1820, 1821, +1822, 1823, 1824, 1825, 1826, 1827, 1828, 1829, 1830, 1831, 1832, 1833, 1834, 1835, 1836, +1837, 1838, 1839, 1840, 1841, 1842, 1843, 1844, 1845, 1846, 1847, 1848, 1849, 1850, 1851, +1852, 1853, 1854, 1855, 1856, 1857, 1858, 1859, 1860, 1861, 1862, 1863, 1864, 1865, 1866, +1867, 1868, 1869, 1870, 1871, 1872, 1873, 1874, 1875, 1876, 1877, 1878, 1879, 1880, 1881, +1882, 1883, 1884, 1885, 1886, 1887, 1888, 1889, 1890, 1891, 1892, 1893, 1894, 1895, 1896, +1897, 1898, 1899, 1900, 1901, 1902, 1903, 1904, 1905, 1906, 1907, 1908, 1909, 1910, 1911, +1912, 1913, 1914, 1915, 1916, 1917, 1918, 1919, 1920, 1921, 1922, 1923, 1924, 1925, 1926, +1927, 1928, 1929, 1930, 1931, 1932, 1933, 1934, 1935, 1936, 1937, 1938, 1939, 1940, 1941, +1942, 1943, 1944, 1945, 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, +1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, 1969, 1970, 1971, +1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, 1981, 1982, 1983, 1984, 1985, 1986, +1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, +2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026, 2027, 2028, 2029, 2030, 2031, +2032, 2033, 2034, 2035, 2036, 2037, 2038, 2039, 2040, 2041, 2042, 2043, 2044, 2045, 2046, +2047, 2048, 2049, 2050, 2051, 2052, 2053, 2054, 2055, 2056, 2057, 2058, 2059, 2060, 2061, +2062, 2063, 2064, 2065, 2066, 2067, 2068, 2069, 2070, 2071, 2072, 2073, 2074, 2075, 2076, +2077, 2078, 2079, 2080, 2081, 2082, 2083, 2084, 2085, 2086, 2087, 2088, 2089, 2090, 2091, +2092, 2093, 2094, 2095, 2096, 2097, 2098, 2099, 2100, 2101, 2102, 2103, 2104, 2105, 2106, +2107, 2108, 2109, 2110, 2111, 2112, 2113, 2114, 2115, 2116, 2117, 2118, 2119, 2120, 2121, +2122, 2123, 2124, 2125, 2126, 2127, 2128, 2129, 2130, 2131, 2132, 2133, 2134, 2135, 2136, +2137, 2138, 2139, 2140, 2141, 2142, 2143, 2144, 2145, 2146, 2147, 2148, 2149, 2150, 2151, +2152, 2153, 2154, 2155, 2156, 2157, 2158, 2159, 2160, 2161, 2162, 2163, 2164, 2165, 2166, +2167, 2168, 2169, 2170, 2171, 2172, 2173, 2174, 2175, 2176, 2177, 2178, 2179, 2180, 2181, +2182, 2183, 2184, 2185, 2186, 2187, 2188, 2189, 2190, 2191, 2192, 2193, 2194, 2195, 2196, +2197, 2198, 2199, 2200, 2201, 2202, 2203, 2204, 2205, 2206, 2207, 2208, 2209, 2210, 2211, +2212, 2213, 2214, 2215, 2216, 2217, 2218, 2219, 2220, 2221, 2222, 2223, 2224, 2225, 2226, +2227, 2228, 2229, 2230, 2231, 2232, 2233, 2234, 2235, 2236, 2237, 2238, 2239, 2240, 2241, +2242, 2243, 2244, 2245, 2246, 2247, 2248, 2249, 2250, 2251, 2252, 2253, 2254, 2255, 2256, +2257, 2258, 2259, 2260, 2261, 2262, 2263, 2264, 2265, 2266, 2267, 2268, 2269, 2270, 2271, +2272, 2273, 2274, 2275, 2276, 2277, 2278, 2279, 2280, 2281, 2282, 2283, 2284, 2285, 2286, +2287, 2288, 2289, 2290, 2291, 2292, 2293, 2294, 2295, 2296, 2297, 2298, 2299, 2300, 2301, +2302, 2303, 2304, 2305, 2306, 2307, 2308, 2309, 2310, 2311, 2312, 2313, 2314, 2315, 2316, +2317, 2318, 2319, 2320, 2321, 2322, 2323, 2324, 2325, 2326, 2327, 2328, 2329, 2330, 2331, +2332, 2333, 2334, 2335, 2336, 2337, 2338, 2339, 2340, 2341, 2342, 2343, 2344, 2345, 2346, +2347, 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, 2357, 2358, 2359, 2360, 2361, +2362, 2363, 2364, 2365, 2366, 2367, 2368, 2369, 2370, 2371, 2372, 2373, 2374, 2375, 2376, +2377, 2378, 2379, 2380, 2381, 2382, 2383, 2384, 2385, 2386, 2387, 2388, 2389, 2390, 2391, +2392, 2393, 2394, 2395, 2396, 2397, 2398, 2399, 2400, 2401, 2402, 2403, 2404, 2405, 2406, +2407, 2408, 2409, 2410, 2411, 2412, 2413, 2414, 2415, 2416, 2417, 2418, 2419, 2420, 2421, +2422, 2423, 2424, 2425, 2426, 2427, 2428, 2429, 2430, 2431, 2432, 2433, 2434, 2435, 2436, +2437, 2438, 2439, 2440, 2441, 2442, 2443, 2444, 2445, 2446, 2447, 2448, 2449, 2450, 2451, +2452, 2453, 2454, 2455, 2456, 2457, 2458, 2459, 2460, 2461, 2462, 2463, 2464, 2465, 2466, +2467, 2468, 2469, 2470, 2471, 2472, 2473, 2474, 2475, 2476, 2477, 2478, 2479, 2480, 2481, +2482, 2483, 2484, 2485, 2486, 2487, 2488, 2489, 2490, 2491, 2492, 2493, 2494, 2495, 2496, +2497, 2498, 2499, 2500, 2501, 2502, 2503, 2504, 2505, 2506, 2507, 2508, 2509, 2510, 2511, +2512, 2513, 2514, 2515, 2516, 2517, 2518, 2519, 2520, 2521, 2522, 2523, 2524, 2525, 2526, +2527, 2528, 2529, 2530, 2531, 2532, 2533, 2534, 2535, 2536, 2537, 2538, 2539, 2540, 2541, +2542, 2543, 2544, 2545, 2546, 2547, 2548, 2549, 2550, 2551, 2552, 2553, 2554, 2555, 2556, +2557, 2558, 2559, 2560, 2561, 2562, 2563, 2564, 2565, 2566, 2567, 2568, 2569, 2570, 2571, +2572, 2573, 2574, 2575, 2576, 2577, 2578, 2579, 2580, 2581, 2582, 2583, 2584, 2585, 2586, +2587, 2588, 2589, 2590, 2591, 2592, 2593, 2594, 2595, 2596, 2597, 2598, 2599, 2600, 2601, +2602, 2603, 2604, 2605, 2606, 2607, 2608, 2609, 2610, 2611, 2612, 2613, 2614, 2615, 2616, +2617, 2618, 2619, 2620, 2621, 2622, 2623, 2624, 2625, 2626, 2627, 2628, 2629, 2630, 2631, +2632, 2633, 2634, 2635, 2636, 2637, 2638, 2639, 2640, 2641, 2642, 2643, 2644, 2645, 2646, +2647, 2648, 2649, 2650, 2651, 2652, 2653, 2654, 2655, 2656, 2657, 2658, 2659, 2660, 2661, +2662, 2663, 2664, 2665, 2666, 2667, 2668, 2669, 2670, 2671, 2672, 2673, 2674, 2675, 2676, +2677, 2678, 2679, 2680, 2681, 2682, 2683, 2684, 2685, 2686, 2687, 2688, 2689, 2690, 2691, +2692, 2693, 2694, 2695, 2696, 2697, 2698, 2699, 2700, 2701, 2702, 2703, 2704, 2705, 2706, +2707, 2708, 2709, 2710, 2711, 2712, 2713, 2714, 2715, 2716, 2717, 2718, 2719, 2720, 2721, +2722, 2723, 2724, 2725, 2726, 2727, 2728, 2729, 2730, 2731, 2732, 2733, 2734, 2735, 2736, +2737, 2738, 2739, 2740, 2741, 2742, 2743, 2744, 2745, 2746, 2747, 2748, 2749, 2750, 2751, +2752, 2753, 2754, 2755, 2756, 2757, 2758, 2759, 2760, 2761, 2762, 2763, 2764, 2765, 2766, +2767, 2768, 2769, 2770, 2771, 2772, 2773, 2774, 2775, 2776, 2777, 2778, 2779, 2780, 2781, +2782, 2783, 2784, 2785, 2786, 2787, 2788, 2789, 2790, 2791, 2792, 2793, 2794, 2795, 2796, +2797, 2798, 2799, 2800, 2801, 2802, 2803, 2804, 2805, 2806, 2807, 2808, 2809, 2810, 2811, +2812, 2813, 2814, 2815, 2816, 2817, 2818, 2819, 2820, 2821, 2822, 2823, 2824, 2825, 2826, +2827, 2828, 2829, 2830, 2831, 2832, 2833, 2834, 2835, 2836, 2837, 2838, 2839, 2840, 2841, +2842, 2843, 2844, 2845, 2846, 2847, 2848, 2849, 2850, 2851, 2852, 2853, 2854, 2855, 2856, +2857, 2858, 2859, 2860, 2861, 2862, 2863, 9, 11, 6, 7, 8, 10, 4, 5, +0, 1, 2, 3, 0, 2, 10, 3, 6, 1, 9, 11, 4, 8, 7, +5, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, +14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, +29, 30, 31, 32, 33, 34, 35, 68, 116, 22, 10, 97, 117, 52, 110, +7, 19, 111, 115, 6, 18, 0, 74, 88, 12, 48, 108, 99, 109, 38, +104, 4, 16, 24, 96, 15, 3, 50, 84, 71, 85, 36, 102, 103, 119, +54, 86, 100, 30, 25, 105, 113, 69, 53, 63, 98, 87, 93, 89, 95, +9, 21, 62, 114, 5, 17, 26, 55, 2, 14, 65, 8, 20, 64, 92, +42, 106, 11, 23, 46, 66, 34, 101, 107, 56, 35, 47, 57, 67, 76, +118, 75, 83, 82, 94, 37, 77, 29, 51, 1, 13, 27, 49, 58, 112, +39, 59, 28, 70, 44, 73, 80, 81, 32, 72, 31, 43, 33, 40, 41, +45, 60, 61, 78, 79, 90, 91 +}; + + +/**Function************************************************************* + + Synopsis [Reads library from array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Dar_LibReadNodes() +{ + Vec_Int_t * vResult; + int i; + vResult = Vec_IntAlloc( s_nDataSize1 ); + for ( i = 0; i < s_nDataSize1; i++ ) + Vec_IntPush( vResult, s_Data1[i] ); + return vResult; +} + +/**Function************************************************************* + + Synopsis [Reads library from array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Dar_LibReadOuts() +{ + Vec_Int_t * vResult; + int i; + vResult = Vec_IntAlloc( s_nDataSize2 ); + for ( i = 0; i < s_nDataSize2; i++ ) + Vec_IntPush( vResult, s_Data2[i] ); + return vResult; +} + +/**Function************************************************************* + + Synopsis [Reads library from array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Dar_LibReadPrios() +{ + Vec_Int_t * vResult; + int i; + vResult = Vec_IntAlloc( s_nDataSize3 ); + for ( i = 0; i < s_nDataSize3; i++ ) + Vec_IntPush( vResult, s_Data3[i] ); + return vResult; +} + +//#if 0 + +#include "abc.h" + +/**Function************************************************************* + + Synopsis [Generate arrays.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_NtkGenerateArrays( Abc_Ntk_t * pNtk ) +{ + extern int Io_WriteAigerEncode( char * pBuffer, int Pos, unsigned x ); + + Abc_Obj_t * pObj; + int i, Count = 0; + assert( Abc_NtkPiNum(pNtk) == 4 ); + assert( Abc_NtkIsStrash(pNtk) ); + assert( Abc_ObjFanoutNum(Abc_AigConst1(pNtk)) == 0 ); +/* + { + unsigned char * pBuffer; + int Pos, uLit, uLit0, uLit1, Size, Digit; + + Abc_NtkForEachPi( pNtk, pObj, i ) + pObj->pCopy = (void *)Count++; + + Pos = 0; + pBuffer = ALLOC( char, 200000 ); + Abc_AigForEachAnd( pNtk, pObj, i ) + { + pObj->pCopy = (void *)Count++; + uLit = ((int)pObj->pCopy << 1); + uLit0 = (((int)Abc_ObjFanin0(pObj)->pCopy) << 1) | Abc_ObjFaninC0(pObj); + uLit1 = (((int)Abc_ObjFanin1(pObj)->pCopy) << 1) | Abc_ObjFaninC1(pObj); + assert( uLit0 < uLit1 ); + Pos = Io_WriteAigerEncode( pBuffer, Pos, uLit - uLit1 ); + Pos = Io_WriteAigerEncode( pBuffer, Pos, uLit1 - uLit0 ); + } + // write the buffer + Size = 0; + for ( i = 0; i < Pos; i++ ) + { + if ( i % 36 == 0 ) + printf( "\n" ); + + Digit = pBuffer[i] & 0xF; + if ( Digit < 10 ) + printf( "%d", Digit ); + else + printf( "%c", Digit - 10 + 'A' ); + + + Digit = pBuffer[i]; + Digit >>= 4; + if ( Digit < 10 ) + printf( "%d", Digit ); + else + printf( "%c", Digit - 10 + 'A' ); + + } + printf( "\n" ); + printf( "Size = %d.\n", Pos ); + } +*/ + + + Abc_NtkForEachPi( pNtk, pObj, i ) + pObj->pCopy = (void *)Count++; + Abc_AigForEachAnd( pNtk, pObj, i ) + { +// if ( (Count - 4) % 6 == 0 ) +// printf( "\n" ); +// printf( "%5d,", (((int)Abc_ObjFanin0(pObj)->pCopy) << 1) | Abc_ObjFaninC0(pObj) ); +// printf( "%5d,", (((int)Abc_ObjFanin1(pObj)->pCopy) << 1) | Abc_ObjFaninC1(pObj) ); + pObj->pCopy = (void *)Count++; + } +// printf( "\n" ); +// printf( "Nodes = %d.\n", Count-4 ); + + + + Abc_NtkForEachPo( pNtk, pObj, i ) + { + if ( i % 12 == 0 ) + printf( "\n" ); + printf( "%5d,", (int)Abc_ObjFanin0(pObj)->pCopy ); + } + printf( "\n" ); + printf( "Outputs = %d.\n", Abc_NtkPoNum(pNtk) ); + + +/* + { + unsigned char * pBuffer; + Vec_Int_t * vOuts; + int Pos, Prev, Out; + + vOuts = Vec_IntAlloc( 25000 ); + Abc_NtkForEachPo( pNtk, pObj, i ) + Vec_IntPush( vOuts, Abc_ObjFaninId0(pObj) ); + Vec_IntSort( vOuts, 0 ); + + Pos = 0; + pBuffer = ALLOC( char, 50000 ); + Prev = 0; + Vec_IntForEachEntry( vOuts, Out, i ) + { + assert( Prev < Out ); + Pos = Io_WriteAigerEncode( pBuffer, Pos, Out - Prev ); + Prev = Out; + } + Vec_IntFree( vOuts ); + + // write the buffer + for ( i = 0; i < Pos; i++ ) + { + if ( i % 32 == 0 ) + printf( "\n" ); + printf( "%d,", pBuffer[i] ); + } + printf( "\n" ); + printf( "Size = %d.\n", Pos ); + } +*/ + +} + +//#endif + + + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/dar/darInt.h b/abc_with_bb_support/src/aig/dar/darInt.h new file mode 100644 index 000000000..585e88d60 --- /dev/null +++ b/abc_with_bb_support/src/aig/dar/darInt.h @@ -0,0 +1,161 @@ +/**CFile**************************************************************** + + FileName [darInt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting.] + + Synopsis [Internal declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: darInt.h,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __DAR_INT_H__ +#define __DAR_INT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +#include "vec.h" +#include "aig.h" +#include "dar.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Dar_Man_t_ Dar_Man_t; +typedef struct Dar_Cut_t_ Dar_Cut_t; + +// the AIG 4-cut +struct Dar_Cut_t_ // 6 words +{ + unsigned uSign; // cut signature + unsigned uTruth : 16; // the truth table of the cut function + unsigned Value : 11; // the value of the cut + unsigned fBest : 1; // marks the best cut + unsigned fUsed : 1; // marks the cut currently in use + unsigned nLeaves : 3; // the number of leaves + int pLeaves[4]; // the array of leaves +}; + +// the AIG manager +struct Dar_Man_t_ +{ + // input data + Dar_RwrPar_t * pPars; // rewriting parameters + Aig_Man_t * pAig; // AIG manager + // various data members + Aig_MmFixed_t * pMemCuts; // memory manager for cuts + void * pManCnf; // CNF managers + // current rewriting step + Vec_Ptr_t * vLeavesBest; // the best set of leaves + int OutBest; // the best output (in the library) + int OutNumBest; // the best number of the output + int GainBest; // the best gain + int LevelBest; // the level of node with the best gain + int ClassBest; // the equivalence class of the best replacement + // function statistics + int nTotalSubgs; // the total number of subgraphs tried + int ClassTimes[222];// the runtimes for each class + int ClassGains[222];// the gains for each class + int ClassSubgs[222];// the graphs for each class + int nCutMemUsed; // memory used for cuts + // rewriting statistics + int nNodesInit; // the original number of nodes + int nNodesTried; // the number of nodes attempted + int nCutsAll; // all cut pairs + int nCutsTried; // computed cuts + int nCutsUsed; // used cuts + int nCutsBad; // bad cuts due to absent fanin + int nCutsGood; // good cuts + int nCutsSkipped; // skipped bad cuts + // timing statistics + int timeCuts; + int timeEval; + int timeOther; + int timeTotal; + int time1; + int time2; +}; + +static inline Dar_Cut_t * Dar_ObjCuts( Aig_Obj_t * pObj ) { return pObj->pData; } +static inline void Dar_ObjSetCuts( Aig_Obj_t * pObj, Dar_Cut_t * pCuts ) { assert( !Aig_ObjIsNone(pObj) ); pObj->pData = pCuts; } + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// ITERATORS /// +//////////////////////////////////////////////////////////////////////// + +// iterator over all cuts of the node +#define Dar_ObjForEachCutAll( pObj, pCut, i ) \ + for ( (pCut) = Dar_ObjCuts(pObj), i = 0; i < (int)(pObj)->nCuts; i++, pCut++ ) +#define Dar_ObjForEachCut( pObj, pCut, i ) \ + for ( (pCut) = Dar_ObjCuts(pObj), i = 0; i < (int)(pObj)->nCuts; i++, pCut++ ) if ( (pCut)->fUsed==0 ) {} else +// iterator over leaves of the cut +#define Dar_CutForEachLeaf( p, pCut, pLeaf, i ) \ + for ( i = 0; (i < (int)(pCut)->nLeaves) && (((pLeaf) = Aig_ManObj(p, (pCut)->pLeaves[i])), 1); i++ ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== darBalance.c ========================================================*/ +/*=== darCore.c ===========================================================*/ +/*=== darCut.c ============================================================*/ +extern void Dar_ManCutsStart( Dar_Man_t * p ); +extern void Dar_ManCutsFree( Dar_Man_t * p ); +extern Dar_Cut_t * Dar_ObjComputeCuts_rec( Dar_Man_t * p, Aig_Obj_t * pObj ); +extern Dar_Cut_t * Dar_ObjComputeCuts( Dar_Man_t * p, Aig_Obj_t * pObj ); +/*=== darData.c ===========================================================*/ +extern Vec_Int_t * Dar_LibReadNodes(); +extern Vec_Int_t * Dar_LibReadOuts(); +extern Vec_Int_t * Dar_LibReadPrios(); +/*=== darLib.c ============================================================*/ +extern void Dar_LibStart(); +extern void Dar_LibStop(); +extern void Dar_LibPrepare( int nSubgraphs ); +extern void Dar_LibReturnCanonicals( unsigned * pCanons ); +extern void Dar_LibEval( Dar_Man_t * p, Aig_Obj_t * pRoot, Dar_Cut_t * pCut, int Required ); +extern Aig_Obj_t * Dar_LibBuildBest( Dar_Man_t * p ); +/*=== darMan.c ============================================================*/ +extern Dar_Man_t * Dar_ManStart( Aig_Man_t * pAig, Dar_RwrPar_t * pPars ); +extern void Dar_ManStop( Dar_Man_t * p ); +extern void Dar_ManPrintStats( Dar_Man_t * p ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/aig/dar/darLib.c b/abc_with_bb_support/src/aig/dar/darLib.c new file mode 100644 index 000000000..f5fc904c9 --- /dev/null +++ b/abc_with_bb_support/src/aig/dar/darLib.c @@ -0,0 +1,980 @@ +/**CFile**************************************************************** + + FileName [darLib.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting.] + + Synopsis [Library of AIG subgraphs used for rewriting.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: darLib.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "darInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Dar_Lib_t_ Dar_Lib_t; +typedef struct Dar_LibObj_t_ Dar_LibObj_t; +typedef struct Dar_LibDat_t_ Dar_LibDat_t; + +struct Dar_LibObj_t_ // library object (2 words) +{ + unsigned Fan0 : 16; // the first fanin + unsigned Fan1 : 16; // the second fanin + unsigned fCompl0 : 1; // the first compl attribute + unsigned fCompl1 : 1; // the second compl attribute + unsigned fPhase : 1; // the phase of the node + unsigned fTerm : 1; // indicates a PI + unsigned Num : 28; // internal use +}; + +struct Dar_LibDat_t_ // library object data +{ + Aig_Obj_t * pFunc; // the corresponding AIG node if it exists + int Level; // level of this node after it is constructured + int TravId; // traversal ID of the library object data + unsigned char fMffc; // set to one if node is part of MFFC + unsigned char nLats[3]; // the number of latches on the input/output stem +}; + +struct Dar_Lib_t_ // library +{ + // objects + Dar_LibObj_t * pObjs; // the set of library objects + int nObjs; // the number of objects used + int iObj; // the current object + // structures by class + int nSubgr[222]; // the number of subgraphs by class + int * pSubgr[222]; // the subgraphs for each class + int * pSubgrMem; // memory for subgraph pointers + int nSubgrTotal; // the total number of subgraph + // structure priorities + int * pPriosMem; // memory for priority of structures + int * pPrios[222]; // pointers to the priority numbers + // structure places in the priorities + int * pPlaceMem; // memory for places of structures in the priority lists + int * pPlace[222]; // pointers to the places numbers + // structure scores + int * pScoreMem; // memory for scores of structures + int * pScore[222]; // pointers to the scores numbers + // nodes by class + int nNodes[222]; // the number of nodes by class + int * pNodes[222]; // the nodes for each class + int * pNodesMem; // memory for nodes pointers + int nNodesTotal; // the total number of nodes + // prepared library + int nSubgraphs; + int nNodes0Max; + // nodes by class + int nNodes0[222]; // the number of nodes by class + int * pNodes0[222]; // the nodes for each class + int * pNodes0Mem; // memory for nodes pointers + int nNodes0Total; // the total number of nodes + // structures by class + int nSubgr0[222]; // the number of subgraphs by class + int * pSubgr0[222]; // the subgraphs for each class + int * pSubgr0Mem; // memory for subgraph pointers + int nSubgr0Total; // the total number of subgraph + // object data + Dar_LibDat_t * pDatas; + int nDatas; + // information about NPN classes + char ** pPerms4; + unsigned short * puCanons; + char * pPhases; + char * pPerms; + unsigned char * pMap; +}; + +static Dar_Lib_t * s_DarLib = NULL; + +static inline Dar_LibObj_t * Dar_LibObj( Dar_Lib_t * p, int Id ) { return p->pObjs + Id; } +static inline int Dar_LibObjTruth( Dar_LibObj_t * pObj ) { return pObj->Num < (0xFFFF & ~pObj->Num) ? pObj->Num : (0xFFFF & ~pObj->Num); } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dar_Lib_t * Dar_LibAlloc( int nObjs ) +{ + unsigned uTruths[4] = { 0xAAAA, 0xCCCC, 0xF0F0, 0xFF00 }; + Dar_Lib_t * p; + int i, clk = clock(); + p = ALLOC( Dar_Lib_t, 1 ); + memset( p, 0, sizeof(Dar_Lib_t) ); + // allocate objects + p->nObjs = nObjs; + p->pObjs = ALLOC( Dar_LibObj_t, nObjs ); + memset( p->pObjs, 0, sizeof(Dar_LibObj_t) * nObjs ); + // allocate canonical data + p->pPerms4 = Extra_Permutations( 4 ); + Extra_Truth4VarNPN( &p->puCanons, &p->pPhases, &p->pPerms, &p->pMap ); + // start the elementary objects + p->iObj = 4; + for ( i = 0; i < 4; i++ ) + { + p->pObjs[i].fTerm = 1; + p->pObjs[i].Num = uTruths[i]; + } +// PRT( "Library start", clock() - clk ); + return p; +} + +/**Function************************************************************* + + Synopsis [Frees the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_LibFree( Dar_Lib_t * p ) +{ + free( p->pObjs ); + free( p->pDatas ); + free( p->pNodesMem ); + free( p->pNodes0Mem ); + free( p->pSubgrMem ); + free( p->pSubgr0Mem ); + free( p->pPriosMem ); + FREE( p->pPlaceMem ); + FREE( p->pScoreMem ); + free( p->pPerms4 ); + free( p->puCanons ); + free( p->pPhases ); + free( p->pPerms ); + free( p->pMap ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Returns canonical truth tables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_LibReturnCanonicals( unsigned * pCanons ) +{ + int Visits[222] = {0}; + int i, k; + // find canonical truth tables + for ( i = k = 0; i < (1<<16); i++ ) + if ( !Visits[s_DarLib->pMap[i]] ) + { + Visits[s_DarLib->pMap[i]] = 1; + pCanons[k++] = ((i<<16) | i); + } + assert( k == 222 ); +} + +/**Function************************************************************* + + Synopsis [Adds one AND to the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_LibAddNode( Dar_Lib_t * p, int Id0, int Id1, int fCompl0, int fCompl1 ) +{ + Dar_LibObj_t * pFan0 = Dar_LibObj( p, Id0 ); + Dar_LibObj_t * pFan1 = Dar_LibObj( p, Id1 ); + Dar_LibObj_t * pObj = p->pObjs + p->iObj++; + pObj->Fan0 = Id0; + pObj->Fan1 = Id1; + pObj->fCompl0 = fCompl0; + pObj->fCompl1 = fCompl1; + pObj->fPhase = (fCompl0 ^ pFan0->fPhase) & (fCompl1 ^ pFan1->fPhase); + pObj->Num = 0xFFFF & (fCompl0? ~pFan0->Num : pFan0->Num) & (fCompl1? ~pFan1->Num : pFan1->Num); +} + +/**Function************************************************************* + + Synopsis [Adds one AND to the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_LibSetup_rec( Dar_Lib_t * p, Dar_LibObj_t * pObj, int Class, int fCollect ) +{ + if ( pObj->fTerm || (int)pObj->Num == Class ) + return; + pObj->Num = Class; + Dar_LibSetup_rec( p, Dar_LibObj(p, pObj->Fan0), Class, fCollect ); + Dar_LibSetup_rec( p, Dar_LibObj(p, pObj->Fan1), Class, fCollect ); + if ( fCollect ) + p->pNodes[Class][ p->nNodes[Class]++ ] = pObj-p->pObjs; + else + p->nNodes[Class]++; +} + +/**Function************************************************************* + + Synopsis [Adds one AND to the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_LibSetup( Dar_Lib_t * p, Vec_Int_t * vOuts, Vec_Int_t * vPrios ) +{ + int fTraining = 0; + Dar_LibObj_t * pObj; + int nNodesTotal, uTruth, Class, Out, i, k; + assert( p->iObj == p->nObjs ); + + // count the number of representatives of each class + for ( i = 0; i < 222; i++ ) + p->nSubgr[i] = p->nNodes[i] = 0; + Vec_IntForEachEntry( vOuts, Out, i ) + { + pObj = Dar_LibObj( p, Out ); + uTruth = Dar_LibObjTruth( pObj ); + Class = p->pMap[uTruth]; + p->nSubgr[Class]++; + } + // allocate memory for the roots of each class + p->pSubgrMem = ALLOC( int, Vec_IntSize(vOuts) ); + p->pSubgr0Mem = ALLOC( int, Vec_IntSize(vOuts) ); + p->nSubgrTotal = 0; + for ( i = 0; i < 222; i++ ) + { + p->pSubgr[i] = p->pSubgrMem + p->nSubgrTotal; + p->pSubgr0[i] = p->pSubgr0Mem + p->nSubgrTotal; + p->nSubgrTotal += p->nSubgr[i]; + p->nSubgr[i] = 0; + } + assert( p->nSubgrTotal == Vec_IntSize(vOuts) ); + // add the outputs to storage + Vec_IntForEachEntry( vOuts, Out, i ) + { + pObj = Dar_LibObj( p, Out ); + uTruth = Dar_LibObjTruth( pObj ); + Class = p->pMap[uTruth]; + p->pSubgr[Class][ p->nSubgr[Class]++ ] = Out; + } + + if ( fTraining ) + { + // allocate memory for the priority of roots of each class + p->pPriosMem = ALLOC( int, Vec_IntSize(vOuts) ); + p->nSubgrTotal = 0; + for ( i = 0; i < 222; i++ ) + { + p->pPrios[i] = p->pPriosMem + p->nSubgrTotal; + p->nSubgrTotal += p->nSubgr[i]; + for ( k = 0; k < p->nSubgr[i]; k++ ) + p->pPrios[i][k] = k; + + } + assert( p->nSubgrTotal == Vec_IntSize(vOuts) ); + + // allocate memory for the priority of roots of each class + p->pPlaceMem = ALLOC( int, Vec_IntSize(vOuts) ); + p->nSubgrTotal = 0; + for ( i = 0; i < 222; i++ ) + { + p->pPlace[i] = p->pPlaceMem + p->nSubgrTotal; + p->nSubgrTotal += p->nSubgr[i]; + for ( k = 0; k < p->nSubgr[i]; k++ ) + p->pPlace[i][k] = k; + + } + assert( p->nSubgrTotal == Vec_IntSize(vOuts) ); + + // allocate memory for the priority of roots of each class + p->pScoreMem = ALLOC( int, Vec_IntSize(vOuts) ); + p->nSubgrTotal = 0; + for ( i = 0; i < 222; i++ ) + { + p->pScore[i] = p->pScoreMem + p->nSubgrTotal; + p->nSubgrTotal += p->nSubgr[i]; + for ( k = 0; k < p->nSubgr[i]; k++ ) + p->pScore[i][k] = 0; + + } + assert( p->nSubgrTotal == Vec_IntSize(vOuts) ); + } + else + { + int Counter = 0; + // allocate memory for the priority of roots of each class + p->pPriosMem = ALLOC( int, Vec_IntSize(vOuts) ); + p->nSubgrTotal = 0; + for ( i = 0; i < 222; i++ ) + { + p->pPrios[i] = p->pPriosMem + p->nSubgrTotal; + p->nSubgrTotal += p->nSubgr[i]; + for ( k = 0; k < p->nSubgr[i]; k++ ) + p->pPrios[i][k] = Vec_IntEntry(vPrios, Counter++); + + } + assert( p->nSubgrTotal == Vec_IntSize(vOuts) ); + assert( Counter == Vec_IntSize(vPrios) ); + } + + // create traversal IDs + for ( i = 0; i < p->iObj; i++ ) + Dar_LibObj(p, i)->Num = 0xff; + // count nodes in each class + for ( i = 0; i < 222; i++ ) + for ( k = 0; k < p->nSubgr[i]; k++ ) + Dar_LibSetup_rec( p, Dar_LibObj(p, p->pSubgr[i][k]), i, 0 ); + // count the total number of nodes + p->nNodesTotal = 0; + for ( i = 0; i < 222; i++ ) + p->nNodesTotal += p->nNodes[i]; + // allocate memory for the nodes of each class + p->pNodesMem = ALLOC( int, p->nNodesTotal ); + p->pNodes0Mem = ALLOC( int, p->nNodesTotal ); + p->nNodesTotal = 0; + for ( i = 0; i < 222; i++ ) + { + p->pNodes[i] = p->pNodesMem + p->nNodesTotal; + p->pNodes0[i] = p->pNodes0Mem + p->nNodesTotal; + p->nNodesTotal += p->nNodes[i]; + p->nNodes[i] = 0; + } + // create traversal IDs + for ( i = 0; i < p->iObj; i++ ) + Dar_LibObj(p, i)->Num = 0xff; + // add the nodes to storage + nNodesTotal = 0; + for ( i = 0; i < 222; i++ ) + { + for ( k = 0; k < p->nSubgr[i]; k++ ) + Dar_LibSetup_rec( p, Dar_LibObj(p, p->pSubgr[i][k]), i, 1 ); + nNodesTotal += p->nNodes[i]; +//printf( "Class %3d : Subgraphs = %4d. Nodes = %5d.\n", i, p->nSubgr[i], p->nNodes[i] ); + } + assert( nNodesTotal == p->nNodesTotal ); + // prepare the number of the PI nodes + for ( i = 0; i < 4; i++ ) + Dar_LibObj(p, i)->Num = i; +} + +/**Function************************************************************* + + Synopsis [Starts the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_LibCreateData( Dar_Lib_t * p, int nDatas ) +{ + if ( p->nDatas == nDatas ) + return; + FREE( p->pDatas ); + // allocate datas + p->nDatas = nDatas; + p->pDatas = ALLOC( Dar_LibDat_t, nDatas ); + memset( p->pDatas, 0, sizeof(Dar_LibDat_t) * nDatas ); +} + +/**Function************************************************************* + + Synopsis [Adds one AND to the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_LibSetup0_rec( Dar_Lib_t * p, Dar_LibObj_t * pObj, int Class, int fCollect ) +{ + if ( pObj->fTerm || (int)pObj->Num == Class ) + return; + pObj->Num = Class; + Dar_LibSetup0_rec( p, Dar_LibObj(p, pObj->Fan0), Class, fCollect ); + Dar_LibSetup0_rec( p, Dar_LibObj(p, pObj->Fan1), Class, fCollect ); + if ( fCollect ) + p->pNodes0[Class][ p->nNodes0[Class]++ ] = pObj-p->pObjs; + else + p->nNodes0[Class]++; +} + +/**Function************************************************************* + + Synopsis [Starts the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_LibPrepare( int nSubgraphs ) +{ + Dar_Lib_t * p = s_DarLib; + int i, k, nNodes0Total; + if ( p->nSubgraphs == nSubgraphs ) + return; + + // favor special classes: + // 1 : F = (!d*!c*!b*!a) + // 4 : F = (!d*!c*!(b*a)) + // 12 : F = (!d*!(c*!(!b*!a))) + // 20 : F = (!d*!(c*b*a)) + + // set the subgraph counters + p->nSubgr0Total = 0; + for ( i = 0; i < 222; i++ ) + { +// if ( i == 1 || i == 4 || i == 12 || i == 20 ) // special classes + if ( i == 1 ) // special classes + p->nSubgr0[i] = p->nSubgr[i]; + else + p->nSubgr0[i] = AIG_MIN( p->nSubgr[i], nSubgraphs ); + p->nSubgr0Total += p->nSubgr0[i]; + for ( k = 0; k < p->nSubgr0[i]; k++ ) + p->pSubgr0[i][k] = p->pSubgr[i][ p->pPrios[i][k] ]; + } + + // count the number of nodes + // clean node counters + for ( i = 0; i < 222; i++ ) + p->nNodes0[i] = 0; + // create traversal IDs + for ( i = 0; i < p->iObj; i++ ) + Dar_LibObj(p, i)->Num = 0xff; + // count nodes in each class + // count the total number of nodes and the largest class + p->nNodes0Total = 0; + p->nNodes0Max = 0; + for ( i = 0; i < 222; i++ ) + { + for ( k = 0; k < p->nSubgr0[i]; k++ ) + Dar_LibSetup0_rec( p, Dar_LibObj(p, p->pSubgr0[i][k]), i, 0 ); + p->nNodes0Total += p->nNodes0[i]; + p->nNodes0Max = AIG_MAX( p->nNodes0Max, p->nNodes0[i] ); + } + + // clean node counters + for ( i = 0; i < 222; i++ ) + p->nNodes0[i] = 0; + // create traversal IDs + for ( i = 0; i < p->iObj; i++ ) + Dar_LibObj(p, i)->Num = 0xff; + // add the nodes to storage + nNodes0Total = 0; + for ( i = 0; i < 222; i++ ) + { + for ( k = 0; k < p->nSubgr0[i]; k++ ) + Dar_LibSetup0_rec( p, Dar_LibObj(p, p->pSubgr0[i][k]), i, 1 ); + nNodes0Total += p->nNodes0[i]; + } + assert( nNodes0Total == p->nNodes0Total ); + // prepare the number of the PI nodes + for ( i = 0; i < 4; i++ ) + Dar_LibObj(p, i)->Num = i; + + // realloc the datas + Dar_LibCreateData( p, p->nNodes0Max + 32 ); + // allocated more because Dar_LibBuildBest() sometimes requires more entries +} + +/**Function************************************************************* + + Synopsis [Reads library from array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dar_Lib_t * Dar_LibRead() +{ + Vec_Int_t * vObjs, * vOuts, * vPrios; + Dar_Lib_t * p; + int i; + // read nodes and outputs + vObjs = Dar_LibReadNodes(); + vOuts = Dar_LibReadOuts(); + vPrios = Dar_LibReadPrios(); + // create library + p = Dar_LibAlloc( Vec_IntSize(vObjs)/2 + 4 ); + // create nodes + for ( i = 0; i < vObjs->nSize; i += 2 ) + Dar_LibAddNode( p, vObjs->pArray[i] >> 1, vObjs->pArray[i+1] >> 1, + vObjs->pArray[i] & 1, vObjs->pArray[i+1] & 1 ); + // create outputs + Dar_LibSetup( p, vOuts, vPrios ); + Vec_IntFree( vObjs ); + Vec_IntFree( vOuts ); + Vec_IntFree( vPrios ); + return p; +} + +/**Function************************************************************* + + Synopsis [Starts the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_LibStart() +{ + int clk = clock(); + assert( s_DarLib == NULL ); + s_DarLib = Dar_LibRead(); + printf( "The 4-input library started with %d nodes and %d subgraphs. ", s_DarLib->nObjs - 4, s_DarLib->nSubgrTotal ); + PRT( "Time", clock() - clk ); +} + +/**Function************************************************************* + + Synopsis [Stops the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_LibStop() +{ + assert( s_DarLib != NULL ); + Dar_LibFree( s_DarLib ); + s_DarLib = NULL; +} + +/**Function************************************************************* + + Synopsis [Updates the score of the class and adjusts the priority of this class.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_LibIncrementScore( int Class, int Out, int Gain ) +{ + int * pPrios = s_DarLib->pPrios[Class]; // pPrios[i] = Out + int * pPlace = s_DarLib->pPlace[Class]; // pPlace[Out] = i + int * pScore = s_DarLib->pScore[Class]; // score of Out + int Out2; + assert( Class >= 0 && Class < 222 ); + assert( Out >= 0 && Out < s_DarLib->nSubgr[Class] ); + assert( pPlace[pPrios[Out]] == Out ); + // increment the score + pScore[Out] += Gain; + // move the out in the order + while ( pPlace[Out] > 0 && pScore[Out] > pScore[ pPrios[pPlace[Out]-1] ] ) + { + // get the previous output in the priority list + Out2 = pPrios[pPlace[Out]-1]; + // swap Out and Out2 + pPlace[Out]--; + pPlace[Out2]++; + pPrios[pPlace[Out]] = Out; + pPrios[pPlace[Out2]] = Out2; + } +} + +/**Function************************************************************* + + Synopsis [Prints out the priorities into the file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_LibDumpPriorities() +{ + int i, k, Out, Out2, Counter = 0, Printed = 0; + printf( "\nOutput priorities (total = %d):\n", s_DarLib->nSubgrTotal ); + for ( i = 0; i < 222; i++ ) + { +// printf( "Class%d: ", i ); + for ( k = 0; k < s_DarLib->nSubgr[i]; k++ ) + { + Out = s_DarLib->pPrios[i][k]; + Out2 = k == 0 ? Out : s_DarLib->pPrios[i][k-1]; + assert( s_DarLib->pScore[i][Out2] >= s_DarLib->pScore[i][Out] ); +// printf( "%d(%d), ", Out, s_DarLib->pScore[i][Out] ); + printf( "%d, ", Out ); + Printed++; + if ( ++Counter == 15 ) + { + printf( "\n" ); + Counter = 0; + } + } + } + printf( "\n" ); + assert( Printed == s_DarLib->nSubgrTotal ); +} + + +/**Function************************************************************* + + Synopsis [Matches the cut with its canonical form.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dar_LibCutMatch( Dar_Man_t * p, Dar_Cut_t * pCut ) +{ + Aig_Obj_t * pFanin; + unsigned uPhase; + char * pPerm; + int i; + assert( pCut->nLeaves == 4 ); + // get the fanin permutation + uPhase = s_DarLib->pPhases[pCut->uTruth]; + pPerm = s_DarLib->pPerms4[ s_DarLib->pPerms[pCut->uTruth] ]; + // collect fanins with the corresponding permutation/phase + for ( i = 0; i < (int)pCut->nLeaves; i++ ) + { + pFanin = Aig_ManObj( p->pAig, pCut->pLeaves[pPerm[i]] ); + if ( pFanin == NULL ) + { + p->nCutsBad++; + return 0; + } + pFanin = Aig_NotCond(pFanin, ((uPhase >> i) & 1) ); + s_DarLib->pDatas[i].pFunc = pFanin; + s_DarLib->pDatas[i].Level = Aig_Regular(pFanin)->Level; + } + p->nCutsGood++; + return 1; +} + + + +/**Function************************************************************* + + Synopsis [Marks the MFFC of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dar_LibCutMarkMffc( Aig_Man_t * p, Aig_Obj_t * pRoot, int nLeaves ) +{ + int i, nNodes; + // mark the cut leaves + for ( i = 0; i < nLeaves; i++ ) + Aig_Regular(s_DarLib->pDatas[i].pFunc)->nRefs++; + // label MFFC with current ID + nNodes = Aig_NodeMffsLabel( p, pRoot ); + // unmark the cut leaves + for ( i = 0; i < nLeaves; i++ ) + Aig_Regular(s_DarLib->pDatas[i].pFunc)->nRefs--; + return nNodes; +} + +/**Function************************************************************* + + Synopsis [Evaluates one cut.] + + Description [Returns the best gain.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_LibObjPrint_rec( Dar_LibObj_t * pObj ) +{ + if ( pObj->fTerm ) + { + printf( "%c", 'a' + pObj - s_DarLib->pObjs ); + return; + } + printf( "(" ); + Dar_LibObjPrint_rec( Dar_LibObj(s_DarLib, pObj->Fan0) ); + if ( pObj->fCompl0 ) + printf( "\'" ); + Dar_LibObjPrint_rec( Dar_LibObj(s_DarLib, pObj->Fan1) ); + if ( pObj->fCompl0 ) + printf( "\'" ); + printf( ")" ); +} + + +/**Function************************************************************* + + Synopsis [Assigns numbers to the nodes of one class.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_LibEvalAssignNums( Dar_Man_t * p, int Class ) +{ + Dar_LibObj_t * pObj; + Dar_LibDat_t * pData, * pData0, * pData1; + Aig_Obj_t * pFanin0, * pFanin1; + int i; + for ( i = 0; i < s_DarLib->nNodes0[Class]; i++ ) + { + // get one class node, assign its temporary number and set its data + pObj = Dar_LibObj(s_DarLib, s_DarLib->pNodes0[Class][i]); + pObj->Num = 4 + i; + assert( (int)pObj->Num < s_DarLib->nNodes0Max + 4 ); + pData = s_DarLib->pDatas + pObj->Num; + pData->fMffc = 0; + pData->pFunc = NULL; + pData->TravId = 0xFFFF; + + // explore the fanins + assert( (int)Dar_LibObj(s_DarLib, pObj->Fan0)->Num < s_DarLib->nNodes0Max + 4 ); + assert( (int)Dar_LibObj(s_DarLib, pObj->Fan1)->Num < s_DarLib->nNodes0Max + 4 ); + pData0 = s_DarLib->pDatas + Dar_LibObj(s_DarLib, pObj->Fan0)->Num; + pData1 = s_DarLib->pDatas + Dar_LibObj(s_DarLib, pObj->Fan1)->Num; + pData->Level = 1 + AIG_MAX(pData0->Level, pData1->Level); + if ( pData0->pFunc == NULL || pData1->pFunc == NULL ) + continue; + pFanin0 = Aig_NotCond( pData0->pFunc, pObj->fCompl0 ); + pFanin1 = Aig_NotCond( pData1->pFunc, pObj->fCompl1 ); + pData->pFunc = Aig_TableLookupTwo( p->pAig, pFanin0, pFanin1 ); + if ( pData->pFunc ) + { + // update the level to be more accurate + pData->Level = Aig_Regular(pData->pFunc)->Level; + // mark the node if it is part of MFFC + pData->fMffc = Aig_ObjIsTravIdCurrent(p->pAig, pData->pFunc); + } + } +} + +/**Function************************************************************* + + Synopsis [Evaluates one cut.] + + Description [Returns the best gain.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dar_LibEval_rec( Dar_LibObj_t * pObj, int Out, int nNodesSaved, int Required ) +{ + Dar_LibDat_t * pData; + int Area; + if ( pObj->fTerm ) + return 0; + assert( pObj->Num > 3 ); + pData = s_DarLib->pDatas + pObj->Num; + if ( pData->Level > Required ) + return 0xff; + if ( pData->pFunc && !pData->fMffc ) + return 0; + if ( pData->TravId == Out ) + return 0; + pData->TravId = Out; + // this is a new node - get a bound on the area of its branches + nNodesSaved--; + Area = Dar_LibEval_rec( Dar_LibObj(s_DarLib, pObj->Fan0), Out, nNodesSaved, Required+1 ); + if ( Area > nNodesSaved ) + return 0xff; + Area += Dar_LibEval_rec( Dar_LibObj(s_DarLib, pObj->Fan1), Out, nNodesSaved, Required+1 ); + if ( Area > nNodesSaved ) + return 0xff; + return Area + 1; +} + +/**Function************************************************************* + + Synopsis [Evaluates one cut.] + + Description [Returns the best gain.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_LibEval( Dar_Man_t * p, Aig_Obj_t * pRoot, Dar_Cut_t * pCut, int Required ) +{ + int fTraining = 0; + Dar_LibObj_t * pObj; + int Out, k, Class, nNodesSaved, nNodesAdded, nNodesGained, clk; + clk = clock(); + if ( pCut->nLeaves != 4 ) + return; + // check if the cut exits and assigns leaves and their levels + if ( !Dar_LibCutMatch(p, pCut) ) + return; + // mark MFFC of the node + nNodesSaved = Dar_LibCutMarkMffc( p->pAig, pRoot, pCut->nLeaves ); + // evaluate the cut + Class = s_DarLib->pMap[pCut->uTruth]; + Dar_LibEvalAssignNums( p, Class ); + // profile outputs by their savings + p->nTotalSubgs += s_DarLib->nSubgr0[Class]; + p->ClassSubgs[Class] += s_DarLib->nSubgr0[Class]; + for ( Out = 0; Out < s_DarLib->nSubgr0[Class]; Out++ ) + { + pObj = Dar_LibObj(s_DarLib, s_DarLib->pSubgr0[Class][Out]); + if ( Aig_Regular(s_DarLib->pDatas[pObj->Num].pFunc) == pRoot ) + continue; + nNodesAdded = Dar_LibEval_rec( pObj, Out, nNodesSaved - !p->pPars->fUseZeros, Required ); + nNodesGained = nNodesSaved - nNodesAdded; + if ( fTraining && nNodesGained >= 0 ) + Dar_LibIncrementScore( Class, Out, nNodesGained + 1 ); + if ( nNodesGained < 0 || (nNodesGained == 0 && !p->pPars->fUseZeros) ) + continue; + if ( nNodesGained < p->GainBest || + (nNodesGained == p->GainBest && s_DarLib->pDatas[pObj->Num].Level >= p->LevelBest) ) + continue; + // remember this possibility + Vec_PtrClear( p->vLeavesBest ); + for ( k = 0; k < (int)pCut->nLeaves; k++ ) + Vec_PtrPush( p->vLeavesBest, s_DarLib->pDatas[k].pFunc ); + p->OutBest = s_DarLib->pSubgr0[Class][Out]; + p->OutNumBest = Out; + p->LevelBest = s_DarLib->pDatas[pObj->Num].Level; + p->GainBest = nNodesGained; + p->ClassBest = Class; + assert( p->LevelBest <= Required ); + } +clk = clock() - clk; +p->ClassTimes[Class] += clk; +p->timeEval += clk; +} + +/**Function************************************************************* + + Synopsis [Clears the fields of the nodes used in this cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_LibBuildClear_rec( Dar_LibObj_t * pObj, int * pCounter ) +{ + if ( pObj->fTerm ) + return; + pObj->Num = (*pCounter)++; + s_DarLib->pDatas[ pObj->Num ].pFunc = NULL; + Dar_LibBuildClear_rec( Dar_LibObj(s_DarLib, pObj->Fan0), pCounter ); + Dar_LibBuildClear_rec( Dar_LibObj(s_DarLib, pObj->Fan1), pCounter ); +} + +/**Function************************************************************* + + Synopsis [Reconstructs the best cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Dar_LibBuildBest_rec( Dar_Man_t * p, Dar_LibObj_t * pObj ) +{ + Aig_Obj_t * pFanin0, * pFanin1; + Dar_LibDat_t * pData = s_DarLib->pDatas + pObj->Num; + if ( pData->pFunc ) + return pData->pFunc; + pFanin0 = Dar_LibBuildBest_rec( p, Dar_LibObj(s_DarLib, pObj->Fan0) ); + pFanin1 = Dar_LibBuildBest_rec( p, Dar_LibObj(s_DarLib, pObj->Fan1) ); + pFanin0 = Aig_NotCond( pFanin0, pObj->fCompl0 ); + pFanin1 = Aig_NotCond( pFanin1, pObj->fCompl1 ); + pData->pFunc = Aig_And( p->pAig, pFanin0, pFanin1 ); +// assert( pData->Level == (int)Aig_Regular(pData->pFunc)->Level ); + return pData->pFunc; +} + +/**Function************************************************************* + + Synopsis [Reconstructs the best cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Dar_LibBuildBest( Dar_Man_t * p ) +{ + int i, Counter = 4; + for ( i = 0; i < Vec_PtrSize(p->vLeavesBest); i++ ) + s_DarLib->pDatas[i].pFunc = Vec_PtrEntry( p->vLeavesBest, i ); + Dar_LibBuildClear_rec( Dar_LibObj(s_DarLib, p->OutBest), &Counter ); + return Dar_LibBuildBest_rec( p, Dar_LibObj(s_DarLib, p->OutBest) ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/dar/darMan.c b/abc_with_bb_support/src/aig/dar/darMan.c new file mode 100644 index 000000000..dbef4e006 --- /dev/null +++ b/abc_with_bb_support/src/aig/dar/darMan.c @@ -0,0 +1,132 @@ +/**CFile**************************************************************** + + FileName [darMan.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting.] + + Synopsis [AIG manager.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: darMan.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "darInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the rewriting manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dar_Man_t * Dar_ManStart( Aig_Man_t * pAig, Dar_RwrPar_t * pPars ) +{ + Dar_Man_t * p; + // start the manager + p = ALLOC( Dar_Man_t, 1 ); + memset( p, 0, sizeof(Dar_Man_t) ); + p->pPars = pPars; + p->pAig = pAig; + // prepare the internal memory manager + p->pMemCuts = Aig_MmFixedStart( p->pPars->nCutsMax * sizeof(Dar_Cut_t), 1024 ); + // other data + p->vLeavesBest = Vec_PtrAlloc( 4 ); + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the rewriting manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_ManStop( Dar_Man_t * p ) +{ + if ( p->pPars->fVerbose ) + Dar_ManPrintStats( p ); + if ( p->pMemCuts ) + Aig_MmFixedStop( p->pMemCuts, 0 ); + if ( p->vLeavesBest ) + Vec_PtrFree( p->vLeavesBest ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Stops the AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_ManPrintStats( Dar_Man_t * p ) +{ + unsigned pCanons[222]; + int Gain, i; + extern void Kit_DsdPrintFromTruth( unsigned * pTruth, int nVars ); + + Gain = p->nNodesInit - Aig_ManNodeNum(p->pAig); + printf( "Tried = %8d. Beg = %8d. End = %8d. Gain = %6d. (%6.2f %%). Cut mem = %d Mb\n", + p->nNodesTried, p->nNodesInit, Aig_ManNodeNum(p->pAig), Gain, 100.0*Gain/p->nNodesInit, p->nCutMemUsed ); + printf( "Cuts = %8d. Tried = %8d. Used = %8d. Bad = %5d. Skipped = %5d. Ave = %.2f.\n", + p->nCutsAll, p->nCutsTried, p->nCutsUsed, p->nCutsBad, p->nCutsSkipped, + (float)p->nCutsUsed/Aig_ManNodeNum(p->pAig) ); + + printf( "Bufs = %5d. BufMax = %5d. BufReplace = %6d. BufFix = %6d. Levels = %4d.\n", + Aig_ManBufNum(p->pAig), p->pAig->nBufMax, p->pAig->nBufReplaces, p->pAig->nBufFixes, Aig_ManLevels(p->pAig) ); + PRT( "Cuts ", p->timeCuts ); + PRT( "Eval ", p->timeEval ); + PRT( "Other ", p->timeOther ); + PRT( "TOTAL ", p->timeTotal ); + + if ( !p->pPars->fVeryVerbose ) + return; + Dar_LibReturnCanonicals( pCanons ); + for ( i = 0; i < 222; i++ ) + { + if ( p->ClassGains[i] == 0 && p->ClassTimes[i] == 0 ) + continue; + printf( "%3d : ", i ); + printf( "G = %6d (%5.2f %%) ", p->ClassGains[i], Gain? 100.0*p->ClassGains[i]/Gain : 0.0 ); + printf( "S = %8d (%5.2f %%) ", p->ClassSubgs[i], p->nTotalSubgs? 100.0*p->ClassSubgs[i]/p->nTotalSubgs : 0.0 ); + printf( "R = %7d ", p->ClassGains[i]? p->ClassSubgs[i]/p->ClassGains[i] : 9999999 ); + Kit_DsdPrintFromTruth( pCanons + i, 4 ); +// PRTP( "T", p->ClassTimes[i], p->timeEval ); + } +} + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/dar/darRefact.c b/abc_with_bb_support/src/aig/dar/darRefact.c new file mode 100644 index 000000000..715a203f2 --- /dev/null +++ b/abc_with_bb_support/src/aig/dar/darRefact.c @@ -0,0 +1,591 @@ +/**CFile**************************************************************** + + FileName [darRefact.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting.] + + Synopsis [Refactoring.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: darRefact.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "darInt.h" +#include "kit.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// the refactoring manager +typedef struct Ref_Man_t_ Ref_Man_t; +struct Ref_Man_t_ +{ + // input data + Dar_RefPar_t * pPars; // rewriting parameters + Aig_Man_t * pAig; // AIG manager + // computed cuts + Vec_Vec_t * vCuts; // the storage for cuts + // truth table and ISOP + Vec_Ptr_t * vTruthElem; // elementary truth tables + Vec_Ptr_t * vTruthStore; // storage for truth tables + Vec_Int_t * vMemory; // storage for ISOP + Vec_Ptr_t * vCutNodes; // storage for internal nodes of the cut + // various data members + Vec_Ptr_t * vLeavesBest; // the best set of leaves + Kit_Graph_t * pGraphBest; // the best factored form + int GainBest; // the best gain + int LevelBest; // the level of node with the best gain + // node statistics + int nNodesInit; // the initial number of nodes + int nNodesTried; // the number of nodes tried + int nNodesBelow; // the number of nodes below the level limit + int nNodesExten; // the number of nodes with extended cut + int nCutsUsed; // the number of rewriting steps + int nCutsTried; // the number of cuts tries + // timing statistics + int timeCuts; + int timeEval; + int timeOther; + int timeTotal; +}; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Returns the structure with default assignment of parameters.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_ManDefaultRefParams( Dar_RefPar_t * pPars ) +{ + memset( pPars, 0, sizeof(Dar_RefPar_t) ); + pPars->nMffcMin = 2; // the min MFFC size for which refactoring is used + pPars->nLeafMax = 12; // the max number of leaves of a cut + pPars->nCutsMax = 5; // the max number of cuts to consider + pPars->fUpdateLevel = 0; + pPars->fUseZeros = 0; + pPars->fVerbose = 0; + pPars->fVeryVerbose = 0; +} + +/**Function************************************************************* + + Synopsis [Starts the rewriting manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ref_Man_t * Dar_ManRefStart( Aig_Man_t * pAig, Dar_RefPar_t * pPars ) +{ + Ref_Man_t * p; + // start the manager + p = ALLOC( Ref_Man_t, 1 ); + memset( p, 0, sizeof(Ref_Man_t) ); + p->pAig = pAig; + p->pPars = pPars; + // other data + p->vCuts = Vec_VecStart( pPars->nCutsMax ); + p->vTruthElem = Vec_PtrAllocTruthTables( pPars->nLeafMax ); + p->vTruthStore = Vec_PtrAllocSimInfo( 256, Kit_TruthWordNum(pPars->nLeafMax) ); + p->vMemory = Vec_IntAlloc( 1 << 16 ); + p->vCutNodes = Vec_PtrAlloc( 256 ); + p->vLeavesBest = Vec_PtrAlloc( pPars->nLeafMax ); + return p; +} + +/**Function************************************************************* + + Synopsis [Prints out the statistics of the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_ManRefPrintStats( Ref_Man_t * p ) +{ + int Gain = p->nNodesInit - Aig_ManNodeNum(p->pAig); + printf( "NodesBeg = %8d. NodesEnd = %8d. Gain = %6d. (%6.2f %%).\n", + p->nNodesInit, Aig_ManNodeNum(p->pAig), Gain, 100.0*Gain/p->nNodesInit ); + printf( "Tried = %6d. Below = %5d. Extended = %5d. Used = %5d. Levels = %4d.\n", + p->nNodesTried, p->nNodesBelow, p->nNodesExten, p->nCutsUsed, Aig_ManLevels(p->pAig) ); + PRT( "Cuts ", p->timeCuts ); + PRT( "Eval ", p->timeEval ); + PRT( "Other ", p->timeOther ); + PRT( "TOTAL ", p->timeTotal ); +} + +/**Function************************************************************* + + Synopsis [Stops the rewriting manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dar_ManRefStop( Ref_Man_t * p ) +{ + if ( p->pPars->fVerbose ) + Dar_ManRefPrintStats( p ); + Vec_VecFree( p->vCuts ); + Vec_PtrFree( p->vTruthElem ); + Vec_PtrFree( p->vTruthStore ); + Vec_PtrFree( p->vLeavesBest ); + Vec_IntFree( p->vMemory ); + Vec_PtrFree( p->vCutNodes ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ref_ObjComputeCuts( Aig_Man_t * pAig, Aig_Obj_t * pRoot, Vec_Vec_t * vCuts ) +{ +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ref_ObjPrint( Aig_Obj_t * pObj ) +{ + printf( "%d", pObj? Aig_Regular(pObj)->Id : -1 ); + if ( pObj ) + printf( "(%d) ", Aig_IsComplement(pObj) ); +} + +/**Function************************************************************* + + Synopsis [Counts the number of new nodes added when using this graph.] + + Description [AIG nodes for the fanins should be assigned to pNode->pFunc + of the leaves of the graph before calling this procedure. + Returns -1 if the number of nodes and levels exceeded the given limit or + the number of levels exceeded the maximum allowed level.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dar_RefactTryGraph( Aig_Man_t * pAig, Aig_Obj_t * pRoot, Vec_Ptr_t * vCut, Kit_Graph_t * pGraph, int NodeMax, int LevelMax ) +{ + Kit_Node_t * pNode, * pNode0, * pNode1; + Aig_Obj_t * pAnd, * pAnd0, * pAnd1; + int i, Counter, LevelNew, LevelOld; + // check for constant function or a literal + if ( Kit_GraphIsConst(pGraph) || Kit_GraphIsVar(pGraph) ) + return 0; + // set the levels of the leaves + Kit_GraphForEachLeaf( pGraph, pNode, i ) + { + pNode->pFunc = Vec_PtrEntry(vCut, i); + pNode->Level = Aig_Regular(pNode->pFunc)->Level; + assert( Aig_Regular(pNode->pFunc)->Level < (1<<14)-1 ); + } +//printf( "Trying:\n" ); + // compute the AIG size after adding the internal nodes + Counter = 0; + Kit_GraphForEachNode( pGraph, pNode, i ) + { + // get the children of this node + pNode0 = Kit_GraphNode( pGraph, pNode->eEdge0.Node ); + pNode1 = Kit_GraphNode( pGraph, pNode->eEdge1.Node ); + // get the AIG nodes corresponding to the children + pAnd0 = pNode0->pFunc; + pAnd1 = pNode1->pFunc; + if ( pAnd0 && pAnd1 ) + { + // if they are both present, find the resulting node + pAnd0 = Aig_NotCond( pAnd0, pNode->eEdge0.fCompl ); + pAnd1 = Aig_NotCond( pAnd1, pNode->eEdge1.fCompl ); + pAnd = Aig_TableLookupTwo( pAig, pAnd0, pAnd1 ); + // return -1 if the node is the same as the original root + if ( Aig_Regular(pAnd) == pRoot ) + return -1; + } + else + pAnd = NULL; + // count the number of added nodes + if ( pAnd == NULL || Aig_ObjIsTravIdCurrent(pAig, Aig_Regular(pAnd)) ) + { + if ( ++Counter > NodeMax ) + return -1; + } + // count the number of new levels + LevelNew = 1 + AIG_MAX( pNode0->Level, pNode1->Level ); + if ( pAnd ) + { + if ( Aig_Regular(pAnd) == Aig_ManConst1(pAig) ) + LevelNew = 0; + else if ( Aig_Regular(pAnd) == Aig_Regular(pAnd0) ) + LevelNew = (int)Aig_Regular(pAnd0)->Level; + else if ( Aig_Regular(pAnd) == Aig_Regular(pAnd1) ) + LevelNew = (int)Aig_Regular(pAnd1)->Level; + LevelOld = (int)Aig_Regular(pAnd)->Level; +// assert( LevelNew == LevelOld ); + } + if ( LevelNew > LevelMax ) + return -1; + pNode->pFunc = pAnd; + pNode->Level = LevelNew; +/* +printf( "Checking " ); +Ref_ObjPrint( pAnd0 ); +printf( " and " ); +Ref_ObjPrint( pAnd1 ); +printf( " Result " ); +Ref_ObjPrint( pNode->pFunc ); +printf( "\n" ); +*/ + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t * Dar_RefactBuildGraph( Aig_Man_t * pAig, Vec_Ptr_t * vCut, Kit_Graph_t * pGraph ) +{ + Aig_Obj_t * pAnd0, * pAnd1; + Kit_Node_t * pNode; + int i; + // check for constant function + if ( Kit_GraphIsConst(pGraph) ) + return Aig_NotCond( Aig_ManConst1(pAig), Kit_GraphIsComplement(pGraph) ); + // set the leaves + Kit_GraphForEachLeaf( pGraph, pNode, i ) + pNode->pFunc = Vec_PtrEntry(vCut, i); + // check for a literal + if ( Kit_GraphIsVar(pGraph) ) + return Aig_NotCond( Kit_GraphVar(pGraph)->pFunc, Kit_GraphIsComplement(pGraph) ); + // build the AIG nodes corresponding to the AND gates of the graph +//printf( "Building (current number %d):\n", Aig_ManObjIdMax(pAig) ); + Kit_GraphForEachNode( pGraph, pNode, i ) + { + pAnd0 = Aig_NotCond( Kit_GraphNode(pGraph, pNode->eEdge0.Node)->pFunc, pNode->eEdge0.fCompl ); + pAnd1 = Aig_NotCond( Kit_GraphNode(pGraph, pNode->eEdge1.Node)->pFunc, pNode->eEdge1.fCompl ); + pNode->pFunc = Aig_And( pAig, pAnd0, pAnd1 ); +/* +printf( "Checking " ); +Ref_ObjPrint( pAnd0 ); +printf( " and " ); +Ref_ObjPrint( pAnd1 ); +printf( " Result " ); +Ref_ObjPrint( pNode->pFunc ); +printf( "\n" ); +*/ + } + // complement the result if necessary + return Aig_NotCond( pNode->pFunc, Kit_GraphIsComplement(pGraph) ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dar_ManRefactorTryCuts( Ref_Man_t * p, Aig_Obj_t * pObj, int nNodesSaved, int Required ) +{ + Vec_Ptr_t * vCut; + Kit_Graph_t * pGraphCur; + int k, RetValue, GainCur, nNodesAdded; + unsigned * pTruth; + + p->GainBest = -1; + p->pGraphBest = NULL; + Vec_VecForEachLevel( p->vCuts, vCut, k ) + { + if ( Vec_PtrSize(vCut) == 0 ) + continue; +// if ( Vec_PtrSize(vCut) != 0 && Vec_PtrSize(Vec_VecEntry(p->vCuts, k+1)) != 0 ) +// continue; + + p->nCutsTried++; + // get the cut nodes + Aig_ObjCollectCut( pObj, vCut, p->vCutNodes ); + // get the truth table + pTruth = Aig_ManCutTruth( pObj, vCut, p->vCutNodes, p->vTruthElem, p->vTruthStore ); + if ( Kit_TruthIsConst0(pTruth, Vec_PtrSize(vCut)) ) + { + p->GainBest = Vec_PtrSize(p->vCutNodes); + p->pGraphBest = Kit_GraphCreateConst0(); + Vec_PtrCopy( p->vLeavesBest, vCut ); + return p->GainBest; + } + if ( Kit_TruthIsConst1(pTruth, Vec_PtrSize(vCut)) ) + { + p->GainBest = Vec_PtrSize(p->vCutNodes); + p->pGraphBest = Kit_GraphCreateConst1(); + Vec_PtrCopy( p->vLeavesBest, vCut ); + return p->GainBest; + } + + // try the positive phase + RetValue = Kit_TruthIsop( pTruth, Vec_PtrSize(vCut), p->vMemory, 0 ); + if ( RetValue > -1 ) + { + pGraphCur = Kit_SopFactor( p->vMemory, 0, Vec_PtrSize(vCut), p->vMemory ); + nNodesAdded = Dar_RefactTryGraph( p->pAig, pObj, vCut, pGraphCur, nNodesSaved - !p->pPars->fUseZeros, Required ); + if ( nNodesAdded > -1 ) + { + GainCur = nNodesSaved - nNodesAdded; + if ( p->GainBest < GainCur || (p->GainBest == GainCur && + (Kit_GraphIsConst(pGraphCur) || Kit_GraphRootLevel(pGraphCur) < Kit_GraphRootLevel(p->pGraphBest))) ) + { + p->GainBest = GainCur; + if ( p->pGraphBest ) + Kit_GraphFree( p->pGraphBest ); + p->pGraphBest = pGraphCur; + Vec_PtrCopy( p->vLeavesBest, vCut ); + } + else + Kit_GraphFree( pGraphCur ); + } + else + Kit_GraphFree( pGraphCur ); + } + // try negative phase + Kit_TruthNot( pTruth, pTruth, Vec_PtrSize(vCut) ); + RetValue = Kit_TruthIsop( pTruth, Vec_PtrSize(vCut), p->vMemory, 0 ); + if ( RetValue > -1 ) + { + pGraphCur = Kit_SopFactor( p->vMemory, 1, Vec_PtrSize(vCut), p->vMemory ); + nNodesAdded = Dar_RefactTryGraph( p->pAig, pObj, vCut, pGraphCur, nNodesSaved - !p->pPars->fUseZeros, Required ); + if ( nNodesAdded > -1 ) + { + GainCur = nNodesSaved - nNodesAdded; + if ( p->GainBest < GainCur || (p->GainBest == GainCur && + (Kit_GraphIsConst(pGraphCur) || Kit_GraphRootLevel(pGraphCur) < Kit_GraphRootLevel(p->pGraphBest))) ) + { + p->GainBest = GainCur; + if ( p->pGraphBest ) + Kit_GraphFree( p->pGraphBest ); + p->pGraphBest = pGraphCur; + Vec_PtrCopy( p->vLeavesBest, vCut ); + } + else + Kit_GraphFree( pGraphCur ); + } + else + Kit_GraphFree( pGraphCur ); + } + } + return p->GainBest; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if a non-PI node has nLevelMin or below.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dar_ObjCutLevelAchieved( Vec_Ptr_t * vCut, int nLevelMin ) +{ + Aig_Obj_t * pObj; + int i; + Vec_PtrForEachEntry( vCut, pObj, i ) + if ( !Aig_ObjIsPi(pObj) && (int)pObj->Level <= nLevelMin ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dar_ManRefactor( Aig_Man_t * pAig, Dar_RefPar_t * pPars ) +{ + ProgressBar * pProgress; + Ref_Man_t * p; + Vec_Ptr_t * vCut, * vCut2; + Aig_Obj_t * pObj, * pObjNew; + int nNodesOld, nNodeBefore, nNodeAfter, nNodesSaved, nNodesSaved2; + int i, Required, nLevelMin, clkStart, clk; + + // start the manager + p = Dar_ManRefStart( pAig, pPars ); + // remove dangling nodes + Aig_ManCleanup( pAig ); + // if updating levels is requested, start fanout and timing + Aig_ManFanoutStart( pAig ); + if ( p->pPars->fUpdateLevel ) + Aig_ManStartReverseLevels( pAig, 0 ); + + // resynthesize each node once + clkStart = clock(); + vCut = Vec_VecEntry( p->vCuts, 0 ); + vCut2 = Vec_VecEntry( p->vCuts, 1 ); + p->nNodesInit = Aig_ManNodeNum(pAig); + nNodesOld = Vec_PtrSize( pAig->vObjs ); + pProgress = Extra_ProgressBarStart( stdout, nNodesOld ); + Aig_ManForEachObj( pAig, pObj, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + if ( !Aig_ObjIsNode(pObj) ) + continue; + if ( i > nNodesOld ) + break; + Vec_VecClear( p->vCuts ); + +//printf( "\nConsidering node %d.\n", pObj->Id ); + // get the bounded MFFC size +clk = clock(); + nLevelMin = AIG_MAX( 0, Aig_ObjLevel(pObj) - 10 ); + nNodesSaved = Aig_NodeMffsSupp( pAig, pObj, nLevelMin, vCut ); + if ( nNodesSaved < p->pPars->nMffcMin ) // too small to consider + { +p->timeCuts += clock() - clk; + continue; + } + p->nNodesTried++; + if ( Vec_PtrSize(vCut) > p->pPars->nLeafMax ) // get one reconv-driven cut + { + Aig_ManFindCut( pObj, vCut, p->vCutNodes, p->pPars->nLeafMax, 50 ); + nNodesSaved = Aig_NodeMffsLabelCut( p->pAig, pObj, vCut ); + } + else if ( Vec_PtrSize(vCut) < p->pPars->nLeafMax - 2 && p->pPars->fExtend ) + { + if ( !Dar_ObjCutLevelAchieved(vCut, nLevelMin) ) + { + if ( Aig_NodeMffsExtendCut( pAig, pObj, vCut, vCut2 ) ) + { + nNodesSaved2 = Aig_NodeMffsLabelCut( p->pAig, pObj, vCut ); + assert( nNodesSaved2 == nNodesSaved ); + } + if ( Vec_PtrSize(vCut2) > p->pPars->nLeafMax ) + Vec_PtrClear(vCut2); + if ( Vec_PtrSize(vCut2) > 0 ) + { + p->nNodesExten++; +// printf( "%d(%d) ", Vec_PtrSize(vCut), Vec_PtrSize(vCut2) ); + } + } + else + p->nNodesBelow++; + } +p->timeCuts += clock() - clk; + + // try the cuts +clk = clock(); + Required = pAig->vLevelR? Aig_ObjRequiredLevel(pAig, pObj) : AIG_INFINITY; + Dar_ManRefactorTryCuts( p, pObj, nNodesSaved, Required ); +p->timeEval += clock() - clk; + + // check the best gain + if ( !(p->GainBest > 0 || (p->GainBest == 0 && p->pPars->fUseZeros)) ) + { + if ( p->pGraphBest ) + Kit_GraphFree( p->pGraphBest ); + continue; + } +//printf( "\n" ); + + // if we end up here, a rewriting step is accepted + nNodeBefore = Aig_ManNodeNum( pAig ); + pObjNew = Dar_RefactBuildGraph( pAig, p->vLeavesBest, p->pGraphBest ); + assert( (int)Aig_Regular(pObjNew)->Level <= Required ); + // replace the node + Aig_ObjReplace( pAig, pObj, pObjNew, 1, p->pPars->fUpdateLevel ); + // compare the gains + nNodeAfter = Aig_ManNodeNum( pAig ); + assert( p->GainBest <= nNodeBefore - nNodeAfter ); + Kit_GraphFree( p->pGraphBest ); + p->nCutsUsed++; +// break; + } +p->timeTotal = clock() - clkStart; +p->timeOther = p->timeTotal - p->timeCuts - p->timeEval; + + Extra_ProgressBarStop( pProgress ); + // put the nodes into the DFS order and reassign their IDs +// Aig_NtkReassignIds( p ); + // fix the levels + Aig_ManFanoutStop( pAig ); + if ( p->pPars->fUpdateLevel ) + Aig_ManStopReverseLevels( pAig ); + + // stop the rewriting manager + Dar_ManRefStop( p ); + Aig_ManCheckPhase( pAig ); + if ( !Aig_ManCheck( pAig ) ) + { + printf( "Dar_ManRefactor: The network check has failed.\n" ); + return 0; + } + return 1; + +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/dar/darResub.c b/abc_with_bb_support/src/aig/dar/darResub.c new file mode 100644 index 000000000..9975d9448 --- /dev/null +++ b/abc_with_bb_support/src/aig/dar/darResub.c @@ -0,0 +1,48 @@ +/**CFile**************************************************************** + + FileName [darResub.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: darResub.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "darInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/dar/darScript.c b/abc_with_bb_support/src/aig/dar/darScript.c new file mode 100644 index 000000000..a34a40268 --- /dev/null +++ b/abc_with_bb_support/src/aig/dar/darScript.c @@ -0,0 +1,204 @@ +/**CFile**************************************************************** + + FileName [darScript.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting.] + + Synopsis [Rewriting scripts.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: darScript.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "darInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs one iteration of AIG rewriting.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Man_t * Dar_ManRewriteDefault( Aig_Man_t * pAig ) +{ + Aig_Man_t * pTemp; + Dar_RwrPar_t Pars, * pPars = &Pars; + Dar_ManDefaultRwrParams( pPars ); + pAig = Aig_ManDup( pTemp = pAig, 0 ); + Aig_ManStop( pTemp ); + Dar_ManRewrite( pAig, pPars ); + pAig = Aig_ManDup( pTemp = pAig, 0 ); + Aig_ManStop( pTemp ); + return pAig; +} + +/**Function************************************************************* + + Synopsis [Reproduces script "compress2".] + + Description [] + + SideEffects [This procedure does not tighten level during restructuring.] + + SeeAlso [] + +***********************************************************************/ +Aig_Man_t * Dar_ManCompress2( Aig_Man_t * pAig, int fBalance, int fUpdateLevel, int fVerbose ) +//alias compress2 "b -l; rw -l; rf -l; b -l; rw -l; rwz -l; b -l; rfz -l; rwz -l; b -l" +{ + Aig_Man_t * pTemp; + + Dar_RwrPar_t ParsRwr, * pParsRwr = &ParsRwr; + Dar_RefPar_t ParsRef, * pParsRef = &ParsRef; + + Dar_ManDefaultRwrParams( pParsRwr ); + Dar_ManDefaultRefParams( pParsRef ); + + pParsRwr->fUpdateLevel = fUpdateLevel; + pParsRef->fUpdateLevel = fUpdateLevel; + + pParsRwr->fVerbose = fVerbose; + pParsRef->fVerbose = fVerbose; + + // balance + if ( fBalance ) + { +// pAig = Dar_ManBalance( pTemp = pAig, fUpdateLevel ); +// Aig_ManStop( pTemp ); + } + + // rewrite + Dar_ManRewrite( pAig, pParsRwr ); + pAig = Aig_ManDup( pTemp = pAig, 0 ); + Aig_ManStop( pTemp ); + + // refactor + Dar_ManRefactor( pAig, pParsRef ); + pAig = Aig_ManDup( pTemp = pAig, 0 ); + Aig_ManStop( pTemp ); + + // balance +// if ( fBalance ) + { + pAig = Dar_ManBalance( pTemp = pAig, fUpdateLevel ); + Aig_ManStop( pTemp ); + } + + // rewrite + Dar_ManRewrite( pAig, pParsRwr ); + pAig = Aig_ManDup( pTemp = pAig, 0 ); + Aig_ManStop( pTemp ); + + pParsRwr->fUseZeros = 1; + pParsRef->fUseZeros = 1; + + // rewrite + Dar_ManRewrite( pAig, pParsRwr ); + pAig = Aig_ManDup( pTemp = pAig, 0 ); + Aig_ManStop( pTemp ); + + // balance + if ( fBalance ) + { + pAig = Dar_ManBalance( pTemp = pAig, fUpdateLevel ); + Aig_ManStop( pTemp ); + } + + // refactor + Dar_ManRefactor( pAig, pParsRef ); + pAig = Aig_ManDup( pTemp = pAig, 0 ); + Aig_ManStop( pTemp ); + + // rewrite + Dar_ManRewrite( pAig, pParsRwr ); + pAig = Aig_ManDup( pTemp = pAig, 0 ); + Aig_ManStop( pTemp ); + + // balance + if ( fBalance ) + { + pAig = Dar_ManBalance( pTemp = pAig, fUpdateLevel ); + Aig_ManStop( pTemp ); + } + return pAig; +} + +/**Function************************************************************* + + Synopsis [Reproduces script "compress2".] + + Description [] + + SideEffects [This procedure does not tighten level during restructuring.] + + SeeAlso [] + +***********************************************************************/ +Aig_Man_t * Dar_ManRwsat( Aig_Man_t * pAig, int fBalance, int fVerbose ) +//alias rwsat "st; rw -l; b -l; rw -l; rf -l" +{ + Aig_Man_t * pTemp; + + Dar_RwrPar_t ParsRwr, * pParsRwr = &ParsRwr; + Dar_RefPar_t ParsRef, * pParsRef = &ParsRef; + + Dar_ManDefaultRwrParams( pParsRwr ); + Dar_ManDefaultRefParams( pParsRef ); + + pParsRwr->fUpdateLevel = 0; + pParsRef->fUpdateLevel = 0; + + pParsRwr->fVerbose = fVerbose; + pParsRef->fVerbose = fVerbose; + + // rewrite + Dar_ManRewrite( pAig, pParsRwr ); + pAig = Aig_ManDup( pTemp = pAig, 0 ); + Aig_ManStop( pTemp ); + + // refactor + Dar_ManRefactor( pAig, pParsRef ); + pAig = Aig_ManDup( pTemp = pAig, 0 ); + Aig_ManStop( pTemp ); + + // balance + if ( fBalance ) + { + pAig = Dar_ManBalance( pTemp = pAig, 0 ); + Aig_ManStop( pTemp ); + } + + // rewrite + Dar_ManRewrite( pAig, pParsRwr ); + pAig = Aig_ManDup( pTemp = pAig, 0 ); + Aig_ManStop( pTemp ); + + return pAig; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/dar/darTruth.c b/abc_with_bb_support/src/aig/dar/darTruth.c new file mode 100644 index 000000000..7255b4b10 --- /dev/null +++ b/abc_with_bb_support/src/aig/dar/darTruth.c @@ -0,0 +1,353 @@ +/**CFile**************************************************************** + + FileName [darTruth.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting.] + + Synopsis [Computes the truth table of a cut.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: darTruth.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "darInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#if 0 + +/**Function************************************************************* + + Synopsis [Computes truth table of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManCollectCut_rec( Aig_Man_t * p, Aig_Obj_t * pNode, Vec_Int_t * vNodes ) +{ + if ( pNode->fMarkA ) + return; + pNode->fMarkA = 1; + assert( Aig_ObjIsAnd(pNode) || Aig_ObjIsExor(pNode) ); + Aig_ManCollectCut_rec( p, Aig_ObjFanin0(pNode), vNodes ); + Aig_ManCollectCut_rec( p, Aig_ObjFanin1(pNode), vNodes ); + Vec_IntPush( vNodes, pNode->Id ); +} + +/**Function************************************************************* + + Synopsis [Computes truth table of the cut.] + + Description [Does not modify the array of leaves. Uses array vTruth to store + temporary truth tables. The returned pointer should be used immediately.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManCollectCut( Aig_Man_t * p, Aig_Obj_t * pRoot, Vec_Int_t * vLeaves, Vec_Int_t * vNodes ) +{ + int i, Leaf; + // collect and mark the leaves + Vec_IntClear( vNodes ); + Vec_IntForEachEntry( vLeaves, Leaf, i ) + { + Vec_IntPush( vNodes, Leaf ); + Aig_ManObj(p, Leaf)->fMarkA = 1; + } + // collect and mark the nodes + Aig_ManCollectCut_rec( p, pRoot, vNodes ); + // clean the nodes + Vec_IntForEachEntry( vNodes, Leaf, i ) + Aig_ManObj(p, Leaf)->fMarkA = 0; +} + +/**Function************************************************************* + + Synopsis [Returns the pointer to the truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned * Aig_ObjGetTruthStore( int ObjNum, Vec_Int_t * vTruth ) +{ + return ((unsigned *)Vec_IntArray(vTruth)) + 8 * ObjNum; +} + +/**Function************************************************************* + + Synopsis [Computes truth table of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManCutTruthOne( Aig_Man_t * p, Aig_Obj_t * pNode, Vec_Int_t * vTruth, int nWords ) +{ + unsigned * pTruth, * pTruth0, * pTruth1; + int i; + pTruth = Aig_ObjGetTruthStore( pNode->Level, vTruth ); + pTruth0 = Aig_ObjGetTruthStore( Aig_ObjFanin0(pNode)->Level, vTruth ); + pTruth1 = Aig_ObjGetTruthStore( Aig_ObjFanin1(pNode)->Level, vTruth ); + if ( Aig_ObjIsExor(pNode) ) + for ( i = 0; i < nWords; i++ ) + pTruth[i] = pTruth0[i] ^ pTruth1[i]; + else if ( !Aig_ObjFaninC0(pNode) && !Aig_ObjFaninC1(pNode) ) + for ( i = 0; i < nWords; i++ ) + pTruth[i] = pTruth0[i] & pTruth1[i]; + else if ( !Aig_ObjFaninC0(pNode) && Aig_ObjFaninC1(pNode) ) + for ( i = 0; i < nWords; i++ ) + pTruth[i] = pTruth0[i] & ~pTruth1[i]; + else if ( Aig_ObjFaninC0(pNode) && !Aig_ObjFaninC1(pNode) ) + for ( i = 0; i < nWords; i++ ) + pTruth[i] = ~pTruth0[i] & pTruth1[i]; + else // if ( Aig_ObjFaninC0(pNode) && Aig_ObjFaninC1(pNode) ) + for ( i = 0; i < nWords; i++ ) + pTruth[i] = ~pTruth0[i] & ~pTruth1[i]; +} + +/**Function************************************************************* + + Synopsis [Computes truth table of the cut.] + + Description [Does not modify the array of leaves. Uses array vTruth to store + temporary truth tables. The returned pointer should be used immediately.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned * Aig_ManCutTruth( Aig_Man_t * p, Aig_Obj_t * pRoot, Vec_Int_t * vLeaves, Vec_Int_t * vNodes, Vec_Int_t * vTruth ) +{ + static unsigned uTruths[8][8] = { // elementary truth tables + { 0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA }, + { 0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC }, + { 0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0 }, + { 0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00 }, + { 0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000 }, + { 0x00000000,0xFFFFFFFF,0x00000000,0xFFFFFFFF,0x00000000,0xFFFFFFFF,0x00000000,0xFFFFFFFF }, + { 0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF,0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF }, + { 0x00000000,0x00000000,0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF } + }; + int i, Leaf; + // collect the cut +// Aig_ManCollectCut( p, pRoot, vLeaves, vNodes ); + // set the node numbers + Vec_IntForEachEntry( vNodes, Leaf, i ) + Aig_ManObj(p, Leaf)->Level = i; + // alloc enough memory + Vec_IntClear( vTruth ); + Vec_IntGrow( vTruth, 8 * Vec_IntSize(vNodes) ); + // set the elementary truth tables + Vec_IntForEachEntry( vLeaves, Leaf, i ) + memcpy( Aig_ObjGetTruthStore(i, vTruth), uTruths[i], 8 * sizeof(unsigned) ); + // compute truths for other nodes + Vec_IntForEachEntryStart( vNodes, Leaf, i, Vec_IntSize(vLeaves) ) + Aig_ManCutTruthOne( p, Aig_ManObj(p, Leaf), vTruth, 8 ); + return Aig_ObjGetTruthStore( pRoot->Level, vTruth ); +} + +static inline int Kit_TruthWordNum( int nVars ) { return nVars <= 5 ? 1 : (1 << (nVars - 5)); } +static inline void Kit_TruthNot( unsigned * pOut, unsigned * pIn, int nVars ) +{ + int w; + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = ~pIn[w]; +} + +/**Function************************************************************* + + Synopsis [Computes the cost based on two ISOPs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ManLargeCutEvalIsop( unsigned * pTruth, int nVars, Vec_Int_t * vMemory ) +{ + extern int Kit_TruthIsop( unsigned * puTruth, int nVars, Vec_Int_t * vMemory, int fTryBoth ); + int RetValue, nClauses; + // compute ISOP for the positive phase + RetValue = Kit_TruthIsop( pTruth, nVars, vMemory, 0 ); + if ( RetValue == -1 ) + return AIG_INFINITY; + assert( RetValue == 0 || RetValue == 1 ); + nClauses = Vec_IntSize( vMemory ); + // compute ISOP for the negative phase + Kit_TruthNot( pTruth, pTruth, nVars ); + RetValue = Kit_TruthIsop( pTruth, nVars, vMemory, 0 ); + if ( RetValue == -1 ) + return AIG_INFINITY; + Kit_TruthNot( pTruth, pTruth, nVars ); + assert( RetValue == 0 || RetValue == 1 ); + nClauses += Vec_IntSize( vMemory ); + return nClauses; +} + +/**Function************************************************************* + + Synopsis [Computes truth table of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManLargeCutCollect_rec( Aig_Man_t * p, Aig_Obj_t * pNode, Vec_Int_t * vLeaves, Vec_Int_t * vNodes ) +{ + if ( Aig_ObjIsTravIdCurrent(p, pNode) ) + return; + if ( Aig_ObjIsTravIdPrevious(p, pNode) ) + { + Vec_IntPush( vLeaves, pNode->Id ); +// Vec_IntPush( vNodes, pNode->Id ); + Aig_ObjSetTravIdCurrent( p, pNode ); + return; + } + assert( Aig_ObjIsAnd(pNode) || Aig_ObjIsExor(pNode) ); + Aig_ObjSetTravIdCurrent( p, pNode ); + Aig_ManLargeCutCollect_rec( p, Aig_ObjFanin0(pNode), vLeaves, vNodes ); + Aig_ManLargeCutCollect_rec( p, Aig_ObjFanin1(pNode), vLeaves, vNodes ); + Vec_IntPush( vNodes, pNode->Id ); +} + +/**Function************************************************************* + + Synopsis [Collect leaves and nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Aig_ManLargeCutCollect( Aig_Man_t * p, Aig_Obj_t * pRoot, Aig_Cut_t * pCutR, Aig_Cut_t * pCutL, int Leaf, + Vec_Int_t * vLeaves, Vec_Int_t * vNodes ) +{ + Vec_Int_t * vTemp; + Aig_Obj_t * pObj; + int Node, i; + + Aig_ManIncrementTravId( p ); + Aig_CutForEachLeaf( p, pCutR, pObj, i ) + if ( pObj->Id != Leaf ) + Aig_ObjSetTravIdCurrent( p, pObj ); + Aig_CutForEachLeaf( p, pCutL, pObj, i ) + Aig_ObjSetTravIdCurrent( p, pObj ); + + // collect the internal nodes and leaves + Aig_ManIncrementTravId( p ); + vTemp = Vec_IntAlloc( 100 ); + Aig_ManLargeCutCollect_rec( p, pRoot, vLeaves, vTemp ); + + Vec_IntForEachEntry( vLeaves, Node, i ) + Vec_IntPush( vNodes, Node ); + Vec_IntForEachEntry( vTemp, Node, i ) + Vec_IntPush( vNodes, Node ); + + Vec_IntFree( vTemp ); + +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Aig_ManLargeCutEval( Aig_Man_t * p, Aig_Obj_t * pRoot, Aig_Cut_t * pCutR, Aig_Cut_t * pCutL, int Leaf ) +{ + Vec_Int_t * vLeaves, * vNodes, * vTruth, * vMemory; + unsigned * pTruth; + int RetValue; +// Aig_Obj_t * pObj; + + vMemory = Vec_IntAlloc( 1 << 16 ); + vTruth = Vec_IntAlloc( 1 << 16 ); + vLeaves = Vec_IntAlloc( 100 ); + vNodes = Vec_IntAlloc( 100 ); + + Aig_ManLargeCutCollect( p, pRoot, pCutR, pCutL, Leaf, vLeaves, vNodes ); +/* + // collect the nodes + Aig_CutForEachLeaf( p, pCutR, pObj, i ) + { + if ( pObj->Id == Leaf ) + continue; + if ( pObj->fMarkA ) + continue; + pObj->fMarkA = 1; + Vec_IntPush( vLeaves, pObj->Id ); + Vec_IntPush( vNodes, pObj->Id ); + } + Aig_CutForEachLeaf( p, pCutL, pObj, i ) + { + if ( pObj->fMarkA ) + continue; + pObj->fMarkA = 1; + Vec_IntPush( vLeaves, pObj->Id ); + Vec_IntPush( vNodes, pObj->Id ); + } + // collect and mark the nodes + Aig_ManCollectCut_rec( p, pRoot, vNodes ); + // clean the nodes + Vec_IntForEachEntry( vNodes, Leaf, i ) + Aig_ManObj(p, Leaf)->fMarkA = 0; +*/ + + pTruth = Aig_ManCutTruth( p, pRoot, vLeaves, vNodes, vTruth ); + RetValue = Aig_ManLargeCutEvalIsop( pTruth, Vec_IntSize(vLeaves), vMemory ); + + Vec_IntFree( vLeaves ); + Vec_IntFree( vNodes ); + Vec_IntFree( vTruth ); + Vec_IntFree( vMemory ); + + return RetValue; +} + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/dar/dar_.c b/abc_with_bb_support/src/aig/dar/dar_.c new file mode 100644 index 000000000..de2fc7ad0 --- /dev/null +++ b/abc_with_bb_support/src/aig/dar/dar_.c @@ -0,0 +1,48 @@ +/**CFile**************************************************************** + + FileName [dar_.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: dar_.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "darInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/dar/module.make b/abc_with_bb_support/src/aig/dar/module.make new file mode 100644 index 000000000..eaf25192d --- /dev/null +++ b/abc_with_bb_support/src/aig/dar/module.make @@ -0,0 +1,10 @@ +SRC += src/aig/dar/darBalance.c \ + src/aig/dar/darCore.c \ + src/aig/dar/darCut.c \ + src/aig/dar/darData.c \ + src/aig/dar/darLib.c \ + src/aig/dar/darMan.c \ + src/aig/dar/darRefact.c \ + src/aig/dar/darResub.c \ + src/aig/dar/darScript.c \ + src/aig/dar/darTruth.c diff --git a/abc_with_bb_support/src/aig/deco/deco.h b/abc_with_bb_support/src/aig/deco/deco.h new file mode 100644 index 000000000..50b7250c7 --- /dev/null +++ b/abc_with_bb_support/src/aig/deco/deco.h @@ -0,0 +1,703 @@ +/**CFile**************************************************************** + + FileName [deco.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [A simple decomposition tree/node data structure and its APIs.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: deco.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __DEC_H__ +#define __DEC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Dec_Edge_t_ Dec_Edge_t; +struct Dec_Edge_t_ +{ + unsigned fCompl : 1; // the complemented bit + unsigned Node : 30; // the decomposition node pointed by the edge +}; + +typedef struct Dec_Node_t_ Dec_Node_t; +struct Dec_Node_t_ +{ + Dec_Edge_t eEdge0; // the left child of the node + Dec_Edge_t eEdge1; // the right child of the node + // other info + void * pFunc; // the function of the node (BDD or AIG) + unsigned Level : 14; // the level of this node in the global AIG + // printing info + unsigned fNodeOr : 1; // marks the original OR node + unsigned fCompl0 : 1; // marks the original complemented edge + unsigned fCompl1 : 1; // marks the original complemented edge + // latch info + unsigned nLat0 : 5; // the number of latches on the first edge + unsigned nLat1 : 5; // the number of latches on the second edge + unsigned nLat2 : 5; // the number of latches on the output edge +}; + +typedef struct Dec_Graph_t_ Dec_Graph_t; +struct Dec_Graph_t_ +{ + int fConst; // marks the constant 1 graph + int nLeaves; // the number of leaves + int nSize; // the number of nodes (including the leaves) + int nCap; // the number of allocated nodes + Dec_Node_t * pNodes; // the array of leaves and internal nodes + Dec_Edge_t eRoot; // the pointer to the topmost node +}; + +typedef struct Dec_Man_t_ Dec_Man_t; +struct Dec_Man_t_ +{ + void * pMvcMem; // memory manager for MVC cover (used for factoring) + Vec_Int_t * vCubes; // storage for cubes + Vec_Int_t * vLits; // storage for literals + // precomputation information about 4-variable functions + unsigned short * puCanons; // canonical forms + char * pPhases; // canonical phases + char * pPerms; // canonical permutations + unsigned char * pMap; // mapping of functions into class numbers +}; + + +//////////////////////////////////////////////////////////////////////// +/// ITERATORS /// +//////////////////////////////////////////////////////////////////////// + +// interator throught the leaves +#define Dec_GraphForEachLeaf( pGraph, pLeaf, i ) \ + for ( i = 0; (i < (pGraph)->nLeaves) && (((pLeaf) = Dec_GraphNode(pGraph, i)), 1); i++ ) +// interator throught the internal nodes +#define Dec_GraphForEachNode( pGraph, pAnd, i ) \ + for ( i = (pGraph)->nLeaves; (i < (pGraph)->nSize) && (((pAnd) = Dec_GraphNode(pGraph, i)), 1); i++ ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Creates an edge pointing to the node in the given polarity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Edge_t Dec_EdgeCreate( int Node, int fCompl ) +{ + Dec_Edge_t eEdge = { fCompl, Node }; + return eEdge; +} + +/**Function************************************************************* + + Synopsis [Converts the edge into unsigned integer.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Dec_EdgeToInt( Dec_Edge_t eEdge ) +{ + return (eEdge.Node << 1) | eEdge.fCompl; +} + +/**Function************************************************************* + + Synopsis [Converts unsigned integer into the edge.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Edge_t Dec_IntToEdge( unsigned Edge ) +{ + return Dec_EdgeCreate( Edge >> 1, Edge & 1 ); +} + +/**Function************************************************************* + + Synopsis [Converts the edge into unsigned integer.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Dec_EdgeToInt_( Dec_Edge_t eEdge ) +{ + return *(unsigned *)&eEdge; +} + +/**Function************************************************************* + + Synopsis [Converts unsigned integer into the edge.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Edge_t Dec_IntToEdge_( unsigned Edge ) +{ + return *(Dec_Edge_t *)&Edge; +} + +/**Function************************************************************* + + Synopsis [Creates a graph with the given number of leaves.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Graph_t * Dec_GraphCreate( int nLeaves ) +{ + Dec_Graph_t * pGraph; + pGraph = ALLOC( Dec_Graph_t, 1 ); + memset( pGraph, 0, sizeof(Dec_Graph_t) ); + pGraph->nLeaves = nLeaves; + pGraph->nSize = nLeaves; + pGraph->nCap = 2 * nLeaves + 50; + pGraph->pNodes = ALLOC( Dec_Node_t, pGraph->nCap ); + memset( pGraph->pNodes, 0, sizeof(Dec_Node_t) * pGraph->nSize ); + return pGraph; +} + +/**Function************************************************************* + + Synopsis [Creates constant 0 graph.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Graph_t * Dec_GraphCreateConst0() +{ + Dec_Graph_t * pGraph; + pGraph = ALLOC( Dec_Graph_t, 1 ); + memset( pGraph, 0, sizeof(Dec_Graph_t) ); + pGraph->fConst = 1; + pGraph->eRoot.fCompl = 1; + return pGraph; +} + +/**Function************************************************************* + + Synopsis [Creates constant 1 graph.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Graph_t * Dec_GraphCreateConst1() +{ + Dec_Graph_t * pGraph; + pGraph = ALLOC( Dec_Graph_t, 1 ); + memset( pGraph, 0, sizeof(Dec_Graph_t) ); + pGraph->fConst = 1; + return pGraph; +} + +/**Function************************************************************* + + Synopsis [Creates the literal graph.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Graph_t * Dec_GraphCreateLeaf( int iLeaf, int nLeaves, int fCompl ) +{ + Dec_Graph_t * pGraph; + assert( 0 <= iLeaf && iLeaf < nLeaves ); + pGraph = Dec_GraphCreate( nLeaves ); + pGraph->eRoot.Node = iLeaf; + pGraph->eRoot.fCompl = fCompl; + return pGraph; +} + +/**Function************************************************************* + + Synopsis [Creates a graph with the given number of leaves.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Dec_GraphFree( Dec_Graph_t * pGraph ) +{ + FREE( pGraph->pNodes ); + free( pGraph ); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the graph is a constant.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Dec_GraphIsConst( Dec_Graph_t * pGraph ) +{ + return pGraph->fConst; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the graph is constant 0.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Dec_GraphIsConst0( Dec_Graph_t * pGraph ) +{ + return pGraph->fConst && pGraph->eRoot.fCompl; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the graph is constant 1.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Dec_GraphIsConst1( Dec_Graph_t * pGraph ) +{ + return pGraph->fConst && !pGraph->eRoot.fCompl; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the graph is complemented.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Dec_GraphIsComplement( Dec_Graph_t * pGraph ) +{ + return pGraph->eRoot.fCompl; +} + +/**Function************************************************************* + + Synopsis [Checks if the graph is complemented.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Dec_GraphComplement( Dec_Graph_t * pGraph ) +{ + pGraph->eRoot.fCompl ^= 1; +} + + +/**Function************************************************************* + + Synopsis [Returns the number of leaves.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Dec_GraphLeaveNum( Dec_Graph_t * pGraph ) +{ + return pGraph->nLeaves; +} + +/**Function************************************************************* + + Synopsis [Returns the number of internal nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Dec_GraphNodeNum( Dec_Graph_t * pGraph ) +{ + return pGraph->nSize - pGraph->nLeaves; +} + +/**Function************************************************************* + + Synopsis [Returns the pointer to the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Node_t * Dec_GraphNode( Dec_Graph_t * pGraph, int i ) +{ + return pGraph->pNodes + i; +} + +/**Function************************************************************* + + Synopsis [Returns the pointer to the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Node_t * Dec_GraphNodeLast( Dec_Graph_t * pGraph ) +{ + return pGraph->pNodes + pGraph->nSize - 1; +} + +/**Function************************************************************* + + Synopsis [Returns the number of the given node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Dec_GraphNodeInt( Dec_Graph_t * pGraph, Dec_Node_t * pNode ) +{ + return pNode - pGraph->pNodes; +} + +/**Function************************************************************* + + Synopsis [Check if the graph represents elementary variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Dec_GraphIsVar( Dec_Graph_t * pGraph ) +{ + return pGraph->eRoot.Node < (unsigned)pGraph->nLeaves; +} + +/**Function************************************************************* + + Synopsis [Check if the graph represents elementary variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Dec_GraphNodeIsVar( Dec_Graph_t * pGraph, Dec_Node_t * pNode ) +{ + return Dec_GraphNodeInt(pGraph,pNode) < pGraph->nLeaves; +} + +/**Function************************************************************* + + Synopsis [Returns the elementary variable elementary variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Node_t * Dec_GraphVar( Dec_Graph_t * pGraph ) +{ + assert( Dec_GraphIsVar( pGraph ) ); + return Dec_GraphNode( pGraph, pGraph->eRoot.Node ); +} + +/**Function************************************************************* + + Synopsis [Returns the number of the elementary variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Dec_GraphVarInt( Dec_Graph_t * pGraph ) +{ + assert( Dec_GraphIsVar( pGraph ) ); + return Dec_GraphNodeInt( pGraph, Dec_GraphVar(pGraph) ); +} + +/**Function************************************************************* + + Synopsis [Sets the root of the graph.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Dec_GraphSetRoot( Dec_Graph_t * pGraph, Dec_Edge_t eRoot ) +{ + pGraph->eRoot = eRoot; +} + +/**Function************************************************************* + + Synopsis [Appends a new node to the graph.] + + Description [This procedure is meant for internal use.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Node_t * Dec_GraphAppendNode( Dec_Graph_t * pGraph ) +{ + Dec_Node_t * pNode; + if ( pGraph->nSize == pGraph->nCap ) + { + pGraph->pNodes = REALLOC( Dec_Node_t, pGraph->pNodes, 2 * pGraph->nCap ); + pGraph->nCap = 2 * pGraph->nCap; + } + pNode = pGraph->pNodes + pGraph->nSize++; + memset( pNode, 0, sizeof(Dec_Node_t) ); + return pNode; +} + +/**Function************************************************************* + + Synopsis [Creates an AND node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Edge_t Dec_GraphAddNodeAnd( Dec_Graph_t * pGraph, Dec_Edge_t eEdge0, Dec_Edge_t eEdge1 ) +{ + Dec_Node_t * pNode; + // get the new node + pNode = Dec_GraphAppendNode( pGraph ); + // set the inputs and other info + pNode->eEdge0 = eEdge0; + pNode->eEdge1 = eEdge1; + pNode->fCompl0 = eEdge0.fCompl; + pNode->fCompl1 = eEdge1.fCompl; + return Dec_EdgeCreate( pGraph->nSize - 1, 0 ); +} + +/**Function************************************************************* + + Synopsis [Creates an OR node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Edge_t Dec_GraphAddNodeOr( Dec_Graph_t * pGraph, Dec_Edge_t eEdge0, Dec_Edge_t eEdge1 ) +{ + Dec_Node_t * pNode; + // get the new node + pNode = Dec_GraphAppendNode( pGraph ); + // set the inputs and other info + pNode->eEdge0 = eEdge0; + pNode->eEdge1 = eEdge1; + pNode->fCompl0 = eEdge0.fCompl; + pNode->fCompl1 = eEdge1.fCompl; + // make adjustments for the OR gate + pNode->fNodeOr = 1; + pNode->eEdge0.fCompl = !pNode->eEdge0.fCompl; + pNode->eEdge1.fCompl = !pNode->eEdge1.fCompl; + return Dec_EdgeCreate( pGraph->nSize - 1, 1 ); +} + +/**Function************************************************************* + + Synopsis [Creates an XOR node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Edge_t Dec_GraphAddNodeXor( Dec_Graph_t * pGraph, Dec_Edge_t eEdge0, Dec_Edge_t eEdge1, int Type ) +{ + Dec_Edge_t eNode0, eNode1, eNode; + if ( Type == 0 ) + { + // derive the first AND + eEdge0.fCompl ^= 1; + eNode0 = Dec_GraphAddNodeAnd( pGraph, eEdge0, eEdge1 ); + eEdge0.fCompl ^= 1; + // derive the second AND + eEdge1.fCompl ^= 1; + eNode1 = Dec_GraphAddNodeAnd( pGraph, eEdge0, eEdge1 ); + // derive the final OR + eNode = Dec_GraphAddNodeOr( pGraph, eNode0, eNode1 ); + } + else + { + // derive the first AND + eNode0 = Dec_GraphAddNodeAnd( pGraph, eEdge0, eEdge1 ); + // derive the second AND + eEdge0.fCompl ^= 1; + eEdge1.fCompl ^= 1; + eNode1 = Dec_GraphAddNodeAnd( pGraph, eEdge0, eEdge1 ); + // derive the final OR + eNode = Dec_GraphAddNodeOr( pGraph, eNode0, eNode1 ); + eNode.fCompl ^= 1; + } + return eNode; +} + +/**Function************************************************************* + + Synopsis [Creates an XOR node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Edge_t Dec_GraphAddNodeMux( Dec_Graph_t * pGraph, Dec_Edge_t eEdgeC, Dec_Edge_t eEdgeT, Dec_Edge_t eEdgeE, int Type ) +{ + Dec_Edge_t eNode0, eNode1, eNode; + if ( Type == 0 ) + { + // derive the first AND + eNode0 = Dec_GraphAddNodeAnd( pGraph, eEdgeC, eEdgeT ); + // derive the second AND + eEdgeC.fCompl ^= 1; + eNode1 = Dec_GraphAddNodeAnd( pGraph, eEdgeC, eEdgeE ); + // derive the final OR + eNode = Dec_GraphAddNodeOr( pGraph, eNode0, eNode1 ); + } + else + { + // complement the arguments + eEdgeT.fCompl ^= 1; + eEdgeE.fCompl ^= 1; + // derive the first AND + eNode0 = Dec_GraphAddNodeAnd( pGraph, eEdgeC, eEdgeT ); + // derive the second AND + eEdgeC.fCompl ^= 1; + eNode1 = Dec_GraphAddNodeAnd( pGraph, eEdgeC, eEdgeE ); + // derive the final OR + eNode = Dec_GraphAddNodeOr( pGraph, eNode0, eNode1 ); + eNode.fCompl ^= 1; + } + return eNode; +} + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/aig/deco/module.make b/abc_with_bb_support/src/aig/deco/module.make new file mode 100644 index 000000000..d6d908e73 --- /dev/null +++ b/abc_with_bb_support/src/aig/deco/module.make @@ -0,0 +1 @@ +SRC += diff --git a/abc_with_bb_support/src/aig/ec/module.make b/abc_with_bb_support/src/aig/ec/module.make new file mode 100644 index 000000000..d6d908e73 --- /dev/null +++ b/abc_with_bb_support/src/aig/ec/module.make @@ -0,0 +1 @@ +SRC += diff --git a/abc_with_bb_support/src/aig/fra/fra.h b/abc_with_bb_support/src/aig/fra/fra.h new file mode 100644 index 000000000..10699a3fd --- /dev/null +++ b/abc_with_bb_support/src/aig/fra/fra.h @@ -0,0 +1,238 @@ +/**CFile**************************************************************** + + FileName [fra.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [[New FRAIG package.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 30, 2007.] + + Revision [$Id: fra.h,v 1.00 2007/06/30 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __FRA_H__ +#define __FRA_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +#include "vec.h" +#include "aig.h" +#include "dar.h" +#include "satSolver.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Fra_Par_t_ Fra_Par_t; +typedef struct Fra_Cla_t_ Fra_Cla_t; +typedef struct Fra_Man_t_ Fra_Man_t; + +// FRAIG parameters +struct Fra_Par_t_ +{ + int nSimWords; // the number of words in the simulation info + double dSimSatur; // the ratio of refined classes when saturation is reached + int fPatScores; // enables simulation pattern scoring + int MaxScore; // max score after which resimulation is used + double dActConeRatio; // the ratio of cone to be bumped + double dActConeBumpMax; // the largest bump in activity + int fChoicing; // enables choicing + int fSpeculate; // use speculative reduction + int fProve; // prove the miter outputs + int fVerbose; // verbose output + int fDoSparse; // skip sparse functions + int fConeBias; // bias variables in the cone (good for unsat runs) + int nBTLimitNode; // conflict limit at a node + int nBTLimitMiter; // conflict limit at an output + int nFramesK; // the number of timeframes to unroll + int fRewrite; // use rewriting for constraint reduction +}; + +// FRAIG equivalence classes +struct Fra_Cla_t_ +{ + Aig_Man_t * pAig; // the original AIG manager + Aig_Obj_t ** pMemRepr; // pointers to representatives of each node + Vec_Ptr_t * vClasses; // equivalence classes + Vec_Ptr_t * vClasses1; // equivalence class of Const1 node + Vec_Ptr_t * vClassesTemp; // temporary storage for new classes + Aig_Obj_t ** pMemClasses; // memory allocated for equivalence classes + Aig_Obj_t ** pMemClassesFree; // memory allocated for equivalence classes to be used + Vec_Ptr_t * vClassOld; // old equivalence class after splitting + Vec_Ptr_t * vClassNew; // new equivalence class(es) after splitting + int nPairs; // the number of pairs of nodes + int fRefinement; // set to 1 when refinement has happened +}; + +// FRAIG manager +struct Fra_Man_t_ +{ + // high-level data + Fra_Par_t * pPars; // parameters governing fraiging + // AIG managers + Aig_Man_t * pManAig; // the starting AIG manager + Aig_Man_t * pManFraig; // the final AIG manager + // mapping AIG into FRAIG + int nFramesAll; // the number of timeframes used + Aig_Obj_t ** pMemFraig; // memory allocated for points to the fraig nodes + // simulation info + unsigned * pSimWords; // memory for simulation information + int nSimWords; // the number of simulation words + // counter example storage + int nPatWords; // the number of words in the counter example + unsigned * pPatWords; // the counter example + int * pPatScores; // the scores of each pattern + // equivalence classes + Fra_Cla_t * pCla; // representation of (candidate) equivalent nodes + // equivalence checking + sat_solver * pSat; // SAT solver + int nSatVars; // the number of variables currently used + Vec_Ptr_t * vPiVars; // the PIs of the cone used + sint64 nBTLimitGlobal; // resource limit + sint64 nInsLimitGlobal; // resource limit + Vec_Ptr_t ** pMemFanins; // the arrays of fanins for some FRAIG nodes + int * pMemSatNums; // the array of SAT numbers for some FRAIG nodes + int nSizeAlloc; // allocated size of the arrays + Vec_Ptr_t * vTimeouts; // the nodes, for which equivalence checking timed out + // statistics + int nSimRounds; + int nNodesMiter; + int nLitsZero; + int nLitsBeg; + int nLitsEnd; + int nNodesBeg; + int nNodesEnd; + int nRegsBeg; + int nRegsEnd; + int nSatCalls; + int nSatCallsSat; + int nSatCallsUnsat; + int nSatProof; + int nSatFails; + int nSatFailsReal; + int nSpeculs; + int nChoices; + int nChoicesFake; + // runtime + int timeSim; + int timeTrav; + int timeRwr; + int timeSat; + int timeSatUnsat; + int timeSatSat; + int timeSatFail; + int timeRef; + int timeTotal; + int time1; + int time2; +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +static inline unsigned * Fra_ObjSim( Aig_Obj_t * pObj ) { return ((Fra_Man_t *)pObj->pData)->pSimWords + ((Fra_Man_t *)pObj->pData)->nSimWords * pObj->Id; } +static inline unsigned Fra_ObjRandomSim() { return (rand() << 24) ^ (rand() << 12) ^ rand(); } + +static inline Aig_Obj_t * Fra_ObjFraig( Aig_Obj_t * pObj, int i ) { return ((Fra_Man_t *)pObj->pData)->pMemFraig[((Fra_Man_t *)pObj->pData)->nFramesAll*pObj->Id + i]; } +static inline void Fra_ObjSetFraig( Aig_Obj_t * pObj, int i, Aig_Obj_t * pNode ) { ((Fra_Man_t *)pObj->pData)->pMemFraig[((Fra_Man_t *)pObj->pData)->nFramesAll*pObj->Id + i] = pNode; } + +static inline Vec_Ptr_t * Fra_ObjFaninVec( Aig_Obj_t * pObj ) { return ((Fra_Man_t *)pObj->pData)->pMemFanins[pObj->Id]; } +static inline void Fra_ObjSetFaninVec( Aig_Obj_t * pObj, Vec_Ptr_t * vFanins ) { ((Fra_Man_t *)pObj->pData)->pMemFanins[pObj->Id] = vFanins; } + +static inline int Fra_ObjSatNum( Aig_Obj_t * pObj ) { return ((Fra_Man_t *)pObj->pData)->pMemSatNums[pObj->Id]; } +static inline void Fra_ObjSetSatNum( Aig_Obj_t * pObj, int Num ) { ((Fra_Man_t *)pObj->pData)->pMemSatNums[pObj->Id] = Num; } + +static inline Aig_Obj_t * Fra_ClassObjRepr( Aig_Obj_t * pObj ) { return ((Fra_Man_t *)pObj->pData)->pCla->pMemRepr[pObj->Id]; } +static inline void Fra_ClassObjSetRepr( Aig_Obj_t * pObj, Aig_Obj_t * pNode ) { ((Fra_Man_t *)pObj->pData)->pCla->pMemRepr[pObj->Id] = pNode; } + +static inline Aig_Obj_t * Fra_ObjChild0Fra( Aig_Obj_t * pObj, int i ) { assert( !Aig_IsComplement(pObj) ); return Aig_ObjFanin0(pObj)? Aig_NotCond(Fra_ObjFraig(Aig_ObjFanin0(pObj),i), Aig_ObjFaninC0(pObj)) : NULL; } +static inline Aig_Obj_t * Fra_ObjChild1Fra( Aig_Obj_t * pObj, int i ) { assert( !Aig_IsComplement(pObj) ); return Aig_ObjFanin1(pObj)? Aig_NotCond(Fra_ObjFraig(Aig_ObjFanin1(pObj),i), Aig_ObjFaninC1(pObj)) : NULL; } + +//////////////////////////////////////////////////////////////////////// +/// ITERATORS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== fraClass.c ========================================================*/ +extern Fra_Cla_t * Fra_ClassesStart( Aig_Man_t * pAig ); +extern void Fra_ClassesStop( Fra_Cla_t * p ); +extern void Fra_ClassesCopyReprs( Fra_Cla_t * p, Vec_Ptr_t * vFailed ); +extern void Fra_ClassesPrint( Fra_Cla_t * p, int fVeryVerbose ); +extern void Fra_ClassesPrepare( Fra_Cla_t * p ); +extern int Fra_ClassesRefine( Fra_Cla_t * p ); +extern int Fra_ClassesRefine1( Fra_Cla_t * p ); +extern int Fra_ClassesCountLits( Fra_Cla_t * p ); +extern int Fra_ClassesCountPairs( Fra_Cla_t * p ); +extern void Fra_ClassesTest( Fra_Cla_t * p, int Id1, int Id2 ); +/*=== fraCnf.c ========================================================*/ +extern void Fra_NodeAddToSolver( Fra_Man_t * p, Aig_Obj_t * pOld, Aig_Obj_t * pNew ); +/*=== fraCore.c ========================================================*/ +extern Aig_Man_t * Fra_FraigPerform( Aig_Man_t * pManAig, Fra_Par_t * pPars ); +extern Aig_Man_t * Fra_FraigChoice( Aig_Man_t * pManAig ); +extern void Fra_FraigSweep( Fra_Man_t * pManAig ); +extern int Fra_FraigMiterStatus( Aig_Man_t * p ); +/*=== fraDfs.c ========================================================*/ +/*=== fraInd.c ========================================================*/ +extern Aig_Man_t * Fra_FraigInduction( Aig_Man_t * p, int nFramesK, int fRewrite, int fVerbose, int * pnIter ); +/*=== fraMan.c ========================================================*/ +extern void Fra_ParamsDefault( Fra_Par_t * pParams ); +extern void Fra_ParamsDefaultSeq( Fra_Par_t * pParams ); +extern Fra_Man_t * Fra_ManStart( Aig_Man_t * pManAig, Fra_Par_t * pParams ); +extern void Fra_ManClean( Fra_Man_t * p ); +extern Aig_Man_t * Fra_ManPrepareComb( Fra_Man_t * p ); +extern void Fra_ManFinalizeComb( Fra_Man_t * p ); +extern void Fra_ManStop( Fra_Man_t * p ); +extern void Fra_ManPrint( Fra_Man_t * p ); +/*=== fraSat.c ========================================================*/ +extern int Fra_NodesAreEquiv( Fra_Man_t * p, Aig_Obj_t * pOld, Aig_Obj_t * pNew ); +extern int Fra_NodeIsConst( Fra_Man_t * p, Aig_Obj_t * pNew ); +/*=== fraSec.c ========================================================*/ +extern int Fra_FraigSec( Aig_Man_t * p, int nFrames, int fVerbose, int fVeryVerbose ); +/*=== fraSim.c ========================================================*/ +extern int Fra_NodeHasZeroSim( Aig_Obj_t * pObj ); +extern int Fra_NodeCompareSims( Aig_Obj_t * pObj0, Aig_Obj_t * pObj1 ); +extern unsigned Fra_NodeHashSims( Aig_Obj_t * pObj ); +extern void Fra_SavePattern( Fra_Man_t * p ); +extern void Fra_Simulate( Fra_Man_t * p, int fInit ); +extern void Fra_Resimulate( Fra_Man_t * p ); +extern int Fra_CheckOutputSims( Fra_Man_t * p ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/aig/fra/fraCec.c b/abc_with_bb_support/src/aig/fra/fraCec.c new file mode 100644 index 000000000..2d5b9dce2 --- /dev/null +++ b/abc_with_bb_support/src/aig/fra/fraCec.c @@ -0,0 +1,48 @@ +/**CFile**************************************************************** + + FileName [fraCec.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New FRAIG package.] + + Synopsis [CEC engined based on fraiging.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 30, 2007.] + + Revision [$Id: fraCec.c,v 1.00 2007/06/30 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fra.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/fra/fraClass.c b/abc_with_bb_support/src/aig/fra/fraClass.c new file mode 100644 index 000000000..f8d65b588 --- /dev/null +++ b/abc_with_bb_support/src/aig/fra/fraClass.c @@ -0,0 +1,570 @@ +/**CFile**************************************************************** + + FileName [fraClass.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New FRAIG package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 30, 2007.] + + Revision [$Id: fraClass.c,v 1.00 2007/06/30 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fra.h" + +/* + The candidate equivalence classes are stored as a vector of pointers + to the array of pointers to the nodes in each class. + The first node of the class is its representative node. + The representative has the smallest topological order among the class nodes. + The nodes inside each class are ordered according to their topological order. + The classes are ordered according to the topological order of their representatives. + The array of pointers to the class nodes is terminated with a NULL pointer. + To enable dynamic addition of new classes (during class refinement), + each array has at least as many NULLs in the end, as there are nodes in the class. +*/ + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static inline Aig_Obj_t * Fra_ObjNext( Aig_Obj_t ** ppNexts, Aig_Obj_t * pObj ) { return ppNexts[pObj->Id]; } +static inline void Fra_ObjSetNext( Aig_Obj_t ** ppNexts, Aig_Obj_t * pObj, Aig_Obj_t * pNext ) { ppNexts[pObj->Id] = pNext; } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts representation of equivalence classes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fra_Cla_t * Fra_ClassesStart( Aig_Man_t * pAig ) +{ + Fra_Cla_t * p; + p = ALLOC( Fra_Cla_t, 1 ); + memset( p, 0, sizeof(Fra_Cla_t) ); + p->pAig = pAig; + p->pMemRepr = ALLOC( Aig_Obj_t *, (Aig_ManObjIdMax(pAig) + 1) ); + memset( p->pMemRepr, 0, sizeof(Aig_Obj_t *) * (Aig_ManObjIdMax(pAig) + 1) ); + p->vClasses = Vec_PtrAlloc( 100 ); + p->vClasses1 = Vec_PtrAlloc( 100 ); + p->vClassesTemp = Vec_PtrAlloc( 100 ); + p->vClassOld = Vec_PtrAlloc( 100 ); + p->vClassNew = Vec_PtrAlloc( 100 ); + return p; +} + +/**Function************************************************************* + + Synopsis [Stop representation of equivalence classes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_ClassesStop( Fra_Cla_t * p ) +{ + FREE( p->pMemClasses ); + FREE( p->pMemRepr ); + if ( p->vClassesTemp ) Vec_PtrFree( p->vClassesTemp ); + if ( p->vClassNew ) Vec_PtrFree( p->vClassNew ); + if ( p->vClassOld ) Vec_PtrFree( p->vClassOld ); + if ( p->vClasses1 ) Vec_PtrFree( p->vClasses1 ); + if ( p->vClasses ) Vec_PtrFree( p->vClasses ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Starts representation of equivalence classes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_ClassesCopyReprs( Fra_Cla_t * p, Vec_Ptr_t * vFailed ) +{ + Aig_Obj_t * pObj; + int i; + Aig_ManReprStart( p->pAig, Aig_ManObjIdMax(p->pAig) + 1 ); + memmove( p->pAig->pReprs, p->pMemRepr, sizeof(Aig_Obj_t *) * (Aig_ManObjIdMax(p->pAig) + 1) ); + if ( vFailed ) + Vec_PtrForEachEntry( vFailed, pObj, i ) + p->pAig->pReprs[pObj->Id] = NULL; +} + +/**Function************************************************************* + + Synopsis [Prints simulation classes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_PrintClass( Aig_Obj_t ** pClass ) +{ + Aig_Obj_t * pTemp; + int i; + printf( "{ " ); + for ( i = 0; pTemp = pClass[i]; i++ ) + printf( "%d ", pTemp->Id ); + printf( "}\n" ); +} + +/**Function************************************************************* + + Synopsis [Prints simulation classes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fra_ClassCount( Aig_Obj_t ** pClass ) +{ + Aig_Obj_t * pTemp; + int i; + for ( i = 0; pTemp = pClass[i]; i++ ); + return i; +} + +/**Function************************************************************* + + Synopsis [Count the number of literals.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fra_ClassesCountLits( Fra_Cla_t * p ) +{ + Aig_Obj_t ** pClass; + int i, nNodes, nLits = 0; + nLits = Vec_PtrSize( p->vClasses1 ); + Vec_PtrForEachEntry( p->vClasses, pClass, i ) + { + nNodes = Fra_ClassCount( pClass ); + assert( nNodes > 1 ); + nLits += nNodes - 1; + } + return nLits; +} + +/**Function************************************************************* + + Synopsis [Count the number of pairs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fra_ClassesCountPairs( Fra_Cla_t * p ) +{ + Aig_Obj_t ** pClass; + int i, nNodes, nPairs = 0; + Vec_PtrForEachEntry( p->vClasses, pClass, i ) + { + nNodes = Fra_ClassCount( pClass ); + assert( nNodes > 1 ); + nPairs += nNodes * (nNodes - 1) / 2; + } + return nPairs; +} + +/**Function************************************************************* + + Synopsis [Prints simulation classes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_ClassesPrint( Fra_Cla_t * p, int fVeryVerbose ) +{ + Aig_Obj_t ** pClass; + Aig_Obj_t * pObj; + int i; + + printf( "Consts = %6d. Classes = %6d. Literals = %6d.\n", + Vec_PtrSize(p->vClasses1), Vec_PtrSize(p->vClasses), Fra_ClassesCountLits(p) ); + + if ( fVeryVerbose ) + { + printf( "Constants { " ); + Vec_PtrForEachEntry( p->vClasses1, pObj, i ) + printf( "%d ", pObj->Id ); + printf( "}\n" ); + Vec_PtrForEachEntry( p->vClasses, pClass, i ) + { + printf( "%3d (%3d) : ", i, Fra_ClassCount(pClass) ); + Fra_PrintClass( pClass ); + } + printf( "\n" ); + } +} + +/**Function************************************************************* + + Synopsis [Creates initial simulation classes.] + + Description [Assumes that simulation info is assigned.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_ClassesPrepare( Fra_Cla_t * p ) +{ + Aig_Obj_t ** ppTable, ** ppNexts; + Aig_Obj_t * pObj, * pTemp; + int i, k, nTableSize, nEntries, nNodes, iEntry; + + // allocate the hash table hashing simulation info into nodes + nTableSize = Aig_PrimeCudd( Aig_ManObjIdMax(p->pAig) + 1 ); + ppTable = ALLOC( Aig_Obj_t *, nTableSize ); + ppNexts = ALLOC( Aig_Obj_t *, nTableSize ); + memset( ppTable, 0, sizeof(Aig_Obj_t *) * nTableSize ); + + // add all the nodes to the hash table + Vec_PtrClear( p->vClasses1 ); + Aig_ManForEachObj( p->pAig, pObj, i ) + { + if ( !Aig_ObjIsNode(pObj) && !Aig_ObjIsPi(pObj) ) + continue; +//printf( "%3d : ", pObj->Id ); +//Extra_PrintBinary( stdout, Fra_ObjSim(pObj), 32 ); +//printf( "\n" ); + // hash the node by its simulation info + iEntry = Fra_NodeHashSims( pObj ) % nTableSize; + // check if the node belongs to the class of constant 1 + if ( iEntry == 0 && Fra_NodeHasZeroSim( pObj ) ) + { + Vec_PtrPush( p->vClasses1, pObj ); + Fra_ClassObjSetRepr( pObj, Aig_ManConst1(p->pAig) ); + continue; + } + // add the node to the class + if ( ppTable[iEntry] == NULL ) + { + ppTable[iEntry] = pObj; + Fra_ObjSetNext( ppNexts, pObj, pObj ); + } + else + { + Fra_ObjSetNext( ppNexts, pObj, Fra_ObjNext(ppNexts,ppTable[iEntry]) ); + Fra_ObjSetNext( ppNexts, ppTable[iEntry], pObj ); + } + } + + // count the total number of nodes in the non-trivial classes + // mark the representative nodes of each equivalence class + nEntries = 0; + for ( i = 0; i < nTableSize; i++ ) + if ( ppTable[i] && ppTable[i] != Fra_ObjNext(ppNexts, ppTable[i]) ) + { + for ( pTemp = Fra_ObjNext(ppNexts, ppTable[i]), k = 1; + pTemp != ppTable[i]; + pTemp = Fra_ObjNext(ppNexts, pTemp), k++ ); + assert( k > 1 ); + nEntries += k; + // mark the node + assert( ppTable[i]->fMarkA == 0 ); + ppTable[i]->fMarkA = 1; + } + + // allocate room for classes + p->pMemClasses = ALLOC( Aig_Obj_t *, 2*(nEntries + Vec_PtrSize(p->vClasses1)) ); + p->pMemClassesFree = p->pMemClasses + 2*nEntries; + + // copy the entries into storage in the topological order + Vec_PtrClear( p->vClasses ); + nEntries = 0; + Aig_ManForEachObj( p->pAig, pObj, i ) + { + if ( !Aig_ObjIsNode(pObj) && !Aig_ObjIsPi(pObj) ) + continue; + // skip the nodes that are not representatives of non-trivial classes + if ( pObj->fMarkA == 0 ) + continue; + pObj->fMarkA = 0; + // add the class of nodes + Vec_PtrPush( p->vClasses, p->pMemClasses + 2*nEntries ); + // count the number of entries in this class + for ( pTemp = Fra_ObjNext(ppNexts, pObj), k = 1; + pTemp != pObj; + pTemp = Fra_ObjNext(ppNexts, pTemp), k++ ); + nNodes = k; + assert( nNodes > 1 ); + // add the nodes to the class in the topological order + p->pMemClasses[2*nEntries] = pObj; + for ( pTemp = Fra_ObjNext(ppNexts, pObj), k = 1; + pTemp != pObj; + pTemp = Fra_ObjNext(ppNexts, pTemp), k++ ) + { + p->pMemClasses[2*nEntries+nNodes-k] = pTemp; + Fra_ClassObjSetRepr( pTemp, pObj ); + } + // add as many empty entries +// memset( p->pMemClasses + 2*nEntries + nNodes, 0, sizeof(Aig_Obj_t *) * nNodes ); + p->pMemClasses[2*nEntries + nNodes] = NULL; + // increment the number of entries + nEntries += k; + } + free( ppTable ); + free( ppNexts ); + // now it is time to refine the classes + Fra_ClassesRefine( p ); +} + +/**Function************************************************************* + + Synopsis [Refines one class using simulation info.] + + Description [Returns the new class if refinement happened.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Obj_t ** Fra_RefineClassOne( Fra_Cla_t * p, Aig_Obj_t ** ppClass ) +{ + Aig_Obj_t * pObj, ** ppThis; + int i; + assert( ppClass[0] != NULL && ppClass[1] != NULL ); + + // check if the class is going to be refined + for ( ppThis = ppClass + 1; pObj = *ppThis; ppThis++ ) + if ( !Fra_NodeCompareSims(ppClass[0], pObj) ) + break; + if ( pObj == NULL ) + return NULL; + // split the class + Vec_PtrClear( p->vClassOld ); + Vec_PtrClear( p->vClassNew ); + Vec_PtrPush( p->vClassOld, ppClass[0] ); + for ( ppThis = ppClass + 1; pObj = *ppThis; ppThis++ ) + if ( Fra_NodeCompareSims(ppClass[0], pObj) ) + Vec_PtrPush( p->vClassOld, pObj ); + else + Vec_PtrPush( p->vClassNew, pObj ); +/* + printf( "Refining class (" ); + Vec_PtrForEachEntry( p->vClassOld, pObj, i ) + printf( "%d,", pObj->Id ); + printf( ") + (" ); + Vec_PtrForEachEntry( p->vClassNew, pObj, i ) + printf( "%d,", pObj->Id ); + printf( ")\n" ); +*/ + // put the nodes back into the class memory + Vec_PtrForEachEntry( p->vClassOld, pObj, i ) + { + ppClass[i] = pObj; + ppClass[Vec_PtrSize(p->vClassOld)+i] = NULL; + Fra_ClassObjSetRepr( pObj, i? ppClass[0] : NULL ); + } + ppClass += 2*Vec_PtrSize(p->vClassOld); + // put the new nodes into the class memory + Vec_PtrForEachEntry( p->vClassNew, pObj, i ) + { + ppClass[i] = pObj; + ppClass[Vec_PtrSize(p->vClassNew)+i] = NULL; + Fra_ClassObjSetRepr( pObj, i? ppClass[0] : NULL ); + } + return ppClass; +} + +/**Function************************************************************* + + Synopsis [Iteratively refines the classes after simulation.] + + Description [Returns the number of refinements performed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fra_RefineClassLastIter( Fra_Cla_t * p, Vec_Ptr_t * vClasses ) +{ + Aig_Obj_t ** pClass, ** pClass2; + int nRefis; + pClass = Vec_PtrEntryLast( vClasses ); + for ( nRefis = 0; pClass2 = Fra_RefineClassOne( p, pClass ); nRefis++ ) + { + // if the original class is trivial, remove it + if ( pClass[1] == NULL ) + Vec_PtrPop( vClasses ); + // if the new class is trivial, stop + if ( pClass2[1] == NULL ) + { + nRefis++; + break; + } + // othewise, add the class and continue + assert( pClass2[0] != NULL ); + Vec_PtrPush( vClasses, pClass2 ); + pClass = pClass2; + } + return nRefis; +} + +/**Function************************************************************* + + Synopsis [Refines the classes after simulation.] + + Description [Assumes that simulation info is assigned. Returns the + number of classes refined.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fra_ClassesRefine( Fra_Cla_t * p ) +{ + Vec_Ptr_t * vTemp; + Aig_Obj_t ** pClass; + int i, nRefis; + // refine the classes + nRefis = 0; + Vec_PtrClear( p->vClassesTemp ); + Vec_PtrForEachEntry( p->vClasses, pClass, i ) + { + // add the class to the new array + assert( pClass[0] != NULL ); + Vec_PtrPush( p->vClassesTemp, pClass ); + // refine the class iteratively + nRefis += Fra_RefineClassLastIter( p, p->vClassesTemp ); + } + // exchange the class representation + vTemp = p->vClassesTemp; + p->vClassesTemp = p->vClasses; + p->vClasses = vTemp; + p->fRefinement = (nRefis > 0); + return nRefis; +} + +/**Function************************************************************* + + Synopsis [Refines constant 1 equivalence class.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fra_ClassesRefine1( Fra_Cla_t * p ) +{ + Aig_Obj_t * pObj, ** ppClass; + int i, k, nRefis; + // check if there is anything to refine + if ( Vec_PtrSize(p->vClasses1) == 0 ) + return 0; + // make sure constant 1 class contains only non-constant nodes + assert( Vec_PtrEntry(p->vClasses1,0) != Aig_ManConst1(p->pAig) ); + // collect all the nodes to be refined + k = 0; + Vec_PtrClear( p->vClassNew ); + Vec_PtrForEachEntry( p->vClasses1, pObj, i ) + { + if ( Fra_NodeHasZeroSim( pObj ) ) + Vec_PtrWriteEntry( p->vClasses1, k++, pObj ); + else + Vec_PtrPush( p->vClassNew, pObj ); + } + Vec_PtrShrink( p->vClasses1, k ); + if ( Vec_PtrSize(p->vClassNew) == 0 ) + return 0; + p->fRefinement = 1; + if ( Vec_PtrSize(p->vClassNew) == 1 ) + { + Fra_ClassObjSetRepr( Vec_PtrEntry(p->vClassNew,0), NULL ); + return 1; + } + // create a new class composed of these nodes + ppClass = p->pMemClassesFree; + p->pMemClassesFree += 2 * Vec_PtrSize(p->vClassNew); + Vec_PtrForEachEntry( p->vClassNew, pObj, i ) + { + ppClass[i] = pObj; + ppClass[Vec_PtrSize(p->vClassNew)+i] = NULL; + Fra_ClassObjSetRepr( pObj, i? ppClass[0] : NULL ); + } + assert( ppClass[0] != NULL ); + Vec_PtrPush( p->vClasses, ppClass ); + // iteratively refine this class + nRefis = 1 + Fra_RefineClassLastIter( p, p->vClasses ); + return nRefis; +} + +/**Function************************************************************* + + Synopsis [Starts representation of equivalence classes with one class.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_ClassesTest( Fra_Cla_t * p, int Id1, int Id2 ) +{ + Aig_Obj_t ** pClass; + p->pMemClasses = ALLOC( Aig_Obj_t *, 4 ); + pClass = p->pMemClasses; + assert( Id1 < Id2 ); + pClass[0] = Aig_ManObj( p->pAig, Id1 ); + pClass[1] = Aig_ManObj( p->pAig, Id2 ); + pClass[2] = NULL; + pClass[3] = NULL; + Fra_ClassObjSetRepr( pClass[1], pClass[0] ); + Vec_PtrPush( p->vClasses, pClass ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/fra/fraCnf.c b/abc_with_bb_support/src/aig/fra/fraCnf.c new file mode 100644 index 000000000..8fc41cc32 --- /dev/null +++ b/abc_with_bb_support/src/aig/fra/fraCnf.c @@ -0,0 +1,286 @@ +/**CFile**************************************************************** + + FileName [fraCnf.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New FRAIG package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 30, 2007.] + + Revision [$Id: fraCnf.c,v 1.00 2007/06/30 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fra.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + + +/**Function************************************************************* + + Synopsis [Addes clauses to the solver.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_AddClausesMux( Fra_Man_t * p, Aig_Obj_t * pNode ) +{ + Aig_Obj_t * pNodeI, * pNodeT, * pNodeE; + int pLits[4], RetValue, VarF, VarI, VarT, VarE, fCompT, fCompE; + + assert( !Aig_IsComplement( pNode ) ); + assert( Aig_ObjIsMuxType( pNode ) ); + // get nodes (I = if, T = then, E = else) + pNodeI = Aig_ObjRecognizeMux( pNode, &pNodeT, &pNodeE ); + // get the variable numbers + VarF = Fra_ObjSatNum(pNode); + VarI = Fra_ObjSatNum(pNodeI); + VarT = Fra_ObjSatNum(Aig_Regular(pNodeT)); + VarE = Fra_ObjSatNum(Aig_Regular(pNodeE)); + // get the complementation flags + fCompT = Aig_IsComplement(pNodeT); + fCompE = Aig_IsComplement(pNodeE); + + // f = ITE(i, t, e) + + // i' + t' + f + // i' + t + f' + // i + e' + f + // i + e + f' + + // create four clauses + pLits[0] = toLitCond(VarI, 1); + pLits[1] = toLitCond(VarT, 1^fCompT); + pLits[2] = toLitCond(VarF, 0); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + 3 ); + assert( RetValue ); + pLits[0] = toLitCond(VarI, 1); + pLits[1] = toLitCond(VarT, 0^fCompT); + pLits[2] = toLitCond(VarF, 1); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + 3 ); + assert( RetValue ); + pLits[0] = toLitCond(VarI, 0); + pLits[1] = toLitCond(VarE, 1^fCompE); + pLits[2] = toLitCond(VarF, 0); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + 3 ); + assert( RetValue ); + pLits[0] = toLitCond(VarI, 0); + pLits[1] = toLitCond(VarE, 0^fCompE); + pLits[2] = toLitCond(VarF, 1); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + 3 ); + assert( RetValue ); + + // two additional clauses + // t' & e' -> f' + // t & e -> f + + // t + e + f' + // t' + e' + f + + if ( VarT == VarE ) + { +// assert( fCompT == !fCompE ); + return; + } + + pLits[0] = toLitCond(VarT, 0^fCompT); + pLits[1] = toLitCond(VarE, 0^fCompE); + pLits[2] = toLitCond(VarF, 1); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + 3 ); + assert( RetValue ); + pLits[0] = toLitCond(VarT, 1^fCompT); + pLits[1] = toLitCond(VarE, 1^fCompE); + pLits[2] = toLitCond(VarF, 0); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + 3 ); + assert( RetValue ); +} + +/**Function************************************************************* + + Synopsis [Addes clauses to the solver.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_AddClausesSuper( Fra_Man_t * p, Aig_Obj_t * pNode, Vec_Ptr_t * vSuper ) +{ + Aig_Obj_t * pFanin; + int * pLits, nLits, RetValue, i; + assert( !Aig_IsComplement(pNode) ); + assert( Aig_ObjIsNode( pNode ) ); + // create storage for literals + nLits = Vec_PtrSize(vSuper) + 1; + pLits = ALLOC( int, nLits ); + // suppose AND-gate is A & B = C + // add !A => !C or A + !C + Vec_PtrForEachEntry( vSuper, pFanin, i ) + { + pLits[0] = toLitCond(Fra_ObjSatNum(Aig_Regular(pFanin)), Aig_IsComplement(pFanin)); + pLits[1] = toLitCond(Fra_ObjSatNum(pNode), 1); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + 2 ); + assert( RetValue ); + } + // add A & B => C or !A + !B + C + Vec_PtrForEachEntry( vSuper, pFanin, i ) + pLits[i] = toLitCond(Fra_ObjSatNum(Aig_Regular(pFanin)), !Aig_IsComplement(pFanin)); + pLits[nLits-1] = toLitCond(Fra_ObjSatNum(pNode), 0); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + nLits ); + assert( RetValue ); + free( pLits ); +} + +/**Function************************************************************* + + Synopsis [Collects the supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_CollectSuper_rec( Aig_Obj_t * pObj, Vec_Ptr_t * vSuper, int fFirst, int fUseMuxes ) +{ + // if the new node is complemented or a PI, another gate begins + if ( Aig_IsComplement(pObj) || Aig_ObjIsPi(pObj) || (!fFirst && Aig_ObjRefs(pObj) > 1) || + (fUseMuxes && Aig_ObjIsMuxType(pObj)) ) + { + Vec_PtrPushUnique( vSuper, pObj ); + return; + } + // go through the branches + Fra_CollectSuper_rec( Aig_ObjChild0(pObj), vSuper, 0, fUseMuxes ); + Fra_CollectSuper_rec( Aig_ObjChild1(pObj), vSuper, 0, fUseMuxes ); +} + +/**Function************************************************************* + + Synopsis [Collects the supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Fra_CollectSuper( Aig_Obj_t * pObj, int fUseMuxes ) +{ + Vec_Ptr_t * vSuper; + assert( !Aig_IsComplement(pObj) ); + assert( !Aig_ObjIsPi(pObj) ); + vSuper = Vec_PtrAlloc( 4 ); + Fra_CollectSuper_rec( pObj, vSuper, 1, fUseMuxes ); + return vSuper; +} + +/**Function************************************************************* + + Synopsis [Updates the solver clause database.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_ObjAddToFrontier( Fra_Man_t * p, Aig_Obj_t * pObj, Vec_Ptr_t * vFrontier ) +{ + Fra_Man_t * pTemp = pObj->pData; + assert( !Aig_IsComplement(pObj) ); + if ( Fra_ObjSatNum(pObj) ) + return; + assert( Fra_ObjSatNum(pObj) == 0 ); + assert( Fra_ObjFaninVec(pObj) == NULL ); + if ( Aig_ObjIsConst1(pObj) ) + return; +//printf( "Assigning node %d number %d\n", pObj->Id, p->nSatVars ); + Fra_ObjSetSatNum( pObj, p->nSatVars++ ); + if ( Aig_ObjIsNode(pObj) ) + Vec_PtrPush( vFrontier, pObj ); +} + +/**Function************************************************************* + + Synopsis [Updates the solver clause database.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_NodeAddToSolver( Fra_Man_t * p, Aig_Obj_t * pOld, Aig_Obj_t * pNew ) +{ + Vec_Ptr_t * vFrontier, * vFanins; + Aig_Obj_t * pNode, * pFanin; + int i, k, fUseMuxes = 1; + assert( pOld || pNew ); + // quit if CNF is ready + if ( (!pOld || Fra_ObjFaninVec(pOld)) && (!pNew || Fra_ObjFaninVec(pNew)) ) + return; + // start the frontier + vFrontier = Vec_PtrAlloc( 100 ); + if ( pOld ) Fra_ObjAddToFrontier( p, pOld, vFrontier ); + if ( pNew ) Fra_ObjAddToFrontier( p, pNew, vFrontier ); + // explore nodes in the frontier + Vec_PtrForEachEntry( vFrontier, pNode, i ) + { + // create the supergate + assert( Fra_ObjSatNum(pNode) ); + assert( Fra_ObjFaninVec(pNode) == NULL ); + if ( fUseMuxes && Aig_ObjIsMuxType(pNode) ) + { + vFanins = Vec_PtrAlloc( 4 ); + Vec_PtrPushUnique( vFanins, Aig_ObjFanin0( Aig_ObjFanin0(pNode) ) ); + Vec_PtrPushUnique( vFanins, Aig_ObjFanin0( Aig_ObjFanin1(pNode) ) ); + Vec_PtrPushUnique( vFanins, Aig_ObjFanin1( Aig_ObjFanin0(pNode) ) ); + Vec_PtrPushUnique( vFanins, Aig_ObjFanin1( Aig_ObjFanin1(pNode) ) ); + Vec_PtrForEachEntry( vFanins, pFanin, k ) + Fra_ObjAddToFrontier( p, Aig_Regular(pFanin), vFrontier ); + Fra_AddClausesMux( p, pNode ); + } + else + { + vFanins = Fra_CollectSuper( pNode, fUseMuxes ); + Vec_PtrForEachEntry( vFanins, pFanin, k ) + Fra_ObjAddToFrontier( p, Aig_Regular(pFanin), vFrontier ); + Fra_AddClausesSuper( p, pNode, vFanins ); + } + assert( Vec_PtrSize(vFanins) > 1 ); + Fra_ObjSetFaninVec( pNode, vFanins ); + } + Vec_PtrFree( vFrontier ); +} + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/fra/fraCore.c b/abc_with_bb_support/src/aig/fra/fraCore.c new file mode 100644 index 000000000..4568330de --- /dev/null +++ b/abc_with_bb_support/src/aig/fra/fraCore.c @@ -0,0 +1,323 @@ +/**CFile**************************************************************** + + FileName [fraCore.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New FRAIG package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 30, 2007.] + + Revision [$Id: fraCore.c,v 1.00 2007/06/30 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fra.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Reports the status of the miter.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fra_FraigMiterStatus( Aig_Man_t * p ) +{ + Aig_Obj_t * pObj, * pObjNew; + int i, CountConst0 = 0, CountNonConst0 = 0, CountUndecided = 0; + if ( p->pData ) + return 0; + Aig_ManForEachPoSeq( p, pObj, i ) + { + pObjNew = Aig_ObjChild0(pObj); + // check if the output is constant 0 + if ( pObjNew == Aig_ManConst0(p) ) + { + CountConst0++; + continue; + } + // check if the output is constant 1 + if ( pObjNew == Aig_ManConst1(p) ) + { + CountNonConst0++; + continue; + } + // check if the output can be constant 0 + if ( Aig_Regular(pObjNew)->fPhase != (unsigned)Aig_IsComplement(pObjNew) ) + { + CountNonConst0++; + continue; + } + CountUndecided++; + } +/* + if ( p->pParams->fVerbose ) + { + printf( "Miter has %d outputs. ", Aig_ManPoNum(p->pManAig) ); + printf( "Const0 = %d. ", CountConst0 ); + printf( "NonConst0 = %d. ", CountNonConst0 ); + printf( "Undecided = %d. ", CountUndecided ); + printf( "\n" ); + } +*/ + if ( CountNonConst0 ) + return 0; + if ( CountUndecided ) + return -1; + return 1; +} + +/**Function************************************************************* + + Synopsis [Write speculative miter for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Fra_FraigNodeSpeculate( Fra_Man_t * p, Aig_Obj_t * pObj, Aig_Obj_t * pObjFraig, Aig_Obj_t * pObjReprFraig ) +{ + static int Counter = 0; + char FileName[20]; + Aig_Man_t * pTemp; + Aig_Obj_t * pNode; + int i; + // create manager with the logic for these two nodes + pTemp = Aig_ManExtractMiter( p->pManFraig, pObjFraig, pObjReprFraig ); + // dump the logic into a file + sprintf( FileName, "aig\\%03d.blif", ++Counter ); + Aig_ManDumpBlif( pTemp, FileName ); + printf( "Speculation cone with %d nodes was written into file \"%s\".\n", Aig_ManNodeNum(pTemp), FileName ); + // clean up + Aig_ManStop( pTemp ); + Aig_ManForEachObj( p->pManFraig, pNode, i ) + pNode->pData = p; +} + +/**Function************************************************************* + + Synopsis [Performs fraiging for one node.] + + Description [Returns the fraiged node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Fra_FraigNode( Fra_Man_t * p, Aig_Obj_t * pObj ) +{ + Aig_Obj_t * pObjRepr, * pObjFraig, * pObjFraig2, * pObjReprFraig; + int RetValue; + assert( !Aig_IsComplement(pObj) ); + // get representative of this class + pObjRepr = Fra_ClassObjRepr( pObj ); + if ( pObjRepr == NULL || // this is a unique node + (!p->pPars->fDoSparse && pObjRepr == Aig_ManConst1(p->pManAig)) ) // this is a sparse node + return; + // get the fraiged node + pObjFraig = Fra_ObjFraig( pObj, p->pPars->nFramesK ); + // get the fraiged representative + pObjReprFraig = Fra_ObjFraig( pObjRepr, p->pPars->nFramesK ); + // if the fraiged nodes are the same, return + if ( Aig_Regular(pObjFraig) == Aig_Regular(pObjReprFraig) ) + return; + assert( p->pPars->nFramesK || Aig_Regular(pObjFraig) != Aig_ManConst1(p->pManFraig) ); + // if they are proved different, the c-ex will be in p->pPatWords + RetValue = Fra_NodesAreEquiv( p, Aig_Regular(pObjReprFraig), Aig_Regular(pObjFraig) ); + if ( RetValue == 1 ) // proved equivalent + { +// if ( p->pPars->fChoicing ) +// Aig_ObjCreateRepr( p->pManFraig, Aig_Regular(pObjReprFraig), Aig_Regular(pObjFraig) ); + // the nodes proved equal + pObjFraig2 = Aig_NotCond( pObjReprFraig, pObj->fPhase ^ pObjRepr->fPhase ); + Fra_ObjSetFraig( pObj, p->pPars->nFramesK, pObjFraig2 ); + return; + } + if ( RetValue == -1 ) // failed + { + if ( p->vTimeouts == NULL ) + p->vTimeouts = Vec_PtrAlloc( 100 ); + Vec_PtrPush( p->vTimeouts, pObj ); + if ( !p->pPars->fSpeculate ) + return; + assert( 0 ); + // speculate + p->nSpeculs++; + pObjFraig2 = Aig_NotCond( pObjReprFraig, pObj->fPhase ^ pObjRepr->fPhase ); + Fra_ObjSetFraig( pObj, p->pPars->nFramesK, pObjFraig2 ); + Fra_FraigNodeSpeculate( p, pObj, Aig_Regular(pObjFraig), Aig_Regular(pObjReprFraig) ); + return; + } +//printf( "Disproved %d and %d.\n", pObj->Id, pObjRepr->Id ); + // disprove the nodes + // if we do not include the node into those disproved, we may end up + // merging this node with another representative, for which proof has timed out + if ( p->vTimeouts ) + Vec_PtrPush( p->vTimeouts, pObj ); + // simulate the counter-example and return the Fraig node + Fra_Resimulate( p ); + assert( Fra_ClassObjRepr(pObj) != pObjRepr ); + if ( Fra_ClassObjRepr(pObj) == pObjRepr ) + printf( "Fra_FraigNode(): Error in class refinement!\n" ); +} + +/**Function************************************************************* + + Synopsis [Performs fraiging for the internal nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_FraigSweep( Fra_Man_t * p ) +{ + ProgressBar * pProgress; + Aig_Obj_t * pObj, * pObjNew; + int i, k = 0; + // duplicate internal nodes + pProgress = Extra_ProgressBarStart( stdout, Aig_ManObjIdMax(p->pManAig) ); + // fraig latch outputs + Aig_ManForEachLoSeq( p->pManAig, pObj, i ) + Fra_FraigNode( p, pObj ); + // fraig internal nodes + Aig_ManForEachNode( p->pManAig, pObj, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + // derive and remember the new fraig node + pObjNew = Aig_And( p->pManFraig, Fra_ObjChild0Fra(pObj,p->pPars->nFramesK), Fra_ObjChild1Fra(pObj,p->pPars->nFramesK) ); + Fra_ObjSetFraig( pObj, p->pPars->nFramesK, pObjNew ); + Aig_Regular(pObjNew)->pData = p; + // quit if simulation detected a counter-example for a PO + if ( p->pManFraig->pData ) + continue; + // perform fraiging + Fra_FraigNode( p, pObj ); + } + Extra_ProgressBarStop( pProgress ); + // try to prove the outputs of the miter + p->nNodesMiter = Aig_ManNodeNum(p->pManFraig); +// Fra_MiterStatus( p->pManFraig ); +// if ( p->pPars->fProve && p->pManFraig->pData == NULL ) +// Fra_MiterProve( p ); +} + +/**Function************************************************************* + + Synopsis [Performs fraiging of the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Man_t * Fra_FraigPerform( Aig_Man_t * pManAig, Fra_Par_t * pPars ) +{ + Fra_Man_t * p; + Aig_Man_t * pManAigNew; + int clk; + if ( Aig_ManNodeNum(pManAig) == 0 ) + return Aig_ManDup(pManAig, 1); +clk = clock(); + assert( Aig_ManLatchNum(pManAig) == 0 ); + p = Fra_ManStart( pManAig, pPars ); + p->pManFraig = Fra_ManPrepareComb( p ); + Fra_Simulate( p, 0 ); + if ( p->pPars->fChoicing ) + Aig_ManReprStart( p->pManFraig, Aig_ManObjIdMax(p->pManAig)+1 ); + // collect initial states + p->nLitsZero = Vec_PtrSize( p->pCla->vClasses1 ); + p->nLitsBeg = Fra_ClassesCountLits( p->pCla ); + p->nNodesBeg = Aig_ManNodeNum(pManAig); + p->nRegsBeg = Aig_ManRegNum(pManAig); + // perform fraig sweep + Fra_FraigSweep( p ); + Fra_ManFinalizeComb( p ); + if ( p->pPars->fChoicing ) + { + int clk2 = clock(); + Fra_ClassesCopyReprs( p->pCla, p->vTimeouts ); + pManAigNew = Aig_ManDupRepr( p->pManAig ); + Aig_ManCreateChoices( pManAigNew ); + Aig_ManStop( p->pManFraig ); + p->pManFraig = NULL; +p->timeTrav += clock() - clk2; + } + else + { + Aig_ManCleanup( p->pManFraig ); + pManAigNew = p->pManFraig; + p->pManFraig = NULL; +/* + Fra_ClassesCopyReprs( p->pCla, p->vTimeouts ); + pManAigNew = Aig_ManDupRepr( p->pManAig ); +// Aig_ManCreateChoices( pManAigNew ); + Aig_ManCleanup( pManAigNew ); + Aig_ManStop( p->pManFraig ); + p->pManFraig = NULL; +*/ + } +p->timeTotal = clock() - clk; + // collect final stats + p->nLitsEnd = Fra_ClassesCountLits( p->pCla ); + p->nNodesEnd = Aig_ManNodeNum(pManAigNew); + p->nRegsEnd = Aig_ManRegNum(pManAigNew); + Fra_ManStop( p ); + return pManAigNew; +} + +/**Function************************************************************* + + Synopsis [Performs choicing of the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Man_t * Fra_FraigChoice( Aig_Man_t * pManAig ) +{ + Fra_Par_t Pars, * pPars = &Pars; + Fra_ParamsDefault( pPars ); + pPars->nBTLimitNode = 1000; + pPars->fVerbose = 0; + pPars->fProve = 0; + pPars->fDoSparse = 1; + pPars->fSpeculate = 0; + pPars->fChoicing = 1; + return Fra_FraigPerform( pManAig, pPars ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/fra/fraInd.c b/abc_with_bb_support/src/aig/fra/fraInd.c new file mode 100644 index 000000000..08afa000c --- /dev/null +++ b/abc_with_bb_support/src/aig/fra/fraInd.c @@ -0,0 +1,327 @@ +/**CFile**************************************************************** + + FileName [fraInd.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New FRAIG package.] + + Synopsis [Inductive prover.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 30, 2007.] + + Revision [$Id: fraInd.c,v 1.00 2007/06/30 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fra.h" +#include "cnf.h" +#include "dar.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs AIG rewriting on the constaint manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_FraigInductionRewrite( Fra_Man_t * p ) +{ + Aig_Man_t * pTemp; + Aig_Obj_t * pObj, * pObjPo; + int nTruePis, k, i, clk = clock(); + // perform AIG rewriting on the speculated frames + pTemp = Aig_ManDup( p->pManFraig, 0 ); +// pTemp = Dar_ManRwsat( pTemp, 1, 0 ); + pTemp = Dar_ManRewriteDefault( pTemp ); + +// printf( "Before = %6d. After = %6d.\n", Aig_ManNodeNum(p->pManFraig), Aig_ManNodeNum(pTemp) ); +//Aig_ManDumpBlif( p->pManFraig, "1.blif" ); +//Aig_ManDumpBlif( pTemp, "2.blif" ); + +// Fra_FramesWriteCone( pTemp ); +// Aig_ManStop( pTemp ); + // transfer PI/register pointers + assert( p->pManFraig->nRegs == pTemp->nRegs ); + assert( p->pManFraig->nAsserts == pTemp->nAsserts ); + nTruePis = Aig_ManPiNum(p->pManAig) - Aig_ManRegNum(p->pManAig); + memset( p->pMemFraig, 0, sizeof(Aig_Obj_t *) * p->nSizeAlloc * p->nFramesAll ); + Fra_ObjSetFraig( Aig_ManConst1(p->pManAig), p->pPars->nFramesK, Aig_ManConst1(pTemp) ); + Aig_ManForEachPiSeq( p->pManAig, pObj, i ) + Fra_ObjSetFraig( pObj, p->pPars->nFramesK, Aig_ManPi(pTemp,nTruePis*p->pPars->nFramesK+i) ); + k = 0; + assert( Aig_ManRegNum(p->pManAig) == Aig_ManPoNum(pTemp) - pTemp->nAsserts ); + Aig_ManForEachLoSeq( p->pManAig, pObj, i ) + { + pObjPo = Aig_ManPo(pTemp, pTemp->nAsserts + k++); + Fra_ObjSetFraig( pObj, p->pPars->nFramesK, Aig_ObjChild0(pObjPo) ); + } + // exchange + Aig_ManStop( p->pManFraig ); + p->pManFraig = pTemp; +p->timeRwr += clock() - clk; +} + +/**Function************************************************************* + + Synopsis [Performs speculative reduction for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Fra_FramesConstrainNode( Aig_Man_t * pManFraig, Aig_Obj_t * pObj, int iFrame ) +{ + Aig_Obj_t * pObjNew, * pObjNew2, * pObjRepr, * pObjReprNew, * pMiter; + // skip nodes without representative + if ( (pObjRepr = Fra_ClassObjRepr(pObj)) == NULL ) + return; + assert( pObjRepr->Id < pObj->Id ); + // get the new node + pObjNew = Fra_ObjFraig( pObj, iFrame ); + // get the new node of the representative + pObjReprNew = Fra_ObjFraig( pObjRepr, iFrame ); + // if this is the same node, no need to add constraints + if ( Aig_Regular(pObjNew) == Aig_Regular(pObjReprNew) ) + return; + // these are different nodes - perform speculative reduction + pObjNew2 = Aig_NotCond( pObjReprNew, pObj->fPhase ^ pObjRepr->fPhase ); + // set the new node + Fra_ObjSetFraig( pObj, iFrame, pObjNew2 ); + // add the constraint + pMiter = Aig_Exor( pManFraig, Aig_Regular(pObjNew), Aig_Regular(pObjReprNew) ); + pMiter = Aig_NotCond( pMiter, Aig_Regular(pMiter)->fPhase ^ Aig_IsComplement(pMiter) ); + pMiter = Aig_Not( pMiter ); + Aig_ObjCreatePo( pManFraig, pMiter ); +} + +/**Function************************************************************* + + Synopsis [Prepares the inductive case with speculative reduction.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Man_t * Fra_FramesWithClasses( Fra_Man_t * p ) +{ + Aig_Man_t * pManFraig; + Aig_Obj_t * pObj, * pObjNew; + Aig_Obj_t ** pLatches; + int i, k, f; + assert( p->pManFraig == NULL ); + assert( Aig_ManRegNum(p->pManAig) > 0 ); + assert( Aig_ManRegNum(p->pManAig) < Aig_ManPiNum(p->pManAig) ); + + // start the fraig package + pManFraig = Aig_ManStart( (Aig_ManObjIdMax(p->pManAig) + 1) * p->nFramesAll ); + pManFraig->nRegs = p->pManAig->nRegs; + // create PI nodes for the frames + for ( f = 0; f < p->nFramesAll; f++ ) + Fra_ObjSetFraig( Aig_ManConst1(p->pManAig), f, Aig_ManConst1(pManFraig) ); + for ( f = 0; f < p->nFramesAll; f++ ) + Aig_ManForEachPiSeq( p->pManAig, pObj, i ) + Fra_ObjSetFraig( pObj, f, Aig_ObjCreatePi(pManFraig) ); + // create latches for the first frame + Aig_ManForEachLoSeq( p->pManAig, pObj, i ) + Fra_ObjSetFraig( pObj, 0, Aig_ObjCreatePi(pManFraig) ); + + // add timeframes + pLatches = ALLOC( Aig_Obj_t *, Aig_ManRegNum(p->pManAig) ); + for ( f = 0; f < p->nFramesAll - 1; f++ ) + { + // set the constraints on the latch outputs + Aig_ManForEachLoSeq( p->pManAig, pObj, i ) + Fra_FramesConstrainNode( pManFraig, pObj, f ); + // add internal nodes of this frame + Aig_ManForEachNode( p->pManAig, pObj, i ) + { + pObjNew = Aig_And( pManFraig, Fra_ObjChild0Fra(pObj,f), Fra_ObjChild1Fra(pObj,f) ); + Fra_ObjSetFraig( pObj, f, pObjNew ); + Fra_FramesConstrainNode( pManFraig, pObj, f ); + } + // save the latch input values + k = 0; + Aig_ManForEachLiSeq( p->pManAig, pObj, i ) + pLatches[k++] = Fra_ObjChild0Fra(pObj,f); + assert( k == Aig_ManRegNum(p->pManAig) ); + // insert them to the latch output values + k = 0; + Aig_ManForEachLoSeq( p->pManAig, pObj, i ) + Fra_ObjSetFraig( pObj, f+1, pLatches[k++] ); + assert( k == Aig_ManRegNum(p->pManAig) ); + } + free( pLatches ); + // mark the asserts + pManFraig->nAsserts = Aig_ManPoNum(pManFraig); + // add the POs for the latch inputs + Aig_ManForEachLiSeq( p->pManAig, pObj, i ) + Aig_ObjCreatePo( pManFraig, Fra_ObjChild0Fra(pObj,f-1) ); + + // remove dangling nodes + Aig_ManCleanup( pManFraig ); + // make sure the satisfying assignment is node assigned + assert( pManFraig->pData == NULL ); + return pManFraig; +} + +/**Function************************************************************* + + Synopsis [Performs choicing of the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Man_t * Fra_FraigInduction( Aig_Man_t * pManAig, int nFramesK, int fRewrite, int fVerbose, int * pnIter ) +{ + Fra_Man_t * p; + Fra_Par_t Pars, * pPars = &Pars; + Aig_Obj_t * pObj; + Cnf_Dat_t * pCnf; + Aig_Man_t * pManAigNew; + int nIter, i, clk = clock(), clk2; + + if ( Aig_ManNodeNum(pManAig) == 0 ) + { + if ( pnIter ) *pnIter = 0; + return Aig_ManDup(pManAig, 1); + } + assert( Aig_ManLatchNum(pManAig) == 0 ); + assert( Aig_ManRegNum(pManAig) > 0 ); + assert( nFramesK > 0 ); +//Aig_ManShow( pManAig, 0, NULL ); + + // get parameters + Fra_ParamsDefaultSeq( pPars ); + pPars->nFramesK = nFramesK; + pPars->fVerbose = fVerbose; + pPars->fRewrite = fRewrite; + + // start the fraig manager for this run + p = Fra_ManStart( pManAig, pPars ); + // derive and refine e-classes using K initialized frames + Fra_Simulate( p, 1 ); + // refine e-classes using sequential simulation? + + p->nLitsZero = Vec_PtrSize( p->pCla->vClasses1 ); + p->nLitsBeg = Fra_ClassesCountLits( p->pCla ); + p->nNodesBeg = Aig_ManNodeNum(pManAig); + p->nRegsBeg = Aig_ManRegNum(pManAig); + + // iterate the inductive case + p->pCla->fRefinement = 1; + for ( nIter = 0; p->pCla->fRefinement; nIter++ ) + { + // mark the classes as non-refined + p->pCla->fRefinement = 0; + // derive non-init K-timeframes while implementing e-classes + p->pManFraig = Fra_FramesWithClasses( p ); + // perform AIG rewriting + if ( p->pPars->fRewrite ) + Fra_FraigInductionRewrite( p ); + // report the intermediate results + if ( fVerbose ) + { + printf( "%3d : Const = %6d. Class = %6d. L = %6d. LR = %6d. N = %6d. NR = %6d.\n", + nIter, Vec_PtrSize(p->pCla->vClasses1), Vec_PtrSize(p->pCla->vClasses), + Fra_ClassesCountLits(p->pCla), p->pManFraig->nAsserts, + Aig_ManNodeNum(p->pManAig), Aig_ManNodeNum(p->pManFraig) ); + } + + // convert the manager to SAT solver (the last nLatches outputs are inputs) + pCnf = Cnf_Derive( p->pManFraig, Aig_ManRegNum(p->pManFraig) ); +// pCnf = Cnf_DeriveSimple( p->pManFraig, Aig_ManRegNum(p->pManFraig) ); +//Cnf_DataWriteIntoFile( pCnf, "temp.cnf", 1 ); + + p->pSat = Cnf_DataWriteIntoSolver( pCnf ); + p->nSatVars = pCnf->nVars; + assert( p->pSat != NULL ); + if ( p->pSat == NULL ) + printf( "Fra_FraigInduction(): Computed CNF is not valid.\n" ); + + // set the pointers to the manager + Aig_ManForEachObj( p->pManFraig, pObj, i ) + pObj->pData = p; + + // transfer PI/LO variable numbers + pObj = Aig_ManConst1( p->pManFraig ); + Fra_ObjSetSatNum( pObj, pCnf->pVarNums[pObj->Id] ); + Aig_ManForEachPi( p->pManFraig, pObj, i ) + Fra_ObjSetSatNum( pObj, pCnf->pVarNums[pObj->Id] ); + // transfer LI variable numbers + Aig_ManForEachLiSeq( p->pManFraig, pObj, i ) + { + Fra_ObjSetSatNum( pObj, pCnf->pVarNums[pObj->Id] ); + Fra_ObjSetFaninVec( pObj, (void *)1 ); + } + Cnf_DataFree( pCnf ); +/* + Aig_ManForEachObj( p->pManFraig, pObj, i ) + { + Fra_ObjSetSatNum( pObj, pCnf->pVarNums[pObj->Id] ); + Fra_ObjSetFaninVec( pObj, (void *)1 ); + } + Cnf_DataFree( pCnf ); +*/ + + // perform sweeping + Fra_FraigSweep( p ); + assert( p->vTimeouts == NULL ); + if ( p->vTimeouts ) + printf( "Fra_FraigInduction(): SAT solver timed out!\n" ); + + // cleanup + Fra_ManClean( p ); + } + // move the classes into representatives and reduce AIG +clk2 = clock(); + Fra_ClassesCopyReprs( p->pCla, p->vTimeouts ); + pManAigNew = Aig_ManDupRepr( pManAig ); + Aig_ManSeqCleanup( pManAigNew ); +p->timeTrav += clock() - clk2; +p->timeTotal = clock() - clk; + // get the final stats + p->nLitsEnd = Fra_ClassesCountLits( p->pCla ); + p->nNodesEnd = Aig_ManNodeNum(pManAigNew); + p->nRegsEnd = Aig_ManRegNum(pManAigNew); + // free the manager + Fra_ManStop( p ); + // check the output +// if ( Aig_ManPoNum(pManAigNew) - Aig_ManRegNum(pManAigNew) == 1 ) +// if ( Aig_ObjChild0( Aig_ManPo(pManAigNew,0) ) == Aig_ManConst0(pManAigNew) ) +// printf( "Proved output constant 0.\n" ); + if ( pnIter ) *pnIter = nIter; + return pManAigNew; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/fra/fraMan.c b/abc_with_bb_support/src/aig/fra/fraMan.c new file mode 100644 index 000000000..40f6abd4d --- /dev/null +++ b/abc_with_bb_support/src/aig/fra/fraMan.c @@ -0,0 +1,301 @@ +/**CFile**************************************************************** + + FileName [fraMan.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New FRAIG package.] + + Synopsis [Starts the FRAIG manager.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 30, 2007.] + + Revision [$Id: fraMan.c,v 1.00 2007/06/30 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fra.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Sets the default solving parameters.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_ParamsDefault( Fra_Par_t * pPars ) +{ + memset( pPars, 0, sizeof(Fra_Par_t) ); + pPars->nSimWords = 32; // the number of words in the simulation info + pPars->dSimSatur = 0.005; // the ratio of refined classes when saturation is reached + pPars->fPatScores = 0; // enables simulation pattern scoring + pPars->MaxScore = 25; // max score after which resimulation is used + pPars->fDoSparse = 1; // skips sparse functions +// pPars->dActConeRatio = 0.05; // the ratio of cone to be bumped +// pPars->dActConeBumpMax = 5.0; // the largest bump of activity + pPars->dActConeRatio = 0.3; // the ratio of cone to be bumped + pPars->dActConeBumpMax = 10.0; // the largest bump of activity + pPars->nBTLimitNode = 100; // conflict limit at a node + pPars->nBTLimitMiter = 500000; // conflict limit at an output + pPars->nFramesK = 0; // the number of timeframes to unroll + pPars->fConeBias = 1; + pPars->fRewrite = 0; +} + +/**Function************************************************************* + + Synopsis [Sets the default solving parameters.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_ParamsDefaultSeq( Fra_Par_t * pPars ) +{ + memset( pPars, 0, sizeof(Fra_Par_t) ); + pPars->nSimWords = 1; // the number of words in the simulation info + pPars->dSimSatur = 0.005; // the ratio of refined classes when saturation is reached + pPars->fPatScores = 0; // enables simulation pattern scoring + pPars->MaxScore = 25; // max score after which resimulation is used + pPars->fDoSparse = 1; // skips sparse functions +// pPars->dActConeRatio = 0.05; // the ratio of cone to be bumped +// pPars->dActConeBumpMax = 5.0; // the largest bump of activity + pPars->dActConeRatio = 0.3; // the ratio of cone to be bumped + pPars->dActConeBumpMax = 10.0; // the largest bump of activity + pPars->nBTLimitNode =10000000; // conflict limit at a node + pPars->nBTLimitMiter = 500000; // conflict limit at an output + pPars->nFramesK = 1; // the number of timeframes to unroll + pPars->fConeBias = 0; + pPars->fRewrite = 0; +} + +/**Function************************************************************* + + Synopsis [Starts the fraiging manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fra_Man_t * Fra_ManStart( Aig_Man_t * pManAig, Fra_Par_t * pPars ) +{ + Fra_Man_t * p; + Aig_Obj_t * pObj; + int i; + // allocate the fraiging manager + p = ALLOC( Fra_Man_t, 1 ); + memset( p, 0, sizeof(Fra_Man_t) ); + p->pPars = pPars; + p->pManAig = pManAig; + p->nSizeAlloc = Aig_ManObjIdMax( pManAig ) + 1; + p->nFramesAll = pPars->nFramesK + 1; + // allocate simulation info + p->nSimWords = pPars->nSimWords * p->nFramesAll; + p->pSimWords = ALLOC( unsigned, p->nSizeAlloc * p->nSimWords ); + // clean simulation info of the constant node + memset( p->pSimWords, 0, sizeof(unsigned) * p->nSizeAlloc * p->nSimWords ); + // allocate storage for sim pattern + p->nPatWords = Aig_BitWordNum( Aig_ManPiNum(pManAig) * p->nFramesAll ); + p->pPatWords = ALLOC( unsigned, p->nPatWords ); + p->pPatScores = ALLOC( int, 32 * p->nSimWords ); + p->vPiVars = Vec_PtrAlloc( 100 ); + // equivalence classes + p->pCla = Fra_ClassesStart( pManAig ); + // allocate other members + p->pMemFraig = ALLOC( Aig_Obj_t *, p->nSizeAlloc * p->nFramesAll ); + memset( p->pMemFraig, 0, sizeof(Aig_Obj_t *) * p->nSizeAlloc * p->nFramesAll ); + p->pMemFanins = ALLOC( Vec_Ptr_t *, p->nSizeAlloc * p->nFramesAll + 10000 ); + memset( p->pMemFanins, 0, sizeof(Vec_Ptr_t *) * p->nSizeAlloc * p->nFramesAll + 10000 ); + p->pMemSatNums = ALLOC( int, p->nSizeAlloc * p->nFramesAll + 10000 ); + memset( p->pMemSatNums, 0, sizeof(int) * p->nSizeAlloc * p->nFramesAll + 10000 ); + // set random number generator + srand( 0xABCABC ); + // set the pointer to the manager + Aig_ManForEachObj( p->pManAig, pObj, i ) + pObj->pData = p; + return p; +} + +/**Function************************************************************* + + Synopsis [Starts the fraiging manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_ManClean( Fra_Man_t * p ) +{ + int i, Limit; + + Limit = Aig_ManObjIdMax(p->pManFraig) + 1; + for ( i = 0; i < Limit; i++ ) + if ( p->pMemFanins[i] && p->pMemFanins[i] != (void *)1 ) + Vec_PtrFree( p->pMemFanins[i] ); + + memset( p->pMemFraig, 0, sizeof(Aig_Obj_t *) * p->nSizeAlloc * p->nFramesAll ); + memset( p->pMemFanins, 0, sizeof(Vec_Ptr_t *) * Limit ); + memset( p->pMemSatNums, 0, sizeof(int) * Limit ); + + Aig_ManStop( p->pManFraig ); + p->pManFraig = NULL; + + sat_solver_delete( p->pSat ); + p->pSat = NULL; +} + +/**Function************************************************************* + + Synopsis [Prepares the new manager to begin fraiging.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Man_t * Fra_ManPrepareComb( Fra_Man_t * p ) +{ + Aig_Man_t * pManFraig; + Aig_Obj_t * pObj; + int i; + assert( p->pManFraig == NULL ); + // start the fraig package + pManFraig = Aig_ManStart( Aig_ManObjIdMax(p->pManAig) + 1 ); + // set the pointers to the available fraig nodes + Fra_ObjSetFraig( Aig_ManConst1(p->pManAig), 0, Aig_ManConst1(pManFraig) ); + Aig_ManForEachPi( p->pManAig, pObj, i ) + Fra_ObjSetFraig( pObj, 0, Aig_ObjCreatePi(pManFraig) ); + // set the pointers to the manager + Aig_ManForEachObj( pManFraig, pObj, i ) + pObj->pData = p; + // make sure the satisfying assignment is node assigned + assert( pManFraig->pData == NULL ); + return pManFraig; +} + +/**Function************************************************************* + + Synopsis [Finalizes the combinational miter after fraiging.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_ManFinalizeComb( Fra_Man_t * p ) +{ + Aig_Obj_t * pObj; + int i; + // add the POs + Aig_ManForEachPo( p->pManAig, pObj, i ) + Aig_ObjCreatePo( p->pManFraig, Fra_ObjChild0Fra(pObj,0) ); + // postprocess + Aig_ManCleanMarkB( p->pManFraig ); +} + + +/**Function************************************************************* + + Synopsis [Stops the fraiging manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_ManStop( Fra_Man_t * p ) +{ + int i; + for ( i = 0; i < p->nSizeAlloc; i++ ) + if ( p->pMemFanins[i] && p->pMemFanins[i] != (void *)1 ) + Vec_PtrFree( p->pMemFanins[i] ); + if ( p->pPars->fVerbose ) + Fra_ManPrint( p ); + if ( p->vTimeouts ) Vec_PtrFree( p->vTimeouts ); + if ( p->vPiVars ) Vec_PtrFree( p->vPiVars ); + if ( p->pSat ) sat_solver_delete( p->pSat ); + if ( p->pCla ) Fra_ClassesStop( p->pCla ); + FREE( p->pMemFraig ); + FREE( p->pMemFanins ); + FREE( p->pMemSatNums ); + FREE( p->pPatScores ); + FREE( p->pPatWords ); + FREE( p->pSimWords ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Prints stats for the fraiging manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_ManPrint( Fra_Man_t * p ) +{ + double nMemory = 1.0*Aig_ManObjIdMax(p->pManAig)*((p->nSimWords+2)*sizeof(unsigned)+6*sizeof(void*))/(1<<20); + printf( "SimWord = %d. Round = %d. Mem = %0.2f Mb. LitBeg = %d. LitEnd = %d. (%6.2f %%).\n", + p->nSimWords, p->nSimRounds, nMemory, p->nLitsBeg, p->nLitsEnd, 100.0*p->nLitsEnd/p->nLitsBeg ); + printf( "Proof = %d. Cex = %d. Fail = %d. FailReal = %d. Zero = %d. C-lim = %d. Vars = %d.\n", + p->nSatProof, p->nSatCallsSat, p->nSatFails, p->nSatFailsReal, p->nLitsZero, p->pPars->nBTLimitNode, p->nSatVars ); + printf( "NBeg = %d. NEnd = %d. (Gain = %6.2f %%). RBeg = %d. REnd = %d. (Gain = %6.2f %%).\n", + p->nNodesBeg, p->nNodesEnd, 100.0*(p->nNodesBeg-p->nNodesEnd)/p->nNodesBeg, + p->nRegsBeg, p->nRegsEnd, 100.0*(p->nRegsBeg-p->nRegsEnd)/p->nRegsBeg ); + if ( p->pSat ) Sat_SolverPrintStats( stdout, p->pSat ); + PRT( "AIG simulation ", p->timeSim ); + PRT( "AIG traversal ", p->timeTrav ); + if ( p->timeRwr ) + { + PRT( "AIG rewriting ", p->timeRwr ); + } + PRT( "SAT solving ", p->timeSat ); + PRT( " Unsat ", p->timeSatUnsat ); + PRT( " Sat ", p->timeSatSat ); + PRT( " Fail ", p->timeSatFail ); + PRT( "Class refining ", p->timeRef ); + PRT( "TOTAL RUNTIME ", p->timeTotal ); + if ( p->time1 ) { PRT( "time1 ", p->time1 ); } + if ( p->nSpeculs ) + printf( "Speculations = %d.\n", p->nSpeculs ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/fra/fraSat.c b/abc_with_bb_support/src/aig/fra/fraSat.c new file mode 100644 index 000000000..9dd6b0a75 --- /dev/null +++ b/abc_with_bb_support/src/aig/fra/fraSat.c @@ -0,0 +1,337 @@ +/**CFile**************************************************************** + + FileName [fraSat.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New FRAIG package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 30, 2007.] + + Revision [$Id: fraSat.c,v 1.00 2007/06/30 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fra.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Fra_SetActivityFactors( Fra_Man_t * p, Aig_Obj_t * pOld, Aig_Obj_t * pNew ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Runs equivalence test for the two nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fra_NodesAreEquiv( Fra_Man_t * p, Aig_Obj_t * pOld, Aig_Obj_t * pNew ) +{ + int pLits[4], RetValue, RetValue1, nBTLimit, clk, clk2 = clock(); + int status; + + // make sure the nodes are not complemented + assert( !Aig_IsComplement(pNew) ); + assert( !Aig_IsComplement(pOld) ); + assert( pNew != pOld ); + + // if at least one of the nodes is a failed node, perform adjustments: + // if the backtrack limit is small, simply skip this node + // if the backtrack limit is > 10, take the quare root of the limit + nBTLimit = p->pPars->nBTLimitNode; + if ( !p->pPars->fSpeculate && p->pPars->nFramesK == 0 && (nBTLimit > 0 && (pOld->fMarkB || pNew->fMarkB)) ) + { + p->nSatFails++; + // fail immediately +// return -1; + if ( nBTLimit <= 10 ) + return -1; + nBTLimit = (int)pow(nBTLimit, 0.7); + } + + p->nSatCalls++; + + // make sure the solver is allocated and has enough variables + if ( p->pSat == NULL ) + { + p->pSat = sat_solver_new(); + p->nSatVars = 1; + sat_solver_setnvars( p->pSat, 1000 ); + } + + // if the nodes do not have SAT variables, allocate them + Fra_NodeAddToSolver( p, pOld, pNew ); + + if ( p->pSat->qtail != p->pSat->qhead ) + { + status = sat_solver_simplify(p->pSat); + assert( status != 0 ); + assert( p->pSat->qtail == p->pSat->qhead ); + } + + // prepare variable activity + if ( p->pPars->fConeBias ) + Fra_SetActivityFactors( p, pOld, pNew ); + + // solve under assumptions + // A = 1; B = 0 OR A = 1; B = 1 +clk = clock(); + pLits[0] = toLitCond( Fra_ObjSatNum(pOld), 0 ); + pLits[1] = toLitCond( Fra_ObjSatNum(pNew), pOld->fPhase == pNew->fPhase ); +//Sat_SolverWriteDimacs( p->pSat, "temp.cnf", pLits, pLits + 2, 1 ); + RetValue1 = sat_solver_solve( p->pSat, pLits, pLits + 2, + (sint64)nBTLimit, (sint64)0, + p->nBTLimitGlobal, p->nInsLimitGlobal ); +p->timeSat += clock() - clk; + if ( RetValue1 == l_False ) + { +p->timeSatUnsat += clock() - clk; + pLits[0] = lit_neg( pLits[0] ); + pLits[1] = lit_neg( pLits[1] ); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + 2 ); + assert( RetValue ); + // continue solving the other implication + p->nSatCallsUnsat++; + } + else if ( RetValue1 == l_True ) + { +p->timeSatSat += clock() - clk; + Fra_SavePattern( p ); + p->nSatCallsSat++; + return 0; + } + else // if ( RetValue1 == l_Undef ) + { +p->timeSatFail += clock() - clk; + // mark the node as the failed node + if ( pOld != p->pManFraig->pConst1 ) + pOld->fMarkB = 1; + pNew->fMarkB = 1; + p->nSatFailsReal++; + return -1; + } + + // if the old node was constant 0, we already know the answer + if ( pOld == p->pManFraig->pConst1 ) + { + p->nSatProof++; + return 1; + } + + // solve under assumptions + // A = 0; B = 1 OR A = 0; B = 0 +clk = clock(); + pLits[0] = toLitCond( Fra_ObjSatNum(pOld), 1 ); + pLits[1] = toLitCond( Fra_ObjSatNum(pNew), pOld->fPhase ^ pNew->fPhase ); + RetValue1 = sat_solver_solve( p->pSat, pLits, pLits + 2, + (sint64)nBTLimit, (sint64)0, + p->nBTLimitGlobal, p->nInsLimitGlobal ); +p->timeSat += clock() - clk; + if ( RetValue1 == l_False ) + { +p->timeSatUnsat += clock() - clk; + pLits[0] = lit_neg( pLits[0] ); + pLits[1] = lit_neg( pLits[1] ); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + 2 ); + assert( RetValue ); + p->nSatCallsUnsat++; + } + else if ( RetValue1 == l_True ) + { +p->timeSatSat += clock() - clk; + Fra_SavePattern( p ); + p->nSatCallsSat++; + return 0; + } + else // if ( RetValue1 == l_Undef ) + { +p->timeSatFail += clock() - clk; + // mark the node as the failed node + pOld->fMarkB = 1; + pNew->fMarkB = 1; + p->nSatFailsReal++; + return -1; + } +/* + // check BDD proof + { + int RetVal; + PRT( "Sat", clock() - clk2 ); + clk2 = clock(); + RetVal = Fra_NodesAreEquivBdd( pOld, pNew ); +// printf( "%d ", RetVal ); + assert( RetVal ); + PRT( "Bdd", clock() - clk2 ); + printf( "\n" ); + } +*/ + // return SAT proof + p->nSatProof++; + return 1; +} + +/**Function************************************************************* + + Synopsis [Runs equivalence test for one node.] + + Description [Returns the fraiged node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fra_NodeIsConst( Fra_Man_t * p, Aig_Obj_t * pNew ) +{ + int pLits[2], RetValue1, RetValue, clk; + + // make sure the nodes are not complemented + assert( !Aig_IsComplement(pNew) ); + assert( pNew != p->pManFraig->pConst1 ); + p->nSatCalls++; + + // make sure the solver is allocated and has enough variables + if ( p->pSat == NULL ) + { + p->pSat = sat_solver_new(); + p->nSatVars = 1; + sat_solver_setnvars( p->pSat, 1000 ); + } + + // if the nodes do not have SAT variables, allocate them + Fra_NodeAddToSolver( p, NULL, pNew ); + + // prepare variable activity + if ( p->pPars->fConeBias ) + Fra_SetActivityFactors( p, NULL, pNew ); + + // solve under assumptions +clk = clock(); + pLits[0] = toLitCond( Fra_ObjSatNum(pNew), pNew->fPhase ); + RetValue1 = sat_solver_solve( p->pSat, pLits, pLits + 1, + (sint64)p->pPars->nBTLimitMiter, (sint64)0, + p->nBTLimitGlobal, p->nInsLimitGlobal ); +p->timeSat += clock() - clk; + if ( RetValue1 == l_False ) + { +p->timeSatUnsat += clock() - clk; + pLits[0] = lit_neg( pLits[0] ); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + 1 ); + assert( RetValue ); + // continue solving the other implication + p->nSatCallsUnsat++; + } + else if ( RetValue1 == l_True ) + { +p->timeSatSat += clock() - clk; + if ( p->pPatWords ) + Fra_SavePattern( p ); + p->nSatCallsSat++; + return 0; + } + else // if ( RetValue1 == l_Undef ) + { +p->timeSatFail += clock() - clk; + // mark the node as the failed node + pNew->fMarkB = 1; + p->nSatFailsReal++; + return -1; + } + + // return SAT proof + p->nSatProof++; + return 1; +} + +/**Function************************************************************* + + Synopsis [Sets variable activities in the cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fra_SetActivityFactors_rec( Fra_Man_t * p, Aig_Obj_t * pObj, int LevelMin, int LevelMax ) +{ + Vec_Ptr_t * vFanins; + Aig_Obj_t * pFanin; + int i, Counter = 0; + assert( !Aig_IsComplement(pObj) ); + assert( Fra_ObjSatNum(pObj) ); + // skip visited variables + if ( Aig_ObjIsTravIdCurrent(p->pManFraig, pObj) ) + return 0; + Aig_ObjSetTravIdCurrent(p->pManFraig, pObj); + // add the PI to the list + if ( pObj->Level <= (unsigned)LevelMin || Aig_ObjIsPi(pObj) ) + return 0; + // set the factor of this variable + // (LevelMax-LevelMin) / (pObj->Level-LevelMin) = p->pPars->dActConeBumpMax / ThisBump + p->pSat->factors[Fra_ObjSatNum(pObj)] = p->pPars->dActConeBumpMax * (pObj->Level - LevelMin)/(LevelMax - LevelMin); + veci_push(&p->pSat->act_vars, Fra_ObjSatNum(pObj)); + // explore the fanins + vFanins = Fra_ObjFaninVec( pObj ); + Vec_PtrForEachEntry( vFanins, pFanin, i ) + Counter += Fra_SetActivityFactors_rec( p, Aig_Regular(pFanin), LevelMin, LevelMax ); + return 1 + Counter; +} + +/**Function************************************************************* + + Synopsis [Sets variable activities in the cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fra_SetActivityFactors( Fra_Man_t * p, Aig_Obj_t * pOld, Aig_Obj_t * pNew ) +{ + int clk, LevelMin, LevelMax; + assert( pOld || pNew ); +clk = clock(); + // reset the active variables + veci_resize(&p->pSat->act_vars, 0); + // prepare for traversal + Aig_ManIncrementTravId( p->pManFraig ); + // determine the min and max level to visit + assert( p->pPars->dActConeRatio > 0 && p->pPars->dActConeRatio < 1 ); + LevelMax = AIG_MAX( (pNew ? pNew->Level : 0), (pOld ? pOld->Level : 0) ); + LevelMin = (int)(LevelMax * (1.0 - p->pPars->dActConeRatio)); + // traverse + if ( pOld && !Aig_ObjIsConst1(pOld) ) + Fra_SetActivityFactors_rec( p, pOld, LevelMin, LevelMax ); + if ( pNew && !Aig_ObjIsConst1(pNew) ) + Fra_SetActivityFactors_rec( p, pNew, LevelMin, LevelMax ); +//Fra_PrintActivity( p ); +p->timeTrav += clock() - clk; + return 1; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/fra/fraSec.c b/abc_with_bb_support/src/aig/fra/fraSec.c new file mode 100644 index 000000000..bc4de82a1 --- /dev/null +++ b/abc_with_bb_support/src/aig/fra/fraSec.c @@ -0,0 +1,95 @@ +/**CFile**************************************************************** + + FileName [fraSec.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New FRAIG package.] + + Synopsis [Performs SEC based on seq sweeping.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 30, 2007.] + + Revision [$Id: fraSec.c,v 1.00 2007/06/30 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fra.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fra_FraigSec( Aig_Man_t * p, int nFramesFix, int fVerbose, int fVeryVerbose ) +{ + Aig_Man_t * pNew; + int nFrames, RetValue, nIter, clk, clkTotal = clock(); + if ( nFramesFix ) + { + nFrames = nFramesFix; + // perform seq sweeping for one frame number + pNew = Fra_FraigInduction( p, nFrames, 0, fVeryVerbose, &nIter ); + } + else + { + // perform seq sweeping while increasing the number of frames + for ( nFrames = 1; ; nFrames++ ) + { +clk = clock(); + pNew = Fra_FraigInduction( p, nFrames, 0, fVeryVerbose, &nIter ); + RetValue = Fra_FraigMiterStatus( pNew ); + if ( fVerbose ) + { + printf( "FRAMES %3d : Iters = %3d. ", nFrames, nIter ); + if ( RetValue == 1 ) + printf( "UNSAT " ); + else + printf( "UNDECIDED " ); +PRT( "Time", clock() - clk ); + } + if ( RetValue != -1 ) + break; + Aig_ManStop( pNew ); + } + } + + // get the miter status + RetValue = Fra_FraigMiterStatus( pNew ); + Aig_ManStop( pNew ); + + // report the miter + if ( RetValue == 1 ) + printf( "Networks are equivalent after seq sweeping with K=%d frames (%d iters). ", nFrames, nIter ); + else if ( RetValue == 0 ) + printf( "Networks are NOT EQUIVALENT. " ); + else + printf( "Networks are UNDECIDED after seq sweeping with K=%d frames (%d iters). ", nFrames, nIter ); +PRT( "Time", clock() - clkTotal ); + return RetValue; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/fra/fraSim.c b/abc_with_bb_support/src/aig/fra/fraSim.c new file mode 100644 index 000000000..299b60986 --- /dev/null +++ b/abc_with_bb_support/src/aig/fra/fraSim.c @@ -0,0 +1,755 @@ +/**CFile**************************************************************** + + FileName [fraSim.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New FRAIG package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 30, 2007.] + + Revision [$Id: fraSim.c,v 1.00 2007/06/30 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fra.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Assigns random patterns to the PI node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_NodeAssignRandom( Fra_Man_t * p, Aig_Obj_t * pObj ) +{ + unsigned * pSims; + int i; + assert( Aig_ObjIsPi(pObj) ); + pSims = Fra_ObjSim(pObj); + for ( i = 0; i < p->nSimWords; i++ ) + pSims[i] = Fra_ObjRandomSim(); +} + +/**Function************************************************************* + + Synopsis [Assigns constant patterns to the PI node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_NodeAssignConst( Fra_Man_t * p, Aig_Obj_t * pObj, int fConst1, int iFrame ) +{ + unsigned * pSims; + int i; + assert( Aig_ObjIsPi(pObj) ); + pSims = Fra_ObjSim(pObj) + p->pPars->nSimWords * iFrame; + for ( i = 0; i < p->pPars->nSimWords; i++ ) + pSims[i] = fConst1? ~(unsigned)0 : 0; +} + +/**Function************************************************************* + + Synopsis [Assings random simulation info for the PIs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_AssignRandom( Fra_Man_t * p, int fInit ) +{ + Aig_Obj_t * pObj; + int i; + if ( fInit ) + { + assert( Aig_ManRegNum(p->pManAig) > 0 ); + assert( Aig_ManRegNum(p->pManAig) < Aig_ManPiNum(p->pManAig) ); + // assign random info for primary inputs + Aig_ManForEachPiSeq( p->pManAig, pObj, i ) + Fra_NodeAssignRandom( p, pObj ); + // assign the initial state for the latches + Aig_ManForEachLoSeq( p->pManAig, pObj, i ) + Fra_NodeAssignConst( p, pObj, 0, 0 ); + } + else + { + Aig_ManForEachPi( p->pManAig, pObj, i ) + Fra_NodeAssignRandom( p, pObj ); + } +} + +/**Function************************************************************* + + Synopsis [Assings distance-1 simulation info for the PIs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_AssignDist1( Fra_Man_t * p, unsigned * pPat ) +{ + Aig_Obj_t * pObj; + int f, i, k, Limit, nTruePis; + if ( p->pPars->nFramesK == 0 ) + { + assert( p->nFramesAll == 1 ); + // copy the PI info + Aig_ManForEachPi( p->pManAig, pObj, i ) + Fra_NodeAssignConst( p, pObj, Aig_InfoHasBit(pPat, i), 0 ); + // flip one bit + Limit = AIG_MIN( Aig_ManPiNum(p->pManAig), p->nSimWords * 32 - 1 ); + for ( i = 0; i < Limit; i++ ) + Aig_InfoXorBit( Fra_ObjSim( Aig_ManPi(p->pManAig,i) ), i+1 ); + } + else + { + // copy the PI info for each frame + nTruePis = Aig_ManPiNum(p->pManAig) - Aig_ManRegNum(p->pManAig); + for ( f = 0; f < p->nFramesAll; f++ ) + Aig_ManForEachPiSeq( p->pManAig, pObj, i ) + Fra_NodeAssignConst( p, pObj, Aig_InfoHasBit(pPat, nTruePis * f + i), f ); + // copy the latch info + k = 0; + Aig_ManForEachLoSeq( p->pManAig, pObj, i ) + Fra_NodeAssignConst( p, pObj, Aig_InfoHasBit(pPat, nTruePis * p->nFramesAll + k++), 0 ); + assert( p->pManFraig == NULL || nTruePis * p->nFramesAll + k == Aig_ManPiNum(p->pManFraig) ); + + // flip one bit of the last frame + if ( p->nFramesAll == 2 ) + { + Limit = AIG_MIN( nTruePis, p->pPars->nSimWords * 32 - 1 ); + for ( i = 0; i < Limit; i++ ) + Aig_InfoXorBit( Fra_ObjSim( Aig_ManPi(p->pManAig, i) ), i+1 ); +// Aig_InfoXorBit( Fra_ObjSim( Aig_ManPi(p->pManAig, nTruePis*(p->nFramesAll-2) + i) ), i+1 ); + } + } +} + +/**Function************************************************************* + + Synopsis [Returns 1 if simulation info is composed of all zeros.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fra_NodeHasZeroSim( Aig_Obj_t * pObj ) +{ + Fra_Man_t * p = pObj->pData; + unsigned * pSims; + int i; + pSims = Fra_ObjSim(pObj); + for ( i = 0; i < p->nSimWords; i++ ) + if ( pSims[i] ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if simulation infos are equal.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fra_NodeCompareSims( Aig_Obj_t * pObj0, Aig_Obj_t * pObj1 ) +{ + Fra_Man_t * p = pObj0->pData; + unsigned * pSims0, * pSims1; + int i; + pSims0 = Fra_ObjSim(pObj0); + pSims1 = Fra_ObjSim(pObj1); + for ( i = 0; i < p->nSimWords; i++ ) + if ( pSims0[i] != pSims1[i] ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Computes hash value of the node using its simulation info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Fra_NodeHashSims( Aig_Obj_t * pObj ) +{ + Fra_Man_t * p = pObj->pData; + static int s_FPrimes[128] = { + 1009, 1049, 1093, 1151, 1201, 1249, 1297, 1361, 1427, 1459, + 1499, 1559, 1607, 1657, 1709, 1759, 1823, 1877, 1933, 1997, + 2039, 2089, 2141, 2213, 2269, 2311, 2371, 2411, 2467, 2543, + 2609, 2663, 2699, 2741, 2797, 2851, 2909, 2969, 3037, 3089, + 3169, 3221, 3299, 3331, 3389, 3461, 3517, 3557, 3613, 3671, + 3719, 3779, 3847, 3907, 3943, 4013, 4073, 4129, 4201, 4243, + 4289, 4363, 4441, 4493, 4549, 4621, 4663, 4729, 4793, 4871, + 4933, 4973, 5021, 5087, 5153, 5227, 5281, 5351, 5417, 5471, + 5519, 5573, 5651, 5693, 5749, 5821, 5861, 5923, 6011, 6073, + 6131, 6199, 6257, 6301, 6353, 6397, 6481, 6563, 6619, 6689, + 6737, 6803, 6863, 6917, 6977, 7027, 7109, 7187, 7237, 7309, + 7393, 7477, 7523, 7561, 7607, 7681, 7727, 7817, 7877, 7933, + 8011, 8039, 8059, 8081, 8093, 8111, 8123, 8147 + }; + unsigned * pSims; + unsigned uHash; + int i; + assert( p->nSimWords <= 128 ); + uHash = 0; + pSims = Fra_ObjSim(pObj); + for ( i = 0; i < p->nSimWords; i++ ) + uHash ^= pSims[i] * s_FPrimes[i]; + return uHash; +} + +/**Function************************************************************* + + Synopsis [Simulates one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_NodeSimulate( Fra_Man_t * p, Aig_Obj_t * pObj, int iFrame ) +{ + unsigned * pSims, * pSims0, * pSims1; + int fCompl, fCompl0, fCompl1, i; + int nSimWords = p->pPars->nSimWords; + assert( !Aig_IsComplement(pObj) ); + assert( Aig_ObjIsNode(pObj) ); + assert( iFrame == 0 || nSimWords < p->nSimWords ); + // get hold of the simulation information + pSims = Fra_ObjSim(pObj) + nSimWords * iFrame; + pSims0 = Fra_ObjSim(Aig_ObjFanin0(pObj)) + nSimWords * iFrame; + pSims1 = Fra_ObjSim(Aig_ObjFanin1(pObj)) + nSimWords * iFrame; + // get complemented attributes of the children using their random info + fCompl = pObj->fPhase; + fCompl0 = Aig_ObjPhaseReal(Aig_ObjChild0(pObj)); + fCompl1 = Aig_ObjPhaseReal(Aig_ObjChild1(pObj)); + // simulate + if ( fCompl0 && fCompl1 ) + { + if ( fCompl ) + for ( i = 0; i < nSimWords; i++ ) + pSims[i] = (pSims0[i] | pSims1[i]); + else + for ( i = 0; i < nSimWords; i++ ) + pSims[i] = ~(pSims0[i] | pSims1[i]); + } + else if ( fCompl0 && !fCompl1 ) + { + if ( fCompl ) + for ( i = 0; i < nSimWords; i++ ) + pSims[i] = (pSims0[i] | ~pSims1[i]); + else + for ( i = 0; i < nSimWords; i++ ) + pSims[i] = (~pSims0[i] & pSims1[i]); + } + else if ( !fCompl0 && fCompl1 ) + { + if ( fCompl ) + for ( i = 0; i < nSimWords; i++ ) + pSims[i] = (~pSims0[i] | pSims1[i]); + else + for ( i = 0; i < nSimWords; i++ ) + pSims[i] = (pSims0[i] & ~pSims1[i]); + } + else // if ( !fCompl0 && !fCompl1 ) + { + if ( fCompl ) + for ( i = 0; i < nSimWords; i++ ) + pSims[i] = ~(pSims0[i] & pSims1[i]); + else + for ( i = 0; i < nSimWords; i++ ) + pSims[i] = (pSims0[i] & pSims1[i]); + } +} + +/**Function************************************************************* + + Synopsis [Simulates one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_NodeCopyFanin( Fra_Man_t * p, Aig_Obj_t * pObj, int iFrame ) +{ + unsigned * pSims, * pSims0; + int fCompl, fCompl0, i; + int nSimWords = p->pPars->nSimWords; + assert( !Aig_IsComplement(pObj) ); + assert( Aig_ObjIsPo(pObj) ); + assert( iFrame == 0 || nSimWords < p->nSimWords ); + // get hold of the simulation information + pSims = Fra_ObjSim(pObj) + nSimWords * iFrame; + pSims0 = Fra_ObjSim(Aig_ObjFanin0(pObj)) + nSimWords * iFrame; + // get complemented attributes of the children using their random info + fCompl = pObj->fPhase; + fCompl0 = Aig_ObjPhaseReal(Aig_ObjChild0(pObj)); + // copy information as it is + if ( fCompl0 ) + for ( i = 0; i < nSimWords; i++ ) + pSims[i] = ~pSims0[i]; + else + for ( i = 0; i < nSimWords; i++ ) + pSims[i] = pSims0[i]; +} + +/**Function************************************************************* + + Synopsis [Simulates one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_NodeTransferNext( Fra_Man_t * p, Aig_Obj_t * pOut, Aig_Obj_t * pIn, int iFrame ) +{ + unsigned * pSims0, * pSims1; + int i, nSimWords = p->pPars->nSimWords; + assert( !Aig_IsComplement(pOut) ); + assert( !Aig_IsComplement(pIn) ); + assert( Aig_ObjIsPo(pOut) ); + assert( Aig_ObjIsPi(pIn) ); + assert( iFrame == 0 || nSimWords < p->nSimWords ); + // get hold of the simulation information + pSims0 = Fra_ObjSim(pOut) + nSimWords * iFrame; + pSims1 = Fra_ObjSim(pIn) + nSimWords * (iFrame+1); + // copy information as it is + for ( i = 0; i < nSimWords; i++ ) + pSims1[i] = pSims0[i]; +} + + +/**Function************************************************************* + + Synopsis [Generated const 0 pattern.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_SavePattern0( Fra_Man_t * p, int fInit ) +{ + memset( p->pPatWords, 0, sizeof(unsigned) * p->nPatWords ); +} + +/**Function************************************************************* + + Synopsis [[Generated const 1 pattern.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_SavePattern1( Fra_Man_t * p, int fInit ) +{ + Aig_Obj_t * pObj; + int i, k, nTruePis; + memset( p->pPatWords, 0xff, sizeof(unsigned) * p->nPatWords ); + if ( !fInit ) + return; + nTruePis = Aig_ManPiNum(p->pManAig) - Aig_ManRegNum(p->pManAig); + k = 0; + Aig_ManForEachLoSeq( p->pManAig, pObj, i ) + Aig_InfoXorBit( p->pPatWords, nTruePis * p->nFramesAll + k++ ); +} + +/**Function************************************************************* + + Synopsis [Copy pattern from the solver into the internal storage.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_SavePattern( Fra_Man_t * p ) +{ + Aig_Obj_t * pObj; + int i; + memset( p->pPatWords, 0, sizeof(unsigned) * p->nPatWords ); + Aig_ManForEachPi( p->pManFraig, pObj, i ) + if ( p->pSat->model.ptr[Fra_ObjSatNum(pObj)] == l_True ) + Aig_InfoSetBit( p->pPatWords, i ); +/* + printf( "Pattern: " ); + Aig_ManForEachPi( p->pManFraig, pObj, i ) + printf( "%d", Aig_InfoHasBit( p->pPatWords, i ) ); + printf( "\n" ); +*/ +} + +/**Function************************************************************* + + Synopsis [Cleans pattern scores.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_CleanPatScores( Fra_Man_t * p ) +{ + int i, nLimit = p->nSimWords * 32; + for ( i = 0; i < nLimit; i++ ) + p->pPatScores[i] = 0; +} + +/**Function************************************************************* + + Synopsis [Adds to pattern scores.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_AddToPatScores( Fra_Man_t * p, Aig_Obj_t * pClass, Aig_Obj_t * pClassNew ) +{ + unsigned * pSims0, * pSims1; + unsigned uDiff; + int i, w; + // get hold of the simulation information + pSims0 = Fra_ObjSim(pClass); + pSims1 = Fra_ObjSim(pClassNew); + // iterate through the differences and record the score + for ( w = 0; w < p->nSimWords; w++ ) + { + uDiff = pSims0[w] ^ pSims1[w]; + if ( uDiff == 0 ) + continue; + for ( i = 0; i < 32; i++ ) + if ( uDiff & ( 1 << i ) ) + p->pPatScores[w*32+i]++; + } +} + +/**Function************************************************************* + + Synopsis [Selects the best pattern.] + + Description [Returns 1 if such pattern is found.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fra_SelectBestPat( Fra_Man_t * p ) +{ + unsigned * pSims; + Aig_Obj_t * pObj; + int i, nLimit = p->nSimWords * 32, MaxScore = 0, BestPat = -1; + for ( i = 1; i < nLimit; i++ ) + { + if ( MaxScore < p->pPatScores[i] ) + { + MaxScore = p->pPatScores[i]; + BestPat = i; + } + } + if ( MaxScore == 0 ) + return 0; +// if ( MaxScore > p->pPars->MaxScore ) +// printf( "Max score is %3d. ", MaxScore ); + // copy the best pattern into the selected pattern + memset( p->pPatWords, 0, sizeof(unsigned) * p->nPatWords ); + Aig_ManForEachPi( p->pManAig, pObj, i ) + { + pSims = Fra_ObjSim(pObj); + if ( Aig_InfoHasBit(pSims, BestPat) ) + Aig_InfoSetBit(p->pPatWords, i); + } + return MaxScore; +} + +/**Function************************************************************* + + Synopsis [Simulates AIG manager.] + + Description [Assumes that the PI simulation info is attached.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_SimulateOne( Fra_Man_t * p ) +{ + Aig_Obj_t * pObj, * pObjLi, * pObjLo; + int f, i, clk; +clk = clock(); + for ( f = 0; f < p->nFramesAll; f++ ) + { + // simulate the nodes + Aig_ManForEachNode( p->pManAig, pObj, i ) + Fra_NodeSimulate( p, pObj, f ); + if ( f == p->nFramesAll - 1 ) + break; + // copy simulation info into outputs + Aig_ManForEachLiSeq( p->pManAig, pObj, i ) + Fra_NodeCopyFanin( p, pObj, f ); + // copy simulation info into the inputs +// for ( i = 0; i < Aig_ManRegNum(p->pManAig); i++ ) +// Fra_NodeTransferNext( p, Aig_ManLi(p->pManAig, i), Aig_ManLo(p->pManAig, i), f ); + Aig_ManForEachLiLoSeq( p->pManAig, pObjLi, pObjLo, i ) + Fra_NodeTransferNext( p, pObjLi, pObjLo, f ); + } +p->timeSim += clock() - clk; +p->nSimRounds++; +} + +/**Function************************************************************* + + Synopsis [Resimulates fraiging manager after finding a counter-example.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_Resimulate( Fra_Man_t * p ) +{ + int nChanges, clk; + Fra_AssignDist1( p, p->pPatWords ); + Fra_SimulateOne( p ); + if ( p->pPars->fPatScores ) + Fra_CleanPatScores( p ); + if ( p->pPars->fProve && Fra_CheckOutputSims(p) ) + return; +clk = clock(); + nChanges = Fra_ClassesRefine( p->pCla ); + nChanges += Fra_ClassesRefine1( p->pCla ); +p->timeRef += clock() - clk; + if ( nChanges < 1 ) + printf( "Error: A counter-example did not refine classes!\n" ); + assert( nChanges >= 1 ); +//printf( "Refined classes = %5d. Changes = %4d.\n", Vec_PtrSize(p->vClasses), nChanges ); + + if ( !p->pPars->fPatScores ) + return; + + // perform additional simulation using dist1 patterns derived from successful patterns + while ( Fra_SelectBestPat(p) > p->pPars->MaxScore ) + { + Fra_AssignDist1( p, p->pPatWords ); + Fra_SimulateOne( p ); + Fra_CleanPatScores( p ); + if ( p->pPars->fProve && Fra_CheckOutputSims(p) ) + return; +clk = clock(); + nChanges = Fra_ClassesRefine( p->pCla ); + nChanges += Fra_ClassesRefine1( p->pCla ); +p->timeRef += clock() - clk; +//printf( "Refined class!!! = %5d. Changes = %4d. Pairs = %6d.\n", p->lClasses.nItems, nChanges, Fra_CountPairsClasses(p) ); + if ( nChanges == 0 ) + break; + } +} + +/**Function************************************************************* + + Synopsis [Performs simulation of the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_Simulate( Fra_Man_t * p, int fInit ) +{ + int nChanges, nClasses, clk; + assert( !fInit || Aig_ManRegNum(p->pManAig) ); + // start the classes + Fra_AssignRandom( p, fInit ); + Fra_SimulateOne( p ); + Fra_ClassesPrepare( p->pCla ); +// Fra_ClassesPrint( p->pCla, 0 ); +//printf( "Starting classes = %5d. Pairs = %6d.\n", p->lClasses.nItems, Fra_CountPairsClasses(p) ); + + // refine classes by walking 0/1 patterns + Fra_SavePattern0( p, fInit ); + Fra_AssignDist1( p, p->pPatWords ); + Fra_SimulateOne( p ); + if ( p->pPars->fProve && Fra_CheckOutputSims(p) ) + return; +clk = clock(); + nChanges = Fra_ClassesRefine( p->pCla ); + nChanges += Fra_ClassesRefine1( p->pCla ); +p->timeRef += clock() - clk; +//printf( "Refined classes = %5d. Changes = %4d. Pairs = %6d.\n", p->lClasses.nItems, nChanges, Fra_CountPairsClasses(p) ); + Fra_SavePattern1( p, fInit ); + Fra_AssignDist1( p, p->pPatWords ); + Fra_SimulateOne( p ); + if ( p->pPars->fProve && Fra_CheckOutputSims(p) ) + return; +clk = clock(); + nChanges = Fra_ClassesRefine( p->pCla ); + nChanges += Fra_ClassesRefine1( p->pCla ); +p->timeRef += clock() - clk; + +//printf( "Refined classes = %5d. Changes = %4d. Pairs = %6d.\n", p->lClasses.nItems, nChanges, Fra_CountPairsClasses(p) ); + // refine classes by random simulation + do { + Fra_AssignRandom( p, fInit ); + Fra_SimulateOne( p ); + nClasses = Vec_PtrSize(p->pCla->vClasses); + if ( p->pPars->fProve && Fra_CheckOutputSims(p) ) + return; +clk = clock(); + nChanges = Fra_ClassesRefine( p->pCla ); + nChanges += Fra_ClassesRefine1( p->pCla ); +p->timeRef += clock() - clk; +//printf( "Refined classes = %5d. Changes = %4d. Pairs = %6d.\n", p->lClasses.nItems, nChanges, Fra_CountPairsClasses(p) ); + } while ( (double)nChanges / nClasses > p->pPars->dSimSatur ); + +// if ( p->pPars->fVerbose ) +// printf( "Consts = %6d. Classes = %6d. Literals = %6d.\n", +// Vec_PtrSize(p->pCla->vClasses1), Vec_PtrSize(p->pCla->vClasses), Fra_ClassesCountLits(p->pCla) ); + +// Fra_ClassesPrint( p->pCla, 0 ); +} + +/**Function************************************************************* + + Synopsis [Creates the counter-example from the successful pattern.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fra_CheckOutputSimsSavePattern( Fra_Man_t * p, Aig_Obj_t * pObj ) +{ + unsigned * pSims; + int i, k, BestPat, * pModel; + // find the word of the pattern + pSims = Fra_ObjSim(pObj); + for ( i = 0; i < p->nSimWords; i++ ) + if ( pSims[i] ) + break; + assert( i < p->nSimWords ); + // find the bit of the pattern + for ( k = 0; k < 32; k++ ) + if ( pSims[i] & (1 << k) ) + break; + assert( k < 32 ); + // determine the best pattern + BestPat = i * 32 + k; + // fill in the counter-example data + pModel = ALLOC( int, Aig_ManPiNum(p->pManFraig) ); + Aig_ManForEachPi( p->pManAig, pObj, i ) + { + pModel[i] = Aig_InfoHasBit(Fra_ObjSim(pObj), BestPat); +// printf( "%d", pModel[i] ); + } +// printf( "\n" ); + // set the model + assert( p->pManFraig->pData == NULL ); + p->pManFraig->pData = pModel; + return; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the one of the output is already non-constant 0.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fra_CheckOutputSims( Fra_Man_t * p ) +{ + Aig_Obj_t * pObj; + int i; + // make sure the reference simulation pattern does not detect the bug + pObj = Aig_ManPo( p->pManAig, 0 ); + assert( Aig_ObjFanin0(pObj)->fPhase == (unsigned)Aig_ObjFaninC0(pObj) ); + Aig_ManForEachPo( p->pManAig, pObj, i ) + { + if ( !Fra_NodeHasZeroSim( Aig_ObjFanin0(pObj) ) ) + { + // create the counter-example from this pattern + Fra_CheckOutputSimsSavePattern( p, Aig_ObjFanin0(pObj) ); + return 1; + } + } + return 0; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/fra/fra_.c b/abc_with_bb_support/src/aig/fra/fra_.c new file mode 100644 index 000000000..57243d46e --- /dev/null +++ b/abc_with_bb_support/src/aig/fra/fra_.c @@ -0,0 +1,48 @@ +/**CFile**************************************************************** + + FileName [fra_.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New FRAIG package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 30, 2007.] + + Revision [$Id: fra_.c,v 1.00 2007/06/30 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fra.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/fra/module.make b/abc_with_bb_support/src/aig/fra/module.make new file mode 100644 index 000000000..8ad1f4a90 --- /dev/null +++ b/abc_with_bb_support/src/aig/fra/module.make @@ -0,0 +1,9 @@ +SRC += src/aig/fra/fraCec.c \ + src/aig/fra/fraClass.c \ + src/aig/fra/fraCnf.c \ + src/aig/fra/fraCore.c \ + src/aig/fra/fraInd.c \ + src/aig/fra/fraMan.c \ + src/aig/fra/fraSat.c \ + src/aig/fra/fraSec.c \ + src/aig/fra/fraSim.c diff --git a/abc_with_bb_support/src/aig/hop/cudd2.c b/abc_with_bb_support/src/aig/hop/cudd2.c new file mode 100644 index 000000000..b46d8b59d --- /dev/null +++ b/abc_with_bb_support/src/aig/hop/cudd2.c @@ -0,0 +1,355 @@ +/**CFile**************************************************************** + + FileName [cudd2.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Minimalistic And-Inverter Graph package.] + + Synopsis [Recording AIGs for the BDD operations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - October 3, 2006.] + + Revision [$Id: cudd2.c,v 1.00 2006/10/03 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "hop.h" +#include "st.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Aig_CuddMan_t_ Aig_CuddMan_t; +struct Aig_CuddMan_t_ +{ + Aig_Man_t * pAig; // internal AIG package + st_table * pTable; // hash table mapping BDD nodes into AIG nodes +}; + +// static Cudd AIG manager used in this experiment +static Aig_CuddMan_t * s_pCuddMan = NULL; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Start AIG recording.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cudd2_Init( unsigned int numVars, unsigned int numVarsZ, unsigned int numSlots, unsigned int cacheSize, unsigned long maxMemory, void * pCudd ) +{ + int v; + // start the BDD-to-AIG manager when the first BDD manager is allocated + if ( s_pCuddMan != NULL ) + return; + s_pCuddMan = ALLOC( Aig_CuddMan_t, 1 ); + s_pCuddMan->pAig = Aig_ManStart(); + s_pCuddMan->pTable = st_init_table( st_ptrcmp, st_ptrhash ); + for ( v = 0; v < (int)numVars; v++ ) + Aig_ObjCreatePi( s_pCuddMan->pAig ); +} + +/**Function************************************************************* + + Synopsis [Stops AIG recording.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cudd2_Quit( void * pCudd ) +{ + assert( s_pCuddMan != NULL ); + Aig_ManDumpBlif( s_pCuddMan->pAig, "aig_temp.blif" ); + Aig_ManStop( s_pCuddMan->pAig ); + st_free_table( s_pCuddMan->pTable ); + free( s_pCuddMan ); + s_pCuddMan = NULL; +} + +/**Function************************************************************* + + Synopsis [Fetches AIG node corresponding to the BDD node from the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static Aig_Obj_t * Cudd2_GetArg( void * pArg ) +{ + Aig_Obj_t * pNode; + assert( s_pCuddMan != NULL ); + if ( !st_lookup( s_pCuddMan->pTable, (char *)Aig_Regular(pArg), (char **)&pNode ) ) + { + printf( "Cudd2_GetArg(): An argument BDD is not in the hash table.\n" ); + return NULL; + } + return Aig_NotCond( pNode, Aig_IsComplement(pArg) ); +} + +/**Function************************************************************* + + Synopsis [Inserts the AIG node corresponding to the BDD node into the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static void Cudd2_SetArg( Aig_Obj_t * pNode, void * pResult ) +{ + assert( s_pCuddMan != NULL ); + if ( st_is_member( s_pCuddMan->pTable, (char *)Aig_Regular(pResult) ) ) + return; + pNode = Aig_NotCond( pNode, Aig_IsComplement(pResult) ); + st_insert( s_pCuddMan->pTable, (char *)Aig_Regular(pResult), (char *)pNode ); +} + +/**Function************************************************************* + + Synopsis [Registers constant 1 node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cudd2_bddOne( void * pCudd, void * pResult ) +{ + Cudd2_SetArg( Aig_ManConst1(s_pCuddMan->pAig), pResult ); +} + +/**Function************************************************************* + + Synopsis [Adds elementary variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cudd2_bddIthVar( void * pCudd, int iVar, void * pResult ) +{ + int v; + assert( s_pCuddMan != NULL ); + for ( v = Aig_ManPiNum(s_pCuddMan->pAig); v <= iVar; v++ ) + Aig_ObjCreatePi( s_pCuddMan->pAig ); + Cudd2_SetArg( Aig_ManPi(s_pCuddMan->pAig, iVar), pResult ); +} + +/**Function************************************************************* + + Synopsis [Performs BDD operation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cudd2_bddAnd( void * pCudd, void * pArg0, void * pArg1, void * pResult ) +{ + Aig_Obj_t * pNode0, * pNode1, * pNode; + pNode0 = Cudd2_GetArg( pArg0 ); + pNode1 = Cudd2_GetArg( pArg1 ); + pNode = Aig_And( s_pCuddMan->pAig, pNode0, pNode1 ); + Cudd2_SetArg( pNode, pResult ); +} + +/**Function************************************************************* + + Synopsis [Performs BDD operation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cudd2_bddOr( void * pCudd, void * pArg0, void * pArg1, void * pResult ) +{ + Cudd2_bddAnd( pCudd, Aig_Not(pArg0), Aig_Not(pArg1), Aig_Not(pResult) ); +} + +/**Function************************************************************* + + Synopsis [Performs BDD operation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cudd2_bddNand( void * pCudd, void * pArg0, void * pArg1, void * pResult ) +{ + Cudd2_bddAnd( pCudd, pArg0, pArg1, Aig_Not(pResult) ); +} + +/**Function************************************************************* + + Synopsis [Performs BDD operation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cudd2_bddNor( void * pCudd, void * pArg0, void * pArg1, void * pResult ) +{ + Cudd2_bddAnd( pCudd, Aig_Not(pArg0), Aig_Not(pArg1), pResult ); +} + +/**Function************************************************************* + + Synopsis [Performs BDD operation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cudd2_bddXor( void * pCudd, void * pArg0, void * pArg1, void * pResult ) +{ + Aig_Obj_t * pNode0, * pNode1, * pNode; + pNode0 = Cudd2_GetArg( pArg0 ); + pNode1 = Cudd2_GetArg( pArg1 ); + pNode = Aig_Exor( s_pCuddMan->pAig, pNode0, pNode1 ); + Cudd2_SetArg( pNode, pResult ); +} + +/**Function************************************************************* + + Synopsis [Performs BDD operation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cudd2_bddXnor( void * pCudd, void * pArg0, void * pArg1, void * pResult ) +{ + Cudd2_bddXor( pCudd, pArg0, pArg1, Aig_Not(pResult) ); +} + +/**Function************************************************************* + + Synopsis [Performs BDD operation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cudd2_bddIte( void * pCudd, void * pArg0, void * pArg1, void * pArg2, void * pResult ) +{ + Aig_Obj_t * pNode0, * pNode1, * pNode2, * pNode; + pNode0 = Cudd2_GetArg( pArg0 ); + pNode1 = Cudd2_GetArg( pArg1 ); + pNode2 = Cudd2_GetArg( pArg2 ); + pNode = Aig_Mux( s_pCuddMan->pAig, pNode0, pNode1, pNode2 ); + Cudd2_SetArg( pNode, pResult ); +} + +/**Function************************************************************* + + Synopsis [Performs BDD operation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cudd2_bddCompose( void * pCudd, void * pArg0, void * pArg1, int v, void * pResult ) +{ + Aig_Obj_t * pNode0, * pNode1, * pNode; + pNode0 = Cudd2_GetArg( pArg0 ); + pNode1 = Cudd2_GetArg( pArg1 ); + pNode = Aig_Compose( s_pCuddMan->pAig, pNode0, pNode1, v ); + Cudd2_SetArg( pNode, pResult ); +} + +/**Function************************************************************* + + Synopsis [Should be called after each containment check.] + + Description [Result should be 1 if Cudd2_bddLeq returned 1.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cudd2_bddLeq( void * pCudd, void * pArg0, void * pArg1, int Result ) +{ + Aig_Obj_t * pNode0, * pNode1, * pNode; + pNode0 = Cudd2_GetArg( pArg0 ); + pNode1 = Cudd2_GetArg( pArg1 ); + pNode = Aig_And( s_pCuddMan->pAig, pNode0, Aig_Not(pNode1) ); + Aig_ObjCreatePo( s_pCuddMan->pAig, pNode ); +} + +/**Function************************************************************* + + Synopsis [Should be called after each equality check.] + + Description [Result should be 1 if they are equal.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cudd2_bddEqual( void * pCudd, void * pArg0, void * pArg1, int Result ) +{ + Aig_Obj_t * pNode0, * pNode1, * pNode; + pNode0 = Cudd2_GetArg( pArg0 ); + pNode1 = Cudd2_GetArg( pArg1 ); + pNode = Aig_Exor( s_pCuddMan->pAig, pNode0, pNode1 ); + Aig_ObjCreatePo( s_pCuddMan->pAig, pNode ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/hop/cudd2.h b/abc_with_bb_support/src/aig/hop/cudd2.h new file mode 100644 index 000000000..f3b0df25d --- /dev/null +++ b/abc_with_bb_support/src/aig/hop/cudd2.h @@ -0,0 +1,82 @@ +/**CFile**************************************************************** + + FileName [cudd2.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Minimalistic And-Inverter Graph package.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - October 3, 2006.] + + Revision [$Id: cudd2.h,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __CUDD2_H__ +#define __CUDD2_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +// HA: Added for printing messages +#ifndef MSG +#define MSG(msg) (printf("%s = \n",(msg))); +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////// +/// ITERATORS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +extern void Cudd2_Init ( unsigned int numVars, unsigned int numVarsZ, unsigned int numSlots, unsigned int cacheSize, unsigned long maxMemory, void * pCudd ); +extern void Cudd2_Quit ( void * pCudd ); +extern void Cudd2_bddOne ( void * pCudd, void * pResult ); +extern void Cudd2_bddIthVar ( void * pCudd, int iVar, void * pResult ); +extern void Cudd2_bddAnd ( void * pCudd, void * pArg0, void * pArg1, void * pResult ); +extern void Cudd2_bddOr ( void * pCudd, void * pArg0, void * pArg1, void * pResult ); +extern void Cudd2_bddNand ( void * pCudd, void * pArg0, void * pArg1, void * pResult ); +extern void Cudd2_bddNor ( void * pCudd, void * pArg0, void * pArg1, void * pResult ); +extern void Cudd2_bddXor ( void * pCudd, void * pArg0, void * pArg1, void * pResult ); +extern void Cudd2_bddXnor ( void * pCudd, void * pArg0, void * pArg1, void * pResult ); +extern void Cudd2_bddIte ( void * pCudd, void * pArg0, void * pArg1, void * pArg2, void * pResult ); +extern void Cudd2_bddCompose( void * pCudd, void * pArg0, void * pArg1, int v, void * pResult ); +extern void Cudd2_bddLeq ( void * pCudd, void * pArg0, void * pArg1, int Result ); +extern void Cudd2_bddEqual ( void * pCudd, void * pArg0, void * pArg1, int Result ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/aig/hop/hop.h b/abc_with_bb_support/src/aig/hop/hop.h new file mode 100644 index 000000000..d2abafe24 --- /dev/null +++ b/abc_with_bb_support/src/aig/hop/hop.h @@ -0,0 +1,345 @@ +/**CFile**************************************************************** + + FileName [hop.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Minimalistic And-Inverter Graph package.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: hop.h,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __HOP_H__ +#define __HOP_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +#include "vec.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Hop_Man_t_ Hop_Man_t; +typedef struct Hop_Obj_t_ Hop_Obj_t; +typedef int Hop_Edge_t; + +// object types +typedef enum { + AIG_NONE, // 0: non-existent object + AIG_CONST1, // 1: constant 1 + AIG_PI, // 2: primary input + AIG_PO, // 3: primary output + AIG_AND, // 4: AND node + AIG_EXOR, // 5: EXOR node + AIG_VOID // 6: unused object +} Hop_Type_t; + +// the AIG node +struct Hop_Obj_t_ // 6 words +{ + void * pData; // misc + Hop_Obj_t * pNext; // strashing table + Hop_Obj_t * pFanin0; // fanin + Hop_Obj_t * pFanin1; // fanin + unsigned long Type : 3; // object type + unsigned long fPhase : 1; // value under 000...0 pattern + unsigned long fMarkA : 1; // multipurpose mask + unsigned long fMarkB : 1; // multipurpose mask + unsigned long nRefs : 26; // reference count (level) + int Id; // unique ID of the node +}; + +// the AIG manager +struct Hop_Man_t_ +{ + // AIG nodes + Vec_Ptr_t * vPis; // the array of PIs + Vec_Ptr_t * vPos; // the array of POs + Vec_Ptr_t * vObjs; // the array of all nodes (optional) + Hop_Obj_t * pConst1; // the constant 1 node + Hop_Obj_t Ghost; // the ghost node + // AIG node counters + int nObjs[AIG_VOID];// the number of objects by type + int nCreated; // the number of created objects + int nDeleted; // the number of deleted objects + // stuctural hash table + Hop_Obj_t ** pTable; // structural hash table + int nTableSize; // structural hash table size + // various data members + void * pData; // the temporary data + int nTravIds; // the current traversal ID + int fRefCount; // enables reference counting + int fCatchExor; // enables EXOR nodes + // memory management + Vec_Ptr_t * vChunks; // allocated memory pieces + Vec_Ptr_t * vPages; // memory pages used by nodes + Hop_Obj_t * pListFree; // the list of free nodes + // timing statistics + int time1; + int time2; +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define AIG_MIN(a,b) (((a) < (b))? (a) : (b)) +#define AIG_MAX(a,b) (((a) > (b))? (a) : (b)) + +#ifndef PRT +#define PRT(a,t) printf("%s = ", (a)); printf("%6.2f sec\n", (float)(t)/(float)(CLOCKS_PER_SEC)) +#endif + +static inline int Hop_BitWordNum( int nBits ) { return (nBits>>5) + ((nBits&31) > 0); } +static inline int Hop_TruthWordNum( int nVars ) { return nVars <= 5 ? 1 : (1 << (nVars - 5)); } +static inline int Hop_InfoHasBit( unsigned * p, int i ) { return (p[(i)>>5] & (1<<((i) & 31))) > 0; } +static inline void Hop_InfoSetBit( unsigned * p, int i ) { p[(i)>>5] |= (1<<((i) & 31)); } +static inline void Hop_InfoXorBit( unsigned * p, int i ) { p[(i)>>5] ^= (1<<((i) & 31)); } + +static inline Hop_Obj_t * Hop_Regular( Hop_Obj_t * p ) { return (Hop_Obj_t *)((unsigned long)(p) & ~01); } +static inline Hop_Obj_t * Hop_Not( Hop_Obj_t * p ) { return (Hop_Obj_t *)((unsigned long)(p) ^ 01); } +static inline Hop_Obj_t * Hop_NotCond( Hop_Obj_t * p, int c ) { return (Hop_Obj_t *)((unsigned long)(p) ^ (c)); } +static inline int Hop_IsComplement( Hop_Obj_t * p ) { return (int )(((unsigned long)p) & 01); } + +static inline Hop_Obj_t * Hop_ManConst0( Hop_Man_t * p ) { return Hop_Not(p->pConst1); } +static inline Hop_Obj_t * Hop_ManConst1( Hop_Man_t * p ) { return p->pConst1; } +static inline Hop_Obj_t * Hop_ManGhost( Hop_Man_t * p ) { return &p->Ghost; } +static inline Hop_Obj_t * Hop_ManPi( Hop_Man_t * p, int i ) { return (Hop_Obj_t *)Vec_PtrEntry(p->vPis, i); } +static inline Hop_Obj_t * Hop_ManPo( Hop_Man_t * p, int i ) { return (Hop_Obj_t *)Vec_PtrEntry(p->vPos, i); } +static inline Hop_Obj_t * Hop_ManObj( Hop_Man_t * p, int i ) { return p->vObjs ? (Hop_Obj_t *)Vec_PtrEntry(p->vObjs, i) : NULL; } + +static inline Hop_Edge_t Hop_EdgeCreate( int Id, int fCompl ) { return (Id << 1) | fCompl; } +static inline int Hop_EdgeId( Hop_Edge_t Edge ) { return Edge >> 1; } +static inline int Hop_EdgeIsComplement( Hop_Edge_t Edge ) { return Edge & 1; } +static inline Hop_Edge_t Hop_EdgeRegular( Hop_Edge_t Edge ) { return (Edge >> 1) << 1; } +static inline Hop_Edge_t Hop_EdgeNot( Hop_Edge_t Edge ) { return Edge ^ 1; } +static inline Hop_Edge_t Hop_EdgeNotCond( Hop_Edge_t Edge, int fCond ) { return Edge ^ fCond; } + +static inline int Hop_ManPiNum( Hop_Man_t * p ) { return p->nObjs[AIG_PI]; } +static inline int Hop_ManPoNum( Hop_Man_t * p ) { return p->nObjs[AIG_PO]; } +static inline int Hop_ManAndNum( Hop_Man_t * p ) { return p->nObjs[AIG_AND]; } +static inline int Hop_ManExorNum( Hop_Man_t * p ) { return p->nObjs[AIG_EXOR]; } +static inline int Hop_ManNodeNum( Hop_Man_t * p ) { return p->nObjs[AIG_AND]+p->nObjs[AIG_EXOR];} +static inline int Hop_ManGetCost( Hop_Man_t * p ) { return p->nObjs[AIG_AND]+3*p->nObjs[AIG_EXOR]; } +static inline int Hop_ManObjNum( Hop_Man_t * p ) { return p->nCreated - p->nDeleted; } + +static inline Hop_Type_t Hop_ObjType( Hop_Obj_t * pObj ) { return (Hop_Type_t)pObj->Type; } +static inline int Hop_ObjIsNone( Hop_Obj_t * pObj ) { return pObj->Type == AIG_NONE; } +static inline int Hop_ObjIsConst1( Hop_Obj_t * pObj ) { assert(!Hop_IsComplement(pObj)); return pObj->Type == AIG_CONST1; } +static inline int Hop_ObjIsPi( Hop_Obj_t * pObj ) { return pObj->Type == AIG_PI; } +static inline int Hop_ObjIsPo( Hop_Obj_t * pObj ) { return pObj->Type == AIG_PO; } +static inline int Hop_ObjIsAnd( Hop_Obj_t * pObj ) { return pObj->Type == AIG_AND; } +static inline int Hop_ObjIsExor( Hop_Obj_t * pObj ) { return pObj->Type == AIG_EXOR; } +static inline int Hop_ObjIsNode( Hop_Obj_t * pObj ) { return pObj->Type == AIG_AND || pObj->Type == AIG_EXOR; } +static inline int Hop_ObjIsTerm( Hop_Obj_t * pObj ) { return pObj->Type == AIG_PI || pObj->Type == AIG_PO || pObj->Type == AIG_CONST1; } +static inline int Hop_ObjIsHash( Hop_Obj_t * pObj ) { return pObj->Type == AIG_AND || pObj->Type == AIG_EXOR; } + +static inline int Hop_ObjIsMarkA( Hop_Obj_t * pObj ) { return pObj->fMarkA; } +static inline void Hop_ObjSetMarkA( Hop_Obj_t * pObj ) { pObj->fMarkA = 1; } +static inline void Hop_ObjClearMarkA( Hop_Obj_t * pObj ) { pObj->fMarkA = 0; } + +static inline void Hop_ObjSetTravId( Hop_Obj_t * pObj, int TravId ) { pObj->pData = (void *)TravId; } +static inline void Hop_ObjSetTravIdCurrent( Hop_Man_t * p, Hop_Obj_t * pObj ) { pObj->pData = (void *)p->nTravIds; } +static inline void Hop_ObjSetTravIdPrevious( Hop_Man_t * p, Hop_Obj_t * pObj ) { pObj->pData = (void *)(p->nTravIds - 1); } +static inline int Hop_ObjIsTravIdCurrent( Hop_Man_t * p, Hop_Obj_t * pObj ) { return (int )((int)pObj->pData == p->nTravIds); } +static inline int Hop_ObjIsTravIdPrevious( Hop_Man_t * p, Hop_Obj_t * pObj ) { return (int )((int)pObj->pData == p->nTravIds - 1); } + +static inline int Hop_ObjTravId( Hop_Obj_t * pObj ) { return (int)pObj->pData; } +static inline int Hop_ObjPhase( Hop_Obj_t * pObj ) { return pObj->fPhase; } +static inline int Hop_ObjRefs( Hop_Obj_t * pObj ) { return pObj->nRefs; } +static inline void Hop_ObjRef( Hop_Obj_t * pObj ) { pObj->nRefs++; } +static inline void Hop_ObjDeref( Hop_Obj_t * pObj ) { assert( pObj->nRefs > 0 ); pObj->nRefs--; } +static inline void Hop_ObjClearRef( Hop_Obj_t * pObj ) { pObj->nRefs = 0; } +static inline int Hop_ObjFaninC0( Hop_Obj_t * pObj ) { return Hop_IsComplement(pObj->pFanin0); } +static inline int Hop_ObjFaninC1( Hop_Obj_t * pObj ) { return Hop_IsComplement(pObj->pFanin1); } +static inline Hop_Obj_t * Hop_ObjFanin0( Hop_Obj_t * pObj ) { return Hop_Regular(pObj->pFanin0); } +static inline Hop_Obj_t * Hop_ObjFanin1( Hop_Obj_t * pObj ) { return Hop_Regular(pObj->pFanin1); } +static inline Hop_Obj_t * Hop_ObjChild0( Hop_Obj_t * pObj ) { return pObj->pFanin0; } +static inline Hop_Obj_t * Hop_ObjChild1( Hop_Obj_t * pObj ) { return pObj->pFanin1; } +static inline Hop_Obj_t * Hop_ObjChild0Copy( Hop_Obj_t * pObj ) { assert( !Hop_IsComplement(pObj) ); return Hop_ObjFanin0(pObj)? Hop_NotCond((Hop_Obj_t *)Hop_ObjFanin0(pObj)->pData, Hop_ObjFaninC0(pObj)) : NULL; } +static inline Hop_Obj_t * Hop_ObjChild1Copy( Hop_Obj_t * pObj ) { assert( !Hop_IsComplement(pObj) ); return Hop_ObjFanin1(pObj)? Hop_NotCond((Hop_Obj_t *)Hop_ObjFanin1(pObj)->pData, Hop_ObjFaninC1(pObj)) : NULL; } +static inline int Hop_ObjLevel( Hop_Obj_t * pObj ) { return pObj->nRefs; } +static inline int Hop_ObjLevelNew( Hop_Obj_t * pObj ) { return 1 + Hop_ObjIsExor(pObj) + AIG_MAX(Hop_ObjFanin0(pObj)->nRefs, Hop_ObjFanin1(pObj)->nRefs); } +static inline int Hop_ObjFaninPhase( Hop_Obj_t * pObj ) { return Hop_IsComplement(pObj)? !Hop_Regular(pObj)->fPhase : pObj->fPhase; } +static inline void Hop_ObjClean( Hop_Obj_t * pObj ) { memset( pObj, 0, sizeof(Hop_Obj_t) ); } +static inline int Hop_ObjWhatFanin( Hop_Obj_t * pObj, Hop_Obj_t * pFanin ) +{ + if ( Hop_ObjFanin0(pObj) == pFanin ) return 0; + if ( Hop_ObjFanin1(pObj) == pFanin ) return 1; + assert(0); return -1; +} +static inline int Hop_ObjFanoutC( Hop_Obj_t * pObj, Hop_Obj_t * pFanout ) +{ + if ( Hop_ObjFanin0(pFanout) == pObj ) return Hop_ObjFaninC0(pObj); + if ( Hop_ObjFanin1(pFanout) == pObj ) return Hop_ObjFaninC1(pObj); + assert(0); return -1; +} + +// create the ghost of the new node +static inline Hop_Obj_t * Hop_ObjCreateGhost( Hop_Man_t * p, Hop_Obj_t * p0, Hop_Obj_t * p1, Hop_Type_t Type ) +{ + Hop_Obj_t * pGhost; + assert( Type != AIG_AND || !Hop_ObjIsConst1(Hop_Regular(p0)) ); + assert( p1 == NULL || !Hop_ObjIsConst1(Hop_Regular(p1)) ); + assert( Type == AIG_PI || Hop_Regular(p0) != Hop_Regular(p1) ); + pGhost = Hop_ManGhost(p); + pGhost->Type = Type; + if ( Hop_Regular(p0)->Id < Hop_Regular(p1)->Id ) + { + pGhost->pFanin0 = p0; + pGhost->pFanin1 = p1; + } + else + { + pGhost->pFanin0 = p1; + pGhost->pFanin1 = p0; + } + return pGhost; +} + +// internal memory manager +static inline Hop_Obj_t * Hop_ManFetchMemory( Hop_Man_t * p ) +{ + extern void Hop_ManAddMemory( Hop_Man_t * p ); + Hop_Obj_t * pTemp; + if ( p->pListFree == NULL ) + Hop_ManAddMemory( p ); + pTemp = p->pListFree; + p->pListFree = *((Hop_Obj_t **)pTemp); + memset( pTemp, 0, sizeof(Hop_Obj_t) ); + if ( p->vObjs ) + { + assert( p->nCreated == Vec_PtrSize(p->vObjs) ); + Vec_PtrPush( p->vObjs, pTemp ); + } + pTemp->Id = p->nCreated++; + return pTemp; +} +static inline void Hop_ManRecycleMemory( Hop_Man_t * p, Hop_Obj_t * pEntry ) +{ + pEntry->Type = AIG_NONE; // distinquishes dead node from live node + *((Hop_Obj_t **)pEntry) = p->pListFree; + p->pListFree = pEntry; +} + + +//////////////////////////////////////////////////////////////////////// +/// ITERATORS /// +//////////////////////////////////////////////////////////////////////// + +// iterator over the primary inputs +#define Hop_ManForEachPi( p, pObj, i ) \ + Vec_PtrForEachEntry( p->vPis, pObj, i ) +// iterator over the primary outputs +#define Hop_ManForEachPo( p, pObj, i ) \ + Vec_PtrForEachEntry( p->vPos, pObj, i ) +// iterator over all objects, including those currently not used +#define Hop_ManForEachNode( p, pObj, i ) \ + for ( i = 0; i < p->nTableSize; i++ ) \ + if ( ((pObj) = p->pTable[i]) == NULL ) {} else + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== aigBalance.c ========================================================*/ +extern Hop_Man_t * Hop_ManBalance( Hop_Man_t * p, int fUpdateLevel ); +extern Hop_Obj_t * Hop_NodeBalanceBuildSuper( Hop_Man_t * p, Vec_Ptr_t * vSuper, Hop_Type_t Type, int fUpdateLevel ); +/*=== aigCheck.c ========================================================*/ +extern int Hop_ManCheck( Hop_Man_t * p ); +/*=== aigDfs.c ==========================================================*/ +extern Vec_Ptr_t * Hop_ManDfs( Hop_Man_t * p ); +extern Vec_Ptr_t * Hop_ManDfsNode( Hop_Man_t * p, Hop_Obj_t * pNode ); +extern int Hop_ManCountLevels( Hop_Man_t * p ); +extern void Hop_ManCreateRefs( Hop_Man_t * p ); +extern int Hop_DagSize( Hop_Obj_t * pObj ); +extern void Hop_ConeUnmark_rec( Hop_Obj_t * pObj ); +extern Hop_Obj_t * Hop_Transfer( Hop_Man_t * pSour, Hop_Man_t * pDest, Hop_Obj_t * pObj, int nVars ); +extern Hop_Obj_t * Hop_Compose( Hop_Man_t * p, Hop_Obj_t * pRoot, Hop_Obj_t * pFunc, int iVar ); +/*=== aigMan.c ==========================================================*/ +extern Hop_Man_t * Hop_ManStart(); +extern Hop_Man_t * Hop_ManDup( Hop_Man_t * p ); +extern void Hop_ManStop( Hop_Man_t * p ); +extern int Hop_ManCleanup( Hop_Man_t * p ); +extern void Hop_ManPrintStats( Hop_Man_t * p ); +/*=== aigMem.c ==========================================================*/ +extern void Hop_ManStartMemory( Hop_Man_t * p ); +extern void Hop_ManStopMemory( Hop_Man_t * p ); +/*=== aigObj.c ==========================================================*/ +extern Hop_Obj_t * Hop_ObjCreatePi( Hop_Man_t * p ); +extern Hop_Obj_t * Hop_ObjCreatePo( Hop_Man_t * p, Hop_Obj_t * pDriver ); +extern Hop_Obj_t * Hop_ObjCreate( Hop_Man_t * p, Hop_Obj_t * pGhost ); +extern void Hop_ObjConnect( Hop_Man_t * p, Hop_Obj_t * pObj, Hop_Obj_t * pFan0, Hop_Obj_t * pFan1 ); +extern void Hop_ObjDisconnect( Hop_Man_t * p, Hop_Obj_t * pObj ); +extern void Hop_ObjDelete( Hop_Man_t * p, Hop_Obj_t * pObj ); +extern void Hop_ObjDelete_rec( Hop_Man_t * p, Hop_Obj_t * pObj ); +extern Hop_Obj_t * Hop_ObjRepr( Hop_Obj_t * pObj ); +extern void Hop_ObjCreateChoice( Hop_Obj_t * pOld, Hop_Obj_t * pNew ); +/*=== aigOper.c =========================================================*/ +extern Hop_Obj_t * Hop_IthVar( Hop_Man_t * p, int i ); +extern Hop_Obj_t * Hop_Oper( Hop_Man_t * p, Hop_Obj_t * p0, Hop_Obj_t * p1, Hop_Type_t Type ); +extern Hop_Obj_t * Hop_And( Hop_Man_t * p, Hop_Obj_t * p0, Hop_Obj_t * p1 ); +extern Hop_Obj_t * Hop_Or( Hop_Man_t * p, Hop_Obj_t * p0, Hop_Obj_t * p1 ); +extern Hop_Obj_t * Hop_Exor( Hop_Man_t * p, Hop_Obj_t * p0, Hop_Obj_t * p1 ); +extern Hop_Obj_t * Hop_Mux( Hop_Man_t * p, Hop_Obj_t * pC, Hop_Obj_t * p1, Hop_Obj_t * p0 ); +extern Hop_Obj_t * Hop_Maj( Hop_Man_t * p, Hop_Obj_t * pA, Hop_Obj_t * pB, Hop_Obj_t * pC ); +extern Hop_Obj_t * Hop_Miter( Hop_Man_t * p, Vec_Ptr_t * vPairs ); +extern Hop_Obj_t * Hop_CreateAnd( Hop_Man_t * p, int nVars ); +extern Hop_Obj_t * Hop_CreateOr( Hop_Man_t * p, int nVars ); +extern Hop_Obj_t * Hop_CreateExor( Hop_Man_t * p, int nVars ); +/*=== aigTable.c ========================================================*/ +extern Hop_Obj_t * Hop_TableLookup( Hop_Man_t * p, Hop_Obj_t * pGhost ); +extern void Hop_TableInsert( Hop_Man_t * p, Hop_Obj_t * pObj ); +extern void Hop_TableDelete( Hop_Man_t * p, Hop_Obj_t * pObj ); +extern int Hop_TableCountEntries( Hop_Man_t * p ); +extern void Hop_TableProfile( Hop_Man_t * p ); +/*=== aigUtil.c =========================================================*/ +extern void Hop_ManIncrementTravId( Hop_Man_t * p ); +extern void Hop_ManCleanData( Hop_Man_t * p ); +extern void Hop_ObjCleanData_rec( Hop_Obj_t * pObj ); +extern void Hop_ObjCollectMulti( Hop_Obj_t * pFunc, Vec_Ptr_t * vSuper ); +extern int Hop_ObjIsMuxType( Hop_Obj_t * pObj ); +extern int Hop_ObjRecognizeExor( Hop_Obj_t * pObj, Hop_Obj_t ** ppFan0, Hop_Obj_t ** ppFan1 ); +extern Hop_Obj_t * Hop_ObjRecognizeMux( Hop_Obj_t * pObj, Hop_Obj_t ** ppObjT, Hop_Obj_t ** ppObjE ); +extern void Hop_ObjPrintEqn( FILE * pFile, Hop_Obj_t * pObj, Vec_Vec_t * vLevels, int Level ); +extern void Hop_ObjPrintVerilog( FILE * pFile, Hop_Obj_t * pObj, Vec_Vec_t * vLevels, int Level ); +extern void Hop_ObjPrintVerbose( Hop_Obj_t * pObj, int fHaig ); +extern void Hop_ManPrintVerbose( Hop_Man_t * p, int fHaig ); +extern void Hop_ManDumpBlif( Hop_Man_t * p, char * pFileName ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/aig/hop/hopBalance.c b/abc_with_bb_support/src/aig/hop/hopBalance.c new file mode 100644 index 000000000..fe81e14e0 --- /dev/null +++ b/abc_with_bb_support/src/aig/hop/hopBalance.c @@ -0,0 +1,391 @@ +/**CFile**************************************************************** + + FileName [hopBalance.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Minimalistic And-Inverter Graph package.] + + Synopsis [Algebraic AIG balancing.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: hopBalance.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "hop.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Hop_Obj_t * Hop_NodeBalance_rec( Hop_Man_t * pNew, Hop_Obj_t * pObj, Vec_Vec_t * vStore, int Level, int fUpdateLevel ); +static Vec_Ptr_t * Hop_NodeBalanceCone( Hop_Obj_t * pObj, Vec_Vec_t * vStore, int Level ); +static int Hop_NodeBalanceFindLeft( Vec_Ptr_t * vSuper ); +static void Hop_NodeBalancePermute( Hop_Man_t * p, Vec_Ptr_t * vSuper, int LeftBound, int fExor ); +static void Hop_NodeBalancePushUniqueOrderByLevel( Vec_Ptr_t * vStore, Hop_Obj_t * pObj ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs algebraic balancing of the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Man_t * Hop_ManBalance( Hop_Man_t * p, int fUpdateLevel ) +{ + Hop_Man_t * pNew; + Hop_Obj_t * pObj, * pObjNew; + Vec_Vec_t * vStore; + int i; + // create the new manager + pNew = Hop_ManStart(); + pNew->fRefCount = 0; + // map the PI nodes + Hop_ManCleanData( p ); + Hop_ManConst1(p)->pData = Hop_ManConst1(pNew); + Hop_ManForEachPi( p, pObj, i ) + pObj->pData = Hop_ObjCreatePi(pNew); + // balance the AIG + vStore = Vec_VecAlloc( 50 ); + Hop_ManForEachPo( p, pObj, i ) + { + pObjNew = Hop_NodeBalance_rec( pNew, Hop_ObjFanin0(pObj), vStore, 0, fUpdateLevel ); + Hop_ObjCreatePo( pNew, Hop_NotCond( pObjNew, Hop_ObjFaninC0(pObj) ) ); + } + Vec_VecFree( vStore ); + // remove dangling nodes +// Hop_ManCreateRefs( pNew ); +// if ( i = Hop_ManCleanup( pNew ) ) +// printf( "Cleanup after balancing removed %d dangling nodes.\n", i ); + // check the resulting AIG + if ( !Hop_ManCheck(pNew) ) + printf( "Hop_ManBalance(): The check has failed.\n" ); + return pNew; +} + +/**Function************************************************************* + + Synopsis [Returns the new node constructed.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_NodeBalance_rec( Hop_Man_t * pNew, Hop_Obj_t * pObjOld, Vec_Vec_t * vStore, int Level, int fUpdateLevel ) +{ + Hop_Obj_t * pObjNew; + Vec_Ptr_t * vSuper; + int i; + assert( !Hop_IsComplement(pObjOld) ); + // return if the result is known + if ( pObjOld->pData ) + return pObjOld->pData; + assert( Hop_ObjIsNode(pObjOld) ); + // get the implication supergate + vSuper = Hop_NodeBalanceCone( pObjOld, vStore, Level ); + // check if supergate contains two nodes in the opposite polarity + if ( vSuper->nSize == 0 ) + return pObjOld->pData = Hop_ManConst0(pNew); + if ( Vec_PtrSize(vSuper) < 2 ) + printf( "BUG!\n" ); + // for each old node, derive the new well-balanced node + for ( i = 0; i < Vec_PtrSize(vSuper); i++ ) + { + pObjNew = Hop_NodeBalance_rec( pNew, Hop_Regular(vSuper->pArray[i]), vStore, Level + 1, fUpdateLevel ); + vSuper->pArray[i] = Hop_NotCond( pObjNew, Hop_IsComplement(vSuper->pArray[i]) ); + } + // build the supergate + pObjNew = Hop_NodeBalanceBuildSuper( pNew, vSuper, Hop_ObjType(pObjOld), fUpdateLevel ); + // make sure the balanced node is not assigned +// assert( pObjOld->Level >= Hop_Regular(pObjNew)->Level ); + assert( pObjOld->pData == NULL ); + return pObjOld->pData = pObjNew; +} + +/**Function************************************************************* + + Synopsis [Collects the nodes of the supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Hop_NodeBalanceCone_rec( Hop_Obj_t * pRoot, Hop_Obj_t * pObj, Vec_Ptr_t * vSuper ) +{ + int RetValue1, RetValue2, i; + // check if the node is visited + if ( Hop_Regular(pObj)->fMarkB ) + { + // check if the node occurs in the same polarity + for ( i = 0; i < vSuper->nSize; i++ ) + if ( vSuper->pArray[i] == pObj ) + return 1; + // check if the node is present in the opposite polarity + for ( i = 0; i < vSuper->nSize; i++ ) + if ( vSuper->pArray[i] == Hop_Not(pObj) ) + return -1; + assert( 0 ); + return 0; + } + // if the new node is complemented or a PI, another gate begins + if ( pObj != pRoot && (Hop_IsComplement(pObj) || Hop_ObjType(pObj) != Hop_ObjType(pRoot) || Hop_ObjRefs(pObj) > 1) ) + { + Vec_PtrPush( vSuper, pObj ); + Hop_Regular(pObj)->fMarkB = 1; + return 0; + } + assert( !Hop_IsComplement(pObj) ); + assert( Hop_ObjIsNode(pObj) ); + // go through the branches + RetValue1 = Hop_NodeBalanceCone_rec( pRoot, Hop_ObjChild0(pObj), vSuper ); + RetValue2 = Hop_NodeBalanceCone_rec( pRoot, Hop_ObjChild1(pObj), vSuper ); + if ( RetValue1 == -1 || RetValue2 == -1 ) + return -1; + // return 1 if at least one branch has a duplicate + return RetValue1 || RetValue2; +} + +/**Function************************************************************* + + Synopsis [Collects the nodes of the supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Hop_NodeBalanceCone( Hop_Obj_t * pObj, Vec_Vec_t * vStore, int Level ) +{ + Vec_Ptr_t * vNodes; + int RetValue, i; + assert( !Hop_IsComplement(pObj) ); + // extend the storage + if ( Vec_VecSize( vStore ) <= Level ) + Vec_VecPush( vStore, Level, 0 ); + // get the temporary array of nodes + vNodes = Vec_VecEntry( vStore, Level ); + Vec_PtrClear( vNodes ); + // collect the nodes in the implication supergate + RetValue = Hop_NodeBalanceCone_rec( pObj, pObj, vNodes ); + assert( vNodes->nSize > 1 ); + // unmark the visited nodes + Vec_PtrForEachEntry( vNodes, pObj, i ) + Hop_Regular(pObj)->fMarkB = 0; + // if we found the node and its complement in the same implication supergate, + // return empty set of nodes (meaning that we should use constant-0 node) + if ( RetValue == -1 ) + vNodes->nSize = 0; + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Procedure used for sorting the nodes in decreasing order of levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Hop_NodeCompareLevelsDecrease( Hop_Obj_t ** pp1, Hop_Obj_t ** pp2 ) +{ + int Diff = Hop_ObjLevel(Hop_Regular(*pp1)) - Hop_ObjLevel(Hop_Regular(*pp2)); + if ( Diff > 0 ) + return -1; + if ( Diff < 0 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Builds implication supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_NodeBalanceBuildSuper( Hop_Man_t * p, Vec_Ptr_t * vSuper, Hop_Type_t Type, int fUpdateLevel ) +{ + Hop_Obj_t * pObj1, * pObj2; + int LeftBound; + assert( vSuper->nSize > 1 ); + // sort the new nodes by level in the decreasing order + Vec_PtrSort( vSuper, Hop_NodeCompareLevelsDecrease ); + // balance the nodes + while ( vSuper->nSize > 1 ) + { + // find the left bound on the node to be paired + LeftBound = (!fUpdateLevel)? 0 : Hop_NodeBalanceFindLeft( vSuper ); + // find the node that can be shared (if no such node, randomize choice) + Hop_NodeBalancePermute( p, vSuper, LeftBound, Type == AIG_EXOR ); + // pull out the last two nodes + pObj1 = Vec_PtrPop(vSuper); + pObj2 = Vec_PtrPop(vSuper); + Hop_NodeBalancePushUniqueOrderByLevel( vSuper, Hop_Oper(p, pObj1, pObj2, Type) ); + } + return Vec_PtrEntry(vSuper, 0); +} + +/**Function************************************************************* + + Synopsis [Finds the left bound on the next candidate to be paired.] + + Description [The nodes in the array are in the decreasing order of levels. + The last node in the array has the smallest level. By default it would be paired + with the next node on the left. However, it may be possible to pair it with some + other node on the left, in such a way that the new node is shared. This procedure + finds the index of the left-most node, which can be paired with the last node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Hop_NodeBalanceFindLeft( Vec_Ptr_t * vSuper ) +{ + Hop_Obj_t * pObjRight, * pObjLeft; + int Current; + // if two or less nodes, pair with the first + if ( Vec_PtrSize(vSuper) < 3 ) + return 0; + // set the pointer to the one before the last + Current = Vec_PtrSize(vSuper) - 2; + pObjRight = Vec_PtrEntry( vSuper, Current ); + // go through the nodes to the left of this one + for ( Current--; Current >= 0; Current-- ) + { + // get the next node on the left + pObjLeft = Vec_PtrEntry( vSuper, Current ); + // if the level of this node is different, quit the loop + if ( Hop_ObjLevel(Hop_Regular(pObjLeft)) != Hop_ObjLevel(Hop_Regular(pObjRight)) ) + break; + } + Current++; + // get the node, for which the equality holds + pObjLeft = Vec_PtrEntry( vSuper, Current ); + assert( Hop_ObjLevel(Hop_Regular(pObjLeft)) == Hop_ObjLevel(Hop_Regular(pObjRight)) ); + return Current; +} + +/**Function************************************************************* + + Synopsis [Moves closer to the end the node that is best for sharing.] + + Description [If there is no node with sharing, randomly chooses one of + the legal nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_NodeBalancePermute( Hop_Man_t * p, Vec_Ptr_t * vSuper, int LeftBound, int fExor ) +{ + Hop_Obj_t * pObj1, * pObj2, * pObj3, * pGhost; + int RightBound, i; + // get the right bound + RightBound = Vec_PtrSize(vSuper) - 2; + assert( LeftBound <= RightBound ); + if ( LeftBound == RightBound ) + return; + // get the two last nodes + pObj1 = Vec_PtrEntry( vSuper, RightBound + 1 ); + pObj2 = Vec_PtrEntry( vSuper, RightBound ); + if ( Hop_Regular(pObj1) == p->pConst1 || Hop_Regular(pObj2) == p->pConst1 ) + return; + // find the first node that can be shared + for ( i = RightBound; i >= LeftBound; i-- ) + { + pObj3 = Vec_PtrEntry( vSuper, i ); + if ( Hop_Regular(pObj3) == p->pConst1 ) + { + Vec_PtrWriteEntry( vSuper, i, pObj2 ); + Vec_PtrWriteEntry( vSuper, RightBound, pObj3 ); + return; + } + pGhost = Hop_ObjCreateGhost( p, pObj1, pObj3, fExor? AIG_EXOR : AIG_AND ); + if ( Hop_TableLookup( p, pGhost ) ) + { + if ( pObj3 == pObj2 ) + return; + Vec_PtrWriteEntry( vSuper, i, pObj2 ); + Vec_PtrWriteEntry( vSuper, RightBound, pObj3 ); + return; + } + } +/* + // we did not find the node to share, randomize choice + { + int Choice = rand() % (RightBound - LeftBound + 1); + pObj3 = Vec_PtrEntry( vSuper, LeftBound + Choice ); + if ( pObj3 == pObj2 ) + return; + Vec_PtrWriteEntry( vSuper, LeftBound + Choice, pObj2 ); + Vec_PtrWriteEntry( vSuper, RightBound, pObj3 ); + } +*/ +} + +/**Function************************************************************* + + Synopsis [Inserts a new node in the order by levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_NodeBalancePushUniqueOrderByLevel( Vec_Ptr_t * vStore, Hop_Obj_t * pObj ) +{ + Hop_Obj_t * pObj1, * pObj2; + int i; + if ( Vec_PtrPushUnique(vStore, pObj) ) + return; + // find the p of the node + for ( i = vStore->nSize-1; i > 0; i-- ) + { + pObj1 = vStore->pArray[i ]; + pObj2 = vStore->pArray[i-1]; + if ( Hop_ObjLevel(Hop_Regular(pObj1)) <= Hop_ObjLevel(Hop_Regular(pObj2)) ) + break; + vStore->pArray[i ] = pObj2; + vStore->pArray[i-1] = pObj1; + } +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/hop/hopCheck.c b/abc_with_bb_support/src/aig/hop/hopCheck.c new file mode 100644 index 000000000..ddbdb1a90 --- /dev/null +++ b/abc_with_bb_support/src/aig/hop/hopCheck.c @@ -0,0 +1,110 @@ +/**CFile**************************************************************** + + FileName [hopCheck.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Minimalistic And-Inverter Graph package.] + + Synopsis [AIG checking procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: hopCheck.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "hop.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Checks the consistency of the AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Hop_ManCheck( Hop_Man_t * p ) +{ + Hop_Obj_t * pObj, * pObj2; + int i; + // check primary inputs + Hop_ManForEachPi( p, pObj, i ) + { + if ( Hop_ObjFanin0(pObj) || Hop_ObjFanin1(pObj) ) + { + printf( "Hop_ManCheck: The PI node \"%p\" has fanins.\n", pObj ); + return 0; + } + } + // check primary outputs + Hop_ManForEachPo( p, pObj, i ) + { + if ( !Hop_ObjFanin0(pObj) ) + { + printf( "Hop_ManCheck: The PO node \"%p\" has NULL fanin.\n", pObj ); + return 0; + } + if ( Hop_ObjFanin1(pObj) ) + { + printf( "Hop_ManCheck: The PO node \"%p\" has second fanin.\n", pObj ); + return 0; + } + } + // check internal nodes + Hop_ManForEachNode( p, pObj, i ) + { + if ( !Hop_ObjFanin0(pObj) || !Hop_ObjFanin1(pObj) ) + { + printf( "Hop_ManCheck: The AIG has internal node \"%p\" with a NULL fanin.\n", pObj ); + return 0; + } + if ( Hop_ObjFanin0(pObj)->Id >= Hop_ObjFanin1(pObj)->Id ) + { + printf( "Hop_ManCheck: The AIG has node \"%p\" with a wrong ordering of fanins.\n", pObj ); + return 0; + } + pObj2 = Hop_TableLookup( p, pObj ); + if ( pObj2 != pObj ) + { + printf( "Hop_ManCheck: Node \"%p\" is not in the structural hashing table.\n", pObj ); + return 0; + } + } + // count the total number of nodes + if ( Hop_ManObjNum(p) != 1 + Hop_ManPiNum(p) + Hop_ManPoNum(p) + Hop_ManAndNum(p) + Hop_ManExorNum(p) ) + { + printf( "Hop_ManCheck: The number of created nodes is wrong.\n" ); + return 0; + } + // count the number of nodes in the table + if ( Hop_TableCountEntries(p) != Hop_ManAndNum(p) + Hop_ManExorNum(p) ) + { + printf( "Hop_ManCheck: The number of nodes in the structural hashing table is wrong.\n" ); + return 0; + } +// if ( !Hop_ManIsAcyclic(p) ) +// return 0; + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/hop/hopDfs.c b/abc_with_bb_support/src/aig/hop/hopDfs.c new file mode 100644 index 000000000..7c293423f --- /dev/null +++ b/abc_with_bb_support/src/aig/hop/hopDfs.c @@ -0,0 +1,399 @@ +/**CFile**************************************************************** + + FileName [hopDfs.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Minimalistic And-Inverter Graph package.] + + Synopsis [DFS traversal procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: hopDfs.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "hop.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Collects internal nodes in the DFS order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ManDfs_rec( Hop_Obj_t * pObj, Vec_Ptr_t * vNodes ) +{ + assert( !Hop_IsComplement(pObj) ); + if ( !Hop_ObjIsNode(pObj) || Hop_ObjIsMarkA(pObj) ) + return; + Hop_ManDfs_rec( Hop_ObjFanin0(pObj), vNodes ); + Hop_ManDfs_rec( Hop_ObjFanin1(pObj), vNodes ); + assert( !Hop_ObjIsMarkA(pObj) ); // loop detection + Hop_ObjSetMarkA(pObj); + Vec_PtrPush( vNodes, pObj ); +} + +/**Function************************************************************* + + Synopsis [Collects internal nodes in the DFS order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Hop_ManDfs( Hop_Man_t * p ) +{ + Vec_Ptr_t * vNodes; + Hop_Obj_t * pObj; + int i; + vNodes = Vec_PtrAlloc( Hop_ManNodeNum(p) ); + Hop_ManForEachNode( p, pObj, i ) + Hop_ManDfs_rec( pObj, vNodes ); + Hop_ManForEachNode( p, pObj, i ) + Hop_ObjClearMarkA(pObj); + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Collects internal nodes in the DFS order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Hop_ManDfsNode( Hop_Man_t * p, Hop_Obj_t * pNode ) +{ + Vec_Ptr_t * vNodes; + Hop_Obj_t * pObj; + int i; + assert( !Hop_IsComplement(pNode) ); + vNodes = Vec_PtrAlloc( 16 ); + Hop_ManDfs_rec( pNode, vNodes ); + Vec_PtrForEachEntry( vNodes, pObj, i ) + Hop_ObjClearMarkA(pObj); + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Computes the max number of levels in the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Hop_ManCountLevels( Hop_Man_t * p ) +{ + Vec_Ptr_t * vNodes; + Hop_Obj_t * pObj; + int i, LevelsMax, Level0, Level1; + // initialize the levels + Hop_ManConst1(p)->pData = NULL; + Hop_ManForEachPi( p, pObj, i ) + pObj->pData = NULL; + // compute levels in a DFS order + vNodes = Hop_ManDfs( p ); + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + Level0 = (int)Hop_ObjFanin0(pObj)->pData; + Level1 = (int)Hop_ObjFanin1(pObj)->pData; + pObj->pData = (void *)(1 + Hop_ObjIsExor(pObj) + AIG_MAX(Level0, Level1)); + } + Vec_PtrFree( vNodes ); + // get levels of the POs + LevelsMax = 0; + Hop_ManForEachPo( p, pObj, i ) + LevelsMax = AIG_MAX( LevelsMax, (int)Hop_ObjFanin0(pObj)->pData ); + return LevelsMax; +} + +/**Function************************************************************* + + Synopsis [Creates correct reference counters at each node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ManCreateRefs( Hop_Man_t * p ) +{ + Hop_Obj_t * pObj; + int i; + if ( p->fRefCount ) + return; + p->fRefCount = 1; + // clear refs + Hop_ObjClearRef( Hop_ManConst1(p) ); + Hop_ManForEachPi( p, pObj, i ) + Hop_ObjClearRef( pObj ); + Hop_ManForEachNode( p, pObj, i ) + Hop_ObjClearRef( pObj ); + Hop_ManForEachPo( p, pObj, i ) + Hop_ObjClearRef( pObj ); + // set refs + Hop_ManForEachNode( p, pObj, i ) + { + Hop_ObjRef( Hop_ObjFanin0(pObj) ); + Hop_ObjRef( Hop_ObjFanin1(pObj) ); + } + Hop_ManForEachPo( p, pObj, i ) + Hop_ObjRef( Hop_ObjFanin0(pObj) ); +} + +/**Function************************************************************* + + Synopsis [Counts the number of AIG nodes rooted at this cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ConeMark_rec( Hop_Obj_t * pObj ) +{ + assert( !Hop_IsComplement(pObj) ); + if ( !Hop_ObjIsNode(pObj) || Hop_ObjIsMarkA(pObj) ) + return; + Hop_ConeMark_rec( Hop_ObjFanin0(pObj) ); + Hop_ConeMark_rec( Hop_ObjFanin1(pObj) ); + assert( !Hop_ObjIsMarkA(pObj) ); // loop detection + Hop_ObjSetMarkA( pObj ); +} + +/**Function************************************************************* + + Synopsis [Counts the number of AIG nodes rooted at this cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ConeCleanAndMark_rec( Hop_Obj_t * pObj ) +{ + assert( !Hop_IsComplement(pObj) ); + if ( !Hop_ObjIsNode(pObj) || Hop_ObjIsMarkA(pObj) ) + return; + Hop_ConeCleanAndMark_rec( Hop_ObjFanin0(pObj) ); + Hop_ConeCleanAndMark_rec( Hop_ObjFanin1(pObj) ); + assert( !Hop_ObjIsMarkA(pObj) ); // loop detection + Hop_ObjSetMarkA( pObj ); + pObj->pData = NULL; +} + +/**Function************************************************************* + + Synopsis [Counts the number of AIG nodes rooted at this cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Hop_ConeCountAndMark_rec( Hop_Obj_t * pObj ) +{ + int Counter; + assert( !Hop_IsComplement(pObj) ); + if ( !Hop_ObjIsNode(pObj) || Hop_ObjIsMarkA(pObj) ) + return 0; + Counter = 1 + Hop_ConeCountAndMark_rec( Hop_ObjFanin0(pObj) ) + + Hop_ConeCountAndMark_rec( Hop_ObjFanin1(pObj) ); + assert( !Hop_ObjIsMarkA(pObj) ); // loop detection + Hop_ObjSetMarkA( pObj ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Counts the number of AIG nodes rooted at this cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ConeUnmark_rec( Hop_Obj_t * pObj ) +{ + assert( !Hop_IsComplement(pObj) ); + if ( !Hop_ObjIsNode(pObj) || !Hop_ObjIsMarkA(pObj) ) + return; + Hop_ConeUnmark_rec( Hop_ObjFanin0(pObj) ); + Hop_ConeUnmark_rec( Hop_ObjFanin1(pObj) ); + assert( Hop_ObjIsMarkA(pObj) ); // loop detection + Hop_ObjClearMarkA( pObj ); +} + +/**Function************************************************************* + + Synopsis [Counts the number of AIG nodes rooted at this cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Hop_DagSize( Hop_Obj_t * pObj ) +{ + int Counter; + Counter = Hop_ConeCountAndMark_rec( Hop_Regular(pObj) ); + Hop_ConeUnmark_rec( Hop_Regular(pObj) ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Transfers the AIG from one manager into another.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_Transfer_rec( Hop_Man_t * pDest, Hop_Obj_t * pObj ) +{ + assert( !Hop_IsComplement(pObj) ); + if ( !Hop_ObjIsNode(pObj) || Hop_ObjIsMarkA(pObj) ) + return; + Hop_Transfer_rec( pDest, Hop_ObjFanin0(pObj) ); + Hop_Transfer_rec( pDest, Hop_ObjFanin1(pObj) ); + pObj->pData = Hop_And( pDest, Hop_ObjChild0Copy(pObj), Hop_ObjChild1Copy(pObj) ); + assert( !Hop_ObjIsMarkA(pObj) ); // loop detection + Hop_ObjSetMarkA( pObj ); +} + +/**Function************************************************************* + + Synopsis [Transfers the AIG from one manager into another.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_Transfer( Hop_Man_t * pSour, Hop_Man_t * pDest, Hop_Obj_t * pRoot, int nVars ) +{ + Hop_Obj_t * pObj; + int i; + // solve simple cases + if ( pSour == pDest ) + return pRoot; + if ( Hop_ObjIsConst1( Hop_Regular(pRoot) ) ) + return Hop_NotCond( Hop_ManConst1(pDest), Hop_IsComplement(pRoot) ); + // set the PI mapping + Hop_ManForEachPi( pSour, pObj, i ) + { + if ( i == nVars ) + break; + pObj->pData = Hop_IthVar(pDest, i); + } + // transfer and set markings + Hop_Transfer_rec( pDest, Hop_Regular(pRoot) ); + // clear the markings + Hop_ConeUnmark_rec( Hop_Regular(pRoot) ); + return Hop_NotCond( Hop_Regular(pRoot)->pData, Hop_IsComplement(pRoot) ); +} + +/**Function************************************************************* + + Synopsis [Composes the AIG (pRoot) with the function (pFunc) using PI var (iVar).] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_Compose_rec( Hop_Man_t * p, Hop_Obj_t * pObj, Hop_Obj_t * pFunc, Hop_Obj_t * pVar ) +{ + assert( !Hop_IsComplement(pObj) ); + if ( Hop_ObjIsMarkA(pObj) ) + return; + if ( Hop_ObjIsConst1(pObj) || Hop_ObjIsPi(pObj) ) + { + pObj->pData = pObj == pVar ? pFunc : pObj; + return; + } + Hop_Compose_rec( p, Hop_ObjFanin0(pObj), pFunc, pVar ); + Hop_Compose_rec( p, Hop_ObjFanin1(pObj), pFunc, pVar ); + pObj->pData = Hop_And( p, Hop_ObjChild0Copy(pObj), Hop_ObjChild1Copy(pObj) ); + assert( !Hop_ObjIsMarkA(pObj) ); // loop detection + Hop_ObjSetMarkA( pObj ); +} + +/**Function************************************************************* + + Synopsis [Composes the AIG (pRoot) with the function (pFunc) using PI var (iVar).] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_Compose( Hop_Man_t * p, Hop_Obj_t * pRoot, Hop_Obj_t * pFunc, int iVar ) +{ + // quit if the PI variable is not defined + if ( iVar >= Hop_ManPiNum(p) ) + { + printf( "Hop_Compose(): The PI variable %d is not defined.\n", iVar ); + return NULL; + } + // recursively perform composition + Hop_Compose_rec( p, Hop_Regular(pRoot), pFunc, Hop_ManPi(p, iVar) ); + // clear the markings + Hop_ConeUnmark_rec( Hop_Regular(pRoot) ); + return Hop_NotCond( Hop_Regular(pRoot)->pData, Hop_IsComplement(pRoot) ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/hop/hopMan.c b/abc_with_bb_support/src/aig/hop/hopMan.c new file mode 100644 index 000000000..a689704d7 --- /dev/null +++ b/abc_with_bb_support/src/aig/hop/hopMan.c @@ -0,0 +1,164 @@ +/**CFile**************************************************************** + + FileName [hopMan.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Minimalistic And-Inverter Graph package.] + + Synopsis [AIG manager.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: hopMan.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "hop.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Man_t * Hop_ManStart() +{ + Hop_Man_t * p; + // start the manager + p = ALLOC( Hop_Man_t, 1 ); + memset( p, 0, sizeof(Hop_Man_t) ); + // perform initializations + p->nTravIds = 1; + p->fRefCount = 1; + p->fCatchExor = 0; + // allocate arrays for nodes + p->vPis = Vec_PtrAlloc( 100 ); + p->vPos = Vec_PtrAlloc( 100 ); + // prepare the internal memory manager + Hop_ManStartMemory( p ); + // create the constant node + p->pConst1 = Hop_ManFetchMemory( p ); + p->pConst1->Type = AIG_CONST1; + p->pConst1->fPhase = 1; + p->nCreated = 1; + // start the table +// p->nTableSize = 107; + p->nTableSize = 10007; + p->pTable = ALLOC( Hop_Obj_t *, p->nTableSize ); + memset( p->pTable, 0, sizeof(Hop_Obj_t *) * p->nTableSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ManStop( Hop_Man_t * p ) +{ + Hop_Obj_t * pObj; + int i; + // make sure the nodes have clean marks + pObj = Hop_ManConst1(p); + assert( !pObj->fMarkA && !pObj->fMarkB ); + Hop_ManForEachPi( p, pObj, i ) + assert( !pObj->fMarkA && !pObj->fMarkB ); + Hop_ManForEachPo( p, pObj, i ) + assert( !pObj->fMarkA && !pObj->fMarkB ); + Hop_ManForEachNode( p, pObj, i ) + assert( !pObj->fMarkA && !pObj->fMarkB ); + // print time + if ( p->time1 ) { PRT( "time1", p->time1 ); } + if ( p->time2 ) { PRT( "time2", p->time2 ); } +// Hop_TableProfile( p ); + if ( p->vChunks ) Hop_ManStopMemory( p ); + if ( p->vPis ) Vec_PtrFree( p->vPis ); + if ( p->vPos ) Vec_PtrFree( p->vPos ); + if ( p->vObjs ) Vec_PtrFree( p->vObjs ); + free( p->pTable ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Returns the number of dangling nodes removed.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Hop_ManCleanup( Hop_Man_t * p ) +{ + Vec_Ptr_t * vObjs; + Hop_Obj_t * pNode; + int i, nNodesOld; + assert( p->fRefCount ); + nNodesOld = Hop_ManNodeNum(p); + // collect roots of dangling nodes + vObjs = Vec_PtrAlloc( 100 ); + Hop_ManForEachNode( p, pNode, i ) + if ( Hop_ObjRefs(pNode) == 0 ) + Vec_PtrPush( vObjs, pNode ); + // recursively remove dangling nodes + Vec_PtrForEachEntry( vObjs, pNode, i ) + Hop_ObjDelete_rec( p, pNode ); + Vec_PtrFree( vObjs ); + return nNodesOld - Hop_ManNodeNum(p); +} + +/**Function************************************************************* + + Synopsis [Stops the AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ManPrintStats( Hop_Man_t * p ) +{ + printf( "PI/PO = %d/%d. ", Hop_ManPiNum(p), Hop_ManPoNum(p) ); + printf( "A = %7d. ", Hop_ManAndNum(p) ); + printf( "X = %5d. ", Hop_ManExorNum(p) ); + printf( "Cre = %7d. ", p->nCreated ); + printf( "Del = %7d. ", p->nDeleted ); + printf( "Lev = %3d. ", Hop_ManCountLevels(p) ); + printf( "\n" ); +} + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/hop/hopMem.c b/abc_with_bb_support/src/aig/hop/hopMem.c new file mode 100644 index 000000000..6d6ebfc61 --- /dev/null +++ b/abc_with_bb_support/src/aig/hop/hopMem.c @@ -0,0 +1,115 @@ +/**CFile**************************************************************** + + FileName [hopMem.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Minimalistic And-Inverter Graph package.] + + Synopsis [Memory management for the AIG nodes.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: hopMem.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "hop.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// memory management +#define IVY_PAGE_SIZE 12 // page size containing 2^IVY_PAGE_SIZE nodes +#define IVY_PAGE_MASK 4095 // page bitmask (2^IVY_PAGE_SIZE)-1 + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the internal memory manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ManStartMemory( Hop_Man_t * p ) +{ + p->vChunks = Vec_PtrAlloc( 128 ); + p->vPages = Vec_PtrAlloc( 128 ); +} + +/**Function************************************************************* + + Synopsis [Stops the internal memory manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ManStopMemory( Hop_Man_t * p ) +{ + void * pMemory; + int i; + Vec_PtrForEachEntry( p->vChunks, pMemory, i ) + free( pMemory ); + Vec_PtrFree( p->vChunks ); + Vec_PtrFree( p->vPages ); + p->pListFree = NULL; +} + +/**Function************************************************************* + + Synopsis [Allocates additional memory for the nodes.] + + Description [Allocates IVY_PAGE_SIZE nodes. Aligns memory by 32 bytes. + Records the pointer to the AIG manager in the -1 entry.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ManAddMemory( Hop_Man_t * p ) +{ + char * pMemory; + int i, nBytes; + assert( sizeof(Hop_Obj_t) <= 64 ); + assert( p->pListFree == NULL ); +// assert( (Hop_ManObjNum(p) & IVY_PAGE_MASK) == 0 ); + // allocate new memory page + nBytes = sizeof(Hop_Obj_t) * (1<vChunks, pMemory ); + // align memory at the 32-byte boundary + pMemory = pMemory + 64 - (((int)pMemory) & 63); + // remember the manager in the first entry + Vec_PtrPush( p->vPages, pMemory ); + // break the memory down into nodes + p->pListFree = (Hop_Obj_t *)pMemory; + for ( i = 1; i <= IVY_PAGE_MASK; i++ ) + { + *((char **)pMemory) = pMemory + sizeof(Hop_Obj_t); + pMemory += sizeof(Hop_Obj_t); + } + *((char **)pMemory) = NULL; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/hop/hopObj.c b/abc_with_bb_support/src/aig/hop/hopObj.c new file mode 100644 index 000000000..7276be063 --- /dev/null +++ b/abc_with_bb_support/src/aig/hop/hopObj.c @@ -0,0 +1,271 @@ +/**CFile**************************************************************** + + FileName [hopObj.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Minimalistic And-Inverter Graph package.] + + Synopsis [Adding/removing objects.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: hopObj.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "hop.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Creates primary input.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_ObjCreatePi( Hop_Man_t * p ) +{ + Hop_Obj_t * pObj; + pObj = Hop_ManFetchMemory( p ); + pObj->Type = AIG_PI; + Vec_PtrPush( p->vPis, pObj ); + p->nObjs[AIG_PI]++; + return pObj; +} + +/**Function************************************************************* + + Synopsis [Creates primary output with the given driver.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_ObjCreatePo( Hop_Man_t * p, Hop_Obj_t * pDriver ) +{ + Hop_Obj_t * pObj; + pObj = Hop_ManFetchMemory( p ); + pObj->Type = AIG_PO; + Vec_PtrPush( p->vPos, pObj ); + // add connections + pObj->pFanin0 = pDriver; + if ( p->fRefCount ) + Hop_ObjRef( Hop_Regular(pDriver) ); + else + pObj->nRefs = Hop_ObjLevel( Hop_Regular(pDriver) ); + // set the phase + pObj->fPhase = Hop_ObjFaninPhase(pDriver); + // update node counters of the manager + p->nObjs[AIG_PO]++; + return pObj; +} + +/**Function************************************************************* + + Synopsis [Create the new node assuming it does not exist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_ObjCreate( Hop_Man_t * p, Hop_Obj_t * pGhost ) +{ + Hop_Obj_t * pObj; + assert( !Hop_IsComplement(pGhost) ); + assert( Hop_ObjIsNode(pGhost) ); + assert( pGhost == &p->Ghost ); + // get memory for the new object + pObj = Hop_ManFetchMemory( p ); + pObj->Type = pGhost->Type; + // add connections + Hop_ObjConnect( p, pObj, pGhost->pFanin0, pGhost->pFanin1 ); + // update node counters of the manager + p->nObjs[Hop_ObjType(pObj)]++; + assert( pObj->pData == NULL ); + return pObj; +} + +/**Function************************************************************* + + Synopsis [Connect the object to the fanin.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ObjConnect( Hop_Man_t * p, Hop_Obj_t * pObj, Hop_Obj_t * pFan0, Hop_Obj_t * pFan1 ) +{ + assert( !Hop_IsComplement(pObj) ); + assert( Hop_ObjIsNode(pObj) ); + // add the first fanin + pObj->pFanin0 = pFan0; + pObj->pFanin1 = pFan1; + // increment references of the fanins and add their fanouts + if ( p->fRefCount ) + { + if ( pFan0 != NULL ) + Hop_ObjRef( Hop_ObjFanin0(pObj) ); + if ( pFan1 != NULL ) + Hop_ObjRef( Hop_ObjFanin1(pObj) ); + } + else + pObj->nRefs = Hop_ObjLevelNew( pObj ); + // set the phase + pObj->fPhase = Hop_ObjFaninPhase(pFan0) & Hop_ObjFaninPhase(pFan1); + // add the node to the structural hash table + Hop_TableInsert( p, pObj ); +} + +/**Function************************************************************* + + Synopsis [Connect the object to the fanin.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ObjDisconnect( Hop_Man_t * p, Hop_Obj_t * pObj ) +{ + assert( !Hop_IsComplement(pObj) ); + assert( Hop_ObjIsNode(pObj) ); + // remove connections + if ( pObj->pFanin0 != NULL ) + Hop_ObjDeref(Hop_ObjFanin0(pObj)); + if ( pObj->pFanin1 != NULL ) + Hop_ObjDeref(Hop_ObjFanin1(pObj)); + // remove the node from the structural hash table + Hop_TableDelete( p, pObj ); + // add the first fanin + pObj->pFanin0 = NULL; + pObj->pFanin1 = NULL; +} + +/**Function************************************************************* + + Synopsis [Deletes the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ObjDelete( Hop_Man_t * p, Hop_Obj_t * pObj ) +{ + assert( !Hop_IsComplement(pObj) ); + assert( !Hop_ObjIsTerm(pObj) ); + assert( Hop_ObjRefs(pObj) == 0 ); + // update node counters of the manager + p->nObjs[pObj->Type]--; + p->nDeleted++; + // remove connections + Hop_ObjDisconnect( p, pObj ); + // remove PIs/POs from the arrays + if ( Hop_ObjIsPi(pObj) ) + Vec_PtrRemove( p->vPis, pObj ); + // free the node + Hop_ManRecycleMemory( p, pObj ); +} + +/**Function************************************************************* + + Synopsis [Deletes the MFFC of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ObjDelete_rec( Hop_Man_t * p, Hop_Obj_t * pObj ) +{ + Hop_Obj_t * pFanin0, * pFanin1; + assert( !Hop_IsComplement(pObj) ); + if ( Hop_ObjIsConst1(pObj) || Hop_ObjIsPi(pObj) ) + return; + assert( Hop_ObjIsNode(pObj) ); + pFanin0 = Hop_ObjFanin0(pObj); + pFanin1 = Hop_ObjFanin1(pObj); + Hop_ObjDelete( p, pObj ); + if ( pFanin0 && !Hop_ObjIsNone(pFanin0) && Hop_ObjRefs(pFanin0) == 0 ) + Hop_ObjDelete_rec( p, pFanin0 ); + if ( pFanin1 && !Hop_ObjIsNone(pFanin1) && Hop_ObjRefs(pFanin1) == 0 ) + Hop_ObjDelete_rec( p, pFanin1 ); +} + +/**Function************************************************************* + + Synopsis [Returns the representative of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_ObjRepr( Hop_Obj_t * pObj ) +{ + assert( !Hop_IsComplement(pObj) ); + if ( pObj->pData == NULL || pObj->pData == pObj ) + return pObj; + return Hop_ObjRepr( pObj->pData ); +} + +/**Function************************************************************* + + Synopsis [Sets an equivalence relation between the nodes.] + + Description [Makes the representative of pNew point to the representaive of pOld.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ObjCreateChoice( Hop_Obj_t * pOld, Hop_Obj_t * pNew ) +{ + Hop_Obj_t * pOldRepr; + Hop_Obj_t * pNewRepr; + assert( pOld != NULL && pNew != NULL ); + pOldRepr = Hop_ObjRepr(pOld); + pNewRepr = Hop_ObjRepr(pNew); + if ( pNewRepr != pOldRepr ) + pNewRepr->pData = pOldRepr; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/hop/hopOper.c b/abc_with_bb_support/src/aig/hop/hopOper.c new file mode 100644 index 000000000..067e060a4 --- /dev/null +++ b/abc_with_bb_support/src/aig/hop/hopOper.c @@ -0,0 +1,373 @@ +/**CFile**************************************************************** + + FileName [hopOper.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Minimalistic And-Inverter Graph package.] + + Synopsis [AIG operations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: hopOper.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "hop.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// procedure to detect an EXOR gate +static inline int Hop_ObjIsExorType( Hop_Obj_t * p0, Hop_Obj_t * p1, Hop_Obj_t ** ppFan0, Hop_Obj_t ** ppFan1 ) +{ + if ( !Hop_IsComplement(p0) || !Hop_IsComplement(p1) ) + return 0; + p0 = Hop_Regular(p0); + p1 = Hop_Regular(p1); + if ( !Hop_ObjIsAnd(p0) || !Hop_ObjIsAnd(p1) ) + return 0; + if ( Hop_ObjFanin0(p0) != Hop_ObjFanin0(p1) || Hop_ObjFanin1(p0) != Hop_ObjFanin1(p1) ) + return 0; + if ( Hop_ObjFaninC0(p0) == Hop_ObjFaninC0(p1) || Hop_ObjFaninC1(p0) == Hop_ObjFaninC1(p1) ) + return 0; + *ppFan0 = Hop_ObjChild0(p0); + *ppFan1 = Hop_ObjChild1(p0); + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Returns i-th elementary variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_IthVar( Hop_Man_t * p, int i ) +{ + int v; + for ( v = Hop_ManPiNum(p); v <= i; v++ ) + Hop_ObjCreatePi( p ); + assert( i < Vec_PtrSize(p->vPis) ); + return Hop_ManPi( p, i ); +} + +/**Function************************************************************* + + Synopsis [Perform one operation.] + + Description [The argument nodes can be complemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_Oper( Hop_Man_t * p, Hop_Obj_t * p0, Hop_Obj_t * p1, Hop_Type_t Type ) +{ + if ( Type == AIG_AND ) + return Hop_And( p, p0, p1 ); + if ( Type == AIG_EXOR ) + return Hop_Exor( p, p0, p1 ); + assert( 0 ); + return NULL; +} + +/**Function************************************************************* + + Synopsis [Performs canonicization step.] + + Description [The argument nodes can be complemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_And( Hop_Man_t * p, Hop_Obj_t * p0, Hop_Obj_t * p1 ) +{ + Hop_Obj_t * pGhost, * pResult; +// Hop_Obj_t * pFan0, * pFan1; + // check trivial cases + if ( p0 == p1 ) + return p0; + if ( p0 == Hop_Not(p1) ) + return Hop_Not(p->pConst1); + if ( Hop_Regular(p0) == p->pConst1 ) + return p0 == p->pConst1 ? p1 : Hop_Not(p->pConst1); + if ( Hop_Regular(p1) == p->pConst1 ) + return p1 == p->pConst1 ? p0 : Hop_Not(p->pConst1); + // check if it can be an EXOR gate +// if ( Hop_ObjIsExorType( p0, p1, &pFan0, &pFan1 ) ) +// return Hop_Exor( p, pFan0, pFan1 ); + // check the table + pGhost = Hop_ObjCreateGhost( p, p0, p1, AIG_AND ); + if ( pResult = Hop_TableLookup( p, pGhost ) ) + return pResult; + return Hop_ObjCreate( p, pGhost ); +} + +/**Function************************************************************* + + Synopsis [Performs canonicization step.] + + Description [The argument nodes can be complemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_Exor( Hop_Man_t * p, Hop_Obj_t * p0, Hop_Obj_t * p1 ) +{ +/* + Hop_Obj_t * pGhost, * pResult; + // check trivial cases + if ( p0 == p1 ) + return Hop_Not(p->pConst1); + if ( p0 == Hop_Not(p1) ) + return p->pConst1; + if ( Hop_Regular(p0) == p->pConst1 ) + return Hop_NotCond( p1, p0 == p->pConst1 ); + if ( Hop_Regular(p1) == p->pConst1 ) + return Hop_NotCond( p0, p1 == p->pConst1 ); + // check the table + pGhost = Hop_ObjCreateGhost( p, p0, p1, AIG_EXOR ); + if ( pResult = Hop_TableLookup( p, pGhost ) ) + return pResult; + return Hop_ObjCreate( p, pGhost ); +*/ + return Hop_Or( p, Hop_And(p, p0, Hop_Not(p1)), Hop_And(p, Hop_Not(p0), p1) ); +} + +/**Function************************************************************* + + Synopsis [Implements Boolean OR.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_Or( Hop_Man_t * p, Hop_Obj_t * p0, Hop_Obj_t * p1 ) +{ + return Hop_Not( Hop_And( p, Hop_Not(p0), Hop_Not(p1) ) ); +} + +/**Function************************************************************* + + Synopsis [Implements ITE operation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_Mux( Hop_Man_t * p, Hop_Obj_t * pC, Hop_Obj_t * p1, Hop_Obj_t * p0 ) +{ +/* + Hop_Obj_t * pTempA1, * pTempA2, * pTempB1, * pTempB2, * pTemp; + int Count0, Count1; + // consider trivial cases + if ( p0 == Hop_Not(p1) ) + return Hop_Exor( p, pC, p0 ); + // other cases can be added + // implement the first MUX (F = C * x1 + C' * x0) + + // check for constants here!!! + + pTempA1 = Hop_TableLookup( p, Hop_ObjCreateGhost(p, pC, p1, AIG_AND) ); + pTempA2 = Hop_TableLookup( p, Hop_ObjCreateGhost(p, Hop_Not(pC), p0, AIG_AND) ); + if ( pTempA1 && pTempA2 ) + { + pTemp = Hop_TableLookup( p, Hop_ObjCreateGhost(p, Hop_Not(pTempA1), Hop_Not(pTempA2), AIG_AND) ); + if ( pTemp ) return Hop_Not(pTemp); + } + Count0 = (pTempA1 != NULL) + (pTempA2 != NULL); + // implement the second MUX (F' = C * x1' + C' * x0') + pTempB1 = Hop_TableLookup( p, Hop_ObjCreateGhost(p, pC, Hop_Not(p1), AIG_AND) ); + pTempB2 = Hop_TableLookup( p, Hop_ObjCreateGhost(p, Hop_Not(pC), Hop_Not(p0), AIG_AND) ); + if ( pTempB1 && pTempB2 ) + { + pTemp = Hop_TableLookup( p, Hop_ObjCreateGhost(p, Hop_Not(pTempB1), Hop_Not(pTempB2), AIG_AND) ); + if ( pTemp ) return pTemp; + } + Count1 = (pTempB1 != NULL) + (pTempB2 != NULL); + // compare and decide which one to implement + if ( Count0 >= Count1 ) + { + pTempA1 = pTempA1? pTempA1 : Hop_And(p, pC, p1); + pTempA2 = pTempA2? pTempA2 : Hop_And(p, Hop_Not(pC), p0); + return Hop_Or( p, pTempA1, pTempA2 ); + } + pTempB1 = pTempB1? pTempB1 : Hop_And(p, pC, Hop_Not(p1)); + pTempB2 = pTempB2? pTempB2 : Hop_And(p, Hop_Not(pC), Hop_Not(p0)); + return Hop_Not( Hop_Or( p, pTempB1, pTempB2 ) ); +*/ + return Hop_Or( p, Hop_And(p, pC, p1), Hop_And(p, Hop_Not(pC), p0) ); +} + +/**Function************************************************************* + + Synopsis [Implements ITE operation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_Maj( Hop_Man_t * p, Hop_Obj_t * pA, Hop_Obj_t * pB, Hop_Obj_t * pC ) +{ + return Hop_Or( p, Hop_Or(p, Hop_And(p, pA, pB), Hop_And(p, pA, pC)), Hop_And(p, pB, pC) ); +} + +/**Function************************************************************* + + Synopsis [Constructs the well-balanced tree of gates.] + + Description [Disregards levels and possible logic sharing.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_Multi_rec( Hop_Man_t * p, Hop_Obj_t ** ppObjs, int nObjs, Hop_Type_t Type ) +{ + Hop_Obj_t * pObj1, * pObj2; + if ( nObjs == 1 ) + return ppObjs[0]; + pObj1 = Hop_Multi_rec( p, ppObjs, nObjs/2, Type ); + pObj2 = Hop_Multi_rec( p, ppObjs + nObjs/2, nObjs - nObjs/2, Type ); + return Hop_Oper( p, pObj1, pObj2, Type ); +} + +/**Function************************************************************* + + Synopsis [Old code.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_Multi( Hop_Man_t * p, Hop_Obj_t ** pArgs, int nArgs, Hop_Type_t Type ) +{ + assert( Type == AIG_AND || Type == AIG_EXOR ); + assert( nArgs > 0 ); + return Hop_Multi_rec( p, pArgs, nArgs, Type ); +} + +/**Function************************************************************* + + Synopsis [Implements the miter.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_Miter( Hop_Man_t * p, Vec_Ptr_t * vPairs ) +{ + int i; + assert( vPairs->nSize > 0 ); + assert( vPairs->nSize % 2 == 0 ); + // go through the cubes of the node's SOP + for ( i = 0; i < vPairs->nSize; i += 2 ) + vPairs->pArray[i/2] = Hop_Not( Hop_Exor( p, vPairs->pArray[i], vPairs->pArray[i+1] ) ); + vPairs->nSize = vPairs->nSize/2; + return Hop_Not( Hop_Multi_rec( p, (Hop_Obj_t **)vPairs->pArray, vPairs->nSize, AIG_AND ) ); +} + +/**Function************************************************************* + + Synopsis [Creates AND function with nVars inputs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_CreateAnd( Hop_Man_t * p, int nVars ) +{ + Hop_Obj_t * pFunc; + int i; + pFunc = Hop_ManConst1( p ); + for ( i = 0; i < nVars; i++ ) + pFunc = Hop_And( p, pFunc, Hop_IthVar(p, i) ); + return pFunc; +} + +/**Function************************************************************* + + Synopsis [Creates AND function with nVars inputs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_CreateOr( Hop_Man_t * p, int nVars ) +{ + Hop_Obj_t * pFunc; + int i; + pFunc = Hop_ManConst0( p ); + for ( i = 0; i < nVars; i++ ) + pFunc = Hop_Or( p, pFunc, Hop_IthVar(p, i) ); + return pFunc; +} + +/**Function************************************************************* + + Synopsis [Creates AND function with nVars inputs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_CreateExor( Hop_Man_t * p, int nVars ) +{ + Hop_Obj_t * pFunc; + int i; + pFunc = Hop_ManConst0( p ); + for ( i = 0; i < nVars; i++ ) + pFunc = Hop_Exor( p, pFunc, Hop_IthVar(p, i) ); + return pFunc; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/hop/hopTable.c b/abc_with_bb_support/src/aig/hop/hopTable.c new file mode 100644 index 000000000..818897e2b --- /dev/null +++ b/abc_with_bb_support/src/aig/hop/hopTable.c @@ -0,0 +1,262 @@ +/**CFile**************************************************************** + + FileName [hopTable.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Minimalistic And-Inverter Graph package.] + + Synopsis [Structural hashing table.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006. ] + + Revision [$Id: hopTable.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "hop.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// hashing the node +static unsigned long Hop_Hash( Hop_Obj_t * pObj, int TableSize ) +{ + unsigned long Key = Hop_ObjIsExor(pObj) * 1699; + Key ^= (long)Hop_ObjFanin0(pObj) * 7937; + Key ^= (long)Hop_ObjFanin1(pObj) * 2971; + Key ^= Hop_ObjFaninC0(pObj) * 911; + Key ^= Hop_ObjFaninC1(pObj) * 353; + return Key % TableSize; +} + +// returns the place where this node is stored (or should be stored) +static Hop_Obj_t ** Hop_TableFind( Hop_Man_t * p, Hop_Obj_t * pObj ) +{ + Hop_Obj_t ** ppEntry; + assert( Hop_ObjChild0(pObj) && Hop_ObjChild1(pObj) ); + assert( Hop_ObjFanin0(pObj)->Id < Hop_ObjFanin1(pObj)->Id ); + for ( ppEntry = p->pTable + Hop_Hash(pObj, p->nTableSize); *ppEntry; ppEntry = &(*ppEntry)->pNext ) + if ( *ppEntry == pObj ) + return ppEntry; + assert( *ppEntry == NULL ); + return ppEntry; +} + +static void Hop_TableResize( Hop_Man_t * p ); +static unsigned int Cudd_PrimeAig( unsigned int p ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Checks if a node with the given attributes is in the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_TableLookup( Hop_Man_t * p, Hop_Obj_t * pGhost ) +{ + Hop_Obj_t * pEntry; + assert( !Hop_IsComplement(pGhost) ); + assert( Hop_ObjChild0(pGhost) && Hop_ObjChild1(pGhost) ); + assert( Hop_ObjFanin0(pGhost)->Id < Hop_ObjFanin1(pGhost)->Id ); + if ( p->fRefCount && (!Hop_ObjRefs(Hop_ObjFanin0(pGhost)) || !Hop_ObjRefs(Hop_ObjFanin1(pGhost))) ) + return NULL; + for ( pEntry = p->pTable[Hop_Hash(pGhost, p->nTableSize)]; pEntry; pEntry = pEntry->pNext ) + { + if ( Hop_ObjChild0(pEntry) == Hop_ObjChild0(pGhost) && + Hop_ObjChild1(pEntry) == Hop_ObjChild1(pGhost) && + Hop_ObjType(pEntry) == Hop_ObjType(pGhost) ) + return pEntry; + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [Adds the new node to the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_TableInsert( Hop_Man_t * p, Hop_Obj_t * pObj ) +{ + Hop_Obj_t ** ppPlace; + assert( !Hop_IsComplement(pObj) ); + assert( Hop_TableLookup(p, pObj) == NULL ); + if ( (pObj->Id & 0xFF) == 0 && 2 * p->nTableSize < Hop_ManNodeNum(p) ) + Hop_TableResize( p ); + ppPlace = Hop_TableFind( p, pObj ); + assert( *ppPlace == NULL ); + *ppPlace = pObj; +} + +/**Function************************************************************* + + Synopsis [Deletes the node from the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_TableDelete( Hop_Man_t * p, Hop_Obj_t * pObj ) +{ + Hop_Obj_t ** ppPlace; + assert( !Hop_IsComplement(pObj) ); + ppPlace = Hop_TableFind( p, pObj ); + assert( *ppPlace == pObj ); // node should be in the table + // remove the node + *ppPlace = pObj->pNext; + pObj->pNext = NULL; +} + +/**Function************************************************************* + + Synopsis [Count the number of nodes in the table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Hop_TableCountEntries( Hop_Man_t * p ) +{ + Hop_Obj_t * pEntry; + int i, Counter = 0; + for ( i = 0; i < p->nTableSize; i++ ) + for ( pEntry = p->pTable[i]; pEntry; pEntry = pEntry->pNext ) + Counter++; + return Counter; +} + +/**Function************************************************************* + + Synopsis [Resizes the table.] + + Description [Typically this procedure should not be called.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_TableResize( Hop_Man_t * p ) +{ + Hop_Obj_t * pEntry, * pNext; + Hop_Obj_t ** pTableOld, ** ppPlace; + int nTableSizeOld, Counter, nEntries, i, clk; +clk = clock(); + // save the old table + pTableOld = p->pTable; + nTableSizeOld = p->nTableSize; + // get the new table + p->nTableSize = Cudd_PrimeAig( 2 * Hop_ManNodeNum(p) ); + p->pTable = ALLOC( Hop_Obj_t *, p->nTableSize ); + memset( p->pTable, 0, sizeof(Hop_Obj_t *) * p->nTableSize ); + // rehash the entries from the old table + Counter = 0; + for ( i = 0; i < nTableSizeOld; i++ ) + for ( pEntry = pTableOld[i], pNext = pEntry? pEntry->pNext : NULL; pEntry; pEntry = pNext, pNext = pEntry? pEntry->pNext : NULL ) + { + // get the place where this entry goes in the table + ppPlace = Hop_TableFind( p, pEntry ); + assert( *ppPlace == NULL ); // should not be there + // add the entry to the list + *ppPlace = pEntry; + pEntry->pNext = NULL; + Counter++; + } + nEntries = Hop_ManNodeNum(p); + assert( Counter == nEntries ); +// printf( "Increasing the structural table size from %6d to %6d. ", nTableSizeOld, p->nTableSize ); +// PRT( "Time", clock() - clk ); + // replace the table and the parameters + free( pTableOld ); +} + +/**Function******************************************************************** + + Synopsis [Profiles the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void Hop_TableProfile( Hop_Man_t * p ) +{ + Hop_Obj_t * pEntry; + int i, Counter; + for ( i = 0; i < p->nTableSize; i++ ) + { + Counter = 0; + for ( pEntry = p->pTable[i]; pEntry; pEntry = pEntry->pNext ) + Counter++; + if ( Counter ) + printf( "%d ", Counter ); + } +} + +/**Function******************************************************************** + + Synopsis [Returns the next prime >= p.] + + Description [Copied from CUDD, for stand-aloneness.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +unsigned int Cudd_PrimeAig( unsigned int p) +{ + int i,pn; + p--; + do { + p++; + if (p&1) { + pn = 1; + i = 3; + while ((unsigned) (i * i) <= p) { + if (p % i == 0) { + pn = 0; + break; + } + i += 2; + } + } else { + pn = 0; + } + } while (!pn); + return(p); + +} /* end of Cudd_Prime */ + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/hop/hopUtil.c b/abc_with_bb_support/src/aig/hop/hopUtil.c new file mode 100644 index 000000000..1082db3e1 --- /dev/null +++ b/abc_with_bb_support/src/aig/hop/hopUtil.c @@ -0,0 +1,572 @@ +/**CFile**************************************************************** + + FileName [hopUtil.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [Various procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: hopUtil.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "hop.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Increments the current traversal ID of the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ManIncrementTravId( Hop_Man_t * p ) +{ + if ( p->nTravIds >= (1<<30)-1 ) + Hop_ManCleanData( p ); + p->nTravIds++; +} + +/**Function************************************************************* + + Synopsis [Cleans the data pointers for the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ManCleanData( Hop_Man_t * p ) +{ + Hop_Obj_t * pObj; + int i; + p->nTravIds = 1; + Hop_ManConst1(p)->pData = NULL; + Hop_ManForEachPi( p, pObj, i ) + pObj->pData = NULL; + Hop_ManForEachPo( p, pObj, i ) + pObj->pData = NULL; + Hop_ManForEachNode( p, pObj, i ) + pObj->pData = NULL; +} + +/**Function************************************************************* + + Synopsis [Recursively cleans the data pointers in the cone of the node.] + + Description [Applicable to small AIGs only because no caching is performed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ObjCleanData_rec( Hop_Obj_t * pObj ) +{ + assert( !Hop_IsComplement(pObj) ); + assert( !Hop_ObjIsPo(pObj) ); + if ( Hop_ObjIsAnd(pObj) ) + { + Hop_ObjCleanData_rec( Hop_ObjFanin0(pObj) ); + Hop_ObjCleanData_rec( Hop_ObjFanin1(pObj) ); + } + pObj->pData = NULL; +} + +/**Function************************************************************* + + Synopsis [Detects multi-input gate rooted at this node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ObjCollectMulti_rec( Hop_Obj_t * pRoot, Hop_Obj_t * pObj, Vec_Ptr_t * vSuper ) +{ + if ( pRoot != pObj && (Hop_IsComplement(pObj) || Hop_ObjIsPi(pObj) || Hop_ObjType(pRoot) != Hop_ObjType(pObj)) ) + { + Vec_PtrPushUnique(vSuper, pObj); + return; + } + Hop_ObjCollectMulti_rec( pRoot, Hop_ObjChild0(pObj), vSuper ); + Hop_ObjCollectMulti_rec( pRoot, Hop_ObjChild1(pObj), vSuper ); +} + +/**Function************************************************************* + + Synopsis [Detects multi-input gate rooted at this node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ObjCollectMulti( Hop_Obj_t * pRoot, Vec_Ptr_t * vSuper ) +{ + assert( !Hop_IsComplement(pRoot) ); + Vec_PtrClear( vSuper ); + Hop_ObjCollectMulti_rec( pRoot, pRoot, vSuper ); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the node is the root of MUX or EXOR/NEXOR.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Hop_ObjIsMuxType( Hop_Obj_t * pNode ) +{ + Hop_Obj_t * pNode0, * pNode1; + // check that the node is regular + assert( !Hop_IsComplement(pNode) ); + // if the node is not AND, this is not MUX + if ( !Hop_ObjIsAnd(pNode) ) + return 0; + // if the children are not complemented, this is not MUX + if ( !Hop_ObjFaninC0(pNode) || !Hop_ObjFaninC1(pNode) ) + return 0; + // get children + pNode0 = Hop_ObjFanin0(pNode); + pNode1 = Hop_ObjFanin1(pNode); + // if the children are not ANDs, this is not MUX + if ( !Hop_ObjIsAnd(pNode0) || !Hop_ObjIsAnd(pNode1) ) + return 0; + // otherwise the node is MUX iff it has a pair of equal grandchildren + return (Hop_ObjFanin0(pNode0) == Hop_ObjFanin0(pNode1) && (Hop_ObjFaninC0(pNode0) ^ Hop_ObjFaninC0(pNode1))) || + (Hop_ObjFanin0(pNode0) == Hop_ObjFanin1(pNode1) && (Hop_ObjFaninC0(pNode0) ^ Hop_ObjFaninC1(pNode1))) || + (Hop_ObjFanin1(pNode0) == Hop_ObjFanin0(pNode1) && (Hop_ObjFaninC1(pNode0) ^ Hop_ObjFaninC0(pNode1))) || + (Hop_ObjFanin1(pNode0) == Hop_ObjFanin1(pNode1) && (Hop_ObjFaninC1(pNode0) ^ Hop_ObjFaninC1(pNode1))); +} + + +/**Function************************************************************* + + Synopsis [Recognizes what nodes are inputs of the EXOR.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Hop_ObjRecognizeExor( Hop_Obj_t * pObj, Hop_Obj_t ** ppFan0, Hop_Obj_t ** ppFan1 ) +{ + Hop_Obj_t * p0, * p1; + assert( !Hop_IsComplement(pObj) ); + if ( !Hop_ObjIsNode(pObj) ) + return 0; + if ( Hop_ObjIsExor(pObj) ) + { + *ppFan0 = Hop_ObjChild0(pObj); + *ppFan1 = Hop_ObjChild1(pObj); + return 1; + } + assert( Hop_ObjIsAnd(pObj) ); + p0 = Hop_ObjChild0(pObj); + p1 = Hop_ObjChild1(pObj); + if ( !Hop_IsComplement(p0) || !Hop_IsComplement(p1) ) + return 0; + p0 = Hop_Regular(p0); + p1 = Hop_Regular(p1); + if ( !Hop_ObjIsAnd(p0) || !Hop_ObjIsAnd(p1) ) + return 0; + if ( Hop_ObjFanin0(p0) != Hop_ObjFanin0(p1) || Hop_ObjFanin1(p0) != Hop_ObjFanin1(p1) ) + return 0; + if ( Hop_ObjFaninC0(p0) == Hop_ObjFaninC0(p1) || Hop_ObjFaninC1(p0) == Hop_ObjFaninC1(p1) ) + return 0; + *ppFan0 = Hop_ObjChild0(p0); + *ppFan1 = Hop_ObjChild1(p0); + return 1; +} + +/**Function************************************************************* + + Synopsis [Recognizes what nodes are control and data inputs of a MUX.] + + Description [If the node is a MUX, returns the control variable C. + Assigns nodes T and E to be the then and else variables of the MUX. + Node C is never complemented. Nodes T and E can be complemented. + This function also recognizes EXOR/NEXOR gates as MUXes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Hop_ObjRecognizeMux( Hop_Obj_t * pNode, Hop_Obj_t ** ppNodeT, Hop_Obj_t ** ppNodeE ) +{ + Hop_Obj_t * pNode0, * pNode1; + assert( !Hop_IsComplement(pNode) ); + assert( Hop_ObjIsMuxType(pNode) ); + // get children + pNode0 = Hop_ObjFanin0(pNode); + pNode1 = Hop_ObjFanin1(pNode); + + // find the control variable + if ( Hop_ObjFanin1(pNode0) == Hop_ObjFanin1(pNode1) && (Hop_ObjFaninC1(pNode0) ^ Hop_ObjFaninC1(pNode1)) ) + { +// if ( Fraig_IsComplement(pNode1->p2) ) + if ( Hop_ObjFaninC1(pNode0) ) + { // pNode2->p2 is positive phase of C + *ppNodeT = Hop_Not(Hop_ObjChild0(pNode1));//pNode2->p1); + *ppNodeE = Hop_Not(Hop_ObjChild0(pNode0));//pNode1->p1); + return Hop_ObjChild1(pNode1);//pNode2->p2; + } + else + { // pNode1->p2 is positive phase of C + *ppNodeT = Hop_Not(Hop_ObjChild0(pNode0));//pNode1->p1); + *ppNodeE = Hop_Not(Hop_ObjChild0(pNode1));//pNode2->p1); + return Hop_ObjChild1(pNode0);//pNode1->p2; + } + } + else if ( Hop_ObjFanin0(pNode0) == Hop_ObjFanin0(pNode1) && (Hop_ObjFaninC0(pNode0) ^ Hop_ObjFaninC0(pNode1)) ) + { +// if ( Fraig_IsComplement(pNode1->p1) ) + if ( Hop_ObjFaninC0(pNode0) ) + { // pNode2->p1 is positive phase of C + *ppNodeT = Hop_Not(Hop_ObjChild1(pNode1));//pNode2->p2); + *ppNodeE = Hop_Not(Hop_ObjChild1(pNode0));//pNode1->p2); + return Hop_ObjChild0(pNode1);//pNode2->p1; + } + else + { // pNode1->p1 is positive phase of C + *ppNodeT = Hop_Not(Hop_ObjChild1(pNode0));//pNode1->p2); + *ppNodeE = Hop_Not(Hop_ObjChild1(pNode1));//pNode2->p2); + return Hop_ObjChild0(pNode0);//pNode1->p1; + } + } + else if ( Hop_ObjFanin0(pNode0) == Hop_ObjFanin1(pNode1) && (Hop_ObjFaninC0(pNode0) ^ Hop_ObjFaninC1(pNode1)) ) + { +// if ( Fraig_IsComplement(pNode1->p1) ) + if ( Hop_ObjFaninC0(pNode0) ) + { // pNode2->p2 is positive phase of C + *ppNodeT = Hop_Not(Hop_ObjChild0(pNode1));//pNode2->p1); + *ppNodeE = Hop_Not(Hop_ObjChild1(pNode0));//pNode1->p2); + return Hop_ObjChild1(pNode1);//pNode2->p2; + } + else + { // pNode1->p1 is positive phase of C + *ppNodeT = Hop_Not(Hop_ObjChild1(pNode0));//pNode1->p2); + *ppNodeE = Hop_Not(Hop_ObjChild0(pNode1));//pNode2->p1); + return Hop_ObjChild0(pNode0);//pNode1->p1; + } + } + else if ( Hop_ObjFanin1(pNode0) == Hop_ObjFanin0(pNode1) && (Hop_ObjFaninC1(pNode0) ^ Hop_ObjFaninC0(pNode1)) ) + { +// if ( Fraig_IsComplement(pNode1->p2) ) + if ( Hop_ObjFaninC1(pNode0) ) + { // pNode2->p1 is positive phase of C + *ppNodeT = Hop_Not(Hop_ObjChild1(pNode1));//pNode2->p2); + *ppNodeE = Hop_Not(Hop_ObjChild0(pNode0));//pNode1->p1); + return Hop_ObjChild0(pNode1);//pNode2->p1; + } + else + { // pNode1->p2 is positive phase of C + *ppNodeT = Hop_Not(Hop_ObjChild0(pNode0));//pNode1->p1); + *ppNodeE = Hop_Not(Hop_ObjChild1(pNode1));//pNode2->p2); + return Hop_ObjChild1(pNode0);//pNode1->p2; + } + } + assert( 0 ); // this is not MUX + return NULL; +} + + +/**Function************************************************************* + + Synopsis [Prints Eqn formula for the AIG rooted at this node.] + + Description [The formula is in terms of PIs, which should have + their names assigned in pObj->pData fields.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ObjPrintEqn( FILE * pFile, Hop_Obj_t * pObj, Vec_Vec_t * vLevels, int Level ) +{ + Vec_Ptr_t * vSuper; + Hop_Obj_t * pFanin; + int fCompl, i; + // store the complemented attribute + fCompl = Hop_IsComplement(pObj); + pObj = Hop_Regular(pObj); + // constant case + if ( Hop_ObjIsConst1(pObj) ) + { + fprintf( pFile, "%d", !fCompl ); + return; + } + // PI case + if ( Hop_ObjIsPi(pObj) ) + { + fprintf( pFile, "%s%s", fCompl? "!" : "", pObj->pData ); + return; + } + // AND case + Vec_VecExpand( vLevels, Level ); + vSuper = Vec_VecEntry(vLevels, Level); + Hop_ObjCollectMulti( pObj, vSuper ); + fprintf( pFile, "%s", (Level==0? "" : "(") ); + Vec_PtrForEachEntry( vSuper, pFanin, i ) + { + Hop_ObjPrintEqn( pFile, Hop_NotCond(pFanin, fCompl), vLevels, Level+1 ); + if ( i < Vec_PtrSize(vSuper) - 1 ) + fprintf( pFile, " %s ", fCompl? "+" : "*" ); + } + fprintf( pFile, "%s", (Level==0? "" : ")") ); + return; +} + +/**Function************************************************************* + + Synopsis [Prints Verilog formula for the AIG rooted at this node.] + + Description [The formula is in terms of PIs, which should have + their names assigned in pObj->pData fields.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ObjPrintVerilog( FILE * pFile, Hop_Obj_t * pObj, Vec_Vec_t * vLevels, int Level ) +{ + Vec_Ptr_t * vSuper; + Hop_Obj_t * pFanin, * pFanin0, * pFanin1, * pFaninC; + int fCompl, i; + // store the complemented attribute + fCompl = Hop_IsComplement(pObj); + pObj = Hop_Regular(pObj); + // constant case + if ( Hop_ObjIsConst1(pObj) ) + { + fprintf( pFile, "1\'b%d", !fCompl ); + return; + } + // PI case + if ( Hop_ObjIsPi(pObj) ) + { + fprintf( pFile, "%s%s", fCompl? "~" : "", pObj->pData ); + return; + } + // EXOR case + if ( Hop_ObjIsExor(pObj) ) + { + Vec_VecExpand( vLevels, Level ); + vSuper = Vec_VecEntry( vLevels, Level ); + Hop_ObjCollectMulti( pObj, vSuper ); + fprintf( pFile, "%s", (Level==0? "" : "(") ); + Vec_PtrForEachEntry( vSuper, pFanin, i ) + { + Hop_ObjPrintVerilog( pFile, Hop_NotCond(pFanin, (fCompl && i==0)), vLevels, Level+1 ); + if ( i < Vec_PtrSize(vSuper) - 1 ) + fprintf( pFile, " ^ " ); + } + fprintf( pFile, "%s", (Level==0? "" : ")") ); + return; + } + // MUX case + if ( Hop_ObjIsMuxType(pObj) ) + { + if ( Hop_ObjRecognizeExor( pObj, &pFanin0, &pFanin1 ) ) + { + fprintf( pFile, "%s", (Level==0? "" : "(") ); + Hop_ObjPrintVerilog( pFile, Hop_NotCond(pFanin0, fCompl), vLevels, Level+1 ); + fprintf( pFile, " ^ " ); + Hop_ObjPrintVerilog( pFile, pFanin1, vLevels, Level+1 ); + fprintf( pFile, "%s", (Level==0? "" : ")") ); + } + else + { + pFaninC = Hop_ObjRecognizeMux( pObj, &pFanin1, &pFanin0 ); + fprintf( pFile, "%s", (Level==0? "" : "(") ); + Hop_ObjPrintVerilog( pFile, pFaninC, vLevels, Level+1 ); + fprintf( pFile, " ? " ); + Hop_ObjPrintVerilog( pFile, Hop_NotCond(pFanin1, fCompl), vLevels, Level+1 ); + fprintf( pFile, " : " ); + Hop_ObjPrintVerilog( pFile, Hop_NotCond(pFanin0, fCompl), vLevels, Level+1 ); + fprintf( pFile, "%s", (Level==0? "" : ")") ); + } + return; + } + // AND case + Vec_VecExpand( vLevels, Level ); + vSuper = Vec_VecEntry(vLevels, Level); + Hop_ObjCollectMulti( pObj, vSuper ); + fprintf( pFile, "%s", (Level==0? "" : "(") ); + Vec_PtrForEachEntry( vSuper, pFanin, i ) + { + Hop_ObjPrintVerilog( pFile, Hop_NotCond(pFanin, fCompl), vLevels, Level+1 ); + if ( i < Vec_PtrSize(vSuper) - 1 ) + fprintf( pFile, " %s ", fCompl? "|" : "&" ); + } + fprintf( pFile, "%s", (Level==0? "" : ")") ); + return; +} + + +/**Function************************************************************* + + Synopsis [Prints node in HAIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ObjPrintVerbose( Hop_Obj_t * pObj, int fHaig ) +{ + assert( !Hop_IsComplement(pObj) ); + printf( "Node %p : ", pObj ); + if ( Hop_ObjIsConst1(pObj) ) + printf( "constant 1" ); + else if ( Hop_ObjIsPi(pObj) ) + printf( "PI" ); + else + printf( "AND( %p%s, %p%s )", + Hop_ObjFanin0(pObj), (Hop_ObjFaninC0(pObj)? "\'" : " "), + Hop_ObjFanin1(pObj), (Hop_ObjFaninC1(pObj)? "\'" : " ") ); + printf( " (refs = %3d)", Hop_ObjRefs(pObj) ); +} + +/**Function************************************************************* + + Synopsis [Prints node in HAIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ManPrintVerbose( Hop_Man_t * p, int fHaig ) +{ + Vec_Ptr_t * vNodes; + Hop_Obj_t * pObj; + int i; + printf( "PIs: " ); + Hop_ManForEachPi( p, pObj, i ) + printf( " %p", pObj ); + printf( "\n" ); + vNodes = Hop_ManDfs( p ); + Vec_PtrForEachEntry( vNodes, pObj, i ) + Hop_ObjPrintVerbose( pObj, fHaig ), printf( "\n" ); + printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [Writes the AIG into the BLIF file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Hop_ManDumpBlif( Hop_Man_t * p, char * pFileName ) +{ + FILE * pFile; + Vec_Ptr_t * vNodes; + Hop_Obj_t * pObj, * pConst1 = NULL; + int i, nDigits, Counter = 0; + if ( Hop_ManPoNum(p) == 0 ) + { + printf( "Hop_ManDumpBlif(): AIG manager does not have POs.\n" ); + return; + } + // collect nodes in the DFS order + vNodes = Hop_ManDfs( p ); + // assign IDs to objects + Hop_ManConst1(p)->pData = (void *)Counter++; + Hop_ManForEachPi( p, pObj, i ) + pObj->pData = (void *)Counter++; + Hop_ManForEachPo( p, pObj, i ) + pObj->pData = (void *)Counter++; + Vec_PtrForEachEntry( vNodes, pObj, i ) + pObj->pData = (void *)Counter++; + nDigits = Extra_Base10Log( Counter ); + // write the file + pFile = fopen( pFileName, "w" ); + fprintf( pFile, "# BLIF file written by procedure Hop_ManDumpBlif() in ABC\n" ); + fprintf( pFile, "# http://www.eecs.berkeley.edu/~alanmi/abc/\n" ); + fprintf( pFile, ".model test\n" ); + // write PIs + fprintf( pFile, ".inputs" ); + Hop_ManForEachPi( p, pObj, i ) + fprintf( pFile, " n%0*d", nDigits, (int)pObj->pData ); + fprintf( pFile, "\n" ); + // write POs + fprintf( pFile, ".outputs" ); + Hop_ManForEachPo( p, pObj, i ) + fprintf( pFile, " n%0*d", nDigits, (int)pObj->pData ); + fprintf( pFile, "\n" ); + // write nodes + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + fprintf( pFile, ".names n%0*d n%0*d n%0*d\n", + nDigits, (int)Hop_ObjFanin0(pObj)->pData, + nDigits, (int)Hop_ObjFanin1(pObj)->pData, + nDigits, (int)pObj->pData ); + fprintf( pFile, "%d%d 1\n", !Hop_ObjFaninC0(pObj), !Hop_ObjFaninC1(pObj) ); + } + // write POs + Hop_ManForEachPo( p, pObj, i ) + { + fprintf( pFile, ".names n%0*d n%0*d\n", + nDigits, (int)Hop_ObjFanin0(pObj)->pData, + nDigits, (int)pObj->pData ); + fprintf( pFile, "%d 1\n", !Hop_ObjFaninC0(pObj) ); + if ( Hop_ObjIsConst1(Hop_ObjFanin0(pObj)) ) + pConst1 = Hop_ManConst1(p); + } + if ( pConst1 ) + fprintf( pFile, ".names n%0*d\n 1\n", nDigits, (int)pConst1->pData ); + fprintf( pFile, ".end\n\n" ); + fclose( pFile ); + Vec_PtrFree( vNodes ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/hop/hop_.c b/abc_with_bb_support/src/aig/hop/hop_.c new file mode 100644 index 000000000..506dcaed4 --- /dev/null +++ b/abc_with_bb_support/src/aig/hop/hop_.c @@ -0,0 +1,48 @@ +/**CFile**************************************************************** + + FileName [ivy_.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Minimalistic And-Inverter Graph package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivy_.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/hop/module.make b/abc_with_bb_support/src/aig/hop/module.make new file mode 100644 index 000000000..8c8f7dc54 --- /dev/null +++ b/abc_with_bb_support/src/aig/hop/module.make @@ -0,0 +1,9 @@ +SRC += src/aig/hop/hopBalance.c \ + src/aig/hop/hopCheck.c \ + src/aig/hop/hopDfs.c \ + src/aig/hop/hopMan.c \ + src/aig/hop/hopMem.c \ + src/aig/hop/hopObj.c \ + src/aig/hop/hopOper.c \ + src/aig/hop/hopTable.c \ + src/aig/hop/hopUtil.c diff --git a/abc_with_bb_support/src/aig/ivy/attr.h b/abc_with_bb_support/src/aig/ivy/attr.h new file mode 100644 index 000000000..74ddca749 --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/attr.h @@ -0,0 +1,414 @@ +/**CFile**************************************************************** + + FileName [attr.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network attributes.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: attr.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __ATTR_H__ +#define __ATTR_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include "extra.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Attr_ManStruct_t_ Attr_Man_t; +struct Attr_ManStruct_t_ +{ + // attribute info + int nAttrSize; // the size of each attribute in bytes + Extra_MmFixed_t * pManMem; // memory manager for attributes + int nAttrs; // the number of attributes allocated + void ** pAttrs; // the array of attributes + int fUseInt; // uses integer attributes + // attribute specific info + void * pManAttr; // the manager for this attribute + void (*pFuncFreeMan) (void *); // the procedure to call to free attribute-specific manager + void (*pFuncFreeObj) (void *, void *); // the procedure to call to free attribute-specific data +}; + +// at any time, an attribute of the given ID can be +// - not available (p->nAttrs < Id) +// - available but not allocated (p->nAttrs >= Id && p->pAttrs[Id] == NULL) +// - available and allocated (p->nAttrs >= Id && p->pAttrs[Id] != NULL) + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates the attribute manager.] + + Description [The manager is simple if it does not need memory manager.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Attr_Man_t * Attr_ManAlloc( int nAttrSize, int fManMem ) +{ + Attr_Man_t * p; + p = ALLOC( Attr_Man_t, 1 ); + memset( p, 0, sizeof(Attr_Man_t) ); + p->nAttrSize = nAttrSize; + if ( fManMem ) + p->pManMem = Extra_MmFixedStart( nAttrSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Start the attribute manager for integers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Attr_Man_t * Attr_ManStartInt( int nAttrs ) +{ + Attr_Man_t * p; + p = Attr_ManAlloc( sizeof(int), 0 ); + p->nAttrs = nAttrs; + p->pAttrs = (void **)ALLOC( int, nAttrs ); + memset( (int *)p->pAttrs, 0, sizeof(int) * nAttrs ); + p->fUseInt = 1; + return p; +} + +/**Function************************************************************* + + Synopsis [Start the attribute manager for pointers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Attr_Man_t * Attr_ManStartPtr( int nAttrs ) +{ + Attr_Man_t * p; + p = Attr_ManAlloc( sizeof(void *), 0 ); + p->nAttrs = nAttrs; + p->pAttrs = ALLOC( void *, nAttrs ); + memset( p->pAttrs, 0, sizeof(void *) * nAttrs ); + return p; +} + +/**Function************************************************************* + + Synopsis [Start the attribute manager for the fixed entry size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Attr_Man_t * Attr_ManStartPtrMem( int nAttrs, int nAttrSize ) +{ + Attr_Man_t * p; + int i; + p = Attr_ManAlloc( nAttrSize, 1 ); + p->nAttrs = nAttrs; + p->pAttrs = ALLOC( void *, nAttrs ); + for ( i = 0; i < p->nAttrs; i++ ) + { + p->pAttrs[i] = Extra_MmFixedEntryFetch( p->pManMem ); + memset( p->pAttrs[i], 0, nAttrSize ); + } + return p; +} + +/**Function************************************************************* + + Synopsis [Stop the attribute manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Attr_ManStop( Attr_Man_t * p ) +{ + // free the attributes of objects + if ( p->pFuncFreeObj ) + { + int i; + if ( p->fUseInt ) + { + for ( i = 0; i < p->nAttrs; i++ ) + if ( ((int *)p->pAttrs)[i] ) + p->pFuncFreeObj( p->pManAttr, (void *)((int *)p->pAttrs)[i] ); + } + else + { + for ( i = 0; i < p->nAttrs; i++ ) + if ( p->pAttrs[i] ) + p->pFuncFreeObj( p->pManAttr, p->pAttrs[i] ); + } + } + // free the attribute manager + if ( p->pManAttr && p->pFuncFreeMan ) + p->pFuncFreeMan( p->pManAttr ); + // free the memory manager + if ( p->pManMem ) + Extra_MmFixedStop( p->pManMem); + // free the attribute manager + FREE( p->pAttrs ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Reads the attribute of the given object.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Attr_ManReadAttrInt( Attr_Man_t * p, int Id ) +{ + assert( p->fUseInt ); + if ( Id >= p->nAttrs ) + return 0; + return ((int *)p->pAttrs)[Id]; +} + +/**Function************************************************************* + + Synopsis [Reads the attribute of the given object.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void * Attr_ManReadAttrPtr( Attr_Man_t * p, int Id ) +{ + assert( !p->fUseInt ); + if ( Id >= p->nAttrs ) + return NULL; + return p->pAttrs[Id]; +} + +/**Function************************************************************* + + Synopsis [Writes the attribute of the given object.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Attr_ManWriteAttrInt( Attr_Man_t * p, int Id, int Attr ) +{ + assert( p->fUseInt ); + ((int *)p->pAttrs)[Id] = Attr; +} + +/**Function************************************************************* + + Synopsis [Writes the attribute of the given object.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Attr_ManWriteAttrPtr( Attr_Man_t * p, int Id, void * pAttr ) +{ + assert( !p->fUseInt ); + assert( p->pManMem == NULL ); + p->pAttrs[Id] = pAttr; +} + +/**Function************************************************************* + + Synopsis [Returns or creates the pointer to the attribute of the given object.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int * Attr_ManFetchSpotInt( Attr_Man_t * p, int Id ) +{ + assert( p->fUseInt ); + if ( Id >= p->nAttrs ) + { + // save the old size + int i, nAttrsOld = p->nAttrs; + // get the new size + p->nAttrs = p->nAttrs? 2*p->nAttrs : 1024; + p->pAttrs = realloc( p->pAttrs, sizeof(int) * p->nAttrs ); + // fill in the empty spots + for ( i = nAttrsOld; i < p->nAttrs; i++ ) + ((int *)p->pAttrs)[Id] = 0; + } + return ((int *)p->pAttrs) + Id; +} + +/**Function************************************************************* + + Synopsis [Returns or creates the pointer to the attribute of the given object.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void ** Attr_ManFetchSpotPtr( Attr_Man_t * p, int Id ) +{ + assert( !p->fUseInt ); + if ( Id >= p->nAttrs ) + { + // save the old size + int i, nAttrsOld = p->nAttrs; + // get the new size + p->nAttrs = p->nAttrs? 2*p->nAttrs : 1024; + p->pAttrs = realloc( p->pAttrs, sizeof(void *) * p->nAttrs ); + // fill in the empty spots + for ( i = nAttrsOld; i < p->nAttrs; i++ ) + p->pAttrs[Id] = NULL; + } + // if memory manager is available but entry is not created, create it + if ( p->pManMem && p->pAttrs[Id] != NULL ) + { + p->pAttrs[Id] = Extra_MmFixedEntryFetch( p->pManMem ); + memset( p->pAttrs[Id], 0, p->nAttrSize ); + } + return p->pAttrs + Id; +} + + +/**Function************************************************************* + + Synopsis [Returns or creates the attribute of the given object.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Attr_ManFetchAttrInt( Attr_Man_t * p, int Id ) +{ + return *Attr_ManFetchSpotInt( p, Id ); +} + +/**Function************************************************************* + + Synopsis [Returns or creates the attribute of the given object.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void * Attr_ManFetchAttrPtr( Attr_Man_t * p, int Id ) +{ + return *Attr_ManFetchSpotPtr( p, Id ); +} + +/**Function************************************************************* + + Synopsis [Sets the attribute of the given object.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Attr_ManSetAttrInt( Attr_Man_t * p, int Id, int Attr ) +{ + *Attr_ManFetchSpotInt( p, Id ) = Attr; +} + +/**Function************************************************************* + + Synopsis [Sets the attribute of the given object.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Attr_ManSetAttrPtr( Attr_Man_t * p, int Id, void * pAttr ) +{ + assert( p->pManMem == NULL ); + *Attr_ManFetchSpotPtr( p, Id ) = pAttr; +} + + + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/aig/ivy/ivy.h b/abc_with_bb_support/src/aig/ivy/ivy.h new file mode 100644 index 000000000..7f78eb52b --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivy.h @@ -0,0 +1,556 @@ +/**CFile**************************************************************** + + FileName [ivy.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivy.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __IVY_H__ +#define __IVY_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include +#include "vec.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Ivy_Man_t_ Ivy_Man_t; +typedef struct Ivy_Obj_t_ Ivy_Obj_t; +typedef int Ivy_Edge_t; +typedef struct Ivy_FraigParams_t_ Ivy_FraigParams_t; + +// object types +typedef enum { + IVY_NONE, // 0: non-existent object + IVY_PI, // 1: primary input (and constant 1 node) + IVY_PO, // 2: primary output + IVY_ASSERT, // 3: assertion + IVY_LATCH, // 4: sequential element + IVY_AND, // 5: AND node + IVY_EXOR, // 6: EXOR node + IVY_BUF, // 7: buffer (temporary) + IVY_VOID // 8: unused object +} Ivy_Type_t; + +// latch initial values +typedef enum { + IVY_INIT_NONE, // 0: not a latch + IVY_INIT_0, // 1: zero + IVY_INIT_1, // 2: one + IVY_INIT_DC // 3: don't-care +} Ivy_Init_t; + +// the AIG node +struct Ivy_Obj_t_ // 24 bytes (32-bit) or 32 bytes (64-bit) // 10 words - 16 words +{ + int Id; // integer ID + int TravId; // traversal ID + unsigned Type : 4; // object type + unsigned fMarkA : 1; // multipurpose mask + unsigned fMarkB : 1; // multipurpose mask + unsigned fExFan : 1; // set to 1 if last fanout added is EXOR + unsigned fPhase : 1; // value under 000...0 pattern + unsigned fFailTfo : 1; // the TFO of the failed node + unsigned Init : 2; // latch initial value + unsigned Level : 21; // logic level + int nRefs; // reference counter + Ivy_Obj_t * pFanin0; // fanin + Ivy_Obj_t * pFanin1; // fanin + Ivy_Obj_t * pFanout; // fanout + Ivy_Obj_t * pNextFan0; // next fanout of the first fanin + Ivy_Obj_t * pNextFan1; // next fanout of the second fanin + Ivy_Obj_t * pPrevFan0; // prev fanout of the first fanin + Ivy_Obj_t * pPrevFan1; // prev fanout of the second fanin + Ivy_Obj_t * pEquiv; // equivalent node +}; + +// the AIG manager +struct Ivy_Man_t_ +{ + // AIG nodes + Vec_Ptr_t * vPis; // the array of PIs + Vec_Ptr_t * vPos; // the array of POs + Vec_Ptr_t * vBufs; // the array of buffers + Vec_Ptr_t * vObjs; // the array of objects + Ivy_Obj_t * pConst1; // the constant 1 node + Ivy_Obj_t Ghost; // the ghost node + // AIG node counters + int nObjs[IVY_VOID];// the number of objects by type + int nCreated; // the number of created objects + int nDeleted; // the number of deleted objects + // stuctural hash table + int * pTable; // structural hash table + int nTableSize; // structural hash table size + // various data members + int fCatchExor; // set to 1 to detect EXORs + int nTravIds; // the traversal ID + int nLevelMax; // the maximum level + Vec_Int_t * vRequired; // required times + int fFanout; // fanout is allocated + void * pData; // the temporary data + void * pCopy; // the temporary data + Ivy_Man_t * pHaig; // history AIG if present + int nClassesSkip; // the number of skipped classes + // memory management + Vec_Ptr_t * vChunks; // allocated memory pieces + Vec_Ptr_t * vPages; // memory pages used by nodes + Ivy_Obj_t * pListFree; // the list of free nodes + // timing statistics + int time1; + int time2; +}; + +struct Ivy_FraigParams_t_ +{ + int nSimWords; // the number of words in the simulation info + double dSimSatur; // the ratio of refined classes when saturation is reached + int fPatScores; // enables simulation pattern scoring + int MaxScore; // max score after which resimulation is used + double dActConeRatio; // the ratio of cone to be bumped + double dActConeBumpMax; // the largest bump in activity + int fProve; // prove the miter outputs + int fVerbose; // verbose output + int fDoSparse; // skip sparse functions + int nBTLimitNode; // conflict limit at a node + int nBTLimitMiter; // conflict limit at an output +// int nBTLimitGlobal; // conflict limit global +// int nInsLimitNode; // inspection limit at a node +// int nInsLimitMiter; // inspection limit at an output +// int nInsLimitGlobal; // inspection limit global +}; + + +#define IVY_CUT_LIMIT 256 +#define IVY_CUT_INPUT 6 + +typedef struct Ivy_Cut_t_ Ivy_Cut_t; +struct Ivy_Cut_t_ +{ + int nLatches; + short nSize; + short nSizeMax; + int pArray[IVY_CUT_INPUT]; + unsigned uHash; +}; + +typedef struct Ivy_Store_t_ Ivy_Store_t; +struct Ivy_Store_t_ +{ + int nCuts; + int nCutsM; + int nCutsMax; + int fSatur; + Ivy_Cut_t pCuts[IVY_CUT_LIMIT]; // storage for cuts +}; + +#define IVY_LEAF_MASK 255 +#define IVY_LEAF_BITS 8 + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define IVY_MIN(a,b) (((a) < (b))? (a) : (b)) +#define IVY_MAX(a,b) (((a) > (b))? (a) : (b)) + +static inline int Ivy_BitWordNum( int nBits ) { return (nBits>>5) + ((nBits&31) > 0); } +static inline int Ivy_TruthWordNum( int nVars ) { return nVars <= 5 ? 1 : (1 << (nVars - 5)); } +static inline int Ivy_InfoHasBit( unsigned * p, int i ) { return (p[(i)>>5] & (1<<((i) & 31))) > 0; } +static inline void Ivy_InfoSetBit( unsigned * p, int i ) { p[(i)>>5] |= (1<<((i) & 31)); } +static inline void Ivy_InfoXorBit( unsigned * p, int i ) { p[(i)>>5] ^= (1<<((i) & 31)); } + +static inline Ivy_Obj_t * Ivy_Regular( Ivy_Obj_t * p ) { return (Ivy_Obj_t *)((unsigned long)(p) & ~01); } +static inline Ivy_Obj_t * Ivy_Not( Ivy_Obj_t * p ) { return (Ivy_Obj_t *)((unsigned long)(p) ^ 01); } +static inline Ivy_Obj_t * Ivy_NotCond( Ivy_Obj_t * p, int c ) { return (Ivy_Obj_t *)((unsigned long)(p) ^ (c)); } +static inline int Ivy_IsComplement( Ivy_Obj_t * p ) { return (int )(((unsigned long)p) & 01); } + +static inline Ivy_Obj_t * Ivy_ManConst0( Ivy_Man_t * p ) { return Ivy_Not(p->pConst1); } +static inline Ivy_Obj_t * Ivy_ManConst1( Ivy_Man_t * p ) { return p->pConst1; } +static inline Ivy_Obj_t * Ivy_ManGhost( Ivy_Man_t * p ) { return &p->Ghost; } +static inline Ivy_Obj_t * Ivy_ManPi( Ivy_Man_t * p, int i ) { return (Ivy_Obj_t *)Vec_PtrEntry(p->vPis, i); } +static inline Ivy_Obj_t * Ivy_ManPo( Ivy_Man_t * p, int i ) { return (Ivy_Obj_t *)Vec_PtrEntry(p->vPos, i); } +static inline Ivy_Obj_t * Ivy_ManObj( Ivy_Man_t * p, int i ) { return (Ivy_Obj_t *)Vec_PtrEntry(p->vObjs, i); } + +static inline Ivy_Edge_t Ivy_EdgeCreate( int Id, int fCompl ) { return (Id << 1) | fCompl; } +static inline int Ivy_EdgeId( Ivy_Edge_t Edge ) { return Edge >> 1; } +static inline int Ivy_EdgeIsComplement( Ivy_Edge_t Edge ) { return Edge & 1; } +static inline Ivy_Edge_t Ivy_EdgeRegular( Ivy_Edge_t Edge ) { return (Edge >> 1) << 1; } +static inline Ivy_Edge_t Ivy_EdgeNot( Ivy_Edge_t Edge ) { return Edge ^ 1; } +static inline Ivy_Edge_t Ivy_EdgeNotCond( Ivy_Edge_t Edge, int fCond ) { return Edge ^ fCond; } +static inline Ivy_Edge_t Ivy_EdgeFromNode( Ivy_Obj_t * pNode ) { return Ivy_EdgeCreate( Ivy_Regular(pNode)->Id, Ivy_IsComplement(pNode) ); } +static inline Ivy_Obj_t * Ivy_EdgeToNode( Ivy_Man_t * p, Ivy_Edge_t Edge ){ return Ivy_NotCond( Ivy_ManObj(p, Ivy_EdgeId(Edge)), Ivy_EdgeIsComplement(Edge) ); } + +static inline int Ivy_LeafCreate( int Id, int Lat ) { return (Id << IVY_LEAF_BITS) | Lat; } +static inline int Ivy_LeafId( int Leaf ) { return Leaf >> IVY_LEAF_BITS; } +static inline int Ivy_LeafLat( int Leaf ) { return Leaf & IVY_LEAF_MASK; } + +static inline int Ivy_ManPiNum( Ivy_Man_t * p ) { return p->nObjs[IVY_PI]; } +static inline int Ivy_ManPoNum( Ivy_Man_t * p ) { return p->nObjs[IVY_PO]; } +static inline int Ivy_ManAssertNum( Ivy_Man_t * p ) { return p->nObjs[IVY_ASSERT]; } +static inline int Ivy_ManLatchNum( Ivy_Man_t * p ) { return p->nObjs[IVY_LATCH]; } +static inline int Ivy_ManAndNum( Ivy_Man_t * p ) { return p->nObjs[IVY_AND]; } +static inline int Ivy_ManExorNum( Ivy_Man_t * p ) { return p->nObjs[IVY_EXOR]; } +static inline int Ivy_ManBufNum( Ivy_Man_t * p ) { return p->nObjs[IVY_BUF]; } +static inline int Ivy_ManObjNum( Ivy_Man_t * p ) { return p->nCreated - p->nDeleted; } +static inline int Ivy_ManObjIdMax( Ivy_Man_t * p ) { return Vec_PtrSize(p->vObjs)-1; } +static inline int Ivy_ManNodeNum( Ivy_Man_t * p ) { return p->nObjs[IVY_AND]+p->nObjs[IVY_EXOR];} +static inline int Ivy_ManHashObjNum( Ivy_Man_t * p ) { return p->nObjs[IVY_AND]+p->nObjs[IVY_EXOR]+p->nObjs[IVY_LATCH]; } +static inline int Ivy_ManGetCost( Ivy_Man_t * p ) { return p->nObjs[IVY_AND]+3*p->nObjs[IVY_EXOR]+8*p->nObjs[IVY_LATCH]; } + +static inline Ivy_Type_t Ivy_ObjType( Ivy_Obj_t * pObj ) { return pObj->Type; } +static inline Ivy_Init_t Ivy_ObjInit( Ivy_Obj_t * pObj ) { return pObj->Init; } +static inline int Ivy_ObjIsConst1( Ivy_Obj_t * pObj ) { return pObj->Id == 0; } +static inline int Ivy_ObjIsGhost( Ivy_Obj_t * pObj ) { return pObj->Id < 0; } +static inline int Ivy_ObjIsNone( Ivy_Obj_t * pObj ) { return pObj->Type == IVY_NONE; } +static inline int Ivy_ObjIsPi( Ivy_Obj_t * pObj ) { return pObj->Type == IVY_PI; } +static inline int Ivy_ObjIsPo( Ivy_Obj_t * pObj ) { return pObj->Type == IVY_PO; } +static inline int Ivy_ObjIsCi( Ivy_Obj_t * pObj ) { return pObj->Type == IVY_PI || pObj->Type == IVY_LATCH; } +static inline int Ivy_ObjIsCo( Ivy_Obj_t * pObj ) { return pObj->Type == IVY_PO || pObj->Type == IVY_LATCH; } +static inline int Ivy_ObjIsAssert( Ivy_Obj_t * pObj ) { return pObj->Type == IVY_ASSERT; } +static inline int Ivy_ObjIsLatch( Ivy_Obj_t * pObj ) { return pObj->Type == IVY_LATCH; } +static inline int Ivy_ObjIsAnd( Ivy_Obj_t * pObj ) { return pObj->Type == IVY_AND; } +static inline int Ivy_ObjIsExor( Ivy_Obj_t * pObj ) { return pObj->Type == IVY_EXOR; } +static inline int Ivy_ObjIsBuf( Ivy_Obj_t * pObj ) { return pObj->Type == IVY_BUF; } +static inline int Ivy_ObjIsNode( Ivy_Obj_t * pObj ) { return pObj->Type == IVY_AND || pObj->Type == IVY_EXOR; } +static inline int Ivy_ObjIsTerm( Ivy_Obj_t * pObj ) { return pObj->Type == IVY_PI || pObj->Type == IVY_PO || pObj->Type == IVY_ASSERT; } +static inline int Ivy_ObjIsHash( Ivy_Obj_t * pObj ) { return pObj->Type == IVY_AND || pObj->Type == IVY_EXOR || pObj->Type == IVY_LATCH; } +static inline int Ivy_ObjIsOneFanin( Ivy_Obj_t * pObj ) { return pObj->Type == IVY_PO || pObj->Type == IVY_ASSERT || pObj->Type == IVY_BUF || pObj->Type == IVY_LATCH; } + +static inline int Ivy_ObjIsMarkA( Ivy_Obj_t * pObj ) { return pObj->fMarkA; } +static inline void Ivy_ObjSetMarkA( Ivy_Obj_t * pObj ) { pObj->fMarkA = 1; } +static inline void Ivy_ObjClearMarkA( Ivy_Obj_t * pObj ) { pObj->fMarkA = 0; } + +static inline void Ivy_ObjSetTravId( Ivy_Obj_t * pObj, int TravId ) { pObj->TravId = TravId; } +static inline void Ivy_ObjSetTravIdCurrent( Ivy_Man_t * p, Ivy_Obj_t * pObj ) { pObj->TravId = p->nTravIds; } +static inline void Ivy_ObjSetTravIdPrevious( Ivy_Man_t * p, Ivy_Obj_t * pObj ) { pObj->TravId = p->nTravIds - 1; } +static inline int Ivy_ObjIsTravIdCurrent( Ivy_Man_t * p, Ivy_Obj_t * pObj ) { return (int )((int)pObj->TravId == p->nTravIds); } +static inline int Ivy_ObjIsTravIdPrevious( Ivy_Man_t * p, Ivy_Obj_t * pObj ) { return (int )((int)pObj->TravId == p->nTravIds - 1); } + +static inline int Ivy_ObjId( Ivy_Obj_t * pObj ) { return pObj->Id; } +static inline int Ivy_ObjTravId( Ivy_Obj_t * pObj ) { return pObj->TravId; } +static inline int Ivy_ObjPhase( Ivy_Obj_t * pObj ) { return pObj->fPhase; } +static inline int Ivy_ObjExorFanout( Ivy_Obj_t * pObj ) { return pObj->fExFan; } +static inline int Ivy_ObjRefs( Ivy_Obj_t * pObj ) { return pObj->nRefs; } +static inline void Ivy_ObjRefsInc( Ivy_Obj_t * pObj ) { pObj->nRefs++; } +static inline void Ivy_ObjRefsDec( Ivy_Obj_t * pObj ) { assert( pObj->nRefs > 0 ); pObj->nRefs--; } +static inline int Ivy_ObjFaninId0( Ivy_Obj_t * pObj ) { return pObj->pFanin0? Ivy_ObjId(Ivy_Regular(pObj->pFanin0)) : 0; } +static inline int Ivy_ObjFaninId1( Ivy_Obj_t * pObj ) { return pObj->pFanin1? Ivy_ObjId(Ivy_Regular(pObj->pFanin1)) : 0; } +static inline int Ivy_ObjFaninC0( Ivy_Obj_t * pObj ) { return Ivy_IsComplement(pObj->pFanin0); } +static inline int Ivy_ObjFaninC1( Ivy_Obj_t * pObj ) { return Ivy_IsComplement(pObj->pFanin1); } +static inline Ivy_Obj_t * Ivy_ObjFanin0( Ivy_Obj_t * pObj ) { return Ivy_Regular(pObj->pFanin0); } +static inline Ivy_Obj_t * Ivy_ObjFanin1( Ivy_Obj_t * pObj ) { return Ivy_Regular(pObj->pFanin1); } +static inline Ivy_Obj_t * Ivy_ObjChild0( Ivy_Obj_t * pObj ) { return pObj->pFanin0; } +static inline Ivy_Obj_t * Ivy_ObjChild1( Ivy_Obj_t * pObj ) { return pObj->pFanin1; } +static inline Ivy_Obj_t * Ivy_ObjChild0Equiv( Ivy_Obj_t * pObj ) { assert( !Ivy_IsComplement(pObj) ); return Ivy_ObjFanin0(pObj)? Ivy_NotCond(Ivy_ObjFanin0(pObj)->pEquiv, Ivy_ObjFaninC0(pObj)) : NULL; } +static inline Ivy_Obj_t * Ivy_ObjChild1Equiv( Ivy_Obj_t * pObj ) { assert( !Ivy_IsComplement(pObj) ); return Ivy_ObjFanin1(pObj)? Ivy_NotCond(Ivy_ObjFanin1(pObj)->pEquiv, Ivy_ObjFaninC1(pObj)) : NULL; } +static inline Ivy_Obj_t * Ivy_ObjEquiv( Ivy_Obj_t * pObj ) { return Ivy_Regular(pObj)->pEquiv? Ivy_NotCond(Ivy_Regular(pObj)->pEquiv, Ivy_IsComplement(pObj)) : NULL; } +static inline int Ivy_ObjLevel( Ivy_Obj_t * pObj ) { return pObj->Level; } +static inline int Ivy_ObjLevelNew( Ivy_Obj_t * pObj ) { return 1 + Ivy_ObjIsExor(pObj) + IVY_MAX(Ivy_ObjFanin0(pObj)->Level, Ivy_ObjFanin1(pObj)->Level); } +static inline int Ivy_ObjFaninPhase( Ivy_Obj_t * pObj ) { return Ivy_IsComplement(pObj)? !Ivy_Regular(pObj)->fPhase : pObj->fPhase; } + +static inline void Ivy_ObjClean( Ivy_Obj_t * pObj ) +{ + int IdSaved = pObj->Id; + memset( pObj, 0, sizeof(Ivy_Obj_t) ); + pObj->Id = IdSaved; +} +static inline void Ivy_ObjOverwrite( Ivy_Obj_t * pBase, Ivy_Obj_t * pData ) +{ + int IdSaved = pBase->Id; + memcpy( pBase, pData, sizeof(Ivy_Obj_t) ); + pBase->Id = IdSaved; +} +static inline int Ivy_ObjWhatFanin( Ivy_Obj_t * pObj, Ivy_Obj_t * pFanin ) +{ + if ( Ivy_ObjFanin0(pObj) == pFanin ) return 0; + if ( Ivy_ObjFanin1(pObj) == pFanin ) return 1; + assert(0); return -1; +} +static inline int Ivy_ObjFanoutC( Ivy_Obj_t * pObj, Ivy_Obj_t * pFanout ) +{ + if ( Ivy_ObjFanin0(pFanout) == pObj ) return Ivy_ObjFaninC0(pObj); + if ( Ivy_ObjFanin1(pFanout) == pObj ) return Ivy_ObjFaninC1(pObj); + assert(0); return -1; +} + +// create the ghost of the new node +static inline Ivy_Obj_t * Ivy_ObjCreateGhost( Ivy_Man_t * p, Ivy_Obj_t * p0, Ivy_Obj_t * p1, Ivy_Type_t Type, Ivy_Init_t Init ) +{ + Ivy_Obj_t * pGhost, * pTemp; + assert( Type != IVY_AND || !Ivy_ObjIsConst1(Ivy_Regular(p0)) ); + assert( p1 == NULL || !Ivy_ObjIsConst1(Ivy_Regular(p1)) ); + assert( Type == IVY_PI || Ivy_Regular(p0) != Ivy_Regular(p1) ); + assert( Type != IVY_LATCH || !Ivy_IsComplement(p0) ); +// assert( p1 == NULL || (!Ivy_ObjIsLatch(Ivy_Regular(p0)) || !Ivy_ObjIsLatch(Ivy_Regular(p1))) ); + pGhost = Ivy_ManGhost(p); + pGhost->Type = Type; + pGhost->Init = Init; + pGhost->pFanin0 = p0; + pGhost->pFanin1 = p1; + if ( p1 && Ivy_ObjFaninId0(pGhost) > Ivy_ObjFaninId1(pGhost) ) + pTemp = pGhost->pFanin0, pGhost->pFanin0 = pGhost->pFanin1, pGhost->pFanin1 = pTemp; + return pGhost; +} + +// get the complemented initial state +static Ivy_Init_t Ivy_InitNotCond( Ivy_Init_t Init, int fCompl ) +{ + assert( Init != IVY_INIT_NONE ); + if ( fCompl == 0 ) + return Init; + if ( Init == IVY_INIT_0 ) + return IVY_INIT_1; + if ( Init == IVY_INIT_1 ) + return IVY_INIT_0; + return IVY_INIT_DC; +} + +// get the initial state after forward retiming over AND gate +static Ivy_Init_t Ivy_InitAnd( Ivy_Init_t InitA, Ivy_Init_t InitB ) +{ + assert( InitA != IVY_INIT_NONE && InitB != IVY_INIT_NONE ); + if ( InitA == IVY_INIT_0 || InitB == IVY_INIT_0 ) + return IVY_INIT_0; + if ( InitA == IVY_INIT_DC || InitB == IVY_INIT_DC ) + return IVY_INIT_DC; + return IVY_INIT_1; +} + +// get the initial state after forward retiming over EXOR gate +static Ivy_Init_t Ivy_InitExor( Ivy_Init_t InitA, Ivy_Init_t InitB ) +{ + assert( InitA != IVY_INIT_NONE && InitB != IVY_INIT_NONE ); + if ( InitA == IVY_INIT_DC || InitB == IVY_INIT_DC ) + return IVY_INIT_DC; + if ( InitA == IVY_INIT_0 && InitB == IVY_INIT_1 ) + return IVY_INIT_1; + if ( InitA == IVY_INIT_1 && InitB == IVY_INIT_0 ) + return IVY_INIT_1; + return IVY_INIT_0; +} + +// internal memory manager +static inline Ivy_Obj_t * Ivy_ManFetchMemory( Ivy_Man_t * p ) +{ + extern void Ivy_ManAddMemory( Ivy_Man_t * p ); + Ivy_Obj_t * pTemp; + if ( p->pListFree == NULL ) + Ivy_ManAddMemory( p ); + pTemp = p->pListFree; + p->pListFree = *((Ivy_Obj_t **)pTemp); + memset( pTemp, 0, sizeof(Ivy_Obj_t) ); + return pTemp; +} +static inline void Ivy_ManRecycleMemory( Ivy_Man_t * p, Ivy_Obj_t * pEntry ) +{ + pEntry->Type = IVY_NONE; // distinquishes dead node from live node + *((Ivy_Obj_t **)pEntry) = p->pListFree; + p->pListFree = pEntry; +} + + +//////////////////////////////////////////////////////////////////////// +/// ITERATORS /// +//////////////////////////////////////////////////////////////////////// + +// iterator over the primary inputs +#define Ivy_ManForEachPi( p, pObj, i ) \ + Vec_PtrForEachEntry( p->vPis, pObj, i ) +// iterator over the primary outputs +#define Ivy_ManForEachPo( p, pObj, i ) \ + Vec_PtrForEachEntry( p->vPos, pObj, i ) +// iterator over all objects, including those currently not used +#define Ivy_ManForEachObj( p, pObj, i ) \ + Vec_PtrForEachEntry( p->vObjs, pObj, i ) if ( (pObj) == NULL ) {} else +// iterator over the combinational inputs +#define Ivy_ManForEachCi( p, pObj, i ) \ + Ivy_ManForEachObj( p, pObj, i ) if ( !Ivy_ObjIsCi(pObj) ) {} else +// iterator over the combinational outputs +#define Ivy_ManForEachCo( p, pObj, i ) \ + Ivy_ManForEachObj( p, pObj, i ) if ( !Ivy_ObjIsCo(pObj) ) {} else +// iterator over logic nodes (AND and EXOR gates) +#define Ivy_ManForEachNode( p, pObj, i ) \ + Ivy_ManForEachObj( p, pObj, i ) if ( !Ivy_ObjIsNode(pObj) ) {} else +// iterator over logic latches +#define Ivy_ManForEachLatch( p, pObj, i ) \ + Ivy_ManForEachObj( p, pObj, i ) if ( !Ivy_ObjIsLatch(pObj) ) {} else +// iterator over the nodes whose IDs are stored in the array +#define Ivy_ManForEachNodeVec( p, vIds, pObj, i ) \ + for ( i = 0; i < Vec_IntSize(vIds) && ((pObj) = Ivy_ManObj(p, Vec_IntEntry(vIds,i))); i++ ) +// iterator over the fanouts of an object +#define Ivy_ObjForEachFanout( p, pObj, vArray, pFanout, i ) \ + for ( i = 0, Ivy_ObjCollectFanouts(p, pObj, vArray); \ + i < Vec_PtrSize(vArray) && ((pFanout) = Vec_PtrEntry(vArray,i)); i++ ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== ivyBalance.c ========================================================*/ +extern Ivy_Man_t * Ivy_ManBalance( Ivy_Man_t * p, int fUpdateLevel ); +extern Ivy_Obj_t * Ivy_NodeBalanceBuildSuper( Ivy_Man_t * p, Vec_Ptr_t * vSuper, Ivy_Type_t Type, int fUpdateLevel ); +/*=== ivyCanon.c ========================================================*/ +extern Ivy_Obj_t * Ivy_CanonAnd( Ivy_Man_t * p, Ivy_Obj_t * p0, Ivy_Obj_t * p1 ); +extern Ivy_Obj_t * Ivy_CanonExor( Ivy_Man_t * p, Ivy_Obj_t * p0, Ivy_Obj_t * p1 ); +extern Ivy_Obj_t * Ivy_CanonLatch( Ivy_Man_t * p, Ivy_Obj_t * pObj, Ivy_Init_t Init ); +/*=== ivyCheck.c ========================================================*/ +extern int Ivy_ManCheck( Ivy_Man_t * p ); +extern int Ivy_ManCheckFanoutNums( Ivy_Man_t * p ); +extern int Ivy_ManCheckFanouts( Ivy_Man_t * p ); +extern int Ivy_ManCheckChoices( Ivy_Man_t * p ); +/*=== ivyCut.c ==========================================================*/ +extern void Ivy_ManSeqFindCut( Ivy_Man_t * p, Ivy_Obj_t * pNode, Vec_Int_t * vFront, Vec_Int_t * vInside, int nSize ); +extern Ivy_Store_t * Ivy_NodeFindCutsAll( Ivy_Man_t * p, Ivy_Obj_t * pObj, int nLeaves ); +/*=== ivyDfs.c ==========================================================*/ +extern Vec_Int_t * Ivy_ManDfs( Ivy_Man_t * p ); +extern Vec_Int_t * Ivy_ManDfsSeq( Ivy_Man_t * p, Vec_Int_t ** pvLatches ); +extern void Ivy_ManCollectCone( Ivy_Obj_t * pObj, Vec_Ptr_t * vFront, Vec_Ptr_t * vCone ); +extern Vec_Vec_t * Ivy_ManLevelize( Ivy_Man_t * p ); +extern Vec_Int_t * Ivy_ManRequiredLevels( Ivy_Man_t * p ); +extern int Ivy_ManIsAcyclic( Ivy_Man_t * p ); +extern int Ivy_ManSetLevels( Ivy_Man_t * p, int fHaig ); +/*=== ivyDsd.c ==========================================================*/ +extern int Ivy_TruthDsd( unsigned uTruth, Vec_Int_t * vTree ); +extern void Ivy_TruthDsdPrint( FILE * pFile, Vec_Int_t * vTree ); +extern unsigned Ivy_TruthDsdCompute( Vec_Int_t * vTree ); +extern void Ivy_TruthDsdComputePrint( unsigned uTruth ); +extern Ivy_Obj_t * Ivy_ManDsdConstruct( Ivy_Man_t * p, Vec_Int_t * vFront, Vec_Int_t * vTree ); +/*=== ivyFanout.c ==========================================================*/ +extern void Ivy_ManStartFanout( Ivy_Man_t * p ); +extern void Ivy_ManStopFanout( Ivy_Man_t * p ); +extern void Ivy_ObjAddFanout( Ivy_Man_t * p, Ivy_Obj_t * pObj, Ivy_Obj_t * pFanout ); +extern void Ivy_ObjDeleteFanout( Ivy_Man_t * p, Ivy_Obj_t * pObj, Ivy_Obj_t * pFanout ); +extern void Ivy_ObjPatchFanout( Ivy_Man_t * p, Ivy_Obj_t * pObj, Ivy_Obj_t * pFanoutOld, Ivy_Obj_t * pFanoutNew ); +extern void Ivy_ObjCollectFanouts( Ivy_Man_t * p, Ivy_Obj_t * pObj, Vec_Ptr_t * vArray ); +extern Ivy_Obj_t * Ivy_ObjReadFirstFanout( Ivy_Man_t * p, Ivy_Obj_t * pObj ); +extern int Ivy_ObjFanoutNum( Ivy_Man_t * p, Ivy_Obj_t * pObj ); +/*=== ivyFastMap.c =============================================================*/ +extern void Ivy_FastMapPerform( Ivy_Man_t * pAig, int nLimit, int fRecovery, int fVerbose ); +extern void Ivy_FastMapStop( Ivy_Man_t * pAig ); +extern void Ivy_FastMapReadSupp( Ivy_Man_t * pAig, Ivy_Obj_t * pObj, Vec_Int_t * vLeaves ); +extern void Ivy_FastMapReverseLevel( Ivy_Man_t * pAig ); +/*=== ivyFraig.c ==========================================================*/ +extern int Ivy_FraigProve( Ivy_Man_t ** ppManAig, void * pPars ); +extern Ivy_Man_t * Ivy_FraigPerform( Ivy_Man_t * pManAig, Ivy_FraigParams_t * pParams ); +extern Ivy_Man_t * Ivy_FraigMiter( Ivy_Man_t * pManAig, Ivy_FraigParams_t * pParams ); +extern void Ivy_FraigParamsDefault( Ivy_FraigParams_t * pParams ); +/*=== ivyHaig.c ==========================================================*/ +extern void Ivy_ManHaigStart( Ivy_Man_t * p, int fVerbose ); +extern void Ivy_ManHaigTrasfer( Ivy_Man_t * p, Ivy_Man_t * pNew ); +extern void Ivy_ManHaigStop( Ivy_Man_t * p ); +extern void Ivy_ManHaigPostprocess( Ivy_Man_t * p, int fVerbose ); +extern void Ivy_ManHaigCreateObj( Ivy_Man_t * p, Ivy_Obj_t * pObj ); +extern void Ivy_ManHaigCreateChoice( Ivy_Man_t * p, Ivy_Obj_t * pObjOld, Ivy_Obj_t * pObjNew ); +extern void Ivy_ManHaigSimulate( Ivy_Man_t * p ); +/*=== ivyMan.c ==========================================================*/ +extern Ivy_Man_t * Ivy_ManStart(); +extern Ivy_Man_t * Ivy_ManStartFrom( Ivy_Man_t * p ); +extern Ivy_Man_t * Ivy_ManDup( Ivy_Man_t * p ); +extern Ivy_Man_t * Ivy_ManFrames( Ivy_Man_t * pMan, int nLatches, int nFrames, int fInit, Vec_Ptr_t ** pvMapping ); +extern void Ivy_ManStop( Ivy_Man_t * p ); +extern int Ivy_ManCleanup( Ivy_Man_t * p ); +extern int Ivy_ManPropagateBuffers( Ivy_Man_t * p, int fUpdateLevel ); +extern void Ivy_ManPrintStats( Ivy_Man_t * p ); +extern void Ivy_ManMakeSeq( Ivy_Man_t * p, int nLatches, int * pInits ); +/*=== ivyMem.c ==========================================================*/ +extern void Ivy_ManStartMemory( Ivy_Man_t * p ); +extern void Ivy_ManStopMemory( Ivy_Man_t * p ); +/*=== ivyMulti.c ==========================================================*/ +extern Ivy_Obj_t * Ivy_Multi( Ivy_Man_t * p, Ivy_Obj_t ** pArgs, int nArgs, Ivy_Type_t Type ); +extern Ivy_Obj_t * Ivy_Multi1( Ivy_Man_t * p, Ivy_Obj_t ** pArgs, int nArgs, Ivy_Type_t Type ); +extern Ivy_Obj_t * Ivy_Multi_rec( Ivy_Man_t * p, Ivy_Obj_t ** ppObjs, int nObjs, Ivy_Type_t Type ); +extern Ivy_Obj_t * Ivy_MultiBalance_rec( Ivy_Man_t * p, Ivy_Obj_t ** pArgs, int nArgs, Ivy_Type_t Type ); +extern int Ivy_MultiPlus( Ivy_Man_t * p, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vCone, Ivy_Type_t Type, int nLimit, Vec_Ptr_t * vSol ); +/*=== ivyObj.c ==========================================================*/ +extern Ivy_Obj_t * Ivy_ObjCreatePi( Ivy_Man_t * p ); +extern Ivy_Obj_t * Ivy_ObjCreatePo( Ivy_Man_t * p, Ivy_Obj_t * pDriver ); +extern Ivy_Obj_t * Ivy_ObjCreate( Ivy_Man_t * p, Ivy_Obj_t * pGhost ); +extern void Ivy_ObjConnect( Ivy_Man_t * p, Ivy_Obj_t * pObj, Ivy_Obj_t * pFan0, Ivy_Obj_t * pFan1 ); +extern void Ivy_ObjDisconnect( Ivy_Man_t * p, Ivy_Obj_t * pObj ); +extern void Ivy_ObjPatchFanin0( Ivy_Man_t * p, Ivy_Obj_t * pObj, Ivy_Obj_t * pFaninNew ); +extern void Ivy_ObjDelete( Ivy_Man_t * p, Ivy_Obj_t * pObj, int fFreeTop ); +extern void Ivy_ObjDelete_rec( Ivy_Man_t * p, Ivy_Obj_t * pObj, int fFreeTop ); +extern void Ivy_ObjReplace( Ivy_Man_t * p, Ivy_Obj_t * pObjOld, Ivy_Obj_t * pObjNew, int fDeleteOld, int fFreeTop, int fUpdateLevel ); +extern void Ivy_NodeFixBufferFanins( Ivy_Man_t * p, Ivy_Obj_t * pNode, int fUpdateLevel ); +/*=== ivyOper.c =========================================================*/ +extern Ivy_Obj_t * Ivy_Oper( Ivy_Man_t * p, Ivy_Obj_t * p0, Ivy_Obj_t * p1, Ivy_Type_t Type ); +extern Ivy_Obj_t * Ivy_And( Ivy_Man_t * p, Ivy_Obj_t * p0, Ivy_Obj_t * p1 ); +extern Ivy_Obj_t * Ivy_Or( Ivy_Man_t * p, Ivy_Obj_t * p0, Ivy_Obj_t * p1 ); +extern Ivy_Obj_t * Ivy_Exor( Ivy_Man_t * p, Ivy_Obj_t * p0, Ivy_Obj_t * p1 ); +extern Ivy_Obj_t * Ivy_Mux( Ivy_Man_t * p, Ivy_Obj_t * pC, Ivy_Obj_t * p1, Ivy_Obj_t * p0 ); +extern Ivy_Obj_t * Ivy_Maj( Ivy_Man_t * p, Ivy_Obj_t * pA, Ivy_Obj_t * pB, Ivy_Obj_t * pC ); +extern Ivy_Obj_t * Ivy_Miter( Ivy_Man_t * p, Vec_Ptr_t * vPairs ); +extern Ivy_Obj_t * Ivy_Latch( Ivy_Man_t * p, Ivy_Obj_t * pObj, Ivy_Init_t Init ); +/*=== ivyResyn.c =========================================================*/ +extern Ivy_Man_t * Ivy_ManResyn0( Ivy_Man_t * p, int fUpdateLevel, int fVerbose ); +extern Ivy_Man_t * Ivy_ManResyn( Ivy_Man_t * p, int fUpdateLevel, int fVerbose ); +extern Ivy_Man_t * Ivy_ManRwsat( Ivy_Man_t * pMan, int fVerbose ); +/*=== ivyRewrite.c =========================================================*/ +extern int Ivy_ManSeqRewrite( Ivy_Man_t * p, int fUpdateLevel, int fUseZeroCost ); +extern int Ivy_ManRewriteAlg( Ivy_Man_t * p, int fUpdateLevel, int fUseZeroCost ); +extern int Ivy_ManRewritePre( Ivy_Man_t * p, int fUpdateLevel, int fUseZeroCost, int fVerbose ); +/*=== ivySeq.c =========================================================*/ +extern int Ivy_ManRewriteSeq( Ivy_Man_t * p, int fUseZeroCost, int fVerbose ); +/*=== ivyShow.c =========================================================*/ +extern void Ivy_ManShow( Ivy_Man_t * pMan, int fHaig, Vec_Ptr_t * vBold ); +/*=== ivyTable.c ========================================================*/ +extern Ivy_Obj_t * Ivy_TableLookup( Ivy_Man_t * p, Ivy_Obj_t * pObj ); +extern void Ivy_TableInsert( Ivy_Man_t * p, Ivy_Obj_t * pObj ); +extern void Ivy_TableDelete( Ivy_Man_t * p, Ivy_Obj_t * pObj ); +extern void Ivy_TableUpdate( Ivy_Man_t * p, Ivy_Obj_t * pObj, int ObjIdNew ); +extern int Ivy_TableCountEntries( Ivy_Man_t * p ); +extern void Ivy_TableProfile( Ivy_Man_t * p ); +/*=== ivyUtil.c =========================================================*/ +extern void Ivy_ManIncrementTravId( Ivy_Man_t * p ); +extern void Ivy_ManCleanTravId( Ivy_Man_t * p ); +extern unsigned * Ivy_ManCutTruth( Ivy_Man_t * p, Ivy_Obj_t * pRoot, Vec_Int_t * vLeaves, Vec_Int_t * vNodes, Vec_Int_t * vTruth ); +extern void Ivy_ManCollectCut( Ivy_Man_t * p, Ivy_Obj_t * pRoot, Vec_Int_t * vLeaves, Vec_Int_t * vNodes ); +extern Vec_Int_t * Ivy_ManLatches( Ivy_Man_t * p ); +extern int Ivy_ManLevels( Ivy_Man_t * p ); +extern void Ivy_ManResetLevels( Ivy_Man_t * p ); +extern int Ivy_ObjMffcLabel( Ivy_Man_t * p, Ivy_Obj_t * pObj ); +extern void Ivy_ObjUpdateLevel_rec( Ivy_Man_t * p, Ivy_Obj_t * pObj ); +extern void Ivy_ObjUpdateLevelR_rec( Ivy_Man_t * p, Ivy_Obj_t * pObj, int ReqNew ); +extern int Ivy_ObjIsMuxType( Ivy_Obj_t * pObj ); +extern Ivy_Obj_t * Ivy_ObjRecognizeMux( Ivy_Obj_t * pObj, Ivy_Obj_t ** ppObjT, Ivy_Obj_t ** ppObjE ); +extern Ivy_Obj_t * Ivy_ObjReal( Ivy_Obj_t * pObj ); +extern void Ivy_ObjPrintVerbose( Ivy_Man_t * p, Ivy_Obj_t * pObj, int fHaig ); +extern void Ivy_ManPrintVerbose( Ivy_Man_t * p, int fHaig ); +extern int Ivy_CutTruthPrint( Ivy_Man_t * p, Ivy_Cut_t * pCut, unsigned uTruth ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/aig/ivy/ivyBalance.c b/abc_with_bb_support/src/aig/ivy/ivyBalance.c new file mode 100644 index 000000000..9401b2d22 --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyBalance.c @@ -0,0 +1,404 @@ +/**CFile**************************************************************** + + FileName [ivyBalance.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [Algebraic AIG balancing.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyBalance.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Ivy_NodeBalance_rec( Ivy_Man_t * pNew, Ivy_Obj_t * pObj, Vec_Vec_t * vStore, int Level, int fUpdateLevel ); +static Vec_Ptr_t * Ivy_NodeBalanceCone( Ivy_Obj_t * pObj, Vec_Vec_t * vStore, int Level ); +static int Ivy_NodeBalanceFindLeft( Vec_Ptr_t * vSuper ); +static void Ivy_NodeBalancePermute( Ivy_Man_t * p, Vec_Ptr_t * vSuper, int LeftBound, int fExor ); +static void Ivy_NodeBalancePushUniqueOrderByLevel( Vec_Ptr_t * vStore, Ivy_Obj_t * pObj ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs algebraic balancing of the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Man_t * Ivy_ManBalance( Ivy_Man_t * p, int fUpdateLevel ) +{ + Ivy_Man_t * pNew; + Ivy_Obj_t * pObj, * pDriver; + Vec_Vec_t * vStore; + int i, NewNodeId; + // clean the old manager + Ivy_ManCleanTravId( p ); + // create the new manager + pNew = Ivy_ManStart(); + // map the nodes + Ivy_ManConst1(p)->TravId = Ivy_EdgeFromNode( Ivy_ManConst1(pNew) ); + Ivy_ManForEachPi( p, pObj, i ) + pObj->TravId = Ivy_EdgeFromNode( Ivy_ObjCreatePi(pNew) ); + // if HAIG is defined, trasfer the pointers to the PIs/latches +// if ( p->pHaig ) +// Ivy_ManHaigTrasfer( p, pNew ); + // balance the AIG + vStore = Vec_VecAlloc( 50 ); + Ivy_ManForEachPo( p, pObj, i ) + { + pDriver = Ivy_ObjReal( Ivy_ObjChild0(pObj) ); + NewNodeId = Ivy_NodeBalance_rec( pNew, Ivy_Regular(pDriver), vStore, 0, fUpdateLevel ); + NewNodeId = Ivy_EdgeNotCond( NewNodeId, Ivy_IsComplement(pDriver) ); + Ivy_ObjCreatePo( pNew, Ivy_EdgeToNode(pNew, NewNodeId) ); + } + Vec_VecFree( vStore ); + if ( i = Ivy_ManCleanup( pNew ) ) + { +// printf( "Cleanup after balancing removed %d dangling nodes.\n", i ); + } + // check the resulting AIG + if ( !Ivy_ManCheck(pNew) ) + printf( "Ivy_ManBalance(): The check has failed.\n" ); + return pNew; +} + +/**Function************************************************************* + + Synopsis [Procedure used for sorting the nodes in decreasing order of levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_NodeCompareLevelsDecrease( Ivy_Obj_t ** pp1, Ivy_Obj_t ** pp2 ) +{ + int Diff = Ivy_Regular(*pp1)->Level - Ivy_Regular(*pp2)->Level; + if ( Diff > 0 ) + return -1; + if ( Diff < 0 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Returns the ID of new node constructed.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_NodeBalance_rec( Ivy_Man_t * pNew, Ivy_Obj_t * pObjOld, Vec_Vec_t * vStore, int Level, int fUpdateLevel ) +{ + Ivy_Obj_t * pObjNew; + Vec_Ptr_t * vSuper; + int i, NewNodeId; + assert( !Ivy_IsComplement(pObjOld) ); + assert( !Ivy_ObjIsBuf(pObjOld) ); + // return if the result is known + if ( Ivy_ObjIsConst1(pObjOld) ) + return pObjOld->TravId; + if ( pObjOld->TravId ) + return pObjOld->TravId; + assert( Ivy_ObjIsNode(pObjOld) ); + // get the implication supergate + vSuper = Ivy_NodeBalanceCone( pObjOld, vStore, Level ); + if ( vSuper->nSize == 0 ) + { // it means that the supergate contains two nodes in the opposite polarity + pObjOld->TravId = Ivy_EdgeFromNode( Ivy_ManConst0(pNew) ); + return pObjOld->TravId; + } + if ( vSuper->nSize < 2 ) + printf( "BUG!\n" ); + // for each old node, derive the new well-balanced node + for ( i = 0; i < vSuper->nSize; i++ ) + { + NewNodeId = Ivy_NodeBalance_rec( pNew, Ivy_Regular(vSuper->pArray[i]), vStore, Level + 1, fUpdateLevel ); + NewNodeId = Ivy_EdgeNotCond( NewNodeId, Ivy_IsComplement(vSuper->pArray[i]) ); + vSuper->pArray[i] = Ivy_EdgeToNode( pNew, NewNodeId ); + } + // build the supergate + pObjNew = Ivy_NodeBalanceBuildSuper( pNew, vSuper, Ivy_ObjType(pObjOld), fUpdateLevel ); + vSuper->nSize = 0; + // make sure the balanced node is not assigned + assert( pObjOld->TravId == 0 ); + pObjOld->TravId = Ivy_EdgeFromNode( pObjNew ); +// assert( pObjOld->Level >= Ivy_Regular(pObjNew)->Level ); + return pObjOld->TravId; +} + +/**Function************************************************************* + + Synopsis [Builds implication supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_NodeBalanceBuildSuper( Ivy_Man_t * p, Vec_Ptr_t * vSuper, Ivy_Type_t Type, int fUpdateLevel ) +{ + Ivy_Obj_t * pObj1, * pObj2; + int LeftBound; + assert( vSuper->nSize > 1 ); + // sort the new nodes by level in the decreasing order + Vec_PtrSort( vSuper, Ivy_NodeCompareLevelsDecrease ); + // balance the nodes + while ( vSuper->nSize > 1 ) + { + // find the left bound on the node to be paired + LeftBound = (!fUpdateLevel)? 0 : Ivy_NodeBalanceFindLeft( vSuper ); + // find the node that can be shared (if no such node, randomize choice) + Ivy_NodeBalancePermute( p, vSuper, LeftBound, Type == IVY_EXOR ); + // pull out the last two nodes + pObj1 = Vec_PtrPop(vSuper); + pObj2 = Vec_PtrPop(vSuper); + Ivy_NodeBalancePushUniqueOrderByLevel( vSuper, Ivy_Oper(p, pObj1, pObj2, Type) ); + } + return Vec_PtrEntry(vSuper, 0); +} + +/**Function************************************************************* + + Synopsis [Collects the nodes of the supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_NodeBalanceCone_rec( Ivy_Obj_t * pRoot, Ivy_Obj_t * pObj, Vec_Ptr_t * vSuper ) +{ + int RetValue1, RetValue2, i; + // check if the node is visited + if ( Ivy_Regular(pObj)->fMarkB ) + { + // check if the node occurs in the same polarity + for ( i = 0; i < vSuper->nSize; i++ ) + if ( vSuper->pArray[i] == pObj ) + return 1; + // check if the node is present in the opposite polarity + for ( i = 0; i < vSuper->nSize; i++ ) + if ( vSuper->pArray[i] == Ivy_Not(pObj) ) + return -1; + assert( 0 ); + return 0; + } + // if the new node is complemented or a PI, another gate begins + if ( pObj != pRoot && (Ivy_IsComplement(pObj) || Ivy_ObjType(pObj) != Ivy_ObjType(pRoot) || Ivy_ObjRefs(pObj) > 1) ) + { + Vec_PtrPush( vSuper, pObj ); + Ivy_Regular(pObj)->fMarkB = 1; + return 0; + } + assert( !Ivy_IsComplement(pObj) ); + assert( Ivy_ObjIsNode(pObj) ); + // go through the branches + RetValue1 = Ivy_NodeBalanceCone_rec( pRoot, Ivy_ObjReal( Ivy_ObjChild0(pObj) ), vSuper ); + RetValue2 = Ivy_NodeBalanceCone_rec( pRoot, Ivy_ObjReal( Ivy_ObjChild1(pObj) ), vSuper ); + if ( RetValue1 == -1 || RetValue2 == -1 ) + return -1; + // return 1 if at least one branch has a duplicate + return RetValue1 || RetValue2; +} + +/**Function************************************************************* + + Synopsis [Collects the nodes of the supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Ivy_NodeBalanceCone( Ivy_Obj_t * pObj, Vec_Vec_t * vStore, int Level ) +{ + Vec_Ptr_t * vNodes; + int RetValue, i; + assert( !Ivy_IsComplement(pObj) ); + // extend the storage + if ( Vec_VecSize( vStore ) <= Level ) + Vec_VecPush( vStore, Level, 0 ); + // get the temporary array of nodes + vNodes = Vec_VecEntry( vStore, Level ); + Vec_PtrClear( vNodes ); + // collect the nodes in the implication supergate + RetValue = Ivy_NodeBalanceCone_rec( pObj, pObj, vNodes ); + assert( vNodes->nSize > 1 ); + // unmark the visited nodes + Vec_PtrForEachEntry( vNodes, pObj, i ) + Ivy_Regular(pObj)->fMarkB = 0; + // if we found the node and its complement in the same implication supergate, + // return empty set of nodes (meaning that we should use constant-0 node) + if ( RetValue == -1 ) + vNodes->nSize = 0; + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Finds the left bound on the next candidate to be paired.] + + Description [The nodes in the array are in the decreasing order of levels. + The last node in the array has the smallest level. By default it would be paired + with the next node on the left. However, it may be possible to pair it with some + other node on the left, in such a way that the new node is shared. This procedure + finds the index of the left-most node, which can be paired with the last node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_NodeBalanceFindLeft( Vec_Ptr_t * vSuper ) +{ + Ivy_Obj_t * pObjRight, * pObjLeft; + int Current; + // if two or less nodes, pair with the first + if ( Vec_PtrSize(vSuper) < 3 ) + return 0; + // set the pointer to the one before the last + Current = Vec_PtrSize(vSuper) - 2; + pObjRight = Vec_PtrEntry( vSuper, Current ); + // go through the nodes to the left of this one + for ( Current--; Current >= 0; Current-- ) + { + // get the next node on the left + pObjLeft = Vec_PtrEntry( vSuper, Current ); + // if the level of this node is different, quit the loop + if ( Ivy_Regular(pObjLeft)->Level != Ivy_Regular(pObjRight)->Level ) + break; + } + Current++; + // get the node, for which the equality holds + pObjLeft = Vec_PtrEntry( vSuper, Current ); + assert( Ivy_Regular(pObjLeft)->Level == Ivy_Regular(pObjRight)->Level ); + return Current; +} + +/**Function************************************************************* + + Synopsis [Moves closer to the end the node that is best for sharing.] + + Description [If there is no node with sharing, randomly chooses one of + the legal nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_NodeBalancePermute( Ivy_Man_t * p, Vec_Ptr_t * vSuper, int LeftBound, int fExor ) +{ + Ivy_Obj_t * pObj1, * pObj2, * pObj3, * pGhost; + int RightBound, i; + // get the right bound + RightBound = Vec_PtrSize(vSuper) - 2; + assert( LeftBound <= RightBound ); + if ( LeftBound == RightBound ) + return; + // get the two last nodes + pObj1 = Vec_PtrEntry( vSuper, RightBound + 1 ); + pObj2 = Vec_PtrEntry( vSuper, RightBound ); + if ( Ivy_Regular(pObj1) == p->pConst1 || Ivy_Regular(pObj2) == p->pConst1 ) + return; + // find the first node that can be shared + for ( i = RightBound; i >= LeftBound; i-- ) + { + pObj3 = Vec_PtrEntry( vSuper, i ); + if ( Ivy_Regular(pObj3) == p->pConst1 ) + { + Vec_PtrWriteEntry( vSuper, i, pObj2 ); + Vec_PtrWriteEntry( vSuper, RightBound, pObj3 ); + return; + } + pGhost = Ivy_ObjCreateGhost( p, pObj1, pObj3, fExor? IVY_EXOR : IVY_AND, IVY_INIT_NONE ); + if ( Ivy_TableLookup( p, pGhost ) ) + { + if ( pObj3 == pObj2 ) + return; + Vec_PtrWriteEntry( vSuper, i, pObj2 ); + Vec_PtrWriteEntry( vSuper, RightBound, pObj3 ); + return; + } + } +/* + // we did not find the node to share, randomize choice + { + int Choice = rand() % (RightBound - LeftBound + 1); + pObj3 = Vec_PtrEntry( vSuper, LeftBound + Choice ); + if ( pObj3 == pObj2 ) + return; + Vec_PtrWriteEntry( vSuper, LeftBound + Choice, pObj2 ); + Vec_PtrWriteEntry( vSuper, RightBound, pObj3 ); + } +*/ +} + +/**Function************************************************************* + + Synopsis [Inserts a new node in the order by levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_NodeBalancePushUniqueOrderByLevel( Vec_Ptr_t * vStore, Ivy_Obj_t * pObj ) +{ + Ivy_Obj_t * pObj1, * pObj2; + int i; + if ( Vec_PtrPushUnique(vStore, pObj) ) + return; + // find the p of the node + for ( i = vStore->nSize-1; i > 0; i-- ) + { + pObj1 = vStore->pArray[i ]; + pObj2 = vStore->pArray[i-1]; + if ( Ivy_Regular(pObj1)->Level <= Ivy_Regular(pObj2)->Level ) + break; + vStore->pArray[i ] = pObj2; + vStore->pArray[i-1] = pObj1; + } +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyCanon.c b/abc_with_bb_support/src/aig/ivy/ivyCanon.c new file mode 100644 index 000000000..681487148 --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyCanon.c @@ -0,0 +1,144 @@ +/**CFile**************************************************************** + + FileName [ivyCanon.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [Finding canonical form of objects.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyCanon.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Ivy_Obj_t * Ivy_TableLookupPair_rec( Ivy_Man_t * p, Ivy_Obj_t * pObj0, Ivy_Obj_t * pObj1, int fCompl0, int fCompl1, Ivy_Type_t Type ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Creates the canonical form of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_CanonPair_rec( Ivy_Man_t * p, Ivy_Obj_t * pGhost ) +{ + Ivy_Obj_t * pResult, * pLat0, * pLat1; + Ivy_Init_t Init, Init0, Init1; + int fCompl0, fCompl1; + Ivy_Type_t Type; + assert( Ivy_ObjIsNode(pGhost) ); + assert( Ivy_ObjIsAnd(pGhost) || (!Ivy_ObjFaninC0(pGhost) && !Ivy_ObjFaninC1(pGhost)) ); + assert( Ivy_ObjFaninId0(pGhost) != 0 && Ivy_ObjFaninId1(pGhost) != 0 ); + // consider the case when the pair is canonical + if ( !Ivy_ObjIsLatch(Ivy_ObjFanin0(pGhost)) || !Ivy_ObjIsLatch(Ivy_ObjFanin1(pGhost)) ) + { + if ( pResult = Ivy_TableLookup( p, pGhost ) ) + return pResult; + return Ivy_ObjCreate( p, pGhost ); + } + /// remember the latches + pLat0 = Ivy_ObjFanin0(pGhost); + pLat1 = Ivy_ObjFanin1(pGhost); + // remember type and compls + Type = Ivy_ObjType(pGhost); + fCompl0 = Ivy_ObjFaninC0(pGhost); + fCompl1 = Ivy_ObjFaninC1(pGhost); + // call recursively + pResult = Ivy_Oper( p, Ivy_NotCond(Ivy_ObjFanin0(pLat0), fCompl0), Ivy_NotCond(Ivy_ObjFanin0(pLat1), fCompl1), Type ); + // build latch on top of this + Init0 = Ivy_InitNotCond( Ivy_ObjInit(pLat0), fCompl0 ); + Init1 = Ivy_InitNotCond( Ivy_ObjInit(pLat1), fCompl1 ); + Init = (Type == IVY_AND)? Ivy_InitAnd(Init0, Init1) : Ivy_InitExor(Init0, Init1); + return Ivy_Latch( p, pResult, Init ); +} + +/**Function************************************************************* + + Synopsis [Creates the canonical form of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_CanonAnd( Ivy_Man_t * p, Ivy_Obj_t * pObj0, Ivy_Obj_t * pObj1 ) +{ + Ivy_Obj_t * pGhost, * pResult; + pGhost = Ivy_ObjCreateGhost( p, pObj0, pObj1, IVY_AND, IVY_INIT_NONE ); + pResult = Ivy_CanonPair_rec( p, pGhost ); + return pResult; +} + +/**Function************************************************************* + + Synopsis [Creates the canonical form of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_CanonExor( Ivy_Man_t * p, Ivy_Obj_t * pObj0, Ivy_Obj_t * pObj1 ) +{ + Ivy_Obj_t * pGhost, * pResult; + int fCompl = Ivy_IsComplement(pObj0) ^ Ivy_IsComplement(pObj1); + pObj0 = Ivy_Regular(pObj0); + pObj1 = Ivy_Regular(pObj1); + pGhost = Ivy_ObjCreateGhost( p, pObj0, pObj1, IVY_EXOR, IVY_INIT_NONE ); + pResult = Ivy_CanonPair_rec( p, pGhost ); + return Ivy_NotCond( pResult, fCompl ); +} + +/**Function************************************************************* + + Synopsis [Creates the canonical form of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_CanonLatch( Ivy_Man_t * p, Ivy_Obj_t * pObj, Ivy_Init_t Init ) +{ + Ivy_Obj_t * pGhost, * pResult; + int fCompl = Ivy_IsComplement(pObj); + pObj = Ivy_Regular(pObj); + pGhost = Ivy_ObjCreateGhost( p, pObj, NULL, IVY_LATCH, Ivy_InitNotCond(Init, fCompl) ); + pResult = Ivy_TableLookup( p, pGhost ); + if ( pResult == NULL ) + pResult = Ivy_ObjCreate( p, pGhost ); + return Ivy_NotCond( pResult, fCompl ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyCheck.c b/abc_with_bb_support/src/aig/ivy/ivyCheck.c new file mode 100644 index 000000000..7a2dd5b49 --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyCheck.c @@ -0,0 +1,273 @@ +/**CFile**************************************************************** + + FileName [ivyCheck.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [AIG checking procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyCheck.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Checks the consistency of the AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManCheck( Ivy_Man_t * p ) +{ + Ivy_Obj_t * pObj, * pObj2; + int i; + Ivy_ManForEachObj( p, pObj, i ) + { + // skip deleted nodes + if ( Ivy_ObjId(pObj) != i ) + { + printf( "Ivy_ManCheck: Node with ID %d is listed as number %d in the array of objects.\n", pObj->Id, i ); + return 0; + } + // consider the constant node and PIs + if ( i == 0 || Ivy_ObjIsPi(pObj) ) + { + if ( Ivy_ObjFaninId0(pObj) || Ivy_ObjFaninId1(pObj) || Ivy_ObjLevel(pObj) ) + { + printf( "Ivy_ManCheck: The AIG has non-standard constant or PI node with ID \"%d\".\n", pObj->Id ); + return 0; + } + continue; + } + if ( Ivy_ObjIsPo(pObj) ) + { + if ( Ivy_ObjFaninId1(pObj) ) + { + printf( "Ivy_ManCheck: The AIG has non-standard PO node with ID \"%d\".\n", pObj->Id ); + return 0; + } + continue; + } + if ( Ivy_ObjIsBuf(pObj) ) + { + if ( Ivy_ObjFanin1(pObj) ) + { + printf( "Ivy_ManCheck: The buffer with ID \"%d\" contains second fanin.\n", pObj->Id ); + return 0; + } + continue; + } + if ( Ivy_ObjIsLatch(pObj) ) + { + if ( Ivy_ObjFanin1(pObj) ) + { + printf( "Ivy_ManCheck: The latch with ID \"%d\" contains second fanin.\n", pObj->Id ); + return 0; + } + if ( Ivy_ObjInit(pObj) == IVY_INIT_NONE ) + { + printf( "Ivy_ManCheck: The latch with ID \"%d\" does not have initial state.\n", pObj->Id ); + return 0; + } + pObj2 = Ivy_TableLookup( p, pObj ); + if ( pObj2 != pObj ) + printf( "Ivy_ManCheck: Latch with ID \"%d\" is not in the structural hashing table.\n", pObj->Id ); + continue; + } + // consider the AND node + if ( !Ivy_ObjFanin0(pObj) || !Ivy_ObjFanin1(pObj) ) + { + printf( "Ivy_ManCheck: The AIG has internal node \"%d\" with a NULL fanin.\n", pObj->Id ); + return 0; + } + if ( Ivy_ObjFaninId0(pObj) >= Ivy_ObjFaninId1(pObj) ) + { + printf( "Ivy_ManCheck: The AIG has node \"%d\" with a wrong ordering of fanins.\n", pObj->Id ); + return 0; + } + if ( Ivy_ObjLevel(pObj) != Ivy_ObjLevelNew(pObj) ) + printf( "Ivy_ManCheck: Node with ID \"%d\" has level %d but should have level %d.\n", pObj->Id, Ivy_ObjLevel(pObj), Ivy_ObjLevelNew(pObj) ); + pObj2 = Ivy_TableLookup( p, pObj ); + if ( pObj2 != pObj ) + printf( "Ivy_ManCheck: Node with ID \"%d\" is not in the structural hashing table.\n", pObj->Id ); + if ( Ivy_ObjRefs(pObj) == 0 ) + printf( "Ivy_ManCheck: Node with ID \"%d\" has no fanouts.\n", pObj->Id ); + // check fanouts + if ( p->fFanout && Ivy_ObjRefs(pObj) != Ivy_ObjFanoutNum(p, pObj) ) + printf( "Ivy_ManCheck: Node with ID \"%d\" has mismatch between the number of fanouts and refs.\n", pObj->Id ); + } + // count the number of nodes in the table + if ( Ivy_TableCountEntries(p) != Ivy_ManAndNum(p) + Ivy_ManExorNum(p) + Ivy_ManLatchNum(p) ) + { + printf( "Ivy_ManCheck: The number of nodes in the structural hashing table is wrong.\n" ); + return 0; + } +// if ( !Ivy_ManCheckFanouts(p) ) +// return 0; + if ( !Ivy_ManIsAcyclic(p) ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Verifies the fanouts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManCheckFanoutNums( Ivy_Man_t * p ) +{ + Ivy_Obj_t * pObj; + int i, Counter = 0; + Ivy_ManForEachObj( p, pObj, i ) + if ( Ivy_ObjIsNode(pObj) ) + Counter += (Ivy_ObjRefs(pObj) == 0); + if ( Counter ) + printf( "Sequential AIG has %d dangling nodes.\n", Counter ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Verifies the fanouts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManCheckFanouts( Ivy_Man_t * p ) +{ + Vec_Ptr_t * vFanouts; + Ivy_Obj_t * pObj, * pFanout, * pFanin; + int i, k, RetValue = 1; + if ( !p->fFanout ) + return 1; + vFanouts = Vec_PtrAlloc( 100 ); + // make sure every fanin is a fanout + Ivy_ManForEachObj( p, pObj, i ) + { + pFanin = Ivy_ObjFanin0(pObj); + if ( pFanin == NULL ) + continue; + Ivy_ObjForEachFanout( p, pFanin, vFanouts, pFanout, k ) + if ( pFanout == pObj ) + break; + if ( k == Vec_PtrSize(vFanouts) ) + { + printf( "Node %d is a fanin of node %d but the fanout is not there.\n", pFanin->Id, pObj->Id ); + RetValue = 0; + } + + pFanin = Ivy_ObjFanin1(pObj); + if ( pFanin == NULL ) + continue; + Ivy_ObjForEachFanout( p, pFanin, vFanouts, pFanout, k ) + if ( pFanout == pObj ) + break; + if ( k == Vec_PtrSize(vFanouts) ) + { + printf( "Node %d is a fanin of node %d but the fanout is not there.\n", pFanin->Id, pObj->Id ); + RetValue = 0; + } + // check that the previous fanout has the same fanin + if ( pObj->pPrevFan0 ) + { + if ( Ivy_ObjFanin0(pObj->pPrevFan0) != Ivy_ObjFanin0(pObj) && + Ivy_ObjFanin0(pObj->pPrevFan0) != Ivy_ObjFanin1(pObj) && + Ivy_ObjFanin1(pObj->pPrevFan0) != Ivy_ObjFanin0(pObj) && + Ivy_ObjFanin1(pObj->pPrevFan0) != Ivy_ObjFanin1(pObj) ) + { + printf( "Node %d has prev %d without common fanin.\n", pObj->Id, pObj->pPrevFan0->Id ); + RetValue = 0; + } + } + // check that the previous fanout has the same fanin + if ( pObj->pPrevFan1 ) + { + if ( Ivy_ObjFanin0(pObj->pPrevFan1) != Ivy_ObjFanin0(pObj) && + Ivy_ObjFanin0(pObj->pPrevFan1) != Ivy_ObjFanin1(pObj) && + Ivy_ObjFanin1(pObj->pPrevFan1) != Ivy_ObjFanin0(pObj) && + Ivy_ObjFanin1(pObj->pPrevFan1) != Ivy_ObjFanin1(pObj) ) + { + printf( "Node %d has prev %d without common fanin.\n", pObj->Id, pObj->pPrevFan1->Id ); + RetValue = 0; + } + } + } + // make sure every fanout is a fanin + Ivy_ManForEachObj( p, pObj, i ) + { + Ivy_ObjForEachFanout( p, pObj, vFanouts, pFanout, k ) + if ( Ivy_ObjFanin0(pFanout) != pObj && Ivy_ObjFanin1(pFanout) != pObj ) + { + printf( "Node %d is a fanout of node %d but the fanin is not there.\n", pFanout->Id, pObj->Id ); + RetValue = 0; + } + } + Vec_PtrFree( vFanouts ); + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Checks that each choice node has exactly one node with fanouts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManCheckChoices( Ivy_Man_t * p ) +{ + Ivy_Obj_t * pObj, * pTemp; + int i; + Ivy_ManForEachObj( p->pHaig, pObj, i ) + { + if ( Ivy_ObjRefs(pObj) == 0 ) + continue; + // count the number of nodes in the loop + assert( !Ivy_IsComplement(pObj->pEquiv) ); + for ( pTemp = pObj->pEquiv; pTemp && pTemp != pObj; pTemp = Ivy_Regular(pTemp->pEquiv) ) + if ( Ivy_ObjRefs(pTemp) > 1 ) + printf( "Node %d has member %d in its equiv class with %d fanouts.\n", pObj->Id, pTemp->Id, Ivy_ObjRefs(pTemp) ); + } + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyCut.c b/abc_with_bb_support/src/aig/ivy/ivyCut.c new file mode 100644 index 000000000..597eab324 --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyCut.c @@ -0,0 +1,989 @@ +/**CFile**************************************************************** + + FileName [ivyCut.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [Computes reconvergence driven sequential cut.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyCut.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static inline int Ivy_NodeCutHashValue( int NodeId ) { return 1 << (NodeId % 31); } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Evaluate the cost of removing the node from the set of leaves.] + + Description [Returns the number of new leaves that will be brought in. + Returns large number if the node cannot be removed from the set of leaves.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Ivy_NodeGetLeafCostOne( Ivy_Man_t * p, int Leaf, Vec_Int_t * vInside ) +{ + Ivy_Obj_t * pNode; + int nLatches, FaninLeaf, Cost; + // make sure leaf is not a contant node + assert( Leaf > 0 ); + // get the node + pNode = Ivy_ManObj( p, Ivy_LeafId(Leaf) ); + // cannot expand over the PI node + if ( Ivy_ObjIsPi(pNode) || Ivy_ObjIsConst1(pNode) ) + return 999; + // get the number of latches + nLatches = Ivy_LeafLat(Leaf) + Ivy_ObjIsLatch(pNode); + if ( nLatches > 15 ) + return 999; + // get the first fanin + FaninLeaf = Ivy_LeafCreate( Ivy_ObjFaninId0(pNode), nLatches ); + Cost = FaninLeaf && (Vec_IntFind(vInside, FaninLeaf) == -1); + // quit if this is the one fanin node + if ( Ivy_ObjIsLatch(pNode) || Ivy_ObjIsBuf(pNode) ) + return Cost; + assert( Ivy_ObjIsNode(pNode) ); + // get the second fanin + FaninLeaf = Ivy_LeafCreate( Ivy_ObjFaninId1(pNode), nLatches ); + Cost += FaninLeaf && (Vec_IntFind(vInside, FaninLeaf) == -1); + return Cost; +} + +/**Function************************************************************* + + Synopsis [Builds reconvergence-driven cut by changing one leaf at a time.] + + Description [This procedure looks at the current leaves and tries to change + one leaf at a time in such a way that the cut grows as little as possible. + In evaluating the fanins, this procedure looks only at their immediate + predecessors (this is why it is called a one-level construction procedure).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManSeqFindCut_int( Ivy_Man_t * p, Vec_Int_t * vFront, Vec_Int_t * vInside, int nSizeLimit ) +{ + Ivy_Obj_t * pNode; + int CostBest, CostCur, Leaf, LeafBest, Next, nLatches, i; + int LeavesBest[10]; + int Counter; + + // add random selection of the best fanin!!! + + // find the best fanin + CostBest = 99; + LeafBest = -1; + Counter = -1; +//printf( "Evaluating fanins of the cut:\n" ); + Vec_IntForEachEntry( vFront, Leaf, i ) + { + CostCur = Ivy_NodeGetLeafCostOne( p, Leaf, vInside ); +//printf( " Fanin %s has cost %d.\n", Ivy_ObjName(pNode), CostCur ); + if ( CostBest > CostCur ) + { + CostBest = CostCur; + LeafBest = Leaf; + LeavesBest[0] = Leaf; + Counter = 1; + } + else if ( CostBest == CostCur ) + LeavesBest[Counter++] = Leaf; + + if ( CostBest <= 1 ) // can be if ( CostBest <= 1 ) + break; + } + if ( CostBest == 99 ) + return 0; +// return Ivy_NodeBuildCutLevelTwo_int( vInside, vFront, nFaninLimit ); + + assert( CostBest < 3 ); + if ( Vec_IntSize(vFront) - 1 + CostBest > nSizeLimit ) + return 0; +// return Ivy_NodeBuildCutLevelTwo_int( vInside, vFront, nFaninLimit ); + + assert( Counter > 0 ); +printf( "%d", Counter ); + + LeafBest = LeavesBest[rand() % Counter]; + + // remove the node from the array + assert( LeafBest >= 0 ); + Vec_IntRemove( vFront, LeafBest ); +//printf( "Removing fanin %s.\n", Ivy_ObjName(pNode) ); + + // get the node and its latches + pNode = Ivy_ManObj( p, Ivy_LeafId(LeafBest) ); + nLatches = Ivy_LeafLat(LeafBest) + Ivy_ObjIsLatch(pNode); + assert( Ivy_ObjIsNode(pNode) || Ivy_ObjIsLatch(pNode) || Ivy_ObjIsBuf(pNode) ); + + // add the left child to the fanins + Next = Ivy_LeafCreate( Ivy_ObjFaninId0(pNode), nLatches ); + if ( Next && Vec_IntFind(vInside, Next) == -1 ) + { +//printf( "Adding fanin %s.\n", Ivy_ObjName(pNext) ); + Vec_IntPush( vFront, Next ); + Vec_IntPush( vInside, Next ); + } + + // quit if this is the one fanin node + if ( Ivy_ObjIsLatch(pNode) || Ivy_ObjIsBuf(pNode) ) + return 1; + assert( Ivy_ObjIsNode(pNode) ); + + // add the right child to the fanins + Next = Ivy_LeafCreate( Ivy_ObjFaninId1(pNode), nLatches ); + if ( Next && Vec_IntFind(vInside, Next) == -1 ) + { +//printf( "Adding fanin %s.\n", Ivy_ObjName(pNext) ); + Vec_IntPush( vFront, Next ); + Vec_IntPush( vInside, Next ); + } + assert( Vec_IntSize(vFront) <= nSizeLimit ); + // keep doing this + return 1; +} + +/**Function************************************************************* + + Synopsis [Computes one sequential cut of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManSeqFindCut( Ivy_Man_t * p, Ivy_Obj_t * pRoot, Vec_Int_t * vFront, Vec_Int_t * vInside, int nSize ) +{ + assert( !Ivy_IsComplement(pRoot) ); + assert( Ivy_ObjIsNode(pRoot) ); + assert( Ivy_ObjFaninId0(pRoot) ); + assert( Ivy_ObjFaninId1(pRoot) ); + + // start the cut + Vec_IntClear( vFront ); + Vec_IntPush( vFront, Ivy_LeafCreate(Ivy_ObjFaninId0(pRoot), 0) ); + Vec_IntPush( vFront, Ivy_LeafCreate(Ivy_ObjFaninId1(pRoot), 0) ); + + // start the visited nodes + Vec_IntClear( vInside ); + Vec_IntPush( vInside, Ivy_LeafCreate(pRoot->Id, 0) ); + Vec_IntPush( vInside, Ivy_LeafCreate(Ivy_ObjFaninId0(pRoot), 0) ); + Vec_IntPush( vInside, Ivy_LeafCreate(Ivy_ObjFaninId1(pRoot), 0) ); + + // compute the cut + while ( Ivy_ManSeqFindCut_int( p, vFront, vInside, nSize ) ); + assert( Vec_IntSize(vFront) <= nSize ); +} + + + + + +/**Function************************************************************* + + Synopsis [Computing Boolean cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManFindBoolCut_rec( Ivy_Man_t * p, Ivy_Obj_t * pObj, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vVolume, Ivy_Obj_t * pPivot ) +{ + int RetValue0, RetValue1; + if ( pObj == pPivot ) + { + Vec_PtrPushUnique( vLeaves, pObj ); + Vec_PtrPushUnique( vVolume, pObj ); + return 1; + } + if ( pObj->fMarkA ) + return 0; + +// assert( !Ivy_ObjIsCi(pObj) ); + if ( Ivy_ObjIsCi(pObj) ) + return 0; + + if ( Ivy_ObjIsBuf(pObj) ) + { + RetValue0 = Ivy_ManFindBoolCut_rec( p, Ivy_ObjFanin0(pObj), vLeaves, vVolume, pPivot ); + if ( !RetValue0 ) + return 0; + Vec_PtrPushUnique( vVolume, pObj ); + return 1; + } + assert( Ivy_ObjIsNode(pObj) ); + RetValue0 = Ivy_ManFindBoolCut_rec( p, Ivy_ObjFanin0(pObj), vLeaves, vVolume, pPivot ); + RetValue1 = Ivy_ManFindBoolCut_rec( p, Ivy_ObjFanin1(pObj), vLeaves, vVolume, pPivot ); + if ( !RetValue0 && !RetValue1 ) + return 0; + // add new leaves + if ( !RetValue0 ) + { + Vec_PtrPushUnique( vLeaves, Ivy_ObjFanin0(pObj) ); + Vec_PtrPushUnique( vVolume, Ivy_ObjFanin0(pObj) ); + } + if ( !RetValue1 ) + { + Vec_PtrPushUnique( vLeaves, Ivy_ObjFanin1(pObj) ); + Vec_PtrPushUnique( vVolume, Ivy_ObjFanin1(pObj) ); + } + Vec_PtrPushUnique( vVolume, pObj ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Returns the cost of one node (how many new nodes are added.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManFindBoolCutCost( Ivy_Obj_t * pObj ) +{ + int Cost; + // make sure the node is in the construction zone + assert( pObj->fMarkA == 1 ); + // cannot expand over the PI node + if ( Ivy_ObjIsCi(pObj) ) + return 999; + // always expand over the buffer + if ( Ivy_ObjIsBuf(pObj) ) + return !Ivy_ObjFanin0(pObj)->fMarkA; + // get the cost of the cone + Cost = (!Ivy_ObjFanin0(pObj)->fMarkA) + (!Ivy_ObjFanin1(pObj)->fMarkA); + // return the number of nodes to be added to the leaves if this node is removed + return Cost; +} + +/**Function************************************************************* + + Synopsis [Computing Boolean cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManFindBoolCut( Ivy_Man_t * p, Ivy_Obj_t * pRoot, Vec_Ptr_t * vFront, Vec_Ptr_t * vVolume, Vec_Ptr_t * vLeaves ) +{ + Ivy_Obj_t * pObj, * pFaninC, * pFanin0, * pFanin1, * pPivot; + int RetValue, LevelLimit, Lev, k; + assert( !Ivy_IsComplement(pRoot) ); + // clear the frontier and collect the nodes + Vec_PtrClear( vFront ); + Vec_PtrClear( vVolume ); + if ( Ivy_ObjIsMuxType(pRoot) ) + pFaninC = Ivy_ObjRecognizeMux( pRoot, &pFanin0, &pFanin1 ); + else + { + pFaninC = NULL; + pFanin0 = Ivy_ObjFanin0(pRoot); + pFanin1 = Ivy_ObjFanin1(pRoot); + } + // start cone A + pFanin0->fMarkA = 1; + Vec_PtrPush( vFront, pFanin0 ); + Vec_PtrPush( vVolume, pFanin0 ); + // start cone B + pFanin1->fMarkB = 1; + Vec_PtrPush( vFront, pFanin1 ); + Vec_PtrPush( vVolume, pFanin1 ); + // iteratively expand until the common node (pPivot) is found or limit is reached + assert( Ivy_ObjLevel(pRoot) == Ivy_ObjLevelNew(pRoot) ); + pPivot = NULL; + LevelLimit = IVY_MAX( Ivy_ObjLevel(pRoot) - 10, 1 ); + for ( Lev = Ivy_ObjLevel(pRoot) - 1; Lev >= LevelLimit; Lev-- ) + { + while ( 1 ) + { + // find the next node to expand on this level + Vec_PtrForEachEntry( vFront, pObj, k ) + if ( (int)pObj->Level == Lev ) + break; + if ( k == Vec_PtrSize(vFront) ) + break; + assert( (int)pObj->Level <= Lev ); + assert( pObj->fMarkA ^ pObj->fMarkB ); + // remove the old node + Vec_PtrRemove( vFront, pObj ); + + // expand this node + pFanin0 = Ivy_ObjFanin0(pObj); + if ( !pFanin0->fMarkA && !pFanin0->fMarkB ) + { + Vec_PtrPush( vFront, pFanin0 ); + Vec_PtrPush( vVolume, pFanin0 ); + } + // mark the new nodes + if ( pObj->fMarkA ) + pFanin0->fMarkA = 1; + if ( pObj->fMarkB ) + pFanin0->fMarkB = 1; + + if ( Ivy_ObjIsBuf(pObj) ) + { + if ( pFanin0->fMarkA && pFanin0->fMarkB ) + { + pPivot = pFanin0; + break; + } + continue; + } + + // expand this node + pFanin1 = Ivy_ObjFanin1(pObj); + if ( !pFanin1->fMarkA && !pFanin1->fMarkB ) + { + Vec_PtrPush( vFront, pFanin1 ); + Vec_PtrPush( vVolume, pFanin1 ); + } + // mark the new nodes + if ( pObj->fMarkA ) + pFanin1->fMarkA = 1; + if ( pObj->fMarkB ) + pFanin1->fMarkB = 1; + + // consider if it is time to quit + if ( pFanin0->fMarkA && pFanin0->fMarkB ) + { + pPivot = pFanin0; + break; + } + if ( pFanin1->fMarkA && pFanin1->fMarkB ) + { + pPivot = pFanin1; + break; + } + } + if ( pPivot != NULL ) + break; + } + if ( pPivot == NULL ) + return 0; + // if the MUX control is defined, it should not be + if ( pFaninC && !pFaninC->fMarkA && !pFaninC->fMarkB ) + Vec_PtrPush( vFront, pFaninC ); + // clean the markings + Vec_PtrForEachEntry( vVolume, pObj, k ) + pObj->fMarkA = pObj->fMarkB = 0; + + // mark the nodes on the frontier (including the pivot) + Vec_PtrForEachEntry( vFront, pObj, k ) + pObj->fMarkA = 1; + // cut exists, collect all the nodes on the shortest path to the pivot + Vec_PtrClear( vLeaves ); + Vec_PtrClear( vVolume ); + RetValue = Ivy_ManFindBoolCut_rec( p, pRoot, vLeaves, vVolume, pPivot ); + assert( RetValue == 1 ); + // unmark the nodes on the frontier (including the pivot) + Vec_PtrForEachEntry( vFront, pObj, k ) + pObj->fMarkA = 0; + + // mark the nodes in the volume + Vec_PtrForEachEntry( vVolume, pObj, k ) + pObj->fMarkA = 1; + // expand the cut without increasing its size + while ( 1 ) + { + Vec_PtrForEachEntry( vLeaves, pObj, k ) + if ( Ivy_ManFindBoolCutCost(pObj) < 2 ) + break; + if ( k == Vec_PtrSize(vLeaves) ) + break; + // the node can be expanded + // remove the old node + Vec_PtrRemove( vLeaves, pObj ); + // expand this node + pFanin0 = Ivy_ObjFanin0(pObj); + if ( !pFanin0->fMarkA ) + { + pFanin0->fMarkA = 1; + Vec_PtrPush( vVolume, pFanin0 ); + Vec_PtrPush( vLeaves, pFanin0 ); + } + if ( Ivy_ObjIsBuf(pObj) ) + continue; + // expand this node + pFanin1 = Ivy_ObjFanin1(pObj); + if ( !pFanin1->fMarkA ) + { + pFanin1->fMarkA = 1; + Vec_PtrPush( vVolume, pFanin1 ); + Vec_PtrPush( vLeaves, pFanin1 ); + } + } + // unmark the nodes in the volume + Vec_PtrForEachEntry( vVolume, pObj, k ) + pObj->fMarkA = 0; + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManTestCutsBool( Ivy_Man_t * p ) +{ + Vec_Ptr_t * vFront, * vVolume, * vLeaves; + Ivy_Obj_t * pObj;//, * pTemp; + int i, RetValue;//, k; + vFront = Vec_PtrAlloc( 100 ); + vVolume = Vec_PtrAlloc( 100 ); + vLeaves = Vec_PtrAlloc( 100 ); + Ivy_ManForEachObj( p, pObj, i ) + { + if ( !Ivy_ObjIsNode(pObj) ) + continue; + if ( Ivy_ObjIsMuxType(pObj) ) + { + printf( "m" ); + continue; + } + if ( Ivy_ObjIsExor(pObj) ) + printf( "x" ); + RetValue = Ivy_ManFindBoolCut( p, pObj, vFront, vVolume, vLeaves ); + if ( RetValue == 0 ) + printf( "- " ); + else + printf( "%d ", Vec_PtrSize(vLeaves) ); +/* + printf( "( " ); + Vec_PtrForEachEntry( vFront, pTemp, k ) + printf( "%d ", Ivy_ObjRefs(Ivy_Regular(pTemp)) ); + printf( ")\n" ); +*/ + } + printf( "\n" ); + Vec_PtrFree( vFront ); + Vec_PtrFree( vVolume ); + Vec_PtrFree( vLeaves ); +} + + + +/**Function************************************************************* + + Synopsis [Find the hash value of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Ivy_NodeCutHash( Ivy_Cut_t * pCut ) +{ + int i; +// for ( i = 1; i < pCut->nSize; i++ ) +// assert( pCut->pArray[i-1] < pCut->pArray[i] ); + pCut->uHash = 0; + for ( i = 0; i < pCut->nSize; i++ ) + pCut->uHash |= (1 << (pCut->pArray[i] % 31)); + return pCut->uHash; +} + +/**Function************************************************************* + + Synopsis [Removes one node to the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Ivy_NodeCutShrink( Ivy_Cut_t * pCut, int iOld ) +{ + int i, k; + for ( i = k = 0; i < pCut->nSize; i++ ) + if ( pCut->pArray[i] != iOld ) + pCut->pArray[k++] = pCut->pArray[i]; + assert( k == pCut->nSize - 1 ); + pCut->nSize--; +} + +/**Function************************************************************* + + Synopsis [Adds one node to the cut.] + + Description [Returns 1 if the cuts is still okay.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Ivy_NodeCutExtend( Ivy_Cut_t * pCut, int iNew ) +{ + int i; + for ( i = 0; i < pCut->nSize; i++ ) + if ( pCut->pArray[i] == iNew ) + return 1; + // check if there is room + if ( pCut->nSize == pCut->nSizeMax ) + return 0; + // add the new one + for ( i = pCut->nSize - 1; i >= 0; i-- ) + if ( pCut->pArray[i] > iNew ) + pCut->pArray[i+1] = pCut->pArray[i]; + else + { + assert( pCut->pArray[i] < iNew ); + break; + } + pCut->pArray[i+1] = iNew; + pCut->nSize++; + return 1; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the cut can be constructed; 0 otherwise.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Ivy_NodeCutPrescreen( Ivy_Cut_t * pCut, int Id0, int Id1 ) +{ + int i; + if ( pCut->nSize < pCut->nSizeMax ) + return 1; + for ( i = 0; i < pCut->nSize; i++ ) + if ( pCut->pArray[i] == Id0 || pCut->pArray[i] == Id1 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Derives new cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Ivy_NodeCutDeriveNew( Ivy_Cut_t * pCut, Ivy_Cut_t * pCutNew, int IdOld, int IdNew0, int IdNew1 ) +{ + unsigned uHash = 0; + int i, k; + assert( pCut->nSize > 0 ); + assert( IdNew0 < IdNew1 ); + for ( i = k = 0; i < pCut->nSize; i++ ) + { + if ( pCut->pArray[i] == IdOld ) + continue; + if ( IdNew0 <= pCut->pArray[i] ) + { + if ( IdNew0 < pCut->pArray[i] ) + { + pCutNew->pArray[ k++ ] = IdNew0; + uHash |= Ivy_NodeCutHashValue( IdNew0 ); + } + IdNew0 = 0x7FFFFFFF; + } + if ( IdNew1 <= pCut->pArray[i] ) + { + if ( IdNew1 < pCut->pArray[i] ) + { + pCutNew->pArray[ k++ ] = IdNew1; + uHash |= Ivy_NodeCutHashValue( IdNew1 ); + } + IdNew1 = 0x7FFFFFFF; + } + pCutNew->pArray[ k++ ] = pCut->pArray[i]; + uHash |= Ivy_NodeCutHashValue( pCut->pArray[i] ); + } + if ( IdNew0 < 0x7FFFFFFF ) + { + pCutNew->pArray[ k++ ] = IdNew0; + uHash |= Ivy_NodeCutHashValue( IdNew0 ); + } + if ( IdNew1 < 0x7FFFFFFF ) + { + pCutNew->pArray[ k++ ] = IdNew1; + uHash |= Ivy_NodeCutHashValue( IdNew1 ); + } + pCutNew->nSize = k; + pCutNew->uHash = uHash; + assert( pCutNew->nSize <= pCut->nSizeMax ); +// for ( i = 1; i < pCutNew->nSize; i++ ) +// assert( pCutNew->pArray[i-1] < pCutNew->pArray[i] ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Check if the cut exists.] + + Description [Returns 1 if the cut exists.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_NodeCutFindOrAdd( Ivy_Store_t * pCutStore, Ivy_Cut_t * pCutNew ) +{ + Ivy_Cut_t * pCut; + int i, k; + assert( pCutNew->uHash ); + // try to find the cut + for ( i = 0; i < pCutStore->nCuts; i++ ) + { + pCut = pCutStore->pCuts + i; + if ( pCut->uHash == pCutNew->uHash && pCut->nSize == pCutNew->nSize ) + { + for ( k = 0; k < pCutNew->nSize; k++ ) + if ( pCut->pArray[k] != pCutNew->pArray[k] ) + break; + if ( k == pCutNew->nSize ) + return 1; + } + } + assert( pCutStore->nCuts < pCutStore->nCutsMax ); + // add the cut + pCut = pCutStore->pCuts + pCutStore->nCuts++; + *pCut = *pCutNew; + return 0; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if pDom is contained in pCut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Ivy_CutCheckDominance( Ivy_Cut_t * pDom, Ivy_Cut_t * pCut ) +{ + int i, k; + for ( i = 0; i < pDom->nSize; i++ ) + { + for ( k = 0; k < pCut->nSize; k++ ) + if ( pDom->pArray[i] == pCut->pArray[k] ) + break; + if ( k == pCut->nSize ) // node i in pDom is not contained in pCut + return 0; + } + // every node in pDom is contained in pCut + return 1; +} + +/**Function************************************************************* + + Synopsis [Check if the cut exists.] + + Description [Returns 1 if the cut exists.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_NodeCutFindOrAddFilter( Ivy_Store_t * pCutStore, Ivy_Cut_t * pCutNew ) +{ + Ivy_Cut_t * pCut; + int i, k; + assert( pCutNew->uHash ); + // try to find the cut + for ( i = 0; i < pCutStore->nCuts; i++ ) + { + pCut = pCutStore->pCuts + i; + if ( pCut->nSize == 0 ) + continue; + if ( pCut->nSize == pCutNew->nSize ) + { + if ( pCut->uHash == pCutNew->uHash ) + { + for ( k = 0; k < pCutNew->nSize; k++ ) + if ( pCut->pArray[k] != pCutNew->pArray[k] ) + break; + if ( k == pCutNew->nSize ) + return 1; + } + continue; + } + if ( pCut->nSize < pCutNew->nSize ) + { + // skip the non-contained cuts + if ( (pCut->uHash & pCutNew->uHash) != pCut->uHash ) + continue; + // check containment seriously + if ( Ivy_CutCheckDominance( pCut, pCutNew ) ) + return 1; + continue; + } + // check potential containment of other cut + + // skip the non-contained cuts + if ( (pCut->uHash & pCutNew->uHash) != pCutNew->uHash ) + continue; + // check containment seriously + if ( Ivy_CutCheckDominance( pCutNew, pCut ) ) + { + // remove the current cut +// --pCutStore->nCuts; +// for ( k = i; k < pCutStore->nCuts; k++ ) +// pCutStore->pCuts[k] = pCutStore->pCuts[k+1]; +// i--; + pCut->nSize = 0; + } + } + assert( pCutStore->nCuts < pCutStore->nCutsMax ); + // add the cut + pCut = pCutStore->pCuts + pCutStore->nCuts++; + *pCut = *pCutNew; + return 0; +} + +/**Function************************************************************* + + Synopsis [Print the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_NodeCompactCuts( Ivy_Store_t * pCutStore ) +{ + Ivy_Cut_t * pCut; + int i, k; + for ( i = k = 0; i < pCutStore->nCuts; i++ ) + { + pCut = pCutStore->pCuts + i; + if ( pCut->nSize == 0 ) + continue; + pCutStore->pCuts[k++] = *pCut; + } + pCutStore->nCuts = k; +} + +/**Function************************************************************* + + Synopsis [Print the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_NodePrintCut( Ivy_Cut_t * pCut ) +{ + int i; + assert( pCut->nSize > 0 ); + printf( "%d : {", pCut->nSize ); + for ( i = 0; i < pCut->nSize; i++ ) + printf( " %d", pCut->pArray[i] ); + printf( " }\n" ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_NodePrintCuts( Ivy_Store_t * pCutStore ) +{ + int i; + printf( "Node %d\n", pCutStore->pCuts[0].pArray[0] ); + for ( i = 0; i < pCutStore->nCuts; i++ ) + Ivy_NodePrintCut( pCutStore->pCuts + i ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Ivy_Obj_t * Ivy_ObjRealFanin( Ivy_Obj_t * pObj ) +{ + if ( !Ivy_ObjIsBuf(pObj) ) + return pObj; + return Ivy_ObjRealFanin( Ivy_ObjFanin0(pObj) ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Store_t * Ivy_NodeFindCutsAll( Ivy_Man_t * p, Ivy_Obj_t * pObj, int nLeaves ) +{ + static Ivy_Store_t CutStore, * pCutStore = &CutStore; + Ivy_Cut_t CutNew, * pCutNew = &CutNew, * pCut; + Ivy_Obj_t * pLeaf; + int i, k, iLeaf0, iLeaf1; + + assert( nLeaves <= IVY_CUT_INPUT ); + + // start the structure + pCutStore->nCuts = 0; + pCutStore->nCutsMax = IVY_CUT_LIMIT; + // start the trivial cut + pCutNew->uHash = 0; + pCutNew->nSize = 1; + pCutNew->nSizeMax = nLeaves; + pCutNew->pArray[0] = pObj->Id; + Ivy_NodeCutHash( pCutNew ); + // add the trivial cut + Ivy_NodeCutFindOrAdd( pCutStore, pCutNew ); + assert( pCutStore->nCuts == 1 ); + + // explore the cuts + for ( i = 0; i < pCutStore->nCuts; i++ ) + { + // expand this cut + pCut = pCutStore->pCuts + i; + if ( pCut->nSize == 0 ) + continue; + for ( k = 0; k < pCut->nSize; k++ ) + { + pLeaf = Ivy_ManObj( p, pCut->pArray[k] ); + if ( Ivy_ObjIsCi(pLeaf) ) + continue; +/* + *pCutNew = *pCut; + Ivy_NodeCutShrink( pCutNew, pLeaf->Id ); + if ( !Ivy_NodeCutExtend( pCutNew, Ivy_ObjFaninId0(pLeaf) ) ) + continue; + if ( Ivy_ObjIsNode(pLeaf) && !Ivy_NodeCutExtend( pCutNew, Ivy_ObjFaninId1(pLeaf) ) ) + continue; + Ivy_NodeCutHash( pCutNew ); +*/ + iLeaf0 = Ivy_ObjId( Ivy_ObjRealFanin(Ivy_ObjFanin0(pLeaf)) ); + iLeaf1 = Ivy_ObjId( Ivy_ObjRealFanin(Ivy_ObjFanin1(pLeaf)) ); +// if ( iLeaf0 == iLeaf1 ) // strange situation observed on Jan 18, 2007 +// continue; + if ( !Ivy_NodeCutPrescreen( pCut, iLeaf0, iLeaf1 ) ) + continue; + if ( iLeaf0 > iLeaf1 ) + Ivy_NodeCutDeriveNew( pCut, pCutNew, pCut->pArray[k], iLeaf1, iLeaf0 ); + else + Ivy_NodeCutDeriveNew( pCut, pCutNew, pCut->pArray[k], iLeaf0, iLeaf1 ); + Ivy_NodeCutFindOrAddFilter( pCutStore, pCutNew ); + if ( pCutStore->nCuts == IVY_CUT_LIMIT ) + break; + } + if ( pCutStore->nCuts == IVY_CUT_LIMIT ) + break; + } + Ivy_NodeCompactCuts( pCutStore ); +// Ivy_NodePrintCuts( pCutStore ); + return pCutStore; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManTestCutsAll( Ivy_Man_t * p ) +{ + Ivy_Obj_t * pObj; + int i, nCutsCut, nCutsTotal, nNodeTotal, nNodeOver; + int clk = clock(); + nNodeTotal = nNodeOver = 0; + nCutsTotal = -Ivy_ManNodeNum(p); + Ivy_ManForEachObj( p, pObj, i ) + { + if ( !Ivy_ObjIsNode(pObj) ) + continue; + nCutsCut = Ivy_NodeFindCutsAll( p, pObj, 5 )->nCuts; + nCutsTotal += nCutsCut; + nNodeOver += (nCutsCut == IVY_CUT_LIMIT); + nNodeTotal++; + } + printf( "Total cuts = %6d. Trivial = %6d. Nodes = %6d. Satur = %6d. ", + nCutsTotal, Ivy_ManPiNum(p) + Ivy_ManNodeNum(p), nNodeTotal, nNodeOver ); + PRT( "Time", clock() - clk ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyCutTrav.c b/abc_with_bb_support/src/aig/ivy/ivyCutTrav.c new file mode 100644 index 000000000..1607e6752 --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyCutTrav.c @@ -0,0 +1,473 @@ +/**CFile**************************************************************** + + FileName [ivyCutTrav.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyCutTrav.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static unsigned * Ivy_NodeCutElementary( Vec_Int_t * vStore, int nWords, int NodeId ); +static void Ivy_NodeComputeVolume( Ivy_Obj_t * pObj, int nNodeLimit, Vec_Ptr_t * vNodes, Vec_Ptr_t * vFront ); +static void Ivy_NodeFindCutsMerge( Vec_Ptr_t * vCuts0, Vec_Ptr_t * vCuts1, Vec_Ptr_t * vCuts, int nLeaves, int nWords, Vec_Int_t * vStore ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes cuts for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Store_t * Ivy_NodeFindCutsTravAll( Ivy_Man_t * p, Ivy_Obj_t * pObj, int nLeaves, int nNodeLimit, + Vec_Ptr_t * vNodes, Vec_Ptr_t * vFront, Vec_Int_t * vStore, Vec_Vec_t * vBitCuts ) +{ + static Ivy_Store_t CutStore, * pCutStore = &CutStore; + Vec_Ptr_t * vCuts, * vCuts0, * vCuts1; + unsigned * pBitCut; + Ivy_Obj_t * pLeaf; + Ivy_Cut_t * pCut; + int i, k, nWords, nNodes; + + assert( nLeaves <= IVY_CUT_INPUT ); + + // find the given number of nodes in the TFI + Ivy_NodeComputeVolume( pObj, nNodeLimit - 1, vNodes, vFront ); + nNodes = Vec_PtrSize(vNodes); +// assert( nNodes <= nNodeLimit ); + + // make sure vBitCuts has enough room + Vec_VecExpand( vBitCuts, nNodes-1 ); + Vec_VecClear( vBitCuts ); + + // prepare the memory manager + Vec_IntClear( vStore ); + Vec_IntGrow( vStore, 64000 ); + + // set elementary cuts for the leaves + nWords = Extra_BitWordNum( nNodes ); + Vec_PtrForEachEntry( vFront, pLeaf, i ) + { + assert( Ivy_ObjTravId(pLeaf) < nNodes ); + // get the new bitcut + pBitCut = Ivy_NodeCutElementary( vStore, nWords, Ivy_ObjTravId(pLeaf) ); + // set it as the cut of this leaf + Vec_VecPush( vBitCuts, Ivy_ObjTravId(pLeaf), pBitCut ); + } + + // compute the cuts for each node + Vec_PtrForEachEntry( vNodes, pLeaf, i ) + { + // skip the leaves + vCuts = Vec_VecEntry( vBitCuts, Ivy_ObjTravId(pLeaf) ); + if ( Vec_PtrSize(vCuts) > 0 ) + continue; + // add elementary cut + pBitCut = Ivy_NodeCutElementary( vStore, nWords, Ivy_ObjTravId(pLeaf) ); + // set it as the cut of this leaf + Vec_VecPush( vBitCuts, Ivy_ObjTravId(pLeaf), pBitCut ); + // get the fanin cuts + vCuts0 = Vec_VecEntry( vBitCuts, Ivy_ObjTravId( Ivy_ObjFanin0(pLeaf) ) ); + vCuts1 = Vec_VecEntry( vBitCuts, Ivy_ObjTravId( Ivy_ObjFanin1(pLeaf) ) ); + assert( Vec_PtrSize(vCuts0) > 0 ); + assert( Vec_PtrSize(vCuts1) > 0 ); + // merge the cuts + Ivy_NodeFindCutsMerge( vCuts0, vCuts1, vCuts, nLeaves, nWords, vStore ); + } + + // start the structure + pCutStore->nCuts = 0; + pCutStore->nCutsMax = IVY_CUT_LIMIT; + // collect the cuts of the root node + vCuts = Vec_VecEntry( vBitCuts, Ivy_ObjTravId(pObj) ); + Vec_PtrForEachEntry( vCuts, pBitCut, i ) + { + pCut = pCutStore->pCuts + pCutStore->nCuts++; + pCut->nSize = 0; + pCut->nSizeMax = nLeaves; + pCut->uHash = 0; + for ( k = 0; k < nNodes; k++ ) + if ( Extra_TruthHasBit(pBitCut, k) ) + pCut->pArray[ pCut->nSize++ ] = Ivy_ObjId( Vec_PtrEntry(vNodes, k) ); + assert( pCut->nSize <= nLeaves ); + if ( pCutStore->nCuts == pCutStore->nCutsMax ) + break; + } + + // clean the travIds + Vec_PtrForEachEntry( vNodes, pLeaf, i ) + pLeaf->TravId = 0; + return pCutStore; +} + +/**Function************************************************************* + + Synopsis [Creates elementary bit-cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned * Ivy_NodeCutElementary( Vec_Int_t * vStore, int nWords, int NodeId ) +{ + unsigned * pBitCut; + pBitCut = Vec_IntFetch( vStore, nWords ); + memset( pBitCut, 0, 4 * nWords ); + Extra_TruthSetBit( pBitCut, NodeId ); + return pBitCut; +} + +/**Function************************************************************* + + Synopsis [Compares the node by level.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_CompareNodesByLevel( Ivy_Obj_t ** ppObj1, Ivy_Obj_t ** ppObj2 ) +{ + Ivy_Obj_t * pObj1 = *ppObj1; + Ivy_Obj_t * pObj2 = *ppObj2; + if ( pObj1->Level < pObj2->Level ) + return -1; + if ( pObj1->Level > pObj2->Level ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Mark all nodes up to the given depth.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_NodeComputeVolumeTrav1_rec( Ivy_Obj_t * pObj, int Depth ) +{ + if ( Ivy_ObjIsCi(pObj) || Depth == 0 ) + return; + Ivy_NodeComputeVolumeTrav1_rec( Ivy_ObjFanin0(pObj), Depth - 1 ); + Ivy_NodeComputeVolumeTrav1_rec( Ivy_ObjFanin1(pObj), Depth - 1 ); + pObj->fMarkA = 1; +} + +/**Function************************************************************* + + Synopsis [Collect the marked nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_NodeComputeVolumeTrav2_rec( Ivy_Obj_t * pObj, Vec_Ptr_t * vNodes ) +{ + if ( !pObj->fMarkA ) + return; + Ivy_NodeComputeVolumeTrav2_rec( Ivy_ObjFanin0(pObj), vNodes ); + Ivy_NodeComputeVolumeTrav2_rec( Ivy_ObjFanin1(pObj), vNodes ); + Vec_PtrPush( vNodes, pObj ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_NodeComputeVolume( Ivy_Obj_t * pObj, int nNodeLimit, Vec_Ptr_t * vNodes, Vec_Ptr_t * vFront ) +{ + Ivy_Obj_t * pTemp, * pFanin; + int i, nNodes; + // mark nodes up to the given depth + Ivy_NodeComputeVolumeTrav1_rec( pObj, 6 ); + // collect the marked nodes + Vec_PtrClear( vFront ); + Ivy_NodeComputeVolumeTrav2_rec( pObj, vFront ); + // find the fanins that are not marked + Vec_PtrClear( vNodes ); + Vec_PtrForEachEntry( vFront, pTemp, i ) + { + pFanin = Ivy_ObjFanin0(pTemp); + if ( !pFanin->fMarkA ) + { + pFanin->fMarkA = 1; + Vec_PtrPush( vNodes, pFanin ); + } + pFanin = Ivy_ObjFanin1(pTemp); + if ( !pFanin->fMarkA ) + { + pFanin->fMarkA = 1; + Vec_PtrPush( vNodes, pFanin ); + } + } + // remember the number of nodes in the frontier + nNodes = Vec_PtrSize( vNodes ); + // add the remaining nodes + Vec_PtrForEachEntry( vFront, pTemp, i ) + Vec_PtrPush( vNodes, pTemp ); + // unmark the nodes + Vec_PtrForEachEntry( vNodes, pTemp, i ) + { + pTemp->fMarkA = 0; + pTemp->TravId = i; + } + // collect the frontier nodes + Vec_PtrClear( vFront ); + Vec_PtrForEachEntryStop( vNodes, pTemp, i, nNodes ) + Vec_PtrPush( vFront, pTemp ); +// printf( "%d ", Vec_PtrSize(vNodes) ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_NodeComputeVolume2( Ivy_Obj_t * pObj, int nNodeLimit, Vec_Ptr_t * vNodes, Vec_Ptr_t * vFront ) +{ + Ivy_Obj_t * pLeaf, * pPivot, * pFanin; + int LevelMax, i; + assert( Ivy_ObjIsNode(pObj) ); + // clear arrays + Vec_PtrClear( vNodes ); + Vec_PtrClear( vFront ); + // add the root + pObj->fMarkA = 1; + Vec_PtrPush( vNodes, pObj ); + Vec_PtrPush( vFront, pObj ); + // expand node with maximum level + LevelMax = pObj->Level; + do { + // get the node to expand + pPivot = NULL; + Vec_PtrForEachEntryReverse( vFront, pLeaf, i ) + { + if ( (int)pLeaf->Level == LevelMax ) + { + pPivot = pLeaf; + break; + } + } + // decrease level if we did not find the node + if ( pPivot == NULL ) + { + if ( --LevelMax == 0 ) + break; + continue; + } + // the node to expand is found + // remove it from frontier + Vec_PtrRemove( vFront, pPivot ); + // add fanins + pFanin = Ivy_ObjFanin0(pPivot); + if ( !pFanin->fMarkA ) + { + pFanin->fMarkA = 1; + Vec_PtrPush( vNodes, pFanin ); + Vec_PtrPush( vFront, pFanin ); + } + pFanin = Ivy_ObjFanin1(pPivot); + if ( pFanin && !pFanin->fMarkA ) + { + pFanin->fMarkA = 1; + Vec_PtrPush( vNodes, pFanin ); + Vec_PtrPush( vFront, pFanin ); + } + // quit if we collected enough nodes + } while ( Vec_PtrSize(vNodes) < nNodeLimit ); + + // sort nodes by level + Vec_PtrSort( vNodes, Ivy_CompareNodesByLevel ); + // make sure the nodes are ordered in the increasing number of levels + pFanin = Vec_PtrEntry( vNodes, 0 ); + pPivot = Vec_PtrEntryLast( vNodes ); + assert( pFanin->Level <= pPivot->Level ); + + // clean the marks and remember node numbers in the TravId + Vec_PtrForEachEntry( vNodes, pFanin, i ) + { + pFanin->fMarkA = 0; + pFanin->TravId = i; + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Extra_TruthOrWords( unsigned * pOut, unsigned * pIn0, unsigned * pIn1, int nWords ) +{ + int w; + for ( w = nWords-1; w >= 0; w-- ) + pOut[w] = pIn0[w] | pIn1[w]; +} +static inline int Extra_TruthIsImplyWords( unsigned * pIn1, unsigned * pIn2, int nWords ) +{ + int w; + for ( w = nWords-1; w >= 0; w-- ) + if ( pIn1[w] & ~pIn2[w] ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Merges two sets of bit-cuts at a node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_NodeFindCutsMerge( Vec_Ptr_t * vCuts0, Vec_Ptr_t * vCuts1, Vec_Ptr_t * vCuts, + int nLeaves, int nWords, Vec_Int_t * vStore ) +{ + unsigned * pBitCut, * pBitCut0, * pBitCut1, * pBitCutTest; + int i, k, c, w, Counter; + // iterate through the cut pairs + Vec_PtrForEachEntry( vCuts0, pBitCut0, i ) + Vec_PtrForEachEntry( vCuts1, pBitCut1, k ) + { + // skip infeasible cuts + Counter = 0; + for ( w = 0; w < nWords; w++ ) + { + Counter += Extra_WordCountOnes( pBitCut0[w] | pBitCut1[w] ); + if ( Counter > nLeaves ) + break; + } + if ( Counter > nLeaves ) + continue; + // the new cut is feasible - create it + pBitCutTest = Vec_IntFetch( vStore, nWords ); + Extra_TruthOrWords( pBitCutTest, pBitCut0, pBitCut1, nWords ); + // filter contained cuts; try to find containing cut + w = 0; + Vec_PtrForEachEntry( vCuts, pBitCut, c ) + { + if ( Extra_TruthIsImplyWords( pBitCut, pBitCutTest, nWords ) ) + break; + if ( Extra_TruthIsImplyWords( pBitCutTest, pBitCut, nWords ) ) + continue; + Vec_PtrWriteEntry( vCuts, w++, pBitCut ); + } + if ( c != Vec_PtrSize(vCuts) ) + continue; + Vec_PtrShrink( vCuts, w ); + // add the cut + Vec_PtrPush( vCuts, pBitCutTest ); + } +} + +/**Function************************************************************* + + Synopsis [Compute the set of all cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManTestCutsTravAll( Ivy_Man_t * p ) +{ + Ivy_Store_t * pStore; + Ivy_Obj_t * pObj; + Vec_Ptr_t * vNodes, * vFront; + Vec_Int_t * vStore; + Vec_Vec_t * vBitCuts; + int i, nCutsCut, nCutsTotal, nNodeTotal, nNodeOver; + int clk = clock(); + + vNodes = Vec_PtrAlloc( 100 ); + vFront = Vec_PtrAlloc( 100 ); + vStore = Vec_IntAlloc( 100 ); + vBitCuts = Vec_VecAlloc( 100 ); + + nNodeTotal = nNodeOver = 0; + nCutsTotal = -Ivy_ManNodeNum(p); + Ivy_ManForEachObj( p, pObj, i ) + { + if ( !Ivy_ObjIsNode(pObj) ) + continue; + pStore = Ivy_NodeFindCutsTravAll( p, pObj, 4, 60, vNodes, vFront, vStore, vBitCuts ); + nCutsCut = pStore->nCuts; + nCutsTotal += nCutsCut; + nNodeOver += (nCutsCut == IVY_CUT_LIMIT); + nNodeTotal++; + } + printf( "Total cuts = %6d. Trivial = %6d. Nodes = %6d. Satur = %6d. ", + nCutsTotal, Ivy_ManPiNum(p) + Ivy_ManNodeNum(p), nNodeTotal, nNodeOver ); + PRT( "Time", clock() - clk ); + + Vec_PtrFree( vNodes ); + Vec_PtrFree( vFront ); + Vec_IntFree( vStore ); + Vec_VecFree( vBitCuts ); + +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyDfs.c b/abc_with_bb_support/src/aig/ivy/ivyDfs.c new file mode 100644 index 000000000..31e190024 --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyDfs.c @@ -0,0 +1,493 @@ +/**CFile**************************************************************** + + FileName [ivyDfs.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [DFS collection procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyDfs.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Collects nodes in the DFS order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManDfs_rec( Ivy_Man_t * p, Ivy_Obj_t * pObj, Vec_Int_t * vNodes ) +{ + if ( Ivy_ObjIsMarkA(pObj) ) + return; + Ivy_ObjSetMarkA(pObj); + if ( Ivy_ObjIsConst1(pObj) || Ivy_ObjIsCi(pObj) ) + { + if ( p->pHaig == NULL && pObj->pEquiv ) + Ivy_ManDfs_rec( p, Ivy_Regular(pObj->pEquiv), vNodes ); + return; + } +//printf( "visiting node %d\n", pObj->Id ); +/* + if ( pObj->Id == 87 || pObj->Id == 90 ) + { + int y = 0; + } +*/ + assert( Ivy_ObjIsBuf(pObj) || Ivy_ObjIsAnd(pObj) || Ivy_ObjIsExor(pObj) ); + Ivy_ManDfs_rec( p, Ivy_ObjFanin0(pObj), vNodes ); + if ( !Ivy_ObjIsBuf(pObj) ) + Ivy_ManDfs_rec( p, Ivy_ObjFanin1(pObj), vNodes ); + if ( p->pHaig == NULL && pObj->pEquiv ) + Ivy_ManDfs_rec( p, Ivy_Regular(pObj->pEquiv), vNodes ); + Vec_IntPush( vNodes, pObj->Id ); + +//printf( "adding node %d with fanins %d and %d and equiv %d (refs = %d)\n", +// pObj->Id, Ivy_ObjFanin0(pObj)->Id, Ivy_ObjFanin1(pObj)->Id, +// pObj->pEquiv? Ivy_Regular(pObj->pEquiv)->Id: -1, Ivy_ObjRefs(pObj) ); +} + +/**Function************************************************************* + + Synopsis [Collects AND/EXOR nodes in the DFS order from CIs to COs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Ivy_ManDfs( Ivy_Man_t * p ) +{ + Vec_Int_t * vNodes; + Ivy_Obj_t * pObj; + int i; + assert( Ivy_ManLatchNum(p) == 0 ); + // make sure the nodes are not marked + Ivy_ManForEachObj( p, pObj, i ) + assert( !pObj->fMarkA && !pObj->fMarkB ); + // collect the nodes + vNodes = Vec_IntAlloc( Ivy_ManNodeNum(p) ); + Ivy_ManForEachPo( p, pObj, i ) + Ivy_ManDfs_rec( p, Ivy_ObjFanin0(pObj), vNodes ); + // unmark the collected nodes +// Ivy_ManForEachNodeVec( p, vNodes, pObj, i ) +// Ivy_ObjClearMarkA(pObj); + Ivy_ManForEachObj( p, pObj, i ) + Ivy_ObjClearMarkA(pObj); + // make sure network does not have dangling nodes + assert( Vec_IntSize(vNodes) == Ivy_ManNodeNum(p) + Ivy_ManBufNum(p) ); + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Collects AND/EXOR nodes in the DFS order from CIs to COs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Ivy_ManDfsSeq( Ivy_Man_t * p, Vec_Int_t ** pvLatches ) +{ + Vec_Int_t * vNodes, * vLatches; + Ivy_Obj_t * pObj; + int i; +// assert( Ivy_ManLatchNum(p) > 0 ); + // make sure the nodes are not marked + Ivy_ManForEachObj( p, pObj, i ) + assert( !pObj->fMarkA && !pObj->fMarkB ); + // collect the latches + vLatches = Vec_IntAlloc( Ivy_ManLatchNum(p) ); + Ivy_ManForEachLatch( p, pObj, i ) + Vec_IntPush( vLatches, pObj->Id ); + // collect the nodes + vNodes = Vec_IntAlloc( Ivy_ManNodeNum(p) ); + Ivy_ManForEachPo( p, pObj, i ) + Ivy_ManDfs_rec( p, Ivy_ObjFanin0(pObj), vNodes ); + Ivy_ManForEachNodeVec( p, vLatches, pObj, i ) + Ivy_ManDfs_rec( p, Ivy_ObjFanin0(pObj), vNodes ); + // unmark the collected nodes +// Ivy_ManForEachNodeVec( p, vNodes, pObj, i ) +// Ivy_ObjClearMarkA(pObj); + Ivy_ManForEachObj( p, pObj, i ) + Ivy_ObjClearMarkA(pObj); + // make sure network does not have dangling nodes +// assert( Vec_IntSize(vNodes) == Ivy_ManNodeNum(p) + Ivy_ManBufNum(p) ); + +// temporary!!! + + if ( pvLatches == NULL ) + Vec_IntFree( vLatches ); + else + *pvLatches = vLatches; + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Collects nodes in the cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManCollectCone_rec( Ivy_Obj_t * pObj, Vec_Ptr_t * vCone ) +{ + if ( pObj->fMarkA ) + return; + if ( Ivy_ObjIsBuf(pObj) ) + { + Ivy_ManCollectCone_rec( Ivy_ObjFanin0(pObj), vCone ); + Vec_PtrPush( vCone, pObj ); + return; + } + assert( Ivy_ObjIsNode(pObj) ); + Ivy_ManCollectCone_rec( Ivy_ObjFanin0(pObj), vCone ); + Ivy_ManCollectCone_rec( Ivy_ObjFanin1(pObj), vCone ); + Vec_PtrPushUnique( vCone, pObj ); +} + +/**Function************************************************************* + + Synopsis [Collects nodes in the cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManCollectCone( Ivy_Obj_t * pObj, Vec_Ptr_t * vFront, Vec_Ptr_t * vCone ) +{ + Ivy_Obj_t * pTemp; + int i; + assert( !Ivy_IsComplement(pObj) ); + assert( Ivy_ObjIsNode(pObj) ); + // mark the nodes + Vec_PtrForEachEntry( vFront, pTemp, i ) + Ivy_Regular(pTemp)->fMarkA = 1; + assert( pObj->fMarkA == 0 ); + // collect the cone + Vec_PtrClear( vCone ); + Ivy_ManCollectCone_rec( pObj, vCone ); + // unmark the nodes + Vec_PtrForEachEntry( vFront, pTemp, i ) + Ivy_Regular(pTemp)->fMarkA = 0; +} + +/**Function************************************************************* + + Synopsis [Returns the nodes by level.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Vec_t * Ivy_ManLevelize( Ivy_Man_t * p ) +{ + Vec_Vec_t * vNodes; + Ivy_Obj_t * pObj; + int i; + vNodes = Vec_VecAlloc( 100 ); + Ivy_ManForEachObj( p, pObj, i ) + { + assert( !Ivy_ObjIsBuf(pObj) ); + if ( Ivy_ObjIsNode(pObj) ) + Vec_VecPush( vNodes, pObj->Level, pObj ); + } + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Computes required levels for each node.] + + Description [Assumes topological ordering of the nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Ivy_ManRequiredLevels( Ivy_Man_t * p ) +{ + Ivy_Obj_t * pObj; + Vec_Int_t * vLevelsR; + Vec_Vec_t * vNodes; + int i, k, Level, LevelMax; + assert( p->vRequired == NULL ); + // start the required times + vLevelsR = Vec_IntStart( Ivy_ManObjIdMax(p) + 1 ); + // iterate through the nodes in the reverse order + vNodes = Ivy_ManLevelize( p ); + Vec_VecForEachEntryReverseReverse( vNodes, pObj, i, k ) + { + Level = Vec_IntEntry( vLevelsR, pObj->Id ) + 1 + Ivy_ObjIsExor(pObj); + if ( Vec_IntEntry( vLevelsR, Ivy_ObjFaninId0(pObj) ) < Level ) + Vec_IntWriteEntry( vLevelsR, Ivy_ObjFaninId0(pObj), Level ); + if ( Vec_IntEntry( vLevelsR, Ivy_ObjFaninId1(pObj) ) < Level ) + Vec_IntWriteEntry( vLevelsR, Ivy_ObjFaninId1(pObj), Level ); + } + Vec_VecFree( vNodes ); + // convert it into the required times + LevelMax = Ivy_ManLevels( p ); +//printf( "max %5d\n",LevelMax ); + Ivy_ManForEachObj( p, pObj, i ) + { + Level = Vec_IntEntry( vLevelsR, pObj->Id ); + Vec_IntWriteEntry( vLevelsR, pObj->Id, LevelMax - Level ); +//printf( "%5d : %5d %5d\n", pObj->Id, Level, LevelMax - Level ); + } + p->vRequired = vLevelsR; + return vLevelsR; +} + +/**Function************************************************************* + + Synopsis [Recursively detects combinational loops.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManIsAcyclic_rec( Ivy_Man_t * p, Ivy_Obj_t * pObj ) +{ + // skip the node if it is already visited + if ( Ivy_ObjIsTravIdPrevious(p, pObj) ) + return 1; + // check if the node is part of the combinational loop + if ( Ivy_ObjIsTravIdCurrent(p, pObj) ) + { + fprintf( stdout, "Manager contains combinational loop!\n" ); + fprintf( stdout, "Node \"%d\" is encountered twice on the following path:\n", Ivy_ObjId(pObj) ); + fprintf( stdout, " %d", Ivy_ObjId(pObj) ); + return 0; + } + // mark this node as a node on the current path + Ivy_ObjSetTravIdCurrent( p, pObj ); + // explore equivalent nodes if pObj is the main node + if ( p->pHaig == NULL && pObj->pEquiv && Ivy_ObjRefs(pObj) > 0 ) + { + Ivy_Obj_t * pTemp; + assert( !Ivy_IsComplement(pObj->pEquiv) ); + for ( pTemp = pObj->pEquiv; pTemp != pObj; pTemp = Ivy_Regular(pTemp->pEquiv) ) + { + // traverse the fanin's cone searching for the loop + if ( !Ivy_ManIsAcyclic_rec(p, pTemp) ) + { + // return as soon as the loop is detected + fprintf( stdout, " -> (%d", Ivy_ObjId(pObj) ); + for ( pTemp = pObj->pEquiv; pTemp != pObj; pTemp = Ivy_Regular(pTemp->pEquiv) ) + fprintf( stdout, " %d", Ivy_ObjId(pTemp) ); + fprintf( stdout, ")" ); + return 0; + } + } + } + // quite if it is a CI node + if ( Ivy_ObjIsCi(pObj) || Ivy_ObjIsConst1(pObj) ) + { + // mark this node as a visited node + Ivy_ObjSetTravIdPrevious( p, pObj ); + return 1; + } + assert( Ivy_ObjIsNode(pObj) || Ivy_ObjIsBuf(pObj) ); + // traverse the fanin's cone searching for the loop + if ( !Ivy_ManIsAcyclic_rec(p, Ivy_ObjFanin0(pObj)) ) + { + // return as soon as the loop is detected + fprintf( stdout, " -> %d", Ivy_ObjId(pObj) ); + return 0; + } + // traverse the fanin's cone searching for the loop + if ( Ivy_ObjIsNode(pObj) && !Ivy_ManIsAcyclic_rec(p, Ivy_ObjFanin1(pObj)) ) + { + // return as soon as the loop is detected + fprintf( stdout, " -> %d", Ivy_ObjId(pObj) ); + return 0; + } + // mark this node as a visited node + Ivy_ObjSetTravIdPrevious( p, pObj ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Detects combinational loops.] + + Description [This procedure is based on the idea suggested by Donald Chai. + As we traverse the network and visit the nodes, we need to distinquish + three types of nodes: (1) those that are visited for the first time, + (2) those that have been visited in this traversal but are currently not + on the traversal path, (3) those that have been visited and are currently + on the travesal path. When the node of type (3) is encountered, it means + that there is a combinational loop. To mark the three types of nodes, + two new values of the traversal IDs are used.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManIsAcyclic( Ivy_Man_t * p ) +{ + Ivy_Obj_t * pObj; + int fAcyclic, i; + // set the traversal ID for this DFS ordering + Ivy_ManIncrementTravId( p ); + Ivy_ManIncrementTravId( p ); + // pObj->TravId == pNet->nTravIds means "pObj is on the path" + // pObj->TravId == pNet->nTravIds - 1 means "pObj is visited but is not on the path" + // pObj->TravId < pNet->nTravIds - 1 means "pObj is not visited" + // traverse the network to detect cycles + fAcyclic = 1; + Ivy_ManForEachCo( p, pObj, i ) + { + // traverse the output logic cone + if ( fAcyclic = Ivy_ManIsAcyclic_rec(p, Ivy_ObjFanin0(pObj)) ) + continue; + // stop as soon as the first loop is detected + fprintf( stdout, " (cone of %s \"%d\")\n", Ivy_ObjIsLatch(pObj)? "latch" : "PO", Ivy_ObjId(pObj) ); + break; + } + return fAcyclic; +} + +/**Function************************************************************* + + Synopsis [Sets the levels of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManSetLevels_rec( Ivy_Obj_t * pObj, int fHaig ) +{ + // quit if the node is visited + if ( Ivy_ObjIsMarkA(pObj) ) + return pObj->Level; + Ivy_ObjSetMarkA(pObj); + // quit if this is a CI + if ( Ivy_ObjIsConst1(pObj) || Ivy_ObjIsCi(pObj) ) + return 0; + assert( Ivy_ObjIsBuf(pObj) || Ivy_ObjIsAnd(pObj) || Ivy_ObjIsExor(pObj) ); + // get levels of the fanins + Ivy_ManSetLevels_rec( Ivy_ObjFanin0(pObj), fHaig ); + if ( !Ivy_ObjIsBuf(pObj) ) + Ivy_ManSetLevels_rec( Ivy_ObjFanin1(pObj), fHaig ); + // get level of the node + if ( Ivy_ObjIsBuf(pObj) ) + pObj->Level = 1 + Ivy_ObjFanin0(pObj)->Level; + else if ( Ivy_ObjIsNode(pObj) ) + pObj->Level = Ivy_ObjLevelNew( pObj ); + else assert( 0 ); + // get level of other choices + if ( fHaig && pObj->pEquiv && Ivy_ObjRefs(pObj) > 0 ) + { + Ivy_Obj_t * pTemp; + unsigned LevelMax = pObj->Level; + for ( pTemp = pObj->pEquiv; pTemp != pObj; pTemp = Ivy_Regular(pTemp->pEquiv) ) + { + Ivy_ManSetLevels_rec( pTemp, fHaig ); + LevelMax = IVY_MAX( LevelMax, pTemp->Level ); + } + // get this level + pObj->Level = LevelMax; + for ( pTemp = pObj->pEquiv; pTemp != pObj; pTemp = Ivy_Regular(pTemp->pEquiv) ) + pTemp->Level = LevelMax; + } + return pObj->Level; +} + +/**Function************************************************************* + + Synopsis [Sets the levels of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManSetLevels( Ivy_Man_t * p, int fHaig ) +{ + Ivy_Obj_t * pObj; + int i, LevelMax; + // check if CIs have choices + if ( fHaig ) + { + Ivy_ManForEachCi( p, pObj, i ) + if ( pObj->pEquiv ) + printf( "CI %d has a choice, which will not be visualized.\n", pObj->Id ); + } + // clean the levels + Ivy_ManForEachObj( p, pObj, i ) + pObj->Level = 0; + // compute the levels + LevelMax = 0; + Ivy_ManForEachCo( p, pObj, i ) + { + Ivy_ManSetLevels_rec( Ivy_ObjFanin0(pObj), fHaig ); + LevelMax = IVY_MAX( LevelMax, (int)Ivy_ObjFanin0(pObj)->Level ); + } + // compute levels of nodes without fanout + Ivy_ManForEachObj( p, pObj, i ) + if ( (Ivy_ObjIsNode(pObj) || Ivy_ObjIsBuf(pObj)) && Ivy_ObjRefs(pObj) == 0 ) + { + Ivy_ManSetLevels_rec( pObj, fHaig ); + LevelMax = IVY_MAX( LevelMax, (int)pObj->Level ); + } + // clean the marks + Ivy_ManForEachObj( p, pObj, i ) + Ivy_ObjClearMarkA(pObj); + return LevelMax; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyDsd.c b/abc_with_bb_support/src/aig/ivy/ivyDsd.c new file mode 100644 index 000000000..eb539112d --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyDsd.c @@ -0,0 +1,819 @@ +/**CFile**************************************************************** + + FileName [ivyDsd.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [Disjoint-support decomposition.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyDsd.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// decomposition types +typedef enum { + IVY_DEC_PI, // 0: var + IVY_DEC_CONST1, // 1: CONST1 + IVY_DEC_BUF, // 2: BUF + IVY_DEC_AND, // 3: AND + IVY_DEC_EXOR, // 4: EXOR + IVY_DEC_MUX, // 5: MUX + IVY_DEC_MAJ, // 6: MAJ + IVY_DEC_PRIME // 7: undecomposable +} Ivy_DecType_t; + +typedef struct Ivy_Dec_t_ Ivy_Dec_t; +struct Ivy_Dec_t_ +{ + unsigned Type : 4; // the node type (PI, CONST1, AND, EXOR, MUX, PRIME) + unsigned fCompl : 1; // shows if node is complemented (root node only) + unsigned nFans : 3; // the number of fanins + unsigned Fan0 : 4; // fanin 0 + unsigned Fan1 : 4; // fanin 1 + unsigned Fan2 : 4; // fanin 2 + unsigned Fan3 : 4; // fanin 3 + unsigned Fan4 : 4; // fanin 4 + unsigned Fan5 : 4; // fanin 5 +}; + +static inline int Ivy_DecToInt( Ivy_Dec_t Node ) { return *((int *)&Node); } +static inline Ivy_Dec_t Ivy_IntToDec( int Node ) { return *((Ivy_Dec_t *)&Node); } +static inline void Ivy_DecClear( Ivy_Dec_t * pNode ) { *((int *)pNode) = 0; } + + +static unsigned s_Masks[6][2] = { + { 0x55555555, 0xAAAAAAAA }, + { 0x33333333, 0xCCCCCCCC }, + { 0x0F0F0F0F, 0xF0F0F0F0 }, + { 0x00FF00FF, 0xFF00FF00 }, + { 0x0000FFFF, 0xFFFF0000 }, + { 0x00000000, 0xFFFFFFFF } +}; + +static inline int Ivy_TruthWordCountOnes( unsigned uWord ) +{ + uWord = (uWord & 0x55555555) + ((uWord>>1) & 0x55555555); + uWord = (uWord & 0x33333333) + ((uWord>>2) & 0x33333333); + uWord = (uWord & 0x0F0F0F0F) + ((uWord>>4) & 0x0F0F0F0F); + uWord = (uWord & 0x00FF00FF) + ((uWord>>8) & 0x00FF00FF); + return (uWord & 0x0000FFFF) + (uWord>>16); +} + +static inline int Ivy_TruthCofactorIsConst( unsigned uTruth, int Var, int Cof, int Const ) +{ + if ( Const == 0 ) + return (uTruth & s_Masks[Var][Cof]) == 0; + else + return (uTruth & s_Masks[Var][Cof]) == s_Masks[Var][Cof]; +} + +static inline int Ivy_TruthCofactorIsOne( unsigned uTruth, int Var ) +{ + return (uTruth & s_Masks[Var][0]) == 0; +} + +static inline unsigned Ivy_TruthCofactor( unsigned uTruth, int Var ) +{ + unsigned uCofactor = uTruth & s_Masks[Var >> 1][(Var & 1) == 0]; + int Shift = (1 << (Var >> 1)); + if ( Var & 1 ) + return uCofactor | (uCofactor << Shift); + return uCofactor | (uCofactor >> Shift); +} + +static inline unsigned Ivy_TruthCofactor2( unsigned uTruth, int Var0, int Var1 ) +{ + return Ivy_TruthCofactor( Ivy_TruthCofactor(uTruth, Var0), Var1 ); +} + +// returns 1 if the truth table depends on this var (var is regular interger var) +static inline int Ivy_TruthDepends( unsigned uTruth, int Var ) +{ + return Ivy_TruthCofactor(uTruth, Var << 1) != Ivy_TruthCofactor(uTruth, (Var << 1) | 1); +} + +static inline void Ivy_DecSetVar( Ivy_Dec_t * pNode, int iNum, unsigned Var ) +{ + assert( iNum >= 0 && iNum <= 5 ); + switch( iNum ) + { + case 0: pNode->Fan0 = Var; break; + case 1: pNode->Fan1 = Var; break; + case 2: pNode->Fan2 = Var; break; + case 3: pNode->Fan3 = Var; break; + case 4: pNode->Fan4 = Var; break; + case 5: pNode->Fan5 = Var; break; + } +} + +static inline unsigned Ivy_DecGetVar( Ivy_Dec_t * pNode, int iNum ) +{ + assert( iNum >= 0 && iNum <= 5 ); + switch( iNum ) + { + case 0: return pNode->Fan0; + case 1: return pNode->Fan1; + case 2: return pNode->Fan2; + case 3: return pNode->Fan3; + case 4: return pNode->Fan4; + case 5: return pNode->Fan5; + } + return ~0; +} + +static int Ivy_TruthDecompose_rec( unsigned uTruth, Vec_Int_t * vTree ); +static int Ivy_TruthRecognizeMuxMaj( unsigned uTruth, int * pSupp, int nSupp, Vec_Int_t * vTree ); + +//int nTruthDsd; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes DSD of truth table of 5 variables or less.] + + Description [Returns 1 if the function is a constant or is fully + DSD decomposable using AND/EXOR/MUX gates.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_TruthDsd( unsigned uTruth, Vec_Int_t * vTree ) +{ + Ivy_Dec_t Node; + int i, RetValue; + // set the PI variables + Vec_IntClear( vTree ); + for ( i = 0; i < 5; i++ ) + Vec_IntPush( vTree, 0 ); + // check if it is a constant + if ( uTruth == 0 || ~uTruth == 0 ) + { + Ivy_DecClear( &Node ); + Node.Type = IVY_DEC_CONST1; + Node.fCompl = (uTruth == 0); + Vec_IntPush( vTree, Ivy_DecToInt(Node) ); + return 1; + } + // perform the decomposition + RetValue = Ivy_TruthDecompose_rec( uTruth, vTree ); + if ( RetValue == -1 ) + return 0; + // get the topmost node + if ( (RetValue >> 1) < 5 ) + { // add buffer + Ivy_DecClear( &Node ); + Node.Type = IVY_DEC_BUF; + Node.fCompl = (RetValue & 1); + Node.Fan0 = ((RetValue >> 1) << 1); + Vec_IntPush( vTree, Ivy_DecToInt(Node) ); + } + else if ( RetValue & 1 ) + { // check if the topmost node has to be complemented + Node = Ivy_IntToDec( Vec_IntPop(vTree) ); + assert( Node.fCompl == 0 ); + Node.fCompl = (RetValue & 1); + Vec_IntPush( vTree, Ivy_DecToInt(Node) ); + } + if ( uTruth != Ivy_TruthDsdCompute(vTree) ) + printf( "Verification failed.\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Computes DSD of truth table.] + + Description [Returns the number of topmost decomposition node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_TruthDecompose_rec( unsigned uTruth, Vec_Int_t * vTree ) +{ + Ivy_Dec_t Node; + int Supp[5], Vars0[5], Vars1[5], Vars2[5], * pVars; + int nSupp, Count0, Count1, Count2, nVars, RetValue, fCompl, i; + unsigned uTruthCof, uCof0, uCof1; + + // get constant confactors + Count0 = Count1 = Count2 = nSupp = 0; + for ( i = 0; i < 5; i++ ) + { + if ( Ivy_TruthCofactorIsConst(uTruth, i, 0, 0) ) + Vars0[Count0++] = (i << 1) | 0; + else if ( Ivy_TruthCofactorIsConst(uTruth, i, 1, 0) ) + Vars0[Count0++] = (i << 1) | 1; + else if ( Ivy_TruthCofactorIsConst(uTruth, i, 0, 1) ) + Vars1[Count1++] = (i << 1) | 0; + else if ( Ivy_TruthCofactorIsConst(uTruth, i, 1, 1) ) + Vars1[Count1++] = (i << 1) | 1; + else + { + uCof0 = Ivy_TruthCofactor( uTruth, (i << 1) | 1 ); + uCof1 = Ivy_TruthCofactor( uTruth, (i << 1) | 0 ); + if ( uCof0 == ~uCof1 ) + Vars2[Count2++] = (i << 1) | 0; + else if ( uCof0 != uCof1 ) + Supp[nSupp++] = i; + } + } + assert( Count0 == 0 || Count1 == 0 ); + assert( Count0 == 0 || Count2 == 0 ); + assert( Count1 == 0 || Count2 == 0 ); + + // consider the case of a single variable + if ( Count0 == 1 && nSupp == 0 ) + return Vars0[0]; + + // consider more complex decompositions + if ( Count0 == 0 && Count1 == 0 && Count2 == 0 ) + return Ivy_TruthRecognizeMuxMaj( uTruth, Supp, nSupp, vTree ); + + // extract the nodes + Ivy_DecClear( &Node ); + if ( Count0 > 0 ) + nVars = Count0, pVars = Vars0, Node.Type = IVY_DEC_AND, fCompl = 0; + else if ( Count1 > 0 ) + nVars = Count1, pVars = Vars1, Node.Type = IVY_DEC_AND, fCompl = 1, uTruth = ~uTruth; + else if ( Count2 > 0 ) + nVars = Count2, pVars = Vars2, Node.Type = IVY_DEC_EXOR, fCompl = 0; + else + assert( 0 ); + Node.nFans = nVars+(nSupp>0); + + // compute cofactor + uTruthCof = uTruth; + for ( i = 0; i < nVars; i++ ) + { + uTruthCof = Ivy_TruthCofactor( uTruthCof, pVars[i] ); + Ivy_DecSetVar( &Node, i, pVars[i] ); + } + + if ( Node.Type == IVY_DEC_EXOR ) + fCompl ^= ((Node.nFans & 1) == 0); + + if ( nSupp > 0 ) + { + assert( uTruthCof != 0 && ~uTruthCof != 0 ); + // call recursively + RetValue = Ivy_TruthDecompose_rec( uTruthCof, vTree ); + // quit if non-decomposable + if ( RetValue == -1 ) + return -1; + // remove the complement from the child if the node is EXOR + if ( Node.Type == IVY_DEC_EXOR && (RetValue & 1) ) + { + fCompl ^= 1; + RetValue ^= 1; + } + // set the new decomposition + Ivy_DecSetVar( &Node, nVars, RetValue ); + } + else if ( Node.Type == IVY_DEC_EXOR ) + fCompl ^= (uTruthCof == 0); + + Vec_IntPush( vTree, Ivy_DecToInt(Node) ); + return ((Vec_IntSize(vTree)-1) << 1) | fCompl; +} + +/**Function************************************************************* + + Synopsis [Returns a non-negative number if the truth table is a MUX.] + + Description [If the truth table is a MUX, returns the variable as follows: + first, control variable; second, positive cofactor; third, negative cofactor.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_TruthRecognizeMuxMaj( unsigned uTruth, int * pSupp, int nSupp, Vec_Int_t * vTree ) +{ + Ivy_Dec_t Node; + int i, k, RetValue0, RetValue1; + unsigned uCof0, uCof1, Num; + char Count[3]; + assert( nSupp >= 3 ); + // start the node + Ivy_DecClear( &Node ); + Node.Type = IVY_DEC_MUX; + Node.nFans = 3; + // try each of the variables + for ( i = 0; i < nSupp; i++ ) + { + // get the cofactors with respect to these variables + uCof0 = Ivy_TruthCofactor( uTruth, (pSupp[i] << 1) | 1 ); + uCof1 = Ivy_TruthCofactor( uTruth, pSupp[i] << 1 ); + // go through all other variables and make sure + // each of them belongs to the support of one cofactor + for ( k = 0; k < nSupp; k++ ) + { + if ( k == i ) + continue; + if ( Ivy_TruthDepends(uCof0, pSupp[k]) && Ivy_TruthDepends(uCof1, pSupp[k]) ) + break; + } + if ( k < nSupp ) + continue; + // MUX decomposition exists + RetValue0 = Ivy_TruthDecompose_rec( uCof0, vTree ); + if ( RetValue0 == -1 ) + break; + RetValue1 = Ivy_TruthDecompose_rec( uCof1, vTree ); + if ( RetValue1 == -1 ) + break; + // both of them exist; create the node + Ivy_DecSetVar( &Node, 0, pSupp[i] << 1 ); + Ivy_DecSetVar( &Node, 1, RetValue1 ); + Ivy_DecSetVar( &Node, 2, RetValue0 ); + Vec_IntPush( vTree, Ivy_DecToInt(Node) ); + return ((Vec_IntSize(vTree)-1) << 1) | 0; + } + // check majority gate + if ( nSupp > 3 ) + return -1; + if ( Ivy_TruthWordCountOnes(uTruth) != 16 ) + return -1; + // this is a majority gate; determine polarity + Node.Type = IVY_DEC_MAJ; + Count[0] = Count[1] = Count[2] = 0; + for ( i = 0; i < 8; i++ ) + { + Num = 0; + for ( k = 0; k < 3; k++ ) + if ( i & (1 << k) ) + Num |= (1 << pSupp[k]); + assert( Num < 32 ); + if ( (uTruth & (1 << Num)) == 0 ) + continue; + for ( k = 0; k < 3; k++ ) + if ( i & (1 << k) ) + Count[k]++; + } + assert( Count[0] == 1 || Count[0] == 3 ); + assert( Count[1] == 1 || Count[1] == 3 ); + assert( Count[2] == 1 || Count[2] == 3 ); + Ivy_DecSetVar( &Node, 0, (pSupp[0] << 1)|(Count[0] == 1) ); + Ivy_DecSetVar( &Node, 1, (pSupp[1] << 1)|(Count[1] == 1) ); + Ivy_DecSetVar( &Node, 2, (pSupp[2] << 1)|(Count[2] == 1) ); + Vec_IntPush( vTree, Ivy_DecToInt(Node) ); + return ((Vec_IntSize(vTree)-1) << 1) | 0; +} + + +/**Function************************************************************* + + Synopsis [Computes truth table of decomposition tree for verification.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Ivy_TruthDsdCompute_rec( int iNode, Vec_Int_t * vTree ) +{ + unsigned uTruthChild, uTruthTotal; + int Var, i; + // get the node + Ivy_Dec_t Node = Ivy_IntToDec( Vec_IntEntry(vTree, iNode) ); + // compute the node function + if ( Node.Type == IVY_DEC_CONST1 ) + return s_Masks[5][ !Node.fCompl ]; + if ( Node.Type == IVY_DEC_PI ) + return s_Masks[iNode][ !Node.fCompl ]; + if ( Node.Type == IVY_DEC_BUF ) + { + uTruthTotal = Ivy_TruthDsdCompute_rec( Node.Fan0 >> 1, vTree ); + return Node.fCompl? ~uTruthTotal : uTruthTotal; + } + if ( Node.Type == IVY_DEC_AND ) + { + uTruthTotal = s_Masks[5][1]; + for ( i = 0; i < (int)Node.nFans; i++ ) + { + Var = Ivy_DecGetVar( &Node, i ); + uTruthChild = Ivy_TruthDsdCompute_rec( Var >> 1, vTree ); + uTruthTotal = (Var & 1)? uTruthTotal & ~uTruthChild : uTruthTotal & uTruthChild; + } + return Node.fCompl? ~uTruthTotal : uTruthTotal; + } + if ( Node.Type == IVY_DEC_EXOR ) + { + uTruthTotal = 0; + for ( i = 0; i < (int)Node.nFans; i++ ) + { + Var = Ivy_DecGetVar( &Node, i ); + uTruthTotal ^= Ivy_TruthDsdCompute_rec( Var >> 1, vTree ); + assert( (Var & 1) == 0 ); + } + return Node.fCompl? ~uTruthTotal : uTruthTotal; + } + assert( Node.fCompl == 0 ); + if ( Node.Type == IVY_DEC_MUX || Node.Type == IVY_DEC_MAJ ) + { + unsigned uTruthChildC, uTruthChild1, uTruthChild0; + int VarC, Var1, Var0; + VarC = Ivy_DecGetVar( &Node, 0 ); + Var1 = Ivy_DecGetVar( &Node, 1 ); + Var0 = Ivy_DecGetVar( &Node, 2 ); + uTruthChildC = Ivy_TruthDsdCompute_rec( VarC >> 1, vTree ); + uTruthChild1 = Ivy_TruthDsdCompute_rec( Var1 >> 1, vTree ); + uTruthChild0 = Ivy_TruthDsdCompute_rec( Var0 >> 1, vTree ); + assert( Node.Type == IVY_DEC_MAJ || (VarC & 1) == 0 ); + uTruthChildC = (VarC & 1)? ~uTruthChildC : uTruthChildC; + uTruthChild1 = (Var1 & 1)? ~uTruthChild1 : uTruthChild1; + uTruthChild0 = (Var0 & 1)? ~uTruthChild0 : uTruthChild0; + if ( Node.Type == IVY_DEC_MUX ) + return (uTruthChildC & uTruthChild1) | (~uTruthChildC & uTruthChild0); + else + return (uTruthChildC & uTruthChild1) | (uTruthChildC & uTruthChild0) | (uTruthChild1 & uTruthChild0); + } + assert( 0 ); + return 0; +} + + +/**Function************************************************************* + + Synopsis [Computes truth table of decomposition tree for verification.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Ivy_TruthDsdCompute( Vec_Int_t * vTree ) +{ + return Ivy_TruthDsdCompute_rec( Vec_IntSize(vTree)-1, vTree ); +} + +/**Function************************************************************* + + Synopsis [Prints the decomposition tree.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_TruthDsdPrint_rec( FILE * pFile, int iNode, Vec_Int_t * vTree ) +{ + int Var, i; + // get the node + Ivy_Dec_t Node = Ivy_IntToDec( Vec_IntEntry(vTree, iNode) ); + // compute the node function + if ( Node.Type == IVY_DEC_CONST1 ) + fprintf( pFile, "Const1%s", (Node.fCompl? "\'" : "") ); + else if ( Node.Type == IVY_DEC_PI ) + fprintf( pFile, "%c%s", 'a' + iNode, (Node.fCompl? "\'" : "") ); + else if ( Node.Type == IVY_DEC_BUF ) + { + Ivy_TruthDsdPrint_rec( pFile, Node.Fan0 >> 1, vTree ); + fprintf( pFile, "%s", (Node.fCompl? "\'" : "") ); + } + else if ( Node.Type == IVY_DEC_AND ) + { + fprintf( pFile, "AND(" ); + for ( i = 0; i < (int)Node.nFans; i++ ) + { + Var = Ivy_DecGetVar( &Node, i ); + Ivy_TruthDsdPrint_rec( pFile, Var >> 1, vTree ); + fprintf( pFile, "%s", (Var & 1)? "\'" : "" ); + if ( i != (int)Node.nFans-1 ) + fprintf( pFile, "," ); + } + fprintf( pFile, ")%s", (Node.fCompl? "\'" : "") ); + } + else if ( Node.Type == IVY_DEC_EXOR ) + { + fprintf( pFile, "EXOR(" ); + for ( i = 0; i < (int)Node.nFans; i++ ) + { + Var = Ivy_DecGetVar( &Node, i ); + Ivy_TruthDsdPrint_rec( pFile, Var >> 1, vTree ); + if ( i != (int)Node.nFans-1 ) + fprintf( pFile, "," ); + assert( (Var & 1) == 0 ); + } + fprintf( pFile, ")%s", (Node.fCompl? "\'" : "") ); + } + else if ( Node.Type == IVY_DEC_MUX || Node.Type == IVY_DEC_MAJ ) + { + int VarC, Var1, Var0; + assert( Node.fCompl == 0 ); + VarC = Ivy_DecGetVar( &Node, 0 ); + Var1 = Ivy_DecGetVar( &Node, 1 ); + Var0 = Ivy_DecGetVar( &Node, 2 ); + fprintf( pFile, "%s", (Node.Type == IVY_DEC_MUX)? "MUX(" : "MAJ(" ); + Ivy_TruthDsdPrint_rec( pFile, VarC >> 1, vTree ); + fprintf( pFile, "%s", (VarC & 1)? "\'" : "" ); + fprintf( pFile, "," ); + Ivy_TruthDsdPrint_rec( pFile, Var1 >> 1, vTree ); + fprintf( pFile, "%s", (Var1 & 1)? "\'" : "" ); + fprintf( pFile, "," ); + Ivy_TruthDsdPrint_rec( pFile, Var0 >> 1, vTree ); + fprintf( pFile, "%s", (Var0 & 1)? "\'" : "" ); + fprintf( pFile, ")" ); + } + else assert( 0 ); +} + + +/**Function************************************************************* + + Synopsis [Prints the decomposition tree.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_TruthDsdPrint( FILE * pFile, Vec_Int_t * vTree ) +{ + fprintf( pFile, "F = " ); + Ivy_TruthDsdPrint_rec( pFile, Vec_IntSize(vTree)-1, vTree ); + fprintf( pFile, "\n" ); +} + +/**Function************************************************************* + + Synopsis [Implement DSD in the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_ManDsdConstruct_rec( Ivy_Man_t * p, Vec_Int_t * vFront, int iNode, Vec_Int_t * vTree ) +{ + Ivy_Obj_t * pResult, * pChild, * pNodes[16]; + int Var, i; + // get the node + Ivy_Dec_t Node = Ivy_IntToDec( Vec_IntEntry(vTree, iNode) ); + // compute the node function + if ( Node.Type == IVY_DEC_CONST1 ) + return Ivy_NotCond( Ivy_ManConst1(p), Node.fCompl ); + if ( Node.Type == IVY_DEC_PI ) + { + pResult = Ivy_ManObj( p, Vec_IntEntry(vFront, iNode) ); + return Ivy_NotCond( pResult, Node.fCompl ); + } + if ( Node.Type == IVY_DEC_BUF ) + { + pResult = Ivy_ManDsdConstruct_rec( p, vFront, Node.Fan0 >> 1, vTree ); + return Ivy_NotCond( pResult, Node.fCompl ); + } + if ( Node.Type == IVY_DEC_AND || Node.Type == IVY_DEC_EXOR ) + { + for ( i = 0; i < (int)Node.nFans; i++ ) + { + Var = Ivy_DecGetVar( &Node, i ); + assert( Node.Type == IVY_DEC_AND || (Var & 1) == 0 ); + pChild = Ivy_ManDsdConstruct_rec( p, vFront, Var >> 1, vTree ); + pChild = Ivy_NotCond( pChild, (Var & 1) ); + pNodes[i] = pChild; + } + +// Ivy_MultiEval( pNodes, Node.nFans, Node.Type == IVY_DEC_AND ? IVY_AND : IVY_EXOR ); + + pResult = Ivy_Multi( p, pNodes, Node.nFans, Node.Type == IVY_DEC_AND ? IVY_AND : IVY_EXOR ); + return Ivy_NotCond( pResult, Node.fCompl ); + } + assert( Node.fCompl == 0 ); + if ( Node.Type == IVY_DEC_MUX || Node.Type == IVY_DEC_MAJ ) + { + int VarC, Var1, Var0; + VarC = Ivy_DecGetVar( &Node, 0 ); + Var1 = Ivy_DecGetVar( &Node, 1 ); + Var0 = Ivy_DecGetVar( &Node, 2 ); + pNodes[0] = Ivy_ManDsdConstruct_rec( p, vFront, VarC >> 1, vTree ); + pNodes[1] = Ivy_ManDsdConstruct_rec( p, vFront, Var1 >> 1, vTree ); + pNodes[2] = Ivy_ManDsdConstruct_rec( p, vFront, Var0 >> 1, vTree ); + assert( Node.Type == IVY_DEC_MAJ || (VarC & 1) == 0 ); + pNodes[0] = Ivy_NotCond( pNodes[0], (VarC & 1) ); + pNodes[1] = Ivy_NotCond( pNodes[1], (Var1 & 1) ); + pNodes[2] = Ivy_NotCond( pNodes[2], (Var0 & 1) ); + if ( Node.Type == IVY_DEC_MUX ) + return Ivy_Mux( p, pNodes[0], pNodes[1], pNodes[2] ); + else + return Ivy_Maj( p, pNodes[0], pNodes[1], pNodes[2] ); + } + assert( 0 ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Implement DSD in the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_ManDsdConstruct( Ivy_Man_t * p, Vec_Int_t * vFront, Vec_Int_t * vTree ) +{ + int Entry, i; + // implement latches on the frontier (TEMPORARY!!!) + Vec_IntForEachEntry( vFront, Entry, i ) + Vec_IntWriteEntry( vFront, i, Ivy_LeafId(Entry) ); + // recursively construct the tree + return Ivy_ManDsdConstruct_rec( p, vFront, Vec_IntSize(vTree)-1, vTree ); +} + + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_TruthDsdComputePrint( unsigned uTruth ) +{ + static Vec_Int_t * vTree = NULL; + if ( vTree == NULL ) + vTree = Vec_IntAlloc( 12 ); + if ( Ivy_TruthDsd( uTruth, vTree ) ) + Ivy_TruthDsdPrint( stdout, vTree ); + else + printf( "Undecomposable\n" ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_TruthTestOne( unsigned uTruth ) +{ + static int Counter = 0; + static Vec_Int_t * vTree = NULL; + // decompose + if ( vTree == NULL ) + vTree = Vec_IntAlloc( 12 ); + + if ( !Ivy_TruthDsd( uTruth, vTree ) ) + { +// printf( "Undecomposable\n" ); + } + else + { +// nTruthDsd++; + printf( "%5d : ", Counter++ ); + Extra_PrintBinary( stdout, &uTruth, 32 ); + printf( " " ); + Ivy_TruthDsdPrint( stdout, vTree ); + if ( uTruth != Ivy_TruthDsdCompute(vTree) ) + printf( "Verification failed.\n" ); + } +// Vec_IntFree( vTree ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_TruthTest() +{ + FILE * pFile; + char Buffer[100]; + unsigned uTruth; + int i; + + pFile = fopen( "npn4.txt", "r" ); + for ( i = 0; i < 222; i++ ) +// pFile = fopen( "npn5.txt", "r" ); +// for ( i = 0; i < 616126; i++ ) + { + fscanf( pFile, "%s", Buffer ); + Extra_ReadHexadecimal( &uTruth, Buffer+2, 4 ); +// Extra_ReadHexadecimal( &uTruth, Buffer+2, 5 ); + uTruth |= (uTruth << 16); +// uTruth = ~uTruth; + Ivy_TruthTestOne( uTruth ); + } + fclose( pFile ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_TruthTest3() +{ + FILE * pFile; + char Buffer[100]; + unsigned uTruth; + int i; + + pFile = fopen( "npn3.txt", "r" ); + for ( i = 0; i < 14; i++ ) + { + fscanf( pFile, "%s", Buffer ); + Extra_ReadHexadecimal( &uTruth, Buffer+2, 3 ); + uTruth = uTruth | (uTruth << 8) | (uTruth << 16) | (uTruth << 24); + Ivy_TruthTestOne( uTruth ); + } + fclose( pFile ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_TruthTest5() +{ + FILE * pFile; + char Buffer[100]; + unsigned uTruth; + int i; + +// pFile = fopen( "npn4.txt", "r" ); +// for ( i = 0; i < 222; i++ ) + pFile = fopen( "npn5.txt", "r" ); + for ( i = 0; i < 616126; i++ ) + { + fscanf( pFile, "%s", Buffer ); +// Extra_ReadHexadecimal( &uTruth, Buffer+2, 4 ); + Extra_ReadHexadecimal( &uTruth, Buffer+2, 5 ); +// uTruth |= (uTruth << 16); +// uTruth = ~uTruth; + Ivy_TruthTestOne( uTruth ); + } + fclose( pFile ); +} + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyFanout.c b/abc_with_bb_support/src/aig/ivy/ivyFanout.c new file mode 100644 index 000000000..ba65e2070 --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyFanout.c @@ -0,0 +1,309 @@ +/**CFile**************************************************************** + + FileName [ivyFanout.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [Representation of the fanouts.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyFanout.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// getting hold of the next fanout of the node +static inline Ivy_Obj_t * Ivy_ObjNextFanout( Ivy_Obj_t * pObj, Ivy_Obj_t * pFanout ) +{ + assert( !Ivy_IsComplement(pObj) ); + assert( !Ivy_IsComplement(pFanout) ); + if ( pFanout == NULL ) + return NULL; + if ( Ivy_ObjFanin0(pFanout) == pObj ) + return pFanout->pNextFan0; + assert( Ivy_ObjFanin1(pFanout) == pObj ); + return pFanout->pNextFan1; +} + +// getting hold of the previous fanout of the node +static inline Ivy_Obj_t * Ivy_ObjPrevFanout( Ivy_Obj_t * pObj, Ivy_Obj_t * pFanout ) +{ + assert( !Ivy_IsComplement(pObj) ); + assert( !Ivy_IsComplement(pFanout) ); + if ( pFanout == NULL ) + return NULL; + if ( Ivy_ObjFanin0(pFanout) == pObj ) + return pFanout->pPrevFan0; + assert( Ivy_ObjFanin1(pFanout) == pObj ); + return pFanout->pPrevFan1; +} + +// getting hold of the place where the next fanout will be attached +static inline Ivy_Obj_t ** Ivy_ObjNextFanoutPlace( Ivy_Obj_t * pObj, Ivy_Obj_t * pFanout ) +{ + assert( !Ivy_IsComplement(pObj) ); + assert( !Ivy_IsComplement(pFanout) ); + if ( Ivy_ObjFanin0(pFanout) == pObj ) + return &pFanout->pNextFan0; + assert( Ivy_ObjFanin1(pFanout) == pObj ); + return &pFanout->pNextFan1; +} + +// getting hold of the place where the next fanout will be attached +static inline Ivy_Obj_t ** Ivy_ObjPrevFanoutPlace( Ivy_Obj_t * pObj, Ivy_Obj_t * pFanout ) +{ + assert( !Ivy_IsComplement(pObj) ); + assert( !Ivy_IsComplement(pFanout) ); + if ( Ivy_ObjFanin0(pFanout) == pObj ) + return &pFanout->pPrevFan0; + assert( Ivy_ObjFanin1(pFanout) == pObj ); + return &pFanout->pPrevFan1; +} + +// getting hold of the place where the next fanout will be attached +static inline Ivy_Obj_t ** Ivy_ObjPrevNextFanoutPlace( Ivy_Obj_t * pObj, Ivy_Obj_t * pFanout ) +{ + Ivy_Obj_t * pTemp; + assert( !Ivy_IsComplement(pObj) ); + assert( !Ivy_IsComplement(pFanout) ); + pTemp = Ivy_ObjPrevFanout(pObj, pFanout); + if ( pTemp == NULL ) + return &pObj->pFanout; + if ( Ivy_ObjFanin0(pTemp) == pObj ) + return &pTemp->pNextFan0; + assert( Ivy_ObjFanin1(pTemp) == pObj ); + return &pTemp->pNextFan1; +} + +// getting hold of the place where the next fanout will be attached +static inline Ivy_Obj_t ** Ivy_ObjNextPrevFanoutPlace( Ivy_Obj_t * pObj, Ivy_Obj_t * pFanout ) +{ + Ivy_Obj_t * pTemp; + assert( !Ivy_IsComplement(pObj) ); + assert( !Ivy_IsComplement(pFanout) ); + pTemp = Ivy_ObjNextFanout(pObj, pFanout); + if ( pTemp == NULL ) + return NULL; + if ( Ivy_ObjFanin0(pTemp) == pObj ) + return &pTemp->pPrevFan0; + assert( Ivy_ObjFanin1(pTemp) == pObj ); + return &pTemp->pPrevFan1; +} + +// iterator through the fanouts of the node +#define Ivy_ObjForEachFanoutInt( pObj, pFanout ) \ + for ( pFanout = (pObj)->pFanout; pFanout; \ + pFanout = Ivy_ObjNextFanout(pObj, pFanout) ) + +// safe iterator through the fanouts of the node +#define Ivy_ObjForEachFanoutIntSafe( pObj, pFanout, pFanout2 ) \ + for ( pFanout = (pObj)->pFanout, \ + pFanout2 = Ivy_ObjNextFanout(pObj, pFanout); \ + pFanout; \ + pFanout = pFanout2, \ + pFanout2 = Ivy_ObjNextFanout(pObj, pFanout) ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the fanout representation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManStartFanout( Ivy_Man_t * p ) +{ + Ivy_Obj_t * pObj; + int i; + assert( !p->fFanout ); + p->fFanout = 1; + Ivy_ManForEachObj( p, pObj, i ) + { + if ( Ivy_ObjFanin0(pObj) ) + Ivy_ObjAddFanout( p, Ivy_ObjFanin0(pObj), pObj ); + if ( Ivy_ObjFanin1(pObj) ) + Ivy_ObjAddFanout( p, Ivy_ObjFanin1(pObj), pObj ); + } +} + +/**Function************************************************************* + + Synopsis [Stops the fanout representation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManStopFanout( Ivy_Man_t * p ) +{ + Ivy_Obj_t * pObj; + int i; + assert( p->fFanout ); + p->fFanout = 0; + Ivy_ManForEachObj( p, pObj, i ) + pObj->pFanout = pObj->pNextFan0 = pObj->pNextFan1 = pObj->pPrevFan0 = pObj->pPrevFan1 = NULL; +} + +/**Function************************************************************* + + Synopsis [Add the fanout.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ObjAddFanout( Ivy_Man_t * p, Ivy_Obj_t * pFanin, Ivy_Obj_t * pFanout ) +{ + assert( p->fFanout ); + if ( pFanin->pFanout ) + { + *Ivy_ObjNextFanoutPlace(pFanin, pFanout) = pFanin->pFanout; + *Ivy_ObjPrevFanoutPlace(pFanin, pFanin->pFanout) = pFanout; + } + pFanin->pFanout = pFanout; +} + +/**Function************************************************************* + + Synopsis [Removes the fanout.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ObjDeleteFanout( Ivy_Man_t * p, Ivy_Obj_t * pFanin, Ivy_Obj_t * pFanout ) +{ + Ivy_Obj_t ** ppPlace1, ** ppPlace2, ** ppPlaceN; + assert( pFanin->pFanout != NULL ); + + ppPlace1 = Ivy_ObjNextFanoutPlace(pFanin, pFanout); + ppPlaceN = Ivy_ObjPrevNextFanoutPlace(pFanin, pFanout); + assert( *ppPlaceN == pFanout ); + if ( ppPlaceN ) + *ppPlaceN = *ppPlace1; + + ppPlace2 = Ivy_ObjPrevFanoutPlace(pFanin, pFanout); + ppPlaceN = Ivy_ObjNextPrevFanoutPlace(pFanin, pFanout); + assert( ppPlaceN == NULL || *ppPlaceN == pFanout ); + if ( ppPlaceN ) + *ppPlaceN = *ppPlace2; + + *ppPlace1 = NULL; + *ppPlace2 = NULL; +} + +/**Function************************************************************* + + Synopsis [Replaces the fanout of pOld to be pFanoutNew.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ObjPatchFanout( Ivy_Man_t * p, Ivy_Obj_t * pFanin, Ivy_Obj_t * pFanoutOld, Ivy_Obj_t * pFanoutNew ) +{ + Ivy_Obj_t ** ppPlace; + ppPlace = Ivy_ObjPrevNextFanoutPlace(pFanin, pFanoutOld); + assert( *ppPlace == pFanoutOld ); + if ( ppPlace ) + *ppPlace = pFanoutNew; + ppPlace = Ivy_ObjNextPrevFanoutPlace(pFanin, pFanoutOld); + assert( ppPlace == NULL || *ppPlace == pFanoutOld ); + if ( ppPlace ) + *ppPlace = pFanoutNew; + // assuming that pFanoutNew already points to the next fanout +} + +/**Function************************************************************* + + Synopsis [Starts iteration through the fanouts.] + + Description [Copies the currently available fanouts into the array.] + + SideEffects [Can be used while the fanouts are being removed.] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ObjCollectFanouts( Ivy_Man_t * p, Ivy_Obj_t * pObj, Vec_Ptr_t * vArray ) +{ + Ivy_Obj_t * pFanout; + assert( p->fFanout ); + assert( !Ivy_IsComplement(pObj) ); + Vec_PtrClear( vArray ); + Ivy_ObjForEachFanoutInt( pObj, pFanout ) + Vec_PtrPush( vArray, pFanout ); +} + +/**Function************************************************************* + + Synopsis [Reads one fanout.] + + Description [Returns fanout if there is only one fanout.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_ObjReadFirstFanout( Ivy_Man_t * p, Ivy_Obj_t * pObj ) +{ + return pObj->pFanout; +} + +/**Function************************************************************* + + Synopsis [Reads one fanout.] + + Description [Returns fanout if there is only one fanout.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ObjFanoutNum( Ivy_Man_t * p, Ivy_Obj_t * pObj ) +{ + Ivy_Obj_t * pFanout; + int Counter = 0; + Ivy_ObjForEachFanoutInt( pObj, pFanout ) + Counter++; + return Counter; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyFastMap.c b/abc_with_bb_support/src/aig/ivy/ivyFastMap.c new file mode 100644 index 000000000..1f2e12497 --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyFastMap.c @@ -0,0 +1,1593 @@ +/**CFile**************************************************************** + + FileName [ivyFastMap.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [Fast FPGA mapping.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyFastMap.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define IVY_INFINITY 10000 + +typedef struct Ivy_SuppMan_t_ Ivy_SuppMan_t; +struct Ivy_SuppMan_t_ +{ + int nLimit; // the limit on the number of inputs + int nObjs; // the number of entries + int nSize; // size of each entry in bytes + char * pMem; // memory allocated + Vec_Vec_t * vLuts; // the array of nodes used in the mapping +}; + +typedef struct Ivy_Supp_t_ Ivy_Supp_t; +struct Ivy_Supp_t_ +{ + char nSize; // the number of support nodes + char fMark; // multipurpose mask + char fMark2; // multipurpose mask + char fMark3; // multipurpose mask + int nRefs; // the number of references + short Delay; // the delay of the node + short DelayR; // the reverse delay of the node + int pArray[0]; // the support nodes +}; + +static inline Ivy_Supp_t * Ivy_ObjSupp( Ivy_Man_t * pAig, Ivy_Obj_t * pObj ) +{ + return (Ivy_Supp_t *)(((Ivy_SuppMan_t*)pAig->pData)->pMem + pObj->Id * ((Ivy_SuppMan_t*)pAig->pData)->nSize); +} +static inline Ivy_Supp_t * Ivy_ObjSuppStart( Ivy_Man_t * pAig, Ivy_Obj_t * pObj ) +{ + Ivy_Supp_t * pSupp; + pSupp = Ivy_ObjSupp( pAig, pObj ); + pSupp->fMark = 0; + pSupp->Delay = 0; + pSupp->nSize = 1; + pSupp->pArray[0] = pObj->Id; + return pSupp; +} + +static void Ivy_FastMapPrint( Ivy_Man_t * pAig, int Delay, int Area, int Time, char * pStr ); +static int Ivy_FastMapDelay( Ivy_Man_t * pAig ); +static int Ivy_FastMapArea( Ivy_Man_t * pAig ); +static void Ivy_FastMapNode( Ivy_Man_t * pAig, Ivy_Obj_t * pObj, int nLimit ); +static void Ivy_FastMapNodeArea( Ivy_Man_t * pAig, Ivy_Obj_t * pObj, int nLimit ); +static int Ivy_FastMapMerge( Ivy_Supp_t * pSupp0, Ivy_Supp_t * pSupp1, Ivy_Supp_t * pSupp, int nLimit ); +static void Ivy_FastMapRequired( Ivy_Man_t * pAig, int Delay, int fSetInter ); +static void Ivy_FastMapRecover( Ivy_Man_t * pAig, int nLimit ); +static int Ivy_FastMapNodeDelay( Ivy_Man_t * pAig, Ivy_Obj_t * pObj ); +static int Ivy_FastMapNodeAreaRefed( Ivy_Man_t * pAig, Ivy_Obj_t * pObj ); +static int Ivy_FastMapNodeAreaDerefed( Ivy_Man_t * pAig, Ivy_Obj_t * pObj ); +static void Ivy_FastMapNodeRecover( Ivy_Man_t * pAig, Ivy_Obj_t * pObj, int nLimit, Vec_Ptr_t * vFront, Vec_Ptr_t * vFrontOld ); +static int Ivy_FastMapNodeRef( Ivy_Man_t * pAig, Ivy_Obj_t * pObj ); +static int Ivy_FastMapNodeDeref( Ivy_Man_t * pAig, Ivy_Obj_t * pObj ); + + +extern int s_MappingTime; +extern int s_MappingMem; + + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs fast K-LUT mapping of the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FastMapPerform( Ivy_Man_t * pAig, int nLimit, int fRecovery, int fVerbose ) +{ + Ivy_SuppMan_t * pMan; + Ivy_Obj_t * pObj; + int i, Delay, Area, clk, clkTotal = clock(); + // start the memory for supports + pMan = ALLOC( Ivy_SuppMan_t, 1 ); + memset( pMan, 0, sizeof(Ivy_SuppMan_t) ); + pMan->nLimit = nLimit; + pMan->nObjs = Ivy_ManObjIdMax(pAig) + 1; + pMan->nSize = sizeof(Ivy_Supp_t) + nLimit * sizeof(int); + pMan->pMem = (char *)malloc( pMan->nObjs * pMan->nSize ); + memset( pMan->pMem, 0, pMan->nObjs * pMan->nSize ); + pMan->vLuts = Vec_VecAlloc( 100 ); + pAig->pData = pMan; +clk = clock(); + // set the PI mapping + Ivy_ObjSuppStart( pAig, Ivy_ManConst1(pAig) ); + Ivy_ManForEachPi( pAig, pObj, i ) + Ivy_ObjSuppStart( pAig, pObj ); + // iterate through all nodes in the topological order + Ivy_ManForEachNode( pAig, pObj, i ) + Ivy_FastMapNode( pAig, pObj, nLimit ); + // find the best arrival time and area + Delay = Ivy_FastMapDelay( pAig ); + Area = Ivy_FastMapArea(pAig); + if ( fVerbose ) + Ivy_FastMapPrint( pAig, Delay, Area, clock() - clk, "Delay oriented mapping: " ); + +// 2-1-2 (doing 2-1-2-1-2 improves 0.5%) + + if ( fRecovery ) + { +clk = clock(); + Ivy_FastMapRequired( pAig, Delay, 0 ); + // remap the nodes + Ivy_FastMapRecover( pAig, nLimit ); + Delay = Ivy_FastMapDelay( pAig ); + Area = Ivy_FastMapArea(pAig); + if ( fVerbose ) + Ivy_FastMapPrint( pAig, Delay, Area, clock() - clk, "Area recovery 2 : " ); + +clk = clock(); + Ivy_FastMapRequired( pAig, Delay, 0 ); + // iterate through all nodes in the topological order + Ivy_ManForEachNode( pAig, pObj, i ) + Ivy_FastMapNodeArea( pAig, pObj, nLimit ); + Delay = Ivy_FastMapDelay( pAig ); + Area = Ivy_FastMapArea(pAig); + if ( fVerbose ) + Ivy_FastMapPrint( pAig, Delay, Area, clock() - clk, "Area recovery 1 : " ); + +clk = clock(); + Ivy_FastMapRequired( pAig, Delay, 0 ); + // remap the nodes + Ivy_FastMapRecover( pAig, nLimit ); + Delay = Ivy_FastMapDelay( pAig ); + Area = Ivy_FastMapArea(pAig); + if ( fVerbose ) + Ivy_FastMapPrint( pAig, Delay, Area, clock() - clk, "Area recovery 2 : " ); + } + + + s_MappingTime = clock() - clkTotal; + s_MappingMem = pMan->nObjs * pMan->nSize; +/* + { + Vec_Ptr_t * vNodes; + vNodes = Vec_PtrAlloc( 100 ); + Vec_VecForEachEntry( pMan->vLuts, pObj, i, k ) + Vec_PtrPush( vNodes, pObj ); + Ivy_ManShow( pAig, 0, vNodes ); + Vec_PtrFree( vNodes ); + } +*/ +} + +/**Function************************************************************* + + Synopsis [Cleans memory used for decomposition.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FastMapStop( Ivy_Man_t * pAig ) +{ + Ivy_SuppMan_t * p = pAig->pData; + Vec_VecFree( p->vLuts ); + free( p->pMem ); + free( p ); + pAig->pData = NULL; +} + +/**Function************************************************************* + + Synopsis [Prints statistics.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FastMapPrint( Ivy_Man_t * pAig, int Delay, int Area, int Time, char * pStr ) +{ + printf( "%s : Delay = %3d. Area = %6d. ", pStr, Delay, Area ); + PRT( "Time", Time ); +} + +/**Function************************************************************* + + Synopsis [Computes delay after LUT mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FastMapDelay( Ivy_Man_t * pAig ) +{ + Ivy_Supp_t * pSupp; + Ivy_Obj_t * pObj; + int i, DelayMax = 0; + Ivy_ManForEachPo( pAig, pObj, i ) + { + pObj = Ivy_ObjFanin0(pObj); + if ( !Ivy_ObjIsNode(pObj) ) + continue; + pSupp = Ivy_ObjSupp( pAig, pObj ); + if ( DelayMax < pSupp->Delay ) + DelayMax = pSupp->Delay; + } + return DelayMax; +} + +/**Function************************************************************* + + Synopsis [Computes area after mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FastMapArea_rec( Ivy_Man_t * pAig, Ivy_Obj_t * pObj, Vec_Vec_t * vLuts ) +{ + Ivy_Supp_t * pSupp; + int i, Counter; + pSupp = Ivy_ObjSupp( pAig, pObj ); + // skip visited nodes and PIs + if ( pSupp->fMark || pSupp->nSize == 1 ) + return 0; + pSupp->fMark = 1; + // compute the area of this node + Counter = 0; + for ( i = 0; i < pSupp->nSize; i++ ) + Counter += Ivy_FastMapArea_rec( pAig, Ivy_ManObj(pAig, pSupp->pArray[i]), vLuts ); + // add the node to the array of LUTs + Vec_VecPush( vLuts, pSupp->Delay, pObj ); + return 1 + Counter; +} + +/**Function************************************************************* + + Synopsis [Computes area after mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FastMapArea( Ivy_Man_t * pAig ) +{ + Vec_Vec_t * vLuts; + Ivy_Obj_t * pObj; + int i, Counter = 0; + // get the array to store the nodes + vLuts = ((Ivy_SuppMan_t *)pAig->pData)->vLuts; + Vec_VecClear( vLuts ); + // explore starting from each node + Ivy_ManForEachPo( pAig, pObj, i ) + Counter += Ivy_FastMapArea_rec( pAig, Ivy_ObjFanin0(pObj), vLuts ); + // clean the marks + Ivy_ManForEachNode( pAig, pObj, i ) + Ivy_ObjSupp( pAig, pObj )->fMark = 0; + return Counter; +} + +/**Function************************************************************* + + Synopsis [Performs fast mapping for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Ivy_ObjIsNodeInt1( Ivy_Obj_t * pObj ) +{ + return Ivy_ObjIsNode(pObj) && Ivy_ObjRefs(pObj) == 1; +} + +/**Function************************************************************* + + Synopsis [Performs fast mapping for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Ivy_ObjIsNodeInt2( Ivy_Obj_t * pObj ) +{ + return Ivy_ObjIsNode(pObj) && Ivy_ObjRefs(pObj) <= 2; +} + +/**Function************************************************************* + + Synopsis [Performs fast mapping for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntSelectSort( int * pArray, int nSize ) +{ + int temp, i, j, best_i; + for ( i = 0; i < nSize-1; i++ ) + { + best_i = i; + for ( j = i+1; j < nSize; j++ ) + if ( pArray[j] < pArray[best_i] ) + best_i = j; + temp = pArray[i]; + pArray[i] = pArray[best_i]; + pArray[best_i] = temp; + } +} + +/**Function************************************************************* + + Synopsis [Performs fast mapping for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntRemoveDup( int * pArray, int nSize ) +{ + int i, k; + if ( nSize < 2 ) + return nSize; + for ( i = k = 1; i < nSize; i++ ) + if ( pArray[i] != pArray[i-1] ) + pArray[k++] = pArray[i]; + return k; +} + +/**Function************************************************************* + + Synopsis [Performs fast mapping for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FastMapNodeArea2( Ivy_Man_t * pAig, Ivy_Obj_t * pObj, int nLimit ) +{ + static int Store[32], StoreSize; + static char Supp0[16], Supp1[16]; + static Ivy_Supp_t * pTemp0 = (Ivy_Supp_t *)Supp0; + static Ivy_Supp_t * pTemp1 = (Ivy_Supp_t *)Supp1; + Ivy_Obj_t * pFanin0, * pFanin1; + Ivy_Supp_t * pSupp0, * pSupp1, * pSupp; + int RetValue, DelayOld; + assert( nLimit <= 32 ); + assert( Ivy_ObjIsNode(pObj) ); + // get the fanins + pFanin0 = Ivy_ObjFanin0(pObj); + pFanin1 = Ivy_ObjFanin1(pObj); + // get the supports + pSupp0 = Ivy_ObjSupp( pAig, pFanin0 ); + pSupp1 = Ivy_ObjSupp( pAig, pFanin1 ); + pSupp = Ivy_ObjSupp( pAig, pObj ); + assert( pSupp->fMark == 0 ); + // get the old delay of the node + DelayOld = Ivy_FastMapNodeDelay(pAig, pObj); + assert( DelayOld <= pSupp->DelayR ); + // copy the current cut + memcpy( Store, pSupp->pArray, sizeof(int) * pSupp->nSize ); + StoreSize = pSupp->nSize; + // get the fanin support + if ( Ivy_ObjRefs(pFanin0) > 1 && pSupp0->Delay < pSupp->DelayR ) + { + pSupp0 = pTemp0; + pSupp0->nSize = 1; + pSupp0->pArray[0] = Ivy_ObjFaninId0(pObj); + } + // get the fanin support + if ( Ivy_ObjRefs(pFanin1) > 1 && pSupp1->Delay < pSupp->DelayR ) + { + pSupp1 = pTemp1; + pSupp1->nSize = 1; + pSupp1->pArray[0] = Ivy_ObjFaninId1(pObj); + } + // merge the cuts + if ( pSupp0->nSize < pSupp1->nSize ) + RetValue = Ivy_FastMapMerge( pSupp1, pSupp0, pSupp, nLimit ); + else + RetValue = Ivy_FastMapMerge( pSupp0, pSupp1, pSupp, nLimit ); + if ( !RetValue ) + { + pSupp->nSize = 2; + pSupp->pArray[0] = Ivy_ObjFaninId0(pObj); + pSupp->pArray[1] = Ivy_ObjFaninId1(pObj); + } + // check the resulting delay + pSupp->Delay = Ivy_FastMapNodeDelay(pAig, pObj); + if ( pSupp->Delay > pSupp->DelayR ) + { + pSupp->nSize = StoreSize; + memcpy( pSupp->pArray, Store, sizeof(int) * pSupp->nSize ); + pSupp->Delay = DelayOld; + } +} + +/**Function************************************************************* + + Synopsis [Performs fast mapping for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FastMapNodeArea( Ivy_Man_t * pAig, Ivy_Obj_t * pObj, int nLimit ) +{ + static int Store[32], StoreSize; + static char Supp0[16], Supp1[16]; + static Ivy_Supp_t * pTemp0 = (Ivy_Supp_t *)Supp0; + static Ivy_Supp_t * pTemp1 = (Ivy_Supp_t *)Supp1; + Ivy_Obj_t * pFanin0, * pFanin1; + Ivy_Supp_t * pSupp0, * pSupp1, * pSupp; + int RetValue, DelayOld, RefsOld; + int AreaBef, AreaAft; + assert( nLimit <= 32 ); + assert( Ivy_ObjIsNode(pObj) ); + // get the fanins + pFanin0 = Ivy_ObjFanin0(pObj); + pFanin1 = Ivy_ObjFanin1(pObj); + // get the supports + pSupp0 = Ivy_ObjSupp( pAig, pFanin0 ); + pSupp1 = Ivy_ObjSupp( pAig, pFanin1 ); + pSupp = Ivy_ObjSupp( pAig, pObj ); + assert( pSupp->fMark == 0 ); + + // get the area + if ( pSupp->nRefs == 0 ) + AreaBef = Ivy_FastMapNodeAreaDerefed( pAig, pObj ); + else + AreaBef = Ivy_FastMapNodeAreaRefed( pAig, pObj ); +// if ( AreaBef == 1 ) +// return; + + // deref the cut if the node is refed + if ( pSupp->nRefs != 0 ) + Ivy_FastMapNodeDeref( pAig, pObj ); + + // get the old delay of the node + DelayOld = Ivy_FastMapNodeDelay(pAig, pObj); + assert( DelayOld <= pSupp->DelayR ); + // copy the current cut + memcpy( Store, pSupp->pArray, sizeof(int) * pSupp->nSize ); + StoreSize = pSupp->nSize; + // get the fanin support + if ( Ivy_ObjRefs(pFanin0) > 2 && pSupp0->Delay < pSupp->DelayR ) +// if ( pSupp0->nRefs > 0 && pSupp0->Delay < pSupp->DelayR ) // this leads to 2% worse results + { + pSupp0 = pTemp0; + pSupp0->nSize = 1; + pSupp0->pArray[0] = Ivy_ObjFaninId0(pObj); + } + // get the fanin support + if ( Ivy_ObjRefs(pFanin1) > 2 && pSupp1->Delay < pSupp->DelayR ) +// if ( pSupp1->nRefs > 0 && pSupp1->Delay < pSupp->DelayR ) + { + pSupp1 = pTemp1; + pSupp1->nSize = 1; + pSupp1->pArray[0] = Ivy_ObjFaninId1(pObj); + } + // merge the cuts + if ( pSupp0->nSize < pSupp1->nSize ) + RetValue = Ivy_FastMapMerge( pSupp1, pSupp0, pSupp, nLimit ); + else + RetValue = Ivy_FastMapMerge( pSupp0, pSupp1, pSupp, nLimit ); + if ( !RetValue ) + { + pSupp->nSize = 2; + pSupp->pArray[0] = Ivy_ObjFaninId0(pObj); + pSupp->pArray[1] = Ivy_ObjFaninId1(pObj); + } + + // check the resulting delay + pSupp->Delay = Ivy_FastMapNodeDelay(pAig, pObj); + + RefsOld = pSupp->nRefs; pSupp->nRefs = 0; + AreaAft = Ivy_FastMapNodeAreaDerefed( pAig, pObj ); + pSupp->nRefs = RefsOld; + + if ( AreaAft > AreaBef || pSupp->Delay > pSupp->DelayR ) +// if ( pSupp->Delay > pSupp->DelayR ) + { + pSupp->nSize = StoreSize; + memcpy( pSupp->pArray, Store, sizeof(int) * pSupp->nSize ); + pSupp->Delay = DelayOld; +// printf( "-" ); + } +// else +// printf( "+" ); + + if ( pSupp->nRefs != 0 ) + Ivy_FastMapNodeRef( pAig, pObj ); +} + +/**Function************************************************************* + + Synopsis [Performs fast mapping for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FastMapNode( Ivy_Man_t * pAig, Ivy_Obj_t * pObj, int nLimit ) +{ + Ivy_Supp_t * pSupp0, * pSupp1, * pSupp; + int fFaninParam = 2; + int RetValue; + assert( Ivy_ObjIsNode(pObj) ); + // get the supports + pSupp0 = Ivy_ObjSupp( pAig, Ivy_ObjFanin0(pObj) ); + pSupp1 = Ivy_ObjSupp( pAig, Ivy_ObjFanin1(pObj) ); + pSupp = Ivy_ObjSupp( pAig, pObj ); + pSupp->fMark = 0; + // get the delays + if ( pSupp0->Delay == pSupp1->Delay ) + pSupp->Delay = (pSupp0->Delay == 0) ? pSupp0->Delay + 1: pSupp0->Delay; + else if ( pSupp0->Delay > pSupp1->Delay ) + { + pSupp->Delay = pSupp0->Delay; + pSupp1 = Ivy_ObjSupp( pAig, Ivy_ManConst1(pAig) ); + pSupp1->pArray[0] = Ivy_ObjFaninId1(pObj); + } + else // if ( pSupp0->Delay < pSupp1->Delay ) + { + pSupp->Delay = pSupp1->Delay; + pSupp0 = Ivy_ObjSupp( pAig, Ivy_ManConst1(pAig) ); + pSupp0->pArray[0] = Ivy_ObjFaninId0(pObj); + } + // merge the cuts + if ( pSupp0->nSize < pSupp1->nSize ) + RetValue = Ivy_FastMapMerge( pSupp1, pSupp0, pSupp, nLimit ); + else + RetValue = Ivy_FastMapMerge( pSupp0, pSupp1, pSupp, nLimit ); + if ( !RetValue ) + { + pSupp->Delay++; + if ( fFaninParam == 2 ) + { + pSupp->nSize = 2; + pSupp->pArray[0] = Ivy_ObjFaninId0(pObj); + pSupp->pArray[1] = Ivy_ObjFaninId1(pObj); + } + else if ( fFaninParam == 3 ) + { + Ivy_Obj_t * pFanin0, * pFanin1, * pFaninA, * pFaninB; + pFanin0 = Ivy_ObjFanin0(pObj); + pFanin1 = Ivy_ObjFanin1(pObj); + pSupp->nSize = 0; + // process the first fanin + if ( Ivy_ObjIsNodeInt1(pFanin0) ) + { + pFaninA = Ivy_ObjFanin0(pFanin0); + pFaninB = Ivy_ObjFanin1(pFanin0); + if ( Ivy_ObjIsNodeInt1(pFaninA) && Ivy_ObjIsNodeInt1(pFaninB) ) + pSupp->pArray[pSupp->nSize++] = Ivy_ObjId(pFanin0); + else + { + pSupp->pArray[pSupp->nSize++] = Ivy_ObjId(pFaninA); + pSupp->pArray[pSupp->nSize++] = Ivy_ObjId(pFaninB); + } + } + else + pSupp->pArray[pSupp->nSize++] = Ivy_ObjId(pFanin0); + // process the second fanin + if ( Ivy_ObjIsNodeInt1(pFanin1) ) + { + pFaninA = Ivy_ObjFanin0(pFanin1); + pFaninB = Ivy_ObjFanin1(pFanin1); + if ( Ivy_ObjIsNodeInt1(pFaninA) && Ivy_ObjIsNodeInt1(pFaninB) ) + pSupp->pArray[pSupp->nSize++] = Ivy_ObjId(pFanin1); + else + { + pSupp->pArray[pSupp->nSize++] = Ivy_ObjId(pFaninA); + pSupp->pArray[pSupp->nSize++] = Ivy_ObjId(pFaninB); + } + } + else + pSupp->pArray[pSupp->nSize++] = Ivy_ObjId(pFanin1); + // sort the fanins + Vec_IntSelectSort( pSupp->pArray, pSupp->nSize ); + pSupp->nSize = Vec_IntRemoveDup( pSupp->pArray, pSupp->nSize ); + assert( pSupp->pArray[0] < pSupp->pArray[1] ); + } + else if ( fFaninParam == 4 ) + { + Ivy_Obj_t * pFanin0, * pFanin1, * pFaninA, * pFaninB; + pFanin0 = Ivy_ObjFanin0(pObj); + pFanin1 = Ivy_ObjFanin1(pObj); + pSupp->nSize = 0; + // consider the case when exactly one of them is internal + if ( Ivy_ObjIsNodeInt1(pFanin0) ^ Ivy_ObjIsNodeInt1(pFanin1) ) + { + pSupp0 = Ivy_ObjSupp( pAig, Ivy_ObjFanin0(pObj) ); + pSupp1 = Ivy_ObjSupp( pAig, Ivy_ObjFanin1(pObj) ); + if ( Ivy_ObjIsNodeInt1(pFanin0) && pSupp0->nSize < nLimit ) + { + pSupp->Delay = IVY_MAX( pSupp0->Delay, pSupp1->Delay + 1 ); + pSupp1 = Ivy_ObjSupp( pAig, Ivy_ManConst1(pAig) ); + pSupp1->pArray[0] = Ivy_ObjId(pFanin1); + // merge the cuts + RetValue = Ivy_FastMapMerge( pSupp0, pSupp1, pSupp, nLimit ); + assert( RetValue ); + assert( pSupp->nSize > 1 ); + return; + } + if ( Ivy_ObjIsNodeInt1(pFanin1) && pSupp1->nSize < nLimit ) + { + pSupp->Delay = IVY_MAX( pSupp1->Delay, pSupp0->Delay + 1 ); + pSupp0 = Ivy_ObjSupp( pAig, Ivy_ManConst1(pAig) ); + pSupp0->pArray[0] = Ivy_ObjId(pFanin0); + // merge the cuts + RetValue = Ivy_FastMapMerge( pSupp1, pSupp0, pSupp, nLimit ); + assert( RetValue ); + assert( pSupp->nSize > 1 ); + return; + } + } + // process the first fanin + if ( Ivy_ObjIsNodeInt1(pFanin0) ) + { + pFaninA = Ivy_ObjFanin0(pFanin0); + pFaninB = Ivy_ObjFanin1(pFanin0); + if ( Ivy_ObjIsNodeInt1(pFaninA) && Ivy_ObjIsNodeInt1(pFaninB) ) + pSupp->pArray[pSupp->nSize++] = Ivy_ObjId(pFanin0); + else + { + pSupp->pArray[pSupp->nSize++] = Ivy_ObjId(pFaninA); + pSupp->pArray[pSupp->nSize++] = Ivy_ObjId(pFaninB); + } + } + else + pSupp->pArray[pSupp->nSize++] = Ivy_ObjId(pFanin0); + // process the second fanin + if ( Ivy_ObjIsNodeInt1(pFanin1) ) + { + pFaninA = Ivy_ObjFanin0(pFanin1); + pFaninB = Ivy_ObjFanin1(pFanin1); + if ( Ivy_ObjIsNodeInt1(pFaninA) && Ivy_ObjIsNodeInt1(pFaninB) ) + pSupp->pArray[pSupp->nSize++] = Ivy_ObjId(pFanin1); + else + { + pSupp->pArray[pSupp->nSize++] = Ivy_ObjId(pFaninA); + pSupp->pArray[pSupp->nSize++] = Ivy_ObjId(pFaninB); + } + } + else + pSupp->pArray[pSupp->nSize++] = Ivy_ObjId(pFanin1); + // sort the fanins + Vec_IntSelectSort( pSupp->pArray, pSupp->nSize ); + pSupp->nSize = Vec_IntRemoveDup( pSupp->pArray, pSupp->nSize ); + assert( pSupp->pArray[0] < pSupp->pArray[1] ); + assert( pSupp->nSize > 1 ); + } + } + assert( pSupp->Delay > 0 ); +} + +/**Function************************************************************* + + Synopsis [Merges two supports] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FastMapMerge( Ivy_Supp_t * pSupp0, Ivy_Supp_t * pSupp1, Ivy_Supp_t * pSupp, int nLimit ) +{ + int i, k, c; + assert( pSupp0->nSize >= pSupp1->nSize ); + // the case of the largest cut sizes + if ( pSupp0->nSize == nLimit && pSupp1->nSize == nLimit ) + { + for ( i = 0; i < pSupp0->nSize; i++ ) + if ( pSupp0->pArray[i] != pSupp1->pArray[i] ) + return 0; + for ( i = 0; i < pSupp0->nSize; i++ ) + pSupp->pArray[i] = pSupp0->pArray[i]; + pSupp->nSize = pSupp0->nSize; + return 1; + } + // the case when one of the cuts is the largest + if ( pSupp0->nSize == nLimit ) + { + for ( i = 0; i < pSupp1->nSize; i++ ) + { + for ( k = pSupp0->nSize - 1; k >= 0; k-- ) + if ( pSupp0->pArray[k] == pSupp1->pArray[i] ) + break; + if ( k == -1 ) // did not find + return 0; + } + for ( i = 0; i < pSupp0->nSize; i++ ) + pSupp->pArray[i] = pSupp0->pArray[i]; + pSupp->nSize = pSupp0->nSize; + return 1; + } + + // compare two cuts with different numbers + i = k = 0; + for ( c = 0; c < nLimit; c++ ) + { + if ( k == pSupp1->nSize ) + { + if ( i == pSupp0->nSize ) + { + pSupp->nSize = c; + return 1; + } + pSupp->pArray[c] = pSupp0->pArray[i++]; + continue; + } + if ( i == pSupp0->nSize ) + { + if ( k == pSupp1->nSize ) + { + pSupp->nSize = c; + return 1; + } + pSupp->pArray[c] = pSupp1->pArray[k++]; + continue; + } + if ( pSupp0->pArray[i] < pSupp1->pArray[k] ) + { + pSupp->pArray[c] = pSupp0->pArray[i++]; + continue; + } + if ( pSupp0->pArray[i] > pSupp1->pArray[k] ) + { + pSupp->pArray[c] = pSupp1->pArray[k++]; + continue; + } + pSupp->pArray[c] = pSupp0->pArray[i++]; + k++; + } + if ( i < pSupp0->nSize || k < pSupp1->nSize ) + return 0; + pSupp->nSize = c; + return 1; +} + +/**Function************************************************************* + + Synopsis [Creates integer vector with the support of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FastMapReadSupp( Ivy_Man_t * pAig, Ivy_Obj_t * pObj, Vec_Int_t * vLeaves ) +{ + Ivy_Supp_t * pSupp; + pSupp = Ivy_ObjSupp( pAig, pObj ); + vLeaves->nCap = 8; + vLeaves->nSize = pSupp->nSize; + vLeaves->pArray = pSupp->pArray; +} + +/**Function************************************************************* + + Synopsis [Sets the required times of the intermediate nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FastMapRequired_rec( Ivy_Man_t * pAig, Ivy_Obj_t * pObj, Ivy_Obj_t * pRoot, int DelayR ) +{ + Ivy_Supp_t * pSupp; + pSupp = Ivy_ObjSupp( pAig, pObj ); + if ( pObj != pRoot && (pSupp->nRefs > 0 || Ivy_ObjIsCi(pObj)) ) + return; + Ivy_FastMapRequired_rec( pAig, Ivy_ObjFanin0(pObj), pRoot, DelayR ); + Ivy_FastMapRequired_rec( pAig, Ivy_ObjFanin1(pObj), pRoot, DelayR ); +// assert( pObj == pRoot || pSupp->DelayR == IVY_INFINITY ); + pSupp->DelayR = DelayR; +} + +/**Function************************************************************* + + Synopsis [Computes the required times for each node.] + + Description [Sets reference counters for each node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FastMapRequired( Ivy_Man_t * pAig, int Delay, int fSetInter ) +{ + Vec_Vec_t * vLuts; + Vec_Ptr_t * vNodes; + Ivy_Obj_t * pObj; + Ivy_Supp_t * pSupp, * pSuppF; + int i, k, c; + // clean the required times + Ivy_ManForEachPi( pAig, pObj, i ) + { + pSupp = Ivy_ObjSupp( pAig, pObj ); + pSupp->DelayR = IVY_INFINITY; + pSupp->nRefs = 0; + } + Ivy_ManForEachNode( pAig, pObj, i ) + { + pSupp = Ivy_ObjSupp( pAig, pObj ); + pSupp->DelayR = IVY_INFINITY; + pSupp->nRefs = 0; + } + // set the required times of the POs + Ivy_ManForEachPo( pAig, pObj, i ) + { + pSupp = Ivy_ObjSupp( pAig, Ivy_ObjFanin0(pObj) ); + pSupp->DelayR = Delay; + pSupp->nRefs++; + } + // get the levelized nodes used in the mapping + vLuts = ((Ivy_SuppMan_t *)pAig->pData)->vLuts; + // propagate the required times + Vec_VecForEachLevelReverse( vLuts, vNodes, i ) + Vec_PtrForEachEntry( vNodes, pObj, k ) + { + pSupp = Ivy_ObjSupp( pAig, pObj ); + assert( pSupp->nRefs > 0 ); + for ( c = 0; c < pSupp->nSize; c++ ) + { + pSuppF = Ivy_ObjSupp( pAig, Ivy_ManObj(pAig, pSupp->pArray[c]) ); + pSuppF->DelayR = IVY_MIN( pSuppF->DelayR, pSupp->DelayR - 1 ); + pSuppF->nRefs++; + } + } +/* + // print out some of the required times + Ivy_ManForEachPi( pAig, pObj, i ) + { + pSupp = Ivy_ObjSupp( pAig, pObj ); + printf( "%d ", pSupp->DelayR ); + } + printf( "\n" ); +*/ + + if ( fSetInter ) + { + // set the required times of the intermediate nodes + Vec_VecForEachLevelReverse( vLuts, vNodes, i ) + Vec_PtrForEachEntry( vNodes, pObj, k ) + { + pSupp = Ivy_ObjSupp( pAig, pObj ); + Ivy_FastMapRequired_rec( pAig, pObj, pObj, pSupp->DelayR ); + } + // make sure that all required times are assigned + Ivy_ManForEachNode( pAig, pObj, i ) + { + pSupp = Ivy_ObjSupp( pAig, pObj ); + assert( pSupp->DelayR < IVY_INFINITY ); + } + } +} + +/**Function************************************************************* + + Synopsis [Performs area recovery for each node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FastMapRecover( Ivy_Man_t * pAig, int nLimit ) +{ + Vec_Ptr_t * vFront, * vFrontOld; + Ivy_Obj_t * pObj; + int i; + vFront = Vec_PtrAlloc( nLimit ); + vFrontOld = Vec_PtrAlloc( nLimit ); + Ivy_ManCleanTravId( pAig ); + // iterate through all nodes in the topological order + Ivy_ManForEachNode( pAig, pObj, i ) + Ivy_FastMapNodeRecover( pAig, pObj, nLimit, vFront, vFrontOld ); + Vec_PtrFree( vFrontOld ); + Vec_PtrFree( vFront ); +} + +/**Function************************************************************* + + Synopsis [Computes the delay of the cut rooted at this node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FastMapNodeDelay( Ivy_Man_t * pAig, Ivy_Obj_t * pObj ) +{ + Ivy_Supp_t * pSupp, * pSuppF; + int c, Delay = 0; + pSupp = Ivy_ObjSupp( pAig, pObj ); + for ( c = 0; c < pSupp->nSize; c++ ) + { + pSuppF = Ivy_ObjSupp( pAig, Ivy_ManObj(pAig, pSupp->pArray[c]) ); + Delay = IVY_MAX( Delay, pSuppF->Delay ); + } + return 1 + Delay; +} + + +/**function************************************************************* + + synopsis [References the cut.] + + description [This procedure is similar to the procedure NodeReclaim.] + + sideeffects [] + + seealso [] + +***********************************************************************/ +int Ivy_FastMapNodeRef( Ivy_Man_t * pAig, Ivy_Obj_t * pObj ) +{ + Ivy_Supp_t * pSupp, * pSuppF; + Ivy_Obj_t * pNodeChild; + int aArea, i; + // start the area of this cut + aArea = 1; + // go through the children + pSupp = Ivy_ObjSupp( pAig, pObj ); + assert( pSupp->nSize > 1 ); + for ( i = 0; i < pSupp->nSize; i++ ) + { + pNodeChild = Ivy_ManObj(pAig, pSupp->pArray[i]); + pSuppF = Ivy_ObjSupp( pAig, pNodeChild ); + assert( pSuppF->nRefs >= 0 ); + if ( pSuppF->nRefs++ > 0 ) + continue; + if ( pSuppF->nSize == 1 ) + continue; + aArea += Ivy_FastMapNodeRef( pAig, pNodeChild ); + } + return aArea; +} + +/**function************************************************************* + + synopsis [Dereferences the cut.] + + description [This procedure is similar to the procedure NodeRecusiveDeref.] + + sideeffects [] + + seealso [] + +***********************************************************************/ +int Ivy_FastMapNodeDeref( Ivy_Man_t * pAig, Ivy_Obj_t * pObj ) +{ + Ivy_Supp_t * pSupp, * pSuppF; + Ivy_Obj_t * pNodeChild; + int aArea, i; + // start the area of this cut + aArea = 1; + // go through the children + pSupp = Ivy_ObjSupp( pAig, pObj ); + assert( pSupp->nSize > 1 ); + for ( i = 0; i < pSupp->nSize; i++ ) + { + pNodeChild = Ivy_ManObj(pAig, pSupp->pArray[i]); + pSuppF = Ivy_ObjSupp( pAig, pNodeChild ); + assert( pSuppF->nRefs > 0 ); + if ( --pSuppF->nRefs > 0 ) + continue; + if ( pSuppF->nSize == 1 ) + continue; + aArea += Ivy_FastMapNodeDeref( pAig, pNodeChild ); + } + return aArea; +} + +/**function************************************************************* + + synopsis [Computes the exact area associated with the cut.] + + description [] + + sideeffects [] + + seealso [] + +***********************************************************************/ +int Ivy_FastMapNodeAreaRefed( Ivy_Man_t * pAig, Ivy_Obj_t * pObj ) +{ + Ivy_Supp_t * pSupp; + int aResult, aResult2; + if ( Ivy_ObjIsCi(pObj) ) + return 0; + assert( Ivy_ObjIsNode(pObj) ); + pSupp = Ivy_ObjSupp( pAig, pObj ); + assert( pSupp->nRefs > 0 ); + aResult = Ivy_FastMapNodeDeref( pAig, pObj ); + aResult2 = Ivy_FastMapNodeRef( pAig, pObj ); + assert( aResult == aResult2 ); + return aResult; +} + +/**function************************************************************* + + synopsis [Computes the exact area associated with the cut.] + + description [] + + sideeffects [] + + seealso [] + +***********************************************************************/ +int Ivy_FastMapNodeAreaDerefed( Ivy_Man_t * pAig, Ivy_Obj_t * pObj ) +{ + Ivy_Supp_t * pSupp; + int aResult, aResult2; + if ( Ivy_ObjIsCi(pObj) ) + return 0; + assert( Ivy_ObjIsNode(pObj) ); + pSupp = Ivy_ObjSupp( pAig, pObj ); + assert( pSupp->nRefs == 0 ); + aResult2 = Ivy_FastMapNodeRef( pAig, pObj ); + aResult = Ivy_FastMapNodeDeref( pAig, pObj ); + assert( aResult == aResult2 ); + return aResult; +} + + + + +/**Function************************************************************* + + Synopsis [Counts the number of nodes with no external fanout.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FastMapCutCost( Ivy_Man_t * pAig, Vec_Ptr_t * vFront ) +{ + Ivy_Supp_t * pSuppF; + Ivy_Obj_t * pFanin; + int i, Counter = 0; + Vec_PtrForEachEntry( vFront, pFanin, i ) + { + pSuppF = Ivy_ObjSupp( pAig, pFanin ); + if ( pSuppF->nRefs == 0 ) + Counter++; + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Performs area recovery for each node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FastMapMark_rec( Ivy_Man_t * pAig, Ivy_Obj_t * pObj ) +{ + if ( Ivy_ObjIsTravIdCurrent(pAig, pObj) ) + return; + assert( Ivy_ObjIsNode(pObj) ); + Ivy_FastMapMark_rec( pAig, Ivy_ObjFanin0(pObj) ); + Ivy_FastMapMark_rec( pAig, Ivy_ObjFanin1(pObj) ); + Ivy_ObjSetTravIdCurrent(pAig, pObj); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the number of fanins will grow.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FastMapNodeWillGrow( Ivy_Man_t * pAig, Ivy_Obj_t * pObj ) +{ + Ivy_Obj_t * pFanin0, * pFanin1; + assert( Ivy_ObjIsNode(pObj) ); + pFanin0 = Ivy_ObjFanin0(pObj); + pFanin1 = Ivy_ObjFanin1(pObj); + return !Ivy_ObjIsTravIdCurrent(pAig, pFanin0) && !Ivy_ObjIsTravIdCurrent(pAig, pFanin1); +} + +/**Function************************************************************* + + Synopsis [Returns the increase in the number of fanins with no external refs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FastMapNodeFaninCost( Ivy_Man_t * pAig, Ivy_Obj_t * pObj ) +{ + Ivy_Supp_t * pSuppF; + Ivy_Obj_t * pFanin; + int Counter = 0; + assert( Ivy_ObjIsNode(pObj) ); + // check if the node has external refs + pSuppF = Ivy_ObjSupp( pAig, pObj ); + if ( pSuppF->nRefs == 0 ) + Counter--; + // increment the number of fanins without external refs + pFanin = Ivy_ObjFanin0(pObj); + pSuppF = Ivy_ObjSupp( pAig, pFanin ); + if ( !Ivy_ObjIsTravIdCurrent(pAig, pFanin) && pSuppF->nRefs == 0 ) + Counter++; + // increment the number of fanins without external refs + pFanin = Ivy_ObjFanin1(pObj); + pSuppF = Ivy_ObjSupp( pAig, pFanin ); + if ( !Ivy_ObjIsTravIdCurrent(pAig, pFanin) && pSuppF->nRefs == 0 ) + Counter++; + return Counter; +} + +/**Function************************************************************* + + Synopsis [Updates the frontier.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FastMapNodeFaninUpdate( Ivy_Man_t * pAig, Ivy_Obj_t * pObj, Vec_Ptr_t * vFront ) +{ + Ivy_Obj_t * pFanin; + assert( Ivy_ObjIsNode(pObj) ); + Vec_PtrRemove( vFront, pObj ); + pFanin = Ivy_ObjFanin0(pObj); + if ( !Ivy_ObjIsTravIdCurrent(pAig, pFanin) ) + { + Ivy_ObjSetTravIdCurrent(pAig, pFanin); + Vec_PtrPush( vFront, pFanin ); + } + pFanin = Ivy_ObjFanin1(pObj); + if ( !Ivy_ObjIsTravIdCurrent(pAig, pFanin) ) + { + Ivy_ObjSetTravIdCurrent(pAig, pFanin); + Vec_PtrPush( vFront, pFanin ); + } +} + +/**Function************************************************************* + + Synopsis [Compacts the number of external refs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FastMapNodeFaninCompact0( Ivy_Man_t * pAig, Ivy_Obj_t * pObj, int nLimit, Vec_Ptr_t * vFront ) +{ + Ivy_Obj_t * pFanin; + int i; + Vec_PtrForEachEntry( vFront, pFanin, i ) + { + if ( Ivy_ObjIsCi(pFanin) ) + continue; + if ( Ivy_FastMapNodeWillGrow(pAig, pFanin) ) + continue; + if ( Ivy_FastMapNodeFaninCost(pAig, pFanin) <= 0 ) + { + Ivy_FastMapNodeFaninUpdate( pAig, pFanin, vFront ); + return 1; + } + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Compacts the number of external refs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FastMapNodeFaninCompact1( Ivy_Man_t * pAig, Ivy_Obj_t * pObj, int nLimit, Vec_Ptr_t * vFront ) +{ + Ivy_Obj_t * pFanin; + int i; + Vec_PtrForEachEntry( vFront, pFanin, i ) + { + if ( Ivy_ObjIsCi(pFanin) ) + continue; + if ( Ivy_FastMapNodeFaninCost(pAig, pFanin) < 0 ) + { + Ivy_FastMapNodeFaninUpdate( pAig, pFanin, vFront ); + return 1; + } + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Compacts the number of external refs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FastMapNodeFaninCompact2( Ivy_Man_t * pAig, Ivy_Obj_t * pObj, int nLimit, Vec_Ptr_t * vFront ) +{ + Ivy_Obj_t * pFanin; + int i; + Vec_PtrForEachEntry( vFront, pFanin, i ) + { + if ( Ivy_ObjIsCi(pFanin) ) + continue; + if ( Ivy_FastMapNodeFaninCost(pAig, pFanin) <= 0 ) + { + Ivy_FastMapNodeFaninUpdate( pAig, pFanin, vFront ); + return 1; + } + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Compacts the number of external refs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FastMapNodeFaninCompact_int( Ivy_Man_t * pAig, Ivy_Obj_t * pObj, int nLimit, Vec_Ptr_t * vFront ) +{ + if ( Ivy_FastMapNodeFaninCompact0(pAig, pObj, nLimit, vFront) ) + return 1; + if ( Vec_PtrSize(vFront) < nLimit && Ivy_FastMapNodeFaninCompact1(pAig, pObj, nLimit, vFront) ) + return 1; + if ( Vec_PtrSize(vFront) < nLimit && Ivy_FastMapNodeFaninCompact2(pAig, pObj, nLimit, vFront) ) + return 1; + assert( Vec_PtrSize(vFront) <= nLimit ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Compacts the number of external refs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FastMapNodeFaninCompact( Ivy_Man_t * pAig, Ivy_Obj_t * pObj, int nLimit, Vec_Ptr_t * vFront ) +{ + while ( Ivy_FastMapNodeFaninCompact_int( pAig, pObj, nLimit, vFront ) ); +} + +/**Function************************************************************* + + Synopsis [Prepares node mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FastMapNodePrepare( Ivy_Man_t * pAig, Ivy_Obj_t * pObj, int nLimit, Vec_Ptr_t * vFront, Vec_Ptr_t * vFrontOld ) +{ + Ivy_Supp_t * pSupp; + Ivy_Obj_t * pFanin; + int i; + pSupp = Ivy_ObjSupp( pAig, pObj ); + // expand the cut downwards from the given place + Vec_PtrClear( vFront ); + Vec_PtrClear( vFrontOld ); + Ivy_ManIncrementTravId( pAig ); + for ( i = 0; i < pSupp->nSize; i++ ) + { + pFanin = Ivy_ManObj(pAig, pSupp->pArray[i]); + Vec_PtrPush( vFront, pFanin ); + Vec_PtrPush( vFrontOld, pFanin ); + Ivy_ObjSetTravIdCurrent( pAig, pFanin ); + } + // mark the nodes in the cone + Ivy_FastMapMark_rec( pAig, pObj ); +} + +/**Function************************************************************* + + Synopsis [Updates the frontier.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FastMapNodeUpdate( Ivy_Man_t * pAig, Ivy_Obj_t * pObj, Vec_Ptr_t * vFront ) +{ + Ivy_Supp_t * pSupp; + Ivy_Obj_t * pFanin; + int i; + pSupp = Ivy_ObjSupp( pAig, pObj ); + // deref node's cut + Ivy_FastMapNodeDeref( pAig, pObj ); + // update the node's cut + pSupp->nSize = Vec_PtrSize(vFront); + Vec_PtrForEachEntry( vFront, pFanin, i ) + pSupp->pArray[i] = pFanin->Id; + // ref the new cut + Ivy_FastMapNodeRef( pAig, pObj ); +} + +/**Function************************************************************* + + Synopsis [Performs area recovery for each node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FastMapNodeRecover2( Ivy_Man_t * pAig, Ivy_Obj_t * pObj, int nLimit, Vec_Ptr_t * vFront, Vec_Ptr_t * vFrontOld ) +{ + Ivy_Supp_t * pSupp; + int CostBef, CostAft; + int AreaBef, AreaAft; + pSupp = Ivy_ObjSupp( pAig, pObj ); +// if ( pSupp->nRefs == 0 ) +// return; + if ( pSupp->nRefs == 0 ) + AreaBef = Ivy_FastMapNodeAreaDerefed( pAig, pObj ); + else + AreaBef = Ivy_FastMapNodeAreaRefed( pAig, pObj ); + // get the area + if ( AreaBef == 1 ) + return; + + if ( pSupp->nRefs == 0 ) + { + pSupp->nRefs = 1000000; + Ivy_FastMapNodeRef( pAig, pObj ); + } + // the cut is non-trivial + Ivy_FastMapNodePrepare( pAig, pObj, nLimit, vFront, vFrontOld ); + // iteratively modify the cut + CostBef = Ivy_FastMapCutCost( pAig, vFront ); + Ivy_FastMapNodeFaninCompact( pAig, pObj, nLimit, vFront ); + CostAft = Ivy_FastMapCutCost( pAig, vFront ); + assert( CostBef >= CostAft ); + // update the node + Ivy_FastMapNodeUpdate( pAig, pObj, vFront ); + // get the new area + AreaAft = Ivy_FastMapNodeAreaRefed( pAig, pObj ); + if ( AreaAft > AreaBef ) + { + Ivy_FastMapNodeUpdate( pAig, pObj, vFrontOld ); + AreaAft = Ivy_FastMapNodeAreaRefed( pAig, pObj ); + assert( AreaAft == AreaBef ); + } + if ( pSupp->nRefs == 1000000 ) + { + pSupp->nRefs = 0; + Ivy_FastMapNodeDeref( pAig, pObj ); + } +} + +/**Function************************************************************* + + Synopsis [Performs area recovery for each node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FastMapNodeRecover( Ivy_Man_t * pAig, Ivy_Obj_t * pObj, int nLimit, Vec_Ptr_t * vFront, Vec_Ptr_t * vFrontOld ) +{ + Ivy_Supp_t * pSupp; + int CostBef, CostAft; + int AreaBef, AreaAft; + int DelayOld; + pSupp = Ivy_ObjSupp( pAig, pObj ); + DelayOld = pSupp->Delay = Ivy_FastMapNodeDelay( pAig, pObj ); + assert( pSupp->Delay <= pSupp->DelayR ); + if ( pSupp->nRefs == 0 ) + return; + // get the area + AreaBef = Ivy_FastMapNodeAreaRefed( pAig, pObj ); +// if ( AreaBef == 1 ) +// return; + if ( pObj->Id == 102 ) + { + int x = 0; + } + // the cut is non-trivial + Ivy_FastMapNodePrepare( pAig, pObj, nLimit, vFront, vFrontOld ); + // iteratively modify the cut + Ivy_FastMapNodeDeref( pAig, pObj ); + CostBef = Ivy_FastMapCutCost( pAig, vFront ); + Ivy_FastMapNodeFaninCompact( pAig, pObj, nLimit, vFront ); + CostAft = Ivy_FastMapCutCost( pAig, vFront ); + Ivy_FastMapNodeRef( pAig, pObj ); + assert( CostBef >= CostAft ); + // update the node + Ivy_FastMapNodeUpdate( pAig, pObj, vFront ); + pSupp->Delay = Ivy_FastMapNodeDelay( pAig, pObj ); + // get the new area + AreaAft = Ivy_FastMapNodeAreaRefed( pAig, pObj ); + if ( AreaAft > AreaBef || pSupp->Delay > pSupp->DelayR ) + { + Ivy_FastMapNodeUpdate( pAig, pObj, vFrontOld ); + AreaAft = Ivy_FastMapNodeAreaRefed( pAig, pObj ); + assert( AreaAft == AreaBef ); + pSupp->Delay = DelayOld; + } +} + +/**Function************************************************************* + + Synopsis [Performs area recovery for each node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FastMapNodeRecover4( Ivy_Man_t * pAig, Ivy_Obj_t * pObj, int nLimit, Vec_Ptr_t * vFront, Vec_Ptr_t * vFrontOld ) +{ + Ivy_Supp_t * pSupp; + int CostBef, CostAft; + int AreaBef, AreaAft; + int DelayOld; + pSupp = Ivy_ObjSupp( pAig, pObj ); + DelayOld = pSupp->Delay = Ivy_FastMapNodeDelay( pAig, pObj ); + assert( pSupp->Delay <= pSupp->DelayR ); +// if ( pSupp->nRefs == 0 ) +// return; +// AreaBef = Ivy_FastMapNodeAreaRefed( pAig, pObj ); + // get the area + if ( pSupp->nRefs == 0 ) + AreaBef = Ivy_FastMapNodeAreaDerefed( pAig, pObj ); + else + AreaBef = Ivy_FastMapNodeAreaRefed( pAig, pObj ); + if ( AreaBef == 1 ) + return; + + if ( pSupp->nRefs == 0 ) + { + pSupp->nRefs = 1000000; + Ivy_FastMapNodeRef( pAig, pObj ); + } + // the cut is non-trivial + Ivy_FastMapNodePrepare( pAig, pObj, nLimit, vFront, vFrontOld ); + // iteratively modify the cut + CostBef = Ivy_FastMapCutCost( pAig, vFront ); + Ivy_FastMapNodeFaninCompact( pAig, pObj, nLimit, vFront ); + CostAft = Ivy_FastMapCutCost( pAig, vFront ); + assert( CostBef >= CostAft ); + // update the node + Ivy_FastMapNodeUpdate( pAig, pObj, vFront ); + pSupp->Delay = Ivy_FastMapNodeDelay( pAig, pObj ); + // get the new area + AreaAft = Ivy_FastMapNodeAreaRefed( pAig, pObj ); + if ( AreaAft > AreaBef || pSupp->Delay > pSupp->DelayR ) + { + Ivy_FastMapNodeUpdate( pAig, pObj, vFrontOld ); + AreaAft = Ivy_FastMapNodeAreaRefed( pAig, pObj ); + assert( AreaAft == AreaBef ); + pSupp->Delay = DelayOld; + } + if ( pSupp->nRefs == 1000000 ) + { + pSupp->nRefs = 0; + Ivy_FastMapNodeDeref( pAig, pObj ); + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyFraig.c b/abc_with_bb_support/src/aig/ivy/ivyFraig.c new file mode 100644 index 000000000..6e78acabf --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyFraig.c @@ -0,0 +1,2756 @@ +/**CFile**************************************************************** + + FileName [ivyFraig.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [Functional reduction of AIGs] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyFraig.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "satSolver.h" +#include "extra.h" +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Ivy_FraigMan_t_ Ivy_FraigMan_t; +typedef struct Ivy_FraigSim_t_ Ivy_FraigSim_t; +typedef struct Ivy_FraigList_t_ Ivy_FraigList_t; + +struct Ivy_FraigList_t_ +{ + Ivy_Obj_t * pHead; + Ivy_Obj_t * pTail; + int nItems; +}; + +struct Ivy_FraigSim_t_ +{ + int Type; + Ivy_FraigSim_t * pNext; + Ivy_FraigSim_t * pFanin0; + Ivy_FraigSim_t * pFanin1; + unsigned pData[0]; +}; + +struct Ivy_FraigMan_t_ +{ + // general info + Ivy_FraigParams_t * pParams; // various parameters + // temporary backtrack limits because "sint64" cannot be defined in Ivy_FraigParams_t ... + sint64 nBTLimitGlobal; // global limit on the number of backtracks + sint64 nInsLimitGlobal;// global limit on the number of clause inspects + // AIG manager + Ivy_Man_t * pManAig; // the starting AIG manager + Ivy_Man_t * pManFraig; // the final AIG manager + // simulation information + int nSimWords; // the number of words + char * pSimWords; // the simulation info + Ivy_FraigSim_t * pSimStart; // the list of simulation info for internal nodes + // counter example storage + int nPatWords; // the number of words in the counter example + unsigned * pPatWords; // the counter example + int * pPatScores; // the scores of each pattern + // equivalence classes + Ivy_FraigList_t lClasses; // equivalence classes + Ivy_FraigList_t lCand; // candidatates + int nPairs; // the number of pairs of nodes + // equivalence checking + sat_solver * pSat; // SAT solver + int nSatVars; // the number of variables currently used + Vec_Ptr_t * vPiVars; // the PIs of the cone used + // other + ProgressBar * pProgress; + // statistics + int nSimRounds; + int nNodesMiter; + int nClassesZero; + int nClassesBeg; + int nClassesEnd; + int nPairsBeg; + int nPairsEnd; + int nSatCalls; + int nSatCallsSat; + int nSatCallsUnsat; + int nSatProof; + int nSatFails; + int nSatFailsReal; + // runtime + int timeSim; + int timeTrav; + int timeSat; + int timeSatUnsat; + int timeSatSat; + int timeSatFail; + int timeRef; + int timeTotal; + int time1; + int time2; +}; + +typedef struct Prove_ParamsStruct_t_ Prove_Params_t; +struct Prove_ParamsStruct_t_ +{ + // general parameters + int fUseFraiging; // enables fraiging + int fUseRewriting; // enables rewriting + int fUseBdds; // enables BDD construction when other methods fail + int fVerbose; // prints verbose stats + // iterations + int nItersMax; // the number of iterations + // mitering + int nMiteringLimitStart; // starting mitering limit + float nMiteringLimitMulti; // multiplicative coefficient to increase the limit in each iteration + // rewriting + int nRewritingLimitStart; // the number of rewriting iterations + float nRewritingLimitMulti; // multiplicative coefficient to increase the limit in each iteration + // fraiging + int nFraigingLimitStart; // starting backtrack(conflict) limit + float nFraigingLimitMulti; // multiplicative coefficient to increase the limit in each iteration + // last-gasp BDD construction + int nBddSizeLimit; // the number of BDD nodes when construction is aborted + int fBddReorder; // enables dynamic BDD variable reordering + // last-gasp mitering + int nMiteringLimitLast; // final mitering limit + // global SAT solver limits + sint64 nTotalBacktrackLimit; // global limit on the number of backtracks + sint64 nTotalInspectLimit; // global limit on the number of clause inspects + // global resources applied + sint64 nTotalBacktracksMade; // the total number of backtracks made + sint64 nTotalInspectsMade; // the total number of inspects made +}; + +static inline Ivy_FraigSim_t * Ivy_ObjSim( Ivy_Obj_t * pObj ) { return (Ivy_FraigSim_t *)pObj->pFanout; } +static inline Ivy_Obj_t * Ivy_ObjClassNodeLast( Ivy_Obj_t * pObj ) { return pObj->pNextFan0; } +static inline Ivy_Obj_t * Ivy_ObjClassNodeRepr( Ivy_Obj_t * pObj ) { return pObj->pNextFan0; } +static inline Ivy_Obj_t * Ivy_ObjClassNodeNext( Ivy_Obj_t * pObj ) { return pObj->pNextFan1; } +static inline Ivy_Obj_t * Ivy_ObjNodeHashNext( Ivy_Obj_t * pObj ) { return pObj->pPrevFan0; } +static inline Ivy_Obj_t * Ivy_ObjEquivListNext( Ivy_Obj_t * pObj ) { return pObj->pPrevFan0; } +static inline Ivy_Obj_t * Ivy_ObjEquivListPrev( Ivy_Obj_t * pObj ) { return pObj->pPrevFan1; } +static inline Ivy_Obj_t * Ivy_ObjFraig( Ivy_Obj_t * pObj ) { return pObj->pEquiv; } +static inline int Ivy_ObjSatNum( Ivy_Obj_t * pObj ) { return (int)pObj->pNextFan0; } +static inline Vec_Ptr_t * Ivy_ObjFaninVec( Ivy_Obj_t * pObj ) { return (Vec_Ptr_t *)pObj->pNextFan1; } + +static inline void Ivy_ObjSetSim( Ivy_Obj_t * pObj, Ivy_FraigSim_t * pSim ) { pObj->pFanout = (Ivy_Obj_t *)pSim; } +static inline void Ivy_ObjSetClassNodeLast( Ivy_Obj_t * pObj, Ivy_Obj_t * pLast ) { pObj->pNextFan0 = pLast; } +static inline void Ivy_ObjSetClassNodeRepr( Ivy_Obj_t * pObj, Ivy_Obj_t * pRepr ) { pObj->pNextFan0 = pRepr; } +static inline void Ivy_ObjSetClassNodeNext( Ivy_Obj_t * pObj, Ivy_Obj_t * pNext ) { pObj->pNextFan1 = pNext; } +static inline void Ivy_ObjSetNodeHashNext( Ivy_Obj_t * pObj, Ivy_Obj_t * pNext ) { pObj->pPrevFan0 = pNext; } +static inline void Ivy_ObjSetEquivListNext( Ivy_Obj_t * pObj, Ivy_Obj_t * pNext ) { pObj->pPrevFan0 = pNext; } +static inline void Ivy_ObjSetEquivListPrev( Ivy_Obj_t * pObj, Ivy_Obj_t * pPrev ) { pObj->pPrevFan1 = pPrev; } +static inline void Ivy_ObjSetFraig( Ivy_Obj_t * pObj, Ivy_Obj_t * pNode ) { pObj->pEquiv = pNode; } +static inline void Ivy_ObjSetSatNum( Ivy_Obj_t * pObj, int Num ) { pObj->pNextFan0 = (Ivy_Obj_t *)Num; } +static inline void Ivy_ObjSetFaninVec( Ivy_Obj_t * pObj, Vec_Ptr_t * vFanins ) { pObj->pNextFan1 = (Ivy_Obj_t *)vFanins; } + +static inline unsigned Ivy_ObjRandomSim() { return (rand() << 24) ^ (rand() << 12) ^ rand(); } + +// iterate through equivalence classes +#define Ivy_FraigForEachEquivClass( pList, pEnt ) \ + for ( pEnt = pList; \ + pEnt; \ + pEnt = Ivy_ObjEquivListNext(pEnt) ) +#define Ivy_FraigForEachEquivClassSafe( pList, pEnt, pEnt2 ) \ + for ( pEnt = pList, \ + pEnt2 = pEnt? Ivy_ObjEquivListNext(pEnt): NULL; \ + pEnt; \ + pEnt = pEnt2, \ + pEnt2 = pEnt? Ivy_ObjEquivListNext(pEnt): NULL ) +// iterate through nodes in one class +#define Ivy_FraigForEachClassNode( pClass, pEnt ) \ + for ( pEnt = pClass; \ + pEnt; \ + pEnt = Ivy_ObjClassNodeNext(pEnt) ) +// iterate through nodes in the hash table +#define Ivy_FraigForEachBinNode( pBin, pEnt ) \ + for ( pEnt = pBin; \ + pEnt; \ + pEnt = Ivy_ObjNodeHashNext(pEnt) ) + +static Ivy_FraigMan_t * Ivy_FraigStart( Ivy_Man_t * pManAig, Ivy_FraigParams_t * pParams ); +static Ivy_FraigMan_t * Ivy_FraigStartSimple( Ivy_Man_t * pManAig, Ivy_FraigParams_t * pParams ); +static Ivy_Man_t * Ivy_FraigPerform_int( Ivy_Man_t * pManAig, Ivy_FraigParams_t * pParams, sint64 nBTLimitGlobal, sint64 nInsLimitGlobal, sint64 * pnSatConfs, sint64 * pnSatInspects ); +static void Ivy_FraigPrint( Ivy_FraigMan_t * p ); +static void Ivy_FraigStop( Ivy_FraigMan_t * p ); +static void Ivy_FraigSimulate( Ivy_FraigMan_t * p ); +static void Ivy_FraigSweep( Ivy_FraigMan_t * p ); +static Ivy_Obj_t * Ivy_FraigAnd( Ivy_FraigMan_t * p, Ivy_Obj_t * pObjOld ); +static int Ivy_FraigNodesAreEquiv( Ivy_FraigMan_t * p, Ivy_Obj_t * pObj0, Ivy_Obj_t * pObj1 ); +static int Ivy_FraigNodeIsConst( Ivy_FraigMan_t * p, Ivy_Obj_t * pObj ); +static void Ivy_FraigNodeAddToSolver( Ivy_FraigMan_t * p, Ivy_Obj_t * pObj0, Ivy_Obj_t * pObj1 ); +static int Ivy_FraigSetActivityFactors( Ivy_FraigMan_t * p, Ivy_Obj_t * pOld, Ivy_Obj_t * pNew ); +static void Ivy_FraigAddToPatScores( Ivy_FraigMan_t * p, Ivy_Obj_t * pClass, Ivy_Obj_t * pClassNew ); +static int Ivy_FraigMiterStatus( Ivy_Man_t * pMan ); +static void Ivy_FraigMiterProve( Ivy_FraigMan_t * p ); +static void Ivy_FraigMiterPrint( Ivy_Man_t * pNtk, char * pString, int clk, int fVerbose ); +static int * Ivy_FraigCreateModel( Ivy_FraigMan_t * p ); + +static int Ivy_FraigNodesAreEquivBdd( Ivy_Obj_t * pObj1, Ivy_Obj_t * pObj2 ); + +static sint64 s_nBTLimitGlobal = 0; +static sint64 s_nInsLimitGlobal = 0; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Sets the default solving parameters.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigParamsDefault( Ivy_FraigParams_t * pParams ) +{ + memset( pParams, 0, sizeof(Ivy_FraigParams_t) ); + pParams->nSimWords = 32; // the number of words in the simulation info + pParams->dSimSatur = 0.005; // the ratio of refined classes when saturation is reached + pParams->fPatScores = 0; // enables simulation pattern scoring + pParams->MaxScore = 25; // max score after which resimulation is used + pParams->fDoSparse = 1; // skips sparse functions +// pParams->dActConeRatio = 0.05; // the ratio of cone to be bumped +// pParams->dActConeBumpMax = 5.0; // the largest bump of activity + pParams->dActConeRatio = 0.3; // the ratio of cone to be bumped + pParams->dActConeBumpMax = 10.0; // the largest bump of activity + + pParams->nBTLimitNode = 100; // conflict limit at a node + pParams->nBTLimitMiter = 500000; // conflict limit at an output +// pParams->nBTLimitGlobal = 0; // conflict limit global +// pParams->nInsLimitGlobal = 0; // inspection limit global +} + +/**Function************************************************************* + + Synopsis [Performs combinational equivalence checking for the miter.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FraigProve( Ivy_Man_t ** ppManAig, void * pPars ) +{ + Prove_Params_t * pParams = pPars; + Ivy_FraigParams_t Params, * pIvyParams = &Params; + Ivy_Man_t * pManAig, * pManTemp; + int RetValue, nIter, clk, timeStart = clock();//, Counter; + sint64 nSatConfs, nSatInspects; + + // start the network and parameters + pManAig = *ppManAig; + Ivy_FraigParamsDefault( pIvyParams ); + pIvyParams->fVerbose = pParams->fVerbose; + pIvyParams->fProve = 1; + + if ( pParams->fVerbose ) + { + printf( "RESOURCE LIMITS: Iterations = %d. Rewriting = %s. Fraiging = %s.\n", + pParams->nItersMax, pParams->fUseRewriting? "yes":"no", pParams->fUseFraiging? "yes":"no" ); + printf( "Mitering = %d (%3.1f). Rewriting = %d (%3.1f). Fraiging = %d (%3.1f).\n", + pParams->nMiteringLimitStart, pParams->nMiteringLimitMulti, + pParams->nRewritingLimitStart, pParams->nRewritingLimitMulti, + pParams->nFraigingLimitStart, pParams->nFraigingLimitMulti ); + printf( "Mitering last = %d.\n", + pParams->nMiteringLimitLast ); + } + + // if SAT only, solve without iteration + if ( !pParams->fUseRewriting && !pParams->fUseFraiging ) + { + clk = clock(); + pIvyParams->nBTLimitMiter = pParams->nMiteringLimitLast / Ivy_ManPoNum(pManAig); + pManAig = Ivy_FraigMiter( pManTemp = pManAig, pIvyParams ); Ivy_ManStop( pManTemp ); + RetValue = Ivy_FraigMiterStatus( pManAig ); + Ivy_FraigMiterPrint( pManAig, "SAT solving", clk, pParams->fVerbose ); + *ppManAig = pManAig; + return RetValue; + } + + if ( Ivy_ManNodeNum(pManAig) < 500 ) + { + // run the first mitering + clk = clock(); + pIvyParams->nBTLimitMiter = pParams->nMiteringLimitStart / Ivy_ManPoNum(pManAig); + pManAig = Ivy_FraigMiter( pManTemp = pManAig, pIvyParams ); Ivy_ManStop( pManTemp ); + RetValue = Ivy_FraigMiterStatus( pManAig ); + Ivy_FraigMiterPrint( pManAig, "SAT solving", clk, pParams->fVerbose ); + if ( RetValue >= 0 ) + { + *ppManAig = pManAig; + return RetValue; + } + } + + // check the current resource limits + RetValue = -1; + for ( nIter = 0; nIter < pParams->nItersMax; nIter++ ) + { + if ( pParams->fVerbose ) + { + printf( "ITERATION %2d : Confs = %6d. FraigBTL = %3d. \n", nIter+1, + (int)(pParams->nMiteringLimitStart * pow(pParams->nMiteringLimitMulti,nIter)), + (int)(pParams->nFraigingLimitStart * pow(pParams->nFraigingLimitMulti,nIter)) ); + fflush( stdout ); + } + + // try rewriting + if ( pParams->fUseRewriting ) + { // bug in Ivy_NodeFindCutsAll() when leaves are identical! +/* + clk = clock(); + Counter = (int)(pParams->nRewritingLimitStart * pow(pParams->nRewritingLimitMulti,nIter)); + pManAig = Ivy_ManRwsat( pManAig, 0 ); + RetValue = Ivy_FraigMiterStatus( pManAig ); + Ivy_FraigMiterPrint( pManAig, "Rewriting ", clk, pParams->fVerbose ); +*/ + } + if ( RetValue >= 0 ) + break; + + // try fraiging followed by mitering + if ( pParams->fUseFraiging ) + { + clk = clock(); + pIvyParams->nBTLimitNode = (int)(pParams->nFraigingLimitStart * pow(pParams->nFraigingLimitMulti,nIter)); + pIvyParams->nBTLimitMiter = (int)(pParams->nMiteringLimitStart * pow(pParams->nMiteringLimitMulti,nIter)) / Ivy_ManPoNum(pManAig); + pManAig = Ivy_FraigPerform_int( pManTemp = pManAig, pIvyParams, pParams->nTotalBacktrackLimit, pParams->nTotalInspectLimit, &nSatConfs, &nSatInspects ); Ivy_ManStop( pManTemp ); + RetValue = Ivy_FraigMiterStatus( pManAig ); + Ivy_FraigMiterPrint( pManAig, "Fraiging ", clk, pParams->fVerbose ); + } + if ( RetValue >= 0 ) + break; + + // add to the number of backtracks and inspects + pParams->nTotalBacktracksMade += nSatConfs; + pParams->nTotalInspectsMade += nSatInspects; + // check if global resource limit is reached + if ( (pParams->nTotalBacktrackLimit && pParams->nTotalBacktracksMade >= pParams->nTotalBacktrackLimit) || + (pParams->nTotalInspectLimit && pParams->nTotalInspectsMade >= pParams->nTotalInspectLimit) ) + { + printf( "Reached global limit on conflicts/inspects. Quitting.\n" ); + *ppManAig = pManAig; + return -1; + } + } + + if ( RetValue < 0 ) + { + if ( pParams->fVerbose ) + { + printf( "Attempting SAT with conflict limit %d ...\n", pParams->nMiteringLimitLast ); + fflush( stdout ); + } + clk = clock(); + pIvyParams->nBTLimitMiter = pParams->nMiteringLimitLast / Ivy_ManPoNum(pManAig); + if ( pParams->nTotalBacktrackLimit ) + s_nBTLimitGlobal = pParams->nTotalBacktrackLimit - pParams->nTotalBacktracksMade; + if ( pParams->nTotalInspectLimit ) + s_nInsLimitGlobal = pParams->nTotalInspectLimit - pParams->nTotalInspectsMade; + pManAig = Ivy_FraigMiter( pManTemp = pManAig, pIvyParams ); Ivy_ManStop( pManTemp ); + s_nBTLimitGlobal = 0; + s_nInsLimitGlobal = 0; + RetValue = Ivy_FraigMiterStatus( pManAig ); + Ivy_FraigMiterPrint( pManAig, "SAT solving", clk, pParams->fVerbose ); + // make sure that the sover never returns "undecided" when infinite resource limits are set + if( RetValue == -1 && pParams->nTotalInspectLimit == 0 && + pParams->nTotalBacktrackLimit == 0 ) + { + extern void Prove_ParamsPrint( Prove_Params_t * pParams ); + Prove_ParamsPrint( pParams ); + printf("ERROR: ABC has returned \"undecided\" in spite of no limits...\n"); + exit(1); + } + } + + // assign the model if it was proved by rewriting (const 1 miter) + if ( RetValue == 0 && pManAig->pData == NULL ) + { + pManAig->pData = ALLOC( int, Ivy_ManPiNum(pManAig) ); + memset( pManAig->pData, 0, sizeof(int) * Ivy_ManPiNum(pManAig) ); + } + *ppManAig = pManAig; + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Performs fraiging of the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Man_t * Ivy_FraigPerform_int( Ivy_Man_t * pManAig, Ivy_FraigParams_t * pParams, sint64 nBTLimitGlobal, sint64 nInsLimitGlobal, sint64 * pnSatConfs, sint64 * pnSatInspects ) +{ + Ivy_FraigMan_t * p; + Ivy_Man_t * pManAigNew; + int clk; + if ( Ivy_ManNodeNum(pManAig) == 0 ) + return Ivy_ManDup(pManAig); +clk = clock(); + assert( Ivy_ManLatchNum(pManAig) == 0 ); + p = Ivy_FraigStart( pManAig, pParams ); + // set global limits + p->nBTLimitGlobal = nBTLimitGlobal; + p->nInsLimitGlobal = nInsLimitGlobal; + + Ivy_FraigSimulate( p ); + Ivy_FraigSweep( p ); + pManAigNew = p->pManFraig; +p->timeTotal = clock() - clk; + if ( pnSatConfs ) + *pnSatConfs = p->pSat? p->pSat->stats.conflicts : 0; + if ( pnSatInspects ) + *pnSatInspects = p->pSat? p->pSat->stats.inspects : 0; + Ivy_FraigStop( p ); + return pManAigNew; +} + +/**Function************************************************************* + + Synopsis [Performs fraiging of the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Man_t * Ivy_FraigPerform( Ivy_Man_t * pManAig, Ivy_FraigParams_t * pParams ) +{ + Ivy_FraigMan_t * p; + Ivy_Man_t * pManAigNew; + int clk; + if ( Ivy_ManNodeNum(pManAig) == 0 ) + return Ivy_ManDup(pManAig); +clk = clock(); + assert( Ivy_ManLatchNum(pManAig) == 0 ); + p = Ivy_FraigStart( pManAig, pParams ); + Ivy_FraigSimulate( p ); + Ivy_FraigSweep( p ); + pManAigNew = p->pManFraig; +p->timeTotal = clock() - clk; + Ivy_FraigStop( p ); + return pManAigNew; +} + +/**Function************************************************************* + + Synopsis [Applies brute-force SAT to the miter.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Man_t * Ivy_FraigMiter( Ivy_Man_t * pManAig, Ivy_FraigParams_t * pParams ) +{ + Ivy_FraigMan_t * p; + Ivy_Man_t * pManAigNew; + Ivy_Obj_t * pObj; + int i, clk; +clk = clock(); + assert( Ivy_ManLatchNum(pManAig) == 0 ); + p = Ivy_FraigStartSimple( pManAig, pParams ); + // set global limits + p->nBTLimitGlobal = s_nBTLimitGlobal; + p->nInsLimitGlobal = s_nInsLimitGlobal; + // duplicate internal nodes + Ivy_ManForEachNode( p->pManAig, pObj, i ) + pObj->pEquiv = Ivy_And( p->pManFraig, Ivy_ObjChild0Equiv(pObj), Ivy_ObjChild1Equiv(pObj) ); + // try to prove each output of the miter + Ivy_FraigMiterProve( p ); + // add the POs + Ivy_ManForEachPo( p->pManAig, pObj, i ) + Ivy_ObjCreatePo( p->pManFraig, Ivy_ObjChild0Equiv(pObj) ); + // clean the new manager + Ivy_ManForEachObj( p->pManFraig, pObj, i ) + { + if ( Ivy_ObjFaninVec(pObj) ) + Vec_PtrFree( Ivy_ObjFaninVec(pObj) ); + pObj->pNextFan0 = pObj->pNextFan1 = NULL; + } + // remove dangling nodes + Ivy_ManCleanup( p->pManFraig ); + pManAigNew = p->pManFraig; +p->timeTotal = clock() - clk; + +//printf( "Final nodes = %6d. ", Ivy_ManNodeNum(pManAigNew) ); +//PRT( "Time", p->timeTotal ); + Ivy_FraigStop( p ); + return pManAigNew; +} + +/**Function************************************************************* + + Synopsis [Starts the fraiging manager without simulation info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_FraigMan_t * Ivy_FraigStartSimple( Ivy_Man_t * pManAig, Ivy_FraigParams_t * pParams ) +{ + Ivy_FraigMan_t * p; + // allocat the fraiging manager + p = ALLOC( Ivy_FraigMan_t, 1 ); + memset( p, 0, sizeof(Ivy_FraigMan_t) ); + p->pParams = pParams; + p->pManAig = pManAig; + p->pManFraig = Ivy_ManStartFrom( pManAig ); + p->vPiVars = Vec_PtrAlloc( 100 ); + return p; +} + +/**Function************************************************************* + + Synopsis [Starts the fraiging manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_FraigMan_t * Ivy_FraigStart( Ivy_Man_t * pManAig, Ivy_FraigParams_t * pParams ) +{ + Ivy_FraigMan_t * p; + Ivy_FraigSim_t * pSims; + Ivy_Obj_t * pObj; + int i, k, EntrySize; + // clean the fanout representation + Ivy_ManForEachObj( pManAig, pObj, i ) +// pObj->pEquiv = pObj->pFanout = pObj->pNextFan0 = pObj->pNextFan1 = pObj->pPrevFan0 = pObj->pPrevFan1 = NULL; + assert( !pObj->pEquiv && !pObj->pFanout ); + // allocat the fraiging manager + p = ALLOC( Ivy_FraigMan_t, 1 ); + memset( p, 0, sizeof(Ivy_FraigMan_t) ); + p->pParams = pParams; + p->pManAig = pManAig; + p->pManFraig = Ivy_ManStartFrom( pManAig ); + // allocate simulation info + p->nSimWords = pParams->nSimWords; +// p->pSimWords = ALLOC( unsigned, Ivy_ManObjNum(pManAig) * p->nSimWords ); + EntrySize = sizeof(Ivy_FraigSim_t) + sizeof(unsigned) * p->nSimWords; + p->pSimWords = (char *)malloc( Ivy_ManObjNum(pManAig) * EntrySize ); + memset( p->pSimWords, 0, EntrySize ); + k = 0; + Ivy_ManForEachObj( pManAig, pObj, i ) + { + pSims = (Ivy_FraigSim_t *)(p->pSimWords + EntrySize * k++); + pSims->pNext = NULL; + if ( Ivy_ObjIsNode(pObj) ) + { + if ( p->pSimStart == NULL ) + p->pSimStart = pSims; + else + ((Ivy_FraigSim_t *)(p->pSimWords + EntrySize * (k-2)))->pNext = pSims; + pSims->pFanin0 = Ivy_ObjSim( Ivy_ObjFanin0(pObj) ); + pSims->pFanin1 = Ivy_ObjSim( Ivy_ObjFanin1(pObj) ); + pSims->Type = (Ivy_ObjFaninPhase(Ivy_ObjChild0(pObj)) << 2) | (Ivy_ObjFaninPhase(Ivy_ObjChild1(pObj)) << 1) | pObj->fPhase; + } + else + { + pSims->pFanin0 = NULL; + pSims->pFanin1 = NULL; + pSims->Type = 0; + } + Ivy_ObjSetSim( pObj, pSims ); + } + assert( k == Ivy_ManObjNum(pManAig) ); + // allocate storage for sim pattern + p->nPatWords = Ivy_BitWordNum( Ivy_ManPiNum(pManAig) ); + p->pPatWords = ALLOC( unsigned, p->nPatWords ); + p->pPatScores = ALLOC( int, 32 * p->nSimWords ); + p->vPiVars = Vec_PtrAlloc( 100 ); + // set random number generator + srand( 0xABCABC ); + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the fraiging manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigStop( Ivy_FraigMan_t * p ) +{ + if ( p->pParams->fVerbose ) + Ivy_FraigPrint( p ); + if ( p->vPiVars ) Vec_PtrFree( p->vPiVars ); + if ( p->pSat ) sat_solver_delete( p->pSat ); + FREE( p->pPatScores ); + FREE( p->pPatWords ); + FREE( p->pSimWords ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Prints stats for the fraiging manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigPrint( Ivy_FraigMan_t * p ) +{ + double nMemory; + nMemory = (double)Ivy_ManObjNum(p->pManAig)*p->nSimWords*sizeof(unsigned)/(1<<20); + printf( "SimWords = %d. Rounds = %d. Mem = %0.2f Mb. ", p->nSimWords, p->nSimRounds, nMemory ); + printf( "Classes: Beg = %d. End = %d.\n", p->nClassesBeg, p->nClassesEnd ); + printf( "Limits: BTNode = %d. BTMiter = %d.\n", p->pParams->nBTLimitNode, p->pParams->nBTLimitMiter ); + printf( "Proof = %d. Counter-example = %d. Fail = %d. FailReal = %d. Zero = %d.\n", + p->nSatProof, p->nSatCallsSat, p->nSatFails, p->nSatFailsReal, p->nClassesZero ); + printf( "Final = %d. Miter = %d. Total = %d. Mux = %d. (Exor = %d.) SatVars = %d.\n", + Ivy_ManNodeNum(p->pManFraig), p->nNodesMiter, Ivy_ManNodeNum(p->pManAig), 0, 0, p->nSatVars ); + if ( p->pSat ) Sat_SolverPrintStats( stdout, p->pSat ); + PRT( "AIG simulation ", p->timeSim ); + PRT( "AIG traversal ", p->timeTrav ); + PRT( "SAT solving ", p->timeSat ); + PRT( " Unsat ", p->timeSatUnsat ); + PRT( " Sat ", p->timeSatSat ); + PRT( " Fail ", p->timeSatFail ); + PRT( "Class refining ", p->timeRef ); + PRT( "TOTAL RUNTIME ", p->timeTotal ); + if ( p->time1 ) { PRT( "time1 ", p->time1 ); } +} + + + +/**Function************************************************************* + + Synopsis [Assigns random patterns to the PI node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_NodeAssignRandom( Ivy_FraigMan_t * p, Ivy_Obj_t * pObj ) +{ + Ivy_FraigSim_t * pSims; + int i; + assert( Ivy_ObjIsPi(pObj) ); + pSims = Ivy_ObjSim(pObj); + for ( i = 0; i < p->nSimWords; i++ ) + pSims->pData[i] = Ivy_ObjRandomSim(); +} + +/**Function************************************************************* + + Synopsis [Assigns constant patterns to the PI node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_NodeAssignConst( Ivy_FraigMan_t * p, Ivy_Obj_t * pObj, int fConst1 ) +{ + Ivy_FraigSim_t * pSims; + int i; + assert( Ivy_ObjIsPi(pObj) ); + pSims = Ivy_ObjSim(pObj); + for ( i = 0; i < p->nSimWords; i++ ) + pSims->pData[i] = fConst1? ~(unsigned)0 : 0; +} + +/**Function************************************************************* + + Synopsis [Assings random simulation info for the PIs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigAssignRandom( Ivy_FraigMan_t * p ) +{ + Ivy_Obj_t * pObj; + int i; + Ivy_ManForEachPi( p->pManAig, pObj, i ) + Ivy_NodeAssignRandom( p, pObj ); +} + +/**Function************************************************************* + + Synopsis [Assings distance-1 simulation info for the PIs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigAssignDist1( Ivy_FraigMan_t * p, unsigned * pPat ) +{ + Ivy_Obj_t * pObj; + int i, Limit; + Ivy_ManForEachPi( p->pManAig, pObj, i ) + { + Ivy_NodeAssignConst( p, pObj, Ivy_InfoHasBit(pPat, i) ); +// printf( "%d", Ivy_InfoHasBit(pPat, i) ); + } +// printf( "\n" ); + + Limit = IVY_MIN( Ivy_ManPiNum(p->pManAig), p->nSimWords * 32 - 1 ); + for ( i = 0; i < Limit; i++ ) + Ivy_InfoXorBit( Ivy_ObjSim( Ivy_ManPi(p->pManAig,i) )->pData, i+1 ); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if simulation info is composed of all zeros.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_NodeHasZeroSim( Ivy_FraigMan_t * p, Ivy_Obj_t * pObj ) +{ + Ivy_FraigSim_t * pSims; + int i; + pSims = Ivy_ObjSim(pObj); + for ( i = 0; i < p->nSimWords; i++ ) + if ( pSims->pData[i] ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if simulation info is composed of all zeros.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_NodeComplementSim( Ivy_FraigMan_t * p, Ivy_Obj_t * pObj ) +{ + Ivy_FraigSim_t * pSims; + int i; + pSims = Ivy_ObjSim(pObj); + for ( i = 0; i < p->nSimWords; i++ ) + pSims->pData[i] = ~pSims->pData[i]; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if simulation infos are equal.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_NodeCompareSims( Ivy_FraigMan_t * p, Ivy_Obj_t * pObj0, Ivy_Obj_t * pObj1 ) +{ + Ivy_FraigSim_t * pSims0, * pSims1; + int i; + pSims0 = Ivy_ObjSim(pObj0); + pSims1 = Ivy_ObjSim(pObj1); + for ( i = 0; i < p->nSimWords; i++ ) + if ( pSims0->pData[i] != pSims1->pData[i] ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Simulates one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_NodeSimulateSim( Ivy_FraigMan_t * p, Ivy_FraigSim_t * pSims ) +{ + unsigned * pData, * pData0, * pData1; + int i; + pData = pSims->pData; + pData0 = pSims->pFanin0->pData; + pData1 = pSims->pFanin1->pData; + switch( pSims->Type ) + { + case 0: + for ( i = 0; i < p->nSimWords; i++ ) + pData[i] = (pData0[i] & pData1[i]); + break; + case 1: + for ( i = 0; i < p->nSimWords; i++ ) + pData[i] = ~(pData0[i] & pData1[i]); + break; + case 2: + for ( i = 0; i < p->nSimWords; i++ ) + pData[i] = (pData0[i] & ~pData1[i]); + break; + case 3: + for ( i = 0; i < p->nSimWords; i++ ) + pData[i] = (~pData0[i] | pData1[i]); + break; + case 4: + for ( i = 0; i < p->nSimWords; i++ ) + pData[i] = (~pData0[i] & pData1[i]); + break; + case 5: + for ( i = 0; i < p->nSimWords; i++ ) + pData[i] = (pData0[i] | ~pData1[i]); + break; + case 6: + for ( i = 0; i < p->nSimWords; i++ ) + pData[i] = ~(pData0[i] | pData1[i]); + break; + case 7: + for ( i = 0; i < p->nSimWords; i++ ) + pData[i] = (pData0[i] | pData1[i]); + break; + } +} + +/**Function************************************************************* + + Synopsis [Simulates one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_NodeSimulate( Ivy_FraigMan_t * p, Ivy_Obj_t * pObj ) +{ + Ivy_FraigSim_t * pSims, * pSims0, * pSims1; + int fCompl, fCompl0, fCompl1, i; + assert( !Ivy_IsComplement(pObj) ); + // get hold of the simulation information + pSims = Ivy_ObjSim(pObj); + pSims0 = Ivy_ObjSim(Ivy_ObjFanin0(pObj)); + pSims1 = Ivy_ObjSim(Ivy_ObjFanin1(pObj)); + // get complemented attributes of the children using their random info + fCompl = pObj->fPhase; + fCompl0 = Ivy_ObjFaninPhase(Ivy_ObjChild0(pObj)); + fCompl1 = Ivy_ObjFaninPhase(Ivy_ObjChild1(pObj)); + // simulate + if ( fCompl0 && fCompl1 ) + { + if ( fCompl ) + for ( i = 0; i < p->nSimWords; i++ ) + pSims->pData[i] = (pSims0->pData[i] | pSims1->pData[i]); + else + for ( i = 0; i < p->nSimWords; i++ ) + pSims->pData[i] = ~(pSims0->pData[i] | pSims1->pData[i]); + } + else if ( fCompl0 && !fCompl1 ) + { + if ( fCompl ) + for ( i = 0; i < p->nSimWords; i++ ) + pSims->pData[i] = (pSims0->pData[i] | ~pSims1->pData[i]); + else + for ( i = 0; i < p->nSimWords; i++ ) + pSims->pData[i] = (~pSims0->pData[i] & pSims1->pData[i]); + } + else if ( !fCompl0 && fCompl1 ) + { + if ( fCompl ) + for ( i = 0; i < p->nSimWords; i++ ) + pSims->pData[i] = (~pSims0->pData[i] | pSims1->pData[i]); + else + for ( i = 0; i < p->nSimWords; i++ ) + pSims->pData[i] = (pSims0->pData[i] & ~pSims1->pData[i]); + } + else // if ( !fCompl0 && !fCompl1 ) + { + if ( fCompl ) + for ( i = 0; i < p->nSimWords; i++ ) + pSims->pData[i] = ~(pSims0->pData[i] & pSims1->pData[i]); + else + for ( i = 0; i < p->nSimWords; i++ ) + pSims->pData[i] = (pSims0->pData[i] & pSims1->pData[i]); + } +} + +/**Function************************************************************* + + Synopsis [Computes hash value using simulation info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Ivy_NodeHash( Ivy_FraigMan_t * p, Ivy_Obj_t * pObj ) +{ + static int s_FPrimes[128] = { + 1009, 1049, 1093, 1151, 1201, 1249, 1297, 1361, 1427, 1459, + 1499, 1559, 1607, 1657, 1709, 1759, 1823, 1877, 1933, 1997, + 2039, 2089, 2141, 2213, 2269, 2311, 2371, 2411, 2467, 2543, + 2609, 2663, 2699, 2741, 2797, 2851, 2909, 2969, 3037, 3089, + 3169, 3221, 3299, 3331, 3389, 3461, 3517, 3557, 3613, 3671, + 3719, 3779, 3847, 3907, 3943, 4013, 4073, 4129, 4201, 4243, + 4289, 4363, 4441, 4493, 4549, 4621, 4663, 4729, 4793, 4871, + 4933, 4973, 5021, 5087, 5153, 5227, 5281, 5351, 5417, 5471, + 5519, 5573, 5651, 5693, 5749, 5821, 5861, 5923, 6011, 6073, + 6131, 6199, 6257, 6301, 6353, 6397, 6481, 6563, 6619, 6689, + 6737, 6803, 6863, 6917, 6977, 7027, 7109, 7187, 7237, 7309, + 7393, 7477, 7523, 7561, 7607, 7681, 7727, 7817, 7877, 7933, + 8011, 8039, 8059, 8081, 8093, 8111, 8123, 8147 + }; + Ivy_FraigSim_t * pSims; + unsigned uHash; + int i; + assert( p->nSimWords <= 128 ); + uHash = 0; + pSims = Ivy_ObjSim(pObj); + for ( i = 0; i < p->nSimWords; i++ ) + uHash ^= pSims->pData[i] * s_FPrimes[i]; + return uHash; +} + +/**Function************************************************************* + + Synopsis [Simulates AIG manager.] + + Description [Assumes that the PI simulation info is attached.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigSimulateOne( Ivy_FraigMan_t * p ) +{ + Ivy_Obj_t * pObj; + int i, clk; +clk = clock(); + Ivy_ManForEachNode( p->pManAig, pObj, i ) + { + Ivy_NodeSimulate( p, pObj ); +/* + if ( Ivy_ObjFraig(pObj) == NULL ) + printf( "%3d --- -- %d : ", pObj->Id, pObj->fPhase ); + else + printf( "%3d %3d %2d %d : ", pObj->Id, Ivy_Regular(Ivy_ObjFraig(pObj))->Id, Ivy_ObjSatNum(Ivy_Regular(Ivy_ObjFraig(pObj))), pObj->fPhase ); + Extra_PrintBinary( stdout, Ivy_ObjSim(pObj), 30 ); + printf( "\n" ); +*/ + } +p->timeSim += clock() - clk; +p->nSimRounds++; +} + +/**Function************************************************************* + + Synopsis [Simulates AIG manager.] + + Description [Assumes that the PI simulation info is attached.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigSimulateOneSim( Ivy_FraigMan_t * p ) +{ + Ivy_FraigSim_t * pSims; + int clk; +clk = clock(); + for ( pSims = p->pSimStart; pSims; pSims = pSims->pNext ) + Ivy_NodeSimulateSim( p, pSims ); +p->timeSim += clock() - clk; +p->nSimRounds++; +} + +/**Function************************************************************* + + Synopsis [Adds one node to the equivalence class.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_NodeAddToClass( Ivy_Obj_t * pClass, Ivy_Obj_t * pObj ) +{ + if ( Ivy_ObjClassNodeNext(pClass) == NULL ) + Ivy_ObjSetClassNodeNext( pClass, pObj ); + else + Ivy_ObjSetClassNodeNext( Ivy_ObjClassNodeLast(pClass), pObj ); + Ivy_ObjSetClassNodeLast( pClass, pObj ); + Ivy_ObjSetClassNodeRepr( pObj, pClass ); + Ivy_ObjSetClassNodeNext( pObj, NULL ); +} + +/**Function************************************************************* + + Synopsis [Adds equivalence class to the list of classes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigAddClass( Ivy_FraigList_t * pList, Ivy_Obj_t * pClass ) +{ + if ( pList->pHead == NULL ) + { + pList->pHead = pClass; + pList->pTail = pClass; + Ivy_ObjSetEquivListPrev( pClass, NULL ); + Ivy_ObjSetEquivListNext( pClass, NULL ); + } + else + { + Ivy_ObjSetEquivListNext( pList->pTail, pClass ); + Ivy_ObjSetEquivListPrev( pClass, pList->pTail ); + Ivy_ObjSetEquivListNext( pClass, NULL ); + pList->pTail = pClass; + } + pList->nItems++; +} + +/**Function************************************************************* + + Synopsis [Updates the list of classes after base class has split.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigInsertClass( Ivy_FraigList_t * pList, Ivy_Obj_t * pBase, Ivy_Obj_t * pClass ) +{ + Ivy_ObjSetEquivListPrev( pClass, pBase ); + Ivy_ObjSetEquivListNext( pClass, Ivy_ObjEquivListNext(pBase) ); + if ( Ivy_ObjEquivListNext(pBase) ) + Ivy_ObjSetEquivListPrev( Ivy_ObjEquivListNext(pBase), pClass ); + Ivy_ObjSetEquivListNext( pBase, pClass ); + if ( pList->pTail == pBase ) + pList->pTail = pClass; + pList->nItems++; +} + +/**Function************************************************************* + + Synopsis [Removes equivalence class from the list of classes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigRemoveClass( Ivy_FraigList_t * pList, Ivy_Obj_t * pClass ) +{ + if ( pList->pHead == pClass ) + pList->pHead = Ivy_ObjEquivListNext(pClass); + if ( pList->pTail == pClass ) + pList->pTail = Ivy_ObjEquivListPrev(pClass); + if ( Ivy_ObjEquivListPrev(pClass) ) + Ivy_ObjSetEquivListNext( Ivy_ObjEquivListPrev(pClass), Ivy_ObjEquivListNext(pClass) ); + if ( Ivy_ObjEquivListNext(pClass) ) + Ivy_ObjSetEquivListPrev( Ivy_ObjEquivListNext(pClass), Ivy_ObjEquivListPrev(pClass) ); + Ivy_ObjSetEquivListNext( pClass, NULL ); + Ivy_ObjSetEquivListPrev( pClass, NULL ); + pClass->fMarkA = 0; + pList->nItems--; +} + +/**Function************************************************************* + + Synopsis [Count the number of pairs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FraigCountPairsClasses( Ivy_FraigMan_t * p ) +{ + Ivy_Obj_t * pClass, * pNode; + int nPairs = 0, nNodes; + return nPairs; + + Ivy_FraigForEachEquivClass( p->lClasses.pHead, pClass ) + { + nNodes = 0; + Ivy_FraigForEachClassNode( pClass, pNode ) + nNodes++; + nPairs += nNodes * (nNodes - 1) / 2; + } + return nPairs; +} + +/**Function************************************************************* + + Synopsis [Creates initial simulation classes.] + + Description [Assumes that simulation info is assigned.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigCreateClasses( Ivy_FraigMan_t * p ) +{ + Ivy_Obj_t ** pTable; + Ivy_Obj_t * pObj, * pConst1, * pBin, * pEntry; + int i, nTableSize; + unsigned Hash; + pConst1 = Ivy_ManConst1(p->pManAig); + // allocate the table + nTableSize = Ivy_ManObjNum(p->pManAig) / 2 + 13; + pTable = ALLOC( Ivy_Obj_t *, nTableSize ); + memset( pTable, 0, sizeof(Ivy_Obj_t *) * nTableSize ); + // collect nodes into the table + Ivy_ManForEachObj( p->pManAig, pObj, i ) + { + if ( !Ivy_ObjIsPi(pObj) && !Ivy_ObjIsNode(pObj) ) + continue; + Hash = Ivy_NodeHash( p, pObj ); + if ( Hash == 0 && Ivy_NodeHasZeroSim( p, pObj ) ) + { + Ivy_NodeAddToClass( pConst1, pObj ); + continue; + } + // add the node to the table + pBin = pTable[Hash % nTableSize]; + Ivy_FraigForEachBinNode( pBin, pEntry ) + if ( Ivy_NodeCompareSims( p, pEntry, pObj ) ) + { + Ivy_NodeAddToClass( pEntry, pObj ); + break; + } + // check if the entry was added + if ( pEntry ) + continue; + Ivy_ObjSetNodeHashNext( pObj, pBin ); + pTable[Hash % nTableSize] = pObj; + } + // collect non-trivial classes + assert( p->lClasses.pHead == NULL ); + Ivy_ManForEachObj( p->pManAig, pObj, i ) + { + if ( !Ivy_ObjIsConst1(pObj) && !Ivy_ObjIsPi(pObj) && !Ivy_ObjIsNode(pObj) ) + continue; + Ivy_ObjSetNodeHashNext( pObj, NULL ); + if ( Ivy_ObjClassNodeRepr(pObj) == NULL ) + { + assert( Ivy_ObjClassNodeNext(pObj) == NULL ); + continue; + } + // recognize the head of the class + if ( Ivy_ObjClassNodeNext( Ivy_ObjClassNodeRepr(pObj) ) != NULL ) + continue; + // clean the class representative and add it to the list + Ivy_ObjSetClassNodeRepr( pObj, NULL ); + Ivy_FraigAddClass( &p->lClasses, pObj ); + } + // free the table + free( pTable ); +} + +/**Function************************************************************* + + Synopsis [Recursively refines the class after simulation.] + + Description [Returns 1 if the class has changed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FraigRefineClass_rec( Ivy_FraigMan_t * p, Ivy_Obj_t * pClass ) +{ + Ivy_Obj_t * pClassNew, * pListOld, * pListNew, * pNode; + int RetValue = 0; + // check if there is refinement + pListOld = pClass; + Ivy_FraigForEachClassNode( Ivy_ObjClassNodeNext(pClass), pClassNew ) + { + if ( !Ivy_NodeCompareSims(p, pClass, pClassNew) ) + { + if ( p->pParams->fPatScores ) + Ivy_FraigAddToPatScores( p, pClass, pClassNew ); + break; + } + pListOld = pClassNew; + } + if ( pClassNew == NULL ) + return 0; + // set representative of the new class + Ivy_ObjSetClassNodeRepr( pClassNew, NULL ); + // start the new list + pListNew = pClassNew; + // go through the remaining nodes and sort them into two groups: + // (1) matches of the old node; (2) non-matches of the old node + Ivy_FraigForEachClassNode( Ivy_ObjClassNodeNext(pClassNew), pNode ) + if ( Ivy_NodeCompareSims( p, pClass, pNode ) ) + { + Ivy_ObjSetClassNodeNext( pListOld, pNode ); + pListOld = pNode; + } + else + { + Ivy_ObjSetClassNodeNext( pListNew, pNode ); + Ivy_ObjSetClassNodeRepr( pNode, pClassNew ); + pListNew = pNode; + } + // finish both lists + Ivy_ObjSetClassNodeNext( pListNew, NULL ); + Ivy_ObjSetClassNodeNext( pListOld, NULL ); + // update the list of classes + Ivy_FraigInsertClass( &p->lClasses, pClass, pClassNew ); + // if the old class is trivial, remove it + if ( Ivy_ObjClassNodeNext(pClass) == NULL ) + Ivy_FraigRemoveClass( &p->lClasses, pClass ); + // if the new class is trivial, remove it; otherwise, try to refine it + if ( Ivy_ObjClassNodeNext(pClassNew) == NULL ) + Ivy_FraigRemoveClass( &p->lClasses, pClassNew ); + else + RetValue = Ivy_FraigRefineClass_rec( p, pClassNew ); + return RetValue + 1; +} + +/**Function************************************************************* + + Synopsis [Creates the counter-example from the successful pattern.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigCheckOutputSimsSavePattern( Ivy_FraigMan_t * p, Ivy_Obj_t * pObj ) +{ + Ivy_FraigSim_t * pSims; + int i, k, BestPat, * pModel; + // find the word of the pattern + pSims = Ivy_ObjSim(pObj); + for ( i = 0; i < p->nSimWords; i++ ) + if ( pSims->pData[i] ) + break; + assert( i < p->nSimWords ); + // find the bit of the pattern + for ( k = 0; k < 32; k++ ) + if ( pSims->pData[i] & (1 << k) ) + break; + assert( k < 32 ); + // determine the best pattern + BestPat = i * 32 + k; + // fill in the counter-example data + pModel = ALLOC( int, Ivy_ManPiNum(p->pManFraig) ); + Ivy_ManForEachPi( p->pManAig, pObj, i ) + { + pModel[i] = Ivy_InfoHasBit(Ivy_ObjSim(pObj)->pData, BestPat); +// printf( "%d", pModel[i] ); + } +// printf( "\n" ); + // set the model + assert( p->pManFraig->pData == NULL ); + p->pManFraig->pData = pModel; + return; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the one of the output is already non-constant 0.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FraigCheckOutputSims( Ivy_FraigMan_t * p ) +{ + Ivy_Obj_t * pObj; + int i; + // make sure the reference simulation pattern does not detect the bug + pObj = Ivy_ManPo( p->pManAig, 0 ); + assert( Ivy_ObjFanin0(pObj)->fPhase == (unsigned)Ivy_ObjFaninC0(pObj) ); // Ivy_ObjFaninPhase(Ivy_ObjChild0(pObj)) == 0 + Ivy_ManForEachPo( p->pManAig, pObj, i ) + { + // complement simulation info +// if ( Ivy_ObjFanin0(pObj)->fPhase ^ Ivy_ObjFaninC0(pObj) ) // Ivy_ObjFaninPhase(Ivy_ObjChild0(pObj)) +// Ivy_NodeComplementSim( p, Ivy_ObjFanin0(pObj) ); + // check + if ( !Ivy_NodeHasZeroSim( p, Ivy_ObjFanin0(pObj) ) ) + { + // create the counter-example from this pattern + Ivy_FraigCheckOutputSimsSavePattern( p, Ivy_ObjFanin0(pObj) ); + return 1; + } + // complement simulation info +// if ( Ivy_ObjFanin0(pObj)->fPhase ^ Ivy_ObjFaninC0(pObj) ) +// Ivy_NodeComplementSim( p, Ivy_ObjFanin0(pObj) ); + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Refines the classes after simulation.] + + Description [Assumes that simulation info is assigned. Returns the + number of classes refined.] + + SideEffects [Large equivalence class of constant 0 may cause problems.] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FraigRefineClasses( Ivy_FraigMan_t * p ) +{ + Ivy_Obj_t * pClass, * pClass2; + int clk, RetValue, Counter = 0; + // check if some outputs already became non-constant + // this is a special case when computation can be stopped!!! + if ( p->pParams->fProve ) + Ivy_FraigCheckOutputSims( p ); + if ( p->pManFraig->pData ) + return 0; + // refine the classed +clk = clock(); + Ivy_FraigForEachEquivClassSafe( p->lClasses.pHead, pClass, pClass2 ) + { + if ( pClass->fMarkA ) + continue; + RetValue = Ivy_FraigRefineClass_rec( p, pClass ); + Counter += ( RetValue > 0 ); +//if ( Ivy_ObjIsConst1(pClass) ) +//printf( "%d ", RetValue ); +//if ( Ivy_ObjIsConst1(pClass) ) +// p->time1 += clock() - clk; + } +p->timeRef += clock() - clk; + return Counter; +} + +/**Function************************************************************* + + Synopsis [Print the class.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigPrintClass( Ivy_Obj_t * pClass ) +{ + Ivy_Obj_t * pObj; + printf( "Class {" ); + Ivy_FraigForEachClassNode( pClass, pObj ) + printf( " %d(%d)%c", pObj->Id, pObj->Level, pObj->fPhase? '+' : '-' ); + printf( " }\n" ); +} + +/**Function************************************************************* + + Synopsis [Count the number of elements in the class.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FraigCountClassNodes( Ivy_Obj_t * pClass ) +{ + Ivy_Obj_t * pObj; + int Counter = 0; + Ivy_FraigForEachClassNode( pClass, pObj ) + Counter++; + return Counter; +} + +/**Function************************************************************* + + Synopsis [Prints simulation classes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigPrintSimClasses( Ivy_FraigMan_t * p ) +{ + Ivy_Obj_t * pClass; + Ivy_FraigForEachEquivClass( p->lClasses.pHead, pClass ) + { +// Ivy_FraigPrintClass( pClass ); + printf( "%d ", Ivy_FraigCountClassNodes( pClass ) ); + } +// printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [Generated const 0 pattern.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigSavePattern0( Ivy_FraigMan_t * p ) +{ + memset( p->pPatWords, 0, sizeof(unsigned) * p->nPatWords ); +} + +/**Function************************************************************* + + Synopsis [[Generated const 1 pattern.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigSavePattern1( Ivy_FraigMan_t * p ) +{ + memset( p->pPatWords, 0xff, sizeof(unsigned) * p->nPatWords ); +} + +/**Function************************************************************* + + Synopsis [Generates the counter-example satisfying the miter.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int * Ivy_FraigCreateModel( Ivy_FraigMan_t * p ) +{ + int * pModel; + Ivy_Obj_t * pObj; + int i; + pModel = ALLOC( int, Ivy_ManPiNum(p->pManFraig) ); + Ivy_ManForEachPi( p->pManFraig, pObj, i ) + pModel[i] = ( p->pSat->model.ptr[Ivy_ObjSatNum(pObj)] == l_True ); + return pModel; +} + +/**Function************************************************************* + + Synopsis [Copy pattern from the solver into the internal storage.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigSavePattern( Ivy_FraigMan_t * p ) +{ + Ivy_Obj_t * pObj; + int i; + memset( p->pPatWords, 0, sizeof(unsigned) * p->nPatWords ); + Ivy_ManForEachPi( p->pManFraig, pObj, i ) +// Vec_PtrForEachEntry( p->vPiVars, pObj, i ) + if ( p->pSat->model.ptr[Ivy_ObjSatNum(pObj)] == l_True ) + Ivy_InfoSetBit( p->pPatWords, i ); +// Ivy_InfoSetBit( p->pPatWords, pObj->Id - 1 ); +} + +/**Function************************************************************* + + Synopsis [Copy pattern from the solver into the internal storage.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigSavePattern2( Ivy_FraigMan_t * p ) +{ + Ivy_Obj_t * pObj; + int i; + memset( p->pPatWords, 0, sizeof(unsigned) * p->nPatWords ); +// Ivy_ManForEachPi( p->pManFraig, pObj, i ) + Vec_PtrForEachEntry( p->vPiVars, pObj, i ) + if ( p->pSat->model.ptr[Ivy_ObjSatNum(pObj)] == l_True ) +// Ivy_InfoSetBit( p->pPatWords, i ); + Ivy_InfoSetBit( p->pPatWords, pObj->Id - 1 ); +} + +/**Function************************************************************* + + Synopsis [Copy pattern from the solver into the internal storage.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigSavePattern3( Ivy_FraigMan_t * p ) +{ + Ivy_Obj_t * pObj; + int i; + for ( i = 0; i < p->nPatWords; i++ ) + p->pPatWords[i] = Ivy_ObjRandomSim(); + Vec_PtrForEachEntry( p->vPiVars, pObj, i ) + if ( Ivy_InfoHasBit( p->pPatWords, pObj->Id - 1 ) ^ (p->pSat->model.ptr[Ivy_ObjSatNum(pObj)] == l_True) ) + Ivy_InfoXorBit( p->pPatWords, pObj->Id - 1 ); +} + + +/**Function************************************************************* + + Synopsis [Performs simulation of the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigSimulate( Ivy_FraigMan_t * p ) +{ + int nChanges, nClasses; + // start the classes + Ivy_FraigAssignRandom( p ); + Ivy_FraigSimulateOne( p ); + Ivy_FraigCreateClasses( p ); +//printf( "Starting classes = %5d. Pairs = %6d.\n", p->lClasses.nItems, Ivy_FraigCountPairsClasses(p) ); + // refine classes by walking 0/1 patterns + Ivy_FraigSavePattern0( p ); + Ivy_FraigAssignDist1( p, p->pPatWords ); + Ivy_FraigSimulateOne( p ); + nChanges = Ivy_FraigRefineClasses( p ); + if ( p->pManFraig->pData ) + return; +//printf( "Refined classes = %5d. Changes = %4d. Pairs = %6d.\n", p->lClasses.nItems, nChanges, Ivy_FraigCountPairsClasses(p) ); + Ivy_FraigSavePattern1( p ); + Ivy_FraigAssignDist1( p, p->pPatWords ); + Ivy_FraigSimulateOne( p ); + nChanges = Ivy_FraigRefineClasses( p ); + if ( p->pManFraig->pData ) + return; +//printf( "Refined classes = %5d. Changes = %4d. Pairs = %6d.\n", p->lClasses.nItems, nChanges, Ivy_FraigCountPairsClasses(p) ); + // refine classes by random simulation + do { + Ivy_FraigAssignRandom( p ); + Ivy_FraigSimulateOne( p ); + nClasses = p->lClasses.nItems; + nChanges = Ivy_FraigRefineClasses( p ); + if ( p->pManFraig->pData ) + return; +//printf( "Refined classes = %5d. Changes = %4d. Pairs = %6d.\n", p->lClasses.nItems, nChanges, Ivy_FraigCountPairsClasses(p) ); + } while ( (double)nChanges / nClasses > p->pParams->dSimSatur ); +// Ivy_FraigPrintSimClasses( p ); +} + + + +/**Function************************************************************* + + Synopsis [Cleans pattern scores.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigCleanPatScores( Ivy_FraigMan_t * p ) +{ + int i, nLimit = p->nSimWords * 32; + for ( i = 0; i < nLimit; i++ ) + p->pPatScores[i] = 0; +} + +/**Function************************************************************* + + Synopsis [Adds to pattern scores.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigAddToPatScores( Ivy_FraigMan_t * p, Ivy_Obj_t * pClass, Ivy_Obj_t * pClassNew ) +{ + Ivy_FraigSim_t * pSims0, * pSims1; + unsigned uDiff; + int i, w; + // get hold of the simulation information + pSims0 = Ivy_ObjSim(pClass); + pSims1 = Ivy_ObjSim(pClassNew); + // iterate through the differences and record the score + for ( w = 0; w < p->nSimWords; w++ ) + { + uDiff = pSims0->pData[w] ^ pSims1->pData[w]; + if ( uDiff == 0 ) + continue; + for ( i = 0; i < 32; i++ ) + if ( uDiff & ( 1 << i ) ) + p->pPatScores[w*32+i]++; + } +} + +/**Function************************************************************* + + Synopsis [Selects the best pattern.] + + Description [Returns 1 if such pattern is found.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FraigSelectBestPat( Ivy_FraigMan_t * p ) +{ + Ivy_FraigSim_t * pSims; + Ivy_Obj_t * pObj; + int i, nLimit = p->nSimWords * 32, MaxScore = 0, BestPat = -1; + for ( i = 1; i < nLimit; i++ ) + { + if ( MaxScore < p->pPatScores[i] ) + { + MaxScore = p->pPatScores[i]; + BestPat = i; + } + } + if ( MaxScore == 0 ) + return 0; +// if ( MaxScore > p->pParams->MaxScore ) +// printf( "Max score is %3d. ", MaxScore ); + // copy the best pattern into the selected pattern + memset( p->pPatWords, 0, sizeof(unsigned) * p->nPatWords ); + Ivy_ManForEachPi( p->pManAig, pObj, i ) + { + pSims = Ivy_ObjSim(pObj); + if ( Ivy_InfoHasBit(pSims->pData, BestPat) ) + Ivy_InfoSetBit(p->pPatWords, i); + } + return MaxScore; +} + +/**Function************************************************************* + + Synopsis [Resimulates fraiging manager after finding a counter-example.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigResimulate( Ivy_FraigMan_t * p ) +{ + int nChanges; + Ivy_FraigAssignDist1( p, p->pPatWords ); + Ivy_FraigSimulateOne( p ); + if ( p->pParams->fPatScores ) + Ivy_FraigCleanPatScores( p ); + nChanges = Ivy_FraigRefineClasses( p ); + if ( p->pManFraig->pData ) + return; + if ( nChanges < 1 ) + printf( "Error: A counter-example did not refine classes!\n" ); + assert( nChanges >= 1 ); +//printf( "Refined classes! = %5d. Changes = %4d.\n", p->lClasses.nItems, nChanges ); + if ( !p->pParams->fPatScores ) + return; + + // perform additional simulation using dist1 patterns derived from successful patterns + while ( Ivy_FraigSelectBestPat(p) > p->pParams->MaxScore ) + { + Ivy_FraigAssignDist1( p, p->pPatWords ); + Ivy_FraigSimulateOne( p ); + Ivy_FraigCleanPatScores( p ); + nChanges = Ivy_FraigRefineClasses( p ); + if ( p->pManFraig->pData ) + return; +//printf( "Refined class!!! = %5d. Changes = %4d. Pairs = %6d.\n", p->lClasses.nItems, nChanges, Ivy_FraigCountPairsClasses(p) ); + if ( nChanges == 0 ) + break; + } +} + + +/**Function************************************************************* + + Synopsis [Prints the status of the miter.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigMiterPrint( Ivy_Man_t * pNtk, char * pString, int clk, int fVerbose ) +{ + if ( !fVerbose ) + return; + printf( "Nodes = %7d. Levels = %4d. ", Ivy_ManNodeNum(pNtk), Ivy_ManLevels(pNtk) ); + PRT( pString, clock() - clk ); +} + +/**Function************************************************************* + + Synopsis [Reports the status of the miter.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FraigMiterStatus( Ivy_Man_t * pMan ) +{ + Ivy_Obj_t * pObj, * pObjNew; + int i, CountConst0 = 0, CountNonConst0 = 0, CountUndecided = 0; + if ( pMan->pData ) + return 0; + Ivy_ManForEachPo( pMan, pObj, i ) + { + pObjNew = Ivy_ObjChild0(pObj); + // check if the output is constant 1 + if ( pObjNew == pMan->pConst1 ) + { + CountNonConst0++; + continue; + } + // check if the output is constant 0 + if ( pObjNew == Ivy_Not(pMan->pConst1) ) + { + CountConst0++; + continue; + } + // check if the output can be constant 0 + if ( Ivy_Regular(pObjNew)->fPhase != (unsigned)Ivy_IsComplement(pObjNew) ) + { + CountNonConst0++; + continue; + } + CountUndecided++; + } +/* + if ( p->pParams->fVerbose ) + { + printf( "Miter has %d outputs. ", Ivy_ManPoNum(p->pManAig) ); + printf( "Const0 = %d. ", CountConst0 ); + printf( "NonConst0 = %d. ", CountNonConst0 ); + printf( "Undecided = %d. ", CountUndecided ); + printf( "\n" ); + } +*/ + if ( CountNonConst0 ) + return 0; + if ( CountUndecided ) + return -1; + return 1; +} + +/**Function************************************************************* + + Synopsis [Tries to prove each output of the miter until encountering a sat output.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigMiterProve( Ivy_FraigMan_t * p ) +{ + Ivy_Obj_t * pObj, * pObjNew; + int i, RetValue, clk = clock(); + int fVerbose = 0; + Ivy_ManForEachPo( p->pManAig, pObj, i ) + { + if ( i && fVerbose ) + { + PRT( "Time", clock() -clk ); + } + pObjNew = Ivy_ObjChild0Equiv(pObj); + // check if the output is constant 1 + if ( pObjNew == p->pManFraig->pConst1 ) + { + if ( fVerbose ) + printf( "Output %2d (out of %2d) is constant 1. ", i, Ivy_ManPoNum(p->pManAig) ); + // assing constant 0 model + p->pManFraig->pData = ALLOC( int, Ivy_ManPiNum(p->pManFraig) ); + memset( p->pManFraig->pData, 0, sizeof(int) * Ivy_ManPiNum(p->pManFraig) ); + break; + } + // check if the output is constant 0 + if ( pObjNew == Ivy_Not(p->pManFraig->pConst1) ) + { + if ( fVerbose ) + printf( "Output %2d (out of %2d) is already constant 0. ", i, Ivy_ManPoNum(p->pManAig) ); + continue; + } + // check if the output can be constant 0 + if ( Ivy_Regular(pObjNew)->fPhase != (unsigned)Ivy_IsComplement(pObjNew) ) + { + if ( fVerbose ) + printf( "Output %2d (out of %2d) cannot be constant 0. ", i, Ivy_ManPoNum(p->pManAig) ); + // assing constant 0 model + p->pManFraig->pData = ALLOC( int, Ivy_ManPiNum(p->pManFraig) ); + memset( p->pManFraig->pData, 0, sizeof(int) * Ivy_ManPiNum(p->pManFraig) ); + break; + } +/* + // check the representative of this node + pRepr = Ivy_ObjClassNodeRepr(Ivy_ObjFanin0(pObj)); + if ( Ivy_Regular(pRepr) != p->pManAig->pConst1 ) + printf( "Representative is not constant 1.\n" ); + else + printf( "Representative is constant 1.\n" ); +*/ + // try to prove the output constant 0 + RetValue = Ivy_FraigNodeIsConst( p, Ivy_Regular(pObjNew) ); + if ( RetValue == 1 ) // proved equivalent + { + if ( fVerbose ) + printf( "Output %2d (out of %2d) was proved constant 0. ", i, Ivy_ManPoNum(p->pManAig) ); + // set the constant miter + Ivy_ObjFanin0(pObj)->pEquiv = Ivy_NotCond( p->pManFraig->pConst1, !Ivy_ObjFaninC0(pObj) ); + continue; + } + if ( RetValue == -1 ) // failed + { + if ( fVerbose ) + printf( "Output %2d (out of %2d) has timed out at %d backtracks. ", i, Ivy_ManPoNum(p->pManAig), p->pParams->nBTLimitMiter ); + continue; + } + // proved satisfiable + if ( fVerbose ) + printf( "Output %2d (out of %2d) was proved NOT a constant 0. ", i, Ivy_ManPoNum(p->pManAig) ); + // create the model + p->pManFraig->pData = Ivy_FraigCreateModel(p); + break; + } + if ( fVerbose ) + { + PRT( "Time", clock() -clk ); + } +} + +/**Function************************************************************* + + Synopsis [Performs fraiging for the internal nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigSweep( Ivy_FraigMan_t * p ) +{ + Ivy_Obj_t * pObj;//, * pTemp; + int i, k = 0; +p->nClassesZero = p->lClasses.pHead? (Ivy_ObjIsConst1(p->lClasses.pHead) ? Ivy_FraigCountClassNodes(p->lClasses.pHead) : 0) : 0; +p->nClassesBeg = p->lClasses.nItems; + // duplicate internal nodes + p->pProgress = Extra_ProgressBarStart( stdout, Ivy_ManNodeNum(p->pManAig) ); + Ivy_ManForEachNode( p->pManAig, pObj, i ) + { + Extra_ProgressBarUpdate( p->pProgress, k++, NULL ); + // default to simple strashing if simulation detected a counter-example for a PO + if ( p->pManFraig->pData ) + pObj->pEquiv = Ivy_And( p->pManFraig, Ivy_ObjChild0Equiv(pObj), Ivy_ObjChild1Equiv(pObj) ); + else + pObj->pEquiv = Ivy_FraigAnd( p, pObj ); + assert( pObj->pEquiv != NULL ); +// pTemp = Ivy_Regular(pObj->pEquiv); +// assert( Ivy_Regular(pObj->pEquiv)->Type ); + } + Extra_ProgressBarStop( p->pProgress ); +p->nClassesEnd = p->lClasses.nItems; + // try to prove the outputs of the miter + p->nNodesMiter = Ivy_ManNodeNum(p->pManFraig); +// Ivy_FraigMiterStatus( p->pManFraig ); + if ( p->pParams->fProve && p->pManFraig->pData == NULL ) + Ivy_FraigMiterProve( p ); + // add the POs + Ivy_ManForEachPo( p->pManAig, pObj, i ) + Ivy_ObjCreatePo( p->pManFraig, Ivy_ObjChild0Equiv(pObj) ); + // clean the old manager + Ivy_ManForEachObj( p->pManAig, pObj, i ) + pObj->pFanout = pObj->pNextFan0 = pObj->pNextFan1 = pObj->pPrevFan0 = pObj->pPrevFan1 = NULL; + // clean the new manager + Ivy_ManForEachObj( p->pManFraig, pObj, i ) + { + if ( Ivy_ObjFaninVec(pObj) ) + Vec_PtrFree( Ivy_ObjFaninVec(pObj) ); + pObj->pNextFan0 = pObj->pNextFan1 = NULL; + } + // remove dangling nodes + Ivy_ManCleanup( p->pManFraig ); + // clean up the class marks + Ivy_FraigForEachEquivClass( p->lClasses.pHead, pObj ) + pObj->fMarkA = 0; +} + +/**Function************************************************************* + + Synopsis [Performs fraiging for one node.] + + Description [Returns the fraiged node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_FraigAnd( Ivy_FraigMan_t * p, Ivy_Obj_t * pObjOld ) +{ + Ivy_Obj_t * pObjNew, * pFanin0New, * pFanin1New, * pObjReprNew; + int RetValue; + // get the fraiged fanins + pFanin0New = Ivy_ObjChild0Equiv(pObjOld); + pFanin1New = Ivy_ObjChild1Equiv(pObjOld); + // get the candidate fraig node + pObjNew = Ivy_And( p->pManFraig, pFanin0New, pFanin1New ); + // get representative of this class + if ( Ivy_ObjClassNodeRepr(pObjOld) == NULL || // this is a unique node + (!p->pParams->fDoSparse && Ivy_ObjClassNodeRepr(pObjOld) == p->pManAig->pConst1) ) // this is a sparse node + { + assert( Ivy_Regular(pFanin0New) != Ivy_Regular(pFanin1New) ); + assert( pObjNew != Ivy_Regular(pFanin0New) ); + assert( pObjNew != Ivy_Regular(pFanin1New) ); + return pObjNew; + } + // get the fraiged representative + pObjReprNew = Ivy_ObjFraig(Ivy_ObjClassNodeRepr(pObjOld)); + // if the fraiged nodes are the same return + if ( Ivy_Regular(pObjNew) == Ivy_Regular(pObjReprNew) ) + return pObjNew; + assert( Ivy_Regular(pObjNew) != Ivy_ManConst1(p->pManFraig) ); +// printf( "Node = %d. Repr = %d.\n", pObjOld->Id, Ivy_ObjClassNodeRepr(pObjOld)->Id ); + + // they are different (the counter-example is in p->pPatWords) + RetValue = Ivy_FraigNodesAreEquiv( p, Ivy_Regular(pObjReprNew), Ivy_Regular(pObjNew) ); + if ( RetValue == 1 ) // proved equivalent + { + // mark the class as proved + if ( Ivy_ObjClassNodeNext(pObjOld) == NULL ) + Ivy_ObjClassNodeRepr(pObjOld)->fMarkA = 1; + return Ivy_NotCond( pObjReprNew, pObjOld->fPhase ^ Ivy_ObjClassNodeRepr(pObjOld)->fPhase ); + } + if ( RetValue == -1 ) // failed + return pObjNew; + // simulate the counter-example and return the new node + Ivy_FraigResimulate( p ); + return pObjNew; +} + +/**Function************************************************************* + + Synopsis [Prints variable activity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigPrintActivity( Ivy_FraigMan_t * p ) +{ + int i; + for ( i = 0; i < p->nSatVars; i++ ) + printf( "%d %.3f ", i, p->pSat->activity[i] ); + printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [Runs equivalence test for the two nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FraigNodesAreEquiv( Ivy_FraigMan_t * p, Ivy_Obj_t * pOld, Ivy_Obj_t * pNew ) +{ + int pLits[4], RetValue, RetValue1, nBTLimit, clk, clk2 = clock(); + + // make sure the nodes are not complemented + assert( !Ivy_IsComplement(pNew) ); + assert( !Ivy_IsComplement(pOld) ); + assert( pNew != pOld ); + + // if at least one of the nodes is a failed node, perform adjustments: + // if the backtrack limit is small, simply skip this node + // if the backtrack limit is > 10, take the quare root of the limit + nBTLimit = p->pParams->nBTLimitNode; + if ( nBTLimit > 0 && (pOld->fFailTfo || pNew->fFailTfo) ) + { + p->nSatFails++; + // fail immediately +// return -1; + if ( nBTLimit <= 10 ) + return -1; + nBTLimit = (int)pow(nBTLimit, 0.7); + } + p->nSatCalls++; + + // make sure the solver is allocated and has enough variables + if ( p->pSat == NULL ) + { + p->pSat = sat_solver_new(); + p->nSatVars = 1; + sat_solver_setnvars( p->pSat, 1000 ); + } + + // if the nodes do not have SAT variables, allocate them + Ivy_FraigNodeAddToSolver( p, pOld, pNew ); + + // prepare variable activity + Ivy_FraigSetActivityFactors( p, pOld, pNew ); + + // solve under assumptions + // A = 1; B = 0 OR A = 1; B = 1 +clk = clock(); + pLits[0] = toLitCond( Ivy_ObjSatNum(pOld), 0 ); + pLits[1] = toLitCond( Ivy_ObjSatNum(pNew), pOld->fPhase == pNew->fPhase ); +//Sat_SolverWriteDimacs( p->pSat, "temp.cnf", pLits, pLits + 2, 1 ); + RetValue1 = sat_solver_solve( p->pSat, pLits, pLits + 2, + (sint64)nBTLimit, (sint64)0, + p->nBTLimitGlobal, p->nInsLimitGlobal ); +p->timeSat += clock() - clk; + if ( RetValue1 == l_False ) + { +p->timeSatUnsat += clock() - clk; + pLits[0] = lit_neg( pLits[0] ); + pLits[1] = lit_neg( pLits[1] ); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + 2 ); + assert( RetValue ); + // continue solving the other implication + p->nSatCallsUnsat++; + } + else if ( RetValue1 == l_True ) + { +p->timeSatSat += clock() - clk; + Ivy_FraigSavePattern( p ); + p->nSatCallsSat++; + return 0; + } + else // if ( RetValue1 == l_Undef ) + { +p->timeSatFail += clock() - clk; + // mark the node as the failed node + if ( pOld != p->pManFraig->pConst1 ) + pOld->fFailTfo = 1; + pNew->fFailTfo = 1; + p->nSatFailsReal++; + return -1; + } + + // if the old node was constant 0, we already know the answer + if ( pOld == p->pManFraig->pConst1 ) + { + p->nSatProof++; + return 1; + } + + // solve under assumptions + // A = 0; B = 1 OR A = 0; B = 0 +clk = clock(); + pLits[0] = toLitCond( Ivy_ObjSatNum(pOld), 1 ); + pLits[1] = toLitCond( Ivy_ObjSatNum(pNew), pOld->fPhase ^ pNew->fPhase ); + RetValue1 = sat_solver_solve( p->pSat, pLits, pLits + 2, + (sint64)nBTLimit, (sint64)0, + p->nBTLimitGlobal, p->nInsLimitGlobal ); +p->timeSat += clock() - clk; + if ( RetValue1 == l_False ) + { +p->timeSatUnsat += clock() - clk; + pLits[0] = lit_neg( pLits[0] ); + pLits[1] = lit_neg( pLits[1] ); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + 2 ); + assert( RetValue ); + p->nSatCallsUnsat++; + } + else if ( RetValue1 == l_True ) + { +p->timeSatSat += clock() - clk; + Ivy_FraigSavePattern( p ); + p->nSatCallsSat++; + return 0; + } + else // if ( RetValue1 == l_Undef ) + { +p->timeSatFail += clock() - clk; + // mark the node as the failed node + pOld->fFailTfo = 1; + pNew->fFailTfo = 1; + p->nSatFailsReal++; + return -1; + } +/* + // check BDD proof + { + int RetVal; + PRT( "Sat", clock() - clk2 ); + clk2 = clock(); + RetVal = Ivy_FraigNodesAreEquivBdd( pOld, pNew ); +// printf( "%d ", RetVal ); + assert( RetVal ); + PRT( "Bdd", clock() - clk2 ); + printf( "\n" ); + } +*/ + // return SAT proof + p->nSatProof++; + return 1; +} + +/**Function************************************************************* + + Synopsis [Runs equivalence test for one node.] + + Description [Returns the fraiged node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FraigNodeIsConst( Ivy_FraigMan_t * p, Ivy_Obj_t * pNew ) +{ + int pLits[2], RetValue1, RetValue, clk; + + // make sure the nodes are not complemented + assert( !Ivy_IsComplement(pNew) ); + assert( pNew != p->pManFraig->pConst1 ); + p->nSatCalls++; + + // make sure the solver is allocated and has enough variables + if ( p->pSat == NULL ) + { + p->pSat = sat_solver_new(); + p->nSatVars = 1; + sat_solver_setnvars( p->pSat, 1000 ); + } + + // if the nodes do not have SAT variables, allocate them + Ivy_FraigNodeAddToSolver( p, NULL, pNew ); + + // prepare variable activity + Ivy_FraigSetActivityFactors( p, NULL, pNew ); + + // solve under assumptions +clk = clock(); + pLits[0] = toLitCond( Ivy_ObjSatNum(pNew), pNew->fPhase ); + RetValue1 = sat_solver_solve( p->pSat, pLits, pLits + 1, + (sint64)p->pParams->nBTLimitMiter, (sint64)0, + p->nBTLimitGlobal, p->nInsLimitGlobal ); +p->timeSat += clock() - clk; + if ( RetValue1 == l_False ) + { +p->timeSatUnsat += clock() - clk; + pLits[0] = lit_neg( pLits[0] ); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + 1 ); + assert( RetValue ); + // continue solving the other implication + p->nSatCallsUnsat++; + } + else if ( RetValue1 == l_True ) + { +p->timeSatSat += clock() - clk; + if ( p->pPatWords ) + Ivy_FraigSavePattern( p ); + p->nSatCallsSat++; + return 0; + } + else // if ( RetValue1 == l_Undef ) + { +p->timeSatFail += clock() - clk; + // mark the node as the failed node + pNew->fFailTfo = 1; + p->nSatFailsReal++; + return -1; + } + + // return SAT proof + p->nSatProof++; + return 1; +} + +/**Function************************************************************* + + Synopsis [Addes clauses to the solver.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigAddClausesMux( Ivy_FraigMan_t * p, Ivy_Obj_t * pNode ) +{ + Ivy_Obj_t * pNodeI, * pNodeT, * pNodeE; + int pLits[4], RetValue, VarF, VarI, VarT, VarE, fCompT, fCompE; + + assert( !Ivy_IsComplement( pNode ) ); + assert( Ivy_ObjIsMuxType( pNode ) ); + // get nodes (I = if, T = then, E = else) + pNodeI = Ivy_ObjRecognizeMux( pNode, &pNodeT, &pNodeE ); + // get the variable numbers + VarF = Ivy_ObjSatNum(pNode); + VarI = Ivy_ObjSatNum(pNodeI); + VarT = Ivy_ObjSatNum(Ivy_Regular(pNodeT)); + VarE = Ivy_ObjSatNum(Ivy_Regular(pNodeE)); + // get the complementation flags + fCompT = Ivy_IsComplement(pNodeT); + fCompE = Ivy_IsComplement(pNodeE); + + // f = ITE(i, t, e) + + // i' + t' + f + // i' + t + f' + // i + e' + f + // i + e + f' + + // create four clauses + pLits[0] = toLitCond(VarI, 1); + pLits[1] = toLitCond(VarT, 1^fCompT); + pLits[2] = toLitCond(VarF, 0); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + 3 ); + assert( RetValue ); + pLits[0] = toLitCond(VarI, 1); + pLits[1] = toLitCond(VarT, 0^fCompT); + pLits[2] = toLitCond(VarF, 1); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + 3 ); + assert( RetValue ); + pLits[0] = toLitCond(VarI, 0); + pLits[1] = toLitCond(VarE, 1^fCompE); + pLits[2] = toLitCond(VarF, 0); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + 3 ); + assert( RetValue ); + pLits[0] = toLitCond(VarI, 0); + pLits[1] = toLitCond(VarE, 0^fCompE); + pLits[2] = toLitCond(VarF, 1); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + 3 ); + assert( RetValue ); + + // two additional clauses + // t' & e' -> f' + // t & e -> f + + // t + e + f' + // t' + e' + f + + if ( VarT == VarE ) + { +// assert( fCompT == !fCompE ); + return; + } + + pLits[0] = toLitCond(VarT, 0^fCompT); + pLits[1] = toLitCond(VarE, 0^fCompE); + pLits[2] = toLitCond(VarF, 1); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + 3 ); + assert( RetValue ); + pLits[0] = toLitCond(VarT, 1^fCompT); + pLits[1] = toLitCond(VarE, 1^fCompE); + pLits[2] = toLitCond(VarF, 0); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + 3 ); + assert( RetValue ); +} + +/**Function************************************************************* + + Synopsis [Addes clauses to the solver.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigAddClausesSuper( Ivy_FraigMan_t * p, Ivy_Obj_t * pNode, Vec_Ptr_t * vSuper ) +{ + Ivy_Obj_t * pFanin; + int * pLits, nLits, RetValue, i; + assert( !Ivy_IsComplement(pNode) ); + assert( Ivy_ObjIsNode( pNode ) ); + // create storage for literals + nLits = Vec_PtrSize(vSuper) + 1; + pLits = ALLOC( int, nLits ); + // suppose AND-gate is A & B = C + // add !A => !C or A + !C + Vec_PtrForEachEntry( vSuper, pFanin, i ) + { + pLits[0] = toLitCond(Ivy_ObjSatNum(Ivy_Regular(pFanin)), Ivy_IsComplement(pFanin)); + pLits[1] = toLitCond(Ivy_ObjSatNum(pNode), 1); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + 2 ); + assert( RetValue ); + } + // add A & B => C or !A + !B + C + Vec_PtrForEachEntry( vSuper, pFanin, i ) + pLits[i] = toLitCond(Ivy_ObjSatNum(Ivy_Regular(pFanin)), !Ivy_IsComplement(pFanin)); + pLits[nLits-1] = toLitCond(Ivy_ObjSatNum(pNode), 0); + RetValue = sat_solver_addclause( p->pSat, pLits, pLits + nLits ); + assert( RetValue ); + free( pLits ); +} + +/**Function************************************************************* + + Synopsis [Collects the supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigCollectSuper_rec( Ivy_Obj_t * pObj, Vec_Ptr_t * vSuper, int fFirst, int fUseMuxes ) +{ + // if the new node is complemented or a PI, another gate begins + if ( Ivy_IsComplement(pObj) || Ivy_ObjIsPi(pObj) || (!fFirst && Ivy_ObjRefs(pObj) > 1) || + (fUseMuxes && Ivy_ObjIsMuxType(pObj)) ) + { + Vec_PtrPushUnique( vSuper, pObj ); + return; + } + // go through the branches + Ivy_FraigCollectSuper_rec( Ivy_ObjChild0(pObj), vSuper, 0, fUseMuxes ); + Ivy_FraigCollectSuper_rec( Ivy_ObjChild1(pObj), vSuper, 0, fUseMuxes ); +} + +/**Function************************************************************* + + Synopsis [Collects the supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Ivy_FraigCollectSuper( Ivy_Obj_t * pObj, int fUseMuxes ) +{ + Vec_Ptr_t * vSuper; + assert( !Ivy_IsComplement(pObj) ); + assert( !Ivy_ObjIsPi(pObj) ); + vSuper = Vec_PtrAlloc( 4 ); + Ivy_FraigCollectSuper_rec( pObj, vSuper, 1, fUseMuxes ); + return vSuper; +} + +/**Function************************************************************* + + Synopsis [Updates the solver clause database.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigObjAddToFrontier( Ivy_FraigMan_t * p, Ivy_Obj_t * pObj, Vec_Ptr_t * vFrontier ) +{ + assert( !Ivy_IsComplement(pObj) ); + if ( Ivy_ObjSatNum(pObj) ) + return; + assert( Ivy_ObjSatNum(pObj) == 0 ); + assert( Ivy_ObjFaninVec(pObj) == NULL ); + if ( Ivy_ObjIsConst1(pObj) ) + return; +//printf( "Assigning node %d number %d\n", pObj->Id, p->nSatVars ); + Ivy_ObjSetSatNum( pObj, p->nSatVars++ ); + if ( Ivy_ObjIsNode(pObj) ) + Vec_PtrPush( vFrontier, pObj ); +} + +/**Function************************************************************* + + Synopsis [Updates the solver clause database.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_FraigNodeAddToSolver( Ivy_FraigMan_t * p, Ivy_Obj_t * pOld, Ivy_Obj_t * pNew ) +{ + Vec_Ptr_t * vFrontier, * vFanins; + Ivy_Obj_t * pNode, * pFanin; + int i, k, fUseMuxes = 1; + assert( pOld || pNew ); + // quit if CNF is ready + if ( (!pOld || Ivy_ObjFaninVec(pOld)) && (!pNew || Ivy_ObjFaninVec(pNew)) ) + return; + // start the frontier + vFrontier = Vec_PtrAlloc( 100 ); + if ( pOld ) Ivy_FraigObjAddToFrontier( p, pOld, vFrontier ); + if ( pNew ) Ivy_FraigObjAddToFrontier( p, pNew, vFrontier ); + // explore nodes in the frontier + Vec_PtrForEachEntry( vFrontier, pNode, i ) + { + // create the supergate + assert( Ivy_ObjSatNum(pNode) ); + assert( Ivy_ObjFaninVec(pNode) == NULL ); + if ( fUseMuxes && Ivy_ObjIsMuxType(pNode) ) + { + vFanins = Vec_PtrAlloc( 4 ); + Vec_PtrPushUnique( vFanins, Ivy_ObjFanin0( Ivy_ObjFanin0(pNode) ) ); + Vec_PtrPushUnique( vFanins, Ivy_ObjFanin0( Ivy_ObjFanin1(pNode) ) ); + Vec_PtrPushUnique( vFanins, Ivy_ObjFanin1( Ivy_ObjFanin0(pNode) ) ); + Vec_PtrPushUnique( vFanins, Ivy_ObjFanin1( Ivy_ObjFanin1(pNode) ) ); + Vec_PtrForEachEntry( vFanins, pFanin, k ) + Ivy_FraigObjAddToFrontier( p, Ivy_Regular(pFanin), vFrontier ); + Ivy_FraigAddClausesMux( p, pNode ); + } + else + { + vFanins = Ivy_FraigCollectSuper( pNode, fUseMuxes ); + Vec_PtrForEachEntry( vFanins, pFanin, k ) + Ivy_FraigObjAddToFrontier( p, Ivy_Regular(pFanin), vFrontier ); + Ivy_FraigAddClausesSuper( p, pNode, vFanins ); + } + assert( Vec_PtrSize(vFanins) > 1 ); + Ivy_ObjSetFaninVec( pNode, vFanins ); + } + Vec_PtrFree( vFrontier ); +} + +/**Function************************************************************* + + Synopsis [Sets variable activities in the cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FraigSetActivityFactors_rec( Ivy_FraigMan_t * p, Ivy_Obj_t * pObj, int LevelMin, int LevelMax ) +{ + Vec_Ptr_t * vFanins; + Ivy_Obj_t * pFanin; + int i, Counter = 0; + assert( !Ivy_IsComplement(pObj) ); + assert( Ivy_ObjSatNum(pObj) ); + // skip visited variables + if ( Ivy_ObjIsTravIdCurrent(p->pManFraig, pObj) ) + return 0; + Ivy_ObjSetTravIdCurrent(p->pManFraig, pObj); + // add the PI to the list + if ( pObj->Level <= (unsigned)LevelMin || Ivy_ObjIsPi(pObj) ) + return 0; + // set the factor of this variable + // (LevelMax-LevelMin) / (pObj->Level-LevelMin) = p->pParams->dActConeBumpMax / ThisBump + p->pSat->factors[Ivy_ObjSatNum(pObj)] = p->pParams->dActConeBumpMax * (pObj->Level - LevelMin)/(LevelMax - LevelMin); + veci_push(&p->pSat->act_vars, Ivy_ObjSatNum(pObj)); + // explore the fanins + vFanins = Ivy_ObjFaninVec( pObj ); + Vec_PtrForEachEntry( vFanins, pFanin, i ) + Counter += Ivy_FraigSetActivityFactors_rec( p, Ivy_Regular(pFanin), LevelMin, LevelMax ); + return 1 + Counter; +} + +/**Function************************************************************* + + Synopsis [Sets variable activities in the cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FraigSetActivityFactors( Ivy_FraigMan_t * p, Ivy_Obj_t * pOld, Ivy_Obj_t * pNew ) +{ + int clk, LevelMin, LevelMax; + assert( pOld || pNew ); +clk = clock(); + // reset the active variables + veci_resize(&p->pSat->act_vars, 0); + // prepare for traversal + Ivy_ManIncrementTravId( p->pManFraig ); + // determine the min and max level to visit + assert( p->pParams->dActConeRatio > 0 && p->pParams->dActConeRatio < 1 ); + LevelMax = IVY_MAX( (pNew ? pNew->Level : 0), (pOld ? pOld->Level : 0) ); + LevelMin = (int)(LevelMax * (1.0 - p->pParams->dActConeRatio)); + // traverse + if ( pOld && !Ivy_ObjIsConst1(pOld) ) + Ivy_FraigSetActivityFactors_rec( p, pOld, LevelMin, LevelMax ); + if ( pNew && !Ivy_ObjIsConst1(pNew) ) + Ivy_FraigSetActivityFactors_rec( p, pNew, LevelMin, LevelMax ); +//Ivy_FraigPrintActivity( p ); +p->timeTrav += clock() - clk; + return 1; +} + + + +#include "cuddInt.h" + +/**Function************************************************************* + + Synopsis [Checks equivalence using BDDs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Ivy_FraigNodesAreEquivBdd_int( DdManager * dd, DdNode * bFunc, Vec_Ptr_t * vFront, int Level ) +{ + DdNode ** pFuncs; + DdNode * bFuncNew; + Vec_Ptr_t * vTemp; + Ivy_Obj_t * pObj, * pFanin; + int i, NewSize; + // create new frontier + vTemp = Vec_PtrAlloc( 100 ); + Vec_PtrForEachEntry( vFront, pObj, i ) + { + if ( (int)pObj->Level != Level ) + { + pObj->fMarkB = 1; + pObj->TravId = Vec_PtrSize(vTemp); + Vec_PtrPush( vTemp, pObj ); + continue; + } + + pFanin = Ivy_ObjFanin0(pObj); + if ( pFanin->fMarkB == 0 ) + { + pFanin->fMarkB = 1; + pFanin->TravId = Vec_PtrSize(vTemp); + Vec_PtrPush( vTemp, pFanin ); + } + + pFanin = Ivy_ObjFanin1(pObj); + if ( pFanin->fMarkB == 0 ) + { + pFanin->fMarkB = 1; + pFanin->TravId = Vec_PtrSize(vTemp); + Vec_PtrPush( vTemp, pFanin ); + } + } + // collect the permutation + NewSize = IVY_MAX(dd->size, Vec_PtrSize(vTemp)); + pFuncs = ALLOC( DdNode *, NewSize ); + Vec_PtrForEachEntry( vFront, pObj, i ) + { + if ( (int)pObj->Level != Level ) + pFuncs[i] = Cudd_bddIthVar( dd, pObj->TravId ); + else + pFuncs[i] = Cudd_bddAnd( dd, + Cudd_NotCond( Cudd_bddIthVar(dd, Ivy_ObjFanin0(pObj)->TravId), Ivy_ObjFaninC0(pObj) ), + Cudd_NotCond( Cudd_bddIthVar(dd, Ivy_ObjFanin1(pObj)->TravId), Ivy_ObjFaninC1(pObj) ) ); + Cudd_Ref( pFuncs[i] ); + } + // add the remaining vars + assert( NewSize == dd->size ); + for ( i = Vec_PtrSize(vFront); i < dd->size; i++ ) + { + pFuncs[i] = Cudd_bddIthVar( dd, i ); + Cudd_Ref( pFuncs[i] ); + } + + // create new + bFuncNew = Cudd_bddVectorCompose( dd, bFunc, pFuncs ); Cudd_Ref( bFuncNew ); + // clean trav Id + Vec_PtrForEachEntry( vTemp, pObj, i ) + { + pObj->fMarkB = 0; + pObj->TravId = 0; + } + // deref + for ( i = 0; i < dd->size; i++ ) + Cudd_RecursiveDeref( dd, pFuncs[i] ); + free( pFuncs ); + + free( vFront->pArray ); + *vFront = *vTemp; + + vTemp->nCap = vTemp->nSize = 0; + vTemp->pArray = NULL; + Vec_PtrFree( vTemp ); + + Cudd_Deref( bFuncNew ); + return bFuncNew; +} + +/**Function************************************************************* + + Synopsis [Checks equivalence using BDDs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_FraigNodesAreEquivBdd( Ivy_Obj_t * pObj1, Ivy_Obj_t * pObj2 ) +{ + static DdManager * dd = NULL; + DdNode * bFunc, * bTemp; + Vec_Ptr_t * vFront; + Ivy_Obj_t * pObj; + int i, RetValue, Iter, Level; + // start the manager + if ( dd == NULL ) + dd = Cudd_Init( 50, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 ); + // create front + vFront = Vec_PtrAlloc( 100 ); + Vec_PtrPush( vFront, pObj1 ); + Vec_PtrPush( vFront, pObj2 ); + // get the function + bFunc = Cudd_bddXor( dd, Cudd_bddIthVar(dd,0), Cudd_bddIthVar(dd,1) ); Cudd_Ref( bFunc ); + bFunc = Cudd_NotCond( bFunc, pObj1->fPhase != pObj2->fPhase ); + // try running BDDs + for ( Iter = 0; ; Iter++ ) + { + // find max level + Level = 0; + Vec_PtrForEachEntry( vFront, pObj, i ) + if ( Level < (int)pObj->Level ) + Level = (int)pObj->Level; + if ( Level == 0 ) + break; + bFunc = Ivy_FraigNodesAreEquivBdd_int( dd, bTemp = bFunc, vFront, Level ); Cudd_Ref( bFunc ); + Cudd_RecursiveDeref( dd, bTemp ); + if ( bFunc == Cudd_ReadLogicZero(dd) ) // proved + {printf( "%d", Iter ); break;} + if ( Cudd_DagSize(bFunc) > 1000 ) + {printf( "b" ); break;} + if ( dd->size > 120 ) + {printf( "s" ); break;} + if ( Iter > 50 ) + {printf( "i" ); break;} + } + if ( bFunc == Cudd_ReadLogicZero(dd) ) // unsat + RetValue = 1; + else if ( Level == 0 ) // sat + RetValue = 0; + else + RetValue = -1; // spaceout/timeout + Cudd_RecursiveDeref( dd, bFunc ); + Vec_PtrFree( vFront ); + return RetValue; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyHaig.c b/abc_with_bb_support/src/aig/ivy/ivyHaig.c new file mode 100644 index 000000000..7174563d4 --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyHaig.c @@ -0,0 +1,530 @@ +/**CFile**************************************************************** + + FileName [ivyHaig.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [HAIG management procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyHaig.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/* + HAIGing rules in working AIG: + - Each node in the working AIG has a pointer to the corresponding node in HAIG + (this node is not necessarily the representative of the equivalence class of HAIG nodes) + - This pointer is complemented if the AIG node and its corresponding HAIG node have different phase + + Choice node rules in HAIG: + - Equivalent nodes are linked into a ring + - Exactly one node in the ring has fanouts (this node is called the representative) + - The pointer going from a node to the next node in the ring is complemented + if the first node is complemented, compared to the representative node of the equivalence class + - (consequence of the above) The representative node always has non-complemented pointer to the next node + - New nodes are inserted into the ring immediately after the representative node +*/ + +// returns the representative node of the given HAIG node +static inline Ivy_Obj_t * Ivy_HaigObjRepr( Ivy_Obj_t * pObj ) +{ + Ivy_Obj_t * pTemp; + assert( !Ivy_IsComplement(pObj) ); + // if the node has no equivalent node or has fanout, it is representative + if ( pObj->pEquiv == NULL || Ivy_ObjRefs(pObj) > 0 ) + return pObj; + // the node belongs to a class and is not a representative + // complemented edge (pObj->pEquiv) tells if it is complemented w.r.t. the repr + for ( pTemp = Ivy_Regular(pObj->pEquiv); pTemp != pObj; pTemp = Ivy_Regular(pTemp->pEquiv) ) + if ( Ivy_ObjRefs(pTemp) > 0 ) + break; + // return the representative node + assert( Ivy_ObjRefs(pTemp) > 0 ); + return Ivy_NotCond( pTemp, Ivy_IsComplement(pObj->pEquiv) ); +} + +// counts the number of nodes in the equivalence class +static inline int Ivy_HaigObjCountClass( Ivy_Obj_t * pObj ) +{ + Ivy_Obj_t * pTemp; + int Counter; + assert( !Ivy_IsComplement(pObj) ); + assert( Ivy_ObjRefs(pObj) > 0 ); + if ( pObj->pEquiv == NULL ) + return 1; + assert( !Ivy_IsComplement(pObj->pEquiv) ); + Counter = 1; + for ( pTemp = pObj->pEquiv; pTemp != pObj; pTemp = Ivy_Regular(pTemp->pEquiv) ) + Counter++; + return Counter; +} + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts HAIG for the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManHaigStart( Ivy_Man_t * p, int fVerbose ) +{ + Vec_Int_t * vLatches; + Ivy_Obj_t * pObj; + int i; + assert( p->pHaig == NULL ); + p->pHaig = Ivy_ManDup( p ); + + if ( fVerbose ) + { + printf( "Starting : " ); + Ivy_ManPrintStats( p->pHaig ); + } + + // collect latches of design D and set their values to be DC + vLatches = Vec_IntAlloc( 100 ); + Ivy_ManForEachLatch( p->pHaig, pObj, i ) + { + pObj->Init = IVY_INIT_DC; + Vec_IntPush( vLatches, pObj->Id ); + } + p->pHaig->pData = vLatches; +/* + { + int x; + Ivy_ManShow( p, 0, NULL ); + Ivy_ManShow( p->pHaig, 1, NULL ); + x = 0; + } +*/ +} + +/**Function************************************************************* + + Synopsis [Transfers the HAIG to the newly created manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManHaigTrasfer( Ivy_Man_t * p, Ivy_Man_t * pNew ) +{ + Ivy_Obj_t * pObj; + int i; + assert( p->pHaig != NULL ); + Ivy_ManConst1(pNew)->pEquiv = Ivy_ManConst1(p)->pEquiv; + Ivy_ManForEachPi( pNew, pObj, i ) + pObj->pEquiv = Ivy_ManPi( p, i )->pEquiv; + pNew->pHaig = p->pHaig; +} + +/**Function************************************************************* + + Synopsis [Stops HAIG for the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManHaigStop( Ivy_Man_t * p ) +{ + Ivy_Obj_t * pObj; + int i; + assert( p->pHaig != NULL ); + Vec_IntFree( p->pHaig->pData ); + Ivy_ManStop( p->pHaig ); + p->pHaig = NULL; + // remove dangling pointers to the HAIG objects + Ivy_ManForEachObj( p, pObj, i ) + pObj->pEquiv = NULL; +} + +/**Function************************************************************* + + Synopsis [Creates a new node in HAIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManHaigCreateObj( Ivy_Man_t * p, Ivy_Obj_t * pObj ) +{ + Ivy_Obj_t * pEquiv0, * pEquiv1; + assert( p->pHaig != NULL ); + assert( !Ivy_IsComplement(pObj) ); + if ( Ivy_ObjType(pObj) == IVY_BUF ) + pObj->pEquiv = Ivy_ObjChild0Equiv(pObj); + else if ( Ivy_ObjType(pObj) == IVY_LATCH ) + { +// pObj->pEquiv = Ivy_Latch( p->pHaig, Ivy_ObjChild0Equiv(pObj), pObj->Init ); + pEquiv0 = Ivy_ObjChild0Equiv(pObj); + pEquiv0 = Ivy_NotCond( Ivy_HaigObjRepr(Ivy_Regular(pEquiv0)), Ivy_IsComplement(pEquiv0) ); + pObj->pEquiv = Ivy_Latch( p->pHaig, pEquiv0, pObj->Init ); + } + else if ( Ivy_ObjType(pObj) == IVY_AND ) + { +// pObj->pEquiv = Ivy_And( p->pHaig, Ivy_ObjChild0Equiv(pObj), Ivy_ObjChild1Equiv(pObj) ); + pEquiv0 = Ivy_ObjChild0Equiv(pObj); + pEquiv0 = Ivy_NotCond( Ivy_HaigObjRepr(Ivy_Regular(pEquiv0)), Ivy_IsComplement(pEquiv0) ); + pEquiv1 = Ivy_ObjChild1Equiv(pObj); + pEquiv1 = Ivy_NotCond( Ivy_HaigObjRepr(Ivy_Regular(pEquiv1)), Ivy_IsComplement(pEquiv1) ); + pObj->pEquiv = Ivy_And( p->pHaig, pEquiv0, pEquiv1 ); + } + else assert( 0 ); + // make sure the node points to the representative +// pObj->pEquiv = Ivy_NotCond( Ivy_HaigObjRepr(Ivy_Regular(pObj->pEquiv)), Ivy_IsComplement(pObj->pEquiv) ); +} + +/**Function************************************************************* + + Synopsis [Checks if the old node is in the TFI of the new node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ObjIsInTfi_rec( Ivy_Obj_t * pObjNew, Ivy_Obj_t * pObjOld, int Levels ) +{ + if ( pObjNew == pObjOld ) + return 1; + if ( Levels == 0 || Ivy_ObjIsCi(pObjNew) || Ivy_ObjIsConst1(pObjNew) ) + return 0; + if ( Ivy_ObjIsInTfi_rec( Ivy_ObjFanin0(pObjNew), pObjOld, Levels - 1 ) ) + return 1; + if ( Ivy_ObjIsNode(pObjNew) && Ivy_ObjIsInTfi_rec( Ivy_ObjFanin1(pObjNew), pObjOld, Levels - 1 ) ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Sets the pair of equivalent nodes in HAIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManHaigCreateChoice( Ivy_Man_t * p, Ivy_Obj_t * pObjOld, Ivy_Obj_t * pObjNew ) +{ + Ivy_Obj_t * pObjOldHaig, * pObjNewHaig; + Ivy_Obj_t * pObjOldHaigR, * pObjNewHaigR; + int fCompl; +//printf( "\nCreating choice for %d and %d in AIG\n", pObjOld->Id, Ivy_Regular(pObjNew)->Id ); + + assert( p->pHaig != NULL ); + assert( !Ivy_IsComplement(pObjOld) ); + // get pointers to the representatives of pObjOld and pObjNew + pObjOldHaig = pObjOld->pEquiv; + pObjNewHaig = Ivy_NotCond( Ivy_Regular(pObjNew)->pEquiv, Ivy_IsComplement(pObjNew) ); + // get the classes + pObjOldHaig = Ivy_NotCond( Ivy_HaigObjRepr(Ivy_Regular(pObjOldHaig)), Ivy_IsComplement(pObjOldHaig) ); + pObjNewHaig = Ivy_NotCond( Ivy_HaigObjRepr(Ivy_Regular(pObjNewHaig)), Ivy_IsComplement(pObjNewHaig) ); + // get regular pointers + pObjOldHaigR = Ivy_Regular(pObjOldHaig); + pObjNewHaigR = Ivy_Regular(pObjNewHaig); + // check if there is phase difference between them + fCompl = (Ivy_IsComplement(pObjOldHaig) != Ivy_IsComplement(pObjNewHaig)); + // if the class is the same, nothing to do + if ( pObjOldHaigR == pObjNewHaigR ) + return; + // if the second node belongs to a class, do not merge classes (for the time being) + if ( Ivy_ObjRefs(pObjOldHaigR) == 0 || pObjNewHaigR->pEquiv != NULL || + Ivy_ObjRefs(pObjNewHaigR) > 0 ) //|| Ivy_ObjIsInTfi_rec(pObjNewHaigR, pObjOldHaigR, 10) ) + { +/* + if ( pObjNewHaigR->pEquiv != NULL ) + printf( "c" ); + if ( Ivy_ObjRefs(pObjNewHaigR) > 0 ) + printf( "f" ); + printf( " " ); +*/ + p->pHaig->nClassesSkip++; + return; + } + + // add this node to the class of pObjOldHaig + assert( Ivy_ObjRefs(pObjOldHaigR) > 0 ); + assert( !Ivy_IsComplement(pObjOldHaigR->pEquiv) ); + if ( pObjOldHaigR->pEquiv == NULL ) + pObjNewHaigR->pEquiv = Ivy_NotCond( pObjOldHaigR, fCompl ); + else + pObjNewHaigR->pEquiv = Ivy_NotCond( pObjOldHaigR->pEquiv, fCompl ); + pObjOldHaigR->pEquiv = pObjNewHaigR; +//printf( "Setting choice node %d -> %d.\n", pObjOldHaigR->Id, pObjNewHaigR->Id ); + // update the class of the new node +// Ivy_Regular(pObjNew)->pEquiv = Ivy_NotCond( pObjOldHaigR, fCompl ^ Ivy_IsComplement(pObjNew) ); +//printf( "Creating choice for %d and %d in HAIG\n", pObjOldHaigR->Id, pObjNewHaigR->Id ); + +// if ( pObjOldHaigR->Id == 13 ) +// { +// Ivy_ManShow( p, 0 ); +// Ivy_ManShow( p->pHaig, 1 ); +// } +// if ( !Ivy_ManIsAcyclic( p->pHaig ) ) +// printf( "HAIG contains a cycle\n" ); +} + +/**Function************************************************************* + + Synopsis [Count the number of choices and choice nodes in HAIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManHaigCountChoices( Ivy_Man_t * p, int * pnChoices ) +{ + Ivy_Obj_t * pObj; + int nChoices, nChoiceNodes, Counter, i; + assert( p->pHaig != NULL ); + nChoices = nChoiceNodes = 0; + Ivy_ManForEachObj( p->pHaig, pObj, i ) + { + if ( Ivy_ObjIsTerm(pObj) || i == 0 ) + continue; + if ( Ivy_ObjRefs(pObj) == 0 ) + continue; + Counter = Ivy_HaigObjCountClass( pObj ); + nChoiceNodes += (int)(Counter > 1); + nChoices += Counter - 1; +// if ( Counter > 1 ) +// printf( "Choice node %d %s\n", pObj->Id, Ivy_ObjIsLatch(pObj)? "(latch)": "" ); + } + *pnChoices = nChoices; + return nChoiceNodes; +} + +/**Function************************************************************* + + Synopsis [Prints statistics of the HAIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManHaigPostprocess( Ivy_Man_t * p, int fVerbose ) +{ + int nChoices, nChoiceNodes; + + assert( p->pHaig != NULL ); + + if ( fVerbose ) + { + printf( "Final : " ); + Ivy_ManPrintStats( p ); + printf( "HAIG : " ); + Ivy_ManPrintStats( p->pHaig ); + + // print choice node stats + nChoiceNodes = Ivy_ManHaigCountChoices( p, &nChoices ); + printf( "Total choice nodes = %d. Total choices = %d. Skipped classes = %d.\n", + nChoiceNodes, nChoices, p->pHaig->nClassesSkip ); + } + + if ( Ivy_ManIsAcyclic( p->pHaig ) ) + { + if ( fVerbose ) + printf( "HAIG is acyclic\n" ); + } + else + printf( "HAIG contains a cycle\n" ); + +// if ( fVerbose ) +// Ivy_ManHaigSimulate( p ); +} + + +/**Function************************************************************* + + Synopsis [Applies the simulation rules.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Ivy_Init_t Ivy_ManHaigSimulateAnd( Ivy_Init_t In0, Ivy_Init_t In1 ) +{ + assert( In0 != IVY_INIT_NONE && In1 != IVY_INIT_NONE ); + if ( In0 == IVY_INIT_DC || In1 == IVY_INIT_DC ) + return IVY_INIT_DC; + if ( In0 == IVY_INIT_1 && In1 == IVY_INIT_1 ) + return IVY_INIT_1; + return IVY_INIT_0; +} + +/**Function************************************************************* + + Synopsis [Applies the simulation rules.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Ivy_Init_t Ivy_ManHaigSimulateChoice( Ivy_Init_t In0, Ivy_Init_t In1 ) +{ + assert( In0 != IVY_INIT_NONE && In1 != IVY_INIT_NONE ); + if ( (In0 == IVY_INIT_0 && In1 == IVY_INIT_1) || (In0 == IVY_INIT_1 && In1 == IVY_INIT_0) ) + { + printf( "Compatibility fails.\n" ); + return IVY_INIT_0; + } + if ( In0 == IVY_INIT_DC && In1 == IVY_INIT_DC ) + return IVY_INIT_DC; + if ( In0 != IVY_INIT_DC ) + return In0; + return In1; +} + +/**Function************************************************************* + + Synopsis [Simulate HAIG using modified 3-valued simulation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManHaigSimulate( Ivy_Man_t * p ) +{ + Vec_Int_t * vNodes, * vLatches, * vLatchesD; + Ivy_Obj_t * pObj, * pTemp; + Ivy_Init_t In0, In1; + int i, k, Counter; + int fVerbose = 0; + + // check choices + Ivy_ManCheckChoices( p ); + + // switch to HAIG + assert( p->pHaig != NULL ); + p = p->pHaig; + +if ( fVerbose ) +Ivy_ManForEachPi( p, pObj, i ) +printf( "Setting PI %d\n", pObj->Id ); + + // collect latches and nodes in the DFS order + vNodes = Ivy_ManDfsSeq( p, &vLatches ); + +if ( fVerbose ) +Ivy_ManForEachNodeVec( p, vNodes, pObj, i ) +printf( "Collected node %d with fanins %d and %d\n", pObj->Id, Ivy_ObjFanin0(pObj)->Id, Ivy_ObjFanin1(pObj)->Id ); + + // set the PI values + Ivy_ManConst1(p)->Init = IVY_INIT_1; + Ivy_ManForEachPi( p, pObj, i ) + pObj->Init = IVY_INIT_0; + + // set the latch values + Ivy_ManForEachNodeVec( p, vLatches, pObj, i ) + pObj->Init = IVY_INIT_DC; + // set the latches of D to be determinate + vLatchesD = p->pData; + Ivy_ManForEachNodeVec( p, vLatchesD, pObj, i ) + pObj->Init = IVY_INIT_0; + + // perform several rounds of simulation + for ( k = 0; k < 10; k++ ) + { + // count the number of non-determinate values + Counter = 0; + Ivy_ManForEachNodeVec( p, vLatches, pObj, i ) + Counter += ( pObj->Init == IVY_INIT_DC ); + printf( "Iter %d : Non-determinate = %d\n", k, Counter ); + + // simulate the internal nodes + Ivy_ManForEachNodeVec( p, vNodes, pObj, i ) + { +if ( fVerbose ) +printf( "Processing node %d with fanins %d and %d\n", pObj->Id, Ivy_ObjFanin0(pObj)->Id, Ivy_ObjFanin1(pObj)->Id ); + In0 = Ivy_InitNotCond( Ivy_ObjFanin0(pObj)->Init, Ivy_ObjFaninC0(pObj) ); + In1 = Ivy_InitNotCond( Ivy_ObjFanin1(pObj)->Init, Ivy_ObjFaninC1(pObj) ); + pObj->Init = Ivy_ManHaigSimulateAnd( In0, In1 ); + // simulate the equivalence class if the node is a representative + if ( pObj->pEquiv && Ivy_ObjRefs(pObj) > 0 ) + { +if ( fVerbose ) +printf( "Processing choice node %d\n", pObj->Id ); + In0 = pObj->Init; + assert( !Ivy_IsComplement(pObj->pEquiv) ); + for ( pTemp = pObj->pEquiv; pTemp != pObj; pTemp = Ivy_Regular(pTemp->pEquiv) ) + { +if ( fVerbose ) +printf( "Processing secondary node %d\n", pTemp->Id ); + In1 = Ivy_InitNotCond( pTemp->Init, Ivy_IsComplement(pTemp->pEquiv) ); + In0 = Ivy_ManHaigSimulateChoice( In0, In1 ); + } + pObj->Init = In0; + } + } + + // simulate the latches + Ivy_ManForEachNodeVec( p, vLatches, pObj, i ) + { + pObj->Level = Ivy_ObjFanin0(pObj)->Init; +if ( fVerbose ) +printf( "Using latch %d with fanin %d\n", pObj->Id, Ivy_ObjFanin0(pObj)->Id ); + } + Ivy_ManForEachNodeVec( p, vLatches, pObj, i ) + pObj->Init = pObj->Level, pObj->Level = 0; + } + // free arrays + Vec_IntFree( vNodes ); + Vec_IntFree( vLatches ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyMan.c b/abc_with_bb_support/src/aig/ivy/ivyMan.c new file mode 100644 index 000000000..517917475 --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyMan.c @@ -0,0 +1,546 @@ +/**CFile**************************************************************** + + FileName [ivyMan.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [AIG manager.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivy_.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Man_t * Ivy_ManStart() +{ + Ivy_Man_t * p; + // start the manager + p = ALLOC( Ivy_Man_t, 1 ); + memset( p, 0, sizeof(Ivy_Man_t) ); + // perform initializations + p->Ghost.Id = -1; + p->nTravIds = 1; + p->fCatchExor = 1; + // allocate arrays for nodes + p->vPis = Vec_PtrAlloc( 100 ); + p->vPos = Vec_PtrAlloc( 100 ); + p->vBufs = Vec_PtrAlloc( 100 ); + p->vObjs = Vec_PtrAlloc( 100 ); + // prepare the internal memory manager + Ivy_ManStartMemory( p ); + // create the constant node + p->pConst1 = Ivy_ManFetchMemory( p ); + p->pConst1->fPhase = 1; + Vec_PtrPush( p->vObjs, p->pConst1 ); + p->nCreated = 1; + // start the table + p->nTableSize = 10007; + p->pTable = ALLOC( int, p->nTableSize ); + memset( p->pTable, 0, sizeof(int) * p->nTableSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Duplicates the AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Man_t * Ivy_ManStartFrom( Ivy_Man_t * p ) +{ + Ivy_Man_t * pNew; + Ivy_Obj_t * pObj; + int i; + // create the new manager + pNew = Ivy_ManStart(); + // create the PIs + Ivy_ManConst1(p)->pEquiv = Ivy_ManConst1(pNew); + Ivy_ManForEachPi( p, pObj, i ) + pObj->pEquiv = Ivy_ObjCreatePi(pNew); + return pNew; +} + +/**Function************************************************************* + + Synopsis [Duplicates the AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Man_t * Ivy_ManDup( Ivy_Man_t * p ) +{ + Vec_Int_t * vNodes, * vLatches; + Ivy_Man_t * pNew; + Ivy_Obj_t * pObj; + int i; + // collect latches and nodes in the DFS order + vNodes = Ivy_ManDfsSeq( p, &vLatches ); + // create the new manager + pNew = Ivy_ManStart(); + // create the PIs + Ivy_ManConst1(p)->pEquiv = Ivy_ManConst1(pNew); + Ivy_ManForEachPi( p, pObj, i ) + pObj->pEquiv = Ivy_ObjCreatePi(pNew); + // create the fake PIs for latches + Ivy_ManForEachNodeVec( p, vLatches, pObj, i ) + pObj->pEquiv = Ivy_ObjCreatePi(pNew); + // duplicate internal nodes + Ivy_ManForEachNodeVec( p, vNodes, pObj, i ) + if ( Ivy_ObjIsBuf(pObj) ) + pObj->pEquiv = Ivy_ObjChild0Equiv(pObj); + else + pObj->pEquiv = Ivy_And( pNew, Ivy_ObjChild0Equiv(pObj), Ivy_ObjChild1Equiv(pObj) ); + // add the POs + Ivy_ManForEachPo( p, pObj, i ) + Ivy_ObjCreatePo( pNew, Ivy_ObjChild0Equiv(pObj) ); + // transform additional PI nodes into latches and connect them + Ivy_ManForEachNodeVec( p, vLatches, pObj, i ) + { + assert( !Ivy_ObjFaninC0(pObj) ); + pObj->pEquiv->Type = IVY_LATCH; + pObj->pEquiv->Init = pObj->Init; + Ivy_ObjConnect( pNew, pObj->pEquiv, Ivy_ObjChild0Equiv(pObj), NULL ); + } + // shrink the arrays + Vec_PtrShrink( pNew->vPis, Ivy_ManPiNum(p) ); + // update the counters of different objects + pNew->nObjs[IVY_PI] -= Ivy_ManLatchNum(p); + pNew->nObjs[IVY_LATCH] += Ivy_ManLatchNum(p); + // free arrays + Vec_IntFree( vNodes ); + Vec_IntFree( vLatches ); + // make sure structural hashing did not change anything + assert( Ivy_ManNodeNum(p) == Ivy_ManNodeNum(pNew) ); + assert( Ivy_ManLatchNum(p) == Ivy_ManLatchNum(pNew) ); + // check the resulting network + if ( !Ivy_ManCheck(pNew) ) + printf( "Ivy_ManMakeSeq(): The check has failed.\n" ); + return pNew; +} + +/**Function************************************************************* + + Synopsis [Stops the AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Man_t * Ivy_ManFrames( Ivy_Man_t * pMan, int nLatches, int nFrames, int fInit, Vec_Ptr_t ** pvMapping ) +{ + Vec_Ptr_t * vMapping; + Ivy_Man_t * pNew; + Ivy_Obj_t * pObj; + int i, f, nPis, nPos, nIdMax; + assert( Ivy_ManLatchNum(pMan) == 0 ); + assert( nFrames > 0 ); + // prepare the mapping + nPis = Ivy_ManPiNum(pMan) - nLatches; + nPos = Ivy_ManPoNum(pMan) - nLatches; + nIdMax = Ivy_ManObjIdMax(pMan); + // create the new manager + pNew = Ivy_ManStart(); + // set the starting values of latch inputs + for ( i = 0; i < nLatches; i++ ) + Ivy_ManPo(pMan, nPos+i)->pEquiv = fInit? Ivy_Not(Ivy_ManConst1(pNew)) : Ivy_ObjCreatePi(pNew); + // add timeframes + vMapping = Vec_PtrStart( nIdMax * nFrames + 1 ); + for ( f = 0; f < nFrames; f++ ) + { + // create PIs + Ivy_ManConst1(pMan)->pEquiv = Ivy_ManConst1(pNew); + for ( i = 0; i < nPis; i++ ) + Ivy_ManPi(pMan, i)->pEquiv = Ivy_ObjCreatePi(pNew); + // transfer values to latch outputs + for ( i = 0; i < nLatches; i++ ) + Ivy_ManPi(pMan, nPis+i)->pEquiv = Ivy_ManPo(pMan, nPos+i)->pEquiv; + // perform strashing + Ivy_ManForEachNode( pMan, pObj, i ) + pObj->pEquiv = Ivy_And( pNew, Ivy_ObjChild0Equiv(pObj), Ivy_ObjChild1Equiv(pObj) ); + // create POs + for ( i = 0; i < nPos; i++ ) + Ivy_ManPo(pMan, i)->pEquiv = Ivy_ObjCreatePo( pNew, Ivy_ObjChild0Equiv(Ivy_ManPo(pMan, i)) ); + // set the results of latch inputs + for ( i = 0; i < nLatches; i++ ) + Ivy_ManPo(pMan, nPos+i)->pEquiv = Ivy_ObjChild0Equiv(Ivy_ManPo(pMan, nPos+i)); + // save the pointers in this frame + Ivy_ManForEachObj( pMan, pObj, i ) + Vec_PtrWriteEntry( vMapping, f * nIdMax + i, pObj->pEquiv ); + } + // connect latches + if ( !fInit ) + for ( i = 0; i < nLatches; i++ ) + Ivy_ObjCreatePo( pNew, Ivy_ManPo(pMan, nPos+i)->pEquiv ); + // remove dangling nodes + Ivy_ManCleanup(pNew); + *pvMapping = vMapping; + // check the resulting network + if ( !Ivy_ManCheck(pNew) ) + printf( "Ivy_ManFrames(): The check has failed.\n" ); + return pNew; +} + + +/**Function************************************************************* + + Synopsis [Stops the AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManStop( Ivy_Man_t * p ) +{ + if ( p->time1 ) { PRT( "Update lev ", p->time1 ); } + if ( p->time2 ) { PRT( "Update levR ", p->time2 ); } +// Ivy_TableProfile( p ); +// if ( p->vFanouts ) Ivy_ManStopFanout( p ); + if ( p->vChunks ) Ivy_ManStopMemory( p ); + if ( p->vRequired ) Vec_IntFree( p->vRequired ); + if ( p->vPis ) Vec_PtrFree( p->vPis ); + if ( p->vPos ) Vec_PtrFree( p->vPos ); + if ( p->vBufs ) Vec_PtrFree( p->vBufs ); + if ( p->vObjs ) Vec_PtrFree( p->vObjs ); + free( p->pTable ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Removes nodes without fanout.] + + Description [Returns the number of dangling nodes removed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManCleanup( Ivy_Man_t * p ) +{ + Ivy_Obj_t * pNode; + int i, nNodesOld; + nNodesOld = Ivy_ManNodeNum(p); + Ivy_ManForEachObj( p, pNode, i ) + if ( Ivy_ObjIsNode(pNode) || Ivy_ObjIsLatch(pNode) || Ivy_ObjIsBuf(pNode) ) + if ( Ivy_ObjRefs(pNode) == 0 ) + Ivy_ObjDelete_rec( p, pNode, 1 ); +//printf( "Cleanup removed %d nodes.\n", nNodesOld - Ivy_ManNodeNum(p) ); + return nNodesOld - Ivy_ManNodeNum(p); +} + +/**Function************************************************************* + + Synopsis [Marks nodes reachable from the given one.] + + Description [Returns the number of dangling nodes removed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManCleanupSeq_rec( Ivy_Obj_t * pObj ) +{ + if ( Ivy_ObjIsMarkA(pObj) ) + return; + Ivy_ObjSetMarkA(pObj); + if ( pObj->pFanin0 != NULL ) + Ivy_ManCleanupSeq_rec( Ivy_ObjFanin0(pObj) ); + if ( pObj->pFanin1 != NULL ) + Ivy_ManCleanupSeq_rec( Ivy_ObjFanin1(pObj) ); +} + +/**Function************************************************************* + + Synopsis [Removes logic that does not feed into POs.] + + Description [Returns the number of dangling nodes removed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManCleanupSeq( Ivy_Man_t * p ) +{ + Vec_Ptr_t * vNodes; + Ivy_Obj_t * pObj; + int i, RetValue; + // mark the constant and PIs + Ivy_ObjSetMarkA( Ivy_ManConst1(p) ); + Ivy_ManForEachPi( p, pObj, i ) + Ivy_ObjSetMarkA( pObj ); + // mark nodes visited from POs + Ivy_ManForEachPo( p, pObj, i ) + Ivy_ManCleanupSeq_rec( pObj ); + // collect unmarked nodes + vNodes = Vec_PtrAlloc( 100 ); + Ivy_ManForEachObj( p, pObj, i ) + { + if ( Ivy_ObjIsMarkA(pObj) ) + Ivy_ObjClearMarkA(pObj); + else + Vec_PtrPush( vNodes, pObj ); + } + if ( Vec_PtrSize(vNodes) == 0 ) + { + Vec_PtrFree( vNodes ); +//printf( "Sequential sweep cleaned out %d nodes.\n", 0 ); + return 0; + } + // disconnect the marked objects + Vec_PtrForEachEntry( vNodes, pObj, i ) + Ivy_ObjDisconnect( p, pObj ); + // remove the dangling objects + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + assert( Ivy_ObjIsNode(pObj) || Ivy_ObjIsLatch(pObj) || Ivy_ObjIsBuf(pObj) ); + assert( Ivy_ObjRefs(pObj) == 0 ); + // update node counters of the manager + p->nObjs[pObj->Type]--; + p->nDeleted++; + // delete buffer from the array of buffers + if ( p->fFanout && Ivy_ObjIsBuf(pObj) ) + Vec_PtrRemove( p->vBufs, pObj ); + // free the node + Vec_PtrWriteEntry( p->vObjs, pObj->Id, NULL ); + Ivy_ManRecycleMemory( p, pObj ); + } + // return the number of nodes freed + RetValue = Vec_PtrSize(vNodes); + Vec_PtrFree( vNodes ); +//printf( "Sequential sweep cleaned out %d nodes.\n", RetValue ); + return RetValue; +} + + +/**Function************************************************************* + + Synopsis [Checks if latches form self-loop.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManLatchIsSelfFeed_rec( Ivy_Obj_t * pLatch, Ivy_Obj_t * pLatchRoot ) +{ + if ( !Ivy_ObjIsLatch(pLatch) && !Ivy_ObjIsBuf(pLatch) ) + return 0; + if ( pLatch == pLatchRoot ) + return 1; + return Ivy_ManLatchIsSelfFeed_rec( Ivy_ObjFanin0(pLatch), pLatchRoot ); +} + +/**Function************************************************************* + + Synopsis [Checks if latches form self-loop.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManLatchIsSelfFeed( Ivy_Obj_t * pLatch ) +{ + if ( !Ivy_ObjIsLatch(pLatch) ) + return 0; + return Ivy_ManLatchIsSelfFeed_rec( Ivy_ObjFanin0(pLatch), pLatch ); +} + + +/**Function************************************************************* + + Synopsis [Returns the number of dangling nodes removed.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManPropagateBuffers( Ivy_Man_t * p, int fUpdateLevel ) +{ + Ivy_Obj_t * pNode; + int LimitFactor = 10; + int NodeBeg = Ivy_ManNodeNum(p); + int nSteps; + for ( nSteps = 0; Vec_PtrSize(p->vBufs) > 0; nSteps++ ) + { + pNode = Vec_PtrEntryLast(p->vBufs); + while ( Ivy_ObjIsBuf(pNode) ) + pNode = Ivy_ObjReadFirstFanout( p, pNode ); + // check if this buffer should remain + if ( Ivy_ManLatchIsSelfFeed(pNode) ) + { + Vec_PtrPop(p->vBufs); + continue; + } +//printf( "Propagating buffer %d with input %d and output %d\n", Ivy_ObjFaninId0(pNode), Ivy_ObjFaninId0(Ivy_ObjFanin0(pNode)), pNode->Id ); +//printf( "Latch num %d\n", Ivy_ManLatchNum(p) ); + Ivy_NodeFixBufferFanins( p, pNode, fUpdateLevel ); + if ( nSteps > NodeBeg * LimitFactor ) + { + printf( "Structural hashing is not finished after %d forward latch moves.\n", NodeBeg * LimitFactor ); + printf( "This circuit cannot be forward-retimed completely. Quitting.\n" ); + break; + } + } +// printf( "Number of steps = %d. Nodes beg = %d. Nodes end = %d.\n", nSteps, NodeBeg, Ivy_ManNodeNum(p) ); + return nSteps; +} + +/**Function************************************************************* + + Synopsis [Stops the AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManPrintStats( Ivy_Man_t * p ) +{ + printf( "PI/PO = %d/%d ", Ivy_ManPiNum(p), Ivy_ManPoNum(p) ); + printf( "A = %7d. ", Ivy_ManAndNum(p) ); + printf( "L = %5d. ", Ivy_ManLatchNum(p) ); +// printf( "X = %d. ", Ivy_ManExorNum(p) ); +// printf( "B = %3d. ", Ivy_ManBufNum(p) ); + printf( "MaxID = %7d. ", Ivy_ManObjIdMax(p) ); +// printf( "Cre = %d. ", p->nCreated ); +// printf( "Del = %d. ", p->nDeleted ); + printf( "Lev = %3d. ", Ivy_ManLatchNum(p)? -1 : Ivy_ManLevels(p) ); + printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [Converts a combinational AIG manager into a sequential one.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManMakeSeq( Ivy_Man_t * p, int nLatches, int * pInits ) +{ + Ivy_Obj_t * pObj, * pLatch; + Ivy_Init_t Init; + int i; + if ( nLatches == 0 ) + return; + assert( nLatches < Ivy_ManPiNum(p) && nLatches < Ivy_ManPoNum(p) ); + assert( Ivy_ManPiNum(p) == Vec_PtrSize(p->vPis) ); + assert( Ivy_ManPoNum(p) == Vec_PtrSize(p->vPos) ); + assert( Vec_PtrSize( p->vBufs ) == 0 ); + // create fanouts + if ( p->fFanout == 0 ) + Ivy_ManStartFanout( p ); + // collect the POs to be converted into latches + for ( i = 0; i < nLatches; i++ ) + { + // get the latch value + Init = pInits? pInits[i] : IVY_INIT_0; + // create latch + pObj = Ivy_ManPo( p, Ivy_ManPoNum(p) - nLatches + i ); + pLatch = Ivy_Latch( p, Ivy_ObjChild0(pObj), Init ); + Ivy_ObjDisconnect( p, pObj ); + // recycle the old PO object + Vec_PtrWriteEntry( p->vObjs, pObj->Id, NULL ); + Ivy_ManRecycleMemory( p, pObj ); + // convert the corresponding PI to a buffer and connect it to the latch + pObj = Ivy_ManPi( p, Ivy_ManPiNum(p) - nLatches + i ); + pObj->Type = IVY_BUF; + Ivy_ObjConnect( p, pObj, pLatch, NULL ); + // save the buffer + Vec_PtrPush( p->vBufs, pObj ); + } + // shrink the arrays + Vec_PtrShrink( p->vPis, Ivy_ManPiNum(p) - nLatches ); + Vec_PtrShrink( p->vPos, Ivy_ManPoNum(p) - nLatches ); + // update the counters of different objects + p->nObjs[IVY_PI] -= nLatches; + p->nObjs[IVY_PO] -= nLatches; + p->nObjs[IVY_BUF] += nLatches; + p->nDeleted -= 2 * nLatches; + // remove dangling nodes + Ivy_ManCleanup(p); + Ivy_ManCleanupSeq(p); +/* + // check for dangling nodes + Ivy_ManForEachObj( p, pObj, i ) + if ( !Ivy_ObjIsPi(pObj) && !Ivy_ObjIsPo(pObj) && !Ivy_ObjIsConst1(pObj) ) + { + assert( Ivy_ObjRefs(pObj) > 0 ); + assert( Ivy_ObjRefs(pObj) == Ivy_ObjFanoutNum(p, pObj) ); + } +*/ + // perform hashing by propagating the buffers + Ivy_ManPropagateBuffers( p, 0 ); + if ( Ivy_ManBufNum(p) ) + printf( "The number of remaining buffers is %d.\n", Ivy_ManBufNum(p) ); + // fix the levels + Ivy_ManResetLevels( p ); + // check the resulting network + if ( !Ivy_ManCheck(p) ) + printf( "Ivy_ManMakeSeq(): The check has failed.\n" ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyMem.c b/abc_with_bb_support/src/aig/ivy/ivyMem.c new file mode 100644 index 000000000..e00ff1315 --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyMem.c @@ -0,0 +1,116 @@ +/**CFile**************************************************************** + + FileName [ivyMem.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [Memory management for the AIG nodes.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyMem.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// memory management +#define IVY_PAGE_SIZE 12 // page size containing 2^IVY_PAGE_SIZE nodes +#define IVY_PAGE_MASK 4095 // page bitmask (2^IVY_PAGE_SIZE)-1 + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the internal memory manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManStartMemory( Ivy_Man_t * p ) +{ + p->vChunks = Vec_PtrAlloc( 128 ); + p->vPages = Vec_PtrAlloc( 128 ); +} + +/**Function************************************************************* + + Synopsis [Stops the internal memory manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManStopMemory( Ivy_Man_t * p ) +{ + void * pMemory; + int i; + Vec_PtrForEachEntry( p->vChunks, pMemory, i ) + free( pMemory ); + Vec_PtrFree( p->vChunks ); + Vec_PtrFree( p->vPages ); + p->pListFree = NULL; +} + +/**Function************************************************************* + + Synopsis [Allocates additional memory for the nodes.] + + Description [Allocates IVY_PAGE_SIZE nodes. Aligns memory by 32 bytes. + Records the pointer to the AIG manager in the -1 entry.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManAddMemory( Ivy_Man_t * p ) +{ + char * pMemory; + int i, nBytes; + int EntrySizeMax = 128; + assert( sizeof(Ivy_Obj_t) <= EntrySizeMax ); + assert( p->pListFree == NULL ); +// assert( (Ivy_ManObjNum(p) & IVY_PAGE_MASK) == 0 ); + // allocate new memory page + nBytes = sizeof(Ivy_Obj_t) * (1<vChunks, pMemory ); + // align memory at the 32-byte boundary + pMemory = pMemory + EntrySizeMax - (((int)pMemory) & (EntrySizeMax-1)); + // remember the manager in the first entry + Vec_PtrPush( p->vPages, pMemory ); + // break the memory down into nodes + p->pListFree = (Ivy_Obj_t *)pMemory; + for ( i = 1; i <= IVY_PAGE_MASK; i++ ) + { + *((char **)pMemory) = pMemory + sizeof(Ivy_Obj_t); + pMemory += sizeof(Ivy_Obj_t); + } + *((char **)pMemory) = NULL; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyMulti.c b/abc_with_bb_support/src/aig/ivy/ivyMulti.c new file mode 100644 index 000000000..ee25dd740 --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyMulti.c @@ -0,0 +1,301 @@ +/**CFile**************************************************************** + + FileName [ivyMulti.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [Constructing multi-input AND/EXOR gates.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyMulti.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define IVY_EVAL_LIMIT 128 + +typedef struct Ivy_Eva_t_ Ivy_Eva_t; +struct Ivy_Eva_t_ +{ + Ivy_Obj_t * pArg; // the argument node + unsigned Mask; // the mask of covered nodes + int Weight; // the number of covered nodes +}; + +static void Ivy_MultiPrint( Ivy_Man_t * p, Ivy_Eva_t * pEvals, int nLeaves, int nEvals ); +static int Ivy_MultiCover( Ivy_Man_t * p, Ivy_Eva_t * pEvals, int nLeaves, int nEvals, int nLimit, Vec_Ptr_t * vSols ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Constructs a balanced tree while taking sharing into account.] + + Description [Returns 1 if the implementation exists.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_MultiPlus( Ivy_Man_t * p, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vCone, Ivy_Type_t Type, int nLimit, Vec_Ptr_t * vSols ) +{ + static Ivy_Eva_t pEvals[IVY_EVAL_LIMIT]; + Ivy_Eva_t * pEval, * pFan0, * pFan1; + Ivy_Obj_t * pObj, * pTemp; + int nEvals, nEvalsOld, i, k, x, nLeaves; + unsigned uMaskAll; + + // consider special cases + nLeaves = Vec_PtrSize(vLeaves); + assert( nLeaves > 2 ); + if ( nLeaves > 32 || nLeaves + Vec_PtrSize(vCone) > IVY_EVAL_LIMIT ) + return 0; +// if ( nLeaves == 1 ) +// return Vec_PtrEntry( vLeaves, 0 ); +// if ( nLeaves == 2 ) +// return Ivy_Oper( Vec_PtrEntry(vLeaves, 0), Vec_PtrEntry(vLeaves, 1), Type ); + + // set the leaf entries + uMaskAll = ((1 << nLeaves) - 1); + nEvals = 0; + Vec_PtrForEachEntry( vLeaves, pObj, i ) + { + pEval = pEvals + nEvals; + pEval->pArg = pObj; + pEval->Mask = (1 << nEvals); + pEval->Weight = 1; + // mark the leaf + Ivy_Regular(pObj)->TravId = nEvals; + nEvals++; + } + + // propagate masks through the cone + Vec_PtrForEachEntry( vCone, pObj, i ) + { + pObj->TravId = nEvals + i; + if ( Ivy_ObjIsBuf(pObj) ) + pEvals[pObj->TravId].Mask = pEvals[Ivy_ObjFanin0(pObj)->TravId].Mask; + else + pEvals[pObj->TravId].Mask = pEvals[Ivy_ObjFanin0(pObj)->TravId].Mask | pEvals[Ivy_ObjFanin1(pObj)->TravId].Mask; + } + + // set the internal entries + Vec_PtrForEachEntry( vCone, pObj, i ) + { + if ( i == Vec_PtrSize(vCone) - 1 ) + break; + // skip buffers + if ( Ivy_ObjIsBuf(pObj) ) + continue; + // skip nodes without external fanout + if ( Ivy_ObjRefs(pObj) == 0 ) + continue; + assert( !Ivy_IsComplement(pObj) ); + pEval = pEvals + nEvals; + pEval->pArg = pObj; + pEval->Mask = pEvals[pObj->TravId].Mask; + pEval->Weight = Extra_WordCountOnes(pEval->Mask); + // mark the node + pObj->TravId = nEvals; + nEvals++; + } + + // find the available nodes + nEvalsOld = nEvals; + for ( i = 1; i < nEvals; i++ ) + for ( k = 0; k < i; k++ ) + { + pFan0 = pEvals + i; + pFan1 = pEvals + k; + pTemp = Ivy_TableLookup(p, Ivy_ObjCreateGhost(p, pFan0->pArg, pFan1->pArg, Type, IVY_INIT_NONE)); + // skip nodes in the cone + if ( pTemp == NULL || pTemp->fMarkB ) + continue; + // skip the leaves + for ( x = 0; x < nLeaves; x++ ) + if ( pTemp == Ivy_Regular(vLeaves->pArray[x]) ) + break; + if ( x < nLeaves ) + continue; + pEval = pEvals + nEvals; + pEval->pArg = pTemp; + pEval->Mask = pFan0->Mask | pFan1->Mask; + pEval->Weight = (pFan0->Mask & pFan1->Mask) ? Extra_WordCountOnes(pEval->Mask) : pFan0->Weight + pFan1->Weight; + // save the argument + pObj->TravId = nEvals; + nEvals++; + // quit if the number of entries exceeded the limit + if ( nEvals == IVY_EVAL_LIMIT ) + goto Outside; + // quit if we found an acceptable implementation + if ( pEval->Mask == uMaskAll ) + goto Outside; + } +Outside: + +// Ivy_MultiPrint( pEvals, nLeaves, nEvals ); + if ( !Ivy_MultiCover( p, pEvals, nLeaves, nEvals, nLimit, vSols ) ) + return 0; + assert( Vec_PtrSize( vSols ) > 0 ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Computes how many uncovered ones this one covers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_MultiPrint( Ivy_Man_t * p, Ivy_Eva_t * pEvals, int nLeaves, int nEvals ) +{ + Ivy_Eva_t * pEval; + int i, k; + for ( i = nLeaves; i < nEvals; i++ ) + { + pEval = pEvals + i; + printf( "%2d (id = %5d) : |", i-nLeaves, Ivy_ObjId(pEval->pArg) ); + for ( k = 0; k < nLeaves; k++ ) + { + if ( pEval->Mask & (1 << k) ) + printf( "+" ); + else + printf( " " ); + } + printf( "| Lev = %d.\n", Ivy_ObjLevel(pEval->pArg) ); + } +} + +/**Function************************************************************* + + Synopsis [Computes how many uncovered ones this one covers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Ivy_MultiWeight( unsigned uMask, int nMaskOnes, unsigned uFound ) +{ + assert( uMask & ~uFound ); + if ( (uMask & uFound) == 0 ) + return nMaskOnes; + return Extra_WordCountOnes( uMask & ~uFound ); +} + +/**Function************************************************************* + + Synopsis [Finds the cover.] + + Description [Returns 1 if the cover is found.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_MultiCover( Ivy_Man_t * p, Ivy_Eva_t * pEvals, int nLeaves, int nEvals, int nLimit, Vec_Ptr_t * vSols ) +{ + int fVerbose = 0; + Ivy_Eva_t * pEval, * pEvalBest; + unsigned uMaskAll, uFound, uTemp; + int i, k, BestK, WeightBest, WeightCur, LevelBest, LevelCur; + uMaskAll = (nLeaves == 32)? (~(unsigned)0) : ((1 << nLeaves) - 1); + uFound = 0; + // solve the covering problem + if ( fVerbose ) + printf( "Solution: " ); + Vec_PtrClear( vSols ); + for ( i = 0; i < nLimit; i++ ) + { + BestK = -1; + for ( k = nEvals - 1; k >= 0; k-- ) + { + pEval = pEvals + k; + if ( (pEval->Mask & ~uFound) == 0 ) + continue; + if ( BestK == -1 ) + { + BestK = k; + pEvalBest = pEval; + WeightBest = Ivy_MultiWeight( pEvalBest->Mask, pEvalBest->Weight, uFound ); + LevelBest = Ivy_ObjLevel( Ivy_Regular(pEvalBest->pArg) ); + continue; + } + // compare BestK and the new one (k) + WeightCur = Ivy_MultiWeight( pEval->Mask, pEval->Weight, uFound ); + LevelCur = Ivy_ObjLevel( Ivy_Regular(pEval->pArg) ); + if ( WeightBest < WeightCur || + (WeightBest == WeightCur && LevelBest > LevelCur) ) + { + BestK = k; + pEvalBest = pEval; + WeightBest = WeightCur; + LevelBest = LevelCur; + } + } + assert( BestK != -1 ); + // if the cost is only 1, take the leaf + if ( WeightBest == 1 && BestK >= nLeaves ) + { + uTemp = (pEvalBest->Mask & ~uFound); + for ( k = 0; k < nLeaves; k++ ) + if ( uTemp & (1 << k) ) + break; + assert( k < nLeaves ); + BestK = k; + pEvalBest = pEvals + BestK; + } + if ( fVerbose ) + { + if ( BestK < nLeaves ) + printf( "L(%d) ", BestK ); + else + printf( "%d ", BestK - nLeaves ); + } + // update the found set + Vec_PtrPush( vSols, pEvalBest->pArg ); + uFound |= pEvalBest->Mask; + if ( uFound == uMaskAll ) + break; + } + if ( uFound == uMaskAll ) + { + if ( fVerbose ) + printf( " Found \n\n" ); + return 1; + } + else + { + if ( fVerbose ) + printf( " Not found \n\n" ); + return 0; + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyMulti8.c b/abc_with_bb_support/src/aig/ivy/ivyMulti8.c new file mode 100644 index 000000000..781041eb6 --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyMulti8.c @@ -0,0 +1,427 @@ +/**CFile**************************************************************** + + FileName [ivyMulti.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [Constructing multi-input AND/EXOR gates.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyMulti.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Ivy_Eval_t_ Ivy_Eval_t; +struct Ivy_Eval_t_ +{ + unsigned Mask : 5; // the mask of covered nodes + unsigned Weight : 3; // the number of covered nodes + unsigned Cost : 4; // the number of overlapping nodes + unsigned Level : 12; // the level of this node + unsigned Fan0 : 4; // the first fanin + unsigned Fan1 : 4; // the second fanin +}; + +static Ivy_Obj_t * Ivy_MultiBuild_rec( Ivy_Eval_t * pEvals, int iNum, Ivy_Obj_t ** pArgs, int nArgs, Ivy_Type_t Type ); +static void Ivy_MultiSort( Ivy_Obj_t ** pArgs, int nArgs ); +static int Ivy_MultiPushUniqueOrderByLevel( Ivy_Obj_t ** pArray, int nArgs, Ivy_Obj_t * pNode ); +static Ivy_Obj_t * Ivy_MultiEval( Ivy_Obj_t ** pArgs, int nArgs, Ivy_Type_t Type ); + + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Constructs the well-balanced tree of gates.] + + Description [Disregards levels and possible logic sharing.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_Multi_rec( Ivy_Obj_t ** ppObjs, int nObjs, Ivy_Type_t Type ) +{ + Ivy_Obj_t * pObj1, * pObj2; + if ( nObjs == 1 ) + return ppObjs[0]; + pObj1 = Ivy_Multi_rec( ppObjs, nObjs/2, Type ); + pObj2 = Ivy_Multi_rec( ppObjs + nObjs/2, nObjs - nObjs/2, Type ); + return Ivy_Oper( pObj1, pObj2, Type ); +} + +/**Function************************************************************* + + Synopsis [Constructs a balanced tree while taking sharing into account.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_Multi( Ivy_Obj_t ** pArgsInit, int nArgs, Ivy_Type_t Type ) +{ + static char NumBits[32] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5}; + static Ivy_Eval_t pEvals[15+15*14/2]; + static Ivy_Obj_t * pArgs[16]; + Ivy_Eval_t * pEva, * pEvaBest; + int nArgsNew, nEvals, i, k; + Ivy_Obj_t * pTemp; + + // consider the case of one argument + assert( nArgs > 0 ); + if ( nArgs == 1 ) + return pArgsInit[0]; + // consider the case of two arguments + if ( nArgs == 2 ) + return Ivy_Oper( pArgsInit[0], pArgsInit[1], Type ); + +//Ivy_MultiEval( pArgsInit, nArgs, Type ); printf( "\n" ); + + // set the initial ones + for ( i = 0; i < nArgs; i++ ) + { + pArgs[i] = pArgsInit[i]; + pEva = pEvals + i; + pEva->Mask = (1 << i); + pEva->Weight = 1; + pEva->Cost = 0; + pEva->Level = Ivy_Regular(pArgs[i])->Level; + pEva->Fan0 = 0; + pEva->Fan1 = 0; + } + + // find the available nodes + pEvaBest = pEvals; + nArgsNew = nArgs; + for ( i = 1; i < nArgsNew; i++ ) + for ( k = 0; k < i; k++ ) + if ( pTemp = Ivy_TableLookup(Ivy_ObjCreateGhost(pArgs[k], pArgs[i], Type, IVY_INIT_NONE)) ) + { + pEva = pEvals + nArgsNew; + pEva->Mask = pEvals[k].Mask | pEvals[i].Mask; + pEva->Weight = NumBits[pEva->Mask]; + pEva->Cost = pEvals[k].Cost + pEvals[i].Cost + NumBits[pEvals[k].Mask & pEvals[i].Mask]; + pEva->Level = 1 + IVY_MAX(pEvals[k].Level, pEvals[i].Level); + pEva->Fan0 = k; + pEva->Fan1 = i; +// assert( pEva->Level == (unsigned)Ivy_ObjLevel(pTemp) ); + // compare + if ( pEvaBest->Weight < pEva->Weight || + pEvaBest->Weight == pEva->Weight && pEvaBest->Cost > pEva->Cost || + pEvaBest->Weight == pEva->Weight && pEvaBest->Cost == pEva->Cost && pEvaBest->Level > pEva->Level ) + pEvaBest = pEva; + // save the argument + pArgs[nArgsNew++] = pTemp; + if ( nArgsNew == 15 ) + goto Outside; + } +Outside: + +// printf( "Best = %d.\n", pEvaBest - pEvals ); + + // the case of no common nodes + if ( nArgsNew == nArgs ) + { + Ivy_MultiSort( pArgs, nArgs ); + return Ivy_MultiBalance_rec( pArgs, nArgs, Type ); + } + // the case of one common node + if ( nArgsNew == nArgs + 1 ) + { + assert( pEvaBest - pEvals == nArgs ); + k = 0; + for ( i = 0; i < nArgs; i++ ) + if ( i != (int)pEvaBest->Fan0 && i != (int)pEvaBest->Fan1 ) + pArgs[k++] = pArgs[i]; + pArgs[k++] = pArgs[nArgs]; + assert( k == nArgs - 1 ); + nArgs = k; + Ivy_MultiSort( pArgs, nArgs ); + return Ivy_MultiBalance_rec( pArgs, nArgs, Type ); + } + // the case when there is a node that covers everything + if ( (int)pEvaBest->Mask == ((1 << nArgs) - 1) ) + return Ivy_MultiBuild_rec( pEvals, pEvaBest - pEvals, pArgs, nArgsNew, Type ); + + // evaluate node pairs + nEvals = nArgsNew; + for ( i = 1; i < nArgsNew; i++ ) + for ( k = 0; k < i; k++ ) + { + pEva = pEvals + nEvals; + pEva->Mask = pEvals[k].Mask | pEvals[i].Mask; + pEva->Weight = NumBits[pEva->Mask]; + pEva->Cost = pEvals[k].Cost + pEvals[i].Cost + NumBits[pEvals[k].Mask & pEvals[i].Mask]; + pEva->Level = 1 + IVY_MAX(pEvals[k].Level, pEvals[i].Level); + pEva->Fan0 = k; + pEva->Fan1 = i; + // compare + if ( pEvaBest->Weight < pEva->Weight || + pEvaBest->Weight == pEva->Weight && pEvaBest->Cost > pEva->Cost || + pEvaBest->Weight == pEva->Weight && pEvaBest->Cost == pEva->Cost && pEvaBest->Level > pEva->Level ) + pEvaBest = pEva; + // save the argument + nEvals++; + } + assert( pEvaBest - pEvals >= nArgsNew ); + +// printf( "Used (%d, %d).\n", pEvaBest->Fan0, pEvaBest->Fan1 ); + + // get the best implementation + pTemp = Ivy_MultiBuild_rec( pEvals, pEvaBest - pEvals, pArgs, nArgsNew, Type ); + + // collect those not covered by EvaBest + k = 0; + for ( i = 0; i < nArgs; i++ ) + if ( (pEvaBest->Mask & (1 << i)) == 0 ) + pArgs[k++] = pArgs[i]; + pArgs[k++] = pTemp; + assert( k == nArgs - (int)pEvaBest->Weight + 1 ); + nArgs = k; + Ivy_MultiSort( pArgs, nArgs ); + return Ivy_MultiBalance_rec( pArgs, nArgs, Type ); +} + +/**Function************************************************************* + + Synopsis [Implements multi-input AND/EXOR operation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_MultiBuild_rec( Ivy_Eval_t * pEvals, int iNum, Ivy_Obj_t ** pArgs, int nArgs, Ivy_Type_t Type ) +{ + Ivy_Obj_t * pNode0, * pNode1; + if ( iNum < nArgs ) + return pArgs[iNum]; + pNode0 = Ivy_MultiBuild_rec( pEvals, pEvals[iNum].Fan0, pArgs, nArgs, Type ); + pNode1 = Ivy_MultiBuild_rec( pEvals, pEvals[iNum].Fan1, pArgs, nArgs, Type ); + return Ivy_Oper( pNode0, pNode1, Type ); +} + +/**Function************************************************************* + + Synopsis [Selection-sorts the nodes in the decreasing over of level.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_MultiSort( Ivy_Obj_t ** pArgs, int nArgs ) +{ + Ivy_Obj_t * pTemp; + int i, j, iBest; + + for ( i = 0; i < nArgs-1; i++ ) + { + iBest = i; + for ( j = i+1; j < nArgs; j++ ) + if ( Ivy_Regular(pArgs[j])->Level > Ivy_Regular(pArgs[iBest])->Level ) + iBest = j; + pTemp = pArgs[i]; + pArgs[i] = pArgs[iBest]; + pArgs[iBest] = pTemp; + } +} + +/**Function************************************************************* + + Synopsis [Inserts a new node in the order by levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_MultiPushUniqueOrderByLevel( Ivy_Obj_t ** pArray, int nArgs, Ivy_Obj_t * pNode ) +{ + Ivy_Obj_t * pNode1, * pNode2; + int i; + // try to find the node in the array + for ( i = 0; i < nArgs; i++ ) + if ( pArray[i] == pNode ) + return nArgs; + // put the node last + pArray[nArgs++] = pNode; + // find the place to put the new node + for ( i = nArgs-1; i > 0; i-- ) + { + pNode1 = pArray[i ]; + pNode2 = pArray[i-1]; + if ( Ivy_Regular(pNode1)->Level <= Ivy_Regular(pNode2)->Level ) + break; + pArray[i ] = pNode2; + pArray[i-1] = pNode1; + } + return nArgs; +} + +/**Function************************************************************* + + Synopsis [Balances the array recursively.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_MultiBalance_rec( Ivy_Obj_t ** pArgs, int nArgs, Ivy_Type_t Type ) +{ + Ivy_Obj_t * pNodeNew; + // consider the case of one argument + assert( nArgs > 0 ); + if ( nArgs == 1 ) + return pArgs[0]; + // consider the case of two arguments + if ( nArgs == 2 ) + return Ivy_Oper( pArgs[0], pArgs[1], Type ); + // get the last two nodes + pNodeNew = Ivy_Oper( pArgs[nArgs-1], pArgs[nArgs-2], Type ); + // add the new node + nArgs = Ivy_MultiPushUniqueOrderByLevel( pArgs, nArgs - 2, pNodeNew ); + return Ivy_MultiBalance_rec( pArgs, nArgs, Type ); +} + +/**Function************************************************************* + + Synopsis [Implements multi-input AND/EXOR operation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_MultiEval( Ivy_Obj_t ** pArgs, int nArgs, Ivy_Type_t Type ) +{ + Ivy_Obj_t * pTemp; + int i, k; + int nArgsOld = nArgs; + for ( i = 0; i < nArgs; i++ ) + printf( "%d[%d] ", i, Ivy_Regular(pArgs[i])->Level ); + for ( i = 1; i < nArgs; i++ ) + for ( k = 0; k < i; k++ ) + { + pTemp = Ivy_TableLookup(Ivy_ObjCreateGhost(pArgs[k], pArgs[i], Type, IVY_INIT_NONE)); + if ( pTemp != NULL ) + { + printf( "%d[%d]=(%d,%d) ", nArgs, Ivy_Regular(pTemp)->Level, k, i ); + pArgs[nArgs++] = pTemp; + } + } + printf( " ((%d/%d)) ", nArgsOld, nArgs-nArgsOld ); + return NULL; +} + + + +/**Function************************************************************* + + Synopsis [Old code.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_Multi1( Ivy_Obj_t ** pArgs, int nArgs, Ivy_Type_t Type ) +{ + Ivy_Obj_t * pArgsRef[5], * pTemp; + int i, k, m, nArgsNew, Counter = 0; + + +//Ivy_MultiEval( pArgs, nArgs, Type ); printf( "\n" ); + + + assert( Type == IVY_AND || Type == IVY_EXOR ); + assert( nArgs > 0 ); + if ( nArgs == 1 ) + return pArgs[0]; + + // find the nodes with more than one fanout + nArgsNew = 0; + for ( i = 0; i < nArgs; i++ ) + if ( Ivy_ObjRefs( Ivy_Regular(pArgs[i]) ) > 0 ) + pArgsRef[nArgsNew++] = pArgs[i]; + + // go through pairs + if ( nArgsNew >= 2 ) + for ( i = 0; i < nArgsNew; i++ ) + for ( k = i + 1; k < nArgsNew; k++ ) + if ( pTemp = Ivy_TableLookup(Ivy_ObjCreateGhost(pArgsRef[i], pArgsRef[k], Type, IVY_INIT_NONE)) ) + Counter++; +// printf( "%d", Counter ); + + // go through pairs + if ( nArgsNew >= 2 ) + for ( i = 0; i < nArgsNew; i++ ) + for ( k = i + 1; k < nArgsNew; k++ ) + if ( pTemp = Ivy_TableLookup(Ivy_ObjCreateGhost(pArgsRef[i], pArgsRef[k], Type, IVY_INIT_NONE)) ) + { + nArgsNew = 0; + for ( m = 0; m < nArgs; m++ ) + if ( pArgs[m] != pArgsRef[i] && pArgs[m] != pArgsRef[k] ) + pArgs[nArgsNew++] = pArgs[m]; + pArgs[nArgsNew++] = pTemp; + assert( nArgsNew == nArgs - 1 ); + return Ivy_Multi1( pArgs, nArgsNew, Type ); + } + return Ivy_Multi_rec( pArgs, nArgs, Type ); +} + +/**Function************************************************************* + + Synopsis [Old code.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_Multi2( Ivy_Obj_t ** pArgs, int nArgs, Ivy_Type_t Type ) +{ + assert( Type == IVY_AND || Type == IVY_EXOR ); + assert( nArgs > 0 ); + return Ivy_Multi_rec( pArgs, nArgs, Type ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyObj.c b/abc_with_bb_support/src/aig/ivy/ivyObj.c new file mode 100644 index 000000000..de8f14789 --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyObj.c @@ -0,0 +1,476 @@ +/**CFile**************************************************************** + + FileName [ivyObj.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [Adding/removing objects.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyObj.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Create the new node assuming it does not exist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_ObjCreatePi( Ivy_Man_t * p ) +{ + return Ivy_ObjCreate( p, Ivy_ObjCreateGhost(p, NULL, NULL, IVY_PI, IVY_INIT_NONE) ); +} + +/**Function************************************************************* + + Synopsis [Create the new node assuming it does not exist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_ObjCreatePo( Ivy_Man_t * p, Ivy_Obj_t * pDriver ) +{ + return Ivy_ObjCreate( p, Ivy_ObjCreateGhost(p, pDriver, NULL, IVY_PO, IVY_INIT_NONE) ); +} + +/**Function************************************************************* + + Synopsis [Create the new node assuming it does not exist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_ObjCreate( Ivy_Man_t * p, Ivy_Obj_t * pGhost ) +{ + Ivy_Obj_t * pObj; + assert( !Ivy_IsComplement(pGhost) ); + assert( Ivy_ObjIsGhost(pGhost) ); + assert( Ivy_TableLookup(p, pGhost) == NULL ); + // get memory for the new object + pObj = Ivy_ManFetchMemory( p ); + assert( Ivy_ObjIsNone(pObj) ); + pObj->Id = Vec_PtrSize(p->vObjs); + Vec_PtrPush( p->vObjs, pObj ); + // add basic info (fanins, compls, type, init) + pObj->Type = pGhost->Type; + pObj->Init = pGhost->Init; + // add connections + Ivy_ObjConnect( p, pObj, pGhost->pFanin0, pGhost->pFanin1 ); + // compute level + if ( Ivy_ObjIsNode(pObj) ) + pObj->Level = Ivy_ObjLevelNew(pObj); + else if ( Ivy_ObjIsLatch(pObj) ) + pObj->Level = 0; + else if ( Ivy_ObjIsOneFanin(pObj) ) + pObj->Level = Ivy_ObjFanin0(pObj)->Level; + else if ( !Ivy_ObjIsPi(pObj) ) + assert( 0 ); + // create phase + if ( Ivy_ObjIsNode(pObj) ) + pObj->fPhase = Ivy_ObjFaninPhase(Ivy_ObjChild0(pObj)) & Ivy_ObjFaninPhase(Ivy_ObjChild1(pObj)); + else if ( Ivy_ObjIsOneFanin(pObj) ) + pObj->fPhase = Ivy_ObjFaninPhase(Ivy_ObjChild0(pObj)); + // set the fail TFO flag + if ( Ivy_ObjIsNode(pObj) ) + pObj->fFailTfo = Ivy_ObjFanin0(pObj)->fFailTfo | Ivy_ObjFanin1(pObj)->fFailTfo; + // mark the fanins in a special way if the node is EXOR + if ( Ivy_ObjIsExor(pObj) ) + { + Ivy_ObjFanin0(pObj)->fExFan = 1; + Ivy_ObjFanin1(pObj)->fExFan = 1; + } + // add PIs/POs to the arrays + if ( Ivy_ObjIsPi(pObj) ) + Vec_PtrPush( p->vPis, pObj ); + else if ( Ivy_ObjIsPo(pObj) ) + Vec_PtrPush( p->vPos, pObj ); +// else if ( Ivy_ObjIsBuf(pObj) ) +// Vec_PtrPush( p->vBufs, pObj ); + if ( p->vRequired && Vec_IntSize(p->vRequired) <= pObj->Id ) + Vec_IntFillExtra( p->vRequired, 2 * Vec_IntSize(p->vRequired), 1000000 ); + // update node counters of the manager + p->nObjs[Ivy_ObjType(pObj)]++; + p->nCreated++; + +// printf( "Adding %sAIG node: ", p->pHaig==NULL? "H":" " ); +// Ivy_ObjPrintVerbose( p, pObj, p->pHaig==NULL ); +// printf( "\n" ); + + // if HAIG is defined, create a corresponding node + if ( p->pHaig ) + Ivy_ManHaigCreateObj( p, pObj ); + return pObj; +} + +/**Function************************************************************* + + Synopsis [Connect the object to the fanin.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ObjConnect( Ivy_Man_t * p, Ivy_Obj_t * pObj, Ivy_Obj_t * pFan0, Ivy_Obj_t * pFan1 ) +{ + assert( !Ivy_IsComplement(pObj) ); + assert( Ivy_ObjIsPi(pObj) || Ivy_ObjIsOneFanin(pObj) || pFan1 != NULL ); + // add the first fanin + pObj->pFanin0 = pFan0; + pObj->pFanin1 = pFan1; + // increment references of the fanins and add their fanouts + if ( Ivy_ObjFanin0(pObj) != NULL ) + { + Ivy_ObjRefsInc( Ivy_ObjFanin0(pObj) ); + if ( p->fFanout ) + Ivy_ObjAddFanout( p, Ivy_ObjFanin0(pObj), pObj ); + } + if ( Ivy_ObjFanin1(pObj) != NULL ) + { + Ivy_ObjRefsInc( Ivy_ObjFanin1(pObj) ); + if ( p->fFanout ) + Ivy_ObjAddFanout( p, Ivy_ObjFanin1(pObj), pObj ); + } + // add the node to the structural hash table + Ivy_TableInsert( p, pObj ); +} + +/**Function************************************************************* + + Synopsis [Connect the object to the fanin.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ObjDisconnect( Ivy_Man_t * p, Ivy_Obj_t * pObj ) +{ + assert( !Ivy_IsComplement(pObj) ); + assert( Ivy_ObjIsPi(pObj) || Ivy_ObjIsOneFanin(pObj) || Ivy_ObjFanin1(pObj) != NULL ); + // remove connections + if ( pObj->pFanin0 != NULL ) + { + Ivy_ObjRefsDec(Ivy_ObjFanin0(pObj)); + if ( p->fFanout ) + Ivy_ObjDeleteFanout( p, Ivy_ObjFanin0(pObj), pObj ); + } + if ( pObj->pFanin1 != NULL ) + { + Ivy_ObjRefsDec(Ivy_ObjFanin1(pObj)); + if ( p->fFanout ) + Ivy_ObjDeleteFanout( p, Ivy_ObjFanin1(pObj), pObj ); + } + assert( pObj->pNextFan0 == NULL ); + assert( pObj->pNextFan1 == NULL ); + assert( pObj->pPrevFan0 == NULL ); + assert( pObj->pPrevFan1 == NULL ); + // remove the node from the structural hash table + Ivy_TableDelete( p, pObj ); + // add the first fanin + pObj->pFanin0 = NULL; + pObj->pFanin1 = NULL; +} + +/**Function************************************************************* + + Synopsis [Replaces the first fanin of the node by the new fanin.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ObjPatchFanin0( Ivy_Man_t * p, Ivy_Obj_t * pObj, Ivy_Obj_t * pFaninNew ) +{ + Ivy_Obj_t * pFaninOld; + assert( !Ivy_IsComplement(pObj) ); + pFaninOld = Ivy_ObjFanin0(pObj); + // decrement ref and remove fanout + Ivy_ObjRefsDec( pFaninOld ); + if ( p->fFanout ) + Ivy_ObjDeleteFanout( p, pFaninOld, pObj ); + // update the fanin + pObj->pFanin0 = pFaninNew; + // increment ref and add fanout + Ivy_ObjRefsInc( Ivy_Regular(pFaninNew) ); + if ( p->fFanout ) + Ivy_ObjAddFanout( p, Ivy_Regular(pFaninNew), pObj ); + // get rid of old fanin + if ( !Ivy_ObjIsPi(pFaninOld) && !Ivy_ObjIsConst1(pFaninOld) && Ivy_ObjRefs(pFaninOld) == 0 ) + Ivy_ObjDelete_rec( p, pFaninOld, 1 ); +} + +/**Function************************************************************* + + Synopsis [Deletes the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ObjDelete( Ivy_Man_t * p, Ivy_Obj_t * pObj, int fFreeTop ) +{ + assert( !Ivy_IsComplement(pObj) ); + assert( Ivy_ObjRefs(pObj) == 0 || !fFreeTop ); + // update node counters of the manager + p->nObjs[pObj->Type]--; + p->nDeleted++; + // remove connections + Ivy_ObjDisconnect( p, pObj ); + // remove PIs/POs from the arrays + if ( Ivy_ObjIsPi(pObj) ) + Vec_PtrRemove( p->vPis, pObj ); + else if ( Ivy_ObjIsPo(pObj) ) + Vec_PtrRemove( p->vPos, pObj ); + else if ( p->fFanout && Ivy_ObjIsBuf(pObj) ) + Vec_PtrRemove( p->vBufs, pObj ); + // clean and recycle the entry + if ( fFreeTop ) + { + // free the node + Vec_PtrWriteEntry( p->vObjs, pObj->Id, NULL ); + Ivy_ManRecycleMemory( p, pObj ); + } + else + { + int nRefsOld = pObj->nRefs; + Ivy_Obj_t * pFanout = pObj->pFanout; + Ivy_ObjClean( pObj ); + pObj->pFanout = pFanout; + pObj->nRefs = nRefsOld; + } +} + +/**Function************************************************************* + + Synopsis [Deletes the MFFC of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ObjDelete_rec( Ivy_Man_t * p, Ivy_Obj_t * pObj, int fFreeTop ) +{ + Ivy_Obj_t * pFanin0, * pFanin1; + assert( !Ivy_IsComplement(pObj) ); + assert( !Ivy_ObjIsNone(pObj) ); + if ( Ivy_ObjIsConst1(pObj) || Ivy_ObjIsPi(pObj) ) + return; + pFanin0 = Ivy_ObjFanin0(pObj); + pFanin1 = Ivy_ObjFanin1(pObj); + Ivy_ObjDelete( p, pObj, fFreeTop ); + if ( pFanin0 && !Ivy_ObjIsNone(pFanin0) && Ivy_ObjRefs(pFanin0) == 0 ) + Ivy_ObjDelete_rec( p, pFanin0, 1 ); + if ( pFanin1 && !Ivy_ObjIsNone(pFanin1) && Ivy_ObjRefs(pFanin1) == 0 ) + Ivy_ObjDelete_rec( p, pFanin1, 1 ); +} + +/**Function************************************************************* + + Synopsis [Replaces one object by another.] + + Description [Both objects are currently in the manager. The new object + (pObjNew) should be used instead of the old object (pObjOld). If the + new object is complemented or used, the buffer is added.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ObjReplace( Ivy_Man_t * p, Ivy_Obj_t * pObjOld, Ivy_Obj_t * pObjNew, int fDeleteOld, int fFreeTop, int fUpdateLevel ) +{ + int nRefsOld;//, clk; + // the object to be replaced cannot be complemented + assert( !Ivy_IsComplement(pObjOld) ); + // the object to be replaced cannot be a terminal + assert( Ivy_ObjIsNone(pObjOld) || !Ivy_ObjIsPi(pObjOld) ); + // the object to be used cannot be a PO or assert + assert( !Ivy_ObjIsBuf(Ivy_Regular(pObjNew)) ); + // the object cannot be the same + assert( pObjOld != Ivy_Regular(pObjNew) ); +//printf( "Replacing %d by %d.\n", Ivy_Regular(pObjOld)->Id, Ivy_Regular(pObjNew)->Id ); + + // if HAIG is defined, create the choice node + if ( p->pHaig ) + { +// if ( pObjOld->Id == 31 ) +// { +// Ivy_ManShow( p, 0 ); +// Ivy_ManShow( p->pHaig, 1 ); +// } + Ivy_ManHaigCreateChoice( p, pObjOld, pObjNew ); + } + // if the new object is complemented or already used, add the buffer + if ( Ivy_IsComplement(pObjNew) || Ivy_ObjIsLatch(pObjNew) || Ivy_ObjRefs(pObjNew) > 0 || Ivy_ObjIsPi(pObjNew) || Ivy_ObjIsConst1(pObjNew) ) + pObjNew = Ivy_ObjCreate( p, Ivy_ObjCreateGhost(p, pObjNew, NULL, IVY_BUF, IVY_INIT_NONE) ); + assert( !Ivy_IsComplement(pObjNew) ); + if ( fUpdateLevel ) + { +//clk = clock(); + // if the new node's arrival time is different, recursively update arrival time of the fanouts + if ( p->fFanout && !Ivy_ObjIsBuf(pObjNew) && pObjOld->Level != pObjNew->Level ) + { + assert( Ivy_ObjIsNode(pObjOld) ); + pObjOld->Level = pObjNew->Level; + Ivy_ObjUpdateLevel_rec( p, pObjOld ); + } +//p->time1 += clock() - clk; + // if the new node's required time has changed, recursively update required time of the fanins +//clk = clock(); + if ( p->vRequired ) + { + int ReqNew = Vec_IntEntry(p->vRequired, pObjOld->Id); + if ( ReqNew < Vec_IntEntry(p->vRequired, pObjNew->Id) ) + { + Vec_IntWriteEntry( p->vRequired, pObjNew->Id, ReqNew ); + Ivy_ObjUpdateLevelR_rec( p, pObjNew, ReqNew ); + } + } +//p->time2 += clock() - clk; + } + // delete the old object + if ( fDeleteOld ) + Ivy_ObjDelete_rec( p, pObjOld, fFreeTop ); + // make sure object is not pointing to itself + assert( Ivy_ObjFanin0(pObjNew) == NULL || pObjOld != Ivy_ObjFanin0(pObjNew) ); + assert( Ivy_ObjFanin1(pObjNew) == NULL || pObjOld != Ivy_ObjFanin1(pObjNew) ); + // make sure the old node has no fanin fanout pointers + if ( p->fFanout ) + { + assert( pObjOld->pFanout != NULL ); + assert( pObjNew->pFanout == NULL ); + pObjNew->pFanout = pObjOld->pFanout; + } + // transfer the old object + assert( Ivy_ObjRefs(pObjNew) == 0 ); + nRefsOld = pObjOld->nRefs; + Ivy_ObjOverwrite( pObjOld, pObjNew ); + pObjOld->nRefs = nRefsOld; + // patch the fanout of the fanins + if ( p->fFanout ) + { + Ivy_ObjPatchFanout( p, Ivy_ObjFanin0(pObjOld), pObjNew, pObjOld ); + if ( Ivy_ObjFanin1(pObjOld) ) + Ivy_ObjPatchFanout( p, Ivy_ObjFanin1(pObjOld), pObjNew, pObjOld ); + } + // update the hash table + Ivy_TableUpdate( p, pObjNew, pObjOld->Id ); + // recycle the object that was taken over by pObjOld + Vec_PtrWriteEntry( p->vObjs, pObjNew->Id, NULL ); + Ivy_ManRecycleMemory( p, pObjNew ); + // if the new node is the buffer propagate it + if ( p->fFanout && Ivy_ObjIsBuf(pObjOld) ) + Vec_PtrPush( p->vBufs, pObjOld ); +// Ivy_ManCheckFanouts( p ); +// printf( "\n" ); +/* + if ( p->pHaig ) + { + int x; + Ivy_ManShow( p, 0, NULL ); + Ivy_ManShow( p->pHaig, 1, NULL ); + x = 0; + } +*/ +// if ( Ivy_ManCheckFanoutNums(p) ) +// { +// int x = 0; +// } +} + +/**Function************************************************************* + + Synopsis [Fixes buffer fanins.] + + Description [This situation happens because NodeReplace is a lazy + procedure, which does not propagate the change to the fanouts but + instead records the change in the form of a buf/inv node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_NodeFixBufferFanins( Ivy_Man_t * p, Ivy_Obj_t * pNode, int fUpdateLevel ) +{ + Ivy_Obj_t * pFanReal0, * pFanReal1, * pResult; + if ( Ivy_ObjIsPo(pNode) ) + { + if ( !Ivy_ObjIsBuf(Ivy_ObjFanin0(pNode)) ) + return; + pFanReal0 = Ivy_ObjReal( Ivy_ObjChild0(pNode) ); + Ivy_ObjPatchFanin0( p, pNode, pFanReal0 ); +// Ivy_ManCheckFanouts( p ); + return; + } + if ( !Ivy_ObjIsBuf(Ivy_ObjFanin0(pNode)) && !Ivy_ObjIsBuf(Ivy_ObjFanin1(pNode)) ) + return; + // get the real fanins + pFanReal0 = Ivy_ObjReal( Ivy_ObjChild0(pNode) ); + pFanReal1 = Ivy_ObjReal( Ivy_ObjChild1(pNode) ); + // get the new node + if ( Ivy_ObjIsNode(pNode) ) + pResult = Ivy_Oper( p, pFanReal0, pFanReal1, Ivy_ObjType(pNode) ); + else if ( Ivy_ObjIsLatch(pNode) ) + pResult = Ivy_Latch( p, pFanReal0, Ivy_ObjInit(pNode) ); + else + assert( 0 ); + +//printf( "===== Replacing %d by %d.\n", pNode->Id, pResult->Id ); +//Ivy_ObjPrintVerbose( p, pNode, 0 ); printf( "\n" ); +//Ivy_ObjPrintVerbose( p, pResult, 0 ); printf( "\n" ); + + // perform the replacement + Ivy_ObjReplace( p, pNode, pResult, 1, 0, fUpdateLevel ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyOper.c b/abc_with_bb_support/src/aig/ivy/ivyOper.c new file mode 100644 index 000000000..0d204c6b7 --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyOper.c @@ -0,0 +1,293 @@ +/**CFile**************************************************************** + + FileName [ivyOper.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [AIG operations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyOper.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// procedure to detect an EXOR gate +static inline int Ivy_ObjIsExorType( Ivy_Obj_t * p0, Ivy_Obj_t * p1, Ivy_Obj_t ** ppFan0, Ivy_Obj_t ** ppFan1 ) +{ + if ( !Ivy_IsComplement(p0) || !Ivy_IsComplement(p1) ) + return 0; + p0 = Ivy_Regular(p0); + p1 = Ivy_Regular(p1); + if ( !Ivy_ObjIsAnd(p0) || !Ivy_ObjIsAnd(p1) ) + return 0; + if ( Ivy_ObjFanin0(p0) != Ivy_ObjFanin0(p1) || Ivy_ObjFanin1(p0) != Ivy_ObjFanin1(p1) ) + return 0; + if ( Ivy_ObjFaninC0(p0) == Ivy_ObjFaninC0(p1) || Ivy_ObjFaninC1(p0) == Ivy_ObjFaninC1(p1) ) + return 0; + *ppFan0 = Ivy_ObjChild0(p0); + *ppFan1 = Ivy_ObjChild1(p0); + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Perform one operation.] + + Description [The argument nodes can be complemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_Oper( Ivy_Man_t * p, Ivy_Obj_t * p0, Ivy_Obj_t * p1, Ivy_Type_t Type ) +{ + if ( Type == IVY_AND ) + return Ivy_And( p, p0, p1 ); + if ( Type == IVY_EXOR ) + return Ivy_Exor( p, p0, p1 ); + assert( 0 ); + return NULL; +} + +/**Function************************************************************* + + Synopsis [Performs canonicization step.] + + Description [The argument nodes can be complemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_And( Ivy_Man_t * p, Ivy_Obj_t * p0, Ivy_Obj_t * p1 ) +{ +// Ivy_Obj_t * pFan0, * pFan1; + // check trivial cases + if ( p0 == p1 ) + return p0; + if ( p0 == Ivy_Not(p1) ) + return Ivy_Not(p->pConst1); + if ( Ivy_Regular(p0) == p->pConst1 ) + return p0 == p->pConst1 ? p1 : Ivy_Not(p->pConst1); + if ( Ivy_Regular(p1) == p->pConst1 ) + return p1 == p->pConst1 ? p0 : Ivy_Not(p->pConst1); + // check if it can be an EXOR gate +// if ( Ivy_ObjIsExorType( p0, p1, &pFan0, &pFan1 ) ) +// return Ivy_CanonExor( pFan0, pFan1 ); + return Ivy_CanonAnd( p, p0, p1 ); +} + +/**Function************************************************************* + + Synopsis [Performs canonicization step.] + + Description [The argument nodes can be complemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_Exor( Ivy_Man_t * p, Ivy_Obj_t * p0, Ivy_Obj_t * p1 ) +{ +/* + // check trivial cases + if ( p0 == p1 ) + return Ivy_Not(p->pConst1); + if ( p0 == Ivy_Not(p1) ) + return p->pConst1; + if ( Ivy_Regular(p0) == p->pConst1 ) + return Ivy_NotCond( p1, p0 == p->pConst1 ); + if ( Ivy_Regular(p1) == p->pConst1 ) + return Ivy_NotCond( p0, p1 == p->pConst1 ); + // check the table + return Ivy_CanonExor( p, p0, p1 ); +*/ + return Ivy_Or( p, Ivy_And(p, p0, Ivy_Not(p1)), Ivy_And(p, Ivy_Not(p0), p1) ); +} + +/**Function************************************************************* + + Synopsis [Implements Boolean OR.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_Or( Ivy_Man_t * p, Ivy_Obj_t * p0, Ivy_Obj_t * p1 ) +{ + return Ivy_Not( Ivy_And( p, Ivy_Not(p0), Ivy_Not(p1) ) ); +} + +/**Function************************************************************* + + Synopsis [Implements ITE operation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_Mux( Ivy_Man_t * p, Ivy_Obj_t * pC, Ivy_Obj_t * p1, Ivy_Obj_t * p0 ) +{ + Ivy_Obj_t * pTempA1, * pTempA2, * pTempB1, * pTempB2, * pTemp; + int Count0, Count1; + // consider trivial cases + if ( p0 == Ivy_Not(p1) ) + return Ivy_Exor( p, pC, p0 ); + // other cases can be added + // implement the first MUX (F = C * x1 + C' * x0) + pTempA1 = Ivy_TableLookup( p, Ivy_ObjCreateGhost(p, pC, p1, IVY_AND, IVY_INIT_NONE) ); + pTempA2 = Ivy_TableLookup( p, Ivy_ObjCreateGhost(p, Ivy_Not(pC), p0, IVY_AND, IVY_INIT_NONE) ); + if ( pTempA1 && pTempA2 ) + { + pTemp = Ivy_TableLookup( p, Ivy_ObjCreateGhost(p, Ivy_Not(pTempA1), Ivy_Not(pTempA2), IVY_AND, IVY_INIT_NONE) ); + if ( pTemp ) return Ivy_Not(pTemp); + } + Count0 = (pTempA1 != NULL) + (pTempA2 != NULL); + // implement the second MUX (F' = C * x1' + C' * x0') + pTempB1 = Ivy_TableLookup( p, Ivy_ObjCreateGhost(p, pC, Ivy_Not(p1), IVY_AND, IVY_INIT_NONE) ); + pTempB2 = Ivy_TableLookup( p, Ivy_ObjCreateGhost(p, Ivy_Not(pC), Ivy_Not(p0), IVY_AND, IVY_INIT_NONE) ); + if ( pTempB1 && pTempB2 ) + { + pTemp = Ivy_TableLookup( p, Ivy_ObjCreateGhost(p, Ivy_Not(pTempB1), Ivy_Not(pTempB2), IVY_AND, IVY_INIT_NONE) ); + if ( pTemp ) return pTemp; + } + Count1 = (pTempB1 != NULL) + (pTempB2 != NULL); + // compare and decide which one to implement + if ( Count0 >= Count1 ) + { + pTempA1 = pTempA1? pTempA1 : Ivy_And(p, pC, p1); + pTempA2 = pTempA2? pTempA2 : Ivy_And(p, Ivy_Not(pC), p0); + return Ivy_Or( p, pTempA1, pTempA2 ); + } + pTempB1 = pTempB1? pTempB1 : Ivy_And(p, pC, Ivy_Not(p1)); + pTempB2 = pTempB2? pTempB2 : Ivy_And(p, Ivy_Not(pC), Ivy_Not(p0)); + return Ivy_Not( Ivy_Or( p, pTempB1, pTempB2 ) ); + +// return Ivy_Or( Ivy_And(pC, p1), Ivy_And(Ivy_Not(pC), p0) ); +} + +/**Function************************************************************* + + Synopsis [Implements ITE operation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_Maj( Ivy_Man_t * p, Ivy_Obj_t * pA, Ivy_Obj_t * pB, Ivy_Obj_t * pC ) +{ + return Ivy_Or( p, Ivy_Or(p, Ivy_And(p, pA, pB), Ivy_And(p, pA, pC)), Ivy_And(p, pB, pC) ); +} + +/**Function************************************************************* + + Synopsis [Constructs the well-balanced tree of gates.] + + Description [Disregards levels and possible logic sharing.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_Multi_rec( Ivy_Man_t * p, Ivy_Obj_t ** ppObjs, int nObjs, Ivy_Type_t Type ) +{ + Ivy_Obj_t * pObj1, * pObj2; + if ( nObjs == 1 ) + return ppObjs[0]; + pObj1 = Ivy_Multi_rec( p, ppObjs, nObjs/2, Type ); + pObj2 = Ivy_Multi_rec( p, ppObjs + nObjs/2, nObjs - nObjs/2, Type ); + return Ivy_Oper( p, pObj1, pObj2, Type ); +} + +/**Function************************************************************* + + Synopsis [Old code.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_Multi( Ivy_Man_t * p, Ivy_Obj_t ** pArgs, int nArgs, Ivy_Type_t Type ) +{ + assert( Type == IVY_AND || Type == IVY_EXOR ); + assert( nArgs > 0 ); + return Ivy_Multi_rec( p, pArgs, nArgs, Type ); +} + +/**Function************************************************************* + + Synopsis [Implements the miter.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_Miter( Ivy_Man_t * p, Vec_Ptr_t * vPairs ) +{ + int i; + assert( vPairs->nSize > 0 ); + assert( vPairs->nSize % 2 == 0 ); + // go through the cubes of the node's SOP + for ( i = 0; i < vPairs->nSize; i += 2 ) + vPairs->pArray[i/2] = Ivy_Not( Ivy_Exor( p, vPairs->pArray[i], vPairs->pArray[i+1] ) ); + vPairs->nSize = vPairs->nSize/2; + return Ivy_Not( Ivy_Multi_rec( p, (Ivy_Obj_t **)vPairs->pArray, vPairs->nSize, IVY_AND ) ); +} + +/**Function************************************************************* + + Synopsis [Performs canonicization step.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_Latch( Ivy_Man_t * p, Ivy_Obj_t * pObj, Ivy_Init_t Init ) +{ + return Ivy_CanonLatch( p, pObj, Init ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyResyn.c b/abc_with_bb_support/src/aig/ivy/ivyResyn.c new file mode 100644 index 000000000..43f3d719b --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyResyn.c @@ -0,0 +1,196 @@ +/**CFile**************************************************************** + + FileName [ivyResyn.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [AIG rewriting script.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyResyn.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs several passes of rewriting on the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Man_t * Ivy_ManResyn0( Ivy_Man_t * pMan, int fUpdateLevel, int fVerbose ) +{ + int clk; + Ivy_Man_t * pTemp; + +if ( fVerbose ) { printf( "Original:\n" ); } +if ( fVerbose ) Ivy_ManPrintStats( pMan ); + +clk = clock(); + pMan = Ivy_ManBalance( pMan, fUpdateLevel ); +if ( fVerbose ) { printf( "\n" ); } +if ( fVerbose ) { PRT( "Balance", clock() - clk ); } +if ( fVerbose ) Ivy_ManPrintStats( pMan ); + +// Ivy_ManRewriteAlg( pMan, fUpdateLevel, 0 ); +clk = clock(); + Ivy_ManRewritePre( pMan, fUpdateLevel, 0, 0 ); +if ( fVerbose ) { printf( "\n" ); } +if ( fVerbose ) { PRT( "Rewrite", clock() - clk ); } +if ( fVerbose ) Ivy_ManPrintStats( pMan ); + +clk = clock(); + pMan = Ivy_ManBalance( pTemp = pMan, fUpdateLevel ); + Ivy_ManStop( pTemp ); +if ( fVerbose ) { printf( "\n" ); } +if ( fVerbose ) { PRT( "Balance", clock() - clk ); } +if ( fVerbose ) Ivy_ManPrintStats( pMan ); + return pMan; +} + +/**Function************************************************************* + + Synopsis [Performs several passes of rewriting on the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Man_t * Ivy_ManResyn( Ivy_Man_t * pMan, int fUpdateLevel, int fVerbose ) +{ + int clk; + Ivy_Man_t * pTemp; + +if ( fVerbose ) { printf( "Original:\n" ); } +if ( fVerbose ) Ivy_ManPrintStats( pMan ); + +clk = clock(); + pMan = Ivy_ManBalance( pMan, fUpdateLevel ); +if ( fVerbose ) { printf( "\n" ); } +if ( fVerbose ) { PRT( "Balance", clock() - clk ); } +if ( fVerbose ) Ivy_ManPrintStats( pMan ); + +// Ivy_ManRewriteAlg( pMan, fUpdateLevel, 0 ); +clk = clock(); + Ivy_ManRewritePre( pMan, fUpdateLevel, 0, 0 ); +if ( fVerbose ) { printf( "\n" ); } +if ( fVerbose ) { PRT( "Rewrite", clock() - clk ); } +if ( fVerbose ) Ivy_ManPrintStats( pMan ); + +clk = clock(); + pMan = Ivy_ManBalance( pTemp = pMan, fUpdateLevel ); + Ivy_ManStop( pTemp ); +if ( fVerbose ) { printf( "\n" ); } +if ( fVerbose ) { PRT( "Balance", clock() - clk ); } +if ( fVerbose ) Ivy_ManPrintStats( pMan ); + +// Ivy_ManRewriteAlg( pMan, fUpdateLevel, 1 ); +clk = clock(); + Ivy_ManRewritePre( pMan, fUpdateLevel, 1, 0 ); +if ( fVerbose ) { printf( "\n" ); } +if ( fVerbose ) { PRT( "Rewrite", clock() - clk ); } +if ( fVerbose ) Ivy_ManPrintStats( pMan ); + +clk = clock(); + pMan = Ivy_ManBalance( pTemp = pMan, fUpdateLevel ); + Ivy_ManStop( pTemp ); +if ( fVerbose ) { printf( "\n" ); } +if ( fVerbose ) { PRT( "Balance", clock() - clk ); } +if ( fVerbose ) Ivy_ManPrintStats( pMan ); + +// Ivy_ManRewriteAlg( pMan, fUpdateLevel, 1 ); +clk = clock(); + Ivy_ManRewritePre( pMan, fUpdateLevel, 1, 0 ); +if ( fVerbose ) { printf( "\n" ); } +if ( fVerbose ) { PRT( "Rewrite", clock() - clk ); } +if ( fVerbose ) Ivy_ManPrintStats( pMan ); + +clk = clock(); + pMan = Ivy_ManBalance( pTemp = pMan, fUpdateLevel ); + Ivy_ManStop( pTemp ); +if ( fVerbose ) { printf( "\n" ); } +if ( fVerbose ) { PRT( "Balance", clock() - clk ); } +if ( fVerbose ) Ivy_ManPrintStats( pMan ); + return pMan; +} + +/**Function************************************************************* + + Synopsis [Performs several passes of rewriting on the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Man_t * Ivy_ManRwsat( Ivy_Man_t * pMan, int fVerbose ) +{ + int clk; + Ivy_Man_t * pTemp; + +if ( fVerbose ) { printf( "Original:\n" ); } +if ( fVerbose ) Ivy_ManPrintStats( pMan ); + +clk = clock(); + Ivy_ManRewritePre( pMan, 0, 0, 0 ); +if ( fVerbose ) { printf( "\n" ); } +if ( fVerbose ) { PRT( "Rewrite", clock() - clk ); } +if ( fVerbose ) Ivy_ManPrintStats( pMan ); + +clk = clock(); + pMan = Ivy_ManBalance( pTemp = pMan, 0 ); +// pMan = Ivy_ManDup( pTemp = pMan ); + Ivy_ManStop( pTemp ); +if ( fVerbose ) { printf( "\n" ); } +if ( fVerbose ) { PRT( "Balance", clock() - clk ); } +if ( fVerbose ) Ivy_ManPrintStats( pMan ); + +/* +clk = clock(); + Ivy_ManRewritePre( pMan, 0, 0, 0 ); +if ( fVerbose ) { printf( "\n" ); } +if ( fVerbose ) { PRT( "Rewrite", clock() - clk ); } +if ( fVerbose ) Ivy_ManPrintStats( pMan ); + +clk = clock(); + pMan = Ivy_ManBalance( pTemp = pMan, 0 ); + Ivy_ManStop( pTemp ); +if ( fVerbose ) { printf( "\n" ); } +if ( fVerbose ) { PRT( "Balance", clock() - clk ); } +if ( fVerbose ) Ivy_ManPrintStats( pMan ); +*/ + return pMan; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyRwr.c b/abc_with_bb_support/src/aig/ivy/ivyRwr.c new file mode 100644 index 000000000..148e4e008 --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyRwr.c @@ -0,0 +1,609 @@ +/**CFile**************************************************************** + + FileName [ivyRwt.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [Rewriting based on precomputation.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyRwt.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" +#include "deco.h" +#include "rwt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static unsigned Ivy_NodeGetTruth( Ivy_Obj_t * pObj, int * pNums, int nNums ); +static int Ivy_NodeRewrite( Ivy_Man_t * pMan, Rwt_Man_t * p, Ivy_Obj_t * pNode, int fUpdateLevel, int fUseZeroCost ); +static Dec_Graph_t * Rwt_CutEvaluate( Ivy_Man_t * pMan, Rwt_Man_t * p, Ivy_Obj_t * pRoot, + Vec_Ptr_t * vFaninsCur, int nNodesSaved, int LevelMax, int * pGainBest, unsigned uTruth ); + +static int Ivy_GraphToNetworkCount( Ivy_Man_t * p, Ivy_Obj_t * pRoot, Dec_Graph_t * pGraph, int NodeMax, int LevelMax ); +static void Ivy_GraphUpdateNetwork( Ivy_Man_t * p, Ivy_Obj_t * pRoot, Dec_Graph_t * pGraph, int fUpdateLevel, int nGain ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs incremental rewriting of the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManRewritePre( Ivy_Man_t * p, int fUpdateLevel, int fUseZeroCost, int fVerbose ) +{ + Rwt_Man_t * pManRwt; + Ivy_Obj_t * pNode; + int i, nNodes, nGain; + int clk, clkStart = clock(); + // start the rewriting manager + pManRwt = Rwt_ManStart( 0 ); + p->pData = pManRwt; + if ( pManRwt == NULL ) + return 0; + // create fanouts + if ( fUpdateLevel && p->fFanout == 0 ) + Ivy_ManStartFanout( p ); + // compute the reverse levels if level update is requested + if ( fUpdateLevel ) + Ivy_ManRequiredLevels( p ); + // set the number of levels +// p->nLevelMax = Ivy_ManLevels( p ); + // resynthesize each node once + nNodes = Ivy_ManObjIdMax(p); + Ivy_ManForEachNode( p, pNode, i ) + { + // fix the fanin buffer problem + Ivy_NodeFixBufferFanins( p, pNode, 1 ); + if ( Ivy_ObjIsBuf(pNode) ) + continue; + // stop if all nodes have been tried once + if ( i > nNodes ) + break; + // for each cut, try to resynthesize it + nGain = Ivy_NodeRewrite( p, pManRwt, pNode, fUpdateLevel, fUseZeroCost ); + if ( nGain > 0 || nGain == 0 && fUseZeroCost ) + { + Dec_Graph_t * pGraph = Rwt_ManReadDecs(pManRwt); + int fCompl = Rwt_ManReadCompl(pManRwt); +/* + { + Ivy_Obj_t * pObj; + int i; + printf( "USING: (" ); + Vec_PtrForEachEntry( Rwt_ManReadLeaves(pManRwt), pObj, i ) + printf( "%d ", Ivy_ObjFanoutNum(Ivy_Regular(pObj)) ); + printf( ") Gain = %d.\n", nGain ); + } + if ( nGain > 0 ) + { // print stats on the MFFC + extern void Ivy_NodeMffsConeSuppPrint( Ivy_Obj_t * pNode ); + printf( "Node %6d : Gain = %4d ", pNode->Id, nGain ); + Ivy_NodeMffsConeSuppPrint( pNode ); + } +*/ + // complement the FF if needed +clk = clock(); + if ( fCompl ) Dec_GraphComplement( pGraph ); + Ivy_GraphUpdateNetwork( p, pNode, pGraph, fUpdateLevel, nGain ); + if ( fCompl ) Dec_GraphComplement( pGraph ); +Rwt_ManAddTimeUpdate( pManRwt, clock() - clk ); + } + } +Rwt_ManAddTimeTotal( pManRwt, clock() - clkStart ); + // print stats + if ( fVerbose ) + Rwt_ManPrintStats( pManRwt ); + // delete the managers + Rwt_ManStop( pManRwt ); + p->pData = NULL; + // fix the levels + if ( fUpdateLevel ) + Vec_IntFree( p->vRequired ), p->vRequired = NULL; + else + Ivy_ManResetLevels( p ); + // check + if ( i = Ivy_ManCleanup(p) ) + printf( "Cleanup after rewriting removed %d dangling nodes.\n", i ); + if ( !Ivy_ManCheck(p) ) + printf( "Ivy_ManRewritePre(): The check has failed.\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Performs rewriting for one node.] + + Description [This procedure considers all the cuts computed for the node + and tries to rewrite each of them using the "forest" of different AIG + structures precomputed and stored in the RWR manager. + Determines the best rewriting and computes the gain in the number of AIG + nodes in the final network. In the end, p->vFanins contains information + about the best cut that can be used for rewriting, while p->pGraph gives + the decomposition dag (represented using decomposition graph data structure). + Returns gain in the number of nodes or -1 if node cannot be rewritten.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_NodeRewrite( Ivy_Man_t * pMan, Rwt_Man_t * p, Ivy_Obj_t * pNode, int fUpdateLevel, int fUseZeroCost ) +{ + int fVeryVerbose = 0; + Dec_Graph_t * pGraph; + Ivy_Store_t * pStore; + Ivy_Cut_t * pCut; + Ivy_Obj_t * pFanin; + unsigned uPhase, uTruthBest, uTruth; + char * pPerm; + int Required, nNodesSaved, nNodesSaveCur; + int i, c, GainCur, GainBest = -1; + int clk, clk2; + + p->nNodesConsidered++; + // get the required times + Required = fUpdateLevel? Vec_IntEntry( pMan->vRequired, pNode->Id ) : 1000000; + // get the node's cuts +clk = clock(); + pStore = Ivy_NodeFindCutsAll( pMan, pNode, 5 ); +p->timeCut += clock() - clk; + + // go through the cuts +clk = clock(); + for ( c = 1; c < pStore->nCuts; c++ ) + { + pCut = pStore->pCuts + c; + // consider only 4-input cuts + if ( pCut->nSize != 4 ) + continue; + // skip the cuts with buffers + for ( i = 0; i < (int)pCut->nSize; i++ ) + if ( Ivy_ObjIsBuf( Ivy_ManObj(pMan, pCut->pArray[i]) ) ) + break; + if ( i != pCut->nSize ) + { + p->nCutsBad++; + continue; + } + p->nCutsGood++; + // get the fanin permutation +clk2 = clock(); + uTruth = 0xFFFF & Ivy_NodeGetTruth( pNode, pCut->pArray, pCut->nSize ); // truth table +p->timeTruth += clock() - clk2; + pPerm = p->pPerms4[ p->pPerms[uTruth] ]; + uPhase = p->pPhases[uTruth]; + // collect fanins with the corresponding permutation/phase + Vec_PtrClear( p->vFaninsCur ); + Vec_PtrFill( p->vFaninsCur, (int)pCut->nSize, 0 ); + for ( i = 0; i < (int)pCut->nSize; i++ ) + { + pFanin = Ivy_ManObj( pMan, pCut->pArray[pPerm[i]] ); + assert( Ivy_ObjIsNode(pFanin) || Ivy_ObjIsCi(pFanin) ); + pFanin = Ivy_NotCond(pFanin, ((uPhase & (1< 0) ); + Vec_PtrWriteEntry( p->vFaninsCur, i, pFanin ); + } +clk2 = clock(); +/* + printf( "Considering: (" ); + Vec_PtrForEachEntry( p->vFaninsCur, pFanin, i ) + printf( "%d ", Ivy_ObjFanoutNum(Ivy_Regular(pFanin)) ); + printf( ")\n" ); +*/ + // mark the fanin boundary + Vec_PtrForEachEntry( p->vFaninsCur, pFanin, i ) + Ivy_ObjRefsInc( Ivy_Regular(pFanin) ); + // label MFFC with current ID + Ivy_ManIncrementTravId( pMan ); + nNodesSaved = Ivy_ObjMffcLabel( pMan, pNode ); + // unmark the fanin boundary + Vec_PtrForEachEntry( p->vFaninsCur, pFanin, i ) + Ivy_ObjRefsDec( Ivy_Regular(pFanin) ); +p->timeMffc += clock() - clk2; + + // evaluate the cut +clk2 = clock(); + pGraph = Rwt_CutEvaluate( pMan, p, pNode, p->vFaninsCur, nNodesSaved, Required, &GainCur, uTruth ); +p->timeEval += clock() - clk2; + + // check if the cut is better than the current best one + if ( pGraph != NULL && GainBest < GainCur ) + { + // save this form + nNodesSaveCur = nNodesSaved; + GainBest = GainCur; + p->pGraph = pGraph; + p->fCompl = ((uPhase & (1<<4)) > 0); + uTruthBest = uTruth; + // collect fanins in the + Vec_PtrClear( p->vFanins ); + Vec_PtrForEachEntry( p->vFaninsCur, pFanin, i ) + Vec_PtrPush( p->vFanins, pFanin ); + } + } +p->timeRes += clock() - clk; + + if ( GainBest == -1 ) + return -1; + +// printf( "%d", nNodesSaveCur - GainBest ); +/* + if ( GainBest > 0 ) + { + if ( Rwt_CutIsintean( pNode, p->vFanins ) ) + printf( "b" ); + else + { + printf( "Node %d : ", pNode->Id ); + Vec_PtrForEachEntry( p->vFanins, pFanin, i ) + printf( "%d ", Ivy_Regular(pFanin)->Id ); + printf( "a" ); + } + } +*/ +/* + if ( GainBest > 0 ) + if ( p->fCompl ) + printf( "c" ); + else + printf( "." ); +*/ + + // copy the leaves + Vec_PtrForEachEntry( p->vFanins, pFanin, i ) + Dec_GraphNode(p->pGraph, i)->pFunc = pFanin; + + p->nScores[p->pMap[uTruthBest]]++; + p->nNodesGained += GainBest; + if ( fUseZeroCost || GainBest > 0 ) + p->nNodesRewritten++; + + // report the progress + if ( fVeryVerbose && GainBest > 0 ) + { + printf( "Node %6d : ", Ivy_ObjId(pNode) ); + printf( "Fanins = %d. ", p->vFanins->nSize ); + printf( "Save = %d. ", nNodesSaveCur ); + printf( "Add = %d. ", nNodesSaveCur-GainBest ); + printf( "GAIN = %d. ", GainBest ); + printf( "Cone = %d. ", p->pGraph? Dec_GraphNodeNum(p->pGraph) : 0 ); + printf( "Class = %d. ", p->pMap[uTruthBest] ); + printf( "\n" ); + } + return GainBest; +} + +/**Function************************************************************* + + Synopsis [Computes the truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Ivy_NodeGetTruth_rec( Ivy_Obj_t * pObj, int * pNums, int nNums ) +{ + static unsigned uMasks[5] = { 0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, 0xFF00FF00, 0xFFFF0000 }; + unsigned uTruth0, uTruth1; + int i; + for ( i = 0; i < nNums; i++ ) + if ( pObj->Id == pNums[i] ) + return uMasks[i]; + assert( Ivy_ObjIsNode(pObj) || Ivy_ObjIsBuf(pObj) ); + uTruth0 = Ivy_NodeGetTruth_rec( Ivy_ObjFanin0(pObj), pNums, nNums ); + if ( Ivy_ObjFaninC0(pObj) ) + uTruth0 = ~uTruth0; + if ( Ivy_ObjIsBuf(pObj) ) + return uTruth0; + uTruth1 = Ivy_NodeGetTruth_rec( Ivy_ObjFanin1(pObj), pNums, nNums ); + if ( Ivy_ObjFaninC1(pObj) ) + uTruth1 = ~uTruth1; + return uTruth0 & uTruth1; +} + + +/**Function************************************************************* + + Synopsis [Computes the truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Ivy_NodeGetTruth( Ivy_Obj_t * pObj, int * pNums, int nNums ) +{ + assert( nNums < 6 ); + return Ivy_NodeGetTruth_rec( pObj, pNums, nNums ); +} + +/**Function************************************************************* + + Synopsis [Evaluates the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Rwt_CutEvaluate( Ivy_Man_t * pMan, Rwt_Man_t * p, Ivy_Obj_t * pRoot, Vec_Ptr_t * vFaninsCur, int nNodesSaved, int LevelMax, int * pGainBest, unsigned uTruth ) +{ + Vec_Ptr_t * vSubgraphs; + Dec_Graph_t * pGraphBest, * pGraphCur; + Rwt_Node_t * pNode, * pFanin; + int nNodesAdded, GainBest, i, k; + // find the matching class of subgraphs + vSubgraphs = Vec_VecEntry( p->vClasses, p->pMap[uTruth] ); + p->nSubgraphs += vSubgraphs->nSize; + // determine the best subgraph + GainBest = -1; + Vec_PtrForEachEntry( vSubgraphs, pNode, i ) + { + // get the current graph + pGraphCur = (Dec_Graph_t *)pNode->pNext; + // copy the leaves + Vec_PtrForEachEntry( vFaninsCur, pFanin, k ) + Dec_GraphNode(pGraphCur, k)->pFunc = pFanin; + // detect how many unlabeled nodes will be reused + nNodesAdded = Ivy_GraphToNetworkCount( pMan, pRoot, pGraphCur, nNodesSaved, LevelMax ); + if ( nNodesAdded == -1 ) + continue; + assert( nNodesSaved >= nNodesAdded ); + // count the gain at this node + if ( GainBest < nNodesSaved - nNodesAdded ) + { + GainBest = nNodesSaved - nNodesAdded; + pGraphBest = pGraphCur; + } + } + if ( GainBest == -1 ) + return NULL; + *pGainBest = GainBest; + return pGraphBest; +} + + +/**Function************************************************************* + + Synopsis [Counts the number of new nodes added when using this graph.] + + Description [AIG nodes for the fanins should be assigned to pNode->pFunc + of the leaves of the graph before calling this procedure. + Returns -1 if the number of nodes and levels exceeded the given limit or + the number of levels exceeded the maximum allowed level.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_GraphToNetworkCount( Ivy_Man_t * p, Ivy_Obj_t * pRoot, Dec_Graph_t * pGraph, int NodeMax, int LevelMax ) +{ + Dec_Node_t * pNode, * pNode0, * pNode1; + Ivy_Obj_t * pAnd, * pAnd0, * pAnd1; + int i, Counter, LevelNew, LevelOld; + // check for constant function or a literal + if ( Dec_GraphIsConst(pGraph) || Dec_GraphIsVar(pGraph) ) + return 0; + // set the levels of the leaves + Dec_GraphForEachLeaf( pGraph, pNode, i ) + pNode->Level = Ivy_Regular(pNode->pFunc)->Level; + // compute the AIG size after adding the internal nodes + Counter = 0; + Dec_GraphForEachNode( pGraph, pNode, i ) + { + // get the children of this node + pNode0 = Dec_GraphNode( pGraph, pNode->eEdge0.Node ); + pNode1 = Dec_GraphNode( pGraph, pNode->eEdge1.Node ); + // get the AIG nodes corresponding to the children + pAnd0 = pNode0->pFunc; + pAnd1 = pNode1->pFunc; + if ( pAnd0 && pAnd1 ) + { + // if they are both present, find the resulting node + pAnd0 = Ivy_NotCond( pAnd0, pNode->eEdge0.fCompl ); + pAnd1 = Ivy_NotCond( pAnd1, pNode->eEdge1.fCompl ); + pAnd = Ivy_TableLookup( p, Ivy_ObjCreateGhost(p, pAnd0, pAnd1, IVY_AND, IVY_INIT_NONE) ); + // return -1 if the node is the same as the original root + if ( Ivy_Regular(pAnd) == pRoot ) + return -1; + } + else + pAnd = NULL; + // count the number of added nodes + if ( pAnd == NULL || Ivy_ObjIsTravIdCurrent(p, Ivy_Regular(pAnd)) ) + { + if ( ++Counter > NodeMax ) + return -1; + } + // count the number of new levels + LevelNew = 1 + RWT_MAX( pNode0->Level, pNode1->Level ); + if ( pAnd ) + { + if ( Ivy_Regular(pAnd) == p->pConst1 ) + LevelNew = 0; + else if ( Ivy_Regular(pAnd) == Ivy_Regular(pAnd0) ) + LevelNew = (int)Ivy_Regular(pAnd0)->Level; + else if ( Ivy_Regular(pAnd) == Ivy_Regular(pAnd1) ) + LevelNew = (int)Ivy_Regular(pAnd1)->Level; + LevelOld = (int)Ivy_Regular(pAnd)->Level; +// assert( LevelNew == LevelOld ); + } + if ( LevelNew > LevelMax ) + return -1; + pNode->pFunc = pAnd; + pNode->Level = LevelNew; + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Transforms the decomposition graph into the AIG.] + + Description [AIG nodes for the fanins should be assigned to pNode->pFunc + of the leaves of the graph before calling this procedure.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_GraphToNetwork( Ivy_Man_t * p, Dec_Graph_t * pGraph ) +{ + Ivy_Obj_t * pAnd0, * pAnd1; + Dec_Node_t * pNode; + int i; + // check for constant function + if ( Dec_GraphIsConst(pGraph) ) + return Ivy_NotCond( Ivy_ManConst1(p), Dec_GraphIsComplement(pGraph) ); + // check for a literal + if ( Dec_GraphIsVar(pGraph) ) + return Ivy_NotCond( Dec_GraphVar(pGraph)->pFunc, Dec_GraphIsComplement(pGraph) ); + // build the AIG nodes corresponding to the AND gates of the graph + Dec_GraphForEachNode( pGraph, pNode, i ) + { + pAnd0 = Ivy_NotCond( Dec_GraphNode(pGraph, pNode->eEdge0.Node)->pFunc, pNode->eEdge0.fCompl ); + pAnd1 = Ivy_NotCond( Dec_GraphNode(pGraph, pNode->eEdge1.Node)->pFunc, pNode->eEdge1.fCompl ); + pNode->pFunc = Ivy_And( p, pAnd0, pAnd1 ); + } + // complement the result if necessary + return Ivy_NotCond( pNode->pFunc, Dec_GraphIsComplement(pGraph) ); +} + +/**Function************************************************************* + + Synopsis [Replaces MFFC of the node by the new factored form.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_GraphUpdateNetwork( Ivy_Man_t * p, Ivy_Obj_t * pRoot, Dec_Graph_t * pGraph, int fUpdateLevel, int nGain ) +{ + Ivy_Obj_t * pRootNew; + int nNodesNew, nNodesOld, Required; + Required = fUpdateLevel? Vec_IntEntry( p->vRequired, pRoot->Id ) : 1000000; + nNodesOld = Ivy_ManNodeNum(p); + // create the new structure of nodes + pRootNew = Ivy_GraphToNetwork( p, pGraph ); + assert( (int)Ivy_Regular(pRootNew)->Level <= Required ); +// if ( Ivy_Regular(pRootNew)->Level == Required ) +// printf( "Difference %d.\n", Ivy_Regular(pRootNew)->Level - Required ); + // remove the old nodes +// Ivy_AigReplace( pMan->pManFunc, pRoot, pRootNew, fUpdateLevel ); +/* + if ( Ivy_IsComplement(pRootNew) ) + printf( "c" ); + else + printf( "d" ); + if ( Ivy_ObjRefs(Ivy_Regular(pRootNew)) > 0 ) + printf( "%d", Ivy_ObjRefs(Ivy_Regular(pRootNew)) ); + printf( " " ); +*/ + Ivy_ObjReplace( p, pRoot, pRootNew, 1, 0, 1 ); + // compare the gains + nNodesNew = Ivy_ManNodeNum(p); + assert( nGain <= nNodesOld - nNodesNew ); + // propagate the buffer + Ivy_ManPropagateBuffers( p, 1 ); +} + +/**Function************************************************************* + + Synopsis [Replaces MFFC of the node by the new factored form.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_GraphUpdateNetwork3( Ivy_Man_t * p, Ivy_Obj_t * pRoot, Dec_Graph_t * pGraph, int fUpdateLevel, int nGain ) +{ + Ivy_Obj_t * pRootNew, * pFanin; + int nNodesNew, nNodesOld, i, nRefsOld; + nNodesOld = Ivy_ManNodeNum(p); + +//printf( "Before = %d. ", Ivy_ManNodeNum(p) ); + // mark the cut + Vec_PtrForEachEntry( ((Rwt_Man_t *)p->pData)->vFanins, pFanin, i ) + Ivy_ObjRefsInc( Ivy_Regular(pFanin) ); + // deref the old cone + nRefsOld = pRoot->nRefs; + pRoot->nRefs = 0; + Ivy_ObjDelete_rec( p, pRoot, 0 ); + pRoot->nRefs = nRefsOld; + // unmark the cut + Vec_PtrForEachEntry( ((Rwt_Man_t *)p->pData)->vFanins, pFanin, i ) + Ivy_ObjRefsDec( Ivy_Regular(pFanin) ); +//printf( "Deref = %d. ", Ivy_ManNodeNum(p) ); + + // create the new structure of nodes + pRootNew = Ivy_GraphToNetwork( p, pGraph ); +//printf( "Create = %d. ", Ivy_ManNodeNum(p) ); + // remove the old nodes +// Ivy_AigReplace( pMan->pManFunc, pRoot, pRootNew, fUpdateLevel ); +/* + if ( Ivy_IsComplement(pRootNew) ) + printf( "c" ); + else + printf( "d" ); + if ( Ivy_ObjRefs(Ivy_Regular(pRootNew)) > 0 ) + printf( "%d", Ivy_ObjRefs(Ivy_Regular(pRootNew)) ); + printf( " " ); +*/ + Ivy_ObjReplace( p, pRoot, pRootNew, 0, 0, 1 ); +//printf( "Replace = %d. ", Ivy_ManNodeNum(p) ); + + // delete remaining dangling nodes + Vec_PtrForEachEntry( ((Rwt_Man_t *)p->pData)->vFanins, pFanin, i ) + { + pFanin = Ivy_Regular(pFanin); + if ( !Ivy_ObjIsNone(pFanin) && Ivy_ObjRefs(pFanin) == 0 ) + Ivy_ObjDelete_rec( p, pFanin, 1 ); + } +//printf( "Deref = %d. ", Ivy_ManNodeNum(p) ); +//printf( "\n" ); + + // compare the gains + nNodesNew = Ivy_ManNodeNum(p); + assert( nGain <= nNodesOld - nNodesNew ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyRwrAlg.c b/abc_with_bb_support/src/aig/ivy/ivyRwrAlg.c new file mode 100644 index 000000000..f2a82dd09 --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyRwrAlg.c @@ -0,0 +1,408 @@ +/**CFile**************************************************************** + + FileName [ivyRwrAlg.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [Algebraic AIG rewriting.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyRwrAlg.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Ivy_ManFindAlgCut( Ivy_Obj_t * pRoot, Vec_Ptr_t * vFront, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vCone ); +static Ivy_Obj_t * Ivy_NodeRewriteAlg( Ivy_Obj_t * pObj, Vec_Ptr_t * vFront, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vCone, Vec_Ptr_t * vSols, int LevelR, int fUseZeroCost ); +static int Ivy_NodeCountMffc( Ivy_Obj_t * pNode ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Algebraic AIG rewriting.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManRewriteAlg( Ivy_Man_t * p, int fUpdateLevel, int fUseZeroCost ) +{ + Vec_Int_t * vRequired; + Vec_Ptr_t * vFront, * vLeaves, * vCone, * vSol; + Ivy_Obj_t * pObj, * pResult; + int i, RetValue, LevelR, nNodesOld; + int CountUsed, CountUndo; + vRequired = fUpdateLevel? Ivy_ManRequiredLevels( p ) : NULL; + vFront = Vec_PtrAlloc( 100 ); + vLeaves = Vec_PtrAlloc( 100 ); + vCone = Vec_PtrAlloc( 100 ); + vSol = Vec_PtrAlloc( 100 ); + // go through the nodes in the topological order + CountUsed = CountUndo = 0; + nNodesOld = Ivy_ManObjIdNext(p); + Ivy_ManForEachObj( p, pObj, i ) + { + assert( !Ivy_ObjIsBuf(pObj) ); + if ( i >= nNodesOld ) + break; + // skip no-nodes and MUX roots + if ( !Ivy_ObjIsNode(pObj) || Ivy_ObjIsExor(pObj) || Ivy_ObjIsMuxType(pObj) ) + continue; +// if ( pObj->Id > 297 ) // 296 --- 297 +// break; + if ( pObj->Id == 297 ) + { + int x = 0; + } + // get the largest algebraic cut + RetValue = Ivy_ManFindAlgCut( pObj, vFront, vLeaves, vCone ); + // the case of a trivial tree cut + if ( RetValue == 1 ) + continue; + // the case of constant 0 cone + if ( RetValue == -1 ) + { + Ivy_ObjReplace( pObj, Ivy_ManConst0(p), 1, 0, 1 ); + continue; + } + assert( Vec_PtrSize(vLeaves) > 2 ); + // get the required level for this node + LevelR = vRequired? Vec_IntEntry(vRequired, pObj->Id) : 1000000; + // create a new cone + pResult = Ivy_NodeRewriteAlg( pObj, vFront, vLeaves, vCone, vSol, LevelR, fUseZeroCost ); + if ( pResult == NULL || pResult == pObj ) + continue; + assert( Vec_PtrSize(vSol) == 1 || !Ivy_IsComplement(pResult) ); + if ( Ivy_ObjLevel(Ivy_Regular(pResult)) > LevelR && Ivy_ObjRefs(Ivy_Regular(pResult)) == 0 ) + Ivy_ObjDelete_rec(Ivy_Regular(pResult), 1), CountUndo++; + else + Ivy_ObjReplace( pObj, pResult, 1, 0, 1 ), CountUsed++; + } + printf( "Used = %d. Undo = %d.\n", CountUsed, CountUndo ); + Vec_PtrFree( vFront ); + Vec_PtrFree( vCone ); + Vec_PtrFree( vSol ); + if ( vRequired ) Vec_IntFree( vRequired ); + if ( i = Ivy_ManCleanup(p) ) + printf( "Cleanup after rewriting removed %d dangling nodes.\n", i ); + if ( !Ivy_ManCheck(p) ) + printf( "Ivy_ManRewriteAlg(): The check has failed.\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Analizes one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_NodeRewriteAlg( Ivy_Obj_t * pObj, Vec_Ptr_t * vFront, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vCone, Vec_Ptr_t * vSols, int LevelR, int fUseZeroCost ) +{ + int fVerbose = 0; + Ivy_Obj_t * pTemp; + int k, Counter, nMffc, RetValue; + + if ( fVerbose ) + { + if ( Ivy_ObjIsExor(pObj) ) + printf( "x " ); + else + printf( " " ); + } + +/* + printf( "%d ", Vec_PtrSize(vFront) ); + printf( "( " ); + Vec_PtrForEachEntry( vFront, pTemp, k ) + printf( "%d ", Ivy_ObjRefs(Ivy_Regular(pTemp)) ); + printf( ")\n" ); +*/ + // collect nodes in the cone + if ( Ivy_ObjIsExor(pObj) ) + Ivy_ManCollectCone( pObj, vFront, vCone ); + else + Ivy_ManCollectCone( pObj, vLeaves, vCone ); + + // deref nodes in the cone + Vec_PtrForEachEntry( vCone, pTemp, k ) + { + Ivy_ObjRefsDec( Ivy_ObjFanin0(pTemp) ); + Ivy_ObjRefsDec( Ivy_ObjFanin1(pTemp) ); + pTemp->fMarkB = 1; + } + + // count the MFFC size + Vec_PtrForEachEntry( vFront, pTemp, k ) + Ivy_Regular(pTemp)->fMarkA = 1; + nMffc = Ivy_NodeCountMffc( pObj ); + Vec_PtrForEachEntry( vFront, pTemp, k ) + Ivy_Regular(pTemp)->fMarkA = 0; + + if ( fVerbose ) + { + Counter = 0; + Vec_PtrForEachEntry( vCone, pTemp, k ) + Counter += (Ivy_ObjRefs(pTemp) > 0); + printf( "%5d : Leaves = %2d. Cone = %2d. ConeRef = %2d. Mffc = %d. Lev = %d. LevR = %d.\n", + pObj->Id, Vec_PtrSize(vFront), Vec_PtrSize(vCone), Counter-1, nMffc, Ivy_ObjLevel(pObj), LevelR ); + } +/* + printf( "Leaves:" ); + Vec_PtrForEachEntry( vLeaves, pTemp, k ) + printf( " %d%s", Ivy_Regular(pTemp)->Id, Ivy_IsComplement(pTemp)? "\'" : "" ); + printf( "\n" ); + printf( "Cone:\n" ); + Vec_PtrForEachEntry( vCone, pTemp, k ) + printf( " %5d = %d%s %d%s\n", pTemp->Id, + Ivy_ObjFaninId0(pTemp), Ivy_ObjFaninC0(pTemp)? "\'" : "", + Ivy_ObjFaninId1(pTemp), Ivy_ObjFaninC1(pTemp)? "\'" : "" ); +*/ + + RetValue = Ivy_MultiPlus( vLeaves, vCone, Ivy_ObjType(pObj), nMffc + fUseZeroCost, vSols ); + + // ref nodes in the cone + Vec_PtrForEachEntry( vCone, pTemp, k ) + { + Ivy_ObjRefsInc( Ivy_ObjFanin0(pTemp) ); + Ivy_ObjRefsInc( Ivy_ObjFanin1(pTemp) ); + pTemp->fMarkA = 0; + pTemp->fMarkB = 0; + } + + if ( !RetValue ) + return NULL; + + if ( Vec_PtrSize( vSols ) == 1 ) + return Vec_PtrEntry( vSols, 0 ); + return Ivy_NodeBalanceBuildSuper( vSols, Ivy_ObjType(pObj), 1 ); +} + +/**Function************************************************************* + + Synopsis [Comparison for node pointers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_NodeCountMffc_rec( Ivy_Obj_t * pNode ) +{ + if ( Ivy_ObjRefs(pNode) > 0 || Ivy_ObjIsCi(pNode) || pNode->fMarkA ) + return 0; + assert( pNode->fMarkB ); + pNode->fMarkA = 1; +// printf( "%d ", pNode->Id ); + if ( Ivy_ObjIsBuf(pNode) ) + return Ivy_NodeCountMffc_rec( Ivy_ObjFanin0(pNode) ); + return 1 + Ivy_NodeCountMffc_rec( Ivy_ObjFanin0(pNode) ) + Ivy_NodeCountMffc_rec( Ivy_ObjFanin1(pNode) ); +} + +/**Function************************************************************* + + Synopsis [Comparison for node pointers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_NodeCountMffc( Ivy_Obj_t * pNode ) +{ + assert( pNode->fMarkB ); + return 1 + Ivy_NodeCountMffc_rec( Ivy_ObjFanin0(pNode) ) + Ivy_NodeCountMffc_rec( Ivy_ObjFanin1(pNode) ); +} + +/**Function************************************************************* + + Synopsis [Comparison for node pointers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManFindAlgCutCompare( Ivy_Obj_t ** pp1, Ivy_Obj_t ** pp2 ) +{ + if ( *pp1 < *pp2 ) + return -1; + if ( *pp1 > *pp2 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Computing one algebraic cut.] + + Description [Returns 1 if the tree-leaves of this node where traversed + and found to have no external references (and have not been collected). + Returns 0 if the tree-leaves have external references and are collected.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManFindAlgCut_rec( Ivy_Obj_t * pObj, Ivy_Type_t Type, Vec_Ptr_t * vFront, Vec_Ptr_t * vCone ) +{ + int RetValue0, RetValue1; + Ivy_Obj_t * pObjR = Ivy_Regular(pObj); + assert( !Ivy_ObjIsBuf(pObjR) ); + assert( Type != IVY_EXOR || !Ivy_IsComplement(pObj) ); + + // make sure the node is not visited twice in different polarities + if ( Ivy_IsComplement(pObj) ) + { // if complemented, mark B + if ( pObjR->fMarkA ) + return -1; + pObjR->fMarkB = 1; + } + else + { // if non-complicated, mark A + if ( pObjR->fMarkB ) + return -1; + pObjR->fMarkA = 1; + } + Vec_PtrPush( vCone, pObjR ); + + // if the node is the end of the tree, return + if ( Ivy_IsComplement(pObj) || Ivy_ObjType(pObj) != Type ) + { + if ( Ivy_ObjRefs(pObjR) == 1 ) + return 1; + assert( Ivy_ObjRefs(pObjR) > 1 ); + Vec_PtrPush( vFront, pObj ); + return 0; + } + + // branch on the node + assert( !Ivy_IsComplement(pObj) ); + assert( Ivy_ObjIsNode(pObj) ); + // what if buffer has more than one fanout??? + RetValue0 = Ivy_ManFindAlgCut_rec( Ivy_ObjReal( Ivy_ObjChild0(pObj) ), Type, vFront, vCone ); + RetValue1 = Ivy_ManFindAlgCut_rec( Ivy_ObjReal( Ivy_ObjChild1(pObj) ), Type, vFront, vCone ); + if ( RetValue0 == -1 || RetValue1 == -1 ) + return -1; + + // the case when both have no external references + if ( RetValue0 && RetValue1 ) + { + if ( Ivy_ObjRefs(pObj) == 1 ) + return 1; + assert( Ivy_ObjRefs(pObj) > 1 ); + Vec_PtrPush( vFront, pObj ); + return 0; + } + // the case when one of them has external references + if ( RetValue0 ) + Vec_PtrPush( vFront, Ivy_ObjReal( Ivy_ObjChild0(pObj) ) ); + if ( RetValue1 ) + Vec_PtrPush( vFront, Ivy_ObjReal( Ivy_ObjChild1(pObj) ) ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Computing one algebraic cut.] + + Description [Algebraic cut stops when we hit (a) CI, (b) complemented edge, + (c) boundary of different gates. Returns 1 if this is a pure tree. + Returns -1 if the contant 0 is detected. Return 0 if the array can be used.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManFindAlgCut( Ivy_Obj_t * pRoot, Vec_Ptr_t * vFront, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vCone ) +{ + Ivy_Obj_t * pObj, * pPrev; + int RetValue, i; + assert( !Ivy_IsComplement(pRoot) ); + assert( Ivy_ObjIsNode(pRoot) ); + // clear the frontier and collect the nodes + Vec_PtrClear( vCone ); + Vec_PtrClear( vFront ); + Vec_PtrClear( vLeaves ); + RetValue = Ivy_ManFindAlgCut_rec( pRoot, Ivy_ObjType(pRoot), vFront, vCone ); + // clean the marks + Vec_PtrForEachEntry( vCone, pObj, i ) + pObj->fMarkA = pObj->fMarkB = 0; + // quit if the same node is found in both polarities + if ( RetValue == -1 ) + return -1; + // return if the node is the root of a tree + if ( RetValue == 1 ) + return 1; + // return if the cut is composed of two nodes + if ( Vec_PtrSize(vFront) <= 2 ) + return 1; + // sort the entries in increasing order + Vec_PtrSort( vFront, Ivy_ManFindAlgCutCompare ); + // remove duplicates from vFront and save the nodes in vLeaves + pPrev = Vec_PtrEntry(vFront, 0); + Vec_PtrPush( vLeaves, pPrev ); + Vec_PtrForEachEntryStart( vFront, pObj, i, 1 ) + { + // compare current entry and the previous entry + if ( pObj == pPrev ) + { + if ( Ivy_ObjIsExor(pRoot) ) // A <+> A = 0 + { + // vLeaves are no longer structural support of pRoot!!! + Vec_PtrPop(vLeaves); + pPrev = Vec_PtrSize(vLeaves) == 0 ? NULL : Vec_PtrEntryLast(vLeaves); + } + continue; + } + if ( pObj == Ivy_Not(pPrev) ) + { + assert( Ivy_ObjIsAnd(pRoot) ); + return -1; + } + pPrev = pObj; + Vec_PtrPush( vLeaves, pObj ); + } + if ( Vec_PtrSize(vLeaves) == 0 ) + return -1; + if ( Vec_PtrSize(vLeaves) <= 2 ) + return 1; + return 0; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivySeq.c b/abc_with_bb_support/src/aig/ivy/ivySeq.c new file mode 100644 index 000000000..92bafd172 --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivySeq.c @@ -0,0 +1,1134 @@ +/**CFile**************************************************************** + + FileName [ivySeq.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivySeq.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" +#include "deco.h" +#include "rwt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Ivy_NodeRewriteSeq( Ivy_Man_t * pMan, Rwt_Man_t * p, Ivy_Obj_t * pNode, int fUseZeroCost ); +static void Ivy_GraphPrepare( Dec_Graph_t * pGraph, Ivy_Cut_t * pCut, Vec_Ptr_t * vFanins, char * pPerm ); +static unsigned Ivy_CutGetTruth( Ivy_Man_t * p, Ivy_Obj_t * pObj, int * pNums, int nNums ); +static Dec_Graph_t * Rwt_CutEvaluateSeq( Ivy_Man_t * pMan, Rwt_Man_t * p, Ivy_Obj_t * pRoot, Ivy_Cut_t * pCut, char * pPerm, Vec_Ptr_t * vFaninsCur, int nNodesSaved, int * pGainBest, unsigned uTruth ); +static int Ivy_GraphToNetworkSeqCountSeq( Ivy_Man_t * p, Ivy_Obj_t * pRoot, Dec_Graph_t * pGraph, int NodeMax ); +static Ivy_Obj_t * Ivy_GraphToNetworkSeq( Ivy_Man_t * p, Dec_Graph_t * pGraph ); +static void Ivy_GraphUpdateNetworkSeq( Ivy_Man_t * p, Ivy_Obj_t * pRoot, Dec_Graph_t * pGraph, int nGain ); +static Ivy_Store_t * Ivy_CutComputeForNode( Ivy_Man_t * p, Ivy_Obj_t * pObj, int nLeaves ); + +static inline int Ivy_CutHashValue( int NodeId ) { return 1 << (NodeId % 31); } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//int nMoves; +//int nMovesS; +//int nClauses; +//int timeInv; + +/**Function************************************************************* + + Synopsis [Performs incremental rewriting of the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManRewriteSeq( Ivy_Man_t * p, int fUseZeroCost, int fVerbose ) +{ + Rwt_Man_t * pManRwt; + Ivy_Obj_t * pNode; + int i, nNodes, nGain; + int clk, clkStart = clock(); + + // set the DC latch values + Ivy_ManForEachLatch( p, pNode, i ) + pNode->Init = IVY_INIT_DC; + // start the rewriting manager + pManRwt = Rwt_ManStart( 0 ); + p->pData = pManRwt; + if ( pManRwt == NULL ) + return 0; + // create fanouts + if ( p->fFanout == 0 ) + Ivy_ManStartFanout( p ); + // resynthesize each node once + nNodes = Ivy_ManObjIdMax(p); + Ivy_ManForEachNode( p, pNode, i ) + { + assert( !Ivy_ObjIsBuf(pNode) ); + assert( !Ivy_ObjIsBuf(Ivy_ObjFanin0(pNode)) ); + assert( !Ivy_ObjIsBuf(Ivy_ObjFanin1(pNode)) ); + // fix the fanin buffer problem +// Ivy_NodeFixBufferFanins( p, pNode ); +// if ( Ivy_ObjIsBuf(pNode) ) +// continue; + // stop if all nodes have been tried once + if ( i > nNodes ) + break; + // for each cut, try to resynthesize it + nGain = Ivy_NodeRewriteSeq( p, pManRwt, pNode, fUseZeroCost ); + if ( nGain > 0 || nGain == 0 && fUseZeroCost ) + { + Dec_Graph_t * pGraph = Rwt_ManReadDecs(pManRwt); + int fCompl = Rwt_ManReadCompl(pManRwt); + // complement the FF if needed +clk = clock(); + if ( fCompl ) Dec_GraphComplement( pGraph ); + Ivy_GraphUpdateNetworkSeq( p, pNode, pGraph, nGain ); + if ( fCompl ) Dec_GraphComplement( pGraph ); +Rwt_ManAddTimeUpdate( pManRwt, clock() - clk ); + } + } +Rwt_ManAddTimeTotal( pManRwt, clock() - clkStart ); + // print stats + if ( fVerbose ) + Rwt_ManPrintStats( pManRwt ); + // delete the managers + Rwt_ManStop( pManRwt ); + p->pData = NULL; + // fix the levels + Ivy_ManResetLevels( p ); +// if ( Ivy_ManCheckFanoutNums(p) ) +// printf( "Ivy_ManRewritePre(): The check has failed.\n" ); + // check + if ( !Ivy_ManCheck(p) ) + printf( "Ivy_ManRewritePre(): The check has failed.\n" ); + return 1; +} + + +/**Function************************************************************* + + Synopsis [Performs rewriting for one node.] + + Description [This procedure considers all the cuts computed for the node + and tries to rewrite each of them using the "forest" of different AIG + structures precomputed and stored in the RWR manager. + Determines the best rewriting and computes the gain in the number of AIG + nodes in the final network. In the end, p->vFanins contains information + about the best cut that can be used for rewriting, while p->pGraph gives + the decomposition dag (represented using decomposition graph data structure). + Returns gain in the number of nodes or -1 if node cannot be rewritten.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_NodeRewriteSeq( Ivy_Man_t * pMan, Rwt_Man_t * p, Ivy_Obj_t * pNode, int fUseZeroCost ) +{ + int fVeryVerbose = 0; + Dec_Graph_t * pGraph; + Ivy_Store_t * pStore; + Ivy_Cut_t * pCut; + Ivy_Obj_t * pFanin;//, * pFanout; + Vec_Ptr_t * vFanout; + unsigned uPhase, uTruthBest, uTruth;//, nNewClauses; + char * pPerm; + int nNodesSaved, nNodesSaveCur; + int i, c, GainCur, GainBest = -1; + int clk, clk2;//, clk3; + + p->nNodesConsidered++; + // get the node's cuts +clk = clock(); + pStore = Ivy_CutComputeForNode( pMan, pNode, 5 ); +p->timeCut += clock() - clk; + + // go through the cuts +clk = clock(); + vFanout = Vec_PtrAlloc( 100 ); + for ( c = 1; c < pStore->nCuts; c++ ) + { + pCut = pStore->pCuts + c; + // consider only 4-input cuts + if ( pCut->nSize != 4 ) + continue; + // skip the cuts with buffers + for ( i = 0; i < (int)pCut->nSize; i++ ) + if ( Ivy_ObjIsBuf( Ivy_ManObj(pMan, Ivy_LeafId(pCut->pArray[i])) ) ) + break; + if ( i != pCut->nSize ) + { + p->nCutsBad++; + continue; + } + p->nCutsGood++; + // get the fanin permutation +clk2 = clock(); + uTruth = 0xFFFF & Ivy_CutGetTruth( pMan, pNode, pCut->pArray, pCut->nSize ); // truth table +p->timeTruth += clock() - clk2; + pPerm = p->pPerms4[ p->pPerms[uTruth] ]; + uPhase = p->pPhases[uTruth]; + // collect fanins with the corresponding permutation/phase + Vec_PtrClear( p->vFaninsCur ); + Vec_PtrFill( p->vFaninsCur, (int)pCut->nSize, 0 ); + for ( i = 0; i < (int)pCut->nSize; i++ ) + { + pFanin = Ivy_ManObj( pMan, Ivy_LeafId( pCut->pArray[pPerm[i]] ) ); + assert( Ivy_ObjIsNode(pFanin) || Ivy_ObjIsCi(pFanin) || Ivy_ObjIsConst1(pFanin) ); + pFanin = Ivy_NotCond(pFanin, ((uPhase & (1< 0) ); + Vec_PtrWriteEntry( p->vFaninsCur, i, pFanin ); + } +clk2 = clock(); + // mark the fanin boundary + Vec_PtrForEachEntry( p->vFaninsCur, pFanin, i ) + Ivy_ObjRefsInc( Ivy_Regular(pFanin) ); + // label MFFC with current ID + Ivy_ManIncrementTravId( pMan ); + nNodesSaved = Ivy_ObjMffcLabel( pMan, pNode ); + // label fanouts with the current ID +// Ivy_ObjForEachFanout( pMan, pNode, vFanout, pFanout, i ) +// Ivy_ObjSetTravIdCurrent( pMan, pFanout ); + // unmark the fanin boundary + Vec_PtrForEachEntry( p->vFaninsCur, pFanin, i ) + Ivy_ObjRefsDec( Ivy_Regular(pFanin) ); +p->timeMffc += clock() - clk2; + + // evaluate the cut +clk2 = clock(); + pGraph = Rwt_CutEvaluateSeq( pMan, p, pNode, pCut, pPerm, p->vFaninsCur, nNodesSaved, &GainCur, uTruth ); +p->timeEval += clock() - clk2; + + + // check if the cut is better than the current best one + if ( pGraph != NULL && GainBest < GainCur ) + { + // save this form + nNodesSaveCur = nNodesSaved; + GainBest = GainCur; + p->pGraph = pGraph; + p->pCut = pCut; + p->pPerm = pPerm; + p->fCompl = ((uPhase & (1<<4)) > 0); + uTruthBest = uTruth; + // collect fanins in the + Vec_PtrClear( p->vFanins ); + Vec_PtrForEachEntry( p->vFaninsCur, pFanin, i ) + Vec_PtrPush( p->vFanins, pFanin ); + } + } + Vec_PtrFree( vFanout ); +p->timeRes += clock() - clk; + + if ( GainBest == -1 ) + return -1; +/* + { + Ivy_Cut_t * pCut = p->pCut; + printf( "Node %5d. Using cut : {", Ivy_ObjId(pNode) ); + for ( i = 0; i < pCut->nSize; i++ ) + printf( " %d(%d)", Ivy_LeafId(pCut->pArray[i]), Ivy_LeafLat(pCut->pArray[i]) ); + printf( " }\n" ); + } +*/ + +//clk3 = clock(); +//nNewClauses = Ivy_CutTruthPrint( pMan, p->pCut, uTruth ); +//timeInv += clock() - clk; + +// nClauses += nNewClauses; +// nMoves++; +// if ( nNewClauses > 0 ) +// nMovesS++; + + // copy the leaves + Ivy_GraphPrepare( p->pGraph, p->pCut, p->vFanins, p->pPerm ); + + p->nScores[p->pMap[uTruthBest]]++; + p->nNodesGained += GainBest; + if ( fUseZeroCost || GainBest > 0 ) + p->nNodesRewritten++; + +/* + if ( GainBest > 0 ) + { + Ivy_Cut_t * pCut = p->pCut; + printf( "Node %5d. Using cut : {", Ivy_ObjId(pNode) ); + for ( i = 0; i < pCut->nSize; i++ ) + printf( " %5d(%2d)", Ivy_LeafId(pCut->pArray[i]), Ivy_LeafLat(pCut->pArray[i]) ); + printf( " }\n" ); + } +*/ + + // report the progress + if ( fVeryVerbose && GainBest > 0 ) + { + printf( "Node %6d : ", Ivy_ObjId(pNode) ); + printf( "Fanins = %d. ", p->vFanins->nSize ); + printf( "Save = %d. ", nNodesSaveCur ); + printf( "Add = %d. ", nNodesSaveCur-GainBest ); + printf( "GAIN = %d. ", GainBest ); + printf( "Cone = %d. ", p->pGraph? Dec_GraphNodeNum(p->pGraph) : 0 ); + printf( "Class = %d. ", p->pMap[uTruthBest] ); + printf( "\n" ); + } + return GainBest; +} + + +/**Function************************************************************* + + Synopsis [Evaluates the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Rwt_CutEvaluateSeq( Ivy_Man_t * pMan, Rwt_Man_t * p, Ivy_Obj_t * pRoot, Ivy_Cut_t * pCut, char * pPerm, Vec_Ptr_t * vFaninsCur, int nNodesSaved, int * pGainBest, unsigned uTruth ) +{ + Vec_Ptr_t * vSubgraphs; + Dec_Graph_t * pGraphBest, * pGraphCur; + Rwt_Node_t * pNode; + int nNodesAdded, GainBest, i; + // find the matching class of subgraphs + vSubgraphs = Vec_VecEntry( p->vClasses, p->pMap[uTruth] ); + p->nSubgraphs += vSubgraphs->nSize; + // determine the best subgraph + GainBest = -1; + Vec_PtrForEachEntry( vSubgraphs, pNode, i ) + { + // get the current graph + pGraphCur = (Dec_Graph_t *)pNode->pNext; + +// if ( pRoot->Id == 8648 ) +// Dec_GraphPrint( stdout, pGraphCur, NULL, NULL ); + // copy the leaves +// Vec_PtrForEachEntry( vFaninsCur, pFanin, k ) +// Dec_GraphNode(pGraphCur, k)->pFunc = pFanin; + Ivy_GraphPrepare( pGraphCur, pCut, vFaninsCur, pPerm ); + + // detect how many unlabeled nodes will be reused + nNodesAdded = Ivy_GraphToNetworkSeqCountSeq( pMan, pRoot, pGraphCur, nNodesSaved ); + if ( nNodesAdded == -1 ) + continue; + assert( nNodesSaved >= nNodesAdded ); + // count the gain at this node + if ( GainBest < nNodesSaved - nNodesAdded ) + { + GainBest = nNodesSaved - nNodesAdded; + pGraphBest = pGraphCur; + } + } + if ( GainBest == -1 ) + return NULL; + *pGainBest = GainBest; + return pGraphBest; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_GraphPrepare( Dec_Graph_t * pGraph, Ivy_Cut_t * pCut, Vec_Ptr_t * vFanins, char * pPerm ) +{ + Dec_Node_t * pNode, * pNode0, * pNode1; + int i; + assert( Dec_GraphLeaveNum(pGraph) == pCut->nSize ); + assert( Vec_PtrSize(vFanins) == pCut->nSize ); + // label the leaves with latch numbers + Dec_GraphForEachLeaf( pGraph, pNode, i ) + { + pNode->pFunc = Vec_PtrEntry( vFanins, i ); + pNode->nLat2 = Ivy_LeafLat( pCut->pArray[pPerm[i]] ); + } + // propagate latches through the nodes + Dec_GraphForEachNode( pGraph, pNode, i ) + { + // get the children of this node + pNode0 = Dec_GraphNode( pGraph, pNode->eEdge0.Node ); + pNode1 = Dec_GraphNode( pGraph, pNode->eEdge1.Node ); + // distribute the latches + pNode->nLat2 = IVY_MIN( pNode0->nLat2, pNode1->nLat2 ); + pNode->nLat0 = pNode0->nLat2 - pNode->nLat2; + pNode->nLat1 = pNode1->nLat2 - pNode->nLat2; + } +} + +/**Function************************************************************* + + Synopsis [Counts the number of new nodes added when using this graph.] + + Description [AIG nodes for the fanins should be assigned to pNode->pFunc + of the leaves of the graph before calling this procedure. + Returns -1 if the number of nodes and levels exceeded the given limit or + the number of levels exceeded the maximum allowed level.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_GraphToNetworkSeqCountSeq( Ivy_Man_t * p, Ivy_Obj_t * pRoot, Dec_Graph_t * pGraph, int NodeMax ) +{ + Dec_Node_t * pNode, * pNode0, * pNode1; + Ivy_Obj_t * pAnd, * pAnd0, * pAnd1; + int i, k, Counter, fCompl; + // check for constant function or a literal + if ( Dec_GraphIsConst(pGraph) || Dec_GraphIsVar(pGraph) ) + return 0; + // compute the AIG size after adding the internal nodes + Counter = 0; + Dec_GraphForEachNode( pGraph, pNode, i ) + { + // get the children of this node + pNode0 = Dec_GraphNode( pGraph, pNode->eEdge0.Node ); + pNode1 = Dec_GraphNode( pGraph, pNode->eEdge1.Node ); + // get the AIG nodes corresponding to the children + pAnd0 = pNode0->pFunc; + pAnd1 = pNode1->pFunc; + // skip the latches + for ( k = 0; pAnd0 && k < (int)pNode->nLat0; k++ ) + { + fCompl = Ivy_IsComplement(pAnd0); + pAnd0 = Ivy_TableLookup( p, Ivy_ObjCreateGhost(p, Ivy_Regular(pAnd0), NULL, IVY_LATCH, IVY_INIT_DC) ); + if ( pAnd0 ) + pAnd0 = Ivy_NotCond( pAnd0, fCompl ); + } + for ( k = 0; pAnd1 && k < (int)pNode->nLat1; k++ ) + { + fCompl = Ivy_IsComplement(pAnd1); + pAnd1 = Ivy_TableLookup( p, Ivy_ObjCreateGhost(p, Ivy_Regular(pAnd1), NULL, IVY_LATCH, IVY_INIT_DC) ); + if ( pAnd1 ) + pAnd1 = Ivy_NotCond( pAnd1, fCompl ); + } + // get the new node + if ( pAnd0 && pAnd1 ) + { + // if they are both present, find the resulting node + pAnd0 = Ivy_NotCond( pAnd0, pNode->eEdge0.fCompl ); + pAnd1 = Ivy_NotCond( pAnd1, pNode->eEdge1.fCompl ); + assert( !Ivy_ObjIsLatch(Ivy_Regular(pAnd0)) || !Ivy_ObjIsLatch(Ivy_Regular(pAnd1)) ); + if ( Ivy_Regular(pAnd0) == Ivy_Regular(pAnd1) || Ivy_ObjIsConst1(Ivy_Regular(pAnd0)) || Ivy_ObjIsConst1(Ivy_Regular(pAnd1)) ) + pAnd = Ivy_And( p, pAnd0, pAnd1 ); + else + pAnd = Ivy_TableLookup( p, Ivy_ObjCreateGhost(p, pAnd0, pAnd1, IVY_AND, IVY_INIT_NONE) ); + // return -1 if the node is the same as the original root + if ( Ivy_Regular(pAnd) == pRoot ) + return -1; + } + else + pAnd = NULL; + // count the number of added nodes + if ( pAnd == NULL || Ivy_ObjIsTravIdCurrent(p, Ivy_Regular(pAnd)) ) + { + if ( ++Counter > NodeMax ) + return -1; + } + pNode->pFunc = pAnd; + } + return Counter; +} + + +/**Function************************************************************* + + Synopsis [Transforms the decomposition graph into the AIG.] + + Description [AIG nodes for the fanins should be assigned to pNode->pFunc + of the leaves of the graph before calling this procedure.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_GraphToNetworkSeq( Ivy_Man_t * p, Dec_Graph_t * pGraph ) +{ + Ivy_Obj_t * pAnd0, * pAnd1; + Dec_Node_t * pNode; + int i, k; + // check for constant function + if ( Dec_GraphIsConst(pGraph) ) + return Ivy_NotCond( Ivy_ManConst1(p), Dec_GraphIsComplement(pGraph) ); + // check for a literal + if ( Dec_GraphIsVar(pGraph) ) + { + // get the variable node + pNode = Dec_GraphVar(pGraph); + // add the remaining latches + for ( k = 0; k < (int)pNode->nLat2; k++ ) + pNode->pFunc = Ivy_Latch( p, pNode->pFunc, IVY_INIT_DC ); + return Ivy_NotCond( pNode->pFunc, Dec_GraphIsComplement(pGraph) ); + } + // build the AIG nodes corresponding to the AND gates of the graph + Dec_GraphForEachNode( pGraph, pNode, i ) + { + pAnd0 = Ivy_NotCond( Dec_GraphNode(pGraph, pNode->eEdge0.Node)->pFunc, pNode->eEdge0.fCompl ); + pAnd1 = Ivy_NotCond( Dec_GraphNode(pGraph, pNode->eEdge1.Node)->pFunc, pNode->eEdge1.fCompl ); + // add the latches + for ( k = 0; k < (int)pNode->nLat0; k++ ) + pAnd0 = Ivy_Latch( p, pAnd0, IVY_INIT_DC ); + for ( k = 0; k < (int)pNode->nLat1; k++ ) + pAnd1 = Ivy_Latch( p, pAnd1, IVY_INIT_DC ); + // create the node + pNode->pFunc = Ivy_And( p, pAnd0, pAnd1 ); + } + // add the remaining latches + for ( k = 0; k < (int)pNode->nLat2; k++ ) + pNode->pFunc = Ivy_Latch( p, pNode->pFunc, IVY_INIT_DC ); + // complement the result if necessary + return Ivy_NotCond( pNode->pFunc, Dec_GraphIsComplement(pGraph) ); +} + +/**Function************************************************************* + + Synopsis [Replaces MFFC of the node by the new factored form.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_GraphUpdateNetworkSeq( Ivy_Man_t * p, Ivy_Obj_t * pRoot, Dec_Graph_t * pGraph, int nGain ) +{ + Ivy_Obj_t * pRootNew; + int nNodesNew, nNodesOld; + nNodesOld = Ivy_ManNodeNum(p); + // create the new structure of nodes + pRootNew = Ivy_GraphToNetworkSeq( p, pGraph ); + Ivy_ObjReplace( p, pRoot, pRootNew, 1, 0, 0 ); + // compare the gains + nNodesNew = Ivy_ManNodeNum(p); + assert( nGain <= nNodesOld - nNodesNew ); + // propagate the buffer + Ivy_ManPropagateBuffers( p, 0 ); +} + + + + + + + + + +/**Function************************************************************* + + Synopsis [Computes the truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Ivy_CutGetTruth_rec( Ivy_Man_t * p, int Leaf, int * pNums, int nNums ) +{ + static unsigned uMasks[5] = { 0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, 0xFF00FF00, 0xFFFF0000 }; + unsigned uTruth0, uTruth1; + Ivy_Obj_t * pObj; + int i; + for ( i = 0; i < nNums; i++ ) + if ( Leaf == pNums[i] ) + return uMasks[i]; + pObj = Ivy_ManObj( p, Ivy_LeafId(Leaf) ); + if ( Ivy_ObjIsLatch(pObj) ) + { + assert( !Ivy_ObjFaninC0(pObj) ); + Leaf = Ivy_LeafCreate( Ivy_ObjFaninId0(pObj), Ivy_LeafLat(Leaf) + 1 ); + return Ivy_CutGetTruth_rec( p, Leaf, pNums, nNums ); + } + assert( Ivy_ObjIsNode(pObj) || Ivy_ObjIsBuf(pObj) ); + Leaf = Ivy_LeafCreate( Ivy_ObjFaninId0(pObj), Ivy_LeafLat(Leaf) ); + uTruth0 = Ivy_CutGetTruth_rec( p, Leaf, pNums, nNums ); + if ( Ivy_ObjFaninC0(pObj) ) + uTruth0 = ~uTruth0; + if ( Ivy_ObjIsBuf(pObj) ) + return uTruth0; + Leaf = Ivy_LeafCreate( Ivy_ObjFaninId1(pObj), Ivy_LeafLat(Leaf) ); + uTruth1 = Ivy_CutGetTruth_rec( p, Leaf, pNums, nNums ); + if ( Ivy_ObjFaninC1(pObj) ) + uTruth1 = ~uTruth1; + return uTruth0 & uTruth1; +} + + +/**Function************************************************************* + + Synopsis [Computes the truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Ivy_CutGetTruth( Ivy_Man_t * p, Ivy_Obj_t * pObj, int * pNums, int nNums ) +{ + assert( Ivy_ObjIsNode(pObj) ); + assert( nNums < 6 ); + return Ivy_CutGetTruth_rec( p, Ivy_LeafCreate(pObj->Id, 0), pNums, nNums ); +} + + + + + +/**Function************************************************************* + + Synopsis [Returns 1 if the cut can be constructed; 0 otherwise.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Ivy_CutPrescreen( Ivy_Cut_t * pCut, int Id0, int Id1 ) +{ + int i; + if ( pCut->nSize < pCut->nSizeMax ) + return 1; + for ( i = 0; i < pCut->nSize; i++ ) + if ( pCut->pArray[i] == Id0 || pCut->pArray[i] == Id1 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Derives new cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Ivy_CutDeriveNew2( Ivy_Cut_t * pCut, Ivy_Cut_t * pCutNew, int IdOld, int IdNew0, int IdNew1 ) +{ + unsigned uHash = 0; + int i, k; + assert( pCut->nSize > 0 ); + assert( IdNew0 < IdNew1 ); + for ( i = k = 0; i < pCut->nSize; i++ ) + { + if ( pCut->pArray[i] == IdOld ) + continue; + if ( IdNew0 >= 0 ) + { + if ( IdNew0 <= pCut->pArray[i] ) + { + if ( IdNew0 < pCut->pArray[i] ) + { + if ( k == pCut->nSizeMax ) + return 0; + pCutNew->pArray[ k++ ] = IdNew0; + uHash |= Ivy_CutHashValue( IdNew0 ); + } + IdNew0 = -1; + } + } + if ( IdNew1 >= 0 ) + { + if ( IdNew1 <= pCut->pArray[i] ) + { + if ( IdNew1 < pCut->pArray[i] ) + { + if ( k == pCut->nSizeMax ) + return 0; + pCutNew->pArray[ k++ ] = IdNew1; + uHash |= Ivy_CutHashValue( IdNew1 ); + } + IdNew1 = -1; + } + } + if ( k == pCut->nSizeMax ) + return 0; + pCutNew->pArray[ k++ ] = pCut->pArray[i]; + uHash |= Ivy_CutHashValue( pCut->pArray[i] ); + } + if ( IdNew0 >= 0 ) + { + if ( k == pCut->nSizeMax ) + return 0; + pCutNew->pArray[ k++ ] = IdNew0; + uHash |= Ivy_CutHashValue( IdNew0 ); + } + if ( IdNew1 >= 0 ) + { + if ( k == pCut->nSizeMax ) + return 0; + pCutNew->pArray[ k++ ] = IdNew1; + uHash |= Ivy_CutHashValue( IdNew1 ); + } + pCutNew->nSize = k; + pCutNew->uHash = uHash; + assert( pCutNew->nSize <= pCut->nSizeMax ); + for ( i = 1; i < pCutNew->nSize; i++ ) + assert( pCutNew->pArray[i-1] < pCutNew->pArray[i] ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Derives new cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Ivy_CutDeriveNew( Ivy_Cut_t * pCut, Ivy_Cut_t * pCutNew, int IdOld, int IdNew0, int IdNew1 ) +{ + unsigned uHash = 0; + int i, k; + assert( pCut->nSize > 0 ); + assert( IdNew0 < IdNew1 ); + for ( i = k = 0; i < pCut->nSize; i++ ) + { + if ( pCut->pArray[i] == IdOld ) + continue; + if ( IdNew0 <= pCut->pArray[i] ) + { + if ( IdNew0 < pCut->pArray[i] ) + { + pCutNew->pArray[ k++ ] = IdNew0; + uHash |= Ivy_CutHashValue( IdNew0 ); + } + IdNew0 = 0x7FFFFFFF; + } + if ( IdNew1 <= pCut->pArray[i] ) + { + if ( IdNew1 < pCut->pArray[i] ) + { + pCutNew->pArray[ k++ ] = IdNew1; + uHash |= Ivy_CutHashValue( IdNew1 ); + } + IdNew1 = 0x7FFFFFFF; + } + pCutNew->pArray[ k++ ] = pCut->pArray[i]; + uHash |= Ivy_CutHashValue( pCut->pArray[i] ); + } + if ( IdNew0 < 0x7FFFFFFF ) + { + pCutNew->pArray[ k++ ] = IdNew0; + uHash |= Ivy_CutHashValue( IdNew0 ); + } + if ( IdNew1 < 0x7FFFFFFF ) + { + pCutNew->pArray[ k++ ] = IdNew1; + uHash |= Ivy_CutHashValue( IdNew1 ); + } + pCutNew->nSize = k; + pCutNew->uHash = uHash; + assert( pCutNew->nSize <= pCut->nSizeMax ); +// for ( i = 1; i < pCutNew->nSize; i++ ) +// assert( pCutNew->pArray[i-1] < pCutNew->pArray[i] ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Find the hash value of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Ivy_NodeCutHash( Ivy_Cut_t * pCut ) +{ + int i; + pCut->uHash = 0; + for ( i = 0; i < pCut->nSize; i++ ) + pCut->uHash |= (1 << (pCut->pArray[i] % 31)); + return pCut->uHash; +} + +/**Function************************************************************* + + Synopsis [Derives new cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Ivy_CutDeriveNew3( Ivy_Cut_t * pCut, Ivy_Cut_t * pCutNew, int IdOld, int IdNew0, int IdNew1 ) +{ + int i, k; + assert( pCut->nSize > 0 ); + assert( IdNew0 < IdNew1 ); + for ( i = k = 0; i < pCut->nSize; i++ ) + { + if ( pCut->pArray[i] == IdOld ) + continue; + if ( IdNew0 <= pCut->pArray[i] ) + { + if ( IdNew0 < pCut->pArray[i] ) + pCutNew->pArray[ k++ ] = IdNew0; + IdNew0 = 0x7FFFFFFF; + } + if ( IdNew1 <= pCut->pArray[i] ) + { + if ( IdNew1 < pCut->pArray[i] ) + pCutNew->pArray[ k++ ] = IdNew1; + IdNew1 = 0x7FFFFFFF; + } + pCutNew->pArray[ k++ ] = pCut->pArray[i]; + } + if ( IdNew0 < 0x7FFFFFFF ) + pCutNew->pArray[ k++ ] = IdNew0; + if ( IdNew1 < 0x7FFFFFFF ) + pCutNew->pArray[ k++ ] = IdNew1; + pCutNew->nSize = k; + assert( pCutNew->nSize <= pCut->nSizeMax ); + Ivy_NodeCutHash( pCutNew ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if pDom is contained in pCut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Ivy_CutCheckDominance( Ivy_Cut_t * pDom, Ivy_Cut_t * pCut ) +{ + int i, k; + for ( i = 0; i < pDom->nSize; i++ ) + { + assert( i==0 || pDom->pArray[i-1] < pDom->pArray[i] ); + for ( k = 0; k < pCut->nSize; k++ ) + if ( pDom->pArray[i] == pCut->pArray[k] ) + break; + if ( k == pCut->nSize ) // node i in pDom is not contained in pCut + return 0; + } + // every node in pDom is contained in pCut + return 1; +} + +/**Function************************************************************* + + Synopsis [Check if the cut exists.] + + Description [Returns 1 if the cut exists.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_CutFindOrAddFilter( Ivy_Store_t * pCutStore, Ivy_Cut_t * pCutNew ) +{ + Ivy_Cut_t * pCut; + int i, k; + assert( pCutNew->uHash ); + // try to find the cut + for ( i = 0; i < pCutStore->nCuts; i++ ) + { + pCut = pCutStore->pCuts + i; + if ( pCut->nSize == 0 ) + continue; + if ( pCut->nSize == pCutNew->nSize ) + { + if ( pCut->uHash == pCutNew->uHash ) + { + for ( k = 0; k < pCutNew->nSize; k++ ) + if ( pCut->pArray[k] != pCutNew->pArray[k] ) + break; + if ( k == pCutNew->nSize ) + return 1; + } + continue; + } + if ( pCut->nSize < pCutNew->nSize ) + { + // skip the non-contained cuts + if ( (pCut->uHash & pCutNew->uHash) != pCut->uHash ) + continue; + // check containment seriously + if ( Ivy_CutCheckDominance( pCut, pCutNew ) ) + return 1; + continue; + } + // check potential containment of other cut + + // skip the non-contained cuts + if ( (pCut->uHash & pCutNew->uHash) != pCutNew->uHash ) + continue; + // check containment seriously + if ( Ivy_CutCheckDominance( pCutNew, pCut ) ) + { + // remove the current cut + pCut->nSize = 0; + } + } + assert( pCutStore->nCuts < pCutStore->nCutsMax ); + // add the cut + pCut = pCutStore->pCuts + pCutStore->nCuts++; + *pCut = *pCutNew; + return 0; +} + +/**Function************************************************************* + + Synopsis [Compresses the cut representation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_CutCompactAll( Ivy_Store_t * pCutStore ) +{ + Ivy_Cut_t * pCut; + int i, k; + pCutStore->nCutsM = 0; + for ( i = k = 0; i < pCutStore->nCuts; i++ ) + { + pCut = pCutStore->pCuts + i; + if ( pCut->nSize == 0 ) + continue; + if ( pCut->nSize < pCut->nSizeMax ) + pCutStore->nCutsM++; + pCutStore->pCuts[k++] = *pCut; + } + pCutStore->nCuts = k; +} + +/**Function************************************************************* + + Synopsis [Print the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_CutPrintForNode( Ivy_Cut_t * pCut ) +{ + int i; + assert( pCut->nSize > 0 ); + printf( "%d : {", pCut->nSize ); + for ( i = 0; i < pCut->nSize; i++ ) + printf( " %d", pCut->pArray[i] ); + printf( " }\n" ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_CutPrintForNodes( Ivy_Store_t * pCutStore ) +{ + int i; + printf( "Node %d\n", pCutStore->pCuts[0].pArray[0] ); + for ( i = 0; i < pCutStore->nCuts; i++ ) + Ivy_CutPrintForNode( pCutStore->pCuts + i ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Ivy_CutReadLeaf( Ivy_Obj_t * pFanin ) +{ + int nLats, iLeaf; + assert( !Ivy_IsComplement(pFanin) ); + if ( !Ivy_ObjIsLatch(pFanin) ) + return Ivy_LeafCreate( pFanin->Id, 0 ); + iLeaf = Ivy_CutReadLeaf(Ivy_ObjFanin0(pFanin)); + nLats = Ivy_LeafLat(iLeaf); + assert( nLats < IVY_LEAF_MASK ); + return 1 + iLeaf; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Store_t * Ivy_CutComputeForNode( Ivy_Man_t * p, Ivy_Obj_t * pObj, int nLeaves ) +{ + static Ivy_Store_t CutStore, * pCutStore = &CutStore; + Ivy_Cut_t CutNew, * pCutNew = &CutNew, * pCut; + Ivy_Man_t * pMan = p; + Ivy_Obj_t * pLeaf; + int i, k, Temp, nLats, iLeaf0, iLeaf1; + + assert( nLeaves <= IVY_CUT_INPUT ); + + // start the structure + pCutStore->nCuts = 0; + pCutStore->nCutsMax = IVY_CUT_LIMIT; + // start the trivial cut + pCutNew->uHash = 0; + pCutNew->nSize = 1; + pCutNew->nSizeMax = nLeaves; + pCutNew->pArray[0] = Ivy_LeafCreate( pObj->Id, 0 ); + pCutNew->uHash = Ivy_CutHashValue( pCutNew->pArray[0] ); + // add the trivial cut + pCutStore->pCuts[pCutStore->nCuts++] = *pCutNew; + assert( pCutStore->nCuts == 1 ); + + // explore the cuts + for ( i = 0; i < pCutStore->nCuts; i++ ) + { + // expand this cut + pCut = pCutStore->pCuts + i; + if ( pCut->nSize == 0 ) + continue; + for ( k = 0; k < pCut->nSize; k++ ) + { + pLeaf = Ivy_ManObj( p, Ivy_LeafId(pCut->pArray[k]) ); + if ( Ivy_ObjIsCi(pLeaf) || Ivy_ObjIsConst1(pLeaf) ) + continue; + assert( Ivy_ObjIsNode(pLeaf) ); + nLats = Ivy_LeafLat(pCut->pArray[k]); + + // get the fanins fanins + iLeaf0 = Ivy_CutReadLeaf( Ivy_ObjFanin0(pLeaf) ); + iLeaf1 = Ivy_CutReadLeaf( Ivy_ObjFanin1(pLeaf) ); + assert( nLats + Ivy_LeafLat(iLeaf0) < IVY_LEAF_MASK && nLats + Ivy_LeafLat(iLeaf1) < IVY_LEAF_MASK ); + iLeaf0 = nLats + iLeaf0; + iLeaf1 = nLats + iLeaf1; + if ( !Ivy_CutPrescreen( pCut, iLeaf0, iLeaf1 ) ) + continue; + // the given cut exist + if ( iLeaf0 > iLeaf1 ) + Temp = iLeaf0, iLeaf0 = iLeaf1, iLeaf1 = Temp; + // create the new cut + if ( !Ivy_CutDeriveNew( pCut, pCutNew, pCut->pArray[k], iLeaf0, iLeaf1 ) ) + continue; + // add the cut + Ivy_CutFindOrAddFilter( pCutStore, pCutNew ); + if ( pCutStore->nCuts == IVY_CUT_LIMIT ) + break; + } + if ( pCutStore->nCuts == IVY_CUT_LIMIT ) + break; + } + if ( pCutStore->nCuts == IVY_CUT_LIMIT ) + pCutStore->fSatur = 1; + else + pCutStore->fSatur = 0; +// printf( "%d ", pCutStore->nCuts ); + Ivy_CutCompactAll( pCutStore ); +// printf( "%d \n", pCutStore->nCuts ); +// Ivy_CutPrintForNodes( pCutStore ); + return pCutStore; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_CutComputeAll( Ivy_Man_t * p, int nInputs ) +{ + Ivy_Store_t * pStore; + Ivy_Obj_t * pObj; + int i, nCutsTotal, nCutsTotalM, nNodeTotal, nNodeOver; + int clk = clock(); + if ( nInputs > IVY_CUT_INPUT ) + { + printf( "Cannot compute cuts for more than %d inputs.\n", IVY_CUT_INPUT ); + return; + } + nNodeTotal = nNodeOver = 0; + nCutsTotal = nCutsTotalM = -Ivy_ManNodeNum(p); + Ivy_ManForEachObj( p, pObj, i ) + { + if ( !Ivy_ObjIsNode(pObj) ) + continue; + pStore = Ivy_CutComputeForNode( p, pObj, nInputs ); + nCutsTotal += pStore->nCuts; + nCutsTotalM += pStore->nCutsM; + nNodeOver += pStore->fSatur; + nNodeTotal++; + } + printf( "All = %6d. Minus = %6d. Triv = %6d. Node = %6d. Satur = %6d. ", + nCutsTotal, nCutsTotalM, Ivy_ManPiNum(p) + Ivy_ManNodeNum(p), nNodeTotal, nNodeOver ); + PRT( "Time", clock() - clk ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyShow.c b/abc_with_bb_support/src/aig/ivy/ivyShow.c new file mode 100644 index 000000000..ae5d605ab --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyShow.c @@ -0,0 +1,338 @@ +/**CFile**************************************************************** + + FileName [ivyShow.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [Visualization of HAIG.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyShow.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Ivy_WriteDotAig( Ivy_Man_t * pMan, char * pFileName, int fHaig, Vec_Ptr_t * vBold ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManShow( Ivy_Man_t * pMan, int fHaig, Vec_Ptr_t * vBold ) +{ + extern void Abc_ShowFile( char * FileNameDot ); + static Counter = 0; + char FileNameDot[200]; + FILE * pFile; + // create the file name +// Ivy_ShowGetFileName( pMan->pName, FileNameDot ); + sprintf( FileNameDot, "temp%02d.dot", Counter++ ); + // check that the file can be opened + if ( (pFile = fopen( FileNameDot, "w" )) == NULL ) + { + fprintf( stdout, "Cannot open the intermediate file \"%s\".\n", FileNameDot ); + return; + } + fclose( pFile ); + // generate the file + Ivy_WriteDotAig( pMan, FileNameDot, fHaig, vBold ); + // visualize the file + Abc_ShowFile( FileNameDot ); +} + +/**Function************************************************************* + + Synopsis [Writes the graph structure of AIG for DOT.] + + Description [Useful for graph visualization using tools such as GraphViz: + http://www.graphviz.org/] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_WriteDotAig( Ivy_Man_t * pMan, char * pFileName, int fHaig, Vec_Ptr_t * vBold ) +{ + FILE * pFile; + Ivy_Obj_t * pNode, * pTemp, * pPrev; + int LevelMax, Level, i; + + if ( Ivy_ManNodeNum(pMan) > 200 ) + { + fprintf( stdout, "Cannot visualize AIG with more than 200 nodes.\n" ); + return; + } + if ( (pFile = fopen( pFileName, "w" )) == NULL ) + { + fprintf( stdout, "Cannot open the intermediate file \"%s\".\n", pFileName ); + return; + } + + // mark the nodes + if ( vBold ) + Vec_PtrForEachEntry( vBold, pNode, i ) + pNode->fMarkB = 1; + + // compute levels + LevelMax = 1 + Ivy_ManSetLevels( pMan, fHaig ); + + // write the DOT header + fprintf( pFile, "# %s\n", "AIG structure generated by IVY package" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "digraph AIG {\n" ); + fprintf( pFile, "size = \"7.5,10\";\n" ); +// fprintf( pFile, "ranksep = 0.5;\n" ); +// fprintf( pFile, "nodesep = 0.5;\n" ); + fprintf( pFile, "center = true;\n" ); +// fprintf( pFile, "orientation = landscape;\n" ); +// fprintf( pFile, "edge [fontsize = 10];\n" ); +// fprintf( pFile, "edge [dir = none];\n" ); + fprintf( pFile, "edge [dir = back];\n" ); + fprintf( pFile, "\n" ); + + // labels on the left of the picture + fprintf( pFile, "{\n" ); + fprintf( pFile, " node [shape = plaintext];\n" ); + fprintf( pFile, " edge [style = invis];\n" ); + fprintf( pFile, " LevelTitle1 [label=\"\"];\n" ); + fprintf( pFile, " LevelTitle2 [label=\"\"];\n" ); + // generate node names with labels + for ( Level = LevelMax; Level >= 0; Level-- ) + { + // the visible node name + fprintf( pFile, " Level%d", Level ); + fprintf( pFile, " [label = " ); + // label name + fprintf( pFile, "\"" ); + fprintf( pFile, "\"" ); + fprintf( pFile, "];\n" ); + } + + // genetate the sequence of visible/invisible nodes to mark levels + fprintf( pFile, " LevelTitle1 -> LevelTitle2 ->" ); + for ( Level = LevelMax; Level >= 0; Level-- ) + { + // the visible node name + fprintf( pFile, " Level%d", Level ); + // the connector + if ( Level != 0 ) + fprintf( pFile, " ->" ); + else + fprintf( pFile, ";" ); + } + fprintf( pFile, "\n" ); + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + + // generate title box on top + fprintf( pFile, "{\n" ); + fprintf( pFile, " rank = same;\n" ); + fprintf( pFile, " LevelTitle1;\n" ); + fprintf( pFile, " title1 [shape=plaintext,\n" ); + fprintf( pFile, " fontsize=20,\n" ); + fprintf( pFile, " fontname = \"Times-Roman\",\n" ); + fprintf( pFile, " label=\"" ); + fprintf( pFile, "%s", "AIG structure visualized by ABC" ); + fprintf( pFile, "\\n" ); + fprintf( pFile, "Benchmark \\\"%s\\\". ", "aig" ); + fprintf( pFile, "Time was %s. ", Extra_TimeStamp() ); + fprintf( pFile, "\"\n" ); + fprintf( pFile, " ];\n" ); + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + + // generate statistics box + fprintf( pFile, "{\n" ); + fprintf( pFile, " rank = same;\n" ); + fprintf( pFile, " LevelTitle2;\n" ); + fprintf( pFile, " title2 [shape=plaintext,\n" ); + fprintf( pFile, " fontsize=18,\n" ); + fprintf( pFile, " fontname = \"Times-Roman\",\n" ); + fprintf( pFile, " label=\"" ); + fprintf( pFile, "The set contains %d logic nodes and spans %d levels.", Ivy_ManNodeNum(pMan), LevelMax ); + fprintf( pFile, "\\n" ); + fprintf( pFile, "\"\n" ); + fprintf( pFile, " ];\n" ); + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + + // generate the COs + fprintf( pFile, "{\n" ); + fprintf( pFile, " rank = same;\n" ); + // the labeling node of this level + fprintf( pFile, " Level%d;\n", LevelMax ); + // generate the CO nodes + Ivy_ManForEachCo( pMan, pNode, i ) + { + if ( fHaig || pNode->pEquiv == NULL ) + fprintf( pFile, " Node%d%s [label = \"%d%s\"", pNode->Id, + (Ivy_ObjIsLatch(pNode)? "_in":""), pNode->Id, (Ivy_ObjIsLatch(pNode)? "_in":"") ); + else + fprintf( pFile, " Node%d%s [label = \"%d%s(%d%s)\"", pNode->Id, + (Ivy_ObjIsLatch(pNode)? "_in":""), pNode->Id, (Ivy_ObjIsLatch(pNode)? "_in":""), + Ivy_Regular(pNode->pEquiv)->Id, Ivy_IsComplement(pNode->pEquiv)? "\'":"" ); + fprintf( pFile, ", shape = %s", (Ivy_ObjIsLatch(pNode)? "box":"invtriangle") ); + fprintf( pFile, ", color = coral, fillcolor = coral" ); + fprintf( pFile, "];\n" ); + } + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + + // generate nodes of each rank + for ( Level = LevelMax - 1; Level > 0; Level-- ) + { + fprintf( pFile, "{\n" ); + fprintf( pFile, " rank = same;\n" ); + // the labeling node of this level + fprintf( pFile, " Level%d;\n", Level ); + Ivy_ManForEachObj( pMan, pNode, i ) + { + if ( (int)pNode->Level != Level ) + continue; + if ( fHaig || pNode->pEquiv == NULL ) + fprintf( pFile, " Node%d [label = \"%d\"", pNode->Id, pNode->Id ); + else + fprintf( pFile, " Node%d [label = \"%d(%d%s)\"", pNode->Id, pNode->Id, + Ivy_Regular(pNode->pEquiv)->Id, Ivy_IsComplement(pNode->pEquiv)? "\'":"" ); + fprintf( pFile, ", shape = ellipse" ); + if ( vBold && pNode->fMarkB ) + fprintf( pFile, ", style = filled" ); + fprintf( pFile, "];\n" ); + } + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + } + + // generate the CI nodes + fprintf( pFile, "{\n" ); + fprintf( pFile, " rank = same;\n" ); + // the labeling node of this level + fprintf( pFile, " Level%d;\n", 0 ); + // generate constant node + if ( Ivy_ObjRefs(Ivy_ManConst1(pMan)) > 0 ) + { + pNode = Ivy_ManConst1(pMan); + // check if the costant node is present + fprintf( pFile, " Node%d [label = \"Const1\"", pNode->Id ); + fprintf( pFile, ", shape = ellipse" ); + fprintf( pFile, ", color = coral, fillcolor = coral" ); + fprintf( pFile, "];\n" ); + } + // generate the CI nodes + Ivy_ManForEachCi( pMan, pNode, i ) + { + if ( fHaig || pNode->pEquiv == NULL ) + fprintf( pFile, " Node%d%s [label = \"%d%s\"", pNode->Id, + (Ivy_ObjIsLatch(pNode)? "_out":""), pNode->Id, (Ivy_ObjIsLatch(pNode)? "_out":"") ); + else + fprintf( pFile, " Node%d%s [label = \"%d%s(%d%s)\"", pNode->Id, + (Ivy_ObjIsLatch(pNode)? "_out":""), pNode->Id, (Ivy_ObjIsLatch(pNode)? "_out":""), + Ivy_Regular(pNode->pEquiv)->Id, Ivy_IsComplement(pNode->pEquiv)? "\'":"" ); + fprintf( pFile, ", shape = %s", (Ivy_ObjIsLatch(pNode)? "box":"triangle") ); + fprintf( pFile, ", color = coral, fillcolor = coral" ); + fprintf( pFile, "];\n" ); + } + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + + // generate invisible edges from the square down + fprintf( pFile, "title1 -> title2 [style = invis];\n" ); + Ivy_ManForEachCo( pMan, pNode, i ) + fprintf( pFile, "title2 -> Node%d%s [style = invis];\n", pNode->Id, (Ivy_ObjIsLatch(pNode)? "_in":"") ); + + // generate edges + Ivy_ManForEachObj( pMan, pNode, i ) + { + if ( !Ivy_ObjIsNode(pNode) && !Ivy_ObjIsCo(pNode) && !Ivy_ObjIsBuf(pNode) ) + continue; + // generate the edge from this node to the next + fprintf( pFile, "Node%d%s", pNode->Id, (Ivy_ObjIsLatch(pNode)? "_in":"") ); + fprintf( pFile, " -> " ); + fprintf( pFile, "Node%d%s", Ivy_ObjFaninId0(pNode), (Ivy_ObjIsLatch(Ivy_ObjFanin0(pNode))? "_out":"") ); + fprintf( pFile, " [" ); + fprintf( pFile, "style = %s", Ivy_ObjFaninC0(pNode)? "dotted" : "bold" ); +// if ( Ivy_NtkIsSeq(pNode->pMan) && Seq_ObjFaninL0(pNode) > 0 ) +// fprintf( pFile, ", label = \"%s\"", Seq_ObjFaninGetInitPrintable(pNode,0) ); + fprintf( pFile, "]" ); + fprintf( pFile, ";\n" ); + if ( !Ivy_ObjIsNode(pNode) ) + continue; + // generate the edge from this node to the next + fprintf( pFile, "Node%d", pNode->Id ); + fprintf( pFile, " -> " ); + fprintf( pFile, "Node%d%s", Ivy_ObjFaninId1(pNode), (Ivy_ObjIsLatch(Ivy_ObjFanin1(pNode))? "_out":"") ); + fprintf( pFile, " [" ); + fprintf( pFile, "style = %s", Ivy_ObjFaninC1(pNode)? "dotted" : "bold" ); +// if ( Ivy_NtkIsSeq(pNode->pMan) && Seq_ObjFaninL1(pNode) > 0 ) +// fprintf( pFile, ", label = \"%s\"", Seq_ObjFaninGetInitPrintable(pNode,1) ); + fprintf( pFile, "]" ); + fprintf( pFile, ";\n" ); + // generate the edges between the equivalent nodes + if ( fHaig && pNode->pEquiv && Ivy_ObjRefs(pNode) > 0 ) + { + pPrev = pNode; + for ( pTemp = pNode->pEquiv; pTemp != pNode; pTemp = Ivy_Regular(pTemp->pEquiv) ) + { + fprintf( pFile, "Node%d", pPrev->Id ); + fprintf( pFile, " -> " ); + fprintf( pFile, "Node%d", pTemp->Id ); + fprintf( pFile, " [style = %s]", Ivy_IsComplement(pTemp->pEquiv)? "dotted" : "bold" ); + fprintf( pFile, ";\n" ); + pPrev = pTemp; + } + // connect the last node with the first + fprintf( pFile, "Node%d", pPrev->Id ); + fprintf( pFile, " -> " ); + fprintf( pFile, "Node%d", pNode->Id ); + fprintf( pFile, " [style = %s]", Ivy_IsComplement(pPrev->pEquiv)? "dotted" : "bold" ); + fprintf( pFile, ";\n" ); + } + } + + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + fclose( pFile ); + + // unmark nodes + if ( vBold ) + Vec_PtrForEachEntry( vBold, pNode, i ) + pNode->fMarkB = 0; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyTable.c b/abc_with_bb_support/src/aig/ivy/ivyTable.c new file mode 100644 index 000000000..429400acd --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyTable.c @@ -0,0 +1,301 @@ +/**CFile**************************************************************** + + FileName [ivyTable.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [Structural hashing table.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006. ] + + Revision [$Id: ivyTable.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// hashing the node +static unsigned Ivy_Hash( Ivy_Obj_t * pObj, int TableSize ) +{ + unsigned Key = Ivy_ObjIsExor(pObj) * 1699; + Key ^= Ivy_ObjFaninId0(pObj) * 7937; + Key ^= Ivy_ObjFaninId1(pObj) * 2971; + Key ^= Ivy_ObjFaninC0(pObj) * 911; + Key ^= Ivy_ObjFaninC1(pObj) * 353; + Key ^= Ivy_ObjInit(pObj) * 911; + return Key % TableSize; +} + +// returns the place where this node is stored (or should be stored) +static int * Ivy_TableFind( Ivy_Man_t * p, Ivy_Obj_t * pObj ) +{ + int i; + assert( Ivy_ObjIsHash(pObj) ); + for ( i = Ivy_Hash(pObj, p->nTableSize); p->pTable[i]; i = (i+1) % p->nTableSize ) + if ( p->pTable[i] == pObj->Id ) + break; + return p->pTable + i; +} + +static void Ivy_TableResize( Ivy_Man_t * p ); +static unsigned int Cudd_PrimeAig( unsigned int p ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Checks if node with the given attributes is in the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_TableLookup( Ivy_Man_t * p, Ivy_Obj_t * pObj ) +{ + Ivy_Obj_t * pEntry; + int i; + assert( !Ivy_IsComplement(pObj) ); + if ( !Ivy_ObjIsHash(pObj) ) + return NULL; + assert( Ivy_ObjIsLatch(pObj) || Ivy_ObjFaninId0(pObj) > 0 ); + assert( Ivy_ObjFaninId1(pObj) == 0 || Ivy_ObjFaninId0(pObj) < Ivy_ObjFaninId1(pObj) ); + if ( Ivy_ObjFanin0(pObj)->nRefs == 0 || (Ivy_ObjChild1(pObj) && Ivy_ObjFanin1(pObj)->nRefs == 0) ) + return NULL; + for ( i = Ivy_Hash(pObj, p->nTableSize); p->pTable[i]; i = (i+1) % p->nTableSize ) + { + pEntry = Ivy_ManObj( p, p->pTable[i] ); + if ( Ivy_ObjChild0(pEntry) == Ivy_ObjChild0(pObj) && + Ivy_ObjChild1(pEntry) == Ivy_ObjChild1(pObj) && + Ivy_ObjInit(pEntry) == Ivy_ObjInit(pObj) && + Ivy_ObjType(pEntry) == Ivy_ObjType(pObj) ) + return pEntry; + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [Adds the node to the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_TableInsert( Ivy_Man_t * p, Ivy_Obj_t * pObj ) +{ + int * pPlace; + assert( !Ivy_IsComplement(pObj) ); + if ( !Ivy_ObjIsHash(pObj) ) + return; + if ( (pObj->Id & 63) == 0 ) + { + if ( p->nTableSize < 2 * Ivy_ManHashObjNum(p) ) + Ivy_TableResize( p ); + } + pPlace = Ivy_TableFind( p, pObj ); + assert( *pPlace == 0 ); + *pPlace = pObj->Id; +} + +/**Function************************************************************* + + Synopsis [Deletes the node from the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_TableDelete( Ivy_Man_t * p, Ivy_Obj_t * pObj ) +{ + Ivy_Obj_t * pEntry; + int i, * pPlace; + assert( !Ivy_IsComplement(pObj) ); + if ( !Ivy_ObjIsHash(pObj) ) + return; + pPlace = Ivy_TableFind( p, pObj ); + assert( *pPlace == pObj->Id ); // node should be in the table + *pPlace = 0; + // rehash the adjacent entries + i = pPlace - p->pTable; + for ( i = (i+1) % p->nTableSize; p->pTable[i]; i = (i+1) % p->nTableSize ) + { + pEntry = Ivy_ManObj( p, p->pTable[i] ); + p->pTable[i] = 0; + Ivy_TableInsert( p, pEntry ); + } +} + +/**Function************************************************************* + + Synopsis [Updates the table to point to the new node.] + + Description [If the old node (pObj) is in the table, updates the table + to point to an object with different ID (ObjIdNew). The table should + not contain an object with ObjIdNew (this is currently not checked).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_TableUpdate( Ivy_Man_t * p, Ivy_Obj_t * pObj, int ObjIdNew ) +{ + int * pPlace; + assert( !Ivy_IsComplement(pObj) ); + if ( !Ivy_ObjIsHash(pObj) ) + return; + pPlace = Ivy_TableFind( p, pObj ); + assert( *pPlace == pObj->Id ); // node should be in the table + *pPlace = ObjIdNew; +} + +/**Function************************************************************* + + Synopsis [Count the number of nodes in the table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_TableCountEntries( Ivy_Man_t * p ) +{ + int i, Counter = 0; + for ( i = 0; i < p->nTableSize; i++ ) + Counter += (p->pTable[i] != 0); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Resizes the table.] + + Description [Typically this procedure should not be called.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_TableResize( Ivy_Man_t * p ) +{ + int * pTableOld, * pPlace; + int nTableSizeOld, Counter, nEntries, e, clk; +clk = clock(); + // save the old table + pTableOld = p->pTable; + nTableSizeOld = p->nTableSize; + // get the new table + p->nTableSize = Cudd_PrimeAig( 5 * Ivy_ManHashObjNum(p) ); + p->pTable = ALLOC( int, p->nTableSize ); + memset( p->pTable, 0, sizeof(int) * p->nTableSize ); + // rehash the entries from the old table + Counter = 0; + for ( e = 0; e < nTableSizeOld; e++ ) + { + if ( pTableOld[e] == 0 ) + continue; + Counter++; + // get the place where this entry goes in the table table + pPlace = Ivy_TableFind( p, Ivy_ManObj(p, pTableOld[e]) ); + assert( *pPlace == 0 ); // should not be in the table + *pPlace = pTableOld[e]; + } + nEntries = Ivy_ManHashObjNum(p); +// assert( Counter == nEntries ); +// printf( "Increasing the structural table size from %6d to %6d. ", nTableSizeOld, p->nTableSize ); +// PRT( "Time", clock() - clk ); + // replace the table and the parameters + free( pTableOld ); +} + +/**Function******************************************************************** + + Synopsis [Profiles the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void Ivy_TableProfile( Ivy_Man_t * p ) +{ + int i, Counter = 0; + for ( i = 0; i < p->nTableSize; i++ ) + { + if ( p->pTable[i] ) + Counter++; + else if ( Counter ) + { + printf( "%d ", Counter ); + Counter = 0; + } + } +} + +/**Function******************************************************************** + + Synopsis [Returns the next prime >= p.] + + Description [Copied from CUDD, for stand-aloneness.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +unsigned int Cudd_PrimeAig( unsigned int p) +{ + int i,pn; + + p--; + do { + p++; + if (p&1) { + pn = 1; + i = 3; + while ((unsigned) (i * i) <= p) { + if (p % i == 0) { + pn = 0; + break; + } + i += 2; + } + } else { + pn = 0; + } + } while (!pn); + return(p); + +} /* end of Cudd_Prime */ + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivyUtil.c b/abc_with_bb_support/src/aig/ivy/ivyUtil.c new file mode 100644 index 000000000..00dcf6970 --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivyUtil.c @@ -0,0 +1,818 @@ +/**CFile**************************************************************** + + FileName [ivyUtil.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [Various procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivyUtil.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Increments the current traversal ID of the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManIncrementTravId( Ivy_Man_t * p ) +{ + if ( p->nTravIds >= (1<<30)-1 - 1000 ) + Ivy_ManCleanTravId( p ); + p->nTravIds++; +} + +/**Function************************************************************* + + Synopsis [Sets the DFS ordering of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManCleanTravId( Ivy_Man_t * p ) +{ + Ivy_Obj_t * pObj; + int i; + p->nTravIds = 1; + Ivy_ManForEachObj( p, pObj, i ) + pObj->TravId = 0; +} + +/**Function************************************************************* + + Synopsis [Computes truth table of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManCollectCut_rec( Ivy_Man_t * p, Ivy_Obj_t * pNode, Vec_Int_t * vNodes ) +{ + if ( pNode->fMarkA ) + return; + pNode->fMarkA = 1; + assert( Ivy_ObjIsAnd(pNode) || Ivy_ObjIsExor(pNode) ); + Ivy_ManCollectCut_rec( p, Ivy_ObjFanin0(pNode), vNodes ); + Ivy_ManCollectCut_rec( p, Ivy_ObjFanin1(pNode), vNodes ); + Vec_IntPush( vNodes, pNode->Id ); +} + +/**Function************************************************************* + + Synopsis [Computes truth table of the cut.] + + Description [Does not modify the array of leaves. Uses array vTruth to store + temporary truth tables. The returned pointer should be used immediately.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManCollectCut( Ivy_Man_t * p, Ivy_Obj_t * pRoot, Vec_Int_t * vLeaves, Vec_Int_t * vNodes ) +{ + int i, Leaf; + // collect and mark the leaves + Vec_IntClear( vNodes ); + Vec_IntForEachEntry( vLeaves, Leaf, i ) + { + Vec_IntPush( vNodes, Leaf ); + Ivy_ManObj(p, Leaf)->fMarkA = 1; + } + // collect and mark the nodes + Ivy_ManCollectCut_rec( p, pRoot, vNodes ); + // clean the nodes + Vec_IntForEachEntry( vNodes, Leaf, i ) + Ivy_ManObj(p, Leaf)->fMarkA = 0; +} + +/**Function************************************************************* + + Synopsis [Returns the pointer to the truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned * Ivy_ObjGetTruthStore( int ObjNum, Vec_Int_t * vTruth ) +{ + return ((unsigned *)Vec_IntArray(vTruth)) + 8 * ObjNum; +} + +/**Function************************************************************* + + Synopsis [Computes truth table of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManCutTruthOne( Ivy_Man_t * p, Ivy_Obj_t * pNode, Vec_Int_t * vTruth, int nWords ) +{ + unsigned * pTruth, * pTruth0, * pTruth1; + int i; + pTruth = Ivy_ObjGetTruthStore( pNode->TravId, vTruth ); + pTruth0 = Ivy_ObjGetTruthStore( Ivy_ObjFanin0(pNode)->TravId, vTruth ); + pTruth1 = Ivy_ObjGetTruthStore( Ivy_ObjFanin1(pNode)->TravId, vTruth ); + if ( Ivy_ObjIsExor(pNode) ) + for ( i = 0; i < nWords; i++ ) + pTruth[i] = pTruth0[i] ^ pTruth1[i]; + else if ( !Ivy_ObjFaninC0(pNode) && !Ivy_ObjFaninC1(pNode) ) + for ( i = 0; i < nWords; i++ ) + pTruth[i] = pTruth0[i] & pTruth1[i]; + else if ( !Ivy_ObjFaninC0(pNode) && Ivy_ObjFaninC1(pNode) ) + for ( i = 0; i < nWords; i++ ) + pTruth[i] = pTruth0[i] & ~pTruth1[i]; + else if ( Ivy_ObjFaninC0(pNode) && !Ivy_ObjFaninC1(pNode) ) + for ( i = 0; i < nWords; i++ ) + pTruth[i] = ~pTruth0[i] & pTruth1[i]; + else // if ( Ivy_ObjFaninC0(pNode) && Ivy_ObjFaninC1(pNode) ) + for ( i = 0; i < nWords; i++ ) + pTruth[i] = ~pTruth0[i] & ~pTruth1[i]; +} + +/**Function************************************************************* + + Synopsis [Computes truth table of the cut.] + + Description [Does not modify the array of leaves. Uses array vTruth to store + temporary truth tables. The returned pointer should be used immediately.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned * Ivy_ManCutTruth( Ivy_Man_t * p, Ivy_Obj_t * pRoot, Vec_Int_t * vLeaves, Vec_Int_t * vNodes, Vec_Int_t * vTruth ) +{ + static unsigned uTruths[8][8] = { // elementary truth tables + { 0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA }, + { 0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC }, + { 0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0 }, + { 0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00 }, + { 0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000 }, + { 0x00000000,0xFFFFFFFF,0x00000000,0xFFFFFFFF,0x00000000,0xFFFFFFFF,0x00000000,0xFFFFFFFF }, + { 0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF,0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF }, + { 0x00000000,0x00000000,0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF } + }; + int i, Leaf; + // collect the cut + Ivy_ManCollectCut( p, pRoot, vLeaves, vNodes ); + // set the node numbers + Vec_IntForEachEntry( vNodes, Leaf, i ) + Ivy_ManObj(p, Leaf)->TravId = i; + // alloc enough memory + Vec_IntClear( vTruth ); + Vec_IntGrow( vTruth, 8 * Vec_IntSize(vNodes) ); + // set the elementary truth tables + Vec_IntForEachEntry( vLeaves, Leaf, i ) + memcpy( Ivy_ObjGetTruthStore(i, vTruth), uTruths[i], 8 * sizeof(unsigned) ); + // compute truths for other nodes + Vec_IntForEachEntryStart( vNodes, Leaf, i, Vec_IntSize(vLeaves) ) + Ivy_ManCutTruthOne( p, Ivy_ManObj(p, Leaf), vTruth, 8 ); + return Ivy_ObjGetTruthStore( pRoot->TravId, vTruth ); +} + +/**Function************************************************************* + + Synopsis [Collect the latches.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Ivy_ManLatches( Ivy_Man_t * p ) +{ + Vec_Int_t * vLatches; + Ivy_Obj_t * pObj; + int i; + vLatches = Vec_IntAlloc( Ivy_ManLatchNum(p) ); + Ivy_ManForEachLatch( p, pObj, i ) + Vec_IntPush( vLatches, pObj->Id ); + return vLatches; +} + +/**Function************************************************************* + + Synopsis [Collect the latches.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManLevels( Ivy_Man_t * p ) +{ + Ivy_Obj_t * pObj; + int i, LevelMax = 0; + Ivy_ManForEachPo( p, pObj, i ) + LevelMax = IVY_MAX( LevelMax, (int)Ivy_ObjFanin0(pObj)->Level ); + return LevelMax; +} + +/**Function************************************************************* + + Synopsis [Collect the latches.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ManResetLevels_rec( Ivy_Obj_t * pObj ) +{ + if ( pObj->Level || Ivy_ObjIsCi(pObj) || Ivy_ObjIsConst1(pObj) ) + return pObj->Level; + if ( Ivy_ObjIsBuf(pObj) ) + return pObj->Level = Ivy_ManResetLevels_rec( Ivy_ObjFanin0(pObj) ); + assert( Ivy_ObjIsNode(pObj) ); + Ivy_ManResetLevels_rec( Ivy_ObjFanin0(pObj) ); + Ivy_ManResetLevels_rec( Ivy_ObjFanin1(pObj) ); + return pObj->Level = Ivy_ObjLevelNew( pObj ); +} + +/**Function************************************************************* + + Synopsis [Collect the latches.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManResetLevels( Ivy_Man_t * p ) +{ + Ivy_Obj_t * pObj; + int i; + Ivy_ManForEachObj( p, pObj, i ) + pObj->Level = 0; + Ivy_ManForEachCo( p, pObj, i ) + Ivy_ManResetLevels_rec( Ivy_ObjFanin0(pObj) ); +} + +/**Function************************************************************* + + Synopsis [References/references the node and returns MFFC size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ObjRefDeref( Ivy_Man_t * p, Ivy_Obj_t * pNode, int fReference, int fLabel ) +{ + Ivy_Obj_t * pNode0, * pNode1; + int Counter; + // label visited nodes + if ( fLabel ) + Ivy_ObjSetTravIdCurrent( p, pNode ); + // skip the CI + if ( Ivy_ObjIsPi(pNode) ) + return 0; + assert( Ivy_ObjIsNode(pNode) || Ivy_ObjIsBuf(pNode) || Ivy_ObjIsLatch(pNode) ); + // process the internal node + pNode0 = Ivy_ObjFanin0(pNode); + pNode1 = Ivy_ObjFanin1(pNode); + Counter = Ivy_ObjIsNode(pNode); + if ( fReference ) + { + if ( pNode0->nRefs++ == 0 ) + Counter += Ivy_ObjRefDeref( p, pNode0, fReference, fLabel ); + if ( pNode1 && pNode1->nRefs++ == 0 ) + Counter += Ivy_ObjRefDeref( p, pNode1, fReference, fLabel ); + } + else + { + assert( pNode0->nRefs > 0 ); + assert( pNode1 == NULL || pNode1->nRefs > 0 ); + if ( --pNode0->nRefs == 0 ) + Counter += Ivy_ObjRefDeref( p, pNode0, fReference, fLabel ); + if ( pNode1 && --pNode1->nRefs == 0 ) + Counter += Ivy_ObjRefDeref( p, pNode1, fReference, fLabel ); + } + return Counter; +} + + +/**Function************************************************************* + + Synopsis [Labels MFFC with the current label.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ObjMffcLabel( Ivy_Man_t * p, Ivy_Obj_t * pNode ) +{ + int nConeSize1, nConeSize2; + assert( !Ivy_IsComplement( pNode ) ); + assert( Ivy_ObjIsNode( pNode ) ); + nConeSize1 = Ivy_ObjRefDeref( p, pNode, 0, 1 ); // dereference + nConeSize2 = Ivy_ObjRefDeref( p, pNode, 1, 0 ); // reference + assert( nConeSize1 == nConeSize2 ); + assert( nConeSize1 > 0 ); + return nConeSize1; +} + +/**Function************************************************************* + + Synopsis [Recursively updates fanout levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ObjUpdateLevel_rec( Ivy_Man_t * p, Ivy_Obj_t * pObj ) +{ + Ivy_Obj_t * pFanout; + Vec_Ptr_t * vFanouts; + int i, LevelNew; + assert( p->fFanout ); + assert( Ivy_ObjIsNode(pObj) ); + vFanouts = Vec_PtrAlloc( 10 ); + Ivy_ObjForEachFanout( p, pObj, vFanouts, pFanout, i ) + { + if ( Ivy_ObjIsCo(pFanout) ) + { +// assert( (int)Ivy_ObjFanin0(pFanout)->Level <= p->nLevelMax ); + continue; + } + LevelNew = Ivy_ObjLevelNew( pFanout ); + if ( (int)pFanout->Level == LevelNew ) + continue; + pFanout->Level = LevelNew; + Ivy_ObjUpdateLevel_rec( p, pFanout ); + } + Vec_PtrFree( vFanouts ); +} + +/**Function************************************************************* + + Synopsis [Compute the new required level.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ObjLevelRNew( Ivy_Man_t * p, Ivy_Obj_t * pObj ) +{ + Ivy_Obj_t * pFanout; + Vec_Ptr_t * vFanouts; + int i, Required, LevelNew = 1000000; + assert( p->fFanout && p->vRequired ); + vFanouts = Vec_PtrAlloc( 10 ); + Ivy_ObjForEachFanout( p, pObj, vFanouts, pFanout, i ) + { + Required = Vec_IntEntry(p->vRequired, pFanout->Id); + LevelNew = IVY_MIN( LevelNew, Required ); + } + Vec_PtrFree( vFanouts ); + return LevelNew - 1; +} + +/**Function************************************************************* + + Synopsis [Recursively updates fanout levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ObjUpdateLevelR_rec( Ivy_Man_t * p, Ivy_Obj_t * pObj, int ReqNew ) +{ + Ivy_Obj_t * pFanin; + if ( Ivy_ObjIsConst1(pObj) || Ivy_ObjIsCi(pObj) ) + return; + assert( Ivy_ObjIsNode(pObj) || Ivy_ObjIsBuf(pObj) ); + // process the first fanin + pFanin = Ivy_ObjFanin0(pObj); + if ( Vec_IntEntry(p->vRequired, pFanin->Id) > ReqNew - 1 ) + { + Vec_IntWriteEntry( p->vRequired, pFanin->Id, ReqNew - 1 ); + Ivy_ObjUpdateLevelR_rec( p, pFanin, ReqNew - 1 ); + } + if ( Ivy_ObjIsBuf(pObj) ) + return; + // process the second fanin + pFanin = Ivy_ObjFanin1(pObj); + if ( Vec_IntEntry(p->vRequired, pFanin->Id) > ReqNew - 1 ) + { + Vec_IntWriteEntry( p->vRequired, pFanin->Id, ReqNew - 1 ); + Ivy_ObjUpdateLevelR_rec( p, pFanin, ReqNew - 1 ); + } +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the node is the root of MUX or EXOR/NEXOR.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_ObjIsMuxType( Ivy_Obj_t * pNode ) +{ + Ivy_Obj_t * pNode0, * pNode1; + // check that the node is regular + assert( !Ivy_IsComplement(pNode) ); + // if the node is not AND, this is not MUX + if ( !Ivy_ObjIsAnd(pNode) ) + return 0; + // if the children are not complemented, this is not MUX + if ( !Ivy_ObjFaninC0(pNode) || !Ivy_ObjFaninC1(pNode) ) + return 0; + // get children + pNode0 = Ivy_ObjFanin0(pNode); + pNode1 = Ivy_ObjFanin1(pNode); + // if the children are not ANDs, this is not MUX + if ( !Ivy_ObjIsAnd(pNode0) || !Ivy_ObjIsAnd(pNode1) ) + return 0; + // otherwise the node is MUX iff it has a pair of equal grandchildren + return (Ivy_ObjFaninId0(pNode0) == Ivy_ObjFaninId0(pNode1) && (Ivy_ObjFaninC0(pNode0) ^ Ivy_ObjFaninC0(pNode1))) || + (Ivy_ObjFaninId0(pNode0) == Ivy_ObjFaninId1(pNode1) && (Ivy_ObjFaninC0(pNode0) ^ Ivy_ObjFaninC1(pNode1))) || + (Ivy_ObjFaninId1(pNode0) == Ivy_ObjFaninId0(pNode1) && (Ivy_ObjFaninC1(pNode0) ^ Ivy_ObjFaninC0(pNode1))) || + (Ivy_ObjFaninId1(pNode0) == Ivy_ObjFaninId1(pNode1) && (Ivy_ObjFaninC1(pNode0) ^ Ivy_ObjFaninC1(pNode1))); +} + +/**Function************************************************************* + + Synopsis [Recognizes what nodes are control and data inputs of a MUX.] + + Description [If the node is a MUX, returns the control variable C. + Assigns nodes T and E to be the then and else variables of the MUX. + Node C is never complemented. Nodes T and E can be complemented. + This function also recognizes EXOR/NEXOR gates as MUXes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_ObjRecognizeMux( Ivy_Obj_t * pNode, Ivy_Obj_t ** ppNodeT, Ivy_Obj_t ** ppNodeE ) +{ + Ivy_Obj_t * pNode0, * pNode1; + assert( !Ivy_IsComplement(pNode) ); + assert( Ivy_ObjIsMuxType(pNode) ); + // get children + pNode0 = Ivy_ObjFanin0(pNode); + pNode1 = Ivy_ObjFanin1(pNode); + // find the control variable +// if ( pNode1->p1 == Fraig_Not(pNode2->p1) ) + if ( Ivy_ObjFaninId0(pNode0) == Ivy_ObjFaninId0(pNode1) && (Ivy_ObjFaninC0(pNode0) ^ Ivy_ObjFaninC0(pNode1)) ) + { +// if ( Fraig_IsComplement(pNode1->p1) ) + if ( Ivy_ObjFaninC0(pNode0) ) + { // pNode2->p1 is positive phase of C + *ppNodeT = Ivy_Not(Ivy_ObjChild1(pNode1));//pNode2->p2); + *ppNodeE = Ivy_Not(Ivy_ObjChild1(pNode0));//pNode1->p2); + return Ivy_ObjChild0(pNode1);//pNode2->p1; + } + else + { // pNode1->p1 is positive phase of C + *ppNodeT = Ivy_Not(Ivy_ObjChild1(pNode0));//pNode1->p2); + *ppNodeE = Ivy_Not(Ivy_ObjChild1(pNode1));//pNode2->p2); + return Ivy_ObjChild0(pNode0);//pNode1->p1; + } + } +// else if ( pNode1->p1 == Fraig_Not(pNode2->p2) ) + else if ( Ivy_ObjFaninId0(pNode0) == Ivy_ObjFaninId1(pNode1) && (Ivy_ObjFaninC0(pNode0) ^ Ivy_ObjFaninC1(pNode1)) ) + { +// if ( Fraig_IsComplement(pNode1->p1) ) + if ( Ivy_ObjFaninC0(pNode0) ) + { // pNode2->p2 is positive phase of C + *ppNodeT = Ivy_Not(Ivy_ObjChild0(pNode1));//pNode2->p1); + *ppNodeE = Ivy_Not(Ivy_ObjChild1(pNode0));//pNode1->p2); + return Ivy_ObjChild1(pNode1);//pNode2->p2; + } + else + { // pNode1->p1 is positive phase of C + *ppNodeT = Ivy_Not(Ivy_ObjChild1(pNode0));//pNode1->p2); + *ppNodeE = Ivy_Not(Ivy_ObjChild0(pNode1));//pNode2->p1); + return Ivy_ObjChild0(pNode0);//pNode1->p1; + } + } +// else if ( pNode1->p2 == Fraig_Not(pNode2->p1) ) + else if ( Ivy_ObjFaninId1(pNode0) == Ivy_ObjFaninId0(pNode1) && (Ivy_ObjFaninC1(pNode0) ^ Ivy_ObjFaninC0(pNode1)) ) + { +// if ( Fraig_IsComplement(pNode1->p2) ) + if ( Ivy_ObjFaninC1(pNode0) ) + { // pNode2->p1 is positive phase of C + *ppNodeT = Ivy_Not(Ivy_ObjChild1(pNode1));//pNode2->p2); + *ppNodeE = Ivy_Not(Ivy_ObjChild0(pNode0));//pNode1->p1); + return Ivy_ObjChild0(pNode1);//pNode2->p1; + } + else + { // pNode1->p2 is positive phase of C + *ppNodeT = Ivy_Not(Ivy_ObjChild0(pNode0));//pNode1->p1); + *ppNodeE = Ivy_Not(Ivy_ObjChild1(pNode1));//pNode2->p2); + return Ivy_ObjChild1(pNode0);//pNode1->p2; + } + } +// else if ( pNode1->p2 == Fraig_Not(pNode2->p2) ) + else if ( Ivy_ObjFaninId1(pNode0) == Ivy_ObjFaninId1(pNode1) && (Ivy_ObjFaninC1(pNode0) ^ Ivy_ObjFaninC1(pNode1)) ) + { +// if ( Fraig_IsComplement(pNode1->p2) ) + if ( Ivy_ObjFaninC1(pNode0) ) + { // pNode2->p2 is positive phase of C + *ppNodeT = Ivy_Not(Ivy_ObjChild0(pNode1));//pNode2->p1); + *ppNodeE = Ivy_Not(Ivy_ObjChild0(pNode0));//pNode1->p1); + return Ivy_ObjChild1(pNode1);//pNode2->p2; + } + else + { // pNode1->p2 is positive phase of C + *ppNodeT = Ivy_Not(Ivy_ObjChild0(pNode0));//pNode1->p1); + *ppNodeE = Ivy_Not(Ivy_ObjChild0(pNode1));//pNode2->p1); + return Ivy_ObjChild1(pNode0);//pNode1->p2; + } + } + assert( 0 ); // this is not MUX + return NULL; +} + +/**Function************************************************************* + + Synopsis [Returns the real fanin.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Ivy_ObjReal( Ivy_Obj_t * pObj ) +{ + Ivy_Obj_t * pFanin; + if ( pObj == NULL || !Ivy_ObjIsBuf( Ivy_Regular(pObj) ) ) + return pObj; + pFanin = Ivy_ObjReal( Ivy_ObjChild0(Ivy_Regular(pObj)) ); + return Ivy_NotCond( pFanin, Ivy_IsComplement(pObj) ); +} + +/**Function************************************************************* + + Synopsis [Prints node in HAIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ObjPrintVerbose( Ivy_Man_t * p, Ivy_Obj_t * pObj, int fHaig ) +{ + Ivy_Obj_t * pTemp; + int fShowFanouts = 0; + assert( !Ivy_IsComplement(pObj) ); + printf( "Node %5d : ", Ivy_ObjId(pObj) ); + if ( Ivy_ObjIsConst1(pObj) ) + printf( "constant 1" ); + else if ( Ivy_ObjIsPi(pObj) ) + printf( "PI" ); + else if ( Ivy_ObjIsPo(pObj) ) + printf( "PO" ); + else if ( Ivy_ObjIsLatch(pObj) ) + printf( "latch (%d%s)", Ivy_ObjFanin0(pObj)->Id, (Ivy_ObjFaninC0(pObj)? "\'" : " ") ); + else if ( Ivy_ObjIsBuf(pObj) ) + printf( "buffer (%d%s)", Ivy_ObjFanin0(pObj)->Id, (Ivy_ObjFaninC0(pObj)? "\'" : " ") ); + else + printf( "AND( %5d%s, %5d%s )", + Ivy_ObjFanin0(pObj)->Id, (Ivy_ObjFaninC0(pObj)? "\'" : " "), + Ivy_ObjFanin1(pObj)->Id, (Ivy_ObjFaninC1(pObj)? "\'" : " ") ); + printf( " (refs = %3d)", Ivy_ObjRefs(pObj) ); + if ( fShowFanouts ) + { + Vec_Ptr_t * vFanouts; + Ivy_Obj_t * pFanout; + int i; + vFanouts = Vec_PtrAlloc( 10 ); + printf( "\nFanouts:\n" ); + Ivy_ObjForEachFanout( p, pObj, vFanouts, pFanout, i ) + { + printf( " " ); + printf( "Node %5d : ", Ivy_ObjId(pFanout) ); + if ( Ivy_ObjIsPo(pFanout) ) + printf( "PO" ); + else if ( Ivy_ObjIsLatch(pFanout) ) + printf( "latch (%d%s)", Ivy_ObjFanin0(pFanout)->Id, (Ivy_ObjFaninC0(pFanout)? "\'" : " ") ); + else if ( Ivy_ObjIsBuf(pFanout) ) + printf( "buffer (%d%s)", Ivy_ObjFanin0(pFanout)->Id, (Ivy_ObjFaninC0(pFanout)? "\'" : " ") ); + else + printf( "AND( %5d%s, %5d%s )", + Ivy_ObjFanin0(pFanout)->Id, (Ivy_ObjFaninC0(pFanout)? "\'" : " "), + Ivy_ObjFanin1(pFanout)->Id, (Ivy_ObjFaninC1(pFanout)? "\'" : " ") ); + printf( "\n" ); + } + Vec_PtrFree( vFanouts ); + return; + } + if ( !fHaig ) + { + if ( pObj->pEquiv == NULL ) + printf( " HAIG node not given" ); + else + printf( " HAIG node = %d%s", Ivy_Regular(pObj->pEquiv)->Id, (Ivy_IsComplement(pObj->pEquiv)? "\'" : " ") ); + return; + } + if ( pObj->pEquiv == NULL ) + return; + // there are choices + if ( Ivy_ObjRefs(pObj) > 0 ) + { + // print equivalence class + printf( " { %5d ", pObj->Id ); + assert( !Ivy_IsComplement(pObj->pEquiv) ); + for ( pTemp = pObj->pEquiv; pTemp != pObj; pTemp = Ivy_Regular(pTemp->pEquiv) ) + printf( " %5d%s", pTemp->Id, (Ivy_IsComplement(pTemp->pEquiv)? "\'" : " ") ); + printf( " }" ); + return; + } + // this is a secondary node + for ( pTemp = Ivy_Regular(pObj->pEquiv); Ivy_ObjRefs(pTemp) == 0; pTemp = Ivy_Regular(pTemp->pEquiv) ); + assert( Ivy_ObjRefs(pTemp) > 0 ); + printf( " class of %d", pTemp->Id ); +} + +/**Function************************************************************* + + Synopsis [Prints node in HAIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ivy_ManPrintVerbose( Ivy_Man_t * p, int fHaig ) +{ + Vec_Int_t * vNodes; + Ivy_Obj_t * pObj; + int i; + printf( "PIs: " ); + Ivy_ManForEachPi( p, pObj, i ) + printf( " %d", pObj->Id ); + printf( "\n" ); + printf( "POs: " ); + Ivy_ManForEachPo( p, pObj, i ) + printf( " %d", pObj->Id ); + printf( "\n" ); + printf( "Latches: " ); + Ivy_ManForEachLatch( p, pObj, i ) + printf( " %d=%d%s", pObj->Id, Ivy_ObjFanin0(pObj)->Id, (Ivy_ObjFaninC0(pObj)? "\'" : " ") ); + printf( "\n" ); + vNodes = Ivy_ManDfsSeq( p, NULL ); + Ivy_ManForEachNodeVec( p, vNodes, pObj, i ) + Ivy_ObjPrintVerbose( p, pObj, fHaig ), printf( "\n" ); + printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [Performs incremental rewriting of the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_CutTruthPrint2( Ivy_Man_t * p, Ivy_Cut_t * pCut, unsigned uTruth ) +{ + int i; + printf( "Trying cut : {" ); + for ( i = 0; i < pCut->nSize; i++ ) + printf( " %6d(%d)", Ivy_LeafId(pCut->pArray[i]), Ivy_LeafLat(pCut->pArray[i]) ); + printf( " } " ); + Extra_PrintBinary( stdout, &uTruth, 16 ); printf( "\n" ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Performs incremental rewriting of the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ivy_CutTruthPrint( Ivy_Man_t * p, Ivy_Cut_t * pCut, unsigned uTruth ) +{ + Vec_Ptr_t * vArray; + Ivy_Obj_t * pObj, * pFanout; + int nLatches = 0; + int nPresent = 0; + int i, k; + int fVerbose = 0; + + if ( fVerbose ) + printf( "Trying cut : {" ); + for ( i = 0; i < pCut->nSize; i++ ) + { + if ( fVerbose ) + printf( " %6d(%d)", Ivy_LeafId(pCut->pArray[i]), Ivy_LeafLat(pCut->pArray[i]) ); + nLatches += Ivy_LeafLat(pCut->pArray[i]); + } + if ( fVerbose ) + printf( " } " ); + if ( fVerbose ) + printf( "Latches = %d. ", nLatches ); + + // check if there are latches on the fanout edges + vArray = Vec_PtrAlloc( 100 ); + for ( i = 0; i < pCut->nSize; i++ ) + { + pObj = Ivy_ManObj( p, Ivy_LeafId(pCut->pArray[i]) ); + Ivy_ObjForEachFanout( p, pObj, vArray, pFanout, k ) + { + if ( Ivy_ObjIsLatch(pFanout) ) + { + nPresent++; + break; + } + } + } + Vec_PtrSize( vArray ); + if ( fVerbose ) + { + printf( "Present = %d. ", nPresent ); + if ( nLatches > nPresent ) + printf( "Clauses = %d. ", 2*(nLatches - nPresent) ); + printf( "\n" ); + } + return ( nLatches > nPresent ) ? 2*(nLatches - nPresent) : 0; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/ivy_.c b/abc_with_bb_support/src/aig/ivy/ivy_.c new file mode 100644 index 000000000..b99c7dce3 --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/ivy_.c @@ -0,0 +1,48 @@ +/**CFile**************************************************************** + + FileName [ivy_.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [And-Inverter Graph package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 11, 2006.] + + Revision [$Id: ivy_.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/ivy/module.make b/abc_with_bb_support/src/aig/ivy/module.make new file mode 100644 index 000000000..38ca958fe --- /dev/null +++ b/abc_with_bb_support/src/aig/ivy/module.make @@ -0,0 +1,22 @@ +SRC += src/aig/ivy/ivyBalance.c \ + src/aig/ivy/ivyCanon.c \ + src/aig/ivy/ivyCheck.c \ + src/aig/ivy/ivyCut.c \ + src/aig/ivy/ivyCutTrav.c \ + src/aig/ivy/ivyDfs.c \ + src/aig/ivy/ivyDsd.c \ + src/aig/ivy/ivyFanout.c \ + src/aig/ivy/ivyFastMap.c \ + src/aig/ivy/ivyFraig.c \ + src/aig/ivy/ivyHaig.c \ + src/aig/ivy/ivyMan.c \ + src/aig/ivy/ivyMem.c \ + src/aig/ivy/ivyMulti.c \ + src/aig/ivy/ivyObj.c \ + src/aig/ivy/ivyOper.c \ + src/aig/ivy/ivyResyn.c \ + src/aig/ivy/ivyRwr.c \ + src/aig/ivy/ivySeq.c \ + src/aig/ivy/ivyShow.c \ + src/aig/ivy/ivyTable.c \ + src/aig/ivy/ivyUtil.c diff --git a/abc_with_bb_support/src/aig/kit/kit.h b/abc_with_bb_support/src/aig/kit/kit.h new file mode 100644 index 000000000..4e54b5b3f --- /dev/null +++ b/abc_with_bb_support/src/aig/kit/kit.h @@ -0,0 +1,544 @@ +/**CFile**************************************************************** + + FileName [kit.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Computation kit.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Dec 6, 2006.] + + Revision [$Id: kit.h,v 1.00 2006/12/06 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __KIT_H__ +#define __KIT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include "vec.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Kit_Sop_t_ Kit_Sop_t; +struct Kit_Sop_t_ +{ + int nCubes; // the number of cubes + unsigned * pCubes; // the storage for cubes +}; + +typedef struct Kit_Edge_t_ Kit_Edge_t; +struct Kit_Edge_t_ +{ + unsigned fCompl : 1; // the complemented bit + unsigned Node : 30; // the decomposition node pointed by the edge +}; + +typedef struct Kit_Node_t_ Kit_Node_t; +struct Kit_Node_t_ +{ + Kit_Edge_t eEdge0; // the left child of the node + Kit_Edge_t eEdge1; // the right child of the node + // other info + void * pFunc; // the function of the node (BDD or AIG) + unsigned Level : 14; // the level of this node in the global AIG + // printing info + unsigned fNodeOr : 1; // marks the original OR node + unsigned fCompl0 : 1; // marks the original complemented edge + unsigned fCompl1 : 1; // marks the original complemented edge + // latch info + unsigned nLat0 : 5; // the number of latches on the first edge + unsigned nLat1 : 5; // the number of latches on the second edge + unsigned nLat2 : 5; // the number of latches on the output edge +}; + +typedef struct Kit_Graph_t_ Kit_Graph_t; +struct Kit_Graph_t_ +{ + int fConst; // marks the constant 1 graph + int nLeaves; // the number of leaves + int nSize; // the number of nodes (including the leaves) + int nCap; // the number of allocated nodes + Kit_Node_t * pNodes; // the array of leaves and internal nodes + Kit_Edge_t eRoot; // the pointer to the topmost node +}; + + +// DSD node types +typedef enum { + KIT_DSD_NONE = 0, // 0: unknown + KIT_DSD_CONST1, // 1: constant 1 + KIT_DSD_VAR, // 2: elementary variable + KIT_DSD_AND, // 3: multi-input AND + KIT_DSD_XOR, // 4: multi-input XOR + KIT_DSD_PRIME // 5: arbitrary function of 3+ variables +} Kit_Dsd_t; + +// DSD node +typedef struct Kit_DsdObj_t_ Kit_DsdObj_t; +struct Kit_DsdObj_t_ +{ + unsigned Id : 6; // the number of this node + unsigned Type : 3; // none, const, var, AND, XOR, MUX, PRIME + unsigned fMark : 1; // finished checking output + unsigned Offset : 8; // offset to the truth table + unsigned nRefs : 8; // offset to the truth table + unsigned nFans : 6; // the number of fanins of this node + unsigned char pFans[0]; // the fanin literals +}; + +// DSD network +typedef struct Kit_DsdNtk_t_ Kit_DsdNtk_t; +struct Kit_DsdNtk_t_ +{ + unsigned char nVars; // at most 16 (perhaps 18?) + unsigned char nNodesAlloc; // the number of allocated nodes (at most nVars) + unsigned char nNodes; // the number of nodes + unsigned char Root; // the root of the tree + unsigned * pMem; // memory for the truth tables (memory manager?) + unsigned * pSupps; // supports of the nodes + Kit_DsdObj_t** pNodes; // the nodes +}; + +// DSD manager +typedef struct Kit_DsdMan_t_ Kit_DsdMan_t; +struct Kit_DsdMan_t_ +{ + int nVars; // the maximum number of variables + int nWords; // the number of words in TTs + Vec_Ptr_t * vTtElems; // elementary truth tables + Vec_Ptr_t * vTtNodes; // the node truth tables +}; + +static inline int Kit_DsdVar2Lit( int Var, int fCompl ) { return Var + Var + fCompl; } +static inline int Kit_DsdLit2Var( int Lit ) { return Lit >> 1; } +static inline int Kit_DsdLitIsCompl( int Lit ) { return Lit & 1; } +static inline int Kit_DsdLitNot( int Lit ) { return Lit ^ 1; } +static inline int Kit_DsdLitNotCond( int Lit, int c ) { return Lit ^ (int)(c > 0); } +static inline int Kit_DsdLitRegular( int Lit ) { return Lit & 0xfe; } + +static inline unsigned Kit_DsdObjOffset( int nFans ) { return (nFans >> 2) + ((nFans & 3) > 0); } +static inline unsigned * Kit_DsdObjTruth( Kit_DsdObj_t * pObj ) { return pObj->Type == KIT_DSD_PRIME ? (unsigned *)pObj->pFans + pObj->Offset: NULL; } +static inline Kit_DsdObj_t * Kit_DsdNtkObj( Kit_DsdNtk_t * pNtk, int Id ) { assert( Id >= 0 && Id < pNtk->nVars + pNtk->nNodes ); return Id < pNtk->nVars ? NULL : pNtk->pNodes[Id - pNtk->nVars]; } +static inline Kit_DsdObj_t * Kit_DsdNtkRoot( Kit_DsdNtk_t * pNtk ) { return Kit_DsdNtkObj( pNtk, Kit_DsdLit2Var(pNtk->Root) ); } +static inline int Kit_DsdLitIsLeaf( Kit_DsdNtk_t * pNtk, int Lit ) { int Id = Kit_DsdLit2Var(Lit); assert( Id >= 0 && Id < pNtk->nVars + pNtk->nNodes ); return Id < pNtk->nVars; } +static inline unsigned Kit_DsdLitSupport( Kit_DsdNtk_t * pNtk, int Lit ) { int Id = Kit_DsdLit2Var(Lit); assert( Id >= 0 && Id < pNtk->nVars + pNtk->nNodes ); return pNtk->pSupps? (Id < pNtk->nVars? (1 << Id) : pNtk->pSupps[Id - pNtk->nVars]) : 0; } + +#define Kit_DsdNtkForEachObj( pNtk, pObj, i ) \ + for ( i = 0; (i < (pNtk)->nNodes) && ((pObj) = (pNtk)->pNodes[i]); i++ ) +#define Kit_DsdObjForEachFanin( pNtk, pObj, iLit, i ) \ + for ( i = 0; (i < (pObj)->nFans) && ((iLit) = (pObj)->pFans[i], 1); i++ ) + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define KIT_MIN(a,b) (((a) < (b))? (a) : (b)) +#define KIT_MAX(a,b) (((a) > (b))? (a) : (b)) +#define KIT_INFINITY (100000000) + +#ifndef ALLOC +#define ALLOC(type, num) ((type *) malloc(sizeof(type) * (num))) +#endif + +#ifndef FREE +#define FREE(obj) ((obj) ? (free((char *) (obj)), (obj) = 0) : 0) +#endif + +#ifndef REALLOC +#define REALLOC(type, obj, num) \ + ((obj) ? ((type *) realloc((char *)(obj), sizeof(type) * (num))) : \ + ((type *) malloc(sizeof(type) * (num)))) +#endif + +static inline int Kit_CubeHasLit( unsigned uCube, int i ) { return(uCube & (unsigned)(1< 0; } +static inline unsigned Kit_CubeSetLit( unsigned uCube, int i ) { return uCube | (unsigned)(1<> (32-nVar); } + +static inline int Kit_CubeIsMarked( unsigned uCube ) { return Kit_CubeHasLit( uCube, 31 ); } +static inline unsigned Kit_CubeMark( unsigned uCube ) { return Kit_CubeSetLit( uCube, 31 ); } +static inline unsigned Kit_CubeUnmark( unsigned uCube ) { return Kit_CubeRemLit( uCube, 31 ); } + +static inline int Kit_SopCubeNum( Kit_Sop_t * cSop ) { return cSop->nCubes; } +static inline unsigned Kit_SopCube( Kit_Sop_t * cSop, int i ) { return cSop->pCubes[i]; } +static inline void Kit_SopShrink( Kit_Sop_t * cSop, int nCubesNew ) { cSop->nCubes = nCubesNew; } +static inline void Kit_SopPushCube( Kit_Sop_t * cSop, unsigned uCube ) { cSop->pCubes[cSop->nCubes++] = uCube; } +static inline void Kit_SopWriteCube( Kit_Sop_t * cSop, unsigned uCube, int i ) { cSop->pCubes[i] = uCube; } + +static inline Kit_Edge_t Kit_EdgeCreate( int Node, int fCompl ) { Kit_Edge_t eEdge = { fCompl, Node }; return eEdge; } +static inline unsigned Kit_EdgeToInt( Kit_Edge_t eEdge ) { return (eEdge.Node << 1) | eEdge.fCompl; } +static inline Kit_Edge_t Kit_IntToEdge( unsigned Edge ) { return Kit_EdgeCreate( Edge >> 1, Edge & 1 ); } +static inline unsigned Kit_EdgeToInt_( Kit_Edge_t eEdge ) { return *(unsigned *)&eEdge; } +static inline Kit_Edge_t Kit_IntToEdge_( unsigned Edge ) { return *(Kit_Edge_t *)&Edge; } + +static inline int Kit_GraphIsConst( Kit_Graph_t * pGraph ) { return pGraph->fConst; } +static inline int Kit_GraphIsConst0( Kit_Graph_t * pGraph ) { return pGraph->fConst && pGraph->eRoot.fCompl; } +static inline int Kit_GraphIsConst1( Kit_Graph_t * pGraph ) { return pGraph->fConst && !pGraph->eRoot.fCompl; } +static inline int Kit_GraphIsComplement( Kit_Graph_t * pGraph ) { return pGraph->eRoot.fCompl; } +static inline int Kit_GraphIsVar( Kit_Graph_t * pGraph ) { return pGraph->eRoot.Node < (unsigned)pGraph->nLeaves; } +static inline void Kit_GraphComplement( Kit_Graph_t * pGraph ) { pGraph->eRoot.fCompl ^= 1; } +static inline void Kit_GraphSetRoot( Kit_Graph_t * pGraph, Kit_Edge_t eRoot ) { pGraph->eRoot = eRoot; } +static inline int Kit_GraphLeaveNum( Kit_Graph_t * pGraph ) { return pGraph->nLeaves; } +static inline int Kit_GraphNodeNum( Kit_Graph_t * pGraph ) { return pGraph->nSize - pGraph->nLeaves; } +static inline Kit_Node_t * Kit_GraphNode( Kit_Graph_t * pGraph, int i ) { return pGraph->pNodes + i; } +static inline Kit_Node_t * Kit_GraphNodeLast( Kit_Graph_t * pGraph ) { return pGraph->pNodes + pGraph->nSize - 1; } +static inline int Kit_GraphNodeInt( Kit_Graph_t * pGraph, Kit_Node_t * pNode ) { return pNode - pGraph->pNodes; } +static inline int Kit_GraphNodeIsVar( Kit_Graph_t * pGraph, Kit_Node_t * pNode ) { return Kit_GraphNodeInt(pGraph,pNode) < pGraph->nLeaves; } +static inline Kit_Node_t * Kit_GraphVar( Kit_Graph_t * pGraph ) { assert( Kit_GraphIsVar( pGraph ) ); return Kit_GraphNode( pGraph, pGraph->eRoot.Node ); } +static inline int Kit_GraphVarInt( Kit_Graph_t * pGraph ) { assert( Kit_GraphIsVar( pGraph ) ); return Kit_GraphNodeInt( pGraph, Kit_GraphVar(pGraph) ); } +static inline Kit_Node_t * Kit_GraphNodeFanin0( Kit_Graph_t * pGraph, Kit_Node_t * pNode ){ return Kit_GraphNodeIsVar(pGraph, pNode)? NULL : Kit_GraphNode(pGraph, pNode->eEdge0.Node); } +static inline Kit_Node_t * Kit_GraphNodeFanin1( Kit_Graph_t * pGraph, Kit_Node_t * pNode ){ return Kit_GraphNodeIsVar(pGraph, pNode)? NULL : Kit_GraphNode(pGraph, pNode->eEdge1.Node); } +static inline int Kit_GraphRootLevel( Kit_Graph_t * pGraph ) { return Kit_GraphNode(pGraph, pGraph->eRoot.Node)->Level; } + +static inline int Kit_Float2Int( float Val ) { return *((int *)&Val); } +static inline float Kit_Int2Float( int Num ) { return *((float *)&Num); } +static inline int Kit_BitWordNum( int nBits ) { return nBits/(8*sizeof(unsigned)) + ((nBits%(8*sizeof(unsigned))) > 0); } +static inline int Kit_TruthWordNum( int nVars ) { return nVars <= 5 ? 1 : (1 << (nVars - 5)); } +static inline unsigned Kit_BitMask( int nBits ) { assert( nBits <= 32 ); return ~((~(unsigned)0) << nBits); } + +static inline void Kit_TruthSetBit( unsigned * p, int Bit ) { p[Bit>>5] |= (1<<(Bit & 31)); } +static inline void Kit_TruthXorBit( unsigned * p, int Bit ) { p[Bit>>5] ^= (1<<(Bit & 31)); } +static inline int Kit_TruthHasBit( unsigned * p, int Bit ) { return (p[Bit>>5] & (1<<(Bit & 31))) > 0; } + +static inline int Kit_WordFindFirstBit( unsigned uWord ) +{ + int i; + for ( i = 0; i < 32; i++ ) + if ( uWord & (1 << i) ) + return i; + return -1; +} +static inline int Kit_WordHasOneBit( unsigned uWord ) +{ + return (uWord & (uWord - 1)) == 0; +} +static inline int Kit_WordCountOnes( unsigned uWord ) +{ + uWord = (uWord & 0x55555555) + ((uWord>>1) & 0x55555555); + uWord = (uWord & 0x33333333) + ((uWord>>2) & 0x33333333); + uWord = (uWord & 0x0F0F0F0F) + ((uWord>>4) & 0x0F0F0F0F); + uWord = (uWord & 0x00FF00FF) + ((uWord>>8) & 0x00FF00FF); + return (uWord & 0x0000FFFF) + (uWord>>16); +} +static inline int Kit_TruthCountOnes( unsigned * pIn, int nVars ) +{ + int w, Counter = 0; + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + Counter += Kit_WordCountOnes(pIn[w]); + return Counter; +} +static inline int Kit_TruthFindFirstBit( unsigned * pIn, int nVars ) +{ + int w; + for ( w = 0; w < Kit_TruthWordNum(nVars); w++ ) + if ( pIn[w] ) + return 32*w + Kit_WordFindFirstBit(pIn[w]); + return -1; +} +static inline int Kit_TruthFindFirstZero( unsigned * pIn, int nVars ) +{ + int w; + for ( w = 0; w < Kit_TruthWordNum(nVars); w++ ) + if ( ~pIn[w] ) + return 32*w + Kit_WordFindFirstBit(~pIn[w]); + return -1; +} +static inline int Kit_TruthIsEqual( unsigned * pIn0, unsigned * pIn1, int nVars ) +{ + int w; + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + if ( pIn0[w] != pIn1[w] ) + return 0; + return 1; +} +static inline int Kit_TruthIsOpposite( unsigned * pIn0, unsigned * pIn1, int nVars ) +{ + int w; + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + if ( pIn0[w] != ~pIn1[w] ) + return 0; + return 1; +} +static inline int Kit_TruthIsEqualWithPhase( unsigned * pIn0, unsigned * pIn1, int nVars ) +{ + int w; + if ( (pIn0[0] & 1) == (pIn1[0] & 1) ) + { + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + if ( pIn0[w] != pIn1[w] ) + return 0; + } + else + { + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + if ( pIn0[w] != ~pIn1[w] ) + return 0; + } + return 1; +} +static inline int Kit_TruthIsConst0( unsigned * pIn, int nVars ) +{ + int w; + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + if ( pIn[w] ) + return 0; + return 1; +} +static inline int Kit_TruthIsConst1( unsigned * pIn, int nVars ) +{ + int w; + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + if ( pIn[w] != ~(unsigned)0 ) + return 0; + return 1; +} +static inline int Kit_TruthIsImply( unsigned * pIn1, unsigned * pIn2, int nVars ) +{ + int w; + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + if ( pIn1[w] & ~pIn2[w] ) + return 0; + return 1; +} +static inline int Kit_TruthIsDisjoint( unsigned * pIn1, unsigned * pIn2, int nVars ) +{ + int w; + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + if ( pIn1[w] & pIn2[w] ) + return 0; + return 1; +} +static inline int Kit_TruthIsDisjoint3( unsigned * pIn1, unsigned * pIn2, unsigned * pIn3, int nVars ) +{ + int w; + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + if ( pIn1[w] & pIn2[w] & pIn3[w] ) + return 0; + return 1; +} +static inline void Kit_TruthCopy( unsigned * pOut, unsigned * pIn, int nVars ) +{ + int w; + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = pIn[w]; +} +static inline void Kit_TruthClear( unsigned * pOut, int nVars ) +{ + int w; + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = 0; +} +static inline void Kit_TruthFill( unsigned * pOut, int nVars ) +{ + int w; + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = ~(unsigned)0; +} +static inline void Kit_TruthNot( unsigned * pOut, unsigned * pIn, int nVars ) +{ + int w; + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = ~pIn[w]; +} +static inline void Kit_TruthAnd( unsigned * pOut, unsigned * pIn0, unsigned * pIn1, int nVars ) +{ + int w; + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = pIn0[w] & pIn1[w]; +} +static inline void Kit_TruthOr( unsigned * pOut, unsigned * pIn0, unsigned * pIn1, int nVars ) +{ + int w; + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = pIn0[w] | pIn1[w]; +} +static inline void Kit_TruthXor( unsigned * pOut, unsigned * pIn0, unsigned * pIn1, int nVars ) +{ + int w; + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = pIn0[w] ^ pIn1[w]; +} +static inline void Kit_TruthSharp( unsigned * pOut, unsigned * pIn0, unsigned * pIn1, int nVars ) +{ + int w; + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = pIn0[w] & ~pIn1[w]; +} +static inline void Kit_TruthNand( unsigned * pOut, unsigned * pIn0, unsigned * pIn1, int nVars ) +{ + int w; + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = ~(pIn0[w] & pIn1[w]); +} +static inline void Kit_TruthAndPhase( unsigned * pOut, unsigned * pIn0, unsigned * pIn1, int nVars, int fCompl0, int fCompl1 ) +{ + int w; + if ( fCompl0 && fCompl1 ) + { + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = ~(pIn0[w] | pIn1[w]); + } + else if ( fCompl0 && !fCompl1 ) + { + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = ~pIn0[w] & pIn1[w]; + } + else if ( !fCompl0 && fCompl1 ) + { + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = pIn0[w] & ~pIn1[w]; + } + else // if ( !fCompl0 && !fCompl1 ) + { + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = pIn0[w] & pIn1[w]; + } +} +static inline void Kit_TruthMux( unsigned * pOut, unsigned * pIn0, unsigned * pIn1, unsigned * pCtrl, int nVars ) +{ + int w; + for ( w = Kit_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = (pIn0[w] & ~pCtrl[w]) | (pIn1[w] & pCtrl[w]); +} + +//////////////////////////////////////////////////////////////////////// +/// ITERATORS /// +//////////////////////////////////////////////////////////////////////// + +#define Kit_SopForEachCube( cSop, uCube, i ) \ + for ( i = 0; (i < Kit_SopCubeNum(cSop)) && ((uCube) = Kit_SopCube(cSop, i)); i++ ) +#define Kit_CubeForEachLiteral( uCube, Lit, nLits, i ) \ + for ( i = 0; (i < (nLits)) && ((Lit) = Kit_CubeHasLit(uCube, i)); i++ ) + +#define Kit_GraphForEachLeaf( pGraph, pLeaf, i ) \ + for ( i = 0; (i < (pGraph)->nLeaves) && (((pLeaf) = Kit_GraphNode(pGraph, i)), 1); i++ ) +#define Kit_GraphForEachNode( pGraph, pAnd, i ) \ + for ( i = (pGraph)->nLeaves; (i < (pGraph)->nSize) && (((pAnd) = Kit_GraphNode(pGraph, i)), 1); i++ ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== kitBdd.c ==========================================================*/ +extern DdNode * Kit_SopToBdd( DdManager * dd, Kit_Sop_t * cSop, int nVars ); +extern DdNode * Kit_GraphToBdd( DdManager * dd, Kit_Graph_t * pGraph ); +extern DdNode * Kit_TruthToBdd( DdManager * dd, unsigned * pTruth, int nVars, int fMSBonTop ); +/*=== kitDsd.c ==========================================================*/ +extern Kit_DsdMan_t * Kit_DsdManAlloc( int nVars ); +extern void Kit_DsdManFree( Kit_DsdMan_t * p ); +extern Kit_DsdNtk_t * Kit_DsdDeriveNtk( unsigned * pTruth, int nVars, int nLutSize ); +extern unsigned * Kit_DsdTruthCompute( Kit_DsdMan_t * p, Kit_DsdNtk_t * pNtk, unsigned uSupp ); +extern void Kit_DsdTruth( Kit_DsdNtk_t * pNtk, unsigned * pTruthRes ); +extern void Kit_DsdTruthPartial( Kit_DsdMan_t * p, Kit_DsdNtk_t * pNtk, unsigned * pTruthRes, unsigned uSupp ); +extern void Kit_DsdPrint( FILE * pFile, Kit_DsdNtk_t * pNtk ); +extern void Kit_DsdPrintExpanded( Kit_DsdNtk_t * pNtk ); +extern void Kit_DsdPrintFromTruth( unsigned * pTruth, int nVars ); +extern Kit_DsdNtk_t * Kit_DsdDecompose( unsigned * pTruth, int nVars ); +extern Kit_DsdNtk_t * Kit_DsdDecomposeMux( unsigned * pTruth, int nVars, int nDecMux ); +extern void Kit_DsdVerify( Kit_DsdNtk_t * pNtk, unsigned * pTruth, int nVars ); +extern void Kit_DsdNtkFree( Kit_DsdNtk_t * pNtk ); +extern int Kit_DsdNonDsdSizeMax( Kit_DsdNtk_t * pNtk ); +extern void Kit_DsdGetSupports( Kit_DsdNtk_t * p ); +extern Kit_DsdNtk_t * Kit_DsdExpand( Kit_DsdNtk_t * p ); +extern Kit_DsdNtk_t * Kit_DsdShrink( Kit_DsdNtk_t * p, int pPrios[] ); +extern void Kit_DsdRotate( Kit_DsdNtk_t * p, int pFreqs[] ); +extern int Kit_DsdCofactoring( unsigned * pTruth, int nVars, int * pCofVars, int nLimit, int fVerbose ); +/*=== kitFactor.c ==========================================================*/ +extern Kit_Graph_t * Kit_SopFactor( Vec_Int_t * vCover, int fCompl, int nVars, Vec_Int_t * vMemory ); +/*=== kitGraph.c ==========================================================*/ +extern Kit_Graph_t * Kit_GraphCreate( int nLeaves ); +extern Kit_Graph_t * Kit_GraphCreateConst0(); +extern Kit_Graph_t * Kit_GraphCreateConst1(); +extern Kit_Graph_t * Kit_GraphCreateLeaf( int iLeaf, int nLeaves, int fCompl ); +extern void Kit_GraphFree( Kit_Graph_t * pGraph ); +extern Kit_Node_t * Kit_GraphAppendNode( Kit_Graph_t * pGraph ); +extern Kit_Edge_t Kit_GraphAddNodeAnd( Kit_Graph_t * pGraph, Kit_Edge_t eEdge0, Kit_Edge_t eEdge1 ); +extern Kit_Edge_t Kit_GraphAddNodeOr( Kit_Graph_t * pGraph, Kit_Edge_t eEdge0, Kit_Edge_t eEdge1 ); +extern Kit_Edge_t Kit_GraphAddNodeXor( Kit_Graph_t * pGraph, Kit_Edge_t eEdge0, Kit_Edge_t eEdge1, int Type ); +extern Kit_Edge_t Kit_GraphAddNodeMux( Kit_Graph_t * pGraph, Kit_Edge_t eEdgeC, Kit_Edge_t eEdgeT, Kit_Edge_t eEdgeE, int Type ); +extern unsigned Kit_GraphToTruth( Kit_Graph_t * pGraph ); +extern Kit_Graph_t * Kit_TruthToGraph( unsigned * pTruth, int nVars, Vec_Int_t * vMemory ); +extern int Kit_GraphLeafDepth_rec( Kit_Graph_t * pGraph, Kit_Node_t * pNode, Kit_Node_t * pLeaf ); +/*=== kitHop.c ==========================================================*/ +/*=== kitIsop.c ==========================================================*/ +extern int Kit_TruthIsop( unsigned * puTruth, int nVars, Vec_Int_t * vMemory, int fTryBoth ); +/*=== kitSop.c ==========================================================*/ +extern void Kit_SopCreate( Kit_Sop_t * cResult, Vec_Int_t * vInput, int nVars, Vec_Int_t * vMemory ); +extern void Kit_SopCreateInverse( Kit_Sop_t * cResult, Vec_Int_t * vInput, int nVars, Vec_Int_t * vMemory ); +extern void Kit_SopDup( Kit_Sop_t * cResult, Kit_Sop_t * cSop, Vec_Int_t * vMemory ); +extern void Kit_SopDivideByLiteralQuo( Kit_Sop_t * cSop, int iLit ); +extern void Kit_SopDivideByCube( Kit_Sop_t * cSop, Kit_Sop_t * cDiv, Kit_Sop_t * vQuo, Kit_Sop_t * vRem, Vec_Int_t * vMemory ); +extern void Kit_SopDivideInternal( Kit_Sop_t * cSop, Kit_Sop_t * cDiv, Kit_Sop_t * vQuo, Kit_Sop_t * vRem, Vec_Int_t * vMemory ); +extern void Kit_SopMakeCubeFree( Kit_Sop_t * cSop ); +extern int Kit_SopIsCubeFree( Kit_Sop_t * cSop ); +extern void Kit_SopCommonCubeCover( Kit_Sop_t * cResult, Kit_Sop_t * cSop, Vec_Int_t * vMemory ); +extern int Kit_SopAnyLiteral( Kit_Sop_t * cSop, int nLits ); +extern int Kit_SopDivisor( Kit_Sop_t * cResult, Kit_Sop_t * cSop, int nLits, Vec_Int_t * vMemory ); +extern void Kit_SopBestLiteralCover( Kit_Sop_t * cResult, Kit_Sop_t * cSop, unsigned uCube, int nLits, Vec_Int_t * vMemory ); +/*=== kitTruth.c ==========================================================*/ +extern void Kit_TruthSwapAdjacentVars( unsigned * pOut, unsigned * pIn, int nVars, int Start ); +extern void Kit_TruthStretch( unsigned * pOut, unsigned * pIn, int nVars, int nVarsAll, unsigned Phase, int fReturnIn ); +extern void Kit_TruthShrink( unsigned * pOut, unsigned * pIn, int nVars, int nVarsAll, unsigned Phase, int fReturnIn ); +extern int Kit_TruthVarInSupport( unsigned * pTruth, int nVars, int iVar ); +extern int Kit_TruthSupportSize( unsigned * pTruth, int nVars ); +extern unsigned Kit_TruthSupport( unsigned * pTruth, int nVars ); +extern void Kit_TruthCofactor0( unsigned * pTruth, int nVars, int iVar ); +extern void Kit_TruthCofactor1( unsigned * pTruth, int nVars, int iVar ); +extern void Kit_TruthCofactor0New( unsigned * pOut, unsigned * pIn, int nVars, int iVar ); +extern void Kit_TruthCofactor1New( unsigned * pOut, unsigned * pIn, int nVars, int iVar ); +extern void Kit_TruthExist( unsigned * pTruth, int nVars, int iVar ); +extern void Kit_TruthExistNew( unsigned * pRes, unsigned * pTruth, int nVars, int iVar ); +extern void Kit_TruthExistSet( unsigned * pRes, unsigned * pTruth, int nVars, unsigned uMask ); +extern void Kit_TruthForall( unsigned * pTruth, int nVars, int iVar ); +extern void Kit_TruthForallNew( unsigned * pRes, unsigned * pTruth, int nVars, int iVar ); +extern void Kit_TruthForallSet( unsigned * pRes, unsigned * pTruth, int nVars, unsigned uMask ); +extern void Kit_TruthUniqueNew( unsigned * pRes, unsigned * pTruth, int nVars, int iVar ); +extern void Kit_TruthMuxVar( unsigned * pOut, unsigned * pCof0, unsigned * pCof1, int nVars, int iVar ); +extern void Kit_TruthChangePhase( unsigned * pTruth, int nVars, int iVar ); +extern int Kit_TruthMinCofSuppOverlap( unsigned * pTruth, int nVars, int * pVarMin ); +extern int Kit_TruthBestCofVar( unsigned * pTruth, int nVars, unsigned * pCof0, unsigned * pCof1 ); +extern void Kit_TruthCountOnesInCofs( unsigned * pTruth, int nVars, short * pStore ); +extern void Kit_TruthCountOnesInCofsSlow( unsigned * pTruth, int nVars, short * pStore, unsigned * pAux ); +extern unsigned Kit_TruthHash( unsigned * pIn, int nWords ); +extern unsigned Kit_TruthSemiCanonicize( unsigned * pInOut, unsigned * pAux, int nVars, char * pCanonPerm, short * pStore ); +extern char * Kit_TruthDumpToFile( unsigned * pTruth, int nVars, int nFile ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/aig/kit/kitBdd.c b/abc_with_bb_support/src/aig/kit/kitBdd.c new file mode 100644 index 000000000..e5add3e39 --- /dev/null +++ b/abc_with_bb_support/src/aig/kit/kitBdd.c @@ -0,0 +1,231 @@ +/**CFile**************************************************************** + + FileName [kitBdd.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Computation kit.] + + Synopsis [Procedures involving BDDs.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Dec 6, 2006.] + + Revision [$Id: kitBdd.c,v 1.00 2006/12/06 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "kit.h" +#include "extra.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Derives the BDD for the given SOP.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Kit_SopToBdd( DdManager * dd, Kit_Sop_t * cSop, int nVars ) +{ + DdNode * bSum, * bCube, * bTemp, * bVar; + unsigned uCube; + int Value, i, v; + assert( nVars < 16 ); + // start the cover + bSum = Cudd_ReadLogicZero(dd); Cudd_Ref( bSum ); + // check the logic function of the node + Kit_SopForEachCube( cSop, uCube, i ) + { + bCube = Cudd_ReadOne(dd); Cudd_Ref( bCube ); + for ( v = 0; v < nVars; v++ ) + { + Value = ((uCube >> 2*v) & 3); + if ( Value == 1 ) + bVar = Cudd_Not( Cudd_bddIthVar( dd, v ) ); + else if ( Value == 2 ) + bVar = Cudd_bddIthVar( dd, v ); + else + continue; + bCube = Cudd_bddAnd( dd, bTemp = bCube, bVar ); Cudd_Ref( bCube ); + Cudd_RecursiveDeref( dd, bTemp ); + } + bSum = Cudd_bddOr( dd, bTemp = bSum, bCube ); + Cudd_Ref( bSum ); + Cudd_RecursiveDeref( dd, bTemp ); + Cudd_RecursiveDeref( dd, bCube ); + } + // complement the result if necessary + Cudd_Deref( bSum ); + return bSum; +} + +/**Function************************************************************* + + Synopsis [Converts graph to BDD.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Kit_GraphToBdd( DdManager * dd, Kit_Graph_t * pGraph ) +{ + DdNode * bFunc, * bFunc0, * bFunc1; + Kit_Node_t * pNode; + int i; + + // sanity checks + assert( Kit_GraphLeaveNum(pGraph) >= 0 ); + assert( Kit_GraphLeaveNum(pGraph) <= pGraph->nSize ); + + // check for constant function + if ( Kit_GraphIsConst(pGraph) ) + return Cudd_NotCond( b1, Kit_GraphIsComplement(pGraph) ); + // check for a literal + if ( Kit_GraphIsVar(pGraph) ) + return Cudd_NotCond( Cudd_bddIthVar(dd, Kit_GraphVarInt(pGraph)), Kit_GraphIsComplement(pGraph) ); + + // assign the elementary variables + Kit_GraphForEachLeaf( pGraph, pNode, i ) + pNode->pFunc = Cudd_bddIthVar( dd, i ); + + // compute the function for each internal node + Kit_GraphForEachNode( pGraph, pNode, i ) + { + bFunc0 = Cudd_NotCond( Kit_GraphNode(pGraph, pNode->eEdge0.Node)->pFunc, pNode->eEdge0.fCompl ); + bFunc1 = Cudd_NotCond( Kit_GraphNode(pGraph, pNode->eEdge1.Node)->pFunc, pNode->eEdge1.fCompl ); + pNode->pFunc = Cudd_bddAnd( dd, bFunc0, bFunc1 ); Cudd_Ref( pNode->pFunc ); + } + + // deref the intermediate results + bFunc = pNode->pFunc; Cudd_Ref( bFunc ); + Kit_GraphForEachNode( pGraph, pNode, i ) + Cudd_RecursiveDeref( dd, pNode->pFunc ); + Cudd_Deref( bFunc ); + + // complement the result if necessary + return Cudd_NotCond( bFunc, Kit_GraphIsComplement(pGraph) ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Kit_TruthToBdd_rec( DdManager * dd, unsigned * pTruth, int iBit, int nVars, int nVarsTotal, int fMSBonTop ) +{ + DdNode * bF0, * bF1, * bF; + int Var; + if ( nVars <= 5 ) + { + unsigned uTruth, uMask; + uMask = ((~(unsigned)0) >> (32 - (1<>5] >> (iBit&31)) & uMask; + if ( uTruth == 0 ) + return b0; + if ( uTruth == uMask ) + return b1; + } + // find the variable to use + Var = fMSBonTop? nVarsTotal-nVars : nVars-1; + // other special cases can be added + bF0 = Kit_TruthToBdd_rec( dd, pTruth, iBit, nVars-1, nVarsTotal, fMSBonTop ); Cudd_Ref( bF0 ); + bF1 = Kit_TruthToBdd_rec( dd, pTruth, iBit+(1<<(nVars-1)), nVars-1, nVarsTotal, fMSBonTop ); Cudd_Ref( bF1 ); + bF = Cudd_bddIte( dd, dd->vars[Var], bF1, bF0 ); Cudd_Ref( bF ); + Cudd_RecursiveDeref( dd, bF0 ); + Cudd_RecursiveDeref( dd, bF1 ); + Cudd_Deref( bF ); + return bF; +} + +/**Function************************************************************* + + Synopsis [Compute BDD corresponding to the truth table.] + + Description [If truth table has N vars, the BDD depends on N topmost + variables of the BDD manager. The most significant variable of the table + is encoded by the topmost variable of the manager. BDD construction is + efficient in this case because BDD is constructed one node at a time, + by simply adding BDD nodes on top of existent BDD nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Kit_TruthToBdd( DdManager * dd, unsigned * pTruth, int nVars, int fMSBonTop ) +{ + return Kit_TruthToBdd_rec( dd, pTruth, 0, nVars, nVars, fMSBonTop ); +} + +/**Function************************************************************* + + Synopsis [Verifies that the factoring is correct.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_SopFactorVerify( Vec_Int_t * vCover, Kit_Graph_t * pFForm, int nVars ) +{ + static DdManager * dd = NULL; + Kit_Sop_t Sop, * cSop = &Sop; + DdNode * bFunc1, * bFunc2; + Vec_Int_t * vMemory; + int RetValue; + // get the manager + if ( dd == NULL ) + dd = Cudd_Init( 16, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 ); + // derive SOP + vMemory = Vec_IntAlloc( Vec_IntSize(vCover) ); + Kit_SopCreate( cSop, vCover, nVars, vMemory ); + // get the functions + bFunc1 = Kit_SopToBdd( dd, cSop, nVars ); Cudd_Ref( bFunc1 ); + bFunc2 = Kit_GraphToBdd( dd, pFForm ); Cudd_Ref( bFunc2 ); +//Extra_bddPrint( dd, bFunc1 ); printf("\n"); +//Extra_bddPrint( dd, bFunc2 ); printf("\n"); + RetValue = (bFunc1 == bFunc2); + if ( bFunc1 != bFunc2 ) + { + int s; + Extra_bddPrint( dd, bFunc1 ); printf("\n"); + Extra_bddPrint( dd, bFunc2 ); printf("\n"); + s = 0; + } + Cudd_RecursiveDeref( dd, bFunc1 ); + Cudd_RecursiveDeref( dd, bFunc2 ); + Vec_IntFree( vMemory ); + return RetValue; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/kit/kitDsd.c b/abc_with_bb_support/src/aig/kit/kitDsd.c new file mode 100644 index 000000000..124d15763 --- /dev/null +++ b/abc_with_bb_support/src/aig/kit/kitDsd.c @@ -0,0 +1,2185 @@ +/**CFile**************************************************************** + + FileName [kitDsd.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Computation kit.] + + Synopsis [Performs disjoint-support decomposition based on truth tables.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Dec 6, 2006.] + + Revision [$Id: kitDsd.c,v 1.00 2006/12/06 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "kit.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates the DSD manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_DsdMan_t * Kit_DsdManAlloc( int nVars ) +{ + Kit_DsdMan_t * p; + p = ALLOC( Kit_DsdMan_t, 1 ); + memset( p, 0, sizeof(Kit_DsdMan_t) ); + p->nVars = nVars; + p->nWords = Kit_TruthWordNum( p->nVars ); + p->vTtElems = Vec_PtrAllocTruthTables( p->nVars ); + p->vTtNodes = Vec_PtrAllocSimInfo( 1024, p->nWords ); + return p; +} + +/**Function************************************************************* + + Synopsis [Deallocates the DSD manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_DsdManFree( Kit_DsdMan_t * p ) +{ + Vec_PtrFree( p->vTtElems ); + Vec_PtrFree( p->vTtNodes ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Allocates the DSD node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_DsdObj_t * Kit_DsdObjAlloc( Kit_DsdNtk_t * pNtk, Kit_Dsd_t Type, int nFans ) +{ + Kit_DsdObj_t * pObj; + int nSize = sizeof(Kit_DsdObj_t) + sizeof(unsigned) * (Kit_DsdObjOffset(nFans) + (Type == KIT_DSD_PRIME) * Kit_TruthWordNum(nFans)); + pObj = (Kit_DsdObj_t *)ALLOC( char, nSize ); + memset( pObj, 0, nSize ); + pObj->Id = pNtk->nVars + pNtk->nNodes; + pObj->Type = Type; + pObj->nFans = nFans; + pObj->Offset = Kit_DsdObjOffset( nFans ); + // add the object + if ( pNtk->nNodes == pNtk->nNodesAlloc ) + { + pNtk->nNodesAlloc *= 2; + pNtk->pNodes = REALLOC( Kit_DsdObj_t *, pNtk->pNodes, pNtk->nNodesAlloc ); + } + assert( pNtk->nNodes < pNtk->nNodesAlloc ); + pNtk->pNodes[pNtk->nNodes++] = pObj; + return pObj; +} + +/**Function************************************************************* + + Synopsis [Deallocates the DSD node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_DsdObjFree( Kit_DsdNtk_t * p, Kit_DsdObj_t * pObj ) +{ + free( pObj ); +} + +/**Function************************************************************* + + Synopsis [Allocates the DSD network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_DsdNtk_t * Kit_DsdNtkAlloc( int nVars ) +{ + Kit_DsdNtk_t * pNtk; + pNtk = ALLOC( Kit_DsdNtk_t, 1 ); + memset( pNtk, 0, sizeof(Kit_DsdNtk_t) ); + pNtk->pNodes = ALLOC( Kit_DsdObj_t *, nVars ); + pNtk->nVars = nVars; + pNtk->nNodesAlloc = nVars; + pNtk->pMem = ALLOC( unsigned, 6 * Kit_TruthWordNum(nVars) ); + return pNtk; +} + +/**Function************************************************************* + + Synopsis [Deallocate the DSD network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_DsdNtkFree( Kit_DsdNtk_t * pNtk ) +{ + Kit_DsdObj_t * pObj; + unsigned i; + Kit_DsdNtkForEachObj( pNtk, pObj, i ) + free( pObj ); + FREE( pNtk->pSupps ); + free( pNtk->pNodes ); + free( pNtk->pMem ); + free( pNtk ); +} + +/**Function************************************************************* + + Synopsis [Prints the hex unsigned into a file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_DsdPrintHex( FILE * pFile, unsigned * pTruth, int nFans ) +{ + int nDigits, Digit, k; + nDigits = (1 << nFans) / 4; + for ( k = nDigits - 1; k >= 0; k-- ) + { + Digit = ((pTruth[k/8] >> ((k%8) * 4)) & 15); + if ( Digit < 10 ) + fprintf( pFile, "%d", Digit ); + else + fprintf( pFile, "%c", 'A' + Digit-10 ); + } +} + +/**Function************************************************************* + + Synopsis [Recursively print the DSD formula.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_DsdPrint_rec( FILE * pFile, Kit_DsdNtk_t * pNtk, int Id ) +{ + Kit_DsdObj_t * pObj; + unsigned iLit, i; + char Symbol; + + pObj = Kit_DsdNtkObj( pNtk, Id ); + if ( pObj == NULL ) + { + assert( Id < pNtk->nVars ); + fprintf( pFile, "%c", 'a' + Id ); + return; + } + + if ( pObj->Type == KIT_DSD_CONST1 ) + { + assert( pObj->nFans == 0 ); + fprintf( pFile, "Const1" ); + return; + } + + if ( pObj->Type == KIT_DSD_VAR ) + assert( pObj->nFans == 1 ); + + if ( pObj->Type == KIT_DSD_AND ) + Symbol = '*'; + else if ( pObj->Type == KIT_DSD_XOR ) + Symbol = '+'; + else + Symbol = ','; + + if ( pObj->Type == KIT_DSD_PRIME ) + Kit_DsdPrintHex( stdout, Kit_DsdObjTruth(pObj), pObj->nFans ); + + fprintf( pFile, "(" ); + Kit_DsdObjForEachFanin( pNtk, pObj, iLit, i ) + { + if ( Kit_DsdLitIsCompl(iLit) ) + fprintf( pFile, "!" ); + Kit_DsdPrint_rec( pFile, pNtk, Kit_DsdLit2Var(iLit) ); + if ( i < pObj->nFans - 1 ) + fprintf( pFile, "%c", Symbol ); + } + fprintf( pFile, ")" ); +} + +/**Function************************************************************* + + Synopsis [Print the DSD formula.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_DsdPrint( FILE * pFile, Kit_DsdNtk_t * pNtk ) +{ + fprintf( pFile, "F = " ); + if ( Kit_DsdLitIsCompl(pNtk->Root) ) + fprintf( pFile, "!" ); + Kit_DsdPrint_rec( pFile, pNtk, Kit_DsdLit2Var(pNtk->Root) ); + fprintf( pFile, "\n" ); +} + +/**Function************************************************************* + + Synopsis [Print the DSD formula.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_DsdPrintExpanded( Kit_DsdNtk_t * pNtk ) +{ + Kit_DsdNtk_t * pTemp; + pTemp = Kit_DsdExpand( pNtk ); + Kit_DsdPrint( stdout, pTemp ); + Kit_DsdNtkFree( pTemp ); +} + +/**Function************************************************************* + + Synopsis [Print the DSD formula.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_DsdPrintFromTruth( unsigned * pTruth, int nVars ) +{ + Kit_DsdNtk_t * pTemp; + pTemp = Kit_DsdDecomposeMux( pTruth, nVars, 5 ); + Kit_DsdVerify( pTemp, pTruth, nVars ); + Kit_DsdPrintExpanded( pTemp ); + Kit_DsdNtkFree( pTemp ); +} + +/**Function************************************************************* + + Synopsis [Derives the truth table of the DSD node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned * Kit_DsdTruthComputeNode_rec( Kit_DsdMan_t * p, Kit_DsdNtk_t * pNtk, int Id, unsigned uSupp ) +{ + Kit_DsdObj_t * pObj; + unsigned * pTruthRes, * pTruthPrime, * pTruthMint, * pTruthFans[16]; + unsigned i, m, iLit, nMints, fCompl, fPartial = 0; + + // get the node with this ID + pObj = Kit_DsdNtkObj( pNtk, Id ); + pTruthRes = Vec_PtrEntry( p->vTtNodes, Id ); + + // special case: literal of an internal node + if ( pObj == NULL ) + { + assert( Id < pNtk->nVars ); + assert( !uSupp || uSupp != (uSupp & ~(1<Type == KIT_DSD_CONST1 ) + { + assert( pObj->nFans == 0 ); + Kit_TruthFill( pTruthRes, pNtk->nVars ); + return pTruthRes; + } + + // elementary variable node + if ( pObj->Type == KIT_DSD_VAR ) + { + assert( pObj->nFans == 1 ); + iLit = pObj->pFans[0]; + assert( Kit_DsdLitIsLeaf( pNtk, iLit ) ); + pTruthFans[0] = Kit_DsdTruthComputeNode_rec( p, pNtk, Kit_DsdLit2Var(iLit), uSupp ); + if ( Kit_DsdLitIsCompl(iLit) ) + Kit_TruthNot( pTruthRes, pTruthFans[0], pNtk->nVars ); + else + Kit_TruthCopy( pTruthRes, pTruthFans[0], pNtk->nVars ); + return pTruthRes; + } + + // collect the truth tables of the fanins + if ( uSupp ) + { + Kit_DsdObjForEachFanin( pNtk, pObj, iLit, i ) + if ( uSupp != (uSupp & ~Kit_DsdLitSupport(pNtk, iLit)) ) + pTruthFans[i] = Kit_DsdTruthComputeNode_rec( p, pNtk, Kit_DsdLit2Var(iLit), uSupp ); + else + { + pTruthFans[i] = NULL; + fPartial = 1; + } + } + else + { + Kit_DsdObjForEachFanin( pNtk, pObj, iLit, i ) + pTruthFans[i] = Kit_DsdTruthComputeNode_rec( p, pNtk, Kit_DsdLit2Var(iLit), uSupp ); + } + // create the truth table + + // simple gates + if ( pObj->Type == KIT_DSD_AND ) + { + Kit_TruthFill( pTruthRes, pNtk->nVars ); + Kit_DsdObjForEachFanin( pNtk, pObj, iLit, i ) + if ( pTruthFans[i] ) + Kit_TruthAndPhase( pTruthRes, pTruthRes, pTruthFans[i], pNtk->nVars, 0, Kit_DsdLitIsCompl(iLit) ); + return pTruthRes; + } + if ( pObj->Type == KIT_DSD_XOR ) + { + Kit_TruthClear( pTruthRes, pNtk->nVars ); + fCompl = 0; + Kit_DsdObjForEachFanin( pNtk, pObj, iLit, i ) + { + if ( pTruthFans[i] ) + { + Kit_TruthXor( pTruthRes, pTruthRes, pTruthFans[i], pNtk->nVars ); + fCompl ^= Kit_DsdLitIsCompl(iLit); + } + } + if ( fCompl ) + Kit_TruthNot( pTruthRes, pTruthRes, pNtk->nVars ); + return pTruthRes; + } + assert( pObj->Type == KIT_DSD_PRIME ); + + if ( uSupp && fPartial ) + { + // find the only non-empty component + Kit_DsdObjForEachFanin( pNtk, pObj, iLit, i ) + if ( pTruthFans[i] ) + break; + assert( i < pObj->nFans ); + return pTruthFans[i]; + } + + // get the truth table of the prime node + pTruthPrime = Kit_DsdObjTruth( pObj ); + // get storage for the temporary minterm + pTruthMint = Vec_PtrEntry(p->vTtNodes, pNtk->nVars + pNtk->nNodes); + + // go through the minterms + nMints = (1 << pObj->nFans); + Kit_TruthClear( pTruthRes, pNtk->nVars ); + for ( m = 0; m < nMints; m++ ) + { + if ( !Kit_TruthHasBit(pTruthPrime, m) ) + continue; + Kit_TruthFill( pTruthMint, pNtk->nVars ); + Kit_DsdObjForEachFanin( pNtk, pObj, iLit, i ) + Kit_TruthAndPhase( pTruthMint, pTruthMint, pTruthFans[i], pNtk->nVars, 0, ((m & (1<nVars ); + } + return pTruthRes; +} + +/**Function************************************************************* + + Synopsis [Derives the truth table of the DSD network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned * Kit_DsdTruthCompute( Kit_DsdMan_t * p, Kit_DsdNtk_t * pNtk, unsigned uSupp ) +{ + unsigned * pTruthRes; + int i; + // if support is specified, request that supports are available + if ( uSupp ) + Kit_DsdGetSupports( pNtk ); + // assign elementary truth tables + assert( pNtk->nVars <= p->nVars ); + for ( i = 0; i < (int)pNtk->nVars; i++ ) + Kit_TruthCopy( Vec_PtrEntry(p->vTtNodes, i), Vec_PtrEntry(p->vTtElems, i), p->nVars ); + // compute truth table for each node + pTruthRes = Kit_DsdTruthComputeNode_rec( p, pNtk, Kit_DsdLit2Var(pNtk->Root), uSupp ); + // complement the truth table if needed + if ( Kit_DsdLitIsCompl(pNtk->Root) ) + Kit_TruthNot( pTruthRes, pTruthRes, pNtk->nVars ); + return pTruthRes; +} + +/**Function************************************************************* + + Synopsis [Derives the truth table of the DSD network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_DsdTruth( Kit_DsdNtk_t * pNtk, unsigned * pTruthRes ) +{ + Kit_DsdMan_t * p; + unsigned * pTruth; + p = Kit_DsdManAlloc( pNtk->nVars ); + pTruth = Kit_DsdTruthCompute( p, pNtk, 0 ); + Kit_TruthCopy( pTruthRes, pTruth, pNtk->nVars ); + Kit_DsdManFree( p ); +} + +/**Function************************************************************* + + Synopsis [Derives the truth table of the DSD network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_DsdTruthPartial( Kit_DsdMan_t * p, Kit_DsdNtk_t * pNtk, unsigned * pTruthRes, unsigned uSupp ) +{ + unsigned * pTruth = Kit_DsdTruthCompute( p, pNtk, uSupp ); + Kit_TruthCopy( pTruthRes, pTruth, pNtk->nVars ); +} + +/**Function************************************************************* + + Synopsis [Counts the number of blocks of the given number of inputs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_DsdCountLuts_rec( Kit_DsdNtk_t * pNtk, int nLutSize, int Id, int * pCounter ) +{ + Kit_DsdObj_t * pObj; + unsigned iLit, i, Res0, Res1; + pObj = Kit_DsdNtkObj( pNtk, Id ); + if ( pObj == NULL ) + return 0; + if ( pObj->Type == KIT_DSD_AND || pObj->Type == KIT_DSD_XOR ) + { + assert( pObj->nFans == 2 ); + Res0 = Kit_DsdCountLuts_rec( pNtk, nLutSize, Kit_DsdLit2Var(pObj->pFans[0]), pCounter ); + Res1 = Kit_DsdCountLuts_rec( pNtk, nLutSize, Kit_DsdLit2Var(pObj->pFans[1]), pCounter ); + if ( Res0 == 0 && Res1 > 0 ) + return Res1 - 1; + if ( Res0 > 0 && Res1 == 0 ) + return Res0 - 1; + (*pCounter)++; + return nLutSize - 2; + } + assert( pObj->Type == KIT_DSD_PRIME ); + if ( (int)pObj->nFans > nLutSize ) //+ 1 ) + { + *pCounter = 1000; + return 0; + } + Kit_DsdObjForEachFanin( pNtk, pObj, iLit, i ) + Kit_DsdCountLuts_rec( pNtk, nLutSize, Kit_DsdLit2Var(iLit), pCounter ); + (*pCounter)++; +// if ( (int)pObj->nFans == nLutSize + 1 ) +// (*pCounter)++; + return nLutSize - pObj->nFans; +} + +/**Function************************************************************* + + Synopsis [Counts the number of blocks of the given number of inputs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_DsdCountLuts( Kit_DsdNtk_t * pNtk, int nLutSize ) +{ + int Counter = 0; + if ( Kit_DsdNtkRoot(pNtk)->Type == KIT_DSD_CONST1 ) + return 0; + if ( Kit_DsdNtkRoot(pNtk)->Type == KIT_DSD_VAR ) + return 0; + Kit_DsdCountLuts_rec( pNtk, nLutSize, Kit_DsdLit2Var(pNtk->Root), &Counter ); + if ( Counter >= 1000 ) + return -1; + return Counter; +} + +/**Function************************************************************* + + Synopsis [Counts the number of blocks of the given number of inputs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_DsdNonDsdSizeMax( Kit_DsdNtk_t * pNtk ) +{ + Kit_DsdObj_t * pObj; + unsigned i, nSizeMax = 0; + Kit_DsdNtkForEachObj( pNtk, pObj, i ) + { + if ( pObj->Type != KIT_DSD_PRIME ) + continue; + if ( nSizeMax < pObj->nFans ) + nSizeMax = pObj->nFans; + } + return nSizeMax; +} + + +/**Function************************************************************* + + Synopsis [Expands the node.] + + Description [Returns the new literal.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_DsdExpandCollectAnd_rec( Kit_DsdNtk_t * p, int iLit, int * piLitsNew, int * nLitsNew ) +{ + Kit_DsdObj_t * pObj; + unsigned i, iLitFanin; + // check the end of the supergate + pObj = Kit_DsdNtkObj( p, Kit_DsdLit2Var(iLit) ); + if ( Kit_DsdLitIsCompl(iLit) || Kit_DsdLit2Var(iLit) < p->nVars || pObj->Type != KIT_DSD_AND ) + { + piLitsNew[(*nLitsNew)++] = iLit; + return; + } + // iterate through the fanins + Kit_DsdObjForEachFanin( p, pObj, iLitFanin, i ) + Kit_DsdExpandCollectAnd_rec( p, iLitFanin, piLitsNew, nLitsNew ); +} + +/**Function************************************************************* + + Synopsis [Expands the node.] + + Description [Returns the new literal.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_DsdExpandCollectXor_rec( Kit_DsdNtk_t * p, int iLit, int * piLitsNew, int * nLitsNew ) +{ + Kit_DsdObj_t * pObj; + unsigned i, iLitFanin; + // check the end of the supergate + pObj = Kit_DsdNtkObj( p, Kit_DsdLit2Var(iLit) ); + if ( Kit_DsdLit2Var(iLit) < p->nVars || pObj->Type != KIT_DSD_XOR ) + { + piLitsNew[(*nLitsNew)++] = iLit; + return; + } + // iterate through the fanins + pObj = Kit_DsdNtkObj( p, Kit_DsdLit2Var(iLit) ); + Kit_DsdObjForEachFanin( p, pObj, iLitFanin, i ) + Kit_DsdExpandCollectXor_rec( p, iLitFanin, piLitsNew, nLitsNew ); + // if the literal was complemented, pass the complemented attribute somewhere + if ( Kit_DsdLitIsCompl(iLit) ) + piLitsNew[0] = Kit_DsdLitNot( piLitsNew[0] ); +} + +/**Function************************************************************* + + Synopsis [Expands the node.] + + Description [Returns the new literal.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_DsdExpandNode_rec( Kit_DsdNtk_t * pNew, Kit_DsdNtk_t * p, int iLit ) +{ + unsigned * pTruth, * pTruthNew; + unsigned i, iLitFanin, piLitsNew[16], nLitsNew = 0; + Kit_DsdObj_t * pObj, * pObjNew; + + // consider the case of simple gate + pObj = Kit_DsdNtkObj( p, Kit_DsdLit2Var(iLit) ); + if ( pObj == NULL ) + return iLit; + if ( pObj->Type == KIT_DSD_AND ) + { + Kit_DsdExpandCollectAnd_rec( p, Kit_DsdLitRegular(iLit), piLitsNew, &nLitsNew ); + pObjNew = Kit_DsdObjAlloc( pNew, KIT_DSD_AND, nLitsNew ); + for ( i = 0; i < pObjNew->nFans; i++ ) + pObjNew->pFans[i] = Kit_DsdExpandNode_rec( pNew, p, piLitsNew[i] ); + return Kit_DsdVar2Lit( pObjNew->Id, Kit_DsdLitIsCompl(iLit) ); + } + if ( pObj->Type == KIT_DSD_XOR ) + { + int fCompl = Kit_DsdLitIsCompl(iLit); + Kit_DsdExpandCollectXor_rec( p, Kit_DsdLitRegular(iLit), piLitsNew, &nLitsNew ); + pObjNew = Kit_DsdObjAlloc( pNew, KIT_DSD_XOR, nLitsNew ); + for ( i = 0; i < pObjNew->nFans; i++ ) + { + pObjNew->pFans[i] = Kit_DsdExpandNode_rec( pNew, p, Kit_DsdLitRegular(piLitsNew[i]) ); + fCompl ^= Kit_DsdLitIsCompl(piLitsNew[i]); + } + return Kit_DsdVar2Lit( pObjNew->Id, fCompl ); + } + assert( pObj->Type == KIT_DSD_PRIME ); + + // create new PRIME node + pObjNew = Kit_DsdObjAlloc( pNew, KIT_DSD_PRIME, pObj->nFans ); + // copy the truth table + pTruth = Kit_DsdObjTruth( pObj ); + pTruthNew = Kit_DsdObjTruth( pObjNew ); + Kit_TruthCopy( pTruthNew, pTruth, pObj->nFans ); + // create fanins + Kit_DsdObjForEachFanin( pNtk, pObj, iLitFanin, i ) + { + pObjNew->pFans[i] = Kit_DsdExpandNode_rec( pNew, p, iLitFanin ); + // complement the corresponding inputs of the truth table + if ( Kit_DsdLitIsCompl(pObjNew->pFans[i]) ) + { + pObjNew->pFans[i] = Kit_DsdLitRegular(pObjNew->pFans[i]); + Kit_TruthChangePhase( pTruthNew, pObjNew->nFans, i ); + } + } + // if the incoming phase is complemented, absorb it into the prime node + if ( Kit_DsdLitIsCompl(iLit) ) + Kit_TruthNot( pTruthNew, pTruthNew, pObj->nFans ); + return Kit_DsdVar2Lit( pObjNew->Id, 0 ); +} + +/**Function************************************************************* + + Synopsis [Expands the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_DsdNtk_t * Kit_DsdExpand( Kit_DsdNtk_t * p ) +{ + Kit_DsdNtk_t * pNew; + Kit_DsdObj_t * pObjNew; + assert( p->nVars <= 16 ); + // create a new network + pNew = Kit_DsdNtkAlloc( p->nVars ); + // consider simple special cases + if ( Kit_DsdNtkRoot(p)->Type == KIT_DSD_CONST1 ) + { + pObjNew = Kit_DsdObjAlloc( pNew, KIT_DSD_CONST1, 0 ); + pNew->Root = Kit_DsdVar2Lit( pObjNew->Id, Kit_DsdLitIsCompl(p->Root) ); + return pNew; + } + if ( Kit_DsdNtkRoot(p)->Type == KIT_DSD_VAR ) + { + pObjNew = Kit_DsdObjAlloc( pNew, KIT_DSD_VAR, 1 ); + pObjNew->pFans[0] = Kit_DsdNtkRoot(p)->pFans[0]; + pNew->Root = Kit_DsdVar2Lit( pObjNew->Id, Kit_DsdLitIsCompl(p->Root) ); + return pNew; + } + // convert the root node + pNew->Root = Kit_DsdExpandNode_rec( pNew, p, p->Root ); + return pNew; +} + +/**Function************************************************************* + + Synopsis [Sorts the literals by their support.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_DsdCompSort( int pPrios[], unsigned uSupps[], unsigned char * piLits, int nVars, int piLitsRes[] ) +{ + int nSuppSizes[16], Priority[16], pOrder[16]; + int i, k, iVarBest, SuppMax, PrioMax; + // compute support sizes and priorities of the components + for ( i = 0; i < nVars; i++ ) + { + assert( uSupps[i] ); + pOrder[i] = i; + Priority[i] = KIT_INFINITY; + for ( k = 0; k < 16; k++ ) + if ( uSupps[i] & (1 << k) ) + Priority[i] = KIT_MIN( Priority[i], pPrios[k] ); + assert( Priority[i] != 16 ); + nSuppSizes[i] = Kit_WordCountOnes(uSupps[i]); + } + // sort the components by pririty + Extra_BubbleSort( pOrder, Priority, nVars, 0 ); + // find the component by with largest size and lowest priority + iVarBest = -1; + SuppMax = 0; + PrioMax = 0; + for ( i = 0; i < nVars; i++ ) + { + if ( SuppMax < nSuppSizes[i] || (SuppMax == nSuppSizes[i] && PrioMax < Priority[i]) ) + { + SuppMax = nSuppSizes[i]; + PrioMax = Priority[i]; + iVarBest = i; + } + } + assert( iVarBest != -1 ); + // copy the resulting literals + k = 0; + piLitsRes[k++] = piLits[iVarBest]; + for ( i = 0; i < nVars; i++ ) + { + if ( pOrder[i] == iVarBest ) + continue; + piLitsRes[k++] = piLits[pOrder[i]]; + } + assert( k == nVars ); +} + +/**Function************************************************************* + + Synopsis [Shrinks multi-input nodes.] + + Description [Takes the array of variable priorities pPrios.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_DsdShrink_rec( Kit_DsdNtk_t * pNew, Kit_DsdNtk_t * p, int iLit, int pPrios[] ) +{ + Kit_DsdObj_t * pObj, * pObjNew; + unsigned * pTruth, * pTruthNew; + unsigned i, piLitsNew[16], uSupps[16]; + int iLitFanin, iLitNew; + + // consider the case of simple gate + pObj = Kit_DsdNtkObj( p, Kit_DsdLit2Var(iLit) ); + if ( pObj == NULL ) + return iLit; + if ( pObj->Type == KIT_DSD_AND ) + { + // get the supports + Kit_DsdObjForEachFanin( p, pObj, iLitFanin, i ) + uSupps[i] = Kit_DsdLitSupport( p, iLitFanin ); + // put the largest component last + // sort other components in the decreasing order of priority of their vars + Kit_DsdCompSort( pPrios, uSupps, pObj->pFans, pObj->nFans, piLitsNew ); + // construct the two-input node network + iLitNew = Kit_DsdShrink_rec( pNew, p, piLitsNew[0], pPrios ); + for ( i = 1; i < pObj->nFans; i++ ) + { + pObjNew = Kit_DsdObjAlloc( pNew, KIT_DSD_AND, 2 ); + pObjNew->pFans[0] = Kit_DsdShrink_rec( pNew, p, piLitsNew[i], pPrios ); + pObjNew->pFans[1] = iLitNew; + iLitNew = Kit_DsdVar2Lit( pObjNew->Id, 0 ); + } + return Kit_DsdVar2Lit( pObjNew->Id, Kit_DsdLitIsCompl(iLit) ); + } + if ( pObj->Type == KIT_DSD_XOR ) + { + // get the supports + Kit_DsdObjForEachFanin( p, pObj, iLitFanin, i ) + { + assert( !Kit_DsdLitIsCompl(iLitFanin) ); + uSupps[i] = Kit_DsdLitSupport( p, iLitFanin ); + } + // put the largest component last + // sort other components in the decreasing order of priority of their vars + Kit_DsdCompSort( pPrios, uSupps, pObj->pFans, pObj->nFans, piLitsNew ); + // construct the two-input node network + iLitNew = Kit_DsdShrink_rec( pNew, p, piLitsNew[0], pPrios ); + for ( i = 1; i < pObj->nFans; i++ ) + { + pObjNew = Kit_DsdObjAlloc( pNew, KIT_DSD_XOR, 2 ); + pObjNew->pFans[0] = Kit_DsdShrink_rec( pNew, p, piLitsNew[i], pPrios ); + pObjNew->pFans[1] = iLitNew; + iLitNew = Kit_DsdVar2Lit( pObjNew->Id, 0 ); + } + return Kit_DsdVar2Lit( pObjNew->Id, Kit_DsdLitIsCompl(iLit) ); + } + assert( pObj->Type == KIT_DSD_PRIME ); + + // create new PRIME node + pObjNew = Kit_DsdObjAlloc( pNew, KIT_DSD_PRIME, pObj->nFans ); + // copy the truth table + pTruth = Kit_DsdObjTruth( pObj ); + pTruthNew = Kit_DsdObjTruth( pObjNew ); + Kit_TruthCopy( pTruthNew, pTruth, pObj->nFans ); + // create fanins + Kit_DsdObjForEachFanin( pNtk, pObj, iLitFanin, i ) + { + pObjNew->pFans[i] = Kit_DsdShrink_rec( pNew, p, iLitFanin, pPrios ); + // complement the corresponding inputs of the truth table + if ( Kit_DsdLitIsCompl(pObjNew->pFans[i]) ) + { + pObjNew->pFans[i] = Kit_DsdLitRegular(pObjNew->pFans[i]); + Kit_TruthChangePhase( pTruthNew, pObjNew->nFans, i ); + } + } + // if the incoming phase is complemented, absorb it into the prime node + if ( Kit_DsdLitIsCompl(iLit) ) + Kit_TruthNot( pTruthNew, pTruthNew, pObj->nFans ); + return Kit_DsdVar2Lit( pObjNew->Id, 0 ); +} + +/**Function************************************************************* + + Synopsis [Shrinks the network.] + + Description [Transforms the network to have two-input nodes so that the + higher-ordered nodes were decomposed out first.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_DsdNtk_t * Kit_DsdShrink( Kit_DsdNtk_t * p, int pPrios[] ) +{ + Kit_DsdNtk_t * pNew; + Kit_DsdObj_t * pObjNew; + assert( p->nVars <= 16 ); + // create a new network + pNew = Kit_DsdNtkAlloc( p->nVars ); + // consider simple special cases + if ( Kit_DsdNtkRoot(p)->Type == KIT_DSD_CONST1 ) + { + pObjNew = Kit_DsdObjAlloc( pNew, KIT_DSD_CONST1, 0 ); + pNew->Root = Kit_DsdVar2Lit( pObjNew->Id, Kit_DsdLitIsCompl(p->Root) ); + return pNew; + } + if ( Kit_DsdNtkRoot(p)->Type == KIT_DSD_VAR ) + { + pObjNew = Kit_DsdObjAlloc( pNew, KIT_DSD_VAR, 1 ); + pObjNew->pFans[0] = Kit_DsdNtkRoot(p)->pFans[0]; + pNew->Root = Kit_DsdVar2Lit( pObjNew->Id, Kit_DsdLitIsCompl(p->Root) ); + return pNew; + } + // convert the root node + pNew->Root = Kit_DsdShrink_rec( pNew, p, p->Root, pPrios ); + return pNew; +} + +/**Function************************************************************* + + Synopsis [Rotates the network.] + + Description [Transforms prime nodes to have the fanin with the + highest frequency of supports go first.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_DsdRotate( Kit_DsdNtk_t * p, int pFreqs[] ) +{ + Kit_DsdObj_t * pObj; + unsigned * pIn, * pOut, * pTemp, k; + int i, v, Temp, uSuppFanin, iFaninLit, WeightMax, FaninMax, nSwaps; + int Weights[16]; + // go through the prime nodes + Kit_DsdNtkForEachObj( p, pObj, i ) + { + if ( pObj->Type != KIT_DSD_PRIME ) + continue; + // count the fanin frequencies + Kit_DsdObjForEachFanin( p, pObj, iFaninLit, k ) + { + uSuppFanin = Kit_DsdLitSupport( p, iFaninLit ); + Weights[k] = 0; + for ( v = 0; v < 16; v++ ) + if ( uSuppFanin & (1 << v) ) + Weights[k] += pFreqs[v] - 1; + } + // find the most frequent fanin + WeightMax = 0; + FaninMax = -1; + for ( k = 0; k < pObj->nFans; k++ ) + if ( WeightMax < Weights[k] ) + { + WeightMax = Weights[k]; + FaninMax = k; + } + // no need to reorder if there are no frequent fanins + if ( FaninMax == -1 ) + continue; + // move the fanins number k to the first place + nSwaps = 0; + pIn = Kit_DsdObjTruth(pObj); + pOut = p->pMem; +// for ( v = FaninMax; v < ((int)pObj->nFans)-1; v++ ) + for ( v = FaninMax-1; v >= 0; v-- ) + { + // swap the fanins + Temp = pObj->pFans[v]; + pObj->pFans[v] = pObj->pFans[v+1]; + pObj->pFans[v+1] = Temp; + // swap the truth table variables + Kit_TruthSwapAdjacentVars( pOut, pIn, pObj->nFans, v ); + pTemp = pIn; pIn = pOut; pOut = pTemp; + nSwaps++; + } + if ( nSwaps & 1 ) + Kit_TruthCopy( pOut, pIn, pObj->nFans ); + } +} + +/**Function************************************************************* + + Synopsis [Compute the support.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Kit_DsdGetSupports_rec( Kit_DsdNtk_t * p, int iLit ) +{ + Kit_DsdObj_t * pObj; + unsigned uSupport, k; + int iFaninLit; + pObj = Kit_DsdNtkObj( p, Kit_DsdLit2Var(iLit) ); + if ( pObj == NULL ) + return Kit_DsdLitSupport( p, iLit ); + uSupport = 0; + Kit_DsdObjForEachFanin( p, pObj, iFaninLit, k ) + uSupport |= Kit_DsdGetSupports_rec( p, iFaninLit ); + p->pSupps[pObj->Id - p->nVars] = uSupport; + assert( uSupport <= 0xFFFF ); + return uSupport; +} + +/**Function************************************************************* + + Synopsis [Compute the support.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_DsdGetSupports( Kit_DsdNtk_t * p ) +{ + Kit_DsdObj_t * pRoot; + assert( p->pSupps == NULL ); + p->pSupps = ALLOC( unsigned, p->nNodes ); + // consider simple special cases + pRoot = Kit_DsdNtkRoot(p); + if ( pRoot->Type == KIT_DSD_CONST1 ) + { + assert( p->nNodes == 1 ); + p->pSupps[0] = 0; + } + if ( pRoot->Type == KIT_DSD_VAR ) + { + assert( p->nNodes == 1 ); + p->pSupps[0] = Kit_DsdLitSupport( p, pRoot->pFans[0] ); + } + else + Kit_DsdGetSupports_rec( p, p->Root ); + assert( p->pSupps[0] <= 0xFFFF ); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if there is a component with more than 3 inputs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_DsdFindLargeBox_rec( Kit_DsdNtk_t * pNtk, int Id, int Size ) +{ + Kit_DsdObj_t * pObj; + unsigned iLit, i, RetValue; + pObj = Kit_DsdNtkObj( pNtk, Id ); + if ( pObj == NULL ) + return 0; + if ( pObj->Type == KIT_DSD_PRIME && (int)pObj->nFans > Size ) + return 1; + RetValue = 0; + Kit_DsdObjForEachFanin( pNtk, pObj, iLit, i ) + RetValue |= Kit_DsdFindLargeBox_rec( pNtk, Kit_DsdLit2Var(iLit), Size ); + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if there is a component with more than 3 inputs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_DsdFindLargeBox( Kit_DsdNtk_t * pNtk, int Size ) +{ + return Kit_DsdFindLargeBox_rec( pNtk, Kit_DsdLit2Var(pNtk->Root), Size ); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the non-DSD 4-var func is implementable with two 3-LUTs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_DsdRootNodeHasCommonVars( Kit_DsdObj_t * pObj0, Kit_DsdObj_t * pObj1 ) +{ + unsigned i, k; + for ( i = 0; i < pObj0->nFans; i++ ) + { + if ( Kit_DsdLit2Var(pObj0->pFans[i]) >= 4 ) + continue; + for ( k = 0; k < pObj1->nFans; k++ ) + if ( Kit_DsdLit2Var(pObj0->pFans[i]) == Kit_DsdLit2Var(pObj1->pFans[k]) ) + return 1; + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the non-DSD 4-var func is implementable with two 3-LUTs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_DsdCheckVar4Dec2( Kit_DsdNtk_t * pNtk0, Kit_DsdNtk_t * pNtk1 ) +{ + assert( pNtk0->nVars == 4 ); + assert( pNtk1->nVars == 4 ); + if ( Kit_DsdFindLargeBox(pNtk0, 2) ) + return 0; + if ( Kit_DsdFindLargeBox(pNtk1, 2) ) + return 0; + return Kit_DsdRootNodeHasCommonVars( Kit_DsdNtkRoot(pNtk0), Kit_DsdNtkRoot(pNtk1) ); +} + +/**Function************************************************************* + + Synopsis [Performs decomposition of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_DsdDecompose_rec( Kit_DsdNtk_t * pNtk, Kit_DsdObj_t * pObj, unsigned uSupp, unsigned char * pPar, int nDecMux ) +{ + Kit_DsdObj_t * pRes, * pRes0, * pRes1; + int nWords = Kit_TruthWordNum(pObj->nFans); + unsigned * pTruth = Kit_DsdObjTruth(pObj); + unsigned * pCofs2[2] = { pNtk->pMem, pNtk->pMem + nWords }; + unsigned * pCofs4[2][2] = { {pNtk->pMem + 2 * nWords, pNtk->pMem + 3 * nWords}, {pNtk->pMem + 4 * nWords, pNtk->pMem + 5 * nWords} }; + int i, iLit0, iLit1, nFans0, nFans1, nPairs; + int fEquals[2][2], fOppos, fPairs[4][4]; + unsigned j, k, nFansNew, uSupp0, uSupp1; + + assert( pObj->nFans > 0 ); + assert( pObj->Type == KIT_DSD_PRIME ); + assert( uSupp == (uSupp0 = (unsigned)Kit_TruthSupport(pTruth, pObj->nFans)) ); + + // compress the truth table + if ( uSupp != Kit_BitMask(pObj->nFans) ) + { + nFansNew = Kit_WordCountOnes(uSupp); + Kit_TruthShrink( pNtk->pMem, pTruth, nFansNew, pObj->nFans, uSupp, 1 ); + for ( j = k = 0; j < pObj->nFans; j++ ) + if ( uSupp & (1 << j) ) + pObj->pFans[k++] = pObj->pFans[j]; + assert( k == nFansNew ); + pObj->nFans = k; + uSupp = Kit_BitMask(pObj->nFans); + } + + // consider the single variable case + if ( pObj->nFans == 1 ) + { + pObj->Type = KIT_DSD_NONE; + if ( pTruth[0] == 0x55555555 ) + pObj->pFans[0] = Kit_DsdLitNot(pObj->pFans[0]); + else + assert( pTruth[0] == 0xAAAAAAAA ); + // update the parent pointer + *pPar = Kit_DsdLitNotCond( pObj->pFans[0], Kit_DsdLitIsCompl(*pPar) ); + return; + } + + // decompose the output + if ( !pObj->fMark ) + for ( i = pObj->nFans - 1; i >= 0; i-- ) + { + // get the two-variable cofactors + Kit_TruthCofactor0New( pCofs2[0], pTruth, pObj->nFans, i ); + Kit_TruthCofactor1New( pCofs2[1], pTruth, pObj->nFans, i ); +// assert( !Kit_TruthVarInSupport( pCofs2[0], pObj->nFans, i) ); +// assert( !Kit_TruthVarInSupport( pCofs2[1], pObj->nFans, i) ); + // get the constant cofs + fEquals[0][0] = Kit_TruthIsConst0( pCofs2[0], pObj->nFans ); + fEquals[0][1] = Kit_TruthIsConst0( pCofs2[1], pObj->nFans ); + fEquals[1][0] = Kit_TruthIsConst1( pCofs2[0], pObj->nFans ); + fEquals[1][1] = Kit_TruthIsConst1( pCofs2[1], pObj->nFans ); + fOppos = Kit_TruthIsOpposite( pCofs2[0], pCofs2[1], pObj->nFans ); + assert( !Kit_TruthIsEqual(pCofs2[0], pCofs2[1], pObj->nFans) ); + if ( fEquals[0][0] + fEquals[0][1] + fEquals[1][0] + fEquals[1][1] + fOppos == 0 ) + { + // check the MUX decomposition + uSupp0 = Kit_TruthSupport( pCofs2[0], pObj->nFans ); + uSupp1 = Kit_TruthSupport( pCofs2[1], pObj->nFans ); + assert( uSupp == (uSupp0 | uSupp1 | (1<nFans ); + pRes1 = Kit_DsdObjAlloc( pNtk, KIT_DSD_PRIME, pObj->nFans ); + for ( k = 0; k < pObj->nFans; k++ ) + { + pRes0->pFans[k] = (uSupp0 & (1 << k))? pObj->pFans[k] : 127; + pRes1->pFans[k] = (uSupp1 & (1 << k))? pObj->pFans[k] : 127; + } + Kit_TruthCopy( Kit_DsdObjTruth(pRes0), pCofs2[0], pObj->nFans ); + Kit_TruthCopy( Kit_DsdObjTruth(pRes1), pCofs2[1], pObj->nFans ); + // update the current one + assert( pObj->Type == KIT_DSD_PRIME ); + pTruth[0] = 0xCACACACA; + pObj->nFans = 3; + pObj->pFans[2] = pObj->pFans[i]; + pObj->pFans[0] = 2*pRes0->Id; pRes0->nRefs++; + pObj->pFans[1] = 2*pRes1->Id; pRes1->nRefs++; + // call recursively + Kit_DsdDecompose_rec( pNtk, pRes0, uSupp0, pObj->pFans + 0, nDecMux ); + Kit_DsdDecompose_rec( pNtk, pRes1, uSupp1, pObj->pFans + 1, nDecMux ); + return; + } + + // create the new node + pRes = Kit_DsdObjAlloc( pNtk, KIT_DSD_AND, 2 ); + pRes->nRefs++; + pRes->nFans = 2; + pRes->pFans[0] = pObj->pFans[i]; pObj->pFans[i] = 127; uSupp &= ~(1 << i); + pRes->pFans[1] = 2*pObj->Id; + // update the parent pointer + *pPar = Kit_DsdLitNotCond( 2 * pRes->Id, Kit_DsdLitIsCompl(*pPar) ); + // consider different decompositions + if ( fEquals[0][0] ) + { + Kit_TruthCopy( pTruth, pCofs2[1], pObj->nFans ); + } + else if ( fEquals[0][1] ) + { + pRes->pFans[0] = Kit_DsdLitNot(pRes->pFans[0]); + Kit_TruthCopy( pTruth, pCofs2[0], pObj->nFans ); + } + else if ( fEquals[1][0] ) + { + *pPar = Kit_DsdLitNot(*pPar); + pRes->pFans[1] = Kit_DsdLitNot(pRes->pFans[1]); + Kit_TruthCopy( pTruth, pCofs2[1], pObj->nFans ); + } + else if ( fEquals[1][1] ) + { + *pPar = Kit_DsdLitNot(*pPar); + pRes->pFans[0] = Kit_DsdLitNot(pRes->pFans[0]); + pRes->pFans[1] = Kit_DsdLitNot(pRes->pFans[1]); + Kit_TruthCopy( pTruth, pCofs2[0], pObj->nFans ); + } + else if ( fOppos ) + { + pRes->Type = KIT_DSD_XOR; + Kit_TruthCopy( pTruth, pCofs2[0], pObj->nFans ); + } + else + assert( 0 ); + // decompose the remainder + assert( Kit_DsdObjTruth(pObj) == pTruth ); + Kit_DsdDecompose_rec( pNtk, pObj, uSupp, pRes->pFans + 1, nDecMux ); + return; + } + pObj->fMark = 1; + + // decompose the input + for ( i = pObj->nFans - 1; i >= 0; i-- ) + { + assert( Kit_TruthVarInSupport( pTruth, pObj->nFans, i ) ); + // get the single variale cofactors + Kit_TruthCofactor0New( pCofs2[0], pTruth, pObj->nFans, i ); + Kit_TruthCofactor1New( pCofs2[1], pTruth, pObj->nFans, i ); + // check the existence of MUX decomposition + uSupp0 = Kit_TruthSupport( pCofs2[0], pObj->nFans ); + uSupp1 = Kit_TruthSupport( pCofs2[1], pObj->nFans ); + assert( uSupp == (uSupp0 | uSupp1 | (1<fMark = 0; + Kit_DsdDecompose_rec( pNtk, pObj, uSupp, pPar, nDecMux ); + return; + } + assert( uSupp0 && uSupp1 ); + // get the number of unique variables + nFans0 = Kit_WordCountOnes( uSupp0 & ~uSupp1 ); + nFans1 = Kit_WordCountOnes( uSupp1 & ~uSupp0 ); + if ( nFans0 == 1 && nFans1 == 1 ) + { + // get the cofactors w.r.t. the unique variables + iLit0 = Kit_WordFindFirstBit( uSupp0 & ~uSupp1 ); + iLit1 = Kit_WordFindFirstBit( uSupp1 & ~uSupp0 ); + // get four cofactors + Kit_TruthCofactor0New( pCofs4[0][0], pCofs2[0], pObj->nFans, iLit0 ); + Kit_TruthCofactor1New( pCofs4[0][1], pCofs2[0], pObj->nFans, iLit0 ); + Kit_TruthCofactor0New( pCofs4[1][0], pCofs2[1], pObj->nFans, iLit1 ); + Kit_TruthCofactor1New( pCofs4[1][1], pCofs2[1], pObj->nFans, iLit1 ); + // check existence conditions + fEquals[0][0] = Kit_TruthIsEqual( pCofs4[0][0], pCofs4[1][0], pObj->nFans ); + fEquals[0][1] = Kit_TruthIsEqual( pCofs4[0][1], pCofs4[1][1], pObj->nFans ); + fEquals[1][0] = Kit_TruthIsEqual( pCofs4[0][0], pCofs4[1][1], pObj->nFans ); + fEquals[1][1] = Kit_TruthIsEqual( pCofs4[0][1], pCofs4[1][0], pObj->nFans ); + if ( (fEquals[0][0] && fEquals[0][1]) || (fEquals[1][0] && fEquals[1][1]) ) + { + // construct the MUX + pRes = Kit_DsdObjAlloc( pNtk, KIT_DSD_PRIME, 3 ); + Kit_DsdObjTruth(pRes)[0] = 0xCACACACA; + pRes->nRefs++; + pRes->nFans = 3; + pRes->pFans[0] = pObj->pFans[iLit0]; pObj->pFans[iLit0] = 127; uSupp &= ~(1 << iLit0); + pRes->pFans[1] = pObj->pFans[iLit1]; pObj->pFans[iLit1] = 127; uSupp &= ~(1 << iLit1); + pRes->pFans[2] = pObj->pFans[i]; pObj->pFans[i] = 2 * pRes->Id; // remains in support + // update the node +// if ( fEquals[0][0] && fEquals[0][1] ) +// Kit_TruthMuxVar( pTruth, pCofs4[0][0], pCofs4[0][1], pObj->nFans, i ); +// else +// Kit_TruthMuxVar( pTruth, pCofs4[0][1], pCofs4[0][0], pObj->nFans, i ); + Kit_TruthMuxVar( pTruth, pCofs4[1][0], pCofs4[1][1], pObj->nFans, i ); + if ( fEquals[1][0] && fEquals[1][1] ) + pRes->pFans[0] = Kit_DsdLitNot(pRes->pFans[0]); + // decompose the remainder + Kit_DsdDecompose_rec( pNtk, pObj, uSupp, pPar, nDecMux ); + return; + } + } + + // try other inputs + for ( k = i+1; k < pObj->nFans; k++ ) + { + // get four cofactors ik + Kit_TruthCofactor0New( pCofs4[0][0], pCofs2[0], pObj->nFans, k ); // 00 + Kit_TruthCofactor1New( pCofs4[0][1], pCofs2[0], pObj->nFans, k ); // 01 + Kit_TruthCofactor0New( pCofs4[1][0], pCofs2[1], pObj->nFans, k ); // 10 + Kit_TruthCofactor1New( pCofs4[1][1], pCofs2[1], pObj->nFans, k ); // 11 + // compare equal pairs + fPairs[0][1] = fPairs[1][0] = Kit_TruthIsEqual( pCofs4[0][0], pCofs4[0][1], pObj->nFans ); + fPairs[0][2] = fPairs[2][0] = Kit_TruthIsEqual( pCofs4[0][0], pCofs4[1][0], pObj->nFans ); + fPairs[0][3] = fPairs[3][0] = Kit_TruthIsEqual( pCofs4[0][0], pCofs4[1][1], pObj->nFans ); + fPairs[1][2] = fPairs[2][1] = Kit_TruthIsEqual( pCofs4[0][1], pCofs4[1][0], pObj->nFans ); + fPairs[1][3] = fPairs[3][1] = Kit_TruthIsEqual( pCofs4[0][1], pCofs4[1][1], pObj->nFans ); + fPairs[2][3] = fPairs[3][2] = Kit_TruthIsEqual( pCofs4[1][0], pCofs4[1][1], pObj->nFans ); + nPairs = fPairs[0][1] + fPairs[0][2] + fPairs[0][3] + fPairs[1][2] + fPairs[1][3] + fPairs[2][3]; + if ( nPairs != 3 && nPairs != 2 ) + continue; + + // decomposition exists + pRes = Kit_DsdObjAlloc( pNtk, KIT_DSD_AND, 2 ); + pRes->nRefs++; + pRes->nFans = 2; + pRes->pFans[0] = pObj->pFans[k]; pObj->pFans[k] = 2 * pRes->Id; // remains in support + pRes->pFans[1] = pObj->pFans[i]; pObj->pFans[i] = 127; uSupp &= ~(1 << i); + if ( !fPairs[0][1] && !fPairs[0][2] && !fPairs[0][3] ) // 00 + { + pRes->pFans[0] = Kit_DsdLitNot(pRes->pFans[0]); + pRes->pFans[1] = Kit_DsdLitNot(pRes->pFans[1]); + Kit_TruthMuxVar( pTruth, pCofs4[1][1], pCofs4[0][0], pObj->nFans, k ); + } + else if ( !fPairs[1][0] && !fPairs[1][2] && !fPairs[1][3] ) // 01 + { + pRes->pFans[1] = Kit_DsdLitNot(pRes->pFans[1]); + Kit_TruthMuxVar( pTruth, pCofs4[0][0], pCofs4[0][1], pObj->nFans, k ); + } + else if ( !fPairs[2][0] && !fPairs[2][1] && !fPairs[2][3] ) // 10 + { + pRes->pFans[0] = Kit_DsdLitNot(pRes->pFans[0]); + Kit_TruthMuxVar( pTruth, pCofs4[0][0], pCofs4[1][0], pObj->nFans, k ); + } + else if ( !fPairs[3][0] && !fPairs[3][1] && !fPairs[3][2] ) // 11 + { +// unsigned uSupp0 = Kit_TruthSupport(pCofs4[0][0], pObj->nFans); +// unsigned uSupp1 = Kit_TruthSupport(pCofs4[1][1], pObj->nFans); +// unsigned uSupp; +// Extra_PrintBinary( stdout, &uSupp0, pObj->nFans ); printf( "\n" ); +// Extra_PrintBinary( stdout, &uSupp1, pObj->nFans ); printf( "\n" ); + Kit_TruthMuxVar( pTruth, pCofs4[0][0], pCofs4[1][1], pObj->nFans, k ); +// uSupp = Kit_TruthSupport(pTruth, pObj->nFans); +// Extra_PrintBinary( stdout, &uSupp, pObj->nFans ); printf( "\n" ); printf( "\n" ); + } + else + { + assert( fPairs[0][3] && fPairs[1][2] ); + pRes->Type = KIT_DSD_XOR;; + Kit_TruthMuxVar( pTruth, pCofs4[0][0], pCofs4[0][1], pObj->nFans, k ); + } + // decompose the remainder + Kit_DsdDecompose_rec( pNtk, pObj, uSupp, pPar, nDecMux ); + return; + } + } +/* + // if all decomposition methods failed and we are still above the limit, perform MUX-decomposition + if ( nDecMux > 0 && (int)pObj->nFans > nDecMux ) + { + int iBestVar = Kit_TruthBestCofVar( pTruth, pObj->nFans, pCofs2[0], pCofs2[1] ); + uSupp0 = Kit_TruthSupport( pCofs2[0], pObj->nFans ); + uSupp1 = Kit_TruthSupport( pCofs2[1], pObj->nFans ); + // perform MUX decomposition + pRes0 = Kit_DsdObjAlloc( pNtk, KIT_DSD_PRIME, pObj->nFans ); + pRes1 = Kit_DsdObjAlloc( pNtk, KIT_DSD_PRIME, pObj->nFans ); + for ( k = 0; k < pObj->nFans; k++ ) + pRes0->pFans[k] = pRes1->pFans[k] = pObj->pFans[k]; + Kit_TruthCopy( Kit_DsdObjTruth(pRes0), pCofs2[0], pObj->nFans ); + Kit_TruthCopy( Kit_DsdObjTruth(pRes1), pCofs2[1], pObj->nFans ); + // update the current one + assert( pObj->Type == KIT_DSD_PRIME ); + pTruth[0] = 0xCACACACA; + pObj->nFans = 3; + pObj->pFans[0] = 2*pRes0->Id; pRes0->nRefs++; + pObj->pFans[1] = 2*pRes1->Id; pRes1->nRefs++; + pObj->pFans[2] = pObj->pFans[iBestVar]; + // call recursively + Kit_DsdDecompose_rec( pNtk, pRes0, uSupp0, pObj->pFans + 0, nDecMux ); + Kit_DsdDecompose_rec( pNtk, pRes1, uSupp1, pObj->pFans + 1, nDecMux ); + } +*/ +} + +/**Function************************************************************* + + Synopsis [Performs decomposition of the truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_DsdNtk_t * Kit_DsdDecomposeInt( unsigned * pTruth, int nVars, int nDecMux ) +{ + Kit_DsdNtk_t * pNtk; + Kit_DsdObj_t * pObj; + unsigned uSupp; + int i, nVarsReal; + assert( nVars <= 16 ); + pNtk = Kit_DsdNtkAlloc( nVars ); + pNtk->Root = Kit_DsdVar2Lit( pNtk->nVars, 0 ); + // create the first node + pObj = Kit_DsdObjAlloc( pNtk, KIT_DSD_PRIME, nVars ); + assert( pNtk->pNodes[0] == pObj ); + for ( i = 0; i < nVars; i++ ) + pObj->pFans[i] = Kit_DsdVar2Lit( i, 0 ); + Kit_TruthCopy( Kit_DsdObjTruth(pObj), pTruth, nVars ); + uSupp = Kit_TruthSupport( pTruth, nVars ); + // consider special cases + nVarsReal = Kit_WordCountOnes( uSupp ); + if ( nVarsReal == 0 ) + { + pObj->Type = KIT_DSD_CONST1; + pObj->nFans = 0; + if ( pTruth[0] == 0 ) + pNtk->Root = Kit_DsdLitNot(pNtk->Root); + return pNtk; + } + if ( nVarsReal == 1 ) + { + pObj->Type = KIT_DSD_VAR; + pObj->nFans = 1; + pObj->pFans[0] = Kit_DsdVar2Lit( Kit_WordFindFirstBit(uSupp), (pTruth[0] & 1) ); + return pNtk; + } + Kit_DsdDecompose_rec( pNtk, pNtk->pNodes[0], uSupp, &pNtk->Root, nDecMux ); + return pNtk; +} + +/**Function************************************************************* + + Synopsis [Performs decomposition of the truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_DsdNtk_t * Kit_DsdDecompose( unsigned * pTruth, int nVars ) +{ + return Kit_DsdDecomposeInt( pTruth, nVars, 0 ); +} + +/**Function************************************************************* + + Synopsis [Performs decomposition of the truth table.] + + Description [Uses MUXes to break-down large prime nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_DsdNtk_t * Kit_DsdDecomposeMux( unsigned * pTruth, int nVars, int nDecMux ) +{ + return Kit_DsdDecomposeInt( pTruth, nVars, nDecMux ); +} + +/**Function************************************************************* + + Synopsis [Performs decomposition of the truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_DsdTestCofs( Kit_DsdNtk_t * pNtk, unsigned * pTruthInit ) +{ + Kit_DsdNtk_t * pNtk0, * pNtk1, * pTemp; +// Kit_DsdObj_t * pRoot; + unsigned * pCofs2[2] = { pNtk->pMem, pNtk->pMem + Kit_TruthWordNum(pNtk->nVars) }; + unsigned i, * pTruth; + int fVerbose = 1; + int RetValue = 0; + + pTruth = pTruthInit; +// pRoot = Kit_DsdNtkRoot(pNtk); +// pTruth = Kit_DsdObjTruth(pRoot); +// assert( pRoot->nFans == pNtk->nVars ); + + if ( fVerbose ) + { + printf( "Function: " ); +// Extra_PrintBinary( stdout, pTruth, (1 << pNtk->nVars) ); + Extra_PrintHexadecimal( stdout, pTruth, pNtk->nVars ); + printf( "\n" ); + Kit_DsdPrint( stdout, pNtk ); + } + for ( i = 0; i < pNtk->nVars; i++ ) + { + Kit_TruthCofactor0New( pCofs2[0], pTruth, pNtk->nVars, i ); + pNtk0 = Kit_DsdDecompose( pCofs2[0], pNtk->nVars ); + pNtk0 = Kit_DsdExpand( pTemp = pNtk0 ); + Kit_DsdNtkFree( pTemp ); + + if ( fVerbose ) + { + printf( "Cof%d0: ", i ); + Kit_DsdPrint( stdout, pNtk0 ); + } + + Kit_TruthCofactor1New( pCofs2[1], pTruth, pNtk->nVars, i ); + pNtk1 = Kit_DsdDecompose( pCofs2[1], pNtk->nVars ); + pNtk1 = Kit_DsdExpand( pTemp = pNtk1 ); + Kit_DsdNtkFree( pTemp ); + + if ( fVerbose ) + { + printf( "Cof%d1: ", i ); + Kit_DsdPrint( stdout, pNtk1 ); + } + +// if ( Kit_DsdCheckVar4Dec2( pNtk0, pNtk1 ) ) +// RetValue = 1; + + Kit_DsdNtkFree( pNtk0 ); + Kit_DsdNtkFree( pNtk1 ); + } + if ( fVerbose ) + printf( "\n" ); + + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Performs decomposition of the truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_DsdEval( unsigned * pTruth, int nVars, int nLutSize ) +{ + Kit_DsdMan_t * p; + Kit_DsdNtk_t * pNtk; + unsigned * pTruthC; + int Result; + + // decompose the function + pNtk = Kit_DsdDecompose( pTruth, nVars ); + Result = Kit_DsdCountLuts( pNtk, nLutSize ); +// printf( "\n" ); +// Kit_DsdPrint( stdout, pNtk ); +// printf( "Eval = %d.\n", Result ); + + // recompute the truth table + p = Kit_DsdManAlloc( nVars ); + pTruthC = Kit_DsdTruthCompute( p, pNtk, 0 ); + if ( !Extra_TruthIsEqual( pTruth, pTruthC, nVars ) ) + printf( "Verification failed.\n" ); + Kit_DsdManFree( p ); + + Kit_DsdNtkFree( pNtk ); + return Result; +} + +/**Function************************************************************* + + Synopsis [Performs decomposition of the truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_DsdVerify( Kit_DsdNtk_t * pNtk, unsigned * pTruth, int nVars ) +{ + Kit_DsdMan_t * p; + unsigned * pTruthC; + p = Kit_DsdManAlloc( nVars ); + pTruthC = Kit_DsdTruthCompute( p, pNtk, 0 ); + if ( !Extra_TruthIsEqual( pTruth, pTruthC, nVars ) ) + printf( "Verification failed.\n" ); + Kit_DsdManFree( p ); +} + +/**Function************************************************************* + + Synopsis [Performs decomposition of the truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_DsdTest( unsigned * pTruth, int nVars ) +{ + Kit_DsdMan_t * p; + unsigned * pTruthC; + Kit_DsdNtk_t * pNtk, * pTemp; + pNtk = Kit_DsdDecompose( pTruth, nVars ); + +// if ( Kit_DsdFindLargeBox(pNtk, Kit_DsdLit2Var(pNtk->Root)) ) +// Kit_DsdPrint( stdout, pNtk ); + +// if ( Kit_DsdNtkRoot(pNtk)->nFans == (unsigned)nVars && nVars == 6 ) + + printf( "\n" ); + Kit_DsdPrint( stdout, pNtk ); + + pNtk = Kit_DsdExpand( pTemp = pNtk ); + Kit_DsdNtkFree( pTemp ); + + Kit_DsdPrint( stdout, pNtk ); + +// if ( Kit_DsdFindLargeBox(pNtk, Kit_DsdLit2Var(pNtk->Root)) ) +// Kit_DsdTestCofs( pNtk, pTruth ); + + // recompute the truth table + p = Kit_DsdManAlloc( nVars ); + pTruthC = Kit_DsdTruthCompute( p, pNtk, 0 ); +// Extra_PrintBinary( stdout, pTruth, 1 << nVars ); printf( "\n" ); +// Extra_PrintBinary( stdout, pTruthC, 1 << nVars ); printf( "\n" ); + if ( Extra_TruthIsEqual( pTruth, pTruthC, nVars ) ) + { +// printf( "Verification is okay.\n" ); + } + else + printf( "Verification failed.\n" ); + Kit_DsdManFree( p ); + + + Kit_DsdNtkFree( pNtk ); +} + +/**Function************************************************************* + + Synopsis [Performs decomposition of the truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_DsdPrecompute4Vars() +{ + Kit_DsdMan_t * p; + Kit_DsdNtk_t * pNtk, * pTemp; + FILE * pFile; + unsigned uTruth; + unsigned * pTruthC; + char Buffer[256]; + int i, RetValue; + int Counter1 = 0, Counter2 = 0; + + pFile = fopen( "5npn/npn4.txt", "r" ); + for ( i = 0; fgets( Buffer, 100, pFile ); i++ ) + { + Buffer[6] = 0; + Extra_ReadHexadecimal( &uTruth, Buffer+2, 4 ); + uTruth = ((uTruth & 0xffff) << 16) | (uTruth & 0xffff); + pNtk = Kit_DsdDecompose( &uTruth, 4 ); + + pNtk = Kit_DsdExpand( pTemp = pNtk ); + Kit_DsdNtkFree( pTemp ); + + + if ( Kit_DsdFindLargeBox(pNtk, 3) ) + { +// RetValue = 0; + RetValue = Kit_DsdTestCofs( pNtk, &uTruth ); + printf( "\n" ); + printf( "%3d : Non-DSD function %s %s\n", i, Buffer + 2, RetValue? "implementable" : "" ); + Kit_DsdPrint( stdout, pNtk ); + + Counter1++; + Counter2 += RetValue; + } + +/* + printf( "%3d : Function %s ", i, Buffer + 2 ); + if ( !Kit_DsdFindLargeBox(pNtk, 3) ) + Kit_DsdPrint( stdout, pNtk ); + else + printf( "\n" ); +*/ + + p = Kit_DsdManAlloc( 4 ); + pTruthC = Kit_DsdTruthCompute( p, pNtk, 0 ); + if ( !Extra_TruthIsEqual( &uTruth, pTruthC, 4 ) ) + printf( "Verification failed.\n" ); + Kit_DsdManFree( p ); + + Kit_DsdNtkFree( pNtk ); + } + fclose( pFile ); + printf( "non-DSD = %d implementable = %d\n", Counter1, Counter2 ); +} + + +/**Function************************************************************* + + Synopsis [Returns the set of cofactoring variables.] + + Description [If there is no DSD components returns 0.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_DsdCofactoringGetVars( Kit_DsdNtk_t ** ppNtk, int nSize, int * pVars ) +{ + Kit_DsdObj_t * pObj; + unsigned m; + int i, k, v, Var, nVars, iFaninLit; + // go through all the networks + nVars = 0; + for ( i = 0; i < nSize; i++ ) + { + // go through the prime objects of each networks + Kit_DsdNtkForEachObj( ppNtk[i], pObj, k ) + { + if ( pObj->Type != KIT_DSD_PRIME ) + continue; + if ( pObj->nFans == 3 ) + continue; + // collect direct fanin variables + Kit_DsdObjForEachFanin( ppNtk[i], pObj, iFaninLit, m ) + { + if ( !Kit_DsdLitIsLeaf(ppNtk[i], iFaninLit) ) + continue; + // add it to the array + Var = Kit_DsdLit2Var( iFaninLit ); + for ( v = 0; v < nVars; v++ ) + if ( pVars[v] == Var ) + break; + if ( v == nVars ) + pVars[nVars++] = Var; + } + } + } + return nVars; +} + +/**Function************************************************************* + + Synopsis [Canonical decomposition into completely DSD-structure.] + + Description [Returns the number of cofactoring steps. Also returns + the cofactoring variables in pVars.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_DsdCofactoring( unsigned * pTruth, int nVars, int * pCofVars, int nLimit, int fVerbose ) +{ + Kit_DsdNtk_t * ppNtks[5][16] = {0}, * pTemp; + unsigned * ppCofs[5][16]; + int pTryVars[16], nTryVars; + int nPrimeSizeMin, nPrimeSizeMax, nPrimeSizeCur; + int nSuppSizeMin, nSuppSizeMax, iVarBest; + int i, k, v, nStep, nSize, nMemSize; + assert( nLimit < 5 ); + + // allocate storage for cofactors + nMemSize = Kit_TruthWordNum(nVars); + ppCofs[0][0] = ALLOC( unsigned, 80 * nMemSize ); + nSize = 0; + for ( i = 0; i < 5; i++ ) + for ( k = 0; k < 16; k++ ) + ppCofs[i][k] = ppCofs[0][0] + nMemSize * nSize++; + assert( nSize == 80 ); + + // copy the function + Kit_TruthCopy( ppCofs[0][0], pTruth, nVars ); + ppNtks[0][0] = Kit_DsdDecompose( ppCofs[0][0], nVars ); + + if ( fVerbose ) + printf( "\nProcessing prime function with %d support variables:\n", nVars ); + + // perform recursive cofactoring + for ( nStep = 0; nStep < nLimit; nStep++ ) + { + nSize = (1 << nStep); + // find the variables to use in the cofactoring step + nTryVars = Kit_DsdCofactoringGetVars( ppNtks[nStep], nSize, pTryVars ); + if ( nTryVars == 0 ) + break; + // cofactor w.r.t. the above variables + iVarBest = -1; + nPrimeSizeMin = 10000; + nSuppSizeMin = 10000; + for ( v = 0; v < nTryVars; v++ ) + { + nPrimeSizeMax = 0; + nSuppSizeMax = 0; + for ( i = 0; i < nSize; i++ ) + { + // cofactor and decompose cofactors + Kit_TruthCofactor0New( ppCofs[nStep+1][2*i+0], ppCofs[nStep][i], nVars, pTryVars[v] ); + Kit_TruthCofactor1New( ppCofs[nStep+1][2*i+1], ppCofs[nStep][i], nVars, pTryVars[v] ); + ppNtks[nStep+1][2*i+0] = Kit_DsdDecompose( ppCofs[nStep+1][2*i+0], nVars ); + ppNtks[nStep+1][2*i+1] = Kit_DsdDecompose( ppCofs[nStep+1][2*i+1], nVars ); + // compute the largest non-decomp block + nPrimeSizeCur = Kit_DsdNonDsdSizeMax(ppNtks[nStep+1][2*i+0]); + nPrimeSizeMax = KIT_MAX( nPrimeSizeMax, nPrimeSizeCur ); + nPrimeSizeCur = Kit_DsdNonDsdSizeMax(ppNtks[nStep+1][2*i+1]); + nPrimeSizeMax = KIT_MAX( nPrimeSizeMax, nPrimeSizeCur ); + // compute the sum total of supports + nSuppSizeMax += Kit_TruthSupportSize( ppCofs[nStep+1][2*i+0], nVars ); + nSuppSizeMax += Kit_TruthSupportSize( ppCofs[nStep+1][2*i+1], nVars ); + // free the networks + Kit_DsdNtkFree( ppNtks[nStep+1][2*i+0] ); + Kit_DsdNtkFree( ppNtks[nStep+1][2*i+1] ); + } + // find the min max support size of the prime component + if ( nPrimeSizeMin > nPrimeSizeMax || (nPrimeSizeMin == nPrimeSizeMax && nSuppSizeMin > nSuppSizeMax) ) + { + nPrimeSizeMin = nPrimeSizeMax; + nSuppSizeMin = nSuppSizeMax; + iVarBest = pTryVars[v]; + } + } + assert( iVarBest != -1 ); + // save the variable + if ( pCofVars ) + pCofVars[nStep] = iVarBest; + // cofactor w.r.t. the best + for ( i = 0; i < nSize; i++ ) + { + Kit_TruthCofactor0New( ppCofs[nStep+1][2*i+0], ppCofs[nStep][i], nVars, iVarBest ); + Kit_TruthCofactor1New( ppCofs[nStep+1][2*i+1], ppCofs[nStep][i], nVars, iVarBest ); + ppNtks[nStep+1][2*i+0] = Kit_DsdDecompose( ppCofs[nStep+1][2*i+0], nVars ); + ppNtks[nStep+1][2*i+1] = Kit_DsdDecompose( ppCofs[nStep+1][2*i+1], nVars ); + if ( fVerbose ) + { + ppNtks[nStep+1][2*i+0] = Kit_DsdExpand( pTemp = ppNtks[nStep+1][2*i+0] ); + Kit_DsdNtkFree( pTemp ); + ppNtks[nStep+1][2*i+1] = Kit_DsdExpand( pTemp = ppNtks[nStep+1][2*i+1] ); + Kit_DsdNtkFree( pTemp ); + + printf( "Cof%d%d: ", nStep+1, 2*i+0 ); + Kit_DsdPrint( stdout, ppNtks[nStep+1][2*i+0] ); + printf( "Cof%d%d: ", nStep+1, 2*i+1 ); + Kit_DsdPrint( stdout, ppNtks[nStep+1][2*i+1] ); + } + } + } + + // free the networks + for ( i = 0; i < 5; i++ ) + for ( k = 0; k < 16; k++ ) + if ( ppNtks[i][k] ) + Kit_DsdNtkFree( ppNtks[i][k] ); + free( ppCofs[0][0] ); + + assert( nStep <= nLimit ); + return nStep; +} + +/**Function************************************************************* + + Synopsis [Canonical decomposition into completely DSD-structure.] + + Description [Returns the number of cofactoring steps. Also returns + the cofactoring variables in pVars.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_DsdPrintCofactors( unsigned * pTruth, int nVars, int nCofLevel, int fVerbose ) +{ + Kit_DsdNtk_t * ppNtks[32] = {0}, * pTemp; + unsigned * ppCofs[5][16]; + int piCofVar[5]; + int nPrimeSizeMax, nPrimeSizeCur, nSuppSizeMax; + int i, k, v1, v2, v3, v4, s, nSteps, nSize, nMemSize; + assert( nCofLevel < 5 ); + + // print the function + ppNtks[0] = Kit_DsdDecompose( pTruth, nVars ); + ppNtks[0] = Kit_DsdExpand( pTemp = ppNtks[0] ); + Kit_DsdNtkFree( pTemp ); + if ( fVerbose ) + Kit_DsdPrint( stdout, ppNtks[0] ); + Kit_DsdNtkFree( ppNtks[0] ); + + // allocate storage for cofactors + nMemSize = Kit_TruthWordNum(nVars); + ppCofs[0][0] = ALLOC( unsigned, 80 * nMemSize ); + nSize = 0; + for ( i = 0; i < 5; i++ ) + for ( k = 0; k < 16; k++ ) + ppCofs[i][k] = ppCofs[0][0] + nMemSize * nSize++; + assert( nSize == 80 ); + + // copy the function + Kit_TruthCopy( ppCofs[0][0], pTruth, nVars ); + + if ( nCofLevel == 1 ) + for ( v1 = 0; v1 < nVars; v1++ ) + { + nSteps = 0; + piCofVar[nSteps++] = v1; + + printf( " Variables { " ); + for ( i = 0; i < nSteps; i++ ) + printf( "%c ", 'a' + piCofVar[i] ); + printf( "}\n" ); + + // single cofactors + for ( s = 1; s <= nSteps; s++ ) + { + for ( k = 0; k < s; k++ ) + { + nSize = (1 << k); + for ( i = 0; i < nSize; i++ ) + { + Kit_TruthCofactor0New( ppCofs[k+1][2*i+0], ppCofs[k][i], nVars, piCofVar[k] ); + Kit_TruthCofactor1New( ppCofs[k+1][2*i+1], ppCofs[k][i], nVars, piCofVar[k] ); + } + } + } + // compute DSD networks + nSize = (1 << nSteps); + nPrimeSizeMax = 0; + nSuppSizeMax = 0; + for ( i = 0; i < nSize; i++ ) + { + ppNtks[i] = Kit_DsdDecompose( ppCofs[nSteps][i], nVars ); + ppNtks[i] = Kit_DsdExpand( pTemp = ppNtks[i] ); + Kit_DsdNtkFree( pTemp ); + if ( fVerbose ) + { + printf( "Cof%d%d: ", nSteps, i ); + Kit_DsdPrint( stdout, ppNtks[i] ); + } + // compute the largest non-decomp block + nPrimeSizeCur = Kit_DsdNonDsdSizeMax(ppNtks[i]); + nPrimeSizeMax = KIT_MAX( nPrimeSizeMax, nPrimeSizeCur ); + Kit_DsdNtkFree( ppNtks[i] ); + nSuppSizeMax += Kit_TruthSupportSize( ppCofs[nSteps][i], nVars ); + } + printf( "Max = %2d. Supps = %2d.\n", nPrimeSizeMax, nSuppSizeMax ); + } + + if ( nCofLevel == 2 ) + for ( v1 = 0; v1 < nVars; v1++ ) + for ( v2 = v1+1; v2 < nVars; v2++ ) + { + nSteps = 0; + piCofVar[nSteps++] = v1; + piCofVar[nSteps++] = v2; + + printf( " Variables { " ); + for ( i = 0; i < nSteps; i++ ) + printf( "%c ", 'a' + piCofVar[i] ); + printf( "}\n" ); + + // single cofactors + for ( s = 1; s <= nSteps; s++ ) + { + for ( k = 0; k < s; k++ ) + { + nSize = (1 << k); + for ( i = 0; i < nSize; i++ ) + { + Kit_TruthCofactor0New( ppCofs[k+1][2*i+0], ppCofs[k][i], nVars, piCofVar[k] ); + Kit_TruthCofactor1New( ppCofs[k+1][2*i+1], ppCofs[k][i], nVars, piCofVar[k] ); + } + } + } + // compute DSD networks + nSize = (1 << nSteps); + nPrimeSizeMax = 0; + nSuppSizeMax = 0; + for ( i = 0; i < nSize; i++ ) + { + ppNtks[i] = Kit_DsdDecompose( ppCofs[nSteps][i], nVars ); + ppNtks[i] = Kit_DsdExpand( pTemp = ppNtks[i] ); + Kit_DsdNtkFree( pTemp ); + if ( fVerbose ) + { + printf( "Cof%d%d: ", nSteps, i ); + Kit_DsdPrint( stdout, ppNtks[i] ); + } + // compute the largest non-decomp block + nPrimeSizeCur = Kit_DsdNonDsdSizeMax(ppNtks[i]); + nPrimeSizeMax = KIT_MAX( nPrimeSizeMax, nPrimeSizeCur ); + Kit_DsdNtkFree( ppNtks[i] ); + nSuppSizeMax += Kit_TruthSupportSize( ppCofs[nSteps][i], nVars ); + } + printf( "Max = %2d. Supps = %2d.\n", nPrimeSizeMax, nSuppSizeMax ); + } + + if ( nCofLevel == 3 ) + for ( v1 = 0; v1 < nVars; v1++ ) + for ( v2 = v1+1; v2 < nVars; v2++ ) + for ( v3 = v2+1; v3 < nVars; v3++ ) + { + nSteps = 0; + piCofVar[nSteps++] = v1; + piCofVar[nSteps++] = v2; + piCofVar[nSteps++] = v3; + + printf( " Variables { " ); + for ( i = 0; i < nSteps; i++ ) + printf( "%c ", 'a' + piCofVar[i] ); + printf( "}\n" ); + + // single cofactors + for ( s = 1; s <= nSteps; s++ ) + { + for ( k = 0; k < s; k++ ) + { + nSize = (1 << k); + for ( i = 0; i < nSize; i++ ) + { + Kit_TruthCofactor0New( ppCofs[k+1][2*i+0], ppCofs[k][i], nVars, piCofVar[k] ); + Kit_TruthCofactor1New( ppCofs[k+1][2*i+1], ppCofs[k][i], nVars, piCofVar[k] ); + } + } + } + // compute DSD networks + nSize = (1 << nSteps); + nPrimeSizeMax = 0; + nSuppSizeMax = 0; + for ( i = 0; i < nSize; i++ ) + { + ppNtks[i] = Kit_DsdDecompose( ppCofs[nSteps][i], nVars ); + ppNtks[i] = Kit_DsdExpand( pTemp = ppNtks[i] ); + Kit_DsdNtkFree( pTemp ); + if ( fVerbose ) + { + printf( "Cof%d%d: ", nSteps, i ); + Kit_DsdPrint( stdout, ppNtks[i] ); + } + // compute the largest non-decomp block + nPrimeSizeCur = Kit_DsdNonDsdSizeMax(ppNtks[i]); + nPrimeSizeMax = KIT_MAX( nPrimeSizeMax, nPrimeSizeCur ); + Kit_DsdNtkFree( ppNtks[i] ); + nSuppSizeMax += Kit_TruthSupportSize( ppCofs[nSteps][i], nVars ); + } + printf( "Max = %2d. Supps = %2d.\n", nPrimeSizeMax, nSuppSizeMax ); + } + + if ( nCofLevel == 4 ) + for ( v1 = 0; v1 < nVars; v1++ ) + for ( v2 = v1+1; v2 < nVars; v2++ ) + for ( v3 = v2+1; v3 < nVars; v3++ ) + for ( v4 = v3+1; v4 < nVars; v4++ ) + { + nSteps = 0; + piCofVar[nSteps++] = v1; + piCofVar[nSteps++] = v2; + piCofVar[nSteps++] = v3; + piCofVar[nSteps++] = v4; + + printf( " Variables { " ); + for ( i = 0; i < nSteps; i++ ) + printf( "%c ", 'a' + piCofVar[i] ); + printf( "}\n" ); + + // single cofactors + for ( s = 1; s <= nSteps; s++ ) + { + for ( k = 0; k < s; k++ ) + { + nSize = (1 << k); + for ( i = 0; i < nSize; i++ ) + { + Kit_TruthCofactor0New( ppCofs[k+1][2*i+0], ppCofs[k][i], nVars, piCofVar[k] ); + Kit_TruthCofactor1New( ppCofs[k+1][2*i+1], ppCofs[k][i], nVars, piCofVar[k] ); + } + } + } + // compute DSD networks + nSize = (1 << nSteps); + nPrimeSizeMax = 0; + nSuppSizeMax = 0; + for ( i = 0; i < nSize; i++ ) + { + ppNtks[i] = Kit_DsdDecompose( ppCofs[nSteps][i], nVars ); + ppNtks[i] = Kit_DsdExpand( pTemp = ppNtks[i] ); + Kit_DsdNtkFree( pTemp ); + if ( fVerbose ) + { + printf( "Cof%d%d: ", nSteps, i ); + Kit_DsdPrint( stdout, ppNtks[i] ); + } + // compute the largest non-decomp block + nPrimeSizeCur = Kit_DsdNonDsdSizeMax(ppNtks[i]); + nPrimeSizeMax = KIT_MAX( nPrimeSizeMax, nPrimeSizeCur ); + Kit_DsdNtkFree( ppNtks[i] ); + nSuppSizeMax += Kit_TruthSupportSize( ppCofs[nSteps][i], nVars ); + } + printf( "Max = %2d. Supps = %2d.\n", nPrimeSizeMax, nSuppSizeMax ); + } + + + free( ppCofs[0][0] ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/kit/kitFactor.c b/abc_with_bb_support/src/aig/kit/kitFactor.c new file mode 100644 index 000000000..42faaeac0 --- /dev/null +++ b/abc_with_bb_support/src/aig/kit/kitFactor.c @@ -0,0 +1,338 @@ +/**CFile**************************************************************** + + FileName [kitFactor.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Computation kit.] + + Synopsis [Algebraic factoring.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Dec 6, 2006.] + + Revision [$Id: kitFactor.c,v 1.00 2006/12/06 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "kit.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// factoring fails if intermediate memory usage exceed this limit +#define KIT_FACTOR_MEM_LIMIT (1<<16) + +static Kit_Edge_t Kit_SopFactor_rec( Kit_Graph_t * pFForm, Kit_Sop_t * cSop, int nLits, Vec_Int_t * vMemory ); +static Kit_Edge_t Kit_SopFactorLF_rec( Kit_Graph_t * pFForm, Kit_Sop_t * cSop, Kit_Sop_t * cSimple, int nLits, Vec_Int_t * vMemory ); +static Kit_Edge_t Kit_SopFactorTrivial( Kit_Graph_t * pFForm, Kit_Sop_t * cSop, int nLits ); +static Kit_Edge_t Kit_SopFactorTrivialCube( Kit_Graph_t * pFForm, unsigned uCube, int nLits ); + +extern int Kit_SopFactorVerify( Vec_Int_t * cSop, Kit_Graph_t * pFForm, int nVars ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Factors the cover.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_Graph_t * Kit_SopFactor( Vec_Int_t * vCover, int fCompl, int nVars, Vec_Int_t * vMemory ) +{ + Kit_Sop_t Sop, Res; + Kit_Sop_t * cSop = &Sop, * cRes = &Res; + Kit_Graph_t * pFForm; + Kit_Edge_t eRoot; + int nCubes = Vec_IntSize(vCover); + + // works for up to 15 variables because divisin procedure + // used the last bit for marking the cubes going to the remainder + assert( nVars < 16 ); + + // check for trivial functions + if ( Vec_IntSize(vCover) == 0 ) + return Kit_GraphCreateConst0(); + if ( Vec_IntSize(vCover) == 1 && Vec_IntEntry(vCover, 0) == 0 ) //(int)Kit_CubeMask(2 * nVars) ) + return Kit_GraphCreateConst1(); + + // prepare memory manager +// Vec_IntClear( vMemory ); + Vec_IntGrow( vMemory, KIT_FACTOR_MEM_LIMIT ); + + // perform CST + Kit_SopCreateInverse( cSop, vCover, 2 * nVars, vMemory ); // CST + + // start the factored form + pFForm = Kit_GraphCreate( nVars ); + // factor the cover + eRoot = Kit_SopFactor_rec( pFForm, cSop, 2 * nVars, vMemory ); + // finalize the factored form + Kit_GraphSetRoot( pFForm, eRoot ); + if ( fCompl ) + Kit_GraphComplement( pFForm ); + + // verify the factored form +// Vec_IntShrink( vCover, nCubes ); +// if ( !Kit_SopFactorVerify( vCover, pFForm, nVars ) ) +// printf( "Verification has failed.\n" ); + return pFForm; +} + +/**Function************************************************************* + + Synopsis [Recursive factoring procedure.] + + Description [For the pseudo-code, see Hachtel/Somenzi, + Logic synthesis and verification algorithms, Kluwer, 1996, p. 432.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_Edge_t Kit_SopFactor_rec( Kit_Graph_t * pFForm, Kit_Sop_t * cSop, int nLits, Vec_Int_t * vMemory ) +{ + Kit_Sop_t Div, Quo, Rem, Com; + Kit_Sop_t * cDiv = &Div, * cQuo = &Quo, * cRem = &Rem, * cCom = &Com; + Kit_Edge_t eNodeDiv, eNodeQuo, eNodeRem, eNodeAnd; + + // make sure the cover contains some cubes + assert( Kit_SopCubeNum(cSop) > 0 ); + + // get the divisor + if ( !Kit_SopDivisor(cDiv, cSop, nLits, vMemory) ) + return Kit_SopFactorTrivial( pFForm, cSop, nLits ); + + // divide the cover by the divisor + Kit_SopDivideInternal( cSop, cDiv, cQuo, cRem, vMemory ); + + // check the trivial case + assert( Kit_SopCubeNum(cQuo) > 0 ); + if ( Kit_SopCubeNum(cQuo) == 1 ) + return Kit_SopFactorLF_rec( pFForm, cSop, cQuo, nLits, vMemory ); + + // make the quotient cube free + Kit_SopMakeCubeFree( cQuo ); + + // divide the cover by the quotient + Kit_SopDivideInternal( cSop, cQuo, cDiv, cRem, vMemory ); + + // check the trivial case + if ( Kit_SopIsCubeFree( cDiv ) ) + { + eNodeDiv = Kit_SopFactor_rec( pFForm, cDiv, nLits, vMemory ); + eNodeQuo = Kit_SopFactor_rec( pFForm, cQuo, nLits, vMemory ); + eNodeAnd = Kit_GraphAddNodeAnd( pFForm, eNodeDiv, eNodeQuo ); + if ( Kit_SopCubeNum(cRem) == 0 ) + return eNodeAnd; + eNodeRem = Kit_SopFactor_rec( pFForm, cRem, nLits, vMemory ); + return Kit_GraphAddNodeOr( pFForm, eNodeAnd, eNodeRem ); + } + + // get the common cube + Kit_SopCommonCubeCover( cCom, cDiv, vMemory ); + + // solve the simple problem + return Kit_SopFactorLF_rec( pFForm, cSop, cCom, nLits, vMemory ); +} + + +/**Function************************************************************* + + Synopsis [Internal recursive factoring procedure for the leaf case.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_Edge_t Kit_SopFactorLF_rec( Kit_Graph_t * pFForm, Kit_Sop_t * cSop, Kit_Sop_t * cSimple, int nLits, Vec_Int_t * vMemory ) +{ + Kit_Sop_t Div, Quo, Rem; + Kit_Sop_t * cDiv = &Div, * cQuo = &Quo, * cRem = &Rem; + Kit_Edge_t eNodeDiv, eNodeQuo, eNodeRem, eNodeAnd; + assert( Kit_SopCubeNum(cSimple) == 1 ); + // get the most often occurring literal + Kit_SopBestLiteralCover( cDiv, cSop, Kit_SopCube(cSimple, 0), nLits, vMemory ); + // divide the cover by the literal + Kit_SopDivideByCube( cSop, cDiv, cQuo, cRem, vMemory ); + // get the node pointer for the literal + eNodeDiv = Kit_SopFactorTrivialCube( pFForm, Kit_SopCube(cDiv, 0), nLits ); + // factor the quotient and remainder + eNodeQuo = Kit_SopFactor_rec( pFForm, cQuo, nLits, vMemory ); + eNodeAnd = Kit_GraphAddNodeAnd( pFForm, eNodeDiv, eNodeQuo ); + if ( Kit_SopCubeNum(cRem) == 0 ) + return eNodeAnd; + eNodeRem = Kit_SopFactor_rec( pFForm, cRem, nLits, vMemory ); + return Kit_GraphAddNodeOr( pFForm, eNodeAnd, eNodeRem ); +} + + +/**Function************************************************************* + + Synopsis [Factoring cube.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_Edge_t Kit_SopFactorTrivialCube_rec( Kit_Graph_t * pFForm, unsigned uCube, int nStart, int nFinish ) +{ + Kit_Edge_t eNode1, eNode2; + int i, iLit, nLits, nLits1, nLits2; + assert( uCube ); + // count the number of literals in this interval + nLits = 0; + for ( i = nStart; i < nFinish; i++ ) + if ( Kit_CubeHasLit(uCube, i) ) + { + iLit = i; + nLits++; + } + // quit if there is only one literal + if ( nLits == 1 ) + return Kit_EdgeCreate( iLit/2, iLit%2 ); // CST + // split the literals into two parts + nLits1 = nLits/2; + nLits2 = nLits - nLits1; +// nLits2 = nLits/2; +// nLits1 = nLits - nLits2; + // find the splitting point + nLits = 0; + for ( i = nStart; i < nFinish; i++ ) + if ( Kit_CubeHasLit(uCube, i) ) + { + if ( nLits == nLits1 ) + break; + nLits++; + } + // recursively construct the tree for the parts + eNode1 = Kit_SopFactorTrivialCube_rec( pFForm, uCube, nStart, i ); + eNode2 = Kit_SopFactorTrivialCube_rec( pFForm, uCube, i, nFinish ); + return Kit_GraphAddNodeAnd( pFForm, eNode1, eNode2 ); +} + +/**Function************************************************************* + + Synopsis [Factoring cube.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_Edge_t Kit_SopFactorTrivialCube( Kit_Graph_t * pFForm, unsigned uCube, int nLits ) +{ + return Kit_SopFactorTrivialCube_rec( pFForm, uCube, 0, nLits ); +} + +/**Function************************************************************* + + Synopsis [Factoring SOP.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_Edge_t Kit_SopFactorTrivial_rec( Kit_Graph_t * pFForm, unsigned * pCubes, int nCubes, int nLits ) +{ + Kit_Edge_t eNode1, eNode2; + int nCubes1, nCubes2; + if ( nCubes == 1 ) + return Kit_SopFactorTrivialCube_rec( pFForm, pCubes[0], 0, nLits ); + // split the cubes into two parts + nCubes1 = nCubes/2; + nCubes2 = nCubes - nCubes1; +// nCubes2 = nCubes/2; +// nCubes1 = nCubes - nCubes2; + // recursively construct the tree for the parts + eNode1 = Kit_SopFactorTrivial_rec( pFForm, pCubes, nCubes1, nLits ); + eNode2 = Kit_SopFactorTrivial_rec( pFForm, pCubes + nCubes1, nCubes2, nLits ); + return Kit_GraphAddNodeOr( pFForm, eNode1, eNode2 ); +} + +/**Function************************************************************* + + Synopsis [Factoring the cover, which has no algebraic divisors.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_Edge_t Kit_SopFactorTrivial( Kit_Graph_t * pFForm, Kit_Sop_t * cSop, int nLits ) +{ + return Kit_SopFactorTrivial_rec( pFForm, cSop->pCubes, cSop->nCubes, nLits ); +} + + +/**Function************************************************************* + + Synopsis [Testing procedure for the factoring code.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_FactorTest( unsigned * pTruth, int nVars ) +{ + Vec_Int_t * vCover, * vMemory; + Kit_Graph_t * pGraph; +// unsigned uTruthRes; + int RetValue; + + // derive SOP + vCover = Vec_IntAlloc( 0 ); + RetValue = Kit_TruthIsop( pTruth, nVars, vCover, 0 ); + assert( RetValue == 0 ); + + // derive factored form + vMemory = Vec_IntAlloc( 0 ); + pGraph = Kit_SopFactor( vCover, 0, nVars, vMemory ); +/* + // derive truth table + assert( nVars <= 5 ); + uTruthRes = Kit_GraphToTruth( pGraph ); + if ( uTruthRes != pTruth[0] ) + printf( "Verification failed!" ); +*/ + printf( "Vars = %2d. Cubes = %3d. FFNodes = %3d. FF_memory = %3d.\n", + nVars, Vec_IntSize(vCover), Kit_GraphNodeNum(pGraph), Vec_IntSize(vMemory) ); + + Vec_IntFree( vMemory ); + Vec_IntFree( vCover ); + Kit_GraphFree( pGraph ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/kit/kitGraph.c b/abc_with_bb_support/src/aig/kit/kitGraph.c new file mode 100644 index 000000000..1df0d6820 --- /dev/null +++ b/abc_with_bb_support/src/aig/kit/kitGraph.c @@ -0,0 +1,397 @@ +/**CFile**************************************************************** + + FileName [kitGraph.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Computation kit.] + + Synopsis [Decomposition graph representation.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Dec 6, 2006.] + + Revision [$Id: kitGraph.c,v 1.00 2006/12/06 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "kit.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Creates a graph with the given number of leaves.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_Graph_t * Kit_GraphCreate( int nLeaves ) +{ + Kit_Graph_t * pGraph; + pGraph = ALLOC( Kit_Graph_t, 1 ); + memset( pGraph, 0, sizeof(Kit_Graph_t) ); + pGraph->nLeaves = nLeaves; + pGraph->nSize = nLeaves; + pGraph->nCap = 2 * nLeaves + 50; + pGraph->pNodes = ALLOC( Kit_Node_t, pGraph->nCap ); + memset( pGraph->pNodes, 0, sizeof(Kit_Node_t) * pGraph->nSize ); + return pGraph; +} + +/**Function************************************************************* + + Synopsis [Creates constant 0 graph.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_Graph_t * Kit_GraphCreateConst0() +{ + Kit_Graph_t * pGraph; + pGraph = ALLOC( Kit_Graph_t, 1 ); + memset( pGraph, 0, sizeof(Kit_Graph_t) ); + pGraph->fConst = 1; + pGraph->eRoot.fCompl = 1; + return pGraph; +} + +/**Function************************************************************* + + Synopsis [Creates constant 1 graph.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_Graph_t * Kit_GraphCreateConst1() +{ + Kit_Graph_t * pGraph; + pGraph = ALLOC( Kit_Graph_t, 1 ); + memset( pGraph, 0, sizeof(Kit_Graph_t) ); + pGraph->fConst = 1; + return pGraph; +} + +/**Function************************************************************* + + Synopsis [Creates the literal graph.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_Graph_t * Kit_GraphCreateLeaf( int iLeaf, int nLeaves, int fCompl ) +{ + Kit_Graph_t * pGraph; + assert( 0 <= iLeaf && iLeaf < nLeaves ); + pGraph = Kit_GraphCreate( nLeaves ); + pGraph->eRoot.Node = iLeaf; + pGraph->eRoot.fCompl = fCompl; + return pGraph; +} + +/**Function************************************************************* + + Synopsis [Creates a graph with the given number of leaves.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_GraphFree( Kit_Graph_t * pGraph ) +{ + FREE( pGraph->pNodes ); + free( pGraph ); +} + +/**Function************************************************************* + + Synopsis [Appends a new node to the graph.] + + Description [This procedure is meant for internal use.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_Node_t * Kit_GraphAppendNode( Kit_Graph_t * pGraph ) +{ + Kit_Node_t * pNode; + if ( pGraph->nSize == pGraph->nCap ) + { + pGraph->pNodes = REALLOC( Kit_Node_t, pGraph->pNodes, 2 * pGraph->nCap ); + pGraph->nCap = 2 * pGraph->nCap; + } + pNode = pGraph->pNodes + pGraph->nSize++; + memset( pNode, 0, sizeof(Kit_Node_t) ); + return pNode; +} + +/**Function************************************************************* + + Synopsis [Creates an AND node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_Edge_t Kit_GraphAddNodeAnd( Kit_Graph_t * pGraph, Kit_Edge_t eEdge0, Kit_Edge_t eEdge1 ) +{ + Kit_Node_t * pNode; + // get the new node + pNode = Kit_GraphAppendNode( pGraph ); + // set the inputs and other info + pNode->eEdge0 = eEdge0; + pNode->eEdge1 = eEdge1; + pNode->fCompl0 = eEdge0.fCompl; + pNode->fCompl1 = eEdge1.fCompl; + return Kit_EdgeCreate( pGraph->nSize - 1, 0 ); +} + +/**Function************************************************************* + + Synopsis [Creates an OR node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_Edge_t Kit_GraphAddNodeOr( Kit_Graph_t * pGraph, Kit_Edge_t eEdge0, Kit_Edge_t eEdge1 ) +{ + Kit_Node_t * pNode; + // get the new node + pNode = Kit_GraphAppendNode( pGraph ); + // set the inputs and other info + pNode->eEdge0 = eEdge0; + pNode->eEdge1 = eEdge1; + pNode->fCompl0 = eEdge0.fCompl; + pNode->fCompl1 = eEdge1.fCompl; + // make adjustments for the OR gate + pNode->fNodeOr = 1; + pNode->eEdge0.fCompl = !pNode->eEdge0.fCompl; + pNode->eEdge1.fCompl = !pNode->eEdge1.fCompl; + return Kit_EdgeCreate( pGraph->nSize - 1, 1 ); +} + +/**Function************************************************************* + + Synopsis [Creates an XOR node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_Edge_t Kit_GraphAddNodeXor( Kit_Graph_t * pGraph, Kit_Edge_t eEdge0, Kit_Edge_t eEdge1, int Type ) +{ + Kit_Edge_t eNode0, eNode1, eNode; + if ( Type == 0 ) + { + // derive the first AND + eEdge0.fCompl ^= 1; + eNode0 = Kit_GraphAddNodeAnd( pGraph, eEdge0, eEdge1 ); + eEdge0.fCompl ^= 1; + // derive the second AND + eEdge1.fCompl ^= 1; + eNode1 = Kit_GraphAddNodeAnd( pGraph, eEdge0, eEdge1 ); + // derive the final OR + eNode = Kit_GraphAddNodeOr( pGraph, eNode0, eNode1 ); + } + else + { + // derive the first AND + eNode0 = Kit_GraphAddNodeAnd( pGraph, eEdge0, eEdge1 ); + // derive the second AND + eEdge0.fCompl ^= 1; + eEdge1.fCompl ^= 1; + eNode1 = Kit_GraphAddNodeAnd( pGraph, eEdge0, eEdge1 ); + // derive the final OR + eNode = Kit_GraphAddNodeOr( pGraph, eNode0, eNode1 ); + eNode.fCompl ^= 1; + } + return eNode; +} + +/**Function************************************************************* + + Synopsis [Creates an XOR node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_Edge_t Kit_GraphAddNodeMux( Kit_Graph_t * pGraph, Kit_Edge_t eEdgeC, Kit_Edge_t eEdgeT, Kit_Edge_t eEdgeE, int Type ) +{ + Kit_Edge_t eNode0, eNode1, eNode; + if ( Type == 0 ) + { + // derive the first AND + eNode0 = Kit_GraphAddNodeAnd( pGraph, eEdgeC, eEdgeT ); + // derive the second AND + eEdgeC.fCompl ^= 1; + eNode1 = Kit_GraphAddNodeAnd( pGraph, eEdgeC, eEdgeE ); + // derive the final OR + eNode = Kit_GraphAddNodeOr( pGraph, eNode0, eNode1 ); + } + else + { + // complement the arguments + eEdgeT.fCompl ^= 1; + eEdgeE.fCompl ^= 1; + // derive the first AND + eNode0 = Kit_GraphAddNodeAnd( pGraph, eEdgeC, eEdgeT ); + // derive the second AND + eEdgeC.fCompl ^= 1; + eNode1 = Kit_GraphAddNodeAnd( pGraph, eEdgeC, eEdgeE ); + // derive the final OR + eNode = Kit_GraphAddNodeOr( pGraph, eNode0, eNode1 ); + eNode.fCompl ^= 1; + } + return eNode; +} + +/**Function************************************************************* + + Synopsis [Derives the truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Kit_GraphToTruth( Kit_Graph_t * pGraph ) +{ + unsigned uTruths[5] = { 0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, 0xFF00FF00, 0xFFFF0000 }; + unsigned uTruth, uTruth0, uTruth1; + Kit_Node_t * pNode; + int i; + + // sanity checks + assert( Kit_GraphLeaveNum(pGraph) >= 0 ); + assert( Kit_GraphLeaveNum(pGraph) <= pGraph->nSize ); + assert( Kit_GraphLeaveNum(pGraph) <= 5 ); + + // check for constant function + if ( Kit_GraphIsConst(pGraph) ) + return Kit_GraphIsComplement(pGraph)? 0 : ~((unsigned)0); + // check for a literal + if ( Kit_GraphIsVar(pGraph) ) + return Kit_GraphIsComplement(pGraph)? ~uTruths[Kit_GraphVarInt(pGraph)] : uTruths[Kit_GraphVarInt(pGraph)]; + + // assign the elementary variables + Kit_GraphForEachLeaf( pGraph, pNode, i ) + pNode->pFunc = (void *)uTruths[i]; + + // compute the function for each internal node + Kit_GraphForEachNode( pGraph, pNode, i ) + { + uTruth0 = (unsigned)Kit_GraphNode(pGraph, pNode->eEdge0.Node)->pFunc; + uTruth1 = (unsigned)Kit_GraphNode(pGraph, pNode->eEdge1.Node)->pFunc; + uTruth0 = pNode->eEdge0.fCompl? ~uTruth0 : uTruth0; + uTruth1 = pNode->eEdge1.fCompl? ~uTruth1 : uTruth1; + uTruth = uTruth0 & uTruth1; + pNode->pFunc = (void *)uTruth; + } + + // complement the result if necessary + return Kit_GraphIsComplement(pGraph)? ~uTruth : uTruth; +} + +/**Function************************************************************* + + Synopsis [Derives the factored form from the truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Kit_Graph_t * Kit_TruthToGraph( unsigned * pTruth, int nVars, Vec_Int_t * vMemory ) +{ + Kit_Graph_t * pGraph; + int RetValue; + // derive SOP + RetValue = Kit_TruthIsop( pTruth, nVars, vMemory, 1 ); // tried 1 and found not useful in "renode" + if ( RetValue == -1 ) + return NULL; + if ( Vec_IntSize(vMemory) > 128 ) + return NULL; +// printf( "Isop size = %d.\n", Vec_IntSize(vMemory) ); + assert( RetValue == 0 || RetValue == 1 ); + // derive factored form + pGraph = Kit_SopFactor( vMemory, RetValue, nVars, vMemory ); + return pGraph; +} + +/**Function************************************************************* + + Synopsis [Derives the maximum depth from the leaf to the root.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_GraphLeafDepth_rec( Kit_Graph_t * pGraph, Kit_Node_t * pNode, Kit_Node_t * pLeaf ) +{ + int Depth0, Depth1, Depth; + if ( pNode == pLeaf ) + return 0; + if ( Kit_GraphNodeIsVar(pGraph, pNode) ) + return -100; + Depth0 = Kit_GraphLeafDepth_rec( pGraph, Kit_GraphNodeFanin0(pGraph, pNode), pLeaf ); + Depth1 = Kit_GraphLeafDepth_rec( pGraph, Kit_GraphNodeFanin1(pGraph, pNode), pLeaf ); + Depth = KIT_MAX( Depth0, Depth1 ); + Depth = (Depth == -100) ? -100 : Depth + 1; + return Depth; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/aig/kit/kitHop.c b/abc_with_bb_support/src/aig/kit/kitHop.c new file mode 100644 index 000000000..1cf1b6254 --- /dev/null +++ b/abc_with_bb_support/src/aig/kit/kitHop.c @@ -0,0 +1,115 @@ +/**CFile**************************************************************** + + FileName [kitHop.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Computation kit.] + + Synopsis [Procedures involving AIGs.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Dec 6, 2006.] + + Revision [$Id: kitHop.c,v 1.00 2006/12/06 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "kit.h" +#include "hop.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + + +/**Function************************************************************* + + Synopsis [Transforms the decomposition graph into the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Kit_GraphToHopInternal( Hop_Man_t * pMan, Kit_Graph_t * pGraph ) +{ + Kit_Node_t * pNode; + Hop_Obj_t * pAnd0, * pAnd1; + int i; + // check for constant function + if ( Kit_GraphIsConst(pGraph) ) + return Hop_NotCond( Hop_ManConst1(pMan), Kit_GraphIsComplement(pGraph) ); + // check for a literal + if ( Kit_GraphIsVar(pGraph) ) + return Hop_NotCond( Kit_GraphVar(pGraph)->pFunc, Kit_GraphIsComplement(pGraph) ); + // build the AIG nodes corresponding to the AND gates of the graph + Kit_GraphForEachNode( pGraph, pNode, i ) + { + pAnd0 = Hop_NotCond( Kit_GraphNode(pGraph, pNode->eEdge0.Node)->pFunc, pNode->eEdge0.fCompl ); + pAnd1 = Hop_NotCond( Kit_GraphNode(pGraph, pNode->eEdge1.Node)->pFunc, pNode->eEdge1.fCompl ); + pNode->pFunc = Hop_And( pMan, pAnd0, pAnd1 ); + } + // complement the result if necessary + return Hop_NotCond( pNode->pFunc, Kit_GraphIsComplement(pGraph) ); +} + +/**Function************************************************************* + + Synopsis [Strashes one logic node using its SOP.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Kit_GraphToHop( Hop_Man_t * pMan, Kit_Graph_t * pGraph ) +{ + Kit_Node_t * pNode; + int i; + // collect the fanins + Kit_GraphForEachLeaf( pGraph, pNode, i ) + pNode->pFunc = Hop_IthVar( pMan, i ); + // perform strashing + return Kit_GraphToHopInternal( pMan, pGraph ); +} + +/**Function************************************************************* + + Synopsis [Strashes one logic node using its SOP.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Kit_CoverToHop( Hop_Man_t * pMan, Vec_Int_t * vCover, int nVars, Vec_Int_t * vMemory ) +{ + Kit_Graph_t * pGraph; + Hop_Obj_t * pFunc; + // perform factoring + pGraph = Kit_SopFactor( vCover, 0, nVars, vMemory ); + // convert graph to the AIG + pFunc = Kit_GraphToHop( pMan, pGraph ); + Kit_GraphFree( pGraph ); + return pFunc; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/kit/kitIsop.c b/abc_with_bb_support/src/aig/kit/kitIsop.c new file mode 100644 index 000000000..e90abcb9d --- /dev/null +++ b/abc_with_bb_support/src/aig/kit/kitIsop.c @@ -0,0 +1,325 @@ +/**CFile**************************************************************** + + FileName [kitIsop.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Computation kit.] + + Synopsis [ISOP computation based on Morreale's algorithm.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Dec 6, 2006.] + + Revision [$Id: kitIsop.c,v 1.00 2006/12/06 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "kit.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// ISOP computation fails if intermediate memory usage exceed this limit +#define KIT_ISOP_MEM_LIMIT (1<<16) + +// static procedures to compute ISOP +static unsigned * Kit_TruthIsop_rec( unsigned * puOn, unsigned * puOnDc, int nVars, Kit_Sop_t * pcRes, Vec_Int_t * vStore ); +static unsigned Kit_TruthIsop5_rec( unsigned uOn, unsigned uOnDc, int nVars, Kit_Sop_t * pcRes, Vec_Int_t * vStore ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes ISOP from TT.] + + Description [Returns the cover in vMemory. Uses the rest of array in vMemory + as an intermediate memory storage. Returns the cover with -1 cubes, if the + the computation exceeded the memory limit (KIT_ISOP_MEM_LIMIT words of + intermediate data).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_TruthIsop( unsigned * puTruth, int nVars, Vec_Int_t * vMemory, int fTryBoth ) +{ + Kit_Sop_t cRes, * pcRes = &cRes; + Kit_Sop_t cRes2, * pcRes2 = &cRes2; + unsigned * pResult; + int RetValue = 0; + assert( nVars >= 0 && nVars < 16 ); + // if nVars < 5, make sure it does not depend on those vars +// for ( i = nVars; i < 5; i++ ) +// assert( !Extra_TruthVarInSupport(puTruth, 5, i) ); + // prepare memory manager + Vec_IntClear( vMemory ); + Vec_IntGrow( vMemory, KIT_ISOP_MEM_LIMIT ); + // compute ISOP for the direct polarity + pResult = Kit_TruthIsop_rec( puTruth, puTruth, nVars, pcRes, vMemory ); + if ( pcRes->nCubes == -1 ) + { + vMemory->nSize = -1; + return -1; + } + assert( Extra_TruthIsEqual( puTruth, pResult, nVars ) ); + if ( pcRes->nCubes == 0 || (pcRes->nCubes == 1 && pcRes->pCubes[0] == 0) ) + { + vMemory->pArray[0] = 0; + Vec_IntShrink( vMemory, pcRes->nCubes ); + return 0; + } + if ( fTryBoth ) + { + // compute ISOP for the complemented polarity + Extra_TruthNot( puTruth, puTruth, nVars ); + pResult = Kit_TruthIsop_rec( puTruth, puTruth, nVars, pcRes2, vMemory ); + if ( pcRes2->nCubes >= 0 ) + { + assert( Extra_TruthIsEqual( puTruth, pResult, nVars ) ); + if ( pcRes->nCubes > pcRes2->nCubes ) + { + RetValue = 1; + pcRes = pcRes2; + } + } + Extra_TruthNot( puTruth, puTruth, nVars ); + } +// printf( "%d ", vMemory->nSize ); + // move the cover representation to the beginning of the memory buffer + memmove( vMemory->pArray, pcRes->pCubes, pcRes->nCubes * sizeof(unsigned) ); + Vec_IntShrink( vMemory, pcRes->nCubes ); + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Computes ISOP 6 variables or more.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned * Kit_TruthIsop_rec( unsigned * puOn, unsigned * puOnDc, int nVars, Kit_Sop_t * pcRes, Vec_Int_t * vStore ) +{ + Kit_Sop_t cRes0, cRes1, cRes2; + Kit_Sop_t * pcRes0 = &cRes0, * pcRes1 = &cRes1, * pcRes2 = &cRes2; + unsigned * puRes0, * puRes1, * puRes2; + unsigned * puOn0, * puOn1, * puOnDc0, * puOnDc1, * pTemp, * pTemp0, * pTemp1; + int i, k, Var, nWords, nWordsAll; +// assert( Extra_TruthIsImply( puOn, puOnDc, nVars ) ); + // allocate room for the resulting truth table + nWordsAll = Extra_TruthWordNum( nVars ); + pTemp = Vec_IntFetch( vStore, nWordsAll ); + if ( pTemp == NULL ) + { + pcRes->nCubes = -1; + return NULL; + } + // check for constants + if ( Extra_TruthIsConst0( puOn, nVars ) ) + { + pcRes->nCubes = 0; + pcRes->pCubes = NULL; + Extra_TruthClear( pTemp, nVars ); + return pTemp; + } + if ( Extra_TruthIsConst1( puOnDc, nVars ) ) + { + pcRes->nCubes = 1; + pcRes->pCubes = Vec_IntFetch( vStore, 1 ); + if ( pcRes->pCubes == NULL ) + { + pcRes->nCubes = -1; + return NULL; + } + pcRes->pCubes[0] = 0; + Extra_TruthFill( pTemp, nVars ); + return pTemp; + } + assert( nVars > 0 ); + // find the topmost var + for ( Var = nVars-1; Var >= 0; Var-- ) + if ( Extra_TruthVarInSupport( puOn, nVars, Var ) || + Extra_TruthVarInSupport( puOnDc, nVars, Var ) ) + break; + assert( Var >= 0 ); + // consider a simple case when one-word computation can be used + if ( Var < 5 ) + { + unsigned uRes = Kit_TruthIsop5_rec( puOn[0], puOnDc[0], Var+1, pcRes, vStore ); + for ( i = 0; i < nWordsAll; i++ ) + pTemp[i] = uRes; + return pTemp; + } + assert( Var >= 5 ); + nWords = Extra_TruthWordNum( Var ); + // cofactor + puOn0 = puOn; puOn1 = puOn + nWords; + puOnDc0 = puOnDc; puOnDc1 = puOnDc + nWords; + pTemp0 = pTemp; pTemp1 = pTemp + nWords; + // solve for cofactors + Extra_TruthSharp( pTemp0, puOn0, puOnDc1, Var ); + puRes0 = Kit_TruthIsop_rec( pTemp0, puOnDc0, Var, pcRes0, vStore ); + if ( pcRes0->nCubes == -1 ) + { + pcRes->nCubes = -1; + return NULL; + } + Extra_TruthSharp( pTemp1, puOn1, puOnDc0, Var ); + puRes1 = Kit_TruthIsop_rec( pTemp1, puOnDc1, Var, pcRes1, vStore ); + if ( pcRes1->nCubes == -1 ) + { + pcRes->nCubes = -1; + return NULL; + } + Extra_TruthSharp( pTemp0, puOn0, puRes0, Var ); + Extra_TruthSharp( pTemp1, puOn1, puRes1, Var ); + Extra_TruthOr( pTemp0, pTemp0, pTemp1, Var ); + Extra_TruthAnd( pTemp1, puOnDc0, puOnDc1, Var ); + puRes2 = Kit_TruthIsop_rec( pTemp0, pTemp1, Var, pcRes2, vStore ); + if ( pcRes2->nCubes == -1 ) + { + pcRes->nCubes = -1; + return NULL; + } + // create the resulting cover + pcRes->nCubes = pcRes0->nCubes + pcRes1->nCubes + pcRes2->nCubes; + pcRes->pCubes = Vec_IntFetch( vStore, pcRes->nCubes ); + if ( pcRes->pCubes == NULL ) + { + pcRes->nCubes = -1; + return NULL; + } + k = 0; + for ( i = 0; i < pcRes0->nCubes; i++ ) + pcRes->pCubes[k++] = pcRes0->pCubes[i] | (1 << ((Var<<1)+0)); + for ( i = 0; i < pcRes1->nCubes; i++ ) + pcRes->pCubes[k++] = pcRes1->pCubes[i] | (1 << ((Var<<1)+1)); + for ( i = 0; i < pcRes2->nCubes; i++ ) + pcRes->pCubes[k++] = pcRes2->pCubes[i]; + assert( k == pcRes->nCubes ); + // create the resulting truth table + Extra_TruthOr( pTemp0, puRes0, puRes2, Var ); + Extra_TruthOr( pTemp1, puRes1, puRes2, Var ); + // copy the table if needed + nWords <<= 1; + for ( i = 1; i < nWordsAll/nWords; i++ ) + for ( k = 0; k < nWords; k++ ) + pTemp[i*nWords + k] = pTemp[k]; + // verify in the end +// assert( Extra_TruthIsImply( puOn, pTemp, nVars ) ); +// assert( Extra_TruthIsImply( pTemp, puOnDc, nVars ) ); + return pTemp; +} + +/**Function************************************************************* + + Synopsis [Computes ISOP for 5 variables or less.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Kit_TruthIsop5_rec( unsigned uOn, unsigned uOnDc, int nVars, Kit_Sop_t * pcRes, Vec_Int_t * vStore ) +{ + unsigned uMasks[5] = { 0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, 0xFF00FF00, 0xFFFF0000 }; + Kit_Sop_t cRes0, cRes1, cRes2; + Kit_Sop_t * pcRes0 = &cRes0, * pcRes1 = &cRes1, * pcRes2 = &cRes2; + unsigned uOn0, uOn1, uOnDc0, uOnDc1, uRes0, uRes1, uRes2; + int i, k, Var; + assert( nVars <= 5 ); + assert( (uOn & ~uOnDc) == 0 ); + if ( uOn == 0 ) + { + pcRes->nCubes = 0; + pcRes->pCubes = NULL; + return 0; + } + if ( uOnDc == 0xFFFFFFFF ) + { + pcRes->nCubes = 1; + pcRes->pCubes = Vec_IntFetch( vStore, 1 ); + if ( pcRes->pCubes == NULL ) + { + pcRes->nCubes = -1; + return 0; + } + pcRes->pCubes[0] = 0; + return 0xFFFFFFFF; + } + assert( nVars > 0 ); + // find the topmost var + for ( Var = nVars-1; Var >= 0; Var-- ) + if ( Extra_TruthVarInSupport( &uOn, 5, Var ) || + Extra_TruthVarInSupport( &uOnDc, 5, Var ) ) + break; + assert( Var >= 0 ); + // cofactor + uOn0 = uOn1 = uOn; + uOnDc0 = uOnDc1 = uOnDc; + Extra_TruthCofactor0( &uOn0, Var + 1, Var ); + Extra_TruthCofactor1( &uOn1, Var + 1, Var ); + Extra_TruthCofactor0( &uOnDc0, Var + 1, Var ); + Extra_TruthCofactor1( &uOnDc1, Var + 1, Var ); + // solve for cofactors + uRes0 = Kit_TruthIsop5_rec( uOn0 & ~uOnDc1, uOnDc0, Var, pcRes0, vStore ); + if ( pcRes0->nCubes == -1 ) + { + pcRes->nCubes = -1; + return 0; + } + uRes1 = Kit_TruthIsop5_rec( uOn1 & ~uOnDc0, uOnDc1, Var, pcRes1, vStore ); + if ( pcRes1->nCubes == -1 ) + { + pcRes->nCubes = -1; + return 0; + } + uRes2 = Kit_TruthIsop5_rec( (uOn0 & ~uRes0) | (uOn1 & ~uRes1), uOnDc0 & uOnDc1, Var, pcRes2, vStore ); + if ( pcRes2->nCubes == -1 ) + { + pcRes->nCubes = -1; + return 0; + } + // create the resulting cover + pcRes->nCubes = pcRes0->nCubes + pcRes1->nCubes + pcRes2->nCubes; + pcRes->pCubes = Vec_IntFetch( vStore, pcRes->nCubes ); + if ( pcRes->pCubes == NULL ) + { + pcRes->nCubes = -1; + return 0; + } + k = 0; + for ( i = 0; i < pcRes0->nCubes; i++ ) + pcRes->pCubes[k++] = pcRes0->pCubes[i] | (1 << ((Var<<1)+0)); + for ( i = 0; i < pcRes1->nCubes; i++ ) + pcRes->pCubes[k++] = pcRes1->pCubes[i] | (1 << ((Var<<1)+1)); + for ( i = 0; i < pcRes2->nCubes; i++ ) + pcRes->pCubes[k++] = pcRes2->pCubes[i]; + assert( k == pcRes->nCubes ); + // derive the final truth table + uRes2 |= (uRes0 & ~uMasks[Var]) | (uRes1 & uMasks[Var]); +// assert( (uOn & ~uRes2) == 0 ); +// assert( (uRes2 & ~uOnDc) == 0 ); + return uRes2; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/kit/kitSop.c b/abc_with_bb_support/src/aig/kit/kitSop.c new file mode 100644 index 000000000..139971fc7 --- /dev/null +++ b/abc_with_bb_support/src/aig/kit/kitSop.c @@ -0,0 +1,570 @@ +/**CFile**************************************************************** + + FileName [kitSop.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Computation kit.] + + Synopsis [Procedures involving SOPs.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Dec 6, 2006.] + + Revision [$Id: kitSop.c,v 1.00 2006/12/06 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "kit.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Creates SOP from the cube array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_SopCreate( Kit_Sop_t * cResult, Vec_Int_t * vInput, int nVars, Vec_Int_t * vMemory ) +{ + unsigned uCube; + int i; + // start the cover + cResult->nCubes = 0; + cResult->pCubes = Vec_IntFetch( vMemory, Vec_IntSize(vInput) ); + // add the cubes + Vec_IntForEachEntry( vInput, uCube, i ) + Kit_SopPushCube( cResult, uCube ); +} + +/**Function************************************************************* + + Synopsis [Creates SOP from the cube array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_SopCreateInverse( Kit_Sop_t * cResult, Vec_Int_t * vInput, int nLits, Vec_Int_t * vMemory ) +{ + unsigned uCube, uMask = 0; + int i, nCubes = Vec_IntSize(vInput); + // start the cover + cResult->nCubes = 0; + cResult->pCubes = Vec_IntFetch( vMemory, nCubes ); + // add the cubes +// Vec_IntForEachEntry( vInput, uCube, i ) + for ( i = 0; i < nCubes; i++ ) + { + uCube = Vec_IntEntry( vInput, i ); + uMask = ((uCube | (uCube >> 1)) & 0x55555555); + uMask |= (uMask << 1); + Kit_SopPushCube( cResult, uCube ^ uMask ); + } +} + +/**Function************************************************************* + + Synopsis [Duplicates SOP.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_SopDup( Kit_Sop_t * cResult, Kit_Sop_t * cSop, Vec_Int_t * vMemory ) +{ + unsigned uCube; + int i; + // start the cover + cResult->nCubes = 0; + cResult->pCubes = Vec_IntFetch( vMemory, Kit_SopCubeNum(cSop) ); + // add the cubes + Kit_SopForEachCube( cSop, uCube, i ) + Kit_SopPushCube( cResult, uCube ); +} + +/**Function************************************************************* + + Synopsis [Derives the quotient of division by literal.] + + Description [Reduces the cover to be equal to the result of + division of the given cover by the literal.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_SopDivideByLiteralQuo( Kit_Sop_t * cSop, int iLit ) +{ + unsigned uCube; + int i, k = 0; + Kit_SopForEachCube( cSop, uCube, i ) + { + if ( Kit_CubeHasLit(uCube, iLit) ) + Kit_SopWriteCube( cSop, Kit_CubeRemLit(uCube, iLit), k++ ); + } + Kit_SopShrink( cSop, k ); +} + + +/**Function************************************************************* + + Synopsis [Divides cover by one cube.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_SopDivideByCube( Kit_Sop_t * cSop, Kit_Sop_t * cDiv, Kit_Sop_t * vQuo, Kit_Sop_t * vRem, Vec_Int_t * vMemory ) +{ + unsigned uCube, uDiv; + int i; + // get the only cube + assert( Kit_SopCubeNum(cDiv) == 1 ); + uDiv = Kit_SopCube(cDiv, 0); + // allocate covers + vQuo->nCubes = 0; + vQuo->pCubes = Vec_IntFetch( vMemory, Kit_SopCubeNum(cSop) ); + vRem->nCubes = 0; + vRem->pCubes = Vec_IntFetch( vMemory, Kit_SopCubeNum(cSop) ); + // sort the cubes + Kit_SopForEachCube( cSop, uCube, i ) + { + if ( Kit_CubeContains( uCube, uDiv ) ) + Kit_SopPushCube( vQuo, Kit_CubeSharp(uCube, uDiv) ); + else + Kit_SopPushCube( vRem, uCube ); + } +} + +/**Function************************************************************* + + Synopsis [Divides cover by one cube.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_SopDivideInternal( Kit_Sop_t * cSop, Kit_Sop_t * cDiv, Kit_Sop_t * vQuo, Kit_Sop_t * vRem, Vec_Int_t * vMemory ) +{ + unsigned uCube, uDiv, uCube2, uDiv2, uQuo; + int i, i2, k, k2, nCubesRem; + assert( Kit_SopCubeNum(cSop) >= Kit_SopCubeNum(cDiv) ); + // consider special case + if ( Kit_SopCubeNum(cDiv) == 1 ) + { + Kit_SopDivideByCube( cSop, cDiv, vQuo, vRem, vMemory ); + return; + } + // allocate quotient + vQuo->nCubes = 0; + vQuo->pCubes = Vec_IntFetch( vMemory, Kit_SopCubeNum(cSop) / Kit_SopCubeNum(cDiv) ); + // for each cube of the cover + // it either belongs to the quotient or to the remainder + Kit_SopForEachCube( cSop, uCube, i ) + { + // skip taken cubes + if ( Kit_CubeIsMarked(uCube) ) + continue; + // find a matching cube in the divisor + Kit_SopForEachCube( cDiv, uDiv, k ) + if ( Kit_CubeContains( uCube, uDiv ) ) + break; + // the cube is not found + if ( k == Kit_SopCubeNum(cDiv) ) + continue; + // the quotient cube exists + uQuo = Kit_CubeSharp( uCube, uDiv ); + // find corresponding cubes for other cubes of the divisor + Kit_SopForEachCube( cDiv, uDiv2, k2 ) + { + if ( k2 == k ) + continue; + // find a matching cube + Kit_SopForEachCube( cSop, uCube2, i2 ) + { + // skip taken cubes + if ( Kit_CubeIsMarked(uCube2) ) + continue; + // check if the cube can be used + if ( Kit_CubeContains( uCube2, uDiv2 ) && uQuo == Kit_CubeSharp( uCube2, uDiv2 ) ) + break; + } + // the case when the cube is not found + if ( i2 == Kit_SopCubeNum(cSop) ) + break; + } + // we did not find some cubes - continue looking at other cubes + if ( k2 != Kit_SopCubeNum(cDiv) ) + continue; + // we found all cubes - add the quotient cube + Kit_SopPushCube( vQuo, uQuo ); + + // mark the first cube + Kit_SopWriteCube( cSop, Kit_CubeMark(uCube), i ); + // mark other cubes that have this quotient + Kit_SopForEachCube( cDiv, uDiv2, k2 ) + { + if ( k2 == k ) + continue; + // find a matching cube + Kit_SopForEachCube( cSop, uCube2, i2 ) + { + // skip taken cubes + if ( Kit_CubeIsMarked(uCube2) ) + continue; + // check if the cube can be used + if ( Kit_CubeContains( uCube2, uDiv2 ) && uQuo == Kit_CubeSharp( uCube2, uDiv2 ) ) + break; + } + assert( i2 < Kit_SopCubeNum(cSop) ); + // the cube is found, mark it + // (later we will add all unmarked cubes to the remainder) + Kit_SopWriteCube( cSop, Kit_CubeMark(uCube2), i2 ); + } + } + // determine the number of cubes in the remainder + nCubesRem = Kit_SopCubeNum(cSop) - Kit_SopCubeNum(vQuo) * Kit_SopCubeNum(cDiv); + // allocate remainder + vRem->nCubes = 0; + vRem->pCubes = Vec_IntFetch( vMemory, nCubesRem ); + // finally add the remaining unmarked cubes to the remainder + // and clean the marked cubes in the cover + Kit_SopForEachCube( cSop, uCube, i ) + { + if ( !Kit_CubeIsMarked(uCube) ) + { + Kit_SopPushCube( vRem, uCube ); + continue; + } + Kit_SopWriteCube( cSop, Kit_CubeUnmark(uCube), i ); + } + assert( nCubesRem == Kit_SopCubeNum(vRem) ); +} + +/**Function************************************************************* + + Synopsis [Returns the common cube.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Kit_SopCommonCube( Kit_Sop_t * cSop ) +{ + unsigned uMask, uCube; + int i; + uMask = ~(unsigned)0; + Kit_SopForEachCube( cSop, uCube, i ) + uMask &= uCube; + return uMask; +} + +/**Function************************************************************* + + Synopsis [Makes the cover cube-free.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_SopMakeCubeFree( Kit_Sop_t * cSop ) +{ + unsigned uMask, uCube; + int i; + uMask = Kit_SopCommonCube( cSop ); + if ( uMask == 0 ) + return; + // remove the common cube + Kit_SopForEachCube( cSop, uCube, i ) + Kit_SopWriteCube( cSop, Kit_CubeSharp(uCube, uMask), i ); +} + +/**Function************************************************************* + + Synopsis [Checks if the cover is cube-free.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_SopIsCubeFree( Kit_Sop_t * cSop ) +{ + return Kit_SopCommonCube( cSop ) == 0; +} + +/**Function************************************************************* + + Synopsis [Creates SOP composes of the common cube of the given SOP.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_SopCommonCubeCover( Kit_Sop_t * cResult, Kit_Sop_t * cSop, Vec_Int_t * vMemory ) +{ + assert( Kit_SopCubeNum(cSop) > 0 ); + cResult->nCubes = 0; + cResult->pCubes = Vec_IntFetch( vMemory, 1 ); + Kit_SopPushCube( cResult, Kit_SopCommonCube(cSop) ); +} + + +/**Function************************************************************* + + Synopsis [Find any literal that occurs more than once.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_SopAnyLiteral( Kit_Sop_t * cSop, int nLits ) +{ + unsigned uCube; + int i, k, nLitsCur; + // go through each literal + for ( i = 0; i < nLits; i++ ) + { + // go through all the cubes + nLitsCur = 0; + Kit_SopForEachCube( cSop, uCube, k ) + if ( Kit_CubeHasLit(uCube, i) ) + nLitsCur++; + if ( nLitsCur > 1 ) + return i; + } + return -1; +} + +/**Function************************************************************* + + Synopsis [Find the least often occurring literal.] + + Description [Find the least often occurring literal among those + that occur more than once.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_SopWorstLiteral( Kit_Sop_t * cSop, int nLits ) +{ + unsigned uCube; + int i, k, iMin, nLitsMin, nLitsCur; + int fUseFirst = 1; + + // go through each literal + iMin = -1; + nLitsMin = 1000000; + for ( i = 0; i < nLits; i++ ) + { + // go through all the cubes + nLitsCur = 0; + Kit_SopForEachCube( cSop, uCube, k ) + if ( Kit_CubeHasLit(uCube, i) ) + nLitsCur++; + // skip the literal that does not occur or occurs once + if ( nLitsCur < 2 ) + continue; + // check if this is the best literal + if ( fUseFirst ) + { + if ( nLitsMin > nLitsCur ) + { + nLitsMin = nLitsCur; + iMin = i; + } + } + else + { + if ( nLitsMin >= nLitsCur ) + { + nLitsMin = nLitsCur; + iMin = i; + } + } + } + if ( nLitsMin < 1000000 ) + return iMin; + return -1; +} + +/**Function************************************************************* + + Synopsis [Find the least often occurring literal.] + + Description [Find the least often occurring literal among those + that occur more than once.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_SopBestLiteral( Kit_Sop_t * cSop, int nLits, unsigned uMask ) +{ + unsigned uCube; + int i, k, iMax, nLitsMax, nLitsCur; + int fUseFirst = 1; + + // go through each literal + iMax = -1; + nLitsMax = -1; + for ( i = 0; i < nLits; i++ ) + { + if ( !Kit_CubeHasLit(uMask, i) ) + continue; + // go through all the cubes + nLitsCur = 0; + Kit_SopForEachCube( cSop, uCube, k ) + if ( Kit_CubeHasLit(uCube, i) ) + nLitsCur++; + // skip the literal that does not occur or occurs once + if ( nLitsCur < 2 ) + continue; + // check if this is the best literal + if ( fUseFirst ) + { + if ( nLitsMax < nLitsCur ) + { + nLitsMax = nLitsCur; + iMax = i; + } + } + else + { + if ( nLitsMax <= nLitsCur ) + { + nLitsMax = nLitsCur; + iMax = i; + } + } + } + if ( nLitsMax >= 0 ) + return iMax; + return -1; +} + +/**Function************************************************************* + + Synopsis [Computes a level-zero kernel.] + + Description [Modifies the cover to contain one level-zero kernel.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_SopDivisorZeroKernel_rec( Kit_Sop_t * cSop, int nLits ) +{ + int iLit; + // find any literal that occurs at least two times + iLit = Kit_SopWorstLiteral( cSop, nLits ); + if ( iLit == -1 ) + return; + // derive the cube-free quotient + Kit_SopDivideByLiteralQuo( cSop, iLit ); // the same cover + Kit_SopMakeCubeFree( cSop ); // the same cover + // call recursively + Kit_SopDivisorZeroKernel_rec( cSop, nLits ); // the same cover +} + +/**Function************************************************************* + + Synopsis [Computes the quick divisor of the cover.] + + Description [Returns 0, if there is no divisor other than trivial.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_SopDivisor( Kit_Sop_t * cResult, Kit_Sop_t * cSop, int nLits, Vec_Int_t * vMemory ) +{ + if ( Kit_SopCubeNum(cSop) <= 1 ) + return 0; + if ( Kit_SopAnyLiteral( cSop, nLits ) == -1 ) + return 0; + // duplicate the cover + Kit_SopDup( cResult, cSop, vMemory ); + // perform the kerneling + Kit_SopDivisorZeroKernel_rec( cResult, nLits ); + assert( Kit_SopCubeNum(cResult) > 0 ); + return 1; +} + + +/**Function************************************************************* + + Synopsis [Create the one-literal cover with the best literal from cSop.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_SopBestLiteralCover( Kit_Sop_t * cResult, Kit_Sop_t * cSop, unsigned uCube, int nLits, Vec_Int_t * vMemory ) +{ + int iLitBest; + // get the best literal + iLitBest = Kit_SopBestLiteral( cSop, nLits, uCube ); + // start the cover + cResult->nCubes = 0; + cResult->pCubes = Vec_IntFetch( vMemory, 1 ); + // set the cube + Kit_SopPushCube( cResult, Kit_CubeSetLit(0, iLitBest) ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/kit/kitTruth.c b/abc_with_bb_support/src/aig/kit/kitTruth.c new file mode 100644 index 000000000..5cb4b6a84 --- /dev/null +++ b/abc_with_bb_support/src/aig/kit/kitTruth.c @@ -0,0 +1,1640 @@ +/**CFile**************************************************************** + + FileName [kitTruth.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Computation kit.] + + Synopsis [Procedures involving truth tables.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Dec 6, 2006.] + + Revision [$Id: kitTruth.c,v 1.00 2006/12/06 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "kit.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static unsigned s_VarMasks[5][2] = { + { 0x33333333, 0xAAAAAAAA }, + { 0x55555555, 0xCCCCCCCC }, + { 0x0F0F0F0F, 0xF0F0F0F0 }, + { 0x00FF00FF, 0xFF00FF00 }, + { 0x0000FFFF, 0xFFFF0000 } +}; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Swaps two adjacent variables in the truth table.] + + Description [Swaps var number Start and var number Start+1 (0-based numbers). + The input truth table is pIn. The output truth table is pOut.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_TruthSwapAdjacentVars( unsigned * pOut, unsigned * pIn, int nVars, int iVar ) +{ + static unsigned PMasks[4][3] = { + { 0x99999999, 0x22222222, 0x44444444 }, + { 0xC3C3C3C3, 0x0C0C0C0C, 0x30303030 }, + { 0xF00FF00F, 0x00F000F0, 0x0F000F00 }, + { 0xFF0000FF, 0x0000FF00, 0x00FF0000 } + }; + int nWords = Kit_TruthWordNum( nVars ); + int i, k, Step, Shift; + + assert( iVar < nVars - 1 ); + if ( iVar < 4 ) + { + Shift = (1 << iVar); + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pIn[i] & PMasks[iVar][0]) | ((pIn[i] & PMasks[iVar][1]) << Shift) | ((pIn[i] & PMasks[iVar][2]) >> Shift); + } + else if ( iVar > 4 ) + { + Step = (1 << (iVar - 5)); + for ( k = 0; k < nWords; k += 4*Step ) + { + for ( i = 0; i < Step; i++ ) + pOut[i] = pIn[i]; + for ( i = 0; i < Step; i++ ) + pOut[Step+i] = pIn[2*Step+i]; + for ( i = 0; i < Step; i++ ) + pOut[2*Step+i] = pIn[Step+i]; + for ( i = 0; i < Step; i++ ) + pOut[3*Step+i] = pIn[3*Step+i]; + pIn += 4*Step; + pOut += 4*Step; + } + } + else // if ( iVar == 4 ) + { + for ( i = 0; i < nWords; i += 2 ) + { + pOut[i] = (pIn[i] & 0x0000FFFF) | ((pIn[i+1] & 0x0000FFFF) << 16); + pOut[i+1] = (pIn[i+1] & 0xFFFF0000) | ((pIn[i] & 0xFFFF0000) >> 16); + } + } +} + +/**Function************************************************************* + + Synopsis [Swaps two adjacent variables in the truth table.] + + Description [Swaps var number Start and var number Start+1 (0-based numbers). + The input truth table is pIn. The output truth table is pOut.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_TruthSwapAdjacentVars2( unsigned * pIn, unsigned * pOut, int nVars, int Start ) +{ + int nWords = (nVars <= 5)? 1 : (1 << (nVars-5)); + int i, k, Step; + + assert( Start < nVars - 1 ); + switch ( Start ) + { + case 0: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pIn[i] & 0x99999999) | ((pIn[i] & 0x22222222) << 1) | ((pIn[i] & 0x44444444) >> 1); + return; + case 1: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pIn[i] & 0xC3C3C3C3) | ((pIn[i] & 0x0C0C0C0C) << 2) | ((pIn[i] & 0x30303030) >> 2); + return; + case 2: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pIn[i] & 0xF00FF00F) | ((pIn[i] & 0x00F000F0) << 4) | ((pIn[i] & 0x0F000F00) >> 4); + return; + case 3: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pIn[i] & 0xFF0000FF) | ((pIn[i] & 0x0000FF00) << 8) | ((pIn[i] & 0x00FF0000) >> 8); + return; + case 4: + for ( i = 0; i < nWords; i += 2 ) + { + pOut[i] = (pIn[i] & 0x0000FFFF) | ((pIn[i+1] & 0x0000FFFF) << 16); + pOut[i+1] = (pIn[i+1] & 0xFFFF0000) | ((pIn[i] & 0xFFFF0000) >> 16); + } + return; + default: + Step = (1 << (Start - 5)); + for ( k = 0; k < nWords; k += 4*Step ) + { + for ( i = 0; i < Step; i++ ) + pOut[i] = pIn[i]; + for ( i = 0; i < Step; i++ ) + pOut[Step+i] = pIn[2*Step+i]; + for ( i = 0; i < Step; i++ ) + pOut[2*Step+i] = pIn[Step+i]; + for ( i = 0; i < Step; i++ ) + pOut[3*Step+i] = pIn[3*Step+i]; + pIn += 4*Step; + pOut += 4*Step; + } + return; + } +} + +/**Function************************************************************* + + Synopsis [Expands the truth table according to the phase.] + + Description [The input and output truth tables are in pIn/pOut. The current number + of variables is nVars. The total number of variables in nVarsAll. The last argument + (Phase) contains shows where the variables should go.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_TruthStretch( unsigned * pOut, unsigned * pIn, int nVars, int nVarsAll, unsigned Phase, int fReturnIn ) +{ + unsigned * pTemp; + int i, k, Var = nVars - 1, Counter = 0; + for ( i = nVarsAll - 1; i >= 0; i-- ) + if ( Phase & (1 << i) ) + { + for ( k = Var; k < i; k++ ) + { + Kit_TruthSwapAdjacentVars( pOut, pIn, nVarsAll, k ); + pTemp = pIn; pIn = pOut; pOut = pTemp; + Counter++; + } + Var--; + } + assert( Var == -1 ); + // swap if it was moved an even number of times + if ( fReturnIn ^ !(Counter & 1) ) + Kit_TruthCopy( pOut, pIn, nVarsAll ); +} + +/**Function************************************************************* + + Synopsis [Shrinks the truth table according to the phase.] + + Description [The input and output truth tables are in pIn/pOut. The current number + of variables is nVars. The total number of variables in nVarsAll. The last argument + (Phase) contains shows what variables should remain.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_TruthShrink( unsigned * pOut, unsigned * pIn, int nVars, int nVarsAll, unsigned Phase, int fReturnIn ) +{ + unsigned * pTemp; + int i, k, Var = 0, Counter = 0; + for ( i = 0; i < nVarsAll; i++ ) + if ( Phase & (1 << i) ) + { + for ( k = i-1; k >= Var; k-- ) + { + Kit_TruthSwapAdjacentVars( pOut, pIn, nVarsAll, k ); + pTemp = pIn; pIn = pOut; pOut = pTemp; + Counter++; + } + Var++; + } + assert( Var == nVars ); + // swap if it was moved an even number of times + if ( fReturnIn ^ !(Counter & 1) ) + Kit_TruthCopy( pOut, pIn, nVarsAll ); +} + + +/**Function************************************************************* + + Synopsis [Returns 1 if TT depends on the given variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_TruthVarInSupport( unsigned * pTruth, int nVars, int iVar ) +{ + int nWords = Kit_TruthWordNum( nVars ); + int i, k, Step; + + assert( iVar < nVars ); + switch ( iVar ) + { + case 0: + for ( i = 0; i < nWords; i++ ) + if ( (pTruth[i] & 0x55555555) != ((pTruth[i] & 0xAAAAAAAA) >> 1) ) + return 1; + return 0; + case 1: + for ( i = 0; i < nWords; i++ ) + if ( (pTruth[i] & 0x33333333) != ((pTruth[i] & 0xCCCCCCCC) >> 2) ) + return 1; + return 0; + case 2: + for ( i = 0; i < nWords; i++ ) + if ( (pTruth[i] & 0x0F0F0F0F) != ((pTruth[i] & 0xF0F0F0F0) >> 4) ) + return 1; + return 0; + case 3: + for ( i = 0; i < nWords; i++ ) + if ( (pTruth[i] & 0x00FF00FF) != ((pTruth[i] & 0xFF00FF00) >> 8) ) + return 1; + return 0; + case 4: + for ( i = 0; i < nWords; i++ ) + if ( (pTruth[i] & 0x0000FFFF) != ((pTruth[i] & 0xFFFF0000) >> 16) ) + return 1; + return 0; + default: + Step = (1 << (iVar - 5)); + for ( k = 0; k < nWords; k += 2*Step ) + { + for ( i = 0; i < Step; i++ ) + if ( pTruth[i] != pTruth[Step+i] ) + return 1; + pTruth += 2*Step; + } + return 0; + } +} + +/**Function************************************************************* + + Synopsis [Returns the number of support vars.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_TruthSupportSize( unsigned * pTruth, int nVars ) +{ + int i, Counter = 0; + for ( i = 0; i < nVars; i++ ) + Counter += Kit_TruthVarInSupport( pTruth, nVars, i ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Returns support of the function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Kit_TruthSupport( unsigned * pTruth, int nVars ) +{ + int i, Support = 0; + for ( i = 0; i < nVars; i++ ) + if ( Kit_TruthVarInSupport( pTruth, nVars, i ) ) + Support |= (1 << i); + return Support; +} + + + +/**Function************************************************************* + + Synopsis [Computes negative cofactor of the function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_TruthCofactor0( unsigned * pTruth, int nVars, int iVar ) +{ + int nWords = Kit_TruthWordNum( nVars ); + int i, k, Step; + + assert( iVar < nVars ); + switch ( iVar ) + { + case 0: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = (pTruth[i] & 0x55555555) | ((pTruth[i] & 0x55555555) << 1); + return; + case 1: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = (pTruth[i] & 0x33333333) | ((pTruth[i] & 0x33333333) << 2); + return; + case 2: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = (pTruth[i] & 0x0F0F0F0F) | ((pTruth[i] & 0x0F0F0F0F) << 4); + return; + case 3: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = (pTruth[i] & 0x00FF00FF) | ((pTruth[i] & 0x00FF00FF) << 8); + return; + case 4: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = (pTruth[i] & 0x0000FFFF) | ((pTruth[i] & 0x0000FFFF) << 16); + return; + default: + Step = (1 << (iVar - 5)); + for ( k = 0; k < nWords; k += 2*Step ) + { + for ( i = 0; i < Step; i++ ) + pTruth[Step+i] = pTruth[i]; + pTruth += 2*Step; + } + return; + } +} + +/**Function************************************************************* + + Synopsis [Computes positive cofactor of the function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_TruthCofactor1( unsigned * pTruth, int nVars, int iVar ) +{ + int nWords = Kit_TruthWordNum( nVars ); + int i, k, Step; + + assert( iVar < nVars ); + switch ( iVar ) + { + case 0: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = (pTruth[i] & 0xAAAAAAAA) | ((pTruth[i] & 0xAAAAAAAA) >> 1); + return; + case 1: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = (pTruth[i] & 0xCCCCCCCC) | ((pTruth[i] & 0xCCCCCCCC) >> 2); + return; + case 2: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = (pTruth[i] & 0xF0F0F0F0) | ((pTruth[i] & 0xF0F0F0F0) >> 4); + return; + case 3: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = (pTruth[i] & 0xFF00FF00) | ((pTruth[i] & 0xFF00FF00) >> 8); + return; + case 4: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = (pTruth[i] & 0xFFFF0000) | ((pTruth[i] & 0xFFFF0000) >> 16); + return; + default: + Step = (1 << (iVar - 5)); + for ( k = 0; k < nWords; k += 2*Step ) + { + for ( i = 0; i < Step; i++ ) + pTruth[i] = pTruth[Step+i]; + pTruth += 2*Step; + } + return; + } +} + +/**Function************************************************************* + + Synopsis [Computes positive cofactor of the function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_TruthCofactor0New( unsigned * pOut, unsigned * pIn, int nVars, int iVar ) +{ + int nWords = Kit_TruthWordNum( nVars ); + int i, k, Step; + + assert( iVar < nVars ); + switch ( iVar ) + { + case 0: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pIn[i] & 0x55555555) | ((pIn[i] & 0x55555555) << 1); + return; + case 1: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pIn[i] & 0x33333333) | ((pIn[i] & 0x33333333) << 2); + return; + case 2: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pIn[i] & 0x0F0F0F0F) | ((pIn[i] & 0x0F0F0F0F) << 4); + return; + case 3: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pIn[i] & 0x00FF00FF) | ((pIn[i] & 0x00FF00FF) << 8); + return; + case 4: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pIn[i] & 0x0000FFFF) | ((pIn[i] & 0x0000FFFF) << 16); + return; + default: + Step = (1 << (iVar - 5)); + for ( k = 0; k < nWords; k += 2*Step ) + { + for ( i = 0; i < Step; i++ ) + pOut[i] = pOut[Step+i] = pIn[i]; + pIn += 2*Step; + pOut += 2*Step; + } + return; + } +} + +/**Function************************************************************* + + Synopsis [Computes positive cofactor of the function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_TruthCofactor1New( unsigned * pOut, unsigned * pIn, int nVars, int iVar ) +{ + int nWords = Kit_TruthWordNum( nVars ); + int i, k, Step; + + assert( iVar < nVars ); + switch ( iVar ) + { + case 0: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pIn[i] & 0xAAAAAAAA) | ((pIn[i] & 0xAAAAAAAA) >> 1); + return; + case 1: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pIn[i] & 0xCCCCCCCC) | ((pIn[i] & 0xCCCCCCCC) >> 2); + return; + case 2: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pIn[i] & 0xF0F0F0F0) | ((pIn[i] & 0xF0F0F0F0) >> 4); + return; + case 3: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pIn[i] & 0xFF00FF00) | ((pIn[i] & 0xFF00FF00) >> 8); + return; + case 4: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pIn[i] & 0xFFFF0000) | ((pIn[i] & 0xFFFF0000) >> 16); + return; + default: + Step = (1 << (iVar - 5)); + for ( k = 0; k < nWords; k += 2*Step ) + { + for ( i = 0; i < Step; i++ ) + pOut[i] = pOut[Step+i] = pIn[Step+i]; + pIn += 2*Step; + pOut += 2*Step; + } + return; + } +} + + +/**Function************************************************************* + + Synopsis [Existentially quantifies the variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_TruthExist( unsigned * pTruth, int nVars, int iVar ) +{ + int nWords = Kit_TruthWordNum( nVars ); + int i, k, Step; + + assert( iVar < nVars ); + switch ( iVar ) + { + case 0: + for ( i = 0; i < nWords; i++ ) + pTruth[i] |= ((pTruth[i] & 0xAAAAAAAA) >> 1) | ((pTruth[i] & 0x55555555) << 1); + return; + case 1: + for ( i = 0; i < nWords; i++ ) + pTruth[i] |= ((pTruth[i] & 0xCCCCCCCC) >> 2) | ((pTruth[i] & 0x33333333) << 2); + return; + case 2: + for ( i = 0; i < nWords; i++ ) + pTruth[i] |= ((pTruth[i] & 0xF0F0F0F0) >> 4) | ((pTruth[i] & 0x0F0F0F0F) << 4); + return; + case 3: + for ( i = 0; i < nWords; i++ ) + pTruth[i] |= ((pTruth[i] & 0xFF00FF00) >> 8) | ((pTruth[i] & 0x00FF00FF) << 8); + return; + case 4: + for ( i = 0; i < nWords; i++ ) + pTruth[i] |= ((pTruth[i] & 0xFFFF0000) >> 16) | ((pTruth[i] & 0x0000FFFF) << 16); + return; + default: + Step = (1 << (iVar - 5)); + for ( k = 0; k < nWords; k += 2*Step ) + { + for ( i = 0; i < Step; i++ ) + { + pTruth[i] |= pTruth[Step+i]; + pTruth[Step+i] = pTruth[i]; + } + pTruth += 2*Step; + } + return; + } +} + +/**Function************************************************************* + + Synopsis [Existentially quantifies the variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_TruthExistNew( unsigned * pRes, unsigned * pTruth, int nVars, int iVar ) +{ + int nWords = Kit_TruthWordNum( nVars ); + int i, k, Step; + + assert( iVar < nVars ); + switch ( iVar ) + { + case 0: + for ( i = 0; i < nWords; i++ ) + pRes[i] = pTruth[i] | ((pTruth[i] & 0xAAAAAAAA) >> 1) | ((pTruth[i] & 0x55555555) << 1); + return; + case 1: + for ( i = 0; i < nWords; i++ ) + pRes[i] = pTruth[i] | ((pTruth[i] & 0xCCCCCCCC) >> 2) | ((pTruth[i] & 0x33333333) << 2); + return; + case 2: + for ( i = 0; i < nWords; i++ ) + pRes[i] = pTruth[i] | ((pTruth[i] & 0xF0F0F0F0) >> 4) | ((pTruth[i] & 0x0F0F0F0F) << 4); + return; + case 3: + for ( i = 0; i < nWords; i++ ) + pRes[i] = pTruth[i] | ((pTruth[i] & 0xFF00FF00) >> 8) | ((pTruth[i] & 0x00FF00FF) << 8); + return; + case 4: + for ( i = 0; i < nWords; i++ ) + pRes[i] = pTruth[i] | ((pTruth[i] & 0xFFFF0000) >> 16) | ((pTruth[i] & 0x0000FFFF) << 16); + return; + default: + Step = (1 << (iVar - 5)); + for ( k = 0; k < nWords; k += 2*Step ) + { + for ( i = 0; i < Step; i++ ) + { + pRes[i] = pTruth[i] | pTruth[Step+i]; + pRes[Step+i] = pRes[i]; + } + pRes += 2*Step; + pTruth += 2*Step; + } + return; + } +} + +/**Function************************************************************* + + Synopsis [Existantially quantifies the set of variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_TruthExistSet( unsigned * pRes, unsigned * pTruth, int nVars, unsigned uMask ) +{ + int v; + Kit_TruthCopy( pRes, pTruth, nVars ); + for ( v = 0; v < nVars; v++ ) + if ( uMask & (1 << v) ) + Kit_TruthExist( pRes, nVars, v ); +} + +/**Function************************************************************* + + Synopsis [Unversally quantifies the variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_TruthForall( unsigned * pTruth, int nVars, int iVar ) +{ + int nWords = Kit_TruthWordNum( nVars ); + int i, k, Step; + + assert( iVar < nVars ); + switch ( iVar ) + { + case 0: + for ( i = 0; i < nWords; i++ ) + pTruth[i] &= ((pTruth[i] & 0xAAAAAAAA) >> 1) | ((pTruth[i] & 0x55555555) << 1); + return; + case 1: + for ( i = 0; i < nWords; i++ ) + pTruth[i] &= ((pTruth[i] & 0xCCCCCCCC) >> 2) | ((pTruth[i] & 0x33333333) << 2); + return; + case 2: + for ( i = 0; i < nWords; i++ ) + pTruth[i] &= ((pTruth[i] & 0xF0F0F0F0) >> 4) | ((pTruth[i] & 0x0F0F0F0F) << 4); + return; + case 3: + for ( i = 0; i < nWords; i++ ) + pTruth[i] &= ((pTruth[i] & 0xFF00FF00) >> 8) | ((pTruth[i] & 0x00FF00FF) << 8); + return; + case 4: + for ( i = 0; i < nWords; i++ ) + pTruth[i] &= ((pTruth[i] & 0xFFFF0000) >> 16) | ((pTruth[i] & 0x0000FFFF) << 16); + return; + default: + Step = (1 << (iVar - 5)); + for ( k = 0; k < nWords; k += 2*Step ) + { + for ( i = 0; i < Step; i++ ) + { + pTruth[i] &= pTruth[Step+i]; + pTruth[Step+i] = pTruth[i]; + } + pTruth += 2*Step; + } + return; + } +} + +/**Function************************************************************* + + Synopsis [Universally quantifies the variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_TruthForallNew( unsigned * pRes, unsigned * pTruth, int nVars, int iVar ) +{ + int nWords = Kit_TruthWordNum( nVars ); + int i, k, Step; + + assert( iVar < nVars ); + switch ( iVar ) + { + case 0: + for ( i = 0; i < nWords; i++ ) + pRes[i] = pTruth[i] & (((pTruth[i] & 0xAAAAAAAA) >> 1) | ((pTruth[i] & 0x55555555) << 1)); + return; + case 1: + for ( i = 0; i < nWords; i++ ) + pRes[i] = pTruth[i] & (((pTruth[i] & 0xCCCCCCCC) >> 2) | ((pTruth[i] & 0x33333333) << 2)); + return; + case 2: + for ( i = 0; i < nWords; i++ ) + pRes[i] = pTruth[i] & (((pTruth[i] & 0xF0F0F0F0) >> 4) | ((pTruth[i] & 0x0F0F0F0F) << 4)); + return; + case 3: + for ( i = 0; i < nWords; i++ ) + pRes[i] = pTruth[i] & (((pTruth[i] & 0xFF00FF00) >> 8) | ((pTruth[i] & 0x00FF00FF) << 8)); + return; + case 4: + for ( i = 0; i < nWords; i++ ) + pRes[i] = pTruth[i] & (((pTruth[i] & 0xFFFF0000) >> 16) | ((pTruth[i] & 0x0000FFFF) << 16)); + return; + default: + Step = (1 << (iVar - 5)); + for ( k = 0; k < nWords; k += 2*Step ) + { + for ( i = 0; i < Step; i++ ) + { + pRes[i] = pTruth[i] & pTruth[Step+i]; + pRes[Step+i] = pRes[i]; + } + pRes += 2*Step; + pTruth += 2*Step; + } + return; + } +} + +/**Function************************************************************* + + Synopsis [Universally quantifies the variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_TruthUniqueNew( unsigned * pRes, unsigned * pTruth, int nVars, int iVar ) +{ + int nWords = Kit_TruthWordNum( nVars ); + int i, k, Step; + + assert( iVar < nVars ); + switch ( iVar ) + { + case 0: + for ( i = 0; i < nWords; i++ ) + pRes[i] = pTruth[i] ^ (((pTruth[i] & 0xAAAAAAAA) >> 1) | ((pTruth[i] & 0x55555555) << 1)); + return; + case 1: + for ( i = 0; i < nWords; i++ ) + pRes[i] = pTruth[i] ^ (((pTruth[i] & 0xCCCCCCCC) >> 2) | ((pTruth[i] & 0x33333333) << 2)); + return; + case 2: + for ( i = 0; i < nWords; i++ ) + pRes[i] = pTruth[i] ^ (((pTruth[i] & 0xF0F0F0F0) >> 4) | ((pTruth[i] & 0x0F0F0F0F) << 4)); + return; + case 3: + for ( i = 0; i < nWords; i++ ) + pRes[i] = pTruth[i] ^ (((pTruth[i] & 0xFF00FF00) >> 8) | ((pTruth[i] & 0x00FF00FF) << 8)); + return; + case 4: + for ( i = 0; i < nWords; i++ ) + pRes[i] = pTruth[i] ^ (((pTruth[i] & 0xFFFF0000) >> 16) | ((pTruth[i] & 0x0000FFFF) << 16)); + return; + default: + Step = (1 << (iVar - 5)); + for ( k = 0; k < nWords; k += 2*Step ) + { + for ( i = 0; i < Step; i++ ) + { + pRes[i] = pTruth[i] ^ pTruth[Step+i]; + pRes[Step+i] = pRes[i]; + } + pRes += 2*Step; + pTruth += 2*Step; + } + return; + } +} + +/**Function************************************************************* + + Synopsis [Universally quantifies the set of variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_TruthForallSet( unsigned * pRes, unsigned * pTruth, int nVars, unsigned uMask ) +{ + int v; + Kit_TruthCopy( pRes, pTruth, nVars ); + for ( v = 0; v < nVars; v++ ) + if ( uMask & (1 << v) ) + Kit_TruthForall( pRes, nVars, v ); +} + + +/**Function************************************************************* + + Synopsis [Computes negative cofactor of the function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_TruthMuxVar( unsigned * pOut, unsigned * pCof0, unsigned * pCof1, int nVars, int iVar ) +{ + int nWords = Kit_TruthWordNum( nVars ); + int i, k, Step; + + assert( iVar < nVars ); + switch ( iVar ) + { + case 0: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pCof0[i] & 0x55555555) | (pCof1[i] & 0xAAAAAAAA); + return; + case 1: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pCof0[i] & 0x33333333) | (pCof1[i] & 0xCCCCCCCC); + return; + case 2: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pCof0[i] & 0x0F0F0F0F) | (pCof1[i] & 0xF0F0F0F0); + return; + case 3: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pCof0[i] & 0x00FF00FF) | (pCof1[i] & 0xFF00FF00); + return; + case 4: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pCof0[i] & 0x0000FFFF) | (pCof1[i] & 0xFFFF0000); + return; + default: + Step = (1 << (iVar - 5)); + for ( k = 0; k < nWords; k += 2*Step ) + { + for ( i = 0; i < Step; i++ ) + { + pOut[i] = pCof0[i]; + pOut[Step+i] = pCof1[Step+i]; + } + pOut += 2*Step; + pCof0 += 2*Step; + pCof1 += 2*Step; + } + return; + } +} + +/**Function************************************************************* + + Synopsis [Checks symmetry of two variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_TruthVarsSymm( unsigned * pTruth, int nVars, int iVar0, int iVar1 ) +{ + static unsigned uTemp0[16], uTemp1[16]; + assert( nVars <= 9 ); + // compute Cof01 + Kit_TruthCopy( uTemp0, pTruth, nVars ); + Kit_TruthCofactor0( uTemp0, nVars, iVar0 ); + Kit_TruthCofactor1( uTemp0, nVars, iVar1 ); + // compute Cof10 + Kit_TruthCopy( uTemp1, pTruth, nVars ); + Kit_TruthCofactor1( uTemp1, nVars, iVar0 ); + Kit_TruthCofactor0( uTemp1, nVars, iVar1 ); + // compare + return Kit_TruthIsEqual( uTemp0, uTemp1, nVars ); +} + +/**Function************************************************************* + + Synopsis [Checks antisymmetry of two variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_TruthVarsAntiSymm( unsigned * pTruth, int nVars, int iVar0, int iVar1 ) +{ + static unsigned uTemp0[16], uTemp1[16]; + assert( nVars <= 9 ); + // compute Cof00 + Kit_TruthCopy( uTemp0, pTruth, nVars ); + Kit_TruthCofactor0( uTemp0, nVars, iVar0 ); + Kit_TruthCofactor0( uTemp0, nVars, iVar1 ); + // compute Cof11 + Kit_TruthCopy( uTemp1, pTruth, nVars ); + Kit_TruthCofactor1( uTemp1, nVars, iVar0 ); + Kit_TruthCofactor1( uTemp1, nVars, iVar1 ); + // compare + return Kit_TruthIsEqual( uTemp0, uTemp1, nVars ); +} + +/**Function************************************************************* + + Synopsis [Changes phase of the function w.r.t. one variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_TruthChangePhase( unsigned * pTruth, int nVars, int iVar ) +{ + int nWords = Kit_TruthWordNum( nVars ); + int i, k, Step; + unsigned Temp; + + assert( iVar < nVars ); + switch ( iVar ) + { + case 0: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = ((pTruth[i] & 0x55555555) << 1) | ((pTruth[i] & 0xAAAAAAAA) >> 1); + return; + case 1: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = ((pTruth[i] & 0x33333333) << 2) | ((pTruth[i] & 0xCCCCCCCC) >> 2); + return; + case 2: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = ((pTruth[i] & 0x0F0F0F0F) << 4) | ((pTruth[i] & 0xF0F0F0F0) >> 4); + return; + case 3: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = ((pTruth[i] & 0x00FF00FF) << 8) | ((pTruth[i] & 0xFF00FF00) >> 8); + return; + case 4: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = ((pTruth[i] & 0x0000FFFF) << 16) | ((pTruth[i] & 0xFFFF0000) >> 16); + return; + default: + Step = (1 << (iVar - 5)); + for ( k = 0; k < nWords; k += 2*Step ) + { + for ( i = 0; i < Step; i++ ) + { + Temp = pTruth[i]; + pTruth[i] = pTruth[Step+i]; + pTruth[Step+i] = Temp; + } + pTruth += 2*Step; + } + return; + } +} + +/**Function************************************************************* + + Synopsis [Computes minimum overlap in supports of cofactors.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_TruthMinCofSuppOverlap( unsigned * pTruth, int nVars, int * pVarMin ) +{ + static unsigned uCofactor[16]; + int i, ValueCur, ValueMin, VarMin; + unsigned uSupp0, uSupp1; + int nVars0, nVars1; + assert( nVars <= 9 ); + ValueMin = 32; + VarMin = -1; + for ( i = 0; i < nVars; i++ ) + { + // get negative cofactor + Kit_TruthCopy( uCofactor, pTruth, nVars ); + Kit_TruthCofactor0( uCofactor, nVars, i ); + uSupp0 = Kit_TruthSupport( uCofactor, nVars ); + nVars0 = Kit_WordCountOnes( uSupp0 ); +//Kit_PrintBinary( stdout, &uSupp0, 8 ); printf( "\n" ); + // get positive cofactor + Kit_TruthCopy( uCofactor, pTruth, nVars ); + Kit_TruthCofactor1( uCofactor, nVars, i ); + uSupp1 = Kit_TruthSupport( uCofactor, nVars ); + nVars1 = Kit_WordCountOnes( uSupp1 ); +//Kit_PrintBinary( stdout, &uSupp1, 8 ); printf( "\n" ); + // get the number of common vars + ValueCur = Kit_WordCountOnes( uSupp0 & uSupp1 ); + if ( ValueMin > ValueCur && nVars0 <= 5 && nVars1 <= 5 ) + { + ValueMin = ValueCur; + VarMin = i; + } + if ( ValueMin == 0 ) + break; + } + if ( pVarMin ) + *pVarMin = VarMin; + return ValueMin; +} + + +/**Function************************************************************* + + Synopsis [Find the best cofactoring variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_TruthBestCofVar( unsigned * pTruth, int nVars, unsigned * pCof0, unsigned * pCof1 ) +{ + int i, iBestVar, nSuppSizeCur0, nSuppSizeCur1, nSuppSizeCur, nSuppSizeMin; + if ( Kit_TruthIsConst0(pTruth, nVars) || Kit_TruthIsConst1(pTruth, nVars) ) + return -1; + // iterate through variables + iBestVar = -1; + nSuppSizeMin = KIT_INFINITY; + for ( i = 0; i < nVars; i++ ) + { + // cofactor the functiona and get support sizes + Kit_TruthCofactor0New( pCof0, pTruth, nVars, i ); + Kit_TruthCofactor1New( pCof1, pTruth, nVars, i ); + nSuppSizeCur0 = Kit_TruthSupportSize( pCof0, nVars ); + nSuppSizeCur1 = Kit_TruthSupportSize( pCof1, nVars ); + nSuppSizeCur = nSuppSizeCur0 + nSuppSizeCur1; + // compare this variable with other variables + if ( nSuppSizeMin > nSuppSizeCur ) + { + nSuppSizeMin = nSuppSizeCur; + iBestVar = i; + } + } + assert( iBestVar != -1 ); + // cofactor w.r.t. this variable + Kit_TruthCofactor0New( pCof0, pTruth, nVars, iBestVar ); + Kit_TruthCofactor1New( pCof1, pTruth, nVars, iBestVar ); + return iBestVar; +} + + +/**Function************************************************************* + + Synopsis [Counts the number of 1's in each cofactor.] + + Description [The resulting numbers are stored in the array of shorts, + whose length is 2*nVars. The number of 1's is counted in a different + space than the original function. For example, if the function depends + on k variables, the cofactors are assumed to depend on k-1 variables.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_TruthCountOnesInCofs( unsigned * pTruth, int nVars, short * pStore ) +{ + int nWords = Kit_TruthWordNum( nVars ); + int i, k, Counter; + memset( pStore, 0, sizeof(short) * 2 * nVars ); + if ( nVars <= 5 ) + { + if ( nVars > 0 ) + { + pStore[2*0+0] = Kit_WordCountOnes( pTruth[0] & 0x55555555 ); + pStore[2*0+1] = Kit_WordCountOnes( pTruth[0] & 0xAAAAAAAA ); + } + if ( nVars > 1 ) + { + pStore[2*1+0] = Kit_WordCountOnes( pTruth[0] & 0x33333333 ); + pStore[2*1+1] = Kit_WordCountOnes( pTruth[0] & 0xCCCCCCCC ); + } + if ( nVars > 2 ) + { + pStore[2*2+0] = Kit_WordCountOnes( pTruth[0] & 0x0F0F0F0F ); + pStore[2*2+1] = Kit_WordCountOnes( pTruth[0] & 0xF0F0F0F0 ); + } + if ( nVars > 3 ) + { + pStore[2*3+0] = Kit_WordCountOnes( pTruth[0] & 0x00FF00FF ); + pStore[2*3+1] = Kit_WordCountOnes( pTruth[0] & 0xFF00FF00 ); + } + if ( nVars > 4 ) + { + pStore[2*4+0] = Kit_WordCountOnes( pTruth[0] & 0x0000FFFF ); + pStore[2*4+1] = Kit_WordCountOnes( pTruth[0] & 0xFFFF0000 ); + } + return; + } + // nVars >= 6 + // count 1's for all other variables + for ( k = 0; k < nWords; k++ ) + { + Counter = Kit_WordCountOnes( pTruth[k] ); + for ( i = 5; i < nVars; i++ ) + if ( k & (1 << (i-5)) ) + pStore[2*i+1] += Counter; + else + pStore[2*i+0] += Counter; + } + // count 1's for the first five variables + for ( k = 0; k < nWords/2; k++ ) + { + pStore[2*0+0] += Kit_WordCountOnes( (pTruth[0] & 0x55555555) | ((pTruth[1] & 0x55555555) << 1) ); + pStore[2*0+1] += Kit_WordCountOnes( (pTruth[0] & 0xAAAAAAAA) | ((pTruth[1] & 0xAAAAAAAA) >> 1) ); + pStore[2*1+0] += Kit_WordCountOnes( (pTruth[0] & 0x33333333) | ((pTruth[1] & 0x33333333) << 2) ); + pStore[2*1+1] += Kit_WordCountOnes( (pTruth[0] & 0xCCCCCCCC) | ((pTruth[1] & 0xCCCCCCCC) >> 2) ); + pStore[2*2+0] += Kit_WordCountOnes( (pTruth[0] & 0x0F0F0F0F) | ((pTruth[1] & 0x0F0F0F0F) << 4) ); + pStore[2*2+1] += Kit_WordCountOnes( (pTruth[0] & 0xF0F0F0F0) | ((pTruth[1] & 0xF0F0F0F0) >> 4) ); + pStore[2*3+0] += Kit_WordCountOnes( (pTruth[0] & 0x00FF00FF) | ((pTruth[1] & 0x00FF00FF) << 8) ); + pStore[2*3+1] += Kit_WordCountOnes( (pTruth[0] & 0xFF00FF00) | ((pTruth[1] & 0xFF00FF00) >> 8) ); + pStore[2*4+0] += Kit_WordCountOnes( (pTruth[0] & 0x0000FFFF) | ((pTruth[1] & 0x0000FFFF) << 16) ); + pStore[2*4+1] += Kit_WordCountOnes( (pTruth[0] & 0xFFFF0000) | ((pTruth[1] & 0xFFFF0000) >> 16) ); + pTruth += 2; + } +} + +/**Function************************************************************* + + Synopsis [Counts the number of 1's in each cofactor.] + + Description [Verifies the above procedure.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_TruthCountOnesInCofsSlow( unsigned * pTruth, int nVars, short * pStore, unsigned * pAux ) +{ + int i; + for ( i = 0; i < nVars; i++ ) + { + Kit_TruthCofactor0New( pAux, pTruth, nVars, i ); + pStore[2*i+0] = Kit_TruthCountOnes( pAux, nVars ) / 2; + Kit_TruthCofactor1New( pAux, pTruth, nVars, i ); + pStore[2*i+1] = Kit_TruthCountOnes( pAux, nVars ) / 2; + } +} + +/**Function************************************************************* + + Synopsis [Canonicize the truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Kit_TruthHash( unsigned * pIn, int nWords ) +{ + // The 1,024 smallest prime numbers used to compute the hash value + // http://www.math.utah.edu/~alfeld/math/primelist.html + static int HashPrimes[1024] = { 2, 3, 5, + 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, + 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, + 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, + 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, + 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, + 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, + 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, + 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, + 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, + 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, + 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, + 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, + 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, + 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, + 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, + 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, + 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, + 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, + 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, + 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 2131, 2137, + 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, + 2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, + 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, + 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, + 2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, + 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791, + 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, + 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011, 3019, 3023, + 3037, 3041, 3049, 3061, 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, + 3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271, + 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359, 3361, 3371, 3373, + 3389, 3391, 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, + 3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, + 3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, + 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823, 3833, + 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923, 3929, 3931, + 3943, 3947, 3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, + 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177, + 4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, + 4289, 4297, 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 4421, 4423, + 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519, 4523, 4547, + 4549, 4561, 4567, 4583, 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657, + 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, 4759, 4783, 4787, 4789, + 4793, 4799, 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, + 4933, 4937, 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, 5009, 5011, + 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, 5099, 5101, 5107, 5113, 5119, 5147, + 5153, 5167, 5171, 5179, 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, + 5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 5407, 5413, + 5417, 5419, 5431, 5437, 5441, 5443, 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, + 5519, 5521, 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639, 5641, 5647, + 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741, 5743, + 5749, 5779, 5783, 5791, 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857, + 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, 5953, 5981, 5987, 6007, + 6011, 6029, 6037, 6043, 6047, 6053, 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, + 6131, 6133, 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, 6229, 6247, + 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301, 6311, 6317, 6323, 6329, 6337, 6343, + 6353, 6359, 6361, 6367, 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473, + 6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571, 6577, 6581, 6599, 6607, + 6619, 6637, 6653, 6659, 6661, 6673, 6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, + 6737, 6761, 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, 6841, 6857, + 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917, 6947, 6949, 6959, 6961, 6967, 6971, + 6977, 6983, 6991, 6997, 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103, + 7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, 7211, 7213, 7219, 7229, + 7237, 7243, 7247, 7253, 7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, + 7393, 7411, 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, 7507, 7517, + 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561, 7573, 7577, 7583, 7589, 7591, 7603, + 7607, 7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723, + 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829, 7841, 7853, 7867, 7873, + 7877, 7879, 7883, 7901, 7907, 7919, 7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009, + 8011, 8017, 8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111, 8117, 8123, + 8147, 8161 }; + int i; + unsigned uHashKey; + assert( nWords <= 1024 ); + uHashKey = 0; + for ( i = 0; i < nWords; i++ ) + uHashKey ^= HashPrimes[i] * pIn[i]; + return uHashKey; +} + + +/**Function************************************************************* + + Synopsis [Canonicize the truth table.] + + Description [Returns the phase. ] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Kit_TruthSemiCanonicize( unsigned * pInOut, unsigned * pAux, int nVars, char * pCanonPerm, short * pStore ) +{ +// short pStore2[32]; + unsigned * pIn = pInOut, * pOut = pAux, * pTemp; + int nWords = Kit_TruthWordNum( nVars ); + int i, Temp, fChange, Counter;//, nOnes;//, k, j, w, Limit; + unsigned uCanonPhase; + + // canonicize output + uCanonPhase = 0; +/* + nOnes = Kit_TruthCountOnes(pIn, nVars); + if ( (nOnes > nWords * 16) )//|| ((nOnes == nWords * 16) && (pIn[0] & 1)) ) + { + uCanonPhase |= (1 << nVars); + Kit_TruthNot( pIn, pIn, nVars ); + } +*/ + // collect the minterm counts + Kit_TruthCountOnesInCofs( pIn, nVars, pStore ); +// Kit_TruthCountOnesInCofsSlow( pIn, nVars, pStore2, pAux ); +// for ( i = 0; i < 2*nVars; i++ ) +// { +// assert( pStore[i] == pStore2[i] ); +// } + + // canonicize phase + for ( i = 0; i < nVars; i++ ) + { + if ( pStore[2*i+0] >= pStore[2*i+1] ) + continue; + uCanonPhase |= (1 << i); + Temp = pStore[2*i+0]; + pStore[2*i+0] = pStore[2*i+1]; + pStore[2*i+1] = Temp; + Kit_TruthChangePhase( pIn, nVars, i ); + } + +// Kit_PrintHexadecimal( stdout, pIn, nVars ); +// printf( "\n" ); + + // permute + Counter = 0; + do { + fChange = 0; + for ( i = 0; i < nVars-1; i++ ) + { + if ( pStore[2*i] >= pStore[2*(i+1)] ) + continue; + Counter++; + fChange = 1; + + Temp = pCanonPerm[i]; + pCanonPerm[i] = pCanonPerm[i+1]; + pCanonPerm[i+1] = Temp; + + Temp = pStore[2*i]; + pStore[2*i] = pStore[2*(i+1)]; + pStore[2*(i+1)] = Temp; + + Temp = pStore[2*i+1]; + pStore[2*i+1] = pStore[2*(i+1)+1]; + pStore[2*(i+1)+1] = Temp; + + // if the polarity of variables is different, swap them + if ( ((uCanonPhase & (1 << i)) > 0) != ((uCanonPhase & (1 << (i+1))) > 0) ) + { + uCanonPhase ^= (1 << i); + uCanonPhase ^= (1 << (i+1)); + } + + Kit_TruthSwapAdjacentVars( pOut, pIn, nVars, i ); + pTemp = pIn; pIn = pOut; pOut = pTemp; + } + } while ( fChange ); + +/* + Extra_PrintBinary( stdout, &uCanonPhase, nVars+1 ); printf( " : " ); + for ( i = 0; i < nVars; i++ ) + printf( "%d=%d/%d ", pCanonPerm[i], pStore[2*i], pStore[2*i+1] ); + printf( " C = %d\n", Counter ); + Extra_PrintHexadecimal( stdout, pIn, nVars ); + printf( "\n" ); +*/ + +/* + // process symmetric variable groups + uSymms = 0; + for ( i = 0; i < nVars-1; i++ ) + { + if ( pStore[2*i] != pStore[2*(i+1)] ) // i and i+1 cannot be symmetric + continue; + if ( pStore[2*i] != pStore[2*i+1] ) + continue; + if ( Kit_TruthVarsSymm( pIn, nVars, i, i+1 ) ) + continue; + if ( Kit_TruthVarsAntiSymm( pIn, nVars, i, i+1 ) ) + Kit_TruthChangePhase( pIn, nVars, i+1 ); + } +*/ + +/* + // process symmetric variable groups + uSymms = 0; + for ( i = 0; i < nVars-1; i++ ) + { + if ( pStore[2*i] != pStore[2*(i+1)] ) // i and i+1 cannot be symmetric + continue; + // i and i+1 can be symmetric + // find the end of this group + for ( k = i+1; k < nVars; k++ ) + if ( pStore[2*i] != pStore[2*k] ) + break; + Limit = k; + assert( i < Limit-1 ); + // go through the variables in this group + for ( j = i + 1; j < Limit; j++ ) + { + // check symmetry + if ( Kit_TruthVarsSymm( pIn, nVars, i, j ) ) + { + uSymms |= (1 << j); + continue; + } + // they are phase-unknown + if ( pStore[2*i] == pStore[2*i+1] ) + { + if ( Kit_TruthVarsAntiSymm( pIn, nVars, i, j ) ) + { + Kit_TruthChangePhase( pIn, nVars, j ); + uCanonPhase ^= (1 << j); + uSymms |= (1 << j); + continue; + } + } + + // they are not symmetric - move j as far as it goes in the group + for ( k = j; k < Limit-1; k++ ) + { + Counter++; + + Temp = pCanonPerm[k]; + pCanonPerm[k] = pCanonPerm[k+1]; + pCanonPerm[k+1] = Temp; + + assert( pStore[2*k] == pStore[2*(k+1)] ); + Kit_TruthSwapAdjacentVars( pOut, pIn, nVars, k ); + pTemp = pIn; pIn = pOut; pOut = pTemp; + } + Limit--; + j--; + } + i = Limit - 1; + } +*/ + + // swap if it was moved an even number of times + if ( Counter & 1 ) + Kit_TruthCopy( pOut, pIn, nVars ); + return uCanonPhase; +} + + +/**Function************************************************************* + + Synopsis [Fast counting minterms in the cofactors of a function.] + + Description [Returns the total number of minterms in the function. + The resulting array (pRes) contains the number of minterms in 0-cofactor + w.r.t. each variables. The additional array (pBytes) is used for internal + storage. It should have the size equal to the number of truth table bytes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Kit_TruthCountMinterms( unsigned * pTruth, int nVars, int * pRes, int * pBytes ) +{ + // the number of 1s if every byte as well as in the 0-cofactors w.r.t. three variables + static unsigned Table[256] = { + 0x00000000, 0x01010101, 0x01010001, 0x02020102, 0x01000101, 0x02010202, 0x02010102, 0x03020203, + 0x01000001, 0x02010102, 0x02010002, 0x03020103, 0x02000102, 0x03010203, 0x03010103, 0x04020204, + 0x00010101, 0x01020202, 0x01020102, 0x02030203, 0x01010202, 0x02020303, 0x02020203, 0x03030304, + 0x01010102, 0x02020203, 0x02020103, 0x03030204, 0x02010203, 0x03020304, 0x03020204, 0x04030305, + 0x00010001, 0x01020102, 0x01020002, 0x02030103, 0x01010102, 0x02020203, 0x02020103, 0x03030204, + 0x01010002, 0x02020103, 0x02020003, 0x03030104, 0x02010103, 0x03020204, 0x03020104, 0x04030205, + 0x00020102, 0x01030203, 0x01030103, 0x02040204, 0x01020203, 0x02030304, 0x02030204, 0x03040305, + 0x01020103, 0x02030204, 0x02030104, 0x03040205, 0x02020204, 0x03030305, 0x03030205, 0x04040306, + 0x00000101, 0x01010202, 0x01010102, 0x02020203, 0x01000202, 0x02010303, 0x02010203, 0x03020304, + 0x01000102, 0x02010203, 0x02010103, 0x03020204, 0x02000203, 0x03010304, 0x03010204, 0x04020305, + 0x00010202, 0x01020303, 0x01020203, 0x02030304, 0x01010303, 0x02020404, 0x02020304, 0x03030405, + 0x01010203, 0x02020304, 0x02020204, 0x03030305, 0x02010304, 0x03020405, 0x03020305, 0x04030406, + 0x00010102, 0x01020203, 0x01020103, 0x02030204, 0x01010203, 0x02020304, 0x02020204, 0x03030305, + 0x01010103, 0x02020204, 0x02020104, 0x03030205, 0x02010204, 0x03020305, 0x03020205, 0x04030306, + 0x00020203, 0x01030304, 0x01030204, 0x02040305, 0x01020304, 0x02030405, 0x02030305, 0x03040406, + 0x01020204, 0x02030305, 0x02030205, 0x03040306, 0x02020305, 0x03030406, 0x03030306, 0x04040407, + 0x00000001, 0x01010102, 0x01010002, 0x02020103, 0x01000102, 0x02010203, 0x02010103, 0x03020204, + 0x01000002, 0x02010103, 0x02010003, 0x03020104, 0x02000103, 0x03010204, 0x03010104, 0x04020205, + 0x00010102, 0x01020203, 0x01020103, 0x02030204, 0x01010203, 0x02020304, 0x02020204, 0x03030305, + 0x01010103, 0x02020204, 0x02020104, 0x03030205, 0x02010204, 0x03020305, 0x03020205, 0x04030306, + 0x00010002, 0x01020103, 0x01020003, 0x02030104, 0x01010103, 0x02020204, 0x02020104, 0x03030205, + 0x01010003, 0x02020104, 0x02020004, 0x03030105, 0x02010104, 0x03020205, 0x03020105, 0x04030206, + 0x00020103, 0x01030204, 0x01030104, 0x02040205, 0x01020204, 0x02030305, 0x02030205, 0x03040306, + 0x01020104, 0x02030205, 0x02030105, 0x03040206, 0x02020205, 0x03030306, 0x03030206, 0x04040307, + 0x00000102, 0x01010203, 0x01010103, 0x02020204, 0x01000203, 0x02010304, 0x02010204, 0x03020305, + 0x01000103, 0x02010204, 0x02010104, 0x03020205, 0x02000204, 0x03010305, 0x03010205, 0x04020306, + 0x00010203, 0x01020304, 0x01020204, 0x02030305, 0x01010304, 0x02020405, 0x02020305, 0x03030406, + 0x01010204, 0x02020305, 0x02020205, 0x03030306, 0x02010305, 0x03020406, 0x03020306, 0x04030407, + 0x00010103, 0x01020204, 0x01020104, 0x02030205, 0x01010204, 0x02020305, 0x02020205, 0x03030306, + 0x01010104, 0x02020205, 0x02020105, 0x03030206, 0x02010205, 0x03020306, 0x03020206, 0x04030307, + 0x00020204, 0x01030305, 0x01030205, 0x02040306, 0x01020305, 0x02030406, 0x02030306, 0x03040407, + 0x01020205, 0x02030306, 0x02030206, 0x03040307, 0x02020306, 0x03030407, 0x03030307, 0x04040408 + }; + unsigned uSum; + unsigned char * pTruthC, * pLimit; + int i, iVar, Step, nWords, nBytes, nTotal; + + assert( nVars <= 20 ); + + // clear storage + memset( pRes, 0, sizeof(int) * nVars ); + + // count the number of one's in 0-cofactors of the first three variables + nTotal = uSum = 0; + nWords = Kit_TruthWordNum( nVars ); + nBytes = nWords * 4; + pTruthC = (unsigned char *)pTruth; + pLimit = pTruthC + nBytes; + for ( ; pTruthC < pLimit; pTruthC++ ) + { + uSum += Table[*pTruthC]; + *pBytes++ = (Table[*pTruthC] & 0xff); + if ( (uSum & 0xff) > 246 ) + { + nTotal += (uSum & 0xff); + pRes[0] += ((uSum >> 8) & 0xff); + pRes[2] += ((uSum >> 16) & 0xff); + pRes[3] += ((uSum >> 24) & 0xff); + uSum = 0; + } + } + if ( uSum ) + { + nTotal += (uSum & 0xff); + pRes[0] += ((uSum >> 8) & 0xff); + pRes[1] += ((uSum >> 16) & 0xff); + pRes[2] += ((uSum >> 24) & 0xff); + } + + // count all other variables + for ( iVar = 3, Step = 1; Step < nBytes; Step *= 2, iVar++ ) + for ( i = 0; i < nBytes; i += Step + Step ) + { + pRes[iVar] += pBytes[i]; + pBytes[i] += pBytes[i+Step]; + } + assert( pBytes[0] == nTotal ); + assert( iVar == nVars ); + return nTotal; +} + +/**Function************************************************************* + + Synopsis [Fast counting minterms for the functions.] + + Description [Returns 0 if the function is a constant.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Kit_TruthCountMintermsPrecomp() +{ + int bit_count[256] = { + 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 + }; + unsigned i, uWord; + for ( i = 0; i < 256; i++ ) + { + if ( i % 8 == 0 ) + printf( "\n" ); + uWord = bit_count[i]; + uWord |= (bit_count[i & 0x55] << 8); + uWord |= (bit_count[i & 0x33] << 16); + uWord |= (bit_count[i & 0x0f] << 24); + printf( "0x" ); + Extra_PrintHexadecimal( stdout, &uWord, 5 ); + printf( ", " ); + } +} + +/**Function************************************************************* + + Synopsis [Dumps truth table into a file.] + + Description [Generates script file for reading into ABC.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Kit_TruthDumpToFile( unsigned * pTruth, int nVars, int nFile ) +{ + static char pFileName[100]; + FILE * pFile; + sprintf( pFileName, "s%03d", nFile ); + pFile = fopen( pFileName, "w" ); + fprintf( pFile, "rt " ); + Extra_PrintHexadecimal( pFile, pTruth, nVars ); + fprintf( pFile, "; bdd; sop; ps\n" ); + fclose( pFile ); + return pFileName; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/kit/kit_.c b/abc_with_bb_support/src/aig/kit/kit_.c new file mode 100644 index 000000000..cf79cff51 --- /dev/null +++ b/abc_with_bb_support/src/aig/kit/kit_.c @@ -0,0 +1,48 @@ +/**CFile**************************************************************** + + FileName [kit_.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Computation kit.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Dec 6, 2006.] + + Revision [$Id: kit_.c,v 1.00 2006/12/06 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "kit.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/kit/module.make b/abc_with_bb_support/src/aig/kit/module.make new file mode 100644 index 000000000..fee721881 --- /dev/null +++ b/abc_with_bb_support/src/aig/kit/module.make @@ -0,0 +1,8 @@ +SRC += src/aig/kit/kitBdd.c \ + src/aig/kit/kitDsd.c \ + src/aig/kit/kitFactor.c \ + src/aig/kit/kitGraph.c \ + src/aig/kit/kitHop.c \ + src/aig/kit/kitIsop.c \ + src/aig/kit/kitSop.c \ + src/aig/kit/kitTruth.c diff --git a/abc_with_bb_support/src/aig/mem/mem.c b/abc_with_bb_support/src/aig/mem/mem.c new file mode 100644 index 000000000..a4da9c922 --- /dev/null +++ b/abc_with_bb_support/src/aig/mem/mem.c @@ -0,0 +1,604 @@ +/**CFile**************************************************************** + + FileName [esopMem.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Cover manipulation package.] + + Synopsis [Memory managers.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: esopMem.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include +#include +#include +#include +#include "mem.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +struct Mem_Fixed_t_ +{ + // information about individual entries + int nEntrySize; // the size of one entry + int nEntriesAlloc; // the total number of entries allocated + int nEntriesUsed; // the number of entries in use + int nEntriesMax; // the max number of entries in use + char * pEntriesFree; // the linked list of free entries + + // this is where the memory is stored + int nChunkSize; // the size of one chunk + int nChunksAlloc; // the maximum number of memory chunks + int nChunks; // the current number of memory chunks + char ** pChunks; // the allocated memory + + // statistics + int nMemoryUsed; // memory used in the allocated entries + int nMemoryAlloc; // memory allocated +}; + +struct Mem_Flex_t_ +{ + // information about individual entries + int nEntriesUsed; // the number of entries allocated + char * pCurrent; // the current pointer to free memory + char * pEnd; // the first entry outside the free memory + + // this is where the memory is stored + int nChunkSize; // the size of one chunk + int nChunksAlloc; // the maximum number of memory chunks + int nChunks; // the current number of memory chunks + char ** pChunks; // the allocated memory + + // statistics + int nMemoryUsed; // memory used in the allocated entries + int nMemoryAlloc; // memory allocated +}; + +struct Mem_Step_t_ +{ + int nMems; // the number of fixed memory managers employed + Mem_Fixed_t ** pMems; // memory managers: 2^1 words, 2^2 words, etc + int nMapSize; // the size of the memory array + Mem_Fixed_t ** pMap; // maps the number of bytes into its memory manager +}; + +#define ALLOC(type, num) ((type *) malloc(sizeof(type) * (num))) +#define FREE(obj) ((obj) ? (free((char *) (obj)), (obj) = 0) : 0) +#define REALLOC(type, obj, num) \ + ((obj) ? ((type *) realloc((char *)(obj), sizeof(type) * (num))) : \ + ((type *) malloc(sizeof(type) * (num)))) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates memory pieces of fixed size.] + + Description [The size of the chunk is computed as the minimum of + 1024 entries and 64K. Can only work with entry size at least 4 byte long.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mem_Fixed_t * Mem_FixedStart( int nEntrySize ) +{ + Mem_Fixed_t * p; + + p = ALLOC( Mem_Fixed_t, 1 ); + memset( p, 0, sizeof(Mem_Fixed_t) ); + + p->nEntrySize = nEntrySize; + p->nEntriesAlloc = 0; + p->nEntriesUsed = 0; + p->pEntriesFree = NULL; + + if ( nEntrySize * (1 << 10) < (1<<16) ) + p->nChunkSize = (1 << 10); + else + p->nChunkSize = (1<<16) / nEntrySize; + if ( p->nChunkSize < 8 ) + p->nChunkSize = 8; + + p->nChunksAlloc = 64; + p->nChunks = 0; + p->pChunks = ALLOC( char *, p->nChunksAlloc ); + + p->nMemoryUsed = 0; + p->nMemoryAlloc = 0; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mem_FixedStop( Mem_Fixed_t * p, int fVerbose ) +{ + int i; + if ( p == NULL ) + return; + if ( fVerbose ) + { + printf( "Fixed memory manager: Entry = %5d. Chunk = %5d. Chunks used = %5d.\n", + p->nEntrySize, p->nChunkSize, p->nChunks ); + printf( " Entries used = %8d. Entries peak = %8d. Memory used = %8d. Memory alloc = %8d.\n", + p->nEntriesUsed, p->nEntriesMax, p->nEntrySize * p->nEntriesUsed, p->nMemoryAlloc ); + } + for ( i = 0; i < p->nChunks; i++ ) + free( p->pChunks[i] ); + free( p->pChunks ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Mem_FixedEntryFetch( Mem_Fixed_t * p ) +{ + char * pTemp; + int i; + + // check if there are still free entries + if ( p->nEntriesUsed == p->nEntriesAlloc ) + { // need to allocate more entries + assert( p->pEntriesFree == NULL ); + if ( p->nChunks == p->nChunksAlloc ) + { + p->nChunksAlloc *= 2; + p->pChunks = REALLOC( char *, p->pChunks, p->nChunksAlloc ); + } + p->pEntriesFree = ALLOC( char, p->nEntrySize * p->nChunkSize ); + p->nMemoryAlloc += p->nEntrySize * p->nChunkSize; + // transform these entries into a linked list + pTemp = p->pEntriesFree; + for ( i = 1; i < p->nChunkSize; i++ ) + { + *((char **)pTemp) = pTemp + p->nEntrySize; + pTemp += p->nEntrySize; + } + // set the last link + *((char **)pTemp) = NULL; + // add the chunk to the chunk storage + p->pChunks[ p->nChunks++ ] = p->pEntriesFree; + // add to the number of entries allocated + p->nEntriesAlloc += p->nChunkSize; + } + // incrememt the counter of used entries + p->nEntriesUsed++; + if ( p->nEntriesMax < p->nEntriesUsed ) + p->nEntriesMax = p->nEntriesUsed; + // return the first entry in the free entry list + pTemp = p->pEntriesFree; + p->pEntriesFree = *((char **)pTemp); + return pTemp; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mem_FixedEntryRecycle( Mem_Fixed_t * p, char * pEntry ) +{ + // decrement the counter of used entries + p->nEntriesUsed--; + // add the entry to the linked list of free entries + *((char **)pEntry) = p->pEntriesFree; + p->pEntriesFree = pEntry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [Relocates all the memory except the first chunk.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mem_FixedRestart( Mem_Fixed_t * p ) +{ + int i; + char * pTemp; + + // deallocate all chunks except the first one + for ( i = 1; i < p->nChunks; i++ ) + free( p->pChunks[i] ); + p->nChunks = 1; + // transform these entries into a linked list + pTemp = p->pChunks[0]; + for ( i = 1; i < p->nChunkSize; i++ ) + { + *((char **)pTemp) = pTemp + p->nEntrySize; + pTemp += p->nEntrySize; + } + // set the last link + *((char **)pTemp) = NULL; + // set the free entry list + p->pEntriesFree = p->pChunks[0]; + // set the correct statistics + p->nMemoryAlloc = p->nEntrySize * p->nChunkSize; + p->nMemoryUsed = 0; + p->nEntriesAlloc = p->nChunkSize; + p->nEntriesUsed = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mem_FixedReadMemUsage( Mem_Fixed_t * p ) +{ + return p->nMemoryAlloc; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mem_FixedReadMaxEntriesUsed( Mem_Fixed_t * p ) +{ + return p->nEntriesMax; +} + + + +/**Function************************************************************* + + Synopsis [Allocates entries of flexible size.] + + Description [Can only work with entry size at least 4 byte long.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mem_Flex_t * Mem_FlexStart() +{ + Mem_Flex_t * p; + + p = ALLOC( Mem_Flex_t, 1 ); + memset( p, 0, sizeof(Mem_Flex_t) ); + + p->nEntriesUsed = 0; + p->pCurrent = NULL; + p->pEnd = NULL; + + p->nChunkSize = (1 << 14); + p->nChunksAlloc = 64; + p->nChunks = 0; + p->pChunks = ALLOC( char *, p->nChunksAlloc ); + + p->nMemoryUsed = 0; + p->nMemoryAlloc = 0; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mem_FlexStop( Mem_Flex_t * p, int fVerbose ) +{ + int i; + if ( p == NULL ) + return; + if ( fVerbose ) + { + printf( "Flexible memory manager: Chunk size = %d. Chunks used = %d.\n", + p->nChunkSize, p->nChunks ); + printf( " Entries used = %d. Memory used = %d. Memory alloc = %d.\n", + p->nEntriesUsed, p->nMemoryUsed, p->nMemoryAlloc ); + } + for ( i = 0; i < p->nChunks; i++ ) + free( p->pChunks[i] ); + free( p->pChunks ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Mem_FlexEntryFetch( Mem_Flex_t * p, int nBytes ) +{ + char * pTemp; + // check if there are still free entries + if ( p->pCurrent == NULL || p->pCurrent + nBytes > p->pEnd ) + { // need to allocate more entries + if ( p->nChunks == p->nChunksAlloc ) + { + p->nChunksAlloc *= 2; + p->pChunks = REALLOC( char *, p->pChunks, p->nChunksAlloc ); + } + if ( nBytes > p->nChunkSize ) + { + // resize the chunk size if more memory is requested than it can give + // (ideally, this should never happen) + p->nChunkSize = 2 * nBytes; + } + p->pCurrent = ALLOC( char, p->nChunkSize ); + p->pEnd = p->pCurrent + p->nChunkSize; + p->nMemoryAlloc += p->nChunkSize; + // add the chunk to the chunk storage + p->pChunks[ p->nChunks++ ] = p->pCurrent; + } + assert( p->pCurrent + nBytes <= p->pEnd ); + // increment the counter of used entries + p->nEntriesUsed++; + // keep track of the memory used + p->nMemoryUsed += nBytes; + // return the next entry + pTemp = p->pCurrent; + p->pCurrent += nBytes; + return pTemp; +} + +/**Function************************************************************* + + Synopsis [] + + Description [Relocates all the memory except the first chunk.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mem_FlexRestart( Mem_Flex_t * p ) +{ + int i; + if ( p->nChunks == 0 ) + return; + // deallocate all chunks except the first one + for ( i = 1; i < p->nChunks; i++ ) + free( p->pChunks[i] ); + p->nChunks = 1; + p->nMemoryAlloc = p->nChunkSize; + // transform these entries into a linked list + p->pCurrent = p->pChunks[0]; + p->pEnd = p->pCurrent + p->nChunkSize; + p->nEntriesUsed = 0; + p->nMemoryUsed = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mem_FlexReadMemUsage( Mem_Flex_t * p ) +{ + return p->nMemoryUsed; +} + + + + + +/**Function************************************************************* + + Synopsis [Starts the hierarchical memory manager.] + + Description [This manager can allocate entries of any size. + Iternally they are mapped into the entries with the number of bytes + equal to the power of 2. The smallest entry size is 8 bytes. The + next one is 16 bytes etc. So, if the user requests 6 bytes, he gets + 8 byte entry. If we asks for 25 bytes, he gets 32 byte entry etc. + The input parameters "nSteps" says how many fixed memory managers + are employed internally. Calling this procedure with nSteps equal + to 10 results in 10 hierarchically arranged internal memory managers, + which can allocate up to 4096 (1Kb) entries. Requests for larger + entries are handed over to malloc() and then free()ed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mem_Step_t * Mem_StepStart( int nSteps ) +{ + Mem_Step_t * p; + int i, k; + p = ALLOC( Mem_Step_t, 1 ); + memset( p, 0, sizeof(Mem_Step_t) ); + p->nMems = nSteps; + // start the fixed memory managers + p->pMems = ALLOC( Mem_Fixed_t *, p->nMems ); + for ( i = 0; i < p->nMems; i++ ) + p->pMems[i] = Mem_FixedStart( (8<nMapSize = (4<nMems); + p->pMap = ALLOC( Mem_Fixed_t *, p->nMapSize+1 ); + p->pMap[0] = NULL; + for ( k = 1; k <= 4; k++ ) + p->pMap[k] = p->pMems[0]; + for ( i = 0; i < p->nMems; i++ ) + for ( k = (4<pMap[k] = p->pMems[i]; +//for ( i = 1; i < 100; i ++ ) +//printf( "%10d: size = %10d\n", i, p->pMap[i]->nEntrySize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the memory manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mem_StepStop( Mem_Step_t * p, int fVerbose ) +{ + int i; + for ( i = 0; i < p->nMems; i++ ) + Mem_FixedStop( p->pMems[i], fVerbose ); +// if ( p->pLargeChunks ) +// { +// for ( i = 0; i < p->nLargeChunks; i++ ) +// free( p->pLargeChunks[i] ); +// free( p->pLargeChunks ); +// } + free( p->pMems ); + free( p->pMap ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Creates the entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Mem_StepEntryFetch( Mem_Step_t * p, int nBytes ) +{ + if ( nBytes == 0 ) + return NULL; + if ( nBytes > p->nMapSize ) + { +// printf( "Allocating %d bytes.\n", nBytes ); +/* + if ( p->nLargeChunks == p->nLargeChunksAlloc ) + { + if ( p->nLargeChunksAlloc == 0 ) + p->nLargeChunksAlloc = 5; + p->nLargeChunksAlloc *= 2; + p->pLargeChunks = REALLOC( char *, p->pLargeChunks, p->nLargeChunksAlloc ); + } + p->pLargeChunks[ p->nLargeChunks++ ] = ALLOC( char, nBytes ); + return p->pLargeChunks[ p->nLargeChunks - 1 ]; +*/ + return ALLOC( char, nBytes ); + } + return Mem_FixedEntryFetch( p->pMap[nBytes] ); +} + + +/**Function************************************************************* + + Synopsis [Recycles the entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mem_StepEntryRecycle( Mem_Step_t * p, char * pEntry, int nBytes ) +{ + if ( nBytes == 0 ) + return; + if ( nBytes > p->nMapSize ) + { + free( pEntry ); + return; + } + Mem_FixedEntryRecycle( p->pMap[nBytes], pEntry ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mem_StepReadMemUsage( Mem_Step_t * p ) +{ + int i, nMemTotal = 0; + for ( i = 0; i < p->nMems; i++ ) + nMemTotal += p->pMems[i]->nMemoryAlloc; + return nMemTotal; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/aig/mem/mem.h b/abc_with_bb_support/src/aig/mem/mem.h new file mode 100644 index 000000000..baadbde44 --- /dev/null +++ b/abc_with_bb_support/src/aig/mem/mem.h @@ -0,0 +1,72 @@ +/**CFile**************************************************************** + + FileName [mem.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Memory management.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: mem.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __MEM_H__ +#define __MEM_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Mem_Fixed_t_ Mem_Fixed_t; +typedef struct Mem_Flex_t_ Mem_Flex_t; +typedef struct Mem_Step_t_ Mem_Step_t; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== mem.c ===========================================================*/ +// fixed-size-block memory manager +extern Mem_Fixed_t * Mem_FixedStart( int nEntrySize ); +extern void Mem_FixedStop( Mem_Fixed_t * p, int fVerbose ); +extern char * Mem_FixedEntryFetch( Mem_Fixed_t * p ); +extern void Mem_FixedEntryRecycle( Mem_Fixed_t * p, char * pEntry ); +extern void Mem_FixedRestart( Mem_Fixed_t * p ); +extern int Mem_FixedReadMemUsage( Mem_Fixed_t * p ); +extern int Mem_FixedReadMaxEntriesUsed( Mem_Fixed_t * p ); +// flexible-size-block memory manager +extern Mem_Flex_t * Mem_FlexStart(); +extern void Mem_FlexStop( Mem_Flex_t * p, int fVerbose ); +extern char * Mem_FlexEntryFetch( Mem_Flex_t * p, int nBytes ); +extern void Mem_FlexRestart( Mem_Flex_t * p ); +extern int Mem_FlexReadMemUsage( Mem_Flex_t * p ); +// hierarchical memory manager +extern Mem_Step_t * Mem_StepStart( int nSteps ); +extern void Mem_StepStop( Mem_Step_t * p, int fVerbose ); +extern char * Mem_StepEntryFetch( Mem_Step_t * p, int nBytes ); +extern void Mem_StepEntryRecycle( Mem_Step_t * p, char * pEntry, int nBytes ); +extern int Mem_StepReadMemUsage( Mem_Step_t * p ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/mem/module.make b/abc_with_bb_support/src/aig/mem/module.make new file mode 100644 index 000000000..54d9eae65 --- /dev/null +++ b/abc_with_bb_support/src/aig/mem/module.make @@ -0,0 +1 @@ +SRC += src/aig/mem/mem.c diff --git a/abc_with_bb_support/src/aig/rwt/module.make b/abc_with_bb_support/src/aig/rwt/module.make new file mode 100644 index 000000000..e2d6bea16 --- /dev/null +++ b/abc_with_bb_support/src/aig/rwt/module.make @@ -0,0 +1,3 @@ +SRC += src/aig/rwt/rwtDec.c \ + src/aig/rwt/rwtMan.c \ + src/aig/rwt/rwtUtil.c diff --git a/abc_with_bb_support/src/aig/rwt/rwt.h b/abc_with_bb_support/src/aig/rwt/rwt.h new file mode 100644 index 000000000..8c6da3a8e --- /dev/null +++ b/abc_with_bb_support/src/aig/rwt/rwt.h @@ -0,0 +1,155 @@ +/**CFile**************************************************************** + + FileName [rwt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting package.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: rwt.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __RWT_H__ +#define __RWT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include "mem.h" +#include "vec.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +#define RWT_LIMIT 1048576/4 // ((1 << 20) +#define RWT_MIN(a,b) (((a) < (b))? (a) : (b)) +#define RWT_MAX(a,b) (((a) > (b))? (a) : (b)) + +typedef struct Rwt_Man_t_ Rwt_Man_t; +typedef struct Rwt_Node_t_ Rwt_Node_t; + +struct Rwt_Man_t_ +{ + // internal lookups + int nFuncs; // number of four var functions + unsigned short * puCanons; // canonical forms + char * pPhases; // canonical phases + char * pPerms; // canonical permutations + unsigned char * pMap; // mapping of functions into class numbers + unsigned short * pMapInv; // mapping of classes into functions + char * pPractical; // practical NPN classes + char ** pPerms4; // four-var permutations + // node space + Vec_Ptr_t * vForest; // all the nodes + Rwt_Node_t ** pTable; // the hash table of nodes by their canonical form + Vec_Vec_t * vClasses; // the nodes of the equivalence classes + Mem_Fixed_t * pMmNode; // memory for nodes and cuts + // statistical variables + int nTravIds; // the counter of traversal IDs + int nConsidered; // the number of nodes considered + int nAdded; // the number of nodes added to lists + int nClasses; // the number of NN classes + // the result of resynthesis + int fCompl; // indicates if the output of FF should be complemented + void * pCut; // the decomposition tree (temporary) + void * pGraph; // the decomposition tree (temporary) + char * pPerm; // permutation used for the best cut + Vec_Ptr_t * vFanins; // the fanins array (temporary) + Vec_Ptr_t * vFaninsCur; // the fanins array (temporary) + Vec_Int_t * vLevNums; // the array of levels (temporary) + Vec_Ptr_t * vNodesTemp; // the nodes in MFFC (temporary) + // node statistics + int nNodesConsidered; + int nNodesRewritten; + int nNodesGained; + int nScores[222]; + int nCutsGood; + int nCutsBad; + int nSubgraphs; + // runtime statistics + int timeStart; + int timeTruth; + int timeCut; + int timeRes; + int timeEval; + int timeMffc; + int timeUpdate; + int timeTotal; +}; + +struct Rwt_Node_t_ // 24 bytes +{ + int Id; // ID + int TravId; // traversal ID + unsigned uTruth : 16; // truth table + unsigned Volume : 8; // volume + unsigned Level : 6; // level + unsigned fUsed : 1; // mark + unsigned fExor : 1; // mark + Rwt_Node_t * p0; // first child + Rwt_Node_t * p1; // second child + Rwt_Node_t * pNext; // next in the table +}; + +// manipulation of complemented attributes +static inline int Rwt_IsComplement( Rwt_Node_t * p ) { return (int)(((unsigned long)p) & 01); } +static inline Rwt_Node_t * Rwt_Regular( Rwt_Node_t * p ) { return (Rwt_Node_t *)((unsigned long)(p) & ~01); } +static inline Rwt_Node_t * Rwt_Not( Rwt_Node_t * p ) { return (Rwt_Node_t *)((unsigned long)(p) ^ 01); } +static inline Rwt_Node_t * Rwt_NotCond( Rwt_Node_t * p, int c ) { return (Rwt_Node_t *)((unsigned long)(p) ^ (c)); } + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== rwrDec.c ========================================================*/ +extern void Rwt_ManPreprocess( Rwt_Man_t * p ); +/*=== rwrMan.c ========================================================*/ +extern Rwt_Man_t * Rwt_ManStart( int fPrecompute ); +extern void Rwt_ManStop( Rwt_Man_t * p ); +extern void Rwt_ManPrintStats( Rwt_Man_t * p ); +extern void Rwt_ManPrintStatsFile( Rwt_Man_t * p ); +extern void * Rwt_ManReadDecs( Rwt_Man_t * p ); +extern Vec_Ptr_t * Rwt_ManReadLeaves( Rwt_Man_t * p ); +extern int Rwt_ManReadCompl( Rwt_Man_t * p ); +extern void Rwt_ManAddTimeCuts( Rwt_Man_t * p, int Time ); +extern void Rwt_ManAddTimeUpdate( Rwt_Man_t * p, int Time ); +extern void Rwt_ManAddTimeTotal( Rwt_Man_t * p, int Time ); +/*=== rwrUtil.c ========================================================*/ +extern void Rwt_ManLoadFromArray( Rwt_Man_t * p, int fVerbose ); +extern char * Rwt_ManGetPractical( Rwt_Man_t * p ); +extern Rwt_Node_t * Rwt_ManAddVar( Rwt_Man_t * p, unsigned uTruth, int fPrecompute ); +extern void Rwt_ManIncTravId( Rwt_Man_t * p ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/aig/rwt/rwtDec.c b/abc_with_bb_support/src/aig/rwt/rwtDec.c new file mode 100644 index 000000000..2c029edcf --- /dev/null +++ b/abc_with_bb_support/src/aig/rwt/rwtDec.c @@ -0,0 +1,150 @@ +/**CFile**************************************************************** + + FileName [rwtDec.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting package.] + + Synopsis [Evaluation and decomposition procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: rwtDec.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "rwt.h" +#include "deco.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Dec_Graph_t * Rwt_NodePreprocess( Rwt_Man_t * p, Rwt_Node_t * pNode ); +static Dec_Edge_t Rwt_TravCollect_rec( Rwt_Man_t * p, Rwt_Node_t * pNode, Dec_Graph_t * pGraph ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Preprocesses computed library of subgraphs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwt_ManPreprocess( Rwt_Man_t * p ) +{ + Dec_Graph_t * pGraph; + Rwt_Node_t * pNode; + int i, k; + // put the nodes into the structure + p->pMapInv = ALLOC( unsigned short, 222 ); + memset( p->pMapInv, 0, sizeof(unsigned short) * 222 ); + p->vClasses = Vec_VecStart( 222 ); + for ( i = 0; i < p->nFuncs; i++ ) + { + if ( p->pTable[i] == NULL ) + continue; + // consider all implementations of this function + for ( pNode = p->pTable[i]; pNode; pNode = pNode->pNext ) + { + assert( pNode->uTruth == p->pTable[i]->uTruth ); + assert( p->pMap[pNode->uTruth] >= 0 && p->pMap[pNode->uTruth] < 222 ); + Vec_VecPush( p->vClasses, p->pMap[pNode->uTruth], pNode ); + p->pMapInv[ p->pMap[pNode->uTruth] ] = p->puCanons[pNode->uTruth]; + } + } + // compute decomposition forms for each node and verify them + Vec_VecForEachEntry( p->vClasses, pNode, i, k ) + { + pGraph = Rwt_NodePreprocess( p, pNode ); + pNode->pNext = (Rwt_Node_t *)pGraph; +// assert( pNode->uTruth == (Dec_GraphDeriveTruth(pGraph) & 0xFFFF) ); + } +} + +/**Function************************************************************* + + Synopsis [Preprocesses subgraphs rooted at this node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Rwt_NodePreprocess( Rwt_Man_t * p, Rwt_Node_t * pNode ) +{ + Dec_Graph_t * pGraph; + Dec_Edge_t eRoot; + assert( !Rwt_IsComplement(pNode) ); + // consider constant + if ( pNode->uTruth == 0 ) + return Dec_GraphCreateConst0(); + // consider the case of elementary var + if ( pNode->uTruth == 0x00FF ) + return Dec_GraphCreateLeaf( 3, 4, 1 ); + // start the subgraphs + pGraph = Dec_GraphCreate( 4 ); + // collect the nodes + Rwt_ManIncTravId( p ); + eRoot = Rwt_TravCollect_rec( p, pNode, pGraph ); + Dec_GraphSetRoot( pGraph, eRoot ); + return pGraph; +} + +/**Function************************************************************* + + Synopsis [Adds one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Edge_t Rwt_TravCollect_rec( Rwt_Man_t * p, Rwt_Node_t * pNode, Dec_Graph_t * pGraph ) +{ + Dec_Edge_t eNode0, eNode1, eNode; + // elementary variable + if ( pNode->fUsed ) + return Dec_EdgeCreate( pNode->Id - 1, 0 ); + // previously visited node + if ( pNode->TravId == p->nTravIds ) + return Dec_IntToEdge( pNode->Volume ); + pNode->TravId = p->nTravIds; + // solve for children + eNode0 = Rwt_TravCollect_rec( p, Rwt_Regular(pNode->p0), pGraph ); + if ( Rwt_IsComplement(pNode->p0) ) + eNode0.fCompl = !eNode0.fCompl; + eNode1 = Rwt_TravCollect_rec( p, Rwt_Regular(pNode->p1), pGraph ); + if ( Rwt_IsComplement(pNode->p1) ) + eNode1.fCompl = !eNode1.fCompl; + // create the decomposition node(s) + if ( pNode->fExor ) + eNode = Dec_GraphAddNodeXor( pGraph, eNode0, eNode1, 0 ); + else + eNode = Dec_GraphAddNodeAnd( pGraph, eNode0, eNode1 ); + // save the result + pNode->Volume = Dec_EdgeToInt( eNode ); + return eNode; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/rwt/rwtMan.c b/abc_with_bb_support/src/aig/rwt/rwtMan.c new file mode 100644 index 000000000..6f4fb89b1 --- /dev/null +++ b/abc_with_bb_support/src/aig/rwt/rwtMan.c @@ -0,0 +1,358 @@ +/**CFile**************************************************************** + + FileName [rwtMan.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting package.] + + Synopsis [Rewriting manager.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: rwtMan.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "rwt.h" +#include "deco.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static unsigned short * s_puCanons = NULL; +static char * s_pPhases = NULL; +static char * s_pPerms = NULL; +static unsigned char * s_pMap = NULL; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts residual rewriting manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwt_ManGlobalStart() +{ + if ( s_puCanons == NULL ) + Extra_Truth4VarNPN( &s_puCanons, &s_pPhases, &s_pPerms, &s_pMap ); +} + +/**Function************************************************************* + + Synopsis [Starts residual rewriting manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwt_ManGlobalStop() +{ + FREE( s_puCanons ); + FREE( s_pPhases ); + FREE( s_pPerms ); + FREE( s_pMap ); +} + +/**Function************************************************************* + + Synopsis [Starts rewriting manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Rwt_Man_t * Rwt_ManStart( int fPrecompute ) +{ + Rwt_Man_t * p; + int clk = clock(); +clk = clock(); + p = ALLOC( Rwt_Man_t, 1 ); + memset( p, 0, sizeof(Rwt_Man_t) ); + p->nFuncs = (1<<16); + // copy the global tables + Rwt_ManGlobalStart(); + p->puCanons = s_puCanons; + p->pPhases = s_pPhases; + p->pPerms = s_pPerms; + p->pMap = s_pMap; + // initialize practical NPN classes + p->pPractical = Rwt_ManGetPractical( p ); + // create the table + p->pTable = ALLOC( Rwt_Node_t *, p->nFuncs ); + memset( p->pTable, 0, sizeof(Rwt_Node_t *) * p->nFuncs ); + // create the elementary nodes + p->pMmNode = Mem_FixedStart( sizeof(Rwt_Node_t) ); + p->vForest = Vec_PtrAlloc( 100 ); + Rwt_ManAddVar( p, 0x0000, fPrecompute ); // constant 0 + Rwt_ManAddVar( p, 0xAAAA, fPrecompute ); // var A + Rwt_ManAddVar( p, 0xCCCC, fPrecompute ); // var B + Rwt_ManAddVar( p, 0xF0F0, fPrecompute ); // var C + Rwt_ManAddVar( p, 0xFF00, fPrecompute ); // var D + p->nClasses = 5; + // other stuff + p->nTravIds = 1; + p->pPerms4 = Extra_Permutations( 4 ); + p->vLevNums = Vec_IntAlloc( 50 ); + p->vFanins = Vec_PtrAlloc( 50 ); + p->vFaninsCur = Vec_PtrAlloc( 50 ); + p->vNodesTemp = Vec_PtrAlloc( 50 ); + if ( fPrecompute ) + { // precompute subgraphs +// Rwt_ManPrecompute( p ); +// Rwt_ManPrint( p ); +// Rwt_ManWriteToArray( p ); + } + else + { // load saved subgraphs + Rwt_ManLoadFromArray( p, 0 ); +// Rwt_ManPrint( p ); + Rwt_ManPreprocess( p ); + } +p->timeStart = clock() - clk; + return p; +} + +/**Function************************************************************* + + Synopsis [Stops rewriting manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwt_ManStop( Rwt_Man_t * p ) +{ + if ( p->vClasses ) + { + Rwt_Node_t * pNode; + int i, k; + Vec_VecForEachEntry( p->vClasses, pNode, i, k ) + Dec_GraphFree( (Dec_Graph_t *)pNode->pNext ); + } + if ( p->vClasses ) Vec_VecFree( p->vClasses ); + Vec_PtrFree( p->vNodesTemp ); + Vec_PtrFree( p->vForest ); + Vec_IntFree( p->vLevNums ); + Vec_PtrFree( p->vFanins ); + Vec_PtrFree( p->vFaninsCur ); + Mem_FixedStop( p->pMmNode, 0 ); + FREE( p->pMapInv ); + free( p->pTable ); + free( p->pPractical ); + free( p->pPerms4 ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Stops the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwt_ManPrintStats( Rwt_Man_t * p ) +{ + int i, Counter = 0; + for ( i = 0; i < 222; i++ ) + Counter += (p->nScores[i] > 0); + + printf( "Rewriting statistics:\n" ); + printf( "Total cuts tries = %8d.\n", p->nCutsGood ); + printf( "Bad cuts found = %8d.\n", p->nCutsBad ); + printf( "Total subgraphs = %8d.\n", p->nSubgraphs ); + printf( "Used NPN classes = %8d.\n", Counter ); + printf( "Nodes considered = %8d.\n", p->nNodesConsidered ); + printf( "Nodes rewritten = %8d.\n", p->nNodesRewritten ); + printf( "Calculated gain = %8d.\n", p->nNodesGained ); + PRT( "Start ", p->timeStart ); + PRT( "Cuts ", p->timeCut ); + PRT( "Truth ", p->timeTruth ); + PRT( "Resynthesis ", p->timeRes ); + PRT( " Mffc ", p->timeMffc ); + PRT( " Eval ", p->timeEval ); + PRT( "Update ", p->timeUpdate ); + PRT( "TOTAL ", p->timeTotal ); + +/* + printf( "The scores are:\n" ); + for ( i = 0; i < 222; i++ ) + if ( p->nScores[i] > 0 ) + { + extern void Ivy_TruthDsdComputePrint( unsigned uTruth ); + printf( "%3d = %8d canon = %5d ", i, p->nScores[i], p->pMapInv[i] ); + Ivy_TruthDsdComputePrint( (unsigned)p->pMapInv[i] | ((unsigned)p->pMapInv[i] << 16) ); + } + printf( "\n" ); +*/ +} + +/**Function************************************************************* + + Synopsis [Stops the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwt_ManPrintStatsFile( Rwt_Man_t * p ) +{ + FILE * pTable; + pTable = fopen( "stats.txt", "a+" ); + fprintf( pTable, "%d ", p->nCutsGood ); + fprintf( pTable, "%d ", p->nSubgraphs ); + fprintf( pTable, "%d ", p->nNodesRewritten ); + fprintf( pTable, "%d", p->nNodesGained ); + fprintf( pTable, "\n" ); + fclose( pTable ); +} + +/**Function************************************************************* + + Synopsis [Stops the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void * Rwt_ManReadDecs( Rwt_Man_t * p ) +{ + return p->pGraph; +} + +/**Function************************************************************* + + Synopsis [Stops the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Rwt_ManReadLeaves( Rwt_Man_t * p ) +{ + return p->vFanins; +} + +/**Function************************************************************* + + Synopsis [Stops the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Rwt_ManReadCompl( Rwt_Man_t * p ) +{ + return p->fCompl; +} + +/**Function************************************************************* + + Synopsis [Stops the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwt_ManAddTimeCuts( Rwt_Man_t * p, int Time ) +{ + p->timeCut += Time; +} + +/**Function************************************************************* + + Synopsis [Stops the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwt_ManAddTimeUpdate( Rwt_Man_t * p, int Time ) +{ + p->timeUpdate += Time; +} + +/**Function************************************************************* + + Synopsis [Stops the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwt_ManAddTimeTotal( Rwt_Man_t * p, int Time ) +{ + p->timeTotal += Time; +} + + +/**Function************************************************************* + + Synopsis [Precomputes AIG subgraphs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwt_Precompute() +{ + Rwt_Man_t * p; + p = Rwt_ManStart( 1 ); + Rwt_ManStop( p ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/aig/rwt/rwtUtil.c b/abc_with_bb_support/src/aig/rwt/rwtUtil.c new file mode 100644 index 000000000..c064a8b71 --- /dev/null +++ b/abc_with_bb_support/src/aig/rwt/rwtUtil.c @@ -0,0 +1,665 @@ +/**CFile**************************************************************** + + FileName [rwtUtil.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting package.] + + Synopsis [Various utilities.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: rwtUtil.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "rwt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// precomputed data +#ifdef _WIN32 +unsigned short s_RwtPracticalClasses[]; +unsigned short s_RwtAigSubgraphs[]; +#else +static unsigned short s_RwtPracticalClasses[]; +static unsigned short s_RwtAigSubgraphs[]; +#endif + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Adds the node to the end of the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwt_ListAddToTail( Rwt_Node_t ** ppList, Rwt_Node_t * pNode ) +{ + Rwt_Node_t * pTemp; + // find the last one + for ( pTemp = *ppList; pTemp; pTemp = pTemp->pNext ) + ppList = &pTemp->pNext; + // attach at the end + *ppList = pNode; +} + +/**Function************************************************************* + + Synopsis [Adds one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Rwt_Node_t * Rwt_ManAddVar( Rwt_Man_t * p, unsigned uTruth, int fPrecompute ) +{ + Rwt_Node_t * pNew; + pNew = (Rwt_Node_t *)Mem_FixedEntryFetch( p->pMmNode ); + pNew->Id = p->vForest->nSize; + pNew->TravId = 0; + pNew->uTruth = uTruth; + pNew->Level = 0; + pNew->Volume = 0; + pNew->fUsed = 1; + pNew->fExor = 0; + pNew->p0 = NULL; + pNew->p1 = NULL; + pNew->pNext = NULL; + Vec_PtrPush( p->vForest, pNew ); + if ( fPrecompute ) + Rwt_ListAddToTail( p->pTable + uTruth, pNew ); + return pNew; +} + +/**Function************************************************************* + + Synopsis [Adds one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Rwt_Node_t * Rwt_ManAddNode( Rwt_Man_t * p, Rwt_Node_t * p0, Rwt_Node_t * p1, int fExor, int Level, int Volume ) +{ + Rwt_Node_t * pNew; + unsigned uTruth; + // compute truth table, leve, volume + p->nConsidered++; + if ( fExor ) + uTruth = (p0->uTruth ^ p1->uTruth); + else + uTruth = (Rwt_IsComplement(p0)? ~Rwt_Regular(p0)->uTruth : Rwt_Regular(p0)->uTruth) & + (Rwt_IsComplement(p1)? ~Rwt_Regular(p1)->uTruth : Rwt_Regular(p1)->uTruth) & 0xFFFF; + // create the new node + pNew = (Rwt_Node_t *)Mem_FixedEntryFetch( p->pMmNode ); + pNew->Id = p->vForest->nSize; + pNew->TravId = 0; + pNew->uTruth = uTruth; + pNew->Level = Level; + pNew->Volume = Volume; + pNew->fUsed = 0; + pNew->fExor = fExor; + pNew->p0 = p0; + pNew->p1 = p1; + pNew->pNext = NULL; + Vec_PtrPush( p->vForest, pNew ); + // do not add if the node is not essential + if ( uTruth != p->puCanons[uTruth] ) + return pNew; + + // add to the list + p->nAdded++; + if ( p->pTable[uTruth] == NULL ) + p->nClasses++; + Rwt_ListAddToTail( p->pTable + uTruth, pNew ); + return pNew; +} + +/**Function************************************************************* + + Synopsis [Adds one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwt_Trav_rec( Rwt_Man_t * p, Rwt_Node_t * pNode, int * pVolume ) +{ + if ( pNode->fUsed || pNode->TravId == p->nTravIds ) + return; + pNode->TravId = p->nTravIds; + (*pVolume)++; + if ( pNode->fExor ) + (*pVolume)++; + Rwt_Trav_rec( p, Rwt_Regular(pNode->p0), pVolume ); + Rwt_Trav_rec( p, Rwt_Regular(pNode->p1), pVolume ); +} + +/**Function************************************************************* + + Synopsis [Adds one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwt_ManIncTravId( Rwt_Man_t * p ) +{ + Rwt_Node_t * pNode; + int i; + if ( p->nTravIds++ < 0x8FFFFFFF ) + return; + Vec_PtrForEachEntry( p->vForest, pNode, i ) + pNode->TravId = 0; + p->nTravIds = 1; +} + +/**Function************************************************************* + + Synopsis [Adds one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Rwt_ManNodeVolume( Rwt_Man_t * p, Rwt_Node_t * p0, Rwt_Node_t * p1 ) +{ + int Volume = 0; + Rwt_ManIncTravId( p ); + Rwt_Trav_rec( p, p0, &Volume ); + Rwt_Trav_rec( p, p1, &Volume ); + return Volume; +} + +/**Function************************************************************* + + Synopsis [Loads data.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwt_ManLoadFromArray( Rwt_Man_t * p, int fVerbose ) +{ + unsigned short * pArray = s_RwtAigSubgraphs; + Rwt_Node_t * p0, * p1; + unsigned Entry0, Entry1; + int Level, Volume, nEntries, fExor; + int i, clk = clock(); + + // reconstruct the forest + for ( i = 0; ; i++ ) + { + Entry0 = pArray[2*i + 0]; + Entry1 = pArray[2*i + 1]; + if ( Entry0 == 0 && Entry1 == 0 ) + break; + // get EXOR flag + fExor = (Entry0 & 1); + Entry0 >>= 1; + // get the nodes + p0 = p->vForest->pArray[Entry0 >> 1]; + p1 = p->vForest->pArray[Entry1 >> 1]; + // compute the level and volume of the new nodes + Level = 1 + RWT_MAX( p0->Level, p1->Level ); + Volume = 1 + Rwt_ManNodeVolume( p, p0, p1 ); + // set the complemented attributes + p0 = Rwt_NotCond( p0, (Entry0 & 1) ); + p1 = Rwt_NotCond( p1, (Entry1 & 1) ); + // add the node +// Rwt_ManTryNode( p, p0, p1, Level, Volume ); + Rwt_ManAddNode( p, p0, p1, fExor, Level, Volume + fExor ); + } + nEntries = i - 1; + if ( fVerbose ) + { + printf( "The number of classes = %d. Canonical nodes = %d.\n", p->nClasses, p->nAdded ); + printf( "The number of nodes loaded = %d. ", nEntries ); PRT( "Loading", clock() - clk ); + } +} + +/**Function************************************************************* + + Synopsis [Create practical classes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Rwt_ManGetPractical( Rwt_Man_t * p ) +{ + char * pPractical; + int i; + pPractical = ALLOC( char, p->nFuncs ); + memset( pPractical, 0, sizeof(char) * p->nFuncs ); + pPractical[0] = 1; + for ( i = 1; ; i++ ) + { + if ( s_RwtPracticalClasses[i] == 0 ) + break; + pPractical[ s_RwtPracticalClasses[i] ] = 1; + } + return pPractical; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + +// the following 135 practical NPN classes of 4-variable functions were computed +// by considering all 4-input cuts appearing in IWLS, MCNC, and ISCAS benchmarks +static unsigned short s_RwtPracticalClasses[] = +{ + 0x0000, 0x0001, 0x0003, 0x0006, 0x0007, 0x000f, 0x0016, 0x0017, 0x0018, 0x0019, 0x001b, + 0x001e, 0x001f, 0x003c, 0x003d, 0x003f, 0x0069, 0x006b, 0x006f, 0x007e, 0x007f, 0x00ff, + 0x0116, 0x0118, 0x0119, 0x011a, 0x011b, 0x011e, 0x011f, 0x012c, 0x012d, 0x012f, 0x013c, + 0x013d, 0x013e, 0x013f, 0x0168, 0x0169, 0x016f, 0x017f, 0x0180, 0x0181, 0x0182, 0x0183, + 0x0186, 0x0189, 0x018b, 0x018f, 0x0198, 0x0199, 0x019b, 0x01a8, 0x01a9, 0x01aa, 0x01ab, + 0x01ac, 0x01ad, 0x01ae, 0x01af, 0x01bf, 0x01e9, 0x01ea, 0x01eb, 0x01ee, 0x01ef, 0x01fe, + 0x033c, 0x033d, 0x033f, 0x0356, 0x0357, 0x0358, 0x0359, 0x035a, 0x035b, 0x035f, 0x0368, + 0x0369, 0x036c, 0x036e, 0x037d, 0x03c0, 0x03c1, 0x03c3, 0x03c7, 0x03cf, 0x03d4, 0x03d5, + 0x03d7, 0x03d8, 0x03d9, 0x03dc, 0x03dd, 0x03de, 0x03fc, 0x0660, 0x0661, 0x0666, 0x0669, + 0x066f, 0x0676, 0x067e, 0x0690, 0x0696, 0x0697, 0x069f, 0x06b1, 0x06b6, 0x06f0, 0x06f2, + 0x06f6, 0x06f9, 0x0776, 0x0778, 0x07b0, 0x07b1, 0x07b4, 0x07bc, 0x07f0, 0x07f2, 0x07f8, + 0x0ff0, 0x1683, 0x1696, 0x1698, 0x169e, 0x16e9, 0x178e, 0x17e8, 0x18e7, 0x19e6, 0x1be4, + 0x1ee1, 0x3cc3, 0x6996, 0x0000 +}; + +static unsigned short s_RwtAigSubgraphs[] = +{ + 0x0008,0x0002, 0x000a,0x0002, 0x0008,0x0003, 0x000a,0x0003, 0x0009,0x0002, + 0x000c,0x0002, 0x000e,0x0002, 0x000c,0x0003, 0x000e,0x0003, 0x000d,0x0002, + 0x000c,0x0004, 0x000e,0x0004, 0x000c,0x0005, 0x000e,0x0005, 0x000d,0x0004, + 0x0010,0x0002, 0x0012,0x0002, 0x0010,0x0003, 0x0012,0x0003, 0x0011,0x0002, + 0x0010,0x0004, 0x0012,0x0004, 0x0010,0x0005, 0x0012,0x0005, 0x0011,0x0004, + 0x0010,0x0006, 0x0012,0x0006, 0x0010,0x0007, 0x0012,0x0007, 0x0011,0x0006, + 0x0016,0x0005, 0x0014,0x0006, 0x0016,0x0006, 0x0014,0x0007, 0x0016,0x0007, + 0x0015,0x0006, 0x0014,0x0008, 0x0016,0x0008, 0x0014,0x0009, 0x0016,0x0009, + 0x0015,0x0008, 0x0018,0x0006, 0x001a,0x0006, 0x0018,0x0007, 0x001a,0x0007, + 0x0019,0x0006, 0x0018,0x0009, 0x001a,0x0009, 0x0019,0x0008, 0x001e,0x0005, + 0x001c,0x0006, 0x001e,0x0006, 0x001c,0x0007, 0x001e,0x0007, 0x001d,0x0006, + 0x001c,0x0008, 0x001e,0x0008, 0x001c,0x0009, 0x001e,0x0009, 0x001d,0x0008, + 0x0020,0x0006, 0x0022,0x0006, 0x0020,0x0007, 0x0022,0x0007, 0x0021,0x0006, + 0x0020,0x0008, 0x0022,0x0008, 0x0020,0x0009, 0x0022,0x0009, 0x0021,0x0008, + 0x0024,0x0006, 0x0026,0x0006, 0x0024,0x0007, 0x0026,0x0007, 0x0025,0x0006, + 0x0026,0x0008, 0x0024,0x0009, 0x0026,0x0009, 0x0025,0x0008, 0x0028,0x0004, + 0x002a,0x0004, 0x0028,0x0005, 0x002a,0x0007, 0x0028,0x0008, 0x002a,0x0009, + 0x0029,0x0008, 0x002a,0x000b, 0x0029,0x000a, 0x002a,0x000f, 0x0029,0x000e, + 0x002a,0x0011, 0x002a,0x0013, 0x002c,0x0004, 0x002e,0x0004, 0x002c,0x0005, + 0x002c,0x0009, 0x002e,0x0009, 0x002d,0x0008, 0x002d,0x000c, 0x002e,0x000f, + 0x002e,0x0011, 0x002e,0x0012, 0x0030,0x0004, 0x0032,0x0007, 0x0032,0x0009, + 0x0031,0x0008, 0x0032,0x000b, 0x0032,0x000d, 0x0032,0x000f, 0x0031,0x000e, + 0x0032,0x0013, 0x0034,0x0004, 0x0036,0x0004, 0x0034,0x0005, 0x0036,0x0005, + 0x0035,0x0004, 0x0036,0x0008, 0x0034,0x0009, 0x0036,0x0009, 0x0035,0x0008, + 0x0036,0x000b, 0x0036,0x000d, 0x0036,0x0011, 0x0035,0x0010, 0x0036,0x0013, + 0x0038,0x0004, 0x0039,0x0004, 0x0038,0x0009, 0x003a,0x0009, 0x0039,0x0008, + 0x0038,0x000b, 0x003a,0x000b, 0x003a,0x000d, 0x003a,0x0011, 0x003a,0x0012, + 0x0038,0x0013, 0x003a,0x0013, 0x003c,0x0002, 0x003e,0x0002, 0x003c,0x0003, + 0x003e,0x0005, 0x003e,0x0007, 0x003c,0x0008, 0x003e,0x0008, 0x003c,0x0009, + 0x003e,0x0009, 0x003d,0x0008, 0x003e,0x000d, 0x003e,0x0011, 0x003e,0x0013, + 0x003e,0x0017, 0x003e,0x001b, 0x003e,0x001d, 0x0040,0x0002, 0x0042,0x0002, + 0x0042,0x0005, 0x0041,0x0006, 0x0042,0x0008, 0x0041,0x0008, 0x0042,0x000d, + 0x0042,0x0011, 0x0042,0x0015, 0x0042,0x0019, 0x0042,0x001b, 0x0042,0x001c, + 0x0041,0x001c, 0x0044,0x0002, 0x0046,0x0003, 0x0045,0x0004, 0x0046,0x0007, + 0x0045,0x0008, 0x0046,0x000b, 0x0046,0x000f, 0x0046,0x0013, 0x0045,0x0012, + 0x0046,0x0017, 0x0046,0x001b, 0x0046,0x0021, 0x0048,0x0002, 0x004a,0x0002, + 0x0048,0x0003, 0x004a,0x0003, 0x0049,0x0002, 0x0048,0x0008, 0x004a,0x0008, + 0x0048,0x0009, 0x004a,0x0009, 0x0049,0x0008, 0x004a,0x000b, 0x004a,0x000f, + 0x004a,0x0011, 0x004a,0x0012, 0x004a,0x0013, 0x004a,0x0015, 0x004a,0x0019, + 0x004a,0x001b, 0x004a,0x001d, 0x004c,0x0002, 0x004c,0x0003, 0x004d,0x0002, + 0x004c,0x0008, 0x004e,0x0008, 0x004c,0x0009, 0x004e,0x0009, 0x004d,0x0008, + 0x004c,0x000b, 0x004e,0x000b, 0x004c,0x000f, 0x004e,0x000f, 0x004e,0x0011, + 0x004c,0x0012, 0x004c,0x0013, 0x004e,0x0013, 0x004e,0x0015, 0x004c,0x0017, + 0x004e,0x0019, 0x004c,0x001b, 0x004e,0x001b, 0x004c,0x001c, 0x004c,0x001d, + 0x004e,0x001d, 0x0050,0x0004, 0x0052,0x0004, 0x0050,0x0006, 0x0052,0x0009, + 0x0052,0x000d, 0x0052,0x000f, 0x0052,0x0013, 0x0052,0x0017, 0x0052,0x0019, + 0x0052,0x001d, 0x0052,0x001f, 0x0052,0x0021, 0x0052,0x0023, 0x0052,0x0024, + 0x0052,0x0025, 0x0051,0x0024, 0x0052,0x0027, 0x0054,0x0004, 0x0056,0x0004, + 0x0054,0x0005, 0x0056,0x0006, 0x0054,0x0007, 0x0056,0x0011, 0x0056,0x001b, + 0x0056,0x001e, 0x0054,0x001f, 0x0056,0x001f, 0x0056,0x0020, 0x0054,0x0021, + 0x0055,0x0020, 0x0056,0x0024, 0x0054,0x0025, 0x0056,0x0025, 0x0055,0x0024, + 0x0054,0x0027, 0x0056,0x0027, 0x0055,0x0026, 0x005a,0x0007, 0x005a,0x0009, + 0x005a,0x000b, 0x005a,0x0015, 0x005a,0x001f, 0x0059,0x0020, 0x0058,0x0024, + 0x005a,0x0024, 0x005a,0x0027, 0x0059,0x0026, 0x005c,0x0004, 0x005e,0x0004, + 0x005c,0x0005, 0x005e,0x0006, 0x005c,0x0007, 0x005d,0x0006, 0x005e,0x000d, + 0x005e,0x0013, 0x005e,0x0017, 0x005c,0x001f, 0x005d,0x001e, 0x005e,0x0020, + 0x005e,0x0021, 0x005e,0x0022, 0x005e,0x0023, 0x005c,0x0024, 0x005e,0x0024, + 0x005c,0x0025, 0x005e,0x0025, 0x005d,0x0024, 0x005e,0x0026, 0x005e,0x0027, + 0x0062,0x0004, 0x0061,0x0004, 0x0062,0x0006, 0x0061,0x0006, 0x0060,0x000f, + 0x0060,0x0013, 0x0062,0x0013, 0x0060,0x0019, 0x0062,0x001c, 0x0060,0x001d, + 0x0062,0x001d, 0x0062,0x001f, 0x0060,0x0021, 0x0060,0x0023, 0x0062,0x0024, + 0x0060,0x0027, 0x0061,0x0026, 0x0064,0x0002, 0x0066,0x0002, 0x0064,0x0006, + 0x0066,0x0007, 0x0066,0x0009, 0x0066,0x000d, 0x0066,0x0013, 0x0066,0x0015, + 0x0066,0x0017, 0x0066,0x0019, 0x0066,0x001a, 0x0065,0x001a, 0x0066,0x001f, + 0x0066,0x0023, 0x0066,0x0027, 0x0066,0x002f, 0x0066,0x0030, 0x006a,0x0002, + 0x0068,0x0003, 0x0068,0x0006, 0x006a,0x0006, 0x006a,0x0011, 0x0068,0x0016, + 0x0068,0x0017, 0x006a,0x0017, 0x006a,0x001a, 0x006a,0x001b, 0x006a,0x0025, + 0x006a,0x002d, 0x006e,0x0003, 0x006e,0x0007, 0x006e,0x0009, 0x006e,0x000b, + 0x006e,0x0015, 0x006e,0x0016, 0x006e,0x0017, 0x006c,0x001a, 0x006e,0x001a, + 0x006e,0x001f, 0x006e,0x002b, 0x006e,0x0035, 0x0070,0x0002, 0x0070,0x0003, + 0x0072,0x0006, 0x0070,0x0007, 0x0071,0x0006, 0x0072,0x000b, 0x0072,0x000f, + 0x0072,0x0013, 0x0070,0x0015, 0x0071,0x0014, 0x0072,0x0017, 0x0072,0x0018, + 0x0070,0x0019, 0x0072,0x0019, 0x0070,0x001a, 0x0070,0x001b, 0x0072,0x001b, + 0x0071,0x001a, 0x0072,0x0021, 0x0072,0x0029, 0x0076,0x0002, 0x0076,0x0003, + 0x0075,0x0002, 0x0076,0x0006, 0x0074,0x0007, 0x0076,0x0007, 0x0075,0x0006, + 0x0076,0x000d, 0x0076,0x0011, 0x0076,0x0013, 0x0075,0x0014, 0x0076,0x0019, + 0x0076,0x001a, 0x0076,0x001b, 0x0075,0x001c, 0x0074,0x0023, 0x0075,0x0022, + 0x0074,0x0026, 0x0076,0x0026, 0x0074,0x0027, 0x0076,0x002b, 0x0076,0x002f, + 0x0078,0x0002, 0x0078,0x0004, 0x007a,0x0004, 0x007a,0x0005, 0x0079,0x0004, + 0x007a,0x0009, 0x007a,0x000a, 0x007a,0x000b, 0x007a,0x000d, 0x007a,0x000f, + 0x007a,0x0010, 0x007a,0x0011, 0x007a,0x0012, 0x007a,0x0013, 0x007a,0x0017, + 0x007a,0x001b, 0x007a,0x0021, 0x007a,0x0027, 0x007a,0x002b, 0x007a,0x002f, + 0x007a,0x0030, 0x0079,0x0034, 0x007a,0x0039, 0x007a,0x003a, 0x007e,0x0002, + 0x007c,0x0004, 0x007e,0x0004, 0x007e,0x000c, 0x007c,0x000d, 0x007e,0x0011, + 0x007e,0x0013, 0x007e,0x001b, 0x007e,0x0025, 0x007e,0x002d, 0x007e,0x0037, + 0x0082,0x0003, 0x0082,0x0005, 0x0082,0x0009, 0x0082,0x000b, 0x0080,0x0010, + 0x0082,0x0010, 0x0082,0x0012, 0x0082,0x0015, 0x0082,0x001f, 0x0082,0x002b, + 0x0082,0x0035, 0x0082,0x0039, 0x0082,0x003f, 0x0084,0x0002, 0x0086,0x0002, + 0x0084,0x0003, 0x0086,0x0003, 0x0085,0x0002, 0x0086,0x0004, 0x0084,0x0005, + 0x0085,0x0004, 0x0086,0x000a, 0x0084,0x000b, 0x0085,0x000a, 0x0086,0x000d, + 0x0086,0x000e, 0x0086,0x000f, 0x0084,0x0010, 0x0084,0x0011, 0x0086,0x0011, + 0x0085,0x0010, 0x0084,0x0012, 0x0084,0x0013, 0x0086,0x0013, 0x0085,0x0012, + 0x0086,0x0019, 0x0086,0x0023, 0x0086,0x0029, 0x0086,0x0033, 0x0086,0x0039, + 0x008a,0x0003, 0x0089,0x0002, 0x0088,0x0004, 0x008a,0x0004, 0x0088,0x0005, + 0x0089,0x0004, 0x008a,0x000b, 0x008a,0x0010, 0x0088,0x0011, 0x008a,0x0011, + 0x0089,0x0010, 0x0088,0x0012, 0x008a,0x0012, 0x0089,0x0012, 0x008a,0x0017, + 0x008a,0x001b, 0x0089,0x0020, 0x008a,0x0025, 0x0088,0x0027, 0x008a,0x002b, + 0x008a,0x002f, 0x008a,0x0039, 0x0088,0x003a, 0x008d,0x0044, 0x0092,0x0009, + 0x0092,0x0025, 0x0092,0x0029, 0x0092,0x002d, 0x0092,0x0033, 0x0092,0x0037, + 0x0092,0x003d, 0x0092,0x0041, 0x0095,0x0002, 0x0095,0x0004, 0x0095,0x0010, + 0x0095,0x0012, 0x0096,0x0021, 0x0096,0x0029, 0x0095,0x002e, 0x0096,0x0030, + 0x0096,0x0033, 0x0096,0x003a, 0x0096,0x0043, 0x009a,0x0008, 0x009a,0x0009, + 0x0099,0x0008, 0x009a,0x0011, 0x009a,0x0023, 0x009a,0x0033, 0x009a,0x003d, + 0x009a,0x0044, 0x009a,0x0045, 0x0099,0x0044, 0x009d,0x0002, 0x009e,0x0008, + 0x009c,0x0009, 0x009e,0x0009, 0x009d,0x0008, 0x009e,0x0011, 0x009d,0x0010, + 0x009e,0x001f, 0x009e,0x003f, 0x00a0,0x0009, 0x00a0,0x0011, 0x00a2,0x0030, + 0x00a2,0x0033, 0x00a6,0x0006, 0x00a6,0x0007, 0x00a6,0x0011, 0x00a6,0x0044, + 0x00a6,0x004b, 0x00aa,0x0007, 0x00aa,0x0015, 0x00ae,0x0006, 0x00ae,0x0011, + 0x00ae,0x001b, 0x00ae,0x0025, 0x00ae,0x003d, 0x00ae,0x0041, 0x00ae,0x0043, + 0x00ae,0x0045, 0x00b2,0x0006, 0x00b0,0x0007, 0x00b1,0x0006, 0x00b2,0x0017, + 0x00b1,0x0016, 0x00b0,0x0019, 0x00b2,0x0021, 0x00b2,0x003d, 0x00b5,0x004a, + 0x00ba,0x0009, 0x00ba,0x000f, 0x00bc,0x0009, 0x00be,0x0009, 0x00be,0x000f, + 0x00bd,0x000e, 0x00be,0x0017, 0x00c2,0x0009, 0x00c2,0x0019, 0x00c2,0x001f, + 0x00c2,0x0033, 0x00c6,0x0009, 0x00c5,0x000e, 0x00c6,0x0015, 0x00c6,0x0023, + 0x00c4,0x002d, 0x00c6,0x002f, 0x00c5,0x002e, 0x00c6,0x0045, 0x00ce,0x0007, + 0x00ce,0x0021, 0x00ce,0x0023, 0x00ce,0x0025, 0x00ce,0x0027, 0x00ce,0x0033, + 0x00ce,0x003d, 0x00d2,0x0006, 0x00d0,0x0015, 0x00d0,0x001b, 0x00d2,0x001b, + 0x00d1,0x001a, 0x00d0,0x001f, 0x00d2,0x0025, 0x00d1,0x0024, 0x00d2,0x0037, + 0x00d2,0x0041, 0x00d2,0x0045, 0x00d9,0x0044, 0x00e1,0x0004, 0x00e2,0x000d, + 0x00e2,0x0021, 0x00e0,0x003a, 0x00e6,0x003d, 0x00e6,0x0061, 0x00e6,0x0067, + 0x00e9,0x0004, 0x00ea,0x0008, 0x00ea,0x0009, 0x00ea,0x0039, 0x00e9,0x0038, + 0x00ea,0x003f, 0x00ec,0x000d, 0x00ee,0x000d, 0x00ee,0x0037, 0x00f2,0x003d, + 0x00f2,0x0062, 0x00f5,0x0002, 0x00fa,0x0017, 0x00fa,0x003d, 0x00fe,0x0006, + 0x00fd,0x0006, 0x00fc,0x0015, 0x00fe,0x001b, 0x00fc,0x0025, 0x00fe,0x0025, + 0x00fd,0x0024, 0x00fe,0x0041, 0x00fe,0x004d, 0x00fd,0x004e, 0x0101,0x0014, + 0x0106,0x004d, 0x010a,0x0009, 0x010a,0x000b, 0x0109,0x000a, 0x010a,0x004f, + 0x010a,0x0058, 0x010e,0x0008, 0x010c,0x0009, 0x010e,0x0009, 0x010d,0x0008, + 0x010e,0x000b, 0x010e,0x002b, 0x010d,0x002a, 0x010e,0x0035, 0x010e,0x003d, + 0x010e,0x003f, 0x010e,0x0049, 0x010e,0x0057, 0x010d,0x0056, 0x010d,0x0058, + 0x0111,0x0004, 0x0111,0x0006, 0x0110,0x0009, 0x0112,0x0009, 0x0111,0x0008, + 0x0112,0x002f, 0x0110,0x0035, 0x0110,0x0037, 0x0112,0x0039, 0x0112,0x003d, + 0x0112,0x003f, 0x0112,0x0045, 0x0111,0x0044, 0x0112,0x004b, 0x0112,0x0059, + 0x0112,0x0069, 0x0112,0x007f, 0x0116,0x0009, 0x0115,0x0008, 0x0114,0x000b, + 0x0116,0x000b, 0x0116,0x0058, 0x011a,0x0015, 0x011a,0x001f, 0x011a,0x002b, + 0x011a,0x003f, 0x011a,0x0049, 0x011a,0x0085, 0x011e,0x0007, 0x011e,0x0019, + 0x011e,0x001b, 0x011e,0x0023, 0x011e,0x0027, 0x011e,0x002f, 0x011e,0x0043, + 0x011e,0x004b, 0x011e,0x004e, 0x011e,0x004f, 0x011e,0x005f, 0x011e,0x0061, + 0x011e,0x0065, 0x011e,0x0083, 0x0122,0x0006, 0x0120,0x0007, 0x0122,0x0007, + 0x0121,0x0006, 0x0122,0x0049, 0x0121,0x004e, 0x0122,0x008f, 0x0125,0x0004, + 0x0124,0x0007, 0x0125,0x0006, 0x0124,0x001b, 0x0126,0x001b, 0x0126,0x0045, + 0x0126,0x0087, 0x0128,0x0007, 0x0129,0x0006, 0x012a,0x0019, 0x012a,0x003d, + 0x012a,0x0051, 0x012a,0x0065, 0x012a,0x0083, 0x012d,0x005a, 0x0132,0x0009, + 0x0132,0x008f, 0x0134,0x0009, 0x0135,0x003e, 0x013a,0x003d, 0x013a,0x0044, + 0x0139,0x0044, 0x013e,0x0009, 0x013d,0x0008, 0x013c,0x003d, 0x013c,0x0044, + 0x013c,0x0053, 0x013e,0x008f, 0x013e,0x0095, 0x0142,0x0044, 0x0142,0x0097, + 0x0142,0x009e, 0x0144,0x0007, 0x0148,0x0015, 0x0148,0x001c, 0x0148,0x001f, + 0x0148,0x0026, 0x0149,0x0086, 0x014d,0x0006, 0x014e,0x0044, 0x014d,0x0048, + 0x014e,0x009e, 0x0152,0x0009, 0x0151,0x00a6, 0x0155,0x0030, 0x015d,0x003a, + 0x0162,0x009e, 0x0164,0x000f, 0x0164,0x0013, 0x0169,0x000e, 0x0174,0x0009, + 0x0179,0x0008, 0x0180,0x0009, 0x0181,0x0044, 0x0186,0x0044, 0x0185,0x0044, + 0x018a,0x0068, 0x0195,0x004e, 0x01a6,0x0009, 0x01a5,0x0008, 0x01b1,0x003a, + 0x01c4,0x0029, 0x01c4,0x0030, 0x01ca,0x008f, 0x01ca,0x0095, 0x01cc,0x0029, + 0x01cc,0x0033, 0x01ce,0x003d, 0x01d6,0x00b2, 0x01d8,0x0009, 0x01d9,0x002a, + 0x01d9,0x0056, 0x01d9,0x00a4, 0x01dd,0x003a, 0x01e2,0x00b2, 0x01e6,0x0013, + 0x01e6,0x009f, 0x01e6,0x00ba, 0x01e6,0x00c0, 0x01e6,0x00d3, 0x01e6,0x00d5, + 0x01e6,0x00e5, 0x01e8,0x0005, 0x01f2,0x0013, 0x01f2,0x0095, 0x01f2,0x009f, + 0x01f2,0x00ba, 0x01f2,0x00c0, 0x01f2,0x00d3, 0x0202,0x008f, 0x0202,0x0095, + 0x0202,0x00f3, 0x0202,0x00f9, 0x020a,0x0044, 0x0209,0x00b4, 0x020e,0x0009, + 0x020d,0x0008, 0x020c,0x003d, 0x020c,0x0044, 0x020c,0x0053, 0x020e,0x008f, + 0x020e,0x0095, 0x020c,0x00b1, 0x020e,0x00f3, 0x020e,0x00f9, 0x0210,0x0013, + 0x0211,0x0024, 0x0210,0x0026, 0x0219,0x0004, 0x021e,0x008f, 0x021e,0x0095, + 0x0221,0x003a, 0x0230,0x0009, 0x0236,0x0009, 0x0234,0x0029, 0x0234,0x0030, + 0x0234,0x0033, 0x0234,0x003a, 0x0234,0x003d, 0x0234,0x0044, 0x0235,0x00a6, + 0x023a,0x0009, 0x023d,0x003a, 0x0245,0x0044, 0x0249,0x003a, 0x024e,0x009e, + 0x024e,0x0106, 0x0251,0x0026, 0x0258,0x0013, 0x0259,0x0024, 0x0258,0x0061, + 0x0259,0x0086, 0x0258,0x00c7, 0x0258,0x00df, 0x0259,0x00ec, 0x0258,0x00fc, + 0x025d,0x0024, 0x025d,0x00de, 0x0260,0x00f6, 0x0268,0x0009, 0x0269,0x0044, + 0x0268,0x00f3, 0x0268,0x00f9, 0x026d,0x003a, 0x0270,0x0068, 0x0275,0x003a, + 0x027a,0x0044, 0x0279,0x0044, 0x027e,0x007e, 0x0281,0x0044, 0x0285,0x0008, + 0x028d,0x0006, 0x028d,0x00d2, 0x0295,0x00cc, 0x0296,0x00f6, 0x0295,0x00f8, + 0x0299,0x0030, 0x029e,0x007e, 0x029d,0x0080, 0x02a6,0x008f, 0x02a6,0x0095, + 0x02aa,0x0029, 0x02aa,0x0030, 0x02b5,0x0008, 0x02b9,0x003a, 0x02bd,0x0004, + 0x02bd,0x00fc, 0x02c2,0x00b2, 0x02c1,0x00b4, 0x02c4,0x0029, 0x02c8,0x0029, + 0x02c8,0x0033, 0x02ca,0x003d, 0x02ce,0x0029, 0x02ce,0x0030, 0x02d2,0x0068, + 0x02d1,0x006a, 0x02d5,0x006a, 0x02d9,0x0008, 0x02de,0x012c, 0x02e2,0x012c, + 0x02e4,0x0009, 0x02e5,0x002a, 0x02e5,0x0056, 0x02e5,0x012c, 0x02ea,0x0029, + 0x02ea,0x0030, 0x02e9,0x0030, 0x02ec,0x0029, 0x02ec,0x0030, 0x02ee,0x012c, + 0x02f1,0x0068, 0x02f1,0x00b2, 0x02f1,0x0108, 0x02f1,0x012c, 0x02f6,0x0013, + 0x02f6,0x0015, 0x02f6,0x001f, 0x02f6,0x0030, 0x02f6,0x0065, 0x02f6,0x0067, + 0x02f6,0x009f, 0x02f6,0x00b6, 0x02f6,0x00b9, 0x02f6,0x00c0, 0x02f6,0x00cf, + 0x02f6,0x0107, 0x02f6,0x010b, 0x02f6,0x010f, 0x02f6,0x0115, 0x02f6,0x012d, + 0x02f6,0x0134, 0x02f6,0x0153, 0x02f6,0x0171, 0x02f6,0x0176, 0x02f8,0x0003, + 0x02fa,0x017b, 0x02fc,0x00ba, 0x02fc,0x00d3, 0x0302,0x0013, 0x0302,0x001f, + 0x0302,0x0030, 0x0302,0x005d, 0x0302,0x0065, 0x0302,0x0067, 0x0302,0x0099, + 0x0302,0x009f, 0x0302,0x00ad, 0x0302,0x00b9, 0x0302,0x00c0, 0x0302,0x00cf, + 0x0301,0x00d2, 0x0301,0x00fe, 0x0302,0x0107, 0x0302,0x010b, 0x0302,0x010f, + 0x0302,0x0117, 0x0302,0x0134, 0x0302,0x0153, 0x0302,0x0157, 0x0302,0x0176, + 0x0306,0x0029, 0x0308,0x00b2, 0x0309,0x00dc, 0x030d,0x00f8, 0x0312,0x00f3, + 0x0318,0x007e, 0x031d,0x0080, 0x0321,0x0008, 0x0321,0x0094, 0x0326,0x017b, + 0x0326,0x0181, 0x0329,0x012e, 0x032a,0x017b, 0x032a,0x0181, 0x032e,0x008f, + 0x032e,0x0095, 0x032e,0x00f3, 0x032e,0x00f9, 0x0332,0x0009, 0x0331,0x0008, + 0x0330,0x003d, 0x0330,0x0044, 0x0330,0x0053, 0x0332,0x008f, 0x0332,0x0095, + 0x0330,0x00b1, 0x0332,0x00f3, 0x0332,0x00f9, 0x0330,0x0127, 0x0332,0x017b, + 0x0332,0x0181, 0x033c,0x0013, 0x033c,0x001c, 0x033d,0x0086, 0x033d,0x00ec, + 0x033d,0x0172, 0x033e,0x019d, 0x0345,0x0002, 0x0344,0x008f, 0x0344,0x00f3, + 0x034d,0x0030, 0x0352,0x0033, 0x0354,0x0029, 0x0354,0x0030, 0x035a,0x0009, + 0x035a,0x017b, 0x035a,0x019b, 0x035a,0x01a2, 0x035e,0x0181, 0x0360,0x0009, + 0x0366,0x0009, 0x0364,0x0029, 0x0364,0x0030, 0x0364,0x0033, 0x0364,0x003a, + 0x0364,0x003d, 0x0364,0x0044, 0x0369,0x0030, 0x0370,0x0029, 0x0370,0x0030, + 0x0376,0x0033, 0x037a,0x0009, 0x037a,0x019b, 0x037a,0x01a2, 0x037c,0x0009, + 0x0382,0x0181, 0x0386,0x0009, 0x0384,0x0029, 0x0384,0x0030, 0x0384,0x0033, + 0x0384,0x003a, 0x0384,0x003d, 0x0384,0x0044, 0x038a,0x0044, 0x038a,0x009e, + 0x038a,0x0106, 0x038a,0x0198, 0x038d,0x010e, 0x038d,0x0152, 0x038d,0x0158, + 0x0392,0x009e, 0x0392,0x0106, 0x0392,0x0198, 0x0395,0x0086, 0x0395,0x009a, + 0x0395,0x00ec, 0x0395,0x0172, 0x0398,0x014e, 0x0398,0x0175, 0x0398,0x018d, + 0x039c,0x0023, 0x039c,0x0027, 0x039c,0x00ef, 0x039c,0x0139, 0x039c,0x0168, + 0x03a0,0x0019, 0x03a0,0x001d, 0x03a0,0x0023, 0x03a0,0x0027, 0x03a1,0x004e, + 0x03a4,0x0162, 0x03a4,0x0183, 0x03a8,0x0013, 0x03a8,0x0027, 0x03a8,0x0133, + 0x03a8,0x0148, 0x03a8,0x0181, 0x03ac,0x0013, 0x03ac,0x0027, 0x03b0,0x017b, + 0x03b0,0x0181, 0x03b4,0x004b, 0x03b4,0x00e0, 0x03b4,0x00fb, 0x03b8,0x000f, + 0x03b8,0x0013, 0x03b8,0x00ab, 0x03b8,0x00bf, 0x03b8,0x00d0, 0x03bd,0x00da, + 0x03bd,0x012c, 0x03c8,0x000f, 0x03c8,0x0013, 0x03c8,0x0019, 0x03c8,0x001d, + 0x03cd,0x0086, 0x03cd,0x00ec, 0x03cd,0x0172, 0x03d2,0x00e0, 0x03d2,0x00ef, + 0x03d2,0x0112, 0x03d2,0x0139, 0x03d2,0x0168, 0x03d6,0x017b, 0x03d6,0x0181, + 0x03da,0x0133, 0x03da,0x0148, 0x03e2,0x0023, 0x03e2,0x0027, 0x03e6,0x0027, + 0x03e6,0x0181, 0x03ee,0x017b, 0x03ee,0x0181, 0x03fe,0x003d, 0x0401,0x012a, + 0x0401,0x019e, 0x0405,0x01a0, 0x040a,0x000d, 0x040a,0x011f, 0x040a,0x016f, + 0x040d,0x012a, 0x0412,0x017b, 0x041a,0x0033, 0x041a,0x003d, 0x041a,0x0181, + 0x0421,0x0086, 0x0421,0x009a, 0x0421,0x00ec, 0x0421,0x0172, 0x042e,0x0205, + 0x043a,0x0205, 0x043e,0x017b, 0x0442,0x01f5, 0x044c,0x0007, 0x0452,0x0033, + 0x0452,0x01ce, 0x0452,0x01d0, 0x0452,0x01f1, 0x0452,0x01fb, 0x0452,0x0225, + 0x0454,0x0005, 0x045a,0x0033, 0x045a,0x0181, 0x045a,0x01ce, 0x045a,0x01d0, + 0x045a,0x01f1, 0x0469,0x01de, 0x046e,0x0181, 0x047a,0x01ce, 0x047a,0x01f1, + 0x0485,0x012c, 0x0489,0x012c, 0x0490,0x01d8, 0x0496,0x0033, 0x0496,0x003d, + 0x0498,0x008f, 0x0498,0x00f3, 0x049e,0x0044, 0x049e,0x0221, 0x04a1,0x0006, + 0x04a2,0x0044, 0x04a6,0x0221, 0x04a9,0x0004, 0x04ac,0x0027, 0x04b1,0x009a, + 0x04b6,0x0097, 0x04b8,0x0027, 0x04c6,0x0219, 0x04ca,0x017b, 0x04cc,0x004b, + 0x04d0,0x00ab, 0x04d6,0x017b, 0x04d8,0x000f, 0x04d8,0x0019, 0x04d8,0x0033, + 0x04d8,0x003d, 0x04de,0x003d, 0x04de,0x0103, 0x04de,0x018b, 0x04de,0x0231, + 0x04e2,0x0044, 0x04e2,0x009e, 0x04e2,0x0106, 0x04e2,0x0198, 0x04e5,0x01a4, + 0x04e5,0x01b6, 0x04ea,0x009e, 0x04ea,0x0106, 0x04ea,0x0198, 0x04ed,0x002e, + 0x04ed,0x0038, 0x04ed,0x00a2, 0x04f1,0x0086, 0x04f1,0x009a, 0x04f1,0x00ec, + 0x04f1,0x0172, 0x04f9,0x004e, 0x04f8,0x0229, 0x04f8,0x022d, 0x0500,0x023e, + 0x0504,0x0217, 0x0510,0x00f3, 0x0514,0x0043, 0x0514,0x004d, 0x0514,0x00c3, + 0x0514,0x013d, 0x0514,0x0215, 0x0514,0x0232, 0x0515,0x0260, 0x0519,0x002a, + 0x0518,0x0030, 0x0518,0x0067, 0x0518,0x00c9, 0x0518,0x01eb, 0x0518,0x01ef, + 0x051c,0x0139, 0x051c,0x0168, 0x0520,0x0027, 0x0526,0x014e, 0x0526,0x0175, + 0x0526,0x018d, 0x052d,0x0200, 0x0532,0x0021, 0x0532,0x00bf, 0x0532,0x00d0, + 0x0532,0x0239, 0x0532,0x0266, 0x053d,0x0024, 0x053d,0x00da, 0x054a,0x000f, + 0x054a,0x00ab, 0x054a,0x023a, 0x054e,0x0043, 0x054e,0x004d, 0x054e,0x00c3, + 0x054e,0x013d, 0x054e,0x0215, 0x054e,0x0232, 0x054e,0x029d, 0x0552,0x014e, + 0x0552,0x018d, 0x0556,0x00f3, 0x0556,0x01e4, 0x055a,0x0299, 0x055d,0x0086, + 0x055d,0x009a, 0x055d,0x00ec, 0x055d,0x0172, 0x0566,0x01dc, 0x0566,0x02a5, + 0x056d,0x020a, 0x057a,0x003d, 0x057a,0x01d4, 0x057a,0x01f3, 0x0579,0x025e, + 0x057e,0x0139, 0x057e,0x0168, 0x0581,0x0006, 0x0586,0x017b, 0x0586,0x0181, + 0x0586,0x028c, 0x0588,0x0007, 0x058e,0x0033, 0x058e,0x008f, 0x058e,0x01d0, + 0x058e,0x027c, 0x0590,0x0003, 0x0596,0x0033, 0x0596,0x008f, 0x0596,0x0095, + 0x0596,0x01d0, 0x0596,0x027c, 0x05a2,0x026f, 0x05a5,0x0284, 0x05aa,0x017b, + 0x05ac,0x0205, 0x05b2,0x008f, 0x05b6,0x017b, 0x05b8,0x01da, 0x05c1,0x0276, + 0x05c6,0x0248, 0x05c8,0x0247, 0x05c8,0x027e, 0x05cc,0x003d, 0x05cc,0x01d4, + 0x05cc,0x01f3, 0x05d0,0x014e, 0x05d0,0x018d, 0x05da,0x00f9, 0x05dd,0x0006, + 0x05de,0x0044, 0x05e5,0x002e, 0x05e6,0x02f1, 0x05ea,0x01d4, 0x05ea,0x01f3, + 0x05ea,0x022d, 0x05ed,0x0002, 0x05f6,0x0027, 0x05fa,0x0097, 0x05fc,0x003d, + 0x0602,0x003d, 0x0606,0x00f3, 0x060a,0x0027, 0x060e,0x003d, 0x060e,0x0103, + 0x060e,0x018b, 0x060e,0x0231, 0x060e,0x02d1, 0x0611,0x01fc, 0x0611,0x0234, + 0x061a,0x0287, 0x061d,0x0214, 0x0621,0x01d4, 0x062a,0x0027, 0x062a,0x022d, + 0x062e,0x009e, 0x062e,0x0106, 0x062e,0x0198, 0x0632,0x009e, 0x0632,0x0106, + 0x0632,0x0198, 0x0639,0x0042, 0x0639,0x00b2, 0x0639,0x0108, 0x063d,0x01f8, + 0x0641,0x0086, 0x0641,0x009a, 0x0641,0x00ec, 0x0641,0x0172, 0x0645,0x0044, + 0x0649,0x0042, 0x0648,0x0087, 0x0648,0x00ed, 0x0648,0x0173, 0x0649,0x01a0, + 0x0648,0x0241, 0x0648,0x026f, 0x0648,0x02df, 0x0648,0x0307, 0x064c,0x023a, + 0x064c,0x02b3, 0x0651,0x0062, 0x0650,0x0217, 0x0651,0x02ac, 0x0650,0x02d6, + 0x0655,0x0042, 0x065d,0x0042, 0x0664,0x02b1, 0x0664,0x02ce, 0x0669,0x0238, + 0x066d,0x002a, 0x066c,0x0039, 0x066d,0x01f6, 0x066c,0x0213, 0x066c,0x022e, + 0x066d,0x02a2, 0x066c,0x02e1, 0x0671,0x002a, 0x0670,0x0030, 0x0670,0x0067, + 0x0670,0x00c9, 0x0670,0x01eb, 0x0670,0x01ef, 0x0670,0x02c3, 0x0675,0x0020, + 0x0678,0x0133, 0x0678,0x0148, 0x067c,0x0027, 0x0681,0x023a, 0x0684,0x0021, + 0x0684,0x00bf, 0x0684,0x00d0, 0x0689,0x01fc, 0x068e,0x0162, 0x068e,0x0183, + 0x0691,0x0200, 0x0696,0x0023, 0x0696,0x00e0, 0x0696,0x00fb, 0x0696,0x0268, + 0x069a,0x0282, 0x069d,0x007e, 0x06a2,0x004b, 0x06a2,0x023e, 0x06a2,0x02dc, + 0x06a6,0x0097, 0x06aa,0x02b1, 0x06aa,0x02ce, 0x06ae,0x0039, 0x06ae,0x0213, + 0x06ae,0x022e, 0x06ae,0x02e1, 0x06b2,0x0162, 0x06b2,0x0183, 0x06b6,0x0023, + 0x06b6,0x00e0, 0x06b6,0x00fb, 0x06ba,0x008f, 0x06ba,0x01e4, 0x06be,0x034b, + 0x06c1,0x0086, 0x06c1,0x009a, 0x06c1,0x00ec, 0x06c1,0x0172, 0x06c6,0x01da, + 0x06c6,0x0280, 0x06c6,0x0351, 0x06ce,0x008f, 0x06d2,0x01e3, 0x06d2,0x0287, + 0x06d2,0x0353, 0x06d6,0x027a, 0x06d6,0x029b, 0x06da,0x0033, 0x06da,0x01ce, + 0x06da,0x01f1, 0x06de,0x0133, 0x06de,0x0148, 0x06e2,0x0021, 0x06e2,0x00bf, + 0x06e2,0x00d0, 0x06e5,0x023a, 0x06e9,0x0004, 0x06ee,0x028c, 0x06ee,0x0338, + 0x06f2,0x0328, 0x06f2,0x0330, 0x06f4,0x0005, 0x06f9,0x01e0, 0x06fe,0x0328, + 0x06fe,0x0330, 0x0702,0x003d, 0x0702,0x00f3, 0x0702,0x0330, 0x0704,0x0003, + 0x070a,0x003d, 0x070a,0x00f3, 0x070a,0x01d4, 0x070a,0x01f3, 0x070a,0x0330, + 0x0711,0x032a, 0x0711,0x032e, 0x0716,0x003d, 0x0718,0x0205, 0x0718,0x0282, + 0x071e,0x00f3, 0x0720,0x01dc, 0x0720,0x02a5, 0x0726,0x0324, 0x072a,0x028a, + 0x072a,0x02a7, 0x0729,0x031c, 0x0729,0x032a, 0x072e,0x003d, 0x072e,0x00f9, + 0x072e,0x022d, 0x072e,0x0248, 0x072e,0x02e4, 0x0730,0x003d, 0x0730,0x0247, + 0x0730,0x02e3, 0x0730,0x0324, 0x0732,0x0324, 0x0739,0x032e, 0x073e,0x003d, + 0x0740,0x003d, 0x0744,0x027a, 0x0744,0x029b, 0x0748,0x0033, 0x0748,0x01ce, + 0x0748,0x01f1, 0x074c,0x0162, 0x074c,0x0183, 0x0750,0x0023, 0x0750,0x00e0, + 0x0750,0x00fb, 0x0755,0x0246, 0x075a,0x0095, 0x075a,0x0397, 0x075d,0x0004, + 0x076a,0x03b3, 0x076d,0x0002, 0x0772,0x02fb, 0x0772,0x0301, 0x0772,0x0315, + 0x0772,0x0397, 0x0776,0x008f, 0x077e,0x0027, 0x078a,0x00a1, 0x0792,0x009d, + 0x0792,0x00c3, 0x0792,0x02fb, 0x0792,0x0301, 0x0792,0x0315, 0x0792,0x03bd, + 0x0796,0x0027, 0x0796,0x024f, 0x079e,0x009d, 0x07a6,0x009d, 0x07a6,0x02fb, + 0x07a6,0x0301, 0x07a6,0x0315, 0x07a6,0x03bd, 0x07aa,0x0027, 0x07aa,0x024f, + 0x07ae,0x009d, 0x07b9,0x004e, 0x07b8,0x0087, 0x07b8,0x00ed, 0x07b8,0x0173, + 0x07b8,0x0197, 0x07b9,0x021a, 0x07b9,0x02b8, 0x07b9,0x0364, 0x07be,0x0029, + 0x07be,0x0030, 0x07c0,0x017b, 0x07c6,0x017b, 0x07c8,0x00f3, 0x07ce,0x00f3, + 0x07d0,0x008f, 0x07d6,0x008f, 0x07d9,0x01e8, 0x07dd,0x0292, 0x07e2,0x0053, + 0x07e6,0x008f, 0x07e6,0x00f3, 0x07e6,0x017b, 0x07e8,0x0029, 0x07e8,0x0030, + 0x07ec,0x0021, 0x07ec,0x02ad, 0x07f2,0x0181, 0x07f2,0x0315, 0x07f4,0x0021, + 0x07f8,0x020f, 0x07fd,0x002e, 0x0800,0x008f, 0x0805,0x0006, 0x0809,0x03c2, + 0x080d,0x0084, 0x0812,0x0009, 0x0811,0x0008, 0x0812,0x00f3, 0x0812,0x00f9, + 0x0812,0x017b, 0x0812,0x0181, 0x0814,0x0033, 0x0818,0x0023, 0x081c,0x0285, + 0x0826,0x03bd, 0x082c,0x008f, 0x082c,0x017b, 0x0832,0x0043, 0x0832,0x011b, + 0x0832,0x01b3, 0x0832,0x01c3, 0x0835,0x032a, 0x0838,0x0085, 0x0839,0x032a, + 0x083e,0x0049, 0x083d,0x0084, 0x083e,0x02fb, 0x083e,0x0301, 0x083e,0x0315, + 0x083e,0x0397, 0x0842,0x0009, 0x0841,0x0008, 0x0844,0x0009, 0x0846,0x008f, + 0x084a,0x0033, 0x084e,0x0285, 0x0851,0x009a, 0x0856,0x00a1, 0x0859,0x031c, + 0x085d,0x00b2, 0x0861,0x0012, 0x0861,0x02cc, 0x0865,0x0058, 0x0865,0x007e, + 0x0869,0x004a, 0x0871,0x0010, 0x0876,0x003d, 0x0879,0x032c, 0x087e,0x0089, + 0x0882,0x0229, 0x0882,0x022d, 0x0882,0x02c7, 0x0882,0x02cb, 0x0886,0x0021, + 0x0886,0x02ad, 0x0885,0x0356, 0x088a,0x0017, 0x088a,0x020f, 0x0889,0x0354, + 0x088d,0x009c, 0x0892,0x0089, 0x0895,0x0246, 0x089a,0x03bd, 0x089e,0x008f, + 0x089e,0x02f9, 0x089e,0x0313, 0x08a1,0x032a, 0x08a6,0x0053, 0x08a6,0x0095, + 0x08a6,0x0397, 0x08a8,0x017b, 0x08ad,0x031a, 0x08b2,0x017b, 0x08b4,0x00f3, + 0x08b5,0x02a0, 0x08b8,0x0089, 0x08c1,0x0024, 0x08c4,0x00f3, 0x08c9,0x007e, + 0x08cd,0x007c, 0x08cd,0x0222, 0x08cd,0x0294, 0x08d1,0x003a, 0x08d6,0x0009, + 0x08d9,0x003a, 0x08dc,0x001f, 0x08e0,0x008f, 0x08e0,0x017b, 0x08e4,0x0009, + 0x08e8,0x01ed, 0x08ed,0x031c, 0x08f2,0x003d, 0x08f6,0x008f, 0x08f6,0x017b, + 0x08fa,0x0009, 0x08fe,0x003d, 0x0902,0x01e9, 0x0904,0x01e9, 0x0904,0x0381, + 0x090a,0x03b1, 0x090d,0x031a, 0x0910,0x0299, 0x0914,0x034b, 0x0919,0x0008, + 0x091c,0x0033, 0x091c,0x003d, 0x0920,0x0027, 0x0924,0x0027, 0x0924,0x01fb, + 0x092a,0x01ce, 0x092a,0x01f1, 0x092d,0x031c, 0x0930,0x001f, 0x0936,0x00c5, + 0x0938,0x00c5, 0x0938,0x0381, 0x093c,0x001b, 0x0942,0x017d, 0x094a,0x0027, + 0x094e,0x0027, 0x094e,0x01fb, 0x0952,0x03b1, 0x095a,0x0029, 0x095a,0x0030, + 0x095d,0x0030, 0x0961,0x0030, 0x0966,0x02f9, 0x0966,0x0313, 0x0968,0x02eb, + 0x096d,0x0008, 0x0970,0x017b, 0x0974,0x0033, 0x0979,0x0150, 0x097d,0x009a, + 0x0982,0x0293, 0x0984,0x0293, 0x0984,0x0379, 0x098a,0x02eb, 0x098e,0x0009, + 0x0992,0x003d, 0x0996,0x003d, 0x0999,0x0062, 0x099e,0x003d, 0x09a0,0x0027, + 0x09a5,0x0144, 0x09a8,0x02b5, 0x09ae,0x008f, 0x09ae,0x009d, 0x09b2,0x004d, + 0x09b2,0x0053, 0x09b2,0x00c3, 0x09b2,0x013d, 0x09b2,0x01c5, 0x09b2,0x0271, + 0x09b4,0x0025, 0x09ba,0x0033, 0x09ba,0x0079, 0x09bc,0x0015, 0x09c2,0x013f, + 0x09c4,0x013f, 0x09c4,0x0379, 0x09ca,0x02b5, 0x09cd,0x0006, 0x09da,0x0009, + 0x09d9,0x0008, 0x09dc,0x000b, 0x09dc,0x004f, 0x09dd,0x0086, 0x09e0,0x0009, + 0x09e6,0x00a1, 0x09e8,0x0009, 0x09ed,0x0086, 0x09f2,0x001f, 0x09f2,0x002f, + 0x09f2,0x0049, 0x09f2,0x006f, 0x09f2,0x0085, 0x09f2,0x0091, 0x09f2,0x00a9, + 0x09f2,0x00d3, 0x09f2,0x00d7, 0x09f2,0x011d, 0x09f2,0x0121, 0x09f2,0x0235, + 0x09f2,0x0393, 0x09f6,0x0324, 0x09f8,0x0049, 0x09f8,0x00a9, 0x09f8,0x011d, + 0x09fe,0x001f, 0x09fe,0x0029, 0x09fe,0x0033, 0x09fe,0x003d, 0x09fe,0x0085, + 0x09fe,0x008f, 0x09fe,0x00d3, 0x0a00,0x003d, 0x0a06,0x012d, 0x0a0e,0x00b3, + 0x0a10,0x000b, 0x0a10,0x0387, 0x0a16,0x0059, 0x0a18,0x0009, 0x0a1e,0x0043, + 0x0a24,0x0085, 0x0a2a,0x0009, 0x0a2d,0x0008, 0x0a32,0x028a, 0x0a32,0x02a7, + 0x0a31,0x031c, 0x0a35,0x032e, 0x0a39,0x0006, 0x0a3a,0x0105, 0x0a3a,0x024f, + 0x0a3c,0x0299, 0x0a42,0x01ed, 0x0a46,0x0299, 0x0a48,0x01ed, 0x0a4c,0x0059, + 0x0a52,0x000b, 0x0a52,0x0387, 0x0a56,0x000b, 0x0a5e,0x0009, 0x0a60,0x003d, + 0x0a66,0x0105, 0x0a6a,0x0195, 0x0a6c,0x000b, 0x0a76,0x0053, 0x0a78,0x0009, + 0x0a7a,0x008f, 0x0a82,0x0299, 0x0a86,0x01ed, 0x0a8a,0x0027, 0x0a8e,0x004b, + 0x0a92,0x003d, 0x0a95,0x0322, 0x0a99,0x0038, 0x0a99,0x0090, 0x0a9c,0x0061, + 0x0a9c,0x00c7, 0x0a9c,0x012d, 0x0a9c,0x016f, 0x0a9c,0x017d, 0x0a9c,0x02c9, + 0x0a9c,0x0383, 0x0aa1,0x0010, 0x0aa4,0x00b3, 0x0aa8,0x002f, 0x0aac,0x0027, + 0x0ab0,0x004b, 0x0ab4,0x0043, 0x0ab9,0x0090, 0x0abd,0x0010, 0x0ac4,0x0019, + 0x0acc,0x00f5, 0x0acc,0x022b, 0x0acc,0x037b, 0x0ad2,0x008f, 0x0ad2,0x01f1, + 0x0ad6,0x0324, 0x0ad9,0x0330, 0x0ade,0x008f, 0x0ade,0x01f1, 0x0ae0,0x017b, + 0x0ae4,0x008f, 0x0ae9,0x004e, 0x0aee,0x0027, 0x0af2,0x028a, 0x0af2,0x02a7, + 0x0af1,0x031c, 0x0af6,0x0027, 0x0af9,0x031c, 0x0afe,0x00e9, 0x0afe,0x02bb, + 0x0b02,0x000b, 0x0b06,0x00f5, 0x0b06,0x022b, 0x0b06,0x037b, 0x0b0a,0x003d, + 0x0000,0x0000 +}; + + diff --git a/abc_with_bb_support/src/base/abc/abc.h b/abc_with_bb_support/src/base/abc/abc.h new file mode 100644 index 000000000..ecda34b11 --- /dev/null +++ b/abc_with_bb_support/src/base/abc/abc.h @@ -0,0 +1,942 @@ +/**CFile**************************************************************** + + FileName [abc.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abc.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __ABC_H__ +#define __ABC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +#include "cuddInt.h" +#include "hop.h" +#include "extra.h" +#include "vec.h" +#include "stmm.h" +#include "nm.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +// network types +typedef enum { + ABC_NTK_NONE = 0, // 0: unknown + ABC_NTK_NETLIST, // 1: network with PIs/POs, latches, nodes, and nets + ABC_NTK_LOGIC, // 2: network with PIs/POs, latches, and nodes + ABC_NTK_STRASH, // 3: structurally hashed AIG (two input AND gates with c-attributes on edges) + ABC_NTK_OTHER // 4: unused +} Abc_NtkType_t; + +// network functionality +typedef enum { + ABC_FUNC_NONE = 0, // 0: unknown + ABC_FUNC_SOP, // 1: sum-of-products + ABC_FUNC_BDD, // 2: binary decision diagrams + ABC_FUNC_AIG, // 3: and-inverter graphs + ABC_FUNC_MAP, // 4: standard cell library + ABC_FUNC_BLIFMV, // 5: BLIF-MV node functions + ABC_FUNC_BLACKBOX, // 6: black box about which nothing is known + ABC_FUNC_OTHER // 7: unused +} Abc_NtkFunc_t; + +// Supported type/functionality combinations: +/*------------------------------------------| +| | SOP | BDD | AIG | Map | +|-----------|-------|-------|-------|-------| +| Netlist | x | | x | x | +|-----------|-------|-------|-------|-------| +| Logic | x | x | x | x | +|-----------|-------|-------|-------|-------| +| Strash | | | x | | +--------------------------------------------|*/ + +// object types +typedef enum { + ABC_OBJ_NONE = 0, // 0: unknown + ABC_OBJ_CONST1, // 1: constant 1 node (AIG only) + ABC_OBJ_PIO, // 2: inout terminal + ABC_OBJ_PI, // 3: primary input terminal + ABC_OBJ_PO, // 4: primary output terminal + ABC_OBJ_BI, // 5: box input terminal + ABC_OBJ_BO, // 6: box output terminal + ABC_OBJ_ASSERT, // 7: assertion terminal + ABC_OBJ_NET, // 8: net + ABC_OBJ_NODE, // 9: node + ABC_OBJ_LATCH, // 10: latch + ABC_OBJ_WHITEBOX, // 11: box with known contents + ABC_OBJ_BLACKBOX, // 12: box with unknown contents + ABC_OBJ_NUMBER // 13: unused +} Abc_ObjType_t; + +// latch initial values +typedef enum { + ABC_INIT_NONE = 0, // 0: unknown + ABC_INIT_ZERO, // 1: zero + ABC_INIT_ONE, // 2: one + ABC_INIT_DC, // 3: don't-care + ABC_INIT_OTHER // 4: unused +} Abc_InitType_t; + +// latch type +typedef enum { + ABC_FALLING_EDGE = 0, + ABC_RISING_EDGE, + ABC_ACTIVE_HIGH, + ABC_ACTIVE_LOW, + ABC_ASYNC +} Abc_LatchType_t; + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +//typedef int bool; +#ifndef __cplusplus +#ifndef bool +#define bool int +#endif +#endif + +#ifndef SINT64 +#define SINT64 + +#ifdef _WIN32 +typedef signed __int64 sint64; // compatible with MS VS 6.0 +#else +typedef long long sint64; +#endif + +#endif + +typedef struct Abc_Lib_t_ Abc_Lib_t; +typedef struct Abc_Ntk_t_ Abc_Ntk_t; +typedef struct Abc_Obj_t_ Abc_Obj_t; +typedef struct Abc_Aig_t_ Abc_Aig_t; +typedef struct Abc_ManTime_t_ Abc_ManTime_t; +typedef struct Abc_ManCut_t_ Abc_ManCut_t; +typedef struct Abc_Time_t_ Abc_Time_t; + +struct Abc_Time_t_ +{ + float Rise; + float Fall; + float Worst; +}; + +struct Abc_Obj_t_ // 12 words +{ + // high-level information + Abc_Ntk_t * pNtk; // the host network + int Id; // the object ID + int TravId; // the traversal ID (if changed, update Abc_NtkIncrementTravId) + // internal information + unsigned Type : 4; // the object type + unsigned fMarkA : 1; // the multipurpose mark + unsigned fMarkB : 1; // the multipurpose mark + unsigned fMarkC : 1; // the multipurpose mark + unsigned fPhase : 1; // the flag to mark the phase of equivalent node + unsigned fExor : 1; // marks AIG node that is a root of EXOR + unsigned fPersist: 1; // marks the persistant AIG node + unsigned fCompl0 : 1; // complemented attribute of the first fanin in the AIG + unsigned fCompl1 : 1; // complemented attribute of the second fanin in the AIG + unsigned Level : 20; // the level of the node + // connectivity + Vec_Int_t vFanins; // the array of fanins + Vec_Int_t vFanouts; // the array of fanouts + // miscellaneous + void * pData; // the network specific data (SOP, BDD, gate, equiv class, etc) + Abc_Obj_t * pNext; // the next pointer in the hash table + Abc_Obj_t * pCopy; // the copy of this object + Hop_Obj_t * pEquiv; // pointer to the HAIG node +}; + +struct Abc_Ntk_t_ +{ + // general information + Abc_NtkType_t ntkType; // type of the network + Abc_NtkFunc_t ntkFunc; // functionality of the network + char * pName; // the network name + char * pSpec; // the name of the spec file if present + Nm_Man_t * pManName; // name manager (stores names of objects) + // components of the network + Vec_Ptr_t * vObjs; // the array of all objects (net, nodes, latches, etc) + Vec_Ptr_t * vPis; // the array of primary inputs + Vec_Ptr_t * vPos; // the array of primary outputs + Vec_Ptr_t * vCis; // the array of combinational inputs (PIs, latches) + Vec_Ptr_t * vCos; // the array of combinational outputs (POs, asserts, latches) + Vec_Ptr_t * vPios; // the array of PIOs + Vec_Ptr_t * vAsserts; // the array of assertions + Vec_Ptr_t * vBoxes; // the array of boxes + // the number of living objects + int nObjs; // the number of live objs + int nObjCounts[ABC_OBJ_NUMBER]; // the number of objects by type + // the backup network and the step number + Abc_Ntk_t * pNetBackup; // the pointer to the previous backup network + int iStep; // the generation number for the given network + // hierarchy + Abc_Lib_t * pDesign; + short fHieVisited; // flag to mark the visited network + short fHiePath; // flag to mark the network on the path + // miscellaneous data members + int nTravIds; // the unique traversal IDs of nodes + Extra_MmFixed_t * pMmObj; // memory manager for objects + Extra_MmStep_t * pMmStep; // memory manager for arrays + void * pManFunc; // functionality manager (AIG manager, BDD manager, or memory manager for SOPs) +// Abc_Lib_t * pVerLib; // for structural verilog designs + Abc_ManTime_t * pManTime; // the timing manager (for mapped networks) stores arrival/required times for all nodes + void * pManCut; // the cut manager (for AIGs) stores information about the cuts computed for the nodes + int LevelMax; // maximum number of levels + Vec_Int_t * vLevelsR; // level in the reverse topological order (for AIGs) + Vec_Ptr_t * vSupps; // CO support information + int * pModel; // counter-example (for miters) + Abc_Ntk_t * pExdc; // the EXDC network (if given) + void * pData; // misc + Abc_Ntk_t * pCopy; + Hop_Man_t * pHaig; // history AIG + // node attributes + Vec_Ptr_t * vAttrs; // managers of various node attributes (node functionality, global BDDs, etc) +}; + +struct Abc_Lib_t_ +{ + char * pName; // the name of the library + void * pManFunc; // functionality manager for the nodes + Vec_Ptr_t * vTops; // the array of top-level modules + Vec_Ptr_t * vModules; // the array of modules + st_table * tModules; // the table hashing module names into their networks + Abc_Lib_t * pLibrary; // the library used to map this design + void * pGenlib; // the genlib library used to map this design +}; + +struct Abc_LatchInfo_t_ +{ + Abc_InitType_t InitValue; + Abc_LatchType_t LatchType; + char *pClkName; +}; + +typedef struct Abc_LatchInfo_t_ Abc_LatchInfo_t; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// maximum/minimum operators +#define ABC_MIN(a,b) (((a) < (b))? (a) : (b)) +#define ABC_MAX(a,b) (((a) > (b))? (a) : (b)) +#define ABC_ABS(a) (((a) >= 0)? (a) :-(a)) +#define ABC_INFINITY (100000000) + +// transforming floats into ints and back +static inline int Abc_Float2Int( float Val ) { return *((int *)&Val); } +static inline float Abc_Int2Float( int Num ) { return *((float *)&Num); } +static inline int Abc_BitWordNum( int nBits ) { return (nBits>>5) + ((nBits&31) > 0); } +static inline int Abc_TruthWordNum( int nVars ) { return nVars <= 5 ? 1 : (1 << (nVars - 5)); } +static inline int Abc_InfoHasBit( unsigned * p, int i ) { return (p[(i)>>5] & (1<<((i) & 31))) > 0; } +static inline void Abc_InfoSetBit( unsigned * p, int i ) { p[(i)>>5] |= (1<<((i) & 31)); } +static inline void Abc_InfoXorBit( unsigned * p, int i ) { p[(i)>>5] ^= (1<<((i) & 31)); } +static inline unsigned Abc_InfoRandomWord() { return ((((unsigned)rand()) << 24) ^ (((unsigned)rand()) << 12) ^ ((unsigned)rand())); } // #define RAND_MAX 0x7fff +static inline void Abc_InfoRandom( unsigned * p, int nWords ) { int i; for ( i = nWords - 1; i >= 0; i-- ) p[i] = Abc_InfoRandomWord(); } +static inline void Abc_InfoClear( unsigned * p, int nWords ) { memset( p, 0, sizeof(unsigned) * nWords ); } +static inline void Abc_InfoFill( unsigned * p, int nWords ) { memset( p, 0xff, sizeof(unsigned) * nWords );} +static inline void Abc_InfoNot( unsigned * p, int nWords ) { int i; for ( i = nWords - 1; i >= 0; i-- ) p[i] = ~p[i]; } +static inline int Abc_InfoIsZero( unsigned * p, int nWords ) { int i; for ( i = nWords - 1; i >= 0; i-- ) if ( p[i] ) return 0; return 1; } +static inline int Abc_InfoIsOne( unsigned * p, int nWords ) { int i; for ( i = nWords - 1; i >= 0; i-- ) if ( ~p[i] ) return 0; return 1; } +static inline void Abc_InfoCopy( unsigned * p, unsigned * q, int nWords ) { int i; for ( i = nWords - 1; i >= 0; i-- ) p[i] = q[i]; } +static inline void Abc_InfoAnd( unsigned * p, unsigned * q, int nWords ) { int i; for ( i = nWords - 1; i >= 0; i-- ) p[i] &= q[i]; } +static inline void Abc_InfoOr( unsigned * p, unsigned * q, int nWords ) { int i; for ( i = nWords - 1; i >= 0; i-- ) p[i] |= q[i]; } +static inline void Abc_InfoXor( unsigned * p, unsigned * q, int nWords ) { int i; for ( i = nWords - 1; i >= 0; i-- ) p[i] ^= q[i]; } +static inline int Abc_InfoIsOrOne( unsigned * p, unsigned * q, int nWords ){ int i; for ( i = nWords - 1; i >= 0; i-- ) if ( ~(p[i] | q[i]) ) return 0; return 1; } +static inline int Abc_InfoIsOrOne3( unsigned * p, unsigned * q, unsigned * r, int nWords ){ int i; for ( i = nWords - 1; i >= 0; i-- ) if ( ~(p[i] | q[i] | r[i]) ) return 0; return 1; } + +// checking the network type +static inline bool Abc_NtkIsNetlist( Abc_Ntk_t * pNtk ) { return pNtk->ntkType == ABC_NTK_NETLIST; } +static inline bool Abc_NtkIsLogic( Abc_Ntk_t * pNtk ) { return pNtk->ntkType == ABC_NTK_LOGIC; } +static inline bool Abc_NtkIsStrash( Abc_Ntk_t * pNtk ) { return pNtk->ntkType == ABC_NTK_STRASH; } + +static inline bool Abc_NtkHasSop( Abc_Ntk_t * pNtk ) { return pNtk->ntkFunc == ABC_FUNC_SOP; } +static inline bool Abc_NtkHasBdd( Abc_Ntk_t * pNtk ) { return pNtk->ntkFunc == ABC_FUNC_BDD; } +static inline bool Abc_NtkHasAig( Abc_Ntk_t * pNtk ) { return pNtk->ntkFunc == ABC_FUNC_AIG; } +static inline bool Abc_NtkHasMapping( Abc_Ntk_t * pNtk ) { return pNtk->ntkFunc == ABC_FUNC_MAP; } +static inline bool Abc_NtkHasBlifMv( Abc_Ntk_t * pNtk ) { return pNtk->ntkFunc == ABC_FUNC_BLIFMV; } +static inline bool Abc_NtkHasBlackbox( Abc_Ntk_t * pNtk ) { return pNtk->ntkFunc == ABC_FUNC_BLACKBOX; } + +static inline bool Abc_NtkIsSopNetlist( Abc_Ntk_t * pNtk ) { return pNtk->ntkFunc == ABC_FUNC_SOP && pNtk->ntkType == ABC_NTK_NETLIST; } +static inline bool Abc_NtkIsAigNetlist( Abc_Ntk_t * pNtk ) { return pNtk->ntkFunc == ABC_FUNC_AIG && pNtk->ntkType == ABC_NTK_NETLIST; } +static inline bool Abc_NtkIsMappedNetlist( Abc_Ntk_t * pNtk ) { return pNtk->ntkFunc == ABC_FUNC_MAP && pNtk->ntkType == ABC_NTK_NETLIST; } +static inline bool Abc_NtkIsBlifMvNetlist( Abc_Ntk_t * pNtk ) { return pNtk->ntkFunc == ABC_FUNC_BLIFMV && pNtk->ntkType == ABC_NTK_NETLIST; } +static inline bool Abc_NtkIsSopLogic( Abc_Ntk_t * pNtk ) { return pNtk->ntkFunc == ABC_FUNC_SOP && pNtk->ntkType == ABC_NTK_LOGIC ; } +static inline bool Abc_NtkIsBddLogic( Abc_Ntk_t * pNtk ) { return pNtk->ntkFunc == ABC_FUNC_BDD && pNtk->ntkType == ABC_NTK_LOGIC ; } +static inline bool Abc_NtkIsAigLogic( Abc_Ntk_t * pNtk ) { return pNtk->ntkFunc == ABC_FUNC_AIG && pNtk->ntkType == ABC_NTK_LOGIC ; } +static inline bool Abc_NtkIsMappedLogic( Abc_Ntk_t * pNtk ) { return pNtk->ntkFunc == ABC_FUNC_MAP && pNtk->ntkType == ABC_NTK_LOGIC ; } + +// reading data members of the network +static inline char * Abc_NtkName( Abc_Ntk_t * pNtk ) { return pNtk->pName; } +static inline char * Abc_NtkSpec( Abc_Ntk_t * pNtk ) { return pNtk->pSpec; } +static inline int Abc_NtkTravId( Abc_Ntk_t * pNtk ) { return pNtk->nTravIds; } +static inline Abc_Ntk_t * Abc_NtkExdc( Abc_Ntk_t * pNtk ) { return pNtk->pExdc; } +static inline Abc_Ntk_t * Abc_NtkBackup( Abc_Ntk_t * pNtk ) { return pNtk->pNetBackup; } +static inline int Abc_NtkStep ( Abc_Ntk_t * pNtk ) { return pNtk->iStep; } + +// setting data members of the network +static inline void Abc_NtkSetName ( Abc_Ntk_t * pNtk, char * pName ) { pNtk->pName = pName; } +static inline void Abc_NtkSetSpec ( Abc_Ntk_t * pNtk, char * pName ) { pNtk->pSpec = pName; } +static inline void Abc_NtkSetBackup( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNetBackup ) { pNtk->pNetBackup = pNetBackup; } +static inline void Abc_NtkSetStep ( Abc_Ntk_t * pNtk, int iStep ) { pNtk->iStep = iStep; } + +// getting the number of objects +static inline int Abc_NtkObjNum( Abc_Ntk_t * pNtk ) { return pNtk->nObjs; } +static inline int Abc_NtkObjNumMax( Abc_Ntk_t * pNtk ) { return Vec_PtrSize(pNtk->vObjs); } +static inline int Abc_NtkPiNum( Abc_Ntk_t * pNtk ) { return Vec_PtrSize(pNtk->vPis); } +static inline int Abc_NtkPoNum( Abc_Ntk_t * pNtk ) { return Vec_PtrSize(pNtk->vPos); } +static inline int Abc_NtkCiNum( Abc_Ntk_t * pNtk ) { return Vec_PtrSize(pNtk->vCis); } +static inline int Abc_NtkCoNum( Abc_Ntk_t * pNtk ) { return Vec_PtrSize(pNtk->vCos); } +static inline int Abc_NtkAssertNum( Abc_Ntk_t * pNtk ) { return Vec_PtrSize(pNtk->vAsserts); } +static inline int Abc_NtkBoxNum( Abc_Ntk_t * pNtk ) { return Vec_PtrSize(pNtk->vBoxes); } +static inline int Abc_NtkBiNum( Abc_Ntk_t * pNtk ) { return pNtk->nObjCounts[ABC_OBJ_BI]; } +static inline int Abc_NtkBoNum( Abc_Ntk_t * pNtk ) { return pNtk->nObjCounts[ABC_OBJ_BO]; } +static inline int Abc_NtkNetNum( Abc_Ntk_t * pNtk ) { return pNtk->nObjCounts[ABC_OBJ_NET]; } +static inline int Abc_NtkNodeNum( Abc_Ntk_t * pNtk ) { return pNtk->nObjCounts[ABC_OBJ_NODE]; } +static inline int Abc_NtkLatchNum( Abc_Ntk_t * pNtk ) { return pNtk->nObjCounts[ABC_OBJ_LATCH]; } +static inline int Abc_NtkWhiteboxNum( Abc_Ntk_t * pNtk ) { return pNtk->nObjCounts[ABC_OBJ_WHITEBOX]; } +static inline int Abc_NtkBlackboxNum( Abc_Ntk_t * pNtk ) { return pNtk->nObjCounts[ABC_OBJ_BLACKBOX]; } +static inline bool Abc_NtkIsComb( Abc_Ntk_t * pNtk ) { return Abc_NtkLatchNum(pNtk) == 0; } +static inline bool Abc_NtkHasOnlyLatchBoxes(Abc_Ntk_t * pNtk ){ return Abc_NtkLatchNum(pNtk) == Abc_NtkBoxNum(pNtk); } + +// creating simple objects +extern Abc_Obj_t * Abc_NtkCreateObj( Abc_Ntk_t * pNtk, Abc_ObjType_t Type ); +static inline Abc_Obj_t * Abc_NtkCreatePi( Abc_Ntk_t * pNtk ) { return Abc_NtkCreateObj( pNtk, ABC_OBJ_PI ); } +static inline Abc_Obj_t * Abc_NtkCreatePo( Abc_Ntk_t * pNtk ) { return Abc_NtkCreateObj( pNtk, ABC_OBJ_PO ); } +static inline Abc_Obj_t * Abc_NtkCreateBi( Abc_Ntk_t * pNtk ) { return Abc_NtkCreateObj( pNtk, ABC_OBJ_BI ); } +static inline Abc_Obj_t * Abc_NtkCreateBo( Abc_Ntk_t * pNtk ) { return Abc_NtkCreateObj( pNtk, ABC_OBJ_BO ); } +static inline Abc_Obj_t * Abc_NtkCreateAssert( Abc_Ntk_t * pNtk ) { return Abc_NtkCreateObj( pNtk, ABC_OBJ_ASSERT ); } +static inline Abc_Obj_t * Abc_NtkCreateNet( Abc_Ntk_t * pNtk ) { return Abc_NtkCreateObj( pNtk, ABC_OBJ_NET ); } +static inline Abc_Obj_t * Abc_NtkCreateNode( Abc_Ntk_t * pNtk ) { return Abc_NtkCreateObj( pNtk, ABC_OBJ_NODE ); } +static inline Abc_Obj_t * Abc_NtkCreateLatch( Abc_Ntk_t * pNtk ) { return Abc_NtkCreateObj( pNtk, ABC_OBJ_LATCH ); } +static inline Abc_Obj_t * Abc_NtkCreateWhitebox( Abc_Ntk_t * pNtk ) { return Abc_NtkCreateObj( pNtk, ABC_OBJ_WHITEBOX ); } +static inline Abc_Obj_t * Abc_NtkCreateBlackbox( Abc_Ntk_t * pNtk ) { return Abc_NtkCreateObj( pNtk, ABC_OBJ_BLACKBOX ); } + +// reading objects +static inline Abc_Obj_t * Abc_NtkObj( Abc_Ntk_t * pNtk, int i ) { return (Abc_Obj_t *)Vec_PtrEntry( pNtk->vObjs, i ); } +static inline Abc_Obj_t * Abc_NtkPi( Abc_Ntk_t * pNtk, int i ) { return (Abc_Obj_t *)Vec_PtrEntry( pNtk->vPis, i ); } +static inline Abc_Obj_t * Abc_NtkPo( Abc_Ntk_t * pNtk, int i ) { return (Abc_Obj_t *)Vec_PtrEntry( pNtk->vPos, i ); } +static inline Abc_Obj_t * Abc_NtkCi( Abc_Ntk_t * pNtk, int i ) { return (Abc_Obj_t *)Vec_PtrEntry( pNtk->vCis, i ); } +static inline Abc_Obj_t * Abc_NtkCo( Abc_Ntk_t * pNtk, int i ) { return (Abc_Obj_t *)Vec_PtrEntry( pNtk->vCos, i ); } +static inline Abc_Obj_t * Abc_NtkAssert( Abc_Ntk_t * pNtk, int i ) { return (Abc_Obj_t *)Vec_PtrEntry( pNtk->vAsserts, i );} +static inline Abc_Obj_t * Abc_NtkBox( Abc_Ntk_t * pNtk, int i ) { return (Abc_Obj_t *)Vec_PtrEntry( pNtk->vBoxes, i ); } + +// working with complemented attributes of objects +static inline bool Abc_ObjIsComplement( Abc_Obj_t * p ) { return (bool)((unsigned long)p & (unsigned long)01); } +static inline Abc_Obj_t * Abc_ObjRegular( Abc_Obj_t * p ) { return (Abc_Obj_t *)((unsigned long)p & ~(unsigned long)01); } +static inline Abc_Obj_t * Abc_ObjNot( Abc_Obj_t * p ) { return (Abc_Obj_t *)((unsigned long)p ^ (unsigned long)01); } +static inline Abc_Obj_t * Abc_ObjNotCond( Abc_Obj_t * p, int c ) { return (Abc_Obj_t *)((unsigned long)p ^ (unsigned long)(c!=0)); } + +// reading data members of the object +static inline unsigned Abc_ObjType( Abc_Obj_t * pObj ) { return pObj->Type; } +static inline unsigned Abc_ObjId( Abc_Obj_t * pObj ) { return pObj->Id; } +static inline int Abc_ObjTravId( Abc_Obj_t * pObj ) { return pObj->TravId; } +static inline int Abc_ObjLevel( Abc_Obj_t * pObj ) { return pObj->Level; } +static inline Vec_Int_t * Abc_ObjFaninVec( Abc_Obj_t * pObj ) { return &pObj->vFanins; } +static inline Vec_Int_t * Abc_ObjFanoutVec( Abc_Obj_t * pObj ) { return &pObj->vFanouts; } +static inline Abc_Obj_t * Abc_ObjCopy( Abc_Obj_t * pObj ) { return pObj->pCopy; } +static inline Abc_Ntk_t * Abc_ObjNtk( Abc_Obj_t * pObj ) { return pObj->pNtk; } +static inline void * Abc_ObjData( Abc_Obj_t * pObj ) { return pObj->pData; } +static inline Hop_Obj_t * Abc_ObjEquiv( Abc_Obj_t * pObj ) { return pObj->pEquiv; } +static inline Abc_Obj_t * Abc_ObjCopyCond( Abc_Obj_t * pObj ) { return Abc_ObjRegular(pObj)->pCopy? Abc_ObjNotCond(Abc_ObjRegular(pObj)->pCopy, Abc_ObjIsComplement(pObj)) : NULL; } + +// setting data members of the network +static inline void Abc_ObjSetLevel( Abc_Obj_t * pObj, int Level ) { pObj->Level = Level; } +static inline void Abc_ObjSetCopy( Abc_Obj_t * pObj, Abc_Obj_t * pCopy ) { pObj->pCopy = pCopy; } +static inline void Abc_ObjSetData( Abc_Obj_t * pObj, void * pData ) { pObj->pData = pData; } + +// checking the object type +static inline bool Abc_ObjIsPio( Abc_Obj_t * pObj ) { return pObj->Type == ABC_OBJ_PIO; } +static inline bool Abc_ObjIsPi( Abc_Obj_t * pObj ) { return pObj->Type == ABC_OBJ_PI; } +static inline bool Abc_ObjIsPo( Abc_Obj_t * pObj ) { return pObj->Type == ABC_OBJ_PO; } +static inline bool Abc_ObjIsBi( Abc_Obj_t * pObj ) { return pObj->Type == ABC_OBJ_BI; } +static inline bool Abc_ObjIsBo( Abc_Obj_t * pObj ) { return pObj->Type == ABC_OBJ_BO; } +static inline bool Abc_ObjIsAssert( Abc_Obj_t * pObj ) { return pObj->Type == ABC_OBJ_ASSERT; } +static inline bool Abc_ObjIsCi( Abc_Obj_t * pObj ) { return pObj->Type == ABC_OBJ_PI || pObj->Type == ABC_OBJ_BO; } +static inline bool Abc_ObjIsCo( Abc_Obj_t * pObj ) { return pObj->Type == ABC_OBJ_PO || pObj->Type == ABC_OBJ_BI || pObj->Type == ABC_OBJ_ASSERT; } +static inline bool Abc_ObjIsTerm( Abc_Obj_t * pObj ) { return Abc_ObjIsCi(pObj) || Abc_ObjIsCo(pObj); } +static inline bool Abc_ObjIsNet( Abc_Obj_t * pObj ) { return pObj->Type == ABC_OBJ_NET; } +static inline bool Abc_ObjIsNode( Abc_Obj_t * pObj ) { return pObj->Type == ABC_OBJ_NODE; } +static inline bool Abc_ObjIsLatch( Abc_Obj_t * pObj ) { return pObj->Type == ABC_OBJ_LATCH; } +static inline bool Abc_ObjIsBox( Abc_Obj_t * pObj ) { return pObj->Type == ABC_OBJ_LATCH || pObj->Type == ABC_OBJ_WHITEBOX || pObj->Type == ABC_OBJ_BLACKBOX; } +static inline bool Abc_ObjIsWhitebox( Abc_Obj_t * pObj ) { return pObj->Type == ABC_OBJ_WHITEBOX;} +static inline bool Abc_ObjIsBlackbox( Abc_Obj_t * pObj ) { return pObj->Type == ABC_OBJ_BLACKBOX;} +static inline void Abc_ObjBlackboxToWhitebox( Abc_Obj_t * pObj ) { assert( Abc_ObjIsBlackbox(pObj) ); pObj->Type = ABC_OBJ_WHITEBOX; pObj->pNtk->nObjCounts[ABC_OBJ_BLACKBOX]--; pObj->pNtk->nObjCounts[ABC_OBJ_WHITEBOX]++; } + +// working with fanin/fanout edges +static inline int Abc_ObjFaninNum( Abc_Obj_t * pObj ) { return pObj->vFanins.nSize; } +static inline int Abc_ObjFanoutNum( Abc_Obj_t * pObj ) { return pObj->vFanouts.nSize; } +static inline int Abc_ObjFaninId( Abc_Obj_t * pObj, int i) { return pObj->vFanins.pArray[i]; } +static inline int Abc_ObjFaninId0( Abc_Obj_t * pObj ) { return pObj->vFanins.pArray[0]; } +static inline int Abc_ObjFaninId1( Abc_Obj_t * pObj ) { return pObj->vFanins.pArray[1]; } +static inline int Abc_ObjFanoutEdgeNum( Abc_Obj_t * pObj, Abc_Obj_t * pFanout ) { assert( Abc_NtkHasAig(pObj->pNtk) ); if ( Abc_ObjFaninId0(pFanout) == pObj->Id ) return 0; if ( Abc_ObjFaninId1(pFanout) == pObj->Id ) return 1; assert( 0 ); return -1; } +static inline Abc_Obj_t * Abc_ObjFanout( Abc_Obj_t * pObj, int i ) { return (Abc_Obj_t *)pObj->pNtk->vObjs->pArray[ pObj->vFanouts.pArray[i] ]; } +static inline Abc_Obj_t * Abc_ObjFanout0( Abc_Obj_t * pObj ) { return (Abc_Obj_t *)pObj->pNtk->vObjs->pArray[ pObj->vFanouts.pArray[0] ]; } +static inline Abc_Obj_t * Abc_ObjFanin( Abc_Obj_t * pObj, int i ) { return (Abc_Obj_t *)pObj->pNtk->vObjs->pArray[ pObj->vFanins.pArray[i] ]; } +static inline Abc_Obj_t * Abc_ObjFanin0( Abc_Obj_t * pObj ) { return (Abc_Obj_t *)pObj->pNtk->vObjs->pArray[ pObj->vFanins.pArray[0] ]; } +static inline Abc_Obj_t * Abc_ObjFanin1( Abc_Obj_t * pObj ) { return (Abc_Obj_t *)pObj->pNtk->vObjs->pArray[ pObj->vFanins.pArray[1] ]; } +static inline Abc_Obj_t * Abc_ObjFanin0Ntk( Abc_Obj_t * pObj ) { return (Abc_NtkIsNetlist(pObj->pNtk)? Abc_ObjFanin0(pObj) : pObj); } +static inline Abc_Obj_t * Abc_ObjFanout0Ntk( Abc_Obj_t * pObj ) { return (Abc_NtkIsNetlist(pObj->pNtk)? Abc_ObjFanout0(pObj) : pObj); } +static inline bool Abc_ObjFaninC0( Abc_Obj_t * pObj ) { return pObj->fCompl0; } +static inline bool Abc_ObjFaninC1( Abc_Obj_t * pObj ) { return pObj->fCompl1; } +static inline bool Abc_ObjFaninC( Abc_Obj_t * pObj, int i ) { assert( i >=0 && i < 2 ); return i? pObj->fCompl1 : pObj->fCompl0; } +static inline void Abc_ObjSetFaninC( Abc_Obj_t * pObj, int i ){ assert( i >=0 && i < 2 ); if ( i ) pObj->fCompl1 = 1; else pObj->fCompl0 = 1; } +static inline void Abc_ObjXorFaninC( Abc_Obj_t * pObj, int i ){ assert( i >=0 && i < 2 ); if ( i ) pObj->fCompl1^= 1; else pObj->fCompl0^= 1; } +static inline Abc_Obj_t * Abc_ObjChild( Abc_Obj_t * pObj, int i ) { return Abc_ObjNotCond( Abc_ObjFanin(pObj,i), Abc_ObjFaninC(pObj,i) );} +static inline Abc_Obj_t * Abc_ObjChild0( Abc_Obj_t * pObj ) { return Abc_ObjNotCond( Abc_ObjFanin0(pObj), Abc_ObjFaninC0(pObj) ); } +static inline Abc_Obj_t * Abc_ObjChild1( Abc_Obj_t * pObj ) { return Abc_ObjNotCond( Abc_ObjFanin1(pObj), Abc_ObjFaninC1(pObj) ); } +static inline Abc_Obj_t * Abc_ObjChildCopy( Abc_Obj_t * pObj, int i ){ return Abc_ObjNotCond( Abc_ObjFanin(pObj,i)->pCopy, Abc_ObjFaninC(pObj,i) ); } +static inline Abc_Obj_t * Abc_ObjChild0Copy( Abc_Obj_t * pObj ) { return Abc_ObjNotCond( Abc_ObjFanin0(pObj)->pCopy, Abc_ObjFaninC0(pObj) ); } +static inline Abc_Obj_t * Abc_ObjChild1Copy( Abc_Obj_t * pObj ) { return Abc_ObjNotCond( Abc_ObjFanin1(pObj)->pCopy, Abc_ObjFaninC1(pObj) ); } +static inline Abc_Obj_t * Abc_ObjChild0Data( Abc_Obj_t * pObj ) { return Abc_ObjNotCond( (Abc_Obj_t *)Abc_ObjFanin0(pObj)->pData, Abc_ObjFaninC0(pObj) ); } +static inline Abc_Obj_t * Abc_ObjChild1Data( Abc_Obj_t * pObj ) { return Abc_ObjNotCond( (Abc_Obj_t *)Abc_ObjFanin1(pObj)->pData, Abc_ObjFaninC1(pObj) ); } +static inline Hop_Obj_t * Abc_ObjChild0Equiv( Abc_Obj_t * pObj ) { return Hop_NotCond( Abc_ObjFanin0(pObj)->pEquiv, Abc_ObjFaninC0(pObj) ); } +static inline Hop_Obj_t * Abc_ObjChild1Equiv( Abc_Obj_t * pObj ) { return Hop_NotCond( Abc_ObjFanin1(pObj)->pEquiv, Abc_ObjFaninC1(pObj) ); } + +// checking the AIG node types +static inline bool Abc_AigNodeIsConst( Abc_Obj_t * pNode ) { assert(Abc_NtkIsStrash(Abc_ObjRegular(pNode)->pNtk)); return Abc_ObjRegular(pNode)->Type == ABC_OBJ_CONST1; } +static inline bool Abc_AigNodeIsAnd( Abc_Obj_t * pNode ) { assert(!Abc_ObjIsComplement(pNode)); assert(Abc_NtkIsStrash(pNode->pNtk)); return Abc_ObjFaninNum(pNode) == 2; } +static inline bool Abc_AigNodeIsChoice( Abc_Obj_t * pNode ) { assert(!Abc_ObjIsComplement(pNode)); assert(Abc_NtkIsStrash(pNode->pNtk)); return pNode->pData != NULL && Abc_ObjFanoutNum(pNode) > 0; } + +// handling persistent nodes +static inline int Abc_NodeIsPersistant( Abc_Obj_t * pNode ) { assert( Abc_AigNodeIsAnd(pNode) ); return pNode->fPersist; } +static inline void Abc_NodeSetPersistant( Abc_Obj_t * pNode ) { assert( Abc_AigNodeIsAnd(pNode) ); pNode->fPersist = 1; } +static inline void Abc_NodeClearPersistant( Abc_Obj_t * pNode ) { assert( Abc_AigNodeIsAnd(pNode) ); pNode->fPersist = 0; } + +// working with the traversal ID +static inline void Abc_NodeSetTravId( Abc_Obj_t * pNode, int TravId ) { pNode->TravId = TravId; } +static inline void Abc_NodeSetTravIdCurrent( Abc_Obj_t * pNode ) { pNode->TravId = pNode->pNtk->nTravIds; } +static inline void Abc_NodeSetTravIdPrevious( Abc_Obj_t * pNode ) { pNode->TravId = pNode->pNtk->nTravIds - 1; } +static inline bool Abc_NodeIsTravIdCurrent( Abc_Obj_t * pNode ) { return (bool)(pNode->TravId == pNode->pNtk->nTravIds); } +static inline bool Abc_NodeIsTravIdPrevious( Abc_Obj_t * pNode ) { return (bool)(pNode->TravId == pNode->pNtk->nTravIds - 1); } + +// checking initial state of the latches +static inline void Abc_LatchSetInitNone( Abc_Obj_t * pLatch ) { assert(Abc_ObjIsLatch(pLatch)); ((Abc_LatchInfo_t *)(pLatch->pData))->InitValue = ABC_INIT_NONE; } +static inline void Abc_LatchSetInit0( Abc_Obj_t * pLatch ) { assert(Abc_ObjIsLatch(pLatch)); ((Abc_LatchInfo_t *)(pLatch->pData))->InitValue = ABC_INIT_ZERO; } +static inline void Abc_LatchSetInit1( Abc_Obj_t * pLatch ) { assert(Abc_ObjIsLatch(pLatch)); ((Abc_LatchInfo_t *)(pLatch->pData))->InitValue = ABC_INIT_ONE; } +static inline void Abc_LatchSetInitDc( Abc_Obj_t * pLatch ) { assert(Abc_ObjIsLatch(pLatch)); ((Abc_LatchInfo_t *)(pLatch->pData))->InitValue = ABC_INIT_DC; } +static inline int Abc_LatchIsInitNone( Abc_Obj_t * pLatch ) { assert(Abc_ObjIsLatch(pLatch)); return ((Abc_LatchInfo_t *)(pLatch->pData))->InitValue == ABC_INIT_NONE; } +static inline int Abc_LatchIsInit0( Abc_Obj_t * pLatch ) { assert(Abc_ObjIsLatch(pLatch)); return ((Abc_LatchInfo_t *)(pLatch->pData))->InitValue == ABC_INIT_ZERO; } +static inline int Abc_LatchIsInit1( Abc_Obj_t * pLatch ) { assert(Abc_ObjIsLatch(pLatch)); return ((Abc_LatchInfo_t *)(pLatch->pData))->InitValue == ABC_INIT_ONE; } +static inline int Abc_LatchIsInitDc( Abc_Obj_t * pLatch ) { assert(Abc_ObjIsLatch(pLatch)); return ((Abc_LatchInfo_t *)(pLatch->pData))->InitValue == ABC_INIT_DC; } +static inline int Abc_LatchInit( Abc_Obj_t * pLatch ) { assert(Abc_ObjIsLatch(pLatch)); return (int)((Abc_LatchInfo_t *)(pLatch->pData))->InitValue; } +// global BDDs of the nodes +static inline void * Abc_NtkGlobalBdd( Abc_Ntk_t * pNtk ) { return (void *)Vec_PtrEntry(pNtk->vAttrs, VEC_ATTR_GLOBAL_BDD); } +static inline DdManager * Abc_NtkGlobalBddMan( Abc_Ntk_t * pNtk ) { return (DdManager *)Vec_AttMan( (Vec_Att_t *)Abc_NtkGlobalBdd(pNtk) ); } +static inline DdNode ** Abc_NtkGlobalBddArray( Abc_Ntk_t * pNtk ) { return (DdNode **)Vec_AttArray( (Vec_Att_t *)Abc_NtkGlobalBdd(pNtk) ); } +static inline DdNode * Abc_ObjGlobalBdd( Abc_Obj_t * pObj ) { return (DdNode *)Vec_AttEntry( (Vec_Att_t *)Abc_NtkGlobalBdd(pObj->pNtk), pObj->Id ); } +static inline void Abc_ObjSetGlobalBdd( Abc_Obj_t * pObj, DdNode * bF ) { Vec_AttWriteEntry( (Vec_Att_t *)Abc_NtkGlobalBdd(pObj->pNtk), pObj->Id, bF ); } + +// MV variables of the nodes +static inline void * Abc_NtkMvVar( Abc_Ntk_t * pNtk ) { return Vec_PtrEntry(pNtk->vAttrs, VEC_ATTR_MVVAR); } +static inline void * Abc_NtkMvVarMan( Abc_Ntk_t * pNtk ) { return Abc_NtkMvVar(pNtk)? Vec_AttMan( (Vec_Att_t *)Abc_NtkMvVar(pNtk) ) : NULL; } +static inline void * Abc_ObjMvVar( Abc_Obj_t * pObj ) { return Abc_NtkMvVar(pObj->pNtk)? Vec_AttEntry( (Vec_Att_t *)Abc_NtkMvVar(pObj->pNtk), pObj->Id ) : NULL; } +static inline int Abc_ObjMvVarNum( Abc_Obj_t * pObj ) { return (Abc_NtkMvVar(pObj->pNtk) && Abc_ObjMvVar(pObj))? *((int*)Abc_ObjMvVar(pObj)) : 2; } +static inline void Abc_ObjSetMvVar( Abc_Obj_t * pObj, void * pV) { Vec_AttWriteEntry( (Vec_Att_t *)Abc_NtkMvVar(pObj->pNtk), pObj->Id, pV ); } + +// outputs the runtime in seconds +#define PRT(a,t) printf("%s = ", (a)); printf("%6.2f sec\n", (float)(t)/(float)(CLOCKS_PER_SEC)) + +//////////////////////////////////////////////////////////////////////// +/// ITERATORS /// +//////////////////////////////////////////////////////////////////////// + +// objects of the network +#define Abc_NtkForEachObj( pNtk, pObj, i ) \ + for ( i = 0; (i < Vec_PtrSize((pNtk)->vObjs)) && (((pObj) = Abc_NtkObj(pNtk, i)), 1); i++ ) \ + if ( (pObj) == NULL ) {} else +#define Abc_NtkForEachNet( pNtk, pNet, i ) \ + for ( i = 0; (i < Vec_PtrSize((pNtk)->vObjs)) && (((pNet) = Abc_NtkObj(pNtk, i)), 1); i++ ) \ + if ( (pNet) == NULL || !Abc_ObjIsNet(pNet) ) {} else +#define Abc_NtkForEachNode( pNtk, pNode, i ) \ + for ( i = 0; (i < Vec_PtrSize((pNtk)->vObjs)) && (((pNode) = Abc_NtkObj(pNtk, i)), 1); i++ ) \ + if ( (pNode) == NULL || !Abc_ObjIsNode(pNode) ) {} else +#define Abc_NtkForEachGate( pNtk, pNode, i ) \ + for ( i = 0; (i < Vec_PtrSize((pNtk)->vObjs)) && (((pNode) = Abc_NtkObj(pNtk, i)), 1); i++ ) \ + if ( (pNode) == NULL || !Abc_ObjIsGate(pNode) ) {} else +#define Abc_AigForEachAnd( pNtk, pNode, i ) \ + for ( i = 0; (i < Vec_PtrSize((pNtk)->vObjs)) && (((pNode) = Abc_NtkObj(pNtk, i)), 1); i++ ) \ + if ( (pNode) == NULL || !Abc_AigNodeIsAnd(pNode) ) {} else +// various boxes +#define Abc_NtkForEachBox( pNtk, pObj, i ) \ + for ( i = 0; (i < Vec_PtrSize((pNtk)->vBoxes)) && (((pObj) = Abc_NtkBox(pNtk, i)), 1); i++ ) +#define Abc_NtkForEachLatch( pNtk, pObj, i ) \ + for ( i = 0; (i < Vec_PtrSize((pNtk)->vBoxes)) && (((pObj) = Abc_NtkBox(pNtk, i)), 1); i++ ) \ + if ( !Abc_ObjIsLatch(pObj) ) {} else +#define Abc_NtkForEachLatchInput( pNtk, pObj, i ) \ + for ( i = 0; (i < Vec_PtrSize((pNtk)->vBoxes)); i++ ) \ + if ( !(Abc_ObjIsLatch(Abc_NtkBox(pNtk, i)) && (((pObj) = Abc_ObjFanin0(Abc_NtkBox(pNtk, i))), 1)) ) {} else +#define Abc_NtkForEachLatchOutput( pNtk, pObj, i ) \ + for ( i = 0; (i < Vec_PtrSize((pNtk)->vBoxes)); i++ ) \ + if ( !(Abc_ObjIsLatch(Abc_NtkBox(pNtk, i)) && (((pObj) = Abc_ObjFanout0(Abc_NtkBox(pNtk, i))), 1)) ) {} else +#define Abc_NtkForEachWhitebox( pNtk, pObj, i ) \ + for ( i = 0; (i < Vec_PtrSize((pNtk)->vBoxes)) && (((pObj) = Abc_NtkBox(pNtk, i)), 1); i++ ) \ + if ( !Abc_ObjIsWhitebox(pObj) ) {} else +#define Abc_NtkForEachBlackbox( pNtk, pObj, i ) \ + for ( i = 0; (i < Vec_PtrSize((pNtk)->vBoxes)) && (((pObj) = Abc_NtkBox(pNtk, i)), 1); i++ ) \ + if ( !Abc_ObjIsBlackbox(pObj) ) {} else +// inputs and outputs +#define Abc_NtkForEachPi( pNtk, pPi, i ) \ + for ( i = 0; (i < Abc_NtkPiNum(pNtk)) && (((pPi) = Abc_NtkPi(pNtk, i)), 1); i++ ) +#define Abc_NtkForEachCi( pNtk, pCi, i ) \ + for ( i = 0; (i < Abc_NtkCiNum(pNtk)) && (((pCi) = Abc_NtkCi(pNtk, i)), 1); i++ ) +#define Abc_NtkForEachPo( pNtk, pPo, i ) \ + for ( i = 0; (i < Abc_NtkPoNum(pNtk)) && (((pPo) = Abc_NtkPo(pNtk, i)), 1); i++ ) +#define Abc_NtkForEachCo( pNtk, pCo, i ) \ + for ( i = 0; (i < Abc_NtkCoNum(pNtk)) && (((pCo) = Abc_NtkCo(pNtk, i)), 1); i++ ) +#define Abc_NtkForEachAssert( pNtk, pObj, i ) \ + for ( i = 0; (i < Vec_PtrSize((pNtk)->vAsserts)) && (((pObj) = Abc_NtkAssert(pNtk, i)), 1); i++ ) +// fanin and fanouts +#define Abc_ObjForEachFanin( pObj, pFanin, i ) \ + for ( i = 0; (i < Abc_ObjFaninNum(pObj)) && (((pFanin) = Abc_ObjFanin(pObj, i)), 1); i++ ) +#define Abc_ObjForEachFanout( pObj, pFanout, i ) \ + for ( i = 0; (i < Abc_ObjFanoutNum(pObj)) && (((pFanout) = Abc_ObjFanout(pObj, i)), 1); i++ ) +// cubes and literals +#define Abc_SopForEachCube( pSop, nFanins, pCube ) \ + for ( pCube = (pSop); *pCube; pCube += (nFanins) + 3 ) +#define Abc_CubeForEachVar( pCube, Value, i ) \ + for ( i = 0; (pCube[i] != ' ') && (Value = pCube[i]); i++ ) + + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== abcAig.c ==========================================================*/ +extern Abc_Aig_t * Abc_AigAlloc( Abc_Ntk_t * pNtk ); +extern void Abc_AigFree( Abc_Aig_t * pMan ); +extern int Abc_AigCleanup( Abc_Aig_t * pMan ); +extern bool Abc_AigCheck( Abc_Aig_t * pMan ); +extern int Abc_AigLevel( Abc_Ntk_t * pNtk ); +extern Abc_Obj_t * Abc_AigConst1( Abc_Ntk_t * pNtk ); +extern Abc_Obj_t * Abc_AigAnd( Abc_Aig_t * pMan, Abc_Obj_t * p0, Abc_Obj_t * p1 ); +extern Abc_Obj_t * Abc_AigAndLookup( Abc_Aig_t * pMan, Abc_Obj_t * p0, Abc_Obj_t * p1 ); +extern Abc_Obj_t * Abc_AigXorLookup( Abc_Aig_t * pMan, Abc_Obj_t * p0, Abc_Obj_t * p1, int * pType ); +extern Abc_Obj_t * Abc_AigMuxLookup( Abc_Aig_t * pMan, Abc_Obj_t * pC, Abc_Obj_t * pT, Abc_Obj_t * pE, int * pType ); +extern Abc_Obj_t * Abc_AigOr( Abc_Aig_t * pMan, Abc_Obj_t * p0, Abc_Obj_t * p1 ); +extern Abc_Obj_t * Abc_AigXor( Abc_Aig_t * pMan, Abc_Obj_t * p0, Abc_Obj_t * p1 ); +extern Abc_Obj_t * Abc_AigMiter( Abc_Aig_t * pMan, Vec_Ptr_t * vPairs ); +extern void Abc_AigReplace( Abc_Aig_t * pMan, Abc_Obj_t * pOld, Abc_Obj_t * pNew, bool fUpdateLevel ); +extern void Abc_AigDeleteNode( Abc_Aig_t * pMan, Abc_Obj_t * pOld ); +extern void Abc_AigRehash( Abc_Aig_t * pMan ); +extern bool Abc_AigNodeHasComplFanoutEdge( Abc_Obj_t * pNode ); +extern bool Abc_AigNodeHasComplFanoutEdgeTrav( Abc_Obj_t * pNode ); +extern void Abc_AigPrintNode( Abc_Obj_t * pNode ); +extern bool Abc_AigNodeIsAcyclic( Abc_Obj_t * pNode, Abc_Obj_t * pRoot ); +extern void Abc_AigCheckFaninOrder( Abc_Aig_t * pMan ); +extern void Abc_AigSetNodePhases( Abc_Ntk_t * pNtk ); +extern Vec_Ptr_t * Abc_AigUpdateStart( Abc_Aig_t * pMan, Vec_Ptr_t ** pvUpdatedNets ); +extern void Abc_AigUpdateStop( Abc_Aig_t * pMan ); +extern void Abc_AigUpdateReset( Abc_Aig_t * pMan ); +/*=== abcAttach.c ==========================================================*/ +extern int Abc_NtkAttach( Abc_Ntk_t * pNtk ); +/*=== abcBlifMv.c ==========================================================*/ +extern void Abc_NtkStartMvVars( Abc_Ntk_t * pNtk ); +extern void Abc_NtkFreeMvVars( Abc_Ntk_t * pNtk ); +extern void Abc_NtkSetMvVarValues( Abc_Obj_t * pObj, int nValues ); +extern Abc_Ntk_t * Abc_NtkStrashBlifMv( Abc_Ntk_t * pNtk ); +extern Abc_Ntk_t * Abc_NtkInsertBlifMv( Abc_Ntk_t * pNtkBase, Abc_Ntk_t * pNtkLogic ); +extern int Abc_NtkConvertToBlifMv( Abc_Ntk_t * pNtk ); +extern char * Abc_NodeConvertSopToMvSop( int nVars, Vec_Int_t * vSop0, Vec_Int_t * vSop1 ); +extern int Abc_NodeEvalMvCost( int nVars, Vec_Int_t * vSop0, Vec_Int_t * vSop1 ); +/*=== abcBalance.c ==========================================================*/ +extern Abc_Ntk_t * Abc_NtkBalance( Abc_Ntk_t * pNtk, bool fDuplicate, bool fSelective, bool fUpdateLevel ); +/*=== abcCheck.c ==========================================================*/ +extern bool Abc_NtkCheck( Abc_Ntk_t * pNtk ); +extern bool Abc_NtkCheckRead( Abc_Ntk_t * pNtk ); +extern bool Abc_NtkDoCheck( Abc_Ntk_t * pNtk ); +extern bool Abc_NtkCheckObj( Abc_Ntk_t * pNtk, Abc_Obj_t * pObj ); +extern bool Abc_NtkCompareSignals( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int fOnlyPis, int fComb ); +extern int Abc_NtkIsAcyclicHierarchy( Abc_Ntk_t * pNtk ); +extern int Abc_NtkCheckUniqueCiNames( Abc_Ntk_t * pNtk ); +extern int Abc_NtkCheckUniqueCoNames( Abc_Ntk_t * pNtk ); +extern int Abc_NtkCheckUniqueCioNames( Abc_Ntk_t * pNtk ); +/*=== abcCollapse.c ==========================================================*/ +extern Abc_Ntk_t * Abc_NtkCollapse( Abc_Ntk_t * pNtk, int fBddSizeMax, int fDualRail, int fReorder, int fVerbose ); +/*=== abcCut.c ==========================================================*/ +extern void * Abc_NodeGetCutsRecursive( void * p, Abc_Obj_t * pObj, int fDag, int fTree ); +extern void * Abc_NodeGetCuts( void * p, Abc_Obj_t * pObj, int fDag, int fTree ); +extern void Abc_NodeGetCutsSeq( void * p, Abc_Obj_t * pObj, int fFirst ); +extern void * Abc_NodeReadCuts( void * p, Abc_Obj_t * pObj ); +extern void Abc_NodeFreeCuts( void * p, Abc_Obj_t * pObj ); +/*=== abcDfs.c ==========================================================*/ +extern Vec_Ptr_t * Abc_NtkDfs( Abc_Ntk_t * pNtk, int fCollectAll ); +extern Vec_Ptr_t * Abc_NtkDfsNodes( Abc_Ntk_t * pNtk, Abc_Obj_t ** ppNodes, int nNodes ); +extern Vec_Ptr_t * Abc_NtkDfsReverse( Abc_Ntk_t * pNtk ); +extern Vec_Ptr_t * Abc_NtkDfsReverseNodes( Abc_Ntk_t * pNtk, Abc_Obj_t ** ppNodes, int nNodes ); +extern Vec_Ptr_t * Abc_NtkDfsReverseNodesContained( Abc_Ntk_t * pNtk, Abc_Obj_t ** ppNodes, int nNodes ); +extern Vec_Ptr_t * Abc_NtkDfsSeq( Abc_Ntk_t * pNtk ); +extern Vec_Ptr_t * Abc_NtkDfsSeqReverse( Abc_Ntk_t * pNtk ); +extern Vec_Ptr_t * Abc_NtkDfsIter( Abc_Ntk_t * pNtk, int fCollectAll ); +extern Vec_Ptr_t * Abc_NtkDfsHie( Abc_Ntk_t * pNtk, int fCollectAll ); +extern bool Abc_NtkIsDfsOrdered( Abc_Ntk_t * pNtk ); +extern Vec_Ptr_t * Abc_NtkSupport( Abc_Ntk_t * pNtk ); +extern Vec_Ptr_t * Abc_NtkNodeSupport( Abc_Ntk_t * pNtk, Abc_Obj_t ** ppNodes, int nNodes ); +extern Vec_Ptr_t * Abc_AigDfs( Abc_Ntk_t * pNtk, int fCollectAll, int fCollectCos ); +extern Vec_Vec_t * Abc_DfsLevelized( Abc_Obj_t * pNode, bool fTfi ); +extern int Abc_NtkLevel( Abc_Ntk_t * pNtk ); +extern int Abc_NtkLevelReverse( Abc_Ntk_t * pNtk ); +extern bool Abc_NtkIsAcyclic( Abc_Ntk_t * pNtk ); +extern Vec_Ptr_t * Abc_AigGetLevelizedOrder( Abc_Ntk_t * pNtk, int fCollectCis ); +/*=== abcFanio.c ==========================================================*/ +extern void Abc_ObjAddFanin( Abc_Obj_t * pObj, Abc_Obj_t * pFanin ); +extern void Abc_ObjDeleteFanin( Abc_Obj_t * pObj, Abc_Obj_t * pFanin ); +extern void Abc_ObjRemoveFanins( Abc_Obj_t * pObj ); +extern void Abc_ObjPatchFanin( Abc_Obj_t * pObj, Abc_Obj_t * pFaninOld, Abc_Obj_t * pFaninNew ); +extern Abc_Obj_t * Abc_ObjInsertBetween( Abc_Obj_t * pNodeIn, Abc_Obj_t * pNodeOut, Abc_ObjType_t Type ); +extern void Abc_ObjTransferFanout( Abc_Obj_t * pObjOld, Abc_Obj_t * pObjNew ); +extern void Abc_ObjReplace( Abc_Obj_t * pObjOld, Abc_Obj_t * pObjNew ); +extern int Abc_ObjFanoutFaninNum( Abc_Obj_t * pFanout, Abc_Obj_t * pFanin ); +/*=== abcFraig.c ==========================================================*/ +extern Abc_Ntk_t * Abc_NtkFraig( Abc_Ntk_t * pNtk, void * pParams, int fAllNodes, int fExdc ); +extern void * Abc_NtkToFraig( Abc_Ntk_t * pNtk, void * pParams, int fAllNodes, int fExdc ); +extern Abc_Ntk_t * Abc_NtkFraigTrust( Abc_Ntk_t * pNtk ); +extern int Abc_NtkFraigStore( Abc_Ntk_t * pNtk ); +extern Abc_Ntk_t * Abc_NtkFraigRestore(); +extern void Abc_NtkFraigStoreClean(); +/*=== abcFunc.c ==========================================================*/ +extern int Abc_NtkSopToBdd( Abc_Ntk_t * pNtk ); +extern DdNode * Abc_ConvertSopToBdd( DdManager * dd, char * pSop ); +extern char * Abc_ConvertBddToSop( Extra_MmFlex_t * pMan, DdManager * dd, DdNode * bFuncOn, DdNode * bFuncOnDc, int nFanins, int fAllPrimes, Vec_Str_t * vCube, int fMode ); +extern int Abc_NtkBddToSop( Abc_Ntk_t * pNtk, int fDirect ); +extern void Abc_NodeBddToCnf( Abc_Obj_t * pNode, Extra_MmFlex_t * pMmMan, Vec_Str_t * vCube, int fAllPrimes, char ** ppSop0, char ** ppSop1 ); +extern int Abc_CountZddCubes( DdManager * dd, DdNode * zCover ); +extern void Abc_NtkLogicMakeDirectSops( Abc_Ntk_t * pNtk ); +extern int Abc_NtkSopToAig( Abc_Ntk_t * pNtk ); +extern int Abc_NtkAigToBdd( Abc_Ntk_t * pNtk ); +extern unsigned * Abc_ConvertAigToTruth( Hop_Man_t * p, Hop_Obj_t * pRoot, int nVars, Vec_Int_t * vTruth, int fMsbFirst ); +extern int Abc_NtkMapToSop( Abc_Ntk_t * pNtk ); +extern int Abc_NtkToSop( Abc_Ntk_t * pNtk, int fDirect ); +extern int Abc_NtkToBdd( Abc_Ntk_t * pNtk ); +extern int Abc_NtkToAig( Abc_Ntk_t * pNtk ); +/*=== abcHaig.c ==========================================================*/ +extern int Abc_NtkHaigStart( Abc_Ntk_t * pNtk ); +extern int Abc_NtkHaigStop( Abc_Ntk_t * pNtk ); +extern Abc_Ntk_t * Abc_NtkHaigUse( Abc_Ntk_t * pNtk ); +/*=== abcHie.c ==========================================================*/ +extern Abc_Ntk_t * Abc_NtkFlattenLogicHierarchy( Abc_Ntk_t * pNtk ); +extern Abc_Ntk_t * Abc_NtkConvertBlackboxes( Abc_Ntk_t * pNtk ); +extern Abc_Ntk_t * Abc_NtkInsertNewLogic( Abc_Ntk_t * pNtkH, Abc_Ntk_t * pNtkL ); +/*=== abcLatch.c ==========================================================*/ +extern bool Abc_NtkLatchIsSelfFeed( Abc_Obj_t * pLatch ); +extern int Abc_NtkCountSelfFeedLatches( Abc_Ntk_t * pNtk ); +extern int Abc_NtkRemoveSelfFeedLatches( Abc_Ntk_t * pNtk ); +extern Vec_Int_t * Abc_NtkCollectLatchValues( Abc_Ntk_t * pNtk ); +extern void Abc_NtkInsertLatchValues( Abc_Ntk_t * pNtk, Vec_Int_t * vValues ); + /*=== abcLib.c ==========================================================*/ +extern Abc_Lib_t * Abc_LibCreate( char * pName ); +extern void Abc_LibFree( Abc_Lib_t * pLib, Abc_Ntk_t * pNtk ); +extern void Abc_LibPrint( Abc_Lib_t * pLib ); +extern int Abc_LibAddModel( Abc_Lib_t * pLib, Abc_Ntk_t * pNtk ); +extern Abc_Ntk_t * Abc_LibFindModelByName( Abc_Lib_t * pLib, char * pName ); +extern int Abc_LibFindTopLevelModels( Abc_Lib_t * pLib ); +extern Abc_Ntk_t * Abc_LibDeriveRoot( Abc_Lib_t * pLib ); +/*=== abcMiter.c ==========================================================*/ +extern int Abc_NtkMinimumBase( Abc_Ntk_t * pNtk ); +extern int Abc_NodeMinimumBase( Abc_Obj_t * pNode ); +extern int Abc_NtkRemoveDupFanins( Abc_Ntk_t * pNtk ); +extern int Abc_NodeRemoveDupFanins( Abc_Obj_t * pNode ); +/*=== abcMiter.c ==========================================================*/ +extern Abc_Ntk_t * Abc_NtkMiter( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int fComb, int nPartSize ); +extern void Abc_NtkMiterAddCone( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkMiter, Abc_Obj_t * pNode ); +extern Abc_Ntk_t * Abc_NtkMiterAnd( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int fOr, int fCompl2 ); +extern Abc_Ntk_t * Abc_NtkMiterCofactor( Abc_Ntk_t * pNtk, Vec_Int_t * vPiValues ); +extern Abc_Ntk_t * Abc_NtkMiterForCofactors( Abc_Ntk_t * pNtk, int Out, int In1, int In2 ); +extern Abc_Ntk_t * Abc_NtkMiterQuantify( Abc_Ntk_t * pNtk, int In, int fExist ); +extern Abc_Ntk_t * Abc_NtkMiterQuantifyPis( Abc_Ntk_t * pNtk ); +extern int Abc_NtkMiterIsConstant( Abc_Ntk_t * pMiter ); +extern void Abc_NtkMiterReport( Abc_Ntk_t * pMiter ); +extern Abc_Ntk_t * Abc_NtkFrames( Abc_Ntk_t * pNtk, int nFrames, int fInitial ); +/*=== abcObj.c ==========================================================*/ +extern Abc_Obj_t * Abc_ObjAlloc( Abc_Ntk_t * pNtk, Abc_ObjType_t Type ); +extern void Abc_ObjRecycle( Abc_Obj_t * pObj ); +extern Abc_Obj_t * Abc_NtkCreateObj( Abc_Ntk_t * pNtk, Abc_ObjType_t Type ); +extern void Abc_NtkDeleteObj( Abc_Obj_t * pObj ); +extern void Abc_NtkDeleteObj_rec( Abc_Obj_t * pObj, int fOnlyNodes ); +extern void Abc_NtkDeleteAll_rec( Abc_Obj_t * pObj ); +extern Abc_Obj_t * Abc_NtkDupObj( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pObj, int fCopyName ); +extern Abc_Obj_t * Abc_NtkDupBox( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pBox, int fCopyName ); +extern Abc_Obj_t * Abc_NtkCloneObj( Abc_Obj_t * pNode ); +extern Abc_Obj_t * Abc_NtkFindNode( Abc_Ntk_t * pNtk, char * pName ); +extern Abc_Obj_t * Abc_NtkFindNet( Abc_Ntk_t * pNtk, char * pName ); +extern Abc_Obj_t * Abc_NtkFindCi( Abc_Ntk_t * pNtk, char * pName ); +extern Abc_Obj_t * Abc_NtkFindCo( Abc_Ntk_t * pNtk, char * pName ); +extern Abc_Obj_t * Abc_NtkFindOrCreateNet( Abc_Ntk_t * pNtk, char * pName ); +extern Abc_Obj_t * Abc_NtkCreateNodeConst0( Abc_Ntk_t * pNtk ); +extern Abc_Obj_t * Abc_NtkCreateNodeConst1( Abc_Ntk_t * pNtk ); +extern Abc_Obj_t * Abc_NtkCreateNodeInv( Abc_Ntk_t * pNtk, Abc_Obj_t * pFanin ); +extern Abc_Obj_t * Abc_NtkCreateNodeBuf( Abc_Ntk_t * pNtk, Abc_Obj_t * pFanin ); +extern Abc_Obj_t * Abc_NtkCreateNodeAnd( Abc_Ntk_t * pNtk, Vec_Ptr_t * vFanins ); +extern Abc_Obj_t * Abc_NtkCreateNodeOr( Abc_Ntk_t * pNtk, Vec_Ptr_t * vFanins ); +extern Abc_Obj_t * Abc_NtkCreateNodeExor( Abc_Ntk_t * pNtk, Vec_Ptr_t * vFanins ); +extern Abc_Obj_t * Abc_NtkCreateNodeMux( Abc_Ntk_t * pNtk, Abc_Obj_t * pNodeC, Abc_Obj_t * pNode1, Abc_Obj_t * pNode0 ); +extern bool Abc_NodeIsConst( Abc_Obj_t * pNode ); +extern bool Abc_NodeIsConst0( Abc_Obj_t * pNode ); +extern bool Abc_NodeIsConst1( Abc_Obj_t * pNode ); +extern bool Abc_NodeIsBuf( Abc_Obj_t * pNode ); +extern bool Abc_NodeIsInv( Abc_Obj_t * pNode ); +extern void Abc_NodeComplement( Abc_Obj_t * pNode ); +/*=== abcNames.c ====================================================*/ +extern char * Abc_ObjName( Abc_Obj_t * pNode ); +extern char * Abc_ObjAssignName( Abc_Obj_t * pObj, char * pName, char * pSuffix ); +extern char * Abc_ObjNameSuffix( Abc_Obj_t * pObj, char * pSuffix ); +extern char * Abc_ObjNameDummy( char * pPrefix, int Num, int nDigits ); +extern void Abc_NtkTrasferNames( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkNew ); +extern void Abc_NtkTrasferNamesNoLatches( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkNew ); +extern Vec_Ptr_t * Abc_NodeGetFaninNames( Abc_Obj_t * pNode ); +extern Vec_Ptr_t * Abc_NodeGetFakeNames( int nNames ); +extern void Abc_NodeFreeNames( Vec_Ptr_t * vNames ); +extern char ** Abc_NtkCollectCioNames( Abc_Ntk_t * pNtk, int fCollectCos ); +extern int Abc_NodeCompareNames( Abc_Obj_t ** pp1, Abc_Obj_t ** pp2 ); +extern void Abc_NtkOrderObjsByName( Abc_Ntk_t * pNtk, int fComb ); +extern void Abc_NtkAddDummyPiNames( Abc_Ntk_t * pNtk ); +extern void Abc_NtkAddDummyPoNames( Abc_Ntk_t * pNtk ); +extern void Abc_NtkAddDummyAssertNames( Abc_Ntk_t * pNtk ); +extern void Abc_NtkAddDummyBoxNames( Abc_Ntk_t * pNtk ); +extern void Abc_NtkShortNames( Abc_Ntk_t * pNtk ); +/*=== abcNetlist.c ==========================================================*/ +extern Abc_Ntk_t * Abc_NtkToLogic( Abc_Ntk_t * pNtk ); +extern Abc_Ntk_t * Abc_NtkToNetlist( Abc_Ntk_t * pNtk ); +extern Abc_Ntk_t * Abc_NtkToNetlistBench( Abc_Ntk_t * pNtk ); +/*=== abcNtbdd.c ==========================================================*/ +extern Abc_Ntk_t * Abc_NtkDeriveFromBdd( DdManager * dd, DdNode * bFunc, char * pNamePo, Vec_Ptr_t * vNamesPi ); +extern Abc_Ntk_t * Abc_NtkBddToMuxes( Abc_Ntk_t * pNtk ); +extern DdManager * Abc_NtkBuildGlobalBdds( Abc_Ntk_t * pNtk, int fBddSizeMax, int fDropInternal, int fReorder, int fVerbose ); +extern DdManager * Abc_NtkFreeGlobalBdds( Abc_Ntk_t * pNtk, int fFreeMan ); +extern int Abc_NtkSizeOfGlobalBdds( Abc_Ntk_t * pNtk ); +/*=== abcNtk.c ==========================================================*/ +extern Abc_Ntk_t * Abc_NtkAlloc( Abc_NtkType_t Type, Abc_NtkFunc_t Func, int fUseMemMan ); +extern Abc_Ntk_t * Abc_NtkStartFrom( Abc_Ntk_t * pNtk, Abc_NtkType_t Type, Abc_NtkFunc_t Func ); +extern Abc_Ntk_t * Abc_NtkStartFromNoLatches( Abc_Ntk_t * pNtk, Abc_NtkType_t Type, Abc_NtkFunc_t Func ); +extern void Abc_NtkFinalize( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkNew ); +extern Abc_Ntk_t * Abc_NtkStartRead( char * pName ); +extern void Abc_NtkFinalizeRead( Abc_Ntk_t * pNtk ); +extern Abc_Ntk_t * Abc_NtkDup( Abc_Ntk_t * pNtk ); +extern Abc_Ntk_t * Abc_NtkCreateCone( Abc_Ntk_t * pNtk, Abc_Obj_t * pNode, char * pNodeName, int fUseAllCis ); +extern Abc_Ntk_t * Abc_NtkCreateConeArray( Abc_Ntk_t * pNtk, Vec_Ptr_t * vRoots, int fUseAllCis ); +extern Abc_Ntk_t * Abc_NtkCreateMffc( Abc_Ntk_t * pNtk, Abc_Obj_t * pNode, char * pNodeName ); +extern Abc_Ntk_t * Abc_NtkCreateTarget( Abc_Ntk_t * pNtk, Vec_Ptr_t * vRoots, Vec_Int_t * vValues ); +extern Abc_Ntk_t * Abc_NtkCreateFromNode( Abc_Ntk_t * pNtk, Abc_Obj_t * pNode ); +extern Abc_Ntk_t * Abc_NtkCreateWithNode( char * pSop ); +extern void Abc_NtkDelete( Abc_Ntk_t * pNtk ); +extern void Abc_NtkFixNonDrivenNets( Abc_Ntk_t * pNtk ); +extern void Abc_NtkMakeComb( Abc_Ntk_t * pNtk ); +/*=== abcPrint.c ==========================================================*/ +extern void Abc_NtkPrintStats( FILE * pFile, Abc_Ntk_t * pNtk, int fFactored ); +extern void Abc_NtkPrintIo( FILE * pFile, Abc_Ntk_t * pNtk ); +extern void Abc_NtkPrintLatch( FILE * pFile, Abc_Ntk_t * pNtk ); +extern void Abc_NtkPrintFanio( FILE * pFile, Abc_Ntk_t * pNtk ); +extern void Abc_NodePrintFanio( FILE * pFile, Abc_Obj_t * pNode ); +extern void Abc_NtkPrintFactor( FILE * pFile, Abc_Ntk_t * pNtk, int fUseRealNames ); +extern void Abc_NodePrintFactor( FILE * pFile, Abc_Obj_t * pNode, int fUseRealNames ); +extern void Abc_NtkPrintLevel( FILE * pFile, Abc_Ntk_t * pNtk, int fProfile, int fListNodes ); +extern void Abc_NodePrintLevel( FILE * pFile, Abc_Obj_t * pNode ); +extern void Abc_NtkPrintSkews( FILE * pFile, Abc_Ntk_t * pNtk, int fPrintAll ); +extern void Abc_ObjPrint( FILE * pFile, Abc_Obj_t * pObj ); +/*=== abcProve.c ==========================================================*/ +extern int Abc_NtkMiterProve( Abc_Ntk_t ** ppNtk, void * pParams ); +extern int Abc_NtkIvyProve( Abc_Ntk_t ** ppNtk, void * pPars ); +/*=== abcRec.c ==========================================================*/ +extern void Abc_NtkRecStart( Abc_Ntk_t * pNtk, int nVars, int nCuts ); +extern void Abc_NtkRecStop(); +extern void Abc_NtkRecAdd( Abc_Ntk_t * pNtk ); +extern void Abc_NtkRecPs(); +extern void Abc_NtkRecFilter( int iVar, int iPlus ); +extern Abc_Ntk_t * Abc_NtkRecUse(); +extern int Abc_NtkRecIsRunning(); +extern int Abc_NtkRecVarNum(); +extern Vec_Int_t * Abc_NtkRecMemory(); +extern int Abc_NtkRecStrashNode( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pObj, unsigned * pTruth, int nVars ); +/*=== abcReconv.c ==========================================================*/ +extern Abc_ManCut_t * Abc_NtkManCutStart( int nNodeSizeMax, int nConeSizeMax, int nNodeFanStop, int nConeFanStop ); +extern void Abc_NtkManCutStop( Abc_ManCut_t * p ); +extern Vec_Ptr_t * Abc_NtkManCutReadCutLarge( Abc_ManCut_t * p ); +extern Vec_Ptr_t * Abc_NtkManCutReadCutSmall( Abc_ManCut_t * p ); +extern Vec_Ptr_t * Abc_NtkManCutReadVisited( Abc_ManCut_t * p ); +extern Vec_Ptr_t * Abc_NodeFindCut( Abc_ManCut_t * p, Abc_Obj_t * pRoot, bool fContain ); +extern void Abc_NodeConeCollect( Abc_Obj_t ** ppRoots, int nRoots, Vec_Ptr_t * vFanins, Vec_Ptr_t * vVisited, int fIncludeFanins ); +extern DdNode * Abc_NodeConeBdd( DdManager * dd, DdNode ** pbVars, Abc_Obj_t * pNode, Vec_Ptr_t * vFanins, Vec_Ptr_t * vVisited ); +extern DdNode * Abc_NodeConeDcs( DdManager * dd, DdNode ** pbVarsX, DdNode ** pbVarsY, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vRoots, Vec_Ptr_t * vVisited ); +extern Vec_Ptr_t * Abc_NodeCollectTfoCands( Abc_ManCut_t * p, Abc_Obj_t * pRoot, Vec_Ptr_t * vFanins, int LevelMax ); +/*=== abcRefs.c ==========================================================*/ +extern int Abc_NodeMffcSize( Abc_Obj_t * pNode ); +extern int Abc_NodeMffcSizeSupp( Abc_Obj_t * pNode ); +extern int Abc_NodeMffcSizeStop( Abc_Obj_t * pNode ); +extern int Abc_NodeMffcLabelAig( Abc_Obj_t * pNode ); +extern int Abc_NodeMffcLabel( Abc_Obj_t * pNode ); +extern void Abc_NodeMffsConeSupp( Abc_Obj_t * pNode, Vec_Ptr_t * vCone, Vec_Ptr_t * vSupp ); +extern int Abc_NodeDeref_rec( Abc_Obj_t * pNode ); +extern int Abc_NodeRef_rec( Abc_Obj_t * pNode ); +/*=== abcRefactor.c ==========================================================*/ +extern int Abc_NtkRefactor( Abc_Ntk_t * pNtk, int nNodeSizeMax, int nConeSizeMax, bool fUpdateLevel, bool fUseZeros, bool fUseDcs, bool fVerbose ); +/*=== abcRewrite.c ==========================================================*/ +extern int Abc_NtkRewrite( Abc_Ntk_t * pNtk, int fUpdateLevel, int fUseZeros, int fVerbose, int fVeryVerbose, int fPlaceEnable ); +/*=== abcSat.c ==========================================================*/ +extern int Abc_NtkMiterSat( Abc_Ntk_t * pNtk, sint64 nConfLimit, sint64 nInsLimit, int fVerbose, sint64 * pNumConfs, sint64 * pNumInspects ); +extern void * Abc_NtkMiterSatCreate( Abc_Ntk_t * pNtk, int fAllPrimes ); +/*=== abcSop.c ==========================================================*/ +extern char * Abc_SopRegister( Extra_MmFlex_t * pMan, char * pName ); +extern char * Abc_SopStart( Extra_MmFlex_t * pMan, int nCubes, int nVars ); +extern char * Abc_SopCreateConst0( Extra_MmFlex_t * pMan ); +extern char * Abc_SopCreateConst1( Extra_MmFlex_t * pMan ); +extern char * Abc_SopCreateAnd2( Extra_MmFlex_t * pMan, int fCompl0, int fCompl1 ); +extern char * Abc_SopCreateAnd( Extra_MmFlex_t * pMan, int nVars, int * pfCompl ); +extern char * Abc_SopCreateNand( Extra_MmFlex_t * pMan, int nVars ); +extern char * Abc_SopCreateOr( Extra_MmFlex_t * pMan, int nVars, int * pfCompl ); +extern char * Abc_SopCreateOrMultiCube( Extra_MmFlex_t * pMan, int nVars, int * pfCompl ); +extern char * Abc_SopCreateNor( Extra_MmFlex_t * pMan, int nVars ); +extern char * Abc_SopCreateXor( Extra_MmFlex_t * pMan, int nVars ); +extern char * Abc_SopCreateXorSpecial( Extra_MmFlex_t * pMan, int nVars ); +extern char * Abc_SopCreateNxor( Extra_MmFlex_t * pMan, int nVars ); +extern char * Abc_SopCreateMux( Extra_MmFlex_t * pMan ); +extern char * Abc_SopCreateInv( Extra_MmFlex_t * pMan ); +extern char * Abc_SopCreateBuf( Extra_MmFlex_t * pMan ); +extern char * Abc_SopCreateFromTruth( Extra_MmFlex_t * pMan, int nVars, unsigned * pTruth ); +extern char * Abc_SopCreateFromIsop( Extra_MmFlex_t * pMan, int nVars, Vec_Int_t * vCover ); +extern int Abc_SopGetCubeNum( char * pSop ); +extern int Abc_SopGetLitNum( char * pSop ); +extern int Abc_SopGetVarNum( char * pSop ); +extern int Abc_SopGetPhase( char * pSop ); +extern int Abc_SopGetIthCareLit( char * pSop, int i ); +extern void Abc_SopComplement( char * pSop ); +extern bool Abc_SopIsComplement( char * pSop ); +extern bool Abc_SopIsConst0( char * pSop ); +extern bool Abc_SopIsConst1( char * pSop ); +extern bool Abc_SopIsBuf( char * pSop ); +extern bool Abc_SopIsInv( char * pSop ); +extern bool Abc_SopIsAndType( char * pSop ); +extern bool Abc_SopIsOrType( char * pSop ); +extern int Abc_SopIsExorType( char * pSop ); +extern bool Abc_SopCheck( char * pSop, int nFanins ); +extern char * Abc_SopFromTruthBin( char * pTruth ); +extern char * Abc_SopFromTruthHex( char * pTruth ); +extern char * Abc_SopEncoderPos( Extra_MmFlex_t * pMan, int iValue, int nValues ); +extern char * Abc_SopEncoderLog( Extra_MmFlex_t * pMan, int iBit, int nValues ); +extern char * Abc_SopDecoderPos( Extra_MmFlex_t * pMan, int nValues ); +extern char * Abc_SopDecoderLog( Extra_MmFlex_t * pMan, int nValues ); +/*=== abcStrash.c ==========================================================*/ +extern Abc_Ntk_t * Abc_NtkStrash( Abc_Ntk_t * pNtk, int fAllNodes, int fCleanup, int fRecord ); +extern Abc_Obj_t * Abc_NodeStrash( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pNode, int fRecord ); +extern int Abc_NtkAppend( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int fAddPos ); +extern Abc_Ntk_t * Abc_NtkTopmost( Abc_Ntk_t * pNtk, int nLevels ); +/*=== abcSweep.c ==========================================================*/ +extern int Abc_NtkSweep( Abc_Ntk_t * pNtk, int fVerbose ); +extern int Abc_NtkCleanup( Abc_Ntk_t * pNtk, int fVerbose ); +extern int Abc_NtkCleanupSeq( Abc_Ntk_t * pNtk, int fLatchSweep, int fAutoSweep, int fVerbose ); +/*=== abcTiming.c ==========================================================*/ +extern Abc_Time_t * Abc_NodeReadArrival( Abc_Obj_t * pNode ); +extern Abc_Time_t * Abc_NodeReadRequired( Abc_Obj_t * pNode ); +extern Abc_Time_t * Abc_NtkReadDefaultArrival( Abc_Ntk_t * pNtk ); +extern Abc_Time_t * Abc_NtkReadDefaultRequired( Abc_Ntk_t * pNtk ); +extern void Abc_NtkTimeSetDefaultArrival( Abc_Ntk_t * pNtk, float Rise, float Fall ); +extern void Abc_NtkTimeSetDefaultRequired( Abc_Ntk_t * pNtk, float Rise, float Fall ); +extern void Abc_NtkTimeSetArrival( Abc_Ntk_t * pNtk, int ObjId, float Rise, float Fall ); +extern void Abc_NtkTimeSetRequired( Abc_Ntk_t * pNtk, int ObjId, float Rise, float Fall ); +extern void Abc_NtkTimeInitialize( Abc_Ntk_t * pNtk ); +extern void Abc_ManTimeStop( Abc_ManTime_t * p ); +extern void Abc_ManTimeDup( Abc_Ntk_t * pNtkOld, Abc_Ntk_t * pNtkNew ); +extern void Abc_NtkSetNodeLevelsArrival( Abc_Ntk_t * pNtk ); +extern float * Abc_NtkGetCiArrivalFloats( Abc_Ntk_t * pNtk ); +extern Abc_Time_t * Abc_NtkGetCiArrivalTimes( Abc_Ntk_t * pNtk ); +extern float Abc_NtkDelayTrace( Abc_Ntk_t * pNtk ); +extern int Abc_ObjLevelNew( Abc_Obj_t * pObj ); +extern int Abc_ObjReverseLevelNew( Abc_Obj_t * pObj ); +extern int Abc_ObjRequiredLevel( Abc_Obj_t * pObj ); +extern int Abc_ObjReverseLevel( Abc_Obj_t * pObj ); +extern void Abc_ObjSetReverseLevel( Abc_Obj_t * pObj, int LevelR ); +extern void Abc_NtkStartReverseLevels( Abc_Ntk_t * pNtk, int nMaxLevelIncrease ); +extern void Abc_NtkStopReverseLevels( Abc_Ntk_t * pNtk ); +extern void Abc_NtkUpdateLevel( Abc_Obj_t * pObjNew, Vec_Vec_t * vLevels ); +extern void Abc_NtkUpdateReverseLevel( Abc_Obj_t * pObjNew, Vec_Vec_t * vLevels ); +extern void Abc_NtkUpdate( Abc_Obj_t * pObj, Abc_Obj_t * pObjNew, Vec_Vec_t * vLevels ); +/*=== abcUtil.c ==========================================================*/ +extern void * Abc_NtkAttrFree( Abc_Ntk_t * pNtk, int Attr, int fFreeMan ); +extern void Abc_NtkIncrementTravId( Abc_Ntk_t * pNtk ); +extern void Abc_NtkOrderCisCos( Abc_Ntk_t * pNtk ); +extern int Abc_NtkGetCubeNum( Abc_Ntk_t * pNtk ); +extern int Abc_NtkGetLitNum( Abc_Ntk_t * pNtk ); +extern int Abc_NtkGetLitFactNum( Abc_Ntk_t * pNtk ); +extern int Abc_NtkGetBddNodeNum( Abc_Ntk_t * pNtk ); +extern int Abc_NtkGetAigNodeNum( Abc_Ntk_t * pNtk ); +extern int Abc_NtkGetClauseNum( Abc_Ntk_t * pNtk ); +extern double Abc_NtkGetMappedArea( Abc_Ntk_t * pNtk ); +extern int Abc_NtkGetExorNum( Abc_Ntk_t * pNtk ); +extern int Abc_NtkGetMuxNum( Abc_Ntk_t * pNtk ); +extern int Abc_NtkGetChoiceNum( Abc_Ntk_t * pNtk ); +extern int Abc_NtkGetFaninMax( Abc_Ntk_t * pNtk ); +extern int Abc_NtkGetTotalFanins( Abc_Ntk_t * pNtk ); +extern void Abc_NtkCleanCopy( Abc_Ntk_t * pNtk ); +extern void Abc_NtkCleanData( Abc_Ntk_t * pNtk ); +extern void Abc_NtkCleanEquiv( Abc_Ntk_t * pNtk ); +extern int Abc_NtkCountCopy( Abc_Ntk_t * pNtk ); +extern Vec_Ptr_t * Abc_NtkSaveCopy( Abc_Ntk_t * pNtk ); +extern void Abc_NtkLoadCopy( Abc_Ntk_t * pNtk, Vec_Ptr_t * vCopies ); +extern void Abc_NtkCleanNext( Abc_Ntk_t * pNtk ); +extern void Abc_NtkCleanMarkA( Abc_Ntk_t * pNtk ); +extern Abc_Obj_t * Abc_NodeFindCoFanout( Abc_Obj_t * pNode ); +extern Abc_Obj_t * Abc_NodeFindNonCoFanout( Abc_Obj_t * pNode ); +extern Abc_Obj_t * Abc_NodeHasUniqueCoFanout( Abc_Obj_t * pNode ); +extern bool Abc_NtkLogicHasSimpleCos( Abc_Ntk_t * pNtk ); +extern int Abc_NtkLogicMakeSimpleCos( Abc_Ntk_t * pNtk, bool fDuplicate ); +extern void Abc_VecObjPushUniqueOrderByLevel( Vec_Ptr_t * p, Abc_Obj_t * pNode ); +extern bool Abc_NodeIsExorType( Abc_Obj_t * pNode ); +extern bool Abc_NodeIsMuxType( Abc_Obj_t * pNode ); +extern bool Abc_NodeIsMuxControlType( Abc_Obj_t * pNode ); +extern Abc_Obj_t * Abc_NodeRecognizeMux( Abc_Obj_t * pNode, Abc_Obj_t ** ppNodeT, Abc_Obj_t ** ppNodeE ); +extern int Abc_NtkPrepareTwoNtks( FILE * pErr, Abc_Ntk_t * pNtk, char ** argv, int argc, Abc_Ntk_t ** ppNtk1, Abc_Ntk_t ** ppNtk2, int * pfDelete1, int * pfDelete2 ); +extern void Abc_NodeCollectFanins( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes ); +extern void Abc_NodeCollectFanouts( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes ); +extern Vec_Ptr_t * Abc_NtkCollectLatches( Abc_Ntk_t * pNtk ); +extern int Abc_NodeCompareLevelsIncrease( Abc_Obj_t ** pp1, Abc_Obj_t ** pp2 ); +extern int Abc_NodeCompareLevelsDecrease( Abc_Obj_t ** pp1, Abc_Obj_t ** pp2 ); +extern Vec_Int_t * Abc_NtkFanoutCounts( Abc_Ntk_t * pNtk ); +extern Vec_Ptr_t * Abc_NtkCollectObjects( Abc_Ntk_t * pNtk ); +extern Vec_Int_t * Abc_NtkGetCiIds( Abc_Ntk_t * pNtk ); +extern void Abc_NtkReassignIds( Abc_Ntk_t * pNtk ); +extern int Abc_ObjPointerCompare( void ** pp1, void ** pp2 ); +extern void Abc_NtkTransferCopy( Abc_Ntk_t * pNtk ); +/*=== abcVerify.c ==========================================================*/ +extern int * Abc_NtkVerifyGetCleanModel( Abc_Ntk_t * pNtk, int nFrames ); +extern int * Abc_NtkVerifySimulatePattern( Abc_Ntk_t * pNtk, int * pModel ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/base/abc/abcAig.c b/abc_with_bb_support/src/base/abc/abcAig.c new file mode 100644 index 000000000..6b644d97f --- /dev/null +++ b/abc_with_bb_support/src/base/abc/abcAig.c @@ -0,0 +1,1476 @@ +/**CFile**************************************************************** + + FileName [abcAig.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Simple structural hashing package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcAig.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +/* + AIG is an And-Inv Graph with structural hashing. + It is always structurally hashed. It means that at any time: + - for each AND gate, there are no other AND gates with the same children + - the constants are propagated + - there is no single-input nodes (inverters/buffers) + Additionally the following invariants are satisfied: + - there are no dangling nodes (the nodes without fanout) + - the level of each AND gate reflects the levels of this fanins + - the EXOR-status of each node is up-to-date + - the AND nodes are in the topological order + - the constant 1 node has always number 0 in the object list + The operations that are performed on AIGs: + - building new nodes (Abc_AigAnd) + - performing elementary Boolean operations (Abc_AigOr, Abc_AigXor, etc) + - replacing one node by another (Abc_AigReplace) + - propagating constants (Abc_AigReplace) + When AIG is duplicated, the new graph is structurally hashed too. + If this repeated hashing leads to fewer nodes, it means the original + AIG was not strictly hashed (one of the conditions above is violated). +*/ + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// the simple AIG manager +struct Abc_Aig_t_ +{ + Abc_Ntk_t * pNtkAig; // the AIG network + Abc_Obj_t * pConst1; // the constant 1 object (not a node!) + Abc_Obj_t ** pBins; // the table bins + int nBins; // the size of the table + int nEntries; // the total number of entries in the table + Vec_Ptr_t * vNodes; // the temporary array of nodes + Vec_Ptr_t * vStackReplaceOld; // the nodes to be replaced + Vec_Ptr_t * vStackReplaceNew; // the nodes to be used for replacement + Vec_Vec_t * vLevels; // the nodes to be updated + Vec_Vec_t * vLevelsR; // the nodes to be updated + Vec_Ptr_t * vAddedCells; // the added nodes + Vec_Ptr_t * vUpdatedNets; // the nodes whose fanouts have changed + + int nStrash0; + int nStrash1; + int nStrash5; + int nStrash2; +}; + +// iterators through the entries in the linked lists of nodes +#define Abc_AigBinForEachEntry( pBin, pEnt ) \ + for ( pEnt = pBin; \ + pEnt; \ + pEnt = pEnt->pNext ) +#define Abc_AigBinForEachEntrySafe( pBin, pEnt, pEnt2 ) \ + for ( pEnt = pBin, \ + pEnt2 = pEnt? pEnt->pNext: NULL; \ + pEnt; \ + pEnt = pEnt2, \ + pEnt2 = pEnt? pEnt->pNext: NULL ) + +// hash key for the structural hash table +//static inline unsigned Abc_HashKey2( Abc_Obj_t * p0, Abc_Obj_t * p1, int TableSize ) { return ((unsigned)(p0) + (unsigned)(p1) * 12582917) % TableSize; } +//static inline unsigned Abc_HashKey2( Abc_Obj_t * p0, Abc_Obj_t * p1, int TableSize ) { return ((unsigned)((a)->Id + (b)->Id) * ((a)->Id + (b)->Id + 1) / 2) % TableSize; } + +// hashing the node +static unsigned Abc_HashKey2( Abc_Obj_t * p0, Abc_Obj_t * p1, int TableSize ) +{ + unsigned Key = 0; + Key ^= Abc_ObjRegular(p0)->Id * 7937; + Key ^= Abc_ObjRegular(p1)->Id * 2971; + Key ^= Abc_ObjIsComplement(p0) * 911; + Key ^= Abc_ObjIsComplement(p1) * 353; + return Key % TableSize; +} + +// structural hash table procedures +static Abc_Obj_t * Abc_AigAndCreate( Abc_Aig_t * pMan, Abc_Obj_t * p0, Abc_Obj_t * p1 ); +static Abc_Obj_t * Abc_AigAndCreateFrom( Abc_Aig_t * pMan, Abc_Obj_t * p0, Abc_Obj_t * p1, Abc_Obj_t * pAnd ); +static void Abc_AigAndDelete( Abc_Aig_t * pMan, Abc_Obj_t * pThis ); +static void Abc_AigResize( Abc_Aig_t * pMan ); +// incremental AIG procedures +static void Abc_AigReplace_int( Abc_Aig_t * pMan, Abc_Obj_t * pOld, Abc_Obj_t * pNew, int fUpdateLevel ); +static void Abc_AigUpdateLevel_int( Abc_Aig_t * pMan ); +static void Abc_AigUpdateLevelR_int( Abc_Aig_t * pMan ); +static void Abc_AigRemoveFromLevelStructure( Vec_Vec_t * vStruct, Abc_Obj_t * pNode ); +static void Abc_AigRemoveFromLevelStructureR( Vec_Vec_t * vStruct, Abc_Obj_t * pNode ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates the local AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Aig_t * Abc_AigAlloc( Abc_Ntk_t * pNtkAig ) +{ + Abc_Aig_t * pMan; + // start the manager + pMan = ALLOC( Abc_Aig_t, 1 ); + memset( pMan, 0, sizeof(Abc_Aig_t) ); + // allocate the table + pMan->nBins = Cudd_PrimeCopy( 10000 ); + pMan->pBins = ALLOC( Abc_Obj_t *, pMan->nBins ); + memset( pMan->pBins, 0, sizeof(Abc_Obj_t *) * pMan->nBins ); + pMan->vNodes = Vec_PtrAlloc( 100 ); + pMan->vLevels = Vec_VecAlloc( 100 ); + pMan->vLevelsR = Vec_VecAlloc( 100 ); + pMan->vStackReplaceOld = Vec_PtrAlloc( 100 ); + pMan->vStackReplaceNew = Vec_PtrAlloc( 100 ); + // create the constant node + assert( pNtkAig->vObjs->nSize == 0 ); + pMan->pConst1 = Abc_NtkCreateObj( pNtkAig, ABC_OBJ_NODE ); + pMan->pConst1->Type = ABC_OBJ_CONST1; + pMan->pConst1->fPhase = 1; + pNtkAig->nObjCounts[ABC_OBJ_NODE]--; + // save the current network + pMan->pNtkAig = pNtkAig; + return pMan; +} + +/**Function************************************************************* + + Synopsis [Deallocates the local AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_AigFree( Abc_Aig_t * pMan ) +{ + assert( Vec_PtrSize( pMan->vStackReplaceOld ) == 0 ); + assert( Vec_PtrSize( pMan->vStackReplaceNew ) == 0 ); + // free the table + if ( pMan->vAddedCells ) + Vec_PtrFree( pMan->vAddedCells ); + if ( pMan->vUpdatedNets ) + Vec_PtrFree( pMan->vUpdatedNets ); + Vec_VecFree( pMan->vLevels ); + Vec_VecFree( pMan->vLevelsR ); + Vec_PtrFree( pMan->vStackReplaceOld ); + Vec_PtrFree( pMan->vStackReplaceNew ); + Vec_PtrFree( pMan->vNodes ); + free( pMan->pBins ); + free( pMan ); +} + +/**Function************************************************************* + + Synopsis [Returns the number of dangling nodes removed.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_AigCleanup( Abc_Aig_t * pMan ) +{ + Vec_Ptr_t * vDangles; + Abc_Obj_t * pAnd; + int i, nNodesOld; +// printf( "Strash0 = %d. Strash1 = %d. Strash100 = %d. StrashM = %d.\n", +// pMan->nStrash0, pMan->nStrash1, pMan->nStrash5, pMan->nStrash2 ); + nNodesOld = pMan->nEntries; + // collect the AND nodes that do not fanout + vDangles = Vec_PtrAlloc( 100 ); + for ( i = 0; i < pMan->nBins; i++ ) + Abc_AigBinForEachEntry( pMan->pBins[i], pAnd ) + if ( Abc_ObjFanoutNum(pAnd) == 0 ) + Vec_PtrPush( vDangles, pAnd ); + // process the dangling nodes and their MFFCs + Vec_PtrForEachEntry( vDangles, pAnd, i ) + Abc_AigDeleteNode( pMan, pAnd ); + Vec_PtrFree( vDangles ); + return nNodesOld - pMan->nEntries; +} + +/**Function************************************************************* + + Synopsis [Makes sure that every node in the table is in the network and vice versa.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_AigCheck( Abc_Aig_t * pMan ) +{ + Abc_Obj_t * pObj, * pAnd; + int i, nFanins, Counter; + Abc_NtkForEachNode( pMan->pNtkAig, pObj, i ) + { + nFanins = Abc_ObjFaninNum(pObj); + if ( nFanins == 0 ) + { + if ( !Abc_AigNodeIsConst(pObj) ) + { + printf( "Abc_AigCheck: The AIG has non-standard constant nodes.\n" ); + return 0; + } + continue; + } + if ( nFanins == 1 ) + { + printf( "Abc_AigCheck: The AIG has single input nodes.\n" ); + return 0; + } + if ( nFanins > 2 ) + { + printf( "Abc_AigCheck: The AIG has non-standard nodes.\n" ); + return 0; + } + if ( pObj->Level != 1 + ABC_MAX( Abc_ObjFanin0(pObj)->Level, Abc_ObjFanin1(pObj)->Level ) ) + printf( "Abc_AigCheck: Node \"%s\" has level that does not agree with the fanin levels.\n", Abc_ObjName(pObj) ); + pAnd = Abc_AigAndLookup( pMan, Abc_ObjChild0(pObj), Abc_ObjChild1(pObj) ); + if ( pAnd != pObj ) + printf( "Abc_AigCheck: Node \"%s\" is not in the structural hashing table.\n", Abc_ObjName(pObj) ); + } + // count the number of nodes in the table + Counter = 0; + for ( i = 0; i < pMan->nBins; i++ ) + Abc_AigBinForEachEntry( pMan->pBins[i], pAnd ) + Counter++; + if ( Counter != Abc_NtkNodeNum(pMan->pNtkAig) ) + { + printf( "Abc_AigCheck: The number of nodes in the structural hashing table is wrong.\n" ); + return 0; + } + // if the node is a choice node, nodes in its class should not have fanouts + Abc_NtkForEachNode( pMan->pNtkAig, pObj, i ) + if ( Abc_AigNodeIsChoice(pObj) ) + for ( pAnd = pObj->pData; pAnd; pAnd = pAnd->pData ) + if ( Abc_ObjFanoutNum(pAnd) > 0 ) + { + printf( "Abc_AigCheck: Representative %s", Abc_ObjName(pAnd) ); + printf( " of choice node %s has %d fanouts.\n", Abc_ObjName(pObj), Abc_ObjFanoutNum(pAnd) ); + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Computes the number of logic levels not counting PIs/POs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_AigLevel( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i, LevelsMax; + assert( Abc_NtkIsStrash(pNtk) ); + // perform the traversal + LevelsMax = 0; + Abc_NtkForEachCo( pNtk, pNode, i ) + if ( LevelsMax < (int)Abc_ObjFanin0(pNode)->Level ) + LevelsMax = (int)Abc_ObjFanin0(pNode)->Level; + return LevelsMax; +} + + +/**Function************************************************************* + + Synopsis [Performs canonicization step.] + + Description [The argument nodes can be complemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_AigAndCreate( Abc_Aig_t * pMan, Abc_Obj_t * p0, Abc_Obj_t * p1 ) +{ + Abc_Obj_t * pAnd; + unsigned Key; + // check if it is a good time for table resizing + if ( pMan->nEntries > 2 * pMan->nBins ) + Abc_AigResize( pMan ); + // order the arguments + if ( Abc_ObjRegular(p0)->Id > Abc_ObjRegular(p1)->Id ) + pAnd = p0, p0 = p1, p1 = pAnd; + // create the new node + pAnd = Abc_NtkCreateNode( pMan->pNtkAig ); + Abc_ObjAddFanin( pAnd, p0 ); + Abc_ObjAddFanin( pAnd, p1 ); + // set the level of the new node + pAnd->Level = 1 + ABC_MAX( Abc_ObjRegular(p0)->Level, Abc_ObjRegular(p1)->Level ); + pAnd->fExor = Abc_NodeIsExorType(pAnd); + pAnd->fPhase = (Abc_ObjIsComplement(p0) ^ Abc_ObjRegular(p0)->fPhase) & (Abc_ObjIsComplement(p1) ^ Abc_ObjRegular(p1)->fPhase); + // add the node to the corresponding linked list in the table + Key = Abc_HashKey2( p0, p1, pMan->nBins ); + pAnd->pNext = pMan->pBins[Key]; + pMan->pBins[Key] = pAnd; + pMan->nEntries++; + // create the cuts if defined +// if ( pAnd->pNtk->pManCut ) +// Abc_NodeGetCuts( pAnd->pNtk->pManCut, pAnd ); + pAnd->pCopy = NULL; + // add the node to the list of updated nodes + if ( pMan->vAddedCells ) + Vec_PtrPush( pMan->vAddedCells, pAnd ); + // create HAIG + if ( pAnd->pNtk->pHaig ) + pAnd->pEquiv = Hop_And( pAnd->pNtk->pHaig, Abc_ObjChild0Equiv(pAnd), Abc_ObjChild1Equiv(pAnd) ); + return pAnd; +} + +/**Function************************************************************* + + Synopsis [Performs canonicization step.] + + Description [The argument nodes can be complemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_AigAndCreateFrom( Abc_Aig_t * pMan, Abc_Obj_t * p0, Abc_Obj_t * p1, Abc_Obj_t * pAnd ) +{ + Abc_Obj_t * pTemp; + unsigned Key; + assert( !Abc_ObjIsComplement(pAnd) ); + // order the arguments + if ( Abc_ObjRegular(p0)->Id > Abc_ObjRegular(p1)->Id ) + pTemp = p0, p0 = p1, p1 = pTemp; + // create the new node + Abc_ObjAddFanin( pAnd, p0 ); + Abc_ObjAddFanin( pAnd, p1 ); + // set the level of the new node + pAnd->Level = 1 + ABC_MAX( Abc_ObjRegular(p0)->Level, Abc_ObjRegular(p1)->Level ); + pAnd->fExor = Abc_NodeIsExorType(pAnd); + // add the node to the corresponding linked list in the table + Key = Abc_HashKey2( p0, p1, pMan->nBins ); + pAnd->pNext = pMan->pBins[Key]; + pMan->pBins[Key] = pAnd; + pMan->nEntries++; + // create the cuts if defined +// if ( pAnd->pNtk->pManCut ) +// Abc_NodeGetCuts( pAnd->pNtk->pManCut, pAnd ); + pAnd->pCopy = NULL; + // add the node to the list of updated nodes +// if ( pMan->vAddedCells ) +// Vec_PtrPush( pMan->vAddedCells, pAnd ); + // create HAIG + if ( pAnd->pNtk->pHaig ) + pAnd->pEquiv = Hop_And( pAnd->pNtk->pHaig, Abc_ObjChild0Equiv(pAnd), Abc_ObjChild1Equiv(pAnd) ); + return pAnd; +} + +/**Function************************************************************* + + Synopsis [Performs canonicization step.] + + Description [The argument nodes can be complemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_AigAndLookup( Abc_Aig_t * pMan, Abc_Obj_t * p0, Abc_Obj_t * p1 ) +{ + Abc_Obj_t * pAnd, * pConst1; + unsigned Key; + assert( Abc_ObjRegular(p0)->pNtk->pManFunc == pMan ); + assert( Abc_ObjRegular(p1)->pNtk->pManFunc == pMan ); + // check for trivial cases + pConst1 = Abc_AigConst1(pMan->pNtkAig); + if ( p0 == p1 ) + return p0; + if ( p0 == Abc_ObjNot(p1) ) + return Abc_ObjNot(pConst1); + if ( Abc_ObjRegular(p0) == pConst1 ) + { + if ( p0 == pConst1 ) + return p1; + return Abc_ObjNot(pConst1); + } + if ( Abc_ObjRegular(p1) == pConst1 ) + { + if ( p1 == pConst1 ) + return p0; + return Abc_ObjNot(pConst1); + } +/* + { + int nFans0 = Abc_ObjFanoutNum( Abc_ObjRegular(p0) ); + int nFans1 = Abc_ObjFanoutNum( Abc_ObjRegular(p1) ); + if ( nFans0 == 0 || nFans1 == 0 ) + pMan->nStrash0++; + else if ( nFans0 == 1 || nFans1 == 1 ) + pMan->nStrash1++; + else if ( nFans0 <= 100 && nFans1 <= 100 ) + pMan->nStrash5++; + else + pMan->nStrash2++; + } +*/ + { + int nFans0 = Abc_ObjFanoutNum( Abc_ObjRegular(p0) ); + int nFans1 = Abc_ObjFanoutNum( Abc_ObjRegular(p1) ); + if ( nFans0 == 0 || nFans1 == 0 ) + return NULL; + } + + // order the arguments + if ( Abc_ObjRegular(p0)->Id > Abc_ObjRegular(p1)->Id ) + pAnd = p0, p0 = p1, p1 = pAnd; + // get the hash key for these two nodes + Key = Abc_HashKey2( p0, p1, pMan->nBins ); + // find the matching node in the table + Abc_AigBinForEachEntry( pMan->pBins[Key], pAnd ) + if ( p0 == Abc_ObjChild0(pAnd) && p1 == Abc_ObjChild1(pAnd) ) + { +// assert( Abc_ObjFanoutNum(Abc_ObjRegular(p0)) && Abc_ObjFanoutNum(p1) ); + return pAnd; + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [Returns the gate implementing EXOR of the two arguments if it exists.] + + Description [The argument nodes can be complemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_AigXorLookup( Abc_Aig_t * pMan, Abc_Obj_t * p0, Abc_Obj_t * p1, int * pType ) +{ + Abc_Obj_t * pNode1, * pNode2, * pNode; + // set the flag to zero + if ( pType ) *pType = 0; + // check the case of XOR(a,b) = OR(ab, a'b')' + if ( (pNode1 = Abc_AigAndLookup(pMan, Abc_ObjNot(p0), Abc_ObjNot(p1))) && + (pNode2 = Abc_AigAndLookup(pMan, p0, p1)) ) + { + pNode = Abc_AigAndLookup( pMan, Abc_ObjNot(pNode1), Abc_ObjNot(pNode2) ); + if ( pNode && pType ) *pType = 1; + return pNode; + } + // check the case of XOR(a,b) = OR(a'b, ab') + if ( (pNode1 = Abc_AigAndLookup(pMan, p0, Abc_ObjNot(p1))) && + (pNode2 = Abc_AigAndLookup(pMan, Abc_ObjNot(p0), p1)) ) + { + pNode = Abc_AigAndLookup( pMan, Abc_ObjNot(pNode1), Abc_ObjNot(pNode2) ); + return pNode? Abc_ObjNot(pNode) : NULL; + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [Returns the gate implementing EXOR of the two arguments if it exists.] + + Description [The argument nodes can be complemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_AigMuxLookup( Abc_Aig_t * pMan, Abc_Obj_t * pC, Abc_Obj_t * pT, Abc_Obj_t * pE, int * pType ) +{ + Abc_Obj_t * pNode1, * pNode2, * pNode; + // set the flag to zero + if ( pType ) *pType = 0; + // check the case of MUX(c,t,e) = OR(ct', c'e')' + if ( (pNode1 = Abc_AigAndLookup(pMan, pC, Abc_ObjNot(pT))) && + (pNode2 = Abc_AigAndLookup(pMan, Abc_ObjNot(pC), Abc_ObjNot(pE))) ) + { + pNode = Abc_AigAndLookup( pMan, Abc_ObjNot(pNode1), Abc_ObjNot(pNode2) ); + if ( pNode && pType ) *pType = 1; + return pNode; + } + // check the case of MUX(c,t,e) = OR(ct, c'e) + if ( (pNode1 = Abc_AigAndLookup(pMan, pC, pT)) && + (pNode2 = Abc_AigAndLookup(pMan, Abc_ObjNot(pC), pE)) ) + { + pNode = Abc_AigAndLookup( pMan, Abc_ObjNot(pNode1), Abc_ObjNot(pNode2) ); + return pNode? Abc_ObjNot(pNode) : NULL; + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [Deletes an AIG node from the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_AigAndDelete( Abc_Aig_t * pMan, Abc_Obj_t * pThis ) +{ + Abc_Obj_t * pAnd, * pAnd0, * pAnd1, ** ppPlace; + unsigned Key; + assert( !Abc_ObjIsComplement(pThis) ); + assert( Abc_ObjIsNode(pThis) ); + assert( Abc_ObjFaninNum(pThis) == 2 ); + assert( pMan->pNtkAig == pThis->pNtk ); + // get the hash key for these two nodes + pAnd0 = Abc_ObjRegular( Abc_ObjChild0(pThis) ); + pAnd1 = Abc_ObjRegular( Abc_ObjChild1(pThis) ); + Key = Abc_HashKey2( Abc_ObjChild0(pThis), Abc_ObjChild1(pThis), pMan->nBins ); + // find the matching node in the table + ppPlace = pMan->pBins + Key; + Abc_AigBinForEachEntry( pMan->pBins[Key], pAnd ) + { + if ( pAnd != pThis ) + { + ppPlace = &pAnd->pNext; + continue; + } + *ppPlace = pAnd->pNext; + break; + } + assert( pAnd == pThis ); + pMan->nEntries--; + // delete the cuts if defined + if ( pThis->pNtk->pManCut ) + Abc_NodeFreeCuts( pThis->pNtk->pManCut, pThis ); +} + +/**Function************************************************************* + + Synopsis [Resizes the hash table of AIG nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_AigResize( Abc_Aig_t * pMan ) +{ + Abc_Obj_t ** pBinsNew; + Abc_Obj_t * pEnt, * pEnt2; + int nBinsNew, Counter, i, clk; + unsigned Key; + +clk = clock(); + // get the new table size + nBinsNew = Cudd_PrimeCopy( 3 * pMan->nBins ); + // allocate a new array + pBinsNew = ALLOC( Abc_Obj_t *, nBinsNew ); + memset( pBinsNew, 0, sizeof(Abc_Obj_t *) * nBinsNew ); + // rehash the entries from the old table + Counter = 0; + for ( i = 0; i < pMan->nBins; i++ ) + Abc_AigBinForEachEntrySafe( pMan->pBins[i], pEnt, pEnt2 ) + { + Key = Abc_HashKey2( Abc_ObjChild0(pEnt), Abc_ObjChild1(pEnt), nBinsNew ); + pEnt->pNext = pBinsNew[Key]; + pBinsNew[Key] = pEnt; + Counter++; + } + assert( Counter == pMan->nEntries ); +// printf( "Increasing the structural table size from %6d to %6d. ", pMan->nBins, nBinsNew ); +// PRT( "Time", clock() - clk ); + // replace the table and the parameters + free( pMan->pBins ); + pMan->pBins = pBinsNew; + pMan->nBins = nBinsNew; +} + +/**Function************************************************************* + + Synopsis [Resizes the hash table of AIG nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_AigRehash( Abc_Aig_t * pMan ) +{ + Abc_Obj_t ** pBinsNew; + Abc_Obj_t * pEnt, * pEnt2; + int * pArray; + unsigned Key; + int Counter, Temp, i; + + // allocate a new array + pBinsNew = ALLOC( Abc_Obj_t *, pMan->nBins ); + memset( pBinsNew, 0, sizeof(Abc_Obj_t *) * pMan->nBins ); + // rehash the entries from the old table + Counter = 0; + for ( i = 0; i < pMan->nBins; i++ ) + Abc_AigBinForEachEntrySafe( pMan->pBins[i], pEnt, pEnt2 ) + { + // swap the fanins if needed + pArray = pEnt->vFanins.pArray; + if ( pArray[0] > pArray[1] ) + { + Temp = pArray[0]; + pArray[0] = pArray[1]; + pArray[1] = Temp; + Temp = pEnt->fCompl0; + pEnt->fCompl0 = pEnt->fCompl1; + pEnt->fCompl1 = Temp; + } + // rehash the node + Key = Abc_HashKey2( Abc_ObjChild0(pEnt), Abc_ObjChild1(pEnt), pMan->nBins ); + pEnt->pNext = pBinsNew[Key]; + pBinsNew[Key] = pEnt; + Counter++; + } + assert( Counter == pMan->nEntries ); + // replace the table and the parameters + free( pMan->pBins ); + pMan->pBins = pBinsNew; +} + + + + + + +/**Function************************************************************* + + Synopsis [Performs canonicization step.] + + Description [The argument nodes can be complemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_AigConst1( Abc_Ntk_t * pNtk ) +{ + assert( Abc_NtkIsStrash(pNtk) ); + return ((Abc_Aig_t *)pNtk->pManFunc)->pConst1; +} + +/**Function************************************************************* + + Synopsis [Performs canonicization step.] + + Description [The argument nodes can be complemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_AigAnd( Abc_Aig_t * pMan, Abc_Obj_t * p0, Abc_Obj_t * p1 ) +{ + Abc_Obj_t * pAnd; + if ( pAnd = Abc_AigAndLookup( pMan, p0, p1 ) ) + return pAnd; + return Abc_AigAndCreate( pMan, p0, p1 ); +} + +/**Function************************************************************* + + Synopsis [Implements Boolean OR.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_AigOr( Abc_Aig_t * pMan, Abc_Obj_t * p0, Abc_Obj_t * p1 ) +{ + return Abc_ObjNot( Abc_AigAnd( pMan, Abc_ObjNot(p0), Abc_ObjNot(p1) ) ); +} + +/**Function************************************************************* + + Synopsis [Implements Boolean XOR.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_AigXor( Abc_Aig_t * pMan, Abc_Obj_t * p0, Abc_Obj_t * p1 ) +{ + return Abc_AigOr( pMan, Abc_AigAnd(pMan, p0, Abc_ObjNot(p1)), + Abc_AigAnd(pMan, p1, Abc_ObjNot(p0)) ); +} + +/**Function************************************************************* + + Synopsis [Implements the miter.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_AigMiter_rec( Abc_Aig_t * pMan, Abc_Obj_t ** ppObjs, int nObjs ) +{ + Abc_Obj_t * pObj1, * pObj2; + if ( nObjs == 1 ) + return ppObjs[0]; + pObj1 = Abc_AigMiter_rec( pMan, ppObjs, nObjs/2 ); + pObj2 = Abc_AigMiter_rec( pMan, ppObjs + nObjs/2, nObjs - nObjs/2 ); + return Abc_AigOr( pMan, pObj1, pObj2 ); +} + +/**Function************************************************************* + + Synopsis [Implements the miter.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_AigMiter( Abc_Aig_t * pMan, Vec_Ptr_t * vPairs ) +{ + int i; + if ( vPairs->nSize == 0 ) + return Abc_ObjNot( Abc_AigConst1(pMan->pNtkAig) ); + assert( vPairs->nSize % 2 == 0 ); + // go through the cubes of the node's SOP + for ( i = 0; i < vPairs->nSize; i += 2 ) + vPairs->pArray[i/2] = Abc_AigXor( pMan, vPairs->pArray[i], vPairs->pArray[i+1] ); + vPairs->nSize = vPairs->nSize/2; + return Abc_AigMiter_rec( pMan, (Abc_Obj_t **)vPairs->pArray, vPairs->nSize ); +} + +/**Function************************************************************* + + Synopsis [Implements the miter.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_AigMiter2( Abc_Aig_t * pMan, Vec_Ptr_t * vPairs ) +{ + Abc_Obj_t * pMiter, * pXor; + int i; + assert( vPairs->nSize % 2 == 0 ); + // go through the cubes of the node's SOP + pMiter = Abc_ObjNot( Abc_AigConst1(pMan->pNtkAig) ); + for ( i = 0; i < vPairs->nSize; i += 2 ) + { + pXor = Abc_AigXor( pMan, vPairs->pArray[i], vPairs->pArray[i+1] ); + pMiter = Abc_AigOr( pMan, pMiter, pXor ); + } + return pMiter; +} + + + + +/**Function************************************************************* + + Synopsis [Replaces one AIG node by the other.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_AigReplace( Abc_Aig_t * pMan, Abc_Obj_t * pOld, Abc_Obj_t * pNew, bool fUpdateLevel ) +{ + assert( Vec_PtrSize(pMan->vStackReplaceOld) == 0 ); + assert( Vec_PtrSize(pMan->vStackReplaceNew) == 0 ); + Vec_PtrPush( pMan->vStackReplaceOld, pOld ); + Vec_PtrPush( pMan->vStackReplaceNew, pNew ); + assert( !Abc_ObjIsComplement(pOld) ); + // create HAIG + if ( pOld->pNtk->pHaig ) + Hop_ObjCreateChoice( pOld->pEquiv, Abc_ObjRegular(pNew)->pEquiv ); + // process the replacements + while ( Vec_PtrSize(pMan->vStackReplaceOld) ) + { + pOld = Vec_PtrPop( pMan->vStackReplaceOld ); + pNew = Vec_PtrPop( pMan->vStackReplaceNew ); + Abc_AigReplace_int( pMan, pOld, pNew, fUpdateLevel ); + } + if ( fUpdateLevel ) + { + Abc_AigUpdateLevel_int( pMan ); + if ( pMan->pNtkAig->vLevelsR ) + Abc_AigUpdateLevelR_int( pMan ); + } +} + +/**Function************************************************************* + + Synopsis [Performs internal replacement step.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_AigReplace_int( Abc_Aig_t * pMan, Abc_Obj_t * pOld, Abc_Obj_t * pNew, int fUpdateLevel ) +{ + Abc_Obj_t * pFanin1, * pFanin2, * pFanout, * pFanoutNew, * pFanoutFanout; + int k, v, iFanin; + // make sure the old node is regular and has fanouts + // (the new node can be complemented and can have fanouts) + assert( !Abc_ObjIsComplement(pOld) ); + assert( Abc_ObjFanoutNum(pOld) > 0 ); + // look at the fanouts of old node + Abc_NodeCollectFanouts( pOld, pMan->vNodes ); + Vec_PtrForEachEntry( pMan->vNodes, pFanout, k ) + { + if ( Abc_ObjIsCo(pFanout) ) + { + Abc_ObjPatchFanin( pFanout, pOld, pNew ); + continue; + } + // find the old node as a fanin of this fanout + iFanin = Vec_IntFind( &pFanout->vFanins, pOld->Id ); + assert( iFanin == 0 || iFanin == 1 ); + // get the new fanin + pFanin1 = Abc_ObjNotCond( pNew, Abc_ObjFaninC(pFanout, iFanin) ); + assert( Abc_ObjRegular(pFanin1) != pFanout ); + // get another fanin + pFanin2 = Abc_ObjChild( pFanout, iFanin ^ 1 ); + assert( Abc_ObjRegular(pFanin2) != pFanout ); + // check if the node with these fanins exists + if ( pFanoutNew = Abc_AigAndLookup( pMan, pFanin1, pFanin2 ) ) + { // such node exists (it may be a constant) + // schedule replacement of the old fanout by the new fanout + Vec_PtrPush( pMan->vStackReplaceOld, pFanout ); + Vec_PtrPush( pMan->vStackReplaceNew, pFanoutNew ); + continue; + } + // such node does not exist - modify the old fanout node + // (this way the change will not propagate all the way to the COs) + assert( Abc_ObjRegular(pFanin1) != Abc_ObjRegular(pFanin2) ); + + // if the node is in the level structure, remove it + if ( pFanout->fMarkA ) + Abc_AigRemoveFromLevelStructure( pMan->vLevels, pFanout ); + // if the node is in the level structure, remove it + if ( pFanout->fMarkB ) + Abc_AigRemoveFromLevelStructureR( pMan->vLevelsR, pFanout ); + + // remove the old fanout node from the structural hashing table + Abc_AigAndDelete( pMan, pFanout ); + // remove the fanins of the old fanout + Abc_ObjRemoveFanins( pFanout ); + // recreate the old fanout with new fanins and add it to the table + Abc_AigAndCreateFrom( pMan, pFanin1, pFanin2, pFanout ); + assert( Abc_AigNodeIsAcyclic(pFanout, pFanout) ); + + if ( fUpdateLevel ) + { + // schedule the updated fanout for updating direct level + assert( pFanout->fMarkA == 0 ); + pFanout->fMarkA = 1; + Vec_VecPush( pMan->vLevels, pFanout->Level, pFanout ); + // schedule the updated fanout for updating reverse level + if ( pMan->pNtkAig->vLevelsR ) + { + assert( pFanout->fMarkB == 0 ); + pFanout->fMarkB = 1; + Vec_VecPush( pMan->vLevelsR, Abc_ObjReverseLevel(pFanout), pFanout ); + } + } + + // the fanout has changed, update EXOR status of its fanouts + Abc_ObjForEachFanout( pFanout, pFanoutFanout, v ) + if ( Abc_AigNodeIsAnd(pFanoutFanout) ) + pFanoutFanout->fExor = Abc_NodeIsExorType(pFanoutFanout); + } + // if the node has no fanouts left, remove its MFFC + if ( Abc_ObjFanoutNum(pOld) == 0 ) + Abc_AigDeleteNode( pMan, pOld ); +} + +/**Function************************************************************* + + Synopsis [Performs internal deletion step.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_AigDeleteNode( Abc_Aig_t * pMan, Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pNode0, * pNode1, * pTemp; + int i, k; + + // make sure the node is regular and dangling + assert( !Abc_ObjIsComplement(pNode) ); + assert( Abc_ObjIsNode(pNode) ); + assert( Abc_ObjFaninNum(pNode) == 2 ); + assert( Abc_ObjFanoutNum(pNode) == 0 ); + + // when deleting an old node that is scheduled for replacement, remove it from the replacement queue + Vec_PtrForEachEntry( pMan->vStackReplaceOld, pTemp, i ) + if ( pNode == pTemp ) + { + // remove the entry from the replacement array + for ( k = i; k < pMan->vStackReplaceOld->nSize - 1; k++ ) + { + pMan->vStackReplaceOld->pArray[k] = pMan->vStackReplaceOld->pArray[k+1]; + pMan->vStackReplaceNew->pArray[k] = pMan->vStackReplaceNew->pArray[k+1]; + } + pMan->vStackReplaceOld->nSize--; + pMan->vStackReplaceNew->nSize--; + } + + // when deleting a new node that should replace another node, do not delete + Vec_PtrForEachEntry( pMan->vStackReplaceNew, pTemp, i ) + if ( pNode == Abc_ObjRegular(pTemp) ) + return; + + // remember the node's fanins + pNode0 = Abc_ObjFanin0( pNode ); + pNode1 = Abc_ObjFanin1( pNode ); + + // add the node to the list of updated nodes + if ( pMan->vUpdatedNets ) + { + Vec_PtrPushUnique( pMan->vUpdatedNets, pNode0 ); + Vec_PtrPushUnique( pMan->vUpdatedNets, pNode1 ); + } + + // remove the node from the table + Abc_AigAndDelete( pMan, pNode ); + // if the node is in the level structure, remove it + if ( pNode->fMarkA ) + Abc_AigRemoveFromLevelStructure( pMan->vLevels, pNode ); + if ( pNode->fMarkB ) + Abc_AigRemoveFromLevelStructureR( pMan->vLevelsR, pNode ); + // remove the node from the network + Abc_NtkDeleteObj( pNode ); + + // call recursively for the fanins + if ( Abc_ObjIsNode(pNode0) && pNode0->vFanouts.nSize == 0 ) + Abc_AigDeleteNode( pMan, pNode0 ); + if ( Abc_ObjIsNode(pNode1) && pNode1->vFanouts.nSize == 0 ) + Abc_AigDeleteNode( pMan, pNode1 ); +} + + +/**Function************************************************************* + + Synopsis [Updates the level of the node after it has changed.] + + Description [This procedure is based on the observation that + after the node's level has changed, the fanouts levels can change too, + but the new fanout levels are always larger than the node's level. + As a result, we can accumulate the nodes to be updated in the queue + and process them in the increasing order of levels.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_AigUpdateLevel_int( Abc_Aig_t * pMan ) +{ + Abc_Obj_t * pNode, * pFanout; + Vec_Ptr_t * vVec; + int LevelNew, i, k, v; + + // go through the nodes and update the level of their fanouts + Vec_VecForEachLevel( pMan->vLevels, vVec, i ) + { + if ( Vec_PtrSize(vVec) == 0 ) + continue; + Vec_PtrForEachEntry( vVec, pNode, k ) + { + if ( pNode == NULL ) + continue; + assert( Abc_ObjIsNode(pNode) ); + assert( (int)pNode->Level == i ); + // clean the mark + assert( pNode->fMarkA == 1 ); + pNode->fMarkA = 0; + // iterate through the fanouts + Abc_ObjForEachFanout( pNode, pFanout, v ) + { + if ( Abc_ObjIsCo(pFanout) ) + continue; + // get the new level of this fanout + LevelNew = 1 + ABC_MAX( Abc_ObjFanin0(pFanout)->Level, Abc_ObjFanin1(pFanout)->Level ); + assert( LevelNew > i ); + if ( (int)pFanout->Level == LevelNew ) // no change + continue; + // if the fanout is present in the data structure, pull it out + if ( pFanout->fMarkA ) + Abc_AigRemoveFromLevelStructure( pMan->vLevels, pFanout ); + // update the fanout level + pFanout->Level = LevelNew; + // add the fanout to the data structure to update its fanouts + assert( pFanout->fMarkA == 0 ); + pFanout->fMarkA = 1; + Vec_VecPush( pMan->vLevels, pFanout->Level, pFanout ); + } + } + Vec_PtrClear( vVec ); + } +} + +/**Function************************************************************* + + Synopsis [Updates the level of the node after it has changed.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_AigUpdateLevelR_int( Abc_Aig_t * pMan ) +{ + Abc_Obj_t * pNode, * pFanin, * pFanout; + Vec_Ptr_t * vVec; + int LevelNew, i, k, v, j; + + // go through the nodes and update the level of their fanouts + Vec_VecForEachLevel( pMan->vLevelsR, vVec, i ) + { + if ( Vec_PtrSize(vVec) == 0 ) + continue; + Vec_PtrForEachEntry( vVec, pNode, k ) + { + if ( pNode == NULL ) + continue; + assert( Abc_ObjIsNode(pNode) ); + assert( Abc_ObjReverseLevel(pNode) == i ); + // clean the mark + assert( pNode->fMarkB == 1 ); + pNode->fMarkB = 0; + // iterate through the fanins + Abc_ObjForEachFanin( pNode, pFanin, v ) + { + if ( Abc_ObjIsCi(pFanin) ) + continue; + // get the new reverse level of this fanin + LevelNew = 0; + Abc_ObjForEachFanout( pFanin, pFanout, j ) + if ( LevelNew < Abc_ObjReverseLevel(pFanout) ) + LevelNew = Abc_ObjReverseLevel(pFanout); + LevelNew += 1; + assert( LevelNew > i ); + if ( Abc_ObjReverseLevel(pFanin) == LevelNew ) // no change + continue; + // if the fanin is present in the data structure, pull it out + if ( pFanin->fMarkB ) + Abc_AigRemoveFromLevelStructureR( pMan->vLevelsR, pFanin ); + // update the reverse level + Abc_ObjSetReverseLevel( pFanin, LevelNew ); + // add the fanin to the data structure to update its fanins + assert( pFanin->fMarkB == 0 ); + pFanin->fMarkB = 1; + Vec_VecPush( pMan->vLevelsR, LevelNew, pFanin ); + } + } + Vec_PtrClear( vVec ); + } +} + +/**Function************************************************************* + + Synopsis [Removes the node from the level structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_AigRemoveFromLevelStructure( Vec_Vec_t * vStruct, Abc_Obj_t * pNode ) +{ + Vec_Ptr_t * vVecTemp; + Abc_Obj_t * pTemp; + int m; + assert( pNode->fMarkA ); + vVecTemp = Vec_VecEntry( vStruct, pNode->Level ); + Vec_PtrForEachEntry( vVecTemp, pTemp, m ) + { + if ( pTemp != pNode ) + continue; + Vec_PtrWriteEntry( vVecTemp, m, NULL ); + break; + } + assert( m < Vec_PtrSize(vVecTemp) ); // found + pNode->fMarkA = 0; +} + +/**Function************************************************************* + + Synopsis [Removes the node from the level structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_AigRemoveFromLevelStructureR( Vec_Vec_t * vStruct, Abc_Obj_t * pNode ) +{ + Vec_Ptr_t * vVecTemp; + Abc_Obj_t * pTemp; + int m; + assert( pNode->fMarkB ); + vVecTemp = Vec_VecEntry( vStruct, Abc_ObjReverseLevel(pNode) ); + Vec_PtrForEachEntry( vVecTemp, pTemp, m ) + { + if ( pTemp != pNode ) + continue; + Vec_PtrWriteEntry( vVecTemp, m, NULL ); + break; + } + assert( m < Vec_PtrSize(vVecTemp) ); // found + pNode->fMarkB = 0; +} + + + + +/**Function************************************************************* + + Synopsis [Returns 1 if the node has at least one complemented fanout.] + + Description [A fanout is complemented if the fanout's fanin edge pointing + to the given node is complemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_AigNodeHasComplFanoutEdge( Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pFanout; + int i, iFanin; + Abc_ObjForEachFanout( pNode, pFanout, i ) + { + iFanin = Vec_IntFind( &pFanout->vFanins, pNode->Id ); + assert( iFanin >= 0 ); + if ( Abc_ObjFaninC( pFanout, iFanin ) ) + return 1; + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the node has at least one complemented fanout.] + + Description [A fanout is complemented if the fanout's fanin edge pointing + to the given node is complemented. Only the fanouts with current TravId + are counted.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_AigNodeHasComplFanoutEdgeTrav( Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pFanout; + int i, iFanin; + Abc_ObjForEachFanout( pNode, pFanout, i ) + { + if ( !Abc_NodeIsTravIdCurrent(pFanout) ) + continue; + iFanin = Vec_IntFind( &pFanout->vFanins, pNode->Id ); + assert( iFanin >= 0 ); + if ( Abc_ObjFaninC( pFanout, iFanin ) ) + return 1; + } + return 0; +} + + +/**Function************************************************************* + + Synopsis [Prints the AIG node for debugging purposes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_AigPrintNode( Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pNodeR = Abc_ObjRegular(pNode); + if ( Abc_ObjIsCi(pNodeR) ) + { + printf( "CI %4s%s.\n", Abc_ObjName(pNodeR), Abc_ObjIsComplement(pNode)? "\'" : "" ); + return; + } + if ( Abc_AigNodeIsConst(pNodeR) ) + { + printf( "Constant 1 %s.\n", Abc_ObjIsComplement(pNode)? "(complemented)" : "" ); + return; + } + // print the node's function + printf( "%7s%s", Abc_ObjName(pNodeR), Abc_ObjIsComplement(pNode)? "\'" : "" ); + printf( " = " ); + printf( "%7s%s", Abc_ObjName(Abc_ObjFanin0(pNodeR)), Abc_ObjFaninC0(pNodeR)? "\'" : "" ); + printf( " * " ); + printf( "%7s%s", Abc_ObjName(Abc_ObjFanin1(pNodeR)), Abc_ObjFaninC1(pNodeR)? "\'" : "" ); + printf( "\n" ); +} + + +/**Function************************************************************* + + Synopsis [Check if the node has a combination loop of depth 1 or 2.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_AigNodeIsAcyclic( Abc_Obj_t * pNode, Abc_Obj_t * pRoot ) +{ + Abc_Obj_t * pFanin0, * pFanin1; + Abc_Obj_t * pChild00, * pChild01; + Abc_Obj_t * pChild10, * pChild11; + if ( !Abc_AigNodeIsAnd(pNode) ) + return 1; + pFanin0 = Abc_ObjFanin0(pNode); + pFanin1 = Abc_ObjFanin1(pNode); + if ( pRoot == pFanin0 || pRoot == pFanin1 ) + return 0; + if ( Abc_ObjIsCi(pFanin0) ) + { + pChild00 = NULL; + pChild01 = NULL; + } + else + { + pChild00 = Abc_ObjFanin0(pFanin0); + pChild01 = Abc_ObjFanin1(pFanin0); + if ( pRoot == pChild00 || pRoot == pChild01 ) + return 0; + } + if ( Abc_ObjIsCi(pFanin1) ) + { + pChild10 = NULL; + pChild11 = NULL; + } + else + { + pChild10 = Abc_ObjFanin0(pFanin1); + pChild11 = Abc_ObjFanin1(pFanin1); + if ( pRoot == pChild10 || pRoot == pChild11 ) + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Resizes the hash table of AIG nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_AigCheckFaninOrder( Abc_Aig_t * pMan ) +{ + Abc_Obj_t * pEnt; + int i; + for ( i = 0; i < pMan->nBins; i++ ) + Abc_AigBinForEachEntry( pMan->pBins[i], pEnt ) + { + if ( Abc_ObjRegular(Abc_ObjChild0(pEnt))->Id > Abc_ObjRegular(Abc_ObjChild1(pEnt))->Id ) + { + int i0 = Abc_ObjRegular(Abc_ObjChild0(pEnt))->Id; + int i1 = Abc_ObjRegular(Abc_ObjChild1(pEnt))->Id; + printf( "Node %d has incorrect ordering of fanins.\n", pEnt->Id ); + } + } +} + +/**Function************************************************************* + + Synopsis [Sets the correct phase of the nodes.] + + Description [The AIG nodes should be in the DFS order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_AigSetNodePhases( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i; + assert( Abc_NtkIsDfsOrdered(pNtk) ); + Abc_AigConst1(pNtk)->fPhase = 1; + Abc_NtkForEachPi( pNtk, pObj, i ) + pObj->fPhase = 0; + Abc_NtkForEachLatchOutput( pNtk, pObj, i ) + pObj->fPhase = Abc_LatchIsInit1(pObj); + Abc_AigForEachAnd( pNtk, pObj, i ) + pObj->fPhase = (Abc_ObjFanin0(pObj)->fPhase ^ Abc_ObjFaninC0(pObj)) & (Abc_ObjFanin1(pObj)->fPhase ^ Abc_ObjFaninC1(pObj)); + Abc_NtkForEachPo( pNtk, pObj, i ) + pObj->fPhase = (Abc_ObjFanin0(pObj)->fPhase ^ Abc_ObjFaninC0(pObj)); + Abc_NtkForEachLatchInput( pNtk, pObj, i ) + pObj->fPhase = (Abc_ObjFanin0(pObj)->fPhase ^ Abc_ObjFaninC0(pObj)); +} + + + +/**Function************************************************************* + + Synopsis [Start the update list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_AigUpdateStart( Abc_Aig_t * pMan, Vec_Ptr_t ** pvUpdatedNets ) +{ + assert( pMan->vAddedCells == NULL ); + pMan->vAddedCells = Vec_PtrAlloc( 1000 ); + pMan->vUpdatedNets = Vec_PtrAlloc( 1000 ); + *pvUpdatedNets = pMan->vUpdatedNets; + return pMan->vAddedCells; +} + +/**Function************************************************************* + + Synopsis [Start the update list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_AigUpdateStop( Abc_Aig_t * pMan ) +{ + assert( pMan->vAddedCells != NULL ); + Vec_PtrFree( pMan->vAddedCells ); + Vec_PtrFree( pMan->vUpdatedNets ); + pMan->vAddedCells = NULL; + pMan->vUpdatedNets = NULL; +} + +/**Function************************************************************* + + Synopsis [Start the update list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_AigUpdateReset( Abc_Aig_t * pMan ) +{ + assert( pMan->vAddedCells != NULL ); + Vec_PtrClear( pMan->vAddedCells ); + Vec_PtrClear( pMan->vUpdatedNets ); +} + +/**Function************************************************************* + + Synopsis [Start the update list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_AigCountNext( Abc_Aig_t * pMan ) +{ + Abc_Obj_t * pAnd; + int i, Counter = 0, CounterTotal = 0; + // count how many nodes have pNext set + for ( i = 0; i < pMan->nBins; i++ ) + Abc_AigBinForEachEntry( pMan->pBins[i], pAnd ) + { + Counter += (pAnd->pNext != NULL); + CounterTotal++; + } + printf( "Counter = %d. Nodes = %d. Ave = %6.2f\n", Counter, CounterTotal, 1.0 * CounterTotal/pMan->nBins ); + return Counter; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abc/abcBlifMv.c b/abc_with_bb_support/src/base/abc/abcBlifMv.c new file mode 100644 index 000000000..0468d4c9f --- /dev/null +++ b/abc_with_bb_support/src/base/abc/abcBlifMv.c @@ -0,0 +1,970 @@ +/**CFile**************************************************************** + + FileName [abcBlifMv.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Procedures to process BLIF-MV networks and AIGs.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcBlifMv.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the Mv-Var manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkStartMvVars( Abc_Ntk_t * pNtk ) +{ + Vec_Att_t * pAttMan; + assert( Abc_NtkMvVar(pNtk) == NULL ); + pAttMan = Vec_AttAlloc( 0, Abc_NtkObjNumMax(pNtk) + 1, Extra_MmFlexStart(), Extra_MmFlexStop, NULL, NULL ); + Vec_PtrWriteEntry( pNtk->vAttrs, VEC_ATTR_MVVAR, pAttMan ); +//printf( "allocing attr\n" ); +} + +/**Function************************************************************* + + Synopsis [Stops the Mv-Var manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkFreeMvVars( Abc_Ntk_t * pNtk ) +{ + void * pUserMan; + pUserMan = Abc_NtkAttrFree( pNtk, VEC_ATTR_GLOBAL_BDD, 0 ); + Extra_MmFlexStop( pUserMan ); +} + +/**Function************************************************************* + + Synopsis [Duplicate the MV variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkSetMvVarValues( Abc_Obj_t * pObj, int nValues ) +{ + Extra_MmFlex_t * pFlex; + struct temp + { + int nValues; + char ** pNames; + } * pVarStruct; + assert( nValues > 1 ); + // skip binary signals + if ( nValues == 2 ) + return; + // skip already assigned signals + if ( Abc_ObjMvVar(pObj) != NULL ) + return; + // create the structure + pFlex = Abc_NtkMvVarMan( pObj->pNtk ); + pVarStruct = (void *)Extra_MmFlexEntryFetch( pFlex, sizeof(struct temp) ); + pVarStruct->nValues = nValues; + pVarStruct->pNames = NULL; + Abc_ObjSetMvVar( pObj, pVarStruct ); +} + +/**Function************************************************************* + + Synopsis [Strashes the BLIF-MV netlist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Abc_StringGetNumber( char ** ppStr ) +{ + char * pStr = *ppStr; + int Number = 0; + assert( *pStr >= '0' && *pStr <= '9' ); + for ( ; *pStr >= '0' && *pStr <= '9'; pStr++ ) + Number = 10 * Number + *pStr - '0'; + *ppStr = pStr; + return Number; +} + +/**Function************************************************************* + + Synopsis [Strashes one node in the BLIF-MV netlist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeStrashBlifMv( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pObj ) +{ + char * pSop; + Abc_Obj_t ** pValues, ** pValuesF, ** pValuesF2; + Abc_Obj_t * pTemp, * pTemp2, * pFanin, * pFanin2, * pNet; + int k, v, Def, DefIndex, Index, nValues, nValuesF, nValuesF2; + + // start the output values + assert( Abc_ObjIsNode(pObj) ); + pNet = Abc_ObjFanout0(pObj); + nValues = Abc_ObjMvVarNum(pNet); + pValues = ALLOC( Abc_Obj_t *, nValues ); + for ( k = 0; k < nValues; k++ ) + pValues[k] = Abc_ObjNot( Abc_AigConst1(pNtkNew) ); + + // get the BLIF-MV formula + pSop = pObj->pData; + // skip the value line +// while ( *pSop++ != '\n' ); + + // handle the constant + if ( Abc_ObjFaninNum(pObj) == 0 ) + { + // skip the default if present + if ( *pSop == 'd' ) + while ( *pSop++ != '\n' ); + // skip space if present + if ( *pSop == ' ' ) + pSop++; + Index = Abc_StringGetNumber( &pSop ); + assert( Index < nValues ); + pValues[Index] = Abc_AigConst1(pNtkNew); + // save the values in the fanout net + pNet->pCopy = (Abc_Obj_t *)pValues; + return 1; + } + + // parse the default line + Def = DefIndex = -1; + if ( *pSop == 'd' ) + { + pSop++; + if ( *pSop == '=' ) + { + pSop++; + DefIndex = Abc_StringGetNumber( &pSop ); + assert( DefIndex < Abc_ObjFaninNum(pObj) ); + } + else if ( *pSop == '-' ) + { + pSop++; + Def = 0; + } + else + { + Def = Abc_StringGetNumber( &pSop ); + assert( Def < nValues ); + } + assert( *pSop == '\n' ); + pSop++; + } + + // convert the values + while ( *pSop ) + { + // extract the values for each cube + pTemp = Abc_AigConst1(pNtkNew); + Abc_ObjForEachFanin( pObj, pFanin, k ) + { + if ( *pSop == '-' ) + { + pSop += 2; + continue; + } + if ( *pSop == '!' ) + { + printf( "Abc_NodeStrashBlifMv(): Cannot handle complement in the MV function of node %s.\n", Abc_ObjName(Abc_ObjFanout0(pObj)) ); + return 0; + } + if ( *pSop == '{' ) + { + printf( "Abc_NodeStrashBlifMv(): Cannot handle braces in the MV function of node %s.\n", Abc_ObjName(Abc_ObjFanout0(pObj)) ); + return 0; + } + // get the value set + nValuesF = Abc_ObjMvVarNum(pFanin); + pValuesF = (Abc_Obj_t **)pFanin->pCopy; + if ( *pSop == '(' ) + { + pSop++; + pTemp2 = Abc_ObjNot( Abc_AigConst1(pNtkNew) ); + while ( *pSop != ')' ) + { + Index = Abc_StringGetNumber( &pSop ); + assert( Index < nValuesF ); + pTemp2 = Abc_AigOr( pNtkNew->pManFunc, pTemp2, pValuesF[Index] ); + assert( *pSop == ')' || *pSop == ',' ); + if ( *pSop == ',' ) + pSop++; + } + assert( *pSop == ')' ); + pSop++; + } + else if ( *pSop == '=' ) + { + pSop++; + // get the fanin index + Index = Abc_StringGetNumber( &pSop ); + assert( Index < Abc_ObjFaninNum(pObj) ); + assert( Index != k ); + // get the fanin + pFanin2 = Abc_ObjFanin( pObj, Index ); + nValuesF2 = Abc_ObjMvVarNum(pFanin2); + pValuesF2 = (Abc_Obj_t **)pFanin2->pCopy; + // create the sum of products of values + assert( nValuesF == nValuesF2 ); + pTemp2 = Abc_ObjNot( Abc_AigConst1(pNtkNew) ); + for ( v = 0; v < nValues; v++ ) + pTemp2 = Abc_AigOr( pNtkNew->pManFunc, pTemp2, Abc_AigAnd(pNtkNew->pManFunc, pValuesF[v], pValuesF2[v]) ); + } + else + { + Index = Abc_StringGetNumber( &pSop ); + assert( Index < nValuesF ); + pTemp2 = pValuesF[Index]; + } + // compute the compute + pTemp = Abc_AigAnd( pNtkNew->pManFunc, pTemp, pTemp2 ); + // advance the reading point + assert( *pSop == ' ' ); + pSop++; + } + // check if the output value is an equal construct + if ( *pSop == '=' ) + { + pSop++; + // get the output value + Index = Abc_StringGetNumber( &pSop ); + assert( Index < Abc_ObjFaninNum(pObj) ); + // add values of the given fanin with the given cube + pFanin = Abc_ObjFanin( pObj, Index ); + nValuesF = Abc_ObjMvVarNum(pFanin); + pValuesF = (Abc_Obj_t **)pFanin->pCopy; + assert( nValuesF == nValues ); // should be guaranteed by the parser + for ( k = 0; k < nValuesF; k++ ) + pValues[k] = Abc_AigOr( pNtkNew->pManFunc, pValues[k], Abc_AigAnd(pNtkNew->pManFunc, pTemp, pValuesF[k]) ); + } + else + { + // get the output value + Index = Abc_StringGetNumber( &pSop ); + assert( Index < nValues ); + pValues[Index] = Abc_AigOr( pNtkNew->pManFunc, pValues[Index], pTemp ); + } + // advance the reading point + assert( *pSop == '\n' ); + pSop++; + } + + // compute the default value + if ( Def >= 0 || DefIndex >= 0 ) + { + pTemp = Abc_AigConst1(pNtkNew); + for ( k = 0; k < nValues; k++ ) + { + if ( k == Def ) + continue; + pTemp = Abc_AigAnd( pNtkNew->pManFunc, pTemp, Abc_ObjNot(pValues[k]) ); + } + + // assign the default value + if ( Def >= 0 ) + pValues[Def] = pTemp; + else + { + assert( DefIndex >= 0 ); + // add values of the given fanin with the given cube + pFanin = Abc_ObjFanin( pObj, DefIndex ); + nValuesF = Abc_ObjMvVarNum(pFanin); + pValuesF = (Abc_Obj_t **)pFanin->pCopy; + assert( nValuesF == nValues ); // should be guaranteed by the parser + for ( k = 0; k < nValuesF; k++ ) + pValues[k] = Abc_AigOr( pNtkNew->pManFunc, pValues[k], Abc_AigAnd(pNtkNew->pManFunc, pTemp, pValuesF[k]) ); + } + + } + + // save the values in the fanout net + pNet->pCopy = (Abc_Obj_t *)pValues; + return 1; +} + +/**Function************************************************************* + + Synopsis [Assigns name with index.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Abc_NtkConvertAssignName( Abc_Obj_t * pObj, Abc_Obj_t * pNet, int Index ) +{ + char Suffix[16]; + assert( Abc_ObjIsTerm(pObj) ); + assert( Abc_ObjIsNet(pNet) ); + sprintf( Suffix, "[%d]", Index ); + Abc_ObjAssignName( pObj, Abc_ObjName(pNet), Suffix ); +} + +/**Function************************************************************* + + Synopsis [Strashes the BLIF-MV netlist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkStrashBlifMv( Abc_Ntk_t * pNtk ) +{ + int fUsePositional = 0; + Vec_Ptr_t * vNodes; + Abc_Obj_t ** pBits; + Abc_Obj_t ** pValues; + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj, * pTemp, * pBit, * pNet; + int i, k, v, nValues, nValuesMax, nBits; + + assert( Abc_NtkIsNetlist(pNtk) ); + assert( Abc_NtkHasBlifMv(pNtk) ); + assert( Abc_NtkWhiteboxNum(pNtk) == 0 ); + assert( Abc_NtkBlackboxNum(pNtk) == 0 ); + + // get the largest number of values + nValuesMax = 2; + Abc_NtkForEachNet( pNtk, pObj, i ) + { + nValues = Abc_ObjMvVarNum(pObj); + if ( nValuesMax < nValues ) + nValuesMax = nValues; + } + nBits = Extra_Base2Log( nValuesMax ); + pBits = ALLOC( Abc_Obj_t *, nBits ); + + // clean the node copy fields + Abc_NtkCleanCopy( pNtk ); + // collect the nodes + vNodes = Abc_NtkDfs( pNtk, 0 ); + + // start the network + pNtkNew = Abc_NtkAlloc( ABC_NTK_STRASH, ABC_FUNC_AIG, 1 ); + // duplicate the name and the spec + pNtkNew->pName = Extra_UtilStrsav( pNtk->pName ); +// pNtkNew->pSpec = Extra_UtilStrsav( pNtk->pName ); + + // encode the CI nets + Abc_NtkIncrementTravId( pNtk ); + if ( fUsePositional ) + { + Abc_NtkForEachCi( pNtk, pObj, i ) + { + pNet = Abc_ObjFanout0(pObj); + nValues = Abc_ObjMvVarNum(pNet); + pValues = ALLOC( Abc_Obj_t *, nValues ); + // create PIs for the values + for ( v = 0; v < nValues; v++ ) + { + pValues[v] = Abc_NtkCreatePi( pNtkNew ); + Abc_NtkConvertAssignName( pValues[v], pNet, v ); + } + // save the values in the fanout net + pNet->pCopy = (Abc_Obj_t *)pValues; + // mark the net + Abc_NodeSetTravIdCurrent( pNet ); + } + } + else + { + Abc_NtkForEachCi( pNtk, pObj, i ) + { + pNet = Abc_ObjFanout0(pObj); + nValues = Abc_ObjMvVarNum(pNet); + pValues = ALLOC( Abc_Obj_t *, nValues ); + // create PIs for the encoding bits + nBits = Extra_Base2Log( nValues ); + for ( k = 0; k < nBits; k++ ) + { + pBits[k] = Abc_NtkCreatePi( pNtkNew ); + Abc_NtkConvertAssignName( pBits[k], pNet, k ); + } + // encode the values + for ( v = 0; v < nValues; v++ ) + { + pValues[v] = Abc_AigConst1(pNtkNew); + for ( k = 0; k < nBits; k++ ) + { + pBit = Abc_ObjNotCond( pBits[k], (v&(1<pManFunc, pValues[v], pBit ); + } + } + // save the values in the fanout net + pNet->pCopy = (Abc_Obj_t *)pValues; + // mark the net + Abc_NodeSetTravIdCurrent( pNet ); + } + } + + // process nodes in the topological order + Vec_PtrForEachEntry( vNodes, pObj, i ) + if ( !Abc_NodeStrashBlifMv( pNtkNew, pObj ) ) + { + Abc_NtkDelete( pNtkNew ); + return NULL; + } + Vec_PtrFree( vNodes ); + + // encode the CO nets + if ( fUsePositional ) + { + Abc_NtkForEachCo( pNtk, pObj, i ) + { + pNet = Abc_ObjFanin0(pObj); + // skip marked nets + if ( Abc_NodeIsTravIdCurrent(pNet) ) + continue; + Abc_NodeSetTravIdCurrent( pNet ); + nValues = Abc_ObjMvVarNum(pNet); + pValues = (Abc_Obj_t **)pNet->pCopy; + for ( v = 0; v < nValues; v++ ) + { + pTemp = Abc_NtkCreatePo( pNtkNew ); + Abc_ObjAddFanin( pTemp, pValues[v] ); + Abc_NtkConvertAssignName( pTemp, pNet, v ); + } + } + } + else + { + Abc_NtkForEachCo( pNtk, pObj, i ) + { + pNet = Abc_ObjFanin0(pObj); + // skip marked nets + if ( Abc_NodeIsTravIdCurrent(pNet) ) + continue; + Abc_NodeSetTravIdCurrent( pNet ); + nValues = Abc_ObjMvVarNum(pNet); + pValues = (Abc_Obj_t **)pNet->pCopy; + nBits = Extra_Base2Log( nValues ); + for ( k = 0; k < nBits; k++ ) + { + pBit = Abc_ObjNot( Abc_AigConst1(pNtkNew) ); + for ( v = 0; v < nValues; v++ ) + if ( v & (1<pManFunc, pBit, pValues[v] ); + pTemp = Abc_NtkCreatePo( pNtkNew ); + Abc_ObjAddFanin( pTemp, pBit ); + Abc_NtkConvertAssignName( pTemp, pNet, k ); + } + } + } + + // cleanup + free( pBits ); + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( pObj->pCopy ) + free( pObj->pCopy ); + + // remove dangling nodes + i = Abc_AigCleanup(pNtkNew->pManFunc); +// printf( "Cleanup removed %d nodes.\n", i ); +// Abc_NtkReassignIds( pNtkNew ); + + // check integrity + if ( !Abc_NtkCheck( pNtkNew ) ) + { + fprintf( stdout, "Abc_NtkStrashBlifMv(): Network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Extract the MV-skeleton of the BLIF-MV network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkSkeletonBlifMv( Abc_Ntk_t * pNtk ) +{ + int fUsePositional = 0; + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj, * pNet, * pNetNew, * pNodeNew, * pTermNew, * pBoxNew; + int i, k, v, nValues, nBits; + + assert( Abc_NtkIsNetlist(pNtk) ); + assert( Abc_NtkHasBlifMv(pNtk) ); + assert( Abc_NtkWhiteboxNum(pNtk) == 0 ); + assert( Abc_NtkBlackboxNum(pNtk) == 0 ); + + // clean the node copy fields + Abc_NtkCleanCopy( pNtk ); + + // start the network + pNtkNew = Abc_NtkAlloc( pNtk->ntkType, pNtk->ntkFunc, 1 ); + // duplicate the name and the spec + pNtkNew->pName = Extra_UtilStrsav( pNtk->pName ); + pNtkNew->pSpec = Extra_UtilStrsav( pNtk->pName ); + // create the internal box (it is important to put it first!) + pBoxNew = Abc_NtkCreateWhitebox( pNtkNew ); + // create PIs and their nets + Abc_NtkForEachPi( pNtk, pObj, i ) + { + Abc_NtkDupObj( pNtkNew, pObj, 0 ); + pNet = Abc_ObjFanout0(pObj); + Abc_NtkDupObj( pNtkNew, pNet, 1 ); + Abc_ObjAddFanin( pNet->pCopy, pObj->pCopy ); + } + // create POs and their nets + Abc_NtkForEachPo( pNtk, pObj, i ) + { + Abc_NtkDupObj( pNtkNew, pObj, 0 ); + pNet = Abc_ObjFanin0(pObj); + if ( pNet->pCopy == NULL ) + Abc_NtkDupObj( pNtkNew, pNet, 1 ); + Abc_ObjAddFanin( pObj->pCopy, pNet->pCopy ); + } + // create latches + Abc_NtkForEachLatch( pNtk, pObj, i ) + { + Abc_NtkDupBox( pNtkNew, pObj, 0 ); + // latch outputs + pNet = Abc_ObjFanout0(Abc_ObjFanout0(pObj)); + assert( pNet->pCopy == NULL ); + Abc_NtkDupObj( pNtkNew, pNet, 1 ); + Abc_ObjAddFanin( pNet->pCopy, Abc_ObjFanout0(pObj)->pCopy ); + // latch inputs + pNet = Abc_ObjFanin0(Abc_ObjFanin0(pObj)); + if ( pNet->pCopy == NULL ) + Abc_NtkDupObj( pNtkNew, pNet, 1 ); + Abc_ObjAddFanin( Abc_ObjFanin0(pObj)->pCopy, pNet->pCopy ); + } + + // encode the CI nets + Abc_NtkIncrementTravId( pNtk ); + if ( fUsePositional ) + { + Abc_NtkForEachCi( pNtk, pObj, i ) + { + pNet = Abc_ObjFanout0(pObj); + nValues = Abc_ObjMvVarNum(pNet); + for ( v = 0; v < nValues; v++ ) + { + pNodeNew = Abc_NtkCreateNode( pNtkNew ); + pNodeNew->pData = Abc_SopEncoderPos( pNtkNew->pManFunc, v, nValues ); + pNetNew = Abc_NtkCreateNet( pNtkNew ); + pTermNew = Abc_NtkCreateBi( pNtkNew ); + Abc_ObjAddFanin( pNodeNew, pNet->pCopy ); + Abc_ObjAddFanin( pNetNew, pNodeNew ); + Abc_ObjAddFanin( pTermNew, pNetNew ); + Abc_ObjAddFanin( pBoxNew, pTermNew ); + } + // mark the net + Abc_NodeSetTravIdCurrent( pNet ); + } + } + else + { + Abc_NtkForEachCi( pNtk, pObj, i ) + { + pNet = Abc_ObjFanout0(pObj); + nValues = Abc_ObjMvVarNum(pNet); + nBits = Extra_Base2Log( nValues ); + for ( k = 0; k < nBits; k++ ) + { + pNodeNew = Abc_NtkCreateNode( pNtkNew ); + pNodeNew->pData = Abc_SopEncoderLog( pNtkNew->pManFunc, k, nValues ); + pNetNew = Abc_NtkCreateNet( pNtkNew ); + pTermNew = Abc_NtkCreateBi( pNtkNew ); + Abc_ObjAddFanin( pNodeNew, pNet->pCopy ); + Abc_ObjAddFanin( pNetNew, pNodeNew ); + Abc_ObjAddFanin( pTermNew, pNetNew ); + Abc_ObjAddFanin( pBoxNew, pTermNew ); + } + // mark the net + Abc_NodeSetTravIdCurrent( pNet ); + } + } + + // encode the CO nets + if ( fUsePositional ) + { + Abc_NtkForEachCo( pNtk, pObj, i ) + { + pNet = Abc_ObjFanin0(pObj); + // skip marked nets + if ( Abc_NodeIsTravIdCurrent(pNet) ) + continue; + Abc_NodeSetTravIdCurrent( pNet ); + nValues = Abc_ObjMvVarNum(pNet); + pNodeNew = Abc_NtkCreateNode( pNtkNew ); + pNodeNew->pData = Abc_SopDecoderPos( pNtkNew->pManFunc, nValues ); + for ( v = 0; v < nValues; v++ ) + { + pTermNew = Abc_NtkCreateBo( pNtkNew ); + pNetNew = Abc_NtkCreateNet( pNtkNew ); + Abc_ObjAddFanin( pTermNew, pBoxNew ); + Abc_ObjAddFanin( pNetNew, pTermNew ); + Abc_ObjAddFanin( pNodeNew, pNetNew ); + } + Abc_ObjAddFanin( pNet->pCopy, pNodeNew ); + } + } + else + { + Abc_NtkForEachCo( pNtk, pObj, i ) + { + pNet = Abc_ObjFanin0(pObj); + // skip marked nets + if ( Abc_NodeIsTravIdCurrent(pNet) ) + continue; + Abc_NodeSetTravIdCurrent( pNet ); + nValues = Abc_ObjMvVarNum(pNet); + nBits = Extra_Base2Log( nValues ); + pNodeNew = Abc_NtkCreateNode( pNtkNew ); + pNodeNew->pData = Abc_SopDecoderLog( pNtkNew->pManFunc, nValues ); + for ( k = 0; k < nBits; k++ ) + { + pTermNew = Abc_NtkCreateBo( pNtkNew ); + pNetNew = Abc_NtkCreateNet( pNtkNew ); + Abc_ObjAddFanin( pTermNew, pBoxNew ); + Abc_ObjAddFanin( pNetNew, pTermNew ); + Abc_ObjAddFanin( pNodeNew, pNetNew ); + } + Abc_ObjAddFanin( pNet->pCopy, pNodeNew ); + } + } + + // if it is a BLIF-MV netlist transfer the values of all nets + if ( Abc_NtkHasBlifMv(pNtk) && Abc_NtkMvVar(pNtk) ) + { + if ( Abc_NtkMvVar( pNtkNew ) == NULL ) + Abc_NtkStartMvVars( pNtkNew ); + Abc_NtkForEachNet( pNtk, pObj, i ) + if ( pObj->pCopy ) + Abc_NtkSetMvVarValues( pObj->pCopy, Abc_ObjMvVarNum(pObj) ); + } + + // check integrity + if ( !Abc_NtkCheck( pNtkNew ) ) + { + fprintf( stdout, "Abc_NtkSkeletonBlifMv(): Network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Inserts processed network into original base MV network.] + + Description [The original network remembers the interface of combinational + logic (PIs/POs/latches names and values). The processed network may + be binary or multi-valued (currently, multi-value is not supported). + The resulting network has the same interface as the original network + while the internal logic is the same as that of the processed network.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkInsertBlifMv( Abc_Ntk_t * pNtkBase, Abc_Ntk_t * pNtkLogic ) +{ + Abc_Ntk_t * pNtkSkel, * pNtkNew; + Abc_Obj_t * pBox; + + assert( Abc_NtkIsNetlist(pNtkBase) ); + assert( Abc_NtkHasBlifMv(pNtkBase) ); + assert( Abc_NtkWhiteboxNum(pNtkBase) == 0 ); + assert( Abc_NtkBlackboxNum(pNtkBase) == 0 ); + + assert( Abc_NtkIsNetlist(pNtkLogic) ); + assert( Abc_NtkHasBlifMv(pNtkLogic) ); + assert( Abc_NtkWhiteboxNum(pNtkLogic) == 0 ); + assert( Abc_NtkBlackboxNum(pNtkLogic) == 0 ); + + // extract the skeleton of the old network + pNtkSkel = Abc_NtkSkeletonBlifMv( pNtkBase ); + + // set the implementation of the box to be the same as the processed network + assert( Abc_NtkWhiteboxNum(pNtkSkel) == 1 ); + pBox = Abc_NtkBox( pNtkSkel, 0 ); + assert( Abc_ObjIsWhitebox(pBox) ); + assert( pBox->pData == NULL ); + assert( Abc_ObjFaninNum(pBox) == Abc_NtkPiNum(pNtkLogic) ); + assert( Abc_ObjFanoutNum(pBox) == Abc_NtkPoNum(pNtkLogic) ); + pBox->pData = pNtkLogic; + + // flatten the hierarchy to insert the processed network + pNtkNew = Abc_NtkFlattenLogicHierarchy( pNtkSkel ); + pBox->pData = NULL; + Abc_NtkDelete( pNtkSkel ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Converts SOP netlist into BLIF-MV netlist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkConvertToBlifMv( Abc_Ntk_t * pNtk ) +{ + Extra_MmFlex_t * pMmFlex; + Abc_Obj_t * pNode; + Vec_Str_t * vCube; + char * pSop0, * pSop1, * pBlifMv, * pCube, * pCur; + int Value, nCubes, nSize, i, k; + + assert( Abc_NtkIsNetlist(pNtk) ); + if ( !Abc_NtkToBdd(pNtk) ) + { + printf( "Converting logic functions to BDDs has failed.\n" ); + return 0; + } + + pMmFlex = Extra_MmFlexStart(); + vCube = Vec_StrAlloc( 100 ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + // convert BDD into cubes for on-set and off-set + Abc_NodeBddToCnf( pNode, pMmFlex, vCube, 0, &pSop0, &pSop1 ); + // allocate room for the MV-SOP + nCubes = Abc_SopGetCubeNum(pSop0) + Abc_SopGetCubeNum(pSop1); + nSize = nCubes*(2*Abc_ObjFaninNum(pNode) + 2)+1; + pBlifMv = Extra_MmFlexEntryFetch( pMmFlex, nSize ); + // add the cubes + pCur = pBlifMv; + Abc_SopForEachCube( pSop0, Abc_ObjFaninNum(pNode), pCube ) + { + Abc_CubeForEachVar( pCube, Value, k ) + { + *pCur++ = Value; + *pCur++ = ' '; + } + *pCur++ = '0'; + *pCur++ = '\n'; + } + Abc_SopForEachCube( pSop1, Abc_ObjFaninNum(pNode), pCube ) + { + Abc_CubeForEachVar( pCube, Value, k ) + { + *pCur++ = Value; + *pCur++ = ' '; + } + *pCur++ = '1'; + *pCur++ = '\n'; + } + *pCur++ = 0; + assert( pCur - pBlifMv == nSize ); + // update the node representation + Cudd_RecursiveDeref( pNtk->pManFunc, pNode->pData ); + pNode->pData = pBlifMv; + } + + // update the functionality type + pNtk->ntkFunc = ABC_FUNC_BLIFMV; + Cudd_Quit( pNtk->pManFunc ); + pNtk->pManFunc = pMmFlex; + + Vec_StrFree( vCube ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Converts SOP into MV-SOP.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_NodeConvertSopToMvSop( int nVars, Vec_Int_t * vSop0, Vec_Int_t * vSop1 ) +{ + char * pMvSop, * pCur; + unsigned uCube; + int nCubes, nSize, Value, i, k; + // consider the case of the constant node + if ( Vec_IntSize(vSop0) == 0 || Vec_IntSize(vSop1) == 0 ) + { + // (temporary) create a tautology cube + pMvSop = ALLOC( char, nVars + 3 ); + for ( k = 0; k < nVars; k++ ) + pMvSop[k] = '-'; + pMvSop[nVars] = '0' + (int)(Vec_IntSize(vSop1) > 0); + pMvSop[nVars+1] = '\n'; + pMvSop[nVars+2] = 0; + return pMvSop; + } + // find the total number of cubes + nCubes = Vec_IntSize(vSop0) + Vec_IntSize(vSop1); + // find the size of the MVSOP represented as a C-string + // (each cube has nVars variables + one output literal + end-of-line, + // and the string is zero-terminated) + nSize = nCubes * (nVars + 2) + 1; + // allocate memory + pMvSop = pCur = ALLOC( char, nSize ); + // fill in the negative polarity cubes + Vec_IntForEachEntry( vSop0, uCube, i ) + { + for ( k = 0; k < nVars; k++ ) + { + Value = (uCube >> (2*k)) & 3; + if ( Value == 1 ) + *pCur++ = '0'; + else if ( Value == 2 ) + *pCur++ = '1'; + else if ( Value == 0 ) + *pCur++ = '-'; + else + assert( 0 ); + } + *pCur++ = '0'; + *pCur++ = '\n'; + } + // fill in the positive polarity cubes + Vec_IntForEachEntry( vSop1, uCube, i ) + { + for ( k = 0; k < nVars; k++ ) + { + Value = (uCube >> (2*k)) & 3; + if ( Value == 1 ) + *pCur++ = '0'; + else if ( Value == 2 ) + *pCur++ = '1'; + else if ( Value == 0 ) + *pCur++ = '-'; + else + assert( 0 ); + } + *pCur++ = '1'; + *pCur++ = '\n'; + } + *pCur++ = 0; + assert( pCur - pMvSop == nSize ); + return pMvSop; +} + + +/**Function************************************************************* + + Synopsis [A prototype of internal cost evaluation procedure.] + + Description [This procedure takes the number of variables (nVars), + the array of values of the inputs and the output (pVarValues) + (note that this array has nVars+1 entries), and an MV-SOP represented + as a C-string with one charater for each literal, including inputs + and output. Each cube is terminated with the new-line character ('\n'). + The string is zero-terminated.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeEvalMvCostInternal( int nVars, int * pVarValues, char * pMvSop ) +{ + // for now, return the number of cubes in the MV-SOP + int Counter = 0; + while ( *pMvSop ) Counter += (*pMvSop++ == '\n'); + return Counter; +} + + +/**Function************************************************************* + + Synopsis [Evaluates the cost of the cut.] + + Description [The Boolean function of the cut is specified by two SOPs, + which represent the negative/positive polarities of the cut function. + Converts these two SOPs into a mutually-agreed-upon representation + to be passed to the internal cost-evaluation procedure (see the above + prototype Abc_NodeEvalMvCostInternal).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeEvalMvCost( int nVars, Vec_Int_t * vSop0, Vec_Int_t * vSop1 ) +{ + char * pMvSop; + int * pVarValues; + int i, RetValue; + // collect the input and output values (currently, they are binary) + pVarValues = ALLOC( int, nVars + 1 ); + for ( i = 0; i <= nVars; i++ ) + pVarValues[i] = 2; + // prepare MV-SOP for evaluation + pMvSop = Abc_NodeConvertSopToMvSop( nVars, vSop0, vSop1 ); + // have a look at the MV-SOP: +// printf( "%s\n", pMvSop ); + // get the result of internal cost evaluation + RetValue = Abc_NodeEvalMvCostInternal( nVars, pVarValues, pMvSop ); + // cleanup + free( pVarValues ); + free( pMvSop ); + return RetValue; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abc/abcCheck.c b/abc_with_bb_support/src/base/abc/abcCheck.c new file mode 100644 index 000000000..0d346f2b8 --- /dev/null +++ b/abc_with_bb_support/src/base/abc/abcCheck.c @@ -0,0 +1,943 @@ +/**CFile**************************************************************** + + FileName [abcCheck.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Consistency checking procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcCheck.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "main.h" +//#include "seq.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static bool Abc_NtkCheckNames( Abc_Ntk_t * pNtk ); +static bool Abc_NtkCheckPis( Abc_Ntk_t * pNtk ); +static bool Abc_NtkCheckPos( Abc_Ntk_t * pNtk ); +//static bool Abc_NtkCheckObj( Abc_Ntk_t * pNtk, Abc_Obj_t * pObj ); +static bool Abc_NtkCheckNet( Abc_Ntk_t * pNtk, Abc_Obj_t * pNet ); +static bool Abc_NtkCheckNode( Abc_Ntk_t * pNtk, Abc_Obj_t * pNode ); +static bool Abc_NtkCheckLatch( Abc_Ntk_t * pNtk, Abc_Obj_t * pLatch ); + +static bool Abc_NtkComparePis( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int fComb ); +static bool Abc_NtkComparePos( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int fComb ); +static bool Abc_NtkCompareLatches( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int fComb ); + +static inline char * Abc_ObjNameNet( Abc_Obj_t * pObj ) { return (Abc_ObjIsNode(pObj) && Abc_NtkIsNetlist(pObj->pNtk)) ? Abc_ObjName(Abc_ObjFanout0(pObj)) : Abc_ObjName(pObj); } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Checks the integrity of the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkCheck( Abc_Ntk_t * pNtk ) +{ + return !Abc_FrameIsFlagEnabled( "check" ) || Abc_NtkDoCheck( pNtk ); +} + +/**Function************************************************************* + + Synopsis [Checks the integrity of the network after reading.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkCheckRead( Abc_Ntk_t * pNtk ) +{ + return !Abc_FrameIsFlagEnabled( "checkread" ) || Abc_NtkDoCheck( pNtk ); +} + +/**Function************************************************************* + + Synopsis [Checks the integrity of the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkDoCheck( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj, * pNet, * pNode; + int i; + + // check network types + if ( !Abc_NtkIsNetlist(pNtk) && !Abc_NtkIsLogic(pNtk) && !Abc_NtkIsStrash(pNtk) ) + { + fprintf( stdout, "NetworkCheck: Unknown network type.\n" ); + return 0; + } + if ( !Abc_NtkHasSop(pNtk) && !Abc_NtkHasBdd(pNtk) && !Abc_NtkHasAig(pNtk) && !Abc_NtkHasMapping(pNtk) && !Abc_NtkHasBlifMv(pNtk) && !Abc_NtkHasBlackbox(pNtk) ) + { + fprintf( stdout, "NetworkCheck: Unknown functionality type.\n" ); + return 0; + } + if ( Abc_NtkHasMapping(pNtk) ) + { + if ( pNtk->pManFunc != Abc_FrameReadLibGen() ) + { + fprintf( stdout, "NetworkCheck: The library of the mapped network is not the global library.\n" ); + return 0; + } + } + + if ( Abc_NtkHasOnlyLatchBoxes(pNtk) ) + { + // check CI/CO numbers + if ( Abc_NtkPiNum(pNtk) + Abc_NtkLatchNum(pNtk) != Abc_NtkCiNum(pNtk) ) + { + fprintf( stdout, "NetworkCheck: Number of CIs does not match number of PIs and latches.\n" ); + fprintf( stdout, "One possible reason is that latches are added twice:\n" ); + fprintf( stdout, "in procedure Abc_NtkCreateObj() and in the user's code.\n" ); + return 0; + } + if ( Abc_NtkPoNum(pNtk) + Abc_NtkAssertNum(pNtk) + Abc_NtkLatchNum(pNtk) != Abc_NtkCoNum(pNtk) ) + { + fprintf( stdout, "NetworkCheck: Number of COs does not match number of POs, asserts, and latches.\n" ); + fprintf( stdout, "One possible reason is that latches are added twice:\n" ); + fprintf( stdout, "in procedure Abc_NtkCreateObj() and in the user's code.\n" ); + return 0; + } + } + + // check the names + if ( !Abc_NtkCheckNames( pNtk ) ) + return 0; + + // check PIs and POs + Abc_NtkCleanCopy( pNtk ); + if ( !Abc_NtkCheckPis( pNtk ) ) + return 0; + if ( !Abc_NtkCheckPos( pNtk ) ) + return 0; + + if ( Abc_NtkHasBlackbox(pNtk) ) + return 1; + + // check the connectivity of objects + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( !Abc_NtkCheckObj( pNtk, pObj ) ) + return 0; + + // if it is a netlist change nets and latches + if ( Abc_NtkIsNetlist(pNtk) ) + { + if ( Abc_NtkNetNum(pNtk) == 0 ) + { + fprintf( stdout, "NetworkCheck: Netlist has no nets.\n" ); + return 0; + } + // check the nets + Abc_NtkForEachNet( pNtk, pNet, i ) + if ( !Abc_NtkCheckNet( pNtk, pNet ) ) + return 0; + } + else + { + if ( Abc_NtkNetNum(pNtk) != 0 ) + { + fprintf( stdout, "NetworkCheck: A network that is not a netlist has nets.\n" ); + return 0; + } + } + + // check the nodes + if ( Abc_NtkIsStrash(pNtk) ) + Abc_AigCheck( pNtk->pManFunc ); + else + { + Abc_NtkForEachNode( pNtk, pNode, i ) + if ( !Abc_NtkCheckNode( pNtk, pNode ) ) + return 0; + } + + // check the latches + Abc_NtkForEachLatch( pNtk, pNode, i ) + if ( !Abc_NtkCheckLatch( pNtk, pNode ) ) + return 0; + + // finally, check for combinational loops +// clk = clock(); + if ( !Abc_NtkIsAcyclic( pNtk ) ) + { + fprintf( stdout, "NetworkCheck: Network contains a combinational loop.\n" ); + return 0; + } +// PRT( "Acyclic ", clock() - clk ); + + // check the EXDC network if present + if ( pNtk->pExdc ) + Abc_NtkCheck( pNtk->pExdc ); +/* + // check the hierarchy + if ( Abc_NtkIsNetlist(pNtk) && pNtk->tName2Model ) + { + stmm_generator * gen; + Abc_Ntk_t * pNtkTemp; + char * pName; + // check other networks + stmm_foreach_item( pNtk->tName2Model, gen, &pName, (char **)&pNtkTemp ) + { + pNtkTemp->fHiePath = pNtkTemp->fHieVisited = 0; + if ( !Abc_NtkCheck( pNtkTemp ) ) + return 0; + } + // check acyclic dependency of the models + if ( !Abc_NtkIsAcyclicHierarchy( pNtk ) ) + { + fprintf( stdout, "NetworkCheck: Network hierarchical dependences contains a cycle.\n" ); + return 0; + } + } +*/ + return 1; +} + +/**Function************************************************************* + + Synopsis [Checks the names.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkCheckNames( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + Vec_Int_t * vNameIds; + char * pName; + int i, NameId; + + if ( Abc_NtkIsNetlist(pNtk) ) + return 1; + + // check that each CI/CO has a name + Abc_NtkForEachCi( pNtk, pObj, i ) + { + pObj = Abc_ObjFanout0Ntk(pObj); + if ( Nm_ManFindNameById(pObj->pNtk->pManName, pObj->Id) == NULL ) + { + fprintf( stdout, "NetworkCheck: CI with ID %d is in the network but not in the name table.\n", pObj->Id ); + return 0; + } + } + Abc_NtkForEachCo( pNtk, pObj, i ) + { + pObj = Abc_ObjFanin0Ntk(pObj); + if ( Nm_ManFindNameById(pObj->pNtk->pManName, pObj->Id) == NULL ) + { + fprintf( stdout, "NetworkCheck: CO with ID %d is in the network but not in the name table.\n", pObj->Id ); + return 0; + } + } + + // return the array of all IDs, which have names + vNameIds = Nm_ManReturnNameIds( pNtk->pManName ); + // make sure that these IDs correspond to live objects + Vec_IntForEachEntry( vNameIds, NameId, i ) + { + if ( Vec_PtrEntry( pNtk->vObjs, NameId ) == NULL ) + { + Vec_IntFree( vNameIds ); + pName = Nm_ManFindNameById(pObj->pNtk->pManName, NameId); + fprintf( stdout, "NetworkCheck: Object with ID %d is deleted but its name \"%s\" remains in the name table.\n", NameId, pName ); + return 0; + } + } + Vec_IntFree( vNameIds ); + + // make sure the CI names are unique + if ( !Abc_NtkCheckUniqueCiNames(pNtk) ) + return 0; + + // make sure the CO names are unique + if ( !Abc_NtkCheckUniqueCoNames(pNtk) ) + return 0; + + // make sure that if a CO has the same name as a CI, they point directly + if ( !Abc_NtkCheckUniqueCioNames(pNtk) ) + return 0; + + return 1; +} + + +/**Function************************************************************* + + Synopsis [Checks the PIs of the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkCheckPis( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i; + + // check that PIs are indeed PIs + Abc_NtkForEachPi( pNtk, pObj, i ) + { + if ( !Abc_ObjIsPi(pObj) ) + { + fprintf( stdout, "NetworkCheck: Object \"%s\" (id=%d) is in the PI list but is not a PI.\n", Abc_ObjName(pObj), pObj->Id ); + return 0; + } + if ( pObj->pData ) + { + fprintf( stdout, "NetworkCheck: A PI \"%s\" has a logic function.\n", Abc_ObjName(pObj) ); + return 0; + } + if ( Abc_ObjFaninNum(pObj) > 0 ) + { + fprintf( stdout, "NetworkCheck: A PI \"%s\" has fanins.\n", Abc_ObjName(pObj) ); + return 0; + } + pObj->pCopy = (Abc_Obj_t *)1; + } + Abc_NtkForEachObj( pNtk, pObj, i ) + { + if ( pObj->pCopy == NULL && Abc_ObjIsPi(pObj) ) + { + fprintf( stdout, "NetworkCheck: Object \"%s\" (id=%d) is a PI but is not in the PI list.\n", Abc_ObjName(pObj), pObj->Id ); + return 0; + } + pObj->pCopy = NULL; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Checks the POs of the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkCheckPos( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i; + + // check that POs are indeed POs + Abc_NtkForEachPo( pNtk, pObj, i ) + { + if ( !Abc_ObjIsPo(pObj) ) + { + fprintf( stdout, "NetworkCheck: Net \"%s\" (id=%d) is in the PO list but is not a PO.\n", Abc_ObjName(pObj), pObj->Id ); + return 0; + } + if ( pObj->pData ) + { + fprintf( stdout, "NetworkCheck: A PO \"%s\" has a logic function.\n", Abc_ObjName(pObj) ); + return 0; + } + if ( Abc_ObjFaninNum(pObj) != 1 ) + { + fprintf( stdout, "NetworkCheck: A PO \"%s\" does not have one fanin.\n", Abc_ObjName(pObj) ); + return 0; + } + if ( Abc_ObjFanoutNum(pObj) > 0 ) + { + fprintf( stdout, "NetworkCheck: A PO \"%s\" has fanouts.\n", Abc_ObjName(pObj) ); + return 0; + } + pObj->pCopy = (Abc_Obj_t *)1; + } + Abc_NtkForEachObj( pNtk, pObj, i ) + { + if ( pObj->pCopy == NULL && Abc_ObjIsPo(pObj) ) + { + fprintf( stdout, "NetworkCheck: Net \"%s\" (id=%d) is in a PO but is not in the PO list.\n", Abc_ObjName(pObj), pObj->Id ); + return 0; + } + pObj->pCopy = NULL; + } + return 1; +} + + +/**Function************************************************************* + + Synopsis [Checks the connectivity of the object.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkCheckObj( Abc_Ntk_t * pNtk, Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanin, * pFanout; + int i, Value = 1; + int k; + + // check the network + if ( pObj->pNtk != pNtk ) + { + fprintf( stdout, "NetworkCheck: Object \"%s\" does not belong to the network.\n", Abc_ObjName(pObj) ); + return 0; + } + // check the object ID + if ( pObj->Id < 0 || (int)pObj->Id >= Abc_NtkObjNumMax(pNtk) ) + { + fprintf( stdout, "NetworkCheck: Object \"%s\" has incorrect ID.\n", Abc_ObjName(pObj) ); + return 0; + } + + if ( !Abc_FrameIsFlagEnabled("checkfio") ) + return Value; + + // go through the fanins of the object and make sure fanins have this object as a fanout + Abc_ObjForEachFanin( pObj, pFanin, i ) + { + if ( Vec_IntFind( &pFanin->vFanouts, pObj->Id ) == -1 ) + { + fprintf( stdout, "NodeCheck: Object \"%s\" has fanin ", Abc_ObjName(pObj) ); + fprintf( stdout, "\"%s\" but the fanin does not have it as a fanout.\n", Abc_ObjName(pFanin) ); + Value = 0; + } + } + // go through the fanouts of the object and make sure fanouts have this object as a fanin + Abc_ObjForEachFanout( pObj, pFanout, i ) + { + if ( Vec_IntFind( &pFanout->vFanins, pObj->Id ) == -1 ) + { + fprintf( stdout, "NodeCheck: Object \"%s\" has fanout ", Abc_ObjName(pObj) ); + fprintf( stdout, "\"%s\" but the fanout does not have it as a fanin.\n", Abc_ObjName(pFanout) ); + Value = 0; + } + } + + // make sure fanins are not duplicated + for ( i = 0; i < pObj->vFanins.nSize; i++ ) + for ( k = i + 1; k < pObj->vFanins.nSize; k++ ) + if ( pObj->vFanins.pArray[k] == pObj->vFanins.pArray[i] ) + { + printf( "Warning: Node %s has", Abc_ObjName(pObj) ); + printf( " duplicated fanin %s.\n", Abc_ObjName(Abc_ObjFanin(pObj,k)) ); + } + + // save time: do not check large fanout lists + if ( pObj->vFanouts.nSize > 100 ) + return Value; + + // make sure fanouts are not duplicated + for ( i = 0; i < pObj->vFanouts.nSize; i++ ) + for ( k = i + 1; k < pObj->vFanouts.nSize; k++ ) + if ( pObj->vFanouts.pArray[k] == pObj->vFanouts.pArray[i] ) + { + printf( "Warning: Node %s has", Abc_ObjName(pObj) ); + printf( " duplicated fanout %s.\n", Abc_ObjName(Abc_ObjFanout(pObj,k)) ); + } + + return Value; +} + +/**Function************************************************************* + + Synopsis [Checks the integrity of a net.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkCheckNet( Abc_Ntk_t * pNtk, Abc_Obj_t * pNet ) +{ + if ( Abc_ObjFaninNum(pNet) == 0 ) + { + fprintf( stdout, "NetworkCheck: Net \"%s\" is not driven.\n", Abc_ObjName(pNet) ); + return 0; + } + if ( Abc_ObjFaninNum(pNet) > 1 ) + { + fprintf( stdout, "NetworkCheck: Net \"%s\" has more than one driver.\n", Abc_ObjName(pNet) ); + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Checks the integrity of a node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkCheckNode( Abc_Ntk_t * pNtk, Abc_Obj_t * pNode ) +{ + // detect internal nodes that do not have nets + if ( Abc_NtkIsNetlist(pNtk) && Abc_ObjFanoutNum(pNode) == 0 ) + { + fprintf( stdout, "Node (id = %d) has no net to drive.\n", pNode->Id ); + return 0; + } + // the node should have a function assigned unless it is an AIG + if ( pNode->pData == NULL ) + { + fprintf( stdout, "NodeCheck: An internal node \"%s\" does not have a logic function.\n", Abc_ObjNameNet(pNode) ); + return 0; + } + // the netlist and SOP logic network should have SOPs + if ( Abc_NtkHasSop(pNtk) ) + { + if ( !Abc_SopCheck( pNode->pData, Abc_ObjFaninNum(pNode) ) ) + { + fprintf( stdout, "NodeCheck: SOP check for node \"%s\" has failed.\n", Abc_ObjNameNet(pNode) ); + return 0; + } + } + else if ( Abc_NtkHasBdd(pNtk) ) + { + int nSuppSize = Cudd_SupportSize(pNtk->pManFunc, pNode->pData); + if ( nSuppSize > Abc_ObjFaninNum(pNode) ) + { + fprintf( stdout, "NodeCheck: BDD of the node \"%s\" has incorrect support size.\n", Abc_ObjNameNet(pNode) ); + return 0; + } + } + else if ( !Abc_NtkHasMapping(pNtk) && !Abc_NtkHasBlifMv(pNtk) && !Abc_NtkHasAig(pNtk) ) + { + assert( 0 ); + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Checks the integrity of a latch.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkCheckLatch( Abc_Ntk_t * pNtk, Abc_Obj_t * pLatch ) +{ + int Value = 1; + // check whether the object is a latch + if ( !Abc_ObjIsLatch(pLatch) ) + { + fprintf( stdout, "NodeCheck: Latch \"%s\" is in a latch list but is not a latch.\n", Abc_ObjName(pLatch) ); + Value = 0; + } + +#if 0 // ??? janders must FIX + // make sure the latch has a reasonable return value + if ( (int)pLatch->pData < ABC_INIT_ZERO || (int)pLatch->pData > ABC_INIT_DC ) + { + fprintf( stdout, "NodeCheck: Latch \"%s\" has incorrect reset value (%d).\n", + Abc_ObjName(pLatch), (int)pLatch->pData ); + Value = 0; + } +#endif + + // make sure the latch has only one fanin + if ( Abc_ObjFaninNum(pLatch) != 1 ) + { + fprintf( stdout, "NodeCheck: Latch \"%s\" has wrong number (%d) of fanins.\n", Abc_ObjName(pLatch), Abc_ObjFaninNum(pLatch) ); + Value = 0; + } + // make sure the latch has only one fanout + if ( Abc_ObjFanoutNum(pLatch) != 1 ) + { + fprintf( stdout, "NodeCheck: Latch \"%s\" has wrong number (%d) of fanouts.\n", Abc_ObjName(pLatch), Abc_ObjFanoutNum(pLatch) ); + Value = 0; + } + // make sure the latch input has only one fanin + if ( Abc_ObjFaninNum(Abc_ObjFanin0(pLatch)) != 1 ) + { + fprintf( stdout, "NodeCheck: Input of latch \"%s\" has wrong number (%d) of fanins.\n", + Abc_ObjName(Abc_ObjFanin0(pLatch)), Abc_ObjFaninNum(Abc_ObjFanin0(pLatch)) ); + Value = 0; + } + // make sure the latch input has only one fanout + if ( Abc_ObjFanoutNum(Abc_ObjFanin0(pLatch)) != 1 ) + { + fprintf( stdout, "NodeCheck: Input of latch \"%s\" has wrong number (%d) of fanouts.\n", + Abc_ObjName(Abc_ObjFanin0(pLatch)), Abc_ObjFanoutNum(Abc_ObjFanin0(pLatch)) ); + Value = 0; + } + // make sure the latch output has only one fanin + if ( Abc_ObjFaninNum(Abc_ObjFanout0(pLatch)) != 1 ) + { + fprintf( stdout, "NodeCheck: Output of latch \"%s\" has wrong number (%d) of fanins.\n", + Abc_ObjName(Abc_ObjFanout0(pLatch)), Abc_ObjFaninNum(Abc_ObjFanout0(pLatch)) ); + Value = 0; + } + return Value; +} + + + + +/**Function************************************************************* + + Synopsis [Compares the PIs of the two networks.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkComparePis( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int fComb ) +{ + Abc_Obj_t * pObj1; + int i; + if ( Abc_NtkPiNum(pNtk1) != Abc_NtkPiNum(pNtk2) ) + { + printf( "Networks have different number of primary inputs.\n" ); + return 0; + } + // for each PI of pNet1 find corresponding PI of pNet2 and reorder them + Abc_NtkForEachPi( pNtk1, pObj1, i ) + { + if ( strcmp( Abc_ObjName(pObj1), Abc_ObjName(Abc_NtkPi(pNtk2,i)) ) != 0 ) + { + printf( "Primary input #%d is different in network 1 ( \"%s\") and in network 2 (\"%s\").\n", + i, Abc_ObjName(pObj1), Abc_ObjName(Abc_NtkPi(pNtk2,i)) ); + return 0; + } + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Compares the POs of the two networks.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkComparePos( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int fComb ) +{ + Abc_Obj_t * pObj1; + int i; + if ( Abc_NtkPoNum(pNtk1) != Abc_NtkPoNum(pNtk2) ) + { + printf( "Networks have different number of primary outputs.\n" ); + return 0; + } + // for each PO of pNet1 find corresponding PO of pNet2 and reorder them + Abc_NtkForEachPo( pNtk1, pObj1, i ) + { + if ( strcmp( Abc_ObjName(pObj1), Abc_ObjName(Abc_NtkPo(pNtk2,i)) ) != 0 ) + { + printf( "Primary output #%d is different in network 1 ( \"%s\") and in network 2 (\"%s\").\n", + i, Abc_ObjName(pObj1), Abc_ObjName(Abc_NtkPo(pNtk2,i)) ); + return 0; + } + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Compares the latches of the two networks.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkCompareBoxes( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int fComb ) +{ + Abc_Obj_t * pObj1; + int i; + assert( Abc_NtkHasOnlyLatchBoxes(pNtk1) ); + assert( Abc_NtkHasOnlyLatchBoxes(pNtk2) ); + if ( !fComb ) + return 1; + if ( Abc_NtkBoxNum(pNtk1) != Abc_NtkBoxNum(pNtk2) ) + { + printf( "Networks have different number of latches.\n" ); + return 0; + } + // for each PI of pNet1 find corresponding PI of pNet2 and reorder them + Abc_NtkForEachBox( pNtk1, pObj1, i ) + { + if ( strcmp( Abc_ObjName(Abc_ObjFanout0(pObj1)), Abc_ObjName(Abc_ObjFanout0(Abc_NtkBox(pNtk2,i))) ) != 0 ) + { + printf( "Box #%d is different in network 1 ( \"%s\") and in network 2 (\"%s\").\n", + i, Abc_ObjName(Abc_ObjFanout0(pObj1)), Abc_ObjName(Abc_ObjFanout0(Abc_NtkBox(pNtk2,i))) ); + return 0; + } + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Compares the signals of the networks.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkCompareSignals( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int fOnlyPis, int fComb ) +{ + Abc_NtkOrderObjsByName( pNtk1, fComb ); + Abc_NtkOrderObjsByName( pNtk2, fComb ); + if ( !Abc_NtkComparePis( pNtk1, pNtk2, fComb ) ) + return 0; + if ( !fOnlyPis ) + { + if ( !Abc_NtkCompareBoxes( pNtk1, pNtk2, fComb ) ) + return 0; + if ( !Abc_NtkComparePos( pNtk1, pNtk2, fComb ) ) + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Returns 0 if the network hierachy contains a cycle.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkIsAcyclicHierarchy_rec( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkNext; + Abc_Obj_t * pObj; + int i; + // return if visited + if ( pNtk->fHieVisited ) + return 1; + pNtk->fHieVisited = 1; + // return if black box + if ( Abc_NtkHasBlackbox(pNtk) ) + return 1; + assert( Abc_NtkIsNetlist(pNtk) ); + // go through all the children networks + Abc_NtkForEachBox( pNtk, pObj, i ) + { + if ( Abc_ObjIsLatch(pObj) ) + continue; + pNtkNext = pObj->pData; + assert( pNtkNext != NULL ); + if ( pNtkNext->fHiePath ) + return 0; + pNtk->fHiePath = 1; + if ( !Abc_NtkIsAcyclicHierarchy_rec( pNtkNext ) ) + return 0; + pNtk->fHiePath = 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Returns 0 if the network hierachy contains a cycle.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkIsAcyclicHierarchy( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pTemp; + int i, RetValue; + assert( Abc_NtkIsNetlist(pNtk) && pNtk->pDesign ); + // clear the modules + Vec_PtrForEachEntry( pNtk->pDesign->vModules, pTemp, i ) + pTemp->fHieVisited = pTemp->fHiePath = 0; + // traverse + pNtk->fHiePath = 1; + RetValue = Abc_NtkIsAcyclicHierarchy_rec( pNtk ); + pNtk->fHiePath = 0; + // clear the modules + Vec_PtrForEachEntry( pNtk->pDesign->vModules, pTemp, i ) + pTemp->fHieVisited = pTemp->fHiePath = 0; + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Returns 0 if CI names are repeated.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkNamesCompare( char ** pName1, char ** pName2 ) +{ + return strcmp( *pName1, *pName2 ); +} + +/**Function************************************************************* + + Synopsis [Returns 0 if CI names are repeated.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkCheckUniqueCiNames( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vNames; + Abc_Obj_t * pObj; + int i, fRetValue = 1; + assert( !Abc_NtkIsNetlist(pNtk) ); + vNames = Vec_PtrAlloc( Abc_NtkCiNum(pNtk) ); + Abc_NtkForEachCi( pNtk, pObj, i ) + Vec_PtrPush( vNames, Abc_ObjName(pObj) ); + Vec_PtrSort( vNames, Abc_NtkNamesCompare ); + for ( i = 1; i < Abc_NtkCiNum(pNtk); i++ ) + if ( !strcmp( Vec_PtrEntry(vNames,i-1), Vec_PtrEntry(vNames,i) ) ) + { + printf( "Abc_NtkCheck: Repeated CI names: %s and %s.\n", Vec_PtrEntry(vNames,i-1), Vec_PtrEntry(vNames,i) ); + fRetValue = 0; + } + Vec_PtrFree( vNames ); + return fRetValue; +} + +/**Function************************************************************* + + Synopsis [Returns 0 if CO names are repeated.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkCheckUniqueCoNames( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vNames; + Abc_Obj_t * pObj; + int i, fRetValue = 1; + assert( !Abc_NtkIsNetlist(pNtk) ); + vNames = Vec_PtrAlloc( Abc_NtkCoNum(pNtk) ); + Abc_NtkForEachCo( pNtk, pObj, i ) + Vec_PtrPush( vNames, Abc_ObjName(pObj) ); + Vec_PtrSort( vNames, Abc_NtkNamesCompare ); + for ( i = 1; i < Abc_NtkCoNum(pNtk); i++ ) + { +// printf( "%s\n", Vec_PtrEntry(vNames,i) ); + if ( !strcmp( Vec_PtrEntry(vNames,i-1), Vec_PtrEntry(vNames,i) ) ) + { + printf( "Abc_NtkCheck: Repeated CO names: %s and %s.\n", Vec_PtrEntry(vNames,i-1), Vec_PtrEntry(vNames,i) ); + fRetValue = 0; + } + } + Vec_PtrFree( vNames ); + return fRetValue; +} + +/**Function************************************************************* + + Synopsis [Returns 0 if there is a pair of CI/CO with the same name and logic in between.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkCheckUniqueCioNames( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj, * pObjCi; + int i, nCiId, fRetValue = 1; + assert( !Abc_NtkIsNetlist(pNtk) ); + Abc_NtkForEachCo( pNtk, pObj, i ) + { + nCiId = Nm_ManFindIdByNameTwoTypes( pNtk->pManName, Abc_ObjName(pObj), ABC_OBJ_PI, ABC_OBJ_BO ); + if ( nCiId == -1 ) + continue; + pObjCi = Abc_NtkObj( pNtk, nCiId ); + assert( !strcmp( Abc_ObjName(pObj), Abc_ObjName(pObjCi) ) ); + if ( Abc_ObjFanin0(pObj) != pObjCi ) + { + printf( "Abc_NtkCheck: A CI/CO pair share the name (%s) but do not link directly.\n", Abc_ObjName(pObj) ); + fRetValue = 0; + } + } + return fRetValue; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abc/abcDfs.c b/abc_with_bb_support/src/base/abc/abcDfs.c new file mode 100644 index 000000000..dade55fae --- /dev/null +++ b/abc_with_bb_support/src/base/abc/abcDfs.c @@ -0,0 +1,1264 @@ +/**CFile**************************************************************** + + FileName [abcDfs.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Procedures that use depth-first search.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcDfs.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs DFS for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDfs_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes ) +{ + Abc_Obj_t * pFanin; + int i; + assert( !Abc_ObjIsNet(pNode) ); + // if this node is already visited, skip + if ( Abc_NodeIsTravIdCurrent( pNode ) ) + return; + // mark the node as visited + Abc_NodeSetTravIdCurrent( pNode ); + // skip the CI + if ( Abc_ObjIsCi(pNode) || (Abc_NtkIsStrash(pNode->pNtk) && Abc_AigNodeIsConst(pNode)) ) + return; + assert( Abc_ObjIsNode( pNode ) || Abc_ObjIsBox( pNode ) ); + // visit the transitive fanin of the node + Abc_ObjForEachFanin( pNode, pFanin, i ) + { +// pFanin = Abc_ObjFanin( pNode, Abc_ObjFaninNum(pNode)-1-i ); + Abc_NtkDfs_rec( Abc_ObjFanin0Ntk(pFanin), vNodes ); + } + // add the node after the fanins have been added + Vec_PtrPush( vNodes, pNode ); +} + +/**Function************************************************************* + + Synopsis [Returns the DFS ordered array of logic nodes.] + + Description [Collects only the internal nodes, leaving CIs and CO. + However it marks with the current TravId both CIs and COs.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkDfs( Abc_Ntk_t * pNtk, int fCollectAll ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj; + int i; + // set the traversal ID + Abc_NtkIncrementTravId( pNtk ); + // start the array of nodes + vNodes = Vec_PtrAlloc( 100 ); + Abc_NtkForEachCo( pNtk, pObj, i ) + { + Abc_NodeSetTravIdCurrent( pObj ); + Abc_NtkDfs_rec( Abc_ObjFanin0Ntk(Abc_ObjFanin0(pObj)), vNodes ); + } + // collect dangling nodes if asked to + if ( fCollectAll ) + { + Abc_NtkForEachNode( pNtk, pObj, i ) + if ( !Abc_NodeIsTravIdCurrent(pObj) ) + Abc_NtkDfs_rec( pObj, vNodes ); + } + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Returns the DFS ordered array of logic nodes.] + + Description [Collects only the internal nodes, leaving out PIs, POs and latches.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkDfsNodes( Abc_Ntk_t * pNtk, Abc_Obj_t ** ppNodes, int nNodes ) +{ + Vec_Ptr_t * vNodes; + int i; + // set the traversal ID + Abc_NtkIncrementTravId( pNtk ); + // start the array of nodes + vNodes = Vec_PtrAlloc( 100 ); + // go through the PO nodes and call for each of them + for ( i = 0; i < nNodes; i++ ) + { + if ( Abc_ObjIsCo(ppNodes[i]) ) + { + Abc_NodeSetTravIdCurrent(ppNodes[i]); + Abc_NtkDfs_rec( Abc_ObjFanin0Ntk(Abc_ObjFanin0(ppNodes[i])), vNodes ); + } + else if ( Abc_ObjIsNode(ppNodes[i]) ) + Abc_NtkDfs_rec( ppNodes[i], vNodes ); + } + return vNodes; +} + + +/**Function************************************************************* + + Synopsis [Performs DFS for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDfsReverse_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes ) +{ + Abc_Obj_t * pFanout; + int i; + assert( !Abc_ObjIsNet(pNode) ); + // if this node is already visited, skip + if ( Abc_NodeIsTravIdCurrent( pNode ) ) + return; + // mark the node as visited + Abc_NodeSetTravIdCurrent( pNode ); + // skip the CI + if ( Abc_ObjIsCo(pNode) ) + return; + assert( Abc_ObjIsNode( pNode ) ); + // visit the transitive fanin of the node + pNode = Abc_ObjFanout0Ntk(pNode); + Abc_ObjForEachFanout( pNode, pFanout, i ) + Abc_NtkDfsReverse_rec( pFanout, vNodes ); + // add the node after the fanins have been added + Vec_PtrPush( vNodes, pNode ); +} + +/**Function************************************************************* + + Synopsis [Returns the reverse DFS ordered array of logic nodes.] + + Description [Collects only the internal nodes, leaving out CIs/COs. + However it marks both CIs and COs with the current TravId.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkDfsReverse( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj, * pFanout; + int i, k; + // set the traversal ID + Abc_NtkIncrementTravId( pNtk ); + // start the array of nodes + vNodes = Vec_PtrAlloc( 100 ); + Abc_NtkForEachCi( pNtk, pObj, i ) + { + Abc_NodeSetTravIdCurrent( pObj ); + pObj = Abc_ObjFanout0Ntk(pObj); + Abc_ObjForEachFanout( pObj, pFanout, k ) + Abc_NtkDfsReverse_rec( pFanout, vNodes ); + } + // add constant nodes in the end + if ( !Abc_NtkIsStrash(pNtk) ) + Abc_NtkForEachNode( pNtk, pObj, i ) + if ( Abc_NodeIsConst(pObj) ) + Vec_PtrPush( vNodes, pObj ); + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Performs DFS for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDfsReverseNodes_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes ) +{ + Abc_Obj_t * pFanout; + int i; + assert( !Abc_ObjIsNet(pNode) ); + // if this node is already visited, skip + if ( Abc_NodeIsTravIdCurrent( pNode ) ) + return; + // mark the node as visited + Abc_NodeSetTravIdCurrent( pNode ); + // skip the CI + if ( Abc_ObjIsCo(pNode) ) + return; + assert( Abc_ObjIsNode( pNode ) ); + // visit the transitive fanin of the node + pNode = Abc_ObjFanout0Ntk(pNode); + Abc_ObjForEachFanout( pNode, pFanout, i ) + Abc_NtkDfsReverseNodes_rec( pFanout, vNodes ); + // add the node after the fanins have been added +// Vec_PtrPush( vNodes, pNode ); + Vec_PtrFillExtra( vNodes, pNode->Level + 1, NULL ); + pNode->pCopy = Vec_PtrEntry( vNodes, pNode->Level ); + Vec_PtrWriteEntry( vNodes, pNode->Level, pNode ); +} + +/**Function************************************************************* + + Synopsis [Returns the levelized array of TFO nodes.] + + Description [Collects the levelized array of internal nodes, leaving out CIs/COs. + However it marks both CIs and COs with the current TravId.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkDfsReverseNodes( Abc_Ntk_t * pNtk, Abc_Obj_t ** ppNodes, int nNodes ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj, * pFanout; + int i, k; + assert( Abc_NtkIsStrash(pNtk) ); + // set the traversal ID + Abc_NtkIncrementTravId( pNtk ); + // start the array of nodes + vNodes = Vec_PtrStart( Abc_AigLevel(pNtk) + 1 ); + for ( i = 0; i < nNodes; i++ ) + { + pObj = ppNodes[i]; + assert( Abc_ObjIsCi(pObj) ); + Abc_NodeSetTravIdCurrent( pObj ); + pObj = Abc_ObjFanout0Ntk(pObj); + Abc_ObjForEachFanout( pObj, pFanout, k ) + Abc_NtkDfsReverseNodes_rec( pFanout, vNodes ); + } + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Returns the levelized array of TFO nodes.] + + Description [Collects the levelized array of internal nodes, leaving out CIs/COs. + However it marks both CIs and COs with the current TravId. + Collects only the nodes whose support does not exceed the set of given CI nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkDfsReverseNodesContained( Abc_Ntk_t * pNtk, Abc_Obj_t ** ppNodes, int nNodes ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj, * pFanout, * pFanin; + int i, k, m, nLevels; + // set the levels + nLevels = Abc_NtkLevel( pNtk ); + // set the traversal ID + Abc_NtkIncrementTravId( pNtk ); + // start the array of nodes + vNodes = Vec_PtrStart( nLevels + 2 ); + for ( i = 0; i < nNodes; i++ ) + { + pObj = ppNodes[i]; + assert( Abc_ObjIsCi(pObj) ); + Abc_NodeSetTravIdCurrent( pObj ); + // add to the array + assert( pObj->Level == 0 ); + pObj->pCopy = Vec_PtrEntry( vNodes, pObj->Level ); + Vec_PtrWriteEntry( vNodes, pObj->Level, pObj ); + } + // iterate through the levels + for ( i = 0; i <= nLevels; i++ ) + { + // iterate through the nodes on each level + for ( pObj = Vec_PtrEntry(vNodes, i); pObj; pObj = pObj->pCopy ) + { + // iterate through the fanouts of each node + Abc_ObjForEachFanout( pObj, pFanout, k ) + { + // skip visited nodes + if ( Abc_NodeIsTravIdCurrent(pFanout) ) + continue; + // visit the fanins of this fanout + Abc_ObjForEachFanin( pFanout, pFanin, m ) + { + if ( !Abc_NodeIsTravIdCurrent(pFanin) ) + break; + } + if ( m < Abc_ObjFaninNum(pFanout) ) + continue; + // all fanins are already collected + + // mark the node as visited + Abc_NodeSetTravIdCurrent( pFanout ); + // handle the COs + if ( Abc_ObjIsCo(pFanout) ) + pFanout->Level = nLevels + 1; + // add to the array + pFanout->pCopy = Vec_PtrEntry( vNodes, pFanout->Level ); + Vec_PtrWriteEntry( vNodes, pFanout->Level, pFanout ); + // handle the COs + if ( Abc_ObjIsCo(pFanout) ) + pFanout->Level = 0; + } + } + } + return vNodes; +} + + +/**Function************************************************************* + + Synopsis [Performs DFS for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDfsSeq_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes ) +{ + Abc_Obj_t * pFanin; + int i; + // if this node is already visited, skip + if ( Abc_NodeIsTravIdCurrent( pNode ) ) + return; + // mark the node as visited + Abc_NodeSetTravIdCurrent( pNode ); + // visit the transitive fanin of the node + Abc_ObjForEachFanin( pNode, pFanin, i ) + Abc_NtkDfsSeq_rec( pFanin, vNodes ); + // add the node after the fanins have been added + Vec_PtrPush( vNodes, pNode ); +} + +/**Function************************************************************* + + Synopsis [Returns the array of nodes and latches reachable from POs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkDfsSeq( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj; + int i; + assert( !Abc_NtkIsNetlist(pNtk) ); + // set the traversal ID + Abc_NtkIncrementTravId( pNtk ); + // start the array of nodes + vNodes = Vec_PtrAlloc( 100 ); + Abc_NtkForEachPo( pNtk, pObj, i ) + Abc_NtkDfsSeq_rec( pObj, vNodes ); + // mark the PIs + Abc_NtkForEachPi( pNtk, pObj, i ) + Abc_NtkDfsSeq_rec( pObj, vNodes ); + return vNodes; +} + + +/**Function************************************************************* + + Synopsis [Performs DFS for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDfsSeqReverse_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes ) +{ + Abc_Obj_t * pFanout; + int i; + // if this node is already visited, skip + if ( Abc_NodeIsTravIdCurrent( pNode ) ) + return; + // mark the node as visited + Abc_NodeSetTravIdCurrent( pNode ); + // visit the transitive fanin of the node + Abc_ObjForEachFanout( pNode, pFanout, i ) + Abc_NtkDfsSeqReverse_rec( pFanout, vNodes ); + // add the node after the fanins have been added + Vec_PtrPush( vNodes, pNode ); +} + +/**Function************************************************************* + + Synopsis [Returns the array of nodes and latches reachable from POs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkDfsSeqReverse( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj; + int i; + assert( !Abc_NtkIsNetlist(pNtk) ); + // set the traversal ID + Abc_NtkIncrementTravId( pNtk ); + // start the array of nodes + vNodes = Vec_PtrAlloc( 100 ); + Abc_NtkForEachPi( pNtk, pObj, i ) + Abc_NtkDfsSeqReverse_rec( pObj, vNodes ); + // mark the logic feeding into POs + Abc_NtkForEachPo( pNtk, pObj, i ) + Abc_NtkDfsSeq_rec( pObj, vNodes ); + return vNodes; +} + + +/**Function************************************************************* + + Synopsis [Iterative version of the DFS procedure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDfs_iter( Vec_Ptr_t * vStack, Abc_Obj_t * pRoot, Vec_Ptr_t * vNodes ) +{ + Abc_Obj_t * pNode, * pFanin; + int iFanin; + // if this node is already visited, skip + if ( Abc_NodeIsTravIdCurrent( pRoot ) ) + return; + // mark the node as visited + Abc_NodeSetTravIdCurrent( pRoot ); + // skip the CI + if ( Abc_ObjIsCi(pRoot) || (Abc_NtkIsStrash(pRoot->pNtk) && Abc_AigNodeIsConst(pRoot)) ) + return; + // add the CI + Vec_PtrClear( vStack ); + Vec_PtrPush( vStack, pRoot ); + Vec_PtrPush( vStack, (void *)0 ); + while ( Vec_PtrSize(vStack) > 0 ) + { + // get the node and its fanin + iFanin = (int)Vec_PtrPop(vStack); + pNode = Vec_PtrPop(vStack); + assert( !Abc_ObjIsNet(pNode) ); + // add it to the array of nodes if we finished + if ( iFanin == Abc_ObjFaninNum(pNode) ) + { + Vec_PtrPush( vNodes, pNode ); + continue; + } + // explore the next fanin + Vec_PtrPush( vStack, pNode ); + Vec_PtrPush( vStack, (void *)(iFanin+1) ); + // get the fanin + pFanin = Abc_ObjFanin0Ntk( Abc_ObjFanin(pNode,iFanin) ); + // if this node is already visited, skip + if ( Abc_NodeIsTravIdCurrent( pFanin ) ) + continue; + // mark the node as visited + Abc_NodeSetTravIdCurrent( pFanin ); + // skip the CI + if ( Abc_ObjIsCi(pFanin) || (Abc_NtkIsStrash(pFanin->pNtk) && Abc_AigNodeIsConst(pFanin)) ) + continue; + Vec_PtrPush( vStack, pFanin ); + Vec_PtrPush( vStack, (void *)0 ); + } +} + +/**Function************************************************************* + + Synopsis [Returns the DFS ordered array of logic nodes.] + + Description [Collects only the internal nodes, leaving CIs and CO. + However it marks with the current TravId both CIs and COs.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkDfsIter( Abc_Ntk_t * pNtk, int fCollectAll ) +{ + Vec_Ptr_t * vNodes, * vStack; + Abc_Obj_t * pObj; + int i; + // set the traversal ID + Abc_NtkIncrementTravId( pNtk ); + // start the array of nodes + vNodes = Vec_PtrAlloc( 1000 ); + vStack = Vec_PtrAlloc( 1000 ); + Abc_NtkForEachCo( pNtk, pObj, i ) + { + Abc_NodeSetTravIdCurrent( pObj ); + Abc_NtkDfs_iter( vStack, Abc_ObjFanin0Ntk(Abc_ObjFanin0(pObj)), vNodes ); + } + // collect dangling nodes if asked to + if ( fCollectAll ) + { + Abc_NtkForEachNode( pNtk, pObj, i ) + if ( !Abc_NodeIsTravIdCurrent(pObj) ) + Abc_NtkDfs_iter( vStack, pObj, vNodes ); + } + Vec_PtrFree( vStack ); + return vNodes; +} + + +/**Function************************************************************* + + Synopsis [Performs DFS for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDfsHie_rec( Abc_Obj_t * pObj, Vec_Ptr_t * vNodes ) +{ + Abc_Obj_t * pFanin; + int i; + // if this node is already visited, skip + if ( Abc_NodeIsTravIdCurrent( pObj ) ) + return; + // mark the node as visited + Abc_NodeSetTravIdCurrent( pObj ); + // visit the transitive fanin of the node + Abc_ObjForEachFanin( pObj, pFanin, i ) + Abc_NtkDfsHie_rec( pFanin, vNodes ); + // add the node after the fanins have been added + Vec_PtrPush( vNodes, pObj ); +} + +/**Function************************************************************* + + Synopsis [Returns the DFS ordered array of all objects.] + + Description [This procedure collects everything from POs to PIs.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkDfsHie( Abc_Ntk_t * pNtk, int fCollectAll ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj; + int i; + // set the traversal ID + Abc_NtkIncrementTravId( pNtk ); + // start the array of nodes + vNodes = Vec_PtrAlloc( 100 ); + Abc_NtkForEachPo( pNtk, pObj, i ) + Abc_NtkDfsHie_rec( pObj, vNodes ); + // collect dangling nodes if asked to + if ( fCollectAll ) + { + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( !Abc_NodeIsTravIdCurrent(pObj) ) + Abc_NtkDfs_rec( pObj, vNodes ); + } + return vNodes; +} + + +/**Function************************************************************* + + Synopsis [Returns 1 if the ordering of nodes is DFS.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkIsDfsOrdered( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode, * pFanin; + int i, k; + // set the traversal ID + Abc_NtkIncrementTravId( pNtk ); + // mark the CIs + Abc_NtkForEachCi( pNtk, pNode, i ) + Abc_NodeSetTravIdCurrent( pNode ); + // go through the nodes + Abc_NtkForEachNode( pNtk, pNode, i ) + { + // check the fanins of the node + Abc_ObjForEachFanin( pNode, pFanin, k ) + if ( !Abc_NodeIsTravIdCurrent(pFanin) ) + return 0; + // check the choices of the node + if ( Abc_NtkIsStrash(pNtk) && Abc_AigNodeIsChoice(pNode) ) + for ( pFanin = pNode->pData; pFanin; pFanin = pFanin->pData ) + if ( !Abc_NodeIsTravIdCurrent(pFanin) ) + return 0; + // mark the node as visited + Abc_NodeSetTravIdCurrent( pNode ); + } + return 1; +} + + +/**Function************************************************************* + + Synopsis [Performs DFS for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkNodeSupport_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes ) +{ + Abc_Obj_t * pFanin; + int i; + assert( !Abc_ObjIsNet(pNode) ); + // if this node is already visited, skip + if ( Abc_NodeIsTravIdCurrent( pNode ) ) + return; + // mark the node as visited + Abc_NodeSetTravIdCurrent( pNode ); + // collect the CI + if ( Abc_ObjIsCi(pNode) || Abc_ObjFaninNum(pNode) == 0 ) + { + Vec_PtrPush( vNodes, pNode ); + return; + } + assert( Abc_ObjIsNode( pNode ) ); + // visit the transitive fanin of the node + Abc_ObjForEachFanin( pNode, pFanin, i ) + Abc_NtkNodeSupport_rec( Abc_ObjFanin0Ntk(pFanin), vNodes ); +} + +/**Function************************************************************* + + Synopsis [Returns the set of CI nodes in the support of the given nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkSupport( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pNode; + int i; + // set the traversal ID + Abc_NtkIncrementTravId( pNtk ); + // start the array of nodes + vNodes = Vec_PtrAlloc( 100 ); + // go through the PO nodes and call for each of them + Abc_NtkForEachCo( pNtk, pNode, i ) + Abc_NtkNodeSupport_rec( Abc_ObjFanin0(pNode), vNodes ); + // add unused CIs + Abc_NtkForEachCi( pNtk, pNode, i ) + if ( !Abc_NodeIsTravIdCurrent( pNode ) ) + Vec_PtrPush( vNodes, pNode ); + assert( Vec_PtrSize(vNodes) == Abc_NtkCiNum(pNtk) ); + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Returns the set of CI nodes in the support of the given nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkNodeSupport( Abc_Ntk_t * pNtk, Abc_Obj_t ** ppNodes, int nNodes ) +{ + Vec_Ptr_t * vNodes; + int i; + // set the traversal ID + Abc_NtkIncrementTravId( pNtk ); + // start the array of nodes + vNodes = Vec_PtrAlloc( 100 ); + // go through the PO nodes and call for each of them + for ( i = 0; i < nNodes; i++ ) + if ( Abc_ObjIsCo(ppNodes[i]) ) + Abc_NtkNodeSupport_rec( Abc_ObjFanin0(ppNodes[i]), vNodes ); + else + Abc_NtkNodeSupport_rec( ppNodes[i], vNodes ); + return vNodes; +} + + +/**Function************************************************************* + + Synopsis [Performs DFS for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_AigDfs_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes ) +{ + Abc_Obj_t * pFanin; + int i; + // if this node is already visited, skip + if ( Abc_NodeIsTravIdCurrent( pNode ) ) + return; + // mark the node as visited + Abc_NodeSetTravIdCurrent( pNode ); + // skip the PI + if ( Abc_ObjIsCi(pNode) || Abc_AigNodeIsConst(pNode) ) + return; + assert( Abc_ObjIsNode( pNode ) ); + // visit the transitive fanin of the node + Abc_ObjForEachFanin( pNode, pFanin, i ) + Abc_AigDfs_rec( pFanin, vNodes ); + // visit the equivalent nodes + if ( Abc_AigNodeIsChoice( pNode ) ) + for ( pFanin = pNode->pData; pFanin; pFanin = pFanin->pData ) + Abc_AigDfs_rec( pFanin, vNodes ); + // add the node after the fanins have been added + Vec_PtrPush( vNodes, pNode ); +} + +/**Function************************************************************* + + Synopsis [Returns the DFS ordered array of logic nodes.] + + Description [Collects only the internal nodes, leaving out CIs/COs. + However it marks both CIs and COs with the current TravId.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_AigDfs( Abc_Ntk_t * pNtk, int fCollectAll, int fCollectCos ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pNode; + int i; + assert( Abc_NtkIsStrash(pNtk) ); + // set the traversal ID + Abc_NtkIncrementTravId( pNtk ); + // start the array of nodes + vNodes = Vec_PtrAlloc( 100 ); + // go through the PO nodes and call for each of them + Abc_NtkForEachCo( pNtk, pNode, i ) + { + Abc_AigDfs_rec( Abc_ObjFanin0(pNode), vNodes ); + Abc_NodeSetTravIdCurrent( pNode ); + if ( fCollectCos ) + Vec_PtrPush( vNodes, pNode ); + } + // collect dangling nodes if asked to + if ( fCollectAll ) + { + Abc_NtkForEachNode( pNtk, pNode, i ) + if ( !Abc_NodeIsTravIdCurrent(pNode) ) + Abc_AigDfs_rec( pNode, vNodes ); + } + return vNodes; +} + + +/**Function************************************************************* + + Synopsis [Collects nodes in the DFS manner by level.] + + Description [The number of levels should be set!!!] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_DfsLevelizedTfo_rec( Abc_Obj_t * pNode, Vec_Vec_t * vLevels ) +{ + Abc_Obj_t * pFanout; + int i; + // if this node is already visited, skip + if ( Abc_NodeIsTravIdCurrent( pNode ) ) + return; + // mark the node as visited + Abc_NodeSetTravIdCurrent( pNode ); + // skip the terminals + if ( Abc_ObjIsCo(pNode) ) + return; + assert( Abc_ObjIsNode(pNode) ); + // add the node to the structure + Vec_VecPush( vLevels, pNode->Level, pNode ); + // visit the TFO + Abc_ObjForEachFanout( pNode, pFanout, i ) + Abc_DfsLevelizedTfo_rec( pFanout, vLevels ); +} + +/**Function************************************************************* + + Synopsis [Collects nodes in the DFS manner by level.] + + Description [The number of levels should be set!!!] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Vec_t * Abc_DfsLevelized( Abc_Obj_t * pNode, bool fTfi ) +{ + Vec_Vec_t * vLevels; + Abc_Obj_t * pFanout; + int i; + assert( fTfi == 0 ); + assert( !Abc_NtkIsNetlist(pNode->pNtk) ); + // set the traversal ID + Abc_NtkIncrementTravId( pNode->pNtk ); + vLevels = Vec_VecAlloc( 100 ); + if ( Abc_ObjIsNode(pNode) ) + Abc_DfsLevelizedTfo_rec( pNode, vLevels ); + else + { + assert( Abc_ObjIsCi(pNode) ); + Abc_NodeSetTravIdCurrent( pNode ); + Abc_ObjForEachFanout( pNode, pFanout, i ) + Abc_DfsLevelizedTfo_rec( pFanout, vLevels ); + } + return vLevels; +} + + +/**Function************************************************************* + + Synopsis [Recursively counts the number of logic levels of one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkLevel_rec( Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pNext; + int i, Level; + assert( !Abc_ObjIsNet(pNode) ); + // skip the PI + if ( Abc_ObjIsCi(pNode) ) + return pNode->Level; + assert( Abc_ObjIsNode( pNode ) ); + // if this node is already visited, return + if ( Abc_NodeIsTravIdCurrent( pNode ) ) + return pNode->Level; + // mark the node as visited + Abc_NodeSetTravIdCurrent( pNode ); + // visit the transitive fanin + pNode->Level = 0; + Abc_ObjForEachFanin( pNode, pNext, i ) + { + Level = Abc_NtkLevel_rec( Abc_ObjFanin0Ntk(pNext) ); + if ( pNode->Level < (unsigned)Level ) + pNode->Level = Level; + } + if ( Abc_ObjFaninNum(pNode) > 0 ) + pNode->Level++; + return pNode->Level; +} + +/**Function************************************************************* + + Synopsis [Recursively counts the number of logic levels of one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkLevelReverse_rec( Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pNext; + int i, Level; + assert( !Abc_ObjIsNet(pNode) ); + // skip the PI + if ( Abc_ObjIsCo(pNode) ) + return pNode->Level; + assert( Abc_ObjIsNode( pNode ) ); + // if this node is already visited, return + if ( Abc_NodeIsTravIdCurrent( pNode ) ) + return pNode->Level; + // mark the node as visited + Abc_NodeSetTravIdCurrent( pNode ); + // visit the transitive fanin + pNode->Level = 0; + Abc_ObjForEachFanout( pNode, pNext, i ) + { + Level = Abc_NtkLevelReverse_rec( Abc_ObjFanout0Ntk(pNext) ); + if ( pNode->Level < (unsigned)Level ) + pNode->Level = Level; + } + if ( Abc_ObjFaninNum(pNode) > 0 ) + pNode->Level++; + return pNode->Level; +} + +/**Function************************************************************* + + Synopsis [Computes the number of logic levels not counting PIs/POs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkLevel( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i, LevelsMax; + // set the CI levels to zero + Abc_NtkForEachCi( pNtk, pNode, i ) + pNode->Level = 0; + // perform the traversal + LevelsMax = 0; + Abc_NtkIncrementTravId( pNtk ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + Abc_NtkLevel_rec( pNode ); + if ( LevelsMax < (int)pNode->Level ) + LevelsMax = (int)pNode->Level; + } + return LevelsMax; +} + +/**Function************************************************************* + + Synopsis [Computes the number of logic levels not counting PIs/POs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkLevelReverse( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i, LevelsMax; + // set the CO levels to zero + Abc_NtkForEachCo( pNtk, pNode, i ) + pNode->Level = 0; + // perform the traversal + LevelsMax = 0; + Abc_NtkIncrementTravId( pNtk ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + Abc_NtkLevelReverse_rec( pNode ); + if ( LevelsMax < (int)pNode->Level ) + LevelsMax = (int)pNode->Level; + } + return LevelsMax; +} + + +/**Function************************************************************* + + Synopsis [Recursively detects combinational loops.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkIsAcyclic_rec( Abc_Obj_t * pNode ) +{ + Abc_Ntk_t * pNtk = pNode->pNtk; + Abc_Obj_t * pFanin; + int fAcyclic, i; + assert( !Abc_ObjIsNet(pNode) ); + if ( Abc_ObjIsCi(pNode) || Abc_ObjIsBox(pNode) || (Abc_NtkIsStrash(pNode->pNtk) && Abc_AigNodeIsConst(pNode)) ) + return 1; + assert( Abc_ObjIsNode(pNode) ); + // make sure the node is not visited + assert( !Abc_NodeIsTravIdPrevious(pNode) ); + // check if the node is part of the combinational loop + if ( Abc_NodeIsTravIdCurrent(pNode) ) + { + fprintf( stdout, "Network \"%s\" contains combinational loop!\n", Abc_NtkName(pNtk) ); + fprintf( stdout, "Node \"%s\" is encountered twice on the following path to the COs:\n", Abc_ObjName(pNode) ); + return 0; + } + // mark this node as a node on the current path + Abc_NodeSetTravIdCurrent( pNode ); + // visit the transitive fanin + Abc_ObjForEachFanin( pNode, pFanin, i ) + { + pFanin = Abc_ObjFanin0Ntk(pFanin); + // make sure there is no mixing of networks + assert( pFanin->pNtk == pNode->pNtk ); + // check if the fanin is visited + if ( Abc_NodeIsTravIdPrevious(pFanin) ) + continue; + // traverse the fanin's cone searching for the loop + if ( fAcyclic = Abc_NtkIsAcyclic_rec(pFanin) ) + continue; + // return as soon as the loop is detected + fprintf( stdout, " %s ->", Abc_ObjName(pFanin) ); + return 0; + } + // visit choices + if ( Abc_NtkIsStrash(pNode->pNtk) && Abc_AigNodeIsChoice(pNode) ) + { + for ( pFanin = pNode->pData; pFanin; pFanin = pFanin->pData ) + { + // check if the fanin is visited + if ( Abc_NodeIsTravIdPrevious(pFanin) ) + continue; + // traverse the fanin's cone searching for the loop + if ( fAcyclic = Abc_NtkIsAcyclic_rec(pFanin) ) + continue; + // return as soon as the loop is detected + fprintf( stdout, " %s", Abc_ObjName(pFanin) ); + fprintf( stdout, " (choice of %s) -> ", Abc_ObjName(pNode) ); + return 0; + } + } + // mark this node as a visited node + Abc_NodeSetTravIdPrevious( pNode ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Detects combinational loops.] + + Description [This procedure is based on the idea suggested by Donald Chai. + As we traverse the network and visit the nodes, we need to distinquish + three types of nodes: (1) those that are visited for the first time, + (2) those that have been visited in this traversal but are currently not + on the traversal path, (3) those that have been visited and are currently + on the travesal path. When the node of type (3) is encountered, it means + that there is a combinational loop. To mark the three types of nodes, + two new values of the traversal IDs are used.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkIsAcyclic( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int fAcyclic, i; + // set the traversal ID for this DFS ordering + Abc_NtkIncrementTravId( pNtk ); + Abc_NtkIncrementTravId( pNtk ); + // pNode->TravId == pNet->nTravIds means "pNode is on the path" + // pNode->TravId == pNet->nTravIds - 1 means "pNode is visited but is not on the path" + // pNode->TravId < pNet->nTravIds - 1 means "pNode is not visited" + // traverse the network to detect cycles + fAcyclic = 1; + Abc_NtkForEachCo( pNtk, pNode, i ) + { + pNode = Abc_ObjFanin0Ntk(Abc_ObjFanin0(pNode)); + if ( Abc_NodeIsTravIdPrevious(pNode) ) + continue; + // traverse the output logic cone + if ( fAcyclic = Abc_NtkIsAcyclic_rec(pNode) ) + continue; + // stop as soon as the first loop is detected + fprintf( stdout, " CO \"%s\"\n", Abc_ObjName(Abc_ObjFanout0(pNode)) ); + break; + } + return fAcyclic; +} + + +/**Function************************************************************* + + Synopsis [Analyses choice nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeSetChoiceLevel_rec( Abc_Obj_t * pNode, int fMaximum ) +{ + Abc_Obj_t * pTemp; + int Level1, Level2, Level, LevelE; + // skip the visited node + if ( Abc_NodeIsTravIdCurrent( pNode ) ) + return (int)pNode->pCopy; + Abc_NodeSetTravIdCurrent( pNode ); + // compute levels of the children nodes + Level1 = Abc_NodeSetChoiceLevel_rec( Abc_ObjFanin0(pNode), fMaximum ); + Level2 = Abc_NodeSetChoiceLevel_rec( Abc_ObjFanin1(pNode), fMaximum ); + Level = 1 + ABC_MAX( Level1, Level2 ); + if ( pNode->pData ) + { + LevelE = Abc_NodeSetChoiceLevel_rec( pNode->pData, fMaximum ); + if ( fMaximum ) + Level = ABC_MAX( Level, LevelE ); + else + Level = ABC_MIN( Level, LevelE ); + // set the level of all equivalent nodes to be the same minimum + for ( pTemp = pNode->pData; pTemp; pTemp = pTemp->pData ) + pTemp->pCopy = (void *)Level; + } + pNode->pCopy = (void *)Level; + return Level; +} + +/**Function************************************************************* + + Synopsis [Resets the levels of the nodes in the choice graph.] + + Description [Makes the level of the choice nodes to be equal to the + maximum of the level of the nodes in the equivalence class. This way + sorting by level leads to the reverse topological order, which is + needed for the required time computation.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_AigSetChoiceLevels( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i, LevelMax, LevelCur; + assert( Abc_NtkIsStrash(pNtk) ); + // set the new travid counter + Abc_NtkIncrementTravId( pNtk ); + // set levels of the CI and constant + Abc_NtkForEachCi( pNtk, pObj, i ) + { + Abc_NodeSetTravIdCurrent( pObj ); + pObj->pCopy = NULL; + } + pObj = Abc_AigConst1( pNtk ); + Abc_NodeSetTravIdCurrent( pObj ); + pObj->pCopy = NULL; + // set levels of all other nodes + LevelMax = 0; + Abc_NtkForEachCo( pNtk, pObj, i ) + { + LevelCur = Abc_NodeSetChoiceLevel_rec( Abc_ObjFanin0(pObj), 1 ); + LevelMax = ABC_MAX( LevelMax, LevelCur ); + } + return LevelMax; +} + +/**Function************************************************************* + + Synopsis [Returns nodes by level from the smallest to the largest.] + + Description [Correctly handles the case of choice nodes, by first + spreading them out across several levels and then collecting.] + + SideEffects [What happens with dangling nodes???] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_AigGetLevelizedOrder( Abc_Ntk_t * pNtk, int fCollectCis ) +{ + Vec_Ptr_t * vNodes, * vLevels; + Abc_Obj_t * pNode, ** ppHead; + int LevelMax, i; + assert( Abc_NtkIsStrash(pNtk) ); + // set the correct levels + Abc_NtkCleanCopy( pNtk ); + LevelMax = Abc_AigSetChoiceLevels( pNtk ); + // relink nodes by level + vLevels = Vec_PtrStart( LevelMax + 1 ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + ppHead = ((Abc_Obj_t **)vLevels->pArray) + (int)pNode->pCopy; + pNode->pCopy = *ppHead; + *ppHead = pNode; + } + // recollect nodes + vNodes = Vec_PtrStart( Abc_NtkNodeNum(pNtk) ); + Vec_PtrForEachEntryStart( vLevels, pNode, i, !fCollectCis ) + for ( ; pNode; pNode = pNode->pCopy ) + Vec_PtrPush( vNodes, pNode ); + Vec_PtrFree( vLevels ); + return vNodes; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abc/abcFanio.c b/abc_with_bb_support/src/base/abc/abcFanio.c new file mode 100644 index 000000000..73b1631e6 --- /dev/null +++ b/abc_with_bb_support/src/base/abc/abcFanio.c @@ -0,0 +1,297 @@ +/**CFile**************************************************************** + + FileName [abcFanio.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Various procedures to connect fanins/fanouts.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcFanio.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Creates fanout/fanin relationship between the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ObjAddFanin( Abc_Obj_t * pObj, Abc_Obj_t * pFanin ) +{ + Abc_Obj_t * pFaninR = Abc_ObjRegular(pFanin); + assert( !Abc_ObjIsComplement(pObj) ); + assert( pObj->pNtk == pFaninR->pNtk ); + assert( pObj->Id >= 0 && pFaninR->Id >= 0 ); + Vec_IntPushMem( pObj->pNtk->pMmStep, &pObj->vFanins, pFaninR->Id ); + Vec_IntPushMem( pObj->pNtk->pMmStep, &pFaninR->vFanouts, pObj->Id ); + if ( Abc_ObjIsComplement(pFanin) ) + Abc_ObjSetFaninC( pObj, Abc_ObjFaninNum(pObj)-1 ); + if ( Abc_ObjIsNet(pObj) && Abc_ObjFaninNum(pObj) > 1 ) + { + int x = 0; + } +// printf( "Adding fanin of %s ", Abc_ObjName(pObj) ); +// printf( "to be %s\n", Abc_ObjName(pFanin) ); +} + + +/**Function************************************************************* + + Synopsis [Destroys fanout/fanin relationship between the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ObjDeleteFanin( Abc_Obj_t * pObj, Abc_Obj_t * pFanin ) +{ + assert( !Abc_ObjIsComplement(pObj) ); + assert( !Abc_ObjIsComplement(pFanin) ); + assert( pObj->pNtk == pFanin->pNtk ); + assert( pObj->Id >= 0 && pFanin->Id >= 0 ); + if ( !Vec_IntRemove( &pObj->vFanins, pFanin->Id ) ) + { + printf( "The obj %d is not found among the fanins of obj %d ...\n", pFanin->Id, pObj->Id ); + return; + } + if ( !Vec_IntRemove( &pFanin->vFanouts, pObj->Id ) ) + { + printf( "The obj %d is not found among the fanouts of obj %d ...\n", pObj->Id, pFanin->Id ); + return; + } +} + + +/**Function************************************************************* + + Synopsis [Destroys fanout/fanin relationship between the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ObjRemoveFanins( Abc_Obj_t * pObj ) +{ + Vec_Int_t * vFaninsOld; + Abc_Obj_t * pFanin; + int k; + // remove old fanins + vFaninsOld = &pObj->vFanins; + for ( k = vFaninsOld->nSize - 1; k >= 0; k-- ) + { + pFanin = Abc_NtkObj( pObj->pNtk, vFaninsOld->pArray[k] ); + Abc_ObjDeleteFanin( pObj, pFanin ); + } + pObj->fCompl0 = 0; + pObj->fCompl1 = 0; + assert( vFaninsOld->nSize == 0 ); +} + +/**Function************************************************************* + + Synopsis [Replaces a fanin of the node.] + + Description [The node is pObj. An old fanin of this node (pFaninOld) has to be + replaced by a new fanin (pFaninNew). Assumes that the node and the old fanin + are not complemented. The new fanin can be complemented. In this case, the + polarity of the new fanin will change, compared to the polarity of the old fanin.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ObjPatchFanin( Abc_Obj_t * pObj, Abc_Obj_t * pFaninOld, Abc_Obj_t * pFaninNew ) +{ + Abc_Obj_t * pFaninNewR = Abc_ObjRegular(pFaninNew); + int iFanin;//, nLats;//, fCompl; + assert( !Abc_ObjIsComplement(pObj) ); + assert( !Abc_ObjIsComplement(pFaninOld) ); + assert( pFaninOld != pFaninNewR ); +// assert( pObj != pFaninOld ); +// assert( pObj != pFaninNewR ); + assert( pObj->pNtk == pFaninOld->pNtk ); + assert( pObj->pNtk == pFaninNewR->pNtk ); + if ( (iFanin = Vec_IntFind( &pObj->vFanins, pFaninOld->Id )) == -1 ) + { + printf( "Node %s is not among", Abc_ObjName(pFaninOld) ); + printf( " the fanins of node %s...\n", Abc_ObjName(pObj) ); + return; + } + + // remember the attributes of the old fanin +// fCompl = Abc_ObjFaninC(pObj, iFanin); + // replace the old fanin entry by the new fanin entry (removes attributes) + Vec_IntWriteEntry( &pObj->vFanins, iFanin, pFaninNewR->Id ); + // set the attributes of the new fanin +// if ( fCompl ^ Abc_ObjIsComplement(pFaninNew) ) +// Abc_ObjSetFaninC( pObj, iFanin ); + if ( Abc_ObjIsComplement(pFaninNew) ) + Abc_ObjXorFaninC( pObj, iFanin ); + +// if ( Abc_NtkIsSeq(pObj->pNtk) && (nLats = Seq_ObjFaninL(pObj, iFanin)) ) +// Seq_ObjSetFaninL( pObj, iFanin, nLats ); + // update the fanout of the fanin + if ( !Vec_IntRemove( &pFaninOld->vFanouts, pObj->Id ) ) + { + printf( "Node %s is not among", Abc_ObjName(pObj) ); + printf( " the fanouts of its old fanin %s...\n", Abc_ObjName(pFaninOld) ); +// return; + } + Vec_IntPushMem( pObj->pNtk->pMmStep, &pFaninNewR->vFanouts, pObj->Id ); +} + +/**Function************************************************************* + + Synopsis [Inserts one-input node of the type specified between the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_ObjInsertBetween( Abc_Obj_t * pNodeIn, Abc_Obj_t * pNodeOut, Abc_ObjType_t Type ) +{ + Abc_Obj_t * pNodeNew; + int iFanoutIndex, iFaninIndex; + // find pNodeOut among the fanouts of pNodeIn + if ( (iFanoutIndex = Vec_IntFind( &pNodeIn->vFanouts, pNodeOut->Id )) == -1 ) + { + printf( "Node %s is not among", Abc_ObjName(pNodeOut) ); + printf( " the fanouts of node %s...\n", Abc_ObjName(pNodeIn) ); + return NULL; + } + // find pNodeIn among the fanins of pNodeOut + if ( (iFaninIndex = Vec_IntFind( &pNodeOut->vFanins, pNodeIn->Id )) == -1 ) + { + printf( "Node %s is not among", Abc_ObjName(pNodeIn) ); + printf( " the fanins of node %s...\n", Abc_ObjName(pNodeOut) ); + return NULL; + } + // create the new node + pNodeNew = Abc_NtkCreateObj( pNodeIn->pNtk, Type ); + // add pNodeIn as fanin and pNodeOut as fanout + Vec_IntPushMem( pNodeNew->pNtk->pMmStep, &pNodeNew->vFanins, pNodeIn->Id ); + Vec_IntPushMem( pNodeNew->pNtk->pMmStep, &pNodeNew->vFanouts, pNodeOut->Id ); + // update the fanout of pNodeIn + Vec_IntWriteEntry( &pNodeIn->vFanouts, iFanoutIndex, pNodeNew->Id ); + // update the fanin of pNodeOut + Vec_IntWriteEntry( &pNodeOut->vFanins, iFaninIndex, pNodeNew->Id ); + return pNodeNew; +} + +/**Function************************************************************* + + Synopsis [Transfers fanout from the old node to the new node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ObjTransferFanout( Abc_Obj_t * pNodeFrom, Abc_Obj_t * pNodeTo ) +{ + Vec_Ptr_t * vFanouts; + int nFanoutsOld, i; + assert( !Abc_ObjIsComplement(pNodeFrom) ); + assert( !Abc_ObjIsComplement(pNodeTo) ); + assert( !Abc_ObjIsPo(pNodeFrom) && !Abc_ObjIsPo(pNodeTo) ); + assert( pNodeFrom->pNtk == pNodeTo->pNtk ); + assert( pNodeFrom != pNodeTo ); + assert( Abc_ObjFanoutNum(pNodeFrom) > 0 ); + // get the fanouts of the old node + nFanoutsOld = Abc_ObjFanoutNum(pNodeTo); + vFanouts = Vec_PtrAlloc( nFanoutsOld ); + Abc_NodeCollectFanouts( pNodeFrom, vFanouts ); + // patch the fanin of each of them + for ( i = 0; i < vFanouts->nSize; i++ ) + Abc_ObjPatchFanin( vFanouts->pArray[i], pNodeFrom, pNodeTo ); + assert( Abc_ObjFanoutNum(pNodeFrom) == 0 ); + assert( Abc_ObjFanoutNum(pNodeTo) == nFanoutsOld + vFanouts->nSize ); + Vec_PtrFree( vFanouts ); +} + +/**Function************************************************************* + + Synopsis [Replaces the node by a new node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ObjReplace( Abc_Obj_t * pNodeOld, Abc_Obj_t * pNodeNew ) +{ + assert( !Abc_ObjIsComplement(pNodeOld) ); + assert( !Abc_ObjIsComplement(pNodeNew) ); + assert( pNodeOld->pNtk == pNodeNew->pNtk ); + assert( pNodeOld != pNodeNew ); + assert( Abc_ObjFanoutNum(pNodeOld) > 0 ); + // transfer the fanouts to the old node + Abc_ObjTransferFanout( pNodeOld, pNodeNew ); + // remove the old node + Abc_NtkDeleteObj_rec( pNodeOld, 1 ); +} + +/**Function************************************************************* + + Synopsis [Returns the index of the fanin in the fanin list of the fanout.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_ObjFanoutFaninNum( Abc_Obj_t * pFanout, Abc_Obj_t * pFanin ) +{ + Abc_Obj_t * pObj; + int i; + Abc_ObjForEachFanin( pFanout, pObj, i ) + if ( pObj == pFanin ) + return i; + return -1; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abc/abcFunc.c b/abc_with_bb_support/src/base/abc/abcFunc.c new file mode 100644 index 000000000..9ad1b0e7a --- /dev/null +++ b/abc_with_bb_support/src/base/abc/abcFunc.c @@ -0,0 +1,1154 @@ +/**CFile**************************************************************** + + FileName [abcFunc.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Transformations between different functionality representations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcFunc.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "main.h" +#include "mio.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define ABC_MUX_CUBES 100000 + +static int Abc_ConvertZddToSop( DdManager * dd, DdNode * zCover, char * pSop, int nFanins, Vec_Str_t * vCube, int fPhase ); +static DdNode * Abc_ConvertAigToBdd( DdManager * dd, Hop_Obj_t * pRoot); +static Hop_Obj_t * Abc_ConvertSopToAig( Hop_Man_t * pMan, char * pSop ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Converts the network from SOP to BDD representation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkSopToBdd( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + DdManager * dd; + int nFaninsMax, i; + + assert( Abc_NtkHasSop(pNtk) ); + + // start the functionality manager + nFaninsMax = Abc_NtkGetFaninMax( pNtk ); + if ( nFaninsMax == 0 ) + printf( "Warning: The network has only constant nodes.\n" ); + + dd = Cudd_Init( nFaninsMax, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 ); + + // convert each node from SOP to BDD + Abc_NtkForEachNode( pNtk, pNode, i ) + { + assert( pNode->pData ); + pNode->pData = Abc_ConvertSopToBdd( dd, pNode->pData ); + if ( pNode->pData == NULL ) + { + printf( "Abc_NtkSopToBdd: Error while converting SOP into BDD.\n" ); + return 0; + } + Cudd_Ref( pNode->pData ); + } + + Extra_MmFlexStop( pNtk->pManFunc ); + pNtk->pManFunc = dd; + + // update the network type + pNtk->ntkFunc = ABC_FUNC_BDD; + return 1; +} + +/**Function************************************************************* + + Synopsis [Converts the node from SOP to BDD representation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Abc_ConvertSopToBdd( DdManager * dd, char * pSop ) +{ + DdNode * bSum, * bCube, * bTemp, * bVar; + char * pCube; + int nVars, Value, v; + + // start the cover + nVars = Abc_SopGetVarNum(pSop); + bSum = Cudd_ReadLogicZero(dd); Cudd_Ref( bSum ); + if ( Abc_SopIsExorType(pSop) ) + { + for ( v = 0; v < nVars; v++ ) + { + bSum = Cudd_bddXor( dd, bTemp = bSum, Cudd_bddIthVar(dd, v) ); Cudd_Ref( bSum ); + Cudd_RecursiveDeref( dd, bTemp ); + } + } + else + { + // check the logic function of the node + Abc_SopForEachCube( pSop, nVars, pCube ) + { + bCube = Cudd_ReadOne(dd); Cudd_Ref( bCube ); + Abc_CubeForEachVar( pCube, Value, v ) + { + if ( Value == '0' ) + bVar = Cudd_Not( Cudd_bddIthVar( dd, v ) ); + else if ( Value == '1' ) + bVar = Cudd_bddIthVar( dd, v ); + else + continue; + bCube = Cudd_bddAnd( dd, bTemp = bCube, bVar ); Cudd_Ref( bCube ); + Cudd_RecursiveDeref( dd, bTemp ); + } + bSum = Cudd_bddOr( dd, bTemp = bSum, bCube ); + Cudd_Ref( bSum ); + Cudd_RecursiveDeref( dd, bTemp ); + Cudd_RecursiveDeref( dd, bCube ); + } + } + // complement the result if necessary + bSum = Cudd_NotCond( bSum, !Abc_SopGetPhase(pSop) ); + Cudd_Deref( bSum ); + return bSum; +} + +/**Function************************************************************* + + Synopsis [Removes complemented SOP covers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkLogicMakeDirectSops( Abc_Ntk_t * pNtk ) +{ + DdManager * dd; + DdNode * bFunc; + Vec_Str_t * vCube; + Abc_Obj_t * pNode; + int nFaninsMax, fFound, i; + + assert( Abc_NtkHasSop(pNtk) ); + + // check if there are nodes with complemented SOPs + fFound = 0; + Abc_NtkForEachNode( pNtk, pNode, i ) + if ( Abc_SopIsComplement(pNode->pData) ) + { + fFound = 1; + break; + } + if ( !fFound ) + return; + + // start the BDD package + nFaninsMax = Abc_NtkGetFaninMax( pNtk ); + if ( nFaninsMax == 0 ) + printf( "Warning: The network has only constant nodes.\n" ); + dd = Cudd_Init( nFaninsMax, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 ); + + // change the cover of negated nodes + vCube = Vec_StrAlloc( 100 ); + Abc_NtkForEachNode( pNtk, pNode, i ) + if ( Abc_SopIsComplement(pNode->pData) ) + { + bFunc = Abc_ConvertSopToBdd( dd, pNode->pData ); Cudd_Ref( bFunc ); + pNode->pData = Abc_ConvertBddToSop( pNtk->pManFunc, dd, bFunc, bFunc, Abc_ObjFaninNum(pNode), 0, vCube, 1 ); + Cudd_RecursiveDeref( dd, bFunc ); + assert( !Abc_SopIsComplement(pNode->pData) ); + } + Vec_StrFree( vCube ); + Extra_StopManager( dd ); +} + + + + + +/**Function************************************************************* + + Synopsis [Converts the network from BDD to SOP representation.] + + Description [If the flag is set to 1, forces the direct phase of all covers.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkBddToSop( Abc_Ntk_t * pNtk, int fDirect ) +{ + Abc_Obj_t * pNode; + Extra_MmFlex_t * pManNew; + DdManager * dd = pNtk->pManFunc; + DdNode * bFunc; + Vec_Str_t * vCube; + int i, fMode; + + if ( fDirect ) + fMode = 1; + else + fMode = -1; + + assert( Abc_NtkHasBdd(pNtk) ); + if ( dd->size > 0 ) + Cudd_zddVarsFromBddVars( dd, 2 ); + // create the new manager + pManNew = Extra_MmFlexStart(); + + // go through the objects + vCube = Vec_StrAlloc( 100 ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + assert( pNode->pData ); + bFunc = pNode->pData; + pNode->pNext = (Abc_Obj_t *)Abc_ConvertBddToSop( pManNew, dd, bFunc, bFunc, Abc_ObjFaninNum(pNode), 0, vCube, fMode ); + if ( pNode->pNext == NULL ) + { + Extra_MmFlexStop( pManNew ); + Abc_NtkCleanNext( pNtk ); +// printf( "Converting from BDDs to SOPs has failed.\n" ); + Vec_StrFree( vCube ); + return 0; + } + } + Vec_StrFree( vCube ); + + // update the network type + pNtk->ntkFunc = ABC_FUNC_SOP; + // set the new manager + pNtk->pManFunc = pManNew; + // transfer from next to data + Abc_NtkForEachNode( pNtk, pNode, i ) + { + Cudd_RecursiveDeref( dd, pNode->pData ); + pNode->pData = pNode->pNext; + pNode->pNext = NULL; + } + + // check for remaining references in the package + Extra_StopManager( dd ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Converts the node from BDD to SOP representation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_ConvertBddToSop( Extra_MmFlex_t * pMan, DdManager * dd, DdNode * bFuncOn, DdNode * bFuncOnDc, int nFanins, int fAllPrimes, Vec_Str_t * vCube, int fMode ) +{ + int fVerify = 0; + char * pSop; + DdNode * bFuncNew, * bCover, * zCover, * zCover0, * zCover1; + int nCubes, nCubes0, nCubes1, fPhase; + + assert( bFuncOn == bFuncOnDc || Cudd_bddLeq( dd, bFuncOn, bFuncOnDc ) ); + if ( Cudd_IsConstant(bFuncOn) || Cudd_IsConstant(bFuncOnDc) ) + { + if ( fMode == -1 ) // if the phase is not known, write constant 1 + fMode = 1; + Vec_StrFill( vCube, nFanins, '-' ); + Vec_StrPush( vCube, '\0' ); + if ( pMan ) + pSop = Extra_MmFlexEntryFetch( pMan, nFanins + 4 ); + else + pSop = ALLOC( char, nFanins + 4 ); + if ( bFuncOn == Cudd_ReadOne(dd) ) + sprintf( pSop, "%s %d\n", vCube->pArray, fMode ); + else + sprintf( pSop, "%s %d\n", vCube->pArray, !fMode ); + return pSop; + } + + + if ( fMode == -1 ) + { // try both phases + assert( fAllPrimes == 0 ); + + // get the ZDD of the negative polarity + bCover = Cudd_zddIsop( dd, Cudd_Not(bFuncOnDc), Cudd_Not(bFuncOn), &zCover0 ); + Cudd_Ref( zCover0 ); + Cudd_Ref( bCover ); + Cudd_RecursiveDeref( dd, bCover ); + nCubes0 = Abc_CountZddCubes( dd, zCover0 ); + + // get the ZDD of the positive polarity + bCover = Cudd_zddIsop( dd, bFuncOn, bFuncOnDc, &zCover1 ); + Cudd_Ref( zCover1 ); + Cudd_Ref( bCover ); + Cudd_RecursiveDeref( dd, bCover ); + nCubes1 = Abc_CountZddCubes( dd, zCover1 ); + + // compare the number of cubes + if ( nCubes1 <= nCubes0 ) + { // use positive polarity + nCubes = nCubes1; + zCover = zCover1; + Cudd_RecursiveDerefZdd( dd, zCover0 ); + fPhase = 1; + } + else + { // use negative polarity + nCubes = nCubes0; + zCover = zCover0; + Cudd_RecursiveDerefZdd( dd, zCover1 ); + fPhase = 0; + } + } + else if ( fMode == 0 ) + { + // get the ZDD of the negative polarity + if ( fAllPrimes ) + { + zCover = Extra_zddPrimes( dd, Cudd_Not(bFuncOnDc) ); + Cudd_Ref( zCover ); + } + else + { + bCover = Cudd_zddIsop( dd, Cudd_Not(bFuncOnDc), Cudd_Not(bFuncOn), &zCover ); + Cudd_Ref( zCover ); + Cudd_Ref( bCover ); + Cudd_RecursiveDeref( dd, bCover ); + } + nCubes = Abc_CountZddCubes( dd, zCover ); + fPhase = 0; + } + else if ( fMode == 1 ) + { + // get the ZDD of the positive polarity + if ( fAllPrimes ) + { + zCover = Extra_zddPrimes( dd, bFuncOnDc ); + Cudd_Ref( zCover ); + } + else + { + bCover = Cudd_zddIsop( dd, bFuncOn, bFuncOnDc, &zCover ); + Cudd_Ref( zCover ); + Cudd_Ref( bCover ); + Cudd_RecursiveDeref( dd, bCover ); + } + nCubes = Abc_CountZddCubes( dd, zCover ); + fPhase = 1; + } + else + { + assert( 0 ); + } + + if ( nCubes > ABC_MUX_CUBES ) + { + Cudd_RecursiveDerefZdd( dd, zCover ); + printf( "The number of cubes exceeded the predefined limit (%d).\n", ABC_MUX_CUBES ); + return NULL; + } + + // allocate memory for the cover + if ( pMan ) + pSop = Extra_MmFlexEntryFetch( pMan, (nFanins + 3) * nCubes + 1 ); + else + pSop = ALLOC( char, (nFanins + 3) * nCubes + 1 ); + pSop[(nFanins + 3) * nCubes] = 0; + // create the SOP + Vec_StrFill( vCube, nFanins, '-' ); + Vec_StrPush( vCube, '\0' ); + Abc_ConvertZddToSop( dd, zCover, pSop, nFanins, vCube, fPhase ); + Cudd_RecursiveDerefZdd( dd, zCover ); + + // verify + if ( fVerify ) + { + bFuncNew = Abc_ConvertSopToBdd( dd, pSop ); Cudd_Ref( bFuncNew ); + if ( bFuncOn == bFuncOnDc ) + { + if ( bFuncNew != bFuncOn ) + printf( "Verification failed.\n" ); + } + else + { + if ( !Cudd_bddLeq(dd, bFuncOn, bFuncNew) || !Cudd_bddLeq(dd, bFuncNew, bFuncOnDc) ) + printf( "Verification failed.\n" ); + } + Cudd_RecursiveDeref( dd, bFuncNew ); + } + return pSop; +} + +/**Function************************************************************* + + Synopsis [Derive the SOP from the ZDD representation of the cubes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ConvertZddToSop_rec( DdManager * dd, DdNode * zCover, char * pSop, int nFanins, Vec_Str_t * vCube, int fPhase, int * pnCubes ) +{ + DdNode * zC0, * zC1, * zC2; + int Index; + + if ( zCover == dd->zero ) + return; + if ( zCover == dd->one ) + { + char * pCube; + pCube = pSop + (*pnCubes) * (nFanins + 3); + sprintf( pCube, "%s %d\n", vCube->pArray, fPhase ); + (*pnCubes)++; + return; + } + Index = zCover->index/2; + assert( Index < nFanins ); + extraDecomposeCover( dd, zCover, &zC0, &zC1, &zC2 ); + vCube->pArray[Index] = '0'; + Abc_ConvertZddToSop_rec( dd, zC0, pSop, nFanins, vCube, fPhase, pnCubes ); + vCube->pArray[Index] = '1'; + Abc_ConvertZddToSop_rec( dd, zC1, pSop, nFanins, vCube, fPhase, pnCubes ); + vCube->pArray[Index] = '-'; + Abc_ConvertZddToSop_rec( dd, zC2, pSop, nFanins, vCube, fPhase, pnCubes ); +} + +/**Function************************************************************* + + Synopsis [Derive the BDD for the function in the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_ConvertZddToSop( DdManager * dd, DdNode * zCover, char * pSop, int nFanins, Vec_Str_t * vCube, int fPhase ) +{ + int nCubes = 0; + Abc_ConvertZddToSop_rec( dd, zCover, pSop, nFanins, vCube, fPhase, &nCubes ); + return nCubes; +} + + +/**Function************************************************************* + + Synopsis [Computes the SOPs of the negative and positive phase of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeBddToCnf( Abc_Obj_t * pNode, Extra_MmFlex_t * pMmMan, Vec_Str_t * vCube, int fAllPrimes, char ** ppSop0, char ** ppSop1 ) +{ + assert( Abc_NtkHasBdd(pNode->pNtk) ); + *ppSop0 = Abc_ConvertBddToSop( pMmMan, pNode->pNtk->pManFunc, pNode->pData, pNode->pData, Abc_ObjFaninNum(pNode), fAllPrimes, vCube, 0 ); + *ppSop1 = Abc_ConvertBddToSop( pMmMan, pNode->pNtk->pManFunc, pNode->pData, pNode->pData, Abc_ObjFaninNum(pNode), fAllPrimes, vCube, 1 ); +} + + + + +/**Function************************************************************* + + Synopsis [Count the number of paths in the ZDD.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_CountZddCubes_rec( DdManager * dd, DdNode * zCover, int * pnCubes ) +{ + DdNode * zC0, * zC1, * zC2; + if ( zCover == dd->zero ) + return; + if ( zCover == dd->one ) + { + (*pnCubes)++; + return; + } + if ( (*pnCubes) > ABC_MUX_CUBES ) + return; + extraDecomposeCover( dd, zCover, &zC0, &zC1, &zC2 ); + Abc_CountZddCubes_rec( dd, zC0, pnCubes ); + Abc_CountZddCubes_rec( dd, zC1, pnCubes ); + Abc_CountZddCubes_rec( dd, zC2, pnCubes ); +} + +/**Function************************************************************* + + Synopsis [Count the number of paths in the ZDD.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CountZddCubes( DdManager * dd, DdNode * zCover ) +{ + int nCubes = 0; + Abc_CountZddCubes_rec( dd, zCover, &nCubes ); + return nCubes; +} + + +/**Function************************************************************* + + Synopsis [Converts the network from SOP to AIG representation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkSopToAig( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + Hop_Man_t * pMan; + int i; + + assert( Abc_NtkHasSop(pNtk) ); + + // start the functionality manager + pMan = Hop_ManStart(); + + // convert each node from SOP to BDD + Abc_NtkForEachNode( pNtk, pNode, i ) + { + assert( pNode->pData ); + pNode->pData = Abc_ConvertSopToAig( pMan, pNode->pData ); + if ( pNode->pData == NULL ) + { + printf( "Abc_NtkSopToAig: Error while converting SOP into AIG.\n" ); + return 0; + } + } + Extra_MmFlexStop( pNtk->pManFunc ); + pNtk->pManFunc = pMan; + + // update the network type + pNtk->ntkFunc = ABC_FUNC_AIG; + return 1; +} + + +/**Function************************************************************* + + Synopsis [Strashes one logic node using its SOP.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Abc_ConvertSopToAigInternal( Hop_Man_t * pMan, char * pSop ) +{ + Hop_Obj_t * pAnd, * pSum; + int i, Value, nFanins; + char * pCube; + // get the number of variables + nFanins = Abc_SopGetVarNum(pSop); + // go through the cubes of the node's SOP + pSum = Hop_ManConst0(pMan); + Abc_SopForEachCube( pSop, nFanins, pCube ) + { + // create the AND of literals + pAnd = Hop_ManConst1(pMan); + Abc_CubeForEachVar( pCube, Value, i ) + { + if ( Value == '1' ) + pAnd = Hop_And( pMan, pAnd, Hop_IthVar(pMan,i) ); + else if ( Value == '0' ) + pAnd = Hop_And( pMan, pAnd, Hop_Not(Hop_IthVar(pMan,i)) ); + } + // add to the sum of cubes + pSum = Hop_Or( pMan, pSum, pAnd ); + } + // decide whether to complement the result + if ( Abc_SopIsComplement(pSop) ) + pSum = Hop_Not(pSum); + return pSum; +} + +/**Function************************************************************* + + Synopsis [Converts the network from AIG to BDD representation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Abc_ConvertSopToAig( Hop_Man_t * pMan, char * pSop ) +{ + extern Hop_Obj_t * Dec_GraphFactorSop( Hop_Man_t * pMan, char * pSop ); + int fUseFactor = 1; + // consider the constant node + if ( Abc_SopGetVarNum(pSop) == 0 ) + return Hop_NotCond( Hop_ManConst1(pMan), Abc_SopIsConst0(pSop) ); + // consider the special case of EXOR function + if ( Abc_SopIsExorType(pSop) ) + return Hop_NotCond( Hop_CreateExor(pMan, Abc_SopGetVarNum(pSop)), Abc_SopIsComplement(pSop) ); + // decide when to use factoring + if ( fUseFactor && Abc_SopGetVarNum(pSop) > 2 && Abc_SopGetCubeNum(pSop) > 1 ) + return Dec_GraphFactorSop( pMan, pSop ); + return Abc_ConvertSopToAigInternal( pMan, pSop ); +} + +/**Function************************************************************* + + Synopsis [Converts the network from AIG to BDD representation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkAigToBdd( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + Hop_Man_t * pMan; + DdManager * dd; + int nFaninsMax, i; + + assert( Abc_NtkHasAig(pNtk) ); + + // start the functionality manager + nFaninsMax = Abc_NtkGetFaninMax( pNtk ); + if ( nFaninsMax == 0 ) + printf( "Warning: The network has only constant nodes.\n" ); + + dd = Cudd_Init( nFaninsMax, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 ); + + // set the mapping of elementary AIG nodes into the elementary BDD nodes + pMan = pNtk->pManFunc; + assert( Hop_ManPiNum(pMan) >= nFaninsMax ); + for ( i = 0; i < nFaninsMax; i++ ) + { + Hop_ManPi(pMan, i)->pData = Cudd_bddIthVar(dd, i); + Cudd_Ref( Hop_ManPi(pMan, i)->pData ); + } + + // convert each node from SOP to BDD + Abc_NtkForEachNode( pNtk, pNode, i ) + { + assert( pNode->pData ); + pNode->pData = Abc_ConvertAigToBdd( dd, pNode->pData ); + if ( pNode->pData == NULL ) + { + printf( "Abc_NtkSopToBdd: Error while converting SOP into BDD.\n" ); + return 0; + } + Cudd_Ref( pNode->pData ); + } + + // dereference intermediate BDD nodes + for ( i = 0; i < nFaninsMax; i++ ) + Cudd_RecursiveDeref( dd, Hop_ManPi(pMan, i)->pData ); + + Hop_ManStop( pNtk->pManFunc ); + pNtk->pManFunc = dd; + + // update the network type + pNtk->ntkFunc = ABC_FUNC_BDD; + return 1; +} + +/**Function************************************************************* + + Synopsis [Construct BDDs and mark AIG nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ConvertAigToBdd_rec1( DdManager * dd, Hop_Obj_t * pObj ) +{ + assert( !Hop_IsComplement(pObj) ); + if ( !Hop_ObjIsNode(pObj) || Hop_ObjIsMarkA(pObj) ) + return; + Abc_ConvertAigToBdd_rec1( dd, Hop_ObjFanin0(pObj) ); + Abc_ConvertAigToBdd_rec1( dd, Hop_ObjFanin1(pObj) ); + pObj->pData = Cudd_bddAnd( dd, (DdNode *)Hop_ObjChild0Copy(pObj), (DdNode *)Hop_ObjChild1Copy(pObj) ); + Cudd_Ref( pObj->pData ); + assert( !Hop_ObjIsMarkA(pObj) ); // loop detection + Hop_ObjSetMarkA( pObj ); +} + +/**Function************************************************************* + + Synopsis [Dereference BDDs and unmark AIG nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ConvertAigToBdd_rec2( DdManager * dd, Hop_Obj_t * pObj ) +{ + assert( !Hop_IsComplement(pObj) ); + if ( !Hop_ObjIsNode(pObj) || !Hop_ObjIsMarkA(pObj) ) + return; + Abc_ConvertAigToBdd_rec2( dd, Hop_ObjFanin0(pObj) ); + Abc_ConvertAigToBdd_rec2( dd, Hop_ObjFanin1(pObj) ); + Cudd_RecursiveDeref( dd, pObj->pData ); + pObj->pData = NULL; + assert( Hop_ObjIsMarkA(pObj) ); // loop detection + Hop_ObjClearMarkA( pObj ); +} + +/**Function************************************************************* + + Synopsis [Converts the network from AIG to BDD representation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Abc_ConvertAigToBdd( DdManager * dd, Hop_Obj_t * pRoot ) +{ + DdNode * bFunc; + // check the case of a constant + if ( Hop_ObjIsConst1( Hop_Regular(pRoot) ) ) + return Cudd_NotCond( Cudd_ReadOne(dd), Hop_IsComplement(pRoot) ); + // construct BDD + Abc_ConvertAigToBdd_rec1( dd, Hop_Regular(pRoot) ); + // hold on to the result + bFunc = Cudd_NotCond( Hop_Regular(pRoot)->pData, Hop_IsComplement(pRoot) ); Cudd_Ref( bFunc ); + // dereference BDD + Abc_ConvertAigToBdd_rec2( dd, Hop_Regular(pRoot) ); + // return the result + Cudd_Deref( bFunc ); + return bFunc; +} + + + +/**Function************************************************************* + + Synopsis [Construct BDDs and mark AIG nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_ConvertAigToTruth_rec1( Hop_Obj_t * pObj ) +{ + int Counter = 0; + assert( !Hop_IsComplement(pObj) ); + if ( !Hop_ObjIsNode(pObj) || Hop_ObjIsMarkA(pObj) ) + return 0; + Counter += Abc_ConvertAigToTruth_rec1( Hop_ObjFanin0(pObj) ); + Counter += Abc_ConvertAigToTruth_rec1( Hop_ObjFanin1(pObj) ); + assert( !Hop_ObjIsMarkA(pObj) ); // loop detection + Hop_ObjSetMarkA( pObj ); + return Counter + 1; +} + +/**Function************************************************************* + + Synopsis [Computes truth table of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned * Abc_ConvertAigToTruth_rec2( Hop_Obj_t * pObj, Vec_Int_t * vTruth, int nWords ) +{ + unsigned * pTruth, * pTruth0, * pTruth1; + int i; + assert( !Hop_IsComplement(pObj) ); + if ( !Hop_ObjIsNode(pObj) || !Hop_ObjIsMarkA(pObj) ) + return pObj->pData; + // compute the truth tables of the fanins + pTruth0 = Abc_ConvertAigToTruth_rec2( Hop_ObjFanin0(pObj), vTruth, nWords ); + pTruth1 = Abc_ConvertAigToTruth_rec2( Hop_ObjFanin1(pObj), vTruth, nWords ); + // creat the truth table of the node + pTruth = Vec_IntFetch( vTruth, nWords ); + if ( Hop_ObjIsExor(pObj) ) + for ( i = 0; i < nWords; i++ ) + pTruth[i] = pTruth0[i] ^ pTruth1[i]; + else if ( !Hop_ObjFaninC0(pObj) && !Hop_ObjFaninC1(pObj) ) + for ( i = 0; i < nWords; i++ ) + pTruth[i] = pTruth0[i] & pTruth1[i]; + else if ( !Hop_ObjFaninC0(pObj) && Hop_ObjFaninC1(pObj) ) + for ( i = 0; i < nWords; i++ ) + pTruth[i] = pTruth0[i] & ~pTruth1[i]; + else if ( Hop_ObjFaninC0(pObj) && !Hop_ObjFaninC1(pObj) ) + for ( i = 0; i < nWords; i++ ) + pTruth[i] = ~pTruth0[i] & pTruth1[i]; + else // if ( Hop_ObjFaninC0(pObj) && Hop_ObjFaninC1(pObj) ) + for ( i = 0; i < nWords; i++ ) + pTruth[i] = ~pTruth0[i] & ~pTruth1[i]; + assert( Hop_ObjIsMarkA(pObj) ); // loop detection + Hop_ObjClearMarkA( pObj ); + pObj->pData = pTruth; + return pTruth; +} + +/**Function************************************************************* + + Synopsis [Computes truth table of the node.] + + Description [Assumes that the structural support is no more than 8 inputs. + Uses array vTruth to store temporary truth tables. The returned pointer should + be used immediately.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned * Abc_ConvertAigToTruth( Hop_Man_t * p, Hop_Obj_t * pRoot, int nVars, Vec_Int_t * vTruth, int fMsbFirst ) +{ + static unsigned uTruths[8][8] = { // elementary truth tables + { 0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA }, + { 0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC }, + { 0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0 }, + { 0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00 }, + { 0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000 }, + { 0x00000000,0xFFFFFFFF,0x00000000,0xFFFFFFFF,0x00000000,0xFFFFFFFF,0x00000000,0xFFFFFFFF }, + { 0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF,0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF }, + { 0x00000000,0x00000000,0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF } + }; + Hop_Obj_t * pObj; + unsigned * pTruth, * pTruth2; + int i, nWords, nNodes; + Vec_Ptr_t * vTtElems; + + // if the number of variables is more than 8, allocate truth tables + if ( nVars > 8 ) + vTtElems = Vec_PtrAllocTruthTables( nVars ); + else + vTtElems = NULL; + + // clear the data fields and set marks + nNodes = Abc_ConvertAigToTruth_rec1( pRoot ); + // prepare memory + nWords = Hop_TruthWordNum( nVars ); + Vec_IntClear( vTruth ); + Vec_IntGrow( vTruth, nWords * (nNodes+1) ); + pTruth = Vec_IntFetch( vTruth, nWords ); + // check the case of a constant + if ( Hop_ObjIsConst1( Hop_Regular(pRoot) ) ) + { + assert( nNodes == 0 ); + if ( Hop_IsComplement(pRoot) ) + Extra_TruthClear( pTruth, nVars ); + else + Extra_TruthFill( pTruth, nVars ); + return pTruth; + } + // set elementary truth tables at the leaves + assert( nVars <= Hop_ManPiNum(p) ); +// assert( Hop_ManPiNum(p) <= 8 ); + if ( fMsbFirst ) + { + Hop_ManForEachPi( p, pObj, i ) + { + if ( vTtElems ) + pObj->pData = Vec_PtrEntry(vTtElems, nVars-1-i); + else + pObj->pData = (void *)uTruths[nVars-1-i]; + } + } + else + { + Hop_ManForEachPi( p, pObj, i ) + { + if ( vTtElems ) + pObj->pData = Vec_PtrEntry(vTtElems, i); + else + pObj->pData = (void *)uTruths[i]; + } + } + // clear the marks and compute the truth table + pTruth2 = Abc_ConvertAigToTruth_rec2( pRoot, vTruth, nWords ); + // copy the result + Extra_TruthCopy( pTruth, pTruth2, nVars ); + if ( vTtElems ) + Vec_PtrFree( vTtElems ); + return pTruth; +} + + +/**Function************************************************************* + + Synopsis [Construct BDDs and mark AIG nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ConvertAigToAig_rec( Abc_Ntk_t * pNtkAig, Hop_Obj_t * pObj ) +{ + assert( !Hop_IsComplement(pObj) ); + if ( !Hop_ObjIsNode(pObj) || Hop_ObjIsMarkA(pObj) ) + return; + Abc_ConvertAigToAig_rec( pNtkAig, Hop_ObjFanin0(pObj) ); + Abc_ConvertAigToAig_rec( pNtkAig, Hop_ObjFanin1(pObj) ); + pObj->pData = Abc_AigAnd( pNtkAig->pManFunc, (Abc_Obj_t *)Hop_ObjChild0Copy(pObj), (Abc_Obj_t *)Hop_ObjChild1Copy(pObj) ); + assert( !Hop_ObjIsMarkA(pObj) ); // loop detection + Hop_ObjSetMarkA( pObj ); +} + +/**Function************************************************************* + + Synopsis [Converts the network from AIG to BDD representation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_ConvertAigToAig( Abc_Ntk_t * pNtkAig, Abc_Obj_t * pObjOld ) +{ + Hop_Man_t * pHopMan; + Hop_Obj_t * pRoot; + Abc_Obj_t * pFanin; + int i; + // get the local AIG + pHopMan = pObjOld->pNtk->pManFunc; + pRoot = pObjOld->pData; + // check the case of a constant + if ( Hop_ObjIsConst1( Hop_Regular(pRoot) ) ) + return Abc_ObjNotCond( Abc_AigConst1(pNtkAig), Hop_IsComplement(pRoot) ); + // assign the fanin nodes + Abc_ObjForEachFanin( pObjOld, pFanin, i ) + { + assert( pFanin->pCopy != NULL ); + Hop_ManPi(pHopMan, i)->pData = pFanin->pCopy; + } + // construct the AIG + Abc_ConvertAigToAig_rec( pNtkAig, Hop_Regular(pRoot) ); + Hop_ConeUnmark_rec( Hop_Regular(pRoot) ); + // return the result + return Abc_ObjNotCond( Hop_Regular(pRoot)->pData, Hop_IsComplement(pRoot) ); +} + + +/**Function************************************************************* + + Synopsis [Unmaps the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkMapToSop( Abc_Ntk_t * pNtk ) +{ + extern void * Abc_FrameReadLibGen(); + Abc_Obj_t * pNode; + char * pSop; + int i; + + assert( Abc_NtkHasMapping(pNtk) ); + // update the functionality manager + assert( pNtk->pManFunc == Abc_FrameReadLibGen() ); + pNtk->pManFunc = Extra_MmFlexStart(); + pNtk->ntkFunc = ABC_FUNC_SOP; + // update the nodes + Abc_NtkForEachNode( pNtk, pNode, i ) + { + pSop = Mio_GateReadSop(pNode->pData); + assert( Abc_SopGetVarNum(pSop) == Abc_ObjFaninNum(pNode) ); + pNode->pData = Abc_SopRegister( pNtk->pManFunc, pSop ); + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Converts SOP functions into BLIF-MV functions.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkSopToBlifMv( Abc_Ntk_t * pNtk ) +{ + return 1; +} + +/**Function************************************************************* + + Synopsis [Convers logic network to the SOP form.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkToSop( Abc_Ntk_t * pNtk, int fDirect ) +{ + assert( !Abc_NtkIsStrash(pNtk) ); + if ( Abc_NtkHasSop(pNtk) ) + { + if ( !fDirect ) + return 1; + if ( !Abc_NtkSopToBdd(pNtk) ) + return 0; + return Abc_NtkBddToSop(pNtk, fDirect); + } + if ( Abc_NtkHasMapping(pNtk) ) + return Abc_NtkMapToSop(pNtk); + if ( Abc_NtkHasBdd(pNtk) ) + return Abc_NtkBddToSop(pNtk, fDirect); + if ( Abc_NtkHasAig(pNtk) ) + { + if ( !Abc_NtkAigToBdd(pNtk) ) + return 0; + return Abc_NtkBddToSop(pNtk, fDirect); + } + assert( 0 ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Convers logic network to the SOP form.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkToBdd( Abc_Ntk_t * pNtk ) +{ + assert( !Abc_NtkIsStrash(pNtk) ); + if ( Abc_NtkHasBdd(pNtk) ) + return 1; + if ( Abc_NtkHasMapping(pNtk) ) + { + Abc_NtkMapToSop(pNtk); + return Abc_NtkSopToBdd(pNtk); + } + if ( Abc_NtkHasSop(pNtk) ) + return Abc_NtkSopToBdd(pNtk); + if ( Abc_NtkHasAig(pNtk) ) + return Abc_NtkAigToBdd(pNtk); + assert( 0 ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Convers logic network to the SOP form.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkToAig( Abc_Ntk_t * pNtk ) +{ + assert( !Abc_NtkIsStrash(pNtk) ); + if ( Abc_NtkHasAig(pNtk) ) + return 1; + if ( Abc_NtkHasMapping(pNtk) ) + { + Abc_NtkMapToSop(pNtk); + return Abc_NtkSopToAig(pNtk); + } + if ( Abc_NtkHasBdd(pNtk) ) + { + if ( !Abc_NtkBddToSop(pNtk,0) ) + return 0; + return Abc_NtkSopToAig(pNtk); + } + if ( Abc_NtkHasSop(pNtk) ) + return Abc_NtkSopToAig(pNtk); + assert( 0 ); + return 0; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abc/abcHie.c b/abc_with_bb_support/src/base/abc/abcHie.c new file mode 100644 index 000000000..7b7d40be1 --- /dev/null +++ b/abc_with_bb_support/src/base/abc/abcHie.c @@ -0,0 +1,492 @@ +/**CFile**************************************************************** + + FileName [abcHie.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Procedures to handle hierarchy.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcHie.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Recursively flattens logic hierarchy of the netlist.] + + Description [When this procedure is called, the PI/PO nets of the old + netlist point to the corresponding nets of the flattened netlist.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkFlattenLogicHierarchy_rec( Abc_Ntk_t * pNtkNew, Abc_Ntk_t * pNtk, int * pCounter ) +{ + char Suffix[1000] = {0}; + Abc_Ntk_t * pNtkModel; + Abc_Obj_t * pObj, * pTerm, * pNet, * pFanin; + int i, k; + + // process the blackbox + if ( Abc_NtkHasBlackbox(pNtk) ) + { + // duplicate the blackbox + assert( Abc_NtkBoxNum(pNtk) == 1 ); + pObj = Abc_NtkBox( pNtk, 0 ); + Abc_NtkDupBox( pNtkNew, pObj, 1 ); + pObj->pCopy->pData = pNtk; + + // connect blackbox fanins to the PI nets + assert( Abc_ObjFaninNum(pObj->pCopy) == Abc_NtkPiNum(pNtk) ); + Abc_NtkForEachPi( pNtk, pTerm, i ) + Abc_ObjAddFanin( Abc_ObjFanin(pObj->pCopy,i), Abc_ObjFanout0(pTerm)->pCopy ); + + // connect blackbox fanouts to the PO nets + assert( Abc_ObjFanoutNum(pObj->pCopy) == Abc_NtkPoNum(pNtk) ); + Abc_NtkForEachPo( pNtk, pTerm, i ) + Abc_ObjAddFanin( Abc_ObjFanin0(pTerm)->pCopy, Abc_ObjFanout(pObj->pCopy,i) ); + return; + } + + (*pCounter)++; + + // create the prefix, which will be appended to the internal names + if ( *pCounter ) + sprintf( Suffix, "_%s_%d", Abc_NtkName(pNtk), *pCounter ); + + // duplicate nets of all boxes, including latches + Abc_NtkForEachBox( pNtk, pObj, i ) + { + Abc_ObjForEachFanin( pObj, pTerm, k ) + { + pNet = Abc_ObjFanin0(pTerm); + if ( pNet->pCopy ) + continue; + pNet->pCopy = Abc_NtkFindOrCreateNet( pNtkNew, Abc_ObjNameSuffix(pNet, Suffix) ); + } + Abc_ObjForEachFanout( pObj, pTerm, k ) + { + pNet = Abc_ObjFanout0(pTerm); + if ( pNet->pCopy ) + continue; + pNet->pCopy = Abc_NtkFindOrCreateNet( pNtkNew, Abc_ObjNameSuffix(pNet, Suffix) ); + } + } + + // mark objects that will not be used + Abc_NtkIncrementTravId( pNtk ); + Abc_NtkForEachPi( pNtk, pTerm, i ) + Abc_NodeSetTravIdCurrent( pTerm ); + Abc_NtkForEachPo( pNtk, pTerm, i ) + Abc_NodeSetTravIdCurrent( pTerm ); + Abc_NtkForEachBox( pNtk, pObj, i ) + { + if ( Abc_ObjIsLatch(pObj) ) + continue; + Abc_NodeSetTravIdCurrent( pObj ); + Abc_ObjForEachFanin( pObj, pTerm, k ) + Abc_NodeSetTravIdCurrent( pTerm ); + Abc_ObjForEachFanout( pObj, pTerm, k ) + Abc_NodeSetTravIdCurrent( pTerm ); + } + + // duplicate objects that do not have prototypes yet + Abc_NtkForEachObj( pNtk, pObj, i ) + { + if ( Abc_NodeIsTravIdCurrent(pObj) ) + continue; + if ( pObj->pCopy ) + continue; + Abc_NtkDupObj( pNtkNew, pObj, 0 ); + } + + // connect objects + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( !Abc_NodeIsTravIdCurrent(pObj) ) + Abc_ObjForEachFanin( pObj, pFanin, k ) + if ( !Abc_NodeIsTravIdCurrent(pFanin) ) + Abc_ObjAddFanin( pObj->pCopy, pFanin->pCopy ); + + // call recursively + Abc_NtkForEachBox( pNtk, pObj, i ) + { + if ( Abc_ObjIsLatch(pObj) ) + continue; + pNtkModel = pObj->pData; + // check the match between the number of actual and formal parameters + assert( Abc_ObjFaninNum(pObj) == Abc_NtkPiNum(pNtkModel) ); + assert( Abc_ObjFanoutNum(pObj) == Abc_NtkPoNum(pNtkModel) ); + // clean the node copy fields + Abc_NtkCleanCopy( pNtkModel ); + // map PIs/POs + Abc_ObjForEachFanin( pObj, pTerm, k ) + Abc_ObjFanout0( Abc_NtkPi(pNtkModel, k) )->pCopy = Abc_ObjFanin0(pTerm)->pCopy; + Abc_ObjForEachFanout( pObj, pTerm, k ) + Abc_ObjFanin0( Abc_NtkPo(pNtkModel, k) )->pCopy = Abc_ObjFanout0(pTerm)->pCopy; + // call recursively + Abc_NtkFlattenLogicHierarchy_rec( pNtkNew, pNtkModel, pCounter ); + } + + // if it is a BLIF-MV netlist transfer the values of all nets + if ( Abc_NtkHasBlifMv(pNtk) && Abc_NtkMvVar(pNtk) ) + { + if ( Abc_NtkMvVar( pNtkNew ) == NULL ) + Abc_NtkStartMvVars( pNtkNew ); + Abc_NtkForEachNet( pNtk, pObj, i ) + Abc_NtkSetMvVarValues( pObj->pCopy, Abc_ObjMvVarNum(pObj) ); + } +} + +/**Function************************************************************* + + Synopsis [Flattens the logic hierarchy of the netlist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFlattenLogicHierarchy( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pTerm, * pNet; + int i, Counter; + extern Abc_Lib_t * Abc_LibDupBlackboxes( Abc_Lib_t * pLib, Abc_Ntk_t * pNtkSave ); + + assert( Abc_NtkIsNetlist(pNtk) ); + // start the network + pNtkNew = Abc_NtkAlloc( pNtk->ntkType, pNtk->ntkFunc, 1 ); + // duplicate the name and the spec + pNtkNew->pName = Extra_UtilStrsav(pNtk->pName); + pNtkNew->pSpec = Extra_UtilStrsav(pNtk->pSpec); + + // clean the node copy fields + Abc_NtkCleanCopy( pNtk ); + + // duplicate PIs/POs and their nets + Abc_NtkForEachPi( pNtk, pTerm, i ) + { + Abc_NtkDupObj( pNtkNew, pTerm, 0 ); + pNet = Abc_ObjFanout0( pTerm ); + pNet->pCopy = Abc_NtkFindOrCreateNet( pNtkNew, Abc_ObjName(pNet) ); + Abc_ObjAddFanin( pNet->pCopy, pTerm->pCopy ); + } + Abc_NtkForEachPo( pNtk, pTerm, i ) + { + Abc_NtkDupObj( pNtkNew, pTerm, 0 ); + pNet = Abc_ObjFanin0( pTerm ); + pNet->pCopy = Abc_NtkFindOrCreateNet( pNtkNew, Abc_ObjName(pNet) ); + Abc_ObjAddFanin( pTerm->pCopy, pNet->pCopy ); + } + + // recursively flatten hierarchy, create internal logic, add new PI/PO names if there are black boxes + Counter = -1; + Abc_NtkFlattenLogicHierarchy_rec( pNtkNew, pNtk, &Counter ); + printf( "Hierarchy reader flattened %d instances of logic boxes and left %d black boxes.\n", + Counter, Abc_NtkBlackboxNum(pNtkNew) ); + + if ( pNtk->pDesign ) + { + // pass on the design + assert( Vec_PtrEntry(pNtk->pDesign->vTops, 0) == pNtk ); + pNtkNew->pDesign = Abc_LibDupBlackboxes( pNtk->pDesign, pNtkNew ); + // update the pointers + Abc_NtkForEachBlackbox( pNtkNew, pTerm, i ) + pTerm->pData = ((Abc_Ntk_t *)pTerm->pData)->pCopy; + } + + // copy the timing information +// Abc_ManTimeDup( pNtk, pNtkNew ); + // duplicate EXDC + if ( pNtk->pExdc ) + printf( "EXDC is not transformed.\n" ); + if ( !Abc_NtkCheck( pNtkNew ) ) + { + fprintf( stdout, "Abc_NtkFlattenLogicHierarchy(): Network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Extracts blackboxes by making them into additional PIs/POs.] + + Description [The input netlist has not logic hierarchy. The resulting + netlist has additional PIs/POs for each blackbox input/output.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkConvertBlackboxes( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj, * pNet, * pFanin, * pTerm; + int i, k; + + assert( Abc_NtkIsNetlist(pNtk) ); + assert( Abc_NtkWhiteboxNum(pNtk) == 0 ); + + // start the network + pNtkNew = Abc_NtkAlloc( pNtk->ntkType, pNtk->ntkFunc, 1 ); + // duplicate the name and the spec + pNtkNew->pName = Extra_UtilStrsav( pNtk->pName ); + pNtkNew->pSpec = Extra_UtilStrsav( pNtk->pSpec ); + + // clean the node copy fields + Abc_NtkCleanCopy( pNtk ); + + // mark the nodes that should not be connected + Abc_NtkIncrementTravId( pNtk ); + Abc_NtkForEachBlackbox( pNtk, pObj, i ) + Abc_NodeSetTravIdCurrent( pObj ); + Abc_NtkForEachCi( pNtk, pTerm, i ) + Abc_NodeSetTravIdCurrent( pTerm ); + Abc_NtkForEachCo( pNtk, pTerm, i ) + Abc_NodeSetTravIdCurrent( pTerm ); + // unmark PIs and LIs/LOs + Abc_NtkForEachPi( pNtk, pTerm, i ) + Abc_NodeSetTravIdPrevious( pTerm ); + Abc_NtkForEachLatchInput( pNtk, pTerm, i ) + Abc_NodeSetTravIdPrevious( pTerm ); + Abc_NtkForEachLatchOutput( pNtk, pTerm, i ) + Abc_NodeSetTravIdPrevious( pTerm ); + // copy the box outputs + Abc_NtkForEachBlackbox( pNtk, pObj, i ) + Abc_ObjForEachFanout( pObj, pTerm, k ) + pTerm->pCopy = Abc_NtkCreatePi( pNtkNew ); + + // duplicate other objects + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( !Abc_NodeIsTravIdCurrent(pObj) ) + Abc_NtkDupObj( pNtkNew, pObj, Abc_ObjIsNet(pObj) ); + + // connect all objects + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( !Abc_NodeIsTravIdCurrent(pObj) ) + Abc_ObjForEachFanin( pObj, pFanin, k ) + Abc_ObjAddFanin( pObj->pCopy, pFanin->pCopy ); + + // create unique PO for each net feeding into blackboxes or POs + Abc_NtkIncrementTravId( pNtk ); + Abc_NtkForEachCo( pNtk, pTerm, i ) + { + // skip latch inputs + assert( Abc_ObjFanoutNum(pTerm) <= 1 ); + if ( Abc_ObjFanoutNum(pTerm) > 0 && Abc_ObjIsLatch(Abc_ObjFanout0(pTerm)) ) + continue; + // check if the net is visited + pNet = Abc_ObjFanin0(pTerm); + if ( Abc_NodeIsTravIdCurrent(pNet) ) + continue; + // create PO + Abc_NodeSetTravIdCurrent( pNet ); + Abc_ObjAddFanin( Abc_NtkCreatePo(pNtkNew), pNet->pCopy ); + } + + // check integrity + if ( !Abc_NtkCheck( pNtkNew ) ) + { + fprintf( stdout, "Abc_NtkConvertBlackboxes(): Network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Inserts blackboxes into the netlist.] + + Description [The first arg is the netlist with blackboxes without logic hierarchy. + The second arg is a non-hierarchical netlist derived from the above netlist after processing. + This procedure create a new netlist, which is comparable to the original netlist with + blackboxes, except that it contains logic nodes from the netlist after processing.] + + SideEffects [This procedure silently assumes that blackboxes appear + only in the top-level model. If they appear in other models as well, + the name of the model and its number were appended to the names of + blackbox inputs/outputs.] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkInsertNewLogic( Abc_Ntk_t * pNtkH, Abc_Ntk_t * pNtkL ) +{ + Abc_Lib_t * pDesign; + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObjH, * pObjL, * pNetH, * pNetL, * pTermH; + int i, k; + + assert( Abc_NtkIsNetlist(pNtkH) ); + assert( Abc_NtkWhiteboxNum(pNtkH) == 0 ); + assert( Abc_NtkBlackboxNum(pNtkH) > 0 ); + + assert( Abc_NtkIsNetlist(pNtkL) ); + assert( Abc_NtkWhiteboxNum(pNtkL) == 0 ); + assert( Abc_NtkBlackboxNum(pNtkL) == 0 ); + + // prepare the logic network for copying + Abc_NtkCleanCopy( pNtkL ); + + // start the network + pNtkNew = Abc_NtkAlloc( pNtkL->ntkType, pNtkL->ntkFunc, 1 ); + // duplicate the name and the spec + pNtkNew->pName = Extra_UtilStrsav( pNtkH->pName ); + pNtkNew->pSpec = Extra_UtilStrsav( pNtkH->pSpec ); + + // make sure every PI/PO has a PI/PO in the processed network + Abc_NtkForEachPi( pNtkH, pObjH, i ) + { + pNetH = Abc_ObjFanout0(pObjH); + pNetL = Abc_NtkFindNet( pNtkL, Abc_ObjName(pNetH) ); + if ( pNetL == NULL || !Abc_ObjIsPi( Abc_ObjFanin0(pNetL) ) ) + { + printf( "Error in Abc_NtkInsertNewLogic(): There is no PI corresponding to the PI %s.\n", Abc_ObjName(pNetH) ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + if ( pNetL->pCopy ) + { + printf( "Error in Abc_NtkInsertNewLogic(): Primary input %s is repeated twice.\n", Abc_ObjName(pNetH) ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + // create the new net + pNetL->pCopy = Abc_NtkFindOrCreateNet( pNtkNew, Abc_ObjName(pNetH) ); + Abc_NtkDupObj( pNtkNew, Abc_ObjFanin0(pNetL), 0 ); + } + + // make sure every BB has a PI/PO in the processed network + Abc_NtkForEachBlackbox( pNtkH, pObjH, i ) + { + // duplicate the box + Abc_NtkDupBox( pNtkNew, pObjH, 0 ); + pObjH->pCopy->pData = pObjH->pData; + // create PIs + Abc_ObjForEachFanout( pObjH, pTermH, k ) + { + pNetH = Abc_ObjFanout0( pTermH ); + pNetL = Abc_NtkFindNet( pNtkL, Abc_ObjName(pNetH) ); + if ( pNetL == NULL || !Abc_ObjIsPi( Abc_ObjFanin0(pNetL) ) ) + { + printf( "Error in Abc_NtkInsertNewLogic(): There is no PI corresponding to the inpout %s of blackbox %s.\n", Abc_ObjName(pNetH), Abc_ObjName(pObjH) ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + if ( pNetL->pCopy ) + { + printf( "Error in Abc_NtkInsertNewLogic(): Box output %s is repeated twice.\n", Abc_ObjName(pNetH) ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + // create net and map the PI + pNetL->pCopy = Abc_NtkFindOrCreateNet( pNtkNew, Abc_ObjName(pNetH) ); + Abc_ObjFanin0(pNetL)->pCopy = pTermH->pCopy; + } + } + + Abc_NtkForEachPo( pNtkH, pObjH, i ) + { + pNetH = Abc_ObjFanin0(pObjH); + pNetL = Abc_NtkFindNet( pNtkL, Abc_ObjName(pNetH) ); + if ( pNetL == NULL || !Abc_ObjIsPo( Abc_ObjFanout0(pNetL) ) ) + { + printf( "Error in Abc_NtkInsertNewLogic(): There is no PO corresponding to the PO %s.\n", Abc_ObjName(pNetH) ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + if ( pNetL->pCopy ) + continue; + // create the new net + pNetL->pCopy = Abc_NtkFindOrCreateNet( pNtkNew, Abc_ObjName(pNetH) ); + Abc_NtkDupObj( pNtkNew, Abc_ObjFanout0(pNetL), 0 ); + } + Abc_NtkForEachBlackbox( pNtkH, pObjH, i ) + { + Abc_ObjForEachFanin( pObjH, pTermH, k ) + { + char * pName; + pNetH = Abc_ObjFanin0( pTermH ); + pName = Abc_ObjName(pNetH); + pNetL = Abc_NtkFindNet( pNtkL, Abc_ObjName(pNetH) ); + if ( pNetL == NULL || !Abc_ObjIsPo( Abc_ObjFanout0(pNetL) ) ) + { + printf( "There is no PO corresponding to the input %s of blackbox %s.\n", Abc_ObjName(pNetH), Abc_ObjName(pObjH) ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + // create net and map the PO + if ( pNetL->pCopy ) + { + if ( Abc_ObjFanout0(pNetL)->pCopy == NULL ) + Abc_ObjFanout0(pNetL)->pCopy = pTermH->pCopy; + else + Abc_ObjAddFanin( pTermH->pCopy, pNetL->pCopy ); + continue; + } + pNetL->pCopy = Abc_NtkFindOrCreateNet( pNtkNew, Abc_ObjName(pNetH) ); + Abc_ObjFanout0(pNetL)->pCopy = pTermH->pCopy; + } + } + + // duplicate other objects of the logic network + Abc_NtkForEachObj( pNtkL, pObjL, i ) + if ( pObjL->pCopy == NULL && !Abc_ObjIsPo(pObjL) ) // skip POs feeding into PIs + Abc_NtkDupObj( pNtkNew, pObjL, Abc_ObjIsNet(pObjL) ); + + // connect objects + Abc_NtkForEachObj( pNtkL, pObjL, i ) + Abc_ObjForEachFanin( pObjL, pNetL, k ) + if ( pObjL->pCopy ) + Abc_ObjAddFanin( pObjL->pCopy, pNetL->pCopy ); + + // transfer the design + pDesign = pNtkH->pDesign; pNtkH->pDesign = NULL; + assert( Vec_PtrEntry( pDesign->vModules, 0 ) == pNtkH ); + Vec_PtrWriteEntry( pDesign->vModules, 0, pNtkNew ); + pNtkNew->pDesign = pDesign; + +//Abc_NtkPrintStats( stdout, pNtkH, 0 ); +//Abc_NtkPrintStats( stdout, pNtkNew, 0 ); + + // check integrity + if ( !Abc_NtkCheck( pNtkNew ) ) + { + fprintf( stdout, "Abc_NtkInsertNewLogic(): Network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abc/abcInt.h b/abc_with_bb_support/src/base/abc/abcInt.h new file mode 100644 index 000000000..c29c2b9e5 --- /dev/null +++ b/abc_with_bb_support/src/base/abc/abcInt.h @@ -0,0 +1,52 @@ +/**CFile**************************************************************** + + FileName [abcInt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Internal declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcInt.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __ABC_INT_H__ +#define __ABC_INT_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +#define ABC_NUM_STEPS 10 + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abc/abcLatch.c b/abc_with_bb_support/src/base/abc/abcLatch.c new file mode 100644 index 000000000..7c4327240 --- /dev/null +++ b/abc_with_bb_support/src/base/abc/abcLatch.c @@ -0,0 +1,219 @@ +/**CFile**************************************************************** + + FileName [abcLatch.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Procedures working with latches.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcLatch.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Checks if latches form self-loop.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkLatchIsSelfFeed_rec( Abc_Obj_t * pLatch, Abc_Obj_t * pLatchRoot ) +{ + Abc_Obj_t * pFanin; + assert( Abc_ObjIsLatch(pLatch) ); + if ( pLatch == pLatchRoot ) + return 1; + pFanin = Abc_ObjFanin0(Abc_ObjFanin0(pLatch)); + if ( !Abc_ObjIsBo(pFanin) || !Abc_ObjIsLatch(Abc_ObjFanin0(pFanin)) ) + return 0; + return Abc_NtkLatchIsSelfFeed_rec( Abc_ObjFanin0(pFanin), pLatch ); +} + +/**Function************************************************************* + + Synopsis [Checks if latches form self-loop.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkLatchIsSelfFeed( Abc_Obj_t * pLatch ) +{ + Abc_Obj_t * pFanin; + assert( Abc_ObjIsLatch(pLatch) ); + pFanin = Abc_ObjFanin0(Abc_ObjFanin0(pLatch)); + if ( !Abc_ObjIsBo(pFanin) || !Abc_ObjIsLatch(Abc_ObjFanin0(pFanin)) ) + return 0; + return Abc_NtkLatchIsSelfFeed_rec( Abc_ObjFanin0(pFanin), pLatch ); +} + +/**Function************************************************************* + + Synopsis [Checks if latches form self-loop.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkCountSelfFeedLatches( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pLatch; + int i, Counter; + Counter = 0; + Abc_NtkForEachLatch( pNtk, pLatch, i ) + { +// if ( Abc_NtkLatchIsSelfFeed(pLatch) && Abc_ObjFanoutNum(pLatch) > 1 ) +// printf( "Fanouts = %d.\n", Abc_ObjFanoutNum(pLatch) ); + Counter += Abc_NtkLatchIsSelfFeed( pLatch ); + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Replaces self-feeding latches by latches with constant inputs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRemoveSelfFeedLatches( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pLatch, * pConst1; + int i, Counter; + Counter = 0; + Abc_NtkForEachLatch( pNtk, pLatch, i ) + { + if ( Abc_NtkLatchIsSelfFeed( pLatch ) ) + { + if ( Abc_NtkIsStrash(pNtk) ) + pConst1 = Abc_AigConst1(pNtk); + else + pConst1 = Abc_NtkCreateNodeConst1(pNtk); + Abc_ObjPatchFanin( Abc_ObjFanin0(pLatch), Abc_ObjFanin0(Abc_ObjFanin0(pLatch)), pConst1 ); + Counter++; + } + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Pipelines the network with latches.] + + Description [] + + SideEffects [Does not check the names of the added latches!!!] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkLatchPipe( Abc_Ntk_t * pNtk, int nLatches ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj, * pLatch, * pFanin, * pFanout; + int i, k, nTotal, nDigits; + if ( nLatches < 1 ) + return; + nTotal = nLatches * Abc_NtkPiNum(pNtk); + nDigits = Extra_Base10Log( nTotal ); + vNodes = Vec_PtrAlloc( 100 ); + Abc_NtkForEachPi( pNtk, pObj, i ) + { + // remember current fanins of the PI + Abc_NodeCollectFanouts( pObj, vNodes ); + // create the latches + for ( pFanin = pObj, k = 0; k < nLatches; k++, pFanin = pLatch ) + { + pLatch = Abc_NtkCreateLatch( pNtk ); + Abc_ObjAddFanin( pLatch, pFanin ); + Abc_LatchSetInitDc( pLatch ); + // create the name of the new latch + Abc_ObjAssignName( pLatch, Abc_ObjNameDummy("LL", i*nLatches + k, nDigits), NULL ); + } + // patch the PI fanouts + Vec_PtrForEachEntry( vNodes, pFanout, k ) + Abc_ObjPatchFanin( pFanout, pObj, pFanin ); + } + Vec_PtrFree( vNodes ); + Abc_NtkLogicMakeSimpleCos( pNtk, 0 ); +} + +/**Function************************************************************* + + Synopsis [Strashes one logic node using its SOP.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Abc_NtkCollectLatchValues( Abc_Ntk_t * pNtk ) +{ + Vec_Int_t * vValues; + Abc_Obj_t * pLatch; + int i; + vValues = Vec_IntAlloc( Abc_NtkLatchNum(pNtk) ); + Abc_NtkForEachLatch( pNtk, pLatch, i ) + Vec_IntPush( vValues, Abc_LatchIsInit1(pLatch) ); + return vValues; +} + +/**Function************************************************************* + + Synopsis [Strashes one logic node using its SOP.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkInsertLatchValues( Abc_Ntk_t * pNtk, Vec_Int_t * vValues ) +{ + Abc_Obj_t * pLatch; + int i; + Abc_NtkForEachLatch( pNtk, pLatch, i ) + pLatch->pData = (void *)(vValues? (Vec_IntEntry(vValues,i)? ABC_INIT_ONE : ABC_INIT_ZERO) : ABC_INIT_DC); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abc/abcLib.c b/abc_with_bb_support/src/base/abc/abcLib.c new file mode 100644 index 000000000..85c430326 --- /dev/null +++ b/abc_with_bb_support/src/base/abc/abcLib.c @@ -0,0 +1,455 @@ +/**CFile**************************************************************** + + FileName [abcLib.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Functions to manipulate verilog libraries.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcLib.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Create the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Lib_t * Abc_LibCreate( char * pName ) +{ + Abc_Lib_t * p; + p = ALLOC( Abc_Lib_t, 1 ); + memset( p, 0, sizeof(Abc_Lib_t) ); + p->pName = Extra_UtilStrsav( pName ); + p->tModules = st_init_table( strcmp, st_strhash ); + p->vTops = Vec_PtrAlloc( 100 ); + p->vModules = Vec_PtrAlloc( 100 ); + p->pManFunc = Hop_ManStart(); + p->pLibrary = NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [Frees the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_LibFree( Abc_Lib_t * pLib, Abc_Ntk_t * pNtkSave ) +{ + Abc_Ntk_t * pNtk; + int i; + if ( pLib->pName ) + free( pLib->pName ); + if ( pLib->pManFunc ) + Hop_ManStop( pLib->pManFunc ); + if ( pLib->tModules ) + st_free_table( pLib->tModules ); + if ( pLib->vModules ) + { + Vec_PtrForEachEntry( pLib->vModules, pNtk, i ) + { +// pNtk->pManFunc = NULL; + if ( pNtk == pNtkSave ) + continue; + pNtk->pManFunc = NULL; + pNtk->pDesign = NULL; + Abc_NtkDelete( pNtk ); + } + Vec_PtrFree( pLib->vModules ); + } + if ( pLib->vTops ) + Vec_PtrFree( pLib->vTops ); + free( pLib ); +} + +/**Function************************************************************* + + Synopsis [Frees the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Lib_t * Abc_LibDupBlackboxes( Abc_Lib_t * pLib, Abc_Ntk_t * pNtkSave ) +{ + Abc_Lib_t * pLibNew; + Abc_Ntk_t * pNtkTemp; + int i; + assert( Vec_PtrSize(pLib->vTops) > 0 ); + assert( Vec_PtrSize(pLib->vModules) > 1 ); + pLibNew = Abc_LibCreate( pLib->pName ); +// pLibNew->pManFunc = pNtkSave->pManFunc; + Vec_PtrPush( pLibNew->vTops, pNtkSave ); + Vec_PtrPush( pLibNew->vModules, pNtkSave ); + Vec_PtrForEachEntry( pLib->vModules, pNtkTemp, i ) + if ( Abc_NtkHasBlackbox( pNtkTemp ) ) + Vec_PtrPush( pLibNew->vModules, Abc_NtkDup(pNtkTemp) ); + return pLibNew; +} + + +/**Function************************************************************* + + Synopsis [Prints the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_LibPrint( Abc_Lib_t * pLib ) +{ + Abc_Ntk_t * pNtk; + Abc_Obj_t * pObj; + int i, k; + printf( "Models of design %s:\n", pLib->pName ); + Vec_PtrForEachEntry( pLib->vModules, pNtk, i ) + { + printf( "%2d : %20s ", i+1, pNtk->pName ); + printf( "nd = %6d lat = %6d whitebox = %3d blackbox = %3d\n", + Abc_NtkNodeNum(pNtk), Abc_NtkLatchNum(pNtk), + Abc_NtkWhiteboxNum(pNtk), Abc_NtkBlackboxNum(pNtk) ); + if ( Abc_NtkBlackboxNum(pNtk) == 0 ) + continue; + Abc_NtkForEachWhitebox( pNtk, pObj, k ) + printf( " %20s (whitebox)\n", Abc_NtkName(pObj->pData) ); + Abc_NtkForEachBlackbox( pNtk, pObj, k ) + printf( " %20s (blackbox)\n", Abc_NtkName(pObj->pData) ); + } +} + +/**Function************************************************************* + + Synopsis [Create the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_LibAddModel( Abc_Lib_t * pLib, Abc_Ntk_t * pNtk ) +{ + if ( st_is_member( pLib->tModules, (char *)pNtk->pName ) ) + return 0; + st_insert( pLib->tModules, (char *)pNtk->pName, (char *)pNtk ); + Vec_PtrPush( pLib->vModules, pNtk ); + pNtk->pDesign = pLib; + return 1; +} + +/**Function************************************************************* + + Synopsis [Create the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_LibFindModelByName( Abc_Lib_t * pLib, char * pName ) +{ + Abc_Ntk_t * pNtk; + if ( !st_is_member( pLib->tModules, (char *)pName ) ) + return NULL; + st_lookup( pLib->tModules, (char *)pName, (char **)&pNtk ); + return pNtk; +} + +/**Function************************************************************* + + Synopsis [Frees the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_LibDeriveRoot( Abc_Lib_t * pLib ) +{ + Abc_Ntk_t * pNtk; + if ( Vec_PtrSize(pLib->vModules) > 1 ) + { + printf( "The design includes more than one module and is currently not used.\n" ); + return NULL; + } + pNtk = Vec_PtrEntry( pLib->vModules, 0 ); Vec_PtrClear( pLib->vModules ); + pNtk->pManFunc = pLib->pManFunc; pLib->pManFunc = NULL; + return pNtk; +} + +/**Function************************************************************* + + Synopsis [Detects the top-level models.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_LibFindTopLevelModels( Abc_Lib_t * pLib ) +{ + Abc_Ntk_t * pNtk, * pNtkBox; + Abc_Obj_t * pObj; + int i, k; + assert( Vec_PtrSize( pLib->vModules ) > 0 ); + // clear the models + Vec_PtrForEachEntry( pLib->vModules, pNtk, i ) + pNtk->fHieVisited = 0; + // mark all the models reachable from other models + Vec_PtrForEachEntry( pLib->vModules, pNtk, i ) + { + Abc_NtkForEachBox( pNtk, pObj, k ) + { + if ( Abc_ObjIsLatch(pObj) ) + continue; + if ( pObj->pData == NULL ) + continue; + pNtkBox = pObj->pData; + pNtkBox->fHieVisited = 1; + } + } + // collect the models that are not marked + Vec_PtrClear( pLib->vTops ); + Vec_PtrForEachEntry( pLib->vModules, pNtk, i ) + { + if ( pNtk->fHieVisited == 0 ) + Vec_PtrPush( pLib->vTops, pNtk ); + else + pNtk->fHieVisited = 0; + } + return Vec_PtrSize( pLib->vTops ); +} + + +/**Function************************************************************* + + Synopsis [Surround boxes without content (black boxes) with BIs/BOs.] + + Description [Returns the number of black boxes converted.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_LibDeriveBlackBoxes( Abc_Ntk_t * pNtk, Abc_Lib_t * pLib ) +{ +/* + Abc_Obj_t * pObj, * pFanin, * pFanout; + int i, k; + assert( Abc_NtkIsNetlist(pNtk) ); + // collect blackbox nodes + assert( Vec_PtrSize(pNtk->vBoxes) == 0 ); + Vec_PtrClear( pNtk->vBoxes ); + Abc_NtkForEachBox( pNtk, pObj, i ) + if ( Abc_NtkNodeNum(pObj->pData) == 0 ) + Vec_PtrPush( pNtk->vBoxes, pObj ); + // return if there is no black boxes without content + if ( Vec_PtrSize(pNtk->vBoxes) == 0 ) + return 0; + // print the boxes + printf( "Black boxes are: " ); + Abc_NtkForEachBox( pNtk, pObj, i ) + printf( " %s", ((Abc_Ntk_t *)pObj->pData)->pName ); + printf( "\n" ); + // iterate through the boxes and add BIs/BOs + Abc_NtkForEachBox( pNtk, pObj, i ) + { + // go through the fanin nets + Abc_ObjForEachFanin( pObj, pFanin, k ) + Abc_ObjInsertBetween( pFanin, pObj, ABC_OBJ_BI ); + // go through the fanout nets + Abc_ObjForEachFanout( pObj, pFanout, k ) + { + Abc_ObjInsertBetween( pObj, pFanout, ABC_OBJ_BO ); + // if the name is not given assign name + if ( pFanout->pData == NULL ) + { + pFanout->pData = Abc_ObjName( pFanout ); + Nm_ManStoreIdName( pNtk->pManName, pFanout->Id, pFanout->pData, NULL ); + } + } + } + return Vec_PtrSize(pNtk->vBoxes); +*/ + return 0; +} + +/**Function************************************************************* + + Synopsis [Derive the AIG of the logic in the netlist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeStrashUsingNetwork_rec( Abc_Ntk_t * pNtkAig, Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanin; + int i; + assert( !Abc_ObjIsNet(pObj) ); + if ( pObj->pCopy ) + return; + // call for the fanins + Abc_ObjForEachFanin( pObj, pFanin, i ) + Abc_NodeStrashUsingNetwork_rec( pNtkAig, Abc_ObjFanin0Ntk(Abc_ObjFanin0(pObj)) ); + // compute for the node + pObj->pCopy = Abc_NodeStrash( pNtkAig, pObj, 0 ); + // set for the fanout net + Abc_ObjFanout0(pObj)->pCopy = pObj->pCopy; +} + +/**Function************************************************************* + + Synopsis [Derive the AIG of the logic in the netlist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeStrashUsingNetwork( Abc_Ntk_t * pNtkAig, Abc_Obj_t * pBox ) +{ + Abc_Ntk_t * pNtkGate; + Abc_Obj_t * pObj; + unsigned * pPolarity; + int i, fCompl; + assert( Abc_ObjIsBox(pBox) ); + pNtkGate = pBox->pData; + pPolarity = (unsigned *)pBox->pNext; + assert( Abc_NtkIsNetlist(pNtkGate) ); + assert( Abc_NtkLatchNum(pNtkGate) == 0 ); + Abc_NtkCleanCopy( pNtkGate ); + // set the PI values + Abc_NtkForEachPi( pNtkGate, pObj, i ) + { + fCompl = (pPolarity && Abc_InfoHasBit(pPolarity, i)); + pObj->pCopy = Abc_ObjNotCond( Abc_ObjFanin(pBox,i)->pCopy, fCompl ); + Abc_ObjFanout0(pObj)->pCopy = pObj->pCopy; + } + // build recursively and set the PO values + Abc_NtkForEachPo( pNtkGate, pObj, i ) + { + Abc_NodeStrashUsingNetwork_rec( pNtkAig, Abc_ObjFanin0Ntk(Abc_ObjFanin0(pObj)) ); + Abc_ObjFanout(pBox,i)->pCopy = Abc_ObjFanin0(pObj)->pCopy; + } +//printf( "processing %d\n", pBox->Id ); +} + +/**Function************************************************************* + + Synopsis [Derive the AIG of the logic in the netlist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_LibDeriveAig( Abc_Ntk_t * pNtk, Abc_Lib_t * pLib ) +{ + ProgressBar * pProgress; + Vec_Ptr_t * vNodes; + Abc_Ntk_t * pNtkAig; + Abc_Obj_t * pObj; + int i, nBoxes; + // explicitly derive black boxes + assert( Abc_NtkIsNetlist(pNtk) ); + nBoxes = Abc_LibDeriveBlackBoxes( pNtk, pLib ); + if ( nBoxes ) + printf( "Detected and transformed %d black boxes.\n", nBoxes ); + // create the new network with black boxes in place + pNtkAig = Abc_NtkStartFrom( pNtk, ABC_NTK_STRASH, ABC_FUNC_AIG ); + // transer to the nets + Abc_NtkForEachCi( pNtk, pObj, i ) + Abc_ObjFanout0(pObj)->pCopy = pObj->pCopy; + // build the AIG for the remaining logic in the netlist + vNodes = Abc_NtkDfs( pNtk, 0 ); + pProgress = Extra_ProgressBarStart( stdout, Vec_PtrSize(vNodes) ); + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + if ( Abc_ObjIsNode(pObj) ) + { + pObj->pCopy = Abc_NodeStrash( pNtkAig, pObj, 0 ); + Abc_ObjFanout0(pObj)->pCopy = pObj->pCopy; + continue; + } + Abc_NodeStrashUsingNetwork( pNtkAig, pObj ); + } + Extra_ProgressBarStop( pProgress ); + Vec_PtrFree( vNodes ); + // deallocate memory manager, which remembers the phase + if ( pNtk->pData ) + { + Extra_MmFlexStop( pNtk->pData ); + pNtk->pData = NULL; + } + // set the COs +// Abc_NtkFinalize( pNtk, pNtkAig ); + Abc_NtkForEachCo( pNtk, pObj, i ) + Abc_ObjAddFanin( pObj->pCopy, Abc_ObjFanin0(pObj)->pCopy ); + Abc_AigCleanup( pNtkAig->pManFunc ); + // make sure that everything is okay + if ( !Abc_NtkCheck( pNtkAig ) ) + { + printf( "Abc_LibDeriveAig: The network check has failed.\n" ); + return 0; + } + return pNtkAig; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abc/abcMinBase.c b/abc_with_bb_support/src/base/abc/abcMinBase.c new file mode 100644 index 000000000..c416cfec3 --- /dev/null +++ b/abc_with_bb_support/src/base/abc/abcMinBase.c @@ -0,0 +1,256 @@ +/**CFile**************************************************************** + + FileName [abcMinBase.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Makes nodes of the network minimum base.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcMinBase.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Abc_NodeSupport( DdNode * bFunc, Vec_Str_t * vSupport, int nVars ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Makes nodes minimum base.] + + Description [Returns the number of changed nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkMinimumBase( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i, Counter; + assert( Abc_NtkIsBddLogic(pNtk) ); + Counter = 0; + Abc_NtkForEachNode( pNtk, pNode, i ) + Counter += Abc_NodeMinimumBase( pNode ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Makes one node minimum base.] + + Description [Returns 1 if the node is changed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeMinimumBase( Abc_Obj_t * pNode ) +{ + Vec_Str_t * vSupport; + Vec_Ptr_t * vFanins; + DdNode * bTemp; + int i, nVars; + + assert( Abc_NtkIsBddLogic(pNode->pNtk) ); + assert( Abc_ObjIsNode(pNode) ); + + // compute support + vSupport = Vec_StrAlloc( 10 ); + nVars = Abc_NodeSupport( Cudd_Regular(pNode->pData), vSupport, Abc_ObjFaninNum(pNode) ); + if ( nVars == Abc_ObjFaninNum(pNode) ) + { + Vec_StrFree( vSupport ); + return 0; + } + + // remove unused fanins + vFanins = Vec_PtrAlloc( Abc_ObjFaninNum(pNode) ); + Abc_NodeCollectFanins( pNode, vFanins ); + for ( i = 0; i < vFanins->nSize; i++ ) + if ( vSupport->pArray[i] == 0 ) + Abc_ObjDeleteFanin( pNode, vFanins->pArray[i] ); + assert( nVars == Abc_ObjFaninNum(pNode) ); + + // update the function of the node + pNode->pData = Extra_bddRemapUp( pNode->pNtk->pManFunc, bTemp = pNode->pData ); Cudd_Ref( pNode->pData ); + Cudd_RecursiveDeref( pNode->pNtk->pManFunc, bTemp ); + Vec_PtrFree( vFanins ); + Vec_StrFree( vSupport ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Makes nodes of the network fanin-dup-free.] + + Description [Returns the number of pairs of duplicated fanins.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRemoveDupFanins( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i, Counter; + assert( Abc_NtkIsBddLogic(pNtk) ); + Counter = 0; + Abc_NtkForEachNode( pNtk, pNode, i ) + Counter += Abc_NodeRemoveDupFanins( pNode ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Removes one pair of duplicated fanins if present.] + + Description [Returns 1 if the node is changed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeRemoveDupFanins_int( Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pFanin1, * pFanin2; + int i, k; + assert( Abc_NtkIsBddLogic(pNode->pNtk) ); + assert( Abc_ObjIsNode(pNode) ); + // make sure fanins are not duplicated + Abc_ObjForEachFanin( pNode, pFanin2, i ) + { + Abc_ObjForEachFanin( pNode, pFanin1, k ) + { + if ( k >= i ) + break; + if ( pFanin1 == pFanin2 ) + { + DdManager * dd = pNode->pNtk->pManFunc; + DdNode * bVar1 = Cudd_bddIthVar( dd, i ); + DdNode * bVar2 = Cudd_bddIthVar( dd, k ); + DdNode * bTrans, * bTemp; + bTrans = Cudd_bddXnor( dd, bVar1, bVar2 ); Cudd_Ref( bTrans ); + pNode->pData = Cudd_bddAndAbstract( dd, bTemp = pNode->pData, bTrans, bVar2 ); Cudd_Ref( pNode->pData ); + Cudd_RecursiveDeref( dd, bTemp ); + Cudd_RecursiveDeref( dd, bTrans ); + Abc_NodeMinimumBase( pNode ); + return 1; + } + } + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Removes duplicated fanins if present.] + + Description [Returns the number of fanins removed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeRemoveDupFanins( Abc_Obj_t * pNode ) +{ + int Counter = 0; + while ( Abc_NodeRemoveDupFanins_int(pNode) ) + Counter++; + return Counter; +} +/**Function************************************************************* + + Synopsis [Computes support of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeSupport_rec( DdNode * bFunc, Vec_Str_t * vSupport ) +{ + if ( cuddIsConstant(bFunc) || Cudd_IsComplement(bFunc->next) ) + return; + vSupport->pArray[ bFunc->index ] = 1; + Abc_NodeSupport_rec( cuddT(bFunc), vSupport ); + Abc_NodeSupport_rec( Cudd_Regular(cuddE(bFunc)), vSupport ); + bFunc->next = Cudd_Not(bFunc->next); +} + +/**Function************************************************************* + + Synopsis [Computes support of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeSupportClear_rec( DdNode * bFunc ) +{ + if ( !Cudd_IsComplement(bFunc->next) ) + return; + bFunc->next = Cudd_Regular(bFunc->next); + if ( cuddIsConstant(bFunc) ) + return; + Abc_NodeSupportClear_rec( cuddT(bFunc) ); + Abc_NodeSupportClear_rec( Cudd_Regular(cuddE(bFunc)) ); +} + +/**Function************************************************************* + + Synopsis [Computes support of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeSupport( DdNode * bFunc, Vec_Str_t * vSupport, int nVars ) +{ + int Counter, i; + // compute the support by marking the BDD + Vec_StrFill( vSupport, nVars, 0 ); + Abc_NodeSupport_rec( bFunc, vSupport ); + // clear the marak + Abc_NodeSupportClear_rec( bFunc ); + // get the number of support variables + Counter = 0; + for ( i = 0; i < nVars; i++ ) + Counter += vSupport->pArray[i]; + return Counter; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abc/abcNames.c b/abc_with_bb_support/src/base/abc/abcNames.c new file mode 100644 index 000000000..ec60d82f7 --- /dev/null +++ b/abc_with_bb_support/src/base/abc/abcNames.c @@ -0,0 +1,467 @@ +/**CFile**************************************************************** + + FileName [abcNames.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Procedures working with net and node names.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcNames.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Returns the unique name for the object.] + + Description [If the name previously did not exist, creates a new unique + name but does not assign this name to the object. The temporary unique + name is stored in a static buffer inside this procedure. It is important + that the name is used before the function is called again!] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_ObjName( Abc_Obj_t * pObj ) +{ + return Nm_ManCreateUniqueName( pObj->pNtk->pManName, pObj->Id ); +} + +/**Function************************************************************* + + Synopsis [Assigns the given name to the object.] + + Description [The object should not have a name assigned. The same + name may be used for several objects, which they share the same net + in the original netlist. (For example, latch output and primary output + may have the same name.) This procedure returns the pointer to the + internally stored representation of the given name.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_ObjAssignName( Abc_Obj_t * pObj, char * pName, char * pSuffix ) +{ + assert( pName != NULL ); + return Nm_ManStoreIdName( pObj->pNtk->pManName, pObj->Id, pObj->Type, pName, pSuffix ); +} + +/**Function************************************************************* + + Synopsis [Gets the long name of the node.] + + Description [This name is the output net's name.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_ObjNameSuffix( Abc_Obj_t * pObj, char * pSuffix ) +{ + static char Buffer[500]; + sprintf( Buffer, "%s%s", Abc_ObjName(pObj), pSuffix ); + return Buffer; +} + +/**Function************************************************************* + + Synopsis [Returns the dummy PI name.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_ObjNameDummy( char * pPrefix, int Num, int nDigits ) +{ + static char Buffer[100]; + sprintf( Buffer, "%s%0*d", pPrefix, nDigits, Num ); + return Buffer; +} + +/**Function************************************************************* + + Synopsis [Tranfers names to the old network.] + + Description [Assumes that the new nodes are attached using pObj->pCopy.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkTrasferNames( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkNew ) +{ + Abc_Obj_t * pObj; + int i; + assert( Abc_NtkPiNum(pNtk) == Abc_NtkPiNum(pNtkNew) ); + assert( Abc_NtkPoNum(pNtk) == Abc_NtkPoNum(pNtkNew) ); + assert( Abc_NtkBoxNum(pNtk) == Abc_NtkBoxNum(pNtkNew) ); + assert( Abc_NtkAssertNum(pNtk) == Abc_NtkAssertNum(pNtkNew) ); + assert( Nm_ManNumEntries(pNtk->pManName) > 0 ); + assert( Nm_ManNumEntries(pNtkNew->pManName) == 0 ); + // copy the CI/CO/box names + Abc_NtkForEachCi( pNtk, pObj, i ) + Abc_ObjAssignName( pObj->pCopy, Abc_ObjName(Abc_ObjFanout0Ntk(pObj)), NULL ); + Abc_NtkForEachCo( pNtk, pObj, i ) + Abc_ObjAssignName( pObj->pCopy, Abc_ObjName(Abc_ObjFanin0Ntk(pObj)), NULL ); + Abc_NtkForEachBox( pNtk, pObj, i ) + Abc_ObjAssignName( pObj->pCopy, Abc_ObjName(pObj), NULL ); +} + +/**Function************************************************************* + + Synopsis [Tranfers names to the old network.] + + Description [Assumes that the new nodes are attached using pObj->pCopy.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkTrasferNamesNoLatches( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkNew ) +{ + Abc_Obj_t * pObj; + int i; + assert( Abc_NtkPiNum(pNtk) == Abc_NtkPiNum(pNtkNew) ); + assert( Abc_NtkPoNum(pNtk) == Abc_NtkPoNum(pNtkNew) ); + assert( Abc_NtkAssertNum(pNtk) == Abc_NtkAssertNum(pNtkNew) ); + assert( Nm_ManNumEntries(pNtk->pManName) > 0 ); + assert( Nm_ManNumEntries(pNtkNew->pManName) == 0 ); + // copy the CI/CO/box name and skip latches and theirs inputs/outputs + Abc_NtkForEachCi( pNtk, pObj, i ) + if ( Abc_ObjFaninNum(pObj) == 0 || !Abc_ObjIsLatch(Abc_ObjFanin0(pObj)) ) + Abc_ObjAssignName( pObj->pCopy, Abc_ObjName(Abc_ObjFanout0Ntk(pObj)), NULL ); + Abc_NtkForEachCo( pNtk, pObj, i ) + if ( Abc_ObjFanoutNum(pObj) == 0 || !Abc_ObjIsLatch(Abc_ObjFanout0(pObj)) ) + Abc_ObjAssignName( pObj->pCopy, Abc_ObjName(Abc_ObjFanin0Ntk(pObj)), NULL ); + Abc_NtkForEachBox( pNtk, pObj, i ) + if ( !Abc_ObjIsLatch(pObj) ) + Abc_ObjAssignName( pObj->pCopy, Abc_ObjName(pObj), NULL ); +} + +/**Function************************************************************* + + Synopsis [Gets fanin node names.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NodeGetFaninNames( Abc_Obj_t * pNode ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pFanin; + int i; + vNodes = Vec_PtrAlloc( 100 ); + Abc_ObjForEachFanin( pNode, pFanin, i ) + Vec_PtrPush( vNodes, Extra_UtilStrsav(Abc_ObjName(pFanin)) ); + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Gets fanin node names.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NodeGetFakeNames( int nNames ) +{ + Vec_Ptr_t * vNames; + char Buffer[5]; + int i; + + vNames = Vec_PtrAlloc( nNames ); + for ( i = 0; i < nNames; i++ ) + { + if ( nNames < 26 ) + { + Buffer[0] = 'a' + i; + Buffer[1] = 0; + } + else + { + Buffer[0] = 'a' + i%26; + Buffer[1] = '0' + i/26; + Buffer[2] = 0; + } + Vec_PtrPush( vNames, Extra_UtilStrsav(Buffer) ); + } + return vNames; +} + +/**Function************************************************************* + + Synopsis [Gets fanin node names.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeFreeNames( Vec_Ptr_t * vNames ) +{ + int i; + if ( vNames == NULL ) + return; + for ( i = 0; i < vNames->nSize; i++ ) + free( vNames->pArray[i] ); + Vec_PtrFree( vNames ); +} + +/**Function************************************************************* + + Synopsis [Collects the CI or CO names.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char ** Abc_NtkCollectCioNames( Abc_Ntk_t * pNtk, int fCollectCos ) +{ + Abc_Obj_t * pObj; + char ** ppNames; + int i; + if ( fCollectCos ) + { + ppNames = ALLOC( char *, Abc_NtkCoNum(pNtk) ); + Abc_NtkForEachCo( pNtk, pObj, i ) + ppNames[i] = Abc_ObjName(pObj); + } + else + { + ppNames = ALLOC( char *, Abc_NtkCiNum(pNtk) ); + Abc_NtkForEachCi( pNtk, pObj, i ) + ppNames[i] = Abc_ObjName(pObj); + } + return ppNames; +} + +/**Function************************************************************* + + Synopsis [Procedure used for sorting the nodes in decreasing order of levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeCompareNames( Abc_Obj_t ** pp1, Abc_Obj_t ** pp2 ) +{ + int Diff = strcmp( (char *)(*pp1)->pCopy, (char *)(*pp2)->pCopy ); + if ( Diff < 0 ) + return -1; + if ( Diff > 0 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Orders PIs/POs/latches alphabetically.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkOrderObjsByName( Abc_Ntk_t * pNtk, int fComb ) +{ + Abc_Obj_t * pObj; + int i; + assert( Abc_NtkAssertNum(pNtk) == 0 ); + assert( Abc_NtkHasOnlyLatchBoxes(pNtk) ); + // temporarily store the names in the copy field + Abc_NtkForEachPi( pNtk, pObj, i ) + pObj->pCopy = (Abc_Obj_t *)Abc_ObjName(pObj); + Abc_NtkForEachPo( pNtk, pObj, i ) + pObj->pCopy = (Abc_Obj_t *)Abc_ObjName(pObj); + Abc_NtkForEachBox( pNtk, pObj, i ) + pObj->pCopy = (Abc_Obj_t *)Abc_ObjName(Abc_ObjFanout0(pObj)); + // order objects alphabetically + qsort( (void *)Vec_PtrArray(pNtk->vPis), Vec_PtrSize(pNtk->vPis), sizeof(Abc_Obj_t *), + (int (*)(const void *, const void *)) Abc_NodeCompareNames ); + qsort( (void *)Vec_PtrArray(pNtk->vPos), Vec_PtrSize(pNtk->vPos), sizeof(Abc_Obj_t *), + (int (*)(const void *, const void *)) Abc_NodeCompareNames ); + // if the comparison if combinational (latches as PIs/POs), order them too + if ( fComb ) + qsort( (void *)Vec_PtrArray(pNtk->vBoxes), Vec_PtrSize(pNtk->vBoxes), sizeof(Abc_Obj_t *), + (int (*)(const void *, const void *)) Abc_NodeCompareNames ); + // order CIs/COs first PIs/POs(Asserts) then latches + Abc_NtkOrderCisCos( pNtk ); + // clean the copy fields + Abc_NtkForEachPi( pNtk, pObj, i ) + pObj->pCopy = NULL; + Abc_NtkForEachPo( pNtk, pObj, i ) + pObj->pCopy = NULL; + Abc_NtkForEachBox( pNtk, pObj, i ) + pObj->pCopy = NULL; +} + +/**Function************************************************************* + + Synopsis [Adds dummy names.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkAddDummyPiNames( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int nDigits, i; + nDigits = Extra_Base10Log( Abc_NtkPiNum(pNtk) ); + Abc_NtkForEachPi( pNtk, pObj, i ) + Abc_ObjAssignName( pObj, Abc_ObjNameDummy("pi", i, nDigits), NULL ); +} + +/**Function************************************************************* + + Synopsis [Adds dummy names.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkAddDummyPoNames( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int nDigits, i; + nDigits = Extra_Base10Log( Abc_NtkPoNum(pNtk) ); + Abc_NtkForEachPo( pNtk, pObj, i ) + Abc_ObjAssignName( pObj, Abc_ObjNameDummy("po", i, nDigits), NULL ); +} + +/**Function************************************************************* + + Synopsis [Adds dummy names.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkAddDummyAssertNames( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int nDigits, i; + nDigits = Extra_Base10Log( Abc_NtkAssertNum(pNtk) ); + Abc_NtkForEachAssert( pNtk, pObj, i ) + Abc_ObjAssignName( pObj, Abc_ObjNameDummy("a", i, nDigits), NULL ); +} + +/**Function************************************************************* + + Synopsis [Adds dummy names.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkAddDummyBoxNames( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int nDigits, i; + assert( !Abc_NtkIsNetlist(pNtk) ); + nDigits = Extra_Base10Log( Abc_NtkLatchNum(pNtk) ); + Abc_NtkForEachLatch( pNtk, pObj, i ) + { + Abc_ObjAssignName( pObj, Abc_ObjNameDummy("l", i, nDigits), NULL ); + Abc_ObjAssignName( Abc_ObjFanin0(pObj), Abc_ObjNameDummy("li", i, nDigits), NULL ); + Abc_ObjAssignName( Abc_ObjFanout0(pObj), Abc_ObjNameDummy("lo", i, nDigits), NULL ); + } +/* + nDigits = Extra_Base10Log( Abc_NtkBlackboxNum(pNtk) ); + Abc_NtkForEachBlackbox( pNtk, pObj, i ) + { + pName = Abc_ObjAssignName( pObj, Abc_ObjNameDummy("B", i, nDigits), NULL ); + nDigitsF = Extra_Base10Log( Abc_ObjFaninNum(pObj) ); + Abc_ObjForEachFanin( pObj, pTerm, k ) + Abc_ObjAssignName( Abc_ObjFanin0(pObj), pName, Abc_ObjNameDummy("i", k, nDigitsF) ); + nDigitsF = Extra_Base10Log( Abc_ObjFanoutNum(pObj) ); + Abc_ObjForEachFanout( pObj, pTerm, k ) + Abc_ObjAssignName( Abc_ObjFanin0(pObj), pName, Abc_ObjNameDummy("o", k, nDigitsF) ); + } +*/ +} + +/**Function************************************************************* + + Synopsis [Replaces names by short names.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkShortNames( Abc_Ntk_t * pNtk ) +{ + Nm_ManFree( pNtk->pManName ); + pNtk->pManName = Nm_ManCreate( Abc_NtkCiNum(pNtk) + Abc_NtkCoNum(pNtk) + Abc_NtkBoxNum(pNtk) ); + Abc_NtkAddDummyPiNames( pNtk ); + Abc_NtkAddDummyPoNames( pNtk ); + Abc_NtkAddDummyAssertNames( pNtk ); + Abc_NtkAddDummyBoxNames( pNtk ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abc/abcNetlist.c b/abc_with_bb_support/src/base/abc/abcNetlist.c new file mode 100644 index 000000000..66a3c83c1 --- /dev/null +++ b/abc_with_bb_support/src/base/abc/abcNetlist.c @@ -0,0 +1,411 @@ +/**CFile**************************************************************** + + FileName [abcNetlist.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Transforms netlist into a logic network and vice versa.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcNetlist.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +//#include "seq.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Abc_NtkAddPoBuffers( Abc_Ntk_t * pNtk ); +static Abc_Ntk_t * Abc_NtkLogicToNetlist( Abc_Ntk_t * pNtk ); +static Abc_Ntk_t * Abc_NtkAigToLogicSop( Abc_Ntk_t * pNtk ); +static Abc_Ntk_t * Abc_NtkAigToLogicSopBench( Abc_Ntk_t * pNtk ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Transform the netlist into a logic network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkToLogic( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj, * pFanin; + int i, k; + // consider the case of the AIG + if ( Abc_NtkIsStrash(pNtk) ) + return Abc_NtkAigToLogicSop( pNtk ); + assert( Abc_NtkIsNetlist(pNtk) ); + // consider simple case when there is hierarchy +// assert( pNtk->pDesign == NULL ); + assert( Abc_NtkWhiteboxNum(pNtk) == 0 ); + assert( Abc_NtkBlackboxNum(pNtk) == 0 ); + // start the network + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_LOGIC, pNtk->ntkFunc ); + // duplicate the nodes + Abc_NtkForEachNode( pNtk, pObj, i ) + Abc_NtkDupObj(pNtkNew, pObj, 0); + // reconnect the internal nodes in the new network + Abc_NtkForEachNode( pNtk, pObj, i ) + Abc_ObjForEachFanin( pObj, pFanin, k ) + Abc_ObjAddFanin( pObj->pCopy, Abc_ObjFanin0(pFanin)->pCopy ); + // collect the CO nodes + Abc_NtkFinalize( pNtk, pNtkNew ); + // fix the problem with CO pointing directly to CIs + Abc_NtkLogicMakeSimpleCos( pNtkNew, 0 ); + // duplicate EXDC + if ( pNtk->pExdc ) + pNtkNew->pExdc = Abc_NtkToLogic( pNtk->pExdc ); + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkToLogic(): Network check has failed.\n" ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Transform the logic network into a netlist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkToNetlist( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkNew, * pNtkTemp; + assert( Abc_NtkIsLogic(pNtk) || Abc_NtkIsStrash(pNtk) ); + if ( Abc_NtkIsStrash(pNtk) ) + { + pNtkTemp = Abc_NtkAigToLogicSop(pNtk); + pNtkNew = Abc_NtkLogicToNetlist( pNtkTemp ); + Abc_NtkDelete( pNtkTemp ); + return pNtkNew; + } + return Abc_NtkLogicToNetlist( pNtk ); +} + +/**Function************************************************************* + + Synopsis [Converts the AIG into the netlist.] + + Description [This procedure does not copy the choices.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkToNetlistBench( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkNew, * pNtkTemp; + assert( Abc_NtkIsStrash(pNtk) ); + pNtkTemp = Abc_NtkAigToLogicSopBench( pNtk ); + pNtkNew = Abc_NtkLogicToNetlist( pNtkTemp ); + Abc_NtkDelete( pNtkTemp ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Transform the logic network into a netlist.] + + Description [The logic network given to this procedure should + have exactly the same structure as the resulting netlist. The COs + can only point to CIs if they have identical names. Otherwise, + they should have a node between them, even if this node is + inverter or buffer.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkLogicToNetlist( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj, * pNet, * pDriver, * pFanin; + int i, k; + + assert( Abc_NtkIsLogic(pNtk) ); + + // remove dangling nodes + Abc_NtkCleanup( pNtk, 0 ); + + // make sure the CO names are unique + Abc_NtkCheckUniqueCiNames( pNtk ); + Abc_NtkCheckUniqueCoNames( pNtk ); + Abc_NtkCheckUniqueCioNames( pNtk ); + +// assert( Abc_NtkLogicHasSimpleCos(pNtk) ); + if ( !Abc_NtkLogicHasSimpleCos(pNtk) ) + { + printf( "Abc_NtkLogicToNetlist() warning: The network is converted to have simple COs.\n" ); + Abc_NtkLogicMakeSimpleCos( pNtk, 0 ); + } + + // start the netlist by creating PI/PO/Latch objects + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_NETLIST, pNtk->ntkFunc ); + // create the CI nets and remember them in the new CI nodes + Abc_NtkForEachCi( pNtk, pObj, i ) + { + pNet = Abc_NtkFindOrCreateNet( pNtkNew, Abc_ObjName(pObj) ); + Abc_ObjAddFanin( pNet, pObj->pCopy ); + pObj->pCopy->pCopy = pNet; + } + // duplicate all nodes + Abc_NtkForEachNode( pNtk, pObj, i ) + Abc_NtkDupObj(pNtkNew, pObj, 0); + // first add the nets to the CO drivers + Abc_NtkForEachCo( pNtk, pObj, i ) + { + pDriver = Abc_ObjFanin0(pObj); + if ( Abc_ObjIsCi(pDriver) ) + { + assert( !strcmp( Abc_ObjName(pDriver), Abc_ObjName(pObj) ) ); + Abc_ObjAddFanin( pObj->pCopy, pDriver->pCopy->pCopy ); + continue; + } + assert( Abc_ObjIsNode(pDriver) ); + // if the CO driver has no net, create it + if ( pDriver->pCopy->pCopy == NULL ) + { + // create the CO net and connect it to CO + pNet = Abc_NtkFindOrCreateNet( pNtkNew, Abc_ObjName(pObj) ); + Abc_ObjAddFanin( pObj->pCopy, pNet ); + // connect the CO net to the new driver and remember it in the new driver + Abc_ObjAddFanin( pNet, pDriver->pCopy ); + pDriver->pCopy->pCopy = pNet; + } + else + { + assert( !strcmp( Abc_ObjName(pDriver->pCopy->pCopy), Abc_ObjName(pObj) ) ); + Abc_ObjAddFanin( pObj->pCopy, pDriver->pCopy->pCopy ); + } + } + // create the missing nets + Abc_NtkForEachNode( pNtk, pObj, i ) + { + if ( pObj->pCopy->pCopy ) // the net of the new object is already created + continue; + // create the new net + pNet = Abc_NtkFindOrCreateNet( pNtkNew, Abc_ObjName(pObj) ); // here we create ridiculous names net line "n48", where 48 is the ID of the node + Abc_ObjAddFanin( pNet, pObj->pCopy ); + pObj->pCopy->pCopy = pNet; + } + // connect nodes to the fanins nets + Abc_NtkForEachNode( pNtk, pObj, i ) + Abc_ObjForEachFanin( pObj, pFanin, k ) + Abc_ObjAddFanin( pObj->pCopy, pFanin->pCopy->pCopy ); + // duplicate EXDC + if ( pNtk->pExdc ) + pNtkNew->pExdc = Abc_NtkToNetlist( pNtk->pExdc ); + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkLogicToNetlist(): Network check has failed.\n" ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Converts the AIG into the logic network with SOPs.] + + Description [Correctly handles the case of choice nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkAigToLogicSop( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj, * pFanin, * pNodeNew; + Vec_Int_t * vInts; + int i, k; + assert( Abc_NtkIsStrash(pNtk) ); + // start the network + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_LOGIC, ABC_FUNC_SOP ); + // if the constant node is used, duplicate it + pObj = Abc_AigConst1(pNtk); + if ( Abc_ObjFanoutNum(pObj) > 0 ) + pObj->pCopy = Abc_NtkCreateNodeConst1(pNtkNew); + // duplicate the nodes and create node functions + Abc_NtkForEachNode( pNtk, pObj, i ) + { + Abc_NtkDupObj(pNtkNew, pObj, 0); + pObj->pCopy->pData = Abc_SopCreateAnd2( pNtkNew->pManFunc, Abc_ObjFaninC0(pObj), Abc_ObjFaninC1(pObj) ); + } + // create the choice nodes + Abc_NtkForEachNode( pNtk, pObj, i ) + { + if ( !Abc_AigNodeIsChoice(pObj) ) + continue; + // create an OR gate + pNodeNew = Abc_NtkCreateNode(pNtkNew); + // add fanins + vInts = Vec_IntAlloc( 10 ); + for ( pFanin = pObj; pFanin; pFanin = pFanin->pData ) + { + Vec_IntPush( vInts, (int)(pObj->fPhase != pFanin->fPhase) ); + Abc_ObjAddFanin( pNodeNew, pFanin->pCopy ); + } + // create the logic function + pNodeNew->pData = Abc_SopCreateOrMultiCube( pNtkNew->pManFunc, Vec_IntSize(vInts), Vec_IntArray(vInts) ); + // set the new node + pObj->pCopy->pCopy = pNodeNew; + Vec_IntFree( vInts ); + } + // connect the internal nodes + Abc_NtkForEachNode( pNtk, pObj, i ) + Abc_ObjForEachFanin( pObj, pFanin, k ) + if ( pFanin->pCopy->pCopy ) + Abc_ObjAddFanin( pObj->pCopy, pFanin->pCopy->pCopy ); + else + Abc_ObjAddFanin( pObj->pCopy, pFanin->pCopy ); + // connect the COs +// Abc_NtkFinalize( pNtk, pNtkNew ); + Abc_NtkForEachCo( pNtk, pObj, i ) + { + pFanin = Abc_ObjFanin0(pObj); + if ( pFanin->pCopy->pCopy ) + pNodeNew = Abc_ObjNotCond(pFanin->pCopy->pCopy, Abc_ObjFaninC0(pObj)); + else + pNodeNew = Abc_ObjNotCond(pFanin->pCopy, Abc_ObjFaninC0(pObj)); + Abc_ObjAddFanin( pObj->pCopy, pNodeNew ); + } + + // fix the problem with complemented and duplicated CO edges + Abc_NtkLogicMakeSimpleCos( pNtkNew, 0 ); + // duplicate the EXDC Ntk + if ( pNtk->pExdc ) + { + if ( Abc_NtkIsStrash(pNtk->pExdc) ) + pNtkNew->pExdc = Abc_NtkAigToLogicSop( pNtk->pExdc ); + else + pNtkNew->pExdc = Abc_NtkDup( pNtk->pExdc ); + } + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkAigToLogicSop(): Network check has failed.\n" ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Converts the AIG into the logic network with SOPs for bench writing.] + + Description [This procedure does not copy the choices.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkAigToLogicSopBench( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj, * pFanin; + Vec_Ptr_t * vNodes; + int i, k; + assert( Abc_NtkIsStrash(pNtk) ); + if ( Abc_NtkGetChoiceNum(pNtk) ) + printf( "Warning: Choice nodes are skipped.\n" ); + // start the network + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_LOGIC, ABC_FUNC_SOP ); + // collect the nodes to be used (marks all nodes with current TravId) + vNodes = Abc_NtkDfs( pNtk, 0 ); + // create inverters for the CI and remember them + pObj = Abc_AigConst1(pNtk); + if ( Abc_AigNodeHasComplFanoutEdgeTrav(pObj) ) + { + pObj->pCopy = Abc_NtkCreateNodeConst1(pNtkNew); + pObj->pCopy->pCopy = Abc_NtkCreateNodeInv( pNtkNew, pObj->pCopy ); + } + Abc_NtkForEachCi( pNtk, pObj, i ) + if ( Abc_AigNodeHasComplFanoutEdgeTrav(pObj) ) + pObj->pCopy->pCopy = Abc_NtkCreateNodeInv( pNtkNew, pObj->pCopy ); + // duplicate the nodes, create node functions, and inverters + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + Abc_NtkDupObj( pNtkNew, pObj, 0 ); + pObj->pCopy->pData = Abc_SopCreateAnd( pNtkNew->pManFunc, 2, NULL ); + if ( Abc_AigNodeHasComplFanoutEdgeTrav(pObj) ) + pObj->pCopy->pCopy = Abc_NtkCreateNodeInv( pNtkNew, pObj->pCopy ); + } + // connect the objects + Vec_PtrForEachEntry( vNodes, pObj, i ) + Abc_ObjForEachFanin( pObj, pFanin, k ) + { + if ( Abc_ObjFaninC( pObj, k ) ) + Abc_ObjAddFanin( pObj->pCopy, pFanin->pCopy->pCopy ); + else + Abc_ObjAddFanin( pObj->pCopy, pFanin->pCopy ); + } + Vec_PtrFree( vNodes ); + // connect the COs + Abc_NtkForEachCo( pNtk, pObj, i ) + { + pFanin = Abc_ObjFanin0(pObj); + if ( Abc_ObjFaninC0( pObj ) ) + Abc_ObjAddFanin( pObj->pCopy, pFanin->pCopy->pCopy ); + else + Abc_ObjAddFanin( pObj->pCopy, pFanin->pCopy ); + } + // fix the problem with complemented and duplicated CO edges + Abc_NtkLogicMakeSimpleCos( pNtkNew, 0 ); + // duplicate the EXDC Ntk + if ( pNtk->pExdc ) + printf( "Warning: The EXDc network is skipped.\n" ); + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkAigToLogicSopBench(): Network check has failed.\n" ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Adds buffers for each PO.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkAddPoBuffers( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj, * pFanin, * pFaninNew; + int i; + assert( Abc_NtkIsStrash(pNtk) ); + Abc_NtkForEachPo( pNtk, pObj, i ) + { + pFanin = Abc_ObjChild0(pObj); + pFaninNew = Abc_NtkCreateNode(pNtk); + Abc_ObjAddFanin( pFaninNew, pFanin ); + Abc_ObjPatchFanin( pObj, pFanin, pFaninNew ); + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abc/abcNtk.c b/abc_with_bb_support/src/base/abc/abcNtk.c new file mode 100644 index 000000000..7c6ced997 --- /dev/null +++ b/abc_with_bb_support/src/base/abc/abcNtk.c @@ -0,0 +1,1094 @@ +/**CFile**************************************************************** + + FileName [abcNtk.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Network creation/duplication/deletion procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcNtk.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "abcInt.h" +#include "main.h" +#include "mio.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Creates a new Ntk.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkAlloc( Abc_NtkType_t Type, Abc_NtkFunc_t Func, int fUseMemMan ) +{ + Abc_Ntk_t * pNtk; + pNtk = ALLOC( Abc_Ntk_t, 1 ); + memset( pNtk, 0, sizeof(Abc_Ntk_t) ); + pNtk->ntkType = Type; + pNtk->ntkFunc = Func; + // start the object storage + pNtk->vObjs = Vec_PtrAlloc( 100 ); + pNtk->vAsserts = Vec_PtrAlloc( 100 ); + pNtk->vPios = Vec_PtrAlloc( 100 ); + pNtk->vPis = Vec_PtrAlloc( 100 ); + pNtk->vPos = Vec_PtrAlloc( 100 ); + pNtk->vCis = Vec_PtrAlloc( 100 ); + pNtk->vCos = Vec_PtrAlloc( 100 ); + pNtk->vBoxes = Vec_PtrAlloc( 100 ); + // start the memory managers + pNtk->pMmObj = fUseMemMan? Extra_MmFixedStart( sizeof(Abc_Obj_t) ) : NULL; + pNtk->pMmStep = fUseMemMan? Extra_MmStepStart( ABC_NUM_STEPS ) : NULL; + // get ready to assign the first Obj ID + pNtk->nTravIds = 1; + // start the functionality manager + if ( Abc_NtkIsStrash(pNtk) ) + pNtk->pManFunc = Abc_AigAlloc( pNtk ); + else if ( Abc_NtkHasSop(pNtk) || Abc_NtkHasBlifMv(pNtk) ) + pNtk->pManFunc = Extra_MmFlexStart(); + else if ( Abc_NtkHasBdd(pNtk) ) + pNtk->pManFunc = Cudd_Init( 20, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 ); + else if ( Abc_NtkHasAig(pNtk) ) + pNtk->pManFunc = Hop_ManStart(); + else if ( Abc_NtkHasMapping(pNtk) ) + pNtk->pManFunc = Abc_FrameReadLibGen(); + else if ( !Abc_NtkHasBlackbox(pNtk) ) + assert( 0 ); + // name manager + pNtk->pManName = Nm_ManCreate( 200 ); + // attribute manager + pNtk->vAttrs = Vec_PtrStart( VEC_ATTR_TOTAL_NUM ); + return pNtk; +} + +/**Function************************************************************* + + Synopsis [Starts a new network using existing network as a model.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkStartFrom( Abc_Ntk_t * pNtk, Abc_NtkType_t Type, Abc_NtkFunc_t Func ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj; + int fCopyNames, i; + if ( pNtk == NULL ) + return NULL; + // decide whether to copy the names + fCopyNames = ( Type != ABC_NTK_NETLIST ); + // start the network + pNtkNew = Abc_NtkAlloc( Type, Func, 1 ); + // duplicate the name and the spec + pNtkNew->pName = Extra_UtilStrsav(pNtk->pName); + pNtkNew->pSpec = Extra_UtilStrsav(pNtk->pSpec); + // clean the node copy fields + Abc_NtkCleanCopy( pNtk ); + // map the constant nodes + if ( Abc_NtkIsStrash(pNtk) && Abc_NtkIsStrash(pNtkNew) ) + Abc_AigConst1(pNtk)->pCopy = Abc_AigConst1(pNtkNew); + // clone CIs/CIs/boxes + Abc_NtkForEachPi( pNtk, pObj, i ) + Abc_NtkDupObj( pNtkNew, pObj, fCopyNames ); + Abc_NtkForEachPo( pNtk, pObj, i ) + Abc_NtkDupObj( pNtkNew, pObj, fCopyNames ); + Abc_NtkForEachAssert( pNtk, pObj, i ) + Abc_NtkDupObj( pNtkNew, pObj, fCopyNames ); + Abc_NtkForEachBox( pNtk, pObj, i ) + Abc_NtkDupBox( pNtkNew, pObj, fCopyNames ); + // transfer the names +// Abc_NtkTrasferNames( pNtk, pNtkNew ); + Abc_ManTimeDup( pNtk, pNtkNew ); + // check that the CI/CO/latches are copied correctly + assert( Abc_NtkCiNum(pNtk) == Abc_NtkCiNum(pNtkNew) ); + assert( Abc_NtkCoNum(pNtk) == Abc_NtkCoNum(pNtkNew) ); + assert( Abc_NtkLatchNum(pNtk) == Abc_NtkLatchNum(pNtkNew) ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Starts a new network using existing network as a model.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkStartFromNoLatches( Abc_Ntk_t * pNtk, Abc_NtkType_t Type, Abc_NtkFunc_t Func ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj; + int i; + if ( pNtk == NULL ) + return NULL; + assert( Type != ABC_NTK_NETLIST ); + // start the network + pNtkNew = Abc_NtkAlloc( Type, Func, 1 ); + // duplicate the name and the spec + pNtkNew->pName = Extra_UtilStrsav(pNtk->pName); + pNtkNew->pSpec = Extra_UtilStrsav(pNtk->pSpec); + // clean the node copy fields + Abc_NtkCleanCopy( pNtk ); + // map the constant nodes + if ( Abc_NtkIsStrash(pNtk) && Abc_NtkIsStrash(pNtkNew) ) + Abc_AigConst1(pNtk)->pCopy = Abc_AigConst1(pNtkNew); + // clone CIs/CIs/boxes + Abc_NtkForEachPi( pNtk, pObj, i ) + Abc_NtkDupObj( pNtkNew, pObj, 1 ); + Abc_NtkForEachPo( pNtk, pObj, i ) + Abc_NtkDupObj( pNtkNew, pObj, 1 ); + Abc_NtkForEachAssert( pNtk, pObj, i ) + Abc_NtkDupObj( pNtkNew, pObj, 1 ); + Abc_NtkForEachBox( pNtk, pObj, i ) + { + if ( Abc_ObjIsLatch(pObj) ) + continue; + Abc_NtkDupBox(pNtkNew, pObj, 1); + } + // transfer the names +// Abc_NtkTrasferNamesNoLatches( pNtk, pNtkNew ); + Abc_ManTimeDup( pNtk, pNtkNew ); + // check that the CI/CO/latches are copied correctly + assert( Abc_NtkPiNum(pNtk) == Abc_NtkPiNum(pNtkNew) ); + assert( Abc_NtkPoNum(pNtk) == Abc_NtkPoNum(pNtkNew) ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Finalizes the network using the existing network as a model.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkFinalize( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkNew ) +{ + Abc_Obj_t * pObj, * pDriver, * pDriverNew; + int i; + // set the COs of the strashed network + Abc_NtkForEachCo( pNtk, pObj, i ) + { + pDriver = Abc_ObjFanin0Ntk( Abc_ObjFanin0(pObj) ); + pDriverNew = Abc_ObjNotCond(pDriver->pCopy, Abc_ObjFaninC0(pObj)); + Abc_ObjAddFanin( pObj->pCopy, pDriverNew ); + } +} + +/**Function************************************************************* + + Synopsis [Starts a new network using existing network as a model.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkStartRead( char * pName ) +{ + Abc_Ntk_t * pNtkNew; + // allocate the empty network + pNtkNew = Abc_NtkAlloc( ABC_NTK_NETLIST, ABC_FUNC_SOP, 1 ); + // set the specs + pNtkNew->pName = Extra_FileNameGeneric(pName); + pNtkNew->pSpec = Extra_UtilStrsav(pName); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Finalizes the network using the existing network as a model.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkFinalizeRead( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pBox, * pObj, * pTerm, * pNet; + int i; + if ( Abc_NtkHasBlackbox(pNtk) && Abc_NtkBoxNum(pNtk) == 0 ) + { + pBox = Abc_NtkCreateBlackbox(pNtk); + Abc_NtkForEachPi( pNtk, pObj, i ) + { + pTerm = Abc_NtkCreateBi(pNtk); + Abc_ObjAddFanin( pTerm, Abc_ObjFanout0(pObj) ); + Abc_ObjAddFanin( pBox, pTerm ); + } + Abc_NtkForEachPo( pNtk, pObj, i ) + { + pTerm = Abc_NtkCreateBo(pNtk); + Abc_ObjAddFanin( pTerm, pBox ); + Abc_ObjAddFanin( Abc_ObjFanin0(pObj), pTerm ); + } + return; + } + assert( Abc_NtkIsNetlist(pNtk) ); + + // check if constant 0 net is used + pNet = Abc_NtkFindNet( pNtk, "1\'b0" ); + if ( pNet ) + { + if ( Abc_ObjFanoutNum(pNet) == 0 ) + Abc_NtkDeleteObj(pNet); + else if ( Abc_ObjFaninNum(pNet) == 0 ) + Abc_ObjAddFanin( pNet, Abc_NtkCreateNodeConst0(pNtk) ); + } + // check if constant 1 net is used + pNet = Abc_NtkFindNet( pNtk, "1\'b1" ); + if ( pNet ) + { + if ( Abc_ObjFanoutNum(pNet) == 0 ) + Abc_NtkDeleteObj(pNet); + else if ( Abc_ObjFaninNum(pNet) == 0 ) + Abc_ObjAddFanin( pNet, Abc_NtkCreateNodeConst1(pNtk) ); + } + // fix the net drivers + Abc_NtkFixNonDrivenNets( pNtk ); + + // reorder the CI/COs to PI/POs first + Abc_NtkOrderCisCos( pNtk ); +} + +/**Function************************************************************* + + Synopsis [Duplicate the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkDup( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj, * pFanin; + int i, k; + if ( pNtk == NULL ) + return NULL; + // start the network + pNtkNew = Abc_NtkStartFrom( pNtk, pNtk->ntkType, pNtk->ntkFunc ); + // copy the internal nodes + if ( Abc_NtkIsStrash(pNtk) ) + { + // copy the AND gates + Abc_AigForEachAnd( pNtk, pObj, i ) + pObj->pCopy = Abc_AigAnd( pNtkNew->pManFunc, Abc_ObjChild0Copy(pObj), Abc_ObjChild1Copy(pObj) ); + // relink the choice nodes + Abc_AigForEachAnd( pNtk, pObj, i ) + if ( pObj->pData ) + pObj->pCopy->pData = ((Abc_Obj_t *)pObj->pData)->pCopy; + // relink the CO nodes + Abc_NtkForEachCo( pNtk, pObj, i ) + Abc_ObjAddFanin( pObj->pCopy, Abc_ObjChild0Copy(pObj) ); + // get the number of nodes before and after + if ( Abc_NtkNodeNum(pNtk) != Abc_NtkNodeNum(pNtkNew) ) + printf( "Warning: Structural hashing during duplication reduced %d nodes (this is a minor bug).\n", + Abc_NtkNodeNum(pNtk) - Abc_NtkNodeNum(pNtkNew) ); + } + else + { + // duplicate the nets and nodes (CIs/COs/latches already dupped) + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( pObj->pCopy == NULL ) + Abc_NtkDupObj(pNtkNew, pObj, 0); + // reconnect all objects (no need to transfer attributes on edges) + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( !Abc_ObjIsBox(pObj) && !Abc_ObjIsBo(pObj) ) + Abc_ObjForEachFanin( pObj, pFanin, k ) + Abc_ObjAddFanin( pObj->pCopy, pFanin->pCopy ); + } + // duplicate the EXDC Ntk + if ( pNtk->pExdc ) + pNtkNew->pExdc = Abc_NtkDup( pNtk->pExdc ); + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkDup(): Network check has failed.\n" ); + pNtk->pCopy = pNtkNew; + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Duplicate the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkDouble( Abc_Ntk_t * pNtk ) +{ + char Buffer[500]; + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj, * pFanin; + int i, k; + assert( Abc_NtkIsLogic(pNtk) ); + + // start the network + pNtkNew = Abc_NtkAlloc( pNtk->ntkType, pNtk->ntkFunc, 1 ); + sprintf( Buffer, "%s%s", pNtk->pName, "_2x" ); + pNtkNew->pName = Extra_UtilStrsav(Buffer); + + // clean the node copy fields + Abc_NtkCleanCopy( pNtk ); + // clone CIs/CIs/boxes + Abc_NtkForEachPi( pNtk, pObj, i ) + Abc_NtkDupObj( pNtkNew, pObj, 0 ); + Abc_NtkForEachPo( pNtk, pObj, i ) + Abc_NtkDupObj( pNtkNew, pObj, 0 ); + Abc_NtkForEachAssert( pNtk, pObj, i ) + Abc_NtkDupObj( pNtkNew, pObj, 0 ); + Abc_NtkForEachBox( pNtk, pObj, i ) + Abc_NtkDupBox( pNtkNew, pObj, 0 ); + // copy the internal nodes + // duplicate the nets and nodes (CIs/COs/latches already dupped) + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( pObj->pCopy == NULL ) + Abc_NtkDupObj(pNtkNew, pObj, 0); + // reconnect all objects (no need to transfer attributes on edges) + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( !Abc_ObjIsBox(pObj) && !Abc_ObjIsBo(pObj) ) + Abc_ObjForEachFanin( pObj, pFanin, k ) + Abc_ObjAddFanin( pObj->pCopy, pFanin->pCopy ); + + // clean the node copy fields + Abc_NtkCleanCopy( pNtk ); + // clone CIs/CIs/boxes + Abc_NtkForEachPi( pNtk, pObj, i ) + Abc_NtkDupObj( pNtkNew, pObj, 0 ); + Abc_NtkForEachPo( pNtk, pObj, i ) + Abc_NtkDupObj( pNtkNew, pObj, 0 ); + Abc_NtkForEachAssert( pNtk, pObj, i ) + Abc_NtkDupObj( pNtkNew, pObj, 0 ); + Abc_NtkForEachBox( pNtk, pObj, i ) + Abc_NtkDupBox( pNtkNew, pObj, 0 ); + // copy the internal nodes + // duplicate the nets and nodes (CIs/COs/latches already dupped) + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( pObj->pCopy == NULL ) + Abc_NtkDupObj(pNtkNew, pObj, 0); + // reconnect all objects (no need to transfer attributes on edges) + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( !Abc_ObjIsBox(pObj) && !Abc_ObjIsBo(pObj) ) + Abc_ObjForEachFanin( pObj, pFanin, k ) + Abc_ObjAddFanin( pObj->pCopy, pFanin->pCopy ); + + // assign names + Abc_NtkForEachCi( pNtk, pObj, i ) + { + Abc_ObjAssignName( Abc_NtkCi(pNtkNew, i), "1_", Abc_ObjName(pObj) ); + Abc_ObjAssignName( Abc_NtkCi(pNtkNew, Abc_NtkCiNum(pNtk) + i), "2_", Abc_ObjName(pObj) ); + } + Abc_NtkForEachCo( pNtk, pObj, i ) + { + Abc_ObjAssignName( Abc_NtkCo(pNtkNew, i), "1_", Abc_ObjName(pObj) ); + Abc_ObjAssignName( Abc_NtkCo(pNtkNew, Abc_NtkCoNum(pNtk) + i), "2_", Abc_ObjName(pObj) ); + } + + // perform the final check + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkDup(): Network check has failed.\n" ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Attaches the second network at the bottom of the first.] + + Description [Returns the first network. Deletes the second network.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkAttachBottom( Abc_Ntk_t * pNtkTop, Abc_Ntk_t * pNtkBottom ) +{ + Abc_Obj_t * pObj, * pFanin, * pBuffer; + Vec_Ptr_t * vNodes; + int i, k; + assert( pNtkBottom != NULL ); + if ( pNtkTop == NULL ) + return pNtkBottom; + // make sure the networks are combinational + assert( Abc_NtkPiNum(pNtkTop) == Abc_NtkCiNum(pNtkTop) ); + assert( Abc_NtkPiNum(pNtkBottom) == Abc_NtkCiNum(pNtkBottom) ); + // make sure the POs of the bottom correspond to the PIs of the top + assert( Abc_NtkPoNum(pNtkBottom) == Abc_NtkPiNum(pNtkTop) ); + assert( Abc_NtkPiNum(pNtkBottom) < Abc_NtkPiNum(pNtkTop) ); + // add buffers for the PIs of the top - save results in the POs of the bottom + Abc_NtkForEachPi( pNtkTop, pObj, i ) + { + pBuffer = Abc_NtkCreateNodeBuf( pNtkTop, NULL ); + Abc_ObjTransferFanout( pObj, pBuffer ); + Abc_NtkPo(pNtkBottom, i)->pCopy = pBuffer; + } + // remove useless PIs of the top + for ( i = Abc_NtkPiNum(pNtkTop) - 1; i >= Abc_NtkPiNum(pNtkBottom); i-- ) + Abc_NtkDeleteObj( Abc_NtkPi(pNtkTop, i) ); + assert( Abc_NtkPiNum(pNtkBottom) == Abc_NtkPiNum(pNtkTop) ); + // copy the bottom network + Abc_NtkForEachPi( pNtkBottom, pObj, i ) + Abc_NtkPi(pNtkBottom, i)->pCopy = Abc_NtkPi(pNtkTop, i); + // construct all nodes + vNodes = Abc_NtkDfs( pNtkBottom, 0 ); + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + Abc_NtkDupObj(pNtkTop, pObj, 0); + Abc_ObjForEachFanin( pObj, pFanin, k ) + Abc_ObjAddFanin( pObj->pCopy, pFanin->pCopy ); + } + Vec_PtrFree( vNodes ); + // connect the POs + Abc_NtkForEachPo( pNtkBottom, pObj, i ) + Abc_ObjAddFanin( pObj->pCopy, Abc_ObjFanin0(pObj)->pCopy ); + // delete old network + Abc_NtkDelete( pNtkBottom ); + // return the network + if ( !Abc_NtkCheck( pNtkTop ) ) + fprintf( stdout, "Abc_NtkAttachBottom(): Network check has failed.\n" ); + return pNtkTop; +} + +/**Function************************************************************* + + Synopsis [Creates the network composed of one logic cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkCreateCone( Abc_Ntk_t * pNtk, Abc_Obj_t * pNode, char * pNodeName, int fUseAllCis ) +{ + Abc_Ntk_t * pNtkNew; + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj, * pFanin, * pNodeCoNew; + char Buffer[1000]; + int i, k; + + assert( Abc_NtkIsLogic(pNtk) || Abc_NtkIsStrash(pNtk) ); + assert( Abc_ObjIsNode(pNode) ); + + // start the network + pNtkNew = Abc_NtkAlloc( pNtk->ntkType, pNtk->ntkFunc, 1 ); + // set the name + sprintf( Buffer, "%s_%s", pNtk->pName, pNodeName ); + pNtkNew->pName = Extra_UtilStrsav(Buffer); + + // establish connection between the constant nodes + if ( Abc_NtkIsStrash(pNtk) ) + Abc_AigConst1(pNtk)->pCopy = Abc_AigConst1(pNtkNew); + + // collect the nodes in the TFI of the output (mark the TFI) + vNodes = Abc_NtkDfsNodes( pNtk, &pNode, 1 ); + // create the PIs + Abc_NtkForEachCi( pNtk, pObj, i ) + { + if ( fUseAllCis || Abc_NodeIsTravIdCurrent(pObj) ) // TravId is set by DFS + { + pObj->pCopy = Abc_NtkCreatePi(pNtkNew); + Abc_ObjAssignName( pObj->pCopy, Abc_ObjName(pObj), NULL ); + } + } + // add the PO corresponding to this output + pNodeCoNew = Abc_NtkCreatePo( pNtkNew ); + Abc_ObjAssignName( pNodeCoNew, pNodeName, NULL ); + // copy the nodes + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + // if it is an AIG, add to the hash table + if ( Abc_NtkIsStrash(pNtk) ) + { + pObj->pCopy = Abc_AigAnd( pNtkNew->pManFunc, Abc_ObjChild0Copy(pObj), Abc_ObjChild1Copy(pObj) ); + } + else + { + Abc_NtkDupObj( pNtkNew, pObj, 0 ); + Abc_ObjForEachFanin( pObj, pFanin, k ) + Abc_ObjAddFanin( pObj->pCopy, pFanin->pCopy ); + } + } + // connect the internal nodes to the new CO + Abc_ObjAddFanin( pNodeCoNew, pNode->pCopy ); + Vec_PtrFree( vNodes ); + + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkCreateCone(): Network check has failed.\n" ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Creates the network composed of several logic cones.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkCreateConeArray( Abc_Ntk_t * pNtk, Vec_Ptr_t * vRoots, int fUseAllCis ) +{ + Abc_Ntk_t * pNtkNew; + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj, * pFanin, * pNodeCoNew; + char Buffer[1000]; + int i, k; + + assert( Abc_NtkIsLogic(pNtk) || Abc_NtkIsStrash(pNtk) ); + + // start the network + pNtkNew = Abc_NtkAlloc( pNtk->ntkType, pNtk->ntkFunc, 1 ); + // set the name + sprintf( Buffer, "%s_part", pNtk->pName ); + pNtkNew->pName = Extra_UtilStrsav(Buffer); + + // establish connection between the constant nodes + if ( Abc_NtkIsStrash(pNtk) ) + Abc_AigConst1(pNtk)->pCopy = Abc_AigConst1(pNtkNew); + + // collect the nodes in the TFI of the output (mark the TFI) + vNodes = Abc_NtkDfsNodes( pNtk, (Abc_Obj_t **)Vec_PtrArray(vRoots), Vec_PtrSize(vRoots) ); + + // create the PIs + Abc_NtkForEachCi( pNtk, pObj, i ) + { + if ( fUseAllCis || Abc_NodeIsTravIdCurrent(pObj) ) // TravId is set by DFS + { + pObj->pCopy = Abc_NtkCreatePi(pNtkNew); + Abc_ObjAssignName( pObj->pCopy, Abc_ObjName(pObj), NULL ); + } + } + + // copy the nodes + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + // if it is an AIG, add to the hash table + if ( Abc_NtkIsStrash(pNtk) ) + { + pObj->pCopy = Abc_AigAnd( pNtkNew->pManFunc, Abc_ObjChild0Copy(pObj), Abc_ObjChild1Copy(pObj) ); + } + else + { + Abc_NtkDupObj( pNtkNew, pObj, 0 ); + Abc_ObjForEachFanin( pObj, pFanin, k ) + Abc_ObjAddFanin( pObj->pCopy, pFanin->pCopy ); + } + } + Vec_PtrFree( vNodes ); + + // add the PO corresponding to the nodes + Vec_PtrForEachEntry( vRoots, pObj, i ) + { + // create the PO node + pNodeCoNew = Abc_NtkCreatePo( pNtkNew ); + // connect the internal nodes to the new CO + if ( Abc_ObjIsCo(pObj) ) + Abc_ObjAddFanin( pNodeCoNew, Abc_ObjChild0Copy(pObj) ); + else + Abc_ObjAddFanin( pNodeCoNew, pObj->pCopy ); + // assign the name + Abc_ObjAssignName( pNodeCoNew, Abc_ObjName(pObj), NULL ); + } + + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkCreateConeArray(): Network check has failed.\n" ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Creates the network composed of MFFC of one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkCreateMffc( Abc_Ntk_t * pNtk, Abc_Obj_t * pNode, char * pNodeName ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj, * pFanin, * pNodeCoNew; + Vec_Ptr_t * vCone, * vSupp; + char Buffer[1000]; + int i, k; + + assert( Abc_NtkIsLogic(pNtk) || Abc_NtkIsStrash(pNtk) ); + assert( Abc_ObjIsNode(pNode) ); + + // start the network + pNtkNew = Abc_NtkAlloc( pNtk->ntkType, pNtk->ntkFunc, 1 ); + // set the name + sprintf( Buffer, "%s_%s", pNtk->pName, pNodeName ); + pNtkNew->pName = Extra_UtilStrsav(Buffer); + + // establish connection between the constant nodes + if ( Abc_NtkIsStrash(pNtk) ) + Abc_AigConst1(pNtk)->pCopy = Abc_AigConst1(pNtkNew); + + // collect the nodes in MFFC + vCone = Vec_PtrAlloc( 100 ); + vSupp = Vec_PtrAlloc( 100 ); + Abc_NodeDeref_rec( pNode ); + Abc_NodeMffsConeSupp( pNode, vCone, vSupp ); + Abc_NodeRef_rec( pNode ); + // create the PIs + Vec_PtrForEachEntry( vSupp, pObj, i ) + { + pObj->pCopy = Abc_NtkCreatePi(pNtkNew); + Abc_ObjAssignName( pObj->pCopy, Abc_ObjName(pObj), NULL ); + } + // create the PO + pNodeCoNew = Abc_NtkCreatePo( pNtkNew ); + Abc_ObjAssignName( pNodeCoNew, pNodeName, NULL ); + // copy the nodes + Vec_PtrForEachEntry( vCone, pObj, i ) + { + // if it is an AIG, add to the hash table + if ( Abc_NtkIsStrash(pNtk) ) + { + pObj->pCopy = Abc_AigAnd( pNtkNew->pManFunc, Abc_ObjChild0Copy(pObj), Abc_ObjChild1Copy(pObj) ); + } + else + { + Abc_NtkDupObj( pNtkNew, pObj, 0 ); + Abc_ObjForEachFanin( pObj, pFanin, k ) + Abc_ObjAddFanin( pObj->pCopy, pFanin->pCopy ); + } + } + // connect the topmost node + Abc_ObjAddFanin( pNodeCoNew, pNode->pCopy ); + Vec_PtrFree( vCone ); + Vec_PtrFree( vSupp ); + + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkCreateMffc(): Network check has failed.\n" ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Creates the miter composed of one multi-output cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkCreateTarget( Abc_Ntk_t * pNtk, Vec_Ptr_t * vRoots, Vec_Int_t * vValues ) +{ + Vec_Ptr_t * vNodes; + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj, * pFinal, * pOther, * pNodePo; + int i; + + assert( Abc_NtkIsLogic(pNtk) ); + + // start the network + Abc_NtkCleanCopy( pNtk ); + pNtkNew = Abc_NtkAlloc( ABC_NTK_STRASH, ABC_FUNC_AIG, 1 ); + pNtkNew->pName = Extra_UtilStrsav(pNtk->pName); + + // collect the nodes in the TFI of the output + vNodes = Abc_NtkDfsNodes( pNtk, (Abc_Obj_t **)vRoots->pArray, vRoots->nSize ); + // create the PIs + Abc_NtkForEachCi( pNtk, pObj, i ) + { + pObj->pCopy = Abc_NtkCreatePi(pNtkNew); + Abc_ObjAssignName( pObj->pCopy, Abc_ObjName(pObj), NULL ); + } + // copy the nodes + Vec_PtrForEachEntry( vNodes, pObj, i ) + pObj->pCopy = Abc_NodeStrash( pNtkNew, pObj, 0 ); + Vec_PtrFree( vNodes ); + + // add the PO + pFinal = Abc_AigConst1( pNtkNew ); + Vec_PtrForEachEntry( vRoots, pObj, i ) + { + if ( Abc_ObjIsCo(pObj) ) + pOther = Abc_ObjFanin0(pObj)->pCopy; + else + pOther = pObj->pCopy; + if ( Vec_IntEntry(vValues, i) == 0 ) + pOther = Abc_ObjNot(pOther); + pFinal = Abc_AigAnd( pNtkNew->pManFunc, pFinal, pOther ); + } + + // add the PO corresponding to this output + pNodePo = Abc_NtkCreatePo( pNtkNew ); + Abc_ObjAddFanin( pNodePo, pFinal ); + Abc_ObjAssignName( pNodePo, "miter", NULL ); + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkCreateTarget(): Network check has failed.\n" ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Creates the network composed of one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkCreateFromNode( Abc_Ntk_t * pNtk, Abc_Obj_t * pNode ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pFanin, * pNodePo; + int i; + // start the network + pNtkNew = Abc_NtkAlloc( pNtk->ntkType, pNtk->ntkFunc, 1 ); + pNtkNew->pName = Extra_UtilStrsav(Abc_ObjName(pNode)); + // add the PIs corresponding to the fanins of the node + Abc_ObjForEachFanin( pNode, pFanin, i ) + { + pFanin->pCopy = Abc_NtkCreatePi( pNtkNew ); + Abc_ObjAssignName( pFanin->pCopy, Abc_ObjName(pFanin), NULL ); + } + // duplicate and connect the node + pNode->pCopy = Abc_NtkDupObj( pNtkNew, pNode, 0 ); + Abc_ObjForEachFanin( pNode, pFanin, i ) + Abc_ObjAddFanin( pNode->pCopy, pFanin->pCopy ); + // create the only PO + pNodePo = Abc_NtkCreatePo( pNtkNew ); + Abc_ObjAddFanin( pNodePo, pNode->pCopy ); + Abc_ObjAssignName( pNodePo, Abc_ObjName(pNode), NULL ); + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkCreateFromNode(): Network check has failed.\n" ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Creates the network composed of one node with the given SOP.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkCreateWithNode( char * pSop ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pFanin, * pNode, * pNodePo; + Vec_Ptr_t * vNames; + int i, nVars; + // start the network + pNtkNew = Abc_NtkAlloc( ABC_NTK_LOGIC, ABC_FUNC_SOP, 1 ); + pNtkNew->pName = Extra_UtilStrsav("ex"); + // create PIs + Vec_PtrPush( pNtkNew->vObjs, NULL ); + nVars = Abc_SopGetVarNum( pSop ); + vNames = Abc_NodeGetFakeNames( nVars ); + for ( i = 0; i < nVars; i++ ) + Abc_ObjAssignName( Abc_NtkCreatePi(pNtkNew), Vec_PtrEntry(vNames, i), NULL ); + Abc_NodeFreeNames( vNames ); + // create the node, add PIs as fanins, set the function + pNode = Abc_NtkCreateNode( pNtkNew ); + Abc_NtkForEachPi( pNtkNew, pFanin, i ) + Abc_ObjAddFanin( pNode, pFanin ); + pNode->pData = Abc_SopRegister( pNtkNew->pManFunc, pSop ); + // create the only PO + pNodePo = Abc_NtkCreatePo(pNtkNew); + Abc_ObjAddFanin( pNodePo, pNode ); + Abc_ObjAssignName( pNodePo, "F", NULL ); + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkCreateWithNode(): Network check has failed.\n" ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Deletes the Ntk.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDelete( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + void * pAttrMan; + int TotalMemory, i; + int LargePiece = (4 << ABC_NUM_STEPS); + if ( pNtk == NULL ) + return; + // free the HAIG + if ( pNtk->pHaig ) + Abc_NtkHaigStop( pNtk ); + // free EXDC Ntk + if ( pNtk->pExdc ) + Abc_NtkDelete( pNtk->pExdc ); + // dereference the BDDs + if ( Abc_NtkHasBdd(pNtk) ) + { + Abc_NtkForEachNode( pNtk, pObj, i ) + Cudd_RecursiveDeref( pNtk->pManFunc, pObj->pData ); + } + // make sure all the marks are clean + Abc_NtkForEachObj( pNtk, pObj, i ) + { + // free large fanout arrays + if ( pNtk->pMmObj && pObj->vFanouts.nCap * 4 > LargePiece ) + FREE( pObj->vFanouts.pArray ); + // these flags should be always zero + // if this is not true, something is wrong somewhere + assert( pObj->fMarkA == 0 ); + assert( pObj->fMarkB == 0 ); + assert( pObj->fMarkC == 0 ); + } + // free the nodes + if ( pNtk->pMmStep == NULL ) + { + Abc_NtkForEachObj( pNtk, pObj, i ) + { + FREE( pObj->vFanouts.pArray ); + FREE( pObj->vFanins.pArray ); + } + } + if ( pNtk->pMmObj == NULL ) + { + Abc_NtkForEachObj( pNtk, pObj, i ) + free( pObj ); + } + + // free the arrays + Vec_PtrFree( pNtk->vPios ); + Vec_PtrFree( pNtk->vPis ); + Vec_PtrFree( pNtk->vPos ); + Vec_PtrFree( pNtk->vCis ); + Vec_PtrFree( pNtk->vCos ); + Vec_PtrFree( pNtk->vAsserts ); + Vec_PtrFree( pNtk->vObjs ); + Vec_PtrFree( pNtk->vBoxes ); + if ( pNtk->vLevelsR ) Vec_IntFree( pNtk->vLevelsR ); + if ( pNtk->pModel ) free( pNtk->pModel ); + TotalMemory = 0; + TotalMemory += pNtk->pMmObj? Extra_MmFixedReadMemUsage(pNtk->pMmObj) : 0; + TotalMemory += pNtk->pMmStep? Extra_MmStepReadMemUsage(pNtk->pMmStep) : 0; +// fprintf( stdout, "The total memory allocated internally by the network = %0.2f Mb.\n", ((double)TotalMemory)/(1<<20) ); + // free the storage + if ( pNtk->pMmObj ) + Extra_MmFixedStop( pNtk->pMmObj ); + if ( pNtk->pMmStep ) + Extra_MmStepStop ( pNtk->pMmStep ); + // name manager + Nm_ManFree( pNtk->pManName ); + // free the timing manager + if ( pNtk->pManTime ) + Abc_ManTimeStop( pNtk->pManTime ); + // start the functionality manager + if ( Abc_NtkIsStrash(pNtk) ) + Abc_AigFree( pNtk->pManFunc ); + else if ( Abc_NtkHasSop(pNtk) || Abc_NtkHasBlifMv(pNtk) ) + Extra_MmFlexStop( pNtk->pManFunc ); + else if ( Abc_NtkHasBdd(pNtk) ) + Extra_StopManager( pNtk->pManFunc ); + else if ( Abc_NtkHasAig(pNtk) ) + { if ( pNtk->pManFunc ) Hop_ManStop( pNtk->pManFunc ); } + else if ( Abc_NtkHasMapping(pNtk) ) + pNtk->pManFunc = NULL; + else if ( !Abc_NtkHasBlackbox(pNtk) ) + assert( 0 ); + // free the hierarchy + if ( pNtk->pDesign ) + { + Abc_LibFree( pNtk->pDesign, pNtk ); + pNtk->pDesign = NULL; + } +// if ( pNtk->pBlackBoxes ) +// Vec_IntFree( pNtk->pBlackBoxes ); + // free node attributes + Vec_PtrForEachEntry( pNtk->vAttrs, pAttrMan, i ) + if ( pAttrMan ) + { +//printf( "deleting attr\n" ); + Vec_AttFree( pAttrMan, 1 ); + } + Vec_PtrFree( pNtk->vAttrs ); + FREE( pNtk->pName ); + FREE( pNtk->pSpec ); + free( pNtk ); +} + +/**Function************************************************************* + + Synopsis [Reads the verilog file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkFixNonDrivenNets( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vNets; + Abc_Obj_t * pNet, * pNode; + int i; + + if ( Abc_NtkNodeNum(pNtk) == 0 && Abc_NtkBoxNum(pNtk) == 0 ) + return; + + // check for non-driven nets + vNets = Vec_PtrAlloc( 100 ); + Abc_NtkForEachNet( pNtk, pNet, i ) + { + if ( Abc_ObjFaninNum(pNet) > 0 ) + continue; + // add the constant 0 driver + pNode = Abc_NtkCreateNodeConst0( pNtk ); + // add the fanout net + Abc_ObjAddFanin( pNet, pNode ); + // add the net to those for which the warning will be printed + Vec_PtrPush( vNets, pNet ); + } + + // print the warning + if ( vNets->nSize > 0 ) + { + printf( "Warning: Constant-0 drivers added to %d non-driven nets in network \"%s\":\n", Vec_PtrSize(vNets), pNtk->pName ); + Vec_PtrForEachEntry( vNets, pNet, i ) + { + printf( "%s%s", (i? ", ": ""), Abc_ObjName(pNet) ); + if ( i == 3 ) + { + if ( Vec_PtrSize(vNets) > 3 ) + printf( " ..." ); + break; + } + } + printf( "\n" ); + } + Vec_PtrFree( vNets ); +} + + +/**Function************************************************************* + + Synopsis [Converts the network to combinational.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMakeComb( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i; + + if ( Abc_NtkIsComb(pNtk) ) + return; + + assert( !Abc_NtkIsNetlist(pNtk) ); + assert( Abc_NtkHasOnlyLatchBoxes(pNtk) ); + + // detach the latches +// Abc_NtkForEachLatch( pNtk, pObj, i ) + Vec_PtrForEachEntryReverse( pNtk->vBoxes, pObj, i ) + Abc_NtkDeleteObj( pObj ); + assert( Abc_NtkLatchNum(pNtk) == 0 ); + assert( Abc_NtkBoxNum(pNtk) == 0 ); + + // move CIs to become PIs + Vec_PtrClear( pNtk->vPis ); + Abc_NtkForEachCi( pNtk, pObj, i ) + { + if ( Abc_ObjIsBo(pObj) ) + { + pObj->Type = ABC_OBJ_PI; + pNtk->nObjCounts[ABC_OBJ_PI]++; + pNtk->nObjCounts[ABC_OBJ_BO]--; + } + Vec_PtrPush( pNtk->vPis, pObj ); + } + assert( Abc_NtkBoNum(pNtk) == 0 ); + + // move COs to become POs + Vec_PtrClear( pNtk->vPos ); + Abc_NtkForEachCo( pNtk, pObj, i ) + { + if ( Abc_ObjIsBi(pObj) ) + { + pObj->Type = ABC_OBJ_PO; + pNtk->nObjCounts[ABC_OBJ_PO]++; + pNtk->nObjCounts[ABC_OBJ_BI]--; + } + Vec_PtrPush( pNtk->vPos, pObj ); + } + assert( Abc_NtkBiNum(pNtk) == 0 ); + + if ( !Abc_NtkCheck( pNtk ) ) + fprintf( stdout, "Abc_NtkMakeComb(): Network check has failed.\n" ); +} + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abc/abcObj.c b/abc_with_bb_support/src/base/abc/abcObj.c new file mode 100644 index 000000000..fb1262f54 --- /dev/null +++ b/abc_with_bb_support/src/base/abc/abcObj.c @@ -0,0 +1,991 @@ +/**CFile**************************************************************** + + FileName [abcObj.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Object creation/duplication/deletion procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcObj.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "abcInt.h" +#include "main.h" +#include "mio.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Creates a new object.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_ObjAlloc( Abc_Ntk_t * pNtk, Abc_ObjType_t Type ) +{ + Abc_Obj_t * pObj; + if ( pNtk->pMmObj ) + pObj = (Abc_Obj_t *)Extra_MmFixedEntryFetch( pNtk->pMmObj ); + else + pObj = (Abc_Obj_t *)ALLOC( Abc_Obj_t, 1 ); + memset( pObj, 0, sizeof(Abc_Obj_t) ); + pObj->pNtk = pNtk; + pObj->Type = Type; + pObj->Id = -1; + return pObj; +} + +/**Function************************************************************* + + Synopsis [Recycles the object.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ObjRecycle( Abc_Obj_t * pObj ) +{ + Abc_Ntk_t * pNtk = pObj->pNtk; + int LargePiece = (4 << ABC_NUM_STEPS); + // free large fanout arrays + if ( pNtk->pMmStep && pObj->vFanouts.nCap * 4 > LargePiece ) + FREE( pObj->vFanouts.pArray ); + if ( pNtk->pMmStep == NULL ) + { + FREE( pObj->vFanouts.pArray ); + FREE( pObj->vFanins.pArray ); + } + // clean the memory to make deleted object distinct from the live one + memset( pObj, 0, sizeof(Abc_Obj_t) ); + // recycle the object + if ( pNtk->pMmObj ) + Extra_MmFixedEntryRecycle( pNtk->pMmObj, (char *)pObj ); + else + free( pObj ); +} + +/**Function************************************************************* + + Synopsis [Adds the node to the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkCreateObj( Abc_Ntk_t * pNtk, Abc_ObjType_t Type ) +{ + Abc_Obj_t * pObj; + Abc_LatchInfo_t * pLatchInfo; + + // create new object, assign ID, and add to the array + pObj = Abc_ObjAlloc( pNtk, Type ); + pObj->Id = pNtk->vObjs->nSize; + Vec_PtrPush( pNtk->vObjs, pObj ); + pNtk->nObjCounts[Type]++; + pNtk->nObjs++; + // perform specialized operations depending on the object type + switch (Type) + { + case ABC_OBJ_NONE: + assert(0); + break; + case ABC_OBJ_CONST1: + assert(0); + break; + case ABC_OBJ_PIO: + assert(0); + break; + case ABC_OBJ_PI: + Vec_PtrPush( pNtk->vPis, pObj ); + Vec_PtrPush( pNtk->vCis, pObj ); + break; + case ABC_OBJ_PO: + Vec_PtrPush( pNtk->vPos, pObj ); + Vec_PtrPush( pNtk->vCos, pObj ); + break; + case ABC_OBJ_BI: + if ( pNtk->vCos ) Vec_PtrPush( pNtk->vCos, pObj ); + break; + case ABC_OBJ_BO: + if ( pNtk->vCis ) Vec_PtrPush( pNtk->vCis, pObj ); + break; + case ABC_OBJ_ASSERT: + Vec_PtrPush( pNtk->vAsserts, pObj ); + Vec_PtrPush( pNtk->vCos, pObj ); + break; + case ABC_OBJ_NET: + case ABC_OBJ_NODE: + break; + case ABC_OBJ_LATCH: + pLatchInfo = (Abc_LatchInfo_t *)ALLOC( Abc_LatchInfo_t, 1); + pLatchInfo->InitValue = ABC_INIT_NONE; + pObj->pData = (void *)pLatchInfo; + case ABC_OBJ_WHITEBOX: + case ABC_OBJ_BLACKBOX: + if ( pNtk->vBoxes ) Vec_PtrPush( pNtk->vBoxes, pObj ); + break; + default: + assert(0); + break; + } + return pObj; +} + +/**Function************************************************************* + + Synopsis [Deletes the object from the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDeleteObj( Abc_Obj_t * pObj ) +{ + Abc_Ntk_t * pNtk = pObj->pNtk; + Vec_Ptr_t * vNodes; + Abc_LatchInfo_t * pLatchInfo; + int i; + assert( !Abc_ObjIsComplement(pObj) ); + // remove from the table of names + if ( Nm_ManFindNameById(pObj->pNtk->pManName, pObj->Id) ) + Nm_ManDeleteIdName(pObj->pNtk->pManName, pObj->Id); + // delete fanins and fanouts + vNodes = Vec_PtrAlloc( 100 ); + Abc_NodeCollectFanouts( pObj, vNodes ); + for ( i = 0; i < vNodes->nSize; i++ ) + Abc_ObjDeleteFanin( vNodes->pArray[i], pObj ); + Abc_NodeCollectFanins( pObj, vNodes ); + for ( i = 0; i < vNodes->nSize; i++ ) + Abc_ObjDeleteFanin( pObj, vNodes->pArray[i] ); + Vec_PtrFree( vNodes ); + // remove from the list of objects + Vec_PtrWriteEntry( pNtk->vObjs, pObj->Id, NULL ); + pObj->Id = (1<<26)-1; + pNtk->nObjCounts[pObj->Type]--; + pNtk->nObjs--; + // perform specialized operations depending on the object type + switch (pObj->Type) + { + case ABC_OBJ_NONE: + assert(0); + break; + case ABC_OBJ_CONST1: + assert(0); + break; + case ABC_OBJ_PIO: + assert(0); + break; + case ABC_OBJ_PI: + Vec_PtrRemove( pNtk->vPis, pObj ); + Vec_PtrRemove( pNtk->vCis, pObj ); + break; + case ABC_OBJ_PO: + Vec_PtrRemove( pNtk->vPos, pObj ); + Vec_PtrRemove( pNtk->vCos, pObj ); + break; + case ABC_OBJ_BI: + if ( pNtk->vCos ) Vec_PtrRemove( pNtk->vCos, pObj ); + break; + case ABC_OBJ_BO: + if ( pNtk->vCis ) Vec_PtrRemove( pNtk->vCis, pObj ); + break; + case ABC_OBJ_ASSERT: + Vec_PtrRemove( pNtk->vAsserts, pObj ); + Vec_PtrRemove( pNtk->vCos, pObj ); + break; + case ABC_OBJ_NET: + break; + case ABC_OBJ_NODE: + if ( Abc_NtkHasBdd(pNtk) ) + Cudd_RecursiveDeref( pNtk->pManFunc, pObj->pData ); + pObj->pData = NULL; + break; + case ABC_OBJ_LATCH: + pLatchInfo = ((Abc_LatchInfo_t *)pObj->pData); + if (pLatchInfo->pClkName) + FREE(pLatchInfo->pClkName); + case ABC_OBJ_WHITEBOX: + case ABC_OBJ_BLACKBOX: + if ( pNtk->vBoxes ) Vec_PtrRemove( pNtk->vBoxes, pObj ); + break; + default: + assert(0); + break; + } + // recycle the object memory + Abc_ObjRecycle( pObj ); +} + +/**Function************************************************************* + + Synopsis [Deletes the node and MFFC of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDeleteObj_rec( Abc_Obj_t * pObj, int fOnlyNodes ) +{ + Vec_Ptr_t * vNodes; + int i; + assert( !Abc_ObjIsComplement(pObj) ); + assert( !Abc_ObjIsPi(pObj) ); + assert( Abc_ObjFanoutNum(pObj) == 0 ); + // delete fanins and fanouts + vNodes = Vec_PtrAlloc( 100 ); + Abc_NodeCollectFanins( pObj, vNodes ); + Abc_NtkDeleteObj( pObj ); + if ( fOnlyNodes ) + { + Vec_PtrForEachEntry( vNodes, pObj, i ) + if ( Abc_ObjIsNode(pObj) && Abc_ObjFanoutNum(pObj) == 0 ) + Abc_NtkDeleteObj_rec( pObj, fOnlyNodes ); + } + else + { + Vec_PtrForEachEntry( vNodes, pObj, i ) + if ( !Abc_ObjIsPi(pObj) && Abc_ObjFanoutNum(pObj) == 0 ) + Abc_NtkDeleteObj_rec( pObj, fOnlyNodes ); + } + Vec_PtrFree( vNodes ); +} + +/**Function************************************************************* + + Synopsis [Deletes the node and MFFC of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDeleteAll_rec( Abc_Obj_t * pObj ) +{ + Vec_Ptr_t * vNodes; + int i; + assert( !Abc_ObjIsComplement(pObj) ); + assert( Abc_ObjFanoutNum(pObj) == 0 ); + // delete fanins and fanouts + vNodes = Vec_PtrAlloc( 100 ); + Abc_NodeCollectFanins( pObj, vNodes ); + Abc_NtkDeleteObj( pObj ); + Vec_PtrForEachEntry( vNodes, pObj, i ) + if ( !Abc_ObjIsNode(pObj) && Abc_ObjFanoutNum(pObj) == 0 ) + Abc_NtkDeleteAll_rec( pObj ); + Vec_PtrFree( vNodes ); +} + +/**Function************************************************************* + + Synopsis [Duplicate the Obj.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkDupObj( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pObj, int fCopyName ) +{ + Abc_Obj_t * pObjNew; + Abc_LatchInfo_t * pLatchInfo; + + // create the new object + pObjNew = Abc_NtkCreateObj( pNtkNew, pObj->Type ); + // transfer names of the terminal objects + if ( fCopyName ) + { + if ( Abc_ObjIsCi(pObj) ) + { + if ( !Abc_NtkIsNetlist(pNtkNew) ) + Abc_ObjAssignName( pObjNew, Abc_ObjName(Abc_ObjFanout0Ntk(pObj)), NULL ); + } + else if ( Abc_ObjIsCo(pObj) ) + { + if ( !Abc_NtkIsNetlist(pNtkNew) ) + { + if ( Abc_ObjIsPo(pObj) ) + Abc_ObjAssignName( pObjNew, Abc_ObjName(Abc_ObjFanin0Ntk(pObj)), NULL ); + else + { + assert( Abc_ObjIsLatch(Abc_ObjFanout0(pObj)) ); + Abc_ObjAssignName( pObjNew, Abc_ObjName(pObj), NULL ); + } + } + } + else if ( Abc_ObjIsBox(pObj) || Abc_ObjIsNet(pObj) ) + Abc_ObjAssignName( pObjNew, Abc_ObjName(pObj), NULL ); + } + // copy functionality/names + if ( Abc_ObjIsNode(pObj) ) // copy the function if functionality is compatible + { + if ( pNtkNew->ntkFunc == pObj->pNtk->ntkFunc ) + { + if ( Abc_NtkIsStrash(pNtkNew) ) + {} + else if ( Abc_NtkHasSop(pNtkNew) || Abc_NtkHasBlifMv(pNtkNew) ) + pObjNew->pData = Abc_SopRegister( pNtkNew->pManFunc, pObj->pData ); + else if ( Abc_NtkHasBdd(pNtkNew) ) + pObjNew->pData = Cudd_bddTransfer(pObj->pNtk->pManFunc, pNtkNew->pManFunc, pObj->pData), Cudd_Ref(pObjNew->pData); + else if ( Abc_NtkHasAig(pNtkNew) ) + pObjNew->pData = Hop_Transfer(pObj->pNtk->pManFunc, pNtkNew->pManFunc, pObj->pData, Abc_ObjFaninNum(pObj)); + else if ( Abc_NtkHasMapping(pNtkNew) ) + pObjNew->pData = pObj->pData; + else assert( 0 ); + } + } + else if ( Abc_ObjIsNet(pObj) ) // copy the name + { + } + else if ( Abc_ObjIsLatch(pObj) ) { // copy the reset value + pLatchInfo = (Abc_LatchInfo_t *)ALLOC( Abc_LatchInfo_t, 1); + pLatchInfo->InitValue = ((Abc_LatchInfo_t *)pObj->pData)->InitValue; + pLatchInfo->pClkName = strdup(((Abc_LatchInfo_t *)pObj->pData)->pClkName); + pLatchInfo->LatchType = ((Abc_LatchInfo_t *)pObj->pData)->LatchType; + pObjNew->pData = (void *)pLatchInfo; + } + // transfer HAIG +// pObjNew->pEquiv = pObj->pEquiv; + // remember the new node in the old node + pObj->pCopy = pObjNew; + return pObjNew; +} + +/**Function************************************************************* + + Synopsis [Duplicates the latch with its input/output terminals.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkDupBox( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pBox, int fCopyName ) +{ + Abc_Obj_t * pTerm, * pBoxNew; + int i; + assert( Abc_ObjIsBox(pBox) ); + // duplicate the box + pBoxNew = Abc_NtkDupObj( pNtkNew, pBox, fCopyName ); + // duplicate the fanins and connect them + Abc_ObjForEachFanin( pBox, pTerm, i ) + Abc_ObjAddFanin( pBoxNew, Abc_NtkDupObj(pNtkNew, pTerm, fCopyName) ); + // duplicate the fanouts and connect them + Abc_ObjForEachFanout( pBox, pTerm, i ) + Abc_ObjAddFanin( Abc_NtkDupObj(pNtkNew, pTerm, fCopyName), pBoxNew ); + return pBoxNew; +} + +/**Function************************************************************* + + Synopsis [Clones the objects in the same network but does not assign its function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkCloneObj( Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pClone, * pFanin; + int i; + pClone = Abc_NtkCreateObj( pObj->pNtk, pObj->Type ); + Abc_ObjForEachFanin( pObj, pFanin, i ) + Abc_ObjAddFanin( pClone, pFanin ); + return pClone; +} + + +/**Function************************************************************* + + Synopsis [Returns the net with the given name.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkFindNode( Abc_Ntk_t * pNtk, char * pName ) +{ + Abc_Obj_t * pObj; + int Num; + // try to find the terminal + Num = Nm_ManFindIdByName( pNtk->pManName, pName, ABC_OBJ_PO ); + if ( Num >= 0 ) + return Abc_ObjFanin0( Abc_NtkObj( pNtk, Num ) ); + Num = Nm_ManFindIdByName( pNtk->pManName, pName, ABC_OBJ_BI ); + if ( Num >= 0 ) + return Abc_ObjFanin0( Abc_NtkObj( pNtk, Num ) ); + Num = Nm_ManFindIdByName( pNtk->pManName, pName, ABC_OBJ_NODE ); + if ( Num >= 0 ) + return Abc_NtkObj( pNtk, Num ); + // find the internal node + if ( pName[0] != 'n' ) + { + printf( "Name \"%s\" is not found among CO or node names (internal names often look as \"n\").\n", pName ); + return NULL; + } + Num = atoi( pName + 1 ); + if ( Num < 0 || Num >= Abc_NtkObjNumMax(pNtk) ) + { + printf( "The node \"%s\" with ID %d is not in the current network.\n", pName, Num ); + return NULL; + } + pObj = Abc_NtkObj( pNtk, Num ); + if ( pObj == NULL ) + { + printf( "The node \"%s\" with ID %d has been removed from the current network.\n", pName, Num ); + return NULL; + } + if ( !Abc_ObjIsNode(pObj) ) + { + printf( "Object with ID %d is not a node.\n", Num ); + return NULL; + } + return pObj; +} + +/**Function************************************************************* + + Synopsis [Returns the net with the given name.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkFindNet( Abc_Ntk_t * pNtk, char * pName ) +{ + Abc_Obj_t * pNet; + int ObjId; + assert( Abc_NtkIsNetlist(pNtk) ); + ObjId = Nm_ManFindIdByName( pNtk->pManName, pName, ABC_OBJ_NET ); + if ( ObjId == -1 ) + return NULL; + pNet = Abc_NtkObj( pNtk, ObjId ); + return pNet; +} + +/**Function************************************************************* + + Synopsis [Returns CI with the given name.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkFindCi( Abc_Ntk_t * pNtk, char * pName ) +{ + int Num; + assert( !Abc_NtkIsNetlist(pNtk) ); + Num = Nm_ManFindIdByName( pNtk->pManName, pName, ABC_OBJ_PI ); + if ( Num >= 0 ) + return Abc_NtkObj( pNtk, Num ); + Num = Nm_ManFindIdByName( pNtk->pManName, pName, ABC_OBJ_BO ); + if ( Num >= 0 ) + return Abc_NtkObj( pNtk, Num ); + return NULL; +} + +/**Function************************************************************* + + Synopsis [Returns CO with the given name.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkFindCo( Abc_Ntk_t * pNtk, char * pName ) +{ + int Num; + assert( !Abc_NtkIsNetlist(pNtk) ); + Num = Nm_ManFindIdByName( pNtk->pManName, pName, ABC_OBJ_PO ); + if ( Num >= 0 ) + return Abc_NtkObj( pNtk, Num ); + Num = Nm_ManFindIdByName( pNtk->pManName, pName, ABC_OBJ_BI ); + if ( Num >= 0 ) + return Abc_NtkObj( pNtk, Num ); + return NULL; +} + + +/**Function************************************************************* + + Synopsis [Finds or creates the net.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkFindOrCreateNet( Abc_Ntk_t * pNtk, char * pName ) +{ + Abc_Obj_t * pNet; + assert( Abc_NtkIsNetlist(pNtk) ); + if ( pName && (pNet = Abc_NtkFindNet( pNtk, pName )) ) + return pNet; +//printf( "Creating net %s.\n", pName ); + // create a new net + pNet = Abc_NtkCreateNet( pNtk ); + if ( pName ) + Nm_ManStoreIdName( pNtk->pManName, pNet->Id, pNet->Type, pName, NULL ); + return pNet; +} + +/**Function************************************************************* + + Synopsis [Creates constant 0 node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkCreateNodeConst0( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + assert( Abc_NtkIsLogic(pNtk) || Abc_NtkIsNetlist(pNtk) ); + pNode = Abc_NtkCreateNode( pNtk ); + if ( Abc_NtkHasSop(pNtk) || Abc_NtkHasBlifMv(pNtk) ) + pNode->pData = Abc_SopRegister( pNtk->pManFunc, " 0\n" ); + else if ( Abc_NtkHasBdd(pNtk) ) + pNode->pData = Cudd_ReadLogicZero(pNtk->pManFunc), Cudd_Ref( pNode->pData ); + else if ( Abc_NtkHasAig(pNtk) ) + pNode->pData = Hop_ManConst0(pNtk->pManFunc); + else if ( Abc_NtkHasMapping(pNtk) ) + pNode->pData = Mio_LibraryReadConst0(Abc_FrameReadLibGen()); + else if ( !Abc_NtkHasBlackbox(pNtk) ) + assert( 0 ); + return pNode; +} + +/**Function************************************************************* + + Synopsis [Creates constant 1 node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkCreateNodeConst1( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + assert( Abc_NtkIsLogic(pNtk) || Abc_NtkIsNetlist(pNtk) ); + pNode = Abc_NtkCreateNode( pNtk ); + if ( Abc_NtkHasSop(pNtk) || Abc_NtkHasBlifMv(pNtk) ) + pNode->pData = Abc_SopRegister( pNtk->pManFunc, " 1\n" ); + else if ( Abc_NtkHasBdd(pNtk) ) + pNode->pData = Cudd_ReadOne(pNtk->pManFunc), Cudd_Ref( pNode->pData ); + else if ( Abc_NtkHasAig(pNtk) ) + pNode->pData = Hop_ManConst1(pNtk->pManFunc); + else if ( Abc_NtkHasMapping(pNtk) ) + pNode->pData = Mio_LibraryReadConst1(Abc_FrameReadLibGen()); + else if ( !Abc_NtkHasBlackbox(pNtk) ) + assert( 0 ); + return pNode; +} + +/**Function************************************************************* + + Synopsis [Creates inverter.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkCreateNodeInv( Abc_Ntk_t * pNtk, Abc_Obj_t * pFanin ) +{ + Abc_Obj_t * pNode; + assert( Abc_NtkIsLogic(pNtk) || Abc_NtkIsNetlist(pNtk) ); + pNode = Abc_NtkCreateNode( pNtk ); + if ( pFanin ) Abc_ObjAddFanin( pNode, pFanin ); + if ( Abc_NtkHasSop(pNtk) ) + pNode->pData = Abc_SopRegister( pNtk->pManFunc, "0 1\n" ); + else if ( Abc_NtkHasBdd(pNtk) ) + pNode->pData = Cudd_Not(Cudd_bddIthVar(pNtk->pManFunc,0)), Cudd_Ref( pNode->pData ); + else if ( Abc_NtkHasAig(pNtk) ) + pNode->pData = Hop_Not(Hop_IthVar(pNtk->pManFunc,0)); + else if ( Abc_NtkHasMapping(pNtk) ) + pNode->pData = Mio_LibraryReadInv(Abc_FrameReadLibGen()); + else + assert( 0 ); + return pNode; +} + +/**Function************************************************************* + + Synopsis [Creates buffer.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkCreateNodeBuf( Abc_Ntk_t * pNtk, Abc_Obj_t * pFanin ) +{ + Abc_Obj_t * pNode; + assert( Abc_NtkIsLogic(pNtk) || Abc_NtkIsNetlist(pNtk) ); + pNode = Abc_NtkCreateNode( pNtk ); + if ( pFanin ) Abc_ObjAddFanin( pNode, pFanin ); + if ( Abc_NtkHasSop(pNtk) ) + pNode->pData = Abc_SopRegister( pNtk->pManFunc, "1 1\n" ); + else if ( Abc_NtkHasBdd(pNtk) ) + pNode->pData = Cudd_bddIthVar(pNtk->pManFunc,0), Cudd_Ref( pNode->pData ); + else if ( Abc_NtkHasAig(pNtk) ) + pNode->pData = Hop_IthVar(pNtk->pManFunc,0); + else if ( Abc_NtkHasMapping(pNtk) ) + pNode->pData = Mio_LibraryReadBuf(Abc_FrameReadLibGen()); + else + assert( 0 ); + return pNode; +} + +/**Function************************************************************* + + Synopsis [Creates AND.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkCreateNodeAnd( Abc_Ntk_t * pNtk, Vec_Ptr_t * vFanins ) +{ + Abc_Obj_t * pNode; + int i; + assert( Abc_NtkIsLogic(pNtk) || Abc_NtkIsNetlist(pNtk) ); + pNode = Abc_NtkCreateNode( pNtk ); + for ( i = 0; i < vFanins->nSize; i++ ) + Abc_ObjAddFanin( pNode, vFanins->pArray[i] ); + if ( Abc_NtkHasSop(pNtk) ) + pNode->pData = Abc_SopCreateAnd( pNtk->pManFunc, Vec_PtrSize(vFanins), NULL ); + else if ( Abc_NtkHasBdd(pNtk) ) + pNode->pData = Extra_bddCreateAnd( pNtk->pManFunc, Vec_PtrSize(vFanins) ), Cudd_Ref(pNode->pData); + else if ( Abc_NtkHasAig(pNtk) ) + pNode->pData = Hop_CreateAnd( pNtk->pManFunc, Vec_PtrSize(vFanins) ); + else + assert( 0 ); + return pNode; +} + +/**Function************************************************************* + + Synopsis [Creates OR.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkCreateNodeOr( Abc_Ntk_t * pNtk, Vec_Ptr_t * vFanins ) +{ + Abc_Obj_t * pNode; + int i; + assert( Abc_NtkIsLogic(pNtk) || Abc_NtkIsNetlist(pNtk) ); + pNode = Abc_NtkCreateNode( pNtk ); + for ( i = 0; i < vFanins->nSize; i++ ) + Abc_ObjAddFanin( pNode, vFanins->pArray[i] ); + if ( Abc_NtkHasSop(pNtk) ) + pNode->pData = Abc_SopCreateOr( pNtk->pManFunc, Vec_PtrSize(vFanins), NULL ); + else if ( Abc_NtkHasBdd(pNtk) ) + pNode->pData = Extra_bddCreateOr( pNtk->pManFunc, Vec_PtrSize(vFanins) ), Cudd_Ref(pNode->pData); + else if ( Abc_NtkHasAig(pNtk) ) + pNode->pData = Hop_CreateOr( pNtk->pManFunc, Vec_PtrSize(vFanins) ); + else + assert( 0 ); + return pNode; +} + +/**Function************************************************************* + + Synopsis [Creates EXOR.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkCreateNodeExor( Abc_Ntk_t * pNtk, Vec_Ptr_t * vFanins ) +{ + Abc_Obj_t * pNode; + int i; + assert( Abc_NtkIsLogic(pNtk) || Abc_NtkIsNetlist(pNtk) ); + pNode = Abc_NtkCreateNode( pNtk ); + for ( i = 0; i < vFanins->nSize; i++ ) + Abc_ObjAddFanin( pNode, vFanins->pArray[i] ); + if ( Abc_NtkHasSop(pNtk) ) + pNode->pData = Abc_SopCreateXorSpecial( pNtk->pManFunc, Vec_PtrSize(vFanins) ); + else if ( Abc_NtkHasBdd(pNtk) ) + pNode->pData = Extra_bddCreateExor( pNtk->pManFunc, Vec_PtrSize(vFanins) ), Cudd_Ref(pNode->pData); + else if ( Abc_NtkHasAig(pNtk) ) + pNode->pData = Hop_CreateExor( pNtk->pManFunc, Vec_PtrSize(vFanins) ); + else + assert( 0 ); + return pNode; +} + +/**Function************************************************************* + + Synopsis [Creates MUX.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkCreateNodeMux( Abc_Ntk_t * pNtk, Abc_Obj_t * pNodeC, Abc_Obj_t * pNode1, Abc_Obj_t * pNode0 ) +{ + Abc_Obj_t * pNode; + assert( Abc_NtkIsLogic(pNtk) ); + pNode = Abc_NtkCreateNode( pNtk ); + Abc_ObjAddFanin( pNode, pNodeC ); + Abc_ObjAddFanin( pNode, pNode1 ); + Abc_ObjAddFanin( pNode, pNode0 ); + if ( Abc_NtkHasSop(pNtk) ) + pNode->pData = Abc_SopRegister( pNtk->pManFunc, "11- 1\n0-1 1\n" ); + else if ( Abc_NtkHasBdd(pNtk) ) + pNode->pData = Cudd_bddIte(pNtk->pManFunc,Cudd_bddIthVar(pNtk->pManFunc,0),Cudd_bddIthVar(pNtk->pManFunc,1),Cudd_bddIthVar(pNtk->pManFunc,2)), Cudd_Ref( pNode->pData ); + else if ( Abc_NtkHasAig(pNtk) ) + pNode->pData = Hop_Mux(pNtk->pManFunc,Hop_IthVar(pNtk->pManFunc,0),Hop_IthVar(pNtk->pManFunc,1),Hop_IthVar(pNtk->pManFunc,2)); + else + assert( 0 ); + return pNode; +} + + +/**Function************************************************************* + + Synopsis [Returns 1 if the node is a constant 0 node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NodeIsConst( Abc_Obj_t * pNode ) +{ + assert( Abc_NtkIsLogic(pNode->pNtk) || Abc_NtkIsNetlist(pNode->pNtk) ); + return Abc_ObjIsNode(pNode) && Abc_ObjFaninNum(pNode) == 0; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the node is a constant 0 node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NodeIsConst0( Abc_Obj_t * pNode ) +{ + Abc_Ntk_t * pNtk = pNode->pNtk; + assert( Abc_NtkIsLogic(pNtk) || Abc_NtkIsNetlist(pNtk) ); + assert( Abc_ObjIsNode(pNode) ); + if ( !Abc_NodeIsConst(pNode) ) + return 0; + if ( Abc_NtkHasSop(pNtk) ) + return Abc_SopIsConst0(pNode->pData); + if ( Abc_NtkHasBdd(pNtk) ) + return Cudd_IsComplement(pNode->pData); + if ( Abc_NtkHasAig(pNtk) ) + return Hop_IsComplement(pNode->pData); + if ( Abc_NtkHasMapping(pNtk) ) + return pNode->pData == Mio_LibraryReadConst0(Abc_FrameReadLibGen()); + assert( 0 ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the node is a constant 1 node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NodeIsConst1( Abc_Obj_t * pNode ) +{ + Abc_Ntk_t * pNtk = pNode->pNtk; + assert( Abc_NtkIsLogic(pNtk) || Abc_NtkIsNetlist(pNtk) ); + assert( Abc_ObjIsNode(pNode) ); + if ( !Abc_NodeIsConst(pNode) ) + return 0; + if ( Abc_NtkHasSop(pNtk) ) + return Abc_SopIsConst1(pNode->pData); + if ( Abc_NtkHasBdd(pNtk) ) + return !Cudd_IsComplement(pNode->pData); + if ( Abc_NtkHasAig(pNtk) ) + return !Hop_IsComplement(pNode->pData); + if ( Abc_NtkHasMapping(pNtk) ) + return pNode->pData == Mio_LibraryReadConst1(Abc_FrameReadLibGen()); + assert( 0 ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the node is a buffer.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NodeIsBuf( Abc_Obj_t * pNode ) +{ + Abc_Ntk_t * pNtk = pNode->pNtk; + assert( Abc_NtkIsLogic(pNtk) || Abc_NtkIsNetlist(pNtk) ); + assert( Abc_ObjIsNode(pNode) ); + if ( Abc_ObjFaninNum(pNode) != 1 ) + return 0; + if ( Abc_NtkHasSop(pNtk) ) + return Abc_SopIsBuf(pNode->pData); + if ( Abc_NtkHasBdd(pNtk) ) + return !Cudd_IsComplement(pNode->pData); + if ( Abc_NtkHasAig(pNtk) ) + return !Hop_IsComplement(pNode->pData); + if ( Abc_NtkHasMapping(pNtk) ) + return pNode->pData == Mio_LibraryReadBuf(Abc_FrameReadLibGen()); + assert( 0 ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the node is an inverter.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NodeIsInv( Abc_Obj_t * pNode ) +{ + Abc_Ntk_t * pNtk = pNode->pNtk; + assert( Abc_NtkIsLogic(pNtk) || Abc_NtkIsNetlist(pNtk) ); + assert( Abc_ObjIsNode(pNode) ); + if ( Abc_ObjFaninNum(pNode) != 1 ) + return 0; + if ( Abc_NtkHasSop(pNtk) ) + return Abc_SopIsInv(pNode->pData); + if ( Abc_NtkHasBdd(pNtk) ) + return Cudd_IsComplement(pNode->pData); + if ( Abc_NtkHasAig(pNtk) ) + return Hop_IsComplement(pNode->pData); + if ( Abc_NtkHasMapping(pNtk) ) + return pNode->pData == Mio_LibraryReadInv(Abc_FrameReadLibGen()); + assert( 0 ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Complements the local functions of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeComplement( Abc_Obj_t * pNode ) +{ + assert( Abc_NtkIsLogic(pNode->pNtk) || Abc_NtkIsNetlist(pNode->pNtk) ); + assert( Abc_ObjIsNode(pNode) ); + if ( Abc_NtkHasSop(pNode->pNtk) ) + Abc_SopComplement( pNode->pData ); + else if ( Abc_NtkHasBdd(pNode->pNtk) ) + pNode->pData = Cudd_Not( pNode->pData ); + else if ( Abc_NtkHasAig(pNode->pNtk) ) + pNode->pData = Hop_Not( pNode->pData ); + else + assert( 0 ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/base/abc/abcRefs.c b/abc_with_bb_support/src/base/abc/abcRefs.c new file mode 100644 index 000000000..9b81d7b4f --- /dev/null +++ b/abc_with_bb_support/src/base/abc/abcRefs.c @@ -0,0 +1,452 @@ +/**CFile**************************************************************** + + FileName [abcRefs.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Procedures using reference counting of the AIG nodes.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcRefs.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Abc_NodeRefDeref( Abc_Obj_t * pNode, bool fReference, bool fLabel ); +static int Abc_NodeRefDerefStop( Abc_Obj_t * pNode, bool fReference ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Returns the MFFC size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeMffcSize( Abc_Obj_t * pNode ) +{ + int nConeSize1, nConeSize2; + assert( Abc_NtkIsStrash(pNode->pNtk) ); + assert( !Abc_ObjIsComplement( pNode ) ); + assert( Abc_ObjIsNode( pNode ) ); + if ( Abc_ObjFaninNum(pNode) == 0 ) + return 0; + nConeSize1 = Abc_NodeRefDeref( pNode, 0, 0 ); // dereference + nConeSize2 = Abc_NodeRefDeref( pNode, 1, 0 ); // reference + assert( nConeSize1 == nConeSize2 ); + assert( nConeSize1 > 0 ); + return nConeSize1; +} + +/**Function************************************************************* + + Synopsis [Returns the MFFC size while stopping at the complemented edges.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeMffcSizeStop( Abc_Obj_t * pNode ) +{ + int nConeSize1, nConeSize2; + assert( Abc_NtkIsStrash(pNode->pNtk) ); + assert( !Abc_ObjIsComplement( pNode ) ); + assert( Abc_ObjIsNode( pNode ) ); + if ( Abc_ObjFaninNum(pNode) == 0 ) + return 0; + nConeSize1 = Abc_NodeRefDerefStop( pNode, 0 ); // dereference + nConeSize2 = Abc_NodeRefDerefStop( pNode, 1 ); // reference + assert( nConeSize1 == nConeSize2 ); + assert( nConeSize1 > 0 ); + return nConeSize1; +} + +/**Function************************************************************* + + Synopsis [Labels MFFC with the current traversal ID.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeMffcLabelAig( Abc_Obj_t * pNode ) +{ + int nConeSize1, nConeSize2; + assert( Abc_NtkIsStrash(pNode->pNtk) ); + assert( !Abc_ObjIsComplement( pNode ) ); + assert( Abc_ObjIsNode( pNode ) ); + if ( Abc_ObjFaninNum(pNode) == 0 ) + return 0; + nConeSize1 = Abc_NodeRefDeref( pNode, 0, 1 ); // dereference + nConeSize2 = Abc_NodeRefDeref( pNode, 1, 0 ); // reference + assert( nConeSize1 == nConeSize2 ); + assert( nConeSize1 > 0 ); + return nConeSize1; +} + +/**Function************************************************************* + + Synopsis [References/references the node and returns MFFC size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeRefDeref( Abc_Obj_t * pNode, bool fReference, bool fLabel ) +{ + Abc_Obj_t * pNode0, * pNode1; + int Counter; + // label visited nodes + if ( fLabel ) + Abc_NodeSetTravIdCurrent( pNode ); + // skip the CI + if ( Abc_ObjIsCi(pNode) ) + return 0; + // process the internal node + pNode0 = Abc_ObjFanin0(pNode); + pNode1 = Abc_ObjFanin1(pNode); + Counter = 1; + if ( fReference ) + { + if ( pNode0->vFanouts.nSize++ == 0 ) + Counter += Abc_NodeRefDeref( pNode0, fReference, fLabel ); + if ( pNode1->vFanouts.nSize++ == 0 ) + Counter += Abc_NodeRefDeref( pNode1, fReference, fLabel ); + } + else + { + assert( pNode0->vFanouts.nSize > 0 ); + assert( pNode1->vFanouts.nSize > 0 ); + if ( --pNode0->vFanouts.nSize == 0 ) + Counter += Abc_NodeRefDeref( pNode0, fReference, fLabel ); + if ( --pNode1->vFanouts.nSize == 0 ) + Counter += Abc_NodeRefDeref( pNode1, fReference, fLabel ); + } + return Counter; +} + + +/**Function************************************************************* + + Synopsis [References/references the node and returns MFFC size.] + + Description [Stops at the complemented edges.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeRefDerefStop( Abc_Obj_t * pNode, bool fReference ) +{ + Abc_Obj_t * pNode0, * pNode1; + int Counter; + // skip the CI + if ( Abc_ObjIsCi(pNode) ) + return 0; + // process the internal node + pNode0 = Abc_ObjFanin0(pNode); + pNode1 = Abc_ObjFanin1(pNode); + Counter = 1; + if ( fReference ) + { + if ( pNode0->vFanouts.nSize++ == 0 && !Abc_ObjFaninC0(pNode) ) + Counter += Abc_NodeRefDerefStop( pNode0, fReference ); + if ( pNode1->vFanouts.nSize++ == 0 && !Abc_ObjFaninC1(pNode) ) + Counter += Abc_NodeRefDerefStop( pNode1, fReference ); + } + else + { + assert( pNode0->vFanouts.nSize > 0 ); + assert( pNode1->vFanouts.nSize > 0 ); + if ( --pNode0->vFanouts.nSize == 0 && !Abc_ObjFaninC0(pNode) ) + Counter += Abc_NodeRefDerefStop( pNode0, fReference ); + if ( --pNode1->vFanouts.nSize == 0 && !Abc_ObjFaninC1(pNode) ) + Counter += Abc_NodeRefDerefStop( pNode1, fReference ); + } + return Counter; +} + + + + +/**Function************************************************************* + + Synopsis [Dereferences the node's MFFC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeDeref_rec( Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pFanin; + int i, Counter = 1; + if ( Abc_ObjIsCi(pNode) ) + return 0; + Abc_ObjForEachFanin( pNode, pFanin, i ) + { + assert( pFanin->vFanouts.nSize > 0 ); + if ( --pFanin->vFanouts.nSize == 0 ) + Counter += Abc_NodeDeref_rec( pFanin ); + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [References the node's MFFC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeRef_rec( Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pFanin; + int i, Counter = 1; + if ( Abc_ObjIsCi(pNode) ) + return 0; + Abc_ObjForEachFanin( pNode, pFanin, i ) + { + if ( pFanin->vFanouts.nSize++ == 0 ) + Counter += Abc_NodeRef_rec( pFanin ); + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Collects the internal and boundary nodes in the derefed MFFC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeMffsConeSupp_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vCone, Vec_Ptr_t * vSupp, int fTopmost ) +{ + Abc_Obj_t * pFanin; + int i; + // skip visited nodes + if ( Abc_NodeIsTravIdCurrent(pNode) ) + return; + Abc_NodeSetTravIdCurrent(pNode); + // add to the new support nodes + if ( !fTopmost && (Abc_ObjIsCi(pNode) || pNode->vFanouts.nSize > 0) ) + { + if ( vSupp ) Vec_PtrPush( vSupp, pNode ); + return; + } + // recur on the children + Abc_ObjForEachFanin( pNode, pFanin, i ) + Abc_NodeMffsConeSupp_rec( pFanin, vCone, vSupp, 0 ); + // collect the internal node + if ( vCone ) Vec_PtrPush( vCone, pNode ); +// printf( "%d ", pNode->Id ); +} + +/**Function************************************************************* + + Synopsis [Collects the support of the derefed MFFC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeMffsConeSupp( Abc_Obj_t * pNode, Vec_Ptr_t * vCone, Vec_Ptr_t * vSupp ) +{ + assert( Abc_ObjIsNode(pNode) ); + assert( !Abc_ObjIsComplement(pNode) ); + if ( vCone ) Vec_PtrClear( vCone ); + if ( vSupp ) Vec_PtrClear( vSupp ); + Abc_NtkIncrementTravId( pNode->pNtk ); + Abc_NodeMffsConeSupp_rec( pNode, vCone, vSupp, 1 ); +// printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [Collects the support of the derefed MFFC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeMffsConeSuppPrint( Abc_Obj_t * pNode ) +{ + Vec_Ptr_t * vCone, * vSupp; + Abc_Obj_t * pObj; + int i; + vCone = Vec_PtrAlloc( 100 ); + vSupp = Vec_PtrAlloc( 100 ); + Abc_NodeDeref_rec( pNode ); + Abc_NodeMffsConeSupp( pNode, vCone, vSupp ); + Abc_NodeRef_rec( pNode ); + printf( "Node = %6s : Supp = %3d Cone = %3d (", + Abc_ObjName(pNode), Vec_PtrSize(vSupp), Vec_PtrSize(vCone) ); + Vec_PtrForEachEntry( vCone, pObj, i ) + printf( " %s", Abc_ObjName(pObj) ); + printf( " )\n" ); + Vec_PtrFree( vCone ); + Vec_PtrFree( vSupp ); +} + +/**Function************************************************************* + + Synopsis [Collects the internal nodes of the MFFC limited by cut.] + + Description [] + + SideEffects [Increments the trav ID and marks visited nodes.] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeMffsInside( Abc_Obj_t * pNode, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vInside ) +{ + Abc_Obj_t * pObj; + int i, Count1, Count2; + // increment the fanout counters for the leaves + Vec_PtrForEachEntry( vLeaves, pObj, i ) + pObj->vFanouts.nSize++; + // dereference the node + Count1 = Abc_NodeDeref_rec( pNode ); + // collect the nodes inside the MFFC + Abc_NodeMffsConeSupp( pNode, vInside, NULL ); + // reference it back + Count2 = Abc_NodeRef_rec( pNode ); + assert( Count1 == Count2 ); + // remove the extra counters + Vec_PtrForEachEntry( vLeaves, pObj, i ) + pObj->vFanouts.nSize--; + return Count1; +} + +/**Function************************************************************* + + Synopsis [Collects the internal nodes of the MFFC limited by cut.] + + Description [] + + SideEffects [Increments the trav ID and marks visited nodes.] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NodeMffsInsideCollect( Abc_Obj_t * pNode ) +{ + Vec_Ptr_t * vInside; + int Count1, Count2; + // dereference the node + Count1 = Abc_NodeDeref_rec( pNode ); + // collect the nodes inside the MFFC + vInside = Vec_PtrAlloc( 10 ); + Abc_NodeMffsConeSupp( pNode, vInside, NULL ); + // reference it back + Count2 = Abc_NodeRef_rec( pNode ); + assert( Count1 == Count2 ); + return vInside; +} + +/**Function************************************************************* + + Synopsis [Collects the internal and boundary nodes in the derefed MFFC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeMffcLabel_rec( Abc_Obj_t * pNode, int fTopmost ) +{ + Abc_Obj_t * pFanin; + int i; + // add to the new support nodes + if ( !fTopmost && (Abc_ObjIsCi(pNode) || pNode->vFanouts.nSize > 0) ) + return; + // skip visited nodes + if ( Abc_NodeIsTravIdCurrent(pNode) ) + return; + Abc_NodeSetTravIdCurrent(pNode); + // recur on the children + Abc_ObjForEachFanin( pNode, pFanin, i ) + Abc_NodeMffcLabel_rec( pFanin, 0 ); + // collect the internal node +// printf( "%d ", pNode->Id ); +} + +/**Function************************************************************* + + Synopsis [Collects the internal nodes of the MFFC limited by cut.] + + Description [] + + SideEffects [Increments the trav ID and marks visited nodes.] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeMffcLabel( Abc_Obj_t * pNode ) +{ + int Count1, Count2; + // dereference the node + Count1 = Abc_NodeDeref_rec( pNode ); + // collect the nodes inside the MFFC + Abc_NtkIncrementTravId( pNode->pNtk ); + Abc_NodeMffcLabel_rec( pNode, 1 ); + // reference it back + Count2 = Abc_NodeRef_rec( pNode ); + assert( Count1 == Count2 ); + return Count1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abc/abcShow.c b/abc_with_bb_support/src/base/abc/abcShow.c new file mode 100644 index 000000000..14f7648b8 --- /dev/null +++ b/abc_with_bb_support/src/base/abc/abcShow.c @@ -0,0 +1,318 @@ +/**CFile**************************************************************** + + FileName [abcShow.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Visualization procedures using DOT software and GSView.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcShow.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifdef WIN32 +#include +#endif + +#include "abc.h" +#include "main.h" +#include "io.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +extern void Abc_ShowFile( char * FileNameDot ); +static void Abc_ShowGetFileName( char * pName, char * pBuffer ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Visualizes BDD of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeShowBdd( Abc_Obj_t * pNode ) +{ + FILE * pFile; + Vec_Ptr_t * vNamesIn; + char FileNameDot[200]; + char * pNameOut; + + assert( Abc_NtkIsBddLogic(pNode->pNtk) ); + // create the file name + Abc_ShowGetFileName( Abc_ObjName(pNode), FileNameDot ); + // check that the file can be opened + if ( (pFile = fopen( FileNameDot, "w" )) == NULL ) + { + fprintf( stdout, "Cannot open the intermediate file \"%s\".\n", FileNameDot ); + return; + } + + // set the node names + vNamesIn = Abc_NodeGetFaninNames( pNode ); + pNameOut = Abc_ObjName(pNode); + Cudd_DumpDot( pNode->pNtk->pManFunc, 1, (DdNode **)&pNode->pData, (char **)vNamesIn->pArray, &pNameOut, pFile ); + Abc_NodeFreeNames( vNamesIn ); + Abc_NtkCleanCopy( pNode->pNtk ); + fclose( pFile ); + + // visualize the file + Abc_ShowFile( FileNameDot ); +} + +/**Function************************************************************* + + Synopsis [Visualizes a reconvergence driven cut at the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeShowCut( Abc_Obj_t * pNode, int nNodeSizeMax, int nConeSizeMax ) +{ + FILE * pFile; + char FileNameDot[200]; + Abc_ManCut_t * p; + Vec_Ptr_t * vCutSmall; + Vec_Ptr_t * vCutLarge; + Vec_Ptr_t * vInside; + Vec_Ptr_t * vNodesTfo; + Abc_Obj_t * pTemp; + int i; + + assert( Abc_NtkIsStrash(pNode->pNtk) ); + + // start the cut computation manager + p = Abc_NtkManCutStart( nNodeSizeMax, nConeSizeMax, 2, ABC_INFINITY ); + // get the recovergence driven cut + vCutSmall = Abc_NodeFindCut( p, pNode, 1 ); + // get the containing cut + vCutLarge = Abc_NtkManCutReadCutLarge( p ); + // get the array for the inside nodes + vInside = Abc_NtkManCutReadVisited( p ); + // get the inside nodes of the containing cone + Abc_NodeConeCollect( &pNode, 1, vCutLarge, vInside, 1 ); + + // add the nodes in the TFO + vNodesTfo = Abc_NodeCollectTfoCands( p, pNode, vCutSmall, ABC_INFINITY ); + Vec_PtrForEachEntry( vNodesTfo, pTemp, i ) + Vec_PtrPushUnique( vInside, pTemp ); + + // create the file name + Abc_ShowGetFileName( Abc_ObjName(pNode), FileNameDot ); + // check that the file can be opened + if ( (pFile = fopen( FileNameDot, "w" )) == NULL ) + { + fprintf( stdout, "Cannot open the intermediate file \"%s\".\n", FileNameDot ); + return; + } + // add the root node to the cone (for visualization) + Vec_PtrPush( vCutSmall, pNode ); + // write the DOT file + Io_WriteDotNtk( pNode->pNtk, vInside, vCutSmall, FileNameDot, 0, 0 ); + // stop the cut computation manager + Abc_NtkManCutStop( p ); + + // visualize the file + Abc_ShowFile( FileNameDot ); +} + +/**Function************************************************************* + + Synopsis [Visualizes AIG with choices.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkShow( Abc_Ntk_t * pNtk, int fGateNames, int fSeq, int fUseReverse ) +{ + FILE * pFile; + Abc_Obj_t * pNode; + Vec_Ptr_t * vNodes; + char FileNameDot[200]; + int i; + + assert( Abc_NtkIsStrash(pNtk) || Abc_NtkIsLogic(pNtk) ); + if ( Abc_NtkIsStrash(pNtk) && Abc_NtkGetChoiceNum(pNtk) ) + { + printf( "Temporarily visualization of AIGs with choice nodes is disabled.\n" ); + return; + } + // convert to logic SOP + if ( Abc_NtkIsLogic(pNtk) ) + Abc_NtkToSop( pNtk, 0 ); + // create the file name + Abc_ShowGetFileName( pNtk->pName, FileNameDot ); + // check that the file can be opened + if ( (pFile = fopen( FileNameDot, "w" )) == NULL ) + { + fprintf( stdout, "Cannot open the intermediate file \"%s\".\n", FileNameDot ); + return; + } + fclose( pFile ); + + // collect all nodes in the network + vNodes = Vec_PtrAlloc( 100 ); + Abc_NtkForEachObj( pNtk, pNode, i ) + Vec_PtrPush( vNodes, pNode ); + // write the DOT file + if ( fSeq ) + Io_WriteDotSeq( pNtk, vNodes, NULL, FileNameDot, fGateNames, fUseReverse ); + else + Io_WriteDotNtk( pNtk, vNodes, NULL, FileNameDot, fGateNames, fUseReverse ); + Vec_PtrFree( vNodes ); + + // visualize the file + Abc_ShowFile( FileNameDot ); +} + + +/**Function************************************************************* + + Synopsis [Shows the given DOT file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ShowFile( char * FileNameDot ) +{ + FILE * pFile; + char * FileGeneric; + char FileNamePs[200]; + char CommandDot[1000]; + char * pDotName; + char * pDotNameWin = "dot.exe"; + char * pDotNameUnix = "dot"; + char * pGsNameWin = "gsview32.exe"; + char * pGsNameUnix = "gv"; + int RetValue; + + // get DOT names from the resource file + if ( Abc_FrameReadFlag("dotwin") ) + pDotNameWin = Abc_FrameReadFlag("dotwin"); + if ( Abc_FrameReadFlag("dotunix") ) + pDotNameUnix = Abc_FrameReadFlag("dotunix"); + +#ifdef WIN32 + pDotName = pDotNameWin; +#else + pDotName = pDotNameUnix; +#endif + + // check if the input DOT file is okay + if ( (pFile = fopen( FileNameDot, "r" )) == NULL ) + { + fprintf( stdout, "Cannot open the intermediate file \"%s\".\n", FileNameDot ); + return; + } + fclose( pFile ); + + // create the PostScript file name + FileGeneric = Extra_FileNameGeneric( FileNameDot ); + sprintf( FileNamePs, "%s.ps", FileGeneric ); + free( FileGeneric ); + + // generate the PostScript file using DOT + sprintf( CommandDot, "%s -Tps -o %s %s", pDotName, FileNamePs, FileNameDot ); + RetValue = system( CommandDot ); + if ( RetValue == -1 ) + { + fprintf( stdout, "Command \"%s\" did not succeed.\n", CommandDot ); + return; + } + // check that the input PostScript file is okay + if ( (pFile = fopen( FileNamePs, "r" )) == NULL ) + { + fprintf( stdout, "Cannot open intermediate file \"%s\".\n", FileNamePs ); + return; + } + fclose( pFile ); + + + // get GSVIEW names from the resource file + if ( Abc_FrameReadFlag("gsviewwin") ) + pGsNameWin = Abc_FrameReadFlag("gsviewwin"); + if ( Abc_FrameReadFlag("gsviewunix") ) + pGsNameUnix = Abc_FrameReadFlag("gsviewunix"); + + // spawn the viewer +#ifdef WIN32 + _unlink( FileNameDot ); + if ( _spawnl( _P_NOWAIT, pGsNameWin, pGsNameWin, FileNamePs, NULL ) == -1 ) + if ( _spawnl( _P_NOWAIT, "C:\\Program Files\\Ghostgum\\gsview\\gsview32.exe", + "C:\\Program Files\\Ghostgum\\gsview\\gsview32.exe", FileNamePs, NULL ) == -1 ) + { + fprintf( stdout, "Cannot find \"%s\".\n", pGsNameWin ); + return; + } +#else + { + char CommandPs[1000]; + unlink( FileNameDot ); + sprintf( CommandPs, "%s %s &", pGsNameUnix, FileNamePs ); + if ( system( CommandPs ) == -1 ) + { + fprintf( stdout, "Cannot execute \"%s\".\n", CommandPs ); + return; + } + } +#endif +} + +/**Function************************************************************* + + Synopsis [Derives the DOT file name.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ShowGetFileName( char * pName, char * pBuffer ) +{ + char * pCur; + // creat the file name + sprintf( pBuffer, "%s.dot", pName ); + // get rid of not-alpha-numeric characters + for ( pCur = pBuffer; *pCur; pCur++ ) + if ( !((*pCur >= '0' && *pCur <= '9') || (*pCur >= 'a' && *pCur <= 'z') || + (*pCur >= 'A' && *pCur <= 'Z') || (*pCur == '.')) ) + *pCur = '_'; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abc/abcSop.c b/abc_with_bb_support/src/base/abc/abcSop.c new file mode 100644 index 000000000..824947c95 --- /dev/null +++ b/abc_with_bb_support/src/base/abc/abcSop.c @@ -0,0 +1,1075 @@ +/**CFile**************************************************************** + + FileName [abcSop.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Implementation of a simple SOP representation of nodes.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcSop.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +/* + The SOPs in this package are represented using char * strings. + For example, the SOP of the node: + + .names c d0 d1 MUX + 01- 1 + 1-1 1 + + is the string: "01- 1\n1-1 1\n" where '\n' is a single char. +*/ + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Registers the cube string with the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopRegister( Extra_MmFlex_t * pMan, char * pName ) +{ + char * pRegName; + if ( pName == NULL ) return NULL; + pRegName = Extra_MmFlexEntryFetch( pMan, strlen(pName) + 1 ); + strcpy( pRegName, pName ); + return pRegName; +} + +/**Function************************************************************* + + Synopsis [Creates the constant 1 cover with the given number of variables and cubes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopStart( Extra_MmFlex_t * pMan, int nCubes, int nVars ) +{ + char * pSopCover, * pCube; + int i, Length; + + Length = nCubes * (nVars + 3); + pSopCover = Extra_MmFlexEntryFetch( pMan, Length + 1 ); + memset( pSopCover, '-', Length ); + pSopCover[Length] = 0; + + for ( i = 0; i < nCubes; i++ ) + { + pCube = pSopCover + i * (nVars + 3); + pCube[nVars + 0] = ' '; + pCube[nVars + 1] = '1'; + pCube[nVars + 2] = '\n'; + } + return pSopCover; +} + +/**Function************************************************************* + + Synopsis [Creates the constant 1 cover with 0 variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopCreateConst1( Extra_MmFlex_t * pMan ) +{ + return Abc_SopRegister( pMan, " 1\n" ); +} + +/**Function************************************************************* + + Synopsis [Creates the constant 1 cover with 0 variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopCreateConst0( Extra_MmFlex_t * pMan ) +{ + return Abc_SopRegister( pMan, " 0\n" ); +} + +/**Function************************************************************* + + Synopsis [Creates the AND2 cover.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopCreateAnd2( Extra_MmFlex_t * pMan, int fCompl0, int fCompl1 ) +{ + char Buffer[6]; + Buffer[0] = '1' - fCompl0; + Buffer[1] = '1' - fCompl1; + Buffer[2] = ' '; + Buffer[3] = '1'; + Buffer[4] = '\n'; + Buffer[5] = 0; + return Abc_SopRegister( pMan, Buffer ); +} + +/**Function************************************************************* + + Synopsis [Creates the multi-input AND cover.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopCreateAnd( Extra_MmFlex_t * pMan, int nVars, int * pfCompl ) +{ + char * pSop; + int i; + pSop = Abc_SopStart( pMan, 1, nVars ); + for ( i = 0; i < nVars; i++ ) + pSop[i] = '1' - (pfCompl? pfCompl[i] : 0); + pSop[nVars + 1] = '1'; + return pSop; +} + +/**Function************************************************************* + + Synopsis [Creates the multi-input NAND cover.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopCreateNand( Extra_MmFlex_t * pMan, int nVars ) +{ + char * pSop; + int i; + pSop = Abc_SopStart( pMan, 1, nVars ); + for ( i = 0; i < nVars; i++ ) + pSop[i] = '1'; + pSop[nVars + 1] = '0'; + return pSop; +} + +/**Function************************************************************* + + Synopsis [Creates the multi-input OR cover.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopCreateOr( Extra_MmFlex_t * pMan, int nVars, int * pfCompl ) +{ + char * pSop; + int i; + pSop = Abc_SopStart( pMan, 1, nVars ); + for ( i = 0; i < nVars; i++ ) + pSop[i] = '0' + (pfCompl? pfCompl[i] : 0); + pSop[nVars + 1] = '0'; + return pSop; +} + +/**Function************************************************************* + + Synopsis [Creates the multi-input OR cover.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopCreateOrMultiCube( Extra_MmFlex_t * pMan, int nVars, int * pfCompl ) +{ + char * pSop, * pCube; + int i; + pSop = Abc_SopStart( pMan, nVars, nVars ); + i = 0; + Abc_SopForEachCube( pSop, nVars, pCube ) + { + pCube[i] = '1' - (pfCompl? pfCompl[i] : 0); + i++; + } + return pSop; +} + +/**Function************************************************************* + + Synopsis [Creates the multi-input NOR cover.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopCreateNor( Extra_MmFlex_t * pMan, int nVars ) +{ + char * pSop; + int i; + pSop = Abc_SopStart( pMan, 1, nVars ); + for ( i = 0; i < nVars; i++ ) + pSop[i] = '0'; + return pSop; +} + +/**Function************************************************************* + + Synopsis [Creates the multi-input XOR cover.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopCreateXor( Extra_MmFlex_t * pMan, int nVars ) +{ + assert( nVars == 2 ); + return Abc_SopRegister(pMan, "01 1\n10 1\n"); +} + +/**Function************************************************************* + + Synopsis [Creates the multi-input XOR cover (special case).] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopCreateXorSpecial( Extra_MmFlex_t * pMan, int nVars ) +{ + char * pSop; + pSop = Abc_SopCreateAnd( pMan, nVars, NULL ); + pSop[nVars+1] = 'x'; + assert( pSop[nVars+2] == '\n' ); + return pSop; +} + +/**Function************************************************************* + + Synopsis [Creates the multi-input XNOR cover.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopCreateNxor( Extra_MmFlex_t * pMan, int nVars ) +{ + assert( nVars == 2 ); + return Abc_SopRegister(pMan, "11 1\n00 1\n"); +} + +/**Function************************************************************* + + Synopsis [Creates the MUX cover.] + + Description [The first input of MUX is the control. The second input + is DATA1. The third input is DATA0.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopCreateMux( Extra_MmFlex_t * pMan ) +{ + return Abc_SopRegister(pMan, "11- 1\n0-1 1\n"); +} + +/**Function************************************************************* + + Synopsis [Creates the inv cover.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopCreateInv( Extra_MmFlex_t * pMan ) +{ + return Abc_SopRegister(pMan, "0 1\n"); +} + +/**Function************************************************************* + + Synopsis [Creates the buf cover.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopCreateBuf( Extra_MmFlex_t * pMan ) +{ + return Abc_SopRegister(pMan, "1 1\n"); +} + +/**Function************************************************************* + + Synopsis [Creates the arbitrary cover from the truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopCreateFromTruth( Extra_MmFlex_t * pMan, int nVars, unsigned * pTruth ) +{ + char * pSop, * pCube; + int nMints, Counter, i, k; + // count the number of true minterms + Counter = 0; + nMints = (1 << nVars); + for ( i = 0; i < nMints; i++ ) + Counter += ((pTruth[i>>5] & (1 << (i&31))) > 0); + // SOP is not well-defined if the truth table is constant 0 + assert( Counter > 0 ); + if ( Counter == 0 ) + return NULL; + // start the cover + pSop = Abc_SopStart( pMan, Counter, nVars ); + // create true minterms + Counter = 0; + for ( i = 0; i < nMints; i++ ) + if ( (pTruth[i>>5] & (1 << (i&31))) > 0 ) + { + pCube = pSop + Counter * (nVars + 3); + for ( k = 0; k < nVars; k++ ) + pCube[k] = '0' + ((i & (1 << k)) > 0); + Counter++; + } + return pSop; +} + +/**Function************************************************************* + + Synopsis [Creates the cover from the ISOP computed from TT.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopCreateFromIsop( Extra_MmFlex_t * pMan, int nVars, Vec_Int_t * vCover ) +{ + char * pSop, * pCube; + int i, k, Entry, Literal; + assert( Vec_IntSize(vCover) > 0 ); + if ( Vec_IntSize(vCover) == 0 ) + return NULL; + // start the cover + pSop = Abc_SopStart( pMan, Vec_IntSize(vCover), nVars ); + // create cubes + Vec_IntForEachEntry( vCover, Entry, i ) + { + pCube = pSop + i * (nVars + 3); + for ( k = 0; k < nVars; k++ ) + { + Literal = 3 & (Entry >> (k << 1)); + if ( Literal == 1 ) + pCube[k] = '0'; + else if ( Literal == 2 ) + pCube[k] = '1'; + else if ( Literal != 0 ) + assert( 0 ); + } + } + return pSop; +} + +/**Function************************************************************* + + Synopsis [Reads the number of cubes in the cover.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_SopGetCubeNum( char * pSop ) +{ + char * pCur; + int nCubes = 0; + if ( pSop == NULL ) + return 0; + for ( pCur = pSop; *pCur; pCur++ ) + nCubes += (*pCur == '\n'); + return nCubes; +} + +/**Function************************************************************* + + Synopsis [Reads the number of SOP literals in the cover.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_SopGetLitNum( char * pSop ) +{ + char * pCur; + int nLits = 0; + if ( pSop == NULL ) + return 0; + for ( pCur = pSop; *pCur; pCur++ ) + { + nLits -= (*pCur == '\n'); + nLits += (*pCur == '0' || *pCur == '1'); + } + return nLits; +} + +/**Function************************************************************* + + Synopsis [Reads the number of variables in the cover.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_SopGetVarNum( char * pSop ) +{ + char * pCur; + for ( pCur = pSop; *pCur != '\n'; pCur++ ); + return pCur - pSop - 2; +} + +/**Function************************************************************* + + Synopsis [Reads the phase of the cover.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_SopGetPhase( char * pSop ) +{ + int nVars = Abc_SopGetVarNum( pSop ); + if ( pSop[nVars+1] == '0' || pSop[nVars+1] == 'n' ) + return 0; + if ( pSop[nVars+1] == '1' || pSop[nVars+1] == 'x' ) + return 1; + assert( 0 ); + return -1; +} + +/**Function************************************************************* + + Synopsis [Returns the i-th literal of the cover.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_SopGetIthCareLit( char * pSop, int i ) +{ + char * pCube; + int nVars; + nVars = Abc_SopGetVarNum( pSop ); + Abc_SopForEachCube( pSop, nVars, pCube ) + if ( pCube[i] != '-' ) + return pCube[i] - '0'; + return -1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_SopComplement( char * pSop ) +{ + char * pCur; + for ( pCur = pSop; *pCur; pCur++ ) + if ( *pCur == '\n' ) + { + if ( *(pCur - 1) == '0' ) + *(pCur - 1) = '1'; + else if ( *(pCur - 1) == '1' ) + *(pCur - 1) = '0'; + else if ( *(pCur - 1) == 'x' ) + *(pCur - 1) = 'n'; + else if ( *(pCur - 1) == 'n' ) + *(pCur - 1) = 'x'; + else + assert( 0 ); + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_SopIsComplement( char * pSop ) +{ + char * pCur; + for ( pCur = pSop; *pCur; pCur++ ) + if ( *pCur == '\n' ) + return (int)(*(pCur - 1) == '0' || *(pCur - 1) == 'n'); + assert( 0 ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Checks if the cover is constant 0.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_SopIsConst0( char * pSop ) +{ + return pSop[0] == ' ' && pSop[1] == '0'; +} + +/**Function************************************************************* + + Synopsis [Checks if the cover is constant 1.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_SopIsConst1( char * pSop ) +{ + return pSop[0] == ' ' && pSop[1] == '1'; +} + +/**Function************************************************************* + + Synopsis [Checks if the cover is constant 1.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_SopIsBuf( char * pSop ) +{ + if ( pSop[4] != 0 ) + return 0; + if ( (pSop[0] == '1' && pSop[2] == '1') || (pSop[0] == '0' && pSop[2] == '0') ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Checks if the cover is constant 1.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_SopIsInv( char * pSop ) +{ + if ( pSop[4] != 0 ) + return 0; + if ( (pSop[0] == '0' && pSop[2] == '1') || (pSop[0] == '1' && pSop[2] == '0') ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Checks if the cover is AND with possibly complemented inputs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_SopIsAndType( char * pSop ) +{ + char * pCur; + if ( Abc_SopGetCubeNum(pSop) != 1 ) + return 0; + for ( pCur = pSop; *pCur != ' '; pCur++ ) + if ( *pCur == '-' ) + return 0; + if ( pCur[1] != '1' ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Checks if the cover is OR with possibly complemented inputs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_SopIsOrType( char * pSop ) +{ + char * pCube, * pCur; + int nVars, nLits; + nVars = Abc_SopGetVarNum( pSop ); + if ( nVars != Abc_SopGetCubeNum(pSop) ) + return 0; + Abc_SopForEachCube( pSop, nVars, pCube ) + { + // count the number of literals in the cube + nLits = 0; + for ( pCur = pCube; *pCur != ' '; pCur++ ) + nLits += ( *pCur != '-' ); + if ( nLits != 1 ) + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_SopIsExorType( char * pSop ) +{ + char * pCur; + for ( pCur = pSop; *pCur; pCur++ ) + if ( *pCur == '\n' ) + return (int)(*(pCur - 1) == 'x' || *(pCur - 1) == 'n'); + assert( 0 ); + return 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_SopCheck( char * pSop, int nFanins ) +{ + char * pCubes, * pCubesOld; + int fFound0 = 0, fFound1 = 0; + + // check the logic function of the node + for ( pCubes = pSop; *pCubes; pCubes++ ) + { + // get the end of the next cube + for ( pCubesOld = pCubes; *pCubes != ' '; pCubes++ ); + // compare the distance + if ( pCubes - pCubesOld != nFanins ) + { + fprintf( stdout, "Abc_SopCheck: SOP has a mismatch between its cover size (%d) and its fanin number (%d).\n", + pCubes - pCubesOld, nFanins ); + return 0; + } + // check the output values for this cube + pCubes++; + if ( *pCubes == '0' ) + fFound0 = 1; + else if ( *pCubes == '1' ) + fFound1 = 1; + else if ( *pCubes != 'x' && *pCubes != 'n' ) + { + fprintf( stdout, "Abc_SopCheck: SOP has a strange character (%c) in the output part of its cube.\n", *pCubes ); + return 0; + } + // check the last symbol (new line) + pCubes++; + if ( *pCubes != '\n' ) + { + fprintf( stdout, "Abc_SopCheck: SOP has a cube without new line in the end.\n" ); + return 0; + } + } + if ( fFound0 && fFound1 ) + { + fprintf( stdout, "Abc_SopCheck: SOP has cubes in both phases.\n" ); + return 0; + } + return 1; +} + + +/**Function************************************************************* + + Synopsis [Derives SOP from the truth table representation.] + + Description [Truth table is expected to be in the hexadecimal notation.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopFromTruthBin( char * pTruth ) +{ + char * pSopCover, * pCube; + int nTruthSize, nVars, Digit, Length, Mint, i, b; + Vec_Int_t * vMints; + + // get the number of variables + nTruthSize = strlen(pTruth); + nVars = Extra_Base2Log( nTruthSize ); + if ( nTruthSize != (1 << (nVars)) ) + { + printf( "String %s does not look like a truth table of a %d-variable function.\n", pTruth, nVars ); + return NULL; + } + + // collect the on-set minterms + vMints = Vec_IntAlloc( 100 ); + for ( i = 0; i < nTruthSize; i++ ) + { + if ( pTruth[i] >= '0' && pTruth[i] <= '1' ) + Digit = pTruth[i] - '0'; + else + { + printf( "String %s does not look like a binary representation of the truth table.\n", pTruth ); + return NULL; + } + if ( Digit == 1 ) + Vec_IntPush( vMints, nTruthSize - 1 - i ); + } + if ( Vec_IntSize( vMints ) == 0 || Vec_IntSize( vMints ) == nTruthSize ) + { + Vec_IntFree( vMints ); + printf( "Cannot create constant function.\n" ); + return NULL; + } + + // create the SOP representation of the minterms + Length = Vec_IntSize(vMints) * (nVars + 3); + pSopCover = ALLOC( char, Length + 1 ); + pSopCover[Length] = 0; + Vec_IntForEachEntry( vMints, Mint, i ) + { + pCube = pSopCover + i * (nVars + 3); + for ( b = 0; b < nVars; b++ ) + if ( Mint & (1 << (nVars-1-b)) ) +// if ( Mint & (1 << b) ) + pCube[b] = '1'; + else + pCube[b] = '0'; + pCube[nVars + 0] = ' '; + pCube[nVars + 1] = '1'; + pCube[nVars + 2] = '\n'; + } + Vec_IntFree( vMints ); + return pSopCover; +} + +/**Function************************************************************* + + Synopsis [Derives SOP from the truth table representation.] + + Description [Truth table is expected to be in the hexadecimal notation.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopFromTruthHex( char * pTruth ) +{ + char * pSopCover, * pCube; + int nTruthSize, nVars, Digit, Length, Mint, i, b; + Vec_Int_t * vMints; + + // get the number of variables + nTruthSize = strlen(pTruth); + nVars = Extra_Base2Log( nTruthSize ) + 2; + if ( nTruthSize != (1 << (nVars-2)) ) + { + printf( "String %s does not look like a truth table of a %d-variable function.\n", pTruth, nVars ); + return NULL; + } + + // collect the on-set minterms + vMints = Vec_IntAlloc( 100 ); + for ( i = 0; i < nTruthSize; i++ ) + { + if ( pTruth[i] >= '0' && pTruth[i] <= '9' ) + Digit = pTruth[i] - '0'; + else if ( pTruth[i] >= 'a' && pTruth[i] <= 'f' ) + Digit = 10 + pTruth[i] - 'a'; + else if ( pTruth[i] >= 'A' && pTruth[i] <= 'F' ) + Digit = 10 + pTruth[i] - 'A'; + else + { + printf( "String %s does not look like a hexadecimal representation of the truth table.\n", pTruth ); + return NULL; + } + for ( b = 0; b < 4; b++ ) + if ( Digit & (1 << b) ) + Vec_IntPush( vMints, 4*(nTruthSize-1-i)+b ); + } + + // create the SOP representation of the minterms + Length = Vec_IntSize(vMints) * (nVars + 3); + pSopCover = ALLOC( char, Length + 1 ); + pSopCover[Length] = 0; + Vec_IntForEachEntry( vMints, Mint, i ) + { + pCube = pSopCover + i * (nVars + 3); + for ( b = 0; b < nVars; b++ ) +// if ( Mint & (1 << (nVars-1-b)) ) + if ( Mint & (1 << b) ) + pCube[b] = '1'; + else + pCube[b] = '0'; + pCube[nVars + 0] = ' '; + pCube[nVars + 1] = '1'; + pCube[nVars + 2] = '\n'; + } + Vec_IntFree( vMints ); + return pSopCover; +} + +/**Function************************************************************* + + Synopsis [Creates one encoder node.] + + Description [Produces MV-SOP for BLIF-MV representation.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopEncoderPos( Extra_MmFlex_t * pMan, int iValue, int nValues ) +{ + char Buffer[32]; + assert( iValue < nValues ); + sprintf( Buffer, "d0\n%d 1\n", iValue ); + return Abc_SopRegister( pMan, Buffer ); +} + +/**Function************************************************************* + + Synopsis [Creates one encoder node.] + + Description [Produces MV-SOP for BLIF-MV representation.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopEncoderLog( Extra_MmFlex_t * pMan, int iBit, int nValues ) +{ + char * pResult; + Vec_Str_t * vSop; + int v, Counter, fFirst = 1, nBits = Extra_Base2Log(nValues); + assert( iBit < nBits ); + // count the number of literals + Counter = 0; + for ( v = 0; v < nValues; v++ ) + Counter += ( (v & (1 << iBit)) > 0 ); + // create the cover + vSop = Vec_StrAlloc( 100 ); + Vec_StrPrintStr( vSop, "d0\n" ); + if ( Counter > 1 ) + Vec_StrPrintStr( vSop, "(" ); + for ( v = 0; v < nValues; v++ ) + if ( v & (1 << iBit) ) + { + if ( fFirst ) + fFirst = 0; + else + Vec_StrPush( vSop, ',' ); + Vec_StrPrintNum( vSop, v ); + } + if ( Counter > 1 ) + Vec_StrPrintStr( vSop, ")" ); + Vec_StrPrintStr( vSop, " 1\n" ); + Vec_StrPush( vSop, 0 ); + pResult = Abc_SopRegister( pMan, Vec_StrArray(vSop) ); + Vec_StrFree( vSop ); + return pResult; +} + +/**Function************************************************************* + + Synopsis [Creates the decoder node.] + + Description [Produces MV-SOP for BLIF-MV representation.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopDecoderPos( Extra_MmFlex_t * pMan, int nValues ) +{ + char * pResult; + Vec_Str_t * vSop; + int i, k; + assert( nValues > 1 ); + vSop = Vec_StrAlloc( 100 ); + for ( i = 0; i < nValues; i++ ) + { + for ( k = 0; k < nValues; k++ ) + { + if ( k == i ) + Vec_StrPrintStr( vSop, "1 " ); + else + Vec_StrPrintStr( vSop, "- " ); + } + Vec_StrPrintNum( vSop, i ); + Vec_StrPush( vSop, '\n' ); + } + Vec_StrPush( vSop, 0 ); + pResult = Abc_SopRegister( pMan, Vec_StrArray(vSop) ); + Vec_StrFree( vSop ); + return pResult; +} + +/**Function************************************************************* + + Synopsis [Creates the decover node.] + + Description [Produces MV-SOP for BLIF-MV representation.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopDecoderLog( Extra_MmFlex_t * pMan, int nValues ) +{ + char * pResult; + Vec_Str_t * vSop; + int i, b, nBits = Extra_Base2Log(nValues); + assert( nValues > 1 && nValues <= (1< 0) ); + Vec_StrPush( vSop, ' ' ); + } + Vec_StrPrintNum( vSop, i ); + Vec_StrPush( vSop, '\n' ); + } + Vec_StrPush( vSop, 0 ); + pResult = Abc_SopRegister( pMan, Vec_StrArray(vSop) ); + Vec_StrFree( vSop ); + return pResult; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abc/abcUtil.c b/abc_with_bb_support/src/base/abc/abcUtil.c new file mode 100644 index 000000000..27acb384c --- /dev/null +++ b/abc_with_bb_support/src/base/abc/abcUtil.c @@ -0,0 +1,1737 @@ +/**CFile**************************************************************** + + FileName [abcUtil.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Various utilities.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcUtil.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "main.h" +#include "mio.h" +#include "dec.h" +//#include "seq.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Frees one attribute manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void * Abc_NtkAttrFree( Abc_Ntk_t * pNtk, int Attr, int fFreeMan ) +{ + void * pUserMan; + Vec_Att_t * pAttrMan; + pAttrMan = Vec_PtrEntry( pNtk->vAttrs, Attr ); + Vec_PtrWriteEntry( pNtk->vAttrs, Attr, NULL ); + pUserMan = Vec_AttFree( pAttrMan, fFreeMan ); + return pUserMan; +} + +/**Function************************************************************* + + Synopsis [Increments the current traversal ID of the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkIncrementTravId( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i; + if ( pNtk->nTravIds >= (1<<30)-1 ) + { + pNtk->nTravIds = 0; + Abc_NtkForEachObj( pNtk, pObj, i ) + pObj->TravId = 0; + } + pNtk->nTravIds++; +} + +/**Function************************************************************* + + Synopsis [Order CI/COs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkOrderCisCos( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj, * pTerm; + int i, k; + Vec_PtrClear( pNtk->vCis ); + Vec_PtrClear( pNtk->vCos ); + Abc_NtkForEachPi( pNtk, pObj, i ) + Vec_PtrPush( pNtk->vCis, pObj ); + Abc_NtkForEachPo( pNtk, pObj, i ) + Vec_PtrPush( pNtk->vCos, pObj ); + Abc_NtkForEachAssert( pNtk, pObj, i ) + Vec_PtrPush( pNtk->vCos, pObj ); + Abc_NtkForEachBox( pNtk, pObj, i ) + { + if ( Abc_ObjIsLatch(pObj) ) + continue; + Abc_ObjForEachFanin( pObj, pTerm, k ) + Vec_PtrPush( pNtk->vCos, pTerm ); + Abc_ObjForEachFanout( pObj, pTerm, k ) + Vec_PtrPush( pNtk->vCis, pTerm ); + } + Abc_NtkForEachBox( pNtk, pObj, i ) + { + if ( !Abc_ObjIsLatch(pObj) ) + continue; + Abc_ObjForEachFanin( pObj, pTerm, k ) + Vec_PtrPush( pNtk->vCos, pTerm ); + Abc_ObjForEachFanout( pObj, pTerm, k ) + Vec_PtrPush( pNtk->vCis, pTerm ); + } +} + +/**Function************************************************************* + + Synopsis [Reads the number of cubes of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkGetCubeNum( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i, nCubes = 0; + assert( Abc_NtkHasSop(pNtk) ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + if ( Abc_NodeIsConst(pNode) ) + continue; + assert( pNode->pData ); + nCubes += Abc_SopGetCubeNum( pNode->pData ); + } + return nCubes; +} + +/**Function************************************************************* + + Synopsis [Reads the number of literals in the SOPs of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkGetLitNum( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i, nLits = 0; + assert( Abc_NtkHasSop(pNtk) ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + assert( pNode->pData ); + nLits += Abc_SopGetLitNum( pNode->pData ); + } + return nLits; +} + +/**Function************************************************************* + + Synopsis [Counts the number of literals in the factored forms.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkGetLitFactNum( Abc_Ntk_t * pNtk ) +{ + Dec_Graph_t * pFactor; + Abc_Obj_t * pNode; + int nNodes, i; + assert( Abc_NtkHasSop(pNtk) ); + nNodes = 0; + Abc_NtkForEachNode( pNtk, pNode, i ) + { + if ( Abc_NodeIsConst(pNode) ) + continue; + pFactor = Dec_Factor( pNode->pData ); + nNodes += 1 + Dec_GraphNodeNum(pFactor); + Dec_GraphFree( pFactor ); + } + return nNodes; +} + +/**Function************************************************************* + + Synopsis [Reads the number of BDD nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkGetBddNodeNum( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i, nNodes = 0; + assert( Abc_NtkIsBddLogic(pNtk) ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + assert( pNode->pData ); + if ( Abc_ObjFaninNum(pNode) < 2 ) + continue; + nNodes += pNode->pData? -1 + Cudd_DagSize( pNode->pData ) : 0; + } + return nNodes; +} + +/**Function************************************************************* + + Synopsis [Reads the number of BDD nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkGetAigNodeNum( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i, nNodes = 0; + assert( Abc_NtkIsAigLogic(pNtk) ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + assert( pNode->pData ); + if ( Abc_ObjFaninNum(pNode) < 2 ) + continue; +//printf( "%d ", Hop_DagSize( pNode->pData ) ); + nNodes += pNode->pData? Hop_DagSize( pNode->pData ) : 0; + } + return nNodes; +} + +/**Function************************************************************* + + Synopsis [Reads the number of BDD nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkGetClauseNum( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + DdNode * bCover, * zCover, * bFunc; + DdManager * dd = pNtk->pManFunc; + int i, nClauses = 0; + assert( Abc_NtkIsBddLogic(pNtk) ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + assert( pNode->pData ); + bFunc = pNode->pData; + + bCover = Cudd_zddIsop( dd, bFunc, bFunc, &zCover ); + Cudd_Ref( bCover ); + Cudd_Ref( zCover ); + nClauses += Abc_CountZddCubes( dd, zCover ); + Cudd_RecursiveDeref( dd, bCover ); + Cudd_RecursiveDerefZdd( dd, zCover ); + + bCover = Cudd_zddIsop( dd, Cudd_Not(bFunc), Cudd_Not(bFunc), &zCover ); + Cudd_Ref( bCover ); + Cudd_Ref( zCover ); + nClauses += Abc_CountZddCubes( dd, zCover ); + Cudd_RecursiveDeref( dd, bCover ); + Cudd_RecursiveDerefZdd( dd, zCover ); + } + return nClauses; +} + +/**Function************************************************************* + + Synopsis [Computes the area of the mapped circuit.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +double Abc_NtkGetMappedArea( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + double TotalArea; + int i; + assert( Abc_NtkHasMapping(pNtk) ); + TotalArea = 0.0; + Abc_NtkForEachNode( pNtk, pNode, i ) + { +// assert( pNode->pData ); + if ( pNode->pData == NULL ) + { + printf( "Node without mapping is encountered.\n" ); + continue; + } + TotalArea += Mio_GateReadArea( pNode->pData ); + } + return TotalArea; +} + +/**Function************************************************************* + + Synopsis [Counts the number of exors.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkGetExorNum( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i, Counter = 0; + Abc_NtkForEachNode( pNtk, pNode, i ) + Counter += pNode->fExor; + return Counter; +} + +/**Function************************************************************* + + Synopsis [Counts the number of exors.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkGetMuxNum( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i, Counter = 0; + Abc_NtkForEachNode( pNtk, pNode, i ) + Counter += Abc_NodeIsMuxType(pNode); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if it is an AIG with choice nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkGetChoiceNum( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i, Counter; + if ( !Abc_NtkIsStrash(pNtk) ) + return 0; + Counter = 0; + Abc_NtkForEachNode( pNtk, pNode, i ) + Counter += Abc_AigNodeIsChoice( pNode ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Reads the maximum number of fanins.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkGetFaninMax( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i, nFaninsMax = 0; + Abc_NtkForEachNode( pNtk, pNode, i ) + { + if ( nFaninsMax < Abc_ObjFaninNum(pNode) ) + nFaninsMax = Abc_ObjFaninNum(pNode); + } + return nFaninsMax; +} + +/**Function************************************************************* + + Synopsis [Reads the total number of all fanins.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkGetTotalFanins( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i, nFanins = 0; + Abc_NtkForEachNode( pNtk, pNode, i ) + nFanins += Abc_ObjFaninNum(pNode); + return nFanins; +} + +/**Function************************************************************* + + Synopsis [Cleans the copy field of all objects.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkCleanCopy( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i; + Abc_NtkForEachObj( pNtk, pObj, i ) + pObj->pCopy = NULL; +} + +/**Function************************************************************* + + Synopsis [Cleans the copy field of all objects.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkCleanData( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i; + Abc_NtkForEachObj( pNtk, pObj, i ) + pObj->pData = NULL; +} + +/**Function************************************************************* + + Synopsis [Cleans the copy field of all objects.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkCleanEquiv( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i; + Abc_NtkForEachObj( pNtk, pObj, i ) + pObj->pEquiv = NULL; +} + +/**Function************************************************************* + + Synopsis [Counts the number of nodes having non-trivial copies.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkCountCopy( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i, Counter = 0; + Abc_NtkForEachObj( pNtk, pObj, i ) + { + if ( Abc_ObjIsNode(pObj) ) + Counter += (pObj->pCopy != NULL); + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Saves copy field of the objects.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkSaveCopy( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vCopies; + Abc_Obj_t * pObj; + int i; + vCopies = Vec_PtrStart( Abc_NtkObjNumMax(pNtk) ); + Abc_NtkForEachObj( pNtk, pObj, i ) + Vec_PtrWriteEntry( vCopies, i, pObj->pCopy ); + return vCopies; +} + +/**Function************************************************************* + + Synopsis [Loads copy field of the objects.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkLoadCopy( Abc_Ntk_t * pNtk, Vec_Ptr_t * vCopies ) +{ + Abc_Obj_t * pObj; + int i; + Abc_NtkForEachObj( pNtk, pObj, i ) + pObj->pCopy = Vec_PtrEntry( vCopies, i ); +} + +/**Function************************************************************* + + Synopsis [Cleans the copy field of all objects.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkCleanNext( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i = 0; + Abc_NtkForEachObj( pNtk, pObj, i ) + pObj->pNext = NULL; +} + +/**Function************************************************************* + + Synopsis [Cleans the copy field of all objects.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkCleanMarkA( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i = 0; + Abc_NtkForEachObj( pNtk, pObj, i ) + pObj->fMarkA = 0; +} + +/**Function************************************************************* + + Synopsis [Checks if the internal node has CO fanout.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NodeFindCoFanout( Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pFanout; + int i; + Abc_ObjForEachFanout( pNode, pFanout, i ) + if ( Abc_ObjIsCo(pFanout) ) + return pFanout; + return NULL; +} + +/**Function************************************************************* + + Synopsis [Checks if the internal node has CO fanout.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NodeFindNonCoFanout( Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pFanout; + int i; + Abc_ObjForEachFanout( pNode, pFanout, i ) + if ( !Abc_ObjIsCo(pFanout) ) + return pFanout; + return NULL; +} + +/**Function************************************************************* + + Synopsis [Checks if the internal node has CO drivers with the same name.] + + Description [Checks if the internal node can borrow its name from CO fanouts. + This is possible if all COs with non-complemented fanin edge pointing to this + node have the same name.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NodeHasUniqueCoFanout( Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pFanout, * pFanoutCo; + int i; + pFanoutCo = NULL; + Abc_ObjForEachFanout( pNode, pFanout, i ) + { + if ( !Abc_ObjIsCo(pFanout) ) + continue; + if ( Abc_ObjFaninC0(pFanout) ) + continue; + if ( pFanoutCo == NULL ) + { + assert( Abc_ObjFaninNum(pFanout) == 1 ); + assert( Abc_ObjFanin0(pFanout) == pNode ); + pFanoutCo = pFanout; + continue; + } + if ( strcmp( Abc_ObjName(pFanoutCo), Abc_ObjName(pFanout) ) ) // they have diff names + return NULL; + } + return pFanoutCo; +} + +/**Function************************************************************* + + Synopsis [Fixes the CO driver problem.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkFixCoDriverProblem( Abc_Obj_t * pDriver, Abc_Obj_t * pNodeCo, int fDuplicate ) +{ + Abc_Ntk_t * pNtk = pDriver->pNtk; + Abc_Obj_t * pDriverNew, * pFanin; + int k; + if ( fDuplicate && !Abc_ObjIsCi(pDriver) ) + { + pDriverNew = Abc_NtkDupObj( pNtk, pDriver, 0 ); + Abc_ObjForEachFanin( pDriver, pFanin, k ) + Abc_ObjAddFanin( pDriverNew, pFanin ); + if ( Abc_ObjFaninC0(pNodeCo) ) + { + // change polarity of the duplicated driver + Abc_NodeComplement( pDriverNew ); + Abc_ObjXorFaninC( pNodeCo, 0 ); + } + } + else + { + // add inverters and buffers when necessary + if ( Abc_ObjFaninC0(pNodeCo) ) + { + pDriverNew = Abc_NtkCreateNodeInv( pNtk, pDriver ); + Abc_ObjXorFaninC( pNodeCo, 0 ); + } + else + pDriverNew = Abc_NtkCreateNodeBuf( pNtk, pDriver ); + } + // update the fanin of the PO node + Abc_ObjPatchFanin( pNodeCo, pDriver, pDriverNew ); + assert( Abc_ObjFanoutNum(pDriverNew) == 1 ); + // remove the old driver if it dangles + // (this happens when the duplicated driver had only one complemented fanout) + if ( Abc_ObjFanoutNum(pDriver) == 0 ) + Abc_NtkDeleteObj( pDriver ); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if COs of a logic network are simple.] + + Description [The COs of a logic network are simple under three conditions: + (1) The edge from CO to its driver is not complemented. + (2) If CI is a driver of a CO, they have the same name.] + (3) If two COs share the same driver, they have the same name.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkLogicHasSimpleCos( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode, * pDriver; + int i; + assert( Abc_NtkIsLogic(pNtk) ); + Abc_NtkIncrementTravId( pNtk ); + Abc_NtkForEachCo( pNtk, pNode, i ) + { + // if the driver is complemented, this is an error + pDriver = Abc_ObjFanin0(pNode); + if ( Abc_ObjFaninC0(pNode) ) + return 0; + // if the driver is a CI and has different name, this is an error + if ( Abc_ObjIsCi(pDriver) && strcmp(Abc_ObjName(pDriver), Abc_ObjName(pNode)) ) + return 0; + // if the driver is visited for the first time, remember the CO name + if ( !Abc_NodeIsTravIdCurrent(pDriver) ) + { + pDriver->pNext = (Abc_Obj_t *)Abc_ObjName(pNode); + Abc_NodeSetTravIdCurrent(pDriver); + continue; + } + // the driver has second CO - if they have different name, this is an error + if ( strcmp((char *)pDriver->pNext, Abc_ObjName(pNode)) ) // diff names + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Transforms the network to have simple COs.] + + Description [The COs of a logic network are simple under three conditions: + (1) The edge from CO to its driver is not complemented. + (2) If CI is a driver of a CO, they have the same name.] + (3) If two COs share the same driver, they have the same name. + In some cases, such as FPGA mapping, we prevent the increase in delay + by duplicating the driver nodes, rather than adding invs/bufs.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkLogicMakeSimpleCos( Abc_Ntk_t * pNtk, bool fDuplicate ) +{ + Abc_Obj_t * pNode, * pDriver; + int i, nDupGates = 0; + assert( Abc_NtkIsLogic(pNtk) ); + Abc_NtkIncrementTravId( pNtk ); + Abc_NtkForEachCo( pNtk, pNode, i ) + { + // if the driver is complemented, this is an error + pDriver = Abc_ObjFanin0(pNode); + if ( Abc_ObjFaninC0(pNode) ) + { + Abc_NtkFixCoDriverProblem( pDriver, pNode, fDuplicate ); + nDupGates++; + continue; + } + // if the driver is a CI and has different name, this is an error + if ( Abc_ObjIsCi(pDriver) && strcmp(Abc_ObjName(pDriver), Abc_ObjName(pNode)) ) + { + Abc_NtkFixCoDriverProblem( pDriver, pNode, fDuplicate ); + nDupGates++; + continue; + } + // if the driver is visited for the first time, remember the CO name + if ( !Abc_NodeIsTravIdCurrent(pDriver) ) + { + pDriver->pNext = (Abc_Obj_t *)Abc_ObjName(pNode); + Abc_NodeSetTravIdCurrent(pDriver); + continue; + } + // the driver has second CO - if they have different name, this is an error + if ( strcmp((char *)pDriver->pNext, Abc_ObjName(pNode)) ) // diff names + { + Abc_NtkFixCoDriverProblem( pDriver, pNode, fDuplicate ); + nDupGates++; + continue; + } + } + assert( Abc_NtkLogicHasSimpleCos(pNtk) ); + return nDupGates; +} + +/**Function************************************************************* + + Synopsis [Inserts a new node in the order by levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_VecObjPushUniqueOrderByLevel( Vec_Ptr_t * p, Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pNode1, * pNode2; + int i; + if ( Vec_PtrPushUnique(p, pNode) ) + return; + // find the p of the node + for ( i = p->nSize-1; i > 0; i-- ) + { + pNode1 = p->pArray[i ]; + pNode2 = p->pArray[i-1]; + if ( Abc_ObjRegular(pNode1)->Level <= Abc_ObjRegular(pNode2)->Level ) + break; + p->pArray[i ] = pNode2; + p->pArray[i-1] = pNode1; + } +} + + + +/**Function************************************************************* + + Synopsis [Returns 1 if the node is the root of EXOR/NEXOR.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NodeIsExorType( Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pNode0, * pNode1; + // check that the node is regular + assert( !Abc_ObjIsComplement(pNode) ); + // if the node is not AND, this is not EXOR + if ( !Abc_AigNodeIsAnd(pNode) ) + return 0; + // if the children are not complemented, this is not EXOR + if ( !Abc_ObjFaninC0(pNode) || !Abc_ObjFaninC1(pNode) ) + return 0; + // get children + pNode0 = Abc_ObjFanin0(pNode); + pNode1 = Abc_ObjFanin1(pNode); + // if the children are not ANDs, this is not EXOR + if ( Abc_ObjFaninNum(pNode0) != 2 || Abc_ObjFaninNum(pNode1) != 2 ) + return 0; + // otherwise, the node is EXOR iff its grand-children are the same + return (Abc_ObjFaninId0(pNode0) == Abc_ObjFaninId0(pNode1) || Abc_ObjFaninId0(pNode0) == Abc_ObjFaninId1(pNode1)) && + (Abc_ObjFaninId1(pNode0) == Abc_ObjFaninId0(pNode1) || Abc_ObjFaninId1(pNode0) == Abc_ObjFaninId1(pNode1)); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the node is the root of MUX or EXOR/NEXOR.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NodeIsMuxType( Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pNode0, * pNode1; + // check that the node is regular + assert( !Abc_ObjIsComplement(pNode) ); + // if the node is not AND, this is not MUX + if ( !Abc_AigNodeIsAnd(pNode) ) + return 0; + // if the children are not complemented, this is not MUX + if ( !Abc_ObjFaninC0(pNode) || !Abc_ObjFaninC1(pNode) ) + return 0; + // get children + pNode0 = Abc_ObjFanin0(pNode); + pNode1 = Abc_ObjFanin1(pNode); + // if the children are not ANDs, this is not MUX + if ( Abc_ObjFaninNum(pNode0) != 2 || Abc_ObjFaninNum(pNode1) != 2 ) + return 0; + // otherwise the node is MUX iff it has a pair of equal grandchildren + return (Abc_ObjFaninId0(pNode0) == Abc_ObjFaninId0(pNode1) && (Abc_ObjFaninC0(pNode0) ^ Abc_ObjFaninC0(pNode1))) || + (Abc_ObjFaninId0(pNode0) == Abc_ObjFaninId1(pNode1) && (Abc_ObjFaninC0(pNode0) ^ Abc_ObjFaninC1(pNode1))) || + (Abc_ObjFaninId1(pNode0) == Abc_ObjFaninId0(pNode1) && (Abc_ObjFaninC1(pNode0) ^ Abc_ObjFaninC0(pNode1))) || + (Abc_ObjFaninId1(pNode0) == Abc_ObjFaninId1(pNode1) && (Abc_ObjFaninC1(pNode0) ^ Abc_ObjFaninC1(pNode1))); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the node is the control type of the MUX.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NodeIsMuxControlType( Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pNode0, * pNode1; + // check that the node is regular + assert( !Abc_ObjIsComplement(pNode) ); + // skip the node that do not have two fanouts + if ( Abc_ObjFanoutNum(pNode) != 2 ) + return 0; + // get the fanouts + pNode0 = Abc_ObjFanout( pNode, 0 ); + pNode1 = Abc_ObjFanout( pNode, 1 ); + // if they have more than one fanout, we are not interested + if ( Abc_ObjFanoutNum(pNode0) != 1 || Abc_ObjFanoutNum(pNode1) != 1 ) + return 0; + // if the fanouts have the same fanout, this is MUX or EXOR (or a redundant gate (CA)(CB)) + return Abc_ObjFanout0(pNode0) == Abc_ObjFanout0(pNode1); +} + +/**Function************************************************************* + + Synopsis [Recognizes what nodes are control and data inputs of a MUX.] + + Description [If the node is a MUX, returns the control variable C. + Assigns nodes T and E to be the then and else variables of the MUX. + Node C is never complemented. Nodes T and E can be complemented. + This function also recognizes EXOR/NEXOR gates as MUXes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NodeRecognizeMux( Abc_Obj_t * pNode, Abc_Obj_t ** ppNodeT, Abc_Obj_t ** ppNodeE ) +{ + Abc_Obj_t * pNode0, * pNode1; + assert( !Abc_ObjIsComplement(pNode) ); + assert( Abc_NodeIsMuxType(pNode) ); + // get children + pNode0 = Abc_ObjFanin0(pNode); + pNode1 = Abc_ObjFanin1(pNode); + // find the control variable +// if ( pNode1->p1 == Fraig_Not(pNode2->p1) ) + if ( Abc_ObjFaninId0(pNode0) == Abc_ObjFaninId0(pNode1) && (Abc_ObjFaninC0(pNode0) ^ Abc_ObjFaninC0(pNode1)) ) + { +// if ( Fraig_IsComplement(pNode1->p1) ) + if ( Abc_ObjFaninC0(pNode0) ) + { // pNode2->p1 is positive phase of C + *ppNodeT = Abc_ObjNot(Abc_ObjChild1(pNode1));//pNode2->p2); + *ppNodeE = Abc_ObjNot(Abc_ObjChild1(pNode0));//pNode1->p2); + return Abc_ObjChild0(pNode1);//pNode2->p1; + } + else + { // pNode1->p1 is positive phase of C + *ppNodeT = Abc_ObjNot(Abc_ObjChild1(pNode0));//pNode1->p2); + *ppNodeE = Abc_ObjNot(Abc_ObjChild1(pNode1));//pNode2->p2); + return Abc_ObjChild0(pNode0);//pNode1->p1; + } + } +// else if ( pNode1->p1 == Fraig_Not(pNode2->p2) ) + else if ( Abc_ObjFaninId0(pNode0) == Abc_ObjFaninId1(pNode1) && (Abc_ObjFaninC0(pNode0) ^ Abc_ObjFaninC1(pNode1)) ) + { +// if ( Fraig_IsComplement(pNode1->p1) ) + if ( Abc_ObjFaninC0(pNode0) ) + { // pNode2->p2 is positive phase of C + *ppNodeT = Abc_ObjNot(Abc_ObjChild0(pNode1));//pNode2->p1); + *ppNodeE = Abc_ObjNot(Abc_ObjChild1(pNode0));//pNode1->p2); + return Abc_ObjChild1(pNode1);//pNode2->p2; + } + else + { // pNode1->p1 is positive phase of C + *ppNodeT = Abc_ObjNot(Abc_ObjChild1(pNode0));//pNode1->p2); + *ppNodeE = Abc_ObjNot(Abc_ObjChild0(pNode1));//pNode2->p1); + return Abc_ObjChild0(pNode0);//pNode1->p1; + } + } +// else if ( pNode1->p2 == Fraig_Not(pNode2->p1) ) + else if ( Abc_ObjFaninId1(pNode0) == Abc_ObjFaninId0(pNode1) && (Abc_ObjFaninC1(pNode0) ^ Abc_ObjFaninC0(pNode1)) ) + { +// if ( Fraig_IsComplement(pNode1->p2) ) + if ( Abc_ObjFaninC1(pNode0) ) + { // pNode2->p1 is positive phase of C + *ppNodeT = Abc_ObjNot(Abc_ObjChild1(pNode1));//pNode2->p2); + *ppNodeE = Abc_ObjNot(Abc_ObjChild0(pNode0));//pNode1->p1); + return Abc_ObjChild0(pNode1);//pNode2->p1; + } + else + { // pNode1->p2 is positive phase of C + *ppNodeT = Abc_ObjNot(Abc_ObjChild0(pNode0));//pNode1->p1); + *ppNodeE = Abc_ObjNot(Abc_ObjChild1(pNode1));//pNode2->p2); + return Abc_ObjChild1(pNode0);//pNode1->p2; + } + } +// else if ( pNode1->p2 == Fraig_Not(pNode2->p2) ) + else if ( Abc_ObjFaninId1(pNode0) == Abc_ObjFaninId1(pNode1) && (Abc_ObjFaninC1(pNode0) ^ Abc_ObjFaninC1(pNode1)) ) + { +// if ( Fraig_IsComplement(pNode1->p2) ) + if ( Abc_ObjFaninC1(pNode0) ) + { // pNode2->p2 is positive phase of C + *ppNodeT = Abc_ObjNot(Abc_ObjChild0(pNode1));//pNode2->p1); + *ppNodeE = Abc_ObjNot(Abc_ObjChild0(pNode0));//pNode1->p1); + return Abc_ObjChild1(pNode1);//pNode2->p2; + } + else + { // pNode1->p2 is positive phase of C + *ppNodeT = Abc_ObjNot(Abc_ObjChild0(pNode0));//pNode1->p1); + *ppNodeE = Abc_ObjNot(Abc_ObjChild0(pNode1));//pNode2->p1); + return Abc_ObjChild1(pNode0);//pNode1->p2; + } + } + assert( 0 ); // this is not MUX + return NULL; +} + +/**Function************************************************************* + + Synopsis [Prepares two network for a two-argument command similar to "verify".] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkPrepareTwoNtks( FILE * pErr, Abc_Ntk_t * pNtk, char ** argv, int argc, + Abc_Ntk_t ** ppNtk1, Abc_Ntk_t ** ppNtk2, int * pfDelete1, int * pfDelete2 ) +{ + int fCheck = 1; + FILE * pFile; + Abc_Ntk_t * pNtk1, * pNtk2; + int util_optind = 0; + + *pfDelete1 = 0; + *pfDelete2 = 0; + if ( argc == util_optind ) + { // use the spec + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty current network.\n" ); + return 0; + } + if ( pNtk->pSpec == NULL ) + { + fprintf( pErr, "The external spec is not given.\n" ); + return 0; + } + pFile = fopen( pNtk->pSpec, "r" ); + if ( pFile == NULL ) + { + fprintf( pErr, "Cannot open the external spec file \"%s\".\n", pNtk->pSpec ); + return 0; + } + else + fclose( pFile ); + pNtk1 = pNtk; + pNtk2 = Io_Read( pNtk->pSpec, Io_ReadFileType(pNtk->pSpec), fCheck ); + if ( pNtk2 == NULL ) + return 0; + *pfDelete2 = 1; + } + else if ( argc == util_optind + 1 ) + { + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty current network.\n" ); + return 0; + } + pNtk1 = pNtk; + pNtk2 = Io_Read( argv[util_optind], Io_ReadFileType(argv[util_optind]), fCheck ); + if ( pNtk2 == NULL ) + return 0; + *pfDelete2 = 1; + } + else if ( argc == util_optind + 2 ) + { + pNtk1 = Io_Read( argv[util_optind], Io_ReadFileType(argv[util_optind]), fCheck ); + if ( pNtk1 == NULL ) + return 0; + pNtk2 = Io_Read( argv[util_optind+1], Io_ReadFileType(argv[util_optind+1]), fCheck ); + if ( pNtk2 == NULL ) + { + Abc_NtkDelete( pNtk1 ); + return 0; + } + *pfDelete1 = 1; + *pfDelete2 = 1; + } + else + { + fprintf( pErr, "Wrong number of arguments.\n" ); + return 0; + } + *ppNtk1 = pNtk1; + *ppNtk2 = pNtk2; + return 1; +} + + +/**Function************************************************************* + + Synopsis [Returns 1 if it is an AIG with choice nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeCollectFanins( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes ) +{ + Abc_Obj_t * pFanin; + int i; + Vec_PtrClear(vNodes); + Abc_ObjForEachFanin( pNode, pFanin, i ) + Vec_PtrPush( vNodes, pFanin ); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if it is an AIG with choice nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeCollectFanouts( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes ) +{ + Abc_Obj_t * pFanout; + int i; + Vec_PtrClear(vNodes); + Abc_ObjForEachFanout( pNode, pFanout, i ) + Vec_PtrPush( vNodes, pFanout ); +} + +/**Function************************************************************* + + Synopsis [Collects all latches in the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkCollectLatches( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vLatches; + Abc_Obj_t * pObj; + int i; + vLatches = Vec_PtrAlloc( 10 ); + Abc_NtkForEachObj( pNtk, pObj, i ) + Vec_PtrPush( vLatches, pObj ); + return vLatches; +} + +/**Function************************************************************* + + Synopsis [Procedure used for sorting the nodes in increasing order of levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeCompareLevelsIncrease( Abc_Obj_t ** pp1, Abc_Obj_t ** pp2 ) +{ + int Diff = Abc_ObjRegular(*pp1)->Level - Abc_ObjRegular(*pp2)->Level; + if ( Diff < 0 ) + return -1; + if ( Diff > 0 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Procedure used for sorting the nodes in decreasing order of levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeCompareLevelsDecrease( Abc_Obj_t ** pp1, Abc_Obj_t ** pp2 ) +{ + int Diff = Abc_ObjRegular(*pp1)->Level - Abc_ObjRegular(*pp2)->Level; + if ( Diff > 0 ) + return -1; + if ( Diff < 0 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Creates the array of fanout counters.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Abc_NtkFanoutCounts( Abc_Ntk_t * pNtk ) +{ + Vec_Int_t * vFanNums; + Abc_Obj_t * pObj; + int i; + vFanNums = Vec_IntAlloc( 0 ); + Vec_IntFill( vFanNums, Abc_NtkObjNumMax(pNtk), -1 ); + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( Abc_ObjIsCi(pObj) || Abc_ObjIsNode(pObj) ) + Vec_IntWriteEntry( vFanNums, i, Abc_ObjFanoutNum(pObj) ); + return vFanNums; +} + +/**Function************************************************************* + + Synopsis [Collects all objects into one array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkCollectObjects( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pNode; + int i; + vNodes = Vec_PtrAlloc( 100 ); + Abc_NtkForEachObj( pNtk, pNode, i ) + Vec_PtrPush( vNodes, pNode ); + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Returns the array of CI IDs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Abc_NtkGetCiIds( Abc_Ntk_t * pNtk ) +{ + Vec_Int_t * vCiIds; + Abc_Obj_t * pObj; + int i; + vCiIds = Vec_IntAlloc( Abc_NtkCiNum(pNtk) ); + Abc_NtkForEachCi( pNtk, pObj, i ) + Vec_IntPush( vCiIds, pObj->Id ); + return vCiIds; +} + +/**Function************************************************************* + + Synopsis [Puts the nodes into the DFS order and reassign their IDs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkReassignIds( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vNodes; + Vec_Ptr_t * vObjsNew; + Abc_Obj_t * pNode, * pTemp, * pConst1; + int i, k; + assert( Abc_NtkIsStrash(pNtk) ); +//printf( "Total = %d. Current = %d.\n", Abc_NtkObjNumMax(pNtk), Abc_NtkObjNum(pNtk) ); + // start the array of objects with new IDs + vObjsNew = Vec_PtrAlloc( pNtk->nObjs ); + // put constant node first + pConst1 = Abc_AigConst1(pNtk); + assert( pConst1->Id == 0 ); + Vec_PtrPush( vObjsNew, pConst1 ); + // put PI nodes next + Abc_NtkForEachPi( pNtk, pNode, i ) + { + pNode->Id = Vec_PtrSize( vObjsNew ); + Vec_PtrPush( vObjsNew, pNode ); + } + // put PO nodes next + Abc_NtkForEachPo( pNtk, pNode, i ) + { + pNode->Id = Vec_PtrSize( vObjsNew ); + Vec_PtrPush( vObjsNew, pNode ); + } + // put assert nodes next + Abc_NtkForEachAssert( pNtk, pNode, i ) + { + pNode->Id = Vec_PtrSize( vObjsNew ); + Vec_PtrPush( vObjsNew, pNode ); + } + // put latches and their inputs/outputs next + Abc_NtkForEachBox( pNtk, pNode, i ) + { + pNode->Id = Vec_PtrSize( vObjsNew ); + Vec_PtrPush( vObjsNew, pNode ); + Abc_ObjForEachFanin( pNode, pTemp, k ) + { + pTemp->Id = Vec_PtrSize( vObjsNew ); + Vec_PtrPush( vObjsNew, pTemp ); + } + Abc_ObjForEachFanout( pNode, pTemp, k ) + { + pTemp->Id = Vec_PtrSize( vObjsNew ); + Vec_PtrPush( vObjsNew, pTemp ); + } + } + // finally, internal nodes in the DFS order + vNodes = Abc_AigDfs( pNtk, 1, 0 ); + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + if ( pNode == pConst1 ) + continue; + pNode->Id = Vec_PtrSize( vObjsNew ); + Vec_PtrPush( vObjsNew, pNode ); + } + Vec_PtrFree( vNodes ); + assert( Vec_PtrSize(vObjsNew) == pNtk->nObjs ); + + // update the fanin/fanout arrays + Abc_NtkForEachObj( pNtk, pNode, i ) + { + Abc_ObjForEachFanin( pNode, pTemp, k ) + pNode->vFanins.pArray[k] = pTemp->Id; + Abc_ObjForEachFanout( pNode, pTemp, k ) + pNode->vFanouts.pArray[k] = pTemp->Id; + } + + // replace the array of objs + Vec_PtrFree( pNtk->vObjs ); + pNtk->vObjs = vObjsNew; + + // rehash the AIG + Abc_AigRehash( pNtk->pManFunc ); + + // update the name manager!!! +} + +/**Function************************************************************* + + Synopsis [Detect cases when non-trivial FF matching is possible.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDetectMatching( Abc_Ntk_t * pNtk ) +{ +/* + Abc_Obj_t * pLatch, * pFanin; + int i, nTFFs, nJKFFs; + nTFFs = nJKFFs = 0; + Abc_NtkForEachLatch( pNtk, pLatch, i ) + { + pFanin = Abc_ObjFanin0(pLatch); + if ( Abc_ObjFaninNum(pFanin) != 2 ) + continue; + if ( Abc_NodeIsExorType(pLatch) ) + { + if ( Abc_ObjFanin0(Abc_ObjFanin0(pFanin)) == pLatch || + Abc_ObjFanin1(Abc_ObjFanin0(pFanin)) == pLatch ) + nTFFs++; + } + if ( Abc_ObjFaninNum( Abc_ObjFanin0(pFanin) ) != 2 || + Abc_ObjFaninNum( Abc_ObjFanin1(pFanin) ) != 2 ) + continue; + + if ( (Abc_ObjFanin0(Abc_ObjFanin0(pFanin)) == pLatch || + Abc_ObjFanin1(Abc_ObjFanin0(pFanin)) == pLatch) && + (Abc_ObjFanin0(Abc_ObjFanin1(pFanin)) == pLatch || + Abc_ObjFanin1(Abc_ObjFanin1(pFanin)) == pLatch) ) + { + nJKFFs++; + } + } + printf( "D = %6d. T = %6d. JK = %6d. (%6.2f %%)\n", + Abc_NtkLatchNum(pNtk), nTFFs, nJKFFs, 100.0 * nJKFFs / Abc_NtkLatchNum(pNtk) ); +*/ +} + + +/**Function************************************************************* + + Synopsis [Compares the pointers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_ObjPointerCompare( void ** pp1, void ** pp2 ) +{ + if ( *pp1 < *pp2 ) + return -1; + if ( *pp1 > *pp2 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Adjusts the copy pointers.] + + Description [This procedure assumes that the network was transformed + into another network, which was in turn transformed into yet another + network. It makes the pCopy pointers of the original network point to + the objects of the yet another network.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkTransferCopy( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i; + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( !Abc_ObjIsNet(pObj) ) + pObj->pCopy = pObj->pCopy? Abc_ObjCopyCond(pObj->pCopy) : NULL; +} + + +/**Function************************************************************* + + Synopsis [Increaments the cut counter.] + + Description [Returns 1 if it becomes equal to the ref counter.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Abc_ObjCrossCutInc( Abc_Obj_t * pObj ) +{ +// pObj->pCopy = (void *)(((int)pObj->pCopy)++); + int Value = (int)pObj->pCopy; + pObj->pCopy = (void *)(Value + 1); + return (int)pObj->pCopy == Abc_ObjFanoutNum(pObj); +} + +/**Function************************************************************* + + Synopsis [Computes cross-cut of the circuit.] + + Description [Returns 1 if it is the last visit to the node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkCrossCut_rec( Abc_Obj_t * pObj, int * pnCutSize, int * pnCutSizeMax ) +{ + Abc_Obj_t * pFanin; + int i, nDecrem = 0; + int fReverse = 0; + if ( Abc_ObjIsCi(pObj) ) + return 0; + // if visited, increment visit counter + if ( Abc_NodeIsTravIdCurrent( pObj ) ) + return Abc_ObjCrossCutInc( pObj ); + Abc_NodeSetTravIdCurrent( pObj ); + // visit the fanins + if ( !Abc_ObjIsCi(pObj) ) + { + if ( fReverse ) + { + Abc_ObjForEachFanin( pObj, pFanin, i ) + { + pFanin = Abc_ObjFanin( pObj, Abc_ObjFaninNum(pObj) - 1 - i ); + nDecrem += Abc_NtkCrossCut_rec( pFanin, pnCutSize, pnCutSizeMax ); + } + } + else + { + Abc_ObjForEachFanin( pObj, pFanin, i ) + nDecrem += Abc_NtkCrossCut_rec( pFanin, pnCutSize, pnCutSizeMax ); + } + } + // count the node + (*pnCutSize)++; + if ( *pnCutSizeMax < *pnCutSize ) + *pnCutSizeMax = *pnCutSize; + (*pnCutSize) -= nDecrem; + return Abc_ObjCrossCutInc( pObj ); +} + +/**Function************************************************************* + + Synopsis [Computes cross-cut of the circuit.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkCrossCut( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int nCutSize = 0, nCutSizeMax = 0; + int i; + Abc_NtkCleanCopy( pNtk ); + Abc_NtkIncrementTravId( pNtk ); + Abc_NtkForEachCo( pNtk, pObj, i ) + { + Abc_NtkCrossCut_rec( pObj, &nCutSize, &nCutSizeMax ); + nCutSize--; + } + assert( nCutSize == 0 ); + printf( "Max cross cut size = %6d. Ratio = %6.2f %%\n", nCutSizeMax, 100.0 * nCutSizeMax/Abc_NtkObjNum(pNtk) ); + return nCutSizeMax; +} + + +/**Function************************************************************* + + Synopsis [Prints all 3-var functions.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrint256() +{ + FILE * pFile; + unsigned i; + pFile = fopen( "4varfs.txt", "w" ); + for ( i = 1; i < (1<<16)-1; i++ ) + { + fprintf( pFile, "read_truth " ); + Extra_PrintBinary( pFile, &i, 16 ); + fprintf( pFile, "; clp; st; w 1.blif; map; cec 1.blif\n" ); + } + fclose( pFile ); +} + + +static int * pSupps; + +/**Function************************************************************* + + Synopsis [Compares the supergates by their level.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkCompareConesCompare( int * pNum1, int * pNum2 ) +{ + if ( pSupps[*pNum1] > pSupps[*pNum2] ) + return -1; + if ( pSupps[*pNum1] < pSupps[*pNum2] ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Analyze choice node support.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkCompareCones( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vSupp, * vNodes, * vReverse; + Abc_Obj_t * pObj, * pTemp; + int Iter, i, k, Counter, CounterCos, CounterCosNew; + int * pPerms; + + // sort COs by support size + pPerms = ALLOC( int, Abc_NtkCoNum(pNtk) ); + pSupps = ALLOC( int, Abc_NtkCoNum(pNtk) ); + Abc_NtkForEachCo( pNtk, pObj, i ) + { + pPerms[i] = i; + vSupp = Abc_NtkNodeSupport( pNtk, &pObj, 1 ); + pSupps[i] = Vec_PtrSize(vSupp); + Vec_PtrFree( vSupp ); + } + qsort( (void *)pPerms, Abc_NtkCoNum(pNtk), sizeof(int), (int (*)(const void *, const void *)) Abc_NtkCompareConesCompare ); + + // consider COs in this order + Iter = 0; + Abc_NtkForEachCo( pNtk, pObj, i ) + { + pObj = Abc_NtkCo( pNtk, pPerms[i] ); + if ( pObj->fMarkA ) + continue; + Iter++; + + vSupp = Abc_NtkNodeSupport( pNtk, &pObj, 1 ); + vNodes = Abc_NtkDfsNodes( pNtk, &pObj, 1 ); + vReverse = Abc_NtkDfsReverseNodesContained( pNtk, (Abc_Obj_t **)Vec_PtrArray(vSupp), Vec_PtrSize(vSupp) ); + // count the number of nodes in the reverse cone + Counter = 0; + for ( k = 1; k < Vec_PtrSize(vReverse) - 1; k++ ) + for ( pTemp = Vec_PtrEntry(vReverse, k); pTemp; pTemp = pTemp->pCopy ) + Counter++; + CounterCos = CounterCosNew = 0; + for ( pTemp = Vec_PtrEntryLast(vReverse); pTemp; pTemp = pTemp->pCopy ) + { + assert( Abc_ObjIsCo(pTemp) ); + CounterCos++; + if ( pTemp->fMarkA == 0 ) + CounterCosNew++; + pTemp->fMarkA = 1; + } + // print statistics + printf( "%4d CO %5d : Supp = %5d. Lev = %3d. Cone = %5d. Rev = %5d. COs = %3d (%3d).\n", + Iter, pPerms[i], Vec_PtrSize(vSupp), Abc_ObjLevel(Abc_ObjFanin0(pObj)), Vec_PtrSize(vNodes), Counter, CounterCos, CounterCosNew ); + + // free arrays + Vec_PtrFree( vSupp ); + Vec_PtrFree( vNodes ); + Vec_PtrFree( vReverse ); + + if ( Vec_PtrSize(vSupp) < 10 ) + break; + } + Abc_NtkForEachCo( pNtk, pObj, i ) + pObj->fMarkA = 0; + + free( pPerms ); + free( pSupps ); +} + +/**Function************************************************************* + + Synopsis [Analyze choice node support.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkCompareSupports( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vSupp; + Abc_Obj_t * pObj, * pTemp; + int i, nNodesOld; + assert( Abc_NtkIsStrash(pNtk) ); + Abc_AigForEachAnd( pNtk, pObj, i ) + { + if ( !Abc_AigNodeIsChoice(pObj) ) + continue; + + vSupp = Abc_NtkNodeSupport( pNtk, &pObj, 1 ); + nNodesOld = Vec_PtrSize(vSupp); + Vec_PtrFree( vSupp ); + + for ( pTemp = pObj->pData; pTemp; pTemp = pTemp->pData ) + { + vSupp = Abc_NtkNodeSupport( pNtk, &pTemp, 1 ); + if ( nNodesOld != Vec_PtrSize(vSupp) ) + printf( "Choice orig = %3d Choice new = %3d\n", nNodesOld, Vec_PtrSize(vSupp) ); + Vec_PtrFree( vSupp ); + } + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abc/abc_.c b/abc_with_bb_support/src/base/abc/abc_.c new file mode 100644 index 000000000..c4f7de7df --- /dev/null +++ b/abc_with_bb_support/src/base/abc/abc_.c @@ -0,0 +1,47 @@ +/**CFile**************************************************************** + + FileName [abc_.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abc_.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abc/module.make b/abc_with_bb_support/src/base/abc/module.make new file mode 100644 index 000000000..c9c0e281b --- /dev/null +++ b/abc_with_bb_support/src/base/abc/module.make @@ -0,0 +1,18 @@ +SRC += src/base/abc/abcAig.c \ + src/base/abc/abcBlifMv.c \ + src/base/abc/abcCheck.c \ + src/base/abc/abcDfs.c \ + src/base/abc/abcFanio.c \ + src/base/abc/abcFunc.c \ + src/base/abc/abcHie.c \ + src/base/abc/abcLatch.c \ + src/base/abc/abcLib.c \ + src/base/abc/abcMinBase.c \ + src/base/abc/abcNames.c \ + src/base/abc/abcNetlist.c \ + src/base/abc/abcNtk.c \ + src/base/abc/abcObj.c \ + src/base/abc/abcRefs.c \ + src/base/abc/abcShow.c \ + src/base/abc/abcSop.c \ + src/base/abc/abcUtil.c diff --git a/abc_with_bb_support/src/base/abci/abc.c b/abc_with_bb_support/src/base/abci/abc.c new file mode 100644 index 000000000..5c53ab1ea --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abc.c @@ -0,0 +1,12160 @@ +/**CFile**************************************************************** + + FileName [abc.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Command file.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abc.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "mainInt.h" +#include "fraig.h" +#include "fxu.h" +#include "cut.h" +#include "fpga.h" +#include "if.h" +#include "res.h" +#include "lpk.h" +#include "aig.h" +#include "dar.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Abc_CommandPrintStats ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandPrintExdc ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandPrintIo ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandPrintLatch ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandPrintFanio ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandPrintMffc ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandPrintFactor ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandPrintLevel ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandPrintSupport ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandPrintSymms ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandPrintUnate ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandPrintAuto ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandPrintKMap ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandPrintGates ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandPrintSharing ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandPrintXCut ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandPrintDsd ( Abc_Frame_t * pAbc, int argc, char ** argv ); + +static int Abc_CommandShow ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandShowBdd ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandShowCut ( Abc_Frame_t * pAbc, int argc, char ** argv ); + +static int Abc_CommandCollapse ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandStrash ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandBalance ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandMulti ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandRenode ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandCleanup ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandSweep ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandFastExtract ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandDisjoint ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandImfs ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandLutpack ( Abc_Frame_t * pAbc, int argc, char ** argv ); + +static int Abc_CommandRewrite ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandRefactor ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandRestructure ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandResubstitute ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandRr ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandCascade ( Abc_Frame_t * pAbc, int argc, char ** argv ); + +static int Abc_CommandLogic ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandComb ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandMiter ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandDemiter ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandOrPos ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandAndPos ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandAppend ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandFrames ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandSop ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandBdd ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandAig ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandReorder ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandOrder ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandMuxes ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandExtSeqDcs ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandCone ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandNode ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandTopmost ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandShortNames ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandExdcFree ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandExdcGet ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandExdcSet ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandCut ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandEspresso ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandGen ( Abc_Frame_t * pAbc, int argc, char ** argv ); +//static int Abc_CommandXyz ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandDouble ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandTest ( Abc_Frame_t * pAbc, int argc, char ** argv ); + +static int Abc_CommandQuaVar ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandQuaRel ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandQuaReach ( Abc_Frame_t * pAbc, int argc, char ** argv ); + +static int Abc_CommandIStrash ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandICut ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandIRewrite ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandDRewrite ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandDRefactor ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandDCompress2 ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandDrwsat ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandIRewriteSeq ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandIResyn ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandISat ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandIFraig ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandDFraig ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandCSweep ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandIProve ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandHaig ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandMini ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandBmc ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandQbf ( Abc_Frame_t * pAbc, int argc, char ** argv ); + +static int Abc_CommandFraig ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandFraigTrust ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandFraigStore ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandFraigRestore ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandFraigClean ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandFraigSweep ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandFraigDress ( Abc_Frame_t * pAbc, int argc, char ** argv ); + +static int Abc_CommandHaigStart ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandHaigStop ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandHaigUse ( Abc_Frame_t * pAbc, int argc, char ** argv ); + +static int Abc_CommandRecStart ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandRecStop ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandRecAdd ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandRecPs ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandRecUse ( Abc_Frame_t * pAbc, int argc, char ** argv ); + +static int Abc_CommandMap ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandUnmap ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandAttach ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandSuperChoice ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandSuperChoiceLut ( Abc_Frame_t * pAbc, int argc, char ** argv ); + +static int Abc_CommandFpga ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandFpgaFast ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandIf ( Abc_Frame_t * pAbc, int argc, char ** argv ); + +static int Abc_CommandScut ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandInit ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandZero ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandPipe ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandSeq ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandUnseq ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandRetime ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandSeqFpga ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandSeqMap ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandSeqSweep ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandSeqCleanup ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandCycle ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandXsim ( Abc_Frame_t * pAbc, int argc, char ** argv ); + +static int Abc_CommandCec ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandSec ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandDSec ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandSat ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandDSat ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandProve ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandDebug ( Abc_Frame_t * pAbc, int argc, char ** argv ); + +static int Abc_CommandTraceStart ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandTraceCheck ( Abc_Frame_t * pAbc, int argc, char ** argv ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_Init( Abc_Frame_t * pAbc ) +{ +// Abc_NtkBddImplicationTest(); + + Cmd_CommandAdd( pAbc, "Printing", "print_stats", Abc_CommandPrintStats, 0 ); + Cmd_CommandAdd( pAbc, "Printing", "print_exdc", Abc_CommandPrintExdc, 0 ); + Cmd_CommandAdd( pAbc, "Printing", "print_io", Abc_CommandPrintIo, 0 ); + Cmd_CommandAdd( pAbc, "Printing", "print_latch", Abc_CommandPrintLatch, 0 ); + Cmd_CommandAdd( pAbc, "Printing", "print_fanio", Abc_CommandPrintFanio, 0 ); + Cmd_CommandAdd( pAbc, "Printing", "print_mffc", Abc_CommandPrintMffc, 0 ); + Cmd_CommandAdd( pAbc, "Printing", "print_factor", Abc_CommandPrintFactor, 0 ); + Cmd_CommandAdd( pAbc, "Printing", "print_level", Abc_CommandPrintLevel, 0 ); + Cmd_CommandAdd( pAbc, "Printing", "print_supp", Abc_CommandPrintSupport, 0 ); + Cmd_CommandAdd( pAbc, "Printing", "print_symm", Abc_CommandPrintSymms, 0 ); + Cmd_CommandAdd( pAbc, "Printing", "print_unate", Abc_CommandPrintUnate, 0 ); + Cmd_CommandAdd( pAbc, "Printing", "print_auto", Abc_CommandPrintAuto, 0 ); + Cmd_CommandAdd( pAbc, "Printing", "print_kmap", Abc_CommandPrintKMap, 0 ); + Cmd_CommandAdd( pAbc, "Printing", "print_gates", Abc_CommandPrintGates, 0 ); + Cmd_CommandAdd( pAbc, "Printing", "print_sharing", Abc_CommandPrintSharing, 0 ); + Cmd_CommandAdd( pAbc, "Printing", "print_xcut", Abc_CommandPrintXCut, 0 ); + Cmd_CommandAdd( pAbc, "Printing", "print_dsd", Abc_CommandPrintDsd, 0 ); + + Cmd_CommandAdd( pAbc, "Printing", "show", Abc_CommandShow, 0 ); + Cmd_CommandAdd( pAbc, "Printing", "show_bdd", Abc_CommandShowBdd, 0 ); + Cmd_CommandAdd( pAbc, "Printing", "show_cut", Abc_CommandShowCut, 0 ); + + Cmd_CommandAdd( pAbc, "Synthesis", "collapse", Abc_CommandCollapse, 1 ); + Cmd_CommandAdd( pAbc, "Synthesis", "strash", Abc_CommandStrash, 1 ); + Cmd_CommandAdd( pAbc, "Synthesis", "balance", Abc_CommandBalance, 1 ); + Cmd_CommandAdd( pAbc, "Synthesis", "multi", Abc_CommandMulti, 1 ); + Cmd_CommandAdd( pAbc, "Synthesis", "renode", Abc_CommandRenode, 1 ); + Cmd_CommandAdd( pAbc, "Synthesis", "cleanup", Abc_CommandCleanup, 1 ); + Cmd_CommandAdd( pAbc, "Synthesis", "sweep", Abc_CommandSweep, 1 ); + Cmd_CommandAdd( pAbc, "Synthesis", "fx", Abc_CommandFastExtract, 1 ); + Cmd_CommandAdd( pAbc, "Synthesis", "dsd", Abc_CommandDisjoint, 1 ); + Cmd_CommandAdd( pAbc, "Synthesis", "imfs", Abc_CommandImfs, 1 ); + Cmd_CommandAdd( pAbc, "Synthesis", "lutpack", Abc_CommandLutpack, 1 ); + + Cmd_CommandAdd( pAbc, "Synthesis", "rewrite", Abc_CommandRewrite, 1 ); + Cmd_CommandAdd( pAbc, "Synthesis", "refactor", Abc_CommandRefactor, 1 ); +// Cmd_CommandAdd( pAbc, "Synthesis", "restructure", Abc_CommandRestructure, 1 ); + Cmd_CommandAdd( pAbc, "Synthesis", "resub", Abc_CommandResubstitute, 1 ); + Cmd_CommandAdd( pAbc, "Synthesis", "rr", Abc_CommandRr, 1 ); + Cmd_CommandAdd( pAbc, "Synthesis", "cascade", Abc_CommandCascade, 1 ); + + Cmd_CommandAdd( pAbc, "Various", "logic", Abc_CommandLogic, 1 ); + Cmd_CommandAdd( pAbc, "Various", "comb", Abc_CommandComb, 1 ); + Cmd_CommandAdd( pAbc, "Various", "miter", Abc_CommandMiter, 1 ); + Cmd_CommandAdd( pAbc, "Various", "demiter", Abc_CommandDemiter, 1 ); + Cmd_CommandAdd( pAbc, "Various", "orpos", Abc_CommandOrPos, 1 ); + Cmd_CommandAdd( pAbc, "Various", "andpos", Abc_CommandAndPos, 1 ); + Cmd_CommandAdd( pAbc, "Various", "append", Abc_CommandAppend, 1 ); + Cmd_CommandAdd( pAbc, "Various", "frames", Abc_CommandFrames, 1 ); + Cmd_CommandAdd( pAbc, "Various", "sop", Abc_CommandSop, 0 ); + Cmd_CommandAdd( pAbc, "Various", "bdd", Abc_CommandBdd, 0 ); + Cmd_CommandAdd( pAbc, "Various", "aig", Abc_CommandAig, 0 ); + Cmd_CommandAdd( pAbc, "Various", "reorder", Abc_CommandReorder, 0 ); + Cmd_CommandAdd( pAbc, "Various", "order", Abc_CommandOrder, 0 ); + Cmd_CommandAdd( pAbc, "Various", "muxes", Abc_CommandMuxes, 1 ); + Cmd_CommandAdd( pAbc, "Various", "ext_seq_dcs", Abc_CommandExtSeqDcs, 0 ); + Cmd_CommandAdd( pAbc, "Various", "cone", Abc_CommandCone, 1 ); + Cmd_CommandAdd( pAbc, "Various", "node", Abc_CommandNode, 1 ); + Cmd_CommandAdd( pAbc, "Various", "topmost", Abc_CommandTopmost, 1 ); + Cmd_CommandAdd( pAbc, "Various", "short_names", Abc_CommandShortNames, 0 ); + Cmd_CommandAdd( pAbc, "Various", "exdc_free", Abc_CommandExdcFree, 1 ); + Cmd_CommandAdd( pAbc, "Various", "exdc_get", Abc_CommandExdcGet, 1 ); + Cmd_CommandAdd( pAbc, "Various", "exdc_set", Abc_CommandExdcSet, 1 ); + Cmd_CommandAdd( pAbc, "Various", "cut", Abc_CommandCut, 0 ); + Cmd_CommandAdd( pAbc, "Various", "espresso", Abc_CommandEspresso, 1 ); + Cmd_CommandAdd( pAbc, "Various", "gen", Abc_CommandGen, 0 ); +// Cmd_CommandAdd( pAbc, "Various", "xyz", Abc_CommandXyz, 1 ); + Cmd_CommandAdd( pAbc, "Various", "double", Abc_CommandDouble, 1 ); + Cmd_CommandAdd( pAbc, "Various", "test", Abc_CommandTest, 0 ); + + Cmd_CommandAdd( pAbc, "Various", "qvar", Abc_CommandQuaVar, 1 ); + Cmd_CommandAdd( pAbc, "Various", "qrel", Abc_CommandQuaRel, 1 ); + Cmd_CommandAdd( pAbc, "Various", "qreach", Abc_CommandQuaReach, 1 ); + + Cmd_CommandAdd( pAbc, "New AIG", "istrash", Abc_CommandIStrash, 1 ); + Cmd_CommandAdd( pAbc, "New AIG", "icut", Abc_CommandICut, 0 ); + Cmd_CommandAdd( pAbc, "New AIG", "irw", Abc_CommandIRewrite, 1 ); + Cmd_CommandAdd( pAbc, "New AIG", "drw", Abc_CommandDRewrite, 1 ); + Cmd_CommandAdd( pAbc, "New AIG", "drf", Abc_CommandDRefactor, 1 ); + Cmd_CommandAdd( pAbc, "New AIG", "dcompress2", Abc_CommandDCompress2, 1 ); + Cmd_CommandAdd( pAbc, "New AIG", "drwsat", Abc_CommandDrwsat, 1 ); + Cmd_CommandAdd( pAbc, "New AIG", "irws", Abc_CommandIRewriteSeq, 1 ); + Cmd_CommandAdd( pAbc, "New AIG", "iresyn", Abc_CommandIResyn, 1 ); + Cmd_CommandAdd( pAbc, "New AIG", "isat", Abc_CommandISat, 1 ); + Cmd_CommandAdd( pAbc, "New AIG", "ifraig", Abc_CommandIFraig, 1 ); + Cmd_CommandAdd( pAbc, "New AIG", "dfraig", Abc_CommandDFraig, 1 ); + Cmd_CommandAdd( pAbc, "New AIG", "csweep", Abc_CommandCSweep, 1 ); + Cmd_CommandAdd( pAbc, "New AIG", "iprove", Abc_CommandIProve, 1 ); + Cmd_CommandAdd( pAbc, "New AIG", "haig", Abc_CommandHaig, 1 ); + Cmd_CommandAdd( pAbc, "New AIG", "mini", Abc_CommandMini, 1 ); + Cmd_CommandAdd( pAbc, "New AIG", "bmc", Abc_CommandBmc, 0 ); + Cmd_CommandAdd( pAbc, "New AIG", "qbf", Abc_CommandQbf, 0 ); + + Cmd_CommandAdd( pAbc, "Fraiging", "fraig", Abc_CommandFraig, 1 ); + Cmd_CommandAdd( pAbc, "Fraiging", "fraig_trust", Abc_CommandFraigTrust, 1 ); + Cmd_CommandAdd( pAbc, "Fraiging", "fraig_store", Abc_CommandFraigStore, 0 ); + Cmd_CommandAdd( pAbc, "Fraiging", "fraig_restore", Abc_CommandFraigRestore, 1 ); + Cmd_CommandAdd( pAbc, "Fraiging", "fraig_clean", Abc_CommandFraigClean, 0 ); + Cmd_CommandAdd( pAbc, "Fraiging", "fraig_sweep", Abc_CommandFraigSweep, 1 ); + Cmd_CommandAdd( pAbc, "Fraiging", "dress", Abc_CommandFraigDress, 1 ); + + Cmd_CommandAdd( pAbc, "Choicing", "haig_start", Abc_CommandHaigStart, 0 ); + Cmd_CommandAdd( pAbc, "Choicing", "haig_stop", Abc_CommandHaigStop, 0 ); + Cmd_CommandAdd( pAbc, "Choicing", "haig_use", Abc_CommandHaigUse, 1 ); + + Cmd_CommandAdd( pAbc, "Choicing", "rec_start", Abc_CommandRecStart, 0 ); + Cmd_CommandAdd( pAbc, "Choicing", "rec_stop", Abc_CommandRecStop, 0 ); + Cmd_CommandAdd( pAbc, "Choicing", "rec_add", Abc_CommandRecAdd, 0 ); + Cmd_CommandAdd( pAbc, "Choicing", "rec_ps", Abc_CommandRecPs, 0 ); + Cmd_CommandAdd( pAbc, "Choicing", "rec_use", Abc_CommandRecUse, 1 ); + + Cmd_CommandAdd( pAbc, "SC mapping", "map", Abc_CommandMap, 1 ); + Cmd_CommandAdd( pAbc, "SC mapping", "unmap", Abc_CommandUnmap, 1 ); + Cmd_CommandAdd( pAbc, "SC mapping", "attach", Abc_CommandAttach, 1 ); + Cmd_CommandAdd( pAbc, "SC mapping", "sc", Abc_CommandSuperChoice, 1 ); + Cmd_CommandAdd( pAbc, "SC mapping", "scl", Abc_CommandSuperChoiceLut, 1 ); + + Cmd_CommandAdd( pAbc, "FPGA mapping", "fpga", Abc_CommandFpga, 1 ); + Cmd_CommandAdd( pAbc, "FPGA mapping", "ffpga", Abc_CommandFpgaFast, 1 ); + Cmd_CommandAdd( pAbc, "FPGA mapping", "if", Abc_CommandIf, 1 ); + +// Cmd_CommandAdd( pAbc, "Sequential", "scut", Abc_CommandScut, 0 ); + Cmd_CommandAdd( pAbc, "Sequential", "init", Abc_CommandInit, 1 ); + Cmd_CommandAdd( pAbc, "Sequential", "zero", Abc_CommandZero, 1 ); +// Cmd_CommandAdd( pAbc, "Sequential", "pipe", Abc_CommandPipe, 1 ); +// Cmd_CommandAdd( pAbc, "Sequential", "seq", Abc_CommandSeq, 1 ); +// Cmd_CommandAdd( pAbc, "Sequential", "unseq", Abc_CommandUnseq, 1 ); + Cmd_CommandAdd( pAbc, "Sequential", "retime", Abc_CommandRetime, 1 ); +// Cmd_CommandAdd( pAbc, "Sequential", "sfpga", Abc_CommandSeqFpga, 1 ); +// Cmd_CommandAdd( pAbc, "Sequential", "smap", Abc_CommandSeqMap, 1 ); + Cmd_CommandAdd( pAbc, "Sequential", "ssweep", Abc_CommandSeqSweep, 1 ); + Cmd_CommandAdd( pAbc, "Sequential", "scleanup", Abc_CommandSeqCleanup, 1 ); + Cmd_CommandAdd( pAbc, "Sequential", "cycle", Abc_CommandCycle, 1 ); + Cmd_CommandAdd( pAbc, "Sequential", "xsim", Abc_CommandXsim, 0 ); + + Cmd_CommandAdd( pAbc, "Verification", "cec", Abc_CommandCec, 0 ); + Cmd_CommandAdd( pAbc, "Verification", "sec", Abc_CommandSec, 0 ); + Cmd_CommandAdd( pAbc, "Verification", "dsec", Abc_CommandDSec, 0 ); + Cmd_CommandAdd( pAbc, "Verification", "sat", Abc_CommandSat, 0 ); + Cmd_CommandAdd( pAbc, "Verification", "dsat", Abc_CommandDSat, 0 ); + Cmd_CommandAdd( pAbc, "Verification", "prove", Abc_CommandProve, 1 ); + Cmd_CommandAdd( pAbc, "Verification", "debug", Abc_CommandDebug, 0 ); + +// Cmd_CommandAdd( pAbc, "Verification", "trace_start", Abc_CommandTraceStart, 0 ); +// Cmd_CommandAdd( pAbc, "Verification", "trace_check", Abc_CommandTraceCheck, 0 ); + +// Rwt_Man4ExploreStart(); +// Map_Var3Print(); +// Map_Var4Test(); + +// Abc_NtkPrint256(); +// Kit_TruthCountMintermsPrecomp(); +// Kit_DsdPrecompute4Vars(); + + { + extern void Dar_LibStart(); + Dar_LibStart(); + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_End() +{ +// Dar_LibDumpPriorities(); + + { + extern void Cnf_ClearMemory(); + Cnf_ClearMemory(); + } + { + extern void Dar_LibStop(); + Dar_LibStop(); + } + + Abc_NtkFraigStoreClean(); +// Rwt_Man4ExplorePrint(); + +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandPrintStats( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + bool fShort; + int c; + int fFactor; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set the defaults + fShort = 1; + fFactor = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "sfh" ) ) != EOF ) + { + switch ( c ) + { + case 's': + fShort ^= 1; + break; + case 'f': + fFactor ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( Abc_FrameReadErr(pAbc), "Empty network.\n" ); + return 1; + } + Abc_NtkPrintStats( pOut, pNtk, fFactor ); + return 0; + +usage: + fprintf( pErr, "usage: print_stats [-fh]\n" ); + fprintf( pErr, "\t prints the network statistics\n" ); + fprintf( pErr, "\t-f : toggles printing the literal count in the factored forms [default = %s]\n", fFactor? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandPrintExdc( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkTemp; + double Percentage; + bool fShort; + int c; + int fPrintDc; + + extern double Abc_NtkSpacePercentage( Abc_Obj_t * pNode ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set the defaults + fShort = 1; + fPrintDc = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "sdh" ) ) != EOF ) + { + switch ( c ) + { + case 's': + fShort ^= 1; + break; + case 'd': + fPrintDc ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( Abc_FrameReadErr(pAbc), "Empty network.\n" ); + return 1; + } + if ( pNtk->pExdc == NULL ) + { + fprintf( Abc_FrameReadErr(pAbc), "Network has no EXDC.\n" ); + return 1; + } + + if ( fPrintDc ) + { + if ( !Abc_NtkIsStrash(pNtk->pExdc) ) + { + pNtkTemp = Abc_NtkStrash(pNtk->pExdc, 0, 0, 0); + Percentage = Abc_NtkSpacePercentage( Abc_ObjChild0( Abc_NtkPo(pNtkTemp, 0) ) ); + Abc_NtkDelete( pNtkTemp ); + } + else + Percentage = Abc_NtkSpacePercentage( Abc_ObjChild0( Abc_NtkPo(pNtk->pExdc, 0) ) ); + + printf( "EXDC network statistics: " ); + printf( "(" ); + if ( Percentage > 0.05 && Percentage < 99.95 ) + printf( "%.2f", Percentage ); + else if ( Percentage > 0.000005 && Percentage < 99.999995 ) + printf( "%.6f", Percentage ); + else + printf( "%f", Percentage ); + printf( " %% don't-cares)\n" ); + } + else + printf( "EXDC network statistics: \n" ); + Abc_NtkPrintStats( pOut, pNtk->pExdc, 0 ); + return 0; + +usage: + fprintf( pErr, "usage: print_exdc [-dh]\n" ); + fprintf( pErr, "\t prints the EXDC network statistics\n" ); + fprintf( pErr, "\t-d : toggles printing don't-care percentage [default = %s]\n", fPrintDc? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandPrintIo( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + Abc_Obj_t * pNode; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( argc > globalUtilOptind + 1 ) + { + fprintf( pErr, "Wrong number of auguments.\n" ); + goto usage; + } + + if ( argc == globalUtilOptind + 1 ) + { + pNode = Abc_NtkFindNode( pNtk, argv[globalUtilOptind] ); + if ( pNode == NULL ) + { + fprintf( pErr, "Cannot find node \"%s\".\n", argv[globalUtilOptind] ); + return 1; + } + Abc_NodePrintFanio( pOut, pNode ); + return 0; + } + // print the nodes + Abc_NtkPrintIo( pOut, pNtk ); + return 0; + +usage: + fprintf( pErr, "usage: print_io [-h] \n" ); + fprintf( pErr, "\t prints the PIs/POs or fanins/fanouts of a node\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + fprintf( pErr, "\tnode : the node to print fanins/fanouts\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandPrintLatch( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + // print the nodes + Abc_NtkPrintLatch( pOut, pNtk ); + return 0; + +usage: + fprintf( pErr, "usage: print_latch [-h]\n" ); + fprintf( pErr, "\t prints information about latches\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandPrintFanio( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + // print the nodes + Abc_NtkPrintFanio( pOut, pNtk ); + return 0; + +usage: + fprintf( pErr, "usage: print_fanio [-h]\n" ); + fprintf( pErr, "\t prints the statistics about fanins/fanouts of all nodes\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandPrintMffc( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + extern void Abc_NtkPrintMffc( FILE * pFile, Abc_Ntk_t * pNtk ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + // print the nodes + Abc_NtkPrintMffc( pOut, pNtk ); + return 0; + +usage: + fprintf( pErr, "usage: print_mffc [-h]\n" ); + fprintf( pErr, "\t prints the MFFC of each node in the network\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandPrintFactor( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + Abc_Obj_t * pNode; + int c; + int fUseRealNames; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fUseRealNames = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "nh" ) ) != EOF ) + { + switch ( c ) + { + case 'n': + fUseRealNames ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( !Abc_NtkIsSopLogic(pNtk) ) + { + fprintf( pErr, "Printing factored forms can be done for SOP networks.\n" ); + return 1; + } + + if ( argc > globalUtilOptind + 1 ) + { + fprintf( pErr, "Wrong number of auguments.\n" ); + goto usage; + } + + if ( argc == globalUtilOptind + 1 ) + { + pNode = Abc_NtkFindNode( pNtk, argv[globalUtilOptind] ); + if ( pNode == NULL ) + { + fprintf( pErr, "Cannot find node \"%s\".\n", argv[globalUtilOptind] ); + return 1; + } + Abc_NodePrintFactor( pOut, pNode, fUseRealNames ); + return 0; + } + // print the nodes + Abc_NtkPrintFactor( pOut, pNtk, fUseRealNames ); + return 0; + +usage: + fprintf( pErr, "usage: print_factor [-nh] \n" ); + fprintf( pErr, "\t prints the factored forms of nodes\n" ); + fprintf( pErr, "\t-n : toggles real/dummy fanin names [default = %s]\n", fUseRealNames? "real": "dummy" ); + fprintf( pErr, "\t-h : print the command usage\n"); + fprintf( pErr, "\tnode : (optional) one node to consider\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandPrintLevel( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + Abc_Obj_t * pNode; + int c; + int fListNodes; + int fProfile; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fListNodes = 0; + fProfile = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "nph" ) ) != EOF ) + { + switch ( c ) + { + case 'n': + fListNodes ^= 1; + break; + case 'p': + fProfile ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( !fProfile && !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "This command works only for AIGs (run \"strash\").\n" ); + return 1; + } + + if ( argc > globalUtilOptind + 1 ) + { + fprintf( pErr, "Wrong number of auguments.\n" ); + goto usage; + } + + if ( argc == globalUtilOptind + 1 ) + { + pNode = Abc_NtkFindNode( pNtk, argv[globalUtilOptind] ); + if ( pNode == NULL ) + { + fprintf( pErr, "Cannot find node \"%s\".\n", argv[globalUtilOptind] ); + return 1; + } + Abc_NodePrintLevel( pOut, pNode ); + return 0; + } + // process all COs + Abc_NtkPrintLevel( pOut, pNtk, fProfile, fListNodes ); + return 0; + +usage: + fprintf( pErr, "usage: print_level [-nph] \n" ); + fprintf( pErr, "\t prints information about node level and cone size\n" ); + fprintf( pErr, "\t-n : toggles printing nodes by levels [default = %s]\n", fListNodes? "yes": "no" ); + fprintf( pErr, "\t-p : toggles printing level profile [default = %s]\n", fProfile? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + fprintf( pErr, "\tnode : (optional) one node to consider\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandPrintSupport( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Vec_Ptr_t * vSuppFun; + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int fStruct; + int fVerbose; + extern Vec_Ptr_t * Sim_ComputeFunSupp( Abc_Ntk_t * pNtk, int fVerbose ); + extern void Abc_NtkPrintStrSupports( Abc_Ntk_t * pNtk ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fStruct = 1; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "svh" ) ) != EOF ) + { + switch ( c ) + { + case 's': + fStruct ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + // print support information + if ( fStruct ) + { + Abc_NtkPrintStrSupports( pNtk ); + return 0; + } + + if ( !Abc_NtkIsComb(pNtk) ) + { + fprintf( pErr, "This command works only for combinational networks (run \"comb\").\n" ); + return 1; + } + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "This command works only for AIGs (run \"strash\").\n" ); + return 1; + } + vSuppFun = Sim_ComputeFunSupp( pNtk, fVerbose ); + free( vSuppFun->pArray[0] ); + Vec_PtrFree( vSuppFun ); + return 0; + +usage: + fprintf( pErr, "usage: print_supp [-svh]\n" ); + fprintf( pErr, "\t prints the supports of the CO nodes\n" ); + fprintf( pErr, "\t-s : toggle printing structural support only [default = %s].\n", fStruct? "yes": "no" ); + fprintf( pErr, "\t-v : enable verbose output [default = %s].\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandPrintSymms( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int fUseBdds; + int fNaive; + int fReorder; + int fVerbose; + extern void Abc_NtkSymmetries( Abc_Ntk_t * pNtk, int fUseBdds, int fNaive, int fReorder, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fUseBdds = 0; + fNaive = 0; + fReorder = 1; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "bnrvh" ) ) != EOF ) + { + switch ( c ) + { + case 'b': + fUseBdds ^= 1; + break; + case 'n': + fNaive ^= 1; + break; + case 'r': + fReorder ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsComb(pNtk) ) + { + fprintf( pErr, "This command works only for combinational networks (run \"comb\").\n" ); + return 1; + } + if ( Abc_NtkIsStrash(pNtk) ) + Abc_NtkSymmetries( pNtk, fUseBdds, fNaive, fReorder, fVerbose ); + else + { + pNtk = Abc_NtkStrash( pNtk, 0, 0, 0 ); + Abc_NtkSymmetries( pNtk, fUseBdds, fNaive, fReorder, fVerbose ); + Abc_NtkDelete( pNtk ); + } + return 0; + +usage: + fprintf( pErr, "usage: print_symm [-bnrvh]\n" ); + fprintf( pErr, "\t computes symmetries of the PO functions\n" ); + fprintf( pErr, "\t-b : toggle BDD-based or SAT-based computations [default = %s].\n", fUseBdds? "BDD": "SAT" ); + fprintf( pErr, "\t-n : enable naive BDD-based computation [default = %s].\n", fNaive? "yes": "no" ); + fprintf( pErr, "\t-r : enable dynamic BDD variable reordering [default = %s].\n", fReorder? "yes": "no" ); + fprintf( pErr, "\t-v : enable verbose output [default = %s].\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandPrintUnate( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int fUseBdds; + int fUseNaive; + int fVerbose; + extern void Abc_NtkPrintUnate( Abc_Ntk_t * pNtk, int fUseBdds, int fUseNaive, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fUseBdds = 1; + fUseNaive = 0; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "bnvh" ) ) != EOF ) + { + switch ( c ) + { + case 'b': + fUseBdds ^= 1; + break; + case 'n': + fUseNaive ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "This command works only for AIGs (run \"strash\").\n" ); + return 1; + } + Abc_NtkPrintUnate( pNtk, fUseBdds, fUseNaive, fVerbose ); + return 0; + +usage: + fprintf( pErr, "usage: print_unate [-bnvh]\n" ); + fprintf( pErr, "\t computes unate variables of the PO functions\n" ); + fprintf( pErr, "\t-b : toggle BDD-based or SAT-based computations [default = %s].\n", fUseBdds? "BDD": "SAT" ); + fprintf( pErr, "\t-n : toggle naive BDD-based computation [default = %s].\n", fUseNaive? "yes": "no" ); + fprintf( pErr, "\t-v : enable verbose output [default = %s].\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandPrintAuto( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int Output; + int fNaive; + int fVerbose; + extern void Abc_NtkAutoPrint( Abc_Ntk_t * pNtk, int Output, int fNaive, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Output = -1; + fNaive = 0; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Onvh" ) ) != EOF ) + { + switch ( c ) + { + case 'O': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-O\" should be followed by an integer.\n" ); + goto usage; + } + Output = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( Output < 0 ) + goto usage; + break; + case 'n': + fNaive ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "This command works only for AIGs (run \"strash\").\n" ); + return 1; + } + + + Abc_NtkAutoPrint( pNtk, Output, fNaive, fVerbose ); + return 0; + +usage: + fprintf( pErr, "usage: print_auto [-O num] [-nvh]\n" ); + fprintf( pErr, "\t computes autosymmetries of the PO functions\n" ); + fprintf( pErr, "\t-O num : (optional) the 0-based number of the output [default = all]\n"); + fprintf( pErr, "\t-n : enable naive BDD-based computation [default = %s].\n", fNaive? "yes": "no" ); + fprintf( pErr, "\t-v : enable verbose output [default = %s].\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandPrintKMap( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + Abc_Obj_t * pNode; + int c; + int fUseRealNames; + + extern void Abc_NodePrintKMap( Abc_Obj_t * pNode, int fUseRealNames ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fUseRealNames = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "nh" ) ) != EOF ) + { + switch ( c ) + { + case 'n': + fUseRealNames ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( !Abc_NtkIsLogic(pNtk) ) + { + fprintf( pErr, "Visualization of Karnaugh maps works for logic networks.\n" ); + return 1; + } + if ( argc > globalUtilOptind + 1 ) + { + fprintf( pErr, "Wrong number of auguments.\n" ); + goto usage; + } + if ( argc == globalUtilOptind ) + { + pNode = Abc_ObjFanin0( Abc_NtkPo(pNtk, 0) ); + if ( !Abc_ObjIsNode(pNode) ) + { + fprintf( pErr, "The driver \"%s\" of the first PO is not an internal node.\n", Abc_ObjName(pNode) ); + return 1; + } + } + else + { + pNode = Abc_NtkFindNode( pNtk, argv[globalUtilOptind] ); + if ( pNode == NULL ) + { + fprintf( pErr, "Cannot find node \"%s\".\n", argv[globalUtilOptind] ); + return 1; + } + } + Abc_NtkToBdd(pNtk); + Abc_NodePrintKMap( pNode, fUseRealNames ); + return 0; + +usage: + fprintf( pErr, "usage: print_kmap [-nh] \n" ); + fprintf( pErr, " shows the truth table of the node\n" ); + fprintf( pErr, "\t-n : toggles real/dummy fanin names [default = %s]\n", fUseRealNames? "real": "dummy" ); + fprintf( pErr, "\t-h : print the command usage\n"); + fprintf( pErr, "\tnode : the node to consider (default = the driver of the first PO)\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandPrintGates( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int fUseLibrary; + + extern void Abc_NtkPrintGates( Abc_Ntk_t * pNtk, int fUseLibrary ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fUseLibrary = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "lh" ) ) != EOF ) + { + switch ( c ) + { + case 'l': + fUseLibrary ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( Abc_NtkHasAig(pNtk) ) + { + fprintf( pErr, "Printing gates does not work for AIGs and sequential AIGs.\n" ); + return 1; + } + + Abc_NtkPrintGates( pNtk, fUseLibrary ); + return 0; + +usage: + fprintf( pErr, "usage: print_gates [-lh]\n" ); + fprintf( pErr, "\t prints statistics about gates used in the network\n" ); + fprintf( pErr, "\t-l : used library gate names (if mapped) [default = %s]\n", fUseLibrary? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandPrintSharing( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int fUseLibrary; + + extern void Abc_NtkPrintSharing( Abc_Ntk_t * pNtk ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fUseLibrary = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "lh" ) ) != EOF ) + { + switch ( c ) + { + case 'l': + fUseLibrary ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + Abc_NtkPrintSharing( pNtk ); + return 0; + +usage: + fprintf( pErr, "usage: print_sharing [-h]\n" ); + fprintf( pErr, "\t prints the number of shared nodes in the TFI cones of the COs\n" ); +// fprintf( pErr, "\t-l : used library gate names (if mapped) [default = %s]\n", fUseLibrary? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandPrintXCut( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int fUseLibrary; + + extern int Abc_NtkCrossCut( Abc_Ntk_t * pNtk ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fUseLibrary = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "lh" ) ) != EOF ) + { + switch ( c ) + { + case 'l': + fUseLibrary ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + Abc_NtkCrossCut( pNtk ); + return 0; + +usage: + fprintf( pErr, "usage: print_xcut [-h]\n" ); + fprintf( pErr, "\t prints the size of the cross cut of the current network\n" ); +// fprintf( pErr, "\t-l : used library gate names (if mapped) [default = %s]\n", fUseLibrary? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandPrintDsd( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int fCofactor; + int nCofLevel; + + extern void Kit_DsdTest( unsigned * pTruth, int nVars ); + extern void Kit_DsdPrintCofactors( unsigned * pTruth, int nVars, int nCofLevel, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nCofLevel = 1; + fCofactor = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Nch" ) ) != EOF ) + { + switch ( c ) + { + case 'N': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-N\" should be followed by an integer.\n" ); + goto usage; + } + nCofLevel = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nCofLevel < 0 ) + goto usage; + break; + case 'c': + fCofactor ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + // get the truth table of the first output + if ( !Abc_NtkIsLogic(pNtk) ) + { + fprintf( pErr, "Currently works only for logic networks.\n" ); + return 1; + } + Abc_NtkToAig( pNtk ); + // convert it to truth table + { + Abc_Obj_t * pObj = Abc_ObjFanin0( Abc_NtkPo(pNtk, 0) ); + Vec_Int_t * vMemory = Vec_IntAlloc(0); + unsigned * pTruth; + if ( !Abc_ObjIsNode(pObj) ) + { + fprintf( pErr, "The fanin of the first PO node does not have a logic function.\n" ); + return 1; + } + if ( Abc_ObjFaninNum(pObj) > 16 ) + { + fprintf( pErr, "Currently works only for up to 16 inputs.\n" ); + return 1; + } + pTruth = Abc_ConvertAigToTruth( pNtk->pManFunc, Hop_Regular(pObj->pData), Abc_ObjFaninNum(pObj), vMemory, 0 ); + if ( Hop_IsComplement(pObj->pData) ) + Extra_TruthNot( pTruth, pTruth, Abc_ObjFaninNum(pObj) ); + Extra_PrintBinary( stdout, pTruth, 1 << Abc_ObjFaninNum(pObj) ); + printf( "\n" ); + if ( fCofactor ) + Kit_DsdPrintCofactors( pTruth, Abc_ObjFaninNum(pObj), nCofLevel, 1 ); + else + Kit_DsdTest( pTruth, Abc_ObjFaninNum(pObj) ); + Vec_IntFree( vMemory ); + } + return 0; + +usage: + fprintf( pErr, "usage: print_dsd [-ch] [-N num]\n" ); + fprintf( pErr, "\t print DSD formula for a single-output function with less than 16 variables\n" ); + fprintf( pErr, "\t-c : toggle recursive cofactoring [default = %s]\n", fCofactor? "yes": "no" ); + fprintf( pErr, "\t-N num : the number of levels to cofactor [default = %d]\n", nCofLevel ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandShow( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int fSeq; + int fGateNames; + int fUseReverse; + extern void Abc_NtkShow( Abc_Ntk_t * pNtk, int fGateNames, int fSeq, int fUseReverse ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fSeq = 0; + fGateNames = 0; + fUseReverse = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "rsgh" ) ) != EOF ) + { + switch ( c ) + { + case 'r': + fUseReverse ^= 1; + break; + case 's': + fSeq ^= 1; + break; + case 'g': + fGateNames ^= 1; + break; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + Abc_NtkShow( pNtk, fGateNames, fSeq, fUseReverse ); + return 0; + +usage: + fprintf( pErr, "usage: show [-srgh]\n" ); + fprintf( pErr, " visualizes the network structure using DOT and GSVIEW\n" ); +#ifdef WIN32 + fprintf( pErr, " \"dot.exe\" and \"gsview32.exe\" should be set in the paths\n" ); + fprintf( pErr, " (\"gsview32.exe\" may be in \"C:\\Program Files\\Ghostgum\\gsview\\\")\n" ); +#endif + fprintf( pErr, "\t-s : toggles visualization of sequential networks [default = %s].\n", fSeq? "yes": "no" ); + fprintf( pErr, "\t-r : toggles ordering nodes in reverse order [default = %s].\n", fUseReverse? "yes": "no" ); + fprintf( pErr, "\t-g : toggles printing gate names for mapped network [default = %s].\n", fGateNames? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandShowBdd( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + Abc_Obj_t * pNode; + int c; + extern void Abc_NodeShowBdd( Abc_Obj_t * pNode ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( !Abc_NtkIsBddLogic(pNtk) ) + { + fprintf( pErr, "Visualizing BDDs can only be done for logic BDD networks (run \"bdd\").\n" ); + return 1; + } + + if ( argc > globalUtilOptind + 1 ) + { + fprintf( pErr, "Wrong number of auguments.\n" ); + goto usage; + } + if ( argc == globalUtilOptind ) + { + pNode = Abc_ObjFanin0( Abc_NtkPo(pNtk, 0) ); + if ( !Abc_ObjIsNode(pNode) ) + { + fprintf( pErr, "The driver \"%s\" of the first PO is not an internal node.\n", Abc_ObjName(pNode) ); + return 1; + } + } + else + { + pNode = Abc_NtkFindNode( pNtk, argv[globalUtilOptind] ); + if ( pNode == NULL ) + { + fprintf( pErr, "Cannot find node \"%s\".\n", argv[globalUtilOptind] ); + return 1; + } + } + Abc_NodeShowBdd( pNode ); + return 0; + +usage: + fprintf( pErr, "usage: show_bdd [-h] \n" ); + fprintf( pErr, " visualizes the BDD of a node using DOT and GSVIEW\n" ); +#ifdef WIN32 + fprintf( pErr, " \"dot.exe\" and \"gsview32.exe\" should be set in the paths\n" ); + fprintf( pErr, " (\"gsview32.exe\" may be in \"C:\\Program Files\\Ghostgum\\gsview\\\")\n" ); +#endif + fprintf( pErr, "\tnode : the node to consider [default = the driver of the first PO]\n"); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandShowCut( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + Abc_Obj_t * pNode; + int c; + int nNodeSizeMax; + int nConeSizeMax; + extern void Abc_NodeShowCut( Abc_Obj_t * pNode, int nNodeSizeMax, int nConeSizeMax ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nNodeSizeMax = 10; + nConeSizeMax = ABC_INFINITY; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "NCh" ) ) != EOF ) + { + switch ( c ) + { + case 'N': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-N\" should be followed by an integer.\n" ); + goto usage; + } + nNodeSizeMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nNodeSizeMax < 0 ) + goto usage; + break; + case 'C': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-C\" should be followed by an integer.\n" ); + goto usage; + } + nConeSizeMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nConeSizeMax < 0 ) + goto usage; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "Visualizing cuts only works for AIGs (run \"strash\").\n" ); + return 1; + } + if ( argc != globalUtilOptind + 1 ) + { + fprintf( pErr, "Wrong number of auguments.\n" ); + goto usage; + } + + pNode = Abc_NtkFindNode( pNtk, argv[globalUtilOptind] ); + if ( pNode == NULL ) + { + fprintf( pErr, "Cannot find node \"%s\".\n", argv[globalUtilOptind] ); + return 1; + } + Abc_NodeShowCut( pNode, nNodeSizeMax, nConeSizeMax ); + return 0; + +usage: + fprintf( pErr, "usage: show_cut [-N num] [-C num] [-h] \n" ); + fprintf( pErr, " visualizes the cut of a node using DOT and GSVIEW\n" ); +#ifdef WIN32 + fprintf( pErr, " \"dot.exe\" and \"gsview32.exe\" should be set in the paths\n" ); + fprintf( pErr, " (\"gsview32.exe\" may be in \"C:\\Program Files\\Ghostgum\\gsview\\\")\n" ); +#endif + fprintf( pErr, "\t-N num : the max size of the cut to be computed [default = %d]\n", nNodeSizeMax ); + fprintf( pErr, "\t-C num : the max support of the containing cone [default = %d]\n", nConeSizeMax ); + fprintf( pErr, "\tnode : the node to consider\n"); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandCollapse( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int fVerbose; + int fBddSizeMax; + int fDualRail; + int fReorder; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fVerbose = 1; + fReorder = 1; + fDualRail = 0; + fBddSizeMax = 50000000; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Brdvh" ) ) != EOF ) + { + switch ( c ) + { + case 'B': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-B\" should be followed by an integer.\n" ); + goto usage; + } + fBddSizeMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( fBddSizeMax < 0 ) + goto usage; + break; + case 'd': + fDualRail ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'r': + fReorder ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( !Abc_NtkIsLogic(pNtk) && !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "Can only collapse a logic network or an AIG.\n" ); + return 1; + } + + // get the new network + if ( Abc_NtkIsStrash(pNtk) ) + pNtkRes = Abc_NtkCollapse( pNtk, fBddSizeMax, fDualRail, fReorder, fVerbose ); + else + { + pNtk = Abc_NtkStrash( pNtk, 0, 0, 0 ); + pNtkRes = Abc_NtkCollapse( pNtk, fBddSizeMax, fDualRail, fReorder, fVerbose ); + Abc_NtkDelete( pNtk ); + } + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Collapsing has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: collapse [-B num] [-rdvh]\n" ); + fprintf( pErr, "\t collapses the network by constructing global BDDs\n" ); + fprintf( pErr, "\t-B num : limit on live BDD nodes during collapsing [default = %d]\n", fBddSizeMax ); + fprintf( pErr, "\t-r : toggles dynamic variable reordering [default = %s]\n", fReorder? "yes": "no" ); + fprintf( pErr, "\t-d : toggles dual-rail collapsing mode [default = %s]\n", fDualRail? "yes": "no" ); + fprintf( pErr, "\t-v : print verbose information [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandStrash( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c; + int fAllNodes; + int fRecord; + int fCleanup; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fAllNodes = 0; + fCleanup = 1; + fRecord = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "acrh" ) ) != EOF ) + { + switch ( c ) + { + case 'a': + fAllNodes ^= 1; + break; + case 'c': + fCleanup ^= 1; + break; + case 'r': + fRecord ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + // get the new network + pNtkRes = Abc_NtkStrash( pNtk, fAllNodes, fCleanup, fRecord ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Strashing has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: strash [-acrh]\n" ); + fprintf( pErr, "\t transforms combinational logic into an AIG\n" ); + fprintf( pErr, "\t-a : toggles between using all nodes and DFS nodes [default = %s]\n", fAllNodes? "all": "DFS" ); + fprintf( pErr, "\t-c : toggles cleanup to remove the dagling AIG nodes [default = %s]\n", fCleanup? "all": "DFS" ); + fprintf( pErr, "\t-r : enables using the record of AIG subgraphs [default = %s]\n", fRecord? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandBalance( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes, * pNtkTemp; + int c; + bool fDuplicate; + bool fSelective; + bool fUpdateLevel; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fDuplicate = 0; + fSelective = 0; + fUpdateLevel = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "ldsh" ) ) != EOF ) + { + switch ( c ) + { + case 'l': + fUpdateLevel ^= 1; + break; + case 'd': + fDuplicate ^= 1; + break; + case 's': + fSelective ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + // get the new network + if ( Abc_NtkIsStrash(pNtk) ) + { + pNtkRes = Abc_NtkBalance( pNtk, fDuplicate, fSelective, fUpdateLevel ); + } + else + { + pNtkTemp = Abc_NtkStrash( pNtk, 0, 0, 0 ); + if ( pNtkTemp == NULL ) + { + fprintf( pErr, "Strashing before balancing has failed.\n" ); + return 1; + } + pNtkRes = Abc_NtkBalance( pNtkTemp, fDuplicate, fSelective, fUpdateLevel ); + Abc_NtkDelete( pNtkTemp ); + } + + // check if balancing worked + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Balancing has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: balance [-ldsh]\n" ); + fprintf( pErr, "\t transforms the current network into a well-balanced AIG\n" ); + fprintf( pErr, "\t-l : toggle minimizing the number of levels [default = %s]\n", fUpdateLevel? "yes": "no" ); + fprintf( pErr, "\t-d : toggle duplication of logic [default = %s]\n", fDuplicate? "yes": "no" ); + fprintf( pErr, "\t-s : toggle duplication on the critical paths [default = %s]\n", fSelective? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandMulti( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int nThresh, nFaninMax, c; + int fCnf; + int fMulti; + int fSimple; + int fFactor; + extern Abc_Ntk_t * Abc_NtkMulti( Abc_Ntk_t * pNtk, int nThresh, int nFaninMax, int fCnf, int fMulti, int fSimple, int fFactor ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nThresh = 1; + nFaninMax = 20; + fCnf = 0; + fMulti = 1; + fSimple = 0; + fFactor = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "TFmcsfh" ) ) != EOF ) + { + switch ( c ) + { + case 'T': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-T\" should be followed by an integer.\n" ); + goto usage; + } + nThresh = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nThresh < 0 ) + goto usage; + break; + case 'F': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-F\" should be followed by an integer.\n" ); + goto usage; + } + nFaninMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nFaninMax < 0 ) + goto usage; + break; + case 'c': + fCnf ^= 1; + break; + case 'm': + fMulti ^= 1; + break; + case 's': + fSimple ^= 1; + break; + case 'f': + fFactor ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "Cannot renode a network that is not an AIG (run \"strash\").\n" ); + return 1; + } + + // get the new network + pNtkRes = Abc_NtkMulti( pNtk, nThresh, nFaninMax, fCnf, fMulti, fSimple, fFactor ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Renoding has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: multi [-T num] [-F num] [-msfch]\n" ); + fprintf( pErr, "\t transforms an AIG into a logic network by creating larger nodes\n" ); + fprintf( pErr, "\t-F num : the maximum fanin size after renoding [default = %d]\n", nFaninMax ); + fprintf( pErr, "\t-T num : the threshold for AIG node duplication [default = %d]\n", nThresh ); + fprintf( pErr, "\t (an AIG node is the root of a new node after renoding\n" ); + fprintf( pErr, "\t if this leads to duplication of no more than %d AIG nodes,\n", nThresh ); + fprintf( pErr, "\t that is, if [(numFanouts(Node)-1) * size(MFFC(Node))] <= %d)\n", nThresh ); + fprintf( pErr, "\t-m : creates multi-input AND graph [default = %s]\n", fMulti? "yes": "no" ); + fprintf( pErr, "\t-s : creates a simple AIG (no renoding) [default = %s]\n", fSimple? "yes": "no" ); + fprintf( pErr, "\t-f : creates a factor-cut network [default = %s]\n", fFactor? "yes": "no" ); + fprintf( pErr, "\t-c : performs renoding to derive the CNF [default = %s]\n", fCnf? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandRenode( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int nLutSize, nCutsMax, c; + int nFlowIters, nAreaIters; + int fArea; + int fUseBdds; + int fUseSops; + int fUseCnfs; + int fUseMv; + int fVerbose; + extern Abc_Ntk_t * Abc_NtkRenode( Abc_Ntk_t * pNtk, int nLutSize, int nCutsMax, int nFlowIters, int nAreaIters, int fArea, int fUseBdds, int fUseSops, int fUseCnfs, int fUseMv, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nLutSize = 8; + nCutsMax = 4; + nFlowIters = 1; + nAreaIters = 1; + fArea = 0; + fUseBdds = 0; + fUseSops = 0; + fUseCnfs = 0; + fUseMv = 0; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "KCFAabscivh" ) ) != EOF ) + { + switch ( c ) + { + case 'K': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-F\" should be followed by an integer.\n" ); + goto usage; + } + nLutSize = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nLutSize < 0 ) + goto usage; + break; + case 'C': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-C\" should be followed by an integer.\n" ); + goto usage; + } + nCutsMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nCutsMax < 0 ) + goto usage; + break; + case 'F': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-F\" should be followed by a positive integer.\n" ); + goto usage; + } + nFlowIters = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nFlowIters < 0 ) + goto usage; + break; + case 'A': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-A\" should be followed by a positive integer.\n" ); + goto usage; + } + nAreaIters = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nAreaIters < 0 ) + goto usage; + break; + case 'a': + fArea ^= 1; + break; + case 'b': + fUseBdds ^= 1; + break; + case 's': + fUseSops ^= 1; + break; + case 'c': + fUseCnfs ^= 1; + break; + case 'i': + fUseMv ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( fUseBdds + fUseSops + fUseCnfs + fUseMv > 1 ) + { + fprintf( pErr, "Cannot optimize two parameters at the same time.\n" ); + return 1; + } + + if ( nLutSize < 3 || nLutSize > IF_MAX_FUNC_LUTSIZE ) + { + fprintf( pErr, "Incorrect LUT size (%d).\n", nLutSize ); + return 1; + } + + if ( nCutsMax < 1 || nCutsMax >= (1<<12) ) + { + fprintf( pErr, "Incorrect number of cuts.\n" ); + return 1; + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "Cannot renode a network that is not an AIG (run \"strash\").\n" ); + return 1; + } + + // get the new network + pNtkRes = Abc_NtkRenode( pNtk, nLutSize, nCutsMax, nFlowIters, nAreaIters, fArea, fUseBdds, fUseSops, fUseCnfs, fUseMv, fVerbose ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Renoding has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: renode [-K num] [-C num] [-F num] [-A num] [-sbciav]\n" ); + fprintf( pErr, "\t transforms the AIG into a logic network with larger nodes\n" ); + fprintf( pErr, "\t while minimizing the number of FF literals of the node SOPs\n" ); + fprintf( pErr, "\t-K num : the max cut size for renoding (2 < num < %d) [default = %d]\n", IF_MAX_FUNC_LUTSIZE+1, nLutSize ); + fprintf( pErr, "\t-C num : the max number of cuts used at a node (0 < num < 2^12) [default = %d]\n", nCutsMax ); + fprintf( pErr, "\t-F num : the number of area flow recovery iterations (num >= 0) [default = %d]\n", nFlowIters ); + fprintf( pErr, "\t-A num : the number of exact area recovery iterations (num >= 0) [default = %d]\n", nAreaIters ); + fprintf( pErr, "\t-s : toggles minimizing SOP cubes instead of FF lits [default = %s]\n", fUseSops? "yes": "no" ); + fprintf( pErr, "\t-b : toggles minimizing BDD nodes instead of FF lits [default = %s]\n", fUseBdds? "yes": "no" ); + fprintf( pErr, "\t-c : toggles minimizing CNF clauses instead of FF lits [default = %s]\n", fUseCnfs? "yes": "no" ); + fprintf( pErr, "\t-i : toggles minimizing MV-SOP instead of FF lits [default = %s]\n", fUseMv? "yes": "no" ); + fprintf( pErr, "\t-a : toggles area-oriented mapping [default = %s]\n", fArea? "yes": "no" ); + fprintf( pErr, "\t-v : print verbose information [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandCleanup( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "Cleanup cannot be performed on the AIG.\n" ); + return 1; + } + // modify the current network + Abc_NtkCleanup( pNtk, 1 ); + return 0; + +usage: + fprintf( pErr, "usage: cleanup [-h]\n" ); + fprintf( pErr, "\t removes dangling nodes\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandSweep( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsLogic(pNtk) ) + { + fprintf( pErr, "The classical (SIS-like) sweep can only be performed on a logic network.\n" ); + return 1; + } + // modify the current network + Abc_NtkSweep( pNtk, 0 ); + return 0; + +usage: + fprintf( pErr, "usage: sweep [-h]\n" ); + fprintf( pErr, "\t removes dangling nodes; propagates constant, buffers, inverters\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandFastExtract( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Abc_Ntk_t * pNtk; + FILE * pOut, * pErr; + Fxu_Data_t * p = NULL; + int c; + extern bool Abc_NtkFastExtract( Abc_Ntk_t * pNtk, Fxu_Data_t * p ); + extern void Abc_NtkFxuFreeInfo( Fxu_Data_t * p ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // allocate the structure + p = ALLOC( Fxu_Data_t, 1 ); + memset( p, 0, sizeof(Fxu_Data_t) ); + // set the defaults + p->nPairsMax = 30000; + p->nNodesExt = 10000; + p->fOnlyS = 0; + p->fOnlyD = 0; + p->fUse0 = 0; + p->fUseCompl = 1; + p->fVerbose = 0; + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "LNsdzcvh")) != EOF ) + { + switch (c) + { + case 'L': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-L\" should be followed by an integer.\n" ); + goto usage; + } + p->nPairsMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( p->nPairsMax < 0 ) + goto usage; + break; + case 'N': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-N\" should be followed by an integer.\n" ); + goto usage; + } + p->nNodesExt = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( p->nNodesExt < 0 ) + goto usage; + break; + case 's': + p->fOnlyS ^= 1; + break; + case 'd': + p->fOnlyD ^= 1; + break; + case 'z': + p->fUse0 ^= 1; + break; + case 'c': + p->fUseCompl ^= 1; + break; + case 'v': + p->fVerbose ^= 1; + break; + case 'h': + goto usage; + break; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + Abc_NtkFxuFreeInfo( p ); + return 1; + } + + if ( Abc_NtkNodeNum(pNtk) == 0 ) + { + fprintf( pErr, "The network does not have internal nodes.\n" ); + Abc_NtkFxuFreeInfo( p ); + return 1; + } + + if ( !Abc_NtkIsLogic(pNtk) ) + { + fprintf( pErr, "Fast extract can only be applied to a logic network (run \"renode\").\n" ); + Abc_NtkFxuFreeInfo( p ); + return 1; + } + + + // the nodes to be merged are linked into the special linked list + Abc_NtkFastExtract( pNtk, p ); + Abc_NtkFxuFreeInfo( p ); + return 0; + +usage: + fprintf( pErr, "usage: fx [-N num] [-L num] [-sdzcvh]\n"); + fprintf( pErr, "\t performs unate fast extract on the current network\n"); + fprintf( pErr, "\t-N num : the maximum number of divisors to extract [default = %d]\n", p->nNodesExt ); + fprintf( pErr, "\t-L num : the maximum number of cube pairs to consider [default = %d]\n", p->nPairsMax ); + fprintf( pErr, "\t-s : use only single-cube divisors [default = %s]\n", p->fOnlyS? "yes": "no" ); + fprintf( pErr, "\t-d : use only double-cube divisors [default = %s]\n", p->fOnlyD? "yes": "no" ); + fprintf( pErr, "\t-z : use zero-weight divisors [default = %s]\n", p->fUse0? "yes": "no" ); + fprintf( pErr, "\t-c : use complement in the binary case [default = %s]\n", p->fUseCompl? "yes": "no" ); + fprintf( pErr, "\t-v : print verbose information [default = %s]\n", p->fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + Abc_NtkFxuFreeInfo( p ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandDisjoint( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes, * pNtkNew; + int fGlobal, fRecursive, fVerbose, fPrint, fShort, c; + + extern Abc_Ntk_t * Abc_NtkDsdGlobal( Abc_Ntk_t * pNtk, bool fVerbose, bool fPrint, bool fShort ); + extern int Abc_NtkDsdLocal( Abc_Ntk_t * pNtk, bool fVerbose, bool fRecursive ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fGlobal = 1; + fRecursive = 0; + fVerbose = 0; + fPrint = 0; + fShort = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "grvpsh" ) ) != EOF ) + { + switch ( c ) + { + case 'g': + fGlobal ^= 1; + break; + case 'r': + fRecursive ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'p': + fPrint ^= 1; + break; + case 's': + fShort ^= 1; + break; + case 'h': + goto usage; + break; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( fGlobal ) + { +// fprintf( stdout, "Performing DSD of global functions of the network.\n" ); + // get the new network + if ( !Abc_NtkIsStrash(pNtk) ) + { + pNtkNew = Abc_NtkStrash( pNtk, 0, 0, 0 ); + pNtkRes = Abc_NtkDsdGlobal( pNtkNew, fVerbose, fPrint, fShort ); + Abc_NtkDelete( pNtkNew ); + } + else + { + pNtkRes = Abc_NtkDsdGlobal( pNtk, fVerbose, fPrint, fShort ); + } + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Global DSD has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + } + else if ( fRecursive ) + { + if ( !Abc_NtkIsBddLogic( pNtk ) ) + { + fprintf( pErr, "This command is only applicable to logic BDD networks.\n" ); + return 1; + } + fprintf( stdout, "Performing recursive DSD and MUX decomposition of local functions.\n" ); + if ( !Abc_NtkDsdLocal( pNtk, fVerbose, fRecursive ) ) + fprintf( pErr, "Recursive DSD has failed.\n" ); + } + else + { + if ( !Abc_NtkIsBddLogic( pNtk ) ) + { + fprintf( pErr, "This command is only applicable to logic BDD networks (run \"bdd\").\n" ); + return 1; + } + fprintf( stdout, "Performing simple non-recursive DSD of local functions.\n" ); + if ( !Abc_NtkDsdLocal( pNtk, fVerbose, fRecursive ) ) + fprintf( pErr, "Simple DSD of local functions has failed.\n" ); + } + return 0; + +usage: + fprintf( pErr, "usage: dsd [-grvpsh]\n" ); + fprintf( pErr, "\t decomposes the network using disjoint-support decomposition\n" ); + fprintf( pErr, "\t-g : toggle DSD of global and local functions [default = %s]\n", fGlobal? "global": "local" ); + fprintf( pErr, "\t-r : toggle recursive DSD/MUX and simple DSD [default = %s]\n", fRecursive? "recursive DSD/MUX": "simple DSD" ); + fprintf( pErr, "\t-v : prints DSD statistics and runtime [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-p : prints DSD structure to the standard output [default = %s]\n", fPrint? "yes": "no" ); + fprintf( pErr, "\t-s : use short PI names when printing DSD structure [default = %s]\n", fShort? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandImfs( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + Res_Par_t Pars, * pPars = &Pars; + int c; + +// printf( "Implementation of this command is not finished.\n" ); +// return 1; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + pPars->nWindow = 62; + pPars->nGrowthLevel = 1; + pPars->nCands = 5; + pPars->nSimWords = 4; + pPars->fArea = 0; + pPars->fVerbose = 0; + pPars->fVeryVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "WSCLavwh" ) ) != EOF ) + { + switch ( c ) + { + case 'W': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-W\" should be followed by an integer.\n" ); + goto usage; + } + pPars->nWindow = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pPars->nWindow < 1 || pPars->nWindow > 99 ) + goto usage; + break; + case 'S': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-S\" should be followed by an integer.\n" ); + goto usage; + } + pPars->nSimWords = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pPars->nSimWords < 1 || pPars->nSimWords > 256 ) + goto usage; + break; + case 'C': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-C\" should be followed by an integer.\n" ); + goto usage; + } + pPars->nCands = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pPars->nCands < 0 || pPars->nCands > ABC_INFINITY ) + goto usage; + break; + case 'L': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-L\" should be followed by an integer.\n" ); + goto usage; + } + pPars->nGrowthLevel = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pPars->nGrowthLevel < 0 || pPars->nGrowthLevel > ABC_INFINITY ) + goto usage; + break; + case 'a': + pPars->fArea ^= 1; + break; + case 'v': + pPars->fVerbose ^= 1; + break; + case 'w': + pPars->fVeryVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsLogic(pNtk) ) + { + fprintf( pErr, "This command can only be applied to a logic network.\n" ); + return 1; + } + + // modify the current network + if ( !Abc_NtkResynthesize( pNtk, pPars ) ) + { + fprintf( pErr, "Resynthesis has failed.\n" ); + return 1; + } + return 0; + +usage: + fprintf( pErr, "usage: imfs [-W ] [-L ] [-C ] [-S ] [-avwh]\n" ); + fprintf( pErr, "\t performs resubstitution-based resynthesis with interpolation\n" ); + fprintf( pErr, "\t-W : fanin/fanout levels (NxM) of the window (00 <= NM <= 99) [default = %d%d]\n", pPars->nWindow/10, pPars->nWindow%10 ); + fprintf( pErr, "\t-L : the largest increase in node level after resynthesis (0 <= num) [default = %d]\n", pPars->nGrowthLevel ); + fprintf( pErr, "\t-C : the max number of resub candidates (1 <= n) [default = %d]\n", pPars->nCands ); + fprintf( pErr, "\t-S : the number of simulation words (1 <= n <= 256) [default = %d]\n", pPars->nSimWords ); + fprintf( pErr, "\t-a : toggle optimization for area only [default = %s]\n", pPars->fArea? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose printout [default = %s]\n", pPars->fVerbose? "yes": "no" ); + fprintf( pErr, "\t-w : toggle printout subgraph statistics [default = %s]\n", pPars->fVeryVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandLutpack( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + Lpk_Par_t Pars, * pPars = &Pars; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + memset( pPars, 0, sizeof(Lpk_Par_t) ); + pPars->nLutsMax = 4; // (N) the maximum number of LUTs in the structure + pPars->nLutsOver = 3; // (Q) the maximum number of LUTs not in the MFFC + pPars->nVarsShared = 0; // (S) the maximum number of shared variables (crossbars) + pPars->nGrowthLevel = 1; // (L) the maximum number of increased levels + pPars->fSatur = 1; + pPars->fZeroCost = 0; + pPars->fFirst = 0; + pPars->fVerbose = 0; + pPars->fVeryVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "NQSLszfvwh" ) ) != EOF ) + { + switch ( c ) + { + case 'N': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-N\" should be followed by an integer.\n" ); + goto usage; + } + pPars->nLutsMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pPars->nLutsMax < 2 || pPars->nLutsMax > 8 ) + goto usage; + break; + case 'Q': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-Q\" should be followed by an integer.\n" ); + goto usage; + } + pPars->nLutsOver = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pPars->nLutsOver < 0 || pPars->nLutsOver > 8 ) + goto usage; + break; + case 'S': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-S\" should be followed by an integer.\n" ); + goto usage; + } + pPars->nVarsShared = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pPars->nVarsShared < 0 || pPars->nVarsShared > 4 ) + goto usage; + break; + case 'L': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-L\" should be followed by an integer.\n" ); + goto usage; + } + pPars->nGrowthLevel = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pPars->nGrowthLevel < 0 || pPars->nGrowthLevel > ABC_INFINITY ) + goto usage; + break; + case 's': + pPars->fSatur ^= 1; + break; + case 'z': + pPars->fZeroCost ^= 1; + break; + case 'f': + pPars->fFirst ^= 1; + break; + case 'v': + pPars->fVerbose ^= 1; + break; + case 'w': + pPars->fVeryVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsLogic(pNtk) ) + { + fprintf( pErr, "This command can only be applied to a logic network.\n" ); + return 1; + } + + // modify the current network + if ( !Lpk_Resynthesize( pNtk, pPars ) ) + { + fprintf( pErr, "Resynthesis has failed.\n" ); + return 1; + } + return 0; + +usage: + fprintf( pErr, "usage: lutpack [-N ] [-Q ] [-S ] [-L ] [-szfvwh]\n" ); + fprintf( pErr, "\t performs \"rewriting\" for LUT networks\n" ); + fprintf( pErr, "\t-N : the max number of LUTs in the structure (2 <= num) [default = %d]\n", pPars->nLutsMax ); + fprintf( pErr, "\t-Q : the max number of LUTs not in MFFC (0 <= num) [default = %d]\n", pPars->nLutsOver ); + fprintf( pErr, "\t-S : the max number of LUT inputs shared (0 <= num) [default = %d]\n", pPars->nVarsShared ); + fprintf( pErr, "\t-L : the largest increase in node level after resynthesis (0 <= num) [default = %d]\n", pPars->nGrowthLevel ); + fprintf( pErr, "\t-s : toggle iteration till saturation [default = %s]\n", pPars->fSatur? "yes": "no" ); + fprintf( pErr, "\t-z : toggle zero-cost replacements [default = %s]\n", pPars->fZeroCost? "yes": "no" ); + fprintf( pErr, "\t-f : toggle using only first node and first cut [default = %s]\n", pPars->fFirst? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose printout [default = %s]\n", pPars->fVerbose? "yes": "no" ); + fprintf( pErr, "\t-w : toggle printout subgraph statistics [default = %s]\n", pPars->fVeryVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandRewrite( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + bool fUpdateLevel; + bool fPrecompute; + bool fUseZeros; + bool fVerbose; + bool fVeryVerbose; + bool fPlaceEnable; + // external functions + extern void Rwr_Precompute(); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fUpdateLevel = 1; + fPrecompute = 0; + fUseZeros = 0; + fVerbose = 0; + fVeryVerbose = 0; + fPlaceEnable = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "lxzvwh" ) ) != EOF ) + { + switch ( c ) + { + case 'l': + fUpdateLevel ^= 1; + break; + case 'x': + fPrecompute ^= 1; + break; + case 'z': + fUseZeros ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'w': + fVeryVerbose ^= 1; + break; + case 'p': + fPlaceEnable ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( fPrecompute ) + { + Rwr_Precompute(); + return 0; + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "This command can only be applied to an AIG (run \"strash\").\n" ); + return 1; + } + if ( Abc_NtkGetChoiceNum(pNtk) ) + { + fprintf( pErr, "AIG resynthesis cannot be applied to AIGs with choice nodes.\n" ); + return 1; + } + + // modify the current network + if ( !Abc_NtkRewrite( pNtk, fUpdateLevel, fUseZeros, fVerbose, fVeryVerbose, fPlaceEnable ) ) + { + fprintf( pErr, "Rewriting has failed.\n" ); + return 1; + } + return 0; + +usage: + fprintf( pErr, "usage: rewrite [-lzvwh]\n" ); + fprintf( pErr, "\t performs technology-independent rewriting of the AIG\n" ); + fprintf( pErr, "\t-l : toggle preserving the number of levels [default = %s]\n", fUpdateLevel? "yes": "no" ); + fprintf( pErr, "\t-z : toggle using zero-cost replacements [default = %s]\n", fUseZeros? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose printout [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-w : toggle printout subgraph statistics [default = %s]\n", fVeryVerbose? "yes": "no" ); +// fprintf( pErr, "\t-p : toggle placement-aware rewriting [default = %s]\n", fPlaceEnable? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandRefactor( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int nNodeSizeMax; + int nConeSizeMax; + bool fUpdateLevel; + bool fUseZeros; + bool fUseDcs; + bool fVerbose; + extern int Abc_NtkRefactor( Abc_Ntk_t * pNtk, int nNodeSizeMax, int nConeSizeMax, bool fUpdateLevel, bool fUseZeros, bool fUseDcs, bool fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nNodeSizeMax = 10; + nConeSizeMax = 16; + fUpdateLevel = 1; + fUseZeros = 0; + fUseDcs = 0; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "NClzdvh" ) ) != EOF ) + { + switch ( c ) + { + case 'N': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-N\" should be followed by an integer.\n" ); + goto usage; + } + nNodeSizeMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nNodeSizeMax < 0 ) + goto usage; + break; + case 'C': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-C\" should be followed by an integer.\n" ); + goto usage; + } + nConeSizeMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nConeSizeMax < 0 ) + goto usage; + break; + case 'l': + fUpdateLevel ^= 1; + break; + case 'z': + fUseZeros ^= 1; + break; + case 'd': + fUseDcs ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "This command can only be applied to an AIG (run \"strash\").\n" ); + return 1; + } + if ( Abc_NtkGetChoiceNum(pNtk) ) + { + fprintf( pErr, "AIG resynthesis cannot be applied to AIGs with choice nodes.\n" ); + return 1; + } + + if ( fUseDcs && nNodeSizeMax >= nConeSizeMax ) + { + fprintf( pErr, "For don't-care to work, containing cone should be larger than collapsed node.\n" ); + return 1; + } + + // modify the current network + if ( !Abc_NtkRefactor( pNtk, nNodeSizeMax, nConeSizeMax, fUpdateLevel, fUseZeros, fUseDcs, fVerbose ) ) + { + fprintf( pErr, "Refactoring has failed.\n" ); + return 1; + } + return 0; + +usage: + fprintf( pErr, "usage: refactor [-N num] [-C num] [-lzdvh]\n" ); + fprintf( pErr, "\t performs technology-independent refactoring of the AIG\n" ); + fprintf( pErr, "\t-N num : the max support of the collapsed node [default = %d]\n", nNodeSizeMax ); + fprintf( pErr, "\t-C num : the max support of the containing cone [default = %d]\n", nConeSizeMax ); + fprintf( pErr, "\t-l : toggle preserving the number of levels [default = %s]\n", fUpdateLevel? "yes": "no" ); + fprintf( pErr, "\t-z : toggle using zero-cost replacements [default = %s]\n", fUseZeros? "yes": "no" ); + fprintf( pErr, "\t-d : toggle using don't-cares [default = %s]\n", fUseDcs? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose printout [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandRestructure( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int nCutsMax; + bool fUpdateLevel; + bool fUseZeros; + bool fVerbose; + extern int Abc_NtkRestructure( Abc_Ntk_t * pNtk, int nCutsMax, bool fUpdateLevel, bool fUseZeros, bool fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nCutsMax = 5; + fUpdateLevel = 0; + fUseZeros = 0; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Klzvh" ) ) != EOF ) + { + switch ( c ) + { + case 'K': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-K\" should be followed by an integer.\n" ); + goto usage; + } + nCutsMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nCutsMax < 0 ) + goto usage; + break; + case 'l': + fUpdateLevel ^= 1; + break; + case 'z': + fUseZeros ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( nCutsMax < 4 || nCutsMax > CUT_SIZE_MAX ) + { + fprintf( pErr, "Can only compute the cuts for %d <= K <= %d.\n", 4, CUT_SIZE_MAX ); + return 1; + } + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "This command can only be applied to an AIG (run \"strash\").\n" ); + return 1; + } + if ( Abc_NtkGetChoiceNum(pNtk) ) + { + fprintf( pErr, "AIG resynthesis cannot be applied to AIGs with choice nodes.\n" ); + return 1; + } + + // modify the current network + if ( !Abc_NtkRestructure( pNtk, nCutsMax, fUpdateLevel, fUseZeros, fVerbose ) ) + { + fprintf( pErr, "Refactoring has failed.\n" ); + return 1; + } + return 0; + +usage: + fprintf( pErr, "usage: restructure [-K num] [-lzvh]\n" ); + fprintf( pErr, "\t performs technology-independent restructuring of the AIG\n" ); + fprintf( pErr, "\t-K num : the max cut size (%d <= num <= %d) [default = %d]\n", CUT_SIZE_MIN, CUT_SIZE_MAX, nCutsMax ); + fprintf( pErr, "\t-l : toggle preserving the number of levels [default = %s]\n", fUpdateLevel? "yes": "no" ); + fprintf( pErr, "\t-z : toggle using zero-cost replacements [default = %s]\n", fUseZeros? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose printout [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandResubstitute( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int RS_CUT_MIN = 4; + int RS_CUT_MAX = 16; + int c; + int nCutsMax; + int nNodesMax; + int nLevelsOdc; + bool fUpdateLevel; + bool fUseZeros; + bool fVerbose; + bool fVeryVerbose; + extern int Abc_NtkResubstitute( Abc_Ntk_t * pNtk, int nCutsMax, int nNodesMax, int nLevelsOdc, bool fUpdateLevel, bool fVerbose, bool fVeryVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nCutsMax = 8; + nNodesMax = 1; + nLevelsOdc = 0; + fUpdateLevel = 1; + fUseZeros = 0; + fVerbose = 0; + fVeryVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "KNFlzvwh" ) ) != EOF ) + { + switch ( c ) + { + case 'K': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-K\" should be followed by an integer.\n" ); + goto usage; + } + nCutsMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nCutsMax < 0 ) + goto usage; + break; + case 'N': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-N\" should be followed by an integer.\n" ); + goto usage; + } + nNodesMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nNodesMax < 0 ) + goto usage; + break; + case 'F': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-F\" should be followed by an integer.\n" ); + goto usage; + } + nLevelsOdc = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nLevelsOdc < 0 ) + goto usage; + break; + case 'l': + fUpdateLevel ^= 1; + break; + case 'z': + fUseZeros ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'w': + fVeryVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( nCutsMax < RS_CUT_MIN || nCutsMax > RS_CUT_MAX ) + { + fprintf( pErr, "Can only compute cuts for %d <= K <= %d.\n", RS_CUT_MIN, RS_CUT_MAX ); + return 1; + } + if ( nNodesMax < 0 || nNodesMax > 3 ) + { + fprintf( pErr, "Can only resubstitute at most 3 nodes.\n" ); + return 1; + } + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "This command can only be applied to an AIG (run \"strash\").\n" ); + return 1; + } + if ( Abc_NtkGetChoiceNum(pNtk) ) + { + fprintf( pErr, "AIG resynthesis cannot be applied to AIGs with choice nodes.\n" ); + return 1; + } + + // modify the current network + if ( !Abc_NtkResubstitute( pNtk, nCutsMax, nNodesMax, nLevelsOdc, fUpdateLevel, fVerbose, fVeryVerbose ) ) + { + fprintf( pErr, "Refactoring has failed.\n" ); + return 1; + } + return 0; + +usage: + fprintf( pErr, "usage: resub [-K num] [-N num] [-F num] [-lzvwh]\n" ); + fprintf( pErr, "\t performs technology-independent restructuring of the AIG\n" ); + fprintf( pErr, "\t-K num : the max cut size (%d <= num <= %d) [default = %d]\n", RS_CUT_MIN, RS_CUT_MAX, nCutsMax ); + fprintf( pErr, "\t-N num : the max number of nodes to add (0 <= num <= 3) [default = %d]\n", nNodesMax ); + fprintf( pErr, "\t-F num : the number of fanout levels for ODC computation [default = %d]\n", nLevelsOdc ); + fprintf( pErr, "\t-l : toggle preserving the number of levels [default = %s]\n", fUpdateLevel? "yes": "no" ); + fprintf( pErr, "\t-z : toggle using zero-cost replacements [default = %s]\n", fUseZeros? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose printout [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-w : toggle verbose printout of ODC computation [default = %s]\n", fVeryVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandRr( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c, Window; + int nFaninLevels; + int nFanoutLevels; + int fUseFanouts; + int fVerbose; + extern int Abc_NtkRR( Abc_Ntk_t * pNtk, int nFaninLevels, int nFanoutLevels, int fUseFanouts, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nFaninLevels = 3; + nFanoutLevels = 3; + fUseFanouts = 0; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Wfvh" ) ) != EOF ) + { + switch ( c ) + { + case 'W': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-W\" should be followed by an integer.\n" ); + goto usage; + } + Window = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( Window < 0 ) + goto usage; + nFaninLevels = Window / 10; + nFanoutLevels = Window % 10; + break; + case 'f': + fUseFanouts ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "This command can only be applied to an AIG (run \"strash\").\n" ); + return 1; + } + if ( Abc_NtkGetChoiceNum(pNtk) ) + { + fprintf( pErr, "AIG resynthesis cannot be applied to AIGs with choice nodes.\n" ); + return 1; + } + + // modify the current network + if ( !Abc_NtkRR( pNtk, nFaninLevels, nFanoutLevels, fUseFanouts, fVerbose ) ) + { + fprintf( pErr, "Redundancy removal has failed.\n" ); + return 1; + } + return 0; + +usage: + fprintf( pErr, "usage: rr [-W NM] [-fvh]\n" ); + fprintf( pErr, "\t removes combinational redundancies in the current network\n" ); + fprintf( pErr, "\t-W NM : window size: TFI (N) and TFO (M) logic levels [default = %d%d]\n", nFaninLevels, nFanoutLevels ); + fprintf( pErr, "\t-f : toggle RR w.r.t. fanouts [default = %s]\n", fUseFanouts? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose printout [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandCascade( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c, nLutSize; + int fCheck; + int fVerbose; + extern Abc_Ntk_t * Abc_NtkCascade( Abc_Ntk_t * pNtk, int nLutSize, int fCheck, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nLutSize = 12; + fCheck = 0; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Kcvh" ) ) != EOF ) + { + switch ( c ) + { + case 'K': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-K\" should be followed by an integer.\n" ); + goto usage; + } + nLutSize = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nLutSize < 0 ) + goto usage; + break; + case 'c': + fCheck ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( !Abc_NtkIsLogic(pNtk) && !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "Can only collapse a logic network or an AIG.\n" ); + return 1; + } + + // get the new network + if ( Abc_NtkIsStrash(pNtk) ) + pNtkRes = Abc_NtkCascade( pNtk, nLutSize, fCheck, fVerbose ); + else + { + pNtk = Abc_NtkStrash( pNtk, 0, 0, 0 ); + pNtkRes = Abc_NtkCascade( pNtk, nLutSize, fCheck, fVerbose ); + Abc_NtkDelete( pNtk ); + } + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Cascade synthesis has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: cascade [-K ] [-cvh]\n" ); + fprintf( pErr, "\t performs LUT cascade synthesis for the current network\n" ); + fprintf( pErr, "\t-K num : the number of LUT inputs [default = %d]\n", nLutSize ); + fprintf( pErr, "\t-c : check equivalence after synthesis [default = %s]\n", fCheck? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose printout [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + fprintf( pErr, "\t \n"); + fprintf( pErr, " A lookup-table cascade is a programmable architecture developed by\n"); + fprintf( pErr, " Professor Tsutomu Sasao (sasao@cse.kyutech.ac.jp) at Kyushu Institute\n"); + fprintf( pErr, " of Technology. This work received Takeda Techno-Entrepreneurship Award:\n"); + fprintf( pErr, " http://www.lsi-cad.com/sasao/photo/takeda.html\n"); + fprintf( pErr, "\t \n"); + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandLogic( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( !Abc_NtkIsStrash( pNtk ) ) + { + fprintf( pErr, "This command is only applicable to strashed networks.\n" ); + return 1; + } + + // get the new network + pNtkRes = Abc_NtkToLogic( pNtk ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Converting to a logic network has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: logic [-h]\n" ); + fprintf( pErr, "\t transforms an AIG into a logic network with SOPs\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandComb( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( Abc_NtkIsComb(pNtk) ) + { + fprintf( pErr, "The network is already combinational.\n" ); + return 0; + } + + // get the new network + pNtkRes = Abc_NtkDup( pNtk ); + Abc_NtkMakeComb( pNtkRes ); + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: comb [-h]\n" ); + fprintf( pErr, "\t makes the current network combinational by replacing latches by PI/PO pairs\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandMiter( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + char Buffer[32]; + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtk1, * pNtk2, * pNtkRes; + int fDelete1, fDelete2; + char ** pArgvNew; + int nArgcNew; + int c; + int fCheck; + int fComb; + int nPartSize; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fComb = 1; + fCheck = 1; + nPartSize = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Pch" ) ) != EOF ) + { + switch ( c ) + { + case 'P': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-P\" should be followed by an integer.\n" ); + goto usage; + } + nPartSize = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nPartSize < 0 ) + goto usage; + break; + case 'c': + fComb ^= 1; + break; + default: + goto usage; + } + } + + pArgvNew = argv + globalUtilOptind; + nArgcNew = argc - globalUtilOptind; + if ( !Abc_NtkPrepareTwoNtks( pErr, pNtk, pArgvNew, nArgcNew, &pNtk1, &pNtk2, &fDelete1, &fDelete2 ) ) + return 1; + + // compute the miter + pNtkRes = Abc_NtkMiter( pNtk1, pNtk2, fComb, nPartSize ); + if ( fDelete1 ) Abc_NtkDelete( pNtk1 ); + if ( fDelete2 ) Abc_NtkDelete( pNtk2 ); + + // get the new network + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Miter computation has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + if ( nPartSize == 0 ) + strcpy( Buffer, "unused" ); + else + sprintf( Buffer, "%d", nPartSize ); + fprintf( pErr, "usage: miter [-P num] [-ch] \n" ); + fprintf( pErr, "\t computes the miter of the two circuits\n" ); + fprintf( pErr, "\t-P num : output partition size [default = %s]\n", Buffer ); + fprintf( pErr, "\t-c : computes combinational miter (latches as POs) [default = %s]\n", fComb? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + fprintf( pErr, "\tfile1 : (optional) the file with the first network\n"); + fprintf( pErr, "\tfile2 : (optional) the file with the second network\n"); + fprintf( pErr, "\t if no files are given, uses the current network and its spec\n"); + fprintf( pErr, "\t if one file is given, uses the current network and the file\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandDemiter( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk;//, * pNtkRes; + int fComb; + int c; + extern int Abc_NtkDemiter( Abc_Ntk_t * pNtk ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "ch" ) ) != EOF ) + { + switch ( c ) + { + case 'c': + fComb ^= 1; + break; + default: + goto usage; + } + } + + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "The network is not strashed.\n" ); + return 1; + } + + if ( Abc_NtkPoNum(pNtk) != 1 ) + { + fprintf( pErr, "The network is not a miter.\n" ); + return 1; + } + + if ( !Abc_NodeIsExorType(Abc_ObjFanin0(Abc_NtkPo(pNtk,0))) ) + { + fprintf( pErr, "The miter's PO is not an EXOR.\n" ); + return 1; + } + + // get the new network + if ( !Abc_NtkDemiter( pNtk ) ) + { + fprintf( pErr, "Demitering has failed.\n" ); + return 1; + } + // replace the current network +// Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: demiter [-h]\n" ); + fprintf( pErr, "\t removes topmost EXOR from the miter to create two POs\n" ); +// fprintf( pErr, "\t-c : computes combinational miter (latches as POs) [default = %s]\n", fComb? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandOrPos( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk;//, * pNtkRes; + int fComb; + int c; + extern int Abc_NtkCombinePos( Abc_Ntk_t * pNtk, int fAnd ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "ch" ) ) != EOF ) + { + switch ( c ) + { + case 'c': + fComb ^= 1; + break; + default: + goto usage; + } + } + + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "The network is not strashed.\n" ); + return 1; + } +/* + if ( Abc_NtkPoNum(pNtk) == 1 ) + { + fprintf( pErr, "The network already has one PO.\n" ); + return 1; + } +*/ +/* + if ( Abc_NtkLatchNum(pNtk) ) + { + fprintf( pErr, "The miter has latches. ORing is not performed.\n" ); + return 1; + } +*/ + // get the new network + if ( !Abc_NtkCombinePos( pNtk, 0 ) ) + { + fprintf( pErr, "ORing the POs has failed.\n" ); + return 1; + } + // replace the current network +// Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: orpos [-h]\n" ); + fprintf( pErr, "\t creates single-output miter by ORing the POs of the current network\n" ); +// fprintf( pErr, "\t-c : computes combinational miter (latches as POs) [default = %s]\n", fComb? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandAndPos( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk;//, * pNtkRes; + int fComb; + int c; + extern int Abc_NtkCombinePos( Abc_Ntk_t * pNtk, int fAnd ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "ch" ) ) != EOF ) + { + switch ( c ) + { + case 'c': + fComb ^= 1; + break; + default: + goto usage; + } + } + + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "The network is not strashed.\n" ); + return 1; + } + + if ( Abc_NtkPoNum(pNtk) == 1 ) + { + fprintf( pErr, "The network already has one PO.\n" ); + return 1; + } + + if ( Abc_NtkLatchNum(pNtk) ) + { + fprintf( pErr, "The miter has latches. ORing is not performed.\n" ); + return 1; + } + + // get the new network + if ( !Abc_NtkCombinePos( pNtk, 1 ) ) + { + fprintf( pErr, "ANDing the POs has failed.\n" ); + return 1; + } + // replace the current network +// Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: andpos [-h]\n" ); + fprintf( pErr, "\t creates single-output miter by ANDing the POs of the current network\n" ); +// fprintf( pErr, "\t-c : computes combinational miter (latches as POs) [default = %s]\n", fComb? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandAppend( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtk2; + char * FileName; + int fComb; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "ch" ) ) != EOF ) + { + switch ( c ) + { + case 'c': + fComb ^= 1; + break; + default: + goto usage; + } + } + + // get the second network + if ( argc != globalUtilOptind + 1 ) + { + fprintf( pErr, "The network to append is not given.\n" ); + return 1; + } + + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "The base network should be strashed for the appending to work.\n" ); + return 1; + } + + // read the second network + FileName = argv[globalUtilOptind]; + pNtk2 = Io_Read( FileName, Io_ReadFileType(FileName), 1 ); + if ( pNtk2 == NULL ) + return 1; + + // check if the second network is combinational + if ( Abc_NtkLatchNum(pNtk2) ) + { + fprintf( pErr, "The second network has latches. Appending does not work for such networks.\n" ); + return 1; + } + + // get the new network + if ( !Abc_NtkAppend( pNtk, pNtk2, 1 ) ) + { + Abc_NtkDelete( pNtk2 ); + fprintf( pErr, "Appending the networks failed.\n" ); + return 1; + } + Abc_NtkDelete( pNtk2 ); + // sweep dangling logic + Abc_AigCleanup( pNtk->pManFunc ); + // replace the current network +// Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: append [-h] \n" ); + fprintf( pErr, "\t appends a combinational network on top of the current network\n" ); +// fprintf( pErr, "\t-c : computes combinational miter (latches as POs) [default = %s]\n", fComb? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + fprintf( pErr, "\t : file name with the second network\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandFrames( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkTemp, * pNtkRes; + int fInitial; + int nFrames; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fInitial = 0; + nFrames = 5; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Fih" ) ) != EOF ) + { + switch ( c ) + { + case 'F': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-F\" should be followed by an integer.\n" ); + goto usage; + } + nFrames = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nFrames <= 0 ) + goto usage; + break; + case 'i': + fInitial ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + // get the new network + if ( !Abc_NtkIsStrash(pNtk) ) + { + pNtkTemp = Abc_NtkStrash( pNtk, 0, 0, 0 ); + pNtkRes = Abc_NtkFrames( pNtkTemp, nFrames, fInitial ); + Abc_NtkDelete( pNtkTemp ); + } + else + pNtkRes = Abc_NtkFrames( pNtk, nFrames, fInitial ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Unrolling the network has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: frames [-F num] [-ih]\n" ); + fprintf( pErr, "\t unrolls the network for a number of time frames\n" ); + fprintf( pErr, "\t-F num : the number of frames to unroll [default = %d]\n", nFrames ); + fprintf( pErr, "\t-i : toggles initializing the first frame [default = %s]\n", fInitial? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandSop( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int fDirect; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fDirect = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "dh" ) ) != EOF ) + { + switch ( c ) + { + case 'd': + fDirect ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsLogic(pNtk) ) + { + fprintf( pErr, "Converting to SOP is possible only for logic networks.\n" ); + return 1; + } + if ( !Abc_NtkToSop(pNtk, fDirect) ) + { + fprintf( pErr, "Converting to SOP has failed.\n" ); + return 1; + } + return 0; + +usage: + fprintf( pErr, "usage: sop [-dh]\n" ); + fprintf( pErr, "\t converts node functions to SOP\n" ); + fprintf( pErr, "\t-d : toggles using both phases or only positive [default = %s]\n", fDirect? "direct": "both" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandBdd( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsLogic(pNtk) ) + { + fprintf( pErr, "Converting to BDD is possible only for logic networks.\n" ); + return 1; + } + if ( Abc_NtkIsBddLogic(pNtk) ) + { + fprintf( pOut, "The logic network is already in the BDD form.\n" ); + return 0; + } + if ( !Abc_NtkToBdd(pNtk) ) + { + fprintf( pErr, "Converting to BDD has failed.\n" ); + return 1; + } + return 0; + +usage: + fprintf( pErr, "usage: bdd [-h]\n" ); + fprintf( pErr, "\t converts node functions to BDD\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandAig( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsLogic(pNtk) ) + { + fprintf( pErr, "Converting to AIG is possible only for logic networks.\n" ); + return 1; + } + if ( Abc_NtkIsAigLogic(pNtk) ) + { + fprintf( pOut, "The logic network is already in the AIG form.\n" ); + return 0; + } + if ( !Abc_NtkToAig(pNtk) ) + { + fprintf( pErr, "Converting to AIG has failed.\n" ); + return 1; + } + return 0; + +usage: + fprintf( pErr, "usage: aig [-h]\n" ); + fprintf( pErr, "\t converts node functions to AIG\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandReorder( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int fVerbose; + extern void Abc_NtkBddReorder( Abc_Ntk_t * pNtk, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "vh" ) ) != EOF ) + { + switch ( c ) + { + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + // get the new network + if ( !Abc_NtkIsBddLogic(pNtk) ) + { + fprintf( pErr, "Variable reordering is possible when node functions are BDDs (run \"bdd\").\n" ); + return 1; + } + Abc_NtkBddReorder( pNtk, fVerbose ); + return 0; + +usage: + fprintf( pErr, "usage: reorder [-vh]\n" ); + fprintf( pErr, "\t reorders local functions of the nodes using sifting\n" ); + fprintf( pErr, "\t-v : prints verbose information [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandOrder( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr, * pFile; + Abc_Ntk_t * pNtk; + char * pFileName; + int c; + int fReverse; + int fVerbose; + extern void Abc_NtkImplementCiOrder( Abc_Ntk_t * pNtk, char * pFileName, int fReverse, int fVerbose ); + extern void Abc_NtkFindCiOrder( Abc_Ntk_t * pNtk, int fReverse, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fReverse = 0; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "rvh" ) ) != EOF ) + { + switch ( c ) + { + case 'r': + fReverse ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } +// if ( Abc_NtkLatchNum(pNtk) > 0 ) +// { +// printf( "Currently this procedure does not work for sequential networks.\n" ); +// return 1; +// } + + // if the var order file is given, implement this order + pFileName = NULL; + if ( argc == globalUtilOptind + 1 ) + { + pFileName = argv[globalUtilOptind]; + pFile = fopen( pFileName, "r" ); + if ( pFile == NULL ) + { + fprintf( pErr, "Cannot open file \"%s\" with the BDD variable order.\n", pFileName ); + return 1; + } + fclose( pFile ); + } + if ( pFileName ) + Abc_NtkImplementCiOrder( pNtk, pFileName, fReverse, fVerbose ); + else + Abc_NtkFindCiOrder( pNtk, fReverse, fVerbose ); + return 0; + +usage: + fprintf( pErr, "usage: order [-rvh] \n" ); + fprintf( pErr, "\t computes a good static CI variable order\n" ); + fprintf( pErr, "\t-r : toggle reverse ordering [default = %s]\n", fReverse? "yes": "no" ); + fprintf( pErr, "\t-v : prints verbose information [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + fprintf( pErr, "\t : (optional) file with the given variable order\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandMuxes( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( !Abc_NtkIsBddLogic(pNtk) ) + { + fprintf( pErr, "Only a BDD logic network can be converted to MUXes.\n" ); + return 1; + } + + // get the new network + pNtkRes = Abc_NtkBddToMuxes( pNtk ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Converting to MUXes has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: muxes [-h]\n" ); + fprintf( pErr, "\t converts the current network by a network derived by\n" ); + fprintf( pErr, "\t replacing all nodes by DAGs isomorphic to the local BDDs\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandExtSeqDcs( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int fVerbose; + extern int Abc_NtkExtractSequentialDcs( Abc_Ntk_t * pNet, bool fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "vh" ) ) != EOF ) + { + switch ( c ) + { + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( Abc_NtkLatchNum(pNtk) == 0 ) + { + fprintf( stdout, "The current network has no latches.\n" ); + return 0; + } + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( stdout, "Extracting sequential don't-cares works only for AIGs (run \"strash\").\n" ); + return 0; + } + if ( !Abc_NtkExtractSequentialDcs( pNtk, fVerbose ) ) + { + fprintf( stdout, "Extracting sequential don't-cares has failed.\n" ); + return 1; + } + return 0; + +usage: + fprintf( pErr, "usage: ext_seq_dcs [-vh]\n" ); + fprintf( pErr, "\t create EXDC network using unreachable states\n" ); + fprintf( pErr, "\t-v : prints verbose information [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandCone( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + Abc_Obj_t * pNode, * pNodeCo; + int c; + int fUseAllCis; + int fUseMffc; + int Output; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fUseAllCis = 0; + fUseMffc = 0; + Output = -1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Omah" ) ) != EOF ) + { + switch ( c ) + { + case 'O': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-O\" should be followed by an integer.\n" ); + goto usage; + } + Output = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( Output < 0 ) + goto usage; + break; + case 'm': + fUseMffc ^= 1; + case 'a': + fUseAllCis ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( !Abc_NtkIsLogic(pNtk) && !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "Currently can only be applied to the logic network or an AIG.\n" ); + return 1; + } + + if ( argc > globalUtilOptind + 1 ) + { + fprintf( pErr, "Wrong number of auguments.\n" ); + goto usage; + } + + pNodeCo = NULL; + if ( argc == globalUtilOptind + 1 ) + { + pNode = Abc_NtkFindNode( pNtk, argv[globalUtilOptind] ); + if ( pNode == NULL ) + { + fprintf( pErr, "Cannot find node \"%s\".\n", argv[globalUtilOptind] ); + return 1; + } + if ( fUseMffc ) + pNtkRes = Abc_NtkCreateMffc( pNtk, pNode, argv[globalUtilOptind] ); + else + pNtkRes = Abc_NtkCreateCone( pNtk, pNode, argv[globalUtilOptind], fUseAllCis ); + } + else + { + if ( Output == -1 ) + { + fprintf( pErr, "The node is not specified.\n" ); + return 1; + } + if ( Output >= Abc_NtkCoNum(pNtk) ) + { + fprintf( pErr, "The 0-based output number (%d) is larger than the number of outputs (%d).\n", Output, Abc_NtkCoNum(pNtk) ); + return 1; + } + pNodeCo = Abc_NtkCo( pNtk, Output ); + if ( fUseMffc ) + pNtkRes = Abc_NtkCreateMffc( pNtk, Abc_ObjFanin0(pNodeCo), Abc_ObjName(pNodeCo) ); + else + pNtkRes = Abc_NtkCreateCone( pNtk, Abc_ObjFanin0(pNodeCo), Abc_ObjName(pNodeCo), fUseAllCis ); + } + if ( pNodeCo && Abc_ObjFaninC0(pNodeCo) ) + printf( "The extracted cone represents the complement function of the CO.\n" ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Writing the logic cone of one node has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: cone [-O num] [-amh] \n" ); + fprintf( pErr, "\t replaces the current network by one logic cone\n" ); + fprintf( pErr, "\t-a : toggle writing all CIs or structral support only [default = %s]\n", fUseAllCis? "all": "structural" ); + fprintf( pErr, "\t-m : toggle writing only MFFC or complete TFI cone [default = %s]\n", fUseMffc? "MFFC": "TFI cone" ); + fprintf( pErr, "\t-h : print the command usage\n"); + fprintf( pErr, "\t-O num : (optional) the 0-based number of the CO to extract\n"); + fprintf( pErr, "\tname : (optional) the name of the node to extract\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandNode( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + Abc_Obj_t * pNode; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( !Abc_NtkIsLogic(pNtk) ) + { + fprintf( pErr, "Currently can only be applied to a logic network.\n" ); + return 1; + } + + if ( argc != globalUtilOptind + 1 ) + { + fprintf( pErr, "Wrong number of auguments.\n" ); + goto usage; + } + + pNode = Abc_NtkFindNode( pNtk, argv[globalUtilOptind] ); + if ( pNode == NULL ) + { + fprintf( pErr, "Cannot find node \"%s\".\n", argv[globalUtilOptind] ); + return 1; + } + + pNtkRes = Abc_NtkCreateFromNode( pNtk, pNode ); +// pNtkRes = Abc_NtkDeriveFromBdd( pNtk->pManFunc, pNode->pData, NULL, NULL ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Splitting one node has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: node [-h] \n" ); + fprintf( pErr, "\t replaces the current network by the network composed of one node\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + fprintf( pErr, "\tname : the node name\n"); + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandTopmost( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c, nLevels; + extern Abc_Ntk_t * Abc_NtkTopmost( Abc_Ntk_t * pNtk, int nLevels ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nLevels = 10; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Nh" ) ) != EOF ) + { + switch ( c ) + { + case 'N': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-N\" should be followed by an integer.\n" ); + goto usage; + } + nLevels = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nLevels < 0 ) + goto usage; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( stdout, "Currently only works for structurally hashed circuits.\n" ); + return 0; + } + + if ( Abc_NtkLatchNum(pNtk) > 0 ) + { + fprintf( stdout, "Currently can only works for combinational circuits.\n" ); + return 0; + } + if ( Abc_NtkPoNum(pNtk) != 1 ) + { + fprintf( stdout, "Currently expects a single-output miter.\n" ); + return 0; + } + + pNtkRes = Abc_NtkTopmost( pNtk, nLevels ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "The command has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: topmost [-N num] [-h]\n" ); + fprintf( pErr, "\t replaces the current network by several of its topmost levels\n" ); + fprintf( pErr, "\t-N num : max number of levels [default = %d]\n", nLevels ); + fprintf( pErr, "\t-h : print the command usage\n"); + fprintf( pErr, "\tname : the node name\n"); + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandShortNames( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + Abc_NtkShortNames( pNtk ); + return 0; + +usage: + fprintf( pErr, "usage: short_names [-h]\n" ); + fprintf( pErr, "\t replaces PI/PO/latch names by short char strings\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandExdcFree( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( pNtk->pExdc == NULL ) + { + fprintf( pErr, "The network has no EXDC.\n" ); + return 1; + } + + Abc_NtkDelete( pNtk->pExdc ); + pNtk->pExdc = NULL; + + // replace the current network + pNtkRes = Abc_NtkDup( pNtk ); + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: exdc_free [-h]\n" ); + fprintf( pErr, "\t frees the EXDC network of the current network\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandExdcGet( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( pNtk->pExdc == NULL ) + { + fprintf( pErr, "The network has no EXDC.\n" ); + return 1; + } + + // replace the current network + pNtkRes = Abc_NtkDup( pNtk->pExdc ); + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: exdc_get [-h]\n" ); + fprintf( pErr, "\t replaces the current network by the EXDC of the current network\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandExdcSet( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr, * pFile; + Abc_Ntk_t * pNtk, * pNtkNew, * pNtkRes; + char * FileName; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( argc != globalUtilOptind + 1 ) + { + goto usage; + } + + // get the input file name + FileName = argv[globalUtilOptind]; + if ( (pFile = fopen( FileName, "r" )) == NULL ) + { + fprintf( pAbc->Err, "Cannot open input file \"%s\". ", FileName ); + if ( FileName = Extra_FileGetSimilarName( FileName, ".mv", ".blif", ".pla", ".eqn", ".bench" ) ) + fprintf( pAbc->Err, "Did you mean \"%s\"?", FileName ); + fprintf( pAbc->Err, "\n" ); + return 1; + } + fclose( pFile ); + + // set the new network + pNtkNew = Io_Read( FileName, Io_ReadFileType(FileName), 1 ); + if ( pNtkNew == NULL ) + { + fprintf( pAbc->Err, "Reading network from file has failed.\n" ); + return 1; + } + + // replace the EXDC + if ( pNtk->pExdc ) + { + Abc_NtkDelete( pNtk->pExdc ); + pNtk->pExdc = NULL; + } + pNtkRes = Abc_NtkDup( pNtk ); + pNtkRes->pExdc = pNtkNew; + + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: exdc_set [-h] \n" ); + fprintf( pErr, "\t sets the network from file as EXDC for the current network\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + fprintf( pErr, "\t : file with the new EXDC network\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandCut( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Cut_Params_t Params, * pParams = &Params; + Cut_Man_t * pCutMan; + Cut_Oracle_t * pCutOracle; + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int fOracle; + extern Cut_Man_t * Abc_NtkCuts( Abc_Ntk_t * pNtk, Cut_Params_t * pParams ); + extern void Abc_NtkCutsOracle( Abc_Ntk_t * pNtk, Cut_Oracle_t * pCutOracle ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fOracle = 0; + memset( pParams, 0, sizeof(Cut_Params_t) ); + pParams->nVarsMax = 5; // the max cut size ("k" of the k-feasible cuts) + pParams->nKeepMax = 1000; // the max number of cuts kept at a node + pParams->fTruth = 0; // compute truth tables + pParams->fFilter = 1; // filter dominated cuts + pParams->fDrop = 0; // drop cuts on the fly + pParams->fDag = 0; // compute DAG cuts + pParams->fTree = 0; // compute tree cuts + pParams->fGlobal = 0; // compute global cuts + pParams->fLocal = 0; // compute local cuts + pParams->fFancy = 0; // compute something fancy + pParams->fMap = 0; // compute mapping delay + pParams->fVerbose = 0; // the verbosiness flag + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "KMtfdxyglzmvoh" ) ) != EOF ) + { + switch ( c ) + { + case 'K': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-K\" should be followed by an integer.\n" ); + goto usage; + } + pParams->nVarsMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pParams->nVarsMax < 0 ) + goto usage; + break; + case 'M': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-M\" should be followed by an integer.\n" ); + goto usage; + } + pParams->nKeepMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pParams->nKeepMax < 0 ) + goto usage; + break; + case 't': + pParams->fTruth ^= 1; + break; + case 'f': + pParams->fFilter ^= 1; + break; + case 'd': + pParams->fDrop ^= 1; + break; + case 'x': + pParams->fDag ^= 1; + break; + case 'y': + pParams->fTree ^= 1; + break; + case 'g': + pParams->fGlobal ^= 1; + break; + case 'l': + pParams->fLocal ^= 1; + break; + case 'z': + pParams->fFancy ^= 1; + break; + case 'm': + pParams->fMap ^= 1; + break; + case 'v': + pParams->fVerbose ^= 1; + break; + case 'o': + fOracle ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "Cut computation is available only for AIGs (run \"strash\").\n" ); + return 1; + } + if ( pParams->nVarsMax < CUT_SIZE_MIN || pParams->nVarsMax > CUT_SIZE_MAX ) + { + fprintf( pErr, "Can only compute the cuts for %d <= K <= %d.\n", CUT_SIZE_MIN, CUT_SIZE_MAX ); + return 1; + } + if ( pParams->fDag && pParams->fTree ) + { + fprintf( pErr, "Cannot compute both DAG cuts and tree cuts at the same time.\n" ); + return 1; + } + + if ( fOracle ) + pParams->fRecord = 1; + pCutMan = Abc_NtkCuts( pNtk, pParams ); + if ( fOracle ) + pCutOracle = Cut_OracleStart( pCutMan ); + Cut_ManStop( pCutMan ); + if ( fOracle ) + { + Abc_NtkCutsOracle( pNtk, pCutOracle ); + Cut_OracleStop( pCutOracle ); + } + return 0; + +usage: + fprintf( pErr, "usage: cut [-K num] [-M num] [-tfdxyzmvh]\n" ); + fprintf( pErr, "\t computes k-feasible cuts for the AIG\n" ); + fprintf( pErr, "\t-K num : max number of leaves (%d <= num <= %d) [default = %d]\n", CUT_SIZE_MIN, CUT_SIZE_MAX, pParams->nVarsMax ); + fprintf( pErr, "\t-M num : max number of cuts stored at a node [default = %d]\n", pParams->nKeepMax ); + fprintf( pErr, "\t-t : toggle truth table computation [default = %s]\n", pParams->fTruth? "yes": "no" ); + fprintf( pErr, "\t-f : toggle filtering of duplicated/dominated [default = %s]\n", pParams->fFilter? "yes": "no" ); + fprintf( pErr, "\t-d : toggle dropping when fanouts are done [default = %s]\n", pParams->fDrop? "yes": "no" ); + fprintf( pErr, "\t-x : toggle computing only DAG cuts [default = %s]\n", pParams->fDag? "yes": "no" ); + fprintf( pErr, "\t-y : toggle computing only tree cuts [default = %s]\n", pParams->fTree? "yes": "no" ); + fprintf( pErr, "\t-g : toggle computing only global cuts [default = %s]\n", pParams->fGlobal? "yes": "no" ); + fprintf( pErr, "\t-l : toggle computing only local cuts [default = %s]\n", pParams->fLocal? "yes": "no" ); + fprintf( pErr, "\t-z : toggle fancy computations [default = %s]\n", pParams->fFancy? "yes": "no" ); + fprintf( pErr, "\t-m : toggle delay-oriented FPGA mapping [default = %s]\n", pParams->fMap? "yes": "no" ); + fprintf( pErr, "\t-v : toggle printing verbose information [default = %s]\n", pParams->fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandScut( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Cut_Params_t Params, * pParams = &Params; + Cut_Man_t * pCutMan; + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + extern Cut_Man_t * Abc_NtkSeqCuts( Abc_Ntk_t * pNtk, Cut_Params_t * pParams ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + memset( pParams, 0, sizeof(Cut_Params_t) ); + pParams->nVarsMax = 5; // the max cut size ("k" of the k-feasible cuts) + pParams->nKeepMax = 1000; // the max number of cuts kept at a node + pParams->fTruth = 0; // compute truth tables + pParams->fFilter = 1; // filter dominated cuts + pParams->fSeq = 1; // compute sequential cuts + pParams->fVerbose = 0; // the verbosiness flag + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "KMtvh" ) ) != EOF ) + { + switch ( c ) + { + case 'K': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-K\" should be followed by an integer.\n" ); + goto usage; + } + pParams->nVarsMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pParams->nVarsMax < 0 ) + goto usage; + break; + case 'M': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-M\" should be followed by an integer.\n" ); + goto usage; + } + pParams->nKeepMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pParams->nKeepMax < 0 ) + goto usage; + break; + case 't': + pParams->fTruth ^= 1; + break; + case 'v': + pParams->fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } +/* + if ( !Abc_NtkIsSeq(pNtk) ) + { + fprintf( pErr, "Sequential cuts can be computed for sequential AIGs (run \"seq\").\n" ); + return 1; + } +*/ + if ( pParams->nVarsMax < CUT_SIZE_MIN || pParams->nVarsMax > CUT_SIZE_MAX ) + { + fprintf( pErr, "Can only compute the cuts for %d <= K <= %d.\n", CUT_SIZE_MIN, CUT_SIZE_MAX ); + return 1; + } + + pCutMan = Abc_NtkSeqCuts( pNtk, pParams ); + Cut_ManStop( pCutMan ); + return 0; + +usage: + fprintf( pErr, "usage: scut [-K num] [-M num] [-tvh]\n" ); + fprintf( pErr, "\t computes k-feasible cuts for the sequential AIG\n" ); + fprintf( pErr, "\t-K num : max number of leaves (%d <= num <= %d) [default = %d]\n", CUT_SIZE_MIN, CUT_SIZE_MAX, pParams->nVarsMax ); + fprintf( pErr, "\t-M num : max number of cuts stored at a node [default = %d]\n", pParams->nKeepMax ); + fprintf( pErr, "\t-t : toggle truth table computation [default = %s]\n", pParams->fTruth? "yes": "no" ); + fprintf( pErr, "\t-v : toggle printing verbose information [default = %s]\n", pParams->fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandEspresso( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int fVerbose; + extern void Abc_NtkEspresso( Abc_Ntk_t * pNtk, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "vh" ) ) != EOF ) + { + switch ( c ) + { + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsLogic(pNtk) ) + { + fprintf( pErr, "SOP minimization is possible for logic networks (run \"renode\").\n" ); + return 1; + } + Abc_NtkEspresso( pNtk, fVerbose ); + return 0; + +usage: + fprintf( pErr, "usage: espresso [-vh]\n" ); + fprintf( pErr, "\t minimizes SOPs of the local functions using Espresso\n" ); + fprintf( pErr, "\t-v : prints verbose information [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandGen( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int nVars; + int fAdder; + int fSorter; + int fMesh; + int fFpga; + int fVerbose; + char * FileName; + extern void Abc_GenAdder( char * pFileName, int nVars ); + extern void Abc_GenSorter( char * pFileName, int nVars ); + extern void Abc_GenMesh( char * pFileName, int nVars ); + extern void Abc_GenFpga( char * pFileName, int nLutSize, int nLuts, int nVars ); + + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nVars = 8; + fAdder = 0; + fSorter = 0; + fMesh = 0; + fFpga = 0; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Nasmfvh" ) ) != EOF ) + { + switch ( c ) + { + case 'N': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-N\" should be followed by an integer.\n" ); + goto usage; + } + nVars = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nVars < 0 ) + goto usage; + break; + case 'a': + fAdder ^= 1; + break; + case 's': + fSorter ^= 1; + break; + case 'm': + fMesh ^= 1; + break; + case 'f': + fFpga ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( argc != globalUtilOptind + 1 ) + { + goto usage; + } + + // get the input file name + FileName = argv[globalUtilOptind]; + if ( fAdder ) + Abc_GenAdder( FileName, nVars ); + else if ( fSorter ) + Abc_GenSorter( FileName, nVars ); + else if ( fMesh ) + Abc_GenMesh( FileName, nVars ); + else if ( fFpga ) + Abc_GenFpga( FileName, 4, 3, 10 ); +// Abc_GenFpga( FileName, 2, 2, 3 ); +// Abc_GenFpga( FileName, 3, 2, 5 ); + else + printf( "Type of circuit is not specified.\n" ); + return 0; + +usage: + fprintf( pErr, "usage: gen [-N] [-asmfvh] \n" ); + fprintf( pErr, "\t generates simple circuits\n" ); + fprintf( pErr, "\t-N num : the number of variables [default = %d]\n", nVars ); + fprintf( pErr, "\t-a : generate ripple-carry adder [default = %s]\n", fAdder? "yes": "no" ); + fprintf( pErr, "\t-s : generate a sorter [default = %s]\n", fSorter? "yes": "no" ); + fprintf( pErr, "\t-m : generate a mesh [default = %s]\n", fMesh? "yes": "no" ); + fprintf( pErr, "\t-f : generate a LUT FPGA structure [default = %s]\n", fFpga? "yes": "no" ); + fprintf( pErr, "\t-v : prints verbose information [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + fprintf( pErr, "\t : output file name\n"); + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandXyz( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes;//, * pNtkTemp; + int c; + int nLutMax; + int nPlaMax; + int RankCost; + int fFastMode; + int fRewriting; + int fSynthesis; + int fVerbose; +// extern Abc_Ntk_t * Abc_NtkXyz( Abc_Ntk_t * pNtk, int nPlaMax, bool fEsop, bool fSop, bool fInvs, bool fVerbose ); + extern void * Abc_NtkPlayer( void * pNtk, int nLutMax, int nPlaMax, int RankCost, int fFastMode, int fRewriting, int fSynthesis, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nLutMax = 8; + nPlaMax = 128; + RankCost = 96000; + fFastMode = 1; + fRewriting = 0; + fSynthesis = 0; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "LPRfrsvh" ) ) != EOF ) + { + switch ( c ) + { + case 'L': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-L\" should be followed by an integer.\n" ); + goto usage; + } + nLutMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nLutMax < 0 ) + goto usage; + break; + case 'P': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-P\" should be followed by an integer.\n" ); + goto usage; + } + nPlaMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nPlaMax < 0 ) + goto usage; + break; + case 'R': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-R\" should be followed by an integer.\n" ); + goto usage; + } + RankCost = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( RankCost < 0 ) + goto usage; + break; + case 'f': + fFastMode ^= 1; + break; + case 'r': + fRewriting ^= 1; + break; + case 's': + fSynthesis ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "Only works for strashed networks.\n" ); + return 1; + } +/* + if ( nLutMax < 2 || nLutMax > 12 || nPlaMax < 8 || nPlaMax > 128 ) + { + fprintf( pErr, "Incorrect LUT/PLA parameters.\n" ); + return 1; + } +*/ + // run the command +// pNtkRes = Abc_NtkXyz( pNtk, nPlaMax, 1, 0, fInvs, fVerbose ); +/* + if ( !Abc_NtkIsStrash(pNtk) ) + { + pNtkTemp = Abc_NtkStrash( pNtk, 0, 1, 0 ); + pNtkRes = Abc_NtkPlayer( pNtkTemp, nLutMax, nPlaMax, RankCost, fFastMode, fRewriting, fSynthesis, fVerbose ); + Abc_NtkDelete( pNtkTemp ); + } + else + pNtkRes = Abc_NtkPlayer( pNtk, nLutMax, nPlaMax, RankCost, fFastMode, fRewriting, fSynthesis, fVerbose ); +*/ + pNtkRes = NULL; + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Command has failed.\n" ); + return 0; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: xyz [-L num] [-P num] [-R num] [-frsvh]\n" ); + fprintf( pErr, "\t specilized LUT/PLA decomposition\n" ); + fprintf( pErr, "\t-L num : maximum number of LUT inputs (2<=num<=8) [default = %d]\n", nLutMax ); + fprintf( pErr, "\t-P num : maximum number of PLA inputs/cubes (8<=num<=128) [default = %d]\n", nPlaMax ); + fprintf( pErr, "\t-R num : maximum are of one decomposition rank [default = %d]\n", RankCost ); + fprintf( pErr, "\t-f : toggle using fast LUT mapping mode [default = %s]\n", fFastMode? "yes": "no" ); + fprintf( pErr, "\t-r : toggle using one pass of AIG rewriting [default = %s]\n", fRewriting? "yes": "no" ); + fprintf( pErr, "\t-s : toggle using synthesis by AIG rewriting [default = %s]\n", fSynthesis? "yes": "no" ); + fprintf( pErr, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandDouble( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c; + int nFrames; + int fVerbose; + extern Abc_Ntk_t * Abc_NtkDouble( Abc_Ntk_t * pNtk ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nFrames = 50; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "vh" ) ) != EOF ) + { + switch ( c ) + { + case 'F': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-F\" should be followed by an integer.\n" ); + goto usage; + } + nFrames = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nFrames < 0 ) + goto usage; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( !Abc_NtkIsSopLogic(pNtk) ) + { + fprintf( pErr, "Only works for logic SOP networks.\n" ); + return 1; + } + + pNtkRes = Abc_NtkDouble( pNtk ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Command has failed.\n" ); + return 0; + } + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: double [-vh]\n" ); + fprintf( pErr, "\t puts together two parallel copies of the current network\n" ); +// fprintf( pErr, "\t-F num : the number of frames to simulate [default = %d]\n", nFrames ); + fprintf( pErr, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandTest( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c; + int nLevels; +// extern Abc_Ntk_t * Abc_NtkNewAig( Abc_Ntk_t * pNtk ); +// extern Abc_Ntk_t * Abc_NtkIvy( Abc_Ntk_t * pNtk ); +// extern void Abc_NtkMaxFlowTest( Abc_Ntk_t * pNtk ); +// extern int Pr_ManProofTest( char * pFileName ); + extern void Abc_NtkCompareSupports( Abc_Ntk_t * pNtk ); + extern void Abc_NtkCompareCones( Abc_Ntk_t * pNtk ); + extern Abc_Ntk_t * Abc_NtkDar( Abc_Ntk_t * pNtk ); + extern Abc_Ntk_t * Abc_NtkDarToCnf( Abc_Ntk_t * pNtk, char * pFileName ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nLevels = 128; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Nh" ) ) != EOF ) + { + switch ( c ) + { + case 'N': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-N\" should be followed by an integer.\n" ); + goto usage; + } + nLevels = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nLevels < 0 ) + goto usage; + break; + case 'h': + goto usage; + default: + goto usage; + } + } +/* + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( Abc_NtkIsSeq(pNtk) ) + { + fprintf( pErr, "Only works for non-sequential networks.\n" ); + return 1; + } +*/ + +// Abc_NtkTestEsop( pNtk ); +// Abc_NtkTestSop( pNtk ); +// printf( "This command is currently not used.\n" ); + // run the command +// pNtkRes = Abc_NtkMiterForCofactors( pNtk, 0, 0, -1 ); +// pNtkRes = Abc_NtkNewAig( pNtk ); + +/* + pNtkRes = NULL; + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Command has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); +*/ + +// if ( Cut_CellIsRunning() ) +// Cut_CellDumpToFile(); +// else +// Cut_CellPrecompute(); +// Cut_CellLoad(); +/* + { + Abc_Ntk_t * pNtkRes; + extern Abc_Ntk_t * Abc_NtkTopmost( Abc_Ntk_t * pNtk, int nLevels ); + pNtkRes = Abc_NtkTopmost( pNtk, nLevels ); + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + } +*/ +// Abc_NtkSimulteBuggyMiter( pNtk ); + +// Rwr_Temp(); +// Abc_MvExperiment(); +// Ivy_TruthTest(); + + +// Ivy_TruthEstimateNodesTest(); +/* + pNtkRes = Abc_NtkIvy( pNtk ); +// pNtkRes = Abc_NtkPlayer( pNtk, nLevels, 0 ); +// pNtkRes = NULL; + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Command has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); +*/ +// Abc_NtkMaxFlowTest( pNtk ); +// Pr_ManProofTest( "trace.cnf" ); + +// Abc_NtkCompareSupports( pNtk ); +// Abc_NtkCompareCones( pNtk ); +/* + { + extern Vec_Vec_t * Abc_NtkPartitionSmart( Abc_Ntk_t * pNtk, int fVerbose ); + Vec_Vec_t * vParts; + vParts = Abc_NtkPartitionSmart( pNtk, 1 ); + Vec_VecFree( vParts ); + } +*/ +// Abc_Ntk4VarTable( pNtk ); +// Dar_NtkGenerateArrays( pNtk ); +// Dar_ManDeriveCnfTest2(); +/* + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "Network should be strashed. Command has failed.\n" ); + return 1; + } +// pNtkRes = Abc_NtkDar( pNtk ); + pNtkRes = Abc_NtkDarToCnf( pNtk, "any.cnf" ); +*/ + pNtkRes = NULL; + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Command has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + + return 0; +usage: + fprintf( pErr, "usage: test [-h]\n" ); + fprintf( pErr, "\t testbench for new procedures\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandQuaVar( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c, iVar, fUniv, fVerbose, RetValue; + extern int Abc_NtkQuantify( Abc_Ntk_t * pNtk, int fUniv, int iVar, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + iVar = 0; + fUniv = 0; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Iuvh" ) ) != EOF ) + { + switch ( c ) + { + case 'I': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-I\" should be followed by an integer.\n" ); + goto usage; + } + iVar = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( iVar < 0 ) + goto usage; + break; + case 'u': + fUniv ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( Abc_NtkGetChoiceNum( pNtk ) ) + { + fprintf( pErr, "This command cannot be applied to an AIG with choice nodes.\n" ); + return 1; + } + + // get the strashed network + pNtkRes = Abc_NtkStrash( pNtk, 0, 1, 0 ); + RetValue = Abc_NtkQuantify( pNtkRes, fUniv, iVar, fVerbose ); + // clean temporary storage for the cofactors + Abc_NtkCleanData( pNtkRes ); + Abc_AigCleanup( pNtkRes->pManFunc ); + // check the result + if ( !RetValue ) + { + fprintf( pErr, "Command has failed.\n" ); + return 0; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: qvar [-I num] [-uvh]\n" ); + fprintf( pErr, "\t quantifies one variable using the AIG\n" ); + fprintf( pErr, "\t-I num : the zero-based index of a variable to quantify [default = %d]\n", iVar ); + fprintf( pErr, "\t-u : toggle universal quantification [default = %s]\n", fUniv? "yes": "no" ); + fprintf( pErr, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandQuaRel( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c, iVar, fInputs, fVerbose; + extern Abc_Ntk_t * Abc_NtkTransRel( Abc_Ntk_t * pNtk, int fInputs, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + iVar = 0; + fInputs = 1; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Iqvh" ) ) != EOF ) + { + switch ( c ) + { + case 'I': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-I\" should be followed by an integer.\n" ); + goto usage; + } + iVar = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( iVar < 0 ) + goto usage; + break; + case 'q': + fInputs ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( Abc_NtkGetChoiceNum( pNtk ) ) + { + fprintf( pErr, "This command cannot be applied to an AIG with choice nodes.\n" ); + return 1; + } + if ( Abc_NtkIsComb(pNtk) ) + { + fprintf( pErr, "This command works only for sequential circuits.\n" ); + return 1; + } + + // get the strashed network + if ( !Abc_NtkIsStrash(pNtk) ) + { + pNtk = Abc_NtkStrash( pNtk, 0, 1, 0 ); + pNtkRes = Abc_NtkTransRel( pNtk, fInputs, fVerbose ); + Abc_NtkDelete( pNtk ); + } + else + pNtkRes = Abc_NtkTransRel( pNtk, fInputs, fVerbose ); + // check if the result is available + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Command has failed.\n" ); + return 0; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: qrel [-qvh]\n" ); + fprintf( pErr, "\t computes transition relation of the sequential network\n" ); +// fprintf( pErr, "\t-I num : the zero-based index of a variable to quantify [default = %d]\n", iVar ); + fprintf( pErr, "\t-q : perform quantification of inputs [default = %s]\n", fInputs? "yes": "no" ); + fprintf( pErr, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandQuaReach( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c, nIters, fVerbose; + extern Abc_Ntk_t * Abc_NtkReachability( Abc_Ntk_t * pNtk, int nIters, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nIters = 256; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Ivh" ) ) != EOF ) + { + switch ( c ) + { + case 'I': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-I\" should be followed by an integer.\n" ); + goto usage; + } + nIters = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nIters < 0 ) + goto usage; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( Abc_NtkGetChoiceNum( pNtk ) ) + { + fprintf( pErr, "This command cannot be applied to an AIG with choice nodes.\n" ); + return 1; + } + if ( !Abc_NtkIsComb(pNtk) ) + { + fprintf( pErr, "This command works only for combinational transition relations.\n" ); + return 1; + } + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "This command works only for strashed networks.\n" ); + return 1; + } + if ( Abc_NtkPoNum(pNtk) > 1 ) + { + fprintf( pErr, "The transition relation should have one output.\n" ); + return 1; + } + if ( Abc_NtkPiNum(pNtk) % 2 != 0 ) + { + fprintf( pErr, "The transition relation should have an even number of inputs.\n" ); + return 1; + } + + pNtkRes = Abc_NtkReachability( pNtk, nIters, fVerbose ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Command has failed.\n" ); + return 0; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: qreach [-I num] [-vh]\n" ); + fprintf( pErr, "\t computes unreachable states using AIG-based quantification\n" ); + fprintf( pErr, "\t assumes that the current network is a transition relation\n" ); + fprintf( pErr, "\t assumes that the initial state is composed of all zeros\n" ); + fprintf( pErr, "\t-I num : the number of image computations to perform [default = %d]\n", nIters ); + fprintf( pErr, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandIStrash( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes, * pNtkTemp; + int c; + extern Abc_Ntk_t * Abc_NtkIvyStrash( Abc_Ntk_t * pNtk ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsStrash(pNtk) ) + { + pNtkTemp = Abc_NtkStrash( pNtk, 0, 1, 0 ); + pNtkRes = Abc_NtkIvyStrash( pNtkTemp ); + Abc_NtkDelete( pNtkTemp ); + } + else + pNtkRes = Abc_NtkIvyStrash( pNtk ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Command has failed.\n" ); + return 0; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: istrash [-h]\n" ); + fprintf( pErr, "\t perform sequential structural hashing\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandICut( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c, nInputs; + extern void Abc_NtkIvyCuts( Abc_Ntk_t * pNtk, int nInputs ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nInputs = 5; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Kh" ) ) != EOF ) + { + switch ( c ) + { + case 'K': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-K\" should be followed by an integer.\n" ); + goto usage; + } + nInputs = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nInputs < 0 ) + goto usage; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + Abc_NtkIvyCuts( pNtk, nInputs ); + return 0; + +usage: + fprintf( pErr, "usage: icut [-K num] [-h]\n" ); + fprintf( pErr, "\t computes sequential cuts of the given size\n" ); + fprintf( pErr, "\t-K num : the number of cut inputs (2 <= num <= 6) [default = %d]\n", nInputs ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandIRewrite( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c, fUpdateLevel, fUseZeroCost, fVerbose; + extern Abc_Ntk_t * Abc_NtkIvyRewrite( Abc_Ntk_t * pNtk, int fUpdateLevel, int fUseZeroCost, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fUpdateLevel = 1; + fUseZeroCost = 0; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "lzvh" ) ) != EOF ) + { + switch ( c ) + { + case 'l': + fUpdateLevel ^= 1; + break; + case 'z': + fUseZeroCost ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + pNtkRes = Abc_NtkIvyRewrite( pNtk, fUpdateLevel, fUseZeroCost, fVerbose ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Command has failed.\n" ); + return 0; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: irw [-lzvh]\n" ); + fprintf( pErr, "\t perform combinational AIG rewriting\n" ); + fprintf( pErr, "\t-l : toggle preserving the number of levels [default = %s]\n", fUpdateLevel? "yes": "no" ); + fprintf( pErr, "\t-z : toggle using zero-cost replacements [default = %s]\n", fUseZeroCost? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose printout [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandDRewrite( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + Dar_RwrPar_t Pars, * pPars = &Pars; + int c; + + extern Abc_Ntk_t * Abc_NtkDRewrite( Abc_Ntk_t * pNtk, Dar_RwrPar_t * pPars ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Dar_ManDefaultRwrParams( pPars ); + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "CNflzvwh" ) ) != EOF ) + { + switch ( c ) + { + case 'C': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-C\" should be followed by an integer.\n" ); + goto usage; + } + pPars->nCutsMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pPars->nCutsMax < 0 ) + goto usage; + break; + case 'N': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-N\" should be followed by an integer.\n" ); + goto usage; + } + pPars->nSubgMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pPars->nSubgMax < 0 ) + goto usage; + break; + case 'f': + pPars->fFanout ^= 1; + break; + case 'l': + pPars->fUpdateLevel ^= 1; + break; + case 'z': + pPars->fUseZeros ^= 1; + break; + case 'v': + pPars->fVerbose ^= 1; + break; + case 'w': + pPars->fVeryVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + pNtkRes = Abc_NtkDRewrite( pNtk, pPars ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Command has failed.\n" ); + return 0; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: drw [-C num] [-N num] [-flzvwh]\n" ); + fprintf( pErr, "\t performs combinational AIG rewriting\n" ); + fprintf( pErr, "\t-C num : the max number of cuts at a node [default = %d]\n", pPars->nCutsMax ); + fprintf( pErr, "\t-N num : the max number of subgraphs tried [default = %d]\n", pPars->nSubgMax ); + fprintf( pErr, "\t-f : toggle representing fanouts [default = %s]\n", pPars->fFanout? "yes": "no" ); + fprintf( pErr, "\t-l : toggle preserving the number of levels [default = %s]\n", pPars->fUpdateLevel? "yes": "no" ); + fprintf( pErr, "\t-z : toggle using zero-cost replacements [default = %s]\n", pPars->fUseZeros? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose printout [default = %s]\n", pPars->fVerbose? "yes": "no" ); + fprintf( pErr, "\t-w : toggle very verbose printout [default = %s]\n", pPars->fVeryVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandDRefactor( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + Dar_RefPar_t Pars, * pPars = &Pars; + int c; + + extern Abc_Ntk_t * Abc_NtkDRefactor( Abc_Ntk_t * pNtk, Dar_RefPar_t * pPars ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Dar_ManDefaultRefParams( pPars ); + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "MKCelzvwh" ) ) != EOF ) + { + switch ( c ) + { + case 'M': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-C\" should be followed by an integer.\n" ); + goto usage; + } + pPars->nMffcMin = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pPars->nMffcMin < 0 ) + goto usage; + break; + case 'K': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-N\" should be followed by an integer.\n" ); + goto usage; + } + pPars->nLeafMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pPars->nLeafMax < 0 ) + goto usage; + break; + case 'C': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-C\" should be followed by an integer.\n" ); + goto usage; + } + pPars->nCutsMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pPars->nCutsMax < 0 ) + goto usage; + break; + case 'e': + pPars->fExtend ^= 1; + break; + case 'l': + pPars->fUpdateLevel ^= 1; + break; + case 'z': + pPars->fUseZeros ^= 1; + break; + case 'v': + pPars->fVerbose ^= 1; + break; + case 'w': + pPars->fVeryVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( pPars->nLeafMax < 4 || pPars->nLeafMax > 15 ) + { + fprintf( pErr, "This command only works for cut sizes 4 <= K <= 15.\n" ); + return 1; + } + pNtkRes = Abc_NtkDRefactor( pNtk, pPars ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Command has failed.\n" ); + return 0; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: drf [-M num] [-K num] [-C num] [-elzvwh]\n" ); + fprintf( pErr, "\t performs combinational AIG refactoring\n" ); + fprintf( pErr, "\t-M num : the min MFFC size to attempt refactoring [default = %d]\n", pPars->nMffcMin ); + fprintf( pErr, "\t-K num : the max number of cuts leaves [default = %d]\n", pPars->nLeafMax ); + fprintf( pErr, "\t-C num : the max number of cuts to try at a node [default = %d]\n", pPars->nCutsMax ); + fprintf( pErr, "\t-e : toggle extending tbe cut below MFFC [default = %s]\n", pPars->fExtend? "yes": "no" ); + fprintf( pErr, "\t-l : toggle preserving the number of levels [default = %s]\n", pPars->fUpdateLevel? "yes": "no" ); + fprintf( pErr, "\t-z : toggle using zero-cost replacements [default = %s]\n", pPars->fUseZeros? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose printout [default = %s]\n", pPars->fVerbose? "yes": "no" ); + fprintf( pErr, "\t-w : toggle very verbose printout [default = %s]\n", pPars->fVeryVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandDCompress2( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int fBalance, fVerbose, fUpdateLevel, c; + + extern Abc_Ntk_t * Abc_NtkDCompress2( Abc_Ntk_t * pNtk, int fBalance, int fUpdateLevel, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fBalance = 0; + fVerbose = 0; + fUpdateLevel = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "blvh" ) ) != EOF ) + { + switch ( c ) + { + case 'b': + fBalance ^= 1; + break; + case 'l': + fUpdateLevel ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + pNtkRes = Abc_NtkDCompress2( pNtk, fBalance, fUpdateLevel, fVerbose ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Command has failed.\n" ); + return 0; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: dcompress2 [-blvh]\n" ); + fprintf( pErr, "\t performs combinational AIG optimization\n" ); + fprintf( pErr, "\t-b : toggle internal balancing [default = %s]\n", fBalance? "yes": "no" ); + fprintf( pErr, "\t-l : toggle updating level [default = %s]\n", fUpdateLevel? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose printout [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandDrwsat( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int fBalance, fVerbose, c; + + extern Abc_Ntk_t * Abc_NtkDrwsat( Abc_Ntk_t * pNtk, int fBalance, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fBalance = 0; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "bvh" ) ) != EOF ) + { + switch ( c ) + { + case 'b': + fBalance ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + pNtkRes = Abc_NtkDrwsat( pNtk, fBalance, fVerbose ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Command has failed.\n" ); + return 0; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: drwsat [-bvh]\n" ); + fprintf( pErr, "\t performs combinational AIG optimization for SAT\n" ); + fprintf( pErr, "\t-b : toggle internal balancing [default = %s]\n", fBalance? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose printout [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandIRewriteSeq( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c, fUpdateLevel, fUseZeroCost, fVerbose; + extern Abc_Ntk_t * Abc_NtkIvyRewriteSeq( Abc_Ntk_t * pNtk, int fUseZeroCost, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fUpdateLevel = 0; + fUseZeroCost = 0; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "lzvh" ) ) != EOF ) + { + switch ( c ) + { + case 'l': + fUpdateLevel ^= 1; + break; + case 'z': + fUseZeroCost ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + pNtkRes = Abc_NtkIvyRewriteSeq( pNtk, fUseZeroCost, fVerbose ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Command has failed.\n" ); + return 0; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: irws [-zvh]\n" ); + fprintf( pErr, "\t perform sequential AIG rewriting\n" ); +// fprintf( pErr, "\t-l : toggle preserving the number of levels [default = %s]\n", fUpdateLevel? "yes": "no" ); + fprintf( pErr, "\t-z : toggle using zero-cost replacements [default = %s]\n", fUseZeroCost? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose printout [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandIResyn( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c, fUpdateLevel, fVerbose; + extern Abc_Ntk_t * Abc_NtkIvyResyn( Abc_Ntk_t * pNtk, int fUpdateLevel, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fUpdateLevel = 1; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "lzvh" ) ) != EOF ) + { + switch ( c ) + { + case 'l': + fUpdateLevel ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + pNtkRes = Abc_NtkIvyResyn( pNtk, fUpdateLevel, fVerbose ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Command has failed.\n" ); + return 0; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: iresyn [-lvh]\n" ); + fprintf( pErr, "\t performs combinational resynthesis\n" ); + fprintf( pErr, "\t-l : toggle preserving the number of levels [default = %s]\n", fUpdateLevel? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose printout [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandISat( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c, fUpdateLevel, fVerbose; + int nConfLimit; + + extern Abc_Ntk_t * Abc_NtkIvySat( Abc_Ntk_t * pNtk, int nConfLimit, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nConfLimit = 100000; + fUpdateLevel = 1; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Clzvh" ) ) != EOF ) + { + switch ( c ) + { + case 'C': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-C\" should be followed by an integer.\n" ); + goto usage; + } + nConfLimit = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nConfLimit < 0 ) + goto usage; + break; + case 'l': + fUpdateLevel ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + pNtkRes = Abc_NtkIvySat( pNtk, nConfLimit, fVerbose ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Command has failed.\n" ); + return 0; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: isat [-C num] [-vh]\n" ); + fprintf( pErr, "\t tries to prove the miter constant 0\n" ); + fprintf( pErr, "\t-C num : limit on the number of conflicts [default = %d]\n", nConfLimit ); +// fprintf( pErr, "\t-l : toggle preserving the number of levels [default = %s]\n", fUpdateLevel? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose printout [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandIFraig( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c, fProve, fVerbose, fDoSparse; + int nConfLimit; + + extern Abc_Ntk_t * Abc_NtkIvyFraig( Abc_Ntk_t * pNtk, int nConfLimit, int fDoSparse, int fProve, int fTransfer, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nConfLimit = 100; + fDoSparse = 0; + fProve = 0; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Cspvh" ) ) != EOF ) + { + switch ( c ) + { + case 'C': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-C\" should be followed by an integer.\n" ); + goto usage; + } + nConfLimit = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nConfLimit < 0 ) + goto usage; + break; + case 's': + fDoSparse ^= 1; + break; + case 'p': + fProve ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + pNtkRes = Abc_NtkIvyFraig( pNtk, nConfLimit, fDoSparse, fProve, 0, fVerbose ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Command has failed.\n" ); + return 0; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: ifraig [-C num] [-spvh]\n" ); + fprintf( pErr, "\t performs fraiging using a new method\n" ); + fprintf( pErr, "\t-C num : limit on the number of conflicts [default = %d]\n", nConfLimit ); + fprintf( pErr, "\t-s : toggle considering sparse functions [default = %s]\n", fDoSparse? "yes": "no" ); + fprintf( pErr, "\t-p : toggle proving the miter outputs [default = %s]\n", fProve? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose printout [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandDFraig( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c, nConfLimit, fDoSparse, fProve, fSpeculate, fChoicing, fVerbose; + + extern Abc_Ntk_t * Abc_NtkDarFraig( Abc_Ntk_t * pNtk, int nConfLimit, int fDoSparse, int fProve, int fTransfer, int fSpeculate, int fChoicing, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nConfLimit = 100; + fDoSparse = 1; + fProve = 0; + fSpeculate = 0; + fChoicing = 0; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Csprcvh" ) ) != EOF ) + { + switch ( c ) + { + case 'C': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-C\" should be followed by an integer.\n" ); + goto usage; + } + nConfLimit = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nConfLimit < 0 ) + goto usage; + break; + case 's': + fDoSparse ^= 1; + break; + case 'p': + fProve ^= 1; + break; + case 'r': + fSpeculate ^= 1; + break; + case 'c': + fChoicing ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + pNtkRes = Abc_NtkDarFraig( pNtk, nConfLimit, fDoSparse, fProve, 0, fSpeculate, fChoicing, fVerbose ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Command has failed.\n" ); + return 0; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: dfraig [-C num] [-sprcvh]\n" ); + fprintf( pErr, "\t performs fraiging using a new method\n" ); + fprintf( pErr, "\t-C num : limit on the number of conflicts [default = %d]\n", nConfLimit ); + fprintf( pErr, "\t-s : toggle considering sparse functions [default = %s]\n", fDoSparse? "yes": "no" ); + fprintf( pErr, "\t-p : toggle proving the miter outputs [default = %s]\n", fProve? "yes": "no" ); + fprintf( pErr, "\t-r : toggle speculative reduction [default = %s]\n", fSpeculate? "yes": "no" ); + fprintf( pErr, "\t-c : toggle accumulation of choices [default = %s]\n", fChoicing? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose printout [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandCSweep( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c, nCutsMax, nLeafMax, fVerbose; + + extern Abc_Ntk_t * Abc_NtkCSweep( Abc_Ntk_t * pNtk, int nCutsMax, int nLeafMax, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nCutsMax = 8; + nLeafMax = 6; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "CKvh" ) ) != EOF ) + { + switch ( c ) + { + case 'C': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-C\" should be followed by an integer.\n" ); + goto usage; + } + nCutsMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nCutsMax < 0 ) + goto usage; + break; + case 'K': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-K\" should be followed by an integer.\n" ); + goto usage; + } + nLeafMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nLeafMax < 0 ) + goto usage; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( nCutsMax < 2 ) + { + fprintf( pErr, "The number of cuts cannot be less than 2.\n" ); + return 1; + } + + if ( nLeafMax < 3 || nLeafMax > 16 ) + { + fprintf( pErr, "The number of leaves is infeasible.\n" ); + return 1; + } + + pNtkRes = Abc_NtkCSweep( pNtk, nCutsMax, nLeafMax, fVerbose ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Command has failed.\n" ); + return 0; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: csweep [-C num] [-K num] [-vh]\n" ); + fprintf( pErr, "\t performs cut sweeping using a new method\n" ); + fprintf( pErr, "\t-C num : limit on the number of cuts (C >= 2) [default = %d]\n", nCutsMax ); + fprintf( pErr, "\t-K num : limit on the cut size (3 <= K <= 16) [default = %d]\n", nLeafMax ); + fprintf( pErr, "\t-v : toggle verbose printout [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandIProve( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Prove_Params_t Params, * pParams = &Params; + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkTemp; + int c, clk, RetValue; + + extern int Abc_NtkIvyProve( Abc_Ntk_t ** ppNtk, void * pPars ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Prove_ParamsSetDefault( pParams ); + pParams->fUseRewriting = 1; + pParams->fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "rvh" ) ) != EOF ) + { + switch ( c ) + { + case 'r': + pParams->fUseRewriting ^= 1; + break; + case 'v': + pParams->fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + clk = clock(); + + if ( Abc_NtkIsStrash(pNtk) ) + pNtkTemp = Abc_NtkDup( pNtk ); + else + pNtkTemp = Abc_NtkStrash( pNtk, 0, 0, 0 ); + + RetValue = Abc_NtkIvyProve( &pNtkTemp, pParams ); + + // verify that the pattern is correct + if ( RetValue == 0 ) + { + int * pSimInfo = Abc_NtkVerifySimulatePattern( pNtk, pNtkTemp->pModel ); + if ( pSimInfo[0] != 1 ) + printf( "ERROR in Abc_NtkMiterProve(): Generated counter-example is invalid.\n" ); + free( pSimInfo ); + } + + if ( RetValue == -1 ) + printf( "UNDECIDED " ); + else if ( RetValue == 0 ) + printf( "SATISFIABLE " ); + else + printf( "UNSATISFIABLE " ); + //printf( "\n" ); + + PRT( "Time", clock() - clk ); + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkTemp ); + return 0; + +usage: + fprintf( pErr, "usage: iprove [-rvh]\n" ); + fprintf( pErr, "\t performs CEC using a new method\n" ); + fprintf( pErr, "\t-r : toggle AIG rewriting [default = %s]\n", pParams->fUseRewriting? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose printout [default = %s]\n", pParams->fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandHaig( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c, fUseZeroCost, fVerbose, nIters; + extern Abc_Ntk_t * Abc_NtkIvyHaig( Abc_Ntk_t * pNtk, int nIters, int fUseZeroCost, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nIters = 2; + fUseZeroCost = 0; + fVerbose = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Izvh" ) ) != EOF ) + { + switch ( c ) + { + case 'I': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-I\" should be followed by a positive integer.\n" ); + goto usage; + } + nIters = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nIters < 0 ) + goto usage; + break; + case 'z': + fUseZeroCost ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + pNtkRes = Abc_NtkIvyHaig( pNtk, nIters, fUseZeroCost, fVerbose ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Command has failed.\n" ); + return 0; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: haig [-Izvh]\n" ); + fprintf( pErr, "\t prints HAIG stats after sequential rewriting\n" ); + fprintf( pErr, "\t-I num : the number of rewriting iterations [default = %d]\n", nIters ); + fprintf( pErr, "\t-z : toggle zero-cost replacements [default = %s]\n", fUseZeroCost? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose printout [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandMini( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c; + extern Abc_Ntk_t * Abc_NtkMiniBalance( Abc_Ntk_t * pNtk ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "Only works for combinatinally strashed AIG networks.\n" ); + return 1; + } + + pNtkRes = Abc_NtkMiniBalance( pNtk ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Command has failed.\n" ); + return 0; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: mini [-h]\n" ); + fprintf( pErr, "\t perform balancing using new package\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandBmc( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int nFrames; + int fInit; + int fVerbose; + + extern void Abc_NtkBmc( Abc_Ntk_t * pNtk, int nFrames, int fInit, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nFrames = 5; + fInit = 0; + fVerbose = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Kivh" ) ) != EOF ) + { + switch ( c ) + { + case 'K': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-R\" should be followed by an integer.\n" ); + goto usage; + } + nFrames = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nFrames < 0 ) + goto usage; + break; + case 'i': + fInit ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( Abc_NtkIsStrash(pNtk) ) + Abc_NtkBmc( pNtk, nFrames, fInit, fVerbose ); + else + { + pNtk = Abc_NtkStrash( pNtk, 0, 1, 0 ); + Abc_NtkBmc( pNtk, nFrames, fInit, fVerbose ); + Abc_NtkDelete( pNtk ); + } + return 0; + +usage: + fprintf( pErr, "usage: bmc [-K num] [-ivh]\n" ); + fprintf( pErr, "\t perform bounded model checking\n" ); + fprintf( pErr, "\t-K num : number of time frames [default = %d]\n", nFrames ); + fprintf( pErr, "\t-i : toggle initialization of the first frame [default = %s]\n", fInit? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose output [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandQbf( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int nPars; + int fVerbose; + + extern void Abc_NtkQbf( Abc_Ntk_t * pNtk, int nPars, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nPars = -1; + fVerbose = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Pvh" ) ) != EOF ) + { + switch ( c ) + { + case 'P': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-P\" should be followed by an integer.\n" ); + goto usage; + } + nPars = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nPars < 0 ) + goto usage; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsComb(pNtk) ) + { + fprintf( pErr, "Works only for combinational networks.\n" ); + return 1; + } + if ( Abc_NtkPoNum(pNtk) != 1 ) + { + fprintf( pErr, "The miter should have one primary output.\n" ); + return 1; + } + if ( !(nPars > 0 && nPars < Abc_NtkPiNum(pNtk)) ) + { + fprintf( pErr, "The number of paramter variables is invalid (should be > 0 and < PI num).\n" ); + return 1; + } + if ( Abc_NtkIsStrash(pNtk) ) + Abc_NtkQbf( pNtk, nPars, fVerbose ); + else + { + pNtk = Abc_NtkStrash( pNtk, 0, 1, 0 ); + Abc_NtkQbf( pNtk, nPars, fVerbose ); + Abc_NtkDelete( pNtk ); + } + return 0; + +usage: + fprintf( pErr, "usage: qbf [-P num] [-vh]\n" ); + fprintf( pErr, "\t solves a quantified boolean formula problem EpVxM(p,x)\n" ); + fprintf( pErr, "\t-P num : number of paramters (should be the first PIs) [default = %d]\n", nPars ); + fprintf( pErr, "\t-v : toggle verbose output [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandFraig( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + char Buffer[100]; + Fraig_Params_t Params, * pParams = &Params; + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int fAllNodes; + int fExdc; + int c; + int fPartition = 0; + extern void Abc_NtkFraigPartitionedTime( Abc_Ntk_t * pNtk, void * pParams ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fExdc = 0; + fAllNodes = 0; + memset( pParams, 0, sizeof(Fraig_Params_t) ); + pParams->nPatsRand = 2048; // the number of words of random simulation info + pParams->nPatsDyna = 2048; // the number of words of dynamic simulation info + pParams->nBTLimit = 100; // the max number of backtracks to perform + pParams->fFuncRed = 1; // performs only one level hashing + pParams->fFeedBack = 1; // enables solver feedback + pParams->fDist1Pats = 1; // enables distance-1 patterns + pParams->fDoSparse = 1; // performs equiv tests for sparse functions + pParams->fChoicing = 0; // enables recording structural choices + pParams->fTryProve = 0; // tries to solve the final miter + pParams->fVerbose = 0; // the verbosiness flag + pParams->fVerboseP = 0; // the verbosiness flag + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "RDCrscptvaeh" ) ) != EOF ) + { + switch ( c ) + { + case 'R': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-R\" should be followed by an integer.\n" ); + goto usage; + } + pParams->nPatsRand = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pParams->nPatsRand < 0 ) + goto usage; + break; + case 'D': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-D\" should be followed by an integer.\n" ); + goto usage; + } + pParams->nPatsDyna = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pParams->nPatsDyna < 0 ) + goto usage; + break; + case 'C': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-C\" should be followed by an integer.\n" ); + goto usage; + } + pParams->nBTLimit = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pParams->nBTLimit < 0 ) + goto usage; + break; + + case 'r': + pParams->fFuncRed ^= 1; + break; + case 's': + pParams->fDoSparse ^= 1; + break; + case 'c': + pParams->fChoicing ^= 1; + break; + case 'p': + pParams->fTryProve ^= 1; + break; + case 'v': + pParams->fVerbose ^= 1; + break; + case 't': + fPartition ^= 1; + break; + case 'a': + fAllNodes ^= 1; + break; + case 'e': + fExdc ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsLogic(pNtk) && !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "Can only fraig a logic network or an AIG.\n" ); + return 1; + } + + // report the proof + pParams->fVerboseP = pParams->fTryProve; + + // get the new network + if ( fPartition ) + { + pNtkRes = Abc_NtkDup( pNtk ); + if ( Abc_NtkIsStrash(pNtk) ) + Abc_NtkFraigPartitionedTime( pNtk, &Params ); + else + { + pNtk = Abc_NtkStrash( pNtk, fAllNodes, !fAllNodes, 0 ); + Abc_NtkFraigPartitionedTime( pNtk, &Params ); + Abc_NtkDelete( pNtk ); + } + } + else + { + if ( Abc_NtkIsStrash(pNtk) ) + pNtkRes = Abc_NtkFraig( pNtk, &Params, fAllNodes, fExdc ); + else + { + pNtk = Abc_NtkStrash( pNtk, fAllNodes, !fAllNodes, 0 ); + pNtkRes = Abc_NtkFraig( pNtk, &Params, fAllNodes, fExdc ); + Abc_NtkDelete( pNtk ); + } + } + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Fraiging has failed.\n" ); + return 1; + } + + if ( pParams->fTryProve ) // report the result + Abc_NtkMiterReport( pNtkRes ); + + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + sprintf( Buffer, "%d", pParams->nBTLimit ); + fprintf( pErr, "usage: fraig [-R num] [-D num] [-C num] [-rscpvtah]\n" ); + fprintf( pErr, "\t transforms a logic network into a functionally reduced AIG\n" ); + fprintf( pErr, "\t-R num : number of random patterns (127 < num < 32769) [default = %d]\n", pParams->nPatsRand ); + fprintf( pErr, "\t-D num : number of systematic patterns (127 < num < 32769) [default = %d]\n", pParams->nPatsDyna ); + fprintf( pErr, "\t-C num : number of backtracks for one SAT problem [default = %s]\n", pParams->nBTLimit==-1? "infinity" : Buffer ); + fprintf( pErr, "\t-r : toggle functional reduction [default = %s]\n", pParams->fFuncRed? "yes": "no" ); + fprintf( pErr, "\t-s : toggle considering sparse functions [default = %s]\n", pParams->fDoSparse? "yes": "no" ); + fprintf( pErr, "\t-c : toggle accumulation of choices [default = %s]\n", pParams->fChoicing? "yes": "no" ); + fprintf( pErr, "\t-p : toggle proving the miter outputs [default = %s]\n", pParams->fTryProve? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose output [default = %s]\n", pParams->fVerbose? "yes": "no" ); + fprintf( pErr, "\t-e : toggle functional sweeping using EXDC [default = %s]\n", fExdc? "yes": "no" ); + fprintf( pErr, "\t-a : toggle between all nodes and DFS nodes [default = %s]\n", fAllNodes? "all": "dfs" ); + fprintf( pErr, "\t-t : toggle using partitioned representation [default = %s]\n", fPartition? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandFraigTrust( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c; + int fDuplicate; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fDuplicate = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "dh" ) ) != EOF ) + { + switch ( c ) + { + case 'd': + fDuplicate ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + // get the new network + pNtkRes = Abc_NtkFraigTrust( pNtk ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Fraiging in the trust mode has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: fraig_trust [-h]\n" ); + fprintf( pErr, "\t transforms the current network into an AIG assuming it is FRAIG with choices\n" ); +// fprintf( pErr, "\t-d : toggle duplication of logic [default = %s]\n", fDuplicate? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandFraigStore( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int fDuplicate; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fDuplicate = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "dh" ) ) != EOF ) + { + switch ( c ) + { + case 'd': + fDuplicate ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + // get the new network + if ( !Abc_NtkFraigStore( pNtk ) ) + { + fprintf( pErr, "Fraig storing has failed.\n" ); + return 1; + } + return 0; + +usage: + fprintf( pErr, "usage: fraig_store [-h]\n" ); + fprintf( pErr, "\t saves the current network in the AIG database\n" ); +// fprintf( pErr, "\t-d : toggle duplication of logic [default = %s]\n", fDuplicate? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandFraigRestore( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c; + int fDuplicate; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fDuplicate = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "dh" ) ) != EOF ) + { + switch ( c ) + { + case 'd': + fDuplicate ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + // get the new network + pNtkRes = Abc_NtkFraigRestore(); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Fraig restoring has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: fraig_restore [-h]\n" ); + fprintf( pErr, "\t makes the current network by fraiging the AIG database\n" ); +// fprintf( pErr, "\t-d : toggle duplication of logic [default = %s]\n", fDuplicate? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandFraigClean( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int fDuplicate; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fDuplicate = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "dh" ) ) != EOF ) + { + switch ( c ) + { + case 'd': + fDuplicate ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + Abc_NtkFraigStoreClean(); + return 0; + +usage: + fprintf( pErr, "usage: fraig_clean [-h]\n" ); + fprintf( pErr, "\t cleans the internal FRAIG storage\n" ); +// fprintf( pErr, "\t-d : toggle duplication of logic [default = %s]\n", fDuplicate? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandFraigSweep( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int fUseInv; + int fExdc; + int fVerbose; + int fVeryVerbose; + extern bool Abc_NtkFraigSweep( Abc_Ntk_t * pNtk, int fUseInv, int fExdc, int fVerbose, int fVeryVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fUseInv = 1; + fExdc = 0; + fVerbose = 0; + fVeryVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "ievwh" ) ) != EOF ) + { + switch ( c ) + { + case 'i': + fUseInv ^= 1; + break; + case 'e': + fExdc ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'w': + fVeryVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "Cannot sweep AIGs (use \"fraig\").\n" ); + return 1; + } + if ( !Abc_NtkIsLogic(pNtk) ) + { + fprintf( pErr, "Transform the current network into a logic network.\n" ); + return 1; + } + // modify the current network + if ( !Abc_NtkFraigSweep( pNtk, fUseInv, fExdc, fVerbose, fVeryVerbose ) ) + { + fprintf( pErr, "Sweeping has failed.\n" ); + return 1; + } + return 0; + +usage: + fprintf( pErr, "usage: fraig_sweep [-evwh]\n" ); + fprintf( pErr, "\t performs technology-dependent sweep\n" ); + fprintf( pErr, "\t-e : toggle functional sweeping using EXDC [default = %s]\n", fExdc? "yes": "no" ); + fprintf( pErr, "\t-v : prints verbose information [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-w : prints equivalence class information [default = %s]\n", fVeryVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandFraigDress( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + char * pFileName; + int c; + int fVerbose; + extern void Abc_NtkDress( Abc_Ntk_t * pNtk, char * pFileName, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fVerbose = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "vh" ) ) != EOF ) + { + switch ( c ) + { + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "This command works only for logic networks.\n" ); + return 1; + } + if ( argc != globalUtilOptind && argc != globalUtilOptind + 1 ) + goto usage; + if ( argc == globalUtilOptind && Abc_NtkSpec(pNtk) == NULL ) + { + fprintf( pErr, "The current network has no spec.\n" ); + return 1; + } + // get the input file name + pFileName = (argc == globalUtilOptind + 1) ? argv[globalUtilOptind] : Abc_NtkSpec(pNtk); + // modify the current network + Abc_NtkDress( pNtk, pFileName, fVerbose ); + return 0; + +usage: + fprintf( pErr, "usage: dress [-vh] \n" ); + fprintf( pErr, "\t transfers internal node names from file to the current network\n" ); + fprintf( pErr, "\t : network with names (if not given, the current network spec is used)\n" ); + fprintf( pErr, "\t-v : prints verbose information [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandHaigStart( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "dh" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "This command works only for AIGs; run strashing (\"st\").\n" ); + return 0; + } + Abc_NtkHaigStart( pNtk ); + return 0; + +usage: + fprintf( pErr, "usage: haig_start [-h]\n" ); + fprintf( pErr, "\t starts constructive accumulation of combinational choices\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandHaigStop( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "dh" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "This command works only for AIGs; run strashing (\"st\").\n" ); + return 0; + } + Abc_NtkHaigStop( pNtk ); + return 0; + +usage: + fprintf( pErr, "usage: haig_stop [-h]\n" ); + fprintf( pErr, "\t cleans the internal storage for combinational choices\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandHaigUse( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "dh" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "This command works only for AIGs; run strashing (\"st\").\n" ); + return 0; + } + // get the new network + pNtkRes = Abc_NtkHaigUse( pNtk ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Transforming internal storage into AIG with choices has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: haig_use [-h]\n" ); + fprintf( pErr, "\t transforms internal storage into an AIG with choices\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandRecStart( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int nVars; + int nCuts; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nVars = 4; + nCuts = 8; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "KCh" ) ) != EOF ) + { + switch ( c ) + { + case 'K': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-K\" should be followed by an integer.\n" ); + goto usage; + } + nVars = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nVars < 1 ) + goto usage; + break; + case 'C': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-C\" should be followed by an integer.\n" ); + goto usage; + } + nCuts = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nCuts < 1 ) + goto usage; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( !(nVars >= 3 && nVars <= 16) ) + { + fprintf( pErr, "The range of allowed values is 3 <= K <= 16.\n" ); + return 0; + } + if ( Abc_NtkRecIsRunning() ) + { + fprintf( pErr, "The AIG subgraph recording is already started.\n" ); + return 0; + } + if ( pNtk && !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "This command works only for AIGs; run strashing (\"st\").\n" ); + return 0; + } + Abc_NtkRecStart( pNtk, nVars, nCuts ); + return 0; + +usage: + fprintf( pErr, "usage: rec_start [-K num] [-C num] [-h]\n" ); + fprintf( pErr, "\t starts recording AIG subgraphs (should be called for\n" ); + fprintf( pErr, "\t an empty network or after reading in a previous record)\n" ); + fprintf( pErr, "\t-K num : the largest number of inputs [default = %d]\n", nVars ); + fprintf( pErr, "\t-C num : the max number of cuts used at a node (0 < num < 2^12) [default = %d]\n", nCuts ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandRecStop( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "dh" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( !Abc_NtkRecIsRunning() ) + { + fprintf( pErr, "This command works only after calling \"rec_start\".\n" ); + return 0; + } + Abc_NtkRecStop(); + return 0; + +usage: + fprintf( pErr, "usage: rec_stop [-h]\n" ); + fprintf( pErr, "\t cleans the internal storage for AIG subgraphs\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandRecAdd( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "dh" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "This command works for AIGs.\n" ); + return 0; + } + if ( !Abc_NtkRecIsRunning() ) + { + fprintf( pErr, "This command works for AIGs after calling \"rec_start\".\n" ); + return 0; + } + Abc_NtkRecAdd( pNtk ); + return 0; + +usage: + fprintf( pErr, "usage: rec_add [-h]\n" ); + fprintf( pErr, "\t adds subgraphs from the current network to the set\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandRecPs( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "dh" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( !Abc_NtkRecIsRunning() ) + { + fprintf( pErr, "This command works for AIGs only after calling \"rec_start\".\n" ); + return 0; + } + Abc_NtkRecPs(); + return 0; + +usage: + fprintf( pErr, "usage: rec_ps [-h]\n" ); + fprintf( pErr, "\t prints statistics about the recorded AIG subgraphs\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandRecUse( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "dh" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( !Abc_NtkRecIsRunning() ) + { + fprintf( pErr, "This command works for AIGs only after calling \"rec_start\".\n" ); + return 0; + } + // get the new network + pNtkRes = Abc_NtkRecUse(); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Transforming internal AIG subgraphs into an AIG with choices has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: rec_use [-h]\n" ); + fprintf( pErr, "\t transforms internal storage into an AIG with choices\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandMap( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + char Buffer[100]; + double DelayTarget; + int fRecovery; + int fSweep; + int fSwitching; + int fVerbose; + int c; + extern Abc_Ntk_t * Abc_NtkMap( Abc_Ntk_t * pNtk, double DelayTarget, int fRecovery, int fSwitching, int fVerbose ); + extern bool Abc_NtkFraigSweep( Abc_Ntk_t * pNtk, int fUseInv, int fExdc, int fVerbose, int fVeryVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + DelayTarget =-1; + fRecovery = 1; + fSweep = 1; + fSwitching = 0; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Daspvh" ) ) != EOF ) + { + switch ( c ) + { + case 'D': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-D\" should be followed by a floating point number.\n" ); + goto usage; + } + DelayTarget = (float)atof(argv[globalUtilOptind]); + globalUtilOptind++; + if ( DelayTarget <= 0.0 ) + goto usage; + break; + case 'a': + fRecovery ^= 1; + break; + case 's': + fSweep ^= 1; + break; + case 'p': + fSwitching ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( !Abc_NtkIsStrash(pNtk) ) + { + pNtk = Abc_NtkStrash( pNtk, 0, 0, 0 ); + if ( pNtk == NULL ) + { + fprintf( pErr, "Strashing before mapping has failed.\n" ); + return 1; + } + pNtk = Abc_NtkBalance( pNtkRes = pNtk, 0, 0, 1 ); + Abc_NtkDelete( pNtkRes ); + if ( pNtk == NULL ) + { + fprintf( pErr, "Balancing before mapping has failed.\n" ); + return 1; + } + fprintf( pOut, "The network was strashed and balanced before mapping.\n" ); + // get the new network + pNtkRes = Abc_NtkMap( pNtk, DelayTarget, fRecovery, fSwitching, fVerbose ); + if ( pNtkRes == NULL ) + { + Abc_NtkDelete( pNtk ); + fprintf( pErr, "Mapping has failed.\n" ); + return 1; + } + Abc_NtkDelete( pNtk ); + } + else + { + // get the new network + pNtkRes = Abc_NtkMap( pNtk, DelayTarget, fRecovery, fSwitching, fVerbose ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Mapping has failed.\n" ); + return 1; + } + } + + if ( fSweep ) + Abc_NtkFraigSweep( pNtkRes, 0, 0, 0, 0 ); + + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + if ( DelayTarget == -1 ) + sprintf( Buffer, "not used" ); + else + sprintf( Buffer, "%.3f", DelayTarget ); + fprintf( pErr, "usage: map [-D float] [-aspvh]\n" ); + fprintf( pErr, "\t performs standard cell mapping of the current network\n" ); + fprintf( pErr, "\t-D float : sets the global required times [default = %s]\n", Buffer ); + fprintf( pErr, "\t-a : toggles area recovery [default = %s]\n", fRecovery? "yes": "no" ); + fprintf( pErr, "\t-s : toggles sweep after mapping [default = %s]\n", fSweep? "yes": "no" ); + fprintf( pErr, "\t-p : optimizes power by minimizing switching [default = %s]\n", fSwitching? "yes": "no" ); + fprintf( pErr, "\t-v : toggles verbose output [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandUnmap( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkHasMapping(pNtk) ) + { + fprintf( pErr, "Cannot unmap the network that is not mapped.\n" ); + return 1; + } + + // get the new network + if ( !Abc_NtkMapToSop( pNtk ) ) + { + fprintf( pErr, "Unmapping has failed.\n" ); + return 1; + } + return 0; + +usage: + fprintf( pErr, "usage: unmap [-h]\n" ); + fprintf( pErr, "\t replaces the library gates by the logic nodes represented using SOPs\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandAttach( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( !Abc_NtkIsSopLogic(pNtk) ) + { + fprintf( pErr, "Can only attach gates if the nodes have SOP representations.\n" ); + return 1; + } + + // get the new network + if ( !Abc_NtkAttach( pNtk ) ) + { + fprintf( pErr, "Attaching gates has failed.\n" ); + return 1; + } + return 0; + +usage: + fprintf( pErr, "usage: attach [-h]\n" ); + fprintf( pErr, "\t replaces the SOP functions by the gates from the library\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandSuperChoice( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c; + extern Abc_Ntk_t * Abc_NtkSuperChoice( Abc_Ntk_t * pNtk ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "Superchoicing works only for the AIG representation (run \"strash\").\n" ); + return 1; + } + + // get the new network + pNtkRes = Abc_NtkSuperChoice( pNtk ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Superchoicing has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: sc [-h]\n" ); + fprintf( pErr, "\t performs superchoicing\n" ); + fprintf( pErr, "\t (accumulate: \"r file.blif; rsup; b; sc; f -ac; wb file_sc.blif\")\n" ); + fprintf( pErr, "\t (map without supergate library: \"r file_sc.blif; ft; map\")\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandSuperChoiceLut( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c; + int nLutSize; + int nCutSizeMax; + int fVerbose; + extern int Abc_NtkSuperChoiceLut( Abc_Ntk_t * pNtk, int nLutSize, int nCutSizeMax, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fVerbose = 1; + nLutSize = 4; + nCutSizeMax = 10; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "KNh" ) ) != EOF ) + { + switch ( c ) + { + case 'K': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-K\" should be followed by a positive integer.\n" ); + goto usage; + } + nLutSize = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nLutSize < 0 ) + goto usage; + break; + case 'N': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-N\" should be followed by a positive integer.\n" ); + goto usage; + } + nCutSizeMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nCutSizeMax < 0 ) + goto usage; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "Superchoicing works only for the AIG representation (run \"strash\").\n" ); + return 1; + } + + // convert the network into the SOP network + pNtkRes = Abc_NtkToLogic( pNtk ); + + // get the new network + if ( !Abc_NtkSuperChoiceLut( pNtkRes, nLutSize, nCutSizeMax, fVerbose ) ) + { + Abc_NtkDelete( pNtkRes ); + fprintf( pErr, "Superchoicing has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: scl [-K num] [-N num] [-vh]\n" ); + fprintf( pErr, "\t performs superchoicing for K-LUTs\n" ); + fprintf( pErr, "\t (accumulate: \"r file.blif; b; scl; f -ac; wb file_sc.blif\")\n" ); + fprintf( pErr, "\t (FPGA map: \"r file_sc.blif; ft; read_lut lutlibK; fpga\")\n" ); + fprintf( pErr, "\t-K num : the number of LUT inputs [default = %d]\n", nLutSize ); + fprintf( pErr, "\t-N num : the max size of the cut [default = %d]\n", nCutSizeMax ); + fprintf( pErr, "\t-v : toggles verbose output [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandFpga( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + char Buffer[100]; + char LutSize[100]; + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c; + int fRecovery; + int fSwitching; + int fLatchPaths; + int fVerbose; + int nLutSize; + float DelayTarget; + + extern Abc_Ntk_t * Abc_NtkFpga( Abc_Ntk_t * pNtk, float DelayTarget, int fRecovery, int fSwitching, int fLatchPaths, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fRecovery = 1; + fSwitching = 0; + fLatchPaths = 0; + fVerbose = 0; + DelayTarget =-1; + nLutSize =-1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "aplvhDK" ) ) != EOF ) + { + switch ( c ) + { + case 'a': + fRecovery ^= 1; + break; + case 'p': + fSwitching ^= 1; + break; + case 'l': + fLatchPaths ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + case 'D': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-D\" should be followed by a floating point number.\n" ); + goto usage; + } + DelayTarget = (float)atof(argv[globalUtilOptind]); + globalUtilOptind++; + if ( DelayTarget <= 0.0 ) + goto usage; + break; + case 'K': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-K\" should be followed by a positive integer.\n" ); + goto usage; + } + nLutSize = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nLutSize < 0 ) + goto usage; + break; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + // create the new LUT library + if ( nLutSize >= 3 && nLutSize <= 10 ) + Fpga_SetSimpleLutLib( nLutSize ); +/* + else + { + fprintf( pErr, "Cannot perform FPGA mapping with LUT size %d.\n", nLutSize ); + return 1; + } +*/ + if ( !Abc_NtkIsStrash(pNtk) ) + { + // strash and balance the network + pNtk = Abc_NtkStrash( pNtk, 0, 0, 0 ); + if ( pNtk == NULL ) + { + fprintf( pErr, "Strashing before FPGA mapping has failed.\n" ); + return 1; + } + pNtk = Abc_NtkBalance( pNtkRes = pNtk, 0, 0, 1 ); + Abc_NtkDelete( pNtkRes ); + if ( pNtk == NULL ) + { + fprintf( pErr, "Balancing before FPGA mapping has failed.\n" ); + return 1; + } + fprintf( pOut, "The network was strashed and balanced before FPGA mapping.\n" ); + // get the new network + pNtkRes = Abc_NtkFpga( pNtk, DelayTarget, fRecovery, fSwitching, fLatchPaths, fVerbose ); + if ( pNtkRes == NULL ) + { + Abc_NtkDelete( pNtk ); + fprintf( pErr, "FPGA mapping has failed.\n" ); + return 1; + } + Abc_NtkDelete( pNtk ); + } + else + { + // get the new network + pNtkRes = Abc_NtkFpga( pNtk, DelayTarget, fRecovery, fSwitching, fLatchPaths, fVerbose ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "FPGA mapping has failed.\n" ); + return 1; + } + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + if ( DelayTarget == -1 ) + sprintf( Buffer, "best possible" ); + else + sprintf( Buffer, "%.2f", DelayTarget ); + if ( nLutSize == -1 ) + sprintf( LutSize, "library" ); + else + sprintf( LutSize, "%d", nLutSize ); + fprintf( pErr, "usage: fpga [-D float] [-K num] [-aplvh]\n" ); + fprintf( pErr, "\t performs FPGA mapping of the current network\n" ); + fprintf( pErr, "\t-a : toggles area recovery [default = %s]\n", fRecovery? "yes": "no" ); + fprintf( pErr, "\t-p : optimizes power by minimizing switching activity [default = %s]\n", fSwitching? "yes": "no" ); + fprintf( pErr, "\t-l : optimizes latch paths for delay, other paths for area [default = %s]\n", fLatchPaths? "yes": "no" ); + fprintf( pErr, "\t-D float : sets the required time for the mapping [default = %s]\n", Buffer ); + fprintf( pErr, "\t-K num : the number of LUT inputs (2 < num < 11) [default = %s]%s\n", LutSize, (nLutSize == -1 ? " (type \"print_lut\")" : "") ); + fprintf( pErr, "\t-v : toggles verbose output [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : prints the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandFpgaFast( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + char Buffer[100]; + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c; + int fRecovery; + int fVerbose; + int nLutSize; + float DelayTarget; + + extern Abc_Ntk_t * Abc_NtkFpgaFast( Abc_Ntk_t * pNtk, int nLutSize, int fRecovery, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fRecovery = 1; + fVerbose = 0; + DelayTarget =-1; + nLutSize = 5; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "avhDK" ) ) != EOF ) + { + switch ( c ) + { + case 'a': + fRecovery ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + case 'D': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-D\" should be followed by a floating point number.\n" ); + goto usage; + } + DelayTarget = (float)atof(argv[globalUtilOptind]); + globalUtilOptind++; + if ( DelayTarget <= 0.0 ) + goto usage; + break; + case 'K': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-K\" should be followed by a positive integer.\n" ); + goto usage; + } + nLutSize = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nLutSize < 0 ) + goto usage; + break; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( !Abc_NtkIsStrash(pNtk) ) + { + // strash and balance the network + pNtk = Abc_NtkStrash( pNtk, 0, 0, 0 ); + if ( pNtk == NULL ) + { + fprintf( pErr, "Strashing before FPGA mapping has failed.\n" ); + return 1; + } + pNtk = Abc_NtkBalance( pNtkRes = pNtk, 0, 0, 1 ); + Abc_NtkDelete( pNtkRes ); + if ( pNtk == NULL ) + { + fprintf( pErr, "Balancing before FPGA mapping has failed.\n" ); + return 1; + } + fprintf( pOut, "The network was strashed and balanced before FPGA mapping.\n" ); + // get the new network + pNtkRes = Abc_NtkFpgaFast( pNtk, nLutSize, fRecovery, fVerbose ); + if ( pNtkRes == NULL ) + { + Abc_NtkDelete( pNtk ); + fprintf( pErr, "FPGA mapping has failed.\n" ); + return 1; + } + Abc_NtkDelete( pNtk ); + } + else + { + // get the new network + pNtkRes = Abc_NtkFpgaFast( pNtk, nLutSize, fRecovery, fVerbose ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "FPGA mapping has failed.\n" ); + return 1; + } + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + if ( DelayTarget == -1 ) + sprintf( Buffer, "not used" ); + else + sprintf( Buffer, "%.2f", DelayTarget ); + fprintf( pErr, "usage: ffpga [-K num] [-avh]\n" ); + fprintf( pErr, "\t performs fast FPGA mapping of the current network\n" ); + fprintf( pErr, "\t-a : toggles area recovery [default = %s]\n", fRecovery? "yes": "no" ); +// fprintf( pErr, "\t-D float : sets the required time for the mapping [default = %s]\n", Buffer ); + fprintf( pErr, "\t-K num : the number of LUT inputs (2 < num < 32) [default = %d]\n", nLutSize ); + fprintf( pErr, "\t-v : toggles verbose output [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : prints the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandIf( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + char Buffer[100]; + char LutSize[100]; + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + If_Par_t Pars, * pPars = &Pars; + int c; + extern Abc_Ntk_t * Abc_NtkIf( Abc_Ntk_t * pNtk, If_Par_t * pPars ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + memset( pPars, 0, sizeof(If_Par_t) ); + // user-controlable paramters + pPars->nLutSize = -1; + pPars->nCutsMax = 8; + pPars->nFlowIters = 1; + pPars->nAreaIters = 2; + pPars->DelayTarget = -1; + pPars->fPreprocess = 1;// + pPars->fArea = 0; + pPars->fFancy = 0; + pPars->fExpRed = 1;// + pPars->fLatchPaths = 0; + pPars->fSeqMap = 0; + pPars->fVerbose = 0;// + // internal parameters + pPars->fTruth = 0; + pPars->nLatches = pNtk? Abc_NtkLatchNum(pNtk) : 0; + pPars->fLiftLeaves = 0; + pPars->pLutLib = Abc_FrameReadLibLut(); + pPars->pTimesArr = NULL; + pPars->pTimesArr = NULL; + pPars->pFuncCost = NULL; + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "KCFADpaflrstvh" ) ) != EOF ) + { + switch ( c ) + { + case 'K': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-K\" should be followed by a positive integer.\n" ); + goto usage; + } + pPars->nLutSize = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pPars->nLutSize < 0 ) + goto usage; + // if the LUT size is specified, disable library + pPars->pLutLib = NULL; + break; + case 'C': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-C\" should be followed by a positive integer.\n" ); + goto usage; + } + pPars->nCutsMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pPars->nCutsMax < 0 ) + goto usage; + break; + case 'F': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-F\" should be followed by a positive integer.\n" ); + goto usage; + } + pPars->nFlowIters = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pPars->nFlowIters < 0 ) + goto usage; + break; + case 'A': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-A\" should be followed by a positive integer.\n" ); + goto usage; + } + pPars->nAreaIters = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pPars->nAreaIters < 0 ) + goto usage; + break; + case 'D': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-D\" should be followed by a floating point number.\n" ); + goto usage; + } + pPars->DelayTarget = (float)atof(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pPars->DelayTarget <= 0.0 ) + goto usage; + break; + case 'p': + pPars->fPreprocess ^= 1; + break; + case 'a': + pPars->fArea ^= 1; + break; + case 'f': + pPars->fFancy ^= 1; + break; + case 'l': + pPars->fLatchPaths ^= 1; + break; + case 'r': + pPars->fExpRed ^= 1; + break; + case 's': + pPars->fSeqMap ^= 1; + break; + case 't': + pPars->fLiftLeaves ^= 1; + break; + case 'v': + pPars->fVerbose ^= 1; + break; + case 'h': + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( pPars->fSeqMap && pPars->nLatches == 0 ) + { + fprintf( pErr, "The network has no latches. Use combinational mapping instead of sequential.\n" ); + return 1; + } + + if ( pPars->nLutSize == -1 ) + { + if ( pPars->pLutLib == NULL ) + { + fprintf( pErr, "The LUT library is not given.\n" ); + return 1; + } + // get LUT size from the library + pPars->nLutSize = pPars->pLutLib->LutMax; + } + + if ( pPars->nLutSize < 3 || pPars->nLutSize > IF_MAX_LUTSIZE ) + { + fprintf( pErr, "Incorrect LUT size (%d).\n", pPars->nLutSize ); + return 1; + } + + if ( pPars->nCutsMax < 1 || pPars->nCutsMax >= (1<<12) ) + { + fprintf( pErr, "Incorrect number of cuts.\n" ); + return 1; + } + + if ( Abc_NtkGetChoiceNum( pNtk ) ) + { + printf( "Performing FPGA mapping with choices.\n" ); +// printf( "Currently mapping with choices is not enabled.\n" ); + pPars->fTruth = 1; +// return 1; + } + + if ( pPars->fTruth && pPars->nLutSize > IF_MAX_FUNC_LUTSIZE ) + { + fprintf( pErr, "Mapping with choices requires computing truth tables. In this case, the LUT size cannot be more than %d.\n", IF_MAX_FUNC_LUTSIZE ); + return 1; + } + + if ( !Abc_NtkIsStrash(pNtk) ) + { + // strash and balance the network + pNtk = Abc_NtkStrash( pNtk, 0, 0, 0 ); + if ( pNtk == NULL ) + { + fprintf( pErr, "Strashing before FPGA mapping has failed.\n" ); + return 1; + } + pNtk = Abc_NtkBalance( pNtkRes = pNtk, 0, 0, 1 ); + Abc_NtkDelete( pNtkRes ); + if ( pNtk == NULL ) + { + fprintf( pErr, "Balancing before FPGA mapping has failed.\n" ); + return 1; + } + fprintf( pOut, "The network was strashed and balanced before FPGA mapping.\n" ); + // get the new network + pNtkRes = Abc_NtkIf( pNtk, pPars ); + if ( pNtkRes == NULL ) + { + Abc_NtkDelete( pNtk ); + fprintf( pErr, "FPGA mapping has failed.\n" ); + return 0; + } + Abc_NtkDelete( pNtk ); + } + else + { + // get the new network + pNtkRes = Abc_NtkIf( pNtk, pPars ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "FPGA mapping has failed.\n" ); + return 0; + } + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + if ( pPars->DelayTarget == -1 ) + sprintf( Buffer, "best possible" ); + else + sprintf( Buffer, "%.2f", pPars->DelayTarget ); + if ( pPars->nLutSize == -1 ) + sprintf( LutSize, "library" ); + else + sprintf( LutSize, "%d", pPars->nLutSize ); + fprintf( pErr, "usage: if [-K num] [-C num] [-F num] [-A num] [-D float] [-pafrsvh]\n" ); + fprintf( pErr, "\t performs FPGA technology mapping of the network\n" ); + fprintf( pErr, "\t-K num : the number of LUT inputs (2 < num < %d) [default = %s]\n", IF_MAX_LUTSIZE+1, LutSize ); + fprintf( pErr, "\t-C num : the max number of priority cuts (0 < num < 2^12) [default = %d]\n", pPars->nCutsMax ); + fprintf( pErr, "\t-F num : the number of area flow recovery iterations (num >= 0) [default = %d]\n", pPars->nFlowIters ); + fprintf( pErr, "\t-A num : the number of exact area recovery iterations (num >= 0) [default = %d]\n", pPars->nAreaIters ); + fprintf( pErr, "\t-D float : sets the delay constraint for the mapping [default = %s]\n", Buffer ); + fprintf( pErr, "\t-p : toggles preprocessing using several starting points [default = %s]\n", pPars->fPreprocess? "yes": "no" ); + fprintf( pErr, "\t-a : toggles area-oriented mapping [default = %s]\n", pPars->fArea? "yes": "no" ); +// fprintf( pErr, "\t-f : toggles one fancy feature [default = %s]\n", pPars->fFancy? "yes": "no" ); + fprintf( pErr, "\t-r : enables expansion/reduction of the best cuts [default = %s]\n", pPars->fExpRed? "yes": "no" ); + fprintf( pErr, "\t-l : optimizes latch paths for delay, other paths for area [default = %s]\n", pPars->fLatchPaths? "yes": "no" ); + fprintf( pErr, "\t-s : toggles sequential mapping [default = %s]\n", pPars->fSeqMap? "yes": "no" ); +// fprintf( pErr, "\t-t : toggles the use of true sequential cuts [default = %s]\n", pPars->fLiftLeaves? "yes": "no" ); + fprintf( pErr, "\t-v : toggles verbose output [default = %s]\n", pPars->fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : prints the command usage\n"); + return 1; +} + + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandInit( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + Abc_Obj_t * pObj; + int c, i; + int fZeros; + int fOnes; + int fRandom; + int fDontCare; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fZeros = 0; + fOnes = 0; + fRandom = 0; + fDontCare = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "zordh" ) ) != EOF ) + { + switch ( c ) + { + case 'z': + fZeros ^= 1; + break; + case 'o': + fOnes ^= 1; + break; + case 'r': + fRandom ^= 1; + break; + case 'd': + fDontCare ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( Abc_NtkIsComb(pNtk) ) + { + fprintf( pErr, "The current network is combinational.\n" ); + return 0; + } + + if ( fZeros ) + { + Abc_NtkForEachLatch( pNtk, pObj, i ) + Abc_LatchSetInit0( pObj ); + } + else if ( fOnes ) + { + Abc_NtkForEachLatch( pNtk, pObj, i ) + Abc_LatchSetInit1( pObj ); + } + else if ( fRandom ) + { + Abc_NtkForEachLatch( pNtk, pObj, i ) + if ( rand() & 1 ) + Abc_LatchSetInit1( pObj ); + else + Abc_LatchSetInit0( pObj ); + } + else if ( fDontCare ) + { + Abc_NtkForEachLatch( pNtk, pObj, i ) + Abc_LatchSetInitDc( pObj ); + } + else + printf( "The initial states remain unchanged.\n" ); + return 0; + +usage: + fprintf( pErr, "usage: init [-zordh]\n" ); + fprintf( pErr, "\t resets initial states of all latches\n" ); + fprintf( pErr, "\t-z : set zeros initial states [default = %s]\n", fZeros? "yes": "no" ); + fprintf( pErr, "\t-o : set ones initial states [default = %s]\n", fOnes? "yes": "no" ); + fprintf( pErr, "\t-d : set don't-care initial states [default = %s]\n", fDontCare? "yes": "no" ); + fprintf( pErr, "\t-r : set random initial states [default = %s]\n", fRandom? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandZero( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c; + + extern Abc_Ntk_t * Abc_NtkRestrashZero( Abc_Ntk_t * pNtk, bool fCleanup ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( Abc_NtkIsComb(pNtk) ) + { + fprintf( pErr, "The current network is combinational.\n" ); + return 0; + } + + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "This command works only for AIGs.\n" ); + return 0; + } + + // get the new network + pNtkRes = Abc_NtkRestrashZero( pNtk, 0 ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Converting to sequential AIG has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: zero [-h]\n" ); + fprintf( pErr, "\t converts latches to have const-0 initial value\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandPipe( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int nLatches; + extern void Abc_NtkLatchPipe( Abc_Ntk_t * pNtk, int nLatches ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nLatches = 5; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Lh" ) ) != EOF ) + { + switch ( c ) + { + case 'L': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-L\" should be followed by a positive integer.\n" ); + goto usage; + } + nLatches = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nLatches < 0 ) + goto usage; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( Abc_NtkIsComb(pNtk) ) + { + fprintf( pErr, "The current network is combinational.\n" ); + return 1; + } + + // update the network + Abc_NtkLatchPipe( pNtk, nLatches ); + return 0; + +usage: + fprintf( pErr, "usage: pipe [-L num] [-h]\n" ); + fprintf( pErr, "\t inserts the given number of latches at each PI for pipelining\n" ); + fprintf( pErr, "\t-L num : the number of latches to insert [default = %d]\n", nLatches ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandSeq( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( Abc_NtkLatchNum(pNtk) == 0 ) + { + fprintf( pErr, "The network has no latches.\n" ); + return 0; + } + + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "Conversion to sequential AIG works only for combinational AIGs (run \"strash\").\n" ); + return 1; + } + + // get the new network +// pNtkRes = Abc_NtkAigToSeq( pNtk ); + pNtkRes = NULL; + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Converting to sequential AIG has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: seq [-h]\n" ); + fprintf( pErr, "\t converts AIG into sequential AIG\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandUnseq( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c; + int fShare; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fShare = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "sh" ) ) != EOF ) + { + switch ( c ) + { + case 's': + fShare ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } +/* + if ( !Abc_NtkIsSeq(pNtk) ) + { + fprintf( pErr, "Conversion to combinational AIG works only for sequential AIG (run \"seq\").\n" ); + return 1; + } +*/ + // share the latches on the fanout edges +// if ( fShare ) +// Seq_NtkShareFanouts(pNtk); + + // get the new network +// pNtkRes = Abc_NtkSeqToLogicSop( pNtk ); + pNtkRes = NULL; + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Converting sequential AIG into an SOP logic network has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: unseq [-sh]\n" ); + fprintf( pErr, "\t converts sequential AIG into an SOP logic network\n" ); + fprintf( pErr, "\t-s : toggle sharing latches [default = %s]\n", fShare? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandRetime( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c, nMaxIters; + int fForward; + int fBackward; + int fVerbose; + int Mode; + extern int Abc_NtkRetime( Abc_Ntk_t * pNtk, int Mode, int fForwardOnly, int fBackwardOnly, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Mode = 5; + fForward = 0; + fBackward = 0; + fVerbose = 0; + nMaxIters = 15; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Mfbvh" ) ) != EOF ) + { + switch ( c ) + { + case 'M': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-M\" should be followed by a positive integer.\n" ); + goto usage; + } + Mode = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( Mode < 0 ) + goto usage; + break; + case 'f': + fForward ^= 1; + break; + case 'b': + fBackward ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( fForward && fBackward ) + { + fprintf( pErr, "Only one switch \"-f\" or \"-b\" can be selected at a time.\n" ); + return 1; + } + + if ( !Abc_NtkLatchNum(pNtk) ) + { + fprintf( pErr, "The network has no latches. Retiming is not performed.\n" ); + return 0; + } + + if ( Mode < 0 || Mode > 6 ) + { + fprintf( pErr, "The mode (%d) is incorrect. Retiming is not performed.\n", Mode ); + return 0; + } + + if ( Abc_NtkIsStrash(pNtk) ) + { + if ( Abc_NtkGetChoiceNum(pNtk) ) + { + fprintf( pErr, "Retiming with choice nodes is not implemented.\n" ); + return 0; + } + // convert the network into an SOP network + pNtkRes = Abc_NtkToLogic( pNtk ); + // perform the retiming + Abc_NtkRetime( pNtkRes, Mode, fForward, fBackward, fVerbose ); + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + } + + // get the network in the SOP form + if ( !Abc_NtkToSop(pNtk, 0) ) + { + printf( "Converting to SOPs has failed.\n" ); + return 0; + } + + if ( !Abc_NtkIsLogic(pNtk) ) + { + fprintf( pErr, "The network is not a logic network. Retiming is not performed.\n" ); + return 0; + } + + // perform the retiming + Abc_NtkRetime( pNtk, Mode, fForward, fBackward, fVerbose ); + return 0; + +usage: + fprintf( pErr, "usage: retime [-M num] [-fbvh]\n" ); + fprintf( pErr, "\t retimes the current network using one of the algorithms:\n" ); + fprintf( pErr, "\t 1: most forward retiming\n" ); + fprintf( pErr, "\t 2: most backward retiming\n" ); + fprintf( pErr, "\t 3: forward and backward min-area retiming\n" ); + fprintf( pErr, "\t 4: forward and backward min-delay retiming\n" ); + fprintf( pErr, "\t 5: mode 3 followed by mode 4\n" ); + fprintf( pErr, "\t 6: Pan's optimum-delay retiming using binary search\n" ); + fprintf( pErr, "\t-M num : the retiming algorithm to use [default = %d]\n", Mode ); + fprintf( pErr, "\t-f : enables forward-only retiming in modes 3,4,5 [default = %s]\n", fForward? "yes": "no" ); + fprintf( pErr, "\t-b : enables backward-only retiming in modes 3,4,5 [default = %s]\n", fBackward? "yes": "no" ); + fprintf( pErr, "\t-v : enables verbose output [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +// fprintf( pErr, "\t-I num : max number of iterations of l-value computation [default = %d]\n", nMaxIters ); +// fprintf( pErr, "\t-f : toggle forward retiming (for AIGs) [default = %s]\n", fForward? "yes": "no" ); +// fprintf( pErr, "\t-b : toggle backward retiming (for AIGs) [default = %s]\n", fBackward? "yes": "no" ); +// fprintf( pErr, "\t-i : toggle computation of initial state [default = %s]\n", fInitial? "yes": "no" ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandSeqFpga( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkNew, * pNtkRes; + int c, nMaxIters; + int fVerbose; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nMaxIters = 15; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Ivh" ) ) != EOF ) + { + switch ( c ) + { + case 'I': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-I\" should be followed by a positive integer.\n" ); + goto usage; + } + nMaxIters = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nMaxIters < 0 ) + goto usage; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( Abc_NtkHasAig(pNtk) ) + { +/* + // quit if there are choice nodes + if ( Abc_NtkGetChoiceNum(pNtk) ) + { + fprintf( pErr, "Currently cannot map/retime networks with choice nodes.\n" ); + return 0; + } +*/ +// if ( Abc_NtkIsStrash(pNtk) ) +// pNtkNew = Abc_NtkAigToSeq(pNtk); +// else +// pNtkNew = Abc_NtkDup(pNtk); + pNtkNew = NULL; + } + else + { + // strash and balance the network + pNtkNew = Abc_NtkStrash( pNtk, 0, 0, 0 ); + if ( pNtkNew == NULL ) + { + fprintf( pErr, "Strashing before FPGA mapping/retiming has failed.\n" ); + return 1; + } + + pNtkNew = Abc_NtkBalance( pNtkRes = pNtkNew, 0, 0, 1 ); + Abc_NtkDelete( pNtkRes ); + if ( pNtkNew == NULL ) + { + fprintf( pErr, "Balancing before FPGA mapping has failed.\n" ); + return 1; + } + + // convert into a sequential AIG +// pNtkNew = Abc_NtkAigToSeq( pNtkRes = pNtkNew ); + pNtkNew = NULL; + Abc_NtkDelete( pNtkRes ); + if ( pNtkNew == NULL ) + { + fprintf( pErr, "Converting into a seq AIG before FPGA mapping/retiming has failed.\n" ); + return 1; + } + + fprintf( pOut, "The network was strashed and balanced before FPGA mapping/retiming.\n" ); + } + + // get the new network +// pNtkRes = Seq_NtkFpgaMapRetime( pNtkNew, nMaxIters, fVerbose ); + pNtkRes = NULL; + if ( pNtkRes == NULL ) + { +// fprintf( pErr, "Sequential FPGA mapping has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return 0; + } + Abc_NtkDelete( pNtkNew ); + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: sfpga [-I num] [-vh]\n" ); + fprintf( pErr, "\t performs integrated sequential FPGA mapping/retiming\n" ); + fprintf( pErr, "\t-I num : max number of iterations of l-value computation [default = %d]\n", nMaxIters ); + fprintf( pErr, "\t-v : toggle verbose output [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandSeqMap( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkNew, * pNtkRes; + int c, nMaxIters; + int fVerbose; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nMaxIters = 15; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Ivh" ) ) != EOF ) + { + switch ( c ) + { + case 'I': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-I\" should be followed by a positive integer.\n" ); + goto usage; + } + nMaxIters = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nMaxIters < 0 ) + goto usage; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( Abc_NtkHasAig(pNtk) ) + { +/* + // quit if there are choice nodes + if ( Abc_NtkGetChoiceNum(pNtk) ) + { + fprintf( pErr, "Currently cannot map/retime networks with choice nodes.\n" ); + return 0; + } +*/ +// if ( Abc_NtkIsStrash(pNtk) ) +// pNtkNew = Abc_NtkAigToSeq(pNtk); +// else +// pNtkNew = Abc_NtkDup(pNtk); + pNtkNew = NULL; + } + else + { + // strash and balance the network + pNtkNew = Abc_NtkStrash( pNtk, 0, 0, 0 ); + if ( pNtkNew == NULL ) + { + fprintf( pErr, "Strashing before SC mapping/retiming has failed.\n" ); + return 1; + } + + pNtkNew = Abc_NtkBalance( pNtkRes = pNtkNew, 0, 0, 1 ); + Abc_NtkDelete( pNtkRes ); + if ( pNtkNew == NULL ) + { + fprintf( pErr, "Balancing before SC mapping/retiming has failed.\n" ); + return 1; + } + + // convert into a sequential AIG +// pNtkNew = Abc_NtkAigToSeq( pNtkRes = pNtkNew ); + pNtkNew = NULL; + Abc_NtkDelete( pNtkRes ); + if ( pNtkNew == NULL ) + { + fprintf( pErr, "Converting into a seq AIG before SC mapping/retiming has failed.\n" ); + return 1; + } + + fprintf( pOut, "The network was strashed and balanced before SC mapping/retiming.\n" ); + } + + // get the new network +// pNtkRes = Seq_MapRetime( pNtkNew, nMaxIters, fVerbose ); + pNtkRes = NULL; + if ( pNtkRes == NULL ) + { +// fprintf( pErr, "Sequential FPGA mapping has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return 0; + } + Abc_NtkDelete( pNtkNew ); + + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: smap [-I num] [-vh]\n" ); + fprintf( pErr, "\t performs integrated sequential standard-cell mapping/retiming\n" ); + fprintf( pErr, "\t-I num : max number of iterations of l-value computation [default = %d]\n", nMaxIters ); + fprintf( pErr, "\t-v : toggle verbose output [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandSeqSweep( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkRes; + int c; + int nFramesK; + int fExdc; + int fImp; + int fRewrite; + int fVerbose; + extern Abc_Ntk_t * Abc_NtkSeqSweep( Abc_Ntk_t * pNtk, int nFrames, int fRewrite, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nFramesK = 1; + fExdc = 1; + fImp = 0; + fRewrite = 0; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Feirvh" ) ) != EOF ) + { + switch ( c ) + { + case 'F': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-F\" should be followed by an integer.\n" ); + goto usage; + } + nFramesK = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nFramesK <= 0 ) + goto usage; + break; + case 'e': + fExdc ^= 1; + break; + case 'i': + fImp ^= 1; + break; + case 'r': + fRewrite ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( Abc_NtkIsComb(pNtk) ) + { + fprintf( pErr, "The network is combinational (run \"fraig\" or \"fraig_sweep\").\n" ); + return 1; + } + + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "Sequential sweep works only for structurally hashed networks (run \"strash\").\n" ); + return 1; + } + + // get the new network + pNtkRes = Abc_NtkSeqSweep( pNtk, nFramesK, fRewrite, fVerbose ); + if ( pNtkRes == NULL ) + { + fprintf( pErr, "Sequential sweeping has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkRes ); + return 0; + +usage: + fprintf( pErr, "usage: ssweep [-F num] [-rvh]\n" ); + fprintf( pErr, "\t performs sequential sweep using K-step induction\n" ); + fprintf( pErr, "\t-F num : number of time frames for induction (1=simple) [default = %d]\n", nFramesK ); +// fprintf( pErr, "\t-e : toggle writing EXDC network [default = %s]\n", fExdc? "yes": "no" ); +// fprintf( pErr, "\t-i : toggle computing implications [default = %s]\n", fImp? "yes": "no" ); + fprintf( pErr, "\t-r : toggle AIG rewriting [default = %s]\n", fRewrite? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose output [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandSeqCleanup( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int fLatchSweep; + int fAutoSweep; + int fVerbose; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fLatchSweep = 0; + fAutoSweep = 0; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "lavh" ) ) != EOF ) + { + switch ( c ) + { + case 'l': + fLatchSweep ^= 1; + break; + case 'a': + fAutoSweep ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsLogic(pNtk) ) + { + fprintf( pErr, "Only works for logic networks.\n" ); + return 1; + } + // modify the current network + Abc_NtkCleanupSeq( pNtk, fLatchSweep, fAutoSweep, fVerbose ); + return 0; + +usage: + fprintf( pErr, "usage: scleanup [-lavh]\n" ); + fprintf( pErr, "\t performs sequential cleanup\n" ); + fprintf( pErr, "\t - removes nodes/latches that do not feed into POs\n" ); + fprintf( pErr, "\t - removes and shared latches driven by constants\n" ); + fprintf( pErr, "\t - replaces autonomous logic by free PI variables\n" ); + fprintf( pErr, "\t (the latter may change sequential behaviour)\n" ); + fprintf( pErr, "\t-l : toggle sweeping latches [default = %s]\n", fLatchSweep? "yes": "no" ); + fprintf( pErr, "\t-a : toggle removing autonomous logic [default = %s]\n", fAutoSweep? "yes": "no" ); + fprintf( pErr, "\t-v : toggle verbose output [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandCycle( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int nFrames; + int fVerbose; + extern void Abc_NtkCycleInitState( Abc_Ntk_t * pNtk, int nFrames, int fVerbose ); + extern void Abc_NtkCycleInitStateSop( Abc_Ntk_t * pNtk, int nFrames, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nFrames = 100; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Fvh" ) ) != EOF ) + { + switch ( c ) + { + case 'F': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-F\" should be followed by an integer.\n" ); + goto usage; + } + nFrames = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nFrames < 0 ) + goto usage; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( !Abc_NtkIsStrash(pNtk) && !Abc_NtkIsSopLogic(pNtk) ) + { + fprintf( pErr, "Only works for strashed networks or logic SOP networks.\n" ); + return 1; + } + + if ( Abc_NtkIsStrash(pNtk) ) + Abc_NtkCycleInitState( pNtk, nFrames, fVerbose ); + else + Abc_NtkCycleInitStateSop( pNtk, nFrames, fVerbose ); + return 0; + +usage: + fprintf( pErr, "usage: cycle [-F num] [-vh]\n" ); + fprintf( pErr, "\t cycles sequiential circuit for the given number of timeframes\n" ); + fprintf( pErr, "\t to derive a new initial state (which may be on the envelope)\n" ); + fprintf( pErr, "\t-F num : the number of frames to simulate [default = %d]\n", nFrames ); + fprintf( pErr, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandXsim( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int nFrames; + int fInputs; + int fVerbose; + extern void Abc_NtkXValueSimulate( Abc_Ntk_t * pNtk, int nFrames, int fInputs, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nFrames = 10; + fInputs = 0; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Fivh" ) ) != EOF ) + { + switch ( c ) + { + case 'F': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-F\" should be followed by an integer.\n" ); + goto usage; + } + nFrames = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nFrames < 0 ) + goto usage; + break; + case 'i': + fInputs ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "Only works for strashed networks.\n" ); + return 1; + } + + Abc_NtkXValueSimulate( pNtk, nFrames, fInputs, fVerbose ); + return 0; + +usage: + fprintf( pErr, "usage: xsim [-F num] [-ivh]\n" ); + fprintf( pErr, "\t performs X-valued simulation of the AIG\n" ); + fprintf( pErr, "\t-F num : the number of frames to simulate [default = %d]\n", nFrames ); + fprintf( pErr, "\t-i : toggle X-valued state or X-valued inputs [default = %s]\n", fInputs? "inputs": "state" ); + fprintf( pErr, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandCec( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + char Buffer[16]; + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtk1, * pNtk2; + int fDelete1, fDelete2; + char ** pArgvNew; + int nArgcNew; + int c; + int fSat; + int fVerbose; + int nSeconds; + int nPartSize; + int nConfLimit; + int nInsLimit; + int fPartition; + + extern void Abc_NtkCecSat( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int nConfLimit, int nInsLimit ); + extern void Abc_NtkCecFraig( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int nSeconds, int fVerbose ); + extern void Abc_NtkCecFraigPart( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int nSeconds, int nPartSize, int fVerbose ); + extern void Abc_NtkCecFraigPartAuto( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int nSeconds, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fSat = 0; + fVerbose = 0; + nSeconds = 20; + nPartSize = 0; + nConfLimit = 10000; + nInsLimit = 0; + fPartition = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "TCIPpsvh" ) ) != EOF ) + { + switch ( c ) + { + case 'T': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-T\" should be followed by an integer.\n" ); + goto usage; + } + nSeconds = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nSeconds < 0 ) + goto usage; + break; + case 'C': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-C\" should be followed by an integer.\n" ); + goto usage; + } + nConfLimit = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nConfLimit < 0 ) + goto usage; + break; + case 'I': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-I\" should be followed by an integer.\n" ); + goto usage; + } + nInsLimit = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nInsLimit < 0 ) + goto usage; + break; + case 'P': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-P\" should be followed by an integer.\n" ); + goto usage; + } + nPartSize = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nPartSize < 0 ) + goto usage; + break; + case 'p': + fPartition ^= 1; + break; + case 's': + fSat ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + default: + goto usage; + } + } + + pArgvNew = argv + globalUtilOptind; + nArgcNew = argc - globalUtilOptind; + if ( !Abc_NtkPrepareTwoNtks( pErr, pNtk, pArgvNew, nArgcNew, &pNtk1, &pNtk2, &fDelete1, &fDelete2 ) ) + return 1; + + // perform equivalence checking + if ( fPartition ) + Abc_NtkCecFraigPartAuto( pNtk1, pNtk2, nSeconds, fVerbose ); + else if ( nPartSize ) + Abc_NtkCecFraigPart( pNtk1, pNtk2, nSeconds, nPartSize, fVerbose ); + else if ( fSat ) + Abc_NtkCecSat( pNtk1, pNtk2, nConfLimit, nInsLimit ); + else + Abc_NtkCecFraig( pNtk1, pNtk2, nSeconds, fVerbose ); + + if ( fDelete1 ) Abc_NtkDelete( pNtk1 ); + if ( fDelete2 ) Abc_NtkDelete( pNtk2 ); + return 0; + +usage: + if ( nPartSize == 0 ) + strcpy( Buffer, "unused" ); + else + sprintf( Buffer, "%d", nPartSize ); + fprintf( pErr, "usage: cec [-T num] [-C num] [-I num] [-P num] [-psvh] \n" ); + fprintf( pErr, "\t performs combinational equivalence checking\n" ); + fprintf( pErr, "\t-T num : approximate runtime limit in seconds [default = %d]\n", nSeconds ); + fprintf( pErr, "\t-C num : limit on the number of conflicts [default = %d]\n", nConfLimit ); + fprintf( pErr, "\t-I num : limit on the number of clause inspections [default = %d]\n", nInsLimit ); + fprintf( pErr, "\t-P num : partition size for multi-output networks [default = %s]\n", Buffer ); + fprintf( pErr, "\t-p : toggle automatic partitioning [default = %s]\n", fPartition? "yes": "no" ); + fprintf( pErr, "\t-s : toggle \"SAT only\" and \"FRAIG + SAT\" [default = %s]\n", fSat? "SAT only": "FRAIG + SAT" ); + fprintf( pErr, "\t-v : toggles verbose output [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + fprintf( pErr, "\tfile1 : (optional) the file with the first network\n"); + fprintf( pErr, "\tfile2 : (optional) the file with the second network\n"); + fprintf( pErr, "\t if no files are given, uses the current network and its spec\n"); + fprintf( pErr, "\t if one file is given, uses the current network and the file\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandSec( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtk1, * pNtk2; + int fDelete1, fDelete2; + char ** pArgvNew; + int nArgcNew; + int c; + int fRetime; + int fSat; + int fVerbose; + int nFrames; + int nSeconds; + int nConfLimit; + int nInsLimit; + + extern void Abc_NtkSecSat( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int nConfLimit, int nInsLimit, int nFrames ); + extern int Abc_NtkSecFraig( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int nSeconds, int nFrames, int fVerbose ); + extern void Abc_NtkSecRetime( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2 ); + + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fRetime = 0; // verification after retiming + fSat = 0; + fVerbose = 0; + nFrames = 5; + nSeconds = 20; + nConfLimit = 10000; + nInsLimit = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "FTCIsrvh" ) ) != EOF ) + { + switch ( c ) + { + case 'F': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-F\" should be followed by an integer.\n" ); + goto usage; + } + nFrames = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nFrames <= 0 ) + goto usage; + break; + case 'T': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-T\" should be followed by an integer.\n" ); + goto usage; + } + nSeconds = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nSeconds < 0 ) + goto usage; + break; + case 'C': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-C\" should be followed by an integer.\n" ); + goto usage; + } + nConfLimit = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nConfLimit < 0 ) + goto usage; + break; + case 'I': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-I\" should be followed by an integer.\n" ); + goto usage; + } + nInsLimit = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nInsLimit < 0 ) + goto usage; + break; + case 'r': + fRetime ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 's': + fSat ^= 1; + break; + default: + goto usage; + } + } + + pArgvNew = argv + globalUtilOptind; + nArgcNew = argc - globalUtilOptind; + if ( !Abc_NtkPrepareTwoNtks( pErr, pNtk, pArgvNew, nArgcNew, &pNtk1, &pNtk2, &fDelete1, &fDelete2 ) ) + return 1; + + if ( Abc_NtkLatchNum(pNtk1) == 0 || Abc_NtkLatchNum(pNtk2) == 0 ) + { + printf( "The network has no latches. Used combinational command \"cec\".\n" ); + return 0; + } + + // perform equivalence checking + if ( fRetime ) + Abc_NtkSecRetime( pNtk1, pNtk2 ); + else if ( fSat ) + Abc_NtkSecSat( pNtk1, pNtk2, nConfLimit, nInsLimit, nFrames ); + else + Abc_NtkSecFraig( pNtk1, pNtk2, nSeconds, nFrames, fVerbose ); + + if ( fDelete1 ) Abc_NtkDelete( pNtk1 ); + if ( fDelete2 ) Abc_NtkDelete( pNtk2 ); + return 0; + +usage: + fprintf( pErr, "usage: sec [-F num] [-T num] [-C num] [-I num] [-srvh] \n" ); + fprintf( pErr, "\t performs bounded sequential equivalence checking\n" ); + fprintf( pErr, "\t-s : toggle \"SAT only\" and \"FRAIG + SAT\" [default = %s]\n", fSat? "SAT only": "FRAIG + SAT" ); + fprintf( pErr, "\t-r : toggles retiming verification [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-v : toggles verbose output [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + fprintf( pErr, "\t-F num : the number of time frames to use [default = %d]\n", nFrames ); + fprintf( pErr, "\t-T num : approximate runtime limit in seconds [default = %d]\n", nSeconds ); + fprintf( pErr, "\t-C num : limit on the number of conflicts [default = %d]\n", nConfLimit ); + fprintf( pErr, "\t-I num : limit on the number of inspections [default = %d]\n", nInsLimit ); + fprintf( pErr, "\tfile1 : (optional) the file with the first network\n"); + fprintf( pErr, "\tfile2 : (optional) the file with the second network\n"); + fprintf( pErr, "\t if no files are given, uses the current network and its spec\n"); + fprintf( pErr, "\t if one file is given, uses the current network and the file\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandDSec( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtk1, * pNtk2; + int fDelete1, fDelete2; + char ** pArgvNew; + int nArgcNew; + int c; + int fVerbose; + int fVeryVerbose; + int nFrames; + + extern int Abc_NtkDarSec( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int nFrames, int fVerbose, int fVeryVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + nFrames = 0; // if 0, iterates through frames + fVerbose = 1; + fVeryVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Fwvh" ) ) != EOF ) + { + switch ( c ) + { + case 'F': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-F\" should be followed by an integer.\n" ); + goto usage; + } + nFrames = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nFrames <= 0 ) + goto usage; + break; + case 'w': + fVeryVerbose ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + default: + goto usage; + } + } + + pArgvNew = argv + globalUtilOptind; + nArgcNew = argc - globalUtilOptind; + if ( !Abc_NtkPrepareTwoNtks( pErr, pNtk, pArgvNew, nArgcNew, &pNtk1, &pNtk2, &fDelete1, &fDelete2 ) ) + return 1; + + if ( Abc_NtkLatchNum(pNtk1) == 0 || Abc_NtkLatchNum(pNtk2) == 0 ) + { + printf( "The network has no latches. Used combinational command \"cec\".\n" ); + return 0; + } + + // perform verification + Abc_NtkDarSec( pNtk1, pNtk2, nFrames, fVerbose, fVeryVerbose ); + + if ( fDelete1 ) Abc_NtkDelete( pNtk1 ); + if ( fDelete2 ) Abc_NtkDelete( pNtk2 ); + return 0; + +usage: + fprintf( pErr, "usage: dsec [-F num] [-wvh] \n" ); + fprintf( pErr, "\t performs inductive sequential equivalence checking\n" ); + fprintf( pErr, "\t-F num : the number of time frames to use [default = %d]\n", nFrames ); + fprintf( pErr, "\t-w : toggles additional verbose output [default = %s]\n", fVeryVerbose? "yes": "no" ); + fprintf( pErr, "\t-v : toggles verbose output [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + fprintf( pErr, "\tfile1 : (optional) the file with the first network\n"); + fprintf( pErr, "\tfile2 : (optional) the file with the second network\n"); + fprintf( pErr, "\t if no files are given, uses the current network and its spec\n"); + fprintf( pErr, "\t if one file is given, uses the current network and the file\n"); + return 1; +} +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandSat( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int RetValue; + int fVerbose; + int nConfLimit; + int nInsLimit; + int clk; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fVerbose = 0; + nConfLimit = 100000; + nInsLimit = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "CIvh" ) ) != EOF ) + { + switch ( c ) + { + case 'C': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-C\" should be followed by an integer.\n" ); + goto usage; + } + nConfLimit = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nConfLimit < 0 ) + goto usage; + break; + case 'I': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-I\" should be followed by an integer.\n" ); + goto usage; + } + nInsLimit = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nInsLimit < 0 ) + goto usage; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( Abc_NtkLatchNum(pNtk) > 0 ) + { + fprintf( stdout, "Currently can only solve the miter for combinational circuits.\n" ); + return 0; + } + + clk = clock(); + if ( Abc_NtkIsStrash(pNtk) ) + { + RetValue = Abc_NtkMiterSat( pNtk, (sint64)nConfLimit, (sint64)nInsLimit, fVerbose, NULL, NULL ); + } + else + { + assert( Abc_NtkIsLogic(pNtk) ); + Abc_NtkToBdd( pNtk ); + RetValue = Abc_NtkMiterSat( pNtk, (sint64)nConfLimit, (sint64)nInsLimit, fVerbose, NULL, NULL ); + } + + // verify that the pattern is correct + if ( RetValue == 0 && Abc_NtkPoNum(pNtk) == 1 ) + { + //int i; + //Abc_Obj_t * pObj; + int * pSimInfo = Abc_NtkVerifySimulatePattern( pNtk, pNtk->pModel ); + if ( pSimInfo[0] != 1 ) + printf( "ERROR in Abc_NtkMiterSat(): Generated counter example is invalid.\n" ); + free( pSimInfo ); + /* + // print model + Abc_NtkForEachPi( pNtk, pObj, i ) + { + printf( "%d", (int)(pNtk->pModel[i] > 0) ); + if ( i == 70 ) + break; + } + printf( "\n" ); + */ + } + + if ( RetValue == -1 ) + printf( "UNDECIDED " ); + else if ( RetValue == 0 ) + printf( "SATISFIABLE " ); + else + printf( "UNSATISFIABLE " ); + //printf( "\n" ); + PRT( "Time", clock() - clk ); + return 0; + +usage: + fprintf( pErr, "usage: sat [-C num] [-I num] [-vh]\n" ); + fprintf( pErr, "\t solves the combinational miter using SAT solver MiniSat-1.14\n" ); + fprintf( pErr, "\t derives CNF from the current network and leave it unchanged\n" ); + fprintf( pErr, "\t-C num : limit on the number of conflicts [default = %d]\n", nConfLimit ); + fprintf( pErr, "\t-I num : limit on the number of inspections [default = %d]\n", nInsLimit ); + fprintf( pErr, "\t-v : prints verbose information [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandDSat( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + int RetValue; + int fVerbose; + int nConfLimit; + int nInsLimit; + int clk; + + extern int Abc_NtkDSat( Abc_Ntk_t * pNtk, sint64 nConfLimit, sint64 nInsLimit, int fVerbose ); + + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fVerbose = 0; + nConfLimit = 100000; + nInsLimit = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "CIvh" ) ) != EOF ) + { + switch ( c ) + { + case 'C': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-C\" should be followed by an integer.\n" ); + goto usage; + } + nConfLimit = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nConfLimit < 0 ) + goto usage; + break; + case 'I': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-I\" should be followed by an integer.\n" ); + goto usage; + } + nInsLimit = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nInsLimit < 0 ) + goto usage; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( Abc_NtkLatchNum(pNtk) > 0 ) + { + fprintf( stdout, "Currently can only solve the miter for combinational circuits.\n" ); + return 0; + } + if ( Abc_NtkPoNum(pNtk) != 1 ) + { + fprintf( stdout, "Currently expects a single-output miter.\n" ); + return 0; + } + + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( stdout, "Currently only works for structurally hashed circuits.\n" ); + return 0; + } + + clk = clock(); + RetValue = Abc_NtkDSat( pNtk, (sint64)nConfLimit, (sint64)nInsLimit, fVerbose ); + // verify that the pattern is correct + if ( RetValue == 0 && Abc_NtkPoNum(pNtk) == 1 ) + { + //int i; + //Abc_Obj_t * pObj; + int * pSimInfo = Abc_NtkVerifySimulatePattern( pNtk, pNtk->pModel ); + if ( pSimInfo[0] != 1 ) + printf( "ERROR in Abc_NtkMiterSat(): Generated counter example is invalid.\n" ); + free( pSimInfo ); + /* + // print model + Abc_NtkForEachPi( pNtk, pObj, i ) + { + printf( "%d", (int)(pNtk->pModel[i] > 0) ); + if ( i == 70 ) + break; + } + printf( "\n" ); + */ + } + + if ( RetValue == -1 ) + printf( "UNDECIDED " ); + else if ( RetValue == 0 ) + printf( "SATISFIABLE " ); + else + printf( "UNSATISFIABLE " ); + //printf( "\n" ); + PRT( "Time", clock() - clk ); + return 0; + +usage: + fprintf( pErr, "usage: dsat [-C num] [-I num] [-vh]\n" ); + fprintf( pErr, "\t solves the combinational miter using SAT solver MiniSat-1.14\n" ); + fprintf( pErr, "\t derives CNF from the current network and leave it unchanged\n" ); + fprintf( pErr, "\t-C num : limit on the number of conflicts [default = %d]\n", nConfLimit ); + fprintf( pErr, "\t-I num : limit on the number of inspections [default = %d]\n", nInsLimit ); + fprintf( pErr, "\t-v : prints verbose information [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandProve( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkTemp; + Prove_Params_t Params, * pParams = &Params; + int c, clk, RetValue; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Prove_ParamsSetDefault( pParams ); + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "NCFLIrfbvh" ) ) != EOF ) + { + switch ( c ) + { + case 'N': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-N\" should be followed by an integer.\n" ); + goto usage; + } + pParams->nItersMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pParams->nItersMax < 0 ) + goto usage; + break; + case 'C': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-C\" should be followed by an integer.\n" ); + goto usage; + } + pParams->nMiteringLimitStart = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pParams->nMiteringLimitStart < 0 ) + goto usage; + break; + case 'F': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-F\" should be followed by an integer.\n" ); + goto usage; + } + pParams->nFraigingLimitStart = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pParams->nFraigingLimitStart < 0 ) + goto usage; + break; + case 'L': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-L\" should be followed by an integer.\n" ); + goto usage; + } + pParams->nMiteringLimitLast = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pParams->nMiteringLimitLast < 0 ) + goto usage; + break; + case 'I': + if ( globalUtilOptind >= argc ) + { + fprintf( pErr, "Command line switch \"-I\" should be followed by an integer.\n" ); + goto usage; + } + pParams->nTotalInspectLimit = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( pParams->nTotalInspectLimit < 0 ) + goto usage; + break; + case 'r': + pParams->fUseRewriting ^= 1; + break; + case 'f': + pParams->fUseFraiging ^= 1; + break; + case 'b': + pParams->fUseBdds ^= 1; + break; + case 'v': + pParams->fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( Abc_NtkLatchNum(pNtk) > 0 ) + { + fprintf( stdout, "Currently can only solve the miter for combinational circuits.\n" ); + return 0; + } + if ( Abc_NtkCoNum(pNtk) != 1 ) + { + fprintf( stdout, "Currently can only solve the miter with one output.\n" ); + return 0; + } + clk = clock(); + + if ( Abc_NtkIsStrash(pNtk) ) + pNtkTemp = Abc_NtkDup( pNtk ); + else + pNtkTemp = Abc_NtkStrash( pNtk, 0, 0, 0 ); + + RetValue = Abc_NtkMiterProve( &pNtkTemp, pParams ); + + // verify that the pattern is correct + if ( RetValue == 0 ) + { + int * pSimInfo = Abc_NtkVerifySimulatePattern( pNtk, pNtkTemp->pModel ); + if ( pSimInfo[0] != 1 ) + printf( "ERROR in Abc_NtkMiterProve(): Generated counter-example is invalid.\n" ); + free( pSimInfo ); + } + + if ( RetValue == -1 ) + printf( "UNDECIDED " ); + else if ( RetValue == 0 ) + printf( "SATISFIABLE " ); + else + printf( "UNSATISFIABLE " ); + //printf( "\n" ); + + PRT( "Time", clock() - clk ); + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkTemp ); + return 0; + +usage: + fprintf( pErr, "usage: prove [-N num] [-C num] [-F num] [-L num] [-I num] [-rfbvh]\n" ); + fprintf( pErr, "\t solves combinational miter by rewriting, FRAIGing, and SAT\n" ); + fprintf( pErr, "\t replaces the current network by the cone modified by rewriting\n" ); + fprintf( pErr, "\t-N num : max number of iterations [default = %d]\n", pParams->nItersMax ); + fprintf( pErr, "\t-C num : max starting number of conflicts in mitering [default = %d]\n", pParams->nMiteringLimitStart ); + fprintf( pErr, "\t-F num : max starting number of conflicts in fraiging [default = %d]\n", pParams->nFraigingLimitStart ); + fprintf( pErr, "\t-L num : max last-gasp number of conflicts in mitering [default = %d]\n", pParams->nMiteringLimitLast ); + fprintf( pErr, "\t-I num : max number of clause inspections in all SAT calls [default = %d]\n", (int)pParams->nTotalInspectLimit ); + fprintf( pErr, "\t-r : toggle the use of rewriting [default = %s]\n", pParams->fUseRewriting? "yes": "no" ); + fprintf( pErr, "\t-f : toggle the use of FRAIGing [default = %s]\n", pParams->fUseFraiging? "yes": "no" ); + fprintf( pErr, "\t-b : toggle the use of BDDs [default = %s]\n", pParams->fUseBdds? "yes": "no" ); + fprintf( pErr, "\t-v : prints verbose information [default = %s]\n", pParams->fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandDebug( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + extern void Abc_NtkAutoDebug( Abc_Ntk_t * pNtk, int (*pFuncError) (Abc_Ntk_t *) ); + extern int Abc_NtkRetimeDebug( Abc_Ntk_t * pNtk ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsLogic(pNtk) ) + { + fprintf( pErr, "This command is applicable to logic networks.\n" ); + return 1; + } + + Abc_NtkAutoDebug( pNtk, Abc_NtkRetimeDebug ); + return 0; + +usage: + fprintf( pErr, "usage: debug [-h]\n" ); + fprintf( pErr, "\t performs automated debugging of the given procedure\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandTraceStart( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "This command is applicable to AIGs.\n" ); + return 1; + } +/* + Abc_HManStart(); + if ( !Abc_HManPopulate( pNtk ) ) + { + fprintf( pErr, "Failed to start the tracing database.\n" ); + return 1; + } +*/ + return 0; + +usage: + fprintf( pErr, "usage: trace_start [-h]\n" ); + fprintf( pErr, "\t starts verification tracing\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandTraceCheck( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk; + int c; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( pErr, "This command is applicable to AIGs.\n" ); + return 1; + } +/* + if ( !Abc_HManIsRunning(pNtk) ) + { + fprintf( pErr, "The tracing database is not available.\n" ); + return 1; + } + + if ( !Abc_HManVerify( 1, pNtk->Id ) ) + fprintf( pErr, "Verification failed.\n" ); + Abc_HManStop(); +*/ + return 0; + +usage: + fprintf( pErr, "usage: trace_check [-h]\n" ); + fprintf( pErr, "\t checks the current network using verification trace\n" ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcAttach.c b/abc_with_bb_support/src/base/abci/abcAttach.c new file mode 100644 index 000000000..e53e0b3de --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcAttach.c @@ -0,0 +1,404 @@ +/**CFile**************************************************************** + + FileName [abcAttach.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Attaches the library gates to the current network.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcAttach.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "main.h" +#include "mio.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define ATTACH_FULL (~((unsigned)0)) +#define ATTACH_MASK(n) ((~((unsigned)0)) >> (32-(n))) + +static void Abc_AttachSetupTruthTables( unsigned uTruths[][2] ); +static void Abc_AttachComputeTruth( char * pSop, unsigned uTruthsIn[][2], unsigned * uTruthNode ); +static Mio_Gate_t * Abc_AttachFind( Mio_Gate_t ** ppGates, unsigned ** puTruthGates, int nGates, unsigned * uTruthNode, int * Perm ); +static int Abc_AttachCompare( unsigned ** puTruthGates, int nGates, unsigned * uTruthNode ); +static int Abc_NodeAttach( Abc_Obj_t * pNode, Mio_Gate_t ** ppGates, unsigned ** puTruthGates, int nGates, unsigned uTruths[][2] ); +static void Abc_TruthPermute( char * pPerm, int nVars, unsigned * uTruthNode, unsigned * uTruthPerm ); + +static char ** s_pPerms = NULL; +static int s_nPerms; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Attaches gates from the current library to the internal nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkAttach( Abc_Ntk_t * pNtk ) +{ + Mio_Library_t * pGenlib; + unsigned ** puTruthGates; + unsigned uTruths[6][2]; + Abc_Obj_t * pNode; + Mio_Gate_t ** ppGates; + int nGates, nFanins, i; + + assert( Abc_NtkIsSopLogic(pNtk) ); + + // check that the library is available + pGenlib = Abc_FrameReadLibGen(); + if ( pGenlib == NULL ) + { + printf( "The current library is not available.\n" ); + return 0; + } + + // start the truth tables + Abc_AttachSetupTruthTables( uTruths ); + + // collect all the gates + ppGates = Mio_CollectRoots( pGenlib, 6, (float)1.0e+20, 1, &nGates ); + + // derive the gate truth tables + puTruthGates = ALLOC( unsigned *, nGates ); + puTruthGates[0] = ALLOC( unsigned, 2 * nGates ); + for ( i = 1; i < nGates; i++ ) + puTruthGates[i] = puTruthGates[i-1] + 2; + for ( i = 0; i < nGates; i++ ) + Mio_DeriveTruthTable( ppGates[i], uTruths, Mio_GateReadInputs(ppGates[i]), 6, puTruthGates[i] ); + + // assign the gates to pNode->pCopy + Abc_NtkCleanCopy( pNtk ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + nFanins = Abc_ObjFaninNum(pNode); + if ( nFanins == 0 ) + { + if ( Abc_SopIsConst1(pNode->pData) ) + pNode->pCopy = (Abc_Obj_t *)Mio_LibraryReadConst1(pGenlib); + else + pNode->pCopy = (Abc_Obj_t *)Mio_LibraryReadConst0(pGenlib); + } + else if ( nFanins == 1 ) + { + if ( Abc_SopIsBuf(pNode->pData) ) + pNode->pCopy = (Abc_Obj_t *)Mio_LibraryReadBuf(pGenlib); + else + pNode->pCopy = (Abc_Obj_t *)Mio_LibraryReadInv(pGenlib); + } + else if ( nFanins > 6 ) + { + printf( "Cannot attach gate with more than 6 inputs to node %s.\n", Abc_ObjName(pNode) ); + free( puTruthGates[0] ); + free( puTruthGates ); + free( ppGates ); + return 0; + } + else if ( !Abc_NodeAttach( pNode, ppGates, puTruthGates, nGates, uTruths ) ) + { + printf( "Could not attach the library gate to node %s.\n", Abc_ObjName(pNode) ); + free( puTruthGates[0] ); + free( puTruthGates ); + free( ppGates ); + return 0; + } + } + free( puTruthGates[0] ); + free( puTruthGates ); + free( ppGates ); + FREE( s_pPerms ); + + // perform the final transformation + Abc_NtkForEachNode( pNtk, pNode, i ) + { + if ( pNode->pCopy == NULL ) + { + printf( "Some elementary gates (constant, buffer, or inverter) are missing in the library.\n" ); + return 0; + } + } + + // replace SOP representation by the gate representation + Abc_NtkForEachNode( pNtk, pNode, i ) + pNode->pData = pNode->pCopy, pNode->pCopy = NULL; + pNtk->ntkFunc = ABC_FUNC_MAP; + Extra_MmFlexStop( pNtk->pManFunc ); + pNtk->pManFunc = pGenlib; + + printf( "Library gates are successfully attached to the nodes.\n" ); + + // make sure that everything is okay + if ( !Abc_NtkCheck( pNtk ) ) + { + printf( "Abc_NtkAttach: The network check has failed.\n" ); + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeAttach( Abc_Obj_t * pNode, Mio_Gate_t ** ppGates, unsigned ** puTruthGates, int nGates, unsigned uTruths[][2] ) +{ + int Perm[10]; + int pTempInts[10]; + unsigned uTruthNode[2]; + Abc_Obj_t * pFanin; + Mio_Gate_t * pGate; + int nFanins, i; + + // compute the node's truth table + Abc_AttachComputeTruth( pNode->pData, uTruths, uTruthNode ); + // find the matching gate and permutation + pGate = Abc_AttachFind( ppGates, puTruthGates, nGates, uTruthNode, Perm ); + if ( pGate == NULL ) + return 0; + // permute the fanins + nFanins = Abc_ObjFaninNum(pNode); + Abc_ObjForEachFanin( pNode, pFanin, i ) + pTempInts[i] = pFanin->Id; + for ( i = 0; i < nFanins; i++ ) + pNode->vFanins.pArray[Perm[i]] = pTempInts[i]; + // set the gate + pNode->pCopy = (Abc_Obj_t *)pGate; + return 1; +} + +/**Function************************************************************* + + Synopsis [Sets up the truth tables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_AttachSetupTruthTables( unsigned uTruths[][2] ) +{ + int m, v; + for ( v = 0; v < 5; v++ ) + uTruths[v][0] = 0; + // set up the truth tables + for ( m = 0; m < 32; m++ ) + for ( v = 0; v < 5; v++ ) + if ( m & (1 << v) ) + uTruths[v][0] |= (1 << m); + // make adjustments for the case of 6 variables + for ( v = 0; v < 5; v++ ) + uTruths[v][1] = uTruths[v][0]; + uTruths[5][0] = 0; + uTruths[5][1] = ATTACH_FULL; +} + +/**Function************************************************************* + + Synopsis [Compute the truth table of the node's cover.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_AttachComputeTruth( char * pSop, unsigned uTruthsIn[][2], unsigned * uTruthRes ) +{ +// Mvc_Cube_t * pCube; + unsigned uSignCube[2]; + int Value; +// int nInputs = pCover->nBits/2; + int nInputs = 6; + int nFanins = Abc_SopGetVarNum(pSop); + char * pCube; + int k; + + // make sure that the number of input truth tables in equal to the number of gate inputs + assert( nInputs < 7 ); + + // clean the resulting truth table + uTruthRes[0] = 0; + uTruthRes[1] = 0; + if ( nInputs < 6 ) + { + // consider the case when only one unsigned can be used +// Mvc_CoverForEachCube( pCover, pCube ) + Abc_SopForEachCube( pSop, nFanins, pCube ) + { + uSignCube[0] = ATTACH_FULL; +// Mvc_CubeForEachVarValue( pCover, pCube, Var, Value ) + Abc_CubeForEachVar( pCube, Value, k ) + { + if ( Value == '0' ) + uSignCube[0] &= ~uTruthsIn[k][0]; + else if ( Value == '1' ) + uSignCube[0] &= uTruthsIn[k][0]; + } + uTruthRes[0] |= uSignCube[0]; + } + if ( Abc_SopGetPhase(pSop) == 0 ) + uTruthRes[0] = ~uTruthRes[0]; + if ( nInputs < 5 ) + uTruthRes[0] &= ATTACH_MASK(1<= 0 ) + { + for ( v = 0; v < 6; v++ ) + Perm[v] = v; + return ppGates[iNum]; + } + // get permutations + if ( s_pPerms == NULL ) + { + s_pPerms = Extra_Permutations( 6 ); + s_nPerms = Extra_Factorial( 6 ); + } + // try permutations + for ( i = 0; i < s_nPerms; i++ ) + { + Abc_TruthPermute( s_pPerms[i], 6, uTruthNode, uTruthPerm ); + if ( (iNum = Abc_AttachCompare( puTruthGates, nGates, uTruthPerm )) >= 0 ) + { + for ( v = 0; v < 6; v++ ) + Perm[v] = (int)s_pPerms[i][v]; + return ppGates[iNum]; + } + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [Find the gate by truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_AttachCompare( unsigned ** puTruthGates, int nGates, unsigned * uTruthNode ) +{ + int i; + for ( i = 0; i < nGates; i++ ) + if ( puTruthGates[i][0] == uTruthNode[0] && puTruthGates[i][1] == uTruthNode[1] ) + return i; + return -1; +} + +/**Function************************************************************* + + Synopsis [Permutes the 6-input truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_TruthPermute( char * pPerm, int nVars, unsigned * uTruthNode, unsigned * uTruthPerm ) +{ + int nMints, iMintPerm, iMint, v; + uTruthPerm[0] = uTruthPerm[1] = 0; + nMints = (1 << nVars); + for ( iMint = 0; iMint < nMints; iMint++ ) + { + if ( (uTruthNode[iMint>>5] & (1 << (iMint&31))) == 0 ) + continue; + iMintPerm = 0; + for ( v = 0; v < nVars; v++ ) + if ( iMint & (1 << v) ) + iMintPerm |= (1 << pPerm[v]); + uTruthPerm[iMintPerm>>5] |= (1 << (iMintPerm&31)); + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcAuto.c b/abc_with_bb_support/src/base/abci/abcAuto.c new file mode 100644 index 000000000..e8c220ffc --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcAuto.c @@ -0,0 +1,239 @@ +/**CFile**************************************************************** + + FileName [abcAuto.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Computation of autosymmetries.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcAuto.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Abc_NtkAutoPrintAll( DdManager * dd, int nInputs, DdNode * pbOutputs[], int nOutputs, char * pInputNames[], char * pOutputNames[], int fNaive ); +static void Abc_NtkAutoPrintOne( DdManager * dd, int nInputs, DdNode * pbOutputs[], int Output, char * pInputNames[], char * pOutputNames[], int fNaive ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkAutoPrint( Abc_Ntk_t * pNtk, int Output, int fNaive, int fVerbose ) +{ + DdManager * dd; // the BDD manager used to hold shared BDDs + DdNode ** pbGlobal; // temporary storage for global BDDs + char ** pInputNames; // pointers to the CI names + char ** pOutputNames; // pointers to the CO names + int nOutputs, nInputs, i; + Vec_Ptr_t * vFuncsGlob; + Abc_Obj_t * pObj; + + // compute the global BDDs + if ( Abc_NtkBuildGlobalBdds(pNtk, 10000000, 1, 1, fVerbose) == NULL ) + return; + + // get information about the network + nInputs = Abc_NtkCiNum(pNtk); + nOutputs = Abc_NtkCoNum(pNtk); +// dd = pNtk->pManGlob; + dd = Abc_NtkGlobalBddMan( pNtk ); + + // complement the global functions + vFuncsGlob = Vec_PtrAlloc( Abc_NtkCoNum(pNtk) ); + Abc_NtkForEachCo( pNtk, pObj, i ) + Vec_PtrPush( vFuncsGlob, Abc_ObjGlobalBdd(pObj) ); + pbGlobal = (DdNode **)Vec_PtrArray( vFuncsGlob ); + + // get the network names + pInputNames = Abc_NtkCollectCioNames( pNtk, 0 ); + pOutputNames = Abc_NtkCollectCioNames( pNtk, 1 ); + + // print the size of the BDDs + if ( fVerbose ) + printf( "Shared BDD size = %6d nodes.\n", Cudd_ReadKeys(dd) - Cudd_ReadDead(dd) ); + + // allocate additional variables + for ( i = 0; i < nInputs; i++ ) + Cudd_bddNewVar( dd ); + assert( Cudd_ReadSize(dd) == 2 * nInputs ); + + // create ZDD variables in the manager + Cudd_zddVarsFromBddVars( dd, 2 ); + + // perform the analysis of the primary output functions for auto-symmetry + if ( Output == -1 ) + Abc_NtkAutoPrintAll( dd, nInputs, pbGlobal, nOutputs, pInputNames, pOutputNames, fNaive ); + else + Abc_NtkAutoPrintOne( dd, nInputs, pbGlobal, Output, pInputNames, pOutputNames, fNaive ); + + // deref the PO functions +// Abc_NtkFreeGlobalBdds( pNtk ); + // stop the global BDD manager +// Extra_StopManager( pNtk->pManGlob ); +// pNtk->pManGlob = NULL; + Abc_NtkFreeGlobalBdds( pNtk, 1 ); + free( pInputNames ); + free( pOutputNames ); + Vec_PtrFree( vFuncsGlob ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkAutoPrintAll( DdManager * dd, int nInputs, DdNode * pbOutputs[], int nOutputs, char * pInputNames[], char * pOutputNames[], int fNaive ) +{ + DdNode * bSpace1, * bSpace2, * bCanVars, * bReduced, * zEquations; + double nMints; + int nSupp, SigCounter, o; + + int nAutos; + int nAutoSyms; + int nAutoSymsMax; + int nAutoSymsMaxSupp; + int nAutoSymOuts; + int nSuppSizeMax; + int clk; + + nAutoSymOuts = 0; + nAutoSyms = 0; + nAutoSymsMax = 0; + nAutoSymsMaxSupp = 0; + nSuppSizeMax = 0; + clk = clock(); + + SigCounter = 0; + for ( o = 0; o < nOutputs; o++ ) + { +// bSpace1 = Extra_bddSpaceFromFunctionFast( dd, pbOutputs[o] ); Cudd_Ref( bSpace1 ); + bSpace1 = Extra_bddSpaceFromFunction( dd, pbOutputs[o], pbOutputs[o] ); Cudd_Ref( bSpace1 ); + bCanVars = Extra_bddSpaceCanonVars( dd, bSpace1 ); Cudd_Ref( bCanVars ); + bReduced = Extra_bddSpaceReduce( dd, pbOutputs[o], bCanVars ); Cudd_Ref( bReduced ); + zEquations = Extra_bddSpaceEquations( dd, bSpace1 ); Cudd_Ref( zEquations ); + + nSupp = Cudd_SupportSize( dd, bSpace1 ); + nMints = Cudd_CountMinterm( dd, bSpace1, nSupp ); + nAutos = Extra_Base2LogDouble(nMints); + printf( "Output #%3d: Inputs = %2d. AutoK = %2d.\n", o, nSupp, nAutos ); + + if ( nAutos > 0 ) + { + nAutoSymOuts++; + nAutoSyms += nAutos; + if ( nAutoSymsMax < nAutos ) + { + nAutoSymsMax = nAutos; + nAutoSymsMaxSupp = nSupp; + } + } + if ( nSuppSizeMax < nSupp ) + nSuppSizeMax = nSupp; + + +//PRB( dd, bCanVars ); +//PRB( dd, bReduced ); +//Cudd_PrintMinterm( dd, bReduced ); +//printf( "The equations are:\n" ); +//Cudd_zddPrintCover( dd, zEquations ); +//printf( "\n" ); +//fflush( stdout ); + + bSpace2 = Extra_bddSpaceFromMatrixPos( dd, zEquations ); Cudd_Ref( bSpace2 ); +//PRB( dd, bSpace1 ); +//PRB( dd, bSpace2 ); + if ( bSpace1 != bSpace2 ) + printf( "Spaces are NOT EQUAL!\n" ); +// else +// printf( "Spaces are equal.\n" ); + + Cudd_RecursiveDeref( dd, bSpace1 ); + Cudd_RecursiveDeref( dd, bSpace2 ); + Cudd_RecursiveDeref( dd, bCanVars ); + Cudd_RecursiveDeref( dd, bReduced ); + Cudd_RecursiveDerefZdd( dd, zEquations ); + } + + printf( "The cumulative statistics for all outputs:\n" ); + printf( "Ins=%3d ", nInputs ); + printf( "InMax=%3d ", nSuppSizeMax ); + printf( "Outs=%3d ", nOutputs ); + printf( "Auto=%3d ", nAutoSymOuts ); + printf( "SumK=%3d ", nAutoSyms ); + printf( "KMax=%2d ", nAutoSymsMax ); + printf( "Supp=%3d ", nAutoSymsMaxSupp ); + printf( "Time=%4.2f ", (float)(clock() - clk)/(float)(CLOCKS_PER_SEC) ); + printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkAutoPrintOne( DdManager * dd, int nInputs, DdNode * pbOutputs[], int Output, char * pInputNames[], char * pOutputNames[], int fNaive ) +{ + DdNode * bSpace1, * bCanVars, * bReduced, * zEquations; + double nMints; + int nSupp, SigCounter; + int nAutos; + + SigCounter = 0; + bSpace1 = Extra_bddSpaceFromFunctionFast( dd, pbOutputs[Output] ); Cudd_Ref( bSpace1 ); +// bSpace1 = Extra_bddSpaceFromFunction( dd, pbOutputs[Output], pbOutputs[Output] ); Cudd_Ref( bSpace1 ); + bCanVars = Extra_bddSpaceCanonVars( dd, bSpace1 ); Cudd_Ref( bCanVars ); + bReduced = Extra_bddSpaceReduce( dd, pbOutputs[Output], bCanVars ); Cudd_Ref( bReduced ); + zEquations = Extra_bddSpaceEquations( dd, bSpace1 ); Cudd_Ref( zEquations ); + + nSupp = Cudd_SupportSize( dd, bSpace1 ); + nMints = Cudd_CountMinterm( dd, bSpace1, nSupp ); + nAutos = Extra_Base2LogDouble(nMints); + printf( "Output #%3d: Inputs = %2d. AutoK = %2d.\n", Output, nSupp, nAutos ); + + Cudd_RecursiveDeref( dd, bSpace1 ); + Cudd_RecursiveDeref( dd, bCanVars ); + Cudd_RecursiveDeref( dd, bReduced ); + Cudd_RecursiveDerefZdd( dd, zEquations ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcBalance.c b/abc_with_bb_support/src/base/abci/abcBalance.c new file mode 100644 index 000000000..2497eaddf --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcBalance.c @@ -0,0 +1,613 @@ +/**CFile**************************************************************** + + FileName [abcBalance.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Performs global balancing of the AIG by the number of levels.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcBalance.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Abc_NtkBalancePerform( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkAig, bool fDuplicate, bool fSelective, bool fUpdateLevel ); +static Abc_Obj_t * Abc_NodeBalance_rec( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pNode, Vec_Vec_t * vStorage, int Level, bool fDuplicate, bool fSelective, bool fUpdateLevel ); +static Vec_Ptr_t * Abc_NodeBalanceCone( Abc_Obj_t * pNode, Vec_Vec_t * vSuper, int Level, int fDuplicate, bool fSelective ); +static int Abc_NodeBalanceCone_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vSuper, bool fFirst, bool fDuplicate, bool fSelective ); +static void Abc_NtkMarkCriticalNodes( Abc_Ntk_t * pNtk ); +static Vec_Ptr_t * Abc_NodeBalanceConeExor( Abc_Obj_t * pNode ); + + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Balances the AIG network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkBalance( Abc_Ntk_t * pNtk, bool fDuplicate, bool fSelective, bool fUpdateLevel ) +{ + extern void Abc_NtkHaigTranfer( Abc_Ntk_t * pNtkOld, Abc_Ntk_t * pNtkNew ); + Abc_Ntk_t * pNtkAig; + assert( Abc_NtkIsStrash(pNtk) ); + // compute the required times + if ( fSelective ) + { + Abc_NtkStartReverseLevels( pNtk, 0 ); + Abc_NtkMarkCriticalNodes( pNtk ); + } + // perform balancing + pNtkAig = Abc_NtkStartFrom( pNtk, ABC_NTK_STRASH, ABC_FUNC_AIG ); + // transfer HAIG + Abc_NtkHaigTranfer( pNtk, pNtkAig ); + // perform balancing + Abc_NtkBalancePerform( pNtk, pNtkAig, fDuplicate, fSelective, fUpdateLevel ); + Abc_NtkFinalize( pNtk, pNtkAig ); + // undo the required times + if ( fSelective ) + { + Abc_NtkStopReverseLevels( pNtk ); + Abc_NtkCleanMarkA( pNtk ); + } + if ( pNtk->pExdc ) + pNtkAig->pExdc = Abc_NtkDup( pNtk->pExdc ); + // make sure everything is okay + if ( !Abc_NtkCheck( pNtkAig ) ) + { + printf( "Abc_NtkBalance: The network check has failed.\n" ); + Abc_NtkDelete( pNtkAig ); + return NULL; + } + return pNtkAig; +} + +/**Function************************************************************* + + Synopsis [Balances the AIG network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkBalancePerform( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkAig, bool fDuplicate, bool fSelective, bool fUpdateLevel ) +{ + int fCheck = 1; + ProgressBar * pProgress; + Vec_Vec_t * vStorage; + Abc_Obj_t * pNode, * pDriver; + int i; + + // set the level of PIs of AIG according to the arrival times of the old network + Abc_NtkSetNodeLevelsArrival( pNtk ); + // allocate temporary storage for supergates + vStorage = Vec_VecStart( 10 ); + // perform balancing of POs + pProgress = Extra_ProgressBarStart( stdout, Abc_NtkCoNum(pNtk) ); + Abc_NtkForEachCo( pNtk, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + // strash the driver node + pDriver = Abc_ObjFanin0(pNode); + Abc_NodeBalance_rec( pNtkAig, pDriver, vStorage, 0, fDuplicate, fSelective, fUpdateLevel ); + } + Extra_ProgressBarStop( pProgress ); + Vec_VecFree( vStorage ); +} + +/**Function************************************************************* + + Synopsis [Finds the left bound on the next candidate to be paired.] + + Description [The nodes in the array are in the decreasing order of levels. + The last node in the array has the smallest level. By default it would be paired + with the next node on the left. However, it may be possible to pair it with some + other node on the left, in such a way that the new node is shared. This procedure + finds the index of the left-most node, which can be paired with the last node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeBalanceFindLeft( Vec_Ptr_t * vSuper ) +{ + Abc_Obj_t * pNodeRight, * pNodeLeft; + int Current; + // if two or less nodes, pair with the first + if ( Vec_PtrSize(vSuper) < 3 ) + return 0; + // set the pointer to the one before the last + Current = Vec_PtrSize(vSuper) - 2; + pNodeRight = Vec_PtrEntry( vSuper, Current ); + // go through the nodes to the left of this one + for ( Current--; Current >= 0; Current-- ) + { + // get the next node on the left + pNodeLeft = Vec_PtrEntry( vSuper, Current ); + // if the level of this node is different, quit the loop + if ( Abc_ObjRegular(pNodeLeft)->Level != Abc_ObjRegular(pNodeRight)->Level ) + break; + } + Current++; + // get the node, for which the equality holds + pNodeLeft = Vec_PtrEntry( vSuper, Current ); + assert( Abc_ObjRegular(pNodeLeft)->Level == Abc_ObjRegular(pNodeRight)->Level ); + return Current; +} + +/**Function************************************************************* + + Synopsis [Moves closer to the end the node that is best for sharing.] + + Description [If there is no node with sharing, randomly chooses one of + the legal nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeBalancePermute( Abc_Ntk_t * pNtkNew, Vec_Ptr_t * vSuper, int LeftBound ) +{ + Abc_Obj_t * pNode1, * pNode2, * pNode3; + int RightBound, i; + // get the right bound + RightBound = Vec_PtrSize(vSuper) - 2; + assert( LeftBound <= RightBound ); + if ( LeftBound == RightBound ) + return; + // get the two last nodes + pNode1 = Vec_PtrEntry( vSuper, RightBound + 1 ); + pNode2 = Vec_PtrEntry( vSuper, RightBound ); + // find the first node that can be shared + for ( i = RightBound; i >= LeftBound; i-- ) + { + pNode3 = Vec_PtrEntry( vSuper, i ); + if ( Abc_AigAndLookup( pNtkNew->pManFunc, pNode1, pNode3 ) ) + { + if ( pNode3 == pNode2 ) + return; + Vec_PtrWriteEntry( vSuper, i, pNode2 ); + Vec_PtrWriteEntry( vSuper, RightBound, pNode3 ); + return; + } + } +/* + // we did not find the node to share, randomize choice + { + int Choice = rand() % (RightBound - LeftBound + 1); + pNode3 = Vec_PtrEntry( vSuper, LeftBound + Choice ); + if ( pNode3 == pNode2 ) + return; + Vec_PtrWriteEntry( vSuper, LeftBound + Choice, pNode2 ); + Vec_PtrWriteEntry( vSuper, RightBound, pNode3 ); + } +*/ +} + +/**Function************************************************************* + + Synopsis [Rebalances the multi-input node rooted at pNodeOld.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NodeBalance_rec( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pNodeOld, Vec_Vec_t * vStorage, int Level, bool fDuplicate, bool fSelective, bool fUpdateLevel ) +{ + Abc_Aig_t * pMan = pNtkNew->pManFunc; + Abc_Obj_t * pNodeNew, * pNode1, * pNode2; + Vec_Ptr_t * vSuper; + int i, LeftBound; + assert( !Abc_ObjIsComplement(pNodeOld) ); + // return if the result if known + if ( pNodeOld->pCopy ) + return pNodeOld->pCopy; + assert( Abc_ObjIsNode(pNodeOld) ); + // get the implication supergate +// Abc_NodeBalanceConeExor( pNodeOld ); + vSuper = Abc_NodeBalanceCone( pNodeOld, vStorage, Level, fDuplicate, fSelective ); + if ( vSuper->nSize == 0 ) + { // it means that the supergate contains two nodes in the opposite polarity + pNodeOld->pCopy = Abc_ObjNot(Abc_AigConst1(pNtkNew)); + return pNodeOld->pCopy; + } + // for each old node, derive the new well-balanced node + for ( i = 0; i < vSuper->nSize; i++ ) + { + pNodeNew = Abc_NodeBalance_rec( pNtkNew, Abc_ObjRegular(vSuper->pArray[i]), vStorage, Level + 1, fDuplicate, fSelective, fUpdateLevel ); + vSuper->pArray[i] = Abc_ObjNotCond( pNodeNew, Abc_ObjIsComplement(vSuper->pArray[i]) ); + } + if ( vSuper->nSize < 2 ) + printf( "BUG!\n" ); + // sort the new nodes by level in the decreasing order + Vec_PtrSort( vSuper, Abc_NodeCompareLevelsDecrease ); + // balance the nodes + assert( vSuper->nSize > 1 ); + while ( vSuper->nSize > 1 ) + { + // find the left bound on the node to be paired + LeftBound = (!fUpdateLevel)? 0 : Abc_NodeBalanceFindLeft( vSuper ); + // find the node that can be shared (if no such node, randomize choice) + Abc_NodeBalancePermute( pNtkNew, vSuper, LeftBound ); + // pull out the last two nodes + pNode1 = Vec_PtrPop(vSuper); + pNode2 = Vec_PtrPop(vSuper); + Abc_VecObjPushUniqueOrderByLevel( vSuper, Abc_AigAnd(pMan, pNode1, pNode2) ); + } + // make sure the balanced node is not assigned + assert( pNodeOld->pCopy == NULL ); + // mark the old node with the new node + pNodeOld->pCopy = vSuper->pArray[0]; + vSuper->nSize = 0; +// if ( Abc_ObjRegular(pNodeOld->pCopy) == Abc_AigConst1(pNtkNew) ) +// printf( "Constant node\n" ); +// assert( pNodeOld->Level >= Abc_ObjRegular(pNodeOld->pCopy)->Level ); + // update HAIG + if ( Abc_ObjRegular(pNodeOld->pCopy)->pNtk->pHaig ) + Hop_ObjCreateChoice( pNodeOld->pEquiv, Abc_ObjRegular(pNodeOld->pCopy)->pEquiv ); + return pNodeOld->pCopy; +} + +/**Function************************************************************* + + Synopsis [Collects the nodes in the cone delimited by fMarkA==1.] + + Description [Returns -1 if the AND-cone has the same node in both polarities. + Returns 1 if the AND-cone has the same node in the same polarity. Returns 0 + if the AND-cone has no repeated nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NodeBalanceCone( Abc_Obj_t * pNode, Vec_Vec_t * vStorage, int Level, int fDuplicate, bool fSelective ) +{ + Vec_Ptr_t * vNodes; + int RetValue, i; + assert( !Abc_ObjIsComplement(pNode) ); + // extend the storage + if ( Vec_VecSize( vStorage ) <= Level ) + Vec_VecPush( vStorage, Level, 0 ); + // get the temporary array of nodes + vNodes = Vec_VecEntry( vStorage, Level ); + Vec_PtrClear( vNodes ); + // collect the nodes in the implication supergate + RetValue = Abc_NodeBalanceCone_rec( pNode, vNodes, 1, fDuplicate, fSelective ); + assert( vNodes->nSize > 1 ); + // unmark the visited nodes + for ( i = 0; i < vNodes->nSize; i++ ) + Abc_ObjRegular((Abc_Obj_t *)vNodes->pArray[i])->fMarkB = 0; + // if we found the node and its complement in the same implication supergate, + // return empty set of nodes (meaning that we should use constant-0 node) + if ( RetValue == -1 ) + vNodes->nSize = 0; + return vNodes; +} + + +/**Function************************************************************* + + Synopsis [Collects the nodes in the cone delimited by fMarkA==1.] + + Description [Returns -1 if the AND-cone has the same node in both polarities. + Returns 1 if the AND-cone has the same node in the same polarity. Returns 0 + if the AND-cone has no repeated nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeBalanceCone_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vSuper, bool fFirst, bool fDuplicate, bool fSelective ) +{ + int RetValue1, RetValue2, i; + // check if the node is visited + if ( Abc_ObjRegular(pNode)->fMarkB ) + { + // check if the node occurs in the same polarity + for ( i = 0; i < vSuper->nSize; i++ ) + if ( vSuper->pArray[i] == pNode ) + return 1; + // check if the node is present in the opposite polarity + for ( i = 0; i < vSuper->nSize; i++ ) + if ( vSuper->pArray[i] == Abc_ObjNot(pNode) ) + return -1; + assert( 0 ); + return 0; + } + // if the new node is complemented or a PI, another gate begins + if ( !fFirst && (Abc_ObjIsComplement(pNode) || !Abc_ObjIsNode(pNode) || !fDuplicate && !fSelective && (Abc_ObjFanoutNum(pNode) > 1)) ) + { + Vec_PtrPush( vSuper, pNode ); + Abc_ObjRegular(pNode)->fMarkB = 1; + return 0; + } + assert( !Abc_ObjIsComplement(pNode) ); + assert( Abc_ObjIsNode(pNode) ); + // go through the branches + RetValue1 = Abc_NodeBalanceCone_rec( Abc_ObjChild0(pNode), vSuper, 0, fDuplicate, fSelective ); + RetValue2 = Abc_NodeBalanceCone_rec( Abc_ObjChild1(pNode), vSuper, 0, fDuplicate, fSelective ); + if ( RetValue1 == -1 || RetValue2 == -1 ) + return -1; + // return 1 if at least one branch has a duplicate + return RetValue1 || RetValue2; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeBalanceConeExor_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vSuper, bool fFirst ) +{ + int RetValue1, RetValue2, i; + // check if the node occurs in the same polarity + for ( i = 0; i < vSuper->nSize; i++ ) + if ( vSuper->pArray[i] == pNode ) + return 1; + // if the new node is complemented or a PI, another gate begins + if ( !fFirst && (!pNode->fExor || !Abc_ObjIsNode(pNode)) ) + { + Vec_PtrPush( vSuper, pNode ); + return 0; + } + assert( !Abc_ObjIsComplement(pNode) ); + assert( Abc_ObjIsNode(pNode) ); + assert( pNode->fExor ); + // go through the branches + RetValue1 = Abc_NodeBalanceConeExor_rec( Abc_ObjFanin0(Abc_ObjFanin0(pNode)), vSuper, 0 ); + RetValue2 = Abc_NodeBalanceConeExor_rec( Abc_ObjFanin1(Abc_ObjFanin0(pNode)), vSuper, 0 ); + if ( RetValue1 == -1 || RetValue2 == -1 ) + return -1; + // return 1 if at least one branch has a duplicate + return RetValue1 || RetValue2; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NodeBalanceConeExor( Abc_Obj_t * pNode ) +{ + Vec_Ptr_t * vSuper; + if ( !pNode->fExor ) + return NULL; + vSuper = Vec_PtrAlloc( 10 ); + Abc_NodeBalanceConeExor_rec( pNode, vSuper, 1 ); + printf( "%d ", Vec_PtrSize(vSuper) ); + Vec_PtrFree( vSuper ); + return NULL; +} + + + +/**Function************************************************************* + + Synopsis [Collects the nodes in the implication supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NodeFindCone_rec( Abc_Obj_t * pNode ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pNodeC, * pNodeT, * pNodeE; + int RetValue, i; + assert( !Abc_ObjIsComplement(pNode) ); + if ( Abc_ObjIsCi(pNode) ) + return NULL; + // start the new array + vNodes = Vec_PtrAlloc( 4 ); + // if the node is the MUX collect its fanins + if ( Abc_NodeIsMuxType(pNode) ) + { + pNodeC = Abc_NodeRecognizeMux( pNode, &pNodeT, &pNodeE ); + Vec_PtrPush( vNodes, Abc_ObjRegular(pNodeC) ); + Vec_PtrPushUnique( vNodes, Abc_ObjRegular(pNodeT) ); + Vec_PtrPushUnique( vNodes, Abc_ObjRegular(pNodeE) ); + } + else + { + // collect the nodes in the implication supergate + RetValue = Abc_NodeBalanceCone_rec( pNode, vNodes, 1, 1, 0 ); + assert( vNodes->nSize > 1 ); + // unmark the visited nodes + Vec_PtrForEachEntry( vNodes, pNode, i ) + Abc_ObjRegular(pNode)->fMarkB = 0; + // if we found the node and its complement in the same implication supergate, + // return empty set of nodes (meaning that we should use constant-0 node) + if ( RetValue == -1 ) + vNodes->nSize = 0; + } + // call for the fanin + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + pNode = Abc_ObjRegular(pNode); + if ( pNode->pCopy ) + continue; + pNode->pCopy = (Abc_Obj_t *)Abc_NodeFindCone_rec( pNode ); + } + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Attaches the implication supergates to internal nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkBalanceAttach( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i; + Abc_NtkCleanCopy( pNtk ); + Abc_NtkForEachCo( pNtk, pNode, i ) + { + pNode = Abc_ObjFanin0(pNode); + if ( pNode->pCopy ) + continue; + pNode->pCopy = (Abc_Obj_t *)Abc_NodeFindCone_rec( pNode ); + } +} + +/**Function************************************************************* + + Synopsis [Attaches the implication supergates to internal nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkBalanceDetach( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i; + Abc_NtkForEachNode( pNtk, pNode, i ) + if ( pNode->pCopy ) + { + Vec_PtrFree( (Vec_Ptr_t *)pNode->pCopy ); + pNode->pCopy = NULL; + } +} + +/**Function************************************************************* + + Synopsis [Compute levels of implication supergates.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkBalanceLevel_rec( Abc_Obj_t * pNode ) +{ + Vec_Ptr_t * vSuper; + Abc_Obj_t * pFanin; + int i, LevelMax; + assert( !Abc_ObjIsComplement(pNode) ); + if ( pNode->Level > 0 ) + return pNode->Level; + if ( Abc_ObjIsCi(pNode) ) + return 0; + vSuper = (Vec_Ptr_t *)pNode->pCopy; + assert( vSuper != NULL ); + LevelMax = 0; + Vec_PtrForEachEntry( vSuper, pFanin, i ) + { + pFanin = Abc_ObjRegular(pFanin); + Abc_NtkBalanceLevel_rec(pFanin); + if ( LevelMax < (int)pFanin->Level ) + LevelMax = pFanin->Level; + } + pNode->Level = LevelMax + 1; + return pNode->Level; +} + + +/**Function************************************************************* + + Synopsis [Compute levels of implication supergates.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkBalanceLevel( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i; + Abc_NtkForEachObj( pNtk, pNode, i ) + pNode->Level = 0; + Abc_NtkForEachCo( pNtk, pNode, i ) + Abc_NtkBalanceLevel_rec( Abc_ObjFanin0(pNode) ); +} + + +/**Function************************************************************* + + Synopsis [Marks the nodes on the critical and near critical paths.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMarkCriticalNodes( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i, Counter = 0; + Abc_NtkForEachNode( pNtk, pNode, i ) + if ( Abc_ObjRequiredLevel(pNode) - pNode->Level <= 1 ) + pNode->fMarkA = 1, Counter++; + printf( "The number of nodes on the critical paths = %6d (%5.2f %%)\n", Counter, 100.0 * Counter / Abc_NtkNodeNum(pNtk) ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcBmc.c b/abc_with_bb_support/src/base/abci/abcBmc.c new file mode 100644 index 000000000..d81b4e93f --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcBmc.c @@ -0,0 +1,115 @@ +/**CFile**************************************************************** + + FileName [abcBmc.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Performs bounded model check.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcBmc.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +extern Ivy_Man_t * Abc_NtkIvyBefore( Abc_Ntk_t * pNtk, int fSeq, int fUseDc ); + +static void Abc_NtkBmcReport( Ivy_Man_t * pMan, Ivy_Man_t * pFrames, Ivy_Man_t * pFraig, Vec_Ptr_t * vMapping, int nFrames ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkBmc( Abc_Ntk_t * pNtk, int nFrames, int fInit, int fVerbose ) +{ + Ivy_FraigParams_t Params, * pParams = &Params; + Ivy_Man_t * pMan, * pFrames, * pFraig; + Vec_Ptr_t * vMapping; + // convert to IVY manager + pMan = Abc_NtkIvyBefore( pNtk, 0, 0 ); + // generate timeframes + pFrames = Ivy_ManFrames( pMan, Abc_NtkLatchNum(pNtk), nFrames, fInit, &vMapping ); + // fraig the timeframes + Ivy_FraigParamsDefault( pParams ); + pParams->nBTLimitNode = ABC_INFINITY; + pParams->fVerbose = 0; + pParams->fProve = 0; + pFraig = Ivy_FraigPerform( pFrames, pParams ); +printf( "Frames have %6d nodes. ", Ivy_ManNodeNum(pFrames) ); +printf( "Fraig has %6d nodes.\n", Ivy_ManNodeNum(pFraig) ); + // report the classes +// if ( fVerbose ) +// Abc_NtkBmcReport( pMan, pFrames, pFraig, vMapping, nFrames ); + // free stuff + Vec_PtrFree( vMapping ); + Ivy_ManStop( pFraig ); + Ivy_ManStop( pFrames ); + Ivy_ManStop( pMan ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkBmcReport( Ivy_Man_t * pMan, Ivy_Man_t * pFrames, Ivy_Man_t * pFraig, Vec_Ptr_t * vMapping, int nFrames ) +{ + Ivy_Obj_t * pFirst1, * pFirst2, * pFirst3; + int i, f, nIdMax, Prev2, Prev3; + nIdMax = Ivy_ManObjIdMax(pMan); + // check what is the number of nodes in each frame + Prev2 = Prev3 = 0; + for ( f = 0; f < nFrames; f++ ) + { + Ivy_ManForEachNode( pMan, pFirst1, i ) + { + pFirst2 = Ivy_Regular( Vec_PtrEntry(vMapping, f * nIdMax + pFirst1->Id) ); + if ( Ivy_ObjIsConst1(pFirst2) || pFirst2->Type == 0 ) + continue; + pFirst3 = Ivy_Regular( pFirst2->pEquiv ); + if ( Ivy_ObjIsConst1(pFirst3) || pFirst3->Type == 0 ) + continue; + break; + } + if ( f ) + printf( "Frame %3d : Strash = %5d Fraig = %5d\n", f, pFirst2->Id - Prev2, pFirst3->Id - Prev3 ); + Prev2 = pFirst2->Id; + Prev3 = pFirst3->Id; + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcCas.c b/abc_with_bb_support/src/base/abci/abcCas.c new file mode 100644 index 000000000..c3cfa03b3 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcCas.c @@ -0,0 +1,111 @@ +/**CFile**************************************************************** + + FileName [abcCas.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Decomposition of shared BDDs into LUT cascade.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcCas.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +/* + This LUT cascade synthesis algorithm is described in the paper: + A. Mishchenko and T. Sasao, "Encoding of Boolean functions and its + application to LUT cascade synthesis", Proc. IWLS '02, pp. 115-120. + http://www.eecs.berkeley.edu/~alanmi/publications/2002/iwls02_enc.pdf +*/ + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +extern int Abc_CascadeExperiment( char * pFileGeneric, DdManager * dd, DdNode ** pOutputs, int nInputs, int nOutputs, int nLutSize, int fCheck, int fVerbose ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkCascade( Abc_Ntk_t * pNtk, int nLutSize, int fCheck, int fVerbose ) +{ + DdManager * dd; + DdNode ** ppOutputs; + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pNode; + char * pFileGeneric; + int fBddSizeMax = 500000; + int fReorder = 1; + int i, clk = clock(); + + assert( Abc_NtkIsStrash(pNtk) ); + // compute the global BDDs + if ( Abc_NtkBuildGlobalBdds(pNtk, fBddSizeMax, 1, fReorder, fVerbose) == NULL ) + return NULL; + + if ( fVerbose ) + { + DdManager * dd = Abc_NtkGlobalBddMan( pNtk ); + printf( "Shared BDD size = %6d nodes. ", Cudd_ReadKeys(dd) - Cudd_ReadDead(dd) ); + PRT( "BDD construction time", clock() - clk ); + } + + // collect global BDDs + dd = Abc_NtkGlobalBddMan( pNtk ); + ppOutputs = ALLOC( DdNode *, Abc_NtkCoNum(pNtk) ); + Abc_NtkForEachCo( pNtk, pNode, i ) + ppOutputs[i] = Abc_ObjGlobalBdd(pNode); + + // call the decomposition + pFileGeneric = Extra_FileNameGeneric( pNtk->pSpec ); + if ( !Abc_CascadeExperiment( pFileGeneric, dd, ppOutputs, Abc_NtkCiNum(pNtk), Abc_NtkCoNum(pNtk), nLutSize, fCheck, fVerbose ) ) + { + // the LUT size is too small + } + + // for now, duplicate the network + pNtkNew = Abc_NtkDup( pNtk ); + + // cleanup + Abc_NtkFreeGlobalBdds( pNtk, 1 ); + free( ppOutputs ); + free( pFileGeneric ); + +// if ( pNtk->pExdc ) +// pNtkNew->pExdc = Abc_NtkDup( pNtk->pExdc ); + // make sure that everything is okay + if ( !Abc_NtkCheck( pNtkNew ) ) + { + printf( "Abc_NtkCollapse: The network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcClpBdd.c b/abc_with_bb_support/src/base/abci/abcClpBdd.c new file mode 100644 index 000000000..d1548c8d6 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcClpBdd.c @@ -0,0 +1,162 @@ +/**CFile**************************************************************** + + FileName [abcCollapse.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Collapsing the network into two-levels.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcCollapse.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Abc_Ntk_t * Abc_NtkFromGlobalBdds( Abc_Ntk_t * pNtk ); +static Abc_Obj_t * Abc_NodeFromGlobalBdds( Abc_Ntk_t * pNtkNew, DdManager * dd, DdNode * bFunc ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Collapses the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkCollapse( Abc_Ntk_t * pNtk, int fBddSizeMax, int fDualRail, int fReorder, int fVerbose ) +{ + Abc_Ntk_t * pNtkNew; + int clk = clock(); + + assert( Abc_NtkIsStrash(pNtk) ); + // compute the global BDDs + if ( Abc_NtkBuildGlobalBdds(pNtk, fBddSizeMax, 1, fReorder, fVerbose) == NULL ) + return NULL; + if ( fVerbose ) + { + DdManager * dd = Abc_NtkGlobalBddMan( pNtk ); + printf( "Shared BDD size = %6d nodes. ", Cudd_ReadKeys(dd) - Cudd_ReadDead(dd) ); + PRT( "BDD construction time", clock() - clk ); + } + + // create the new network + pNtkNew = Abc_NtkFromGlobalBdds( pNtk ); +// Abc_NtkFreeGlobalBdds( pNtk ); + Abc_NtkFreeGlobalBdds( pNtk, 1 ); + if ( pNtkNew == NULL ) + { +// Cudd_Quit( pNtk->pManGlob ); +// pNtk->pManGlob = NULL; + return NULL; + } +// Extra_StopManager( pNtk->pManGlob ); +// pNtk->pManGlob = NULL; + + // make the network minimum base + Abc_NtkMinimumBase( pNtkNew ); + + if ( pNtk->pExdc ) + pNtkNew->pExdc = Abc_NtkDup( pNtk->pExdc ); + + // make sure that everything is okay + if ( !Abc_NtkCheck( pNtkNew ) ) + { + printf( "Abc_NtkCollapse: The network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Derives the network with the given global BDD.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFromGlobalBdds( Abc_Ntk_t * pNtk ) +{ + ProgressBar * pProgress; + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pNode, * pDriver, * pNodeNew; +// DdManager * dd = pNtk->pManGlob; + DdManager * dd = Abc_NtkGlobalBddMan( pNtk ); + int i; + // start the new network + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_LOGIC, ABC_FUNC_BDD ); + // make sure the new manager has the same number of inputs + Cudd_bddIthVar( pNtkNew->pManFunc, dd->size-1 ); + // process the POs + pProgress = Extra_ProgressBarStart( stdout, Abc_NtkCoNum(pNtk) ); + Abc_NtkForEachCo( pNtk, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + pDriver = Abc_ObjFanin0(pNode); + if ( Abc_ObjIsCi(pDriver) && !strcmp(Abc_ObjName(pNode), Abc_ObjName(pDriver)) ) + { + Abc_ObjAddFanin( pNode->pCopy, pDriver->pCopy ); + continue; + } +// pNodeNew = Abc_NodeFromGlobalBdds( pNtkNew, dd, Vec_PtrEntry(pNtk->vFuncsGlob, i) ); + pNodeNew = Abc_NodeFromGlobalBdds( pNtkNew, dd, Abc_ObjGlobalBdd(pNode) ); + Abc_ObjAddFanin( pNode->pCopy, pNodeNew ); + } + Extra_ProgressBarStop( pProgress ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Derives the network with the given global BDD.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NodeFromGlobalBdds( Abc_Ntk_t * pNtkNew, DdManager * dd, DdNode * bFunc ) +{ + Abc_Obj_t * pNodeNew, * pTemp; + int i; + // create a new node + pNodeNew = Abc_NtkCreateNode( pNtkNew ); + // add the fanins in the order, in which they appear in the reordered manager + Abc_NtkForEachCi( pNtkNew, pTemp, i ) + Abc_ObjAddFanin( pNodeNew, Abc_NtkCi(pNtkNew, dd->invperm[i]) ); + // transfer the function + pNodeNew->pData = Extra_TransferLevelByLevel( dd, pNtkNew->pManFunc, bFunc ); Cudd_Ref( pNodeNew->pData ); + return pNodeNew; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcClpSop.c b/abc_with_bb_support/src/base/abci/abcClpSop.c new file mode 100644 index 000000000..c164c2053 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcClpSop.c @@ -0,0 +1,53 @@ +/**CFile**************************************************************** + + FileName [abcCollapse.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Collapsing the network into two-levels.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcCollapse.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Collapses the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkCollapseSop( Abc_Ntk_t * pNtk, int fVerbose ) +{ + Abc_Ntk_t * pNtkNew; + pNtkNew = NULL; + return pNtkNew; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcCut.c b/abc_with_bb_support/src/base/abci/abcCut.c new file mode 100644 index 000000000..4298b8dc3 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcCut.c @@ -0,0 +1,620 @@ +/**CFile**************************************************************** + + FileName [abcCut.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Interface to cut computation.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcCut.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "cut.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Abc_NtkPrintCuts( void * p, Abc_Ntk_t * pNtk, int fSeq ); +static void Abc_NtkPrintCuts_( void * p, Abc_Ntk_t * pNtk, int fSeq ); + +extern int nTotal, nGood, nEqual; + +static Vec_Int_t * Abc_NtkGetNodeAttributes( Abc_Ntk_t * pNtk ); +static int Abc_NtkComputeArea( Abc_Ntk_t * pNtk, Cut_Man_t * p ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes the cuts for the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Man_t * Abc_NtkCuts( Abc_Ntk_t * pNtk, Cut_Params_t * pParams ) +{ + ProgressBar * pProgress; + Cut_Man_t * p; + Abc_Obj_t * pObj, * pNode; + Vec_Ptr_t * vNodes; + Vec_Int_t * vChoices; + int i; + int clk = clock(); + + extern void Abc_NtkBalanceAttach( Abc_Ntk_t * pNtk ); + extern void Abc_NtkBalanceDetach( Abc_Ntk_t * pNtk ); + + nTotal = nGood = nEqual = 0; + + assert( Abc_NtkIsStrash(pNtk) ); + // start the manager + pParams->nIdsMax = Abc_NtkObjNumMax( pNtk ); + p = Cut_ManStart( pParams ); + // compute node attributes if local or global cuts are requested + if ( pParams->fGlobal || pParams->fLocal ) + { + extern Vec_Int_t * Abc_NtkGetNodeAttributes( Abc_Ntk_t * pNtk ); + Cut_ManSetNodeAttrs( p, Abc_NtkGetNodeAttributes(pNtk) ); + } + // prepare for cut dropping + if ( pParams->fDrop ) + Cut_ManSetFanoutCounts( p, Abc_NtkFanoutCounts(pNtk) ); + // set cuts for PIs + Abc_NtkForEachCi( pNtk, pObj, i ) + if ( Abc_ObjFanoutNum(pObj) > 0 ) + Cut_NodeSetTriv( p, pObj->Id ); + // compute cuts for internal nodes + vNodes = Abc_AigDfs( pNtk, 0, 1 ); // collects POs + vChoices = Vec_IntAlloc( 100 ); + pProgress = Extra_ProgressBarStart( stdout, Vec_PtrSize(vNodes) ); + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + // when we reached a CO, it is time to deallocate the cuts + if ( Abc_ObjIsCo(pObj) ) + { + if ( pParams->fDrop ) + Cut_NodeTryDroppingCuts( p, Abc_ObjFaninId0(pObj) ); + continue; + } + // skip constant node, it has no cuts +// if ( Abc_NodeIsConst(pObj) ) +// continue; + Extra_ProgressBarUpdate( pProgress, i, NULL ); + // compute the cuts to the internal node + Abc_NodeGetCuts( p, pObj, pParams->fDag, pParams->fTree ); + // consider dropping the fanins cuts + if ( pParams->fDrop ) + { + Cut_NodeTryDroppingCuts( p, Abc_ObjFaninId0(pObj) ); + Cut_NodeTryDroppingCuts( p, Abc_ObjFaninId1(pObj) ); + } + // add cuts due to choices + if ( Abc_AigNodeIsChoice(pObj) ) + { + Vec_IntClear( vChoices ); + for ( pNode = pObj; pNode; pNode = pNode->pData ) + Vec_IntPush( vChoices, pNode->Id ); + Cut_NodeUnionCuts( p, vChoices ); + } + } + Extra_ProgressBarStop( pProgress ); + Vec_PtrFree( vNodes ); + Vec_IntFree( vChoices ); + Cut_ManPrintStats( p ); +PRT( "TOTAL ", clock() - clk ); + printf( "Area = %d.\n", Abc_NtkComputeArea( pNtk, p ) ); +//Abc_NtkPrintCuts( p, pNtk, 0 ); +// Cut_ManPrintStatsToFile( p, pNtk->pSpec, clock() - clk ); + + // temporary printout of stats + if ( nTotal ) + printf( "Total cuts = %d. Good cuts = %d. Ratio = %5.2f\n", nTotal, nGood, ((double)nGood)/nTotal ); + return p; +} + +/**Function************************************************************* + + Synopsis [Cut computation using the oracle.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkCutsOracle( Abc_Ntk_t * pNtk, Cut_Oracle_t * p ) +{ + Abc_Obj_t * pObj; + Vec_Ptr_t * vNodes; + int i, clk = clock(); + int fDrop = Cut_OracleReadDrop(p); + + assert( Abc_NtkIsStrash(pNtk) ); + + // prepare cut droppping + if ( fDrop ) + Cut_OracleSetFanoutCounts( p, Abc_NtkFanoutCounts(pNtk) ); + + // set cuts for PIs + Abc_NtkForEachCi( pNtk, pObj, i ) + if ( Abc_ObjFanoutNum(pObj) > 0 ) + Cut_OracleNodeSetTriv( p, pObj->Id ); + + // compute cuts for internal nodes + vNodes = Abc_AigDfs( pNtk, 0, 1 ); // collects POs + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + // when we reached a CO, it is time to deallocate the cuts + if ( Abc_ObjIsCo(pObj) ) + { + if ( fDrop ) + Cut_OracleTryDroppingCuts( p, Abc_ObjFaninId0(pObj) ); + continue; + } + // skip constant node, it has no cuts +// if ( Abc_NodeIsConst(pObj) ) +// continue; + // compute the cuts to the internal node + Cut_OracleComputeCuts( p, pObj->Id, Abc_ObjFaninId0(pObj), Abc_ObjFaninId1(pObj), + Abc_ObjFaninC0(pObj), Abc_ObjFaninC1(pObj) ); + // consider dropping the fanins cuts + if ( fDrop ) + { + Cut_OracleTryDroppingCuts( p, Abc_ObjFaninId0(pObj) ); + Cut_OracleTryDroppingCuts( p, Abc_ObjFaninId1(pObj) ); + } + } + Vec_PtrFree( vNodes ); +//PRT( "Total", clock() - clk ); +//Abc_NtkPrintCuts_( p, pNtk, 0 ); +} + + +/**Function************************************************************* + + Synopsis [Computes the cuts for the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Man_t * Abc_NtkSeqCuts( Abc_Ntk_t * pNtk, Cut_Params_t * pParams ) +{ +/* + Cut_Man_t * p; + Abc_Obj_t * pObj, * pNode; + int i, nIters, fStatus; + Vec_Int_t * vChoices; + int clk = clock(); + + assert( Abc_NtkIsSeq(pNtk) ); + assert( pParams->fSeq ); +// assert( Abc_NtkIsDfsOrdered(pNtk) ); + + // start the manager + pParams->nIdsMax = Abc_NtkObjNumMax( pNtk ); + pParams->nCutSet = Abc_NtkCutSetNodeNum( pNtk ); + p = Cut_ManStart( pParams ); + + // set cuts for the constant node and the PIs + pObj = Abc_AigConst1(pNtk); + if ( Abc_ObjFanoutNum(pObj) > 0 ) + Cut_NodeSetTriv( p, pObj->Id ); + Abc_NtkForEachPi( pNtk, pObj, i ) + { +//printf( "Setting trivial cut %d.\n", pObj->Id ); + Cut_NodeSetTriv( p, pObj->Id ); + } + // label the cutset nodes and set their number in the array + // assign the elementary cuts to the cutset nodes + Abc_SeqForEachCutsetNode( pNtk, pObj, i ) + { + assert( pObj->fMarkC == 0 ); + pObj->fMarkC = 1; + pObj->pCopy = (Abc_Obj_t *)i; + Cut_NodeSetTriv( p, pObj->Id ); +//printf( "Setting trivial cut %d.\n", pObj->Id ); + } + + // process the nodes + vChoices = Vec_IntAlloc( 100 ); + for ( nIters = 0; nIters < 10; nIters++ ) + { +//printf( "ITERATION %d:\n", nIters ); + // compute the cuts for the internal nodes + Abc_AigForEachAnd( pNtk, pObj, i ) + { + Abc_NodeGetCutsSeq( p, pObj, nIters==0 ); + // add cuts due to choices + if ( Abc_AigNodeIsChoice(pObj) ) + { + Vec_IntClear( vChoices ); + for ( pNode = pObj; pNode; pNode = pNode->pData ) + Vec_IntPush( vChoices, pNode->Id ); + Cut_NodeUnionCutsSeq( p, vChoices, (pObj->fMarkC ? (int)pObj->pCopy : -1), nIters==0 ); + } + } + // merge the new cuts with the old cuts + Abc_NtkForEachPi( pNtk, pObj, i ) + Cut_NodeNewMergeWithOld( p, pObj->Id ); + Abc_AigForEachAnd( pNtk, pObj, i ) + Cut_NodeNewMergeWithOld( p, pObj->Id ); + // for the cutset, transfer temp cuts to new cuts + fStatus = 0; + Abc_SeqForEachCutsetNode( pNtk, pObj, i ) + fStatus |= Cut_NodeTempTransferToNew( p, pObj->Id, i ); + if ( fStatus == 0 ) + break; + } + Vec_IntFree( vChoices ); + + // if the status is not finished, transfer new to old for the cutset + Abc_SeqForEachCutsetNode( pNtk, pObj, i ) + Cut_NodeNewMergeWithOld( p, pObj->Id ); + + // transfer the old cuts to the new positions + Abc_NtkForEachObj( pNtk, pObj, i ) + Cut_NodeOldTransferToNew( p, pObj->Id ); + + // unlabel the cutset nodes + Abc_SeqForEachCutsetNode( pNtk, pObj, i ) + pObj->fMarkC = 0; +if ( pParams->fVerbose ) +{ + Cut_ManPrintStats( p ); +PRT( "TOTAL ", clock() - clk ); +printf( "Converged after %d iterations.\n", nIters ); +} +//Abc_NtkPrintCuts( p, pNtk, 1 ); + return p; +*/ + return NULL; +} + +/**Function************************************************************* + + Synopsis [Computes area.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkComputeArea( Abc_Ntk_t * pNtk, Cut_Man_t * p ) +{ + Abc_Obj_t * pObj; + int Counter, i; + Counter = 0; + Abc_NtkForEachCo( pNtk, pObj, i ) + Counter += Cut_ManMappingArea_rec( p, Abc_ObjFaninId0(pObj) ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Computes the cuts for the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void * Abc_NodeGetCutsRecursive( void * p, Abc_Obj_t * pObj, int fDag, int fTree ) +{ + void * pList; + if ( pList = Abc_NodeReadCuts( p, pObj ) ) + return pList; + Abc_NodeGetCutsRecursive( p, Abc_ObjFanin0(pObj), fDag, fTree ); + Abc_NodeGetCutsRecursive( p, Abc_ObjFanin1(pObj), fDag, fTree ); + return Abc_NodeGetCuts( p, pObj, fDag, fTree ); +} + +/**Function************************************************************* + + Synopsis [Computes the cuts for the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void * Abc_NodeGetCuts( void * p, Abc_Obj_t * pObj, int fDag, int fTree ) +{ + Abc_Obj_t * pFanin; + int fDagNode, fTriv, TreeCode = 0; +// assert( Abc_NtkIsStrash(pObj->pNtk) ); + assert( Abc_ObjFaninNum(pObj) == 2 ); + + + // check if the node is a DAG node + fDagNode = (Abc_ObjFanoutNum(pObj) > 1 && !Abc_NodeIsMuxControlType(pObj)); + // increment the counter of DAG nodes + if ( fDagNode ) Cut_ManIncrementDagNodes( p ); + // add the trivial cut if the node is a DAG node, or if we compute all cuts + fTriv = fDagNode || !fDag; + // check if fanins are DAG nodes + if ( fTree ) + { + pFanin = Abc_ObjFanin0(pObj); + TreeCode |= (Abc_ObjFanoutNum(pFanin) > 1 && !Abc_NodeIsMuxControlType(pFanin)); + pFanin = Abc_ObjFanin1(pObj); + TreeCode |= ((Abc_ObjFanoutNum(pFanin) > 1 && !Abc_NodeIsMuxControlType(pFanin)) << 1); + } + + + // changes due to the global/local cut computation + { + Cut_Params_t * pParams = Cut_ManReadParams(p); + if ( pParams->fLocal ) + { + Vec_Int_t * vNodeAttrs = Cut_ManReadNodeAttrs(p); + fDagNode = Vec_IntEntry( vNodeAttrs, pObj->Id ); + if ( fDagNode ) Cut_ManIncrementDagNodes( p ); +// fTriv = fDagNode || !pParams->fGlobal; + fTriv = !Vec_IntEntry( vNodeAttrs, pObj->Id ); + TreeCode = 0; + pFanin = Abc_ObjFanin0(pObj); + TreeCode |= Vec_IntEntry( vNodeAttrs, pFanin->Id ); + pFanin = Abc_ObjFanin1(pObj); + TreeCode |= (Vec_IntEntry( vNodeAttrs, pFanin->Id ) << 1); + } + } + return Cut_NodeComputeCuts( p, pObj->Id, Abc_ObjFaninId0(pObj), Abc_ObjFaninId1(pObj), + Abc_ObjFaninC0(pObj), Abc_ObjFaninC1(pObj), fTriv, TreeCode ); +} + +/**Function************************************************************* + + Synopsis [Computes the cuts for the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeGetCutsSeq( void * p, Abc_Obj_t * pObj, int fTriv ) +{ +/* + int CutSetNum; + assert( Abc_NtkIsSeq(pObj->pNtk) ); + assert( Abc_ObjFaninNum(pObj) == 2 ); + fTriv = pObj->fMarkC ? 0 : fTriv; + CutSetNum = pObj->fMarkC ? (int)pObj->pCopy : -1; + Cut_NodeComputeCutsSeq( p, pObj->Id, Abc_ObjFaninId0(pObj), Abc_ObjFaninId1(pObj), + Abc_ObjFaninC0(pObj), Abc_ObjFaninC1(pObj), Seq_ObjFaninL0(pObj), Seq_ObjFaninL1(pObj), fTriv, CutSetNum ); +*/ +} + +/**Function************************************************************* + + Synopsis [Computes the cuts for the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void * Abc_NodeReadCuts( void * p, Abc_Obj_t * pObj ) +{ + return Cut_NodeReadCutsNew( p, pObj->Id ); +} + +/**Function************************************************************* + + Synopsis [Computes the cuts for the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeFreeCuts( void * p, Abc_Obj_t * pObj ) +{ + Cut_NodeFreeCuts( p, pObj->Id ); +} + +/**Function************************************************************* + + Synopsis [Computes the cuts for the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintCuts( void * p, Abc_Ntk_t * pNtk, int fSeq ) +{ + Cut_Man_t * pMan = p; + Cut_Cut_t * pList; + Abc_Obj_t * pObj; + int i; + printf( "Cuts of the network:\n" ); + Abc_NtkForEachObj( pNtk, pObj, i ) + { + pList = Abc_NodeReadCuts( p, pObj ); + printf( "Node %s:\n", Abc_ObjName(pObj) ); + Cut_CutPrintList( pList, fSeq ); + } +} + +/**Function************************************************************* + + Synopsis [Computes the cuts for the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintCuts_( void * p, Abc_Ntk_t * pNtk, int fSeq ) +{ + Cut_Man_t * pMan = p; + Cut_Cut_t * pList; + Abc_Obj_t * pObj; + pObj = Abc_NtkObj( pNtk, 2 * Abc_NtkObjNum(pNtk) / 3 ); + pList = Abc_NodeReadCuts( p, pObj ); + printf( "Node %s:\n", Abc_ObjName(pObj) ); + Cut_CutPrintList( pList, fSeq ); +} + + + + +/**Function************************************************************* + + Synopsis [Assigns global attributes randomly.] + + Description [Old code.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Abc_NtkGetNodeAttributes( Abc_Ntk_t * pNtk ) +{ + Vec_Int_t * vAttrs; +// Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj;//, * pTemp; + int i;//, k; + int nNodesTotal = 0, nMffcsTotal = 0; + extern Vec_Ptr_t * Abc_NodeMffsInsideCollect( Abc_Obj_t * pNode ); + + vAttrs = Vec_IntStart( Abc_NtkObjNumMax(pNtk) + 1 ); +// Abc_NtkForEachCi( pNtk, pObj, i ) +// Vec_IntWriteEntry( vAttrs, pObj->Id, 1 ); + + Abc_NtkForEachObj( pNtk, pObj, i ) + { + if ( Abc_ObjIsNode(pObj) ) + nNodesTotal++; + if ( Abc_ObjIsCo(pObj) && Abc_ObjIsNode(Abc_ObjFanin0(pObj)) ) + nMffcsTotal += Abc_NodeMffcSize( Abc_ObjFanin0(pObj) ); +// if ( Abc_ObjIsNode(pObj) && (rand() % 4 == 0) ) +// if ( Abc_ObjIsNode(pObj) && Abc_ObjFanoutNum(pObj) > 1 && !Abc_NodeIsMuxControlType(pObj) && (rand() % 3 == 0) ) + if ( Abc_ObjIsNode(pObj) && Abc_ObjFanoutNum(pObj) > 1 && !Abc_NodeIsMuxControlType(pObj) ) + { + int nMffc = Abc_NodeMffcSize(pObj); + nMffcsTotal += Abc_NodeMffcSize(pObj); +// printf( "%d ", nMffc ); + + if ( nMffc > 2 || Abc_ObjFanoutNum(pObj) > 8 ) + Vec_IntWriteEntry( vAttrs, pObj->Id, 1 ); + } + } +/* + Abc_NtkForEachObj( pNtk, pObj, i ) + { + if ( Vec_IntEntry( vAttrs, pObj->Id ) ) + { + vNodes = Abc_NodeMffsInsideCollect( pObj ); + Vec_PtrForEachEntry( vNodes, pTemp, k ) + if ( pTemp != pObj ) + Vec_IntWriteEntry( vAttrs, pTemp->Id, 0 ); + Vec_PtrFree( vNodes ); + } + } +*/ + printf( "Total nodes = %d. Total MFFC nodes = %d.\n", nNodesTotal, nMffcsTotal ); + return vAttrs; +} + +/**Function************************************************************* + + Synopsis [Assigns global attributes randomly.] + + Description [Old code.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkSubDagSize_rec( Abc_Obj_t * pObj, Vec_Int_t * vAttrs ) +{ + if ( Abc_NodeIsTravIdCurrent(pObj) ) + return 0; + Abc_NodeSetTravIdCurrent(pObj); + if ( Vec_IntEntry( vAttrs, pObj->Id ) ) + return 0; + if ( Abc_ObjIsCi(pObj) ) + return 1; + assert( Abc_ObjFaninNum(pObj) == 2 ); + return 1 + Abc_NtkSubDagSize_rec(Abc_ObjFanin0(pObj), vAttrs) + + Abc_NtkSubDagSize_rec(Abc_ObjFanin1(pObj), vAttrs); +} + +/**Function************************************************************* + + Synopsis [Assigns global attributes randomly.] + + Description [Old code.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Abc_NtkGetNodeAttributes2( Abc_Ntk_t * pNtk ) +{ + Vec_Int_t * vAttrs; + Abc_Obj_t * pObj; + int i, nSize; + assert( Abc_NtkIsDfsOrdered(pNtk) ); + vAttrs = Vec_IntStart( Abc_NtkObjNumMax(pNtk) + 1 ); + Abc_NtkForEachObj( pNtk, pObj, i ) + { + // skip no-nodes and nodes without fanouts + if ( pObj->Id == 0 || !(Abc_ObjIsNode(pObj) && Abc_ObjFanoutNum(pObj) > 1 && !Abc_NodeIsMuxControlType(pObj)) ) + continue; + // the node has more than one fanout - count its sub-DAG size + Abc_NtkIncrementTravId( pNtk ); + nSize = Abc_NtkSubDagSize_rec( pObj, vAttrs ); + if ( nSize > 15 ) + Vec_IntWriteEntry( vAttrs, pObj->Id, 1 ); + } + return vAttrs; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcDar.c b/abc_with_bb_support/src/base/abci/abcDar.c new file mode 100644 index 000000000..ca568ab93 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcDar.c @@ -0,0 +1,1017 @@ +/**CFile**************************************************************** + + FileName [abcDar.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [DAG-aware rewriting.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcDar.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "aig.h" +#include "dar.h" +#include "cnf.h" +#include "fra.h" +#include "fraig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Converts the network from the AIG manager into ABC.] + + Description [Assumes that registers are ordered after PIs/POs.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Man_t * Abc_NtkToDar( Abc_Ntk_t * pNtk, int fRegisters ) +{ + Aig_Man_t * pMan; + Aig_Obj_t * pObjNew; + Abc_Obj_t * pObj; + int i, nNodes; + // make sure the latches follow PIs/POs + if ( fRegisters ) + { + assert( Abc_NtkBoxNum(pNtk) == Abc_NtkLatchNum(pNtk) ); + Abc_NtkForEachCi( pNtk, pObj, i ) + if ( i < Abc_NtkPiNum(pNtk) ) + assert( Abc_ObjIsPi(pObj) ); + else + assert( Abc_ObjIsBo(pObj) ); + Abc_NtkForEachCo( pNtk, pObj, i ) + if ( i < Abc_NtkPoNum(pNtk) ) + assert( Abc_ObjIsPo(pObj) ); + else + assert( Abc_ObjIsBi(pObj) ); + } + // create the manager + pMan = Aig_ManStart( Abc_NtkNodeNum(pNtk) + 100 ); + // save the number of registers + if ( fRegisters ) + pMan->nRegs = Abc_NtkLatchNum(pNtk); + // transfer the pointers to the basic nodes + Abc_AigConst1(pNtk)->pCopy = (Abc_Obj_t *)Aig_ManConst1(pMan); + Abc_NtkForEachCi( pNtk, pObj, i ) + pObj->pCopy = (Abc_Obj_t *)Aig_ObjCreatePi(pMan); + // complement the 1-values registers + if ( fRegisters ) + Abc_NtkForEachLatch( pNtk, pObj, i ) + if ( Abc_LatchIsInit1(pObj) ) + Abc_ObjFanout0(pObj)->pCopy = Abc_ObjNot(Abc_ObjFanout0(pObj)->pCopy); + // perform the conversion of the internal nodes (assumes DFS ordering) + Abc_NtkForEachNode( pNtk, pObj, i ) + { + pObj->pCopy = (Abc_Obj_t *)Aig_And( pMan, (Aig_Obj_t *)Abc_ObjChild0Copy(pObj), (Aig_Obj_t *)Abc_ObjChild1Copy(pObj) ); +// printf( "%d->%d ", pObj->Id, ((Aig_Obj_t *)pObj->pCopy)->Id ); + } + // create the POs + Abc_NtkForEachCo( pNtk, pObj, i ) + Aig_ObjCreatePo( pMan, (Aig_Obj_t *)Abc_ObjChild0Copy(pObj) ); + // complement the 1-valued registers + if ( fRegisters ) + Aig_ManForEachLiSeq( pMan, pObjNew, i ) + if ( Abc_LatchIsInit1(Abc_ObjFanout0(Abc_NtkCo(pNtk,i))) ) + pObjNew->pFanin0 = Aig_Not(pObjNew->pFanin0); + // remove dangling nodes + if ( nNodes = Aig_ManCleanup( pMan ) ) + printf( "Abc_NtkToDar(): Unexpected %d dangling nodes when converting to AIG!\n", nNodes ); + if ( !Aig_ManCheck( pMan ) ) + { + printf( "Abc_NtkToDar: AIG check has failed.\n" ); + Aig_ManStop( pMan ); + return NULL; + } + return pMan; +} + +/**Function************************************************************* + + Synopsis [Converts the network from the AIG manager into ABC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFromDar( Abc_Ntk_t * pNtkOld, Aig_Man_t * pMan ) +{ + Vec_Ptr_t * vNodes; + Abc_Ntk_t * pNtkNew; + Aig_Obj_t * pObj; + int i; + assert( Aig_ManRegNum(pMan) == Abc_NtkLatchNum(pNtkOld) ); + // perform strashing + pNtkNew = Abc_NtkStartFrom( pNtkOld, ABC_NTK_STRASH, ABC_FUNC_AIG ); + // transfer the pointers to the basic nodes + Aig_ManConst1(pMan)->pData = Abc_AigConst1(pNtkNew); + Aig_ManForEachPi( pMan, pObj, i ) + pObj->pData = Abc_NtkCi(pNtkNew, i); + // rebuild the AIG + vNodes = Aig_ManDfs( pMan ); + Vec_PtrForEachEntry( vNodes, pObj, i ) + if ( Aig_ObjIsBuf(pObj) ) + pObj->pData = (Abc_Obj_t *)Aig_ObjChild0Copy(pObj); + else + pObj->pData = Abc_AigAnd( pNtkNew->pManFunc, (Abc_Obj_t *)Aig_ObjChild0Copy(pObj), (Abc_Obj_t *)Aig_ObjChild1Copy(pObj) ); + Vec_PtrFree( vNodes ); + // connect the PO nodes + Aig_ManForEachPo( pMan, pObj, i ) + Abc_ObjAddFanin( Abc_NtkCo(pNtkNew, i), (Abc_Obj_t *)Aig_ObjChild0Copy(pObj) ); + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkFromDar(): Network check has failed.\n" ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Converts the network from the AIG manager into ABC.] + + Description [This procedure should be called after seq sweeping, + which changes the number of registers.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFromDarSeqSweep( Abc_Ntk_t * pNtkOld, Aig_Man_t * pMan ) +{ + Vec_Ptr_t * vNodes; + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObjNew; + Aig_Obj_t * pObj, * pObjLo, * pObjLi; + int i; + assert( Aig_ManRegNum(pMan) < Abc_NtkLatchNum(pNtkOld) ); + // perform strashing + pNtkNew = Abc_NtkStartFromNoLatches( pNtkOld, ABC_NTK_STRASH, ABC_FUNC_AIG ); + // transfer the pointers to the basic nodes + Aig_ManConst1(pMan)->pData = Abc_AigConst1(pNtkNew); + Aig_ManForEachPiSeq( pMan, pObj, i ) + pObj->pData = Abc_NtkCi(pNtkNew, i); + // create as many latches as there are registers in the manager + Aig_ManForEachLiLoSeq( pMan, pObjLi, pObjLo, i ) + { + pObjNew = Abc_NtkCreateLatch( pNtkNew ); + pObjLi->pData = Abc_NtkCreateBi( pNtkNew ); + pObjLo->pData = Abc_NtkCreateBo( pNtkNew ); + Abc_ObjAddFanin( pObjNew, pObjLi->pData ); + Abc_ObjAddFanin( pObjLo->pData, pObjNew ); + Abc_LatchSetInit0( pObjNew ); + } + Abc_NtkAddDummyBoxNames( pNtkNew ); + // rebuild the AIG + vNodes = Aig_ManDfs( pMan ); + Vec_PtrForEachEntry( vNodes, pObj, i ) + if ( Aig_ObjIsBuf(pObj) ) + pObj->pData = (Abc_Obj_t *)Aig_ObjChild0Copy(pObj); + else + pObj->pData = Abc_AigAnd( pNtkNew->pManFunc, (Abc_Obj_t *)Aig_ObjChild0Copy(pObj), (Abc_Obj_t *)Aig_ObjChild1Copy(pObj) ); + Vec_PtrFree( vNodes ); + // connect the PO nodes + Aig_ManForEachPo( pMan, pObj, i ) + Abc_ObjAddFanin( Abc_NtkCo(pNtkNew, i), (Abc_Obj_t *)Aig_ObjChild0Copy(pObj) ); + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkFromDar(): Network check has failed.\n" ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Converts the network from the AIG manager into ABC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFromDarChoices( Abc_Ntk_t * pNtkOld, Aig_Man_t * pMan ) +{ + Vec_Ptr_t * vNodes; + Abc_Ntk_t * pNtkNew; + Aig_Obj_t * pObj, * pTemp; + int i; + assert( pMan->pEquivs != NULL ); + assert( Aig_ManBufNum(pMan) == 0 ); + // perform strashing + pNtkNew = Abc_NtkStartFrom( pNtkOld, ABC_NTK_STRASH, ABC_FUNC_AIG ); + // transfer the pointers to the basic nodes + Aig_ManConst1(pMan)->pData = Abc_AigConst1(pNtkNew); + Aig_ManForEachPi( pMan, pObj, i ) + pObj->pData = Abc_NtkCi(pNtkNew, i); + // rebuild the AIG + vNodes = Aig_ManDfsChoices( pMan ); + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + pObj->pData = Abc_AigAnd( pNtkNew->pManFunc, (Abc_Obj_t *)Aig_ObjChild0Copy(pObj), (Abc_Obj_t *)Aig_ObjChild1Copy(pObj) ); + if ( pTemp = pMan->pEquivs[pObj->Id] ) + { + Abc_Obj_t * pAbcRepr, * pAbcObj; + assert( pTemp->pData != NULL ); + pAbcRepr = pObj->pData; + pAbcObj = pTemp->pData; + pAbcObj->pData = pAbcRepr->pData; + pAbcRepr->pData = pAbcObj; + } + } +printf( "Total = %d. Collected = %d.\n", Aig_ManNodeNum(pMan), Vec_PtrSize(vNodes) ); + Vec_PtrFree( vNodes ); +/* + { + Abc_Obj_t * pNode; + int k, Counter = 0; + Abc_NtkForEachNode( pNtkNew, pNode, k ) + if ( pNode->pData != 0 ) + { + int x = 0; + Counter++; + } + printf( "Choices = %d.\n", Counter ); + } +*/ + // connect the PO nodes + Aig_ManForEachPo( pMan, pObj, i ) + Abc_ObjAddFanin( Abc_NtkCo(pNtkNew, i), (Abc_Obj_t *)Aig_ObjChild0Copy(pObj) ); + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkFromDar(): Network check has failed.\n" ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Converts the network from the AIG manager into ABC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFromDarSeq( Abc_Ntk_t * pNtkOld, Aig_Man_t * pMan ) +{ + Vec_Ptr_t * vNodes; + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObjNew, * pFaninNew, * pFaninNew0, * pFaninNew1; + Aig_Obj_t * pObj; + int i; +// assert( Aig_ManLatchNum(pMan) > 0 ); + // perform strashing + pNtkNew = Abc_NtkStartFromNoLatches( pNtkOld, ABC_NTK_STRASH, ABC_FUNC_AIG ); + // transfer the pointers to the basic nodes + Aig_ManConst1(pMan)->pData = Abc_AigConst1(pNtkNew); + Aig_ManForEachPi( pMan, pObj, i ) + pObj->pData = Abc_NtkPi(pNtkNew, i); + // create latches of the new network + Aig_ManForEachObj( pMan, pObj, i ) + { + if ( !Aig_ObjIsLatch(pObj) ) + continue; + pObjNew = Abc_NtkCreateLatch( pNtkNew ); + pFaninNew0 = Abc_NtkCreateBi( pNtkNew ); + pFaninNew1 = Abc_NtkCreateBo( pNtkNew ); + Abc_ObjAddFanin( pObjNew, pFaninNew0 ); + Abc_ObjAddFanin( pFaninNew1, pObjNew ); + Abc_LatchSetInit0( pObjNew ); + pObj->pData = Abc_ObjFanout0( pObjNew ); + } + Abc_NtkAddDummyBoxNames( pNtkNew ); + // rebuild the AIG + vNodes = Aig_ManDfs( pMan ); + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + // add the first fanin + pObj->pData = pFaninNew0 = (Abc_Obj_t *)Aig_ObjChild0Copy(pObj); + if ( Aig_ObjIsBuf(pObj) ) + continue; + // add the second fanin + pFaninNew1 = (Abc_Obj_t *)Aig_ObjChild1Copy(pObj); + // create the new node + if ( Aig_ObjIsExor(pObj) ) + pObj->pData = pObjNew = Abc_AigXor( pNtkNew->pManFunc, pFaninNew0, pFaninNew1 ); + else + pObj->pData = pObjNew = Abc_AigAnd( pNtkNew->pManFunc, pFaninNew0, pFaninNew1 ); + } + Vec_PtrFree( vNodes ); + // connect the PO nodes + Aig_ManForEachPo( pMan, pObj, i ) + { + pFaninNew = (Abc_Obj_t *)Aig_ObjChild0Copy( pObj ); + Abc_ObjAddFanin( Abc_NtkPo(pNtkNew, i), pFaninNew ); + } + // connect the latches + Aig_ManForEachObj( pMan, pObj, i ) + { + if ( !Aig_ObjIsLatch(pObj) ) + continue; + pFaninNew = (Abc_Obj_t *)Aig_ObjChild0Copy( pObj ); + Abc_ObjAddFanin( Abc_ObjFanin0(Abc_ObjFanin0(pObj->pData)), pFaninNew ); + } + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkFromIvySeq(): Network check has failed.\n" ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Collect latch values.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Abc_NtkGetLatchValues( Abc_Ntk_t * pNtk ) +{ + Vec_Int_t * vInits; + Abc_Obj_t * pLatch; + int i; + vInits = Vec_IntAlloc( Abc_NtkLatchNum(pNtk) ); + Abc_NtkForEachLatch( pNtk, pLatch, i ) + { + assert( Abc_LatchIsInit1(pLatch) == 0 ); + Vec_IntPush( vInits, Abc_LatchIsInit1(pLatch) ); + } + return vInits; +} + +/**Function************************************************************* + + Synopsis [Performs verification after retiming.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkSecRetime( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2 ) +{ + int fRemove1, fRemove2; + Aig_Man_t * pMan1, * pMan2; + int * pArray; + + fRemove1 = (!Abc_NtkIsStrash(pNtk1)) && (pNtk1 = Abc_NtkStrash(pNtk1, 0, 0, 0)); + fRemove2 = (!Abc_NtkIsStrash(pNtk2)) && (pNtk2 = Abc_NtkStrash(pNtk2, 0, 0, 0)); + + + pMan1 = Abc_NtkToDar( pNtk1, 0 ); + pMan2 = Abc_NtkToDar( pNtk2, 0 ); + + Aig_ManPrintStats( pMan1 ); + Aig_ManPrintStats( pMan2 ); + +// pArray = Abc_NtkGetLatchValues(pNtk1); + pArray = NULL; + Aig_ManSeqStrash( pMan1, Abc_NtkLatchNum(pNtk1), pArray ); + free( pArray ); + +// pArray = Abc_NtkGetLatchValues(pNtk2); + pArray = NULL; + Aig_ManSeqStrash( pMan2, Abc_NtkLatchNum(pNtk2), pArray ); + free( pArray ); + + Aig_ManPrintStats( pMan1 ); + Aig_ManPrintStats( pMan2 ); + + Aig_ManStop( pMan1 ); + Aig_ManStop( pMan2 ); + + + if ( fRemove1 ) Abc_NtkDelete( pNtk1 ); + if ( fRemove2 ) Abc_NtkDelete( pNtk2 ); +} + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkDar( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkAig; + Aig_Man_t * pMan;//, * pTemp; +// int * pArray; + + assert( Abc_NtkIsStrash(pNtk) ); + // convert to the AIG manager + pMan = Abc_NtkToDar( pNtk, 0 ); + if ( pMan == NULL ) + return NULL; + if ( !Aig_ManCheck( pMan ) ) + { + printf( "Abc_NtkDar: AIG check has failed.\n" ); + Aig_ManStop( pMan ); + return NULL; + } + // perform balance + Aig_ManPrintStats( pMan ); +/* + pArray = Abc_NtkGetLatchValues(pNtk); + Aig_ManSeqStrash( pMan, Abc_NtkLatchNum(pNtk), pArray ); + free( pArray ); +*/ + +// Aig_ManDumpBlif( pMan, "aig_temp.blif" ); +// pMan->pPars = Dar_ManDefaultRwrPars(); + Dar_ManRewrite( pMan, NULL ); + Aig_ManPrintStats( pMan ); +// Dar_ManComputeCuts( pMan ); + +/* +{ +extern Aig_Cnf_t * Aig_ManDeriveCnf( Aig_Man_t * p ); +extern void Aig_CnfFree( Aig_Cnf_t * pCnf ); +Aig_Cnf_t * pCnf; +pCnf = Aig_ManDeriveCnf( pMan ); +Aig_CnfFree( pCnf ); +} +*/ + + // convert from the AIG manager + if ( Aig_ManLatchNum(pMan) ) + pNtkAig = Abc_NtkFromDarSeq( pNtk, pMan ); + else + pNtkAig = Abc_NtkFromDar( pNtk, pMan ); + if ( pNtkAig == NULL ) + return NULL; + Aig_ManStop( pMan ); + // make sure everything is okay + if ( !Abc_NtkCheck( pNtkAig ) ) + { + printf( "Abc_NtkDar: The network check has failed.\n" ); + Abc_NtkDelete( pNtkAig ); + return NULL; + } + return pNtkAig; +} + + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkDarFraig( Abc_Ntk_t * pNtk, int nConfLimit, int fDoSparse, int fProve, int fTransfer, int fSpeculate, int fChoicing, int fVerbose ) +{ + Fra_Par_t Pars, * pPars = &Pars; + Abc_Ntk_t * pNtkAig; + Aig_Man_t * pMan, * pTemp; + pMan = Abc_NtkToDar( pNtk, 0 ); + if ( pMan == NULL ) + return NULL; + Fra_ParamsDefault( pPars ); + pPars->nBTLimitNode = nConfLimit; + pPars->fVerbose = fVerbose; + pPars->fProve = fProve; + pPars->fDoSparse = fDoSparse; + pPars->fSpeculate = fSpeculate; + pPars->fChoicing = fChoicing; + pMan = Fra_FraigPerform( pTemp = pMan, pPars ); + if ( fChoicing ) + pNtkAig = Abc_NtkFromDarChoices( pNtk, pMan ); + else + pNtkAig = Abc_NtkFromDar( pNtk, pMan ); + Aig_ManStop( pTemp ); + Aig_ManStop( pMan ); + return pNtkAig; +} + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkCSweep( Abc_Ntk_t * pNtk, int nCutsMax, int nLeafMax, int fVerbose ) +{ + extern Aig_Man_t * Csw_Sweep( Aig_Man_t * pAig, int nCutsMax, int nLeafMax, int fVerbose ); + Abc_Ntk_t * pNtkAig; + Aig_Man_t * pMan, * pTemp; + pMan = Abc_NtkToDar( pNtk, 0 ); + if ( pMan == NULL ) + return NULL; + pMan = Csw_Sweep( pTemp = pMan, nCutsMax, nLeafMax, fVerbose ); + pNtkAig = Abc_NtkFromDar( pNtk, pMan ); + Aig_ManStop( pTemp ); + Aig_ManStop( pMan ); + return pNtkAig; +} + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkDRewrite( Abc_Ntk_t * pNtk, Dar_RwrPar_t * pPars ) +{ + Aig_Man_t * pMan, * pTemp; + Abc_Ntk_t * pNtkAig; + int clk; + assert( Abc_NtkIsStrash(pNtk) ); + pMan = Abc_NtkToDar( pNtk, 0 ); + if ( pMan == NULL ) + return NULL; +// Aig_ManPrintStats( pMan ); +/* +// Aig_ManSupports( pMan ); + { + Vec_Vec_t * vParts; + vParts = Aig_ManPartitionSmart( pMan, 50, 1, NULL ); + Vec_VecFree( vParts ); + } +*/ + Dar_ManRewrite( pMan, pPars ); +// pMan = Dar_ManBalance( pTemp = pMan, pPars->fUpdateLevel ); +// Aig_ManStop( pTemp ); + +clk = clock(); + pMan = Aig_ManDup( pTemp = pMan, 0 ); + Aig_ManStop( pTemp ); +//PRT( "time", clock() - clk ); + +// Aig_ManPrintStats( pMan ); + pNtkAig = Abc_NtkFromDar( pNtk, pMan ); + Aig_ManStop( pMan ); + return pNtkAig; +} + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkDRefactor( Abc_Ntk_t * pNtk, Dar_RefPar_t * pPars ) +{ + Aig_Man_t * pMan, * pTemp; + Abc_Ntk_t * pNtkAig; + int clk; + assert( Abc_NtkIsStrash(pNtk) ); + pMan = Abc_NtkToDar( pNtk, 0 ); + if ( pMan == NULL ) + return NULL; +// Aig_ManPrintStats( pMan ); + + Dar_ManRefactor( pMan, pPars ); +// pMan = Dar_ManBalance( pTemp = pMan, pPars->fUpdateLevel ); +// Aig_ManStop( pTemp ); + +clk = clock(); + pMan = Aig_ManDup( pTemp = pMan, 0 ); + Aig_ManStop( pTemp ); +//PRT( "time", clock() - clk ); + +// Aig_ManPrintStats( pMan ); + pNtkAig = Abc_NtkFromDar( pNtk, pMan ); + Aig_ManStop( pMan ); + return pNtkAig; +} + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkDCompress2( Abc_Ntk_t * pNtk, int fBalance, int fUpdateLevel, int fVerbose ) +{ + Aig_Man_t * pMan;//, * pTemp; + Abc_Ntk_t * pNtkAig; + int clk; + assert( Abc_NtkIsStrash(pNtk) ); + pMan = Abc_NtkToDar( pNtk, 0 ); + if ( pMan == NULL ) + return NULL; +// Aig_ManPrintStats( pMan ); + +clk = clock(); + pMan = Dar_ManCompress2( pMan, fBalance, fUpdateLevel, fVerbose ); +// Aig_ManStop( pTemp ); +//PRT( "time", clock() - clk ); + +// Aig_ManPrintStats( pMan ); + pNtkAig = Abc_NtkFromDar( pNtk, pMan ); + Aig_ManStop( pMan ); + return pNtkAig; +} + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkDrwsat( Abc_Ntk_t * pNtk, int fBalance, int fVerbose ) +{ + Aig_Man_t * pMan;//, * pTemp; + Abc_Ntk_t * pNtkAig; + int clk; + assert( Abc_NtkIsStrash(pNtk) ); + pMan = Abc_NtkToDar( pNtk, 0 ); + if ( pMan == NULL ) + return NULL; +// Aig_ManPrintStats( pMan ); + +clk = clock(); + pMan = Dar_ManRwsat( pMan, fBalance, fVerbose ); +// Aig_ManStop( pTemp ); +//PRT( "time", clock() - clk ); + +// Aig_ManPrintStats( pMan ); + pNtkAig = Abc_NtkFromDar( pNtk, pMan ); + Aig_ManStop( pMan ); + return pNtkAig; +} + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkConstructFromCnf( Abc_Ntk_t * pNtk, Cnf_Man_t * p, Vec_Ptr_t * vMapped ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pNode, * pNodeNew; + Aig_Obj_t * pObj, * pLeaf; + Cnf_Cut_t * pCut; + Vec_Int_t * vCover; + unsigned uTruth; + int i, k, nDupGates; + // create the new network + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_LOGIC, ABC_FUNC_SOP ); + // make the mapper point to the new network + Aig_ManConst1(p->pManAig)->pData = Abc_NtkCreateNodeConst1(pNtkNew); + Abc_NtkForEachCi( pNtk, pNode, i ) + Aig_ManPi(p->pManAig, i)->pData = pNode->pCopy; + // process the nodes in topological order + vCover = Vec_IntAlloc( 1 << 16 ); + Vec_PtrForEachEntry( vMapped, pObj, i ) + { + // create new node + pNodeNew = Abc_NtkCreateNode( pNtkNew ); + // add fanins according to the cut + pCut = pObj->pData; + Cnf_CutForEachLeaf( p->pManAig, pCut, pLeaf, k ) + Abc_ObjAddFanin( pNodeNew, pLeaf->pData ); + // add logic function + if ( pCut->nFanins < 5 ) + { + uTruth = 0xFFFF & *Cnf_CutTruth(pCut); + Cnf_SopConvertToVector( p->pSops[uTruth], p->pSopSizes[uTruth], vCover ); + pNodeNew->pData = Abc_SopCreateFromIsop( pNtkNew->pManFunc, pCut->nFanins, vCover ); + } + else + pNodeNew->pData = Abc_SopCreateFromIsop( pNtkNew->pManFunc, pCut->nFanins, pCut->vIsop[1] ); + // save the node + pObj->pData = pNodeNew; + } + Vec_IntFree( vCover ); + // add the CO drivers + Abc_NtkForEachCo( pNtk, pNode, i ) + { + pObj = Aig_ManPo(p->pManAig, i); + pNodeNew = Abc_ObjNotCond( Aig_ObjFanin0(pObj)->pData, Aig_ObjFaninC0(pObj) ); + Abc_ObjAddFanin( pNode->pCopy, pNodeNew ); + } + + // remove the constant node if not used + pNodeNew = (Abc_Obj_t *)Aig_ManConst1(p->pManAig)->pData; + if ( Abc_ObjFanoutNum(pNodeNew) == 0 ) + Abc_NtkDeleteObj( pNodeNew ); + // minimize the node +// Abc_NtkSweep( pNtkNew, 0 ); + // decouple the PO driver nodes to reduce the number of levels + nDupGates = Abc_NtkLogicMakeSimpleCos( pNtkNew, 1 ); +// if ( nDupGates && If_ManReadVerbose(pIfMan) ) +// printf( "Duplicated %d gates to decouple the CO drivers.\n", nDupGates ); + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkConstructFromCnf(): Network check has failed.\n" ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkDarToCnf( Abc_Ntk_t * pNtk, char * pFileName ) +{ + Vec_Ptr_t * vMapped; + Aig_Man_t * pMan; + Cnf_Man_t * pManCnf; + Cnf_Dat_t * pCnf; + Abc_Ntk_t * pNtkNew = NULL; + assert( Abc_NtkIsStrash(pNtk) ); + + // convert to the AIG manager + pMan = Abc_NtkToDar( pNtk, 0 ); + if ( pMan == NULL ) + return NULL; + if ( !Aig_ManCheck( pMan ) ) + { + printf( "Abc_NtkDarToCnf: AIG check has failed.\n" ); + Aig_ManStop( pMan ); + return NULL; + } + // perform balance + Aig_ManPrintStats( pMan ); + + // derive CNF + pCnf = Cnf_Derive( pMan, 0 ); + pManCnf = Cnf_ManRead(); + + // write the network for verification + vMapped = Cnf_ManScanMapping( pManCnf, 1, 0 ); + pNtkNew = Abc_NtkConstructFromCnf( pNtk, pManCnf, vMapped ); + Vec_PtrFree( vMapped ); + + // write CNF into a file + Cnf_DataWriteIntoFile( pCnf, pFileName, 0 ); + Cnf_DataFree( pCnf ); + Cnf_ClearMemory(); + + Aig_ManStop( pMan ); + return pNtkNew; +} + + +/**Function************************************************************* + + Synopsis [Solves combinational miter using a SAT solver.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkDSat( Abc_Ntk_t * pNtk, sint64 nConfLimit, sint64 nInsLimit, int fVerbose ) +{ + sat_solver * pSat; + Aig_Man_t * pMan; + Cnf_Dat_t * pCnf; + int status, RetValue, clk = clock(); + Vec_Int_t * vCiIds; + + assert( Abc_NtkIsStrash(pNtk) ); + assert( Abc_NtkLatchNum(pNtk) == 0 ); + assert( Abc_NtkPoNum(pNtk) == 1 ); + + // conver to the manager + pMan = Abc_NtkToDar( pNtk, 0 ); + // derive CNF +// pCnf = Cnf_Derive( pMan, 0 ); + pCnf = Cnf_DeriveSimple( pMan, 0 ); + // convert into the SAT solver + pSat = Cnf_DataWriteIntoSolver( pCnf ); + vCiIds = Cnf_DataCollectPiSatNums( pCnf, pMan ); + Aig_ManStop( pMan ); + Cnf_DataFree( pCnf ); + // solve SAT + if ( pSat == NULL ) + { + Vec_IntFree( vCiIds ); +// printf( "Trivially unsat\n" ); + return 1; + } + + +// printf( "Created SAT problem with %d variable and %d clauses. ", sat_solver_nvars(pSat), sat_solver_nclauses(pSat) ); +// PRT( "Time", clock() - clk ); + + // simplify the problem + clk = clock(); + status = sat_solver_simplify(pSat); +// printf( "Simplified the problem to %d variables and %d clauses. ", sat_solver_nvars(pSat), sat_solver_nclauses(pSat) ); +// PRT( "Time", clock() - clk ); + if ( status == 0 ) + { + Vec_IntFree( vCiIds ); + sat_solver_delete( pSat ); +// printf( "The problem is UNSATISFIABLE after simplification.\n" ); + return 1; + } + + // solve the miter + clk = clock(); + if ( fVerbose ) + pSat->verbosity = 1; + status = sat_solver_solve( pSat, NULL, NULL, (sint64)nConfLimit, (sint64)nInsLimit, (sint64)0, (sint64)0 ); + if ( status == l_Undef ) + { +// printf( "The problem timed out.\n" ); + RetValue = -1; + } + else if ( status == l_True ) + { +// printf( "The problem is SATISFIABLE.\n" ); + RetValue = 0; + } + else if ( status == l_False ) + { +// printf( "The problem is UNSATISFIABLE.\n" ); + RetValue = 1; + } + else + assert( 0 ); +// PRT( "SAT sat_solver time", clock() - clk ); +// printf( "The number of conflicts = %d.\n", (int)pSat->sat_solver_stats.conflicts ); + + // if the problem is SAT, get the counterexample + if ( status == l_True ) + { + pNtk->pModel = Sat_SolverGetModel( pSat, vCiIds->pArray, vCiIds->nSize ); + } + // free the sat_solver + if ( fVerbose ) + Sat_SolverPrintStats( stdout, pSat ); +//sat_solver_store_write( pSat, "trace.cnf" ); +//sat_solver_store_free( pSat ); + sat_solver_delete( pSat ); + Vec_IntFree( vCiIds ); + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkSeqSweep( Abc_Ntk_t * pNtk, int nFramesK, int fRewrite, int fVerbose ) +{ + Abc_Ntk_t * pNtkAig; + Aig_Man_t * pMan, * pTemp; + pMan = Abc_NtkToDar( pNtk, 1 ); + if ( pMan == NULL ) + return NULL; +// pMan->nRegs = Abc_NtkLatchNum(pNtk); + + pMan = Fra_FraigInduction( pTemp = pMan, nFramesK, fRewrite, fVerbose, NULL ); + Aig_ManStop( pTemp ); + + if ( Aig_ManRegNum(pMan) < Abc_NtkLatchNum(pNtk) ) + pNtkAig = Abc_NtkFromDarSeqSweep( pNtk, pMan ); + else + pNtkAig = Abc_NtkFromDar( pNtk, pMan ); + Aig_ManStop( pMan ); + return pNtkAig; +} + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkDarSec( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int nFrames, int fVerbose, int fVeryVerbose ) +{ + Fraig_Params_t Params; + Aig_Man_t * pMan; + Abc_Ntk_t * pMiter, * pTemp; + int RetValue; + + // get the miter of the two networks + pMiter = Abc_NtkMiter( pNtk1, pNtk2, 0, 0 ); + if ( pMiter == NULL ) + { + printf( "Miter computation has failed.\n" ); + return 0; + } + RetValue = Abc_NtkMiterIsConstant( pMiter ); + if ( RetValue == 0 ) + { + extern void Abc_NtkVerifyReportErrorSeq( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int * pModel, int nFrames ); + printf( "Networks are NOT EQUIVALENT after structural hashing.\n" ); + // report the error + pMiter->pModel = Abc_NtkVerifyGetCleanModel( pMiter, nFrames ); + Abc_NtkVerifyReportErrorSeq( pNtk1, pNtk2, pMiter->pModel, nFrames ); + FREE( pMiter->pModel ); + Abc_NtkDelete( pMiter ); + return 0; + } + if ( RetValue == 1 ) + { + Abc_NtkDelete( pMiter ); + printf( "Networks are equivalent after structural hashing.\n" ); + return 1; + } + + // preprocess the miter by fraiging it + // (note that for each functional class, fraiging leaves one representative; + // so fraiging does not reduce the number of functions represented by nodes + Fraig_ParamsSetDefault( &Params ); + pMiter = Abc_NtkFraig( pTemp = pMiter, &Params, 0, 0 ); + Abc_NtkDelete( pTemp ); + + // derive the AIG manager + pMan = Abc_NtkToDar( pMiter, 1 ); + Abc_NtkDelete( pMiter ); + if ( pMan == NULL ) + { + printf( "Converting miter into AIG has failed.\n" ); + return -1; + } + assert( pMan->nRegs > 0 ); + + // perform verification + RetValue = Fra_FraigSec( pMan, nFrames, fVerbose, fVeryVerbose ); + Aig_ManStop( pMan ); + return RetValue; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcDebug.c b/abc_with_bb_support/src/base/abci/abcDebug.c new file mode 100644 index 000000000..4a6354ad1 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcDebug.c @@ -0,0 +1,208 @@ +/**CFile**************************************************************** + + FileName [abcDebug.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Automated debugging procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcDebug.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Abc_NtkCountFaninsTotal( Abc_Ntk_t * pNtk ); +static Abc_Ntk_t * Abc_NtkAutoDebugModify( Abc_Ntk_t * pNtk, int ObjNum, int fConst1 ); + +extern void Io_WriteBlifLogic( Abc_Ntk_t * pNtk, char * FileName, int fWriteLatches ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Takes a network and a procedure to test.] + + Description [The network demonstrates the bug in the procedure. + Procedure should return 1 if the bug is demonstrated.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkAutoDebug( Abc_Ntk_t * pNtk, int (*pFuncError) (Abc_Ntk_t *) ) +{ + Abc_Ntk_t * pNtkMod; + char * pFileName = "bug_found.blif"; + int i, nSteps, nIter, ModNum, RandNum = 1, clk, clkTotal = clock(); + assert( Abc_NtkIsLogic(pNtk) ); + srand( 0x123123 ); + // create internal copy of the network + pNtk = Abc_NtkDup(pNtk); + if ( !(*pFuncError)( pNtk ) ) + { + printf( "The original network does not cause the bug. Quitting.\n" ); + Abc_NtkDelete( pNtk ); + return; + } + // perform incremental modifications + for ( nIter = 0; ; nIter++ ) + { + clk = clock(); + // count how many ways of modifying the network exists + nSteps = 2 * Abc_NtkCountFaninsTotal(pNtk); + // try modifying the network as many times + RandNum ^= rand(); + for ( i = 0; i < nSteps; i++ ) + { + // get the shifted number of bug + ModNum = (i + RandNum) % nSteps; + // get the modified network + pNtkMod = Abc_NtkAutoDebugModify( pNtk, ModNum/2, ModNum%2 ); + // write the network + Io_WriteBlifLogic( pNtk, "bug_temp.blif", 1 ); + // check if the bug is still there + if ( (*pFuncError)( pNtkMod ) ) // bug is still there + { + Abc_NtkDelete( pNtk ); + pNtk = pNtkMod; + break; + } + else // no bug + Abc_NtkDelete( pNtkMod ); + } + printf( "Iter %6d : Latches = %6d. Nodes = %6d. Steps = %6d. Error step = %3d. ", + nIter, Abc_NtkLatchNum(pNtk), Abc_NtkNodeNum(pNtk), nSteps, i ); + PRT( "Time", clock() - clk ); + if ( i == nSteps ) // could not modify it while preserving the bug + break; + } + // write out the final network + Io_WriteBlifLogic( pNtk, pFileName, 1 ); + printf( "Final network written into file \"%s\". ", pFileName ); + PRT( "Total time", clock() - clkTotal ); + Abc_NtkDelete( pNtk ); +} + +/**Function************************************************************* + + Synopsis [Counts the total number of fanins.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkCountFaninsTotal( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj, * pFanin; + int i, k, Counter = 0; + Abc_NtkForEachObj( pNtk, pObj, i ) + Abc_ObjForEachFanin( pObj, pFanin, k ) + { + if ( !Abc_ObjIsNode(pObj) && !Abc_ObjIsPo(pObj) ) + continue; + if ( Abc_ObjIsPo(pObj) && Abc_NtkPoNum(pNtk) == 1 ) + continue; + if ( Abc_ObjIsNode(pObj) && Abc_NodeIsConst(pFanin) ) + continue; + Counter++; + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Returns the node and fanin to be modified.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkFindGivenFanin( Abc_Ntk_t * pNtk, int Step, Abc_Obj_t ** ppObj, Abc_Obj_t ** ppFanin ) +{ + Abc_Obj_t * pObj, * pFanin; + int i, k, Counter = 0; + Abc_NtkForEachObj( pNtk, pObj, i ) + Abc_ObjForEachFanin( pObj, pFanin, k ) + { + if ( !Abc_ObjIsNode(pObj) && !Abc_ObjIsPo(pObj) ) + continue; + if ( Abc_ObjIsPo(pObj) && Abc_NtkPoNum(pNtk) == 1 ) + continue; + if ( Abc_ObjIsNode(pObj) && Abc_NodeIsConst(pFanin) ) + continue; + if ( Counter++ == Step ) + { + *ppObj = pObj; + *ppFanin = pFanin; + return 1; + } + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Perform modification with the given number.] + + Description [Modification consists of replacing the node by a constant.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkAutoDebugModify( Abc_Ntk_t * pNtkInit, int Step, int fConst1 ) +{ + extern void Abc_NtkCycleInitStateSop( Abc_Ntk_t * pNtk, int nFrames, int fVerbose ); + Abc_Ntk_t * pNtk; + Abc_Obj_t * pObj, * pFanin, * pConst; + // copy the network + pNtk = Abc_NtkDup( pNtkInit ); + assert( Abc_NtkNodeNum(pNtk) == Abc_NtkNodeNum(pNtkInit) ); + // find the object number + Abc_NtkFindGivenFanin( pNtk, Step, &pObj, &pFanin ); + // consider special case + if ( Abc_ObjIsPo(pObj) && Abc_NodeIsConst(pFanin) ) + { + Abc_NtkDeleteAll_rec( pObj ); + return pNtk; + } + // plug in a constant node + pConst = fConst1? Abc_NtkCreateNodeConst1(pNtk) : Abc_NtkCreateNodeConst0(pNtk); + Abc_ObjTransferFanout( pFanin, pConst ); + Abc_NtkDeleteAll_rec( pFanin ); + + Abc_NtkSweep( pNtk, 0 ); + Abc_NtkCleanupSeq( pNtk, 0, 0, 0 ); + Abc_NtkToSop( pNtk, 0 ); + Abc_NtkCycleInitStateSop( pNtk, 50, 0 ); + return pNtk; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcDress.c b/abc_with_bb_support/src/base/abci/abcDress.c new file mode 100644 index 000000000..33374179e --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcDress.c @@ -0,0 +1,209 @@ +/**CFile**************************************************************** + + FileName [abcDress.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Transfers names from one netlist to the other.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcDress.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "io.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static stmm_table * Abc_NtkDressDeriveMapping( Abc_Ntk_t * pNtk ); +static void Abc_NtkDressTransferNames( Abc_Ntk_t * pNtk, stmm_table * tMapping, int fVerbose ); + +extern Abc_Ntk_t * Abc_NtkIvyFraig( Abc_Ntk_t * pNtk, int nConfLimit, int fDoSparse, int fProve, int fTransfer, int fVerbose ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Transfers names from one netlist to the other.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDress( Abc_Ntk_t * pNtkLogic, char * pFileName, int fVerbose ) +{ + Abc_Ntk_t * pNtkOrig, * pNtkLogicOrig; + Abc_Ntk_t * pMiter, * pMiterFraig; + stmm_table * tMapping; + + assert( Abc_NtkIsLogic(pNtkLogic) ); + + // get the original netlist + pNtkOrig = Io_ReadNetlist( pFileName, Io_ReadFileType(pFileName), 1 ); + if ( pNtkOrig == NULL ) + return; + assert( Abc_NtkIsNetlist(pNtkOrig) ); + + Abc_NtkCleanCopy(pNtkLogic); + Abc_NtkCleanCopy(pNtkOrig); + + // convert it into the logic network + pNtkLogicOrig = Abc_NtkToLogic( pNtkOrig ); + // check that the networks have the same PIs/POs/latches + if ( !Abc_NtkCompareSignals( pNtkLogic, pNtkLogicOrig, 1, 1 ) ) + { + Abc_NtkDelete( pNtkOrig ); + Abc_NtkDelete( pNtkLogicOrig ); + return; + } + + // convert the current logic network into an AIG + pMiter = Abc_NtkStrash( pNtkLogic, 1, 0, 0 ); + + // convert it into the AIG and make the netlist point to the AIG + Abc_NtkAppend( pMiter, pNtkLogicOrig, 1 ); + Abc_NtkTransferCopy( pNtkOrig ); + Abc_NtkDelete( pNtkLogicOrig ); + +if ( fVerbose ) +{ +printf( "After mitering:\n" ); +printf( "Logic: Nodes = %5d. Copy = %5d. \n", Abc_NtkNodeNum(pNtkLogic), Abc_NtkCountCopy(pNtkLogic) ); +printf( "Orig: Nodes = %5d. Copy = %5d. \n", Abc_NtkNodeNum(pNtkOrig), Abc_NtkCountCopy(pNtkOrig) ); +} + + // fraig the miter (miter nodes point to the fraiged miter) + pMiterFraig = Abc_NtkIvyFraig( pMiter, 100, 1, 0, 1, 0 ); + // make netlists point to the fraiged miter + Abc_NtkTransferCopy( pNtkLogic ); + Abc_NtkTransferCopy( pNtkOrig ); + Abc_NtkDelete( pMiter ); + +if ( fVerbose ) +{ +printf( "After fraiging:\n" ); +printf( "Logic: Nodes = %5d. Copy = %5d. \n", Abc_NtkNodeNum(pNtkLogic), Abc_NtkCountCopy(pNtkLogic) ); +printf( "Orig: Nodes = %5d. Copy = %5d. \n", Abc_NtkNodeNum(pNtkOrig), Abc_NtkCountCopy(pNtkOrig) ); +} + + // derive mapping from the fraiged nodes into their prototype nodes in the original netlist + tMapping = Abc_NtkDressDeriveMapping( pNtkOrig ); + + // transfer the names to the new netlist + Abc_NtkDressTransferNames( pNtkLogic, tMapping, fVerbose ); + + // clean up + stmm_free_table( tMapping ); + Abc_NtkDelete( pMiterFraig ); + Abc_NtkDelete( pNtkOrig ); +} + +/**Function************************************************************* + + Synopsis [Returns the mapping from the fraig nodes point into the nodes of the netlist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +stmm_table * Abc_NtkDressDeriveMapping( Abc_Ntk_t * pNtk ) +{ + stmm_table * tResult; + Abc_Obj_t * pNode, * pNodeMap, * pNodeFraig; + int i; + assert( Abc_NtkIsNetlist(pNtk) ); + tResult = stmm_init_table(stmm_ptrcmp,stmm_ptrhash); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + // get the fraiged node + pNodeFraig = Abc_ObjRegular(pNode->pCopy); + // if this node is already mapped, skip + if ( stmm_is_member( tResult, (char *)pNodeFraig ) ) + continue; + // get the mapping of this node + pNodeMap = Abc_ObjNotCond( pNode, Abc_ObjIsComplement(pNode->pCopy) ); + // add the mapping + stmm_insert( tResult, (char *)pNodeFraig, (char *)pNodeMap ); + } + return tResult; +} + +/**Function************************************************************* + + Synopsis [Attaches the names of to the new netlist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDressTransferNames( Abc_Ntk_t * pNtk, stmm_table * tMapping, int fVerbose ) +{ + Abc_Obj_t * pNet, * pNode, * pNodeMap, * pNodeFraig; + char * pName; + int i, Counter = 0, CounterInv = 0, CounterInit = stmm_count(tMapping); + assert( Abc_NtkIsLogic(pNtk) ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + // if the node already has a name, quit + pName = Nm_ManFindNameById( pNtk->pManName, pNode->Id ); + if ( pName != NULL ) + continue; + // get the fraiged node + pNodeFraig = Abc_ObjRegular(pNode->pCopy); + // find the matching node of the original netlist + if ( !stmm_lookup( tMapping, (char *)pNodeFraig, (char **)&pNodeMap ) ) + continue; + // find the true match + pNodeMap = Abc_ObjNotCond( pNodeMap, Abc_ObjIsComplement(pNode->pCopy) ); + // get the name + pNet = Abc_ObjFanout0(Abc_ObjRegular(pNodeMap)); + pName = Nm_ManFindNameById( pNet->pNtk->pManName, pNet->Id ); + assert( pName != NULL ); + // set the name + if ( Abc_ObjIsComplement(pNodeMap) ) + { + Abc_ObjAssignName( pNode, pName, "_inv" ); + CounterInv++; + } + else + { + Abc_ObjAssignName( pNode, pName, NULL ); + Counter++; + } + // remove the name + stmm_delete( tMapping, (char **)&pNodeFraig, (char **)&pNodeMap ); + } + if ( fVerbose ) + { + printf( "Total number of names collected = %5d.\n", CounterInit ); + printf( "Total number of names assigned = %5d. (Dir = %5d. Compl = %5d.)\n", + Counter + CounterInv, Counter, CounterInv ); + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcDsd.c b/abc_with_bb_support/src/base/abci/abcDsd.c new file mode 100644 index 000000000..a50b51739 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcDsd.c @@ -0,0 +1,551 @@ +/**CFile**************************************************************** + + FileName [abcDsd.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Decomposes the network using disjoint-support decomposition.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcDsd.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "dsd.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Abc_Ntk_t * Abc_NtkDsdInternal( Abc_Ntk_t * pNtk, bool fVerbose, bool fPrint, bool fShort ); +static void Abc_NtkDsdConstruct( Dsd_Manager_t * pManDsd, Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkNew ); +static Abc_Obj_t * Abc_NtkDsdConstructNode( Dsd_Manager_t * pManDsd, Dsd_Node_t * pNodeDsd, Abc_Ntk_t * pNtkNew, int * pCounters ); + +static Vec_Ptr_t * Abc_NtkCollectNodesForDsd( Abc_Ntk_t * pNtk ); +static void Abc_NodeDecompDsdAndMux( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes, Dsd_Manager_t * pManDsd, bool fRecursive, int * pCounters ); +static bool Abc_NodeIsForDsd( Abc_Obj_t * pNode ); +static int Abc_NodeFindMuxVar( DdManager * dd, DdNode * bFunc, int nVars ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Derives the DSD network.] + + Description [Takes the strashed network (pNtk), derives global BDDs for + the combinational outputs of this network, and decomposes these BDDs using + disjoint support decomposition. Finally, constructs and return a new + network, which is topologically equivalent to the decomposition tree. + Allocates and frees a new BDD manager and a new DSD manager.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkDsdGlobal( Abc_Ntk_t * pNtk, bool fVerbose, bool fPrint, bool fShort ) +{ + DdManager * dd; + Abc_Ntk_t * pNtkNew; + assert( Abc_NtkIsStrash(pNtk) ); + dd = Abc_NtkBuildGlobalBdds( pNtk, 10000000, 1, 1, fVerbose ); + if ( dd == NULL ) + return NULL; + if ( fVerbose ) + printf( "Shared BDD size = %6d nodes.\n", Cudd_ReadKeys(dd) - Cudd_ReadDead(dd) ); + // transform the result of mapping into a BDD network + pNtkNew = Abc_NtkDsdInternal( pNtk, fVerbose, fPrint, fShort ); + Extra_StopManager( dd ); + if ( pNtkNew == NULL ) + return NULL; + // copy EXDC network + if ( pNtk->pExdc ) + pNtkNew->pExdc = Abc_NtkDup( pNtk->pExdc ); + if ( !Abc_NtkCheck( pNtkNew ) ) + { + printf( "Abc_NtkDsdGlobal: The network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Constructs the decomposed network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkDsdInternal( Abc_Ntk_t * pNtk, bool fVerbose, bool fPrint, bool fShort ) +{ + char ** ppNamesCi, ** ppNamesCo; + Vec_Ptr_t * vFuncsGlob; + Dsd_Manager_t * pManDsd; + Abc_Ntk_t * pNtkNew; + DdManager * dd; + Abc_Obj_t * pObj; + int i; + + // complement the global functions + vFuncsGlob = Vec_PtrAlloc( Abc_NtkCoNum(pNtk) ); + Abc_NtkForEachCo( pNtk, pObj, i ) + Vec_PtrPush( vFuncsGlob, Cudd_NotCond(Abc_ObjGlobalBdd(pObj), Abc_ObjFaninC0(pObj)) ); + + // perform the decomposition + dd = Abc_NtkGlobalBddMan(pNtk); + pManDsd = Dsd_ManagerStart( dd, Abc_NtkCiNum(pNtk), fVerbose ); + Dsd_Decompose( pManDsd, (DdNode **)vFuncsGlob->pArray, Abc_NtkCoNum(pNtk) ); + Vec_PtrFree( vFuncsGlob ); + Abc_NtkFreeGlobalBdds( pNtk, 0 ); + if ( pManDsd == NULL ) + { + Cudd_Quit( dd ); + return NULL; + } + + // start the new network + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_LOGIC, ABC_FUNC_BDD ); + // make sure the new manager has enough inputs + Cudd_bddIthVar( pNtkNew->pManFunc, dd->size-1 ); + // put the results into the new network (save new CO drivers in old CO drivers) + Abc_NtkDsdConstruct( pManDsd, pNtk, pNtkNew ); + // finalize the new network + Abc_NtkFinalize( pNtk, pNtkNew ); + // fix the problem with complemented and duplicated CO edges + Abc_NtkLogicMakeSimpleCos( pNtkNew, 0 ); + if ( fPrint ) + { + ppNamesCi = Abc_NtkCollectCioNames( pNtk, 0 ); + ppNamesCo = Abc_NtkCollectCioNames( pNtk, 1 ); + Dsd_TreePrint( stdout, pManDsd, ppNamesCi, ppNamesCo, fShort, -1 ); + free( ppNamesCi ); + free( ppNamesCo ); + } + + // stop the DSD manager + Dsd_ManagerStop( pManDsd ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Constructs the decomposed network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDsdConstruct( Dsd_Manager_t * pManDsd, Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkNew ) +{ + Dsd_Node_t ** ppNodesDsd; + Dsd_Node_t * pNodeDsd; + Abc_Obj_t * pNode, * pNodeNew, * pDriver; + int i, nNodesDsd; + + // save the CI nodes in the DSD nodes + Dsd_NodeSetMark( Dsd_ManagerReadConst1(pManDsd), (int)Abc_NtkCreateNodeConst1(pNtkNew) ); + Abc_NtkForEachCi( pNtk, pNode, i ) + { + pNodeDsd = Dsd_ManagerReadInput( pManDsd, i ); + Dsd_NodeSetMark( pNodeDsd, (int)pNode->pCopy ); + } + + // collect DSD nodes in DFS order (leaves and const1 are not collected) + ppNodesDsd = Dsd_TreeCollectNodesDfs( pManDsd, &nNodesDsd ); + for ( i = 0; i < nNodesDsd; i++ ) + Abc_NtkDsdConstructNode( pManDsd, ppNodesDsd[i], pNtkNew, NULL ); + free( ppNodesDsd ); + + // set the pointers to the CO drivers + Abc_NtkForEachCo( pNtk, pNode, i ) + { + pDriver = Abc_ObjFanin0( pNode ); + if ( !Abc_ObjIsNode(pDriver) ) + continue; + if ( !Abc_AigNodeIsAnd(pDriver) ) + continue; + pNodeDsd = Dsd_ManagerReadRoot( pManDsd, i ); + pNodeNew = (Abc_Obj_t *)Dsd_NodeReadMark( Dsd_Regular(pNodeDsd) ); + assert( !Abc_ObjIsComplement(pNodeNew) ); + pDriver->pCopy = Abc_ObjNotCond( pNodeNew, Dsd_IsComplement(pNodeDsd) ); + } +} + +/**Function************************************************************* + + Synopsis [Performs DSD using the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkDsdConstructNode( Dsd_Manager_t * pManDsd, Dsd_Node_t * pNodeDsd, Abc_Ntk_t * pNtkNew, int * pCounters ) +{ + DdManager * ddDsd = Dsd_ManagerReadDd( pManDsd ); + DdManager * ddNew = pNtkNew->pManFunc; + Dsd_Node_t * pFaninDsd; + Abc_Obj_t * pNodeNew, * pFanin; + DdNode * bLocal, * bTemp, * bVar; + Dsd_Type_t Type; + int i, nDecs; + + // create the new node + pNodeNew = Abc_NtkCreateNode( pNtkNew ); + // add the fanins + Type = Dsd_NodeReadType( pNodeDsd ); + nDecs = Dsd_NodeReadDecsNum( pNodeDsd ); + assert( nDecs > 1 ); + for ( i = 0; i < nDecs; i++ ) + { + pFaninDsd = Dsd_NodeReadDec( pNodeDsd, i ); + pFanin = (Abc_Obj_t *)Dsd_NodeReadMark(Dsd_Regular(pFaninDsd)); + Abc_ObjAddFanin( pNodeNew, pFanin ); + assert( Type == DSD_NODE_OR || !Dsd_IsComplement(pFaninDsd) ); + } + + // create the local function depending on the type of the node + ddNew = pNtkNew->pManFunc; + switch ( Type ) + { + case DSD_NODE_CONST1: + { + bLocal = ddNew->one; Cudd_Ref( bLocal ); + break; + } + case DSD_NODE_OR: + { + bLocal = Cudd_Not(ddNew->one); Cudd_Ref( bLocal ); + for ( i = 0; i < nDecs; i++ ) + { + pFaninDsd = Dsd_NodeReadDec( pNodeDsd, i ); + bVar = Cudd_NotCond( ddNew->vars[i], Dsd_IsComplement(pFaninDsd) ); + bLocal = Cudd_bddOr( ddNew, bTemp = bLocal, bVar ); Cudd_Ref( bLocal ); + Cudd_RecursiveDeref( ddNew, bTemp ); + } + break; + } + case DSD_NODE_EXOR: + { + bLocal = Cudd_Not(ddNew->one); Cudd_Ref( bLocal ); + for ( i = 0; i < nDecs; i++ ) + { + bLocal = Cudd_bddXor( ddNew, bTemp = bLocal, ddNew->vars[i] ); Cudd_Ref( bLocal ); + Cudd_RecursiveDeref( ddNew, bTemp ); + } + break; + } + case DSD_NODE_PRIME: + { + if ( pCounters ) + { + if ( nDecs < 10 ) + pCounters[nDecs]++; + else + pCounters[10]++; + } + bLocal = Dsd_TreeGetPrimeFunction( ddDsd, pNodeDsd ); Cudd_Ref( bLocal ); + bLocal = Extra_TransferLevelByLevel( ddDsd, ddNew, bTemp = bLocal ); Cudd_Ref( bLocal ); +/* +if ( nDecs == 3 ) +{ +Extra_bddPrint( ddDsd, bTemp ); +printf( "\n" ); +} +*/ + Cudd_RecursiveDeref( ddDsd, bTemp ); + // bLocal is now in the new BDD manager + break; + } + default: + { + assert( 0 ); + break; + } + } + pNodeNew->pData = bLocal; + Dsd_NodeSetMark( pNodeDsd, (int)pNodeNew ); + return pNodeNew; +} + + + + + + +/**Function************************************************************* + + Synopsis [Recursively decomposes internal nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkDsdLocal( Abc_Ntk_t * pNtk, bool fVerbose, bool fRecursive ) +{ + Dsd_Manager_t * pManDsd; + DdManager * dd = pNtk->pManFunc; + Vec_Ptr_t * vNodes; + int i; + int pCounters[11] = {0}; + + assert( Abc_NtkIsBddLogic(pNtk) ); + + // make the network minimum base + Abc_NtkMinimumBase( pNtk ); + + // start the DSD manager + pManDsd = Dsd_ManagerStart( dd, dd->size, 0 ); + + // collect nodes for decomposition + vNodes = Abc_NtkCollectNodesForDsd( pNtk ); + for ( i = 0; i < vNodes->nSize; i++ ) + Abc_NodeDecompDsdAndMux( vNodes->pArray[i], vNodes, pManDsd, fRecursive, pCounters ); + Vec_PtrFree( vNodes ); + + printf( "Number of non-decomposable functions:\n" ); + for ( i = 3; i < 10; i++ ) + printf( "Inputs = %d. Functions = %6d.\n", i, pCounters[i] ); + printf( "Inputs > %d. Functions = %6d.\n", 9, pCounters[10] ); + + // stop the DSD manager + Dsd_ManagerStop( pManDsd ); + + // make sure everything is okay + if ( !Abc_NtkCheck( pNtk ) ) + { + printf( "Abc_NtkDsdRecursive: The network check has failed.\n" ); + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Collects the nodes that may need decomposition.] + + Description [The nodes that do not need decomposition are those + whose BDD has more internal nodes than the support size.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkCollectNodesForDsd( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pNode; + int i; + vNodes = Vec_PtrAlloc( 100 ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + if ( Abc_NodeIsForDsd(pNode) ) + Vec_PtrPush( vNodes, pNode ); + } + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Performs decomposition of one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeDecompDsdAndMux( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes, Dsd_Manager_t * pManDsd, bool fRecursive, int * pCounters ) +{ + DdManager * dd = pNode->pNtk->pManFunc; + Abc_Obj_t * pRoot, * pFanin, * pNode1, * pNode2, * pNodeC; + Dsd_Node_t ** ppNodesDsd, * pNodeDsd, * pFaninDsd; + int i, nNodesDsd, iVar, fCompl; + + // try disjoint support decomposition + pNodeDsd = Dsd_DecomposeOne( pManDsd, pNode->pData ); + fCompl = Dsd_IsComplement( pNodeDsd ); + pNodeDsd = Dsd_Regular( pNodeDsd ); + + // determine what decomposition to use + if ( !fRecursive || Dsd_NodeReadDecsNum(pNodeDsd) != Abc_ObjFaninNum(pNode) ) + { // perform DSD + + // set the inputs + Abc_ObjForEachFanin( pNode, pFanin, i ) + { + pFaninDsd = Dsd_ManagerReadInput( pManDsd, i ); + Dsd_NodeSetMark( pFaninDsd, (int)pFanin ); + } + + // construct the intermediate nodes + ppNodesDsd = Dsd_TreeCollectNodesDfsOne( pManDsd, pNodeDsd, &nNodesDsd ); + for ( i = 0; i < nNodesDsd; i++ ) + { + pRoot = Abc_NtkDsdConstructNode( pManDsd, ppNodesDsd[i], pNode->pNtk, pCounters ); + if ( Abc_NodeIsForDsd(pRoot) && fRecursive ) + Vec_PtrPush( vNodes, pRoot ); + } + free( ppNodesDsd ); + + // remove the current fanins + Abc_ObjRemoveFanins( pNode ); + // add fanin to the root + Abc_ObjAddFanin( pNode, pRoot ); + // update the function to be that of buffer + Cudd_RecursiveDeref( dd, pNode->pData ); + pNode->pData = Cudd_NotCond( dd->vars[0], fCompl ); Cudd_Ref( pNode->pData ); + } + else // perform MUX-decomposition + { + // get the cofactoring variable + iVar = Abc_NodeFindMuxVar( dd, pNode->pData, Abc_ObjFaninNum(pNode) ); + pNodeC = Abc_ObjFanin( pNode, iVar ); + + // get the negative cofactor + pNode1 = Abc_NtkCloneObj( pNode ); + pNode1->pData = Cudd_Cofactor( dd, pNode->pData, Cudd_Not(dd->vars[iVar]) ); Cudd_Ref( pNode1->pData ); + Abc_NodeMinimumBase( pNode1 ); + if ( Abc_NodeIsForDsd(pNode1) ) + Vec_PtrPush( vNodes, pNode1 ); + + // get the positive cofactor + pNode2 = Abc_NtkCloneObj( pNode ); + pNode2->pData = Cudd_Cofactor( dd, pNode->pData, dd->vars[iVar] ); Cudd_Ref( pNode2->pData ); + Abc_NodeMinimumBase( pNode2 ); + if ( Abc_NodeIsForDsd(pNode2) ) + Vec_PtrPush( vNodes, pNode2 ); + + // remove the current fanins + Abc_ObjRemoveFanins( pNode ); + // add new fanins + Abc_ObjAddFanin( pNode, pNodeC ); + Abc_ObjAddFanin( pNode, pNode2 ); + Abc_ObjAddFanin( pNode, pNode1 ); + // update the function to be that of MUX + Cudd_RecursiveDeref( dd, pNode->pData ); + pNode->pData = Cudd_bddIte( dd, dd->vars[0], dd->vars[1], dd->vars[2] ); Cudd_Ref( pNode->pData ); + } +} + +/**Function************************************************************* + + Synopsis [Checks if the node should be decomposed by DSD.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NodeIsForDsd( Abc_Obj_t * pNode ) +{ + DdManager * dd = pNode->pNtk->pManFunc; +// DdNode * bFunc, * bFunc0, * bFunc1; + assert( Abc_ObjIsNode(pNode) ); +// if ( Cudd_DagSize(pNode->pData)-1 > Abc_ObjFaninNum(pNode) ) +// return 1; +// return 0; + +/* + // this does not catch things like a(b+c), which should be decomposed + for ( bFunc = Cudd_Regular(pNode->pData); !cuddIsConstant(bFunc); ) + { + bFunc0 = Cudd_Regular( cuddE(bFunc) ); + bFunc1 = cuddT(bFunc); + if ( bFunc0 == b1 ) + bFunc = bFunc1; + else if ( bFunc1 == b1 || bFunc0 == bFunc1 ) + bFunc = bFunc0; + else + return 1; + } +*/ + if ( Abc_ObjFaninNum(pNode) > 2 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Determines a cofactoring variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeFindMuxVar( DdManager * dd, DdNode * bFunc, int nVars ) +{ + DdNode * bVar, * bCof0, * bCof1; + int SuppSumMin = 1000000; + int i, nSSD, nSSQ, iVar; + +// printf( "\n\nCofactors:\n\n" ); + iVar = -1; + for ( i = 0; i < nVars; i++ ) + { + bVar = dd->vars[i]; + + bCof0 = Cudd_Cofactor( dd, bFunc, Cudd_Not(bVar) ); Cudd_Ref( bCof0 ); + bCof1 = Cudd_Cofactor( dd, bFunc, bVar ); Cudd_Ref( bCof1 ); + +// nodD = Cudd_DagSize(bCof0); +// nodQ = Cudd_DagSize(bCof1); +// printf( "+%02d: D=%2d. Q=%2d. ", i, nodD, nodQ ); +// printf( "S=%2d. D=%2d. ", nodD + nodQ, abs(nodD-nodQ) ); + + nSSD = Cudd_SupportSize( dd, bCof0 ); + nSSQ = Cudd_SupportSize( dd, bCof1 ); + +// printf( "SD=%2d. SQ=%2d. ", nSSD, nSSQ ); +// printf( "S=%2d. D=%2d. ", nSSD + nSSQ, abs(nSSD - nSSQ) ); +// printf( "Cost=%3d. ", Cost(nodD,nodQ,nSSD,nSSQ) ); +// printf( "\n" ); + + Cudd_RecursiveDeref( dd, bCof0 ); + Cudd_RecursiveDeref( dd, bCof1 ); + + if ( SuppSumMin > nSSD + nSSQ ) + { + SuppSumMin = nSSD + nSSQ; + iVar = i; + } + } + return iVar; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcEspresso.c b/abc_with_bb_support/src/base/abci/abcEspresso.c new file mode 100644 index 000000000..b61acd8a3 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcEspresso.c @@ -0,0 +1,250 @@ +/**CFile**************************************************************** + + FileName [abcEspresso.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Procedures to minimize SOPs using Espresso.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcEspresso.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "espresso.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Abc_NodeEspresso( Abc_Obj_t * pNode ); +static pset_family Abc_SopToEspresso( char * pSop ); +static char * Abc_SopFromEspresso( Extra_MmFlex_t * pMan, pset_family Cover ); +static pset_family Abc_EspressoMinimize( pset_family pOnset, pset_family pDcset ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Minimizes SOP representations using Espresso.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkEspresso( Abc_Ntk_t * pNtk, int fVerbose ) +{ + Abc_Obj_t * pNode; + int i; + assert( Abc_NtkIsLogic(pNtk) ); + // convert the network to have SOPs + if ( Abc_NtkHasMapping(pNtk) ) + Abc_NtkMapToSop(pNtk); + else if ( Abc_NtkHasBdd(pNtk) ) + { + if ( !Abc_NtkBddToSop(pNtk, 0) ) + { + printf( "Abc_NtkEspresso(): Converting to SOPs has failed.\n" ); + return; + } + } + // minimize SOPs of all nodes + Abc_NtkForEachNode( pNtk, pNode, i ) + if ( i ) Abc_NodeEspresso( pNode ); +} + +/**Function************************************************************* + + Synopsis [Minimizes SOP representation of one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeEspresso( Abc_Obj_t * pNode ) +{ + extern void define_cube_size( int n ); + pset_family Cover; + int fCompl; + + assert( Abc_ObjIsNode(pNode) ); + // define the cube for this node + define_cube_size( Abc_ObjFaninNum(pNode) ); + // create the Espresso cover + fCompl = Abc_SopIsComplement( pNode->pData ); + Cover = Abc_SopToEspresso( pNode->pData ); + // perform minimization + Cover = Abc_EspressoMinimize( Cover, NULL ); // deletes also cover + // convert back onto the node's SOP representation + pNode->pData = Abc_SopFromEspresso( pNode->pNtk->pManFunc, Cover ); + if ( fCompl ) Abc_SopComplement( pNode->pData ); + sf_free(Cover); +} + +/**Function************************************************************* + + Synopsis [Converts SOP in ABC into SOP representation in Espresso.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +pset_family Abc_SopToEspresso( char * pSop ) +{ + char * pCube; + pset_family Cover; + pset set; + int nCubes, nVars, Value, v; + + if ( pSop == NULL ) + return NULL; + + nVars = Abc_SopGetVarNum(pSop); + nCubes = Abc_SopGetCubeNum(pSop); + assert( cube.size == 2 * nVars ); + + if ( Abc_SopIsConst0(pSop) ) + { + Cover = sf_new(0, cube.size); + return Cover; + } + if ( Abc_SopIsConst1(pSop) ) + { + Cover = sf_new(1, cube.size); + set = GETSET(Cover, Cover->count++); + set_copy( set, cube.fullset ); + return Cover; + } + + // create the cover + Cover = sf_new(nCubes, cube.size); + // fill in the cubes + Abc_SopForEachCube( pSop, nVars, pCube ) + { + set = GETSET(Cover, Cover->count++); + set_copy( set, cube.fullset ); + Abc_CubeForEachVar( pCube, Value, v ) + { + if ( Value == '0' ) + set_remove(set, 2*v+1); + else if ( Value == '1' ) + set_remove(set, 2*v); + } + } + return Cover; +} + +/**Function************************************************************* + + Synopsis [Converts SOP representation in Espresso into SOP in ABC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_SopFromEspresso( Extra_MmFlex_t * pMan, pset_family Cover ) +{ + pset set; + char * pSop, * pCube; + int Lit, nVars, nCubes, i, k; + + nVars = Cover->sf_size/2; + nCubes = Cover->count; + + pSop = Abc_SopStart( pMan, nCubes, nVars ); + + // go through the cubes + i = 0; + Abc_SopForEachCube( pSop, nVars, pCube ) + { + set = GETSET(Cover, i++); + for ( k = 0; k < nVars; k++ ) + { + Lit = GETINPUT(set, k); + if ( Lit == ZERO ) + pCube[k] = '0'; + else if ( Lit == ONE ) + pCube[k] = '1'; + } + } + return pSop; +} + + +/**Function************************************************************* + + Synopsis [Minimizes the cover using Espresso.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +pset_family Abc_EspressoMinimize( pset_family pOnset, pset_family pDcset ) +{ + pset_family pOffset; + int fNewDcset, i; + int fSimple = 0; + int fSparse = 0; + + if ( fSimple ) + { + for ( i = 0; i < cube.num_vars; i++ ) + pOnset = d1merge( pOnset, i ); + pOnset = sf_contain( pOnset ); + return pOnset; + } + + // create the dcset + fNewDcset = (pDcset == NULL); + if ( pDcset == NULL ) + pDcset = sf_new( 1, cube.size ); + pDcset->wsize = pOnset->wsize; + pDcset->sf_size = pOnset->sf_size; + + // derive the offset + if ( pDcset->sf_size == 0 || pDcset->count == 0 ) + pOffset = complement(cube1list(pOnset)); + else + pOffset = complement(cube2list(pOnset, pDcset)); + + // perform minimization + skip_make_sparse = !fSparse; + pOnset = espresso( pOnset, pDcset, pOffset ); + + // free covers + sf_free( pOffset ); + if ( fNewDcset ) + sf_free( pDcset ); + return pOnset; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcExtract.c b/abc_with_bb_support/src/base/abci/abcExtract.c new file mode 100644 index 000000000..7a3fe1c2d --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcExtract.c @@ -0,0 +1,51 @@ +/**CFile**************************************************************** + + FileName [abcMvCost.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Calculating the cost of one MV block.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcMvCost.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_MvCostTest( Abc_Ntk_t * pNtk ) +{ + +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcFpga.c b/abc_with_bb_support/src/base/abci/abcFpga.c new file mode 100644 index 000000000..5d7f581ad --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcFpga.c @@ -0,0 +1,278 @@ +/**CFile**************************************************************** + + FileName [abcFpga.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Interface with the FPGA mapping package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcFpga.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "fpgaInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Fpga_Man_t * Abc_NtkToFpga( Abc_Ntk_t * pNtk, int fRecovery, float * pSwitching, int fLatchPaths, int fVerbose ); +static Abc_Ntk_t * Abc_NtkFromFpga( Fpga_Man_t * pMan, Abc_Ntk_t * pNtk ); +static Abc_Obj_t * Abc_NodeFromFpga_rec( Abc_Ntk_t * pNtkNew, Fpga_Node_t * pNodeFpga ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Interface with the FPGA mapping package.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFpga( Abc_Ntk_t * pNtk, float DelayTarget, int fRecovery, int fSwitching, int fLatchPaths, int fVerbose ) +{ + int fShowSwitching = 1; + Abc_Ntk_t * pNtkNew; + Fpga_Man_t * pMan; + Vec_Int_t * vSwitching; + float * pSwitching = NULL; + + assert( Abc_NtkIsStrash(pNtk) ); + + // print a warning about choice nodes + if ( Abc_NtkGetChoiceNum( pNtk ) ) + printf( "Performing FPGA mapping with choices.\n" ); + + // compute switching activity + fShowSwitching |= fSwitching; + if ( fShowSwitching ) + { + extern Vec_Int_t * Sim_NtkComputeSwitching( Abc_Ntk_t * pNtk, int nPatterns ); + vSwitching = Sim_NtkComputeSwitching( pNtk, 4096 ); + pSwitching = (float *)vSwitching->pArray; + } + + // perform FPGA mapping + pMan = Abc_NtkToFpga( pNtk, fRecovery, pSwitching, fLatchPaths, fVerbose ); + if ( pSwitching ) Vec_IntFree( vSwitching ); + if ( pMan == NULL ) + return NULL; + Fpga_ManSetSwitching( pMan, fSwitching ); + Fpga_ManSetLatchPaths( pMan, fLatchPaths ); + Fpga_ManSetLatchNum( pMan, Abc_NtkLatchNum(pNtk) ); + Fpga_ManSetDelayTarget( pMan, DelayTarget ); + if ( !Fpga_Mapping( pMan ) ) + { + Fpga_ManFree( pMan ); + return NULL; + } + + // transform the result of mapping into a BDD network + pNtkNew = Abc_NtkFromFpga( pMan, pNtk ); + if ( pNtkNew == NULL ) + return NULL; + Fpga_ManFree( pMan ); + + // make the network minimum base + Abc_NtkMinimumBase( pNtkNew ); + + if ( pNtk->pExdc ) + pNtkNew->pExdc = Abc_NtkDup( pNtk->pExdc ); + + // make sure that everything is okay + if ( !Abc_NtkCheck( pNtkNew ) ) + { + printf( "Abc_NtkFpga: The network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Load the network into FPGA manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Man_t * Abc_NtkToFpga( Abc_Ntk_t * pNtk, int fRecovery, float * pSwitching, int fLatchPaths, int fVerbose ) +{ + Fpga_Man_t * pMan; + ProgressBar * pProgress; + Fpga_Node_t * pNodeFpga; + Vec_Ptr_t * vNodes; + Abc_Obj_t * pNode, * pFanin, * pPrev; + float * pfArrivals; + int i; + + assert( Abc_NtkIsStrash(pNtk) ); + + // start the mapping manager and set its parameters + pMan = Fpga_ManCreate( Abc_NtkCiNum(pNtk), Abc_NtkCoNum(pNtk), fVerbose ); + if ( pMan == NULL ) + return NULL; + Fpga_ManSetAreaRecovery( pMan, fRecovery ); + Fpga_ManSetOutputNames( pMan, Abc_NtkCollectCioNames(pNtk, 1) ); + pfArrivals = Abc_NtkGetCiArrivalFloats(pNtk); + if ( fLatchPaths ) + { + for ( i = 0; i < Abc_NtkPiNum(pNtk); i++ ) + pfArrivals[i] = -FPGA_FLOAT_LARGE; + } + Fpga_ManSetInputArrivals( pMan, pfArrivals ); + + // create PIs and remember them in the old nodes + Abc_NtkCleanCopy( pNtk ); + Abc_AigConst1(pNtk)->pCopy = (Abc_Obj_t *)Fpga_ManReadConst1(pMan); + Abc_NtkForEachCi( pNtk, pNode, i ) + { + pNodeFpga = Fpga_ManReadInputs(pMan)[i]; + pNode->pCopy = (Abc_Obj_t *)pNodeFpga; + if ( pSwitching ) + Fpga_NodeSetSwitching( pNodeFpga, pSwitching[pNode->Id] ); + } + + // load the AIG into the mapper + vNodes = Abc_AigDfs( pNtk, 0, 0 ); + pProgress = Extra_ProgressBarStart( stdout, vNodes->nSize ); + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + // add the node to the mapper + pNodeFpga = Fpga_NodeAnd( pMan, + Fpga_NotCond( Abc_ObjFanin0(pNode)->pCopy, Abc_ObjFaninC0(pNode) ), + Fpga_NotCond( Abc_ObjFanin1(pNode)->pCopy, Abc_ObjFaninC1(pNode) ) ); + assert( pNode->pCopy == NULL ); + // remember the node + pNode->pCopy = (Abc_Obj_t *)pNodeFpga; + if ( pSwitching ) + Fpga_NodeSetSwitching( pNodeFpga, pSwitching[pNode->Id] ); + // set up the choice node + if ( Abc_AigNodeIsChoice( pNode ) ) + for ( pPrev = pNode, pFanin = pNode->pData; pFanin; pPrev = pFanin, pFanin = pFanin->pData ) + { + Fpga_NodeSetNextE( (Fpga_Node_t *)pPrev->pCopy, (Fpga_Node_t *)pFanin->pCopy ); + Fpga_NodeSetRepr( (Fpga_Node_t *)pFanin->pCopy, (Fpga_Node_t *)pNode->pCopy ); + } + } + Extra_ProgressBarStop( pProgress ); + Vec_PtrFree( vNodes ); + + // set the primary outputs without copying the phase + Abc_NtkForEachCo( pNtk, pNode, i ) + Fpga_ManReadOutputs(pMan)[i] = (Fpga_Node_t *)Abc_ObjFanin0(pNode)->pCopy; + return pMan; +} + +/**Function************************************************************* + + Synopsis [Creates the mapped network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFromFpga( Fpga_Man_t * pMan, Abc_Ntk_t * pNtk ) +{ + ProgressBar * pProgress; + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pNode, * pNodeNew; + int i, nDupGates; + // create the new network + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_LOGIC, ABC_FUNC_BDD ); + // make the mapper point to the new network + Fpga_CutsCleanSign( pMan ); + Fpga_ManCleanData0( pMan ); + Abc_NtkForEachCi( pNtk, pNode, i ) + Fpga_NodeSetData0( Fpga_ManReadInputs(pMan)[i], (char *)pNode->pCopy ); + // set the constant node +// if ( Fpga_NodeReadRefs(Fpga_ManReadConst1(pMan)) > 0 ) + Fpga_NodeSetData0( Fpga_ManReadConst1(pMan), (char *)Abc_NtkCreateNodeConst1(pNtkNew) ); + // process the nodes in topological order + pProgress = Extra_ProgressBarStart( stdout, Abc_NtkCoNum(pNtk) ); + Abc_NtkForEachCo( pNtk, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + pNodeNew = Abc_NodeFromFpga_rec( pNtkNew, Fpga_ManReadOutputs(pMan)[i] ); + assert( !Abc_ObjIsComplement(pNodeNew) ); + Abc_ObjFanin0(pNode)->pCopy = pNodeNew; + } + Extra_ProgressBarStop( pProgress ); + // finalize the new network + Abc_NtkFinalize( pNtk, pNtkNew ); + // remove the constant node if not used + pNodeNew = (Abc_Obj_t *)Fpga_NodeReadData0(Fpga_ManReadConst1(pMan)); + if ( Abc_ObjFanoutNum(pNodeNew) == 0 ) + Abc_NtkDeleteObj( pNodeNew ); + // decouple the PO driver nodes to reduce the number of levels + nDupGates = Abc_NtkLogicMakeSimpleCos( pNtkNew, 1 ); +// if ( nDupGates && Fpga_ManReadVerbose(pMan) ) +// printf( "Duplicated %d gates to decouple the CO drivers.\n", nDupGates ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Derive one node after FPGA mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NodeFromFpga_rec( Abc_Ntk_t * pNtkNew, Fpga_Node_t * pNodeFpga ) +{ + Fpga_Cut_t * pCutBest; + Fpga_Node_t ** ppLeaves; + Abc_Obj_t * pNodeNew; + int i, nLeaves; + assert( !Fpga_IsComplement(pNodeFpga) ); + // return if the result if known + pNodeNew = (Abc_Obj_t *)Fpga_NodeReadData0( pNodeFpga ); + if ( pNodeNew ) + return pNodeNew; + assert( Fpga_NodeIsAnd(pNodeFpga) ); + // get the parameters of the best cut + pCutBest = Fpga_NodeReadCutBest( pNodeFpga ); + ppLeaves = Fpga_CutReadLeaves( pCutBest ); + nLeaves = Fpga_CutReadLeavesNum( pCutBest ); + // create a new node + pNodeNew = Abc_NtkCreateNode( pNtkNew ); + for ( i = 0; i < nLeaves; i++ ) + Abc_ObjAddFanin( pNodeNew, Abc_NodeFromFpga_rec(pNtkNew, ppLeaves[i]) ); + // derive the function of this node + pNodeNew->pData = Fpga_TruthsCutBdd( pNtkNew->pManFunc, pCutBest ); Cudd_Ref( pNodeNew->pData ); + Fpga_NodeSetData0( pNodeFpga, (char *)pNodeNew ); + return pNodeNew; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcFpgaFast.c b/abc_with_bb_support/src/base/abci/abcFpgaFast.c new file mode 100644 index 000000000..8270f0135 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcFpgaFast.c @@ -0,0 +1,190 @@ +/**CFile**************************************************************** + + FileName [abcFpgaFast.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Fast FPGA mapper.] + + Author [Sungmin Cho] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcFpgaFast.c,v 1.00 2006/09/02 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +extern Ivy_Man_t * Abc_NtkIvyBefore( Abc_Ntk_t * pNtk, int fSeq, int fUseDc ); + +static Abc_Ntk_t * Ivy_ManFpgaToAbc( Abc_Ntk_t * pNtk, Ivy_Man_t * pMan ); +static Abc_Obj_t * Ivy_ManToAbcFast_rec( Abc_Ntk_t * pNtkNew, Ivy_Man_t * pMan, Ivy_Obj_t * pObjIvy, Vec_Int_t * vNodes ); + +static inline void Abc_ObjSetIvy2Abc( Ivy_Man_t * p, int IvyId, Abc_Obj_t * pObjAbc ) { assert(Vec_PtrEntry(p->pCopy, IvyId) == NULL); assert(!Abc_ObjIsComplement(pObjAbc)); Vec_PtrWriteEntry( p->pCopy, IvyId, pObjAbc ); } +static inline Abc_Obj_t * Abc_ObjGetIvy2Abc( Ivy_Man_t * p, int IvyId ) { return Vec_PtrEntry( p->pCopy, IvyId ); } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs fast FPGA mapping of the network.] + + Description [Takes the AIG to be mapped, the LUT size, and verbosity + flag. Produces the new network by fast FPGA mapping of the current + network. If the current network in ABC in not an AIG, the user should + run command "strash" to make sure that the current network into an AIG + before calling this procedure.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFpgaFast( Abc_Ntk_t * pNtk, int nLutSize, int fRecovery, int fVerbose ) +{ + Ivy_Man_t * pMan; + Abc_Ntk_t * pNtkNew; + // make sure the network is an AIG + assert( Abc_NtkIsStrash(pNtk) ); + // convert the network into the AIG + pMan = Abc_NtkIvyBefore( pNtk, 0, 0 ); + // perform fast FPGA mapping + Ivy_FastMapPerform( pMan, nLutSize, fRecovery, fVerbose ); + // convert back into the ABC network + pNtkNew = Ivy_ManFpgaToAbc( pNtk, pMan ); + Ivy_FastMapStop( pMan ); + Ivy_ManStop( pMan ); + // make sure that the final network passes the test + if ( pNtkNew != NULL && !Abc_NtkCheck( pNtkNew ) ) + { + printf( "Abc_NtkFastMap: The network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Constructs the ABC network after mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Ivy_ManFpgaToAbc( Abc_Ntk_t * pNtk, Ivy_Man_t * pMan ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObjAbc, * pObj; + Ivy_Obj_t * pObjIvy; + Vec_Int_t * vNodes; + int i; + // start mapping from Ivy into Abc + pMan->pCopy = Vec_PtrStart( Ivy_ManObjIdMax(pMan) + 1 ); + // start the new ABC network + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_LOGIC, ABC_FUNC_AIG ); + // transfer the pointers to the basic nodes + Abc_ObjSetIvy2Abc( pMan, Ivy_ManConst1(pMan)->Id, Abc_NtkCreateNodeConst1(pNtkNew) ); + Abc_NtkForEachCi( pNtkNew, pObjAbc, i ) + Abc_ObjSetIvy2Abc( pMan, Ivy_ManPi(pMan, i)->Id, pObjAbc ); + // recursively construct the network + vNodes = Vec_IntAlloc( 100 ); + Ivy_ManForEachPo( pMan, pObjIvy, i ) + { + // get the new ABC node corresponding to the old fanin of the PO in IVY + pObjAbc = Ivy_ManToAbcFast_rec( pNtkNew, pMan, Ivy_ObjFanin0(pObjIvy), vNodes ); + // consider the case of complemented fanin of the PO + if ( Ivy_ObjFaninC0(pObjIvy) ) // complement + { + if ( Abc_ObjIsCi(pObjAbc) ) + pObjAbc = Abc_NtkCreateNodeInv( pNtkNew, pObjAbc ); + else + { + // clone the node + pObj = Abc_NtkCloneObj( pObjAbc ); + // set complemented functions + pObj->pData = Hop_Not( pObjAbc->pData ); + // return the new node + pObjAbc = pObj; + } + } + Abc_ObjAddFanin( Abc_NtkCo(pNtkNew, i), pObjAbc ); + } + Vec_IntFree( vNodes ); + Vec_PtrFree( pMan->pCopy ); + pMan->pCopy = NULL; + // remove dangling nodes + Abc_NtkCleanup( pNtkNew, 0 ); + // fix CIs feeding directly into COs + Abc_NtkLogicMakeSimpleCos( pNtkNew, 0 ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Recursively construct the new node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Ivy_ManToAbcFast_rec( Abc_Ntk_t * pNtkNew, Ivy_Man_t * pMan, Ivy_Obj_t * pObjIvy, Vec_Int_t * vNodes ) +{ + Vec_Int_t Supp, * vSupp = &Supp; + Abc_Obj_t * pObjAbc, * pFaninAbc; + Ivy_Obj_t * pNodeIvy; + int i, Entry; + // skip the node if it is a constant or already processed + pObjAbc = Abc_ObjGetIvy2Abc( pMan, pObjIvy->Id ); + if ( pObjAbc ) + return pObjAbc; + assert( Ivy_ObjIsAnd(pObjIvy) || Ivy_ObjIsExor(pObjIvy) ); + // get the support of K-LUT + Ivy_FastMapReadSupp( pMan, pObjIvy, vSupp ); + // create new ABC node and its fanins + pObjAbc = Abc_NtkCreateNode( pNtkNew ); + Vec_IntForEachEntry( vSupp, Entry, i ) + { + pFaninAbc = Ivy_ManToAbcFast_rec( pNtkNew, pMan, Ivy_ManObj(pMan, Entry), vNodes ); + Abc_ObjAddFanin( pObjAbc, pFaninAbc ); + } + // collect the nodes used in the cut + Ivy_ManCollectCut( pMan, pObjIvy, vSupp, vNodes ); + // create the local function + Ivy_ManForEachNodeVec( pMan, vNodes, pNodeIvy, i ) + { + if ( i < Vec_IntSize(vSupp) ) + pNodeIvy->pEquiv = (Ivy_Obj_t *)Hop_IthVar( pNtkNew->pManFunc, i ); + else + pNodeIvy->pEquiv = (Ivy_Obj_t *)Hop_And( pNtkNew->pManFunc, (Hop_Obj_t *)Ivy_ObjChild0Equiv(pNodeIvy), (Hop_Obj_t *)Ivy_ObjChild1Equiv(pNodeIvy) ); + } + // set the local function + pObjAbc->pData = (Abc_Obj_t *)pObjIvy->pEquiv; + // set the node + Abc_ObjSetIvy2Abc( pMan, pObjIvy->Id, pObjAbc ); + return pObjAbc; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcFraig.c b/abc_with_bb_support/src/base/abci/abcFraig.c new file mode 100644 index 000000000..8757094fc --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcFraig.c @@ -0,0 +1,816 @@ +/**CFile**************************************************************** + + FileName [abcFraig.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Procedures interfacing with the FRAIG package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcFraig.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "fraig.h" +#include "main.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +extern Abc_Ntk_t * Abc_NtkFromFraig( Fraig_Man_t * pMan, Abc_Ntk_t * pNtk ); +static Abc_Ntk_t * Abc_NtkFromFraig2( Fraig_Man_t * pMan, Abc_Ntk_t * pNtk ); +static Abc_Obj_t * Abc_NodeFromFraig_rec( Abc_Ntk_t * pNtkNew, Fraig_Node_t * pNodeFraig ); +static void Abc_NtkFromFraig2_rec( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pNode, Vec_Ptr_t * vNodeReprs ); +extern Fraig_Node_t * Abc_NtkToFraigExdc( Fraig_Man_t * pMan, Abc_Ntk_t * pNtkMain, Abc_Ntk_t * pNtkExdc ); +static void Abc_NtkFraigRemapUsingExdc( Fraig_Man_t * pMan, Abc_Ntk_t * pNtk ); + +static int Abc_NtkFraigTrustCheck( Abc_Ntk_t * pNtk ); +static void Abc_NtkFraigTrustOne( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkNew ); +static Abc_Obj_t * Abc_NodeFraigTrust( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pNode ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Interfaces the network with the FRAIG package.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFraig( Abc_Ntk_t * pNtk, void * pParams, int fAllNodes, int fExdc ) +{ + Fraig_Params_t * pPars = pParams; + Abc_Ntk_t * pNtkNew; + Fraig_Man_t * pMan; + // check if EXDC is present + if ( fExdc && pNtk->pExdc == NULL ) + fExdc = 0, printf( "Warning: Networks has no EXDC.\n" ); + // perform fraiging + pMan = Abc_NtkToFraig( pNtk, pParams, fAllNodes, fExdc ); + // add algebraic choices +// if ( pPars->fChoicing ) +// Fraig_ManAddChoices( pMan, 0, 6 ); + // prove the miter if asked to + if ( pPars->fTryProve ) + Fraig_ManProveMiter( pMan ); + // reconstruct FRAIG in the new network + if ( fExdc ) + pNtkNew = Abc_NtkFromFraig2( pMan, pNtk ); + else + pNtkNew = Abc_NtkFromFraig( pMan, pNtk ); + Fraig_ManFree( pMan ); + if ( pNtk->pExdc ) + pNtkNew->pExdc = Abc_NtkDup( pNtk->pExdc ); + // make sure that everything is okay + if ( !Abc_NtkCheck( pNtkNew ) ) + { + printf( "Abc_NtkFraig: The network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Transforms the strashed network into FRAIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void * Abc_NtkToFraig( Abc_Ntk_t * pNtk, void * pParams, int fAllNodes, int fExdc ) +{ + int fInternal = ((Fraig_Params_t *)pParams)->fInternal; + Fraig_Man_t * pMan; + ProgressBar * pProgress; + Vec_Ptr_t * vNodes; + Abc_Obj_t * pNode; + int i; + + assert( Abc_NtkIsStrash(pNtk) ); + + // create the FRAIG manager + pMan = Fraig_ManCreate( pParams ); + + // map the constant node + Abc_NtkCleanCopy( pNtk ); + Abc_AigConst1(pNtk)->pCopy = (Abc_Obj_t *)Fraig_ManReadConst1(pMan); + // create PIs and remember them in the old nodes + Abc_NtkForEachCi( pNtk, pNode, i ) + pNode->pCopy = (Abc_Obj_t *)Fraig_ManReadIthVar(pMan, i); + + // perform strashing + vNodes = Abc_AigDfs( pNtk, fAllNodes, 0 ); + if ( !fInternal ) + pProgress = Extra_ProgressBarStart( stdout, vNodes->nSize ); + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + if ( Abc_ObjFaninNum(pNode) == 0 ) + continue; + if ( !fInternal ) + Extra_ProgressBarUpdate( pProgress, i, NULL ); + pNode->pCopy = (Abc_Obj_t *)Fraig_NodeAnd( pMan, + Fraig_NotCond( Abc_ObjFanin0(pNode)->pCopy, Abc_ObjFaninC0(pNode) ), + Fraig_NotCond( Abc_ObjFanin1(pNode)->pCopy, Abc_ObjFaninC1(pNode) ) ); + } + if ( !fInternal ) + Extra_ProgressBarStop( pProgress ); + Vec_PtrFree( vNodes ); + + // use EXDC to change the mapping of nodes into FRAIG nodes + if ( fExdc ) + Abc_NtkFraigRemapUsingExdc( pMan, pNtk ); + + // set the primary outputs + Abc_NtkForEachCo( pNtk, pNode, i ) + Fraig_ManSetPo( pMan, (Fraig_Node_t *)Abc_ObjNotCond( Abc_ObjFanin0(pNode)->pCopy, Abc_ObjFaninC0(pNode) ) ); + return pMan; +} + +/**Function************************************************************* + + Synopsis [Derives EXDC node for the given network.] + + Description [Assumes that EXDCs of all POs are the same. + Returns the EXDC of the first PO.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_Node_t * Abc_NtkToFraigExdc( Fraig_Man_t * pMan, Abc_Ntk_t * pNtkMain, Abc_Ntk_t * pNtkExdc ) +{ + Abc_Ntk_t * pNtkStrash; + Abc_Obj_t * pObj; + Fraig_Node_t * gResult; + char ** ppNames; + int i, k; + // strash the EXDC network + pNtkStrash = Abc_NtkStrash( pNtkExdc, 0, 0, 0 ); + Abc_NtkCleanCopy( pNtkStrash ); + Abc_AigConst1(pNtkStrash)->pCopy = (Abc_Obj_t *)Fraig_ManReadConst1(pMan); + // set the mapping of the PI nodes + ppNames = Abc_NtkCollectCioNames( pNtkMain, 0 ); + Abc_NtkForEachCi( pNtkStrash, pObj, i ) + { + for ( k = 0; k < Abc_NtkCiNum(pNtkMain); k++ ) + if ( strcmp( Abc_ObjName(pObj), ppNames[k] ) == 0 ) + { + pObj->pCopy = (Abc_Obj_t *)Fraig_ManReadIthVar(pMan, k); + break; + } + assert( pObj->pCopy != NULL ); + } + free( ppNames ); + // build FRAIG for each node + Abc_AigForEachAnd( pNtkStrash, pObj, i ) + pObj->pCopy = (Abc_Obj_t *)Fraig_NodeAnd( pMan, + Fraig_NotCond( Abc_ObjFanin0(pObj)->pCopy, Abc_ObjFaninC0(pObj) ), + Fraig_NotCond( Abc_ObjFanin1(pObj)->pCopy, Abc_ObjFaninC1(pObj) ) ); + // get the EXDC to be returned + pObj = Abc_NtkPo( pNtkStrash, 0 ); + gResult = Fraig_NotCond( Abc_ObjFanin0(pObj)->pCopy, Abc_ObjFaninC0(pObj) ); + Abc_NtkDelete( pNtkStrash ); + return gResult; +} + + +/**Function************************************************************* + + Synopsis [Changes mapping of the old nodes into FRAIG nodes using EXDC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkFraigRemapUsingExdc( Fraig_Man_t * pMan, Abc_Ntk_t * pNtk ) +{ + Fraig_Node_t * gNodeNew, * gNodeExdc; + stmm_table * tTable; + stmm_generator * gen; + Abc_Obj_t * pNode, * pNodeBest; + Abc_Obj_t * pClass, ** ppSlot; + Vec_Ptr_t * vNexts; + int i; + + // get the global don't-cares + assert( pNtk->pExdc ); + gNodeExdc = Abc_NtkToFraigExdc( pMan, pNtk, pNtk->pExdc ); + + // save the next pointers + vNexts = Vec_PtrStart( Abc_NtkObjNumMax(pNtk) ); + Abc_NtkForEachNode( pNtk, pNode, i ) + Vec_PtrWriteEntry( vNexts, pNode->Id, pNode->pNext ); + + // find the classes of AIG nodes which have FRAIG nodes assigned + Abc_NtkCleanNext( pNtk ); + tTable = stmm_init_table(stmm_ptrcmp,stmm_ptrhash); + Abc_NtkForEachNode( pNtk, pNode, i ) + if ( pNode->pCopy ) + { + gNodeNew = Fraig_NodeAnd( pMan, (Fraig_Node_t *)pNode->pCopy, Fraig_Not(gNodeExdc) ); + if ( !stmm_find_or_add( tTable, (char *)Fraig_Regular(gNodeNew), (char ***)&ppSlot ) ) + *ppSlot = NULL; + pNode->pNext = *ppSlot; + *ppSlot = pNode; + } + + // for reach non-trival class, find the node with minimum level, and replace other nodes by it + Abc_AigSetNodePhases( pNtk ); + stmm_foreach_item( tTable, gen, (char **)&gNodeNew, (char **)&pClass ) + { + if ( pClass->pNext == NULL ) + continue; + // find the node with minimum level + pNodeBest = pClass; + for ( pNode = pClass->pNext; pNode; pNode = pNode->pNext ) + if ( pNodeBest->Level > pNode->Level ) + pNodeBest = pNode; + // remap the class nodes + for ( pNode = pClass; pNode; pNode = pNode->pNext ) + pNode->pCopy = Abc_ObjNotCond( pNodeBest->pCopy, pNode->fPhase ^ pNodeBest->fPhase ); + } + stmm_free_table( tTable ); + + // restore the next pointers + Abc_NtkCleanNext( pNtk ); + Abc_NtkForEachNode( pNtk, pNode, i ) + pNode->pNext = Vec_PtrEntry( vNexts, pNode->Id ); + Vec_PtrFree( vNexts ); +} + +/**Function************************************************************* + + Synopsis [Transforms FRAIG into strashed network with choices.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFromFraig( Fraig_Man_t * pMan, Abc_Ntk_t * pNtk ) +{ + ProgressBar * pProgress; + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pNode, * pNodeNew; + int i; + // create the new network + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_STRASH, ABC_FUNC_AIG ); + // make the mapper point to the new network + Abc_NtkForEachCi( pNtk, pNode, i ) + Fraig_NodeSetData1( Fraig_ManReadIthVar(pMan, i), (Fraig_Node_t *)pNode->pCopy ); + // set the constant node + Fraig_NodeSetData1( Fraig_ManReadConst1(pMan), (Fraig_Node_t *)Abc_AigConst1(pNtkNew) ); + // process the nodes in topological order + pProgress = Extra_ProgressBarStart( stdout, Abc_NtkCoNum(pNtk) ); + Abc_NtkForEachCo( pNtk, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + pNodeNew = Abc_NodeFromFraig_rec( pNtkNew, Fraig_ManReadOutputs(pMan)[i] ); + Abc_ObjAddFanin( pNode->pCopy, pNodeNew ); + } + Extra_ProgressBarStop( pProgress ); + Abc_NtkReassignIds( pNtkNew ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Transforms into AIG one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NodeFromFraig_rec( Abc_Ntk_t * pNtkNew, Fraig_Node_t * pNodeFraig ) +{ + Abc_Obj_t * pRes, * pRes0, * pRes1, * pResMin, * pResCur; + Fraig_Node_t * pNodeTemp, * pNodeFraigR = Fraig_Regular(pNodeFraig); + void ** ppTail; + // check if the node was already considered + if ( pRes = (Abc_Obj_t *)Fraig_NodeReadData1(pNodeFraigR) ) + return Abc_ObjNotCond( pRes, Fraig_IsComplement(pNodeFraig) ); + // solve the children + pRes0 = Abc_NodeFromFraig_rec( pNtkNew, Fraig_NodeReadOne(pNodeFraigR) ); + pRes1 = Abc_NodeFromFraig_rec( pNtkNew, Fraig_NodeReadTwo(pNodeFraigR) ); + // derive the new node + pRes = Abc_AigAnd( pNtkNew->pManFunc, pRes0, pRes1 ); + pRes->fPhase = Fraig_NodeReadSimInv( pNodeFraigR ); + // if the node has an equivalence class, find its representative + if ( Fraig_NodeReadRepr(pNodeFraigR) == NULL && Fraig_NodeReadNextE(pNodeFraigR) != NULL ) + { + // go through the FRAIG nodes belonging to this equivalence class + // and find the representative node (the node with the smallest level) + pResMin = pRes; + for ( pNodeTemp = Fraig_NodeReadNextE(pNodeFraigR); pNodeTemp; pNodeTemp = Fraig_NodeReadNextE(pNodeTemp) ) + { + assert( Fraig_NodeReadData1(pNodeTemp) == NULL ); + pResCur = Abc_NodeFromFraig_rec( pNtkNew, pNodeTemp ); + if ( pResMin->Level > pResCur->Level ) + pResMin = pResCur; + } + // link the nodes in such a way that representative goes first + ppTail = &pResMin->pData; + if ( pRes != pResMin ) + { + *ppTail = pRes; + ppTail = &pRes->pData; + } + for ( pNodeTemp = Fraig_NodeReadNextE(pNodeFraigR); pNodeTemp; pNodeTemp = Fraig_NodeReadNextE(pNodeTemp) ) + { + pResCur = (Abc_Obj_t *)Fraig_NodeReadData1(pNodeTemp); + assert( pResCur ); + if ( pResMin == pResCur ) + continue; + *ppTail = pResCur; + ppTail = &pResCur->pData; + } + assert( *ppTail == NULL ); + + // update the phase of the node + pRes = Abc_ObjNotCond( pResMin, (pRes->fPhase ^ pResMin->fPhase) ); + } + Fraig_NodeSetData1( pNodeFraigR, (Fraig_Node_t *)pRes ); + return Abc_ObjNotCond( pRes, Fraig_IsComplement(pNodeFraig) ); +} + +/**Function************************************************************* + + Synopsis [Transforms FRAIG into strashed network without choices.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFromFraig2( Fraig_Man_t * pMan, Abc_Ntk_t * pNtk ) +{ + ProgressBar * pProgress; + stmm_table * tTable; + Vec_Ptr_t * vNodeReprs; + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pNode, * pRepr, ** ppSlot; + int i; + + // map the nodes into their lowest level representives + tTable = stmm_init_table(stmm_ptrcmp,stmm_ptrhash); + pNode = Abc_AigConst1(pNtk); + if ( !stmm_find_or_add( tTable, (char *)Fraig_Regular(pNode->pCopy), (char ***)&ppSlot ) ) + *ppSlot = pNode; + Abc_NtkForEachCi( pNtk, pNode, i ) + if ( !stmm_find_or_add( tTable, (char *)Fraig_Regular(pNode->pCopy), (char ***)&ppSlot ) ) + *ppSlot = pNode; + Abc_NtkForEachNode( pNtk, pNode, i ) + if ( pNode->pCopy ) + { + if ( !stmm_find_or_add( tTable, (char *)Fraig_Regular(pNode->pCopy), (char ***)&ppSlot ) ) + *ppSlot = pNode; + else if ( (*ppSlot)->Level > pNode->Level ) + *ppSlot = pNode; + } + // save representatives for each node + vNodeReprs = Vec_PtrStart( Abc_NtkObjNumMax(pNtk) ); + Abc_NtkForEachNode( pNtk, pNode, i ) + if ( pNode->pCopy ) + { + if ( !stmm_lookup( tTable, (char *)Fraig_Regular(pNode->pCopy), (char **)&pRepr ) ) + assert( 0 ); + if ( pNode != pRepr ) + Vec_PtrWriteEntry( vNodeReprs, pNode->Id, pRepr ); + } + stmm_free_table( tTable ); + + // create the new network + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_STRASH, ABC_FUNC_AIG ); + + // perform strashing + Abc_AigSetNodePhases( pNtk ); + Abc_NtkIncrementTravId( pNtk ); + pProgress = Extra_ProgressBarStart( stdout, Abc_NtkCoNum(pNtk) ); + Abc_NtkForEachCo( pNtk, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + Abc_NtkFromFraig2_rec( pNtkNew, Abc_ObjFanin0(pNode), vNodeReprs ); + } + Extra_ProgressBarStop( pProgress ); + Vec_PtrFree( vNodeReprs ); + + // finalize the network + Abc_NtkFinalize( pNtk, pNtkNew ); + return pNtkNew; +} + + +/**Function************************************************************* + + Synopsis [Transforms into AIG one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkFromFraig2_rec( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pNode, Vec_Ptr_t * vNodeReprs ) +{ + Abc_Obj_t * pRepr; + // skip the PIs and constants + if ( Abc_ObjFaninNum(pNode) < 2 ) + return; + // if this node is already visited, skip + if ( Abc_NodeIsTravIdCurrent( pNode ) ) + return; + // mark the node as visited + Abc_NodeSetTravIdCurrent( pNode ); + assert( Abc_ObjIsNode( pNode ) ); + // get the node's representative + if ( pRepr = Vec_PtrEntry(vNodeReprs, pNode->Id) ) + { + Abc_NtkFromFraig2_rec( pNtkNew, pRepr, vNodeReprs ); + pNode->pCopy = Abc_ObjNotCond( pRepr->pCopy, pRepr->fPhase ^ pNode->fPhase ); + return; + } + Abc_NtkFromFraig2_rec( pNtkNew, Abc_ObjFanin0(pNode), vNodeReprs ); + Abc_NtkFromFraig2_rec( pNtkNew, Abc_ObjFanin1(pNode), vNodeReprs ); + pNode->pCopy = Abc_AigAnd( pNtkNew->pManFunc, Abc_ObjChild0Copy(pNode), Abc_ObjChild1Copy(pNode) ); +} + + + +/**Function************************************************************* + + Synopsis [Interfaces the network with the FRAIG package.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFraigTrust( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkNew; + + if ( !Abc_NtkIsSopLogic(pNtk) ) + { + printf( "Abc_NtkFraigTrust: Trust mode works for netlists and logic SOP networks.\n" ); + return NULL; + } + + if ( !Abc_NtkFraigTrustCheck(pNtk) ) + { + printf( "Abc_NtkFraigTrust: The network does not look like an AIG with choice nodes.\n" ); + return NULL; + } + + // perform strashing + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_STRASH, ABC_FUNC_AIG ); + Abc_NtkFraigTrustOne( pNtk, pNtkNew ); + Abc_NtkFinalize( pNtk, pNtkNew ); + Abc_NtkReassignIds( pNtkNew ); + + // print a warning about choice nodes + printf( "Warning: The resulting AIG contains %d choice nodes.\n", Abc_NtkGetChoiceNum( pNtkNew ) ); + + // make sure that everything is okay + if ( !Abc_NtkCheck( pNtkNew ) ) + { + printf( "Abc_NtkFraigTrust: The network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Checks whether the node can be processed in the trust mode.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkFraigTrustCheck( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i, nFanins; + Abc_NtkForEachNode( pNtk, pNode, i ) + { + nFanins = Abc_ObjFaninNum(pNode); + if ( nFanins < 2 ) + continue; + if ( nFanins == 2 && Abc_SopIsAndType(pNode->pData) ) + continue; + if ( !Abc_SopIsOrType(pNode->pData) ) + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Interfaces the network with the FRAIG package.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkFraigTrustOne( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkNew ) +{ + ProgressBar * pProgress; + Vec_Ptr_t * vNodes; + Abc_Obj_t * pNode, * pNodeNew, * pObj; + int i; + + // perform strashing + vNodes = Abc_NtkDfs( pNtk, 0 ); + pProgress = Extra_ProgressBarStart( stdout, vNodes->nSize ); + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + // get the node + assert( Abc_ObjIsNode(pNode) ); + // strash the node + pNodeNew = Abc_NodeFraigTrust( pNtkNew, pNode ); + // get the old object + if ( Abc_NtkIsNetlist(pNtk) ) + pObj = Abc_ObjFanout0( pNode ); // the fanout net + else + pObj = pNode; // the node itself + // make sure the node is not yet strashed + assert( pObj->pCopy == NULL ); + // mark the old object with the new AIG node + pObj->pCopy = pNodeNew; + } + Vec_PtrFree( vNodes ); + Extra_ProgressBarStop( pProgress ); +} + +/**Function************************************************************* + + Synopsis [Transforms one node into a FRAIG in the trust mode.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NodeFraigTrust( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pSum, * pFanin; + void ** ppTail; + int i, nFanins, fCompl; + + assert( Abc_ObjIsNode(pNode) ); + // get the number of node's fanins + nFanins = Abc_ObjFaninNum( pNode ); + assert( nFanins == Abc_SopGetVarNum(pNode->pData) ); + // check if it is a constant + if ( nFanins == 0 ) + return Abc_ObjNotCond( Abc_AigConst1(pNtkNew), Abc_SopIsConst0(pNode->pData) ); + if ( nFanins == 1 ) + return Abc_ObjNotCond( Abc_ObjFanin0(pNode)->pCopy, Abc_SopIsInv(pNode->pData) ); + if ( nFanins == 2 && Abc_SopIsAndType(pNode->pData) ) + return Abc_AigAnd( pNtkNew->pManFunc, + Abc_ObjNotCond( Abc_ObjFanin0(pNode)->pCopy, !Abc_SopGetIthCareLit(pNode->pData,0) ), + Abc_ObjNotCond( Abc_ObjFanin1(pNode)->pCopy, !Abc_SopGetIthCareLit(pNode->pData,1) ) ); + assert( Abc_SopIsOrType(pNode->pData) ); + fCompl = Abc_SopGetIthCareLit(pNode->pData,0); + // get the root of the choice node (the first fanin) + pSum = Abc_ObjFanin0(pNode)->pCopy; + // connect other fanins + ppTail = &pSum->pData; + Abc_ObjForEachFanin( pNode, pFanin, i ) + { + if ( i == 0 ) + continue; + *ppTail = pFanin->pCopy; + ppTail = &pFanin->pCopy->pData; + // set the complemented bit of this cut + if ( fCompl ^ Abc_SopGetIthCareLit(pNode->pData, i) ) + pFanin->pCopy->fPhase = 1; + } + assert( *ppTail == NULL ); + return pSum; +} + + + + +/**Function************************************************************* + + Synopsis [Interfaces the network with the FRAIG package.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkFraigStore( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pStore; + int nAndsOld; + + if ( !Abc_NtkIsLogic(pNtk) && !Abc_NtkIsStrash(pNtk) ) + { + printf( "The netlist need to be converted into a logic network before adding it to storage.\n" ); + return 0; + } + + // get the network currently stored + pStore = Abc_FrameReadNtkStore(); + if ( pStore == NULL ) + { + // start the stored network + pStore = Abc_NtkStrash( pNtk, 0, 0, 0 ); + if ( pStore == NULL ) + { + printf( "Abc_NtkFraigStore: Initial strashing has failed.\n" ); + return 0; + } + // save the parameters + Abc_FrameSetNtkStore( pStore ); + Abc_FrameSetNtkStoreSize( 1 ); + nAndsOld = 0; + } + else + { + // add the new network to storage + nAndsOld = Abc_NtkNodeNum( pStore ); + if ( !Abc_NtkAppend( pStore, pNtk, 0 ) ) + { + printf( "The current network cannot be appended to the stored network.\n" ); + return 0; + } + // set the number of networks stored + Abc_FrameSetNtkStoreSize( Abc_FrameReadNtkStoreSize() + 1 ); + } + printf( "The number of AIG nodes added to storage = %5d.\n", Abc_NtkNodeNum(pStore) - nAndsOld ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Interfaces the network with the FRAIG package.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFraigRestore() +{ + extern Abc_Ntk_t * Abc_NtkFraigPartitioned( Abc_Ntk_t * pNtk, void * pParams ); + + Fraig_Params_t Params; + Abc_Ntk_t * pStore, * pFraig; + int nWords1, nWords2, nWordsMin; + int clk = clock(); + + // get the stored network + pStore = Abc_FrameReadNtkStore(); + Abc_FrameSetNtkStore( NULL ); + if ( pStore == NULL ) + { + printf( "There are no network currently in storage.\n" ); + return NULL; + } + printf( "Currently stored %d networks with %d nodes will be fraiged.\n", + Abc_FrameReadNtkStoreSize(), Abc_NtkNodeNum(pStore) ); + + // to determine the number of simulation patterns + // use the following strategy + // at least 64 words (32 words random and 32 words dynamic) + // no more than 256M for one circuit (128M + 128M) + nWords1 = 32; + nWords2 = (1<<27) / (Abc_NtkNodeNum(pStore) + Abc_NtkCiNum(pStore)); + nWordsMin = ABC_MIN( nWords1, nWords2 ); + + // set parameters for fraiging + Fraig_ParamsSetDefault( &Params ); + Params.nPatsRand = nWordsMin * 32; // the number of words of random simulation info + Params.nPatsDyna = nWordsMin * 32; // the number of words of dynamic simulation info + Params.nBTLimit = 999999; // the max number of backtracks to perform + Params.fFuncRed = 1; // performs only one level hashing + Params.fFeedBack = 1; // enables solver feedback + Params.fDist1Pats = 1; // enables distance-1 patterns + Params.fDoSparse = 1; // performs equiv tests for sparse functions + Params.fChoicing = 1; // enables recording structural choices + Params.fTryProve = 0; // tries to solve the final miter + Params.fVerbose = 0; // the verbosiness flag + +// Fraig_ManReportChoices( p ); + // transform it into FRAIG +// pFraig = Abc_NtkFraig( pStore, &Params, 1, 0 ); + pFraig = Abc_NtkFraigPartitioned( pStore, &Params ); + +PRT( "Total fraiging time", clock() - clk ); + if ( pFraig == NULL ) + return NULL; + Abc_NtkDelete( pStore ); + return pFraig; +} + +/**Function************************************************************* + + Synopsis [Interfaces the network with the FRAIG package.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkFraigStoreClean() +{ + Abc_Ntk_t * pStore; + // get the stored network + pStore = Abc_FrameReadNtkStore(); + if ( pStore ) + Abc_NtkDelete( pStore ); + Abc_FrameSetNtkStore( NULL ); +} + +/**Function************************************************************* + + Synopsis [Checks the correctness of stored networks.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkFraigStoreCheck( Abc_Ntk_t * pFraig ) +{ + Abc_Obj_t * pNode0, * pNode1; + int nPoOrig, nPoFinal, nStored; + int i, k; + // check that the PO functions are correct + nPoFinal = Abc_NtkPoNum(pFraig); + nStored = Abc_FrameReadNtkStoreSize(); + assert( nPoFinal % nStored == 0 ); + nPoOrig = nPoFinal / nStored; + for ( i = 0; i < nPoOrig; i++ ) + { + pNode0 = Abc_ObjFanin0( Abc_NtkPo(pFraig, i) ); + for ( k = 1; k < nStored; k++ ) + { + pNode1 = Abc_ObjFanin0( Abc_NtkPo(pFraig, k*nPoOrig+i) ); + if ( pNode0 != pNode1 ) + printf( "Verification for PO #%d of network #%d has failed. The PO function is not used.\n", i+1, k+1 ); + } + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcFxu.c b/abc_with_bb_support/src/base/abci/abcFxu.c new file mode 100644 index 000000000..f7260bc06 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcFxu.c @@ -0,0 +1,260 @@ +/**CFile**************************************************************** + + FileName [abcFxu.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Interface with the fast extract package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcFxu.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "fxu.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static bool Abc_NtkFxuCheck( Abc_Ntk_t * pNtk ); +static void Abc_NtkFxuCollectInfo( Abc_Ntk_t * pNtk, Fxu_Data_t * p ); +static void Abc_NtkFxuReconstruct( Abc_Ntk_t * pNtk, Fxu_Data_t * p ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + + +/**Function************************************************************* + + Synopsis [Performs fast_extract on the current network.] + + Description [Takes the network and the maximum number of nodes to extract. + Uses the concurrent double-cube and single cube divisor extraction procedure. + Modifies the network in the end, after extracting all nodes. Note that + Ntk_NetworkSweep() may increase the performance of this procedure because + the single-literal nodes will not be created in the sparse matrix. Returns 1 + if the network has been changed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkFastExtract( Abc_Ntk_t * pNtk, Fxu_Data_t * p ) +{ + assert( Abc_NtkIsLogic(pNtk) ); + // if the network is already in the SOP form, it may come from BLIF file + // and it may not be SCC-free, in which case FXU will not work correctly + if ( Abc_NtkIsSopLogic(pNtk) ) + { // to make sure the SOPs are SCC-free +// Abc_NtkSopToBdd(pNtk); +// Abc_NtkBddToSop(pNtk); + } + // get the network in the SOP form + if ( !Abc_NtkToSop(pNtk, 0) ) + { + printf( "Abc_NtkFastExtract(): Converting to SOPs has failed.\n" ); + return 0; + } + // check if the network meets the requirements + if ( !Abc_NtkFxuCheck(pNtk) ) + { + printf( "Abc_NtkFastExtract: Nodes have duplicated or complemented fanins. FXU is not performed.\n" ); + return 0; + } + // sweep removes useless nodes + Abc_NtkCleanup( pNtk, 0 ); + // collect information about the covers + Abc_NtkFxuCollectInfo( pNtk, p ); + // call the fast extract procedure + if ( Fxu_FastExtract(p) > 0 ) + { + // update the network + Abc_NtkFxuReconstruct( pNtk, p ); + // make sure everything is okay + if ( !Abc_NtkCheck( pNtk ) ) + printf( "Abc_NtkFastExtract: The network check has failed.\n" ); + return 1; + } + else + printf( "Warning: The network has not been changed by \"fx\".\n" ); + return 0; +} + + +/**Function************************************************************* + + Synopsis [Makes sure the nodes do not have complemented and duplicated fanins.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkFxuCheck( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode, * pFanin1, * pFanin2; + int n, i, k; + Abc_NtkForEachNode( pNtk, pNode, n ) + { + Abc_ObjForEachFanin( pNode, pFanin1, i ) + { + if ( i < 2 && Abc_ObjFaninC(pNode, i) ) + return 0; + Abc_ObjForEachFanin( pNode, pFanin2, k ) + { + if ( i == k ) + continue; + if ( pFanin1 == pFanin2 ) + return 0; + } + } + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Collect information about the network for fast_extract.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkFxuCollectInfo( Abc_Ntk_t * pNtk, Fxu_Data_t * p ) +{ + Abc_Obj_t * pNode; + int i; + // add information to the manager + p->pManSop = pNtk->pManFunc; + p->vSops = Vec_PtrAlloc(0); + p->vFanins = Vec_PtrAlloc(0); + p->vSopsNew = Vec_PtrAlloc(0); + p->vFaninsNew = Vec_PtrAlloc(0); + Vec_PtrFill( p->vSops, Abc_NtkObjNumMax(pNtk), NULL ); + Vec_PtrFill( p->vFanins, Abc_NtkObjNumMax(pNtk), NULL ); + Vec_PtrFill( p->vSopsNew, Abc_NtkObjNumMax(pNtk) + p->nNodesExt, NULL ); + Vec_PtrFill( p->vFaninsNew, Abc_NtkObjNumMax(pNtk) + p->nNodesExt, NULL ); + // add SOPs and fanin array + Abc_NtkForEachNode( pNtk, pNode, i ) + { + if ( Abc_SopGetVarNum(pNode->pData) < 2 ) + continue; + if ( Abc_SopGetCubeNum(pNode->pData) < 1 ) + continue; + p->vSops->pArray[i] = pNode->pData; + p->vFanins->pArray[i] = &pNode->vFanins; + } + p->nNodesOld = Abc_NtkObjNumMax(pNtk); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkFxuFreeInfo( Fxu_Data_t * p ) +{ + int i; + // free the arrays of new fanins + if ( p->vFaninsNew ) + for ( i = 0; i < p->vFaninsNew->nSize; i++ ) + if ( p->vFaninsNew->pArray[i] ) + Vec_IntFree( p->vFaninsNew->pArray[i] ); + // free the arrays + if ( p->vSops ) Vec_PtrFree( p->vSops ); + if ( p->vSopsNew ) Vec_PtrFree( p->vSopsNew ); + if ( p->vFanins ) Vec_PtrFree( p->vFanins ); + if ( p->vFaninsNew ) Vec_PtrFree( p->vFaninsNew ); + FREE( p ); +} + +/**Function************************************************************* + + Synopsis [Recostructs the network after FX.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkFxuReconstruct( Abc_Ntk_t * pNtk, Fxu_Data_t * p ) +{ + Vec_Int_t * vFanins; + Abc_Obj_t * pNode, * pFanin; + int i, k; + + assert( p->vFanins->nSize < p->vFaninsNew->nSize ); + // create the new nodes + for ( i = p->vFanins->nSize; i < p->vFanins->nSize + p->nNodesNew; i++ ) + { + // start the node + pNode = Abc_NtkCreateNode( pNtk ); + assert( i == (int)pNode->Id ); + } + // update the old nodes + for ( i = 0; i < p->vFanins->nSize; i++ ) + { + // the new array of fanins + vFanins = p->vFaninsNew->pArray[i]; + if ( vFanins == NULL ) + continue; + // remove old fanins + pNode = Abc_NtkObj( pNtk, i ); + Abc_ObjRemoveFanins( pNode ); + // add new fanins + vFanins = p->vFaninsNew->pArray[i]; + for ( k = 0; k < vFanins->nSize; k++ ) + { + pFanin = Abc_NtkObj( pNtk, vFanins->pArray[k] ); + Abc_ObjAddFanin( pNode, pFanin ); + } + pNode->pData = p->vSopsNew->pArray[i]; + assert( pNode->pData != NULL ); + } + // set up the new nodes + for ( i = p->vFanins->nSize; i < p->vFanins->nSize + p->nNodesNew; i++ ) + { + // get the new node + pNode = Abc_NtkObj( pNtk, i ); + // add the fanins + vFanins = p->vFaninsNew->pArray[i]; + for ( k = 0; k < vFanins->nSize; k++ ) + { + pFanin = Abc_NtkObj( pNtk, vFanins->pArray[k] ); + Abc_ObjAddFanin( pNode, pFanin ); + } + pNode->pData = p->vSopsNew->pArray[i]; + assert( pNode->pData != NULL ); + } +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcGen.c b/abc_with_bb_support/src/base/abci/abcGen.c new file mode 100644 index 000000000..bc67130b2 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcGen.c @@ -0,0 +1,511 @@ +/**CFile**************************************************************** + + FileName [abc_.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abc_.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +void Abc_WriteLayer( FILE * pFile, int nVars, int fSkip1 ); +void Abc_WriteComp( FILE * pFile ); +void Abc_WriteFullAdder( FILE * pFile ); + +void Abc_GenAdder( char * pFileName, int nVars ); +void Abc_GenSorter( char * pFileName, int nVars ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_GenAdder( char * pFileName, int nVars ) +{ + FILE * pFile; + int i; + + assert( nVars > 0 ); + + pFile = fopen( pFileName, "w" ); + fprintf( pFile, "# %d-bit ripple-carry adder generated by ABC on %s\n", nVars, Extra_TimeStamp() ); + fprintf( pFile, ".model Adder%02d\n", nVars ); + + fprintf( pFile, ".inputs" ); + for ( i = 0; i < nVars; i++ ) + fprintf( pFile, " a%02d", i ); + for ( i = 0; i < nVars; i++ ) + fprintf( pFile, " b%02d", i ); + fprintf( pFile, "\n" ); + + fprintf( pFile, ".outputs" ); + for ( i = 0; i <= nVars; i++ ) + fprintf( pFile, " y%02d", i ); + fprintf( pFile, "\n" ); + + fprintf( pFile, ".names c\n" ); + if ( nVars == 1 ) + fprintf( pFile, ".subckt FA a=a00 b=b00 cin=c s=y00 cout=y01\n" ); + else + { + fprintf( pFile, ".subckt FA a=a00 b=b00 cin=c s=y00 cout=%02d\n", 0 ); + for ( i = 1; i < nVars-1; i++ ) + fprintf( pFile, ".subckt FA a=a%02d b=b%02d cin=%02d s=y%02d cout=%02d\n", i, i, i-1, i, i ); + fprintf( pFile, ".subckt FA a=a%02d b=b%02d cin=%02d s=y%02d cout=y%02d\n", i, i, i-1, i, i+1 ); + } + fprintf( pFile, ".end\n" ); + fprintf( pFile, "\n" ); + + Abc_WriteFullAdder( pFile ); + fclose( pFile ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_GenSorter( char * pFileName, int nVars ) +{ + FILE * pFile; + int i, k, Counter, nDigits; + + assert( nVars > 1 ); + + pFile = fopen( pFileName, "w" ); + fprintf( pFile, "# %d-bit sorter generated by ABC on %s\n", nVars, Extra_TimeStamp() ); + fprintf( pFile, ".model Sorter%02d\n", nVars ); + + fprintf( pFile, ".inputs" ); + for ( i = 0; i < nVars; i++ ) + fprintf( pFile, " x%02d", i ); + fprintf( pFile, "\n" ); + + fprintf( pFile, ".outputs" ); + for ( i = 0; i < nVars; i++ ) + fprintf( pFile, " y%02d", i ); + fprintf( pFile, "\n" ); + + Counter = 0; + nDigits = Extra_Base10Log( (nVars-2)*nVars ); + if ( nVars == 2 ) + fprintf( pFile, ".subckt Comp a=x00 b=x01 x=y00 y=y01\n" ); + else + { + fprintf( pFile, ".subckt Layer0" ); + for ( k = 0; k < nVars; k++ ) + fprintf( pFile, " x%02d=x%02d", k, k ); + for ( k = 0; k < nVars; k++ ) + fprintf( pFile, " y%02d=%0*d", k, nDigits, Counter++ ); + fprintf( pFile, "\n" ); + Counter -= nVars; + for ( i = 1; i < nVars-2; i++ ) + { + fprintf( pFile, ".subckt Layer%d", (i&1) ); + for ( k = 0; k < nVars; k++ ) + fprintf( pFile, " x%02d=%0*d", k, nDigits, Counter++ ); + for ( k = 0; k < nVars; k++ ) + fprintf( pFile, " y%02d=%0*d", k, nDigits, Counter++ ); + fprintf( pFile, "\n" ); + Counter -= nVars; + } + fprintf( pFile, ".subckt Layer%d", (i&1) ); + for ( k = 0; k < nVars; k++ ) + fprintf( pFile, " x%02d=%0*d", k, nDigits, Counter++ ); + for ( k = 0; k < nVars; k++ ) + fprintf( pFile, " y%02d=y%02d", k, k ); + fprintf( pFile, "\n" ); + } + fprintf( pFile, ".end\n" ); + fprintf( pFile, "\n" ); + + Abc_WriteLayer( pFile, nVars, 0 ); + Abc_WriteLayer( pFile, nVars, 1 ); + Abc_WriteComp( pFile ); + fclose( pFile ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_WriteLayer( FILE * pFile, int nVars, int fSkip1 ) +{ + int i; + fprintf( pFile, ".model Layer%d\n", fSkip1 ); + fprintf( pFile, ".inputs" ); + for ( i = 0; i < nVars; i++ ) + fprintf( pFile, " x%02d", i ); + fprintf( pFile, "\n" ); + fprintf( pFile, ".outputs" ); + for ( i = 0; i < nVars; i++ ) + fprintf( pFile, " y%02d", i ); + fprintf( pFile, "\n" ); + if ( fSkip1 ) + { + fprintf( pFile, ".names x00 y00\n" ); + fprintf( pFile, "1 1\n" ); + i = 1; + } + else + i = 0; + for ( ; i + 1 < nVars; i += 2 ) + fprintf( pFile, ".subckt Comp a=x%02d b=x%02d x=y%02d y=y%02d\n", i, i+1, i, i+1 ); + if ( i < nVars ) + { + fprintf( pFile, ".names x%02d y%02d\n", i, i ); + fprintf( pFile, "1 1\n" ); + } + fprintf( pFile, ".end\n" ); + fprintf( pFile, "\n" ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_WriteComp( FILE * pFile ) +{ + fprintf( pFile, ".model Comp\n" ); + fprintf( pFile, ".inputs a b\n" ); + fprintf( pFile, ".outputs x y\n" ); + fprintf( pFile, ".names a b x\n" ); + fprintf( pFile, "11 1\n" ); + fprintf( pFile, ".names a b y\n" ); + fprintf( pFile, "1- 1\n" ); + fprintf( pFile, "-1 1\n" ); + fprintf( pFile, ".end\n" ); + fprintf( pFile, "\n" ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_WriteFullAdder( FILE * pFile ) +{ + fprintf( pFile, ".model FA\n" ); + fprintf( pFile, ".inputs a b cin\n" ); + fprintf( pFile, ".outputs s cout\n" ); + fprintf( pFile, ".names a b k\n" ); + fprintf( pFile, "10 1\n" ); + fprintf( pFile, "01 1\n" ); + fprintf( pFile, ".names k cin s\n" ); + fprintf( pFile, "10 1\n" ); + fprintf( pFile, "01 1\n" ); + fprintf( pFile, ".names a b cin cout\n" ); + fprintf( pFile, "11- 1\n" ); + fprintf( pFile, "1-1 1\n" ); + fprintf( pFile, "-11 1\n" ); + fprintf( pFile, ".end\n" ); + fprintf( pFile, "\n" ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_WriteCell( FILE * pFile ) +{ + fprintf( pFile, ".model cell\n" ); + fprintf( pFile, ".inputs px1 px2 py1 py2 x y\n" ); + fprintf( pFile, ".outputs fx fy\n" ); + fprintf( pFile, ".names x y a\n" ); + fprintf( pFile, "11 1\n" ); + fprintf( pFile, ".names px1 a x nx\n" ); + fprintf( pFile, "11- 1\n" ); + fprintf( pFile, "0-1 1\n" ); + fprintf( pFile, ".names py1 a y ny\n" ); + fprintf( pFile, "11- 1\n" ); + fprintf( pFile, "0-1 1\n" ); + fprintf( pFile, ".names px2 nx fx\n" ); + fprintf( pFile, "10 1\n" ); + fprintf( pFile, "01 1\n" ); + fprintf( pFile, ".names py2 ny fy\n" ); + fprintf( pFile, "10 1\n" ); + fprintf( pFile, "01 1\n" ); + fprintf( pFile, ".end\n" ); + fprintf( pFile, "\n" ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_GenMesh( char * pFileName, int nVars ) +{ + FILE * pFile; + int i, k; + + assert( nVars > 0 ); + + pFile = fopen( pFileName, "w" ); + fprintf( pFile, "# %dx%d mesh generated by ABC on %s\n", nVars, nVars, Extra_TimeStamp() ); + fprintf( pFile, ".model mesh%d\n", nVars ); + + for ( i = 0; i < nVars; i++ ) + for ( k = 0; k < nVars; k++ ) + { + fprintf( pFile, ".inputs" ); + fprintf( pFile, " p%d%dx1", i, k ); + fprintf( pFile, " p%d%dx2", i, k ); + fprintf( pFile, " p%d%dy1", i, k ); + fprintf( pFile, " p%d%dy2", i, k ); + fprintf( pFile, "\n" ); + } + fprintf( pFile, ".inputs" ); + for ( i = 0; i < nVars; i++ ) + fprintf( pFile, " v%02d v%02d", 2*i, 2*i+1 ); + fprintf( pFile, "\n" ); + + fprintf( pFile, ".outputs" ); + fprintf( pFile, " fx00" ); + fprintf( pFile, "\n" ); + + for ( i = 0; i < nVars; i++ ) // horizontal + for ( k = 0; k < nVars; k++ ) // vertical + { + fprintf( pFile, ".subckt cell" ); + fprintf( pFile, " px1=p%d%dx1", i, k ); + fprintf( pFile, " px2=p%d%dx2", i, k ); + fprintf( pFile, " py1=p%d%dy1", i, k ); + fprintf( pFile, " py2=p%d%dy2", i, k ); + if ( k == nVars - 1 ) + fprintf( pFile, " x=v%02d", i ); + else + fprintf( pFile, " x=fx%d%d", i, k+1 ); + if ( i == nVars - 1 ) + fprintf( pFile, " y=v%02d", nVars+k ); + else + fprintf( pFile, " y=fy%d%d", i+1, k ); + // outputs + fprintf( pFile, " fx=fx%d%d", i, k ); + fprintf( pFile, " fy=fy%d%d", i, k ); + fprintf( pFile, "\n" ); + } + fprintf( pFile, ".end\n" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + + Abc_WriteCell( pFile ); + fclose( pFile ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_WriteKLut( FILE * pFile, int nLutSize ) +{ + int i, iVar, iNext, nPars = (1 << nLutSize); + fprintf( pFile, "\n" ); + fprintf( pFile, ".model lut%d\n", nLutSize ); + fprintf( pFile, ".inputs" ); + for ( i = 0; i < nPars; i++ ) + fprintf( pFile, " p%02d", i ); + fprintf( pFile, "\n" ); + fprintf( pFile, ".inputs" ); + for ( i = 0; i < nLutSize; i++ ) + fprintf( pFile, " i%d", i ); + fprintf( pFile, "\n" ); + fprintf( pFile, ".outputs o\n" ); + fprintf( pFile, ".names n01 o\n" ); + fprintf( pFile, "1 1\n" ); + // write internal MUXes + iVar = 0; + iNext = 2; + for ( i = 1; i < nPars; i++ ) + { + if ( i == iNext ) + { + iNext *= 2; + iVar++; + } + if ( iVar == nLutSize - 1 ) + fprintf( pFile, ".names i%d p%02d p%02d n%02d\n", iVar, 2*(i-nPars/2), 2*(i-nPars/2)+1, i ); + else + fprintf( pFile, ".names i%d n%02d n%02d n%02d\n", iVar, 2*i, 2*i+1, i ); + fprintf( pFile, "01- 1\n" ); + fprintf( pFile, "1-1 1\n" ); + } + fprintf( pFile, ".end\n" ); + fprintf( pFile, "\n" ); +} + +/**Function************************************************************* + + Synopsis [Generates structure of L K-LUTs implementing an N-var function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_GenFpga( char * pFileName, int nLutSize, int nLuts, int nVars ) +{ + FILE * pFile; + int nVarsLut = (1 << nLutSize); // the number of LUT variables + int nVarsLog = Extra_Base2Log( nVars + nLuts - 1 ); // the number of encoding vars + int nVarsDeg = (1 << nVarsLog); // the number of LUT variables (total) + int nParsLut = nLuts * (1 << nLutSize); // the number of LUT params + int nParsVar = nLuts * nLutSize * nVarsLog; // the number of var params + int i, j, k; + + assert( nVars > 0 ); + + pFile = fopen( pFileName, "w" ); + fprintf( pFile, "# Structure with %d %d-LUTs for %d-var function generated by ABC on %s\n", nLuts, nLutSize, nVars, Extra_TimeStamp() ); + fprintf( pFile, ".model struct%dx%d_%d\n", nLuts, nLutSize, nVars ); + + fprintf( pFile, ".inputs" ); + for ( i = 0; i < nParsLut; i++ ) + fprintf( pFile, " pl%02d", i ); + fprintf( pFile, "\n" ); + + fprintf( pFile, ".inputs" ); + for ( i = 0; i < nParsVar; i++ ) + fprintf( pFile, " pv%02d", i ); + fprintf( pFile, "\n" ); + + fprintf( pFile, ".inputs" ); + for ( i = 0; i < nVars; i++ ) + fprintf( pFile, " v%02d", i ); + fprintf( pFile, "\n" ); + + fprintf( pFile, ".outputs" ); + fprintf( pFile, " v%02d", nVars + nLuts - 1 ); + fprintf( pFile, "\n" ); + fprintf( pFile, ".names Gnd\n" ); + fprintf( pFile, " 0\n" ); + + // generate LUTs + for ( i = 0; i < nLuts; i++ ) + { + fprintf( pFile, ".subckt lut%d", nLutSize ); + // generate config parameters + for ( k = 0; k < nVarsLut; k++ ) + fprintf( pFile, " p%02d=pl%02d", k, i * nVarsLut + k ); + // generate the inputs + for ( k = 0; k < nLutSize; k++ ) + fprintf( pFile, " i%d=s%02d", k, i * nLutSize + k ); + // generate the output + fprintf( pFile, " o=v%02d", nVars + i ); + fprintf( pFile, "\n" ); + } + + // generate LUT inputs + for ( i = 0; i < nLuts; i++ ) + { + for ( j = 0; j < nLutSize; j++ ) + { + fprintf( pFile, ".subckt lut%d", nVarsLog ); + // generate config parameters + for ( k = 0; k < nVarsDeg; k++ ) + { + if ( k < nVars + nLuts - 1 && k < nVars + i ) + fprintf( pFile, " p%02d=v%02d", k, k ); + else + fprintf( pFile, " p%02d=Gnd", k ); + } + // generate the inputs + for ( k = 0; k < nVarsLog; k++ ) + fprintf( pFile, " i%d=pv%02d", k, (i * nLutSize + j) * nVarsLog + k ); + // generate the output + fprintf( pFile, " o=s%02d", i * nLutSize + j ); + fprintf( pFile, "\n" ); + } + } + + fprintf( pFile, ".end\n" ); + fprintf( pFile, "\n" ); + + // generate LUTs + Abc_WriteKLut( pFile, nLutSize ); + if ( nVarsLog != nLutSize ) + Abc_WriteKLut( pFile, nVarsLog ); + fclose( pFile ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcHaig.c b/abc_with_bb_support/src/base/abci/abcHaig.c new file mode 100644 index 000000000..d0bb90a0f --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcHaig.c @@ -0,0 +1,726 @@ +/**CFile**************************************************************** + + FileName [abcHaig.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Implements history AIG for combinational rewriting.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcHaig.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Start history AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkHaigStart( Abc_Ntk_t * pNtk ) +{ + Hop_Man_t * p; + Abc_Obj_t * pObj, * pTemp; + int i; + assert( Abc_NtkIsStrash(pNtk) ); + // check if the package is already started + if ( pNtk->pHaig ) + { + Abc_NtkHaigStop( pNtk ); + assert( pNtk->pHaig == NULL ); + printf( "Warning: Previous history AIG was removed.\n" ); + } + // make sure the data is clean + Abc_NtkForEachObj( pNtk, pObj, i ) + assert( pObj->pEquiv == NULL ); + // start the HOP package + p = Hop_ManStart(); + p->vObjs = Vec_PtrAlloc( 4096 ); + Vec_PtrPush( p->vObjs, Hop_ManConst1(p) ); + // map the constant node + Abc_AigConst1(pNtk)->pEquiv = Hop_ManConst1(p); + // map the CIs + Abc_NtkForEachCi( pNtk, pObj, i ) + pObj->pEquiv = Hop_ObjCreatePi(p); + // map the internal nodes + Abc_NtkForEachNode( pNtk, pObj, i ) + pObj->pEquiv = Hop_And( p, Abc_ObjChild0Equiv(pObj), Abc_ObjChild1Equiv(pObj) ); + // map the choice nodes + if ( Abc_NtkGetChoiceNum( pNtk ) ) + { + // print warning about choice nodes + printf( "Warning: The choice nodes in the original AIG are converted into HAIG.\n" ); + Abc_NtkForEachNode( pNtk, pObj, i ) + { + if ( !Abc_AigNodeIsChoice( pObj ) ) + continue; + for ( pTemp = pObj->pData; pTemp; pTemp = pTemp->pData ) + Hop_ObjCreateChoice( pObj->pEquiv, pTemp->pEquiv ); + } + } + // make sure everything is okay + if ( !Hop_ManCheck(p) ) + { + printf( "Abc_NtkHaigStart: Check for History AIG has failed.\n" ); + Hop_ManStop(p); + return 0; + } + pNtk->pHaig = p; + return 1; +} + +/**Function************************************************************* + + Synopsis [Stops history AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkHaigStop( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i; + assert( Abc_NtkIsStrash(pNtk) ); + if ( pNtk->pHaig == NULL ) + { + printf( "Warning: History AIG is not allocated.\n" ); + return 1; + } + Abc_NtkForEachObj( pNtk, pObj, i ) + pObj->pEquiv = NULL; + Hop_ManStop( pNtk->pHaig ); + pNtk->pHaig = NULL; + return 1; +} + +/**Function************************************************************* + + Synopsis [Transfers the HAIG to the new network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkHaigTranfer( Abc_Ntk_t * pNtkOld, Abc_Ntk_t * pNtkNew ) +{ + Abc_Obj_t * pObj; + int i; + if ( pNtkOld->pHaig == NULL ) + return; + // transfer the package + assert( pNtkNew->pHaig == NULL ); + pNtkNew->pHaig = pNtkOld->pHaig; + pNtkOld->pHaig = NULL; + // transfer constant pointer + Abc_AigConst1(pNtkOld)->pCopy->pEquiv = Abc_AigConst1(pNtkOld)->pEquiv; + // transfer the CI pointers + Abc_NtkForEachCi( pNtkOld, pObj, i ) + pObj->pCopy->pEquiv = pObj->pEquiv; +} + + + +/**Function************************************************************* + + Synopsis [Collects the nodes in the classes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkHaigCollectMembers( Hop_Man_t * p ) +{ + Vec_Ptr_t * vObjs; + Hop_Obj_t * pObj; + int i; + vObjs = Vec_PtrAlloc( 4098 ); + Vec_PtrForEachEntry( p->vObjs, pObj, i ) + { + if ( pObj->pData == NULL ) + continue; + pObj->pData = Hop_ObjRepr( pObj ); + Vec_PtrPush( vObjs, pObj ); + } + return vObjs; +} + +/**Function************************************************************* + + Synopsis [Creates classes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkHaigCreateClasses( Vec_Ptr_t * vMembers ) +{ + Vec_Ptr_t * vClasses; + Hop_Obj_t * pObj, * pRepr; + int i; + + // count classes + vClasses = Vec_PtrAlloc( 4098 ); + Vec_PtrForEachEntry( vMembers, pObj, i ) + { + pRepr = pObj->pData; + assert( pRepr->pData == NULL ); + if ( pRepr->fMarkA == 0 ) // new + { + pRepr->fMarkA = 1; + Vec_PtrPush( vClasses, pRepr ); + } + } + + // set representatives as representatives + Vec_PtrForEachEntry( vClasses, pObj, i ) + { + pObj->fMarkA = 0; + pObj->pData = pObj; + } + + // go through the members and update + Vec_PtrForEachEntry( vMembers, pObj, i ) + { + pRepr = pObj->pData; + if ( ((Hop_Obj_t *)pRepr->pData)->Id > pObj->Id ) + pRepr->pData = pObj; + } + + // change representatives of the class + Vec_PtrForEachEntry( vMembers, pObj, i ) + { + pRepr = pObj->pData; + pObj->pData = pRepr->pData; + assert( ((Hop_Obj_t *)pObj->pData)->Id <= pObj->Id ); + } + + // update classes + Vec_PtrForEachEntry( vClasses, pObj, i ) + { + pRepr = pObj->pData; + assert( pRepr->pData == pRepr ); +// pRepr->pData = NULL; + Vec_PtrWriteEntry( vClasses, i, pRepr ); + Vec_PtrPush( vMembers, pObj ); + } + + Vec_PtrForEachEntry( vMembers, pObj, i ) + if ( pObj->pData == pObj ) + pObj->pData = NULL; + +/* + Vec_PtrForEachEntry( vMembers, pObj, i ) + { + printf( "ObjId = %4d : ", pObj->Id ); + if ( pObj->pData == NULL ) + { + printf( "NULL" ); + } + else + { + printf( "%4d", ((Hop_Obj_t *)pObj->pData)->Id ); + assert( ((Hop_Obj_t *)pObj->pData)->Id <= pObj->Id ); + } + printf( "\n" ); + } +*/ + return vClasses; +} + +/**Function************************************************************* + + Synopsis [Counts how many data members have non-trivial fanout.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkHaigCountFans( Hop_Man_t * p ) +{ + Hop_Obj_t * pObj; + int i, Counter = 0; + Vec_PtrForEachEntry( p->vObjs, pObj, i ) + { + if ( pObj->pData == NULL ) + continue; + if ( Hop_ObjRefs(pObj) > 0 ) + Counter++; + } + printf( "The number of class members with fanouts = %5d.\n", Counter ); + return Counter; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Hop_Obj_t * Hop_ObjReprHop( Hop_Obj_t * pObj ) +{ + Hop_Obj_t * pRepr; + assert( pObj->pNext != NULL ); + if ( pObj->pData == NULL ) + return pObj->pNext; + pRepr = pObj->pData; + assert( pRepr->pData == pRepr ); + return Hop_NotCond( pRepr->pNext, pObj->fPhase ^ pRepr->fPhase ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Hop_Obj_t * Hop_ObjChild0Hop( Hop_Obj_t * pObj ) { return Hop_NotCond( Hop_ObjReprHop(Hop_ObjFanin0(pObj)), Hop_ObjFaninC0(pObj) ); } +static inline Hop_Obj_t * Hop_ObjChild1Hop( Hop_Obj_t * pObj ) { return Hop_NotCond( Hop_ObjReprHop(Hop_ObjFanin1(pObj)), Hop_ObjFaninC1(pObj) ); } + +/**Function************************************************************* + + Synopsis [Stops history AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Man_t * Abc_NtkHaigReconstruct( Hop_Man_t * p ) +{ + Hop_Man_t * pNew; + Hop_Obj_t * pObj; + int i, Counter = 0; + Vec_PtrForEachEntry( p->vObjs, pObj, i ) + pObj->pNext = NULL; + // start the HOP package + pNew = Hop_ManStart(); + pNew->vObjs = Vec_PtrAlloc( p->nCreated ); + Vec_PtrPush( pNew->vObjs, Hop_ManConst1(pNew) ); + // map the constant node + Hop_ManConst1(p)->pNext = Hop_ManConst1(pNew); + // map the CIs + Hop_ManForEachPi( p, pObj, i ) + pObj->pNext = Hop_ObjCreatePi(pNew); + // map the internal nodes + Vec_PtrForEachEntry( p->vObjs, pObj, i ) + { + if ( !Hop_ObjIsNode(pObj) ) + continue; + pObj->pNext = Hop_And( pNew, Hop_ObjChild0Hop(pObj), Hop_ObjChild1Hop(pObj) ); +// assert( !Hop_IsComplement(pObj->pNext) ); + if ( Hop_ManConst1(pNew) == Hop_Regular(pObj->pNext) ) + Counter++; + if ( pObj->pData ) // member of the class + Hop_Regular(pObj->pNext)->pData = Hop_Regular(((Hop_Obj_t *)pObj->pData)->pNext); + } +// printf( " Counter = %d.\n", Counter ); + // transfer the POs + Hop_ManForEachPo( p, pObj, i ) + Hop_ObjCreatePo( pNew, Hop_ObjChild0Hop(pObj) ); + // check the new manager + if ( !Hop_ManCheck(pNew) ) + { + printf( "Abc_NtkHaigReconstruct: Check for History AIG has failed.\n" ); + Hop_ManStop(pNew); + return NULL; + } + return pNew; +} + + +/**Function************************************************************* + + Synopsis [Returns 1 if pOld is in the TFI of pNew.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkHaigCheckTfi_rec( Abc_Obj_t * pNode, Abc_Obj_t * pOld ) +{ + if ( pNode == NULL ) + return 0; + if ( pNode == pOld ) + return 1; + // check the trivial cases + if ( Abc_ObjIsCi(pNode) ) + return 0; + assert( Abc_ObjIsNode(pNode) ); + // if this node is already visited, skip + if ( Abc_NodeIsTravIdCurrent( pNode ) ) + return 0; + // mark the node as visited + Abc_NodeSetTravIdCurrent( pNode ); + // check the children + if ( Abc_NtkHaigCheckTfi_rec( Abc_ObjFanin0(pNode), pOld ) ) + return 1; + if ( Abc_NtkHaigCheckTfi_rec( Abc_ObjFanin1(pNode), pOld ) ) + return 1; + // check equivalent nodes + return Abc_NtkHaigCheckTfi_rec( pNode->pData, pOld ); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if pOld is in the TFI of pNew.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkHaigCheckTfi( Abc_Ntk_t * pNtk, Abc_Obj_t * pOld, Abc_Obj_t * pNew ) +{ + assert( !Abc_ObjIsComplement(pOld) ); + assert( !Abc_ObjIsComplement(pNew) ); + Abc_NtkIncrementTravId(pNtk); + return Abc_NtkHaigCheckTfi_rec( pNew, pOld ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Abc_Obj_t * Hop_ObjChild0Next( Hop_Obj_t * pObj ) { return Abc_ObjNotCond( (Abc_Obj_t *)Hop_ObjFanin0(pObj)->pNext, Hop_ObjFaninC0(pObj) ); } +static inline Abc_Obj_t * Hop_ObjChild1Next( Hop_Obj_t * pObj ) { return Abc_ObjNotCond( (Abc_Obj_t *)Hop_ObjFanin1(pObj)->pNext, Hop_ObjFaninC1(pObj) ); } + +/**Function************************************************************* + + Synopsis [Stops history AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkHaigRecreateAig( Abc_Ntk_t * pNtk, Hop_Man_t * p ) +{ + Abc_Ntk_t * pNtkAig; + Abc_Obj_t * pObjOld, * pObjAbcThis, * pObjAbcRepr; + Hop_Obj_t * pObj; + int i; + assert( p->nCreated == Vec_PtrSize(p->vObjs) ); + + // start the new network + pNtkAig = Abc_NtkStartFrom( pNtk, ABC_NTK_STRASH, ABC_FUNC_AIG ); + + // transfer new nodes to the PIs of HOP + Hop_ManConst1(p)->pNext = (Hop_Obj_t *)Abc_AigConst1( pNtkAig ); + Hop_ManForEachPi( p, pObj, i ) + pObj->pNext = (Hop_Obj_t *)Abc_NtkCi( pNtkAig, i ); + + // construct new nodes + Vec_PtrForEachEntry( p->vObjs, pObj, i ) + { + if ( !Hop_ObjIsNode(pObj) ) + continue; + pObj->pNext = (Hop_Obj_t *)Abc_AigAnd( pNtkAig->pManFunc, Hop_ObjChild0Next(pObj), Hop_ObjChild1Next(pObj) ); + assert( !Hop_IsComplement(pObj->pNext) ); + } + + // set the COs + Abc_NtkForEachCo( pNtk, pObjOld, i ) + Abc_ObjAddFanin( pObjOld->pCopy, Hop_ObjChild0Next(Hop_ManPo(p,i)) ); + + // construct choice nodes + Vec_PtrForEachEntry( p->vObjs, pObj, i ) + { + // skip the node without choices + if ( pObj->pData == NULL ) + continue; + // skip the representative of the class + if ( pObj->pData == pObj ) + continue; + // do not create choices for constant 1 and PIs + if ( !Hop_ObjIsNode(pObj->pData) ) + continue; + // get the corresponding new nodes + pObjAbcThis = (Abc_Obj_t *)pObj->pNext; + pObjAbcRepr = (Abc_Obj_t *)((Hop_Obj_t *)pObj->pData)->pNext; + // the new node cannot be already in the class + assert( pObjAbcThis->pData == NULL ); + // the new node cannot have fanouts + assert( Abc_ObjFanoutNum(pObjAbcThis) == 0 ); + // these should be different nodes + assert( pObjAbcRepr != pObjAbcThis ); + // do not create choices if there is a path from pObjAbcThis to pObjAbcRepr + if ( !Abc_NtkHaigCheckTfi( pNtkAig, pObjAbcRepr, pObjAbcThis ) ) + { + // find the last node in the class + while ( pObjAbcRepr->pData ) + pObjAbcRepr = pObjAbcRepr->pData; + // add the new node at the end of the list + pObjAbcRepr->pData = pObjAbcThis; + } + } + + // finish the new network +// Abc_NtkFinalize( pNtk, pNtkAig ); +// Abc_AigCleanup( pNtkAig->pManFunc ); + // check correctness of the network + if ( !Abc_NtkCheck( pNtkAig ) ) + { + printf( "Abc_NtkHaigUse: The network check has failed.\n" ); + Abc_NtkDelete( pNtkAig ); + return NULL; + } + return pNtkAig; +} + +/**Function************************************************************* + + Synopsis [Resets representatives.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkHaigResetReprsOld( Hop_Man_t * pMan ) +{ + Vec_Ptr_t * vMembers, * vClasses; + + // collect members of the classes and make them point to reprs + vMembers = Abc_NtkHaigCollectMembers( pMan ); + printf( "Collected %6d class members.\n", Vec_PtrSize(vMembers) ); + + // create classes + vClasses = Abc_NtkHaigCreateClasses( vMembers ); + printf( "Collected %6d classes. (Ave = %5.2f)\n", Vec_PtrSize(vClasses), + (float)(Vec_PtrSize(vMembers))/Vec_PtrSize(vClasses) ); + + Vec_PtrFree( vMembers ); + Vec_PtrFree( vClasses ); +} + +/**Function************************************************************* + + Synopsis [Resets representatives.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkHaigResetReprs( Hop_Man_t * p ) +{ + Hop_Obj_t * pObj, * pRepr; + int i, nClasses, nMembers, nFanouts, nNormals; + // clear self-classes + Vec_PtrForEachEntry( p->vObjs, pObj, i ) + { + // fix the strange situation of double-loop + pRepr = pObj->pData; + if ( pRepr && pRepr->pData == pObj ) + pRepr->pData = pRepr; + // remove self-loops + if ( pObj->pData == pObj ) + pObj->pData = NULL; + } + // set representatives + Vec_PtrForEachEntry( p->vObjs, pObj, i ) + { + if ( pObj->pData == NULL ) + continue; + // get representative of the node + pRepr = Hop_ObjRepr( pObj ); + pRepr->pData = pRepr; + // set the representative + pObj->pData = pRepr; + } + // make each class point to the smallest topological order + Vec_PtrForEachEntry( p->vObjs, pObj, i ) + { + if ( pObj->pData == NULL ) + continue; + pRepr = Hop_ObjRepr( pObj ); + if ( pRepr->Id > pObj->Id ) + { + pRepr->pData = pObj; + pObj->pData = pObj; + } + else + pObj->pData = pRepr; + } + // count classes, members, and fanouts - and verify + nMembers = nClasses = nFanouts = nNormals = 0; + Vec_PtrForEachEntry( p->vObjs, pObj, i ) + { + if ( pObj->pData == NULL ) + continue; + // count members + nMembers++; + // count the classes and fanouts + if ( pObj->pData == pObj ) + nClasses++; + else if ( Hop_ObjRefs(pObj) > 0 ) + nFanouts++; + else + nNormals++; + // compare representatives + pRepr = Hop_ObjRepr( pObj ); + assert( pObj->pData == pRepr ); + assert( pRepr->Id <= pObj->Id ); + } +// printf( "Nodes = %7d. Member = %7d. Classes = %6d. Fanouts = %6d. Normals = %6d.\n", +// Hop_ManNodeNum(p), nMembers, nClasses, nFanouts, nNormals ); + return nFanouts; +} + +/**Function************************************************************* + + Synopsis [Stops history AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkHaigUse( Abc_Ntk_t * pNtk ) +{ + Hop_Man_t * pMan, * pManTemp; + Abc_Ntk_t * pNtkAig; + Abc_Obj_t * pObj; + int i; + + // check if HAIG is available + assert( Abc_NtkIsStrash(pNtk) ); + if ( pNtk->pHaig == NULL ) + { + printf( "Warning: History AIG is not available.\n" ); + return NULL; + } + // convert HOP package into AIG with choices + // print HAIG stats +// Hop_ManPrintStats( pMan ); // USES DATA!!! + + // add the POs + Abc_NtkForEachCo( pNtk, pObj, i ) + Hop_ObjCreatePo( pNtk->pHaig, Abc_ObjChild0Equiv(pObj) ); + + // clean the old network + Abc_NtkForEachObj( pNtk, pObj, i ) + pObj->pEquiv = NULL; + pMan = pNtk->pHaig; + pNtk->pHaig = 0; + + // iteratively reconstruct the HOP manager to create choice nodes + while ( Abc_NtkHaigResetReprs( pMan ) ) + { + pMan = Abc_NtkHaigReconstruct( pManTemp = pMan ); + Hop_ManStop( pManTemp ); + } + + // traverse in the topological order and create new AIG + pNtkAig = Abc_NtkHaigRecreateAig( pNtk, pMan ); + Hop_ManStop( pMan ); + + // free HAIG + return pNtkAig; +} + +/**Function************************************************************* + + Synopsis [Transform HOP manager into the one without loops.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkHopRemoveLoops( Abc_Ntk_t * pNtk, Hop_Man_t * pMan ) +{ + Abc_Ntk_t * pNtkAig; + Hop_Man_t * pManTemp; + + // iteratively reconstruct the HOP manager to create choice nodes + while ( Abc_NtkHaigResetReprs( pMan ) ) + { + pMan = Abc_NtkHaigReconstruct( pManTemp = pMan ); + Hop_ManStop( pManTemp ); + } + + // traverse in the topological order and create new AIG + pNtkAig = Abc_NtkHaigRecreateAig( pNtk, pMan ); + Hop_ManStop( pMan ); + return pNtkAig; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcIf.c b/abc_with_bb_support/src/base/abci/abcIf.c new file mode 100644 index 000000000..8aeef15b0 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcIf.c @@ -0,0 +1,501 @@ +/**CFile**************************************************************** + + FileName [abcIf.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Interface with the FPGA mapping package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - November 21, 2006.] + + Revision [$Id: abcIf.c,v 1.00 2006/11/21 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "if.h" +#include "kit.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static If_Man_t * Abc_NtkToIf( Abc_Ntk_t * pNtk, If_Par_t * pPars ); +static Abc_Ntk_t * Abc_NtkFromIf( If_Man_t * pIfMan, Abc_Ntk_t * pNtk ); +extern Abc_Obj_t * Abc_NodeFromIf_rec( Abc_Ntk_t * pNtkNew, If_Man_t * pIfMan, If_Obj_t * pIfObj, Vec_Int_t * vCover ); +static Hop_Obj_t * Abc_NodeIfToHop( Hop_Man_t * pHopMan, If_Man_t * pIfMan, If_Obj_t * pIfObj ); +static Vec_Ptr_t * Abc_NtkFindGoodOrder( Abc_Ntk_t * pNtk ); + +extern void Abc_NtkBddReorder( Abc_Ntk_t * pNtk, int fVerbose ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Interface with the FPGA mapping package.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkIf( Abc_Ntk_t * pNtk, If_Par_t * pPars ) +{ + Abc_Ntk_t * pNtkNew; + If_Man_t * pIfMan; + + assert( Abc_NtkIsStrash(pNtk) ); + + // get timing information + pPars->pTimesArr = Abc_NtkGetCiArrivalFloats(pNtk); + pPars->pTimesReq = NULL; + + // set the latch paths + if ( pPars->fLatchPaths && pPars->pTimesArr ) + { + int c; + for ( c = 0; c < Abc_NtkPiNum(pNtk); c++ ) + pPars->pTimesArr[c] = -ABC_INFINITY; + } + + // perform FPGA mapping + pIfMan = Abc_NtkToIf( pNtk, pPars ); + if ( pIfMan == NULL ) + return NULL; + if ( !If_ManPerformMapping( pIfMan ) ) + { + If_ManStop( pIfMan ); + return NULL; + } + + // transform the result of mapping into the new network + pNtkNew = Abc_NtkFromIf( pIfMan, pNtk ); + if ( pNtkNew == NULL ) + return NULL; + If_ManStop( pIfMan ); + + // duplicate EXDC + if ( pNtk->pExdc ) + pNtkNew->pExdc = Abc_NtkDup( pNtk->pExdc ); + + // make sure that everything is okay + if ( !Abc_NtkCheck( pNtkNew ) ) + { + printf( "Abc_NtkIf: The network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Load the network into FPGA manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +If_Man_t * Abc_NtkToIf( Abc_Ntk_t * pNtk, If_Par_t * pPars ) +{ + ProgressBar * pProgress; + If_Man_t * pIfMan; + Abc_Obj_t * pNode, * pFanin, * pPrev; + Vec_Ptr_t * vNodes; + int i; + + assert( Abc_NtkIsStrash(pNtk) ); +// vNodes = Abc_NtkFindGoodOrder( pNtk ); + vNodes = Abc_AigDfs( pNtk, 0, 0 ); + + // start the mapping manager and set its parameters + pIfMan = If_ManStart( pPars ); + + // print warning about excessive memory usage + if ( 1.0 * Abc_NtkObjNum(pNtk) * pIfMan->nObjBytes / (1<<30) > 1.0 ) + printf( "Warning: The mapper will allocate %.1f Gb for to represent the subject graph with %d AIG nodes.\n", + 1.0 * Abc_NtkObjNum(pNtk) * pIfMan->nObjBytes / (1<<30), Abc_NtkObjNum(pNtk) ); + + // create PIs and remember them in the old nodes + Abc_AigConst1(pNtk)->pCopy = (Abc_Obj_t *)If_ManConst1( pIfMan ); + Abc_NtkForEachCi( pNtk, pNode, i ) + { + pNode->pCopy = (Abc_Obj_t *)If_ManCreateCi( pIfMan ); +//printf( "AIG CI %2d -> IF CI %2d\n", pNode->Id, ((If_Obj_t *)pNode->pCopy)->Id ); + } + + // load the AIG into the mapper + pProgress = Extra_ProgressBarStart( stdout, Abc_NtkObjNumMax(pNtk) ); +// Abc_AigForEachAnd( pNtk, pNode, i ) + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, "Initial" ); + // add the node to the mapper + pNode->pCopy = (Abc_Obj_t *)If_ManCreateAnd( pIfMan, + If_NotCond( (If_Obj_t *)Abc_ObjFanin0(pNode)->pCopy, Abc_ObjFaninC0(pNode) ), + If_NotCond( (If_Obj_t *)Abc_ObjFanin1(pNode)->pCopy, Abc_ObjFaninC1(pNode) ) ); + // set up the choice node + if ( Abc_AigNodeIsChoice( pNode ) ) + { + pIfMan->nChoices++; + for ( pPrev = pNode, pFanin = pNode->pData; pFanin; pPrev = pFanin, pFanin = pFanin->pData ) + If_ObjSetChoice( (If_Obj_t *)pPrev->pCopy, (If_Obj_t *)pFanin->pCopy ); + If_ManCreateChoice( pIfMan, (If_Obj_t *)pNode->pCopy ); + } +//printf( "AIG node %2d -> IF node %2d\n", pNode->Id, ((If_Obj_t *)pNode->pCopy)->Id ); + } + Extra_ProgressBarStop( pProgress ); + Vec_PtrFree( vNodes ); + + // set the primary outputs without copying the phase + Abc_NtkForEachCo( pNtk, pNode, i ) + If_ManCreateCo( pIfMan, If_NotCond( (If_Obj_t *)Abc_ObjFanin0(pNode)->pCopy, Abc_ObjFaninC0(pNode) ) ); + return pIfMan; +} + +/**Function************************************************************* + + Synopsis [Creates the mapped network.] + + Description [Assuming the copy field of the mapped nodes are NULL.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFromIf( If_Man_t * pIfMan, Abc_Ntk_t * pNtk ) +{ + ProgressBar * pProgress; + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pNode, * pNodeNew; + Vec_Int_t * vCover; + int i, nDupGates; + // create the new network + if ( pIfMan->pPars->fUseBdds || pIfMan->pPars->fUseCnfs || pIfMan->pPars->fUseMv ) + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_LOGIC, ABC_FUNC_BDD ); + else if ( pIfMan->pPars->fUseSops ) + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_LOGIC, ABC_FUNC_SOP ); + else + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_LOGIC, ABC_FUNC_AIG ); + // prepare the mapping manager + If_ManCleanNodeCopy( pIfMan ); + If_ManCleanCutData( pIfMan ); + // make the mapper point to the new network + If_ObjSetCopy( If_ManConst1(pIfMan), Abc_NtkCreateNodeConst1(pNtkNew) ); + Abc_NtkForEachCi( pNtk, pNode, i ) + If_ObjSetCopy( If_ManCi(pIfMan, i), pNode->pCopy ); + // process the nodes in topological order + vCover = Vec_IntAlloc( 1 << 16 ); + pProgress = Extra_ProgressBarStart( stdout, Abc_NtkCoNum(pNtk) ); + Abc_NtkForEachCo( pNtk, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, "Final" ); + pNodeNew = Abc_NodeFromIf_rec( pNtkNew, pIfMan, If_ObjFanin0(If_ManCo(pIfMan, i)), vCover ); + pNodeNew = Abc_ObjNotCond( pNodeNew, If_ObjFaninC0(If_ManCo(pIfMan, i)) ); + Abc_ObjAddFanin( pNode->pCopy, pNodeNew ); + } + Extra_ProgressBarStop( pProgress ); + Vec_IntFree( vCover ); + // remove the constant node if not used + pNodeNew = (Abc_Obj_t *)If_ObjCopy( If_ManConst1(pIfMan) ); + if ( Abc_ObjFanoutNum(pNodeNew) == 0 ) + Abc_NtkDeleteObj( pNodeNew ); + // minimize the node + if ( pIfMan->pPars->fUseBdds || pIfMan->pPars->fUseCnfs || pIfMan->pPars->fUseMv ) + Abc_NtkSweep( pNtkNew, 0 ); + if ( pIfMan->pPars->fUseBdds ) + Abc_NtkBddReorder( pNtkNew, 0 ); + // decouple the PO driver nodes to reduce the number of levels + nDupGates = Abc_NtkLogicMakeSimpleCos( pNtkNew, 1 ); +// if ( nDupGates && If_ManReadVerbose(pIfMan) ) +// printf( "Duplicated %d gates to decouple the CO drivers.\n", nDupGates ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Derive one node after FPGA mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NodeFromIf_rec( Abc_Ntk_t * pNtkNew, If_Man_t * pIfMan, If_Obj_t * pIfObj, Vec_Int_t * vCover ) +{ + Abc_Obj_t * pNodeNew; + If_Cut_t * pCutBest; + If_Obj_t * pIfLeaf; + int i; + // return if the result if known + pNodeNew = (Abc_Obj_t *)If_ObjCopy( pIfObj ); + if ( pNodeNew ) + return pNodeNew; + assert( pIfObj->Type == IF_AND ); + // get the parameters of the best cut + // create a new node + pNodeNew = Abc_NtkCreateNode( pNtkNew ); + pCutBest = If_ObjCutBest( pIfObj ); + if ( pIfMan->pPars->fUseCnfs || pIfMan->pPars->fUseMv ) + { + If_CutForEachLeafReverse( pIfMan, pCutBest, pIfLeaf, i ) + Abc_ObjAddFanin( pNodeNew, Abc_NodeFromIf_rec(pNtkNew, pIfMan, pIfLeaf, vCover) ); + } + else + { + If_CutForEachLeaf( pIfMan, pCutBest, pIfLeaf, i ) + Abc_ObjAddFanin( pNodeNew, Abc_NodeFromIf_rec(pNtkNew, pIfMan, pIfLeaf, vCover) ); + } + // set the level of the new node + pNodeNew->Level = Abc_ObjLevelNew( pNodeNew ); + // derive the function of this node + if ( pIfMan->pPars->fTruth ) + { + if ( pIfMan->pPars->fUseBdds ) + { + // transform truth table into the BDD + pNodeNew->pData = Kit_TruthToBdd( pNtkNew->pManFunc, If_CutTruth(pCutBest), If_CutLeaveNum(pCutBest), 0 ); Cudd_Ref(pNodeNew->pData); + } + else if ( pIfMan->pPars->fUseCnfs || pIfMan->pPars->fUseMv ) + { + // transform truth table into the BDD + pNodeNew->pData = Kit_TruthToBdd( pNtkNew->pManFunc, If_CutTruth(pCutBest), If_CutLeaveNum(pCutBest), 1 ); Cudd_Ref(pNodeNew->pData); + } + else if ( pIfMan->pPars->fUseSops ) + { + // transform truth table into the SOP + int RetValue = Kit_TruthIsop( If_CutTruth(pCutBest), If_CutLeaveNum(pCutBest), vCover, 1 ); + assert( RetValue == 0 || RetValue == 1 ); + // check the case of constant cover + if ( Vec_IntSize(vCover) == 0 || (Vec_IntSize(vCover) == 1 && Vec_IntEntry(vCover,0) == 0) ) + { + assert( RetValue == 0 ); + pNodeNew->pData = Abc_SopCreateAnd( pNtkNew->pManFunc, If_CutLeaveNum(pCutBest), NULL ); + pNodeNew = (Vec_IntSize(vCover) == 0) ? Abc_NtkCreateNodeConst0(pNtkNew) : Abc_NtkCreateNodeConst1(pNtkNew); + } + else + { + // derive the AIG for that tree + pNodeNew->pData = Abc_SopCreateFromIsop( pNtkNew->pManFunc, If_CutLeaveNum(pCutBest), vCover ); + if ( RetValue ) + Abc_SopComplement( pNodeNew->pData ); + } + } + else + { + extern Hop_Obj_t * Kit_GraphToHop( Hop_Man_t * pMan, Kit_Graph_t * pGraph ); + // transform truth table into the decomposition tree + Kit_Graph_t * pGraph = Kit_TruthToGraph( If_CutTruth(pCutBest), If_CutLeaveNum(pCutBest), vCover ); + // derive the AIG for the decomposition tree + pNodeNew->pData = Kit_GraphToHop( pNtkNew->pManFunc, pGraph ); + Kit_GraphFree( pGraph ); + } + // complement the node if the cut was complemented + if ( pCutBest->fCompl ) + Abc_NodeComplement( pNodeNew ); + } + else + pNodeNew->pData = Abc_NodeIfToHop( pNtkNew->pManFunc, pIfMan, pIfObj ); + If_ObjSetCopy( pIfObj, pNodeNew ); + return pNodeNew; +} + +/**Function************************************************************* + + Synopsis [Recursively derives the truth table for the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Abc_NodeIfToHop_rec( Hop_Man_t * pHopMan, If_Man_t * pIfMan, If_Obj_t * pIfObj, Vec_Ptr_t * vVisited ) +{ + If_Cut_t * pCut; + Hop_Obj_t * gFunc, * gFunc0, * gFunc1; + // get the best cut + pCut = If_ObjCutBest(pIfObj); + // if the cut is visited, return the result + if ( If_CutData(pCut) ) + return If_CutData(pCut); + // compute the functions of the children + gFunc0 = Abc_NodeIfToHop_rec( pHopMan, pIfMan, pIfObj->pFanin0, vVisited ); + gFunc1 = Abc_NodeIfToHop_rec( pHopMan, pIfMan, pIfObj->pFanin1, vVisited ); + // get the function of the cut + gFunc = Hop_And( pHopMan, Hop_NotCond(gFunc0, pIfObj->fCompl0), Hop_NotCond(gFunc1, pIfObj->fCompl1) ); + assert( If_CutData(pCut) == NULL ); + If_CutSetData( pCut, gFunc ); + // add this cut to the visited list + Vec_PtrPush( vVisited, pCut ); + return gFunc; +} + +/**Function************************************************************* + + Synopsis [Derives the truth table for one cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Abc_NodeIfToHop( Hop_Man_t * pHopMan, If_Man_t * pIfMan, If_Obj_t * pIfObj ) +{ + If_Cut_t * pCut; + Hop_Obj_t * gFunc; + If_Obj_t * pLeaf; + int i; + // get the best cut + pCut = If_ObjCutBest(pIfObj); + assert( pCut->nLeaves > 1 ); + // set the leaf variables + If_CutForEachLeaf( pIfMan, pCut, pLeaf, i ) + If_CutSetData( If_ObjCutBest(pLeaf), Hop_IthVar(pHopMan, i) ); + // recursively compute the function while collecting visited cuts + Vec_PtrClear( pIfMan->vTemp ); + gFunc = Abc_NodeIfToHop_rec( pHopMan, pIfMan, pIfObj, pIfMan->vTemp ); +// printf( "%d ", Vec_PtrSize(p->vTemp) ); + // clean the cuts + If_CutForEachLeaf( pIfMan, pCut, pLeaf, i ) + If_CutSetData( If_ObjCutBest(pLeaf), NULL ); + Vec_PtrForEachEntry( pIfMan->vTemp, pCut, i ) + If_CutSetData( pCut, NULL ); + return gFunc; +} + + +/**Function************************************************************* + + Synopsis [Comparison for two nodes with the flow.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_ObjCompareFlow( Abc_Obj_t ** ppNode0, Abc_Obj_t ** ppNode1 ) +{ + float Flow0 = Abc_Int2Float((int)(*ppNode0)->pCopy); + float Flow1 = Abc_Int2Float((int)(*ppNode1)->pCopy); + if ( Flow0 > Flow1 ) + return -1; + if ( Flow0 < Flow1 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Orders AIG nodes so that nodes from larger cones go first.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkFindGoodOrder_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes ) +{ + if ( !Abc_ObjIsNode(pNode) ) + return; + assert( Abc_ObjIsNode( pNode ) ); + // if this node is already visited, skip + if ( Abc_NodeIsTravIdCurrent( pNode ) ) + return; + // mark the node as visited + Abc_NodeSetTravIdCurrent( pNode ); + // visit the transitive fanin of the node + Abc_NtkFindGoodOrder_rec( Abc_ObjFanin0(pNode), vNodes ); + Abc_NtkFindGoodOrder_rec( Abc_ObjFanin1(pNode), vNodes ); + // add the node after the fanins have been added + Vec_PtrPush( vNodes, pNode ); +} + +/**Function************************************************************* + + Synopsis [Orders AIG nodes so that nodes from larger cones go first.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkFindGoodOrder( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vNodes, * vCos; + Abc_Obj_t * pNode, * pFanin0, * pFanin1; + float Flow0, Flow1; + int i; + + // initialize the flow + Abc_AigConst1(pNtk)->pCopy = NULL; + Abc_NtkForEachCi( pNtk, pNode, i ) + pNode->pCopy = NULL; + // compute the flow + Abc_AigForEachAnd( pNtk, pNode, i ) + { + pFanin0 = Abc_ObjFanin0(pNode); + pFanin1 = Abc_ObjFanin1(pNode); + Flow0 = Abc_Int2Float((int)pFanin0->pCopy)/Abc_ObjFanoutNum(pFanin0); + Flow1 = Abc_Int2Float((int)pFanin1->pCopy)/Abc_ObjFanoutNum(pFanin1); + pNode->pCopy = (Abc_Obj_t *)Abc_Float2Int(Flow0 + Flow1+(float)1.0); + } + // find the flow of the COs + vCos = Vec_PtrAlloc( Abc_NtkCoNum(pNtk) ); + Abc_NtkForEachCo( pNtk, pNode, i ) + { + pNode->pCopy = Abc_ObjFanin0(pNode)->pCopy; +// pNode->pCopy = (Abc_Obj_t *)Abc_Float2Int((float)Abc_ObjFanin0(pNode)->Level); + Vec_PtrPush( vCos, pNode ); + } + + // sort nodes in the increasing order of the flow + qsort( (Abc_Obj_t **)Vec_PtrArray(vCos), Abc_NtkCoNum(pNtk), + sizeof(Abc_Obj_t *), (int (*)(const void *, const void *))Abc_ObjCompareFlow ); + // verify sorting + pFanin0 = Vec_PtrEntry(vCos, 0); + pFanin1 = Vec_PtrEntryLast(vCos); + assert( Abc_Int2Float((int)pFanin0->pCopy) >= Abc_Int2Float((int)pFanin1->pCopy) ); + + // collect the nodes in the topological order from the new array + Abc_NtkIncrementTravId( pNtk ); + vNodes = Vec_PtrAlloc( 100 ); + Vec_PtrForEachEntry( vCos, pNode, i ) + { + Abc_NtkFindGoodOrder_rec( Abc_ObjFanin0(pNode), vNodes ); +// printf( "%.2f ", Abc_Int2Float((int)pNode->pCopy) ); + } + Vec_PtrFree( vCos ); + return vNodes; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcIvy.c b/abc_with_bb_support/src/base/abci/abcIvy.c new file mode 100644 index 000000000..07ab6b19e --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcIvy.c @@ -0,0 +1,1101 @@ +/**CFile**************************************************************** + + FileName [abcIvy.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Strashing of the current network.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcIvy.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "dec.h" +#include "ivy.h" +#include "fraig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Abc_Ntk_t * Abc_NtkFromIvy( Abc_Ntk_t * pNtkOld, Ivy_Man_t * pMan ); +static Abc_Ntk_t * Abc_NtkFromIvySeq( Abc_Ntk_t * pNtkOld, Ivy_Man_t * pMan, int fHaig ); +static Ivy_Man_t * Abc_NtkToIvy( Abc_Ntk_t * pNtkOld ); + +static void Abc_NtkStrashPerformAig( Abc_Ntk_t * pNtk, Ivy_Man_t * pMan ); +static Ivy_Obj_t * Abc_NodeStrashAig( Ivy_Man_t * pMan, Abc_Obj_t * pNode ); +static Ivy_Obj_t * Abc_NodeStrashAigSopAig( Ivy_Man_t * pMan, Abc_Obj_t * pNode, char * pSop ); +static Ivy_Obj_t * Abc_NodeStrashAigExorAig( Ivy_Man_t * pMan, Abc_Obj_t * pNode, char * pSop ); +static Ivy_Obj_t * Abc_NodeStrashAigFactorAig( Ivy_Man_t * pMan, Abc_Obj_t * pNode, char * pSop ); +extern char * Mio_GateReadSop( void * pGate ); + +typedef int Abc_Edge_t; +static inline Abc_Edge_t Abc_EdgeCreate( int Id, int fCompl ) { return (Id << 1) | fCompl; } +static inline int Abc_EdgeId( Abc_Edge_t Edge ) { return Edge >> 1; } +static inline int Abc_EdgeIsComplement( Abc_Edge_t Edge ) { return Edge & 1; } +static inline Abc_Edge_t Abc_EdgeRegular( Abc_Edge_t Edge ) { return (Edge >> 1) << 1; } +static inline Abc_Edge_t Abc_EdgeNot( Abc_Edge_t Edge ) { return Edge ^ 1; } +static inline Abc_Edge_t Abc_EdgeNotCond( Abc_Edge_t Edge, int fCond ) { return Edge ^ fCond; } +static inline Abc_Edge_t Abc_EdgeFromNode( Abc_Obj_t * pNode ) { return Abc_EdgeCreate( Abc_ObjRegular(pNode)->Id, Abc_ObjIsComplement(pNode) ); } +static inline Abc_Obj_t * Abc_EdgeToNode( Abc_Ntk_t * p, Abc_Edge_t Edge ) { return Abc_ObjNotCond( Abc_NtkObj(p, Abc_EdgeId(Edge)), Abc_EdgeIsComplement(Edge) ); } + +static inline Abc_Obj_t * Abc_ObjFanin0Ivy( Abc_Ntk_t * p, Ivy_Obj_t * pObj ) { return Abc_ObjNotCond( Abc_EdgeToNode(p, Ivy_ObjFanin0(pObj)->TravId), Ivy_ObjFaninC0(pObj) ); } +static inline Abc_Obj_t * Abc_ObjFanin1Ivy( Abc_Ntk_t * p, Ivy_Obj_t * pObj ) { return Abc_ObjNotCond( Abc_EdgeToNode(p, Ivy_ObjFanin1(pObj)->TravId), Ivy_ObjFaninC1(pObj) ); } + +static Vec_Int_t * Abc_NtkCollectLatchValuesIvy( Abc_Ntk_t * pNtk, int fUseDcs ); + +extern int timeRetime; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Prepares the IVY package.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Man_t * Abc_NtkIvyBefore( Abc_Ntk_t * pNtk, int fSeq, int fUseDc ) +{ + Ivy_Man_t * pMan; + int fCleanup = 1; +//timeRetime = clock(); + assert( !Abc_NtkIsNetlist(pNtk) ); + if ( Abc_NtkIsBddLogic(pNtk) ) + { + if ( !Abc_NtkBddToSop(pNtk, 0) ) + { + printf( "Abc_NtkIvyBefore(): Converting to SOPs has failed.\n" ); + return NULL; + } + } + if ( fSeq && Abc_NtkCountSelfFeedLatches(pNtk) ) + { + printf( "Warning: The network has %d self-feeding latches.\n", Abc_NtkCountSelfFeedLatches(pNtk) ); +// return NULL; + } + // print warning about choice nodes + if ( Abc_NtkGetChoiceNum( pNtk ) ) + printf( "Warning: The choice nodes in the initial AIG are removed by strashing.\n" ); + // convert to the AIG manager + pMan = Abc_NtkToIvy( pNtk ); + if ( !Ivy_ManCheck( pMan ) ) + { + printf( "AIG check has failed.\n" ); + Ivy_ManStop( pMan ); + return NULL; + } +// Ivy_ManPrintStats( pMan ); + if ( fSeq ) + { + int nLatches = Abc_NtkLatchNum(pNtk); + Vec_Int_t * vInit = Abc_NtkCollectLatchValuesIvy( pNtk, fUseDc ); + Ivy_ManMakeSeq( pMan, nLatches, vInit->pArray ); + Vec_IntFree( vInit ); +// Ivy_ManPrintStats( pMan ); + } +//timeRetime = clock() - timeRetime; + return pMan; +} + +/**Function************************************************************* + + Synopsis [Prepares the IVY package.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkIvyAfter( Abc_Ntk_t * pNtk, Ivy_Man_t * pMan, int fSeq, int fHaig ) +{ + Abc_Ntk_t * pNtkAig; + int nNodes, fCleanup = 1; + // convert from the AIG manager + if ( fSeq ) + pNtkAig = Abc_NtkFromIvySeq( pNtk, pMan, fHaig ); + else + pNtkAig = Abc_NtkFromIvy( pNtk, pMan ); + // report the cleanup results + if ( !fHaig && fCleanup && (nNodes = Abc_AigCleanup(pNtkAig->pManFunc)) ) + printf( "Warning: AIG cleanup removed %d nodes (this is not a bug).\n", nNodes ); + // duplicate EXDC + if ( pNtk->pExdc ) + pNtkAig->pExdc = Abc_NtkDup( pNtk->pExdc ); + // make sure everything is okay + if ( !Abc_NtkCheck( pNtkAig ) ) + { + printf( "Abc_NtkStrash: The network check has failed.\n" ); + Abc_NtkDelete( pNtkAig ); + return NULL; + } + return pNtkAig; +} + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkIvyStrash( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkAig; + Ivy_Man_t * pMan; + pMan = Abc_NtkIvyBefore( pNtk, 1, 0 ); + if ( pMan == NULL ) + return NULL; + pNtkAig = Abc_NtkIvyAfter( pNtk, pMan, 1, 0 ); + Ivy_ManStop( pMan ); + return pNtkAig; +} + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkIvyHaig( Abc_Ntk_t * pNtk, int nIters, int fUseZeroCost, int fVerbose ) +{ + Abc_Ntk_t * pNtkAig; + Ivy_Man_t * pMan; + int clk; +// int i; +/* +extern int nMoves; +extern int nMovesS; +extern int nClauses; +extern int timeInv; + +nMoves = 0; +nMovesS = 0; +nClauses = 0; +timeInv = 0; +*/ + pMan = Abc_NtkIvyBefore( pNtk, 1, 1 ); + if ( pMan == NULL ) + return NULL; +//timeRetime = clock(); + +clk = clock(); + Ivy_ManHaigStart( pMan, fVerbose ); +// Ivy_ManRewriteSeq( pMan, 0, 0 ); +// for ( i = 0; i < nIters; i++ ) +// Ivy_ManRewriteSeq( pMan, fUseZeroCost, 0 ); + +//printf( "%d ", Ivy_ManNodeNum(pMan) ); + Ivy_ManRewriteSeq( pMan, 0, 0 ); + Ivy_ManRewriteSeq( pMan, 0, 0 ); + Ivy_ManRewriteSeq( pMan, 1, 0 ); +//printf( "%d ", Ivy_ManNodeNum(pMan) ); +//printf( "%d ", Ivy_ManNodeNum(pMan->pHaig) ); +//PRT( " ", clock() - clk ); +//printf( "\n" ); +/* + printf( "Moves = %d. ", nMoves ); + printf( "MovesS = %d. ", nMovesS ); + printf( "Clauses = %d. ", nClauses ); + PRT( "Time", timeInv ); +*/ +// Ivy_ManRewriteSeq( pMan, 1, 0 ); +//printf( "Haig size = %d.\n", Ivy_ManNodeNum(pMan->pHaig) ); +// Ivy_ManHaigPostprocess( pMan, fVerbose ); +//timeRetime = clock() - timeRetime; + + // write working AIG into the current network +// pNtkAig = Abc_NtkIvyAfter( pNtk, pMan, 1, 0 ); + // write HAIG into the current network + pNtkAig = Abc_NtkIvyAfter( pNtk, pMan->pHaig, 1, 1 ); + + Ivy_ManHaigStop( pMan ); + Ivy_ManStop( pMan ); + return pNtkAig; +} + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkIvyCuts( Abc_Ntk_t * pNtk, int nInputs ) +{ + extern void Ivy_CutComputeAll( Ivy_Man_t * p, int nInputs ); + Ivy_Man_t * pMan; + pMan = Abc_NtkIvyBefore( pNtk, 1, 0 ); + if ( pMan == NULL ) + return; + Ivy_CutComputeAll( pMan, nInputs ); + Ivy_ManStop( pMan ); +} + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkIvyRewrite( Abc_Ntk_t * pNtk, int fUpdateLevel, int fUseZeroCost, int fVerbose ) +{ + Abc_Ntk_t * pNtkAig; + Ivy_Man_t * pMan; + pMan = Abc_NtkIvyBefore( pNtk, 0, 0 ); + if ( pMan == NULL ) + return NULL; +//timeRetime = clock(); + Ivy_ManRewritePre( pMan, fUpdateLevel, fUseZeroCost, fVerbose ); +//timeRetime = clock() - timeRetime; + pNtkAig = Abc_NtkIvyAfter( pNtk, pMan, 0, 0 ); + Ivy_ManStop( pMan ); + return pNtkAig; +} + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkIvyRewriteSeq( Abc_Ntk_t * pNtk, int fUseZeroCost, int fVerbose ) +{ + Abc_Ntk_t * pNtkAig; + Ivy_Man_t * pMan; + pMan = Abc_NtkIvyBefore( pNtk, 1, 1 ); + if ( pMan == NULL ) + return NULL; +//timeRetime = clock(); + Ivy_ManRewriteSeq( pMan, fUseZeroCost, fVerbose ); +//timeRetime = clock() - timeRetime; +// Ivy_ManRewriteSeq( pMan, 1, 0 ); +// Ivy_ManRewriteSeq( pMan, 1, 0 ); + pNtkAig = Abc_NtkIvyAfter( pNtk, pMan, 1, 0 ); + Ivy_ManStop( pMan ); + return pNtkAig; +} + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkIvyResyn0( Abc_Ntk_t * pNtk, int fUpdateLevel, int fVerbose ) +{ + Abc_Ntk_t * pNtkAig; + Ivy_Man_t * pMan, * pTemp; + pMan = Abc_NtkIvyBefore( pNtk, 0, 0 ); + if ( pMan == NULL ) + return NULL; + pMan = Ivy_ManResyn0( pTemp = pMan, fUpdateLevel, fVerbose ); + Ivy_ManStop( pTemp ); + pNtkAig = Abc_NtkIvyAfter( pNtk, pMan, 0, 0 ); + Ivy_ManStop( pMan ); + return pNtkAig; +} + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkIvyResyn( Abc_Ntk_t * pNtk, int fUpdateLevel, int fVerbose ) +{ + Abc_Ntk_t * pNtkAig; + Ivy_Man_t * pMan, * pTemp; + pMan = Abc_NtkIvyBefore( pNtk, 0, 0 ); + if ( pMan == NULL ) + return NULL; + pMan = Ivy_ManResyn( pTemp = pMan, fUpdateLevel, fVerbose ); + Ivy_ManStop( pTemp ); + pNtkAig = Abc_NtkIvyAfter( pNtk, pMan, 0, 0 ); + Ivy_ManStop( pMan ); + return pNtkAig; +} + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkIvySat( Abc_Ntk_t * pNtk, int nConfLimit, int fVerbose ) +{ + Ivy_FraigParams_t Params, * pParams = &Params; + Abc_Ntk_t * pNtkAig; + Ivy_Man_t * pMan, * pTemp; + pMan = Abc_NtkIvyBefore( pNtk, 0, 0 ); + if ( pMan == NULL ) + return NULL; + Ivy_FraigParamsDefault( pParams ); + pParams->nBTLimitMiter = nConfLimit; + pParams->fVerbose = fVerbose; +// pMan = Ivy_FraigPerform( pTemp = pMan, pParams ); + pMan = Ivy_FraigMiter( pTemp = pMan, pParams ); + Ivy_ManStop( pTemp ); + pNtkAig = Abc_NtkIvyAfter( pNtk, pMan, 0, 0 ); + Ivy_ManStop( pMan ); + return pNtkAig; +} + +/**Function************************************************************* + + Synopsis [Sets the final nodes to point to the original nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkTransferPointers( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkAig ) +{ + Abc_Obj_t * pObj; + Ivy_Obj_t * pObjIvy, * pObjFraig; + int i; + pObj = Abc_AigConst1(pNtk); + pObj->pCopy = Abc_AigConst1(pNtkAig); + Abc_NtkForEachCi( pNtk, pObj, i ) + pObj->pCopy = Abc_NtkCi(pNtkAig, i); + Abc_NtkForEachCo( pNtk, pObj, i ) + pObj->pCopy = Abc_NtkCo(pNtkAig, i); + Abc_NtkForEachLatch( pNtk, pObj, i ) + pObj->pCopy = Abc_NtkBox(pNtkAig, i); + Abc_NtkForEachNode( pNtk, pObj, i ) + { + pObjIvy = (Ivy_Obj_t *)pObj->pCopy; + if ( pObjIvy == NULL ) + continue; + pObjFraig = Ivy_ObjEquiv( pObjIvy ); + if ( pObjFraig == NULL ) + continue; + pObj->pCopy = Abc_EdgeToNode( pNtkAig, Ivy_Regular(pObjFraig)->TravId ); + pObj->pCopy = Abc_ObjNotCond( pObj->pCopy, Ivy_IsComplement(pObjFraig) ); + } +} + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkIvyFraig( Abc_Ntk_t * pNtk, int nConfLimit, int fDoSparse, int fProve, int fTransfer, int fVerbose ) +{ + Ivy_FraigParams_t Params, * pParams = &Params; + Abc_Ntk_t * pNtkAig; + Ivy_Man_t * pMan, * pTemp; + pMan = Abc_NtkIvyBefore( pNtk, 0, 0 ); + if ( pMan == NULL ) + return NULL; + Ivy_FraigParamsDefault( pParams ); + pParams->nBTLimitNode = nConfLimit; + pParams->fVerbose = fVerbose; + pParams->fProve = fProve; + pParams->fDoSparse = fDoSparse; + pMan = Ivy_FraigPerform( pTemp = pMan, pParams ); + // transfer the pointers + if ( fTransfer == 1 ) + { + Vec_Ptr_t * vCopies; + vCopies = Abc_NtkSaveCopy( pNtk ); + pNtkAig = Abc_NtkIvyAfter( pNtk, pMan, 0, 0 ); + Abc_NtkLoadCopy( pNtk, vCopies ); + Vec_PtrFree( vCopies ); + Abc_NtkTransferPointers( pNtk, pNtkAig ); + } + else + pNtkAig = Abc_NtkIvyAfter( pNtk, pMan, 0, 0 ); + Ivy_ManStop( pTemp ); + Ivy_ManStop( pMan ); + return pNtkAig; +} + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkIvyProve( Abc_Ntk_t ** ppNtk, void * pPars ) +{ + Prove_Params_t * pParams = pPars; + Abc_Ntk_t * pNtk = *ppNtk, * pNtkTemp; + Abc_Obj_t * pObj, * pFanin; + Ivy_Man_t * pMan; + int RetValue; + assert( Abc_NtkIsStrash(pNtk) || Abc_NtkIsLogic(pNtk) ); + // experiment with various parameters settings +// pParams->fUseBdds = 1; +// pParams->fBddReorder = 1; +// pParams->nTotalBacktrackLimit = 10000; + + // strash the network if it is not strashed already + if ( !Abc_NtkIsStrash(pNtk) ) + { + pNtk = Abc_NtkStrash( pNtkTemp = pNtk, 0, 1, 0 ); + Abc_NtkDelete( pNtkTemp ); + } + + // check the case when the 0000 simulation pattern detect the bug + pObj = Abc_NtkPo(pNtk,0); + pFanin = Abc_ObjFanin0(pObj); + if ( Abc_ObjFanin0(pObj)->fPhase != (unsigned)Abc_ObjFaninC0(pObj) ) + { + pNtk->pModel = ALLOC( int, Abc_NtkPiNum(pNtk) ); + memset( pNtk->pModel, 0, sizeof(int) * Abc_NtkPiNum(pNtk) ); + return 0; + } + + // if SAT only, solve without iteration + RetValue = Abc_NtkMiterSat( pNtk, 2*(sint64)pParams->nMiteringLimitStart, (sint64)0, 0, NULL, NULL ); + if ( RetValue >= 0 ) + return RetValue; + + // apply AIG rewriting + if ( pParams->fUseRewriting && Abc_NtkNodeNum(pNtk) > 500 ) + { + pParams->fUseRewriting = 0; + pNtk = Abc_NtkBalance( pNtkTemp = pNtk, 0, 0, 0 ); + Abc_NtkDelete( pNtkTemp ); + Abc_NtkRewrite( pNtk, 0, 0, 0, 0, 0 ); + pNtk = Abc_NtkBalance( pNtkTemp = pNtk, 0, 0, 0 ); + Abc_NtkDelete( pNtkTemp ); + Abc_NtkRewrite( pNtk, 0, 0, 0, 0, 0 ); + Abc_NtkRefactor( pNtk, 10, 16, 0, 0, 0, 0 ); + } + + // convert ABC network into IVY network + pMan = Abc_NtkIvyBefore( pNtk, 0, 0 ); + + // solve the CEC problem + RetValue = Ivy_FraigProve( &pMan, pParams ); + // convert IVY network into ABC network + pNtk = Abc_NtkIvyAfter( pNtkTemp = pNtk, pMan, 0, 0 ); + Abc_NtkDelete( pNtkTemp ); + // transfer model if given + pNtk->pModel = pMan->pData; pMan->pData = NULL; + Ivy_ManStop( pMan ); + + // try to prove it using brute force SAT + if ( RetValue < 0 && pParams->fUseBdds ) + { + if ( pParams->fVerbose ) + { + printf( "Attempting BDDs with node limit %d ...\n", pParams->nBddSizeLimit ); + fflush( stdout ); + } + pNtk = Abc_NtkCollapse( pNtkTemp = pNtk, pParams->nBddSizeLimit, 0, pParams->fBddReorder, 0 ); + if ( pNtk ) + { + Abc_NtkDelete( pNtkTemp ); + RetValue = ( (Abc_NtkNodeNum(pNtk) == 1) && (Abc_ObjFanin0(Abc_NtkPo(pNtk,0))->pData == Cudd_ReadLogicZero(pNtk->pManFunc)) ); + } + else + pNtk = pNtkTemp; + } + + // return the result + *ppNtk = pNtk; + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkIvy( Abc_Ntk_t * pNtk ) +{ +// Abc_Ntk_t * pNtkAig; + Ivy_Man_t * pMan;//, * pTemp; + int fCleanup = 1; +// int nNodes; + int nLatches = Abc_NtkLatchNum(pNtk); + Vec_Int_t * vInit = Abc_NtkCollectLatchValuesIvy( pNtk, 0 ); + + assert( !Abc_NtkIsNetlist(pNtk) ); + if ( Abc_NtkIsBddLogic(pNtk) ) + { + if ( !Abc_NtkBddToSop(pNtk, 0) ) + { + Vec_IntFree( vInit ); + printf( "Abc_NtkIvy(): Converting to SOPs has failed.\n" ); + return NULL; + } + } + if ( Abc_NtkCountSelfFeedLatches(pNtk) ) + { + printf( "Warning: The network has %d self-feeding latches. Quitting.\n", Abc_NtkCountSelfFeedLatches(pNtk) ); + return NULL; + } + + // print warning about choice nodes + if ( Abc_NtkGetChoiceNum( pNtk ) ) + printf( "Warning: The choice nodes in the initial AIG are removed by strashing.\n" ); + + // convert to the AIG manager + pMan = Abc_NtkToIvy( pNtk ); + if ( !Ivy_ManCheck( pMan ) ) + { + Vec_IntFree( vInit ); + printf( "AIG check has failed.\n" ); + Ivy_ManStop( pMan ); + return NULL; + } + +// Ivy_MffcTest( pMan ); +// Ivy_ManPrintStats( pMan ); + +// pMan = Ivy_ManBalance( pTemp = pMan, 1 ); +// Ivy_ManStop( pTemp ); + +// Ivy_ManSeqRewrite( pMan, 0, 0 ); +// Ivy_ManTestCutsAlg( pMan ); +// Ivy_ManTestCutsBool( pMan ); +// Ivy_ManRewriteAlg( pMan, 1, 1 ); + +// pMan = Ivy_ManResyn( pTemp = pMan, 1, 0 ); +// Ivy_ManStop( pTemp ); + +// Ivy_ManTestCutsAll( pMan ); +// Ivy_ManTestCutsTravAll( pMan ); + +// Ivy_ManPrintStats( pMan ); + +// Ivy_ManPrintStats( pMan ); +// Ivy_ManRewritePre( pMan, 1, 0, 0 ); +// Ivy_ManPrintStats( pMan ); +// printf( "\n" ); + +// Ivy_ManPrintStats( pMan ); +// Ivy_ManMakeSeq( pMan, nLatches, pInit ); +// Ivy_ManPrintStats( pMan ); + +// Ivy_ManRequiredLevels( pMan ); + +// Ivy_FastMapPerform( pMan, 8 ); + Ivy_ManStop( pMan ); + return NULL; + + +/* + // convert from the AIG manager + pNtkAig = Abc_NtkFromIvy( pNtk, pMan ); +// pNtkAig = Abc_NtkFromIvySeq( pNtk, pMan ); + Ivy_ManStop( pMan ); + + // report the cleanup results + if ( fCleanup && (nNodes = Abc_AigCleanup(pNtkAig->pManFunc)) ) + printf( "Warning: AIG cleanup removed %d nodes (this is not a bug).\n", nNodes ); + // duplicate EXDC + if ( pNtk->pExdc ) + pNtkAig->pExdc = Abc_NtkDup( pNtk->pExdc ); + // make sure everything is okay + if ( !Abc_NtkCheck( pNtkAig ) ) + { + FREE( pInit ); + printf( "Abc_NtkStrash: The network check has failed.\n" ); + Abc_NtkDelete( pNtkAig ); + return NULL; + } + + FREE( pInit ); + return pNtkAig; +*/ +} + + + +/**Function************************************************************* + + Synopsis [Converts the network from the AIG manager into ABC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFromIvy( Abc_Ntk_t * pNtkOld, Ivy_Man_t * pMan ) +{ + Vec_Int_t * vNodes; + Abc_Ntk_t * pNtk; + Abc_Obj_t * pObj, * pObjNew, * pFaninNew, * pFaninNew0, * pFaninNew1; + Ivy_Obj_t * pNode; + int i; + // perform strashing + pNtk = Abc_NtkStartFrom( pNtkOld, ABC_NTK_STRASH, ABC_FUNC_AIG ); + // transfer the pointers to the basic nodes + Ivy_ManConst1(pMan)->TravId = Abc_EdgeFromNode( Abc_AigConst1(pNtk) ); + Abc_NtkForEachCi( pNtkOld, pObj, i ) + Ivy_ManPi(pMan, i)->TravId = Abc_EdgeFromNode( pObj->pCopy ); + // rebuild the AIG + vNodes = Ivy_ManDfs( pMan ); + Ivy_ManForEachNodeVec( pMan, vNodes, pNode, i ) + { + // add the first fanin + pFaninNew0 = Abc_ObjFanin0Ivy( pNtk, pNode ); + if ( Ivy_ObjIsBuf(pNode) ) + { + pNode->TravId = Abc_EdgeFromNode( pFaninNew0 ); + continue; + } + // add the second fanin + pFaninNew1 = Abc_ObjFanin1Ivy( pNtk, pNode ); + // create the new node + if ( Ivy_ObjIsExor(pNode) ) + pObjNew = Abc_AigXor( pNtk->pManFunc, pFaninNew0, pFaninNew1 ); + else + pObjNew = Abc_AigAnd( pNtk->pManFunc, pFaninNew0, pFaninNew1 ); + pNode->TravId = Abc_EdgeFromNode( pObjNew ); + } + // connect the PO nodes + Abc_NtkForEachCo( pNtkOld, pObj, i ) + { + pFaninNew = Abc_ObjFanin0Ivy( pNtk, Ivy_ManPo(pMan, i) ); + Abc_ObjAddFanin( pObj->pCopy, pFaninNew ); + } + Vec_IntFree( vNodes ); + if ( !Abc_NtkCheck( pNtk ) ) + fprintf( stdout, "Abc_NtkFromIvy(): Network check has failed.\n" ); + return pNtk; +} + +/**Function************************************************************* + + Synopsis [Converts the network from the AIG manager into ABC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFromIvySeq( Abc_Ntk_t * pNtkOld, Ivy_Man_t * pMan, int fHaig ) +{ + Vec_Int_t * vNodes, * vLatches; + Abc_Ntk_t * pNtk; + Abc_Obj_t * pObj, * pObjNew, * pFaninNew, * pFaninNew0, * pFaninNew1; + Ivy_Obj_t * pNode, * pTemp; + int i; +// assert( Ivy_ManLatchNum(pMan) > 0 ); + // perform strashing + pNtk = Abc_NtkStartFromNoLatches( pNtkOld, ABC_NTK_STRASH, ABC_FUNC_AIG ); + // transfer the pointers to the basic nodes + Ivy_ManConst1(pMan)->TravId = Abc_EdgeFromNode( Abc_AigConst1(pNtk) ); + Abc_NtkForEachPi( pNtkOld, pObj, i ) + Ivy_ManPi(pMan, i)->TravId = Abc_EdgeFromNode( pObj->pCopy ); + // create latches of the new network + vNodes = Ivy_ManDfsSeq( pMan, &vLatches ); + Ivy_ManForEachNodeVec( pMan, vLatches, pNode, i ) + { + pObjNew = Abc_NtkCreateLatch( pNtk ); + pFaninNew0 = Abc_NtkCreateBi( pNtk ); + pFaninNew1 = Abc_NtkCreateBo( pNtk ); + Abc_ObjAddFanin( pObjNew, pFaninNew0 ); + Abc_ObjAddFanin( pFaninNew1, pObjNew ); + if ( fHaig || Ivy_ObjInit(pNode) == IVY_INIT_DC ) + Abc_LatchSetInitDc( pObjNew ); + else if ( Ivy_ObjInit(pNode) == IVY_INIT_1 ) + Abc_LatchSetInit1( pObjNew ); + else if ( Ivy_ObjInit(pNode) == IVY_INIT_0 ) + Abc_LatchSetInit0( pObjNew ); + else assert( 0 ); + pNode->TravId = Abc_EdgeFromNode( pFaninNew1 ); + } + Abc_NtkAddDummyBoxNames( pNtk ); + // rebuild the AIG + Ivy_ManForEachNodeVec( pMan, vNodes, pNode, i ) + { + // add the first fanin + pFaninNew0 = Abc_ObjFanin0Ivy( pNtk, pNode ); + if ( Ivy_ObjIsBuf(pNode) ) + { + pNode->TravId = Abc_EdgeFromNode( pFaninNew0 ); + continue; + } + // add the second fanin + pFaninNew1 = Abc_ObjFanin1Ivy( pNtk, pNode ); + // create the new node + if ( Ivy_ObjIsExor(pNode) ) + pObjNew = Abc_AigXor( pNtk->pManFunc, pFaninNew0, pFaninNew1 ); + else + pObjNew = Abc_AigAnd( pNtk->pManFunc, pFaninNew0, pFaninNew1 ); + pNode->TravId = Abc_EdgeFromNode( pObjNew ); + // process the choice nodes + if ( fHaig && pNode->pEquiv && Ivy_ObjRefs(pNode) > 0 ) + { + pFaninNew = Abc_EdgeToNode( pNtk, pNode->TravId ); +// pFaninNew->fPhase = 0; + assert( !Ivy_IsComplement(pNode->pEquiv) ); + for ( pTemp = pNode->pEquiv; pTemp != pNode; pTemp = Ivy_Regular(pTemp->pEquiv) ) + { + pFaninNew1 = Abc_EdgeToNode( pNtk, pTemp->TravId ); +// pFaninNew1->fPhase = Ivy_IsComplement( pTemp->pEquiv ); + pFaninNew->pData = pFaninNew1; + pFaninNew = pFaninNew1; + } + pFaninNew->pData = NULL; +// printf( "Writing choice node %d.\n", pNode->Id ); + } + } + // connect the PO nodes + Abc_NtkForEachPo( pNtkOld, pObj, i ) + { + pFaninNew = Abc_ObjFanin0Ivy( pNtk, Ivy_ManPo(pMan, i) ); + Abc_ObjAddFanin( pObj->pCopy, pFaninNew ); + } + // connect the latches + Ivy_ManForEachNodeVec( pMan, vLatches, pNode, i ) + { + pFaninNew = Abc_ObjFanin0Ivy( pNtk, pNode ); + Abc_ObjAddFanin( Abc_ObjFanin0(Abc_NtkBox(pNtk, i)), pFaninNew ); + } + Vec_IntFree( vLatches ); + Vec_IntFree( vNodes ); + if ( !Abc_NtkCheck( pNtk ) ) + fprintf( stdout, "Abc_NtkFromIvySeq(): Network check has failed.\n" ); + return pNtk; +} + +/**Function************************************************************* + + Synopsis [Converts the network from the AIG manager into ABC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Man_t * Abc_NtkToIvy( Abc_Ntk_t * pNtkOld ) +{ + Ivy_Man_t * pMan; + Abc_Obj_t * pObj; + Ivy_Obj_t * pFanin; + int i; + // create the manager + assert( Abc_NtkHasSop(pNtkOld) || Abc_NtkIsStrash(pNtkOld) ); + pMan = Ivy_ManStart(); + // create the PIs + if ( Abc_NtkIsStrash(pNtkOld) ) + Abc_AigConst1(pNtkOld)->pCopy = (Abc_Obj_t *)Ivy_ManConst1(pMan); + Abc_NtkForEachCi( pNtkOld, pObj, i ) + pObj->pCopy = (Abc_Obj_t *)Ivy_ObjCreatePi(pMan); + // perform the conversion of the internal nodes + Abc_NtkStrashPerformAig( pNtkOld, pMan ); + // create the POs + Abc_NtkForEachCo( pNtkOld, pObj, i ) + { + pFanin = (Ivy_Obj_t *)Abc_ObjFanin0(pObj)->pCopy; + pFanin = Ivy_NotCond( pFanin, Abc_ObjFaninC0(pObj) ); + Ivy_ObjCreatePo( pMan, pFanin ); + } + Ivy_ManCleanup( pMan ); + return pMan; +} + +/**Function************************************************************* + + Synopsis [Prepares the network for strashing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkStrashPerformAig( Abc_Ntk_t * pNtk, Ivy_Man_t * pMan ) +{ +// ProgressBar * pProgress; + Vec_Ptr_t * vNodes; + Abc_Obj_t * pNode; + int i; + vNodes = Abc_NtkDfs( pNtk, 0 ); +// pProgress = Extra_ProgressBarStart( stdout, vNodes->nSize ); + Vec_PtrForEachEntry( vNodes, pNode, i ) + { +// Extra_ProgressBarUpdate( pProgress, i, NULL ); + pNode->pCopy = (Abc_Obj_t *)Abc_NodeStrashAig( pMan, pNode ); + } +// Extra_ProgressBarStop( pProgress ); + Vec_PtrFree( vNodes ); +} + +/**Function************************************************************* + + Synopsis [Strashes one logic node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Abc_NodeStrashAig( Ivy_Man_t * pMan, Abc_Obj_t * pNode ) +{ + int fUseFactor = 1; + char * pSop; + Ivy_Obj_t * pFanin0, * pFanin1; + + assert( Abc_ObjIsNode(pNode) ); + + // consider the case when the graph is an AIG + if ( Abc_NtkIsStrash(pNode->pNtk) ) + { + if ( Abc_AigNodeIsConst(pNode) ) + return Ivy_ManConst1(pMan); + pFanin0 = (Ivy_Obj_t *)Abc_ObjFanin0(pNode)->pCopy; + pFanin0 = Ivy_NotCond( pFanin0, Abc_ObjFaninC0(pNode) ); + pFanin1 = (Ivy_Obj_t *)Abc_ObjFanin1(pNode)->pCopy; + pFanin1 = Ivy_NotCond( pFanin1, Abc_ObjFaninC1(pNode) ); + return Ivy_And( pMan, pFanin0, pFanin1 ); + } + + // get the SOP of the node + if ( Abc_NtkHasMapping(pNode->pNtk) ) + pSop = Mio_GateReadSop(pNode->pData); + else + pSop = pNode->pData; + + // consider the constant node + if ( Abc_NodeIsConst(pNode) ) + return Ivy_NotCond( Ivy_ManConst1(pMan), Abc_SopIsConst0(pSop) ); + + // consider the special case of EXOR function + if ( Abc_SopIsExorType(pSop) ) + return Abc_NodeStrashAigExorAig( pMan, pNode, pSop ); + + // decide when to use factoring + if ( fUseFactor && Abc_ObjFaninNum(pNode) > 2 && Abc_SopGetCubeNum(pSop) > 1 ) + return Abc_NodeStrashAigFactorAig( pMan, pNode, pSop ); + return Abc_NodeStrashAigSopAig( pMan, pNode, pSop ); +} + +/**Function************************************************************* + + Synopsis [Strashes one logic node using its SOP.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Abc_NodeStrashAigSopAig( Ivy_Man_t * pMan, Abc_Obj_t * pNode, char * pSop ) +{ + Abc_Obj_t * pFanin; + Ivy_Obj_t * pAnd, * pSum; + char * pCube; + int i, nFanins; + + // get the number of node's fanins + nFanins = Abc_ObjFaninNum( pNode ); + assert( nFanins == Abc_SopGetVarNum(pSop) ); + // go through the cubes of the node's SOP + pSum = Ivy_Not( Ivy_ManConst1(pMan) ); + Abc_SopForEachCube( pSop, nFanins, pCube ) + { + // create the AND of literals + pAnd = Ivy_ManConst1(pMan); + Abc_ObjForEachFanin( pNode, pFanin, i ) // pFanin can be a net + { + if ( pCube[i] == '1' ) + pAnd = Ivy_And( pMan, pAnd, (Ivy_Obj_t *)pFanin->pCopy ); + else if ( pCube[i] == '0' ) + pAnd = Ivy_And( pMan, pAnd, Ivy_Not((Ivy_Obj_t *)pFanin->pCopy) ); + } + // add to the sum of cubes + pSum = Ivy_Or( pMan, pSum, pAnd ); + } + // decide whether to complement the result + if ( Abc_SopIsComplement(pSop) ) + pSum = Ivy_Not(pSum); + return pSum; +} + +/**Function************************************************************* + + Synopsis [Strashed n-input XOR function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Abc_NodeStrashAigExorAig( Ivy_Man_t * pMan, Abc_Obj_t * pNode, char * pSop ) +{ + Abc_Obj_t * pFanin; + Ivy_Obj_t * pSum; + int i, nFanins; + // get the number of node's fanins + nFanins = Abc_ObjFaninNum( pNode ); + assert( nFanins == Abc_SopGetVarNum(pSop) ); + // go through the cubes of the node's SOP + pSum = Ivy_Not( Ivy_ManConst1(pMan) ); + for ( i = 0; i < nFanins; i++ ) + { + pFanin = Abc_ObjFanin( pNode, i ); + pSum = Ivy_Exor( pMan, pSum, (Ivy_Obj_t *)pFanin->pCopy ); + } + if ( Abc_SopIsComplement(pSop) ) + pSum = Ivy_Not(pSum); + return pSum; +} + +/**Function************************************************************* + + Synopsis [Strashes one logic node using its SOP.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Abc_NodeStrashAigFactorAig( Ivy_Man_t * pMan, Abc_Obj_t * pRoot, char * pSop ) +{ + Dec_Graph_t * pFForm; + Dec_Node_t * pNode; + Ivy_Obj_t * pAnd; + int i; + +// extern Ivy_Obj_t * Dec_GraphToNetworkAig( Ivy_Man_t * pMan, Dec_Graph_t * pGraph ); + extern Ivy_Obj_t * Dec_GraphToNetworkIvy( Ivy_Man_t * pMan, Dec_Graph_t * pGraph ); + +// assert( 0 ); + + // perform factoring + pFForm = Dec_Factor( pSop ); + // collect the fanins + Dec_GraphForEachLeaf( pFForm, pNode, i ) + pNode->pFunc = Abc_ObjFanin(pRoot,i)->pCopy; + // perform strashing +// pAnd = Dec_GraphToNetworkAig( pMan, pFForm ); + pAnd = Dec_GraphToNetworkIvy( pMan, pFForm ); +// pAnd = NULL; + + Dec_GraphFree( pFForm ); + return pAnd; +} + +/**Function************************************************************* + + Synopsis [Strashes one logic node using its SOP.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Abc_NtkCollectLatchValuesIvy( Abc_Ntk_t * pNtk, int fUseDcs ) +{ + Abc_Obj_t * pLatch; + Vec_Int_t * vArray; + int i; + vArray = Vec_IntAlloc( Abc_NtkLatchNum(pNtk) ); + Abc_NtkForEachLatch( pNtk, pLatch, i ) + { + if ( fUseDcs || Abc_LatchIsInitDc(pLatch) ) + Vec_IntPush( vArray, IVY_INIT_DC ); + else if ( Abc_LatchIsInit1(pLatch) ) + Vec_IntPush( vArray, IVY_INIT_1 ); + else if ( Abc_LatchIsInit0(pLatch) ) + Vec_IntPush( vArray, IVY_INIT_0 ); + else assert( 0 ); + } + return vArray; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcLut.c b/abc_with_bb_support/src/base/abci/abcLut.c new file mode 100644 index 000000000..6aae5749a --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcLut.c @@ -0,0 +1,786 @@ +/**CFile**************************************************************** + + FileName [abcLut.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Superchoicing for K-LUTs.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcLut.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "cut.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define SCL_LUT_MAX 6 // the maximum LUT size +#define SCL_VARS_MAX 15 // the maximum number of variables +#define SCL_NODE_MAX 1000 // the maximum number of nodes + +typedef struct Abc_ManScl_t_ Abc_ManScl_t; +struct Abc_ManScl_t_ +{ + // paramers + int nLutSize; // the LUT size + int nCutSizeMax; // the max number of leaves of the cone + int nNodesMax; // the max number of divisors in the cone + int nWords; // the number of machine words in sim info + // structural representation of the cone + Vec_Ptr_t * vLeaves; // leaves of the cut + Vec_Ptr_t * vVolume; // volume of the cut + int pBSet[SCL_VARS_MAX]; // bound set + // functional representation of the cone + unsigned * uTruth; // truth table of the cone + // representation of truth tables + unsigned ** uVars; // elementary truth tables + unsigned ** uSims; // truth tables of the nodes + unsigned ** uCofs; // truth tables of the cofactors +}; + +static Vec_Ptr_t * s_pLeaves = NULL; + +static Cut_Man_t * Abc_NtkStartCutManForScl( Abc_Ntk_t * pNtk, int nLutSize ); +static Abc_ManScl_t * Abc_ManSclStart( int nLutSize, int nCutSizeMax, int nNodesMax ); +static void Abc_ManSclStop( Abc_ManScl_t * p ); +static void Abc_NodeLutMap( Cut_Man_t * pManCuts, Abc_Obj_t * pObj ); + +static Abc_Obj_t * Abc_NodeSuperChoiceLut( Abc_ManScl_t * pManScl, Abc_Obj_t * pObj ); +static int Abc_NodeDecomposeStep( Abc_ManScl_t * pManScl ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs superchoicing for K-LUTs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkSuperChoiceLut( Abc_Ntk_t * pNtk, int nLutSize, int nCutSizeMax, int fVerbose ) +{ + ProgressBar * pProgress; + Abc_ManCut_t * pManCut; + Abc_ManScl_t * pManScl; + Cut_Man_t * pManCuts; + Abc_Obj_t * pObj, * pFanin, * pObjTop; + int i, LevelMax, nNodes; + int nNodesTried, nNodesDec, nNodesExist, nNodesUsed; + + assert( Abc_NtkIsSopLogic(pNtk) ); + if ( nLutSize < 3 || nLutSize > SCL_LUT_MAX ) + { + printf( "LUT size (%d) does not belong to the interval: 3 <= LUT size <= %d\n", nLutSize, SCL_LUT_MAX ); + return 0; + } + if ( nCutSizeMax <= nLutSize || nCutSizeMax > SCL_VARS_MAX ) + { + printf( "Cut size (%d) does not belong to the interval: LUT size (%d) < Cut size <= %d\n", nCutSizeMax, nLutSize, SCL_VARS_MAX ); + return 0; + } + + assert( nLutSize <= SCL_LUT_MAX ); + assert( nCutSizeMax <= SCL_VARS_MAX ); + nNodesTried = nNodesDec = nNodesExist = nNodesUsed = 0; + + // set the delays of the CIs + Abc_NtkForEachCi( pNtk, pObj, i ) + pObj->Level = 0; + +//Abc_NtkLevel( pNtk ); + + // start the managers + pManScl = Abc_ManSclStart( nLutSize, nCutSizeMax, 1000 ); + pManCuts = Abc_NtkStartCutManForScl( pNtk, nLutSize ); + pManCut = Abc_NtkManCutStart( nCutSizeMax, 100000, 100000, 100000 ); + s_pLeaves = Abc_NtkManCutReadCutSmall( pManCut ); + pManScl->vVolume = Abc_NtkManCutReadVisited( pManCut ); + + // process each internal node (assuming topological order of nodes!!!) + nNodes = Abc_NtkObjNumMax(pNtk); + pProgress = Extra_ProgressBarStart( stdout, nNodes ); + Abc_NtkForEachObj( pNtk, pObj, i ) + { +// if ( i != nNodes-1 ) +// continue; + Extra_ProgressBarUpdate( pProgress, i, NULL ); + if ( i >= nNodes ) + break; + if ( Abc_ObjFaninNum(pObj) != 2 ) + continue; + nNodesTried++; + + // map this node using regular cuts +// pObj->Level = 0; + Abc_NodeLutMap( pManCuts, pObj ); + // compute the cut + pManScl->vLeaves = Abc_NodeFindCut( pManCut, pObj, 0 ); + if ( Vec_PtrSize(pManScl->vLeaves) <= nLutSize ) + continue; + // get the volume of the cut + if ( Vec_PtrSize(pManScl->vVolume) > SCL_NODE_MAX ) + continue; + nNodesDec++; + + // decompose the cut + pObjTop = Abc_NodeSuperChoiceLut( pManScl, pObj ); + if ( pObjTop == NULL ) + continue; + nNodesExist++; + + // if there is no delay improvement, skip; otherwise, update level + if ( pObjTop->Level >= pObj->Level ) + { + Abc_NtkDeleteObj_rec( pObjTop, 1 ); + continue; + } + pObj->Level = pObjTop->Level; + nNodesUsed++; + } + Extra_ProgressBarStop( pProgress ); + + // delete the managers + Abc_ManSclStop( pManScl ); + Abc_NtkManCutStop( pManCut ); + Cut_ManStop( pManCuts ); + + // get the largest arrival time + LevelMax = 0; + Abc_NtkForEachCo( pNtk, pObj, i ) + { + pFanin = Abc_ObjFanin0( pObj ); + // skip inv/buf + if ( Abc_ObjFaninNum(pFanin) == 1 ) + pFanin = Abc_ObjFanin0( pFanin ); + // get the new level + LevelMax = ABC_MAX( LevelMax, (int)pFanin->Level ); + } + + if ( fVerbose ) + printf( "Try = %d. Dec = %d. Exist = %d. Use = %d. SUPER = %d levels of %d-LUTs.\n", + nNodesTried, nNodesDec, nNodesExist, nNodesUsed, LevelMax, nLutSize ); +// if ( fVerbose ) +// printf( "The network is superchoiced for %d levels of %d-LUTs.\n", LevelMax, nLutSize ); + + // clean the data field + Abc_NtkForEachObj( pNtk, pObj, i ) + pObj->pNext = NULL; + + // check + if ( !Abc_NtkCheck( pNtk ) ) + { + printf( "Abc_NtkSuperChoiceLut: The network check has failed.\n" ); + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Performs LUT mapping of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeLutMap( Cut_Man_t * pManCuts, Abc_Obj_t * pObj ) +{ + Cut_Cut_t * pCut; + Abc_Obj_t * pFanin; + int i, DelayMax; + pCut = (Cut_Cut_t *)Abc_NodeGetCutsRecursive( pManCuts, pObj, 0, 0 ); + assert( pCut != NULL ); + assert( pObj->Level == 0 ); + // go through the cuts + pObj->Level = ABC_INFINITY; + for ( pCut = pCut->pNext; pCut; pCut = pCut->pNext ) + { + DelayMax = 0; + for ( i = 0; i < (int)pCut->nLeaves; i++ ) + { + pFanin = Abc_NtkObj( pObj->pNtk, pCut->pLeaves[i] ); +// assert( Abc_ObjIsCi(pFanin) || pFanin->Level > 0 ); // should hold if node ordering is topological + if ( DelayMax < (int)pFanin->Level ) + DelayMax = pFanin->Level; + } + if ( (int)pObj->Level > DelayMax ) + pObj->Level = DelayMax; + } + assert( pObj->Level < ABC_INFINITY ); + pObj->Level++; +// printf( "%d(%d) ", pObj->Id, pObj->Level ); +} + +/**Function************************************************************* + + Synopsis [Starts the cut manager for rewriting.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Man_t * Abc_NtkStartCutManForScl( Abc_Ntk_t * pNtk, int nLutSize ) +{ + static Cut_Params_t Params, * pParams = &Params; + Cut_Man_t * pManCut; + Abc_Obj_t * pObj; + int i; + // start the cut manager + memset( pParams, 0, sizeof(Cut_Params_t) ); + pParams->nVarsMax = nLutSize; // the max cut size ("k" of the k-feasible cuts) + pParams->nKeepMax = 500; // the max number of cuts kept at a node + pParams->fTruth = 0; // compute truth tables + pParams->fFilter = 1; // filter dominated cuts + pParams->fSeq = 0; // compute sequential cuts + pParams->fDrop = 0; // drop cuts on the fly + pParams->fVerbose = 0; // the verbosiness flag + pParams->nIdsMax = Abc_NtkObjNumMax( pNtk ); + pManCut = Cut_ManStart( pParams ); + if ( pParams->fDrop ) + Cut_ManSetFanoutCounts( pManCut, Abc_NtkFanoutCounts(pNtk) ); + // set cuts for PIs + Abc_NtkForEachCi( pNtk, pObj, i ) + if ( Abc_ObjFanoutNum(pObj) > 0 ) + Cut_NodeSetTriv( pManCut, pObj->Id ); + return pManCut; +} + +/**Function************************************************************* + + Synopsis [Starts the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_ManScl_t * Abc_ManSclStart( int nLutSize, int nCutSizeMax, int nNodesMax ) +{ + Abc_ManScl_t * p; + int i, k; + assert( sizeof(unsigned) == 4 ); + p = ALLOC( Abc_ManScl_t, 1 ); + memset( p, 0, sizeof(Abc_ManScl_t) ); + p->nLutSize = nLutSize; + p->nCutSizeMax = nCutSizeMax; + p->nNodesMax = nNodesMax; + p->nWords = Extra_TruthWordNum(nCutSizeMax); + // allocate simulation info + p->uVars = (unsigned **)Extra_ArrayAlloc( nCutSizeMax, p->nWords, 4 ); + p->uSims = (unsigned **)Extra_ArrayAlloc( nNodesMax, p->nWords, 4 ); + p->uCofs = (unsigned **)Extra_ArrayAlloc( 2 << nLutSize, p->nWords, 4 ); + memset( p->uVars[0], 0, nCutSizeMax * p->nWords * 4 ); + // assign elementary truth tables + for ( k = 0; k < p->nCutSizeMax; k++ ) + for ( i = 0; i < p->nWords * 32; i++ ) + if ( i & (1 << k) ) + p->uVars[k][i>>5] |= (1 << (i&31)); + // other data structures +// p->vBound = Vec_IntAlloc( nCutSizeMax ); + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ManSclStop( Abc_ManScl_t * p ) +{ +// Vec_IntFree( p->vBound ); + free( p->uVars ); + free( p->uSims ); + free( p->uCofs ); + free( p ); +} + + +/**Function************************************************************* + + Synopsis [Performs superchoicing for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned * Abc_NodeSuperChoiceTruth( Abc_ManScl_t * pManScl ) +{ + Abc_Obj_t * pObj; + unsigned * puData0, * puData1, * puData; + char * pSop; + int i, k; + // set elementary truth tables + Vec_PtrForEachEntry( pManScl->vLeaves, pObj, i ) + pObj->pNext = (Abc_Obj_t *)pManScl->uVars[i]; + // compute truth tables for internal nodes + Vec_PtrForEachEntry( pManScl->vVolume, pObj, i ) + { + // set storage for the node's simulation info + pObj->pNext = (Abc_Obj_t *)pManScl->uSims[i]; + // get pointer to the simulation info + puData = (unsigned *)pObj->pNext; + puData0 = (unsigned *)Abc_ObjFanin0(pObj)->pNext; + puData1 = (unsigned *)Abc_ObjFanin1(pObj)->pNext; + // simulate + pSop = pObj->pData; + if ( pSop[0] == '0' && pSop[1] == '0' ) + for ( k = 0; k < pManScl->nWords; k++ ) + puData[k] = ~puData0[k] & ~puData1[k]; + else if ( pSop[0] == '0' ) + for ( k = 0; k < pManScl->nWords; k++ ) + puData[k] = ~puData0[k] & puData1[k]; + else if ( pSop[1] == '0' ) + for ( k = 0; k < pManScl->nWords; k++ ) + puData[k] = puData0[k] & ~puData1[k]; + else + for ( k = 0; k < pManScl->nWords; k++ ) + puData[k] = puData0[k] & puData1[k]; + } + return puData; +} + +/**Function************************************************************* + + Synopsis [Performs superchoicing for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeSuperChoiceCollect2_rec( Abc_Obj_t * pObj, Vec_Ptr_t * vVolume ) +{ + if ( pObj->fMarkC ) + return; + pObj->fMarkC = 1; + assert( Abc_ObjFaninNum(pObj) == 2 ); + Abc_NodeSuperChoiceCollect2_rec( Abc_ObjFanin0(pObj), vVolume ); + Abc_NodeSuperChoiceCollect2_rec( Abc_ObjFanin1(pObj), vVolume ); + Vec_PtrPush( vVolume, pObj ); +} + +/**Function************************************************************* + + Synopsis [Performs superchoicing for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeSuperChoiceCollect2( Abc_Obj_t * pRoot, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vVolume ) +{ + Abc_Obj_t * pObj; + int i; + Vec_PtrForEachEntry( vLeaves, pObj, i ) + pObj->fMarkC = 1; + Vec_PtrClear( vVolume ); + Abc_NodeSuperChoiceCollect2_rec( pRoot, vVolume ); + Vec_PtrForEachEntry( vLeaves, pObj, i ) + pObj->fMarkC = 0; + Vec_PtrForEachEntry( vVolume, pObj, i ) + pObj->fMarkC = 0; +} + +/**Function************************************************************* + + Synopsis [Performs superchoicing for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeSuperChoiceCollect_rec( Abc_Obj_t * pObj, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vVolume ) +{ + if ( pObj->fMarkB ) + { + Vec_PtrPush( vLeaves, pObj ); + pObj->fMarkB = 0; + } + if ( pObj->fMarkC ) + return; + pObj->fMarkC = 1; + assert( Abc_ObjFaninNum(pObj) == 2 ); + Abc_NodeSuperChoiceCollect_rec( Abc_ObjFanin0(pObj), vLeaves, vVolume ); + Abc_NodeSuperChoiceCollect_rec( Abc_ObjFanin1(pObj), vLeaves, vVolume ); + Vec_PtrPush( vVolume, pObj ); +} + +/**Function************************************************************* + + Synopsis [Performs superchoicing for one node.] + + Description [Orders the leaves topologically.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeSuperChoiceCollect( Abc_Obj_t * pRoot, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vVolume ) +{ + Abc_Obj_t * pObj; + int i, nLeaves; + nLeaves = Vec_PtrSize(vLeaves); + Vec_PtrForEachEntry( vLeaves, pObj, i ) + pObj->fMarkB = pObj->fMarkC = 1; + Vec_PtrClear( vVolume ); + Vec_PtrClear( vLeaves ); + Abc_NodeSuperChoiceCollect_rec( pRoot, vLeaves, vVolume ); + assert( Vec_PtrSize(vLeaves) == nLeaves ); + Vec_PtrForEachEntry( vLeaves, pObj, i ) + pObj->fMarkC = 0; + Vec_PtrForEachEntry( vVolume, pObj, i ) + pObj->fMarkC = 0; +} + +/**Function************************************************************* + + Synopsis [Performs superchoicing for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeLeavesRemove( Vec_Ptr_t * vLeaves, unsigned uPhase, int nVars ) +{ + int i; + for ( i = nVars - 1; i >= 0; i-- ) + if ( uPhase & (1 << i) ) + Vec_PtrRemove( vLeaves, Vec_PtrEntry(vLeaves, i) ); +} + +/**Function************************************************************* + + Synopsis [Performs superchoicing for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeGetLevel( Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanin; + int i, Level; + Level = 0; + Abc_ObjForEachFanin( pObj, pFanin, i ) + Level = ABC_MAX( Level, (int)pFanin->Level ); + return Level + 1; +} + +/**Function************************************************************* + + Synopsis [Performs superchoicing for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NodeSuperChoiceLut( Abc_ManScl_t * p, Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanin, * pObjNew; + int i, nVars, uSupport, nSuppVars; + // collect the cone using DFS (excluding leaves) + Abc_NodeSuperChoiceCollect2( pObj, p->vLeaves, p->vVolume ); + assert( Vec_PtrEntryLast(p->vVolume) == pObj ); + // compute the truth table + p->uTruth = Abc_NodeSuperChoiceTruth( p ); + // get the support of this truth table + nVars = Vec_PtrSize(p->vLeaves); + uSupport = Extra_TruthSupport(p->uTruth, nVars); + nSuppVars = Extra_WordCountOnes(uSupport); + assert( nSuppVars <= nVars ); + if ( nSuppVars == 0 ) + { + pObj->Level = 0; + return NULL; + } + if ( nSuppVars == 1 ) + { + // find the variable + for ( i = 0; i < nVars; i++ ) + if ( uSupport & (1 << i) ) + break; + assert( i < nVars ); + pFanin = Vec_PtrEntry( p->vLeaves, i ); + pObj->Level = pFanin->Level; + return NULL; + } + // support-minimize the truth table + if ( nSuppVars != nVars ) + { + Extra_TruthShrink( p->uCofs[0], p->uTruth, nSuppVars, nVars, uSupport ); + Extra_TruthCopy( p->uTruth, p->uCofs[0], nVars ); + Abc_NodeLeavesRemove( p->vLeaves, ((1 << nVars) - 1) & ~uSupport, nVars ); + } +// return NULL; + // decompose the truth table recursively + while ( Vec_PtrSize(p->vLeaves) > p->nLutSize ) + if ( !Abc_NodeDecomposeStep( p ) ) + { + Vec_PtrForEachEntry( p->vLeaves, pFanin, i ) + if ( Abc_ObjIsNode(pFanin) && Abc_ObjFanoutNum(pFanin) == 0 ) + Abc_NtkDeleteObj_rec( pFanin, 1 ); + return NULL; + } + // create the topmost node + pObjNew = Abc_NtkCreateNode( pObj->pNtk ); + Vec_PtrForEachEntry( p->vLeaves, pFanin, i ) + Abc_ObjAddFanin( pObjNew, pFanin ); + // create the function + pObjNew->pData = Abc_SopCreateFromTruth( pObj->pNtk->pManFunc, Vec_PtrSize(p->vLeaves), p->uTruth ); // need ISOP + pObjNew->Level = Abc_NodeGetLevel( pObjNew ); + return pObjNew; +} + +/**Function************************************************************* + + Synopsis [Procedure used for sorting the nodes in increasing order of levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeCompareLevelsInc( int * pp1, int * pp2 ) +{ + Abc_Obj_t * pNode1, * pNode2; + pNode1 = Vec_PtrEntry(s_pLeaves, *pp1); + pNode2 = Vec_PtrEntry(s_pLeaves, *pp2); + if ( pNode1->Level < pNode2->Level ) + return -1; + if ( pNode1->Level > pNode2->Level ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Selects the earliest arriving nodes from the array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeDecomposeSort( Abc_Obj_t ** pLeaves, int nVars, int * pBSet, int nLutSize ) +{ + Abc_Obj_t * pTemp[SCL_VARS_MAX]; + int i, k, kBest, LevelMin; + assert( nLutSize < nVars ); + assert( nVars <= SCL_VARS_MAX ); + // copy nodes into the internal storage +// printf( "(" ); + for ( i = 0; i < nVars; i++ ) + { + pTemp[i] = pLeaves[i]; +// printf( " %d", pLeaves[i]->Level ); + } +// printf( " )\n" ); + // choose one node at a time + for ( i = 0; i < nLutSize; i++ ) + { + kBest = -1; + LevelMin = ABC_INFINITY; + for ( k = 0; k < nVars; k++ ) + if ( pTemp[k] && LevelMin > (int)pTemp[k]->Level ) + { + LevelMin = pTemp[k]->Level; + kBest = k; + } + pBSet[i] = kBest; + pTemp[kBest] = NULL; + } +} + +/**Function************************************************************* + + Synopsis [Performs superchoicing for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeDecomposeStep( Abc_ManScl_t * p ) +{ + static char pCofClasses[1<vLeaves, 0))->pNtk; + // find the earliest nodes + nVars = Vec_PtrSize(p->vLeaves); + assert( nVars > p->nLutSize ); +/* + for ( v = 0; v < nVars; v++ ) + p->pBSet[v] = v; + qsort( (void *)p->pBSet, nVars, sizeof(int), + (int (*)(const void *, const void *)) Abc_NodeCompareLevelsInc ); +*/ + Abc_NodeDecomposeSort( (Abc_Obj_t **)Vec_PtrArray(p->vLeaves), Vec_PtrSize(p->vLeaves), p->pBSet, p->nLutSize ); + assert( ((Abc_Obj_t *)Vec_PtrEntry(p->vLeaves, p->pBSet[0]))->Level <= + ((Abc_Obj_t *)Vec_PtrEntry(p->vLeaves, p->pBSet[1]))->Level ); + // cofactor w.r.t. the selected variables + Extra_TruthCopy( p->uCofs[1], p->uTruth, nVars ); + c = 2; + for ( v = 0; v < p->nLutSize; v++ ) + for ( k = 0; k < (1<uCofs[c], p->uCofs[c/2], nVars ); + Extra_TruthCopy( p->uCofs[c+1], p->uCofs[c/2], nVars ); + Extra_TruthCofactor0( p->uCofs[c], nVars, p->pBSet[v] ); + Extra_TruthCofactor1( p->uCofs[c+1], nVars, p->pBSet[v] ); + c += 2; + } + assert( c == (2 << p->nLutSize) ); + // count unique cofactors + nClasses = 0; + nCofs = (1 << p->nLutSize); + for ( i = 0; i < nCofs; i++ ) + { + pTruthCof = p->uCofs[ nCofs + i ]; + for ( k = 0; k < nClasses; k++ ) + { + pTruthClass = p->uCofs[ nCofs + pCofClasses[k][0] ]; + if ( Extra_TruthIsEqual( pTruthCof, pTruthClass, nVars ) ) + { + pCofClasses[k][ nCofClasses[k]++ ] = i; + break; + } + } + if ( k != nClasses ) + continue; + // not found + pCofClasses[nClasses][0] = i; + nCofClasses[nClasses] = 1; + nClasses++; + if ( nClasses > nCofs/2 ) + return 0; + } + // the number of cofactors is acceptable + nVarsNew = Extra_Base2Log( nClasses ); + assert( nVarsNew < p->nLutSize ); + // create the remainder truth table + // for each class of cofactors, multiply cofactor truth table by its code + Extra_TruthClear( p->uTruth, nVars ); + for ( k = 0; k < nClasses; k++ ) + { + pTruthClass = p->uCofs[ nCofs + pCofClasses[k][0] ]; + for ( v = 0; v < nVarsNew; v++ ) + if ( k & (1 << v) ) + Extra_TruthAnd( pTruthClass, pTruthClass, p->uVars[p->pBSet[v]], nVars ); + else + Extra_TruthSharp( pTruthClass, pTruthClass, p->uVars[p->pBSet[v]], nVars ); + Extra_TruthOr( p->uTruth, p->uTruth, pTruthClass, nVars ); + } + // create nodes + pTruth = p->uCofs[0]; + for ( v = 0; v < nVarsNew; v++ ) + { + Extra_TruthClear( pTruth, p->nLutSize ); + for ( k = 0; k < nClasses; k++ ) + if ( k & (1 << v) ) + for ( i = 0; i < nCofClasses[k]; i++ ) + { + pTruthCof = p->uCofs[1]; + Extra_TruthFill( pTruthCof, p->nLutSize ); + for ( w = 0; w < p->nLutSize; w++ ) + if ( pCofClasses[k][i] & (1 << (p->nLutSize-1-w)) ) + Extra_TruthAnd( pTruthCof, pTruthCof, p->uVars[w], p->nLutSize ); + else + Extra_TruthSharp( pTruthCof, pTruthCof, p->uVars[w], p->nLutSize ); + Extra_TruthOr( pTruth, pTruth, pTruthCof, p->nLutSize ); + } + // implement the node + pObjNew = Abc_NtkCreateNode( pNtk ); + for ( i = 0; i < p->nLutSize; i++ ) + { + pFanin = Vec_PtrEntry( p->vLeaves, p->pBSet[i] ); + Abc_ObjAddFanin( pObjNew, pFanin ); + } + // create the function + pObjNew->pData = Abc_SopCreateFromTruth( pNtk->pManFunc, p->nLutSize, pTruth ); // need ISOP + pObjNew->Level = Abc_NodeGetLevel( pObjNew ); + pNodesNew[v] = pObjNew; + } + // put the new nodes back into the list + for ( v = 0; v < nVarsNew; v++ ) + Vec_PtrWriteEntry( p->vLeaves, p->pBSet[v], pNodesNew[v] ); + // compute the variables that should be removed + uPhase = 0; + for ( v = nVarsNew; v < p->nLutSize; v++ ) + uPhase |= (1 << p->pBSet[v]); + // remove entries from the array + Abc_NodeLeavesRemove( p->vLeaves, uPhase, nVars ); + // update truth table + Extra_TruthShrink( p->uCofs[0], p->uTruth, nVars - p->nLutSize + nVarsNew, nVars, ((1 << nVars) - 1) & ~uPhase ); + Extra_TruthCopy( p->uTruth, p->uCofs[0], nVars ); + assert( !Extra_TruthVarInSupport( p->uTruth, nVars, nVars - p->nLutSize + nVarsNew ) ); + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcMap.c b/abc_with_bb_support/src/base/abci/abcMap.c new file mode 100644 index 000000000..c25f3308e --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcMap.c @@ -0,0 +1,657 @@ +/**CFile**************************************************************** + + FileName [abcMap.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Interface with the SC mapping package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcMap.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "main.h" +#include "mio.h" +#include "mapper.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Map_Man_t * Abc_NtkToMap( Abc_Ntk_t * pNtk, double DelayTarget, int fRecovery, float * pSwitching, int fVerbose ); +static Abc_Ntk_t * Abc_NtkFromMap( Map_Man_t * pMan, Abc_Ntk_t * pNtk ); +static Abc_Obj_t * Abc_NodeFromMap_rec( Abc_Ntk_t * pNtkNew, Map_Node_t * pNodeMap, int fPhase ); +static Abc_Obj_t * Abc_NodeFromMapPhase_rec( Abc_Ntk_t * pNtkNew, Map_Node_t * pNodeMap, int fPhase ); +static Abc_Obj_t * Abc_NodeFromMapSuper_rec( Abc_Ntk_t * pNtkNew, Map_Node_t * pNodeMap, Map_Super_t * pSuper, Abc_Obj_t * pNodePis[], int nNodePis ); + +static Abc_Ntk_t * Abc_NtkFromMapSuperChoice( Map_Man_t * pMan, Abc_Ntk_t * pNtk ); +static void Abc_NodeSuperChoice( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pNode ); +static void Abc_NodeFromMapCutPhase( Abc_Ntk_t * pNtkNew, Map_Cut_t * pCut, int fPhase ); +static Abc_Obj_t * Abc_NodeFromMapSuperChoice_rec( Abc_Ntk_t * pNtkNew, Map_Super_t * pSuper, Abc_Obj_t * pNodePis[], int nNodePis ); + + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Interface with the mapping package.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkMap( Abc_Ntk_t * pNtk, double DelayTarget, int fRecovery, int fSwitching, int fVerbose ) +{ + int fShowSwitching = 1; + Abc_Ntk_t * pNtkNew; + Map_Man_t * pMan; + Vec_Int_t * vSwitching; + float * pSwitching = NULL; + int clk; + + assert( Abc_NtkIsStrash(pNtk) ); + + // check that the library is available + if ( Abc_FrameReadLibGen() == NULL ) + { + printf( "The current library is not available.\n" ); + return 0; + } + + // derive the supergate library + if ( Abc_FrameReadLibSuper() == NULL && Abc_FrameReadLibGen() ) + { + printf( "A simple supergate library is derived from gate library \"%s\".\n", + Mio_LibraryReadName(Abc_FrameReadLibGen()) ); + Map_SuperLibDeriveFromGenlib( Abc_FrameReadLibGen() ); + } + + // print a warning about choice nodes + if ( Abc_NtkGetChoiceNum( pNtk ) ) + printf( "Performing mapping with choices.\n" ); + + // compute switching activity + fShowSwitching |= fSwitching; + if ( fShowSwitching ) + { + extern Vec_Int_t * Sim_NtkComputeSwitching( Abc_Ntk_t * pNtk, int nPatterns ); + vSwitching = Sim_NtkComputeSwitching( pNtk, 4096 ); + pSwitching = (float *)vSwitching->pArray; + } + + // perform the mapping + pMan = Abc_NtkToMap( pNtk, DelayTarget, fRecovery, pSwitching, fVerbose ); + if ( pSwitching ) Vec_IntFree( vSwitching ); + if ( pMan == NULL ) + return NULL; +clk = clock(); + Map_ManSetSwitching( pMan, fSwitching ); + if ( !Map_Mapping( pMan ) ) + { + Map_ManFree( pMan ); + return NULL; + } +// Map_ManPrintStatsToFile( pNtk->pSpec, Map_ManReadAreaFinal(pMan), Map_ManReadRequiredGlo(pMan), clock()-clk ); + + // reconstruct the network after mapping + pNtkNew = Abc_NtkFromMap( pMan, pNtk ); + if ( pNtkNew == NULL ) + return NULL; + Map_ManFree( pMan ); + + if ( pNtk->pExdc ) + pNtkNew->pExdc = Abc_NtkDup( pNtk->pExdc ); + + // make sure that everything is okay + if ( !Abc_NtkCheck( pNtkNew ) ) + { + printf( "Abc_NtkMap: The network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Load the network into manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Man_t * Abc_NtkToMap( Abc_Ntk_t * pNtk, double DelayTarget, int fRecovery, float * pSwitching, int fVerbose ) +{ + Map_Man_t * pMan; + ProgressBar * pProgress; + Map_Node_t * pNodeMap; + Vec_Ptr_t * vNodes; + Abc_Obj_t * pNode, * pFanin, * pPrev; + int i; + + assert( Abc_NtkIsStrash(pNtk) ); + + // start the mapping manager and set its parameters + pMan = Map_ManCreate( Abc_NtkPiNum(pNtk) + Abc_NtkLatchNum(pNtk), Abc_NtkPoNum(pNtk) + Abc_NtkLatchNum(pNtk), fVerbose ); + if ( pMan == NULL ) + return NULL; + Map_ManSetAreaRecovery( pMan, fRecovery ); + Map_ManSetOutputNames( pMan, Abc_NtkCollectCioNames(pNtk, 1) ); + Map_ManSetDelayTarget( pMan, (float)DelayTarget ); + Map_ManSetInputArrivals( pMan, (Map_Time_t *)Abc_NtkGetCiArrivalTimes(pNtk) ); + + // create PIs and remember them in the old nodes + Abc_NtkCleanCopy( pNtk ); + Abc_AigConst1(pNtk)->pCopy = (Abc_Obj_t *)Map_ManReadConst1(pMan); + Abc_NtkForEachCi( pNtk, pNode, i ) + { + pNodeMap = Map_ManReadInputs(pMan)[i]; + pNode->pCopy = (Abc_Obj_t *)pNodeMap; + if ( pSwitching ) + Map_NodeSetSwitching( pNodeMap, pSwitching[pNode->Id] ); + } + + // load the AIG into the mapper + vNodes = Abc_AigDfs( pNtk, 0, 0 ); + pProgress = Extra_ProgressBarStart( stdout, vNodes->nSize ); + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + // add the node to the mapper + pNodeMap = Map_NodeAnd( pMan, + Map_NotCond( Abc_ObjFanin0(pNode)->pCopy, Abc_ObjFaninC0(pNode) ), + Map_NotCond( Abc_ObjFanin1(pNode)->pCopy, Abc_ObjFaninC1(pNode) ) ); + assert( pNode->pCopy == NULL ); + // remember the node + pNode->pCopy = (Abc_Obj_t *)pNodeMap; + if ( pSwitching ) + Map_NodeSetSwitching( pNodeMap, pSwitching[pNode->Id] ); + // set up the choice node + if ( Abc_AigNodeIsChoice( pNode ) ) + for ( pPrev = pNode, pFanin = pNode->pData; pFanin; pPrev = pFanin, pFanin = pFanin->pData ) + { + Map_NodeSetNextE( (Map_Node_t *)pPrev->pCopy, (Map_Node_t *)pFanin->pCopy ); + Map_NodeSetRepr( (Map_Node_t *)pFanin->pCopy, (Map_Node_t *)pNode->pCopy ); + } + } + Extra_ProgressBarStop( pProgress ); + Vec_PtrFree( vNodes ); + + // set the primary outputs in the required phase + Abc_NtkForEachCo( pNtk, pNode, i ) + Map_ManReadOutputs(pMan)[i] = Map_NotCond( (Map_Node_t *)Abc_ObjFanin0(pNode)->pCopy, Abc_ObjFaninC0(pNode) ); + return pMan; +} + +/**Function************************************************************* + + Synopsis [Creates the mapped network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFromMap( Map_Man_t * pMan, Abc_Ntk_t * pNtk ) +{ + ProgressBar * pProgress; + Abc_Ntk_t * pNtkNew; + Map_Node_t * pNodeMap; + Abc_Obj_t * pNode, * pNodeNew; + int i, nDupGates; + // create the new network + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_LOGIC, ABC_FUNC_MAP ); + // make the mapper point to the new network + Map_ManCleanData( pMan ); + Abc_NtkForEachCi( pNtk, pNode, i ) + Map_NodeSetData( Map_ManReadInputs(pMan)[i], 1, (char *)pNode->pCopy ); + // assign the mapping of the required phase to the POs + pProgress = Extra_ProgressBarStart( stdout, Abc_NtkCoNum(pNtk) ); + Abc_NtkForEachCo( pNtk, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + pNodeMap = Map_ManReadOutputs(pMan)[i]; + pNodeNew = Abc_NodeFromMap_rec( pNtkNew, Map_Regular(pNodeMap), !Map_IsComplement(pNodeMap) ); + assert( !Abc_ObjIsComplement(pNodeNew) ); + Abc_ObjAddFanin( pNode->pCopy, pNodeNew ); + } + Extra_ProgressBarStop( pProgress ); + // decouple the PO driver nodes to reduce the number of levels + nDupGates = Abc_NtkLogicMakeSimpleCos( pNtkNew, 1 ); +// if ( nDupGates && Map_ManReadVerbose(pMan) ) +// printf( "Duplicated %d gates to decouple the CO drivers.\n", nDupGates ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Constructs the nodes corrresponding to one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NodeFromMap_rec( Abc_Ntk_t * pNtkNew, Map_Node_t * pNodeMap, int fPhase ) +{ + Abc_Obj_t * pNodeNew, * pNodeInv; + + // check the case of constant node + if ( Map_NodeIsConst(pNodeMap) ) + return fPhase? Abc_NtkCreateNodeConst1(pNtkNew) : Abc_NtkCreateNodeConst0(pNtkNew); + + // check if the phase is already implemented + pNodeNew = (Abc_Obj_t *)Map_NodeReadData( pNodeMap, fPhase ); + if ( pNodeNew ) + return pNodeNew; + + // implement the node if the best cut is assigned + if ( Map_NodeReadCutBest(pNodeMap, fPhase) != NULL ) + return Abc_NodeFromMapPhase_rec( pNtkNew, pNodeMap, fPhase ); + + // if the cut is not assigned, implement the node + assert( Map_NodeReadCutBest(pNodeMap, !fPhase) != NULL || Map_NodeIsConst(pNodeMap) ); + pNodeNew = Abc_NodeFromMapPhase_rec( pNtkNew, pNodeMap, !fPhase ); + + // add the inverter + pNodeInv = Abc_NtkCreateNode( pNtkNew ); + Abc_ObjAddFanin( pNodeInv, pNodeNew ); + pNodeInv->pData = Mio_LibraryReadInv(Map_ManReadGenLib(Map_NodeReadMan(pNodeMap))); + + // set the inverter + Map_NodeSetData( pNodeMap, fPhase, (char *)pNodeInv ); + return pNodeInv; +} + +/**Function************************************************************* + + Synopsis [Constructs the nodes corrresponding to one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NodeFromMapPhase_rec( Abc_Ntk_t * pNtkNew, Map_Node_t * pNodeMap, int fPhase ) +{ + Abc_Obj_t * pNodePIs[10]; + Abc_Obj_t * pNodeNew; + Map_Node_t ** ppLeaves; + Map_Cut_t * pCutBest; + Map_Super_t * pSuperBest; + unsigned uPhaseBest; + int i, fInvPin, nLeaves; + + // make sure the node can be implemented in this phase + assert( Map_NodeReadCutBest(pNodeMap, fPhase) != NULL || Map_NodeIsConst(pNodeMap) ); + // check if the phase is already implemented + pNodeNew = (Abc_Obj_t *)Map_NodeReadData( pNodeMap, fPhase ); + if ( pNodeNew ) + return pNodeNew; + + // get the information about the best cut + pCutBest = Map_NodeReadCutBest( pNodeMap, fPhase ); + pSuperBest = Map_CutReadSuperBest( pCutBest, fPhase ); + uPhaseBest = Map_CutReadPhaseBest( pCutBest, fPhase ); + nLeaves = Map_CutReadLeavesNum( pCutBest ); + ppLeaves = Map_CutReadLeaves( pCutBest ); + + // collect the PI nodes + for ( i = 0; i < nLeaves; i++ ) + { + fInvPin = ((uPhaseBest & (1 << i)) > 0); + pNodePIs[i] = Abc_NodeFromMap_rec( pNtkNew, ppLeaves[i], !fInvPin ); + assert( pNodePIs[i] != NULL ); + } + + // implement the supergate + pNodeNew = Abc_NodeFromMapSuper_rec( pNtkNew, pNodeMap, pSuperBest, pNodePIs, nLeaves ); + Map_NodeSetData( pNodeMap, fPhase, (char *)pNodeNew ); + return pNodeNew; +} + +/**Function************************************************************* + + Synopsis [Constructs the nodes corrresponding to one supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NodeFromMapSuper_rec( Abc_Ntk_t * pNtkNew, Map_Node_t * pNodeMap, Map_Super_t * pSuper, Abc_Obj_t * pNodePis[], int nNodePis ) +{ + Mio_Gate_t * pRoot; + Map_Super_t ** ppFanins; + Abc_Obj_t * pNodeNew, * pNodeFanin; + int nFanins, Number, i; + + // get the parameters of the supergate + pRoot = Map_SuperReadRoot(pSuper); + if ( pRoot == NULL ) + { + Number = Map_SuperReadNum(pSuper); + if ( Number < nNodePis ) + { + return pNodePis[Number]; + } + else + { +// assert( 0 ); + /* It might happen that a super gate with 5 inputs is constructed that + * actually depends only on the first four variables; i.e the fifth is a + * don't care -- in that case we connect constant node for the fifth + * (since the cut only has 4 variables). An interesting question is what + * if the first variable (and not the fifth one is the redundant one; + * can that happen?) */ + return Abc_NtkCreateNodeConst0(pNtkNew); + } + } + + // get information about the fanins of the supergate + nFanins = Map_SuperReadFaninNum( pSuper ); + ppFanins = Map_SuperReadFanins( pSuper ); + // create a new node with these fanins + pNodeNew = Abc_NtkCreateNode( pNtkNew ); + for ( i = 0; i < nFanins; i++ ) + { + pNodeFanin = Abc_NodeFromMapSuper_rec( pNtkNew, pNodeMap, ppFanins[i], pNodePis, nNodePis ); + Abc_ObjAddFanin( pNodeNew, pNodeFanin ); + } + pNodeNew->pData = pRoot; + return pNodeNew; +} + + + + + +/**Function************************************************************* + + Synopsis [Interface with the mapping package.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkSuperChoice( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkNew; + + Map_Man_t * pMan; + + assert( Abc_NtkIsStrash(pNtk) ); + + // check that the library is available + if ( Abc_FrameReadLibGen() == NULL ) + { + printf( "The current library is not available.\n" ); + return 0; + } + + // derive the supergate library + if ( Abc_FrameReadLibSuper() == NULL && Abc_FrameReadLibGen() ) + { + printf( "A simple supergate library is derived from gate library \"%s\".\n", + Mio_LibraryReadName(Abc_FrameReadLibGen()) ); + Map_SuperLibDeriveFromGenlib( Abc_FrameReadLibGen() ); + } + + // print a warning about choice nodes + if ( Abc_NtkGetChoiceNum( pNtk ) ) + printf( "Performing mapping with choices.\n" ); + + // perform the mapping + pMan = Abc_NtkToMap( pNtk, -1, 1, NULL, 0 ); + if ( pMan == NULL ) + return NULL; + if ( !Map_Mapping( pMan ) ) + { + Map_ManFree( pMan ); + return NULL; + } + + // reconstruct the network after mapping + pNtkNew = Abc_NtkFromMapSuperChoice( pMan, pNtk ); + if ( pNtkNew == NULL ) + return NULL; + Map_ManFree( pMan ); + + // make sure that everything is okay + if ( !Abc_NtkCheck( pNtkNew ) ) + { + printf( "Abc_NtkMap: The network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + + +/**Function************************************************************* + + Synopsis [Creates the mapped network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFromMapSuperChoice( Map_Man_t * pMan, Abc_Ntk_t * pNtk ) +{ + extern Abc_Ntk_t * Abc_NtkMulti( Abc_Ntk_t * pNtk, int nThresh, int nFaninMax, int fCnf, int fMulti, int fSimple, int fFactor ); + ProgressBar * pProgress; + Abc_Ntk_t * pNtkNew, * pNtkNew2; + Abc_Obj_t * pNode; + int i; + + // save the pointer to the mapped nodes + Abc_NtkForEachCi( pNtk, pNode, i ) + pNode->pNext = pNode->pCopy; + Abc_NtkForEachPo( pNtk, pNode, i ) + pNode->pNext = pNode->pCopy; + Abc_NtkForEachNode( pNtk, pNode, i ) + pNode->pNext = pNode->pCopy; + + // duplicate the network + pNtkNew2 = Abc_NtkDup( pNtk ); + pNtkNew = Abc_NtkMulti( pNtkNew2, 0, 20, 0, 0, 1, 0 ); + if ( !Abc_NtkBddToSop( pNtkNew, 0 ) ) + { + printf( "Abc_NtkFromMapSuperChoice(): Converting to SOPs has failed.\n" ); + return NULL; + } + + // set the old network to point to the new network + Abc_NtkForEachCi( pNtk, pNode, i ) + pNode->pCopy = pNode->pCopy->pCopy; + Abc_NtkForEachPo( pNtk, pNode, i ) + pNode->pCopy = pNode->pCopy->pCopy; + Abc_NtkForEachNode( pNtk, pNode, i ) + pNode->pCopy = pNode->pCopy->pCopy; + Abc_NtkDelete( pNtkNew2 ); + + // set the pointers from the mapper to the new nodes + Abc_NtkForEachCi( pNtk, pNode, i ) + { + Map_NodeSetData( Map_ManReadInputs(pMan)[i], 0, (char *)Abc_NtkCreateNodeInv(pNtkNew,pNode->pCopy) ); + Map_NodeSetData( Map_ManReadInputs(pMan)[i], 1, (char *)pNode->pCopy ); + } + Abc_NtkForEachNode( pNtk, pNode, i ) + { +// if ( Abc_NodeIsConst(pNode) ) +// continue; + Map_NodeSetData( (Map_Node_t *)pNode->pNext, 0, (char *)Abc_NtkCreateNodeInv(pNtkNew,pNode->pCopy) ); + Map_NodeSetData( (Map_Node_t *)pNode->pNext, 1, (char *)pNode->pCopy ); + } + + // assign the mapping of the required phase to the POs + pProgress = Extra_ProgressBarStart( stdout, Abc_NtkObjNumMax(pNtk) ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); +// if ( Abc_NodeIsConst(pNode) ) +// continue; + Abc_NodeSuperChoice( pNtkNew, pNode ); + } + Extra_ProgressBarStop( pProgress ); + return pNtkNew; +} + + +/**Function************************************************************* + + Synopsis [Creates the mapped network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeSuperChoice( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pNode ) +{ + Map_Node_t * pMapNode = (Map_Node_t *)pNode->pNext; + Map_Cut_t * pCuts, * pTemp; + + pCuts = Map_NodeReadCuts(pMapNode); + for ( pTemp = Map_CutReadNext(pCuts); pTemp; pTemp = Map_CutReadNext(pTemp) ) + { + Abc_NodeFromMapCutPhase( pNtkNew, pTemp, 0 ); + Abc_NodeFromMapCutPhase( pNtkNew, pTemp, 1 ); + } +} + + +/**Function************************************************************* + + Synopsis [Constructs the nodes corrresponding to one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeFromMapCutPhase( Abc_Ntk_t * pNtkNew, Map_Cut_t * pCut, int fPhase ) +{ + Abc_Obj_t * pNodePIs[10]; + Map_Node_t ** ppLeaves; + Map_Super_t * pSuperBest; + unsigned uPhaseBest; + int i, fInvPin, nLeaves; + + pSuperBest = Map_CutReadSuperBest( pCut, fPhase ); + if ( pSuperBest == NULL ) + return; + + // get the information about the best cut + uPhaseBest = Map_CutReadPhaseBest( pCut, fPhase ); + nLeaves = Map_CutReadLeavesNum( pCut ); + ppLeaves = Map_CutReadLeaves( pCut ); + + // collect the PI nodes + for ( i = 0; i < nLeaves; i++ ) + { + fInvPin = ((uPhaseBest & (1 << i)) > 0); + pNodePIs[i] = (Abc_Obj_t *)Map_NodeReadData( ppLeaves[i], !fInvPin ); + assert( pNodePIs[i] != NULL ); + } + + // implement the supergate + Abc_NodeFromMapSuperChoice_rec( pNtkNew, pSuperBest, pNodePIs, nLeaves ); +} + + +/**Function************************************************************* + + Synopsis [Constructs the nodes corrresponding to one supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NodeFromMapSuperChoice_rec( Abc_Ntk_t * pNtkNew, Map_Super_t * pSuper, Abc_Obj_t * pNodePis[], int nNodePis ) +{ + Mio_Gate_t * pRoot; + Map_Super_t ** ppFanins; + Abc_Obj_t * pNodeNew, * pNodeFanin; + int nFanins, Number, i; + + // get the parameters of the supergate + pRoot = Map_SuperReadRoot(pSuper); + if ( pRoot == NULL ) + { + Number = Map_SuperReadNum(pSuper); + if ( Number < nNodePis ) + { + return pNodePis[Number]; + } + else + { +// assert( 0 ); + /* It might happen that a super gate with 5 inputs is constructed that + * actually depends only on the first four variables; i.e the fifth is a + * don't care -- in that case we connect constant node for the fifth + * (since the cut only has 4 variables). An interesting question is what + * if the first variable (and not the fifth one is the redundant one; + * can that happen?) */ + return Abc_NtkCreateNodeConst0(pNtkNew); + } + } + + // get information about the fanins of the supergate + nFanins = Map_SuperReadFaninNum( pSuper ); + ppFanins = Map_SuperReadFanins( pSuper ); + // create a new node with these fanins + pNodeNew = Abc_NtkCreateNode( pNtkNew ); + for ( i = 0; i < nFanins; i++ ) + { + pNodeFanin = Abc_NodeFromMapSuperChoice_rec( pNtkNew, ppFanins[i], pNodePis, nNodePis ); + Abc_ObjAddFanin( pNodeNew, pNodeFanin ); + } + pNodeNew->pData = Abc_SopRegister( pNtkNew->pManFunc, Mio_GateReadSop(pRoot) ); + return pNodeNew; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcMeasure.c b/abc_with_bb_support/src/base/abci/abcMeasure.c new file mode 100644 index 000000000..d307466a2 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcMeasure.c @@ -0,0 +1,478 @@ +/**CFile**************************************************************** + + FileName [abc_.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abc_.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "kit.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintMeasures( unsigned * pTruth, int nVars ) +{ + unsigned uCofs[10][32]; + int i, k, nOnes; + + // total pairs + nOnes = Kit_TruthCountOnes( uCofs[0], nVars ); + printf( "Total = %d.\n", nOnes * ((1 << nVars) - nOnes) ); + + // print measures for individual variables + for ( i = 0; i < nVars; i++ ) + { + Kit_TruthUniqueNew( uCofs[0], pTruth, nVars, i ); + nOnes = Kit_TruthCountOnes( uCofs[0], nVars ); + printf( "%7d ", nOnes ); + } + printf( "\n" ); + + // consider pairs + for ( i = 0; i < nVars; i++ ) + for ( k = 0; k < nVars; k++ ) + { + if ( i == k ) + { + printf( " " ); + continue; + } + Kit_TruthCofactor0New( uCofs[0], pTruth, nVars, i ); + Kit_TruthCofactor1New( uCofs[1], pTruth, nVars, i ); + + Kit_TruthCofactor0New( uCofs[2], uCofs[0], nVars, k ); // 00 + Kit_TruthCofactor1New( uCofs[3], uCofs[0], nVars, k ); // 01 + Kit_TruthCofactor0New( uCofs[4], uCofs[1], nVars, k ); // 10 + Kit_TruthCofactor1New( uCofs[5], uCofs[1], nVars, k ); // 11 + + Kit_TruthAndPhase( uCofs[6], uCofs[2], uCofs[5], nVars, 0, 1 ); // 00 & 11' + Kit_TruthAndPhase( uCofs[7], uCofs[2], uCofs[5], nVars, 1, 0 ); // 00' & 11 + Kit_TruthAndPhase( uCofs[8], uCofs[3], uCofs[4], nVars, 0, 1 ); // 01 & 10' + Kit_TruthAndPhase( uCofs[9], uCofs[3], uCofs[4], nVars, 1, 0 ); // 01' & 10 + + nOnes = Kit_TruthCountOnes( uCofs[6], nVars ) + + Kit_TruthCountOnes( uCofs[7], nVars ) + + Kit_TruthCountOnes( uCofs[8], nVars ) + + Kit_TruthCountOnes( uCofs[9], nVars ); + + printf( "%7d ", nOnes ); + if ( k == nVars - 1 ) + printf( "\n" ); + } + printf( "\n" ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_Ntk4VarObjPrint_rec( Abc_Obj_t * pObj ) +{ + if ( pObj == Abc_AigConst1(pObj->pNtk) ) + { + printf( "1" ); + return; + } + if ( Abc_ObjIsPi(pObj) ) + { + printf( "%c", pObj->Id - 1 + 'a' ); + return; + } + + printf( "(" ); + Abc_Ntk4VarObjPrint_rec( Abc_ObjFanin0(pObj) ); + if ( Abc_ObjFaninC0(pObj) ) + printf( "\'" ); + Abc_Ntk4VarObjPrint_rec( Abc_ObjFanin1(pObj) ); + if ( Abc_ObjFaninC1(pObj) ) + printf( "\'" ); + printf( ")" ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Abc_Ntk4VarObj( Vec_Ptr_t * vNodes ) +{ + Abc_Obj_t * pObj; + unsigned uTruth0, uTruth1; + int i; + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + uTruth0 = (unsigned)(Abc_ObjFanin0(pObj)->pCopy); + uTruth1 = (unsigned)(Abc_ObjFanin1(pObj)->pCopy); + if ( Abc_ObjFaninC0(pObj) ) + uTruth0 = ~uTruth0; + if ( Abc_ObjFaninC1(pObj) ) + uTruth1 = ~uTruth1; + pObj->pCopy = (void *)(uTruth0 & uTruth1); + } + return uTruth0 & uTruth1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_Ntk4VarTable( Abc_Ntk_t * pNtk ) +{ + static unsigned u4VarTruths[4] = { 0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, 0xFF00FF00 }; + static unsigned u4VarTts[222] = { + 0x0000, 0x0001, 0x0003, 0x0006, 0x0007, 0x000f, 0x0016, 0x0017, 0x0018, 0x0019, + 0x001b, 0x001e, 0x001f, 0x003c, 0x003d, 0x003f, 0x0069, 0x006b, 0x006f, 0x007e, + 0x007f, 0x00ff, 0x0116, 0x0117, 0x0118, 0x0119, 0x011a, 0x011b, 0x011e, 0x011f, + 0x012c, 0x012d, 0x012f, 0x013c, 0x013d, 0x013e, 0x013f, 0x0168, 0x0169, 0x016a, + 0x016b, 0x016e, 0x016f, 0x017e, 0x017f, 0x0180, 0x0181, 0x0182, 0x0183, 0x0186, + 0x0187, 0x0189, 0x018b, 0x018f, 0x0196, 0x0197, 0x0198, 0x0199, 0x019a, 0x019b, + 0x019e, 0x019f, 0x01a8, 0x01a9, 0x01aa, 0x01ab, 0x01ac, 0x01ad, 0x01ae, 0x01af, + 0x01bc, 0x01bd, 0x01be, 0x01bf, 0x01e8, 0x01e9, 0x01ea, 0x01eb, 0x01ee, 0x01ef, + 0x01fe, 0x033c, 0x033d, 0x033f, 0x0356, 0x0357, 0x0358, 0x0359, 0x035a, 0x035b, + 0x035e, 0x035f, 0x0368, 0x0369, 0x036a, 0x036b, 0x036c, 0x036d, 0x036e, 0x036f, + 0x037c, 0x037d, 0x037e, 0x03c0, 0x03c1, 0x03c3, 0x03c5, 0x03c6, 0x03c7, 0x03cf, + 0x03d4, 0x03d5, 0x03d6, 0x03d7, 0x03d8, 0x03d9, 0x03db, 0x03dc, 0x03dd, 0x03de, + 0x03fc, 0x0660, 0x0661, 0x0662, 0x0663, 0x0666, 0x0667, 0x0669, 0x066b, 0x066f, + 0x0672, 0x0673, 0x0676, 0x0678, 0x0679, 0x067a, 0x067b, 0x067e, 0x0690, 0x0691, + 0x0693, 0x0696, 0x0697, 0x069f, 0x06b0, 0x06b1, 0x06b2, 0x06b3, 0x06b4, 0x06b5, + 0x06b6, 0x06b7, 0x06b9, 0x06bd, 0x06f0, 0x06f1, 0x06f2, 0x06f6, 0x06f9, 0x0776, + 0x0778, 0x0779, 0x077a, 0x077e, 0x07b0, 0x07b1, 0x07b4, 0x07b5, 0x07b6, 0x07bc, + 0x07e0, 0x07e1, 0x07e2, 0x07e3, 0x07e6, 0x07e9, 0x07f0, 0x07f1, 0x07f2, 0x07f8, + 0x0ff0, 0x1668, 0x1669, 0x166a, 0x166b, 0x166e, 0x167e, 0x1681, 0x1683, 0x1686, + 0x1687, 0x1689, 0x168b, 0x168e, 0x1696, 0x1697, 0x1698, 0x1699, 0x169a, 0x169b, + 0x169e, 0x16a9, 0x16ac, 0x16ad, 0x16bc, 0x16e9, 0x177e, 0x178e, 0x1796, 0x1798, + 0x179a, 0x17ac, 0x17e8, 0x18e7, 0x19e1, 0x19e3, 0x19e6, 0x1bd8, 0x1be4, 0x1ee1, + 0x3cc3, 0x6996 + }; + int Counters[222] = {0}; + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj; + unsigned uTruth; + int i, k, Count = 0; + + unsigned short * puCanons = NULL; + unsigned char * puMap = NULL; + Extra_Truth4VarNPN( &puCanons, NULL, NULL, &puMap ); + + // set elementary truth tables + assert( Abc_NtkPiNum(pNtk) == 4 ); + Abc_AigConst1(pNtk)->pCopy = (void *)0xFFFFFFFF; + Abc_NtkForEachPi( pNtk, pObj, i ) + pObj->pCopy = (void *)u4VarTruths[i]; + + // create truth tables + Abc_NtkForEachPo( pNtk, pObj, i ) + { + vNodes = Abc_NtkDfsNodes( pNtk, &pObj, 1 ); + if ( Vec_PtrSize(vNodes) == 0 ) + uTruth = (unsigned)Abc_ObjFanin0(pObj)->pCopy; + else + uTruth = Abc_Ntk4VarObj( vNodes ); + + if ( (uTruth & 0xFFFF) < (~uTruth & 0xFFFF) ) + uTruth = uTruth & 0xFFFF; + else + uTruth = ~uTruth & 0xFFFF; + + for ( k = 0; k < 222; k++ ) + if ( u4VarTts[k] == uTruth ) + break; + if ( k == 222 ) + continue; +/* +// if ( uTruth == 1725 ) + if ( k == 96 ) + { + printf( "%d : ", Vec_PtrSize(vNodes) ); + Abc_Ntk4VarObjPrint_rec( Abc_ObjFanin0(pObj) ); + printf( "\n" ); + } +*/ + Counters[k]++; + +// Counters[ puMap[uTruth & 0xFFFF] ]++; + Vec_PtrFree( vNodes ); + } + free( puCanons ); + free( puMap ); + + Count = 0; + for ( k = 0; k < 222; k++ ) + { + printf( "%d/%x/%d ", k, u4VarTts[k], Counters[k] ); + Count += Counters[k]; + } + printf( " Total = %d\n", Count ); +} + + + + +/**Function************************************************************* + + Synopsis [Returns 1 if there are no more than 2 unique cofactors.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkPrintOneDecompCheckCofList( unsigned * uCofs, int nCofs ) +{ + int i, Ind = -1; + assert( nCofs > 2 ); + for ( i = 1; i < nCofs; i++ ) + { + if ( uCofs[i] == uCofs[0] ) + continue; + if ( Ind == -1 ) + { + Ind = i; + continue; + } + if ( uCofs[i] == uCofs[Ind] ) + continue; + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Checks all cofactors with the given mask.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkPrintOneDecompCheck( unsigned * uCofs, int nCofs, unsigned uMask ) +{ + unsigned pCofs[32][32]; + int nCofNums[32] = {0}; + int uMasks[32]; + int nGroups = 0; + int i, k; + for ( i = 0; i < nCofs; i++ ) + { + // find group of this cof + for ( k = 0; k < nGroups; k++ ) + if ( (int)(i & uMask) == uMasks[k] ) + break; + if ( k == nGroups ) + { + uMasks[k] = (i & uMask); + nGroups++; + } + // save cof in the group + pCofs[k][ nCofNums[k]++ ] = uCofs[i]; + assert( nCofNums[k] <= 32 ); + assert( nGroups <= 32 ); + } + // check the groups + for ( i = 0; i < nGroups; i++ ) + if ( !Abc_NtkPrintOneDecompCheckCofList(pCofs[i], nCofNums[i]) ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintOneDecomp_rec( unsigned * uCofs, int nCofs, int nVars, unsigned uMask, int * pBestSize, unsigned * puBestMask ) +{ + unsigned uMaskNew; + int v, last, Counter = 0; + // find the last variable in the mask + for ( v = 0; v < nVars; v++ ) + if ( uMask & (1< 3 ) + return; + // try adding one variable after the last + for ( v = last + 1; v < nVars; v++ ) + { + uMaskNew = uMask | (1 << v); + if ( !Abc_NtkPrintOneDecompCheck( uCofs, nCofs, uMaskNew ) ) + continue; + if ( *pBestSize < Counter + 1 ) + { + *pBestSize = Counter + 1; + *puBestMask = uMaskNew; + } + // try other masks + Abc_NtkPrintOneDecomp_rec( uCofs, nCofs, nVars, uMaskNew, pBestSize, puBestMask ); + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintOneDecomp( unsigned * pTruth, int nVars ) +{ + int BoundSet = 6; + unsigned uCofs[64], uMask, uBestMask = 0; + int i, nCofs, nMints, nMintShift, BestSize = 1; + + assert( nVars > BoundSet ); + assert( nVars <= BoundSet + 5 ); // at most 5 variable cofactors + + // collect the cofactors + nCofs = (1 << BoundSet); + nMints = (1 << (nVars-BoundSet)); + nMintShift = 0; + uMask = Kit_CubeMask( nMints ); + for ( i = 0; i < nCofs; i++ ) + { + uCofs[i] = (pTruth[nMintShift/32] >> (nMintShift % 32)) & uMask; + nMintShift += nMints; + } + + // try removing variables + for ( i = 0; i < BoundSet; i++ ) + Abc_NtkPrintOneDecomp_rec( uCofs, nCofs, nVars, (1 << i), &BestSize, &uBestMask ); + + printf( "Best size = %d ", BestSize ); + printf( "Best mask = " ); + Extra_PrintBinary( stdout, &uBestMask, nVars ); + printf( "\n" ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintOneDec( unsigned * pTruth, int nVars ) +{ + unsigned uCof[(1<<11)], * pOut = uCof, * pIn = pTruth, * pTemp; + int nDiffs[16]; + int Order[16]; + int i, fChange, Temp, Counter; + + // find the ordering + for ( i = 0; i < nVars; i++ ) + { + Kit_TruthUniqueNew( uCof, pTruth, nVars, i ); + nDiffs[i] = Kit_TruthCountOnes( uCof, nVars ); + Order[i] = i; + } + + // permute truth table to least active variable first + Counter = 0; + do { + fChange = 0; + for ( i = 0; i < nVars-1; i++ ) + { + if ( nDiffs[i] <= nDiffs[i+1] ) + continue; + fChange = 1; + Counter++; + + Temp = nDiffs[i]; + nDiffs[i] = nDiffs[i+1]; + nDiffs[i+1] = Temp; + + Temp = Order[i]; + Order[i] = Order[i+1]; + Order[i+1] = Temp; + + Extra_TruthSwapAdjacentVars( pOut, pIn, nVars, i ); + pTemp = pIn; pIn = pOut; pOut = pTemp; + } + } while ( fChange ); + + // swap if it was moved an even number of times + if ( Counter & 1 ) + Extra_TruthCopy( pOut, pIn, nVars ); + + // call the decomposition + Abc_NtkPrintOneDecomp( pTruth, nVars ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcMini.c b/abc_with_bb_support/src/base/abci/abcMini.c new file mode 100644 index 000000000..b528315c8 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcMini.c @@ -0,0 +1,153 @@ +/**CFile**************************************************************** + + FileName [abcMini.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Interface to the minimalistic AIG package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcMini.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Hop_Man_t * Abc_NtkToMini( Abc_Ntk_t * pNtk ); +static Abc_Ntk_t * Abc_NtkFromMini( Abc_Ntk_t * pNtkOld, Hop_Man_t * pMan ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Gives the current ABC network to AIG manager for processing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkMiniBalance( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkAig; + Hop_Man_t * pMan, * pTemp; + assert( Abc_NtkIsStrash(pNtk) ); + // convert to the AIG manager + pMan = Abc_NtkToMini( pNtk ); + if ( pMan == NULL ) + return NULL; + if ( !Hop_ManCheck( pMan ) ) + { + printf( "AIG check has failed.\n" ); + Hop_ManStop( pMan ); + return NULL; + } + // perform balance + Hop_ManPrintStats( pMan ); +// Hop_ManDumpBlif( pMan, "aig_temp.blif" ); + pMan = Hop_ManBalance( pTemp = pMan, 1 ); + Hop_ManStop( pTemp ); + Hop_ManPrintStats( pMan ); + // convert from the AIG manager + pNtkAig = Abc_NtkFromMini( pNtk, pMan ); + if ( pNtkAig == NULL ) + return NULL; + Hop_ManStop( pMan ); + // make sure everything is okay + if ( !Abc_NtkCheck( pNtkAig ) ) + { + printf( "Abc_NtkStrash: The network check has failed.\n" ); + Abc_NtkDelete( pNtkAig ); + return NULL; + } + return pNtkAig; +} + +/**Function************************************************************* + + Synopsis [Converts the network from the AIG manager into ABC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Man_t * Abc_NtkToMini( Abc_Ntk_t * pNtk ) +{ + Hop_Man_t * pMan; + Abc_Obj_t * pObj; + int i; + // create the manager + pMan = Hop_ManStart(); + // transfer the pointers to the basic nodes + Abc_AigConst1(pNtk)->pCopy = (Abc_Obj_t *)Hop_ManConst1(pMan); + Abc_NtkForEachCi( pNtk, pObj, i ) + pObj->pCopy = (Abc_Obj_t *)Hop_ObjCreatePi(pMan); + // perform the conversion of the internal nodes (assumes DFS ordering) + Abc_NtkForEachNode( pNtk, pObj, i ) + pObj->pCopy = (Abc_Obj_t *)Hop_And( pMan, (Hop_Obj_t *)Abc_ObjChild0Copy(pObj), (Hop_Obj_t *)Abc_ObjChild1Copy(pObj) ); + // create the POs + Abc_NtkForEachCo( pNtk, pObj, i ) + Hop_ObjCreatePo( pMan, (Hop_Obj_t *)Abc_ObjChild0Copy(pObj) ); + Hop_ManCleanup( pMan ); + return pMan; +} + +/**Function************************************************************* + + Synopsis [Converts the network from the AIG manager into ABC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFromMini( Abc_Ntk_t * pNtk, Hop_Man_t * pMan ) +{ + Vec_Ptr_t * vNodes; + Abc_Ntk_t * pNtkNew; + Hop_Obj_t * pObj; + int i; + // perform strashing + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_STRASH, ABC_FUNC_AIG ); + // transfer the pointers to the basic nodes + Hop_ManConst1(pMan)->pData = Abc_AigConst1(pNtkNew); + Hop_ManForEachPi( pMan, pObj, i ) + pObj->pData = Abc_NtkCi(pNtkNew, i); + // rebuild the AIG + vNodes = Hop_ManDfs( pMan ); + Vec_PtrForEachEntry( vNodes, pObj, i ) + pObj->pData = Abc_AigAnd( pNtkNew->pManFunc, (Abc_Obj_t *)Hop_ObjChild0Copy(pObj), (Abc_Obj_t *)Hop_ObjChild1Copy(pObj) ); + Vec_PtrFree( vNodes ); + // connect the PO nodes + Hop_ManForEachPo( pMan, pObj, i ) + Abc_ObjAddFanin( Abc_NtkCo(pNtkNew, i), (Abc_Obj_t *)Hop_ObjChild0Copy(pObj) ); + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkFromMini(): Network check has failed.\n" ); + return pNtkNew; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcMiter.c b/abc_with_bb_support/src/base/abci/abcMiter.c new file mode 100644 index 000000000..4a1f055d1 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcMiter.c @@ -0,0 +1,1138 @@ +/**CFile**************************************************************** + + FileName [abcMiter.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Procedures to derive the miter of two circuits.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcMiter.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Abc_Ntk_t * Abc_NtkMiterInt( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int fComb, int nPartSize ); +static void Abc_NtkMiterPrepare( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, Abc_Ntk_t * pNtkMiter, int fComb, int nPartSize ); +static void Abc_NtkMiterAddOne( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkMiter ); +static void Abc_NtkMiterFinalize( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, Abc_Ntk_t * pNtkMiter, int fComb, int nPartSize ); +static void Abc_NtkAddFrame( Abc_Ntk_t * pNetNew, Abc_Ntk_t * pNet, int iFrame ); + +// to be exported +typedef void (*AddFrameMapping)( Abc_Obj_t*, Abc_Obj_t*, int, void*); +extern Abc_Ntk_t * Abc_NtkFrames2( Abc_Ntk_t * pNtk, int nFrames, int fInitial, AddFrameMapping addFrameMapping, void* arg ); +static void Abc_NtkAddFrame2( Abc_Ntk_t * pNtkFrames, Abc_Ntk_t * pNtk, int iFrame, Vec_Ptr_t * vNodes, AddFrameMapping addFrameMapping, void* arg ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Derives the miter of two networks.] + + Description [Preprocesses the networks to make sure that they are strashed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkMiter( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int fComb, int nPartSize ) +{ + Abc_Ntk_t * pTemp = NULL; + int fRemove1, fRemove2; + assert( Abc_NtkHasOnlyLatchBoxes(pNtk1) ); + assert( Abc_NtkHasOnlyLatchBoxes(pNtk2) ); + // check that the networks have the same PIs/POs/latches + if ( !Abc_NtkCompareSignals( pNtk1, pNtk2, 0, fComb ) ) + return NULL; + // make sure the circuits are strashed + fRemove1 = (!Abc_NtkIsStrash(pNtk1)) && (pNtk1 = Abc_NtkStrash(pNtk1, 0, 0, 0)); + fRemove2 = (!Abc_NtkIsStrash(pNtk2)) && (pNtk2 = Abc_NtkStrash(pNtk2, 0, 0, 0)); + if ( pNtk1 && pNtk2 ) + pTemp = Abc_NtkMiterInt( pNtk1, pNtk2, fComb, nPartSize ); + if ( fRemove1 ) Abc_NtkDelete( pNtk1 ); + if ( fRemove2 ) Abc_NtkDelete( pNtk2 ); + return pTemp; +} + +/**Function************************************************************* + + Synopsis [Derives the miter of two sequential networks.] + + Description [Assumes that the networks are strashed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkMiterInt( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int fComb, int nPartSize ) +{ + char Buffer[1000]; + Abc_Ntk_t * pNtkMiter; + + assert( Abc_NtkIsStrash(pNtk1) ); + assert( Abc_NtkIsStrash(pNtk2) ); + + // start the new network + pNtkMiter = Abc_NtkAlloc( ABC_NTK_STRASH, ABC_FUNC_AIG, 1 ); + sprintf( Buffer, "%s_%s_miter", pNtk1->pName, pNtk2->pName ); + pNtkMiter->pName = Extra_UtilStrsav(Buffer); + + // perform strashing + Abc_NtkMiterPrepare( pNtk1, pNtk2, pNtkMiter, fComb, nPartSize ); + Abc_NtkMiterAddOne( pNtk1, pNtkMiter ); + Abc_NtkMiterAddOne( pNtk2, pNtkMiter ); + Abc_NtkMiterFinalize( pNtk1, pNtk2, pNtkMiter, fComb, nPartSize ); + Abc_AigCleanup(pNtkMiter->pManFunc); + + // make sure that everything is okay + if ( !Abc_NtkCheck( pNtkMiter ) ) + { + printf( "Abc_NtkMiter: The network check has failed.\n" ); + Abc_NtkDelete( pNtkMiter ); + return NULL; + } + return pNtkMiter; +} + +/**Function************************************************************* + + Synopsis [Prepares the network for mitering.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMiterPrepare( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, Abc_Ntk_t * pNtkMiter, int fComb, int nPartSize ) +{ + Abc_Obj_t * pObj, * pObjNew; + int i; + // clean the copy field in all objects +// Abc_NtkCleanCopy( pNtk1 ); +// Abc_NtkCleanCopy( pNtk2 ); + Abc_AigConst1(pNtk1)->pCopy = Abc_AigConst1(pNtkMiter); + Abc_AigConst1(pNtk2)->pCopy = Abc_AigConst1(pNtkMiter); + + if ( fComb ) + { + // create new PIs and remember them in the old PIs + Abc_NtkForEachCi( pNtk1, pObj, i ) + { + pObjNew = Abc_NtkCreatePi( pNtkMiter ); + // remember this PI in the old PIs + pObj->pCopy = pObjNew; + pObj = Abc_NtkCi(pNtk2, i); + pObj->pCopy = pObjNew; + // add name + Abc_ObjAssignName( pObjNew, Abc_ObjName(pObj), NULL ); + } + if ( nPartSize <= 0 ) + { + // create the only PO + pObjNew = Abc_NtkCreatePo( pNtkMiter ); + // add the PO name + Abc_ObjAssignName( pObjNew, "miter", NULL ); + } + } + else + { + // create new PIs and remember them in the old PIs + Abc_NtkForEachPi( pNtk1, pObj, i ) + { + pObjNew = Abc_NtkCreatePi( pNtkMiter ); + // remember this PI in the old PIs + pObj->pCopy = pObjNew; + pObj = Abc_NtkPi(pNtk2, i); + pObj->pCopy = pObjNew; + // add name + Abc_ObjAssignName( pObjNew, Abc_ObjName(pObj), NULL ); + } + if ( nPartSize <= 0 ) + { + // create the only PO + pObjNew = Abc_NtkCreatePo( pNtkMiter ); + // add the PO name + Abc_ObjAssignName( pObjNew, "miter", NULL ); + } + // create the latches + Abc_NtkForEachLatch( pNtk1, pObj, i ) + { + pObjNew = Abc_NtkDupBox( pNtkMiter, pObj, 0 ); + // add names + Abc_ObjAssignName( pObjNew, Abc_ObjName(pObj), "_1" ); + Abc_ObjAssignName( Abc_ObjFanin0(pObjNew), Abc_ObjName(Abc_ObjFanin0(pObj)), "_1" ); + Abc_ObjAssignName( Abc_ObjFanout0(pObjNew), Abc_ObjName(Abc_ObjFanout0(pObj)), "_1" ); + } + Abc_NtkForEachLatch( pNtk2, pObj, i ) + { + pObjNew = Abc_NtkDupBox( pNtkMiter, pObj, 0 ); + // add name + Abc_ObjAssignName( pObjNew, Abc_ObjName(pObj), "_2" ); + Abc_ObjAssignName( Abc_ObjFanin0(pObjNew), Abc_ObjName(Abc_ObjFanin0(pObj)), "_2" ); + Abc_ObjAssignName( Abc_ObjFanout0(pObjNew), Abc_ObjName(Abc_ObjFanout0(pObj)), "_2" ); + } + } +} + +/**Function************************************************************* + + Synopsis [Performs mitering for one network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMiterAddOne( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkMiter ) +{ + Abc_Obj_t * pNode; + int i; + assert( Abc_NtkIsDfsOrdered(pNtk) ); + Abc_AigForEachAnd( pNtk, pNode, i ) + pNode->pCopy = Abc_AigAnd( pNtkMiter->pManFunc, Abc_ObjChild0Copy(pNode), Abc_ObjChild1Copy(pNode) ); +} + +/**Function************************************************************* + + Synopsis [Performs mitering for one network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMiterAddCone( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkMiter, Abc_Obj_t * pRoot ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pNode; + int i; + // map the constant nodes + Abc_AigConst1(pNtk)->pCopy = Abc_AigConst1(pNtkMiter); + // perform strashing + vNodes = Abc_NtkDfsNodes( pNtk, &pRoot, 1 ); + Vec_PtrForEachEntry( vNodes, pNode, i ) + if ( Abc_AigNodeIsAnd(pNode) ) + pNode->pCopy = Abc_AigAnd( pNtkMiter->pManFunc, Abc_ObjChild0Copy(pNode), Abc_ObjChild1Copy(pNode) ); + Vec_PtrFree( vNodes ); +} + + +/**Function************************************************************* + + Synopsis [Finalizes the miter by adding the output part.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMiterFinalize( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, Abc_Ntk_t * pNtkMiter, int fComb, int nPartSize ) +{ + Vec_Ptr_t * vPairs; + Abc_Obj_t * pMiter, * pNode; + int i; + // collect the PO pairs from both networks + vPairs = Vec_PtrAlloc( 100 ); + if ( fComb ) + { + // collect the CO nodes for the miter + Abc_NtkForEachCo( pNtk1, pNode, i ) + { + Vec_PtrPush( vPairs, Abc_ObjChild0Copy(pNode) ); + pNode = Abc_NtkCo( pNtk2, i ); + Vec_PtrPush( vPairs, Abc_ObjChild0Copy(pNode) ); + } + } + else + { + // collect the PO nodes for the miter + Abc_NtkForEachPo( pNtk1, pNode, i ) + { + Vec_PtrPush( vPairs, Abc_ObjChild0Copy(pNode) ); + pNode = Abc_NtkPo( pNtk2, i ); + Vec_PtrPush( vPairs, Abc_ObjChild0Copy(pNode) ); + } + // connect new latches + Abc_NtkForEachLatch( pNtk1, pNode, i ) + Abc_ObjAddFanin( Abc_ObjFanin0(pNode)->pCopy, Abc_ObjChild0Copy(Abc_ObjFanin0(pNode)) ); + Abc_NtkForEachLatch( pNtk2, pNode, i ) + Abc_ObjAddFanin( Abc_ObjFanin0(pNode)->pCopy, Abc_ObjChild0Copy(Abc_ObjFanin0(pNode)) ); + } + // add the miter + if ( nPartSize <= 0 ) + { + pMiter = Abc_AigMiter( pNtkMiter->pManFunc, vPairs ); + Abc_ObjAddFanin( Abc_NtkPo(pNtkMiter,0), pMiter ); + Vec_PtrFree( vPairs ); + } + else + { + char Buffer[1024]; + Vec_Ptr_t * vPairsPart; + int nParts, i, k, iCur; + assert( Vec_PtrSize(vPairs) == 2 * Abc_NtkCoNum(pNtk1) ); + // create partitions + nParts = Abc_NtkCoNum(pNtk1) / nPartSize + (int)((Abc_NtkCoNum(pNtk1) % nPartSize) > 0); + vPairsPart = Vec_PtrAlloc( nPartSize ); + for ( i = 0; i < nParts; i++ ) + { + Vec_PtrClear( vPairsPart ); + for ( k = 0; k < nPartSize; k++ ) + { + iCur = i * nPartSize + k; + if ( iCur >= Abc_NtkCoNum(pNtk1) ) + break; + Vec_PtrPush( vPairsPart, Vec_PtrEntry(vPairs, 2*iCur ) ); + Vec_PtrPush( vPairsPart, Vec_PtrEntry(vPairs, 2*iCur+1) ); + } + pMiter = Abc_AigMiter( pNtkMiter->pManFunc, vPairsPart ); + pNode = Abc_NtkCreatePo( pNtkMiter ); + Abc_ObjAddFanin( pNode, pMiter ); + // assign the name to the node + if ( nPartSize == 1 ) + sprintf( Buffer, "%s", Abc_ObjName(Abc_NtkPo(pNtk1,i)) ); + else + sprintf( Buffer, "%d", i ); + Abc_ObjAssignName( pNode, "miter_", Buffer ); + } + Vec_PtrFree( vPairsPart ); + Vec_PtrFree( vPairs ); + } +} + + + +/**Function************************************************************* + + Synopsis [Derives the AND of two miters.] + + Description [The network should have the same names of PIs.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkMiterAnd( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int fOr, int fCompl2 ) +{ + char Buffer[1000]; + Abc_Ntk_t * pNtkMiter; + Abc_Obj_t * pOutput1, * pOutput2; + Abc_Obj_t * pRoot1, * pRoot2, * pMiter; + + assert( Abc_NtkIsStrash(pNtk1) ); + assert( Abc_NtkIsStrash(pNtk2) ); + assert( 1 == Abc_NtkCoNum(pNtk1) ); + assert( 1 == Abc_NtkCoNum(pNtk2) ); + assert( 0 == Abc_NtkLatchNum(pNtk1) ); + assert( 0 == Abc_NtkLatchNum(pNtk2) ); + assert( Abc_NtkCiNum(pNtk1) == Abc_NtkCiNum(pNtk2) ); + assert( Abc_NtkHasOnlyLatchBoxes(pNtk1) ); + assert( Abc_NtkHasOnlyLatchBoxes(pNtk2) ); + + // start the new network + pNtkMiter = Abc_NtkAlloc( ABC_NTK_STRASH, ABC_FUNC_AIG, 1 ); +// sprintf( Buffer, "%s_%s_miter", pNtk1->pName, pNtk2->pName ); + sprintf( Buffer, "product" ); + pNtkMiter->pName = Extra_UtilStrsav(Buffer); + + // perform strashing + Abc_NtkMiterPrepare( pNtk1, pNtk2, pNtkMiter, 1, -1 ); + Abc_NtkMiterAddOne( pNtk1, pNtkMiter ); + Abc_NtkMiterAddOne( pNtk2, pNtkMiter ); +// Abc_NtkMiterFinalize( pNtk1, pNtk2, pNtkMiter, 1 ); + pRoot1 = Abc_NtkPo(pNtk1,0); + pRoot2 = Abc_NtkPo(pNtk2,0); + pOutput1 = Abc_ObjNotCond( Abc_ObjFanin0(pRoot1)->pCopy, Abc_ObjFaninC0(pRoot1) ); + pOutput2 = Abc_ObjNotCond( Abc_ObjFanin0(pRoot2)->pCopy, Abc_ObjFaninC0(pRoot2) ^ fCompl2 ); + + // create the miter of the two outputs + if ( fOr ) + pMiter = Abc_AigOr( pNtkMiter->pManFunc, pOutput1, pOutput2 ); + else + pMiter = Abc_AigAnd( pNtkMiter->pManFunc, pOutput1, pOutput2 ); + Abc_ObjAddFanin( Abc_NtkPo(pNtkMiter,0), pMiter ); + + // make sure that everything is okay + if ( !Abc_NtkCheck( pNtkMiter ) ) + { + printf( "Abc_NtkMiterAnd: The network check has failed.\n" ); + Abc_NtkDelete( pNtkMiter ); + return NULL; + } + return pNtkMiter; +} + + +/**Function************************************************************* + + Synopsis [Derives the cofactor of the miter w.r.t. the set of vars.] + + Description [The array of variable values contains -1/0/1 for each PI. + -1 means this PI remains, 0/1 means this PI is set to 0/1.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkMiterCofactor( Abc_Ntk_t * pNtk, Vec_Int_t * vPiValues ) +{ + char Buffer[1000]; + Abc_Ntk_t * pNtkMiter; + Abc_Obj_t * pRoot, * pOutput1; + int Value, i; + + assert( Abc_NtkIsStrash(pNtk) ); + assert( 1 == Abc_NtkCoNum(pNtk) ); + assert( Abc_NtkHasOnlyLatchBoxes(pNtk) ); + + // start the new network + pNtkMiter = Abc_NtkAlloc( ABC_NTK_STRASH, ABC_FUNC_AIG, 1 ); + sprintf( Buffer, "%s_miter", pNtk->pName ); + pNtkMiter->pName = Extra_UtilStrsav(Buffer); + + // get the root output + pRoot = Abc_NtkCo( pNtk, 0 ); + + // perform strashing + Abc_NtkMiterPrepare( pNtk, pNtk, pNtkMiter, 1, -1 ); + // set the first cofactor + Vec_IntForEachEntry( vPiValues, Value, i ) + { + if ( Value == -1 ) + continue; + if ( Value == 0 ) + { + Abc_NtkCi(pNtk, i)->pCopy = Abc_ObjNot( Abc_AigConst1(pNtkMiter) ); + continue; + } + if ( Value == 1 ) + { + Abc_NtkCi(pNtk, i)->pCopy = Abc_AigConst1(pNtkMiter); + continue; + } + assert( 0 ); + } + // add the first cofactor + Abc_NtkMiterAddCone( pNtk, pNtkMiter, pRoot ); + + // save the output + pOutput1 = Abc_ObjNotCond( Abc_ObjFanin0(pRoot)->pCopy, Abc_ObjFaninC0(pRoot) ); + + // create the miter of the two outputs + Abc_ObjAddFanin( Abc_NtkPo(pNtkMiter,0), pOutput1 ); + + // make sure that everything is okay + if ( !Abc_NtkCheck( pNtkMiter ) ) + { + printf( "Abc_NtkMiterCofactor: The network check has failed.\n" ); + Abc_NtkDelete( pNtkMiter ); + return NULL; + } + return pNtkMiter; +} +/**Function************************************************************* + + Synopsis [Derives the miter of two cofactors of one output.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkMiterForCofactors( Abc_Ntk_t * pNtk, int Out, int In1, int In2 ) +{ + char Buffer[1000]; + Abc_Ntk_t * pNtkMiter; + Abc_Obj_t * pRoot, * pOutput1, * pOutput2, * pMiter; + + assert( Abc_NtkIsStrash(pNtk) ); + assert( Out < Abc_NtkCoNum(pNtk) ); + assert( In1 < Abc_NtkCiNum(pNtk) ); + assert( In2 < Abc_NtkCiNum(pNtk) ); + assert( Abc_NtkHasOnlyLatchBoxes(pNtk) ); + + // start the new network + pNtkMiter = Abc_NtkAlloc( ABC_NTK_STRASH, ABC_FUNC_AIG, 1 ); + sprintf( Buffer, "%s_miter", Abc_ObjName(Abc_NtkCo(pNtk, Out)) ); + pNtkMiter->pName = Extra_UtilStrsav(Buffer); + + // get the root output + pRoot = Abc_NtkCo( pNtk, Out ); + + // perform strashing + Abc_NtkMiterPrepare( pNtk, pNtk, pNtkMiter, 1, -1 ); + // set the first cofactor + Abc_NtkCi(pNtk, In1)->pCopy = Abc_ObjNot( Abc_AigConst1(pNtkMiter) ); + if ( In2 >= 0 ) + Abc_NtkCi(pNtk, In2)->pCopy = Abc_AigConst1(pNtkMiter); + // add the first cofactor + Abc_NtkMiterAddCone( pNtk, pNtkMiter, pRoot ); + + // save the output + pOutput1 = Abc_ObjFanin0(pRoot)->pCopy; + + // set the second cofactor + Abc_NtkCi(pNtk, In1)->pCopy = Abc_AigConst1(pNtkMiter); + if ( In2 >= 0 ) + Abc_NtkCi(pNtk, In2)->pCopy = Abc_ObjNot( Abc_AigConst1(pNtkMiter) ); + // add the second cofactor + Abc_NtkMiterAddCone( pNtk, pNtkMiter, pRoot ); + + // save the output + pOutput2 = Abc_ObjFanin0(pRoot)->pCopy; + + // create the miter of the two outputs + pMiter = Abc_AigXor( pNtkMiter->pManFunc, pOutput1, pOutput2 ); + Abc_ObjAddFanin( Abc_NtkPo(pNtkMiter,0), pMiter ); + + // make sure that everything is okay + if ( !Abc_NtkCheck( pNtkMiter ) ) + { + printf( "Abc_NtkMiter: The network check has failed.\n" ); + Abc_NtkDelete( pNtkMiter ); + return NULL; + } + return pNtkMiter; +} + + +/**Function************************************************************* + + Synopsis [Derives the miter of two cofactors of one output.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkMiterQuantify( Abc_Ntk_t * pNtk, int In, int fExist ) +{ + Abc_Ntk_t * pNtkMiter; + Abc_Obj_t * pRoot, * pOutput1, * pOutput2, * pMiter; + + assert( Abc_NtkIsStrash(pNtk) ); + assert( 1 == Abc_NtkCoNum(pNtk) ); + assert( In < Abc_NtkCiNum(pNtk) ); + assert( Abc_NtkHasOnlyLatchBoxes(pNtk) ); + + // start the new network + pNtkMiter = Abc_NtkAlloc( ABC_NTK_STRASH, ABC_FUNC_AIG, 1 ); + pNtkMiter->pName = Extra_UtilStrsav( Abc_ObjName(Abc_NtkCo(pNtk, 0)) ); + + // get the root output + pRoot = Abc_NtkCo( pNtk, 0 ); + + // perform strashing + Abc_NtkMiterPrepare( pNtk, pNtk, pNtkMiter, 1, -1 ); + // set the first cofactor + Abc_NtkCi(pNtk, In)->pCopy = Abc_ObjNot( Abc_AigConst1(pNtkMiter) ); + // add the first cofactor + Abc_NtkMiterAddCone( pNtk, pNtkMiter, pRoot ); + // save the output +// pOutput1 = Abc_ObjFanin0(pRoot)->pCopy; + pOutput1 = Abc_ObjNotCond( Abc_ObjFanin0(pRoot)->pCopy, Abc_ObjFaninC0(pRoot) ); + + // set the second cofactor + Abc_NtkCi(pNtk, In)->pCopy = Abc_AigConst1(pNtkMiter); + // add the second cofactor + Abc_NtkMiterAddCone( pNtk, pNtkMiter, pRoot ); + // save the output +// pOutput2 = Abc_ObjFanin0(pRoot)->pCopy; + pOutput2 = Abc_ObjNotCond( Abc_ObjFanin0(pRoot)->pCopy, Abc_ObjFaninC0(pRoot) ); + + // create the miter of the two outputs + if ( fExist ) + pMiter = Abc_AigOr( pNtkMiter->pManFunc, pOutput1, pOutput2 ); + else + pMiter = Abc_AigAnd( pNtkMiter->pManFunc, pOutput1, pOutput2 ); + Abc_ObjAddFanin( Abc_NtkPo(pNtkMiter,0), pMiter ); + + // make sure that everything is okay + if ( !Abc_NtkCheck( pNtkMiter ) ) + { + printf( "Abc_NtkMiter: The network check has failed.\n" ); + Abc_NtkDelete( pNtkMiter ); + return NULL; + } + return pNtkMiter; +} + +/**Function************************************************************* + + Synopsis [Quantifies all the PIs existentially from the only PO of the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkMiterQuantifyPis( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkTemp; + Abc_Obj_t * pObj; + int i; + assert( Abc_NtkHasOnlyLatchBoxes(pNtk) ); + + Abc_NtkForEachPi( pNtk, pObj, i ) + { + if ( Abc_ObjFanoutNum(pObj) == 0 ) + continue; + pNtk = Abc_NtkMiterQuantify( pNtkTemp = pNtk, i, 1 ); + Abc_NtkDelete( pNtkTemp ); + } + + return pNtk; +} + + + + +/**Function************************************************************* + + Synopsis [Checks the status of the miter.] + + Description [Return 0 if the miter is sat for at least one output. + Return 1 if the miter is unsat for all its outputs. Returns -1 if the + miter is undecided for some outputs.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkMiterIsConstant( Abc_Ntk_t * pMiter ) +{ + Abc_Obj_t * pNodePo, * pChild; + int i; + assert( Abc_NtkIsStrash(pMiter) ); + Abc_NtkForEachPo( pMiter, pNodePo, i ) + { + pChild = Abc_ObjChild0( pNodePo ); + if ( Abc_AigNodeIsConst(pChild) ) + { + assert( Abc_ObjRegular(pChild) == Abc_AigConst1(pMiter) ); + if ( !Abc_ObjIsComplement(pChild) ) + { + // if the miter is constant 1, return immediately +// printf( "MITER IS CONSTANT 1!\n" ); + return 0; + } + } + // if the miter is undecided (or satisfiable), return immediately + else + return -1; + } + // return 1, meaning all outputs are constant zero + return 1; +} + +/**Function************************************************************* + + Synopsis [Reports the status of the miter.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMiterReport( Abc_Ntk_t * pMiter ) +{ + Abc_Obj_t * pChild, * pNode; + int i; + if ( Abc_NtkPoNum(pMiter) == 1 ) + { + pChild = Abc_ObjChild0( Abc_NtkPo(pMiter,0) ); + if ( Abc_AigNodeIsConst(pChild) ) + { + if ( Abc_ObjIsComplement(pChild) ) + printf( "Unsatisfiable.\n" ); + else + printf( "Satisfiable. (Constant 1).\n" ); + } + else + printf( "Satisfiable.\n" ); + } + else + { + Abc_NtkForEachPo( pMiter, pNode, i ) + { + pChild = Abc_ObjChild0( Abc_NtkPo(pMiter,i) ); + printf( "Output #%2d : ", i ); + if ( Abc_AigNodeIsConst(pChild) ) + { + if ( Abc_ObjIsComplement(pChild) ) + printf( "Unsatisfiable.\n" ); + else + printf( "Satisfiable. (Constant 1).\n" ); + } + else + printf( "Satisfiable.\n" ); + } + } +} + + +/**Function************************************************************* + + Synopsis [Derives the timeframes of the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFrames( Abc_Ntk_t * pNtk, int nFrames, int fInitial ) +{ + char Buffer[1000]; + ProgressBar * pProgress; + Abc_Ntk_t * pNtkFrames; + Abc_Obj_t * pLatch, * pLatchOut; + int i, Counter; + assert( nFrames > 0 ); + assert( Abc_NtkIsStrash(pNtk) ); + assert( Abc_NtkIsDfsOrdered(pNtk) ); + assert( Abc_NtkHasOnlyLatchBoxes(pNtk) ); + // start the new network + pNtkFrames = Abc_NtkAlloc( ABC_NTK_STRASH, ABC_FUNC_AIG, 1 ); + sprintf( Buffer, "%s_%d_frames", pNtk->pName, nFrames ); + pNtkFrames->pName = Extra_UtilStrsav(Buffer); + // map the constant nodes + Abc_AigConst1(pNtk)->pCopy = Abc_AigConst1(pNtkFrames); + // create new latches (or their initial values) and remember them in the new latches + if ( !fInitial ) + { + Abc_NtkForEachLatch( pNtk, pLatch, i ) + Abc_NtkDupBox( pNtkFrames, pLatch, 1 ); + } + else + { + Counter = 0; + Abc_NtkForEachLatch( pNtk, pLatch, i ) + { + pLatchOut = Abc_ObjFanout0(pLatch); + if ( Abc_LatchIsInitNone(pLatch) || Abc_LatchIsInitDc(pLatch) ) // don't-care initial value - create a new PI + { + pLatchOut->pCopy = Abc_NtkCreatePi(pNtkFrames); + Abc_ObjAssignName( pLatchOut->pCopy, Abc_ObjName(pLatchOut), NULL ); + Counter++; + } + else + pLatchOut->pCopy = Abc_ObjNotCond( Abc_AigConst1(pNtkFrames), Abc_LatchIsInit0(pLatch) ); + } + if ( Counter ) + printf( "Warning: %d uninitialized latches are replaced by free PI variables.\n", Counter ); + } + + // create the timeframes + pProgress = Extra_ProgressBarStart( stdout, nFrames ); + for ( i = 0; i < nFrames; i++ ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + Abc_NtkAddFrame( pNtkFrames, pNtk, i ); + } + Extra_ProgressBarStop( pProgress ); + + // connect the new latches to the outputs of the last frame + if ( !fInitial ) + { + // we cannot use pLatch->pCopy here because pLatch->pCopy is used for temporary storage of strashed values + Abc_NtkForEachLatch( pNtk, pLatch, i ) + Abc_ObjAddFanin( Abc_ObjFanin0(pLatch)->pCopy, Abc_ObjFanout0(pLatch)->pCopy ); + } + + // remove dangling nodes + Abc_AigCleanup( pNtkFrames->pManFunc ); + // reorder the latches + Abc_NtkOrderCisCos( pNtkFrames ); + // make sure that everything is okay + if ( !Abc_NtkCheck( pNtkFrames ) ) + { + printf( "Abc_NtkFrames: The network check has failed.\n" ); + Abc_NtkDelete( pNtkFrames ); + return NULL; + } + return pNtkFrames; +} + +/**Function************************************************************* + + Synopsis [Adds one time frame to the new network.] + + Description [Assumes that the latches of the old network point + to the outputs of the previous frame of the new network (pLatch->pCopy). + In the end, updates the latches of the old network to point to the + outputs of the current frame of the new network.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkAddFrame( Abc_Ntk_t * pNtkFrames, Abc_Ntk_t * pNtk, int iFrame ) +{ + char Buffer[10]; + Abc_Obj_t * pNode, * pLatch; + int i; + // create the prefix to be added to the node names + sprintf( Buffer, "_%02d", iFrame ); + // add the new PI nodes + Abc_NtkForEachPi( pNtk, pNode, i ) + Abc_ObjAssignName( Abc_NtkDupObj(pNtkFrames, pNode, 0), Abc_ObjName(pNode), Buffer ); + // add the internal nodes + Abc_AigForEachAnd( pNtk, pNode, i ) + pNode->pCopy = Abc_AigAnd( pNtkFrames->pManFunc, Abc_ObjChild0Copy(pNode), Abc_ObjChild1Copy(pNode) ); + // add the new POs + Abc_NtkForEachPo( pNtk, pNode, i ) + { + Abc_ObjAssignName( Abc_NtkDupObj(pNtkFrames, pNode, 0), Abc_ObjName(pNode), Buffer ); + Abc_ObjAddFanin( pNode->pCopy, Abc_ObjChild0Copy(pNode) ); + } + // add the new asserts + Abc_NtkForEachAssert( pNtk, pNode, i ) + { + Abc_ObjAssignName( Abc_NtkDupObj(pNtkFrames, pNode, 0), Abc_ObjName(pNode), Buffer ); + Abc_ObjAddFanin( pNode->pCopy, Abc_ObjChild0Copy(pNode) ); + } + // transfer the implementation of the latch inputs to the latch outputs + Abc_NtkForEachLatch( pNtk, pLatch, i ) + pLatch->pCopy = Abc_ObjChild0Copy(Abc_ObjFanin0(pLatch)); + Abc_NtkForEachLatch( pNtk, pLatch, i ) + Abc_ObjFanout0(pLatch)->pCopy = pLatch->pCopy; +} + + + +/**Function************************************************************* + + Synopsis [Derives the timeframes of the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFrames2( Abc_Ntk_t * pNtk, int nFrames, int fInitial, AddFrameMapping addFrameMapping, void* arg ) +{ +/* + char Buffer[1000]; + ProgressBar * pProgress; + Abc_Ntk_t * pNtkFrames; + Vec_Ptr_t * vNodes; + Abc_Obj_t * pLatch, * pLatchNew; + int i, Counter; + assert( nFrames > 0 ); + assert( Abc_NtkIsStrash(pNtk) ); + // start the new network + pNtkFrames = Abc_NtkAlloc( ABC_NTK_STRASH, ABC_FUNC_AIG, 1 ); + sprintf( Buffer, "%s_%d_frames", pNtk->pName, nFrames ); + pNtkFrames->pName = Extra_UtilStrsav(Buffer); + // create new latches (or their initial values) and remember them in the new latches + if ( !fInitial ) + { + Abc_NtkForEachLatch( pNtk, pLatch, i ) { + Abc_NtkDupObj( pNtkFrames, pLatch ); + if (addFrameMapping) addFrameMapping(pLatch->pCopy, pLatch, 0, arg); + } + } + else + { + Counter = 0; + Abc_NtkForEachLatch( pNtk, pLatch, i ) + { + if ( Abc_LatchIsInitDc(pLatch) ) // don't-care initial value - create a new PI + { + pLatch->pCopy = Abc_NtkCreatePi(pNtkFrames); + Abc_ObjAssignName( pLatch->pCopy, Abc_ObjName(pLatch), NULL ); + Counter++; + } + else { + pLatch->pCopy = Abc_ObjNotCond( Abc_AigConst1(pNtkFrames), Abc_LatchIsInit0(pLatch) ); + } + + if (addFrameMapping) addFrameMapping(pLatch->pCopy, pLatch, 0, arg); + } + if ( Counter ) + printf( "Warning: %d uninitialized latches are replaced by free PI variables.\n", Counter ); + } + + // create the timeframes + vNodes = Abc_NtkDfs( pNtk, 0 ); + pProgress = Extra_ProgressBarStart( stdout, nFrames ); + for ( i = 0; i < nFrames; i++ ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + Abc_NtkAddFrame2( pNtkFrames, pNtk, i, vNodes, addFrameMapping, arg ); + } + Extra_ProgressBarStop( pProgress ); + Vec_PtrFree( vNodes ); + + // connect the new latches to the outputs of the last frame + if ( !fInitial ) + { + Abc_NtkForEachLatch( pNtk, pLatch, i ) + { + pLatchNew = Abc_NtkBox(pNtkFrames, i); + Abc_ObjAddFanin( pLatchNew, pLatch->pCopy ); + Abc_ObjAssignName( pLatchNew, Abc_ObjName(pLatch), NULL ); + } + } + Abc_NtkForEachLatch( pNtk, pLatch, i ) + pLatch->pNext = NULL; + + // remove dangling nodes + Abc_AigCleanup( pNtkFrames->pManFunc ); + + // reorder the latches + Abc_NtkOrderCisCos( pNtkFrames ); + + // make sure that everything is okay + if ( !Abc_NtkCheck( pNtkFrames ) ) + { + printf( "Abc_NtkFrames: The network check has failed.\n" ); + Abc_NtkDelete( pNtkFrames ); + return NULL; + } + return pNtkFrames; +*/ + return NULL; +} + +/**Function************************************************************* + + Synopsis [Adds one time frame to the new network.] + + Description [Assumes that the latches of the old network point + to the outputs of the previous frame of the new network (pLatch->pCopy). + In the end, updates the latches of the old network to point to the + outputs of the current frame of the new network.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkAddFrame2( Abc_Ntk_t * pNtkFrames, Abc_Ntk_t * pNtk, int iFrame, Vec_Ptr_t * vNodes, AddFrameMapping addFrameMapping, void* arg ) +{ +/* + char Buffer[10]; + Abc_Obj_t * pNode, * pNodeNew, * pLatch; + Abc_Obj_t * pConst1, * pConst1New; + int i; + // get the constant nodes + pConst1 = Abc_AigConst1(pNtk); + pConst1New = Abc_AigConst1(pNtkFrames); + // create the prefix to be added to the node names + sprintf( Buffer, "_%02d", iFrame ); + // add the new PI nodes + Abc_NtkForEachPi( pNtk, pNode, i ) + { + pNodeNew = Abc_NtkDupObj( pNtkFrames, pNode ); + Abc_ObjAssignName( pNodeNew, Abc_ObjName(pNode), Buffer ); + if (addFrameMapping) addFrameMapping(pNodeNew, pNode, iFrame, arg); + } + // add the internal nodes + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + if ( pNode == pConst1 ) + pNodeNew = pConst1New; + else + pNodeNew = Abc_AigAnd( pNtkFrames->pManFunc, Abc_ObjChild0Copy(pNode), Abc_ObjChild1Copy(pNode) ); + pNode->pCopy = pNodeNew; + if (addFrameMapping) addFrameMapping(pNodeNew, pNode, iFrame, arg); + } + // add the new POs + Abc_NtkForEachPo( pNtk, pNode, i ) + { + pNodeNew = Abc_NtkDupObj( pNtkFrames, pNode ); + Abc_ObjAddFanin( pNodeNew, Abc_ObjChild0Copy(pNode) ); + Abc_ObjAssignName( pNodeNew, Abc_ObjName(pNode), Buffer ); + if (addFrameMapping) addFrameMapping(pNodeNew, pNode, iFrame, arg); + } + // transfer the implementation of the latch drivers to the latches + + // it is important that these two steps are performed it two loops + // and not in the same loop + Abc_NtkForEachLatch( pNtk, pLatch, i ) + pLatch->pNext = Abc_ObjChild0Copy(pLatch); + Abc_NtkForEachLatch( pNtk, pLatch, i ) + pLatch->pCopy = pLatch->pNext; + + Abc_NtkForEachLatch( pNtk, pLatch, i ) + { + if (addFrameMapping) { + // don't give Mike complemented pointers because he doesn't like it + if (Abc_ObjIsComplement(pLatch->pCopy)) { + pNodeNew = Abc_NtkCreateNode( pNtkFrames ); + Abc_ObjAddFanin( pNodeNew, pLatch->pCopy ); + assert(Abc_ObjFaninNum(pNodeNew) == 1); + pNodeNew->Level = 1 + Abc_ObjRegular(pLatch->pCopy)->Level; + + pLatch->pNext = pNodeNew; + pLatch->pCopy = pNodeNew; + } + addFrameMapping(pLatch->pCopy, pLatch, iFrame+1, arg); + } + } +*/ +} + + + +/**Function************************************************************* + + Synopsis [Splits the miter into two logic cones combined by an EXOR] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkDemiter( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNodeC, * pNodeA, * pNodeB, * pNode; + Abc_Obj_t * pPoNew; + Vec_Ptr_t * vNodes1, * vNodes2; + int nCommon, i; + + assert( Abc_NtkIsStrash(pNtk) ); + assert( Abc_NtkPoNum(pNtk) == 1 ); + if ( !Abc_NodeIsExorType(Abc_ObjFanin0(Abc_NtkPo(pNtk,0))) ) + { + printf( "The root of the miter is not an EXOR gate.\n" ); + return 0; + } + pNodeC = Abc_NodeRecognizeMux( Abc_ObjFanin0(Abc_NtkPo(pNtk,0)), &pNodeA, &pNodeB ); + assert( Abc_ObjRegular(pNodeA) == Abc_ObjRegular(pNodeB) ); + if ( Abc_ObjFaninC0(Abc_NtkPo(pNtk,0)) ) + { + pNodeA = Abc_ObjNot(pNodeA); + pNodeB = Abc_ObjNot(pNodeB); + } + + // add the PO corresponding to control input + pPoNew = Abc_NtkCreatePo( pNtk ); + Abc_ObjAddFanin( pPoNew, pNodeC ); + Abc_ObjAssignName( pPoNew, "addOut1", NULL ); + + // add the PO corresponding to other input + pPoNew = Abc_NtkCreatePo( pNtk ); + Abc_ObjAddFanin( pPoNew, pNodeB ); + Abc_ObjAssignName( pPoNew, "addOut2", NULL ); + + // mark the nodes in the first cone + pNodeB = Abc_ObjRegular(pNodeB); + vNodes1 = Abc_NtkDfsNodes( pNtk, &pNodeC, 1 ); + vNodes2 = Abc_NtkDfsNodes( pNtk, &pNodeB, 1 ); + + Vec_PtrForEachEntry( vNodes1, pNode, i ) + pNode->fMarkA = 1; + nCommon = 0; + Vec_PtrForEachEntry( vNodes2, pNode, i ) + nCommon += pNode->fMarkA; + Vec_PtrForEachEntry( vNodes1, pNode, i ) + pNode->fMarkA = 0; + + printf( "First cone = %6d. Second cone = %6d. Common = %6d.\n", vNodes1->nSize, vNodes2->nSize, nCommon ); + Vec_PtrFree( vNodes1 ); + Vec_PtrFree( vNodes2 ); + + // reorder the latches + Abc_NtkOrderCisCos( pNtk ); + // make sure that everything is okay + if ( !Abc_NtkCheck( pNtk ) ) + printf( "Abc_NtkDemiter: The network check has failed.\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Computes OR or AND of the POs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkCombinePos( Abc_Ntk_t * pNtk, int fAnd ) +{ + Abc_Obj_t * pNode, * pMiter; + int i; + assert( Abc_NtkIsStrash(pNtk) ); +// assert( Abc_NtkLatchNum(pNtk) == 0 ); + if ( Abc_NtkPoNum(pNtk) == 1 ) + return 1; + // start the result + if ( fAnd ) + pMiter = Abc_AigConst1(pNtk); + else + pMiter = Abc_ObjNot( Abc_AigConst1(pNtk) ); + // perform operations on the POs + Abc_NtkForEachPo( pNtk, pNode, i ) + if ( fAnd ) + pMiter = Abc_AigAnd( pNtk->pManFunc, pMiter, Abc_ObjChild0(pNode) ); + else + pMiter = Abc_AigOr( pNtk->pManFunc, pMiter, Abc_ObjChild0(pNode) ); + // remove the POs and their names + for ( i = Abc_NtkPoNum(pNtk) - 1; i >= 0; i-- ) + Abc_NtkDeleteObj( Abc_NtkPo(pNtk, i) ); + assert( Abc_NtkPoNum(pNtk) == 0 ); + // create the new PO + pNode = Abc_NtkCreatePo( pNtk ); + Abc_ObjAddFanin( pNode, pMiter ); + Abc_ObjAssignName( pNode, "miter", NULL ); + // make sure that everything is okay + if ( !Abc_NtkCheck( pNtk ) ) + { + printf( "Abc_NtkOrPos: The network check has failed.\n" ); + return 0; + } + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcMulti.c b/abc_with_bb_support/src/base/abci/abcMulti.c new file mode 100644 index 000000000..257e1be98 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcMulti.c @@ -0,0 +1,643 @@ +/**CFile**************************************************************** + + FileName [abcMulti.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Procedures which transform an AIG into multi-input AND-graph.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcMulti.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Abc_NtkMultiInt( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkNew ); +static Abc_Obj_t * Abc_NtkMulti_rec( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pNodeOld ); + +static DdNode * Abc_NtkMultiDeriveBdd_rec( DdManager * dd, Abc_Obj_t * pNodeOld, Vec_Ptr_t * vFanins ); +static DdNode * Abc_NtkMultiDeriveBdd( DdManager * dd, Abc_Obj_t * pNodeOld, Vec_Ptr_t * vFaninsOld ); + +static void Abc_NtkMultiSetBounds( Abc_Ntk_t * pNtk, int nThresh, int nFaninMax ); +static void Abc_NtkMultiSetBoundsCnf( Abc_Ntk_t * pNtk ); +static void Abc_NtkMultiSetBoundsMulti( Abc_Ntk_t * pNtk, int nThresh ); +static void Abc_NtkMultiSetBoundsSimple( Abc_Ntk_t * pNtk ); +static void Abc_NtkMultiSetBoundsFactor( Abc_Ntk_t * pNtk ); +static void Abc_NtkMultiCone( Abc_Obj_t * pNode, Vec_Ptr_t * vCone ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Transforms the AIG into nodes.] + + Description [Threhold is the max number of nodes duplicated at a node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkMulti( Abc_Ntk_t * pNtk, int nThresh, int nFaninMax, int fCnf, int fMulti, int fSimple, int fFactor ) +{ + Abc_Ntk_t * pNtkNew; + + assert( Abc_NtkIsStrash(pNtk) ); + assert( nThresh >= 0 ); + assert( nFaninMax > 1 ); + + // print a warning about choice nodes + if ( Abc_NtkGetChoiceNum( pNtk ) ) + printf( "Warning: The choice nodes in the AIG are removed by renoding.\n" ); + + // define the boundary + if ( fCnf ) + Abc_NtkMultiSetBoundsCnf( pNtk ); + else if ( fMulti ) + Abc_NtkMultiSetBoundsMulti( pNtk, nThresh ); + else if ( fSimple ) + Abc_NtkMultiSetBoundsSimple( pNtk ); + else if ( fFactor ) + Abc_NtkMultiSetBoundsFactor( pNtk ); + else + Abc_NtkMultiSetBounds( pNtk, nThresh, nFaninMax ); + + // perform renoding for this boundary + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_LOGIC, ABC_FUNC_BDD ); + Abc_NtkMultiInt( pNtk, pNtkNew ); + Abc_NtkFinalize( pNtk, pNtkNew ); + + // make the network minimum base + Abc_NtkMinimumBase( pNtkNew ); + + // fix the problem with complemented and duplicated CO edges + Abc_NtkLogicMakeSimpleCos( pNtkNew, 0 ); + + // report the number of CNF objects + if ( fCnf ) + { +// int nClauses = Abc_NtkGetClauseNum(pNtkNew) + 2*Abc_NtkPoNum(pNtkNew) + 2*Abc_NtkLatchNum(pNtkNew); +// printf( "CNF variables = %d. CNF clauses = %d.\n", Abc_NtkNodeNum(pNtkNew), nClauses ); + } +//printf( "Maximum fanin = %d.\n", Abc_NtkGetFaninMax(pNtkNew) ); + + if ( pNtk->pExdc ) + pNtkNew->pExdc = Abc_NtkDup( pNtk->pExdc ); + // make sure everything is okay + if ( !Abc_NtkCheck( pNtkNew ) ) + { + printf( "Abc_NtkMulti: The network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Transforms the AIG into nodes.] + + Description [Threhold is the max number of nodes duplicated at a node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMultiInt( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkNew ) +{ + ProgressBar * pProgress; + Abc_Obj_t * pNode, * pConst1, * pNodeNew; + int i; + + // set the constant node + pConst1 = Abc_AigConst1(pNtk); + if ( Abc_ObjFanoutNum(pConst1) > 0 ) + { + pNodeNew = Abc_NtkCreateNode( pNtkNew ); + pNodeNew->pData = Cudd_ReadOne( pNtkNew->pManFunc ); Cudd_Ref( pNodeNew->pData ); + pConst1->pCopy = pNodeNew; + } + + // perform renoding for POs + pProgress = Extra_ProgressBarStart( stdout, Abc_NtkCoNum(pNtk) ); + Abc_NtkForEachCo( pNtk, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + if ( Abc_ObjIsCi(Abc_ObjFanin0(pNode)) ) + continue; + Abc_NtkMulti_rec( pNtkNew, Abc_ObjFanin0(pNode) ); + } + Extra_ProgressBarStop( pProgress ); + + // clean the boundaries and data field in the old network + Abc_NtkForEachObj( pNtk, pNode, i ) + { + pNode->fMarkA = 0; + pNode->pData = NULL; + } +} + +/**Function************************************************************* + + Synopsis [Find the best multi-input node rooted at the given node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkMulti_rec( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pNodeOld ) +{ + Vec_Ptr_t * vCone; + Abc_Obj_t * pNodeNew; + int i; + + assert( !Abc_ObjIsComplement(pNodeOld) ); + // return if the result if known + if ( pNodeOld->pCopy ) + return pNodeOld->pCopy; + assert( Abc_ObjIsNode(pNodeOld) ); + assert( !Abc_AigNodeIsConst(pNodeOld) ); + assert( pNodeOld->fMarkA ); + +//printf( "%d ", Abc_NodeMffcSizeSupp(pNodeOld) ); + + // collect the renoding cone + vCone = Vec_PtrAlloc( 10 ); + Abc_NtkMultiCone( pNodeOld, vCone ); + + // create a new node + pNodeNew = Abc_NtkCreateNode( pNtkNew ); + for ( i = 0; i < vCone->nSize; i++ ) + Abc_ObjAddFanin( pNodeNew, Abc_NtkMulti_rec(pNtkNew, vCone->pArray[i]) ); + + // derive the function of this node + pNodeNew->pData = Abc_NtkMultiDeriveBdd( pNtkNew->pManFunc, pNodeOld, vCone ); + Cudd_Ref( pNodeNew->pData ); + Vec_PtrFree( vCone ); + + // remember the node + pNodeOld->pCopy = pNodeNew; + return pNodeOld->pCopy; +} + + +/**Function************************************************************* + + Synopsis [Derives the local BDD of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Abc_NtkMultiDeriveBdd( DdManager * dd, Abc_Obj_t * pNodeOld, Vec_Ptr_t * vFaninsOld ) +{ + Abc_Obj_t * pFaninOld; + DdNode * bFunc; + int i; + assert( !Abc_AigNodeIsConst(pNodeOld) ); + assert( Abc_ObjIsNode(pNodeOld) ); + // set the elementary BDD variables for the input nodes + for ( i = 0; i < vFaninsOld->nSize; i++ ) + { + pFaninOld = vFaninsOld->pArray[i]; + pFaninOld->pData = Cudd_bddIthVar( dd, i ); Cudd_Ref( pFaninOld->pData ); + pFaninOld->fMarkC = 1; + } + // call the recursive BDD computation + bFunc = Abc_NtkMultiDeriveBdd_rec( dd, pNodeOld, vFaninsOld ); Cudd_Ref( bFunc ); + // dereference the intermediate nodes + for ( i = 0; i < vFaninsOld->nSize; i++ ) + { + pFaninOld = vFaninsOld->pArray[i]; + Cudd_RecursiveDeref( dd, pFaninOld->pData ); + pFaninOld->fMarkC = 0; + } + Cudd_Deref( bFunc ); + return bFunc; +} + +/**Function************************************************************* + + Synopsis [Derives the local BDD of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Abc_NtkMultiDeriveBdd_rec( DdManager * dd, Abc_Obj_t * pNode, Vec_Ptr_t * vFanins ) +{ + DdNode * bFunc, * bFunc0, * bFunc1; + assert( !Abc_ObjIsComplement(pNode) ); + // if the result is available return + if ( pNode->fMarkC ) + { + assert( pNode->pData ); // network has a cycle + return pNode->pData; + } + // mark the node as visited + pNode->fMarkC = 1; + Vec_PtrPush( vFanins, pNode ); + // compute the result for both branches + bFunc0 = Abc_NtkMultiDeriveBdd_rec( dd, Abc_ObjFanin(pNode,0), vFanins ); Cudd_Ref( bFunc0 ); + bFunc1 = Abc_NtkMultiDeriveBdd_rec( dd, Abc_ObjFanin(pNode,1), vFanins ); Cudd_Ref( bFunc1 ); + bFunc0 = Cudd_NotCond( bFunc0, Abc_ObjFaninC0(pNode) ); + bFunc1 = Cudd_NotCond( bFunc1, Abc_ObjFaninC1(pNode) ); + // get the final result + bFunc = Cudd_bddAnd( dd, bFunc0, bFunc1 ); Cudd_Ref( bFunc ); + Cudd_RecursiveDeref( dd, bFunc0 ); + Cudd_RecursiveDeref( dd, bFunc1 ); + // set the result + pNode->pData = bFunc; + assert( pNode->pData ); + return bFunc; +} + + + +/**Function************************************************************* + + Synopsis [Limits the cones to be no more than the given size.] + + Description [Returns 1 if the last cone was limited. Returns 0 if no changes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkMultiLimit_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vCone, int nFaninMax, int fCanStop, int fFirst ) +{ + int nNodes0, nNodes1; + assert( !Abc_ObjIsComplement(pNode) ); + // check if the node should be added to the fanins + if ( !fFirst && (pNode->fMarkA || !Abc_ObjIsNode(pNode)) ) + { + Vec_PtrPushUnique( vCone, pNode ); + return 0; + } + // if we cannot stop in this branch, collect all nodes + if ( !fCanStop ) + { + Abc_NtkMultiLimit_rec( Abc_ObjFanin(pNode,0), vCone, nFaninMax, 0, 0 ); + Abc_NtkMultiLimit_rec( Abc_ObjFanin(pNode,1), vCone, nFaninMax, 0, 0 ); + return 0; + } + // if we can stop, try the left branch first, and return if we stopped + assert( vCone->nSize == 0 ); + if ( Abc_NtkMultiLimit_rec( Abc_ObjFanin(pNode,0), vCone, nFaninMax, 1, 0 ) ) + return 1; + // save the number of nodes in the left branch and call for the right branch + nNodes0 = vCone->nSize; + assert( nNodes0 <= nFaninMax ); + Abc_NtkMultiLimit_rec( Abc_ObjFanin(pNode,1), vCone, nFaninMax, 0, 0 ); + // check the number of nodes + if ( vCone->nSize <= nFaninMax ) + return 0; + // the number of nodes exceeds the limit + + // get the number of nodes in the right branch + vCone->nSize = 0; + Abc_NtkMultiLimit_rec( Abc_ObjFanin(pNode,1), vCone, nFaninMax, 0, 0 ); + // if this number exceeds the limit, solve the problem for this branch + if ( vCone->nSize > nFaninMax ) + { + int RetValue; + vCone->nSize = 0; + RetValue = Abc_NtkMultiLimit_rec( Abc_ObjFanin(pNode,1), vCone, nFaninMax, 1, 0 ); + assert( RetValue == 1 ); + return 1; + } + + nNodes1 = vCone->nSize; + assert( nNodes1 <= nFaninMax ); + if ( nNodes0 >= nNodes1 ) + { // the left branch is larger - cut it + assert( Abc_ObjFanin(pNode,0)->fMarkA == 0 ); + Abc_ObjFanin(pNode,0)->fMarkA = 1; + } + else + { // the right branch is larger - cut it + assert( Abc_ObjFanin(pNode,1)->fMarkA == 0 ); + Abc_ObjFanin(pNode,1)->fMarkA = 1; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Limits the cones to be no more than the given size.] + + Description [Returns 1 if the last cone was limited. Returns 0 if no changes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkMultiLimit( Abc_Obj_t * pNode, Vec_Ptr_t * vCone, int nFaninMax ) +{ + vCone->nSize = 0; + return Abc_NtkMultiLimit_rec( pNode, vCone, nFaninMax, 1, 1 ); +} + +/**Function************************************************************* + + Synopsis [Sets the expansion boundary for multi-input nodes.] + + Description [The boundary includes the set of PIs and all nodes such that + when expanding over the node we duplicate no more than nThresh nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMultiSetBounds( Abc_Ntk_t * pNtk, int nThresh, int nFaninMax ) +{ + Vec_Ptr_t * vCone = Vec_PtrAlloc(10); + Abc_Obj_t * pNode; + int i, nFanouts, nConeSize; + + // make sure the mark is not set + Abc_NtkForEachObj( pNtk, pNode, i ) + assert( pNode->fMarkA == 0 ); + + // mark the nodes where expansion stops using pNode->fMarkA + Abc_NtkForEachNode( pNtk, pNode, i ) + { + // skip PI/PO nodes +// if ( Abc_NodeIsConst(pNode) ) +// continue; + // mark the nodes with multiple fanouts + nFanouts = Abc_ObjFanoutNum(pNode); + nConeSize = Abc_NodeMffcSize(pNode); + if ( (nFanouts - 1) * nConeSize > nThresh ) + pNode->fMarkA = 1; + } + + // mark the PO drivers + Abc_NtkForEachCo( pNtk, pNode, i ) + Abc_ObjFanin0(pNode)->fMarkA = 1; + + // make sure the fanin limit is met + Abc_NtkForEachNode( pNtk, pNode, i ) + { + // skip PI/PO nodes +// if ( Abc_NodeIsConst(pNode) ) +// continue; + if ( pNode->fMarkA == 0 ) + continue; + // continue cutting branches until it meets the fanin limit + while ( Abc_NtkMultiLimit(pNode, vCone, nFaninMax) ); + assert( vCone->nSize <= nFaninMax ); + } + Vec_PtrFree(vCone); +/* + // make sure the fanin limit is met + Abc_NtkForEachNode( pNtk, pNode, i ) + { + // skip PI/PO nodes +// if ( Abc_NodeIsConst(pNode) ) +// continue; + if ( pNode->fMarkA == 0 ) + continue; + Abc_NtkMultiCone( pNode, vCone ); + assert( vCone->nSize <= nFaninMax ); + } +*/ +} + +/**Function************************************************************* + + Synopsis [Sets the expansion boundary for conversion into CNF.] + + Description [The boundary includes the set of PIs, the roots of MUXes, + the nodes with multiple fanouts and the nodes with complemented outputs.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMultiSetBoundsCnf( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i, nMuxes; + + // make sure the mark is not set + Abc_NtkForEachObj( pNtk, pNode, i ) + assert( pNode->fMarkA == 0 ); + + // mark the nodes where expansion stops using pNode->fMarkA + Abc_NtkForEachNode( pNtk, pNode, i ) + { + // skip PI/PO nodes +// if ( Abc_NodeIsConst(pNode) ) +// continue; + // mark the nodes with multiple fanouts + if ( Abc_ObjFanoutNum(pNode) > 1 ) + pNode->fMarkA = 1; + // mark the nodes that are roots of MUXes + if ( Abc_NodeIsMuxType( pNode ) ) + { + pNode->fMarkA = 1; + Abc_ObjFanin0( Abc_ObjFanin0(pNode) )->fMarkA = 1; + Abc_ObjFanin0( Abc_ObjFanin1(pNode) )->fMarkA = 1; + Abc_ObjFanin1( Abc_ObjFanin0(pNode) )->fMarkA = 1; + Abc_ObjFanin1( Abc_ObjFanin1(pNode) )->fMarkA = 1; + } + else // mark the complemented edges + { + if ( Abc_ObjFaninC0(pNode) ) + Abc_ObjFanin0(pNode)->fMarkA = 1; + if ( Abc_ObjFaninC1(pNode) ) + Abc_ObjFanin1(pNode)->fMarkA = 1; + } + } + + // mark the PO drivers + Abc_NtkForEachCo( pNtk, pNode, i ) + Abc_ObjFanin0(pNode)->fMarkA = 1; + + // count the number of MUXes + nMuxes = 0; + Abc_NtkForEachNode( pNtk, pNode, i ) + { + // skip PI/PO nodes +// if ( Abc_NodeIsConst(pNode) ) +// continue; + if ( Abc_NodeIsMuxType(pNode) && + Abc_ObjFanin0(pNode)->fMarkA == 0 && + Abc_ObjFanin1(pNode)->fMarkA == 0 ) + nMuxes++; + } +// printf( "The number of MUXes detected = %d (%5.2f %% of logic).\n", nMuxes, 300.0*nMuxes/Abc_NtkNodeNum(pNtk) ); +} + +/**Function************************************************************* + + Synopsis [Sets the expansion boundary for conversion into multi-input AND graph.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMultiSetBoundsMulti( Abc_Ntk_t * pNtk, int nThresh ) +{ + Abc_Obj_t * pNode; + int i, nFanouts, nConeSize; + + // make sure the mark is not set + Abc_NtkForEachObj( pNtk, pNode, i ) + assert( pNode->fMarkA == 0 ); + + // mark the nodes where expansion stops using pNode->fMarkA + Abc_NtkForEachNode( pNtk, pNode, i ) + { + // skip PI/PO nodes +// if ( Abc_NodeIsConst(pNode) ) +// continue; + // mark the nodes with multiple fanouts +// if ( Abc_ObjFanoutNum(pNode) > 1 ) +// pNode->fMarkA = 1; + // mark the nodes with multiple fanouts + nFanouts = Abc_ObjFanoutNum(pNode); + nConeSize = Abc_NodeMffcSizeStop(pNode); + if ( (nFanouts - 1) * nConeSize > nThresh ) + pNode->fMarkA = 1; + // mark the children if they are pointed by the complemented edges + if ( Abc_ObjFaninC0(pNode) ) + Abc_ObjFanin0(pNode)->fMarkA = 1; + if ( Abc_ObjFaninC1(pNode) ) + Abc_ObjFanin1(pNode)->fMarkA = 1; + } + + // mark the PO drivers + Abc_NtkForEachCo( pNtk, pNode, i ) + Abc_ObjFanin0(pNode)->fMarkA = 1; +} + +/**Function************************************************************* + + Synopsis [Sets a simple boundary.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMultiSetBoundsSimple( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i; + // make sure the mark is not set + Abc_NtkForEachObj( pNtk, pNode, i ) + assert( pNode->fMarkA == 0 ); + // mark the nodes where expansion stops using pNode->fMarkA + Abc_NtkForEachNode( pNtk, pNode, i ) + pNode->fMarkA = 1; +} + +/**Function************************************************************* + + Synopsis [Sets a factor-cut boundary.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMultiSetBoundsFactor( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i; + // make sure the mark is not set + Abc_NtkForEachObj( pNtk, pNode, i ) + assert( pNode->fMarkA == 0 ); + // mark the nodes where expansion stops using pNode->fMarkA + Abc_NtkForEachNode( pNtk, pNode, i ) + pNode->fMarkA = (pNode->vFanouts.nSize > 1 && !Abc_NodeIsMuxControlType(pNode)); + // mark the PO drivers + Abc_NtkForEachCo( pNtk, pNode, i ) + Abc_ObjFanin0(pNode)->fMarkA = 1; +} + +/**Function************************************************************* + + Synopsis [Collects the fanins of a large node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMultiCone_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vCone ) +{ + assert( !Abc_ObjIsComplement(pNode) ); + if ( pNode->fMarkA || !Abc_ObjIsNode(pNode) ) + { + Vec_PtrPushUnique( vCone, pNode ); + return; + } + Abc_NtkMultiCone_rec( Abc_ObjFanin(pNode,0), vCone ); + Abc_NtkMultiCone_rec( Abc_ObjFanin(pNode,1), vCone ); +} + +/**Function************************************************************* + + Synopsis [Collects the fanins of a large node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMultiCone( Abc_Obj_t * pNode, Vec_Ptr_t * vCone ) +{ + assert( !Abc_ObjIsComplement(pNode) ); + assert( Abc_ObjIsNode(pNode) ); + vCone->nSize = 0; + Abc_NtkMultiCone_rec( Abc_ObjFanin(pNode,0), vCone ); + Abc_NtkMultiCone_rec( Abc_ObjFanin(pNode,1), vCone ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcMv.c b/abc_with_bb_support/src/base/abci/abcMv.c new file mode 100644 index 000000000..4e0eec7f1 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcMv.c @@ -0,0 +1,369 @@ +/**CFile**************************************************************** + + FileName [abcMv.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Multi-valued decomposition.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcMv.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Mv_Man_t_ Mv_Man_t; +struct Mv_Man_t_ +{ + int nInputs; // the number of 4-valued input variables + int nFuncs; // the number of 4-valued functions + DdManager * dd; // representation of functions + DdNode * bValues[15][4]; // representation of i-sets + DdNode * bValueDcs[15][4]; // representation of i-sets don't-cares + DdNode * bFuncs[15]; // representation of functions +}; + +static void Abc_MvDecompose( Mv_Man_t * p ); +static void Abc_MvPrintStats( Mv_Man_t * p ); +static void Abc_MvRead( Mv_Man_t * p ); +static void Abc_MvDeref( Mv_Man_t * p ); +static DdNode * Abc_MvReadCube( DdManager * dd, char * pLine, int nVars ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_MvExperiment() +{ + Mv_Man_t * p; + // get the functions + p = ALLOC( Mv_Man_t, 1 ); + memset( p, 0, sizeof(Mv_Man_t) ); + p->dd = Cudd_Init( 32, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 ); + p->nFuncs = 15; + p->nInputs = 9; + Abc_MvRead( p ); + // process the functions + Abc_MvPrintStats( p ); +// Cudd_ReduceHeap( p->dd, CUDD_REORDER_SYMM_SIFT, 1 ); +// Abc_MvPrintStats( p ); + // try detecting support reducing bound set + Abc_MvDecompose( p ); + + // remove the manager + Abc_MvDeref( p ); + Extra_StopManager( p->dd ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_MvPrintStats( Mv_Man_t * p ) +{ + int i, v; + for ( i = 0; i < 15; i++ ) + { + printf( "%2d : ", i ); + printf( "%3d (%2d) ", Cudd_DagSize(p->bFuncs[i])-1, Cudd_SupportSize(p->dd, p->bFuncs[i]) ); + for ( v = 0; v < 4; v++ ) + printf( "%d = %3d (%2d) ", v, Cudd_DagSize(p->bValues[i][v])-1, Cudd_SupportSize(p->dd, p->bValues[i][v]) ); + printf( "\n" ); + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Abc_MvReadCube( DdManager * dd, char * pLine, int nVars ) +{ + DdNode * bCube, * bVar, * bTemp; + int i; + bCube = Cudd_ReadOne(dd); Cudd_Ref( bCube ); + for ( i = 0; i < nVars; i++ ) + { + if ( pLine[i] == '-' ) + continue; + else if ( pLine[i] == '0' ) // 0 + bVar = Cudd_Not( Cudd_bddIthVar(dd, 29-i) ); + else if ( pLine[i] == '1' ) // 1 + bVar = Cudd_bddIthVar(dd, 29-i); + else assert(0); + bCube = Cudd_bddAnd( dd, bTemp = bCube, bVar ); Cudd_Ref( bCube ); + Cudd_RecursiveDeref( dd, bTemp ); + } + Cudd_Deref( bCube ); + return bCube; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_MvRead( Mv_Man_t * p ) +{ + FILE * pFile; + char Buffer[1000], * pLine; + DdNode * bCube, * bTemp, * bProd, * bVar0, * bVar1, * bCubeSum; + int i, v; + + // start the cube + bCubeSum = Cudd_ReadLogicZero(p->dd); Cudd_Ref( bCubeSum ); + + // start the values + for ( i = 0; i < 15; i++ ) + for ( v = 0; v < 4; v++ ) + { + p->bValues[i][v] = Cudd_ReadLogicZero(p->dd); Cudd_Ref( p->bValues[i][v] ); + p->bValueDcs[i][v] = Cudd_ReadLogicZero(p->dd); Cudd_Ref( p->bValueDcs[i][v] ); + } + + // read the file + pFile = fopen( "input.pla", "r" ); + while ( fgets( Buffer, 1000, pFile ) ) + { + if ( Buffer[0] == '#' ) + continue; + if ( Buffer[0] == '.' ) + { + if ( Buffer[1] == 'e' ) + break; + continue; + } + + // get the cube + bCube = Abc_MvReadCube( p->dd, Buffer, 18 ); Cudd_Ref( bCube ); + + // add it to the values of the output functions + pLine = Buffer + 19; + for ( i = 0; i < 15; i++ ) + { + if ( pLine[2*i] == '-' && pLine[2*i+1] == '-' ) + { + for ( v = 0; v < 4; v++ ) + { + p->bValueDcs[i][v] = Cudd_bddOr( p->dd, bTemp = p->bValueDcs[i][v], bCube ); Cudd_Ref( p->bValueDcs[i][v] ); + Cudd_RecursiveDeref( p->dd, bTemp ); + } + continue; + } + else if ( pLine[2*i] == '0' && pLine[2*i+1] == '0' ) // 0 + v = 0; + else if ( pLine[2*i] == '1' && pLine[2*i+1] == '0' ) // 1 + v = 1; + else if ( pLine[2*i] == '0' && pLine[2*i+1] == '1' ) // 2 + v = 2; + else if ( pLine[2*i] == '1' && pLine[2*i+1] == '1' ) // 3 + v = 3; + else assert( 0 ); + // add the value + p->bValues[i][v] = Cudd_bddOr( p->dd, bTemp = p->bValues[i][v], bCube ); Cudd_Ref( p->bValues[i][v] ); + Cudd_RecursiveDeref( p->dd, bTemp ); + } + + // add the cube + bCubeSum = Cudd_bddOr( p->dd, bTemp = bCubeSum, bCube ); Cudd_Ref( bCubeSum ); + Cudd_RecursiveDeref( p->dd, bTemp ); + Cudd_RecursiveDeref( p->dd, bCube ); + } + + // add the complement of the domain to all values + for ( i = 0; i < 15; i++ ) + for ( v = 0; v < 4; v++ ) + { + if ( p->bValues[i][v] == Cudd_Not(Cudd_ReadOne(p->dd)) ) + continue; + p->bValues[i][v] = Cudd_bddOr( p->dd, bTemp = p->bValues[i][v], p->bValueDcs[i][v] ); Cudd_Ref( p->bValues[i][v] ); + Cudd_RecursiveDeref( p->dd, bTemp ); + p->bValues[i][v] = Cudd_bddOr( p->dd, bTemp = p->bValues[i][v], Cudd_Not(bCubeSum) ); Cudd_Ref( p->bValues[i][v] ); + Cudd_RecursiveDeref( p->dd, bTemp ); + } + printf( "Domain = %5.2f %%.\n", 100.0*Cudd_CountMinterm(p->dd, bCubeSum, 32)/Cudd_CountMinterm(p->dd, Cudd_ReadOne(p->dd), 32) ); + Cudd_RecursiveDeref( p->dd, bCubeSum ); + + // create each output function + for ( i = 0; i < 15; i++ ) + { + p->bFuncs[i] = Cudd_ReadLogicZero(p->dd); Cudd_Ref( p->bFuncs[i] ); + for ( v = 0; v < 4; v++ ) + { + bVar0 = Cudd_NotCond( Cudd_bddIthVar(p->dd, 30), ((v & 1) == 0) ); + bVar1 = Cudd_NotCond( Cudd_bddIthVar(p->dd, 31), ((v & 2) == 0) ); + bCube = Cudd_bddAnd( p->dd, bVar0, bVar1 ); Cudd_Ref( bCube ); + bProd = Cudd_bddAnd( p->dd, p->bValues[i][v], bCube ); Cudd_Ref( bProd ); + Cudd_RecursiveDeref( p->dd, bCube ); + // add the value + p->bFuncs[i] = Cudd_bddOr( p->dd, bTemp = p->bFuncs[i], bProd ); Cudd_Ref( p->bFuncs[i] ); + Cudd_RecursiveDeref( p->dd, bTemp ); + Cudd_RecursiveDeref( p->dd, bProd ); + } + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_MvDeref( Mv_Man_t * p ) +{ + int i, v; + for ( i = 0; i < 15; i++ ) + for ( v = 0; v < 4; v++ ) + { + Cudd_RecursiveDeref( p->dd, p->bValues[i][v] ); + Cudd_RecursiveDeref( p->dd, p->bValueDcs[i][v] ); + } + for ( i = 0; i < 15; i++ ) + Cudd_RecursiveDeref( p->dd, p->bFuncs[i] ); +} + + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_MvDecompose( Mv_Man_t * p ) +{ + DdNode * bCofs[16], * bVarCube1, * bVarCube2, * bVarCube, * bCube, * bVar0, * bVar1;//, * bRes; + int k, i1, i2, v1, v2;//, c1, c2, Counter; + + bVar0 = Cudd_bddIthVar(p->dd, 30); + bVar1 = Cudd_bddIthVar(p->dd, 31); + bCube = Cudd_bddAnd( p->dd, bVar0, bVar1 ); Cudd_Ref( bCube ); + + for ( k = 0; k < p->nFuncs; k++ ) + { + printf( "FUNCTION %d\n", k ); + for ( i1 = 0; i1 < p->nFuncs; i1++ ) + for ( i2 = i1+1; i2 < p->nFuncs; i2++ ) + { + Vec_Ptr_t * vCofs; + + for ( v1 = 0; v1 < 4; v1++ ) + { + bVar0 = Cudd_NotCond( Cudd_bddIthVar(p->dd, 29-2*i1 ), ((v1 & 1) == 0) ); + bVar1 = Cudd_NotCond( Cudd_bddIthVar(p->dd, 29-2*i1-1), ((v1 & 2) == 0) ); + bVarCube1 = Cudd_bddAnd( p->dd, bVar0, bVar1 ); Cudd_Ref( bVarCube1 ); + for ( v2 = 0; v2 < 4; v2++ ) + { + bVar0 = Cudd_NotCond( Cudd_bddIthVar(p->dd, 29-2*i2 ), ((v2 & 1) == 0) ); + bVar1 = Cudd_NotCond( Cudd_bddIthVar(p->dd, 29-2*i2-1), ((v2 & 2) == 0) ); + bVarCube2 = Cudd_bddAnd( p->dd, bVar0, bVar1 ); Cudd_Ref( bVarCube2 ); + bVarCube = Cudd_bddAnd( p->dd, bVarCube1, bVarCube2 ); Cudd_Ref( bVarCube ); + bCofs[v1 * 4 + v2] = Cudd_Cofactor( p->dd, p->bFuncs[k], bVarCube ); Cudd_Ref( bCofs[v1 * 4 + v2] ); + Cudd_RecursiveDeref( p->dd, bVarCube ); + Cudd_RecursiveDeref( p->dd, bVarCube2 ); + } + Cudd_RecursiveDeref( p->dd, bVarCube1 ); + } +/* + // check the compatibility of cofactors + Counter = 0; + for ( c1 = 0; c1 < 16; c1++ ) + { + for ( c2 = 0; c2 <= c1; c2++ ) + printf( " " ); + for ( c2 = c1+1; c2 < 16; c2++ ) + { + bRes = Cudd_bddAndAbstract( p->dd, bCofs[c1], bCofs[c2], bCube ); Cudd_Ref( bRes ); + if ( bRes == Cudd_ReadOne(p->dd) ) + { + printf( "+" ); + Counter++; + } + else + { + printf( " " ); + } + Cudd_RecursiveDeref( p->dd, bRes ); + } + printf( "\n" ); + } +*/ + + vCofs = Vec_PtrAlloc( 16 ); + for ( v1 = 0; v1 < 4; v1++ ) + for ( v2 = 0; v2 < 4; v2++ ) + Vec_PtrPushUnique( vCofs, bCofs[v1 * 4 + v2] ); + printf( "%d ", Vec_PtrSize(vCofs) ); + Vec_PtrFree( vCofs ); + + // free the cofactors + for ( v1 = 0; v1 < 4; v1++ ) + for ( v2 = 0; v2 < 4; v2++ ) + Cudd_RecursiveDeref( p->dd, bCofs[v1 * 4 + v2] ); + + printf( "\n" ); +// printf( "%2d, %2d : %3d\n", i1, i2, Counter ); + } + } + + Cudd_RecursiveDeref( p->dd, bCube ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcNtbdd.c b/abc_with_bb_support/src/base/abci/abcNtbdd.c new file mode 100644 index 000000000..7d48d6115 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcNtbdd.c @@ -0,0 +1,582 @@ +/**CFile**************************************************************** + + FileName [abcNtbdd.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Procedures to translate between the BDD and the network.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcNtbdd.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Abc_NtkBddToMuxesPerform( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkNew ); +static Abc_Obj_t * Abc_NodeBddToMuxes( Abc_Obj_t * pNodeOld, Abc_Ntk_t * pNtkNew ); +static Abc_Obj_t * Abc_NodeBddToMuxes_rec( DdManager * dd, DdNode * bFunc, Abc_Ntk_t * pNtkNew, st_table * tBdd2Node ); +static DdNode * Abc_NodeGlobalBdds_rec( DdManager * dd, Abc_Obj_t * pNode, int nBddSizeMax, int fDropInternal, ProgressBar * pProgress, int * pCounter, int fVerbose ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Constructs the network isomorphic to the given BDD.] + + Description [Assumes that the BDD depends on the variables whose indexes + correspond to the names in the array (pNamesPi). Otherwise, returns NULL. + The resulting network comes with one node, whose functionality is + equal to the given BDD. To decompose this BDD into the network of + multiplexers use Abc_NtkBddToMuxes(). To decompose this BDD into + an And-Inverter Graph, use Abc_NtkStrash().] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkDeriveFromBdd( DdManager * dd, DdNode * bFunc, char * pNamePo, Vec_Ptr_t * vNamesPi ) +{ + Abc_Ntk_t * pNtk; + Vec_Ptr_t * vNamesPiFake = NULL; + Abc_Obj_t * pNode, * pNodePi, * pNodePo; + DdNode * bSupp, * bTemp; + char * pName; + int i; + + // supply fake names if real names are not given + if ( pNamePo == NULL ) + pNamePo = "F"; + if ( vNamesPi == NULL ) + { + vNamesPiFake = Abc_NodeGetFakeNames( dd->size ); + vNamesPi = vNamesPiFake; + } + + // make sure BDD depends on the variables whose index + // does not exceed the size of the array with PI names + bSupp = Cudd_Support( dd, bFunc ); Cudd_Ref( bSupp ); + for ( bTemp = bSupp; bTemp != Cudd_ReadOne(dd); bTemp = cuddT(bTemp) ) + if ( (int)Cudd_NodeReadIndex(bTemp) >= Vec_PtrSize(vNamesPi) ) + break; + Cudd_RecursiveDeref( dd, bSupp ); + if ( bTemp != Cudd_ReadOne(dd) ) + return NULL; + + // start the network + pNtk = Abc_NtkAlloc( ABC_NTK_LOGIC, ABC_FUNC_BDD, 1 ); + pNtk->pName = Extra_UtilStrsav(pNamePo); + // make sure the new manager has enough inputs + Cudd_bddIthVar( pNtk->pManFunc, Vec_PtrSize(vNamesPi) ); + // add the PIs corresponding to the names + Vec_PtrForEachEntry( vNamesPi, pName, i ) + Abc_ObjAssignName( Abc_NtkCreatePi(pNtk), pName, NULL ); + // create the node + pNode = Abc_NtkCreateNode( pNtk ); + pNode->pData = Cudd_bddTransfer( dd, pNtk->pManFunc, bFunc ); Cudd_Ref(pNode->pData); + Abc_NtkForEachPi( pNtk, pNodePi, i ) + Abc_ObjAddFanin( pNode, pNodePi ); + // create the only PO + pNodePo = Abc_NtkCreatePo( pNtk ); + Abc_ObjAddFanin( pNodePo, pNode ); + Abc_ObjAssignName( pNodePo, pNamePo, NULL ); + // make the network minimum base + Abc_NtkMinimumBase( pNtk ); + if ( vNamesPiFake ) + Abc_NodeFreeNames( vNamesPiFake ); + if ( !Abc_NtkCheck( pNtk ) ) + fprintf( stdout, "Abc_NtkDeriveFromBdd(): Network check has failed.\n" ); + return pNtk; +} + + + +/**Function************************************************************* + + Synopsis [Creates the network isomorphic to the union of local BDDs of the nodes.] + + Description [The nodes of the local BDDs are converted into the network nodes + with logic functions equal to the MUX.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkBddToMuxes( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkNew; + assert( Abc_NtkIsBddLogic(pNtk) ); + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_LOGIC, ABC_FUNC_SOP ); + Abc_NtkBddToMuxesPerform( pNtk, pNtkNew ); + Abc_NtkFinalize( pNtk, pNtkNew ); + // make sure everything is okay + if ( !Abc_NtkCheck( pNtkNew ) ) + { + printf( "Abc_NtkBddToMuxes: The network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Converts the network to MUXes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkBddToMuxesPerform( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkNew ) +{ + ProgressBar * pProgress; + Abc_Obj_t * pNode, * pNodeNew; + Vec_Ptr_t * vNodes; + int i; + // perform conversion in the topological order + vNodes = Abc_NtkDfs( pNtk, 0 ); + pProgress = Extra_ProgressBarStart( stdout, vNodes->nSize ); + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + // convert one node + assert( Abc_ObjIsNode(pNode) ); + pNodeNew = Abc_NodeBddToMuxes( pNode, pNtkNew ); + // mark the old node with the new one + assert( pNode->pCopy == NULL ); + pNode->pCopy = pNodeNew; + } + Vec_PtrFree( vNodes ); + Extra_ProgressBarStop( pProgress ); +} + +/**Function************************************************************* + + Synopsis [Converts the node to MUXes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NodeBddToMuxes( Abc_Obj_t * pNodeOld, Abc_Ntk_t * pNtkNew ) +{ + DdManager * dd = pNodeOld->pNtk->pManFunc; + DdNode * bFunc = pNodeOld->pData; + Abc_Obj_t * pFaninOld, * pNodeNew; + st_table * tBdd2Node; + int i; + // create the table mapping BDD nodes into the ABC nodes + tBdd2Node = st_init_table( st_ptrcmp, st_ptrhash ); + // add the constant and the elementary vars + Abc_ObjForEachFanin( pNodeOld, pFaninOld, i ) + st_insert( tBdd2Node, (char *)Cudd_bddIthVar(dd, i), (char *)pFaninOld->pCopy ); + // create the new nodes recursively + pNodeNew = Abc_NodeBddToMuxes_rec( dd, Cudd_Regular(bFunc), pNtkNew, tBdd2Node ); + st_free_table( tBdd2Node ); + if ( Cudd_IsComplement(bFunc) ) + pNodeNew = Abc_NtkCreateNodeInv( pNtkNew, pNodeNew ); + return pNodeNew; +} + +/**Function************************************************************* + + Synopsis [Converts the node to MUXes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NodeBddToMuxes_rec( DdManager * dd, DdNode * bFunc, Abc_Ntk_t * pNtkNew, st_table * tBdd2Node ) +{ + Abc_Obj_t * pNodeNew, * pNodeNew0, * pNodeNew1, * pNodeNewC; + assert( !Cudd_IsComplement(bFunc) ); + if ( bFunc == b1 ) + return Abc_NtkCreateNodeConst1(pNtkNew); + if ( st_lookup( tBdd2Node, (char *)bFunc, (char **)&pNodeNew ) ) + return pNodeNew; + // solve for the children nodes + pNodeNew0 = Abc_NodeBddToMuxes_rec( dd, Cudd_Regular(cuddE(bFunc)), pNtkNew, tBdd2Node ); + if ( Cudd_IsComplement(cuddE(bFunc)) ) + pNodeNew0 = Abc_NtkCreateNodeInv( pNtkNew, pNodeNew0 ); + pNodeNew1 = Abc_NodeBddToMuxes_rec( dd, cuddT(bFunc), pNtkNew, tBdd2Node ); + if ( !st_lookup( tBdd2Node, (char *)Cudd_bddIthVar(dd, bFunc->index), (char **)&pNodeNewC ) ) + assert( 0 ); + // create the MUX node + pNodeNew = Abc_NtkCreateNodeMux( pNtkNew, pNodeNewC, pNodeNew1, pNodeNew0 ); + st_insert( tBdd2Node, (char *)bFunc, (char *)pNodeNew ); + return pNodeNew; +} + + +/**Function************************************************************* + + Synopsis [Derives global BDDs for the COs of the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdManager * Abc_NtkBuildGlobalBdds( Abc_Ntk_t * pNtk, int nBddSizeMax, int fDropInternal, int fReorder, int fVerbose ) +{ + ProgressBar * pProgress; + Abc_Obj_t * pObj, * pFanin; + Vec_Att_t * pAttMan; + DdManager * dd; + DdNode * bFunc; + int i, k, Counter; + + // remove dangling nodes + Abc_AigCleanup( pNtk->pManFunc ); + + // start the manager + assert( Abc_NtkGlobalBdd(pNtk) == NULL ); + dd = Cudd_Init( Abc_NtkCiNum(pNtk), 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 ); + pAttMan = Vec_AttAlloc( 0, Abc_NtkObjNumMax(pNtk) + 1, dd, Extra_StopManager, NULL, Cudd_RecursiveDeref ); + Vec_PtrWriteEntry( pNtk->vAttrs, VEC_ATTR_GLOBAL_BDD, pAttMan ); + + // set reordering + if ( fReorder ) + Cudd_AutodynEnable( dd, CUDD_REORDER_SYMM_SIFT ); + + // assign the constant node BDD + pObj = Abc_AigConst1(pNtk); + if ( Abc_ObjFanoutNum(pObj) > 0 ) + { + bFunc = dd->one; + Abc_ObjSetGlobalBdd( pObj, bFunc ); Cudd_Ref( bFunc ); + } + // set the elementary variables + Abc_NtkForEachCi( pNtk, pObj, i ) + if ( Abc_ObjFanoutNum(pObj) > 0 ) + { + bFunc = dd->vars[i]; +// bFunc = dd->vars[Abc_NtkCiNum(pNtk) - 1 - i]; + Abc_ObjSetGlobalBdd( pObj, bFunc ); Cudd_Ref( bFunc ); + } + + // collect the global functions of the COs + Counter = 0; + // construct the BDDs + pProgress = Extra_ProgressBarStart( stdout, Abc_NtkNodeNum(pNtk) ); + Abc_NtkForEachCo( pNtk, pObj, i ) + { + bFunc = Abc_NodeGlobalBdds_rec( dd, Abc_ObjFanin0(pObj), nBddSizeMax, fDropInternal, pProgress, &Counter, fVerbose ); + if ( bFunc == NULL ) + { + if ( fVerbose ) + printf( "Constructing global BDDs is aborted.\n" ); + Abc_NtkFreeGlobalBdds( pNtk, 0 ); + Cudd_Quit( dd ); + return NULL; + } + bFunc = Cudd_NotCond( bFunc, Abc_ObjFaninC0(pObj) ); Cudd_Ref( bFunc ); + Abc_ObjSetGlobalBdd( pObj, bFunc ); + } + Extra_ProgressBarStop( pProgress ); + +/* + // derefence the intermediate BDDs + Abc_NtkForEachNode( pNtk, pObj, i ) + if ( pObj->pCopy ) + { + Cudd_RecursiveDeref( dd, (DdNode *)pObj->pCopy ); + pObj->pCopy = NULL; + } +*/ +/* + // make sure all nodes are derefed + Abc_NtkForEachObj( pNtk, pObj, i ) + { + if ( pObj->pCopy != NULL ) + printf( "Abc_NtkBuildGlobalBdds() error: Node %d has BDD assigned\n", pObj->Id ); + if ( pObj->vFanouts.nSize > 0 ) + printf( "Abc_NtkBuildGlobalBdds() error: Node %d has refs assigned\n", pObj->Id ); + } +*/ + // reset references + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( !Abc_ObjIsBox(pObj) && !Abc_ObjIsBo(pObj) ) + Abc_ObjForEachFanin( pObj, pFanin, k ) + pFanin->vFanouts.nSize++; + + // reorder one more time + if ( fReorder ) + { + Cudd_ReduceHeap( dd, CUDD_REORDER_SYMM_SIFT, 1 ); + Cudd_AutodynDisable( dd ); + } +// Cudd_PrintInfo( dd, stdout ); + return dd; +} + +/**Function************************************************************* + + Synopsis [Derives the global BDD for one AIG node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Abc_NodeGlobalBdds_rec( DdManager * dd, Abc_Obj_t * pNode, int nBddSizeMax, int fDropInternal, ProgressBar * pProgress, int * pCounter, int fVerbose ) +{ + DdNode * bFunc, * bFunc0, * bFunc1, * bFuncC; + int fDetectMuxes = 1; + assert( !Abc_ObjIsComplement(pNode) ); + if ( Cudd_ReadKeys(dd)-Cudd_ReadDead(dd) > (unsigned)nBddSizeMax ) + { + Extra_ProgressBarStop( pProgress ); + if ( fVerbose ) + printf( "The number of live nodes reached %d.\n", nBddSizeMax ); + fflush( stdout ); + return NULL; + } + // if the result is available return + if ( Abc_ObjGlobalBdd(pNode) == NULL ) + { + Abc_Obj_t * pNodeC, * pNode0, * pNode1; + pNode0 = Abc_ObjFanin0(pNode); + pNode1 = Abc_ObjFanin1(pNode); + // check for the special case when it is MUX/EXOR + if ( fDetectMuxes && + Abc_ObjGlobalBdd(pNode0) == NULL && Abc_ObjGlobalBdd(pNode1) == NULL && + Abc_ObjIsNode(pNode0) && Abc_ObjFanoutNum(pNode0) == 1 && + Abc_ObjIsNode(pNode1) && Abc_ObjFanoutNum(pNode1) == 1 && + Abc_NodeIsMuxType(pNode) ) + { + // deref the fanins + pNode0->vFanouts.nSize--; + pNode1->vFanouts.nSize--; + // recognize the MUX + pNodeC = Abc_NodeRecognizeMux( pNode, &pNode1, &pNode0 ); + assert( Abc_ObjFanoutNum(pNodeC) > 1 ); + // dereference the control once (the second time it will be derefed when BDDs are computed) + pNodeC->vFanouts.nSize--; + + // compute the result for all branches + bFuncC = Abc_NodeGlobalBdds_rec( dd, pNodeC, nBddSizeMax, fDropInternal, pProgress, pCounter, fVerbose ); + if ( bFuncC == NULL ) + return NULL; + Cudd_Ref( bFuncC ); + bFunc0 = Abc_NodeGlobalBdds_rec( dd, Abc_ObjRegular(pNode0), nBddSizeMax, fDropInternal, pProgress, pCounter, fVerbose ); + if ( bFunc0 == NULL ) + return NULL; + Cudd_Ref( bFunc0 ); + bFunc1 = Abc_NodeGlobalBdds_rec( dd, Abc_ObjRegular(pNode1), nBddSizeMax, fDropInternal, pProgress, pCounter, fVerbose ); + if ( bFunc1 == NULL ) + return NULL; + Cudd_Ref( bFunc1 ); + + // complement the branch BDDs + bFunc0 = Cudd_NotCond( bFunc0, Abc_ObjIsComplement(pNode0) ); + bFunc1 = Cudd_NotCond( bFunc1, Abc_ObjIsComplement(pNode1) ); + // get the final result + bFunc = Cudd_bddIte( dd, bFuncC, bFunc1, bFunc0 ); Cudd_Ref( bFunc ); + Cudd_RecursiveDeref( dd, bFunc0 ); + Cudd_RecursiveDeref( dd, bFunc1 ); + Cudd_RecursiveDeref( dd, bFuncC ); + // add the number of used nodes + (*pCounter) += 3; + } + else + { + // compute the result for both branches + bFunc0 = Abc_NodeGlobalBdds_rec( dd, Abc_ObjFanin(pNode,0), nBddSizeMax, fDropInternal, pProgress, pCounter, fVerbose ); + if ( bFunc0 == NULL ) + return NULL; + Cudd_Ref( bFunc0 ); + bFunc1 = Abc_NodeGlobalBdds_rec( dd, Abc_ObjFanin(pNode,1), nBddSizeMax, fDropInternal, pProgress, pCounter, fVerbose ); + if ( bFunc1 == NULL ) + return NULL; + Cudd_Ref( bFunc1 ); + bFunc0 = Cudd_NotCond( bFunc0, Abc_ObjFaninC0(pNode) ); + bFunc1 = Cudd_NotCond( bFunc1, Abc_ObjFaninC1(pNode) ); + // get the final result + bFunc = Cudd_bddAnd( dd, bFunc0, bFunc1 ); Cudd_Ref( bFunc ); + Cudd_RecursiveDeref( dd, bFunc0 ); + Cudd_RecursiveDeref( dd, bFunc1 ); + // add the number of used nodes + (*pCounter)++; + } + // set the result + assert( Abc_ObjGlobalBdd(pNode) == NULL ); + Abc_ObjSetGlobalBdd( pNode, bFunc ); + // increment the progress bar + if ( pProgress ) + Extra_ProgressBarUpdate( pProgress, *pCounter, NULL ); + } + // prepare the return value + bFunc = Abc_ObjGlobalBdd(pNode); + // dereference BDD at the node + if ( --pNode->vFanouts.nSize == 0 && fDropInternal ) + { + Cudd_Deref( bFunc ); + Abc_ObjSetGlobalBdd( pNode, NULL ); + } + return bFunc; +} + +/**Function************************************************************* + + Synopsis [Frees the global BDDs of the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdManager * Abc_NtkFreeGlobalBdds( Abc_Ntk_t * pNtk, int fFreeMan ) +{ + return Abc_NtkAttrFree( pNtk, VEC_ATTR_GLOBAL_BDD, fFreeMan ); +} + +/**Function************************************************************* + + Synopsis [Returns the shared size of global BDDs of the COs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkSizeOfGlobalBdds( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vFuncsGlob; + Abc_Obj_t * pObj; + int RetValue, i; + // complement the global functions + vFuncsGlob = Vec_PtrAlloc( Abc_NtkCoNum(pNtk) ); + Abc_NtkForEachCo( pNtk, pObj, i ) + Vec_PtrPush( vFuncsGlob, Abc_ObjGlobalBdd(pObj) ); + RetValue = Cudd_SharingSize( (DdNode **)Vec_PtrArray(vFuncsGlob), Vec_PtrSize(vFuncsGlob) ); + Vec_PtrFree( vFuncsGlob ); + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Computes the BDD of the logic cone of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +double Abc_NtkSpacePercentage( Abc_Obj_t * pNode ) +{ + /* + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj, * pNodeR; + DdManager * dd; + DdNode * bFunc; + double Result; + int i; + pNodeR = Abc_ObjRegular(pNode); + assert( Abc_NtkIsStrash(pNodeR->pNtk) ); + Abc_NtkCleanCopy( pNodeR->pNtk ); + // get the CIs in the support of the node + vNodes = Abc_NtkNodeSupport( pNodeR->pNtk, &pNodeR, 1 ); + // start the manager + dd = Cudd_Init( Vec_PtrSize(vNodes), 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 ); + Cudd_AutodynEnable( dd, CUDD_REORDER_SYMM_SIFT ); + // assign elementary BDDs for the CIs + Vec_PtrForEachEntry( vNodes, pObj, i ) + pObj->pCopy = (Abc_Obj_t *)dd->vars[i]; + // build the BDD of the cone + bFunc = Abc_NodeGlobalBdds_rec( dd, pNodeR, 10000000, 1, NULL, NULL, 1 ); Cudd_Ref( bFunc ); + bFunc = Cudd_NotCond( bFunc, pNode != pNodeR ); + // count minterms + Result = Cudd_CountMinterm( dd, bFunc, dd->size ); + // get the percentagle + Result *= 100.0; + for ( i = 0; i < dd->size; i++ ) + Result /= 2; + // clean up + Cudd_Quit( dd ); + Vec_PtrFree( vNodes ); + return Result; + */ + return 0.0; +} + + + + +/**Function************************************************************* + + Synopsis [Experiment with BDD-based representation of implications.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkBddImplicationTest() +{ + DdManager * dd; + DdNode * bImp, * bSum, * bTemp; + int nVars = 200; + int nImps = 200; + int i, clk; +clk = clock(); + dd = Cudd_Init( nVars, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 ); + Cudd_AutodynEnable( dd, CUDD_REORDER_SIFT ); + bSum = b0; Cudd_Ref( bSum ); + for ( i = 0; i < nImps; i++ ) + { + printf( "." ); + bImp = Cudd_bddAnd( dd, dd->vars[rand()%nVars], dd->vars[rand()%nVars] ); Cudd_Ref( bImp ); + bSum = Cudd_bddOr( dd, bTemp = bSum, bImp ); Cudd_Ref( bSum ); + Cudd_RecursiveDeref( dd, bTemp ); + Cudd_RecursiveDeref( dd, bImp ); + } + printf( "The BDD before = %d.\n", Cudd_DagSize(bSum) ); + Cudd_ReduceHeap( dd, CUDD_REORDER_SIFT, 1 ); + printf( "The BDD after = %d.\n", Cudd_DagSize(bSum) ); +PRT( "Time", clock() - clk ); + Cudd_RecursiveDeref( dd, bSum ); + Cudd_Quit( dd ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcOdc.c b/abc_with_bb_support/src/base/abci/abcOdc.c new file mode 100644 index 000000000..e87f00ba2 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcOdc.c @@ -0,0 +1,1134 @@ +/**CFile**************************************************************** + + FileName [abcOdc.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Scalable computation of observability don't-cares.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcOdc.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define ABC_DC_MAX_NODES (1<<15) + +typedef unsigned short Odc_Lit_t; + +typedef struct Odc_Obj_t_ Odc_Obj_t; // 16 bytes +struct Odc_Obj_t_ +{ + Odc_Lit_t iFan0; // first fanin + Odc_Lit_t iFan1; // second fanin + Odc_Lit_t iNext; // next node in the hash table + unsigned short TravId; // the traversal ID + unsigned uData; // the computed data + unsigned uMask; // the variable mask +}; + +typedef struct Odc_Man_t_ Odc_Man_t; +struct Odc_Man_t_ +{ + // dont'-care parameters + int nVarsMax; // the max number of cut variables + int nLevels; // the number of ODC levels + int fVerbose; // the verbosiness flag + int fVeryVerbose;// the verbosiness flag to print per-node stats + int nPercCutoff; // cutoff percentage + + // windowing + Abc_Obj_t * pNode; // the node for windowing + Vec_Ptr_t * vLeaves; // the number of the cut + Vec_Ptr_t * vRoots; // the roots of the cut + Vec_Ptr_t * vBranches; // additional inputs + + // internal AIG package + // objects + int nPis; // number of PIs (nVarsMax + 32) + int nObjs; // number of objects (Const1, PIs, ANDs) + int nObjsAlloc; // number of objects allocated + Odc_Obj_t * pObjs; // objects + Odc_Lit_t iRoot; // the root object + unsigned short nTravIds; // the number of travIDs + // structural hashing + Odc_Lit_t * pTable; // hash table + int nTableSize; // hash table size + Vec_Int_t * vUsedSpots; // the used spots + + // truth tables + int nBits; // the number of bits + int nWords; // the number of words + Vec_Ptr_t * vTruths; // truth tables for each node + Vec_Ptr_t * vTruthsElem; // elementary truth tables for the PIs + unsigned * puTruth; // the place where the resulting truth table does + + // statistics + int nWins; // the number of windows processed + int nWinsEmpty; // the number of empty windows + int nSimsEmpty; // the number of empty simulation infos + int nQuantsOver; // the number of quantification overflows + int nWinsFinish; // the number of windows that finished + int nTotalDcs; // total percentage of DCs + + // runtime + int timeClean; // windowing + int timeWin; // windowing + int timeMiter; // computing the miter + int timeSim; // simulation + int timeQuant; // quantification + int timeTruth; // truth table + int timeTotal; // useful runtime + int timeAbort; // aborted runtime +}; + + +// quantity of different objects +static inline int Odc_PiNum( Odc_Man_t * p ) { return p->nPis; } +static inline int Odc_NodeNum( Odc_Man_t * p ) { return p->nObjs - p->nPis - 1; } +static inline int Odc_ObjNum( Odc_Man_t * p ) { return p->nObjs; } + +// complemented attributes of objects +static inline int Odc_IsComplement( Odc_Lit_t Lit ) { return Lit & (Odc_Lit_t)1; } +static inline Odc_Lit_t Odc_Regular( Odc_Lit_t Lit ) { return Lit & ~(Odc_Lit_t)1; } +static inline Odc_Lit_t Odc_Not( Odc_Lit_t Lit ) { return Lit ^ (Odc_Lit_t)1; } +static inline Odc_Lit_t Odc_NotCond( Odc_Lit_t Lit, int c ) { return Lit ^ (Odc_Lit_t)(c!=0); } + +// specialized Literals +static inline Odc_Lit_t Odc_Const0() { return 1; } +static inline Odc_Lit_t Odc_Const1() { return 0; } +static inline Odc_Lit_t Odc_Var( Odc_Man_t * p, int i ) { assert( i >= 0 && i < p->nPis ); return (i+1) << 1; } +static inline int Odc_IsConst( Odc_Lit_t Lit ) { return Lit < (Odc_Lit_t)2; } +static inline int Odc_IsTerm( Odc_Man_t * p, Odc_Lit_t Lit ) { return (int)(Lit>>1) <= p->nPis; } + +// accessing internal storage +static inline Odc_Obj_t * Odc_ObjNew( Odc_Man_t * p ) { assert( p->nObjs < p->nObjsAlloc ); return p->pObjs + p->nObjs++; } +static inline Odc_Lit_t Odc_Obj2Lit( Odc_Man_t * p, Odc_Obj_t * pObj ) { assert( pObj ); return (pObj - p->pObjs) << 1; } +static inline Odc_Obj_t * Odc_Lit2Obj( Odc_Man_t * p, Odc_Lit_t Lit ) { assert( !(Lit & 1) && (int)(Lit>>1) < p->nObjs ); return p->pObjs + (Lit>>1); } + +// fanins and their complements +static inline Odc_Lit_t Odc_ObjChild0( Odc_Obj_t * pObj ) { return pObj->iFan0; } +static inline Odc_Lit_t Odc_ObjChild1( Odc_Obj_t * pObj ) { return pObj->iFan1; } +static inline Odc_Lit_t Odc_ObjFanin0( Odc_Obj_t * pObj ) { return Odc_Regular(pObj->iFan0); } +static inline Odc_Lit_t Odc_ObjFanin1( Odc_Obj_t * pObj ) { return Odc_Regular(pObj->iFan1); } +static inline int Odc_ObjFaninC0( Odc_Obj_t * pObj ) { return Odc_IsComplement(pObj->iFan0); } +static inline int Odc_ObjFaninC1( Odc_Obj_t * pObj ) { return Odc_IsComplement(pObj->iFan1); } + +// traversal IDs +static inline void Odc_ManIncrementTravId( Odc_Man_t * p ) { p->nTravIds++; } +static inline void Odc_ObjSetTravIdCurrent( Odc_Man_t * p, Odc_Obj_t * pObj ) { pObj->TravId = p->nTravIds; } +static inline int Odc_ObjIsTravIdCurrent( Odc_Man_t * p, Odc_Obj_t * pObj ) { return (int )((int)pObj->TravId == p->nTravIds); } + +// truth tables +static inline unsigned * Odc_ObjTruth( Odc_Man_t * p, Odc_Lit_t Lit ) { assert( !(Lit & 1) ); return Vec_PtrEntry(p->vTruths, Lit >> 1); } + +// iterators +#define Odc_ForEachPi( p, Lit, i ) \ + for ( i = 0; (i < Odc_PiNum(p)) && (((Lit) = Odc_Var(p, i)), 1); i++ ) +#define Odc_ForEachAnd( p, pObj, i ) \ + for ( i = 1 + Odc_CiNum(p); (i < Odc_ObjNum(p)) && ((pObj) = (p)->pObjs + i); i++ ) + + +// exported functions +extern Odc_Man_t * Abc_NtkDontCareAlloc( int nVarsMax, int nLevels, int fVerbose, int fVeryVerbose ); +extern void Abc_NtkDontCareClear( Odc_Man_t * p ); +extern void Abc_NtkDontCareFree( Odc_Man_t * p ); +extern int Abc_NtkDontCareCompute( Odc_Man_t * p, Abc_Obj_t * pNode, Vec_Ptr_t * vLeaves, unsigned * puTruth ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates the don't-care manager.] + + Description [The parameters are the max number of cut variables, + the number of fanout levels used for the ODC computation, and verbosiness.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Odc_Man_t * Abc_NtkDontCareAlloc( int nVarsMax, int nLevels, int fVerbose, int fVeryVerbose ) +{ + Odc_Man_t * p; + unsigned * pData; + int i, k; + p = ALLOC( Odc_Man_t, 1 ); + memset( p, 0, sizeof(Odc_Man_t) ); + assert( nVarsMax > 4 && nVarsMax < 16 ); + assert( nLevels > 0 && nLevels < 10 ); + + srand( 0xABC ); + + // dont'-care parameters + p->nVarsMax = nVarsMax; + p->nLevels = nLevels; + p->fVerbose = fVerbose; + p->fVeryVerbose = fVeryVerbose; + p->nPercCutoff = 10; + + // windowing + p->vRoots = Vec_PtrAlloc( 128 ); + p->vBranches = Vec_PtrAlloc( 128 ); + + // internal AIG package + // allocate room for objects + p->nObjsAlloc = ABC_DC_MAX_NODES; + p->pObjs = ALLOC( Odc_Obj_t, p->nObjsAlloc * sizeof(Odc_Obj_t) ); + p->nPis = nVarsMax + 32; + p->nObjs = 1 + p->nPis; + memset( p->pObjs, 0, p->nObjs * sizeof(Odc_Obj_t) ); + // set the PI masks + for ( i = 0; i < 32; i++ ) + p->pObjs[1 + p->nVarsMax + i].uMask = (1 << i); + // allocate hash table + p->nTableSize = p->nObjsAlloc/3 + 1; + p->pTable = ALLOC( Odc_Lit_t, p->nTableSize * sizeof(Odc_Lit_t) ); + memset( p->pTable, 0, p->nTableSize * sizeof(Odc_Lit_t) ); + p->vUsedSpots = Vec_IntAlloc( 1000 ); + + // truth tables + p->nWords = Abc_TruthWordNum( p->nVarsMax ); + p->nBits = p->nWords * 8 * sizeof(unsigned); + p->vTruths = Vec_PtrAllocSimInfo( p->nObjsAlloc, p->nWords ); + p->vTruthsElem = Vec_PtrAllocSimInfo( p->nVarsMax, p->nWords ); + + // set elementary truth tables + Abc_InfoFill( Vec_PtrEntry(p->vTruths, 0), p->nWords ); + for ( k = 0; k < p->nVarsMax; k++ ) + { +// pData = Odc_ObjTruth( p, Odc_Var(p, k) ); + pData = Vec_PtrEntry( p->vTruthsElem, k ); + Abc_InfoClear( pData, p->nWords ); + for ( i = 0; i < p->nBits; i++ ) + if ( i & (1 << k) ) + pData[i>>5] |= (1 << (i&31)); + } + + // set random truth table for the additional inputs + for ( k = p->nVarsMax; k < p->nPis; k++ ) + { + pData = Odc_ObjTruth( p, Odc_Var(p, k) ); + Abc_InfoRandom( pData, p->nWords ); + } + + // set the miter to the unused value + p->iRoot = 0xffff; + return p; +} + +/**Function************************************************************* + + Synopsis [Clears the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDontCareClear( Odc_Man_t * p ) +{ + int clk = clock(); + // clean the structural hashing table + if ( Vec_IntSize(p->vUsedSpots) > p->nTableSize/3 ) // more than one third + memset( p->pTable, 0, sizeof(Odc_Lit_t) * p->nTableSize ); + else + { + int iSpot, i; + Vec_IntForEachEntry( p->vUsedSpots, iSpot, i ) + p->pTable[iSpot] = 0; + } + Vec_IntClear( p->vUsedSpots ); + // reset the number of nodes + p->nObjs = 1 + p->nPis; + // reset the root node + p->iRoot = 0xffff; + +p->timeClean += clock() - clk; +} + +/**Function************************************************************* + + Synopsis [Frees the don't-care manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDontCareFree( Odc_Man_t * p ) +{ + if ( p->fVerbose ) + { + printf( "Wins = %5d. Empty = %5d. SimsEmpty = %5d. QuantOver = %5d. WinsFinish = %5d.\n", + p->nWins, p->nWinsEmpty, p->nSimsEmpty, p->nQuantsOver, p->nWinsFinish ); + printf( "Ave DCs per window = %6.2f %%. Ave DCs per finished window = %6.2f %%.\n", + 1.0*p->nTotalDcs/p->nWins, 1.0*p->nTotalDcs/p->nWinsFinish ); + printf( "Runtime stats of the ODC manager:\n" ); + PRT( "Cleaning ", p->timeClean ); + PRT( "Windowing ", p->timeWin ); + PRT( "Miter ", p->timeMiter ); + PRT( "Simulation ", p->timeSim ); + PRT( "Quantifying ", p->timeQuant ); + PRT( "Truth table ", p->timeTruth ); + PRT( "TOTAL ", p->timeTotal ); + PRT( "Aborted ", p->timeAbort ); + } + Vec_PtrFree( p->vRoots ); + Vec_PtrFree( p->vBranches ); + Vec_PtrFree( p->vTruths ); + Vec_PtrFree( p->vTruthsElem ); + Vec_IntFree( p->vUsedSpots ); + free( p->pObjs ); + free( p->pTable ); + free( p ); +} + + + +/**Function************************************************************* + + Synopsis [Marks the TFO of the collected nodes up to the given level.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDontCareWinSweepLeafTfo_rec( Abc_Obj_t * pObj, int nLevelLimit, Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pFanout; + int i; + if ( Abc_ObjIsCo(pObj) || (int)pObj->Level > nLevelLimit || pObj == pNode ) + return; + if ( Abc_NodeIsTravIdCurrent(pObj) ) + return; + Abc_NodeSetTravIdCurrent( pObj ); + //////////////////////////////////////// + // try to reduce the runtime + if ( Abc_ObjFanoutNum(pObj) > 100 ) + return; + //////////////////////////////////////// + Abc_ObjForEachFanout( pObj, pFanout, i ) + Abc_NtkDontCareWinSweepLeafTfo_rec( pFanout, nLevelLimit, pNode ); +} + +/**Function************************************************************* + + Synopsis [Marks the TFO of the collected nodes up to the given level.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDontCareWinSweepLeafTfo( Odc_Man_t * p ) +{ + Abc_Obj_t * pObj; + int i; + Abc_NtkIncrementTravId( p->pNode->pNtk ); + Vec_PtrForEachEntry( p->vLeaves, pObj, i ) + Abc_NtkDontCareWinSweepLeafTfo_rec( pObj, p->pNode->Level + p->nLevels, p->pNode ); +} + +/**Function************************************************************* + + Synopsis [Recursively collects the roots.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDontCareWinCollectRoots_rec( Abc_Obj_t * pObj, Vec_Ptr_t * vRoots ) +{ + Abc_Obj_t * pFanout; + int i; + assert( Abc_ObjIsNode(pObj) ); + assert( Abc_NodeIsTravIdCurrent(pObj) ); + // check if the node has all fanouts marked + Abc_ObjForEachFanout( pObj, pFanout, i ) + if ( !Abc_NodeIsTravIdCurrent(pFanout) ) + break; + // if some of the fanouts are unmarked, add the node to the root + if ( i < Abc_ObjFanoutNum(pObj) ) + { + Vec_PtrPushUnique( vRoots, pObj ); + return; + } + // otherwise, call recursively + Abc_ObjForEachFanout( pObj, pFanout, i ) + Abc_NtkDontCareWinCollectRoots_rec( pFanout, vRoots ); +} + +/**Function************************************************************* + + Synopsis [Collects the roots of the window.] + + Description [Roots of the window are the nodes that have at least + one fanout that it not in the TFO of the leaves.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDontCareWinCollectRoots( Odc_Man_t * p ) +{ + assert( !Abc_NodeIsTravIdCurrent(p->pNode) ); + // mark the node with the old traversal ID + Abc_NodeSetTravIdCurrent( p->pNode ); + // collect the roots + Vec_PtrClear( p->vRoots ); + Abc_NtkDontCareWinCollectRoots_rec( p->pNode, p->vRoots ); +} + +/**Function************************************************************* + + Synopsis [Recursively adds missing nodes and leaves.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkDontCareWinAddMissing_rec( Odc_Man_t * p, Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanin; + int i; + // skip the already collected leaves and branches + if ( Abc_NodeIsTravIdCurrent(pObj) ) + return 1; + // if this is not an internal node - make it a new branch + if ( !Abc_NodeIsTravIdPrevious(pObj) || Abc_ObjIsCi(pObj) ) //|| (int)pObj->Level <= p->nLevLeaves ) + { + Abc_NodeSetTravIdCurrent( pObj ); + Vec_PtrPush( p->vBranches, pObj ); + return Vec_PtrSize(p->vBranches) <= 32; + } + // visit the fanins of the node + Abc_ObjForEachFanin( pObj, pFanin, i ) + if ( !Abc_NtkDontCareWinAddMissing_rec( p, pFanin ) ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Adds to the window nodes and leaves in the TFI of the roots.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkDontCareWinAddMissing( Odc_Man_t * p ) +{ + Abc_Obj_t * pObj; + int i; + // set the leaves + Abc_NtkIncrementTravId( p->pNode->pNtk ); + Vec_PtrForEachEntry( p->vLeaves, pObj, i ) + Abc_NodeSetTravIdCurrent( pObj ); + // explore from the roots + Vec_PtrClear( p->vBranches ); + Vec_PtrForEachEntry( p->vRoots, pObj, i ) + if ( !Abc_NtkDontCareWinAddMissing_rec( p, pObj ) ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Computes window for the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkDontCareWindow( Odc_Man_t * p ) +{ + // mark the TFO of the collected nodes up to the given level (p->pNode->Level + p->nWinTfoMax) + Abc_NtkDontCareWinSweepLeafTfo( p ); + // find the roots of the window + Abc_NtkDontCareWinCollectRoots( p ); + if ( Vec_PtrSize(p->vRoots) == 1 && Vec_PtrEntry(p->vRoots, 0) == p->pNode ) + { +// printf( "Empty window\n" ); + return 0; + } + // add the nodes in the TFI of the roots that are not yet in the window + if ( !Abc_NtkDontCareWinAddMissing( p ) ) + { +// printf( "Too many branches (%d)\n", Vec_PtrSize(p->vBranches) ); + return 0; + } + return 1; +} + + + + + +/**Function************************************************************* + + Synopsis [Performing hashing of two AIG Literals.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Odc_HashKey( Odc_Lit_t iFan0, Odc_Lit_t iFan1, int TableSize ) +{ + unsigned Key = 0; + Key ^= Odc_Regular(iFan0) * 7937; + Key ^= Odc_Regular(iFan1) * 2971; + Key ^= Odc_IsComplement(iFan0) * 911; + Key ^= Odc_IsComplement(iFan1) * 353; + return Key % TableSize; +} + +/**Function************************************************************* + + Synopsis [Checks if the given name node already exists in the table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Odc_Lit_t * Odc_HashLookup( Odc_Man_t * p, Odc_Lit_t iFan0, Odc_Lit_t iFan1 ) +{ + Odc_Obj_t * pObj; + Odc_Lit_t * pEntry; + unsigned uHashKey; + assert( iFan0 < iFan1 ); + // get the hash key for this node + uHashKey = Odc_HashKey( iFan0, iFan1, p->nTableSize ); + // remember the spot in the hash table that will be used + if ( p->pTable[uHashKey] == 0 ) + Vec_IntPush( p->vUsedSpots, uHashKey ); + // find the entry + for ( pEntry = p->pTable + uHashKey; *pEntry; pEntry = &pObj->iNext ) + { + pObj = Odc_Lit2Obj( p, *pEntry ); + if ( pObj->iFan0 == iFan0 && pObj->iFan1 == iFan1 ) + return pEntry; + } + return pEntry; +} + +/**Function************************************************************* + + Synopsis [Finds node by structural hashing or creates a new node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Odc_Lit_t Odc_And( Odc_Man_t * p, Odc_Lit_t iFan0, Odc_Lit_t iFan1 ) +{ + Odc_Obj_t * pObj; + Odc_Lit_t * pEntry; + unsigned uMask0, uMask1; + int Temp; + // consider trivial cases + if ( iFan0 == iFan1 ) + return iFan0; + if ( iFan0 == Odc_Not(iFan1) ) + return Odc_Const0(); + if ( Odc_Regular(iFan0) == Odc_Const1() ) + return iFan0 == Odc_Const1() ? iFan1 : Odc_Const0(); + if ( Odc_Regular(iFan1) == Odc_Const1() ) + return iFan1 == Odc_Const1() ? iFan0 : Odc_Const0(); + // canonicize the fanin order + if ( iFan0 > iFan1 ) + Temp = iFan0, iFan0 = iFan1, iFan1 = Temp; + // check if a node with these fanins exists + pEntry = Odc_HashLookup( p, iFan0, iFan1 ); + if ( *pEntry ) + return *pEntry; + // create a new node + pObj = Odc_ObjNew( p ); + pObj->iFan0 = iFan0; + pObj->iFan1 = iFan1; + pObj->iNext = 0; + pObj->TravId = 0; + // set the mask + uMask0 = Odc_Lit2Obj(p, Odc_Regular(iFan0))->uMask; + uMask1 = Odc_Lit2Obj(p, Odc_Regular(iFan1))->uMask; + pObj->uMask = uMask0 | uMask1; + // add to the table + *pEntry = Odc_Obj2Lit( p, pObj ); + return *pEntry; +} + +/**Function************************************************************* + + Synopsis [Boolean OR.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Odc_Lit_t Odc_Or( Odc_Man_t * p, Odc_Lit_t iFan0, Odc_Lit_t iFan1 ) +{ + return Odc_Not( Odc_And(p, Odc_Not(iFan0), Odc_Not(iFan1)) ); +} + +/**Function************************************************************* + + Synopsis [Boolean XOR.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Odc_Lit_t Odc_Xor( Odc_Man_t * p, Odc_Lit_t iFan0, Odc_Lit_t iFan1 ) +{ + return Odc_Or( p, Odc_And(p, iFan0, Odc_Not(iFan1)), Odc_And(p, Odc_Not(iFan0), iFan1) ); +} + + + + + +/**Function************************************************************* + + Synopsis [Transfers the window into the AIG package.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void * Abc_NtkDontCareTransfer_rec( Odc_Man_t * p, Abc_Obj_t * pNode, Abc_Obj_t * pPivot ) +{ + unsigned uData0, uData1; + Odc_Lit_t uLit0, uLit1, uRes0, uRes1; + assert( !Abc_ObjIsComplement(pNode) ); + // skip visited objects + if ( Abc_NodeIsTravIdCurrent(pNode) ) + return pNode->pCopy; + Abc_NodeSetTravIdCurrent(pNode); + assert( Abc_ObjIsNode(pNode) ); + // consider the case when the node is the pivot + if ( pNode == pPivot ) + return pNode->pCopy = (void *)((Odc_Const1() << 16) | Odc_Const0()); + // compute the cofactors + uData0 = (unsigned)Abc_NtkDontCareTransfer_rec( p, Abc_ObjFanin0(pNode), pPivot ); + uData1 = (unsigned)Abc_NtkDontCareTransfer_rec( p, Abc_ObjFanin1(pNode), pPivot ); + // find the 0-cofactor + uLit0 = Odc_NotCond( (Odc_Lit_t)(uData0 & 0xffff), Abc_ObjFaninC0(pNode) ); + uLit1 = Odc_NotCond( (Odc_Lit_t)(uData1 & 0xffff), Abc_ObjFaninC1(pNode) ); + uRes0 = Odc_And( p, uLit0, uLit1 ); + // find the 1-cofactor + uLit0 = Odc_NotCond( (Odc_Lit_t)(uData0 >> 16), Abc_ObjFaninC0(pNode) ); + uLit1 = Odc_NotCond( (Odc_Lit_t)(uData1 >> 16), Abc_ObjFaninC1(pNode) ); + uRes1 = Odc_And( p, uLit0, uLit1 ); + // find the result + return pNode->pCopy = (void *)((uRes1 << 16) | uRes0); +} + +/**Function************************************************************* + + Synopsis [Transfers the window into the AIG package.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkDontCareTransfer( Odc_Man_t * p ) +{ + Abc_Obj_t * pObj; + Odc_Lit_t uRes0, uRes1; + Odc_Lit_t uLit; + unsigned uData; + int i; + Abc_NtkIncrementTravId( p->pNode->pNtk ); + // set elementary variables at the leaves + Vec_PtrForEachEntry( p->vLeaves, pObj, i ) + { + uLit = Odc_Var( p, i ); + pObj->pCopy = (void *)((uLit << 16) | uLit); + Abc_NodeSetTravIdCurrent(pObj); + } + // set elementary variables at the branched + Vec_PtrForEachEntry( p->vBranches, pObj, i ) + { + uLit = Odc_Var( p, i+p->nVarsMax ); + pObj->pCopy = (void *)((uLit << 16) | uLit); + Abc_NodeSetTravIdCurrent(pObj); + } + // compute the AIG for the window + p->iRoot = Odc_Const0(); + Vec_PtrForEachEntry( p->vRoots, pObj, i ) + { + uData = (unsigned)Abc_NtkDontCareTransfer_rec( p, pObj, p->pNode ); + // get the cofactors + uRes0 = uData & 0xffff; + uRes1 = uData >> 16; + // compute the miter +// assert( uRes0 != uRes1 ); // may be false if the node is redundant w.r.t. this root + uLit = Odc_Xor( p, uRes0, uRes1 ); + p->iRoot = Odc_Or( p, p->iRoot, uLit ); + } + return 1; +} + + +/**Function************************************************************* + + Synopsis [Recursively computes the pair of cofactors.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Abc_NtkDontCareCofactors_rec( Odc_Man_t * p, Odc_Lit_t Lit, unsigned uMask ) +{ + Odc_Obj_t * pObj; + unsigned uData0, uData1; + Odc_Lit_t uLit0, uLit1, uRes0, uRes1; + assert( !Odc_IsComplement(Lit) ); + // skip visited objects + pObj = Odc_Lit2Obj( p, Lit ); + if ( Odc_ObjIsTravIdCurrent(p, pObj) ) + return pObj->uData; + Odc_ObjSetTravIdCurrent(p, pObj); + // skip objects out of the cone + if ( (pObj->uMask & uMask) == 0 ) + return pObj->uData = ((Lit << 16) | Lit); + // consider the case when the node is the var + if ( pObj->uMask == uMask && Odc_IsTerm(p, Lit) ) + return pObj->uData = ((Odc_Const1() << 16) | Odc_Const0()); + // compute the cofactors + uData0 = Abc_NtkDontCareCofactors_rec( p, Odc_ObjFanin0(pObj), uMask ); + uData1 = Abc_NtkDontCareCofactors_rec( p, Odc_ObjFanin1(pObj), uMask ); + // find the 0-cofactor + uLit0 = Odc_NotCond( (Odc_Lit_t)(uData0 & 0xffff), Odc_ObjFaninC0(pObj) ); + uLit1 = Odc_NotCond( (Odc_Lit_t)(uData1 & 0xffff), Odc_ObjFaninC1(pObj) ); + uRes0 = Odc_And( p, uLit0, uLit1 ); + // find the 1-cofactor + uLit0 = Odc_NotCond( (Odc_Lit_t)(uData0 >> 16), Odc_ObjFaninC0(pObj) ); + uLit1 = Odc_NotCond( (Odc_Lit_t)(uData1 >> 16), Odc_ObjFaninC1(pObj) ); + uRes1 = Odc_And( p, uLit0, uLit1 ); + // find the result + return pObj->uData = ((uRes1 << 16) | uRes0); +} + +/**Function************************************************************* + + Synopsis [Quantifies the branch variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkDontCareQuantify( Odc_Man_t * p ) +{ + Odc_Lit_t uRes0, uRes1; + unsigned uData; + int i; + assert( p->iRoot < 0xffff ); + assert( Vec_PtrSize(p->vBranches) <= 32 ); // the mask size + for ( i = 0; i < Vec_PtrSize(p->vBranches); i++ ) + { + // compute the cofactors w.r.t. this variable + Odc_ManIncrementTravId( p ); + uData = Abc_NtkDontCareCofactors_rec( p, Odc_Regular(p->iRoot), (1 << i) ); + uRes0 = Odc_NotCond( (Odc_Lit_t)(uData & 0xffff), Odc_IsComplement(p->iRoot) ); + uRes1 = Odc_NotCond( (Odc_Lit_t)(uData >> 16), Odc_IsComplement(p->iRoot) ); + // quantify this variable existentially + p->iRoot = Odc_Or( p, uRes0, uRes1 ); + // check the limit + if ( Odc_ObjNum(p) > ABC_DC_MAX_NODES/2 ) + return 0; + } + assert( p->nObjs <= p->nObjsAlloc ); + return 1; +} + + + +/**Function************************************************************* + + Synopsis [Set elementary truth tables for PIs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDontCareSimulateSetElem2( Odc_Man_t * p ) +{ + unsigned * pData; + int i, k; + for ( k = 0; k < p->nVarsMax; k++ ) + { + pData = Odc_ObjTruth( p, Odc_Var(p, k) ); + Abc_InfoClear( pData, p->nWords ); + for ( i = 0; i < p->nBits; i++ ) + if ( i & (1 << k) ) + pData[i>>5] |= (1 << (i&31)); + } +} + +/**Function************************************************************* + + Synopsis [Set elementary truth tables for PIs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDontCareSimulateSetElem( Odc_Man_t * p ) +{ + unsigned * pData, * pData2; + int k; + for ( k = 0; k < p->nVarsMax; k++ ) + { + pData = Odc_ObjTruth( p, Odc_Var(p, k) ); + pData2 = Vec_PtrEntry( p->vTruthsElem, k ); + Abc_InfoCopy( pData, pData2, p->nWords ); + } +} + +/**Function************************************************************* + + Synopsis [Set random simulation words for PIs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDontCareSimulateSetRand( Odc_Man_t * p ) +{ + unsigned * pData; + int w, k, Number; + for ( w = 0; w < p->nWords; w++ ) + { + Number = rand(); + for ( k = 0; k < p->nVarsMax; k++ ) + { + pData = Odc_ObjTruth( p, Odc_Var(p, k) ); + pData[w] = (Number & (1<nWords; w++ ) + if ( puTruth[w] ) + Counter++; + return Counter; +} + +/**Function************************************************************* + + Synopsis [Simulates one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDontCareTruthOne( Odc_Man_t * p, Odc_Lit_t Lit ) +{ + Odc_Obj_t * pObj; + unsigned * pInfo, * pInfo1, * pInfo2; + int k, fComp1, fComp2; + assert( !Odc_IsComplement( Lit ) ); + assert( !Odc_IsTerm( p, Lit ) ); + // get the truth tables + pObj = Odc_Lit2Obj( p, Lit ); + pInfo = Odc_ObjTruth( p, Lit ); + pInfo1 = Odc_ObjTruth( p, Odc_ObjFanin0(pObj) ); + pInfo2 = Odc_ObjTruth( p, Odc_ObjFanin1(pObj) ); + fComp1 = Odc_ObjFaninC0( pObj ); + fComp2 = Odc_ObjFaninC1( pObj ); + // simulate + if ( fComp1 && fComp2 ) + for ( k = 0; k < p->nWords; k++ ) + pInfo[k] = ~pInfo1[k] & ~pInfo2[k]; + else if ( fComp1 && !fComp2 ) + for ( k = 0; k < p->nWords; k++ ) + pInfo[k] = ~pInfo1[k] & pInfo2[k]; + else if ( !fComp1 && fComp2 ) + for ( k = 0; k < p->nWords; k++ ) + pInfo[k] = pInfo1[k] & ~pInfo2[k]; + else // if ( fComp1 && fComp2 ) + for ( k = 0; k < p->nWords; k++ ) + pInfo[k] = pInfo1[k] & pInfo2[k]; +} + +/**Function************************************************************* + + Synopsis [Computes the truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDontCareSimulate_rec( Odc_Man_t * p, Odc_Lit_t Lit ) +{ + Odc_Obj_t * pObj; + assert( !Odc_IsComplement(Lit) ); + // skip terminals + if ( Odc_IsTerm(p, Lit) ) + return; + // skip visited objects + pObj = Odc_Lit2Obj( p, Lit ); + if ( Odc_ObjIsTravIdCurrent(p, pObj) ) + return; + Odc_ObjSetTravIdCurrent(p, pObj); + // call recursively + Abc_NtkDontCareSimulate_rec( p, Odc_ObjFanin0(pObj) ); + Abc_NtkDontCareSimulate_rec( p, Odc_ObjFanin1(pObj) ); + // construct the truth table + Abc_NtkDontCareTruthOne( p, Lit ); +} + +/**Function************************************************************* + + Synopsis [Computes the truth table of the care set.] + + Description [Returns the number of ones in the simulation info.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkDontCareSimulate( Odc_Man_t * p, unsigned * puTruth ) +{ + Odc_ManIncrementTravId( p ); + Abc_NtkDontCareSimulate_rec( p, Odc_Regular(p->iRoot) ); + Abc_InfoCopy( puTruth, Odc_ObjTruth(p, Odc_Regular(p->iRoot)), p->nWords ); + if ( Odc_IsComplement(p->iRoot) ) + Abc_InfoNot( puTruth, p->nWords ); + return Extra_TruthCountOnes( puTruth, p->nVarsMax ); +} + +/**Function************************************************************* + + Synopsis [Computes the truth table of the care set.] + + Description [Returns the number of ones in the simulation info.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkDontCareSimulateBefore( Odc_Man_t * p, unsigned * puTruth ) +{ + int nIters = 2; + int nRounds, Counter, r; + // decide how many rounds to simulate + nRounds = p->nBits / p->nWords; + Counter = 0; + for ( r = 0; r < nIters; r++ ) + { + Abc_NtkDontCareSimulateSetRand( p ); + Abc_NtkDontCareSimulate( p, puTruth ); + Counter += Abc_NtkDontCareCountMintsWord( p, puTruth ); + } + // normalize + Counter = Counter * nRounds / nIters; + return Counter; +} + +/**Function************************************************************* + + Synopsis [Computes ODCs for the node in terms of the cut variables.] + + Description [Returns the number of don't care minterms in the truth table. + In particular, this procedure returns 0 if there is no don't-cares.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkDontCareCompute( Odc_Man_t * p, Abc_Obj_t * pNode, Vec_Ptr_t * vLeaves, unsigned * puTruth ) +{ + int nMints, RetValue; + int clk, clkTotal = clock(); + + p->nWins++; + + // set the parameters + assert( !Abc_ObjIsComplement(pNode) ); + assert( Abc_ObjIsNode(pNode) ); + assert( Vec_PtrSize(vLeaves) <= p->nVarsMax ); + p->vLeaves = vLeaves; + p->pNode = pNode; + + // compute the window +clk = clock(); + RetValue = Abc_NtkDontCareWindow( p ); +p->timeWin += clock() - clk; + if ( !RetValue ) + { +p->timeAbort += clock() - clkTotal; + Abc_InfoFill( puTruth, p->nWords ); + p->nWinsEmpty++; + return 0; + } + + if ( p->fVeryVerbose ) + { + printf( " %5d : ", pNode->Id ); + printf( "Leaf = %2d ", Vec_PtrSize(p->vLeaves) ); + printf( "Root = %2d ", Vec_PtrSize(p->vRoots) ); + printf( "Bran = %2d ", Vec_PtrSize(p->vBranches) ); + printf( " | " ); + } + + // transfer the window into the AIG package +clk = clock(); + Abc_NtkDontCareTransfer( p ); +p->timeMiter += clock() - clk; + + // simulate to estimate the amount of don't-cares +clk = clock(); + nMints = Abc_NtkDontCareSimulateBefore( p, puTruth ); +p->timeSim += clock() - clk; + if ( p->fVeryVerbose ) + { + printf( "AIG = %5d ", Odc_NodeNum(p) ); + printf( "%6.2f %% ", 100.0 * (p->nBits - nMints) / p->nBits ); + } + + // if there is less then the given percentage of don't-cares, skip + if ( 100.0 * (p->nBits - nMints) / p->nBits < 1.0 * p->nPercCutoff ) + { +p->timeAbort += clock() - clkTotal; + if ( p->fVeryVerbose ) + printf( "Simulation cutoff.\n" ); + Abc_InfoFill( puTruth, p->nWords ); + p->nSimsEmpty++; + return 0; + } + + // quantify external variables +clk = clock(); + RetValue = Abc_NtkDontCareQuantify( p ); +p->timeQuant += clock() - clk; + if ( !RetValue ) + { +p->timeAbort += clock() - clkTotal; + if ( p->fVeryVerbose ) + printf( "=== Overflow! ===\n" ); + Abc_InfoFill( puTruth, p->nWords ); + p->nQuantsOver++; + return 0; + } + + // get the truth table +clk = clock(); + Abc_NtkDontCareSimulateSetElem( p ); + nMints = Abc_NtkDontCareSimulate( p, puTruth ); +p->timeTruth += clock() - clk; + if ( p->fVeryVerbose ) + { + printf( "AIG = %5d ", Odc_NodeNum(p) ); + printf( "%6.2f %% ", 100.0 * (p->nBits - nMints) / p->nBits ); + printf( "\n" ); + } +p->timeTotal += clock() - clkTotal; + p->nWinsFinish++; + p->nTotalDcs += (int)(100.0 * (p->nBits - nMints) / p->nBits); + return nMints; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcOrder.c b/abc_with_bb_support/src/base/abci/abcOrder.c new file mode 100644 index 000000000..d383a3fbd --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcOrder.c @@ -0,0 +1,131 @@ +/**CFile**************************************************************** + + FileName [abcOrder.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Exploring static BDD variable orders.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcOrder.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Abc_NtkChangeCiOrder( Abc_Ntk_t * pNtk, Vec_Ptr_t * vSupp, int fReverse ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Changes the order of primary inputs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkFindCiOrder( Abc_Ntk_t * pNtk, int fReverse, int fVerbose ) +{ + Vec_Ptr_t * vSupp; + vSupp = Abc_NtkSupport( pNtk ); + Abc_NtkChangeCiOrder( pNtk, vSupp, fReverse ); + Vec_PtrFree( vSupp ); +} + +/**Function************************************************************* + + Synopsis [Implements the given variable order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkImplementCiOrder( Abc_Ntk_t * pNtk, char * pFileName, int fReverse, int fVerbose ) +{ + char Buffer[1000]; + FILE * pFile; + Vec_Ptr_t * vSupp; + Abc_Obj_t * pObj; + pFile = fopen( pFileName, "r" ); + vSupp = Vec_PtrAlloc( Abc_NtkCiNum(pNtk) ); + while ( fscanf( pFile, "%s", Buffer ) == 1 ) + { + pObj = Abc_NtkFindCi( pNtk, Buffer ); + if ( pObj == NULL || !Abc_ObjIsCi(pObj) ) + { + printf( "Name \"%s\" is not a PI name. Cannot use this order.\n", Buffer ); + Vec_PtrFree( vSupp ); + fclose( pFile ); + return; + } + Vec_PtrPush( vSupp, pObj ); + } + fclose( pFile ); + if ( Vec_PtrSize(vSupp) != Abc_NtkCiNum(pNtk) ) + { + printf( "The number of names in the order (%d) is not the same as the number of PIs (%d).\n", Vec_PtrSize(vSupp), Abc_NtkCiNum(pNtk) ); + Vec_PtrFree( vSupp ); + return; + } + Abc_NtkChangeCiOrder( pNtk, vSupp, fReverse ); + Vec_PtrFree( vSupp ); +} + +/**Function************************************************************* + + Synopsis [Changes the order of primary inputs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkChangeCiOrder( Abc_Ntk_t * pNtk, Vec_Ptr_t * vSupp, int fReverse ) +{ + Abc_Obj_t * pObj; + int i; + assert( Vec_PtrSize(vSupp) == Abc_NtkCiNum(pNtk) ); + // order CIs using the array + if ( fReverse ) + Vec_PtrForEachEntry( vSupp, pObj, i ) + Vec_PtrWriteEntry( pNtk->vCis, Vec_PtrSize(vSupp)-1-i, pObj ); + else + Vec_PtrForEachEntry( vSupp, pObj, i ) + Vec_PtrWriteEntry( pNtk->vCis, i, pObj ); + // order PIs accordingly + Vec_PtrClear( pNtk->vPis ); + Abc_NtkForEachCi( pNtk, pObj, i ) + if ( Abc_ObjIsPi(pObj) ) + Vec_PtrPush( pNtk->vPis, pObj ); +// Abc_NtkForEachCi( pNtk, pObj, i ) +// printf( "%s ", Abc_ObjName(pObj) ); +// printf( "\n" ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcPart.c b/abc_with_bb_support/src/base/abci/abcPart.c new file mode 100644 index 000000000..75c13f3bb --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcPart.c @@ -0,0 +1,891 @@ +/**CFile**************************************************************** + + FileName [abcPart.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Output partitioning package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcPart.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Prepare supports.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkPartitionCollectSupps( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vSupp, * vSupports; + Vec_Int_t * vSuppI; + Abc_Obj_t * pObj, * pTemp; + int i, k; + // set the PI numbers + Abc_NtkForEachCi( pNtk, pObj, i ) + pObj->pCopy = (void *)i; + // save hte CI numbers + vSupports = Vec_PtrAlloc( Abc_NtkCoNum(pNtk) ); + Abc_NtkForEachCo( pNtk, pObj, i ) + { + vSupp = Abc_NtkNodeSupport( pNtk, &pObj, 1 ); + vSuppI = (Vec_Int_t *)vSupp; + Vec_PtrForEachEntry( vSupp, pTemp, k ) + Vec_IntWriteEntry( vSuppI, k, (int)pTemp->pCopy ); + Vec_IntSort( vSuppI, 0 ); + // append the number of this output + Vec_IntPush( vSuppI, i ); + // save the support in the vector + Vec_PtrPush( vSupports, vSuppI ); + } + // sort supports by size + Vec_VecSort( (Vec_Vec_t *)vSupports, 1 ); + return vSupports; +} + +/**Function************************************************************* + + Synopsis [Start char-bases support representation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_NtkSuppCharStart( Vec_Int_t * vOne, int nPis ) +{ + char * pBuffer; + int i, Entry; + pBuffer = ALLOC( char, nPis ); + memset( pBuffer, 0, sizeof(char) * nPis ); + Vec_IntForEachEntry( vOne, Entry, i ) + { + assert( Entry < nPis ); + pBuffer[Entry] = 1; + } + return pBuffer; +} + +/**Function************************************************************* + + Synopsis [Add to char-bases support representation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkSuppCharAdd( char * pBuffer, Vec_Int_t * vOne, int nPis ) +{ + int i, Entry; + Vec_IntForEachEntry( vOne, Entry, i ) + { + assert( Entry < nPis ); + pBuffer[Entry] = 1; + } +} + +/**Function************************************************************* + + Synopsis [Find the common variables using char-bases support representation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkSuppCharCommon( char * pBuffer, Vec_Int_t * vOne ) +{ + int i, Entry, nCommon = 0; + Vec_IntForEachEntry( vOne, Entry, i ) + nCommon += pBuffer[Entry]; + return nCommon; +} + +/**Function************************************************************* + + Synopsis [Find the best partition.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkPartitionSmartFindPart( Vec_Ptr_t * vPartSuppsAll, Vec_Ptr_t * vPartsAll, Vec_Ptr_t * vPartSuppsChar, int nPartSizeLimit, Vec_Int_t * vOne ) +{ +/* + Vec_Int_t * vPartSupp, * vPart; + double Attract, Repulse, Cost, CostBest; + int i, nCommon, iBest; + iBest = -1; + CostBest = 0.0; + Vec_PtrForEachEntry( vPartSuppsAll, vPartSupp, i ) + { + vPart = Vec_PtrEntry( vPartsAll, i ); + if ( nPartSizeLimit > 0 && Vec_IntSize(vPart) >= nPartSizeLimit ) + continue; + nCommon = Vec_IntTwoCountCommon( vPartSupp, vOne ); + if ( nCommon == 0 ) + continue; + if ( nCommon == Vec_IntSize(vOne) ) + return i; + Attract = 1.0 * nCommon / Vec_IntSize(vOne); + if ( Vec_IntSize(vPartSupp) < 100 ) + Repulse = 1.0; + else + Repulse = log10( Vec_IntSize(vPartSupp) / 10.0 ); + Cost = pow( Attract, pow(Repulse, 5.0) ); + if ( CostBest < Cost ) + { + CostBest = Cost; + iBest = i; + } + } + if ( CostBest < 0.6 ) + return -1; + return iBest; +*/ + + Vec_Int_t * vPartSupp, * vPart; + int Attract, Repulse, Value, ValueBest; + int i, nCommon, iBest; +// int nCommon2; + iBest = -1; + ValueBest = 0; + Vec_PtrForEachEntry( vPartSuppsAll, vPartSupp, i ) + { + vPart = Vec_PtrEntry( vPartsAll, i ); + if ( nPartSizeLimit > 0 && Vec_IntSize(vPart) >= nPartSizeLimit ) + continue; +// nCommon2 = Vec_IntTwoCountCommon( vPartSupp, vOne ); + nCommon = Abc_NtkSuppCharCommon( Vec_PtrEntry(vPartSuppsChar, i), vOne ); +// assert( nCommon2 == nCommon ); + if ( nCommon == 0 ) + continue; + if ( nCommon == Vec_IntSize(vOne) ) + return i; + Attract = 1000 * nCommon / Vec_IntSize(vOne); + if ( Vec_IntSize(vPartSupp) < 100 ) + Repulse = 1; + else + Repulse = 1+Extra_Base2Log(Vec_IntSize(vPartSupp)-100); + Value = Attract/Repulse; + if ( ValueBest < Value ) + { + ValueBest = Value; + iBest = i; + } + } + if ( ValueBest < 75 ) + return -1; + return iBest; +} + +/**Function************************************************************* + + Synopsis [Perform the smart partitioning.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPartitionPrint( Abc_Ntk_t * pNtk, Vec_Ptr_t * vPartsAll, Vec_Ptr_t * vPartSuppsAll ) +{ + Vec_Int_t * vOne; + int i, nOutputs, Counter; + + Counter = 0; + Vec_PtrForEachEntry( vPartSuppsAll, vOne, i ) + { + nOutputs = Vec_IntSize(Vec_PtrEntry(vPartsAll, i)); + printf( "%d=(%d,%d) ", i, Vec_IntSize(vOne), nOutputs ); + Counter += nOutputs; + if ( i == Vec_PtrSize(vPartsAll) - 1 ) + break; + } + assert( Counter == Abc_NtkCoNum(pNtk) ); + printf( "\nTotal = %d. Outputs = %d.\n", Counter, Abc_NtkCoNum(pNtk) ); +} + +/**Function************************************************************* + + Synopsis [Perform the smart partitioning.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPartitionCompact( Vec_Ptr_t * vPartsAll, Vec_Ptr_t * vPartSuppsAll, int nPartSizeLimit ) +{ + Vec_Int_t * vOne, * vPart, * vPartSupp, * vTemp; + int i, iPart; + + if ( nPartSizeLimit == 0 ) + nPartSizeLimit = 200; + + // pack smaller partitions into larger blocks + iPart = 0; + vPart = vPartSupp = NULL; + Vec_PtrForEachEntry( vPartSuppsAll, vOne, i ) + { + if ( Vec_IntSize(vOne) < nPartSizeLimit ) + { + if ( vPartSupp == NULL ) + { + assert( vPart == NULL ); + vPartSupp = Vec_IntDup(vOne); + vPart = Vec_PtrEntry(vPartsAll, i); + } + else + { + vPartSupp = Vec_IntTwoMerge( vTemp = vPartSupp, vOne ); + Vec_IntFree( vTemp ); + vPart = Vec_IntTwoMerge( vTemp = vPart, Vec_PtrEntry(vPartsAll, i) ); + Vec_IntFree( vTemp ); + Vec_IntFree( Vec_PtrEntry(vPartsAll, i) ); + } + if ( Vec_IntSize(vPartSupp) < nPartSizeLimit ) + continue; + } + else + vPart = Vec_PtrEntry(vPartsAll, i); + // add the partition + Vec_PtrWriteEntry( vPartsAll, iPart, vPart ); + vPart = NULL; + if ( vPartSupp ) + { + Vec_IntFree( Vec_PtrEntry(vPartSuppsAll, iPart) ); + Vec_PtrWriteEntry( vPartSuppsAll, iPart, vPartSupp ); + vPartSupp = NULL; + } + iPart++; + } + // add the last one + if ( vPart ) + { + Vec_PtrWriteEntry( vPartsAll, iPart, vPart ); + vPart = NULL; + + assert( vPartSupp != NULL ); + Vec_IntFree( Vec_PtrEntry(vPartSuppsAll, iPart) ); + Vec_PtrWriteEntry( vPartSuppsAll, iPart, vPartSupp ); + vPartSupp = NULL; + iPart++; + } + Vec_PtrShrink( vPartsAll, iPart ); + Vec_PtrShrink( vPartsAll, iPart ); +} + +/**Function************************************************************* + + Synopsis [Perform the smart partitioning.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Vec_t * Abc_NtkPartitionSmart( Abc_Ntk_t * pNtk, int nPartSizeLimit, int fVerbose ) +{ + Vec_Ptr_t * vPartSuppsChar; + Vec_Ptr_t * vSupps, * vPartsAll, * vPartsAll2, * vPartSuppsAll, * vPartPtr; + Vec_Int_t * vOne, * vPart, * vPartSupp, * vTemp; + int i, iPart, iOut, clk, clk2, timeFind = 0; + + // compute the supports for all outputs +clk = clock(); + vSupps = Abc_NtkPartitionCollectSupps( pNtk ); +if ( fVerbose ) +{ +PRT( "Supps", clock() - clk ); +} + // start char-based support representation + vPartSuppsChar = Vec_PtrAlloc( 1000 ); + + // create partitions +clk = clock(); + vPartsAll = Vec_PtrAlloc( 256 ); + vPartSuppsAll = Vec_PtrAlloc( 256 ); + Vec_PtrForEachEntry( vSupps, vOne, i ) + { + // get the output number + iOut = Vec_IntPop(vOne); + // find closely matching part +clk2 = clock(); + iPart = Abc_NtkPartitionSmartFindPart( vPartSuppsAll, vPartsAll, vPartSuppsChar, nPartSizeLimit, vOne ); +timeFind += clock() - clk2; +//printf( "%4d->%4d ", i, iPart ); + if ( iPart == -1 ) + { + // create new partition + vPart = Vec_IntAlloc( 32 ); + Vec_IntPush( vPart, iOut ); + // create new partition support + vPartSupp = Vec_IntDup( vOne ); + // add this partition and its support + Vec_PtrPush( vPartsAll, vPart ); + Vec_PtrPush( vPartSuppsAll, vPartSupp ); + + Vec_PtrPush( vPartSuppsChar, Abc_NtkSuppCharStart(vOne, Abc_NtkPiNum(pNtk)) ); + } + else + { + // add output to this partition + vPart = Vec_PtrEntry( vPartsAll, iPart ); + Vec_IntPush( vPart, iOut ); + // merge supports + vPartSupp = Vec_PtrEntry( vPartSuppsAll, iPart ); + vPartSupp = Vec_IntTwoMerge( vTemp = vPartSupp, vOne ); + Vec_IntFree( vTemp ); + // reinsert new support + Vec_PtrWriteEntry( vPartSuppsAll, iPart, vPartSupp ); + + Abc_NtkSuppCharAdd( Vec_PtrEntry(vPartSuppsChar, iPart), vOne, Abc_NtkPiNum(pNtk) ); + } + } + + // stop char-based support representation + Vec_PtrForEachEntry( vPartSuppsChar, vTemp, i ) + free( vTemp ); + Vec_PtrFree( vPartSuppsChar ); + +//printf( "\n" ); +if ( fVerbose ) +{ +PRT( "Parts", clock() - clk ); +//PRT( "Find ", timeFind ); +} + +clk = clock(); + // remember number of supports + Vec_PtrForEachEntry( vPartSuppsAll, vOne, i ) + Vec_IntPush( vOne, i ); + // sort the supports in the decreasing order + Vec_VecSort( (Vec_Vec_t *)vPartSuppsAll, 1 ); + // reproduce partitions + vPartsAll2 = Vec_PtrAlloc( 256 ); + Vec_PtrForEachEntry( vPartSuppsAll, vOne, i ) + Vec_PtrPush( vPartsAll2, Vec_PtrEntry(vPartsAll, Vec_IntPop(vOne)) ); + Vec_PtrFree( vPartsAll ); + vPartsAll = vPartsAll2; + + // compact small partitions +// Abc_NtkPartitionPrint( pNtk, vPartsAll, vPartSuppsAll ); + Abc_NtkPartitionCompact( vPartsAll, vPartSuppsAll, nPartSizeLimit ); + if ( fVerbose ) +// Abc_NtkPartitionPrint( pNtk, vPartsAll, vPartSuppsAll ); + printf( "Created %d partitions.\n", Vec_PtrSize(vPartsAll) ); + +if ( fVerbose ) +{ +PRT( "Comps", clock() - clk ); +} + + // cleanup + Vec_VecFree( (Vec_Vec_t *)vSupps ); + Vec_VecFree( (Vec_Vec_t *)vPartSuppsAll ); + + // converts from intergers to nodes + Vec_PtrForEachEntry( vPartsAll, vPart, iPart ) + { + vPartPtr = Vec_PtrAlloc( Vec_IntSize(vPart) ); + Vec_IntForEachEntry( vPart, iOut, i ) + Vec_PtrPush( vPartPtr, Abc_NtkCo(pNtk, iOut) ); + Vec_IntFree( vPart ); + Vec_PtrWriteEntry( vPartsAll, iPart, vPartPtr ); + } + return (Vec_Vec_t *)vPartsAll; +} + +/**Function************************************************************* + + Synopsis [Perform the naive partitioning.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Vec_t * Abc_NtkPartitionNaive( Abc_Ntk_t * pNtk, int nPartSize ) +{ + Vec_Vec_t * vParts; + Abc_Obj_t * pObj; + int nParts, i; + nParts = (Abc_NtkCoNum(pNtk) / nPartSize) + ((Abc_NtkCoNum(pNtk) % nPartSize) > 0); + vParts = Vec_VecStart( nParts ); + Abc_NtkForEachCo( pNtk, pObj, i ) + Vec_VecPush( vParts, i / nPartSize, pObj ); + return vParts; +} + +/**Function************************************************************* + + Synopsis [Returns representative of the given node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkPartStitchFindRepr_rec( Vec_Ptr_t * vEquiv, Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pRepr; + pRepr = Vec_PtrEntry( vEquiv, pObj->Id ); + if ( pRepr == NULL || pRepr == pObj ) + return pObj; + return Abc_NtkPartStitchFindRepr_rec( vEquiv, pRepr ); +} + +/**Function************************************************************* + + Synopsis [Returns the representative of the fanin.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Abc_Obj_t * Abc_NtkPartStitchCopy0( Vec_Ptr_t * vEquiv, Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFan = Abc_ObjFanin0( pObj ); + Abc_Obj_t * pRepr = Abc_NtkPartStitchFindRepr_rec( vEquiv, pFan ); + return Abc_ObjNotCond( pRepr->pCopy, pRepr->fPhase ^ pFan->fPhase ^ Abc_ObjFaninC1(pObj) ); +} +static inline Abc_Obj_t * Abc_NtkPartStitchCopy1( Vec_Ptr_t * vEquiv, Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFan = Abc_ObjFanin1( pObj ); + Abc_Obj_t * pRepr = Abc_NtkPartStitchFindRepr_rec( vEquiv, pFan ); + return Abc_ObjNotCond( pRepr->pCopy, pRepr->fPhase ^ pFan->fPhase ^ Abc_ObjFaninC1(pObj) ); +} + +/**Function************************************************************* + + Synopsis [Stitches together several networks with choice nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkPartStitchChoices_old( Abc_Ntk_t * pNtk, Vec_Ptr_t * vParts ) +{ + Vec_Ptr_t * vNodes, * vEquiv; + Abc_Ntk_t * pNtkNew, * pNtkNew2, * pNtkTemp; + Abc_Obj_t * pObj, * pFanin, * pRepr0, * pRepr1, * pRepr; + int i, k, iNodeId; + + // start a new network similar to the original one + assert( Abc_NtkIsStrash(pNtk) ); + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_STRASH, ABC_FUNC_AIG ); + // duplicate the name and the spec + pNtkNew->pName = Extra_UtilStrsav(pNtk->pName); + pNtkNew->pSpec = Extra_UtilStrsav(pNtk->pSpec); + + // annotate parts to point to the new network + vEquiv = Vec_PtrStart( Abc_NtkObjNumMax(pNtk) + 1 ); + Vec_PtrForEachEntry( vParts, pNtkTemp, i ) + { + assert( Abc_NtkIsStrash(pNtkTemp) ); + Abc_NtkCleanCopy( pNtkTemp ); + + // map the CI nodes + Abc_AigConst1(pNtkTemp)->pCopy = Abc_AigConst1(pNtkNew); + Abc_NtkForEachCi( pNtkTemp, pObj, k ) + { + iNodeId = Nm_ManFindIdByNameTwoTypes( pNtkNew->pManName, Abc_ObjName(pObj), ABC_OBJ_PI, ABC_OBJ_BO ); + if ( iNodeId == -1 ) + { + printf( "Cannot find CI node %s in the original network.\n", Abc_ObjName(pObj) ); + return NULL; + } + pObj->pCopy = Abc_NtkObj( pNtkNew, iNodeId ); + } + + // add the internal nodes while saving representatives + vNodes = Abc_AigDfs( pNtkTemp, 1, 0 ); + Vec_PtrForEachEntry( vNodes, pObj, k ) + { + pObj->pCopy = Abc_AigAnd( pNtkNew->pManFunc, Abc_ObjChild0Copy(pObj), Abc_ObjChild1Copy(pObj) ); + assert( !Abc_ObjIsComplement(pObj->pCopy) ); + if ( !Abc_AigNodeIsChoice(pObj) ) + continue; + // find the earliest representative of the choice node + pRepr0 = NULL; + for ( pFanin = pObj; pFanin; pFanin = pFanin->pData ) + { + pRepr1 = Abc_NtkPartStitchFindRepr_rec( vEquiv, pFanin->pCopy ); + if ( pRepr0 == NULL || pRepr0->Id > pRepr1->Id ) + pRepr0 = pRepr1; + } + // set this representative for the representives of all choices + for ( pFanin = pObj; pFanin; pFanin = pFanin->pData ) + { + pRepr1 = Abc_NtkPartStitchFindRepr_rec( vEquiv, pFanin->pCopy ); + Vec_PtrWriteEntry( vEquiv, pRepr1->Id, pRepr0 ); + } + } + Vec_PtrFree( vNodes ); + + // map the CO nodes + Abc_NtkForEachCo( pNtkTemp, pObj, k ) + { + iNodeId = Nm_ManFindIdByNameTwoTypes( pNtkNew->pManName, Abc_ObjName(pObj), ABC_OBJ_PO, ABC_OBJ_BI ); + if ( iNodeId == -1 ) + { + printf( "Cannot find CO node %s in the original network.\n", Abc_ObjName(pObj) ); + return NULL; + } + pObj->pCopy = Abc_NtkObj( pNtkNew, iNodeId ); + Abc_ObjAddFanin( pObj->pCopy, Abc_ObjChild0Copy(pObj) ); + } + } + + // reconstruct the AIG + pNtkNew2 = Abc_NtkStartFrom( pNtkNew, ABC_NTK_STRASH, ABC_FUNC_AIG ); + // duplicate the name and the spec + pNtkNew2->pName = Extra_UtilStrsav(pNtkNew->pName); + pNtkNew2->pSpec = Extra_UtilStrsav(pNtkNew->pSpec); + // duplicate internal nodes + Abc_AigForEachAnd( pNtkNew, pObj, i ) + { + pRepr0 = Abc_NtkPartStitchCopy0( vEquiv, pObj ); + pRepr1 = Abc_NtkPartStitchCopy1( vEquiv, pObj ); + pObj->pCopy = Abc_AigAnd( pNtkNew2->pManFunc, pRepr0, pRepr1 ); + assert( !Abc_ObjIsComplement(pObj->pCopy) ); + // add the choice if applicable + pRepr = Abc_NtkPartStitchFindRepr_rec( vEquiv, pObj ); + if ( pObj != pRepr ) + { + assert( pObj->Id > pRepr->Id ); + if ( pObj->pCopy != pRepr->pCopy ) + { + assert( pObj->pCopy->Id > pRepr->pCopy->Id ); + pObj->pCopy->pData = pRepr->pCopy->pData; + pRepr->pCopy->pData = pObj->pCopy; + } + } + } + // connect the COs + Abc_NtkForEachCo( pNtkNew, pObj, k ) + Abc_ObjAddFanin( pObj->pCopy, Abc_NtkPartStitchCopy0(vEquiv,pObj) ); + + // replace the network + Abc_NtkDelete( pNtkNew ); + pNtkNew = pNtkNew2; + + // check correctness of the new network + Vec_PtrFree( vEquiv ); + if ( !Abc_NtkCheck( pNtkNew ) ) + { + printf( "Abc_NtkPartStitchChoices: The network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Hop_Obj_t * Hop_ObjChild0Next( Abc_Obj_t * pObj ) { return Hop_NotCond( (Hop_Obj_t *)Abc_ObjFanin0(pObj)->pNext, Abc_ObjFaninC0(pObj) ); } +static inline Hop_Obj_t * Hop_ObjChild1Next( Abc_Obj_t * pObj ) { return Hop_NotCond( (Hop_Obj_t *)Abc_ObjFanin1(pObj)->pNext, Abc_ObjFaninC1(pObj) ); } + + +/**Function************************************************************* + + Synopsis [Stitches together several networks with choice nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Man_t * Abc_NtkPartStartHop( Abc_Ntk_t * pNtk ) +{ + Hop_Man_t * pMan; + Abc_Obj_t * pObj; + int i; + // start the HOP package + pMan = Hop_ManStart(); + pMan->vObjs = Vec_PtrAlloc( Abc_NtkObjNumMax(pNtk) + 1 ); + Vec_PtrPush( pMan->vObjs, Hop_ManConst1(pMan) ); + // map constant node and PIs + Abc_AigConst1(pNtk)->pNext = (Abc_Obj_t *)Hop_ManConst1(pMan); + Abc_NtkForEachCi( pNtk, pObj, i ) + pObj->pNext = (Abc_Obj_t *)Hop_ObjCreatePi(pMan); + // map the internal nodes + Abc_AigForEachAnd( pNtk, pObj, i ) + { + pObj->pNext = (Abc_Obj_t *)Hop_And( pMan, Hop_ObjChild0Next(pObj), Hop_ObjChild1Next(pObj) ); + assert( !Abc_ObjIsComplement(pObj->pNext) ); + } + // set the choice nodes + Abc_AigForEachAnd( pNtk, pObj, i ) + { + if ( pObj->pCopy ) + ((Hop_Obj_t *)pObj->pNext)->pData = pObj->pCopy->pNext; + } + // transfer the POs + Abc_NtkForEachCo( pNtk, pObj, i ) + Hop_ObjCreatePo( pMan, Hop_ObjChild0Next(pObj) ); + // check the new manager + if ( !Hop_ManCheck(pMan) ) + printf( "Abc_NtkPartStartHop: HOP manager check has failed.\n" ); + return pMan; +} + +/**Function************************************************************* + + Synopsis [Stitches together several networks with choice nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkPartStitchChoices( Abc_Ntk_t * pNtk, Vec_Ptr_t * vParts ) +{ + extern Abc_Ntk_t * Abc_NtkHopRemoveLoops( Abc_Ntk_t * pNtk, Hop_Man_t * pMan ); + + Hop_Man_t * pMan; + Vec_Ptr_t * vNodes; + Abc_Ntk_t * pNtkNew, * pNtkTemp; + Abc_Obj_t * pObj, * pFanin; + int i, k, iNodeId; + + // start a new network similar to the original one + assert( Abc_NtkIsStrash(pNtk) ); + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_STRASH, ABC_FUNC_AIG ); + + // annotate parts to point to the new network + Vec_PtrForEachEntry( vParts, pNtkTemp, i ) + { + assert( Abc_NtkIsStrash(pNtkTemp) ); + Abc_NtkCleanCopy( pNtkTemp ); + + // map the CI nodes + Abc_AigConst1(pNtkTemp)->pCopy = Abc_AigConst1(pNtkNew); + Abc_NtkForEachCi( pNtkTemp, pObj, k ) + { + iNodeId = Nm_ManFindIdByNameTwoTypes( pNtkNew->pManName, Abc_ObjName(pObj), ABC_OBJ_PI, ABC_OBJ_BO ); + if ( iNodeId == -1 ) + { + printf( "Cannot find CI node %s in the original network.\n", Abc_ObjName(pObj) ); + return NULL; + } + pObj->pCopy = Abc_NtkObj( pNtkNew, iNodeId ); + } + + // add the internal nodes while saving representatives + vNodes = Abc_AigDfs( pNtkTemp, 1, 0 ); + Vec_PtrForEachEntry( vNodes, pObj, k ) + { + pObj->pCopy = Abc_AigAnd( pNtkNew->pManFunc, Abc_ObjChild0Copy(pObj), Abc_ObjChild1Copy(pObj) ); + assert( !Abc_ObjIsComplement(pObj->pCopy) ); + if ( Abc_AigNodeIsChoice(pObj) ) + for ( pFanin = pObj->pData; pFanin; pFanin = pFanin->pData ) + pFanin->pCopy->pCopy = pObj->pCopy; + } + Vec_PtrFree( vNodes ); + + // map the CO nodes + Abc_NtkForEachCo( pNtkTemp, pObj, k ) + { + iNodeId = Nm_ManFindIdByNameTwoTypes( pNtkNew->pManName, Abc_ObjName(pObj), ABC_OBJ_PO, ABC_OBJ_BI ); + if ( iNodeId == -1 ) + { + printf( "Cannot find CO node %s in the original network.\n", Abc_ObjName(pObj) ); + return NULL; + } + pObj->pCopy = Abc_NtkObj( pNtkNew, iNodeId ); + Abc_ObjAddFanin( pObj->pCopy, Abc_ObjChild0Copy(pObj) ); + } + } + + // transform into the HOP manager + pMan = Abc_NtkPartStartHop( pNtkNew ); + pNtkNew = Abc_NtkHopRemoveLoops( pNtkTemp = pNtkNew, pMan ); + Abc_NtkDelete( pNtkTemp ); + + // check correctness of the new network + if ( !Abc_NtkCheck( pNtkNew ) ) + { + printf( "Abc_NtkPartStitchChoices: The network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Stitches together several networks with choice nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkFraigPartitioned( Abc_Ntk_t * pNtk, void * pParams ) +{ + extern int Cmd_CommandExecute( void * pAbc, char * sCommand ); + extern void * Abc_FrameGetGlobalFrame(); + + Vec_Vec_t * vParts; + Vec_Ptr_t * vFraigs, * vOne; + Abc_Ntk_t * pNtkAig, * pNtkFraig; + int i; + + // perform partitioning + assert( Abc_NtkIsStrash(pNtk) ); +// vParts = Abc_NtkPartitionNaive( pNtk, 20 ); + vParts = Abc_NtkPartitionSmart( pNtk, 0, 1 ); + + Cmd_CommandExecute( Abc_FrameGetGlobalFrame(), "unset progressbar" ); + + // fraig each partition + vFraigs = Vec_PtrAlloc( Vec_VecSize(vParts) ); + Vec_VecForEachLevel( vParts, vOne, i ) + { + pNtkAig = Abc_NtkCreateConeArray( pNtk, vOne, 0 ); + pNtkFraig = Abc_NtkFraig( pNtkAig, pParams, 0, 0 ); + Vec_PtrPush( vFraigs, pNtkFraig ); + Abc_NtkDelete( pNtkAig ); + + printf( "Finished part %d (out of %d)\r", i+1, Vec_VecSize(vParts) ); + } + Vec_VecFree( vParts ); + + Cmd_CommandExecute( Abc_FrameGetGlobalFrame(), "set progressbar" ); + + // derive the final network + pNtkFraig = Abc_NtkPartStitchChoices( pNtk, vFraigs ); + Vec_PtrForEachEntry( vFraigs, pNtkAig, i ) + { +// Abc_NtkPrintStats( stdout, pNtkAig, 0 ); + Abc_NtkDelete( pNtkAig ); + } + Vec_PtrFree( vFraigs ); + + return pNtkFraig; +} + +/**Function************************************************************* + + Synopsis [Stitches together several networks with choice nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkFraigPartitionedTime( Abc_Ntk_t * pNtk, void * pParams ) +{ + extern int Cmd_CommandExecute( void * pAbc, char * sCommand ); + extern void * Abc_FrameGetGlobalFrame(); + + Vec_Vec_t * vParts; + Vec_Ptr_t * vFraigs, * vOne; + Abc_Ntk_t * pNtkAig, * pNtkFraig; + int i; + int clk = clock(); + + // perform partitioning + assert( Abc_NtkIsStrash(pNtk) ); +// vParts = Abc_NtkPartitionNaive( pNtk, 20 ); + vParts = Abc_NtkPartitionSmart( pNtk, 0, 0 ); + + Cmd_CommandExecute( Abc_FrameGetGlobalFrame(), "unset progressbar" ); + + // fraig each partition + vFraigs = Vec_PtrAlloc( Vec_VecSize(vParts) ); + Vec_VecForEachLevel( vParts, vOne, i ) + { + pNtkAig = Abc_NtkCreateConeArray( pNtk, vOne, 0 ); + pNtkFraig = Abc_NtkFraig( pNtkAig, pParams, 0, 0 ); + Vec_PtrPush( vFraigs, pNtkFraig ); + Abc_NtkDelete( pNtkAig ); + + printf( "Finished part %d (out of %d)\r", i+1, Vec_VecSize(vParts) ); + } + Vec_VecFree( vParts ); + + Cmd_CommandExecute( Abc_FrameGetGlobalFrame(), "set progressbar" ); + + // derive the final network + Vec_PtrForEachEntry( vFraigs, pNtkAig, i ) + Abc_NtkDelete( pNtkAig ); + Vec_PtrFree( vFraigs ); + + PRT( "Partitioned fraiging time", clock() - clk ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcPlace.c b/abc_with_bb_support/src/base/abci/abcPlace.c new file mode 100644 index 000000000..9f339c967 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcPlace.c @@ -0,0 +1,255 @@ +/**CFile**************************************************************** + + FileName [abcPlace.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Interface with a placer.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcPlace.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +// placement includes +#include "place_base.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +AbstractCell *abstractCells = NULL; +ConcreteCell *cells = NULL; +ConcreteNet *nets = NULL; +int nAllocSize = 0; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Creates a new cell.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Abc_PlaceCreateCell( Abc_Obj_t * pObj, int fAnd ) +{ + assert( cells[pObj->Id].m_id == 0 ); + + cells[pObj->Id].m_id = pObj->Id; + cells[pObj->Id].m_label = ""; + cells[pObj->Id].m_parent = &(abstractCells[fAnd]); + cells[pObj->Id].m_fixed = 0; + addConcreteCell(&(cells[pObj->Id])); +} + +/**Function************************************************************* + + Synopsis [Updates the net.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Abc_PlaceUpdateNet( Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanout; + int k; + // free the old array of net terminals + if ( nets[pObj->Id].m_terms ) + free( nets[pObj->Id].m_terms ); + // fill in the net with the new information + nets[pObj->Id].m_id = pObj->Id; + nets[pObj->Id].m_weight = 1.0; + nets[pObj->Id].m_numTerms = Abc_ObjFanoutNum(pObj); //fanout + nets[pObj->Id].m_terms = ALLOC(ConcreteCell*, Abc_ObjFanoutNum(pObj)); + Abc_ObjForEachFanout( pObj, pFanout, k ) + nets[pObj->Id].m_terms[k] = &(cells[pFanout->Id]); + addConcreteNet(&(nets[pObj->Id])); +} + +/**Function************************************************************* + + Synopsis [Returns the placement cost of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Abc_PlaceEvaluateCut( Abc_Obj_t * pRoot, Vec_Ptr_t * vFanins ) +{ + Abc_Obj_t * pObj; +// double x, y; + int i; + Vec_PtrForEachEntry( vFanins, pObj, i ) + { +// pObj->Id + } + return 0.0; +} + +/**Function************************************************************* + + Synopsis [Updates placement after one step of rewriting.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_PlaceUpdate( Vec_Ptr_t * vAddedCells, Vec_Ptr_t * vUpdatedNets ) +{ + Abc_Obj_t * pObj, * pFanin; + int i, k; + Vec_Ptr_t * vCells, * vNets; + + // start the arrays of new cells and nets + vCells = Vec_PtrAlloc( 16 ); + vNets = Vec_PtrAlloc( 32 ); + + // go through the new nodes + Vec_PtrForEachEntry( vAddedCells, pObj, i ) + { + assert( !Abc_ObjIsComplement(pObj) ); + Abc_PlaceCreateCell( pObj, 1 ); + Abc_PlaceUpdateNet( pObj ); + + // add the new cell and its fanin nets to temporary storage + Vec_PtrPush( vCells, &(cells[pObj->Id]) ); + Abc_ObjForEachFanin( pObj, pFanin, k ) + Vec_PtrPushUnique( vNets, &(nets[pFanin->Id]) ); + } + + // go through the modified nets + Vec_PtrForEachEntry( vUpdatedNets, pObj, i ) + { + assert( !Abc_ObjIsComplement(pObj) ); + if ( Abc_ObjType(pObj) == ABC_OBJ_NONE ) // dead node + continue; + Abc_PlaceUpdateNet( pObj ); + } + + // update the placement +// fastPlace( Vec_PtrSize(vCells), (ConcreteCell **)Vec_PtrArray(vCells), +// Vec_PtrSize(vNets), (ConcreteNet **)Vec_PtrArray(vNets) ); + + // clean up + Vec_PtrFree( vCells ); + Vec_PtrFree( vNets ); +} + +/**Function************************************************************* + + Synopsis [This procedure is called before the writing start.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_PlaceBegin( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i; + + // allocate and clean internal storage + nAllocSize = 5 * Abc_NtkObjNumMax(pNtk); + cells = REALLOC(ConcreteCell, cells, nAllocSize); + nets = REALLOC(ConcreteNet, nets, nAllocSize); + memset( cells, 0, sizeof(ConcreteCell) * nAllocSize ); + memset( nets, 0, sizeof(ConcreteNet) * nAllocSize ); + + // create AbstractCells + // 1: pad + // 2: and + if (!abstractCells) + abstractCells = ALLOC(AbstractCell,2); + + abstractCells[0].m_height = 1.0; + abstractCells[0].m_width = 1.0; + abstractCells[0].m_label = "pio"; + abstractCells[0].m_pad = 1; + + abstractCells[1].m_height = 1.0; + abstractCells[1].m_width = 1.0; + abstractCells[1].m_label = "and"; + abstractCells[1].m_pad = 0; + + // input pads + Abc_NtkForEachCi( pNtk, pObj, i ) + Abc_PlaceCreateCell( pObj, 0 ); + + // ouput pads + Abc_NtkForEachCo( pNtk, pObj, i ) + Abc_PlaceCreateCell( pObj, 0 ); + + // AND nodes + Abc_AigForEachAnd( pNtk, pObj, i ) + Abc_PlaceCreateCell( pObj, 1 ); + + // all nets + Abc_NtkForEachObj( pNtk, pObj, i ) + { + if ( !Abc_ObjIsCi(pObj) && !Abc_ObjIsNode(pObj) ) + continue; + Abc_PlaceUpdateNet( pObj ); + } + + globalPreplace((float)0.8); + globalPlace(); +} + +/**Function************************************************************* + + Synopsis [This procedure is called after the writing completes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_PlaceEnd( Abc_Ntk_t * pNtk ) +{ + int i; + + + // clean up + for ( i = 0; i < nAllocSize; i++ ) + FREE( nets[i].m_terms ); + FREE( abstractCells ); + FREE( cells ); + FREE( nets ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcPrint.c b/abc_with_bb_support/src/base/abci/abcPrint.c new file mode 100644 index 000000000..63fb5645a --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcPrint.c @@ -0,0 +1,949 @@ +/**CFile**************************************************************** + + FileName [abcPrint.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Printing statistics.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcPrint.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "dec.h" +#include "main.h" +#include "mio.h" +//#include "seq.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//extern int s_TotalNodes = 0; +//extern int s_TotalChanges = 0; + +int s_MappingTime = 0; +int s_MappingMem = 0; +int s_ResubTime = 0; +int s_ResynTime = 0; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Print the vital stats of the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintStats( FILE * pFile, Abc_Ntk_t * pNtk, int fFactored ) +{ + int Num; + +// if ( Abc_NtkIsStrash(pNtk) ) +// Abc_AigCountNext( pNtk->pManFunc ); + + fprintf( pFile, "%-13s:", pNtk->pName ); + if ( Abc_NtkAssertNum(pNtk) ) + fprintf( pFile, " i/o/a = %4d/%4d/%4d", Abc_NtkPiNum(pNtk), Abc_NtkPoNum(pNtk), Abc_NtkAssertNum(pNtk) ); + else + fprintf( pFile, " i/o = %4d/%4d", Abc_NtkPiNum(pNtk), Abc_NtkPoNum(pNtk) ); + fprintf( pFile, " lat = %4d", Abc_NtkLatchNum(pNtk) ); + if ( Abc_NtkIsNetlist(pNtk) ) + { + fprintf( pFile, " net = %5d", Abc_NtkNetNum(pNtk) ); + fprintf( pFile, " nd = %5d", Abc_NtkNodeNum(pNtk) ); + fprintf( pFile, " wbox = %3d", Abc_NtkWhiteboxNum(pNtk) ); + fprintf( pFile, " bbox = %3d", Abc_NtkBlackboxNum(pNtk) ); + } + else if ( Abc_NtkIsStrash(pNtk) ) + { + fprintf( pFile, " and = %5d", Abc_NtkNodeNum(pNtk) ); + if ( Num = Abc_NtkGetChoiceNum(pNtk) ) + fprintf( pFile, " (choice = %d)", Num ); + if ( Num = Abc_NtkGetExorNum(pNtk) ) + fprintf( pFile, " (exor = %d)", Num ); +// if ( Num2 = Abc_NtkGetMuxNum(pNtk) ) +// fprintf( pFile, " (mux = %d)", Num2-Num ); +// if ( Num2 ) +// fprintf( pFile, " (other = %d)", Abc_NtkNodeNum(pNtk)-3*Num2 ); + } + else + { + fprintf( pFile, " nd = %5d", Abc_NtkNodeNum(pNtk) ); + fprintf( pFile, " net = %5d", Abc_NtkGetTotalFanins(pNtk) ); + } + + if ( Abc_NtkIsStrash(pNtk) || Abc_NtkIsNetlist(pNtk) ) + { + } + else if ( Abc_NtkHasSop(pNtk) ) + { + + fprintf( pFile, " cube = %5d", Abc_NtkGetCubeNum(pNtk) ); +// fprintf( pFile, " lit(sop) = %5d", Abc_NtkGetLitNum(pNtk) ); + if ( fFactored ) + fprintf( pFile, " lit(fac) = %5d", Abc_NtkGetLitFactNum(pNtk) ); + } + else if ( Abc_NtkHasAig(pNtk) ) + fprintf( pFile, " aig = %5d", Abc_NtkGetAigNodeNum(pNtk) ); + else if ( Abc_NtkHasBdd(pNtk) ) + fprintf( pFile, " bdd = %5d", Abc_NtkGetBddNodeNum(pNtk) ); + else if ( Abc_NtkHasMapping(pNtk) ) + { + fprintf( pFile, " area = %5.2f", Abc_NtkGetMappedArea(pNtk) ); + fprintf( pFile, " delay = %5.2f", Abc_NtkDelayTrace(pNtk) ); + } + else if ( !Abc_NtkHasBlackbox(pNtk) ) + { + assert( 0 ); + } + + if ( Abc_NtkIsStrash(pNtk) ) + fprintf( pFile, " lev = %3d", Abc_AigLevel(pNtk) ); + else + fprintf( pFile, " lev = %3d", Abc_NtkLevel(pNtk) ); + + fprintf( pFile, "\n" ); + +// Abc_NtkCrossCut( pNtk ); + + // print the statistic into a file +/* + { + FILE * pTable; + pTable = fopen( "iscas/seqmap__stats.txt", "a+" ); + fprintf( pTable, "%s ", pNtk->pName ); + fprintf( pTable, "%d ", Abc_NtkPiNum(pNtk) ); + fprintf( pTable, "%d ", Abc_NtkPoNum(pNtk) ); + fprintf( pTable, "%d ", Abc_NtkLatchNum(pNtk) ); + fprintf( pTable, "%d ", Abc_NtkNodeNum(pNtk) ); + fprintf( pTable, "%d ", Abc_NtkLevel(pNtk) ); + fprintf( pTable, "\n" ); + fclose( pTable ); + } +*/ +/* + // print the statistic into a file + { + FILE * pTable; + pTable = fopen( "stats.txt", "a+" ); + fprintf( pTable, "%s ", pNtk->pSpec ); + fprintf( pTable, "%.0f ", Abc_NtkGetMappedArea(pNtk) ); + fprintf( pTable, "%.2f ", Abc_NtkDelayTrace(pNtk) ); + fprintf( pTable, "\n" ); + fclose( pTable ); + } +*/ + +/* + // print the statistic into a file + { + FILE * pTable; + pTable = fopen( "x/stats_new.txt", "a+" ); + fprintf( pTable, "%s ", pNtk->pName ); +// fprintf( pTable, "%d ", Abc_NtkPiNum(pNtk) ); +// fprintf( pTable, "%d ", Abc_NtkPoNum(pNtk) ); +// fprintf( pTable, "%d ", Abc_NtkLevel(pNtk) ); +// fprintf( pTable, "%d ", Abc_NtkNodeNum(pNtk) ); +// fprintf( pTable, "%d ", Abc_NtkGetTotalFanins(pNtk) ); +// fprintf( pTable, "%d ", Abc_NtkLatchNum(pNtk) ); +// fprintf( pTable, "%.2f ", (float)(s_MappingMem)/(float)(1<<20) ); + fprintf( pTable, "%.2f", (float)(s_MappingTime)/(float)(CLOCKS_PER_SEC) ); +// fprintf( pTable, "%.2f", (float)(s_ResynTime)/(float)(CLOCKS_PER_SEC) ); + fprintf( pTable, "\n" ); + fclose( pTable ); + + s_ResynTime = 0; + } +*/ + +/* + // print the statistic into a file + { + static int Counter = 0; + extern int timeRetime; + FILE * pTable; + Counter++; + pTable = fopen( "a/ret__stats.txt", "a+" ); + fprintf( pTable, "%s ", pNtk->pName ); + fprintf( pTable, "%d ", Abc_NtkNodeNum(pNtk) ); + fprintf( pTable, "%d ", Abc_NtkLatchNum(pNtk) ); + fprintf( pTable, "%d ", Abc_NtkLevel(pNtk) ); + fprintf( pTable, "%.2f ", (float)(timeRetime)/(float)(CLOCKS_PER_SEC) ); + if ( Counter % 4 == 0 ) + fprintf( pTable, "\n" ); + fclose( pTable ); + } +*/ + +/* + // print the statistic into a file + { + static int Counter = 0; + extern int timeRetime; + FILE * pTable; + Counter++; + pTable = fopen( "d/stats.txt", "a+" ); + fprintf( pTable, "%s ", pNtk->pName ); +// fprintf( pTable, "%d ", Abc_NtkPiNum(pNtk) ); +// fprintf( pTable, "%d ", Abc_NtkPoNum(pNtk) ); +// fprintf( pTable, "%d ", Abc_NtkLatchNum(pNtk) ); + fprintf( pTable, "%d ", Abc_NtkNodeNum(pNtk) ); + fprintf( pTable, "%.2f ", (float)(timeRetime)/(float)(CLOCKS_PER_SEC) ); + fprintf( pTable, "\n" ); + fclose( pTable ); + } +*/ + +/* + s_TotalNodes += Abc_NtkNodeNum(pNtk); + printf( "Total nodes = %6d %6.2f Mb Changes = %6d.\n", + s_TotalNodes, s_TotalNodes * 20.0 / (1<<20), s_TotalChanges ); +*/ +} + +/**Function************************************************************* + + Synopsis [Prints PIs/POs and LIs/LOs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintIo( FILE * pFile, Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i; + + fprintf( pFile, "Primary inputs (%d): ", Abc_NtkPiNum(pNtk) ); + Abc_NtkForEachPi( pNtk, pObj, i ) + fprintf( pFile, " %s", Abc_ObjName(pObj) ); +// fprintf( pFile, " %s(%d)", Abc_ObjName(pObj), Abc_ObjFanoutNum(pObj) ); + fprintf( pFile, "\n" ); + + fprintf( pFile, "Primary outputs (%d):", Abc_NtkPoNum(pNtk) ); + Abc_NtkForEachPo( pNtk, pObj, i ) + fprintf( pFile, " %s", Abc_ObjName(pObj) ); + fprintf( pFile, "\n" ); + + fprintf( pFile, "Latches (%d): ", Abc_NtkLatchNum(pNtk) ); + Abc_NtkForEachLatch( pNtk, pObj, i ) + fprintf( pFile, " %s(%s=%s)", Abc_ObjName(pObj), + Abc_ObjName(Abc_ObjFanout0(pObj)), Abc_ObjName(Abc_ObjFanin0(pObj)) ); + fprintf( pFile, "\n" ); +} + +/**Function************************************************************* + + Synopsis [Prints statistics about latches.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintLatch( FILE * pFile, Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pLatch, * pFanin; + int i, Counter0, Counter1, Counter2; + int InitNums[4], Init; + + assert( !Abc_NtkIsNetlist(pNtk) ); + if ( Abc_NtkLatchNum(pNtk) == 0 ) + { + fprintf( pFile, "The network is combinational.\n" ); + return; + } + + for ( i = 0; i < 4; i++ ) + InitNums[i] = 0; + Counter0 = Counter1 = Counter2 = 0; + Abc_NtkForEachLatch( pNtk, pLatch, i ) + { + Init = Abc_LatchInit( pLatch ); + assert( Init < 4 ); + InitNums[Init]++; + + pFanin = Abc_ObjFanin0(Abc_ObjFanin0(pLatch)); + if ( Abc_NtkIsLogic(pNtk) ) + { + if ( !Abc_NodeIsConst(pFanin) ) + continue; + } + else if ( Abc_NtkIsStrash(pNtk) ) + { + if ( !Abc_AigNodeIsConst(pFanin) ) + continue; + } + else + assert( 0 ); + + // the latch input is a constant node + Counter0++; + if ( Abc_LatchIsInitDc(pLatch) ) + { + Counter1++; + continue; + } + // count the number of cases when the constant is equal to the initial value + if ( Abc_NtkIsStrash(pNtk) ) + { + if ( Abc_LatchIsInit1(pLatch) == !Abc_ObjFaninC0(pLatch) ) + Counter2++; + } + else + { + if ( Abc_LatchIsInit1(pLatch) == Abc_NodeIsConst1(pLatch) ) + Counter2++; + } + } + fprintf( pFile, "%-15s: ", pNtk->pName ); + fprintf( pFile, "Latch = %6d. No = %4d. Zero = %4d. One = %4d. DC = %4d.\n", + Abc_NtkLatchNum(pNtk), InitNums[0], InitNums[1], InitNums[2], InitNums[3] ); + fprintf( pFile, "Const fanin = %3d. DC init = %3d. Matching init = %3d. ", Counter0, Counter1, Counter2 ); + fprintf( pFile, "Self-feed latches = %2d.\n", Abc_NtkCountSelfFeedLatches(pNtk) ); +} + +/**Function************************************************************* + + Synopsis [Prints the distribution of fanins/fanouts in the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintFanio( FILE * pFile, Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i, k, nFanins, nFanouts; + Vec_Int_t * vFanins, * vFanouts; + int nOldSize, nNewSize; + + vFanins = Vec_IntAlloc( 0 ); + vFanouts = Vec_IntAlloc( 0 ); + Vec_IntFill( vFanins, 100, 0 ); + Vec_IntFill( vFanouts, 100, 0 ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + nFanins = Abc_ObjFaninNum(pNode); + if ( Abc_NtkIsNetlist(pNtk) ) + nFanouts = Abc_ObjFanoutNum( Abc_ObjFanout0(pNode) ); + else + nFanouts = Abc_ObjFanoutNum(pNode); +// nFanouts = Abc_NodeMffcSize(pNode); + if ( nFanins > vFanins->nSize || nFanouts > vFanouts->nSize ) + { + nOldSize = vFanins->nSize; + nNewSize = ABC_MAX(nFanins, nFanouts) + 10; + Vec_IntGrow( vFanins, nNewSize ); + Vec_IntGrow( vFanouts, nNewSize ); + for ( k = nOldSize; k < nNewSize; k++ ) + { + Vec_IntPush( vFanins, 0 ); + Vec_IntPush( vFanouts, 0 ); + } + } + vFanins->pArray[nFanins]++; + vFanouts->pArray[nFanouts]++; + } + fprintf( pFile, "The distribution of fanins and fanouts in the network:\n" ); + fprintf( pFile, " Number Nodes with fanin Nodes with fanout\n" ); + for ( k = 0; k < vFanins->nSize; k++ ) + { + if ( vFanins->pArray[k] == 0 && vFanouts->pArray[k] == 0 ) + continue; + fprintf( pFile, "%5d : ", k ); + if ( vFanins->pArray[k] == 0 ) + fprintf( pFile, " " ); + else + fprintf( pFile, "%12d ", vFanins->pArray[k] ); + fprintf( pFile, " " ); + if ( vFanouts->pArray[k] == 0 ) + fprintf( pFile, " " ); + else + fprintf( pFile, "%12d ", vFanouts->pArray[k] ); + fprintf( pFile, "\n" ); + } + Vec_IntFree( vFanins ); + Vec_IntFree( vFanouts ); +} + +/**Function************************************************************* + + Synopsis [Prints the fanins/fanouts of a node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodePrintFanio( FILE * pFile, Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pNode2; + int i; + if ( Abc_ObjIsPo(pNode) ) + pNode = Abc_ObjFanin0(pNode); + + fprintf( pFile, "Node %s", Abc_ObjName(pNode) ); + fprintf( pFile, "\n" ); + + fprintf( pFile, "Fanins (%d): ", Abc_ObjFaninNum(pNode) ); + Abc_ObjForEachFanin( pNode, pNode2, i ) + fprintf( pFile, " %s", Abc_ObjName(pNode2) ); + fprintf( pFile, "\n" ); + + fprintf( pFile, "Fanouts (%d): ", Abc_ObjFaninNum(pNode) ); + Abc_ObjForEachFanout( pNode, pNode2, i ) + fprintf( pFile, " %s", Abc_ObjName(pNode2) ); + fprintf( pFile, "\n" ); +} + +/**Function************************************************************* + + Synopsis [Prints the MFFCs of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintMffc( FILE * pFile, Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i; + extern void Abc_NodeMffsConeSuppPrint( Abc_Obj_t * pNode ); + Abc_NtkForEachNode( pNtk, pNode, i ) + Abc_NodeMffsConeSuppPrint( pNode ); +} + +/**Function************************************************************* + + Synopsis [Prints the factored form of one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintFactor( FILE * pFile, Abc_Ntk_t * pNtk, int fUseRealNames ) +{ + Abc_Obj_t * pNode; + int i; + assert( Abc_NtkIsSopLogic(pNtk) ); + Abc_NtkForEachNode( pNtk, pNode, i ) + Abc_NodePrintFactor( pFile, pNode, fUseRealNames ); +} + +/**Function************************************************************* + + Synopsis [Prints the factored form of one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodePrintFactor( FILE * pFile, Abc_Obj_t * pNode, int fUseRealNames ) +{ + Dec_Graph_t * pGraph; + Vec_Ptr_t * vNamesIn; + if ( Abc_ObjIsCo(pNode) ) + pNode = Abc_ObjFanin0(pNode); + if ( Abc_ObjIsPi(pNode) ) + { + fprintf( pFile, "Skipping the PI node.\n" ); + return; + } + if ( Abc_ObjIsLatch(pNode) ) + { + fprintf( pFile, "Skipping the latch.\n" ); + return; + } + assert( Abc_ObjIsNode(pNode) ); + pGraph = Dec_Factor( pNode->pData ); + if ( fUseRealNames ) + { + vNamesIn = Abc_NodeGetFaninNames(pNode); + Dec_GraphPrint( stdout, pGraph, (char **)vNamesIn->pArray, Abc_ObjName(pNode) ); + Abc_NodeFreeNames( vNamesIn ); + } + else + Dec_GraphPrint( stdout, pGraph, (char **)NULL, Abc_ObjName(pNode) ); + Dec_GraphFree( pGraph ); +} + + +/**Function************************************************************* + + Synopsis [Prints the level stats of the PO node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintLevel( FILE * pFile, Abc_Ntk_t * pNtk, int fProfile, int fListNodes ) +{ + Abc_Obj_t * pNode; + int i, k, Length; + + if ( fListNodes ) + { + int nLevels; + nLevels = Abc_NtkLevel(pNtk); + printf( "Nodes by level:\n" ); + for ( i = 0; i <= nLevels; i++ ) + { + printf( "%2d : ", i ); + Abc_NtkForEachNode( pNtk, pNode, k ) + if ( (int)pNode->Level == i ) + printf( " %s", Abc_ObjName(pNode) ); + printf( "\n" ); + } + return; + } + + // print the delay profile + if ( fProfile && Abc_NtkHasMapping(pNtk) ) + { + int nIntervals = 12; + float DelayMax, DelayCur, DelayDelta; + int * pLevelCounts; + int DelayInt, nOutsSum, nOutsTotal; + + // get the max delay and delta + DelayMax = Abc_NtkDelayTrace( pNtk ); + DelayDelta = DelayMax/nIntervals; + // collect outputs by delay + pLevelCounts = ALLOC( int, nIntervals ); + memset( pLevelCounts, 0, sizeof(int) * nIntervals ); + Abc_NtkForEachCo( pNtk, pNode, i ) + { + DelayCur = Abc_NodeReadArrival( Abc_ObjFanin0(pNode) )->Worst; + DelayInt = (int)(DelayCur / DelayDelta); + if ( DelayInt >= nIntervals ) + DelayInt = nIntervals - 1; + pLevelCounts[DelayInt]++; + } + + nOutsSum = 0; + nOutsTotal = Abc_NtkCoNum(pNtk); + for ( i = 0; i < nIntervals; i++ ) + { + nOutsSum += pLevelCounts[i]; + printf( "[%8.2f - %8.2f] : COs = %4d. %5.1f %%\n", + DelayDelta * i, DelayDelta * (i+1), pLevelCounts[i], 100.0 * nOutsSum/nOutsTotal ); + } + free( pLevelCounts ); + return; + } + else if ( fProfile ) + { + int LevelMax, * pLevelCounts; + int nOutsSum, nOutsTotal; + + if ( !Abc_NtkIsStrash(pNtk) ) + Abc_NtkLevel(pNtk); + + LevelMax = 0; + Abc_NtkForEachCo( pNtk, pNode, i ) + if ( LevelMax < (int)Abc_ObjFanin0(pNode)->Level ) + LevelMax = Abc_ObjFanin0(pNode)->Level; + pLevelCounts = ALLOC( int, LevelMax + 1 ); + memset( pLevelCounts, 0, sizeof(int) * (LevelMax + 1) ); + Abc_NtkForEachCo( pNtk, pNode, i ) + pLevelCounts[Abc_ObjFanin0(pNode)->Level]++; + + nOutsSum = 0; + nOutsTotal = Abc_NtkCoNum(pNtk); + for ( i = 0; i <= LevelMax; i++ ) + if ( pLevelCounts[i] ) + { + nOutsSum += pLevelCounts[i]; + printf( "Level = %4d. COs = %4d. %5.1f %%\n", i, pLevelCounts[i], 100.0 * nOutsSum/nOutsTotal ); + } + free( pLevelCounts ); + return; + } + assert( Abc_NtkIsStrash(pNtk) ); + + // find the longest name + Length = 0; + Abc_NtkForEachCo( pNtk, pNode, i ) + if ( Length < (int)strlen(Abc_ObjName(pNode)) ) + Length = strlen(Abc_ObjName(pNode)); + if ( Length < 5 ) + Length = 5; + // print stats for each output + Abc_NtkForEachCo( pNtk, pNode, i ) + { + fprintf( pFile, "CO %4d : %*s ", i, Length, Abc_ObjName(pNode) ); + Abc_NodePrintLevel( pFile, pNode ); + } +} + +/**Function************************************************************* + + Synopsis [Prints the factored form of one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodePrintLevel( FILE * pFile, Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pDriver; + Vec_Ptr_t * vNodes; + + pDriver = Abc_ObjIsCo(pNode)? Abc_ObjFanin0(pNode) : pNode; + if ( Abc_ObjIsPi(pDriver) ) + { + fprintf( pFile, "Primary input.\n" ); + return; + } + if ( Abc_ObjIsLatch(pDriver) ) + { + fprintf( pFile, "Latch.\n" ); + return; + } + if ( Abc_NodeIsConst(pDriver) ) + { + fprintf( pFile, "Constant %d.\n", !Abc_ObjFaninC0(pNode) ); + return; + } + // print the level + fprintf( pFile, "Level = %3d. ", pDriver->Level ); + // print the size of MFFC + fprintf( pFile, "Mffc = %5d. ", Abc_NodeMffcSize(pDriver) ); + // print the size of the shole cone + vNodes = Abc_NtkDfsNodes( pNode->pNtk, &pDriver, 1 ); + fprintf( pFile, "Cone = %5d. ", Vec_PtrSize(vNodes) ); + Vec_PtrFree( vNodes ); + fprintf( pFile, "\n" ); +} + +/**Function************************************************************* + + Synopsis [Prints the factored form of one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodePrintKMap( Abc_Obj_t * pNode, int fUseRealNames ) +{ + Vec_Ptr_t * vNamesIn; + if ( fUseRealNames ) + { + vNamesIn = Abc_NodeGetFaninNames(pNode); + Extra_PrintKMap( stdout, pNode->pNtk->pManFunc, pNode->pData, Cudd_Not(pNode->pData), + Abc_ObjFaninNum(pNode), NULL, 0, (char **)vNamesIn->pArray ); + Abc_NodeFreeNames( vNamesIn ); + } + else + Extra_PrintKMap( stdout, pNode->pNtk->pManFunc, pNode->pData, Cudd_Not(pNode->pData), + Abc_ObjFaninNum(pNode), NULL, 0, NULL ); + +} + +/**Function************************************************************* + + Synopsis [Prints statistics about gates used in the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintGates( Abc_Ntk_t * pNtk, int fUseLibrary ) +{ + Abc_Obj_t * pObj; + int fHasBdds, i; + int CountConst, CountBuf, CountInv, CountAnd, CountOr, CountOther, CounterTotal; + char * pSop; + + if ( fUseLibrary && Abc_NtkHasMapping(pNtk) ) + { + stmm_table * tTable; + stmm_generator * gen; + char * pName; + int * pCounter, Counter; + double Area, AreaTotal; + + // count the gates by name + CounterTotal = 0; + tTable = stmm_init_table(strcmp, stmm_strhash); + Abc_NtkForEachNode( pNtk, pObj, i ) + { + if ( i == 0 ) continue; + if ( !stmm_find_or_add( tTable, Mio_GateReadName(pObj->pData), (char ***)&pCounter ) ) + *pCounter = 0; + (*pCounter)++; + CounterTotal++; + } + // print the gates + AreaTotal = Abc_NtkGetMappedArea(pNtk); + stmm_foreach_item( tTable, gen, (char **)&pName, (char **)&Counter ) + { + Area = Counter * Mio_GateReadArea(Mio_LibraryReadGateByName(pNtk->pManFunc,pName)); + printf( "%-12s = %8d %10.2f %6.2f %%\n", pName, Counter, Area, 100.0 * Area / AreaTotal ); + } + printf( "%-12s = %8d %10.2f %6.2f %%\n", "TOTAL", CounterTotal, AreaTotal, 100.0 ); + stmm_free_table( tTable ); + return; + } + + if ( Abc_NtkIsAigLogic(pNtk) ) + return; + + // transform logic functions from BDD to SOP + if ( fHasBdds = Abc_NtkIsBddLogic(pNtk) ) + { + if ( !Abc_NtkBddToSop(pNtk, 0) ) + { + printf( "Abc_NtkPrintGates(): Converting to SOPs has failed.\n" ); + return; + } + } + + // get hold of the SOP of the node + CountConst = CountBuf = CountInv = CountAnd = CountOr = CountOther = CounterTotal = 0; + Abc_NtkForEachNode( pNtk, pObj, i ) + { + if ( i == 0 ) continue; + if ( Abc_NtkHasMapping(pNtk) ) + pSop = Mio_GateReadSop(pObj->pData); + else + pSop = pObj->pData; + // collect the stats + if ( Abc_SopIsConst0(pSop) || Abc_SopIsConst1(pSop) ) + CountConst++; + else if ( Abc_SopIsBuf(pSop) ) + CountBuf++; + else if ( Abc_SopIsInv(pSop) ) + CountInv++; + else if ( !Abc_SopIsComplement(pSop) && Abc_SopIsAndType(pSop) || Abc_SopIsComplement(pSop) && Abc_SopIsOrType(pSop) ) + CountAnd++; + else if ( Abc_SopIsComplement(pSop) && Abc_SopIsAndType(pSop) || !Abc_SopIsComplement(pSop) && Abc_SopIsOrType(pSop) ) + CountOr++; + else + CountOther++; + CounterTotal++; + } + printf( "Const = %8d %6.2f %%\n", CountConst , 100.0 * CountConst / CounterTotal ); + printf( "Buffer = %8d %6.2f %%\n", CountBuf , 100.0 * CountBuf / CounterTotal ); + printf( "Inverter = %8d %6.2f %%\n", CountInv , 100.0 * CountInv / CounterTotal ); + printf( "And = %8d %6.2f %%\n", CountAnd , 100.0 * CountAnd / CounterTotal ); + printf( "Or = %8d %6.2f %%\n", CountOr , 100.0 * CountOr / CounterTotal ); + printf( "Other = %8d %6.2f %%\n", CountOther , 100.0 * CountOther / CounterTotal ); + printf( "TOTAL = %8d %6.2f %%\n", CounterTotal, 100.0 * CounterTotal / CounterTotal ); + + // convert the network back into BDDs if this is how it was + if ( fHasBdds ) + Abc_NtkSopToBdd(pNtk); +} + +/**Function************************************************************* + + Synopsis [Prints statistics about gates used in the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintSharing( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vNodes1, * vNodes2; + Abc_Obj_t * pObj1, * pObj2, * pNode1, * pNode2; + int i, k, m, n, Counter; + + // print the template + printf( "Statistics about sharing of logic nodes among the CO pairs.\n" ); + printf( "(CO1,CO2)=NumShared : " ); + // go though the CO pairs + Abc_NtkForEachCo( pNtk, pObj1, i ) + { + vNodes1 = Abc_NtkDfsNodes( pNtk, &pObj1, 1 ); + // mark the nodes + Vec_PtrForEachEntry( vNodes1, pNode1, m ) + pNode1->fMarkA = 1; + // go through the second COs + Abc_NtkForEachCo( pNtk, pObj2, k ) + { + if ( i >= k ) + continue; + vNodes2 = Abc_NtkDfsNodes( pNtk, &pObj2, 1 ); + // count the number of marked + Counter = 0; + Vec_PtrForEachEntry( vNodes2, pNode2, n ) + Counter += pNode2->fMarkA; + // print + printf( "(%d,%d)=%d ", i, k, Counter ); + Vec_PtrFree( vNodes2 ); + } + // unmark the nodes + Vec_PtrForEachEntry( vNodes1, pNode1, m ) + pNode1->fMarkA = 0; + Vec_PtrFree( vNodes1 ); + } + printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [Prints info for each output cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintStrSupports( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vSupp, * vNodes; + Abc_Obj_t * pObj; + int i; + printf( "Structural support info:\n" ); + Abc_NtkForEachCo( pNtk, pObj, i ) + { + vSupp = Abc_NtkNodeSupport( pNtk, &pObj, 1 ); + vNodes = Abc_NtkDfsNodes( pNtk, &pObj, 1 ); + printf( "%20s : Cone = %5d. Supp = %5d.\n", + Abc_ObjName(pObj), vNodes->nSize, vSupp->nSize ); + Vec_PtrFree( vNodes ); + Vec_PtrFree( vSupp ); + } +} + +/**Function************************************************************* + + Synopsis [Prints information about the object.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ObjPrint( FILE * pFile, Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanin; + int i; + fprintf( pFile, "Object %5d : ", pObj->Id ); + switch ( pObj->Type ) + { + case ABC_OBJ_NONE: + fprintf( pFile, "NONE " ); + break; + case ABC_OBJ_CONST1: + fprintf( pFile, "Const1 " ); + break; + case ABC_OBJ_PIO: + fprintf( pFile, "PIO " ); + break; + case ABC_OBJ_PI: + fprintf( pFile, "PI " ); + break; + case ABC_OBJ_PO: + fprintf( pFile, "PO " ); + break; + case ABC_OBJ_BI: + fprintf( pFile, "BI " ); + break; + case ABC_OBJ_BO: + fprintf( pFile, "BO " ); + break; + case ABC_OBJ_ASSERT: + fprintf( pFile, "Assert " ); + break; + case ABC_OBJ_NET: + fprintf( pFile, "Net " ); + break; + case ABC_OBJ_NODE: + fprintf( pFile, "Node " ); + break; + case ABC_OBJ_LATCH: + fprintf( pFile, "Latch " ); + break; + case ABC_OBJ_WHITEBOX: + fprintf( pFile, "Whitebox" ); + break; + case ABC_OBJ_BLACKBOX: + fprintf( pFile, "Blackbox" ); + break; + default: + assert(0); + break; + } + // print the fanins + fprintf( pFile, " Fanins ( " ); + Abc_ObjForEachFanin( pObj, pFanin, i ) + fprintf( pFile, "%d ", pFanin->Id ); + fprintf( pFile, ") " ); +/* + fprintf( pFile, " Fanouts ( " ); + Abc_ObjForEachFanout( pObj, pFanin, i ) + fprintf( pFile, "%d(%c) ", pFanin->Id, Abc_NodeIsTravIdCurrent(pFanin)? '+' : '-' ); + fprintf( pFile, ") " ); +*/ + // print the logic function + if ( Abc_ObjIsNode(pObj) && Abc_NtkIsSopLogic(pObj->pNtk) ) + fprintf( pFile, " %s", pObj->pData ); + else + fprintf( pFile, "\n" ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcProve.c b/abc_with_bb_support/src/base/abci/abcProve.c new file mode 100644 index 000000000..5d47dcd0d --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcProve.c @@ -0,0 +1,343 @@ +/**CFile**************************************************************** + + FileName [abcProve.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Proves the miter using AIG rewriting, FRAIGing, and SAT solving.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcProve.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "fraig.h" +#include "math.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +extern int Abc_NtkRefactor( Abc_Ntk_t * pNtk, int nNodeSizeMax, int nConeSizeMax, bool fUpdateLevel, bool fUseZeros, bool fUseDcs, bool fVerbose ); +extern Abc_Ntk_t * Abc_NtkFromFraig( Fraig_Man_t * pMan, Abc_Ntk_t * pNtk ); + +static Abc_Ntk_t * Abc_NtkMiterFraig( Abc_Ntk_t * pNtk, int nBTLimit, sint64 nInspLimit, int * pRetValue, int * pNumFails, sint64 * pNumConfs, sint64 * pNumInspects ); +static void Abc_NtkMiterPrint( Abc_Ntk_t * pNtk, char * pString, int clk, int fVerbose ); + + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Attempts to solve the miter using a number of tricks.] + + Description [Returns -1 if timed out; 0 if SAT; 1 if UNSAT. Returns + a simplified version of the original network (or a constant 0 network). + In case the network is not a constant zero and a SAT assignment is found, + pNtk->pModel contains a satisfying assignment.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkMiterProve( Abc_Ntk_t ** ppNtk, void * pPars ) +{ + Prove_Params_t * pParams = pPars; + Abc_Ntk_t * pNtk, * pNtkTemp; + int RetValue, nIter, nSatFails, Counter, clk, timeStart = clock(); + sint64 nSatConfs, nSatInspects, nInspectLimit; + + // get the starting network + pNtk = *ppNtk; + assert( Abc_NtkIsStrash(pNtk) ); + assert( Abc_NtkPoNum(pNtk) == 1 ); + + if ( pParams->fVerbose ) + { + printf( "RESOURCE LIMITS: Iterations = %d. Rewriting = %s. Fraiging = %s.\n", + pParams->nItersMax, pParams->fUseRewriting? "yes":"no", pParams->fUseFraiging? "yes":"no" ); + printf( "Mitering = %d (%3.1f). Rewriting = %d (%3.1f). Fraiging = %d (%3.1f).\n", + pParams->nMiteringLimitStart, pParams->nMiteringLimitMulti, + pParams->nRewritingLimitStart, pParams->nRewritingLimitMulti, + pParams->nFraigingLimitStart, pParams->nFraigingLimitMulti ); + printf( "Mitering last = %d.\n", + pParams->nMiteringLimitLast ); + } + + // if SAT only, solve without iteration + if ( !pParams->fUseRewriting && !pParams->fUseFraiging ) + { + clk = clock(); + RetValue = Abc_NtkMiterSat( pNtk, (sint64)pParams->nMiteringLimitLast, (sint64)0, 0, NULL, NULL ); + Abc_NtkMiterPrint( pNtk, "SAT solving", clk, pParams->fVerbose ); + *ppNtk = pNtk; + return RetValue; + } + + // check the current resource limits + for ( nIter = 0; nIter < pParams->nItersMax; nIter++ ) + { + if ( pParams->fVerbose ) + { + printf( "ITERATION %2d : Confs = %6d. FraigBTL = %3d. \n", nIter+1, + (int)(pParams->nMiteringLimitStart * pow(pParams->nMiteringLimitMulti,nIter)), + (int)(pParams->nFraigingLimitStart * pow(pParams->nFraigingLimitMulti,nIter)) ); + fflush( stdout ); + } + + // try brute-force SAT + clk = clock(); + nInspectLimit = pParams->nTotalInspectLimit? pParams->nTotalInspectLimit - pParams->nTotalInspectsMade : 0; + RetValue = Abc_NtkMiterSat( pNtk, (sint64)(pParams->nMiteringLimitStart * pow(pParams->nMiteringLimitMulti,nIter)), (sint64)nInspectLimit, 0, &nSatConfs, &nSatInspects ); + Abc_NtkMiterPrint( pNtk, "SAT solving", clk, pParams->fVerbose ); + if ( RetValue >= 0 ) + break; + + // add to the number of backtracks and inspects + pParams->nTotalBacktracksMade += nSatConfs; + pParams->nTotalInspectsMade += nSatInspects; + // check if global resource limit is reached + if ( (pParams->nTotalBacktrackLimit && pParams->nTotalBacktracksMade >= pParams->nTotalBacktrackLimit) || + (pParams->nTotalInspectLimit && pParams->nTotalInspectsMade >= pParams->nTotalInspectLimit) ) + { + printf( "Reached global limit on conflicts/inspects. Quitting.\n" ); + *ppNtk = pNtk; + return -1; + } + + // try rewriting + if ( pParams->fUseRewriting ) + { + clk = clock(); + Counter = (int)(pParams->nRewritingLimitStart * pow(pParams->nRewritingLimitMulti,nIter)); +// Counter = 1; + while ( 1 ) + { +/* + extern Abc_Ntk_t * Abc_NtkIvyResyn( Abc_Ntk_t * pNtk, int fUpdateLevel, int fVerbose ); + pNtk = Abc_NtkIvyResyn( pNtkTemp = pNtk, 0, 0 ); Abc_NtkDelete( pNtkTemp ); + if ( (RetValue = Abc_NtkMiterIsConstant(pNtk)) >= 0 ) + break; + if ( --Counter == 0 ) + break; +*/ +/* + Abc_NtkRewrite( pNtk, 0, 0, 0, 0, 0 ); + if ( (RetValue = Abc_NtkMiterIsConstant(pNtk)) >= 0 ) + break; + if ( --Counter == 0 ) + break; +*/ + Abc_NtkRewrite( pNtk, 0, 0, 0, 0, 0 ); + if ( (RetValue = Abc_NtkMiterIsConstant(pNtk)) >= 0 ) + break; + if ( --Counter == 0 ) + break; + Abc_NtkRefactor( pNtk, 10, 16, 0, 0, 0, 0 ); + if ( (RetValue = Abc_NtkMiterIsConstant(pNtk)) >= 0 ) + break; + if ( --Counter == 0 ) + break; + pNtk = Abc_NtkBalance( pNtkTemp = pNtk, 0, 0, 0 ); Abc_NtkDelete( pNtkTemp ); + if ( (RetValue = Abc_NtkMiterIsConstant(pNtk)) >= 0 ) + break; + if ( --Counter == 0 ) + break; + } + Abc_NtkMiterPrint( pNtk, "Rewriting ", clk, pParams->fVerbose ); + } + + if ( pParams->fUseFraiging ) + { + // try FRAIGing + clk = clock(); + nInspectLimit = pParams->nTotalInspectLimit? pParams->nTotalInspectLimit - pParams->nTotalInspectsMade : 0; + pNtk = Abc_NtkMiterFraig( pNtkTemp = pNtk, (int)(pParams->nFraigingLimitStart * pow(pParams->nFraigingLimitMulti,nIter)), nInspectLimit, &RetValue, &nSatFails, &nSatConfs, &nSatInspects ); Abc_NtkDelete( pNtkTemp ); + Abc_NtkMiterPrint( pNtk, "FRAIGing ", clk, pParams->fVerbose ); +// printf( "NumFails = %d\n", nSatFails ); + if ( RetValue >= 0 ) + break; + + // add to the number of backtracks and inspects + pParams->nTotalBacktracksMade += nSatConfs; + pParams->nTotalInspectsMade += nSatInspects; + // check if global resource limit is reached + if ( (pParams->nTotalBacktrackLimit && pParams->nTotalBacktracksMade >= pParams->nTotalBacktrackLimit) || + (pParams->nTotalInspectLimit && pParams->nTotalInspectsMade >= pParams->nTotalInspectLimit) ) + { + printf( "Reached global limit on conflicts/inspects. Quitting.\n" ); + *ppNtk = pNtk; + return -1; + } + } + + } + + // try to prove it using brute force SAT + if ( RetValue < 0 && pParams->fUseBdds ) + { + if ( pParams->fVerbose ) + { + printf( "Attempting BDDs with node limit %d ...\n", pParams->nBddSizeLimit ); + fflush( stdout ); + } + clk = clock(); + pNtk = Abc_NtkCollapse( pNtkTemp = pNtk, pParams->nBddSizeLimit, 0, pParams->fBddReorder, 0 ); + if ( pNtk ) + { + Abc_NtkDelete( pNtkTemp ); + RetValue = ( (Abc_NtkNodeNum(pNtk) == 1) && (Abc_ObjFanin0(Abc_NtkPo(pNtk,0))->pData == Cudd_ReadLogicZero(pNtk->pManFunc)) ); + } + else + pNtk = pNtkTemp; + Abc_NtkMiterPrint( pNtk, "BDD building", clk, pParams->fVerbose ); + } + + if ( RetValue < 0 ) + { + if ( pParams->fVerbose ) + { + printf( "Attempting SAT with conflict limit %d ...\n", pParams->nMiteringLimitLast ); + fflush( stdout ); + } + clk = clock(); + nInspectLimit = pParams->nTotalInspectLimit? pParams->nTotalInspectLimit - pParams->nTotalInspectsMade : 0; + RetValue = Abc_NtkMiterSat( pNtk, (sint64)pParams->nMiteringLimitLast, (sint64)nInspectLimit, 0, NULL, NULL ); + Abc_NtkMiterPrint( pNtk, "SAT solving", clk, pParams->fVerbose ); + } + + // assign the model if it was proved by rewriting (const 1 miter) + if ( RetValue == 0 && pNtk->pModel == NULL ) + { + pNtk->pModel = ALLOC( int, Abc_NtkCiNum(pNtk) ); + memset( pNtk->pModel, 0, sizeof(int) * Abc_NtkCiNum(pNtk) ); + } + *ppNtk = pNtk; + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Attempts to solve the miter using a number of tricks.] + + Description [Returns -1 if timed out; 0 if SAT; 1 if UNSAT.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkMiterFraig( Abc_Ntk_t * pNtk, int nBTLimit, sint64 nInspLimit, int * pRetValue, int * pNumFails, sint64 * pNumConfs, sint64 * pNumInspects ) +{ + Abc_Ntk_t * pNtkNew; + Fraig_Params_t Params, * pParams = &Params; + Fraig_Man_t * pMan; + int nWords1, nWords2, nWordsMin, RetValue; + int * pModel; + + // to determine the number of simulation patterns + // use the following strategy + // at least 64 words (32 words random and 32 words dynamic) + // no more than 256M for one circuit (128M + 128M) + nWords1 = 32; + nWords2 = (1<<27) / (Abc_NtkNodeNum(pNtk) + Abc_NtkCiNum(pNtk)); + nWordsMin = ABC_MIN( nWords1, nWords2 ); + + // set the FRAIGing parameters + Fraig_ParamsSetDefault( pParams ); + pParams->nPatsRand = nWordsMin * 32; // the number of words of random simulation info + pParams->nPatsDyna = nWordsMin * 32; // the number of words of dynamic simulation info + pParams->nBTLimit = nBTLimit; // the max number of backtracks + pParams->nSeconds = -1; // the runtime limit + pParams->fTryProve = 0; // do not try to prove the final miter + pParams->fDoSparse = 1; // try proving sparse functions + pParams->fVerbose = 0; + pParams->nInspLimit = nInspLimit; + + // transform the target into a fraig + pMan = Abc_NtkToFraig( pNtk, pParams, 0, 0 ); + Fraig_ManProveMiter( pMan ); + RetValue = Fraig_ManCheckMiter( pMan ); + + // create the network + pNtkNew = Abc_NtkFromFraig( pMan, pNtk ); + + // save model + if ( RetValue == 0 ) + { + pModel = Fraig_ManReadModel( pMan ); + FREE( pNtkNew->pModel ); + pNtkNew->pModel = ALLOC( int, Abc_NtkCiNum(pNtkNew) ); + memcpy( pNtkNew->pModel, pModel, sizeof(int) * Abc_NtkCiNum(pNtkNew) ); + } + + // save the return values + *pRetValue = RetValue; + *pNumFails = Fraig_ManReadSatFails( pMan ); + *pNumConfs = Fraig_ManReadConflicts( pMan ); + *pNumInspects = Fraig_ManReadInspects( pMan ); + + // delete the fraig manager + Fraig_ManFree( pMan ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Attempts to solve the miter using a number of tricks.] + + Description [Returns -1 if timed out; 0 if SAT; 1 if UNSAT.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMiterPrint( Abc_Ntk_t * pNtk, char * pString, int clk, int fVerbose ) +{ + if ( !fVerbose ) + return; + printf( "Nodes = %7d. Levels = %4d. ", Abc_NtkNodeNum(pNtk), + Abc_NtkIsStrash(pNtk)? Abc_AigLevel(pNtk) : Abc_NtkLevel(pNtk) ); + PRT( pString, clock() - clk ); +} + + +/**Function************************************************************* + + Synopsis [Implements resynthesis for CEC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkMiterRwsat( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkTemp; + Abc_NtkRewrite( pNtk, 0, 0, 0, 0, 0 ); + pNtk = Abc_NtkBalance( pNtkTemp = pNtk, 0, 0, 0 ); Abc_NtkDelete( pNtkTemp ); + Abc_NtkRewrite( pNtk, 0, 0, 0, 0, 0 ); + Abc_NtkRefactor( pNtk, 10, 16, 0, 0, 0, 0 ); + return pNtk; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcQbf.c b/abc_with_bb_support/src/base/abci/abcQbf.c new file mode 100644 index 000000000..f41993fb7 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcQbf.c @@ -0,0 +1,260 @@ +/**CFile**************************************************************** + + FileName [abcQbf.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Implementation of a simple QBF solver.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcQbf.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +/* + Implementation of a simple QBF solver along the lines of + A. Solar-Lezama, L. Tancau, R. Bodik, V. Saraswat, and S. Seshia, + "Combinatorial sketching for finite programs", 12th International + Conference on Architectural Support for Programming Languages and + Operating Systems (ASPLOS 2006), San Jose, CA, October 2006. +*/ + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Abc_NtkModelToVector( Abc_Ntk_t * pNtk, Vec_Int_t * vPiValues ); +static void Abc_NtkVectorClearPars( Vec_Int_t * vPiValues, int nPars ); +static void Abc_NtkVectorClearVars( Abc_Ntk_t * pNtk, Vec_Int_t * vPiValues, int nPars ); +static void Abc_NtkVectorPrintPars( Vec_Int_t * vPiValues, int nPars ); +static void Abc_NtkVectorPrintVars( Abc_Ntk_t * pNtk, Vec_Int_t * vPiValues, int nPars ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Solve the QBF problem EpAx[M(p,x)].] + + Description [Variables p go first, followed by variable x. + The number of parameters is nPars. The miter is in pNtk. + The miter expresses EQUALITY of the implementation and spec.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkQbf( Abc_Ntk_t * pNtk, int nPars, int fVerbose ) +{ + Abc_Ntk_t * pNtkVer, * pNtkSyn, * pNtkSyn2, * pNtkTemp; + Vec_Int_t * vPiValues; + int clkTotal = clock(), clkS, clkV; + int nIters, nIterMax = 500, nInputs, RetValue, fFound = 0; + + assert( Abc_NtkIsStrash(pNtk) ); + assert( Abc_NtkIsComb(pNtk) ); + assert( Abc_NtkPoNum(pNtk) == 1 ); + assert( nPars > 0 && nPars < Abc_NtkPiNum(pNtk) ); + assert( Abc_NtkPiNum(pNtk)-nPars < 32 ); + nInputs = Abc_NtkPiNum(pNtk) - nPars; + + // initialize the synthesized network with 0000-combination + vPiValues = Vec_IntStart( Abc_NtkPiNum(pNtk) ); + Abc_NtkVectorClearPars( vPiValues, nPars ); + pNtkSyn = Abc_NtkMiterCofactor( pNtk, vPiValues ); + if ( fVerbose ) + { + printf( "Iter %2d : ", 0 ); + printf( "AIG = %6d ", Abc_NtkNodeNum(pNtkSyn) ); + Abc_NtkVectorPrintVars( pNtk, vPiValues, nPars ); + printf( "\n" ); + } + + // iteratively solve + for ( nIters = 0; nIters < nIterMax; nIters++ ) + { + // solve the synthesis instance +clkS = clock(); + RetValue = Abc_NtkMiterSat( pNtkSyn, 0, 0, 0, NULL, NULL ); +clkS = clock() - clkS; + if ( RetValue == 0 ) + Abc_NtkModelToVector( pNtkSyn, vPiValues ); + if ( RetValue == 1 ) + { + break; + } + if ( RetValue == -1 ) + { + printf( "Synthesis timed out.\n" ); + break; + } + // there is a counter-example + + // construct the verification instance + Abc_NtkVectorClearVars( pNtk, vPiValues, nPars ); + pNtkVer = Abc_NtkMiterCofactor( pNtk, vPiValues ); + // complement the output + Abc_ObjXorFaninC( Abc_NtkPo(pNtkVer,0), 0 ); + + // solve the verification instance +clkV = clock(); + RetValue = Abc_NtkMiterSat( pNtkVer, 0, 0, 0, NULL, NULL ); +clkV = clock() - clkV; + if ( RetValue == 0 ) + Abc_NtkModelToVector( pNtkVer, vPiValues ); + Abc_NtkDelete( pNtkVer ); + if ( RetValue == 1 ) + { + fFound = 1; + break; + } + if ( RetValue == -1 ) + { + printf( "Verification timed out.\n" ); + break; + } + // there is a counter-example + + // create a new synthesis network + Abc_NtkVectorClearPars( vPiValues, nPars ); + pNtkSyn2 = Abc_NtkMiterCofactor( pNtk, vPiValues ); + // add to the synthesis instance + pNtkSyn = Abc_NtkMiterAnd( pNtkTemp = pNtkSyn, pNtkSyn2, 0, 0 ); + Abc_NtkDelete( pNtkSyn2 ); + Abc_NtkDelete( pNtkTemp ); + + if ( fVerbose ) + { + printf( "Iter %2d : ", nIters+1 ); + printf( "AIG = %6d ", Abc_NtkNodeNum(pNtkSyn) ); + Abc_NtkVectorPrintVars( pNtk, vPiValues, nPars ); + printf( " " ); + PRTn( "Syn", clkS ); + PRT( "Ver", clkV ); + } + } + Abc_NtkDelete( pNtkSyn ); + // report the results + if ( fFound ) + { + printf( "Parameters: " ); + Abc_NtkVectorPrintPars( vPiValues, nPars ); + printf( "\n" ); + printf( "Solved after %d interations. ", nIters ); + } + else if ( nIters == nIterMax ) + printf( "Unsolved after %d interations. ", nIters ); + else + printf( "Implementation does not exist. " ); + PRT( "Total runtime", clock() - clkTotal ); + Vec_IntFree( vPiValues ); +} + + +/**Function************************************************************* + + Synopsis [Translates model into the vector of values.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkModelToVector( Abc_Ntk_t * pNtk, Vec_Int_t * vPiValues ) +{ + int * pModel, i; + pModel = pNtk->pModel; + for ( i = 0; i < Abc_NtkPiNum(pNtk); i++ ) + Vec_IntWriteEntry( vPiValues, i, pModel[i] ); +} + +/**Function************************************************************* + + Synopsis [Clears parameters.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkVectorClearPars( Vec_Int_t * vPiValues, int nPars ) +{ + int i; + for ( i = 0; i < nPars; i++ ) + Vec_IntWriteEntry( vPiValues, i, -1 ); +} + +/**Function************************************************************* + + Synopsis [Clears variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkVectorClearVars( Abc_Ntk_t * pNtk, Vec_Int_t * vPiValues, int nPars ) +{ + int i; + for ( i = nPars; i < Abc_NtkPiNum(pNtk); i++ ) + Vec_IntWriteEntry( vPiValues, i, -1 ); +} + +/**Function************************************************************* + + Synopsis [Clears variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkVectorPrintPars( Vec_Int_t * vPiValues, int nPars ) +{ + int i; + for ( i = 0; i < nPars; i++ ) + printf( "%d", Vec_IntEntry(vPiValues,i) ); +} + +/**Function************************************************************* + + Synopsis [Clears variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkVectorPrintVars( Abc_Ntk_t * pNtk, Vec_Int_t * vPiValues, int nPars ) +{ + int i; + for ( i = nPars; i < Abc_NtkPiNum(pNtk); i++ ) + printf( "%d", Vec_IntEntry(vPiValues,i) ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcQuant.c b/abc_with_bb_support/src/base/abci/abcQuant.c new file mode 100644 index 000000000..515cafc63 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcQuant.c @@ -0,0 +1,419 @@ +/**CFile**************************************************************** + + FileName [abcQuant.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [AIG-based variable quantification.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcQuant.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs fast synthesis.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkSynthesize( Abc_Ntk_t ** ppNtk, int fMoreEffort ) +{ + Abc_Ntk_t * pNtk, * pNtkTemp; + + pNtk = *ppNtk; + + Abc_NtkRewrite( pNtk, 0, 0, 0, 0, 0 ); + Abc_NtkRefactor( pNtk, 10, 16, 0, 0, 0, 0 ); + pNtk = Abc_NtkBalance( pNtkTemp = pNtk, 0, 0, 0 ); + Abc_NtkDelete( pNtkTemp ); + + if ( fMoreEffort ) + { + Abc_NtkRewrite( pNtk, 0, 0, 0, 0, 0 ); + Abc_NtkRefactor( pNtk, 10, 16, 0, 0, 0, 0 ); + pNtk = Abc_NtkBalance( pNtkTemp = pNtk, 0, 0, 0 ); + Abc_NtkDelete( pNtkTemp ); + } + + *ppNtk = pNtk; +} + +/**Function************************************************************* + + Synopsis [Existentially quantifies one variable.] + + Description [] + + SideEffects [This procedure creates dangling nodes in the AIG.] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkQuantify( Abc_Ntk_t * pNtk, int fUniv, int iVar, int fVerbose ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj, * pNext, * pFanin; + int i; + assert( Abc_NtkIsStrash(pNtk) ); + assert( iVar < Abc_NtkCiNum(pNtk) ); + + // collect the internal nodes + pObj = Abc_NtkCi( pNtk, iVar ); + vNodes = Abc_NtkDfsReverseNodes( pNtk, &pObj, 1 ); + + // assign the cofactors of the CI node to be constants + pObj->pCopy = Abc_ObjNot( Abc_AigConst1(pNtk) ); + pObj->pData = Abc_AigConst1(pNtk); + + // quantify the nodes + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + for ( pNext = pObj? pObj->pCopy : pObj; pObj; pObj = pNext, pNext = pObj? pObj->pCopy : pObj ) + { + pFanin = Abc_ObjFanin0(pObj); + if ( !Abc_NodeIsTravIdCurrent(pFanin) ) + pFanin->pCopy = pFanin->pData = pFanin; + pFanin = Abc_ObjFanin1(pObj); + if ( !Abc_NodeIsTravIdCurrent(pFanin) ) + pFanin->pCopy = pFanin->pData = pFanin; + pObj->pCopy = Abc_AigAnd( pNtk->pManFunc, Abc_ObjChild0Copy(pObj), Abc_ObjChild1Copy(pObj) ); + pObj->pData = Abc_AigAnd( pNtk->pManFunc, Abc_ObjChild0Data(pObj), Abc_ObjChild1Data(pObj) ); + } + } + Vec_PtrFree( vNodes ); + + // update the affected COs + Abc_NtkForEachCo( pNtk, pObj, i ) + { + if ( !Abc_NodeIsTravIdCurrent(pObj) ) + continue; + pFanin = Abc_ObjFanin0(pObj); + // get the result of quantification + if ( fUniv ) + pNext = Abc_AigAnd( pNtk->pManFunc, Abc_ObjChild0Copy(pObj), Abc_ObjChild0Data(pObj) ); + else + pNext = Abc_AigOr( pNtk->pManFunc, Abc_ObjChild0Copy(pObj), Abc_ObjChild0Data(pObj) ); + pNext = Abc_ObjNotCond( pNext, Abc_ObjFaninC0(pObj) ); + if ( Abc_ObjRegular(pNext) == pFanin ) + continue; + // update the fanins of the CO + Abc_ObjPatchFanin( pObj, pFanin, pNext ); +// if ( Abc_ObjFanoutNum(pFanin) == 0 ) +// Abc_AigDeleteNode( pNtk->pManFunc, pFanin ); + } + + // make sure the node has no fanouts +// pObj = Abc_NtkCi( pNtk, iVar ); +// assert( Abc_ObjFanoutNum(pObj) == 0 ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Constructs the transition relation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkTransRel( Abc_Ntk_t * pNtk, int fInputs, int fVerbose ) +{ + char Buffer[1000]; + Vec_Ptr_t * vPairs; + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj, * pMiter; + int i, nLatches; + int fSynthesis = 1; + + assert( Abc_NtkIsStrash(pNtk) ); + assert( Abc_NtkLatchNum(pNtk) ); + nLatches = Abc_NtkLatchNum(pNtk); + // start the network + pNtkNew = Abc_NtkAlloc( ABC_NTK_STRASH, ABC_FUNC_AIG, 1 ); + // duplicate the name and the spec + sprintf( Buffer, "%s_TR", pNtk->pName ); + pNtkNew->pName = Extra_UtilStrsav(pNtk->pName); +// pNtkNew->pSpec = Extra_UtilStrsav(pNtk->pSpec); + Abc_NtkCleanCopy( pNtk ); + // create current state variables + Abc_NtkForEachLatchOutput( pNtk, pObj, i ) + { + pObj->pCopy = Abc_NtkCreatePi(pNtkNew); + Abc_ObjAssignName( pObj->pCopy, Abc_ObjName(pObj), NULL ); + } + // create next state variables + Abc_NtkForEachLatchInput( pNtk, pObj, i ) + Abc_ObjAssignName( Abc_NtkCreatePi(pNtkNew), Abc_ObjName(pObj), NULL ); + // create PI variables + Abc_NtkForEachPi( pNtk, pObj, i ) + Abc_NtkDupObj( pNtkNew, pObj, 1 ); + // create the PO + Abc_NtkCreatePo( pNtkNew ); + // restrash the nodes (assuming a topological order of the old network) + Abc_AigConst1(pNtk)->pCopy = Abc_AigConst1(pNtkNew); + Abc_NtkForEachNode( pNtk, pObj, i ) + pObj->pCopy = Abc_AigAnd( pNtkNew->pManFunc, Abc_ObjChild0Copy(pObj), Abc_ObjChild1Copy(pObj) ); + // create the function of the primary output + assert( Abc_NtkBoxNum(pNtk) == Abc_NtkLatchNum(pNtk) ); + vPairs = Vec_PtrAlloc( 2*nLatches ); + Abc_NtkForEachLatchInput( pNtk, pObj, i ) + { + Vec_PtrPush( vPairs, Abc_ObjChild0Copy(pObj) ); + Vec_PtrPush( vPairs, Abc_NtkPi(pNtkNew, i+nLatches) ); + } + pMiter = Abc_AigMiter( pNtkNew->pManFunc, vPairs ); + Vec_PtrFree( vPairs ); + // add the primary output + Abc_ObjAddFanin( Abc_NtkPo(pNtkNew,0), Abc_ObjNot(pMiter) ); + Abc_ObjAssignName( Abc_NtkPo(pNtkNew,0), "rel", NULL ); + + // quantify inputs + if ( fInputs ) + { + assert( Abc_NtkPiNum(pNtkNew) == Abc_NtkPiNum(pNtk) + 2*nLatches ); + for ( i = Abc_NtkPiNum(pNtkNew) - 1; i >= 2*nLatches; i-- ) +// for ( i = 2*nLatches; i < Abc_NtkPiNum(pNtkNew); i++ ) + { + Abc_NtkQuantify( pNtkNew, 0, i, fVerbose ); +// if ( fSynthesis && (i % 3 == 2) ) + if ( fSynthesis ) + { + Abc_NtkCleanData( pNtkNew ); + Abc_AigCleanup( pNtkNew->pManFunc ); + Abc_NtkSynthesize( &pNtkNew, 1 ); + } +// printf( "Var = %3d. Nodes = %6d. ", Abc_NtkPiNum(pNtkNew) - 1 - i, Abc_NtkNodeNum(pNtkNew) ); +// printf( "Var = %3d. Nodes = %6d. ", i - 2*nLatches, Abc_NtkNodeNum(pNtkNew) ); + } +// printf( "\n" ); + Abc_NtkCleanData( pNtkNew ); + Abc_AigCleanup( pNtkNew->pManFunc ); + for ( i = Abc_NtkPiNum(pNtkNew) - 1; i >= 2*nLatches; i-- ) + { + pObj = Abc_NtkPi( pNtkNew, i ); + assert( Abc_ObjFanoutNum(pObj) == 0 ); + Abc_NtkDeleteObj( pObj ); + } + } + + // check consistency of the network + if ( !Abc_NtkCheck( pNtkNew ) ) + { + printf( "Abc_NtkTransRel: The network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + + +/**Function************************************************************* + + Synopsis [Performs one image computation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkInitialState( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pMiter; + int i, nVars = Abc_NtkPiNum(pNtk)/2; + assert( Abc_NtkIsStrash(pNtk) ); + // start the new network + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_STRASH, ABC_FUNC_AIG ); + // compute the all-zero state in terms of the CS variables + pMiter = Abc_AigConst1(pNtkNew); + for ( i = 0; i < nVars; i++ ) + pMiter = Abc_AigAnd( pNtkNew->pManFunc, pMiter, Abc_ObjNot( Abc_NtkPi(pNtkNew, i) ) ); + // add the PO + Abc_ObjAddFanin( Abc_NtkPo(pNtkNew,0), pMiter ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Swaps current state and next state variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkSwapVariables( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pMiter, * pObj, * pObj0, * pObj1; + int i, nVars = Abc_NtkPiNum(pNtk)/2; + assert( Abc_NtkIsStrash(pNtk) ); + // start the new network + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_STRASH, ABC_FUNC_AIG ); + // update the PIs + for ( i = 0; i < nVars; i++ ) + { + pObj0 = Abc_NtkPi( pNtk, i ); + pObj1 = Abc_NtkPi( pNtk, i+nVars ); + pMiter = pObj0->pCopy; + pObj0->pCopy = pObj1->pCopy; + pObj1->pCopy = pMiter; + } + // restrash + Abc_NtkForEachNode( pNtk, pObj, i ) + pObj->pCopy = Abc_AigAnd( pNtkNew->pManFunc, Abc_ObjChild0Copy(pObj), Abc_ObjChild1Copy(pObj) ); + // add the PO + pMiter = Abc_ObjChild0Copy( Abc_NtkPo(pNtk,0) ); + Abc_ObjAddFanin( Abc_NtkPo(pNtkNew,0), pMiter ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Performs reachability analisys.] + + Description [Assumes that the input is the transition relation.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkReachability( Abc_Ntk_t * pNtkRel, int nIters, int fVerbose ) +{ + Abc_Obj_t * pObj; + Abc_Ntk_t * pNtkFront, * pNtkReached, * pNtkNext, * pNtkTemp; + int clk, i, v, nVars, nNodesOld, nNodesNew, nNodesPrev; + int fFixedPoint = 0; + int fSynthesis = 1; + int fMoreEffort = 1; + + assert( Abc_NtkIsStrash(pNtkRel) ); + assert( Abc_NtkLatchNum(pNtkRel) == 0 ); + assert( Abc_NtkPiNum(pNtkRel) % 2 == 0 ); + + // compute the network composed of the initial states + pNtkFront = Abc_NtkInitialState( pNtkRel ); + pNtkReached = Abc_NtkDup( pNtkFront ); +//Abc_NtkShow( pNtkReached, 0, 0, 0 ); + +// if ( fVerbose ) +// printf( "Transition relation = %6d.\n", Abc_NtkNodeNum(pNtkRel) ); + + // perform iterations of reachability analysis + nNodesPrev = Abc_NtkNodeNum(pNtkFront); + nVars = Abc_NtkPiNum(pNtkRel)/2; + for ( i = 0; i < nIters; i++ ) + { + clk = clock(); + // get the set of next states + pNtkNext = Abc_NtkMiterAnd( pNtkRel, pNtkFront, 0, 0 ); + Abc_NtkDelete( pNtkFront ); + // quantify the current state variables + for ( v = 0; v < nVars; v++ ) + { + Abc_NtkQuantify( pNtkNext, 0, v, fVerbose ); + if ( fSynthesis && (v % 3 == 2) ) + { + Abc_NtkCleanData( pNtkNext ); + Abc_AigCleanup( pNtkNext->pManFunc ); + Abc_NtkSynthesize( &pNtkNext, fMoreEffort ); + } + } + Abc_NtkCleanData( pNtkNext ); + Abc_AigCleanup( pNtkNext->pManFunc ); + if ( fSynthesis ) + Abc_NtkSynthesize( &pNtkNext, 1 ); + // map the next states into the current states + pNtkNext = Abc_NtkSwapVariables( pNtkTemp = pNtkNext ); + Abc_NtkDelete( pNtkTemp ); + // check the termination condition + if ( Abc_ObjFanin0(Abc_NtkPo(pNtkNext,0)) == Abc_AigConst1(pNtkNext) ) + { + fFixedPoint = 1; + printf( "Fixed point is reached!\n" ); + Abc_NtkDelete( pNtkNext ); + break; + } + // compute new front + pNtkFront = Abc_NtkMiterAnd( pNtkNext, pNtkReached, 0, 1 ); + Abc_NtkDelete( pNtkNext ); + // add the reached states + pNtkReached = Abc_NtkMiterAnd( pNtkTemp = pNtkReached, pNtkFront, 1, 0 ); + Abc_NtkDelete( pNtkTemp ); + // compress the size of Front + nNodesOld = Abc_NtkNodeNum(pNtkFront); + if ( fSynthesis ) + { + Abc_NtkSynthesize( &pNtkFront, fMoreEffort ); + Abc_NtkSynthesize( &pNtkReached, fMoreEffort ); + } + nNodesNew = Abc_NtkNodeNum(pNtkFront); + // print statistics + if ( fVerbose ) + { + printf( "I = %3d : Reach = %6d Fr = %6d FrM = %6d %7.2f %% ", + i + 1, Abc_NtkNodeNum(pNtkReached), nNodesOld, nNodesNew, 100.0*(nNodesNew-nNodesPrev)/nNodesPrev ); + PRT( "T", clock() - clk ); + } + nNodesPrev = Abc_NtkNodeNum(pNtkFront); + } + if ( !fFixedPoint ) + fprintf( stdout, "Reachability analysis stopped after %d iterations.\n", nIters ); + + // complement the output to represent the set of unreachable states + Abc_ObjXorFaninC( Abc_NtkPo(pNtkReached,0), 0 ); + + // remove next state variables + for ( i = 2*nVars - 1; i >= nVars; i-- ) + { + pObj = Abc_NtkPi( pNtkReached, i ); + assert( Abc_ObjFanoutNum(pObj) == 0 ); + Abc_NtkDeleteObj( pObj ); + } + + // check consistency of the network + if ( !Abc_NtkCheck( pNtkReached ) ) + { + printf( "Abc_NtkReachability: The network check has failed.\n" ); + Abc_NtkDelete( pNtkReached ); + return NULL; + } + return pNtkReached; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcRec.c b/abc_with_bb_support/src/base/abci/abcRec.c new file mode 100644 index 000000000..84d1e8756 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcRec.c @@ -0,0 +1,1173 @@ +/**CFile**************************************************************** + + FileName [abcRec.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Record of semi-canonical AIG subgraphs.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcRec.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "if.h" +#include "kit.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Abc_ManRec_t_ Abc_ManRec_t; +struct Abc_ManRec_t_ +{ + Abc_Ntk_t * pNtk; // the record + Vec_Ptr_t * vTtElems; // the elementary truth tables + Vec_Ptr_t * vTtNodes; // the node truth tables + Abc_Obj_t ** pBins; // hash table mapping truth tables into nodes + int nBins; // the number of allocated bins + int nVars; // the number of variables + int nVarsInit; // the number of variables requested initially + int nWords; // the number of TT words + int nCuts; // the max number of cuts to use + // temporaries + int * pBytes; // temporary storage for minterms + int * pMints; // temporary storage for minterm counters + unsigned * pTemp1; // temporary truth table + unsigned * pTemp2; // temporary truth table + Vec_Ptr_t * vNodes; // the temporary nodes + Vec_Ptr_t * vTtTemps; // the truth tables for the internal nodes of the cut + Vec_Ptr_t * vLabels; // temporary storage for AIG node labels + Vec_Str_t * vCosts; // temporary storage for costs + Vec_Int_t * vMemory; // temporary memory for truth tables + // statistics + int nTried; // the number of cuts tried + int nFilterSize; // the number of same structures + int nFilterRedund; // the number of same structures + int nFilterVolume; // the number of same structures + int nFilterTruth; // the number of same structures + int nFilterError; // the number of same structures + int nFilterSame; // the number of same structures + int nAdded; // the number of subgraphs added + int nAddedFuncs; // the number of functions added + // rewriting + int nFunsFound; // the found functions + int nFunsNotFound; // the missing functions + // runtime + int timeCollect; // the runtime to canonicize + int timeTruth; // the runtime to canonicize + int timeCanon; // the runtime to canonicize + int timeOther; // the runtime to canonicize + int timeTotal; // the runtime to canonicize +}; + +// the truth table is canonicized in such a way that for (00000) its value is 0 + +static Abc_Obj_t ** Abc_NtkRecTableLookup( Abc_ManRec_t * p, unsigned * pTruth, int nVars ); +static int Abc_NtkRecComputeTruth( Abc_Obj_t * pObj, Vec_Ptr_t * vTtNodes, int nVars ); +static int Abc_NtkRecAddCutCheckCycle_rec( Abc_Obj_t * pRoot, Abc_Obj_t * pObj ); + +static Abc_ManRec_t * s_pMan = NULL; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the record for the given network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRecIsRunning() +{ + return s_pMan != NULL; +} + +/**Function************************************************************* + + Synopsis [Starts the record for the given network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRecVarNum() +{ + return (s_pMan != NULL)? s_pMan->nVars : -1; +} + +/**Function************************************************************* + + Synopsis [Starts the record for the given network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Abc_NtkRecMemory() +{ + return s_pMan->vMemory; +} + +/**Function************************************************************* + + Synopsis [Starts the record for the given network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkRecStart( Abc_Ntk_t * pNtk, int nVars, int nCuts ) +{ + Abc_ManRec_t * p; + Abc_Obj_t * pObj, ** ppSpot; + char Buffer[10]; + unsigned * pTruth; + int i, RetValue; + int clkTotal = clock(), clk; + + assert( s_pMan == NULL ); + if ( pNtk == NULL ) + { + assert( nVars > 2 && nVars <= 16 ); + pNtk = Abc_NtkAlloc( ABC_NTK_STRASH, ABC_FUNC_AIG, 1 ); + pNtk->pName = Extra_UtilStrsav( "record" ); + } + else + { + if ( Abc_NtkGetChoiceNum(pNtk) > 0 ) + { + printf( "The starting record should be a network without choice nodes.\n" ); + return; + } + if ( Abc_NtkPiNum(pNtk) > 16 ) + { + printf( "The starting record should be a network with no more than %d primary inputs.\n", 16 ); + return; + } + if ( Abc_NtkPiNum(pNtk) > nVars ) + printf( "The starting record has %d inputs (warning only).\n", Abc_NtkPiNum(pNtk) ); + pNtk = Abc_NtkDup( pNtk ); + } + // create the primary inputs + for ( i = Abc_NtkPiNum(pNtk); i < nVars; i++ ) + { + pObj = Abc_NtkCreatePi( pNtk ); + Buffer[0] = 'a' + i; + Buffer[1] = 0; + Abc_ObjAssignName( pObj, Buffer, NULL ); + } + Abc_NtkCleanCopy( pNtk ); + Abc_NtkCleanEquiv( pNtk ); + + // start the manager + p = ALLOC( Abc_ManRec_t, 1 ); + memset( p, 0, sizeof(Abc_ManRec_t) ); + p->pNtk = pNtk; + p->nVars = Abc_NtkPiNum(pNtk); + p->nWords = Kit_TruthWordNum( p->nVars ); + p->nCuts = nCuts; + p->nVarsInit = nVars; + + // create elementary truth tables + p->vTtElems = Vec_PtrAlloc( 0 ); assert( p->vTtElems->pArray == NULL ); + p->vTtElems->nSize = p->nVars; + p->vTtElems->nCap = p->nVars; + p->vTtElems->pArray = (void *)Extra_TruthElementary( p->nVars ); + + // allocate room for node truth tables + if ( Abc_NtkObjNum(pNtk) > (1<<14) ) + p->vTtNodes = Vec_PtrAllocSimInfo( 2 * Abc_NtkObjNum(pNtk), p->nWords ); + else + p->vTtNodes = Vec_PtrAllocSimInfo( 1<<14, p->nWords ); + + // create hash table + p->nBins = 50011; + p->pBins = ALLOC( Abc_Obj_t *, p->nBins ); + memset( p->pBins, 0, sizeof(Abc_Obj_t *) * p->nBins ); + + // set elementary tables + Kit_TruthFill( Vec_PtrEntry(p->vTtNodes, 0), p->nVars ); + Abc_NtkForEachPi( pNtk, pObj, i ) + Kit_TruthCopy( Vec_PtrEntry(p->vTtNodes, pObj->Id), Vec_PtrEntry(p->vTtElems, i), p->nVars ); + + // compute the tables +clk = clock(); + Abc_AigForEachAnd( pNtk, pObj, i ) + { + RetValue = Abc_NtkRecComputeTruth( pObj, p->vTtNodes, p->nVars ); + assert( RetValue ); + } +p->timeTruth += clock() - clk; + + // insert the PO nodes into the table + Abc_NtkForEachPo( pNtk, pObj, i ) + { + p->nTried++; + p->nAdded++; + + pObj = Abc_ObjFanin0(pObj); + pTruth = Vec_PtrEntry( p->vTtNodes, pObj->Id ); + + if ( pTruth[0] == 1128481603 ) + { + int x = 0; + } + + // add the resulting truth table to the hash table + ppSpot = Abc_NtkRecTableLookup( p, pTruth, p->nVars ); + assert( pObj->pEquiv == NULL ); + assert( pObj->pCopy == NULL ); + if ( *ppSpot == NULL ) + { + p->nAddedFuncs++; + *ppSpot = pObj; + } + else + { + pObj->pEquiv = (*ppSpot)->pEquiv; + (*ppSpot)->pEquiv = (Hop_Obj_t *)pObj; + if ( !Abc_NtkRecAddCutCheckCycle_rec(*ppSpot, pObj) ) + printf( "Loop!\n" ); + } + } + + // temporaries + p->pBytes = ALLOC( int, 4*p->nWords ); + p->pMints = ALLOC( int, 2*p->nVars ); + p->pTemp1 = ALLOC( unsigned, p->nWords ); + p->pTemp2 = ALLOC( unsigned, p->nWords ); + p->vNodes = Vec_PtrAlloc( 100 ); + p->vTtTemps = Vec_PtrAllocSimInfo( 64, p->nWords ); + p->vMemory = Vec_IntAlloc( Abc_TruthWordNum(p->nVars) * 1000 ); + + // set the manager + s_pMan = p; +p->timeTotal += clock() - clkTotal; +} + +/**Function************************************************************* + + Synopsis [Returns the given record.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkRecStop() +{ + assert( s_pMan != NULL ); + if ( s_pMan->pNtk ) + Abc_NtkDelete( s_pMan->pNtk ); + Vec_PtrFree( s_pMan->vTtNodes ); + Vec_PtrFree( s_pMan->vTtElems ); + free( s_pMan->pBins ); + + // temporaries + free( s_pMan->pBytes ); + free( s_pMan->pMints ); + free( s_pMan->pTemp1 ); + free( s_pMan->pTemp2 ); + Vec_PtrFree( s_pMan->vNodes ); + Vec_PtrFree( s_pMan->vTtTemps ); + if ( s_pMan->vLabels ) + Vec_PtrFree( s_pMan->vLabels ); + if ( s_pMan->vCosts ) + Vec_StrFree( s_pMan->vCosts ); + Vec_IntFree( s_pMan->vMemory ); + + free( s_pMan ); + s_pMan = NULL; +} + +/**Function************************************************************* + + Synopsis [Returns the given record.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkRecUse() +{ + Abc_ManRec_t * p = s_pMan; + Abc_Ntk_t * pNtk = p->pNtk; + assert( p != NULL ); + Abc_NtkRecPs(); + p->pNtk = NULL; + Abc_NtkRecStop(); + return pNtk; +} + +static inline void Abc_ObjSetMax( Abc_Obj_t * pObj, int Value ) { assert( pObj->Level < 0xff ); pObj->Level = (Value << 8) | (pObj->Level & 0xff); } +static inline void Abc_ObjClearMax( Abc_Obj_t * pObj ) { pObj->Level = (pObj->Level & 0xff); } +static inline int Abc_ObjGetMax( Abc_Obj_t * pObj ) { return (pObj->Level >> 8) & 0xff; } + +/**Function************************************************************* + + Synopsis [Print statistics about the current record.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkRecPs() +{ + int Counter, Counters[17] = {0}; + int CounterS, CountersS[17] = {0}; + Abc_ManRec_t * p = s_pMan; + Abc_Ntk_t * pNtk = p->pNtk; + Abc_Obj_t * pObj, * pEntry, * pTemp; + int i; + + // set the max PI number + Abc_NtkForEachPi( pNtk, pObj, i ) + Abc_ObjSetMax( pObj, i+1 ); + Abc_AigForEachAnd( pNtk, pObj, i ) + Abc_ObjSetMax( pObj, ABC_MAX( Abc_ObjGetMax(Abc_ObjFanin0(pObj)), Abc_ObjGetMax(Abc_ObjFanin1(pObj)) ) ); + // go through the table + Counter = CounterS = 0; + for ( i = 0; i < p->nBins; i++ ) + for ( pEntry = p->pBins[i]; pEntry; pEntry = pEntry->pCopy ) + { + Counters[ Abc_ObjGetMax(pEntry) ]++; + Counter++; + for ( pTemp = pEntry; pTemp; pTemp = (Abc_Obj_t *)pTemp->pEquiv ) + { + assert( Abc_ObjGetMax(pTemp) == Abc_ObjGetMax(pEntry) ); + CountersS[ Abc_ObjGetMax(pTemp) ]++; + CounterS++; + } + } +// printf( "Functions = %d. Expected = %d.\n", Counter, p->nAddedFuncs ); +// printf( "Subgraphs = %d. Expected = %d.\n", CounterS, p->nAdded ); + assert( Counter == p->nAddedFuncs ); + assert( CounterS == p->nAdded ); + + // clean + Abc_NtkForEachObj( pNtk, pObj, i ) + { + Abc_ObjClearMax( pObj ); + } + + printf( "The record with %d AND nodes in %d subgraphs for %d functions with %d inputs:\n", + Abc_NtkNodeNum(pNtk), Abc_NtkPoNum(pNtk), p->nAddedFuncs, Abc_NtkPiNum(pNtk) ); + for ( i = 0; i <= 16; i++ ) + { + if ( Counters[i] ) + printf( "Inputs = %2d. Funcs = %8d. Subgrs = %8d. Ratio = %6.2f.\n", i, Counters[i], CountersS[i], 1.0*CountersS[i]/Counters[i] ); + } + + printf( "Subgraphs tried = %8d. (%6.2f %%)\n", p->nTried, !p->nTried? 0 : 100.0*p->nTried/p->nTried ); + printf( "Subgraphs filtered by support size = %8d. (%6.2f %%)\n", p->nFilterSize, !p->nTried? 0 : 100.0*p->nFilterSize/p->nTried ); + printf( "Subgraphs filtered by structural redundancy = %8d. (%6.2f %%)\n", p->nFilterRedund, !p->nTried? 0 : 100.0*p->nFilterRedund/p->nTried ); + printf( "Subgraphs filtered by volume = %8d. (%6.2f %%)\n", p->nFilterVolume, !p->nTried? 0 : 100.0*p->nFilterVolume/p->nTried ); + printf( "Subgraphs filtered by TT redundancy = %8d. (%6.2f %%)\n", p->nFilterTruth, !p->nTried? 0 : 100.0*p->nFilterTruth/p->nTried ); + printf( "Subgraphs filtered by error = %8d. (%6.2f %%)\n", p->nFilterError, !p->nTried? 0 : 100.0*p->nFilterError/p->nTried ); + printf( "Subgraphs filtered by isomorphism = %8d. (%6.2f %%)\n", p->nFilterSame, !p->nTried? 0 : 100.0*p->nFilterSame/p->nTried ); + printf( "Subgraphs added = %8d. (%6.2f %%)\n", p->nAdded, !p->nTried? 0 : 100.0*p->nAdded/p->nTried ); + printf( "Functions added = %8d. (%6.2f %%)\n", p->nAddedFuncs, !p->nTried? 0 : 100.0*p->nAddedFuncs/p->nTried ); + + p->timeOther = p->timeTotal - p->timeCollect - p->timeTruth - p->timeCanon; + PRTP( "Collecting nodes ", p->timeCollect, p->timeTotal ); + PRTP( "Computing truth ", p->timeTruth, p->timeTotal ); + PRTP( "Canonicizing ", p->timeCanon, p->timeTotal ); + PRTP( "Other ", p->timeOther, p->timeTotal ); + PRTP( "TOTAL ", p->timeTotal, p->timeTotal ); + if ( p->nFunsFound ) + printf( "During rewriting found = %d and not found = %d functions.\n", p->nFunsFound, p->nFunsNotFound ); +} + +/**Function************************************************************* + + Synopsis [Filters the current record.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkRecFilter( int iVar, int iPlus ) +{ +} + +/**Function************************************************************* + + Synopsis [Returns the hash key.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Abc_NtkRecTableHash( unsigned * pTruth, int nVars, int nBins, int * pPrimes ) +{ + int i, nWords = Kit_TruthWordNum( nVars ); + unsigned uHash = 0; + for ( i = 0; i < nWords; i++ ) + uHash ^= pTruth[i] * pPrimes[i & 0x7]; + return uHash % nBins; +} + +/**Function************************************************************* + + Synopsis [Returns the given record.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t ** Abc_NtkRecTableLookup( Abc_ManRec_t * p, unsigned * pTruth, int nVars ) +{ + static int s_Primes[10] = { 1291, 1699, 2357, 4177, 5147, 5647, 6343, 7103, 7873, 8147 }; + Abc_Obj_t ** ppSpot, * pEntry; + ppSpot = p->pBins + Abc_NtkRecTableHash( pTruth, nVars, p->nBins, s_Primes ); + for ( pEntry = *ppSpot; pEntry; ppSpot = &pEntry->pCopy, pEntry = pEntry->pCopy ) + if ( Kit_TruthIsEqualWithPhase(Vec_PtrEntry(p->vTtNodes, pEntry->Id), pTruth, nVars) ) + return ppSpot; + return ppSpot; +} + +/**Function************************************************************* + + Synopsis [Computes the truth table of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRecComputeTruth( Abc_Obj_t * pObj, Vec_Ptr_t * vTtNodes, int nVars ) +{ + unsigned * pTruth, * pTruth0, * pTruth1; + int RetValue; + assert( Abc_ObjIsNode(pObj) ); + pTruth = Vec_PtrEntry( vTtNodes, pObj->Id ); + pTruth0 = Vec_PtrEntry( vTtNodes, Abc_ObjFaninId0(pObj) ); + pTruth1 = Vec_PtrEntry( vTtNodes, Abc_ObjFaninId1(pObj) ); + Kit_TruthAndPhase( pTruth, pTruth0, pTruth1, nVars, Abc_ObjFaninC0(pObj), Abc_ObjFaninC1(pObj) ); + assert( (pTruth[0] & 1) == pObj->fPhase ); + RetValue = ((pTruth[0] & 1) == pObj->fPhase); + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Performs renoding as technology mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkRecAdd( Abc_Ntk_t * pNtk ) +{ + extern Abc_Ntk_t * Abc_NtkIf( Abc_Ntk_t * pNtk, If_Par_t * pPars ); + extern int Abc_NtkRecAddCut( If_Man_t * pIfMan, If_Obj_t * pRoot, If_Cut_t * pCut ); + + If_Par_t Pars, * pPars = &Pars; + Abc_Ntk_t * pNtkNew; + int clk = clock(); + + if ( Abc_NtkGetChoiceNum( pNtk ) ) + printf( "Performing renoding with choices.\n" ); + + // set defaults + memset( pPars, 0, sizeof(If_Par_t) ); + // user-controlable paramters + pPars->nLutSize = s_pMan->nVarsInit; + pPars->nCutsMax = s_pMan->nCuts; + pPars->nFlowIters = 0; + pPars->nAreaIters = 0; + pPars->DelayTarget = -1; + pPars->fPreprocess = 0; + pPars->fArea = 1; + pPars->fFancy = 0; + pPars->fExpRed = 0; + pPars->fLatchPaths = 0; + pPars->fSeqMap = 0; + pPars->fVerbose = 0; + // internal parameters + pPars->fTruth = 0; + pPars->fUsePerm = 0; + pPars->nLatches = 0; + pPars->pLutLib = NULL; // Abc_FrameReadLibLut(); + pPars->pTimesArr = NULL; + pPars->pTimesArr = NULL; + pPars->fUseBdds = 0; + pPars->fUseSops = 0; + pPars->fUseCnfs = 0; + pPars->fUseMv = 0; + pPars->pFuncCost = NULL; + pPars->pFuncUser = Abc_NtkRecAddCut; + + // perform recording + pNtkNew = Abc_NtkIf( pNtk, pPars ); + Abc_NtkDelete( pNtkNew ); +s_pMan->timeTotal += clock() - clk; + +// if ( !Abc_NtkCheck( s_pMan->pNtk ) ) +// printf( "Abc_NtkRecAdd: The network check has failed.\n" ); +} + +/**Function************************************************************* + + Synopsis [Adds the cut function to the internal storage.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkRecCollectNodes_rec( If_Obj_t * pNode, Vec_Ptr_t * vNodes ) +{ + if ( pNode->fMark ) + return; + pNode->fMark = 1; + assert( If_ObjIsAnd(pNode) ); + Abc_NtkRecCollectNodes_rec( If_ObjFanin0(pNode), vNodes ); + Abc_NtkRecCollectNodes_rec( If_ObjFanin1(pNode), vNodes ); + Vec_PtrPush( vNodes, pNode ); +} + +/**Function************************************************************* + + Synopsis [Adds the cut function to the internal storage.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRecCollectNodes( If_Man_t * pIfMan, If_Obj_t * pRoot, If_Cut_t * pCut, Vec_Ptr_t * vNodes ) +{ + If_Obj_t * pLeaf; + int i, RetValue = 1; + + // collect the internal nodes of the cut + Vec_PtrClear( vNodes ); + If_CutForEachLeaf( pIfMan, pCut, pLeaf, i ) + { + Vec_PtrPush( vNodes, pLeaf ); + assert( pLeaf->fMark == 0 ); + pLeaf->fMark = 1; + } + + // collect other nodes + Abc_NtkRecCollectNodes_rec( pRoot, vNodes ); + + // check if there are leaves, such that both of their fanins are marked + // this indicates a redundant cut + If_CutForEachLeaf( pIfMan, pCut, pLeaf, i ) + { + if ( !If_ObjIsAnd(pLeaf) ) + continue; + if ( If_ObjFanin0(pLeaf)->fMark && If_ObjFanin1(pLeaf)->fMark ) + { + RetValue = 0; + break; + } + } + + // clean the mark + Vec_PtrForEachEntry( vNodes, pLeaf, i ) + pLeaf->fMark = 0; +/* + if ( pRoot->Id == 2639 ) + { + // print the cut + Vec_PtrForEachEntry( vNodes, pLeaf, i ) + { + if ( If_ObjIsAnd(pLeaf) ) + printf( "%4d = %c%4d & %c%4d\n", pLeaf->Id, + (If_ObjFaninC0(pLeaf)? '-':'+'), If_ObjFanin0(pLeaf)->Id, + (If_ObjFaninC1(pLeaf)? '-':'+'), If_ObjFanin1(pLeaf)->Id ); + else + printf( "%4d = pi\n", pLeaf->Id ); + } + printf( "\n" ); + } +*/ + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Computes truth tables of nodes in the cut.] + + Description [Returns 0 if the TT does not depend on some cut variables. + Or if the TT can be expressed simpler using other nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRecCutTruth( Vec_Ptr_t * vNodes, int nLeaves, Vec_Ptr_t * vTtTemps, Vec_Ptr_t * vTtElems ) +{ + unsigned * pSims, * pSims0, * pSims1; + unsigned * pTemp = s_pMan->pTemp2; + unsigned uWord; + If_Obj_t * pObj, * pObj2, * pRoot; + int i, k, nLimit, nInputs = s_pMan->nVars; + + assert( Vec_PtrSize(vNodes) > nLeaves ); + + // set the elementary truth tables and compute the truth tables of the nodes + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + pObj->pCopy = Vec_PtrEntry(vTtTemps, i); + pSims = (unsigned *)pObj->pCopy; + if ( i < nLeaves ) + { + Kit_TruthCopy( pSims, Vec_PtrEntry(vTtElems, i), nInputs ); + continue; + } + assert( If_ObjIsAnd(pObj) ); + // get hold of the simulation information + pSims0 = (unsigned *)If_ObjFanin0(pObj)->pCopy; + pSims1 = (unsigned *)If_ObjFanin1(pObj)->pCopy; + // simulate the node + Kit_TruthAndPhase( pSims, pSims0, pSims1, nInputs, If_ObjFaninC0(pObj), If_ObjFaninC1(pObj) ); + } + + // check the support size + pRoot = Vec_PtrEntryLast( vNodes ); + pSims = (unsigned *)pRoot->pCopy; + if ( Kit_TruthSupport(pSims, nInputs) != Kit_BitMask(nLeaves) ) + return 0; + + // make sure none of the nodes has the same simulation info as the output + // check pairwise comparisons + nLimit = Vec_PtrSize(vNodes) - 1; + Vec_PtrForEachEntryStop( vNodes, pObj, i, nLimit ) + { + pSims0 = (unsigned *)pObj->pCopy; + if ( Kit_TruthIsEqualWithPhase(pSims, pSims0, nInputs) ) + return 0; + Vec_PtrForEachEntryStop( vNodes, pObj2, k, i ) + { + if ( (If_ObjFanin0(pRoot) == pObj && If_ObjFanin1(pRoot) == pObj2) || + (If_ObjFanin1(pRoot) == pObj && If_ObjFanin0(pRoot) == pObj2) ) + continue; + pSims1 = (unsigned *)pObj2->pCopy; + + uWord = pSims0[0] & pSims1[0]; + if ( pSims[0] == uWord || pSims[0] == ~uWord ) + { + Kit_TruthAndPhase( pTemp, pSims0, pSims1, nInputs, 0, 0 ); + if ( Kit_TruthIsEqualWithPhase(pSims, pTemp, nInputs) ) + return 0; + } + + uWord = pSims0[0] & ~pSims1[0]; + if ( pSims[0] == uWord || pSims[0] == ~uWord ) + { + Kit_TruthAndPhase( pTemp, pSims0, pSims1, nInputs, 0, 1 ); + if ( Kit_TruthIsEqualWithPhase(pSims, pTemp, nInputs) ) + return 0; + } + + uWord = ~pSims0[0] & pSims1[0]; + if ( pSims[0] == uWord || pSims[0] == ~uWord ) + { + Kit_TruthAndPhase( pTemp, pSims0, pSims1, nInputs, 1, 0 ); + if ( Kit_TruthIsEqualWithPhase(pSims, pTemp, nInputs) ) + return 0; + } + + uWord = ~pSims0[0] & ~pSims1[0]; + if ( pSims[0] == uWord || pSims[0] == ~uWord ) + { + Kit_TruthAndPhase( pTemp, pSims0, pSims1, nInputs, 1, 1 ); + if ( Kit_TruthIsEqualWithPhase(pSims, pTemp, nInputs) ) + return 0; + } + } + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Adds the cut function to the internal storage.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRecAddCutCheckCycle_rec( Abc_Obj_t * pRoot, Abc_Obj_t * pObj ) +{ + assert( pRoot->Level > 0 ); + if ( pObj->Level < pRoot->Level ) + return 1; + if ( pObj == pRoot ) + return 0; + if ( !Abc_NtkRecAddCutCheckCycle_rec(pRoot, Abc_ObjFanin0(pObj)) ) + return 0; + if ( !Abc_NtkRecAddCutCheckCycle_rec(pRoot, Abc_ObjFanin1(pObj)) ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Adds the cut function to the internal storage.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRecAddCut( If_Man_t * pIfMan, If_Obj_t * pRoot, If_Cut_t * pCut ) +{ + static int s_MaxSize[16] = { 0 }; + char Buffer[40], Name[20], Truth[20]; + char pCanonPerm[16]; + Abc_Obj_t * pObj, * pFanin0, * pFanin1, ** ppSpot, * pObjPo; + Abc_Ntk_t * pAig = s_pMan->pNtk; + If_Obj_t * pIfObj; + Vec_Ptr_t * vNodes = s_pMan->vNodes; + unsigned * pInOut = s_pMan->pTemp1; + unsigned * pTemp = s_pMan->pTemp2; + unsigned * pTruth; + int i, RetValue, nNodes, nNodesBeg, nInputs = s_pMan->nVars, nLeaves = If_CutLeaveNum(pCut); + unsigned uCanonPhase; + int clk; + + if ( pRoot->Id == 2639 ) + { + int y = 0; + } + + assert( nInputs <= 16 ); + assert( nInputs == (int)pCut->nLimit ); + s_pMan->nTried++; + + // skip small cuts + if ( nLeaves < 3 ) + { + s_pMan->nFilterSize++; + return 1; + } + + // collect internal nodes and skip redundant cuts +clk = clock(); + RetValue = Abc_NtkRecCollectNodes( pIfMan, pRoot, pCut, vNodes ); +s_pMan->timeCollect += clock() - clk; + if ( !RetValue ) + { + s_pMan->nFilterRedund++; + return 1; + } + + // skip cuts with very large volume + if ( Vec_PtrSize(vNodes) > nLeaves + 3*(nLeaves-1) + s_MaxSize[nLeaves] ) + { + s_pMan->nFilterVolume++; + return 1; + } + + // compute truth table and skip the redundant structures +clk = clock(); + RetValue = Abc_NtkRecCutTruth( vNodes, nLeaves, s_pMan->vTtTemps, s_pMan->vTtElems ); +s_pMan->timeTruth += clock() - clk; + if ( !RetValue ) + { + s_pMan->nFilterTruth++; + return 1; + } + + // copy the truth table + Kit_TruthCopy( pInOut, (unsigned *)pRoot->pCopy, nInputs ); + + // set permutation + for ( i = 0; i < nInputs; i++ ) + pCanonPerm[i] = i; + + // semi-canonicize the truth table +clk = clock(); + uCanonPhase = Kit_TruthSemiCanonicize( pInOut, pTemp, nInputs, pCanonPerm, (short *)s_pMan->pMints ); +s_pMan->timeCanon += clock() - clk; + // pCanonPerm and uCanonPhase show what was the variable corresponding to each var in the current truth + + // go through the variables in the new truth table + for ( i = 0; i < nLeaves; i++ ) + { + // get hold of the corresponding leaf + pIfObj = If_ManObj( pIfMan, pCut->pLeaves[pCanonPerm[i]] ); + // get hold of the corresponding new node + pObj = Abc_NtkPi( pAig, i ); + pObj = Abc_ObjNotCond( pObj, (uCanonPhase & (1 << i)) ); + // map them + pIfObj->pCopy = pObj; +/* + if ( pRoot->Id == 2639 ) + { + unsigned uSupp; + printf( "Node %6d : ", pIfObj->Id ); + printf( "Support " ); + uSupp = Kit_TruthSupport(Vec_PtrEntry( s_pMan->vTtNodes, Abc_ObjRegular(pObj)->Id ), nInputs); + Extra_PrintBinary( stdout, &uSupp, nInputs ); + printf( " " ); + Extra_PrintBinary( stdout, Vec_PtrEntry( s_pMan->vTtNodes, Abc_ObjRegular(pObj)->Id ), 1<<6 ); + printf( "\n" ); + } +*/ + } + + // build the node and compute its truth table + nNodesBeg = Abc_NtkObjNumMax( pAig ); + Vec_PtrForEachEntryStart( vNodes, pIfObj, i, nLeaves ) + { + pFanin0 = Abc_ObjNotCond( If_ObjFanin0(pIfObj)->pCopy, If_ObjFaninC0(pIfObj) ); + pFanin1 = Abc_ObjNotCond( If_ObjFanin1(pIfObj)->pCopy, If_ObjFaninC1(pIfObj) ); + + nNodes = Abc_NtkObjNumMax( pAig ); + pObj = Abc_AigAnd( pAig->pManFunc, pFanin0, pFanin1 ); + assert( !Abc_ObjIsComplement(pObj) ); + pIfObj->pCopy = pObj; + + if ( pObj->Id == nNodes ) + { + // increase storage for truth tables + if ( Vec_PtrSize(s_pMan->vTtNodes) <= pObj->Id ) + Vec_PtrDoubleSimInfo(s_pMan->vTtNodes); + // compute the truth table + RetValue = Abc_NtkRecComputeTruth( pObj, s_pMan->vTtNodes, nInputs ); + if ( RetValue == 0 ) + { + s_pMan->nFilterError++; + printf( "T" ); + return 1; + } + } + } + + pTruth = Vec_PtrEntry( s_pMan->vTtNodes, pObj->Id ); + if ( Kit_TruthSupport(pTruth, nInputs) != Kit_BitMask(nLeaves) ) + { + s_pMan->nFilterError++; + printf( "S" ); + return 1; + } + + // compare the truth tables + if ( !Kit_TruthIsEqualWithPhase( pTruth, pInOut, nInputs ) ) + { + s_pMan->nFilterError++; + printf( "F" ); + return 1; + } +// Extra_PrintBinary( stdout, pInOut, 8 ); printf( "\n" ); + + // if not new nodes were added and the node has a CO fanout + if ( nNodesBeg == Abc_NtkObjNumMax(pAig) && Abc_NodeFindCoFanout(pObj) != NULL ) + { + s_pMan->nFilterSame++; + return 1; + } + s_pMan->nAdded++; + + // create PO for this node + pObjPo = Abc_NtkCreatePo(pAig); + Abc_ObjAddFanin( pObjPo, pObj ); + + // assign the name to this PO + sprintf( Name, "%d_%06d", nLeaves, Abc_NtkPoNum(pAig) ); + if ( (nInputs <= 6) && 0 ) + { + Extra_PrintHexadecimalString( Truth, pInOut, nInputs ); + sprintf( Buffer, "%s_%s", Name, Truth ); + } + else + { + sprintf( Buffer, "%s", Name ); + } + Abc_ObjAssignName( pObjPo, Buffer, NULL ); + + // add the resulting truth table to the hash table + ppSpot = Abc_NtkRecTableLookup( s_pMan, pTruth, nInputs ); + assert( pObj->pEquiv == NULL ); + assert( pObj->pCopy == NULL ); + if ( *ppSpot == NULL ) + { + s_pMan->nAddedFuncs++; + *ppSpot = pObj; + } + else + { + pObj->pEquiv = (*ppSpot)->pEquiv; + (*ppSpot)->pEquiv = (Hop_Obj_t *)pObj; + if ( !Abc_NtkRecAddCutCheckCycle_rec(*ppSpot, pObj) ) + printf( "Loop!\n" ); + } + return 1; +} + + +/**Function************************************************************* + + Synopsis [Labels the record AIG with the corresponding new AIG nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkRecStrashNodeLabel_rec( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pObj, int fBuild, Vec_Ptr_t * vLabels ) +{ + Abc_Obj_t * pFanin0New, * pFanin1New, * pLabel; + assert( !Abc_ObjIsComplement(pObj) ); + // if this node is already visited, skip + if ( Abc_NodeIsTravIdCurrent( pObj ) ) + return Vec_PtrEntry( vLabels, pObj->Id ); + assert( Abc_ObjIsNode(pObj) ); + // mark the node as visited + Abc_NodeSetTravIdCurrent( pObj ); + // label the fanins + pFanin0New = Abc_NtkRecStrashNodeLabel_rec( pNtkNew, Abc_ObjFanin0(pObj), fBuild, vLabels ); + pFanin1New = Abc_NtkRecStrashNodeLabel_rec( pNtkNew, Abc_ObjFanin1(pObj), fBuild, vLabels ); + // label the node if possible + pLabel = NULL; + if ( pFanin0New && pFanin1New ) + { + pFanin0New = Abc_ObjNotCond( pFanin0New, Abc_ObjFaninC0(pObj) ); + pFanin1New = Abc_ObjNotCond( pFanin1New, Abc_ObjFaninC1(pObj) ); + if ( fBuild ) + pLabel = Abc_AigAnd( pNtkNew->pManFunc, pFanin0New, pFanin1New ); + else + pLabel = Abc_AigAndLookup( pNtkNew->pManFunc, pFanin0New, pFanin1New ); + } + Vec_PtrWriteEntry( vLabels, pObj->Id, pLabel ); + return pLabel; +} + +/**Function************************************************************* + + Synopsis [Counts the area of the given node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRecStrashNodeCount_rec( Abc_Obj_t * pObj, Vec_Str_t * vCosts, Vec_Ptr_t * vLabels ) +{ + int Cost0, Cost1; + if ( Vec_PtrEntry( vLabels, pObj->Id ) ) + return 0; + assert( Abc_ObjIsNode(pObj) ); + // if this node is already visited, skip + if ( Abc_NodeIsTravIdCurrent( pObj ) ) + return Vec_StrEntry( vCosts, pObj->Id ); + // mark the node as visited + Abc_NodeSetTravIdCurrent( pObj ); + // count for the fanins + Cost0 = Abc_NtkRecStrashNodeCount_rec( Abc_ObjFanin0(pObj), vCosts, vLabels ); + Cost1 = Abc_NtkRecStrashNodeCount_rec( Abc_ObjFanin1(pObj), vCosts, vLabels ); + Vec_StrWriteEntry( vCosts, pObj->Id, (char)(Cost0 + Cost1 + 1) ); + return Cost0 + Cost1 + 1; +} + +/**Function************************************************************* + + Synopsis [Strashes the given node using its local function.] + + Description [Assumes that the fanins are already strashed. + Returns 0 if the function is not found in the table.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRecStrashNode( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pObj, unsigned * pTruth, int nVars ) +{ + char pCanonPerm[16]; + Abc_Ntk_t * pAig = s_pMan->pNtk; + unsigned * pInOut = s_pMan->pTemp1; + unsigned * pTemp = s_pMan->pTemp2; + unsigned * pTruthRec; + Abc_Obj_t * pCand, * pCandMin, * pLeaf, * pFanin, ** ppSpot; + unsigned uCanonPhase; + int i, nLeaves, CostMin, Cost, nOnes, fCompl; + + // check if the record works + nLeaves = Abc_ObjFaninNum(pObj); + assert( nLeaves >= 3 && nLeaves <= s_pMan->nVars ); + pFanin = Abc_ObjFanin0(pObj); + assert( Abc_ObjRegular(pFanin->pCopy)->pNtk == pNtkNew ); + assert( s_pMan != NULL ); + assert( nVars == s_pMan->nVars ); + + // copy the truth table + Kit_TruthCopy( pInOut, pTruth, nVars ); + + // set permutation + for ( i = 0; i < nVars; i++ ) + pCanonPerm[i] = i; + + // canonicize the truth table + uCanonPhase = Kit_TruthSemiCanonicize( pInOut, pTemp, nVars, pCanonPerm, (short *)s_pMan->pMints ); + + // get hold of the curresponding class + ppSpot = Abc_NtkRecTableLookup( s_pMan, pInOut, nVars ); + if ( *ppSpot == NULL ) + { + s_pMan->nFunsNotFound++; +// printf( "The class of a function with %d inputs is not found.\n", nLeaves ); + return 0; + } + s_pMan->nFunsFound++; + + // make sure the truth table is the same + pTruthRec = Vec_PtrEntry( s_pMan->vTtNodes, (*ppSpot)->Id ); + if ( !Kit_TruthIsEqualWithPhase( pTruthRec, pInOut, nVars ) ) + { + assert( 0 ); + return 0; + } + + + // allocate storage for costs + if ( s_pMan->vLabels && Vec_PtrSize(s_pMan->vLabels) < Abc_NtkObjNumMax(pAig) ) + { + Vec_PtrFree( s_pMan->vLabels ); + s_pMan->vLabels = NULL; + } + if ( s_pMan->vLabels == NULL ) + s_pMan->vLabels = Vec_PtrStart( Abc_NtkObjNumMax(pAig) ); + + // go through the variables in the new truth table + Abc_NtkIncrementTravId( pAig ); + for ( i = 0; i < nLeaves; i++ ) + { + // get hold of the corresponding fanin + pFanin = Abc_ObjFanin( pObj, pCanonPerm[i] )->pCopy; + pFanin = Abc_ObjNotCond( pFanin, (uCanonPhase & (1 << i)) ); + // label the PI of the AIG subgraphs with this fanin + pLeaf = Abc_NtkPi( pAig, i ); + Vec_PtrWriteEntry( s_pMan->vLabels, pLeaf->Id, pFanin ); + Abc_NodeSetTravIdCurrent( pLeaf ); + } + + // go through the candidates - and recursively label them + for ( pCand = *ppSpot; pCand; pCand = (Abc_Obj_t *)pCand->pEquiv ) + Abc_NtkRecStrashNodeLabel_rec( pNtkNew, pCand, 0, s_pMan->vLabels ); + + + // allocate storage for costs + if ( s_pMan->vCosts && Vec_StrSize(s_pMan->vCosts) < Abc_NtkObjNumMax(pAig) ) + { + Vec_StrFree( s_pMan->vCosts ); + s_pMan->vCosts = NULL; + } + if ( s_pMan->vCosts == NULL ) + s_pMan->vCosts = Vec_StrStart( Abc_NtkObjNumMax(pAig) ); + + // find the best subgraph + CostMin = ABC_INFINITY; + pCandMin = NULL; + for ( pCand = *ppSpot; pCand; pCand = (Abc_Obj_t *)pCand->pEquiv ) + { + // label the leaves + Abc_NtkIncrementTravId( pAig ); + // count the number of non-labeled nodes + Cost = Abc_NtkRecStrashNodeCount_rec( pCand, s_pMan->vCosts, s_pMan->vLabels ); + if ( CostMin > Cost ) + { +// printf( "%d ", Cost ); + CostMin = Cost; + pCandMin = pCand; + } + } +// printf( "\n" ); + assert( pCandMin != NULL ); + if ( pCandMin == NULL ) + return 0; + + + // label the leaves + Abc_NtkIncrementTravId( pAig ); + for ( i = 0; i < nLeaves; i++ ) + Abc_NodeSetTravIdCurrent( Abc_NtkPi(pAig, i) ); + + // implement the subgraph + pObj->pCopy = Abc_NtkRecStrashNodeLabel_rec( pNtkNew, pCandMin, 1, s_pMan->vLabels ); + assert( Abc_ObjRegular(pObj->pCopy)->pNtk == pNtkNew ); + + // determine phase difference + nOnes = Kit_TruthCountOnes(pTruth, nVars); + fCompl = (nOnes > (1<< nVars)/2); +// assert( fCompl == ((uCanonPhase & (1 << nVars)) > 0) ); + + nOnes = Kit_TruthCountOnes(pTruthRec, nVars); + fCompl ^= (nOnes > (1<< nVars)/2); + // complement + pObj->pCopy = Abc_ObjNotCond( pObj->pCopy, fCompl ); + return 1; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcReconv.c b/abc_with_bb_support/src/base/abci/abcReconv.c new file mode 100644 index 000000000..5acbe3f83 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcReconv.c @@ -0,0 +1,762 @@ +/**CFile**************************************************************** + + FileName [abcReconv.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Computation of reconvergence-driven cuts.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcReconv.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +struct Abc_ManCut_t_ +{ + // user specified parameters + int nNodeSizeMax; // the limit on the size of the supernode + int nConeSizeMax; // the limit on the size of the containing cone + int nNodeFanStop; // the limit on the size of the supernode + int nConeFanStop; // the limit on the size of the containing cone + // internal parameters + Vec_Ptr_t * vNodeLeaves; // fanins of the collapsed node (the cut) + Vec_Ptr_t * vConeLeaves; // fanins of the containing cone + Vec_Ptr_t * vVisited; // the visited nodes + Vec_Vec_t * vLevels; // the data structure to compute TFO nodes + Vec_Ptr_t * vNodesTfo; // the nodes in the TFO of the cut +}; + +static int Abc_NodeBuildCutLevelOne_int( Vec_Ptr_t * vVisited, Vec_Ptr_t * vLeaves, int nSizeLimit, int nFaninLimit ); +static int Abc_NodeBuildCutLevelTwo_int( Vec_Ptr_t * vVisited, Vec_Ptr_t * vLeaves, int nFaninLimit ); +static void Abc_NodeConeMarkCollect_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vVisited ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Unmarks the TFI cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Abc_NodesMark( Vec_Ptr_t * vVisited ) +{ + Abc_Obj_t * pNode; + int i; + Vec_PtrForEachEntry( vVisited, pNode, i ) + pNode->fMarkA = 1; +} + +/**Function************************************************************* + + Synopsis [Unmarks the TFI cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Abc_NodesUnmark( Vec_Ptr_t * vVisited ) +{ + Abc_Obj_t * pNode; + int i; + Vec_PtrForEachEntry( vVisited, pNode, i ) + pNode->fMarkA = 0; +} + +/**Function************************************************************* + + Synopsis [Unmarks the TFI cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Abc_NodesUnmarkB( Vec_Ptr_t * vVisited ) +{ + Abc_Obj_t * pNode; + int i; + Vec_PtrForEachEntry( vVisited, pNode, i ) + pNode->fMarkB = 0; +} + +/**Function************************************************************* + + Synopsis [Evaluate the cost of removing the node from the set of leaves.] + + Description [Returns the number of new leaves that will be brought in. + Returns large number if the node cannot be removed from the set of leaves.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Abc_NodeGetLeafCostOne( Abc_Obj_t * pNode, int nFaninLimit ) +{ + int Cost; + // make sure the node is in the construction zone + assert( pNode->fMarkB == 1 ); + // cannot expand over the PI node + if ( Abc_ObjIsCi(pNode) ) + return 999; + // get the cost of the cone + Cost = (!Abc_ObjFanin0(pNode)->fMarkB) + (!Abc_ObjFanin1(pNode)->fMarkB); + // always accept if the number of leaves does not increase + if ( Cost < 2 ) + return Cost; + // skip nodes with many fanouts + if ( Abc_ObjFanoutNum(pNode) > nFaninLimit ) + return 999; + // return the number of nodes that will be on the leaves if this node is removed + return Cost; +} + +/**Function************************************************************* + + Synopsis [Evaluate the cost of removing the node from the set of leaves.] + + Description [Returns the number of new leaves that will be brought in. + Returns large number if the node cannot be removed from the set of leaves.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Abc_NodeGetLeafCostTwo( Abc_Obj_t * pNode, int nFaninLimit, + Abc_Obj_t ** ppLeafToAdd, Abc_Obj_t ** pNodeToMark1, Abc_Obj_t ** pNodeToMark2 ) +{ + Abc_Obj_t * pFanin0, * pFanin1, * pTemp; + Abc_Obj_t * pGrand, * pGrandToAdd; + // make sure the node is in the construction zone + assert( pNode->fMarkB == 1 ); + // cannot expand over the PI node + if ( Abc_ObjIsCi(pNode) ) + return 999; + // skip nodes with many fanouts +// if ( Abc_ObjFanoutNum(pNode) > nFaninLimit ) +// return 999; + // get the children + pFanin0 = Abc_ObjFanin0(pNode); + pFanin1 = Abc_ObjFanin1(pNode); + assert( !pFanin0->fMarkB && !pFanin1->fMarkB ); + // count the number of unique grandchildren that will be included + // return infinite cost if this number if more than 1 + if ( Abc_ObjIsCi(pFanin0) && Abc_ObjIsCi(pFanin1) ) + return 999; + // consider the special case when a non-CI fanin can be dropped + if ( !Abc_ObjIsCi(pFanin0) && Abc_ObjFanin0(pFanin0)->fMarkB && Abc_ObjFanin1(pFanin0)->fMarkB ) + { + *ppLeafToAdd = pFanin1; + *pNodeToMark1 = pFanin0; + *pNodeToMark2 = NULL; + return 1; + } + if ( !Abc_ObjIsCi(pFanin1) && Abc_ObjFanin0(pFanin1)->fMarkB && Abc_ObjFanin1(pFanin1)->fMarkB ) + { + *ppLeafToAdd = pFanin0; + *pNodeToMark1 = pFanin1; + *pNodeToMark2 = NULL; + return 1; + } + + // make the first node CI if any + if ( Abc_ObjIsCi(pFanin1) ) + pTemp = pFanin0, pFanin0 = pFanin1, pFanin1 = pTemp; + // consider the first node + pGrandToAdd = NULL; + if ( Abc_ObjIsCi(pFanin0) ) + { + *pNodeToMark1 = NULL; + pGrandToAdd = pFanin0; + } + else + { + *pNodeToMark1 = pFanin0; + pGrand = Abc_ObjFanin0(pFanin0); + if ( !pGrand->fMarkB ) + { + if ( pGrandToAdd && pGrandToAdd != pGrand ) + return 999; + pGrandToAdd = pGrand; + } + pGrand = Abc_ObjFanin1(pFanin0); + if ( !pGrand->fMarkB ) + { + if ( pGrandToAdd && pGrandToAdd != pGrand ) + return 999; + pGrandToAdd = pGrand; + } + } + // consider the second node + *pNodeToMark2 = pFanin1; + pGrand = Abc_ObjFanin0(pFanin1); + if ( !pGrand->fMarkB ) + { + if ( pGrandToAdd && pGrandToAdd != pGrand ) + return 999; + pGrandToAdd = pGrand; + } + pGrand = Abc_ObjFanin1(pFanin1); + if ( !pGrand->fMarkB ) + { + if ( pGrandToAdd && pGrandToAdd != pGrand ) + return 999; + pGrandToAdd = pGrand; + } + assert( pGrandToAdd != NULL ); + *ppLeafToAdd = pGrandToAdd; + return 1; +} + + +/**Function************************************************************* + + Synopsis [Finds a fanin-limited, reconvergence-driven cut for the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NodeFindCut( Abc_ManCut_t * p, Abc_Obj_t * pRoot, bool fContain ) +{ + Abc_Obj_t * pNode; + int i; + + assert( !Abc_ObjIsComplement(pRoot) ); + assert( Abc_ObjIsNode(pRoot) ); + + // start the visited nodes and mark them + Vec_PtrClear( p->vVisited ); + Vec_PtrPush( p->vVisited, pRoot ); + Vec_PtrPush( p->vVisited, Abc_ObjFanin0(pRoot) ); + Vec_PtrPush( p->vVisited, Abc_ObjFanin1(pRoot) ); + pRoot->fMarkB = 1; + Abc_ObjFanin0(pRoot)->fMarkB = 1; + Abc_ObjFanin1(pRoot)->fMarkB = 1; + + // start the cut + Vec_PtrClear( p->vNodeLeaves ); + Vec_PtrPush( p->vNodeLeaves, Abc_ObjFanin0(pRoot) ); + Vec_PtrPush( p->vNodeLeaves, Abc_ObjFanin1(pRoot) ); + + // compute the cut + while ( Abc_NodeBuildCutLevelOne_int( p->vVisited, p->vNodeLeaves, p->nNodeSizeMax, p->nNodeFanStop ) ); + assert( Vec_PtrSize(p->vNodeLeaves) <= p->nNodeSizeMax ); + + // return if containing cut is not requested + if ( !fContain ) + { + // unmark both fMarkA and fMarkB in tbe TFI + Abc_NodesUnmarkB( p->vVisited ); + return p->vNodeLeaves; + } + +//printf( "\n\n\n" ); + // compute the containing cut + assert( p->nNodeSizeMax < p->nConeSizeMax ); + // copy the current boundary + Vec_PtrClear( p->vConeLeaves ); + Vec_PtrForEachEntry( p->vNodeLeaves, pNode, i ) + Vec_PtrPush( p->vConeLeaves, pNode ); + // compute the containing cut + while ( Abc_NodeBuildCutLevelOne_int( p->vVisited, p->vConeLeaves, p->nConeSizeMax, p->nConeFanStop ) ); + assert( Vec_PtrSize(p->vConeLeaves) <= p->nConeSizeMax ); + // unmark TFI using fMarkA and fMarkB + Abc_NodesUnmarkB( p->vVisited ); + return p->vNodeLeaves; +} + +/**Function************************************************************* + + Synopsis [Builds reconvergence-driven cut by changing one leaf at a time.] + + Description [This procedure looks at the current leaves and tries to change + one leaf at a time in such a way that the cut grows as little as possible. + In evaluating the fanins, this procedure looks only at their immediate + predecessors (this is why it is called a one-level construction procedure).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeBuildCutLevelOne_int( Vec_Ptr_t * vVisited, Vec_Ptr_t * vLeaves, int nSizeLimit, int nFaninLimit ) +{ + Abc_Obj_t * pNode, * pFaninBest, * pNext; + int CostBest, CostCur, i; + // find the best fanin + CostBest = 100; + pFaninBest = NULL; +//printf( "Evaluating fanins of the cut:\n" ); + Vec_PtrForEachEntry( vLeaves, pNode, i ) + { + CostCur = Abc_NodeGetLeafCostOne( pNode, nFaninLimit ); +//printf( " Fanin %s has cost %d.\n", Abc_ObjName(pNode), CostCur ); +// if ( CostBest > CostCur ) // performance improvement: expand the variable with the smallest level + if ( CostBest > CostCur || + (CostBest == CostCur && pNode->Level > pFaninBest->Level) ) + { + CostBest = CostCur; + pFaninBest = pNode; + } + if ( CostBest == 0 ) + break; + } + if ( pFaninBest == NULL ) + return 0; +// return Abc_NodeBuildCutLevelTwo_int( vVisited, vLeaves, nFaninLimit ); + + assert( CostBest < 3 ); + if ( vLeaves->nSize - 1 + CostBest > nSizeLimit ) + return 0; +// return Abc_NodeBuildCutLevelTwo_int( vVisited, vLeaves, nFaninLimit ); + + assert( Abc_ObjIsNode(pFaninBest) ); + // remove the node from the array + Vec_PtrRemove( vLeaves, pFaninBest ); +//printf( "Removing fanin %s.\n", Abc_ObjName(pFaninBest) ); + + // add the left child to the fanins + pNext = Abc_ObjFanin0(pFaninBest); + if ( !pNext->fMarkB ) + { +//printf( "Adding fanin %s.\n", Abc_ObjName(pNext) ); + pNext->fMarkB = 1; + Vec_PtrPush( vLeaves, pNext ); + Vec_PtrPush( vVisited, pNext ); + } + // add the right child to the fanins + pNext = Abc_ObjFanin1(pFaninBest); + if ( !pNext->fMarkB ) + { +//printf( "Adding fanin %s.\n", Abc_ObjName(pNext) ); + pNext->fMarkB = 1; + Vec_PtrPush( vLeaves, pNext ); + Vec_PtrPush( vVisited, pNext ); + } + assert( vLeaves->nSize <= nSizeLimit ); + // keep doing this + return 1; +} + +/**Function************************************************************* + + Synopsis [Builds reconvergence-driven cut by changing one leaf at a time.] + + Description [This procedure looks at the current leaves and tries to change + one leaf at a time in such a way that the cut grows as little as possible. + In evaluating the fanins, this procedure looks across two levels of fanins + (this is why it is called a two-level construction procedure).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeBuildCutLevelTwo_int( Vec_Ptr_t * vVisited, Vec_Ptr_t * vLeaves, int nFaninLimit ) +{ + Abc_Obj_t * pNode, * pLeafToAdd, * pNodeToMark1, * pNodeToMark2; + int CostCur, i; + // find the best fanin + Vec_PtrForEachEntry( vLeaves, pNode, i ) + { + CostCur = Abc_NodeGetLeafCostTwo( pNode, nFaninLimit, &pLeafToAdd, &pNodeToMark1, &pNodeToMark2 ); + if ( CostCur < 2 ) + break; + } + if ( CostCur > 2 ) + return 0; + // remove the node from the array + Vec_PtrRemove( vLeaves, pNode ); + // add the node to the leaves + if ( pLeafToAdd ) + { + assert( !pLeafToAdd->fMarkB ); + pLeafToAdd->fMarkB = 1; + Vec_PtrPush( vLeaves, pLeafToAdd ); + Vec_PtrPush( vVisited, pLeafToAdd ); + } + // mark the other nodes + if ( pNodeToMark1 ) + { + assert( !pNodeToMark1->fMarkB ); + pNodeToMark1->fMarkB = 1; + Vec_PtrPush( vVisited, pNodeToMark1 ); + } + if ( pNodeToMark2 ) + { + assert( !pNodeToMark2->fMarkB ); + pNodeToMark2->fMarkB = 1; + Vec_PtrPush( vVisited, pNodeToMark2 ); + } + // keep doing this + return 1; +} + + +/**Function************************************************************* + + Synopsis [Get the nodes contained in the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeConeCollect( Abc_Obj_t ** ppRoots, int nRoots, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vVisited, int fIncludeFanins ) +{ + Abc_Obj_t * pTemp; + int i; + // mark the fanins of the cone + Abc_NodesMark( vLeaves ); + // collect the nodes in the DFS order + Vec_PtrClear( vVisited ); + // add the fanins + if ( fIncludeFanins ) + Vec_PtrForEachEntry( vLeaves, pTemp, i ) + Vec_PtrPush( vVisited, pTemp ); + // add other nodes + for ( i = 0; i < nRoots; i++ ) + Abc_NodeConeMarkCollect_rec( ppRoots[i], vVisited ); + // unmark both sets + Abc_NodesUnmark( vLeaves ); + Abc_NodesUnmark( vVisited ); +} + +/**Function************************************************************* + + Synopsis [Marks the TFI cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeConeMarkCollect_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vVisited ) +{ + if ( pNode->fMarkA == 1 ) + return; + // visit transitive fanin + if ( Abc_ObjIsNode(pNode) ) + { + Abc_NodeConeMarkCollect_rec( Abc_ObjFanin0(pNode), vVisited ); + Abc_NodeConeMarkCollect_rec( Abc_ObjFanin1(pNode), vVisited ); + } + assert( pNode->fMarkA == 0 ); + pNode->fMarkA = 1; + Vec_PtrPush( vVisited, pNode ); +} + +/**Function************************************************************* + + Synopsis [Returns BDD representing the logic function of the cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Abc_NodeConeBdd( DdManager * dd, DdNode ** pbVars, Abc_Obj_t * pRoot, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vVisited ) +{ + Abc_Obj_t * pNode; + DdNode * bFunc0, * bFunc1, * bFunc; + int i; + // get the nodes in the cut without fanins in the DFS order + Abc_NodeConeCollect( &pRoot, 1, vLeaves, vVisited, 0 ); + // set the elementary BDDs + Vec_PtrForEachEntry( vLeaves, pNode, i ) + pNode->pCopy = (Abc_Obj_t *)pbVars[i]; + // compute the BDDs for the collected nodes + Vec_PtrForEachEntry( vVisited, pNode, i ) + { + assert( !Abc_ObjIsPi(pNode) ); + bFunc0 = Cudd_NotCond( Abc_ObjFanin0(pNode)->pCopy, Abc_ObjFaninC0(pNode) ); + bFunc1 = Cudd_NotCond( Abc_ObjFanin1(pNode)->pCopy, Abc_ObjFaninC1(pNode) ); + bFunc = Cudd_bddAnd( dd, bFunc0, bFunc1 ); Cudd_Ref( bFunc ); + pNode->pCopy = (Abc_Obj_t *)bFunc; + } + Cudd_Ref( bFunc ); + // dereference the intermediate ones + Vec_PtrForEachEntry( vVisited, pNode, i ) + Cudd_RecursiveDeref( dd, (DdNode *)pNode->pCopy ); + Cudd_Deref( bFunc ); + return bFunc; +} + +/**Function************************************************************* + + Synopsis [Returns BDD representing the transition relation of the cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Abc_NodeConeDcs( DdManager * dd, DdNode ** pbVarsX, DdNode ** pbVarsY, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vRoots, Vec_Ptr_t * vVisited ) +{ + DdNode * bFunc0, * bFunc1, * bFunc, * bTrans, * bTemp, * bCube, * bResult; + Abc_Obj_t * pNode; + int i; + // get the nodes in the cut without fanins in the DFS order + Abc_NodeConeCollect( (Abc_Obj_t **)vRoots->pArray, vRoots->nSize, vLeaves, vVisited, 0 ); + // set the elementary BDDs + Vec_PtrForEachEntry( vLeaves, pNode, i ) + pNode->pCopy = (Abc_Obj_t *)pbVarsX[i]; + // compute the BDDs for the collected nodes + Vec_PtrForEachEntry( vVisited, pNode, i ) + { + bFunc0 = Cudd_NotCond( Abc_ObjFanin0(pNode)->pCopy, Abc_ObjFaninC0(pNode) ); + bFunc1 = Cudd_NotCond( Abc_ObjFanin1(pNode)->pCopy, Abc_ObjFaninC1(pNode) ); + bFunc = Cudd_bddAnd( dd, bFunc0, bFunc1 ); Cudd_Ref( bFunc ); + pNode->pCopy = (Abc_Obj_t *)bFunc; + } + // compute the transition relation of the cone + bTrans = b1; Cudd_Ref( bTrans ); + Vec_PtrForEachEntry( vRoots, pNode, i ) + { + bFunc = Cudd_bddXnor( dd, (DdNode *)pNode->pCopy, pbVarsY[i] ); Cudd_Ref( bFunc ); + bTrans = Cudd_bddAnd( dd, bTemp = bTrans, bFunc ); Cudd_Ref( bTrans ); + Cudd_RecursiveDeref( dd, bTemp ); + Cudd_RecursiveDeref( dd, bFunc ); + } + // dereference the intermediate ones + Vec_PtrForEachEntry( vVisited, pNode, i ) + Cudd_RecursiveDeref( dd, (DdNode *)pNode->pCopy ); + // compute don't-cares + bCube = Extra_bddComputeRangeCube( dd, vRoots->nSize, vRoots->nSize + vLeaves->nSize ); Cudd_Ref( bCube ); + bResult = Cudd_bddExistAbstract( dd, bTrans, bCube ); Cudd_Ref( bResult ); + bResult = Cudd_Not( bResult ); + Cudd_RecursiveDeref( dd, bCube ); + Cudd_RecursiveDeref( dd, bTrans ); + Cudd_Deref( bResult ); + return bResult; +} + +/**Function************************************************************* + + Synopsis [Starts the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_ManCut_t * Abc_NtkManCutStart( int nNodeSizeMax, int nConeSizeMax, int nNodeFanStop, int nConeFanStop ) +{ + Abc_ManCut_t * p; + p = ALLOC( Abc_ManCut_t, 1 ); + memset( p, 0, sizeof(Abc_ManCut_t) ); + p->vNodeLeaves = Vec_PtrAlloc( 100 ); + p->vConeLeaves = Vec_PtrAlloc( 100 ); + p->vVisited = Vec_PtrAlloc( 100 ); + p->vLevels = Vec_VecAlloc( 100 ); + p->vNodesTfo = Vec_PtrAlloc( 100 ); + p->nNodeSizeMax = nNodeSizeMax; + p->nConeSizeMax = nConeSizeMax; + p->nNodeFanStop = nNodeFanStop; + p->nConeFanStop = nConeFanStop; + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkManCutStop( Abc_ManCut_t * p ) +{ + Vec_PtrFree( p->vNodeLeaves ); + Vec_PtrFree( p->vConeLeaves ); + Vec_PtrFree( p->vVisited ); + Vec_VecFree( p->vLevels ); + Vec_PtrFree( p->vNodesTfo ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Returns the leaves of the cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkManCutReadCutLarge( Abc_ManCut_t * p ) +{ + return p->vConeLeaves; +} + +/**Function************************************************************* + + Synopsis [Returns the leaves of the cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkManCutReadCutSmall( Abc_ManCut_t * p ) +{ + return p->vNodeLeaves; +} + +/**Function************************************************************* + + Synopsis [Returns the leaves of the cone.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkManCutReadVisited( Abc_ManCut_t * p ) +{ + return p->vVisited; +} + + + +/**Function************************************************************* + + Synopsis [Collects the TFO of the cut in the topological order.] + + Description [TFO of the cut is defined as a set of nodes, for which the cut + is a cut, that is, every path from the collected nodes to the CIs goes through + a node in the cut. The nodes are collected if their level does not exceed + the given number (LevelMax). The nodes are returned in the topological order. + If the root node is given, its MFFC is marked, so that the collected nodes + do not contain any nodes in the MFFC.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NodeCollectTfoCands( Abc_ManCut_t * p, Abc_Obj_t * pRoot, Vec_Ptr_t * vLeaves, int LevelMax ) +{ + Abc_Ntk_t * pNtk = pRoot->pNtk; + Vec_Ptr_t * vVec; + Abc_Obj_t * pNode, * pFanout; + int i, k, v, LevelMin; + assert( Abc_NtkIsStrash(pNtk) ); + + // assuming that the structure is clean + Vec_VecForEachLevel( p->vLevels, vVec, i ) + assert( vVec->nSize == 0 ); + + // put fanins into the structure while labeling them + Abc_NtkIncrementTravId( pNtk ); + LevelMin = -1; + Vec_PtrForEachEntry( vLeaves, pNode, i ) + { + if ( pNode->Level > (unsigned)LevelMax ) + continue; + Abc_NodeSetTravIdCurrent( pNode ); + Vec_VecPush( p->vLevels, pNode->Level, pNode ); + if ( LevelMin < (int)pNode->Level ) + LevelMin = pNode->Level; + } + assert( LevelMin >= 0 ); + + // mark MFFC + if ( pRoot ) + Abc_NodeMffcLabelAig( pRoot ); + + // go through the levels up + Vec_PtrClear( p->vNodesTfo ); + Vec_VecForEachEntryStart( p->vLevels, pNode, i, k, LevelMin ) + { + if ( i > LevelMax ) + break; + // if the node is not marked, it is not a fanin + if ( !Abc_NodeIsTravIdCurrent(pNode) ) + { + // check if it belongs to the TFO + if ( !Abc_NodeIsTravIdCurrent(Abc_ObjFanin0(pNode)) || + !Abc_NodeIsTravIdCurrent(Abc_ObjFanin1(pNode)) ) + continue; + // save the node in the TFO and label it + Vec_PtrPush( p->vNodesTfo, pNode ); + Abc_NodeSetTravIdCurrent( pNode ); + } + // go through the fanouts and add them to the structure if they meet the conditions + Abc_ObjForEachFanout( pNode, pFanout, v ) + { + // skip if fanout is a CO or its level exceeds + if ( Abc_ObjIsCo(pFanout) || pFanout->Level > (unsigned)LevelMax ) + continue; + // skip if it is already added or if it is in MFFC + if ( Abc_NodeIsTravIdCurrent(pFanout) ) + continue; + // add it to the structure but do not mark it (until tested later) + Vec_VecPushUnique( p->vLevels, pFanout->Level, pFanout ); + } + } + + // clear the levelized structure + Vec_VecForEachLevelStart( p->vLevels, vVec, i, LevelMin ) + { + if ( i > LevelMax ) + break; + Vec_PtrClear( vVec ); + } + return p->vNodesTfo; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcRefactor.c b/abc_with_bb_support/src/base/abci/abcRefactor.c new file mode 100644 index 000000000..1b66fc07d --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcRefactor.c @@ -0,0 +1,379 @@ +/**CFile**************************************************************** + + FileName [abcRefactor.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Resynthesis based on collapsing and refactoring.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcRefactor.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "dec.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Abc_ManRef_t_ Abc_ManRef_t; +struct Abc_ManRef_t_ +{ + // user specified parameters + int nNodeSizeMax; // the limit on the size of the supernode + int nConeSizeMax; // the limit on the size of the containing cone + int fVerbose; // the verbosity flag + // internal data structures + DdManager * dd; // the BDD manager + Vec_Str_t * vCube; // temporary + Vec_Int_t * vForm; // temporary + Vec_Ptr_t * vVisited; // temporary + Vec_Ptr_t * vLeaves; // temporary + // node statistics + int nLastGain; + int nNodesConsidered; + int nNodesRefactored; + int nNodesGained; + int nNodesBeg; + int nNodesEnd; + // runtime statistics + int timeCut; + int timeBdd; + int timeDcs; + int timeSop; + int timeFact; + int timeEval; + int timeRes; + int timeNtk; + int timeTotal; +}; + +static void Abc_NtkManRefPrintStats( Abc_ManRef_t * p ); +static Abc_ManRef_t * Abc_NtkManRefStart( int nNodeSizeMax, int nConeSizeMax, bool fUseDcs, bool fVerbose ); +static void Abc_NtkManRefStop( Abc_ManRef_t * p ); +static Dec_Graph_t * Abc_NodeRefactor( Abc_ManRef_t * p, Abc_Obj_t * pNode, Vec_Ptr_t * vFanins, bool fUpdateLevel, bool fUseZeros, bool fUseDcs, bool fVerbose ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs incremental resynthesis of the AIG.] + + Description [Starting from each node, computes a reconvergence-driven cut, + derives BDD of the cut function, constructs ISOP, factors the ISOP, + and replaces the current implementation of the MFFC of the node by the + new factored form, if the number of AIG nodes is reduced and the total + number of levels of the AIG network is not increated. Returns the + number of AIG nodes saved.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRefactor( Abc_Ntk_t * pNtk, int nNodeSizeMax, int nConeSizeMax, bool fUpdateLevel, bool fUseZeros, bool fUseDcs, bool fVerbose ) +{ + ProgressBar * pProgress; + Abc_ManRef_t * pManRef; + Abc_ManCut_t * pManCut; + Dec_Graph_t * pFForm; + Vec_Ptr_t * vFanins; + Abc_Obj_t * pNode; + int clk, clkStart = clock(); + int i, nNodes; + + assert( Abc_NtkIsStrash(pNtk) ); + // cleanup the AIG + Abc_AigCleanup(pNtk->pManFunc); + // start the managers + pManCut = Abc_NtkManCutStart( nNodeSizeMax, nConeSizeMax, 2, 1000 ); + pManRef = Abc_NtkManRefStart( nNodeSizeMax, nConeSizeMax, fUseDcs, fVerbose ); + pManRef->vLeaves = Abc_NtkManCutReadCutLarge( pManCut ); + // compute the reverse levels if level update is requested + if ( fUpdateLevel ) + Abc_NtkStartReverseLevels( pNtk, 0 ); + + // resynthesize each node once + pManRef->nNodesBeg = Abc_NtkNodeNum(pNtk); + nNodes = Abc_NtkObjNumMax(pNtk); + pProgress = Extra_ProgressBarStart( stdout, nNodes ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + // skip the constant node +// if ( Abc_NodeIsConst(pNode) ) +// continue; + // skip persistant nodes + if ( Abc_NodeIsPersistant(pNode) ) + continue; + // skip the nodes with many fanouts + if ( Abc_ObjFanoutNum(pNode) > 1000 ) + continue; + // stop if all nodes have been tried once + if ( i >= nNodes ) + break; + // compute a reconvergence-driven cut +clk = clock(); + vFanins = Abc_NodeFindCut( pManCut, pNode, fUseDcs ); +pManRef->timeCut += clock() - clk; + // evaluate this cut +clk = clock(); + pFForm = Abc_NodeRefactor( pManRef, pNode, vFanins, fUpdateLevel, fUseZeros, fUseDcs, fVerbose ); +pManRef->timeRes += clock() - clk; + if ( pFForm == NULL ) + continue; + // acceptable replacement found, update the graph +clk = clock(); + Dec_GraphUpdateNetwork( pNode, pFForm, fUpdateLevel, pManRef->nLastGain ); +pManRef->timeNtk += clock() - clk; + Dec_GraphFree( pFForm ); +// { +// extern int s_TotalChanges; +// s_TotalChanges++; +// } + } + Extra_ProgressBarStop( pProgress ); +pManRef->timeTotal = clock() - clkStart; + pManRef->nNodesEnd = Abc_NtkNodeNum(pNtk); + + // print statistics of the manager + if ( fVerbose ) + Abc_NtkManRefPrintStats( pManRef ); + // delete the managers + Abc_NtkManCutStop( pManCut ); + Abc_NtkManRefStop( pManRef ); + // put the nodes into the DFS order and reassign their IDs + Abc_NtkReassignIds( pNtk ); +// Abc_AigCheckFaninOrder( pNtk->pManFunc ); + // fix the levels + if ( fUpdateLevel ) + Abc_NtkStopReverseLevels( pNtk ); + else + Abc_NtkLevel( pNtk ); + // check + if ( !Abc_NtkCheck( pNtk ) ) + { + printf( "Abc_NtkRefactor: The network check has failed.\n" ); + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Resynthesizes the node using refactoring.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_NodeRefactor( Abc_ManRef_t * p, Abc_Obj_t * pNode, Vec_Ptr_t * vFanins, bool fUpdateLevel, bool fUseZeros, bool fUseDcs, bool fVerbose ) +{ + int fVeryVerbose = 0; + Abc_Obj_t * pFanin; + Dec_Graph_t * pFForm; + DdNode * bNodeFunc; + int nNodesSaved, nNodesAdded, i, clk; + char * pSop; + int Required; + + Required = fUpdateLevel? Abc_ObjRequiredLevel(pNode) : ABC_INFINITY; + + p->nNodesConsidered++; + + // get the function of the cut +clk = clock(); + bNodeFunc = Abc_NodeConeBdd( p->dd, p->dd->vars, pNode, vFanins, p->vVisited ); Cudd_Ref( bNodeFunc ); +p->timeBdd += clock() - clk; + + // if don't-care are used, transform the function into ISOP + if ( fUseDcs ) + { + DdNode * bNodeDc, * bNodeOn, * bNodeOnDc; + int nMints, nMintsDc; +clk = clock(); + // get the don't-cares + bNodeDc = Abc_NodeConeDcs( p->dd, p->dd->vars + vFanins->nSize, p->dd->vars, p->vLeaves, vFanins, p->vVisited ); Cudd_Ref( bNodeDc ); + nMints = (1 << vFanins->nSize); + nMintsDc = (int)Cudd_CountMinterm( p->dd, bNodeDc, vFanins->nSize ); +// printf( "Percentage of minterms = %5.2f.\n", 100.0 * nMintsDc / nMints ); + // get the ISF + bNodeOn = Cudd_bddAnd( p->dd, bNodeFunc, Cudd_Not(bNodeDc) ); Cudd_Ref( bNodeOn ); + bNodeOnDc = Cudd_bddOr ( p->dd, bNodeFunc, bNodeDc ); Cudd_Ref( bNodeOnDc ); + Cudd_RecursiveDeref( p->dd, bNodeFunc ); + Cudd_RecursiveDeref( p->dd, bNodeDc ); + // get the ISOP + bNodeFunc = Cudd_bddIsop( p->dd, bNodeOn, bNodeOnDc ); Cudd_Ref( bNodeFunc ); + Cudd_RecursiveDeref( p->dd, bNodeOn ); + Cudd_RecursiveDeref( p->dd, bNodeOnDc ); +p->timeDcs += clock() - clk; + } + + // always accept the case of constant node + if ( Cudd_IsConstant(bNodeFunc) ) + { + p->nLastGain = Abc_NodeMffcSize( pNode ); + p->nNodesGained += p->nLastGain; + p->nNodesRefactored++; + Cudd_RecursiveDeref( p->dd, bNodeFunc ); + if ( Cudd_IsComplement(bNodeFunc) ) + return Dec_GraphCreateConst0(); + return Dec_GraphCreateConst1(); + } + + // get the SOP of the cut +clk = clock(); + pSop = Abc_ConvertBddToSop( NULL, p->dd, bNodeFunc, bNodeFunc, vFanins->nSize, 0, p->vCube, -1 ); +p->timeSop += clock() - clk; + + // get the factored form +clk = clock(); + pFForm = Dec_Factor( pSop ); + free( pSop ); +p->timeFact += clock() - clk; + + // mark the fanin boundary + // (can mark only essential fanins, belonging to bNodeFunc!) + Vec_PtrForEachEntry( vFanins, pFanin, i ) + pFanin->vFanouts.nSize++; + // label MFFC with current traversal ID + Abc_NtkIncrementTravId( pNode->pNtk ); + nNodesSaved = Abc_NodeMffcLabelAig( pNode ); + // unmark the fanin boundary and set the fanins as leaves in the form + Vec_PtrForEachEntry( vFanins, pFanin, i ) + { + pFanin->vFanouts.nSize--; + Dec_GraphNode(pFForm, i)->pFunc = pFanin; + } + + // detect how many new nodes will be added (while taking into account reused nodes) +clk = clock(); + nNodesAdded = Dec_GraphToNetworkCount( pNode, pFForm, nNodesSaved, Required ); +p->timeEval += clock() - clk; + // quit if there is no improvement + if ( nNodesAdded == -1 || nNodesAdded == nNodesSaved && !fUseZeros ) + { + Cudd_RecursiveDeref( p->dd, bNodeFunc ); + Dec_GraphFree( pFForm ); + return NULL; + } + + // compute the total gain in the number of nodes + p->nLastGain = nNodesSaved - nNodesAdded; + p->nNodesGained += p->nLastGain; + p->nNodesRefactored++; + + // report the progress + if ( fVeryVerbose ) + { + printf( "Node %6s : ", Abc_ObjName(pNode) ); + printf( "Cone = %2d. ", vFanins->nSize ); + printf( "BDD = %2d. ", Cudd_DagSize(bNodeFunc) ); + printf( "FF = %2d. ", 1 + Dec_GraphNodeNum(pFForm) ); + printf( "MFFC = %2d. ", nNodesSaved ); + printf( "Add = %2d. ", nNodesAdded ); + printf( "GAIN = %2d. ", p->nLastGain ); + printf( "\n" ); + } + Cudd_RecursiveDeref( p->dd, bNodeFunc ); + return pFForm; +} + + +/**Function************************************************************* + + Synopsis [Starts the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_ManRef_t * Abc_NtkManRefStart( int nNodeSizeMax, int nConeSizeMax, bool fUseDcs, bool fVerbose ) +{ + Abc_ManRef_t * p; + p = ALLOC( Abc_ManRef_t, 1 ); + memset( p, 0, sizeof(Abc_ManRef_t) ); + p->vCube = Vec_StrAlloc( 100 ); + p->vVisited = Vec_PtrAlloc( 100 ); + p->nNodeSizeMax = nNodeSizeMax; + p->nConeSizeMax = nConeSizeMax; + p->fVerbose = fVerbose; + // start the BDD manager + if ( fUseDcs ) + p->dd = Cudd_Init( p->nNodeSizeMax + p->nConeSizeMax, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 ); + else + p->dd = Cudd_Init( p->nNodeSizeMax, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 ); + Cudd_zddVarsFromBddVars( p->dd, 2 ); + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkManRefStop( Abc_ManRef_t * p ) +{ + Extra_StopManager( p->dd ); + Vec_PtrFree( p->vVisited ); + Vec_StrFree( p->vCube ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Stops the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkManRefPrintStats( Abc_ManRef_t * p ) +{ + printf( "Refactoring statistics:\n" ); + printf( "Nodes considered = %8d.\n", p->nNodesConsidered ); + printf( "Nodes refactored = %8d.\n", p->nNodesRefactored ); + printf( "Gain = %8d. (%6.2f %%).\n", p->nNodesBeg-p->nNodesEnd, 100.0*(p->nNodesBeg-p->nNodesEnd)/p->nNodesBeg ); + PRT( "Cuts ", p->timeCut ); + PRT( "Resynthesis", p->timeRes ); + PRT( " BDD ", p->timeBdd ); + PRT( " DCs ", p->timeDcs ); + PRT( " SOP ", p->timeSop ); + PRT( " FF ", p->timeFact ); + PRT( " Eval ", p->timeEval ); + PRT( "AIG update ", p->timeNtk ); + PRT( "TOTAL ", p->timeTotal ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcRenode.c b/abc_with_bb_support/src/base/abci/abcRenode.c new file mode 100644 index 000000000..09ac8aaa4 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcRenode.c @@ -0,0 +1,311 @@ +/**CFile**************************************************************** + + FileName [abcRenode.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcRenode.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "reo.h" +#include "if.h" +#include "kit.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Abc_NtkRenodeEvalAig( If_Cut_t * pCut ); +static int Abc_NtkRenodeEvalBdd( If_Cut_t * pCut ); +static int Abc_NtkRenodeEvalSop( If_Cut_t * pCut ); +static int Abc_NtkRenodeEvalCnf( If_Cut_t * pCut ); +static int Abc_NtkRenodeEvalMv( If_Cut_t * pCut ); + +static reo_man * s_pReo = NULL; +static DdManager * s_pDd = NULL; +static Vec_Int_t * s_vMemory = NULL; +static Vec_Int_t * s_vMemory2 = NULL; + +static int nDsdCounter = 0; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs renoding as technology mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkRenode( Abc_Ntk_t * pNtk, int nFaninMax, int nCubeMax, int nFlowIters, int nAreaIters, int fArea, int fUseBdds, int fUseSops, int fUseCnfs, int fUseMv, int fVerbose ) +{ + extern Abc_Ntk_t * Abc_NtkIf( Abc_Ntk_t * pNtk, If_Par_t * pPars ); + If_Par_t Pars, * pPars = &Pars; + Abc_Ntk_t * pNtkNew; + + if ( Abc_NtkGetChoiceNum( pNtk ) ) + printf( "Performing renoding with choices.\n" ); + + nDsdCounter = 0; + + // set defaults + memset( pPars, 0, sizeof(If_Par_t) ); + // user-controlable paramters + pPars->nLutSize = nFaninMax; + pPars->nCutsMax = nCubeMax; + pPars->nFlowIters = nFlowIters; + pPars->nAreaIters = nAreaIters; + pPars->DelayTarget = -1; + pPars->fPreprocess = 1; + pPars->fArea = fArea; + pPars->fFancy = 0; + pPars->fExpRed = 0; // + pPars->fLatchPaths = 0; + pPars->fSeqMap = 0; + pPars->fVerbose = fVerbose; + // internal parameters + pPars->fTruth = 1; + pPars->fUsePerm = 1; + pPars->nLatches = 0; + pPars->pLutLib = NULL; // Abc_FrameReadLibLut(); + pPars->pTimesArr = NULL; + pPars->pTimesArr = NULL; + pPars->fUseBdds = fUseBdds; + pPars->fUseSops = fUseSops; + pPars->fUseCnfs = fUseCnfs; + pPars->fUseMv = fUseMv; + if ( fUseBdds ) + pPars->pFuncCost = Abc_NtkRenodeEvalBdd; + else if ( fUseSops ) + pPars->pFuncCost = Abc_NtkRenodeEvalSop; + else if ( fUseCnfs ) + { + pPars->fArea = 1; + pPars->pFuncCost = Abc_NtkRenodeEvalCnf; + } + else if ( fUseMv ) + pPars->pFuncCost = Abc_NtkRenodeEvalMv; + else + pPars->pFuncCost = Abc_NtkRenodeEvalAig; + + // start the manager + if ( fUseBdds ) + { + assert( s_pReo == NULL ); + s_pDd = Cudd_Init( nFaninMax, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 ); + s_pReo = Extra_ReorderInit( nFaninMax, 100 ); + pPars->pReoMan = s_pReo; + } + else + { + assert( s_vMemory == NULL ); + s_vMemory = Vec_IntAlloc( 1 << 16 ); + s_vMemory2 = Vec_IntAlloc( 1 << 16 ); + } + + // perform mapping/renoding + pNtkNew = Abc_NtkIf( pNtk, pPars ); + + // start the manager + if ( fUseBdds ) + { + Extra_StopManager( s_pDd ); + Extra_ReorderQuit( s_pReo ); + s_pReo = NULL; + s_pDd = NULL; + } + else + { + Vec_IntFree( s_vMemory ); + Vec_IntFree( s_vMemory2 ); + s_vMemory = NULL; + s_vMemory2 = NULL; + } + +// printf( "Decomposed %d functions.\n", nDsdCounter ); + + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Computes the cost based on the factored form.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRenodeEvalAig( If_Cut_t * pCut ) +{ + Kit_Graph_t * pGraph; + int i, nNodes; +/* +extern void Kit_DsdTest( unsigned * pTruth, int nVars ); +if ( If_CutLeaveNum(pCut) == 8 ) +{ + nDsdCounter++; + Kit_DsdTest( If_CutTruth(pCut), If_CutLeaveNum(pCut) ); +} +*/ + pGraph = Kit_TruthToGraph( If_CutTruth(pCut), If_CutLeaveNum(pCut), s_vMemory ); + if ( pGraph == NULL ) + { + for ( i = 0; i < If_CutLeaveNum(pCut); i++ ) + pCut->pPerm[i] = 100; + return IF_COST_MAX; + } + nNodes = Kit_GraphNodeNum( pGraph ); + for ( i = 0; i < If_CutLeaveNum(pCut); i++ ) + pCut->pPerm[i] = Kit_GraphLeafDepth_rec( pGraph, Kit_GraphNodeLast(pGraph), Kit_GraphNode(pGraph, i) ); + Kit_GraphFree( pGraph ); + return nNodes; +} + +/**Function************************************************************* + + Synopsis [Computes the cost based on the BDD size after reordering.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRenodeEvalBdd( If_Cut_t * pCut ) +{ + int pOrder[IF_MAX_LUTSIZE]; + DdNode * bFunc, * bFuncNew; + int i, k, nNodes; + for ( i = 0; i < If_CutLeaveNum(pCut); i++ ) + pCut->pPerm[i] = pOrder[i] = -100; + bFunc = Kit_TruthToBdd( s_pDd, If_CutTruth(pCut), If_CutLeaveNum(pCut), 0 ); Cudd_Ref( bFunc ); + bFuncNew = Extra_Reorder( s_pReo, s_pDd, bFunc, pOrder ); Cudd_Ref( bFuncNew ); + for ( i = k = 0; i < If_CutLeaveNum(pCut); i++ ) + if ( pOrder[i] >= 0 ) + pCut->pPerm[pOrder[i]] = ++k; // double-check this! + nNodes = -1 + Cudd_DagSize( bFuncNew ); + Cudd_RecursiveDeref( s_pDd, bFuncNew ); + Cudd_RecursiveDeref( s_pDd, bFunc ); + return nNodes; +} + +/**Function************************************************************* + + Synopsis [Computes the cost based on ISOP.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRenodeEvalSop( If_Cut_t * pCut ) +{ + int i, RetValue; + for ( i = 0; i < If_CutLeaveNum(pCut); i++ ) + pCut->pPerm[i] = 1; + RetValue = Kit_TruthIsop( If_CutTruth(pCut), If_CutLeaveNum(pCut), s_vMemory, 1 ); + if ( RetValue == -1 ) + return IF_COST_MAX; + assert( RetValue == 0 || RetValue == 1 ); + return Vec_IntSize( s_vMemory ); +} + +/**Function************************************************************* + + Synopsis [Computes the cost based on two ISOPs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRenodeEvalCnf( If_Cut_t * pCut ) +{ + int i, RetValue, nClauses; + // set internal mapper parameters + for ( i = 0; i < If_CutLeaveNum(pCut); i++ ) + pCut->pPerm[i] = 1; + // compute ISOP for the positive phase + RetValue = Kit_TruthIsop( If_CutTruth(pCut), If_CutLeaveNum(pCut), s_vMemory, 0 ); + if ( RetValue == -1 ) + return IF_COST_MAX; + assert( RetValue == 0 || RetValue == 1 ); + nClauses = Vec_IntSize( s_vMemory ); + // compute ISOP for the negative phase + Kit_TruthNot( If_CutTruth(pCut), If_CutTruth(pCut), If_CutLeaveNum(pCut) ); + RetValue = Kit_TruthIsop( If_CutTruth(pCut), If_CutLeaveNum(pCut), s_vMemory, 0 ); + Kit_TruthNot( If_CutTruth(pCut), If_CutTruth(pCut), If_CutLeaveNum(pCut) ); + if ( RetValue == -1 ) + return IF_COST_MAX; + assert( RetValue == 0 || RetValue == 1 ); + nClauses += Vec_IntSize( s_vMemory ); + return nClauses; +} + +/**Function************************************************************* + + Synopsis [Computes the cost of MV-SOP of the cut function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRenodeEvalMv( If_Cut_t * pCut ) +{ + int i, RetValue; + // set internal mapper parameters + for ( i = 0; i < If_CutLeaveNum(pCut); i++ ) + pCut->pPerm[i] = 1; + // compute ISOP for the positive phase + RetValue = Kit_TruthIsop( If_CutTruth(pCut), If_CutLeaveNum(pCut), s_vMemory, 0 ); + if ( RetValue == -1 ) + return IF_COST_MAX; + assert( RetValue == 0 || RetValue == 1 ); + // compute ISOP for the negative phase + Kit_TruthNot( If_CutTruth(pCut), If_CutTruth(pCut), If_CutLeaveNum(pCut) ); + RetValue = Kit_TruthIsop( If_CutTruth(pCut), If_CutLeaveNum(pCut), s_vMemory2, 0 ); + Kit_TruthNot( If_CutTruth(pCut), If_CutTruth(pCut), If_CutLeaveNum(pCut) ); + if ( RetValue == -1 ) + return IF_COST_MAX; + assert( RetValue == 0 || RetValue == 1 ); + // return the cost of the cut + RetValue = Abc_NodeEvalMvCost( If_CutLeaveNum(pCut), s_vMemory, s_vMemory2 ); + if ( RetValue >= IF_COST_MAX ) + return IF_COST_MAX; + return RetValue; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcReorder.c b/abc_with_bb_support/src/base/abci/abcReorder.c new file mode 100644 index 000000000..c36b7cae7 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcReorder.c @@ -0,0 +1,100 @@ +/**CFile**************************************************************** + + FileName [abcReorder.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Reordering local BDDs of the nodes.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcReorder.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "reo.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Reorders BDD of the local function of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeBddReorder( reo_man * p, Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pFanin; + DdNode * bFunc; + int * pOrder, i; + // create the temporary array for the variable order + pOrder = ALLOC( int, Abc_ObjFaninNum(pNode) ); + for ( i = 0; i < Abc_ObjFaninNum(pNode); i++ ) + pOrder[i] = -1; + // reorder the BDD + bFunc = Extra_Reorder( p, pNode->pNtk->pManFunc, pNode->pData, pOrder ); Cudd_Ref( bFunc ); + Cudd_RecursiveDeref( pNode->pNtk->pManFunc, pNode->pData ); + pNode->pData = bFunc; + // update the fanin order + Abc_ObjForEachFanin( pNode, pFanin, i ) + pOrder[i] = pNode->vFanins.pArray[ pOrder[i] ]; + Abc_ObjForEachFanin( pNode, pFanin, i ) + pNode->vFanins.pArray[i] = pOrder[i]; + free( pOrder ); +} + +/**Function************************************************************* + + Synopsis [Reorders BDDs of the local functions.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkBddReorder( Abc_Ntk_t * pNtk, int fVerbose ) +{ + reo_man * p; + Abc_Obj_t * pNode; + int i; + p = Extra_ReorderInit( Abc_NtkGetFaninMax(pNtk), 100 ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + if ( Abc_ObjFaninNum(pNode) < 3 ) + continue; + if ( fVerbose ) + fprintf( stdout, "%10s: ", Abc_ObjName(pNode) ); + if ( fVerbose ) + fprintf( stdout, "Before = %5d BDD nodes. ", Cudd_DagSize(pNode->pData) ); + Abc_NodeBddReorder( p, pNode ); + if ( fVerbose ) + fprintf( stdout, "After = %5d BDD nodes.\n", Cudd_DagSize(pNode->pData) ); + } + Extra_ReorderQuit( p ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcRestruct.c b/abc_with_bb_support/src/base/abci/abcRestruct.c new file mode 100644 index 000000000..92445ca43 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcRestruct.c @@ -0,0 +1,1496 @@ +/**CFile**************************************************************** + + FileName [abcRestruct.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcRestruct.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "dec.h" +#include "dsd.h" +#include "cut.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define RST_RANDOM_UNSIGNED ((((unsigned)rand()) << 24) ^ (((unsigned)rand()) << 12) ^ ((unsigned)rand())) + +typedef struct Abc_ManRst_t_ Abc_ManRst_t; +struct Abc_ManRst_t_ +{ + // the network + Abc_Ntk_t * pNtk; // the network for restructuring + // user specified parameters + int nCutMax; // the limit on the size of the supernode + int fUpdateLevel; // turns on watching the number of levels + int fUseZeros; // turns on zero-cost replacements + int fVerbose; // the verbosity flag + // internal data structures + DdManager * dd; // the BDD manager + Dsd_Manager_t * pManDsd; // the DSD manager + Vec_Ptr_t * vVisited; // temporary + Vec_Ptr_t * vLeaves; // temporary + Vec_Ptr_t * vDecs; // temporary + Vec_Ptr_t * vTemp; // temporary + Vec_Int_t * vSims; // temporary + Vec_Int_t * vRands; // temporary + Vec_Int_t * vOnes; // temporary + Vec_Int_t * vBinate; // temporary + Vec_Int_t * vTwos; // temporary + // node statistics + int nLastGain; + int nCutsConsidered; + int nCutsExplored; + int nNodesConsidered; + int nNodesRestructured; + int nNodesGained; + // runtime statistics + int timeCut; + int timeBdd; + int timeDsd; + int timeEval; + int timeRes; + int timeNtk; + int timeTotal; +}; + +static Dec_Graph_t * Abc_NodeResubstitute( Abc_ManRst_t * p, Abc_Obj_t * pNode, Cut_Cut_t * pCutList ); + +static Dec_Graph_t * Abc_NodeRestructure( Abc_ManRst_t * p, Abc_Obj_t * pNode, Cut_Cut_t * pCutList ); +static Dec_Graph_t * Abc_NodeRestructureCut( Abc_ManRst_t * p, Abc_Obj_t * pNode, Cut_Cut_t * pCut ); +static Dec_Graph_t * Abc_NodeEvaluateDsd( Abc_ManRst_t * pManRst, Dsd_Node_t * pNodeDsd, Abc_Obj_t * pRoot, int Required, int nNodesSaved, int * pnNodesAdded ); + +static Cut_Man_t * Abc_NtkStartCutManForRestruct( Abc_Ntk_t * pNtk, int nCutMax, int fDag ); +static Abc_ManRst_t * Abc_NtkManRstStart( int nCutMax, bool fUpdateLevel, bool fUseZeros, bool fVerbose ); +static void Abc_NtkManRstStop( Abc_ManRst_t * p ); +static void Abc_NtkManRstPrintStats( Abc_ManRst_t * p ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Implements AIG restructuring.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRestructure( Abc_Ntk_t * pNtk, int nCutMax, bool fUpdateLevel, bool fUseZeros, bool fVerbose ) +{ + ProgressBar * pProgress; + Abc_ManRst_t * pManRst; + Cut_Man_t * pManCut; + Cut_Cut_t * pCutList; + Dec_Graph_t * pGraph; + Abc_Obj_t * pNode; + int clk, clkStart = clock(); + int fMulti = 1; + int fResub = 0; + int i, nNodes; + + assert( Abc_NtkIsStrash(pNtk) ); + // cleanup the AIG + Abc_AigCleanup(pNtk->pManFunc); + Abc_NtkCleanCopy(pNtk); + + // compute the reverse levels if level update is requested + if ( fUpdateLevel ) + Abc_NtkStartReverseLevels( pNtk, 0 ); + + // start the restructuring manager + pManRst = Abc_NtkManRstStart( nCutMax, fUpdateLevel, fUseZeros, fVerbose ); + pManRst->pNtk = pNtk; + // start the cut manager +clk = clock(); + pManCut = Abc_NtkStartCutManForRestruct( pNtk, nCutMax, fMulti ); +pManRst->timeCut += clock() - clk; +// pNtk->pManCut = pManCut; + + // resynthesize each node once + nNodes = Abc_NtkObjNumMax(pNtk); + pProgress = Extra_ProgressBarStart( stdout, nNodes ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + // skip the constant node +// if ( Abc_NodeIsConst(pNode) ) +// continue; + // skip persistant nodes + if ( Abc_NodeIsPersistant(pNode) ) + continue; + // skip the node if it is inside the tree +// if ( Abc_ObjFanoutNum(pNode) < 2 ) +// continue; + // skip the nodes with too many fanouts + if ( Abc_ObjFanoutNum(pNode) > 1000 ) + continue; + // stop if all nodes have been tried once + if ( i >= nNodes ) + break; + // get the cuts for the given node +clk = clock(); + pCutList = Abc_NodeGetCutsRecursive( pManCut, pNode, fMulti, 0 ); +pManRst->timeCut += clock() - clk; + + // perform restructuring +clk = clock(); + if ( fResub ) + pGraph = Abc_NodeResubstitute( pManRst, pNode, pCutList ); + else + pGraph = Abc_NodeRestructure( pManRst, pNode, pCutList ); +pManRst->timeRes += clock() - clk; + if ( pGraph == NULL ) + continue; + + // acceptable replacement found, update the graph +clk = clock(); + Dec_GraphUpdateNetwork( pNode, pGraph, fUpdateLevel, pManRst->nLastGain ); +pManRst->timeNtk += clock() - clk; + Dec_GraphFree( pGraph ); + } + Extra_ProgressBarStop( pProgress ); +pManRst->timeTotal = clock() - clkStart; + + // print statistics of the manager +// if ( fVerbose ) + Abc_NtkManRstPrintStats( pManRst ); + // delete the managers + Cut_ManStop( pManCut ); + Abc_NtkManRstStop( pManRst ); + // put the nodes into the DFS order and reassign their IDs + Abc_NtkReassignIds( pNtk ); +// Abc_AigCheckFaninOrder( pNtk->pManFunc ); + // fix the levels + if ( fUpdateLevel ) + Abc_NtkStopReverseLevels( pNtk ); + else + Abc_NtkLevel( pNtk ); + // check + if ( !Abc_NtkCheck( pNtk ) ) + { + printf( "Abc_NtkRefactor: The network check has failed.\n" ); + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_RestructNodeDivisors( Abc_ManRst_t * p, Abc_Obj_t * pRoot, int nNodesSaved ) +{ + Abc_Obj_t * pNode, * pFanout;//, * pFanin; + int i, k; + // start with the leaves + Vec_PtrClear( p->vDecs ); + Vec_PtrForEachEntry( p->vLeaves, pNode, i ) + { + Vec_PtrPush( p->vDecs, pNode ); + assert( pNode->fMarkC == 0 ); + pNode->fMarkC = 1; + } + // explore the fanouts + Vec_PtrForEachEntry( p->vDecs, pNode, i ) + { + // if the fanout has both fanins in the set, add it + Abc_ObjForEachFanout( pNode, pFanout, k ) + { + if ( pFanout->fMarkC || Abc_ObjIsPo(pFanout) ) + continue; + if ( Abc_ObjFanin0(pFanout)->fMarkC && Abc_ObjFanin1(pFanout)->fMarkC ) + { + Vec_PtrPush( p->vDecs, pFanout ); + pFanout->fMarkC = 1; + } + } + } + // unmark the nodes + Vec_PtrForEachEntry( p->vDecs, pNode, i ) + pNode->fMarkC = 0; +/* + // print the nodes + Vec_PtrForEachEntryStart( p->vDecs, pNode, i, Vec_PtrSize(p->vLeaves) ) + { + printf( "%2d %s = ", i, Abc_NodeIsTravIdCurrent(pNode)? "*" : " " ); + // find the first fanin + Vec_PtrForEachEntry( p->vDecs, pFanin, k ) + if ( Abc_ObjFanin0(pNode) == pFanin ) + break; + if ( k < Vec_PtrSize(p->vLeaves) ) + printf( "%c", 'a' + k ); + else + printf( "%d", k ); + printf( "%s ", Abc_ObjFaninC0(pNode)? "\'" : "" ); + // find the second fanin + Vec_PtrForEachEntry( p->vDecs, pFanin, k ) + if ( Abc_ObjFanin1(pNode) == pFanin ) + break; + if ( k < Vec_PtrSize(p->vLeaves) ) + printf( "%c", 'a' + k ); + else + printf( "%d", k ); + printf( "%s ", Abc_ObjFaninC1(pNode)? "\'" : "" ); + printf( "\n" ); + } +*/ + printf( "%d\n", Vec_PtrSize(p->vDecs)-nNodesSaved-Vec_PtrSize(p->vLeaves) ); +} + + +/**Function************************************************************* + + Synopsis [Starts the cut manager for rewriting.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_NodeRestructure( Abc_ManRst_t * p, Abc_Obj_t * pNode, Cut_Cut_t * pCutList ) +{ + Dec_Graph_t * pGraph; + Cut_Cut_t * pCut; +// int nCuts; + p->nNodesConsidered++; +/* + // count the number of cuts with four inputs or more + nCuts = 0; + for ( pCut = pCutList; pCut; pCut = pCut->pNext ) + nCuts += (int)(pCut->nLeaves > 3); + printf( "-----------------------------------\n" ); + printf( "Node %6d : Factor-cuts = %5d.\n", pNode->Id, nCuts ); +*/ + // go through the interesting cuts + for ( pCut = pCutList; pCut; pCut = pCut->pNext ) + { + if ( pCut->nLeaves < 4 ) + continue; + if ( pGraph = Abc_NodeRestructureCut( p, pNode, pCut ) ) + return pGraph; + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [Starts the cut manager for rewriting.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_NodeRestructureCut( Abc_ManRst_t * p, Abc_Obj_t * pRoot, Cut_Cut_t * pCut ) +{ + Dec_Graph_t * pGraph; + Dsd_Node_t * pNodeDsd; + Abc_Obj_t * pLeaf; + DdNode * bFunc; + int nNodesSaved, nNodesAdded; + int Required, nMaxSize, clk, i; + int fVeryVerbose = 0; + + p->nCutsConsidered++; + + // get the required time for the node + Required = p->fUpdateLevel? Abc_ObjRequiredLevel(pRoot) : ABC_INFINITY; + + // collect the leaves of the cut + Vec_PtrClear( p->vLeaves ); + for ( i = 0; i < (int)pCut->nLeaves; i++ ) + { + pLeaf = Abc_NtkObj(pRoot->pNtk, pCut->pLeaves[i]); + if ( pLeaf == NULL ) // the so-called "bad cut phenomenon" is due to removed nodes + return NULL; + Vec_PtrPush( p->vLeaves, pLeaf ); + } + + if ( pRoot->Id == 29 ) + { + int x = 0; + } + +clk = clock(); + // collect the internal nodes of the cut +// Abc_NodeConeCollect( &pRoot, 1, p->vLeaves, p->vVisited, 0 ); + // derive the BDD of the cut + bFunc = Abc_NodeConeBdd( p->dd, p->dd->vars, pRoot, p->vLeaves, p->vVisited ); Cudd_Ref( bFunc ); +p->timeBdd += clock() - clk; + + // consider the special case, when the function is a constant + if ( Cudd_IsConstant(bFunc) ) + { + p->nLastGain = Abc_NodeMffcSize( pRoot ); + p->nNodesGained += p->nLastGain; + p->nNodesRestructured++; + Cudd_RecursiveDeref( p->dd, bFunc ); + if ( Cudd_IsComplement(bFunc) ) + return Dec_GraphCreateConst0(); + return Dec_GraphCreateConst1(); + } + +clk = clock(); + // try disjoint support decomposition + pNodeDsd = Dsd_DecomposeOne( p->pManDsd, bFunc ); +p->timeDsd += clock() - clk; + + // skip nodes with non-decomposable blocks + Dsd_TreeNodeGetInfoOne( pNodeDsd, NULL, &nMaxSize ); + if ( nMaxSize > 3 ) + { + Cudd_RecursiveDeref( p->dd, bFunc ); + return NULL; + } + + +/* + // skip nodes that cannot be improved + if ( Vec_PtrSize(p->vVisited) <= Dsd_TreeGetAigCost(pNodeDsd) ) + { + Cudd_RecursiveDeref( p->dd, bFunc ); + return NULL; + } +*/ + + p->nCutsExplored++; + + // mark the fanin boundary + // (can mark only essential fanins, belonging to bNodeFunc!) + Vec_PtrForEachEntry( p->vLeaves, pLeaf, i ) + pLeaf->vFanouts.nSize++; + // label MFFC with current traversal ID + Abc_NtkIncrementTravId( pRoot->pNtk ); + nNodesSaved = Abc_NodeMffcLabelAig( pRoot ); + // unmark the fanin boundary and set the fanins as leaves in the form + Vec_PtrForEachEntry( p->vLeaves, pLeaf, i ) + pLeaf->vFanouts.nSize--; +/* + if ( nNodesSaved < 3 ) + { + Cudd_RecursiveDeref( p->dd, bFunc ); + return NULL; + } +*/ + +/* + printf( "%5d : Cut-size = %d. Old AIG = %2d. New AIG = %2d. Old MFFC = %2d.\n", + pRoot->Id, pCut->nLeaves, Vec_PtrSize(p->vVisited), Dsd_TreeGetAigCost(pNodeDsd), + nNodesSaved ); + Dsd_NodePrint( stdout, pNodeDsd ); + + Abc_RestructNodeDivisors( p, pRoot ); + + if ( pRoot->Id == 433 ) + { + int x = 0; + } +*/ +// Abc_RestructNodeDivisors( p, pRoot, nNodesSaved ); + + + // detect how many new nodes will be added (while taking into account reused nodes) +clk = clock(); + if ( nMaxSize > 3 ) + pGraph = NULL; + else + pGraph = Abc_NodeEvaluateDsd( p, pNodeDsd, pRoot, Required, nNodesSaved, &nNodesAdded ); +// pGraph = NULL; +p->timeEval += clock() - clk; + + // quit if there is no improvement + if ( pGraph == NULL || nNodesAdded == -1 || nNodesAdded == nNodesSaved && !p->fUseZeros ) + { + Cudd_RecursiveDeref( p->dd, bFunc ); + if ( pGraph ) Dec_GraphFree( pGraph ); + return NULL; + } + +/* + // print stats + printf( "%5d : Cut-size = %d. Old AIG = %2d. New AIG = %2d. Old MFFC = %2d. New MFFC = %2d. Gain = %d.\n", + pRoot->Id, pCut->nLeaves, Vec_PtrSize(p->vVisited), Dsd_TreeGetAigCost(pNodeDsd), + nNodesSaved, nNodesAdded, (nNodesAdded == -1)? 0 : nNodesSaved-nNodesAdded ); +// Dsd_NodePrint( stdout, pNodeDsd ); +// Dec_GraphPrint( stdout, pGraph, NULL, NULL ); +*/ + + // compute the total gain in the number of nodes + p->nLastGain = nNodesSaved - nNodesAdded; + p->nNodesGained += p->nLastGain; + p->nNodesRestructured++; + + // report the progress + if ( fVeryVerbose ) + { + printf( "Node %6s : ", Abc_ObjName(pRoot) ); + printf( "Cone = %2d. ", p->vLeaves->nSize ); + printf( "BDD = %2d. ", Cudd_DagSize(bFunc) ); + printf( "FF = %2d. ", 1 + Dec_GraphNodeNum(pGraph) ); + printf( "MFFC = %2d. ", nNodesSaved ); + printf( "Add = %2d. ", nNodesAdded ); + printf( "GAIN = %2d. ", p->nLastGain ); + printf( "\n" ); + } + Cudd_RecursiveDeref( p->dd, bFunc ); + return pGraph; +} + + +/**Function************************************************************* + + Synopsis [Moves closer to the end the node that is best for sharing.] + + Description [If the flag is set, tries to find an EXOR, otherwise, tries + to find an OR.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeEdgeDsdPermute( Dec_Graph_t * pGraph, Abc_ManRst_t * pManRst, Vec_Int_t * vEdges, int fExor ) +{ + Dec_Edge_t eNode1, eNode2, eNode3; + Abc_Obj_t * pNode1, * pNode2, * pNode3, * pTemp; + int LeftBound = 0, RightBound, i; + // get the right bound + RightBound = Vec_IntSize(vEdges) - 2; + assert( LeftBound <= RightBound ); + if ( LeftBound == RightBound ) + return; + // get the two last nodes + eNode1 = Dec_IntToEdge( Vec_IntEntry(vEdges, RightBound + 1) ); + eNode2 = Dec_IntToEdge( Vec_IntEntry(vEdges, RightBound ) ); + pNode1 = Dec_GraphNode( pGraph, eNode1.Node )->pFunc; + pNode2 = Dec_GraphNode( pGraph, eNode2.Node )->pFunc; + pNode1 = !pNode1? NULL : Abc_ObjNotCond( pNode1, eNode1.fCompl ); + pNode2 = !pNode2? NULL : Abc_ObjNotCond( pNode2, eNode2.fCompl ); + // quit if the last node does not exist + if ( pNode1 == NULL ) + return; + // find the first node that can be shared + for ( i = RightBound; i >= LeftBound; i-- ) + { + // get the third node + eNode3 = Dec_IntToEdge( Vec_IntEntry(vEdges, i) ); + pNode3 = Dec_GraphNode( pGraph, eNode3.Node )->pFunc; + pNode3 = !pNode3? NULL : Abc_ObjNotCond( pNode3, eNode3.fCompl ); + if ( pNode3 == NULL ) + continue; + // check if the node exists + if ( fExor ) + { + if ( pNode1 && pNode3 ) + { + pTemp = Abc_AigXorLookup( pManRst->pNtk->pManFunc, pNode1, pNode3, NULL ); + if ( !pTemp || Abc_NodeIsTravIdCurrent(Abc_ObjRegular(pTemp)) ) + continue; + + if ( pNode3 == pNode2 ) + return; + Vec_IntWriteEntry( vEdges, i, Dec_EdgeToInt(eNode2) ); + Vec_IntWriteEntry( vEdges, RightBound, Dec_EdgeToInt(eNode3) ); + return; + } + } + else + { + if ( pNode1 && pNode3 ) + { + pTemp = Abc_AigAndLookup( pManRst->pNtk->pManFunc, Abc_ObjNot(pNode1), Abc_ObjNot(pNode3) ); + if ( !pTemp || Abc_NodeIsTravIdCurrent(Abc_ObjRegular(pTemp)) ) + continue; + + if ( eNode3.Node == eNode2.Node ) + return; + Vec_IntWriteEntry( vEdges, i, Dec_EdgeToInt(eNode2) ); + Vec_IntWriteEntry( vEdges, RightBound, Dec_EdgeToInt(eNode3) ); + return; + } + } + } +} + +/**Function************************************************************* + + Synopsis [Adds the new edge in the given order.] + + Description [Similar to Vec_IntPushOrder, except in decreasing order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeEdgeDsdPushOrdered( Dec_Graph_t * pGraph, Vec_Int_t * vEdges, int Edge ) +{ + int i, NodeOld, NodeNew; + vEdges->nSize++; + for ( i = vEdges->nSize-2; i >= 0; i-- ) + { + NodeOld = Dec_IntToEdge(vEdges->pArray[i]).Node; + NodeNew = Dec_IntToEdge(Edge).Node; + // use <= because we are trying to push the new (non-existent) nodes as far as possible + if ( Dec_GraphNode(pGraph, NodeOld)->Level <= Dec_GraphNode(pGraph, NodeNew)->Level ) + vEdges->pArray[i+1] = vEdges->pArray[i]; + else + break; + } + vEdges->pArray[i+1] = Edge; +} + +/**Function************************************************************* + + Synopsis [Evaluation one DSD.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Edge_t Abc_NodeEvaluateDsd_rec( Dec_Graph_t * pGraph, Abc_ManRst_t * pManRst, Dsd_Node_t * pNodeDsd, int Required, int nNodesSaved, int * pnNodesAdded ) +{ + Dec_Edge_t eNode1, eNode2, eNode3, eResult, eQuit = { 0, 2006 }; + Abc_Obj_t * pNode1, * pNode2, * pNode3, * pNode4, * pTemp; + Dsd_Node_t * pChildDsd; + Dsd_Type_t DecType; + Vec_Int_t * vEdges; + int Level1, Level2, Level3, Level4; + int i, Index, fCompl, Type; + + // remove the complemented attribute + fCompl = Dsd_IsComplement( pNodeDsd ); + pNodeDsd = Dsd_Regular( pNodeDsd ); + + // consider the trivial case + DecType = Dsd_NodeReadType( pNodeDsd ); + if ( DecType == DSD_NODE_BUF ) + { + Index = Dsd_NodeReadFunc(pNodeDsd)->index; + assert( Index < Dec_GraphLeaveNum(pGraph) ); + eResult = Dec_EdgeCreate( Index, fCompl ); + return eResult; + } + assert( DecType == DSD_NODE_OR || DecType == DSD_NODE_EXOR || DecType == DSD_NODE_PRIME ); + + // solve the problem for the children + vEdges = Vec_IntAlloc( Dsd_NodeReadDecsNum(pNodeDsd) ); + Dsd_NodeForEachChild( pNodeDsd, i, pChildDsd ) + { + eResult = Abc_NodeEvaluateDsd_rec( pGraph, pManRst, pChildDsd, Required, nNodesSaved, pnNodesAdded ); + if ( eResult.Node == eQuit.Node ) // infeasible + { + Vec_IntFree( vEdges ); + return eQuit; + } + // order the inputs only if this is OR or EXOR + if ( DecType == DSD_NODE_PRIME ) + Vec_IntPush( vEdges, Dec_EdgeToInt(eResult) ); + else + Abc_NodeEdgeDsdPushOrdered( pGraph, vEdges, Dec_EdgeToInt(eResult) ); + } + // the edges are sorted by the level of their nodes in decreasing order + + + // consider special cases + if ( DecType == DSD_NODE_OR ) + { + // try to balance the nodes by delay + assert( Vec_IntSize(vEdges) > 1 ); + while ( Vec_IntSize(vEdges) > 1 ) + { + // permute the last two entries + if ( Vec_IntSize(vEdges) > 2 ) + Abc_NodeEdgeDsdPermute( pGraph, pManRst, vEdges, 0 ); + // get the two last nodes + eNode1 = Dec_IntToEdge( Vec_IntPop(vEdges) ); + eNode2 = Dec_IntToEdge( Vec_IntPop(vEdges) ); + pNode1 = Dec_GraphNode( pGraph, eNode1.Node )->pFunc; + pNode2 = Dec_GraphNode( pGraph, eNode2.Node )->pFunc; + pNode1 = !pNode1? NULL : Abc_ObjNotCond( pNode1, eNode1.fCompl ); + pNode2 = !pNode2? NULL : Abc_ObjNotCond( pNode2, eNode2.fCompl ); + // check if the new node exists + pNode3 = NULL; + if ( pNode1 && pNode2 ) + { + pNode3 = Abc_AigAndLookup( pManRst->pNtk->pManFunc, Abc_ObjNot(pNode1), Abc_ObjNot(pNode2) ); + pNode3 = !pNode3? NULL : Abc_ObjNot(pNode3); + } + // create the new node + eNode3 = Dec_GraphAddNodeOr( pGraph, eNode1, eNode2 ); + // set level + Level1 = Dec_GraphNode( pGraph, eNode1.Node )->Level; + Level2 = Dec_GraphNode( pGraph, eNode2.Node )->Level; + Dec_GraphNode( pGraph, eNode3.Node )->Level = 1 + ABC_MAX(Level1, Level2); + // get the new node if possible + if ( pNode3 ) + { + Dec_GraphNode( pGraph, eNode3.Node )->pFunc = Abc_ObjNotCond(pNode3, eNode3.fCompl); + Level3 = Dec_GraphNode( pGraph, eNode3.Node )->Level; + assert( Required == ABC_INFINITY || Level3 == (int)Abc_ObjRegular(pNode3)->Level ); + } + if ( !pNode3 || Abc_NodeIsTravIdCurrent(Abc_ObjRegular(pNode3)) ) + { + (*pnNodesAdded)++; + if ( *pnNodesAdded > nNodesSaved ) + { + Vec_IntFree( vEdges ); + return eQuit; + } + } + // add the resulting node to the form + Abc_NodeEdgeDsdPushOrdered( pGraph, vEdges, Dec_EdgeToInt(eNode3) ); + } + // get the last node + eResult = Dec_IntToEdge( Vec_IntPop(vEdges) ); + Vec_IntFree( vEdges ); + // complement the graph if the node was complemented + eResult.fCompl ^= fCompl; + return eResult; + } + if ( DecType == DSD_NODE_EXOR ) + { + // try to balance the nodes by delay + assert( Vec_IntSize(vEdges) > 1 ); + while ( Vec_IntSize(vEdges) > 1 ) + { + // permute the last two entries + if ( Vec_IntSize(vEdges) > 2 ) + Abc_NodeEdgeDsdPermute( pGraph, pManRst, vEdges, 1 ); + // get the two last nodes + eNode1 = Dec_IntToEdge( Vec_IntPop(vEdges) ); + eNode2 = Dec_IntToEdge( Vec_IntPop(vEdges) ); + pNode1 = Dec_GraphNode( pGraph, eNode1.Node )->pFunc; + pNode2 = Dec_GraphNode( pGraph, eNode2.Node )->pFunc; + pNode1 = !pNode1? NULL : Abc_ObjNotCond( pNode1, eNode1.fCompl ); + pNode2 = !pNode2? NULL : Abc_ObjNotCond( pNode2, eNode2.fCompl ); + // check if the new node exists + Type = 0; + pNode3 = NULL; + if ( pNode1 && pNode2 ) + pNode3 = Abc_AigXorLookup( pManRst->pNtk->pManFunc, pNode1, pNode2, &Type ); + // create the new node + eNode3 = Dec_GraphAddNodeXor( pGraph, eNode1, eNode2, Type ); // should have the same structure as in AIG + // set level + Level1 = Dec_GraphNode( pGraph, eNode1.Node )->Level; + Level2 = Dec_GraphNode( pGraph, eNode2.Node )->Level; + Dec_GraphNode( pGraph, eNode3.Node )->Level = 2 + ABC_MAX(Level1, Level2); + // get the new node if possible + if ( pNode3 ) + { + Dec_GraphNode( pGraph, eNode3.Node )->pFunc = Abc_ObjNotCond(pNode3, eNode3.fCompl); + Level3 = Dec_GraphNode( pGraph, eNode3.Node )->Level; + assert( Required == ABC_INFINITY || Level3 == (int)Abc_ObjRegular(pNode3)->Level ); + } + if ( !pNode3 || Abc_NodeIsTravIdCurrent(Abc_ObjRegular(pNode3)) ) + { + (*pnNodesAdded)++; + if ( !pNode1 || !pNode2 ) + (*pnNodesAdded) += 2; + else if ( Type == 0 ) + { + pTemp = Abc_AigAndLookup( pManRst->pNtk->pManFunc, pNode1, Abc_ObjNot(pNode2) ); + if ( !pTemp || Abc_NodeIsTravIdCurrent(Abc_ObjRegular(pTemp)) ) + (*pnNodesAdded)++; + pTemp = Abc_AigAndLookup( pManRst->pNtk->pManFunc, Abc_ObjNot(pNode1), pNode2 ); + if ( !pTemp || Abc_NodeIsTravIdCurrent(Abc_ObjRegular(pTemp)) ) + (*pnNodesAdded)++; + } + else + { + pTemp = Abc_AigAndLookup( pManRst->pNtk->pManFunc, Abc_ObjNot(pNode1), Abc_ObjNot(pNode2) ); + if ( !pTemp || Abc_NodeIsTravIdCurrent(Abc_ObjRegular(pTemp)) ) + (*pnNodesAdded)++; + pTemp = Abc_AigAndLookup( pManRst->pNtk->pManFunc, pNode1, pNode2 ); + if ( !pTemp || Abc_NodeIsTravIdCurrent(Abc_ObjRegular(pTemp)) ) + (*pnNodesAdded)++; + } + if ( *pnNodesAdded > nNodesSaved ) + { + Vec_IntFree( vEdges ); + return eQuit; + } + } + // add the resulting node to the form + Abc_NodeEdgeDsdPushOrdered( pGraph, vEdges, Dec_EdgeToInt(eNode3) ); + } + // get the last node + eResult = Dec_IntToEdge( Vec_IntPop(vEdges) ); + Vec_IntFree( vEdges ); + // complement the graph if the node is complemented + eResult.fCompl ^= fCompl; + return eResult; + } + if ( DecType == DSD_NODE_PRIME ) + { + DdNode * bLocal, * bVar, * bCofT, * bCofE; + bLocal = Dsd_TreeGetPrimeFunction( pManRst->dd, pNodeDsd ); Cudd_Ref( bLocal ); +//Extra_bddPrint( pManRst->dd, bLocal ); + + bVar = pManRst->dd->vars[0]; + bCofE = Cudd_Cofactor( pManRst->dd, bLocal, Cudd_Not(bVar) ); Cudd_Ref( bCofE ); + bCofT = Cudd_Cofactor( pManRst->dd, bLocal, bVar ); Cudd_Ref( bCofT ); + if ( !Extra_bddIsVar(bCofE) || !Extra_bddIsVar(bCofT) ) + { + Cudd_RecursiveDeref( pManRst->dd, bCofE ); + Cudd_RecursiveDeref( pManRst->dd, bCofT ); + bVar = pManRst->dd->vars[1]; + bCofE = Cudd_Cofactor( pManRst->dd, bLocal, Cudd_Not(bVar) ); Cudd_Ref( bCofE ); + bCofT = Cudd_Cofactor( pManRst->dd, bLocal, bVar ); Cudd_Ref( bCofT ); + if ( !Extra_bddIsVar(bCofE) || !Extra_bddIsVar(bCofT) ) + { + Cudd_RecursiveDeref( pManRst->dd, bCofE ); + Cudd_RecursiveDeref( pManRst->dd, bCofT ); + bVar = pManRst->dd->vars[2]; + bCofE = Cudd_Cofactor( pManRst->dd, bLocal, Cudd_Not(bVar) ); Cudd_Ref( bCofE ); + bCofT = Cudd_Cofactor( pManRst->dd, bLocal, bVar ); Cudd_Ref( bCofT ); + if ( !Extra_bddIsVar(bCofE) || !Extra_bddIsVar(bCofT) ) + { + Cudd_RecursiveDeref( pManRst->dd, bCofE ); + Cudd_RecursiveDeref( pManRst->dd, bCofT ); + Cudd_RecursiveDeref( pManRst->dd, bLocal ); + Vec_IntFree( vEdges ); + return eQuit; + } + } + } + Cudd_RecursiveDeref( pManRst->dd, bLocal ); + // we found the control variable (bVar) and the var-cofactors (bCofT, bCofE) + + // find the graph nodes + eNode1 = Dec_IntToEdge( Vec_IntEntry(vEdges, bVar->index) ); + eNode2 = Dec_IntToEdge( Vec_IntEntry(vEdges, Cudd_Regular(bCofT)->index) ); + eNode3 = Dec_IntToEdge( Vec_IntEntry(vEdges, Cudd_Regular(bCofE)->index) ); + // add the complements to the graph nodes + eNode2.fCompl ^= Cudd_IsComplement(bCofT); + eNode3.fCompl ^= Cudd_IsComplement(bCofE); + + // because the cofactors are vars, we can just as well deref them here + Cudd_RecursiveDeref( pManRst->dd, bCofE ); + Cudd_RecursiveDeref( pManRst->dd, bCofT ); + + // find the ABC nodes + pNode1 = Dec_GraphNode( pGraph, eNode1.Node )->pFunc; + pNode2 = Dec_GraphNode( pGraph, eNode2.Node )->pFunc; + pNode3 = Dec_GraphNode( pGraph, eNode3.Node )->pFunc; + pNode1 = !pNode1? NULL : Abc_ObjNotCond( pNode1, eNode1.fCompl ); + pNode2 = !pNode2? NULL : Abc_ObjNotCond( pNode2, eNode2.fCompl ); + pNode3 = !pNode3? NULL : Abc_ObjNotCond( pNode3, eNode3.fCompl ); + + // check if the new node exists + Type = 0; + pNode4 = NULL; + if ( pNode1 && pNode2 && pNode3 ) + pNode4 = Abc_AigMuxLookup( pManRst->pNtk->pManFunc, pNode1, pNode2, pNode3, &Type ); + + // create the new node + eResult = Dec_GraphAddNodeMux( pGraph, eNode1, eNode2, eNode3, Type ); // should have the same structure as AIG + + // set level + Level1 = Dec_GraphNode( pGraph, eNode1.Node )->Level; + Level2 = Dec_GraphNode( pGraph, eNode2.Node )->Level; + Level3 = Dec_GraphNode( pGraph, eNode3.Node )->Level; + Dec_GraphNode( pGraph, eResult.Node )->Level = 2 + ABC_MAX( ABC_MAX(Level1, Level2), Level3 ); + // get the new node if possible + if ( pNode4 ) + { + Dec_GraphNode( pGraph, eResult.Node )->pFunc = Abc_ObjNotCond(pNode4, eResult.fCompl); + Level4 = Dec_GraphNode( pGraph, eResult.Node )->Level; + assert( Required == ABC_INFINITY || Level4 == (int)Abc_ObjRegular(pNode4)->Level ); + } + if ( !pNode4 || Abc_NodeIsTravIdCurrent(Abc_ObjRegular(pNode4)) ) + { + (*pnNodesAdded)++; + if ( Type == 0 ) + { + if ( !pNode1 || !pNode2 ) + (*pnNodesAdded)++; + else + { + pTemp = Abc_AigAndLookup( pManRst->pNtk->pManFunc, pNode1, pNode2 ); + if ( !pTemp || Abc_NodeIsTravIdCurrent(Abc_ObjRegular(pTemp)) ) + (*pnNodesAdded)++; + } + if ( !pNode1 || !pNode3 ) + (*pnNodesAdded)++; + else + { + pTemp = Abc_AigAndLookup( pManRst->pNtk->pManFunc, Abc_ObjNot(pNode1), pNode3 ); + if ( !pTemp || Abc_NodeIsTravIdCurrent(Abc_ObjRegular(pTemp)) ) + (*pnNodesAdded)++; + } + } + else + { + if ( !pNode1 || !pNode2 ) + (*pnNodesAdded)++; + else + { + pTemp = Abc_AigAndLookup( pManRst->pNtk->pManFunc, pNode1, Abc_ObjNot(pNode2) ); + if ( !pTemp || Abc_NodeIsTravIdCurrent(Abc_ObjRegular(pTemp)) ) + (*pnNodesAdded)++; + } + if ( !pNode1 || !pNode3 ) + (*pnNodesAdded)++; + else + { + pTemp = Abc_AigAndLookup( pManRst->pNtk->pManFunc, Abc_ObjNot(pNode1), Abc_ObjNot(pNode3) ); + if ( !pTemp || Abc_NodeIsTravIdCurrent(Abc_ObjRegular(pTemp)) ) + (*pnNodesAdded)++; + } + } + if ( *pnNodesAdded > nNodesSaved ) + { + Vec_IntFree( vEdges ); + return eQuit; + } + } + + Vec_IntFree( vEdges ); + // complement the graph if the node was complemented + eResult.fCompl ^= fCompl; + return eResult; + } + Vec_IntFree( vEdges ); + return eQuit; +} + +/**Function************************************************************* + + Synopsis [Evaluation one DSD.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_NodeEvaluateDsd( Abc_ManRst_t * pManRst, Dsd_Node_t * pNodeDsd, Abc_Obj_t * pRoot, int Required, int nNodesSaved, int * pnNodesAdded ) +{ + Dec_Graph_t * pGraph; + Dec_Edge_t gEdge; + Abc_Obj_t * pLeaf; + Dec_Node_t * pNode; + int i; + + // create the graph and set the leaves + pGraph = Dec_GraphCreate( Vec_PtrSize(pManRst->vLeaves) ); + Dec_GraphForEachLeaf( pGraph, pNode, i ) + { + pLeaf = Vec_PtrEntry( pManRst->vLeaves, i ); + pNode->pFunc = pLeaf; + pNode->Level = pLeaf->Level; + } + + // create the decomposition structure from the DSD + *pnNodesAdded = 0; + gEdge = Abc_NodeEvaluateDsd_rec( pGraph, pManRst, pNodeDsd, Required, nNodesSaved, pnNodesAdded ); + if ( gEdge.Node > 1000 ) // infeasible + { + *pnNodesAdded = -1; + Dec_GraphFree( pGraph ); + return NULL; + } + + // quit if the root node is the same + pLeaf = Dec_GraphNode( pGraph, gEdge.Node )->pFunc; + if ( Abc_ObjRegular(pLeaf) == pRoot ) + { + *pnNodesAdded = -1; + Dec_GraphFree( pGraph ); + return NULL; + } + + Dec_GraphSetRoot( pGraph, gEdge ); + return pGraph; +} + + + +/**Function************************************************************* + + Synopsis [Starts the cut manager for rewriting.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Man_t * Abc_NtkStartCutManForRestruct( Abc_Ntk_t * pNtk, int nCutMax, int fDag ) +{ + static Cut_Params_t Params, * pParams = &Params; + Cut_Man_t * pManCut; + Abc_Obj_t * pObj; + int i; + // start the cut manager + memset( pParams, 0, sizeof(Cut_Params_t) ); + pParams->nVarsMax = nCutMax; // the max cut size ("k" of the k-feasible cuts) + pParams->nKeepMax = 250; // the max number of cuts kept at a node + pParams->fTruth = 0; // compute truth tables + pParams->fFilter = 1; // filter dominated cuts + pParams->fSeq = 0; // compute sequential cuts + pParams->fDrop = 0; // drop cuts on the fly + pParams->fDag = fDag; // compute DAG cuts + pParams->fTree = 0; // compute tree cuts + pParams->fVerbose = 0; // the verbosiness flag + pParams->nIdsMax = Abc_NtkObjNumMax( pNtk ); + pManCut = Cut_ManStart( pParams ); + if ( pParams->fDrop ) + Cut_ManSetFanoutCounts( pManCut, Abc_NtkFanoutCounts(pNtk) ); + // set cuts for PIs + Abc_NtkForEachCi( pNtk, pObj, i ) + if ( Abc_ObjFanoutNum(pObj) > 0 ) + Cut_NodeSetTriv( pManCut, pObj->Id ); + return pManCut; +} + +/**Function************************************************************* + + Synopsis [Starts the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_ManRst_t * Abc_NtkManRstStart( int nCutMax, bool fUpdateLevel, bool fUseZeros, bool fVerbose ) +{ + Abc_ManRst_t * p; + p = ALLOC( Abc_ManRst_t, 1 ); + memset( p, 0, sizeof(Abc_ManRst_t) ); + // set the parameters + p->nCutMax = nCutMax; + p->fUpdateLevel = fUpdateLevel; + p->fUseZeros = fUseZeros; + p->fVerbose = fVerbose; + // start the BDD manager + p->dd = Cudd_Init( p->nCutMax, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 ); + Cudd_zddVarsFromBddVars( p->dd, 2 ); + // start the DSD manager + p->pManDsd = Dsd_ManagerStart( p->dd, p->dd->size, 0 ); + // other temp datastructures + p->vVisited = Vec_PtrAlloc( 100 ); + p->vLeaves = Vec_PtrAlloc( 100 ); + p->vDecs = Vec_PtrAlloc( 100 ); + p->vTemp = Vec_PtrAlloc( 100 ); + p->vSims = Vec_IntAlloc( 100 ); + p->vOnes = Vec_IntAlloc( 100 ); + p->vBinate = Vec_IntAlloc( 100 ); + p->vTwos = Vec_IntAlloc( 100 ); + p->vRands = Vec_IntAlloc( 20 ); + + { + int i; + for ( i = 0; i < 20; i++ ) + Vec_IntPush( p->vRands, (int)RST_RANDOM_UNSIGNED ); + } + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkManRstStop( Abc_ManRst_t * p ) +{ + Dsd_ManagerStop( p->pManDsd ); + Extra_StopManager( p->dd ); + Vec_PtrFree( p->vDecs ); + Vec_PtrFree( p->vLeaves ); + Vec_PtrFree( p->vVisited ); + Vec_PtrFree( p->vTemp ); + Vec_IntFree( p->vSims ); + Vec_IntFree( p->vOnes ); + Vec_IntFree( p->vBinate ); + Vec_IntFree( p->vTwos ); + Vec_IntFree( p->vRands ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Stops the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkManRstPrintStats( Abc_ManRst_t * p ) +{ + printf( "Refactoring statistics:\n" ); + printf( "Nodes considered = %8d.\n", p->nNodesConsidered ); + printf( "Cuts considered = %8d.\n", p->nCutsConsidered ); + printf( "Cuts explored = %8d.\n", p->nCutsExplored ); + printf( "Nodes restructured = %8d.\n", p->nNodesRestructured ); + printf( "Calculated gain = %8d.\n", p->nNodesGained ); + PRT( "Cuts ", p->timeCut ); + PRT( "Resynthesis", p->timeRes ); + PRT( " BDD ", p->timeBdd ); + PRT( " DSD ", p->timeDsd ); + PRT( " Eval ", p->timeEval ); + PRT( "AIG update ", p->timeNtk ); + PRT( "TOTAL ", p->timeTotal ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_Abc_NodeResubCollectDivs( Abc_ManRst_t * p, Abc_Obj_t * pRoot, Cut_Cut_t * pCut ) +{ + Abc_Obj_t * pNode, * pFanout; + int i, k; + // collect the leaves of the cut + Vec_PtrClear( p->vDecs ); + Abc_NtkIncrementTravId( pRoot->pNtk ); + for ( i = 0; i < (int)pCut->nLeaves; i++ ) + { + pNode = Abc_NtkObj(pRoot->pNtk, pCut->pLeaves[i]); + if ( pNode == NULL ) // the so-called "bad cut phenomenon" is due to removed nodes + return 0; + Vec_PtrPush( p->vDecs, pNode ); + Abc_NodeSetTravIdCurrent( pNode ); + } + // explore the fanouts + Vec_PtrForEachEntry( p->vDecs, pNode, i ) + { + // if the fanout has both fanins in the set, add it + Abc_ObjForEachFanout( pNode, pFanout, k ) + { + if ( Abc_NodeIsTravIdCurrent(pFanout) || Abc_ObjIsPo(pFanout) ) + continue; + if ( Abc_NodeIsTravIdCurrent(Abc_ObjFanin0(pFanout)) && Abc_NodeIsTravIdCurrent(Abc_ObjFanin1(pFanout)) ) + { + Vec_PtrPush( p->vDecs, pFanout ); + Abc_NodeSetTravIdCurrent( pFanout ); + } + } + } + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeResubMffc_rec( Abc_Obj_t * pNode ) +{ + if ( Abc_NodeIsTravIdCurrent(pNode) ) + return 0; + Abc_NodeSetTravIdCurrent( pNode ); + return 1 + Abc_NodeResubMffc_rec( Abc_ObjFanin0(pNode) ) + + Abc_NodeResubMffc_rec( Abc_ObjFanin1(pNode) ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeResubMffc( Abc_ManRst_t * p, Vec_Ptr_t * vDecs, int nLeaves, Abc_Obj_t * pRoot ) +{ + Abc_Obj_t * pObj; + int Counter, i, k; + // increment the traversal ID for the leaves + Abc_NtkIncrementTravId( pRoot->pNtk ); + // label the leaves + Vec_PtrForEachEntryStop( vDecs, pObj, i, nLeaves ) + Abc_NodeSetTravIdCurrent( pObj ); + // make sure the node is in the cone and is no one of the leaves + assert( Abc_NodeIsTravIdPrevious(pRoot) ); + Counter = Abc_NodeResubMffc_rec( pRoot ); + // move the labeled nodes to the end + Vec_PtrClear( p->vTemp ); + k = 0; + Vec_PtrForEachEntryStart( vDecs, pObj, i, nLeaves ) + if ( Abc_NodeIsTravIdCurrent(pObj) ) + Vec_PtrPush( p->vTemp, pObj ); + else + Vec_PtrWriteEntry( vDecs, k++, pObj ); + // add the labeled nodes + Vec_PtrForEachEntry( p->vTemp, pObj, i ) + Vec_PtrWriteEntry( vDecs, k++, pObj ); + assert( k == Vec_PtrSize(p->vDecs) ); + assert( pRoot == Vec_PtrEntryLast(p->vDecs) ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Performs simulation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeMffcSimulate( Vec_Ptr_t * vDecs, int nLeaves, Vec_Int_t * vRands, Vec_Int_t * vSims ) +{ + Abc_Obj_t * pObj; + unsigned uData0, uData1, uData; + int i; + // initialize random simulation data + Vec_IntClear( vSims ); + Vec_PtrForEachEntryStop( vDecs, pObj, i, nLeaves ) + { + uData = (unsigned)Vec_IntEntry( vRands, i ); + pObj->pData = (void *)uData; + Vec_IntPush( vSims, uData ); + } + // simulate + Vec_PtrForEachEntryStart( vDecs, pObj, i, nLeaves ) + { + uData0 = (unsigned)Abc_ObjFanin0(pObj)->pData; + uData1 = (unsigned)Abc_ObjFanin1(pObj)->pData; + uData = (Abc_ObjFaninC0(pObj)? ~uData0 : uData0) & (Abc_ObjFaninC1(pObj)? ~uData1 : uData1); + pObj->pData = (void *)uData; + Vec_IntPush( vSims, uData ); + } +} + +/**Function************************************************************* + + Synopsis [Full equality check.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeCheckFull( Abc_ManRst_t * p, Dec_Graph_t * pGraph ) +{ + return 1; +} +/**Function************************************************************* + + Synopsis [Detect contants.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_NodeMffcConstants( Abc_ManRst_t * p, Vec_Int_t * vSims ) +{ + Dec_Graph_t * pGraph; + unsigned uRoot; + // get the root node + uRoot = (unsigned)Vec_IntEntryLast( vSims ); + // get the graph if the node looks constant + if ( uRoot == 0 ) + pGraph = Dec_GraphCreateConst0(); + else if ( uRoot == ~(unsigned)0 ) + pGraph = Dec_GraphCreateConst1(); + // check the graph + if ( Abc_NodeCheckFull( p, pGraph ) ) + return pGraph; + Dec_GraphFree( pGraph ); + return NULL; +} + +/**Function************************************************************* + + Synopsis [Detect single non-overlaps.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_NodeMffcSingleVar( Abc_ManRst_t * p, Vec_Int_t * vSims, int nNodes, Vec_Int_t * vOnes ) +{ + Dec_Graph_t * pGraph; + unsigned uRoot, uNode; + int i; + + Vec_IntClear( vOnes ); + Vec_IntClear( p->vBinate ); + uRoot = (unsigned)Vec_IntEntryLast( vSims ); + for ( i = 0; i < nNodes; i++ ) + { + uNode = (unsigned)Vec_IntEntry( vSims, i ); + if ( uRoot == uNode || uRoot == ~uNode ) + { + pGraph = Dec_GraphCreate( 1 ); + Dec_GraphNode( pGraph, 0 )->pFunc = Vec_PtrEntry( p->vDecs, i ); + Dec_GraphSetRoot( pGraph, Dec_IntToEdge( (int)(uRoot == ~uNode) ) ); + // check the graph + if ( Abc_NodeCheckFull( p, pGraph ) ) + return pGraph; + Dec_GraphFree( pGraph ); + } + if ( (uRoot & uNode) == 0 ) + Vec_IntPush( vOnes, i << 1 ); + else if ( (uRoot & ~uNode) == 0 ) + Vec_IntPush( vOnes, (i << 1) + 1 ); + else + Vec_IntPush( p->vBinate, i ); + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [Detect single non-overlaps.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_NodeMffcSingleNode( Abc_ManRst_t * p, Vec_Int_t * vSims, int nNodes, Vec_Int_t * vOnes ) +{ + Dec_Graph_t * pGraph; + Dec_Edge_t eNode0, eNode1, eRoot; + unsigned uRoot; + int i, k; + uRoot = (unsigned)Vec_IntEntryLast( vSims ); + for ( i = 0; i < vOnes->nSize; i++ ) + for ( k = i+1; k < vOnes->nSize; k++ ) + if ( ~uRoot == ((unsigned)vOnes->pArray[i] | (unsigned)vOnes->pArray[k]) ) + { + eNode0 = Dec_IntToEdge( vOnes->pArray[i] ^ 1 ); + eNode1 = Dec_IntToEdge( vOnes->pArray[k] ^ 1 ); + pGraph = Dec_GraphCreate( 2 ); + Dec_GraphNode( pGraph, 0 )->pFunc = Vec_PtrEntry( p->vDecs, eNode0.Node ); + Dec_GraphNode( pGraph, 1 )->pFunc = Vec_PtrEntry( p->vDecs, eNode1.Node ); + eRoot = Dec_GraphAddNodeAnd( pGraph, eNode0, eNode1 ); + Dec_GraphSetRoot( pGraph, eRoot ); + if ( Abc_NodeCheckFull( p, pGraph ) ) + return pGraph; + Dec_GraphFree( pGraph ); + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [Detect single non-overlaps.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_NodeMffcDoubleNode( Abc_ManRst_t * p, Vec_Int_t * vSims, int nNodes, Vec_Int_t * vOnes ) +{ +// Dec_Graph_t * pGraph; +// unsigned uRoot, uNode; +// int i; + + + return NULL; +} + +/**Function************************************************************* + + Synopsis [Evaluates resubstution of one cut.] + + Description [Returns the graph to add if any.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_NodeResubEval( Abc_ManRst_t * p, Abc_Obj_t * pRoot, Cut_Cut_t * pCut ) +{ + Dec_Graph_t * pGraph; + int nNodesSaved; + + // collect the nodes in the cut + if ( !Abc_Abc_NodeResubCollectDivs( p, pRoot, pCut ) ) + return NULL; + + // label MFFC and count its size + nNodesSaved = Abc_NodeResubMffc( p, p->vDecs, pCut->nLeaves, pRoot ); + assert( nNodesSaved > 0 ); + + // simulate MFFC + Abc_NodeMffcSimulate( p->vDecs, pCut->nLeaves, p->vRands, p->vSims ); + + // check for constant output + pGraph = Abc_NodeMffcConstants( p, p->vSims ); + if ( pGraph ) + { + p->nNodesGained += nNodesSaved; + p->nNodesRestructured++; + return pGraph; + } + + // check for one literal (fill up the ones array) + pGraph = Abc_NodeMffcSingleVar( p, p->vSims, Vec_IntSize(p->vSims) - nNodesSaved, p->vOnes ); + if ( pGraph ) + { + p->nNodesGained += nNodesSaved; + p->nNodesRestructured++; + return pGraph; + } + if ( nNodesSaved == 1 ) + return NULL; + + // look for one node + pGraph = Abc_NodeMffcSingleNode( p, p->vSims, Vec_IntSize(p->vSims) - nNodesSaved, p->vOnes ); + if ( pGraph ) + { + p->nNodesGained += nNodesSaved - 1; + p->nNodesRestructured++; + return pGraph; + } + if ( nNodesSaved == 2 ) + return NULL; + + // look for two nodes + pGraph = Abc_NodeMffcDoubleNode( p, p->vSims, Vec_IntSize(p->vSims) - nNodesSaved, p->vOnes ); + if ( pGraph ) + { + p->nNodesGained += nNodesSaved - 2; + p->nNodesRestructured++; + return pGraph; + } + if ( nNodesSaved == 3 ) + return NULL; +/* + // look for MUX/EXOR + pGraph = Abc_NodeMffcMuxNode( p, p->vSims, Vec_IntSize(p->vSims) - nNodesSaved ); + if ( pGraph ) + { + p->nNodesGained += nNodesSaved - 1; + p->nNodesRestructured++; + return pGraph; + } +*/ + return NULL; +} + +/**Function************************************************************* + + Synopsis [Performs resubstution.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_NodeResubstitute( Abc_ManRst_t * p, Abc_Obj_t * pNode, Cut_Cut_t * pCutList ) +{ + Dec_Graph_t * pGraph, * pGraphBest = NULL; + Cut_Cut_t * pCut; + int nCuts; + p->nNodesConsidered++; + + // count the number of cuts with four inputs or more + nCuts = 0; + for ( pCut = pCutList; pCut; pCut = pCut->pNext ) + nCuts += (int)(pCut->nLeaves > 3); + printf( "-----------------------------------\n" ); + printf( "Node %6d : Factor-cuts = %5d.\n", pNode->Id, nCuts ); + + // go through the interesting cuts + for ( pCut = pCutList; pCut; pCut = pCut->pNext ) + { + if ( pCut->nLeaves < 4 ) + continue; + pGraph = Abc_NodeResubEval( p, pNode, pCut ); + if ( pGraph == NULL ) + continue; + if ( !pGraphBest || Dec_GraphNodeNum(pGraph) < Dec_GraphNodeNum(pGraphBest) ) + { + if ( pGraphBest ) + Dec_GraphFree(pGraphBest); + pGraphBest = pGraph; + } + else + Dec_GraphFree(pGraph); + } + return pGraphBest; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcResub.c b/abc_with_bb_support/src/base/abci/abcResub.c new file mode 100644 index 000000000..6c4f7bd39 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcResub.c @@ -0,0 +1,1951 @@ +/**CFile**************************************************************** + + FileName [abcResub.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Resubstitution manager.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcResub.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "dec.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define ABC_RS_DIV1_MAX 150 // the max number of divisors to consider +#define ABC_RS_DIV2_MAX 500 // the max number of pair-wise divisors to consider + +typedef struct Abc_ManRes_t_ Abc_ManRes_t; +struct Abc_ManRes_t_ +{ + // paramers + int nLeavesMax; // the max number of leaves in the cone + int nDivsMax; // the max number of divisors in the cone + // representation of the cone + Abc_Obj_t * pRoot; // the root of the cone + int nLeaves; // the number of leaves + int nDivs; // the number of all divisor (including leaves) + int nMffc; // the size of MFFC + int nLastGain; // the gain the number of nodes + Vec_Ptr_t * vDivs; // the divisors + // representation of the simulation info + int nBits; // the number of simulation bits + int nWords; // the number of unsigneds for siminfo + Vec_Ptr_t * vSims; // simulation info + unsigned * pInfo; // pointer to simulation info + // observability don't-cares + unsigned * pCareSet; + // internal divisor storage + Vec_Ptr_t * vDivs1UP; // the single-node unate divisors + Vec_Ptr_t * vDivs1UN; // the single-node unate divisors + Vec_Ptr_t * vDivs1B; // the single-node binate divisors + Vec_Ptr_t * vDivs2UP0; // the double-node unate divisors + Vec_Ptr_t * vDivs2UP1; // the double-node unate divisors + Vec_Ptr_t * vDivs2UN0; // the double-node unate divisors + Vec_Ptr_t * vDivs2UN1; // the double-node unate divisors + // other data + Vec_Ptr_t * vTemp; // temporary array of nodes + // runtime statistics + int timeCut; + int timeTruth; + int timeRes; + int timeDiv; + int timeMffc; + int timeSim; + int timeRes1; + int timeResD; + int timeRes2; + int timeRes3; + int timeNtk; + int timeTotal; + // improvement statistics + int nUsedNodeC; + int nUsedNode0; + int nUsedNode1Or; + int nUsedNode1And; + int nUsedNode2Or; + int nUsedNode2And; + int nUsedNode2OrAnd; + int nUsedNode2AndOr; + int nUsedNode3OrAnd; + int nUsedNode3AndOr; + int nUsedNodeTotal; + int nTotalDivs; + int nTotalLeaves; + int nTotalGain; + int nNodesBeg; + int nNodesEnd; +}; + +// external procedures +static Abc_ManRes_t* Abc_ManResubStart( int nLeavesMax, int nDivsMax ); +static void Abc_ManResubStop( Abc_ManRes_t * p ); +static Dec_Graph_t * Abc_ManResubEval( Abc_ManRes_t * p, Abc_Obj_t * pRoot, Vec_Ptr_t * vLeaves, int nSteps, bool fUpdateLevel, int fVerbose ); +static void Abc_ManResubCleanup( Abc_ManRes_t * p ); +static void Abc_ManResubPrint( Abc_ManRes_t * p ); + +// other procedures +static int Abc_ManResubCollectDivs( Abc_ManRes_t * p, Abc_Obj_t * pRoot, Vec_Ptr_t * vLeaves, int Required ); +static void Abc_ManResubSimulate( Vec_Ptr_t * vDivs, int nLeaves, Vec_Ptr_t * vSims, int nLeavesMax, int nWords ); +static void Abc_ManResubPrintDivs( Abc_ManRes_t * p, Abc_Obj_t * pRoot, Vec_Ptr_t * vLeaves ); + +static void Abc_ManResubDivsS( Abc_ManRes_t * p, int Required ); +static void Abc_ManResubDivsD( Abc_ManRes_t * p, int Required ); +static Dec_Graph_t * Abc_ManResubQuit( Abc_ManRes_t * p ); +static Dec_Graph_t * Abc_ManResubDivs0( Abc_ManRes_t * p ); +static Dec_Graph_t * Abc_ManResubDivs1( Abc_ManRes_t * p, int Required ); +static Dec_Graph_t * Abc_ManResubDivs12( Abc_ManRes_t * p, int Required ); +static Dec_Graph_t * Abc_ManResubDivs2( Abc_ManRes_t * p, int Required ); +static Dec_Graph_t * Abc_ManResubDivs3( Abc_ManRes_t * p, int Required ); + +static Vec_Ptr_t * Abc_CutFactorLarge( Abc_Obj_t * pNode, int nLeavesMax ); +static int Abc_CutVolumeCheck( Abc_Obj_t * pNode, Vec_Ptr_t * vLeaves ); + +// don't-care manager +extern void * Abc_NtkDontCareAlloc( int nVarsMax, int nLevels, int fVerbose, int fVeryVerbose ); +extern void Abc_NtkDontCareClear( void * p ); +extern void Abc_NtkDontCareFree( void * p ); +extern int Abc_NtkDontCareCompute( void * p, Abc_Obj_t * pNode, Vec_Ptr_t * vLeaves, unsigned * puTruth ); + +extern int s_ResubTime; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs incremental resynthesis of the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkResubstitute( Abc_Ntk_t * pNtk, int nCutMax, int nStepsMax, int nLevelsOdc, bool fUpdateLevel, bool fVerbose, bool fVeryVerbose ) +{ + ProgressBar * pProgress; + Abc_ManRes_t * pManRes; + Abc_ManCut_t * pManCut; + void * pManOdc = NULL; + Dec_Graph_t * pFForm; + Vec_Ptr_t * vLeaves; + Abc_Obj_t * pNode; + int clk, clkStart = clock(); + int i, nNodes; + + assert( Abc_NtkIsStrash(pNtk) ); + + // cleanup the AIG + Abc_AigCleanup(pNtk->pManFunc); + // start the managers + pManCut = Abc_NtkManCutStart( nCutMax, 100000, 100000, 100000 ); + pManRes = Abc_ManResubStart( nCutMax, ABC_RS_DIV1_MAX ); + if ( nLevelsOdc > 0 ) + pManOdc = Abc_NtkDontCareAlloc( nCutMax, nLevelsOdc, fVerbose, fVeryVerbose ); + + // compute the reverse levels if level update is requested + if ( fUpdateLevel ) + Abc_NtkStartReverseLevels( pNtk, 0 ); + + if ( Abc_NtkLatchNum(pNtk) ) + Abc_NtkForEachLatch(pNtk, pNode, i) + pNode->pNext = pNode->pData; + + // resynthesize each node once + pManRes->nNodesBeg = Abc_NtkNodeNum(pNtk); + nNodes = Abc_NtkObjNumMax(pNtk); + pProgress = Extra_ProgressBarStart( stdout, nNodes ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + // skip the constant node +// if ( Abc_NodeIsConst(pNode) ) +// continue; + // skip persistant nodes + if ( Abc_NodeIsPersistant(pNode) ) + continue; + // skip the nodes with many fanouts + if ( Abc_ObjFanoutNum(pNode) > 1000 ) + continue; + // stop if all nodes have been tried once + if ( i >= nNodes ) + break; + + // compute a reconvergence-driven cut +clk = clock(); + vLeaves = Abc_NodeFindCut( pManCut, pNode, 0 ); +// vLeaves = Abc_CutFactorLarge( pNode, nCutMax ); +pManRes->timeCut += clock() - clk; +/* + if ( fVerbose && vLeaves ) + printf( "Node %6d : Leaves = %3d. Volume = %3d.\n", pNode->Id, Vec_PtrSize(vLeaves), Abc_CutVolumeCheck(pNode, vLeaves) ); + if ( vLeaves == NULL ) + continue; +*/ + // get the don't-cares + if ( pManOdc ) + { +clk = clock(); + Abc_NtkDontCareClear( pManOdc ); + Abc_NtkDontCareCompute( pManOdc, pNode, vLeaves, pManRes->pCareSet ); +pManRes->timeTruth += clock() - clk; + } + + // evaluate this cut +clk = clock(); + pFForm = Abc_ManResubEval( pManRes, pNode, vLeaves, nStepsMax, fUpdateLevel, fVerbose ); +// Vec_PtrFree( vLeaves ); +// Abc_ManResubCleanup( pManRes ); +pManRes->timeRes += clock() - clk; + if ( pFForm == NULL ) + continue; + pManRes->nTotalGain += pManRes->nLastGain; +/* + if ( pManRes->nLeaves == 4 && pManRes->nMffc == 2 && pManRes->nLastGain == 1 ) + { + printf( "%6d : L = %2d. V = %2d. Mffc = %2d. Divs = %3d. Up = %3d. Un = %3d. B = %3d.\n", + pNode->Id, pManRes->nLeaves, Abc_CutVolumeCheck(pNode, vLeaves), pManRes->nMffc, pManRes->nDivs, + pManRes->vDivs1UP->nSize, pManRes->vDivs1UN->nSize, pManRes->vDivs1B->nSize ); + Abc_ManResubPrintDivs( pManRes, pNode, vLeaves ); + } +*/ + // acceptable replacement found, update the graph +clk = clock(); + Dec_GraphUpdateNetwork( pNode, pFForm, fUpdateLevel, pManRes->nLastGain ); +pManRes->timeNtk += clock() - clk; + Dec_GraphFree( pFForm ); + } + Extra_ProgressBarStop( pProgress ); +pManRes->timeTotal = clock() - clkStart; + pManRes->nNodesEnd = Abc_NtkNodeNum(pNtk); + + // print statistics + if ( fVerbose ) + Abc_ManResubPrint( pManRes ); + + // delete the managers + Abc_ManResubStop( pManRes ); + Abc_NtkManCutStop( pManCut ); + if ( pManOdc ) Abc_NtkDontCareFree( pManOdc ); + + // clean the data field + Abc_NtkForEachObj( pNtk, pNode, i ) + pNode->pData = NULL; + + if ( Abc_NtkLatchNum(pNtk) ) + Abc_NtkForEachLatch(pNtk, pNode, i) + pNode->pData = pNode->pNext, pNode->pNext = NULL; + + // put the nodes into the DFS order and reassign their IDs + Abc_NtkReassignIds( pNtk ); +// Abc_AigCheckFaninOrder( pNtk->pManFunc ); + // fix the levels + if ( fUpdateLevel ) + Abc_NtkStopReverseLevels( pNtk ); + else + Abc_NtkLevel( pNtk ); + // check + if ( !Abc_NtkCheck( pNtk ) ) + { + printf( "Abc_NtkRefactor: The network check has failed.\n" ); + return 0; + } +s_ResubTime = clock() - clkStart; + return 1; +} + + + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_ManRes_t * Abc_ManResubStart( int nLeavesMax, int nDivsMax ) +{ + Abc_ManRes_t * p; + unsigned * pData; + int i, k; + assert( sizeof(unsigned) == 4 ); + p = ALLOC( Abc_ManRes_t, 1 ); + memset( p, 0, sizeof(Abc_ManRes_t) ); + p->nLeavesMax = nLeavesMax; + p->nDivsMax = nDivsMax; + p->vDivs = Vec_PtrAlloc( p->nDivsMax ); + // allocate simulation info + p->nBits = (1 << p->nLeavesMax); + p->nWords = (p->nBits <= 32)? 1 : (p->nBits / 32); + p->pInfo = ALLOC( unsigned, p->nWords * (p->nDivsMax + 1) ); + memset( p->pInfo, 0, sizeof(unsigned) * p->nWords * p->nLeavesMax ); + p->vSims = Vec_PtrAlloc( p->nDivsMax ); + for ( i = 0; i < p->nDivsMax; i++ ) + Vec_PtrPush( p->vSims, p->pInfo + i * p->nWords ); + // assign the care set + p->pCareSet = p->pInfo + p->nDivsMax * p->nWords; + Abc_InfoFill( p->pCareSet, p->nWords ); + // set elementary truth tables + for ( k = 0; k < p->nLeavesMax; k++ ) + { + pData = p->vSims->pArray[k]; + for ( i = 0; i < p->nBits; i++ ) + if ( i & (1 << k) ) + pData[i>>5] |= (1 << (i&31)); + } + // create the remaining divisors + p->vDivs1UP = Vec_PtrAlloc( p->nDivsMax ); + p->vDivs1UN = Vec_PtrAlloc( p->nDivsMax ); + p->vDivs1B = Vec_PtrAlloc( p->nDivsMax ); + p->vDivs2UP0 = Vec_PtrAlloc( p->nDivsMax ); + p->vDivs2UP1 = Vec_PtrAlloc( p->nDivsMax ); + p->vDivs2UN0 = Vec_PtrAlloc( p->nDivsMax ); + p->vDivs2UN1 = Vec_PtrAlloc( p->nDivsMax ); + p->vTemp = Vec_PtrAlloc( p->nDivsMax ); + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ManResubStop( Abc_ManRes_t * p ) +{ + Vec_PtrFree( p->vDivs ); + Vec_PtrFree( p->vSims ); + Vec_PtrFree( p->vDivs1UP ); + Vec_PtrFree( p->vDivs1UN ); + Vec_PtrFree( p->vDivs1B ); + Vec_PtrFree( p->vDivs2UP0 ); + Vec_PtrFree( p->vDivs2UP1 ); + Vec_PtrFree( p->vDivs2UN0 ); + Vec_PtrFree( p->vDivs2UN1 ); + Vec_PtrFree( p->vTemp ); + free( p->pInfo ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ManResubPrint( Abc_ManRes_t * p ) +{ + printf( "Used constants = %6d. ", p->nUsedNodeC ); PRT( "Cuts ", p->timeCut ); + printf( "Used replacements = %6d. ", p->nUsedNode0 ); PRT( "Resub ", p->timeRes ); + printf( "Used single ORs = %6d. ", p->nUsedNode1Or ); PRT( " Div ", p->timeDiv ); + printf( "Used single ANDs = %6d. ", p->nUsedNode1And ); PRT( " Mffc ", p->timeMffc ); + printf( "Used double ORs = %6d. ", p->nUsedNode2Or ); PRT( " Sim ", p->timeSim ); + printf( "Used double ANDs = %6d. ", p->nUsedNode2And ); PRT( " 1 ", p->timeRes1 ); + printf( "Used OR-AND = %6d. ", p->nUsedNode2OrAnd ); PRT( " D ", p->timeResD ); + printf( "Used AND-OR = %6d. ", p->nUsedNode2AndOr ); PRT( " 2 ", p->timeRes2 ); + printf( "Used OR-2ANDs = %6d. ", p->nUsedNode3OrAnd ); PRT( "Truth ", p->timeTruth ); //PRT( " 3 ", p->timeRes3 ); + printf( "Used AND-2ORs = %6d. ", p->nUsedNode3AndOr ); PRT( "AIG ", p->timeNtk ); + printf( "TOTAL = %6d. ", p->nUsedNodeC + + p->nUsedNode0 + + p->nUsedNode1Or + + p->nUsedNode1And + + p->nUsedNode2Or + + p->nUsedNode2And + + p->nUsedNode2OrAnd + + p->nUsedNode2AndOr + + p->nUsedNode3OrAnd + + p->nUsedNode3AndOr + ); PRT( "TOTAL ", p->timeTotal ); + printf( "Total leaves = %8d.\n", p->nTotalLeaves ); + printf( "Total divisors = %8d.\n", p->nTotalDivs ); +// printf( "Total gain = %8d.\n", p->nTotalGain ); + printf( "Gain = %8d. (%6.2f %%).\n", p->nNodesBeg-p->nNodesEnd, 100.0*(p->nNodesBeg-p->nNodesEnd)/p->nNodesBeg ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ManResubCollectDivs_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vInternal ) +{ + // skip visited nodes + if ( Abc_NodeIsTravIdCurrent(pNode) ) + return; + Abc_NodeSetTravIdCurrent(pNode); + // collect the fanins + Abc_ManResubCollectDivs_rec( Abc_ObjFanin0(pNode), vInternal ); + Abc_ManResubCollectDivs_rec( Abc_ObjFanin1(pNode), vInternal ); + // collect the internal node + if ( pNode->fMarkA == 0 ) + Vec_PtrPush( vInternal, pNode ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_ManResubCollectDivs( Abc_ManRes_t * p, Abc_Obj_t * pRoot, Vec_Ptr_t * vLeaves, int Required ) +{ + Abc_Obj_t * pNode, * pFanout; + int i, k, Limit, Counter; + + Vec_PtrClear( p->vDivs1UP ); + Vec_PtrClear( p->vDivs1UN ); + Vec_PtrClear( p->vDivs1B ); + + // add the leaves of the cuts to the divisors + Vec_PtrClear( p->vDivs ); + Abc_NtkIncrementTravId( pRoot->pNtk ); + Vec_PtrForEachEntry( vLeaves, pNode, i ) + { + Vec_PtrPush( p->vDivs, pNode ); + Abc_NodeSetTravIdCurrent( pNode ); + } + + // mark nodes in the MFFC + Vec_PtrForEachEntry( p->vTemp, pNode, i ) + pNode->fMarkA = 1; + // collect the cone (without MFFC) + Abc_ManResubCollectDivs_rec( pRoot, p->vDivs ); + // unmark the current MFFC + Vec_PtrForEachEntry( p->vTemp, pNode, i ) + pNode->fMarkA = 0; + + // check if the number of divisors is not exceeded + if ( Vec_PtrSize(p->vDivs) - Vec_PtrSize(vLeaves) + Vec_PtrSize(p->vTemp) >= Vec_PtrSize(p->vSims) - p->nLeavesMax ) + return 0; + + // get the number of divisors to collect + Limit = Vec_PtrSize(p->vSims) - p->nLeavesMax - (Vec_PtrSize(p->vDivs) - Vec_PtrSize(vLeaves) + Vec_PtrSize(p->vTemp)); + + // explore the fanouts, which are not in the MFFC + Counter = 0; + Vec_PtrForEachEntry( p->vDivs, pNode, i ) + { + if ( Abc_ObjFanoutNum(pNode) > 100 ) + { +// printf( "%d ", Abc_ObjFanoutNum(pNode) ); + continue; + } + // if the fanout has both fanins in the set, add it + Abc_ObjForEachFanout( pNode, pFanout, k ) + { + if ( Abc_NodeIsTravIdCurrent(pFanout) || Abc_ObjIsCo(pFanout) || (int)pFanout->Level > Required ) + continue; + if ( Abc_NodeIsTravIdCurrent(Abc_ObjFanin0(pFanout)) && Abc_NodeIsTravIdCurrent(Abc_ObjFanin1(pFanout)) ) + { + if ( Abc_ObjFanin0(pFanout) == pRoot || Abc_ObjFanin1(pFanout) == pRoot ) + continue; + Vec_PtrPush( p->vDivs, pFanout ); + Abc_NodeSetTravIdCurrent( pFanout ); + // quit computing divisors if there is too many of them + if ( ++Counter == Limit ) + goto Quits; + } + } + } + +Quits : + // get the number of divisors + p->nDivs = Vec_PtrSize(p->vDivs); + + // add the nodes in the MFFC + Vec_PtrForEachEntry( p->vTemp, pNode, i ) + Vec_PtrPush( p->vDivs, pNode ); + assert( pRoot == Vec_PtrEntryLast(p->vDivs) ); + + assert( Vec_PtrSize(p->vDivs) - Vec_PtrSize(vLeaves) <= Vec_PtrSize(p->vSims) - p->nLeavesMax ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ManResubPrintDivs( Abc_ManRes_t * p, Abc_Obj_t * pRoot, Vec_Ptr_t * vLeaves ) +{ + Abc_Obj_t * pFanin, * pNode; + int i, k; + // print the nodes + Vec_PtrForEachEntry( p->vDivs, pNode, i ) + { + if ( i < Vec_PtrSize(vLeaves) ) + { + printf( "%6d : %c\n", pNode->Id, 'a'+i ); + continue; + } + printf( "%6d : %2d = ", pNode->Id, i ); + // find the first fanin + Vec_PtrForEachEntry( p->vDivs, pFanin, k ) + if ( Abc_ObjFanin0(pNode) == pFanin ) + break; + if ( k < Vec_PtrSize(vLeaves) ) + printf( "%c", 'a' + k ); + else + printf( "%d", k ); + printf( "%s ", Abc_ObjFaninC0(pNode)? "\'" : "" ); + // find the second fanin + Vec_PtrForEachEntry( p->vDivs, pFanin, k ) + if ( Abc_ObjFanin1(pNode) == pFanin ) + break; + if ( k < Vec_PtrSize(vLeaves) ) + printf( "%c", 'a' + k ); + else + printf( "%d", k ); + printf( "%s ", Abc_ObjFaninC1(pNode)? "\'" : "" ); + if ( pNode == pRoot ) + printf( " root" ); + printf( "\n" ); + } + printf( "\n" ); +} + + +/**Function************************************************************* + + Synopsis [Performs simulation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ManResubSimulate( Vec_Ptr_t * vDivs, int nLeaves, Vec_Ptr_t * vSims, int nLeavesMax, int nWords ) +{ + Abc_Obj_t * pObj; + unsigned * puData0, * puData1, * puData; + int i, k; + assert( Vec_PtrSize(vDivs) - nLeaves <= Vec_PtrSize(vSims) - nLeavesMax ); + // simulate + Vec_PtrForEachEntry( vDivs, pObj, i ) + { + if ( i < nLeaves ) + { // initialize the leaf + pObj->pData = Vec_PtrEntry( vSims, i ); + continue; + } + // set storage for the node's simulation info + pObj->pData = Vec_PtrEntry( vSims, i - nLeaves + nLeavesMax ); + // get pointer to the simulation info + puData = pObj->pData; + puData0 = Abc_ObjFanin0(pObj)->pData; + puData1 = Abc_ObjFanin1(pObj)->pData; + // simulate + if ( Abc_ObjFaninC0(pObj) && Abc_ObjFaninC1(pObj) ) + for ( k = 0; k < nWords; k++ ) + puData[k] = ~puData0[k] & ~puData1[k]; + else if ( Abc_ObjFaninC0(pObj) ) + for ( k = 0; k < nWords; k++ ) + puData[k] = ~puData0[k] & puData1[k]; + else if ( Abc_ObjFaninC1(pObj) ) + for ( k = 0; k < nWords; k++ ) + puData[k] = puData0[k] & ~puData1[k]; + else + for ( k = 0; k < nWords; k++ ) + puData[k] = puData0[k] & puData1[k]; + } + // normalize + Vec_PtrForEachEntry( vDivs, pObj, i ) + { + puData = pObj->pData; + pObj->fPhase = (puData[0] & 1); + if ( pObj->fPhase ) + for ( k = 0; k < nWords; k++ ) + puData[k] = ~puData[k]; + } +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_ManResubQuit0( Abc_Obj_t * pRoot, Abc_Obj_t * pObj ) +{ + Dec_Graph_t * pGraph; + Dec_Edge_t eRoot; + pGraph = Dec_GraphCreate( 1 ); + Dec_GraphNode( pGraph, 0 )->pFunc = pObj; + eRoot = Dec_EdgeCreate( 0, pObj->fPhase ); + Dec_GraphSetRoot( pGraph, eRoot ); + if ( pRoot->fPhase ) + Dec_GraphComplement( pGraph ); + return pGraph; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_ManResubQuit1( Abc_Obj_t * pRoot, Abc_Obj_t * pObj0, Abc_Obj_t * pObj1, int fOrGate ) +{ + Dec_Graph_t * pGraph; + Dec_Edge_t eRoot, eNode0, eNode1; + assert( pObj0 != pObj1 ); + assert( !Abc_ObjIsComplement(pObj0) ); + assert( !Abc_ObjIsComplement(pObj1) ); + pGraph = Dec_GraphCreate( 2 ); + Dec_GraphNode( pGraph, 0 )->pFunc = pObj0; + Dec_GraphNode( pGraph, 1 )->pFunc = pObj1; + eNode0 = Dec_EdgeCreate( 0, pObj0->fPhase ); + eNode1 = Dec_EdgeCreate( 1, pObj1->fPhase ); + if ( fOrGate ) + eRoot = Dec_GraphAddNodeOr( pGraph, eNode0, eNode1 ); + else + eRoot = Dec_GraphAddNodeAnd( pGraph, eNode0, eNode1 ); + Dec_GraphSetRoot( pGraph, eRoot ); + if ( pRoot->fPhase ) + Dec_GraphComplement( pGraph ); + return pGraph; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_ManResubQuit21( Abc_Obj_t * pRoot, Abc_Obj_t * pObj0, Abc_Obj_t * pObj1, Abc_Obj_t * pObj2, int fOrGate ) +{ + Dec_Graph_t * pGraph; + Dec_Edge_t eRoot, eNode0, eNode1, eNode2; + assert( pObj0 != pObj1 ); + assert( !Abc_ObjIsComplement(pObj0) ); + assert( !Abc_ObjIsComplement(pObj1) ); + assert( !Abc_ObjIsComplement(pObj2) ); + pGraph = Dec_GraphCreate( 3 ); + Dec_GraphNode( pGraph, 0 )->pFunc = pObj0; + Dec_GraphNode( pGraph, 1 )->pFunc = pObj1; + Dec_GraphNode( pGraph, 2 )->pFunc = pObj2; + eNode0 = Dec_EdgeCreate( 0, pObj0->fPhase ); + eNode1 = Dec_EdgeCreate( 1, pObj1->fPhase ); + eNode2 = Dec_EdgeCreate( 2, pObj2->fPhase ); + if ( fOrGate ) + { + eRoot = Dec_GraphAddNodeOr( pGraph, eNode0, eNode1 ); + eRoot = Dec_GraphAddNodeOr( pGraph, eNode2, eRoot ); + } + else + { + eRoot = Dec_GraphAddNodeAnd( pGraph, eNode0, eNode1 ); + eRoot = Dec_GraphAddNodeAnd( pGraph, eNode2, eRoot ); + } + Dec_GraphSetRoot( pGraph, eRoot ); + if ( pRoot->fPhase ) + Dec_GraphComplement( pGraph ); + return pGraph; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_ManResubQuit2( Abc_Obj_t * pRoot, Abc_Obj_t * pObj0, Abc_Obj_t * pObj1, Abc_Obj_t * pObj2, int fOrGate ) +{ + Dec_Graph_t * pGraph; + Dec_Edge_t eRoot, ePrev, eNode0, eNode1, eNode2; + assert( pObj0 != pObj1 ); + assert( pObj0 != pObj2 ); + assert( pObj1 != pObj2 ); + assert( !Abc_ObjIsComplement(pObj0) ); + pGraph = Dec_GraphCreate( 3 ); + Dec_GraphNode( pGraph, 0 )->pFunc = Abc_ObjRegular(pObj0); + Dec_GraphNode( pGraph, 1 )->pFunc = Abc_ObjRegular(pObj1); + Dec_GraphNode( pGraph, 2 )->pFunc = Abc_ObjRegular(pObj2); + eNode0 = Dec_EdgeCreate( 0, Abc_ObjRegular(pObj0)->fPhase ); + if ( Abc_ObjIsComplement(pObj1) && Abc_ObjIsComplement(pObj2) ) + { + eNode1 = Dec_EdgeCreate( 1, Abc_ObjRegular(pObj1)->fPhase ); + eNode2 = Dec_EdgeCreate( 2, Abc_ObjRegular(pObj2)->fPhase ); + ePrev = Dec_GraphAddNodeOr( pGraph, eNode1, eNode2 ); + } + else + { + eNode1 = Dec_EdgeCreate( 1, Abc_ObjRegular(pObj1)->fPhase ^ Abc_ObjIsComplement(pObj1) ); + eNode2 = Dec_EdgeCreate( 2, Abc_ObjRegular(pObj2)->fPhase ^ Abc_ObjIsComplement(pObj2) ); + ePrev = Dec_GraphAddNodeAnd( pGraph, eNode1, eNode2 ); + } + if ( fOrGate ) + eRoot = Dec_GraphAddNodeOr( pGraph, eNode0, ePrev ); + else + eRoot = Dec_GraphAddNodeAnd( pGraph, eNode0, ePrev ); + Dec_GraphSetRoot( pGraph, eRoot ); + if ( pRoot->fPhase ) + Dec_GraphComplement( pGraph ); + return pGraph; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_ManResubQuit3( Abc_Obj_t * pRoot, Abc_Obj_t * pObj0, Abc_Obj_t * pObj1, Abc_Obj_t * pObj2, Abc_Obj_t * pObj3, int fOrGate ) +{ + Dec_Graph_t * pGraph; + Dec_Edge_t eRoot, ePrev0, ePrev1, eNode0, eNode1, eNode2, eNode3; + assert( pObj0 != pObj1 ); + assert( pObj2 != pObj3 ); + pGraph = Dec_GraphCreate( 4 ); + Dec_GraphNode( pGraph, 0 )->pFunc = Abc_ObjRegular(pObj0); + Dec_GraphNode( pGraph, 1 )->pFunc = Abc_ObjRegular(pObj1); + Dec_GraphNode( pGraph, 2 )->pFunc = Abc_ObjRegular(pObj2); + Dec_GraphNode( pGraph, 3 )->pFunc = Abc_ObjRegular(pObj3); + if ( Abc_ObjIsComplement(pObj0) && Abc_ObjIsComplement(pObj1) ) + { + eNode0 = Dec_EdgeCreate( 0, Abc_ObjRegular(pObj0)->fPhase ); + eNode1 = Dec_EdgeCreate( 1, Abc_ObjRegular(pObj1)->fPhase ); + ePrev0 = Dec_GraphAddNodeOr( pGraph, eNode0, eNode1 ); + if ( Abc_ObjIsComplement(pObj2) && Abc_ObjIsComplement(pObj3) ) + { + eNode2 = Dec_EdgeCreate( 2, Abc_ObjRegular(pObj2)->fPhase ); + eNode3 = Dec_EdgeCreate( 3, Abc_ObjRegular(pObj3)->fPhase ); + ePrev1 = Dec_GraphAddNodeOr( pGraph, eNode2, eNode3 ); + } + else + { + eNode2 = Dec_EdgeCreate( 2, Abc_ObjRegular(pObj2)->fPhase ^ Abc_ObjIsComplement(pObj2) ); + eNode3 = Dec_EdgeCreate( 3, Abc_ObjRegular(pObj3)->fPhase ^ Abc_ObjIsComplement(pObj3) ); + ePrev1 = Dec_GraphAddNodeAnd( pGraph, eNode2, eNode3 ); + } + } + else + { + eNode0 = Dec_EdgeCreate( 0, Abc_ObjRegular(pObj0)->fPhase ^ Abc_ObjIsComplement(pObj0) ); + eNode1 = Dec_EdgeCreate( 1, Abc_ObjRegular(pObj1)->fPhase ^ Abc_ObjIsComplement(pObj1) ); + ePrev0 = Dec_GraphAddNodeAnd( pGraph, eNode0, eNode1 ); + if ( Abc_ObjIsComplement(pObj2) && Abc_ObjIsComplement(pObj3) ) + { + eNode2 = Dec_EdgeCreate( 2, Abc_ObjRegular(pObj2)->fPhase ); + eNode3 = Dec_EdgeCreate( 3, Abc_ObjRegular(pObj3)->fPhase ); + ePrev1 = Dec_GraphAddNodeOr( pGraph, eNode2, eNode3 ); + } + else + { + eNode2 = Dec_EdgeCreate( 2, Abc_ObjRegular(pObj2)->fPhase ^ Abc_ObjIsComplement(pObj2) ); + eNode3 = Dec_EdgeCreate( 3, Abc_ObjRegular(pObj3)->fPhase ^ Abc_ObjIsComplement(pObj3) ); + ePrev1 = Dec_GraphAddNodeAnd( pGraph, eNode2, eNode3 ); + } + } + if ( fOrGate ) + eRoot = Dec_GraphAddNodeOr( pGraph, ePrev0, ePrev1 ); + else + eRoot = Dec_GraphAddNodeAnd( pGraph, ePrev0, ePrev1 ); + Dec_GraphSetRoot( pGraph, eRoot ); + if ( pRoot->fPhase ) + Dec_GraphComplement( pGraph ); + return pGraph; +} + + + + +/**Function************************************************************* + + Synopsis [Derives single-node unate/binate divisors.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ManResubDivsS( Abc_ManRes_t * p, int Required ) +{ + Abc_Obj_t * pObj; + unsigned * puData, * puDataR; + int i, w; + Vec_PtrClear( p->vDivs1UP ); + Vec_PtrClear( p->vDivs1UN ); + Vec_PtrClear( p->vDivs1B ); + puDataR = p->pRoot->pData; + Vec_PtrForEachEntryStop( p->vDivs, pObj, i, p->nDivs ) + { + if ( (int)pObj->Level > Required - 1 ) + continue; + + puData = pObj->pData; + // check positive containment + for ( w = 0; w < p->nWords; w++ ) +// if ( puData[w] & ~puDataR[w] ) + if ( puData[w] & ~puDataR[w] & p->pCareSet[w] ) // care set + break; + if ( w == p->nWords ) + { + Vec_PtrPush( p->vDivs1UP, pObj ); + continue; + } + // check negative containment + for ( w = 0; w < p->nWords; w++ ) +// if ( ~puData[w] & puDataR[w] ) + if ( ~puData[w] & puDataR[w] & p->pCareSet[w] ) // care set + break; + if ( w == p->nWords ) + { + Vec_PtrPush( p->vDivs1UN, pObj ); + continue; + } + // add the node to binates + Vec_PtrPush( p->vDivs1B, pObj ); + } +} + +/**Function************************************************************* + + Synopsis [Derives double-node unate/binate divisors.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ManResubDivsD( Abc_ManRes_t * p, int Required ) +{ + Abc_Obj_t * pObj0, * pObj1; + unsigned * puData0, * puData1, * puDataR; + int i, k, w; + Vec_PtrClear( p->vDivs2UP0 ); + Vec_PtrClear( p->vDivs2UP1 ); + Vec_PtrClear( p->vDivs2UN0 ); + Vec_PtrClear( p->vDivs2UN1 ); + puDataR = p->pRoot->pData; + Vec_PtrForEachEntry( p->vDivs1B, pObj0, i ) + { + if ( (int)pObj0->Level > Required - 2 ) + continue; + + puData0 = pObj0->pData; + Vec_PtrForEachEntryStart( p->vDivs1B, pObj1, k, i + 1 ) + { + if ( (int)pObj1->Level > Required - 2 ) + continue; + + puData1 = pObj1->pData; + + if ( Vec_PtrSize(p->vDivs2UP0) < ABC_RS_DIV2_MAX ) + { + // get positive unate divisors + for ( w = 0; w < p->nWords; w++ ) +// if ( (puData0[w] & puData1[w]) & ~puDataR[w] ) + if ( (puData0[w] & puData1[w]) & ~puDataR[w] & p->pCareSet[w] ) // care set + break; + if ( w == p->nWords ) + { + Vec_PtrPush( p->vDivs2UP0, pObj0 ); + Vec_PtrPush( p->vDivs2UP1, pObj1 ); + } + for ( w = 0; w < p->nWords; w++ ) +// if ( (~puData0[w] & puData1[w]) & ~puDataR[w] ) + if ( (~puData0[w] & puData1[w]) & ~puDataR[w] & p->pCareSet[w] ) // care set + break; + if ( w == p->nWords ) + { + Vec_PtrPush( p->vDivs2UP0, Abc_ObjNot(pObj0) ); + Vec_PtrPush( p->vDivs2UP1, pObj1 ); + } + for ( w = 0; w < p->nWords; w++ ) +// if ( (puData0[w] & ~puData1[w]) & ~puDataR[w] ) + if ( (puData0[w] & ~puData1[w]) & ~puDataR[w] & p->pCareSet[w] ) // care set + break; + if ( w == p->nWords ) + { + Vec_PtrPush( p->vDivs2UP0, pObj0 ); + Vec_PtrPush( p->vDivs2UP1, Abc_ObjNot(pObj1) ); + } + for ( w = 0; w < p->nWords; w++ ) +// if ( (puData0[w] | puData1[w]) & ~puDataR[w] ) + if ( (puData0[w] | puData1[w]) & ~puDataR[w] & p->pCareSet[w] ) // care set + break; + if ( w == p->nWords ) + { + Vec_PtrPush( p->vDivs2UP0, Abc_ObjNot(pObj0) ); + Vec_PtrPush( p->vDivs2UP1, Abc_ObjNot(pObj1) ); + } + } + + if ( Vec_PtrSize(p->vDivs2UN0) < ABC_RS_DIV2_MAX ) + { + // get negative unate divisors + for ( w = 0; w < p->nWords; w++ ) +// if ( ~(puData0[w] & puData1[w]) & puDataR[w] ) + if ( ~(puData0[w] & puData1[w]) & puDataR[w] & p->pCareSet[w] ) // care set + break; + if ( w == p->nWords ) + { + Vec_PtrPush( p->vDivs2UN0, pObj0 ); + Vec_PtrPush( p->vDivs2UN1, pObj1 ); + } + for ( w = 0; w < p->nWords; w++ ) +// if ( ~(~puData0[w] & puData1[w]) & puDataR[w] ) + if ( ~(~puData0[w] & puData1[w]) & puDataR[w] & p->pCareSet[w] ) // care set + break; + if ( w == p->nWords ) + { + Vec_PtrPush( p->vDivs2UN0, Abc_ObjNot(pObj0) ); + Vec_PtrPush( p->vDivs2UN1, pObj1 ); + } + for ( w = 0; w < p->nWords; w++ ) +// if ( ~(puData0[w] & ~puData1[w]) & puDataR[w] ) + if ( ~(puData0[w] & ~puData1[w]) & puDataR[w] & p->pCareSet[w] ) // care set + break; + if ( w == p->nWords ) + { + Vec_PtrPush( p->vDivs2UN0, pObj0 ); + Vec_PtrPush( p->vDivs2UN1, Abc_ObjNot(pObj1) ); + } + for ( w = 0; w < p->nWords; w++ ) +// if ( ~(puData0[w] | puData1[w]) & puDataR[w] ) + if ( ~(puData0[w] | puData1[w]) & puDataR[w] & p->pCareSet[w] ) // care set + break; + if ( w == p->nWords ) + { + Vec_PtrPush( p->vDivs2UN0, Abc_ObjNot(pObj0) ); + Vec_PtrPush( p->vDivs2UN1, Abc_ObjNot(pObj1) ); + } + } + } + } +// printf( "%d %d ", Vec_PtrSize(p->vDivs2UP0), Vec_PtrSize(p->vDivs2UN0) ); +} + + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_ManResubQuit( Abc_ManRes_t * p ) +{ + Dec_Graph_t * pGraph; + unsigned * upData; + int w; + upData = p->pRoot->pData; + for ( w = 0; w < p->nWords; w++ ) +// if ( upData[w] ) + if ( upData[w] & p->pCareSet[w] ) // care set + break; + if ( w != p->nWords ) + return NULL; + // get constant node graph + if ( p->pRoot->fPhase ) + pGraph = Dec_GraphCreateConst1(); + else + pGraph = Dec_GraphCreateConst0(); + return pGraph; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_ManResubDivs0( Abc_ManRes_t * p ) +{ + Abc_Obj_t * pObj; + unsigned * puData, * puDataR; + int i, w; + puDataR = p->pRoot->pData; + Vec_PtrForEachEntryStop( p->vDivs, pObj, i, p->nDivs ) + { + puData = pObj->pData; + for ( w = 0; w < p->nWords; w++ ) +// if ( puData[w] != puDataR[w] ) + if ( (puData[w] ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + if ( w == p->nWords ) + return Abc_ManResubQuit0( p->pRoot, pObj ); + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_ManResubDivs1( Abc_ManRes_t * p, int Required ) +{ + Abc_Obj_t * pObj0, * pObj1; + unsigned * puData0, * puData1, * puDataR; + int i, k, w; + puDataR = p->pRoot->pData; + // check positive unate divisors + Vec_PtrForEachEntry( p->vDivs1UP, pObj0, i ) + { + puData0 = pObj0->pData; + Vec_PtrForEachEntryStart( p->vDivs1UP, pObj1, k, i + 1 ) + { + puData1 = pObj1->pData; + for ( w = 0; w < p->nWords; w++ ) +// if ( (puData0[w] | puData1[w]) != puDataR[w] ) + if ( ((puData0[w] | puData1[w]) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + if ( w == p->nWords ) + { + p->nUsedNode1Or++; + return Abc_ManResubQuit1( p->pRoot, pObj0, pObj1, 1 ); + } + } + } + // check negative unate divisors + Vec_PtrForEachEntry( p->vDivs1UN, pObj0, i ) + { + puData0 = pObj0->pData; + Vec_PtrForEachEntryStart( p->vDivs1UN, pObj1, k, i + 1 ) + { + puData1 = pObj1->pData; + for ( w = 0; w < p->nWords; w++ ) +// if ( (puData0[w] & puData1[w]) != puDataR[w] ) + if ( ((puData0[w] & puData1[w]) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + if ( w == p->nWords ) + { + p->nUsedNode1And++; + return Abc_ManResubQuit1( p->pRoot, pObj0, pObj1, 0 ); + } + } + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_ManResubDivs12( Abc_ManRes_t * p, int Required ) +{ + Abc_Obj_t * pObj0, * pObj1, * pObj2, * pObjMax, * pObjMin0, * pObjMin1; + unsigned * puData0, * puData1, * puData2, * puDataR; + int i, k, j, w, LevelMax; + puDataR = p->pRoot->pData; + // check positive unate divisors + Vec_PtrForEachEntry( p->vDivs1UP, pObj0, i ) + { + puData0 = pObj0->pData; + Vec_PtrForEachEntryStart( p->vDivs1UP, pObj1, k, i + 1 ) + { + puData1 = pObj1->pData; + Vec_PtrForEachEntryStart( p->vDivs1UP, pObj2, j, k + 1 ) + { + puData2 = pObj2->pData; + for ( w = 0; w < p->nWords; w++ ) +// if ( (puData0[w] | puData1[w] | puData2[w]) != puDataR[w] ) + if ( ((puData0[w] | puData1[w] | puData2[w]) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + if ( w == p->nWords ) + { + LevelMax = ABC_MAX( pObj0->Level, ABC_MAX(pObj1->Level, pObj2->Level) ); + assert( LevelMax <= Required - 1 ); + + pObjMax = NULL; + if ( (int)pObj0->Level == LevelMax ) + pObjMax = pObj0, pObjMin0 = pObj1, pObjMin1 = pObj2; + if ( (int)pObj1->Level == LevelMax ) + { + if ( pObjMax ) continue; + pObjMax = pObj1, pObjMin0 = pObj0, pObjMin1 = pObj2; + } + if ( (int)pObj2->Level == LevelMax ) + { + if ( pObjMax ) continue; + pObjMax = pObj2, pObjMin0 = pObj0, pObjMin1 = pObj1; + } + + p->nUsedNode2Or++; + return Abc_ManResubQuit21( p->pRoot, pObjMin0, pObjMin1, pObjMax, 1 ); + } + } + } + } + // check negative unate divisors + Vec_PtrForEachEntry( p->vDivs1UN, pObj0, i ) + { + puData0 = pObj0->pData; + Vec_PtrForEachEntryStart( p->vDivs1UN, pObj1, k, i + 1 ) + { + puData1 = pObj1->pData; + Vec_PtrForEachEntryStart( p->vDivs1UN, pObj2, j, k + 1 ) + { + puData2 = pObj2->pData; + for ( w = 0; w < p->nWords; w++ ) +// if ( (puData0[w] & puData1[w] & puData2[w]) != puDataR[w] ) + if ( ((puData0[w] & puData1[w] & puData2[w]) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + if ( w == p->nWords ) + { + LevelMax = ABC_MAX( pObj0->Level, ABC_MAX(pObj1->Level, pObj2->Level) ); + assert( LevelMax <= Required - 1 ); + + pObjMax = NULL; + if ( (int)pObj0->Level == LevelMax ) + pObjMax = pObj0, pObjMin0 = pObj1, pObjMin1 = pObj2; + if ( (int)pObj1->Level == LevelMax ) + { + if ( pObjMax ) continue; + pObjMax = pObj1, pObjMin0 = pObj0, pObjMin1 = pObj2; + } + if ( (int)pObj2->Level == LevelMax ) + { + if ( pObjMax ) continue; + pObjMax = pObj2, pObjMin0 = pObj0, pObjMin1 = pObj1; + } + + p->nUsedNode2And++; + return Abc_ManResubQuit21( p->pRoot, pObjMin0, pObjMin1, pObjMax, 0 ); + } + } + } + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_ManResubDivs2( Abc_ManRes_t * p, int Required ) +{ + Abc_Obj_t * pObj0, * pObj1, * pObj2; + unsigned * puData0, * puData1, * puData2, * puDataR; + int i, k, w; + puDataR = p->pRoot->pData; + // check positive unate divisors + Vec_PtrForEachEntry( p->vDivs1UP, pObj0, i ) + { + puData0 = pObj0->pData; + Vec_PtrForEachEntry( p->vDivs2UP0, pObj1, k ) + { + pObj2 = Vec_PtrEntry( p->vDivs2UP1, k ); + + puData1 = Abc_ObjRegular(pObj1)->pData; + puData2 = Abc_ObjRegular(pObj2)->pData; + if ( Abc_ObjIsComplement(pObj1) && Abc_ObjIsComplement(pObj2) ) + { + for ( w = 0; w < p->nWords; w++ ) +// if ( (puData0[w] | (puData1[w] | puData2[w])) != puDataR[w] ) + if ( ((puData0[w] | (puData1[w] | puData2[w])) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + } + else if ( Abc_ObjIsComplement(pObj1) ) + { + for ( w = 0; w < p->nWords; w++ ) +// if ( (puData0[w] | (~puData1[w] & puData2[w])) != puDataR[w] ) + if ( ((puData0[w] | (~puData1[w] & puData2[w])) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + } + else if ( Abc_ObjIsComplement(pObj2) ) + { + for ( w = 0; w < p->nWords; w++ ) +// if ( (puData0[w] | (puData1[w] & ~puData2[w])) != puDataR[w] ) + if ( ((puData0[w] | (puData1[w] & ~puData2[w])) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + } + else + { + for ( w = 0; w < p->nWords; w++ ) +// if ( (puData0[w] | (puData1[w] & puData2[w])) != puDataR[w] ) + if ( ((puData0[w] | (puData1[w] & puData2[w])) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + } + if ( w == p->nWords ) + { + p->nUsedNode2OrAnd++; + return Abc_ManResubQuit2( p->pRoot, pObj0, pObj1, pObj2, 1 ); + } + } + } + // check negative unate divisors + Vec_PtrForEachEntry( p->vDivs1UN, pObj0, i ) + { + puData0 = pObj0->pData; + Vec_PtrForEachEntry( p->vDivs2UN0, pObj1, k ) + { + pObj2 = Vec_PtrEntry( p->vDivs2UN1, k ); + + puData1 = Abc_ObjRegular(pObj1)->pData; + puData2 = Abc_ObjRegular(pObj2)->pData; + if ( Abc_ObjIsComplement(pObj1) && Abc_ObjIsComplement(pObj2) ) + { + for ( w = 0; w < p->nWords; w++ ) +// if ( (puData0[w] & (puData1[w] | puData2[w])) != puDataR[w] ) + if ( ((puData0[w] & (puData1[w] | puData2[w])) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + } + else if ( Abc_ObjIsComplement(pObj1) ) + { + for ( w = 0; w < p->nWords; w++ ) +// if ( (puData0[w] & (~puData1[w] & puData2[w])) != puDataR[w] ) + if ( ((puData0[w] & (~puData1[w] & puData2[w])) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + } + else if ( Abc_ObjIsComplement(pObj2) ) + { + for ( w = 0; w < p->nWords; w++ ) +// if ( (puData0[w] & (puData1[w] & ~puData2[w])) != puDataR[w] ) + if ( ((puData0[w] & (puData1[w] & ~puData2[w])) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + } + else + { + for ( w = 0; w < p->nWords; w++ ) +// if ( (puData0[w] & (puData1[w] & puData2[w])) != puDataR[w] ) + if ( ((puData0[w] & (puData1[w] & puData2[w])) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + } + if ( w == p->nWords ) + { + p->nUsedNode2AndOr++; + return Abc_ManResubQuit2( p->pRoot, pObj0, pObj1, pObj2, 0 ); + } + } + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_ManResubDivs3( Abc_ManRes_t * p, int Required ) +{ + Abc_Obj_t * pObj0, * pObj1, * pObj2, * pObj3; + unsigned * puData0, * puData1, * puData2, * puData3, * puDataR; + int i, k, w, Flag; + puDataR = p->pRoot->pData; + // check positive unate divisors + Vec_PtrForEachEntry( p->vDivs2UP0, pObj0, i ) + { + pObj1 = Vec_PtrEntry( p->vDivs2UP1, i ); + puData0 = Abc_ObjRegular(pObj0)->pData; + puData1 = Abc_ObjRegular(pObj1)->pData; + Flag = (Abc_ObjIsComplement(pObj0) << 3) | (Abc_ObjIsComplement(pObj1) << 2); + + Vec_PtrForEachEntryStart( p->vDivs2UP0, pObj2, k, i + 1 ) + { + pObj3 = Vec_PtrEntry( p->vDivs2UP1, k ); + puData2 = Abc_ObjRegular(pObj2)->pData; + puData3 = Abc_ObjRegular(pObj3)->pData; + + Flag = (Flag & 12) | (Abc_ObjIsComplement(pObj2) << 1) | Abc_ObjIsComplement(pObj3); + assert( Flag < 16 ); + switch( Flag ) + { + case 0: // 0000 + for ( w = 0; w < p->nWords; w++ ) +// if ( ((puData0[w] & puData1[w]) | (puData2[w] & puData3[w])) != puDataR[w] ) + if ( (((puData0[w] & puData1[w]) | (puData2[w] & puData3[w])) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + break; + case 1: // 0001 + for ( w = 0; w < p->nWords; w++ ) +// if ( ((puData0[w] & puData1[w]) | (puData2[w] & ~puData3[w])) != puDataR[w] ) + if ( (((puData0[w] & puData1[w]) | (puData2[w] & ~puData3[w])) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + break; + case 2: // 0010 + for ( w = 0; w < p->nWords; w++ ) +// if ( ((puData0[w] & puData1[w]) | (~puData2[w] & puData3[w])) != puDataR[w] ) + if ( (((puData0[w] & puData1[w]) | (~puData2[w] & puData3[w])) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + break; + case 3: // 0011 + for ( w = 0; w < p->nWords; w++ ) +// if ( ((puData0[w] & puData1[w]) | (puData2[w] | puData3[w])) != puDataR[w] ) + if ( (((puData0[w] & puData1[w]) | (puData2[w] | puData3[w])) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + break; + + case 4: // 0100 + for ( w = 0; w < p->nWords; w++ ) +// if ( ((puData0[w] & ~puData1[w]) | (puData2[w] & puData3[w])) != puDataR[w] ) + if ( (((puData0[w] & ~puData1[w]) | (puData2[w] & puData3[w])) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + break; + case 5: // 0101 + for ( w = 0; w < p->nWords; w++ ) +// if ( ((puData0[w] & ~puData1[w]) | (puData2[w] & ~puData3[w])) != puDataR[w] ) + if ( (((puData0[w] & ~puData1[w]) | (puData2[w] & ~puData3[w])) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + break; + case 6: // 0110 + for ( w = 0; w < p->nWords; w++ ) +// if ( ((puData0[w] & ~puData1[w]) | (~puData2[w] & puData3[w])) != puDataR[w] ) + if ( (((puData0[w] & ~puData1[w]) | (~puData2[w] & puData3[w])) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + break; + case 7: // 0111 + for ( w = 0; w < p->nWords; w++ ) +// if ( ((puData0[w] & ~puData1[w]) | (puData2[w] | puData3[w])) != puDataR[w] ) + if ( (((puData0[w] & ~puData1[w]) | (puData2[w] | puData3[w])) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + break; + + case 8: // 1000 + for ( w = 0; w < p->nWords; w++ ) +// if ( ((~puData0[w] & puData1[w]) | (puData2[w] & puData3[w])) != puDataR[w] ) + if ( (((~puData0[w] & puData1[w]) | (puData2[w] & puData3[w])) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + break; + case 9: // 1001 + for ( w = 0; w < p->nWords; w++ ) +// if ( ((~puData0[w] & puData1[w]) | (puData2[w] & ~puData3[w])) != puDataR[w] ) + if ( (((~puData0[w] & puData1[w]) | (puData2[w] & ~puData3[w])) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + break; + case 10: // 1010 + for ( w = 0; w < p->nWords; w++ ) +// if ( ((~puData0[w] & puData1[w]) | (~puData2[w] & puData3[w])) != puDataR[w] ) + if ( (((~puData0[w] & puData1[w]) | (~puData2[w] & puData3[w])) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + break; + case 11: // 1011 + for ( w = 0; w < p->nWords; w++ ) +// if ( ((~puData0[w] & puData1[w]) | (puData2[w] | puData3[w])) != puDataR[w] ) + if ( (((~puData0[w] & puData1[w]) | (puData2[w] | puData3[w])) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + break; + + case 12: // 1100 + for ( w = 0; w < p->nWords; w++ ) +// if ( ((puData0[w] | puData1[w]) | (puData2[w] & puData3[w])) != puDataR[w] ) + if ( (((puData0[w] | puData1[w]) | (puData2[w] & puData3[w])) ^ puDataR[w]) & p->pCareSet[w] ) // care set + break; + break; + case 13: // 1101 + for ( w = 0; w < p->nWords; w++ ) +// if ( ((puData0[w] | puData1[w]) | (puData2[w] & ~puData3[w])) != puDataR[w] ) + if ( (((puData0[w] | puData1[w]) | (puData2[w] & ~puData3[w])) ^ puDataR[w]) & p->pCareSet[w] ) + break; + break; + case 14: // 1110 + for ( w = 0; w < p->nWords; w++ ) +// if ( ((puData0[w] | puData1[w]) | (~puData2[w] & puData3[w])) != puDataR[w] ) + if ( (((puData0[w] | puData1[w]) | (~puData2[w] & puData3[w])) ^ puDataR[w]) & p->pCareSet[w] ) + break; + break; + case 15: // 1111 + for ( w = 0; w < p->nWords; w++ ) +// if ( ((puData0[w] | puData1[w]) | (puData2[w] | puData3[w])) != puDataR[w] ) + if ( (((puData0[w] | puData1[w]) | (puData2[w] | puData3[w])) ^ puDataR[w]) & p->pCareSet[w] ) + break; + break; + + } + if ( w == p->nWords ) + { + p->nUsedNode3OrAnd++; + return Abc_ManResubQuit3( p->pRoot, pObj0, pObj1, pObj2, pObj3, 1 ); + } + } + } +/* + // check negative unate divisors + Vec_PtrForEachEntry( p->vDivs2UN0, pObj0, i ) + { + pObj1 = Vec_PtrEntry( p->vDivs2UN1, i ); + puData0 = Abc_ObjRegular(pObj0)->pData; + puData1 = Abc_ObjRegular(pObj1)->pData; + Flag = (Abc_ObjIsComplement(pObj0) << 3) | (Abc_ObjIsComplement(pObj1) << 2); + + Vec_PtrForEachEntryStart( p->vDivs2UN0, pObj2, k, i + 1 ) + { + pObj3 = Vec_PtrEntry( p->vDivs2UN1, k ); + puData2 = Abc_ObjRegular(pObj2)->pData; + puData3 = Abc_ObjRegular(pObj3)->pData; + + Flag = (Flag & 12) | (Abc_ObjIsComplement(pObj2) << 1) | Abc_ObjIsComplement(pObj3); + assert( Flag < 16 ); + switch( Flag ) + { + case 0: // 0000 + for ( w = 0; w < p->nWords; w++ ) + if ( ((puData0[w] & puData1[w]) & (puData2[w] & puData3[w])) != puDataR[w] ) + break; + break; + case 1: // 0001 + for ( w = 0; w < p->nWords; w++ ) + if ( ((puData0[w] & puData1[w]) & (puData2[w] & ~puData3[w])) != puDataR[w] ) + break; + break; + case 2: // 0010 + for ( w = 0; w < p->nWords; w++ ) + if ( ((puData0[w] & puData1[w]) & (~puData2[w] & puData3[w])) != puDataR[w] ) + break; + break; + case 3: // 0011 + for ( w = 0; w < p->nWords; w++ ) + if ( ((puData0[w] & puData1[w]) & (puData2[w] | puData3[w])) != puDataR[w] ) + break; + break; + + case 4: // 0100 + for ( w = 0; w < p->nWords; w++ ) + if ( ((puData0[w] & ~puData1[w]) & (puData2[w] & puData3[w])) != puDataR[w] ) + break; + break; + case 5: // 0101 + for ( w = 0; w < p->nWords; w++ ) + if ( ((puData0[w] & ~puData1[w]) & (puData2[w] & ~puData3[w])) != puDataR[w] ) + break; + break; + case 6: // 0110 + for ( w = 0; w < p->nWords; w++ ) + if ( ((puData0[w] & ~puData1[w]) & (~puData2[w] & puData3[w])) != puDataR[w] ) + break; + break; + case 7: // 0111 + for ( w = 0; w < p->nWords; w++ ) + if ( ((puData0[w] & ~puData1[w]) & (puData2[w] | puData3[w])) != puDataR[w] ) + break; + break; + + case 8: // 1000 + for ( w = 0; w < p->nWords; w++ ) + if ( ((~puData0[w] & puData1[w]) & (puData2[w] & puData3[w])) != puDataR[w] ) + break; + break; + case 9: // 1001 + for ( w = 0; w < p->nWords; w++ ) + if ( ((~puData0[w] & puData1[w]) & (puData2[w] & ~puData3[w])) != puDataR[w] ) + break; + break; + case 10: // 1010 + for ( w = 0; w < p->nWords; w++ ) + if ( ((~puData0[w] & puData1[w]) & (~puData2[w] & puData3[w])) != puDataR[w] ) + break; + break; + case 11: // 1011 + for ( w = 0; w < p->nWords; w++ ) + if ( ((~puData0[w] & puData1[w]) & (puData2[w] | puData3[w])) != puDataR[w] ) + break; + break; + + case 12: // 1100 + for ( w = 0; w < p->nWords; w++ ) + if ( ((puData0[w] | puData1[w]) & (puData2[w] & puData3[w])) != puDataR[w] ) + break; + break; + case 13: // 1101 + for ( w = 0; w < p->nWords; w++ ) + if ( ((puData0[w] | puData1[w]) & (puData2[w] & ~puData3[w])) != puDataR[w] ) + break; + break; + case 14: // 1110 + for ( w = 0; w < p->nWords; w++ ) + if ( ((puData0[w] | puData1[w]) & (~puData2[w] & puData3[w])) != puDataR[w] ) + break; + break; + case 15: // 1111 + for ( w = 0; w < p->nWords; w++ ) + if ( ((puData0[w] | puData1[w]) & (puData2[w] | puData3[w])) != puDataR[w] ) + break; + break; + + } + if ( w == p->nWords ) + { + p->nUsedNode3AndOr++; + return Abc_ManResubQuit3( p->pRoot, pObj0, pObj1, pObj2, pObj3, 0 ); + } + } + } +*/ + return NULL; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ManResubCleanup( Abc_ManRes_t * p ) +{ + Abc_Obj_t * pObj; + int i; + Vec_PtrForEachEntry( p->vDivs, pObj, i ) + pObj->pData = NULL; + Vec_PtrClear( p->vDivs ); + p->pRoot = NULL; +} + +/**Function************************************************************* + + Synopsis [Evaluates resubstution of one cut.] + + Description [Returns the graph to add if any.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Abc_ManResubEval( Abc_ManRes_t * p, Abc_Obj_t * pRoot, Vec_Ptr_t * vLeaves, int nSteps, bool fUpdateLevel, bool fVerbose ) +{ + extern int Abc_NodeMffsInside( Abc_Obj_t * pNode, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vInside ); + Dec_Graph_t * pGraph; + int Required; + int clk; + + Required = fUpdateLevel? Abc_ObjRequiredLevel(pRoot) : ABC_INFINITY; + + assert( nSteps >= 0 ); + assert( nSteps <= 3 ); + p->pRoot = pRoot; + p->nLeaves = Vec_PtrSize(vLeaves); + p->nLastGain = -1; + + // collect the MFFC +clk = clock(); + p->nMffc = Abc_NodeMffsInside( pRoot, vLeaves, p->vTemp ); +p->timeMffc += clock() - clk; + assert( p->nMffc > 0 ); + + // collect the divisor nodes +clk = clock(); + if ( !Abc_ManResubCollectDivs( p, pRoot, vLeaves, Required ) ) + return NULL; + p->timeDiv += clock() - clk; + + p->nTotalDivs += p->nDivs; + p->nTotalLeaves += p->nLeaves; + + // simulate the nodes +clk = clock(); + Abc_ManResubSimulate( p->vDivs, p->nLeaves, p->vSims, p->nLeavesMax, p->nWords ); +p->timeSim += clock() - clk; + +clk = clock(); + // consider constants + if ( pGraph = Abc_ManResubQuit( p ) ) + { + p->nUsedNodeC++; + p->nLastGain = p->nMffc; + return pGraph; + } + + // consider equal nodes + if ( pGraph = Abc_ManResubDivs0( p ) ) + { +p->timeRes1 += clock() - clk; + p->nUsedNode0++; + p->nLastGain = p->nMffc; + return pGraph; + } + if ( nSteps == 0 || p->nMffc == 1 ) + { +p->timeRes1 += clock() - clk; + return NULL; + } + + // get the one level divisors + Abc_ManResubDivsS( p, Required ); + + // consider one node + if ( pGraph = Abc_ManResubDivs1( p, Required ) ) + { +p->timeRes1 += clock() - clk; + p->nLastGain = p->nMffc - 1; + return pGraph; + } +p->timeRes1 += clock() - clk; + if ( nSteps == 1 || p->nMffc == 2 ) + return NULL; + +clk = clock(); + // consider triples + if ( pGraph = Abc_ManResubDivs12( p, Required ) ) + { +p->timeRes2 += clock() - clk; + p->nLastGain = p->nMffc - 2; + return pGraph; + } +p->timeRes2 += clock() - clk; + + // get the two level divisors +clk = clock(); + Abc_ManResubDivsD( p, Required ); +p->timeResD += clock() - clk; + + // consider two nodes +clk = clock(); + if ( pGraph = Abc_ManResubDivs2( p, Required ) ) + { +p->timeRes2 += clock() - clk; + p->nLastGain = p->nMffc - 2; + return pGraph; + } +p->timeRes2 += clock() - clk; + if ( nSteps == 2 || p->nMffc == 3 ) + return NULL; + + // consider two nodes +clk = clock(); + if ( pGraph = Abc_ManResubDivs3( p, Required ) ) + { +p->timeRes3 += clock() - clk; + p->nLastGain = p->nMffc - 3; + return pGraph; + } +p->timeRes3 += clock() - clk; + if ( nSteps == 3 || p->nLeavesMax == 4 ) + return NULL; + return NULL; +} + + + + +/**Function************************************************************* + + Synopsis [Computes the volume and checks if the cut is feasible.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CutVolumeCheck_rec( Abc_Obj_t * pObj ) +{ + // quit if the node is visited (or if it is a leaf) + if ( Abc_NodeIsTravIdCurrent(pObj) ) + return 0; + Abc_NodeSetTravIdCurrent(pObj); + // report the error + if ( Abc_ObjIsCi(pObj) ) + printf( "Abc_CutVolumeCheck() ERROR: The set of nodes is not a cut!\n" ); + // count the number of nodes in the leaves + return 1 + Abc_CutVolumeCheck_rec( Abc_ObjFanin0(pObj) ) + + Abc_CutVolumeCheck_rec( Abc_ObjFanin1(pObj) ); +} + +/**Function************************************************************* + + Synopsis [Computes the volume and checks if the cut is feasible.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CutVolumeCheck( Abc_Obj_t * pNode, Vec_Ptr_t * vLeaves ) +{ + Abc_Obj_t * pObj; + int i; + // mark the leaves + Abc_NtkIncrementTravId( pNode->pNtk ); + Vec_PtrForEachEntry( vLeaves, pObj, i ) + Abc_NodeSetTravIdCurrent( pObj ); + // traverse the nodes starting from the given one and count them + return Abc_CutVolumeCheck_rec( pNode ); +} + +/**Function************************************************************* + + Synopsis [Computes the factor cut of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_CutFactor_rec( Abc_Obj_t * pObj, Vec_Ptr_t * vLeaves ) +{ + if ( pObj->fMarkA ) + return; + if ( Abc_ObjIsCi(pObj) || (Abc_ObjFanoutNum(pObj) > 1 && !Abc_NodeIsMuxControlType(pObj)) ) + { + Vec_PtrPush( vLeaves, pObj ); + pObj->fMarkA = 1; + return; + } + Abc_CutFactor_rec( Abc_ObjFanin0(pObj), vLeaves ); + Abc_CutFactor_rec( Abc_ObjFanin1(pObj), vLeaves ); +} + +/**Function************************************************************* + + Synopsis [Computes the factor cut of the node.] + + Description [Factor-cut is the cut at a node in terms of factor-nodes. + Factor-nodes are roots of the node trees (MUXes/EXORs are counted as single nodes). + Factor-cut is unique for the given node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_CutFactor( Abc_Obj_t * pNode ) +{ + Vec_Ptr_t * vLeaves; + Abc_Obj_t * pObj; + int i; + assert( !Abc_ObjIsCi(pNode) ); + vLeaves = Vec_PtrAlloc( 10 ); + Abc_CutFactor_rec( Abc_ObjFanin0(pNode), vLeaves ); + Abc_CutFactor_rec( Abc_ObjFanin1(pNode), vLeaves ); + Vec_PtrForEachEntry( vLeaves, pObj, i ) + pObj->fMarkA = 0; + return vLeaves; +} + +/**Function************************************************************* + + Synopsis [Cut computation.] + + Description [This cut computation works as follows: + It starts with the factor cut at the node. If the factor-cut is large, quit. + It supports the set of leaves of the cut under construction and labels all nodes + in the cut under construction, including the leaves. + It computes the factor-cuts of the leaves and checks if it is easible to add any of them. + If it is, it randomly chooses one feasible and continues.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_CutFactorLarge( Abc_Obj_t * pNode, int nLeavesMax ) +{ + Vec_Ptr_t * vLeaves, * vFactors, * vFact, * vNext; + Vec_Int_t * vFeasible; + Abc_Obj_t * pLeaf, * pTemp; + int i, k, Counter, RandLeaf; + int BestCut, BestShare; + assert( Abc_ObjIsNode(pNode) ); + // get one factor-cut + vLeaves = Abc_CutFactor( pNode ); + if ( Vec_PtrSize(vLeaves) > nLeavesMax ) + { + Vec_PtrFree(vLeaves); + return NULL; + } + if ( Vec_PtrSize(vLeaves) == nLeavesMax ) + return vLeaves; + // initialize the factor cuts for the leaves + vFactors = Vec_PtrAlloc( nLeavesMax ); + Abc_NtkIncrementTravId( pNode->pNtk ); + Vec_PtrForEachEntry( vLeaves, pLeaf, i ) + { + Abc_NodeSetTravIdCurrent( pLeaf ); + if ( Abc_ObjIsCi(pLeaf) ) + Vec_PtrPush( vFactors, NULL ); + else + Vec_PtrPush( vFactors, Abc_CutFactor(pLeaf) ); + } + // construct larger factor cuts + vFeasible = Vec_IntAlloc( nLeavesMax ); + while ( 1 ) + { + BestCut = -1; + // find the next feasible cut to add + Vec_IntClear( vFeasible ); + Vec_PtrForEachEntry( vFactors, vFact, i ) + { + if ( vFact == NULL ) + continue; + // count the number of unmarked leaves of this factor cut + Counter = 0; + Vec_PtrForEachEntry( vFact, pTemp, k ) + Counter += !Abc_NodeIsTravIdCurrent(pTemp); + // if the number of new leaves is smaller than the diff, it is feasible + if ( Counter <= nLeavesMax - Vec_PtrSize(vLeaves) + 1 ) + { + Vec_IntPush( vFeasible, i ); + if ( BestCut == -1 || BestShare < Vec_PtrSize(vFact) - Counter ) + BestCut = i, BestShare = Vec_PtrSize(vFact) - Counter; + } + } + // quit if there is no feasible factor cuts + if ( Vec_IntSize(vFeasible) == 0 ) + break; + // randomly choose one leaf and get its factor cut +// RandLeaf = Vec_IntEntry( vFeasible, rand() % Vec_IntSize(vFeasible) ); + // choose the cut that has most sharing with the other cuts + RandLeaf = BestCut; + + pLeaf = Vec_PtrEntry( vLeaves, RandLeaf ); + vNext = Vec_PtrEntry( vFactors, RandLeaf ); + // unmark this leaf + Abc_NodeSetTravIdPrevious( pLeaf ); + // remove this cut from the leaves and factor cuts + for ( i = RandLeaf; i < Vec_PtrSize(vLeaves)-1; i++ ) + { + Vec_PtrWriteEntry( vLeaves, i, Vec_PtrEntry(vLeaves, i+1) ); + Vec_PtrWriteEntry( vFactors, i, Vec_PtrEntry(vFactors,i+1) ); + } + Vec_PtrShrink( vLeaves, Vec_PtrSize(vLeaves) -1 ); + Vec_PtrShrink( vFactors, Vec_PtrSize(vFactors)-1 ); + // add new leaves, compute their factor cuts + Vec_PtrForEachEntry( vNext, pLeaf, i ) + { + if ( Abc_NodeIsTravIdCurrent(pLeaf) ) + continue; + Abc_NodeSetTravIdCurrent( pLeaf ); + Vec_PtrPush( vLeaves, pLeaf ); + if ( Abc_ObjIsCi(pLeaf) ) + Vec_PtrPush( vFactors, NULL ); + else + Vec_PtrPush( vFactors, Abc_CutFactor(pLeaf) ); + } + Vec_PtrFree( vNext ); + assert( Vec_PtrSize(vLeaves) <= nLeavesMax ); + if ( Vec_PtrSize(vLeaves) == nLeavesMax ) + break; + } + + // remove temporary storage + Vec_PtrForEachEntry( vFactors, vFact, i ) + if ( vFact ) Vec_PtrFree( vFact ); + Vec_PtrFree( vFactors ); + Vec_IntFree( vFeasible ); + return vLeaves; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcRewrite.c b/abc_with_bb_support/src/base/abci/abcRewrite.c new file mode 100644 index 000000000..3c3e64067 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcRewrite.c @@ -0,0 +1,414 @@ +/**CFile**************************************************************** + + FileName [abcRewrite.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Technology-independent resynthesis of the AIG based on DAG aware rewriting.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcRewrite.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "rwr.h" +#include "dec.h" + +/* + The ideas realized in this package are inspired by the paper: + Per Bjesse, Arne Boralv, "DAG-aware circuit compression for + formal verification", Proc. ICCAD 2004, pp. 42-49. +*/ + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Cut_Man_t * Abc_NtkStartCutManForRewrite( Abc_Ntk_t * pNtk ); +static void Abc_NodePrintCuts( Abc_Obj_t * pNode ); +static void Abc_ManShowCutCone( Abc_Obj_t * pNode, Vec_Ptr_t * vLeaves ); + +extern void Abc_PlaceBegin( Abc_Ntk_t * pNtk ); +extern void Abc_PlaceEnd( Abc_Ntk_t * pNtk ); +extern void Abc_PlaceUpdate( Vec_Ptr_t * vAddedCells, Vec_Ptr_t * vUpdatedNets ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs incremental rewriting of the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRewrite( Abc_Ntk_t * pNtk, int fUpdateLevel, int fUseZeros, int fVerbose, int fVeryVerbose, int fPlaceEnable ) +{ + ProgressBar * pProgress; + Cut_Man_t * pManCut; + Rwr_Man_t * pManRwr; + Abc_Obj_t * pNode; + Vec_Ptr_t * vAddedCells = NULL, * vUpdatedNets = NULL; + Dec_Graph_t * pGraph; + int i, nNodes, nGain, fCompl; + int clk, clkStart = clock(); + + assert( Abc_NtkIsStrash(pNtk) ); + // cleanup the AIG + Abc_AigCleanup(pNtk->pManFunc); +/* + { + Vec_Vec_t * vParts; + vParts = Abc_NtkPartitionSmart( pNtk, 50, 1 ); + Vec_VecFree( vParts ); + } +*/ + + // start placement package + if ( fPlaceEnable ) + { + Abc_PlaceBegin( pNtk ); + vAddedCells = Abc_AigUpdateStart( pNtk->pManFunc, &vUpdatedNets ); + } + + // start the rewriting manager + pManRwr = Rwr_ManStart( 0 ); + if ( pManRwr == NULL ) + return 0; + // compute the reverse levels if level update is requested + if ( fUpdateLevel ) + Abc_NtkStartReverseLevels( pNtk, 0 ); + // start the cut manager +clk = clock(); + pManCut = Abc_NtkStartCutManForRewrite( pNtk ); +Rwr_ManAddTimeCuts( pManRwr, clock() - clk ); + pNtk->pManCut = pManCut; + + if ( fVeryVerbose ) + Rwr_ScoresClean( pManRwr ); + + // resynthesize each node once + pManRwr->nNodesBeg = Abc_NtkNodeNum(pNtk); + nNodes = Abc_NtkObjNumMax(pNtk); + pProgress = Extra_ProgressBarStart( stdout, nNodes ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + // stop if all nodes have been tried once + if ( i >= nNodes ) + break; + // skip persistant nodes + if ( Abc_NodeIsPersistant(pNode) ) + continue; + // skip the nodes with many fanouts + if ( Abc_ObjFanoutNum(pNode) > 1000 ) + continue; + + // for each cut, try to resynthesize it + nGain = Rwr_NodeRewrite( pManRwr, pManCut, pNode, fUpdateLevel, fUseZeros, fPlaceEnable ); + if ( !(nGain > 0 || nGain == 0 && fUseZeros) ) + continue; + // if we end up here, a rewriting step is accepted + + // get hold of the new subgraph to be added to the AIG + pGraph = Rwr_ManReadDecs(pManRwr); + fCompl = Rwr_ManReadCompl(pManRwr); + + // reset the array of the changed nodes + if ( fPlaceEnable ) + Abc_AigUpdateReset( pNtk->pManFunc ); + + // complement the FF if needed + if ( fCompl ) Dec_GraphComplement( pGraph ); +clk = clock(); + Dec_GraphUpdateNetwork( pNode, pGraph, fUpdateLevel, nGain ); +Rwr_ManAddTimeUpdate( pManRwr, clock() - clk ); + if ( fCompl ) Dec_GraphComplement( pGraph ); + + // use the array of changed nodes to update placement + if ( fPlaceEnable ) + Abc_PlaceUpdate( vAddedCells, vUpdatedNets ); + } + Extra_ProgressBarStop( pProgress ); +Rwr_ManAddTimeTotal( pManRwr, clock() - clkStart ); + // print stats + pManRwr->nNodesEnd = Abc_NtkNodeNum(pNtk); + if ( fVerbose ) + Rwr_ManPrintStats( pManRwr ); +// Rwr_ManPrintStatsFile( pManRwr ); + if ( fVeryVerbose ) + Rwr_ScoresReport( pManRwr ); + // delete the managers + Rwr_ManStop( pManRwr ); + Cut_ManStop( pManCut ); + pNtk->pManCut = NULL; + + // start placement package + if ( fPlaceEnable ) + { + Abc_PlaceEnd( pNtk ); + Abc_AigUpdateStop( pNtk->pManFunc ); + } + + // put the nodes into the DFS order and reassign their IDs + { +// int clk = clock(); + Abc_NtkReassignIds( pNtk ); +// PRT( "time", clock() - clk ); + } +// Abc_AigCheckFaninOrder( pNtk->pManFunc ); + // fix the levels + if ( fUpdateLevel ) + Abc_NtkStopReverseLevels( pNtk ); + else + Abc_NtkLevel( pNtk ); + // check + if ( !Abc_NtkCheck( pNtk ) ) + { + printf( "Abc_NtkRewrite: The network check has failed.\n" ); + return 0; + } + return 1; +} + + +/**Function************************************************************* + + Synopsis [Starts the cut manager for rewriting.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Man_t * Abc_NtkStartCutManForRewrite( Abc_Ntk_t * pNtk ) +{ + static Cut_Params_t Params, * pParams = &Params; + Cut_Man_t * pManCut; + Abc_Obj_t * pObj; + int i; + // start the cut manager + memset( pParams, 0, sizeof(Cut_Params_t) ); + pParams->nVarsMax = 4; // the max cut size ("k" of the k-feasible cuts) + pParams->nKeepMax = 250; // the max number of cuts kept at a node + pParams->fTruth = 1; // compute truth tables + pParams->fFilter = 1; // filter dominated cuts + pParams->fSeq = 0; // compute sequential cuts + pParams->fDrop = 0; // drop cuts on the fly + pParams->fVerbose = 0; // the verbosiness flag + pParams->nIdsMax = Abc_NtkObjNumMax( pNtk ); + pManCut = Cut_ManStart( pParams ); + if ( pParams->fDrop ) + Cut_ManSetFanoutCounts( pManCut, Abc_NtkFanoutCounts(pNtk) ); + // set cuts for PIs + Abc_NtkForEachCi( pNtk, pObj, i ) + if ( Abc_ObjFanoutNum(pObj) > 0 ) + Cut_NodeSetTriv( pManCut, pObj->Id ); + return pManCut; +} + +/**Function************************************************************* + + Synopsis [Prints the cuts at the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodePrintCuts( Abc_Obj_t * pNode ) +{ + Vec_Ptr_t * vCuts; + Cut_Cut_t * pCut; + int k; + + printf( "\nNode %s\n", Abc_ObjName(pNode) ); + vCuts = (Vec_Ptr_t *)pNode->pCopy; + Vec_PtrForEachEntry( vCuts, pCut, k ) + { + Extra_PrintBinary( stdout, (unsigned *)&pCut->uSign, 16 ); + printf( " " ); + Cut_CutPrint( pCut, 0 ); + printf( "\n" ); + } +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ManRewritePrintDivs( Vec_Ptr_t * vDivs, int nLeaves ) +{ + Abc_Obj_t * pFanin, * pNode, * pRoot; + int i, k; + pRoot = Vec_PtrEntryLast(vDivs); + // print the nodes + Vec_PtrForEachEntry( vDivs, pNode, i ) + { + if ( i < nLeaves ) + { + printf( "%6d : %c\n", pNode->Id, 'a'+i ); + continue; + } + printf( "%6d : %2d = ", pNode->Id, i ); + // find the first fanin + Vec_PtrForEachEntry( vDivs, pFanin, k ) + if ( Abc_ObjFanin0(pNode) == pFanin ) + break; + if ( k < nLeaves ) + printf( "%c", 'a' + k ); + else + printf( "%d", k ); + printf( "%s ", Abc_ObjFaninC0(pNode)? "\'" : "" ); + // find the second fanin + Vec_PtrForEachEntry( vDivs, pFanin, k ) + if ( Abc_ObjFanin1(pNode) == pFanin ) + break; + if ( k < nLeaves ) + printf( "%c", 'a' + k ); + else + printf( "%d", k ); + printf( "%s ", Abc_ObjFaninC1(pNode)? "\'" : "" ); + if ( pNode == pRoot ) + printf( " root" ); + printf( "\n" ); + } + printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ManShowCutCone_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vDivs ) +{ + if ( Abc_NodeIsTravIdCurrent(pNode) ) + return; + Abc_NodeSetTravIdCurrent(pNode); + Abc_ManShowCutCone_rec( Abc_ObjFanin0(pNode), vDivs ); + Abc_ManShowCutCone_rec( Abc_ObjFanin1(pNode), vDivs ); + Vec_PtrPush( vDivs, pNode ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ManShowCutCone( Abc_Obj_t * pNode, Vec_Ptr_t * vLeaves ) +{ + Abc_Ntk_t * pNtk = pNode->pNtk; + Abc_Obj_t * pObj; + Vec_Ptr_t * vDivs; + int i; + vDivs = Vec_PtrAlloc( 100 ); + Abc_NtkIncrementTravId( pNtk ); + Vec_PtrForEachEntry( vLeaves, pObj, i ) + { + Abc_NodeSetTravIdCurrent( Abc_ObjRegular(pObj) ); + Vec_PtrPush( vDivs, Abc_ObjRegular(pObj) ); + } + Abc_ManShowCutCone_rec( pNode, vDivs ); + Abc_ManRewritePrintDivs( vDivs, Vec_PtrSize(vLeaves) ); + Vec_PtrFree( vDivs ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_RwrExpWithCut_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vLeaves, int fUseA ) +{ + if ( Vec_PtrFind(vLeaves, pNode) >= 0 || Vec_PtrFind(vLeaves, Abc_ObjNot(pNode)) >= 0 ) + { + if ( fUseA ) + Abc_ObjRegular(pNode)->fMarkA = 1; + else + Abc_ObjRegular(pNode)->fMarkB = 1; + return; + } + assert( Abc_ObjIsNode(pNode) ); + Abc_RwrExpWithCut_rec( Abc_ObjFanin0(pNode), vLeaves, fUseA ); + Abc_RwrExpWithCut_rec( Abc_ObjFanin1(pNode), vLeaves, fUseA ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_RwrExpWithCut( Abc_Obj_t * pNode, Vec_Ptr_t * vLeaves ) +{ + Abc_Obj_t * pObj; + int i, CountA, CountB; + Abc_RwrExpWithCut_rec( Abc_ObjFanin0(pNode), vLeaves, 1 ); + Abc_RwrExpWithCut_rec( Abc_ObjFanin1(pNode), vLeaves, 0 ); + CountA = CountB = 0; + Vec_PtrForEachEntry( vLeaves, pObj, i ) + { + CountA += Abc_ObjRegular(pObj)->fMarkA; + CountB += Abc_ObjRegular(pObj)->fMarkB; + Abc_ObjRegular(pObj)->fMarkA = 0; + Abc_ObjRegular(pObj)->fMarkB = 0; + } + printf( "(%d,%d:%d) ", CountA, CountB, CountA+CountB-Vec_PtrSize(vLeaves) ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcRr.c b/abc_with_bb_support/src/base/abci/abcRr.c new file mode 100644 index 000000000..11b38df3e --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcRr.c @@ -0,0 +1,999 @@ +/**CFile**************************************************************** + + FileName [abcRr.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Redundancy removal.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcRr.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "fraig.h" +#include "sim.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Abc_RRMan_t_ Abc_RRMan_t; +struct Abc_RRMan_t_ +{ + // the parameters + Abc_Ntk_t * pNtk; // the network + int nFaninLevels; // the number of fanin levels + int nFanoutLevels; // the number of fanout levels + // the node/fanin/fanout + Abc_Obj_t * pNode; // the node + Abc_Obj_t * pFanin; // the fanin + Abc_Obj_t * pFanout; // the fanout + // the intermediate cones + Vec_Ptr_t * vFaninLeaves; // the leaves of the fanin cone + Vec_Ptr_t * vFanoutRoots; // the roots of the fanout cone + // the window + Vec_Ptr_t * vLeaves; // the leaves of the window + Vec_Ptr_t * vCone; // the internal nodes of the window + Vec_Ptr_t * vRoots; // the roots of the window + Abc_Ntk_t * pWnd; // the window derived for the edge + // the miter + Abc_Ntk_t * pMiter; // the miter derived from the window + Prove_Params_t * pParams; // the miter proving parameters + // statistical variables + int nNodesOld; // the old number of nodes + int nLevelsOld; // the old number of levels + int nEdgesTried; // the number of nodes tried + int nEdgesRemoved; // the number of nodes proved + int timeWindow; // the time to construct the window + int timeMiter; // the time to construct the miter + int timeProve; // the time to prove the miter + int timeUpdate; // the network update time + int timeTotal; // the total runtime +}; + +static Abc_RRMan_t * Abc_RRManStart(); +static void Abc_RRManStop( Abc_RRMan_t * p ); +static void Abc_RRManPrintStats( Abc_RRMan_t * p ); +static void Abc_RRManClean( Abc_RRMan_t * p ); +static int Abc_NtkRRProve( Abc_RRMan_t * p ); +static int Abc_NtkRRUpdate( Abc_Ntk_t * pNtk, Abc_Obj_t * pNode, Abc_Obj_t * pFanin, Abc_Obj_t * pFanout ); +static int Abc_NtkRRWindow( Abc_RRMan_t * p ); + +static int Abc_NtkRRTfi_int( Vec_Ptr_t * vLeaves, int LevelLimit ); +static int Abc_NtkRRTfo_int( Vec_Ptr_t * vLeaves, Vec_Ptr_t * vRoots, int LevelLimit, Abc_Obj_t * pEdgeFanin, Abc_Obj_t * pEdgeFanout ); +static int Abc_NtkRRTfo_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vRoots, int LevelLimit ); +static void Abc_NtkRRTfi_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vCone, int LevelLimit ); +static Abc_Ntk_t * Abc_NtkWindow( Abc_Ntk_t * pNtk, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vCone, Vec_Ptr_t * vRoots ); + +static void Abc_NtkRRSimulateStart( Abc_Ntk_t * pNtk ); +static void Abc_NtkRRSimulateStop( Abc_Ntk_t * pNtk ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Removes stuck-at redundancies.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRR( Abc_Ntk_t * pNtk, int nFaninLevels, int nFanoutLevels, int fUseFanouts, int fVerbose ) +{ + ProgressBar * pProgress; + Abc_RRMan_t * p; + Abc_Obj_t * pNode, * pFanin, * pFanout; + int i, k, m, nNodes, RetValue, clk, clkTotal = clock(); + // start the manager + p = Abc_RRManStart( nFaninLevels, nFanoutLevels ); + p->pNtk = pNtk; + p->nFaninLevels = nFaninLevels; + p->nFanoutLevels = nFanoutLevels; + p->nNodesOld = Abc_NtkNodeNum(pNtk); + p->nLevelsOld = Abc_AigLevel(pNtk); + // remember latch values +// Abc_NtkForEachLatch( pNtk, pNode, i ) +// pNode->pNext = pNode->pData; + // go through the nodes + Abc_NtkCleanCopy(pNtk); + nNodes = Abc_NtkObjNumMax(pNtk); + Abc_NtkRRSimulateStart(pNtk); + pProgress = Extra_ProgressBarStart( stdout, nNodes ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + // stop if all nodes have been tried once + if ( i >= nNodes ) + break; + // skip the constant node +// if ( Abc_NodeIsConst(pNode) ) +// continue; + // skip persistant nodes + if ( Abc_NodeIsPersistant(pNode) ) + continue; + // skip the nodes with many fanouts + if ( Abc_ObjFanoutNum(pNode) > 1000 ) + continue; + // construct the window + if ( !fUseFanouts ) + { + Abc_ObjForEachFanin( pNode, pFanin, k ) + { + // skip the nodes with only one fanout (tree nodes) + if ( Abc_ObjFanoutNum(pFanin) == 1 ) + continue; +/* + if ( pFanin->Id == 228 && pNode->Id == 2649 ) + { + int k = 0; + } +*/ + p->nEdgesTried++; + Abc_RRManClean( p ); + p->pNode = pNode; + p->pFanin = pFanin; + p->pFanout = NULL; + + clk = clock(); + RetValue = Abc_NtkRRWindow( p ); + p->timeWindow += clock() - clk; + if ( !RetValue ) + continue; +/* + if ( pFanin->Id == 228 && pNode->Id == 2649 ) + { + Abc_NtkShowAig( p->pWnd, 0 ); + } +*/ + clk = clock(); + RetValue = Abc_NtkRRProve( p ); + p->timeMiter += clock() - clk; + if ( !RetValue ) + continue; +//printf( "%d -> %d (%d)\n", pFanin->Id, pNode->Id, k ); + + clk = clock(); + Abc_NtkRRUpdate( pNtk, p->pNode, p->pFanin, p->pFanout ); + p->timeUpdate += clock() - clk; + + p->nEdgesRemoved++; + break; + } + continue; + } + // use the fanouts + Abc_ObjForEachFanin( pNode, pFanin, k ) + Abc_ObjForEachFanout( pNode, pFanout, m ) + { + // skip the nodes with only one fanout (tree nodes) +// if ( Abc_ObjFanoutNum(pFanin) == 1 && Abc_ObjFanoutNum(pNode) == 1 ) +// continue; + + p->nEdgesTried++; + Abc_RRManClean( p ); + p->pNode = pNode; + p->pFanin = pFanin; + p->pFanout = pFanout; + + clk = clock(); + RetValue = Abc_NtkRRWindow( p ); + p->timeWindow += clock() - clk; + if ( !RetValue ) + continue; + + clk = clock(); + RetValue = Abc_NtkRRProve( p ); + p->timeMiter += clock() - clk; + if ( !RetValue ) + continue; + + clk = clock(); + Abc_NtkRRUpdate( pNtk, p->pNode, p->pFanin, p->pFanout ); + p->timeUpdate += clock() - clk; + + p->nEdgesRemoved++; + break; + } + } + Abc_NtkRRSimulateStop(pNtk); + Extra_ProgressBarStop( pProgress ); + p->timeTotal = clock() - clkTotal; + if ( fVerbose ) + Abc_RRManPrintStats( p ); + Abc_RRManStop( p ); + // restore latch values +// Abc_NtkForEachLatch( pNtk, pNode, i ) +// pNode->pData = pNode->pNext, pNode->pNext = NULL; + // put the nodes into the DFS order and reassign their IDs + Abc_NtkReassignIds( pNtk ); + Abc_NtkLevel( pNtk ); + // check + if ( !Abc_NtkCheck( pNtk ) ) + { + printf( "Abc_NtkRR: The network check has failed.\n" ); + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Start the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_RRMan_t * Abc_RRManStart() +{ + Abc_RRMan_t * p; + p = ALLOC( Abc_RRMan_t, 1 ); + memset( p, 0, sizeof(Abc_RRMan_t) ); + p->vFaninLeaves = Vec_PtrAlloc( 100 ); // the leaves of the fanin cone + p->vFanoutRoots = Vec_PtrAlloc( 100 ); // the roots of the fanout cone + p->vLeaves = Vec_PtrAlloc( 100 ); // the leaves of the window + p->vCone = Vec_PtrAlloc( 100 ); // the internal nodes of the window + p->vRoots = Vec_PtrAlloc( 100 ); // the roots of the window + p->pParams = ALLOC( Prove_Params_t, 1 ); + memset( p->pParams, 0, sizeof(Prove_Params_t) ); + Prove_ParamsSetDefault( p->pParams ); + return p; +} + +/**Function************************************************************* + + Synopsis [Stop the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_RRManStop( Abc_RRMan_t * p ) +{ + Abc_RRManClean( p ); + Vec_PtrFree( p->vFaninLeaves ); + Vec_PtrFree( p->vFanoutRoots ); + Vec_PtrFree( p->vLeaves ); + Vec_PtrFree( p->vCone ); + Vec_PtrFree( p->vRoots ); + free( p->pParams ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Stop the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_RRManPrintStats( Abc_RRMan_t * p ) +{ + double Ratio = 100.0*(p->nNodesOld - Abc_NtkNodeNum(p->pNtk))/p->nNodesOld; + printf( "Redundancy removal statistics:\n" ); + printf( "Edges tried = %6d.\n", p->nEdgesTried ); + printf( "Edges removed = %6d. (%5.2f %%)\n", p->nEdgesRemoved, 100.0*p->nEdgesRemoved/p->nEdgesTried ); + printf( "Node gain = %6d. (%5.2f %%)\n", p->nNodesOld - Abc_NtkNodeNum(p->pNtk), Ratio ); + printf( "Level gain = %6d.\n", p->nLevelsOld - Abc_AigLevel(p->pNtk) ); + PRT( "Windowing ", p->timeWindow ); + PRT( "Miter ", p->timeMiter ); + PRT( " Construct ", p->timeMiter - p->timeProve ); + PRT( " Prove ", p->timeProve ); + PRT( "Update ", p->timeUpdate ); + PRT( "TOTAL ", p->timeTotal ); +} + +/**Function************************************************************* + + Synopsis [Clean the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_RRManClean( Abc_RRMan_t * p ) +{ + p->pNode = NULL; + p->pFanin = NULL; + p->pFanout = NULL; + Vec_PtrClear( p->vFaninLeaves ); + Vec_PtrClear( p->vFanoutRoots ); + Vec_PtrClear( p->vLeaves ); + Vec_PtrClear( p->vCone ); + Vec_PtrClear( p->vRoots ); + if ( p->pWnd ) Abc_NtkDelete( p->pWnd ); + if ( p->pMiter ) Abc_NtkDelete( p->pMiter ); + p->pWnd = NULL; + p->pMiter = NULL; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the miter is constant 0.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRRProve( Abc_RRMan_t * p ) +{ + Abc_Ntk_t * pWndCopy; + int RetValue, clk; +// Abc_NtkShowAig( p->pWnd, 0 ); + pWndCopy = Abc_NtkDup( p->pWnd ); + Abc_NtkRRUpdate( pWndCopy, p->pNode->pCopy->pCopy, p->pFanin->pCopy->pCopy, p->pFanout? p->pFanout->pCopy->pCopy : NULL ); + if ( !Abc_NtkIsDfsOrdered(pWndCopy) ) + Abc_NtkReassignIds(pWndCopy); + p->pMiter = Abc_NtkMiter( p->pWnd, pWndCopy, 1, 0 ); + Abc_NtkDelete( pWndCopy ); +clk = clock(); + RetValue = Abc_NtkMiterProve( &p->pMiter, p->pParams ); +p->timeProve += clock() - clk; + if ( RetValue == 1 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Updates the network after redundancy removal.] + + Description [This procedure assumes that non-control value of the fanin + was proved redundant. It is okay to concentrate on non-control values + because the control values can be seen as redundancy of the fanout edge.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRRUpdate( Abc_Ntk_t * pNtk, Abc_Obj_t * pNode, Abc_Obj_t * pFanin, Abc_Obj_t * pFanout ) +{ + Abc_Obj_t * pNodeNew, * pFanoutNew; + assert( pFanout == NULL ); + assert( !Abc_ObjIsComplement(pNode) ); + assert( !Abc_ObjIsComplement(pFanin) ); + assert( !Abc_ObjIsComplement(pFanout) ); + // find the node after redundancy removal + if ( pFanin == Abc_ObjFanin0(pNode) ) + pNodeNew = Abc_ObjChild1(pNode); + else if ( pFanin == Abc_ObjFanin1(pNode) ) + pNodeNew = Abc_ObjChild0(pNode); + else assert( 0 ); + // replace + if ( pFanout == NULL ) + { + Abc_AigReplace( pNtk->pManFunc, pNode, pNodeNew, 1 ); + return 1; + } + // find the fanout after redundancy removal + if ( pNode == Abc_ObjFanin0(pFanout) ) + pFanoutNew = Abc_AigAnd( pNtk->pManFunc, Abc_ObjNotCond(pNodeNew,Abc_ObjFaninC0(pFanout)), Abc_ObjChild1(pFanout) ); + else if ( pNode == Abc_ObjFanin1(pFanout) ) + pFanoutNew = Abc_AigAnd( pNtk->pManFunc, Abc_ObjNotCond(pNodeNew,Abc_ObjFaninC1(pFanout)), Abc_ObjChild0(pFanout) ); + else assert( 0 ); + // replace + Abc_AigReplace( pNtk->pManFunc, pFanout, pFanoutNew, 1 ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Constructs window for checking RR.] + + Description [If the window (p->pWnd) with the given scope (p->nFaninLevels, + p->nFanoutLevels) cannot be constructed, returns 0. Otherwise, returns 1. + The levels are measured from the fanin node (pFanin) and the fanout node + (pEdgeFanout), respectively.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRRWindow( Abc_RRMan_t * p ) +{ + Abc_Obj_t * pObj, * pEdgeFanin, * pEdgeFanout; + int i, LevelMin, LevelMax, RetValue; + + // get the edge + pEdgeFanout = p->pFanout? p->pFanout : p->pNode; + pEdgeFanin = p->pFanout? p->pNode : p->pFanin; + // get the minimum and maximum levels of the window + LevelMin = ABC_MAX( 0, ((int)p->pFanin->Level) - p->nFaninLevels ); + LevelMax = (int)pEdgeFanout->Level + p->nFanoutLevels; + + // start the TFI leaves with the fanin + Abc_NtkIncrementTravId( p->pNtk ); + Abc_NodeSetTravIdCurrent( p->pFanin ); + Vec_PtrPush( p->vFaninLeaves, p->pFanin ); + // mark the TFI cone and collect the leaves down to the given level + while ( Abc_NtkRRTfi_int(p->vFaninLeaves, LevelMin) ); + + // mark the leaves with the new TravId + Abc_NtkIncrementTravId( p->pNtk ); + Vec_PtrForEachEntry( p->vFaninLeaves, pObj, i ) + Abc_NodeSetTravIdCurrent( pObj ); + // traverse the TFO cone of the leaves (while skipping the edge) + // (a) mark the nodes in the cone using the current TravId + // (b) collect the nodes that have external fanouts into p->vFanoutRoots + while ( Abc_NtkRRTfo_int(p->vFaninLeaves, p->vFanoutRoots, LevelMax, pEdgeFanin, pEdgeFanout) ); + + // mark the fanout roots + Vec_PtrForEachEntry( p->vFanoutRoots, pObj, i ) + pObj->fMarkA = 1; + // collect roots reachable from the fanout (p->vRoots) + RetValue = Abc_NtkRRTfo_rec( pEdgeFanout, p->vRoots, LevelMax + 1 ); + // unmark the fanout roots + Vec_PtrForEachEntry( p->vFanoutRoots, pObj, i ) + pObj->fMarkA = 0; + + // return if the window is infeasible + if ( RetValue == 0 ) + return 0; + + // collect the DFS-ordered new cone (p->vCone) and new leaves (p->vLeaves) + // using the previous marks coming from the TFO cone + Abc_NtkIncrementTravId( p->pNtk ); + Vec_PtrForEachEntry( p->vRoots, pObj, i ) + Abc_NtkRRTfi_rec( pObj, p->vLeaves, p->vCone, LevelMin ); + + // create a new network + p->pWnd = Abc_NtkWindow( p->pNtk, p->vLeaves, p->vCone, p->vRoots ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Marks the nodes in the TFI and collects their leaves.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRRTfi_int( Vec_Ptr_t * vLeaves, int LevelLimit ) +{ + Abc_Obj_t * pObj, * pNext; + int i, k, LevelMax, nSize; + assert( LevelLimit >= 0 ); + // find the maximum level of leaves + LevelMax = 0; + Vec_PtrForEachEntry( vLeaves, pObj, i ) + if ( LevelMax < (int)pObj->Level ) + LevelMax = pObj->Level; + // if the nodes are all PIs, LevelMax == 0 + if ( LevelMax <= LevelLimit ) + return 0; + // expand the nodes with the minimum level + nSize = Vec_PtrSize(vLeaves); + Vec_PtrForEachEntryStop( vLeaves, pObj, i, nSize ) + { + if ( LevelMax != (int)pObj->Level ) + continue; + Abc_ObjForEachFanin( pObj, pNext, k ) + { + if ( Abc_NodeIsTravIdCurrent(pNext) ) + continue; + Abc_NodeSetTravIdCurrent( pNext ); + Vec_PtrPush( vLeaves, pNext ); + } + } + // remove old nodes (cannot remove a PI) + k = 0; + Vec_PtrForEachEntry( vLeaves, pObj, i ) + { + if ( LevelMax == (int)pObj->Level ) + continue; + Vec_PtrWriteEntry( vLeaves, k++, pObj ); + } + Vec_PtrShrink( vLeaves, k ); + if ( Vec_PtrSize(vLeaves) > 2000 ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Marks the nodes in the TFO and collects their roots.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRRTfo_int( Vec_Ptr_t * vLeaves, Vec_Ptr_t * vRoots, int LevelLimit, Abc_Obj_t * pEdgeFanin, Abc_Obj_t * pEdgeFanout ) +{ + Abc_Obj_t * pObj, * pNext; + int i, k, LevelMin, nSize, fObjIsRoot; + // find the minimum level of leaves + LevelMin = ABC_INFINITY; + Vec_PtrForEachEntry( vLeaves, pObj, i ) + if ( LevelMin > (int)pObj->Level ) + LevelMin = pObj->Level; + // if the minimum level exceed the limit, we are done + if ( LevelMin > LevelLimit ) + return 0; + // expand the nodes with the minimum level + nSize = Vec_PtrSize(vLeaves); + Vec_PtrForEachEntryStop( vLeaves, pObj, i, nSize ) + { + if ( LevelMin != (int)pObj->Level ) + continue; + fObjIsRoot = 0; + Abc_ObjForEachFanout( pObj, pNext, k ) + { + // check if the fanout is outside of the cone + if ( Abc_ObjIsCo(pNext) || pNext->Level > (unsigned)LevelLimit ) + { + fObjIsRoot = 1; + continue; + } + // skip the edge under check + if ( pObj == pEdgeFanin && pNext == pEdgeFanout ) + continue; + // skip the visited fanouts + if ( Abc_NodeIsTravIdCurrent(pNext) ) + continue; + Abc_NodeSetTravIdCurrent( pNext ); + Vec_PtrPush( vLeaves, pNext ); + } + if ( fObjIsRoot ) + Vec_PtrPush( vRoots, pObj ); + } + // remove old nodes + k = 0; + Vec_PtrForEachEntry( vLeaves, pObj, i ) + { + if ( LevelMin == (int)pObj->Level ) + continue; + Vec_PtrWriteEntry( vLeaves, k++, pObj ); + } + Vec_PtrShrink( vLeaves, k ); + if ( Vec_PtrSize(vLeaves) > 2000 ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Collects the roots in the TFO of the node.] + + Description [Note that this procedure can be improved by + marking and skipping the visited nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRRTfo_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vRoots, int LevelLimit ) +{ + Abc_Obj_t * pFanout; + int i; + // if we encountered a node outside of the TFO cone of the fanins, quit + if ( Abc_ObjIsCo(pNode) || pNode->Level > (unsigned)LevelLimit ) + return 0; + // if we encountered a node on the boundary, add it to the roots + if ( pNode->fMarkA ) + { + Vec_PtrPushUnique( vRoots, pNode ); + return 1; + } + // mark the node with the current TravId (needed to have all internal nodes marked) + Abc_NodeSetTravIdCurrent( pNode ); + // traverse the fanouts + Abc_ObjForEachFanout( pNode, pFanout, i ) + if ( !Abc_NtkRRTfo_rec( pFanout, vRoots, LevelLimit ) ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Collects the leaves and cone of the roots.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkRRTfi_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vCone, int LevelLimit ) +{ + Abc_Obj_t * pFanin; + int i; + // skip visited nodes + if ( Abc_NodeIsTravIdCurrent(pNode) ) + return; + // add node to leaves if it is not in TFI cone of the leaves (marked before) or below the limit + if ( !Abc_NodeIsTravIdPrevious(pNode) || (int)pNode->Level <= LevelLimit ) + { + Abc_NodeSetTravIdCurrent( pNode ); + Vec_PtrPush( vLeaves, pNode ); + return; + } + // mark the node as visited + Abc_NodeSetTravIdCurrent( pNode ); + // call for the node's fanins + Abc_ObjForEachFanin( pNode, pFanin, i ) + Abc_NtkRRTfi_rec( pFanin, vLeaves, vCone, LevelLimit ); + // add the node to the cone in topological order + Vec_PtrPush( vCone, pNode ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkWindow( Abc_Ntk_t * pNtk, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vCone, Vec_Ptr_t * vRoots ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj; + int fCheck = 1; + int i; + assert( Abc_NtkIsStrash(pNtk) ); + // start the network + pNtkNew = Abc_NtkAlloc( pNtk->ntkType, pNtk->ntkFunc, 1 ); + // duplicate the name and the spec + pNtkNew->pName = Extra_UtilStrsav( "temp" ); + // map the constant nodes + Abc_AigConst1(pNtk)->pCopy = Abc_AigConst1(pNtkNew); + // create and map the PIs + Vec_PtrForEachEntry( vLeaves, pObj, i ) + pObj->pCopy = Abc_NtkCreatePi(pNtkNew); + // copy the AND gates + Vec_PtrForEachEntry( vCone, pObj, i ) + pObj->pCopy = Abc_AigAnd( pNtkNew->pManFunc, Abc_ObjChild0Copy(pObj), Abc_ObjChild1Copy(pObj) ); + // compare the number of nodes before and after + if ( Vec_PtrSize(vCone) != Abc_NtkNodeNum(pNtkNew) ) + printf( "Warning: Structural hashing during windowing reduced %d nodes (this is a bug).\n", + Vec_PtrSize(vCone) - Abc_NtkNodeNum(pNtkNew) ); + // create the POs + Vec_PtrForEachEntry( vRoots, pObj, i ) + { + assert( !Abc_ObjIsComplement(pObj->pCopy) ); + Abc_ObjAddFanin( Abc_NtkCreatePo(pNtkNew), pObj->pCopy ); + } + // add the PI/PO names + Abc_NtkAddDummyPiNames( pNtkNew ); + Abc_NtkAddDummyPoNames( pNtkNew ); + Abc_NtkAddDummyAssertNames( pNtkNew ); + // check + if ( fCheck && !Abc_NtkCheck( pNtkNew ) ) + { + printf( "Abc_NtkWindow: The network check has failed.\n" ); + return NULL; + } + return pNtkNew; +} + + +/**Function************************************************************* + + Synopsis [Starts simulation to detect non-redundant edges.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkRRSimulateStart( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + unsigned uData, uData0, uData1; + int i; + Abc_AigConst1(pNtk)->pData = (void *)~((unsigned)0); + Abc_NtkForEachCi( pNtk, pObj, i ) + pObj->pData = (void *)SIM_RANDOM_UNSIGNED; + Abc_NtkForEachNode( pNtk, pObj, i ) + { + if ( i == 0 ) continue; + uData0 = (unsigned)Abc_ObjFanin0(pObj)->pData; + uData1 = (unsigned)Abc_ObjFanin1(pObj)->pData; + uData = Abc_ObjFaninC0(pObj)? ~uData0 : uData0; + uData &= Abc_ObjFaninC1(pObj)? ~uData1 : uData1; + assert( pObj->pData == NULL ); + pObj->pData = (void *)uData; + } +} + +/**Function************************************************************* + + Synopsis [Stops simulation to detect non-redundant edges.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkRRSimulateStop( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i; + Abc_NtkForEachObj( pNtk, pObj, i ) + pObj->pData = NULL; +} + + + + + + + +static void Sim_TraverseNodes_rec( Abc_Obj_t * pRoot, Vec_Str_t * vTargets, Vec_Ptr_t * vNodes ); +static void Sim_CollectNodes_rec( Abc_Obj_t * pRoot, Vec_Ptr_t * vField ); +static void Sim_SimulateCollected( Vec_Str_t * vTargets, Vec_Ptr_t * vNodes, Vec_Ptr_t * vField ); + +/**Function************************************************************* + + Synopsis [Simulation to detect non-redundant edges.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Str_t * Abc_NtkRRSimulate( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vNodes, * vField; + Vec_Str_t * vTargets; + Abc_Obj_t * pObj; + unsigned uData, uData0, uData1; + int PrevCi, Phase, i, k; + + // start the candidates + vTargets = Vec_StrStart( Abc_NtkObjNumMax(pNtk) + 1 ); + Abc_NtkForEachNode( pNtk, pObj, i ) + { + Phase = ((Abc_ObjFanoutNum(Abc_ObjFanin1(pObj)) > 1) << 1); + Phase |= (Abc_ObjFanoutNum(Abc_ObjFanin0(pObj)) > 1); + Vec_StrWriteEntry( vTargets, pObj->Id, (char)Phase ); + } + + // simulate patters and store them in copy + Abc_AigConst1(pNtk)->pCopy = (Abc_Obj_t *)~((unsigned)0); + Abc_NtkForEachCi( pNtk, pObj, i ) + pObj->pCopy = (Abc_Obj_t *)SIM_RANDOM_UNSIGNED; + Abc_NtkForEachNode( pNtk, pObj, i ) + { + if ( i == 0 ) continue; + uData0 = (unsigned)Abc_ObjFanin0(pObj)->pData; + uData1 = (unsigned)Abc_ObjFanin1(pObj)->pData; + uData = Abc_ObjFaninC0(pObj)? ~uData0 : uData0; + uData &= Abc_ObjFaninC1(pObj)? ~uData1 : uData1; + pObj->pCopy = (Abc_Obj_t *)uData; + } + // store the result in data + Abc_NtkForEachCo( pNtk, pObj, i ) + { + uData0 = (unsigned)Abc_ObjFanin0(pObj)->pData; + if ( Abc_ObjFaninC0(pObj) ) + pObj->pData = (void *)~uData0; + else + pObj->pData = (void *)uData0; + } + + // refine the candidates + for ( PrevCi = 0; PrevCi < Abc_NtkCiNum(pNtk); PrevCi = i ) + { + vNodes = Vec_PtrAlloc( 10 ); + Abc_NtkIncrementTravId( pNtk ); + for ( i = PrevCi; i < Abc_NtkCiNum(pNtk); i++ ) + { + Sim_TraverseNodes_rec( Abc_NtkCi(pNtk, i), vTargets, vNodes ); + if ( Vec_PtrSize(vNodes) > 128 ) + break; + } + // collect the marked nodes in the topological order + vField = Vec_PtrAlloc( 10 ); + Abc_NtkIncrementTravId( pNtk ); + Abc_NtkForEachCo( pNtk, pObj, k ) + Sim_CollectNodes_rec( pObj, vField ); + + // simulate these nodes + Sim_SimulateCollected( vTargets, vNodes, vField ); + // prepare for the next loop + Vec_PtrFree( vNodes ); + } + + // clean + Abc_NtkForEachObj( pNtk, pObj, i ) + pObj->pData = NULL; + return vTargets; +} + +/**Function************************************************************* + + Synopsis [Collects nodes starting from the given node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_TraverseNodes_rec( Abc_Obj_t * pRoot, Vec_Str_t * vTargets, Vec_Ptr_t * vNodes ) +{ + Abc_Obj_t * pFanout; + char Entry; + int k; + if ( Abc_NodeIsTravIdCurrent(pRoot) ) + return; + Abc_NodeSetTravIdCurrent( pRoot ); + // save the reached targets + Entry = Vec_StrEntry(vTargets, pRoot->Id); + if ( Entry & 1 ) + Vec_PtrPush( vNodes, Abc_ObjNot(pRoot) ); + if ( Entry & 2 ) + Vec_PtrPush( vNodes, pRoot ); + // explore the fanouts + Abc_ObjForEachFanout( pRoot, pFanout, k ) + Sim_TraverseNodes_rec( pFanout, vTargets, vNodes ); +} + +/**Function************************************************************* + + Synopsis [Collects nodes starting from the given node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_CollectNodes_rec( Abc_Obj_t * pRoot, Vec_Ptr_t * vField ) +{ + Abc_Obj_t * pFanin; + int i; + if ( Abc_NodeIsTravIdCurrent(pRoot) ) + return; + if ( !Abc_NodeIsTravIdPrevious(pRoot) ) + return; + Abc_NodeSetTravIdCurrent( pRoot ); + Abc_ObjForEachFanin( pRoot, pFanin, i ) + Sim_CollectNodes_rec( pFanin, vField ); + if ( !Abc_ObjIsCo(pRoot) ) + pRoot->pData = (void *)Vec_PtrSize(vField); + Vec_PtrPush( vField, pRoot ); +} + +/**Function************************************************************* + + Synopsis [Simulate the given nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_SimulateCollected( Vec_Str_t * vTargets, Vec_Ptr_t * vNodes, Vec_Ptr_t * vField ) +{ + Abc_Obj_t * pObj, * pFanin0, * pFanin1, * pDisproved; + Vec_Ptr_t * vSims; + unsigned * pUnsigned, * pUnsignedF; + int i, k, Phase, fCompl; + // get simulation info + vSims = Sim_UtilInfoAlloc( Vec_PtrSize(vField), Vec_PtrSize(vNodes), 0 ); + // simulate the nodes + Vec_PtrForEachEntry( vField, pObj, i ) + { + if ( Abc_ObjIsCi(pObj) ) + { + pUnsigned = Vec_PtrEntry( vSims, i ); + for ( k = 0; k < Vec_PtrSize(vNodes); k++ ) + pUnsigned[k] = (unsigned)pObj->pCopy; + continue; + } + if ( Abc_ObjIsCo(pObj) ) + { + pUnsigned = Vec_PtrEntry( vSims, i ); + pUnsignedF = Vec_PtrEntry( vSims, (int)Abc_ObjFanin0(pObj)->pData ); + if ( Abc_ObjFaninC0(pObj) ) + for ( k = 0; k < Vec_PtrSize(vNodes); k++ ) + pUnsigned[k] = ~pUnsignedF[k]; + else + for ( k = 0; k < Vec_PtrSize(vNodes); k++ ) + pUnsigned[k] = pUnsignedF[k]; + // update targets + for ( k = 0; k < Vec_PtrSize(vNodes); k++ ) + { + if ( pUnsigned[k] == (unsigned)pObj->pData ) + continue; + pDisproved = Vec_PtrEntry( vNodes, k ); + fCompl = Abc_ObjIsComplement(pDisproved); + pDisproved = Abc_ObjRegular(pDisproved); + Phase = Vec_StrEntry( vTargets, pDisproved->Id ); + if ( fCompl ) + Phase = (Phase & 2); + else + Phase = (Phase & 1); + Vec_StrWriteEntry( vTargets, pDisproved->Id, (char)Phase ); + } + continue; + } + // simulate the node + pFanin0 = Abc_ObjFanin0(pObj); + pFanin1 = Abc_ObjFanin1(pObj); + } +} + + + +/* + { + unsigned uData; + if ( pFanin == Abc_ObjFanin0(pNode) ) + { + uData = (unsigned)Abc_ObjFanin1(pNode)->pData; + uData = Abc_ObjFaninC1(pNode)? ~uData : uData; + } + else if ( pFanin == Abc_ObjFanin1(pNode) ) + { + uData = (unsigned)Abc_ObjFanin0(pNode)->pData; + uData = Abc_ObjFaninC0(pNode)? ~uData : uData; + } + uData ^= (unsigned)pNode->pData; +// Extra_PrintBinary( stdout, &uData, 32 ); printf( "\n" ); + if ( Extra_WordCountOnes(uData) > 8 ) + continue; + } +*/ + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcSat.c b/abc_with_bb_support/src/base/abci/abcSat.c new file mode 100644 index 000000000..e29716225 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcSat.c @@ -0,0 +1,884 @@ +/**CFile**************************************************************** + + FileName [abcSat.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Procedures to solve the miter using the internal SAT sat_solver.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcSat.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "satSolver.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static sat_solver * Abc_NtkMiterSatCreateLogic( Abc_Ntk_t * pNtk, int fAllPrimes ); +extern Vec_Int_t * Abc_NtkGetCiSatVarNums( Abc_Ntk_t * pNtk ); +static nMuxes; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Attempts to solve the miter using an internal SAT sat_solver.] + + Description [Returns -1 if timed out; 0 if SAT; 1 if UNSAT.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkMiterSat( Abc_Ntk_t * pNtk, sint64 nConfLimit, sint64 nInsLimit, int fVerbose, sint64 * pNumConfs, sint64 * pNumInspects ) +{ + sat_solver * pSat; + lbool status; + int RetValue, clk; + + if ( pNumConfs ) + *pNumConfs = 0; + if ( pNumInspects ) + *pNumInspects = 0; + + assert( Abc_NtkLatchNum(pNtk) == 0 ); + +// if ( Abc_NtkPoNum(pNtk) > 1 ) +// fprintf( stdout, "Warning: The miter has %d outputs. SAT will try to prove all of them.\n", Abc_NtkPoNum(pNtk) ); + + // load clauses into the sat_solver + clk = clock(); + pSat = Abc_NtkMiterSatCreate( pNtk, 0 ); + if ( pSat == NULL ) + return 1; +//printf( "%d \n", pSat->clauses.size ); +//sat_solver_delete( pSat ); +//return 1; + +// printf( "Created SAT problem with %d variable and %d clauses. ", sat_solver_nvars(pSat), sat_solver_nclauses(pSat) ); +// PRT( "Time", clock() - clk ); + + // simplify the problem + clk = clock(); + status = sat_solver_simplify(pSat); +// printf( "Simplified the problem to %d variables and %d clauses. ", sat_solver_nvars(pSat), sat_solver_nclauses(pSat) ); +// PRT( "Time", clock() - clk ); + if ( status == 0 ) + { + sat_solver_delete( pSat ); +// printf( "The problem is UNSATISFIABLE after simplification.\n" ); + return 1; + } + + // solve the miter + clk = clock(); + if ( fVerbose ) + pSat->verbosity = 1; + status = sat_solver_solve( pSat, NULL, NULL, (sint64)nConfLimit, (sint64)nInsLimit, (sint64)0, (sint64)0 ); + if ( status == l_Undef ) + { +// printf( "The problem timed out.\n" ); + RetValue = -1; + } + else if ( status == l_True ) + { +// printf( "The problem is SATISFIABLE.\n" ); + RetValue = 0; + } + else if ( status == l_False ) + { +// printf( "The problem is UNSATISFIABLE.\n" ); + RetValue = 1; + } + else + assert( 0 ); +// PRT( "SAT sat_solver time", clock() - clk ); +// printf( "The number of conflicts = %d.\n", (int)pSat->sat_solver_stats.conflicts ); + + // if the problem is SAT, get the counterexample + if ( status == l_True ) + { +// Vec_Int_t * vCiIds = Abc_NtkGetCiIds( pNtk ); + Vec_Int_t * vCiIds = Abc_NtkGetCiSatVarNums( pNtk ); + pNtk->pModel = Sat_SolverGetModel( pSat, vCiIds->pArray, vCiIds->nSize ); + Vec_IntFree( vCiIds ); + } + // free the sat_solver + if ( fVerbose ) + Sat_SolverPrintStats( stdout, pSat ); + + if ( pNumConfs ) + *pNumConfs = (int)pSat->stats.conflicts; + if ( pNumInspects ) + *pNumInspects = (int)pSat->stats.inspects; + +sat_solver_store_write( pSat, "trace.cnf" ); +sat_solver_store_free( pSat ); + + sat_solver_delete( pSat ); + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Returns the array of CI IDs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Abc_NtkGetCiSatVarNums( Abc_Ntk_t * pNtk ) +{ + Vec_Int_t * vCiIds; + Abc_Obj_t * pObj; + int i; + vCiIds = Vec_IntAlloc( Abc_NtkCiNum(pNtk) ); + Abc_NtkForEachCi( pNtk, pObj, i ) + Vec_IntPush( vCiIds, (int)pObj->pCopy ); + return vCiIds; +} + + + +/**Function************************************************************* + + Synopsis [Adds trivial clause.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkClauseTriv( sat_solver * pSat, Abc_Obj_t * pNode, Vec_Int_t * vVars ) +{ +//printf( "Adding triv %d. %d\n", Abc_ObjRegular(pNode)->Id, (int)pSat->sat_solver_stats.clauses ); + vVars->nSize = 0; + Vec_IntPush( vVars, toLitCond( (int)Abc_ObjRegular(pNode)->pCopy, Abc_ObjIsComplement(pNode) ) ); +// Vec_IntPush( vVars, toLitCond( (int)Abc_ObjRegular(pNode)->Id, Abc_ObjIsComplement(pNode) ) ); + return sat_solver_addclause( pSat, vVars->pArray, vVars->pArray + vVars->nSize ); +} + +/**Function************************************************************* + + Synopsis [Adds trivial clause.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkClauseTop( sat_solver * pSat, Vec_Ptr_t * vNodes, Vec_Int_t * vVars ) +{ + Abc_Obj_t * pNode; + int i; +//printf( "Adding triv %d. %d\n", Abc_ObjRegular(pNode)->Id, (int)pSat->sat_solver_stats.clauses ); + vVars->nSize = 0; + Vec_PtrForEachEntry( vNodes, pNode, i ) + Vec_IntPush( vVars, toLitCond( (int)Abc_ObjRegular(pNode)->pCopy, Abc_ObjIsComplement(pNode) ) ); +// Vec_IntPush( vVars, toLitCond( (int)Abc_ObjRegular(pNode)->Id, Abc_ObjIsComplement(pNode) ) ); + return sat_solver_addclause( pSat, vVars->pArray, vVars->pArray + vVars->nSize ); +} + +/**Function************************************************************* + + Synopsis [Adds trivial clause.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkClauseAnd( sat_solver * pSat, Abc_Obj_t * pNode, Vec_Ptr_t * vSuper, Vec_Int_t * vVars ) +{ + int fComp1, Var, Var1, i; +//printf( "Adding AND %d. (%d) %d\n", pNode->Id, vSuper->nSize+1, (int)pSat->sat_solver_stats.clauses ); + + assert( !Abc_ObjIsComplement( pNode ) ); + assert( Abc_ObjIsNode( pNode ) ); + +// nVars = sat_solver_nvars(pSat); + Var = (int)pNode->pCopy; +// Var = pNode->Id; + +// assert( Var < nVars ); + for ( i = 0; i < vSuper->nSize; i++ ) + { + // get the predecessor nodes + // get the complemented attributes of the nodes + fComp1 = Abc_ObjIsComplement(vSuper->pArray[i]); + // determine the variable numbers + Var1 = (int)Abc_ObjRegular(vSuper->pArray[i])->pCopy; +// Var1 = (int)Abc_ObjRegular(vSuper->pArray[i])->Id; + + // check that the variables are in the SAT manager +// assert( Var1 < nVars ); + + // suppose the AND-gate is A * B = C + // add !A => !C or A + !C + // fprintf( pFile, "%d %d 0%c", Var1, -Var, 10 ); + vVars->nSize = 0; + Vec_IntPush( vVars, toLitCond(Var1, fComp1) ); + Vec_IntPush( vVars, toLitCond(Var, 1 ) ); + if ( !sat_solver_addclause( pSat, vVars->pArray, vVars->pArray + vVars->nSize ) ) + return 0; + } + + // add A & B => C or !A + !B + C +// fprintf( pFile, "%d %d %d 0%c", -Var1, -Var2, Var, 10 ); + vVars->nSize = 0; + for ( i = 0; i < vSuper->nSize; i++ ) + { + // get the predecessor nodes + // get the complemented attributes of the nodes + fComp1 = Abc_ObjIsComplement(vSuper->pArray[i]); + // determine the variable numbers + Var1 = (int)Abc_ObjRegular(vSuper->pArray[i])->pCopy; +// Var1 = (int)Abc_ObjRegular(vSuper->pArray[i])->Id; + // add this variable to the array + Vec_IntPush( vVars, toLitCond(Var1, !fComp1) ); + } + Vec_IntPush( vVars, toLitCond(Var, 0) ); + return sat_solver_addclause( pSat, vVars->pArray, vVars->pArray + vVars->nSize ); +} + +/**Function************************************************************* + + Synopsis [Adds trivial clause.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkClauseMux( sat_solver * pSat, Abc_Obj_t * pNode, Abc_Obj_t * pNodeC, Abc_Obj_t * pNodeT, Abc_Obj_t * pNodeE, Vec_Int_t * vVars ) +{ + int VarF, VarI, VarT, VarE, fCompT, fCompE; +//printf( "Adding mux %d. %d\n", pNode->Id, (int)pSat->sat_solver_stats.clauses ); + + assert( !Abc_ObjIsComplement( pNode ) ); + assert( Abc_NodeIsMuxType( pNode ) ); + // get the variable numbers + VarF = (int)pNode->pCopy; + VarI = (int)pNodeC->pCopy; + VarT = (int)Abc_ObjRegular(pNodeT)->pCopy; + VarE = (int)Abc_ObjRegular(pNodeE)->pCopy; +// VarF = (int)pNode->Id; +// VarI = (int)pNodeC->Id; +// VarT = (int)Abc_ObjRegular(pNodeT)->Id; +// VarE = (int)Abc_ObjRegular(pNodeE)->Id; + + // get the complementation flags + fCompT = Abc_ObjIsComplement(pNodeT); + fCompE = Abc_ObjIsComplement(pNodeE); + + // f = ITE(i, t, e) + // i' + t' + f + // i' + t + f' + // i + e' + f + // i + e + f' + // create four clauses + vVars->nSize = 0; + Vec_IntPush( vVars, toLitCond(VarI, 1) ); + Vec_IntPush( vVars, toLitCond(VarT, 1^fCompT) ); + Vec_IntPush( vVars, toLitCond(VarF, 0) ); + if ( !sat_solver_addclause( pSat, vVars->pArray, vVars->pArray + vVars->nSize ) ) + return 0; + vVars->nSize = 0; + Vec_IntPush( vVars, toLitCond(VarI, 1) ); + Vec_IntPush( vVars, toLitCond(VarT, 0^fCompT) ); + Vec_IntPush( vVars, toLitCond(VarF, 1) ); + if ( !sat_solver_addclause( pSat, vVars->pArray, vVars->pArray + vVars->nSize ) ) + return 0; + vVars->nSize = 0; + Vec_IntPush( vVars, toLitCond(VarI, 0) ); + Vec_IntPush( vVars, toLitCond(VarE, 1^fCompE) ); + Vec_IntPush( vVars, toLitCond(VarF, 0) ); + if ( !sat_solver_addclause( pSat, vVars->pArray, vVars->pArray + vVars->nSize ) ) + return 0; + vVars->nSize = 0; + Vec_IntPush( vVars, toLitCond(VarI, 0) ); + Vec_IntPush( vVars, toLitCond(VarE, 0^fCompE) ); + Vec_IntPush( vVars, toLitCond(VarF, 1) ); + if ( !sat_solver_addclause( pSat, vVars->pArray, vVars->pArray + vVars->nSize ) ) + return 0; + + if ( VarT == VarE ) + { +// assert( fCompT == !fCompE ); + return 1; + } + + // two additional clauses + // t' & e' -> f' t + e + f' + // t & e -> f t' + e' + f + vVars->nSize = 0; + Vec_IntPush( vVars, toLitCond(VarT, 0^fCompT) ); + Vec_IntPush( vVars, toLitCond(VarE, 0^fCompE) ); + Vec_IntPush( vVars, toLitCond(VarF, 1) ); + if ( !sat_solver_addclause( pSat, vVars->pArray, vVars->pArray + vVars->nSize ) ) + return 0; + vVars->nSize = 0; + Vec_IntPush( vVars, toLitCond(VarT, 1^fCompT) ); + Vec_IntPush( vVars, toLitCond(VarE, 1^fCompE) ); + Vec_IntPush( vVars, toLitCond(VarF, 0) ); + return sat_solver_addclause( pSat, vVars->pArray, vVars->pArray + vVars->nSize ); +} + +/**Function************************************************************* + + Synopsis [Returns the array of nodes to be combined into one multi-input AND-gate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkCollectSupergate_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vSuper, int fFirst, int fStopAtMux ) +{ + int RetValue1, RetValue2, i; + // check if the node is visited + if ( Abc_ObjRegular(pNode)->fMarkB ) + { + // check if the node occurs in the same polarity + for ( i = 0; i < vSuper->nSize; i++ ) + if ( vSuper->pArray[i] == pNode ) + return 1; + // check if the node is present in the opposite polarity + for ( i = 0; i < vSuper->nSize; i++ ) + if ( vSuper->pArray[i] == Abc_ObjNot(pNode) ) + return -1; + assert( 0 ); + return 0; + } + // if the new node is complemented or a PI, another gate begins + if ( !fFirst ) + if ( Abc_ObjIsComplement(pNode) || !Abc_ObjIsNode(pNode) || Abc_ObjFanoutNum(pNode) > 1 || fStopAtMux && Abc_NodeIsMuxType(pNode) ) + { + Vec_PtrPush( vSuper, pNode ); + Abc_ObjRegular(pNode)->fMarkB = 1; + return 0; + } + assert( !Abc_ObjIsComplement(pNode) ); + assert( Abc_ObjIsNode(pNode) ); + // go through the branches + RetValue1 = Abc_NtkCollectSupergate_rec( Abc_ObjChild0(pNode), vSuper, 0, fStopAtMux ); + RetValue2 = Abc_NtkCollectSupergate_rec( Abc_ObjChild1(pNode), vSuper, 0, fStopAtMux ); + if ( RetValue1 == -1 || RetValue2 == -1 ) + return -1; + // return 1 if at least one branch has a duplicate + return RetValue1 || RetValue2; +} + +/**Function************************************************************* + + Synopsis [Returns the array of nodes to be combined into one multi-input AND-gate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkCollectSupergate( Abc_Obj_t * pNode, int fStopAtMux, Vec_Ptr_t * vNodes ) +{ + int RetValue, i; + assert( !Abc_ObjIsComplement(pNode) ); + // collect the nodes in the implication supergate + Vec_PtrClear( vNodes ); + RetValue = Abc_NtkCollectSupergate_rec( pNode, vNodes, 1, fStopAtMux ); + assert( vNodes->nSize > 1 ); + // unmark the visited nodes + for ( i = 0; i < vNodes->nSize; i++ ) + Abc_ObjRegular((Abc_Obj_t *)vNodes->pArray[i])->fMarkB = 0; + // if we found the node and its complement in the same implication supergate, + // return empty set of nodes (meaning that we should use constant-0 node) + if ( RetValue == -1 ) + vNodes->nSize = 0; +} + + +/**Function************************************************************* + + Synopsis [Computes the factor of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkNodeFactor( Abc_Obj_t * pObj, int nLevelMax ) +{ +// nLevelMax = ((nLevelMax)/2)*3; + assert( (int)pObj->Level <= nLevelMax ); +// return (int)(100000000.0 * pow(0.999, nLevelMax - pObj->Level)); + return (int)(100000000.0 * (1 + 0.01 * pObj->Level)); +// return (int)(100000000.0 / ((nLevelMax)/2)*3 - pObj->Level); +} + +/**Function************************************************************* + + Synopsis [Sets up the SAT sat_solver.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkMiterSatCreateInt( sat_solver * pSat, Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode, * pFanin, * pNodeC, * pNodeT, * pNodeE; + Vec_Ptr_t * vNodes, * vSuper; + Vec_Int_t * vVars; + int i, k, fUseMuxes = 1; + int clk1 = clock(); +// int fOrderCiVarsFirst = 0; + int nLevelsMax = Abc_AigLevel(pNtk); + int RetValue = 0; + + assert( Abc_NtkIsStrash(pNtk) ); + + // clean the CI node pointers + Abc_NtkForEachCi( pNtk, pNode, i ) + pNode->pCopy = NULL; + + // start the data structures + vNodes = Vec_PtrAlloc( 1000 ); // the nodes corresponding to vars in the sat_solver + vSuper = Vec_PtrAlloc( 100 ); // the nodes belonging to the given implication supergate + vVars = Vec_IntAlloc( 100 ); // the temporary array for variables in the clause + + // add the clause for the constant node + pNode = Abc_AigConst1(pNtk); + pNode->fMarkA = 1; + pNode->pCopy = (Abc_Obj_t *)vNodes->nSize; + Vec_PtrPush( vNodes, pNode ); + Abc_NtkClauseTriv( pSat, pNode, vVars ); +/* + // add the PI variables first + Abc_NtkForEachCi( pNtk, pNode, i ) + { + pNode->fMarkA = 1; + pNode->pCopy = (Abc_Obj_t *)vNodes->nSize; + Vec_PtrPush( vNodes, pNode ); + } +*/ + // collect the nodes that need clauses and top-level assignments + Vec_PtrClear( vSuper ); + Abc_NtkForEachCo( pNtk, pNode, i ) + { + // get the fanin + pFanin = Abc_ObjFanin0(pNode); + // create the node's variable + if ( pFanin->fMarkA == 0 ) + { + pFanin->fMarkA = 1; + pFanin->pCopy = (Abc_Obj_t *)vNodes->nSize; + Vec_PtrPush( vNodes, pFanin ); + } + // add the trivial clause + Vec_PtrPush( vSuper, Abc_ObjChild0(pNode) ); + } + if ( !Abc_NtkClauseTop( pSat, vSuper, vVars ) ) + goto Quits; + + + // add the clauses + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + assert( !Abc_ObjIsComplement(pNode) ); + if ( !Abc_AigNodeIsAnd(pNode) ) + continue; +//printf( "%d ", pNode->Id ); + + // add the clauses + if ( fUseMuxes && Abc_NodeIsMuxType(pNode) ) + { + nMuxes++; + + pNodeC = Abc_NodeRecognizeMux( pNode, &pNodeT, &pNodeE ); + Vec_PtrClear( vSuper ); + Vec_PtrPush( vSuper, pNodeC ); + Vec_PtrPush( vSuper, pNodeT ); + Vec_PtrPush( vSuper, pNodeE ); + // add the fanin nodes to explore + Vec_PtrForEachEntry( vSuper, pFanin, k ) + { + pFanin = Abc_ObjRegular(pFanin); + if ( pFanin->fMarkA == 0 ) + { + pFanin->fMarkA = 1; + pFanin->pCopy = (Abc_Obj_t *)vNodes->nSize; + Vec_PtrPush( vNodes, pFanin ); + } + } + // add the clauses + if ( !Abc_NtkClauseMux( pSat, pNode, pNodeC, pNodeT, pNodeE, vVars ) ) + goto Quits; + } + else + { + // get the supergate + Abc_NtkCollectSupergate( pNode, fUseMuxes, vSuper ); + // add the fanin nodes to explore + Vec_PtrForEachEntry( vSuper, pFanin, k ) + { + pFanin = Abc_ObjRegular(pFanin); + if ( pFanin->fMarkA == 0 ) + { + pFanin->fMarkA = 1; + pFanin->pCopy = (Abc_Obj_t *)vNodes->nSize; + Vec_PtrPush( vNodes, pFanin ); + } + } + // add the clauses + if ( vSuper->nSize == 0 ) + { + if ( !Abc_NtkClauseTriv( pSat, Abc_ObjNot(pNode), vVars ) ) +// if ( !Abc_NtkClauseTriv( pSat, pNode, vVars ) ) + goto Quits; + } + else + { + if ( !Abc_NtkClauseAnd( pSat, pNode, vSuper, vVars ) ) + goto Quits; + } + } + } +/* + // set preferred variables + if ( fOrderCiVarsFirst ) + { + int * pPrefVars = ALLOC( int, Abc_NtkCiNum(pNtk) ); + int nVars = 0; + Abc_NtkForEachCi( pNtk, pNode, i ) + { + if ( pNode->fMarkA == 0 ) + continue; + pPrefVars[nVars++] = (int)pNode->pCopy; + } + nVars = ABC_MIN( nVars, 10 ); + ASat_SolverSetPrefVars( pSat, pPrefVars, nVars ); + } +*/ + RetValue = 1; +Quits : + // delete + Vec_IntFree( vVars ); + Vec_PtrFree( vNodes ); + Vec_PtrFree( vSuper ); + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Sets up the SAT sat_solver.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void * Abc_NtkMiterSatCreate( Abc_Ntk_t * pNtk, int fAllPrimes ) +{ + sat_solver * pSat; + Abc_Obj_t * pNode; + int RetValue, i, clk = clock(); + + assert( Abc_NtkIsStrash(pNtk) || Abc_NtkIsBddLogic(pNtk) ); + if ( Abc_NtkIsBddLogic(pNtk) ) + return Abc_NtkMiterSatCreateLogic(pNtk, fAllPrimes); + + nMuxes = 0; + pSat = sat_solver_new(); +//sat_solver_store_alloc( pSat ); + RetValue = Abc_NtkMiterSatCreateInt( pSat, pNtk ); +sat_solver_store_mark_roots( pSat ); + + Abc_NtkForEachObj( pNtk, pNode, i ) + pNode->fMarkA = 0; +// ASat_SolverWriteDimacs( pSat, "temp_sat.cnf", NULL, NULL, 1 ); + if ( RetValue == 0 ) + { + sat_solver_delete(pSat); + return NULL; + } +// printf( "Ands = %6d. Muxes = %6d (%5.2f %%). ", Abc_NtkNodeNum(pNtk), nMuxes, 300.0*nMuxes/Abc_NtkNodeNum(pNtk) ); +// PRT( "Creating sat_solver", clock() - clk ); + return pSat; +} + + + + +/**Function************************************************************* + + Synopsis [Adds clauses for the internal node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeAddClauses( sat_solver * pSat, char * pSop0, char * pSop1, Abc_Obj_t * pNode, Vec_Int_t * vVars ) +{ + Abc_Obj_t * pFanin; + int i, c, nFanins; + int RetValue; + char * pCube; + + nFanins = Abc_ObjFaninNum( pNode ); + assert( nFanins == Abc_SopGetVarNum( pSop0 ) ); + +// if ( nFanins == 0 ) + if ( Cudd_Regular(pNode->pData) == Cudd_ReadOne(pNode->pNtk->pManFunc) ) + { + vVars->nSize = 0; +// if ( Abc_SopIsConst1(pSop1) ) + if ( !Cudd_IsComplement(pNode->pData) ) + Vec_IntPush( vVars, toLit(pNode->Id) ); + else + Vec_IntPush( vVars, lit_neg(toLit(pNode->Id)) ); + RetValue = sat_solver_addclause( pSat, vVars->pArray, vVars->pArray + vVars->nSize ); + if ( !RetValue ) + { + printf( "The CNF is trivially UNSAT.\n" ); + return 0; + } + return 1; + } + + // add clauses for the negative phase + for ( c = 0; ; c++ ) + { + // get the cube + pCube = pSop0 + c * (nFanins + 3); + if ( *pCube == 0 ) + break; + // add the clause + vVars->nSize = 0; + Abc_ObjForEachFanin( pNode, pFanin, i ) + { + if ( pCube[i] == '0' ) + Vec_IntPush( vVars, toLit(pFanin->Id) ); + else if ( pCube[i] == '1' ) + Vec_IntPush( vVars, lit_neg(toLit(pFanin->Id)) ); + } + Vec_IntPush( vVars, lit_neg(toLit(pNode->Id)) ); + RetValue = sat_solver_addclause( pSat, vVars->pArray, vVars->pArray + vVars->nSize ); + if ( !RetValue ) + { + printf( "The CNF is trivially UNSAT.\n" ); + return 0; + } + } + + // add clauses for the positive phase + for ( c = 0; ; c++ ) + { + // get the cube + pCube = pSop1 + c * (nFanins + 3); + if ( *pCube == 0 ) + break; + // add the clause + vVars->nSize = 0; + Abc_ObjForEachFanin( pNode, pFanin, i ) + { + if ( pCube[i] == '0' ) + Vec_IntPush( vVars, toLit(pFanin->Id) ); + else if ( pCube[i] == '1' ) + Vec_IntPush( vVars, lit_neg(toLit(pFanin->Id)) ); + } + Vec_IntPush( vVars, toLit(pNode->Id) ); + RetValue = sat_solver_addclause( pSat, vVars->pArray, vVars->pArray + vVars->nSize ); + if ( !RetValue ) + { + printf( "The CNF is trivially UNSAT.\n" ); + return 0; + } + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Adds clauses for the PO node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeAddClausesTop( sat_solver * pSat, Abc_Obj_t * pNode, Vec_Int_t * vVars ) +{ + Abc_Obj_t * pFanin; + int RetValue; + + pFanin = Abc_ObjFanin0(pNode); + if ( Abc_ObjFaninC0(pNode) ) + { + vVars->nSize = 0; + Vec_IntPush( vVars, toLit(pFanin->Id) ); + Vec_IntPush( vVars, toLit(pNode->Id) ); + RetValue = sat_solver_addclause( pSat, vVars->pArray, vVars->pArray + vVars->nSize ); + if ( !RetValue ) + { + printf( "The CNF is trivially UNSAT.\n" ); + return 0; + } + + vVars->nSize = 0; + Vec_IntPush( vVars, lit_neg(toLit(pFanin->Id)) ); + Vec_IntPush( vVars, lit_neg(toLit(pNode->Id)) ); + RetValue = sat_solver_addclause( pSat, vVars->pArray, vVars->pArray + vVars->nSize ); + if ( !RetValue ) + { + printf( "The CNF is trivially UNSAT.\n" ); + return 0; + } + } + else + { + vVars->nSize = 0; + Vec_IntPush( vVars, lit_neg(toLit(pFanin->Id)) ); + Vec_IntPush( vVars, toLit(pNode->Id) ); + RetValue = sat_solver_addclause( pSat, vVars->pArray, vVars->pArray + vVars->nSize ); + if ( !RetValue ) + { + printf( "The CNF is trivially UNSAT.\n" ); + return 0; + } + + vVars->nSize = 0; + Vec_IntPush( vVars, toLit(pFanin->Id) ); + Vec_IntPush( vVars, lit_neg(toLit(pNode->Id)) ); + RetValue = sat_solver_addclause( pSat, vVars->pArray, vVars->pArray + vVars->nSize ); + if ( !RetValue ) + { + printf( "The CNF is trivially UNSAT.\n" ); + return 0; + } + } + + vVars->nSize = 0; + Vec_IntPush( vVars, toLit(pNode->Id) ); + RetValue = sat_solver_addclause( pSat, vVars->pArray, vVars->pArray + vVars->nSize ); + if ( !RetValue ) + { + printf( "The CNF is trivially UNSAT.\n" ); + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Sets up the SAT sat_solver.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +sat_solver * Abc_NtkMiterSatCreateLogic( Abc_Ntk_t * pNtk, int fAllPrimes ) +{ + sat_solver * pSat; + Extra_MmFlex_t * pMmFlex; + Abc_Obj_t * pNode; + Vec_Str_t * vCube; + Vec_Int_t * vVars; + char * pSop0, * pSop1; + int i; + + assert( Abc_NtkIsBddLogic(pNtk) ); + + // transfer the IDs to the copy field + Abc_NtkForEachPi( pNtk, pNode, i ) + pNode->pCopy = (void *)pNode->Id; + + // start the data structures + pSat = sat_solver_new(); +sat_solver_store_alloc( pSat ); + pMmFlex = Extra_MmFlexStart(); + vCube = Vec_StrAlloc( 100 ); + vVars = Vec_IntAlloc( 100 ); + + // add clauses for each internal nodes + Abc_NtkForEachNode( pNtk, pNode, i ) + { + // derive SOPs for both phases of the node + Abc_NodeBddToCnf( pNode, pMmFlex, vCube, fAllPrimes, &pSop0, &pSop1 ); + // add the clauses to the sat_solver + if ( !Abc_NodeAddClauses( pSat, pSop0, pSop1, pNode, vVars ) ) + { + sat_solver_delete( pSat ); + pSat = NULL; + goto finish; + } + } + // add clauses for each PO + Abc_NtkForEachPo( pNtk, pNode, i ) + { + if ( !Abc_NodeAddClausesTop( pSat, pNode, vVars ) ) + { + sat_solver_delete( pSat ); + pSat = NULL; + goto finish; + } + } +sat_solver_store_mark_roots( pSat ); + +finish: + // delete + Vec_StrFree( vCube ); + Vec_IntFree( vVars ); + Extra_MmFlexStop( pMmFlex ); + return pSat; +} + + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcStrash.c b/abc_with_bb_support/src/base/abci/abcStrash.c new file mode 100644 index 000000000..cd4d83738 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcStrash.c @@ -0,0 +1,477 @@ +/**CFile**************************************************************** + + FileName [abcStrash.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Strashing of the current network.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcStrash.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "extra.h" +#include "dec.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Abc_NtkStrashPerform( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkNew, int fAllNodes, int fRecord ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Reapplies structural hashing to the AIG.] + + Description [Because of the structural hashing, this procedure should not + change the number of nodes. It is useful to detect the bugs in the original AIG.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkRestrash( Abc_Ntk_t * pNtk, bool fCleanup ) +{ + extern int timeRetime; + Abc_Ntk_t * pNtkAig; + Abc_Obj_t * pObj; + int i, nNodes;//, RetValue; + assert( Abc_NtkIsStrash(pNtk) ); +//timeRetime = clock(); + // print warning about choice nodes + if ( Abc_NtkGetChoiceNum( pNtk ) ) + printf( "Warning: The choice nodes in the original AIG are removed by strashing.\n" ); + // start the new network (constants and CIs of the old network will point to the their counterparts in the new network) + pNtkAig = Abc_NtkStartFrom( pNtk, ABC_NTK_STRASH, ABC_FUNC_AIG ); + // restrash the nodes (assuming a topological order of the old network) + Abc_NtkForEachNode( pNtk, pObj, i ) + pObj->pCopy = Abc_AigAnd( pNtkAig->pManFunc, Abc_ObjChild0Copy(pObj), Abc_ObjChild1Copy(pObj) ); + // finalize the network + Abc_NtkFinalize( pNtk, pNtkAig ); + // print warning about self-feed latches +// if ( Abc_NtkCountSelfFeedLatches(pNtkAig) ) +// printf( "Warning: The network has %d self-feeding latches.\n", Abc_NtkCountSelfFeedLatches(pNtkAig) ); + // perform cleanup if requested + if ( fCleanup && (nNodes = Abc_AigCleanup(pNtkAig->pManFunc)) ) + printf( "Abc_NtkRestrash(): AIG cleanup removed %d nodes (this is a bug).\n", nNodes ); + // duplicate EXDC + if ( pNtk->pExdc ) + pNtkAig->pExdc = Abc_NtkDup( pNtk->pExdc ); + // make sure everything is okay + if ( !Abc_NtkCheck( pNtkAig ) ) + { + printf( "Abc_NtkStrash: The network check has failed.\n" ); + Abc_NtkDelete( pNtkAig ); + return NULL; + } +//timeRetime = clock() - timeRetime; +// if ( RetValue = Abc_NtkRemoveSelfFeedLatches(pNtkAig) ) +// printf( "Modified %d self-feeding latches. The result will not verify.\n", RetValue ); + return pNtkAig; + +} + +/**Function************************************************************* + + Synopsis [Reapplies structural hashing to the AIG.] + + Description [Because of the structural hashing, this procedure should not + change the number of nodes. It is useful to detect the bugs in the original AIG.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkRestrashZero( Abc_Ntk_t * pNtk, bool fCleanup ) +{ + extern int timeRetime; + Abc_Ntk_t * pNtkAig; + Abc_Obj_t * pObj; + int i, nNodes;//, RetValue; + assert( Abc_NtkIsStrash(pNtk) ); +//timeRetime = clock(); + // print warning about choice nodes + if ( Abc_NtkGetChoiceNum( pNtk ) ) + printf( "Warning: The choice nodes in the original AIG are removed by strashing.\n" ); + // start the new network (constants and CIs of the old network will point to the their counterparts in the new network) + pNtkAig = Abc_NtkStartFrom( pNtk, ABC_NTK_STRASH, ABC_FUNC_AIG ); + // complement the 1-values registers + Abc_NtkForEachLatch( pNtk, pObj, i ) + if ( Abc_LatchIsInit1(pObj) ) + Abc_ObjFanout0(pObj)->pCopy = Abc_ObjNot(Abc_ObjFanout0(pObj)->pCopy); + // restrash the nodes (assuming a topological order of the old network) + Abc_NtkForEachNode( pNtk, pObj, i ) + pObj->pCopy = Abc_AigAnd( pNtkAig->pManFunc, Abc_ObjChild0Copy(pObj), Abc_ObjChild1Copy(pObj) ); + // finalize the network + Abc_NtkFinalize( pNtk, pNtkAig ); + // complement the 1-valued registers + Abc_NtkForEachLatch( pNtkAig, pObj, i ) + if ( Abc_LatchIsInit1(pObj) ) + Abc_ObjXorFaninC( Abc_ObjFanin0(pObj), 0 ); + // set all constant-0 values + Abc_NtkForEachLatch( pNtkAig, pObj, i ) + Abc_LatchSetInit0( pObj ); + + // print warning about self-feed latches +// if ( Abc_NtkCountSelfFeedLatches(pNtkAig) ) +// printf( "Warning: The network has %d self-feeding latches.\n", Abc_NtkCountSelfFeedLatches(pNtkAig) ); + // perform cleanup if requested + if ( fCleanup && (nNodes = Abc_AigCleanup(pNtkAig->pManFunc)) ) + printf( "Abc_NtkRestrash(): AIG cleanup removed %d nodes (this is a bug).\n", nNodes ); + // duplicate EXDC + if ( pNtk->pExdc ) + pNtkAig->pExdc = Abc_NtkDup( pNtk->pExdc ); + // make sure everything is okay + if ( !Abc_NtkCheck( pNtkAig ) ) + { + printf( "Abc_NtkStrash: The network check has failed.\n" ); + Abc_NtkDelete( pNtkAig ); + return NULL; + } +//timeRetime = clock() - timeRetime; +// if ( RetValue = Abc_NtkRemoveSelfFeedLatches(pNtkAig) ) +// printf( "Modified %d self-feeding latches. The result will not verify.\n", RetValue ); + return pNtkAig; + +} + +/**Function************************************************************* + + Synopsis [Transforms logic network into structurally hashed AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkStrash( Abc_Ntk_t * pNtk, int fAllNodes, int fCleanup, int fRecord ) +{ + Abc_Ntk_t * pNtkAig; + int nNodes; + assert( Abc_NtkIsLogic(pNtk) || Abc_NtkIsStrash(pNtk) ); + // consider the special case when the network is already structurally hashed + if ( Abc_NtkIsStrash(pNtk) ) + return Abc_NtkRestrash( pNtk, fCleanup ); + // convert the node representation in the logic network to the AIG form + if ( !Abc_NtkToAig(pNtk) ) + { + printf( "Converting to AIGs has failed.\n" ); + return NULL; + } + // perform strashing +// Abc_NtkCleanCopy( pNtk ); + pNtkAig = Abc_NtkStartFrom( pNtk, ABC_NTK_STRASH, ABC_FUNC_AIG ); + Abc_NtkStrashPerform( pNtk, pNtkAig, fAllNodes, fRecord ); + Abc_NtkFinalize( pNtk, pNtkAig ); + // print warning about self-feed latches +// if ( Abc_NtkCountSelfFeedLatches(pNtkAig) ) +// printf( "Warning: The network has %d self-feeding latches.\n", Abc_NtkCountSelfFeedLatches(pNtkAig) ); + // perform cleanup if requested + nNodes = fCleanup? Abc_AigCleanup(pNtkAig->pManFunc) : 0; +// if ( nNodes ) +// printf( "Warning: AIG cleanup removed %d nodes (this is not a bug).\n", nNodes ); + // duplicate EXDC + if ( pNtk->pExdc ) + pNtkAig->pExdc = Abc_NtkDup( pNtk->pExdc ); + // make sure everything is okay + if ( !Abc_NtkCheck( pNtkAig ) ) + { + printf( "Abc_NtkStrash: The network check has failed.\n" ); + Abc_NtkDelete( pNtkAig ); + return NULL; + } + return pNtkAig; +} + +/**Function************************************************************* + + Synopsis [Appends the second network to the first.] + + Description [Modifies the first network by adding the logic of the second. + Performs structural hashing while appending the networks. Does not change + the second network. Returns 0 if the appending failed, 1 otherise.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkAppend( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int fAddPos ) +{ + Abc_Obj_t * pObj; + char * pName; + int i, nNewCis; + // the first network should be an AIG + assert( Abc_NtkIsStrash(pNtk1) ); + assert( Abc_NtkIsLogic(pNtk2) || Abc_NtkIsStrash(pNtk2) ); + if ( Abc_NtkIsLogic(pNtk2) && !Abc_NtkToAig(pNtk2) ) + { + printf( "Converting to AIGs has failed.\n" ); + return 0; + } + // check that the networks have the same PIs + // reorder PIs of pNtk2 according to pNtk1 + if ( !Abc_NtkCompareSignals( pNtk1, pNtk2, 1, 1 ) ) + printf( "Abc_NtkAppend(): The union of the network PIs is computed (warning).\n" ); + // perform strashing + nNewCis = 0; + Abc_NtkCleanCopy( pNtk2 ); + Abc_AigConst1(pNtk2)->pCopy = Abc_AigConst1(pNtk1); + Abc_NtkForEachCi( pNtk2, pObj, i ) + { + pName = Abc_ObjName(pObj); + pObj->pCopy = Abc_NtkFindCi(pNtk1, Abc_ObjName(pObj)); + if ( pObj->pCopy == NULL ) + { + pObj->pCopy = Abc_NtkDupObj(pNtk1, pObj, 1); + nNewCis++; + } + } + if ( nNewCis ) + printf( "Warning: Procedure Abc_NtkAppend() added %d new CIs.\n", nNewCis ); + // add pNtk2 to pNtk1 while strashing + if ( Abc_NtkIsLogic(pNtk2) ) + Abc_NtkStrashPerform( pNtk2, pNtk1, 1, 0 ); + else + Abc_NtkForEachNode( pNtk2, pObj, i ) + pObj->pCopy = Abc_AigAnd( pNtk1->pManFunc, Abc_ObjChild0Copy(pObj), Abc_ObjChild1Copy(pObj) ); + // add the COs of the second network + if ( fAddPos ) + { + Abc_NtkForEachPo( pNtk2, pObj, i ) + { + Abc_NtkDupObj( pNtk1, pObj, 0 ); + Abc_ObjAddFanin( pObj->pCopy, Abc_ObjChild0Copy(pObj) ); + Abc_ObjAssignName( pObj->pCopy, Abc_ObjName(pObj->pCopy), NULL ); + } + } + else + { + Abc_Obj_t * pObjOld, * pDriverOld, * pDriverNew; + int fCompl, iNodeId; + // OR the choices + Abc_NtkForEachCo( pNtk2, pObj, i ) + { + iNodeId = Nm_ManFindIdByNameTwoTypes( pNtk1->pManName, Abc_ObjName(pObj), ABC_OBJ_PO, ABC_OBJ_BI ); + assert( iNodeId >= 0 ); + pObjOld = Abc_NtkObj( pNtk1, iNodeId ); + // derive the new driver + pDriverOld = Abc_ObjChild0( pObjOld ); + pDriverNew = Abc_ObjChild0Copy( pObj ); + pDriverNew = Abc_AigOr( pNtk1->pManFunc, pDriverOld, pDriverNew ); + if ( Abc_ObjRegular(pDriverOld) == Abc_ObjRegular(pDriverNew) ) + continue; + // replace the old driver by the new driver + fCompl = Abc_ObjRegular(pDriverOld)->fPhase ^ Abc_ObjRegular(pDriverNew)->fPhase; + Abc_ObjPatchFanin( pObjOld, Abc_ObjRegular(pDriverOld), Abc_ObjNotCond(Abc_ObjRegular(pDriverNew), fCompl) ); + } + } + // make sure that everything is okay + if ( !Abc_NtkCheck( pNtk1 ) ) + { + printf( "Abc_NtkAppend: The network check has failed.\n" ); + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Prepares the network for strashing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkStrashPerform( Abc_Ntk_t * pNtkOld, Abc_Ntk_t * pNtkNew, int fAllNodes, int fRecord ) +{ + ProgressBar * pProgress; + Vec_Ptr_t * vNodes; + Abc_Obj_t * pNodeOld; + int i, clk = clock(); + assert( Abc_NtkIsLogic(pNtkOld) ); + assert( Abc_NtkIsStrash(pNtkNew) ); +// vNodes = Abc_NtkDfs( pNtkOld, fAllNodes ); + vNodes = Abc_NtkDfsIter( pNtkOld, fAllNodes ); +//printf( "Nodes = %d. ", Vec_PtrSize(vNodes) ); +//PRT( "Time", clock() - clk ); + pProgress = Extra_ProgressBarStart( stdout, vNodes->nSize ); + Vec_PtrForEachEntry( vNodes, pNodeOld, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + pNodeOld->pCopy = Abc_NodeStrash( pNtkNew, pNodeOld, fRecord ); + } + Extra_ProgressBarStop( pProgress ); + Vec_PtrFree( vNodes ); +} + +/**Function************************************************************* + + Synopsis [Transfers the AIG from one manager into another.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeStrash_rec( Abc_Aig_t * pMan, Hop_Obj_t * pObj ) +{ + assert( !Hop_IsComplement(pObj) ); + if ( !Hop_ObjIsNode(pObj) || Hop_ObjIsMarkA(pObj) ) + return; + Abc_NodeStrash_rec( pMan, Hop_ObjFanin0(pObj) ); + Abc_NodeStrash_rec( pMan, Hop_ObjFanin1(pObj) ); + pObj->pData = Abc_AigAnd( pMan, (Abc_Obj_t *)Hop_ObjChild0Copy(pObj), (Abc_Obj_t *)Hop_ObjChild1Copy(pObj) ); + assert( !Hop_ObjIsMarkA(pObj) ); // loop detection + Hop_ObjSetMarkA( pObj ); +} + +/**Function************************************************************* + + Synopsis [Strashes one logic node.] + + Description [Assume the network is in the AIG form] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NodeStrash( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pNodeOld, int fRecord ) +{ + Hop_Man_t * pMan; + Hop_Obj_t * pRoot; + Abc_Obj_t * pFanin; + int i; + assert( Abc_ObjIsNode(pNodeOld) ); + assert( Abc_NtkHasAig(pNodeOld->pNtk) && !Abc_NtkIsStrash(pNodeOld->pNtk) ); + // get the local AIG manager and the local root node + pMan = pNodeOld->pNtk->pManFunc; + pRoot = pNodeOld->pData; + // check the constant case + if ( Abc_NodeIsConst(pNodeOld) || Hop_Regular(pRoot) == Hop_ManConst1(pMan) ) + return Abc_ObjNotCond( Abc_AigConst1(pNtkNew), Hop_IsComplement(pRoot) ); + // perform special case-strashing using the record of AIG subgraphs + if ( fRecord && Abc_NtkRecIsRunning() && Abc_ObjFaninNum(pNodeOld) > 2 && Abc_ObjFaninNum(pNodeOld) <= Abc_NtkRecVarNum() ) + { + extern Vec_Int_t * Abc_NtkRecMemory(); + extern int Abc_NtkRecStrashNode( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pObj, unsigned * pTruth, int nVars ); + int nVars = Abc_NtkRecVarNum(); + Vec_Int_t * vMemory = Abc_NtkRecMemory(); + unsigned * pTruth = Abc_ConvertAigToTruth( pMan, Hop_Regular(pRoot), nVars, vMemory, 0 ); + assert( Extra_TruthSupportSize(pTruth, nVars) == Abc_ObjFaninNum(pNodeOld) ); // should be swept + if ( Hop_IsComplement(pRoot) ) + Extra_TruthNot( pTruth, pTruth, nVars ); + if ( Abc_NtkRecStrashNode( pNtkNew, pNodeOld, pTruth, nVars ) ) + return pNodeOld->pCopy; + } + // set elementary variables + Abc_ObjForEachFanin( pNodeOld, pFanin, i ) + Hop_IthVar(pMan, i)->pData = pFanin->pCopy; + // strash the AIG of this node + Abc_NodeStrash_rec( pNtkNew->pManFunc, Hop_Regular(pRoot) ); + Hop_ConeUnmark_rec( Hop_Regular(pRoot) ); + // return the final node + return Abc_ObjNotCond( Hop_Regular(pRoot)->pData, Hop_IsComplement(pRoot) ); +} + + + + + + + +/**Function************************************************************* + + Synopsis [Copies the topmost levels of the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkTopmost_rec( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pNode, int LevelCut ) +{ + assert( !Abc_ObjIsComplement(pNode) ); + if ( pNode->pCopy ) + return pNode->pCopy; + if ( pNode->Level <= (unsigned)LevelCut ) + return pNode->pCopy = Abc_NtkCreatePi( pNtkNew ); + Abc_NtkTopmost_rec( pNtkNew, Abc_ObjFanin0(pNode), LevelCut ); + Abc_NtkTopmost_rec( pNtkNew, Abc_ObjFanin1(pNode), LevelCut ); + return pNode->pCopy = Abc_AigAnd( pNtkNew->pManFunc, Abc_ObjChild0Copy(pNode), Abc_ObjChild1Copy(pNode) ); +} + +/**Function************************************************************* + + Synopsis [Copies the topmost levels of the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkTopmost( Abc_Ntk_t * pNtk, int nLevels ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObjNew, * pPoNew; + int LevelCut; + assert( Abc_NtkIsStrash(pNtk) ); + assert( Abc_NtkCoNum(pNtk) == 1 ); + // get the cutoff level + LevelCut = ABC_MAX( 0, Abc_AigLevel(pNtk) - nLevels ); + // start the network + pNtkNew = Abc_NtkAlloc( ABC_NTK_STRASH, ABC_FUNC_AIG, 1 ); + pNtkNew->pName = Extra_UtilStrsav(pNtk->pName); + Abc_AigConst1(pNtk)->pCopy = Abc_AigConst1(pNtkNew); + // create PIs below the cut and nodes above the cut + Abc_NtkCleanCopy( pNtk ); + pObjNew = Abc_NtkTopmost_rec( pNtkNew, Abc_ObjFanin0(Abc_NtkPo(pNtk, 0)), LevelCut ); + pObjNew = Abc_ObjNotCond( pObjNew, Abc_ObjFaninC0(Abc_NtkPo(pNtk, 0)) ); + // add the PO node and name + pPoNew = Abc_NtkCreatePo(pNtkNew); + Abc_ObjAddFanin( pPoNew, pObjNew ); + Abc_NtkAddDummyPiNames( pNtkNew ); + Abc_ObjAssignName( pPoNew, Abc_ObjName(Abc_NtkPo(pNtk, 0)), NULL ); + // make sure everything is okay + if ( !Abc_NtkCheck( pNtkNew ) ) + { + printf( "Abc_NtkTopmost: The network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcSweep.c b/abc_with_bb_support/src/base/abci/abcSweep.c new file mode 100644 index 000000000..e30c98a21 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcSweep.c @@ -0,0 +1,948 @@ +/**CFile**************************************************************** + + FileName [abcDsd.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Technology dependent sweep.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcDsd.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "fraig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Abc_NtkFraigSweepUsingExdc( Fraig_Man_t * pMan, Abc_Ntk_t * pNtk ); +static stmm_table * Abc_NtkFraigEquiv( Abc_Ntk_t * pNtk, int fUseInv, int fVerbose, int fVeryVerbose ); +static void Abc_NtkFraigTransform( Abc_Ntk_t * pNtk, stmm_table * tEquiv, int fUseInv, bool fVerbose ); +static void Abc_NtkFraigMergeClassMapped( Abc_Ntk_t * pNtk, Abc_Obj_t * pChain, int fUseInv, int fVerbose ); +static void Abc_NtkFraigMergeClass( Abc_Ntk_t * pNtk, Abc_Obj_t * pChain, int fUseInv, int fVerbose ); +static int Abc_NodeDroppingCost( Abc_Obj_t * pNode ); + +static int Abc_NtkReduceNodes( Abc_Ntk_t * pNtk, Vec_Ptr_t * vNodes ); +static void Abc_NodeSweep( Abc_Obj_t * pNode, int fVerbose ); +static void Abc_NodeConstantInput( Abc_Obj_t * pNode, Abc_Obj_t * pFanin, bool fConst0 ); +static void Abc_NodeComplementInput( Abc_Obj_t * pNode, Abc_Obj_t * pFanin ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Sweping functionally equivalence nodes.] + + Description [Removes gates with equivalent functionality. Works for + both technology-independent and mapped networks. If the flag is set, + allows adding inverters at the gate outputs.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkFraigSweep( Abc_Ntk_t * pNtk, int fUseInv, int fExdc, int fVerbose, int fVeryVerbose ) +{ + Fraig_Params_t Params; + Abc_Ntk_t * pNtkAig; + Fraig_Man_t * pMan; + stmm_table * tEquiv; + Abc_Obj_t * pObj; + int i, fUseTrick; + + assert( !Abc_NtkIsStrash(pNtk) ); + + // save gate assignments + fUseTrick = 0; + if ( Abc_NtkIsMappedLogic(pNtk) ) + { + fUseTrick = 1; + Abc_NtkForEachNode( pNtk, pObj, i ) + pObj->pNext = pObj->pData; + } + // derive the AIG + pNtkAig = Abc_NtkStrash( pNtk, 0, 1, 0 ); + // reconstruct gate assignments + if ( fUseTrick ) + { + extern void * Abc_FrameReadLibGen(); + Hop_ManStop( pNtk->pManFunc ); + pNtk->pManFunc = Abc_FrameReadLibGen(); + pNtk->ntkFunc = ABC_FUNC_MAP; + Abc_NtkForEachNode( pNtk, pObj, i ) + pObj->pData = pObj->pNext, pObj->pNext = NULL; + } + + // perform fraiging of the AIG + Fraig_ParamsSetDefault( &Params ); + pMan = Abc_NtkToFraig( pNtkAig, &Params, 0, 0 ); + // cannot use EXDC with FRAIG because it can create classes of equivalent FRAIG nodes + // with representative nodes that do not correspond to the nodes with the current network + + // update FRAIG using EXDC + if ( fExdc ) + { + if ( pNtk->pExdc == NULL ) + printf( "Warning: Networks has no EXDC.\n" ); + else + Abc_NtkFraigSweepUsingExdc( pMan, pNtk ); + } + // assign levels to the nodes of the network + Abc_NtkLevel( pNtk ); + + // collect the classes of equivalent nets + tEquiv = Abc_NtkFraigEquiv( pNtk, fUseInv, fVerbose, fVeryVerbose ); + + // transform the network into the equivalent one + Abc_NtkFraigTransform( pNtk, tEquiv, fUseInv, fVerbose ); + stmm_free_table( tEquiv ); + + // free the manager + Fraig_ManFree( pMan ); + Abc_NtkDelete( pNtkAig ); + + // cleanup the dangling nodes + if ( Abc_NtkHasMapping(pNtk) ) + Abc_NtkCleanup( pNtk, fVerbose ); + else + Abc_NtkSweep( pNtk, fVerbose ); + + // check + if ( !Abc_NtkCheck( pNtk ) ) + { + printf( "Abc_NtkFraigSweep: The network check has failed.\n" ); + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Sweep the network using EXDC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkFraigSweepUsingExdc( Fraig_Man_t * pMan, Abc_Ntk_t * pNtk ) +{ + Fraig_Node_t * gNodeExdc, * gNode, * gNodeRes; + Abc_Obj_t * pNode, * pNodeAig; + int i; + extern Fraig_Node_t * Abc_NtkToFraigExdc( Fraig_Man_t * pMan, Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkExdc ); + + assert( pNtk->pExdc ); + // derive FRAIG node representing don't-cares in the EXDC network + gNodeExdc = Abc_NtkToFraigExdc( pMan, pNtk, pNtk->pExdc ); + // update the node pointers + Abc_NtkForEachNode( pNtk, pNode, i ) + { + // skip the constant input nodes + if ( Abc_ObjFaninNum(pNode) == 0 ) + continue; + // get the strashed node + pNodeAig = pNode->pCopy; + // skip the dangling nodes + if ( pNodeAig == NULL ) + continue; + // get the FRAIG node + gNode = Fraig_NotCond( Abc_ObjRegular(pNodeAig)->pCopy, Abc_ObjIsComplement(pNodeAig) ); + // perform ANDing with EXDC + gNodeRes = Fraig_NodeAnd( pMan, gNode, Fraig_Not(gNodeExdc) ); + // write the node back + Abc_ObjRegular(pNodeAig)->pCopy = (Abc_Obj_t *)Fraig_NotCond( gNodeRes, Abc_ObjIsComplement(pNodeAig) ); + } +} + +/**Function************************************************************* + + Synopsis [Collects equivalence classses of node in the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +stmm_table * Abc_NtkFraigEquiv( Abc_Ntk_t * pNtk, int fUseInv, int fVerbose, int fVeryVerbose ) +{ + Abc_Obj_t * pList, * pNode, * pNodeAig; + Fraig_Node_t * gNode; + Abc_Obj_t ** ppSlot; + stmm_table * tStrash2Net; + stmm_table * tResult; + stmm_generator * gen; + int c, Counter; + + // create mapping of strashed nodes into the corresponding network nodes + tStrash2Net = stmm_init_table(stmm_ptrcmp,stmm_ptrhash); + Abc_NtkForEachNode( pNtk, pNode, c ) + { + // skip the constant input nodes + if ( Abc_ObjFaninNum(pNode) == 0 ) + continue; + // get the strashed node + pNodeAig = pNode->pCopy; + // skip the dangling nodes + if ( pNodeAig == NULL ) + continue; + // skip the nodes that fanout into COs + if ( Abc_NodeFindCoFanout(pNode) ) + continue; + // get the FRAIG node + gNode = Fraig_NotCond( Abc_ObjRegular(pNodeAig)->pCopy, Abc_ObjIsComplement(pNodeAig) ); + if ( !stmm_find_or_add( tStrash2Net, (char *)Fraig_Regular(gNode), (char ***)&ppSlot ) ) + *ppSlot = NULL; + // add the node to the list + pNode->pNext = *ppSlot; + *ppSlot = pNode; + // mark the node if it is complemented + pNode->fPhase = Fraig_IsComplement(gNode); + } + + // print the classes + c = 0; + Counter = 0; + tResult = stmm_init_table(stmm_ptrcmp,stmm_ptrhash); + stmm_foreach_item( tStrash2Net, gen, (char **)&gNode, (char **)&pList ) + { + // skip the trival classes + if ( pList == NULL || pList->pNext == NULL ) + continue; + // add the non-trival class + stmm_insert( tResult, (char *)pList, NULL ); + // count nodes in the non-trival classes + for ( pNode = pList; pNode; pNode = pNode->pNext ) + Counter++; + + if ( fVeryVerbose ) + { + printf( "Class %2d : {", c ); + for ( pNode = pList; pNode; pNode = pNode->pNext ) + { + pNode->pCopy = NULL; + printf( " %s", Abc_ObjName(pNode) ); + printf( "(%c)", pNode->fPhase? '-' : '+' ); + printf( "(%d)", pNode->Level ); + } + printf( " }\n" ); + c++; + } + } + if ( fVerbose || fVeryVerbose ) + { + printf( "Sweeping stats for network \"%s\":\n", pNtk->pName ); + printf( "Internal nodes = %d. Different functions (up to compl) = %d.\n", Abc_NtkNodeNum(pNtk), stmm_count(tStrash2Net) ); + printf( "Non-trivial classes = %d. Nodes in non-trivial classes = %d.\n", stmm_count(tResult), Counter ); + } + stmm_free_table( tStrash2Net ); + return tResult; +} + + +/**Function************************************************************* + + Synopsis [Transforms the network using the equivalence relation on nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkFraigTransform( Abc_Ntk_t * pNtk, stmm_table * tEquiv, int fUseInv, bool fVerbose ) +{ + stmm_generator * gen; + Abc_Obj_t * pList; + if ( stmm_count(tEquiv) == 0 ) + return; + // merge nodes in the classes + if ( Abc_NtkHasMapping( pNtk ) ) + { + Abc_NtkDelayTrace( pNtk ); + stmm_foreach_item( tEquiv, gen, (char **)&pList, NULL ) + Abc_NtkFraigMergeClassMapped( pNtk, pList, fUseInv, fVerbose ); + } + else + { + stmm_foreach_item( tEquiv, gen, (char **)&pList, NULL ) + Abc_NtkFraigMergeClass( pNtk, pList, fUseInv, fVerbose ); + } +} + + +/**Function************************************************************* + + Synopsis [Transforms the list of one-phase equivalent nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkFraigMergeClassMapped( Abc_Ntk_t * pNtk, Abc_Obj_t * pChain, int fUseInv, int fVerbose ) +{ + Abc_Obj_t * pListDir, * pListInv; + Abc_Obj_t * pNodeMin, * pNode, * pNext; + float Arrival1, Arrival2; + + assert( pChain ); + assert( pChain->pNext ); + + // divide the nodes into two parts: + // those that need the invertor and those that don't need + pListDir = pListInv = NULL; + for ( pNode = pChain, pNext = pChain->pNext; + pNode; + pNode = pNext, pNext = pNode? pNode->pNext : NULL ) + { + // check to which class the node belongs + if ( pNode->fPhase == 1 ) + { + pNode->pNext = pListDir; + pListDir = pNode; + } + else + { + pNode->pNext = pListInv; + pListInv = pNode; + } + } + + // find the node with the smallest number of logic levels + pNodeMin = pListDir; + for ( pNode = pListDir; pNode; pNode = pNode->pNext ) + { + Arrival1 = Abc_NodeReadArrival(pNodeMin)->Worst; + Arrival2 = Abc_NodeReadArrival(pNode )->Worst; + assert( Abc_ObjIsCi(pNodeMin) || Arrival1 > 0 ); + assert( Abc_ObjIsCi(pNode) || Arrival2 > 0 ); + if ( Arrival1 > Arrival2 || + Arrival1 == Arrival2 && pNodeMin->Level > pNode->Level || + Arrival1 == Arrival2 && pNodeMin->Level == pNode->Level && + Abc_NodeDroppingCost(pNodeMin) < Abc_NodeDroppingCost(pNode) ) + pNodeMin = pNode; + } + + // move the fanouts of the direct nodes + for ( pNode = pListDir; pNode; pNode = pNode->pNext ) + if ( pNode != pNodeMin ) + Abc_ObjTransferFanout( pNode, pNodeMin ); + + // find the node with the smallest number of logic levels + pNodeMin = pListInv; + for ( pNode = pListInv; pNode; pNode = pNode->pNext ) + { + Arrival1 = Abc_NodeReadArrival(pNodeMin)->Worst; + Arrival2 = Abc_NodeReadArrival(pNode )->Worst; + assert( Abc_ObjIsCi(pNodeMin) || Arrival1 > 0 ); + assert( Abc_ObjIsCi(pNode) || Arrival2 > 0 ); + if ( Arrival1 > Arrival2 || + Arrival1 == Arrival2 && pNodeMin->Level > pNode->Level || + Arrival1 == Arrival2 && pNodeMin->Level == pNode->Level && + Abc_NodeDroppingCost(pNodeMin) < Abc_NodeDroppingCost(pNode) ) + pNodeMin = pNode; + } + + // move the fanouts of the direct nodes + for ( pNode = pListInv; pNode; pNode = pNode->pNext ) + if ( pNode != pNodeMin ) + Abc_ObjTransferFanout( pNode, pNodeMin ); +} + +/**Function************************************************************* + + Synopsis [Process one equivalence class of nodes.] + + Description [This function does not remove the nodes. It only switches + around the connections.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkFraigMergeClass( Abc_Ntk_t * pNtk, Abc_Obj_t * pChain, int fUseInv, int fVerbose ) +{ + Abc_Obj_t * pListDir, * pListInv; + Abc_Obj_t * pNodeMin, * pNodeMinInv; + Abc_Obj_t * pNode, * pNext; + + assert( pChain ); + assert( pChain->pNext ); + + // find the node with the smallest number of logic levels + pNodeMin = pChain; + for ( pNode = pChain->pNext; pNode; pNode = pNode->pNext ) + if ( pNodeMin->Level > pNode->Level || + ( pNodeMin->Level == pNode->Level && + Abc_NodeDroppingCost(pNodeMin) < Abc_NodeDroppingCost(pNode) ) ) + pNodeMin = pNode; + + // divide the nodes into two parts: + // those that need the invertor and those that don't need + pListDir = pListInv = NULL; + for ( pNode = pChain, pNext = pChain->pNext; + pNode; + pNode = pNext, pNext = pNode? pNode->pNext : NULL ) + { + if ( pNode == pNodeMin ) + continue; + // check to which class the node belongs + if ( pNodeMin->fPhase == pNode->fPhase ) + { + pNode->pNext = pListDir; + pListDir = pNode; + } + else + { + pNode->pNext = pListInv; + pListInv = pNode; + } + } + + // move the fanouts of the direct nodes + for ( pNode = pListDir; pNode; pNode = pNode->pNext ) + Abc_ObjTransferFanout( pNode, pNodeMin ); + + // skip if there are no inverted nodes + if ( pListInv == NULL ) + return; + + // add the invertor + pNodeMinInv = Abc_NtkCreateNodeInv( pNtk, pNodeMin ); + + // move the fanouts of the inverted nodes + for ( pNode = pListInv; pNode; pNode = pNode->pNext ) + Abc_ObjTransferFanout( pNode, pNodeMinInv ); +} + + +/**Function************************************************************* + + Synopsis [Returns the number of literals saved if this node becomes useless.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeDroppingCost( Abc_Obj_t * pNode ) +{ + return 1; +} + + + + + +/**Function************************************************************* + + Synopsis [Removes dangling nodes.] + + Description [Returns the number of nodes removed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkCleanup( Abc_Ntk_t * pNtk, int fVerbose ) +{ + Vec_Ptr_t * vNodes; + int Counter; + assert( Abc_NtkIsLogic(pNtk) ); + // mark the nodes reachable from the POs + vNodes = Abc_NtkDfs( pNtk, 0 ); + Counter = Abc_NtkReduceNodes( pNtk, vNodes ); + if ( fVerbose ) + printf( "Cleanup removed %d dangling nodes.\n", Counter ); + Vec_PtrFree( vNodes ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Preserves the nodes collected in the array.] + + Description [Returns the number of nodes removed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkReduceNodes( Abc_Ntk_t * pNtk, Vec_Ptr_t * vNodes ) +{ + Abc_Obj_t * pNode; + int i, Counter; + assert( Abc_NtkIsLogic(pNtk) ); + // mark the nodes reachable from the POs + Vec_PtrForEachEntry( vNodes, pNode, i ) + pNode->fMarkA = 1; + // remove the non-marked nodes + Counter = 0; + Abc_NtkForEachNode( pNtk, pNode, i ) + if ( pNode->fMarkA == 0 ) + { + Abc_NtkDeleteObj( pNode ); + Counter++; + } + // unmark the remaining nodes + Vec_PtrForEachEntry( vNodes, pNode, i ) + pNode->fMarkA = 0; + // check + if ( !Abc_NtkCheck( pNtk ) ) + printf( "Abc_NtkCleanup: The network check has failed.\n" ); + return Counter; +} + + + + +/**Function************************************************************* + + Synopsis [Tranditional sweep of the network.] + + Description [Propagates constant and single-input node, removes dangling nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkSweep( Abc_Ntk_t * pNtk, int fVerbose ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pNode, * pFanout, * pDriver; + int i, nNodesOld; + assert( Abc_NtkIsLogic(pNtk) ); + // convert network to BDD representation + if ( !Abc_NtkToBdd(pNtk) ) + { + fprintf( stdout, "Converting to BDD has failed.\n" ); + return 1; + } + // perform cleanup + nNodesOld = Abc_NtkNodeNum(pNtk); + Abc_NtkCleanup( pNtk, 0 ); + // prepare nodes for sweeping + Abc_NtkRemoveDupFanins(pNtk); + Abc_NtkMinimumBase(pNtk); + // collect sweepable nodes + vNodes = Vec_PtrAlloc( 100 ); + Abc_NtkForEachNode( pNtk, pNode, i ) + if ( Abc_ObjFaninNum(pNode) < 2 ) + Vec_PtrPush( vNodes, pNode ); + // sweep the nodes + while ( Vec_PtrSize(vNodes) > 0 ) + { + // get any sweepable node + pNode = Vec_PtrPop(vNodes); + if ( !Abc_ObjIsNode(pNode) ) + continue; + // get any non-CO fanout of this node + pFanout = Abc_NodeFindNonCoFanout(pNode); + if ( pFanout == NULL ) + continue; + assert( Abc_ObjIsNode(pFanout) ); + // transform the function of the fanout + if ( Abc_ObjFaninNum(pNode) == 0 ) + Abc_NodeConstantInput( pFanout, pNode, Abc_NodeIsConst0(pNode) ); + else + { + assert( Abc_ObjFaninNum(pNode) == 1 ); + pDriver = Abc_ObjFanin0(pNode); + if ( Abc_NodeIsInv(pNode) ) + Abc_NodeComplementInput( pFanout, pNode ); + Abc_ObjPatchFanin( pFanout, pNode, pDriver ); + } + Abc_NodeRemoveDupFanins( pFanout ); + Abc_NodeMinimumBase( pFanout ); + // check if the fanout should be added + if ( Abc_ObjFaninNum(pFanout) < 2 ) + Vec_PtrPush( vNodes, pFanout ); + // check if the node has other fanouts + if ( Abc_ObjFanoutNum(pNode) > 0 ) + Vec_PtrPush( vNodes, pNode ); + else + Abc_NtkDeleteObj_rec( pNode, 1 ); + } + Vec_PtrFree( vNodes ); + // sweep a node into its CO fanout if all of this is true: + // (a) this node is a single-input node + // (b) the driver of the node has only one fanout (this node) + // (c) the driver is a node + Abc_NtkForEachCo( pNtk, pFanout, i ) + { + pNode = Abc_ObjFanin0(pFanout); + if ( Abc_ObjFaninNum(pNode) != 1 ) + continue; + pDriver = Abc_ObjFanin0(pNode); + if ( !(Abc_ObjFanoutNum(pDriver) == 1 && Abc_ObjIsNode(pDriver)) ) + continue; + // trasform this CO + if ( Abc_NodeIsInv(pNode) ) + pDriver->pData = Cudd_Not(pDriver->pData); + Abc_ObjPatchFanin( pFanout, pNode, pDriver ); + } + // perform cleanup + Abc_NtkCleanup( pNtk, 0 ); + // report + if ( fVerbose ) + printf( "Sweep removed %d nodes.\n", nNodesOld - Abc_NtkNodeNum(pNtk) ); + return nNodesOld - Abc_NtkNodeNum(pNtk); +} + + +/**Function************************************************************* + + Synopsis [Replaces the local function by its cofactor.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeConstantInput( Abc_Obj_t * pNode, Abc_Obj_t * pFanin, bool fConst0 ) +{ + DdManager * dd = pNode->pNtk->pManFunc; + DdNode * bVar, * bTemp; + int iFanin; + assert( Abc_NtkIsBddLogic(pNode->pNtk) ); + if ( (iFanin = Vec_IntFind( &pNode->vFanins, pFanin->Id )) == -1 ) + { + printf( "Node %s should be among", Abc_ObjName(pFanin) ); + printf( " the fanins of node %s...\n", Abc_ObjName(pNode) ); + return; + } + bVar = Cudd_NotCond( Cudd_bddIthVar(dd, iFanin), fConst0 ); + pNode->pData = Cudd_Cofactor( dd, bTemp = pNode->pData, bVar ); Cudd_Ref( pNode->pData ); + Cudd_RecursiveDeref( dd, bTemp ); +} + +/**Function************************************************************* + + Synopsis [Changes the polarity of one fanin.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeComplementInput( Abc_Obj_t * pNode, Abc_Obj_t * pFanin ) +{ + DdManager * dd = pNode->pNtk->pManFunc; + DdNode * bVar, * bCof0, * bCof1; + int iFanin; + assert( Abc_NtkIsBddLogic(pNode->pNtk) ); + if ( (iFanin = Vec_IntFind( &pNode->vFanins, pFanin->Id )) == -1 ) + { + printf( "Node %s should be among", Abc_ObjName(pFanin) ); + printf( " the fanins of node %s...\n", Abc_ObjName(pNode) ); + return; + } + bVar = Cudd_bddIthVar( dd, iFanin ); + bCof0 = Cudd_Cofactor( dd, pNode->pData, Cudd_Not(bVar) ); Cudd_Ref( bCof0 ); + bCof1 = Cudd_Cofactor( dd, pNode->pData, bVar ); Cudd_Ref( bCof1 ); + Cudd_RecursiveDeref( dd, pNode->pData ); + pNode->pData = Cudd_bddIte( dd, bVar, bCof0, bCof1 ); Cudd_Ref( pNode->pData ); + Cudd_RecursiveDeref( dd, bCof0 ); + Cudd_RecursiveDeref( dd, bCof1 ); +} + + + +/**Function************************************************************* + + Synopsis [Removes all objects whose trav ID is not current.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NodeRemoveNonCurrentObjects( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int Counter, i; + int fVerbose = 0; + + // report on the nodes to be deleted + if ( fVerbose ) + { + printf( "These nodes will be deleted: \n" ); + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( !Abc_NodeIsTravIdCurrent( pObj ) ) + { + printf( " " ); + Abc_ObjPrint( stdout, pObj ); + } + } + + // delete the nodes + Counter = 0; + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( !Abc_NodeIsTravIdCurrent( pObj ) ) + { + Abc_NtkDeleteObj( pObj ); + Counter++; + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Check if the fanin of this latch is a constant.] + + Description [Returns 0/1 if constant; -1 if not a constant.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkSetTravId_rec( Abc_Obj_t * pObj ) +{ + Abc_NodeSetTravIdCurrent(pObj); + if ( Abc_ObjFaninNum(pObj) == 0 ) + return; + assert( Abc_ObjFaninNum(pObj) == 1 ); + Abc_NtkSetTravId_rec( Abc_ObjFanin0(pObj) ); +} + +/**Function************************************************************* + + Synopsis [Check if the fanin of this latch is a constant.] + + Description [Returns 0/1 if constant; -1 if not a constant.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkCheckConstant_rec( Abc_Obj_t * pObj ) +{ + if ( Abc_ObjFaninNum(pObj) == 0 ) + { + if ( !Abc_ObjIsNode(pObj) ) + return -1; + if ( Abc_NodeIsConst0(pObj) ) + return 0; + if ( Abc_NodeIsConst1(pObj) ) + return 1; + assert( 0 ); + return -1; + } + if ( Abc_ObjIsLatch(pObj) || Abc_ObjFaninNum(pObj) > 1 ) + return -1; + if ( !Abc_ObjIsNode(pObj) || Abc_NodeIsBuf(pObj) ) + return Abc_NtkCheckConstant_rec( Abc_ObjFanin0(pObj) ); + if ( Abc_NodeIsInv(pObj) ) + { + int RetValue = Abc_NtkCheckConstant_rec( Abc_ObjFanin0(pObj) ); + if ( RetValue == 0 ) + return 1; + if ( RetValue == 1 ) + return 0; + return RetValue; + } + assert( 0 ); + return -1; +} + +/**Function************************************************************* + + Synopsis [Removes redundant latches.] + + Description [The redundant latches are of two types: + - Latches fed by a constant which matches the init value of the latch. + - Latches fed by a constant which does not match the init value of the latch + can be all replaced by one latch.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkLatchSweep( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pFanin, * pLatch, * pLatchPivot = NULL; + int Counter, RetValue, i; + Counter = 0; + // go through the latches + Abc_NtkForEachLatch( pNtk, pLatch, i ) + { + // check if the latch has constant input + RetValue = Abc_NtkCheckConstant_rec( Abc_ObjFanin0(pLatch) ); + if ( RetValue == -1 ) + continue; + // found a latch with constant fanin + if ( (RetValue == 1 && Abc_LatchIsInit0(pLatch)) || + (RetValue == 0 && Abc_LatchIsInit1(pLatch)) ) + { + // fanin constant differs from the latch init value + if ( pLatchPivot == NULL ) + { + pLatchPivot = pLatch; + continue; + } + if ( Abc_LatchInit(pLatch) != Abc_LatchInit(pLatchPivot) ) // add inverter + pFanin = Abc_NtkCreateNodeInv( pNtk, Abc_ObjFanout0(pLatchPivot) ); + else + pFanin = Abc_ObjFanout0(pLatchPivot); + } + else + pFanin = Abc_ObjFanin0(Abc_ObjFanin0(pLatch)); + // replace latch + Abc_ObjTransferFanout( Abc_ObjFanout0(pLatch), pFanin ); + // delete the extra nodes + Abc_NtkDeleteObj_rec( Abc_ObjFanout0(pLatch), 0 ); + Counter++; + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Replaces autonumnous logic by free inputs.] + + Description [Assumes that non-autonomous logic is marked with + the current ID.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkReplaceAutonomousLogic( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode, * pFanin; + Vec_Ptr_t * vNodes; + int i, k, Counter; + // collect the nodes that feed into the reachable logic + vNodes = Vec_PtrAlloc( 100 ); + Abc_NtkForEachObj( pNtk, pNode, i ) + { + // skip non-visited fanins + if ( !Abc_NodeIsTravIdCurrent(pNode) ) + continue; + // look for non-visited fanins + Abc_ObjForEachFanin( pNode, pFanin, k ) + { + // skip visited fanins + if ( Abc_NodeIsTravIdCurrent(pFanin) ) + continue; + // skip constants and latches fed by constants + if ( Abc_NtkCheckConstant_rec(pFanin) != -1 || + (Abc_ObjIsBo(pFanin) && Abc_NtkCheckConstant_rec(Abc_ObjFanin0(Abc_ObjFanin0(pFanin))) != -1) ) + { + Abc_NtkSetTravId_rec( pFanin ); + continue; + } + assert( !Abc_ObjIsLatch(pFanin) ); + Vec_PtrPush( vNodes, pFanin ); + } + } + Vec_PtrUniqify( vNodes, Abc_ObjPointerCompare ); + // replace these nodes by the PIs + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + pFanin = Abc_NtkCreatePi(pNtk); + Abc_ObjAssignName( pFanin, Abc_ObjName(pFanin), NULL ); + Abc_NodeSetTravIdCurrent( pFanin ); + Abc_ObjTransferFanout( pNode, pFanin ); + } + Counter = Vec_PtrSize(vNodes); + Vec_PtrFree( vNodes ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Sequential cleanup.] + + Description [Performs three tasks: + - Removes logic that does not feed into POs. + - Removes latches driven by constant values equal to the initial state. + - Replaces the autonomous components by additional PI variables.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkCleanupSeq( Abc_Ntk_t * pNtk, int fLatchSweep, int fAutoSweep, int fVerbose ) +{ + Vec_Ptr_t * vNodes; + int Counter; + assert( Abc_NtkIsLogic(pNtk) ); + // mark the nodes reachable from the POs + vNodes = Abc_NtkDfsSeq( pNtk ); + Vec_PtrFree( vNodes ); + // remove the non-marked nodes + Counter = Abc_NodeRemoveNonCurrentObjects( pNtk ); + if ( fVerbose ) + printf( "Cleanup removed %4d dangling objects.\n", Counter ); + // check if some of the latches can be removed + if ( fLatchSweep ) + { + Counter = Abc_NtkLatchSweep( pNtk ); + if ( fVerbose ) + printf( "Cleanup removed %4d redundant latches.\n", Counter ); + } + // detect the autonomous components + if ( fAutoSweep ) + { + vNodes = Abc_NtkDfsSeqReverse( pNtk ); + Vec_PtrFree( vNodes ); + // replace them by PIs + Counter = Abc_NtkReplaceAutonomousLogic( pNtk ); + if ( fVerbose ) + printf( "Cleanup added %4d additional PIs.\n", Counter ); + // remove the non-marked nodes + Counter = Abc_NodeRemoveNonCurrentObjects( pNtk ); + if ( fVerbose ) + printf( "Cleanup removed %4d autonomous objects.\n", Counter ); + } + // check + if ( !Abc_NtkCheck( pNtk ) ) + printf( "Abc_NtkCleanupSeq: The network check has failed.\n" ); + return 1; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcSymm.c b/abc_with_bb_support/src/base/abci/abcSymm.c new file mode 100644 index 000000000..d69574cde --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcSymm.c @@ -0,0 +1,229 @@ +/**CFile**************************************************************** + + FileName [abcSymm.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Computation of two-variable symmetries.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcSymm.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Abc_NtkSymmetriesUsingBdds( Abc_Ntk_t * pNtk, int fNaive, int fReorder, int fVerbose ); +static void Abc_NtkSymmetriesUsingSandS( Abc_Ntk_t * pNtk, int fVerbose ); +static void Ntk_NetworkSymmsBdd( DdManager * dd, Abc_Ntk_t * pNtk, int fNaive, int fVerbose ); +static void Ntk_NetworkSymmsPrint( Abc_Ntk_t * pNtk, Extra_SymmInfo_t * pSymms ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [The top level procedure to compute symmetries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkSymmetries( Abc_Ntk_t * pNtk, int fUseBdds, int fNaive, int fReorder, int fVerbose ) +{ + if ( fUseBdds || fNaive ) + Abc_NtkSymmetriesUsingBdds( pNtk, fNaive, fReorder, fVerbose ); + else + Abc_NtkSymmetriesUsingSandS( pNtk, fVerbose ); +} + +/**Function************************************************************* + + Synopsis [Symmetry computation using simulation and SAT.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkSymmetriesUsingSandS( Abc_Ntk_t * pNtk, int fVerbose ) +{ + extern int Sim_ComputeTwoVarSymms( Abc_Ntk_t * pNtk, int fVerbose ); + int nSymms = Sim_ComputeTwoVarSymms( pNtk, fVerbose ); + printf( "The total number of symmetries is %d.\n", nSymms ); +} + +/**Function************************************************************* + + Synopsis [Symmetry computation using BDDs (both naive and smart).] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkSymmetriesUsingBdds( Abc_Ntk_t * pNtk, int fNaive, int fReorder, int fVerbose ) +{ + DdManager * dd; + int clk, clkBdd, clkSym; + int fGarbCollect = 1; + + // compute the global functions +clk = clock(); + dd = Abc_NtkBuildGlobalBdds( pNtk, 10000000, 1, fReorder, fVerbose ); + printf( "Shared BDD size = %d nodes.\n", Abc_NtkSizeOfGlobalBdds(pNtk) ); + Cudd_AutodynDisable( dd ); + if ( !fGarbCollect ) + Cudd_DisableGarbageCollection( dd ); + Cudd_zddVarsFromBddVars( dd, 2 ); +clkBdd = clock() - clk; + // create the collapsed network +clk = clock(); + Ntk_NetworkSymmsBdd( dd, pNtk, fNaive, fVerbose ); +clkSym = clock() - clk; + // undo the global functions + Abc_NtkFreeGlobalBdds( pNtk, 1 ); +printf( "Statistics of BDD-based symmetry detection:\n" ); +printf( "Algorithm = %s. Reordering = %s. Garbage collection = %s.\n", + fNaive? "naive" : "fast", fReorder? "yes" : "no", fGarbCollect? "yes" : "no" ); +PRT( "Constructing BDDs", clkBdd ); +PRT( "Computing symms ", clkSym ); +PRT( "TOTAL ", clkBdd + clkSym ); +} + +/**Function************************************************************* + + Synopsis [Symmetry computation using BDDs (both naive and smart).] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ntk_NetworkSymmsBdd( DdManager * dd, Abc_Ntk_t * pNtk, int fNaive, int fVerbose ) +{ + Extra_SymmInfo_t * pSymms; + Abc_Obj_t * pNode; + DdNode * bFunc; + int nSymms = 0; + int nSupps = 0; + int i; + + // compute symmetry info for each PO + Abc_NtkForEachCo( pNtk, pNode, i ) + { +// bFunc = pNtk->vFuncsGlob->pArray[i]; + bFunc = Abc_ObjGlobalBdd( pNode ); + nSupps += Cudd_SupportSize( dd, bFunc ); + if ( Cudd_IsConstant(bFunc) ) + continue; + if ( fNaive ) + pSymms = Extra_SymmPairsComputeNaive( dd, bFunc ); + else + pSymms = Extra_SymmPairsCompute( dd, bFunc ); + nSymms += pSymms->nSymms; + if ( fVerbose ) + { + printf( "Output %6s (%d): ", Abc_ObjName(pNode), pSymms->nSymms ); + Ntk_NetworkSymmsPrint( pNtk, pSymms ); + } +//Extra_SymmPairsPrint( pSymms ); + Extra_SymmPairsDissolve( pSymms ); + } + printf( "Total number of vars in functional supports = %8d.\n", nSupps ); + printf( "Total number of two-variable symmetries = %8d.\n", nSymms ); +} + +/**Function************************************************************* + + Synopsis [Printing symmetry groups from the symmetry data structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ntk_NetworkSymmsPrint( Abc_Ntk_t * pNtk, Extra_SymmInfo_t * pSymms ) +{ + char ** pInputNames; + int * pVarTaken; + int i, k, nVars, nSize, fStart; + + // get variable names + nVars = Abc_NtkCiNum(pNtk); + pInputNames = Abc_NtkCollectCioNames( pNtk, 0 ); + + // alloc the array of marks + pVarTaken = ALLOC( int, nVars ); + memset( pVarTaken, 0, sizeof(int) * nVars ); + + // print the groups + fStart = 1; + nSize = pSymms->nVars; + for ( i = 0; i < nSize; i++ ) + { + // skip the variable already considered + if ( pVarTaken[i] ) + continue; + // find all the vars symmetric with this one + for ( k = 0; k < nSize; k++ ) + { + if ( k == i ) + continue; + if ( pSymms->pSymms[i][k] == 0 ) + continue; + // vars i and k are symmetric + assert( pVarTaken[k] == 0 ); + // there is a new symmetry pair + if ( fStart == 1 ) + { // start a new symmetry class + fStart = 0; + printf( " { %s", pInputNames[ pSymms->pVars[i] ] ); + // mark the var as taken + pVarTaken[i] = 1; + } + printf( " %s", pInputNames[ pSymms->pVars[k] ] ); + // mark the var as taken + pVarTaken[k] = 1; + } + if ( fStart == 0 ) + { + printf( " }" ); + fStart = 1; + } + } + printf( "\n" ); + + free( pInputNames ); + free( pVarTaken ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcTiming.c b/abc_with_bb_support/src/base/abci/abcTiming.c new file mode 100644 index 000000000..754c803c9 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcTiming.c @@ -0,0 +1,905 @@ +/**CFile**************************************************************** + + FileName [abcTiming.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Computation of timing info for mapped circuits.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcTiming.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "main.h" +#include "mio.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +struct Abc_ManTime_t_ +{ + Abc_Time_t tArrDef; + Abc_Time_t tReqDef; + Vec_Ptr_t * vArrs; + Vec_Ptr_t * vReqs; +}; + +// static functions +static Abc_ManTime_t * Abc_ManTimeStart(); +static void Abc_ManTimeExpand( Abc_ManTime_t * p, int nSize, int fProgressive ); +void Abc_NtkTimePrepare( Abc_Ntk_t * pNtk ); + +void Abc_NodeDelayTraceArrival( Abc_Obj_t * pNode ); + +// accessing the arrival and required times of a node +static inline Abc_Time_t * Abc_NodeArrival( Abc_Obj_t * pNode ) { return pNode->pNtk->pManTime->vArrs->pArray[pNode->Id]; } +static inline Abc_Time_t * Abc_NodeRequired( Abc_Obj_t * pNode ) { return pNode->pNtk->pManTime->vReqs->pArray[pNode->Id]; } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Reads the arrival time of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Time_t * Abc_NodeReadArrival( Abc_Obj_t * pNode ) +{ + assert( pNode->pNtk->pManTime ); + return Abc_NodeArrival(pNode); +} + +/**Function************************************************************* + + Synopsis [Reads the arrival time of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Time_t * Abc_NodeReadRequired( Abc_Obj_t * pNode ) +{ + assert( pNode->pNtk->pManTime ); + return Abc_NodeRequired(pNode); +} + +/**Function************************************************************* + + Synopsis [Reads the arrival time of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Time_t * Abc_NtkReadDefaultArrival( Abc_Ntk_t * pNtk ) +{ + assert( pNtk->pManTime ); + return &pNtk->pManTime->tArrDef; +} + +/**Function************************************************************* + + Synopsis [Reads the arrival time of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Time_t * Abc_NtkReadDefaultRequired( Abc_Ntk_t * pNtk ) +{ + assert( pNtk->pManTime ); + return &pNtk->pManTime->tReqDef; +} + +/**Function************************************************************* + + Synopsis [Sets the default arrival time for the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkTimeSetDefaultArrival( Abc_Ntk_t * pNtk, float Rise, float Fall ) +{ + if ( Rise == 0.0 && Fall == 0.0 ) + return; + if ( pNtk->pManTime == NULL ) + pNtk->pManTime = Abc_ManTimeStart(); + pNtk->pManTime->tArrDef.Rise = Rise; + pNtk->pManTime->tArrDef.Fall = Fall; + pNtk->pManTime->tArrDef.Worst = ABC_MAX( Rise, Fall ); +} + +/**Function************************************************************* + + Synopsis [Sets the default arrival time for the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkTimeSetDefaultRequired( Abc_Ntk_t * pNtk, float Rise, float Fall ) +{ + if ( Rise == 0.0 && Fall == 0.0 ) + return; + if ( pNtk->pManTime == NULL ) + pNtk->pManTime = Abc_ManTimeStart(); + pNtk->pManTime->tReqDef.Rise = Rise; + pNtk->pManTime->tReqDef.Rise = Fall; + pNtk->pManTime->tReqDef.Worst = ABC_MAX( Rise, Fall ); +} + +/**Function************************************************************* + + Synopsis [Sets the arrival time for an object.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkTimeSetArrival( Abc_Ntk_t * pNtk, int ObjId, float Rise, float Fall ) +{ + Vec_Ptr_t * vTimes; + Abc_Time_t * pTime; + if ( pNtk->pManTime == NULL ) + pNtk->pManTime = Abc_ManTimeStart(); + if ( pNtk->pManTime->tArrDef.Rise == Rise && pNtk->pManTime->tArrDef.Fall == Fall ) + return; + Abc_ManTimeExpand( pNtk->pManTime, ObjId + 1, 1 ); + // set the arrival time + vTimes = pNtk->pManTime->vArrs; + pTime = vTimes->pArray[ObjId]; + pTime->Rise = Rise; + pTime->Fall = Rise; + pTime->Worst = ABC_MAX( Rise, Fall ); +} + +/**Function************************************************************* + + Synopsis [Sets the arrival time for an object.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkTimeSetRequired( Abc_Ntk_t * pNtk, int ObjId, float Rise, float Fall ) +{ + Vec_Ptr_t * vTimes; + Abc_Time_t * pTime; + if ( pNtk->pManTime == NULL ) + pNtk->pManTime = Abc_ManTimeStart(); + if ( pNtk->pManTime->tReqDef.Rise == Rise && pNtk->pManTime->tReqDef.Fall == Fall ) + return; + Abc_ManTimeExpand( pNtk->pManTime, ObjId + 1, 1 ); + // set the required time + vTimes = pNtk->pManTime->vReqs; + pTime = vTimes->pArray[ObjId]; + pTime->Rise = Rise; + pTime->Fall = Rise; + pTime->Worst = ABC_MAX( Rise, Fall ); +} + +/**Function************************************************************* + + Synopsis [Finalizes the timing manager after setting arr/req times.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkTimeInitialize( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + Abc_Time_t ** ppTimes, * pTime; + int i; + if ( pNtk->pManTime == NULL ) + return; + Abc_ManTimeExpand( pNtk->pManTime, Abc_NtkObjNumMax(pNtk), 0 ); + // set the default timing + ppTimes = (Abc_Time_t **)pNtk->pManTime->vArrs->pArray; + Abc_NtkForEachPi( pNtk, pObj, i ) + { + pTime = ppTimes[pObj->Id]; + if ( pTime->Worst != -ABC_INFINITY ) + continue; + *pTime = pNtk->pManTime->tArrDef; + } + // set the default timing + ppTimes = (Abc_Time_t **)pNtk->pManTime->vReqs->pArray; + Abc_NtkForEachPo( pNtk, pObj, i ) + { + pTime = ppTimes[pObj->Id]; + if ( pTime->Worst != -ABC_INFINITY ) + continue; + *pTime = pNtk->pManTime->tReqDef; + } + // set the 0 arrival times for latch outputs and constant nodes + ppTimes = (Abc_Time_t **)pNtk->pManTime->vArrs->pArray; + Abc_NtkForEachLatchOutput( pNtk, pObj, i ) + { + pTime = ppTimes[pObj->Id]; + pTime->Fall = pTime->Rise = pTime->Worst = 0.0; + } +} + +/**Function************************************************************* + + Synopsis [Prepares the timing manager for delay trace.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkTimePrepare( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + Abc_Time_t ** ppTimes, * pTime; + int i; + // if there is no timing manager, allocate and initialize + if ( pNtk->pManTime == NULL ) + { + pNtk->pManTime = Abc_ManTimeStart(); + Abc_NtkTimeInitialize( pNtk ); + return; + } + // if timing manager is given, expand it if necessary + Abc_ManTimeExpand( pNtk->pManTime, Abc_NtkObjNumMax(pNtk), 0 ); + // clean arrivals except for PIs + ppTimes = (Abc_Time_t **)pNtk->pManTime->vArrs->pArray; + Abc_NtkForEachNode( pNtk, pObj, i ) + { + pTime = ppTimes[pObj->Id]; + pTime->Fall = pTime->Rise = pTime->Worst = -ABC_INFINITY; + } + Abc_NtkForEachPo( pNtk, pObj, i ) + { + pTime = ppTimes[pObj->Id]; + pTime->Fall = pTime->Rise = pTime->Worst = -ABC_INFINITY; + } + // clean required except for POs +} + + + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_ManTime_t * Abc_ManTimeStart() +{ + Abc_ManTime_t * p; + p = ALLOC( Abc_ManTime_t, 1 ); + memset( p, 0, sizeof(Abc_ManTime_t) ); + p->vArrs = Vec_PtrAlloc( 0 ); + p->vReqs = Vec_PtrAlloc( 0 ); + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ManTimeStop( Abc_ManTime_t * p ) +{ + if ( p->vArrs->nSize > 0 ) + { + free( p->vArrs->pArray[0] ); + Vec_PtrFree( p->vArrs ); + } + if ( p->vReqs->nSize > 0 ) + { + free( p->vReqs->pArray[0] ); + Vec_PtrFree( p->vReqs ); + } + free( p ); +} + +/**Function************************************************************* + + Synopsis [Duplicates the timing manager with the PI/PO timing info.] + + Description [The PIs/POs of the new network should be allocated.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ManTimeDup( Abc_Ntk_t * pNtkOld, Abc_Ntk_t * pNtkNew ) +{ + Abc_Obj_t * pObj; + Abc_Time_t ** ppTimesOld, ** ppTimesNew; + int i; + if ( pNtkOld->pManTime == NULL ) + return; + assert( Abc_NtkPiNum(pNtkOld) == Abc_NtkPiNum(pNtkNew) ); + assert( Abc_NtkPoNum(pNtkOld) == Abc_NtkPoNum(pNtkNew) ); + assert( Abc_NtkLatchNum(pNtkOld) == Abc_NtkLatchNum(pNtkNew) ); + // create the new timing manager + pNtkNew->pManTime = Abc_ManTimeStart(); + Abc_ManTimeExpand( pNtkNew->pManTime, Abc_NtkObjNumMax(pNtkNew), 0 ); + // set the default timing + pNtkNew->pManTime->tArrDef = pNtkOld->pManTime->tArrDef; + pNtkNew->pManTime->tReqDef = pNtkOld->pManTime->tReqDef; + // set the CI timing + ppTimesOld = (Abc_Time_t **)pNtkOld->pManTime->vArrs->pArray; + ppTimesNew = (Abc_Time_t **)pNtkNew->pManTime->vArrs->pArray; + Abc_NtkForEachCi( pNtkOld, pObj, i ) + *ppTimesNew[ Abc_NtkCi(pNtkNew,i)->Id ] = *ppTimesOld[ pObj->Id ]; + // set the CO timing + ppTimesOld = (Abc_Time_t **)pNtkOld->pManTime->vReqs->pArray; + ppTimesNew = (Abc_Time_t **)pNtkNew->pManTime->vReqs->pArray; + Abc_NtkForEachCo( pNtkOld, pObj, i ) + *ppTimesNew[ Abc_NtkCo(pNtkNew,i)->Id ] = *ppTimesOld[ pObj->Id ]; +} + +/**Function************************************************************* + + Synopsis [Expends the storage for timing information.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ManTimeExpand( Abc_ManTime_t * p, int nSize, int fProgressive ) +{ + Vec_Ptr_t * vTimes; + Abc_Time_t * ppTimes, * ppTimesOld, * pTime; + int nSizeOld, nSizeNew, i; + + nSizeOld = p->vArrs->nSize; + if ( nSizeOld >= nSize ) + return; + nSizeNew = fProgressive? 2 * nSize : nSize; + if ( nSizeNew < 100 ) + nSizeNew = 100; + + vTimes = p->vArrs; + Vec_PtrGrow( vTimes, nSizeNew ); + vTimes->nSize = nSizeNew; + ppTimesOld = ( nSizeOld == 0 )? NULL : vTimes->pArray[0]; + ppTimes = REALLOC( Abc_Time_t, ppTimesOld, nSizeNew ); + for ( i = 0; i < nSizeNew; i++ ) + vTimes->pArray[i] = ppTimes + i; + for ( i = nSizeOld; i < nSizeNew; i++ ) + { + pTime = vTimes->pArray[i]; + pTime->Rise = -ABC_INFINITY; + pTime->Fall = -ABC_INFINITY; + pTime->Worst = -ABC_INFINITY; + } + + vTimes = p->vReqs; + Vec_PtrGrow( vTimes, nSizeNew ); + vTimes->nSize = nSizeNew; + ppTimesOld = ( nSizeOld == 0 )? NULL : vTimes->pArray[0]; + ppTimes = REALLOC( Abc_Time_t, ppTimesOld, nSizeNew ); + for ( i = 0; i < nSizeNew; i++ ) + vTimes->pArray[i] = ppTimes + i; + for ( i = nSizeOld; i < nSizeNew; i++ ) + { + pTime = vTimes->pArray[i]; + pTime->Rise = -ABC_INFINITY; + pTime->Fall = -ABC_INFINITY; + pTime->Worst = -ABC_INFINITY; + } +} + + + + + + +/**Function************************************************************* + + Synopsis [Sets the CI node levels according to the arrival info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkSetNodeLevelsArrival( Abc_Ntk_t * pNtkOld ) +{ + Abc_Obj_t * pNodeOld, * pNodeNew; + float tAndDelay; + int i; + if ( pNtkOld->pManTime == NULL ) + return; + if ( Mio_LibraryReadNand2(Abc_FrameReadLibGen()) == NULL ) + return; + tAndDelay = Mio_LibraryReadDelayNand2Max(Abc_FrameReadLibGen()); + Abc_NtkForEachPi( pNtkOld, pNodeOld, i ) + { + pNodeNew = pNodeOld->pCopy; + pNodeNew->Level = (int)(Abc_NodeArrival(pNodeOld)->Worst / tAndDelay); + } +} + +/**Function************************************************************* + + Synopsis [Sets the CI node levels according to the arrival info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Time_t * Abc_NtkGetCiArrivalTimes( Abc_Ntk_t * pNtk ) +{ + Abc_Time_t * p; + Abc_Obj_t * pNode; + int i; + p = ALLOC( Abc_Time_t, Abc_NtkCiNum(pNtk) ); + memset( p, 0, sizeof(Abc_Time_t) * Abc_NtkCiNum(pNtk) ); + if ( pNtk->pManTime == NULL ) + return p; + // set the PI arrival times + Abc_NtkForEachPi( pNtk, pNode, i ) + p[i] = *Abc_NodeArrival(pNode); + return p; +} + + +/**Function************************************************************* + + Synopsis [Sets the CI node levels according to the arrival info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float * Abc_NtkGetCiArrivalFloats( Abc_Ntk_t * pNtk ) +{ + float * p; + Abc_Obj_t * pNode; + int i; + p = ALLOC( float, Abc_NtkCiNum(pNtk) ); + memset( p, 0, sizeof(float) * Abc_NtkCiNum(pNtk) ); + if ( pNtk->pManTime == NULL ) + return p; + // set the PI arrival times + Abc_NtkForEachPi( pNtk, pNode, i ) + p[i] = Abc_NodeArrival(pNode)->Worst; + return p; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Abc_NtkDelayTrace( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode, * pDriver; + Vec_Ptr_t * vNodes; + Abc_Time_t * pTime; + float tArrivalMax; + int i; + + assert( Abc_NtkIsMappedLogic(pNtk) ); + + Abc_NtkTimePrepare( pNtk ); + vNodes = Abc_NtkDfs( pNtk, 1 ); + for ( i = 0; i < vNodes->nSize; i++ ) + Abc_NodeDelayTraceArrival( vNodes->pArray[i] ); + Vec_PtrFree( vNodes ); + + // get the latest arrival times + tArrivalMax = -ABC_INFINITY; + Abc_NtkForEachCo( pNtk, pNode, i ) + { + pDriver = Abc_ObjFanin0(pNode); + pTime = Abc_NodeArrival(pDriver); + if ( tArrivalMax < pTime->Worst ) + tArrivalMax = pTime->Worst; + } + return tArrivalMax; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeDelayTraceArrival( Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pFanin; + Abc_Time_t * pTimeIn, * pTimeOut; + float tDelayBlockRise, tDelayBlockFall; + Mio_PinPhase_t PinPhase; + Mio_Pin_t * pPin; + int i; + + // start the arrival time of the node + pTimeOut = Abc_NodeArrival(pNode); + pTimeOut->Rise = pTimeOut->Fall = -ABC_INFINITY; + // go through the pins of the gate + pPin = Mio_GateReadPins(pNode->pData); + Abc_ObjForEachFanin( pNode, pFanin, i ) + { + pTimeIn = Abc_NodeArrival(pFanin); + // get the interesting parameters of this pin + PinPhase = Mio_PinReadPhase(pPin); + tDelayBlockRise = (float)Mio_PinReadDelayBlockRise( pPin ); + tDelayBlockFall = (float)Mio_PinReadDelayBlockFall( pPin ); + // compute the arrival times of the positive phase + if ( PinPhase != MIO_PHASE_INV ) // NONINV phase is present + { + if ( pTimeOut->Rise < pTimeIn->Rise + tDelayBlockRise ) + pTimeOut->Rise = pTimeIn->Rise + tDelayBlockRise; + if ( pTimeOut->Fall < pTimeIn->Fall + tDelayBlockFall ) + pTimeOut->Fall = pTimeIn->Fall + tDelayBlockFall; + } + if ( PinPhase != MIO_PHASE_NONINV ) // INV phase is present + { + if ( pTimeOut->Rise < pTimeIn->Fall + tDelayBlockRise ) + pTimeOut->Rise = pTimeIn->Fall + tDelayBlockRise; + if ( pTimeOut->Fall < pTimeIn->Rise + tDelayBlockFall ) + pTimeOut->Fall = pTimeIn->Rise + tDelayBlockFall; + } + pPin = Mio_PinReadNext(pPin); + } + pTimeOut->Worst = ABC_MAX( pTimeOut->Rise, pTimeOut->Fall ); +} + + + + +/**Function************************************************************* + + Synopsis [Computes the level of the node using its fanin levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_ObjLevelNew( Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanin; + int i, Level = 0; + Abc_ObjForEachFanin( pObj, pFanin, i ) + Level = ABC_MAX( Level, Abc_ObjLevel(pFanin) ); + return Level + 1; +} + +/**Function************************************************************* + + Synopsis [Computes the reverse level of the node using its fanout levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_ObjReverseLevelNew( Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanout; + int i, LevelCur, Level = 0; + Abc_ObjForEachFanout( pObj, pFanout, i ) + { + LevelCur = Abc_ObjReverseLevel( pFanout ); + Level = ABC_MAX( Level, LevelCur ); + } + return Level + 1; +} + +/**Function************************************************************* + + Synopsis [Returns required level of the node.] + + Description [Converts the reverse levels of the node into its required + level as follows: ReqLevel(Node) = MaxLevels(Ntk) + 1 - LevelR(Node).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_ObjRequiredLevel( Abc_Obj_t * pObj ) +{ + Abc_Ntk_t * pNtk = pObj->pNtk; + assert( pNtk->vLevelsR ); + return pNtk->LevelMax + 1 - Abc_ObjReverseLevel(pObj); +} + +/**Function************************************************************* + + Synopsis [Returns the reverse level of the node.] + + Description [The reverse level is the level of the node in reverse + topological order, starting from the COs.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_ObjReverseLevel( Abc_Obj_t * pObj ) +{ + Abc_Ntk_t * pNtk = pObj->pNtk; + assert( pNtk->vLevelsR ); + Vec_IntFillExtra( pNtk->vLevelsR, pObj->Id + 1, 0 ); + return Vec_IntEntry(pNtk->vLevelsR, pObj->Id); +} + +/**Function************************************************************* + + Synopsis [Sets the reverse level of the node.] + + Description [The reverse level is the level of the node in reverse + topological order, starting from the COs.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ObjSetReverseLevel( Abc_Obj_t * pObj, int LevelR ) +{ + Abc_Ntk_t * pNtk = pObj->pNtk; + assert( pNtk->vLevelsR ); + Vec_IntFillExtra( pNtk->vLevelsR, pObj->Id + 1, 0 ); + Vec_IntWriteEntry( pNtk->vLevelsR, pObj->Id, LevelR ); +} + +/**Function************************************************************* + + Synopsis [Prepares for the computation of required levels.] + + Description [This procedure should be called before the required times + are used. It starts internal data structures, which records the level + from the COs of the network nodes in reverse topologogical order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkStartReverseLevels( Abc_Ntk_t * pNtk, int nMaxLevelIncrease ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj; + int i; + // remember the maximum number of direct levels + pNtk->LevelMax = Abc_NtkLevel(pNtk) + nMaxLevelIncrease; + // start the reverse levels + pNtk->vLevelsR = Vec_IntAlloc( 0 ); + Vec_IntFill( pNtk->vLevelsR, 1 + Abc_NtkObjNumMax(pNtk), 0 ); + // compute levels in reverse topological order + vNodes = Abc_NtkDfsReverse( pNtk ); + Vec_PtrForEachEntry( vNodes, pObj, i ) + Abc_ObjSetReverseLevel( pObj, Abc_ObjReverseLevelNew(pObj) ); + Vec_PtrFree( vNodes ); +} + +/**Function************************************************************* + + Synopsis [Cleans the data structures used to compute required levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkStopReverseLevels( Abc_Ntk_t * pNtk ) +{ + assert( pNtk->vLevelsR ); + Vec_IntFree( pNtk->vLevelsR ); + pNtk->vLevelsR = NULL; + pNtk->LevelMax = 0; + +} + +/**Function************************************************************* + + Synopsis [Incrementally updates level of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkUpdateLevel( Abc_Obj_t * pObjNew, Vec_Vec_t * vLevels ) +{ + Abc_Obj_t * pFanout, * pTemp; + int LevelOld, Lev, k, m; + // check if level has changed + LevelOld = Abc_ObjLevel(pObjNew); + if ( LevelOld == Abc_ObjLevelNew(pObjNew) ) + return; + // start the data structure for level update + // we cannot fail to visit a node when using this structure because the + // nodes are stored by their _old_ levels, which are assumed to be correct + Vec_VecClear( vLevels ); + Vec_VecPush( vLevels, LevelOld, pObjNew ); + pObjNew->fMarkA = 1; + // recursively update level + Vec_VecForEachEntryStart( vLevels, pTemp, Lev, k, LevelOld ) + { + pTemp->fMarkA = 0; + assert( Abc_ObjLevel(pTemp) == Lev ); + Abc_ObjSetLevel( pTemp, Abc_ObjLevelNew(pTemp) ); + // if the level did not change, no need to check the fanout levels + if ( Abc_ObjLevel(pTemp) == Lev ) + continue; + // schedule fanout for level update + Abc_ObjForEachFanout( pTemp, pFanout, m ) + { + if ( !Abc_ObjIsCo(pFanout) && !pFanout->fMarkA ) + { + assert( Abc_ObjLevel(pFanout) >= Lev ); + Vec_VecPush( vLevels, Abc_ObjLevel(pFanout), pFanout ); + pFanout->fMarkA = 1; + } + } + } +} + +/**Function************************************************************* + + Synopsis [Incrementally updates level of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkUpdateReverseLevel( Abc_Obj_t * pObjNew, Vec_Vec_t * vLevels ) +{ + Abc_Obj_t * pFanin, * pTemp; + int LevelOld, LevFanin, Lev, k, m; + // check if level has changed + LevelOld = Abc_ObjReverseLevel(pObjNew); + if ( LevelOld == Abc_ObjReverseLevelNew(pObjNew) ) + return; + // start the data structure for level update + // we cannot fail to visit a node when using this structure because the + // nodes are stored by their _old_ levels, which are assumed to be correct + Vec_VecClear( vLevels ); + Vec_VecPush( vLevels, LevelOld, pObjNew ); + pObjNew->fMarkA = 1; + // recursively update level + Vec_VecForEachEntryStart( vLevels, pTemp, Lev, k, LevelOld ) + { + pTemp->fMarkA = 0; + LevelOld = Abc_ObjReverseLevel(pTemp); + assert( LevelOld == Lev ); + Abc_ObjSetReverseLevel( pTemp, Abc_ObjReverseLevelNew(pTemp) ); + // if the level did not change, no need to check the fanout levels + if ( Abc_ObjReverseLevel(pTemp) == Lev ) + continue; + // schedule fanins for level update + Abc_ObjForEachFanin( pTemp, pFanin, m ) + { + if ( !Abc_ObjIsCi(pFanin) && !pFanin->fMarkA ) + { + LevFanin = Abc_ObjReverseLevel( pFanin ); + assert( LevFanin >= Lev ); + Vec_VecPush( vLevels, LevFanin, pFanin ); + pFanin->fMarkA = 1; + } + } + } +} + +/**Function************************************************************* + + Synopsis [Replaces the node and incrementally updates levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkUpdate( Abc_Obj_t * pObj, Abc_Obj_t * pObjNew, Vec_Vec_t * vLevels ) +{ + // replace the old node by the new node + pObjNew->Level = pObj->Level; + Abc_ObjReplace( pObj, pObjNew ); + // update the level of the node + Abc_NtkUpdateLevel( pObjNew, vLevels ); + Abc_ObjSetReverseLevel( pObjNew, 0 ); + Abc_NtkUpdateReverseLevel( pObjNew, vLevels ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcUnate.c b/abc_with_bb_support/src/base/abci/abcUnate.c new file mode 100644 index 000000000..8832f669e --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcUnate.c @@ -0,0 +1,155 @@ +/**CFile**************************************************************** + + FileName [abcUnate.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcUnate.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Abc_NtkPrintUnateBdd( Abc_Ntk_t * pNtk, int fUseNaive, int fVerbose ); +static void Abc_NtkPrintUnateSat( Abc_Ntk_t * pNtk, int fVerbose ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Detects unate variables of the multi-output function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintUnate( Abc_Ntk_t * pNtk, int fUseBdds, int fUseNaive, int fVerbose ) +{ + if ( fUseBdds || fUseNaive ) + Abc_NtkPrintUnateBdd( pNtk, fUseNaive, fVerbose ); + else + Abc_NtkPrintUnateSat( pNtk, fVerbose ); +} + +/**Function************************************************************* + + Synopsis [Detects unate variables using BDDs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintUnateBdd( Abc_Ntk_t * pNtk, int fUseNaive, int fVerbose ) +{ + Abc_Obj_t * pNode; + Extra_UnateInfo_t * p; + DdManager * dd; // the BDD manager used to hold shared BDDs +// DdNode ** pbGlobal; // temporary storage for global BDDs + int TotalSupps = 0; + int TotalUnate = 0; + int i, clk = clock(); + int clkBdd, clkUnate; + + // compute the global BDDs + dd = Abc_NtkBuildGlobalBdds(pNtk, 10000000, 1, 1, fVerbose); + if ( dd == NULL ) + return; +clkBdd = clock() - clk; + + // get information about the network +// dd = pNtk->pManGlob; +// dd = Abc_NtkGlobalBddMan( pNtk ); +// pbGlobal = (DdNode **)Vec_PtrArray( pNtk->vFuncsGlob ); + + // print the size of the BDDs + printf( "Shared BDD size = %6d nodes.\n", Cudd_ReadKeys(dd) - Cudd_ReadDead(dd) ); + + // perform naive BDD-based computation + if ( fUseNaive ) + { + Abc_NtkForEachCo( pNtk, pNode, i ) + { +// p = Extra_UnateComputeSlow( dd, pbGlobal[i] ); + p = Extra_UnateComputeSlow( dd, Abc_ObjGlobalBdd(pNode) ); + if ( fVerbose ) + Extra_UnateInfoPrint( p ); + TotalSupps += p->nVars; + TotalUnate += p->nUnate; + Extra_UnateInfoDissolve( p ); + } + } + // perform smart BDD-based computation + else + { + // create ZDD variables in the manager + Cudd_zddVarsFromBddVars( dd, 2 ); + Abc_NtkForEachCo( pNtk, pNode, i ) + { +// p = Extra_UnateComputeFast( dd, pbGlobal[i] ); + p = Extra_UnateComputeFast( dd, Abc_ObjGlobalBdd(pNode) ); + if ( fVerbose ) + Extra_UnateInfoPrint( p ); + TotalSupps += p->nVars; + TotalUnate += p->nUnate; + Extra_UnateInfoDissolve( p ); + } + } +clkUnate = clock() - clk - clkBdd; + + // print stats + printf( "Ins/Outs = %4d/%4d. Total supp = %5d. Total unate = %5d.\n", + Abc_NtkCiNum(pNtk), Abc_NtkCoNum(pNtk), TotalSupps, TotalUnate ); + PRT( "Glob BDDs", clkBdd ); + PRT( "Unateness", clkUnate ); + PRT( "Total ", clock() - clk ); + + // deref the PO functions +// Abc_NtkFreeGlobalBdds( pNtk ); + // stop the global BDD manager +// Extra_StopManager( pNtk->pManGlob ); +// pNtk->pManGlob = NULL; + Abc_NtkFreeGlobalBdds( pNtk, 1 ); +} + +/**Function************************************************************* + + Synopsis [Detects unate variables using SAT.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintUnateSat( Abc_Ntk_t * pNtk, int fVerbose ) +{ +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcUnreach.c b/abc_with_bb_support/src/base/abci/abcUnreach.c new file mode 100644 index 000000000..b43a0bba1 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcUnreach.c @@ -0,0 +1,349 @@ +/**CFile**************************************************************** + + FileName [abcUnreach.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Computes unreachable states for small benchmarks.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcUnreach.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static DdNode * Abc_NtkTransitionRelation( DdManager * dd, Abc_Ntk_t * pNtk, int fVerbose ); +static DdNode * Abc_NtkInitStateAndVarMap( DdManager * dd, Abc_Ntk_t * pNtk, int fVerbose ); +static DdNode * Abc_NtkComputeUnreachable( DdManager * dd, Abc_Ntk_t * pNtk, DdNode * bRelation, DdNode * bInitial, bool fVerbose ); +static Abc_Ntk_t * Abc_NtkConstructExdc ( DdManager * dd, Abc_Ntk_t * pNtk, DdNode * bUnreach ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Extracts sequential DCs of the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkExtractSequentialDcs( Abc_Ntk_t * pNtk, bool fVerbose ) +{ + int fReorder = 1; + DdManager * dd; + DdNode * bRelation, * bInitial, * bUnreach; + + // remove EXDC network if present + if ( pNtk->pExdc ) + { + Abc_NtkDelete( pNtk->pExdc ); + pNtk->pExdc = NULL; + } + + // compute the global BDDs of the latches + dd = Abc_NtkBuildGlobalBdds( pNtk, 10000000, 1, 1, fVerbose ); + if ( dd == NULL ) + return 0; + if ( fVerbose ) + printf( "Shared BDD size = %6d nodes.\n", Cudd_ReadKeys(dd) - Cudd_ReadDead(dd) ); + + // create the transition relation (dereferenced global BDDs) + bRelation = Abc_NtkTransitionRelation( dd, pNtk, fVerbose ); Cudd_Ref( bRelation ); + // create the initial state and the variable map + bInitial = Abc_NtkInitStateAndVarMap( dd, pNtk, fVerbose ); Cudd_Ref( bInitial ); + // compute the unreachable states + bUnreach = Abc_NtkComputeUnreachable( dd, pNtk, bRelation, bInitial, fVerbose ); Cudd_Ref( bUnreach ); + Cudd_RecursiveDeref( dd, bRelation ); + Cudd_RecursiveDeref( dd, bInitial ); + + // reorder and disable reordering + if ( fReorder ) + { + if ( fVerbose ) + fprintf( stdout, "BDD nodes in the unreachable states before reordering %d.\n", Cudd_DagSize(bUnreach) ); + Cudd_ReduceHeap( dd, CUDD_REORDER_SYMM_SIFT, 1 ); + Cudd_AutodynDisable( dd ); + if ( fVerbose ) + fprintf( stdout, "BDD nodes in the unreachable states after reordering %d.\n", Cudd_DagSize(bUnreach) ); + } + + // allocate ZDD variables + Cudd_zddVarsFromBddVars( dd, 2 ); + // create the EXDC network representing the unreachable states + if ( pNtk->pExdc ) + Abc_NtkDelete( pNtk->pExdc ); + pNtk->pExdc = Abc_NtkConstructExdc( dd, pNtk, bUnreach ); + Cudd_RecursiveDeref( dd, bUnreach ); + Extra_StopManager( dd ); +// pNtk->pManGlob = NULL; + + // make sure that everything is okay + if ( pNtk->pExdc && !Abc_NtkCheck( pNtk->pExdc ) ) + { + printf( "Abc_NtkExtractSequentialDcs: The network check has failed.\n" ); + Abc_NtkDelete( pNtk->pExdc ); + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Computes the transition relation of the network.] + + Description [Assumes that the global BDDs are computed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Abc_NtkTransitionRelation( DdManager * dd, Abc_Ntk_t * pNtk, int fVerbose ) +{ + DdNode * bRel, * bTemp, * bProd, * bVar, * bInputs; + Abc_Obj_t * pNode; + int fReorder = 1; + int i; + + // extand the BDD manager to represent NS variables + assert( dd->size == Abc_NtkCiNum(pNtk) ); + Cudd_bddIthVar( dd, Abc_NtkCiNum(pNtk) + Abc_NtkLatchNum(pNtk) - 1 ); + + // enable reordering + if ( fReorder ) + Cudd_AutodynEnable( dd, CUDD_REORDER_SYMM_SIFT ); + else + Cudd_AutodynDisable( dd ); + + // compute the transition relation + bRel = b1; Cudd_Ref( bRel ); + Abc_NtkForEachLatch( pNtk, pNode, i ) + { + bVar = Cudd_bddIthVar( dd, Abc_NtkCiNum(pNtk) + i ); +// bProd = Cudd_bddXnor( dd, bVar, pNtk->vFuncsGlob->pArray[i] ); Cudd_Ref( bProd ); + bProd = Cudd_bddXnor( dd, bVar, Abc_ObjGlobalBdd(Abc_ObjFanin0(pNode)) ); Cudd_Ref( bProd ); + bRel = Cudd_bddAnd( dd, bTemp = bRel, bProd ); Cudd_Ref( bRel ); + Cudd_RecursiveDeref( dd, bTemp ); + Cudd_RecursiveDeref( dd, bProd ); + } + // free the global BDDs +// Abc_NtkFreeGlobalBdds( pNtk ); + Abc_NtkFreeGlobalBdds( pNtk, 0 ); + + // quantify the PI variables + bInputs = Extra_bddComputeRangeCube( dd, 0, Abc_NtkPiNum(pNtk) ); Cudd_Ref( bInputs ); + bRel = Cudd_bddExistAbstract( dd, bTemp = bRel, bInputs ); Cudd_Ref( bRel ); + Cudd_RecursiveDeref( dd, bTemp ); + Cudd_RecursiveDeref( dd, bInputs ); + + // reorder and disable reordering + if ( fReorder ) + { + if ( fVerbose ) + fprintf( stdout, "BDD nodes in the transition relation before reordering %d.\n", Cudd_DagSize(bRel) ); + Cudd_ReduceHeap( dd, CUDD_REORDER_SYMM_SIFT, 100 ); + Cudd_AutodynDisable( dd ); + if ( fVerbose ) + fprintf( stdout, "BDD nodes in the transition relation after reordering %d.\n", Cudd_DagSize(bRel) ); + } + Cudd_Deref( bRel ); + return bRel; +} + +/**Function************************************************************* + + Synopsis [Computes the initial state and sets up the variable map.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Abc_NtkInitStateAndVarMap( DdManager * dd, Abc_Ntk_t * pNtk, int fVerbose ) +{ + DdNode ** pbVarsX, ** pbVarsY; + DdNode * bTemp, * bProd, * bVar; + Abc_Obj_t * pLatch; + int i; + + // set the variable mapping for Cudd_bddVarMap() + pbVarsX = ALLOC( DdNode *, dd->size ); + pbVarsY = ALLOC( DdNode *, dd->size ); + bProd = b1; Cudd_Ref( bProd ); + Abc_NtkForEachLatch( pNtk, pLatch, i ) + { + pbVarsX[i] = dd->vars[ Abc_NtkPiNum(pNtk) + i ]; + pbVarsY[i] = dd->vars[ Abc_NtkCiNum(pNtk) + i ]; + // get the initial value of the latch + bVar = Cudd_NotCond( pbVarsX[i], !Abc_LatchIsInit1(pLatch) ); + bProd = Cudd_bddAnd( dd, bTemp = bProd, bVar ); Cudd_Ref( bProd ); + Cudd_RecursiveDeref( dd, bTemp ); + } + Cudd_SetVarMap( dd, pbVarsX, pbVarsY, Abc_NtkLatchNum(pNtk) ); + FREE( pbVarsX ); + FREE( pbVarsY ); + + Cudd_Deref( bProd ); + return bProd; +} + +/**Function************************************************************* + + Synopsis [Computes the set of unreachable states.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Abc_NtkComputeUnreachable( DdManager * dd, Abc_Ntk_t * pNtk, DdNode * bTrans, DdNode * bInitial, bool fVerbose ) +{ + DdNode * bRelation, * bReached, * bCubeCs; + DdNode * bCurrent, * bNext, * bTemp; + int nIters, nMints; + + // perform reachability analisys + bCurrent = bInitial; Cudd_Ref( bCurrent ); + bReached = bInitial; Cudd_Ref( bReached ); + bRelation = bTrans; Cudd_Ref( bRelation ); + bCubeCs = Extra_bddComputeRangeCube( dd, Abc_NtkPiNum(pNtk), Abc_NtkCiNum(pNtk) ); Cudd_Ref( bCubeCs ); + for ( nIters = 1; ; nIters++ ) + { + // compute the next states + bNext = Cudd_bddAndAbstract( dd, bRelation, bCurrent, bCubeCs ); Cudd_Ref( bNext ); + Cudd_RecursiveDeref( dd, bCurrent ); + // remap these states into the current state vars + bNext = Cudd_bddVarMap( dd, bTemp = bNext ); Cudd_Ref( bNext ); + Cudd_RecursiveDeref( dd, bTemp ); + // check if there are any new states + if ( Cudd_bddLeq( dd, bNext, bReached ) ) + break; + // get the new states + bCurrent = Cudd_bddAnd( dd, bNext, Cudd_Not(bReached) ); Cudd_Ref( bCurrent ); + // minimize the new states with the reached states +// bCurrent = Cudd_bddConstrain( dd, bTemp = bCurrent, Cudd_Not(bReached) ); Cudd_Ref( bCurrent ); +// Cudd_RecursiveDeref( dd, bTemp ); + // add to the reached states + bReached = Cudd_bddOr( dd, bTemp = bReached, bNext ); Cudd_Ref( bReached ); + Cudd_RecursiveDeref( dd, bTemp ); + Cudd_RecursiveDeref( dd, bNext ); + // minimize the transition relation +// bRelation = Cudd_bddConstrain( dd, bTemp = bRelation, Cudd_Not(bReached) ); Cudd_Ref( bRelation ); +// Cudd_RecursiveDeref( dd, bTemp ); + } + Cudd_RecursiveDeref( dd, bRelation ); + Cudd_RecursiveDeref( dd, bCubeCs ); + Cudd_RecursiveDeref( dd, bNext ); + // report the stats + if ( fVerbose ) + { + nMints = (int)Cudd_CountMinterm(dd, bReached, Abc_NtkLatchNum(pNtk) ); + fprintf( stdout, "Reachability analysis completed in %d iterations.\n", nIters ); + fprintf( stdout, "The number of minterms in the reachable state set = %d. (%6.2f %%)\n", nMints, 100.0*nMints/(1<pName = Extra_UtilStrsav( "exdc" ); + pNtkNew->pSpec = NULL; + + // create PIs corresponding to LOs + Abc_NtkForEachLatchOutput( pNtk, pNode, i ) + Abc_ObjAssignName( pNode->pCopy = Abc_NtkCreatePi(pNtkNew), Abc_ObjName(pNode), NULL ); + // cannot ADD POs here because pLatch->pCopy point to the PIs + + // create a new node + pNodeNew = Abc_NtkCreateNode(pNtkNew); + // add the fanins corresponding to latch outputs + Abc_NtkForEachLatchOutput( pNtk, pNode, i ) + Abc_ObjAddFanin( pNodeNew, pNode->pCopy ); + + // create the logic function + pPermute = ALLOC( int, dd->size ); + for ( i = 0; i < dd->size; i++ ) + pPermute[i] = -1; + Abc_NtkForEachLatch( pNtk, pNode, i ) + pPermute[Abc_NtkPiNum(pNtk) + i] = i; + // remap the functions + pNodeNew->pData = Extra_TransferPermute( dd, pNtkNew->pManFunc, bUnreach, pPermute ); Cudd_Ref( pNodeNew->pData ); + free( pPermute ); + Abc_NodeMinimumBase( pNodeNew ); + + // for each CO, create PO (skip POs equal to CIs because of name conflict) + Abc_NtkForEachPo( pNtk, pNode, i ) + if ( !Abc_ObjIsCi(Abc_ObjFanin0(pNode)) ) + Abc_ObjAssignName( pNode->pCopy = Abc_NtkCreatePo(pNtkNew), Abc_ObjName(pNode), NULL ); + Abc_NtkForEachLatchInput( pNtk, pNode, i ) + Abc_ObjAssignName( pNode->pCopy = Abc_NtkCreatePo(pNtkNew), Abc_ObjName(pNode), NULL ); + + // link to the POs of the network + Abc_NtkForEachPo( pNtk, pNode, i ) + if ( !Abc_ObjIsCi(Abc_ObjFanin0(pNode)) ) + Abc_ObjAddFanin( pNode->pCopy, pNodeNew ); + Abc_NtkForEachLatchInput( pNtk, pNode, i ) + Abc_ObjAddFanin( pNode->pCopy, pNodeNew ); + + // remove the extra nodes + Abc_AigCleanup( pNtkNew->pManFunc ); + + // fix the problem with complemented and duplicated CO edges + Abc_NtkLogicMakeSimpleCos( pNtkNew, 0 ); + + // transform the network to the SOP representation + if ( !Abc_NtkBddToSop( pNtkNew, 0 ) ) + { + printf( "Abc_NtkConstructExdc(): Converting to SOPs has failed.\n" ); + return NULL; + } + return pNtkNew; +// return NULL; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcVerify.c b/abc_with_bb_support/src/base/abci/abcVerify.c new file mode 100644 index 000000000..f7014aa05 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcVerify.c @@ -0,0 +1,1010 @@ +/**CFile**************************************************************** + + FileName [abcVerify.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Combinational and sequential verification for two networks.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcVerify.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "fraig.h" +#include "sim.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Abc_NtkVerifyReportError( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int * pModel ); +extern void Abc_NtkVerifyReportErrorSeq( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int * pModel, int nFrames ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Verifies combinational equivalence by brute-force SAT.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkCecSat( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int nConfLimit, int nInsLimit ) +{ + extern Abc_Ntk_t * Abc_NtkMulti( Abc_Ntk_t * pNtk, int nThresh, int nFaninMax, int fCnf, int fMulti, int fSimple, int fFactor ); + Abc_Ntk_t * pMiter; + Abc_Ntk_t * pCnf; + int RetValue; + + // get the miter of the two networks + pMiter = Abc_NtkMiter( pNtk1, pNtk2, 1, 0 ); + if ( pMiter == NULL ) + { + printf( "Miter computation has failed.\n" ); + return; + } + RetValue = Abc_NtkMiterIsConstant( pMiter ); + if ( RetValue == 0 ) + { + printf( "Networks are NOT EQUIVALENT after structural hashing.\n" ); + // report the error + pMiter->pModel = Abc_NtkVerifyGetCleanModel( pMiter, 1 ); + Abc_NtkVerifyReportError( pNtk1, pNtk2, pMiter->pModel ); + FREE( pMiter->pModel ); + Abc_NtkDelete( pMiter ); + return; + } + if ( RetValue == 1 ) + { + Abc_NtkDelete( pMiter ); + printf( "Networks are equivalent after structural hashing.\n" ); + return; + } + + // convert the miter into a CNF + pCnf = Abc_NtkMulti( pMiter, 0, 100, 1, 0, 0, 0 ); + Abc_NtkDelete( pMiter ); + if ( pCnf == NULL ) + { + printf( "Renoding for CNF has failed.\n" ); + return; + } + + // solve the CNF using the SAT solver + RetValue = Abc_NtkMiterSat( pCnf, (sint64)nConfLimit, (sint64)nInsLimit, 0, NULL, NULL ); + if ( RetValue == -1 ) + printf( "Networks are undecided (SAT solver timed out).\n" ); + else if ( RetValue == 0 ) + printf( "Networks are NOT EQUIVALENT after SAT.\n" ); + else + printf( "Networks are equivalent after SAT.\n" ); + if ( pCnf->pModel ) + Abc_NtkVerifyReportError( pNtk1, pNtk2, pCnf->pModel ); + FREE( pCnf->pModel ); + Abc_NtkDelete( pCnf ); +} + + +/**Function************************************************************* + + Synopsis [Verifies sequential equivalence by fraiging followed by SAT.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkCecFraig( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int nSeconds, int fVerbose ) +{ + Prove_Params_t Params, * pParams = &Params; +// Fraig_Params_t Params; +// Fraig_Man_t * pMan; + Abc_Ntk_t * pMiter; + int RetValue; + + // get the miter of the two networks + pMiter = Abc_NtkMiter( pNtk1, pNtk2, 1, 0 ); + if ( pMiter == NULL ) + { + printf( "Miter computation has failed.\n" ); + return; + } + RetValue = Abc_NtkMiterIsConstant( pMiter ); + if ( RetValue == 0 ) + { + printf( "Networks are NOT EQUIVALENT after structural hashing.\n" ); + // report the error + pMiter->pModel = Abc_NtkVerifyGetCleanModel( pMiter, 1 ); + Abc_NtkVerifyReportError( pNtk1, pNtk2, pMiter->pModel ); + FREE( pMiter->pModel ); + Abc_NtkDelete( pMiter ); + return; + } + if ( RetValue == 1 ) + { + printf( "Networks are equivalent after structural hashing.\n" ); + Abc_NtkDelete( pMiter ); + return; + } +/* + // convert the miter into a FRAIG + Fraig_ParamsSetDefault( &Params ); + Params.fVerbose = fVerbose; + Params.nSeconds = nSeconds; +// Params.fFuncRed = 0; +// Params.nPatsRand = 0; +// Params.nPatsDyna = 0; + pMan = Abc_NtkToFraig( pMiter, &Params, 0, 0 ); + Fraig_ManProveMiter( pMan ); + + // analyze the result + RetValue = Fraig_ManCheckMiter( pMan ); + // report the result + if ( RetValue == -1 ) + printf( "Networks are undecided (SAT solver timed out on the final miter).\n" ); + else if ( RetValue == 1 ) + printf( "Networks are equivalent after fraiging.\n" ); + else if ( RetValue == 0 ) + { + printf( "Networks are NOT EQUIVALENT after fraiging.\n" ); + Abc_NtkVerifyReportError( pNtk1, pNtk2, Fraig_ManReadModel(pMan) ); + } + else assert( 0 ); + // delete the fraig manager + Fraig_ManFree( pMan ); + // delete the miter + Abc_NtkDelete( pMiter ); +*/ + // solve the CNF using the SAT solver + Prove_ParamsSetDefault( pParams ); + pParams->nItersMax = 5; +// RetValue = Abc_NtkMiterProve( &pMiter, pParams ); +// pParams->fVerbose = 1; + RetValue = Abc_NtkIvyProve( &pMiter, pParams ); + if ( RetValue == -1 ) + printf( "Networks are undecided (resource limits is reached).\n" ); + else if ( RetValue == 0 ) + { + int * pSimInfo = Abc_NtkVerifySimulatePattern( pMiter, pMiter->pModel ); + if ( pSimInfo[0] != 1 ) + printf( "ERROR in Abc_NtkMiterProve(): Generated counter-example is invalid.\n" ); + else + printf( "Networks are NOT EQUIVALENT.\n" ); + free( pSimInfo ); + } + else + printf( "Networks are equivalent.\n" ); + if ( pMiter->pModel ) + Abc_NtkVerifyReportError( pNtk1, pNtk2, pMiter->pModel ); + Abc_NtkDelete( pMiter ); +} + +/**Function************************************************************* + + Synopsis [Verifies sequential equivalence by fraiging followed by SAT.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkCecFraigPart( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int nSeconds, int nPartSize, int fVerbose ) +{ + extern int Cmd_CommandExecute( void * pAbc, char * sCommand ); + extern void * Abc_FrameGetGlobalFrame(); + + Prove_Params_t Params, * pParams = &Params; + Abc_Ntk_t * pMiter, * pMiterPart; + Abc_Obj_t * pObj; + int i, RetValue, Status, nOutputs; + + // solve the CNF using the SAT solver + Prove_ParamsSetDefault( pParams ); + pParams->nItersMax = 5; + // pParams->fVerbose = 1; + + assert( nPartSize > 0 ); + + // get the miter of the two networks + pMiter = Abc_NtkMiter( pNtk1, pNtk2, 1, nPartSize ); + if ( pMiter == NULL ) + { + printf( "Miter computation has failed.\n" ); + return; + } + RetValue = Abc_NtkMiterIsConstant( pMiter ); + if ( RetValue == 0 ) + { + printf( "Networks are NOT EQUIVALENT after structural hashing.\n" ); + // report the error + pMiter->pModel = Abc_NtkVerifyGetCleanModel( pMiter, 1 ); + Abc_NtkVerifyReportError( pNtk1, pNtk2, pMiter->pModel ); + FREE( pMiter->pModel ); + Abc_NtkDelete( pMiter ); + return; + } + if ( RetValue == 1 ) + { + printf( "Networks are equivalent after structural hashing.\n" ); + Abc_NtkDelete( pMiter ); + return; + } + + Cmd_CommandExecute( Abc_FrameGetGlobalFrame(), "unset progressbar" ); + + // solve the problem iteratively for each output of the miter + Status = 1; + nOutputs = 0; + Abc_NtkForEachPo( pMiter, pObj, i ) + { + if ( Abc_ObjFanin0(pObj) == Abc_AigConst1(pMiter) ) + { + if ( Abc_ObjFaninC0(pObj) ) // complemented -> const 0 + RetValue = 1; + else + RetValue = 0; + pMiterPart = NULL; + } + else + { + // get the cone of this output + pMiterPart = Abc_NtkCreateCone( pMiter, Abc_ObjFanin0(pObj), Abc_ObjName(pObj), 0 ); + if ( Abc_ObjFaninC0(pObj) ) + Abc_ObjXorFaninC( Abc_NtkPo(pMiterPart,0), 0 ); + // solve the cone + // RetValue = Abc_NtkMiterProve( &pMiterPart, pParams ); + RetValue = Abc_NtkIvyProve( &pMiterPart, pParams ); + } + + if ( RetValue == -1 ) + { + printf( "Networks are undecided (resource limits is reached).\r" ); + Status = -1; + } + else if ( RetValue == 0 ) + { + int * pSimInfo = Abc_NtkVerifySimulatePattern( pMiterPart, pMiterPart->pModel ); + if ( pSimInfo[0] != 1 ) + printf( "ERROR in Abc_NtkMiterProve(): Generated counter-example is invalid.\n" ); + else + printf( "Networks are NOT EQUIVALENT. \n" ); + free( pSimInfo ); + Status = 0; + break; + } + else + { + printf( "Finished part %d (out of %d)\r", i+1, Abc_NtkPoNum(pMiter) ); + nOutputs += nPartSize; + } +// if ( pMiter->pModel ) +// Abc_NtkVerifyReportError( pNtk1, pNtk2, pMiter->pModel ); + if ( pMiterPart ) + Abc_NtkDelete( pMiterPart ); + } + + Cmd_CommandExecute( Abc_FrameGetGlobalFrame(), "set progressbar" ); + + if ( Status == 1 ) + printf( "Networks are equivalent. \n" ); + else if ( Status == -1 ) + printf( "Timed out after verifying %d outputs (out of %d).\n", nOutputs, Abc_NtkCoNum(pNtk1) ); + Abc_NtkDelete( pMiter ); +} + +/**Function************************************************************* + + Synopsis [Verifies sequential equivalence by fraiging followed by SAT.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkCecFraigPartAuto( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int nSeconds, int fVerbose ) +{ + extern int Abc_NtkCombinePos( Abc_Ntk_t * pNtk, int fAnd ); + extern Vec_Vec_t * Abc_NtkPartitionSmart( Abc_Ntk_t * pNtk, int nPartSizeLimit, int fVerbose ); + extern int Cmd_CommandExecute( void * pAbc, char * sCommand ); + extern void * Abc_FrameGetGlobalFrame(); + + Vec_Vec_t * vParts; + Vec_Ptr_t * vOne; + Prove_Params_t Params, * pParams = &Params; + Abc_Ntk_t * pMiter, * pMiterPart; + int i, RetValue, Status, nOutputs; + + // solve the CNF using the SAT solver + Prove_ParamsSetDefault( pParams ); + pParams->nItersMax = 5; + // pParams->fVerbose = 1; + + // get the miter of the two networks + pMiter = Abc_NtkMiter( pNtk1, pNtk2, 1, 1 ); + if ( pMiter == NULL ) + { + printf( "Miter computation has failed.\n" ); + return; + } + RetValue = Abc_NtkMiterIsConstant( pMiter ); + if ( RetValue == 0 ) + { + printf( "Networks are NOT EQUIVALENT after structural hashing.\n" ); + // report the error + pMiter->pModel = Abc_NtkVerifyGetCleanModel( pMiter, 1 ); + Abc_NtkVerifyReportError( pNtk1, pNtk2, pMiter->pModel ); + FREE( pMiter->pModel ); + Abc_NtkDelete( pMiter ); + return; + } + if ( RetValue == 1 ) + { + printf( "Networks are equivalent after structural hashing.\n" ); + Abc_NtkDelete( pMiter ); + return; + } + + Cmd_CommandExecute( Abc_FrameGetGlobalFrame(), "unset progressbar" ); + + // partition the outputs + vParts = Abc_NtkPartitionSmart( pMiter, 50, 1 ); + + // fraig each partition + Status = 1; + nOutputs = 0; + Vec_VecForEachLevel( vParts, vOne, i ) + { + // get this part of the miter + pMiterPart = Abc_NtkCreateConeArray( pMiter, vOne, 0 ); + Abc_NtkCombinePos( pMiterPart, 0 ); + // check the miter for being constant + RetValue = Abc_NtkMiterIsConstant( pMiterPart ); + if ( RetValue == 0 ) + { + printf( "Networks are NOT EQUIVALENT after partitioning.\n" ); + Abc_NtkDelete( pMiterPart ); + break; + } + if ( RetValue == 1 ) + { + Abc_NtkDelete( pMiterPart ); + continue; + } + // solve the problem + RetValue = Abc_NtkIvyProve( &pMiterPart, pParams ); + if ( RetValue == -1 ) + { + printf( "Networks are undecided (resource limits is reached).\r" ); + Status = -1; + } + else if ( RetValue == 0 ) + { + int * pSimInfo = Abc_NtkVerifySimulatePattern( pMiterPart, pMiterPart->pModel ); + if ( pSimInfo[0] != 1 ) + printf( "ERROR in Abc_NtkMiterProve(): Generated counter-example is invalid.\n" ); + else + printf( "Networks are NOT EQUIVALENT. \n" ); + free( pSimInfo ); + Status = 0; + Abc_NtkDelete( pMiterPart ); + break; + } + else + { + printf( "Finished part %d (out of %d)\r", i+1, Vec_VecSize(vParts) ); + nOutputs += Vec_PtrSize(vOne); + } + Abc_NtkDelete( pMiterPart ); + } + Vec_VecFree( vParts ); + + Cmd_CommandExecute( Abc_FrameGetGlobalFrame(), "set progressbar" ); + + if ( Status == 1 ) + printf( "Networks are equivalent. \n" ); + else if ( Status == -1 ) + printf( "Timed out after verifying %d outputs (out of %d).\n", nOutputs, Abc_NtkCoNum(pNtk1) ); + Abc_NtkDelete( pMiter ); +} + +/**Function************************************************************* + + Synopsis [Verifies sequential equivalence by brute-force SAT.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkSecSat( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int nConfLimit, int nInsLimit, int nFrames ) +{ + extern Abc_Ntk_t * Abc_NtkMulti( Abc_Ntk_t * pNtk, int nThresh, int nFaninMax, int fCnf, int fMulti, int fSimple, int fFactor ); + Abc_Ntk_t * pMiter; + Abc_Ntk_t * pFrames; + Abc_Ntk_t * pCnf; + int RetValue; + + // get the miter of the two networks + pMiter = Abc_NtkMiter( pNtk1, pNtk2, 0, 0 ); + if ( pMiter == NULL ) + { + printf( "Miter computation has failed.\n" ); + return; + } + RetValue = Abc_NtkMiterIsConstant( pMiter ); + if ( RetValue == 0 ) + { + Abc_NtkDelete( pMiter ); + printf( "Networks are NOT EQUIVALENT after structural hashing.\n" ); + return; + } + if ( RetValue == 1 ) + { + Abc_NtkDelete( pMiter ); + printf( "Networks are equivalent after structural hashing.\n" ); + return; + } + + // create the timeframes + pFrames = Abc_NtkFrames( pMiter, nFrames, 1 ); + Abc_NtkDelete( pMiter ); + if ( pFrames == NULL ) + { + printf( "Frames computation has failed.\n" ); + return; + } + RetValue = Abc_NtkMiterIsConstant( pFrames ); + if ( RetValue == 0 ) + { + Abc_NtkDelete( pFrames ); + printf( "Networks are NOT EQUIVALENT after framing.\n" ); + return; + } + if ( RetValue == 1 ) + { + Abc_NtkDelete( pFrames ); + printf( "Networks are equivalent after framing.\n" ); + return; + } + + // convert the miter into a CNF + pCnf = Abc_NtkMulti( pFrames, 0, 100, 1, 0, 0, 0 ); + Abc_NtkDelete( pFrames ); + if ( pCnf == NULL ) + { + printf( "Renoding for CNF has failed.\n" ); + return; + } + + // solve the CNF using the SAT solver + RetValue = Abc_NtkMiterSat( pCnf, (sint64)nConfLimit, (sint64)nInsLimit, 0, NULL, NULL ); + if ( RetValue == -1 ) + printf( "Networks are undecided (SAT solver timed out).\n" ); + else if ( RetValue == 0 ) + printf( "Networks are NOT EQUIVALENT after SAT.\n" ); + else + printf( "Networks are equivalent after SAT.\n" ); + Abc_NtkDelete( pCnf ); +} + +/**Function************************************************************* + + Synopsis [Verifies combinational equivalence by fraiging followed by SAT] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkSecFraig( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int nSeconds, int nFrames, int fVerbose ) +{ + Fraig_Params_t Params; + Fraig_Man_t * pMan; + Abc_Ntk_t * pMiter; + Abc_Ntk_t * pFrames; + int RetValue; + + // get the miter of the two networks + pMiter = Abc_NtkMiter( pNtk1, pNtk2, 0, 0 ); + if ( pMiter == NULL ) + { + printf( "Miter computation has failed.\n" ); + return 0; + } + RetValue = Abc_NtkMiterIsConstant( pMiter ); + if ( RetValue == 0 ) + { + printf( "Networks are NOT EQUIVALENT after structural hashing.\n" ); + // report the error + pMiter->pModel = Abc_NtkVerifyGetCleanModel( pMiter, nFrames ); + Abc_NtkVerifyReportErrorSeq( pNtk1, pNtk2, pMiter->pModel, nFrames ); + FREE( pMiter->pModel ); + Abc_NtkDelete( pMiter ); + return 0; + } + if ( RetValue == 1 ) + { + Abc_NtkDelete( pMiter ); + printf( "Networks are equivalent after structural hashing.\n" ); + return 1; + } + + // create the timeframes + pFrames = Abc_NtkFrames( pMiter, nFrames, 1 ); + Abc_NtkDelete( pMiter ); + if ( pFrames == NULL ) + { + printf( "Frames computation has failed.\n" ); + return 0; + } + RetValue = Abc_NtkMiterIsConstant( pFrames ); + if ( RetValue == 0 ) + { + printf( "Networks are NOT EQUIVALENT after framing.\n" ); + // report the error + pFrames->pModel = Abc_NtkVerifyGetCleanModel( pFrames, 1 ); +// Abc_NtkVerifyReportErrorSeq( pNtk1, pNtk2, pFrames->pModel, nFrames ); + FREE( pFrames->pModel ); + Abc_NtkDelete( pFrames ); + return 0; + } + if ( RetValue == 1 ) + { + Abc_NtkDelete( pFrames ); + printf( "Networks are equivalent after framing.\n" ); + return 1; + } + + // convert the miter into a FRAIG + Fraig_ParamsSetDefault( &Params ); + Params.fVerbose = fVerbose; + Params.nSeconds = nSeconds; +// Params.fFuncRed = 0; +// Params.nPatsRand = 0; +// Params.nPatsDyna = 0; + pMan = Abc_NtkToFraig( pFrames, &Params, 0, 0 ); + Fraig_ManProveMiter( pMan ); + + // analyze the result + RetValue = Fraig_ManCheckMiter( pMan ); + // report the result + if ( RetValue == -1 ) + printf( "Networks are undecided (SAT solver timed out on the final miter).\n" ); + else if ( RetValue == 1 ) + printf( "Networks are equivalent after fraiging.\n" ); + else if ( RetValue == 0 ) + { + printf( "Networks are NOT EQUIVALENT after fraiging.\n" ); +// Abc_NtkVerifyReportErrorSeq( pNtk1, pNtk2, Fraig_ManReadModel(pMan), nFrames ); + } + else assert( 0 ); + // delete the fraig manager + Fraig_ManFree( pMan ); + // delete the miter + Abc_NtkDelete( pFrames ); + return RetValue == 1; +} + +/**Function************************************************************* + + Synopsis [Returns a dummy pattern full of zeros.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int * Abc_NtkVerifyGetCleanModel( Abc_Ntk_t * pNtk, int nFrames ) +{ + int * pModel = ALLOC( int, Abc_NtkCiNum(pNtk) * nFrames ); + memset( pModel, 0, sizeof(int) * Abc_NtkCiNum(pNtk) * nFrames ); + return pModel; +} + +/**Function************************************************************* + + Synopsis [Returns the PO values under the given input pattern.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int * Abc_NtkVerifySimulatePattern( Abc_Ntk_t * pNtk, int * pModel ) +{ + Abc_Obj_t * pNode; + int * pValues, Value0, Value1, i; + int fStrashed = 0; + if ( !Abc_NtkIsStrash(pNtk) ) + { + pNtk = Abc_NtkStrash(pNtk, 0, 0, 0); + fStrashed = 1; + } +/* + printf( "Counter example: " ); + Abc_NtkForEachCi( pNtk, pNode, i ) + printf( " %d", pModel[i] ); + printf( "\n" ); +*/ + // increment the trav ID + Abc_NtkIncrementTravId( pNtk ); + // set the CI values + Abc_AigConst1(pNtk)->pCopy = (void *)1; + Abc_NtkForEachCi( pNtk, pNode, i ) + pNode->pCopy = (void *)pModel[i]; + // simulate in the topological order + Abc_NtkForEachNode( pNtk, pNode, i ) + { + Value0 = ((int)Abc_ObjFanin0(pNode)->pCopy) ^ Abc_ObjFaninC0(pNode); + Value1 = ((int)Abc_ObjFanin1(pNode)->pCopy) ^ Abc_ObjFaninC1(pNode); + pNode->pCopy = (void *)(Value0 & Value1); + } + // fill the output values + pValues = ALLOC( int, Abc_NtkCoNum(pNtk) ); + Abc_NtkForEachCo( pNtk, pNode, i ) + pValues[i] = ((int)Abc_ObjFanin0(pNode)->pCopy) ^ Abc_ObjFaninC0(pNode); + if ( fStrashed ) + Abc_NtkDelete( pNtk ); + return pValues; +} + + +/**Function************************************************************* + + Synopsis [Reports mismatch between the two networks.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkVerifyReportError( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int * pModel ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pNode; + int * pValues1, * pValues2; + int nErrors, nPrinted, i, iNode = -1; + + assert( Abc_NtkCiNum(pNtk1) == Abc_NtkCiNum(pNtk2) ); + assert( Abc_NtkCoNum(pNtk1) == Abc_NtkCoNum(pNtk2) ); + // get the CO values under this model + pValues1 = Abc_NtkVerifySimulatePattern( pNtk1, pModel ); + pValues2 = Abc_NtkVerifySimulatePattern( pNtk2, pModel ); + // count the mismatches + nErrors = 0; + for ( i = 0; i < Abc_NtkCoNum(pNtk1); i++ ) + nErrors += (int)( pValues1[i] != pValues2[i] ); + printf( "Verification failed for at least %d outputs: ", nErrors ); + // print the first 3 outputs + nPrinted = 0; + for ( i = 0; i < Abc_NtkCoNum(pNtk1); i++ ) + if ( pValues1[i] != pValues2[i] ) + { + if ( iNode == -1 ) + iNode = i; + printf( " %s", Abc_ObjName(Abc_NtkCo(pNtk1,i)) ); + if ( ++nPrinted == 3 ) + break; + } + if ( nPrinted != nErrors ) + printf( " ..." ); + printf( "\n" ); + // report mismatch for the first output + if ( iNode >= 0 ) + { + printf( "Output %s: Value in Network1 = %d. Value in Network2 = %d.\n", + Abc_ObjName(Abc_NtkCo(pNtk1,iNode)), pValues1[iNode], pValues2[iNode] ); + printf( "Input pattern: " ); + // collect PIs in the cone + pNode = Abc_NtkCo(pNtk1,iNode); + vNodes = Abc_NtkNodeSupport( pNtk1, &pNode, 1 ); + // set the PI numbers + Abc_NtkForEachCi( pNtk1, pNode, i ) + pNode->pCopy = (void*)i; + // print the model + pNode = Vec_PtrEntry( vNodes, 0 ); + if ( Abc_ObjIsCi(pNode) ) + { + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + assert( Abc_ObjIsCi(pNode) ); + printf( " %s=%d", Abc_ObjName(pNode), pModel[(int)pNode->pCopy] ); + } + } + printf( "\n" ); + Vec_PtrFree( vNodes ); + } + free( pValues1 ); + free( pValues2 ); +} + + +/**Function************************************************************* + + Synopsis [Computes the COs in the support of the PO in the given frame.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkGetSeqPoSupp( Abc_Ntk_t * pNtk, int iFrame, int iNumPo ) +{ + Abc_Ntk_t * pFrames; + Abc_Obj_t * pObj, * pNodePo; + Vec_Ptr_t * vSupp; + int i, k; + // get the timeframes of the network + pFrames = Abc_NtkFrames( pNtk, iFrame + 1, 0 ); +//Abc_NtkShowAig( pFrames ); + + // get the PO of the timeframes + pNodePo = Abc_NtkPo( pFrames, iFrame * Abc_NtkPoNum(pNtk) + iNumPo ); + // set the support + vSupp = Abc_NtkNodeSupport( pFrames, &pNodePo, 1 ); + // mark the support of the frames + Abc_NtkForEachCi( pFrames, pObj, i ) + pObj->pCopy = NULL; + Vec_PtrForEachEntry( vSupp, pObj, i ) + pObj->pCopy = (void *)1; + // mark the support of the network if the support of the timeframes is marked + Abc_NtkForEachCi( pNtk, pObj, i ) + pObj->pCopy = NULL; + Abc_NtkForEachLatch( pNtk, pObj, i ) + if ( Abc_NtkBox(pFrames, i)->pCopy ) + pObj->pCopy = (void *)1; + Abc_NtkForEachPi( pNtk, pObj, i ) + for ( k = 0; k <= iFrame; k++ ) + if ( Abc_NtkPi(pFrames, k*Abc_NtkPiNum(pNtk) + i)->pCopy ) + pObj->pCopy = (void *)1; + // free stuff + Vec_PtrFree( vSupp ); + Abc_NtkDelete( pFrames ); +} + +/**Function************************************************************* + + Synopsis [Reports mismatch between the two sequential networks.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkVerifyReportErrorSeq( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int * pModel, int nFrames ) +{ + Vec_Ptr_t * vInfo1, * vInfo2; + Abc_Obj_t * pObj, * pObjError, * pObj1, * pObj2; + int ValueError1, ValueError2; + unsigned * pPats1, * pPats2; + int i, o, k, nErrors, iFrameError, iNodePo, nPrinted; + int fRemove1 = 0, fRemove2 = 0; + + if ( !Abc_NtkIsStrash(pNtk1) ) + fRemove1 = 1, pNtk1 = Abc_NtkStrash( pNtk1, 0, 0, 0 ); + if ( !Abc_NtkIsStrash(pNtk2) ) + fRemove2 = 1, pNtk2 = Abc_NtkStrash( pNtk2, 0, 0, 0 ); + + // simulate sequential circuits + vInfo1 = Sim_SimulateSeqModel( pNtk1, nFrames, pModel ); + vInfo2 = Sim_SimulateSeqModel( pNtk2, nFrames, pModel ); + + // look for a discrepancy in the PO values + nErrors = 0; + pObjError = NULL; + for ( i = 0; i < nFrames; i++ ) + { + if ( pObjError ) + break; + Abc_NtkForEachPo( pNtk1, pObj1, o ) + { + pObj2 = Abc_NtkPo( pNtk2, o ); + pPats1 = Sim_SimInfoGet(vInfo1, pObj1); + pPats2 = Sim_SimInfoGet(vInfo2, pObj2); + if ( pPats1[i] == pPats2[i] ) + continue; + nErrors++; + if ( pObjError == NULL ) + { + pObjError = pObj1; + iFrameError = i; + iNodePo = o; + ValueError1 = (pPats1[i] > 0); + ValueError2 = (pPats2[i] > 0); + } + } + } + + if ( pObjError == NULL ) + { + printf( "No output mismatches detected.\n" ); + Sim_UtilInfoFree( vInfo1 ); + Sim_UtilInfoFree( vInfo2 ); + if ( fRemove1 ) Abc_NtkDelete( pNtk1 ); + if ( fRemove2 ) Abc_NtkDelete( pNtk2 ); + return; + } + + printf( "Verification failed for at least %d output%s of frame %d: ", nErrors, (nErrors>1? "s":""), iFrameError+1 ); + // print the first 3 outputs + nPrinted = 0; + Abc_NtkForEachPo( pNtk1, pObj1, o ) + { + pObj2 = Abc_NtkPo( pNtk2, o ); + pPats1 = Sim_SimInfoGet(vInfo1, pObj1); + pPats2 = Sim_SimInfoGet(vInfo2, pObj2); + if ( pPats1[iFrameError] == pPats2[iFrameError] ) + continue; + printf( " %s", Abc_ObjName(pObj1) ); + if ( ++nPrinted == 3 ) + break; + } + if ( nPrinted != nErrors ) + printf( " ..." ); + printf( "\n" ); + + // mark CIs of the networks in the cone of influence of this output + Abc_NtkGetSeqPoSupp( pNtk1, iFrameError, iNodePo ); + Abc_NtkGetSeqPoSupp( pNtk2, iFrameError, iNodePo ); + + // report mismatch for the first output + printf( "Output %s: Value in Network1 = %d. Value in Network2 = %d.\n", + Abc_ObjName(pObjError), ValueError1, ValueError2 ); + + printf( "The cone of influence of output %s in Network1:\n", Abc_ObjName(pObjError) ); + printf( "PIs: " ); + Abc_NtkForEachPi( pNtk1, pObj, i ) + if ( pObj->pCopy ) + printf( "%s ", Abc_ObjName(pObj) ); + printf( "\n" ); + printf( "Latches: " ); + Abc_NtkForEachLatch( pNtk1, pObj, i ) + if ( pObj->pCopy ) + printf( "%s ", Abc_ObjName(pObj) ); + printf( "\n" ); + + printf( "The cone of influence of output %s in Network2:\n", Abc_ObjName(pObjError) ); + printf( "PIs: " ); + Abc_NtkForEachPi( pNtk2, pObj, i ) + if ( pObj->pCopy ) + printf( "%s ", Abc_ObjName(pObj) ); + printf( "\n" ); + printf( "Latches: " ); + Abc_NtkForEachLatch( pNtk2, pObj, i ) + if ( pObj->pCopy ) + printf( "%s ", Abc_ObjName(pObj) ); + printf( "\n" ); + + // print the patterns + for ( i = 0; i <= iFrameError; i++ ) + { + printf( "Frame %d: ", i+1 ); + + printf( "PI(1):" ); + Abc_NtkForEachPi( pNtk1, pObj, k ) + if ( pObj->pCopy ) + printf( "%d", Sim_SimInfoGet(vInfo1, pObj)[i] > 0 ); + printf( " " ); + printf( "L(1):" ); + Abc_NtkForEachLatch( pNtk1, pObj, k ) + if ( pObj->pCopy ) + printf( "%d", Sim_SimInfoGet(vInfo1, pObj)[i] > 0 ); + printf( " " ); + printf( "%s(1):", Abc_ObjName(pObjError) ); + printf( "%d", Sim_SimInfoGet(vInfo1, pObjError)[i] > 0 ); + + printf( " " ); + + printf( "PI(2):" ); + Abc_NtkForEachPi( pNtk2, pObj, k ) + if ( pObj->pCopy ) + printf( "%d", Sim_SimInfoGet(vInfo2, pObj)[i] > 0 ); + printf( " " ); + printf( "L(2):" ); + Abc_NtkForEachLatch( pNtk2, pObj, k ) + if ( pObj->pCopy ) + printf( "%d", Sim_SimInfoGet(vInfo2, pObj)[i] > 0 ); + printf( " " ); + printf( "%s(2):", Abc_ObjName(pObjError) ); + printf( "%d", Sim_SimInfoGet(vInfo2, pObjError)[i] > 0 ); + + printf( "\n" ); + } + Abc_NtkForEachCi( pNtk1, pObj, i ) + pObj->pCopy = NULL; + Abc_NtkForEachCi( pNtk2, pObj, i ) + pObj->pCopy = NULL; + + Sim_UtilInfoFree( vInfo1 ); + Sim_UtilInfoFree( vInfo2 ); + if ( fRemove1 ) Abc_NtkDelete( pNtk1 ); + if ( fRemove2 ) Abc_NtkDelete( pNtk2 ); +} + +/**Function************************************************************* + + Synopsis [Simulates buggy miter emailed by Mike.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkSimulteBuggyMiter( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i; + int * pModel1, * pModel2, * pResult1, * pResult2; + char * vPiValues1 = "01001011100000000011010110101000000"; + char * vPiValues2 = "11001101011101011111110100100010001"; + + assert( strlen(vPiValues1) == (unsigned)Abc_NtkPiNum(pNtk) ); + assert( 1 == Abc_NtkPoNum(pNtk) ); + + pModel1 = ALLOC( int, Abc_NtkCiNum(pNtk) ); + Abc_NtkForEachPi( pNtk, pObj, i ) + pModel1[i] = vPiValues1[i] - '0'; + Abc_NtkForEachLatch( pNtk, pObj, i ) + pModel1[Abc_NtkPiNum(pNtk)+i] = ((int)pObj->pData) - 1; + + pResult1 = Abc_NtkVerifySimulatePattern( pNtk, pModel1 ); + printf( "Value = %d\n", pResult1[0] ); + + pModel2 = ALLOC( int, Abc_NtkCiNum(pNtk) ); + Abc_NtkForEachPi( pNtk, pObj, i ) + pModel2[i] = vPiValues2[i] - '0'; + Abc_NtkForEachLatch( pNtk, pObj, i ) + pModel2[Abc_NtkPiNum(pNtk)+i] = pResult1[Abc_NtkPoNum(pNtk)+i]; + + pResult2 = Abc_NtkVerifySimulatePattern( pNtk, pModel2 ); + printf( "Value = %d\n", pResult2[0] ); + + free( pModel1 ); + free( pModel2 ); + free( pResult1 ); + free( pResult2 ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abcXsim.c b/abc_with_bb_support/src/base/abci/abcXsim.c new file mode 100644 index 000000000..e646dcbda --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abcXsim.c @@ -0,0 +1,207 @@ +/**CFile**************************************************************** + + FileName [abcXsim.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Using X-valued simulation.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcXsim.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define XVS0 ABC_INIT_ZERO +#define XVS1 ABC_INIT_ONE +#define XVSX ABC_INIT_DC + +static inline void Abc_ObjSetXsim( Abc_Obj_t * pObj, int Value ) { pObj->pCopy = (void *)Value; } +static inline int Abc_ObjGetXsim( Abc_Obj_t * pObj ) { return (int)pObj->pCopy; } +static inline int Abc_XsimInv( int Value ) +{ + if ( Value == XVS0 ) + return XVS1; + if ( Value == XVS1 ) + return XVS0; + assert( Value == XVSX ); + return XVSX; +} +static inline int Abc_XsimAnd( int Value0, int Value1 ) +{ + if ( Value0 == XVS0 || Value1 == XVS0 ) + return XVS0; + if ( Value0 == XVSX || Value1 == XVSX ) + return XVSX; + assert( Value0 == XVS1 && Value1 == XVS1 ); + return XVS1; +} +static inline int Abc_XsimRand2() +{ + return (rand() & 1) ? XVS1 : XVS0; +} +static inline int Abc_XsimRand3() +{ + int RetValue; + do { + RetValue = rand() & 3; + } while ( RetValue == 0 ); + return RetValue; +} +static inline int Abc_ObjGetXsimFanin0( Abc_Obj_t * pObj ) +{ + int RetValue; + RetValue = Abc_ObjGetXsim(Abc_ObjFanin0(pObj)); + return Abc_ObjFaninC0(pObj)? Abc_XsimInv(RetValue) : RetValue; +} +static inline int Abc_ObjGetXsimFanin1( Abc_Obj_t * pObj ) +{ + int RetValue; + RetValue = Abc_ObjGetXsim(Abc_ObjFanin1(pObj)); + return Abc_ObjFaninC1(pObj)? Abc_XsimInv(RetValue) : RetValue; +} +static inline void Abc_XsimPrint( FILE * pFile, int Value ) +{ + if ( Value == XVS0 ) + { + fprintf( pFile, "0" ); + return; + } + if ( Value == XVS1 ) + { + fprintf( pFile, "1" ); + return; + } + assert( Value == XVSX ); + fprintf( pFile, "x" ); +} + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs X-valued simulation of the sequential network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkXValueSimulate( Abc_Ntk_t * pNtk, int nFrames, int fInputs, int fVerbose ) +{ + Abc_Obj_t * pObj; + int i, f; + assert( Abc_NtkIsStrash(pNtk) ); + srand( 0x12341234 ); + // start simulation + Abc_ObjSetXsim( Abc_AigConst1(pNtk), XVS1 ); + if ( fInputs ) + { + Abc_NtkForEachPi( pNtk, pObj, i ) + Abc_ObjSetXsim( pObj, XVSX ); + Abc_NtkForEachLatch( pNtk, pObj, i ) + Abc_ObjSetXsim( Abc_ObjFanout0(pObj), Abc_LatchInit(pObj) ); + } + else + { + Abc_NtkForEachPi( pNtk, pObj, i ) + Abc_ObjSetXsim( pObj, Abc_XsimRand2() ); + Abc_NtkForEachLatch( pNtk, pObj, i ) + Abc_ObjSetXsim( Abc_ObjFanout0(pObj), XVSX ); + } + // simulate and print the result + fprintf( stdout, "Frame : Inputs : Latches : Outputs\n" ); + for ( f = 0; f < nFrames; f++ ) + { + Abc_AigForEachAnd( pNtk, pObj, i ) + Abc_ObjSetXsim( pObj, Abc_XsimAnd(Abc_ObjGetXsimFanin0(pObj), Abc_ObjGetXsimFanin1(pObj)) ); + Abc_NtkForEachCo( pNtk, pObj, i ) + Abc_ObjSetXsim( pObj, Abc_ObjGetXsimFanin0(pObj) ); + // print out + fprintf( stdout, "%2d : ", f ); + Abc_NtkForEachPi( pNtk, pObj, i ) + Abc_XsimPrint( stdout, Abc_ObjGetXsim(pObj) ); + fprintf( stdout, " : " ); + Abc_NtkForEachLatch( pNtk, pObj, i ) + Abc_XsimPrint( stdout, Abc_ObjGetXsim(Abc_ObjFanout0(pObj)) ); + fprintf( stdout, " : " ); + Abc_NtkForEachPo( pNtk, pObj, i ) + Abc_XsimPrint( stdout, Abc_ObjGetXsim(pObj) ); + fprintf( stdout, "\n" ); + // assign input values + if ( fInputs ) + Abc_NtkForEachPi( pNtk, pObj, i ) + Abc_ObjSetXsim( pObj, XVSX ); + else + Abc_NtkForEachPi( pNtk, pObj, i ) + Abc_ObjSetXsim( pObj, Abc_XsimRand2() ); + // transfer the latch values + Abc_NtkForEachLatch( pNtk, pObj, i ) + Abc_ObjSetXsim( Abc_ObjFanout0(pObj), Abc_ObjGetXsim(Abc_ObjFanin0(pObj)) ); + } +} + +/**Function************************************************************* + + Synopsis [Cycles the circuit to create a new initial state.] + + Description [Simulates the circuit with random input for the given + number of timeframes to get a better initial state.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkCycleInitState( Abc_Ntk_t * pNtk, int nFrames, int fVerbose ) +{ + Abc_Obj_t * pObj; + int i, f; + assert( Abc_NtkIsStrash(pNtk) ); + srand( 0x12341234 ); + // initialize the values + Abc_ObjSetXsim( Abc_AigConst1(pNtk), XVS1 ); + Abc_NtkForEachPi( pNtk, pObj, i ) + Abc_ObjSetXsim( pObj, Abc_XsimRand2() ); + Abc_NtkForEachLatch( pNtk, pObj, i ) + Abc_ObjSetXsim( Abc_ObjFanout0(pObj), Abc_LatchIsInit1(pObj)? XVS1 : XVS0 ); + // simulate for the given number of timeframes + for ( f = 0; f < nFrames; f++ ) + { + Abc_AigForEachAnd( pNtk, pObj, i ) + Abc_ObjSetXsim( pObj, Abc_XsimAnd(Abc_ObjGetXsimFanin0(pObj), Abc_ObjGetXsimFanin1(pObj)) ); + Abc_NtkForEachCo( pNtk, pObj, i ) + Abc_ObjSetXsim( pObj, Abc_ObjGetXsimFanin0(pObj) ); + // assign input values + Abc_NtkForEachPi( pNtk, pObj, i ) + Abc_ObjSetXsim( pObj, Abc_XsimRand2() ); + // transfer the latch values + Abc_NtkForEachLatch( pNtk, pObj, i ) + Abc_ObjSetXsim( Abc_ObjFanout0(pObj), Abc_ObjGetXsim(Abc_ObjFanin0(pObj)) ); + } + // set the final values + Abc_NtkForEachLatch( pNtk, pObj, i ) + pObj->pData = (void *)Abc_ObjGetXsim(Abc_ObjFanout0(pObj)); +} + +/////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abc_.c b/abc_with_bb_support/src/base/abci/abc_.c new file mode 100644 index 000000000..36dd5b26c --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abc_.c @@ -0,0 +1,48 @@ +/**CFile**************************************************************** + + FileName [abc_.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abc_.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/abci/abc_new.h b/abc_with_bb_support/src/base/abci/abc_new.h new file mode 100644 index 000000000..6cb1e5b0d --- /dev/null +++ b/abc_with_bb_support/src/base/abci/abc_new.h @@ -0,0 +1,23 @@ +struct Abc_Obj_t_ // 6 words +{ + Abc_Obj_t * pCopy; // the copy of this object + Abc_Ntk_t * pNtk; // the host network + int Id; // the object ID + int TravId; // the traversal ID + int nRefs; // the number of fanouts + unsigned Type : 4; // the object type + unsigned fMarkA : 1; // the multipurpose mark + unsigned fMarkB : 1; // the multipurpose mark + unsigned fPhase : 1; // the flag to mark the phase of equivalent node + unsigned fPersist: 1; // marks the persistant AIG node + unsigned nFanins : 24; // the level of the node + Abc_Obj_t * Fanins[0]; // the array of fanins +}; + +struct Abc_Pin_t_ // 4 words +{ + Abc_Pin_t * pNext; + Abc_Pin_t * pPrev; + Abc_Obj_t * pFanin; + Abc_Obj_t * pFanout; +}; diff --git a/abc_with_bb_support/src/base/abci/module.make b/abc_with_bb_support/src/base/abci/module.make new file mode 100644 index 000000000..dfc8c74c2 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/module.make @@ -0,0 +1,55 @@ +SRC += src/base/abci/abc.c \ + src/base/abci/abcAttach.c \ + src/base/abci/abcAuto.c \ + src/base/abci/abcBalance.c \ + src/base/abci/abcBmc.c \ + src/base/abci/abcCas.c \ + src/base/abci/abcClpBdd.c \ + src/base/abci/abcClpSop.c \ + src/base/abci/abcCut.c \ + src/base/abci/abcDar.c \ + src/base/abci/abcDebug.c \ + src/base/abci/abcDress.c \ + src/base/abci/abcDsd.c \ + src/base/abci/abcEspresso.c \ + src/base/abci/abcExtract.c \ + src/base/abci/abcFpga.c \ + src/base/abci/abcFpgaFast.c \ + src/base/abci/abcFraig.c \ + src/base/abci/abcFxu.c \ + src/base/abci/abcGen.c \ + src/base/abci/abcHaig.c \ + src/base/abci/abcIf.c \ + src/base/abci/abcIvy.c \ + src/base/abci/abcLut.c \ + src/base/abci/abcMap.c \ + src/base/abci/abcMini.c \ + src/base/abci/abcMiter.c \ + src/base/abci/abcMulti.c \ + src/base/abci/abcNtbdd.c \ + src/base/abci/abcOdc.c \ + src/base/abci/abcOrder.c \ + src/base/abci/abcPart.c \ + src/base/abci/abcPlace.c \ + src/base/abci/abcPrint.c \ + src/base/abci/abcProve.c \ + src/base/abci/abcQbf.c \ + src/base/abci/abcQuant.c \ + src/base/abci/abcRec.c \ + src/base/abci/abcReconv.c \ + src/base/abci/abcRefactor.c \ + src/base/abci/abcRenode.c \ + src/base/abci/abcReorder.c \ + src/base/abci/abcRestruct.c \ + src/base/abci/abcResub.c \ + src/base/abci/abcRewrite.c \ + src/base/abci/abcRr.c \ + src/base/abci/abcSat.c \ + src/base/abci/abcStrash.c \ + src/base/abci/abcSweep.c \ + src/base/abci/abcSymm.c \ + src/base/abci/abcTiming.c \ + src/base/abci/abcUnate.c \ + src/base/abci/abcUnreach.c \ + src/base/abci/abcVerify.c \ + src/base/abci/abcXsim.c diff --git a/abc_with_bb_support/src/base/abci/xaaaa.c b/abc_with_bb_support/src/base/abci/xaaaa.c new file mode 100644 index 000000000..a0a473685 --- /dev/null +++ b/abc_with_bb_support/src/base/abci/xaaaa.c @@ -0,0 +1,155 @@ +/**CFile**************************************************************** + + FileName [abc_.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abc_.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "kit.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintMeasures( unsigned * pTruth, int nVars ) +{ + unsigned uCofs[10][32]; + int i, k, nOnes; + + // total pairs + nOnes = Kit_TruthCountOnes( uCofs[0], nVars ); + printf( "Total = %d.\n", nOnes * ((1 << nVars) - nOnes) ); + + // print measures for individual variables + for ( i = 0; i < nVars; i++ ) + { + Kit_TruthUniqueNew( uCofs[0], pTruth, nVars, i ); + nOnes = Kit_TruthCountOnes( uCofs[0], nVars ); + printf( "%7d ", nOnes ); + } + printf( "\n" ); + + // consider pairs + for ( i = 0; i < nVars; i++ ) + for ( k = 0; k < nVars; k++ ) + { + if ( i == k ) + { + printf( " " ); + continue; + } + Kit_TruthCofactor0New( uCofs[0], pTruth, nVars, i ); + Kit_TruthCofactor1New( uCofs[1], pTruth, nVars, i ); + + Kit_TruthCofactor0New( uCofs[2], uCofs[0], nVars, k ); // 00 + Kit_TruthCofactor1New( uCofs[3], uCofs[0], nVars, k ); // 01 + Kit_TruthCofactor0New( uCofs[4], uCofs[1], nVars, k ); // 10 + Kit_TruthCofactor1New( uCofs[5], uCofs[1], nVars, k ); // 11 + + Kit_TruthAndPhase( uCofs[6], uCofs[2], uCofs[5], nVars, 0, 1 ); // 00 & 11' + Kit_TruthAndPhase( uCofs[7], uCofs[2], uCofs[5], nVars, 1, 0 ); // 00' & 11 + Kit_TruthAndPhase( uCofs[8], uCofs[3], uCofs[4], nVars, 0, 1 ); // 01 & 10' + Kit_TruthAndPhase( uCofs[9], uCofs[3], uCofs[4], nVars, 1, 0 ); // 01' & 10 + + nOnes = Kit_TruthCountOnes( uCofs[6], nVars ) + + Kit_TruthCountOnes( uCofs[7], nVars ) + + Kit_TruthCountOnes( uCofs[8], nVars ) + + Kit_TruthCountOnes( uCofs[9], nVars ); + + printf( "%7d ", nOnes ); + if ( k == nVars - 1 ) + printf( "\n" ); + } + printf( "\n" ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_Ntk4VarTable( Abc_Ntk_t * pNtk ) +{ + static u4VarTts[222] = { + 0x0000, 0x0001, 0x0003, 0x0006, 0x0007, 0x000f, 0x0016, 0x0017, 0x0018, 0x0019, + 0x001b, 0x001e, 0x001f, 0x003c, 0x003d, 0x003f, 0x0069, 0x006b, 0x006f, 0x007e, + 0x007f, 0x00ff, 0x0116, 0x0117, 0x0118, 0x0119, 0x011a, 0x011b, 0x011e, 0x011f, + 0x012c, 0x012d, 0x012f, 0x013c, 0x013d, 0x013e, 0x013f, 0x0168, 0x0169, 0x016a, + 0x016b, 0x016e, 0x016f, 0x017e, 0x017f, 0x0180, 0x0181, 0x0182, 0x0183, 0x0186, + 0x0187, 0x0189, 0x018b, 0x018f, 0x0196, 0x0197, 0x0198, 0x0199, 0x019a, 0x019b, + 0x019e, 0x019f, 0x01a8, 0x01a9, 0x01aa, 0x01ab, 0x01ac, 0x01ad, 0x01ae, 0x01af, + 0x01bc, 0x01bd, 0x01be, 0x01bf, 0x01e8, 0x01e9, 0x01ea, 0x01eb, 0x01ee, 0x01ef, + 0x01fe, 0x033c, 0x033d, 0x033f, 0x0356, 0x0357, 0x0358, 0x0359, 0x035a, 0x035b, + 0x035e, 0x035f, 0x0368, 0x0369, 0x036a, 0x036b, 0x036c, 0x036d, 0x036e, 0x036f, + 0x037c, 0x037d, 0x037e, 0x03c0, 0x03c1, 0x03c3, 0x03c5, 0x03c6, 0x03c7, 0x03cf, + 0x03d4, 0x03d5, 0x03d6, 0x03d7, 0x03d8, 0x03d9, 0x03db, 0x03dc, 0x03dd, 0x03de, + 0x03fc, 0x0660, 0x0661, 0x0662, 0x0663, 0x0666, 0x0667, 0x0669, 0x066b, 0x066f, + 0x0672, 0x0673, 0x0676, 0x0678, 0x0679, 0x067a, 0x067b, 0x067e, 0x0690, 0x0691, + 0x0693, 0x0696, 0x0697, 0x069f, 0x06b0, 0x06b1, 0x06b2, 0x06b3, 0x06b4, 0x06b5, + 0x06b6, 0x06b7, 0x06b9, 0x06bd, 0x06f0, 0x06f1, 0x06f2, 0x06f6, 0x06f9, 0x0776, + 0x0778, 0x0779, 0x077a, 0x077e, 0x07b0, 0x07b1, 0x07b4, 0x07b5, 0x07b6, 0x07bc, + 0x07e0, 0x07e1, 0x07e2, 0x07e3, 0x07e6, 0x07e9, 0x07f0, 0x07f1, 0x07f2, 0x07f8, + 0x0ff0, 0x1668, 0x1669, 0x166a, 0x166b, 0x166e, 0x167e, 0x1681, 0x1683, 0x1686, + 0x1687, 0x1689, 0x168b, 0x168e, 0x1696, 0x1697, 0x1698, 0x1699, 0x169a, 0x169b, + 0x169e, 0x16a9, 0x16ac, 0x16ad, 0x16bc, 0x16e9, 0x177e, 0x178e, 0x1796, 0x1798, + 0x179a, 0x17ac, 0x17e8, 0x18e7, 0x19e1, 0x19e3, 0x19e6, 0x1bd8, 0x1be4, 0x1ee1, + 0x3cc3, 0x6996 + }; + int Counters[222]; + + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj; + int i; + + // set elementary truth tables + Abc_NtkForEachPi( pNtk, pObj, i ) + pObj + + Abc_NtkForEachPo( pNtk, pObj, i ) + { + vNodes = Abc_NtkDfsNodes( pNtk, &pObj, i ); + Vec_PtrFree( vNodes ); + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/cmd/cmd.c b/abc_with_bb_support/src/base/cmd/cmd.c new file mode 100644 index 000000000..3487a8361 --- /dev/null +++ b/abc_with_bb_support/src/base/cmd/cmd.c @@ -0,0 +1,1674 @@ +/**CFile**************************************************************** + + FileName [cmd.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Command file.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cmd.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifdef WIN32 +#include +#endif + +#include "mainInt.h" +#include "cmdInt.h" +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int CmdCommandTime ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int CmdCommandEcho ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int CmdCommandQuit ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int CmdCommandWhich ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int CmdCommandHistory ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int CmdCommandAlias ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int CmdCommandUnalias ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int CmdCommandHelp ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int CmdCommandSource ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int CmdCommandSetVariable ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int CmdCommandUnsetVariable ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int CmdCommandUndo ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int CmdCommandRecall ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int CmdCommandEmpty ( Abc_Frame_t * pAbc, int argc, char ** argv ); +#ifdef WIN32 +static int CmdCommandLs ( Abc_Frame_t * pAbc, int argc, char ** argv ); +#endif +static int CmdCommandSis ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int CmdCommandMvsis ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int CmdCommandCapo ( Abc_Frame_t * pAbc, int argc, char ** argv ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function******************************************************************** + + Synopsis [Initializes the command package.] + + SideEffects [Commands are added to the command table.] + + SeeAlso [Cmd_End] + +******************************************************************************/ +void Cmd_Init( Abc_Frame_t * pAbc ) +{ + pAbc->tCommands = st_init_table(strcmp, st_strhash); + pAbc->tAliases = st_init_table(strcmp, st_strhash); + pAbc->tFlags = st_init_table(strcmp, st_strhash); + pAbc->aHistory = Vec_PtrAlloc( 100 ); + + Cmd_CommandAdd( pAbc, "Basic", "time", CmdCommandTime, 0); + Cmd_CommandAdd( pAbc, "Basic", "echo", CmdCommandEcho, 0); + Cmd_CommandAdd( pAbc, "Basic", "quit", CmdCommandQuit, 0); + Cmd_CommandAdd( pAbc, "Basic", "history", CmdCommandHistory, 0); + Cmd_CommandAdd( pAbc, "Basic", "alias", CmdCommandAlias, 0); + Cmd_CommandAdd( pAbc, "Basic", "unalias", CmdCommandUnalias, 0); + Cmd_CommandAdd( pAbc, "Basic", "help", CmdCommandHelp, 0); + Cmd_CommandAdd( pAbc, "Basic", "source", CmdCommandSource, 0); + Cmd_CommandAdd( pAbc, "Basic", "set", CmdCommandSetVariable, 0); + Cmd_CommandAdd( pAbc, "Basic", "unset", CmdCommandUnsetVariable, 0); + Cmd_CommandAdd( pAbc, "Basic", "undo", CmdCommandUndo, 0); + Cmd_CommandAdd( pAbc, "Basic", "recall", CmdCommandRecall, 0); + Cmd_CommandAdd( pAbc, "Basic", "empty", CmdCommandEmpty, 0); +#ifdef WIN32 + Cmd_CommandAdd( pAbc, "Basic", "ls", CmdCommandLs, 0 ); +#endif + + Cmd_CommandAdd( pAbc, "Various", "sis", CmdCommandSis, 1); + Cmd_CommandAdd( pAbc, "Various", "mvsis", CmdCommandMvsis, 1); + Cmd_CommandAdd( pAbc, "Various", "capo", CmdCommandCapo, 0); +} + +/**Function******************************************************************** + + Synopsis [Ends the command package.] + + Description [Ends the command package. Tables are freed.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void Cmd_End( Abc_Frame_t * pAbc ) +{ + st_generator * gen; + char * pKey, * pValue; + int i; + +// st_free_table( pAbc->tCommands, (void (*)()) 0, CmdCommandFree ); +// st_free_table( pAbc->tAliases, (void (*)()) 0, CmdCommandAliasFree ); +// st_free_table( pAbc->tFlags, free, free ); + + st_foreach_item( pAbc->tCommands, gen, (char **)&pKey, (char **)&pValue ) + CmdCommandFree( (Abc_Command *)pValue ); + st_free_table( pAbc->tCommands ); + + st_foreach_item( pAbc->tAliases, gen, (char **)&pKey, (char **)&pValue ) + CmdCommandAliasFree( (Abc_Alias *)pValue ); + st_free_table( pAbc->tAliases ); + + st_foreach_item( pAbc->tFlags, gen, (char **)&pKey, (char **)&pValue ) + free( pKey ), free( pValue ); + st_free_table( pAbc->tFlags ); + + for ( i = 0; i < pAbc->aHistory->nSize; i++ ) + free( pAbc->aHistory->pArray[i] ); + Vec_PtrFree( pAbc->aHistory ); +} + + + +/**Function******************************************************************** + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int CmdCommandTime( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + int c; + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( argc != globalUtilOptind ) + { + goto usage; + } + + pAbc->TimeTotal += pAbc->TimeCommand; + fprintf( pAbc->Out, "elapse: %3.2f seconds, total: %3.2f seconds\n", + (float)pAbc->TimeCommand / CLOCKS_PER_SEC, (float)pAbc->TimeTotal / CLOCKS_PER_SEC ); +/* + { + FILE * pTable; + pTable = fopen( "runtimes.txt", "a+" ); + fprintf( pTable, "%4.2f\n", (float)pAbc->TimeCommand / CLOCKS_PER_SEC ); + fclose( pTable ); + } +*/ + pAbc->TimeCommand = 0; + return 0; + + usage: + fprintf( pAbc->Err, "usage: time [-h]\n" ); + fprintf( pAbc->Err, " -h \t\tprint the command usage\n" ); + return 1; +} + +/**Function******************************************************************** + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int CmdCommandEcho( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + int i; + int c; + int n = 1; + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "hn" ) ) != EOF ) + { + switch ( c ) + { + case 'n': + n = 0; + break; + case 'h': + goto usage; + break; + default: + goto usage; + } + } + + for ( i = globalUtilOptind; i < argc; i++ ) + fprintf( pAbc->Out, "%s ", argv[i] ); + if ( n ) + fprintf( pAbc->Out, "\n" ); + else + fflush ( pAbc->Out ); + return 0; + + usage: + fprintf( pAbc->Err, "usage: echo [-h] string \n" ); + fprintf( pAbc->Err, " -n \t\tsuppress newline at the end\n" ); + fprintf( pAbc->Err, " -h \t\tprint the command usage\n" ); + return ( 1 ); +} + +/**Function******************************************************************** + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int CmdCommandQuit( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + int c; + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "hs" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + break; + case 's': + return -2; + break; + default: + goto usage; + } + } + + if ( argc != globalUtilOptind ) + goto usage; + return -1; + + usage: + fprintf( pAbc->Err, "usage: quit [-h] [-s]\n" ); + fprintf( pAbc->Err, " -h print the command usage\n" ); + fprintf( pAbc->Err, + " -s frees all the memory before quitting\n" ); + return 1; +} + +/**Function******************************************************************** + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int CmdCommandWhich( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + return 0; +} + +/**Function******************************************************************** + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int CmdCommandHistory( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + int i, c, num, size; + + num = 20; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default : + goto usage; + } + } + if ( argc > 2 ) + goto usage; + + // get the number of commands to print + if ( argc == globalUtilOptind + 1 ) + num = atoi(argv[globalUtilOptind]); + // print the commands + size = pAbc->aHistory->nSize; + num = ( num < size ) ? num : size; + for ( i = size - num; i < size; i++ ) + fprintf( pAbc->Out, "%s", pAbc->aHistory->pArray[i] ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: history [-h] \n" ); + fprintf( pAbc->Err, " prints the latest command entered on the command line\n" ); + fprintf( pAbc->Err, " -h : print the command usage\n" ); + fprintf( pAbc->Err, "num : print the last num commands\n" ); + return ( 1 ); +} + +/**Function******************************************************************** + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int CmdCommandAlias( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + char *key, *value; + int c; + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + break; + default: + goto usage; + } + } + + + if ( argc == 1 ) + { + CmdPrintTable( pAbc->tAliases, 1 ); + return 0; + + } + else if ( argc == 2 ) + { + if ( st_lookup( pAbc->tAliases, argv[1], &value ) ) + CmdCommandAliasPrint( pAbc, ( Abc_Alias * ) value ); + return 0; + } + + // delete any existing alias + key = argv[1]; + if ( st_delete( pAbc->tAliases, &key, &value ) ) + CmdCommandAliasFree( ( Abc_Alias * ) value ); + CmdCommandAliasAdd( pAbc, argv[1], argc - 2, argv + 2 ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: alias [-h] [command [string]]\n" ); + fprintf( pAbc->Err, " -h \t\tprint the command usage\n" ); + return ( 1 ); +} + +/**Function******************************************************************** + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int CmdCommandUnalias( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + int i; + char *key, *value; + int c; + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + break; + default: + goto usage; + } + } + + if ( argc < 2 ) + { + goto usage; + } + + for ( i = 1; i < argc; i++ ) + { + key = argv[i]; + if ( st_delete( pAbc->tAliases, &key, &value ) ) + { + CmdCommandAliasFree( ( Abc_Alias * ) value ); + } + } + return 0; + + usage: + fprintf( pAbc->Err, "usage: unalias [-h] alias_names\n" ); + fprintf( pAbc->Err, " -h \t\tprint the command usage\n" ); + return 1; +} + +/**Function******************************************************************** + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int CmdCommandHelp( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + bool fPrintAll; + int c; + + fPrintAll = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "ah" ) ) != EOF ) + { + switch ( c ) + { + case 'a': + case 'v': + fPrintAll ^= 1; + break; + break; + case 'h': + goto usage; + break; + default: + goto usage; + } + } + + if ( argc != globalUtilOptind ) + goto usage; + + CmdCommandPrint( pAbc, fPrintAll ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: help [-a] [-h]\n" ); + fprintf( pAbc->Err, " prints the list of available commands by group\n" ); + fprintf( pAbc->Err, " -a toggle printing hidden commands [default = %s]\n", fPrintAll? "yes": "no" ); + fprintf( pAbc->Err, " -h print the command usage\n" ); + return 1; +} + +/**Function******************************************************************** + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int CmdCommandSource( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + int c, echo, prompt, silent, interactive, quit_count, lp_count; + int status = 0; /* initialize so that lint doesn't complain */ + int lp_file_index, did_subst; + char *prompt_string, *real_filename, line[MAX_STR], *command; + FILE *fp; + + interactive = silent = prompt = echo = 0; + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "ipsxh" ) ) != EOF ) + { + switch ( c ) + { + case 'i': /* a hack to distinguish EOF from stdin */ + interactive = 1; + break; + case 'p': + prompt ^= 1; + break; + case 's': + silent ^= 1; + break; + case 'x': + echo ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + /* added to avoid core-dumping when no script file is specified */ + if ( argc == globalUtilOptind ) + { + goto usage; + } + + lp_file_index = globalUtilOptind; + lp_count = 0; + + /* + * FIX (Tom, 5/7/95): I'm not sure what the purpose of this outer do loop + * is. In particular, lp_file_index is never modified in the loop, so it + * looks it would just read the same file over again. Also, SIS had + * lp_count initialized to -1, and hence, any file sourced by SIS (if -l or + * -t options on "source" were used in SIS) would actually be executed + * twice. + */ + do + { + lp_count++; /* increment the loop counter */ + + fp = CmdFileOpen( pAbc, argv[lp_file_index], "r", &real_filename, silent ); + if ( fp == NULL ) + { + FREE( real_filename ); + return !silent; /* error return if not silent */ + } + + quit_count = 0; + do + { + if ( prompt ) + { + prompt_string = Cmd_FlagReadByName( pAbc, "prompt" ); + if ( prompt_string == NULL ) + prompt_string = "abc> "; + + } + else + { + prompt_string = NULL; + } + + /* clear errors -- e.g., EOF reached from stdin */ + clearerr( fp ); + + /* read another command line */ +// if (CmdFgetsFilec(line, MAX_STR, fp, prompt_string) == NULL) { +// Abc_UtilsPrintPrompt(prompt_string); +// fflush(stdout); + if ( fgets( line, MAX_STR, fp ) == NULL ) + { + if ( interactive ) + { + if ( quit_count++ < 5 ) + { + fprintf( pAbc->Err, "\nUse \"quit\" to leave ABC.\n" ); + continue; + } + status = -1; /* fake a 'quit' */ + } + else + { + status = 0; /* successful end of 'source' ; loop? */ + } + break; + } + quit_count = 0; + + if ( echo ) + { + fprintf( pAbc->Out, "abc - > %s", line ); + } + command = CmdHistorySubstitution( pAbc, line, &did_subst ); + if ( command == NULL ) + { + status = 1; + break; + } + if ( did_subst ) + { + if ( interactive ) + { + fprintf( pAbc->Out, "%s\n", command ); + } + } + if ( command != line ) + { + ( void ) strcpy( line, command ); + } + if ( interactive && *line != '\0' ) + { + Cmd_HistoryAddCommand( pAbc, Extra_UtilStrsav(line) ); + if ( pAbc->Hst != NULL ) + { + fprintf( pAbc->Hst, "%s\n", line ); + ( void ) fflush( pAbc->Hst ); + } + } + + status = Cmd_CommandExecute( pAbc, line ); + } + while ( status == 0 ); + + if ( fp != stdin ) + { + if ( status > 0 ) + { + fprintf( pAbc->Err, + "** cmd error: aborting 'source %s'\n", + real_filename ); + } + ( void ) fclose( fp ); + } + FREE( real_filename ); + + } + while ( ( status == 0 ) && ( lp_count <= 0 ) ); + + return status; + + usage: + fprintf( pAbc->Err, "usage: source [-psxh] \n" ); + fprintf( pAbc->Err, "\t-p supply prompt before reading each line [default = %s]\n", prompt? "yes": "no" ); + fprintf( pAbc->Err, "\t-s silently ignore nonexistant file [default = %s]\n", silent? "yes": "no" ); + fprintf( pAbc->Err, "\t-x echo each line as it is executed [default = %s]\n", echo? "yes": "no" ); + fprintf( pAbc->Err, "\t-h print the command usage\n" ); + return 1; +} + +/**Function******************************************************************** + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int CmdCommandSetVariable( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + char *flag_value, *key, *value; + int c; + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc == 0 || argc > 3 ) + { + goto usage; + } + else if ( argc == 1 ) + { + CmdPrintTable( pAbc->tFlags, 0 ); + return 0; + } + else + { + key = argv[1]; + if ( st_delete( pAbc->tFlags, &key, &value ) ) + { + FREE( key ); + FREE( value ); + } + + flag_value = argc == 2 ? Extra_UtilStrsav( "" ) : Extra_UtilStrsav( argv[2] ); +// flag_value = argc == 2 ? NULL : Extra_UtilStrsav(argv[2]); + st_insert( pAbc->tFlags, Extra_UtilStrsav(argv[1]), flag_value ); + + if ( strcmp( argv[1], "abcout" ) == 0 ) + { + if ( pAbc->Out != stdout ) + fclose( pAbc->Out ); + if ( strcmp( flag_value, "" ) == 0 ) + flag_value = "-"; + pAbc->Out = CmdFileOpen( pAbc, flag_value, "w", NULL, 0 ); + if ( pAbc->Out == NULL ) + pAbc->Out = stdout; +#if HAVE_SETVBUF + setvbuf( pAbc->Out, ( char * ) NULL, _IOLBF, 0 ); +#endif + } + if ( strcmp( argv[1], "abcerr" ) == 0 ) + { + if ( pAbc->Err != stderr ) + fclose( pAbc->Err ); + if ( strcmp( flag_value, "" ) == 0 ) + flag_value = "-"; + pAbc->Err = CmdFileOpen( pAbc, flag_value, "w", NULL, 0 ); + if ( pAbc->Err == NULL ) + pAbc->Err = stderr; +#if HAVE_SETVBUF + setvbuf( pAbc->Err, ( char * ) NULL, _IOLBF, 0 ); +#endif + } + if ( strcmp( argv[1], "history" ) == 0 ) + { + if ( pAbc->Hst != NULL ) + fclose( pAbc->Hst ); + if ( strcmp( flag_value, "" ) == 0 ) + pAbc->Hst = NULL; + else + { + pAbc->Hst = CmdFileOpen( pAbc, flag_value, "w", NULL, 0 ); + if ( pAbc->Hst == NULL ) + pAbc->Hst = NULL; + } + } + return 0; + } + + usage: + fprintf( pAbc->Err, "usage: set [-h] \n" ); + fprintf( pAbc->Err, "\t sets the value of parameter \n" ); + fprintf( pAbc->Err, "\t-h : print the command usage\n" ); + return 1; + +} + +/**Function******************************************************************** + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int CmdCommandUnsetVariable( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + int i; + char *key, *value; + int c; + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + break; + default: + goto usage; + } + } + + if ( argc < 2 ) + { + goto usage; + } + + for ( i = 1; i < argc; i++ ) + { + key = argv[i]; + if ( st_delete( pAbc->tFlags, &key, &value ) ) + { + FREE( key ); + FREE( value ); + } + } + return 0; + + + usage: + fprintf( pAbc->Err, "usage: unset [-h] \n" ); + fprintf( pAbc->Err, "\t removes the value of parameter \n" ); + fprintf( pAbc->Err, "\t-h : print the command usage\n" ); + return 1; +} + +/**Function******************************************************************** + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int CmdCommandUndo( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + if ( pAbc->pNtkCur == NULL ) + { + fprintf( pAbc->Out, "Empty network.\n" ); + return 0; + } + + // if there are no arguments on the command line + // set the current network to be the network from the previous step + if ( argc == 1 ) + return CmdCommandRecall( pAbc, argc, argv ); + + fprintf( pAbc->Err, "usage: undo\n" ); + fprintf( pAbc->Err, " sets the current network to be the previously saved network\n" ); + return 1; + +} + +/**Function******************************************************************** + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int CmdCommandRecall( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + Abc_Ntk_t * pNtk; + int iStep, iStepFound; + int nNetsToSave, c; + char * pValue; + int iStepStart, iStepStop; + + if ( pAbc->pNtkCur == NULL ) + { + fprintf( pAbc->Out, "Empty network.\n" ); + return 0; + } + + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + // get the number of networks to save + pValue = Cmd_FlagReadByName( pAbc, "savesteps" ); + // if the value of steps to save is not set, assume 1-level undo + if ( pValue == NULL ) + nNetsToSave = 1; + else + nNetsToSave = atoi(pValue); + + // if there are no arguments on the command line + // set the current network to be the network from the previous step + if ( argc == 1 ) + { + // get the previously saved network + pNtk = Abc_NtkBackup(pAbc->pNtkCur); + if ( pNtk == NULL ) + fprintf( pAbc->Out, "There is no previously saved network.\n" ); + else // set the current network to be the copy of the previous one + Abc_FrameSetCurrentNetwork( pAbc, Abc_NtkDup(pNtk) ); + return 0; + } + if ( argc == 2 ) // the second argument is the number of the step to return to + { + // read the number of the step to return to + iStep = atoi(argv[1]); + // check whether it is reasonable + if ( iStep >= pAbc->nSteps ) + { + iStepStart = pAbc->nSteps - nNetsToSave; + if ( iStepStart <= 0 ) + iStepStart = 1; + iStepStop = pAbc->nSteps; + if ( iStepStop <= 0 ) + iStepStop = 1; + if ( iStepStart == iStepStop ) + fprintf( pAbc->Out, "Can only recall step %d.\n", iStepStop ); + else + fprintf( pAbc->Out, "Can only recall steps %d-%d.\n", iStepStart, iStepStop ); + } + else if ( iStep < 0 ) + fprintf( pAbc->Out, "Cannot recall step %d.\n", iStep ); + else if ( iStep == 0 ) + Abc_FrameDeleteAllNetworks( pAbc ); + else + { + // scroll backward through the list of networks + // to determine if such a network exist + iStepFound = 0; + for ( pNtk = pAbc->pNtkCur; pNtk; pNtk = Abc_NtkBackup(pNtk) ) + if ( (iStepFound = Abc_NtkStep(pNtk)) == iStep ) + break; + if ( pNtk == NULL ) + { + iStepStart = iStepFound; + if ( iStepStart <= 0 ) + iStepStart = 1; + iStepStop = pAbc->nSteps; + if ( iStepStop <= 0 ) + iStepStop = 1; + if ( iStepStart == iStepStop ) + fprintf( pAbc->Out, "Can only recall step %d.\n", iStepStop ); + else + fprintf( pAbc->Out, "Can only recall steps %d-%d.\n", iStepStart, iStepStop ); + } + else + Abc_FrameSetCurrentNetwork( pAbc, Abc_NtkDup(pNtk) ); + } + return 0; + } + +usage: + + fprintf( pAbc->Err, "usage: recall -h \n" ); + fprintf( pAbc->Err, " set the current network to be one of the previous networks\n" ); + fprintf( pAbc->Err, " : level to return to [default = previous]\n" ); + fprintf( pAbc->Err, " -h : print the command usage\n"); + return 1; +} + + +/**Function******************************************************************** + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int CmdCommandEmpty( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + int c; + + if ( pAbc->pNtkCur == NULL ) + { + fprintf( pAbc->Out, "Empty network.\n" ); + return 0; + } + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + + Abc_FrameDeleteAllNetworks( pAbc ); + Abc_FrameRestart( pAbc ); + return 0; +usage: + + fprintf( pAbc->Err, "usage: empty [-h]\n" ); + fprintf( pAbc->Err, " removes all the currently stored networks\n" ); + fprintf( pAbc->Err, " -h : print the command usage\n"); + return 1; +} + + +#if 0 + +/**Function******************************************************************** + + Synopsis [Donald's version.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int CmdCommandUndo( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + Abc_Ntk_t * pNtkTemp; + int id, c; + + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + break; + default: + goto usage; + } + } + if (globalUtilOptind <= argc) { + pNtkTemp = pAbc->pNtk; + pAbc->pNtk = pAbc->pNtkSaved; + pAbc->pNtkSaved = pNtkTemp; + } + id = atoi(argv[globalUtilOptind]); + pNtkTemp = Cmd_HistoryGetSnapshot(pAbc, id); + if (!pNtkTemp) + fprintf( pAbc->Err, "Snapshot %d does not exist\n", id); + else + pAbc->pNtk = Abc_NtkDup(pNtkTemp, Abc_NtkMan(pNtkTemp)); + + return 0; +usage: + fprintf( pAbc->Err, "usage: undo\n" ); + fprintf( pAbc->Err, " swaps the current network and the backup network\n" ); + return 1; +} + +#endif + + +#ifdef WIN32 +/**Function************************************************************* + + Synopsis [Command to print the contents of the current directory (Windows).] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +#include + +// these structures are defined in but are for some reason invisible +typedef unsigned long _fsize_t; // Could be 64 bits for Win32 + +struct _finddata_t { + unsigned attrib; + time_t time_create; // -1 for FAT file systems + time_t time_access; // -1 for FAT file systems + time_t time_write; + _fsize_t size; + char name[260]; +}; + +extern long _findfirst( char *filespec, struct _finddata_t *fileinfo ); +extern int _findnext( long handle, struct _finddata_t *fileinfo ); +extern int _findclose( long handle ); + +int CmdCommandLs( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + struct _finddata_t c_file; + long hFile; + int fLong = 0; + int fOnlyBLIF = 0; + char Buffer[25]; + int Counter = 0; + int fPrintedNewLine; + char c; + + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "lb") ) != EOF ) + { + switch (c) + { + case 'l': + fLong = 1; + break; + case 'b': + fOnlyBLIF = 1; + break; + default: + goto usage; + } + } + + // find first .mv file in current directory + if( (hFile = _findfirst( ((fOnlyBLIF)? "*.mv": "*.*"), &c_file )) == -1L ) + { + if ( fOnlyBLIF ) + fprintf( pAbc->Out, "No *.mv files in the current directory.\n" ); + else + fprintf( pAbc->Out, "No files in the current directory.\n" ); + } + else + { + if ( fLong ) + { + fprintf( pAbc->Out, " File Date Size | File Date Size \n" ); + fprintf( pAbc->Out, " ----------------------------------------------------------------------------- \n" ); + do + { + strcpy( Buffer, ctime( &(c_file.time_write) ) ); + Buffer[16] = 0; + fprintf( pAbc->Out, " %-17s %.24s%7ld", c_file.name, Buffer+4, c_file.size ); + if ( ++Counter % 2 == 0 ) + { + fprintf( pAbc->Out, "\n" ); + fPrintedNewLine = 1; + } + else + { + fprintf( pAbc->Out, " |" ); + fPrintedNewLine = 0; + } + } + while( _findnext( hFile, &c_file ) == 0 ); + } + else + { + do + { + fprintf( pAbc->Out, " %-18s", c_file.name ); + if ( ++Counter % 4 == 0 ) + { + fprintf( pAbc->Out, "\n" ); + fPrintedNewLine = 1; + } + else + { + fprintf( pAbc->Out, " " ); + fPrintedNewLine = 0; + } + } + while( _findnext( hFile, &c_file ) == 0 ); + } + if ( !fPrintedNewLine ) + fprintf( pAbc->Out, "\n" ); + _findclose( hFile ); + } + return 0; + +usage: + fprintf( pAbc->Err, "Usage: ls [-l] [-b]\n" ); + fprintf( pAbc->Err, " print the file names in the current directory\n" ); + fprintf( pAbc->Err, " -l : print in the long format [default = short]\n" ); + fprintf( pAbc->Err, " -b : print only .mv files [default = all]\n" ); + return 1; +} +#endif + + + +#ifdef WIN32 +#define unlink _unlink +#endif + +/**Function******************************************************************** + + Synopsis [Calls SIS internally.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int CmdCommandSis( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + FILE * pFile; + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkNew, * pNetlist; + char * pNameWin = "sis.exe"; + char * pNameUnix = "sis"; + char Command[1000], Buffer[100]; + char * pSisName; + int i; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + goto usage; + } + + if ( strcmp( argv[0], "sis" ) != 0 ) + { + fprintf( pErr, "Wrong command: \"%s\".\n", argv[0] ); + goto usage; + } + + if ( argc == 1 ) + goto usage; + if ( strcmp( argv[1], "-h" ) == 0 ) + goto usage; + if ( strcmp( argv[1], "-?" ) == 0 ) + goto usage; + + // get the names from the resource file + if ( Cmd_FlagReadByName(pAbc, "siswin") ) + pNameWin = Cmd_FlagReadByName(pAbc, "siswin"); + if ( Cmd_FlagReadByName(pAbc, "sisunix") ) + pNameUnix = Cmd_FlagReadByName(pAbc, "sisunix"); + + // check if SIS is available + if ( (pFile = fopen( pNameWin, "r" )) ) + pSisName = pNameWin; + else if ( (pFile = fopen( pNameUnix, "r" )) ) + pSisName = pNameUnix; + else if ( pFile == NULL ) + { + fprintf( pErr, "Cannot find \"%s\" or \"%s\" in the current directory.\n", pNameWin, pNameUnix ); + goto usage; + } + fclose( pFile ); + + if ( Abc_NtkIsMappedLogic(pNtk) ) + { + Abc_NtkMapToSop(pNtk); + printf( "The current network is unmapped before calling SIS.\n" ); + } + + // write out the current network + if ( Abc_NtkIsLogic(pNtk) ) + Abc_NtkToSop(pNtk, 0); + pNetlist = Abc_NtkToNetlist(pNtk); + if ( pNetlist == NULL ) + { + fprintf( pErr, "Cannot produce the intermediate network.\n" ); + goto usage; + } + Io_WriteBlif( pNetlist, "_sis_in.blif", 1 ); + Abc_NtkDelete( pNetlist ); + + // create the file for sis + sprintf( Command, "%s -x -c ", pSisName ); + strcat ( Command, "\"" ); + strcat ( Command, "read_blif _sis_in.blif" ); + strcat ( Command, "; " ); + for ( i = 1; i < argc; i++ ) + { + sprintf( Buffer, " %s", argv[i] ); + strcat( Command, Buffer ); + } + strcat( Command, "; " ); + strcat( Command, "write_blif _sis_out.blif" ); + strcat( Command, "\"" ); + + // call SIS + if ( system( Command ) ) + { + fprintf( pErr, "The following command has returned non-zero exit status:\n" ); + fprintf( pErr, "\"%s\"\n", Command ); + unlink( "_sis_in.blif" ); + goto usage; + } + + // read in the SIS output + if ( (pFile = fopen( "_sis_out.blif", "r" )) == NULL ) + { + fprintf( pErr, "Cannot open SIS output file \"%s\".\n", "_sis_out.blif" ); + unlink( "_sis_in.blif" ); + goto usage; + } + fclose( pFile ); + + // set the new network + pNtkNew = Io_Read( "_sis_out.blif", IO_FILE_BLIF, 1 ); + // set the original spec of the new network + if ( pNtk->pSpec ) + { + FREE( pNtkNew->pSpec ); + pNtkNew->pSpec = Extra_UtilStrsav( pNtk->pSpec ); + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkNew ); + + // remove temporary networks + unlink( "_sis_in.blif" ); + unlink( "_sis_out.blif" ); + return 0; + +usage: + fprintf( pErr, "\n" ); + fprintf( pErr, "Usage: sis [-h] \n"); + fprintf( pErr, " invokes SIS command for the current ABC network\n" ); + fprintf( pErr, " (the executable of SIS should be in the same directory)\n" ); + fprintf( pErr, " -h : print the command usage\n" ); + fprintf( pErr, " : a SIS command (or a semicolon-separated list of commands in quotes)\n" ); + fprintf( pErr, " Example 1: sis eliminate 0\n" ); + fprintf( pErr, " Example 2: sis \"ps; rd; fx; ps\"\n" ); + fprintf( pErr, " Example 3: sis source script.rugged\n" ); + return 1; // error exit +} + + +/**Function******************************************************************** + + Synopsis [Calls SIS internally.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int CmdCommandMvsis( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + FILE * pFile; + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNtkNew, * pNetlist; + char Command[1000], Buffer[100]; + char * pNameWin = "mvsis.exe"; + char * pNameUnix = "mvsis"; + char * pMvsisName; + int i; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + goto usage; + } + + if ( strcmp( argv[0], "mvsis" ) != 0 ) + { + fprintf( pErr, "Wrong command: \"%s\".\n", argv[0] ); + goto usage; + } + + if ( argc == 1 ) + goto usage; + if ( strcmp( argv[1], "-h" ) == 0 ) + goto usage; + if ( strcmp( argv[1], "-?" ) == 0 ) + goto usage; + + // get the names from the resource file + if ( Cmd_FlagReadByName(pAbc, "mvsiswin") ) + pNameWin = Cmd_FlagReadByName(pAbc, "mvsiswin"); + if ( Cmd_FlagReadByName(pAbc, "mvsisunix") ) + pNameUnix = Cmd_FlagReadByName(pAbc, "mvsisunix"); + + // check if MVSIS is available + if ( (pFile = fopen( pNameWin, "r" )) ) + pMvsisName = pNameWin; + else if ( (pFile = fopen( pNameUnix, "r" )) ) + pMvsisName = pNameUnix; + else if ( pFile == NULL ) + { + fprintf( pErr, "Cannot find \"%s\" or \"%s\" in the current directory.\n", pNameWin, pNameUnix ); + goto usage; + } + fclose( pFile ); + + if ( Abc_NtkIsMappedLogic(pNtk) ) + { + Abc_NtkMapToSop(pNtk); + printf( "The current network is unmapped before calling MVSIS.\n" ); + } + + // write out the current network + if ( Abc_NtkIsLogic(pNtk) ) + Abc_NtkToSop(pNtk, 0); + pNetlist = Abc_NtkToNetlist(pNtk); + if ( pNetlist == NULL ) + { + fprintf( pErr, "Cannot produce the intermediate network.\n" ); + goto usage; + } + Io_WriteBlif( pNetlist, "_mvsis_in.blif", 1 ); + Abc_NtkDelete( pNetlist ); + + // create the file for MVSIS + sprintf( Command, "%s -x -c ", pMvsisName ); + strcat ( Command, "\"" ); + strcat ( Command, "read_blif _mvsis_in.blif" ); + strcat ( Command, "; " ); + for ( i = 1; i < argc; i++ ) + { + sprintf( Buffer, " %s", argv[i] ); + strcat( Command, Buffer ); + } + strcat( Command, "; " ); + strcat( Command, "write_blif _mvsis_out.blif" ); + strcat( Command, "\"" ); + + // call MVSIS + if ( system( Command ) ) + { + fprintf( pErr, "The following command has returned non-zero exit status:\n" ); + fprintf( pErr, "\"%s\"\n", Command ); + unlink( "_mvsis_in.blif" ); + goto usage; + } + + // read in the MVSIS output + if ( (pFile = fopen( "_mvsis_out.blif", "r" )) == NULL ) + { + fprintf( pErr, "Cannot open MVSIS output file \"%s\".\n", "_mvsis_out.blif" ); + unlink( "_mvsis_in.blif" ); + goto usage; + } + fclose( pFile ); + + // set the new network + pNtkNew = Io_Read( "_mvsis_out.blif", IO_FILE_BLIF, 1 ); + // set the original spec of the new network + if ( pNtk->pSpec ) + { + FREE( pNtkNew->pSpec ); + pNtkNew->pSpec = Extra_UtilStrsav( pNtk->pSpec ); + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkNew ); + + // remove temporary networks + unlink( "_mvsis_in.blif" ); + unlink( "_mvsis_out.blif" ); + return 0; + +usage: + fprintf( pErr, "\n" ); + fprintf( pErr, "Usage: mvsis [-h] \n"); + fprintf( pErr, " invokes MVSIS command for the current ABC network\n" ); + fprintf( pErr, " (the executable of MVSIS should be in the same directory)\n" ); + fprintf( pErr, " -h : print the command usage\n" ); + fprintf( pErr, " : a MVSIS command (or a semicolon-separated list of commands in quotes)\n" ); + fprintf( pErr, " Example 1: mvsis fraig_sweep\n" ); + fprintf( pErr, " Example 2: mvsis \"ps; fxu; ps\"\n" ); + fprintf( pErr, " Example 3: mvsis source mvsis.rugged\n" ); + return 1; // error exit +} + + +/**Function******************************************************************** + + Synopsis [Calls Capo internally.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int CmdCommandCapo( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + FILE * pFile; + FILE * pOut, * pErr; + Abc_Ntk_t * pNtk, * pNetlist; + char Command[1000], Buffer[100]; + char * pProgNameCapoWin = "capo.exe"; + char * pProgNameCapoUnix = "capo"; + char * pProgNameGnuplotWin = "wgnuplot.exe"; + char * pProgNameGnuplotUnix = "gnuplot"; + char * pProgNameCapo; + char * pProgNameGnuplot; + char * pPlotFileName; + int i; + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + goto usage; + } + + if ( strcmp( argv[0], "capo" ) != 0 ) + { + fprintf( pErr, "Wrong command: \"%s\".\n", argv[0] ); + goto usage; + } + + if ( argc > 1 ) + { + if ( strcmp( argv[1], "-h" ) == 0 ) + goto usage; + if ( strcmp( argv[1], "-?" ) == 0 ) + goto usage; + } + + // get the names from the resource file + if ( Cmd_FlagReadByName(pAbc, "capowin") ) + pProgNameCapoWin = Cmd_FlagReadByName(pAbc, "capowin"); + if ( Cmd_FlagReadByName(pAbc, "capounix") ) + pProgNameCapoUnix = Cmd_FlagReadByName(pAbc, "capounix"); + + // check if capo is available + if ( (pFile = fopen( pProgNameCapoWin, "r" )) ) + pProgNameCapo = pProgNameCapoWin; + else if ( (pFile = fopen( pProgNameCapoUnix, "r" )) ) + pProgNameCapo = pProgNameCapoUnix; + else if ( pFile == NULL ) + { + fprintf( pErr, "Cannot find \"%s\" or \"%s\" in the current directory.\n", pProgNameCapoWin, pProgNameCapoUnix ); + goto usage; + } + fclose( pFile ); + + if ( Abc_NtkIsMappedLogic(pNtk) ) + { + Abc_NtkMapToSop(pNtk); + printf( "The current network is unmapped before calling Capo.\n" ); + } + + // write out the current network + if ( Abc_NtkIsLogic(pNtk) ) + Abc_NtkToSop(pNtk, 0); + pNetlist = Abc_NtkToNetlist(pNtk); + if ( pNetlist == NULL ) + { + fprintf( pErr, "Cannot produce the intermediate network.\n" ); + goto usage; + } + Io_WriteBlif( pNetlist, "_capo_in.blif", 1 ); + Abc_NtkDelete( pNetlist ); + + // create the file for Capo + sprintf( Command, "%s -f _capo_in.blif -log out.txt ", pProgNameCapo ); + pPlotFileName = NULL; + for ( i = 1; i < argc; i++ ) + { + sprintf( Buffer, " %s", argv[i] ); + strcat( Command, Buffer ); + if ( !strcmp( argv[i], "-plot" ) ) + pPlotFileName = argv[i+1]; + } + + // call Capo + if ( system( Command ) ) + { + fprintf( pErr, "The following command has returned non-zero exit status:\n" ); + fprintf( pErr, "\"%s\"\n", Command ); + unlink( "_capo_in.blif" ); + goto usage; + } + // remove temporary networks + unlink( "_capo_in.blif" ); + if ( pPlotFileName == NULL ) + return 0; + + // get the file name + sprintf( Buffer, "%s.plt", pPlotFileName ); + pPlotFileName = Buffer; + + // read in the Capo plotting output + if ( (pFile = fopen( pPlotFileName, "r" )) == NULL ) + { + fprintf( pErr, "Cannot open the plot file \"%s\".\n\n", pPlotFileName ); + goto usage; + } + fclose( pFile ); + + // get the names from the plotting software + if ( Cmd_FlagReadByName(pAbc, "gnuplotwin") ) + pProgNameGnuplotWin = Cmd_FlagReadByName(pAbc, "gnuplotwin"); + if ( Cmd_FlagReadByName(pAbc, "gnuplotunix") ) + pProgNameGnuplotUnix = Cmd_FlagReadByName(pAbc, "gnuplotunix"); + + // check if Gnuplot is available + if ( (pFile = fopen( pProgNameGnuplotWin, "r" )) ) + pProgNameGnuplot = pProgNameGnuplotWin; + else if ( (pFile = fopen( pProgNameGnuplotUnix, "r" )) ) + pProgNameGnuplot = pProgNameGnuplotUnix; + else if ( pFile == NULL ) + { + fprintf( pErr, "Cannot find \"%s\" or \"%s\" in the current directory.\n", pProgNameGnuplotWin, pProgNameGnuplotUnix ); + goto usage; + } + fclose( pFile ); + + // spawn the viewer +#ifdef WIN32 + if ( _spawnl( _P_NOWAIT, pProgNameGnuplot, pProgNameGnuplot, pPlotFileName, NULL ) == -1 ) + { + fprintf( stdout, "Cannot find \"%s\".\n", pProgNameGnuplot ); + goto usage; + } +#else + { + sprintf( Command, "%s %s ", pProgNameGnuplot, pPlotFileName ); + if ( system( Command ) == -1 ) + { + fprintf( stdout, "Cannot execute \"%s\".\n", Command ); + goto usage; + } + } +#endif + + // remove temporary networks +// unlink( pPlotFileName ); + return 0; + +usage: + fprintf( pErr, "\n" ); + fprintf( pErr, "Usage: capo [-h] \n"); + fprintf( pErr, " peforms placement of the current network using Capo\n" ); + fprintf( pErr, " a Capo binary should be present in the same directory\n" ); + fprintf( pErr, " (if plotting, the Gnuplot binary should also be present)\n" ); + fprintf( pErr, " -h : print the command usage\n" ); + fprintf( pErr, " : a Capo command\n" ); + fprintf( pErr, " Example 1: capo\n" ); + fprintf( pErr, " (performs placement with default options)\n" ); + fprintf( pErr, " Example 2: capo -AR -WS -save\n" ); + fprintf( pErr, " (specifies the aspect ratio [default = 1.0] and\n" ); + fprintf( pErr, " the whitespace percentage [0%%; 100%%) [default = 15%%])\n" ); + fprintf( pErr, " Example 3: capo -plot \n" ); + fprintf( pErr, " (produces and visualize it using Gnuplot)\n" ); + fprintf( pErr, " Example 4: capo -help\n" ); + fprintf( pErr, " (prints the default usage message of the Capo binary)\n" ); + fprintf( pErr, " Please refer to the Capo webpage for additional information:\n" ); + fprintf( pErr, " http://vlsicad.eecs.umich.edu/BK/PDtools/\n" ); + return 1; // error exit +} + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/cmd/cmd.h b/abc_with_bb_support/src/base/cmd/cmd.h new file mode 100644 index 000000000..59cf115ec --- /dev/null +++ b/abc_with_bb_support/src/base/cmd/cmd.h @@ -0,0 +1,73 @@ +/**CFile**************************************************************** + + FileName [cmd.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [External declarations of the command package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cmd.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __CMD_H__ +#define __CMD_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct MvCommand Abc_Command; // one command +typedef struct MvAlias Abc_Alias; // one alias + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== cmd.c ===========================================================*/ +extern void Cmd_Init(); +extern void Cmd_End(); +/*=== cmdApi.c ========================================================*/ +extern void Cmd_CommandAdd( Abc_Frame_t * pAbc, char * sGroup, char * sName, void * pFunc, int fChanges ); +extern int Cmd_CommandExecute( Abc_Frame_t * pAbc, char * sCommand ); +/*=== cmdFlag.c ========================================================*/ +extern char * Cmd_FlagReadByName( Abc_Frame_t * pAbc, char * flag ); +extern void Cmd_FlagDeleteByName( Abc_Frame_t * pAbc, char * key ); +extern void Cmd_FlagUpdateValue( Abc_Frame_t * pAbc, char * key, char * value ); +/*=== cmdHist.c ========================================================*/ +extern void Cmd_HistoryAddCommand( Abc_Frame_t * pAbc, char * command ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/base/cmd/cmdAlias.c b/abc_with_bb_support/src/base/cmd/cmdAlias.c new file mode 100644 index 000000000..5492ad5cc --- /dev/null +++ b/abc_with_bb_support/src/base/cmd/cmdAlias.c @@ -0,0 +1,120 @@ +/**CFile**************************************************************** + + FileName [cmdAlias.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures dealing with aliases in the command package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cmdAlias.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cmdInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void CmdCommandAliasAdd( Abc_Frame_t * pAbc, char * sName, int argc, char ** argv ) +{ + Abc_Alias * pAlias; + int fStatus, i; + + pAlias = ALLOC(Abc_Alias, 1); + pAlias->sName = Extra_UtilStrsav(sName); + pAlias->argc = argc; + pAlias->argv = ALLOC(char *, pAlias->argc); + for(i = 0; i < argc; i++) + pAlias->argv[i] = Extra_UtilStrsav(argv[i]); + fStatus = st_insert( pAbc->tAliases, pAlias->sName, (char *) pAlias ); + assert(!fStatus); +} + +/**Function******************************************************************** + + Synopsis [required] + + Description [optional] + + SideEffects [required] + + SeeAlso [optional] + +******************************************************************************/ +void CmdCommandAliasPrint( Abc_Frame_t * pAbc, Abc_Alias * pAlias ) +{ + int i; + fprintf(pAbc->Out, "%-15s", pAlias->sName); + for(i = 0; i < pAlias->argc; i++) + fprintf( pAbc->Out, " %s", pAlias->argv[i] ); + fprintf( pAbc->Out, "\n" ); +} + +/**Function******************************************************************** + + Synopsis [required] + + Description [optional] + + SideEffects [required] + + SeeAlso [optional] + +******************************************************************************/ +char * CmdCommandAliasLookup( Abc_Frame_t * pAbc, char * sCommand ) +{ + Abc_Alias * pAlias; + char * value; + if (!st_lookup( pAbc->tAliases, sCommand, &value)) + return sCommand; + pAlias = (Abc_Alias *) value; + return pAlias->argv[0]; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void CmdCommandAliasFree( Abc_Alias * pAlias ) +{ + CmdFreeArgv( pAlias->argc, pAlias->argv ); + FREE(pAlias->sName); + FREE(pAlias); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/cmd/cmdApi.c b/abc_with_bb_support/src/base/cmd/cmdApi.c new file mode 100644 index 000000000..69998ef70 --- /dev/null +++ b/abc_with_bb_support/src/base/cmd/cmdApi.c @@ -0,0 +1,104 @@ +/**CFile**************************************************************** + + FileName [cmdApi.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [External procedures of the command package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cmdApi.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "mainInt.h" +#include "cmdInt.h" +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cmd_CommandAdd( Abc_Frame_t * pAbc, char * sGroup, char * sName, void * pFunc, int fChanges ) +{ + char * key, * value; + Abc_Command * pCommand; + int fStatus; + + key = sName; + if ( st_delete( pAbc->tCommands, &key, &value ) ) + { + // delete existing definition for this command + fprintf( pAbc->Err, "Cmd warning: redefining '%s'\n", sName ); + CmdCommandFree( (Abc_Command *)value ); + } + + // create the new command + pCommand = ALLOC( Abc_Command, 1 ); + pCommand->sName = Extra_UtilStrsav( sName ); + pCommand->sGroup = Extra_UtilStrsav( sGroup ); + pCommand->pFunc = pFunc; + pCommand->fChange = fChanges; + fStatus = st_insert( pAbc->tCommands, sName, (char *)pCommand ); + assert( !fStatus ); // the command should not be in the table +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Cmd_CommandExecute( Abc_Frame_t * pAbc, char * sCommand ) +{ + int fStatus = 0, argc, loop; + char * sCommandNext, **argv; + + if ( !pAbc->fAutoexac ) + Cmd_HistoryAddCommand(pAbc, sCommand); + sCommandNext = sCommand; + do + { + sCommandNext = CmdSplitLine( pAbc, sCommandNext, &argc, &argv ); + loop = 0; + fStatus = CmdApplyAlias( pAbc, &argc, &argv, &loop ); + if ( fStatus == 0 ) + fStatus = CmdCommandDispatch( pAbc, argc, argv ); + CmdFreeArgv( argc, argv ); + } + while ( fStatus == 0 && *sCommandNext != '\0' ); + return fStatus; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/cmd/cmdFlag.c b/abc_with_bb_support/src/base/cmd/cmdFlag.c new file mode 100644 index 000000000..2bf5733e6 --- /dev/null +++ b/abc_with_bb_support/src/base/cmd/cmdFlag.c @@ -0,0 +1,104 @@ +/**CFile**************************************************************** + + FileName [cmdFlag.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures working with flags.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cmdFlag.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "mainInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + + +/**Function******************************************************************** + + Synopsis [Looks up value of flag in table of named values.] + + Description [The command parser maintains a table of named values. These + are manipulated using the 'set' and 'unset' commands. The value of the + named flag is returned, or NULL is returned if the flag has not been set.] + + SideEffects [] + +******************************************************************************/ +char * Cmd_FlagReadByName( Abc_Frame_t * pAbc, char * flag ) +{ + char * value; + if ( st_lookup(pAbc->tFlags, flag, &value) ) + return value; + return NULL; +} + + +/**Function******************************************************************** + + Synopsis [Updates a set value by calling instead of set command.] + + Description [Updates a set value by calling instead of set command.] + + SideEffects [] + +******************************************************************************/ +void Cmd_FlagUpdateValue( Abc_Frame_t * pAbc, char * key, char * value ) +{ + char * oldValue, * newValue; + if ( !key ) + return; + if ( value ) + newValue = Extra_UtilStrsav(value); + else + newValue = Extra_UtilStrsav(""); +// newValue = NULL; + if ( st_delete(pAbc->tFlags, &key, &oldValue) ) + FREE(oldValue); + st_insert( pAbc->tFlags, key, newValue ); +} + + +/**Function******************************************************************** + + Synopsis [Deletes a set value by calling instead of unset command.] + + Description [Deletes a set value by calling instead of unset command.] + + SideEffects [] + +******************************************************************************/ +void Cmd_FlagDeleteByName( Abc_Frame_t * pAbc, char * key ) +{ + char *value; + if ( !key ) + return; + if ( st_delete( pAbc->tFlags, &key, &value ) ) + { + FREE(key); + FREE(value); + } +} + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/cmd/cmdHist.c b/abc_with_bb_support/src/base/cmd/cmdHist.c new file mode 100644 index 000000000..33c3abec7 --- /dev/null +++ b/abc_with_bb_support/src/base/cmd/cmdHist.c @@ -0,0 +1,55 @@ +/**CFile**************************************************************** + + FileName [cmdHist.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures working with history.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cmdHist.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "mainInt.h" +#include "cmd.h" +#include "cmdInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cmd_HistoryAddCommand( Abc_Frame_t * p, char * command ) +{ + static char Buffer[MAX_STR]; + strcpy( Buffer, command ); + if ( command[strlen(command)-1] != '\n' ) + strcat( Buffer, "\n" ); + Vec_PtrPush( p->aHistory, Extra_UtilStrsav(Buffer) ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/base/cmd/cmdInt.h b/abc_with_bb_support/src/base/cmd/cmdInt.h new file mode 100644 index 000000000..01ec00060 --- /dev/null +++ b/abc_with_bb_support/src/base/cmd/cmdInt.h @@ -0,0 +1,83 @@ +/**CFile**************************************************************** + + FileName [cmdInt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Internal declarations of the command package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cmdInt.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __CMD_INT_H__ +#define __CMD_INT_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include "mainInt.h" +#include "cmd.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +struct MvCommand +{ + char * sName; // the command name + char * sGroup; // the group name + void * pFunc; // the function to execute the command + int fChange; // set to 1 to mark that the network is changed +}; + +struct MvAlias +{ + char * sName; // the alias name + int argc; // the number of alias parts + char ** argv; // the alias parts +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== cmdAlias.c =============-========================================*/ +extern void CmdCommandAliasAdd( Abc_Frame_t * pAbc, char * sName, int argc, char ** argv ); +extern void CmdCommandAliasPrint( Abc_Frame_t * pAbc, Abc_Alias * pAlias ); +extern char * CmdCommandAliasLookup( Abc_Frame_t * pAbc, char * sCommand ); +extern void CmdCommandAliasFree( Abc_Alias * p ); +/*=== cmdUtils.c =======================================================*/ +extern int CmdCommandDispatch( Abc_Frame_t * pAbc, int argc, char ** argv ); +extern char * CmdSplitLine( Abc_Frame_t * pAbc, char * sCommand, int * argc, char *** argv ); +extern int CmdApplyAlias( Abc_Frame_t * pAbc, int * argc, char *** argv, int * loop ); +extern char * CmdHistorySubstitution( Abc_Frame_t * pAbc, char * line, int * changed ); +extern FILE * CmdFileOpen( Abc_Frame_t * pAbc, char * sFileName, char * sMode, char ** pFileNameReal, int silent ); +extern void CmdFreeArgv( int argc, char ** argv ); +extern void CmdCommandFree( Abc_Command * pCommand ); +extern void CmdCommandPrint( Abc_Frame_t * pAbc, bool fPrintAll ); +extern void CmdPrintTable( st_table * tTable, int fAliases ); + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + +#endif + diff --git a/abc_with_bb_support/src/base/cmd/cmdUtils.c b/abc_with_bb_support/src/base/cmd/cmdUtils.c new file mode 100644 index 000000000..aa1696f5b --- /dev/null +++ b/abc_with_bb_support/src/base/cmd/cmdUtils.c @@ -0,0 +1,649 @@ +/**CFile**************************************************************** + + FileName [cmdUtils.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Various utilities of the command package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cmdUtils.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "mainInt.h" +#include "abc.h" +#include "cmdInt.h" +#include // proper declaration of isspace + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int CmdCommandPrintCompare( Abc_Command ** ppC1, Abc_Command ** ppC2 ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int cmdCheckShellEscape( Abc_Frame_t * pAbc, int argc, char ** argv) +{ + if (argv[0][0] == '!') + { + const int size = 4096; + int i; + char buffer[4096]; + strncpy (buffer, &argv[0][1], size); + for (i = 1; i < argc; ++i) + { + strncat (buffer, " ", size); + strncat (buffer, argv[i], size); + } + if (buffer[0] == 0) + strncpy (buffer, "/bin/sh", size); + system (buffer); + + // NOTE: Since we reconstruct the cmdline by concatenating + // the parts, we lose information. So a command like + // `!ls "file name"` will be sent to the system as + // `ls file name` which is a BUG + + return 1; + } + else + { + return 0; + } +} + +/**Function************************************************************* + + Synopsis [Executes one command.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int CmdCommandDispatch( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + Abc_Ntk_t * pNetCopy; + int (*pFunc) ( Abc_Frame_t *, int, char ** ); + Abc_Command * pCommand; + char * value; + int fError; + int clk; + + if ( argc == 0 ) + return 0; + + if ( cmdCheckShellEscape( pAbc, argc, argv ) == 1 ) + return 0; + + // get the command + if ( !st_lookup( pAbc->tCommands, argv[0], (char **)&pCommand ) ) + { // the command is not in the table + fprintf( pAbc->Err, "** cmd error: unknown command '%s'\n", argv[0] ); + return 1; + } + + // get the backup network if the command is going to change the network + if ( pCommand->fChange ) + { + if ( pAbc->pNtkCur && Abc_FrameIsFlagEnabled( "backup" ) ) + { + pNetCopy = Abc_NtkDup( pAbc->pNtkCur ); + Abc_FrameSetCurrentNetwork( pAbc, pNetCopy ); + // swap the current network and the backup network + // to prevent the effect of resetting the short names + Abc_FrameSwapCurrentAndBackup( pAbc ); + } + } + + // execute the command + clk = Extra_CpuTime(); + pFunc = (int (*)(Abc_Frame_t *, int, char **))pCommand->pFunc; + fError = (*pFunc)( pAbc, argc, argv ); + pAbc->TimeCommand += (Extra_CpuTime() - clk); + + // automatic execution of arbitrary command after each command + // usually this is a passive command ... + if ( fError == 0 && !pAbc->fAutoexac ) + { + if ( st_lookup( pAbc->tFlags, "autoexec", &value ) ) + { + pAbc->fAutoexac = 1; + fError = Cmd_CommandExecute( pAbc, value ); + pAbc->fAutoexac = 0; + } + } + return fError; +} + +/**Function************************************************************* + + Synopsis [Splits the command line string into individual commands.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * CmdSplitLine( Abc_Frame_t * pAbc, char *sCommand, int *argc, char ***argv ) +{ + char *p, *start, c; + int i, j; + char *new_arg; + Vec_Ptr_t * vArgs; + int single_quote, double_quote; + + vArgs = Vec_PtrAlloc( 10 ); + + p = sCommand; + for ( ;; ) + { + // skip leading white space + while ( isspace( ( int ) *p ) ) + { + p++; + } + + // skip until end of this token + single_quote = double_quote = 0; + for ( start = p; ( c = *p ) != '\0'; p++ ) + { + if ( c == ';' || c == '#' || isspace( ( int ) c ) ) + { + if ( !single_quote && !double_quote ) + { + break; + } + } + if ( c == '\'' ) + { + single_quote = !single_quote; + } + if ( c == '"' ) + { + double_quote = !double_quote; + } + } + if ( single_quote || double_quote ) + { + ( void ) fprintf( pAbc->Err, "** cmd warning: ignoring unbalanced quote ...\n" ); + } + if ( start == p ) + break; + + new_arg = ALLOC( char, p - start + 1 ); + j = 0; + for ( i = 0; i < p - start; i++ ) + { + c = start[i]; + if ( ( c != '\'' ) && ( c != '\"' ) ) + { + new_arg[j++] = isspace( ( int ) c ) ? ' ' : start[i]; + } + } + new_arg[j] = '\0'; + Vec_PtrPush( vArgs, new_arg ); + } + + *argc = vArgs->nSize; + *argv = (char **)Vec_PtrReleaseArray( vArgs ); + Vec_PtrFree( vArgs ); + if ( *p == ';' ) + { + p++; + } + else if ( *p == '#' ) + { + for ( ; *p != 0; p++ ); // skip to end of line + } + return p; +} + +/**Function************************************************************* + + Synopsis [Replaces parts of the command line string by aliases if given.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int CmdApplyAlias( Abc_Frame_t * pAbc, int *argcp, char ***argvp, int *loop ) +{ + int i, argc, stopit, added, offset, did_subst, subst, fError, newc, j; + char *arg, **argv, **newv; + Abc_Alias *alias; + + argc = *argcp; + argv = *argvp; + stopit = 0; + for ( ; *loop < 200; ( *loop )++ ) + { + if ( argc == 0 ) + return 0; + if ( stopit != 0 || st_lookup( pAbc->tAliases, argv[0], (char **) &alias ) == 0 ) + { + return 0; + } + if ( strcmp( argv[0], alias->argv[0] ) == 0 ) + { + stopit = 1; + } + FREE( argv[0] ); + added = alias->argc - 1; + + /* shift all the arguments to the right */ + if ( added != 0 ) + { + argv = REALLOC( char *, argv, argc + added ); + for ( i = argc - 1; i >= 1; i-- ) + { + argv[i + added] = argv[i]; + } + for ( i = 1; i <= added; i++ ) + { + argv[i] = NULL; + } + argc += added; + } + subst = 0; + for ( i = 0, offset = 0; i < alias->argc; i++, offset++ ) + { + arg = CmdHistorySubstitution( pAbc, alias->argv[i], &did_subst ); + if ( arg == NULL ) + { + *argcp = argc; + *argvp = argv; + return ( 1 ); + } + if ( did_subst != 0 ) + { + subst = 1; + } + fError = 0; + do + { + arg = CmdSplitLine( pAbc, arg, &newc, &newv ); + /* + * If there's a complete `;' terminated command in `arg', + * when split_line() returns arg[0] != '\0'. + */ + if ( arg[0] == '\0' ) + { /* just a bunch of words */ + break; + } + fError = CmdApplyAlias( pAbc, &newc, &newv, loop ); + if ( fError == 0 ) + { + fError = CmdCommandDispatch( pAbc, newc, newv ); + } + CmdFreeArgv( newc, newv ); + } + while ( fError == 0 ); + if ( fError != 0 ) + { + *argcp = argc; + *argvp = argv; + return ( 1 ); + } + added = newc - 1; + if ( added != 0 ) + { + argv = REALLOC( char *, argv, argc + added ); + for ( j = argc - 1; j > offset; j-- ) + { + argv[j + added] = argv[j]; + } + argc += added; + } + for ( j = 0; j <= added; j++ ) + { + argv[j + offset] = newv[j]; + } + FREE( newv ); + offset += added; + } + if ( subst == 1 ) + { + for ( i = offset; i < argc; i++ ) + { + FREE( argv[i] ); + } + argc = offset; + } + *argcp = argc; + *argvp = argv; + } + + fprintf( pAbc->Err, "** cmd warning: alias loop\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Performs history substitution (now, disabled).] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * CmdHistorySubstitution( Abc_Frame_t * pAbc, char *line, int *changed ) +{ + // as of today, no history substitution + *changed = 0; + return line; +} + +/**Function************************************************************* + + Synopsis [Opens the file with path (now, disabled).] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +FILE * CmdFileOpen( Abc_Frame_t * pAbc, char *sFileName, char *sMode, char **pFileNameReal, int silent ) +{ + char * sRealName, * sPathUsr, * sPathLib, * sPathAll; + FILE * pFile; + + if (strcmp(sFileName, "-") == 0) { + if (strcmp(sMode, "w") == 0) { + sRealName = Extra_UtilStrsav( "stdout" ); + pFile = stdout; + } + else { + sRealName = Extra_UtilStrsav( "stdin" ); + pFile = stdin; + } + } + else { + sRealName = NULL; + if (strcmp(sMode, "r") == 0) { + + /* combine both pathes if exist */ + sPathUsr = Cmd_FlagReadByName(pAbc,"open_path"); + sPathLib = Cmd_FlagReadByName(pAbc,"lib_path"); + + if ( sPathUsr == NULL && sPathLib == NULL ) { + sPathAll = NULL; + } + else if ( sPathUsr == NULL ) { + sPathAll = Extra_UtilStrsav( sPathLib ); + } + else if ( sPathLib == NULL ) { + sPathAll = Extra_UtilStrsav( sPathUsr ); + } + else { + sPathAll = ALLOC( char, strlen(sPathLib)+strlen(sPathUsr)+5 ); + sprintf( sPathAll, "%s:%s",sPathUsr, sPathLib ); + } + if ( sPathAll != NULL ) { + sRealName = Extra_UtilFileSearch(sFileName, sPathAll, "r"); + FREE( sPathAll ); + } + } + if (sRealName == NULL) { + sRealName = Extra_UtilTildeExpand(sFileName); + } + if ((pFile = fopen(sRealName, sMode)) == NULL) { + if (! silent) { + perror(sRealName); + } + } + } + if ( pFileNameReal ) + *pFileNameReal = sRealName; + else + FREE(sRealName); + + return pFile; +} + +/**Function************************************************************* + + Synopsis [Frees the previously allocated argv array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void CmdFreeArgv( int argc, char **argv ) +{ + int i; + for ( i = 0; i < argc; i++ ) + FREE( argv[i] ); + FREE( argv ); +} + +/**Function************************************************************* + + Synopsis [Frees the previously allocated command.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void CmdCommandFree( Abc_Command * pCommand ) +{ + free( pCommand->sGroup ); + free( pCommand->sName ); + free( pCommand ); +} + + +/**Function************************************************************* + + Synopsis [Prints commands alphabetically by group.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void CmdCommandPrint( Abc_Frame_t * pAbc, bool fPrintAll ) +{ + char *key, *value; + st_generator * gen; + Abc_Command ** ppCommands; + Abc_Command * pCommands; + int nCommands, i; + char * sGroupCur; + int LenghtMax, nColumns, iCom = 0; + + // put all commands into one array + nCommands = st_count( pAbc->tCommands ); + ppCommands = ALLOC( Abc_Command *, nCommands ); + i = 0; + st_foreach_item( pAbc->tCommands, gen, &key, &value ) + { + pCommands = (Abc_Command *)value; + if ( fPrintAll || pCommands->sName[0] != '_' ) + ppCommands[i++] = pCommands; + } + nCommands = i; + + // sort command by group and then by name, alphabetically + qsort( (void *)ppCommands, nCommands, sizeof(Abc_Command *), + (int (*)(const void *, const void *)) CmdCommandPrintCompare ); + assert( CmdCommandPrintCompare( ppCommands, ppCommands + nCommands - 1 ) <= 0 ); + + // get the longest command name + LenghtMax = 0; + for ( i = 0; i < nCommands; i++ ) + if ( LenghtMax < (int)strlen(ppCommands[i]->sName) ) + LenghtMax = (int)strlen(ppCommands[i]->sName); + // get the number of columns + nColumns = 79 / (LenghtMax + 2); + + // print the starting message + fprintf( pAbc->Out, " Welcome to ABC!" ); + + // print the command by group + sGroupCur = NULL; + for ( i = 0; i < nCommands; i++ ) + if ( sGroupCur && strcmp( sGroupCur, ppCommands[i]->sGroup ) == 0 ) + { // this command belongs to the same group as the previous one + if ( iCom++ % nColumns == 0 ) + fprintf( pAbc->Out, "\n" ); + // print this command + fprintf( pAbc->Out, " %-*s", LenghtMax, ppCommands[i]->sName ); + } + else + { // this command starts the new group of commands + // start the new group + fprintf( pAbc->Out, "\n" ); + fprintf( pAbc->Out, "\n" ); + fprintf( pAbc->Out, "%s commands:\n", ppCommands[i]->sGroup ); + // print this command + fprintf( pAbc->Out, " %-*s", LenghtMax, ppCommands[i]->sName ); + // remember current command group + sGroupCur = ppCommands[i]->sGroup; + // reset the command counter + iCom = 1; + } + fprintf( pAbc->Out, "\n" ); + FREE( ppCommands ); +} + +/**Function************************************************************* + + Synopsis [Comparision function used for sorting commands.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int CmdCommandPrintCompare( Abc_Command ** ppC1, Abc_Command ** ppC2 ) +{ + Abc_Command * pC1 = *ppC1; + Abc_Command * pC2 = *ppC2; + int RetValue; + + RetValue = strcmp( pC1->sGroup, pC2->sGroup ); + if ( RetValue < 0 ) + return -1; + if ( RetValue > 0 ) + return 1; + // the command belong to the same group + + // put commands with "_" at the end of the list + if ( pC1->sName[0] != '_' && pC2->sName[0] == '_' ) + return -1; + if ( pC1->sName[0] == '_' && pC2->sName[0] != '_' ) + return 1; + + RetValue = strcmp( pC1->sName, pC2->sName ); + if ( RetValue < 0 ) + return -1; + if ( RetValue > 0 ) + return 1; + // should not be two indentical commands + assert( 0 ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Comparision function used for sorting commands.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int CmdNamePrintCompare( char ** ppC1, char ** ppC2 ) +{ + return strcmp( *ppC1, *ppC2 ); +} + +/**Function************************************************************* + + Synopsis [Comparision function used for sorting commands.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void CmdPrintTable( st_table * tTable, int fAliases ) +{ + st_generator * gen; + char ** ppNames; + char * key, * value; + int nNames, i; + + // collect keys in the array + ppNames = ALLOC( char *, st_count(tTable) ); + nNames = 0; + st_foreach_item( tTable, gen, &key, &value ) + ppNames[nNames++] = key; + + // sort array by name + qsort( (void *)ppNames, nNames, sizeof(char *), + (int (*)(const void *, const void *))CmdNamePrintCompare ); + + // print in this order + for ( i = 0; i < nNames; i++ ) + { + st_lookup( tTable, ppNames[i], &value ); + if ( fAliases ) + CmdCommandAliasPrint( Abc_FrameGetGlobalFrame(), (Abc_Alias *)value ); + else + fprintf( stdout, "%-15s %-15s\n", ppNames[i], value ); + } + free( ppNames ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/base/cmd/module.make b/abc_with_bb_support/src/base/cmd/module.make new file mode 100644 index 000000000..1454067f1 --- /dev/null +++ b/abc_with_bb_support/src/base/cmd/module.make @@ -0,0 +1,6 @@ +SRC += src/base/cmd/cmd.c \ + src/base/cmd/cmdAlias.c \ + src/base/cmd/cmdApi.c \ + src/base/cmd/cmdFlag.c \ + src/base/cmd/cmdHist.c \ + src/base/cmd/cmdUtils.c diff --git a/abc_with_bb_support/src/base/func/funcBlifMv.c b/abc_with_bb_support/src/base/func/funcBlifMv.c new file mode 100644 index 000000000..6e42b7907 --- /dev/null +++ b/abc_with_bb_support/src/base/func/funcBlifMv.c @@ -0,0 +1,62 @@ +/**CFile**************************************************************** + + FileName [funcBlifMv.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Implementation of BLIF-MV representation of the nodes.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: funcBlifMv.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" + +/* + The BLIF-MV tables are represented using char * strings. + For example, the representation of the table + + .table c d0 d1 x + .default 0 + 0 - - =d0 + 1 - 1 1 + + is the string: "2 2 2 2\n0\n0 - - =1\n1 - 1 1\n" where '\n' is a single char. +*/ + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Abc_ConvertBlifMvToAig( Hop_Man_t * pMan, char * pSop ) +{ +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/io/io.c b/abc_with_bb_support/src/base/io/io.c new file mode 100644 index 000000000..9a34fae84 --- /dev/null +++ b/abc_with_bb_support/src/base/io/io.c @@ -0,0 +1,1893 @@ +/**CFile**************************************************************** + + FileName [io.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Command file.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: io.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" +#include "mainInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int IoCommandRead ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandReadAiger ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandReadBaf ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandReadBlif ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandReadBlifMv ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandReadBench ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandReadDsd ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandReadEdif ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandReadEqn ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandReadPla ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandReadTruth ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandReadVerilog ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandReadVer ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandReadVerLib ( Abc_Frame_t * pAbc, int argc, char **argv ); + +static int IoCommandWrite ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandWriteHie ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandWriteAiger ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandWriteBaf ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandWriteBlif ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandWriteBlifMv ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandWriteBench ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandWriteCellNet( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandWriteCnf ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandWriteCounter( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandWriteDot ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandWriteEqn ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandWriteGml ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandWriteList ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandWritePla ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandWriteVerilog( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandWriteVerLib ( Abc_Frame_t * pAbc, int argc, char **argv ); + +extern int glo_fMapped; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_Init( Abc_Frame_t * pAbc ) +{ + Cmd_CommandAdd( pAbc, "I/O", "read", IoCommandRead, 1 ); + Cmd_CommandAdd( pAbc, "I/O", "read_aiger", IoCommandReadAiger, 1 ); + Cmd_CommandAdd( pAbc, "I/O", "read_baf", IoCommandReadBaf, 1 ); + Cmd_CommandAdd( pAbc, "I/O", "read_blif", IoCommandReadBlif, 1 ); + Cmd_CommandAdd( pAbc, "I/O", "read_blif_mv", IoCommandReadBlif, 1 ); + Cmd_CommandAdd( pAbc, "I/O", "read_bench", IoCommandReadBench, 1 ); + Cmd_CommandAdd( pAbc, "I/O", "read_dsd", IoCommandReadDsd, 1 ); +// Cmd_CommandAdd( pAbc, "I/O", "read_edif", IoCommandReadEdif, 1 ); + Cmd_CommandAdd( pAbc, "I/O", "read_eqn", IoCommandReadEqn, 1 ); + Cmd_CommandAdd( pAbc, "I/O", "read_pla", IoCommandReadPla, 1 ); + Cmd_CommandAdd( pAbc, "I/O", "read_truth", IoCommandReadTruth, 1 ); + Cmd_CommandAdd( pAbc, "I/O", "read_verilog", IoCommandReadVerilog, 1 ); +// Cmd_CommandAdd( pAbc, "I/O", "read_ver", IoCommandReadVer, 1 ); +// Cmd_CommandAdd( pAbc, "I/O", "read_verlib", IoCommandReadVerLib, 0 ); + + Cmd_CommandAdd( pAbc, "I/O", "write", IoCommandWrite, 0 ); + Cmd_CommandAdd( pAbc, "I/O", "write_hie", IoCommandWriteHie, 0 ); + Cmd_CommandAdd( pAbc, "I/O", "write_aiger", IoCommandWriteAiger, 0 ); + Cmd_CommandAdd( pAbc, "I/O", "write_baf", IoCommandWriteBaf, 0 ); + Cmd_CommandAdd( pAbc, "I/O", "write_blif", IoCommandWriteBlif, 0 ); + Cmd_CommandAdd( pAbc, "I/O", "write_blif_mv", IoCommandWriteBlifMv, 0 ); + Cmd_CommandAdd( pAbc, "I/O", "write_bench", IoCommandWriteBench, 0 ); + Cmd_CommandAdd( pAbc, "I/O", "write_cellnet", IoCommandWriteCellNet, 0 ); + Cmd_CommandAdd( pAbc, "I/O", "write_counter", IoCommandWriteCounter, 0 ); + Cmd_CommandAdd( pAbc, "I/O", "write_cnf", IoCommandWriteCnf, 0 ); + Cmd_CommandAdd( pAbc, "I/O", "write_dot", IoCommandWriteDot, 0 ); + Cmd_CommandAdd( pAbc, "I/O", "write_eqn", IoCommandWriteEqn, 0 ); + Cmd_CommandAdd( pAbc, "I/O", "write_gml", IoCommandWriteGml, 0 ); +// Cmd_CommandAdd( pAbc, "I/O", "write_list", IoCommandWriteList, 0 ); + Cmd_CommandAdd( pAbc, "I/O", "write_pla", IoCommandWritePla, 0 ); + Cmd_CommandAdd( pAbc, "I/O", "write_verilog", IoCommandWriteVerilog, 0 ); +// Cmd_CommandAdd( pAbc, "I/O", "write_verlib", IoCommandWriteVerLib, 0 ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_End() +{ +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandRead( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Abc_Ntk_t * pNtk; + char * pFileName; + int fCheck; + int c; + + fCheck = 1; + glo_fMapped = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "mch" ) ) != EOF ) + { + switch ( c ) + { + case 'm': + glo_fMapped ^= 1; + break; + case 'c': + fCheck ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the input file name + pFileName = argv[globalUtilOptind]; + // read the file using the corresponding file reader + pNtk = Io_Read( pFileName, Io_ReadFileType(pFileName), fCheck ); + if ( pNtk == NULL ) + return 1; + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtk ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: read [-mch] \n" ); + fprintf( pAbc->Err, "\t replaces the current network by the network read from \n" ); + fprintf( pAbc->Err, "\t by calling the parser that matches the extension of \n" ); + fprintf( pAbc->Err, "\t (to read a hierarchical design, use \"read_hie\")\n" ); + fprintf( pAbc->Err, "\t-m : toggle reading mapped Verilog [default = %s]\n", glo_fMapped? "yes":"no" ); + fprintf( pAbc->Err, "\t-c : toggle network check after reading [default = %s]\n", fCheck? "yes":"no" ); + fprintf( pAbc->Err, "\t-h : prints the command summary\n" ); + fprintf( pAbc->Err, "\tfile : the name of a file to read\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandReadAiger( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Abc_Ntk_t * pNtk; + char * pFileName; + int fCheck; + int c; + + fCheck = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "ch" ) ) != EOF ) + { + switch ( c ) + { + case 'c': + fCheck ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the input file name + pFileName = argv[globalUtilOptind]; + // read the file using the corresponding file reader + pNtk = Io_Read( pFileName, IO_FILE_AIGER, fCheck ); + if ( pNtk == NULL ) + return 1; + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtk ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: read_aiger [-ch] \n" ); + fprintf( pAbc->Err, "\t read the network in the AIGER format (http://fmv.jku.at/aiger)\n" ); + fprintf( pAbc->Err, "\t-c : toggle network check after reading [default = %s]\n", fCheck? "yes":"no" ); + fprintf( pAbc->Err, "\t-h : prints the command summary\n" ); + fprintf( pAbc->Err, "\tfile : the name of a file to read\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandReadBaf( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Abc_Ntk_t * pNtk; + char * pFileName; + int fCheck; + int c; + + fCheck = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "ch" ) ) != EOF ) + { + switch ( c ) + { + case 'c': + fCheck ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the input file name + pFileName = argv[globalUtilOptind]; + // read the file using the corresponding file reader + pNtk = Io_Read( pFileName, IO_FILE_BAF, fCheck ); + if ( pNtk == NULL ) + return 1; + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtk ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: read_baf [-ch] \n" ); + fprintf( pAbc->Err, "\t read the network in Binary Aig Format (BAF)\n" ); + fprintf( pAbc->Err, "\t-c : toggle network check after reading [default = %s]\n", fCheck? "yes":"no" ); + fprintf( pAbc->Err, "\t-h : prints the command summary\n" ); + fprintf( pAbc->Err, "\tfile : the name of a file to read\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandReadBlif( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Abc_Ntk_t * pNtk; + char * pFileName; + int fReadAsAig; + int fCheck; + int c; + extern Abc_Ntk_t * Io_ReadBlifAsAig( char * pFileName, int fCheck ); + + fCheck = 1; + fReadAsAig = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "ach" ) ) != EOF ) + { + switch ( c ) + { + case 'a': + fReadAsAig ^= 1; + break; + case 'c': + fCheck ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the input file name + pFileName = argv[globalUtilOptind]; + // read the file using the corresponding file reader + if ( fReadAsAig ) + pNtk = Io_ReadBlifAsAig( pFileName, fCheck ); + else +// pNtk = Io_Read( pFileName, IO_FILE_BLIF, fCheck ); + { + Abc_Ntk_t * pTemp; + pNtk = Io_ReadBlif( pFileName, fCheck ); + pNtk = Abc_NtkToLogic( pTemp = pNtk ); + Abc_NtkDelete( pTemp ); + } + + if ( pNtk == NULL ) + return 1; + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtk ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: read_blif [-ach] \n" ); + fprintf( pAbc->Err, "\t read the network in binary BLIF format\n" ); + fprintf( pAbc->Err, "\t-a : toggle creating AIG while reading the file [default = %s]\n", fReadAsAig? "yes":"no" ); + fprintf( pAbc->Err, "\t-c : toggle network check after reading [default = %s]\n", fCheck? "yes":"no" ); + fprintf( pAbc->Err, "\t-h : prints the command summary\n" ); + fprintf( pAbc->Err, "\tfile : the name of a file to read\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandReadBlifMv( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Abc_Ntk_t * pNtk; + char * pFileName; + int fCheck; + int c; + + fCheck = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "ch" ) ) != EOF ) + { + switch ( c ) + { + case 'c': + fCheck ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the input file name + pFileName = argv[globalUtilOptind]; + // read the file using the corresponding file reader + pNtk = Io_Read( pFileName, IO_FILE_BLIFMV, fCheck ); + if ( pNtk == NULL ) + return 1; + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtk ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: read_blif_mv [-ch] \n" ); + fprintf( pAbc->Err, "\t read the network in BLIF-MV format\n" ); + fprintf( pAbc->Err, "\t-c : toggle network check after reading [default = %s]\n", fCheck? "yes":"no" ); + fprintf( pAbc->Err, "\t-h : prints the command summary\n" ); + fprintf( pAbc->Err, "\tfile : the name of a file to read\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandReadBench( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Abc_Ntk_t * pNtk; + char * pFileName; + int fCheck; + int c; + + fCheck = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "ch" ) ) != EOF ) + { + switch ( c ) + { + case 'c': + fCheck ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the input file name + pFileName = argv[globalUtilOptind]; + // read the file using the corresponding file reader + pNtk = Io_Read( pFileName, IO_FILE_BENCH, fCheck ); + if ( pNtk == NULL ) + return 1; + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtk ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: read_bench [-ch] \n" ); + fprintf( pAbc->Err, "\t read the network in BENCH format\n" ); + fprintf( pAbc->Err, "\t-c : toggle network check after reading [default = %s]\n", fCheck? "yes":"no" ); + fprintf( pAbc->Err, "\t-h : prints the command summary\n" ); + fprintf( pAbc->Err, "\tfile : the name of a file to read\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandReadDsd( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Abc_Ntk_t * pNtk; + char * pString; + int fCheck; + int c; + extern Abc_Ntk_t * Io_ReadDsd( char * pFormula ); + + fCheck = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "ch" ) ) != EOF ) + { + switch ( c ) + { + case 'c': + fCheck ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the input file name + pString = argv[globalUtilOptind]; + // read the file using the corresponding file reader + pNtk = Io_ReadDsd( pString ); + if ( pNtk == NULL ) + return 1; + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtk ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: read_dsd [-h] \n" ); + fprintf( pAbc->Err, "\t parses a formula representing DSD of a function\n" ); + fprintf( pAbc->Err, "\t-h : prints the command summary\n" ); + fprintf( pAbc->Err, "\tformula : the formula representing disjoint-support decomposition (DSD)\n" ); + fprintf( pAbc->Err, "\t Example of a formula: !(a*(b+CA(!d,e*f,c))*79B3(g,h,i,k))\n" ); + fprintf( pAbc->Err, "\t where \'!\' is an INV, \'*\' is an AND, \'+\' is an XOR, \n" ); + fprintf( pAbc->Err, "\t CA and 79B3 are hexadecimal representations of truth tables\n" ); + fprintf( pAbc->Err, "\t (in this case CA=11001010 is truth table of MUX(Data0,Data1,Ctrl))\n" ); + fprintf( pAbc->Err, "\t The lower chars (a,b,c,etc) are reserved for elementary variables.\n" ); + fprintf( pAbc->Err, "\t The upper chars (A,B,C,etc) are reserved for hexadecimal digits.\n" ); + fprintf( pAbc->Err, "\t No spaces are allowed in formulas. In parantheses, LSB goes first.\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandReadEdif( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Abc_Ntk_t * pNtk; + char * pFileName; + int fCheck; + int c; + + fCheck = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "ch" ) ) != EOF ) + { + switch ( c ) + { + case 'c': + fCheck ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the input file name + pFileName = argv[globalUtilOptind]; + // read the file using the corresponding file reader + pNtk = Io_Read( pFileName, IO_FILE_EDIF, fCheck ); + if ( pNtk == NULL ) + return 1; + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtk ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: read_edif [-ch] \n" ); + fprintf( pAbc->Err, "\t read the network in EDIF (works only for ISCAS benchmarks)\n" ); + fprintf( pAbc->Err, "\t-c : toggle network check after reading [default = %s]\n", fCheck? "yes":"no" ); + fprintf( pAbc->Err, "\t-h : prints the command summary\n" ); + fprintf( pAbc->Err, "\tfile : the name of a file to read\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandReadEqn( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Abc_Ntk_t * pNtk; + char * pFileName; + int fCheck; + int c; + + fCheck = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "ch" ) ) != EOF ) + { + switch ( c ) + { + case 'c': + fCheck ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the input file name + pFileName = argv[globalUtilOptind]; + // read the file using the corresponding file reader + pNtk = Io_Read( pFileName, IO_FILE_EQN, fCheck ); + if ( pNtk == NULL ) + return 1; + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtk ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: read_eqn [-ch] \n" ); + fprintf( pAbc->Err, "\t read the network in equation format\n" ); + fprintf( pAbc->Err, "\t-c : toggle network check after reading [default = %s]\n", fCheck? "yes":"no" ); + fprintf( pAbc->Err, "\t-h : prints the command summary\n" ); + fprintf( pAbc->Err, "\tfile : the name of a file to read\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandReadPla( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Abc_Ntk_t * pNtk; + char * pFileName; + int fCheck; + int c; + + fCheck = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "ch" ) ) != EOF ) + { + switch ( c ) + { + case 'c': + fCheck ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the input file name + pFileName = argv[globalUtilOptind]; + // read the file using the corresponding file reader + pNtk = Io_Read( pFileName, IO_FILE_PLA, fCheck ); + if ( pNtk == NULL ) + return 1; + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtk ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: read_pla [-ch] \n" ); + fprintf( pAbc->Err, "\t read the network in PLA\n" ); + fprintf( pAbc->Err, "\t-c : toggle network check after reading [default = %s]\n", fCheck? "yes":"no" ); + fprintf( pAbc->Err, "\t-h : prints the command summary\n" ); + fprintf( pAbc->Err, "\tfile : the name of a file to read\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandReadTruth( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Abc_Ntk_t * pNtk; + char * pSopCover; + int fHex; + int c; + + fHex = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "xh" ) ) != EOF ) + { + switch ( c ) + { + case 'x': + fHex ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( argc != globalUtilOptind + 1 ) + { + goto usage; + } + + // convert truth table to SOP + if ( fHex ) + pSopCover = Abc_SopFromTruthHex(argv[globalUtilOptind]); + else + pSopCover = Abc_SopFromTruthBin(argv[globalUtilOptind]); + if ( pSopCover == NULL ) + { + fprintf( pAbc->Err, "Reading truth table has failed.\n" ); + return 1; + } + + pNtk = Abc_NtkCreateWithNode( pSopCover ); + free( pSopCover ); + if ( pNtk == NULL ) + { + fprintf( pAbc->Err, "Deriving the network has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtk ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: read_truth [-xh] \n" ); + fprintf( pAbc->Err, "\t creates network with node having given truth table\n" ); + fprintf( pAbc->Err, "\t-x : toggles between bin and hex representation [default = %s]\n", fHex? "hex":"bin" ); + fprintf( pAbc->Err, "\t-h : prints the command summary\n" ); + fprintf( pAbc->Err, "\ttruth : truth table with most signficant bit first (e.g. 1000 for AND(a,b))\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandReadVerilog( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Abc_Ntk_t * pNtk; + char * pFileName; + int fCheck; + int c; + + fCheck = 1; + glo_fMapped = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "mch" ) ) != EOF ) + { + switch ( c ) + { + case 'm': + glo_fMapped ^= 1; + break; + case 'c': + fCheck ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the input file name + pFileName = argv[globalUtilOptind]; + // read the file using the corresponding file reader + pNtk = Io_Read( pFileName, IO_FILE_VERILOG, fCheck ); + if ( pNtk == NULL ) + return 1; + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtk ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: read_verilog [-mch] \n" ); + fprintf( pAbc->Err, "\t read the network in Verilog (IWLS 2002/2005 subset)\n" ); + fprintf( pAbc->Err, "\t-m : toggle reading mapped Verilog [default = %s]\n", glo_fMapped? "yes":"no" ); + fprintf( pAbc->Err, "\t-c : toggle network check after reading [default = %s]\n", fCheck? "yes":"no" ); + fprintf( pAbc->Err, "\t-h : prints the command summary\n" ); + fprintf( pAbc->Err, "\tfile : the name of a file to read\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandReadVer( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Abc_Ntk_t * pNtk, * pNtkNew; + Abc_Lib_t * pDesign; + char * pFileName; + FILE * pFile; + int fCheck; + int c; + extern Abc_Ntk_t * Abc_LibDeriveAig( Abc_Ntk_t * pNtk, Abc_Lib_t * pLib ); + extern Abc_Lib_t * Ver_ParseFile( char * pFileName, Abc_Lib_t * pGateLib, int fCheck, int fUseMemMan ); + + printf( "Stand-alone structural Verilog reader is available as command \"read_verilog\".\n" ); + return 0; + + fCheck = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "ch" ) ) != EOF ) + { + switch ( c ) + { + case 'c': + fCheck ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( argc != globalUtilOptind + 1 ) + { + goto usage; + } + + // get the input file name + pFileName = argv[globalUtilOptind]; + if ( (pFile = fopen( pFileName, "r" )) == NULL ) + { + fprintf( pAbc->Err, "Cannot open input file \"%s\". ", pFileName ); + if ( pFileName = Extra_FileGetSimilarName( pFileName, ".blif", ".bench", ".pla", ".baf", ".aig" ) ) + fprintf( pAbc->Err, "Did you mean \"%s\"?", pFileName ); + fprintf( pAbc->Err, "\n" ); + return 1; + } + fclose( pFile ); + + // set the new network + pDesign = Ver_ParseFile( pFileName, Abc_FrameReadLibVer(), fCheck, 1 ); + if ( pDesign == NULL ) + { + fprintf( pAbc->Err, "Reading network from the verilog file has failed.\n" ); + return 1; + } + + // derive root design + pNtk = Abc_LibDeriveRoot( pDesign ); + Abc_LibFree( pDesign, NULL ); + if ( pNtk == NULL ) + { + fprintf( pAbc->Err, "Deriving root module has failed.\n" ); + return 1; + } + + // derive the AIG network from this design + pNtkNew = Abc_LibDeriveAig( pNtk, Abc_FrameReadLibVer() ); + Abc_NtkDelete( pNtk ); + if ( pNtkNew == NULL ) + { + fprintf( pAbc->Err, "Converting root module to AIG has failed.\n" ); + return 1; + } + // replace the current network + Abc_FrameReplaceCurrentNetwork( pAbc, pNtkNew ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: read_ver [-ch] \n" ); + fprintf( pAbc->Err, "\t read a network in structural verilog (using current library)\n" ); + fprintf( pAbc->Err, "\t-c : toggle network check after reading [default = %s]\n", fCheck? "yes":"no" ); + fprintf( pAbc->Err, "\t-h : prints the command summary\n" ); + fprintf( pAbc->Err, "\tfile : the name of a file to read\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandReadVerLib( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Abc_Lib_t * pLibrary; + char * pFileName; + FILE * pFile; + int fCheck; + int c; + extern Abc_Lib_t * Ver_ParseFile( char * pFileName, Abc_Lib_t * pGateLib, int fCheck, int fUseMemMan ); + + fCheck = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "ch" ) ) != EOF ) + { + switch ( c ) + { + case 'c': + fCheck ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( argc != globalUtilOptind + 1 ) + { + goto usage; + } + + // get the input file name + pFileName = argv[globalUtilOptind]; + if ( (pFile = fopen( pFileName, "r" )) == NULL ) + { + fprintf( pAbc->Err, "Cannot open input file \"%s\". ", pFileName ); + if ( pFileName = Extra_FileGetSimilarName( pFileName, ".blif", ".bench", ".pla", ".baf", ".aig" ) ) + fprintf( pAbc->Err, "Did you mean \"%s\"?", pFileName ); + fprintf( pAbc->Err, "\n" ); + return 1; + } + fclose( pFile ); + + // set the new network + pLibrary = Ver_ParseFile( pFileName, NULL, fCheck, 0 ); + if ( pLibrary == NULL ) + { + fprintf( pAbc->Err, "Reading library from the verilog file has failed.\n" ); + return 1; + } + printf( "The library contains %d gates.\n", st_count(pLibrary->tModules) ); + // free old library + if ( Abc_FrameReadLibVer() ) + Abc_LibFree( Abc_FrameReadLibVer(), NULL ); + // read new library + Abc_FrameSetLibVer( pLibrary ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: read_verlib [-ch] \n" ); + fprintf( pAbc->Err, "\t read a gate library in structural verilog\n" ); + fprintf( pAbc->Err, "\t-c : toggle network check after reading [default = %s]\n", fCheck? "yes":"no" ); + fprintf( pAbc->Err, "\t-h : prints the command summary\n" ); + fprintf( pAbc->Err, "\tfile : the name of a file to read\n" ); + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandWrite( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + char * pFileName; + int c; + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the output file name + pFileName = argv[globalUtilOptind]; + // call the corresponding file writer + Io_Write( pAbc->pNtkCur, pFileName, Io_ReadFileType(pFileName) ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: write [-h] \n" ); + fprintf( pAbc->Err, "\t writes the current network into by calling\n" ); + fprintf( pAbc->Err, "\t the writer that matches the extension of \n" ); + fprintf( pAbc->Err, "\t-h : print the help massage\n" ); + fprintf( pAbc->Err, "\tfile : the name of the file to write\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandWriteHie( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + char * pBaseName, * pFileName; + int c; + + glo_fMapped = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "mh" ) ) != EOF ) + { + switch ( c ) + { + case 'm': + glo_fMapped ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 2 ) + goto usage; + // get the output file name + pBaseName = argv[globalUtilOptind]; + pFileName = argv[globalUtilOptind+1]; + // call the corresponding file writer +// Io_Write( pAbc->pNtkCur, pFileName, Io_ReadFileType(pFileName) ); + Io_WriteHie( pAbc->pNtkCur, pBaseName, pFileName ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: write_hie [-h] \n" ); + fprintf( pAbc->Err, "\t writes the current network into by calling\n" ); + fprintf( pAbc->Err, "\t the hierarchical writer that matches the extension of \n" ); + fprintf( pAbc->Err, "\t-m : toggle reading mapped Verilog for [default = %s]\n", glo_fMapped? "yes":"no" ); + fprintf( pAbc->Err, "\t-h : print the help massage\n" ); + fprintf( pAbc->Err, "\torig : the name of the original file with the hierarchical design\n" ); + fprintf( pAbc->Err, "\tfile : the name of the file to write\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandWriteAiger( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + char * pFileName; + int fWriteSymbols; + int c; + + fWriteSymbols = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "sh" ) ) != EOF ) + { + switch ( c ) + { + case 's': + fWriteSymbols ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the output file name + pFileName = argv[globalUtilOptind]; + // call the corresponding file writer + if ( fWriteSymbols ) + Io_Write( pAbc->pNtkCur, pFileName, IO_FILE_AIGER ); + else + { + if ( !Abc_NtkIsStrash(pAbc->pNtkCur) ) + { + fprintf( stdout, "Writing this format is only possible for structurally hashed AIGs.\n" ); + return 1; + } + Io_WriteAiger( pAbc->pNtkCur, pFileName, 0 ); + } + return 0; + +usage: + fprintf( pAbc->Err, "usage: write_aiger [-sh] \n" ); + fprintf( pAbc->Err, "\t write the network in the AIGER format (http://fmv.jku.at/aiger)\n" ); + fprintf( pAbc->Err, "\t-s : toggle saving I/O names [default = %s]\n", fWriteSymbols? "yes" : "no" ); + fprintf( pAbc->Err, "\t-h : print the help massage\n" ); + fprintf( pAbc->Err, "\tfile : the name of the file to write (extension .aig)\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandWriteBaf( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + char * pFileName; + int c; + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the output file name + pFileName = argv[globalUtilOptind]; + // call the corresponding file writer + Io_Write( pAbc->pNtkCur, pFileName, IO_FILE_BAF ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: write_baf [-h] \n" ); + fprintf( pAbc->Err, "\t write the network into a BLIF file\n" ); + fprintf( pAbc->Err, "\t-h : print the help massage\n" ); + fprintf( pAbc->Err, "\tfile : the name of the file to write (extension .baf)\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandWriteBlif( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + char * pFileName; + int c; + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the output file name + pFileName = argv[globalUtilOptind]; + // call the corresponding file writer + Io_Write( pAbc->pNtkCur, pFileName, IO_FILE_BLIF ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: write_blif [-h] \n" ); + fprintf( pAbc->Err, "\t write the network into a BLIF file\n" ); + fprintf( pAbc->Err, "\t-h : print the help massage\n" ); + fprintf( pAbc->Err, "\tfile : the name of the file to write (extension .blif)\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandWriteBlifMv( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + char * pFileName; + int c; + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the output file name + pFileName = argv[globalUtilOptind]; + // call the corresponding file writer + Io_Write( pAbc->pNtkCur, pFileName, IO_FILE_BLIFMV ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: write_blif_mv [-h] \n" ); + fprintf( pAbc->Err, "\t write the network into a BLIF-MV file\n" ); + fprintf( pAbc->Err, "\t-h : print the help massage\n" ); + fprintf( pAbc->Err, "\tfile : the name of the file to write (extension .blif)\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandWriteBench( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + char * pFileName; + int fUseLuts; + int c; + + fUseLuts = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "lh" ) ) != EOF ) + { + switch ( c ) + { + case 'l': + fUseLuts ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the output file name + pFileName = argv[globalUtilOptind]; + // call the corresponding file writer + if ( !fUseLuts ) + Io_Write( pAbc->pNtkCur, pFileName, IO_FILE_BENCH ); + else + { + Abc_Ntk_t * pNtkTemp; + pNtkTemp = Abc_NtkToNetlist( pAbc->pNtkCur ); + Abc_NtkToAig( pNtkTemp ); + Io_WriteBenchLut( pNtkTemp, pFileName ); + Abc_NtkDelete( pNtkTemp ); + } + return 0; + +usage: + fprintf( pAbc->Err, "usage: write_bench [-lh] \n" ); + fprintf( pAbc->Err, "\t write the network in BENCH format\n" ); + fprintf( pAbc->Err, "\t-l : toggle using LUTs in the output [default = %s]\n", fUseLuts? "yes" : "no" ); + fprintf( pAbc->Err, "\t-h : print the help massage\n" ); + fprintf( pAbc->Err, "\tfile : the name of the file to write (extension .bench)\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandWriteCellNet( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + Abc_Ntk_t * pNtk; + char * pFileName; + int c; + extern void Io_WriteCellNet( Abc_Ntk_t * pNtk, char * pFileName ); + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + pNtk = pAbc->pNtkCur; + if ( pNtk == NULL ) + { + fprintf( pAbc->Out, "Empty network.\n" ); + return 0; + } + // get the output file name + pFileName = argv[globalUtilOptind]; + // call the corresponding file writer + if ( !Abc_NtkIsLogic(pNtk) ) + { + fprintf( pAbc->Out, "The network should be a logic network (if it an AIG, use command \"logic\")\n" ); + return 0; + } + Io_WriteCellNet( pNtk, pFileName ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: write_cellnet [-h] \n" ); + fprintf( pAbc->Err, "\t write the network is the cellnet format\n" ); + fprintf( pAbc->Err, "\t-h : print the help massage\n" ); + fprintf( pAbc->Err, "\tfile : the name of the file to write\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandWriteCnf( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + char * pFileName; + int c; + int fAllPrimes; + int fNewAlgo; + extern Abc_Ntk_t * Abc_NtkDarToCnf( Abc_Ntk_t * pNtk, char * pFileName ); + + fNewAlgo = 1; + fAllPrimes = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "nph" ) ) != EOF ) + { + switch ( c ) + { + case 'n': + fNewAlgo ^= 1; + break; + case 'p': + fAllPrimes ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the output file name + pFileName = argv[globalUtilOptind]; + // check if the feature will be used + if ( Abc_NtkIsStrash(pAbc->pNtkCur) && fAllPrimes ) + { + fAllPrimes = 0; + printf( "Warning: Selected option to write all primes has no effect when deriving CNF from AIG.\n" ); + } + // call the corresponding file writer + if ( fNewAlgo ) + Abc_NtkDarToCnf( pAbc->pNtkCur, pFileName ); + else if ( fAllPrimes ) + Io_WriteCnf( pAbc->pNtkCur, pFileName, 1 ); + else + Io_Write( pAbc->pNtkCur, pFileName, IO_FILE_CNF ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: write_cnf [-nph] \n" ); + fprintf( pAbc->Err, "\t write the miter cone into a CNF file\n" ); + fprintf( pAbc->Err, "\t-n : toggle using new algorithm [default = %s]\n", fNewAlgo? "yes" : "no" ); + fprintf( pAbc->Err, "\t-p : toggle using all primes to enhance implicativity [default = %s]\n", fAllPrimes? "yes" : "no" ); + fprintf( pAbc->Err, "\t-h : print the help massage\n" ); + fprintf( pAbc->Err, "\tfile : the name of the file to write\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandWriteDot( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + char * pFileName; + int c; + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the output file name + pFileName = argv[globalUtilOptind]; + // call the corresponding file writer + Io_Write( pAbc->pNtkCur, pFileName, IO_FILE_DOT ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: write_dot [-h] \n" ); + fprintf( pAbc->Err, "\t write the current network into a DOT file\n" ); + fprintf( pAbc->Err, "\t-h : print the help massage\n" ); + fprintf( pAbc->Err, "\tfile : the name of the file to write\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandWriteCounter( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + Abc_Ntk_t * pNtk; + char * pFileName; + int c; + int fNames; + + fNames = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "nh" ) ) != EOF ) + { + switch ( c ) + { + case 'n': + fNames ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + pNtk = pAbc->pNtkCur; + if ( pNtk == NULL ) + { + fprintf( pAbc->Out, "Empty network.\n" ); + return 0; + } + + if ( argc != globalUtilOptind + 1 ) + { + goto usage; + } + // get the input file name + pFileName = argv[globalUtilOptind]; + + if ( pNtk->pModel == NULL ) + { + fprintf( pAbc->Out, "Counter-example is not available.\n" ); + return 0; + } + + // write the counter-example into the file + { + Abc_Obj_t * pObj; + FILE * pFile = fopen( pFileName, "w" ); + int i; + if ( pFile == NULL ) + { + fprintf( stdout, "IoCommandWriteCounter(): Cannot open the output file \"%s\".\n", pFileName ); + return 1; + } + if ( fNames ) + { + Abc_NtkForEachPi( pNtk, pObj, i ) + fprintf( pFile, "%s=%c ", Abc_ObjName(pObj), '0'+(pNtk->pModel[i]==1) ); + } + else + { + Abc_NtkForEachPi( pNtk, pObj, i ) + fprintf( pFile, "%c", '0'+(pNtk->pModel[i]==1) ); + } + fprintf( pFile, "\n" ); + fclose( pFile ); + } + + return 0; + +usage: + fprintf( pAbc->Err, "usage: write_counter [-nh] \n" ); + fprintf( pAbc->Err, "\t writes the counter-example derived by \"prove\" or \"sat\"\n" ); + fprintf( pAbc->Err, "\t the file contains values for each PI in the natural order\n" ); + fprintf( pAbc->Err, "\t-n : write input names into the file [default = %s]\n", fNames? "yes": "no" ); + fprintf( pAbc->Err, "\t-h : print the help massage\n" ); + fprintf( pAbc->Err, "\tfile : the name of the file to write\n" ); + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandWriteEqn( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + char * pFileName; + int c; + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the output file name + pFileName = argv[globalUtilOptind]; + // call the corresponding file writer + Io_Write( pAbc->pNtkCur, pFileName, IO_FILE_EQN ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: write_eqn [-h] \n" ); + fprintf( pAbc->Err, "\t write the current network in the equation format\n" ); + fprintf( pAbc->Err, "\t-h : print the help massage\n" ); + fprintf( pAbc->Err, "\tfile : the name of the file to write\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandWriteGml( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + char * pFileName; + int c; + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the output file name + pFileName = argv[globalUtilOptind]; + // call the corresponding file writer + Io_Write( pAbc->pNtkCur, pFileName, IO_FILE_GML ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: write_gml [-h] \n" ); + fprintf( pAbc->Err, "\t write network using graph representation formal GML\n" ); + fprintf( pAbc->Err, "\t-h : print the help massage\n" ); + fprintf( pAbc->Err, "\tfile : the name of the file to write\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandWriteList( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + char * pFileName; + int fUseHost; + int c; + + printf( "This command currently does not work.\n" ); + return 0; + + fUseHost = 1; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "nh" ) ) != EOF ) + { + switch ( c ) + { + case 'n': + fUseHost ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + if ( pAbc->pNtkCur == NULL ) + { + fprintf( pAbc->Out, "Empty network.\n" ); + return 0; + } +/* + if ( !Abc_NtkIsSeq(pAbc->pNtkCur) ) + { + fprintf( stdout, "IoCommandWriteList(): Can write adjacency list for sequential AIGs only.\n" ); + return 0; + } +*/ + // get the input file name + pFileName = argv[globalUtilOptind]; + // write the file + Io_WriteList( pAbc->pNtkCur, pFileName, fUseHost ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: write_list [-nh] \n" ); + fprintf( pAbc->Err, "\t write network using graph representation formal GML\n" ); + fprintf( pAbc->Err, "\t-n : toggle writing host node [default = %s]\n", fUseHost? "yes":"no" ); + fprintf( pAbc->Err, "\t-h : print the help massage\n" ); + fprintf( pAbc->Err, "\tfile : the name of the file to write\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandWritePla( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + char * pFileName; + int c; + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the output file name + pFileName = argv[globalUtilOptind]; + // call the corresponding file writer + Io_Write( pAbc->pNtkCur, pFileName, IO_FILE_PLA ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: write_pla [-h] \n" ); + fprintf( pAbc->Err, "\t write the collapsed network into a PLA file\n" ); + fprintf( pAbc->Err, "\t-h : print the help massage\n" ); + fprintf( pAbc->Err, "\tfile : the name of the file to write\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandWriteVerilog( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + char * pFileName; + int c; + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the output file name + pFileName = argv[globalUtilOptind]; + // call the corresponding file writer + Io_Write( pAbc->pNtkCur, pFileName, IO_FILE_VERILOG ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: write_verilog [-h] \n" ); + fprintf( pAbc->Err, "\t write the current network in Verilog format\n" ); + fprintf( pAbc->Err, "\t-h : print the help massage\n" ); + fprintf( pAbc->Err, "\tfile : the name of the file to write\n" ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandWriteVerLib( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + Abc_Lib_t * pLibrary; + char * pFileName; + int c; + extern void Io_WriteVerilogLibrary( Abc_Lib_t * pLibrary, char * pFileName ); + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "h" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + // get the input file name + pFileName = argv[globalUtilOptind]; + // derive the netlist + pLibrary = Abc_FrameReadLibVer(); + if ( pLibrary == NULL ) + { + fprintf( pAbc->Out, "Verilog library is not specified.\n" ); + return 0; + } +// Io_WriteVerilogLibrary( pLibrary, pFileName ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: write_verlib [-h] \n" ); + fprintf( pAbc->Err, "\t write the current verilog library\n" ); + fprintf( pAbc->Err, "\t-h : print the help massage\n" ); + fprintf( pAbc->Err, "\tfile : the name of the file to write\n" ); + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/io/io.h b/abc_with_bb_support/src/base/io/io.h new file mode 100644 index 000000000..efa17c5a3 --- /dev/null +++ b/abc_with_bb_support/src/base/io/io.h @@ -0,0 +1,146 @@ +/**CFile**************************************************************** + + FileName [io.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: io.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __IO_H__ +#define __IO_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +// network functionality +typedef enum { + IO_FILE_NONE = 0, + IO_FILE_AIGER, + IO_FILE_BAF, + IO_FILE_BLIF, + IO_FILE_BLIFMV, + IO_FILE_BENCH, + IO_FILE_CNF, + IO_FILE_DOT, + IO_FILE_EDIF, + IO_FILE_EQN, + IO_FILE_GML, + IO_FILE_LIST, + IO_FILE_PLA, + IO_FILE_VERILOG, + IO_FILE_UNKNOWN +} Io_FileType_t; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define IO_WRITE_LINE_LENGTH 78 // the output line length + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== abcReadAiger.c ==========================================================*/ +extern Abc_Ntk_t * Io_ReadAiger( char * pFileName, int fCheck ); +/*=== abcReadBaf.c ============================================================*/ +extern Abc_Ntk_t * Io_ReadBaf( char * pFileName, int fCheck ); +/*=== abcReadBlif.c ===========================================================*/ +extern Abc_Ntk_t * Io_ReadBlif( char * pFileName, int fCheck ); +/*=== abcReadBlifMv.c =========================================================*/ +extern Abc_Ntk_t * Io_ReadBlifMv( char * pFileName, int fBlifMv, int fCheck ); +/*=== abcReadBench.c ==========================================================*/ +extern Abc_Ntk_t * Io_ReadBench( char * pFileName, int fCheck ); +/*=== abcReadEdif.c ===========================================================*/ +extern Abc_Ntk_t * Io_ReadEdif( char * pFileName, int fCheck ); +/*=== abcReadEqn.c ============================================================*/ +extern Abc_Ntk_t * Io_ReadEqn( char * pFileName, int fCheck ); +/*=== abcReadPla.c ============================================================*/ +extern Abc_Ntk_t * Io_ReadPla( char * pFileName, int fCheck ); +/*=== abcReadVerilog.c ========================================================*/ +extern Abc_Ntk_t * Io_ReadVerilog( char * pFileName, int fCheck ); +/*=== abcWriteAiger.c =========================================================*/ +extern void Io_WriteAiger( Abc_Ntk_t * pNtk, char * pFileName, int fWriteSymbols ); +/*=== abcWriteBaf.c ===========================================================*/ +extern void Io_WriteBaf( Abc_Ntk_t * pNtk, char * pFileName ); +/*=== abcWriteBlif.c ==========================================================*/ +extern void Io_WriteBlifLogic( Abc_Ntk_t * pNtk, char * pFileName, int fWriteLatches ); +extern void Io_WriteBlif( Abc_Ntk_t * pNtk, char * pFileName, int fWriteLatches ); +extern void Io_WriteTimingInfo( FILE * pFile, Abc_Ntk_t * pNtk ); +/*=== abcWriteBlifMv.c ==========================================================*/ +extern void Io_WriteBlifMv( Abc_Ntk_t * pNtk, char * FileName ); +/*=== abcWriteBench.c =========================================================*/ +extern int Io_WriteBench( Abc_Ntk_t * pNtk, char * FileName ); +extern int Io_WriteBenchLut( Abc_Ntk_t * pNtk, char * FileName ); +/*=== abcWriteCnf.c ===========================================================*/ +extern int Io_WriteCnf( Abc_Ntk_t * pNtk, char * FileName, int fAllPrimes ); +/*=== abcWriteDot.c ===========================================================*/ +extern void Io_WriteDot( Abc_Ntk_t * pNtk, char * FileName ); +extern void Io_WriteDotNtk( Abc_Ntk_t * pNtk, Vec_Ptr_t * vNodes, Vec_Ptr_t * vNodesShow, char * pFileName, int fGateNames, int fUseReverse ); +extern void Io_WriteDotSeq( Abc_Ntk_t * pNtk, Vec_Ptr_t * vNodes, Vec_Ptr_t * vNodesShow, char * pFileName, int fGateNames, int fUseReverse ); +/*=== abcWriteEqn.c ===========================================================*/ +extern void Io_WriteEqn( Abc_Ntk_t * pNtk, char * pFileName ); +/*=== abcWriteGml.c ===========================================================*/ +extern void Io_WriteGml( Abc_Ntk_t * pNtk, char * pFileName ); +/*=== abcWriteList.c ==========================================================*/ +extern void Io_WriteList( Abc_Ntk_t * pNtk, char * pFileName, int fUseHost ); +/*=== abcWritePla.c ===========================================================*/ +extern int Io_WritePla( Abc_Ntk_t * pNtk, char * FileName ); +/*=== abcWriteVerilog.c =======================================================*/ +extern void Io_WriteVerilog( Abc_Ntk_t * pNtk, char * FileName ); +/*=== abcUtil.c ===============================================================*/ +extern Io_FileType_t Io_ReadFileType( char * pFileName ); +extern Abc_Ntk_t * Io_ReadNetlist( char * pFileName, Io_FileType_t FileType, int fCheck ); +extern Abc_Ntk_t * Io_Read( char * pFileName, Io_FileType_t FileType, int fCheck ); +extern void Io_Write( Abc_Ntk_t * pNtk, char * pFileName, Io_FileType_t FileType ); +extern void Io_WriteHie( Abc_Ntk_t * pNtk, char * pBaseName, char * pFileName ); +extern Abc_Obj_t * Io_ReadCreatePi( Abc_Ntk_t * pNtk, char * pName ); +extern Abc_Obj_t * Io_ReadCreatePo( Abc_Ntk_t * pNtk, char * pName ); +extern Abc_Obj_t * Io_ReadCreateAssert( Abc_Ntk_t * pNtk, char * pName ); +extern Abc_Obj_t * Io_ReadCreateLatch( Abc_Ntk_t * pNtk, char * pNetLI, char * pNetLO ); +extern Abc_Obj_t * Io_ReadCreateResetLatch( Abc_Ntk_t * pNtk, int fBlifMv ); +extern Abc_Obj_t * Io_ReadCreateResetMux( Abc_Ntk_t * pNtk, char * pResetLO, char * pDataLI, int fBlifMv ); +extern Abc_Obj_t * Io_ReadCreateNode( Abc_Ntk_t * pNtk, char * pNameOut, char * pNamesIn[], int nInputs ); +extern Abc_Obj_t * Io_ReadCreateConst( Abc_Ntk_t * pNtk, char * pName, bool fConst1 ); +extern Abc_Obj_t * Io_ReadCreateInv( Abc_Ntk_t * pNtk, char * pNameIn, char * pNameOut ); +extern Abc_Obj_t * Io_ReadCreateBuf( Abc_Ntk_t * pNtk, char * pNameIn, char * pNameOut ); +extern FILE * Io_FileOpen( const char * FileName, const char * PathVar, const char * Mode, int fVerbose ); + + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/base/io/ioInt.h b/abc_with_bb_support/src/base/io/ioInt.h new file mode 100644 index 000000000..6cfd5d0e7 --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioInt.h @@ -0,0 +1,49 @@ +/**CFile**************************************************************** + + FileName [ioInt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: ioInt.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __IO_INT_H__ +#define __IO_INT_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/base/io/ioReadAiger.c b/abc_with_bb_support/src/base/io/ioReadAiger.c new file mode 100644 index 000000000..60dc87a13 --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioReadAiger.c @@ -0,0 +1,278 @@ +/**CFile**************************************************************** + + FileName [ioReadAiger.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures to read binary AIGER format developed by + Armin Biere, Johannes Kepler University (http://fmv.jku.at/)] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - December 16, 2006.] + + Revision [$Id: ioReadAiger.c,v 1.00 2006/12/16 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +unsigned Io_ReadAigerDecode( char ** ppPos ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Reads the AIG in the binary AIGER format.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Io_ReadAiger( char * pFileName, int fCheck ) +{ + ProgressBar * pProgress; + FILE * pFile; + Vec_Ptr_t * vNodes, * vTerms; + Abc_Obj_t * pObj, * pNode0, * pNode1; + Abc_Ntk_t * pNtkNew; + int nTotal, nInputs, nOutputs, nLatches, nAnds, nFileSize, iTerm, nDigits, i; + char * pContents, * pDrivers, * pSymbols, * pCur, * pName, * pType; + unsigned uLit0, uLit1, uLit; + + // read the file into the buffer + nFileSize = Extra_FileSize( pFileName ); + pFile = fopen( pFileName, "rb" ); + pContents = ALLOC( char, nFileSize ); + fread( pContents, nFileSize, 1, pFile ); + fclose( pFile ); + + // check if the input file format is correct + if ( strncmp(pContents, "aig", 3) != 0 ) + { + fprintf( stdout, "Wrong input file format.\n" ); + return NULL; + } + + // allocate the empty AIG + pNtkNew = Abc_NtkAlloc( ABC_NTK_STRASH, ABC_FUNC_AIG, 1 ); + pName = Extra_FileNameGeneric( pFileName ); + pNtkNew->pName = Extra_UtilStrsav( pName ); + pNtkNew->pSpec = Extra_UtilStrsav( pFileName ); + free( pName ); + + + // read the file type + pCur = pContents; while ( *pCur++ != ' ' ); + // read the number of objects + nTotal = atoi( pCur ); while ( *pCur++ != ' ' ); + // read the number of inputs + nInputs = atoi( pCur ); while ( *pCur++ != ' ' ); + // read the number of latches + nLatches = atoi( pCur ); while ( *pCur++ != ' ' ); + // read the number of outputs + nOutputs = atoi( pCur ); while ( *pCur++ != ' ' ); + // read the number of nodes + nAnds = atoi( pCur ); while ( *pCur++ != '\n' ); + // check the parameters + if ( nTotal != nInputs + nLatches + nAnds ) + { + fprintf( stdout, "The paramters are wrong.\n" ); + return NULL; + } + + // prepare the array of nodes + vNodes = Vec_PtrAlloc( 1 + nInputs + nLatches + nAnds ); + Vec_PtrPush( vNodes, Abc_ObjNot( Abc_AigConst1(pNtkNew) ) ); + + // create the PIs + for ( i = 0; i < nInputs; i++ ) + { + pObj = Abc_NtkCreatePi(pNtkNew); + Vec_PtrPush( vNodes, pObj ); + } + // create the POs + for ( i = 0; i < nOutputs; i++ ) + { + pObj = Abc_NtkCreatePo(pNtkNew); + } + // create the latches + nDigits = Extra_Base10Log( nLatches ); + for ( i = 0; i < nLatches; i++ ) + { + pObj = Abc_NtkCreateLatch(pNtkNew); + Abc_LatchSetInit0( pObj ); + pNode0 = Abc_NtkCreateBi(pNtkNew); + pNode1 = Abc_NtkCreateBo(pNtkNew); + Abc_ObjAddFanin( pObj, pNode0 ); + Abc_ObjAddFanin( pNode1, pObj ); + Vec_PtrPush( vNodes, pNode1 ); + // assign names to latch and its input + Abc_ObjAssignName( pObj, Abc_ObjNameDummy("_L", i, nDigits), NULL ); + +// printf( "Creating latch %s with input %d and output %d.\n", Abc_ObjName(pObj), pNode0->Id, pNode1->Id ); + } + + // remember the beginning of latch/PO literals + pDrivers = pCur; + + // scroll to the beginning of the binary data + for ( i = 0; i < nLatches + nOutputs; ) + if ( *pCur++ == '\n' ) + i++; + + // create the AND gates + pProgress = Extra_ProgressBarStart( stdout, nAnds ); + for ( i = 0; i < nAnds; i++ ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + uLit = ((i + 1 + nInputs + nLatches) << 1); + uLit1 = uLit - Io_ReadAigerDecode( &pCur ); + uLit0 = uLit1 - Io_ReadAigerDecode( &pCur ); +// assert( uLit1 > uLit0 ); + pNode0 = Abc_ObjNotCond( Vec_PtrEntry(vNodes, uLit0 >> 1), uLit0 & 1 ); + pNode1 = Abc_ObjNotCond( Vec_PtrEntry(vNodes, uLit1 >> 1), uLit1 & 1 ); + assert( Vec_PtrSize(vNodes) == i + 1 + nInputs + nLatches ); + Vec_PtrPush( vNodes, Abc_AigAnd(pNtkNew->pManFunc, pNode0, pNode1) ); + } + Extra_ProgressBarStop( pProgress ); + + // remember the place where symbols begin + pSymbols = pCur; + + // read the latch driver literals + pCur = pDrivers; + Abc_NtkForEachLatchInput( pNtkNew, pObj, i ) + { + uLit0 = atoi( pCur ); while ( *pCur++ != '\n' ); + pNode0 = Abc_ObjNotCond( Vec_PtrEntry(vNodes, uLit0 >> 1), (uLit0 & 1) );//^ (uLit0 < 2) ); + Abc_ObjAddFanin( pObj, pNode0 ); + +// printf( "Adding input %d to latch input %d.\n", pNode0->Id, pObj->Id ); + + } + // read the PO driver literals + Abc_NtkForEachPo( pNtkNew, pObj, i ) + { + uLit0 = atoi( pCur ); while ( *pCur++ != '\n' ); + pNode0 = Abc_ObjNotCond( Vec_PtrEntry(vNodes, uLit0 >> 1), (uLit0 & 1) );//^ (uLit0 < 2) ); + Abc_ObjAddFanin( pObj, pNode0 ); + } + + // read the names if present + pCur = pSymbols; + while ( pCur < pContents + nFileSize && *pCur != 'c' ) + { + // get the terminal type + pType = pCur; + if ( *pCur == 'i' ) + vTerms = pNtkNew->vPis; + else if ( *pCur == 'l' ) + vTerms = pNtkNew->vBoxes; + else if ( *pCur == 'o' ) + vTerms = pNtkNew->vPos; + else + { + fprintf( stdout, "Wrong terminal type.\n" ); + return NULL; + } + // get the terminal number + iTerm = atoi( ++pCur ); while ( *pCur++ != ' ' ); + // get the node + if ( iTerm >= Vec_PtrSize(vTerms) ) + { + fprintf( stdout, "The number of terminal is out of bound.\n" ); + return NULL; + } + pObj = Vec_PtrEntry( vTerms, iTerm ); + if ( *pType == 'l' ) + pObj = Abc_ObjFanout0(pObj); + // assign the name + pName = pCur; while ( *pCur++ != '\n' ); + // assign this name + *(pCur-1) = 0; + Abc_ObjAssignName( pObj, pName, NULL ); + if ( *pType == 'l' ) + Abc_ObjAssignName( Abc_ObjFanin0(Abc_ObjFanin0(pObj)), Abc_ObjName(pObj), "L" ); + // mark the node as named + pObj->pCopy = (Abc_Obj_t *)Abc_ObjName(pObj); + } + // skipping the comments + free( pContents ); + Vec_PtrFree( vNodes ); + + // assign the remaining names + Abc_NtkForEachPi( pNtkNew, pObj, i ) + { + if ( pObj->pCopy ) continue; + Abc_ObjAssignName( pObj, Abc_ObjName(pObj), NULL ); + } + Abc_NtkForEachLatchOutput( pNtkNew, pObj, i ) + { + if ( pObj->pCopy ) continue; + Abc_ObjAssignName( pObj, Abc_ObjName(pObj), NULL ); + Abc_ObjAssignName( Abc_ObjFanin0(Abc_ObjFanin0(pObj)), Abc_ObjName(pObj), NULL ); + } + Abc_NtkForEachPo( pNtkNew, pObj, i ) + { + if ( pObj->pCopy ) continue; + Abc_ObjAssignName( pObj, Abc_ObjName(pObj), NULL ); + } + + // remove the extra nodes + Abc_AigCleanup( pNtkNew->pManFunc ); + + // check the result + if ( fCheck && !Abc_NtkCheckRead( pNtkNew ) ) + { + printf( "Io_ReadAiger: The network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; +} + + +/**Function************************************************************* + + Synopsis [Extracts one unsigned AIG edge from the input buffer.] + + Description [This procedure is a slightly modified version of Armin Biere's + procedure "unsigned decode (FILE * file)". ] + + SideEffects [Updates the current reading position.] + + SeeAlso [] + +***********************************************************************/ +unsigned Io_ReadAigerDecode( char ** ppPos ) +{ + unsigned x = 0, i = 0; + unsigned char ch; + +// while ((ch = getnoneofch (file)) & 0x80) + while ((ch = *(*ppPos)++) & 0x80) + x |= (ch & 0x7f) << (7 * i++); + + return x | (ch << (7 * i)); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/io/ioReadBaf.c b/abc_with_bb_support/src/base/io/ioReadBaf.c new file mode 100644 index 000000000..4f6d61524 --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioReadBaf.c @@ -0,0 +1,171 @@ +/**CFile**************************************************************** + + FileName [ioReadBaf.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures to read AIG in the binary format.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: ioReadBaf.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Reads the AIG in the binary format.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Io_ReadBaf( char * pFileName, int fCheck ) +{ + ProgressBar * pProgress; + FILE * pFile; + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj, * pNode0, * pNode1; + Abc_Ntk_t * pNtkNew; + int nInputs, nOutputs, nLatches, nAnds, nFileSize, Num, i; + char * pContents, * pName, * pCur; + unsigned * pBufferNode; + + // read the file into the buffer + nFileSize = Extra_FileSize( pFileName ); + pFile = fopen( pFileName, "rb" ); + pContents = ALLOC( char, nFileSize ); + fread( pContents, nFileSize, 1, pFile ); + fclose( pFile ); + + // skip the comments (comment lines begin with '#' and end with '\n') + for ( pCur = pContents; *pCur == '#'; ) + while ( *pCur++ != '\n' ); + + // read the name + pName = pCur; while ( *pCur++ ); + // read the number of inputs + nInputs = atoi( pCur ); while ( *pCur++ ); + // read the number of outputs + nOutputs = atoi( pCur ); while ( *pCur++ ); + // read the number of latches + nLatches = atoi( pCur ); while ( *pCur++ ); + // read the number of nodes + nAnds = atoi( pCur ); while ( *pCur++ ); + + // allocate the empty AIG + pNtkNew = Abc_NtkAlloc( ABC_NTK_STRASH, ABC_FUNC_AIG, 1 ); + pNtkNew->pName = Extra_UtilStrsav( pName ); + pNtkNew->pSpec = Extra_UtilStrsav( pFileName ); + + // prepare the array of nodes + vNodes = Vec_PtrAlloc( 1 + nInputs + nLatches + nAnds ); + Vec_PtrPush( vNodes, Abc_AigConst1(pNtkNew) ); + + // create the PIs + for ( i = 0; i < nInputs; i++ ) + { + pObj = Abc_NtkCreatePi(pNtkNew); + Abc_ObjAssignName( pObj, pCur, NULL ); while ( *pCur++ ); + Vec_PtrPush( vNodes, pObj ); + } + // create the POs + for ( i = 0; i < nOutputs; i++ ) + { + pObj = Abc_NtkCreatePo(pNtkNew); + Abc_ObjAssignName( pObj, pCur, NULL ); while ( *pCur++ ); + } + // create the latches + for ( i = 0; i < nLatches; i++ ) + { + pObj = Abc_NtkCreateLatch(pNtkNew); + Abc_ObjAssignName( pObj, pCur, NULL ); while ( *pCur++ ); + + pNode0 = Abc_NtkCreateBi(pNtkNew); + Abc_ObjAssignName( pNode0, pCur, NULL ); while ( *pCur++ ); + + pNode1 = Abc_NtkCreateBo(pNtkNew); + Abc_ObjAssignName( pNode1, pCur, NULL ); while ( *pCur++ ); + Vec_PtrPush( vNodes, pNode1 ); + + Abc_ObjAddFanin( pObj, pNode0 ); + Abc_ObjAddFanin( pNode1, pObj ); + } + + // get the pointer to the beginning of the node array + pBufferNode = (int *)(pContents + (nFileSize - (2 * nAnds + nOutputs + nLatches) * sizeof(int)) ); + // make sure we are at the place where the nodes begin + if ( pBufferNode != (int *)pCur ) + { + free( pContents ); + Vec_PtrFree( vNodes ); + Abc_NtkDelete( pNtkNew ); + printf( "Warning: Internal reader error.\n" ); + return NULL; + } + + // create the AND gates + pProgress = Extra_ProgressBarStart( stdout, nAnds ); + for ( i = 0; i < nAnds; i++ ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + pNode0 = Abc_ObjNotCond( Vec_PtrEntry(vNodes, pBufferNode[2*i+0] >> 1), pBufferNode[2*i+0] & 1 ); + pNode1 = Abc_ObjNotCond( Vec_PtrEntry(vNodes, pBufferNode[2*i+1] >> 1), pBufferNode[2*i+1] & 1 ); + Vec_PtrPush( vNodes, Abc_AigAnd(pNtkNew->pManFunc, pNode0, pNode1) ); + } + Extra_ProgressBarStop( pProgress ); + + // read the POs + Abc_NtkForEachCo( pNtkNew, pObj, i ) + { + Num = pBufferNode[2*nAnds+i]; + if ( Abc_ObjFanoutNum(pObj) > 0 && Abc_ObjIsLatch(Abc_ObjFanout0(pObj)) ) + { + Abc_ObjSetData( Abc_ObjFanout0(pObj), (void *)(Num & 3) ); + Num >>= 2; + } + pNode0 = Abc_ObjNotCond( Vec_PtrEntry(vNodes, Num >> 1), Num & 1 ); + Abc_ObjAddFanin( pObj, pNode0 ); + } + free( pContents ); + Vec_PtrFree( vNodes ); + + // remove the extra nodes +// Abc_AigCleanup( pNtkNew->pManFunc ); + + // check the result + if ( fCheck && !Abc_NtkCheckRead( pNtkNew ) ) + { + printf( "Io_ReadBaf: The network check has failed.\n" ); + Abc_NtkDelete( pNtkNew ); + return NULL; + } + return pNtkNew; + +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/io/ioReadBench.c b/abc_with_bb_support/src/base/io/ioReadBench.c new file mode 100644 index 000000000..b9f21e3e2 --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioReadBench.c @@ -0,0 +1,277 @@ +/**CFile**************************************************************** + + FileName [ioReadBench.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures to read BENCH files.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: ioReadBench.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Abc_Ntk_t * Io_ReadBenchNetwork( Extra_FileReader_t * p ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Reads the network from a BENCH file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Io_ReadBench( char * pFileName, int fCheck ) +{ + Extra_FileReader_t * p; + Abc_Ntk_t * pNtk; + + // start the file + p = Extra_FileReaderAlloc( pFileName, "#", "\n\r", " \t,()=" ); + if ( p == NULL ) + return NULL; + + // read the network + pNtk = Io_ReadBenchNetwork( p ); + Extra_FileReaderFree( p ); + if ( pNtk == NULL ) + return NULL; + + // make sure that everything is okay with the network structure + if ( fCheck && !Abc_NtkCheckRead( pNtk ) ) + { + printf( "Io_ReadBench: The network check has failed.\n" ); + Abc_NtkDelete( pNtk ); + return NULL; + } + return pNtk; +} +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Io_ReadBenchNetwork( Extra_FileReader_t * p ) +{ + ProgressBar * pProgress; + Vec_Ptr_t * vTokens; + Abc_Ntk_t * pNtk; + Abc_Obj_t * pNode; + Vec_Str_t * vString; + unsigned uTruth[8]; + char * pType, ** ppNames, * pString; + int iLine, nNames, nDigits, fLutsPresent = 0; + + // allocate the empty network + pNtk = Abc_NtkStartRead( Extra_FileReaderGetFileName(p) ); + + // go through the lines of the file + vString = Vec_StrAlloc( 100 ); + pProgress = Extra_ProgressBarStart( stdout, Extra_FileReaderGetFileSize(p) ); + for ( iLine = 0; vTokens = Extra_FileReaderGetTokens(p); iLine++ ) + { + Extra_ProgressBarUpdate( pProgress, Extra_FileReaderGetCurPosition(p), NULL ); + + if ( vTokens->nSize == 1 ) + { + printf( "%s: Wrong input file format.\n", Extra_FileReaderGetFileName(p) ); + Vec_StrFree( vString ); + Abc_NtkDelete( pNtk ); + return NULL; + } + + // get the type of the line + if ( strncmp( vTokens->pArray[0], "INPUT", 5 ) == 0 ) + Io_ReadCreatePi( pNtk, vTokens->pArray[1] ); + else if ( strncmp( vTokens->pArray[0], "OUTPUT", 5 ) == 0 ) + Io_ReadCreatePo( pNtk, vTokens->pArray[1] ); + else + { + // get the node name and the node type + pType = vTokens->pArray[1]; + if ( strncmp(pType, "DFF", 3) == 0 ) // works for both DFF and DFFRSE + { + pNode = Io_ReadCreateLatch( pNtk, vTokens->pArray[2], vTokens->pArray[0] ); +// Abc_LatchSetInit0( pNode ); + if ( pType[3] == '0' ) + Abc_LatchSetInit0( pNode ); + else if ( pType[3] == '1' ) + Abc_LatchSetInit1( pNode ); + else + Abc_LatchSetInitDc( pNode ); + } + else if ( strcmp(pType, "LUT") == 0 ) + { + fLutsPresent = 1; + ppNames = (char **)vTokens->pArray + 3; + nNames = vTokens->nSize - 3; + // check the number of inputs + if ( nNames > 8 ) + { + printf( "%s: Currently cannot read truth tables with more than 8 inputs (%d).\n", Extra_FileReaderGetFileName(p), nNames ); + Vec_StrFree( vString ); + Abc_NtkDelete( pNtk ); + return NULL; + } + // get the hex string + pString = vTokens->pArray[2]; + if ( strncmp( pString, "0x", 2 ) ) + { + printf( "%s: The LUT signature (%s) does not look like a hexadecimal beginning with \"0x\".\n", Extra_FileReaderGetFileName(p), pString ); + Vec_StrFree( vString ); + Abc_NtkDelete( pNtk ); + return NULL; + } + pString += 2; + // pad the string with zero's if needed + nDigits = (1 << nNames) / 4; + if ( nDigits == 0 ) + nDigits = 1; + if ( strlen(pString) < (unsigned)nDigits ) + { + Vec_StrFill( vString, nDigits - strlen(pString), '0' ); + Vec_StrPrintStr( vString, pString ); + Vec_StrPush( vString, 0 ); + pString = Vec_StrArray( vString ); + } + // read the hex number from the string + if ( !Extra_ReadHexadecimal( uTruth, pString, nNames ) ) + { + printf( "%s: Reading hexadecimal number (%s) has failed.\n", Extra_FileReaderGetFileName(p), pString ); + Vec_StrFree( vString ); + Abc_NtkDelete( pNtk ); + return NULL; + } + // check if the node is a constant node + if ( Extra_TruthIsConst0(uTruth, nNames) ) + { + pNode = Io_ReadCreateNode( pNtk, vTokens->pArray[0], ppNames, 0 ); + Abc_ObjSetData( pNode, Abc_SopRegister( pNtk->pManFunc, " 0\n" ) ); + } + else if ( Extra_TruthIsConst1(uTruth, nNames) ) + { + pNode = Io_ReadCreateNode( pNtk, vTokens->pArray[0], ppNames, 0 ); + Abc_ObjSetData( pNode, Abc_SopRegister( pNtk->pManFunc, " 1\n" ) ); + } + else + { + // create the node + pNode = Io_ReadCreateNode( pNtk, vTokens->pArray[0], ppNames, nNames ); + assert( nNames > 0 ); + if ( nNames > 1 ) + Abc_ObjSetData( pNode, Abc_SopCreateFromTruth(pNtk->pManFunc, nNames, uTruth) ); + else if ( pString[0] == '2' ) + Abc_ObjSetData( pNode, Abc_SopCreateBuf(pNtk->pManFunc) ); + else if ( pString[0] == '1' ) + Abc_ObjSetData( pNode, Abc_SopCreateInv(pNtk->pManFunc) ); + else + { + printf( "%s: Reading truth table (%s) of single-input node has failed.\n", Extra_FileReaderGetFileName(p), pString ); + Vec_StrFree( vString ); + Abc_NtkDelete( pNtk ); + return NULL; + } + } + } + else + { + // create a new node and add it to the network + ppNames = (char **)vTokens->pArray + 2; + nNames = vTokens->nSize - 2; + pNode = Io_ReadCreateNode( pNtk, vTokens->pArray[0], ppNames, nNames ); + // assign the cover + if ( strcmp(pType, "AND") == 0 ) + Abc_ObjSetData( pNode, Abc_SopCreateAnd(pNtk->pManFunc, nNames, NULL) ); + else if ( strcmp(pType, "OR") == 0 ) + Abc_ObjSetData( pNode, Abc_SopCreateOr(pNtk->pManFunc, nNames, NULL) ); + else if ( strcmp(pType, "NAND") == 0 ) + Abc_ObjSetData( pNode, Abc_SopCreateNand(pNtk->pManFunc, nNames) ); + else if ( strcmp(pType, "NOR") == 0 ) + Abc_ObjSetData( pNode, Abc_SopCreateNor(pNtk->pManFunc, nNames) ); + else if ( strcmp(pType, "XOR") == 0 ) + Abc_ObjSetData( pNode, Abc_SopCreateXor(pNtk->pManFunc, nNames) ); + else if ( strcmp(pType, "NXOR") == 0 || strcmp(pType, "XNOR") == 0 ) + Abc_ObjSetData( pNode, Abc_SopCreateNxor(pNtk->pManFunc, nNames) ); + else if ( strncmp(pType, "BUF", 3) == 0 ) + Abc_ObjSetData( pNode, Abc_SopCreateBuf(pNtk->pManFunc) ); + else if ( strcmp(pType, "NOT") == 0 ) + Abc_ObjSetData( pNode, Abc_SopCreateInv(pNtk->pManFunc) ); + else if ( strncmp(pType, "MUX", 3) == 0 ) + Abc_ObjSetData( pNode, Abc_SopRegister(pNtk->pManFunc, "1-0 1\n-11 1\n") ); + else if ( strncmp(pType, "gnd", 3) == 0 ) + Abc_ObjSetData( pNode, Abc_SopRegister( pNtk->pManFunc, " 0\n" ) ); + else if ( strncmp(pType, "vdd", 3) == 0 ) + Abc_ObjSetData( pNode, Abc_SopRegister( pNtk->pManFunc, " 1\n" ) ); + else + { + printf( "Io_ReadBenchNetwork(): Cannot determine gate type \"%s\" in line %d.\n", pType, Extra_FileReaderGetLineNumber(p, 0) ); + Vec_StrFree( vString ); + Abc_NtkDelete( pNtk ); + return NULL; + } + } + } + } + Extra_ProgressBarStop( pProgress ); + Vec_StrFree( vString ); + + // check if constant have been added +// if ( pNet = Abc_NtkFindNet( pNtk, "vdd" ) ) +// Io_ReadCreateConst( pNtk, "vdd", 1 ); +// if ( pNet = Abc_NtkFindNet( pNtk, "gnd" ) ) +// Io_ReadCreateConst( pNtk, "gnd", 0 ); + + Abc_NtkFinalizeRead( pNtk ); + + // if LUTs are present, collapse the truth tables into cubes + if ( fLutsPresent ) + { + if ( !Abc_NtkToBdd(pNtk) ) + { + printf( "Io_ReadBenchNetwork(): Converting to BDD has failed.\n" ); + Abc_NtkDelete( pNtk ); + return NULL; + } + if ( !Abc_NtkToSop(pNtk, 0) ) + { + printf( "Io_ReadBenchNetwork(): Converting to SOP has failed.\n" ); + Abc_NtkDelete( pNtk ); + return NULL; + } + } + return pNtk; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + + diff --git a/abc_with_bb_support/src/base/io/ioReadBlif.c b/abc_with_bb_support/src/base/io/ioReadBlif.c new file mode 100644 index 000000000..409df1e93 --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioReadBlif.c @@ -0,0 +1,1131 @@ +/**CFile**************************************************************** + + FileName [ioReadBlif.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures to read BLIF files.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: ioReadBlif.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" +#include "main.h" +#include "mio.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Io_ReadBlif_t_ Io_ReadBlif_t; // all reading info +struct Io_ReadBlif_t_ +{ + // general info about file + char * pFileName; // the name of the file + Extra_FileReader_t * pReader; // the input file reader + // current processing info + Abc_Ntk_t * pNtkMaster; // the primary network + Abc_Ntk_t * pNtkCur; // the primary network + int LineCur; // the line currently parsed + // temporary storage for tokens + Vec_Ptr_t * vTokens; // the current tokens + Vec_Ptr_t * vNewTokens; // the temporary storage for the tokens + Vec_Str_t * vCubes; // the temporary storage for the tokens + // the error message + FILE * Output; // the output stream + char sError[1000]; // the error string generated during parsing + int fError; // set to 1 when error occurs +}; + +static Io_ReadBlif_t * Io_ReadBlifFile( char * pFileName ); +static void Io_ReadBlifFree( Io_ReadBlif_t * p ); +static void Io_ReadBlifPrintErrorMessage( Io_ReadBlif_t * p ); +static Vec_Ptr_t * Io_ReadBlifGetTokens( Io_ReadBlif_t * p ); +static char * Io_ReadBlifCleanName( char * pName ); + +static Abc_Ntk_t * Io_ReadBlifNetwork( Io_ReadBlif_t * p ); +static Abc_Ntk_t * Io_ReadBlifNetworkOne( Io_ReadBlif_t * p ); +static int Io_ReadBlifNetworkInputs( Io_ReadBlif_t * p, Vec_Ptr_t * vTokens ); +static int Io_ReadBlifNetworkOutputs( Io_ReadBlif_t * p, Vec_Ptr_t * vTokens ); +static int Io_ReadBlifNetworkAsserts( Io_ReadBlif_t * p, Vec_Ptr_t * vTokens ); +static int Io_ReadBlifNetworkLatch( Io_ReadBlif_t * p, Vec_Ptr_t * vTokens ); +static int Io_ReadBlifNetworkNames( Io_ReadBlif_t * p, Vec_Ptr_t ** pvTokens ); +static int Io_ReadBlifNetworkGate( Io_ReadBlif_t * p, Vec_Ptr_t * vTokens ); +static int Io_ReadBlifNetworkSubcircuit( Io_ReadBlif_t * p, Vec_Ptr_t * vTokens ); +static int Io_ReadBlifNetworkInputArrival( Io_ReadBlif_t * p, Vec_Ptr_t * vTokens ); +static int Io_ReadBlifNetworkDefaultInputArrival( Io_ReadBlif_t * p, Vec_Ptr_t * vTokens ); +static int Io_ReadBlifNetworkConnectBoxes( Io_ReadBlif_t * p, Abc_Ntk_t * pNtkMaster ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Reads the (hierarchical) network from the BLIF file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Io_ReadBlif( char * pFileName, int fCheck ) +{ + Io_ReadBlif_t * p; + Abc_Ntk_t * pNtk; + + // start the file + p = Io_ReadBlifFile( pFileName ); + if ( p == NULL ) + return NULL; + + // read the hierarchical network + pNtk = Io_ReadBlifNetwork( p ); + if ( pNtk == NULL ) + { + Io_ReadBlifFree( p ); + return NULL; + } + pNtk->pSpec = Extra_UtilStrsav( pFileName ); + Abc_NtkTimeInitialize( pNtk ); + Io_ReadBlifFree( p ); + + // make sure that everything is okay with the network structure + if ( fCheck && !Abc_NtkCheckRead( pNtk ) ) + { + printf( "Io_ReadBlif: The network check has failed.\n" ); + Abc_NtkDelete( pNtk ); + return NULL; + } + return pNtk; +} + +/**Function************************************************************* + + Synopsis [Iteratively reads several networks in the hierarchical design.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Io_ReadBlifNetwork( Io_ReadBlif_t * p ) +{ + Abc_Ntk_t * pNtk, * pNtkMaster; + + // read the name of the master network + p->vTokens = Io_ReadBlifGetTokens(p); + if ( p->vTokens == NULL || strcmp( p->vTokens->pArray[0], ".model" ) ) + { + p->LineCur = 0; + sprintf( p->sError, "Wrong input file format." ); + Io_ReadBlifPrintErrorMessage( p ); + return NULL; + } + + // read networks (with EXDC) + pNtkMaster = NULL; + while ( p->vTokens ) + { + // read the network and its EXDC if present + pNtk = Io_ReadBlifNetworkOne( p ); + if ( pNtk == NULL ) + break; + if ( p->vTokens && strcmp(p->vTokens->pArray[0], ".exdc") == 0 ) + { + pNtk->pExdc = Io_ReadBlifNetworkOne( p ); + Abc_NtkFinalizeRead( pNtk->pExdc ); + if ( pNtk->pExdc == NULL ) + break; + } + // add this network as part of the hierarchy + if ( pNtkMaster == NULL ) // no master network so far + { + p->pNtkMaster = pNtkMaster = pNtk; + continue; + } +/* + // make sure hierarchy does not have the network with this name + if ( pNtkMaster->tName2Model && stmm_is_member( pNtkMaster->tName2Model, pNtk->pName ) ) + { + p->LineCur = 0; + sprintf( p->sError, "Model %s is multiply defined in the file.", pNtk->pName ); + Io_ReadBlifPrintErrorMessage( p ); + Abc_NtkDelete( pNtk ); + Abc_NtkDelete( pNtkMaster ); + pNtkMaster = NULL; + return NULL; + } + // add the network to the hierarchy + if ( pNtkMaster->tName2Model == NULL ) + pNtkMaster->tName2Model = stmm_init_table(strcmp, stmm_strhash); + stmm_insert( pNtkMaster->tName2Model, pNtk->pName, (char *)pNtk ); +*/ + } +/* + // if there is a hierarchy, connect the boxes + if ( pNtkMaster && pNtkMaster->tName2Model ) + { + if ( Io_ReadBlifNetworkConnectBoxes( p, pNtkMaster ) ) + { + Abc_NtkDelete( pNtkMaster ); + return NULL; + } + } + else +*/ + if ( !p->fError ) + Abc_NtkFinalizeRead( pNtkMaster ); + // return the master network + return pNtkMaster; +} + +/**Function************************************************************* + + Synopsis [Reads one (main or exdc) network from the BLIF file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Io_ReadBlifNetworkOne( Io_ReadBlif_t * p ) +{ + ProgressBar * pProgress; + Abc_Ntk_t * pNtk; + char * pDirective; + int iLine, fTokensReady, fStatus; + + // make sure the tokens are present + assert( p->vTokens != NULL ); + + // create the new network + p->pNtkCur = pNtk = Abc_NtkAlloc( ABC_NTK_NETLIST, ABC_FUNC_SOP, 1 ); + // read the model name + if ( strcmp( p->vTokens->pArray[0], ".model" ) == 0 ) + pNtk->pName = Extra_UtilStrsav( p->vTokens->pArray[1] ); + else if ( strcmp( p->vTokens->pArray[0], ".exdc" ) != 0 ) + { + printf( "%s: File parsing skipped after line %d (\"%s\").\n", p->pFileName, + Extra_FileReaderGetLineNumber(p->pReader, 0), p->vTokens->pArray[0] ); + Abc_NtkDelete(pNtk); + p->pNtkCur = NULL; + return NULL; + } + + // read the inputs/outputs + if ( p->pNtkMaster == NULL ) + pProgress = Extra_ProgressBarStart( stdout, Extra_FileReaderGetFileSize(p->pReader) ); + fTokensReady = fStatus = 0; + for ( iLine = 0; fTokensReady || (p->vTokens = Io_ReadBlifGetTokens(p)); iLine++ ) + { + if ( p->pNtkMaster == NULL && iLine % 1000 == 0 ) + Extra_ProgressBarUpdate( pProgress, Extra_FileReaderGetCurPosition(p->pReader), NULL ); + + // consider different line types + fTokensReady = 0; + pDirective = p->vTokens->pArray[0]; + if ( !strcmp( pDirective, ".names" ) ) + { fStatus = Io_ReadBlifNetworkNames( p, &p->vTokens ); fTokensReady = 1; } + else if ( !strcmp( pDirective, ".gate" ) ) + fStatus = Io_ReadBlifNetworkGate( p, p->vTokens ); + else if ( !strcmp( pDirective, ".latch" ) ) + fStatus = Io_ReadBlifNetworkLatch( p, p->vTokens ); + else if ( !strcmp( pDirective, ".inputs" ) ) + fStatus = Io_ReadBlifNetworkInputs( p, p->vTokens ); + else if ( !strcmp( pDirective, ".outputs" ) ) + fStatus = Io_ReadBlifNetworkOutputs( p, p->vTokens ); + else if ( !strcmp( pDirective, ".asserts" ) ) + fStatus = Io_ReadBlifNetworkAsserts( p, p->vTokens ); + else if ( !strcmp( pDirective, ".input_arrival" ) ) + fStatus = Io_ReadBlifNetworkInputArrival( p, p->vTokens ); + else if ( !strcmp( pDirective, ".default_input_arrival" ) ) + fStatus = Io_ReadBlifNetworkDefaultInputArrival( p, p->vTokens ); +// else if ( !strcmp( pDirective, ".subckt" ) ) +// fStatus = Io_ReadBlifNetworkSubcircuit( p, p->vTokens ); + else if ( !strcmp( pDirective, ".exdc" ) ) + break; + else if ( !strcmp( pDirective, ".end" ) ) + { + p->vTokens = Io_ReadBlifGetTokens(p); + break; + } + else if ( !strcmp( pDirective, ".blackbox" ) ) + { + pNtk->ntkType = ABC_NTK_NETLIST; + pNtk->ntkFunc = ABC_FUNC_BLACKBOX; + Extra_MmFlexStop( pNtk->pManFunc ); + pNtk->pManFunc = NULL; + } + else + printf( "%s (line %d): Skipping directive \"%s\".\n", p->pFileName, + Extra_FileReaderGetLineNumber(p->pReader, 0), pDirective ); + if ( p->vTokens == NULL ) // some files do not have ".end" in the end + break; + if ( fStatus == 1 ) + { + Extra_ProgressBarStop( pProgress ); + Abc_NtkDelete( pNtk ); + return NULL; + } + } + if ( p->pNtkMaster == NULL ) + Extra_ProgressBarStop( pProgress ); + return pNtk; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_ReadBlifNetworkInputs( Io_ReadBlif_t * p, Vec_Ptr_t * vTokens ) +{ + int i; + for ( i = 1; i < vTokens->nSize; i++ ) + Io_ReadCreatePi( p->pNtkCur, vTokens->pArray[i] ); + return 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_ReadBlifNetworkOutputs( Io_ReadBlif_t * p, Vec_Ptr_t * vTokens ) +{ + int i; + for ( i = 1; i < vTokens->nSize; i++ ) + Io_ReadCreatePo( p->pNtkCur, vTokens->pArray[i] ); + return 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_ReadBlifNetworkAsserts( Io_ReadBlif_t * p, Vec_Ptr_t * vTokens ) +{ + int i; + for ( i = 1; i < vTokens->nSize; i++ ) + Io_ReadCreateAssert( p->pNtkCur, vTokens->pArray[i] ); + return 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_ReadBlifNetworkLatch( Io_ReadBlif_t * p, Vec_Ptr_t * vTokens ) +{ + Abc_Ntk_t * pNtk = p->pNtkCur; + Abc_Obj_t * pLatch; + char * pLatchType; + Abc_LatchInfo_t* pLatchInfo; + int ResetValue; + if ( vTokens->nSize < 3 ) + { + p->LineCur = Extra_FileReaderGetLineNumber(p->pReader, 0); + sprintf( p->sError, "The .latch line does not have enough tokens." ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + // create the latch + pLatch = Io_ReadCreateLatch( pNtk, vTokens->pArray[1], vTokens->pArray[2] ); + // get the latch reset value + if ( vTokens->nSize == 3 ) + Abc_LatchSetInitDc( pLatch ); + else + { + pLatchInfo = ((Abc_LatchInfo_t *)pLatch->pData); + + if (vTokens->nSize >= 5) + { + pLatchInfo->pClkName = strdup(vTokens->pArray[4]); + pLatchType = vTokens->pArray[3]; + + if (!strcmp(pLatchType, "re")) + pLatchInfo->LatchType = ABC_RISING_EDGE; + else if (!strcmp(pLatchType, "fe")) + pLatchInfo->LatchType = ABC_FALLING_EDGE; + else if (!strcmp(pLatchType, "ah")) + pLatchInfo->LatchType = ABC_ACTIVE_HIGH; + else if (!strcmp(pLatchType, "al")) + pLatchInfo->LatchType = ABC_ACTIVE_LOW; + else + pLatchInfo->LatchType = ABC_ASYNC; // god knows when it is used... + } + else + { + pLatchInfo->pClkName = 0; + pLatchInfo->LatchType = ABC_ASYNC; + } + + ResetValue = atoi(vTokens->pArray[vTokens->nSize-1]); + if ( ResetValue != 0 && ResetValue != 1 && ResetValue != 2 ) + { + p->LineCur = Extra_FileReaderGetLineNumber(p->pReader, 0); + sprintf( p->sError, "The .latch line has an unknown reset value (%s).", vTokens->pArray[3] ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + if ( ResetValue == 0 ) + Abc_LatchSetInit0( pLatch ); + else if ( ResetValue == 1 ) + Abc_LatchSetInit1( pLatch ); + else if ( ResetValue == 2 ) + Abc_LatchSetInitDc( pLatch ); + } + return 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_ReadBlifNetworkNames( Io_ReadBlif_t * p, Vec_Ptr_t ** pvTokens ) +{ + Vec_Ptr_t * vTokens = *pvTokens; + Abc_Ntk_t * pNtk = p->pNtkCur; + Abc_Obj_t * pNode; + char * pToken, Char, ** ppNames; + int nFanins, nNames; + + // create a new node and add it to the network + if ( vTokens->nSize < 2 ) + { + p->LineCur = Extra_FileReaderGetLineNumber(p->pReader, 0); + sprintf( p->sError, "The .names line has less than two tokens." ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + + // create the node + ppNames = (char **)vTokens->pArray + 1; + nNames = vTokens->nSize - 2; + pNode = Io_ReadCreateNode( pNtk, ppNames[nNames], ppNames, nNames ); + + // derive the functionality of the node + p->vCubes->nSize = 0; + nFanins = vTokens->nSize - 2; + if ( nFanins == 0 ) + { + while ( vTokens = Io_ReadBlifGetTokens(p) ) + { + pToken = vTokens->pArray[0]; + if ( pToken[0] == '.' ) + break; + // read the cube + if ( vTokens->nSize != 1 ) + { + p->LineCur = Extra_FileReaderGetLineNumber(p->pReader, 0); + sprintf( p->sError, "The number of tokens in the constant cube is wrong." ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + // create the cube + Char = ((char *)vTokens->pArray[0])[0]; + Vec_StrPush( p->vCubes, ' ' ); + Vec_StrPush( p->vCubes, Char ); + Vec_StrPush( p->vCubes, '\n' ); + } + } + else + { + while ( vTokens = Io_ReadBlifGetTokens(p) ) + { + pToken = vTokens->pArray[0]; + if ( pToken[0] == '.' ) + break; + // read the cube + if ( vTokens->nSize != 2 ) + { + p->LineCur = Extra_FileReaderGetLineNumber(p->pReader, 0); + sprintf( p->sError, "The number of tokens in the cube is wrong." ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + // create the cube + Vec_StrAppend( p->vCubes, vTokens->pArray[0] ); + // check the char + Char = ((char *)vTokens->pArray[1])[0]; + if ( Char != '0' && Char != '1' && Char != 'x' && Char != 'n' ) + { + p->LineCur = Extra_FileReaderGetLineNumber(p->pReader, 0); + sprintf( p->sError, "The output character in the constant cube is wrong." ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + Vec_StrPush( p->vCubes, ' ' ); + Vec_StrPush( p->vCubes, Char ); + Vec_StrPush( p->vCubes, '\n' ); + } + } + // if there is nothing there + if ( p->vCubes->nSize == 0 ) + { + // create an empty cube + Vec_StrPush( p->vCubes, ' ' ); + Vec_StrPush( p->vCubes, '0' ); + Vec_StrPush( p->vCubes, '\n' ); + } + Vec_StrPush( p->vCubes, 0 ); + + // set the pointer to the functionality of the node + Abc_ObjSetData( pNode, Abc_SopRegister(pNtk->pManFunc, p->vCubes->pArray) ); + + // check the size + if ( Abc_ObjFaninNum(pNode) != Abc_SopGetVarNum(Abc_ObjData(pNode)) ) + { + p->LineCur = Extra_FileReaderGetLineNumber(p->pReader, 0); + sprintf( p->sError, "The number of fanins (%d) of node %s is different from SOP size (%d).", + Abc_ObjFaninNum(pNode), Abc_ObjName(Abc_ObjFanout(pNode,0)), Abc_SopGetVarNum(Abc_ObjData(pNode)) ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + + // return the last array of tokens + *pvTokens = vTokens; + return 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_ReadBlifNetworkGate( Io_ReadBlif_t * p, Vec_Ptr_t * vTokens ) +{ + Mio_Library_t * pGenlib; + Mio_Gate_t * pGate; + Abc_Obj_t * pNode; + char ** ppNames; + int i, nNames; + + // check that the library is available + pGenlib = Abc_FrameReadLibGen(); + if ( pGenlib == NULL ) + { + p->LineCur = Extra_FileReaderGetLineNumber(p->pReader, 0); + sprintf( p->sError, "The current library is not available." ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + + // create a new node and add it to the network + if ( vTokens->nSize < 2 ) + { + p->LineCur = Extra_FileReaderGetLineNumber(p->pReader, 0); + sprintf( p->sError, "The .gate line has less than two tokens." ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + + // get the gate + pGate = Mio_LibraryReadGateByName( pGenlib, vTokens->pArray[1] ); + if ( pGate == NULL ) + { + p->LineCur = Extra_FileReaderGetLineNumber(p->pReader, 0); + sprintf( p->sError, "Cannot find gate \"%s\" in the library.", vTokens->pArray[1] ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + + // if this is the first line with gate, update the network type + if ( Abc_NtkNodeNum(p->pNtkCur) == 0 ) + { + assert( p->pNtkCur->ntkFunc == ABC_FUNC_SOP ); + p->pNtkCur->ntkFunc = ABC_FUNC_MAP; + Extra_MmFlexStop( p->pNtkCur->pManFunc ); + p->pNtkCur->pManFunc = pGenlib; + } + + // remove the formal parameter names + for ( i = 2; i < vTokens->nSize; i++ ) + { + vTokens->pArray[i] = Io_ReadBlifCleanName( vTokens->pArray[i] ); + if ( vTokens->pArray[i] == NULL ) + { + p->LineCur = Extra_FileReaderGetLineNumber(p->pReader, 0); + sprintf( p->sError, "Invalid gate input assignment." ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + } + + // create the node + ppNames = (char **)vTokens->pArray + 2; + nNames = vTokens->nSize - 3; + pNode = Io_ReadCreateNode( p->pNtkCur, ppNames[nNames], ppNames, nNames ); + + // set the pointer to the functionality of the node + Abc_ObjSetData( pNode, pGate ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Creates a multi-input multi-output box in the hierarchical design.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_ReadBlifNetworkSubcircuit( Io_ReadBlif_t * p, Vec_Ptr_t * vTokens ) +{ + Abc_Obj_t * pBox; + Vec_Ptr_t * vNames; + char * pName; + int i; + + // create a new node and add it to the network + if ( vTokens->nSize < 3 ) + { + p->LineCur = Extra_FileReaderGetLineNumber(p->pReader, 0); + sprintf( p->sError, "The .subcircuit line has less than three tokens." ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + + // store the names of formal/actual inputs/outputs of the box + vNames = Vec_PtrAlloc( 10 ); + Vec_PtrForEachEntryStart( vTokens, pName, i, 1 ) +// Vec_PtrPush( vNames, Abc_NtkRegisterName(p->pNtkCur, pName) ); + Vec_PtrPush( vNames, Extra_UtilStrsav(pName) ); // memory leak!!! + + // create a new box and add it to the network + pBox = Abc_NtkCreateBlackbox( p->pNtkCur ); + // set the pointer to the node names + Abc_ObjSetData( pBox, vNames ); + // remember the line of the file + pBox->pCopy = (void *)Extra_FileReaderGetLineNumber(p->pReader, 0); + return 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Io_ReadBlifCleanName( char * pName ) +{ + int i, Length; + Length = strlen(pName); + for ( i = 0; i < Length; i++ ) + if ( pName[i] == '=' ) + return pName + i + 1; + return NULL; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_ReadBlifNetworkInputArrival( Io_ReadBlif_t * p, Vec_Ptr_t * vTokens ) +{ + Abc_Obj_t * pNet; + char * pFoo1, * pFoo2; + double TimeRise, TimeFall; + + // make sure this is indeed the .inputs line + assert( strncmp( vTokens->pArray[0], ".input_arrival", 14 ) == 0 ); + if ( vTokens->nSize != 4 ) + { + p->LineCur = Extra_FileReaderGetLineNumber(p->pReader, 0); + sprintf( p->sError, "Wrong number of arguments on .input_arrival line." ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + pNet = Abc_NtkFindNet( p->pNtkCur, vTokens->pArray[1] ); + if ( pNet == NULL ) + { + p->LineCur = Extra_FileReaderGetLineNumber(p->pReader, 0); + sprintf( p->sError, "Cannot find object corresponding to %s on .input_arrival line.", vTokens->pArray[1] ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + TimeRise = strtod( vTokens->pArray[2], &pFoo1 ); + TimeFall = strtod( vTokens->pArray[3], &pFoo2 ); + if ( *pFoo1 != '\0' || *pFoo2 != '\0' ) + { + p->LineCur = Extra_FileReaderGetLineNumber(p->pReader, 0); + sprintf( p->sError, "Bad value (%s %s) for rise or fall time on .input_arrival line.", vTokens->pArray[2], vTokens->pArray[3] ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + // set the arrival time + Abc_NtkTimeSetArrival( p->pNtkCur, Abc_ObjFanin0(pNet)->Id, (float)TimeRise, (float)TimeFall ); + return 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_ReadBlifNetworkDefaultInputArrival( Io_ReadBlif_t * p, Vec_Ptr_t * vTokens ) +{ + char * pFoo1, * pFoo2; + double TimeRise, TimeFall; + + // make sure this is indeed the .inputs line + assert( strncmp( vTokens->pArray[0], ".default_input_arrival", 23 ) == 0 ); + if ( vTokens->nSize != 3 ) + { + p->LineCur = Extra_FileReaderGetLineNumber(p->pReader, 0); + sprintf( p->sError, "Wrong number of arguments on .default_input_arrival line." ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + TimeRise = strtod( vTokens->pArray[1], &pFoo1 ); + TimeFall = strtod( vTokens->pArray[2], &pFoo2 ); + if ( *pFoo1 != '\0' || *pFoo2 != '\0' ) + { + p->LineCur = Extra_FileReaderGetLineNumber(p->pReader, 0); + sprintf( p->sError, "Bad value (%s %s) for rise or fall time on .default_input_arrival line.", vTokens->pArray[1], vTokens->pArray[2] ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + // set the arrival time + Abc_NtkTimeSetDefaultArrival( p->pNtkCur, (float)TimeRise, (float)TimeFall ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Prints the error message including the file name and line number.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_ReadBlifPrintErrorMessage( Io_ReadBlif_t * p ) +{ + p->fError = 1; + if ( p->LineCur == 0 ) // the line number is not given + fprintf( p->Output, "%s: %s\n", p->pFileName, p->sError ); + else // print the error message with the line number + fprintf( p->Output, "%s (line %d): %s\n", p->pFileName, p->LineCur, p->sError ); +} + +/**Function************************************************************* + + Synopsis [Gets the tokens taking into account the line breaks.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Io_ReadBlifGetTokens( Io_ReadBlif_t * p ) +{ + Vec_Ptr_t * vTokens; + char * pLastToken; + int i; + + // get rid of the old tokens + if ( p->vNewTokens->nSize > 0 ) + { + for ( i = 0; i < p->vNewTokens->nSize; i++ ) + free( p->vNewTokens->pArray[i] ); + p->vNewTokens->nSize = 0; + } + + // get the new tokens + vTokens = Extra_FileReaderGetTokens(p->pReader); + if ( vTokens == NULL ) + return vTokens; + + // check if there is a transfer to another line + pLastToken = vTokens->pArray[vTokens->nSize - 1]; + if ( pLastToken[ strlen(pLastToken)-1 ] != '\\' ) + return vTokens; + + // remove the slash + pLastToken[ strlen(pLastToken)-1 ] = 0; + if ( pLastToken[0] == 0 ) + vTokens->nSize--; + // load them into the new array + for ( i = 0; i < vTokens->nSize; i++ ) + Vec_PtrPush( p->vNewTokens, Extra_UtilStrsav(vTokens->pArray[i]) ); + + // load as long as there is the line break + while ( 1 ) + { + // get the new tokens + vTokens = Extra_FileReaderGetTokens(p->pReader); + if ( vTokens->nSize == 0 ) + return p->vNewTokens; + // check if there is a transfer to another line + pLastToken = vTokens->pArray[vTokens->nSize - 1]; + if ( pLastToken[ strlen(pLastToken)-1 ] == '\\' ) + { + // remove the slash + pLastToken[ strlen(pLastToken)-1 ] = 0; + if ( pLastToken[0] == 0 ) + vTokens->nSize--; + // load them into the new array + for ( i = 0; i < vTokens->nSize; i++ ) + Vec_PtrPush( p->vNewTokens, Extra_UtilStrsav(vTokens->pArray[i]) ); + continue; + } + // otherwise, load them and break + for ( i = 0; i < vTokens->nSize; i++ ) + Vec_PtrPush( p->vNewTokens, Extra_UtilStrsav(vTokens->pArray[i]) ); + break; + } + return p->vNewTokens; +} + +/**Function************************************************************* + + Synopsis [Starts the reading data structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Io_ReadBlif_t * Io_ReadBlifFile( char * pFileName ) +{ + Extra_FileReader_t * pReader; + Io_ReadBlif_t * p; + + // start the reader + pReader = Extra_FileReaderAlloc( pFileName, "#", "\n\r", " \t" ); + + if ( pReader == NULL ) + return NULL; + + // start the reading data structure + p = ALLOC( Io_ReadBlif_t, 1 ); + memset( p, 0, sizeof(Io_ReadBlif_t) ); + p->pFileName = pFileName; + p->pReader = pReader; + p->Output = stdout; + p->vNewTokens = Vec_PtrAlloc( 100 ); + p->vCubes = Vec_StrAlloc( 100 ); + return p; +} + +/**Function************************************************************* + + Synopsis [Frees the data structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_ReadBlifFree( Io_ReadBlif_t * p ) +{ + Extra_FileReaderFree( p->pReader ); + Vec_PtrFree( p->vNewTokens ); + Vec_StrFree( p->vCubes ); + free( p ); +} + + +/**Function************************************************************* + + Synopsis [Connect one box.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_ReadBlifNetworkConnectBoxesOneBox( Io_ReadBlif_t * p, Abc_Obj_t * pBox, stmm_table * tName2Model ) +{ + Vec_Ptr_t * pNames; + Abc_Ntk_t * pNtkModel; + Abc_Obj_t * pObj, * pNet; + char * pName, * pActual; + int i, Length, Start; + + // get the model for this box + pNames = pBox->pData; + if ( !stmm_lookup( tName2Model, Vec_PtrEntry(pNames, 0), (char **)&pNtkModel ) ) + { + p->LineCur = (int)pBox->pCopy; + sprintf( p->sError, "Cannot find the model for subcircuit %s.", Vec_PtrEntry(pNames, 0) ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + + // create the fanins of the box + Abc_NtkForEachPi( pNtkModel, pObj, i ) + pObj->pCopy = NULL; + if ( Abc_NtkPiNum(pNtkModel) == 0 ) + Start = 1; + else + { + Vec_PtrForEachEntryStart( pNames, pName, i, 1 ) + { + pActual = Io_ReadBlifCleanName(pName); + if ( pActual == NULL ) + { + p->LineCur = (int)pBox->pCopy; + sprintf( p->sError, "Cannot parse formal/actual name pair \"%s\".", pName ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + Length = pActual - pName - 1; + pName[Length] = 0; + // find the PI net with this name + pObj = Abc_NtkFindNet( pNtkModel, pName ); + if ( pObj == NULL ) + { + p->LineCur = (int)pBox->pCopy; + sprintf( p->sError, "Cannot find formal input \"%s\" as an PI of model \"%s\".", pName, Vec_PtrEntry(pNames, 0) ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + // get the PI + pObj = Abc_ObjFanin0(pObj); + // quit if this is not a PI net + if ( !Abc_ObjIsPi(pObj) ) + { + pName[Length] = '='; + Start = i; + break; + } + // remember the actual name in the net + if ( pObj->pCopy != NULL ) + { + p->LineCur = (int)pBox->pCopy; + sprintf( p->sError, "Formal input \"%s\" is used more than once.", pName ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + pObj->pCopy = (void *)pActual; + // quit if we processed all PIs + if ( i == Abc_NtkPiNum(pNtkModel) ) + { + Start = i+1; + break; + } + } + } + // create the fanins of the box + Abc_NtkForEachPi( pNtkModel, pObj, i ) + { + pActual = (void *)pObj->pCopy; + if ( pActual == NULL ) + { + p->LineCur = (int)pBox->pCopy; + sprintf( p->sError, "Formal input \"%s\" of model %s is not driven.", pName, Vec_PtrEntry(pNames, 0) ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + pNet = Abc_NtkFindOrCreateNet( pBox->pNtk, pActual ); + Abc_ObjAddFanin( pBox, pNet ); + } + Abc_NtkForEachPi( pNtkModel, pObj, i ) + pObj->pCopy = NULL; + + // create the fanouts of the box + Abc_NtkForEachPo( pNtkModel, pObj, i ) + pObj->pCopy = NULL; + Vec_PtrForEachEntryStart( pNames, pName, i, Start ) + { + pActual = Io_ReadBlifCleanName(pName); + if ( pActual == NULL ) + { + p->LineCur = (int)pBox->pCopy; + sprintf( p->sError, "Cannot parse formal/actual name pair \"%s\".", pName ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + Length = pActual - pName - 1; + pName[Length] = 0; + // find the PO net with this name + pObj = Abc_NtkFindNet( pNtkModel, pName ); + if ( pObj == NULL ) + { + p->LineCur = (int)pBox->pCopy; + sprintf( p->sError, "Cannot find formal output \"%s\" as an PO of model \"%s\".", pName, Vec_PtrEntry(pNames, 0) ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + // get the PO + pObj = Abc_ObjFanout0(pObj); + if ( pObj->pCopy != NULL ) + { + p->LineCur = (int)pBox->pCopy; + sprintf( p->sError, "Formal output \"%s\" is used more than once.", pName ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + pObj->pCopy = (void *)pActual; + } + // create the fanouts of the box + Abc_NtkForEachPo( pNtkModel, pObj, i ) + { + pActual = (void *)pObj->pCopy; + if ( pActual == NULL ) + { + p->LineCur = (int)pBox->pCopy; + sprintf( p->sError, "Formal output \"%s\" of model %s is not driven.", pName, Vec_PtrEntry(pNames, 0) ); + Io_ReadBlifPrintErrorMessage( p ); + return 1; + } + pNet = Abc_NtkFindOrCreateNet( pBox->pNtk, pActual ); + Abc_ObjAddFanin( pNet, pBox ); + } + Abc_NtkForEachPo( pNtkModel, pObj, i ) + pObj->pCopy = NULL; + + // remove the array of names, assign the pointer to the model + Vec_PtrForEachEntry( pBox->pData, pName, i ) + free( pName ); + Vec_PtrFree( pBox->pData ); + pBox->pData = pNtkModel; + return 0; +} + +/**Function************************************************************* + + Synopsis [Connect the boxes in the hierarchy of networks.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_ReadBlifNetworkConnectBoxesOne( Io_ReadBlif_t * p, Abc_Ntk_t * pNtk, stmm_table * tName2Model ) +{ + Abc_Obj_t * pBox; + int i; + // go through the boxes + Abc_NtkForEachBlackbox( pNtk, pBox, i ) + if ( Io_ReadBlifNetworkConnectBoxesOneBox( p, pBox, tName2Model ) ) + return 1; + Abc_NtkFinalizeRead( pNtk ); + return 0; +} + +#if 0 + +/**Function************************************************************* + + Synopsis [Connect the boxes in the hierarchy of networks.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_ReadBlifNetworkConnectBoxes( Io_ReadBlif_t * p, Abc_Ntk_t * pNtkMaster ) +{ + stmm_generator * gen; + Abc_Ntk_t * pNtk; + char * pName; + // connect the master network + if ( Io_ReadBlifNetworkConnectBoxesOne( p, pNtkMaster, pNtkMaster->tName2Model ) ) + return 1; + // connect other networks + stmm_foreach_item( pNtkMaster->tName2Model, gen, &pName, (char **)&pNtk ) + if ( Io_ReadBlifNetworkConnectBoxesOne( p, pNtk, pNtkMaster->tName2Model ) ) + return 1; + return 0; +} + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/io/ioReadBlifAig.c b/abc_with_bb_support/src/base/io/ioReadBlifAig.c new file mode 100644 index 000000000..36d61532f --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioReadBlifAig.c @@ -0,0 +1,1013 @@ +/**CFile**************************************************************** + + FileName [ioReadBlifAig.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures to read BLIF file into AIG.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - December 23, 2006.] + + Revision [$Id: ioReadBlifAig.c,v 1.00 2006/12/23 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "extra.h" +#include "vecPtr.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// latch initial values +typedef enum { + IO_BLIF_INIT_NONE = 0, // 0: unknown + IO_BLIF_INIT_ZERO, // 1: zero + IO_BLIF_INIT_ONE, // 2: one + IO_BLIF_INIT_DC // 3: don't-care +} Io_BlifInit_t; + +typedef struct Io_BlifObj_t_ Io_BlifObj_t; // parsing object +struct Io_BlifObj_t_ +{ + unsigned fPi : 1; // the object is a primary input + unsigned fPo : 1; // the object is a primary output + unsigned fLi : 1; // the object is a latch input + unsigned fLo : 1; // the object is a latch output + unsigned fDef : 1; // the object is defined as a table (node, PO, LI) + unsigned fLoop : 1; // flag for loop detection + unsigned Init : 2; // the latch initial state + unsigned Offset : 24; // temporary number + char * pName; // the name of this object + void * pEquiv; // the AIG node representing this line + Io_BlifObj_t * pNext; // the next obj in the hash table +}; + +typedef struct Io_BlifMan_t_ Io_BlifMan_t; // parsing manager +struct Io_BlifMan_t_ +{ + // general info about file + char * pFileName; // the name of the file + char * pBuffer; // the begining of the file buffer + Vec_Ptr_t * vLines; // the line beginnings + // temporary objects + Io_BlifObj_t * pObjects; // the storage for objects + int nObjects; // the number of objects allocated + int iObjNext; // the next free object + // file lines + char * pModel; // .model line + Vec_Ptr_t * vInputs; // .inputs lines + Vec_Ptr_t * vOutputs; // .outputs lines + Vec_Ptr_t * vLatches; // .latches lines + Vec_Ptr_t * vNames; // .names lines + // network objects + Vec_Ptr_t * vPis; // the PI structures + Vec_Ptr_t * vPos; // the PO structures + Vec_Ptr_t * vLis; // the LI structures + Vec_Ptr_t * vLos; // the LO structures + // mapping of names into objects + Io_BlifObj_t ** pTable; // the hash table + int nTableSize; // the hash table size + // current processing info + Abc_Ntk_t * pAig; // the network under construction + Vec_Ptr_t * vTokens; // the current tokens + char sError[512]; // the error string generated during parsing + // statistics + int nTablesRead; // the number of processed tables + int nTablesLeft; // the number of dangling tables +}; + +// static functions +static Io_BlifMan_t * Io_BlifAlloc(); +static void Io_BlifFree( Io_BlifMan_t * p ); +static char * Io_BlifLoadFile( char * pFileName ); +static void Io_BlifReadPreparse( Io_BlifMan_t * p ); +static Abc_Ntk_t * Io_BlifParse( Io_BlifMan_t * p ); +static int Io_BlifParseModel( Io_BlifMan_t * p, char * pLine ); +static int Io_BlifParseInputs( Io_BlifMan_t * p, char * pLine ); +static int Io_BlifParseOutputs( Io_BlifMan_t * p, char * pLine ); +static int Io_BlifParseLatch( Io_BlifMan_t * p, char * pLine ); +static int Io_BlifParseNames( Io_BlifMan_t * p, char * pLine ); +static int Io_BlifParseConstruct( Io_BlifMan_t * p ); +static int Io_BlifCharIsSpace( char s ) { return s == ' ' || s == '\t' || s == '\r' || s == '\n'; } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Reads the network from the BLIF file as an AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Io_ReadBlifAsAig( char * pFileName, int fCheck ) +{ + FILE * pFile; + Io_BlifMan_t * p; + Abc_Ntk_t * pAig; + + // check that the file is available + pFile = fopen( pFileName, "rb" ); + if ( pFile == NULL ) + { + printf( "Io_Blif(): The file is unavailable (absent or open).\n" ); + return 0; + } + fclose( pFile ); + + // start the file reader + p = Io_BlifAlloc(); + p->pFileName = pFileName; + p->pBuffer = Io_BlifLoadFile( pFileName ); + if ( p->pBuffer == NULL ) + { + Io_BlifFree( p ); + return NULL; + } + // prepare the file for parsing + Io_BlifReadPreparse( p ); + // construct the network + pAig = Io_BlifParse( p ); + if ( p->sError[0] ) + fprintf( stdout, "%s\n", p->sError ); + if ( pAig == NULL ) + return NULL; + Io_BlifFree( p ); + + // make sure that everything is okay with the network structure + if ( fCheck && !Abc_NtkCheckRead( pAig ) ) + { + printf( "Io_Blif: The network check has failed.\n" ); + Abc_NtkDelete( pAig ); + return NULL; + } + return pAig; +} + +/**Function************************************************************* + + Synopsis [Allocates the BLIF parsing structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static Io_BlifMan_t * Io_BlifAlloc() +{ + Io_BlifMan_t * p; + p = ALLOC( Io_BlifMan_t, 1 ); + memset( p, 0, sizeof(Io_BlifMan_t) ); + p->vLines = Vec_PtrAlloc( 512 ); + p->vInputs = Vec_PtrAlloc( 512 ); + p->vOutputs = Vec_PtrAlloc( 512 ); + p->vLatches = Vec_PtrAlloc( 512 ); + p->vNames = Vec_PtrAlloc( 512 ); + p->vTokens = Vec_PtrAlloc( 512 ); + p->vPis = Vec_PtrAlloc( 512 ); + p->vPos = Vec_PtrAlloc( 512 ); + p->vLis = Vec_PtrAlloc( 512 ); + p->vLos = Vec_PtrAlloc( 512 ); + return p; +} + +/**Function************************************************************* + + Synopsis [Frees the BLIF parsing structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static void Io_BlifFree( Io_BlifMan_t * p ) +{ + if ( p->pAig ) + Abc_NtkDelete( p->pAig ); + if ( p->pBuffer ) free( p->pBuffer ); + if ( p->pObjects ) free( p->pObjects ); + if ( p->pTable ) free( p->pTable ); + Vec_PtrFree( p->vLines ); + Vec_PtrFree( p->vInputs ); + Vec_PtrFree( p->vOutputs ); + Vec_PtrFree( p->vLatches ); + Vec_PtrFree( p->vNames ); + Vec_PtrFree( p->vTokens ); + Vec_PtrFree( p->vPis ); + Vec_PtrFree( p->vPos ); + Vec_PtrFree( p->vLis ); + Vec_PtrFree( p->vLos ); + free( p ); +} + + +/**Function************************************************************* + + Synopsis [Hashing for character strings.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static unsigned Io_BlifHashString( char * pName, int TableSize ) +{ + static int s_Primes[10] = { + 1291, 1699, 2357, 4177, 5147, + 5647, 6343, 7103, 7873, 8147 + }; + unsigned i, Key = 0; + for ( i = 0; pName[i] != '\0'; i++ ) + Key ^= s_Primes[i%10]*pName[i]*pName[i]; + return Key % TableSize; +} + +/**Function************************************************************* + + Synopsis [Checks if the given name exists in the table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static Io_BlifObj_t ** Io_BlifHashLookup( Io_BlifMan_t * p, char * pName ) +{ + Io_BlifObj_t ** ppEntry; + for ( ppEntry = p->pTable + Io_BlifHashString(pName, p->nTableSize); *ppEntry; ppEntry = &(*ppEntry)->pNext ) + if ( !strcmp((*ppEntry)->pName, pName) ) + return ppEntry; + return ppEntry; +} + +/**Function************************************************************* + + Synopsis [Finds or add the given name to the table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static Io_BlifObj_t * Io_BlifHashFindOrAdd( Io_BlifMan_t * p, char * pName ) +{ + Io_BlifObj_t ** ppEntry; + ppEntry = Io_BlifHashLookup( p, pName ); + if ( *ppEntry == NULL ) + { + assert( p->iObjNext < p->nObjects ); + *ppEntry = p->pObjects + p->iObjNext++; + (*ppEntry)->pName = pName; + } + return *ppEntry; +} + + +/**Function************************************************************* + + Synopsis [Collects the already split tokens.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static void Io_BlifCollectTokens( Vec_Ptr_t * vTokens, char * pInput, char * pOutput ) +{ + char * pCur; + Vec_PtrClear( vTokens ); + for ( pCur = pInput; pCur < pOutput; pCur++ ) + { + if ( *pCur == 0 ) + continue; + Vec_PtrPush( vTokens, pCur ); + while ( *++pCur ); + } +} + +/**Function************************************************************* + + Synopsis [Splits the line into tokens.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static void Io_BlifSplitIntoTokens( Vec_Ptr_t * vTokens, char * pLine, char Stop ) +{ + char * pCur; + // clear spaces + for ( pCur = pLine; *pCur != Stop; pCur++ ) + if ( Io_BlifCharIsSpace(*pCur) ) + *pCur = 0; + // collect tokens + Io_BlifCollectTokens( vTokens, pLine, pCur ); +} + +/**Function************************************************************* + + Synopsis [Returns the 1-based number of the line in which the token occurs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_BlifGetLine( Io_BlifMan_t * p, char * pToken ) +{ + char * pLine; + int i; + Vec_PtrForEachEntry( p->vLines, pLine, i ) + if ( pToken < pLine ) + return i; + return -1; +} + +/**Function************************************************************* + + Synopsis [Conservatively estimates the number of primary inputs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_BlifEstimatePiNum( Io_BlifMan_t * p ) +{ + char * pCur; + int i, fSpaces; + int Counter = 0; + Vec_PtrForEachEntry( p->vInputs, pCur, i ) + for ( fSpaces = 0; *pCur; pCur++ ) + { + if ( Io_BlifCharIsSpace(*pCur) ) + { + if ( !fSpaces ) + Counter++; + fSpaces = 1; + } + else + fSpaces = 0; + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Conservatively estimates the number of AIG nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_BlifEstimateAndNum( Io_BlifMan_t * p ) +{ + Io_BlifObj_t * pObj; + char * pCur; + int i, CounterOne, Counter = 0; + for ( i = 0; i < p->iObjNext; i++ ) + { + pObj = p->pObjects + i; + if ( !pObj->fDef ) + continue; + CounterOne = 0; + for ( pCur = pObj->pName + strlen(pObj->pName); *pCur != '.'; pCur++ ) + if ( *pCur == '0' || *pCur == '1' ) + CounterOne++; + if ( CounterOne ) + Counter += CounterOne - 1; + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Reads the file into a character buffer.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static char * Io_BlifLoadFile( char * pFileName ) +{ + FILE * pFile; + int nFileSize; + char * pContents; + pFile = fopen( pFileName, "rb" ); + if ( pFile == NULL ) + { + printf( "Io_BlifLoadFile(): The file is unavailable (absent or open).\n" ); + return NULL; + } + fseek( pFile, 0, SEEK_END ); + nFileSize = ftell( pFile ); + if ( nFileSize == 0 ) + { + printf( "Io_BlifLoadFile(): The file is empty.\n" ); + return NULL; + } + pContents = ALLOC( char, nFileSize + 10 ); + rewind( pFile ); + fread( pContents, nFileSize, 1, pFile ); + fclose( pFile ); + // finish off the file with the spare .end line + // some benchmarks suddenly break off without this line + strcpy( pContents + nFileSize, "\n.end\n" ); + return pContents; +} + +/**Function************************************************************* + + Synopsis [Prepares the parsing.] + + Description [Performs several preliminary operations: + - Cuts the file buffer into separate lines. + - Removes comments and line extenders. + - Sorts lines by directives. + - Estimates the number of objects. + - Allocates room for the objects. + - Allocates room for the hash table.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static void Io_BlifReadPreparse( Io_BlifMan_t * p ) +{ + char * pCur, * pPrev; + int i, fComment = 0; + // parse the buffer into lines and remove comments + Vec_PtrPush( p->vLines, p->pBuffer ); + for ( pCur = p->pBuffer; *pCur; pCur++ ) + { + if ( *pCur == '\n' ) + { + *pCur = 0; + fComment = 0; + Vec_PtrPush( p->vLines, pCur + 1 ); + } + else if ( *pCur == '#' ) + fComment = 1; + // remove comments + if ( fComment ) + *pCur = 0; + } + + // unfold the line extensions and sort lines by directive + Vec_PtrForEachEntry( p->vLines, pCur, i ) + { + if ( *pCur == 0 ) + continue; + // find previous non-space character + for ( pPrev = pCur - 2; pPrev >= p->pBuffer; pPrev-- ) + if ( !Io_BlifCharIsSpace(*pPrev) ) + break; + // if it is the line extender, overwrite it with spaces + if ( *pPrev == '\\' ) + { + for ( ; *pPrev; pPrev++ ) + *pPrev = ' '; + *pPrev = ' '; + continue; + } + // skip spaces at the beginning of the line + while ( Io_BlifCharIsSpace(*pCur++) ); + // parse directives + if ( *(pCur-1) != '.' ) + continue; + if ( !strncmp(pCur, "names", 5) ) + Vec_PtrPush( p->vNames, pCur ); + else if ( !strncmp(pCur, "latch", 5) ) + Vec_PtrPush( p->vLatches, pCur ); + else if ( !strncmp(pCur, "inputs", 6) ) + Vec_PtrPush( p->vInputs, pCur ); + else if ( !strncmp(pCur, "outputs", 7) ) + Vec_PtrPush( p->vOutputs, pCur ); + else if ( !strncmp(pCur, "model", 5) ) + p->pModel = pCur; + else if ( !strncmp(pCur, "end", 3) || !strncmp(pCur, "exdc", 4) ) + break; + else + { + pCur--; + if ( pCur[strlen(pCur)-1] == '\r' ) + pCur[strlen(pCur)-1] = 0; + fprintf( stdout, "Line %d: Skipping line \"%s\".\n", Io_BlifGetLine(p, pCur), pCur ); + } + } + + // count the number of objects + p->nObjects = Io_BlifEstimatePiNum(p) + Vec_PtrSize(p->vLatches) + Vec_PtrSize(p->vNames) + 512; + + // allocate memory for objects + p->pObjects = ALLOC( Io_BlifObj_t, p->nObjects ); + memset( p->pObjects, 0, p->nObjects * sizeof(Io_BlifObj_t) ); + + // allocate memory for the hash table + p->nTableSize = p->nObjects/2 + 1; + p->pTable = ALLOC( Io_BlifObj_t *, p->nTableSize ); + memset( p->pTable, 0, p->nTableSize * sizeof(Io_BlifObj_t *) ); +} + + +/**Function************************************************************* + + Synopsis [Reads the AIG in the binary AIGER format.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static Abc_Ntk_t * Io_BlifParse( Io_BlifMan_t * p ) +{ + Abc_Ntk_t * pAig; + char * pLine; + int i; + // parse the model + if ( !Io_BlifParseModel( p, p->pModel ) ) + return NULL; + // parse the inputs + Vec_PtrForEachEntry( p->vInputs, pLine, i ) + if ( !Io_BlifParseInputs( p, pLine ) ) + return NULL; + // parse the outputs + Vec_PtrForEachEntry( p->vOutputs, pLine, i ) + if ( !Io_BlifParseOutputs( p, pLine ) ) + return NULL; + // parse the latches + Vec_PtrForEachEntry( p->vLatches, pLine, i ) + if ( !Io_BlifParseLatch( p, pLine ) ) + return NULL; + // parse the nodes + Vec_PtrForEachEntry( p->vNames, pLine, i ) + if ( !Io_BlifParseNames( p, pLine ) ) + return NULL; + // reconstruct the network from the parsed data + if ( !Io_BlifParseConstruct( p ) ) + return NULL; + // return the network + pAig = p->pAig; + p->pAig = NULL; + return pAig; +} + +/**Function************************************************************* + + Synopsis [Parses the model line.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_BlifParseModel( Io_BlifMan_t * p, char * pLine ) +{ + char * pToken; + Io_BlifSplitIntoTokens( p->vTokens, pLine, '\0' ); + pToken = Vec_PtrEntry( p->vTokens, 0 ); + assert( !strcmp(pToken, "model") ); + if ( Vec_PtrSize(p->vTokens) != 2 ) + { + sprintf( p->sError, "Line %d: Model line has %d entries while it should have 2.", Io_BlifGetLine(p, pToken), Vec_PtrSize(p->vTokens) ); + return 0; + } + p->pModel = Vec_PtrEntry( p->vTokens, 1 ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Parses the inputs line.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_BlifParseInputs( Io_BlifMan_t * p, char * pLine ) +{ + Io_BlifObj_t * pObj; + char * pToken; + int i; + Io_BlifSplitIntoTokens( p->vTokens, pLine, '\0' ); + pToken = Vec_PtrEntry(p->vTokens, 0); + assert( !strcmp(pToken, "inputs") ); + Vec_PtrForEachEntryStart( p->vTokens, pToken, i, 1 ) + { + pObj = Io_BlifHashFindOrAdd( p, pToken ); + if ( pObj->fPi ) + { + sprintf( p->sError, "Line %d: Primary input (%s) is defined more than once.", Io_BlifGetLine(p, pToken), pToken ); + return 0; + } + pObj->fPi = 1; + Vec_PtrPush( p->vPis, pObj ); + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Parses the outputs line.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_BlifParseOutputs( Io_BlifMan_t * p, char * pLine ) +{ + Io_BlifObj_t * pObj; + char * pToken; + int i; + Io_BlifSplitIntoTokens( p->vTokens, pLine, '\0' ); + pToken = Vec_PtrEntry(p->vTokens, 0); + assert( !strcmp(pToken, "outputs") ); + Vec_PtrForEachEntryStart( p->vTokens, pToken, i, 1 ) + { + pObj = Io_BlifHashFindOrAdd( p, pToken ); + if ( pObj->fPo ) + fprintf( stdout, "Line %d: Primary output (%s) is defined more than once (warning only).\n", Io_BlifGetLine(p, pToken), pToken ); + pObj->fPo = 1; + Vec_PtrPush( p->vPos, pObj ); + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Parses the latches line.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_BlifParseLatch( Io_BlifMan_t * p, char * pLine ) +{ + Io_BlifObj_t * pObj; + char * pToken; + int Init; + Io_BlifSplitIntoTokens( p->vTokens, pLine, '\0' ); + pToken = Vec_PtrEntry(p->vTokens,0); + assert( !strcmp(pToken, "latch") ); + if ( Vec_PtrSize(p->vTokens) < 3 ) + { + sprintf( p->sError, "Line %d: Latch does not have input name and output name.", Io_BlifGetLine(p, pToken) ); + return 0; + } + // get initial value + if ( Vec_PtrSize(p->vTokens) > 3 ) + Init = atoi( Vec_PtrEntry(p->vTokens,3) ); + else + Init = 2; + if ( Init < 0 || Init > 2 ) + { + sprintf( p->sError, "Line %d: Initial state of the latch is incorrect (%s).", Io_BlifGetLine(p, pToken), Vec_PtrEntry(p->vTokens,3) ); + return 0; + } + if ( Init == 0 ) + Init = IO_BLIF_INIT_ZERO; + else if ( Init == 1 ) + Init = IO_BLIF_INIT_ONE; + else // if ( Init == 2 ) + Init = IO_BLIF_INIT_DC; + // get latch input + pObj = Io_BlifHashFindOrAdd( p, Vec_PtrEntry(p->vTokens,1) ); + pObj->fLi = 1; + Vec_PtrPush( p->vLis, pObj ); + pObj->Init = Init; + // get latch output + pObj = Io_BlifHashFindOrAdd( p, Vec_PtrEntry(p->vTokens,2) ); + if ( pObj->fPi ) + { + sprintf( p->sError, "Line %d: Primary input (%s) is also defined latch output.", Io_BlifGetLine(p, pToken), Vec_PtrEntry(p->vTokens,2) ); + return 0; + } + if ( pObj->fLo ) + { + sprintf( p->sError, "Line %d: Latch output (%s) is defined as the output of another latch.", Io_BlifGetLine(p, pToken), Vec_PtrEntry(p->vTokens,2) ); + return 0; + } + pObj->fLo = 1; + Vec_PtrPush( p->vLos, pObj ); + pObj->Init = Init; + return 1; +} + +/**Function************************************************************* + + Synopsis [Parses the nodes line.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_BlifParseNames( Io_BlifMan_t * p, char * pLine ) +{ + Io_BlifObj_t * pObj; + char * pName; + Io_BlifSplitIntoTokens( p->vTokens, pLine, '\0' ); + assert( !strcmp(Vec_PtrEntry(p->vTokens,0), "names") ); + pName = Vec_PtrEntryLast( p->vTokens ); + pObj = Io_BlifHashFindOrAdd( p, pName ); + if ( pObj->fPi ) + { + sprintf( p->sError, "Line %d: Primary input (%s) has a table.", Io_BlifGetLine(p, pName), pName ); + return 0; + } + if ( pObj->fLo ) + { + sprintf( p->sError, "Line %d: Latch output (%s) has a table.", Io_BlifGetLine(p, pName), pName ); + return 0; + } + if ( pObj->fDef ) + { + sprintf( p->sError, "Line %d: Signal (%s) is defined more than once.", Io_BlifGetLine(p, pName), pName ); + return 0; + } + pObj->fDef = 1; + // remember offset to the first fanin name + pObj->pName = pName; + pObj->Offset = pObj->pName - (char *)Vec_PtrEntry(p->vTokens,1); + return 1; +} + + +/**Function************************************************************* + + Synopsis [Constructs the AIG from the file parsing info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static Abc_Obj_t * Io_BlifParseTable( Io_BlifMan_t * p, char * pTable, Vec_Ptr_t * vFanins ) +{ + char * pProduct, * pOutput; + Abc_Obj_t * pRes, * pCube; + int i, k, Polarity = -1; + + p->nTablesRead++; + // get the tokens + Io_BlifSplitIntoTokens( p->vTokens, pTable, '.' ); + if ( Vec_PtrSize(p->vTokens) == 0 ) + return Abc_ObjNot( Abc_AigConst1(p->pAig) ); + if ( Vec_PtrSize(p->vTokens) == 1 ) + { + pOutput = Vec_PtrEntry( p->vTokens, 0 ); + if ( ((pOutput[0] - '0') & 0x8E) || pOutput[1] ) + { + sprintf( p->sError, "Line %d: Constant table has wrong output value (%s).", Io_BlifGetLine(p, pOutput), pOutput ); + return NULL; + } + return Abc_ObjNotCond( Abc_AigConst1(p->pAig), pOutput[0] == '0' ); + } + pProduct = Vec_PtrEntry( p->vTokens, 0 ); + if ( Vec_PtrSize(p->vTokens) % 2 == 1 ) + { + sprintf( p->sError, "Line %d: Table has odd number of tokens (%d).", Io_BlifGetLine(p, pProduct), Vec_PtrSize(p->vTokens) ); + return NULL; + } + // parse the table + pRes = Abc_ObjNot( Abc_AigConst1(p->pAig) ); + for ( i = 0; i < Vec_PtrSize(p->vTokens)/2; i++ ) + { + pProduct = Vec_PtrEntry( p->vTokens, 2*i + 0 ); + pOutput = Vec_PtrEntry( p->vTokens, 2*i + 1 ); + if ( strlen(pProduct) != (unsigned)Vec_PtrSize(vFanins) ) + { + sprintf( p->sError, "Line %d: Cube (%s) has size different from the fanin count (%d).", Io_BlifGetLine(p, pProduct), pProduct, Vec_PtrSize(vFanins) ); + return NULL; + } + if ( ((pOutput[0] - '0') & 0x8E) || pOutput[1] ) + { + sprintf( p->sError, "Line %d: Output value (%s) is incorrect.", Io_BlifGetLine(p, pProduct), pOutput ); + return NULL; + } + if ( Polarity == -1 ) + Polarity = pOutput[0] - '0'; + else if ( Polarity != pOutput[0] - '0' ) + { + sprintf( p->sError, "Line %d: Output value (%s) differs from the value in the first line of the table (%d).", Io_BlifGetLine(p, pProduct), pOutput, Polarity ); + return NULL; + } + // parse one product product + pCube = Abc_AigConst1(p->pAig); + for ( k = 0; pProduct[k]; k++ ) + { + if ( pProduct[k] == '0' ) + pCube = Abc_AigAnd( p->pAig->pManFunc, pCube, Abc_ObjNot(Vec_PtrEntry(vFanins,k)) ); + else if ( pProduct[k] == '1' ) + pCube = Abc_AigAnd( p->pAig->pManFunc, pCube, Vec_PtrEntry(vFanins,k) ); + else if ( pProduct[k] != '-' ) + { + sprintf( p->sError, "Line %d: Product term (%s) contains character (%c).", Io_BlifGetLine(p, pProduct), pProduct, pProduct[k] ); + return NULL; + } + } + pRes = Abc_AigOr( p->pAig->pManFunc, pRes, pCube ); + } + pRes = Abc_ObjNotCond( pRes, Polarity == 0 ); + return pRes; +} + +/**Function************************************************************* + + Synopsis [Constructs the AIG from the file parsing info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static Abc_Obj_t * Io_BlifParseConstruct_rec( Io_BlifMan_t * p, char * pName ) +{ + Vec_Ptr_t * vFanins; + Abc_Obj_t * pFaninAbc; + Io_BlifObj_t * pObjIo; + char * pNameFanin; + int i; + // get the IO object with this name + pObjIo = *Io_BlifHashLookup( p, pName ); + if ( pObjIo == NULL ) + { + sprintf( p->sError, "Line %d: Signal (%s) is not defined as a table.", Io_BlifGetLine(p, pName), pName ); + return NULL; + } + // loop detection + if ( pObjIo->fLoop ) + { + sprintf( p->sError, "Line %d: Signal (%s) appears twice on a combinational path.", Io_BlifGetLine(p, pName), pName ); + return NULL; + } + // check if the AIG is already constructed + if ( pObjIo->pEquiv ) + return pObjIo->pEquiv; + // mark this node on the path + pObjIo->fLoop = 1; + // construct the AIGs for the fanins + vFanins = Vec_PtrAlloc( 8 ); + Io_BlifCollectTokens( vFanins, pObjIo->pName - pObjIo->Offset, pObjIo->pName ); + Vec_PtrForEachEntry( vFanins, pNameFanin, i ) + { + pFaninAbc = Io_BlifParseConstruct_rec( p, pNameFanin ); + if ( pFaninAbc == NULL ) + { + Vec_PtrFree( vFanins ); + return NULL; + } + Vec_PtrWriteEntry( vFanins, i, pFaninAbc ); + } + // construct the node + pObjIo->pEquiv = Io_BlifParseTable( p, pObjIo->pName + strlen(pObjIo->pName), vFanins ); + Vec_PtrFree( vFanins ); + // unmark this node on the path + pObjIo->fLoop = 0; + // remember the new node + return pObjIo->pEquiv; +} + +/**Function************************************************************* + + Synopsis [Constructs the AIG from the file parsing info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_BlifParseConstruct( Io_BlifMan_t * p ) +{ + Abc_Ntk_t * pAig; + Io_BlifObj_t * pObjIo, * pObjIoInput; + Abc_Obj_t * pObj, * pLatch; + int i; + // allocate the empty AIG + pAig = p->pAig = Abc_NtkAlloc( ABC_NTK_STRASH, ABC_FUNC_AIG, 1 ); + pAig->pName = Extra_UtilStrsav( p->pModel ); + pAig->pSpec = Extra_UtilStrsav( p->pFileName ); + // create PIs + Vec_PtrForEachEntry( p->vPis, pObjIo, i ) + { + pObj = Abc_NtkCreatePi( pAig ); + Abc_ObjAssignName( pObj, pObjIo->pName, NULL ); + pObjIo->pEquiv = pObj; + } + // create POs + Vec_PtrForEachEntry( p->vPos, pObjIo, i ) + { + pObj = Abc_NtkCreatePo( pAig ); + Abc_ObjAssignName( pObj, pObjIo->pName, NULL ); + } + // create latches + Vec_PtrForEachEntry( p->vLos, pObjIo, i ) + { + // add the latch input terminal + pObj = Abc_NtkCreateBi( pAig ); + pObjIoInput = Vec_PtrEntry( p->vLis, i ); + Abc_ObjAssignName( pObj, pObjIoInput->pName, NULL ); + + // add the latch box + pLatch = Abc_NtkCreateLatch( pAig ); + pLatch->pData = (void *)pObjIo->Init; + Abc_ObjAssignName( pLatch, pObjIo->pName, "L" ); + Abc_ObjAddFanin( pLatch, pObj ); + + // add the latch output terminal + pObj = Abc_NtkCreateBo( pAig ); + Abc_ObjAssignName( pObj, pObjIo->pName, NULL ); + Abc_ObjAddFanin( pObj, pLatch ); + // set the value of the latch output +// pObjIo->pEquiv = Abc_ObjNotCond( pObj, pObjIo->Init ); + pObjIo->pEquiv = pObj; + } + // traverse the nodes from the POs + Vec_PtrForEachEntry( p->vPos, pObjIo, i ) + { + pObj = Io_BlifParseConstruct_rec( p, pObjIo->pName ); + if ( pObj == NULL ) + return 0; + Abc_ObjAddFanin( Abc_NtkPo(p->pAig, i), pObj ); + } + // traverse the nodes from the latch inputs + Vec_PtrForEachEntry( p->vLis, pObjIo, i ) + { + pObj = Io_BlifParseConstruct_rec( p, pObjIo->pName ); + if ( pObj == NULL ) + return 0; +// pObj = Abc_ObjNotCond( pObj, pObjIo->Init ); + Abc_ObjAddFanin( Abc_ObjFanin0(Abc_NtkBox(p->pAig, i)), pObj ); + } + p->nTablesLeft = Vec_PtrSize(p->vNames) - p->nTablesRead; + if ( p->nTablesLeft ) + printf( "The number of dangling tables = %d.\n", p->nTablesLeft ); + printf( "AND nodes = %6d. Estimate = %6d.\n", Abc_NtkNodeNum(p->pAig), Io_BlifEstimateAndNum(p) ); + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/io/ioReadBlifMv.c b/abc_with_bb_support/src/base/io/ioReadBlifMv.c new file mode 100644 index 000000000..e5758e678 --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioReadBlifMv.c @@ -0,0 +1,1721 @@ +/**CFile**************************************************************** + + FileName [ioReadBlifMv.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures to read BLIF-MV file.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 8, 2007.] + + Revision [$Id: ioReadBlifMv.c,v 1.00 2007/01/08 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "extra.h" +#include "vecPtr.h" +#include "io.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define IO_BLIFMV_MAXVALUES 256 + +typedef struct Io_MvVar_t_ Io_MvVar_t; // parsing var +typedef struct Io_MvMod_t_ Io_MvMod_t; // parsing model +typedef struct Io_MvMan_t_ Io_MvMan_t; // parsing manager + +struct Io_MvVar_t_ +{ + int nValues; // the number of values + char ** pNames; // the value names +}; + +struct Io_MvMod_t_ +{ + // file lines + char * pName; // .model line + Vec_Ptr_t * vInputs; // .inputs lines + Vec_Ptr_t * vOutputs; // .outputs lines + Vec_Ptr_t * vLatches; // .latch lines + Vec_Ptr_t * vFlops; // .flop lines + Vec_Ptr_t * vResets; // .reset lines + Vec_Ptr_t * vNames; // .names lines + Vec_Ptr_t * vSubckts; // .subckt lines + Vec_Ptr_t * vMvs; // .mv lines + int fBlackBox; // indicates blackbox model + // the resulting network + Abc_Ntk_t * pNtk; + Abc_Obj_t * pResetLatch; + // the parent manager + Io_MvMan_t * pMan; +}; + +struct Io_MvMan_t_ +{ + // general info about file + int fBlifMv; // the file is BLIF-MV + int fUseReset; // the reset circuitry is added + char * pFileName; // the name of the file + char * pBuffer; // the contents of the file + Vec_Ptr_t * vLines; // the line beginnings + // the results of reading + Abc_Lib_t * pDesign; // the design under construction + int nNDnodes; // the counter of ND nodes + // intermediate storage for models + Vec_Ptr_t * vModels; // vector of models + Io_MvMod_t * pLatest; // the current model + // current processing info + Vec_Ptr_t * vTokens; // the current tokens + Vec_Ptr_t * vTokens2; // the current tokens + Vec_Str_t * vFunc; // the local function + // error reporting + char sError[512]; // the error string generated during parsing + // statistics + int nTablesRead; // the number of processed tables + int nTablesLeft; // the number of dangling tables +}; + +// static functions +static Io_MvMan_t * Io_MvAlloc(); +static void Io_MvFree( Io_MvMan_t * p ); +static Io_MvMod_t * Io_MvModAlloc(); +static void Io_MvModFree( Io_MvMod_t * p ); +static char * Io_MvLoadFile( char * pFileName ); +static void Io_MvReadPreparse( Io_MvMan_t * p ); +static void Io_MvReadInterfaces( Io_MvMan_t * p ); +static Abc_Lib_t * Io_MvParse( Io_MvMan_t * p ); +static int Io_MvParseLineModel( Io_MvMod_t * p, char * pLine ); +static int Io_MvParseLineInputs( Io_MvMod_t * p, char * pLine ); +static int Io_MvParseLineOutputs( Io_MvMod_t * p, char * pLine ); +static int Io_MvParseLineLatch( Io_MvMod_t * p, char * pLine ); +static int Io_MvParseLineSubckt( Io_MvMod_t * p, char * pLine ); +static int Io_MvParseLineMv( Io_MvMod_t * p, char * pLine ); +static int Io_MvParseLineNamesMv( Io_MvMod_t * p, char * pLine, int fReset ); +static int Io_MvParseLineNamesBlif( Io_MvMod_t * p, char * pLine ); +static int Io_MvParseLineGateBlif( Io_MvMod_t * p, Vec_Ptr_t * vTokens ); +static Io_MvVar_t * Abc_NtkMvVarDup( Abc_Ntk_t * pNtk, Io_MvVar_t * pVar ); + +static int Io_MvCharIsSpace( char s ) { return s == ' ' || s == '\t' || s == '\r' || s == '\n'; } +static int Io_MvCharIsMvSymb( char s ) { return s == '(' || s == ')' || s == '{' || s == '}' || s == '-' || s == ',' || s == '!'; } + +extern void Abc_NtkStartMvVars( Abc_Ntk_t * pNtk ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Reads the network from the BLIF or BLIF-MV file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Io_ReadBlifMv( char * pFileName, int fBlifMv, int fCheck ) +{ + FILE * pFile; + Io_MvMan_t * p; + Abc_Ntk_t * pNtk; + Abc_Lib_t * pDesign; + char * pDesignName; + int RetValue, i; + + // check that the file is available + pFile = fopen( pFileName, "rb" ); + if ( pFile == NULL ) + { + printf( "Io_ReadBlifMv(): The file is unavailable (absent or open).\n" ); + return 0; + } + fclose( pFile ); + + // start the file reader + p = Io_MvAlloc(); + p->fBlifMv = fBlifMv; + p->fUseReset = 0; + p->pFileName = pFileName; + p->pBuffer = Io_MvLoadFile( pFileName ); + if ( p->pBuffer == NULL ) + { + Io_MvFree( p ); + return NULL; + } + // set the design name + pDesignName = Extra_FileNameGeneric( pFileName ); + p->pDesign = Abc_LibCreate( pDesignName ); + free( pDesignName ); + // free the HOP manager + Hop_ManStop( p->pDesign->pManFunc ); + p->pDesign->pManFunc = NULL; + // prepare the file for parsing + Io_MvReadPreparse( p ); + // parse interfaces of each network + Io_MvReadInterfaces( p ); + // construct the network + pDesign = Io_MvParse( p ); + if ( p->sError[0] ) + fprintf( stdout, "%s\n", p->sError ); + if ( pDesign == NULL ) + return NULL; + Io_MvFree( p ); +// pDesign should be linked to all models of the design + + // make sure that everything is okay with the network structure + if ( fCheck ) + { + Vec_PtrForEachEntry( pDesign->vModules, pNtk, i ) + { + if ( !Abc_NtkCheckRead( pNtk ) ) + { + printf( "Io_ReadBlifMv: The network check has failed for network %s.\n", pNtk->pName ); + Abc_LibFree( pDesign, NULL ); + return NULL; + } + } + } + +//Abc_LibPrint( pDesign ); + + // detect top-level model + RetValue = Abc_LibFindTopLevelModels( pDesign ); + pNtk = Vec_PtrEntry( pDesign->vTops, 0 ); + if ( RetValue > 1 ) + printf( "Warning: The design has %d root-level modules. The first one (%s) will be used. Ignored (%s)\n", + Vec_PtrSize(pDesign->vTops), pNtk->pName, ((Abc_Ntk_t * )Vec_PtrEntry( pDesign->vTops, 1 ))->pName ); + + // extract the master network + pNtk->pDesign = pDesign; + pDesign->pManFunc = NULL; + + // verify the design for cyclic dependence + assert( Vec_PtrSize(pDesign->vModules) > 0 ); + if ( Vec_PtrSize(pDesign->vModules) == 1 ) + { +// printf( "Warning: The design is not hierarchical.\n" ); + Abc_LibFree( pDesign, pNtk ); + pNtk->pDesign = NULL; + pNtk->pSpec = Extra_UtilStrsav( pFileName ); + } + else + Abc_NtkIsAcyclicHierarchy( pNtk ); + +//Io_WriteBlifMv( pNtk, "_temp_.mv" ); + if ( pNtk->pSpec == NULL ) + pNtk->pSpec = Extra_UtilStrsav( pFileName ); + return pNtk; +} + +/**Function************************************************************* + + Synopsis [Allocates the BLIF parsing structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static Io_MvMan_t * Io_MvAlloc() +{ + Io_MvMan_t * p; + p = ALLOC( Io_MvMan_t, 1 ); + memset( p, 0, sizeof(Io_MvMan_t) ); + p->vLines = Vec_PtrAlloc( 512 ); + p->vModels = Vec_PtrAlloc( 512 ); + p->vTokens = Vec_PtrAlloc( 512 ); + p->vTokens2 = Vec_PtrAlloc( 512 ); + p->vFunc = Vec_StrAlloc( 512 ); + return p; +} + +/**Function************************************************************* + + Synopsis [Frees the BLIF parsing structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static void Io_MvFree( Io_MvMan_t * p ) +{ + Io_MvMod_t * pMod; + int i; + if ( p->pDesign ) + Abc_LibFree( p->pDesign, NULL ); + if ( p->pBuffer ) + free( p->pBuffer ); + if ( p->vLines ) + Vec_PtrFree( p->vLines ); + if ( p->vModels ) + { + Vec_PtrForEachEntry( p->vModels, pMod, i ) + Io_MvModFree( pMod ); + Vec_PtrFree( p->vModels ); + } + Vec_PtrFree( p->vTokens ); + Vec_PtrFree( p->vTokens2 ); + Vec_StrFree( p->vFunc ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Allocates the BLIF parsing structure for one model.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static Io_MvMod_t * Io_MvModAlloc() +{ + Io_MvMod_t * p; + p = ALLOC( Io_MvMod_t, 1 ); + memset( p, 0, sizeof(Io_MvMod_t) ); + p->vInputs = Vec_PtrAlloc( 512 ); + p->vOutputs = Vec_PtrAlloc( 512 ); + p->vLatches = Vec_PtrAlloc( 512 ); + p->vFlops = Vec_PtrAlloc( 512 ); + p->vResets = Vec_PtrAlloc( 512 ); + p->vNames = Vec_PtrAlloc( 512 ); + p->vSubckts = Vec_PtrAlloc( 512 ); + p->vMvs = Vec_PtrAlloc( 512 ); + return p; +} + +/**Function************************************************************* + + Synopsis [Allocates the BLIF parsing structure for one model.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static void Io_MvModFree( Io_MvMod_t * p ) +{ +// if ( p->pNtk ) +// Abc_NtkDelete( p->pNtk ); + Vec_PtrFree( p->vInputs ); + Vec_PtrFree( p->vOutputs ); + Vec_PtrFree( p->vLatches ); + Vec_PtrFree( p->vFlops ); + Vec_PtrFree( p->vResets ); + Vec_PtrFree( p->vNames ); + Vec_PtrFree( p->vSubckts ); + Vec_PtrFree( p->vMvs ); + free( p ); +} + + + +/**Function************************************************************* + + Synopsis [Counts the number of given chars.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_MvCountChars( char * pLine, char Char ) +{ + char * pCur; + int Counter = 0; + for ( pCur = pLine; *pCur; pCur++ ) + if ( *pCur == Char ) + Counter++; + return Counter; +} + +/**Function************************************************************* + + Synopsis [Returns the place where the arrow is hiding.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static char * Io_MvFindArrow( char * pLine ) +{ + char * pCur; + for ( pCur = pLine; *(pCur+1); pCur++ ) + if ( *pCur == '-' && *(pCur+1) == '>' ) + { + *pCur = ' '; + *(pCur+1) = ' '; + return pCur; + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [Collects the already split tokens.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static void Io_MvCollectTokens( Vec_Ptr_t * vTokens, char * pInput, char * pOutput ) +{ + char * pCur; + Vec_PtrClear( vTokens ); + for ( pCur = pInput; pCur < pOutput; pCur++ ) + { + if ( *pCur == 0 ) + continue; + Vec_PtrPush( vTokens, pCur ); + while ( *++pCur ); + } +} + +/**Function************************************************************* + + Synopsis [Splits the line into tokens.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static void Io_MvSplitIntoTokens( Vec_Ptr_t * vTokens, char * pLine, char Stop ) +{ + char * pCur; + // clear spaces + for ( pCur = pLine; *pCur != Stop; pCur++ ) + if ( Io_MvCharIsSpace(*pCur) ) + *pCur = 0; + // collect tokens + Io_MvCollectTokens( vTokens, pLine, pCur ); +} + +/**Function************************************************************* + + Synopsis [Splits the line into tokens when .default may be present.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static void Io_MvSplitIntoTokensMv( Vec_Ptr_t * vTokens, char * pLine ) +{ + char * pCur; + // clear spaces + for ( pCur = pLine; *pCur != '.' || *(pCur+1) == 'd'; pCur++ ) + if ( Io_MvCharIsSpace(*pCur) ) + *pCur = 0; + // collect tokens + Io_MvCollectTokens( vTokens, pLine, pCur ); +} + +/**Function************************************************************* + + Synopsis [Splits the line into tokens.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static void Io_MvSplitIntoTokensAndClear( Vec_Ptr_t * vTokens, char * pLine, char Stop, char Char ) +{ + char * pCur; + // clear spaces + for ( pCur = pLine; *pCur != Stop; pCur++ ) + if ( Io_MvCharIsSpace(*pCur) || *pCur == Char ) + *pCur = 0; + // collect tokens + Io_MvCollectTokens( vTokens, pLine, pCur ); +} + +/**Function************************************************************* + + Synopsis [Returns the 1-based number of the line in which the token occurs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_MvGetLine( Io_MvMan_t * p, char * pToken ) +{ + char * pLine; + int i; + Vec_PtrForEachEntry( p->vLines, pLine, i ) + if ( pToken < pLine ) + return i; + return -1; +} + +/**Function************************************************************* + + Synopsis [Reads the file into a character buffer.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static char * Io_MvLoadFile( char * pFileName ) +{ + FILE * pFile; + int nFileSize; + char * pContents; + pFile = fopen( pFileName, "rb" ); + if ( pFile == NULL ) + { + printf( "Io_MvLoadFile(): The file is unavailable (absent or open).\n" ); + return NULL; + } + fseek( pFile, 0, SEEK_END ); + nFileSize = ftell( pFile ); + if ( nFileSize == 0 ) + { + printf( "Io_MvLoadFile(): The file is empty.\n" ); + return NULL; + } + pContents = ALLOC( char, nFileSize + 10 ); + rewind( pFile ); + fread( pContents, nFileSize, 1, pFile ); + fclose( pFile ); + // finish off the file with the spare .end line + // some benchmarks suddenly break off without this line + strcpy( pContents + nFileSize, "\n.end\n" ); + return pContents; +} + +/**Function************************************************************* + + Synopsis [Prepares the parsing.] + + Description [Performs several preliminary operations: + - Cuts the file buffer into separate lines. + - Removes comments and line extenders. + - Sorts lines by directives. + - Estimates the number of objects. + - Allocates room for the objects. + - Allocates room for the hash table.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static void Io_MvReadPreparse( Io_MvMan_t * p ) +{ + char * pCur, * pPrev; + int i, fComment = 0; + // parse the buffer into lines and remove comments + Vec_PtrPush( p->vLines, p->pBuffer ); + for ( pCur = p->pBuffer; *pCur; pCur++ ) + { + if ( *pCur == '\n' ) + { + *pCur = 0; +// if ( *(pCur-1) == '\r' ) +// *(pCur-1) = 0; + fComment = 0; + Vec_PtrPush( p->vLines, pCur + 1 ); + } + else if ( *pCur == '#' ) + fComment = 1; + // remove comments + if ( fComment ) + *pCur = 0; + } + + // unfold the line extensions and sort lines by directive + Vec_PtrForEachEntry( p->vLines, pCur, i ) + { + if ( *pCur == 0 ) + continue; + // find previous non-space character + for ( pPrev = pCur - 2; pPrev >= p->pBuffer; pPrev-- ) + if ( !Io_MvCharIsSpace(*pPrev) ) + break; + // if it is the line extender, overwrite it with spaces + if ( *pPrev == '\\' ) + { + for ( ; *pPrev; pPrev++ ) + *pPrev = ' '; + *pPrev = ' '; + continue; + } + // skip spaces at the beginning of the line + while ( Io_MvCharIsSpace(*pCur++) ); + // parse directives + if ( *(pCur-1) != '.' ) + continue; + if ( !strncmp(pCur, "names", 5) || !strncmp(pCur, "table", 5) || !strncmp(pCur, "gate", 4) ) + Vec_PtrPush( p->pLatest->vNames, pCur ); + else if ( p->fBlifMv && (!strncmp(pCur, "def ", 4) || !strncmp(pCur, "default ", 8)) ) + continue; + else if ( !strncmp(pCur, "latch", 5) ) + Vec_PtrPush( p->pLatest->vLatches, pCur ); + else if ( !strncmp(pCur, "r ", 2) || !strncmp(pCur, "reset ", 6) ) + Vec_PtrPush( p->pLatest->vResets, pCur ); + else if ( !strncmp(pCur, "inputs", 6) ) + Vec_PtrPush( p->pLatest->vInputs, pCur ); + else if ( !strncmp(pCur, "outputs", 7) ) + Vec_PtrPush( p->pLatest->vOutputs, pCur ); + else if ( !strncmp(pCur, "subckt", 6) ) + Vec_PtrPush( p->pLatest->vSubckts, pCur ); + else if ( p->fBlifMv && !strncmp(pCur, "mv", 2) ) + Vec_PtrPush( p->pLatest->vMvs, pCur ); + else if ( !strncmp(pCur, "blackbox", 8) ) + p->pLatest->fBlackBox = 1; + else if ( !strncmp(pCur, "model", 5) ) + { + p->pLatest = Io_MvModAlloc(); + p->pLatest->pName = pCur; + p->pLatest->pMan = p; + } + else if ( !strncmp(pCur, "end", 3) ) + { + if ( p->pLatest ) + Vec_PtrPush( p->vModels, p->pLatest ); + p->pLatest = NULL; + } + else if ( !strncmp(pCur, "exdc", 4) ) + { + fprintf( stdout, "Line %d: Skipping EXDC network.\n", Io_MvGetLine(p, pCur) ); + break; + } + else + { + pCur--; + if ( pCur[strlen(pCur)-1] == '\r' ) + pCur[strlen(pCur)-1] = 0; + fprintf( stdout, "Line %d: Skipping line \"%s\".\n", Io_MvGetLine(p, pCur), pCur ); + } + } +} + +/**Function************************************************************* + + Synopsis [Parses interfaces of the models.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static void Io_MvReadInterfaces( Io_MvMan_t * p ) +{ + Io_MvMod_t * pMod; + char * pLine; + int i, k; + // iterate through the models + Vec_PtrForEachEntry( p->vModels, pMod, i ) + { + // parse the model + if ( !Io_MvParseLineModel( pMod, pMod->pName ) ) + return; + // add model to the design + if ( !Abc_LibAddModel( p->pDesign, pMod->pNtk ) ) + { + sprintf( p->sError, "Line %d: Model %s is defined twice.", Io_MvGetLine(p, pMod->pName), pMod->pName ); + return; + } + // parse the inputs + Vec_PtrForEachEntry( pMod->vInputs, pLine, k ) + if ( !Io_MvParseLineInputs( pMod, pLine ) ) + return; + // parse the outputs + Vec_PtrForEachEntry( pMod->vOutputs, pLine, k ) + if ( !Io_MvParseLineOutputs( pMod, pLine ) ) + return; + } +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static Abc_Lib_t * Io_MvParse( Io_MvMan_t * p ) +{ + Abc_Lib_t * pDesign; + Io_MvMod_t * pMod; + char * pLine; + int i, k; + // iterate through the models + Vec_PtrForEachEntry( p->vModels, pMod, i ) + { + // check if there any MV lines + if ( Vec_PtrSize(pMod->vMvs) > 0 ) + Abc_NtkStartMvVars( pMod->pNtk ); + // parse the mv lines + Vec_PtrForEachEntry( pMod->vMvs, pLine, k ) + if ( !Io_MvParseLineMv( pMod, pLine ) ) + return NULL; + // if reset lines are used there should be the same number of them as latches + if ( Vec_PtrSize(pMod->vResets) > 0 ) + { + if ( Vec_PtrSize(pMod->vLatches) != Vec_PtrSize(pMod->vResets) ) + { + sprintf( p->sError, "Line %d: Model %s has different number of latches (%d) and reset nodes (%d).", + Io_MvGetLine(p, pMod->pName), Abc_NtkName(pMod->pNtk), Vec_PtrSize(pMod->vLatches), Vec_PtrSize(pMod->vResets) ); + return NULL; + } + // create binary latch with 1-data and 0-init + if ( p->fUseReset ) + pMod->pResetLatch = Io_ReadCreateResetLatch( pMod->pNtk, p->fBlifMv ); + } + // parse the latches + Vec_PtrForEachEntry( pMod->vLatches, pLine, k ) + if ( !Io_MvParseLineLatch( pMod, pLine ) ) + return NULL; + // parse the reset lines + if ( p->fUseReset ) + Vec_PtrForEachEntry( pMod->vResets, pLine, k ) + if ( !Io_MvParseLineNamesMv( pMod, pLine, 1 ) ) + return NULL; + // parse the nodes + if ( p->fBlifMv ) + { + Vec_PtrForEachEntry( pMod->vNames, pLine, k ) + if ( !Io_MvParseLineNamesMv( pMod, pLine, 0 ) ) + return NULL; + } + else + { + Vec_PtrForEachEntry( pMod->vNames, pLine, k ) + if ( !Io_MvParseLineNamesBlif( pMod, pLine ) ) + return NULL; + } + // parse the subcircuits + Vec_PtrForEachEntry( pMod->vSubckts, pLine, k ) + if ( !Io_MvParseLineSubckt( pMod, pLine ) ) + return NULL; + // finalize the network + Abc_NtkFinalizeRead( pMod->pNtk ); + } + if ( p->nNDnodes ) +// printf( "Warning: The parser added %d PIs to replace non-deterministic nodes.\n", p->nNDnodes ); + printf( "Warning: The parser added %d constant 0 nodes to replace non-deterministic nodes.\n", p->nNDnodes ); + // return the network + pDesign = p->pDesign; + p->pDesign = NULL; + return pDesign; +} + +/**Function************************************************************* + + Synopsis [Parses the model line.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_MvParseLineModel( Io_MvMod_t * p, char * pLine ) +{ + Vec_Ptr_t * vTokens = p->pMan->vTokens; + char * pToken; + Io_MvSplitIntoTokens( vTokens, pLine, '\0' ); + pToken = Vec_PtrEntry( vTokens, 0 ); + assert( !strcmp(pToken, "model") ); + if ( Vec_PtrSize(vTokens) != 2 ) + { + sprintf( p->pMan->sError, "Line %d: Model line has %d entries while it should have 2.", Io_MvGetLine(p->pMan, pToken), Vec_PtrSize(vTokens) ); + return 0; + } + if ( p->fBlackBox ) + p->pNtk = Abc_NtkAlloc( ABC_NTK_NETLIST, ABC_FUNC_BLACKBOX, 1 ); + else if ( p->pMan->fBlifMv ) + p->pNtk = Abc_NtkAlloc( ABC_NTK_NETLIST, ABC_FUNC_BLIFMV, 1 ); + else + p->pNtk = Abc_NtkAlloc( ABC_NTK_NETLIST, ABC_FUNC_SOP, 1 ); + p->pNtk->pName = Extra_UtilStrsav( Vec_PtrEntry(vTokens, 1) ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Parses the inputs line.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_MvParseLineInputs( Io_MvMod_t * p, char * pLine ) +{ + Vec_Ptr_t * vTokens = p->pMan->vTokens; + char * pToken; + int i; + Io_MvSplitIntoTokens( vTokens, pLine, '\0' ); + pToken = Vec_PtrEntry(vTokens, 0); + assert( !strcmp(pToken, "inputs") ); + Vec_PtrForEachEntryStart( vTokens, pToken, i, 1 ) + Io_ReadCreatePi( p->pNtk, pToken ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Parses the outputs line.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_MvParseLineOutputs( Io_MvMod_t * p, char * pLine ) +{ + Vec_Ptr_t * vTokens = p->pMan->vTokens; + char * pToken; + int i; + Io_MvSplitIntoTokens( vTokens, pLine, '\0' ); + pToken = Vec_PtrEntry(vTokens, 0); + assert( !strcmp(pToken, "outputs") ); + Vec_PtrForEachEntryStart( vTokens, pToken, i, 1 ) + Io_ReadCreatePo( p->pNtk, pToken ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Parses the latches line.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_MvParseLineLatch( Io_MvMod_t * p, char * pLine ) +{ + Vec_Ptr_t * vTokens = p->pMan->vTokens; + Abc_Obj_t * pObj, * pNet; + char * pToken; + char * pLatchType; + Abc_LatchInfo_t* pLatchInfo; + int Init; + Io_MvSplitIntoTokens( vTokens, pLine, '\0' ); + pToken = Vec_PtrEntry(vTokens,0); + assert( !strcmp(pToken, "latch") ); + if ( Vec_PtrSize(vTokens) < 3 ) + { + sprintf( p->pMan->sError, "Line %d: Latch does not have input name and output name.", Io_MvGetLine(p->pMan, pToken) ); + return 0; + } + // create latch + if ( p->pResetLatch == NULL ) + { + pObj = Io_ReadCreateLatch( p->pNtk, Vec_PtrEntry(vTokens,1), Vec_PtrEntry(vTokens,2) ); + + pLatchInfo = ((Abc_LatchInfo_t *)pObj->pData); + pLatchInfo->pClkName = strdup((char *)Vec_PtrEntry(vTokens,4)); + + pLatchType = (char *)Vec_PtrEntry(vTokens,3); + + if (!strcmp(pLatchType, "re")) + pLatchInfo->LatchType = ABC_RISING_EDGE; + else if (!strcmp(pLatchType, "fe")) + pLatchInfo->LatchType = ABC_FALLING_EDGE; + else if (!strcmp(pLatchType, "ah")) + pLatchInfo->LatchType = ABC_ACTIVE_HIGH; + else if (!strcmp(pLatchType, "al")) + pLatchInfo->LatchType = ABC_ACTIVE_LOW; + else + pLatchInfo->LatchType = ABC_ASYNC; // god knows when it is used... + + // get initial value + if ( p->pMan->fBlifMv ) + Abc_LatchSetInit0( pObj ); + else + { + if ( Vec_PtrSize(vTokens) > 6 ) + printf( "Warning: Line %d has .latch directive with unrecognized entries (the total of %d entries).\n", + Io_MvGetLine(p->pMan, pToken), Vec_PtrSize(vTokens) ); + if ( Vec_PtrSize(vTokens) > 3 ) + Init = atoi( Vec_PtrEntry(vTokens,3) ); + else + Init = 2; + if ( Init < 0 || Init > 2 ) + { + sprintf( p->pMan->sError, "Line %d: Initial state of the latch is incorrect \"%s\".", Io_MvGetLine(p->pMan, pToken), Vec_PtrEntry(vTokens,3) ); + return 0; + } + if ( Init == 0 ) + Abc_LatchSetInit0( pObj ); + else if ( Init == 1 ) + Abc_LatchSetInit1( pObj ); + else // if ( Init == 2 ) + Abc_LatchSetInitDc( pObj ); + } + } + else + { + // get the net corresponding to the output of the latch + pNet = Abc_NtkFindOrCreateNet( p->pNtk, Vec_PtrEntry(vTokens,2) ); + // get the net corresponding to the latch output (feeding into reset MUX) + pNet = Abc_NtkFindOrCreateNet( p->pNtk, Abc_ObjNameSuffix(pNet, "_out") ); + // create latch + pObj = Io_ReadCreateLatch( p->pNtk, Vec_PtrEntry(vTokens,1), Abc_ObjName(pNet) ); + Abc_LatchSetInit0( pObj ); + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Parses the subckt line.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_MvParseLineSubckt( Io_MvMod_t * p, char * pLine ) +{ + Vec_Ptr_t * vTokens = p->pMan->vTokens; + Abc_Ntk_t * pModel; + Abc_Obj_t * pBox, * pNet, * pTerm; + char * pToken, * pName, ** ppNames; + int nEquals, i, k; + + // split the line into tokens + nEquals = Io_MvCountChars( pLine, '=' ); + Io_MvSplitIntoTokensAndClear( vTokens, pLine, '\0', '=' ); + pToken = Vec_PtrEntry(vTokens,0); + assert( !strcmp(pToken, "subckt") ); + + // get the model for this box + pName = Vec_PtrEntry(vTokens,1); + pModel = Abc_LibFindModelByName( p->pMan->pDesign, pName ); + if ( pModel == NULL ) + { + sprintf( p->pMan->sError, "Line %d: Cannot find the model for subcircuit %s.", Io_MvGetLine(p->pMan, pToken), pName ); + return 0; + } + + // check if the number of tokens is correct + if ( nEquals != Abc_NtkPiNum(pModel) + Abc_NtkPoNum(pModel) ) + { + sprintf( p->pMan->sError, "Line %d: The number of ports (%d) in .subckt differs from the sum of PIs and POs of the model (%d).", + Io_MvGetLine(p->pMan, pToken), nEquals, Abc_NtkPiNum(pModel) + Abc_NtkPoNum(pModel) ); + return 0; + } + + // get the names + ppNames = (char **)Vec_PtrArray(vTokens) + 2 + p->pMan->fBlifMv; + + // create the box with these terminals + if ( Abc_NtkHasBlackbox(pModel) ) + pBox = Abc_NtkCreateBlackbox( p->pNtk ); + else + pBox = Abc_NtkCreateWhitebox( p->pNtk ); + pBox->pData = pModel; + if ( p->pMan->fBlifMv ) + Abc_ObjAssignName( pBox, Vec_PtrEntry(vTokens,2), NULL ); + Abc_NtkForEachPi( pModel, pTerm, i ) + { + // find this terminal among the formal inputs of the subcircuit + pName = Abc_ObjName(Abc_ObjFanout0(pTerm)); + for ( k = 0; k < nEquals; k++ ) + if ( !strcmp( ppNames[2*k], pName ) ) + break; + if ( k == nEquals ) + { + sprintf( p->pMan->sError, "Line %d: Cannot find PI \"%s\" of the model \"%s\" as a formal input of the subcircuit.", + Io_MvGetLine(p->pMan, pToken), pName, Abc_NtkName(pModel) ); + return 0; + } + // create the BI with the actual name + pNet = Abc_NtkFindOrCreateNet( p->pNtk, ppNames[2*k+1] ); + pTerm = Abc_NtkCreateBi( p->pNtk ); + Abc_ObjAddFanin( pBox, pTerm ); + Abc_ObjAddFanin( pTerm, pNet ); + } + Abc_NtkForEachPo( pModel, pTerm, i ) + { + // find this terminal among the formal outputs of the subcircuit + pName = Abc_ObjName(Abc_ObjFanin0(pTerm)); + for ( k = 0; k < nEquals; k++ ) + if ( !strcmp( ppNames[2*k], pName ) ) + break; + if ( k == nEquals ) + { + sprintf( p->pMan->sError, "Line %d: Cannot find PO \"%s\" of the modell \"%s\" as a formal output of the subcircuit.", + Io_MvGetLine(p->pMan, pToken), pName, Abc_NtkName(pModel) ); + return 0; + } + // create the BI with the actual name + pNet = Abc_NtkFindOrCreateNet( p->pNtk, ppNames[2*k+1] ); + pTerm = Abc_NtkCreateBo( p->pNtk ); + Abc_ObjAddFanin( pNet, pTerm ); + Abc_ObjAddFanin( pTerm, pBox ); + } + return 1; +} + + +/**Function************************************************************* + + Synopsis [Parses the mv line.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_MvParseLineMv( Io_MvMod_t * p, char * pLine ) +{ + Vec_Ptr_t * vTokens = p->pMan->vTokens; + Abc_Obj_t * pObj; + Io_MvVar_t * pVar; + Extra_MmFlex_t * pFlex; + char * pName; + int nCommas, nValues, i, k; + // count commas and get the tokens + nCommas = Io_MvCountChars( pLine, ',' ); + Io_MvSplitIntoTokensAndClear( vTokens, pLine, '\0', ',' ); + pName = Vec_PtrEntry(vTokens,0); + assert( !strcmp(pName, "mv") ); + // get the number of values + if ( Vec_PtrSize(vTokens) <= nCommas + 2 ) + { + sprintf( p->pMan->sError, "Line %d: The number of values in not specified in .mv line.", Io_MvGetLine(p->pMan, pName), pName ); + return 0; + } + nValues = atoi( Vec_PtrEntry(vTokens,nCommas+2) ); + if ( nValues < 2 || nValues > IO_BLIFMV_MAXVALUES ) + { + sprintf( p->pMan->sError, "Line %d: The number of values (%d) is incorrect (should be >= 2 and <= %d).", + Io_MvGetLine(p->pMan, pName), nValues, IO_BLIFMV_MAXVALUES ); + return 0; + } + // if there is no symbolic values, quit + if ( nValues == 2 && Vec_PtrSize(vTokens) == nCommas + 3 ) + return 1; + if ( Vec_PtrSize(vTokens) > nCommas + 3 && Vec_PtrSize(vTokens) - (nCommas + 3) != nValues ) + { + sprintf( p->pMan->sError, "Line %d: Wrong number (%d) of symbolic value names (should be %d).", + Io_MvGetLine(p->pMan, pName), Vec_PtrSize(vTokens) - (nCommas + 3), nValues ); + return 0; + } + // go through variables + pFlex = Abc_NtkMvVarMan( p->pNtk ); + for ( i = 0; i <= nCommas; i++ ) + { + pName = Vec_PtrEntry( vTokens, i+1 ); + pObj = Abc_NtkFindOrCreateNet( p->pNtk, pName ); + // allocate variable + pVar = (Io_MvVar_t *)Extra_MmFlexEntryFetch( pFlex, sizeof(Io_MvVar_t) ); + pVar->nValues = nValues; + pVar->pNames = NULL; + // create names + if ( Vec_PtrSize(vTokens) > nCommas + 3 ) + { + pVar->pNames = (char **)Extra_MmFlexEntryFetch( pFlex, sizeof(char *) * nValues ); + Vec_PtrForEachEntryStart( vTokens, pName, k, nCommas + 3 ) + { + pVar->pNames[k-(nCommas + 3)] = (char *)Extra_MmFlexEntryFetch( pFlex, strlen(pName) + 1 ); + strcpy( pVar->pNames[k-(nCommas + 3)], pName ); + } + } + // save the variable + Abc_ObjSetMvVar( pObj, pVar ); + } + // make sure the names are unique + if ( pVar->pNames ) + { + for ( i = 0; i < nValues; i++ ) + for ( k = i+1; k < nValues; k++ ) + if ( !strcmp(pVar->pNames[i], pVar->pNames[k]) ) + { + pName = Vec_PtrEntry(vTokens,0); + sprintf( p->pMan->sError, "Line %d: Symbolic value name \"%s\" is repeated in .mv line.", + Io_MvGetLine(p->pMan, pName), pVar->pNames[i] ); + return 0; + } + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Writes the values into the BLIF-MV representation for the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_MvWriteValues( Abc_Obj_t * pNode, Vec_Str_t * vFunc ) +{ + char Buffer[10]; + Abc_Obj_t * pFanin; + int i; + // add the fanin number of values + Abc_ObjForEachFanin( pNode, pFanin, i ) + { + sprintf( Buffer, "%d", Abc_ObjMvVarNum(pFanin) ); + Vec_StrAppend( vFunc, Buffer ); + Vec_StrPush( vFunc, ' ' ); + } + // add the node number of values + sprintf( Buffer, "%d", Abc_ObjMvVarNum(Abc_ObjFanout0(pNode)) ); + Vec_StrAppend( vFunc, Buffer ); + Vec_StrPush( vFunc, '\n' ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Translated one literal.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_MvParseLiteralMv( Io_MvMod_t * p, Abc_Obj_t * pNode, char * pToken, Vec_Str_t * vFunc, int iLit ) +{ + char Buffer[10]; + Io_MvVar_t * pVar; + Abc_Obj_t * pFanin, * pNet; + char * pCur, * pNext; + int i; + // consider the equality literal + if ( pToken[0] == '=' ) + { + // find the fanins + Abc_ObjForEachFanin( pNode, pFanin, i ) + if ( !strcmp( Abc_ObjName(pFanin), pToken + 1 ) ) + break; + if ( i == Abc_ObjFaninNum(pNode) ) + { + sprintf( p->pMan->sError, "Line %d: Node name in the table \"%s\" cannot be found on .names line.", + Io_MvGetLine(p->pMan, pToken), pToken + 1 ); + return 0; + } + Vec_StrPush( vFunc, '=' ); + sprintf( Buffer, "%d", i ); + Vec_StrAppend( vFunc, Buffer ); + Vec_StrPush( vFunc, (char)((iLit == -1)? '\n' : ' ') ); + return 1; + } + // consider regular literal + assert( iLit < Abc_ObjFaninNum(pNode) ); + pNet = iLit >= 0 ? Abc_ObjFanin(pNode, iLit) : Abc_ObjFanout0(pNode); + pVar = Abc_ObjMvVar( pNet ); + // if the var is absent or has no symbolic values quit + if ( pVar == NULL || pVar->pNames == NULL ) + { + Vec_StrAppend( vFunc, pToken ); + Vec_StrPush( vFunc, (char)((iLit == -1)? '\n' : ' ') ); + return 1; + } + // parse the literal using symbolic values + for ( pCur = pToken; *pCur; pCur++ ) + { + if ( Io_MvCharIsMvSymb(*pCur) ) + { + Vec_StrPush( vFunc, *pCur ); + continue; + } + // find the next MvSymb char + for ( pNext = pCur+1; *pNext; pNext++ ) + if ( Io_MvCharIsMvSymb(*pNext) ) + break; + // look for the value name + for ( i = 0; i < pVar->nValues; i++ ) + if ( !strncmp( pVar->pNames[i], pCur, pNext-pCur ) ) + break; + if ( i == pVar->nValues ) + { + *pNext = 0; + sprintf( p->pMan->sError, "Line %d: Cannot find value name \"%s\" among the value names of variable \"%s\".", + Io_MvGetLine(p->pMan, pToken), pCur, Abc_ObjName(pNet) ); + return 0; + } + // value name is found + sprintf( Buffer, "%d", i ); + Vec_StrAppend( vFunc, Buffer ); + // update the pointer + pCur = pNext - 1; + } + Vec_StrPush( vFunc, (char)((iLit == -1)? '\n' : ' ') ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Constructs the MV-SOP cover from the file parsing info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static char * Io_MvParseTableMv( Io_MvMod_t * p, Abc_Obj_t * pNode, Vec_Ptr_t * vTokens2, int nInputs, int nOutputs, int iOut ) +{ + Vec_Str_t * vFunc = p->pMan->vFunc; + char * pFirst, * pToken; + int iStart, i; + // prepare the place for the cover + Vec_StrClear( vFunc ); + // write the number of values +// Io_MvWriteValues( pNode, vFunc ); + // get the first token + pFirst = Vec_PtrEntry( vTokens2, 0 ); + if ( pFirst[0] == '.' ) + { + // write the default literal + Vec_StrPush( vFunc, 'd' ); + pToken = Vec_PtrEntry(vTokens2, 1 + iOut); + if ( !Io_MvParseLiteralMv( p, pNode, pToken, vFunc, -1 ) ) + return NULL; + iStart = 1 + nOutputs; + } + else + iStart = 0; + // write the remaining literals + while ( iStart < Vec_PtrSize(vTokens2) ) + { + // input literals + for ( i = 0; i < nInputs; i++ ) + { + pToken = Vec_PtrEntry( vTokens2, iStart + i ); + if ( !Io_MvParseLiteralMv( p, pNode, pToken, vFunc, i ) ) + return NULL; + } + // output literal + pToken = Vec_PtrEntry( vTokens2, iStart + nInputs + iOut ); + if ( !Io_MvParseLiteralMv( p, pNode, pToken, vFunc, -1 ) ) + return NULL; + // update the counter + iStart += nInputs + nOutputs; + } + Vec_StrPush( vFunc, '\0' ); + return Vec_StrArray( vFunc ); +} + +/**Function************************************************************* + + Synopsis [Adds reset circuitry corresponding to latch with pName.] + + Description [Returns the reset node's net.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static Abc_Obj_t * Io_MvParseAddResetCircuit( Io_MvMod_t * p, char * pName ) +{ + char Buffer[50]; + Abc_Obj_t * pNode, * pData0Net, * pData1Net, * pResetLONet, * pOutNet; + Io_MvVar_t * pVar; + // make sure the reset latch exists + assert( p->pResetLatch != NULL ); + // get the reset net + pResetLONet = Abc_ObjFanout0(Abc_ObjFanout0(p->pResetLatch)); + // get the output net + pOutNet = Abc_NtkFindOrCreateNet( p->pNtk, pName ); + // get the data nets + pData0Net = Abc_NtkFindOrCreateNet( p->pNtk, Abc_ObjNameSuffix(pOutNet, "_reset") ); + pData1Net = Abc_NtkFindOrCreateNet( p->pNtk, Abc_ObjNameSuffix(pOutNet, "_out") ); + // duplicate MV variables + if ( Abc_NtkMvVar(p->pNtk) ) + { + pVar = Abc_ObjMvVar( pOutNet ); + Abc_ObjSetMvVar( pData0Net, Abc_NtkMvVarDup(p->pNtk, pVar) ); + Abc_ObjSetMvVar( pData1Net, Abc_NtkMvVarDup(p->pNtk, pVar) ); + } + // create the node + pNode = Abc_NtkCreateNode( p->pNtk ); + // create the output net + Abc_ObjAddFanin( pOutNet, pNode ); + // create the function + if ( p->pMan->fBlifMv ) + { +// Vec_Att_t * p = Abc_NtkMvVar( pNtk ); + int nValues = Abc_ObjMvVarNum(pOutNet); +// sprintf( Buffer, "2 %d %d %d\n1 - - =1\n0 - - =2\n", nValues, nValues, nValues ); + sprintf( Buffer, "1 - - =1\n0 - - =2\n" ); + pNode->pData = Abc_SopRegister( p->pNtk->pManFunc, Buffer ); + } + else + pNode->pData = Abc_SopCreateMux( p->pNtk->pManFunc ); + // add nets + Abc_ObjAddFanin( pNode, pResetLONet ); + Abc_ObjAddFanin( pNode, pData1Net ); + Abc_ObjAddFanin( pNode, pData0Net ); + return pData0Net; +} + +/**Function************************************************************* + + Synopsis [Parses the nodes line.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_MvParseLineNamesMvOne( Io_MvMod_t * p, Vec_Ptr_t * vTokens, Vec_Ptr_t * vTokens2, int nInputs, int nOutputs, int iOut, int fReset ) +{ + Abc_Obj_t * pNet, * pNode; + char * pName; + // get the output name + pName = Vec_PtrEntry( vTokens, Vec_PtrSize(vTokens) - nOutputs + iOut ); + // create the node + if ( fReset ) + { + pNet = Abc_NtkFindNet( p->pNtk, pName ); + if ( pNet == NULL ) + { + sprintf( p->pMan->sError, "Line %d: Latch with output signal \"%s\" does not exist.", Io_MvGetLine(p->pMan, pName), pName ); + return 0; + } +/* + if ( !Abc_ObjIsBo(Abc_ObjFanin0(pNet)) ) + { + sprintf( p->pMan->sError, "Line %d: Reset line \"%s\" defines signal that is not a latch output.", Io_MvGetLine(p->pMan, pName), pName ); + return 0; + } +*/ + // construct the reset circuit and get the reset net feeding into it + pNet = Io_MvParseAddResetCircuit( p, pName ); + // create fanins + pNode = Io_ReadCreateNode( p->pNtk, Abc_ObjName(pNet), (char **)(vTokens->pArray + 1), nInputs ); + assert( nInputs == Vec_PtrSize(vTokens) - 2 ); + } + else + { + pNet = Abc_NtkFindOrCreateNet( p->pNtk, pName ); + if ( Abc_ObjFaninNum(pNet) > 0 ) + { + sprintf( p->pMan->sError, "Line %d: Signal \"%s\" is defined more than once.", Io_MvGetLine(p->pMan, pName), pName ); + return 0; + } + pNode = Io_ReadCreateNode( p->pNtk, pName, (char **)(vTokens->pArray + 1), nInputs ); + } + // create the cover + pNode->pData = Io_MvParseTableMv( p, pNode, vTokens2, nInputs, nOutputs, iOut ); + if ( pNode->pData == NULL ) + return 0; + pNode->pData = Abc_SopRegister( p->pNtk->pManFunc, pNode->pData ); +//printf( "Finished parsing node \"%s\" with table:\n%s\n", pName, pNode->pData ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Parses the nodes line.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_MvParseLineNamesMv( Io_MvMod_t * p, char * pLine, int fReset ) +{ + Vec_Ptr_t * vTokens = p->pMan->vTokens; + Vec_Ptr_t * vTokens2 = p->pMan->vTokens2; + Abc_Obj_t * pNet; + char * pName, * pFirst, * pArrow; + int nInputs, nOutputs, nLiterals, nLines, i; + assert( p->pMan->fBlifMv ); + // get the arrow if it is present + pArrow = Io_MvFindArrow( pLine ); + if ( !p->pMan->fBlifMv && pArrow ) + { + sprintf( p->pMan->sError, "Line %d: Multi-output node symbol (->) in binary BLIF file.", Io_MvGetLine(p->pMan, pLine) ); + return 0; + } + // split names line into tokens + Io_MvSplitIntoTokens( vTokens, pLine, '\0' ); + if ( fReset ) + assert( !strcmp(Vec_PtrEntry(vTokens,0), "r") || !strcmp(Vec_PtrEntry(vTokens,0), "reset") ); + else + assert( !strcmp(Vec_PtrEntry(vTokens,0), "names") || !strcmp(Vec_PtrEntry(vTokens,0), "table") ); + // find the number of inputs and outputs + nInputs = Vec_PtrSize(vTokens) - 2; + nOutputs = 1; + if ( pArrow != NULL ) + { + for ( i = Vec_PtrSize(vTokens) - 2; i >= 1; i-- ) + if ( pArrow < (char*)Vec_PtrEntry(vTokens,i) ) + { + nInputs--; + nOutputs++; + } + } + // split table into tokens + pName = Vec_PtrEntryLast( vTokens ); + Io_MvSplitIntoTokensMv( vTokens2, pName + strlen(pName) ); + pFirst = Vec_PtrEntry( vTokens2, 0 ); + if ( pFirst[0] == '.' ) + { + assert( pFirst[1] == 'd' ); + nLiterals = Vec_PtrSize(vTokens2) - 1 - nOutputs; + } + else + nLiterals = Vec_PtrSize(vTokens2); + // check the number of lines + if ( nLiterals % (nInputs + nOutputs) != 0 ) + { + sprintf( p->pMan->sError, "Line %d: Wrong number of literals in the table of node \"%s\". (Spaces inside literals are not allowed.)", Io_MvGetLine(p->pMan, pFirst), pName ); + return 0; + } + // check for the ND table + nLines = nLiterals / (nInputs + nOutputs); + if ( nInputs == 0 && nLines > 1 ) + { + // add the outputs to the PIs + for ( i = 0; i < nOutputs; i++ ) + { + pName = Vec_PtrEntry( vTokens, Vec_PtrSize(vTokens) - nOutputs + i ); + // get the net corresponding to this node + pNet = Abc_NtkFindOrCreateNet(p->pNtk, pName); + if ( fReset ) + { + assert( p->pResetLatch != NULL ); + // construct the reset circuit and get the reset net feeding into it + pNet = Io_MvParseAddResetCircuit( p, pName ); + } + // add the new PI node +// Abc_ObjAddFanin( pNet, Abc_NtkCreatePi(p->pNtk) ); +// fprintf( stdout, "Io_ReadBlifMv(): Adding PI for internal non-deterministic node \"%s\".\n", pName ); + p->pMan->nNDnodes++; + Abc_ObjAddFanin( pNet, Abc_NtkCreateNodeConst0(p->pNtk) ); + } + return 1; + } + // iterate through the outputs + for ( i = 0; i < nOutputs; i++ ) + { + if ( !Io_MvParseLineNamesMvOne( p, vTokens, vTokens2, nInputs, nOutputs, i, fReset ) ) + return 0; + } + return 1; +} + + +/**Function************************************************************* + + Synopsis [Constructs the SOP cover from the file parsing info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static char * Io_MvParseTableBlif( Io_MvMod_t * p, char * pTable, int nFanins ) +{ + Vec_Ptr_t * vTokens = p->pMan->vTokens; + Vec_Str_t * vFunc = p->pMan->vFunc; + char * pProduct, * pOutput; + int i, Polarity = -1; + + p->pMan->nTablesRead++; + // get the tokens + Io_MvSplitIntoTokens( vTokens, pTable, '.' ); + if ( Vec_PtrSize(vTokens) == 0 ) + return Abc_SopCreateConst0( p->pNtk->pManFunc ); + if ( Vec_PtrSize(vTokens) == 1 ) + { + pOutput = Vec_PtrEntry( vTokens, 0 ); + if ( ((pOutput[0] - '0') & 0x8E) || pOutput[1] ) + { + sprintf( p->pMan->sError, "Line %d: Constant table has wrong output value \"%s\".", Io_MvGetLine(p->pMan, pOutput), pOutput ); + return NULL; + } + return pOutput[0] == '0' ? Abc_SopCreateConst0(p->pNtk->pManFunc) : Abc_SopCreateConst1(p->pNtk->pManFunc); + } + pProduct = Vec_PtrEntry( vTokens, 0 ); + if ( Vec_PtrSize(vTokens) % 2 == 1 ) + { + sprintf( p->pMan->sError, "Line %d: Table has odd number of tokens (%d).", Io_MvGetLine(p->pMan, pProduct), Vec_PtrSize(vTokens) ); + return NULL; + } + // parse the table + Vec_StrClear( vFunc ); + for ( i = 0; i < Vec_PtrSize(vTokens)/2; i++ ) + { + pProduct = Vec_PtrEntry( vTokens, 2*i + 0 ); + pOutput = Vec_PtrEntry( vTokens, 2*i + 1 ); + if ( strlen(pProduct) != (unsigned)nFanins ) + { + sprintf( p->pMan->sError, "Line %d: Cube \"%s\" has size different from the fanin count (%d).", Io_MvGetLine(p->pMan, pProduct), pProduct, nFanins ); + return NULL; + } + if ( ((pOutput[0] - '0') & 0x8E) || pOutput[1] ) + { + sprintf( p->pMan->sError, "Line %d: Output value \"%s\" is incorrect.", Io_MvGetLine(p->pMan, pProduct), pOutput ); + return NULL; + } + if ( Polarity == -1 ) + Polarity = pOutput[0] - '0'; + else if ( Polarity != pOutput[0] - '0' ) + { + sprintf( p->pMan->sError, "Line %d: Output value \"%s\" differs from the value in the first line of the table (%d).", Io_MvGetLine(p->pMan, pProduct), pOutput, Polarity ); + return NULL; + } + // parse one product + Vec_StrAppend( vFunc, pProduct ); + Vec_StrPush( vFunc, ' ' ); + Vec_StrPush( vFunc, pOutput[0] ); + Vec_StrPush( vFunc, '\n' ); + } + Vec_StrPush( vFunc, '\0' ); + return Vec_StrArray( vFunc ); +} + +/**Function************************************************************* + + Synopsis [Parses the nodes line.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_MvParseLineNamesBlif( Io_MvMod_t * p, char * pLine ) +{ + Vec_Ptr_t * vTokens = p->pMan->vTokens; + Abc_Obj_t * pNet, * pNode; + char * pName; + assert( !p->pMan->fBlifMv ); + Io_MvSplitIntoTokens( vTokens, pLine, '\0' ); + // parse the mapped node + if ( !strcmp(Vec_PtrEntry(vTokens,0), "gate") ) + return Io_MvParseLineGateBlif( p, vTokens ); + // parse the regular name line + assert( !strcmp(Vec_PtrEntry(vTokens,0), "names") ); + pName = Vec_PtrEntryLast( vTokens ); + pNet = Abc_NtkFindOrCreateNet( p->pNtk, pName ); + if ( Abc_ObjFaninNum(pNet) > 0 ) + { + sprintf( p->pMan->sError, "Line %d: Signal \"%s\" is defined more than once.", Io_MvGetLine(p->pMan, pName), pName ); + return 0; + } + // create fanins + pNode = Io_ReadCreateNode( p->pNtk, pName, (char **)(vTokens->pArray + 1), Vec_PtrSize(vTokens) - 2 ); + // parse the table of this node + pNode->pData = Io_MvParseTableBlif( p, pName + strlen(pName), Abc_ObjFaninNum(pNode) ); + if ( pNode->pData == NULL ) + return 0; + pNode->pData = Abc_SopRegister( p->pNtk->pManFunc, pNode->pData ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Duplicate the MV variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Io_MvVar_t * Abc_NtkMvVarDup( Abc_Ntk_t * pNtk, Io_MvVar_t * pVar ) +{ + Extra_MmFlex_t * pFlex; + Io_MvVar_t * pVarDup; + int i; + if ( pVar == NULL ) + return NULL; + pFlex = Abc_NtkMvVarMan( pNtk ); + assert( pFlex != NULL ); + pVarDup = (Io_MvVar_t *)Extra_MmFlexEntryFetch( pFlex, sizeof(Io_MvVar_t) ); + pVarDup->nValues = pVar->nValues; + pVarDup->pNames = NULL; + if ( pVar->pNames == NULL ) + return pVarDup; + pVarDup->pNames = (char **)Extra_MmFlexEntryFetch( pFlex, sizeof(char *) * pVar->nValues ); + for ( i = 0; i < pVar->nValues; i++ ) + { + pVarDup->pNames[i] = (char *)Extra_MmFlexEntryFetch( pFlex, strlen(pVar->pNames[i]) + 1 ); + strcpy( pVarDup->pNames[i], pVar->pNames[i] ); + } + return pVarDup; +} + + +#include "mio.h" +#include "main.h" + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static char * Io_ReadBlifCleanName( char * pName ) +{ + int i, Length; + Length = strlen(pName); + for ( i = 0; i < Length; i++ ) + if ( pName[i] == '=' ) + return pName + i + 1; + return NULL; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Io_MvParseLineGateBlif( Io_MvMod_t * p, Vec_Ptr_t * vTokens ) +{ + Mio_Library_t * pGenlib; + Mio_Gate_t * pGate; + Abc_Obj_t * pNode; + char ** ppNames, * pName; + int i, nNames; + + pName = vTokens->pArray[0]; + + // check that the library is available + pGenlib = Abc_FrameReadLibGen(); + if ( pGenlib == NULL ) + { + sprintf( p->pMan->sError, "Line %d: The current library is not available.", Io_MvGetLine(p->pMan, pName) ); + return 0; + } + + // create a new node and add it to the network + if ( vTokens->nSize < 2 ) + { + sprintf( p->pMan->sError, "Line %d: The .gate line has less than two tokens.", Io_MvGetLine(p->pMan, pName) ); + return 0; + } + + // get the gate + pGate = Mio_LibraryReadGateByName( pGenlib, vTokens->pArray[1] ); + if ( pGate == NULL ) + { + sprintf( p->pMan->sError, "Line %d: Cannot find gate \"%s\" in the library.", Io_MvGetLine(p->pMan, pName), vTokens->pArray[1] ); + return 0; + } + + // if this is the first line with gate, update the network type + if ( Abc_NtkNodeNum(p->pNtk) == 0 ) + { + assert( p->pNtk->ntkFunc == ABC_FUNC_SOP ); + p->pNtk->ntkFunc = ABC_FUNC_MAP; + Extra_MmFlexStop( p->pNtk->pManFunc ); + p->pNtk->pManFunc = pGenlib; + } + + // remove the formal parameter names + for ( i = 2; i < vTokens->nSize; i++ ) + { + vTokens->pArray[i] = Io_ReadBlifCleanName( vTokens->pArray[i] ); + if ( vTokens->pArray[i] == NULL ) + { + sprintf( p->pMan->sError, "Line %d: Invalid gate input assignment.", Io_MvGetLine(p->pMan, pName) ); + return 0; + } + } + + // create the node + ppNames = (char **)vTokens->pArray + 2; + nNames = vTokens->nSize - 3; + pNode = Io_ReadCreateNode( p->pNtk, ppNames[nNames], ppNames, nNames ); + + // set the pointer to the functionality of the node + Abc_ObjSetData( pNode, pGate ); + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/io/ioReadDsd.c b/abc_with_bb_support/src/base/io/ioReadDsd.c new file mode 100644 index 000000000..018184f5d --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioReadDsd.c @@ -0,0 +1,308 @@ +/**CFile**************************************************************** + + FileName [ioReadDsd.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedure to read network from file.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: ioReadDsd.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Finds the end of the part.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Io_ReadDsdFindEnd( char * pCur ) +{ + char * pEnd; + int nParts = 0; + assert( *pCur == '(' ); + for ( pEnd = pCur; *pEnd; pEnd++ ) + { + if ( *pEnd == '(' ) + nParts++; + else if ( *pEnd == ')' ) + nParts--; + if ( nParts == 0 ) + return pEnd; + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [Splits the formula into parts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_ReadDsdStrSplit( char * pCur, char * pParts[], int * pTypeXor ) +{ + int fAnd = 0, fXor = 0, fPri = 0, nParts = 0; + assert( *pCur ); + // process the parts + while ( 1 ) + { + // save the current part + pParts[nParts++] = pCur; + // skip the complement + if ( *pCur == '!' ) + pCur++; + // skip var + if ( *pCur >= 'a' && *pCur <= 'z' ) + pCur++; + else + { + // skip hex truth table + while ( (*pCur >= '0' && *pCur <= '9') || (*pCur >= 'A' && *pCur <= 'F') ) + pCur++; + // process parantheses + if ( *pCur != '(' ) + { + printf( "Cannot find the opening paranthesis.\n" ); + break; + } + // find the corresponding closing paranthesis + pCur = Io_ReadDsdFindEnd( pCur ); + if ( pCur == NULL ) + { + printf( "Cannot find the closing paranthesis.\n" ); + break; + } + pCur++; + } + // check the end + if ( *pCur == 0 ) + break; + // check symbol + if ( *pCur != '*' && *pCur != '+' && *pCur != ',' ) + { + printf( "Wrong separating symbol.\n" ); + break; + } + // remember the symbol + fAnd |= (*pCur == '*'); + fXor |= (*pCur == '+'); + fPri |= (*pCur == ','); + *pCur++ = 0; + } + // check separating symbols + if ( fAnd + fXor + fPri > 1 ) + { + printf( "Different types of separating symbol ennPartsed.\n" ); + return 0; + } + *pTypeXor = fXor; + return nParts; +} + +/**Function************************************************************* + + Synopsis [Recursively parses the formula.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Io_ReadDsd_rec( Abc_Ntk_t * pNtk, char * pCur, char * pSop ) +{ + Abc_Obj_t * pObj, * pFanin; + char * pEnd, * pParts[32]; + int i, nParts, TypeExor; + + // consider complemented formula + if ( *pCur == '!' ) + { + pObj = Io_ReadDsd_rec( pNtk, pCur + 1, NULL ); + return Abc_NtkCreateNodeInv( pNtk, pObj ); + } + if ( *pCur == '(' ) + { + assert( pCur[strlen(pCur)-1] == ')' ); + pCur[strlen(pCur)-1] = 0; + nParts = Io_ReadDsdStrSplit( pCur+1, pParts, &TypeExor ); + if ( nParts == 0 ) + { + Abc_NtkDelete( pNtk ); + return NULL; + } + pObj = Abc_NtkCreateNode( pNtk ); + if ( pSop ) + { +// for ( i = nParts - 1; i >= 0; i-- ) + for ( i = 0; i < nParts; i++ ) + { + pFanin = Io_ReadDsd_rec( pNtk, pParts[i], NULL ); + if ( pFanin == NULL ) + return NULL; + Abc_ObjAddFanin( pObj, pFanin ); + } + } + else + { + for ( i = 0; i < nParts; i++ ) + { + pFanin = Io_ReadDsd_rec( pNtk, pParts[i], NULL ); + if ( pFanin == NULL ) + return NULL; + Abc_ObjAddFanin( pObj, pFanin ); + } + } + if ( pSop ) + pObj->pData = Abc_SopRegister( pNtk->pManFunc, pSop ); + else if ( TypeExor ) + pObj->pData = Abc_SopCreateXorSpecial( pNtk->pManFunc, nParts ); + else + pObj->pData = Abc_SopCreateAnd( pNtk->pManFunc, nParts, NULL ); + return pObj; + } + if ( *pCur >= 'a' && *pCur <= 'z' ) + { + assert( *(pCur+1) == 0 ); + return Abc_NtkPi( pNtk, *pCur - 'a' ); + } + + // skip hex truth table + pEnd = pCur; + while ( (*pEnd >= '0' && *pEnd <= '9') || (*pEnd >= 'A' && *pEnd <= 'F') ) + pEnd++; + if ( *pEnd != '(' ) + { + printf( "Cannot find the end of hexidecimal truth table.\n" ); + return NULL; + } + + // parse the truth table + *pEnd = 0; + pSop = Abc_SopFromTruthHex( pCur ); + *pEnd = '('; + pObj = Io_ReadDsd_rec( pNtk, pEnd, pSop ); + free( pSop ); + return pObj; +} + +/**Function************************************************************* + + Synopsis [Derives the DSD network of the formula.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Io_ReadDsd( char * pForm ) +{ + Abc_Ntk_t * pNtk; + Abc_Obj_t * pObj, * pTop; + Vec_Ptr_t * vNames; + char * pCur, * pFormCopy; + int i, nInputs; + + // count the number of elementary variables + nInputs = 0; + for ( pCur = pForm; *pCur; pCur++ ) + if ( *pCur >= 'a' && *pCur <= 'z' ) + nInputs = ABC_MAX( nInputs, *pCur - 'a' ); + nInputs++; + + // create the network + pNtk = Abc_NtkAlloc( ABC_NTK_LOGIC, ABC_FUNC_SOP, 1 ); + pNtk->pName = Extra_UtilStrsav( "dsd" ); + + // create PIs + vNames = Abc_NodeGetFakeNames( nInputs ); + for ( i = 0; i < nInputs; i++ ) + Abc_ObjAssignName( Abc_NtkCreatePi(pNtk), Vec_PtrEntry(vNames, i), NULL ); + Abc_NodeFreeNames( vNames ); + + // transform the formula by inserting parantheses + // this transforms strings like PRIME(a,b,cd) into (PRIME((a),(b),(cd))) + pCur = pFormCopy = ALLOC( char, 3 * strlen(pForm) + 10 ); + *pCur++ = '('; + for ( ; *pForm; pForm++ ) + if ( *pForm == '(' ) + { + *pCur++ = '('; + *pCur++ = '('; + } + else if ( *pForm == ')' ) + { + *pCur++ = ')'; + *pCur++ = ')'; + } + else if ( *pForm == ',' ) + { + *pCur++ = ')'; + *pCur++ = ','; + *pCur++ = '('; + } + else + *pCur++ = *pForm; + *pCur++ = ')'; + *pCur = 0; + + // parse the formula + pObj = Io_ReadDsd_rec( pNtk, pFormCopy, NULL ); + free( pFormCopy ); + if ( pObj == NULL ) + return NULL; + + // create output + pTop = Abc_NtkCreatePo(pNtk); + Abc_ObjAssignName( pTop, "F", NULL ); + Abc_ObjAddFanin( pTop, pObj ); + + // create the only PO + if ( !Abc_NtkCheck( pNtk ) ) + { + fprintf( stdout, "Io_ReadDsd(): Network check has failed.\n" ); + Abc_NtkDelete( pNtk ); + return NULL; + } + return pNtk; +} + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + + diff --git a/abc_with_bb_support/src/base/io/ioReadEdif.c b/abc_with_bb_support/src/base/io/ioReadEdif.c new file mode 100644 index 000000000..6b0c5d493 --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioReadEdif.c @@ -0,0 +1,235 @@ +/**CFile**************************************************************** + + FileName [ioReadEdif.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedure to read ISCAS benchmarks in EDIF.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: ioReadEdif.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Abc_Ntk_t * Io_ReadEdifNetwork( Extra_FileReader_t * p ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Reads the network from an EDIF file.] + + Description [Works only for the ISCAS benchmarks.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Io_ReadEdif( char * pFileName, int fCheck ) +{ + Extra_FileReader_t * p; + Abc_Ntk_t * pNtk; + + printf( "Currently this parser does not work!\n" ); + return NULL; + + // start the file + p = Extra_FileReaderAlloc( pFileName, "#", "\n\r", " \t()" ); + if ( p == NULL ) + return NULL; + + // read the network + pNtk = Io_ReadEdifNetwork( p ); + Extra_FileReaderFree( p ); + if ( pNtk == NULL ) + return NULL; + + // make sure that everything is okay with the network structure + if ( fCheck && !Abc_NtkCheckRead( pNtk ) ) + { + printf( "Io_ReadEdif: The network check has failed.\n" ); + Abc_NtkDelete( pNtk ); + return NULL; + } + return pNtk; +} +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Io_ReadEdifNetwork( Extra_FileReader_t * p ) +{ + ProgressBar * pProgress; + Vec_Ptr_t * vTokens; + Abc_Ntk_t * pNtk; + Abc_Obj_t * pNet, * pObj, * pFanout; + char * pGateName, * pNetName; + int fTokensReady, iLine, i; + + // read the first line + vTokens = Extra_FileReaderGetTokens(p); + if ( strcmp( vTokens->pArray[0], "edif" ) != 0 ) + { + printf( "%s: Wrong input file format.\n", Extra_FileReaderGetFileName(p) ); + return NULL; + } + + // allocate the empty network + pNtk = Abc_NtkStartRead( Extra_FileReaderGetFileName(p) ); + + // go through the lines of the file + fTokensReady = 0; + pProgress = Extra_ProgressBarStart( stdout, Extra_FileReaderGetFileSize(p) ); + for ( iLine = 1; fTokensReady || (vTokens = Extra_FileReaderGetTokens(p)); iLine++ ) + { + Extra_ProgressBarUpdate( pProgress, Extra_FileReaderGetCurPosition(p), NULL ); + + // get the type of the line + fTokensReady = 0; + if ( strcmp( vTokens->pArray[0], "instance" ) == 0 ) + { + pNetName = vTokens->pArray[1]; + pNet = Abc_NtkFindOrCreateNet( pNtk, pNetName ); + vTokens = Extra_FileReaderGetTokens(p); + vTokens = Extra_FileReaderGetTokens(p); + pGateName = vTokens->pArray[1]; + if ( strncmp( pGateName, "Flip", 4 ) == 0 ) + { + pObj = Abc_NtkCreateLatch( pNtk ); + Abc_LatchSetInit0( pObj ); + } + else + { + pObj = Abc_NtkCreateNode( pNtk ); +// pObj->pData = Abc_NtkRegisterName( pNtk, pGateName ); + pObj->pData = Extra_UtilStrsav( pGateName ); // memory leak!!! + } + Abc_ObjAddFanin( pNet, pObj ); + } + else if ( strcmp( vTokens->pArray[0], "net" ) == 0 ) + { + pNetName = vTokens->pArray[1]; + if ( strcmp( pNetName, "CK" ) == 0 || strcmp( pNetName, "RESET" ) == 0 ) + continue; + if ( strcmp( pNetName + strlen(pNetName) - 4, "_out" ) == 0 ) + pNetName[strlen(pNetName) - 4] = 0; + pNet = Abc_NtkFindNet( pNtk, pNetName ); + assert( pNet ); + vTokens = Extra_FileReaderGetTokens(p); + vTokens = Extra_FileReaderGetTokens(p); + vTokens = Extra_FileReaderGetTokens(p); + while ( strcmp( vTokens->pArray[0], "portRef" ) == 0 ) + { + if ( strcmp( pNetName, vTokens->pArray[3] ) != 0 ) + { + pFanout = Abc_NtkFindNet( pNtk, vTokens->pArray[3] ); + Abc_ObjAddFanin( Abc_ObjFanin0(pFanout), pNet ); + } + vTokens = Extra_FileReaderGetTokens(p); + } + fTokensReady = 1; + } + else if ( strcmp( vTokens->pArray[0], "library" ) == 0 ) + { + vTokens = Extra_FileReaderGetTokens(p); + vTokens = Extra_FileReaderGetTokens(p); + vTokens = Extra_FileReaderGetTokens(p); + vTokens = Extra_FileReaderGetTokens(p); + vTokens = Extra_FileReaderGetTokens(p); + while ( strcmp( vTokens->pArray[0], "port" ) == 0 ) + { + pNetName = vTokens->pArray[1]; + if ( strcmp( pNetName, "CK" ) == 0 || strcmp( pNetName, "RESET" ) == 0 ) + { + vTokens = Extra_FileReaderGetTokens(p); + continue; + } + if ( strcmp( pNetName + strlen(pNetName) - 3, "_PO" ) == 0 ) + pNetName[strlen(pNetName) - 3] = 0; + if ( strcmp( vTokens->pArray[3], "INPUT" ) == 0 ) + Io_ReadCreatePi( pNtk, vTokens->pArray[1] ); + else if ( strcmp( vTokens->pArray[3], "OUTPUT" ) == 0 ) + Io_ReadCreatePo( pNtk, vTokens->pArray[1] ); + else + { + printf( "%s (line %d): Wrong interface specification.\n", Extra_FileReaderGetFileName(p), iLine ); + Abc_NtkDelete( pNtk ); + return NULL; + } + vTokens = Extra_FileReaderGetTokens(p); + } + } + else if ( strcmp( vTokens->pArray[0], "design" ) == 0 ) + { + free( pNtk->pName ); + pNtk->pName = Extra_UtilStrsav( vTokens->pArray[3] ); + break; + } + } + Extra_ProgressBarStop( pProgress ); + + // assign logic functions + Abc_NtkForEachNode( pNtk, pObj, i ) + { + if ( strncmp( pObj->pData, "And", 3 ) == 0 ) + Abc_ObjSetData( pObj, Abc_SopCreateAnd(pNtk->pManFunc, Abc_ObjFaninNum(pObj), NULL) ); + else if ( strncmp( pObj->pData, "Or", 2 ) == 0 ) + Abc_ObjSetData( pObj, Abc_SopCreateOr(pNtk->pManFunc, Abc_ObjFaninNum(pObj), NULL) ); + else if ( strncmp( pObj->pData, "Nand", 4 ) == 0 ) + Abc_ObjSetData( pObj, Abc_SopCreateNand(pNtk->pManFunc, Abc_ObjFaninNum(pObj)) ); + else if ( strncmp( pObj->pData, "Nor", 3 ) == 0 ) + Abc_ObjSetData( pObj, Abc_SopCreateNor(pNtk->pManFunc, Abc_ObjFaninNum(pObj)) ); + else if ( strncmp( pObj->pData, "Exor", 4 ) == 0 ) + Abc_ObjSetData( pObj, Abc_SopCreateXor(pNtk->pManFunc, Abc_ObjFaninNum(pObj)) ); + else if ( strncmp( pObj->pData, "Exnor", 5 ) == 0 ) + Abc_ObjSetData( pObj, Abc_SopCreateNxor(pNtk->pManFunc, Abc_ObjFaninNum(pObj)) ); + else if ( strncmp( pObj->pData, "Inv", 3 ) == 0 ) + Abc_ObjSetData( pObj, Abc_SopCreateInv(pNtk->pManFunc) ); + else if ( strncmp( pObj->pData, "Buf", 3 ) == 0 ) + Abc_ObjSetData( pObj, Abc_SopCreateBuf(pNtk->pManFunc) ); + else + { + printf( "%s: Unknown gate type \"%s\".\n", Extra_FileReaderGetFileName(p), pObj->pData ); + Abc_NtkDelete( pNtk ); + return NULL; + } + } + // check if constants have been added +// if ( pNet = Abc_NtkFindNet( pNtk, "VDD" ) ) +// Io_ReadCreateConst( pNtk, "VDD", 1 ); +// if ( pNet = Abc_NtkFindNet( pNtk, "GND" ) ) +// Io_ReadCreateConst( pNtk, "GND", 0 ); + + Abc_NtkFinalizeRead( pNtk ); + return pNtk; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + + diff --git a/abc_with_bb_support/src/base/io/ioReadEqn.c b/abc_with_bb_support/src/base/io/ioReadEqn.c new file mode 100644 index 000000000..27f602472 --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioReadEqn.c @@ -0,0 +1,239 @@ +/**CFile**************************************************************** + + FileName [ioReadEqn.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures to read equation format files.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: ioReadEqn.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Abc_Ntk_t * Io_ReadEqnNetwork( Extra_FileReader_t * p ); +static void Io_ReadEqnStrCompact( char * pStr ); +static int Io_ReadEqnStrFind( Vec_Ptr_t * vTokens, char * pName ); +static void Io_ReadEqnStrCutAt( char * pStr, char * pStop, int fUniqueOnly, Vec_Ptr_t * vTokens ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Reads the network from a BENCH file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Io_ReadEqn( char * pFileName, int fCheck ) +{ + Extra_FileReader_t * p; + Abc_Ntk_t * pNtk; + + // start the file + p = Extra_FileReaderAlloc( pFileName, "#", ";", "=" ); + if ( p == NULL ) + return NULL; + + // read the network + pNtk = Io_ReadEqnNetwork( p ); + Extra_FileReaderFree( p ); + if ( pNtk == NULL ) + return NULL; + + // make sure that everything is okay with the network structure + if ( fCheck && !Abc_NtkCheckRead( pNtk ) ) + { + printf( "Io_ReadEqn: The network check has failed.\n" ); + Abc_NtkDelete( pNtk ); + return NULL; + } + return pNtk; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Io_ReadEqnNetwork( Extra_FileReader_t * p ) +{ + ProgressBar * pProgress; + Vec_Ptr_t * vTokens; + Vec_Ptr_t * vVars; + Abc_Ntk_t * pNtk; + Abc_Obj_t * pNode; + char * pNodeName, * pFormula, * pFormulaCopy, * pVarName; + int iLine, i; + + // allocate the empty network + pNtk = Abc_NtkAlloc( ABC_NTK_NETLIST, ABC_FUNC_AIG, 1 ); + // set the specs + pNtk->pName = Extra_FileNameGeneric(Extra_FileReaderGetFileName(p)); + pNtk->pSpec = Extra_UtilStrsav(Extra_FileReaderGetFileName(p)); + + // go through the lines of the file + vVars = Vec_PtrAlloc( 100 ); + pProgress = Extra_ProgressBarStart( stdout, Extra_FileReaderGetFileSize(p) ); + for ( iLine = 0; vTokens = Extra_FileReaderGetTokens(p); iLine++ ) + { + Extra_ProgressBarUpdate( pProgress, Extra_FileReaderGetCurPosition(p), NULL ); + + // check if the first token contains anything + Io_ReadEqnStrCompact( vTokens->pArray[0] ); + if ( strlen(vTokens->pArray[0]) == 0 ) + break; + + // if the number of tokens is different from two, error + if ( vTokens->nSize != 2 ) + { + printf( "%s: Wrong input file format.\n", Extra_FileReaderGetFileName(p) ); + Abc_NtkDelete( pNtk ); + return NULL; + } + + // get the type of the line + if ( strncmp( vTokens->pArray[0], "INORDER", 7 ) == 0 ) + { + Io_ReadEqnStrCutAt( vTokens->pArray[1], " \n\r\t", 0, vVars ); + Vec_PtrForEachEntry( vVars, pVarName, i ) + Io_ReadCreatePi( pNtk, pVarName ); + } + else if ( strncmp( vTokens->pArray[0], "OUTORDER", 8 ) == 0 ) + { + Io_ReadEqnStrCutAt( vTokens->pArray[1], " \n\r\t", 0, vVars ); + Vec_PtrForEachEntry( vVars, pVarName, i ) + Io_ReadCreatePo( pNtk, pVarName ); + } + else + { + extern Hop_Obj_t * Parse_FormulaParserEqn( FILE * pOutput, char * pFormInit, Vec_Ptr_t * vVarNames, Hop_Man_t * pMan ); + + // get hold of the node name and its formula + pNodeName = vTokens->pArray[0]; + pFormula = vTokens->pArray[1]; + // compact the formula + Io_ReadEqnStrCompact( pFormula ); + + // consider the case of the constant node + if ( pFormula[1] == 0 && (pFormula[0] == '0' || pFormula[0] == '1') ) + { + pFormulaCopy = NULL; + Vec_PtrClear( vVars ); + } + else + { + // make a copy of formula for names + pFormulaCopy = Extra_UtilStrsav( pFormula ); + // find the names of the fanins of this node + Io_ReadEqnStrCutAt( pFormulaCopy, "!*+()", 1, vVars ); + } + // create the node + pNode = Io_ReadCreateNode( pNtk, pNodeName, (char **)Vec_PtrArray(vVars), Vec_PtrSize(vVars) ); + // derive the function + pNode->pData = Parse_FormulaParserEqn( stdout, pFormula, vVars, pNtk->pManFunc ); + // remove the cubes + FREE( pFormulaCopy ); + } + } + Extra_ProgressBarStop( pProgress ); + Vec_PtrFree( vVars ); + Abc_NtkFinalizeRead( pNtk ); + return pNtk; +} + + + +/**Function************************************************************* + + Synopsis [Compacts the string by throwing away space-like chars.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_ReadEqnStrCompact( char * pStr ) +{ + char * pCur, * pNew; + for ( pNew = pCur = pStr; *pCur; pCur++ ) + if ( !(*pCur == ' ' || *pCur == '\n' || *pCur == '\r' || *pCur == '\t') ) + *pNew++ = *pCur; + *pNew = 0; +} + +/**Function************************************************************* + + Synopsis [Determines unique variables in the string.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_ReadEqnStrFind( Vec_Ptr_t * vTokens, char * pName ) +{ + char * pToken; + int i; + Vec_PtrForEachEntry( vTokens, pToken, i ) + if ( strcmp( pToken, pName ) == 0 ) + return i; + return -1; +} + +/**Function************************************************************* + + Synopsis [Cuts the string into pieces using stop chars.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_ReadEqnStrCutAt( char * pStr, char * pStop, int fUniqueOnly, Vec_Ptr_t * vTokens ) +{ + char * pToken; + Vec_PtrClear( vTokens ); + for ( pToken = strtok( pStr, pStop ); pToken; pToken = strtok( NULL, pStop ) ) + if ( !fUniqueOnly || Io_ReadEqnStrFind( vTokens, pToken ) == -1 ) + Vec_PtrPush( vTokens, pToken ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + + diff --git a/abc_with_bb_support/src/base/io/ioReadPla.c b/abc_with_bb_support/src/base/io/ioReadPla.c new file mode 100644 index 000000000..8558d0c1e --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioReadPla.c @@ -0,0 +1,250 @@ +/**CFile**************************************************************** + + FileName [ioReadPla.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedure to read network from file.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: ioReadPla.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Abc_Ntk_t * Io_ReadPlaNetwork( Extra_FileReader_t * p ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Reads the network from a PLA file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Io_ReadPla( char * pFileName, int fCheck ) +{ + Extra_FileReader_t * p; + Abc_Ntk_t * pNtk; + + // start the file + p = Extra_FileReaderAlloc( pFileName, "#", "\n\r", " \t|" ); + if ( p == NULL ) + return NULL; + + // read the network + pNtk = Io_ReadPlaNetwork( p ); + Extra_FileReaderFree( p ); + if ( pNtk == NULL ) + return NULL; + + // make sure that everything is okay with the network structure + if ( fCheck && !Abc_NtkCheckRead( pNtk ) ) + { + printf( "Io_ReadPla: The network check has failed.\n" ); + Abc_NtkDelete( pNtk ); + return NULL; + } + return pNtk; +} +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Io_ReadPlaNetwork( Extra_FileReader_t * p ) +{ + ProgressBar * pProgress; + Vec_Ptr_t * vTokens; + Abc_Ntk_t * pNtk; + Abc_Obj_t * pTermPi, * pTermPo, * pNode; + Vec_Str_t ** ppSops; + char Buffer[100]; + int nInputs = -1, nOutputs = -1, nProducts = -1; + char * pCubeIn, * pCubeOut; + int i, k, iLine, nDigits, nCubes; + + // allocate the empty network + pNtk = Abc_NtkStartRead( Extra_FileReaderGetFileName(p) ); + + // go through the lines of the file + nCubes = 0; + pProgress = Extra_ProgressBarStart( stdout, Extra_FileReaderGetFileSize(p) ); + for ( iLine = 0; vTokens = Extra_FileReaderGetTokens(p); iLine++ ) + { + Extra_ProgressBarUpdate( pProgress, Extra_FileReaderGetCurPosition(p), NULL ); + + // if it is the end of file, quit the loop + if ( strcmp( vTokens->pArray[0], ".e" ) == 0 ) + break; + + if ( vTokens->nSize == 1 ) + { + printf( "%s (line %d): Wrong number of token.\n", + Extra_FileReaderGetFileName(p), iLine+1 ); + Abc_NtkDelete( pNtk ); + return NULL; + } + + if ( strcmp( vTokens->pArray[0], ".i" ) == 0 ) + nInputs = atoi(vTokens->pArray[1]); + else if ( strcmp( vTokens->pArray[0], ".o" ) == 0 ) + nOutputs = atoi(vTokens->pArray[1]); + else if ( strcmp( vTokens->pArray[0], ".p" ) == 0 ) + nProducts = atoi(vTokens->pArray[1]); + else if ( strcmp( vTokens->pArray[0], ".ilb" ) == 0 ) + { + if ( vTokens->nSize - 1 != nInputs ) + printf( "Warning: Mismatch between the number of PIs on the .i line (%d) and the number of PIs on the .ilb line (%d).\n", nInputs, vTokens->nSize - 1 ); + for ( i = 1; i < vTokens->nSize; i++ ) + Io_ReadCreatePi( pNtk, vTokens->pArray[i] ); + } + else if ( strcmp( vTokens->pArray[0], ".ob" ) == 0 ) + { + if ( vTokens->nSize - 1 != nOutputs ) + printf( "Warning: Mismatch between the number of POs on the .o line (%d) and the number of POs on the .ob line (%d).\n", nOutputs, vTokens->nSize - 1 ); + for ( i = 1; i < vTokens->nSize; i++ ) + Io_ReadCreatePo( pNtk, vTokens->pArray[i] ); + } + else + { + // check if the input/output names are given + if ( Abc_NtkPiNum(pNtk) == 0 ) + { + if ( nInputs == -1 ) + { + printf( "%s: The number of inputs is not specified.\n", Extra_FileReaderGetFileName(p) ); + Abc_NtkDelete( pNtk ); + return NULL; + } + nDigits = Extra_Base10Log( nInputs ); + for ( i = 0; i < nInputs; i++ ) + { + sprintf( Buffer, "x%0*d", nDigits, i ); + Io_ReadCreatePi( pNtk, Buffer ); + } + } + if ( Abc_NtkPoNum(pNtk) == 0 ) + { + if ( nOutputs == -1 ) + { + printf( "%s: The number of outputs is not specified.\n", Extra_FileReaderGetFileName(p) ); + Abc_NtkDelete( pNtk ); + return NULL; + } + nDigits = Extra_Base10Log( nOutputs ); + for ( i = 0; i < nOutputs; i++ ) + { + sprintf( Buffer, "z%0*d", nDigits, i ); + Io_ReadCreatePo( pNtk, Buffer ); + } + } + if ( Abc_NtkNodeNum(pNtk) == 0 ) + { // first time here + // create the PO drivers and add them + // start the SOP covers + ppSops = ALLOC( Vec_Str_t *, nOutputs ); + Abc_NtkForEachPo( pNtk, pTermPo, i ) + { + ppSops[i] = Vec_StrAlloc( 100 ); + // create the node + pNode = Abc_NtkCreateNode(pNtk); + // connect the node to the PO net + Abc_ObjAddFanin( Abc_ObjFanin0Ntk(pTermPo), pNode ); + // connect the node to the PI nets + Abc_NtkForEachPi( pNtk, pTermPi, k ) + Abc_ObjAddFanin( pNode, Abc_ObjFanout0Ntk(pTermPi) ); + } + } + // read the cubes + if ( vTokens->nSize != 2 ) + { + printf( "%s (line %d): Input and output cubes are not specified.\n", + Extra_FileReaderGetFileName(p), iLine+1 ); + Abc_NtkDelete( pNtk ); + return NULL; + } + pCubeIn = vTokens->pArray[0]; + pCubeOut = vTokens->pArray[1]; + if ( strlen(pCubeIn) != (unsigned)nInputs ) + { + printf( "%s (line %d): Input cube length (%d) differs from the number of inputs (%d).\n", + Extra_FileReaderGetFileName(p), iLine+1, strlen(pCubeIn), nInputs ); + Abc_NtkDelete( pNtk ); + return NULL; + } + if ( strlen(pCubeOut) != (unsigned)nOutputs ) + { + printf( "%s (line %d): Output cube length (%d) differs from the number of outputs (%d).\n", + Extra_FileReaderGetFileName(p), iLine+1, strlen(pCubeOut), nOutputs ); + Abc_NtkDelete( pNtk ); + return NULL; + } + for ( i = 0; i < nOutputs; i++ ) + { + if ( pCubeOut[i] == '1' ) + { + Vec_StrAppend( ppSops[i], pCubeIn ); + Vec_StrAppend( ppSops[i], " 1\n" ); + } + } + nCubes++; + } + } + Extra_ProgressBarStop( pProgress ); + if ( nProducts != -1 && nCubes != nProducts ) + printf( "Warning: Mismatch between the number of cubes (%d) and the number on .p line (%d).\n", + nCubes, nProducts ); + + // add the SOP covers + Abc_NtkForEachPo( pNtk, pTermPo, i ) + { + pNode = Abc_ObjFanin0Ntk( Abc_ObjFanin0(pTermPo) ); + if ( ppSops[i]->nSize == 0 ) + { + Abc_ObjRemoveFanins(pNode); + pNode->pData = Abc_SopRegister( pNtk->pManFunc, " 0\n" ); + Vec_StrFree( ppSops[i] ); + continue; + } + Vec_StrPush( ppSops[i], 0 ); + pNode->pData = Abc_SopRegister( pNtk->pManFunc, ppSops[i]->pArray ); + Vec_StrFree( ppSops[i] ); + } + free( ppSops ); + Abc_NtkFinalizeRead( pNtk ); + return pNtk; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + + diff --git a/abc_with_bb_support/src/base/io/ioReadVerilog.c b/abc_with_bb_support/src/base/io/ioReadVerilog.c new file mode 100644 index 000000000..5b6c3baa9 --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioReadVerilog.c @@ -0,0 +1,90 @@ +/**CFile**************************************************************** + + FileName [ioReadVerilog.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedure to read network from file.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: ioReadVerilog.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +extern Abc_Lib_t * Ver_ParseFile( char * pFileName, Abc_Lib_t * pGateLib, int fCheck, int fUseMemMan ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Reads hierarchical design from the Verilog file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Io_ReadVerilog( char * pFileName, int fCheck ) +{ + Abc_Ntk_t * pNtk; + Abc_Lib_t * pDesign; + int RetValue; + + // parse the verilog file + pDesign = Ver_ParseFile( pFileName, NULL, fCheck, 1 ); + if ( pDesign == NULL ) + return NULL; + + // detect top-level model + RetValue = Abc_LibFindTopLevelModels( pDesign ); + pNtk = Vec_PtrEntry( pDesign->vTops, 0 ); + if ( RetValue > 1 ) + printf( "Warning: The design has %d root-level modules. The first one (%s) will be used.\n", + Vec_PtrSize(pDesign->vTops), pNtk->pName ); + + // extract the master network + pNtk->pDesign = pDesign; + pDesign->pManFunc = NULL; + + // verify the design for cyclic dependence + assert( Vec_PtrSize(pDesign->vModules) > 0 ); + if ( Vec_PtrSize(pDesign->vModules) == 1 ) + { +// printf( "Warning: The design is not hierarchical.\n" ); + Abc_LibFree( pDesign, pNtk ); + pNtk->pDesign = NULL; + pNtk->pSpec = Extra_UtilStrsav( pFileName ); + } + else + { + // check that there is no cyclic dependency + Abc_NtkIsAcyclicHierarchy( pNtk ); + } + +//Io_WriteVerilog( pNtk, "_temp.v" ); + return pNtk; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + + diff --git a/abc_with_bb_support/src/base/io/ioUtil.c b/abc_with_bb_support/src/base/io/ioUtil.c new file mode 100644 index 000000000..cacef70d1 --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioUtil.c @@ -0,0 +1,752 @@ +/**CFile**************************************************************** + + FileName [ioUtil.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures to write the network in BENCH format.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: ioUtil.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Returns the file type.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Io_FileType_t Io_ReadFileType( char * pFileName ) +{ + char * pExt; + if ( pFileName == NULL ) + return IO_FILE_NONE; + pExt = Extra_FileNameExtension( pFileName ); + if ( pExt == NULL ) + return IO_FILE_NONE; + if ( !strcmp( pExt, "aig" ) ) + return IO_FILE_AIGER; + if ( !strcmp( pExt, "baf" ) ) + return IO_FILE_BAF; + if ( !strcmp( pExt, "blif" ) ) + return IO_FILE_BLIF; + if ( !strcmp( pExt, "bench" ) ) + return IO_FILE_BENCH; + if ( !strcmp( pExt, "cnf" ) ) + return IO_FILE_CNF; + if ( !strcmp( pExt, "dot" ) ) + return IO_FILE_DOT; + if ( !strcmp( pExt, "edif" ) ) + return IO_FILE_EDIF; + if ( !strcmp( pExt, "eqn" ) ) + return IO_FILE_EQN; + if ( !strcmp( pExt, "gml" ) ) + return IO_FILE_GML; + if ( !strcmp( pExt, "list" ) ) + return IO_FILE_LIST; + if ( !strcmp( pExt, "mv" ) ) + return IO_FILE_BLIFMV; + if ( !strcmp( pExt, "pla" ) ) + return IO_FILE_PLA; + if ( !strcmp( pExt, "v" ) ) + return IO_FILE_VERILOG; + return IO_FILE_UNKNOWN; +} + +/**Function************************************************************* + + Synopsis [Read the network from a file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Io_ReadNetlist( char * pFileName, Io_FileType_t FileType, int fCheck ) +{ + FILE * pFile; + Abc_Ntk_t * pNtk; + if ( FileType == IO_FILE_NONE || FileType == IO_FILE_UNKNOWN ) + { + fprintf( stdout, "The generic file reader requires a known file extension.\n" ); + return NULL; + } + // check if the file exists + pFile = fopen( pFileName, "r" ); + if ( pFile == NULL ) + { + fprintf( stdout, "Cannot open input file \"%s\". ", pFileName ); + if ( pFileName = Extra_FileGetSimilarName( pFileName, ".blif", ".bench", ".pla", ".baf", ".aig" ) ) + fprintf( stdout, "Did you mean \"%s\"?", pFileName ); + fprintf( stdout, "\n" ); + return NULL; + } + fclose( pFile ); + // read the AIG + if ( FileType == IO_FILE_AIGER || FileType == IO_FILE_BAF ) + { + if ( FileType == IO_FILE_AIGER ) + pNtk = Io_ReadAiger( pFileName, fCheck ); + else // if ( FileType == IO_FILE_BAF ) + pNtk = Io_ReadBaf( pFileName, fCheck ); + if ( pNtk == NULL ) + { + fprintf( stdout, "Reading AIG from file has failed.\n" ); + return NULL; + } + return pNtk; + } + // read the new netlist + if ( FileType == IO_FILE_BLIF ) +// pNtk = Io_ReadBlif( pFileName, fCheck ); + pNtk = Io_ReadBlifMv( pFileName, 0, fCheck ); + else if ( Io_ReadFileType(pFileName) == IO_FILE_BLIFMV ) + pNtk = Io_ReadBlifMv( pFileName, 1, fCheck ); + else if ( FileType == IO_FILE_BENCH ) + pNtk = Io_ReadBench( pFileName, fCheck ); + else if ( FileType == IO_FILE_EDIF ) + pNtk = Io_ReadEdif( pFileName, fCheck ); + else if ( FileType == IO_FILE_EQN ) + pNtk = Io_ReadEqn( pFileName, fCheck ); + else if ( FileType == IO_FILE_PLA ) + pNtk = Io_ReadPla( pFileName, fCheck ); + else if ( FileType == IO_FILE_VERILOG ) + pNtk = Io_ReadVerilog( pFileName, fCheck ); + else + { + fprintf( stderr, "Unknown file format.\n" ); + return NULL; + } + if ( pNtk == NULL ) + { + fprintf( stdout, "Reading network from file has failed.\n" ); + return NULL; + } + if ( Abc_NtkBlackboxNum(pNtk) || Abc_NtkWhiteboxNum(pNtk) ) + fprintf( stdout, "Warning: The network contains hierarchy.\n" ); + return pNtk; +} + + +/**Function************************************************************* + + Synopsis [Read the network from a file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Io_Read( char * pFileName, Io_FileType_t FileType, int fCheck ) +{ + Abc_Ntk_t * pNtk, * pTemp; + // get the netlist + pNtk = Io_ReadNetlist( pFileName, FileType, fCheck ); + if ( pNtk == NULL ) + return NULL; + if ( !Abc_NtkIsNetlist(pNtk) ) + return pNtk; + // flatten logic hierarchy + assert( Abc_NtkIsNetlist(pNtk) ); + if ( Abc_NtkWhiteboxNum(pNtk) > 0 ) + { + pNtk = Abc_NtkFlattenLogicHierarchy( pTemp = pNtk ); + Abc_NtkDelete( pTemp ); + if ( pNtk == NULL ) + { + fprintf( stdout, "Flattening logic hierarchy has failed.\n" ); + return NULL; + } + } + // convert blackboxes + if ( Abc_NtkBlackboxNum(pNtk) > 0 ) + { + printf( "Hierarchy reader converted %d instances of blackboxes.\n", Abc_NtkBlackboxNum(pNtk) ); + pNtk = Abc_NtkConvertBlackboxes( pTemp = pNtk ); + Abc_NtkDelete( pTemp ); + if ( pNtk == NULL ) + { + fprintf( stdout, "Converting blackboxes has failed.\n" ); + return NULL; + } + } + // consider the case of BLIF-MV + if ( Io_ReadFileType(pFileName) == IO_FILE_BLIFMV ) + { +//Abc_NtkPrintStats( stdout, pNtk, 0 ); +// Io_WriteBlifMv( pNtk, "_temp_.mv" ); + pNtk = Abc_NtkStrashBlifMv( pTemp = pNtk ); + Abc_NtkDelete( pTemp ); + if ( pNtk == NULL ) + { + fprintf( stdout, "Converting BLIF-MV to AIG has failed.\n" ); + return NULL; + } + return pNtk; + } + // convert the netlist into the logic network + pNtk = Abc_NtkToLogic( pTemp = pNtk ); + Abc_NtkDelete( pTemp ); + if ( pNtk == NULL ) + { + fprintf( stdout, "Converting netlist to logic network after reading has failed.\n" ); + return NULL; + } + return pNtk; +} + +/**Function************************************************************* + + Synopsis [Write the network into file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_Write( Abc_Ntk_t * pNtk, char * pFileName, Io_FileType_t FileType ) +{ + Abc_Ntk_t * pNtkTemp, * pNtkCopy; + // check if the current network is available + if ( pNtk == NULL ) + { + fprintf( stdout, "Empty network.\n" ); + return; + } + // check if the file extension if given + if ( FileType == IO_FILE_NONE || FileType == IO_FILE_UNKNOWN ) + { + fprintf( stdout, "The generic file writer requires a known file extension.\n" ); + return; + } + // write the AIG formats + if ( FileType == IO_FILE_AIGER || FileType == IO_FILE_BAF ) + { + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( stdout, "Writing this format is only possible for structurally hashed AIGs.\n" ); + return; + } + if ( FileType == IO_FILE_AIGER ) + Io_WriteAiger( pNtk, pFileName, 1 ); + else // if ( FileType == IO_FILE_BAF ) + Io_WriteBaf( pNtk, pFileName ); + return; + } + // write non-netlist types + if ( FileType == IO_FILE_CNF ) + { + Io_WriteCnf( pNtk, pFileName, 0 ); + return; + } + if ( FileType == IO_FILE_DOT ) + { + Io_WriteDot( pNtk, pFileName ); + return; + } + if ( FileType == IO_FILE_GML ) + { + Io_WriteGml( pNtk, pFileName ); + return; + } +/* + if ( FileType == IO_FILE_BLIFMV ) + { + Io_WriteBlifMv( pNtk, pFileName ); + return; + } +*/ + // convert logic network into netlist + if ( FileType == IO_FILE_PLA ) + { + if ( Abc_NtkLevel(pNtk) > 1 ) + { + fprintf( stdout, "PLA writing is available for collapsed networks.\n" ); + return; + } + if ( Abc_NtkIsComb(pNtk) ) + pNtkTemp = Abc_NtkToNetlist( pNtk ); + else + { + fprintf( stdout, "Latches are writen into the PLA file at PI/PO pairs.\n" ); + pNtkCopy = Abc_NtkDup( pNtk ); + Abc_NtkMakeComb( pNtkCopy ); + pNtkTemp = Abc_NtkToNetlist( pNtk ); + Abc_NtkDelete( pNtkCopy ); + } + if ( !Abc_NtkToSop( pNtk, 1 ) ) + return; + } + else if ( FileType == IO_FILE_BENCH ) + { + if ( !Abc_NtkIsStrash(pNtk) ) + { + fprintf( stdout, "Writing traditional BENCH is available for AIGs only (use \"write_bench\").\n" ); + return; + } + pNtkTemp = Abc_NtkToNetlistBench( pNtk ); + } + else + pNtkTemp = Abc_NtkToNetlist( pNtk ); + + if ( pNtkTemp == NULL ) + { + fprintf( stdout, "Converting to netlist has failed.\n" ); + return; + } + + if ( FileType == IO_FILE_BLIF ) + { + if ( !Abc_NtkHasSop(pNtkTemp) && !Abc_NtkHasMapping(pNtkTemp) ) + Abc_NtkToSop( pNtkTemp, 0 ); + Io_WriteBlif( pNtkTemp, pFileName, 1 ); + } + else if ( FileType == IO_FILE_BLIFMV ) + { + if ( !Abc_NtkConvertToBlifMv( pNtkTemp ) ) + return; + Io_WriteBlifMv( pNtkTemp, pFileName ); + } + else if ( FileType == IO_FILE_BENCH ) + Io_WriteBench( pNtkTemp, pFileName ); + else if ( FileType == IO_FILE_PLA ) + Io_WritePla( pNtkTemp, pFileName ); + else if ( FileType == IO_FILE_EQN ) + { + if ( !Abc_NtkHasAig(pNtkTemp) ) + Abc_NtkToAig( pNtkTemp ); + Io_WriteEqn( pNtkTemp, pFileName ); + } + else if ( FileType == IO_FILE_VERILOG ) + { + if ( !Abc_NtkHasAig(pNtkTemp) && !Abc_NtkHasMapping(pNtkTemp) ) + Abc_NtkToAig( pNtkTemp ); + Io_WriteVerilog( pNtkTemp, pFileName ); + } + else + fprintf( stderr, "Unknown file format.\n" ); + Abc_NtkDelete( pNtkTemp ); +} + +/**Function************************************************************* + + Synopsis [Write the network into file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteHie( Abc_Ntk_t * pNtk, char * pBaseName, char * pFileName ) +{ + Abc_Ntk_t * pNtkTemp, * pNtkResult, * pNtkBase = NULL; + // check if the current network is available + if ( pNtk == NULL ) + { + fprintf( stdout, "Empty network.\n" ); + return; + } + + // read the base network + assert( Abc_NtkIsStrash(pNtk) || Abc_NtkIsLogic(pNtk) ); + if ( Io_ReadFileType(pBaseName) == IO_FILE_BLIF ) + pNtkBase = Io_ReadBlifMv( pBaseName, 0, 1 ); + else if ( Io_ReadFileType(pBaseName) == IO_FILE_BLIFMV ) + pNtkBase = Io_ReadBlifMv( pBaseName, 1, 1 ); + else if ( Io_ReadFileType(pBaseName) == IO_FILE_VERILOG ) + pNtkBase = Io_ReadVerilog( pBaseName, 1 ); + else + fprintf( stderr, "Unknown input file format.\n" ); + if ( pNtkBase == NULL ) + return; + + // flatten logic hierarchy if present + if ( Abc_NtkWhiteboxNum(pNtkBase) > 0 ) + { + pNtkBase = Abc_NtkFlattenLogicHierarchy( pNtkTemp = pNtkBase ); + if ( pNtkBase == NULL ) + return; + Abc_NtkDelete( pNtkTemp ); + } + + // reintroduce the boxes into the netlist + if ( Io_ReadFileType(pBaseName) == IO_FILE_BLIFMV ) + { + if ( Abc_NtkBlackboxNum(pNtkBase) > 0 ) + { + printf( "Hierarchy writer does not support BLIF-MV with blackboxes.\n" ); + Abc_NtkDelete( pNtkBase ); + return; + } + // convert the current network to BLIF-MV + assert( !Abc_NtkIsNetlist(pNtk) ); + pNtkResult = Abc_NtkToNetlist( pNtk ); + if ( !Abc_NtkConvertToBlifMv( pNtkResult ) ) + return; + // reintroduce the network + pNtkResult = Abc_NtkInsertBlifMv( pNtkBase, pNtkTemp = pNtkResult ); + Abc_NtkDelete( pNtkTemp ); + } + else if ( Abc_NtkBlackboxNum(pNtkBase) > 0 ) + { + // derive the netlist + pNtkResult = Abc_NtkToNetlist( pNtk ); + pNtkResult = Abc_NtkInsertNewLogic( pNtkBase, pNtkTemp = pNtkResult ); + Abc_NtkDelete( pNtkTemp ); + if ( pNtkResult ) + printf( "Hierarchy writer reintroduced %d instances of blackboxes.\n", Abc_NtkBlackboxNum(pNtkBase) ); + } + else + { + printf( "Warning: The output network does not contain blackboxes.\n" ); + pNtkResult = Abc_NtkToNetlist( pNtk ); + } + Abc_NtkDelete( pNtkBase ); + if ( pNtkResult == NULL ) + return; + + // write the resulting network + if ( Io_ReadFileType(pFileName) == IO_FILE_BLIF ) + { + if ( !Abc_NtkHasSop(pNtkResult) && !Abc_NtkHasMapping(pNtkResult) ) + Abc_NtkToSop( pNtkResult, 0 ); + Io_WriteBlif( pNtkResult, pFileName, 1 ); + } + else if ( Io_ReadFileType(pFileName) == IO_FILE_VERILOG ) + { + if ( !Abc_NtkHasAig(pNtkResult) && !Abc_NtkHasMapping(pNtkResult) ) + Abc_NtkToAig( pNtkResult ); + Io_WriteVerilog( pNtkResult, pFileName ); + } + else if ( Io_ReadFileType(pFileName) == IO_FILE_BLIFMV ) + { + Io_WriteBlifMv( pNtkResult, pFileName ); + } + else + fprintf( stderr, "Unknown output file format.\n" ); + + Abc_NtkDelete( pNtkResult ); +} + +/**Function************************************************************* + + Synopsis [Creates PI terminal and net.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Io_ReadCreatePi( Abc_Ntk_t * pNtk, char * pName ) +{ + Abc_Obj_t * pNet, * pTerm; + // get the PI net + pNet = Abc_NtkFindNet( pNtk, pName ); + if ( pNet ) + printf( "Warning: PI \"%s\" appears twice in the list.\n", pName ); + pNet = Abc_NtkFindOrCreateNet( pNtk, pName ); + // add the PI node + pTerm = Abc_NtkCreatePi( pNtk ); + Abc_ObjAddFanin( pNet, pTerm ); + return pTerm; +} + +/**Function************************************************************* + + Synopsis [Creates PO terminal and net.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Io_ReadCreatePo( Abc_Ntk_t * pNtk, char * pName ) +{ + Abc_Obj_t * pNet, * pTerm; + // get the PO net + pNet = Abc_NtkFindNet( pNtk, pName ); + if ( pNet && Abc_ObjFaninNum(pNet) == 0 ) + printf( "Warning: PO \"%s\" appears twice in the list.\n", pName ); + pNet = Abc_NtkFindOrCreateNet( pNtk, pName ); + // add the PO node + pTerm = Abc_NtkCreatePo( pNtk ); + Abc_ObjAddFanin( pTerm, pNet ); + return pTerm; +} + +/**Function************************************************************* + + Synopsis [Creates PO terminal and net.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Io_ReadCreateAssert( Abc_Ntk_t * pNtk, char * pName ) +{ + Abc_Obj_t * pNet, * pTerm; + // get the PO net + pNet = Abc_NtkFindNet( pNtk, pName ); + if ( pNet && Abc_ObjFaninNum(pNet) == 0 ) + printf( "Warning: Assert \"%s\" appears twice in the list.\n", pName ); + pNet = Abc_NtkFindOrCreateNet( pNtk, pName ); + // add the PO node + pTerm = Abc_NtkCreateAssert( pNtk ); + Abc_ObjAddFanin( pTerm, pNet ); + return pTerm; +} + +/**Function************************************************************* + + Synopsis [Create a latch with the given input/output.] + + Description [By default, the latch value is unknown (ABC_INIT_NONE).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Io_ReadCreateLatch( Abc_Ntk_t * pNtk, char * pNetLI, char * pNetLO ) +{ + Abc_Obj_t * pLatch, * pTerm, * pNet; + // get the LI net + pNet = Abc_NtkFindOrCreateNet( pNtk, pNetLI ); + // add the BO terminal + pTerm = Abc_NtkCreateBi( pNtk ); + Abc_ObjAddFanin( pTerm, pNet ); + // add the latch box + pLatch = Abc_NtkCreateLatch( pNtk ); + Abc_ObjAddFanin( pLatch, pTerm ); + // add the BI terminal + pTerm = Abc_NtkCreateBo( pNtk ); + Abc_ObjAddFanin( pTerm, pLatch ); + // get the LO net + pNet = Abc_NtkFindOrCreateNet( pNtk, pNetLO ); + Abc_ObjAddFanin( pNet, pTerm ); + // set latch name + Abc_ObjAssignName( pLatch, pNetLO, "L" ); + return pLatch; +} + +/**Function************************************************************* + + Synopsis [Create the reset latch with data=1 and init=0.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Io_ReadCreateResetLatch( Abc_Ntk_t * pNtk, int fBlifMv ) +{ + Abc_Obj_t * pLatch, * pNode; + Abc_Obj_t * pNetLI, * pNetLO; + // create latch with 0 init value +// pLatch = Io_ReadCreateLatch( pNtk, "_resetLI_", "_resetLO_" ); + pNetLI = Abc_NtkCreateNet( pNtk ); + pNetLO = Abc_NtkCreateNet( pNtk ); + Abc_ObjAssignName( pNetLI, Abc_ObjName(pNetLI), NULL ); + Abc_ObjAssignName( pNetLO, Abc_ObjName(pNetLO), NULL ); + pLatch = Io_ReadCreateLatch( pNtk, Abc_ObjName(pNetLI), Abc_ObjName(pNetLO) ); + // set the initial value + Abc_LatchSetInit0( pLatch ); + // feed the latch with constant1- node +// pNode = Abc_NtkCreateNode( pNtk ); +// pNode->pData = Abc_SopRegister( pNtk->pManFunc, "2\n1\n" ); + pNode = Abc_NtkCreateNodeConst1( pNtk ); + Abc_ObjAddFanin( Abc_ObjFanin0(Abc_ObjFanin0(pLatch)), pNode ); + return pLatch; +} + +/**Function************************************************************* + + Synopsis [Create node and the net driven by it.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Io_ReadCreateNode( Abc_Ntk_t * pNtk, char * pNameOut, char * pNamesIn[], int nInputs ) +{ + Abc_Obj_t * pNet, * pNode; + int i; + // create a new node + pNode = Abc_NtkCreateNode( pNtk ); + // add the fanin nets + for ( i = 0; i < nInputs; i++ ) + { + pNet = Abc_NtkFindOrCreateNet( pNtk, pNamesIn[i] ); + Abc_ObjAddFanin( pNode, pNet ); + } + // add the fanout net + pNet = Abc_NtkFindOrCreateNet( pNtk, pNameOut ); + Abc_ObjAddFanin( pNet, pNode ); + return pNode; +} + +/**Function************************************************************* + + Synopsis [Create a constant 0 node driving the net with this name.] + + Description [Assumes that the net already exists.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Io_ReadCreateConst( Abc_Ntk_t * pNtk, char * pName, bool fConst1 ) +{ + Abc_Obj_t * pNet, * pTerm; + pTerm = fConst1? Abc_NtkCreateNodeConst1(pNtk) : Abc_NtkCreateNodeConst0(pNtk); + pNet = Abc_NtkFindNet(pNtk, pName); assert( pNet ); + Abc_ObjAddFanin( pNet, pTerm ); + return pTerm; +} + +/**Function************************************************************* + + Synopsis [Create an inverter or buffer for the given net.] + + Description [Assumes that the nets already exist.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Io_ReadCreateInv( Abc_Ntk_t * pNtk, char * pNameIn, char * pNameOut ) +{ + Abc_Obj_t * pNet, * pNode; + pNet = Abc_NtkFindNet(pNtk, pNameIn); assert( pNet ); + pNode = Abc_NtkCreateNodeInv(pNtk, pNet); + pNet = Abc_NtkFindNet(pNtk, pNameOut); assert( pNet ); + Abc_ObjAddFanin( pNet, pNode ); + return pNode; +} + +/**Function************************************************************* + + Synopsis [Create an inverter or buffer for the given net.] + + Description [Assumes that the nets already exist.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Io_ReadCreateBuf( Abc_Ntk_t * pNtk, char * pNameIn, char * pNameOut ) +{ + Abc_Obj_t * pNet, * pNode; + pNet = Abc_NtkFindNet(pNtk, pNameIn); assert( pNet ); + pNode = Abc_NtkCreateNodeBuf(pNtk, pNet); + pNet = Abc_NtkFindNet(pNtk, pNameOut); assert( pNet ); + Abc_ObjAddFanin( pNet, pNode ); + return pNet; +} + + +/**Function************************************************************* + + Synopsis [Provide an fopen replacement with path lookup] + + Description [Provide an fopen replacement where the path stored + in pathvar MVSIS variable is used to look up the path + for name. Returns NULL if file cannot be opened.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +FILE * Io_FileOpen( const char * FileName, const char * PathVar, const char * Mode, int fVerbose ) +{ + char * t = 0, * c = 0, * i; + extern char * Abc_FrameReadFlag( char * pFlag ); + + if ( PathVar == 0 ) + { + return fopen( FileName, Mode ); + } + else + { + if ( c = Abc_FrameReadFlag( (char*)PathVar ) ) + { + char ActualFileName[4096]; + FILE * fp = 0; + t = Extra_UtilStrsav( c ); + for (i = strtok( t, ":" ); i != 0; i = strtok( 0, ":") ) + { +#ifdef WIN32 + _snprintf ( ActualFileName, 4096, "%s/%s", i, FileName ); +#else + snprintf ( ActualFileName, 4096, "%s/%s", i, FileName ); +#endif + if ( ( fp = fopen ( ActualFileName, Mode ) ) ) + { + if ( fVerbose ) + fprintf ( stdout, "Using file %s\n", ActualFileName ); + free( t ); + return fp; + } + } + free( t ); + return 0; + } + else + { + return fopen( FileName, Mode ); + } + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/io/ioWriteAiger.c b/abc_with_bb_support/src/base/io/ioWriteAiger.c new file mode 100644 index 000000000..646319ef3 --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioWriteAiger.c @@ -0,0 +1,284 @@ +/**CFile**************************************************************** + + FileName [ioWriteAiger.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures to write binary AIGER format developed by + Armin Biere, Johannes Kepler University (http://fmv.jku.at/)] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - December 16, 2006.] + + Revision [$Id: ioWriteAiger.c,v 1.00 2006/12/16 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/* + The following is taken from the AIGER format description, + which can be found at http://fmv.jku.at/aiger +*/ + + +/* + The AIGER And-Inverter Graph (AIG) Format Version 20061129 + ---------------------------------------------------------- + Armin Biere, Johannes Kepler University, 2006 + + This report describes the AIG file format as used by the AIGER library. + The purpose of this report is not only to motivate and document the + format, but also to allow independent implementations of writers and + readers by giving precise and unambiguous definitions. + + ... + +Introduction + + The name AIGER contains as one part the acronym AIG of And-Inverter + Graphs and also if pronounced in German sounds like the name of the + 'Eiger', a mountain in the Swiss alps. This choice should emphasize the + origin of this format. It was first openly discussed at the Alpine + Verification Meeting 2006 in Ascona as a way to provide a simple, compact + file format for a model checking competition affiliated to CAV 2007. + + ... + +Binary Format Definition + + The binary format is semantically a subset of the ASCII format with a + slightly different syntax. The binary format may need to reencode + literals, but translating a file in binary format into ASCII format and + then back in to binary format will result in the same file. + + The main differences of the binary format to the ASCII format are as + follows. After the header the list of input literals and all the + current state literals of a latch can be omitted. Furthermore the + definitions of the AND gates are binary encoded. However, the symbol + table and the comment section are as in the ASCII format. + + The header of an AIGER file in binary format has 'aig' as format + identifier, but otherwise is identical to the ASCII header. The standard + file extension for the binary format is therefore '.aig'. + + A header for the binary format is still in ASCII encoding: + + aig M I L O A + + Constants, variables and literals are handled in the same way as in the + ASCII format. The first simplifying restriction is on the variable + indices of inputs and latches. The variable indices of inputs come first, + followed by the pseudo-primary inputs of the latches and then the variable + indices of all LHS of AND gates: + + input variable indices 1, 2, ... , I + latch variable indices I+1, I+2, ... , (I+L) + AND variable indices I+L+1, I+L+2, ... , (I+L+A) == M + + The corresponding unsigned literals are + + input literals 2, 4, ... , 2*I + latch literals 2*I+2, 2*I+4, ... , 2*(I+L) + AND literals 2*(I+L)+2, 2*(I+L)+4, ... , 2*(I+L+A) == 2*M + + All literals have to be defined, and therefore 'M = I + L + A'. With this + restriction it becomes possible that the inputs and the current state + literals of the latches do not have to be listed explicitly. Therefore, + after the header only the list of 'L' next state literals follows, one per + latch on a single line, and then the 'O' outputs, again one per line. + + In the binary format we assume that the AND gates are ordered and respect + the child parent relation. AND gates with smaller literals on the LHS + come first. Therefore we can assume that the literals on the right-hand + side of a definition of an AND gate are smaller than the LHS literal. + Furthermore we can sort the literals on the RHS, such that the larger + literal comes first. A definition thus consists of three literals + + lhs rhs0 rhs1 + + with 'lhs' even and 'lhs > rhs0 >= rhs1'. Also the variable indices are + pairwise different to avoid combinational self loops. Since the LHS + indices of the definitions are all consecutive (as even integers), + the binary format does not have to keep 'lhs'. In addition, we can use + the order restriction and only write the differences 'delta0' and 'delta1' + instead of 'rhs0' and 'rhs1', with + + delta0 = lhs - rhs0, delta1 = rhs0 - rhs1 + + The differences will all be strictly positive, and in practice often very + small. We can take advantage of this fact by the simple little-endian + encoding of unsigned integers of the next section. After the binary delta + encoding of the RHSs of all AND gates, the optional symbol table and + optional comment section start in the same format as in the ASCII case. + + ... + +*/ + +static unsigned Io_ObjMakeLit( int Var, int fCompl ) { return (Var << 1) | fCompl; } +static unsigned Io_ObjAigerNum( Abc_Obj_t * pObj ) { return (unsigned)pObj->pCopy; } +static void Io_ObjSetAigerNum( Abc_Obj_t * pObj, unsigned Num ) { pObj->pCopy = (void *)Num; } + +int Io_WriteAigerEncode( char * pBuffer, int Pos, unsigned x ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Writes the AIG in the binary AIGER format.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteAiger( Abc_Ntk_t * pNtk, char * pFileName, int fWriteSymbols ) +{ + ProgressBar * pProgress; + FILE * pFile; + Abc_Obj_t * pObj, * pDriver; + int i, nNodes, Pos, nBufferSize; + unsigned char * pBuffer; + unsigned uLit0, uLit1, uLit; + + assert( Abc_NtkIsStrash(pNtk) ); + // start the output stream + pFile = fopen( pFileName, "wb" ); + if ( pFile == NULL ) + { + fprintf( stdout, "Io_WriteAiger(): Cannot open the output file \"%s\".\n", pFileName ); + return; + } + + // set the node numbers to be used in the output file + nNodes = 0; + Io_ObjSetAigerNum( Abc_AigConst1(pNtk), nNodes++ ); + Abc_NtkForEachCi( pNtk, pObj, i ) + Io_ObjSetAigerNum( pObj, nNodes++ ); + Abc_AigForEachAnd( pNtk, pObj, i ) + Io_ObjSetAigerNum( pObj, nNodes++ ); + + // write the header "M I L O A" where M = I + L + A + fprintf( pFile, "aig %u %u %u %u %u\n", + Abc_NtkPiNum(pNtk) + Abc_NtkLatchNum(pNtk) + Abc_NtkNodeNum(pNtk), + Abc_NtkPiNum(pNtk), + Abc_NtkLatchNum(pNtk), + Abc_NtkPoNum(pNtk), + Abc_NtkNodeNum(pNtk) ); + + // if the driver node is a constant, we need to complement the literal below + // because, in the AIGER format, literal 0/1 is represented as number 0/1 + // while, in ABC, constant 1 node has number 0 and so literal 0/1 will be 1/0 + + // write latch drivers + Abc_NtkForEachLatchInput( pNtk, pObj, i ) + { + pDriver = Abc_ObjFanin0(pObj); + fprintf( pFile, "%u\n", Io_ObjMakeLit( Io_ObjAigerNum(pDriver), Abc_ObjFaninC0(pObj) ^ (Io_ObjAigerNum(pDriver) == 0) ) ); + } + + // write PO drivers + Abc_NtkForEachPo( pNtk, pObj, i ) + { + pDriver = Abc_ObjFanin0(pObj); + fprintf( pFile, "%u\n", Io_ObjMakeLit( Io_ObjAigerNum(pDriver), Abc_ObjFaninC0(pObj) ^ (Io_ObjAigerNum(pDriver) == 0) ) ); + } + + // write the nodes into the buffer + Pos = 0; + nBufferSize = 6 * Abc_NtkNodeNum(pNtk) + 100; // skeptically assuming 3 chars per one AIG edge + pBuffer = ALLOC( char, nBufferSize ); + pProgress = Extra_ProgressBarStart( stdout, Abc_NtkObjNumMax(pNtk) ); + Abc_AigForEachAnd( pNtk, pObj, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + uLit = Io_ObjMakeLit( Io_ObjAigerNum(pObj), 0 ); + uLit0 = Io_ObjMakeLit( Io_ObjAigerNum(Abc_ObjFanin0(pObj)), Abc_ObjFaninC0(pObj) ); + uLit1 = Io_ObjMakeLit( Io_ObjAigerNum(Abc_ObjFanin1(pObj)), Abc_ObjFaninC1(pObj) ); + assert( uLit0 < uLit1 ); + Pos = Io_WriteAigerEncode( pBuffer, Pos, uLit - uLit1 ); + Pos = Io_WriteAigerEncode( pBuffer, Pos, uLit1 - uLit0 ); + if ( Pos > nBufferSize - 10 ) + { + printf( "Io_WriteAiger(): AIGER generation has failed because the allocated buffer is too small.\n" ); + fclose( pFile ); + return; + } + } + assert( Pos < nBufferSize ); + Extra_ProgressBarStop( pProgress ); + + // write the buffer + fwrite( pBuffer, 1, Pos, pFile ); + free( pBuffer ); + + // write the symbol table + if ( fWriteSymbols ) + { + // write PIs + Abc_NtkForEachPi( pNtk, pObj, i ) + fprintf( pFile, "i%d %s\n", i, Abc_ObjName(pObj) ); + // write latches + Abc_NtkForEachLatch( pNtk, pObj, i ) + fprintf( pFile, "l%d %s\n", i, Abc_ObjName(Abc_ObjFanout0(pObj)) ); + // write POs + Abc_NtkForEachPo( pNtk, pObj, i ) + fprintf( pFile, "o%d %s\n", i, Abc_ObjName(pObj) ); + } + + // write the comment + fprintf( pFile, "c\n" ); + fprintf( pFile, "%s\n", pNtk->pName ); + fprintf( pFile, "This file in the AIGER format was written by ABC on %s\n", Extra_TimeStamp() ); + fprintf( pFile, "For information about the format, refer to %s\n", "http://fmv.jku.at/aiger" ); + fclose( pFile ); +} + +/**Function************************************************************* + + Synopsis [Adds one unsigned AIG edge to the output buffer.] + + Description [This procedure is a slightly modified version of Armin Biere's + procedure "void encode (FILE * file, unsigned x)" ] + + SideEffects [Returns the current writing position.] + + SeeAlso [] + +***********************************************************************/ +int Io_WriteAigerEncode( char * pBuffer, int Pos, unsigned x ) +{ + unsigned char ch; + while (x & ~0x7f) + { + ch = (x & 0x7f) | 0x80; +// putc (ch, file); + pBuffer[Pos++] = ch; + x >>= 7; + } + ch = x; +// putc (ch, file); + pBuffer[Pos++] = ch; + return Pos; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/io/ioWriteBaf.c b/abc_with_bb_support/src/base/io/ioWriteBaf.c new file mode 100644 index 000000000..690af7564 --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioWriteBaf.c @@ -0,0 +1,168 @@ +/**CFile**************************************************************** + + FileName [ioWriteBaf.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures to write AIG in the binary format.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: ioWriteBaf.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/* + Binary Aig Format + + The motivation for this format is to have + - compact binary representation of large AIGs (~10x more compact than BLIF) + - consequently, fast reading/writing of large AIGs (~10x faster than BLIF) + - representation for all tech-ind info related to an AIG + - human-readable file header + + The header: + (1) May contain several lines of human-readable comments. + Each comment line begins with symbol '#' and ends with symbol '\n'. + (2) Always contains the following data. + - benchmark name + - number of primary inputs + - number of primary outputs + - number of latches + - number of AIG nodes (excluding the constant 1 node) + Each entry is followed by 0-byte (character '\0'): + (3) Next follow the names of the PIs, POs, and latches in this order. + Each name is followed by 0-byte (character '\0'). + Inside each set of names (PIs, POs, latches) there should be no + identical names but the PO names may coincide with PI/latch names. + + The body: + (1) First part of the body contains binary information about the internal AIG nodes. + Each internal AIG node is represented using two edges (each edge is a 4-byte integer). + Each integer is the fanin ID followed by 1-bit representation of the complemented attribute. + (For example, complemented edge to node 10 will be represented as 2*10 + 1 = 21.) + The IDs of the nodes are created as follows: Constant 1 node has ID=0. + CIs (PIs and latch outputs) have 1-based IDs assigned in that order. + Each node in the array of the internal AIG nodes has the ID assigned in that order. + The constant 1 node is not written into the file. + (2) Second part of the body contains binary information about the edges connecting + the COs (POs and latch inputs) to the internal AIG nodes. + Each edge is a 4-byte integer the same way as a node fanin. + The latch initial value (2 bits) is stored in this integer. +*/ + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Writes the AIG in the binary format.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteBaf( Abc_Ntk_t * pNtk, char * pFileName ) +{ + ProgressBar * pProgress; + FILE * pFile; + Abc_Obj_t * pObj; + int i, nNodes, nAnds, nBufferSize; + unsigned * pBufferNode; + assert( Abc_NtkIsStrash(pNtk) ); + // start the output stream + pFile = fopen( pFileName, "wb" ); + if ( pFile == NULL ) + { + fprintf( stdout, "Io_WriteBaf(): Cannot open the output file \"%s\".\n", pFileName ); + return; + } + + // write the comment + fprintf( pFile, "# BAF (Binary Aig Format) for \"%s\" written by ABC on %s\n", pNtk->pName, Extra_TimeStamp() ); + + // write the network name + fprintf( pFile, "%s%c", pNtk->pName, 0 ); + // write the number of PIs + fprintf( pFile, "%d%c", Abc_NtkPiNum(pNtk), 0 ); + // write the number of POs + fprintf( pFile, "%d%c", Abc_NtkPoNum(pNtk), 0 ); + // write the number of latches + fprintf( pFile, "%d%c", Abc_NtkLatchNum(pNtk), 0 ); + // write the number of internal nodes + fprintf( pFile, "%d%c", Abc_NtkNodeNum(pNtk), 0 ); + + // write PIs + Abc_NtkForEachPi( pNtk, pObj, i ) + fprintf( pFile, "%s%c", Abc_ObjName(pObj), 0 ); + // write POs + Abc_NtkForEachPo( pNtk, pObj, i ) + fprintf( pFile, "%s%c", Abc_ObjName(pObj), 0 ); + // write latches + Abc_NtkForEachLatch( pNtk, pObj, i ) + { + fprintf( pFile, "%s%c", Abc_ObjName(pObj), 0 ); + fprintf( pFile, "%s%c", Abc_ObjName(Abc_ObjFanin0(pObj)), 0 ); + fprintf( pFile, "%s%c", Abc_ObjName(Abc_ObjFanout0(pObj)), 0 ); + } + + // set the node numbers to be used in the output file + Abc_NtkCleanCopy( pNtk ); + nNodes = 1; + Abc_NtkForEachCi( pNtk, pObj, i ) + pObj->pCopy = (void *)nNodes++; + Abc_AigForEachAnd( pNtk, pObj, i ) + pObj->pCopy = (void *)nNodes++; + + // write the nodes into the buffer + nAnds = 0; + nBufferSize = Abc_NtkNodeNum(pNtk) * 2 + Abc_NtkCoNum(pNtk); + pBufferNode = ALLOC( int, nBufferSize ); + pProgress = Extra_ProgressBarStart( stdout, nBufferSize ); + Abc_AigForEachAnd( pNtk, pObj, i ) + { + Extra_ProgressBarUpdate( pProgress, nAnds, NULL ); + pBufferNode[nAnds++] = (((int)Abc_ObjFanin0(pObj)->pCopy) << 1) | Abc_ObjFaninC0(pObj); + pBufferNode[nAnds++] = (((int)Abc_ObjFanin1(pObj)->pCopy) << 1) | Abc_ObjFaninC1(pObj); + } + + // write the COs into the buffer + Abc_NtkForEachCo( pNtk, pObj, i ) + { + Extra_ProgressBarUpdate( pProgress, nAnds, NULL ); + pBufferNode[nAnds] = (((int)Abc_ObjFanin0(pObj)->pCopy) << 1) | Abc_ObjFaninC0(pObj); + if ( Abc_ObjFanoutNum(pObj) > 0 && Abc_ObjIsLatch(Abc_ObjFanout0(pObj)) ) + pBufferNode[nAnds] = (pBufferNode[nAnds] << 2) | ((unsigned)Abc_ObjData(Abc_ObjFanout0(pObj)) & 3); + nAnds++; + } + Extra_ProgressBarStop( pProgress ); + assert( nBufferSize == nAnds ); + + // write the buffer + fwrite( pBufferNode, 1, sizeof(int) * nBufferSize, pFile ); + fclose( pFile ); + free( pBufferNode ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/io/ioWriteBench.c b/abc_with_bb_support/src/base/io/ioWriteBench.c new file mode 100644 index 000000000..5d70d78dc --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioWriteBench.c @@ -0,0 +1,335 @@ +/**CFile**************************************************************** + + FileName [ioWriteBench.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures to write the network in BENCH format.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: ioWriteBench.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Io_WriteBenchCheckNames( Abc_Ntk_t * pNtk ); + +static int Io_WriteBenchOne( FILE * pFile, Abc_Ntk_t * pNtk ); +static int Io_WriteBenchOneNode( FILE * pFile, Abc_Obj_t * pNode ); + +static int Io_WriteBenchLutOne( FILE * pFile, Abc_Ntk_t * pNtk ); +static int Io_WriteBenchLutOneNode( FILE * pFile, Abc_Obj_t * pNode, Vec_Int_t * vTruth ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Writes the network in BENCH format.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_WriteBench( Abc_Ntk_t * pNtk, char * pFileName ) +{ + Abc_Ntk_t * pExdc; + FILE * pFile; + assert( Abc_NtkIsSopNetlist(pNtk) ); + if ( !Io_WriteBenchCheckNames(pNtk) ) + { + fprintf( stdout, "Io_WriteBench(): Signal names in this benchmark contain parantheses making them impossible to reproduce in the BENCH format. Use \"short_names\".\n" ); + return 0; + } + pFile = fopen( pFileName, "w" ); + if ( pFile == NULL ) + { + fprintf( stdout, "Io_WriteBench(): Cannot open the output file.\n" ); + return 0; + } + fprintf( pFile, "# Benchmark \"%s\" written by ABC on %s\n", pNtk->pName, Extra_TimeStamp() ); + // write the network + Io_WriteBenchOne( pFile, pNtk ); + // write EXDC network if it exists + pExdc = Abc_NtkExdc( pNtk ); + if ( pExdc ) + printf( "Io_WriteBench: EXDC is not written (warning).\n" ); + // finalize the file + fclose( pFile ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Writes the network in BENCH format.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_WriteBenchOne( FILE * pFile, Abc_Ntk_t * pNtk ) +{ + ProgressBar * pProgress; + Abc_Obj_t * pNode; + int i; + + // write the PIs/POs/latches + Abc_NtkForEachPi( pNtk, pNode, i ) + fprintf( pFile, "INPUT(%s)\n", Abc_ObjName(Abc_ObjFanout0(pNode)) ); + Abc_NtkForEachPo( pNtk, pNode, i ) + fprintf( pFile, "OUTPUT(%s)\n", Abc_ObjName(Abc_ObjFanin0(pNode)) ); + Abc_NtkForEachLatch( pNtk, pNode, i ) + fprintf( pFile, "%-11s = DFF(%s)\n", + Abc_ObjName(Abc_ObjFanout0(Abc_ObjFanout0(pNode))), Abc_ObjName(Abc_ObjFanin0(Abc_ObjFanin0(pNode))) ); + + // write internal nodes + pProgress = Extra_ProgressBarStart( stdout, Abc_NtkObjNumMax(pNtk) ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + Io_WriteBenchOneNode( pFile, pNode ); + } + Extra_ProgressBarStop( pProgress ); + return 1; +} + + +/**Function************************************************************* + + Synopsis [Writes the network in BENCH format.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_WriteBenchOneNode( FILE * pFile, Abc_Obj_t * pNode ) +{ + int nFanins; + + assert( Abc_ObjIsNode(pNode) ); + nFanins = Abc_ObjFaninNum(pNode); + if ( nFanins == 0 ) + { // write the constant 1 node + assert( Abc_NodeIsConst1(pNode) ); + fprintf( pFile, "%-11s", Abc_ObjName(Abc_ObjFanout0(pNode)) ); + fprintf( pFile, " = vdd\n" ); + } + else if ( nFanins == 1 ) + { // write the interver/buffer + if ( Abc_NodeIsBuf(pNode) ) + { + fprintf( pFile, "%-11s = BUFF(", Abc_ObjName(Abc_ObjFanout0(pNode)) ); + fprintf( pFile, "%s)\n", Abc_ObjName(Abc_ObjFanin0(pNode)) ); + } + else + { + fprintf( pFile, "%-11s = NOT(", Abc_ObjName(Abc_ObjFanout0(pNode)) ); + fprintf( pFile, "%s)\n", Abc_ObjName(Abc_ObjFanin0(pNode)) ); + } + } + else + { // write the AND gate + fprintf( pFile, "%-11s", Abc_ObjName(Abc_ObjFanout0(pNode)) ); + fprintf( pFile, " = AND(%s, ", Abc_ObjName(Abc_ObjFanin0(pNode)) ); + fprintf( pFile, "%s)\n", Abc_ObjName(Abc_ObjFanin1(pNode)) ); + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Writes the network in BENCH format with LUTs and DFFRSE.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_WriteBenchLut( Abc_Ntk_t * pNtk, char * pFileName ) +{ + Abc_Ntk_t * pExdc; + FILE * pFile; + assert( Abc_NtkIsAigNetlist(pNtk) ); + if ( !Io_WriteBenchCheckNames(pNtk) ) + { + fprintf( stdout, "Io_WriteBenchLut(): Signal names in this benchmark contain parantheses making them impossible to reproduce in the BENCH format. Use \"short_names\".\n" ); + return 0; + } + pFile = fopen( pFileName, "w" ); + if ( pFile == NULL ) + { + fprintf( stdout, "Io_WriteBench(): Cannot open the output file.\n" ); + return 0; + } + fprintf( pFile, "# Benchmark \"%s\" written by ABC on %s\n", pNtk->pName, Extra_TimeStamp() ); + // write the network + Io_WriteBenchLutOne( pFile, pNtk ); + // write EXDC network if it exists + pExdc = Abc_NtkExdc( pNtk ); + if ( pExdc ) + printf( "Io_WriteBench: EXDC is not written (warning).\n" ); + // finalize the file + fclose( pFile ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Writes the network in BENCH format.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_WriteBenchLutOne( FILE * pFile, Abc_Ntk_t * pNtk ) +{ + ProgressBar * pProgress; + Abc_Obj_t * pNode; + Vec_Int_t * vMemory; + int i; + + // write the PIs/POs/latches + Abc_NtkForEachPi( pNtk, pNode, i ) + fprintf( pFile, "INPUT(%s)\n", Abc_ObjName(Abc_ObjFanout0(pNode)) ); + Abc_NtkForEachPo( pNtk, pNode, i ) + fprintf( pFile, "OUTPUT(%s)\n", Abc_ObjName(Abc_ObjFanin0(pNode)) ); + Abc_NtkForEachLatch( pNtk, pNode, i ) + fprintf( pFile, "%-11s = DFFRSE( %s, gnd, gnd, gnd, gnd )\n", + Abc_ObjName(Abc_ObjFanout0(Abc_ObjFanout0(pNode))), Abc_ObjName(Abc_ObjFanin0(Abc_ObjFanin0(pNode))) ); +//Abc_NtkLevel(pNtk); + // write internal nodes + vMemory = Vec_IntAlloc( 10000 ); + pProgress = Extra_ProgressBarStart( stdout, Abc_NtkObjNumMax(pNtk) ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + Io_WriteBenchLutOneNode( pFile, pNode, vMemory ); + } + Extra_ProgressBarStop( pProgress ); + Vec_IntFree( vMemory ); + return 1; +} + + +/**Function************************************************************* + + Synopsis [Writes the network in BENCH format.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_WriteBenchLutOneNode( FILE * pFile, Abc_Obj_t * pNode, Vec_Int_t * vTruth ) +{ + Abc_Obj_t * pFanin; + unsigned * pTruth; + int i, nFanins; + assert( Abc_ObjIsNode(pNode) ); + nFanins = Abc_ObjFaninNum(pNode); + assert( nFanins <= 8 ); + // compute the truth table + pTruth = Abc_ConvertAigToTruth( pNode->pNtk->pManFunc, Hop_Regular(pNode->pData), nFanins, vTruth, 0 ); + if ( Hop_IsComplement(pNode->pData) ) + Extra_TruthNot( pTruth, pTruth, nFanins ); + // consider simple cases + if ( Extra_TruthIsConst0(pTruth, nFanins) ) + { + fprintf( pFile, "%-11s = gnd\n", Abc_ObjName(Abc_ObjFanout0(pNode)) ); + return 1; + } + if ( Extra_TruthIsConst1(pTruth, nFanins) ) + { + fprintf( pFile, "%-11s = vdd\n", Abc_ObjName(Abc_ObjFanout0(pNode)) ); + return 1; + } + if ( nFanins == 1 ) + { + fprintf( pFile, "%-11s = LUT 0x%d ( %s )\n", + Abc_ObjName(Abc_ObjFanout0(pNode)), + Abc_NodeIsBuf(pNode)? 2 : 1, + Abc_ObjName(Abc_ObjFanin0(pNode)) ); + return 1; + } + // write it in the hexadecimal form + fprintf( pFile, "%-11s = LUT 0x", Abc_ObjName(Abc_ObjFanout0(pNode)) ); + Extra_PrintHexadecimal( pFile, pTruth, nFanins ); +/* + { +extern void Kit_DsdTest( unsigned * pTruth, int nVars ); +Abc_ObjForEachFanin( pNode, pFanin, i ) +printf( "%c%d ", 'a'+i, Abc_ObjFanin0(pFanin)->Level ); +printf( "\n" ); +Kit_DsdTest( pTruth, nFanins ); + } + if ( pNode->Id % 1000 == 0 ) + { + int x = 0; + } +*/ + // write the fanins + fprintf( pFile, " (" ); + Abc_ObjForEachFanin( pNode, pFanin, i ) + fprintf( pFile, " %s%s", Abc_ObjName(pFanin), ((i==nFanins-1)? "" : ",") ); + fprintf( pFile, " )\n" ); + return 1; +} + + +/**Function************************************************************* + + Synopsis [Returns 1 if the names cannot be written into the bench file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_WriteBenchCheckNames( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + char * pName; + int i; + Abc_NtkForEachObj( pNtk, pObj, i ) + for ( pName = Nm_ManFindNameById(pNtk->pManName, i); pName && *pName; pName++ ) + if ( *pName == '(' || *pName == ')' ) + return 0; + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/io/ioWriteBlif.c b/abc_with_bb_support/src/base/io/ioWriteBlif.c new file mode 100644 index 000000000..63ad882b8 --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioWriteBlif.c @@ -0,0 +1,656 @@ +/**CFile**************************************************************** + + FileName [ioWriteBlif.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures to write BLIF files.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: ioWriteBlif.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" +#include "main.h" +#include "mio.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Io_NtkWrite( FILE * pFile, Abc_Ntk_t * pNtk, int fWriteLatches ); +static void Io_NtkWriteOne( FILE * pFile, Abc_Ntk_t * pNtk, int fWriteLatches ); +static void Io_NtkWritePis( FILE * pFile, Abc_Ntk_t * pNtk, int fWriteLatches ); +static void Io_NtkWritePos( FILE * pFile, Abc_Ntk_t * pNtk, int fWriteLatches ); +static void Io_NtkWriteSubckt( FILE * pFile, Abc_Obj_t * pNode ); +static void Io_NtkWriteAsserts( FILE * pFile, Abc_Ntk_t * pNtk ); +static void Io_NtkWriteNodeGate( FILE * pFile, Abc_Obj_t * pNode, int Length ); +static void Io_NtkWriteNodeFanins( FILE * pFile, Abc_Obj_t * pNode ); +static void Io_NtkWriteNode( FILE * pFile, Abc_Obj_t * pNode, int Length ); +static void Io_NtkWriteLatch( FILE * pFile, Abc_Obj_t * pLatch ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Write the network into a BLIF file with the given name.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteBlifLogic( Abc_Ntk_t * pNtk, char * FileName, int fWriteLatches ) +{ + Abc_Ntk_t * pNtkTemp; + // derive the netlist + pNtkTemp = Abc_NtkToNetlist(pNtk); + if ( pNtkTemp == NULL ) + { + fprintf( stdout, "Writing BLIF has failed.\n" ); + return; + } + Io_WriteBlif( pNtkTemp, FileName, fWriteLatches ); + Abc_NtkDelete( pNtkTemp ); +} + +/**Function************************************************************* + + Synopsis [Write the network into a BLIF file with the given name.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteBlif( Abc_Ntk_t * pNtk, char * FileName, int fWriteLatches ) +{ + FILE * pFile; + Abc_Ntk_t * pNtkTemp; + int i; + assert( Abc_NtkIsNetlist(pNtk) ); + // start writing the file + pFile = fopen( FileName, "w" ); + if ( pFile == NULL ) + { + fprintf( stdout, "Io_WriteBlif(): Cannot open the output file.\n" ); + return; + } + fprintf( pFile, "# Benchmark \"%s\" written by ABC on %s\n", pNtk->pName, Extra_TimeStamp() ); + // write the master network + Io_NtkWrite( pFile, pNtk, fWriteLatches ); + // make sure there is no logic hierarchy + assert( Abc_NtkWhiteboxNum(pNtk) == 0 ); + // write the hierarchy if present + if ( Abc_NtkBlackboxNum(pNtk) > 0 ) + { + Vec_PtrForEachEntry( pNtk->pDesign->vModules, pNtkTemp, i ) + { + if ( pNtkTemp == pNtk ) + continue; + fprintf( pFile, "\n\n" ); + Io_NtkWrite( pFile, pNtkTemp, fWriteLatches ); + } + } + fclose( pFile ); +} + +/**Function************************************************************* + + Synopsis [Write the network into a BLIF file with the given name.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWrite( FILE * pFile, Abc_Ntk_t * pNtk, int fWriteLatches ) +{ + Abc_Ntk_t * pExdc; + assert( Abc_NtkIsNetlist(pNtk) ); + // write the model name + fprintf( pFile, ".model %s\n", Abc_NtkName(pNtk) ); + // write the network + Io_NtkWriteOne( pFile, pNtk, fWriteLatches ); + // write EXDC network if it exists + pExdc = Abc_NtkExdc( pNtk ); + if ( pExdc ) + { + fprintf( pFile, "\n" ); + fprintf( pFile, ".exdc\n" ); + Io_NtkWriteOne( pFile, pExdc, fWriteLatches ); + } + // finalize the file + fprintf( pFile, ".end\n" ); +} + +/**Function************************************************************* + + Synopsis [Write one network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWriteOne( FILE * pFile, Abc_Ntk_t * pNtk, int fWriteLatches ) +{ + ProgressBar * pProgress; + Abc_Obj_t * pNode, * pLatch; + int i, Length; + + // write the PIs + fprintf( pFile, ".inputs" ); + Io_NtkWritePis( pFile, pNtk, fWriteLatches ); + fprintf( pFile, "\n" ); + + // write the POs + fprintf( pFile, ".outputs" ); + Io_NtkWritePos( pFile, pNtk, fWriteLatches ); + fprintf( pFile, "\n" ); + + // write the assertions + if ( Abc_NtkAssertNum(pNtk) ) + { + fprintf( pFile, ".asserts" ); + Io_NtkWriteAsserts( pFile, pNtk ); + fprintf( pFile, "\n" ); + } + + // write the blackbox + if ( Abc_NtkHasBlackbox( pNtk ) ) + { + fprintf( pFile, ".blackbox\n" ); + return; + } + + // write the timing info + Io_WriteTimingInfo( pFile, pNtk ); + + // write the latches + if ( fWriteLatches && !Abc_NtkIsComb(pNtk) ) + { + fprintf( pFile, "\n" ); + Abc_NtkForEachLatch( pNtk, pLatch, i ) + Io_NtkWriteLatch( pFile, pLatch ); + fprintf( pFile, "\n" ); + } + + // write the subcircuits + assert( Abc_NtkWhiteboxNum(pNtk) == 0 ); + if ( Abc_NtkBlackboxNum(pNtk) > 0 ) + { + fprintf( pFile, "\n" ); + Abc_NtkForEachBlackbox( pNtk, pNode, i ) + Io_NtkWriteSubckt( pFile, pNode ); + fprintf( pFile, "\n" ); + } + + // write each internal node + Length = Abc_NtkHasMapping(pNtk)? Mio_LibraryReadGateNameMax(pNtk->pManFunc) : 0; + pProgress = Extra_ProgressBarStart( stdout, Abc_NtkObjNumMax(pNtk) ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + Io_NtkWriteNode( pFile, pNode, Length ); + } + Extra_ProgressBarStop( pProgress ); +} + + +/**Function************************************************************* + + Synopsis [Writes the primary input list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWritePis( FILE * pFile, Abc_Ntk_t * pNtk, int fWriteLatches ) +{ + Abc_Obj_t * pTerm, * pNet; + int LineLength; + int AddedLength; + int NameCounter; + int i; + + LineLength = 7; + NameCounter = 0; + + if ( fWriteLatches ) + { + Abc_NtkForEachPi( pNtk, pTerm, i ) + { + pNet = Abc_ObjFanout0(pTerm); + // get the line length after this name is written + AddedLength = strlen(Abc_ObjName(pNet)) + 1; + if ( NameCounter && LineLength + AddedLength + 3 > IO_WRITE_LINE_LENGTH ) + { // write the line extender + fprintf( pFile, " \\\n" ); + // reset the line length + LineLength = 0; + NameCounter = 0; + } + fprintf( pFile, " %s", Abc_ObjName(pNet) ); + LineLength += AddedLength; + NameCounter++; + } + } + else + { + Abc_NtkForEachCi( pNtk, pTerm, i ) + { + pNet = Abc_ObjFanout0(pTerm); + // get the line length after this name is written + AddedLength = strlen(Abc_ObjName(pNet)) + 1; + if ( NameCounter && LineLength + AddedLength + 3 > IO_WRITE_LINE_LENGTH ) + { // write the line extender + fprintf( pFile, " \\\n" ); + // reset the line length + LineLength = 0; + NameCounter = 0; + } + fprintf( pFile, " %s", Abc_ObjName(pNet) ); + LineLength += AddedLength; + NameCounter++; + } + } +} + +/**Function************************************************************* + + Synopsis [Writes the primary input list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWritePos( FILE * pFile, Abc_Ntk_t * pNtk, int fWriteLatches ) +{ + Abc_Obj_t * pTerm, * pNet; + int LineLength; + int AddedLength; + int NameCounter; + int i; + + LineLength = 8; + NameCounter = 0; + + if ( fWriteLatches ) + { + Abc_NtkForEachPo( pNtk, pTerm, i ) + { + pNet = Abc_ObjFanin0(pTerm); + // get the line length after this name is written + AddedLength = strlen(Abc_ObjName(pNet)) + 1; + if ( NameCounter && LineLength + AddedLength + 3 > IO_WRITE_LINE_LENGTH ) + { // write the line extender + fprintf( pFile, " \\\n" ); + // reset the line length + LineLength = 0; + NameCounter = 0; + } + fprintf( pFile, " %s", Abc_ObjName(pNet) ); + LineLength += AddedLength; + NameCounter++; + } + } + else + { + Abc_NtkForEachCo( pNtk, pTerm, i ) + { + if ( Abc_ObjIsAssert(pTerm) ) + continue; + pNet = Abc_ObjFanin0(pTerm); + // get the line length after this name is written + AddedLength = strlen(Abc_ObjName(pNet)) + 1; + if ( NameCounter && LineLength + AddedLength + 3 > IO_WRITE_LINE_LENGTH ) + { // write the line extender + fprintf( pFile, " \\\n" ); + // reset the line length + LineLength = 0; + NameCounter = 0; + } + fprintf( pFile, " %s", Abc_ObjName(pNet) ); + LineLength += AddedLength; + NameCounter++; + } + } +} + +/**Function************************************************************* + + Synopsis [Writes the assertion list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWriteAsserts( FILE * pFile, Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pTerm, * pNet; + int LineLength; + int AddedLength; + int NameCounter; + int i; + + LineLength = 8; + NameCounter = 0; + + Abc_NtkForEachAssert( pNtk, pTerm, i ) + { + pNet = Abc_ObjFanin0(pTerm); + // get the line length after this name is written + AddedLength = strlen(Abc_ObjName(pNet)) + 1; + if ( NameCounter && LineLength + AddedLength + 3 > IO_WRITE_LINE_LENGTH ) + { // write the line extender + fprintf( pFile, " \\\n" ); + // reset the line length + LineLength = 0; + NameCounter = 0; + } + fprintf( pFile, " %s", Abc_ObjName(pNet) ); + LineLength += AddedLength; + NameCounter++; + } +} + +/**Function************************************************************* + + Synopsis [Write subcircuit info into a file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWriteSubckt( FILE * pFile, Abc_Obj_t * pNode ) +{ + Abc_Ntk_t * pModel = pNode->pData; + Abc_Obj_t * pTerm; + char * str; + int i; + int LineLength; + int AddedLength; + + // write the subcircuit + fprintf( pFile, ".subckt %s", Abc_NtkName(pModel) ); + + LineLength = 8 + strlen(Abc_NtkName(pModel)) + 1; + + // write pairs of the formal=actual names + Abc_NtkForEachPi( pModel, pTerm, i ) + { + str = Abc_ObjName(Abc_ObjFanout0(pTerm)); + fprintf( pFile, " %s", str ); + AddedLength = strlen(str) + 1; + + pTerm = Abc_ObjFanin( pNode, i ); + + str = Abc_ObjName(Abc_ObjFanin0(pTerm)); + fprintf( pFile, "=%s", str ); + AddedLength += strlen(str) + 1; + + if ( LineLength + AddedLength + 3 > IO_WRITE_LINE_LENGTH ) + { + fprintf( pFile, " \\\n" ); + LineLength = 0; + } + else + { + LineLength += AddedLength; + } + } + + Abc_NtkForEachPo( pModel, pTerm, i ) + { + str = Abc_ObjName(Abc_ObjFanin0(pTerm)); + fprintf( pFile, " %s", str ); + AddedLength = strlen(str) + 1; + + pTerm = Abc_ObjFanout( pNode, i ); + + str = Abc_ObjName(Abc_ObjFanout0(pTerm)); + fprintf( pFile, "=%s", str ); + AddedLength += strlen(str) + 1; + + if ( LineLength + AddedLength + 3 > IO_WRITE_LINE_LENGTH ) + { + fprintf( pFile, " \\\n" ); + LineLength = 0; + } + else + { + LineLength += AddedLength; + } + } + + fprintf( pFile, "\n" ); +} + +/**Function************************************************************* + + Synopsis [Write the latch into a file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWriteLatch( FILE * pFile, Abc_Obj_t * pLatch ) +{ + Abc_Obj_t * pNetLi, * pNetLo; + Abc_LatchInfo_t *pLatchInfo; + int Reset; + + pLatchInfo = ((Abc_LatchInfo_t *)(pLatch->pData)); + + pNetLi = Abc_ObjFanin0( Abc_ObjFanin0(pLatch) ); + pNetLo = Abc_ObjFanout0( Abc_ObjFanout0(pLatch) ); + Reset = pLatchInfo->InitValue; + // write the latch line + fprintf( pFile, ".latch" ); + fprintf( pFile, " %10s", Abc_ObjName(pNetLi) ); + fprintf( pFile, " %10s", Abc_ObjName(pNetLo) ); + + switch (pLatchInfo->LatchType) + { + case ABC_RISING_EDGE: + fprintf( pFile, " re"); + break; + case ABC_FALLING_EDGE: + fprintf( pFile, " fe"); + break; + case ABC_ACTIVE_HIGH: + fprintf( pFile, " ah"); + break; + case ABC_ACTIVE_LOW: + fprintf( pFile, " al"); + break; + default: + fprintf( pFile, " as"); + break; + } + + fprintf( pFile, " %10s", ((Abc_LatchInfo_t *)pLatch->pData)->pClkName); + fprintf( pFile, " %d\n", Reset-1 ); +} + + +/**Function************************************************************* + + Synopsis [Write the node into a file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWriteNode( FILE * pFile, Abc_Obj_t * pNode, int Length ) +{ + if ( Abc_NtkHasMapping(pNode->pNtk) ) + { + // write the .gate line + fprintf( pFile, ".gate" ); + Io_NtkWriteNodeGate( pFile, pNode, Length ); + fprintf( pFile, "\n" ); + } + else + { + // write the .names line + fprintf( pFile, ".names" ); + Io_NtkWriteNodeFanins( pFile, pNode ); + fprintf( pFile, "\n" ); + // write the cubes + fprintf( pFile, "%s", Abc_ObjData(pNode) ); + } +} + +/**Function************************************************************* + + Synopsis [Writes the primary input list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWriteNodeGate( FILE * pFile, Abc_Obj_t * pNode, int Length ) +{ + Mio_Gate_t * pGate = pNode->pData; + Mio_Pin_t * pGatePin; + int i; + // write the node + fprintf( pFile, " %-*s ", Length, Mio_GateReadName(pGate) ); + for ( pGatePin = Mio_GateReadPins(pGate), i = 0; pGatePin; pGatePin = Mio_PinReadNext(pGatePin), i++ ) + fprintf( pFile, "%s=%s ", Mio_PinReadName(pGatePin), Abc_ObjName( Abc_ObjFanin(pNode,i) ) ); + assert ( i == Abc_ObjFaninNum(pNode) ); + fprintf( pFile, "%s=%s", Mio_GateReadOutName(pGate), Abc_ObjName( Abc_ObjFanout0(pNode) ) ); +} + +/**Function************************************************************* + + Synopsis [Writes the primary input list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWriteNodeFanins( FILE * pFile, Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pNet; + int LineLength; + int AddedLength; + int NameCounter; + char * pName; + int i; + + LineLength = 6; + NameCounter = 0; + Abc_ObjForEachFanin( pNode, pNet, i ) + { + // get the fanin name + pName = Abc_ObjName(pNet); + // get the line length after the fanin name is written + AddedLength = strlen(pName) + 1; + if ( NameCounter && LineLength + AddedLength + 3 > IO_WRITE_LINE_LENGTH ) + { // write the line extender + fprintf( pFile, " \\\n" ); + // reset the line length + LineLength = 0; + NameCounter = 0; + } + fprintf( pFile, " %s", pName ); + LineLength += AddedLength; + NameCounter++; + } + + // get the output name + pName = Abc_ObjName(Abc_ObjFanout0(pNode)); + // get the line length after the output name is written + AddedLength = strlen(pName) + 1; + if ( NameCounter && LineLength + AddedLength > 75 ) + { // write the line extender + fprintf( pFile, " \\\n" ); + // reset the line length + LineLength = 0; + NameCounter = 0; + } + fprintf( pFile, " %s", pName ); +} + +/**Function************************************************************* + + Synopsis [Writes the timing info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteTimingInfo( FILE * pFile, Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + Abc_Time_t * pTime, * pTimeDef; + int i; + + if ( pNtk->pManTime == NULL ) + return; + + pTimeDef = Abc_NtkReadDefaultArrival( pNtk ); + fprintf( pFile, ".default_input_arrival %g %g\n", pTimeDef->Rise, pTimeDef->Fall ); + Abc_NtkForEachPi( pNtk, pNode, i ) + { + pTime = Abc_NodeReadArrival(pNode); + if ( pTime->Rise == pTimeDef->Rise && pTime->Fall == pTimeDef->Fall ) + continue; + fprintf( pFile, ".input_arrival %s %g %g\n", Abc_ObjName(pNode), pTime->Rise, pTime->Fall ); + } +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/io/ioWriteBlifMv.c b/abc_with_bb_support/src/base/io/ioWriteBlifMv.c new file mode 100644 index 000000000..847c24557 --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioWriteBlifMv.c @@ -0,0 +1,519 @@ +/**CFile**************************************************************** + + FileName [ioWriteBlifMv.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures to write BLIF-MV files.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: ioWriteBlifMv.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" +#include "main.h" +#include "mio.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Io_NtkWriteBlifMv( FILE * pFile, Abc_Ntk_t * pNtk ); +static void Io_NtkWriteBlifMvOne( FILE * pFile, Abc_Ntk_t * pNtk ); +static void Io_NtkWriteBlifMvPis( FILE * pFile, Abc_Ntk_t * pNtk ); +static void Io_NtkWriteBlifMvPos( FILE * pFile, Abc_Ntk_t * pNtk ); +static void Io_NtkWriteBlifMvAsserts( FILE * pFile, Abc_Ntk_t * pNtk ); +static void Io_NtkWriteBlifMvNodeFanins( FILE * pFile, Abc_Obj_t * pNode ); +static void Io_NtkWriteBlifMvNode( FILE * pFile, Abc_Obj_t * pNode ); +static void Io_NtkWriteBlifMvLatch( FILE * pFile, Abc_Obj_t * pLatch ); +static void Io_NtkWriteBlifMvSubckt( FILE * pFile, Abc_Obj_t * pNode ); +static void Io_NtkWriteBlifMvValues( FILE * pFile, Abc_Obj_t * pNode ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Write the network into a BLIF file with the given name.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteBlifMv( Abc_Ntk_t * pNtk, char * FileName ) +{ + FILE * pFile; + Abc_Ntk_t * pNtkTemp; + int i; + assert( Abc_NtkIsNetlist(pNtk) ); + assert( Abc_NtkHasBlifMv(pNtk) ); + // start writing the file + pFile = fopen( FileName, "w" ); + if ( pFile == NULL ) + { + fprintf( stdout, "Io_WriteBlifMv(): Cannot open the output file.\n" ); + return; + } + fprintf( pFile, "# Benchmark \"%s\" written by ABC on %s\n", pNtk->pName, Extra_TimeStamp() ); + // write the master network + Io_NtkWriteBlifMv( pFile, pNtk ); + // write the remaining networks + if ( pNtk->pDesign ) + { + Vec_PtrForEachEntry( pNtk->pDesign->vModules, pNtkTemp, i ) + { + if ( pNtkTemp == pNtk ) + continue; + fprintf( pFile, "\n\n" ); + Io_NtkWriteBlifMv( pFile, pNtkTemp ); + } + } + fclose( pFile ); +} + +/**Function************************************************************* + + Synopsis [Write the network into a BLIF file with the given name.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWriteBlifMv( FILE * pFile, Abc_Ntk_t * pNtk ) +{ + assert( Abc_NtkIsNetlist(pNtk) ); + // write the model name + fprintf( pFile, ".model %s\n", Abc_NtkName(pNtk) ); + // write the network + Io_NtkWriteBlifMvOne( pFile, pNtk ); + // write EXDC network if it exists + if ( Abc_NtkExdc(pNtk) ) + printf( "Io_NtkWriteBlifMv(): EXDC is not written.\n" ); + // finalize the file + fprintf( pFile, ".end\n\n\n" ); +} + +/**Function************************************************************* + + Synopsis [Write one network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWriteBlifMvOne( FILE * pFile, Abc_Ntk_t * pNtk ) +{ + ProgressBar * pProgress; + Abc_Obj_t * pNode, * pTerm, * pLatch; + int i; + + // write the PIs + fprintf( pFile, ".inputs" ); + Io_NtkWriteBlifMvPis( pFile, pNtk ); + fprintf( pFile, "\n" ); + + // write the POs + fprintf( pFile, ".outputs" ); + Io_NtkWriteBlifMvPos( pFile, pNtk ); + fprintf( pFile, "\n" ); + + // write the assertions + if ( Abc_NtkAssertNum(pNtk) ) + { + fprintf( pFile, ".asserts" ); + Io_NtkWriteBlifMvAsserts( pFile, pNtk ); + fprintf( pFile, "\n" ); + } + + // write the MV directives + fprintf( pFile, "\n" ); + Abc_NtkForEachCi( pNtk, pTerm, i ) + if ( Abc_ObjMvVarNum(Abc_ObjFanout0(pTerm)) > 2 ) + fprintf( pFile, ".mv %s %d\n", Abc_ObjName(Abc_ObjFanout0(pTerm)), Abc_ObjMvVarNum(Abc_ObjFanout0(pTerm)) ); + Abc_NtkForEachCo( pNtk, pTerm, i ) + if ( Abc_ObjMvVarNum(Abc_ObjFanin0(pTerm)) > 2 ) + fprintf( pFile, ".mv %s %d\n", Abc_ObjName(Abc_ObjFanin0(pTerm)), Abc_ObjMvVarNum(Abc_ObjFanin0(pTerm)) ); + + // write the blackbox + if ( Abc_NtkHasBlackbox( pNtk ) ) + { + fprintf( pFile, ".blackbox\n" ); + return; + } + + // write the timing info +// Io_WriteTimingInfo( pFile, pNtk ); + + // write the latches + if ( !Abc_NtkIsComb(pNtk) ) + { + fprintf( pFile, "\n" ); + Abc_NtkForEachLatch( pNtk, pLatch, i ) + Io_NtkWriteBlifMvLatch( pFile, pLatch ); + fprintf( pFile, "\n" ); + } +/* + // write the subcircuits + assert( Abc_NtkWhiteboxNum(pNtk) == 0 ); + if ( Abc_NtkBlackboxNum(pNtk) > 0 ) + { + fprintf( pFile, "\n" ); + Abc_NtkForEachBlackbox( pNtk, pNode, i ) + Io_NtkWriteBlifMvSubckt( pFile, pNode ); + fprintf( pFile, "\n" ); + } +*/ + if ( Abc_NtkBlackboxNum(pNtk) > 0 || Abc_NtkWhiteboxNum(pNtk) > 0 ) + { + fprintf( pFile, "\n" ); + Abc_NtkForEachBox( pNtk, pNode, i ) + { + if ( Abc_ObjIsLatch(pNode) ) + continue; + Io_NtkWriteBlifMvSubckt( pFile, pNode ); + } + fprintf( pFile, "\n" ); + } + + // write each internal node + pProgress = Extra_ProgressBarStart( stdout, Abc_NtkObjNumMax(pNtk) ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + Io_NtkWriteBlifMvNode( pFile, pNode ); + } + Extra_ProgressBarStop( pProgress ); +} + + +/**Function************************************************************* + + Synopsis [Writes the primary input list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWriteBlifMvPis( FILE * pFile, Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pTerm, * pNet; + int LineLength; + int AddedLength; + int NameCounter; + int i; + + LineLength = 7; + NameCounter = 0; + + Abc_NtkForEachPi( pNtk, pTerm, i ) + { + pNet = Abc_ObjFanout0(pTerm); + // get the line length after this name is written + AddedLength = strlen(Abc_ObjName(pNet)) + 1; + if ( NameCounter && LineLength + AddedLength + 3 > IO_WRITE_LINE_LENGTH ) + { // write the line extender + fprintf( pFile, " \\\n" ); + // reset the line length + LineLength = 0; + NameCounter = 0; + } + fprintf( pFile, " %s", Abc_ObjName(pNet) ); + LineLength += AddedLength; + NameCounter++; + } +} + +/**Function************************************************************* + + Synopsis [Writes the primary input list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWriteBlifMvPos( FILE * pFile, Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pTerm, * pNet; + int LineLength; + int AddedLength; + int NameCounter; + int i; + + LineLength = 8; + NameCounter = 0; + + Abc_NtkForEachPo( pNtk, pTerm, i ) + { + pNet = Abc_ObjFanin0(pTerm); + // get the line length after this name is written + AddedLength = strlen(Abc_ObjName(pNet)) + 1; + if ( NameCounter && LineLength + AddedLength + 3 > IO_WRITE_LINE_LENGTH ) + { // write the line extender + fprintf( pFile, " \\\n" ); + // reset the line length + LineLength = 0; + NameCounter = 0; + } + fprintf( pFile, " %s", Abc_ObjName(pNet) ); + LineLength += AddedLength; + NameCounter++; + } +} + +/**Function************************************************************* + + Synopsis [Writes the assertion list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWriteBlifMvAsserts( FILE * pFile, Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pTerm, * pNet; + int LineLength; + int AddedLength; + int NameCounter; + int i; + + LineLength = 8; + NameCounter = 0; + + Abc_NtkForEachAssert( pNtk, pTerm, i ) + { + pNet = Abc_ObjFanin0(pTerm); + // get the line length after this name is written + AddedLength = strlen(Abc_ObjName(pNet)) + 1; + if ( NameCounter && LineLength + AddedLength + 3 > IO_WRITE_LINE_LENGTH ) + { // write the line extender + fprintf( pFile, " \\\n" ); + // reset the line length + LineLength = 0; + NameCounter = 0; + } + fprintf( pFile, " %s", Abc_ObjName(pNet) ); + LineLength += AddedLength; + NameCounter++; + } +} + +/**Function************************************************************* + + Synopsis [Write the latch into a file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWriteBlifMvLatch( FILE * pFile, Abc_Obj_t * pLatch ) +{ + Abc_Obj_t * pNetLi, * pNetLo; + int Reset; + pNetLi = Abc_ObjFanin0( Abc_ObjFanin0(pLatch) ); + pNetLo = Abc_ObjFanout0( Abc_ObjFanout0(pLatch) ); + Reset = (int)Abc_ObjData( pLatch ); + // write the latch line + fprintf( pFile, ".latch" ); + fprintf( pFile, " %10s", Abc_ObjName(pNetLi) ); + fprintf( pFile, " %10s", Abc_ObjName(pNetLo) ); + fprintf( pFile, "\n" ); + // write the reset node + fprintf( pFile, ".reset %s\n", Abc_ObjName(pNetLo) ); + fprintf( pFile, "%d\n", Reset-1 ); +} + +/**Function************************************************************* + + Synopsis [Write the latch into a file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWriteBlifMvSubckt( FILE * pFile, Abc_Obj_t * pNode ) +{ + Abc_Ntk_t * pModel = pNode->pData; + Abc_Obj_t * pTerm; + int i; + // write the MV directives + fprintf( pFile, "\n" ); + Abc_ObjForEachFanin( pNode, pTerm, i ) + if ( Abc_ObjMvVarNum(pTerm) > 2 ) + fprintf( pFile, ".mv %s %d\n", Abc_ObjName(pTerm), Abc_ObjMvVarNum(pTerm) ); + Abc_ObjForEachFanout( pNode, pTerm, i ) + if ( Abc_ObjMvVarNum(pTerm) > 2 ) + fprintf( pFile, ".mv %s %d\n", Abc_ObjName(pTerm), Abc_ObjMvVarNum(pTerm) ); + // write the subcircuit + fprintf( pFile, ".subckt %s %s", Abc_NtkName(pModel), Abc_ObjName(pNode) ); + // write pairs of the formal=actual names + Abc_NtkForEachPi( pModel, pTerm, i ) + { + fprintf( pFile, " %s", Abc_ObjName(Abc_ObjFanout0(pTerm)) ); + pTerm = Abc_ObjFanin( pNode, i ); + fprintf( pFile, "=%s", Abc_ObjName(Abc_ObjFanin0(pTerm)) ); + } + Abc_NtkForEachPo( pModel, pTerm, i ) + { + fprintf( pFile, " %s", Abc_ObjName(Abc_ObjFanin0(pTerm)) ); + pTerm = Abc_ObjFanout( pNode, i ); + fprintf( pFile, "=%s", Abc_ObjName(Abc_ObjFanout0(pTerm)) ); + } + fprintf( pFile, "\n" ); +} + + +/**Function************************************************************* + + Synopsis [Write the node into a file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWriteBlifMvNode( FILE * pFile, Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pFanin; + char * pCur; + int nValues, iFanin, i; + + // write .mv directives for the fanins + fprintf( pFile, "\n" ); + Abc_ObjForEachFanin( pNode, pFanin, i ) + { +// nValues = atoi(pCur); + nValues = Abc_ObjMvVarNum( pFanin ); + if ( nValues > 2 ) + fprintf( pFile, ".mv %s %d\n", Abc_ObjName(pFanin), nValues ); +// while ( *pCur++ != ' ' ); + } + + // write .mv directives for the node +// nValues = atoi(pCur); + nValues = Abc_ObjMvVarNum( Abc_ObjFanout0(pNode) ); + if ( nValues > 2 ) + fprintf( pFile, ".mv %s %d\n", Abc_ObjName(Abc_ObjFanout0(pNode)), nValues ); +// while ( *pCur++ != '\n' ); + + // write the .names line + fprintf( pFile, ".table" ); + Io_NtkWriteBlifMvNodeFanins( pFile, pNode ); + fprintf( pFile, "\n" ); + + // write the cubes + pCur = Abc_ObjData(pNode); + if ( *pCur == 'd' ) + { + fprintf( pFile, ".default " ); + pCur++; + } + // write the literals + for ( ; *pCur; pCur++ ) + { + fprintf( pFile, "%c", *pCur ); + if ( *pCur != '=' ) + continue; + // get the number + iFanin = atoi( pCur+1 ); + fprintf( pFile, "%s", Abc_ObjName(Abc_ObjFanin(pNode,iFanin)) ); + // scroll on to the next symbol + while ( *pCur != ' ' && *pCur != '\n' ) + pCur++; + pCur--; + } +} + +/**Function************************************************************* + + Synopsis [Writes the primary input list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWriteBlifMvNodeFanins( FILE * pFile, Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pNet; + int LineLength; + int AddedLength; + int NameCounter; + char * pName; + int i; + + LineLength = 6; + NameCounter = 0; + Abc_ObjForEachFanin( pNode, pNet, i ) + { + // get the fanin name + pName = Abc_ObjName(pNet); + // get the line length after the fanin name is written + AddedLength = strlen(pName) + 1; + if ( NameCounter && LineLength + AddedLength + 3 > IO_WRITE_LINE_LENGTH ) + { // write the line extender + fprintf( pFile, " \\\n" ); + // reset the line length + LineLength = 0; + NameCounter = 0; + } + fprintf( pFile, " %s", pName ); + LineLength += AddedLength; + NameCounter++; + } + + // get the output name + pName = Abc_ObjName(Abc_ObjFanout0(pNode)); + // get the line length after the output name is written + AddedLength = strlen(pName) + 1; + if ( NameCounter && LineLength + AddedLength > 75 ) + { // write the line extender + fprintf( pFile, " \\\n" ); + // reset the line length + LineLength = 0; + NameCounter = 0; + } + fprintf( pFile, " %s", pName ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/io/ioWriteCnf.c b/abc_with_bb_support/src/base/io/ioWriteCnf.c new file mode 100644 index 000000000..62fa8738c --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioWriteCnf.c @@ -0,0 +1,115 @@ +/**CFile**************************************************************** + + FileName [ioWriteCnf.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures to output CNF of the miter cone.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: ioWriteCnf.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" +#include "satSolver.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Abc_Ntk_t * s_pNtk = NULL; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Write the miter cone into a CNF file for the SAT solver.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_WriteCnf( Abc_Ntk_t * pNtk, char * pFileName, int fAllPrimes ) +{ + sat_solver * pSat; + if ( Abc_NtkIsStrash(pNtk) ) + printf( "Io_WriteCnf() warning: Generating CNF by applying heuristic AIG to CNF conversion.\n" ); + else + printf( "Io_WriteCnf() warning: Generating CNF by convering logic nodes into CNF clauses.\n" ); + if ( Abc_NtkPoNum(pNtk) != 1 ) + { + fprintf( stdout, "Io_WriteCnf(): Currently can only process the miter (the network with one PO).\n" ); + return 0; + } + if ( Abc_NtkLatchNum(pNtk) != 0 ) + { + fprintf( stdout, "Io_WriteCnf(): Currently can only process the miter for combinational circuits.\n" ); + return 0; + } + if ( Abc_NtkNodeNum(pNtk) == 0 ) + { + fprintf( stdout, "The network has no logic nodes. No CNF file is generaled.\n" ); + return 0; + } + // convert to logic BDD network + if ( Abc_NtkIsLogic(pNtk) ) + Abc_NtkToBdd( pNtk ); + // create solver with clauses + pSat = Abc_NtkMiterSatCreate( pNtk, fAllPrimes ); + if ( pSat == NULL ) + { + fprintf( stdout, "The problem is trivially UNSAT. No CNF file is generated.\n" ); + return 1; + } + // write the clauses + s_pNtk = pNtk; + Sat_SolverWriteDimacs( pSat, pFileName, 0, 0, 1 ); + s_pNtk = NULL; + // free the solver + sat_solver_delete( pSat ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Output the mapping of PIs into variable numbers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteCnfOutputPiMapping( FILE * pFile, int incrementVars ) +{ + extern Vec_Int_t * Abc_NtkGetCiSatVarNums( Abc_Ntk_t * pNtk ); + Abc_Ntk_t * pNtk = s_pNtk; + Vec_Int_t * vCiIds; + Abc_Obj_t * pObj; + int i; + vCiIds = Abc_NtkGetCiSatVarNums( pNtk ); + fprintf( pFile, "c PI variable numbers: \n" ); + Abc_NtkForEachCi( pNtk, pObj, i ) + fprintf( pFile, "c %s %d\n", Abc_ObjName(pObj), Vec_IntEntry(vCiIds, i) + (int)(incrementVars > 0) ); + Vec_IntFree( vCiIds ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/io/ioWriteDot.c b/abc_with_bb_support/src/base/io/ioWriteDot.c new file mode 100644 index 000000000..60c96ee0e --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioWriteDot.c @@ -0,0 +1,809 @@ +/**CFile**************************************************************** + + FileName [ioWriteDot.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures to write the graph structure of AIG in DOT.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: ioWriteDot.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" +#include "main.h" +#include "mio.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static char * Abc_NtkPrintSop( char * pSop ); +static int Abc_NtkCountLogicNodes( Vec_Ptr_t * vNodes ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Writes the graph structure of network for DOT.] + + Description [Useful for graph visualization using tools such as GraphViz: + http://www.graphviz.org/] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteDot( Abc_Ntk_t * pNtk, char * FileName ) +{ + Vec_Ptr_t * vNodes; + vNodes = Abc_NtkCollectObjects( pNtk ); + Io_WriteDotNtk( pNtk, vNodes, NULL, FileName, 0, 0 ); + Vec_PtrFree( vNodes ); +} + +/**Function************************************************************* + + Synopsis [Writes the graph structure of network for DOT.] + + Description [Useful for graph visualization using tools such as GraphViz: + http://www.graphviz.org/] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteDotNtk( Abc_Ntk_t * pNtk, Vec_Ptr_t * vNodes, Vec_Ptr_t * vNodesShow, char * pFileName, int fGateNames, int fUseReverse ) +{ + FILE * pFile; + Abc_Obj_t * pNode, * pFanin; + char * pSopString; + int LevelMin, LevelMax, fHasCos, Level, i, k, fHasBdds, fCompl; + int Limit = 300; + + assert( Abc_NtkIsStrash(pNtk) || Abc_NtkIsLogic(pNtk) ); + + if ( vNodes->nSize < 1 ) + { + printf( "The set has no nodes. DOT file is not written.\n" ); + return; + } + + if ( vNodes->nSize > Limit ) + { + printf( "The set has more than %d nodes. DOT file is not written.\n", Limit ); + return; + } + + // start the stream + if ( (pFile = fopen( pFileName, "w" )) == NULL ) + { + fprintf( stdout, "Cannot open the intermediate file \"%s\".\n", pFileName ); + return; + } + + // transform logic functions from BDD to SOP + if ( fHasBdds = Abc_NtkIsBddLogic(pNtk) ) + { + if ( !Abc_NtkBddToSop(pNtk, 0) ) + { + printf( "Io_WriteDotNtk(): Converting to SOPs has failed.\n" ); + return; + } + } + + // mark the nodes from the set + Vec_PtrForEachEntry( vNodes, pNode, i ) + pNode->fMarkC = 1; + if ( vNodesShow ) + Vec_PtrForEachEntry( vNodesShow, pNode, i ) + pNode->fMarkB = 1; + + // get the levels of nodes + LevelMax = Abc_NtkLevel( pNtk ); + if ( fUseReverse ) + { + LevelMin = Abc_NtkLevelReverse( pNtk ); + assert( LevelMax == LevelMin ); + Vec_PtrForEachEntry( vNodes, pNode, i ) + if ( Abc_ObjIsNode(pNode) ) + pNode->Level = LevelMax - pNode->Level + 1; + } + + // find the largest and the smallest levels + LevelMin = 10000; + LevelMax = -1; + fHasCos = 0; + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + if ( Abc_ObjIsCo(pNode) ) + { + fHasCos = 1; + continue; + } + if ( LevelMin > (int)pNode->Level ) + LevelMin = pNode->Level; + if ( LevelMax < (int)pNode->Level ) + LevelMax = pNode->Level; + } + + // set the level of the CO nodes + if ( fHasCos ) + { + LevelMax++; + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + if ( Abc_ObjIsCo(pNode) ) + pNode->Level = LevelMax; + } + } + + // write the DOT header + fprintf( pFile, "# %s\n", "Network structure generated by ABC" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "digraph network {\n" ); + fprintf( pFile, "size = \"7.5,10\";\n" ); +// fprintf( pFile, "size = \"10,8.5\";\n" ); +// fprintf( pFile, "size = \"14,11\";\n" ); +// fprintf( pFile, "page = \"8,11\";\n" ); +// fprintf( pFile, "ranksep = 0.5;\n" ); +// fprintf( pFile, "nodesep = 0.5;\n" ); + fprintf( pFile, "center = true;\n" ); +// fprintf( pFile, "orientation = landscape;\n" ); +// fprintf( pFile, "edge [fontsize = 10];\n" ); +// fprintf( pFile, "edge [dir = none];\n" ); + fprintf( pFile, "edge [dir = back];\n" ); + fprintf( pFile, "\n" ); + + // labels on the left of the picture + fprintf( pFile, "{\n" ); + fprintf( pFile, " node [shape = plaintext];\n" ); + fprintf( pFile, " edge [style = invis];\n" ); + fprintf( pFile, " LevelTitle1 [label=\"\"];\n" ); + fprintf( pFile, " LevelTitle2 [label=\"\"];\n" ); + // generate node names with labels + for ( Level = LevelMax; Level >= LevelMin; Level-- ) + { + // the visible node name + fprintf( pFile, " Level%d", Level ); + fprintf( pFile, " [label = " ); + // label name + fprintf( pFile, "\"" ); + fprintf( pFile, "\"" ); + fprintf( pFile, "];\n" ); + } + + // genetate the sequence of visible/invisible nodes to mark levels + fprintf( pFile, " LevelTitle1 -> LevelTitle2 ->" ); + for ( Level = LevelMax; Level >= LevelMin; Level-- ) + { + // the visible node name + fprintf( pFile, " Level%d", Level ); + // the connector + if ( Level != LevelMin ) + fprintf( pFile, " ->" ); + else + fprintf( pFile, ";" ); + } + fprintf( pFile, "\n" ); + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + + // generate title box on top + fprintf( pFile, "{\n" ); + fprintf( pFile, " rank = same;\n" ); + fprintf( pFile, " LevelTitle1;\n" ); + fprintf( pFile, " title1 [shape=plaintext,\n" ); + fprintf( pFile, " fontsize=20,\n" ); + fprintf( pFile, " fontname = \"Times-Roman\",\n" ); + fprintf( pFile, " label=\"" ); + fprintf( pFile, "%s", "Network structure visualized by ABC" ); + fprintf( pFile, "\\n" ); + fprintf( pFile, "Benchmark \\\"%s\\\". ", pNtk->pName ); + fprintf( pFile, "Time was %s. ", Extra_TimeStamp() ); + fprintf( pFile, "\"\n" ); + fprintf( pFile, " ];\n" ); + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + + // generate statistics box + fprintf( pFile, "{\n" ); + fprintf( pFile, " rank = same;\n" ); + fprintf( pFile, " LevelTitle2;\n" ); + fprintf( pFile, " title2 [shape=plaintext,\n" ); + fprintf( pFile, " fontsize=18,\n" ); + fprintf( pFile, " fontname = \"Times-Roman\",\n" ); + fprintf( pFile, " label=\"" ); + if ( Abc_NtkObjNum(pNtk) == Vec_PtrSize(vNodes) ) + fprintf( pFile, "The network contains %d logic nodes and %d latches.", Abc_NtkNodeNum(pNtk), Abc_NtkLatchNum(pNtk) ); + else + fprintf( pFile, "The set contains %d logic nodes and spans %d levels.", Abc_NtkCountLogicNodes(vNodes), LevelMax - LevelMin + 1 ); + fprintf( pFile, "\\n" ); + fprintf( pFile, "\"\n" ); + fprintf( pFile, " ];\n" ); + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + + // generate the POs + if ( fHasCos ) + { + fprintf( pFile, "{\n" ); + fprintf( pFile, " rank = same;\n" ); + // the labeling node of this level + fprintf( pFile, " Level%d;\n", LevelMax ); + // generate the PO nodes + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + if ( !Abc_ObjIsCo(pNode) ) + continue; + fprintf( pFile, " Node%d [label = \"%s%s\"", + pNode->Id, + (Abc_ObjIsBi(pNode)? Abc_ObjName(Abc_ObjFanout0(pNode)):Abc_ObjName(pNode)), + (Abc_ObjIsBi(pNode)? "_in":"") ); + fprintf( pFile, ", shape = %s", (Abc_ObjIsBi(pNode)? "box":"invtriangle") ); + if ( pNode->fMarkB ) + fprintf( pFile, ", style = filled" ); + fprintf( pFile, ", color = coral, fillcolor = coral" ); + fprintf( pFile, "];\n" ); + } + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + } + + // generate nodes of each rank + for ( Level = LevelMax - fHasCos; Level >= LevelMin && Level > 0; Level-- ) + { + fprintf( pFile, "{\n" ); + fprintf( pFile, " rank = same;\n" ); + // the labeling node of this level + fprintf( pFile, " Level%d;\n", Level ); + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + if ( (int)pNode->Level != Level ) + continue; + if ( Abc_ObjFaninNum(pNode) == 0 ) + continue; +// fprintf( pFile, " Node%d [label = \"%d\"", pNode->Id, pNode->Id ); + if ( Abc_NtkIsStrash(pNtk) ) + pSopString = ""; + else if ( Abc_NtkHasMapping(pNtk) && fGateNames ) + pSopString = Mio_GateReadName(pNode->pData); + else if ( Abc_NtkHasMapping(pNtk) ) + pSopString = Abc_NtkPrintSop(Mio_GateReadSop(pNode->pData)); + else + pSopString = Abc_NtkPrintSop(pNode->pData); + fprintf( pFile, " Node%d [label = \"%d\\n%s\"", pNode->Id, pNode->Id, pSopString ); + + fprintf( pFile, ", shape = ellipse" ); + if ( pNode->fMarkB ) + fprintf( pFile, ", style = filled" ); + fprintf( pFile, "];\n" ); + } + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + } + + // generate the PI nodes if any + if ( LevelMin == 0 ) + { + fprintf( pFile, "{\n" ); + fprintf( pFile, " rank = same;\n" ); + // the labeling node of this level + fprintf( pFile, " Level%d;\n", LevelMin ); + // generate the PO nodes + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + if ( !Abc_ObjIsCi(pNode) ) + { + // check if the costant node is present + if ( Abc_ObjFaninNum(pNode) == 0 && Abc_ObjFanoutNum(pNode) > 0 ) + { + fprintf( pFile, " Node%d [label = \"Const%d\"", pNode->Id, Abc_NtkIsStrash(pNode->pNtk) || Abc_NodeIsConst1(pNode) ); + fprintf( pFile, ", shape = ellipse" ); + if ( pNode->fMarkB ) + fprintf( pFile, ", style = filled" ); + fprintf( pFile, ", color = coral, fillcolor = coral" ); + fprintf( pFile, "];\n" ); + } + continue; + } + fprintf( pFile, " Node%d [label = \"%s\"", + pNode->Id, + (Abc_ObjIsBo(pNode)? Abc_ObjName(Abc_ObjFanin0(pNode)):Abc_ObjName(pNode)) ); + fprintf( pFile, ", shape = %s", (Abc_ObjIsBo(pNode)? "box":"triangle") ); + if ( pNode->fMarkB ) + fprintf( pFile, ", style = filled" ); + fprintf( pFile, ", color = coral, fillcolor = coral" ); + fprintf( pFile, "];\n" ); + } + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + } + + // generate invisible edges from the square down + fprintf( pFile, "title1 -> title2 [style = invis];\n" ); + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + if ( (int)pNode->Level != LevelMax ) + continue; + fprintf( pFile, "title2 -> Node%d [style = invis];\n", pNode->Id ); + } + + // generate edges + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + if ( Abc_ObjIsLatch(pNode) ) + continue; + Abc_ObjForEachFanin( pNode, pFanin, k ) + { + if ( Abc_ObjIsLatch(pFanin) ) + continue; + fCompl = 0; + if ( Abc_NtkIsStrash(pNtk) ) + fCompl = Abc_ObjFaninC(pNode, k); + // generate the edge from this node to the next + fprintf( pFile, "Node%d", pNode->Id ); + fprintf( pFile, " -> " ); + fprintf( pFile, "Node%d", pFanin->Id ); + fprintf( pFile, " [style = %s", fCompl? "dotted" : "bold" ); +// fprintf( pFile, ", label = \"%c\"", 'a' + k ); + fprintf( pFile, "]" ); + fprintf( pFile, ";\n" ); + } + } + + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + fclose( pFile ); + + // unmark the nodes from the set + Vec_PtrForEachEntry( vNodes, pNode, i ) + pNode->fMarkC = 0; + if ( vNodesShow ) + Vec_PtrForEachEntry( vNodesShow, pNode, i ) + pNode->fMarkB = 0; + + // convert the network back into BDDs if this is how it was + if ( fHasBdds ) + Abc_NtkSopToBdd(pNtk); +} + + +/**Function************************************************************* + + Synopsis [Writes the graph structure of network for DOT.] + + Description [Useful for graph visualization using tools such as GraphViz: + http://www.graphviz.org/] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteDotSeq( Abc_Ntk_t * pNtk, Vec_Ptr_t * vNodes, Vec_Ptr_t * vNodesShow, char * pFileName, int fGateNames, int fUseReverse ) +{ + FILE * pFile; + Abc_Obj_t * pNode, * pFanin; + char * pSopString; + int LevelMin, LevelMax, fHasCos, Level, i, k, fHasBdds, fCompl; + int Limit = 300; + + assert( Abc_NtkIsStrash(pNtk) || Abc_NtkIsLogic(pNtk) ); + + if ( vNodes->nSize < 1 ) + { + printf( "The set has no nodes. DOT file is not written.\n" ); + return; + } + + if ( vNodes->nSize > Limit ) + { + printf( "The set has more than %d nodes. DOT file is not written.\n", Limit ); + return; + } + + // start the stream + if ( (pFile = fopen( pFileName, "w" )) == NULL ) + { + fprintf( stdout, "Cannot open the intermediate file \"%s\".\n", pFileName ); + return; + } + + // transform logic functions from BDD to SOP + if ( fHasBdds = Abc_NtkIsBddLogic(pNtk) ) + { + if ( !Abc_NtkBddToSop(pNtk, 0) ) + { + printf( "Io_WriteDotNtk(): Converting to SOPs has failed.\n" ); + return; + } + } + + // mark the nodes from the set + Vec_PtrForEachEntry( vNodes, pNode, i ) + pNode->fMarkC = 1; + if ( vNodesShow ) + Vec_PtrForEachEntry( vNodesShow, pNode, i ) + pNode->fMarkB = 1; + + // get the levels of nodes + LevelMax = Abc_NtkLevel( pNtk ); + if ( fUseReverse ) + { + LevelMin = Abc_NtkLevelReverse( pNtk ); + assert( LevelMax == LevelMin ); + Vec_PtrForEachEntry( vNodes, pNode, i ) + if ( Abc_ObjIsNode(pNode) ) + pNode->Level = LevelMax - pNode->Level + 1; + } + + // find the largest and the smallest levels + LevelMin = 10000; + LevelMax = -1; + fHasCos = 0; + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + if ( Abc_ObjIsCo(pNode) ) + { + fHasCos = 1; + continue; + } + if ( LevelMin > (int)pNode->Level ) + LevelMin = pNode->Level; + if ( LevelMax < (int)pNode->Level ) + LevelMax = pNode->Level; + } + + // set the level of the CO nodes + if ( fHasCos ) + { + LevelMax++; + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + if ( Abc_ObjIsCo(pNode) ) + pNode->Level = LevelMax; + } + } + + // write the DOT header + fprintf( pFile, "# %s\n", "Network structure generated by ABC" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "digraph network {\n" ); + fprintf( pFile, "size = \"7.5,10\";\n" ); +// fprintf( pFile, "size = \"10,8.5\";\n" ); +// fprintf( pFile, "size = \"14,11\";\n" ); +// fprintf( pFile, "page = \"8,11\";\n" ); +// fprintf( pFile, "ranksep = 0.5;\n" ); +// fprintf( pFile, "nodesep = 0.5;\n" ); + fprintf( pFile, "center = true;\n" ); +// fprintf( pFile, "orientation = landscape;\n" ); +// fprintf( pFile, "edge [fontsize = 10];\n" ); +// fprintf( pFile, "edge [dir = none];\n" ); + fprintf( pFile, "edge [dir = back];\n" ); + fprintf( pFile, "\n" ); + + // labels on the left of the picture + fprintf( pFile, "{\n" ); + fprintf( pFile, " node [shape = plaintext];\n" ); + fprintf( pFile, " edge [style = invis];\n" ); + fprintf( pFile, " LevelTitle1 [label=\"\"];\n" ); + fprintf( pFile, " LevelTitle2 [label=\"\"];\n" ); + // generate node names with labels + for ( Level = LevelMax; Level >= LevelMin; Level-- ) + { + // the visible node name + fprintf( pFile, " Level%d", Level ); + fprintf( pFile, " [label = " ); + // label name + fprintf( pFile, "\"" ); + fprintf( pFile, "\"" ); + fprintf( pFile, "];\n" ); + } + + // genetate the sequence of visible/invisible nodes to mark levels + fprintf( pFile, " LevelTitle1 -> LevelTitle2 ->" ); + for ( Level = LevelMax; Level >= LevelMin; Level-- ) + { + // the visible node name + fprintf( pFile, " Level%d", Level ); + // the connector + if ( Level != LevelMin ) + fprintf( pFile, " ->" ); + else + fprintf( pFile, ";" ); + } + fprintf( pFile, "\n" ); + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + + // generate title box on top + fprintf( pFile, "{\n" ); + fprintf( pFile, " rank = same;\n" ); + fprintf( pFile, " LevelTitle1;\n" ); + fprintf( pFile, " title1 [shape=plaintext,\n" ); + fprintf( pFile, " fontsize=20,\n" ); + fprintf( pFile, " fontname = \"Times-Roman\",\n" ); + fprintf( pFile, " label=\"" ); + fprintf( pFile, "%s", "Network structure visualized by ABC" ); + fprintf( pFile, "\\n" ); + fprintf( pFile, "Benchmark \\\"%s\\\". ", pNtk->pName ); + fprintf( pFile, "Time was %s. ", Extra_TimeStamp() ); + fprintf( pFile, "\"\n" ); + fprintf( pFile, " ];\n" ); + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + + // generate statistics box + fprintf( pFile, "{\n" ); + fprintf( pFile, " rank = same;\n" ); + fprintf( pFile, " LevelTitle2;\n" ); + fprintf( pFile, " title2 [shape=plaintext,\n" ); + fprintf( pFile, " fontsize=18,\n" ); + fprintf( pFile, " fontname = \"Times-Roman\",\n" ); + fprintf( pFile, " label=\"" ); + if ( Abc_NtkObjNum(pNtk) == Vec_PtrSize(vNodes) ) + fprintf( pFile, "The network contains %d logic nodes and %d latches.", Abc_NtkNodeNum(pNtk), Abc_NtkLatchNum(pNtk) ); + else + fprintf( pFile, "The set contains %d logic nodes and spans %d levels.", Abc_NtkCountLogicNodes(vNodes), LevelMax - LevelMin + 1 ); + fprintf( pFile, "\\n" ); + fprintf( pFile, "\"\n" ); + fprintf( pFile, " ];\n" ); + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + + // generate the POs + if ( fHasCos ) + { + fprintf( pFile, "{\n" ); + fprintf( pFile, " rank = same;\n" ); + // the labeling node of this level + fprintf( pFile, " Level%d;\n", LevelMax ); + // generate the PO nodes + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + if ( !Abc_ObjIsPo(pNode) ) + continue; + fprintf( pFile, " Node%d [label = \"%s\"", pNode->Id, Abc_ObjName(pNode) ); + fprintf( pFile, ", shape = %s", "invtriangle" ); + if ( pNode->fMarkB ) + fprintf( pFile, ", style = filled" ); + fprintf( pFile, ", color = coral, fillcolor = coral" ); + fprintf( pFile, "];\n" ); + } + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + } + + // generate nodes of each rank + for ( Level = LevelMax - fHasCos; Level >= LevelMin && Level > 0; Level-- ) + { + fprintf( pFile, "{\n" ); + fprintf( pFile, " rank = same;\n" ); + // the labeling node of this level + fprintf( pFile, " Level%d;\n", Level ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + if ( (int)pNode->Level != Level ) + continue; +// fprintf( pFile, " Node%d [label = \"%d\"", pNode->Id, pNode->Id ); + if ( Abc_NtkIsStrash(pNtk) ) + pSopString = ""; + else if ( Abc_NtkHasMapping(pNtk) && fGateNames ) + pSopString = Mio_GateReadName(pNode->pData); + else if ( Abc_NtkHasMapping(pNtk) ) + pSopString = Abc_NtkPrintSop(Mio_GateReadSop(pNode->pData)); + else + pSopString = Abc_NtkPrintSop(pNode->pData); + fprintf( pFile, " Node%d [label = \"%d\\n%s\"", pNode->Id, pNode->Id, pSopString ); + + fprintf( pFile, ", shape = ellipse" ); + if ( pNode->fMarkB ) + fprintf( pFile, ", style = filled" ); + fprintf( pFile, "];\n" ); + } + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + } + + // generate the PI nodes if any + if ( LevelMin == 0 ) + { + fprintf( pFile, "{\n" ); + fprintf( pFile, " rank = same;\n" ); + // the labeling node of this level + fprintf( pFile, " Level%d;\n", LevelMin ); + // generate the PO nodes + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + if ( pNode->Level > 0 ) + continue; + if ( !Abc_ObjIsPi(pNode) ) + { + // check if the costant node is present + if ( Abc_ObjFaninNum(pNode) == 0 && Abc_ObjFanoutNum(pNode) > 0 ) + { + fprintf( pFile, " Node%d [label = \"Const1\"", pNode->Id ); + fprintf( pFile, ", shape = ellipse" ); + if ( pNode->fMarkB ) + fprintf( pFile, ", style = filled" ); + fprintf( pFile, ", color = coral, fillcolor = coral" ); + fprintf( pFile, "];\n" ); + } + continue; + } + fprintf( pFile, " Node%d [label = \"%s\"", pNode->Id, Abc_ObjName(pNode) ); + fprintf( pFile, ", shape = %s", "triangle" ); + if ( pNode->fMarkB ) + fprintf( pFile, ", style = filled" ); + fprintf( pFile, ", color = coral, fillcolor = coral" ); + fprintf( pFile, "];\n" ); + } + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + } + +// fprintf( pFile, "{\n" ); + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + if ( !Abc_ObjIsLatch(pNode) ) + continue; + fprintf( pFile, "Node%d [label = \"%s\"", pNode->Id, Abc_ObjName(pNode) ); + fprintf( pFile, ", shape = box" ); + if ( pNode->fMarkB ) + fprintf( pFile, ", style = filled" ); + fprintf( pFile, ", color = coral, fillcolor = coral" ); + fprintf( pFile, "];\n" ); + } +// fprintf( pFile, "}" ); +// fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + + // generate invisible edges from the square down + fprintf( pFile, "title1 -> title2 [style = invis];\n" ); + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + if ( (int)pNode->Level != LevelMax ) + continue; + if ( !Abc_ObjIsPo(pNode) ) + continue; + fprintf( pFile, "title2 -> Node%d [style = invis];\n", pNode->Id ); + } + + // generate edges + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + if ( Abc_ObjIsBi(pNode) || Abc_ObjIsBo(pNode) ) + continue; + Abc_ObjForEachFanin( pNode, pFanin, k ) + { + fCompl = 0; + if ( Abc_NtkIsStrash(pNtk) ) + { + if ( Abc_ObjIsBi(pFanin) ) + fCompl = Abc_ObjFaninC(pFanin, k); + else + fCompl = Abc_ObjFaninC(pNode, k); + } + if ( Abc_ObjIsBi(pFanin) || Abc_ObjIsBo(pFanin) ) + pFanin = Abc_ObjFanin0(pFanin); + if ( Abc_ObjIsBi(pFanin) || Abc_ObjIsBo(pFanin) ) + pFanin = Abc_ObjFanin0(pFanin); + if ( !pFanin->fMarkC ) + continue; + + // generate the edge from this node to the next + fprintf( pFile, "Node%d", pNode->Id ); + fprintf( pFile, " -> " ); + fprintf( pFile, "Node%d", pFanin->Id ); + fprintf( pFile, " [style = %s", fCompl? "dotted" : "bold" ); +// fprintf( pFile, ", label = \"%c\"", 'a' + k ); + fprintf( pFile, "]" ); + fprintf( pFile, ";\n" ); + } + } + + fprintf( pFile, "}" ); + fprintf( pFile, "\n" ); + fprintf( pFile, "\n" ); + fclose( pFile ); + + // unmark the nodes from the set + Vec_PtrForEachEntry( vNodes, pNode, i ) + pNode->fMarkC = 0; + if ( vNodesShow ) + Vec_PtrForEachEntry( vNodesShow, pNode, i ) + pNode->fMarkB = 0; + + // convert the network back into BDDs if this is how it was + if ( fHasBdds ) + Abc_NtkSopToBdd(pNtk); +} + + +/**Function************************************************************* + + Synopsis [Computes the printable SOP form.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_NtkPrintSop( char * pSop ) +{ + static char Buffer[1000]; + char * pGet, * pSet; + pSet = Buffer; + for ( pGet = pSop; *pGet; pGet++ ) + { + if ( *pGet == '\n' ) + { + *pSet++ = '\\'; + *pSet++ = 'n'; + } + else + *pSet++ = *pGet; + } + *(pSet-2) = 0; + return Buffer; +} + +/**Function************************************************************* + + Synopsis [Computes the printable SOP form.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkCountLogicNodes( Vec_Ptr_t * vNodes ) +{ + Abc_Obj_t * pObj; + int i, Counter = 0; + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + if ( !Abc_ObjIsNode(pObj) ) + continue; + if ( Abc_ObjFaninNum(pObj) == 0 && Abc_ObjFanoutNum(pObj) == 0 ) + continue; + Counter ++; + } + return Counter; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/io/ioWriteEqn.c b/abc_with_bb_support/src/base/io/ioWriteEqn.c new file mode 100644 index 000000000..d95e88aa9 --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioWriteEqn.c @@ -0,0 +1,252 @@ +/**CFile**************************************************************** + + FileName [ioWriteEqn.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures to write equation representation of the network.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: ioWriteEqn.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Io_NtkWriteEqnOne( FILE * pFile, Abc_Ntk_t * pNtk ); +static void Io_NtkWriteEqnCis( FILE * pFile, Abc_Ntk_t * pNtk ); +static void Io_NtkWriteEqnCos( FILE * pFile, Abc_Ntk_t * pNtk ); +static int Io_NtkWriteEqnCheck( Abc_Ntk_t * pNtk ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Writes the logic network in the equation format.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteEqn( Abc_Ntk_t * pNtk, char * pFileName ) +{ + FILE * pFile; + + assert( Abc_NtkIsAigNetlist(pNtk) ); + if ( Abc_NtkLatchNum(pNtk) > 0 ) + printf( "Warning: only combinational portion is being written.\n" ); + + // check that the names are fine for the EQN format + if ( !Io_NtkWriteEqnCheck(pNtk) ) + return; + + // start the output stream + pFile = fopen( pFileName, "w" ); + if ( pFile == NULL ) + { + fprintf( stdout, "Io_WriteEqn(): Cannot open the output file \"%s\".\n", pFileName ); + return; + } + fprintf( pFile, "# Equations for \"%s\" written by ABC on %s\n", pNtk->pName, Extra_TimeStamp() ); + + // write the equations for the network + Io_NtkWriteEqnOne( pFile, pNtk ); + fprintf( pFile, "\n" ); + fclose( pFile ); +} + +/**Function************************************************************* + + Synopsis [Write one network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWriteEqnOne( FILE * pFile, Abc_Ntk_t * pNtk ) +{ + Vec_Vec_t * vLevels; + ProgressBar * pProgress; + Abc_Obj_t * pNode, * pFanin; + int i, k; + + // write the PIs + fprintf( pFile, "INORDER =" ); + Io_NtkWriteEqnCis( pFile, pNtk ); + fprintf( pFile, ";\n" ); + + // write the POs + fprintf( pFile, "OUTORDER =" ); + Io_NtkWriteEqnCos( pFile, pNtk ); + fprintf( pFile, ";\n" ); + + // write each internal node + vLevels = Vec_VecAlloc( 10 ); + pProgress = Extra_ProgressBarStart( stdout, Abc_NtkObjNumMax(pNtk) ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + fprintf( pFile, "%s = ", Abc_ObjName(Abc_ObjFanout0(pNode)) ); + // set the input names + Abc_ObjForEachFanin( pNode, pFanin, k ) + Hop_IthVar(pNtk->pManFunc, k)->pData = Abc_ObjName(pFanin); + // write the formula + Hop_ObjPrintEqn( pFile, pNode->pData, vLevels, 0 ); + fprintf( pFile, ";\n" ); + } + Extra_ProgressBarStop( pProgress ); + Vec_VecFree( vLevels ); +} + + +/**Function************************************************************* + + Synopsis [Writes the primary input list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWriteEqnCis( FILE * pFile, Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pTerm, * pNet; + int LineLength; + int AddedLength; + int NameCounter; + int i; + + LineLength = 9; + NameCounter = 0; + + Abc_NtkForEachCi( pNtk, pTerm, i ) + { + pNet = Abc_ObjFanout0(pTerm); + // get the line length after this name is written + AddedLength = strlen(Abc_ObjName(pNet)) + 1; + if ( NameCounter && LineLength + AddedLength + 3 > IO_WRITE_LINE_LENGTH ) + { // write the line extender + fprintf( pFile, " \n" ); + // reset the line length + LineLength = 0; + NameCounter = 0; + } + fprintf( pFile, " %s", Abc_ObjName(pNet) ); + LineLength += AddedLength; + NameCounter++; + } +} + +/**Function************************************************************* + + Synopsis [Writes the primary input list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_NtkWriteEqnCos( FILE * pFile, Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pTerm, * pNet; + int LineLength; + int AddedLength; + int NameCounter; + int i; + + LineLength = 10; + NameCounter = 0; + + Abc_NtkForEachCo( pNtk, pTerm, i ) + { + pNet = Abc_ObjFanin0(pTerm); + // get the line length after this name is written + AddedLength = strlen(Abc_ObjName(pNet)) + 1; + if ( NameCounter && LineLength + AddedLength + 3 > IO_WRITE_LINE_LENGTH ) + { // write the line extender + fprintf( pFile, " \n" ); + // reset the line length + LineLength = 0; + NameCounter = 0; + } + fprintf( pFile, " %s", Abc_ObjName(pNet) ); + LineLength += AddedLength; + NameCounter++; + } +} + +/**Function************************************************************* + + Synopsis [Make sure the network does not have offending names.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_NtkWriteEqnCheck( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + char * pName; + int i, k, Length; + int RetValue = 1; + + // make sure the network does not have proper names, such as "0" or "1" or containing parantheses + Abc_NtkForEachObj( pNtk, pObj, i ) + { + pName = Nm_ManFindNameById(pNtk->pManName, i); + if ( pName == NULL ) + continue; + Length = strlen(pName); + if ( pName[0] == '0' || pName[0] == '1' ) + { + RetValue = 0; + break; + } + for ( k = 0; k < Length; k++ ) + if ( pName[k] == '(' || pName[k] == ')' || pName[k] == '!' || pName[k] == '*' || pName[k] == '+' ) + { + RetValue = 0; + break; + } + if ( k < Length ) + break; + } + if ( RetValue == 0 ) + { + printf( "The network cannot be written in the EQN format because object %d has name \"%s\".\n", i, pName ); + printf( "Consider renaming the objects using command \"short_names\" and trying again.\n" ); + } + return RetValue; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/io/ioWriteGml.c b/abc_with_bb_support/src/base/io/ioWriteGml.c new file mode 100644 index 000000000..20ceceed3 --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioWriteGml.c @@ -0,0 +1,116 @@ +/**CFile**************************************************************** + + FileName [ioWriteGml.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures to write the graph structure of AIG in GML.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: ioWriteGml.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Writes the graph structure of AIG in GML.] + + Description [Useful for graph visualization using tools such as yEd: + http://www.yworks.com/] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteGml( Abc_Ntk_t * pNtk, char * pFileName ) +{ + FILE * pFile; + Abc_Obj_t * pObj, * pFanin; + int i, k; + + assert( Abc_NtkIsStrash(pNtk) || Abc_NtkIsLogic(pNtk) ); + + // start the output stream + pFile = fopen( pFileName, "w" ); + if ( pFile == NULL ) + { + fprintf( stdout, "Io_WriteGml(): Cannot open the output file \"%s\".\n", pFileName ); + return; + } + fprintf( pFile, "# GML for \"%s\" written by ABC on %s\n", pNtk->pName, Extra_TimeStamp() ); + fprintf( pFile, "graph [\n" ); + + // output the POs + fprintf( pFile, "\n" ); + Abc_NtkForEachPo( pNtk, pObj, i ) + { + fprintf( pFile, " node [ id %5d label \"%s\"\n", pObj->Id, Abc_ObjName(pObj) ); + fprintf( pFile, " graphics [ type \"triangle\" fill \"#00FFFF\" ]\n" ); // blue + fprintf( pFile, " ]\n" ); + } + // output the PIs + fprintf( pFile, "\n" ); + Abc_NtkForEachPi( pNtk, pObj, i ) + { + fprintf( pFile, " node [ id %5d label \"%s\"\n", pObj->Id, Abc_ObjName(pObj) ); + fprintf( pFile, " graphics [ type \"triangle\" fill \"#00FF00\" ]\n" ); // green + fprintf( pFile, " ]\n" ); + } + // output the latches + fprintf( pFile, "\n" ); + Abc_NtkForEachLatch( pNtk, pObj, i ) + { + fprintf( pFile, " node [ id %5d label \"%s\"\n", pObj->Id, Abc_ObjName(pObj) ); + fprintf( pFile, " graphics [ type \"rectangle\" fill \"#FF0000\" ]\n" ); // red + fprintf( pFile, " ]\n" ); + } + // output the nodes + fprintf( pFile, "\n" ); + Abc_NtkForEachNode( pNtk, pObj, i ) + { + fprintf( pFile, " node [ id %5d label \"%s\"\n", pObj->Id, Abc_ObjName(pObj) ); + fprintf( pFile, " graphics [ type \"ellipse\" fill \"#CCCCFF\" ]\n" ); // grey + fprintf( pFile, " ]\n" ); + } + + // output the edges + fprintf( pFile, "\n" ); + Abc_NtkForEachObj( pNtk, pObj, i ) + { + Abc_ObjForEachFanin( pObj, pFanin, k ) + { + fprintf( pFile, " edge [ source %5d target %5d\n", pObj->Id, pFanin->Id ); + fprintf( pFile, " graphics [ type \"line\" arrow \"first\" ]\n" ); + fprintf( pFile, " ]\n" ); + } + } + + fprintf( pFile, "]\n" ); + fprintf( pFile, "\n" ); + fclose( pFile ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/io/ioWriteList.c b/abc_with_bb_support/src/base/io/ioWriteList.c new file mode 100644 index 000000000..76e9c4e52 --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioWriteList.c @@ -0,0 +1,288 @@ +/**CFile**************************************************************** + + FileName [ioWriteList.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures to write the graph structure of sequential AIG.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: ioWriteList.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" + +/* +-------- Original Message -------- +Subject: Re: abc release and retiming +Date: Sun, 13 Nov 2005 20:31:18 -0500 (EST) +From: Luca Carloni +To: Alan Mishchenko + +Alan, + +My graph-representation file format is based on an adjacency list +representation and is indeed quite simple, in fact maybe too simple... I +used it in order to reason on relatively small weighed direct graphs. I +simply list all vertices, one per line and for each vertex "V_source" I +list all vertices that are "sinks" with respect to it, i.e. such that +there is a distinct arc between "V_source" and each of them (in +paranthesis I list the name of the edge and its weight (number of latency +on that path). For instance, if you look at the following graph, you have +that vertex "v_5" is connected to vertex "v_6" through a directed arc +called "v_5_to_v_6" whose latency is equal to 3, i.e. there are three +flip-flops on this arc. Still, notice that I sometime interpret the graph +also as the representation of a LIS where each node corresponds to a +shell encapsulating a sequential core module (i.e. a module which does not +contain any combinational path between its inputs and its outputs). With +this representation an arc of latency 3 is interpreted as a wire where two +relay stations have been inserted in addition to the flip-flop terminating +the output of the core module. + +Finally, notice that the name of the arc does not necessarily have to be +"v_5_to_v_6", but it could have been something like "arc_222" or "xyz" as +long as it is a unique name in the graph. + +Thanks, +Luca + +Example of graph representation +----------------------------------------------------------------------------- +v_5 > v_6 ([v_5_to_v_6] = 3), v_12 ([v_5_to_v_12] = 2). +v_2 > v_4 ([v_2_to_v_4] = 1), v_10_s0 ([v_2_to_v_10_s0] = 6), v_12 ([v_2_to_v_12] = 3). +v_9 > v_10_s0 ([v_9_to_v_10_s0] = 5), v_12 ([v_9_to_v_12] = 2). +v_12 > v_13 ([v_12_to_v_13] = 5). +v_13 > v_14 ([v_13_to_v_14] = 1). +v_6 > v_7 ([v_6_to_v_7] = 2). +v_4 > v_5 ([v_4_to_v_5] = 2). +v_1 > v_2 ([v_1_to_v_2] = 1). +v_7 > v_8 ([v_7_to_v_8] = 2). +t > . +v_14 > t ([v_14_to_t] = 1), v_5 ([v_14_to_v_5] = 1). +v_8 > v_9 ([v_8_to_v_9] = 2). +s > v_1 ([s_to_v_1] = 1). +v_10_s0 > v_10_s1 ([v_10_s0_to_v_10_s1] = 1). +v_10_s1 > v_4 ([v_10_s1__v_4] = 1), v_8 ([v_10_s1__v_8] = 1). +----------------------------------------------------------------------------- +*/ + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Io_WriteListEdge( FILE * pFile, Abc_Obj_t * pObj ); +static void Io_WriteListHost( FILE * pFile, Abc_Ntk_t * pNtk ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Writes the adjacency list for a sequential AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteList( Abc_Ntk_t * pNtk, char * pFileName, int fUseHost ) +{ + FILE * pFile; + Abc_Obj_t * pObj; + int i; + +// assert( Abc_NtkIsSeq(pNtk) ); + + // start the output stream + pFile = fopen( pFileName, "w" ); + if ( pFile == NULL ) + { + fprintf( stdout, "Io_WriteList(): Cannot open the output file \"%s\".\n", pFileName ); + return; + } + + fprintf( pFile, "# Adjacency list for sequential AIG \"%s\"\n", pNtk->pName ); + fprintf( pFile, "# written by ABC on %s\n", Extra_TimeStamp() ); + + // write the constant node + if ( Abc_ObjFanoutNum( Abc_AigConst1(pNtk) ) > 0 ) + Io_WriteListEdge( pFile, Abc_AigConst1(pNtk) ); + + // write the PI edges + Abc_NtkForEachPi( pNtk, pObj, i ) + Io_WriteListEdge( pFile, pObj ); + + // write the internal nodes + Abc_AigForEachAnd( pNtk, pObj, i ) + Io_WriteListEdge( pFile, pObj ); + + // write the host node + if ( fUseHost ) + Io_WriteListHost( pFile, pNtk ); + else + Abc_NtkForEachPo( pNtk, pObj, i ) + Io_WriteListEdge( pFile, pObj ); + + fprintf( pFile, "\n" ); + fclose( pFile ); +} + +/**Function************************************************************* + + Synopsis [Writes the adjacency list for one edge in a sequential AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteListEdge( FILE * pFile, Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanout; + int i; + fprintf( pFile, "%-10s > ", Abc_ObjName(pObj) ); + Abc_ObjForEachFanout( pObj, pFanout, i ) + { + fprintf( pFile, " %s", Abc_ObjName(pFanout) ); + fprintf( pFile, " ([%s_to_", Abc_ObjName(pObj) ); +// fprintf( pFile, "%s] = %d)", Abc_ObjName(pFanout), Seq_ObjFanoutL(pObj, pFanout) ); + fprintf( pFile, "%s] = %d)", Abc_ObjName(pFanout), 0 ); + if ( i != Abc_ObjFanoutNum(pObj) - 1 ) + fprintf( pFile, "," ); + } + fprintf( pFile, "." ); + fprintf( pFile, "\n" ); +} + +/**Function************************************************************* + + Synopsis [Writes the adjacency list for one edge in a sequential AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteListHost( FILE * pFile, Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i; + + Abc_NtkForEachPo( pNtk, pObj, i ) + { + fprintf( pFile, "%-10s > ", Abc_ObjName(pObj) ); + fprintf( pFile, " %s ([%s_to_%s] = %d)", "HOST", Abc_ObjName(pObj), "HOST", 0 ); + fprintf( pFile, "." ); + fprintf( pFile, "\n" ); + } + + fprintf( pFile, "%-10s > ", "HOST" ); + Abc_NtkForEachPi( pNtk, pObj, i ) + { + fprintf( pFile, " %s", Abc_ObjName(pObj) ); + fprintf( pFile, " ([%s_to_%s] = %d)", "HOST", Abc_ObjName(pObj), 0 ); + if ( i != Abc_NtkPiNum(pNtk) - 1 ) + fprintf( pFile, "," ); + } + fprintf( pFile, "." ); + fprintf( pFile, "\n" ); +} + + +/**Function************************************************************* + + Synopsis [Writes the adjacency list for a sequential AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteCellNet( Abc_Ntk_t * pNtk, char * pFileName ) +{ + FILE * pFile; + Abc_Obj_t * pObj, * pFanout; + int i, k; + + assert( Abc_NtkIsLogic(pNtk) ); + + // start the output stream + pFile = fopen( pFileName, "w" ); + if ( pFile == NULL ) + { + fprintf( stdout, "Io_WriteCellNet(): Cannot open the output file \"%s\".\n", pFileName ); + return; + } + + fprintf( pFile, "# CellNet file for network \"%s\" written by ABC on %s\n", pNtk->pName, Extra_TimeStamp() ); + + // the only tricky part with writing is handling latches: + // each latch comes with (a) single-input latch-input node, (b) latch proper, (c) single-input latch-output node + // we arbitrarily decide to use the interger ID of the latch-input node to represent the latch in the file + // (this ID is used for both the cell and the net driven by that cell) + + // write the PIs + Abc_NtkForEachPi( pNtk, pObj, i ) + fprintf( pFile, "cell %d is 0\n", pObj->Id ); + // write the POs + Abc_NtkForEachPo( pNtk, pObj, i ) + fprintf( pFile, "cell %d is 1\n", pObj->Id ); + // write the latches (use the ID of latch input) + Abc_NtkForEachLatch( pNtk, pObj, i ) + fprintf( pFile, "cell %d is 2\n", Abc_ObjFanin0(pObj)->Id ); + // write the logic nodes + Abc_NtkForEachNode( pNtk, pObj, i ) + fprintf( pFile, "cell %d is %d\n", pObj->Id, 3+Abc_ObjFaninNum(pObj) ); + + // write the nets driven by PIs + Abc_NtkForEachPi( pNtk, pObj, i ) + { + fprintf( pFile, "net %d %d 0", pObj->Id, pObj->Id ); + Abc_ObjForEachFanout( pObj, pFanout, k ) + fprintf( pFile, " %d %d", pFanout->Id, 1 + Abc_ObjFanoutFaninNum(pFanout, pObj) ); + fprintf( pFile, "\n" ); + } + // write the nets driven by latches + Abc_NtkForEachLatch( pNtk, pObj, i ) + { + fprintf( pFile, "net %d %d 0", Abc_ObjFanin0(pObj)->Id, Abc_ObjFanin0(pObj)->Id ); + pObj = Abc_ObjFanout0(pObj); + Abc_ObjForEachFanout( pObj, pFanout, k ) + fprintf( pFile, " %d %d", pFanout->Id, 1 + Abc_ObjFanoutFaninNum(pFanout, pObj) ); + fprintf( pFile, "\n" ); + } + // write the nets driven by nodes + Abc_NtkForEachNode( pNtk, pObj, i ) + { + fprintf( pFile, "net %d %d 0", pObj->Id, pObj->Id ); + Abc_ObjForEachFanout( pObj, pFanout, k ) + fprintf( pFile, " %d %d", pFanout->Id, 1 + Abc_ObjFanoutFaninNum(pFanout, pObj) ); + fprintf( pFile, "\n" ); + } + + fprintf( pFile, "\n" ); + fclose( pFile ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/io/ioWritePla.c b/abc_with_bb_support/src/base/io/ioWritePla.c new file mode 100644 index 000000000..6a4afa79d --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioWritePla.c @@ -0,0 +1,197 @@ +/**CFile**************************************************************** + + FileName [ioWritePla.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures to write the network in BENCH format.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: ioWritePla.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Io_WritePlaOne( FILE * pFile, Abc_Ntk_t * pNtk ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Writes the network in PLA format.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_WritePla( Abc_Ntk_t * pNtk, char * pFileName ) +{ + Abc_Ntk_t * pExdc; + FILE * pFile; + + assert( Abc_NtkIsSopNetlist(pNtk) ); + assert( Abc_NtkLevel(pNtk) == 1 ); + + pFile = fopen( pFileName, "w" ); + if ( pFile == NULL ) + { + fprintf( stdout, "Io_WritePla(): Cannot open the output file.\n" ); + return 0; + } + fprintf( pFile, "# Benchmark \"%s\" written by ABC on %s\n", pNtk->pName, Extra_TimeStamp() ); + // write the network + Io_WritePlaOne( pFile, pNtk ); + // write EXDC network if it exists + pExdc = Abc_NtkExdc( pNtk ); + if ( pExdc ) + printf( "Io_WritePla: EXDC is not written (warning).\n" ); + // finalize the file + fclose( pFile ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Writes the network in PLA format.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_WritePlaOne( FILE * pFile, Abc_Ntk_t * pNtk ) +{ + ProgressBar * pProgress; + Abc_Obj_t * pNode, * pFanin, * pDriver; + char * pCubeIn, * pCubeOut, * pCube; + int i, k, nProducts, nInputs, nOutputs, nFanins; + + nProducts = 0; + Abc_NtkForEachCo( pNtk, pNode, i ) + { + pDriver = Abc_ObjFanin0Ntk( Abc_ObjFanin0(pNode) ); + if ( !Abc_ObjIsNode(pDriver) ) + { + nProducts++; + continue; + } + if ( Abc_NodeIsConst(pDriver) ) + { + if ( Abc_NodeIsConst1(pDriver) ) + nProducts++; + continue; + } + nProducts += Abc_SopGetCubeNum(pDriver->pData); + } + + // collect the parameters + nInputs = Abc_NtkCiNum(pNtk); + nOutputs = Abc_NtkCoNum(pNtk); + pCubeIn = ALLOC( char, nInputs + 1 ); + pCubeOut = ALLOC( char, nOutputs + 1 ); + memset( pCubeIn, '-', nInputs ); pCubeIn[nInputs] = 0; + memset( pCubeOut, '0', nOutputs ); pCubeOut[nOutputs] = 0; + + // write the header + fprintf( pFile, ".i %d\n", nInputs ); + fprintf( pFile, ".o %d\n", nOutputs ); + fprintf( pFile, ".ilb" ); + Abc_NtkForEachCi( pNtk, pNode, i ) + fprintf( pFile, " %s", Abc_ObjName(Abc_ObjFanout0(pNode)) ); + fprintf( pFile, "\n" ); + fprintf( pFile, ".ob" ); + Abc_NtkForEachCo( pNtk, pNode, i ) + fprintf( pFile, " %s", Abc_ObjName(Abc_ObjFanin0(pNode)) ); + fprintf( pFile, "\n" ); + fprintf( pFile, ".p %d\n", nProducts ); + + // mark the CI nodes + Abc_NtkForEachCi( pNtk, pNode, i ) + pNode->pCopy = (Abc_Obj_t *)i; + + // write the cubes + pProgress = Extra_ProgressBarStart( stdout, nOutputs ); + Abc_NtkForEachCo( pNtk, pNode, i ) + { + // prepare the output cube + if ( i - 1 >= 0 ) + pCubeOut[i-1] = '0'; + pCubeOut[i] = '1'; + + // consider special cases of nodes + pDriver = Abc_ObjFanin0Ntk( Abc_ObjFanin0(pNode) ); + if ( !Abc_ObjIsNode(pDriver) ) + { + assert( Abc_ObjIsCi(pDriver) ); + pCubeIn[(int)pDriver->pCopy] = '1' - Abc_ObjFaninC0(pNode); + fprintf( pFile, "%s %s\n", pCubeIn, pCubeOut ); + pCubeIn[(int)pDriver->pCopy] = '-'; + continue; + } + if ( Abc_NodeIsConst(pDriver) ) + { + if ( Abc_NodeIsConst1(pDriver) ) + fprintf( pFile, "%s %s\n", pCubeIn, pCubeOut ); + continue; + } + + // make sure the cover is not complemented + assert( !Abc_SopIsComplement( pDriver->pData ) ); + + // write the cubes + nFanins = Abc_ObjFaninNum(pDriver); + Abc_SopForEachCube( pDriver->pData, nFanins, pCube ) + { + Abc_ObjForEachFanin( pDriver, pFanin, k ) + { + pFanin = Abc_ObjFanin0Ntk(pFanin); + assert( (int)pFanin->pCopy < nInputs ); + pCubeIn[(int)pFanin->pCopy] = pCube[k]; + } + fprintf( pFile, "%s %s\n", pCubeIn, pCubeOut ); + } + // clean the cube for future writing + Abc_ObjForEachFanin( pDriver, pFanin, k ) + { + pFanin = Abc_ObjFanin0Ntk(pFanin); + assert( Abc_ObjIsCi(pFanin) ); + pCubeIn[(int)pFanin->pCopy] = '-'; + } + Extra_ProgressBarUpdate( pProgress, i, NULL ); + } + Extra_ProgressBarStop( pProgress ); + fprintf( pFile, ".e\n" ); + + // clean the CI nodes + Abc_NtkForEachCi( pNtk, pNode, i ) + pNode->pCopy = NULL; + free( pCubeIn ); + free( pCubeOut ); + return 1; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/io/ioWriteVerilog.c b/abc_with_bb_support/src/base/io/ioWriteVerilog.c new file mode 100644 index 000000000..1e062d809 --- /dev/null +++ b/abc_with_bb_support/src/base/io/ioWriteVerilog.c @@ -0,0 +1,636 @@ +/**CFile**************************************************************** + + FileName [ioWriteVerilog.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Command processing package.] + + Synopsis [Procedures to output a special subset of Verilog.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: ioWriteVerilog.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "io.h" +#include "main.h" +#include "mio.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Io_WriteVerilogInt( FILE * pFile, Abc_Ntk_t * pNtk ); +static void Io_WriteVerilogPis( FILE * pFile, Abc_Ntk_t * pNtk, int Start ); +static void Io_WriteVerilogPos( FILE * pFile, Abc_Ntk_t * pNtk, int Start ); +static void Io_WriteVerilogWires( FILE * pFile, Abc_Ntk_t * pNtk, int Start ); +static void Io_WriteVerilogRegs( FILE * pFile, Abc_Ntk_t * pNtk, int Start ); +static void Io_WriteVerilogLatches( FILE * pFile, Abc_Ntk_t * pNtk ); +static void Io_WriteVerilogObjects( FILE * pFile, Abc_Ntk_t * pNtk ); +static int Io_WriteVerilogWiresCount( Abc_Ntk_t * pNtk ); +static char * Io_WriteVerilogGetName( char * pName ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Write verilog.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteVerilog( Abc_Ntk_t * pNtk, char * pFileName ) +{ + Abc_Ntk_t * pNetlist; + FILE * pFile; + int i; + // can only write nodes represented using local AIGs + if ( !Abc_NtkIsAigNetlist(pNtk) && !Abc_NtkIsMappedNetlist(pNtk) ) + { + printf( "Io_WriteVerilog(): Can produce Verilog for mapped or AIG netlists only.\n" ); + return; + } + // start the output stream + pFile = fopen( pFileName, "w" ); + if ( pFile == NULL ) + { + fprintf( stdout, "Io_WriteVerilog(): Cannot open the output file \"%s\".\n", pFileName ); + return; + } + + // write the equations for the network + fprintf( pFile, "// Benchmark \"%s\" written by ABC on %s\n", pNtk->pName, Extra_TimeStamp() ); + fprintf( pFile, "\n" ); + + // write modules + if ( pNtk->pDesign ) + { + // write the network first + Io_WriteVerilogInt( pFile, pNtk ); + // write other things + Vec_PtrForEachEntry( pNtk->pDesign->vModules, pNetlist, i ) + { + assert( Abc_NtkIsNetlist(pNetlist) ); + if ( pNetlist == pNtk ) + continue; + fprintf( pFile, "\n" ); + Io_WriteVerilogInt( pFile, pNetlist ); + } + } + else + { + Io_WriteVerilogInt( pFile, pNtk ); + } + + fprintf( pFile, "\n" ); + fclose( pFile ); +} + +/**Function************************************************************* + + Synopsis [Writes verilog.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteVerilogInt( FILE * pFile, Abc_Ntk_t * pNtk ) +{ + // write inputs and outputs +// fprintf( pFile, "module %s ( gclk,\n ", Abc_NtkName(pNtk) ); + fprintf( pFile, "module %s ( ", Abc_NtkName(pNtk) ); + // add the clock signal if it does not exist + if ( Abc_NtkLatchNum(pNtk) > 0 && Nm_ManFindIdByName(pNtk->pManName, "clock", ABC_OBJ_PI) == -1 ) + fprintf( pFile, "clock, " ); + // write other primary inputs + fprintf( pFile, "\n " ); + if ( Abc_NtkPiNum(pNtk) > 0 ) + { + Io_WriteVerilogPis( pFile, pNtk, 3 ); + fprintf( pFile, ",\n " ); + } + if ( Abc_NtkPoNum(pNtk) > 0 ) + Io_WriteVerilogPos( pFile, pNtk, 3 ); + fprintf( pFile, " );\n" ); + // write inputs, outputs, registers, and wires + if ( Abc_NtkPiNum(pNtk) > 0 ) + { +// fprintf( pFile, " input gclk," ); + fprintf( pFile, " input " ); + Io_WriteVerilogPis( pFile, pNtk, 10 ); + fprintf( pFile, ";\n" ); + } + if ( Abc_NtkPoNum(pNtk) > 0 ) + { + fprintf( pFile, " output" ); + Io_WriteVerilogPos( pFile, pNtk, 5 ); + fprintf( pFile, ";\n" ); + } + // if this is not a blackbox, write internal signals + if ( !Abc_NtkHasBlackbox(pNtk) ) + { + if ( Abc_NtkLatchNum(pNtk) > 0 ) + { + fprintf( pFile, " reg" ); + Io_WriteVerilogRegs( pFile, pNtk, 4 ); + fprintf( pFile, ";\n" ); + } + if ( Io_WriteVerilogWiresCount(pNtk) > 0 ) + { + fprintf( pFile, " wire" ); + Io_WriteVerilogWires( pFile, pNtk, 4 ); + fprintf( pFile, ";\n" ); + } + // write nodes + Io_WriteVerilogObjects( pFile, pNtk ); + // write registers + if ( Abc_NtkLatchNum(pNtk) > 0 ) + Io_WriteVerilogLatches( pFile, pNtk ); + } + // finalize the file + fprintf( pFile, "endmodule\n\n" ); +} + +/**Function************************************************************* + + Synopsis [Writes the primary inputs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteVerilogPis( FILE * pFile, Abc_Ntk_t * pNtk, int Start ) +{ + Abc_Obj_t * pTerm, * pNet; + int LineLength; + int AddedLength; + int NameCounter; + int i; + + LineLength = Start; + NameCounter = 0; + Abc_NtkForEachPi( pNtk, pTerm, i ) + { + pNet = Abc_ObjFanout0(pTerm); + // get the line length after this name is written + AddedLength = strlen(Io_WriteVerilogGetName(Abc_ObjName(pNet))) + 2; + if ( NameCounter && LineLength + AddedLength + 3 > IO_WRITE_LINE_LENGTH ) + { // write the line extender + fprintf( pFile, "\n " ); + // reset the line length + LineLength = 3; + NameCounter = 0; + } + fprintf( pFile, " %s%s", Io_WriteVerilogGetName(Abc_ObjName(pNet)), (i==Abc_NtkPiNum(pNtk)-1)? "" : "," ); + LineLength += AddedLength; + NameCounter++; + } +} + +/**Function************************************************************* + + Synopsis [Writes the primary outputs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteVerilogPos( FILE * pFile, Abc_Ntk_t * pNtk, int Start ) +{ + Abc_Obj_t * pTerm, * pNet, * pSkip; + int LineLength; + int AddedLength; + int NameCounter; + int i; + int nskip; + + pSkip = 0; + nskip = 0; + + LineLength = Start; + NameCounter = 0; + Abc_NtkForEachPo( pNtk, pTerm, i ) + { + pNet = Abc_ObjFanin0(pTerm); + + if ( Abc_ObjIsPi(Abc_ObjFanin0(pNet)) ) + { + // Skip this output since it is a feedthrough -- the same + // name will appear as an input and an output which other + // tools reading verilog do not like. + + nskip++; + pSkip = pNet; // save an example of skipped net + continue; + } + + // get the line length after this name is written + AddedLength = strlen(Io_WriteVerilogGetName(Abc_ObjName(pNet))) + 2; + if ( NameCounter && LineLength + AddedLength + 3 > IO_WRITE_LINE_LENGTH ) + { // write the line extender + fprintf( pFile, "\n " ); + // reset the line length + LineLength = 3; + NameCounter = 0; + } + fprintf( pFile, " %s%s", Io_WriteVerilogGetName(Abc_ObjName(pNet)), (i==Abc_NtkPoNum(pNtk)-1)? "" : "," ); + LineLength += AddedLength; + NameCounter++; + } + + if (nskip != 0) + { + assert (pSkip); + printf( "Io_WriteVerilogPos(): Omitted %d feedthrough nets from output list of module (e.g. %s).\n", nskip, Abc_ObjName(pSkip) ); + return; + } + +} + +/**Function************************************************************* + + Synopsis [Writes the wires.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteVerilogWires( FILE * pFile, Abc_Ntk_t * pNtk, int Start ) +{ + Abc_Obj_t * pObj, * pNet, * pBox, * pTerm; + int LineLength; + int AddedLength; + int NameCounter; + int i, k, Counter, nNodes; + + // count the number of wires + nNodes = Io_WriteVerilogWiresCount( pNtk ); + + // write the wires + Counter = 0; + LineLength = Start; + NameCounter = 0; + Abc_NtkForEachNode( pNtk, pObj, i ) + { + if ( i == 0 ) + continue; + pNet = Abc_ObjFanout0(pObj); + if ( Abc_ObjFanoutNum(pNet) > 0 && Abc_ObjIsCo(Abc_ObjFanout0(pNet)) ) + continue; + Counter++; + // get the line length after this name is written + AddedLength = strlen(Io_WriteVerilogGetName(Abc_ObjName(pNet))) + 2; + if ( NameCounter && LineLength + AddedLength + 3 > IO_WRITE_LINE_LENGTH ) + { // write the line extender + fprintf( pFile, "\n " ); + // reset the line length + LineLength = 3; + NameCounter = 0; + } + fprintf( pFile, " %s%s", Io_WriteVerilogGetName(Abc_ObjName(pNet)), (Counter==nNodes)? "" : "," ); + LineLength += AddedLength; + NameCounter++; + } + Abc_NtkForEachLatch( pNtk, pObj, i ) + { + pNet = Abc_ObjFanin0(Abc_ObjFanin0(pObj)); + Counter++; + // get the line length after this name is written + AddedLength = strlen(Io_WriteVerilogGetName(Abc_ObjName(pNet))) + 2; + if ( NameCounter && LineLength + AddedLength + 3 > IO_WRITE_LINE_LENGTH ) + { // write the line extender + fprintf( pFile, "\n " ); + // reset the line length + LineLength = 3; + NameCounter = 0; + } + fprintf( pFile, " %s%s", Io_WriteVerilogGetName(Abc_ObjName(pNet)), (Counter==nNodes)? "" : "," ); + LineLength += AddedLength; + NameCounter++; + } + Abc_NtkForEachBox( pNtk, pBox, i ) + { + if ( Abc_ObjIsLatch(pBox) ) + continue; + Abc_ObjForEachFanin( pBox, pTerm, k ) + { + pNet = Abc_ObjFanin0(pTerm); + Counter++; + // get the line length after this name is written + AddedLength = strlen(Io_WriteVerilogGetName(Abc_ObjName(pNet))) + 2; + if ( NameCounter && LineLength + AddedLength + 3 > IO_WRITE_LINE_LENGTH ) + { // write the line extender + fprintf( pFile, "\n " ); + // reset the line length + LineLength = 3; + NameCounter = 0; + } + fprintf( pFile, " %s%s", Io_WriteVerilogGetName(Abc_ObjName(pNet)), (Counter==nNodes)? "" : "," ); + LineLength += AddedLength; + NameCounter++; + } + Abc_ObjForEachFanout( pBox, pTerm, k ) + { + pNet = Abc_ObjFanout0(pTerm); + if ( Abc_ObjFanoutNum(pNet) > 0 && Abc_ObjIsCo(Abc_ObjFanout0(pNet)) ) + continue; + Counter++; + // get the line length after this name is written + AddedLength = strlen(Io_WriteVerilogGetName(Abc_ObjName(pNet))) + 2; + if ( NameCounter && LineLength + AddedLength + 3 > IO_WRITE_LINE_LENGTH ) + { // write the line extender + fprintf( pFile, "\n " ); + // reset the line length + LineLength = 3; + NameCounter = 0; + } + fprintf( pFile, " %s%s", Io_WriteVerilogGetName(Abc_ObjName(pNet)), (Counter==nNodes)? "" : "," ); + LineLength += AddedLength; + NameCounter++; + } + } + assert( Counter == nNodes ); +} + +/**Function************************************************************* + + Synopsis [Writes the regs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteVerilogRegs( FILE * pFile, Abc_Ntk_t * pNtk, int Start ) +{ + Abc_Obj_t * pLatch, * pNet; + int LineLength; + int AddedLength; + int NameCounter; + int i, Counter, nNodes; + + // count the number of latches + nNodes = Abc_NtkLatchNum(pNtk); + + // write the wires + Counter = 0; + LineLength = Start; + NameCounter = 0; + Abc_NtkForEachLatch( pNtk, pLatch, i ) + { + pNet = Abc_ObjFanout0(Abc_ObjFanout0(pLatch)); + Counter++; + // get the line length after this name is written + AddedLength = strlen(Io_WriteVerilogGetName(Abc_ObjName(pNet))) + 2; + if ( NameCounter && LineLength + AddedLength + 3 > IO_WRITE_LINE_LENGTH ) + { // write the line extender + fprintf( pFile, "\n " ); + // reset the line length + LineLength = 3; + NameCounter = 0; + } + fprintf( pFile, " %s%s", Io_WriteVerilogGetName(Abc_ObjName(pNet)), (Counter==nNodes)? "" : "," ); + LineLength += AddedLength; + NameCounter++; + } +} + +/**Function************************************************************* + + Synopsis [Writes the latches.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteVerilogLatches( FILE * pFile, Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pLatch; + int i; + if ( Abc_NtkLatchNum(pNtk) == 0 ) + return; + // write the latches +// fprintf( pFile, " always @(posedge %s) begin\n", Io_WriteVerilogGetName(Abc_ObjFanout0(Abc_NtkPi(pNtk,0))) ); +// fprintf( pFile, " always begin\n" ); + fprintf( pFile, " always @ (posedge clock) begin\n" ); + Abc_NtkForEachLatch( pNtk, pLatch, i ) + { + fprintf( pFile, " %s", Io_WriteVerilogGetName(Abc_ObjName(Abc_ObjFanout0(Abc_ObjFanout0(pLatch)))) ); + fprintf( pFile, " <= %s;\n", Io_WriteVerilogGetName(Abc_ObjName(Abc_ObjFanin0(Abc_ObjFanin0(pLatch)))) ); + } + fprintf( pFile, " end\n" ); + // check if there are initial values + Abc_NtkForEachLatch( pNtk, pLatch, i ) + if ( Abc_LatchInit(pLatch) == ABC_INIT_ZERO || Abc_LatchInit(pLatch) == ABC_INIT_ONE ) + break; + if ( i == Abc_NtkLatchNum(pNtk) ) + return; + // write the initial values + fprintf( pFile, " initial begin\n", Io_WriteVerilogGetName(Abc_ObjName(Abc_ObjFanout0(Abc_NtkPi(pNtk,0)))) ); + Abc_NtkForEachLatch( pNtk, pLatch, i ) + { + if ( Abc_LatchInit(pLatch) == ABC_INIT_ZERO ) + fprintf( pFile, " %s <= 1\'b0;\n", Io_WriteVerilogGetName(Abc_ObjName(Abc_ObjFanout0(Abc_ObjFanout0(pLatch)))) ); + else if ( Abc_LatchInit(pLatch) == ABC_INIT_ONE ) + fprintf( pFile, " %s <= 1\'b1;\n", Io_WriteVerilogGetName(Abc_ObjName(Abc_ObjFanout0(Abc_ObjFanout0(pLatch)))) ); + } + fprintf( pFile, " end\n" ); +} + +/**Function************************************************************* + + Synopsis [Writes the nodes and boxes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_WriteVerilogObjects( FILE * pFile, Abc_Ntk_t * pNtk ) +{ + Vec_Vec_t * vLevels; + Abc_Ntk_t * pNtkBox; + Abc_Obj_t * pObj, * pTerm, * pFanin; + Hop_Obj_t * pFunc; + int i, k, Counter, nDigits, Length; + + // write boxes + nDigits = Extra_Base10Log( Abc_NtkBoxNum(pNtk)-Abc_NtkLatchNum(pNtk) ); + Counter = 0; + Abc_NtkForEachBox( pNtk, pObj, i ) + { + if ( Abc_ObjIsLatch(pObj) ) + continue; + pNtkBox = pObj->pData; + fprintf( pFile, " %s box%0*d", pNtkBox->pName, nDigits, Counter++ ); + fprintf( pFile, "(" ); + Abc_NtkForEachPi( pNtkBox, pTerm, k ) + { + fprintf( pFile, ".%s", Io_WriteVerilogGetName(Abc_ObjName(Abc_ObjFanout0(pTerm))) ); + fprintf( pFile, "(%s), ", Io_WriteVerilogGetName(Abc_ObjName(Abc_ObjFanin0(Abc_ObjFanin(pObj,k)))) ); + } + Abc_NtkForEachPo( pNtkBox, pTerm, k ) + { + fprintf( pFile, ".%s", Io_WriteVerilogGetName(Abc_ObjName(Abc_ObjFanin0(pTerm))) ); + fprintf( pFile, "(%s)%s", Io_WriteVerilogGetName(Abc_ObjName(Abc_ObjFanout0(Abc_ObjFanout(pObj,k)))), k==Abc_NtkPoNum(pNtkBox)-1? "":", " ); + } + fprintf( pFile, ");\n" ); + } + // write nodes + if ( Abc_NtkHasMapping(pNtk) ) + { + Length = Mio_LibraryReadGateNameMax(pNtk->pManFunc); + nDigits = Extra_Base10Log( Abc_NtkNodeNum(pNtk) ); + Counter = 0; + Abc_NtkForEachNode( pNtk, pObj, k ) + { + Mio_Gate_t * pGate = pObj->pData; + Mio_Pin_t * pGatePin; + // write the node + fprintf( pFile, " %-*s g%0*d", Length, Mio_GateReadName(pGate), nDigits, Counter++ ); + fprintf( pFile, "(" ); + for ( pGatePin = Mio_GateReadPins(pGate), i = 0; pGatePin; pGatePin = Mio_PinReadNext(pGatePin), i++ ) + { + fprintf( pFile, ".%s", Io_WriteVerilogGetName(Mio_PinReadName(pGatePin)) ); + fprintf( pFile, "(%s), ", Io_WriteVerilogGetName(Abc_ObjName( Abc_ObjFanin(pObj,i) )) ); + } + assert ( i == Abc_ObjFaninNum(pObj) ); + fprintf( pFile, ".%s", Io_WriteVerilogGetName(Mio_GateReadOutName(pGate)) ); + fprintf( pFile, "(%s)", Io_WriteVerilogGetName(Abc_ObjName( Abc_ObjFanout0(pObj) )) ); + fprintf( pFile, ");\n" ); + } + } + else + { + vLevels = Vec_VecAlloc( 10 ); + Abc_NtkForEachNode( pNtk, pObj, i ) + { + pFunc = pObj->pData; + fprintf( pFile, " assign %s = ", Io_WriteVerilogGetName(Abc_ObjName(Abc_ObjFanout0(pObj))) ); + // set the input names + Abc_ObjForEachFanin( pObj, pFanin, k ) + Hop_IthVar(pNtk->pManFunc, k)->pData = Extra_UtilStrsav(Io_WriteVerilogGetName(Abc_ObjName(pFanin))); + // write the formula + Hop_ObjPrintVerilog( pFile, pFunc, vLevels, 0 ); + fprintf( pFile, ";\n" ); + // clear the input names + Abc_ObjForEachFanin( pObj, pFanin, k ) + free( Hop_IthVar(pNtk->pManFunc, k)->pData ); + } + Vec_VecFree( vLevels ); + } +} + +/**Function************************************************************* + + Synopsis [Counts the number of wires.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Io_WriteVerilogWiresCount( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj, * pNet, * pBox; + int i, k, nWires; + nWires = Abc_NtkLatchNum(pNtk); + Abc_NtkForEachNode( pNtk, pObj, i ) + { + if ( i == 0 ) + continue; + pNet = Abc_ObjFanout0(pObj); + if ( Abc_ObjFanoutNum(pNet) > 0 && Abc_ObjIsCo(Abc_ObjFanout0(pNet)) ) + continue; + nWires++; + } + Abc_NtkForEachBox( pNtk, pBox, i ) + { + if ( Abc_ObjIsLatch(pBox) ) + continue; + nWires += Abc_ObjFaninNum(pBox); + Abc_ObjForEachFanout( pBox, pObj, k ) + { + pNet = Abc_ObjFanout0(pObj); + if ( Abc_ObjFanoutNum(pNet) > 0 && Abc_ObjIsCo(Abc_ObjFanout0(pNet)) ) + continue; + nWires++; + } + } + return nWires; +} + +/**Function************************************************************* + + Synopsis [Prepares the name for writing the Verilog file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Io_WriteVerilogGetName( char * pName ) +{ + static char Buffer[500]; + int Length, i; + Length = strlen(pName); + // consider the case of a signal having name "0" or "1" + if ( !(Length == 1 && (pName[0] == '0' || pName[0] == '1')) ) + { + for ( i = 0; i < Length; i++ ) + if ( !((pName[i] >= 'a' && pName[i] <= 'z') || + (pName[i] >= 'A' && pName[i] <= 'Z') || + (pName[i] >= '0' && pName[i] <= '9') || pName[i] == '_') ) + break; + if ( i == Length ) + return pName; + } + // create Verilog style name + Buffer[0] = '\\'; + for ( i = 0; i < Length; i++ ) + Buffer[i+1] = pName[i]; + Buffer[Length+1] = ' '; + Buffer[Length+2] = 0; + return Buffer; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/io/ioWriteVerilog.zip b/abc_with_bb_support/src/base/io/ioWriteVerilog.zip new file mode 100644 index 0000000000000000000000000000000000000000..19e68a892b5a03b77442b3eb2e39b76ee6906c00 GIT binary patch literal 3662 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 +#include + +#ifndef WIN32 +# include +# include +# include +#endif + +/* jluu added March 9, 2011 for cygwin compatibility */ +#define RTLD_LOCAL 0 + +#define MAX_LIBS 256 +static void* libHandles[MAX_LIBS+1]; // will be null terminated + +typedef void (*lib_init_end_func) (Abc_Frame_t * pAbc); + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This will find all the ABC library extensions in the current directory and load them all. +//////////////////////////////////////////////////////////////////////////////////////////////////// +void open_libs() { + int curr_lib = 0; + +#ifdef WIN32 +// printf("Warning: open_libs WIN32 not implemented.\n"); +#else + DIR* dirp; + struct dirent* dp; + char *env, *init_p, *p; + int done; + + env = getenv ("ABC_LIB_PATH"); + if (env == NULL) { +// printf("Warning: ABC_LIB_PATH not defined. Looking into the current directory.\n"); + init_p = malloc (2*sizeof(char)); + init_p[0]='.'; init_p[1] = 0; + } else { + init_p = malloc ((strlen(env)+1)*sizeof(char)); + strcpy (init_p, env); + } + + // Extract directories and read libraries + done = 0; + p = init_p; + while (!done) { + char *endp = strchr (p,':'); + if (endp == NULL) done = 1; // last directory in the list + else *endp = 0; // end of string + + dirp = opendir(p); + if (dirp == NULL) { +// printf("Warning: directory in ABC_LIB_PATH does not exist (%s).\n", p); + continue; + } + + while ((dp = readdir(dirp)) != NULL) { + if ((strncmp("libabc_", dp->d_name, 7) == 0) && + (strcmp(".so", dp->d_name + strlen(dp->d_name) - 3) == 0)) { + + // make sure we don't overflow the handle array + if (curr_lib >= MAX_LIBS) { + printf("Warning: maximum number of ABC libraries (%d) exceeded. Not loading %s.\n", + MAX_LIBS, + dp->d_name); + } + + // attempt to load it + else { + char* szPrefixed = malloc((strlen(dp->d_name) + strlen(p) + 2) * + sizeof(char)); + sprintf(szPrefixed, "%s/", p); + strcat(szPrefixed, dp->d_name); + libHandles[curr_lib] = dlopen(szPrefixed, RTLD_NOW | RTLD_LOCAL); + + // did the load succeed? + if (libHandles[curr_lib] != 0) { + printf("Loaded ABC library: %s (Abc library extension #%d)\n", szPrefixed, curr_lib); + curr_lib++; + } else { + printf("Warning: failed to load ABC library %s:\n\t%s\n", szPrefixed, dlerror()); + } + + free(szPrefixed); + } + } + } + closedir(dirp); + p = endp+1; + } + + free(init_p); +#endif + + // null terminate the list of handles + libHandles[curr_lib] = 0; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This will close all open ABC library extensions +//////////////////////////////////////////////////////////////////////////////////////////////////// +void close_libs() { +#ifdef WIN32 + printf("Warning: close_libs WIN32 not implemented.\n"); +#else + int i; + for (i = 0; libHandles[i] != 0; i++) { + if (dlclose(libHandles[i]) != 0) { + printf("Warning: failed to close library %d\n", i); + } + libHandles[i] = 0; + } +#endif +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This will get a pointer to a function inside of an open library +//////////////////////////////////////////////////////////////////////////////////////////////////// +void* get_fnct_ptr(int lib_num, char* sym_name) { +#ifdef WIN32 + printf("Warning: get_fnct_ptr WIN32 not implemented.\n"); + return 0; +#else + return dlsym(libHandles[lib_num], sym_name); +#endif +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This will call an initialization function in every open library. +//////////////////////////////////////////////////////////////////////////////////////////////////// +void call_inits(Abc_Frame_t* pAbc) { + int i; + lib_init_end_func init_func; + for (i = 0; libHandles[i] != 0; i++) { + init_func = (lib_init_end_func) get_fnct_ptr(i, "abc_init"); + if (init_func == 0) { + printf("Warning: Failed to initialize library %d.\n", i); + } else { + (*init_func)(pAbc); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This will call a shutdown function in every open library. +//////////////////////////////////////////////////////////////////////////////////////////////////// +void call_ends(Abc_Frame_t* pAbc) { + int i; + lib_init_end_func end_func; + for (i = 0; libHandles[i] != 0; i++) { + end_func = (lib_init_end_func) get_fnct_ptr(i, "abc_end"); + if (end_func == 0) { + printf("Warning: Failed to end library %d.\n", i); + } else { + (*end_func)(pAbc); + } + } +} + +void Libs_Init(Abc_Frame_t * pAbc) +{ + open_libs(); + call_inits(pAbc); +} + +void Libs_End(Abc_Frame_t * pAbc) +{ + call_ends(pAbc); + + // It's good practice to close our libraries at this point, but this can mess up any backtrace printed by Valgind. + // close_libs(); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/base/main/main.c b/abc_with_bb_support/src/base/main/main.c new file mode 100644 index 000000000..737f299a8 --- /dev/null +++ b/abc_with_bb_support/src/base/main/main.c @@ -0,0 +1,317 @@ +/**CFile**************************************************************** + + FileName [main.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [The main package.] + + Synopsis [Here everything starts.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: main.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "mainInt.h" + +// this line should be included in the library project +//#define _LIB + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int TypeCheck( Abc_Frame_t * pAbc, char * s); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#ifndef _LIB + +/**Function************************************************************* + + Synopsis [The main() procedure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int main( int argc, char * argv[] ) +{ + Abc_Frame_t * pAbc; + char sCommandUsr[500], sCommandTmp[100], sReadCmd[20], sWriteCmd[20], c; + char * sCommand, * sOutFile, * sInFile; + int fStatus = 0; + bool fBatch, fInitSource, fInitRead, fFinalWrite; + + // added to detect memory leaks: +#ifdef _DEBUG + _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); +#endif + +// Npn_Experiment(); +// Npn_Generate(); + + // get global frame (singleton pattern) + // will be initialized on first call + pAbc = Abc_FrameGetGlobalFrame(); + + // default options + fBatch = 0; + fInitSource = 1; + fInitRead = 0; + fFinalWrite = 0; + sInFile = sOutFile = NULL; + sprintf( sReadCmd, "read" ); + sprintf( sWriteCmd, "write" ); + + Extra_UtilGetoptReset(); + while ((c = Extra_UtilGetopt(argc, argv, "c:hf:F:o:st:T:x")) != EOF) { + switch(c) { + case 'c': + strcpy( sCommandUsr, globalUtilOptarg ); + fBatch = 1; + break; + + case 'f': + sprintf(sCommandUsr, "source %s", globalUtilOptarg); + fBatch = 1; + break; + + case 'F': + sprintf(sCommandUsr, "source -x %s", globalUtilOptarg); + fBatch = 1; + break; + + case 'h': + goto usage; + break; + + case 'o': + sOutFile = globalUtilOptarg; + fFinalWrite = 1; + break; + + case 's': + fInitSource = 0; + break; + + case 't': + if ( TypeCheck( pAbc, globalUtilOptarg ) ) + { + if ( !strcmp(globalUtilOptarg, "none") == 0 ) + { + fInitRead = 1; + sprintf( sReadCmd, "read_%s", globalUtilOptarg ); + } + } + else { + goto usage; + } + fBatch = 1; + break; + + case 'T': + if ( TypeCheck( pAbc, globalUtilOptarg ) ) + { + if (!strcmp(globalUtilOptarg, "none") == 0) + { + fFinalWrite = 1; + sprintf( sWriteCmd, "write_%s", globalUtilOptarg); + } + } + else { + goto usage; + } + fBatch = 1; + break; + + case 'x': + fFinalWrite = 0; + fInitRead = 0; + fBatch = 1; + break; + + default: + goto usage; + } + } + + if ( fBatch ) + { + pAbc->fBatchMode = 1; + + if (argc - globalUtilOptind == 0) + { + sInFile = NULL; + } + else if (argc - globalUtilOptind == 1) + { + fInitRead = 1; + sInFile = argv[globalUtilOptind]; + } + else + { + Abc_UtilsPrintUsage( pAbc, argv[0] ); + } + + // source the resource file + if ( fInitSource ) + { + Abc_UtilsSource( pAbc ); + } + + fStatus = 0; + if ( fInitRead && sInFile ) + { + sprintf( sCommandTmp, "%s %s", sReadCmd, sInFile ); + fStatus = Cmd_CommandExecute( pAbc, sCommandTmp ); + } + + if ( fStatus == 0 ) + { + /* cmd line contains `source ' */ + fStatus = Cmd_CommandExecute( pAbc, sCommandUsr ); + if ( (fStatus == 0 || fStatus == -1) && fFinalWrite && sOutFile ) + { + sprintf( sCommandTmp, "%s %s", sWriteCmd, sOutFile ); + fStatus = Cmd_CommandExecute( pAbc, sCommandTmp ); + } + } + + } + else + { + // start interactive mode + // print the hello line + Abc_UtilsPrintHello( pAbc ); + + // source the resource file + if ( fInitSource ) + { + Abc_UtilsSource( pAbc ); + } + + // execute commands given by the user + while ( !feof(stdin) ) + { + // print command line prompt and + // get the command from the user + sCommand = Abc_UtilsGetUsersInput( pAbc ); + + // execute the user's command + fStatus = Cmd_CommandExecute( pAbc, sCommand ); + + // stop if the user quitted or an error occurred + if ( fStatus == -1 || fStatus == -2 ) + break; + } + } + + // if the memory should be freed, quit packages + if ( fStatus < 0 ) + { + Abc_Stop(); + } + return 0; + +usage: + Abc_UtilsPrintHello( pAbc ); + Abc_UtilsPrintUsage( pAbc, argv[0] ); + return 1; +} + +#endif + +/**Function************************************************************* + + Synopsis [Initialization procedure for the library project.] + + Description [Note that when Abc_Start() is run in a static library + project, it does not load the resource file by default. As a result, + ABC is not set up the same way, as when it is run on a command line. + For example, some error messages while parsing files will not be + produced, and intermediate networks will not be checked for consistancy. + One possibility is to load the resource file after Abc_Start() as follows: + Abc_UtilsSource( Abc_FrameGetGlobalFrame() );] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_Start() +{ + Abc_Frame_t * pAbc; + // added to detect memory leaks: +#ifdef _DEBUG + _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); +#endif + // start the glocal frame + pAbc = Abc_FrameGetGlobalFrame(); + // source the resource file +// Abc_UtilsSource( pAbc ); +} + +/**Function************************************************************* + + Synopsis [Deallocation procedure for the library project.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_Stop() +{ + Abc_Frame_t * pAbc; + pAbc = Abc_FrameGetGlobalFrame(); + // perform uninitializations + Abc_FrameEnd( pAbc ); + // stop the framework + Abc_FrameDeallocate( pAbc ); +} + +/**Function******************************************************************** + + Synopsis [Returns 1 if s is a file type recognized, else returns 0.] + + Description [Returns 1 if s is a file type recognized by ABC, else returns 0. + Recognized types are "blif", "bench", "pla", and "none".] + + SideEffects [] + +******************************************************************************/ +static int TypeCheck( Abc_Frame_t * pAbc, char * s ) +{ + if (strcmp(s, "blif") == 0) + return 1; + else if (strcmp(s, "bench") == 0) + return 1; + else if (strcmp(s, "pla") == 0) + return 1; + else if (strcmp(s, "none") == 0) + return 1; + else { + fprintf( pAbc->Err, "unknown type %s\n", s ); + return 0; + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/main/main.h b/abc_with_bb_support/src/base/main/main.h new file mode 100644 index 000000000..16238d1f8 --- /dev/null +++ b/abc_with_bb_support/src/base/main/main.h @@ -0,0 +1,122 @@ +/**CFile**************************************************************** + + FileName [main.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [The main package.] + + Synopsis [External declarations of the main package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: main.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __MAIN_H__ +#define __MAIN_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// TYPEDEFS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// the framework containing all data +typedef struct Abc_Frame_t_ Abc_Frame_t; + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +// this include should be the first one in the list +// it is used to catch memory leaks on Windows +#include "leaks.h" + +// data structure packages +#include "extra.h" +#include "vec.h" +#include "st.h" + +// core packages +#include "abc.h" +#include "cmd.h" +#include "io.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// GLOBAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== main.c ===========================================================*/ +extern void Abc_Start(); +extern void Abc_Stop(); + +/*=== mainFrame.c ===========================================================*/ +extern Abc_Ntk_t * Abc_FrameReadNtk( Abc_Frame_t * p ); +extern FILE * Abc_FrameReadOut( Abc_Frame_t * p ); +extern FILE * Abc_FrameReadErr( Abc_Frame_t * p ); +extern bool Abc_FrameReadMode( Abc_Frame_t * p ); +extern bool Abc_FrameSetMode( Abc_Frame_t * p, bool fNameMode ); +extern void Abc_FrameRestart( Abc_Frame_t * p ); +extern bool Abc_FrameShowProgress( Abc_Frame_t * p ); + +extern void Abc_FrameSetCurrentNetwork( Abc_Frame_t * p, Abc_Ntk_t * pNet ); +extern void Abc_FrameSwapCurrentAndBackup( Abc_Frame_t * p ); +extern void Abc_FrameReplaceCurrentNetwork( Abc_Frame_t * p, Abc_Ntk_t * pNet ); +extern void Abc_FrameUnmapAllNetworks( Abc_Frame_t * p ); +extern void Abc_FrameDeleteAllNetworks( Abc_Frame_t * p ); + +extern void Abc_FrameSetGlobalFrame( Abc_Frame_t * p ); +extern Abc_Frame_t * Abc_FrameGetGlobalFrame(); + +extern Abc_Ntk_t * Abc_FrameReadNtkStore(); +extern int Abc_FrameReadNtkStoreSize(); +extern void * Abc_FrameReadLibLut(); +extern void * Abc_FrameReadLibGen(); +extern void * Abc_FrameReadLibSuper(); +extern void * Abc_FrameReadLibVer(); +extern void * Abc_FrameReadManDd(); +extern void * Abc_FrameReadManDec(); +extern char * Abc_FrameReadFlag( char * pFlag ); +extern bool Abc_FrameIsFlagEnabled( char * pFlag ); + +extern void Abc_FrameSetNtkStore( Abc_Ntk_t * pNtk ); +extern void Abc_FrameSetNtkStoreSize( int nStored ); +extern void Abc_FrameSetLibLut( void * pLib ); +extern void Abc_FrameSetLibGen( void * pLib ); +extern void Abc_FrameSetLibSuper( void * pLib ); +extern void Abc_FrameSetLibVer( void * pLib ); +extern void Abc_FrameSetFlag( char * pFlag, char * pValue ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/base/main/mainFrame.c b/abc_with_bb_support/src/base/main/mainFrame.c new file mode 100644 index 000000000..86c1b3d30 --- /dev/null +++ b/abc_with_bb_support/src/base/main/mainFrame.c @@ -0,0 +1,502 @@ +/**CFile**************************************************************** + + FileName [mainFrame.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [The main package.] + + Synopsis [The global framework resides in this file.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: mainFrame.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "mainInt.h" +#include "abc.h" +#include "dec.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Abc_Frame_t * s_GlobalFrame = NULL; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [APIs to access parameters in the flobal frame.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_FrameReadNtkStore() { return s_GlobalFrame->pStored; } +int Abc_FrameReadNtkStoreSize() { return s_GlobalFrame->nStored; } +void * Abc_FrameReadLibLut() { return s_GlobalFrame->pLibLut; } +void * Abc_FrameReadLibGen() { return s_GlobalFrame->pLibGen; } +void * Abc_FrameReadLibSuper() { return s_GlobalFrame->pLibSuper; } +void * Abc_FrameReadLibVer() { return s_GlobalFrame->pLibVer; } +void * Abc_FrameReadManDd() { if ( s_GlobalFrame->dd == NULL ) s_GlobalFrame->dd = Cudd_Init( 0, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 ); return s_GlobalFrame->dd; } +void * Abc_FrameReadManDec() { if ( s_GlobalFrame->pManDec == NULL ) s_GlobalFrame->pManDec = Dec_ManStart(); return s_GlobalFrame->pManDec; } +char * Abc_FrameReadFlag( char * pFlag ) { return Cmd_FlagReadByName( s_GlobalFrame, pFlag ); } + +void Abc_FrameSetNtkStore( Abc_Ntk_t * pNtk ) { s_GlobalFrame->pStored = pNtk; } +void Abc_FrameSetNtkStoreSize( int nStored ) { s_GlobalFrame->nStored = nStored; } +void Abc_FrameSetLibLut( void * pLib ) { s_GlobalFrame->pLibLut = pLib; } +void Abc_FrameSetLibGen( void * pLib ) { s_GlobalFrame->pLibGen = pLib; } +void Abc_FrameSetLibSuper( void * pLib ) { s_GlobalFrame->pLibSuper = pLib; } +void Abc_FrameSetLibVer( void * pLib ) { s_GlobalFrame->pLibVer = pLib; } +void Abc_FrameSetFlag( char * pFlag, char * pValue ) { Cmd_FlagUpdateValue( s_GlobalFrame, pFlag, pValue ); } + +/**Function************************************************************* + + Synopsis [Returns 1 if the flag is enabled without value or with value 1.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_FrameIsFlagEnabled( char * pFlag ) +{ + char * pValue; + // if flag is not defined, it is not enabled + pValue = Abc_FrameReadFlag( pFlag ); + if ( pValue == NULL ) + return 0; + // if flag is defined but value is not empty (no parameter) or "1", it is not enabled + if ( strcmp(pValue, "") && strcmp(pValue, "1") ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Frame_t * Abc_FrameAllocate() +{ + Abc_Frame_t * p; + extern void define_cube_size( int n ); + extern void set_espresso_flags(); + // allocate and clean + p = ALLOC( Abc_Frame_t, 1 ); + memset( p, 0, sizeof(Abc_Frame_t) ); + // get version + p->sVersion = Abc_UtilsGetVersion( p ); + // set streams + p->Err = stderr; + p->Out = stdout; + p->Hst = NULL; + // set the starting step + p->nSteps = 1; + p->fBatchMode = 0; + // initialize decomposition manager + define_cube_size(20); + set_espresso_flags(); + // initialize the trace manager +// Abc_HManStart(); + return p; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_FrameDeallocate( Abc_Frame_t * p ) +{ + extern void Rwt_ManGlobalStop(); + extern void undefine_cube_size(); +// extern void Ivy_TruthManStop(); +// Abc_HManStop(); + undefine_cube_size(); + Rwt_ManGlobalStop(); +// Ivy_TruthManStop(); + if ( p->pLibVer ) Abc_LibFree( p->pLibVer, NULL ); + if ( p->pManDec ) Dec_ManStop( p->pManDec ); + if ( p->dd ) Extra_StopManager( p->dd ); + Abc_FrameDeleteAllNetworks( p ); + free( p ); + s_GlobalFrame = NULL; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_FrameRestart( Abc_Frame_t * p ) +{ +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_FrameShowProgress( Abc_Frame_t * p ) +{ + return Abc_FrameIsFlagEnabled( "progressbar" ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_FrameReadNtk( Abc_Frame_t * p ) +{ + return p->pNtkCur; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +FILE * Abc_FrameReadOut( Abc_Frame_t * p ) +{ + return p->Out; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +FILE * Abc_FrameReadErr( Abc_Frame_t * p ) +{ + return p->Err; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_FrameReadMode( Abc_Frame_t * p ) +{ + int fShortNames; + char * pValue; + pValue = Cmd_FlagReadByName( p, "namemode" ); + if ( pValue == NULL ) + fShortNames = 0; + else + fShortNames = atoi(pValue); + return fShortNames; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_FrameSetMode( Abc_Frame_t * p, bool fNameMode ) +{ + char Buffer[2]; + bool fNameModeOld; + fNameModeOld = Abc_FrameReadMode( p ); + Buffer[0] = '0' + fNameMode; + Buffer[1] = 0; + Cmd_FlagUpdateValue( p, "namemode", (char *)Buffer ); + return fNameModeOld; +} + + +/**Function************************************************************* + + Synopsis [Sets the given network to be the current one.] + + Description [Takes the network and makes it the current network. + The previous current network is attached to the given network as + a backup copy. In the stack of backup networks contains too many + networks (defined by the paramater "savesteps"), the bottom + most network is deleted.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_FrameSetCurrentNetwork( Abc_Frame_t * p, Abc_Ntk_t * pNtkNew ) +{ + Abc_Ntk_t * pNtk, * pNtk2, * pNtk3; + int nNetsPresent; + int nNetsToSave; + char * pValue; + + // link it to the previous network + Abc_NtkSetBackup( pNtkNew, p->pNtkCur ); + // set the step of this network + Abc_NtkSetStep( pNtkNew, ++p->nSteps ); + // set this network to be the current network + p->pNtkCur = pNtkNew; + + // remove any extra network that may happen to be in the stack + pValue = Cmd_FlagReadByName( p, "savesteps" ); + // if the value of steps to save is not set, assume 1-level undo + if ( pValue == NULL ) + nNetsToSave = 1; + else + nNetsToSave = atoi(pValue); + + // count the network, remember the last one, and the one before the last one + nNetsPresent = 0; + pNtk2 = pNtk3 = NULL; + for ( pNtk = p->pNtkCur; pNtk; pNtk = Abc_NtkBackup(pNtk2) ) + { + nNetsPresent++; + pNtk3 = pNtk2; + pNtk2 = pNtk; + } + + // remove the earliest backup network if it is more steps away than we store + if ( nNetsPresent - 1 > nNetsToSave ) + { // delete the last network + Abc_NtkDelete( pNtk2 ); + // clean the pointer of the network before the last one + Abc_NtkSetBackup( pNtk3, NULL ); + } +} + +/**Function************************************************************* + + Synopsis [This procedure swaps the current and the backup network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_FrameSwapCurrentAndBackup( Abc_Frame_t * p ) +{ + Abc_Ntk_t * pNtkCur, * pNtkBack, * pNtkBack2; + int iStepCur, iStepBack; + + pNtkCur = p->pNtkCur; + pNtkBack = Abc_NtkBackup( pNtkCur ); + iStepCur = Abc_NtkStep ( pNtkCur ); + + // if there is no backup nothing to reset + if ( pNtkBack == NULL ) + return; + + // remember the backup of the backup + pNtkBack2 = Abc_NtkBackup( pNtkBack ); + iStepBack = Abc_NtkStep ( pNtkBack ); + + // set pNtkCur to be the next after the backup's backup + Abc_NtkSetBackup( pNtkCur, pNtkBack2 ); + Abc_NtkSetStep ( pNtkCur, iStepBack ); + + // set pNtkCur to be the next after the backup + Abc_NtkSetBackup( pNtkBack, pNtkCur ); + Abc_NtkSetStep ( pNtkBack, iStepCur ); + + // set the current network + p->pNtkCur = pNtkBack; +} + + +/**Function************************************************************* + + Synopsis [Replaces the current network by the given one.] + + Description [This procedure does not modify the stack of saved + networks.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_FrameReplaceCurrentNetwork( Abc_Frame_t * p, Abc_Ntk_t * pNtk ) +{ + if ( pNtk == NULL ) + return; + + // transfer the parameters to the new network + if ( p->pNtkCur && Abc_FrameIsFlagEnabled( "backup" ) ) + { + Abc_NtkSetBackup( pNtk, Abc_NtkBackup(p->pNtkCur) ); + Abc_NtkSetStep( pNtk, Abc_NtkStep(p->pNtkCur) ); + // delete the current network + Abc_NtkDelete( p->pNtkCur ); + } + else + { + Abc_NtkSetBackup( pNtk, NULL ); + Abc_NtkSetStep( pNtk, ++p->nSteps ); + // delete the current network if present but backup is disabled + if ( p->pNtkCur ) + Abc_NtkDelete( p->pNtkCur ); + } + // set the new current network + p->pNtkCur = pNtk; +} + +/**Function************************************************************* + + Synopsis [Removes library binding of all currently stored networks.] + + Description [This procedure is called when the library is freed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_FrameUnmapAllNetworks( Abc_Frame_t * p ) +{ + Abc_Ntk_t * pNtk; + for ( pNtk = p->pNtkCur; pNtk; pNtk = Abc_NtkBackup(pNtk) ) + if ( Abc_NtkHasMapping(pNtk) ) + Abc_NtkMapToSop( pNtk ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_FrameDeleteAllNetworks( Abc_Frame_t * p ) +{ + Abc_Ntk_t * pNtk, * pNtk2; + // delete all the currently saved networks + for ( pNtk = p->pNtkCur, + pNtk2 = pNtk? Abc_NtkBackup(pNtk): NULL; + pNtk; + pNtk = pNtk2, + pNtk2 = pNtk? Abc_NtkBackup(pNtk): NULL ) + Abc_NtkDelete( pNtk ); + // set the current network empty + p->pNtkCur = NULL; +// fprintf( p->Out, "All networks have been deleted.\n" ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_FrameSetGlobalFrame( Abc_Frame_t * p ) +{ + s_GlobalFrame = p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Frame_t * Abc_FrameGetGlobalFrame() +{ + if ( s_GlobalFrame == 0 ) + { + // start the framework + s_GlobalFrame = Abc_FrameAllocate(); + // perform initializations + Abc_FrameInit( s_GlobalFrame ); + } + return s_GlobalFrame; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/main/mainInit.c b/abc_with_bb_support/src/base/main/mainInit.c new file mode 100644 index 000000000..6dabc1533 --- /dev/null +++ b/abc_with_bb_support/src/base/main/mainInit.c @@ -0,0 +1,100 @@ +/**CFile**************************************************************** + + FileName [mainInit.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [The main package.] + + Synopsis [Initialization procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: mainInit.c,v 1.3 2005/09/14 22:53:37 casem Exp $] + +***********************************************************************/ + +#include "mainInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +extern void Abc_Init( Abc_Frame_t * pAbc ); +extern void Abc_End ( Abc_Frame_t * pAbc ); +extern void Io_Init( Abc_Frame_t * pAbc ); +extern void Io_End ( Abc_Frame_t * pAbc ); +extern void Cmd_Init( Abc_Frame_t * pAbc ); +extern void Cmd_End ( Abc_Frame_t * pAbc ); +extern void Fpga_Init( Abc_Frame_t * pAbc ); +extern void Fpga_End ( Abc_Frame_t * pAbc ); +extern void Map_Init( Abc_Frame_t * pAbc ); +extern void Map_End ( Abc_Frame_t * pAbc ); +extern void Mio_Init( Abc_Frame_t * pAbc ); +extern void Mio_End ( Abc_Frame_t * pAbc ); +extern void Super_Init( Abc_Frame_t * pAbc ); +extern void Super_End ( Abc_Frame_t * pAbc ); +extern void Libs_Init(Abc_Frame_t * pAbc); +extern void Libs_End(Abc_Frame_t * pAbc); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts all the packages.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_FrameInit( Abc_Frame_t * pAbc ) +{ + Cmd_Init( pAbc ); + Io_Init( pAbc ); + Abc_Init( pAbc ); + Fpga_Init( pAbc ); + Map_Init( pAbc ); + Mio_Init( pAbc ); + Super_Init( pAbc ); + Libs_Init( pAbc ); +} + + +/**Function************************************************************* + + Synopsis [Stops all the packages.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_FrameEnd( Abc_Frame_t * pAbc ) +{ + Abc_End( pAbc ); + Io_End( pAbc ); + Cmd_End( pAbc ); + Fpga_End( pAbc ); + Map_End( pAbc ); + Mio_End( pAbc ); + Super_End( pAbc ); + Libs_End( pAbc ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/main/mainInt.h b/abc_with_bb_support/src/base/main/mainInt.h new file mode 100644 index 000000000..48b6bc3d0 --- /dev/null +++ b/abc_with_bb_support/src/base/main/mainInt.h @@ -0,0 +1,110 @@ +/**CFile**************************************************************** + + FileName [mainInt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [The main package.] + + Synopsis [Internal declarations of the main package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: mainInt.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __MAIN_INT_H__ +#define __MAIN_INT_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include "main.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +// the current version +#define ABC_VERSION "UC Berkeley, ABC for VTR 7.0" + +// the maximum length of an input line +#define MAX_STR 32768 + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +struct Abc_Frame_t_ +{ + // general info + char * sVersion; // the name of the current version + // commands, aliases, etc + st_table * tCommands; // the command table + st_table * tAliases; // the alias table + st_table * tFlags; // the flag table + Vec_Ptr_t * aHistory; // the command history + // the functionality + Abc_Ntk_t * pNtkCur; // the current network + int nSteps; // the counter of different network processed + int fAutoexac; // marks the autoexec mode + int fBatchMode; // are we invoked in batch mode? + // output streams + FILE * Out; + FILE * Err; + FILE * Hst; + // used for runtime measurement + int TimeCommand; // the runtime of the last command + int TimeTotal; // the total runtime of all commands + // temporary storage for structural choices + Abc_Ntk_t * pStored; // the stored networks + int nStored; // the number of stored networks + // decomposition package + void * pManDec; // decomposition manager + DdManager * dd; // temporary BDD package + // libraries for mapping + void * pLibLut; // the current LUT library + void * pLibGen; // the current genlib + void * pLibSuper; // the current supergate library + void * pLibVer; // the current Verilog library +}; + +//////////////////////////////////////////////////////////////////////// +/// GLOBAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== mvMain.c ===========================================================*/ +extern int main( int argc, char * argv[] ); +/*=== mvInit.c ===================================================*/ +extern void Abc_FrameInit( Abc_Frame_t * pAbc ); +extern void Abc_FrameEnd( Abc_Frame_t * pAbc ); +/*=== mvFrame.c =====================================================*/ +extern Abc_Frame_t * Abc_FrameAllocate(); +extern void Abc_FrameDeallocate( Abc_Frame_t * p ); +/*=== mvUtils.c =====================================================*/ +extern char * Abc_UtilsGetVersion( Abc_Frame_t * pAbc ); +extern char * Abc_UtilsGetUsersInput( Abc_Frame_t * pAbc ); +extern void Abc_UtilsPrintHello( Abc_Frame_t * pAbc ); +extern void Abc_UtilsPrintUsage( Abc_Frame_t * pAbc, char * ProgName ); +extern void Abc_UtilsSource( Abc_Frame_t * pAbc ); + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/base/main/mainUtils.c b/abc_with_bb_support/src/base/main/mainUtils.c new file mode 100644 index 000000000..b84240969 --- /dev/null +++ b/abc_with_bb_support/src/base/main/mainUtils.c @@ -0,0 +1,239 @@ +/**CFile**************************************************************** + + FileName [mainUtils.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [The main package.] + + Synopsis [Miscellaneous utilities.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: mainUtils.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "mainInt.h" +/* jluu +#ifndef _WIN32 +#include "readline/readline.h" +#endif +*/ +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// +static char * DateReadFromDateString(char * datestr); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_UtilsGetVersion( Abc_Frame_t * pAbc ) +{ + static char Version[1000]; + sprintf(Version, "%s (compiled %s %s)", ABC_VERSION, __DATE__, __TIME__); + return Version; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Abc_UtilsGetUsersInput( Abc_Frame_t * pAbc ) +{ + static char Buffer[1000], Prompt[1000]; + sprintf( Prompt, "abc %02d> ", pAbc->nSteps ); + +/* jluu #ifdef _WIN32 */ +#ifndef CMD_READLINE + fprintf( pAbc->Out, "%s", Prompt ); + fgets( Buffer, 999, stdin ); + return Buffer; +#else + static char* line = NULL; + if (line != NULL) free(line); + line = readline(Prompt); + if (line == NULL){ printf("***EOF***\n"); exit(0); } + add_history(line); + return line; +#endif +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_UtilsPrintHello( Abc_Frame_t * pAbc ) +{ + fprintf( pAbc->Out, "%s\n", pAbc->sVersion ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_UtilsPrintUsage( Abc_Frame_t * pAbc, char * ProgName ) +{ + fprintf( pAbc->Err, "\n" ); + fprintf( pAbc->Err, + "usage: %s [-c cmd] [-f script] [-h] [-o file] [-s] [-t type] [-T type] [-x] [file]\n", + ProgName); + fprintf( pAbc->Err, " -c cmd\texecute commands `cmd'\n"); + fprintf( pAbc->Err, " -F script\texecute commands from a script file and echo commands\n"); + fprintf( pAbc->Err, " -f script\texecute commands from a script file\n"); + fprintf( pAbc->Err, " -h\t\tprint the command usage\n"); + fprintf( pAbc->Err, " -o file\tspecify output filename to store the result\n"); + fprintf( pAbc->Err, " -s\t\tdo not read any initialization file\n"); + fprintf( pAbc->Err, " -t type\tspecify input type (blif_mv (default), blif_mvs, blif, or none)\n"); + fprintf( pAbc->Err, " -T type\tspecify output type (blif_mv (default), blif_mvs, blif, or none)\n"); + fprintf( pAbc->Err, " -x\t\tequivalent to '-t none -T none'\n"); + fprintf( pAbc->Err, "\n" ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_UtilsSource( Abc_Frame_t * pAbc ) +{ +#ifdef WIN32 + if ( Cmd_CommandExecute(pAbc, "source abc.rc") ) + { + if ( Cmd_CommandExecute(pAbc, "source ..\\abc.rc") == 0 ) + printf( "Loaded \"abc.rc\" from the parent directory.\n" ); + else if ( Cmd_CommandExecute(pAbc, "source ..\\..\\abc.rc") == 0 ) + printf( "Loaded \"abc.rc\" from the grandparent directory.\n" ); + } +#else + { + char * sPath1, * sPath2; + + // If .rc is present in both the home and current directories, then read + // it from the home directory. Otherwise, read it from wherever it's located. + sPath1 = Extra_UtilFileSearch(".rc", "~/", "r"); + sPath2 = Extra_UtilFileSearch(".rc", ".", "r"); + + if ( sPath1 && sPath2 ) { + /* ~/.rc == .rc : Source the file only once */ + (void) Cmd_CommandExecute(pAbc, "source -s ~/.rc"); + } + else { + if (sPath1) { + (void) Cmd_CommandExecute(pAbc, "source -s ~/.rc"); + } + if (sPath2) { + (void) Cmd_CommandExecute(pAbc, "source -s .rc"); + } + } + if ( sPath1 ) FREE(sPath1); + if ( sPath2 ) FREE(sPath2); + + /* execute the abc script which can be open with the "open_path" */ + Cmd_CommandExecute( pAbc, "source -s abc.rc" ); + } +#endif //WIN32 + { + // reset command history + char * pName; + int i; + Vec_PtrForEachEntry( pAbc->aHistory, pName, i ) + free( pName ); + pAbc->aHistory->nSize = 0; + } +} + +/**Function******************************************************************** + + Synopsis [Returns the date in a brief format assuming its coming from + the program `date'.] + + Description [optional] + + SideEffects [] + +******************************************************************************/ +char * +DateReadFromDateString( + char * datestr) +{ + static char result[25]; + char day[10]; + char month[10]; + char zone[10]; + char *at; + int date; + int hour; + int minute; + int second; + int year; + + if (sscanf(datestr, "%s %s %2d %2d:%2d:%2d %s %4d", + day, month, &date, &hour, &minute, &second, zone, &year) == 8) { + if (hour >= 12) { + if (hour >= 13) hour -= 12; + at = "PM"; + } + else { + if (hour == 0) hour = 12; + at = "AM"; + } + (void) sprintf(result, "%d-%3s-%02d at %d:%02d %s", + date, month, year % 100, hour, minute, at); + return result; + } + else { + return datestr; + } +} + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/main/module.make b/abc_with_bb_support/src/base/main/module.make new file mode 100644 index 000000000..20aa2a6cc --- /dev/null +++ b/abc_with_bb_support/src/base/main/module.make @@ -0,0 +1,5 @@ +SRC += src/base/main/main.c \ + src/base/main/mainFrame.c \ + src/base/main/mainInit.c \ + src/base/main/libSupport.c \ + src/base/main/mainUtils.c diff --git a/abc_with_bb_support/src/base/seq/module.make b/abc_with_bb_support/src/base/seq/module.make new file mode 100644 index 000000000..591d9eafb --- /dev/null +++ b/abc_with_bb_support/src/base/seq/module.make @@ -0,0 +1,14 @@ +SRC += src/base/seq/seqAigCore.c \ + src/base/seq/seqAigIter.c \ + src/base/seq/seqCreate.c \ + src/base/seq/seqFpgaCore.c \ + src/base/seq/seqFpgaIter.c \ + src/base/seq/seqLatch.c \ + src/base/seq/seqMan.c \ + src/base/seq/seqMapCore.c \ + src/base/seq/seqMapIter.c \ + src/base/seq/seqMaxMeanCycle.c \ + src/base/seq/seqRetCore.c \ + src/base/seq/seqRetIter.c \ + src/base/seq/seqShare.c \ + src/base/seq/seqUtil.c diff --git a/abc_with_bb_support/src/base/seq/seq.h b/abc_with_bb_support/src/base/seq/seq.h new file mode 100644 index 000000000..92ce0ab3a --- /dev/null +++ b/abc_with_bb_support/src/base/seq/seq.h @@ -0,0 +1,101 @@ +/**CFile**************************************************************** + + FileName [seq.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Construction and manipulation of sequential AIGs.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: seq.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __SEQ_H__ +#define __SEQ_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Abc_Seq_t_ Abc_Seq_t; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== seqAigCore.c ===========================================================*/ +extern void Seq_NtkSeqRetimeDelay( Abc_Ntk_t * pNtk, int nMaxIters, int fInitial, int fVerbose ); +extern void Seq_NtkSeqRetimeForward( Abc_Ntk_t * pNtk, int fInitial, int fVerbose ); +extern void Seq_NtkSeqRetimeBackward( Abc_Ntk_t * pNtk, int fInitial, int fVerbose ); +/*=== seqFpgaCore.c ===============================================================*/ +extern Abc_Ntk_t * Seq_NtkFpgaMapRetime( Abc_Ntk_t * pNtk, int nMaxIters, int fVerbose ); +/*=== seqMapCore.c ===============================================================*/ +extern Abc_Ntk_t * Seq_MapRetime( Abc_Ntk_t * pNtk, int nMaxIters, int fVerbose ); +/*=== seqRetCore.c ===========================================================*/ +extern Abc_Ntk_t * Seq_NtkRetime( Abc_Ntk_t * pNtk, int nMaxIters, int fInitial, int fVerbose ); +/*=== seqLatch.c ===============================================================*/ +extern void Seq_NodeDupLats( Abc_Obj_t * pObjNew, Abc_Obj_t * pObj, int Edge ); +extern int Seq_NodeCompareLats( Abc_Obj_t * pObj1, int Edge1, Abc_Obj_t * pObj2, int Edge2 ); +/*=== seqMan.c ===============================================================*/ +extern Abc_Seq_t * Seq_Create( Abc_Ntk_t * pNtk ); +extern void Seq_Resize( Abc_Seq_t * p, int nMaxId ); +extern void Seq_Delete( Abc_Seq_t * p ); +/*=== seqMaxMeanCycle.c ======================================================*/ +extern float Seq_NtkHoward( Abc_Ntk_t * pNtk, int fVerbose ); +extern void Seq_NtkSkewForward( Abc_Ntk_t * pNtk, float period, int fMinimize ); +/*=== abcSeq.c ===============================================================*/ +extern Abc_Ntk_t * Abc_NtkAigToSeq( Abc_Ntk_t * pNtk ); +extern Abc_Ntk_t * Abc_NtkSeqToLogicSop( Abc_Ntk_t * pNtk ); +extern bool Abc_NtkSeqCheck( Abc_Ntk_t * pNtk ); +/*=== seqShare.c =============================================================*/ +extern void Seq_NtkShareFanouts( Abc_Ntk_t * pNtk ); +extern void Seq_NtkShareLatches( Abc_Ntk_t * pNtkNew, Abc_Ntk_t * pNtk ); +extern void Seq_NtkShareLatchesMapping( Abc_Ntk_t * pNtkNew, Abc_Ntk_t * pNtk, Vec_Ptr_t * vMapAnds, int fFpga ); +extern void Seq_NtkShareLatchesClean( Abc_Ntk_t * pNtk ); +/*=== seqUtil.c ==============================================================*/ +extern char * Seq_ObjFaninGetInitPrintable( Abc_Obj_t * pObj, int Edge ); +extern void Seq_NtkLatchSetValues( Abc_Ntk_t * pNtk, Abc_InitType_t Init ); +extern int Seq_NtkLatchNum( Abc_Ntk_t * pNtk ); +extern int Seq_NtkLatchNumMax( Abc_Ntk_t * pNtk ); +extern int Seq_NtkLatchNumShared( Abc_Ntk_t * pNtk ); +extern void Seq_NtkLatchGetInitNums( Abc_Ntk_t * pNtk, int * pInits ); +extern int Seq_NtkLatchGetEqualFaninNum( Abc_Ntk_t * pNtk ); +extern int Seq_NtkCountNodesAboveLimit( Abc_Ntk_t * pNtk, int Limit ); +extern int Seq_MapComputeAreaFlows( Abc_Ntk_t * pNtk, int fVerbose ); +extern Vec_Ptr_t * Seq_NtkReachNodes( Abc_Ntk_t * pNtk, int fFromPos ); +extern int Seq_NtkCleanup( Abc_Ntk_t * pNtk, int fVerbose ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/base/seq/seqAigCore.c b/abc_with_bb_support/src/base/seq/seqAigCore.c new file mode 100644 index 000000000..12c334998 --- /dev/null +++ b/abc_with_bb_support/src/base/seq/seqAigCore.c @@ -0,0 +1,977 @@ +/**CFile**************************************************************** + + FileName [seqRetCore.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Construction and manipulation of sequential AIGs.] + + Synopsis [The core of retiming procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: seqRetCore.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "seqInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/* + Retiming can be represented in three equivalent forms: + - as a set of integer lags for each node (array of chars by node ID) + - as a set of node numbers with lag for each, fwd and bwd (two arrays of Seq_RetStep_t_) + - as a set of latch moves over the nodes, fwd and bwd (two arrays of node pointers Abc_Obj_t *) +*/ + +static void Abc_ObjRetimeForward( Abc_Obj_t * pObj ); +static int Abc_ObjRetimeBackward( Abc_Obj_t * pObj, Abc_Ntk_t * pNtk, stmm_table * tTable, Vec_Int_t * vValues ); +static void Abc_ObjRetimeBackwardUpdateEdge( Abc_Obj_t * pObj, int Edge, stmm_table * tTable ); +static void Abc_NtkRetimeSetInitialValues( Abc_Ntk_t * pNtk, stmm_table * tTable, int * pModel ); + +static void Seq_NtkImplementRetimingForward( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMoves ); +static int Seq_NtkImplementRetimingBackward( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMoves, int fVerbose ); +static void Abc_ObjRetimeForward( Abc_Obj_t * pObj ); +static int Abc_ObjRetimeBackward( Abc_Obj_t * pObj, Abc_Ntk_t * pNtk, stmm_table * tTable, Vec_Int_t * vValues ); +static void Abc_ObjRetimeBackwardUpdateEdge( Abc_Obj_t * pObj, int Edge, stmm_table * tTable ); +static void Abc_NtkRetimeSetInitialValues( Abc_Ntk_t * pNtk, stmm_table * tTable, int * pModel ); + +static Vec_Ptr_t * Abc_NtkUtilRetimingTry( Abc_Ntk_t * pNtk, bool fForward ); +static Vec_Ptr_t * Abc_NtkUtilRetimingGetMoves( Abc_Ntk_t * pNtk, Vec_Int_t * vSteps, bool fForward ); +static Vec_Int_t * Abc_NtkUtilRetimingSplit( Vec_Str_t * vLags, int fForward ); +static void Abc_ObjRetimeForwardTry( Abc_Obj_t * pObj, int nLatches ); +static void Abc_ObjRetimeBackwardTry( Abc_Obj_t * pObj, int nLatches ); + + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs performs optimal delay retiming.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NtkSeqRetimeDelay( Abc_Ntk_t * pNtk, int nMaxIters, int fInitial, int fVerbose ) +{ + Abc_Seq_t * p = pNtk->pManFunc; + int RetValue; + if ( !fInitial ) + Seq_NtkLatchSetValues( pNtk, ABC_INIT_DC ); + // get the retiming lags + p->nMaxIters = nMaxIters; + if ( !Seq_AigRetimeDelayLags( pNtk, fVerbose ) ) + return; + // implement this retiming + RetValue = Seq_NtkImplementRetiming( pNtk, p->vLags, fVerbose ); + if ( RetValue == 0 ) + printf( "Retiming completed but initial state computation has failed.\n" ); +} + +/**Function************************************************************* + + Synopsis [Performs most forward retiming.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NtkSeqRetimeForward( Abc_Ntk_t * pNtk, int fInitial, int fVerbose ) +{ + Vec_Ptr_t * vMoves; + Abc_Obj_t * pNode; + int i; + if ( !fInitial ) + Seq_NtkLatchSetValues( pNtk, ABC_INIT_DC ); + // get the forward moves + vMoves = Abc_NtkUtilRetimingTry( pNtk, 1 ); + // undo the forward moves + Vec_PtrForEachEntryReverse( vMoves, pNode, i ) + Abc_ObjRetimeBackwardTry( pNode, 1 ); + // implement this forward retiming + Seq_NtkImplementRetimingForward( pNtk, vMoves ); + Vec_PtrFree( vMoves ); +} + +/**Function************************************************************* + + Synopsis [Performs most backward retiming.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NtkSeqRetimeBackward( Abc_Ntk_t * pNtk, int fInitial, int fVerbose ) +{ + Vec_Ptr_t * vMoves; + Abc_Obj_t * pNode; + int i, RetValue; + if ( !fInitial ) + Seq_NtkLatchSetValues( pNtk, ABC_INIT_DC ); + // get the backward moves + vMoves = Abc_NtkUtilRetimingTry( pNtk, 0 ); + // undo the backward moves + Vec_PtrForEachEntryReverse( vMoves, pNode, i ) + Abc_ObjRetimeForwardTry( pNode, 1 ); + // implement this backward retiming + RetValue = Seq_NtkImplementRetimingBackward( pNtk, vMoves, fVerbose ); + Vec_PtrFree( vMoves ); + if ( RetValue == 0 ) + printf( "Retiming completed but initial state computation has failed.\n" ); +} + + + + +/**Function************************************************************* + + Synopsis [Implements the retiming on the sequential AIG.] + + Description [Split the retiming into forward and backward.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_NtkImplementRetiming( Abc_Ntk_t * pNtk, Vec_Str_t * vLags, int fVerbose ) +{ + Vec_Int_t * vSteps; + Vec_Ptr_t * vMoves; + int RetValue; + + // forward retiming + vSteps = Abc_NtkUtilRetimingSplit( vLags, 1 ); + // translate each set of steps into moves + if ( fVerbose ) + printf( "The number of forward steps = %6d.\n", Vec_IntSize(vSteps) ); + vMoves = Abc_NtkUtilRetimingGetMoves( pNtk, vSteps, 1 ); + if ( fVerbose ) + printf( "The number of forward moves = %6d.\n", Vec_PtrSize(vMoves) ); + // implement this retiming + Seq_NtkImplementRetimingForward( pNtk, vMoves ); + Vec_IntFree( vSteps ); + Vec_PtrFree( vMoves ); + + // backward retiming + vSteps = Abc_NtkUtilRetimingSplit( vLags, 0 ); + // translate each set of steps into moves + if ( fVerbose ) + printf( "The number of backward steps = %6d.\n", Vec_IntSize(vSteps) ); + vMoves = Abc_NtkUtilRetimingGetMoves( pNtk, vSteps, 0 ); + if ( fVerbose ) + printf( "The number of backward moves = %6d.\n", Vec_PtrSize(vMoves) ); + // implement this retiming + RetValue = Seq_NtkImplementRetimingBackward( pNtk, vMoves, fVerbose ); + Vec_IntFree( vSteps ); + Vec_PtrFree( vMoves ); + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Implements the given retiming on the sequential AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NtkImplementRetimingForward( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMoves ) +{ + Abc_Obj_t * pNode; + int i; + Vec_PtrForEachEntry( vMoves, pNode, i ) + Abc_ObjRetimeForward( pNode ); +} + +/**Function************************************************************* + + Synopsis [Retimes node forward by one latch.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ObjRetimeForward( Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanout; + int Init0, Init1, Init, i; + assert( Abc_ObjFaninNum(pObj) == 2 ); + assert( Seq_ObjFaninL0(pObj) >= 1 ); + assert( Seq_ObjFaninL1(pObj) >= 1 ); + // remove the init values from the fanins + Init0 = Seq_NodeDeleteFirst( pObj, 0 ); + Init1 = Seq_NodeDeleteFirst( pObj, 1 ); + assert( Init0 != ABC_INIT_NONE ); + assert( Init1 != ABC_INIT_NONE ); + // take into account the complements in the node + if ( Abc_ObjFaninC0(pObj) ) + { + if ( Init0 == ABC_INIT_ZERO ) + Init0 = ABC_INIT_ONE; + else if ( Init0 == ABC_INIT_ONE ) + Init0 = ABC_INIT_ZERO; + } + if ( Abc_ObjFaninC1(pObj) ) + { + if ( Init1 == ABC_INIT_ZERO ) + Init1 = ABC_INIT_ONE; + else if ( Init1 == ABC_INIT_ONE ) + Init1 = ABC_INIT_ZERO; + } + // compute the value at the output of the node + if ( Init0 == ABC_INIT_ZERO || Init1 == ABC_INIT_ZERO ) + Init = ABC_INIT_ZERO; + else if ( Init0 == ABC_INIT_ONE && Init1 == ABC_INIT_ONE ) + Init = ABC_INIT_ONE; + else + Init = ABC_INIT_DC; + + // make sure the label is clean + Abc_ObjForEachFanout( pObj, pFanout, i ) + assert( pFanout->fMarkC == 0 ); + // add the init values to the fanouts + Abc_ObjForEachFanout( pObj, pFanout, i ) + { + if ( pFanout->fMarkC ) + continue; + pFanout->fMarkC = 1; + if ( Abc_ObjFaninId0(pFanout) != Abc_ObjFaninId1(pFanout) ) + Seq_NodeInsertLast( pFanout, Abc_ObjFanoutEdgeNum(pObj, pFanout), Init ); + else + { + assert( Abc_ObjFanin0(pFanout) == pObj ); + Seq_NodeInsertLast( pFanout, 0, Init ); + Seq_NodeInsertLast( pFanout, 1, Init ); + } + } + // clean the label + Abc_ObjForEachFanout( pObj, pFanout, i ) + pFanout->fMarkC = 0; +} + + +/**Function************************************************************* + + Synopsis [Implements the given retiming on the sequential AIG.] + + Description [Returns 0 of initial state computation fails.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_NtkImplementRetimingBackward( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMoves, int fVerbose ) +{ + Seq_RetEdge_t RetEdge; + stmm_table * tTable; + stmm_generator * gen; + Vec_Int_t * vValues; + Abc_Ntk_t * pNtkProb, * pNtkMiter, * pNtkCnf; + Abc_Obj_t * pNode, * pNodeNew; + int * pModel, RetValue, i, clk; + + // return if the retiming is trivial + if ( Vec_PtrSize(vMoves) == 0 ) + return 1; + + // create the network for the initial state computation + // start the table and the array of PO values + pNtkProb = Abc_NtkAlloc( ABC_NTK_LOGIC, ABC_FUNC_SOP, 1 ); + tTable = stmm_init_table( stmm_numcmp, stmm_numhash ); + vValues = Vec_IntAlloc( 100 ); + + // perform the backward moves and build the network for initial state computation + RetValue = 0; + Vec_PtrForEachEntry( vMoves, pNode, i ) + RetValue |= Abc_ObjRetimeBackward( pNode, pNtkProb, tTable, vValues ); + + // add the PIs corresponding to the white spots + stmm_foreach_item( tTable, gen, (char **)&RetEdge, (char **)&pNodeNew ) + Abc_ObjAddFanin( pNodeNew, Abc_NtkCreatePi(pNtkProb) ); + + // add the PI/PO names + Abc_NtkAddDummyPiNames( pNtkProb ); + Abc_NtkAddDummyPoNames( pNtkProb ); + Abc_NtkAddDummyAssertNames( pNtkProb ); + + // make sure everything is okay with the network structure + if ( !Abc_NtkDoCheck( pNtkProb ) ) + { + printf( "Seq_NtkImplementRetimingBackward: The internal network check has failed.\n" ); + Abc_NtkRetimeSetInitialValues( pNtk, tTable, NULL ); + Abc_NtkDelete( pNtkProb ); + stmm_free_table( tTable ); + Vec_IntFree( vValues ); + return 0; + } + + // check if conflict is found + if ( RetValue ) + { + printf( "Seq_NtkImplementRetimingBackward: A top level conflict is detected. DC latch values are used.\n" ); + Abc_NtkRetimeSetInitialValues( pNtk, tTable, NULL ); + Abc_NtkDelete( pNtkProb ); + stmm_free_table( tTable ); + Vec_IntFree( vValues ); + return 0; + } + + // get the miter cone + pNtkMiter = Abc_NtkCreateTarget( pNtkProb, pNtkProb->vCos, vValues ); + Abc_NtkDelete( pNtkProb ); + Vec_IntFree( vValues ); + + if ( fVerbose ) + printf( "The number of ANDs in the AIG = %5d.\n", Abc_NtkNodeNum(pNtkMiter) ); + + // transform the miter into a logic network for efficient CNF construction +// pNtkCnf = Abc_Ntk_Renode( pNtkMiter, 0, 100, 1, 0, 0 ); +// Abc_NtkDelete( pNtkMiter ); + pNtkCnf = pNtkMiter; + + // solve the miter +clk = clock(); +// RetValue = Abc_NtkMiterSat_OldAndRusty( pNtkCnf, 30, 0 ); + RetValue = Abc_NtkMiterSat( pNtkCnf, (sint64)500000, (sint64)50000000, 0, 0, NULL, NULL ); +if ( fVerbose ) +if ( clock() - clk > 100 ) +{ +PRT( "SAT solving time", clock() - clk ); +} + pModel = pNtkCnf->pModel; pNtkCnf->pModel = NULL; + Abc_NtkDelete( pNtkCnf ); + + // analyze the result + if ( RetValue == -1 || RetValue == 1 ) + { + Abc_NtkRetimeSetInitialValues( pNtk, tTable, NULL ); + if ( RetValue == 1 ) + printf( "Seq_NtkImplementRetimingBackward: The problem is unsatisfiable. DC latch values are used.\n" ); + else + printf( "Seq_NtkImplementRetimingBackward: The SAT problem timed out. DC latch values are used.\n" ); + stmm_free_table( tTable ); + return 0; + } + + // set the values of the latches + Abc_NtkRetimeSetInitialValues( pNtk, tTable, pModel ); + stmm_free_table( tTable ); + free( pModel ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Retimes node backward by one latch.] + + Description [Constructs the problem for initial state computation. + Returns 1 if the conflict is found.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_ObjRetimeBackward( Abc_Obj_t * pObj, Abc_Ntk_t * pNtkNew, stmm_table * tTable, Vec_Int_t * vValues ) +{ + Abc_Obj_t * pFanout; + Abc_InitType_t Init, Value; + Seq_RetEdge_t RetEdge; + Abc_Obj_t * pNodeNew, * pFanoutNew, * pBuffer; + int i, Edge, fMet0, fMet1, fMetN; + + // make sure the node can be retimed + assert( Seq_ObjFanoutLMin(pObj) > 0 ); + // get the fanout values + fMet0 = fMet1 = fMetN = 0; + Abc_ObjForEachFanout( pObj, pFanout, i ) + { + if ( Abc_ObjFaninId0(pFanout) == pObj->Id ) + { + Init = Seq_NodeGetInitLast( pFanout, 0 ); + if ( Init == ABC_INIT_ZERO ) + fMet0 = 1; + else if ( Init == ABC_INIT_ONE ) + fMet1 = 1; + else if ( Init == ABC_INIT_NONE ) + fMetN = 1; + } + if ( Abc_ObjFaninId1(pFanout) == pObj->Id ) + { + Init = Seq_NodeGetInitLast( pFanout, 1 ); + if ( Init == ABC_INIT_ZERO ) + fMet0 = 1; + else if ( Init == ABC_INIT_ONE ) + fMet1 = 1; + else if ( Init == ABC_INIT_NONE ) + fMetN = 1; + } + } + + // consider the case when all fanout latches have don't-care values + // the new values on the fanin edges will be don't-cares + if ( !fMet0 && !fMet1 && !fMetN ) + { + // make sure the label is clean + Abc_ObjForEachFanout( pObj, pFanout, i ) + assert( pFanout->fMarkC == 0 ); + // update the fanout edges + Abc_ObjForEachFanout( pObj, pFanout, i ) + { + if ( pFanout->fMarkC ) + continue; + pFanout->fMarkC = 1; + if ( Abc_ObjFaninId0(pFanout) == pObj->Id ) + Seq_NodeDeleteLast( pFanout, 0 ); + if ( Abc_ObjFaninId1(pFanout) == pObj->Id ) + Seq_NodeDeleteLast( pFanout, 1 ); + } + // clean the label + Abc_ObjForEachFanout( pObj, pFanout, i ) + pFanout->fMarkC = 0; + // update the fanin edges + Abc_ObjRetimeBackwardUpdateEdge( pObj, 0, tTable ); + Abc_ObjRetimeBackwardUpdateEdge( pObj, 1, tTable ); + Seq_NodeInsertFirst( pObj, 0, ABC_INIT_DC ); + Seq_NodeInsertFirst( pObj, 1, ABC_INIT_DC ); + return 0; + } + // the initial values on the fanout edges contain 0, 1, or unknown + // the new values on the fanin edges will be unknown + + // add new AND-gate to the network + pNodeNew = Abc_NtkCreateNode( pNtkNew ); + pNodeNew->pData = Abc_SopCreateAnd2( pNtkNew->pManFunc, Abc_ObjFaninC0(pObj), Abc_ObjFaninC1(pObj) ); + + // add PO fanouts if any + if ( fMet0 ) + { + Abc_ObjAddFanin( Abc_NtkCreatePo(pNtkNew), pNodeNew ); + Vec_IntPush( vValues, 0 ); + } + if ( fMet1 ) + { + Abc_ObjAddFanin( Abc_NtkCreatePo(pNtkNew), pNodeNew ); + Vec_IntPush( vValues, 1 ); + } + + // make sure the label is clean + Abc_ObjForEachFanout( pObj, pFanout, i ) + assert( pFanout->fMarkC == 0 ); + // perform the changes + Abc_ObjForEachFanout( pObj, pFanout, i ) + { + if ( pFanout->fMarkC ) + continue; + pFanout->fMarkC = 1; + if ( Abc_ObjFaninId0(pFanout) == pObj->Id ) + { + Edge = 0; + Value = Seq_NodeDeleteLast( pFanout, Edge ); + if ( Value == ABC_INIT_NONE ) + { + // value is unknown, remove it from the table + RetEdge.iNode = pFanout->Id; + RetEdge.iEdge = Edge; + RetEdge.iLatch = Seq_ObjFaninL( pFanout, Edge ); // after edge is removed + if ( !stmm_delete( tTable, (char **)&RetEdge, (char **)&pFanoutNew ) ) + assert( 0 ); + // create the fanout of the AND gate + Abc_ObjAddFanin( pFanoutNew, pNodeNew ); + } + } + if ( Abc_ObjFaninId1(pFanout) == pObj->Id ) + { + Edge = 1; + Value = Seq_NodeDeleteLast( pFanout, Edge ); + if ( Value == ABC_INIT_NONE ) + { + // value is unknown, remove it from the table + RetEdge.iNode = pFanout->Id; + RetEdge.iEdge = Edge; + RetEdge.iLatch = Seq_ObjFaninL( pFanout, Edge ); // after edge is removed + if ( !stmm_delete( tTable, (char **)&RetEdge, (char **)&pFanoutNew ) ) + assert( 0 ); + // create the fanout of the AND gate + Abc_ObjAddFanin( pFanoutNew, pNodeNew ); + } + } + } + // clean the label + Abc_ObjForEachFanout( pObj, pFanout, i ) + pFanout->fMarkC = 0; + + // update the fanin edges + Abc_ObjRetimeBackwardUpdateEdge( pObj, 0, tTable ); + Abc_ObjRetimeBackwardUpdateEdge( pObj, 1, tTable ); + Seq_NodeInsertFirst( pObj, 0, ABC_INIT_NONE ); + Seq_NodeInsertFirst( pObj, 1, ABC_INIT_NONE ); + + // add the buffer + pBuffer = Abc_NtkCreateNode( pNtkNew ); + pBuffer->pData = Abc_SopCreateBuf( pNtkNew->pManFunc ); + Abc_ObjAddFanin( pNodeNew, pBuffer ); + // point to it from the table + RetEdge.iNode = pObj->Id; + RetEdge.iEdge = 0; + RetEdge.iLatch = 0; + if ( stmm_insert( tTable, (char *)Seq_RetEdge2Int(RetEdge), (char *)pBuffer ) ) + assert( 0 ); + + // add the buffer + pBuffer = Abc_NtkCreateNode( pNtkNew ); + pBuffer->pData = Abc_SopCreateBuf( pNtkNew->pManFunc ); + Abc_ObjAddFanin( pNodeNew, pBuffer ); + // point to it from the table + RetEdge.iNode = pObj->Id; + RetEdge.iEdge = 1; + RetEdge.iLatch = 0; + if ( stmm_insert( tTable, (char *)Seq_RetEdge2Int(RetEdge), (char *)pBuffer ) ) + assert( 0 ); + + // report conflict is found + return fMet0 && fMet1; +} + +/**Function************************************************************* + + Synopsis [Generates the printable edge label with the initial state.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ObjRetimeBackwardUpdateEdge( Abc_Obj_t * pObj, int Edge, stmm_table * tTable ) +{ + Abc_Obj_t * pFanoutNew; + Seq_RetEdge_t RetEdge; + Abc_InitType_t Init; + int nLatches, i; + + // get the number of latches on the edge + nLatches = Seq_ObjFaninL( pObj, Edge ); + for ( i = nLatches - 1; i >= 0; i-- ) + { + // get the value of this latch + Init = Seq_NodeGetInitOne( pObj, Edge, i ); + if ( Init != ABC_INIT_NONE ) + continue; + // get the retiming edge + RetEdge.iNode = pObj->Id; + RetEdge.iEdge = Edge; + RetEdge.iLatch = i; + // remove entry from table and add it with a different key + if ( !stmm_delete( tTable, (char **)&RetEdge, (char **)&pFanoutNew ) ) + assert( 0 ); + RetEdge.iLatch++; + if ( stmm_insert( tTable, (char *)Seq_RetEdge2Int(RetEdge), (char *)pFanoutNew ) ) + assert( 0 ); + } +} + +/**Function************************************************************* + + Synopsis [Sets the initial values.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkRetimeSetInitialValues( Abc_Ntk_t * pNtk, stmm_table * tTable, int * pModel ) +{ + Abc_Obj_t * pNode; + stmm_generator * gen; + Seq_RetEdge_t RetEdge; + Abc_InitType_t Init; + int i; + + i = 0; + stmm_foreach_item( tTable, gen, (char **)&RetEdge, NULL ) + { + pNode = Abc_NtkObj( pNtk, RetEdge.iNode ); + Init = pModel? (pModel[i]? ABC_INIT_ONE : ABC_INIT_ZERO) : ABC_INIT_DC; + Seq_NodeSetInitOne( pNode, RetEdge.iEdge, RetEdge.iLatch, Init ); + i++; + } +} + + + +/**Function************************************************************* + + Synopsis [Performs forward retiming of the sequential AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkUtilRetimingTry( Abc_Ntk_t * pNtk, bool fForward ) +{ + Vec_Ptr_t * vNodes, * vMoves; + Abc_Obj_t * pNode, * pFanout, * pFanin; + int i, k, nLatches; + assert( Abc_NtkIsSeq( pNtk ) ); + // assume that all nodes can be retimed + vNodes = Vec_PtrAlloc( 100 ); + Abc_AigForEachAnd( pNtk, pNode, i ) + { + Vec_PtrPush( vNodes, pNode ); + pNode->fMarkA = 1; + } + // process the nodes + vMoves = Vec_PtrAlloc( 100 ); + Vec_PtrForEachEntry( vNodes, pNode, i ) + { +// printf( "(%d,%d) ", Seq_ObjFaninL0(pNode), Seq_ObjFaninL0(pNode) ); + // unmark the node as processed + pNode->fMarkA = 0; + // get the number of latches to retime + if ( fForward ) + nLatches = Seq_ObjFaninLMin(pNode); + else + nLatches = Seq_ObjFanoutLMin(pNode); + if ( nLatches == 0 ) + continue; + assert( nLatches > 0 ); + // retime the latches forward + if ( fForward ) + Abc_ObjRetimeForwardTry( pNode, nLatches ); + else + Abc_ObjRetimeBackwardTry( pNode, nLatches ); + // write the moves + for ( k = 0; k < nLatches; k++ ) + Vec_PtrPush( vMoves, pNode ); + // schedule fanouts for updating + if ( fForward ) + { + Abc_ObjForEachFanout( pNode, pFanout, k ) + { + if ( Abc_ObjFaninNum(pFanout) != 2 || pFanout->fMarkA ) + continue; + pFanout->fMarkA = 1; + Vec_PtrPush( vNodes, pFanout ); + } + } + else + { + Abc_ObjForEachFanin( pNode, pFanin, k ) + { + if ( Abc_ObjFaninNum(pFanin) != 2 || pFanin->fMarkA ) + continue; + pFanin->fMarkA = 1; + Vec_PtrPush( vNodes, pFanin ); + } + } + } + Vec_PtrFree( vNodes ); + // make sure the marks are clean the the retiming is final + Abc_AigForEachAnd( pNtk, pNode, i ) + { + assert( pNode->fMarkA == 0 ); + if ( fForward ) + assert( Seq_ObjFaninLMin(pNode) == 0 ); + else + assert( Seq_ObjFanoutLMin(pNode) == 0 ); + } + return vMoves; +} + +/**Function************************************************************* + + Synopsis [Translates retiming steps into retiming moves.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkUtilRetimingGetMoves( Abc_Ntk_t * pNtk, Vec_Int_t * vSteps, bool fForward ) +{ + Seq_RetStep_t RetStep; + Vec_Ptr_t * vMoves; + Abc_Obj_t * pNode; + int i, k, iNode, nLatches, Number; + int fChange; + assert( Abc_NtkIsSeq( pNtk ) ); + +/* + // try implementing all the moves at once + Vec_IntForEachEntry( vSteps, Number, i ) + { + // get the retiming step + RetStep = Seq_Int2RetStep( Number ); + // get the node to be retimed + pNode = Abc_NtkObj( pNtk, RetStep.iNode ); + assert( RetStep.nLatches > 0 ); + nLatches = RetStep.nLatches; + + if ( fForward ) + Abc_ObjRetimeForwardTry( pNode, nLatches ); + else + Abc_ObjRetimeBackwardTry( pNode, nLatches ); + } + // now look if any node has wrong number of latches + Abc_AigForEachAnd( pNtk, pNode, i ) + { + if ( Seq_ObjFaninL0(pNode) < 0 ) + printf( "Wrong 0node %d.\n", pNode->Id ); + if ( Seq_ObjFaninL1(pNode) < 0 ) + printf( "Wrong 1node %d.\n", pNode->Id ); + } + // try implementing all the moves at once + Vec_IntForEachEntry( vSteps, Number, i ) + { + // get the retiming step + RetStep = Seq_Int2RetStep( Number ); + // get the node to be retimed + pNode = Abc_NtkObj( pNtk, RetStep.iNode ); + assert( RetStep.nLatches > 0 ); + nLatches = RetStep.nLatches; + + if ( !fForward ) + Abc_ObjRetimeForwardTry( pNode, nLatches ); + else + Abc_ObjRetimeBackwardTry( pNode, nLatches ); + } +*/ + + // process the nodes + vMoves = Vec_PtrAlloc( 100 ); + while ( Vec_IntSize(vSteps) > 0 ) + { + iNode = 0; + fChange = 0; + Vec_IntForEachEntry( vSteps, Number, i ) + { + // get the retiming step + RetStep = Seq_Int2RetStep( Number ); + // get the node to be retimed + pNode = Abc_NtkObj( pNtk, RetStep.iNode ); + assert( RetStep.nLatches > 0 ); + // get the number of latches that can be retimed + if ( fForward ) + nLatches = Seq_ObjFaninLMin(pNode); + else + nLatches = Seq_ObjFanoutLMin(pNode); + if ( nLatches == 0 ) + { + Vec_IntWriteEntry( vSteps, iNode++, Seq_RetStep2Int(RetStep) ); + continue; + } + assert( nLatches > 0 ); + fChange = 1; + // get the number of latches to be retimed over this node + nLatches = ABC_MIN( nLatches, (int)RetStep.nLatches ); + // retime the latches forward + if ( fForward ) + Abc_ObjRetimeForwardTry( pNode, nLatches ); + else + Abc_ObjRetimeBackwardTry( pNode, nLatches ); + // write the moves + for ( k = 0; k < nLatches; k++ ) + Vec_PtrPush( vMoves, pNode ); + // subtract the retiming performed + RetStep.nLatches -= nLatches; + // store the node if it is not retimed completely + if ( RetStep.nLatches > 0 ) + Vec_IntWriteEntry( vSteps, iNode++, Seq_RetStep2Int(RetStep) ); + } + // reduce the array + Vec_IntShrink( vSteps, iNode ); + if ( !fChange ) + { + printf( "Warning: %d strange steps (a minor bug to be fixed later).\n", Vec_IntSize(vSteps) ); +/* + Vec_IntForEachEntry( vSteps, Number, i ) + { + RetStep = Seq_Int2RetStep( Number ); + printf( "%d(%d) ", RetStep.iNode, RetStep.nLatches ); + } + printf( "\n" ); +*/ + break; + } + } + // undo the tentative retiming + if ( fForward ) + { + Vec_PtrForEachEntryReverse( vMoves, pNode, i ) + Abc_ObjRetimeBackwardTry( pNode, 1 ); + } + else + { + Vec_PtrForEachEntryReverse( vMoves, pNode, i ) + Abc_ObjRetimeForwardTry( pNode, 1 ); + } + return vMoves; +} + + +/**Function************************************************************* + + Synopsis [Splits retiming into forward and backward.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Abc_NtkUtilRetimingSplit( Vec_Str_t * vLags, int fForward ) +{ + Vec_Int_t * vNodes; + Seq_RetStep_t RetStep; + int Value, i; + vNodes = Vec_IntAlloc( 100 ); + Vec_StrForEachEntry( vLags, Value, i ) + { + if ( Value < 0 && fForward ) + { + RetStep.iNode = i; + RetStep.nLatches = -Value; + Vec_IntPush( vNodes, Seq_RetStep2Int(RetStep) ); + } + else if ( Value > 0 && !fForward ) + { + RetStep.iNode = i; + RetStep.nLatches = Value; + Vec_IntPush( vNodes, Seq_RetStep2Int(RetStep) ); + } + } + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Retime node forward without initial states.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ObjRetimeForwardTry( Abc_Obj_t * pObj, int nLatches ) +{ + Abc_Obj_t * pFanout; + int i; + // make sure it is an AND gate + assert( Abc_ObjFaninNum(pObj) == 2 ); + // make sure it has enough latches +// assert( Seq_ObjFaninL0(pObj) >= nLatches ); +// assert( Seq_ObjFaninL1(pObj) >= nLatches ); + // subtract these latches on the fanin side + Seq_ObjAddFaninL0( pObj, -nLatches ); + Seq_ObjAddFaninL1( pObj, -nLatches ); + // make sure the label is clean + Abc_ObjForEachFanout( pObj, pFanout, i ) + assert( pFanout->fMarkC == 0 ); + // add these latches on the fanout side + Abc_ObjForEachFanout( pObj, pFanout, i ) + { + if ( pFanout->fMarkC ) + continue; + pFanout->fMarkC = 1; + if ( Abc_ObjFaninId0(pFanout) != Abc_ObjFaninId1(pFanout) ) + Seq_ObjAddFanoutL( pObj, pFanout, nLatches ); + else + { + assert( Abc_ObjFanin0(pFanout) == pObj ); + Seq_ObjAddFaninL0( pFanout, nLatches ); + Seq_ObjAddFaninL1( pFanout, nLatches ); + } + } + // clean the label + Abc_ObjForEachFanout( pObj, pFanout, i ) + pFanout->fMarkC = 0; +} + +/**Function************************************************************* + + Synopsis [Retime node backward without initial states.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ObjRetimeBackwardTry( Abc_Obj_t * pObj, int nLatches ) +{ + Abc_Obj_t * pFanout; + int i; + // make sure it is an AND gate + assert( Abc_ObjFaninNum(pObj) == 2 ); + // make sure the label is clean + Abc_ObjForEachFanout( pObj, pFanout, i ) + assert( pFanout->fMarkC == 0 ); + // subtract these latches on the fanout side + Abc_ObjForEachFanout( pObj, pFanout, i ) + { + if ( pFanout->fMarkC ) + continue; + pFanout->fMarkC = 1; +// assert( Abc_ObjFanoutL(pObj, pFanout) >= nLatches ); + if ( Abc_ObjFaninId0(pFanout) != Abc_ObjFaninId1(pFanout) ) + Seq_ObjAddFanoutL( pObj, pFanout, -nLatches ); + else + { + assert( Abc_ObjFanin0(pFanout) == pObj ); + Seq_ObjAddFaninL0( pFanout, -nLatches ); + Seq_ObjAddFaninL1( pFanout, -nLatches ); + } + } + // clean the label + Abc_ObjForEachFanout( pObj, pFanout, i ) + pFanout->fMarkC = 0; + // add these latches on the fanin side + Seq_ObjAddFaninL0( pObj, nLatches ); + Seq_ObjAddFaninL1( pObj, nLatches ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/seq/seqAigIter.c b/abc_with_bb_support/src/base/seq/seqAigIter.c new file mode 100644 index 000000000..67d133886 --- /dev/null +++ b/abc_with_bb_support/src/base/seq/seqAigIter.c @@ -0,0 +1,268 @@ +/**CFile**************************************************************** + + FileName [seqRetIter.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Construction and manipulation of sequential AIGs.] + + Synopsis [The iterative L-Value computation for retiming procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: seqRetIter.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "seqInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// the internal procedures +static int Seq_RetimeSearch_rec( Abc_Ntk_t * pNtk, int FiMin, int FiMax, int fVerbose ); +static int Seq_RetimeForPeriod( Abc_Ntk_t * pNtk, int Fi, int fVerbose ); +static int Seq_RetimeNodeUpdateLValue( Abc_Obj_t * pObj, int Fi ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Retimes AIG for optimal delay using Pan's algorithm.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_AigRetimeDelayLags( Abc_Ntk_t * pNtk, int fVerbose ) +{ + Abc_Seq_t * p = pNtk->pManFunc; + Abc_Obj_t * pNode; + int i, FiMax, RetValue, clk, clkIter; + char NodeLag; + + assert( Abc_NtkIsSeq( pNtk ) ); + + // get the upper bound on the clock period + FiMax = 2 + Seq_NtkLevelMax(pNtk); + + // make sure this clock period is feasible + if ( !Seq_RetimeForPeriod( pNtk, FiMax, fVerbose ) ) + { + Vec_StrFill( p->vLags, p->nSize, 0 ); + printf( "Error: The upper bound on the clock period cannot be computed.\n" ); + printf( "The reason for this error may be the presence in the circuit of logic\n" ); + printf( "that is not reachable from the PIs. Mapping/retiming is not performed.\n" ); + return 0; + } + + // search for the optimal clock period between 0 and nLevelMax +clk = clock(); + p->FiBestInt = Seq_RetimeSearch_rec( pNtk, 0, FiMax, fVerbose ); +clkIter = clock() - clk; + + // recompute the best l-values + RetValue = Seq_RetimeForPeriod( pNtk, p->FiBestInt, fVerbose ); + assert( RetValue ); + + // fix the problem with non-converged delays + Abc_AigForEachAnd( pNtk, pNode, i ) + if ( Seq_NodeGetLValue(pNode) < -ABC_INFINITY/2 ) + Seq_NodeSetLValue( pNode, 0 ); + + // write the retiming lags + Vec_StrFill( p->vLags, p->nSize, 0 ); + Abc_AigForEachAnd( pNtk, pNode, i ) + { + NodeLag = Seq_NodeComputeLag( Seq_NodeGetLValue(pNode), p->FiBestInt ); + Seq_NodeSetLag( pNode, NodeLag ); + } + + // print the result + if ( fVerbose ) + printf( "The best clock period is %3d.\n", p->FiBestInt ); + +/* + printf( "lvalues and lags : " ); + Abc_AigForEachAnd( pNtk, pNode, i ) + printf( "%d=%d(%d) ", pNode->Id, Seq_NodeGetLValue(pNode), Seq_NodeGetLag(pNode) ); + printf( "\n" ); +*/ +/* + { + FILE * pTable; + pTable = fopen( "stats.txt", "a+" ); + fprintf( pTable, "%s ", pNtk->pName ); + fprintf( pTable, "%d ", FiBest ); + fprintf( pTable, "\n" ); + fclose( pTable ); + } +*/ +/* + { + FILE * pTable; + pTable = fopen( "stats.txt", "a+" ); + fprintf( pTable, "%s ", pNtk->pName ); + fprintf( pTable, "%.2f ", (float)(p->timeCuts)/(float)(CLOCKS_PER_SEC) ); + fprintf( pTable, "%.2f ", (float)(clkIter)/(float)(CLOCKS_PER_SEC) ); + fprintf( pTable, "\n" ); + fclose( pTable ); + } +*/ + return 1; + +} + +/**Function************************************************************* + + Synopsis [Performs binary search for the optimal clock period.] + + Description [Assumes that FiMin is infeasible while FiMax is feasible.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_RetimeSearch_rec( Abc_Ntk_t * pNtk, int FiMin, int FiMax, int fVerbose ) +{ + int Median; + assert( FiMin < FiMax ); + if ( FiMin + 1 == FiMax ) + return FiMax; + Median = FiMin + (FiMax - FiMin)/2; + if ( Seq_RetimeForPeriod( pNtk, Median, fVerbose ) ) + return Seq_RetimeSearch_rec( pNtk, FiMin, Median, fVerbose ); // Median is feasible + else + return Seq_RetimeSearch_rec( pNtk, Median, FiMax, fVerbose ); // Median is infeasible +} + +/**Function************************************************************* + + Synopsis [Returns 1 if retiming with this clock period is feasible.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_RetimeForPeriod( Abc_Ntk_t * pNtk, int Fi, int fVerbose ) +{ + Abc_Seq_t * p = pNtk->pManFunc; + Abc_Obj_t * pObj; + int i, c, RetValue, fChange, Counter; + char * pReason = ""; + + // set l-values of all nodes to be minus infinity + Vec_IntFill( p->vLValues, p->nSize, -ABC_INFINITY ); + + // set l-values of constants and PIs + pObj = Abc_NtkObj( pNtk, 0 ); + Seq_NodeSetLValue( pObj, 0 ); + Abc_NtkForEachPi( pNtk, pObj, i ) + Seq_NodeSetLValue( pObj, 0 ); + + // update all values iteratively + Counter = 0; + for ( c = 0; c < p->nMaxIters; c++ ) + { + fChange = 0; + Abc_AigForEachAnd( pNtk, pObj, i ) + { + Counter++; + if ( Seq_NodeCutMan(pObj) ) + RetValue = Seq_FpgaNodeUpdateLValue( pObj, Fi ); + else + RetValue = Seq_RetimeNodeUpdateLValue( pObj, Fi ); + if ( RetValue == SEQ_UPDATE_YES ) + fChange = 1; + } + Abc_NtkForEachPo( pNtk, pObj, i ) + { + if ( Seq_NodeCutMan(pObj) ) + RetValue = Seq_FpgaNodeUpdateLValue( pObj, Fi ); + else + RetValue = Seq_RetimeNodeUpdateLValue( pObj, Fi ); + if ( RetValue == SEQ_UPDATE_FAIL ) + break; + } + if ( RetValue == SEQ_UPDATE_FAIL ) + break; + if ( fChange == 0 ) + break; + } + if ( c == p->nMaxIters ) + { + RetValue = SEQ_UPDATE_FAIL; + pReason = "(timeout)"; + } + else + c++; + // report the results + if ( fVerbose ) + { + if ( RetValue == SEQ_UPDATE_FAIL ) + printf( "Period = %3d. Iterations = %3d. Updates = %10d. Infeasible %s\n", Fi, c, Counter, pReason ); + else + printf( "Period = %3d. Iterations = %3d. Updates = %10d. Feasible\n", Fi, c, Counter ); + } +/* + // check if any AND gates have infinite delay + Counter = 0; + Abc_AigForEachAnd( pNtk, pObj, i ) + Counter += (Seq_NodeGetLValue(pObj) < -ABC_INFINITY/2); + if ( Counter > 0 ) + printf( "Warning: %d internal nodes have wrong l-values!\n", Counter ); +*/ + return RetValue != SEQ_UPDATE_FAIL; +} + +/**Function************************************************************* + + Synopsis [Computes the l-value of the node.] + + Description [The node can be internal or a PO.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_RetimeNodeUpdateLValue( Abc_Obj_t * pObj, int Fi ) +{ + int lValueNew, lValueOld, lValue0, lValue1; + assert( !Abc_ObjIsPi(pObj) ); + assert( Abc_ObjFaninNum(pObj) > 0 ); + lValue0 = Seq_NodeGetLValue(Abc_ObjFanin0(pObj)) - Fi * Seq_ObjFaninL0(pObj); + if ( Abc_ObjIsPo(pObj) ) + return (lValue0 > Fi)? SEQ_UPDATE_FAIL : SEQ_UPDATE_NO; + if ( Abc_ObjFaninNum(pObj) == 2 ) + lValue1 = Seq_NodeGetLValue(Abc_ObjFanin1(pObj)) - Fi * Seq_ObjFaninL1(pObj); + else + lValue1 = -ABC_INFINITY; + lValueNew = 1 + ABC_MAX( lValue0, lValue1 ); + lValueOld = Seq_NodeGetLValue(pObj); +// if ( lValueNew == lValueOld ) + if ( lValueNew <= lValueOld ) + return SEQ_UPDATE_NO; + Seq_NodeSetLValue( pObj, lValueNew ); + return SEQ_UPDATE_YES; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/seq/seqCreate.c b/abc_with_bb_support/src/base/seq/seqCreate.c new file mode 100644 index 000000000..508dfd97e --- /dev/null +++ b/abc_with_bb_support/src/base/seq/seqCreate.c @@ -0,0 +1,482 @@ +/**CFile**************************************************************** + + FileName [seqCreate.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Construction and manipulation of sequential AIGs.] + + Synopsis [Transformations to and from the sequential AIG.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: seqCreate.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "seqInt.h" + +/* + A sequential network is similar to AIG in that it contains only + AND gates. However, the AND-gates are currently not hashed. + + When converting AIG into sequential AIG: + - Const1/PIs/POs remain the same as in the original AIG. + - Instead of the latches, a new cutset is added, which is currently + defined as a set of AND gates that have a latch among their fanouts. + - The edges of a sequential AIG are labeled with latch attributes + in addition to the complementation attibutes. + - The attributes contain information about the number of latches + and their initial states. + - The number of latches is stored directly on the edges. The initial + states are stored in the sequential AIG manager. + + In the current version of the code, the sequential AIG is static + in the sense that the new AIG nodes are never created. + The retiming (or retiming/mapping) is performed by moving the + latches over the static nodes of the AIG. + The new initial state after backward retiming is computed + by setting up and solving a SAT problem. +*/ + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Abc_Obj_t * Abc_NodeAigToSeq( Abc_Obj_t * pObjNew, Abc_Obj_t * pObj, int Edge, Vec_Int_t * vInitValues ); +static void Abc_NtkAigCutsetCopy( Abc_Ntk_t * pNtk ); +static Abc_Obj_t * Abc_NodeSeqToLogic( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pFanin, Seq_Lat_t * pRing, int nLatches ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + + +/**Function************************************************************* + + Synopsis [Converts combinational AIG with latches into sequential AIG.] + + Description [The const/PI/PO nodes are duplicated. The internal + nodes are duplicated in the topological order. The dangling nodes + are not duplicated. The choice nodes are duplicated.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkAigToSeq( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj, * pFaninNew; + Vec_Int_t * vInitValues; + Abc_InitType_t Init; + int i, k, RetValue; + + // make sure it is an AIG without self-feeding latches + assert( Abc_NtkIsStrash(pNtk) ); + assert( Abc_NtkIsDfsOrdered(pNtk) ); + + if ( RetValue = Abc_NtkRemoveSelfFeedLatches(pNtk) ) + printf( "Modified %d self-feeding latches. The result will not verify.\n", RetValue ); + assert( Abc_NtkCountSelfFeedLatches(pNtk) == 0 ); + + // start the network + pNtkNew = Abc_NtkAlloc( ABC_NTK_SEQ, ABC_FUNC_AIG, 1 ); + // duplicate the name and the spec + pNtkNew->pName = Extra_UtilStrsav(pNtk->pName); + pNtkNew->pSpec = Extra_UtilStrsav(pNtk->pSpec); + + // map the constant nodes + Abc_NtkCleanCopy( pNtk ); + Abc_AigConst1(pNtk)->pCopy = Abc_AigConst1(pNtkNew); + + // copy all objects, except the latches and constant + Vec_PtrFill( pNtkNew->vObjs, Abc_NtkObjNumMax(pNtk), NULL ); + Vec_PtrWriteEntry( pNtkNew->vObjs, 0, Abc_AigConst1(pNtk)->pCopy ); + Abc_NtkForEachObj( pNtk, pObj, i ) + { + if ( i == 0 || Abc_ObjIsLatch(pObj) ) + continue; + pObj->pCopy = Abc_ObjAlloc( pNtkNew, pObj->Type ); + pObj->pCopy->Id = pObj->Id; // the ID is the same for both + pObj->pCopy->fPhase = pObj->fPhase; // used to work with choices + pObj->pCopy->Level = pObj->Level; // used for upper bound on clock cycle + Vec_PtrWriteEntry( pNtkNew->vObjs, pObj->pCopy->Id, pObj->pCopy ); + pNtkNew->nObjs++; + } + pNtkNew->nObjCounts[ABC_OBJ_NODE] = pNtk->nObjCounts[ABC_OBJ_NODE]; + + // create PI/PO and their names + Abc_NtkForEachPi( pNtk, pObj, i ) + { + Vec_PtrPush( pNtkNew->vPis, pObj->pCopy ); + Vec_PtrPush( pNtkNew->vCis, pObj->pCopy ); + Abc_ObjAssignName( pObj->pCopy, Abc_ObjName(pObj), NULL ); + } + Abc_NtkForEachPo( pNtk, pObj, i ) + { + Vec_PtrPush( pNtkNew->vPos, pObj->pCopy ); + Vec_PtrPush( pNtkNew->vCos, pObj->pCopy ); + Abc_ObjAssignName( pObj->pCopy, Abc_ObjName(pObj), NULL ); + } + Abc_NtkForEachAssert( pNtk, pObj, i ) + { + Vec_PtrPush( pNtkNew->vAsserts, pObj->pCopy ); + Vec_PtrPush( pNtkNew->vCos, pObj->pCopy ); + Abc_ObjAssignName( pObj->pCopy, Abc_ObjName(pObj), NULL ); + } + + // relink the choice nodes + Abc_AigForEachAnd( pNtk, pObj, i ) + if ( pObj->pData ) + pObj->pCopy->pData = ((Abc_Obj_t *)pObj->pData)->pCopy; + + // start the storage for initial states + Seq_Resize( pNtkNew->pManFunc, Abc_NtkObjNumMax(pNtkNew) ); + // reconnect the internal nodes + vInitValues = Vec_IntAlloc( 100 ); + Abc_NtkForEachObj( pNtk, pObj, i ) + { + // skip constants, PIs, and latches + if ( Abc_ObjFaninNum(pObj) == 0 || Abc_ObjIsLatch(pObj) ) + continue; + // process the first fanin + Vec_IntClear( vInitValues ); + pFaninNew = Abc_NodeAigToSeq( pObj->pCopy, pObj, 0, vInitValues ); + Abc_ObjAddFanin( pObj->pCopy, pFaninNew ); + // store the initial values + Vec_IntForEachEntry( vInitValues, Init, k ) + Seq_NodeInsertFirst( pObj->pCopy, 0, Init ); + // skip single-input nodes + if ( Abc_ObjFaninNum(pObj) == 1 ) + continue; + // process the second fanin + Vec_IntClear( vInitValues ); + pFaninNew = Abc_NodeAigToSeq( pObj->pCopy, pObj, 1, vInitValues ); + Abc_ObjAddFanin( pObj->pCopy, pFaninNew ); + // store the initial values + Vec_IntForEachEntry( vInitValues, Init, k ) + Seq_NodeInsertFirst( pObj->pCopy, 1, Init ); + } + Vec_IntFree( vInitValues ); + + // set the cutset composed of latch drivers + Abc_NtkAigCutsetCopy( pNtk ); + Seq_NtkLatchGetEqualFaninNum( pNtkNew ); + + // copy EXDC and check correctness + if ( pNtk->pExdc ) + fprintf( stdout, "Warning: EXDC is not copied when converting to sequential AIG.\n" ); + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkAigToSeq(): Network check has failed.\n" ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Determines the fanin that is transparent for latches.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NodeAigToSeq( Abc_Obj_t * pObjNew, Abc_Obj_t * pObj, int Edge, Vec_Int_t * vInitValues ) +{ + Abc_Obj_t * pFanin, * pFaninNew; + Abc_InitType_t Init; + // get the given fanin of the node + pFanin = Abc_ObjFanin( pObj, Edge ); + // if fanin is the internal node, return its copy in the corresponding polarity + if ( !Abc_ObjIsLatch(pFanin) ) + return Abc_ObjNotCond( pFanin->pCopy, Abc_ObjFaninC(pObj, Edge) ); + // fanin is a latch + // get the new fanins + pFaninNew = Abc_NodeAigToSeq( pObjNew, pFanin, 0, vInitValues ); + // get the initial state + Init = Abc_LatchInit(pFanin); + // complement the initial state if the inv is retimed over the latch + if ( Abc_ObjIsComplement(pFaninNew) ) + { + if ( Init == ABC_INIT_ZERO ) + Init = ABC_INIT_ONE; + else if ( Init == ABC_INIT_ONE ) + Init = ABC_INIT_ZERO; + else if ( Init != ABC_INIT_DC ) + assert( 0 ); + } + // record the initial state + Vec_IntPush( vInitValues, Init ); + return Abc_ObjNotCond( pFaninNew, Abc_ObjFaninC(pObj, Edge) ); +} + +/**Function************************************************************* + + Synopsis [Collects the cut set nodes.] + + Description [These are internal AND gates that have latch fanouts.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkAigCutsetCopy( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pLatch, * pDriver, * pDriverNew; + int i; + Abc_NtkIncrementTravId(pNtk); + Abc_NtkForEachLatch( pNtk, pLatch, i ) + { + pDriver = Abc_ObjFanin0(pLatch); + if ( Abc_NodeIsTravIdCurrent(pDriver) || !Abc_AigNodeIsAnd(pDriver) ) + continue; + Abc_NodeSetTravIdCurrent(pDriver); + pDriverNew = pDriver->pCopy; + Vec_PtrPush( pDriverNew->pNtk->vCutSet, pDriverNew ); + } +} + +/**Function************************************************************* + + Synopsis [Converts a sequential AIG into a logic SOP network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkSeqToLogicSop( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj, * pFaninNew; + Seq_Lat_t * pRing; + int i; + + assert( Abc_NtkIsSeq(pNtk) ); + // start the network without latches + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_LOGIC, ABC_FUNC_SOP ); + // duplicate the nodes + Abc_AigForEachAnd( pNtk, pObj, i ) + { + Abc_NtkDupObj(pNtkNew, pObj, 0); + pObj->pCopy->pData = Abc_SopCreateAnd2( pNtkNew->pManFunc, Abc_ObjFaninC0(pObj), Abc_ObjFaninC1(pObj) ); + } + // share and create the latches + Seq_NtkShareLatches( pNtkNew, pNtk ); + // connect the objects + Abc_AigForEachAnd( pNtk, pObj, i ) + { + if ( pRing = Seq_NodeGetRing(pObj,0) ) + pFaninNew = pRing->pLatch; + else + pFaninNew = Abc_ObjFanin0(pObj)->pCopy; + Abc_ObjAddFanin( pObj->pCopy, pFaninNew ); + + if ( pRing = Seq_NodeGetRing(pObj,1) ) + pFaninNew = pRing->pLatch; + else + pFaninNew = Abc_ObjFanin1(pObj)->pCopy; + Abc_ObjAddFanin( pObj->pCopy, pFaninNew ); + } + // connect the POs + Abc_NtkForEachPo( pNtk, pObj, i ) + { + if ( pRing = Seq_NodeGetRing(pObj,0) ) + pFaninNew = pRing->pLatch; + else + pFaninNew = Abc_ObjFanin0(pObj)->pCopy; + pFaninNew = Abc_ObjNotCond( pFaninNew, Abc_ObjFaninC0(pObj) ); + Abc_ObjAddFanin( pObj->pCopy, pFaninNew ); + } + // clean the latch pointers + Seq_NtkShareLatchesClean( pNtk ); + + // add the latches and their names + Abc_NtkAddDummyBoxNames( pNtkNew ); + Abc_NtkOrderCisCos( pNtkNew ); + // fix the problem with complemented and duplicated CO edges + Abc_NtkLogicMakeSimpleCos( pNtkNew, 0 ); + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkSeqToLogicSop(): Network check has failed.\n" ); + return pNtkNew; +} + + +/**Function************************************************************* + + Synopsis [Converts a sequential AIG into a logic SOP network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkSeqToLogicSop_old( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj, * pFaninNew; + int i; + + assert( Abc_NtkIsSeq(pNtk) ); + // start the network without latches + pNtkNew = Abc_NtkStartFrom( pNtk, ABC_NTK_LOGIC, ABC_FUNC_SOP ); + + // duplicate the nodes, create node functions + Abc_NtkForEachNode( pNtk, pObj, i ) + { + // skip the constant + if ( Abc_ObjFaninNum(pObj) == 0 ) + continue; + // duplicate the node + Abc_NtkDupObj(pNtkNew, pObj, 0); + if ( Abc_ObjFaninNum(pObj) == 1 ) + { + assert( !Abc_ObjFaninC0(pObj) ); + pObj->pCopy->pData = Abc_SopCreateBuf( pNtkNew->pManFunc ); + continue; + } + pObj->pCopy->pData = Abc_SopCreateAnd2( pNtkNew->pManFunc, Abc_ObjFaninC0(pObj), Abc_ObjFaninC1(pObj) ); + } + // connect the objects + Abc_NtkForEachObj( pNtk, pObj, i ) + { + assert( (int)pObj->Id == i ); + // skip PIs and the constant + if ( Abc_ObjFaninNum(pObj) == 0 ) + continue; + // create the edge + pFaninNew = Abc_NodeSeqToLogic( pNtkNew, Abc_ObjFanin0(pObj), Seq_NodeGetRing(pObj,0), Seq_ObjFaninL0(pObj) ); + Abc_ObjAddFanin( pObj->pCopy, pFaninNew ); + if ( Abc_ObjFaninNum(pObj) == 1 ) + { + // create the complemented edge + if ( Abc_ObjFaninC0(pObj) ) + Abc_ObjSetFaninC( pObj->pCopy, 0 ); + continue; + } + // create the edge + pFaninNew = Abc_NodeSeqToLogic( pNtkNew, Abc_ObjFanin1(pObj), Seq_NodeGetRing(pObj,1), Seq_ObjFaninL1(pObj) ); + Abc_ObjAddFanin( pObj->pCopy, pFaninNew ); + // the complemented edges are subsumed by the node function + } + // add the latches and their names + Abc_NtkAddDummyBoxNames( pNtkNew ); + Abc_NtkOrderCisCos( pNtkNew ); + // fix the problem with complemented and duplicated CO edges + Abc_NtkLogicMakeSimpleCos( pNtkNew, 0 ); + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkSeqToLogicSop(): Network check has failed.\n" ); + return pNtkNew; +} + + +/**Function************************************************************* + + Synopsis [Creates latches on one edge.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NodeSeqToLogic( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pFanin, Seq_Lat_t * pRing, int nLatches ) +{ + Abc_Obj_t * pLatch; + if ( nLatches == 0 ) + { + assert( pFanin->pCopy ); + return pFanin->pCopy; + } + pFanin = Abc_NodeSeqToLogic( pNtkNew, pFanin, Seq_LatNext(pRing), nLatches - 1 ); + pLatch = Abc_NtkCreateLatch( pNtkNew ); + pLatch->pData = (void *)Seq_LatInit( pRing ); + Abc_ObjAddFanin( pLatch, pFanin ); + return pLatch; +} + +/**Function************************************************************* + + Synopsis [Makes sure that every node in the table is in the network and vice versa.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Abc_NtkSeqCheck( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i, nFanins; + Abc_NtkForEachNode( pNtk, pObj, i ) + { + nFanins = Abc_ObjFaninNum(pObj); + if ( nFanins == 0 ) + { + if ( pObj != Abc_AigConst1(pNtk) ) + { + printf( "Abc_SeqCheck: The AIG has non-standard constant nodes.\n" ); + return 0; + } + continue; + } + if ( nFanins == 1 ) + { + printf( "Abc_SeqCheck: The AIG has single input nodes.\n" ); + return 0; + } + if ( nFanins > 2 ) + { + printf( "Abc_SeqCheck: The AIG has non-standard nodes.\n" ); + return 0; + } + } + // check the correctness of the internal representation of the initial states + Abc_NtkForEachObj( pNtk, pObj, i ) + { + nFanins = Abc_ObjFaninNum(pObj); + if ( nFanins == 0 ) + continue; + if ( nFanins == 1 ) + { + if ( Seq_NodeCountLats(pObj, 0) != Seq_ObjFaninL0(pObj) ) + { + printf( "Abc_SeqCheck: Node %d has mismatch in the number of latches.\n", Abc_ObjName(pObj) ); + return 0; + } + } + // look at both inputs + if ( Seq_NodeCountLats(pObj, 0) != Seq_ObjFaninL0(pObj) ) + { + printf( "Abc_SeqCheck: The first fanin of node %d has mismatch in the number of latches.\n", Abc_ObjName(pObj) ); + return 0; + } + if ( Seq_NodeCountLats(pObj, 1) != Seq_ObjFaninL1(pObj) ) + { + printf( "Abc_SeqCheck: The second fanin of node %d has mismatch in the number of latches.\n", Abc_ObjName(pObj) ); + return 0; + } + } + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/seq/seqFpgaCore.c b/abc_with_bb_support/src/base/seq/seqFpgaCore.c new file mode 100644 index 000000000..243119b7a --- /dev/null +++ b/abc_with_bb_support/src/base/seq/seqFpgaCore.c @@ -0,0 +1,643 @@ +/**CFile**************************************************************** + + FileName [seqFpgaCore.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Construction and manipulation of sequential AIGs.] + + Synopsis [The core of FPGA mapping/retiming package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: seqFpgaCore.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "seqInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Abc_Ntk_t * Seq_NtkFpgaDup( Abc_Ntk_t * pNtk ); +static int Seq_NtkFpgaInitCompatible( Abc_Ntk_t * pNtk, int fVerbose ); +static Abc_Ntk_t * Seq_NtkSeqFpgaMapped( Abc_Ntk_t * pNtkNew ); +static int Seq_FpgaMappingCount( Abc_Ntk_t * pNtk ); +static int Seq_FpgaMappingCount_rec( Abc_Ntk_t * pNtk, unsigned SeqEdge, Vec_Ptr_t * vLeaves ); +static Abc_Obj_t * Seq_FpgaMappingBuild_rec( Abc_Ntk_t * pNtkNew, Abc_Ntk_t * pNtk, unsigned SeqEdge, int fTop, int LagCut, Vec_Ptr_t * vLeaves ); +static DdNode * Seq_FpgaMappingBdd_rec( DdManager * dd, Abc_Ntk_t * pNtk, unsigned SeqEdge, Vec_Ptr_t * vLeaves ); +static void Seq_FpgaMappingEdges_rec( Abc_Ntk_t * pNtk, unsigned SeqEdge, Abc_Obj_t * pPrev, Vec_Ptr_t * vLeaves, Vec_Vec_t * vMapEdges ); +static void Seq_FpgaMappingConnect_rec( Abc_Ntk_t * pNtk, unsigned SeqEdge, Abc_Obj_t * pPrev, int Edge, Abc_Obj_t * pRoot, Vec_Ptr_t * vLeaves ); +static DdNode * Seq_FpgaMappingConnectBdd_rec( Abc_Ntk_t * pNtk, unsigned SeqEdge, Abc_Obj_t * pPrev, int Edge, Abc_Obj_t * pRoot, Vec_Ptr_t * vLeaves ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs FPGA mapping and retiming.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Seq_NtkFpgaMapRetime( Abc_Ntk_t * pNtk, int nMaxIters, int fVerbose ) +{ + Abc_Seq_t * p = pNtk->pManFunc; + Abc_Ntk_t * pNtkNew; + Abc_Ntk_t * pNtkMap; + int RetValue; + + // get the LUT library + p->nVarsMax = Fpga_LutLibReadVarMax( Abc_FrameReadLibLut() ); + p->nMaxIters = nMaxIters; + + // find the best mapping and retiming for all nodes (p->vLValues, p->vBestCuts, p->vLags) + if ( !Seq_FpgaMappingDelays( pNtk, fVerbose ) ) + return NULL; + if ( RetValue = Abc_NtkGetChoiceNum(pNtk) ) + { + printf( "The network has %d choices. The resulting network is not derived (this is temporary).\n", RetValue ); + printf( "The mininum clock period computed is %d.\n", p->FiBestInt ); + return NULL; + } + + // duplicate the nodes contained in multiple cuts + pNtkNew = Seq_NtkFpgaDup( pNtk ); +// return pNtkNew; + + // implement the retiming + RetValue = Seq_NtkImplementRetiming( pNtkNew, ((Abc_Seq_t *)pNtkNew->pManFunc)->vLags, fVerbose ); + if ( RetValue == 0 ) + printf( "Retiming completed but initial state computation has failed.\n" ); +// return pNtkNew; + + // check the compatibility of initial states computed + if ( RetValue = Seq_NtkFpgaInitCompatible( pNtkNew, fVerbose ) ) + printf( "The number of LUTs with incompatible edges = %d.\n", RetValue ); + + // create the final mapped network + pNtkMap = Seq_NtkSeqFpgaMapped( pNtkNew ); + Abc_NtkDelete( pNtkNew ); + if ( RetValue ) + printf( "The number of LUTs with more than %d inputs = %d.\n", + p->nVarsMax, Seq_NtkCountNodesAboveLimit(pNtkMap, p->nVarsMax) ); + return pNtkMap; +} + +/**Function************************************************************* + + Synopsis [Derives the network by duplicating some of the nodes.] + + Description [Information about mapping is given as mapping nodes (p->vMapAnds) + and best cuts for each node (p->vMapCuts).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Seq_NtkFpgaDup( Abc_Ntk_t * pNtk ) +{ + Abc_Seq_t * pNew, * p = pNtk->pManFunc; + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj, * pLeaf; + Vec_Ptr_t * vLeaves; + unsigned SeqEdge; + int i, k, nObjsNew, Lag; + + assert( Abc_NtkIsSeq(pNtk) ); + + // start the expanded network + pNtkNew = Abc_NtkStartFrom( pNtk, pNtk->ntkType, pNtk->ntkFunc ); + + // start the new sequential AIG manager + nObjsNew = 1 + Abc_NtkPiNum(pNtk) + Abc_NtkPoNum(pNtk) + Seq_FpgaMappingCount(pNtk); + Seq_Resize( pNtkNew->pManFunc, nObjsNew ); + + // duplicate the nodes in the mapping + Vec_PtrForEachEntry( p->vMapAnds, pObj, i ) + Abc_NtkDupObj( pNtkNew, pObj, 0 ); + + // recursively construct the internals of each node + Vec_PtrForEachEntry( p->vMapAnds, pObj, i ) + { + vLeaves = Vec_VecEntry( p->vMapCuts, i ); + Seq_FpgaMappingBuild_rec( pNtkNew, pNtk, pObj->Id << 8, 1, Seq_NodeGetLag(pObj), vLeaves ); + } + assert( nObjsNew == pNtkNew->nObjs ); + + // set the POs + Abc_NtkFinalize( pNtk, pNtkNew ); + // duplicate the latches on the PO edges + Abc_NtkForEachPo( pNtk, pObj, i ) + Seq_NodeDupLats( pObj->pCopy, pObj, 0 ); + + // transfer the mapping info to the new manager + Vec_PtrForEachEntry( p->vMapAnds, pObj, i ) + { + // get the leaves of the cut + vLeaves = Vec_VecEntry( p->vMapCuts, i ); + // convert the leaf nodes + Vec_PtrForEachEntry( vLeaves, pLeaf, k ) + { + SeqEdge = (unsigned)pLeaf; + pLeaf = Abc_NtkObj( pNtk, SeqEdge >> 8 ); + Lag = (SeqEdge & 255) + Seq_NodeGetLag(pObj) - Seq_NodeGetLag(pLeaf); + assert( Lag >= 0 ); + // translate the old leaf into the leaf in the new network + Vec_PtrWriteEntry( vLeaves, k, (void *)((pLeaf->pCopy->Id << 8) | Lag) ); +// printf( "%d -> %d\n", pLeaf->Id, pLeaf->pCopy->Id ); + } + // convert the root node + Vec_PtrWriteEntry( p->vMapAnds, i, pObj->pCopy ); + } + pNew = pNtkNew->pManFunc; + pNew->nVarsMax = p->nVarsMax; + pNew->vMapAnds = p->vMapAnds; p->vMapAnds = NULL; + pNew->vMapCuts = p->vMapCuts; p->vMapCuts = NULL; + + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Seq_NtkFpgaDup(): Network check has failed.\n" ); + return pNtkNew; +} + + +/**Function************************************************************* + + Synopsis [Checks if the initial states are compatible.] + + Description [Checks of all the initial states on the fanins edges + of the cut have compatible number of latches and initial states. + If this is not true, then the mapped network with the does not have initial + state. Returns the number of LUTs with incompatible edges.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_NtkFpgaInitCompatible( Abc_Ntk_t * pNtk, int fVerbose ) +{ + Abc_Seq_t * p = pNtk->pManFunc; + Abc_Obj_t * pAnd, * pLeaf, * pFanout0, * pFanout1; + Vec_Vec_t * vTotalEdges; + Vec_Ptr_t * vLeaves, * vEdges; + int i, k, m, Edge0, Edge1, nLatchAfter, nLatches1, nLatches2; + unsigned SeqEdge; + int CountBad = 0, CountAll = 0; + + vTotalEdges = Vec_VecStart( p->nVarsMax ); + // go through all the nodes (cuts) used in the mapping + Vec_PtrForEachEntry( p->vMapAnds, pAnd, i ) + { +// printf( "*** Node %d.\n", pAnd->Id ); + + // get the cut of this gate + vLeaves = Vec_VecEntry( p->vMapCuts, i ); + + // get the edges pointing to the leaves + Vec_VecClear( vTotalEdges ); + Seq_FpgaMappingEdges_rec( pNtk, pAnd->Id << 8, NULL, vLeaves, vTotalEdges ); + + // for each leaf, consider its edges + Vec_PtrForEachEntry( vLeaves, pLeaf, k ) + { + SeqEdge = (unsigned)pLeaf; + pLeaf = Abc_NtkObj( pNtk, SeqEdge >> 8 ); + nLatchAfter = SeqEdge & 255; + if ( nLatchAfter == 0 ) + continue; + + // go through the edges + vEdges = Vec_VecEntry( vTotalEdges, k ); + pFanout0 = NULL; + Vec_PtrForEachEntry( vEdges, pFanout1, m ) + { + Edge1 = Abc_ObjIsComplement(pFanout1); + pFanout1 = Abc_ObjRegular(pFanout1); +//printf( "Fanin = %d. Fanout = %d.\n", pLeaf->Id, pFanout1->Id ); + + // make sure this is the same fanin + if ( Edge1 ) + assert( pLeaf == Abc_ObjFanin1(pFanout1) ); + else + assert( pLeaf == Abc_ObjFanin0(pFanout1) ); + + // save the first one + if ( pFanout0 == NULL ) + { + pFanout0 = pFanout1; + Edge0 = Edge1; + continue; + } + // compare the rings + // if they have different number of latches, this is the bug + nLatches1 = Seq_NodeCountLats(pFanout0, Edge0); + nLatches2 = Seq_NodeCountLats(pFanout1, Edge1); + assert( nLatches1 == nLatches2 ); + assert( nLatches1 == nLatchAfter ); + assert( nLatches1 > 0 ); + + // if they have different initial states, this is the problem + if ( !Seq_NodeCompareLats(pFanout0, Edge0, pFanout1, Edge1) ) + { + CountBad++; + break; + } + CountAll++; + } + } + } + if ( fVerbose ) + printf( "The number of pairs of edges checked = %d.\n", CountAll ); + Vec_VecFree( vTotalEdges ); + return CountBad; +} + +/**Function************************************************************* + + Synopsis [Derives the final mapped network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Seq_NtkSeqFpgaMapped( Abc_Ntk_t * pNtk ) +{ + Abc_Seq_t * p = pNtk->pManFunc; + Abc_Ntk_t * pNtkMap; + Vec_Ptr_t * vLeaves; + Abc_Obj_t * pObj, * pFaninNew; + Seq_Lat_t * pRing; + int i; + + assert( Abc_NtkIsSeq(pNtk) ); + + // start the network + pNtkMap = Abc_NtkStartFrom( pNtk, ABC_NTK_LOGIC, ABC_FUNC_BDD ); + + // duplicate the nodes used in the mapping + Vec_PtrForEachEntry( p->vMapAnds, pObj, i ) + pObj->pCopy = Abc_NtkCreateNode( pNtkMap ); + + // create and share the latches + Seq_NtkShareLatchesMapping( pNtkMap, pNtk, p->vMapAnds, 1 ); + + // connect the nodes + Vec_PtrForEachEntry( p->vMapAnds, pObj, i ) + { + // get the leaves of this gate + vLeaves = Vec_VecEntry( p->vMapCuts, i ); + // get the BDD of the node + pObj->pCopy->pData = Seq_FpgaMappingConnectBdd_rec( pNtk, pObj->Id << 8, NULL, -1, pObj, vLeaves ); + Cudd_Ref( pObj->pCopy->pData ); + // complement the BDD of the cut if it came from the opposite polarity choice cut +// if ( Vec_StrEntry(p->vPhase, i) ) +// pObj->pCopy->pData = Cudd_Not( pObj->pCopy->pData ); + } + + // set the POs + Abc_NtkForEachPo( pNtk, pObj, i ) + { + if ( pRing = Seq_NodeGetRing(pObj,0) ) + pFaninNew = pRing->pLatch; + else + pFaninNew = Abc_ObjFanin0(pObj)->pCopy; + pFaninNew = Abc_ObjNotCond( pFaninNew, Abc_ObjFaninC0(pObj) ); + Abc_ObjAddFanin( pObj->pCopy, pFaninNew ); + } + + // add the latches and their names + Abc_NtkAddDummyBoxNames( pNtkMap ); + Abc_NtkOrderCisCos( pNtkMap ); + // fix the problem with complemented and duplicated CO edges + Abc_NtkLogicMakeSimpleCos( pNtkMap, 1 ); + // make the network minimum base + Abc_NtkMinimumBase( pNtkMap ); + if ( !Abc_NtkCheck( pNtkMap ) ) + fprintf( stdout, "Seq_NtkSeqFpgaMapped(): Network check has failed.\n" ); + return pNtkMap; +} + + +/**Function************************************************************* + + Synopsis [Counts the number of nodes in the bag.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_FpgaMappingCount( Abc_Ntk_t * pNtk ) +{ + Abc_Seq_t * p = pNtk->pManFunc; + Vec_Ptr_t * vLeaves; + Abc_Obj_t * pAnd; + int i, Counter = 0; + Vec_PtrForEachEntry( p->vMapAnds, pAnd, i ) + { + vLeaves = Vec_VecEntry( p->vMapCuts, i ); + Counter += Seq_FpgaMappingCount_rec( pNtk, pAnd->Id << 8, vLeaves ); + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Counts the number of nodes in the bag.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_FpgaMappingCount_rec( Abc_Ntk_t * pNtk, unsigned SeqEdge, Vec_Ptr_t * vLeaves ) +{ + Abc_Obj_t * pObj, * pLeaf; + unsigned SeqEdge0, SeqEdge1; + int Lag, i; + // get the object and the lag + pObj = Abc_NtkObj( pNtk, SeqEdge >> 8 ); + Lag = SeqEdge & 255; + // if the node is the fanin of the cut, return + Vec_PtrForEachEntry( vLeaves, pLeaf, i ) + if ( SeqEdge == (unsigned)pLeaf ) + return 0; + // continue unfolding + assert( Abc_AigNodeIsAnd(pObj) ); + // get new sequential edges + assert( Lag + Seq_ObjFaninL0(pObj) < 255 ); + assert( Lag + Seq_ObjFaninL1(pObj) < 255 ); + SeqEdge0 = (Abc_ObjFanin0(pObj)->Id << 8) + Lag + Seq_ObjFaninL0(pObj); + SeqEdge1 = (Abc_ObjFanin1(pObj)->Id << 8) + Lag + Seq_ObjFaninL1(pObj); + // call for the children + return 1 + Seq_FpgaMappingCount_rec( pNtk, SeqEdge0, vLeaves ) + + Seq_FpgaMappingCount_rec( pNtk, SeqEdge1, vLeaves ); +} + +/**Function************************************************************* + + Synopsis [Collects the edges pointing to the leaves of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Seq_FpgaMappingBuild_rec( Abc_Ntk_t * pNtkNew, Abc_Ntk_t * pNtk, unsigned SeqEdge, int fTop, int LagCut, Vec_Ptr_t * vLeaves ) +{ + Abc_Obj_t * pObj, * pObjNew, * pLeaf, * pFaninNew0, * pFaninNew1; + unsigned SeqEdge0, SeqEdge1; + int Lag, i; + // get the object and the lag + pObj = Abc_NtkObj( pNtk, SeqEdge >> 8 ); + Lag = SeqEdge & 255; + // if the node is the fanin of the cut, return + Vec_PtrForEachEntry( vLeaves, pLeaf, i ) + if ( SeqEdge == (unsigned)pLeaf ) + return pObj->pCopy; + // continue unfolding + assert( Abc_AigNodeIsAnd(pObj) ); + // get new sequential edges + assert( Lag + Seq_ObjFaninL0(pObj) < 255 ); + assert( Lag + Seq_ObjFaninL1(pObj) < 255 ); + SeqEdge0 = (Abc_ObjFanin0(pObj)->Id << 8) + Lag + Seq_ObjFaninL0(pObj); + SeqEdge1 = (Abc_ObjFanin1(pObj)->Id << 8) + Lag + Seq_ObjFaninL1(pObj); + // call for the children + pObjNew = fTop? pObj->pCopy : Abc_NtkCreateNode( pNtkNew ); + // solve subproblems + pFaninNew0 = Seq_FpgaMappingBuild_rec( pNtkNew, pNtk, SeqEdge0, 0, LagCut, vLeaves ); + pFaninNew1 = Seq_FpgaMappingBuild_rec( pNtkNew, pNtk, SeqEdge1, 0, LagCut, vLeaves ); + // add the fanins to the node + Abc_ObjAddFanin( pObjNew, Abc_ObjNotCond( pFaninNew0, Abc_ObjFaninC0(pObj) ) ); + Abc_ObjAddFanin( pObjNew, Abc_ObjNotCond( pFaninNew1, Abc_ObjFaninC1(pObj) ) ); + Seq_NodeDupLats( pObjNew, pObj, 0 ); + Seq_NodeDupLats( pObjNew, pObj, 1 ); + // set the lag of the new node equal to the internal lag plus mapping/retiming lag + Seq_NodeSetLag( pObjNew, (char)(Lag + LagCut) ); +// Seq_NodeSetLag( pObjNew, (char)(Lag) ); + return pObjNew; +} + +/**Function************************************************************* + + Synopsis [Derives the BDD of the selected cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Seq_FpgaMappingBdd_rec( DdManager * dd, Abc_Ntk_t * pNtk, unsigned SeqEdge, Vec_Ptr_t * vLeaves ) +{ + Abc_Obj_t * pObj, * pLeaf; + DdNode * bFunc0, * bFunc1, * bFunc; + unsigned SeqEdge0, SeqEdge1; + int Lag, i; + // get the object and the lag + pObj = Abc_NtkObj( pNtk, SeqEdge >> 8 ); + Lag = SeqEdge & 255; + // if the node is the fanin of the cut, return + Vec_PtrForEachEntry( vLeaves, pLeaf, i ) + if ( SeqEdge == (unsigned)pLeaf ) + return Cudd_bddIthVar( dd, i ); + // continue unfolding + assert( Abc_AigNodeIsAnd(pObj) ); + // get new sequential edges + assert( Lag + Seq_ObjFaninL0(pObj) < 255 ); + assert( Lag + Seq_ObjFaninL1(pObj) < 255 ); + SeqEdge0 = (Abc_ObjFanin0(pObj)->Id << 8) + Lag + Seq_ObjFaninL0(pObj); + SeqEdge1 = (Abc_ObjFanin1(pObj)->Id << 8) + Lag + Seq_ObjFaninL1(pObj); + // call for the children + bFunc0 = Seq_FpgaMappingBdd_rec( dd, pNtk, SeqEdge0, vLeaves ); Cudd_Ref( bFunc0 ); + bFunc1 = Seq_FpgaMappingBdd_rec( dd, pNtk, SeqEdge1, vLeaves ); Cudd_Ref( bFunc1 ); + bFunc0 = Cudd_NotCond( bFunc0, Abc_ObjFaninC0(pObj) ); + bFunc1 = Cudd_NotCond( bFunc1, Abc_ObjFaninC1(pObj) ); + // get the BDD of the node + bFunc = Cudd_bddAnd( dd, bFunc0, bFunc1 ); Cudd_Ref( bFunc ); + Cudd_RecursiveDeref( dd, bFunc0 ); + Cudd_RecursiveDeref( dd, bFunc1 ); + // return the BDD + Cudd_Deref( bFunc ); + return bFunc; +} + +/**Function************************************************************* + + Synopsis [Collects the edges pointing to the leaves of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_FpgaMappingEdges_rec( Abc_Ntk_t * pNtk, unsigned SeqEdge, Abc_Obj_t * pPrev, Vec_Ptr_t * vLeaves, Vec_Vec_t * vMapEdges ) +{ + Abc_Obj_t * pObj, * pLeaf; + unsigned SeqEdge0, SeqEdge1; + int Lag, i; + // get the object and the lag + pObj = Abc_NtkObj( pNtk, SeqEdge >> 8 ); + Lag = SeqEdge & 255; + // if the node is the fanin of the cut, return + Vec_PtrForEachEntry( vLeaves, pLeaf, i ) + { + if ( SeqEdge == (unsigned)pLeaf ) + { + assert( pPrev != NULL ); + Vec_VecPush( vMapEdges, i, pPrev ); + return; + } + } + // continue unfolding + assert( Abc_AigNodeIsAnd(pObj) ); + // get new sequential edges + assert( Lag + Seq_ObjFaninL0(pObj) < 255 ); + assert( Lag + Seq_ObjFaninL1(pObj) < 255 ); + SeqEdge0 = (Abc_ObjFanin0(pObj)->Id << 8) + Lag + Seq_ObjFaninL0(pObj); + SeqEdge1 = (Abc_ObjFanin1(pObj)->Id << 8) + Lag + Seq_ObjFaninL1(pObj); + // call for the children + Seq_FpgaMappingEdges_rec( pNtk, SeqEdge0, pObj , vLeaves, vMapEdges ); + Seq_FpgaMappingEdges_rec( pNtk, SeqEdge1, Abc_ObjNot(pObj), vLeaves, vMapEdges ); +} + +/**Function************************************************************* + + Synopsis [Collects the edges pointing to the leaves of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_FpgaMappingConnect_rec( Abc_Ntk_t * pNtk, unsigned SeqEdge, Abc_Obj_t * pPrev, int Edge, Abc_Obj_t * pRoot, Vec_Ptr_t * vLeaves ) +{ + Seq_Lat_t * pRing; + Abc_Obj_t * pObj, * pLeaf, * pFanin, * pFaninNew; + unsigned SeqEdge0, SeqEdge1; + int Lag, i, k; + // get the object and the lag + pObj = Abc_NtkObj( pNtk, SeqEdge >> 8 ); + Lag = SeqEdge & 255; + // if the node is the fanin of the cut, add the connection and return + Vec_PtrForEachEntry( vLeaves, pLeaf, i ) + { + if ( SeqEdge == (unsigned)pLeaf ) + { + assert( pPrev != NULL ); + if ( pRing = Seq_NodeGetRing(pPrev,Edge) ) + pFaninNew = pRing->pLatch; + else + pFaninNew = Abc_ObjFanin(pPrev,Edge)->pCopy; + // check if the root already has this fanin + Abc_ObjForEachFanin( pRoot, pFanin, k ) + if ( pFanin == pFaninNew ) + return; + Abc_ObjAddFanin( pRoot->pCopy, pFaninNew ); + return; + } + } + // continue unfolding + assert( Abc_AigNodeIsAnd(pObj) ); + // get new sequential edges + assert( Lag + Seq_ObjFaninL0(pObj) < 255 ); + assert( Lag + Seq_ObjFaninL1(pObj) < 255 ); + SeqEdge0 = (Abc_ObjFanin0(pObj)->Id << 8) + Lag + Seq_ObjFaninL0(pObj); + SeqEdge1 = (Abc_ObjFanin1(pObj)->Id << 8) + Lag + Seq_ObjFaninL1(pObj); + // call for the children + Seq_FpgaMappingConnect_rec( pNtk, SeqEdge0, pObj, 0, pRoot, vLeaves ); + Seq_FpgaMappingConnect_rec( pNtk, SeqEdge1, pObj, 1, pRoot, vLeaves ); +} + +/**Function************************************************************* + + Synopsis [Collects the edges pointing to the leaves of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Seq_FpgaMappingConnectBdd_rec( Abc_Ntk_t * pNtk, unsigned SeqEdge, Abc_Obj_t * pPrev, int Edge, Abc_Obj_t * pRoot, Vec_Ptr_t * vLeaves ) +{ + Seq_Lat_t * pRing; + Abc_Obj_t * pObj, * pLeaf, * pFanin, * pFaninNew; + unsigned SeqEdge0, SeqEdge1; + DdManager * dd = pRoot->pCopy->pNtk->pManFunc; + DdNode * bFunc, * bFunc0, * bFunc1; + int Lag, i, k; + // get the object and the lag + pObj = Abc_NtkObj( pNtk, SeqEdge >> 8 ); + Lag = SeqEdge & 255; + // if the node is the fanin of the cut, add the connection and return + Vec_PtrForEachEntry( vLeaves, pLeaf, i ) + { + if ( SeqEdge == (unsigned)pLeaf ) + { + assert( pPrev != NULL ); + if ( pRing = Seq_NodeGetRing(pPrev,Edge) ) + pFaninNew = pRing->pLatch; + else + pFaninNew = Abc_ObjFanin(pPrev,Edge)->pCopy; + // check if the root already has this fanin + Abc_ObjForEachFanin( pRoot->pCopy, pFanin, k ) + if ( pFanin == pFaninNew ) + return Cudd_bddIthVar( dd, k ); + Abc_ObjAddFanin( pRoot->pCopy, pFaninNew ); + return Cudd_bddIthVar( dd, k ); + } + } + // continue unfolding + assert( Abc_AigNodeIsAnd(pObj) ); + // get new sequential edges + assert( Lag + Seq_ObjFaninL0(pObj) < 255 ); + assert( Lag + Seq_ObjFaninL1(pObj) < 255 ); + SeqEdge0 = (Abc_ObjFanin0(pObj)->Id << 8) + Lag + Seq_ObjFaninL0(pObj); + SeqEdge1 = (Abc_ObjFanin1(pObj)->Id << 8) + Lag + Seq_ObjFaninL1(pObj); + // call for the children + bFunc0 = Seq_FpgaMappingConnectBdd_rec( pNtk, SeqEdge0, pObj, 0, pRoot, vLeaves ); Cudd_Ref( bFunc0 ); + bFunc1 = Seq_FpgaMappingConnectBdd_rec( pNtk, SeqEdge1, pObj, 1, pRoot, vLeaves ); Cudd_Ref( bFunc1 ); + bFunc0 = Cudd_NotCond( bFunc0, Abc_ObjFaninC0(pObj) ); + bFunc1 = Cudd_NotCond( bFunc1, Abc_ObjFaninC1(pObj) ); + // get the BDD of the node + bFunc = Cudd_bddAnd( dd, bFunc0, bFunc1 ); Cudd_Ref( bFunc ); + Cudd_RecursiveDeref( dd, bFunc0 ); + Cudd_RecursiveDeref( dd, bFunc1 ); + // return the BDD + Cudd_Deref( bFunc ); + return bFunc; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/seq/seqFpgaIter.c b/abc_with_bb_support/src/base/seq/seqFpgaIter.c new file mode 100644 index 000000000..b51f84db3 --- /dev/null +++ b/abc_with_bb_support/src/base/seq/seqFpgaIter.c @@ -0,0 +1,270 @@ +/**CFile**************************************************************** + + FileName [seqFpgaIter.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Construction and manipulation of sequential AIGs.] + + Synopsis [Iterative delay computation in FPGA mapping/retiming package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: seqFpgaIter.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "seqInt.h" +#include "main.h" +#include "fpga.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Seq_FpgaMappingCollectNode_rec( Abc_Obj_t * pAnd, Vec_Ptr_t * vMapping, Vec_Vec_t * vMapCuts ); +static Cut_Cut_t * Seq_FpgaMappingSelectCut( Abc_Obj_t * pAnd ); + +extern Cut_Man_t * Abc_NtkSeqCuts( Abc_Ntk_t * pNtk, Cut_Params_t * pParams ); +extern Cut_Man_t * Abc_NtkCuts( Abc_Ntk_t * pNtk, Cut_Params_t * pParams ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes the retiming lags for FPGA mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_FpgaMappingDelays( Abc_Ntk_t * pNtk, int fVerbose ) +{ + Abc_Seq_t * p = pNtk->pManFunc; + Cut_Params_t Params, * pParams = &Params; + Abc_Obj_t * pObj; + int i, clk; + + // set defaults for cut computation + memset( pParams, 0, sizeof(Cut_Params_t) ); + pParams->nVarsMax = p->nVarsMax; // the max cut size ("k" of the k-feasible cuts) + pParams->nKeepMax = 1000; // the max number of cuts kept at a node + pParams->fTruth = 0; // compute truth tables + pParams->fFilter = 1; // filter dominated cuts + pParams->fSeq = 1; // compute sequential cuts + pParams->fVerbose = fVerbose; // the verbosiness flag + + // compute the cuts +clk = clock(); + p->pCutMan = Abc_NtkSeqCuts( pNtk, pParams ); +// pParams->fSeq = 0; +// p->pCutMan = Abc_NtkCuts( pNtk, pParams ); +p->timeCuts = clock() - clk; + + if ( fVerbose ) + Cut_ManPrintStats( p->pCutMan ); + + // compute area flows +// Seq_MapComputeAreaFlows( pNtk, fVerbose ); + + // compute the delays +clk = clock(); + if ( !Seq_AigRetimeDelayLags( pNtk, fVerbose ) ) + return 0; + p->timeDelay = clock() - clk; + + // collect the nodes and cuts used in the mapping + p->vMapAnds = Vec_PtrAlloc( 1000 ); + p->vMapCuts = Vec_VecAlloc( 1000 ); + Abc_NtkIncrementTravId( pNtk ); + Abc_NtkForEachPo( pNtk, pObj, i ) + Seq_FpgaMappingCollectNode_rec( Abc_ObjFanin0(pObj), p->vMapAnds, p->vMapCuts ); + + if ( fVerbose ) + printf( "The number of LUTs = %d.\n", Vec_PtrSize(p->vMapAnds) ); + + // remove the cuts + Cut_ManStop( p->pCutMan ); + p->pCutMan = NULL; + return 1; +} + +/**Function************************************************************* + + Synopsis [Derives the parameters of the best mapping/retiming for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_FpgaMappingCollectNode_rec( Abc_Obj_t * pAnd, Vec_Ptr_t * vMapping, Vec_Vec_t * vMapCuts ) +{ + Abc_Obj_t * pFanin; + Cut_Cut_t * pCutBest; + int k; + + // skip if this is a non-PI node + if ( !Abc_AigNodeIsAnd(pAnd) ) + return; + // skip a visited node + if ( Abc_NodeIsTravIdCurrent(pAnd) ) + return; + Abc_NodeSetTravIdCurrent(pAnd); + + // visit the fanins of the node + pCutBest = Seq_FpgaMappingSelectCut( pAnd ); + for ( k = 0; k < (int)pCutBest->nLeaves; k++ ) + { + pFanin = Abc_NtkObj( pAnd->pNtk, pCutBest->pLeaves[k] >> 8 ); + Seq_FpgaMappingCollectNode_rec( pFanin, vMapping, vMapCuts ); + } + + // add this node + Vec_PtrPush( vMapping, pAnd ); + for ( k = 0; k < (int)pCutBest->nLeaves; k++ ) + Vec_VecPush( vMapCuts, Vec_PtrSize(vMapping)-1, (void *)pCutBest->pLeaves[k] ); +} + +/**Function************************************************************* + + Synopsis [Selects the best cut to represent the node in the mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Cut_t * Seq_FpgaMappingSelectCut( Abc_Obj_t * pAnd ) +{ + Abc_Obj_t * pFanin; + Cut_Cut_t * pCut, * pCutBest, * pList; + float CostCur, CostMin = ABC_INFINITY; + int ArrivalCut, ArrivalMin, i; + // get the arrival time of the best non-trivial cut + ArrivalMin = Seq_NodeGetLValue( pAnd ); + // iterate through the cuts and select the one with the minimum cost + pList = Abc_NodeReadCuts( Seq_NodeCutMan(pAnd), pAnd ); + CostMin = ABC_INFINITY; + pCutBest = NULL; + for ( pCut = pList->pNext; pCut; pCut = pCut->pNext ) + { + ArrivalCut = *((int *)&pCut->uSign); +// assert( ArrivalCut >= ArrivalMin ); + if ( ArrivalCut > ArrivalMin ) + continue; + CostCur = 0.0; + for ( i = 0; i < (int)pCut->nLeaves; i++ ) + { + pFanin = Abc_NtkObj( pAnd->pNtk, pCut->pLeaves[i] >> 8 ); + if ( Abc_ObjIsPi(pFanin) ) + continue; + if ( Abc_NodeIsTravIdCurrent(pFanin) ) + continue; + CostCur += (float)(1.0 / Abc_ObjFanoutNum(pFanin)); +// CostCur += Seq_NodeGetFlow( pFanin ); + } + if ( CostMin > CostCur ) + { + CostMin = CostCur; + pCutBest = pCut; + } + } + assert( pCutBest != NULL ); + return pCutBest; +} + + +/**Function************************************************************* + + Synopsis [Computes the l-value of the cut.] + + Description [The node should be internal.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Seq_FpgaCutUpdateLValue( Cut_Cut_t * pCut, Abc_Obj_t * pObj, int Fi ) +{ + Abc_Obj_t * pFanin; + int i, lValueMax, lValueCur; + assert( Abc_AigNodeIsAnd(pObj) ); + lValueMax = -ABC_INFINITY; + for ( i = 0; i < (int)pCut->nLeaves; i++ ) + { +// lValue0 = Seq_NodeGetLValue(Abc_ObjFanin0(pObj)) - Fi * Abc_ObjFaninL0(pObj); + pFanin = Abc_NtkObj(pObj->pNtk, pCut->pLeaves[i] >> 8); + lValueCur = Seq_NodeGetLValue(pFanin) - Fi * (pCut->pLeaves[i] & 255); + if ( lValueMax < lValueCur ) + lValueMax = lValueCur; + } + lValueMax += 1; + *((int *)&pCut->uSign) = lValueMax; + return lValueMax; +} + +/**Function************************************************************* + + Synopsis [Computes the l-value of the node.] + + Description [The node can be internal or a PO.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_FpgaNodeUpdateLValue( Abc_Obj_t * pObj, int Fi ) +{ + Cut_Cut_t * pCut, * pList; + int lValueNew, lValueOld, lValueCut; + assert( !Abc_ObjIsPi(pObj) ); + assert( Abc_ObjFaninNum(pObj) > 0 ); + if ( Abc_ObjIsPo(pObj) ) + { + lValueNew = Seq_NodeGetLValue(Abc_ObjFanin0(pObj)) - Fi * Seq_ObjFaninL0(pObj); + return (lValueNew > Fi)? SEQ_UPDATE_FAIL : SEQ_UPDATE_NO; + } + // get the arrival time of the best non-trivial cut + pList = Abc_NodeReadCuts( Seq_NodeCutMan(pObj), pObj ); + // skip the choice nodes + if ( pList == NULL ) + return SEQ_UPDATE_NO; + lValueNew = ABC_INFINITY; + for ( pCut = pList->pNext; pCut; pCut = pCut->pNext ) + { + lValueCut = Seq_FpgaCutUpdateLValue( pCut, pObj, Fi ); + if ( lValueNew > lValueCut ) + lValueNew = lValueCut; + } + // compare the arrival time with the previous arrival time + lValueOld = Seq_NodeGetLValue(pObj); +// if ( lValueNew == lValueOld ) + if ( lValueNew <= lValueOld ) + return SEQ_UPDATE_NO; + Seq_NodeSetLValue( pObj, lValueNew ); +//printf( "%d -> %d ", lValueOld, lValueNew ); + return SEQ_UPDATE_YES; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/seq/seqInt.h b/abc_with_bb_support/src/base/seq/seqInt.h new file mode 100644 index 000000000..b7713b903 --- /dev/null +++ b/abc_with_bb_support/src/base/seq/seqInt.h @@ -0,0 +1,256 @@ +/**CFile**************************************************************** + + FileName [seqInt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Construction and manipulation of sequential AIGs.] + + Synopsis [Internal declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: seqInt.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __SEQ_INT_H__ +#define __SEQ_INT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include "abc.h" +#include "cut.h" +#include "main.h" +#include "mio.h" +#include "mapper.h" +#include "fpga.h" +#include "seq.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +#define SEQ_FULL_MASK 0xFFFFFFFF + +// node status after updating its arrival time +enum { SEQ_UPDATE_FAIL, SEQ_UPDATE_NO, SEQ_UPDATE_YES }; + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +// manager of sequential AIG +struct Abc_Seq_t_ +{ + // sequential information + Abc_Ntk_t * pNtk; // the network + int nSize; // the number of entries in all internal arrays + Vec_Int_t * vNums; // the number of latches on each edge in the AIG + Vec_Ptr_t * vInits; // the initial states for each edge in the AIG + Extra_MmFixed_t * pMmInits; // memory manager for latch structures used to remember init states + int fVerbose; // the verbose flag + float fEpsilon; // the accuracy for delay computation + int fStandCells; // the flag denoting standard cell mapping + int nMaxIters; // the max number of iterations + int FiBestInt; // the best clock period + float FiBestFloat; // the best clock period + // K-feasible cuts + int nVarsMax; // the max cut size + Cut_Man_t * pCutMan; // cut manager + Map_SuperLib_t * pSuperLib; // the current supergate library + // sequential arrival time computation + Vec_Int_t * vAFlows; // the area flow of each cut + Vec_Int_t * vLValues; // the arrival times (L-Values of nodes) + Vec_Int_t * vLValuesN; // the arrival times (L-Values of nodes) + Vec_Str_t * vLags; // the lags of the mapped nodes + Vec_Str_t * vLagsN; // the lags of the mapped nodes + Vec_Str_t * vUses; // the phase usage + // representation of the mapping + Vec_Ptr_t * vMapAnds; // nodes visible in the mapping + Vec_Vec_t * vMapCuts; // best cuts for each node + Vec_Vec_t * vMapDelays; // the delay of each fanin + Vec_Vec_t * vMapFanins; // the delay of each fanin + // runtime stats + int timeCuts; // runtime to compute the cuts + int timeDelay; // runtime to compute the L-values + int timeRet; // runtime to retime the resulting network + int timeNtk; // runtime to create the final network + +}; + +// data structure to store initial state +typedef struct Seq_Lat_t_ Seq_Lat_t; +struct Seq_Lat_t_ +{ + Seq_Lat_t * pNext; // the next Lat in the ring + Seq_Lat_t * pPrev; // the prev Lat in the ring + Abc_Obj_t * pLatch; // the real latch corresponding to Lat +}; + +// representation of latch on the edge +typedef struct Seq_RetEdge_t_ Seq_RetEdge_t; +struct Seq_RetEdge_t_ // 1 word +{ + unsigned iNode : 24; // the ID of the node + unsigned iEdge : 1; // the edge of the node + unsigned iLatch : 7; // the latch number counting from the node +}; + +// representation of one retiming step +typedef struct Seq_RetStep_t_ Seq_RetStep_t; +struct Seq_RetStep_t_ // 1 word +{ + unsigned iNode : 24; // the ID of the node + unsigned nLatches : 8; // the number of latches to retime +}; + +// representation of one mapping match +typedef struct Seq_Match_t_ Seq_Match_t; +struct Seq_Match_t_ // 3 words +{ + Abc_Obj_t * pAnd; // the AND gate used in the mapping + Cut_Cut_t * pCut; // the cut used to map it + Map_Super_t * pSuper; // the supergate used to implement the cut + unsigned fCompl : 1; // the polarity of the AND gate + unsigned fCutInv : 1; // the polarity of the cut + unsigned PolUse : 2; // the polarity use of this node + unsigned uPhase : 14; // the phase assignment at the boundary + unsigned uPhaseR : 14; // the real phase assignment at the boundary +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// transforming retedges into ints and back +static inline int Seq_RetEdge2Int( Seq_RetEdge_t Val ) { return *((int *)&Val); } +static inline Seq_RetEdge_t Seq_Int2RetEdge( int Num ) { return *((Seq_RetEdge_t *)&Num); } +// transforming retsteps into ints and back +static inline int Seq_RetStep2Int( Seq_RetStep_t Val ) { return *((int *)&Val); } +static inline Seq_RetStep_t Seq_Int2RetStep( int Num ) { return *((Seq_RetStep_t *)&Num); } + +// manipulating the number of latches on each edge +static inline Vec_Int_t * Seq_ObjLNums( Abc_Obj_t * pObj ) { return ((Abc_Seq_t*)pObj->pNtk->pManFunc)->vNums; } +static inline int Seq_ObjFaninL( Abc_Obj_t * pObj, int i ) { return Vec_IntEntry(Seq_ObjLNums(pObj), 2*pObj->Id + i); } +static inline int Seq_ObjFaninL0( Abc_Obj_t * pObj ) { return Vec_IntEntry(Seq_ObjLNums(pObj), 2*pObj->Id + 0); } +static inline int Seq_ObjFaninL1( Abc_Obj_t * pObj ) { return Vec_IntEntry(Seq_ObjLNums(pObj), 2*pObj->Id + 1); } +static inline void Seq_ObjSetFaninL( Abc_Obj_t * pObj, int i, int nLats ) { Vec_IntWriteEntry(Seq_ObjLNums(pObj), 2*pObj->Id + i, nLats); } +static inline void Seq_ObjSetFaninL0( Abc_Obj_t * pObj, int nLats ) { Vec_IntWriteEntry(Seq_ObjLNums(pObj), 2*pObj->Id + 0, nLats); } +static inline void Seq_ObjSetFaninL1( Abc_Obj_t * pObj, int nLats ) { Vec_IntWriteEntry(Seq_ObjLNums(pObj), 2*pObj->Id + 1, nLats); } +static inline void Seq_ObjAddFaninL( Abc_Obj_t * pObj, int i, int nLats ) { Vec_IntAddToEntry(Seq_ObjLNums(pObj), 2*pObj->Id + i, nLats); } +static inline void Seq_ObjAddFaninL0( Abc_Obj_t * pObj, int nLats ) { Vec_IntAddToEntry(Seq_ObjLNums(pObj), 2*pObj->Id + 0, nLats); } +static inline void Seq_ObjAddFaninL1( Abc_Obj_t * pObj, int nLats ) { Vec_IntAddToEntry(Seq_ObjLNums(pObj), 2*pObj->Id + 1, nLats); } +static inline int Seq_ObjFanoutL( Abc_Obj_t * pObj, Abc_Obj_t * pFanout ) { return Seq_ObjFaninL( pFanout, Abc_ObjFanoutEdgeNum(pObj,pFanout) ); } +static inline void Seq_ObjSetFanoutL( Abc_Obj_t * pObj, Abc_Obj_t * pFanout, int nLats ) { Seq_ObjSetFaninL( pFanout, Abc_ObjFanoutEdgeNum(pObj,pFanout), nLats ); } +static inline void Seq_ObjAddFanoutL( Abc_Obj_t * pObj, Abc_Obj_t * pFanout, int nLats ) { Seq_ObjAddFaninL( pFanout, Abc_ObjFanoutEdgeNum(pObj,pFanout), nLats ); } +static inline int Seq_ObjFaninLMin( Abc_Obj_t * pObj ) { assert( Abc_ObjIsNode(pObj) ); return ABC_MIN( Seq_ObjFaninL0(pObj), Seq_ObjFaninL1(pObj) ); } +static inline int Seq_ObjFaninLMax( Abc_Obj_t * pObj ) { assert( Abc_ObjIsNode(pObj) ); return ABC_MAX( Seq_ObjFaninL0(pObj), Seq_ObjFaninL1(pObj) ); } + +// reading l-values and lags +static inline Vec_Int_t * Seq_NodeLValues( Abc_Obj_t * pNode ) { return ((Abc_Seq_t *)(pNode)->pNtk->pManFunc)->vLValues; } +static inline Vec_Int_t * Seq_NodeLValuesN( Abc_Obj_t * pNode ) { return ((Abc_Seq_t *)(pNode)->pNtk->pManFunc)->vLValuesN; } +static inline int Seq_NodeGetLValue( Abc_Obj_t * pNode ) { return Vec_IntEntry( Seq_NodeLValues(pNode), (pNode)->Id ); } +static inline void Seq_NodeSetLValue( Abc_Obj_t * pNode, int Value ) { Vec_IntWriteEntry( Seq_NodeLValues(pNode), (pNode)->Id, Value ); } +static inline float Seq_NodeGetLValueP( Abc_Obj_t * pNode ) { return Abc_Int2Float( Vec_IntEntry( Seq_NodeLValues(pNode), (pNode)->Id ) ); } +static inline float Seq_NodeGetLValueN( Abc_Obj_t * pNode ) { return Abc_Int2Float( Vec_IntEntry( Seq_NodeLValuesN(pNode), (pNode)->Id ) ); } +static inline void Seq_NodeSetLValueP( Abc_Obj_t * pNode, float Value ) { Vec_IntWriteEntry( Seq_NodeLValues(pNode), (pNode)->Id, Abc_Float2Int(Value) ); } +static inline void Seq_NodeSetLValueN( Abc_Obj_t * pNode, float Value ) { Vec_IntWriteEntry( Seq_NodeLValuesN(pNode), (pNode)->Id, Abc_Float2Int(Value) ); } + +// reading area flows +static inline Vec_Int_t * Seq_NodeFlow( Abc_Obj_t * pNode ) { return ((Abc_Seq_t *)(pNode)->pNtk->pManFunc)->vAFlows; } +static inline float Seq_NodeGetFlow( Abc_Obj_t * pNode ) { return Abc_Int2Float( Vec_IntEntry( Seq_NodeFlow(pNode), (pNode)->Id ) ); } +static inline void Seq_NodeSetFlow( Abc_Obj_t * pNode, float Value ) { Vec_IntWriteEntry( Seq_NodeFlow(pNode), (pNode)->Id, Abc_Float2Int(Value) ); } + +// reading the contents of the lat +static inline Abc_InitType_t Seq_LatInit( Seq_Lat_t * pLat ) { return ((unsigned)pLat->pPrev) & 3; } +static inline Seq_Lat_t * Seq_LatNext( Seq_Lat_t * pLat ) { return pLat->pNext; } +static inline Seq_Lat_t * Seq_LatPrev( Seq_Lat_t * pLat ) { return (void *)(((unsigned)pLat->pPrev) & (SEQ_FULL_MASK << 2)); } + +// setting the contents of the lat +static inline void Seq_LatSetInit( Seq_Lat_t * pLat, Abc_InitType_t Init ) { pLat->pPrev = (void *)( (3 & Init) | (((unsigned)pLat->pPrev) & (SEQ_FULL_MASK << 2)) ); } +static inline void Seq_LatSetNext( Seq_Lat_t * pLat, Seq_Lat_t * pNext ) { pLat->pNext = pNext; } +static inline void Seq_LatSetPrev( Seq_Lat_t * pLat, Seq_Lat_t * pPrev ) { Abc_InitType_t Init = Seq_LatInit(pLat); pLat->pPrev = pPrev; Seq_LatSetInit(pLat, Init); } + +// accessing retiming lags +static inline Cut_Man_t * Seq_NodeCutMan( Abc_Obj_t * pNode ) { return ((Abc_Seq_t *)(pNode)->pNtk->pManFunc)->pCutMan; } +static inline Vec_Str_t * Seq_NodeLags( Abc_Obj_t * pNode ) { return ((Abc_Seq_t *)(pNode)->pNtk->pManFunc)->vLags; } +static inline Vec_Str_t * Seq_NodeLagsN( Abc_Obj_t * pNode ) { return ((Abc_Seq_t *)(pNode)->pNtk->pManFunc)->vLagsN; } +static inline char Seq_NodeGetLag( Abc_Obj_t * pNode ) { return Vec_StrEntry( Seq_NodeLags(pNode), (pNode)->Id ); } +static inline char Seq_NodeGetLagN( Abc_Obj_t * pNode ) { return Vec_StrEntry( Seq_NodeLagsN(pNode), (pNode)->Id ); } +static inline void Seq_NodeSetLag( Abc_Obj_t * pNode, char Value ) { Vec_StrWriteEntry( Seq_NodeLags(pNode), (pNode)->Id, (Value) ); } +static inline void Seq_NodeSetLagN( Abc_Obj_t * pNode, char Value ) { Vec_StrWriteEntry( Seq_NodeLagsN(pNode), (pNode)->Id, (Value) ); } +static inline int Seq_NodeComputeLag( int LValue, int Fi ) { return (LValue + 1024*Fi)/Fi - 1024 - (int)(LValue % Fi == 0); } +static inline int Seq_NodeComputeLagFloat( float LValue, float Fi ) { return ((int)ceil(LValue/Fi)) - 1; } + +// phase usage +static inline Vec_Str_t * Seq_NodeUses( Abc_Obj_t * pNode ) { return ((Abc_Seq_t *)(pNode)->pNtk->pManFunc)->vUses; } +static inline char Seq_NodeGetUses( Abc_Obj_t * pNode ) { return Vec_StrEntry( Seq_NodeUses(pNode), (pNode)->Id ); } +static inline void Seq_NodeSetUses( Abc_Obj_t * pNode, char Value ) { Vec_StrWriteEntry( Seq_NodeUses(pNode), (pNode)->Id, (Value) ); } + +// accessing initial states +static inline Vec_Ptr_t * Seq_NodeLats( Abc_Obj_t * pObj ) { return ((Abc_Seq_t*)pObj->pNtk->pManFunc)->vInits; } +static inline Seq_Lat_t * Seq_NodeGetRing( Abc_Obj_t * pObj, int Edge ) { return Vec_PtrEntry( Seq_NodeLats(pObj), (pObj->Id<<1)+Edge ); } +static inline void Seq_NodeSetRing( Abc_Obj_t * pObj, int Edge, Seq_Lat_t * pLat ) { Vec_PtrWriteEntry( Seq_NodeLats(pObj), (pObj->Id<<1)+Edge, pLat ); } +static inline Seq_Lat_t * Seq_NodeCreateLat( Abc_Obj_t * pObj ) { Seq_Lat_t * p = (Seq_Lat_t *)Extra_MmFixedEntryFetch( ((Abc_Seq_t*)pObj->pNtk->pManFunc)->pMmInits ); p->pNext = p->pPrev = NULL; p->pLatch = NULL; return p; } +static inline void Seq_NodeRecycleLat( Abc_Obj_t * pObj, Seq_Lat_t * pLat ) { Extra_MmFixedEntryRecycle( ((Abc_Seq_t*)pObj->pNtk->pManFunc)->pMmInits, (char *)pLat ); } + +// getting hold of the structure storing initial states of the latches +static inline Seq_Lat_t * Seq_NodeGetLatFirst( Abc_Obj_t * pObj, int Edge ) { return Seq_NodeGetRing(pObj, Edge); } +static inline Seq_Lat_t * Seq_NodeGetLatLast( Abc_Obj_t * pObj, int Edge ) { return Seq_LatPrev( Seq_NodeGetRing(pObj, Edge) ); } +static inline Seq_Lat_t * Seq_NodeGetLat( Abc_Obj_t * pObj, int Edge, int iLat ) { int c; Seq_Lat_t * pLat = Seq_NodeGetRing(pObj, Edge); for ( c = 0; c != iLat; c++ ) pLat = pLat->pNext; return pLat; } +static inline int Seq_NodeCountLats( Abc_Obj_t * pObj, int Edge ) { int c; Seq_Lat_t * pLat, * pRing = Seq_NodeGetRing(pObj, Edge); if ( pRing == NULL ) return 0; for ( c = 0, pLat = pRing; !c || pLat != pRing; c++ ) pLat = pLat->pNext; return c; } +static inline void Seq_NodeCleanLats( Abc_Obj_t * pObj, int Edge ) { int c; Seq_Lat_t * pLat, * pRing = Seq_NodeGetRing(pObj, Edge); if ( pRing == NULL ) return ; for ( c = 0, pLat = pRing; !c || pLat != pRing; c++ ) pLat->pLatch = NULL, pLat = pLat->pNext; return; } + +// getting/setting initial states of the latches +static inline Abc_InitType_t Seq_NodeGetInitOne( Abc_Obj_t * pObj, int Edge, int iLat ) { return Seq_LatInit( Seq_NodeGetLat(pObj, Edge, iLat) ); } +static inline Abc_InitType_t Seq_NodeGetInitFirst( Abc_Obj_t * pObj, int Edge ) { return Seq_LatInit( Seq_NodeGetLatFirst(pObj, Edge) ); } +static inline Abc_InitType_t Seq_NodeGetInitLast( Abc_Obj_t * pObj, int Edge ) { return Seq_LatInit( Seq_NodeGetLatLast(pObj, Edge) ); } +static inline void Seq_NodeSetInitOne( Abc_Obj_t * pObj, int Edge, int iLat, Abc_InitType_t Init ) { Seq_LatSetInit( Seq_NodeGetLat(pObj, Edge, iLat), Init ); } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== seqAigIter.c =============================================================*/ +extern int Seq_AigRetimeDelayLags( Abc_Ntk_t * pNtk, int fVerbose ); +extern int Seq_NtkImplementRetiming( Abc_Ntk_t * pNtk, Vec_Str_t * vLags, int fVerbose ); +/*=== seqFpgaIter.c ============================================================*/ +extern int Seq_FpgaMappingDelays( Abc_Ntk_t * pNtk, int fVerbose ); +extern int Seq_FpgaNodeUpdateLValue( Abc_Obj_t * pObj, int Fi ); +/*=== seqMapIter.c ============================================================*/ +extern int Seq_MapRetimeDelayLags( Abc_Ntk_t * pNtk, int fVerbose ); +/*=== seqRetIter.c =============================================================*/ +extern int Seq_NtkRetimeDelayLags( Abc_Ntk_t * pNtkOld, Abc_Ntk_t * pNtk, int fVerbose ); +/*=== seqLatch.c ===============================================================*/ +extern void Seq_NodeInsertFirst( Abc_Obj_t * pObj, int Edge, Abc_InitType_t Init ); +extern void Seq_NodeInsertLast( Abc_Obj_t * pObj, int Edge, Abc_InitType_t Init ); +extern Abc_InitType_t Seq_NodeDeleteFirst( Abc_Obj_t * pObj, int Edge ); +extern Abc_InitType_t Seq_NodeDeleteLast( Abc_Obj_t * pObj, int Edge ); +/*=== seqUtil.c ================================================================*/ +extern int Seq_NtkLevelMax( Abc_Ntk_t * pNtk ); +extern int Seq_ObjFanoutLMax( Abc_Obj_t * pObj ); +extern int Seq_ObjFanoutLMin( Abc_Obj_t * pObj ); +extern int Seq_ObjFanoutLSum( Abc_Obj_t * pObj ); +extern int Seq_ObjFaninLSum( Abc_Obj_t * pObj ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/base/seq/seqLatch.c b/abc_with_bb_support/src/base/seq/seqLatch.c new file mode 100644 index 000000000..ded664c9d --- /dev/null +++ b/abc_with_bb_support/src/base/seq/seqLatch.c @@ -0,0 +1,223 @@ +/**CFile**************************************************************** + + FileName [seqLatch.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Construction and manipulation of sequential AIGs.] + + Synopsis [Manipulation of latch data structures representing initial states.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: seqLatch.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "seqInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Insert the first Lat on the edge.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NodeInsertFirst( Abc_Obj_t * pObj, int Edge, Abc_InitType_t Init ) +{ + Seq_Lat_t * pLat, * pRing, * pPrev; + pRing = Seq_NodeGetRing( pObj, Edge ); + pLat = Seq_NodeCreateLat( pObj ); + if ( pRing == NULL ) + { + Seq_LatSetPrev( pLat, pLat ); + Seq_LatSetNext( pLat, pLat ); + Seq_NodeSetRing( pObj, Edge, pLat ); + } + else + { + pPrev = Seq_LatPrev( pRing ); + Seq_LatSetPrev( pLat, pPrev ); + Seq_LatSetNext( pPrev, pLat ); + Seq_LatSetPrev( pRing, pLat ); + Seq_LatSetNext( pLat, pRing ); + Seq_NodeSetRing( pObj, Edge, pLat ); // rotate the ring to make pLat the first + } + Seq_LatSetInit( pLat, Init ); + Seq_ObjAddFaninL( pObj, Edge, 1 ); + assert( pLat->pLatch == NULL ); +} + +/**Function************************************************************* + + Synopsis [Insert the last Lat on the edge.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NodeInsertLast( Abc_Obj_t * pObj, int Edge, Abc_InitType_t Init ) +{ + Seq_Lat_t * pLat, * pRing, * pPrev; + pRing = Seq_NodeGetRing( pObj, Edge ); + pLat = Seq_NodeCreateLat( pObj ); + if ( pRing == NULL ) + { + Seq_LatSetPrev( pLat, pLat ); + Seq_LatSetNext( pLat, pLat ); + Seq_NodeSetRing( pObj, Edge, pLat ); + } + else + { + pPrev = Seq_LatPrev( pRing ); + Seq_LatSetPrev( pLat, pPrev ); + Seq_LatSetNext( pPrev, pLat ); + Seq_LatSetPrev( pRing, pLat ); + Seq_LatSetNext( pLat, pRing ); + } + Seq_LatSetInit( pLat, Init ); + Seq_ObjAddFaninL( pObj, Edge, 1 ); +} + +/**Function************************************************************* + + Synopsis [Delete the first Lat on the edge.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_InitType_t Seq_NodeDeleteFirst( Abc_Obj_t * pObj, int Edge ) +{ + Abc_InitType_t Init; + Seq_Lat_t * pLat, * pRing, * pPrev, * pNext; + pRing = Seq_NodeGetRing( pObj, Edge ); + pLat = pRing; // consider the first latch + if ( pLat->pNext == pLat ) + Seq_NodeSetRing( pObj, Edge, NULL ); + else + { + pPrev = Seq_LatPrev( pLat ); + pNext = Seq_LatNext( pLat ); + Seq_LatSetPrev( pNext, pPrev ); + Seq_LatSetNext( pPrev, pNext ); + Seq_NodeSetRing( pObj, Edge, pNext ); // rotate the ring + } + Init = Seq_LatInit( pLat ); + Seq_NodeRecycleLat( pObj, pLat ); + Seq_ObjAddFaninL( pObj, Edge, -1 ); + return Init; +} + +/**Function************************************************************* + + Synopsis [Delete the last Lat on the edge.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_InitType_t Seq_NodeDeleteLast( Abc_Obj_t * pObj, int Edge ) +{ + Abc_InitType_t Init; + Seq_Lat_t * pLat, * pRing, * pPrev, * pNext; + pRing = Seq_NodeGetRing( pObj, Edge ); + pLat = Seq_LatPrev( pRing ); // consider the last latch + if ( pLat->pNext == pLat ) + Seq_NodeSetRing( pObj, Edge, NULL ); + else + { + pPrev = Seq_LatPrev( pLat ); + pNext = Seq_LatNext( pLat ); + Seq_LatSetPrev( pNext, pPrev ); + Seq_LatSetNext( pPrev, pNext ); + } + Init = Seq_LatInit( pLat ); + Seq_NodeRecycleLat( pObj, pLat ); + Seq_ObjAddFaninL( pObj, Edge, -1 ); + return Init; +} + +/**Function************************************************************* + + Synopsis [Insert the last Lat on the edge.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NodeDupLats( Abc_Obj_t * pObjNew, Abc_Obj_t * pObj, int Edge ) +{ + Seq_Lat_t * pRing, * pLat; + int i, nLatches; + pRing = Seq_NodeGetRing( pObj, Edge ); + if ( pRing == NULL ) + return; + nLatches = Seq_NodeCountLats( pObj, Edge ); + for ( i = 0, pLat = pRing; i < nLatches; i++, pLat = pLat->pNext ) + Seq_NodeInsertLast( pObjNew, Edge, Seq_LatInit(pLat) ); +} + +/**Function************************************************************* + + Synopsis [Insert the last Lat on the edge.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_NodeCompareLats( Abc_Obj_t * pObj1, int Edge1, Abc_Obj_t * pObj2, int Edge2 ) +{ + Seq_Lat_t * pRing1, * pRing2, * pLat1, * pLat2; + int i, nLatches1, nLatches2; + + nLatches1 = Seq_NodeCountLats( pObj1, Edge1 ); + nLatches2 = Seq_NodeCountLats( pObj2, Edge2 ); + if ( nLatches1 != nLatches2 ) + return 0; + + pRing1 = Seq_NodeGetRing( pObj1, Edge1 ); + pRing2 = Seq_NodeGetRing( pObj2, Edge2 ); + for ( i = 0, pLat1 = pRing1, pLat2 = pRing2; i < nLatches1; i++, pLat1 = pLat1->pNext, pLat2 = pLat2->pNext ) + if ( Seq_LatInit(pLat1) != Seq_LatInit(pLat2) ) + return 0; + + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/seq/seqMan.c b/abc_with_bb_support/src/base/seq/seqMan.c new file mode 100644 index 000000000..2ff29ae25 --- /dev/null +++ b/abc_with_bb_support/src/base/seq/seqMan.c @@ -0,0 +1,133 @@ +/**CFile**************************************************************** + + FileName [seqMan.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Construction and manipulation of sequential AIGs.] + + Synopsis [Manager of sequential AIG containing.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: seqMan.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "seqInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates sequential AIG manager.] + + Description [The manager contains all the data structures needed to + represent sequential AIG and compute stand-alone retiming as well as + the integrated mapping/retiming of the sequential AIG.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Seq_t * Seq_Create( Abc_Ntk_t * pNtk ) +{ + Abc_Seq_t * p; + // start the manager + p = ALLOC( Abc_Seq_t, 1 ); + memset( p, 0, sizeof(Abc_Seq_t) ); + p->pNtk = pNtk; + p->nSize = 1000; + p->nMaxIters = 15; + p->pMmInits = Extra_MmFixedStart( sizeof(Seq_Lat_t) ); + p->fEpsilon = (float)0.001; + // create internal data structures + p->vNums = Vec_IntStart( 2 * p->nSize ); + p->vInits = Vec_PtrStart( 2 * p->nSize ); + p->vLValues = Vec_IntStart( p->nSize ); + p->vLags = Vec_StrStart( p->nSize ); + p->vLValuesN = Vec_IntStart( p->nSize ); + p->vAFlows = Vec_IntStart( p->nSize ); + p->vLagsN = Vec_StrStart( p->nSize ); + p->vUses = Vec_StrStart( p->nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Deallocates sequential AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_Resize( Abc_Seq_t * p, int nMaxId ) +{ + if ( p->nSize > nMaxId ) + return; + p->nSize = nMaxId + 1; + Vec_IntFill( p->vNums, 2 * p->nSize, 0 ); + Vec_PtrFill( p->vInits, 2 * p->nSize, NULL ); + Vec_IntFill( p->vLValues, p->nSize, 0 ); + Vec_StrFill( p->vLags, p->nSize, 0 ); + Vec_IntFill( p->vLValuesN, p->nSize, 0 ); + Vec_IntFill( p->vAFlows, p->nSize, 0 ); + Vec_StrFill( p->vLagsN, p->nSize, 0 ); + Vec_StrFill( p->vUses, p->nSize, 0 ); +} + + +/**Function************************************************************* + + Synopsis [Deallocates sequential AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_Delete( Abc_Seq_t * p ) +{ + if ( p->fStandCells && p->vMapAnds ) + { + void * pVoid; int i; + Vec_PtrForEachEntry( p->vMapAnds, pVoid, i ) + free( pVoid ); + } + if ( p->vMapDelays ) Vec_VecFree( p->vMapDelays ); // the nodes used in the mapping + if ( p->vMapFanins ) Vec_VecFree( p->vMapFanins ); // the cuts used in the mapping + if ( p->vMapAnds ) Vec_PtrFree( p->vMapAnds ); // the nodes used in the mapping + if ( p->vMapCuts ) Vec_VecFree( p->vMapCuts ); // the cuts used in the mapping + if ( p->vLValues ) Vec_IntFree( p->vLValues ); // the arrival times (L-Values of nodes) + if ( p->vLags ) Vec_StrFree( p->vLags ); // the lags of the mapped nodes + if ( p->vLValuesN ) Vec_IntFree( p->vLValuesN ); // the arrival times (L-Values of nodes) + if ( p->vAFlows ) Vec_IntFree( p->vAFlows ); // the arrival times (L-Values of nodes) + if ( p->vLagsN ) Vec_StrFree( p->vLagsN ); // the lags of the mapped nodes + if ( p->vUses ) Vec_StrFree( p->vUses ); // the uses of phases + if ( p->vInits ) Vec_PtrFree( p->vInits ); // the initial values of the latches + if ( p->vNums ) Vec_IntFree( p->vNums ); // the numbers of latches + Extra_MmFixedStop( p->pMmInits ); + free( p ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/seq/seqMapCore.c b/abc_with_bb_support/src/base/seq/seqMapCore.c new file mode 100644 index 000000000..77c8ba39c --- /dev/null +++ b/abc_with_bb_support/src/base/seq/seqMapCore.c @@ -0,0 +1,652 @@ +/**CFile**************************************************************** + + FileName [seqMapCore.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Construction and manipulation of sequential AIGs.] + + Synopsis [The core of SC mapping/retiming package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: seqMapCore.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "seqInt.h" +#include "main.h" +#include "mio.h" +#include "mapper.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +extern Abc_Ntk_t * Seq_NtkMapDup( Abc_Ntk_t * pNtk ); +extern int Seq_NtkMapInitCompatible( Abc_Ntk_t * pNtk, int fVerbose ); +extern Abc_Ntk_t * Seq_NtkSeqMapMapped( Abc_Ntk_t * pNtk ); + +static int Seq_MapMappingCount( Abc_Ntk_t * pNtk ); +static int Seq_MapMappingCount_rec( Abc_Ntk_t * pNtk, unsigned SeqEdge, Vec_Ptr_t * vLeaves ); +static Abc_Obj_t * Seq_MapMappingBuild_rec( Abc_Ntk_t * pNtkNew, Abc_Ntk_t * pNtk, unsigned SeqEdge, int fTop, int fCompl, int LagCut, Vec_Ptr_t * vLeaves, unsigned uPhase ); +static DdNode * Seq_MapMappingBdd_rec( DdManager * dd, Abc_Ntk_t * pNtk, unsigned SeqEdge, Vec_Ptr_t * vLeaves ); +static void Seq_MapMappingEdges_rec( Abc_Ntk_t * pNtk, unsigned SeqEdge, Abc_Obj_t * pPrev, Vec_Ptr_t * vLeaves, Vec_Vec_t * vMapEdges ); +static void Seq_MapMappingConnect_rec( Abc_Ntk_t * pNtk, unsigned SeqEdge, Abc_Obj_t * pPrev, int Edge, Abc_Obj_t * pRoot, Vec_Ptr_t * vLeaves ); +static DdNode * Seq_MapMappingConnectBdd_rec( Abc_Ntk_t * pNtk, unsigned SeqEdge, Abc_Obj_t * pPrev, int Edge, Abc_Obj_t * pRoot, Vec_Ptr_t * vLeaves ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs Map mapping and retiming.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Seq_MapRetime( Abc_Ntk_t * pNtk, int nMaxIters, int fVerbose ) +{ + Abc_Seq_t * p = pNtk->pManFunc; + Abc_Ntk_t * pNtkNew; + Abc_Ntk_t * pNtkMap; + int RetValue; + + // derive the supergate library + if ( Abc_FrameReadLibSuper() == NULL && Abc_FrameReadLibGen() ) + { + printf( "A simple supergate library is derived from gate library \"%s\".\n", + Mio_LibraryReadName(Abc_FrameReadLibGen()) ); + Map_SuperLibDeriveFromGenlib( Abc_FrameReadLibGen() ); + } + p->pSuperLib = Abc_FrameReadLibSuper(); + p->nVarsMax = Map_SuperLibReadVarsMax(p->pSuperLib); + p->nMaxIters = nMaxIters; + p->fStandCells = 1; + + // find the best mapping and retiming for all nodes (p->vLValues, p->vBestCuts, p->vLags) + if ( !Seq_MapRetimeDelayLags( pNtk, fVerbose ) ) + return NULL; + if ( RetValue = Abc_NtkGetChoiceNum(pNtk) ) + { + printf( "The network has %d choices. The resulting network is not derived (this is temporary).\n", RetValue ); + printf( "The mininum clock period computed is %5.2f.\n", p->FiBestFloat ); + return NULL; + } + printf( "The mininum clock period computed is %5.2f.\n", p->FiBestFloat ); + printf( "The resulting network is derived as BDD logic network (this is temporary).\n" ); + + // duplicate the nodes contained in multiple cuts + pNtkNew = Seq_NtkMapDup( pNtk ); + + // implement the retiming + RetValue = Seq_NtkImplementRetiming( pNtkNew, ((Abc_Seq_t *)pNtkNew->pManFunc)->vLags, fVerbose ); + if ( RetValue == 0 ) + printf( "Retiming completed but initial state computation has failed.\n" ); + + // check the compatibility of initial states computed + if ( RetValue = Seq_NtkMapInitCompatible( pNtkNew, fVerbose ) ) + printf( "The number of LUTs with incompatible edges = %d.\n", RetValue ); +// return pNtkNew; + + // create the final mapped network + pNtkMap = Seq_NtkSeqMapMapped( pNtkNew ); + Abc_NtkDelete( pNtkNew ); + return pNtkMap; +} + +/**Function************************************************************* + + Synopsis [Derives the network by duplicating some of the nodes.] + + Description [Information about mapping is given as mapping nodes (p->vMapAnds) + and best cuts for each node (p->vMapCuts).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Seq_NtkMapDup( Abc_Ntk_t * pNtk ) +{ + Abc_Seq_t * pNew, * p = pNtk->pManFunc; + Seq_Match_t * pMatch; + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj, * pFanin, * pFaninNew, * pLeaf; + Vec_Ptr_t * vLeaves; + unsigned SeqEdge; + int i, k, nObjsNew, Lag; + + assert( Abc_NtkIsSeq(pNtk) ); + + // start the expanded network + pNtkNew = Abc_NtkStartFrom( pNtk, pNtk->ntkType, pNtk->ntkFunc ); + Abc_NtkCleanNext(pNtk); + + // start the new sequential AIG manager + nObjsNew = 1 + Abc_NtkPiNum(pNtk) + Abc_NtkPoNum(pNtk) + Seq_MapMappingCount(pNtk); + Seq_Resize( pNtkNew->pManFunc, nObjsNew ); + + // duplicate the nodes in the mapping + Vec_PtrForEachEntry( p->vMapAnds, pMatch, i ) + { +// Abc_NtkDupObj( pNtkNew, pMatch->pAnd ); + if ( !pMatch->fCompl ) + pMatch->pAnd->pCopy = Abc_NtkCreateNode( pNtkNew ); + else + pMatch->pAnd->pNext = Abc_NtkCreateNode( pNtkNew ); + } + + // compute the real phase assignment + Vec_PtrForEachEntry( p->vMapAnds, pMatch, i ) + { + pMatch->uPhaseR = 0; + // get the leaves of the cut + vLeaves = Vec_VecEntry( p->vMapCuts, i ); + // convert the leaf nodes + Vec_PtrForEachEntry( vLeaves, pLeaf, k ) + { + SeqEdge = (unsigned)pLeaf; + pLeaf = Abc_NtkObj( pNtk, SeqEdge >> 8 ); + + // set the phase + if ( pMatch->uPhase & (1 << k) ) // neg is required + { + if ( pLeaf->pNext ) // neg is available + pMatch->uPhaseR |= (1 << k); // neg is used +// else +// Seq_NodeSetLag( pLeaf, Seq_NodeGetLagN(pLeaf) ); + } + else // pos is required + { + if ( pLeaf->pCopy == NULL ) // pos is not available + pMatch->uPhaseR |= (1 << k); // neg is used +// else +// Seq_NodeSetLagN( pLeaf, Seq_NodeGetLag(pLeaf) ); + } + } + } + + + // recursively construct the internals of each node + Vec_PtrForEachEntry( p->vMapAnds, pMatch, i ) + { +// if ( pMatch->pSuper == NULL ) +// { +// int x = 0; +// } + vLeaves = Vec_VecEntry( p->vMapCuts, i ); + if ( !pMatch->fCompl ) + Seq_MapMappingBuild_rec( pNtkNew, pNtk, pMatch->pAnd->Id << 8, 1, pMatch->fCompl, Seq_NodeGetLag(pMatch->pAnd), vLeaves, pMatch->uPhaseR ); + else + Seq_MapMappingBuild_rec( pNtkNew, pNtk, pMatch->pAnd->Id << 8, 1, pMatch->fCompl, Seq_NodeGetLagN(pMatch->pAnd), vLeaves, pMatch->uPhaseR ); + } + assert( nObjsNew == pNtkNew->nObjs ); + + // set the POs +// Abc_NtkFinalize( pNtk, pNtkNew ); + Abc_NtkForEachPo( pNtk, pObj, i ) + { + pFanin = Abc_ObjFanin0(pObj); + if ( Abc_ObjFaninC0(pObj) ) + pFaninNew = pFanin->pNext ? pFanin->pNext : pFanin->pCopy; + else + pFaninNew = pFanin->pCopy ? pFanin->pCopy : pFanin->pNext; + pFaninNew = Abc_ObjNotCond( pFaninNew, Abc_ObjFaninC0(pObj) ); + Abc_ObjAddFanin( pObj->pCopy, pFaninNew ); + } + + // duplicate the latches on the PO edges + Abc_NtkForEachPo( pNtk, pObj, i ) + Seq_NodeDupLats( pObj->pCopy, pObj, 0 ); + + // transfer the mapping info to the new manager + Vec_PtrForEachEntry( p->vMapAnds, pMatch, i ) + { + // get the leaves of the cut + vLeaves = Vec_VecEntry( p->vMapCuts, i ); + // convert the leaf nodes + Vec_PtrForEachEntry( vLeaves, pLeaf, k ) + { + SeqEdge = (unsigned)pLeaf; + pLeaf = Abc_NtkObj( pNtk, SeqEdge >> 8 ); + +// Lag = (SeqEdge & 255) + Seq_NodeGetLag(pMatch->pAnd) - Seq_NodeGetLag(pLeaf); + Lag = (SeqEdge & 255) + + (pMatch->fCompl? Seq_NodeGetLagN(pMatch->pAnd) : Seq_NodeGetLag(pMatch->pAnd)) - + (((pMatch->uPhaseR & (1 << k)) > 0)? Seq_NodeGetLagN(pLeaf) : Seq_NodeGetLag(pLeaf) ); + + assert( Lag >= 0 ); + + // translate the old leaf into the leaf in the new network +// if ( pMatch->uPhase & (1 << k) ) // negative phase is required +// pFaninNew = pLeaf->pNext? pLeaf->pNext : pLeaf->pCopy; +// else // positive phase is required +// pFaninNew = pLeaf->pCopy? pLeaf->pCopy : pLeaf->pNext; + + // translate the old leaf into the leaf in the new network + if ( pMatch->uPhaseR & (1 << k) ) // negative phase is required + pFaninNew = pLeaf->pNext; + else // positive phase is required + pFaninNew = pLeaf->pCopy; + + Vec_PtrWriteEntry( vLeaves, k, (void *)((pFaninNew->Id << 8) | Lag) ); +// printf( "%d -> %d\n", pLeaf->Id, pLeaf->pCopy->Id ); + + // UPDATE PHASE!!! leaving only those bits that require inverters + } + // convert the root node +// Vec_PtrWriteEntry( p->vMapAnds, i, pObj->pCopy ); + pMatch->pAnd = pMatch->fCompl? pMatch->pAnd->pNext : pMatch->pAnd->pCopy; + } + pNew = pNtkNew->pManFunc; + pNew->nVarsMax = p->nVarsMax; + pNew->vMapAnds = p->vMapAnds; p->vMapAnds = NULL; + pNew->vMapCuts = p->vMapCuts; p->vMapCuts = NULL; + pNew->fStandCells = p->fStandCells; p->fStandCells = 0; + + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Seq_NtkMapDup(): Network check has failed.\n" ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Checks if the initial states are compatible.] + + Description [Checks of all the initial states on the fanins edges + of the cut have compatible number of latches and initial states. + If this is not true, then the mapped network with the does not have initial + state. Returns the number of LUTs with incompatible edges.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_NtkMapInitCompatible( Abc_Ntk_t * pNtk, int fVerbose ) +{ + Abc_Seq_t * p = pNtk->pManFunc; + Seq_Match_t * pMatch; + Abc_Obj_t * pAnd, * pLeaf, * pFanout0, * pFanout1; + Vec_Vec_t * vTotalEdges; + Vec_Ptr_t * vLeaves, * vEdges; + int i, k, m, Edge0, Edge1, nLatchAfter, nLatches1, nLatches2; + unsigned SeqEdge; + int CountBad = 0, CountAll = 0; + + vTotalEdges = Vec_VecStart( p->nVarsMax ); + // go through all the nodes (cuts) used in the mapping + Vec_PtrForEachEntry( p->vMapAnds, pMatch, i ) + { + pAnd = pMatch->pAnd; +// printf( "*** Node %d.\n", pAnd->Id ); + + // get the cut of this gate + vLeaves = Vec_VecEntry( p->vMapCuts, i ); + + // get the edges pointing to the leaves + Vec_VecClear( vTotalEdges ); + Seq_MapMappingEdges_rec( pNtk, pAnd->Id << 8, NULL, vLeaves, vTotalEdges ); + + // for each leaf, consider its edges + Vec_PtrForEachEntry( vLeaves, pLeaf, k ) + { + SeqEdge = (unsigned)pLeaf; + pLeaf = Abc_NtkObj( pNtk, SeqEdge >> 8 ); + nLatchAfter = SeqEdge & 255; + if ( nLatchAfter == 0 ) + continue; + + // go through the edges + vEdges = Vec_VecEntry( vTotalEdges, k ); + pFanout0 = NULL; + Vec_PtrForEachEntry( vEdges, pFanout1, m ) + { + Edge1 = Abc_ObjIsComplement(pFanout1); + pFanout1 = Abc_ObjRegular(pFanout1); +//printf( "Fanin = %d. Fanout = %d.\n", pLeaf->Id, pFanout1->Id ); + + // make sure this is the same fanin + if ( Edge1 ) + assert( pLeaf == Abc_ObjFanin1(pFanout1) ); + else + assert( pLeaf == Abc_ObjFanin0(pFanout1) ); + + // save the first one + if ( pFanout0 == NULL ) + { + pFanout0 = pFanout1; + Edge0 = Edge1; + continue; + } + // compare the rings + // if they have different number of latches, this is the bug + nLatches1 = Seq_NodeCountLats(pFanout0, Edge0); + nLatches2 = Seq_NodeCountLats(pFanout1, Edge1); + assert( nLatches1 == nLatches2 ); + assert( nLatches1 == nLatchAfter ); + assert( nLatches1 > 0 ); + + // if they have different initial states, this is the problem + if ( !Seq_NodeCompareLats(pFanout0, Edge0, pFanout1, Edge1) ) + { + CountBad++; + break; + } + CountAll++; + } + } + } + if ( fVerbose ) + printf( "The number of pairs of edges checked = %d.\n", CountAll ); + Vec_VecFree( vTotalEdges ); + return CountBad; +} + +/**Function************************************************************* + + Synopsis [Derives the final mapped network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Seq_NtkSeqMapMapped( Abc_Ntk_t * pNtk ) +{ + Abc_Seq_t * p = pNtk->pManFunc; + Seq_Match_t * pMatch; + Abc_Ntk_t * pNtkMap; + Vec_Ptr_t * vLeaves; + Abc_Obj_t * pObj, * pFaninNew; + Seq_Lat_t * pRing; + int i; + + assert( Abc_NtkIsSeq(pNtk) ); + + // start the network + pNtkMap = Abc_NtkStartFrom( pNtk, ABC_NTK_LOGIC, ABC_FUNC_BDD ); + + // duplicate the nodes used in the mapping + Vec_PtrForEachEntry( p->vMapAnds, pMatch, i ) + pMatch->pAnd->pCopy = Abc_NtkCreateNode( pNtkMap ); + + // create and share the latches + Seq_NtkShareLatchesMapping( pNtkMap, pNtk, p->vMapAnds, 0 ); + + // connect the nodes + Vec_PtrForEachEntry( p->vMapAnds, pMatch, i ) + { + pObj = pMatch->pAnd; + // get the leaves of this gate + vLeaves = Vec_VecEntry( p->vMapCuts, i ); + // get the BDD of the node + pObj->pCopy->pData = Seq_MapMappingConnectBdd_rec( pNtk, pObj->Id << 8, NULL, -1, pObj, vLeaves ); + Cudd_Ref( pObj->pCopy->pData ); + // complement the BDD of the cut if it came from the opposite polarity choice cut +// if ( Vec_StrEntry(p->vPhase, i) ) +// pObj->pCopy->pData = Cudd_Not( pObj->pCopy->pData ); + } + + // set the POs + Abc_NtkForEachPo( pNtk, pObj, i ) + { + if ( pRing = Seq_NodeGetRing(pObj,0) ) + pFaninNew = pRing->pLatch; + else + pFaninNew = Abc_ObjFanin0(pObj)->pCopy; + pFaninNew = Abc_ObjNotCond( pFaninNew, Abc_ObjFaninC0(pObj) ); + Abc_ObjAddFanin( pObj->pCopy, pFaninNew ); + } + + // add the latches and their names + Abc_NtkAddDummyBoxNames( pNtkMap ); + Abc_NtkOrderCisCos( pNtkMap ); + // fix the problem with complemented and duplicated CO edges + Abc_NtkLogicMakeSimpleCos( pNtkMap, 1 ); + // make the network minimum base + Abc_NtkMinimumBase( pNtkMap ); + if ( !Abc_NtkCheck( pNtkMap ) ) + fprintf( stdout, "Seq_NtkSeqFpgaMapped(): Network check has failed.\n" ); + return pNtkMap; +} + + + +/**Function************************************************************* + + Synopsis [Counts the number of nodes in the bag.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_MapMappingCount( Abc_Ntk_t * pNtk ) +{ + Abc_Seq_t * p = pNtk->pManFunc; + Vec_Ptr_t * vLeaves; + Seq_Match_t * pMatch; + int i, Counter = 0; + Vec_PtrForEachEntry( p->vMapAnds, pMatch, i ) + { + vLeaves = Vec_VecEntry( p->vMapCuts, i ); + Counter += Seq_MapMappingCount_rec( pNtk, pMatch->pAnd->Id << 8, vLeaves ); + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Counts the number of nodes in the bag.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_MapMappingCount_rec( Abc_Ntk_t * pNtk, unsigned SeqEdge, Vec_Ptr_t * vLeaves ) +{ + Abc_Obj_t * pObj, * pLeaf; + unsigned SeqEdge0, SeqEdge1; + int Lag, i; + // get the object and the lag + pObj = Abc_NtkObj( pNtk, SeqEdge >> 8 ); + Lag = SeqEdge & 255; + // if the node is the fanin of the cut, return + Vec_PtrForEachEntry( vLeaves, pLeaf, i ) + if ( SeqEdge == (unsigned)pLeaf ) + return 0; + // continue unfolding + assert( Abc_AigNodeIsAnd(pObj) ); + // get new sequential edges + assert( Lag + Seq_ObjFaninL0(pObj) < 255 ); + assert( Lag + Seq_ObjFaninL1(pObj) < 255 ); + SeqEdge0 = (Abc_ObjFanin0(pObj)->Id << 8) + Lag + Seq_ObjFaninL0(pObj); + SeqEdge1 = (Abc_ObjFanin1(pObj)->Id << 8) + Lag + Seq_ObjFaninL1(pObj); + // call for the children + return 1 + Seq_MapMappingCount_rec( pNtk, SeqEdge0, vLeaves ) + + Seq_MapMappingCount_rec( pNtk, SeqEdge1, vLeaves ); +} + +/**Function************************************************************* + + Synopsis [Collects the edges pointing to the leaves of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Seq_MapMappingBuild_rec( Abc_Ntk_t * pNtkNew, Abc_Ntk_t * pNtk, unsigned SeqEdge, int fTop, int fCompl, int LagCut, Vec_Ptr_t * vLeaves, unsigned uPhase ) +{ + Abc_Obj_t * pObj, * pObjNew, * pLeaf, * pFaninNew0, * pFaninNew1; + unsigned SeqEdge0, SeqEdge1; + int Lag, i; + // get the object and the lag + pObj = Abc_NtkObj( pNtk, SeqEdge >> 8 ); + Lag = SeqEdge & 255; + // if the node is the fanin of the cut, return + Vec_PtrForEachEntry( vLeaves, pLeaf, i ) + if ( SeqEdge == (unsigned)pLeaf ) + { +// if ( uPhase & (1 << i) ) // negative phase is required +// return pObj->pNext? pObj->pNext : pObj->pCopy; +// else // positive phase is required +// return pObj->pCopy? pObj->pCopy : pObj->pNext; + + if ( uPhase & (1 << i) ) // negative phase is required + return pObj->pNext; + else // positive phase is required + return pObj->pCopy; + } + // continue unfolding + assert( Abc_AigNodeIsAnd(pObj) ); + // get new sequential edges + assert( Lag + Seq_ObjFaninL0(pObj) < 255 ); + assert( Lag + Seq_ObjFaninL1(pObj) < 255 ); + SeqEdge0 = (Abc_ObjFanin0(pObj)->Id << 8) + Lag + Seq_ObjFaninL0(pObj); + SeqEdge1 = (Abc_ObjFanin1(pObj)->Id << 8) + Lag + Seq_ObjFaninL1(pObj); + // call for the children + pObjNew = fTop? (fCompl? pObj->pNext : pObj->pCopy) : Abc_NtkCreateNode( pNtkNew ); + // solve subproblems + pFaninNew0 = Seq_MapMappingBuild_rec( pNtkNew, pNtk, SeqEdge0, 0, fCompl, LagCut, vLeaves, uPhase ); + pFaninNew1 = Seq_MapMappingBuild_rec( pNtkNew, pNtk, SeqEdge1, 0, fCompl, LagCut, vLeaves, uPhase ); + // add the fanins to the node + Abc_ObjAddFanin( pObjNew, Abc_ObjNotCond( pFaninNew0, Abc_ObjFaninC0(pObj) ) ); + Abc_ObjAddFanin( pObjNew, Abc_ObjNotCond( pFaninNew1, Abc_ObjFaninC1(pObj) ) ); + Seq_NodeDupLats( pObjNew, pObj, 0 ); + Seq_NodeDupLats( pObjNew, pObj, 1 ); + // set the lag of the new node equal to the internal lag plus mapping/retiming lag + Seq_NodeSetLag( pObjNew, (char)(Lag + LagCut) ); +// Seq_NodeSetLag( pObjNew, (char)(Lag) ); + return pObjNew; +} + + +/**Function************************************************************* + + Synopsis [Collects the edges pointing to the leaves of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_MapMappingEdges_rec( Abc_Ntk_t * pNtk, unsigned SeqEdge, Abc_Obj_t * pPrev, Vec_Ptr_t * vLeaves, Vec_Vec_t * vMapEdges ) +{ + Abc_Obj_t * pObj, * pLeaf; + unsigned SeqEdge0, SeqEdge1; + int Lag, i; + // get the object and the lag + pObj = Abc_NtkObj( pNtk, SeqEdge >> 8 ); + Lag = SeqEdge & 255; + // if the node is the fanin of the cut, return + Vec_PtrForEachEntry( vLeaves, pLeaf, i ) + { + if ( SeqEdge == (unsigned)pLeaf ) + { + assert( pPrev != NULL ); + Vec_VecPush( vMapEdges, i, pPrev ); + return; + } + } + // continue unfolding + assert( Abc_AigNodeIsAnd(pObj) ); + // get new sequential edges + assert( Lag + Seq_ObjFaninL0(pObj) < 255 ); + assert( Lag + Seq_ObjFaninL1(pObj) < 255 ); + SeqEdge0 = (Abc_ObjFanin0(pObj)->Id << 8) + Lag + Seq_ObjFaninL0(pObj); + SeqEdge1 = (Abc_ObjFanin1(pObj)->Id << 8) + Lag + Seq_ObjFaninL1(pObj); + // call for the children + Seq_MapMappingEdges_rec( pNtk, SeqEdge0, pObj , vLeaves, vMapEdges ); + Seq_MapMappingEdges_rec( pNtk, SeqEdge1, Abc_ObjNot(pObj), vLeaves, vMapEdges ); +} + +/**Function************************************************************* + + Synopsis [Collects the edges pointing to the leaves of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Seq_MapMappingConnectBdd_rec( Abc_Ntk_t * pNtk, unsigned SeqEdge, Abc_Obj_t * pPrev, int Edge, Abc_Obj_t * pRoot, Vec_Ptr_t * vLeaves ) +{ + Seq_Lat_t * pRing; + Abc_Obj_t * pObj, * pLeaf, * pFanin, * pFaninNew; + unsigned SeqEdge0, SeqEdge1; + DdManager * dd = pRoot->pCopy->pNtk->pManFunc; + DdNode * bFunc, * bFunc0, * bFunc1; + int Lag, i, k; + // get the object and the lag + pObj = Abc_NtkObj( pNtk, SeqEdge >> 8 ); + Lag = SeqEdge & 255; + // if the node is the fanin of the cut, add the connection and return + Vec_PtrForEachEntry( vLeaves, pLeaf, i ) + { + if ( SeqEdge == (unsigned)pLeaf ) + { + assert( pPrev != NULL ); + if ( pRing = Seq_NodeGetRing(pPrev,Edge) ) + pFaninNew = pRing->pLatch; + else + pFaninNew = Abc_ObjFanin(pPrev,Edge)->pCopy; + + // check if the root already has this fanin + Abc_ObjForEachFanin( pRoot->pCopy, pFanin, k ) + if ( pFanin == pFaninNew ) + return Cudd_bddIthVar( dd, k ); + Abc_ObjAddFanin( pRoot->pCopy, pFaninNew ); + return Cudd_bddIthVar( dd, k ); + } + } + // continue unfolding + assert( Abc_AigNodeIsAnd(pObj) ); + // get new sequential edges + assert( Lag + Seq_ObjFaninL0(pObj) < 255 ); + assert( Lag + Seq_ObjFaninL1(pObj) < 255 ); + SeqEdge0 = (Abc_ObjFanin0(pObj)->Id << 8) + Lag + Seq_ObjFaninL0(pObj); + SeqEdge1 = (Abc_ObjFanin1(pObj)->Id << 8) + Lag + Seq_ObjFaninL1(pObj); + // call for the children + bFunc0 = Seq_MapMappingConnectBdd_rec( pNtk, SeqEdge0, pObj, 0, pRoot, vLeaves ); Cudd_Ref( bFunc0 ); + bFunc1 = Seq_MapMappingConnectBdd_rec( pNtk, SeqEdge1, pObj, 1, pRoot, vLeaves ); Cudd_Ref( bFunc1 ); + bFunc0 = Cudd_NotCond( bFunc0, Abc_ObjFaninC0(pObj) ); + bFunc1 = Cudd_NotCond( bFunc1, Abc_ObjFaninC1(pObj) ); + // get the BDD of the node + bFunc = Cudd_bddAnd( dd, bFunc0, bFunc1 ); Cudd_Ref( bFunc ); + Cudd_RecursiveDeref( dd, bFunc0 ); + Cudd_RecursiveDeref( dd, bFunc1 ); + // return the BDD + Cudd_Deref( bFunc ); + return bFunc; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/seq/seqMapIter.c b/abc_with_bb_support/src/base/seq/seqMapIter.c new file mode 100644 index 000000000..27a5c6cec --- /dev/null +++ b/abc_with_bb_support/src/base/seq/seqMapIter.c @@ -0,0 +1,623 @@ +/**CFile**************************************************************** + + FileName [seqMapIter.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Construction and manipulation of sequential AIGs.] + + Synopsis [Iterative delay computation in SC mapping/retiming package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: seqMapIter.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "seqInt.h" +#include "main.h" +#include "mio.h" +#include "mapperInt.h" + +// the internal procedures +static float Seq_MapRetimeDelayLagsInternal( Abc_Ntk_t * pNtk, int fVerbose ); +static float Seq_MapRetimeSearch_rec( Abc_Ntk_t * pNtk, float FiMin, float FiMax, float Delta, int fVerbose ); +static int Seq_MapRetimeForPeriod( Abc_Ntk_t * pNtk, float Fi, int fVerbose ); +static int Seq_MapNodeUpdateLValue( Abc_Obj_t * pObj, float Fi, float DelayInv ); +static float Seq_MapCollectNode_rec( Abc_Obj_t * pAnd, float FiBest, Vec_Ptr_t * vMapping, Vec_Vec_t * vMapCuts ); +static void Seq_MapCanonicizeTruthTables( Abc_Ntk_t * pNtk ); + +extern Cut_Man_t * Abc_NtkSeqCuts( Abc_Ntk_t * pNtk, Cut_Params_t * pParams ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes the retiming lags for FPGA mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_MapRetimeDelayLags( Abc_Ntk_t * pNtk, int fVerbose ) +{ + Abc_Seq_t * p = pNtk->pManFunc; + Cut_Params_t Params, * pParams = &Params; + Abc_Obj_t * pObj; + float TotalArea; + int i, clk; + + // set defaults for cut computation + memset( pParams, 0, sizeof(Cut_Params_t) ); + pParams->nVarsMax = p->nVarsMax; // the max cut size ("k" of the k-feasible cuts) + pParams->nKeepMax = 1000; // the max number of cuts kept at a node + pParams->fTruth = 1; // compute truth tables + pParams->fFilter = 1; // filter dominated cuts + pParams->fSeq = 1; // compute sequential cuts + pParams->fVerbose = fVerbose; // the verbosiness flag + + // compute the cuts +clk = clock(); + p->pCutMan = Abc_NtkSeqCuts( pNtk, pParams ); +p->timeCuts = clock() - clk; + if ( fVerbose ) + Cut_ManPrintStats( p->pCutMan ); + + // compute canonical forms of the truth tables of the cuts + Seq_MapCanonicizeTruthTables( pNtk ); + + // compute area flows +// Seq_MapComputeAreaFlows( pNtk, fVerbose ); + + // compute the delays +clk = clock(); + p->FiBestFloat = Seq_MapRetimeDelayLagsInternal( pNtk, fVerbose ); + if ( p->FiBestFloat == 0.0 ) + return 0; +p->timeDelay = clock() - clk; +/* + { + FILE * pTable; + pTable = fopen( "stats.txt", "a+" ); + fprintf( pTable, "%s ", pNtk->pName ); + fprintf( pTable, "%.2f ", p->FiBestFloat ); + fprintf( pTable, "%.2f ", (float)(p->timeCuts)/(float)(CLOCKS_PER_SEC) ); + fprintf( pTable, "%.2f ", (float)(p->timeDelay)/(float)(CLOCKS_PER_SEC) ); + fprintf( pTable, "\n" ); + fclose( pTable ); + } +*/ + // clean the marks + Abc_NtkForEachObj( pNtk, pObj, i ) + assert( !pObj->fMarkA && !pObj->fMarkB ); + + // collect the nodes and cuts used in the mapping + p->vMapAnds = Vec_PtrAlloc( 1000 ); + p->vMapCuts = Vec_VecAlloc( 1000 ); + TotalArea = 0.0; + Abc_NtkForEachPo( pNtk, pObj, i ) + TotalArea += Seq_MapCollectNode_rec( Abc_ObjChild0(pObj), p->FiBestFloat, p->vMapAnds, p->vMapCuts ); + + // clean the marks + Abc_NtkForEachObj( pNtk, pObj, i ) + pObj->fMarkA = pObj->fMarkB = 0; + + if ( fVerbose ) + printf( "Total area = %6.2f.\n", TotalArea ); + + // remove the cuts + Cut_ManStop( p->pCutMan ); + p->pCutMan = NULL; + return 1; +} + +/**Function************************************************************* + + Synopsis [Retimes AIG for optimal delay using Pan's algorithm.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Seq_MapRetimeDelayLagsInternal( Abc_Ntk_t * pNtk, int fVerbose ) +{ + Abc_Seq_t * p = pNtk->pManFunc; + Abc_Obj_t * pNode; + float FiMax, FiBest, Delta; + int i, RetValue; + char NodeLag; + + assert( Abc_NtkIsSeq( pNtk ) ); + + // assign the accuracy for min-period computation + Delta = Mio_LibraryReadDelayNand2Max(Abc_FrameReadLibGen()); + if ( Delta == 0.0 ) + { + Delta = Mio_LibraryReadDelayAnd2Max(Abc_FrameReadLibGen()); + if ( Delta == 0.0 ) + { + printf( "Cannot retime/map if the library does not have NAND2 or AND2.\n" ); + return 0.0; + } + } + + // get the upper bound on the clock period + FiMax = Delta * (5 + Seq_NtkLevelMax(pNtk)); + Delta /= 2; + + // make sure this clock period is feasible + if ( !Seq_MapRetimeForPeriod( pNtk, FiMax, fVerbose ) ) + { + Vec_StrFill( p->vLags, p->nSize, 0 ); + Vec_StrFill( p->vLagsN, p->nSize, 0 ); + printf( "Error: The upper bound on the clock period cannot be computed.\n" ); + printf( "The reason for this error may be the presence in the circuit of logic\n" ); + printf( "that is not reachable from the PIs. Mapping/retiming is not performed.\n" ); + return 0; + } + + // search for the optimal clock period between 0 and nLevelMax + FiBest = Seq_MapRetimeSearch_rec( pNtk, 0.0, FiMax, Delta, fVerbose ); + + // recompute the best l-values + RetValue = Seq_MapRetimeForPeriod( pNtk, FiBest, fVerbose ); + assert( RetValue ); + + // fix the problem with non-converged delays + Abc_AigForEachAnd( pNtk, pNode, i ) + { + if ( Seq_NodeGetLValueP(pNode) < -ABC_INFINITY/2 ) + Seq_NodeSetLValueP( pNode, 0 ); + if ( Seq_NodeGetLValueN(pNode) < -ABC_INFINITY/2 ) + Seq_NodeSetLValueN( pNode, 0 ); + } + + // write the retiming lags for both phases of each node + Vec_StrFill( p->vLags, p->nSize, 0 ); + Vec_StrFill( p->vLagsN, p->nSize, 0 ); + Abc_AigForEachAnd( pNtk, pNode, i ) + { + NodeLag = Seq_NodeComputeLagFloat( Seq_NodeGetLValueP(pNode), FiBest ); + Seq_NodeSetLag( pNode, NodeLag ); + NodeLag = Seq_NodeComputeLagFloat( Seq_NodeGetLValueN(pNode), FiBest ); + Seq_NodeSetLagN( pNode, NodeLag ); +//printf( "%6d=(%d,%d) ", pNode->Id, Seq_NodeGetLag(pNode), Seq_NodeGetLagN(pNode) ); +// if ( Seq_NodeGetLag(pNode) != Seq_NodeGetLagN(pNode) ) +// { +//printf( "%6d=(%d,%d) ", pNode->Id, Seq_NodeGetLag(pNode), Seq_NodeGetLagN(pNode) ); +// } + } +//printf( "\n\n" ); + + // print the result + if ( fVerbose ) + printf( "The best clock period after mapping/retiming is %6.2f.\n", FiBest ); + return FiBest; +} + +/**Function************************************************************* + + Synopsis [Performs binary search for the optimal clock period.] + + Description [Assumes that FiMin is infeasible while FiMax is feasible.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Seq_MapRetimeSearch_rec( Abc_Ntk_t * pNtk, float FiMin, float FiMax, float Delta, int fVerbose ) +{ + float Median; + assert( FiMin < FiMax ); + if ( FiMin + Delta >= FiMax ) + return FiMax; + Median = FiMin + (FiMax - FiMin)/2; + if ( Seq_MapRetimeForPeriod( pNtk, Median, fVerbose ) ) + return Seq_MapRetimeSearch_rec( pNtk, FiMin, Median, Delta, fVerbose ); // Median is feasible + else + return Seq_MapRetimeSearch_rec( pNtk, Median, FiMax, Delta, fVerbose ); // Median is infeasible +} + +/**Function************************************************************* + + Synopsis [Returns 1 if retiming with this clock period is feasible.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_MapRetimeForPeriod( Abc_Ntk_t * pNtk, float Fi, int fVerbose ) +{ + Abc_Seq_t * p = pNtk->pManFunc; + Abc_Obj_t * pObj; + float DelayInv = Mio_LibraryReadDelayInvMax(Abc_FrameReadLibGen()); + int i, c, RetValue, fChange, Counter; + char * pReason = ""; + + // set l-values of all nodes to be minus infinity + Vec_IntFill( p->vLValues, p->nSize, Abc_Float2Int( (float)-ABC_INFINITY ) ); + Vec_IntFill( p->vLValuesN, p->nSize, Abc_Float2Int( (float)-ABC_INFINITY ) ); + Vec_StrFill( p->vUses, p->nSize, 0 ); + + // set l-values of constants and PIs + pObj = Abc_NtkObj( pNtk, 0 ); + Seq_NodeSetLValueP( pObj, 0.0 ); + Seq_NodeSetLValueN( pObj, 0.0 ); + Abc_NtkForEachPi( pNtk, pObj, i ) + { + Seq_NodeSetLValueP( pObj, 0.0 ); + Seq_NodeSetLValueN( pObj, DelayInv ); + } + + // update all values iteratively + Counter = 0; + for ( c = 0; c < p->nMaxIters; c++ ) + { + fChange = 0; + Abc_AigForEachAnd( pNtk, pObj, i ) + { + Counter++; + RetValue = Seq_MapNodeUpdateLValue( pObj, Fi, DelayInv ); + if ( RetValue == SEQ_UPDATE_YES ) + fChange = 1; + } + Abc_NtkForEachPo( pNtk, pObj, i ) + { + RetValue = Seq_MapNodeUpdateLValue( pObj, Fi, DelayInv ); + if ( RetValue == SEQ_UPDATE_FAIL ) + break; + } + if ( RetValue == SEQ_UPDATE_FAIL ) + break; + if ( fChange == 0 ) + break; +//printf( "\n\n" ); + } + if ( c == p->nMaxIters ) + { + RetValue = SEQ_UPDATE_FAIL; + pReason = "(timeout)"; + } + else + c++; + + // report the results + if ( fVerbose ) + { + if ( RetValue == SEQ_UPDATE_FAIL ) + printf( "Period = %6.2f. Iterations = %3d. Updates = %10d. Infeasible %s\n", Fi, c, Counter, pReason ); + else + printf( "Period = %6.2f. Iterations = %3d. Updates = %10d. Feasible\n", Fi, c, Counter ); + } + return RetValue != SEQ_UPDATE_FAIL; +} + + + +/**Function************************************************************* + + Synopsis [Computes the l-value of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Seq_MapSuperGetArrival( Abc_Obj_t * pObj, float Fi, Seq_Match_t * pMatch, float DelayMax ) +{ + Abc_Seq_t * p = pObj->pNtk->pManFunc; + Abc_Obj_t * pFanin; + float lValueCur, lValueMax; + int i; + lValueMax = -ABC_INFINITY; + for ( i = pMatch->pCut->nLeaves - 1; i >= 0; i-- ) + { + // get the arrival time of the fanin + pFanin = Abc_NtkObj( pObj->pNtk, pMatch->pCut->pLeaves[i] >> 8 ); + if ( pMatch->uPhase & (1 << i) ) + lValueCur = Seq_NodeGetLValueN(pFanin) - Fi * (pMatch->pCut->pLeaves[i] & 255); + else + lValueCur = Seq_NodeGetLValueP(pFanin) - Fi * (pMatch->pCut->pLeaves[i] & 255); + // add the arrival time of this pin + if ( lValueMax < lValueCur + pMatch->pSuper->tDelaysR[i].Worst ) + lValueMax = lValueCur + pMatch->pSuper->tDelaysR[i].Worst; + if ( lValueMax < lValueCur + pMatch->pSuper->tDelaysF[i].Worst ) + lValueMax = lValueCur + pMatch->pSuper->tDelaysF[i].Worst; + if ( lValueMax > DelayMax + p->fEpsilon ) + return ABC_INFINITY; + } + return lValueMax; +} + +/**Function************************************************************* + + Synopsis [Computes the l-value of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Seq_MapNodeComputeCut( Abc_Obj_t * pObj, Cut_Cut_t * pCut, int fCompl, float Fi, Seq_Match_t * pMatchBest ) +{ + Seq_Match_t Match, * pMatchCur = &Match; + Abc_Seq_t * p = pObj->pNtk->pManFunc; + Map_Super_t * pSuper, * pSuperList; + unsigned uCanon[2]; + float lValueBest, lValueCur; + int i; + assert( pCut->nLeaves < 6 ); + // get the canonical truth table of this cut + uCanon[0] = uCanon[1] = (fCompl? pCut->uCanon0 : pCut->uCanon1); + if ( uCanon[0] == 0 || ~uCanon[0] == 0 ) + { + if ( pMatchBest ) + { + memset( pMatchBest, 0, sizeof(Seq_Match_t) ); + pMatchBest->pCut = pCut; + } + return (float)0.0; + } + // match the given phase of the cut + pSuperList = Map_SuperTableLookupC( p->pSuperLib, uCanon ); + // compute the arrival times of each supergate + lValueBest = ABC_INFINITY; + for ( pSuper = pSuperList; pSuper; pSuper = pSuper->pNext ) + { + // create the match + pMatchCur->pCut = pCut; + pMatchCur->pSuper = pSuper; + // get the phase + for ( i = 0; i < (int)pSuper->nPhases; i++ ) + { + pMatchCur->uPhase = (fCompl? pCut->Num0 : pCut->Num1) ^ pSuper->uPhases[i]; + // find the arrival time of this match + lValueCur = Seq_MapSuperGetArrival( pObj, Fi, pMatchCur, lValueBest ); + if ( lValueBest > lValueCur )//&& lValueCur > -ABC_INFINITY/2 ) + { + lValueBest = lValueCur; + if ( pMatchBest ) + *pMatchBest = *pMatchCur; + } + } + } +// assert( lValueBest < ABC_INFINITY/2 ); + return lValueBest; +} + +/**Function************************************************************* + + Synopsis [Computes the l-value of the node.] + + Description [The node can be internal or a PO.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Seq_MapNodeComputePhase( Abc_Obj_t * pObj, int fCompl, float Fi, Seq_Match_t * pMatchBest ) +{ + Seq_Match_t Match, * pMatchCur = &Match; + Cut_Cut_t * pList, * pCut; + float lValueBest, lValueCut; + // get the list of cuts + pList = Abc_NodeReadCuts( Seq_NodeCutMan(pObj), pObj ); + // get the arrival time of the best non-trivial cut + lValueBest = ABC_INFINITY; + for ( pCut = pList->pNext; pCut; pCut = pCut->pNext ) + { + lValueCut = Seq_MapNodeComputeCut( pObj, pCut, fCompl, Fi, pMatchBest? pMatchCur : NULL ); + if ( lValueBest > lValueCut ) + { + lValueBest = lValueCut; + if ( pMatchBest ) + *pMatchBest = *pMatchCur; + } + } +// assert( lValueBest < ABC_INFINITY/2 ); + return lValueBest; +} + +/**Function************************************************************* + + Synopsis [Computes the l-value of the node.] + + Description [The node can be internal or a PO.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_MapNodeUpdateLValue( Abc_Obj_t * pObj, float Fi, float DelayInv ) +{ + Abc_Seq_t * p = pObj->pNtk->pManFunc; + Cut_Cut_t * pList; + char Use; + float lValueOld0, lValueOld1, lValue0, lValue1, lValue; + assert( !Abc_ObjIsPi(pObj) ); + assert( Abc_ObjFaninNum(pObj) > 0 ); + // consider the case of the PO + if ( Abc_ObjIsPo(pObj) ) + { + if ( Abc_ObjFaninC0(pObj) ) // PO requires negative polarity + lValue = Seq_NodeGetLValueN(Abc_ObjFanin0(pObj)) - Fi * Seq_ObjFaninL0(pObj); + else + lValue = Seq_NodeGetLValueP(Abc_ObjFanin0(pObj)) - Fi * Seq_ObjFaninL0(pObj); + return (lValue > Fi + p->fEpsilon)? SEQ_UPDATE_FAIL : SEQ_UPDATE_NO; + } + // get the cuts + pList = Abc_NodeReadCuts( Seq_NodeCutMan(pObj), pObj ); + if ( pList == NULL ) + return SEQ_UPDATE_NO; + // compute the arrival time of both phases + lValue0 = Seq_MapNodeComputePhase( pObj, 1, Fi, NULL ); + lValue1 = Seq_MapNodeComputePhase( pObj, 0, Fi, NULL ); + // consider the case when negative phase is too slow + if ( lValue0 > lValue1 + DelayInv + p->fEpsilon ) + lValue0 = lValue1 + DelayInv, Use = 2; + else if ( lValue1 > lValue0 + DelayInv + p->fEpsilon ) + lValue1 = lValue0 + DelayInv, Use = 1; + else + Use = 3; + // set the uses of the phases + Seq_NodeSetUses( pObj, Use ); + // get the old arrival times + lValueOld0 = Seq_NodeGetLValueN(pObj); + lValueOld1 = Seq_NodeGetLValueP(pObj); + // compare + if ( lValue0 <= lValueOld0 + p->fEpsilon && lValue1 <= lValueOld1 + p->fEpsilon ) + return SEQ_UPDATE_NO; + assert( lValue0 < ABC_INFINITY/2 ); + assert( lValue1 < ABC_INFINITY/2 ); + // update the values + if ( lValue0 > lValueOld0 + p->fEpsilon ) + Seq_NodeSetLValueN( pObj, lValue0 ); + if ( lValue1 > lValueOld1 + p->fEpsilon ) + Seq_NodeSetLValueP( pObj, lValue1 ); +//printf( "%6d=(%4.2f,%4.2f) ", pObj->Id, Seq_NodeGetLValueP(pObj), Seq_NodeGetLValueN(pObj) ); + return SEQ_UPDATE_YES; +} + + + +/**Function************************************************************* + + Synopsis [Derives the parameters of the best mapping/retiming for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Seq_MapCollectNode_rec( Abc_Obj_t * pAnd, float FiBest, Vec_Ptr_t * vMapping, Vec_Vec_t * vMapCuts ) +{ + Seq_Match_t * pMatch; + Abc_Obj_t * pFanin; + int k, fCompl, Use; + float AreaInv = Mio_LibraryReadAreaInv(Abc_FrameReadLibGen()); + float Area; + + // get the polarity of the node + fCompl = Abc_ObjIsComplement(pAnd); + pAnd = Abc_ObjRegular(pAnd); + + // skip visited nodes + if ( !fCompl ) + { // need the positive polarity + if ( pAnd->fMarkA ) + return 0.0; + pAnd->fMarkA = 1; + } + else + { // need the negative polarity + if ( pAnd->fMarkB ) + return 0.0; + pAnd->fMarkB = 1; + } + + // skip if this is a PI or a constant + if ( !Abc_AigNodeIsAnd(pAnd) ) + { + if ( Abc_ObjIsPi(pAnd) && fCompl ) + return AreaInv; + return 0.0; + } + + // check the uses of this node + Use = Seq_NodeGetUses( pAnd ); + if ( !fCompl && Use == 1 ) // the pos phase is required; only the neg phase is used + { + Area = Seq_MapCollectNode_rec( Abc_ObjNot(pAnd), FiBest, vMapping, vMapCuts ); + return Area + AreaInv; + } + if ( fCompl && Use == 2 ) // the neg phase is required; only the pos phase is used + { + Area = Seq_MapCollectNode_rec( pAnd, FiBest, vMapping, vMapCuts ); + return Area + AreaInv; + } + // both phases are used; the needed one can be selected + + // get the best match + pMatch = ALLOC( Seq_Match_t, 1 ); + memset( pMatch, 1, sizeof(Seq_Match_t) ); + Seq_MapNodeComputePhase( pAnd, fCompl, FiBest, pMatch ); + pMatch->pAnd = pAnd; + pMatch->fCompl = fCompl; + pMatch->fCutInv = pMatch->pCut->fCompl; + pMatch->PolUse = Use; + + // call for the fanin cuts + Area = pMatch->pSuper? pMatch->pSuper->Area : (float)0.0; + for ( k = 0; k < (int)pMatch->pCut->nLeaves; k++ ) + { + pFanin = Abc_NtkObj( pAnd->pNtk, pMatch->pCut->pLeaves[k] >> 8 ); + if ( pMatch->uPhase & (1 << k) ) + pFanin = Abc_ObjNot( pFanin ); + Area += Seq_MapCollectNode_rec( pFanin, FiBest, vMapping, vMapCuts ); + } + + // add this node + Vec_PtrPush( vMapping, pMatch ); + for ( k = 0; k < (int)pMatch->pCut->nLeaves; k++ ) + Vec_VecPush( vMapCuts, Vec_PtrSize(vMapping)-1, (void *)pMatch->pCut->pLeaves[k] ); + + // the cut will become unavailable when the cuts are deallocated + pMatch->pCut = NULL; + + return Area; +} + +/**Function************************************************************* + + Synopsis [Computes the canonical versions of the truth tables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_MapCanonicizeTruthTables( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + Cut_Cut_t * pCut, * pList; + int i; + Abc_AigForEachAnd( pNtk, pObj, i ) + { + pList = Abc_NodeReadCuts( Seq_NodeCutMan(pObj), pObj ); + if ( pList == NULL ) + continue; + for ( pCut = pList->pNext; pCut; pCut = pCut->pNext ) + Cut_TruthNCanonicize( pCut ); + } +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/base/seq/seqMaxMeanCycle.c b/abc_with_bb_support/src/base/seq/seqMaxMeanCycle.c new file mode 100644 index 000000000..d8660b700 --- /dev/null +++ b/abc_with_bb_support/src/base/seq/seqMaxMeanCycle.c @@ -0,0 +1,567 @@ +/**CFile**************************************************************** + + FileName [seqMaxMeanCycle.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Construction and manipulation of sequential AIGs.] + + Synopsis [Efficient computation of maximum mean cycle times.] + + Author [Aaron P. Hurst] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 15, 2006.] + + Revision [$Id: seqMaxMeanCycle.c,v 1.00 2005/05/15 00:00:00 ahurst Exp $] + +***********************************************************************/ + +#include "seqInt.h" +#include "hash.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +struct Abc_ManTime_t_ +{ + Abc_Time_t tArrDef; + Abc_Time_t tReqDef; + Vec_Ptr_t * vArrs; + Vec_Ptr_t * vReqs; +}; + +typedef struct Seq_HowardData_t_ +{ + char visited; + int mark; + int policy; + float cycle; + float skew; + float delay; +} Seq_HowardData_t; + +// accessing the arrival and required times of a node +static inline Abc_Time_t * Abc_NodeArrival( Abc_Obj_t * pNode ) { return pNode->pNtk->pManTime->vArrs->pArray[pNode->Id]; } +static inline Abc_Time_t * Abc_NodeRequired( Abc_Obj_t * pNode ) { return pNode->pNtk->pManTime->vReqs->pArray[pNode->Id]; } + +Hash_Ptr_t * Seq_NtkPathDelays( Abc_Ntk_t * pNtk, int fVerbose ); +void Seq_NtkMergePios( Abc_Ntk_t * pNtk, Hash_Ptr_t * hFwdDelays, int fVerbose ); + +void Seq_NtkHowardLoop( Abc_Ntk_t * pNtk, Hash_Ptr_t * hFwdDelays, + Hash_Ptr_t * hNodeData, int node, + int *howardDepth, float *howardDelay, int *howardSink, + float *maxMeanCycle); +void Abc_NtkDfsReverse_rec2( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes, Vec_Ptr_t * vEndpoints ); + +#define Seq_NtkGetPathDelay( hFwdDelays, from, to ) \ + (Hash_PtrExists(hFwdDelays, from)?Hash_FltEntry( ((Hash_Flt_t *)Hash_PtrEntry(hFwdDelays, from, 0)), to, 0):0 ) + +#define HOWARD_EPSILON 1e-3 +#define ZERO_SLOP 1e-5 +#define REMOVE_ZERO_SLOP( x ) \ + (x = (x > -ZERO_SLOP && x < ZERO_SLOP)?0:x) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes maximum mean cycle time.] + + Description [Uses Howard's algorithm.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Seq_NtkHoward( Abc_Ntk_t * pNtk, int fVerbose ) { + + Abc_Obj_t * pObj; + Hash_Ptr_t * hFwdDelays; + Hash_Flt_t * hOutgoing; + Hash_Ptr_Entry_t * pSourceEntry, * pNodeEntry; + Hash_Flt_Entry_t * pSinkEntry; + int i, j, iteration = 0; + int source, sink; + int fChanged; + int howardDepth, howardSink = 0; + float delay, howardDelay, t; + float maxMeanCycle = -ABC_INFINITY; + Hash_Ptr_t * hNodeData; + Seq_HowardData_t * pNodeData, * pSourceData, * pSinkData; + + // gather timing constraints + hFwdDelays = Seq_NtkPathDelays( pNtk, fVerbose ); + Seq_NtkMergePios( pNtk, hFwdDelays, fVerbose ); + + // initialize data, create initial policy + hNodeData = Hash_PtrAlloc( hFwdDelays->nSize ); + Hash_PtrForEachEntry( hFwdDelays, pSourceEntry, i ) { + Hash_PtrWriteEntry( hNodeData, pSourceEntry->key, + (pNodeData = ALLOC(Seq_HowardData_t, 1)) ); + pNodeData->skew = 0.0; + pNodeData->policy = 0; + hOutgoing = (Hash_Flt_t *)(pSourceEntry->data); + assert(hOutgoing); + + Hash_FltForEachEntry( hOutgoing, pSinkEntry, j ) { + sink = pSinkEntry->key; + delay = pSinkEntry->data; + if (delay > pNodeData->skew) { + pNodeData->policy = sink; + pNodeData->skew = delay; + } + } + } + + // iteratively refine policy + do { + iteration++; + fChanged = 0; + howardDelay = 0.0; + howardDepth = 0; + + // reset data + Hash_PtrForEachEntry( hNodeData, pNodeEntry, i ) { + pNodeData = (Seq_HowardData_t *)pNodeEntry->data; + pNodeData->skew = -ABC_INFINITY; + pNodeData->cycle = -ABC_INFINITY; + pNodeData->mark = 0; + pNodeData->visited = 0; + } + + // find loops in policy graph + Hash_PtrForEachEntry( hNodeData, pNodeEntry, i ) { + pNodeData = (Seq_HowardData_t *)(pNodeEntry->data); + assert(pNodeData); + if (!pNodeData->visited) + Seq_NtkHowardLoop( pNtk, hFwdDelays, + hNodeData, pNodeEntry->key, + &howardDepth, &howardDelay, &howardSink, &maxMeanCycle); + } + + if (!howardSink) { + return -1; + } + + // improve policy by tightening loops + Hash_PtrForEachEntry( hFwdDelays, pSourceEntry, i ) { + source = pSourceEntry->key; + pSourceData = (Seq_HowardData_t *)Hash_PtrEntry( hNodeData, source, 0 ); + assert(pSourceData); + hOutgoing = (Hash_Flt_t *)(pSourceEntry->data); + assert(hOutgoing); + Hash_FltForEachEntry( hOutgoing, pSinkEntry, j ) { + sink = pSinkEntry->key; + pSinkData = (Seq_HowardData_t *)Hash_PtrEntry( hNodeData, sink, 0 ); + assert(pSinkData); + delay = pSinkEntry->data; + + if (pSinkData->cycle > pSourceData->cycle + HOWARD_EPSILON) { + fChanged = 1; + pSourceData->cycle = pSinkData->cycle; + pSourceData->policy = sink; + } + } + } + + // improve policy by correcting skews + if (!fChanged) { + Hash_PtrForEachEntry( hFwdDelays, pSourceEntry, i ) { + source = pSourceEntry->key; + pSourceData = (Seq_HowardData_t *)Hash_PtrEntry( hNodeData, source, 0 ); + assert(pSourceData); + hOutgoing = (Hash_Flt_t *)(pSourceEntry->data); + assert(hOutgoing); + Hash_FltForEachEntry( hOutgoing, pSinkEntry, j ) { + sink = pSinkEntry->key; + pSinkData = (Seq_HowardData_t *)Hash_PtrEntry( hNodeData, sink, 0 ); + assert(pSinkData); + delay = pSinkEntry->data; + + if (pSinkData->cycle < 0.0 || pSinkData->cycle < pSourceData->cycle) + continue; + + t = delay - pSinkData->cycle + pSinkData->skew; + if (t > pSourceData->skew + HOWARD_EPSILON) { + fChanged = 1; + pSourceData->skew = t; + pSourceData->policy = sink; + } + } + } + } + + if (fVerbose) printf("Iteration %d \t Period = %.2f\n", iteration, maxMeanCycle); + } while (fChanged); + + // set global skew, mmct + pNodeData = Hash_PtrEntry( hNodeData, -1, 0 ); + pNtk->globalSkew = -pNodeData->skew; + pNtk->maxMeanCycle = maxMeanCycle; + + // set endpoint skews + Vec_FltGrow( pNtk->vSkews, Abc_NtkLatchNum( pNtk ) ); + pNtk->vSkews->nSize = Abc_NtkLatchNum( pNtk ); + Abc_NtkForEachLatch( pNtk, pObj, i ) { + pNodeData = Hash_PtrEntry( hNodeData, pObj->Id, 0 ); + // skews are set based on latch # NOT id # + Abc_NtkSetLatSkew( pNtk, i, pNodeData->skew ); + } + + // free node data + Hash_PtrForEachEntry( hNodeData, pNodeEntry, i ) { + pNodeData = (Seq_HowardData_t *)(pNodeEntry->data); + FREE( pNodeData ); + } + Hash_PtrFree(hNodeData); + + // free delay data + Hash_PtrForEachEntry( hFwdDelays, pSourceEntry, i ) { + Hash_FltFree( (Hash_Flt_t *)(pSourceEntry->data) ); + } + Hash_PtrFree(hFwdDelays); + + return maxMeanCycle; +} + +/**Function************************************************************* + + Synopsis [Computes the mean cycle times of current policy graph.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NtkHowardLoop( Abc_Ntk_t * pNtk, Hash_Ptr_t * hFwdDelays, + Hash_Ptr_t * hNodeData, int node, + int *howardDepth, float *howardDelay, int *howardSink, + float *maxMeanCycle) { + + Seq_HowardData_t * pNodeData, *pToData; + float delay, t; + + pNodeData = (Seq_HowardData_t *)Hash_PtrEntry( hNodeData, node, 0 ); + assert(pNodeData); + pNodeData->visited = 1; + pNodeData->mark = ++(*howardDepth); + pNodeData->delay = (*howardDelay); + if (pNodeData->policy) { + pToData = (Seq_HowardData_t *)Hash_PtrEntry( hNodeData, pNodeData->policy, 0 ); + assert(pToData); + delay = Seq_NtkGetPathDelay( hFwdDelays, node, pNodeData->policy ); + assert(delay > 0.0); + (*howardDelay) += delay; + if (pToData->mark) { + t = (*howardDelay - pToData->delay) / (*howardDepth - pToData->mark + 1); + pNodeData->cycle = t; + pNodeData->skew = 0.0; + if (*maxMeanCycle < t) { + *maxMeanCycle = t; + *howardSink = pNodeData->policy; + } + } else { + if(!pToData->visited) { + Seq_NtkHowardLoop(pNtk, hFwdDelays, hNodeData, pNodeData->policy, + howardDepth, howardDelay, howardSink, maxMeanCycle); + } + if(pToData->cycle > 0) { + t = delay - pToData->cycle + pToData->skew; + pNodeData->skew = t; + pNodeData->cycle = pToData->cycle; + } + } + } + *howardDelay = pNodeData->delay; + pNodeData->mark = 0; + --(*howardDepth); +} + +/**Function************************************************************* + + Synopsis [Computes the register-to-register delays.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hash_Ptr_t * Seq_NtkPathDelays( Abc_Ntk_t * pNtk, int fVerbose ) { + + Abc_Time_t * pTime, ** ppTimes; + Abc_Obj_t * pObj, * pDriver, * pStart, * pFanout; + Vec_Ptr_t * vNodes, * vEndpoints; + int i, j, nPaths = 0; + Hash_Flt_t * hOutgoing; + Hash_Ptr_t * hFwdDelays; + float nMaxPath = 0, nSumPath = 0; + + extern void Abc_NtkTimePrepare( Abc_Ntk_t * pNtk ); + extern void Abc_NodeDelayTraceArrival( Abc_Obj_t * pNode ); + + if (fVerbose) printf("Gathering path delays...\n"); + + hFwdDelays = Hash_PtrAlloc( Abc_NtkCiNum( pNtk ) ); + + assert( Abc_NtkIsMappedLogic(pNtk) ); + + Abc_NtkTimePrepare( pNtk ); + ppTimes = (Abc_Time_t **)pNtk->pManTime->vArrs->pArray; + vNodes = Vec_PtrAlloc( 100 ); + vEndpoints = Vec_PtrAlloc( 100 ); + + // set the initial times (i.e. ignore all inputs) + Abc_NtkForEachObj( pNtk, pObj, i) { + pTime = ppTimes[pObj->Id]; + pTime->Fall = pTime->Rise = pTime->Worst = -ABC_INFINITY; + } + + // starting at each Ci, compute timing forward + Abc_NtkForEachCi( pNtk, pStart, j ) { + + hOutgoing = Hash_FltAlloc( 10 ); + Hash_PtrWriteEntry( hFwdDelays, pStart->Id, (void *)(hOutgoing) ); + + // seed the starting point of interest + pTime = ppTimes[pStart->Id]; + pTime->Fall = pTime->Rise = pTime->Worst = 0.0; + + // find a DFS ordering from the start + Abc_NtkIncrementTravId( pNtk ); + Abc_NodeSetTravIdCurrent( pStart ); + pObj = Abc_ObjFanout0Ntk(pStart); + Abc_ObjForEachFanout( pObj, pFanout, i ) + Abc_NtkDfsReverse_rec2( pFanout, vNodes, vEndpoints ); + if ( Abc_ObjIsCo( pStart ) ) + Vec_PtrPush( vEndpoints, pStart ); + + // do timing analysis + for ( i = vNodes->nSize-1; i >= 0; --i ) + Abc_NodeDelayTraceArrival( vNodes->pArray[i] ); + + // there is a path to each set of Co endpoints + Vec_PtrForEachEntry( vEndpoints, pObj, i ) + { + assert(pObj); + assert( Abc_ObjIsCo( pObj ) ); + pDriver = Abc_ObjFanin0(pObj); + pTime = Abc_NodeArrival(pDriver); + if ( pTime->Worst > 0 ) { + Hash_FltWriteEntry( hOutgoing, pObj->Id, pTime->Worst ); + nPaths++; + // if (fVerbose) printf("\tpath %d,%d delay = %f\n", pStart->Id, pObj->Id, pTime->Worst); + nSumPath += pTime->Worst; + if (pTime->Worst > nMaxPath) + nMaxPath = pTime->Worst; + } + } + + // clear the times that were altered + for ( i = 0; i < vNodes->nSize; i++ ) { + pObj = (Abc_Obj_t *)(vNodes->pArray[i]); + pTime = ppTimes[pObj->Id]; + pTime->Fall = pTime->Rise = pTime->Worst = -ABC_INFINITY; + } + pTime = ppTimes[pStart->Id]; + pTime->Fall = pTime->Rise = pTime->Worst = -ABC_INFINITY; + + Vec_PtrClear( vNodes ); + Vec_PtrClear( vEndpoints ); + } + + Vec_PtrFree( vNodes ); + + // rezero Cis (note: these should be restored to values if they were nonzero) + Abc_NtkForEachCi( pNtk, pObj, i) { + pTime = ppTimes[pObj->Id]; + pTime->Fall = pTime->Rise = pTime->Worst = 0.0; + } + + if (fVerbose) printf("Num. paths = %d\tMax. Path Delay = %.2f\tAvg. Path Delay = %.2f\n", nPaths, nMaxPath, nSumPath / nPaths); + return hFwdDelays; +} + + +/**Function************************************************************* + + Synopsis [Merges all the Pios together into one ID = -1.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NtkMergePios( Abc_Ntk_t * pNtk, Hash_Ptr_t * hFwdDelays, + int fVerbose ) { + + Abc_Obj_t * pObj; + Hash_Flt_Entry_t * pSinkEntry; + Hash_Ptr_Entry_t * pSourceEntry; + Hash_Flt_t * hOutgoing, * hPioSource; + int i, j; + int source, sink, nMerges = 0; + float delay = 0, max_delay = 0; + Vec_Int_t * vFreeList; + + vFreeList = Vec_IntAlloc( 10 ); + + // create a new "-1" source entry for the Pios + hPioSource = Hash_FltAlloc( 100 ); + Hash_PtrWriteEntry( hFwdDelays, -1, (void *)(hPioSource) ); + + // merge all edges with a Pio as a source + Abc_NtkForEachPi( pNtk, pObj, i ) { + source = pObj->Id; + hOutgoing = (Hash_Flt_t *)Hash_PtrEntry( hFwdDelays, source, 0 ); + if (!hOutgoing) continue; + + Hash_PtrForEachEntry( hOutgoing, pSinkEntry, j ) { + nMerges++; + sink = pSinkEntry->key; + delay = pSinkEntry->data; + if (Hash_FltEntry( hPioSource, sink, 1 ) < delay) { + Hash_FltWriteEntry( hPioSource, sink, delay ); + } + } + + Hash_FltFree( hOutgoing ); + Hash_PtrRemove( hFwdDelays, source ); + } + + // merge all edges with a Pio as a sink + Hash_PtrForEachEntry( hFwdDelays, pSourceEntry, i ) { + hOutgoing = (Hash_Flt_t *)(pSourceEntry->data); + Hash_FltForEachEntry( hOutgoing, pSinkEntry, j ) { + sink = pSinkEntry->key; + delay = pSinkEntry->data; + + max_delay = -ABC_INFINITY; + if (Abc_ObjIsPo( Abc_NtkObj( pNtk, sink ) )) { + nMerges++; + if (delay > max_delay) + max_delay = delay; + Vec_IntPush( vFreeList, sink ); + } + } + if (max_delay != -ABC_INFINITY) + Hash_FltWriteEntry( hOutgoing, -1, delay ); + // do freeing + while( vFreeList->nSize > 0 ) { + Hash_FltRemove( hOutgoing, Vec_IntPop( vFreeList ) ); + } + } + + if (fVerbose) printf("Merged %d paths into one Pio node\n", nMerges); + +} + +/**Function************************************************************* + + Synopsis [This is a modification of routine from abcDfs.c] + + Description [Recursive DFS from a starting point. Keeps the endpoints.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkDfsReverse_rec2( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes, Vec_Ptr_t * vEndpoints ) +{ + Abc_Obj_t * pFanout; + int i; + assert( !Abc_ObjIsNet(pNode) ); + // if this node is already visited, skip + if ( Abc_NodeIsTravIdCurrent( pNode ) ) + return; + // mark the node as visited + Abc_NodeSetTravIdCurrent( pNode ); + // terminate at the Co + if ( Abc_ObjIsCo(pNode) ) { + Vec_PtrPush( vEndpoints, pNode ); + return; + } + assert( Abc_ObjIsNode( pNode ) ); + // visit the transitive fanin of the node + pNode = Abc_ObjFanout0Ntk(pNode); + Abc_ObjForEachFanout( pNode, pFanout, i ) + Abc_NtkDfsReverse_rec2( pFanout, vNodes, vEndpoints ); + // add the node after the fanins have been added + Vec_PtrPush( vNodes, pNode ); +} + +/**Function************************************************************* + + Synopsis [Converts all skews into forward skews 0vSkews->nSize >= Abc_NtkLatchNum( pNtk )-1 ); + + if (fMinimize) { + // search all offsets for the one that minimizes sum of skews + while(currentOffset < period) { + currentSum = 0; + nextStep = period; + Abc_NtkForEachLatch( pNtk, pObj, i ) { + skew = Abc_NtkGetLatSkew( pNtk, i ) + currentOffset; + skew = (float)(skew - period*floor(skew/period)); + currentSum += skew; + if (skew > ZERO_SLOP && skew < nextStep) { + nextStep = skew; + } + } + + if (currentSum < bestSum) { + bestSum = currentSum; + bestOffset = currentOffset; + } + currentOffset += nextStep; + } + printf("Offseting all skews by %.2f\n", bestOffset); + } + + // convert global skew into forward skew + pNtk->globalSkew = pNtk->globalSkew - bestOffset; + pNtk->globalSkew = (float)(pNtk->globalSkew - period*floor(pNtk->globalSkew/period)); + assert(pNtk->globalSkew>= 0 && pNtk->globalSkew < period); + + // convert endpoint skews into forward skews + Abc_NtkForEachLatch( pNtk, pObj, i ) { + skew = Abc_NtkGetLatSkew( pNtk, i ) + bestOffset; + skew = (float)(skew - period*floor(skew/period)); + REMOVE_ZERO_SLOP( skew ); + assert(skew >=0 && skew < period); + + Abc_NtkSetLatSkew( pNtk, i, skew ); + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/base/seq/seqRetCore.c b/abc_with_bb_support/src/base/seq/seqRetCore.c new file mode 100644 index 000000000..70339443b --- /dev/null +++ b/abc_with_bb_support/src/base/seq/seqRetCore.c @@ -0,0 +1,492 @@ +/**CFile**************************************************************** + + FileName [seqRetCore.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Construction and manipulation of sequential AIGs.] + + Synopsis [The core of FPGA mapping/retiming package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: seqRetCore.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "seqInt.h" +#include "dec.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Abc_Ntk_t * Seq_NtkRetimeDerive( Abc_Ntk_t * pNtk, int fVerbose ); +static Abc_Obj_t * Seq_NodeRetimeDerive( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pNode, char * pSop, Vec_Ptr_t * vFanins ); +static Abc_Ntk_t * Seq_NtkRetimeReconstruct( Abc_Ntk_t * pNtkOld, Abc_Ntk_t * pNtkSeq ); +static Abc_Obj_t * Seq_EdgeReconstruct_rec( Abc_Obj_t * pGoal, Abc_Obj_t * pNode ); +static Abc_Obj_t * Seq_EdgeReconstructPO( Abc_Obj_t * pNode ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs FPGA mapping and retiming.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Seq_NtkRetime( Abc_Ntk_t * pNtk, int nMaxIters, int fInitial, int fVerbose ) +{ + Abc_Seq_t * p; + Abc_Ntk_t * pNtkSeq, * pNtkNew; + int RetValue; + assert( !Abc_NtkHasAig(pNtk) ); + // derive the isomorphic seq AIG + pNtkSeq = Seq_NtkRetimeDerive( pNtk, fVerbose ); + p = pNtkSeq->pManFunc; + p->nMaxIters = nMaxIters; + + if ( !fInitial ) + Seq_NtkLatchSetValues( pNtkSeq, ABC_INIT_DC ); + // find the best mapping and retiming + if ( !Seq_NtkRetimeDelayLags( pNtk, pNtkSeq, fVerbose ) ) + return NULL; + + // implement the retiming + RetValue = Seq_NtkImplementRetiming( pNtkSeq, p->vLags, fVerbose ); + if ( RetValue == 0 ) + printf( "Retiming completed but initial state computation has failed.\n" ); +//return pNtkSeq; + + // create the final mapped network + pNtkNew = Seq_NtkRetimeReconstruct( pNtk, pNtkSeq ); + Abc_NtkDelete( pNtkSeq ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Derives the isomorphic seq AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Seq_NtkRetimeDerive( Abc_Ntk_t * pNtk, int fVerbose ) +{ + Abc_Seq_t * p; + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj, * pFanin, * pMirror; + Vec_Ptr_t * vMapAnds, * vMirrors; + Vec_Vec_t * vMapFanins; + int i, k, RetValue, fHasBdds; + char * pSop; + + // make sure it is an AIG without self-feeding latches + assert( !Abc_NtkHasAig(pNtk) ); + if ( RetValue = Abc_NtkRemoveSelfFeedLatches(pNtk) ) + printf( "Modified %d self-feeding latches. The result will not verify.\n", RetValue ); + assert( Abc_NtkCountSelfFeedLatches(pNtk) == 0 ); + + // remove the dangling nodes + Abc_NtkCleanup( pNtk, fVerbose ); + + // transform logic functions from BDD to SOP + if ( fHasBdds = Abc_NtkIsBddLogic(pNtk) ) + { + if ( !Abc_NtkBddToSop(pNtk, 0) ) + { + printf( "Seq_NtkRetimeDerive(): Converting to SOPs has failed.\n" ); + return NULL; + } + } + + // start the network + pNtkNew = Abc_NtkAlloc( ABC_NTK_SEQ, ABC_FUNC_AIG, 1 ); + // duplicate the name and the spec + pNtkNew->pName = Extra_UtilStrsav(pNtk->pName); + pNtkNew->pSpec = Extra_UtilStrsav(pNtk->pSpec); + + // map the constant nodes + Abc_NtkCleanCopy( pNtk ); + // clone the PIs/POs/latches + Abc_NtkForEachPi( pNtk, pObj, i ) + Abc_NtkDupObj( pNtkNew, pObj, 0 ); + Abc_NtkForEachPo( pNtk, pObj, i ) + Abc_NtkDupObj( pNtkNew, pObj, 0 ); + // copy the names + Abc_NtkForEachPi( pNtk, pObj, i ) + Abc_ObjAssignName( pObj->pCopy, Abc_ObjName(pObj), NULL ); + Abc_NtkForEachPo( pNtk, pObj, i ) + Abc_ObjAssignName( pObj->pCopy, Abc_ObjName(pObj), NULL ); + + // create one AND for each logic node in the topological order + vMapAnds = Abc_NtkDfs( pNtk, 0 ); + Vec_PtrForEachEntry( vMapAnds, pObj, i ) + { + if ( pObj->Id == 0 ) + { + pObj->pCopy = Abc_AigConst1(pNtkNew); + continue; + } + pObj->pCopy = Abc_NtkCreateNode( pNtkNew ); + } + + // make the new seq AIG point to the old network through pNext + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( pObj->pCopy ) pObj->pCopy->pNext = pObj; + + // make latches point to the latch fanins + Abc_NtkForEachLatch( pNtk, pObj, i ) + { + assert( !Abc_ObjIsLatch(Abc_ObjFanin0(pObj)) ); + pObj->pCopy = Abc_ObjFanin0(pObj)->pCopy; + } + + // create internal AND nodes w/o strashing for each logic node (including constants) + vMapFanins = Vec_VecStart( Vec_PtrSize(vMapAnds) ); + Vec_PtrForEachEntry( vMapAnds, pObj, i ) + { + // get the SOP of the node + if ( Abc_NtkHasMapping(pNtk) ) + pSop = Mio_GateReadSop(pObj->pData); + else + pSop = pObj->pData; + pFanin = Seq_NodeRetimeDerive( pNtkNew, pObj, pSop, Vec_VecEntry(vMapFanins, i) ); + Abc_ObjAddFanin( pObj->pCopy, pFanin ); + Abc_ObjAddFanin( pObj->pCopy, pFanin ); + } + // connect the POs + Abc_NtkForEachPo( pNtk, pObj, i ) + Abc_ObjAddFanin( pObj->pCopy, Abc_ObjFanin0(pObj)->pCopy ); + + // start the storage for initial states + p = pNtkNew->pManFunc; + Seq_Resize( p, Abc_NtkObjNumMax(pNtkNew) ); + + // add the sequential edges + Vec_PtrForEachEntry( vMapAnds, pObj, i ) + { + vMirrors = Vec_VecEntry( vMapFanins, i ); + Abc_ObjForEachFanin( pObj, pFanin, k ) + { + pMirror = Vec_PtrEntry( vMirrors, k ); + if ( Abc_ObjIsLatch(pFanin) ) + { + Seq_NodeInsertFirst( pMirror, 0, Abc_LatchInit(pFanin) ); + Seq_NodeInsertFirst( pMirror, 1, Abc_LatchInit(pFanin) ); + } + } + } + // add the sequential edges to the POs + Abc_NtkForEachPo( pNtk, pObj, i ) + { + pFanin = Abc_ObjFanin0(pObj); + if ( Abc_ObjIsLatch(pFanin) ) + Seq_NodeInsertFirst( pObj->pCopy, 0, Abc_LatchInit(pFanin) ); + } + + + // save the fanin/delay info + p->vMapAnds = vMapAnds; + p->vMapFanins = vMapFanins; + p->vMapCuts = Vec_VecStart( Vec_PtrSize(p->vMapAnds) ); + p->vMapDelays = Vec_VecStart( Vec_PtrSize(p->vMapAnds) ); + Vec_PtrForEachEntry( p->vMapAnds, pObj, i ) + { + // change the node to be the new one + Vec_PtrWriteEntry( p->vMapAnds, i, pObj->pCopy ); + // collect the new fanins of this node + Abc_ObjForEachFanin( pObj, pFanin, k ) + Vec_VecPush( p->vMapCuts, i, (void *)( (pFanin->pCopy->Id << 8) | Abc_ObjIsLatch(pFanin) ) ); + // collect the delay info + if ( !Abc_NtkHasMapping(pNtk) ) + { + Abc_ObjForEachFanin( pObj, pFanin, k ) + Vec_VecPush( p->vMapDelays, i, (void *)Abc_Float2Int(1.0) ); + } + else + { + Mio_Pin_t * pPin = Mio_GateReadPins(pObj->pData); + float Max, tDelayBlockRise, tDelayBlockFall; + Abc_ObjForEachFanin( pObj, pFanin, k ) + { + tDelayBlockRise = (float)Mio_PinReadDelayBlockRise( pPin ); + tDelayBlockFall = (float)Mio_PinReadDelayBlockFall( pPin ); + Max = ABC_MAX( tDelayBlockRise, tDelayBlockFall ); + Vec_VecPush( p->vMapDelays, i, (void *)Abc_Float2Int(Max) ); + pPin = Mio_PinReadNext(pPin); + } + } + } + + // set the cutset composed of latch drivers +// Abc_NtkAigCutsetCopy( pNtk ); +// Seq_NtkLatchGetEqualFaninNum( pNtkNew ); + + // convert the network back into BDDs if this is how it was + if ( fHasBdds ) + Abc_NtkSopToBdd(pNtk); + + // copy EXDC and check correctness + if ( pNtk->pExdc ) + fprintf( stdout, "Warning: EXDC is not copied when converting to sequential AIG.\n" ); + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Seq_NtkRetimeDerive(): Network check has failed.\n" ); + return pNtkNew; +} + + +/**Function************************************************************* + + Synopsis [Strashes one logic node using its SOP.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Seq_NodeRetimeDerive( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pRoot, char * pSop, Vec_Ptr_t * vFanins ) +{ + Dec_Graph_t * pFForm; + Dec_Node_t * pNode; + Abc_Obj_t * pResult, * pFanin, * pMirror; + int i, nFanins; + + // get the number of node's fanins + nFanins = Abc_ObjFaninNum( pRoot ); + assert( nFanins == Abc_SopGetVarNum(pSop) ); + if ( nFanins < 2 ) + { + if ( Abc_SopIsConst1(pSop) ) + pFanin = Abc_AigConst1(pNtkNew); + else if ( Abc_SopIsConst0(pSop) ) + pFanin = Abc_ObjNot( Abc_AigConst1(pNtkNew) ); + else if ( Abc_SopIsBuf(pSop) ) + pFanin = Abc_ObjFanin0(pRoot)->pCopy; + else if ( Abc_SopIsInv(pSop) ) + pFanin = Abc_ObjNot( Abc_ObjFanin0(pRoot)->pCopy ); + else + assert( 0 ); + // create the node with these fanins + pMirror = Abc_NtkCreateNode( pNtkNew ); + Abc_ObjAddFanin( pMirror, pFanin ); + Abc_ObjAddFanin( pMirror, pFanin ); + Vec_PtrPush( vFanins, pMirror ); + return pMirror; + } + + // perform factoring + pFForm = Dec_Factor( pSop ); + // collect the fanins + Dec_GraphForEachLeaf( pFForm, pNode, i ) + { + pFanin = Abc_ObjFanin(pRoot,i)->pCopy; + pMirror = Abc_NtkCreateNode( pNtkNew ); + Abc_ObjAddFanin( pMirror, pFanin ); + Abc_ObjAddFanin( pMirror, pFanin ); + Vec_PtrPush( vFanins, pMirror ); + pNode->pFunc = pMirror; + } + // perform strashing + pResult = Dec_GraphToNetworkNoStrash( pNtkNew, pFForm ); + Dec_GraphFree( pFForm ); + return pResult; +} + + +/**Function************************************************************* + + Synopsis [Reconstructs the network after retiming.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Seq_NtkRetimeReconstruct( Abc_Ntk_t * pNtkOld, Abc_Ntk_t * pNtkSeq ) +{ + Abc_Seq_t * p = pNtkSeq->pManFunc; + Seq_Lat_t * pRing0, * pRing1; + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj, * pFanin, * pFaninNew, * pMirror; + Vec_Ptr_t * vMirrors; + int i, k; + + assert( !Abc_NtkIsSeq(pNtkOld) ); + assert( Abc_NtkIsSeq(pNtkSeq) ); + + // transfer the pointers pNtkOld->pNtkSeq from pCopy to pNext + Abc_NtkForEachObj( pNtkOld, pObj, i ) + pObj->pNext = pObj->pCopy; + + // start the final network + pNtkNew = Abc_NtkStartFrom( pNtkSeq, pNtkOld->ntkType, pNtkOld->ntkFunc ); + + // transfer the pointers to the old network + if ( Abc_AigConst1(pNtkOld) ) + Abc_AigConst1(pNtkOld)->pCopy = Abc_AigConst1(pNtkNew); + Abc_NtkForEachPi( pNtkOld, pObj, i ) + pObj->pCopy = pObj->pNext->pCopy; + Abc_NtkForEachPo( pNtkOld, pObj, i ) + pObj->pCopy = pObj->pNext->pCopy; + + // copy the internal nodes of the old network into the new network + // transfer the pointers pNktOld->pNtkNew to pNtkSeq->pNtkNew + Abc_NtkForEachNode( pNtkOld, pObj, i ) + { + if ( i == 0 ) continue; + Abc_NtkDupObj( pNtkNew, pObj, 0 ); + pObj->pNext->pCopy = pObj->pCopy; + } + Abc_NtkForEachLatch( pNtkOld, pObj, i ) + pObj->pCopy = Abc_ObjFanin0(pObj)->pCopy; + + // share the latches + Seq_NtkShareLatches( pNtkNew, pNtkSeq ); + + // connect the objects +// Abc_NtkForEachNode( pNtkOld, pObj, i ) + Vec_PtrForEachEntry( p->vMapAnds, pObj, i ) + { + // pObj is from pNtkSeq - transform to pNtkOld + pObj = pObj->pNext; + // iterate through the fanins of this node in the old network + vMirrors = Vec_VecEntry( p->vMapFanins, i ); + Abc_ObjForEachFanin( pObj, pFanin, k ) + { + pMirror = Vec_PtrEntry( vMirrors, k ); + assert( Seq_ObjFaninL0(pMirror) == Seq_ObjFaninL1(pMirror) ); + pRing0 = Seq_NodeGetRing( pMirror, 0 ); + pRing1 = Seq_NodeGetRing( pMirror, 1 ); + if ( pRing0 == NULL ) + { + Abc_ObjAddFanin( pObj->pCopy, pFanin->pCopy ); + continue; + } +// assert( pRing0->pLatch == pRing1->pLatch ); + if ( pRing0->pLatch->pData > pRing1->pLatch->pData ) + Abc_ObjAddFanin( pObj->pCopy, pRing0->pLatch ); + else + Abc_ObjAddFanin( pObj->pCopy, pRing1->pLatch ); + } + } + + // connect the POs + Abc_NtkForEachPo( pNtkOld, pObj, i ) + { + pFanin = Abc_ObjFanin0(pObj); + pRing0 = Seq_NodeGetRing( Abc_NtkPo(pNtkSeq, i), 0 ); + if ( pRing0 ) + pFaninNew = pRing0->pLatch; + else + pFaninNew = pFanin->pCopy; + assert( pFaninNew != NULL ); + Abc_ObjAddFanin( pObj->pCopy, pFaninNew ); + } + + // clean the result of latch sharing + Seq_NtkShareLatchesClean( pNtkSeq ); + + // add the latches and their names + Abc_NtkAddDummyBoxNames( pNtkNew ); + Abc_NtkOrderCisCos( pNtkNew ); + // fix the problem with complemented and duplicated CO edges + Abc_NtkLogicMakeSimpleCos( pNtkNew, 1 ); + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Seq_NtkRetimeReconstruct(): Network check has failed.\n" ); + return pNtkNew; + +} + +/**Function************************************************************* + + Synopsis [Reconstructs the network after retiming.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Seq_EdgeReconstruct_rec( Abc_Obj_t * pGoal, Abc_Obj_t * pNode ) +{ + Seq_Lat_t * pRing; + Abc_Obj_t * pFanin, * pRes = NULL; + + if ( !Abc_AigNodeIsAnd(pNode) ) + return NULL; + + // consider the first fanin + pFanin = Abc_ObjFanin0(pNode); + if ( pFanin->pCopy == NULL ) // internal node + pRes = Seq_EdgeReconstruct_rec( pGoal, pFanin ); + else if ( pFanin == pGoal ) + { + if ( pRing = Seq_NodeGetRing( pNode, 0 ) ) + pRes = pRing->pLatch; + else + pRes = pFanin->pCopy; + } + if ( pRes != NULL ) + return pRes; + + // consider the second fanin + pFanin = Abc_ObjFanin1(pNode); + if ( pFanin->pCopy == NULL ) // internal node + pRes = Seq_EdgeReconstruct_rec( pGoal, pFanin ); + else if ( pFanin == pGoal ) + { + if ( pRing = Seq_NodeGetRing( pNode, 1 ) ) + pRes = pRing->pLatch; + else + pRes = pFanin->pCopy; + } + return pRes; +} + +/**Function************************************************************* + + Synopsis [Reconstructs the network after retiming.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Seq_EdgeReconstructPO( Abc_Obj_t * pNode ) +{ + Seq_Lat_t * pRing; + assert( Abc_ObjIsPo(pNode) ); + if ( pRing = Seq_NodeGetRing( pNode, 0 ) ) + return pRing->pLatch; + else + return Abc_ObjFanin0(pNode)->pCopy; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/seq/seqRetIter.c b/abc_with_bb_support/src/base/seq/seqRetIter.c new file mode 100644 index 000000000..c0377e93c --- /dev/null +++ b/abc_with_bb_support/src/base/seq/seqRetIter.c @@ -0,0 +1,403 @@ +/**CFile**************************************************************** + + FileName [seqRetIter.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Construction and manipulation of sequential AIGs.] + + Synopsis [Iterative delay computation in FPGA mapping/retiming package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: seqRetIter.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "seqInt.h" +#include "main.h" +#include "fpga.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static float Seq_NtkMappingSearch_rec( Abc_Ntk_t * pNtk, float FiMin, float FiMax, float Delta, int fVerbose ); +static int Seq_NtkMappingForPeriod( Abc_Ntk_t * pNtk, float Fi, int fVerbose ); +static int Seq_NtkNodeUpdateLValue( Abc_Obj_t * pObj, float Fi, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vDelays ); +static void Seq_NodeRetimeSetLag_rec( Abc_Obj_t * pNode, char Lag ); + +static void Seq_NodePrintInfo( Abc_Obj_t * pNode ); +static void Seq_NodePrintInfoPlus( Abc_Obj_t * pNode ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes the retiming lags for arbitrary network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_NtkRetimeDelayLags( Abc_Ntk_t * pNtkOld, Abc_Ntk_t * pNtk, int fVerbose ) +{ + Abc_Seq_t * p = pNtk->pManFunc; + Abc_Obj_t * pNode; + float FiMax, Delta; + int i, RetValue; + char NodeLag; + + assert( Abc_NtkIsSeq( pNtk ) ); + + // the root AND gates and node delay should be assigned + assert( p->vMapAnds ); + assert( p->vMapCuts ); + assert( p->vMapDelays ); + assert( p->vMapFanins ); + + // guess the upper bound on the clock period + if ( Abc_NtkHasMapping(pNtkOld) ) + { + // assign the accuracy for min-period computation + Delta = Mio_LibraryReadDelayNand2Max(Abc_FrameReadLibGen()); + if ( Delta == 0.0 ) + { + Delta = Mio_LibraryReadDelayAnd2Max(Abc_FrameReadLibGen()); + if ( Delta == 0.0 ) + { + printf( "Cannot retime/map if the library does not have NAND2 or AND2.\n" ); + return 0; + } + } + // get the upper bound on the clock period + FiMax = Delta * 2 + Abc_NtkDelayTrace(pNtkOld); + Delta /= 2; + } + else + { + FiMax = (float)2.0 + Abc_NtkGetLevelNum(pNtkOld); + Delta = 1; + } + + // make sure this clock period is feasible + if ( !Seq_NtkMappingForPeriod( pNtk, FiMax, fVerbose ) ) + { + printf( "Error: The upper bound on the clock period cannot be computed.\n" ); + printf( "The reason for this error may be the presence in the circuit of logic\n" ); + printf( "that is not reachable from the PIs. Mapping/retiming is not performed.\n" ); + return 0; + } + + // search for the optimal clock period between 0 and nLevelMax + p->FiBestFloat = Seq_NtkMappingSearch_rec( pNtk, 0.0, FiMax, Delta, fVerbose ); + + // recompute the best l-values + RetValue = Seq_NtkMappingForPeriod( pNtk, p->FiBestFloat, fVerbose ); + assert( RetValue ); + + // fix the problem with non-converged delays + Vec_PtrForEachEntry( p->vMapAnds, pNode, i ) + if ( Seq_NodeGetLValueP(pNode) < -ABC_INFINITY/2 ) + Seq_NodeSetLValueP( pNode, 0 ); + + // experiment by adding an epsilon to all LValues +// Vec_PtrForEachEntry( p->vMapAnds, pNode, i ) +// Seq_NodeSetLValueP( pNode, Seq_NodeGetLValueP(pNode) - p->fEpsilon ); + + // save the retiming lags + // mark the nodes + Vec_PtrForEachEntry( p->vMapAnds, pNode, i ) + pNode->fMarkA = 1; + // process the nodes + Vec_StrFill( p->vLags, p->nSize, 0 ); + Vec_PtrForEachEntry( p->vMapAnds, pNode, i ) + { + if ( Vec_PtrSize( Vec_VecEntry(p->vMapCuts, i) ) == 0 ) + { + Seq_NodeSetLag( pNode, 0 ); + continue; + } + NodeLag = Seq_NodeComputeLagFloat( Seq_NodeGetLValueP(pNode), p->FiBestFloat ); + Seq_NodeRetimeSetLag_rec( pNode, NodeLag ); + } + // unmark the nodes + Vec_PtrForEachEntry( p->vMapAnds, pNode, i ) + pNode->fMarkA = 0; + + // print the result + if ( fVerbose ) + printf( "The best clock period is %6.2f.\n", p->FiBestFloat ); +/* + { + FILE * pTable; + pTable = fopen( "stats.txt", "a+" ); + fprintf( pTable, "%s ", pNtk->pName ); + fprintf( pTable, "%.2f ", FiBest ); + fprintf( pTable, "\n" ); + fclose( pTable ); + } +*/ +// Seq_NodePrintInfo( Abc_NtkObj(pNtk, 847) ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Performs binary search for the optimal clock period.] + + Description [Assumes that FiMin is infeasible while FiMax is feasible.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Seq_NtkMappingSearch_rec( Abc_Ntk_t * pNtk, float FiMin, float FiMax, float Delta, int fVerbose ) +{ + float Median; + assert( FiMin < FiMax ); + if ( FiMin + Delta >= FiMax ) + return FiMax; + Median = FiMin + (FiMax - FiMin)/2; + if ( Seq_NtkMappingForPeriod( pNtk, Median, fVerbose ) ) + return Seq_NtkMappingSearch_rec( pNtk, FiMin, Median, Delta, fVerbose ); // Median is feasible + else + return Seq_NtkMappingSearch_rec( pNtk, Median, FiMax, Delta, fVerbose ); // Median is infeasible +} + +/**Function************************************************************* + + Synopsis [Returns 1 if retiming with this clock period is feasible.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_NtkMappingForPeriod( Abc_Ntk_t * pNtk, float Fi, int fVerbose ) +{ + Abc_Seq_t * p = pNtk->pManFunc; + Vec_Ptr_t * vLeaves, * vDelays; + Abc_Obj_t * pObj; + int i, c, RetValue, fChange, Counter; + char * pReason = ""; + + // set l-values of all nodes to be minus infinity + Vec_IntFill( p->vLValues, p->nSize, Abc_Float2Int( (float)-ABC_INFINITY ) ); + + // set l-values of constants and PIs + pObj = Abc_NtkObj( pNtk, 0 ); + Seq_NodeSetLValueP( pObj, 0.0 ); + Abc_NtkForEachPi( pNtk, pObj, i ) + Seq_NodeSetLValueP( pObj, 0.0 ); + + // update all values iteratively + Counter = 0; + for ( c = 0; c < p->nMaxIters; c++ ) + { + fChange = 0; + Vec_PtrForEachEntry( p->vMapAnds, pObj, i ) + { + Counter++; + vLeaves = Vec_VecEntry( p->vMapCuts, i ); + vDelays = Vec_VecEntry( p->vMapDelays, i ); + if ( Vec_PtrSize(vLeaves) == 0 ) + { + Seq_NodeSetLValueP( pObj, 0.0 ); + continue; + } + RetValue = Seq_NtkNodeUpdateLValue( pObj, Fi, vLeaves, vDelays ); + if ( RetValue == SEQ_UPDATE_YES ) + fChange = 1; + } + Abc_NtkForEachPo( pNtk, pObj, i ) + { + RetValue = Seq_NtkNodeUpdateLValue( pObj, Fi, NULL, NULL ); + if ( RetValue == SEQ_UPDATE_FAIL ) + break; + } + if ( RetValue == SEQ_UPDATE_FAIL ) + break; + if ( fChange == 0 ) + break; + } + if ( c == p->nMaxIters ) + { + RetValue = SEQ_UPDATE_FAIL; + pReason = "(timeout)"; + } + else + c++; + + // report the results + if ( fVerbose ) + { + if ( RetValue == SEQ_UPDATE_FAIL ) + printf( "Period = %6.2f. Iterations = %3d. Updates = %10d. Infeasible %s\n", Fi, c, Counter, pReason ); + else + printf( "Period = %6.2f. Iterations = %3d. Updates = %10d. Feasible\n", Fi, c, Counter ); + } + return RetValue != SEQ_UPDATE_FAIL; +} + +/**Function************************************************************* + + Synopsis [Computes the l-value of the node.] + + Description [The node can be internal or a PO.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_NtkNodeUpdateLValue( Abc_Obj_t * pObj, float Fi, Vec_Ptr_t * vLeaves, Vec_Ptr_t * vDelays ) +{ + Abc_Seq_t * p = pObj->pNtk->pManFunc; + float lValueOld, lValueNew, lValueCur, lValuePin; + unsigned SeqEdge; + Abc_Obj_t * pLeaf; + int i; + + assert( !Abc_ObjIsPi(pObj) ); + assert( Abc_ObjFaninNum(pObj) > 0 ); + // consider the case of the PO + if ( Abc_ObjIsPo(pObj) ) + { + lValueCur = Seq_NodeGetLValueP(Abc_ObjFanin0(pObj)) - Fi * Seq_ObjFaninL0(pObj); + return (lValueCur > Fi + p->fEpsilon)? SEQ_UPDATE_FAIL : SEQ_UPDATE_NO; + } + // get the new arrival time of the cut output + lValueNew = -ABC_INFINITY; + Vec_PtrForEachEntry( vLeaves, pLeaf, i ) + { + SeqEdge = (unsigned)pLeaf; + pLeaf = Abc_NtkObj( pObj->pNtk, SeqEdge >> 8 ); + lValueCur = Seq_NodeGetLValueP(pLeaf) - Fi * (SeqEdge & 255); + lValuePin = Abc_Int2Float( (int)Vec_PtrEntry(vDelays, i) ); + if ( lValueNew < lValuePin + lValueCur ) + lValueNew = lValuePin + lValueCur; + } + // compare + lValueOld = Seq_NodeGetLValueP( pObj ); + if ( lValueNew <= lValueOld + p->fEpsilon ) + return SEQ_UPDATE_NO; + // update the values + if ( lValueNew > lValueOld + p->fEpsilon ) + Seq_NodeSetLValueP( pObj, lValueNew ); + return SEQ_UPDATE_YES; +} + + + +/**Function************************************************************* + + Synopsis [Add sequential edges.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NodeRetimeSetLag_rec( Abc_Obj_t * pNode, char Lag ) +{ + Abc_Obj_t * pFanin; + if ( !Abc_AigNodeIsAnd(pNode) ) + return; + Seq_NodeSetLag( pNode, Lag ); + // consider the first fanin + pFanin = Abc_ObjFanin0(pNode); + if ( pFanin->fMarkA == 0 ) // internal node + Seq_NodeRetimeSetLag_rec( pFanin, Lag ); + // consider the second fanin + pFanin = Abc_ObjFanin1(pNode); + if ( pFanin->fMarkA == 0 ) // internal node + Seq_NodeRetimeSetLag_rec( pFanin, Lag ); +} + + +/**Function************************************************************* + + Synopsis [Add sequential edges.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NodePrintInfo( Abc_Obj_t * pNode ) +{ + Abc_Seq_t * p = pNode->pNtk->pManFunc; + Abc_Obj_t * pFanin, * pObj, * pLeaf; + Vec_Ptr_t * vLeaves; + unsigned SeqEdge; + int i, Number; + + // print the node + printf( " Node = %6d. LValue = %7.2f. Lag = %2d.\n", + pNode->Id, Seq_NodeGetLValueP(pNode), Seq_NodeGetLag(pNode) ); + + // find the number + Vec_PtrForEachEntry( p->vMapAnds, pObj, Number ) + if ( pObj == pNode ) + break; + + // get the leaves + vLeaves = Vec_VecEntry( p->vMapCuts, Number ); + + // print the leaves + Vec_PtrForEachEntry( vLeaves, pLeaf, i ) + { + SeqEdge = (unsigned)pLeaf; + pFanin = Abc_NtkObj( pNode->pNtk, SeqEdge >> 8 ); + // print the leaf + printf( " Fanin%d(%d) = %6d. LValue = %7.2f. Lag = %2d.\n", i, SeqEdge & 255, + pFanin->Id, Seq_NodeGetLValueP(pFanin), Seq_NodeGetLag(pFanin) ); + } +} + +/**Function************************************************************* + + Synopsis [Add sequential edges.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NodePrintInfoPlus( Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pFanout; + int i; + printf( "CENTRAL NODE:\n" ); + Seq_NodePrintInfo( pNode ); + Abc_ObjForEachFanout( pNode, pFanout, i ) + { + printf( "FANOUT%d:\n", i ); + Seq_NodePrintInfo( pFanout ); + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/seq/seqShare.c b/abc_with_bb_support/src/base/seq/seqShare.c new file mode 100644 index 000000000..e7c712501 --- /dev/null +++ b/abc_with_bb_support/src/base/seq/seqShare.c @@ -0,0 +1,388 @@ +/**CFile**************************************************************** + + FileName [seqShare.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Construction and manipulation of sequential AIGs.] + + Synopsis [Latch sharing at the fanout stems.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: seqShare.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "seqInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Seq_NodeShareFanouts( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes ); +static void Seq_NodeShareOne( Abc_Obj_t * pNode, Abc_InitType_t Init, Vec_Ptr_t * vNodes ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Transforms the sequential AIG to take fanout sharing into account.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NtkShareFanouts( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj; + int i; + vNodes = Vec_PtrAlloc( 10 ); + // share the PI latches + Abc_NtkForEachPi( pNtk, pObj, i ) + Seq_NodeShareFanouts( pObj, vNodes ); + // share the node latches + Abc_NtkForEachNode( pNtk, pObj, i ) + Seq_NodeShareFanouts( pObj, vNodes ); + Vec_PtrFree( vNodes ); +} + +/**Function************************************************************* + + Synopsis [Transforms the node to take fanout sharing into account.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NodeShareFanouts( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes ) +{ + Abc_Obj_t * pFanout; + Abc_InitType_t Type; + int nLatches[4], i; + // skip the node with only one fanout + if ( Abc_ObjFanoutNum(pNode) < 2 ) + return; + // clean the the fanout counters + for ( i = 0; i < 4; i++ ) + nLatches[i] = 0; + // find the number of fanouts having latches of each type + Abc_ObjForEachFanout( pNode, pFanout, i ) + { + if ( Seq_ObjFanoutL(pNode, pFanout) == 0 ) + continue; + Type = Seq_NodeGetInitLast( pFanout, Abc_ObjFanoutEdgeNum(pNode, pFanout) ); + nLatches[Type]++; + } + // decide what to do + if ( nLatches[ABC_INIT_ZERO] > 1 && nLatches[ABC_INIT_ONE] > 1 ) // 0-group and 1-group + { + Seq_NodeShareOne( pNode, ABC_INIT_ZERO, vNodes ); // shares 0 and DC + Seq_NodeShareOne( pNode, ABC_INIT_ONE, vNodes ); // shares 1 and DC + } + else if ( nLatches[ABC_INIT_ZERO] > 1 ) // 0-group + Seq_NodeShareOne( pNode, ABC_INIT_ZERO, vNodes ); // shares 0 and DC + else if ( nLatches[ABC_INIT_ONE] > 1 ) // 1-group + Seq_NodeShareOne( pNode, ABC_INIT_ONE, vNodes ); // shares 1 and DC + else if ( nLatches[ABC_INIT_DC] > 1 ) // DC-group + { + if ( nLatches[ABC_INIT_ZERO] > 0 ) + Seq_NodeShareOne( pNode, ABC_INIT_ZERO, vNodes ); // shares 0 and DC + else + Seq_NodeShareOne( pNode, ABC_INIT_ONE, vNodes ); // shares 1 and DC + } +} + +/**Function************************************************************* + + Synopsis [Transforms the node to take fanout sharing into account.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NodeShareOne( Abc_Obj_t * pNode, Abc_InitType_t Init, Vec_Ptr_t * vNodes ) +{ + Vec_Int_t * vNums = Seq_ObjLNums( pNode ); + Vec_Ptr_t * vInits = Seq_NodeLats( pNode ); + Abc_Obj_t * pFanout, * pBuffer; + Abc_InitType_t Type, InitNew; + int i; + // collect the fanouts that satisfy the property (have initial value Init or DC) + InitNew = ABC_INIT_DC; + Vec_PtrClear( vNodes ); + Abc_ObjForEachFanout( pNode, pFanout, i ) + { + if ( Seq_ObjFanoutL(pNode, pFanout) == 0 ) + continue; + Type = Seq_NodeGetInitLast( pFanout, Abc_ObjFanoutEdgeNum(pNode, pFanout) ); + if ( Type == Init ) + InitNew = Init; + if ( Type == Init || Type == ABC_INIT_DC ) + { + Vec_PtrPush( vNodes, pFanout ); + Seq_NodeDeleteLast( pFanout, Abc_ObjFanoutEdgeNum(pNode, pFanout) ); + } + } + // create the new buffer + pBuffer = Abc_NtkCreateNode( pNode->pNtk ); + Abc_ObjAddFanin( pBuffer, pNode ); + + // grow storage for initial states + Vec_PtrGrow( vInits, 2 * pBuffer->Id + 2 ); + for ( i = Vec_PtrSize(vInits); i < 2 * (int)pBuffer->Id + 2; i++ ) + Vec_PtrPush( vInits, NULL ); + // grow storage for numbers of latches + Vec_IntGrow( vNums, 2 * pBuffer->Id + 2 ); + for ( i = Vec_IntSize(vNums); i < 2 * (int)pBuffer->Id + 2; i++ ) + Vec_IntPush( vNums, 0 ); + // insert the new latch + Seq_NodeInsertFirst( pBuffer, 0, InitNew ); + + // redirect the fanouts + Vec_PtrForEachEntry( vNodes, pFanout, i ) + Abc_ObjPatchFanin( pFanout, pNode, pBuffer ); +} + + + + + +/**Function************************************************************* + + Synopsis [Maps virtual latches into real latches.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Seq_NtkShareLatchesKey( Abc_Obj_t * pObj, Abc_InitType_t Init ) +{ + return (pObj->Id << 2) | Init; +} + +/**Function************************************************************* + + Synopsis [Maps virtual latches into real latches.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Seq_NtkShareLatches_rec( Abc_Ntk_t * pNtk, Abc_Obj_t * pObj, Seq_Lat_t * pRing, int nLatch, stmm_table * tLatchMap ) +{ + Abc_Obj_t * pLatch, * pFanin; + Abc_InitType_t Init; + unsigned Key; + if ( nLatch == 0 ) + return pObj; + assert( pRing->pLatch == NULL ); + // get the latch on the previous level + pFanin = Seq_NtkShareLatches_rec( pNtk, pObj, Seq_LatNext(pRing), nLatch - 1, tLatchMap ); + + // get the initial state + Init = Seq_LatInit( pRing ); + // check if the latch with this initial state exists + Key = Seq_NtkShareLatchesKey( pFanin, Init ); + if ( stmm_lookup( tLatchMap, (char *)Key, (char **)&pLatch ) ) + return pRing->pLatch = pLatch; + + // does not exist + if ( Init != ABC_INIT_DC ) + { + // check if the don't-care exists + Key = Seq_NtkShareLatchesKey( pFanin, ABC_INIT_DC ); + if ( stmm_lookup( tLatchMap, (char *)Key, (char **)&pLatch ) ) // yes + { + // update the table + stmm_delete( tLatchMap, (char **)&Key, (char **)&pLatch ); + Key = Seq_NtkShareLatchesKey( pFanin, Init ); + stmm_insert( tLatchMap, (char *)Key, (char *)pLatch ); + // change don't-care to the given value + pLatch->pData = (void *)Init; + return pRing->pLatch = pLatch; + } + + // add the latch with this value + pLatch = Abc_NtkCreateLatch( pNtk ); + pLatch->pData = (void *)Init; + Abc_ObjAddFanin( pLatch, pFanin ); + // add it to the table + Key = Seq_NtkShareLatchesKey( pFanin, Init ); + stmm_insert( tLatchMap, (char *)Key, (char *)pLatch ); + return pRing->pLatch = pLatch; + } + // the init value is the don't-care + + // check if care values exist + Key = Seq_NtkShareLatchesKey( pFanin, ABC_INIT_ZERO ); + if ( stmm_lookup( tLatchMap, (char *)Key, (char **)&pLatch ) ) + { + Seq_LatSetInit( pRing, ABC_INIT_ZERO ); + return pRing->pLatch = pLatch; + } + Key = Seq_NtkShareLatchesKey( pFanin, ABC_INIT_ONE ); + if ( stmm_lookup( tLatchMap, (char *)Key, (char **)&pLatch ) ) + { + Seq_LatSetInit( pRing, ABC_INIT_ONE ); + return pRing->pLatch = pLatch; + } + + // create the don't-care latch + pLatch = Abc_NtkCreateLatch( pNtk ); + pLatch->pData = (void *)ABC_INIT_DC; + Abc_ObjAddFanin( pLatch, pFanin ); + // add it to the table + Key = Seq_NtkShareLatchesKey( pFanin, ABC_INIT_DC ); + stmm_insert( tLatchMap, (char *)Key, (char *)pLatch ); + return pRing->pLatch = pLatch; +} + +/**Function************************************************************* + + Synopsis [Maps virtual latches into real latches.] + + Description [Creates new latches and assigns them to virtual latches + on the edges of a sequential AIG. The nodes of the new network should + be created before this procedure is called.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NtkShareLatches( Abc_Ntk_t * pNtkNew, Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj, * pFanin; + stmm_table * tLatchMap; + int i; + assert( Abc_NtkIsSeq( pNtk ) ); + tLatchMap = stmm_init_table( stmm_ptrcmp, stmm_ptrhash ); + Abc_AigForEachAnd( pNtk, pObj, i ) + { + pFanin = Abc_ObjFanin0(pObj); + Seq_NtkShareLatches_rec( pNtkNew, pFanin->pCopy, Seq_NodeGetRing(pObj,0), Seq_NodeCountLats(pObj,0), tLatchMap ); + pFanin = Abc_ObjFanin1(pObj); + Seq_NtkShareLatches_rec( pNtkNew, pFanin->pCopy, Seq_NodeGetRing(pObj,1), Seq_NodeCountLats(pObj,1), tLatchMap ); + } + Abc_NtkForEachPo( pNtk, pObj, i ) + Seq_NtkShareLatches_rec( pNtkNew, Abc_ObjFanin0(pObj)->pCopy, Seq_NodeGetRing(pObj,0), Seq_NodeCountLats(pObj,0), tLatchMap ); + stmm_free_table( tLatchMap ); +} + +/**Function************************************************************* + + Synopsis [Maps virtual latches into real latches.] + + Description [Creates new latches and assigns them to virtual latches + on the edges of a sequential AIG. The nodes of the new network should + be created before this procedure is called.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NtkShareLatchesMapping( Abc_Ntk_t * pNtkNew, Abc_Ntk_t * pNtk, Vec_Ptr_t * vMapAnds, int fFpga ) +{ + Seq_Match_t * pMatch; + Abc_Obj_t * pObj, * pFanout; + stmm_table * tLatchMap; + Vec_Ptr_t * vNodes; + int i, k; + assert( Abc_NtkIsSeq( pNtk ) ); + + // start the table + tLatchMap = stmm_init_table( stmm_ptrcmp, stmm_ptrhash ); + + // create the array of all nodes with sharable fanouts + vNodes = Vec_PtrAlloc( 100 ); + Vec_PtrPush( vNodes, Abc_AigConst1(pNtk) ); + Abc_NtkForEachPi( pNtk, pObj, i ) + Vec_PtrPush( vNodes, pObj ); + if ( fFpga ) + { + Vec_PtrForEachEntry( vMapAnds, pObj, i ) + Vec_PtrPush( vNodes, pObj ); + } + else + { + Vec_PtrForEachEntry( vMapAnds, pMatch, i ) + Vec_PtrPush( vNodes, pMatch->pAnd ); + } + + // process nodes used in the mapping + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + // make sure the label is clean + Abc_ObjForEachFanout( pObj, pFanout, k ) + assert( pFanout->fMarkC == 0 ); + Abc_ObjForEachFanout( pObj, pFanout, k ) + { + if ( pFanout->fMarkC ) + continue; + pFanout->fMarkC = 1; + if ( Abc_ObjFaninId0(pFanout) == pObj->Id ) + Seq_NtkShareLatches_rec( pNtkNew, pObj->pCopy, Seq_NodeGetRing(pFanout,0), Seq_NodeCountLats(pFanout,0), tLatchMap ); + if ( Abc_ObjFaninId1(pFanout) == pObj->Id ) + Seq_NtkShareLatches_rec( pNtkNew, pObj->pCopy, Seq_NodeGetRing(pFanout,1), Seq_NodeCountLats(pFanout,1), tLatchMap ); + } + // clean the label + Abc_ObjForEachFanout( pObj, pFanout, k ) + pFanout->fMarkC = 0; + } + stmm_free_table( tLatchMap ); + // return to the old array + Vec_PtrFree( vNodes ); +} + +/**Function************************************************************* + + Synopsis [Clean the latches after sharing them.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NtkShareLatchesClean( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i; + assert( Abc_NtkIsSeq( pNtk ) ); + Abc_AigForEachAnd( pNtk, pObj, i ) + { + Seq_NodeCleanLats( pObj, 0 ); + Seq_NodeCleanLats( pObj, 1 ); + } + Abc_NtkForEachPo( pNtk, pObj, i ) + Seq_NodeCleanLats( pObj, 0 ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/base/seq/seqUtil.c b/abc_with_bb_support/src/base/seq/seqUtil.c new file mode 100644 index 000000000..0a55db607 --- /dev/null +++ b/abc_with_bb_support/src/base/seq/seqUtil.c @@ -0,0 +1,597 @@ +/**CFile**************************************************************** + + FileName [seqUtil.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Construction and manipulation of sequential AIGs.] + + Synopsis [Various utilities working with sequential AIGs.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: seqUtil.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "seqInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Returns the maximum latch number on any of the fanouts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_NtkLevelMax( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pNode; + int i, Result; + assert( Abc_NtkIsSeq(pNtk) ); + Result = 0; + Abc_NtkForEachPo( pNtk, pNode, i ) + { + pNode = Abc_ObjFanin0(pNode); + if ( Result < (int)pNode->Level ) + Result = pNode->Level; + } + Abc_SeqForEachCutsetNode( pNtk, pNode, i ) + { + if ( Result < (int)pNode->Level ) + Result = pNode->Level; + } + return Result; +} + +/**Function************************************************************* + + Synopsis [Returns the maximum latch number on any of the fanouts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_ObjFanoutLMax( Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanout; + int i, nLatchCur, nLatchRes; + if ( Abc_ObjFanoutNum(pObj) == 0 ) + return 0; + nLatchRes = 0; + Abc_ObjForEachFanout( pObj, pFanout, i ) + { + nLatchCur = Seq_ObjFanoutL(pObj, pFanout); + if ( nLatchRes < nLatchCur ) + nLatchRes = nLatchCur; + } + assert( nLatchRes >= 0 ); + return nLatchRes; +} + +/**Function************************************************************* + + Synopsis [Returns the minimum latch number on any of the fanouts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_ObjFanoutLMin( Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanout; + int i, nLatchCur, nLatchRes; + if ( Abc_ObjFanoutNum(pObj) == 0 ) + return 0; + nLatchRes = ABC_INFINITY; + Abc_ObjForEachFanout( pObj, pFanout, i ) + { + nLatchCur = Seq_ObjFanoutL(pObj, pFanout); + if ( nLatchRes > nLatchCur ) + nLatchRes = nLatchCur; + } + assert( nLatchRes < ABC_INFINITY ); + return nLatchRes; +} + +/**Function************************************************************* + + Synopsis [Returns the sum of latches on the fanout edges.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_ObjFanoutLSum( Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanout; + int i, nSum = 0; + Abc_ObjForEachFanout( pObj, pFanout, i ) + nSum += Seq_ObjFanoutL(pObj, pFanout); + return nSum; +} + +/**Function************************************************************* + + Synopsis [Returns the sum of latches on the fanin edges.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_ObjFaninLSum( Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanin; + int i, nSum = 0; + Abc_ObjForEachFanin( pObj, pFanin, i ) + nSum += Seq_ObjFaninL(pObj, i); + return nSum; +} + +/**Function************************************************************* + + Synopsis [Generates the printable edge label with the initial state.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Seq_ObjFaninGetInitPrintable( Abc_Obj_t * pObj, int Edge ) +{ + static char Buffer[1000]; + Abc_InitType_t Init; + int nLatches, i; + nLatches = Seq_ObjFaninL( pObj, Edge ); + for ( i = 0; i < nLatches; i++ ) + { + Init = Seq_LatInit( Seq_NodeGetLat(pObj, Edge, i) ); + if ( Init == ABC_INIT_NONE ) + Buffer[i] = '_'; + else if ( Init == ABC_INIT_ZERO ) + Buffer[i] = '0'; + else if ( Init == ABC_INIT_ONE ) + Buffer[i] = '1'; + else if ( Init == ABC_INIT_DC ) + Buffer[i] = 'x'; + else assert( 0 ); + } + Buffer[nLatches] = 0; + return Buffer; +} + +/**Function************************************************************* + + Synopsis [Sets the given value to all the latches of the edge.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NodeLatchSetValues( Abc_Obj_t * pObj, int Edge, Abc_InitType_t Init ) +{ + Seq_Lat_t * pLat, * pRing; + int c; + pRing = Seq_NodeGetRing(pObj, Edge); + if ( pRing == NULL ) + return; + for ( c = 0, pLat = pRing; !c || pLat != pRing; c++, pLat = pLat->pNext ) + Seq_LatSetInit( pLat, Init ); +} + +/**Function************************************************************* + + Synopsis [Sets the given value to all the latches of the edge.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NtkLatchSetValues( Abc_Ntk_t * pNtk, Abc_InitType_t Init ) +{ + Abc_Obj_t * pObj; + int i; + assert( Abc_NtkIsSeq( pNtk ) ); + Abc_NtkForEachPo( pNtk, pObj, i ) + Seq_NodeLatchSetValues( pObj, 0, Init ); + Abc_NtkForEachNode( pNtk, pObj, i ) + { + Seq_NodeLatchSetValues( pObj, 0, Init ); + Seq_NodeLatchSetValues( pObj, 1, Init ); + } +} + + +/**Function************************************************************* + + Synopsis [Counts the number of latches in the sequential AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_NtkLatchNum( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i, Counter; + assert( Abc_NtkIsSeq( pNtk ) ); + Counter = 0; + Abc_NtkForEachNode( pNtk, pObj, i ) + Counter += Seq_ObjFaninLSum( pObj ); + Abc_NtkForEachPo( pNtk, pObj, i ) + Counter += Seq_ObjFaninLSum( pObj ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Counts the number of latches in the sequential AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_NtkLatchNumMax( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i, Max, Cur; + assert( Abc_NtkIsSeq( pNtk ) ); + Max = 0; + Abc_AigForEachAnd( pNtk, pObj, i ) + { + Cur = Seq_ObjFaninLMax( pObj ); + if ( Max < Cur ) + Max = Cur; + } + Abc_NtkForEachPo( pNtk, pObj, i ) + { + Cur = Seq_ObjFaninL0( pObj ); + if ( Max < Cur ) + Max = Cur; + } + return Max; +} + +/**Function************************************************************* + + Synopsis [Counts the number of latches in the sequential AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_NtkLatchNumShared( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i, Counter; + assert( Abc_NtkIsSeq( pNtk ) ); + Counter = 0; + Abc_NtkForEachPi( pNtk, pObj, i ) + Counter += Seq_ObjFanoutLMax( pObj ); + Abc_NtkForEachNode( pNtk, pObj, i ) + Counter += Seq_ObjFanoutLMax( pObj ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Counts the number of latches in the sequential AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_ObjLatchGetInitNums( Abc_Obj_t * pObj, int Edge, int * pInits ) +{ + Abc_InitType_t Init; + int nLatches, i; + nLatches = Seq_ObjFaninL( pObj, Edge ); + for ( i = 0; i < nLatches; i++ ) + { + Init = Seq_NodeGetInitOne( pObj, Edge, i ); + pInits[Init]++; + } +} + +/**Function************************************************************* + + Synopsis [Counts the number of latches in the sequential AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NtkLatchGetInitNums( Abc_Ntk_t * pNtk, int * pInits ) +{ + Abc_Obj_t * pObj; + int i; + assert( Abc_NtkIsSeq( pNtk ) ); + for ( i = 0; i < 4; i++ ) + pInits[i] = 0; + Abc_NtkForEachPo( pNtk, pObj, i ) + Seq_ObjLatchGetInitNums( pObj, 0, pInits ); + Abc_NtkForEachNode( pNtk, pObj, i ) + { + if ( Abc_ObjFaninNum(pObj) > 0 ) + Seq_ObjLatchGetInitNums( pObj, 0, pInits ); + if ( Abc_ObjFaninNum(pObj) > 1 ) + Seq_ObjLatchGetInitNums( pObj, 1, pInits ); + } +} + +/**Function************************************************************* + + Synopsis [Report nodes with equal fanins.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_NtkLatchGetEqualFaninNum( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i, Counter; + assert( Abc_NtkIsSeq( pNtk ) ); + Counter = 0; + Abc_AigForEachAnd( pNtk, pObj, i ) + if ( Abc_ObjFaninId0(pObj) == Abc_ObjFaninId1(pObj) ) + Counter++; + if ( Counter ) + printf( "The number of nodes with equal fanins = %d.\n", Counter ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Returns the maximum latch number on any of the fanouts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_NtkCountNodesAboveLimit( Abc_Ntk_t * pNtk, int Limit ) +{ + Abc_Obj_t * pNode; + int i, Counter; + assert( !Abc_NtkIsSeq(pNtk) ); + Counter = 0; + Abc_NtkForEachNode( pNtk, pNode, i ) + if ( Abc_ObjFaninNum(pNode) > Limit ) + Counter++; + return Counter; +} + +/**Function************************************************************* + + Synopsis [Computes area flows.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_MapComputeAreaFlows( Abc_Ntk_t * pNtk, int fVerbose ) +{ + Abc_Seq_t * p = pNtk->pManFunc; + Abc_Obj_t * pObj; + float AFlow; + int i, c; + + assert( Abc_NtkIsSeq(pNtk) ); + + Vec_IntFill( p->vAFlows, p->nSize, Abc_Float2Int( (float)0.0 ) ); + + // update all values iteratively + for ( c = 0; c < 7; c++ ) + { + Abc_AigForEachAnd( pNtk, pObj, i ) + { + AFlow = (float)1.0 + Seq_NodeGetFlow( Abc_ObjFanin0(pObj) ) + Seq_NodeGetFlow( Abc_ObjFanin1(pObj) ); + AFlow /= Abc_ObjFanoutNum(pObj); + pObj->pNext = (void *)Abc_Float2Int( AFlow ); + } + Abc_AigForEachAnd( pNtk, pObj, i ) + { + AFlow = Abc_Int2Float( (int)pObj->pNext ); + pObj->pNext = NULL; + Seq_NodeSetFlow( pObj, AFlow ); + +// printf( "%5d : %6.1f\n", pObj->Id, Seq_NodeGetFlow(pObj) ); + } +// printf( "\n" ); + } + return 1; +} + + +/**Function************************************************************* + + Synopsis [Collects all the internal nodes reachable from POs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NtkReachNodesFromPos_rec( Abc_Obj_t * pAnd, Vec_Ptr_t * vNodes ) +{ + // skip if this is a non-PI node + if ( !Abc_AigNodeIsAnd(pAnd) ) + return; + // skip a visited node + if ( Abc_NodeIsTravIdCurrent(pAnd) ) + return; + Abc_NodeSetTravIdCurrent(pAnd); + // visit the fanin nodes + Seq_NtkReachNodesFromPos_rec( Abc_ObjFanin0(pAnd), vNodes ); + Seq_NtkReachNodesFromPos_rec( Abc_ObjFanin1(pAnd), vNodes ); + // add this node + Vec_PtrPush( vNodes, pAnd ); +} + +/**Function************************************************************* + + Synopsis [Collects all the internal nodes reachable from POs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Seq_NtkReachNodesFromPis_rec( Abc_Obj_t * pAnd, Vec_Ptr_t * vNodes ) +{ + Abc_Obj_t * pFanout; + int k; + // skip if this is a non-PI node + if ( !Abc_AigNodeIsAnd(pAnd) ) + return; + // skip a visited node + if ( Abc_NodeIsTravIdCurrent(pAnd) ) + return; + Abc_NodeSetTravIdCurrent(pAnd); + // visit the fanin nodes + Abc_ObjForEachFanout( pAnd, pFanout, k ) + Seq_NtkReachNodesFromPis_rec( pFanout, vNodes ); + // add this node + Vec_PtrPush( vNodes, pAnd ); +} + +/**Function************************************************************* + + Synopsis [Collects all the internal nodes reachable from POs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Seq_NtkReachNodes( Abc_Ntk_t * pNtk, int fFromPos ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj, * pFanout; + int i, k; + assert( Abc_NtkIsSeq(pNtk) ); + vNodes = Vec_PtrAlloc( 1000 ); + Abc_NtkIncrementTravId( pNtk ); + if ( fFromPos ) + { + // traverse the cone of each PO + Abc_NtkForEachPo( pNtk, pObj, i ) + Seq_NtkReachNodesFromPos_rec( Abc_ObjFanin0(pObj), vNodes ); + } + else + { + // tranvers the reverse cone of the constant node + pObj = Abc_AigConst1( pNtk ); + Abc_ObjForEachFanout( pObj, pFanout, k ) + Seq_NtkReachNodesFromPis_rec( pFanout, vNodes ); + // tranvers the reverse cone of the PIs + Abc_NtkForEachPi( pNtk, pObj, i ) + Abc_ObjForEachFanout( pObj, pFanout, k ) + Seq_NtkReachNodesFromPis_rec( pFanout, vNodes ); + } + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Perform sequential cleanup.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Seq_NtkCleanup( Abc_Ntk_t * pNtk, int fVerbose ) +{ + Vec_Ptr_t * vNodesPo, * vNodesPi; + int Counter = 0; + assert( Abc_NtkIsSeq(pNtk) ); + // collect the nodes reachable from POs and PIs + vNodesPo = Seq_NtkReachNodes( pNtk, 1 ); + vNodesPi = Seq_NtkReachNodes( pNtk, 0 ); + printf( "Total nodes = %6d. Reachable from POs = %6d. Reachable from PIs = %6d.\n", + Abc_NtkNodeNum(pNtk), Vec_PtrSize(vNodesPo), Vec_PtrSize(vNodesPi) ); + if ( Abc_NtkNodeNum(pNtk) > Vec_PtrSize(vNodesPo) ) + { +// Counter = Abc_NtkReduceNodes( pNtk, vNodesPo ); + Counter = 0; + if ( fVerbose ) + printf( "Cleanup removed %d nodes that are not reachable from the POs.\n", Counter ); + } + Vec_PtrFree( vNodesPo ); + Vec_PtrFree( vNodesPi ); + return Counter; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/temp.c b/abc_with_bb_support/src/base/temp.c new file mode 100644 index 000000000..b981243a0 --- /dev/null +++ b/abc_with_bb_support/src/base/temp.c @@ -0,0 +1,83 @@ + +/**Function************************************************************* + + Synopsis [Command procedure to allow for static BDD variable ordering.] + + Description [This procedure should be integrated in "abc\src\base\abci\abc.c" + similar to how procedure Abc_CommandReorder() is currently integrated.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandOrder( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + FILE * pOut, * pErr, * pFile; + Abc_Ntk_t * pNtk; + char * pFileName; + int c; + int fReverse; + int fVerbose; + extern void Abc_NtkImplementCiOrder( Abc_Ntk_t * pNtk, char * pFileName, int fReverse, int fVerbose ); + extern void Abc_NtkFindCiOrder( Abc_Ntk_t * pNtk, int fReverse, int fVerbose ); + + pNtk = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set defaults + fReverse = 0; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "rvh" ) ) != EOF ) + { + switch ( c ) + { + case 'r': + fReverse ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + + if ( pNtk == NULL ) + { + fprintf( pErr, "Empty network.\n" ); + return 1; + } + + // if the var order file is given, implement this order + pFileName = NULL; + if ( argc == globalUtilOptind + 1 ) + { + pFileName = argv[globalUtilOptind]; + pFile = fopen( pFileName, "r" ); + if ( pFile == NULL ) + { + fprintf( pErr, "Cannot open file \"%s\" with the BDD variable order.\n", pFileName ); + return 1; + } + fclose( pFile ); + } + if ( pFileName ) + Abc_NtkImplementCiOrder( pNtk, pFileName, fReverse, fVerbose ); + else + Abc_NtkFindCiOrder( pNtk, fReverse, fVerbose ); + return 0; + +usage: + fprintf( pErr, "usage: order [-rvh] \n" ); + fprintf( pErr, "\t computes a good static CI variable order\n" ); + fprintf( pErr, "\t-r : toggle reverse ordering [default = %s]\n", fReverse? "yes": "no" ); + fprintf( pErr, "\t-v : prints verbose information [default = %s]\n", fVerbose? "yes": "no" ); + fprintf( pErr, "\t-h : print the command usage\n"); + fprintf( pErr, "\t : (optional) file with the given variable order\n" ); + return 1; +} diff --git a/abc_with_bb_support/src/base/ver/module.make b/abc_with_bb_support/src/base/ver/module.make new file mode 100644 index 000000000..e77cb1da3 --- /dev/null +++ b/abc_with_bb_support/src/base/ver/module.make @@ -0,0 +1,4 @@ +SRC += src/base/ver/verCore.c \ + src/base/ver/verFormula.c \ + src/base/ver/verParse.c \ + src/base/ver/verStream.c diff --git a/abc_with_bb_support/src/base/ver/ver.h b/abc_with_bb_support/src/base/ver/ver.h new file mode 100644 index 000000000..813d94d6b --- /dev/null +++ b/abc_with_bb_support/src/base/ver/ver.h @@ -0,0 +1,118 @@ +/**CFile**************************************************************** + + FileName [ver.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Verilog parser.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - August 19, 2006.] + + Revision [$Id: ver.h,v 1.00 2006/08/19 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __VER_H__ +#define __VER_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Ver_Man_t_ Ver_Man_t; +typedef struct Ver_Stream_t_ Ver_Stream_t; + +struct Ver_Man_t_ +{ + // internal parameters + int fMapped; // mapped verilog + int fUseMemMan; // allocate memory manager in the networks + int fCheck; // checks network for currectness + // input file stream + char * pFileName; + Ver_Stream_t * pReader; + int fNameLast; + ProgressBar * pProgress; + // current design + Abc_Lib_t * pDesign; + st_table * tName2Suffix; + // error handling + FILE * Output; + int fTopLevel; + int fError; + char sError[2000]; + // intermediate structures + Vec_Ptr_t * vNames; + Vec_Ptr_t * vStackFn; + Vec_Int_t * vStackOp; + Vec_Int_t * vPerm; +}; + + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// ITERATORS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== verCore.c ========================================================*/ +extern Abc_Lib_t * Ver_ParseFile( char * pFileName, Abc_Lib_t * pGateLib, int fCheck, int fUseMemMan ); +extern void Ver_ParsePrintErrorMessage( Ver_Man_t * p ); +/*=== verFormula.c ========================================================*/ +extern void * Ver_FormulaParser( char * pFormula, void * pMan, Vec_Ptr_t * vNames, Vec_Ptr_t * vStackFn, Vec_Int_t * vStackOp, char * pErrorMessage ); +extern void * Ver_FormulaReduction( char * pFormula, void * pMan, Vec_Ptr_t * vNames, char * pErrorMessage ); +/*=== verParse.c ========================================================*/ +extern int Ver_ParseSkipComments( Ver_Man_t * p ); +extern char * Ver_ParseGetName( Ver_Man_t * p ); +/*=== verStream.c ========================================================*/ +extern Ver_Stream_t * Ver_StreamAlloc( char * pFileName ); +extern void Ver_StreamFree( Ver_Stream_t * p ); +extern char * Ver_StreamGetFileName( Ver_Stream_t * p ); +extern int Ver_StreamGetFileSize( Ver_Stream_t * p ); +extern int Ver_StreamGetCurPosition( Ver_Stream_t * p ); +extern int Ver_StreamGetLineNumber( Ver_Stream_t * p ); + +extern int Ver_StreamIsOkey( Ver_Stream_t * p ); +extern char Ver_StreamScanChar( Ver_Stream_t * p ); +extern char Ver_StreamPopChar( Ver_Stream_t * p ); +extern void Ver_StreamSkipChars( Ver_Stream_t * p, char * pCharsToSkip ); +extern void Ver_StreamSkipToChars( Ver_Stream_t * p, char * pCharsToStop ); +extern char * Ver_StreamGetWord( Ver_Stream_t * p, char * pCharsToStop ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/base/ver/verCore.c b/abc_with_bb_support/src/base/ver/verCore.c new file mode 100644 index 000000000..7009ef1bd --- /dev/null +++ b/abc_with_bb_support/src/base/ver/verCore.c @@ -0,0 +1,2839 @@ +/**CFile**************************************************************** + + FileName [verCore.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Verilog parser.] + + Synopsis [Parses several flavors of structural Verilog.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - August 19, 2006.] + + Revision [$Id: verCore.c,v 1.00 2006/08/19 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ver.h" +#include "mio.h" +#include "main.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// types of verilog signals +typedef enum { + VER_SIG_NONE = 0, + VER_SIG_INPUT, + VER_SIG_OUTPUT, + VER_SIG_INOUT, + VER_SIG_REG, + VER_SIG_WIRE +} Ver_SignalType_t; + +// types of verilog gates +typedef enum { + VER_GATE_AND = 0, + VER_GATE_OR, + VER_GATE_XOR, + VER_GATE_BUF, + VER_GATE_NAND, + VER_GATE_NOR, + VER_GATE_XNOR, + VER_GATE_NOT +} Ver_GateType_t; + +static Ver_Man_t * Ver_ParseStart( char * pFileName, Abc_Lib_t * pGateLib ); +static void Ver_ParseStop( Ver_Man_t * p ); +static void Ver_ParseFreeData( Ver_Man_t * p ); +static void Ver_ParseInternal( Ver_Man_t * p ); +static int Ver_ParseModule( Ver_Man_t * p ); +static int Ver_ParseSignal( Ver_Man_t * p, Abc_Ntk_t * pNtk, Ver_SignalType_t SigType ); +static int Ver_ParseAlways( Ver_Man_t * p, Abc_Ntk_t * pNtk ); +static int Ver_ParseInitial( Ver_Man_t * p, Abc_Ntk_t * pNtk ); +static int Ver_ParseAssign( Ver_Man_t * p, Abc_Ntk_t * pNtk ); +static int Ver_ParseGateStandard( Ver_Man_t * pMan, Abc_Ntk_t * pNtk, Ver_GateType_t GateType ); +static int Ver_ParseGate( Ver_Man_t * p, Abc_Ntk_t * pNtk, Mio_Gate_t * pGate ); +static int Ver_ParseBox( Ver_Man_t * pMan, Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkBox ); +static int Ver_ParseConnectBox( Ver_Man_t * pMan, Abc_Obj_t * pBox ); +static int Ver_ParseAttachBoxes( Ver_Man_t * pMan ); + +static Abc_Obj_t * Ver_ParseCreatePi( Abc_Ntk_t * pNtk, char * pName ); +static Abc_Obj_t * Ver_ParseCreatePo( Abc_Ntk_t * pNtk, char * pName ); +static Abc_Obj_t * Ver_ParseCreateLatch( Abc_Ntk_t * pNtk, Abc_Obj_t * pNetLI, Abc_Obj_t * pNetLO ); +static Abc_Obj_t * Ver_ParseCreateInv( Abc_Ntk_t * pNtk, Abc_Obj_t * pNet ); + +static void Ver_ParseRemoveSuffixTable( Ver_Man_t * pMan ); + +static inline int Ver_NtkIsDefined( Abc_Ntk_t * pNtkBox ) { assert( pNtkBox->pName ); return Abc_NtkPiNum(pNtkBox) || Abc_NtkPoNum(pNtkBox); } +static inline int Ver_ObjIsConnected( Abc_Obj_t * pObj ) { assert( Abc_ObjIsBox(pObj) ); return Abc_ObjFaninNum(pObj) || Abc_ObjFanoutNum(pObj); } + +int glo_fMapped = 0; // this is bad! + +typedef struct Ver_Bundle_t_ Ver_Bundle_t; +struct Ver_Bundle_t_ +{ + char * pNameFormal; // the name of the formal net + Vec_Ptr_t * vNetsActual; // the vector of actual nets (MSB to LSB) +}; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Start parser.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ver_Man_t * Ver_ParseStart( char * pFileName, Abc_Lib_t * pGateLib ) +{ + Ver_Man_t * p; + p = ALLOC( Ver_Man_t, 1 ); + memset( p, 0, sizeof(Ver_Man_t) ); + p->pFileName = pFileName; + p->pReader = Ver_StreamAlloc( pFileName ); + if ( p->pReader == NULL ) + return NULL; + p->Output = stdout; + p->vNames = Vec_PtrAlloc( 100 ); + p->vStackFn = Vec_PtrAlloc( 100 ); + p->vStackOp = Vec_IntAlloc( 100 ); + p->vPerm = Vec_IntAlloc( 100 ); + // create the design library and assign the technology library + p->pDesign = Abc_LibCreate( pFileName ); + p->pDesign->pLibrary = pGateLib; + p->pDesign->pGenlib = Abc_FrameReadLibGen(); + return p; +} + +/**Function************************************************************* + + Synopsis [Stop parser.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ver_ParseStop( Ver_Man_t * p ) +{ + if ( p->pProgress ) + Extra_ProgressBarStop( p->pProgress ); + Ver_StreamFree( p->pReader ); + Vec_PtrFree( p->vNames ); + Vec_PtrFree( p->vStackFn ); + Vec_IntFree( p->vStackOp ); + Vec_IntFree( p->vPerm ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [File parser.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Lib_t * Ver_ParseFile( char * pFileName, Abc_Lib_t * pGateLib, int fCheck, int fUseMemMan ) +{ + Ver_Man_t * p; + Abc_Lib_t * pDesign; + // start the parser + p = Ver_ParseStart( pFileName, pGateLib ); + p->fMapped = glo_fMapped; + p->fCheck = fCheck; + p->fUseMemMan = fUseMemMan; + if ( glo_fMapped ) + { + Hop_ManStop(p->pDesign->pManFunc); + p->pDesign->pManFunc = NULL; + } + // parse the file + Ver_ParseInternal( p ); + // save the result + pDesign = p->pDesign; + p->pDesign = NULL; + // stop the parser + Ver_ParseStop( p ); + return pDesign; +} + +/**Function************************************************************* + + Synopsis [File parser.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ver_ParseInternal( Ver_Man_t * pMan ) +{ + Abc_Ntk_t * pNtk; + char * pToken; + int i; + + // preparse the modeles + pMan->pProgress = Extra_ProgressBarStart( stdout, Ver_StreamGetFileSize(pMan->pReader) ); + while ( 1 ) + { + // get the next token + pToken = Ver_ParseGetName( pMan ); + if ( pToken == NULL ) + break; + if ( strcmp( pToken, "module" ) ) + { + sprintf( pMan->sError, "Cannot read \"module\" directive." ); + Ver_ParsePrintErrorMessage( pMan ); + return; + } + // parse the module + if ( !Ver_ParseModule(pMan) ) + return; + } + Extra_ProgressBarStop( pMan->pProgress ); + pMan->pProgress = NULL; + + // process defined and undefined boxes + if ( !Ver_ParseAttachBoxes( pMan ) ) + return; + + // connect the boxes and check + Vec_PtrForEachEntry( pMan->pDesign->vModules, pNtk, i ) + { + // fix the dangling nets + Abc_NtkFinalizeRead( pNtk ); + // check the network for correctness + if ( pMan->fCheck && !Abc_NtkCheckRead( pNtk ) ) + { + pMan->fTopLevel = 1; + sprintf( pMan->sError, "The network check has failed for network %s.", pNtk->pName ); + Ver_ParsePrintErrorMessage( pMan ); + return; + } + } +} + +/**Function************************************************************* + + Synopsis [File parser.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ver_ParseFreeData( Ver_Man_t * p ) +{ + if ( p->pDesign ) + { + Abc_LibFree( p->pDesign, NULL ); + p->pDesign = NULL; + } +} + +/**Function************************************************************* + + Synopsis [Prints the error message including the file name and line number.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ver_ParsePrintErrorMessage( Ver_Man_t * p ) +{ + p->fError = 1; + if ( p->fTopLevel ) // the line number is not given + fprintf( p->Output, "%s: %s\n", p->pFileName, p->sError ); + else // print the error message with the line number + fprintf( p->Output, "%s (line %d): %s\n", + p->pFileName, Ver_StreamGetLineNumber(p->pReader), p->sError ); + // free the data + Ver_ParseFreeData( p ); +} + +/**Function************************************************************* + + Synopsis [Finds the network by name or create a new blackbox network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Ver_ParseFindOrCreateNetwork( Ver_Man_t * pMan, char * pName ) +{ + Abc_Ntk_t * pNtkNew; + // check if the network exists + if ( pNtkNew = Abc_LibFindModelByName( pMan->pDesign, pName ) ) + return pNtkNew; +//printf( "Creating network %s.\n", pName ); + // create new network + pNtkNew = Abc_NtkAlloc( ABC_NTK_NETLIST, ABC_FUNC_BLACKBOX, pMan->fUseMemMan ); + pNtkNew->pName = Extra_UtilStrsav( pName ); + pNtkNew->pSpec = NULL; + // add module to the design + Abc_LibAddModel( pMan->pDesign, pNtkNew ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Finds the network by name or create a new blackbox network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Ver_ParseFindNet( Abc_Ntk_t * pNtk, char * pName ) +{ + Abc_Obj_t * pObj; + if ( pObj = Abc_NtkFindNet(pNtk, pName) ) + return pObj; + if ( !strcmp( pName, "1\'b0" ) || !strcmp( pName, "1\'bx" ) ) + return Abc_NtkFindOrCreateNet( pNtk, "1\'b0" ); + if ( !strcmp( pName, "1\'b1" ) ) + return Abc_NtkFindOrCreateNet( pNtk, "1\'b1" ); + return NULL; +} + +/**Function************************************************************* + + Synopsis [Converts the network from the blackbox type into a different one.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseConvertNetwork( Ver_Man_t * pMan, Abc_Ntk_t * pNtk, int fMapped ) +{ + if ( fMapped ) + { + // convert from the blackbox into the network with local functions representated by AIGs + if ( pNtk->ntkFunc == ABC_FUNC_BLACKBOX ) + { + // change network type + assert( pNtk->pManFunc == NULL ); + pNtk->ntkFunc = ABC_FUNC_MAP; + pNtk->pManFunc = pMan->pDesign->pGenlib; + } + else if ( pNtk->ntkFunc != ABC_FUNC_MAP ) + { + sprintf( pMan->sError, "The network %s appears to have both gates and assign statements. Currently such network are not allowed. One way to fix this problem might be to replace assigns by buffers from the library.", pNtk->pName ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + } + else + { + // convert from the blackbox into the network with local functions representated by AIGs + if ( pNtk->ntkFunc == ABC_FUNC_BLACKBOX ) + { + // change network type + assert( pNtk->pManFunc == NULL ); + pNtk->ntkFunc = ABC_FUNC_AIG; + pNtk->pManFunc = pMan->pDesign->pManFunc; + } + else if ( pNtk->ntkFunc != ABC_FUNC_AIG ) + { + sprintf( pMan->sError, "The network %s appears to have both gates and assign statements. Currently such network are not allowed. One way to fix this problem might be to replace assigns by buffers from the library.", pNtk->pName ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Parses one Verilog module.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseModule( Ver_Man_t * pMan ) +{ + Mio_Gate_t * pGate; + Ver_Stream_t * p = pMan->pReader; + Abc_Ntk_t * pNtk, * pNtkTemp; + char * pWord, Symbol; + int RetValue; + + // get the network name + pWord = Ver_ParseGetName( pMan ); + + // get the network with this name + pNtk = Ver_ParseFindOrCreateNetwork( pMan, pWord ); + + // make sure we stopped at the opening paranthesis + if ( Ver_StreamPopChar(p) != '(' ) + { + sprintf( pMan->sError, "Cannot find \"(\" after \"module\" in network %s.", pNtk->pName ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + + // skip to the end of parantheses + do { + if ( Ver_ParseGetName( pMan ) == NULL ) + return 0; + Symbol = Ver_StreamPopChar(p); + } while ( Symbol == ',' ); + assert( Symbol == ')' ); + if ( !Ver_ParseSkipComments( pMan ) ) + return 0; + Symbol = Ver_StreamPopChar(p); + if ( Symbol != ';' ) + { + sprintf( pMan->sError, "Expected closing paranthesis after \"module\"." ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + + // parse the inputs/outputs/registers/wires/inouts + while ( 1 ) + { + Extra_ProgressBarUpdate( pMan->pProgress, Ver_StreamGetCurPosition(p), NULL ); + pWord = Ver_ParseGetName( pMan ); + if ( pWord == NULL ) + return 0; + if ( !strcmp( pWord, "input" ) ) + RetValue = Ver_ParseSignal( pMan, pNtk, VER_SIG_INPUT ); + else if ( !strcmp( pWord, "output" ) ) + RetValue = Ver_ParseSignal( pMan, pNtk, VER_SIG_OUTPUT ); + else if ( !strcmp( pWord, "reg" ) ) + RetValue = Ver_ParseSignal( pMan, pNtk, VER_SIG_REG ); + else if ( !strcmp( pWord, "wire" ) ) + RetValue = Ver_ParseSignal( pMan, pNtk, VER_SIG_WIRE ); + else if ( !strcmp( pWord, "inout" ) ) + RetValue = Ver_ParseSignal( pMan, pNtk, VER_SIG_INOUT ); + else + break; + if ( RetValue == 0 ) + return 0; + } + + // parse the remaining statements + while ( 1 ) + { + Extra_ProgressBarUpdate( pMan->pProgress, Ver_StreamGetCurPosition(p), NULL ); + + if ( !strcmp( pWord, "and" ) ) + RetValue = Ver_ParseGateStandard( pMan, pNtk, VER_GATE_AND ); + else if ( !strcmp( pWord, "or" ) ) + RetValue = Ver_ParseGateStandard( pMan, pNtk, VER_GATE_OR ); + else if ( !strcmp( pWord, "xor" ) ) + RetValue = Ver_ParseGateStandard( pMan, pNtk, VER_GATE_XOR ); + else if ( !strcmp( pWord, "buf" ) ) + RetValue = Ver_ParseGateStandard( pMan, pNtk, VER_GATE_BUF ); + else if ( !strcmp( pWord, "nand" ) ) + RetValue = Ver_ParseGateStandard( pMan, pNtk, VER_GATE_NAND ); + else if ( !strcmp( pWord, "nor" ) ) + RetValue = Ver_ParseGateStandard( pMan, pNtk, VER_GATE_NOR ); + else if ( !strcmp( pWord, "xnor" ) ) + RetValue = Ver_ParseGateStandard( pMan, pNtk, VER_GATE_XNOR ); + else if ( !strcmp( pWord, "not" ) ) + RetValue = Ver_ParseGateStandard( pMan, pNtk, VER_GATE_NOT ); + + else if ( !strcmp( pWord, "assign" ) ) + RetValue = Ver_ParseAssign( pMan, pNtk ); + else if ( !strcmp( pWord, "always" ) ) + RetValue = Ver_ParseAlways( pMan, pNtk ); + else if ( !strcmp( pWord, "initial" ) ) + RetValue = Ver_ParseInitial( pMan, pNtk ); + else if ( !strcmp( pWord, "endmodule" ) ) + break; + else if ( pMan->pDesign->pGenlib && (pGate = Mio_LibraryReadGateByName(pMan->pDesign->pGenlib, pWord)) ) // current design + RetValue = Ver_ParseGate( pMan, pNtk, pGate ); +// else if ( pMan->pDesign->pLibrary && st_lookup(pMan->pDesign->pLibrary->tModules, pWord, (char**)&pNtkTemp) ) // gate library +// RetValue = Ver_ParseGate( pMan, pNtkTemp ); + else // assume this is the box used in the current design + { + pNtkTemp = Ver_ParseFindOrCreateNetwork( pMan, pWord ); + RetValue = Ver_ParseBox( pMan, pNtk, pNtkTemp ); + } + if ( RetValue == 0 ) + return 0; + // skip the comments + if ( !Ver_ParseSkipComments( pMan ) ) + return 0; + // get new word + pWord = Ver_ParseGetName( pMan ); + if ( pWord == NULL ) + return 0; + } + + // convert from the blackbox into the network with local functions representated by AIGs + if ( pNtk->ntkFunc == ABC_FUNC_BLACKBOX ) + { + if ( Abc_NtkNodeNum(pNtk) > 0 || Abc_NtkBoxNum(pNtk) > 0 ) + { + if ( !Ver_ParseConvertNetwork( pMan, pNtk, pMan->fMapped ) ) + return 0; + } + else + { + Abc_Obj_t * pObj, * pBox, * pTerm; + int i; + pBox = Abc_NtkCreateBlackbox(pNtk); + Abc_NtkForEachPi( pNtk, pObj, i ) + { + pTerm = Abc_NtkCreateBi(pNtk); + Abc_ObjAddFanin( pTerm, Abc_ObjFanout0(pObj) ); + Abc_ObjAddFanin( pBox, pTerm ); + } + Abc_NtkForEachPo( pNtk, pObj, i ) + { + pTerm = Abc_NtkCreateBo(pNtk); + Abc_ObjAddFanin( pTerm, pBox ); + Abc_ObjAddFanin( Abc_ObjFanin0(pObj), pTerm ); + } + } + } + + // remove the table if needed + Ver_ParseRemoveSuffixTable( pMan ); + return 1; +} + + +/**Function************************************************************* + + Synopsis [Lookups the suffix of the signal of the form [m:n].] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseLookupSuffix( Ver_Man_t * pMan, char * pWord, int * pnMsb, int * pnLsb ) +{ + unsigned Value; + *pnMsb = *pnLsb = -1; + if ( pMan->tName2Suffix == NULL ) + return 1; + if ( !st_lookup( pMan->tName2Suffix, (char *)pWord, (char **)&Value ) ) + return 1; + *pnMsb = (Value >> 8) & 0xff; + *pnLsb = Value & 0xff; + return 1; +} + +/**Function************************************************************* + + Synopsis [Lookups the suffix of the signal of the form [m:n].] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseInsertsSuffix( Ver_Man_t * pMan, char * pWord, int nMsb, int nLsb ) +{ + unsigned Value; + if ( pMan->tName2Suffix == NULL ) + pMan->tName2Suffix = st_init_table( strcmp, st_strhash ); + if ( st_is_member( pMan->tName2Suffix, pWord ) ) + return 1; + assert( nMsb >= 0 && nMsb < 128 ); + assert( nLsb >= 0 && nLsb < 128 ); + Value = (nMsb << 8) | nLsb; + st_insert( pMan->tName2Suffix, Extra_UtilStrsav(pWord), (char *)Value ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Lookups the suffic of the signal of the form [m:n].] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ver_ParseRemoveSuffixTable( Ver_Man_t * pMan ) +{ + st_generator * gen; + char * pKey, * pValue; + if ( pMan->tName2Suffix == NULL ) + return; + st_foreach_item( pMan->tName2Suffix, gen, (char **)&pKey, (char **)&pValue ) + free( pKey ); + st_free_table( pMan->tName2Suffix ); + pMan->tName2Suffix = NULL; +} + +/**Function************************************************************* + + Synopsis [Determine signal prefix of the form [Beg:End].] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseSignalPrefix( Ver_Man_t * pMan, char ** ppWord, int * pnMsb, int * pnLsb ) +{ + char * pWord = *ppWord; + int nMsb, nLsb; + assert( pWord[0] == '[' ); + // get the beginning + nMsb = atoi( pWord + 1 ); + // find the splitter + while ( *pWord && *pWord != ':' && *pWord != ']' ) + pWord++; + if ( *pWord == 0 ) + { + sprintf( pMan->sError, "Cannot find closing bracket in this line." ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + if ( *pWord == ']' ) + nLsb = nMsb; + else + { + assert( *pWord == ':' ); + nLsb = atoi( pWord + 1 ); + // find the closing paranthesis + while ( *pWord && *pWord != ']' ) + pWord++; + if ( *pWord == 0 ) + { + sprintf( pMan->sError, "Cannot find closing bracket in this line." ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + assert( *pWord == ']' ); + pWord++; + } + assert( nMsb >= 0 && nLsb >= 0 ); + // return + *ppWord = pWord; + *pnMsb = nMsb; + *pnLsb = nLsb; + return 1; +} + +/**Function************************************************************* + + Synopsis [Determine signal suffix of the form [m:n].] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseSignalSuffix( Ver_Man_t * pMan, char * pWord, int * pnMsb, int * pnLsb ) +{ + char * pCur; + int Length; + Length = strlen(pWord); + assert( pWord[Length-1] == ']' ); + // walk backward + for ( pCur = pWord + Length - 2; pCur != pWord; pCur-- ) + if ( *pCur == ':' || *pCur == '[' ) + break; + if ( pCur == pWord ) + { + sprintf( pMan->sError, "Cannot find opening bracket in signal name %s.", pWord ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + if ( *pCur == '[' ) + { + *pnMsb = *pnLsb = atoi(pCur+1); + *pCur = 0; + return 1; + } + assert( *pCur == ':' ); + // get the end of the interval + *pnLsb = atoi(pCur+1); + // find the beginning + for ( pCur = pWord + Length - 2; pCur != pWord; pCur-- ) + if ( *pCur == '[' ) + break; + if ( pCur == pWord ) + { + sprintf( pMan->sError, "Cannot find opening bracket in signal name %s.", pWord ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + assert( *pCur == '[' ); + // get the beginning of the interval + *pnMsb = atoi(pCur+1); + // cut the word + *pCur = 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Returns the values of constant bits.] + + Description [The resulting bits are in MSB to LSB order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseConstant( Ver_Man_t * pMan, char * pWord ) +{ + int nBits, i; + assert( pWord[0] >= '1' && pWord[1] <= '9' ); + nBits = atoi(pWord); + // find the next symbol \' + while ( *pWord && *pWord != '\'' ) + pWord++; + if ( *pWord == 0 ) + { + sprintf( pMan->sError, "Cannot find symbol \' in the constant." ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + assert( *pWord == '\'' ); + pWord++; + if ( *pWord != 'b' ) + { + sprintf( pMan->sError, "Currently can only handle binary constants." ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + pWord++; + // scan the bits + Vec_PtrClear( pMan->vNames ); + for ( i = 0; i < nBits; i++ ) + { + if ( pWord[i] != '0' && pWord[i] != '1' && pWord[i] != 'x' ) + { + sprintf( pMan->sError, "Having problem parsing the binary constant." ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + if ( pWord[i] == 'x' ) + Vec_PtrPush( pMan->vNames, (void *)0 ); + else + Vec_PtrPush( pMan->vNames, (void *)(pWord[i]-'0') ); + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Parses one directive.] + + Description [The signals are added in the order from LSB to MSB.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseSignal( Ver_Man_t * pMan, Abc_Ntk_t * pNtk, Ver_SignalType_t SigType ) +{ + Ver_Stream_t * p = pMan->pReader; + char Buffer[1000], Symbol, * pWord; + int nMsb, nLsb, Bit, Limit, i; + nMsb = nLsb = -1; + while ( 1 ) + { + // get the next word + pWord = Ver_ParseGetName( pMan ); + if ( pWord == NULL ) + return 0; + + // check if the range is specified + if ( pWord[0] == '[' && !pMan->fNameLast ) + { + assert( nMsb == -1 && nLsb == -1 ); + Ver_ParseSignalPrefix( pMan, &pWord, &nMsb, &nLsb ); + // check the case when there is space between bracket and the next word + if ( *pWord == 0 ) + { + // get the signal name + pWord = Ver_ParseGetName( pMan ); + if ( pWord == NULL ) + return 0; + } + } + + // create signals + if ( nMsb == -1 && nLsb == -1 ) + { + if ( SigType == VER_SIG_INPUT || SigType == VER_SIG_INOUT ) + Ver_ParseCreatePi( pNtk, pWord ); + if ( SigType == VER_SIG_OUTPUT || SigType == VER_SIG_INOUT ) + Ver_ParseCreatePo( pNtk, pWord ); + if ( SigType == VER_SIG_WIRE || SigType == VER_SIG_REG ) + Abc_NtkFindOrCreateNet( pNtk, pWord ); + } + else + { + assert( nMsb >= 0 && nLsb >= 0 ); + // add to the hash table + Ver_ParseInsertsSuffix( pMan, pWord, nMsb, nLsb ); + // add signals from Lsb to Msb + Limit = nMsb > nLsb? nMsb - nLsb + 1: nLsb - nMsb + 1; + for ( i = 0, Bit = nLsb; i < Limit; i++, Bit = nMsb > nLsb ? Bit + 1: Bit - 1 ) + { + sprintf( Buffer, "%s[%d]", pWord, Bit ); + if ( SigType == VER_SIG_INPUT || SigType == VER_SIG_INOUT ) + Ver_ParseCreatePi( pNtk, Buffer ); + if ( SigType == VER_SIG_OUTPUT || SigType == VER_SIG_INOUT ) + Ver_ParseCreatePo( pNtk, Buffer ); + if ( SigType == VER_SIG_WIRE || SigType == VER_SIG_REG ) + Abc_NtkFindOrCreateNet( pNtk, Buffer ); + } + } + + Symbol = Ver_StreamPopChar(p); + if ( Symbol == ',' ) + continue; + if ( Symbol == ';' ) + return 1; + break; + } + sprintf( pMan->sError, "Cannot parse signal line (expected , or ;)." ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Parses one directive.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseAlways( Ver_Man_t * pMan, Abc_Ntk_t * pNtk ) +{ + Ver_Stream_t * p = pMan->pReader; + Abc_Obj_t * pNet, * pNet2; + int fStopAfterOne; + char * pWord, * pWord2; + char Symbol; + // parse the directive + pWord = Ver_ParseGetName( pMan ); + if ( pWord == NULL ) + return 0; + if ( pWord[0] == '@' ) + { + Ver_StreamSkipToChars( p, ")" ); + Ver_StreamPopChar(p); + // parse the directive + pWord = Ver_ParseGetName( pMan ); + if ( pWord == NULL ) + return 0; + } + // decide how many statements to parse + fStopAfterOne = 0; + if ( strcmp( pWord, "begin" ) ) + fStopAfterOne = 1; + // iterate over the initial states + while ( 1 ) + { + if ( !fStopAfterOne ) + { + // get the name of the output signal + pWord = Ver_ParseGetName( pMan ); + if ( pWord == NULL ) + return 0; + // look for the end of directive + if ( !strcmp( pWord, "end" ) ) + break; + } + // get the fanout net + pNet = Ver_ParseFindNet( pNtk, pWord ); + if ( pNet == NULL ) + { + sprintf( pMan->sError, "Cannot read the always statement for %s (output wire is not defined).", pWord ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + // get the equality sign + Symbol = Ver_StreamPopChar(p); + if ( Symbol != '<' && Symbol != '=' ) + { + sprintf( pMan->sError, "Cannot read the assign statement for %s (expected <= or =).", pWord ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + if ( Symbol == '<' ) + Ver_StreamPopChar(p); + // skip the comments + if ( !Ver_ParseSkipComments( pMan ) ) + return 0; + // get the second name + pWord2 = Ver_ParseGetName( pMan ); + if ( pWord2 == NULL ) + return 0; + // check if the name is complemented + if ( pWord2[0] == '~' ) + { + pNet2 = Ver_ParseFindNet( pNtk, pWord2+1 ); + pNet2 = Ver_ParseCreateInv( pNtk, pNet2 ); + } + else + pNet2 = Ver_ParseFindNet( pNtk, pWord2 ); + if ( pNet2 == NULL ) + { + sprintf( pMan->sError, "Cannot read the always statement for %s (input wire is not defined).", pWord2 ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + // create the latch + Ver_ParseCreateLatch( pNtk, pNet2, pNet ); + // remove the last symbol + Symbol = Ver_StreamPopChar(p); + assert( Symbol == ';' ); + // quit if only one directive + if ( fStopAfterOne ) + break; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Parses one directive.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseInitial( Ver_Man_t * pMan, Abc_Ntk_t * pNtk ) +{ + Ver_Stream_t * p = pMan->pReader; + Abc_Obj_t * pNode, * pNet; + int fStopAfterOne; + char * pWord, * pEquation; + char Symbol; + // parse the directive + pWord = Ver_ParseGetName( pMan ); + if ( pWord == NULL ) + return 0; + // decide how many statements to parse + fStopAfterOne = 0; + if ( strcmp( pWord, "begin" ) ) + fStopAfterOne = 1; + // iterate over the initial states + while ( 1 ) + { + if ( !fStopAfterOne ) + { + // get the name of the output signal + pWord = Ver_ParseGetName( pMan ); + if ( pWord == NULL ) + return 0; + // look for the end of directive + if ( !strcmp( pWord, "end" ) ) + break; + } + // get the fanout net + pNet = Ver_ParseFindNet( pNtk, pWord ); + if ( pNet == NULL ) + { + sprintf( pMan->sError, "Cannot read the initial statement for %s (output wire is not defined).", pWord ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + // get the equality sign + Symbol = Ver_StreamPopChar(p); + if ( Symbol != '<' && Symbol != '=' ) + { + sprintf( pMan->sError, "Cannot read the assign statement for %s (expected <= or =).", pWord ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + if ( Symbol == '<' ) + Ver_StreamPopChar(p); + // skip the comments + if ( !Ver_ParseSkipComments( pMan ) ) + return 0; + // get the second name + pEquation = Ver_StreamGetWord( p, ";" ); + if ( pEquation == NULL ) + return 0; + // find the corresponding latch + if ( Abc_ObjFaninNum(pNet) == 0 ) + { + sprintf( pMan->sError, "Cannot find the latch to assign the initial value." ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + pNode = Abc_ObjFanin0(Abc_ObjFanin0(pNet)); + assert( Abc_ObjIsLatch(pNode) ); + // set the initial state + if ( !strcmp(pEquation, "0") || !strcmp(pEquation, "1\'b0") ) + Abc_LatchSetInit0( pNode ); + else if ( !strcmp(pEquation, "1") || !strcmp(pEquation, "1\'b1") ) + Abc_LatchSetInit1( pNode ); +// else if ( !strcmp(pEquation, "2") ) +// Abc_LatchSetInitDc( pNode ); + else + { + sprintf( pMan->sError, "Incorrect initial value of the latch %s.", Abc_ObjName(pNet) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + // remove the last symbol + Symbol = Ver_StreamPopChar(p); + assert( Symbol == ';' ); + // quit if only one directive + if ( fStopAfterOne ) + break; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Parses one directive.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseAssign( Ver_Man_t * pMan, Abc_Ntk_t * pNtk ) +{ + char Buffer[1000], Buffer2[1000]; + Ver_Stream_t * p = pMan->pReader; + Abc_Obj_t * pNode, * pNet; + char * pWord, * pName, * pEquation; + Hop_Obj_t * pFunc; + char Symbol; + int i, Bit, Limit, Length, fReduction; + int nMsb, nLsb; + +// if ( Ver_StreamGetLineNumber(p) == 2756 ) +// { +// int x = 0; +// } + + // convert from the blackbox into the network with local functions representated by AIGs + if ( !Ver_ParseConvertNetwork( pMan, pNtk, pMan->fMapped ) ) + return 0; + + while ( 1 ) + { + // get the name of the output signal + pWord = Ver_ParseGetName( pMan ); + if ( pWord == NULL ) + return 0; + // check for vector-inputs + if ( !Ver_ParseLookupSuffix( pMan, pWord, &nMsb, &nLsb ) ) + return 0; + // handle special case of constant assignment + if ( nMsb >= 0 && nLsb >= 0 ) + { + // save the fanout name + strcpy( Buffer, pWord ); + // get the equality sign + if ( Ver_StreamPopChar(p) != '=' ) + { + sprintf( pMan->sError, "Cannot read the assign statement for %s (expected equality sign).", pWord ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + // get the constant + pWord = Ver_ParseGetName( pMan ); + if ( pWord == NULL ) + return 0; + // check if it is indeed a constant + if ( !(pWord[0] >= '0' && pWord[0] <= '9') ) + { + sprintf( pMan->sError, "Currently can only assign vector-signal \"%s\" to be a constant.", Buffer ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + + // get individual bits of the constant + if ( !Ver_ParseConstant( pMan, pWord ) ) + return 0; + // check that the constant has the same size + Limit = nMsb > nLsb? nMsb - nLsb + 1: nLsb - nMsb + 1; + if ( Limit != Vec_PtrSize(pMan->vNames) ) + { + sprintf( pMan->sError, "The constant size (%d) is different from the signal\"%s\" size (%d).", + Vec_PtrSize(pMan->vNames), Buffer, Limit ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + // iterate through the bits + for ( i = 0, Bit = nLsb; i < Limit; i++, Bit = nMsb > nLsb ? Bit + 1: Bit - 1 ) + { + // get the fanin net + if ( Vec_PtrEntry( pMan->vNames, Limit-1-i ) ) + pNet = Ver_ParseFindNet( pNtk, "1\'b1" ); + else + pNet = Ver_ParseFindNet( pNtk, "1\'b0" ); + assert( pNet != NULL ); + + // create the buffer + pNode = Abc_NtkCreateNodeBuf( pNtk, pNet ); + + // get the fanout net + sprintf( Buffer2, "%s[%d]", Buffer, Bit ); + pNet = Ver_ParseFindNet( pNtk, Buffer2 ); + if ( pNet == NULL ) + { + sprintf( pMan->sError, "Cannot read the assign statement for %s (output wire is not defined).", pWord ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + Abc_ObjAddFanin( pNet, pNode ); + } + // go to the end of the line + Ver_ParseSkipComments( pMan ); + } + else + { + // consider the case of reduction operations + fReduction = 0; + if ( pWord[0] == '{' && !pMan->fNameLast ) + fReduction = 1; + if ( fReduction ) + { + pWord++; + pWord[strlen(pWord)-1] = 0; + assert( pWord[0] != '\\' ); + } + // get the fanout net + pNet = Ver_ParseFindNet( pNtk, pWord ); + if ( pNet == NULL ) + { + sprintf( pMan->sError, "Cannot read the assign statement for %s (output wire is not defined).", pWord ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + // get the equality sign + if ( Ver_StreamPopChar(p) != '=' ) + { + sprintf( pMan->sError, "Cannot read the assign statement for %s (expected equality sign).", pWord ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + // skip the comments + if ( !Ver_ParseSkipComments( pMan ) ) + return 0; + // get the second name + if ( fReduction ) + pEquation = Ver_StreamGetWord( p, ";" ); + else + pEquation = Ver_StreamGetWord( p, ",;" ); + if ( pEquation == NULL ) + { + sprintf( pMan->sError, "Cannot read the equation for %s.", Abc_ObjName(pNet) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + + // consider the case of mapped network + Vec_PtrClear( pMan->vNames ); + if ( pMan->fMapped ) + { + if ( !strcmp( pEquation, "1\'b0" ) ) + pFunc = (Hop_Obj_t *)Mio_LibraryReadConst0(Abc_FrameReadLibGen()); + else if ( !strcmp( pEquation, "1\'b1" ) ) + pFunc = (Hop_Obj_t *)Mio_LibraryReadConst1(Abc_FrameReadLibGen()); + else + { + // "assign foo = \bar ;" + if ( *pEquation == '\\' ) + { + pEquation++; + pEquation[strlen(pEquation) - 1] = 0; + } + if ( Ver_ParseFindNet(pNtk, pEquation) == NULL ) + { + sprintf( pMan->sError, "Cannot read Verilog with non-trivial assignments in the mapped netlist." ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + Vec_PtrPush( pMan->vNames, (void *)strlen(pEquation) ); + Vec_PtrPush( pMan->vNames, pEquation ); + // get the buffer + pFunc = (Hop_Obj_t *)Mio_LibraryReadBuf(Abc_FrameReadLibGen()); + if ( pFunc == NULL ) + { + sprintf( pMan->sError, "Reading assign statement for node %s has failed because the genlib library has no buffer.", Abc_ObjName(pNet) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + } + } + else + { + if ( !strcmp(pEquation, "0") || !strcmp(pEquation, "1\'b0") || !strcmp(pEquation, "1\'bx") ) + pFunc = Hop_ManConst0(pNtk->pManFunc); + else if ( !strcmp(pEquation, "1") || !strcmp(pEquation, "1\'b1") ) + pFunc = Hop_ManConst1(pNtk->pManFunc); + else if ( fReduction ) + pFunc = Ver_FormulaReduction( pEquation, pNtk->pManFunc, pMan->vNames, pMan->sError ); + else + pFunc = Ver_FormulaParser( pEquation, pNtk->pManFunc, pMan->vNames, pMan->vStackFn, pMan->vStackOp, pMan->sError ); + if ( pFunc == NULL ) + { + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + } + + // create the node with the given inputs + pNode = Abc_NtkCreateNode( pNtk ); + pNode->pData = pFunc; + Abc_ObjAddFanin( pNet, pNode ); + // connect to fanin nets + for ( i = 0; i < Vec_PtrSize(pMan->vNames)/2; i++ ) + { + // get the name of this signal + Length = (int)Vec_PtrEntry( pMan->vNames, 2*i ); + pName = Vec_PtrEntry( pMan->vNames, 2*i + 1 ); + pName[Length] = 0; + // find the corresponding net + pNet = Ver_ParseFindNet( pNtk, pName ); + if ( pNet == NULL ) + { + sprintf( pMan->sError, "Cannot read the assign statement for %s (input wire %s is not defined).", pWord, pName ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + Abc_ObjAddFanin( pNode, pNet ); + } + } + + Symbol = Ver_StreamPopChar(p); + if ( Symbol == ',' ) + continue; + if ( Symbol == ';' ) + return 1; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Parses one directive.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseGateStandard( Ver_Man_t * pMan, Abc_Ntk_t * pNtk, Ver_GateType_t GateType ) +{ + Ver_Stream_t * p = pMan->pReader; + Abc_Obj_t * pNet, * pNode; + char * pWord, Symbol; + + // convert from the blackbox into the network with local functions representated by AIGs + if ( !Ver_ParseConvertNetwork( pMan, pNtk, pMan->fMapped ) ) + return 0; + + // this is gate name - throw it away + if ( Ver_StreamPopChar(p) != '(' ) + { + sprintf( pMan->sError, "Cannot parse a standard gate (expected opening paranthesis)." ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + Ver_ParseSkipComments( pMan ); + + // create the node + pNode = Abc_NtkCreateNode( pNtk ); + + // parse pairs of formal/actural inputs + while ( 1 ) + { + // parse the output name + pWord = Ver_ParseGetName( pMan ); + if ( pWord == NULL ) + return 0; + // get the net corresponding to this output + pNet = Ver_ParseFindNet( pNtk, pWord ); + if ( pNet == NULL ) + { + sprintf( pMan->sError, "Net is missing in gate %s.", pWord ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + // if this is the first net, add it as an output + if ( Abc_ObjFanoutNum(pNode) == 0 ) + Abc_ObjAddFanin( pNet, pNode ); + else + Abc_ObjAddFanin( pNode, pNet ); + // check if it is the end of gate + Ver_ParseSkipComments( pMan ); + Symbol = Ver_StreamPopChar(p); + if ( Symbol == ')' ) + break; + // skip comma + if ( Symbol != ',' ) + { + sprintf( pMan->sError, "Cannot parse a standard gate %s (expected closing paranthesis).", Abc_ObjName(Abc_ObjFanout0(pNode)) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + Ver_ParseSkipComments( pMan ); + } + if ( (GateType == VER_GATE_BUF || GateType == VER_GATE_NOT) && Abc_ObjFaninNum(pNode) != 1 ) + { + sprintf( pMan->sError, "Buffer or interver with multiple fanouts %s (currently not supported).", Abc_ObjName(Abc_ObjFanout0(pNode)) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + + // check if it is the end of gate + Ver_ParseSkipComments( pMan ); + if ( Ver_StreamPopChar(p) != ';' ) + { + sprintf( pMan->sError, "Cannot read standard gate %s (expected closing semicolumn).", Abc_ObjName(Abc_ObjFanout0(pNode)) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + // add logic function + if ( GateType == VER_GATE_AND || GateType == VER_GATE_NAND ) + pNode->pData = Hop_CreateAnd( pNtk->pManFunc, Abc_ObjFaninNum(pNode) ); + else if ( GateType == VER_GATE_OR || GateType == VER_GATE_NOR ) + pNode->pData = Hop_CreateOr( pNtk->pManFunc, Abc_ObjFaninNum(pNode) ); + else if ( GateType == VER_GATE_XOR || GateType == VER_GATE_XNOR ) + pNode->pData = Hop_CreateExor( pNtk->pManFunc, Abc_ObjFaninNum(pNode) ); + else if ( GateType == VER_GATE_BUF || GateType == VER_GATE_NOT ) + pNode->pData = Hop_CreateAnd( pNtk->pManFunc, Abc_ObjFaninNum(pNode) ); + if ( GateType == VER_GATE_NAND || GateType == VER_GATE_NOR || GateType == VER_GATE_XNOR || GateType == VER_GATE_NOT ) + pNode->pData = Hop_Not( pNode->pData ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Returns the index of the given pin the gate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_FindGateInput( Mio_Gate_t * pGate, char * pName ) +{ + Mio_Pin_t * pGatePin; + int i; + for ( i = 0, pGatePin = Mio_GateReadPins(pGate); pGatePin != NULL; pGatePin = Mio_PinReadNext(pGatePin), i++ ) + if ( strcmp(pName, Mio_PinReadName(pGatePin)) == 0 ) + return i; + if ( strcmp(pName, Mio_GateReadOutName(pGate)) == 0 ) + return i; + return -1; +} + +/**Function************************************************************* + + Synopsis [Parses one directive.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseGate( Ver_Man_t * pMan, Abc_Ntk_t * pNtk, Mio_Gate_t * pGate ) +{ + Ver_Stream_t * p = pMan->pReader; + Abc_Obj_t * pNetActual, * pNode; + char * pWord, Symbol; + int Input, i, nFanins = Mio_GateReadInputs(pGate); + + // convert from the blackbox into the network with local functions representated by gates + if ( 1 != pMan->fMapped ) + { + sprintf( pMan->sError, "The network appears to be mapped. Use \"r -m\" to read mapped Verilog." ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + + // update the network type if needed + if ( !Ver_ParseConvertNetwork( pMan, pNtk, 1 ) ) + return 0; + + // parse the directive and set the pointers to the PIs/POs of the gate + pWord = Ver_ParseGetName( pMan ); + if ( pWord == NULL ) + return 0; + // this is gate name - throw it away + if ( Ver_StreamPopChar(p) != '(' ) + { + sprintf( pMan->sError, "Cannot parse gate %s (expected opening paranthesis).", Mio_GateReadName(pGate) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + Ver_ParseSkipComments( pMan ); + + // start the node + pNode = Abc_NtkCreateNode( pNtk ); + pNode->pData = pGate; + + // parse pairs of formal/actural inputs + Vec_IntClear( pMan->vPerm ); + while ( 1 ) + { + // process one pair of formal/actual parameters + if ( Ver_StreamPopChar(p) != '.' ) + { + sprintf( pMan->sError, "Cannot parse gate %s (expected .).", Mio_GateReadName(pGate) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + + // parse the formal name + pWord = Ver_ParseGetName( pMan ); + if ( pWord == NULL ) + return 0; + + // find the corresponding pin of the gate + Input = Ver_FindGateInput( pGate, pWord ); + if ( Input == -1 ) + { + sprintf( pMan->sError, "Formal input name %s cannot be found in the gate %s.", pWord, Mio_GateReadOutName(pGate) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + + // open the paranthesis + if ( Ver_StreamPopChar(p) != '(' ) + { + sprintf( pMan->sError, "Cannot formal parameter %s of gate %s (expected opening paranthesis).", pWord, Mio_GateReadName(pGate) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + + // parse the actual name + pWord = Ver_ParseGetName( pMan ); + if ( pWord == NULL ) + return 0; + // check if the name is complemented + assert( pWord[0] != '~' ); +/* + fCompl = (pWord[0] == '~'); + if ( fCompl ) + { + fComplUsed = 1; + pWord++; + if ( pNtk->pData == NULL ) + pNtk->pData = Extra_MmFlexStart(); + } +*/ + // get the actual net + pNetActual = Ver_ParseFindNet( pNtk, pWord ); + if ( pNetActual == NULL ) + { + sprintf( pMan->sError, "Actual net %s is missing.", pWord ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + + // close the paranthesis + if ( Ver_StreamPopChar(p) != ')' ) + { + sprintf( pMan->sError, "Cannot formal parameter %s of gate %s (expected closing paranthesis).", pWord, Mio_GateReadName(pGate) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + + // add the fanin + if ( Input < nFanins ) + { + Vec_IntPush( pMan->vPerm, Input ); + Abc_ObjAddFanin( pNode, pNetActual ); // fanin + } + else + Abc_ObjAddFanin( pNetActual, pNode ); // fanout + + // check if it is the end of gate + Ver_ParseSkipComments( pMan ); + Symbol = Ver_StreamPopChar(p); + if ( Symbol == ')' ) + break; + + // skip comma + if ( Symbol != ',' ) + { + sprintf( pMan->sError, "Cannot formal parameter %s of gate %s (expected closing paranthesis).", pWord, Mio_GateReadName(pGate) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + Ver_ParseSkipComments( pMan ); + } + + // check that the gate as the same number of input + if ( !(Abc_ObjFaninNum(pNode) == nFanins && Abc_ObjFanoutNum(pNode) == 1) ) + { + sprintf( pMan->sError, "Parsing of gate %s has failed.", Mio_GateReadName(pGate) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + + // check if it is the end of gate + Ver_ParseSkipComments( pMan ); + if ( Ver_StreamPopChar(p) != ';' ) + { + sprintf( pMan->sError, "Cannot read gate %s (expected closing semicolumn).", Mio_GateReadName(pGate) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + + // check if we need to permute the inputs + Vec_IntForEachEntry( pMan->vPerm, Input, i ) + if ( Input != i ) + break; + if ( i < Vec_IntSize(pMan->vPerm) ) + { + // add the fanin numnbers to the end of the permuation array + for ( i = 0; i < nFanins; i++ ) + Vec_IntPush( pMan->vPerm, Abc_ObjFaninId(pNode, i) ); + // write the fanin numbers into their corresponding places (according to the gate) + for ( i = 0; i < nFanins; i++ ) + Vec_IntWriteEntry( &pNode->vFanins, Vec_IntEntry(pMan->vPerm, i), Vec_IntEntry(pMan->vPerm, i+nFanins) ); + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Parses one directive.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseBox( Ver_Man_t * pMan, Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkBox ) +{ + char Buffer[1000]; + Ver_Stream_t * p = pMan->pReader; + Ver_Bundle_t * pBundle; + Vec_Ptr_t * vBundles; + Abc_Obj_t * pNetActual; + Abc_Obj_t * pNode; + char * pWord, Symbol; + int fCompl, fFormalIsGiven; + int i, k, Bit, Limit, nMsb, nLsb, fQuit, flag; + + // gate the name of the box + pWord = Ver_ParseGetName( pMan ); + if ( pWord == NULL ) + return 0; + + // create a box with this name + pNode = Abc_NtkCreateBlackbox( pNtk ); + pNode->pData = pNtkBox; + Abc_ObjAssignName( pNode, pWord, NULL ); + + // continue parsing the box + if ( Ver_StreamPopChar(p) != '(' ) + { + sprintf( pMan->sError, "Cannot parse box %s (expected opening paranthesis).", Abc_ObjName(pNode) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + Ver_ParseSkipComments( pMan ); + + // parse pairs of formal/actual inputs + vBundles = Vec_PtrAlloc( 16 ); + pNode->pCopy = (Abc_Obj_t *)vBundles; + while ( 1 ) + { + // allocate the bundle (formal name + array of actual nets) + pBundle = ALLOC( Ver_Bundle_t, 1 ); + pBundle->pNameFormal = NULL; + pBundle->vNetsActual = Vec_PtrAlloc( 4 ); + Vec_PtrPush( vBundles, pBundle ); + + // process one pair of formal/actual parameters + fFormalIsGiven = 0; + if ( Ver_StreamScanChar(p) == '.' ) + { + fFormalIsGiven = 1; + if ( Ver_StreamPopChar(p) != '.' ) + { + sprintf( pMan->sError, "Cannot parse box %s (expected .).", Abc_ObjName(pNode) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + + // parse the formal name + pWord = Ver_ParseGetName( pMan ); + if ( pWord == NULL ) + return 0; + + // save the name + pBundle->pNameFormal = Extra_UtilStrsav( pWord ); + + // open the paranthesis + if ( Ver_StreamPopChar(p) != '(' ) + { + sprintf( pMan->sError, "Cannot formal parameter %s of box %s (expected opening paranthesis).", pWord, Abc_ObjName(pNode)); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + Ver_ParseSkipComments( pMan ); + } + + // check if this is the beginning of {} expression + Symbol = Ver_StreamScanChar(p); + + // consider the case of vector-inputs + if ( Symbol == '{' ) + { + // skip this char + Ver_StreamPopChar(p); + + // read actual names + i = 0; + fQuit = 0; + while ( 1 ) + { + // parse the formal name + Ver_ParseSkipComments( pMan ); + pWord = Ver_ParseGetName( pMan ); + if ( pWord == NULL ) + return 0; + + // check if the last char is a closing brace + if ( pWord[strlen(pWord)-1] == '}' ) + { + pWord[strlen(pWord)-1] = 0; + fQuit = 1; + } + if ( pWord[0] == 0 ) + break; + + // check for constant + if ( pWord[0] >= '1' && pWord[0] <= '9' ) + { + if ( !Ver_ParseConstant( pMan, pWord ) ) + return 0; + // add constant MSB to LSB + for ( k = 0; k < Vec_PtrSize(pMan->vNames); k++, i++ ) + { + // get the actual net + sprintf( Buffer, "1\'b%d", (int)(Vec_PtrEntry(pMan->vNames,k) != NULL) ); + pNetActual = Ver_ParseFindNet( pNtk, Buffer ); + if ( pNetActual == NULL ) + { + sprintf( pMan->sError, "Actual net \"%s\" is missing in gate \"%s\".", Buffer, Abc_ObjName(pNode) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + Vec_PtrPush( pBundle->vNetsActual, pNetActual ); + } + } + else + { + // get the suffix of the form [m:n] + if ( pWord[strlen(pWord)-1] == ']' && !pMan->fNameLast ) + Ver_ParseSignalSuffix( pMan, pWord, &nMsb, &nLsb ); + else + Ver_ParseLookupSuffix( pMan, pWord, &nMsb, &nLsb ); + + // generate signals + if ( nMsb == -1 && nLsb == -1 ) + { + // get the actual net + pNetActual = Ver_ParseFindNet( pNtk, pWord ); + if ( pNetActual == NULL ) + { + if ( !strncmp(pWord, "Open_", 5) || + !strncmp(pWord, "dct_unconnected", 15) ) + pNetActual = Abc_NtkCreateNet( pNtk ); + else + { + sprintf( pMan->sError, "Actual net \"%s\" is missing in box \"%s\".", pWord, Abc_ObjName(pNode) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + } + Vec_PtrPush( pBundle->vNetsActual, pNetActual ); + i++; + } + else + { + // go from MSB to LSB + assert( nMsb >= 0 && nLsb >= 0 ); + Limit = (nMsb > nLsb) ? nMsb - nLsb + 1: nLsb - nMsb + 1; + for ( Bit = nMsb, k = Limit - 1; k >= 0; Bit = (nMsb > nLsb ? Bit - 1: Bit + 1), k--, i++ ) + { + // get the actual net + sprintf( Buffer, "%s[%d]", pWord, Bit ); + pNetActual = Ver_ParseFindNet( pNtk, Buffer ); + if ( pNetActual == NULL ) + { + if ( !strncmp(pWord, "Open_", 5) || + !strncmp(pWord, "dct_unconnected", 15) ) + pNetActual = Abc_NtkCreateNet( pNtk ); + else + { + sprintf( pMan->sError, "Actual net \"%s\" is missing in box \"%s\".", pWord, Abc_ObjName(pNode) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + } + Vec_PtrPush( pBundle->vNetsActual, pNetActual ); + } + } + } + + if ( fQuit ) + break; + + // skip comma + Ver_ParseSkipComments( pMan ); + Symbol = Ver_StreamPopChar(p); + if ( Symbol == '}' ) + break; + if ( Symbol != ',' ) + { + sprintf( pMan->sError, "Cannot parse formal parameter %s of gate %s (expected comma).", pWord, Abc_ObjName(pNode) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + } + } + else + { + // get the next word + pWord = Ver_ParseGetName( pMan ); + if ( pWord == NULL ) + return 0; + // consider the case of empty name + fCompl = 0; + if ( pWord[0] == 0 ) + { + pNetActual = Abc_NtkCreateNet( pNtk ); + Vec_PtrPush( pBundle->vNetsActual, Abc_ObjNotCond( pNetActual, fCompl ) ); + } + else + { + // get the actual net + flag=0; + pNetActual = Ver_ParseFindNet( pNtk, pWord ); + if ( pNetActual == NULL ) + { + Ver_ParseLookupSuffix( pMan, pWord, &nMsb, &nLsb ); + if ( nMsb == -1 && nLsb == -1 ) + { + Ver_ParseSignalSuffix( pMan, pWord, &nMsb, &nLsb ); + if ( nMsb == -1 && nLsb == -1 ) + { + if ( !strncmp(pWord, "Open_", 5) || + !strncmp(pWord, "dct_unconnected", 15) ) + { + pNetActual = Abc_NtkCreateNet( pNtk ); + Vec_PtrPush( pBundle->vNetsActual, pNetActual ); + } + else + { + sprintf( pMan->sError, "Actual net \"%s\" is missing in box \"%s\".", pWord, Abc_ObjName(pNode) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + } + else + { + flag=1; + } + } + else + { + flag=1; + } + if (flag) + { + Limit = (nMsb > nLsb) ? nMsb - nLsb + 1: nLsb - nMsb + 1; + for ( Bit = nMsb, k = Limit - 1; k >= 0; Bit = (nMsb > nLsb ? Bit - 1: Bit + 1), k--) + { + // get the actual net + sprintf( Buffer, "%s[%d]", pWord, Bit ); + pNetActual = Ver_ParseFindNet( pNtk, Buffer ); + if ( pNetActual == NULL ) + { + if ( !strncmp(pWord, "Open_", 5) || + !strncmp(pWord, "dct_unconnected", 15)) + pNetActual = Abc_NtkCreateNet( pNtk ); + else + { + sprintf( pMan->sError, "Actual net \"%s\" is missing in box \"%s\".", pWord, Abc_ObjName(pNode) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + } + Vec_PtrPush( pBundle->vNetsActual, pNetActual ); + } + } + } + else + { + Vec_PtrPush( pBundle->vNetsActual, Abc_ObjNotCond( pNetActual, fCompl ) ); + } + } + } + + if ( fFormalIsGiven ) + { + // close the paranthesis + Ver_ParseSkipComments( pMan ); + if ( Ver_StreamPopChar(p) != ')' ) + { + sprintf( pMan->sError, "Cannot parse formal parameter %s of box %s (expected closing paranthesis).", pWord, Abc_ObjName(pNode) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + Ver_ParseSkipComments( pMan ); + } + + // check if it is the end of gate + Symbol = Ver_StreamPopChar(p); + if ( Symbol == ')' ) + break; + // skip comma + if ( Symbol != ',' ) + { + sprintf( pMan->sError, "Cannot parse formal parameter %s of box %s (expected comma).", pWord, Abc_ObjName(pNode) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + Ver_ParseSkipComments( pMan ); + } + + // check if it is the end of gate + Ver_ParseSkipComments( pMan ); + if ( Ver_StreamPopChar(p) != ';' ) + { + sprintf( pMan->sError, "Cannot read box %s (expected closing semicolumn).", Abc_ObjName(pNode) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + + return 1; +} + +/**Function************************************************************* + + Synopsis [Connects one box to the network] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ver_ParseFreeBundle( Ver_Bundle_t * pBundle ) +{ + FREE( pBundle->pNameFormal ); + Vec_PtrFree( pBundle->vNetsActual ); + free( pBundle ); +} + +/**Function************************************************************* + + Synopsis [Connects one box to the network] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseConnectBox( Ver_Man_t * pMan, Abc_Obj_t * pBox ) +{ + Vec_Ptr_t * vBundles = (Vec_Ptr_t *)pBox->pCopy; + Abc_Ntk_t * pNtk = pBox->pNtk; + Abc_Ntk_t * pNtkBox = pBox->pData; + Abc_Obj_t * pTerm, * pTermNew, * pNetAct; + Ver_Bundle_t * pBundle; + char * pNameFormal; + int i, k, j, iBundle, Length; + + assert( !Ver_ObjIsConnected(pBox) ); + assert( Ver_NtkIsDefined(pNtkBox) ); + assert( !Abc_NtkHasBlackbox(pNtkBox) || Abc_NtkBoxNum(pNtkBox) == 1 ); +/* + // clean the PI/PO nets + Abc_NtkForEachPi( pNtkBox, pTerm, i ) + Abc_ObjFanout0(pTerm)->pCopy = NULL; + Abc_NtkForEachPo( pNtkBox, pTerm, i ) + Abc_ObjFanin0(pTerm)->pCopy = NULL; +*/ + // check if some of them do not have formal names + Vec_PtrForEachEntry( vBundles, pBundle, k ) + if ( pBundle->pNameFormal == NULL ) + break; + if ( k < Vec_PtrSize(vBundles) ) + { + printf( "Warning: The instance %s of network %s will be connected without using formal names.\n", pNtkBox->pName, Abc_ObjName(pBox) ); + // add all actual nets in the bundles + iBundle = 0; + Vec_PtrForEachEntry( vBundles, pBundle, j ) + iBundle += Vec_PtrSize(pBundle->vNetsActual); + + // check the number of actual nets is the same as the number of formal nets + if ( iBundle != Abc_NtkPiNum(pNtkBox) + Abc_NtkPoNum(pNtkBox) ) + { + sprintf( pMan->sError, "The number of actual IOs (%d) is different from the number of formal IOs (%d) when instantiating network %s in box %s.", + Vec_PtrSize(vBundles), Abc_NtkPiNum(pNtkBox) + Abc_NtkPoNum(pNtkBox), pNtkBox->pName, Abc_ObjName(pBox) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + // connect bundles in the natural order + iBundle = 0; + Abc_NtkForEachPi( pNtkBox, pTerm, i ) + { + pBundle = Vec_PtrEntry( vBundles, iBundle++ ); + // the bundle is found - add the connections - using order LSB to MSB + Vec_PtrForEachEntryReverse( pBundle->vNetsActual, pNetAct, k ) + { + pTermNew = Abc_NtkCreateBi( pNtk ); + Abc_ObjAddFanin( pBox, pTermNew ); + Abc_ObjAddFanin( pTermNew, pNetAct ); + i++; + } + i--; + } + // create fanins of the box + Abc_NtkForEachPo( pNtkBox, pTerm, i ) + { + pBundle = Vec_PtrEntry( vBundles, iBundle++ ); + // the bundle is found - add the connections - using order LSB to MSB + Vec_PtrForEachEntryReverse( pBundle->vNetsActual, pNetAct, k ) + { + pTermNew = Abc_NtkCreateBo( pNtk ); + Abc_ObjAddFanin( pTermNew, pBox ); + Abc_ObjAddFanin( pNetAct, pTermNew ); + i++; + } + i--; + } + + // free the bundling + Vec_PtrForEachEntry( vBundles, pBundle, k ) + Ver_ParseFreeBundle( pBundle ); + Vec_PtrFree( vBundles ); + pBox->pCopy = NULL; + return 1; + } + + // bundles arrive in any order - but inside each bundle the order is MSB to LSB + // make sure every formal PI has a corresponding net + Abc_NtkForEachPi( pNtkBox, pTerm, i ) + { + // get the name of this formal net + pNameFormal = Abc_ObjName( Abc_ObjFanout0(pTerm) ); + // try to find the bundle with this formal net + pBundle = NULL; + Vec_PtrForEachEntry( vBundles, pBundle, k ) + if ( !strcmp(pBundle->pNameFormal, pNameFormal) ) + break; + assert( pBundle != NULL ); + // if the bundle is not found, try without parantheses + if ( k == Vec_PtrSize(vBundles) ) + { + pBundle = NULL; + Length = strlen(pNameFormal); + if ( pNameFormal[Length-1] == ']' ) + { + // find the opening brace + for ( Length--; Length >= 0; Length-- ) + if ( pNameFormal[Length] == '[' ) + break; + // compare names before brace + if ( Length > 0 ) + { + Vec_PtrForEachEntry( vBundles, pBundle, j ) + if ( !strncmp(pBundle->pNameFormal, pNameFormal, Length) ) + break; + if ( j == Vec_PtrSize(vBundles) ) + pBundle = NULL; + } + } + if ( pBundle == NULL ) + { + sprintf( pMan->sError, "Cannot find an actual net for the formal net %s when instantiating network %s in box %s.", + pNameFormal, pNtkBox->pName, Abc_ObjName(pBox) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + } + // the bundle is found - add the connections - using order LSB to MSB + Vec_PtrForEachEntryReverse( pBundle->vNetsActual, pNetAct, k ) + { + pTermNew = Abc_NtkCreateBi( pNtk ); + Abc_ObjAddFanin( pBox, pTermNew ); + Abc_ObjAddFanin( pTermNew, pNetAct ); + i++; + } + i--; + } + + // connect those formal POs that do have nets + Abc_NtkForEachPo( pNtkBox, pTerm, i ) + { + // get the name of this PI + pNameFormal = Abc_ObjName( Abc_ObjFanin0(pTerm) ); + // try to find this formal net in the bundle + pBundle = NULL; + Vec_PtrForEachEntry( vBundles, pBundle, k ) + if ( !strcmp(pBundle->pNameFormal, pNameFormal) ) + break; + assert( pBundle != NULL ); + // if the name is not found, try without parantheses + if ( k == Vec_PtrSize(vBundles) ) + { + pBundle = NULL; + Length = strlen(pNameFormal); + if ( pNameFormal[Length-1] == ']' ) + { + // find the opening brace + for ( Length--; Length >= 0; Length-- ) + if ( pNameFormal[Length] == '[' ) + break; + // compare names before brace + if ( Length > 0 ) + { + Vec_PtrForEachEntry( vBundles, pBundle, j ) + if ( !strncmp(pBundle->pNameFormal, pNameFormal, Length) ) + break; + if ( j == Vec_PtrSize(vBundles) ) + pBundle = NULL; + } + } + if ( pBundle == NULL ) + { +// printf( "Warning: The formal output %s is not driven when instantiating network %s in box %s.", +// pNameFormal, pNtkBox->pName, Abc_ObjName(pBox) ); + continue; + } + } + // the bundle is found - add the connections + Vec_PtrForEachEntryReverse( pBundle->vNetsActual, pNetAct, k ) + { + if ( !strcmp(Abc_ObjName(pNetAct), "1\'b0") || !strcmp(Abc_ObjName(pNetAct), "1\'b1") ) + { + sprintf( pMan->sError, "It looks like formal output %s is driving a constant net (%s) when instantiating network %s in box %s.", + pBundle->pNameFormal, Abc_ObjName(pNetAct), pNtkBox->pName, Abc_ObjName(pBox) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + pTermNew = Abc_NtkCreateBo( pNtk ); + Abc_ObjAddFanin( pTermNew, pBox ); + Abc_ObjAddFanin( pNetAct, pTermNew ); + i++; + } + i--; + } + + // free the bundling + Vec_PtrForEachEntry( vBundles, pBundle, k ) + Ver_ParseFreeBundle( pBundle ); + Vec_PtrFree( vBundles ); + pBox->pCopy = NULL; + return 1; +} + + +/**Function************************************************************* + + Synopsis [Connects the defined boxes.] + + Description [Returns 2 if there are any undef boxes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseConnectDefBoxes( Ver_Man_t * pMan ) +{ + Abc_Ntk_t * pNtk; + Abc_Obj_t * pBox; + int i, k, RetValue = 1; + // go through all the modules + Vec_PtrForEachEntry( pMan->pDesign->vModules, pNtk, i ) + { + // go through all the boxes of this module + Abc_NtkForEachBox( pNtk, pBox, k ) + { + if ( Abc_ObjIsLatch(pBox) ) + continue; + // skip internal boxes of the blackboxes + if ( pBox->pData == NULL ) + continue; + // if the network is undefined, it will be connected later + if ( !Ver_NtkIsDefined(pBox->pData) ) + { + RetValue = 2; + continue; + } + // connect the box + if ( !Ver_ParseConnectBox( pMan, pBox ) ) + return 0; + // if the network is a true blackbox, skip + if ( Abc_NtkHasBlackbox(pBox->pData) ) + continue; + // convert the box to the whitebox + Abc_ObjBlackboxToWhitebox( pBox ); + } + } + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Collects the undef boxes and maps them into their instances.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Ver_ParseCollectUndefBoxes( Ver_Man_t * pMan ) +{ + Vec_Ptr_t * vUndefs; + Abc_Ntk_t * pNtk, * pNtkBox; + Abc_Obj_t * pBox; + int i, k; + // clear the module structures + Vec_PtrForEachEntry( pMan->pDesign->vModules, pNtk, i ) + pNtk->pData = NULL; + // go through all the blackboxes + vUndefs = Vec_PtrAlloc( 16 ); + Vec_PtrForEachEntry( pMan->pDesign->vModules, pNtk, i ) + { + Abc_NtkForEachBlackbox( pNtk, pBox, k ) + { + pNtkBox = pBox->pData; + if ( pNtkBox == NULL ) + continue; + if ( Ver_NtkIsDefined(pNtkBox) ) + continue; + if ( pNtkBox->pData == NULL ) + { + // save the box + Vec_PtrPush( vUndefs, pNtkBox ); + pNtkBox->pData = Vec_PtrAlloc( 16 ); + } + // save the instance + Vec_PtrPush( pNtkBox->pData, pBox ); + } + } + return vUndefs; +} + +/**Function************************************************************* + + Synopsis [Reports how many times each type of undefined box occurs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ver_ParseReportUndefBoxes( Ver_Man_t * pMan ) +{ + Abc_Ntk_t * pNtk; + Abc_Obj_t * pBox; + int i, k, nBoxes; + // clean + nBoxes = 0; + Vec_PtrForEachEntry( pMan->pDesign->vModules, pNtk, i ) + { + pNtk->fHiePath = 0; + if ( !Ver_NtkIsDefined(pNtk) ) + nBoxes++; + } + // count + Vec_PtrForEachEntry( pMan->pDesign->vModules, pNtk, i ) + Abc_NtkForEachBlackbox( pNtk, pBox, k ) + if ( pBox->pData && !Ver_NtkIsDefined(pBox->pData) ) + ((Abc_Ntk_t *)pBox->pData)->fHiePath++; + // print the stats + printf( "Warning: The design contains %d undefined objects interpreted as blackboxes:\n", nBoxes ); + Vec_PtrForEachEntry( pMan->pDesign->vModules, pNtk, i ) + if ( !Ver_NtkIsDefined(pNtk) ) + printf( "%s (%d) ", Abc_NtkName(pNtk), pNtk->fHiePath ); + printf( "\n" ); + // clean + Vec_PtrForEachEntry( pMan->pDesign->vModules, pNtk, i ) + pNtk->fHiePath = 0; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if there are non-driven nets.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseCheckNondrivenNets( Vec_Ptr_t * vUndefs ) +{ + Abc_Ntk_t * pNtk; + Ver_Bundle_t * pBundle; + Abc_Obj_t * pBox, * pNet; + int i, k, j, m; + // go through undef box types + Vec_PtrForEachEntry( vUndefs, pNtk, i ) + // go through instances of this type + Vec_PtrForEachEntry( pNtk->pData, pBox, k ) + // go through the bundles of this instance + Vec_PtrForEachEntryReverse( (Vec_Ptr_t *)pBox->pCopy, pBundle, j ) + // go through the actual nets of this bundle + if ( pBundle ) + Vec_PtrForEachEntry( pBundle->vNetsActual, pNet, m ) + { + char * pName = Abc_ObjName(pNet); + if ( Abc_ObjFaninNum(pNet) == 0 ) // non-driven + if ( strcmp(Abc_ObjName(pNet), "1\'b0") && strcmp(Abc_ObjName(pNet), "1\'b1") ) // diff from a const + return 1; + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Checks if formal nets with the given name are driven in any of the instances of undef boxes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseFormalNetsAreDriven( Abc_Ntk_t * pNtk, char * pNameFormal ) +{ + Ver_Bundle_t * pBundle; + Abc_Obj_t * pBox, * pNet; + int k, j, m; + // go through instances of this type + Vec_PtrForEachEntry( pNtk->pData, pBox, k ) + { + // find a bundle with the given name in this instance + Vec_PtrForEachEntryReverse( (Vec_Ptr_t *)pBox->pCopy, pBundle, j ) + if ( pBundle && !strcmp( pBundle->pNameFormal, pNameFormal ) ) + break; + // skip non-driven bundles + if ( j == Vec_PtrSize((Vec_Ptr_t *)pBox->pCopy) ) + continue; + // check if all nets are driven in this bundle + Vec_PtrForEachEntry( pBundle->vNetsActual, pNet, m ) + if ( Abc_ObjFaninNum(pNet) > 0 ) + return 1; + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Returns the non-driven bundle that is given distance from the end.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ver_Bundle_t * Ver_ParseGetNondrivenBundle( Abc_Ntk_t * pNtk, int Counter ) +{ + Ver_Bundle_t * pBundle; + Abc_Obj_t * pBox, * pNet; + int k, m; + // go through instances of this type + Vec_PtrForEachEntry( pNtk->pData, pBox, k ) + { + if ( Counter >= Vec_PtrSize((Vec_Ptr_t *)pBox->pCopy) ) + continue; + // get the bundle given distance away + pBundle = Vec_PtrEntry( (Vec_Ptr_t *)pBox->pCopy, Vec_PtrSize((Vec_Ptr_t *)pBox->pCopy) - 1 - Counter ); + if ( pBundle == NULL ) + continue; + // go through the actual nets of this bundle + Vec_PtrForEachEntry( pBundle->vNetsActual, pNet, m ) + if ( !Abc_ObjFaninNum(pNet) && !Ver_ParseFormalNetsAreDriven(pNtk, pBundle->pNameFormal) ) // non-driven + return pBundle; + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [Drives the bundle in the given undef box.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseDriveFormal( Ver_Man_t * pMan, Abc_Ntk_t * pNtk, Ver_Bundle_t * pBundle0 ) +{ + char Buffer[200]; + char * pName; + Ver_Bundle_t * pBundle; + Abc_Obj_t * pBox, * pTerm, * pTermNew, * pNetAct, * pNetFormal; + int k, j, m; + + // drive this net in the undef box + Vec_PtrForEachEntry( pBundle0->vNetsActual, pNetAct, m ) + { + // create the formal net + if ( Vec_PtrSize(pBundle0->vNetsActual) == 1 ) + sprintf( Buffer, "%s", pBundle0->pNameFormal ); + else + sprintf( Buffer, "%s[%d]", pBundle0->pNameFormal, m ); + assert( Abc_NtkFindNet( pNtk, Buffer ) == NULL ); + pNetFormal = Abc_NtkFindOrCreateNet( pNtk, Buffer ); + // connect it to the box + pTerm = Abc_NtkCreateBo( pNtk ); + assert( Abc_NtkBoxNum(pNtk) <= 1 ); + pBox = Abc_NtkBoxNum(pNtk)? Abc_NtkBox(pNtk,0) : Abc_NtkCreateBlackbox(pNtk); + Abc_ObjAddFanin( Abc_NtkCreatePo(pNtk), pNetFormal ); + Abc_ObjAddFanin( pNetFormal, pTerm ); + Abc_ObjAddFanin( pTerm, pBox ); + } + + // go through instances of this type + pName = Extra_UtilStrsav(pBundle0->pNameFormal); + Vec_PtrForEachEntry( pNtk->pData, pBox, k ) + { + // find a bundle with the given name in this instance + Vec_PtrForEachEntryReverse( (Vec_Ptr_t *)pBox->pCopy, pBundle, j ) + if ( pBundle && !strcmp( pBundle->pNameFormal, pName ) ) + break; + // skip non-driven bundles + if ( j == Vec_PtrSize((Vec_Ptr_t *)pBox->pCopy) ) + continue; + // check if any nets are driven in this bundle + Vec_PtrForEachEntry( pBundle->vNetsActual, pNetAct, m ) + if ( Abc_ObjFaninNum(pNetAct) > 0 ) + { + sprintf( pMan->sError, "Internal error while trying to connect undefined boxes. It is likely that the algorithm currently used has its limitations." ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + // drive the nets by the undef box + Vec_PtrForEachEntryReverse( pBundle->vNetsActual, pNetAct, m ) + { + pTermNew = Abc_NtkCreateBo( pNetAct->pNtk ); + Abc_ObjAddFanin( pTermNew, pBox ); + Abc_ObjAddFanin( pNetAct, pTermNew ); + } + // remove the bundle + Ver_ParseFreeBundle( pBundle ); + Vec_PtrWriteEntry( (Vec_Ptr_t *)pBox->pCopy, j, NULL ); + } + free( pName ); + return 1; +} + + +/**Function************************************************************* + + Synopsis [Drives the bundle in the given undef box.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseDriveInputs( Ver_Man_t * pMan, Vec_Ptr_t * vUndefs ) +{ + char Buffer[200]; + Ver_Bundle_t * pBundle; + Abc_Ntk_t * pNtk; + Abc_Obj_t * pBox, * pBox2, * pTerm, * pTermNew, * pNetFormal, * pNetAct; + int i, k, j, m, CountCur, CountTotal = -1; + // iterate through the undef boxes + Vec_PtrForEachEntry( vUndefs, pNtk, i ) + { + // count the number of unconnected bundles for instances of this type of box + CountTotal = -1; + Vec_PtrForEachEntry( pNtk->pData, pBox, k ) + { + CountCur = 0; + Vec_PtrForEachEntry( (Vec_Ptr_t *)pBox->pCopy, pBundle, j ) + CountCur += (pBundle != NULL); + if ( CountTotal == -1 ) + CountTotal = CountCur; + else if ( CountTotal != CountCur ) + { + sprintf( pMan->sError, "The number of formal inputs (%d) is different from the expected one (%d) when instantiating network %s in box %s.", + CountCur, CountTotal, pNtk->pName, Abc_ObjName(pBox) ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; + } + } + + // create formals + pBox = Vec_PtrEntry( pNtk->pData, 0 ); + Vec_PtrForEachEntry( (Vec_Ptr_t *)pBox->pCopy, pBundle, j ) + { + if ( pBundle == NULL ) + continue; + Vec_PtrForEachEntry( pBundle->vNetsActual, pNetAct, m ) + { + // find create the formal net + if ( Vec_PtrSize(pBundle->vNetsActual) == 1 ) + sprintf( Buffer, "%s", pBundle->pNameFormal ); + else + sprintf( Buffer, "%s[%d]", pBundle->pNameFormal, m ); + assert( Abc_NtkFindNet( pNtk, Buffer ) == NULL ); + pNetFormal = Abc_NtkFindOrCreateNet( pNtk, Buffer ); + // connect + pTerm = Abc_NtkCreateBi( pNtk ); + assert( Abc_NtkBoxNum(pNtk) <= 1 ); + pBox2 = Abc_NtkBoxNum(pNtk)? Abc_NtkBox(pNtk,0) : Abc_NtkCreateBlackbox(pNtk); + Abc_ObjAddFanin( pNetFormal, Abc_NtkCreatePi(pNtk) ); + Abc_ObjAddFanin( pTerm, pNetFormal ); + Abc_ObjAddFanin( pBox2, pTerm ); + } + } + + // go through all the boxes + Vec_PtrForEachEntry( pNtk->pData, pBox, k ) + { + // go through all the bundles + Vec_PtrForEachEntry( (Vec_Ptr_t *)pBox->pCopy, pBundle, j ) + { + if ( pBundle == NULL ) + continue; + // drive the nets by the undef box + Vec_PtrForEachEntryReverse( pBundle->vNetsActual, pNetAct, m ) + { + pTermNew = Abc_NtkCreateBi( pNetAct->pNtk ); + Abc_ObjAddFanin( pBox, pTermNew ); + Abc_ObjAddFanin( pTermNew, pNetAct ); + } + // remove the bundle + Ver_ParseFreeBundle( pBundle ); + Vec_PtrWriteEntry( (Vec_Ptr_t *)pBox->pCopy, j, NULL ); + } + + // free the bundles + Vec_PtrFree( (Vec_Ptr_t *)pBox->pCopy ); + pBox->pCopy = NULL; + } + } + return 1; +} + + +/**Function************************************************************* + + Synopsis [Returns the max size of any undef box.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseMaxBoxSize( Vec_Ptr_t * vUndefs ) +{ + Abc_Ntk_t * pNtk; + Abc_Obj_t * pBox; + int i, k, nMaxSize = 0; + // go through undef box types + Vec_PtrForEachEntry( vUndefs, pNtk, i ) + // go through instances of this type + Vec_PtrForEachEntry( pNtk->pData, pBox, k ) + // check the number of bundles of this instance + if ( nMaxSize < Vec_PtrSize((Vec_Ptr_t *)pBox->pCopy) ) + nMaxSize = Vec_PtrSize((Vec_Ptr_t *)pBox->pCopy); + return nMaxSize; +} + +/**Function************************************************************* + + Synopsis [Prints the comprehensive report into a log file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ver_ParsePrintLog( Ver_Man_t * pMan ) +{ + Abc_Ntk_t * pNtk, * pNtkBox; + Abc_Obj_t * pBox; + FILE * pFile; + char * pNameGeneric; + char Buffer[1000]; + int i, k; + + // open the log file + pNameGeneric = Extra_FileNameGeneric( pMan->pFileName ); + sprintf( Buffer, "%s.log", pNameGeneric ); + free( pNameGeneric ); + pFile = fopen( Buffer, "w" ); + + // count the total number of instances and how many times they occur + Vec_PtrForEachEntry( pMan->pDesign->vModules, pNtk, i ) + pNtk->fHieVisited = 0; + Vec_PtrForEachEntry( pMan->pDesign->vModules, pNtk, i ) + Abc_NtkForEachBox( pNtk, pBox, k ) + { + if ( Abc_ObjIsLatch(pBox) ) + continue; + pNtkBox = pBox->pData; + if ( pNtkBox == NULL ) + continue; + pNtkBox->fHieVisited++; + } + // print each box and its stats + fprintf( pFile, "The hierarhical design %s contains %d modules:\n", pMan->pFileName, Vec_PtrSize(pMan->pDesign->vModules) ); + Vec_PtrForEachEntry( pMan->pDesign->vModules, pNtk, i ) + { + fprintf( pFile, "%-24s : ", Abc_NtkName(pNtk) ); + if ( !Ver_NtkIsDefined(pNtk) ) + fprintf( pFile, "undefbox" ); + else if ( Abc_NtkHasBlackbox(pNtk) ) + fprintf( pFile, "blackbox" ); + else + fprintf( pFile, "logicbox" ); + fprintf( pFile, " instantiated %6d times ", pNtk->fHieVisited ); +// fprintf( pFile, "\n " ); + fprintf( pFile, " pi = %4d", Abc_NtkPiNum(pNtk) ); + fprintf( pFile, " po = %4d", Abc_NtkPiNum(pNtk) ); + fprintf( pFile, " nd = %8d", Abc_NtkNodeNum(pNtk) ); + fprintf( pFile, " lat = %6d", Abc_NtkLatchNum(pNtk) ); + fprintf( pFile, " box = %6d", Abc_NtkBoxNum(pNtk)-Abc_NtkLatchNum(pNtk) ); + fprintf( pFile, "\n" ); + } + Vec_PtrForEachEntry( pMan->pDesign->vModules, pNtk, i ) + pNtk->fHieVisited = 0; + + // report instances with dangling outputs + if ( Vec_PtrSize(pMan->pDesign->vModules) > 1 ) + { + Vec_Ptr_t * vBundles; + Ver_Bundle_t * pBundle; + int j, nActNets, Counter = 0, CounterBoxes = 0; + // count the number of instances with dangling outputs + Vec_PtrForEachEntry( pMan->pDesign->vModules, pNtk, i ) + { + Abc_NtkForEachBox( pNtk, pBox, k ) + { + if ( Abc_ObjIsLatch(pBox) ) + continue; + vBundles = (Vec_Ptr_t *)pBox->pCopy; + pNtkBox = pBox->pData; + if ( pNtkBox == NULL ) + continue; + if ( !Ver_NtkIsDefined(pNtkBox) ) + continue; + // count the number of actual nets + nActNets = 0; + Vec_PtrForEachEntry( vBundles, pBundle, j ) + nActNets += Vec_PtrSize(pBundle->vNetsActual); + // the box is defined and will be connected + if ( nActNets != Abc_NtkPiNum(pNtkBox) + Abc_NtkPoNum(pNtkBox) ) + Counter++; + } + } + if ( Counter == 0 ) + fprintf( pFile, "The outputs of all box instances are connected.\n" ); + else + { + fprintf( pFile, "\n" ); + fprintf( pFile, "The outputs of %d box instances are not connected:\n", Counter ); + // enumerate through the boxes + Vec_PtrForEachEntry( pMan->pDesign->vModules, pNtk, i ) + { + Abc_NtkForEachBox( pNtk, pBox, k ) + { + if ( Abc_ObjIsLatch(pBox) ) + continue; + vBundles = (Vec_Ptr_t *)pBox->pCopy; + pNtkBox = pBox->pData; + if ( pNtkBox == NULL ) + continue; + if ( !Ver_NtkIsDefined(pNtkBox) ) + continue; + // count the number of actual nets + nActNets = 0; + Vec_PtrForEachEntry( vBundles, pBundle, j ) + nActNets += Vec_PtrSize(pBundle->vNetsActual); + // the box is defined and will be connected + if ( nActNets != Abc_NtkPiNum(pNtkBox) + Abc_NtkPoNum(pNtkBox) ) + fprintf( pFile, "In module \"%s\" instance \"%s\" of box \"%s\" has different numbers of actual/formal nets (%d/%d).\n", + Abc_NtkName(pNtk), Abc_ObjName(pBox), Abc_NtkName(pNtkBox), nActNets, Abc_NtkPiNum(pNtkBox) + Abc_NtkPoNum(pNtkBox) ); + } + } + } + } + fclose( pFile ); + printf( "Hierarchy statistics can be found in log file \"%s\".\n", Buffer ); +} + + +/**Function************************************************************* + + Synopsis [Attaches the boxes to the network.] + + Description [This procedure is called after the design is parsed. + At that point, all the defined models have their PIs present. + They are connected first. Next undef boxes are processed (if present). + Iteratively, one bundle is selected to be driven by the undef boxes in such + a way that there is no conflict (if it is driven by an instance of the box, + no other net will be driven twice by the same formal net of some other instance + of the same box). In the end, all the remaining nets that cannot be driven + by the undef boxes are connected to the undef boxes as inputs.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseAttachBoxes( Ver_Man_t * pMan ) +{ + Abc_Ntk_t * pNtk; + Ver_Bundle_t * pBundle; + Vec_Ptr_t * vUndefs; + int i, RetValue, Counter, nMaxBoxSize; + + // print the log file + Ver_ParsePrintLog( pMan ); + + // connect defined boxes + RetValue = Ver_ParseConnectDefBoxes( pMan ); + if ( RetValue < 2 ) + return RetValue; + + // report the boxes + Ver_ParseReportUndefBoxes( pMan ); + + // collect undef box types and their actual instances + vUndefs = Ver_ParseCollectUndefBoxes( pMan ); + assert( Vec_PtrSize( vUndefs ) > 0 ); + + // go through all undef box types + Counter = 0; + nMaxBoxSize = Ver_ParseMaxBoxSize( vUndefs ); + while ( Ver_ParseCheckNondrivenNets(vUndefs) && Counter < nMaxBoxSize ) + { + // go through undef box types + pBundle = NULL; + Vec_PtrForEachEntry( vUndefs, pNtk, i ) + if ( pBundle = Ver_ParseGetNondrivenBundle( pNtk, Counter ) ) + break; + if ( pBundle == NULL ) + { + Counter++; + continue; + } + // drive this bundle by this box + if ( !Ver_ParseDriveFormal( pMan, pNtk, pBundle ) ) + return 0; + } + + // make all the remaining bundles the drivers of undefs + if ( !Ver_ParseDriveInputs( pMan, vUndefs ) ) + return 0; + + // cleanup + Vec_PtrForEachEntry( vUndefs, pNtk, i ) + { + Vec_PtrFree( pNtk->pData ); + pNtk->pData = NULL; + } + Vec_PtrFree( vUndefs ); + return 1; +} + + +/**Function************************************************************* + + Synopsis [Creates PI terminal and net.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Ver_ParseCreatePi( Abc_Ntk_t * pNtk, char * pName ) +{ + Abc_Obj_t * pNet, * pTerm; + // get the PI net +// pNet = Ver_ParseFindNet( pNtk, pName ); +// if ( pNet ) +// printf( "Warning: PI \"%s\" appears twice in the list.\n", pName ); + pNet = Abc_NtkFindOrCreateNet( pNtk, pName ); + // add the PI node + pTerm = Abc_NtkCreatePi( pNtk ); + Abc_ObjAddFanin( pNet, pTerm ); + return pTerm; +} + +/**Function************************************************************* + + Synopsis [Creates PO terminal and net.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Ver_ParseCreatePo( Abc_Ntk_t * pNtk, char * pName ) +{ + Abc_Obj_t * pNet, * pTerm; + // get the PO net +// pNet = Ver_ParseFindNet( pNtk, pName ); +// if ( pNet && Abc_ObjFaninNum(pNet) == 0 ) +// printf( "Warning: PO \"%s\" appears twice in the list.\n", pName ); + pNet = Abc_NtkFindOrCreateNet( pNtk, pName ); + // add the PO node + pTerm = Abc_NtkCreatePo( pNtk ); + Abc_ObjAddFanin( pTerm, pNet ); + return pTerm; +} + +/**Function************************************************************* + + Synopsis [Create a latch with the given input/output.] + + Description [By default, the latch value is a don't-care.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Ver_ParseCreateLatch( Abc_Ntk_t * pNtk, Abc_Obj_t * pNetLI, Abc_Obj_t * pNetLO ) +{ + Abc_Obj_t * pLatch, * pTerm; + // add the BO terminal + pTerm = Abc_NtkCreateBi( pNtk ); + Abc_ObjAddFanin( pTerm, pNetLI ); + // add the latch box + pLatch = Abc_NtkCreateLatch( pNtk ); + Abc_ObjAddFanin( pLatch, pTerm ); + // add the BI terminal + pTerm = Abc_NtkCreateBo( pNtk ); + Abc_ObjAddFanin( pTerm, pLatch ); + // get the LO net + Abc_ObjAddFanin( pNetLO, pTerm ); + // set latch name + Abc_ObjAssignName( pLatch, Abc_ObjName(pNetLO), "L" ); + Abc_LatchSetInitDc( pLatch ); + return pLatch; +} + +/**Function************************************************************* + + Synopsis [Creates inverter and returns its net.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Ver_ParseCreateInv( Abc_Ntk_t * pNtk, Abc_Obj_t * pNet ) +{ + Abc_Obj_t * pObj; + pObj = Abc_NtkCreateNodeInv( pNtk, pNet ); + pNet = Abc_NtkCreateNet( pNtk ); + Abc_ObjAddFanin( pNet, pObj ); + return pNet; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/ver/verCore.zip b/abc_with_bb_support/src/base/ver/verCore.zip new file mode 100644 index 0000000000000000000000000000000000000000..cdfcf5a45dce59a01e5cb27e99dce5faeacbfe7e GIT binary patch literal 14624 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 W1^@s60096205<>t0Mj@C0000K{#s-J literal 0 HcmV?d00001 diff --git a/abc_with_bb_support/src/base/ver/verFormula.c b/abc_with_bb_support/src/base/ver/verFormula.c new file mode 100644 index 000000000..2c18ac7d8 --- /dev/null +++ b/abc_with_bb_support/src/base/ver/verFormula.c @@ -0,0 +1,474 @@ +/**CFile**************************************************************** + + FileName [verFormula.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Verilog parser.] + + Synopsis [Formula parser to read Verilog assign statements.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - August 19, 2006.] + + Revision [$Id: verFormula.c,v 1.00 2006/08/19 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ver.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// the list of operation symbols to be used in expressions +#define VER_PARSE_SYM_OPEN '(' // opening paranthesis +#define VER_PARSE_SYM_CLOSE ')' // closing paranthesis +#define VER_PARSE_SYM_CONST0 '0' // constant 0 +#define VER_PARSE_SYM_CONST1 '1' // constant 1 +#define VER_PARSE_SYM_NEGBEF1 '!' // negation before the variable +#define VER_PARSE_SYM_NEGBEF2 '~' // negation before the variable +#define VER_PARSE_SYM_AND '&' // logic AND +#define VER_PARSE_SYM_OR '|' // logic OR +#define VER_PARSE_SYM_XOR '^' // logic XOR +#define VER_PARSE_SYM_MUX1 '?' // first symbol of MUX +#define VER_PARSE_SYM_MUX2 ':' // second symbol of MUX + +// the list of opcodes (also specifying operation precedence) +#define VER_PARSE_OPER_NEG 7 // negation (highest precedence) +#define VER_PARSE_OPER_AND 6 // logic AND +#define VER_PARSE_OPER_XOR 5 // logic EXOR (a'b | ab') +#define VER_PARSE_OPER_OR 4 // logic OR +#define VER_PARSE_OPER_EQU 3 // equvalence (a'b'| ab ) +#define VER_PARSE_OPER_MUX 2 // MUX(a,b,c) (ab | a'c ) +#define VER_PARSE_OPER_MARK 1 // OpStack token standing for an opening paranthesis + +// these are values of the internal Flag +#define VER_PARSE_FLAG_START 1 // after the opening parenthesis +#define VER_PARSE_FLAG_VAR 2 // after operation is received +#define VER_PARSE_FLAG_OPER 3 // after operation symbol is received +#define VER_PARSE_FLAG_ERROR 4 // when error is detected + +static Hop_Obj_t * Ver_FormulaParserTopOper( Hop_Man_t * pMan, Vec_Ptr_t * vStackFn, int Oper ); +static int Ver_FormulaParserFindVar( char * pString, Vec_Ptr_t * vNames ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Parser of the formula encountered in assign statements.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void * Ver_FormulaParser( char * pFormula, void * pMan, Vec_Ptr_t * vNames, Vec_Ptr_t * vStackFn, Vec_Int_t * vStackOp, char * pErrorMessage ) +{ + char * pTemp; + Hop_Obj_t * bFunc, * bTemp; + int nParans, Flag; + int Oper, Oper1, Oper2; + int v; + + // clear the stacks and the names + Vec_PtrClear( vNames ); + Vec_PtrClear( vStackFn ); + Vec_IntClear( vStackOp ); + + if ( !strcmp(pFormula, "0") || !strcmp(pFormula, "1\'b0") ) + return Hop_ManConst0(pMan); + if ( !strcmp(pFormula, "1") || !strcmp(pFormula, "1\'b1") ) + return Hop_ManConst1(pMan); + + // make sure that the number of opening and closing parantheses is the same + nParans = 0; + for ( pTemp = pFormula; *pTemp; pTemp++ ) + if ( *pTemp == '(' ) + nParans++; + else if ( *pTemp == ')' ) + nParans--; + if ( nParans != 0 ) + { + sprintf( pErrorMessage, "Parse_FormulaParser(): Different number of opening and closing parantheses ()." ); + return NULL; + } + + // add parantheses + pTemp = pFormula + strlen(pFormula) + 2; + *pTemp-- = 0; *pTemp = ')'; + while ( --pTemp != pFormula ) + *pTemp = *(pTemp - 1); + *pTemp = '('; + + // perform parsing + Flag = VER_PARSE_FLAG_START; + for ( pTemp = pFormula; *pTemp; pTemp++ ) + { + switch ( *pTemp ) + { + // skip all spaces, tabs, and end-of-lines + case ' ': + case '\t': + case '\r': + case '\n': + continue; +/* + // treat Constant 0 as a variable + case VER_PARSE_SYM_CONST0: + Vec_PtrPush( vStackFn, Hop_ManConst0(pMan) ); // Cudd_Ref( Hop_ManConst0(pMan) ); + if ( Flag == VER_PARSE_FLAG_VAR ) + { + sprintf( pErrorMessage, "Parse_FormulaParser(): No operation symbol before constant 0." ); + Flag = VER_PARSE_FLAG_ERROR; + break; + } + Flag = VER_PARSE_FLAG_VAR; + break; + + // the same for Constant 1 + case VER_PARSE_SYM_CONST1: + Vec_PtrPush( vStackFn, Hop_ManConst1(pMan) ); // Cudd_Ref( Hop_ManConst1(pMan) ); + if ( Flag == VER_PARSE_FLAG_VAR ) + { + sprintf( pErrorMessage, "Parse_FormulaParser(): No operation symbol before constant 1." ); + Flag = VER_PARSE_FLAG_ERROR; + break; + } + Flag = VER_PARSE_FLAG_VAR; + break; +*/ + case VER_PARSE_SYM_NEGBEF1: + case VER_PARSE_SYM_NEGBEF2: + if ( Flag == VER_PARSE_FLAG_VAR ) + {// if NEGBEF follows a variable, AND is assumed + sprintf( pErrorMessage, "Parse_FormulaParser(): Variable before negation." ); + Flag = VER_PARSE_FLAG_ERROR; + break; + } + Vec_IntPush( vStackOp, VER_PARSE_OPER_NEG ); + break; + + case VER_PARSE_SYM_AND: + case VER_PARSE_SYM_OR: + case VER_PARSE_SYM_XOR: + case VER_PARSE_SYM_MUX1: + case VER_PARSE_SYM_MUX2: + if ( Flag != VER_PARSE_FLAG_VAR ) + { + sprintf( pErrorMessage, "Parse_FormulaParser(): There is no variable before AND, EXOR, or OR." ); + Flag = VER_PARSE_FLAG_ERROR; + break; + } + if ( *pTemp == VER_PARSE_SYM_AND ) + Vec_IntPush( vStackOp, VER_PARSE_OPER_AND ); + else if ( *pTemp == VER_PARSE_SYM_OR ) + Vec_IntPush( vStackOp, VER_PARSE_OPER_OR ); + else if ( *pTemp == VER_PARSE_SYM_XOR ) + Vec_IntPush( vStackOp, VER_PARSE_OPER_XOR ); + else if ( *pTemp == VER_PARSE_SYM_MUX1 ) + Vec_IntPush( vStackOp, VER_PARSE_OPER_MUX ); +// else if ( *pTemp == VER_PARSE_SYM_MUX2 ) +// Vec_IntPush( vStackOp, VER_PARSE_OPER_MUX ); + Flag = VER_PARSE_FLAG_OPER; + break; + + case VER_PARSE_SYM_OPEN: + if ( Flag == VER_PARSE_FLAG_VAR ) + { + sprintf( pErrorMessage, "Parse_FormulaParser(): Variable before a paranthesis." ); + Flag = VER_PARSE_FLAG_ERROR; + break; + } + Vec_IntPush( vStackOp, VER_PARSE_OPER_MARK ); + // after an opening bracket, it feels like starting over again + Flag = VER_PARSE_FLAG_START; + break; + + case VER_PARSE_SYM_CLOSE: + if ( Vec_IntSize( vStackOp ) ) + { + while ( 1 ) + { + if ( !Vec_IntSize( vStackOp ) ) + { + sprintf( pErrorMessage, "Parse_FormulaParser(): There is no opening paranthesis\n" ); + Flag = VER_PARSE_FLAG_ERROR; + break; + } + Oper = Vec_IntPop( vStackOp ); + if ( Oper == VER_PARSE_OPER_MARK ) + break; + // skip the second MUX operation +// if ( Oper == VER_PARSE_OPER_MUX2 ) +// { +// Oper = Vec_IntPop( vStackOp ); +// assert( Oper == VER_PARSE_OPER_MUX1 ); +// } + + // perform the given operation + if ( Ver_FormulaParserTopOper( pMan, vStackFn, Oper ) == NULL ) + { + sprintf( pErrorMessage, "Parse_FormulaParser(): Unknown operation\n" ); + return NULL; + } + } + } + else + { + sprintf( pErrorMessage, "Parse_FormulaParser(): There is no opening paranthesis\n" ); + Flag = VER_PARSE_FLAG_ERROR; + break; + } + if ( Flag != VER_PARSE_FLAG_ERROR ) + Flag = VER_PARSE_FLAG_VAR; + break; + + + default: + // scan the next name + v = Ver_FormulaParserFindVar( pTemp, vNames ); + if ( *pTemp == '\\' ) + pTemp++; + pTemp += (int)Vec_PtrEntry( vNames, 2*v ) - 1; + + // assume operation AND, if vars follow one another + if ( Flag == VER_PARSE_FLAG_VAR ) + { + sprintf( pErrorMessage, "Parse_FormulaParser(): Incorrect state." ); + return NULL; + } + bTemp = Hop_IthVar( pMan, v ); + Vec_PtrPush( vStackFn, bTemp ); // Cudd_Ref( bTemp ); + Flag = VER_PARSE_FLAG_VAR; + break; + } + + if ( Flag == VER_PARSE_FLAG_ERROR ) + break; // error exit + else if ( Flag == VER_PARSE_FLAG_START ) + continue; // go on parsing + else if ( Flag == VER_PARSE_FLAG_VAR ) + while ( 1 ) + { // check if there are negations in the OpStack + if ( !Vec_IntSize(vStackOp) ) + break; + Oper = Vec_IntPop( vStackOp ); + if ( Oper != VER_PARSE_OPER_NEG ) + { + Vec_IntPush( vStackOp, Oper ); + break; + } + else + { +// Vec_PtrPush( vStackFn, Cudd_Not(Vec_PtrPop(vStackFn)) ); + Vec_PtrPush( vStackFn, Hop_Not(Vec_PtrPop(vStackFn)) ); + } + } + else // if ( Flag == VER_PARSE_FLAG_OPER ) + while ( 1 ) + { // execute all the operations in the OpStack + // with precedence higher or equal than the last one + Oper1 = Vec_IntPop( vStackOp ); // the last operation + if ( !Vec_IntSize(vStackOp) ) + { // if it is the only operation, push it back + Vec_IntPush( vStackOp, Oper1 ); + break; + } + Oper2 = Vec_IntPop( vStackOp ); // the operation before the last one + if ( Oper2 >= Oper1 && !(Oper1 == Oper2 && Oper1 == VER_PARSE_OPER_MUX) ) + { // if Oper2 precedence is higher or equal, execute it + if ( Ver_FormulaParserTopOper( pMan, vStackFn, Oper2 ) == NULL ) + { + sprintf( pErrorMessage, "Parse_FormulaParser(): Unknown operation\n" ); + return NULL; + } + Vec_IntPush( vStackOp, Oper1 ); // push the last operation back + } + else + { // if Oper2 precedence is lower, push them back and done + Vec_IntPush( vStackOp, Oper2 ); + Vec_IntPush( vStackOp, Oper1 ); + break; + } + } + } + + if ( Flag != VER_PARSE_FLAG_ERROR ) + { + if ( Vec_PtrSize(vStackFn) ) + { + bFunc = Vec_PtrPop(vStackFn); + if ( !Vec_PtrSize(vStackFn) ) + if ( !Vec_IntSize(vStackOp) ) + { +// Cudd_Deref( bFunc ); + return bFunc; + } + else + sprintf( pErrorMessage, "Parse_FormulaParser(): Something is left in the operation stack\n" ); + else + sprintf( pErrorMessage, "Parse_FormulaParser(): Something is left in the function stack\n" ); + } + else + sprintf( pErrorMessage, "Parse_FormulaParser(): The input string is empty\n" ); + } +// Cudd_Ref( bFunc ); +// Cudd_RecursiveDeref( dd, bFunc ); + return NULL; +} + +/**Function************************************************************* + + Synopsis [Performs the operation on the top entries in the stack.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Ver_FormulaParserTopOper( Hop_Man_t * pMan, Vec_Ptr_t * vStackFn, int Oper ) +{ + Hop_Obj_t * bArg0, * bArg1, * bArg2, * bFunc; + // perform the given operation + bArg2 = Vec_PtrPop( vStackFn ); + bArg1 = Vec_PtrPop( vStackFn ); + if ( Oper == VER_PARSE_OPER_AND ) + bFunc = Hop_And( pMan, bArg1, bArg2 ); + else if ( Oper == VER_PARSE_OPER_XOR ) + bFunc = Hop_Exor( pMan, bArg1, bArg2 ); + else if ( Oper == VER_PARSE_OPER_OR ) + bFunc = Hop_Or( pMan, bArg1, bArg2 ); + else if ( Oper == VER_PARSE_OPER_EQU ) + bFunc = Hop_Not( Hop_Exor( pMan, bArg1, bArg2 ) ); + else if ( Oper == VER_PARSE_OPER_MUX ) + { + bArg0 = Vec_PtrPop( vStackFn ); +// bFunc = Cudd_bddIte( dd, bArg0, bArg1, bArg2 ); Cudd_Ref( bFunc ); + bFunc = Hop_Mux( pMan, bArg0, bArg1, bArg2 ); +// Cudd_RecursiveDeref( dd, bArg0 ); +// Cudd_Deref( bFunc ); + } + else + return NULL; +// Cudd_Ref( bFunc ); +// Cudd_RecursiveDeref( dd, bArg1 ); +// Cudd_RecursiveDeref( dd, bArg2 ); + Vec_PtrPush( vStackFn, bFunc ); + return bFunc; +} + +/**Function************************************************************* + + Synopsis [Returns the index of the new variable found.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_FormulaParserFindVar( char * pString, Vec_Ptr_t * vNames ) +{ + char * pTemp, * pTemp2; + int nLength, nLength2, i; + // start the string + pTemp = pString; + // find the end of the string delimited by other characters + if ( *pTemp == '\\' ) + { + pString++; + while ( *pTemp && *pTemp != ' ' ) + pTemp++; + } + else + { + while ( *pTemp && *pTemp != ' ' && *pTemp != '\t' && *pTemp != '\r' && *pTemp != '\n' && *pTemp != ',' && *pTemp != '}' && + *pTemp != VER_PARSE_SYM_OPEN && *pTemp != VER_PARSE_SYM_CLOSE && + *pTemp != VER_PARSE_SYM_NEGBEF1 && *pTemp != VER_PARSE_SYM_NEGBEF2 && + *pTemp != VER_PARSE_SYM_AND && *pTemp != VER_PARSE_SYM_OR && *pTemp != VER_PARSE_SYM_XOR && + *pTemp != VER_PARSE_SYM_MUX1 && *pTemp != VER_PARSE_SYM_MUX2 ) + pTemp++; + } + // look for this string in the array + nLength = pTemp - pString; + for ( i = 0; i < Vec_PtrSize(vNames)/2; i++ ) + { + nLength2 = (int)Vec_PtrEntry( vNames, 2*i + 0 ); + if ( nLength2 != nLength ) + continue; + pTemp2 = Vec_PtrEntry( vNames, 2*i + 1 ); + if ( strncmp( pString, pTemp2, nLength ) ) + continue; + return i; + } + // could not find - add and return the number + Vec_PtrPush( vNames, (void *)nLength ); + Vec_PtrPush( vNames, pString ); + return i; +} + +/**Function************************************************************* + + Synopsis [Returns the AIG representation of the reduction formula.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void * Ver_FormulaReduction( char * pFormula, void * pMan, Vec_Ptr_t * vNames, char * pErrorMessage ) +{ + Hop_Obj_t * pRes; + int v, fCompl; + char Symbol; + + // get the operation + Symbol = *pFormula++; + fCompl = ( Symbol == '~' ); + if ( fCompl ) + Symbol = *pFormula++; + // check the operation + if ( Symbol != '&' && Symbol != '|' && Symbol != '^' ) + { + sprintf( pErrorMessage, "Ver_FormulaReduction(): Unknown operation (%c)\n", Symbol ); + return NULL; + } + // skip the brace + while ( *pFormula++ != '{' ); + // parse the names + Vec_PtrClear( vNames ); + while ( *pFormula != '}' ) + { + v = Ver_FormulaParserFindVar( pFormula, vNames ); + pFormula += (int)Vec_PtrEntry( vNames, 2*v ); + while ( *pFormula == ' ' || *pFormula == ',' ) + pFormula++; + } + // compute the function + if ( Symbol == '&' ) + pRes = Hop_CreateAnd( pMan, Vec_PtrSize(vNames)/2 ); + else if ( Symbol == '|' ) + pRes = Hop_CreateOr( pMan, Vec_PtrSize(vNames)/2 ); + else if ( Symbol == '^' ) + pRes = Hop_CreateExor( pMan, Vec_PtrSize(vNames)/2 ); + return Hop_NotCond( pRes, fCompl ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/ver/verParse.c b/abc_with_bb_support/src/base/ver/verParse.c new file mode 100644 index 000000000..6a268743b --- /dev/null +++ b/abc_with_bb_support/src/base/ver/verParse.c @@ -0,0 +1,117 @@ +/**CFile**************************************************************** + + FileName [verParse.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Verilog parser.] + + Synopsis [Performs some Verilog parsing tasks.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - August 19, 2006.] + + Revision [$Id: verParse.c,v 1.00 2006/08/19 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ver.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Skips the comments of they are present.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_ParseSkipComments( Ver_Man_t * pMan ) +{ + Ver_Stream_t * p = pMan->pReader; + char Symbol; + // skip spaces + Ver_StreamSkipChars( p, " \t\n\r" ); + if ( !Ver_StreamIsOkey(pMan->pReader) ) + return 1; + // read the first symbol + Symbol = Ver_StreamScanChar( p ); + if ( Symbol != '/' ) + return 1; + Ver_StreamPopChar( p ); + // read the second symbol + Symbol = Ver_StreamScanChar( p ); + if ( Symbol == '/' ) + { // skip till the end of line + Ver_StreamSkipToChars( p, "\n" ); + return Ver_ParseSkipComments( pMan ); + } + if ( Symbol == '*' ) + { // skip till the next occurance of */ + Ver_StreamPopChar( p ); + do { + Ver_StreamSkipToChars( p, "*" ); + Ver_StreamPopChar( p ); + } while ( Ver_StreamScanChar( p ) != '/' ); + Ver_StreamPopChar( p ); + return Ver_ParseSkipComments( pMan ); + } + sprintf( pMan->sError, "Cannot parse after symbol \"/\"." ); + Ver_ParsePrintErrorMessage( pMan ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Parses a Verilog name that can be being with a slash.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Ver_ParseGetName( Ver_Man_t * pMan ) +{ + Ver_Stream_t * p = pMan->pReader; + char Symbol; + char * pWord; + pMan->fNameLast = 0; + if ( !Ver_StreamIsOkey(p) ) + return NULL; + if ( !Ver_ParseSkipComments( pMan ) ) + return NULL; + Symbol = Ver_StreamScanChar( p ); + if ( Symbol == '\\' ) + { + pMan->fNameLast = 1; + Ver_StreamPopChar( p ); + pWord = Ver_StreamGetWord( p, " \r\n" ); + } + else + pWord = Ver_StreamGetWord( p, " \t\n\r(),;" ); + if ( !Ver_ParseSkipComments( pMan ) ) + return NULL; + return pWord; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/ver/verStream.c b/abc_with_bb_support/src/base/ver/verStream.c new file mode 100644 index 000000000..b57b010e8 --- /dev/null +++ b/abc_with_bb_support/src/base/ver/verStream.c @@ -0,0 +1,440 @@ +/**CFile**************************************************************** + + FileName [verStream.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Verilog parser.] + + Synopsis [Input file stream, which knows nothing about Verilog.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - August 19, 2006.] + + Revision [$Id: verStream.c,v 1.00 2006/08/19 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ver.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define VER_BUFFER_SIZE 1048576 // 1M - size of the data chunk stored in memory +#define VER_OFFSET_SIZE 4096 // 4K - load new data when less than this is left +#define VER_WORD_SIZE 4096 // 4K - the largest token that can be returned + +#define VER_MINIMUM(a,b) (((a) < (b))? (a) : (b)) + +struct Ver_Stream_t_ +{ + // the input file + char * pFileName; // the input file name + FILE * pFile; // the input file pointer + int nFileSize; // the total number of bytes in the file + int nFileRead; // the number of bytes currently read from file + int nLineCounter; // the counter of lines processed + // temporary storage for data + char * pBuffer; // the buffer + int nBufferSize; // the size of the buffer + char * pBufferCur; // the current reading position + char * pBufferEnd; // the first position not used by currently loaded data + char * pBufferStop; // the position where loading new data will be done + // tokens given to the user + char pChars[VER_WORD_SIZE+5]; // temporary storage for a word (plus end-of-string and two parantheses) + int nChars; // the total number of characters in the word + // status of the parser + int fStop; // this flag goes high when the end of file is reached +}; + +static void Ver_StreamReload( Ver_Stream_t * p ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the file reader for the given file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ver_Stream_t * Ver_StreamAlloc( char * pFileName ) +{ + Ver_Stream_t * p; + FILE * pFile; + int nCharsToRead; + // check if the file can be opened + pFile = fopen( pFileName, "rb" ); + if ( pFile == NULL ) + { + printf( "Ver_StreamAlloc(): Cannot open input file \"%s\".\n", pFileName ); + return NULL; + } + // start the file reader + p = ALLOC( Ver_Stream_t, 1 ); + memset( p, 0, sizeof(Ver_Stream_t) ); + p->pFileName = pFileName; + p->pFile = pFile; + // get the file size, in bytes + fseek( pFile, 0, SEEK_END ); + p->nFileSize = ftell( pFile ); + rewind( pFile ); + // allocate the buffer + p->pBuffer = ALLOC( char, VER_BUFFER_SIZE+1 ); + p->nBufferSize = VER_BUFFER_SIZE; + p->pBufferCur = p->pBuffer; + // determine how many chars to read + nCharsToRead = VER_MINIMUM(p->nFileSize, VER_BUFFER_SIZE); + // load the first part into the buffer + fread( p->pBuffer, nCharsToRead, 1, p->pFile ); + p->nFileRead = nCharsToRead; + // set the ponters to the end and the stopping point + p->pBufferEnd = p->pBuffer + nCharsToRead; + p->pBufferStop = (p->nFileRead == p->nFileSize)? p->pBufferEnd : p->pBuffer + VER_BUFFER_SIZE - VER_OFFSET_SIZE; + // start the arrays + p->nLineCounter = 1; // 1-based line counting + return p; +} + +/**Function************************************************************* + + Synopsis [Loads new data into the file reader.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ver_StreamReload( Ver_Stream_t * p ) +{ + int nCharsUsed, nCharsToRead; + assert( !p->fStop ); + assert( p->pBufferCur > p->pBufferStop ); + assert( p->pBufferCur < p->pBufferEnd ); + // figure out how many chars are still not processed + nCharsUsed = p->pBufferEnd - p->pBufferCur; + // move the remaining data to the beginning of the buffer + memmove( p->pBuffer, p->pBufferCur, nCharsUsed ); + p->pBufferCur = p->pBuffer; + // determine how many chars we will read + nCharsToRead = VER_MINIMUM( p->nBufferSize - nCharsUsed, p->nFileSize - p->nFileRead ); + // read the chars + fread( p->pBuffer + nCharsUsed, nCharsToRead, 1, p->pFile ); + p->nFileRead += nCharsToRead; + // set the ponters to the end and the stopping point + p->pBufferEnd = p->pBuffer + nCharsUsed + nCharsToRead; + p->pBufferStop = (p->nFileRead == p->nFileSize)? p->pBufferEnd : p->pBuffer + VER_BUFFER_SIZE - VER_OFFSET_SIZE; +} + +/**Function************************************************************* + + Synopsis [Stops the file reader.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ver_StreamFree( Ver_Stream_t * p ) +{ + if ( p->pFile ) + fclose( p->pFile ); + FREE( p->pBuffer ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Returns the file size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Ver_StreamGetFileName( Ver_Stream_t * p ) +{ + return p->pFileName; +} + +/**Function************************************************************* + + Synopsis [Returns the file size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_StreamGetFileSize( Ver_Stream_t * p ) +{ + return p->nFileSize; +} + +/**Function************************************************************* + + Synopsis [Returns the current reading position.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_StreamGetCurPosition( Ver_Stream_t * p ) +{ + return p->nFileRead - (p->pBufferEnd - p->pBufferCur); +} + +/**Function************************************************************* + + Synopsis [Returns the line number for the given token.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_StreamGetLineNumber( Ver_Stream_t * p ) +{ + return p->nLineCounter; +} + + + +/**Function************************************************************* + + Synopsis [Returns current symbol.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Ver_StreamIsOkey( Ver_Stream_t * p ) +{ + return !p->fStop; +} + +/**Function************************************************************* + + Synopsis [Returns current symbol.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char Ver_StreamScanChar( Ver_Stream_t * p ) +{ + assert( !p->fStop ); + return *p->pBufferCur; +} + +/**Function************************************************************* + + Synopsis [Returns current symbol and moves to the next.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char Ver_StreamPopChar( Ver_Stream_t * p ) +{ + assert( !p->fStop ); + // check if the new data should to be loaded + if ( p->pBufferCur > p->pBufferStop ) + Ver_StreamReload( p ); + // check if there are symbols left + if ( p->pBufferCur == p->pBufferEnd ) // end of file + { + p->fStop = 1; + return -1; + } + // count the lines + if ( *p->pBufferCur == '\n' ) + p->nLineCounter++; + return *p->pBufferCur++; +} + +/**Function************************************************************* + + Synopsis [Skips the current symbol and all symbols from the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ver_StreamSkipChars( Ver_Stream_t * p, char * pCharsToSkip ) +{ + char * pChar, * pTemp; + assert( !p->fStop ); + assert( pCharsToSkip != NULL ); + // check if the new data should to be loaded + if ( p->pBufferCur > p->pBufferStop ) + Ver_StreamReload( p ); + // skip the symbols + for ( pChar = p->pBufferCur; pChar < p->pBufferEnd; pChar++ ) + { + // skip symbols as long as they are in the list + for ( pTemp = pCharsToSkip; *pTemp; pTemp++ ) + if ( *pChar == *pTemp ) + break; + if ( *pTemp == 0 ) // pChar is not found in the list + { + p->pBufferCur = pChar; + return; + } + // count the lines + if ( *pChar == '\n' ) + p->nLineCounter++; + } + // the file is finished or the last part continued + // through VER_OFFSET_SIZE chars till the end of the buffer + if ( p->pBufferStop == p->pBufferEnd ) // end of file + { + p->fStop = 1; + return; + } + printf( "Ver_StreamSkipSymbol() failed to parse the file \"%s\".\n", p->pFileName ); +} + +/**Function************************************************************* + + Synopsis [Skips all symbols until encountering one from the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Ver_StreamSkipToChars( Ver_Stream_t * p, char * pCharsToStop ) +{ + char * pChar, * pTemp; + assert( !p->fStop ); + assert( pCharsToStop != NULL ); + // check if the new data should to be loaded + if ( p->pBufferCur > p->pBufferStop ) + Ver_StreamReload( p ); + // skip the symbols + for ( pChar = p->pBufferCur; pChar < p->pBufferEnd; pChar++ ) + { + // skip symbols as long as they are NOT in the list + for ( pTemp = pCharsToStop; *pTemp; pTemp++ ) + if ( *pChar == *pTemp ) + break; + if ( *pTemp == 0 ) // pChar is not found in the list + { + // count the lines + if ( *pChar == '\n' ) + p->nLineCounter++; + continue; + } + // the symbol is found - move position and return + p->pBufferCur = pChar; + return; + } + // the file is finished or the last part continued + // through VER_OFFSET_SIZE chars till the end of the buffer + if ( p->pBufferStop == p->pBufferEnd ) // end of file + { + p->fStop = 1; + return; + } + printf( "Ver_StreamSkipToSymbol() failed to parse the file \"%s\".\n", p->pFileName ); +} + +/**Function************************************************************* + + Synopsis [Returns current word delimited by the set of symbols.] + + Description [Modifies the stream by inserting 0 at the first encounter + of one of the symbols in the list.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Ver_StreamGetWord( Ver_Stream_t * p, char * pCharsToStop ) +{ + char * pChar, * pTemp; + if ( p->fStop ) + return NULL; + assert( pCharsToStop != NULL ); + // check if the new data should to be loaded + if ( p->pBufferCur > p->pBufferStop ) + Ver_StreamReload( p ); + // skip the symbols + p->nChars = 0; + for ( pChar = p->pBufferCur; pChar < p->pBufferEnd; pChar++ ) + { + // skip symbols as long as they are NOT in the list + for ( pTemp = pCharsToStop; *pTemp; pTemp++ ) + if ( *pChar == *pTemp ) + break; + if ( *pTemp == 0 ) // pChar is not found in the list + { + p->pChars[p->nChars++] = *pChar; + if ( p->nChars == VER_WORD_SIZE ) + return NULL; + // count the lines + if ( *pChar == '\n' ) + p->nLineCounter++; + continue; + } + // the symbol is found - move the position, set the word end, return the word + p->pBufferCur = pChar; + p->pChars[p->nChars] = 0; + return p->pChars; + } + // the file is finished or the last part continued + // through VER_OFFSET_SIZE chars till the end of the buffer + if ( p->pBufferStop == p->pBufferEnd ) // end of file + { + p->fStop = 1; + p->pChars[p->nChars] = 0; + return p->pChars; + } + printf( "Ver_StreamGetWord() failed to parse the file \"%s\".\n", p->pFileName ); + return NULL; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/ver/verWords.c b/abc_with_bb_support/src/base/ver/verWords.c new file mode 100644 index 000000000..7fdef6c4c --- /dev/null +++ b/abc_with_bb_support/src/base/ver/verWords.c @@ -0,0 +1,48 @@ +/**CFile**************************************************************** + + FileName [verWords.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Verilog parser.] + + Synopsis [Handles keywords that are currently supported.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - August 19, 2006.] + + Revision [$Id: verWords.c,v 1.00 2006/08/19 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ver.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/base/ver/ver_.c b/abc_with_bb_support/src/base/ver/ver_.c new file mode 100644 index 000000000..8bdbe033d --- /dev/null +++ b/abc_with_bb_support/src/base/ver/ver_.c @@ -0,0 +1,48 @@ +/**CFile**************************************************************** + + FileName [ver_.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Verilog parser.] + + Synopsis [Parses several flavors of structural Verilog.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - August 19, 2006.] + + Revision [$Id: ver_.c,v 1.00 2006/08/19 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ver.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/bdd/cas/cas.h b/abc_with_bb_support/src/bdd/cas/cas.h new file mode 100644 index 000000000..a9cf8b80e --- /dev/null +++ b/abc_with_bb_support/src/bdd/cas/cas.h @@ -0,0 +1,62 @@ +/**CFile**************************************************************** + + FileName [cas.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [CASCADE: Decomposition of shared BDDs into a LUT cascade.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cas.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __CAS_H__ +#define __CAS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +#define MAXINPUTS 1024 +#define MAXOUTPUTS 1024 + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== zzz.c ==========================================================*/ + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/bdd/cas/casCore.c b/abc_with_bb_support/src/bdd/cas/casCore.c new file mode 100644 index 000000000..74db7a243 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cas/casCore.c @@ -0,0 +1,1263 @@ +/**CFile**************************************************************** + + FileName [casCore.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [CASCADE: Decomposition of shared BDDs into a LUT cascade.] + + Synopsis [Entrance into the implementation.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Spring 2002.] + + Revision [$Id: casCore.c,v 1.0 2002/01/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include +#include +#include +#include + +#include "extra.h" +#include "cas.h" + +//////////////////////////////////////////////////////////////////////// +/// static functions /// +//////////////////////////////////////////////////////////////////////// + +DdNode * GetSingleOutputFunction( DdManager * dd, DdNode ** pbOuts, int nOuts, DdNode ** pbVarsEnc, int nVarsEnc, int fVerbose ); +DdNode * GetSingleOutputFunctionRemapped( DdManager * dd, DdNode ** pOutputs, int nOuts, DdNode ** pbVarsEnc, int nVarsEnc ); +DdNode * GetSingleOutputFunctionRemappedNewDD( DdManager * dd, DdNode ** pOutputs, int nOuts, DdManager ** DdNew ); + +extern int CreateDecomposedNetwork( DdManager * dd, DdNode * aFunc, char ** pNames, int nNames, char * FileName, int nLutSize, int fCheck, int fVerbose ); + +void WriteSingleOutputFunctionBlif( DdManager * dd, DdNode * aFunc, char ** pNames, int nNames, char * FileName ); + +DdNode * Cudd_bddTransferPermute( DdManager * ddSource, DdManager * ddDestination, DdNode * f, int * Permute ); + +//////////////////////////////////////////////////////////////////////// +/// static varibles /// +//////////////////////////////////////////////////////////////////////// + +//static FILE * pTable = NULL; +//static long s_RemappingTime = 0; + +//////////////////////////////////////////////////////////////////////// +/// debugging macros /// +//////////////////////////////////////////////////////////////////////// + +#define PRD(p) printf( "\nDECOMPOSITION TREE:\n\n" ); PrintDecEntry( (p), 0 ) +#define PRB(f) printf( #f " = " ); Cudd_bddPrint(dd,f); printf( "\n" ) +#define PRK(f,n) Cudd_PrintKMap(stdout,dd,(f),Cudd_Not(f),(n),NULL,0); printf( "K-map for function" #f "\n\n" ) +#define PRK2(f,g,n) Cudd_PrintKMap(stdout,dd,(f),(g),(n),NULL,0); printf( "K-map for function <" #f ", " #g ">\n\n" ) + + +//////////////////////////////////////////////////////////////////////// +/// EXTERNAL FUNCTIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CascadeExperiment( char * pFileGeneric, DdManager * dd, DdNode ** pOutputs, int nInputs, int nOutputs, int nLutSize, int fCheck, int fVerbose ) +{ + int i; + int nVars = nInputs; + int nOuts = nOutputs; + long clk1; + + int nVarsEnc; // the number of additional variables to encode outputs + DdNode * pbVarsEnc[MAXOUTPUTS]; // the BDDs of the encoding vars + + int nNames; // the total number of all inputs + char * pNames[MAXINPUTS]; // the temporary storage for the input (and output encoding) names + + DdNode * aFunc; // the encoded 0-1 BDD containing all the outputs + + char FileNameIni[100]; + char FileNameFin[100]; + char Buffer[100]; + + +//pTable = fopen( "stats.txt", "a+" ); +//fprintf( pTable, "%s ", pFileGeneric ); +//fprintf( pTable, "%d ", nVars ); +//fprintf( pTable, "%d ", nOuts ); + + + // assign the file names + strcpy( FileNameIni, pFileGeneric ); + strcat( FileNameIni, "_ENC.blif" ); + + strcpy( FileNameFin, pFileGeneric ); + strcat( FileNameFin, "_LUT.blif" ); + + + // create the variables to encode the outputs + nVarsEnc = Extra_Base2Log( nOuts ); + for ( i = 0; i < nVarsEnc; i++ ) + pbVarsEnc[i] = Cudd_bddNewVarAtLevel( dd, i ); + + + // store the input names + nNames = nVars + nVarsEnc; + for ( i = 0; i < nVars; i++ ) + { +// pNames[i] = Extra_UtilStrsav( pFunc->pInputNames[i] ); + sprintf( Buffer, "pi%03d", i ); + pNames[i] = Extra_UtilStrsav( Buffer ); + } + // set the encoding variable name + for ( ; i < nNames; i++ ) + { + sprintf( Buffer, "OutEnc_%02d", i-nVars ); + pNames[i] = Extra_UtilStrsav( Buffer ); + } + + + // print the variable order +// printf( "\n" ); +// printf( "Variable order is: " ); +// for ( i = 0; i < dd->size; i++ ) +// printf( " %d", dd->invperm[i] ); +// printf( "\n" ); + + // derive the single-output function + clk1 = clock(); + aFunc = GetSingleOutputFunction( dd, pOutputs, nOuts, pbVarsEnc, nVarsEnc, fVerbose ); Cudd_Ref( aFunc ); +// aFunc = GetSingleOutputFunctionRemapped( dd, pOutputs, nOuts, pbVarsEnc, nVarsEnc ); Cudd_Ref( aFunc ); +// if ( fVerbose ) +// printf( "Single-output function computation time = %.2f sec\n", (float)(clock() - clk1)/(float)(CLOCKS_PER_SEC) ); + +//fprintf( pTable, "%d ", Cudd_SharingSize( pOutputs, nOutputs ) ); +//fprintf( pTable, "%d ", Extra_ProfileWidthSharingMax(dd, pOutputs, nOutputs) ); + + // dispose of the multiple-output function +// Extra_Dissolve( pFunc ); + + // reorder the single output function +// if ( fVerbose ) +// printf( "Reordering variables...\n"); + clk1 = clock(); +// if ( fVerbose ) +// printf( "Node count before = %6d\n", Cudd_DagSize( aFunc ) ); +// Cudd_ReduceHeap(dd, CUDD_REORDER_SIFT,1); + Cudd_ReduceHeap(dd, CUDD_REORDER_SYMM_SIFT,1); + Cudd_ReduceHeap(dd, CUDD_REORDER_SYMM_SIFT,1); +// Cudd_ReduceHeap(dd, CUDD_REORDER_SYMM_SIFT,1); +// Cudd_ReduceHeap(dd, CUDD_REORDER_SYMM_SIFT,1); +// Cudd_ReduceHeap(dd, CUDD_REORDER_SYMM_SIFT,1); +// Cudd_ReduceHeap(dd, CUDD_REORDER_SYMM_SIFT,1); + if ( fVerbose ) + printf( "MTBDD reordered = %6d nodes\n", Cudd_DagSize( aFunc ) ); + if ( fVerbose ) + printf( "Variable reordering time = %.2f sec\n", (float)(clock() - clk1)/(float)(CLOCKS_PER_SEC) ); +// printf( "\n" ); +// printf( "Variable order is: " ); +// for ( i = 0; i < dd->size; i++ ) +// printf( " %d", dd->invperm[i] ); +// printf( "\n" ); +//fprintf( pTable, "%d ", Cudd_DagSize( aFunc ) ); +//fprintf( pTable, "%d ", Extra_ProfileWidthMax(dd, aFunc) ); + + // write the single-output function into BLIF for verification + clk1 = clock(); + if ( fCheck ) + WriteSingleOutputFunctionBlif( dd, aFunc, pNames, nNames, FileNameIni ); +// if ( fVerbose ) +// printf( "Single-output function writing time = %.2f sec\n", (float)(clock() - clk1)/(float)(CLOCKS_PER_SEC) ); + +/* + /////////////////////////////////////////////////////////////////// + // verification of single output function + clk1 = clock(); + { + BFunc g_Func; + DdNode * aRes; + + g_Func.dd = dd; + g_Func.FileInput = Extra_UtilStrsav(FileNameIni); + + if ( Extra_ReadFile( &g_Func ) == 0 ) + { + printf( "\nSomething did not work out while reading the input file for verification\n"); + Extra_Dissolve( &g_Func ); + return; + } + + aRes = Cudd_BddToAdd( dd, g_Func.pOutputs[0] ); Cudd_Ref( aRes ); + + if ( aRes != aFunc ) + printf( "\nVerification FAILED!\n"); + else + printf( "\nVerification okay!\n"); + + Cudd_RecursiveDeref( dd, aRes ); + + // delocate + Extra_Dissolve( &g_Func ); + } + printf( "Preliminary verification time = %.2f sec\n", (float)(clock() - clk1)/(float)(CLOCKS_PER_SEC) ); + /////////////////////////////////////////////////////////////////// +*/ + + if ( !CreateDecomposedNetwork( dd, aFunc, pNames, nNames, FileNameFin, nLutSize, fCheck, fVerbose ) ) + return 0; + +/* + /////////////////////////////////////////////////////////////////// + // verification of the decomposed LUT network + clk1 = clock(); + { + BFunc g_Func; + DdNode * aRes; + + g_Func.dd = dd; + g_Func.FileInput = Extra_UtilStrsav(FileNameFin); + + if ( Extra_ReadFile( &g_Func ) == 0 ) + { + printf( "\nSomething did not work out while reading the input file for verification\n"); + Extra_Dissolve( &g_Func ); + return; + } + + aRes = Cudd_BddToAdd( dd, g_Func.pOutputs[0] ); Cudd_Ref( aRes ); + + if ( aRes != aFunc ) + printf( "\nFinal verification FAILED!\n"); + else + printf( "\nFinal verification okay!\n"); + + Cudd_RecursiveDeref( dd, aRes ); + + // delocate + Extra_Dissolve( &g_Func ); + } + printf( "Final verification time = %.2f sec\n", (float)(clock() - clk1)/(float)(CLOCKS_PER_SEC) ); + /////////////////////////////////////////////////////////////////// +*/ + + // verify the results + if ( fCheck ) + { + extern int Cmd_CommandExecute( void * pAbc, char * sCommand ); + extern void * Abc_FrameGetGlobalFrame(); + char Command[200]; + sprintf( Command, "cec %s %s", FileNameIni, FileNameFin ); + Cmd_CommandExecute( Abc_FrameGetGlobalFrame(), Command ); + } + + Cudd_RecursiveDeref( dd, aFunc ); + + // release the names + for ( i = 0; i < nNames; i++ ) + free( pNames[i] ); + + +//fprintf( pTable, "\n" ); +//fclose( pTable ); + + return 1; +} + +#if 0 + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Experiment2( BFunc * pFunc ) +{ + int i, x, RetValue; + int nVars = pFunc->nInputs; + int nOuts = pFunc->nOutputs; + DdManager * dd = pFunc->dd; + long clk1; + +// int nVarsEnc; // the number of additional variables to encode outputs +// DdNode * pbVarsEnc[MAXOUTPUTS]; // the BDDs of the encoding vars + + int nNames; // the total number of all inputs + char * pNames[MAXINPUTS]; // the temporary storage for the input (and output encoding) names + + DdNode * aFunc; // the encoded 0-1 BDD containing all the outputs + + char FileNameIni[100]; + char FileNameFin[100]; + char Buffer[100]; + + DdManager * DdNew; + +//pTable = fopen( "stats.txt", "a+" ); +//fprintf( pTable, "%s ", pFunc->FileGeneric ); +//fprintf( pTable, "%d ", nVars ); +//fprintf( pTable, "%d ", nOuts ); + + + // assign the file names + strcpy( FileNameIni, pFunc->FileGeneric ); + strcat( FileNameIni, "_ENC.blif" ); + + strcpy( FileNameFin, pFunc->FileGeneric ); + strcat( FileNameFin, "_LUT.blif" ); + + // derive the single-output function IN THE NEW MANAGER + clk1 = clock(); +// aFunc = GetSingleOutputFunction( dd, pFunc->pOutputs, nOuts, pbVarsEnc, nVarsEnc ); Cudd_Ref( aFunc ); + aFunc = GetSingleOutputFunctionRemappedNewDD( dd, pFunc->pOutputs, nOuts, &DdNew ); Cudd_Ref( aFunc ); + printf( "Single-output function derivation time = %.2f sec\n", (float)(clock() - clk1)/(float)(CLOCKS_PER_SEC) ); +// s_RemappingTime = clock() - clk1; + + // dispose of the multiple-output function + Extra_Dissolve( pFunc ); + + // reorder the single output function + printf( "\nReordering variables in the new manager...\n"); + clk1 = clock(); + printf( "Node count before = %d\n", Cudd_DagSize( aFunc ) ); +// Cudd_ReduceHeap(DdNew, CUDD_REORDER_SIFT,1); + Cudd_ReduceHeap(DdNew, CUDD_REORDER_SYMM_SIFT,1); +// Cudd_ReduceHeap(DdNew, CUDD_REORDER_SYMM_SIFT,1); + printf( "Node count after = %d\n", Cudd_DagSize( aFunc ) ); + printf( "Variable reordering time = %.2f sec\n", (float)(clock() - clk1)/(float)(CLOCKS_PER_SEC) ); + printf( "\n" ); + +//fprintf( pTable, "%d ", Cudd_DagSize( aFunc ) ); +//fprintf( pTable, "%d ", Extra_ProfileWidthMax(DdNew, aFunc) ); + + + // create the names to be used with the new manager + nNames = DdNew->size; + for ( x = 0; x < nNames; x++ ) + { + sprintf( Buffer, "v%02d", x ); + pNames[x] = Extra_UtilStrsav( Buffer ); + } + + + + // write the single-output function into BLIF for verification + clk1 = clock(); + WriteSingleOutputFunctionBlif( DdNew, aFunc, pNames, nNames, FileNameIni ); + printf( "Single-output function writing time = %.2f sec\n", (float)(clock() - clk1)/(float)(CLOCKS_PER_SEC) ); + + + /////////////////////////////////////////////////////////////////// + // verification of single output function + clk1 = clock(); + { + BFunc g_Func; + DdNode * aRes; + + g_Func.dd = DdNew; + g_Func.FileInput = Extra_UtilStrsav(FileNameIni); + + if ( Extra_ReadFile( &g_Func ) == 0 ) + { + printf( "\nSomething did not work out while reading the input file for verification\n"); + Extra_Dissolve( &g_Func ); + return; + } + + aRes = Cudd_BddToAdd( DdNew, g_Func.pOutputs[0] ); Cudd_Ref( aRes ); + + if ( aRes != aFunc ) + printf( "\nVerification FAILED!\n"); + else + printf( "\nVerification okay!\n"); + + Cudd_RecursiveDeref( DdNew, aRes ); + + // delocate + Extra_Dissolve( &g_Func ); + } + printf( "Preliminary verification time = %.2f sec\n", (float)(clock() - clk1)/(float)(CLOCKS_PER_SEC) ); + /////////////////////////////////////////////////////////////////// + + + CreateDecomposedNetwork( DdNew, aFunc, pNames, nNames, FileNameFin, nLutSize, 0 ); + +/* + /////////////////////////////////////////////////////////////////// + // verification of the decomposed LUT network + clk1 = clock(); + { + BFunc g_Func; + DdNode * aRes; + + g_Func.dd = DdNew; + g_Func.FileInput = Extra_UtilStrsav(FileNameFin); + + if ( Extra_ReadFile( &g_Func ) == 0 ) + { + printf( "\nSomething did not work out while reading the input file for verification\n"); + Extra_Dissolve( &g_Func ); + return; + } + + aRes = Cudd_BddToAdd( DdNew, g_Func.pOutputs[0] ); Cudd_Ref( aRes ); + + if ( aRes != aFunc ) + printf( "\nFinal verification FAILED!\n"); + else + printf( "\nFinal verification okay!\n"); + + Cudd_RecursiveDeref( DdNew, aRes ); + + // delocate + Extra_Dissolve( &g_Func ); + } + printf( "Final verification time = %.2f sec\n", (float)(clock() - clk1)/(float)(CLOCKS_PER_SEC) ); + /////////////////////////////////////////////////////////////////// +*/ + + + Cudd_RecursiveDeref( DdNew, aFunc ); + + // release the names + for ( i = 0; i < nNames; i++ ) + free( pNames[i] ); + + + + ///////////////////////////////////////////////////////////////////// + // check for remaining references in the package + RetValue = Cudd_CheckZeroRef( DdNew ); + printf( "\nThe number of referenced nodes in the new manager = %d\n", RetValue ); + Cudd_Quit( DdNew ); + +//fprintf( pTable, "\n" ); +//fclose( pTable ); + +} + +#endif + +//////////////////////////////////////////////////////////////////////// +/// SINGLE OUTPUT FUNCTION /// +//////////////////////////////////////////////////////////////////////// + +// the bit count for the first 256 integer numbers +static unsigned char BitCount8[256] = { + 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 +}; + +///////////////////////////////////////////////////////////// +static int s_SuppSize[MAXOUTPUTS]; +int CompareSupports( int *ptrX, int *ptrY ) +{ + return ( s_SuppSize[*ptrY] - s_SuppSize[*ptrX] ); +} +///////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////// +static int s_MintOnes[MAXOUTPUTS]; +int CompareMinterms( int *ptrX, int *ptrY ) +{ + return ( s_MintOnes[*ptrY] - s_MintOnes[*ptrX] ); +} +///////////////////////////////////////////////////////////// + +int GrayCode ( int BinCode ) +{ + return BinCode ^ ( BinCode >> 1 ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * GetSingleOutputFunction( DdManager * dd, DdNode ** pbOuts, int nOuts, DdNode ** pbVarsEnc, int nVarsEnc, int fVerbose ) +{ + int i; + DdNode * bResult, * aResult; + DdNode * bCube, * bTemp, * bProd; + + int Order[MAXOUTPUTS]; +// int OrderMint[MAXOUTPUTS]; + + // sort the output according to their support size + for ( i = 0; i < nOuts; i++ ) + { + s_SuppSize[i] = Cudd_SupportSize( dd, pbOuts[i] ); +// s_MintOnes[i] = BitCount8[i]; + Order[i] = i; +// OrderMint[i] = i; + } + + // order the outputs + qsort( (void*)Order, nOuts, sizeof(int), (int(*)(const void*, const void*)) CompareSupports ); + // order the outputs +// qsort( (void*)OrderMint, nOuts, sizeof(int), (int(*)(const void*, const void*)) CompareMinterms ); + + + bResult = b0; Cudd_Ref( bResult ); + for ( i = 0; i < nOuts; i++ ) + { +// bCube = Cudd_bddBitsToCube( dd, OrderMint[i], nVarsEnc, pbVarsEnc ); Cudd_Ref( bCube ); +// bProd = Cudd_bddAnd( dd, bCube, pbOuts[Order[nOuts-1-i]] ); Cudd_Ref( bProd ); + bCube = Extra_bddBitsToCube( dd, i, nVarsEnc, pbVarsEnc, 1 ); Cudd_Ref( bCube ); + bProd = Cudd_bddAnd( dd, bCube, pbOuts[Order[i]] ); Cudd_Ref( bProd ); + Cudd_RecursiveDeref( dd, bCube ); + + bResult = Cudd_bddOr( dd, bProd, bTemp = bResult ); Cudd_Ref( bResult ); + Cudd_RecursiveDeref( dd, bTemp ); + Cudd_RecursiveDeref( dd, bProd ); + } + + // convert to the ADD +if ( fVerbose ) +printf( "Single BDD size = %6d nodes\n", Cudd_DagSize(bResult) ); + aResult = Cudd_BddToAdd( dd, bResult ); Cudd_Ref( aResult ); + Cudd_RecursiveDeref( dd, bResult ); +if ( fVerbose ) +printf( "MTBDD = %6d nodes\n", Cudd_DagSize(aResult) ); + Cudd_Deref( aResult ); + return aResult; +} +/* +DdNode * GetSingleOutputFunction( DdManager * dd, DdNode ** pbOuts, int nOuts, DdNode ** pbVarsEnc, int nVarsEnc ) +{ + int i; + DdNode * bResult, * aResult; + DdNode * bCube, * bTemp, * bProd; + + bResult = b0; Cudd_Ref( bResult ); + for ( i = 0; i < nOuts; i++ ) + { +// bCube = Extra_bddBitsToCube( dd, i, nVarsEnc, pbVarsEnc ); Cudd_Ref( bCube ); + bCube = Extra_bddBitsToCube( dd, nOuts-1-i, nVarsEnc, pbVarsEnc ); Cudd_Ref( bCube ); + bProd = Cudd_bddAnd( dd, bCube, pbOuts[i] ); Cudd_Ref( bProd ); + Cudd_RecursiveDeref( dd, bCube ); + + bResult = Cudd_bddOr( dd, bProd, bTemp = bResult ); Cudd_Ref( bResult ); + Cudd_RecursiveDeref( dd, bTemp ); + Cudd_RecursiveDeref( dd, bProd ); + } + + // conver to the ADD + aResult = Cudd_BddToAdd( dd, bResult ); Cudd_Ref( aResult ); + Cudd_RecursiveDeref( dd, bResult ); + + Cudd_Deref( aResult ); + return aResult; +} +*/ + + +//////////////////////////////////////////////////////////////////////// +/// INPUT REMAPPING /// +//////////////////////////////////////////////////////////////////////// + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * GetSingleOutputFunctionRemapped( DdManager * dd, DdNode ** pOutputs, int nOuts, DdNode ** pbVarsEnc, int nVarsEnc ) +// returns the ADD of the remapped function +{ + static int Permute[MAXINPUTS]; + static DdNode * pRemapped[MAXOUTPUTS]; + + DdNode * bSupp, * bTemp; + int i, Counter; + int nSuppPrev = -1; + DdNode * bFunc; + DdNode * aFunc; + + Cudd_AutodynDisable(dd); + + // perform the remapping + for ( i = 0; i < nOuts; i++ ) + { + // get support + bSupp = Cudd_Support( dd, pOutputs[i] ); Cudd_Ref( bSupp ); + + // create the variable map + Counter = 0; + for ( bTemp = bSupp; bTemp != dd->one; bTemp = cuddT(bTemp) ) + Permute[bTemp->index] = Counter++; + + // transfer the BDD and remap it + pRemapped[i] = Cudd_bddPermute( dd, pOutputs[i], Permute ); Cudd_Ref( pRemapped[i] ); + + // remove support + Cudd_RecursiveDeref( dd, bSupp ); + } + + // perform the encoding + bFunc = Extra_bddEncodingBinary( dd, pRemapped, nOuts, pbVarsEnc, nVarsEnc ); Cudd_Ref( bFunc ); + + // convert to ADD + aFunc = Cudd_BddToAdd( dd, bFunc ); Cudd_Ref( aFunc ); + Cudd_RecursiveDeref( dd, bFunc ); + + // deref the intermediate results + for ( i = 0; i < nOuts; i++ ) + Cudd_RecursiveDeref( dd, pRemapped[i] ); + + Cudd_Deref( aFunc ); + return aFunc; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * GetSingleOutputFunctionRemappedNewDD( DdManager * dd, DdNode ** pOutputs, int nOuts, DdManager ** DdNew ) +// returns the ADD of the remapped function +{ + static int Permute[MAXINPUTS]; + static DdNode * pRemapped[MAXOUTPUTS]; + + static DdNode * pbVarsEnc[MAXINPUTS]; + int nVarsEnc; + + DdManager * ddnew; + + DdNode * bSupp, * bTemp; + int i, v, Counter; + int nSuppPrev = -1; + DdNode * bFunc; + + // these are in the new manager + DdNode * bFuncNew; + DdNode * aFuncNew; + + int nVarsMax = 0; + + // perform the remapping and write the DDs into the new manager + for ( i = 0; i < nOuts; i++ ) + { + // get support + bSupp = Cudd_Support( dd, pOutputs[i] ); Cudd_Ref( bSupp ); + + // create the variable map + // to remap the DD into the upper part of the manager + Counter = 0; + for ( bTemp = bSupp; bTemp != dd->one; bTemp = cuddT(bTemp) ) + Permute[bTemp->index] = dd->invperm[Counter++]; + + // transfer the BDD and remap it + pRemapped[i] = Cudd_bddPermute( dd, pOutputs[i], Permute ); Cudd_Ref( pRemapped[i] ); + + // remove support + Cudd_RecursiveDeref( dd, bSupp ); + + + // determine the largest support size + if ( nVarsMax < Counter ) + nVarsMax = Counter; + } + + // select the encoding variables to follow immediately after the original variables + nVarsEnc = Extra_Base2Log(nOuts); +/* + for ( v = 0; v < nVarsEnc; v++ ) + if ( nVarsMax + v < dd->size ) + pbVarsEnc[v] = dd->var[ dd->invperm[nVarsMax+v] ]; + else + pbVarsEnc[v] = Cudd_bddNewVar( dd ); +*/ + // create the new variables on top of the manager + for ( v = 0; v < nVarsEnc; v++ ) + pbVarsEnc[v] = Cudd_bddNewVarAtLevel( dd, v ); + +//fprintf( pTable, "%d ", Cudd_SharingSize( pRemapped, nOuts ) ); +//fprintf( pTable, "%d ", Extra_ProfileWidthSharingMax(dd, pRemapped, nOuts) ); + + + // perform the encoding + bFunc = Extra_bddEncodingBinary( dd, pRemapped, nOuts, pbVarsEnc, nVarsEnc ); Cudd_Ref( bFunc ); + + + // find the cross-manager permutation + // the variable from the level v in the old manager + // should become a variable number v in the new manager + for ( v = 0; v < nVarsMax + nVarsEnc; v++ ) + Permute[dd->invperm[v]] = v; + + + /////////////////////////////////////////////////////////////////////////////// + // start the new manager + ddnew = Cudd_Init( nVarsMax + nVarsEnc, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0); +// Cudd_AutodynDisable(ddnew); + Cudd_AutodynEnable(dd, CUDD_REORDER_SYMM_SIFT); + + // transfer it to the new manager + bFuncNew = Cudd_bddTransferPermute( dd, ddnew, bFunc, Permute ); Cudd_Ref( bFuncNew ); + /////////////////////////////////////////////////////////////////////////////// + + + // deref the intermediate results in the old manager + Cudd_RecursiveDeref( dd, bFunc ); + for ( i = 0; i < nOuts; i++ ) + Cudd_RecursiveDeref( dd, pRemapped[i] ); + + + /////////////////////////////////////////////////////////////////////////////// + // convert to ADD in the new manager + aFuncNew = Cudd_BddToAdd( ddnew, bFuncNew ); Cudd_Ref( aFuncNew ); + Cudd_RecursiveDeref( ddnew, bFuncNew ); + + // return the manager + *DdNew = ddnew; + /////////////////////////////////////////////////////////////////////////////// + + Cudd_Deref( aFuncNew ); + return aFuncNew; +} + +//////////////////////////////////////////////////////////////////////// +/// BLIF WRITING FUNCTIONS /// +//////////////////////////////////////////////////////////////////////// + +void WriteDDintoBLIFfile( FILE * pFile, DdNode * Func, char * OutputName, char * Prefix, char ** InputNames ); + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void WriteSingleOutputFunctionBlif( DdManager * dd, DdNode * aFunc, char ** pNames, int nNames, char * FileName ) +{ + int i; + FILE * pFile; + + // start the file + pFile = fopen( FileName, "w" ); + fprintf( pFile, ".model %s\n", FileName ); + + fprintf( pFile, ".inputs" ); + for ( i = 0; i < nNames; i++ ) + fprintf( pFile, " %s", pNames[i] ); + fprintf( pFile, "\n" ); + fprintf( pFile, ".outputs F" ); + fprintf( pFile, "\n" ); + + // write the DD into the file + WriteDDintoBLIFfile( pFile, aFunc, "F", "", pNames ); + + fprintf( pFile, ".end\n" ); + fclose( pFile ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void WriteDDintoBLIFfile( FILE * pFile, DdNode * Func, char * OutputName, char * Prefix, char ** InputNames ) +// writes the main part of the BLIF file +// Func is a BDD or a 0-1 ADD to be written +// OutputName is the name of the output +// Prefix is attached to each intermendiate signal to make it unique +// InputNames are the names of the input signals +// (some part of the code is borrowed from Cudd_DumpDot()) +{ + int i; + st_table * visited; + st_generator * gen = NULL; + long refAddr, diff, mask; + DdNode * Node, * Else, * ElseR, * Then; + + /* Initialize symbol table for visited nodes. */ + visited = st_init_table( st_ptrcmp, st_ptrhash ); + + /* Collect all the nodes of this DD in the symbol table. */ + cuddCollectNodes( Cudd_Regular(Func), visited ); + + /* Find how many most significant hex digits are identical + ** in the addresses of all the nodes. Build a mask based + ** on this knowledge, so that digits that carry no information + ** will not be printed. This is done in two steps. + ** 1. We scan the symbol table to find the bits that differ + ** in at least 2 addresses. + ** 2. We choose one of the possible masks. There are 8 possible + ** masks for 32-bit integer, and 16 possible masks for 64-bit + ** integers. + */ + + /* Find the bits that are different. */ + refAddr = ( long )Cudd_Regular(Func); + diff = 0; + gen = st_init_gen( visited ); + while ( st_gen( gen, ( char ** ) &Node, NULL ) ) + { + diff |= refAddr ^ ( long ) Node; + } + st_free_gen( gen ); + gen = NULL; + + /* Choose the mask. */ + for ( i = 0; ( unsigned ) i < 8 * sizeof( long ); i += 4 ) + { + mask = ( 1 << i ) - 1; + if ( diff <= mask ) + break; + } + + + // write the buffer for the output + fprintf( pFile, ".names %s%lx %s\n", Prefix, ( mask & (long)Cudd_Regular(Func) ) / sizeof(DdNode), OutputName ); + fprintf( pFile, "%s 1\n", (Cudd_IsComplement(Func))? "0": "1" ); + + + gen = st_init_gen( visited ); + while ( st_gen( gen, ( char ** ) &Node, NULL ) ) + { + if ( Node->index == CUDD_MAXINDEX ) + { + // write the terminal node + fprintf( pFile, ".names %s%lx\n", Prefix, ( mask & (long)Node ) / sizeof(DdNode) ); + fprintf( pFile, " %s\n", (cuddV(Node) == 0.0)? "0": "1" ); + continue; + } + + Else = cuddE(Node); + ElseR = Cudd_Regular(Else); + Then = cuddT(Node); + + assert( InputNames[Node->index] ); + if ( Else == ElseR ) + { // no inverter + fprintf( pFile, ".names %s %s%lx %s%lx %s%lx\n", InputNames[Node->index], + Prefix, ( mask & (long)ElseR ) / sizeof(DdNode), + Prefix, ( mask & (long)Then ) / sizeof(DdNode), + Prefix, ( mask & (long)Node ) / sizeof(DdNode) ); + fprintf( pFile, "01- 1\n" ); + fprintf( pFile, "1-1 1\n" ); + } + else + { // inverter + int * pSlot; + fprintf( pFile, ".names %s %s%lx_i %s%lx %s%lx\n", InputNames[Node->index], + Prefix, ( mask & (long)ElseR ) / sizeof(DdNode), + Prefix, ( mask & (long)Then ) / sizeof(DdNode), + Prefix, ( mask & (long)Node ) / sizeof(DdNode) ); + fprintf( pFile, "01- 1\n" ); + fprintf( pFile, "1-1 1\n" ); + + // if the inverter is written, skip + if ( !st_find( visited, (char *)ElseR, (char ***)&pSlot ) ) + assert( 0 ); + if ( *pSlot ) + continue; + *pSlot = 1; + + fprintf( pFile, ".names %s%lx %s%lx_i\n", + Prefix, ( mask & (long)ElseR ) / sizeof(DdNode), + Prefix, ( mask & (long)ElseR ) / sizeof(DdNode) ); + fprintf( pFile, "0 1\n" ); + } + } + st_free_gen( gen ); + gen = NULL; + st_free_table( visited ); +} + + + + +static DdManager * s_ddmin; + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void WriteDDintoBLIFfileReorder( DdManager * dd, FILE * pFile, DdNode * Func, char * OutputName, char * Prefix, char ** InputNames ) +// writes the main part of the BLIF file +// Func is a BDD or a 0-1 ADD to be written +// OutputName is the name of the output +// Prefix is attached to each intermendiate signal to make it unique +// InputNames are the names of the input signals +// (some part of the code is borrowed from Cudd_DumpDot()) +{ + int i; + st_table * visited; + st_generator * gen = NULL; + long refAddr, diff, mask; + DdNode * Node, * Else, * ElseR, * Then; + + + /////////////////////////////////////////////////////////////// + DdNode * bFmin; + int clk1; + + if ( s_ddmin == NULL ) + s_ddmin = Cudd_Init( dd->size, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0); + + clk1 = clock(); + bFmin = Cudd_bddTransfer( dd, s_ddmin, Func ); Cudd_Ref( bFmin ); + + // reorder + printf( "Nodes before = %d. ", Cudd_DagSize(bFmin) ); + Cudd_ReduceHeap(s_ddmin,CUDD_REORDER_SYMM_SIFT,1); +// Cudd_ReduceHeap(s_ddmin,CUDD_REORDER_SYMM_SIFT_CONV,1); + printf( "Nodes after = %d. \n", Cudd_DagSize(bFmin) ); + /////////////////////////////////////////////////////////////// + + + + /* Initialize symbol table for visited nodes. */ + visited = st_init_table( st_ptrcmp, st_ptrhash ); + + /* Collect all the nodes of this DD in the symbol table. */ + cuddCollectNodes( Cudd_Regular(bFmin), visited ); + + /* Find how many most significant hex digits are identical + ** in the addresses of all the nodes. Build a mask based + ** on this knowledge, so that digits that carry no information + ** will not be printed. This is done in two steps. + ** 1. We scan the symbol table to find the bits that differ + ** in at least 2 addresses. + ** 2. We choose one of the possible masks. There are 8 possible + ** masks for 32-bit integer, and 16 possible masks for 64-bit + ** integers. + */ + + /* Find the bits that are different. */ + refAddr = ( long )Cudd_Regular(bFmin); + diff = 0; + gen = st_init_gen( visited ); + while ( st_gen( gen, ( char ** ) &Node, NULL ) ) + { + diff |= refAddr ^ ( long ) Node; + } + st_free_gen( gen ); + gen = NULL; + + /* Choose the mask. */ + for ( i = 0; ( unsigned ) i < 8 * sizeof( long ); i += 4 ) + { + mask = ( 1 << i ) - 1; + if ( diff <= mask ) + break; + } + + + // write the buffer for the output + fprintf( pFile, ".names %s%lx %s\n", Prefix, ( mask & (long)Cudd_Regular(bFmin) ) / sizeof(DdNode), OutputName ); + fprintf( pFile, "%s 1\n", (Cudd_IsComplement(bFmin))? "0": "1" ); + + + gen = st_init_gen( visited ); + while ( st_gen( gen, ( char ** ) &Node, NULL ) ) + { + if ( Node->index == CUDD_MAXINDEX ) + { + // write the terminal node + fprintf( pFile, ".names %s%lx\n", Prefix, ( mask & (long)Node ) / sizeof(DdNode) ); + fprintf( pFile, " %s\n", (cuddV(Node) == 0.0)? "0": "1" ); + continue; + } + + Else = cuddE(Node); + ElseR = Cudd_Regular(Else); + Then = cuddT(Node); + + assert( InputNames[Node->index] ); + if ( Else == ElseR ) + { // no inverter + fprintf( pFile, ".names %s %s%lx %s%lx %s%lx\n", InputNames[Node->index], + Prefix, ( mask & (long)ElseR ) / sizeof(DdNode), + Prefix, ( mask & (long)Then ) / sizeof(DdNode), + Prefix, ( mask & (long)Node ) / sizeof(DdNode) ); + fprintf( pFile, "01- 1\n" ); + fprintf( pFile, "1-1 1\n" ); + } + else + { // inverter + fprintf( pFile, ".names %s %s%lx_i %s%lx %s%lx\n", InputNames[Node->index], + Prefix, ( mask & (long)ElseR ) / sizeof(DdNode), + Prefix, ( mask & (long)Then ) / sizeof(DdNode), + Prefix, ( mask & (long)Node ) / sizeof(DdNode) ); + fprintf( pFile, "01- 1\n" ); + fprintf( pFile, "1-1 1\n" ); + + fprintf( pFile, ".names %s%lx %s%lx_i\n", + Prefix, ( mask & (long)ElseR ) / sizeof(DdNode), + Prefix, ( mask & (long)ElseR ) / sizeof(DdNode) ); + fprintf( pFile, "0 1\n" ); + } + } + st_free_gen( gen ); + gen = NULL; + st_free_table( visited ); + + + ////////////////////////////////////////////////// + Cudd_RecursiveDeref( s_ddmin, bFmin ); + ////////////////////////////////////////////////// +} + + + + +//////////////////////////////////////////////////////////////////////// +/// TRANSFER WITH MAPPING /// +//////////////////////////////////////////////////////////////////////// +static DdNode * cuddBddTransferPermuteRecur +ARGS((DdManager * ddS, DdManager * ddD, DdNode * f, st_table * table, int * Permute )); + +static DdNode * cuddBddTransferPermute +ARGS((DdManager * ddS, DdManager * ddD, DdNode * f, int * Permute)); + +/**Function******************************************************************** + + Synopsis [Convert a BDD from a manager to another one.] + + Description [Convert a BDD from a manager to another one. The orders of the + variables in the two managers may be different. Returns a + pointer to the BDD in the destination manager if successful; NULL + otherwise. The i-th entry in the array Permute tells what is the index + of the i-th variable from the old manager in the new manager.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +Cudd_bddTransferPermute( DdManager * ddSource, + DdManager * ddDestination, DdNode * f, int * Permute ) +{ + DdNode *res; + do + { + ddDestination->reordered = 0; + res = cuddBddTransferPermute( ddSource, ddDestination, f, Permute ); + } + while ( ddDestination->reordered == 1 ); + return ( res ); + +} /* end of Cudd_bddTransferPermute */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Convert a BDD from a manager to another one.] + + Description [Convert a BDD from a manager to another one. Returns a + pointer to the BDD in the destination manager if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddTransferPermute] + +******************************************************************************/ +DdNode * +cuddBddTransferPermute( DdManager * ddS, DdManager * ddD, DdNode * f, int * Permute ) +{ + DdNode *res; + st_table *table = NULL; + st_generator *gen = NULL; + DdNode *key, *value; + + table = st_init_table( st_ptrcmp, st_ptrhash ); + if ( table == NULL ) + goto failure; + res = cuddBddTransferPermuteRecur( ddS, ddD, f, table, Permute ); + if ( res != NULL ) + cuddRef( res ); + + /* Dereference all elements in the table and dispose of the table. + ** This must be done also if res is NULL to avoid leaks in case of + ** reordering. */ + gen = st_init_gen( table ); + if ( gen == NULL ) + goto failure; + while ( st_gen( gen, ( char ** ) &key, ( char ** ) &value ) ) + { + Cudd_RecursiveDeref( ddD, value ); + } + st_free_gen( gen ); + gen = NULL; + st_free_table( table ); + table = NULL; + + if ( res != NULL ) + cuddDeref( res ); + return ( res ); + + failure: + if ( table != NULL ) + st_free_table( table ); + if ( gen != NULL ) + st_free_gen( gen ); + return ( NULL ); + +} /* end of cuddBddTransferPermute */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_bddTransferPermute.] + + Description [Performs the recursive step of Cudd_bddTransferPermute. + Returns a pointer to the result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [cuddBddTransferPermute] + +******************************************************************************/ +static DdNode * +cuddBddTransferPermuteRecur( DdManager * ddS, + DdManager * ddD, DdNode * f, st_table * table, int * Permute ) +{ + DdNode *ft, *fe, *t, *e, *var, *res; + DdNode *one, *zero; + int index; + int comple = 0; + + statLine( ddD ); + one = DD_ONE( ddD ); + comple = Cudd_IsComplement( f ); + + /* Trivial cases. */ + if ( Cudd_IsConstant( f ) ) + return ( Cudd_NotCond( one, comple ) ); + + /* Make canonical to increase the utilization of the cache. */ + f = Cudd_NotCond( f, comple ); + /* Now f is a regular pointer to a non-constant node. */ + + /* Check the cache. */ + if ( st_lookup( table, ( char * ) f, ( char ** ) &res ) ) + return ( Cudd_NotCond( res, comple ) ); + + /* Recursive step. */ + index = Permute[f->index]; + ft = cuddT( f ); + fe = cuddE( f ); + + t = cuddBddTransferPermuteRecur( ddS, ddD, ft, table, Permute ); + if ( t == NULL ) + { + return ( NULL ); + } + cuddRef( t ); + + e = cuddBddTransferPermuteRecur( ddS, ddD, fe, table, Permute ); + if ( e == NULL ) + { + Cudd_RecursiveDeref( ddD, t ); + return ( NULL ); + } + cuddRef( e ); + + zero = Cudd_Not( one ); + var = cuddUniqueInter( ddD, index, one, zero ); + if ( var == NULL ) + { + Cudd_RecursiveDeref( ddD, t ); + Cudd_RecursiveDeref( ddD, e ); + return ( NULL ); + } + res = cuddBddIteRecur( ddD, var, t, e ); + if ( res == NULL ) + { + Cudd_RecursiveDeref( ddD, t ); + Cudd_RecursiveDeref( ddD, e ); + return ( NULL ); + } + cuddRef( res ); + Cudd_RecursiveDeref( ddD, t ); + Cudd_RecursiveDeref( ddD, e ); + + if ( st_add_direct( table, ( char * ) f, ( char * ) res ) == + ST_OUT_OF_MEM ) + { + Cudd_RecursiveDeref( ddD, res ); + return ( NULL ); + } + return ( Cudd_NotCond( res, comple ) ); + +} /* end of cuddBddTransferPermuteRecur */ + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + + + diff --git a/abc_with_bb_support/src/bdd/cas/casDec.c b/abc_with_bb_support/src/bdd/cas/casDec.c new file mode 100644 index 000000000..ce997dd2d --- /dev/null +++ b/abc_with_bb_support/src/bdd/cas/casDec.c @@ -0,0 +1,508 @@ +/**CFile**************************************************************** + + FileName [casDec.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [CASCADE: Decomposition of shared BDDs into a LUT cascade.] + + Synopsis [BDD-based decomposition with encoding.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Spring 2002.] + + Revision [$Id: casDec.c,v 1.0 2002/01/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include +#include +#include +#include + +#include "extra.h" +#include "cas.h" + +//////////////////////////////////////////////////////////////////////// +/// type definitions /// +//////////////////////////////////////////////////////////////////////// + +typedef struct +{ + int nIns; // the number of LUT variables + int nInsP; // the number of inputs coming from the previous LUT + int nCols; // the number of columns in this LUT + int nMulti; // the column multiplicity, [log2(nCols)] + int nSimple; // the number of outputs implemented as direct connections to inputs of the previous block + int Level; // the starting level in the ADD in this LUT + +// DdNode ** pbVarsIn[32]; // the BDDs of the elementary input variables +// DdNode ** pbVarsOut[32]; // the BDDs of the elementary output variables + +// char * pNamesIn[32]; // the names of input variables +// char * pNamesOut[32]; // the names of output variables + + DdNode ** pbCols; // the array of columns represented by BDDs + DdNode ** pbCodes; // the array of codes (in terms of pbVarsOut) + DdNode ** paNodes; // the array of starting ADD nodes on the next level (also referenced) + + DdNode * bRelation; // the relation after encoding + + // the relation depends on the three groups of variables: + // (1) variables on top represent the outputs of the previous cascade + // (2) variables in the middle represent the primary inputs + // (3) variables below (CVars) represent the codes + // + // the replacement is done after computing the relation +} LUT; + + +//////////////////////////////////////////////////////////////////////// +/// static functions /// +//////////////////////////////////////////////////////////////////////// + +// the LUT-2-BLIF writing function +void WriteLUTSintoBLIFfile( FILE * pFile, DdManager * dd, LUT ** pLuts, int nLuts, DdNode ** bCVars, char ** pNames, int nNames, char * FileName ); + +// the function to write a DD (BDD or ADD) as a network of MUXES +extern void WriteDDintoBLIFfile( FILE * pFile, DdNode * Func, char * OutputName, char * Prefix, char ** InputNames ); +extern void WriteDDintoBLIFfileReorder( DdManager * dd, FILE * pFile, DdNode * Func, char * OutputName, char * Prefix, char ** InputNames ); + +//////////////////////////////////////////////////////////////////////// +/// static varibles /// +//////////////////////////////////////////////////////////////////////// + +static int s_LutSize = 15; +static int s_nFuncVars; + +long s_EncodingTime; + +long s_EncSearchTime; +long s_EncComputeTime; + +//////////////////////////////////// +// temporary output variables +//FILE * pTable; +//long s_ReadingTime; +//long s_RemappingTime; +//////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// debugging macros /// +//////////////////////////////////////////////////////////////////////// + +#define PRB(f) printf( #f " = " ); Cudd_bddPrint(dd,f); printf( "\n" ) +#define PRK(f,n) Cudd_PrintKMap(stdout,dd,(f),Cudd_Not(f),(n),NULL,0); printf( "K-map for function" #f "\n\n" ) +#define PRK2(f,g,n) Cudd_PrintKMap(stdout,dd,(f),(g),(n),NULL,0); printf( "K-map for function <" #f ", " #g ">\n\n" ) + + +//////////////////////////////////////////////////////////////////////// +/// EXTERNAL FUNCTIONS /// +//////////////////////////////////////////////////////////////////////// + +int CreateDecomposedNetwork( DdManager * dd, DdNode * aFunc, char ** pNames, int nNames, char * FileName, int nLutSize, int fCheck, int fVerbose ) +// aFunc is a 0-1 ADD for the given function +// pNames (nNames) are the input variable names +// FileName is the name of the output file for the LUT network +// dynamic variable reordering should be disabled when this function is running +{ + static LUT * pLuts[MAXINPUTS]; // the LUT cascade + static int Profile[MAXINPUTS]; // the profile filled in with the info about the BDD width + static int Permute[MAXINPUTS]; // the array to store a temporary permutation of variables + + LUT * p; // the current LUT + int i, v; + + DdNode * bCVars[32]; // these are variables for the codes + + int nVarsRem; // the number of remaining variables + int PrevMulti; // column multiplicity on the previous level + int fLastLut; // flag to signal the last LUT + int nLuts; + int nLutsTotal = 0; + int nLutOutputs = 0; + int nLutOutputsOrig = 0; + + long clk1; + + s_LutSize = nLutSize; + + s_nFuncVars = nNames; + + // get the profile + clk1 = clock(); + Extra_ProfileWidth( dd, aFunc, Profile, -1 ); + + +// for ( v = 0; v < nNames; v++ ) +// printf( "Level = %2d, Width = %2d\n", v+1, Profile[v] ); + + +//printf( "\n" ); + + // mark-up the LUTs + // assuming that the manager has exactly nNames vars (new vars have not been introduced yet) + nVarsRem = nNames; // the number of remaining variables + PrevMulti = 0; // column multiplicity on the previous level + fLastLut = 0; + nLuts = 0; + do + { + p = (LUT*) malloc( sizeof(LUT) ); + memset( p, 0, sizeof(LUT) ); + + if ( nVarsRem + PrevMulti <= s_LutSize ) // this is the last LUT + { + p->nIns = nVarsRem + PrevMulti; + p->nInsP = PrevMulti; + p->nCols = 2; + p->nMulti = 1; + p->Level = nNames-nVarsRem; + + nVarsRem = 0; + PrevMulti = 1; + + fLastLut = 1; + } + else // this is not the last LUT + { + p->nIns = s_LutSize; + p->nInsP = PrevMulti; + p->nCols = Profile[nNames-(nVarsRem-(s_LutSize-PrevMulti))]; + p->nMulti = Extra_Base2Log(p->nCols); + p->Level = nNames-nVarsRem; + + nVarsRem = nVarsRem-(s_LutSize-PrevMulti); + PrevMulti = p->nMulti; + } + + if ( p->nMulti >= s_LutSize ) + { + printf( "The LUT size is too small\n" ); + return 0; + } + + nLutOutputsOrig += p->nMulti; + + +//if ( fVerbose ) +//printf( "Stage %2d: In = %3d, InP = %3d, Cols = %5d, Multi = %2d, Level = %2d\n", +// nLuts+1, p->nIns, p->nInsP, p->nCols, p->nMulti, p->Level ); + + + // there should be as many columns, codes, and nodes, as there are columns on this level + p->pbCols = (DdNode **) malloc( p->nCols * sizeof(DdNode *) ); + p->pbCodes = (DdNode **) malloc( p->nCols * sizeof(DdNode *) ); + p->paNodes = (DdNode **) malloc( p->nCols * sizeof(DdNode *) ); + + pLuts[nLuts] = p; + nLuts++; + } + while ( !fLastLut ); + + +//if ( fVerbose ) +//printf( "The number of cascades = %d\n", nLuts ); + + +//fprintf( pTable, "%d ", nLuts ); + + + // add the new variables at the bottom + for ( i = 0; i < s_LutSize; i++ ) + bCVars[i] = Cudd_bddNewVar(dd); + + // for each LUT - assign the LUT and encode the columns + s_EncodingTime = 0; + for ( i = 0; i < nLuts; i++ ) + { + int RetValue; + DdNode * bVars[32]; + int nVars; + DdNode * bVarsInCube; + DdNode * bVarsCCube; + DdNode * bVarsCube; + int CutLevel; + + p = pLuts[i]; + + // compute the columns of this LUT starting from the given set of nodes with the given codes + // (these codes have been remapped to depend on the topmost variables in the manager) + // for the first LUT, start with the constant 1 BDD + CutLevel = p->Level + p->nIns - p->nInsP; + if ( i == 0 ) + RetValue = Extra_bddNodePathsUnderCutArray( + dd, &aFunc, &(b1), 1, + p->paNodes, p->pbCols, CutLevel ); + else + RetValue = Extra_bddNodePathsUnderCutArray( + dd, pLuts[i-1]->paNodes, pLuts[i-1]->pbCodes, pLuts[i-1]->nCols, + p->paNodes, p->pbCols, CutLevel ); + assert( RetValue == p->nCols ); + // at this point, we have filled out p->paNodes[] and p->pbCols[] of this LUT + // pLuts[i-1]->paNodes depended on normal vars + // pLuts[i-1]->pbCodes depended on the topmost variables + // the resulting p->paNodes depend on normal ADD nodes + // the resulting p->pbCols depend on normal vars and topmost variables in the manager + + // perform the encoding + + // create the cube of these variables + // collect the topmost variables of the manager + nVars = p->nInsP; + for ( v = 0; v < nVars; v++ ) + bVars[v] = dd->vars[ dd->invperm[v] ]; + bVarsCCube = Extra_bddBitsToCube( dd, (1<nIns - p->nInsP; + for ( v = 0; v < nVars; v++ ) + bVars[v] = dd->vars[ dd->invperm[p->Level+v] ]; + bVarsInCube = Extra_bddBitsToCube( dd, (1<nMulti == 1 ); + assert( p->nCols == 2 ); + assert( Cudd_IsConstant( p->paNodes[0] ) ); + assert( Cudd_IsConstant( p->paNodes[1] ) ); + + bVar = ( p->paNodes[0] == a1 )? bCVars[0]: Cudd_Not( bCVars[0] ); + p->bRelation = Cudd_bddIte( dd, bVar, p->pbCols[0], p->pbCols[1] ); Cudd_Ref( p->bRelation ); + } + else + { + long clk2 = clock(); +// p->bRelation = PerformTheEncoding( dd, p->pbCols, p->nCols, bVarsCube, bCVars, p->nMulti, &p->nSimple ); Cudd_Ref( p->bRelation ); + p->bRelation = Extra_bddEncodingNonStrict( dd, p->pbCols, p->nCols, bVarsCube, bCVars, p->nMulti, &p->nSimple ); Cudd_Ref( p->bRelation ); + s_EncodingTime += clock() - clk2; + } + + // update the number of LUT outputs + nLutOutputs += (p->nMulti - p->nSimple); + nLutsTotal += p->nMulti; + +//if ( fVerbose ) +//printf( "Stage %2d: Simple = %d\n", i+1, p->nSimple ); + +if ( fVerbose ) +printf( "Stage %3d: In = %3d InP = %3d Cols = %5d Multi = %2d Simple = %2d Level = %3d\n", + i+1, p->nIns, p->nInsP, p->nCols, p->nMulti, p->nSimple, p->Level ); + + // get the codes from the relation (these are not necessarily cubes) + { + int c; + for ( c = 0; c < p->nCols; c++ ) + { + p->pbCodes[c] = Cudd_bddAndAbstract( dd, p->bRelation, p->pbCols[c], bVarsCube ); Cudd_Ref( p->pbCodes[c] ); + } + } + + Cudd_RecursiveDeref( dd, bVarsCube ); + + // remap the codes to depend on the topmost varibles of the manager + // useful as a preparation for the next step + { + DdNode ** pbTemp; + int k, v; + + pbTemp = (DdNode **) malloc( p->nCols * sizeof(DdNode *) ); + + // create the identical permutation + for ( v = 0; v < dd->size; v++ ) + Permute[v] = v; + + // use the topmost variables of the manager + // to represent the previous level codes + for ( v = 0; v < p->nMulti; v++ ) + Permute[bCVars[v]->index] = dd->invperm[v]; + + Extra_bddPermuteArray( dd, p->pbCodes, pbTemp, p->nCols, Permute ); + // the array pbTemp comes already referenced + + // deref the old codes and assign the new ones + for ( k = 0; k < p->nCols; k++ ) + { + Cudd_RecursiveDeref( dd, p->pbCodes[k] ); + p->pbCodes[k] = pbTemp[k]; + } + free( pbTemp ); + } + } + if ( fVerbose ) + printf( "LUTs: Total = %5d. Final = %5d. Simple = %5d. (%6.2f %%) ", + nLutsTotal, nLutOutputs, nLutsTotal-nLutOutputs, 100.0*(nLutsTotal-nLutOutputs)/nLutsTotal ); + if ( fVerbose ) + printf( "Memory = %6.2f Mb\n", 1.0*nLutOutputs*(1<nCols; v++ ) + { + Cudd_RecursiveDeref( dd, p->pbCols[v] ); + Cudd_RecursiveDeref( dd, p->pbCodes[v] ); + Cudd_RecursiveDeref( dd, p->paNodes[v] ); + } + Cudd_RecursiveDeref( dd, p->bRelation ); + + free( p->pbCols ); + free( p->pbCodes ); + free( p->paNodes ); + free( p ); + } + + return 1; +} + +void WriteLUTSintoBLIFfile( FILE * pFile, DdManager * dd, LUT ** pLuts, int nLuts, DdNode ** bCVars, char ** pNames, int nNames, char * FileName ) +{ + int i, v, o; + static char * pNamesLocalIn[MAXINPUTS]; + static char * pNamesLocalOut[MAXINPUTS]; + static char Buffer[100]; + DdNode * bCube, * bCof, * bFunc; + LUT * p; + + // go through all the LUTs + for ( i = 0; i < nLuts; i++ ) + { + // get the pointer to the LUT + p = pLuts[i]; + + if ( i == nLuts -1 ) + { + assert( p->nMulti == 1 ); + } + + + fprintf( pFile, "#----------------- LUT #%d ----------------------\n", i ); + + + // fill in the names for the current LUT + + // write the outputs of the previous LUT + if ( i != 0 ) + for ( v = 0; v < p->nInsP; v++ ) + { + sprintf( Buffer, "LUT%02d_%02d", i-1, v ); + pNamesLocalIn[dd->invperm[v]] = Extra_UtilStrsav( Buffer ); + } + // write the primary inputs of the current LUT + for ( v = 0; v < p->nIns - p->nInsP; v++ ) + pNamesLocalIn[dd->invperm[p->Level+v]] = Extra_UtilStrsav( pNames[dd->invperm[p->Level+v]] ); + // write the outputs of the current LUT + for ( v = 0; v < p->nMulti; v++ ) + { + sprintf( Buffer, "LUT%02d_%02d", i, v ); + if ( i != nLuts - 1 ) + pNamesLocalOut[v] = Extra_UtilStrsav( Buffer ); + else + pNamesLocalOut[v] = Extra_UtilStrsav( "F" ); + } + + + // write LUT outputs + + // get the prefix + sprintf( Buffer, "L%02d_", i ); + + // get the cube of encoding variables + bCube = Extra_bddBitsToCube( dd, (1<nMulti)-1, p->nMulti, bCVars, 1 ); Cudd_Ref( bCube ); + + // write each output of the LUT + for ( o = 0; o < p->nMulti; o++ ) + { + // get the cofactor of this output + bCof = Cudd_Cofactor( dd, p->bRelation, bCVars[o] ); Cudd_Ref( bCof ); + // quantify the remaining variables to get the function + bFunc = Cudd_bddExistAbstract( dd, bCof, bCube ); Cudd_Ref( bFunc ); + Cudd_RecursiveDeref( dd, bCof ); + + // write BLIF + sprintf( Buffer, "L%02d_%02d_", i, o ); + +// WriteDDintoBLIFfileReorder( dd, pFile, bFunc, pNamesLocalOut[o], Buffer, pNamesLocalIn ); + // does not work well; the advantage is marginal (30%), the run time is huge... + + WriteDDintoBLIFfile( pFile, bFunc, pNamesLocalOut[o], Buffer, pNamesLocalIn ); + Cudd_RecursiveDeref( dd, bFunc ); + } + Cudd_RecursiveDeref( dd, bCube ); + + // clean up the previous local names + for ( v = 0; v < dd->size; v++ ) + { + if ( pNamesLocalIn[v] ) + free( pNamesLocalIn[v] ); + pNamesLocalIn[v] = NULL; + } + for ( v = 0; v < p->nMulti; v++ ) + free( pNamesLocalOut[v] ); + } +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + + + diff --git a/abc_with_bb_support/src/bdd/cas/module.make b/abc_with_bb_support/src/bdd/cas/module.make new file mode 100644 index 000000000..22ee4f634 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cas/module.make @@ -0,0 +1,3 @@ +SRC += src/bdd/cas/casCore.c \ + src/bdd/cas/casDec.c + diff --git a/abc_with_bb_support/src/bdd/cudd/cuBdd.make b/abc_with_bb_support/src/bdd/cudd/cuBdd.make new file mode 100644 index 000000000..f0660bdf0 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuBdd.make @@ -0,0 +1,41 @@ +CSRC_cu += cuddAPI.c cuddAddAbs.c cuddAddApply.c cuddAddFind.c cuddAddIte.c \ + cuddAddInv.c cuddAddNeg.c cuddAddWalsh.c cuddAndAbs.c \ + cuddAnneal.c cuddApa.c cuddApprox.c cuddBddAbs.c cuddBddCorr.c \ + cuddBddIte.c cuddBridge.c cuddCache.c cuddCheck.c cuddClip.c \ + cuddCof.c cuddCompose.c cuddDecomp.c cuddEssent.c cuddExact.c \ + cuddExport.c cuddGenCof.c cuddGenetic.c \ + cuddGroup.c cuddHarwell.c cuddInit.c cuddInteract.c \ + cuddLCache.c cuddLevelQ.c \ + cuddLinear.c cuddLiteral.c cuddMatMult.c cuddPriority.c \ + cuddRead.c cuddRef.c cuddReorder.c cuddSat.c cuddSign.c \ + cuddSolve.c cuddSplit.c cuddSubsetHB.c cuddSubsetSP.c cuddSymmetry.c \ + cuddTable.c cuddUtil.c cuddWindow.c cuddZddCount.c cuddZddFuncs.c \ + cuddZddGroup.c cuddZddIsop.c cuddZddLin.c cuddZddMisc.c cuddZddPort.c \ + cuddZddReord.c cuddZddSetop.c cuddZddSymm.c cuddZddUtil.c +HEADERS_cu += cudd.h cuddInt.h +MISC += testcudd.c r7x8.1.mat doc/cudd.ps doc/cuddAllAbs.html doc/cuddAllDet.html \ + doc/cuddExtAbs.html doc/cuddExtDet.html doc/cuddIntro.css \ + doc/cuddIntro.html doc/footnode.html doc/img1.gif doc/img2.gif \ + doc/img3.gif doc/img4.gif doc/img5.gif doc/index.html \ + doc/node1.html doc/node2.html doc/node3.html doc/node4.html \ + doc/node5.html doc/node6.html doc/node7.html doc/node8.html \ + doc/icons/change_begin.gif \ + doc/icons/change_delete.gif \ + doc/icons/change_end.gif \ + doc/icons/contents_motif.gif \ + doc/icons/cross_ref_motif.gif \ + doc/icons/foot_motif.gif \ + doc/icons/image.gif \ + doc/icons/index_motif.gif \ + doc/icons/next_group_motif.gif \ + doc/icons/next_group_motif_gr.gif \ + doc/icons/next_motif.gif \ + doc/icons/next_motif_gr.gif \ + doc/icons/previous_group_motif.gif \ + doc/icons/previous_group_motif_gr.gif \ + doc/icons/previous_motif.gif \ + doc/icons/previous_motif_gr.gif \ + doc/icons/up_motif.gif \ + doc/icons/up_motif_gr.gif + +DEPENDENCYFILES = $(CSRC_cu) diff --git a/abc_with_bb_support/src/bdd/cudd/cudd.h b/abc_with_bb_support/src/bdd/cudd/cudd.h new file mode 100644 index 000000000..8f5dc4a35 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cudd.h @@ -0,0 +1,959 @@ +/**CHeaderFile***************************************************************** + + FileName [cudd.h] + + PackageName [cudd] + + Synopsis [The University of Colorado decision diagram package.] + + Description [External functions and data strucures of the CUDD package. +

+ Modified by Abelardo Pardo to interface it to VIS. + ] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + + Revision [$Id: cudd.h,v 1.1.1.1 2003/02/24 22:23:50 wjiang Exp $] + +******************************************************************************/ + +#ifndef _CUDD +#define _CUDD + +/*---------------------------------------------------------------------------*/ +/* Nested includes */ +/*---------------------------------------------------------------------------*/ + +#include "mtr.h" +#include "epd.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define CUDD_VERSION "2.3.1" + +#ifndef SIZEOF_VOID_P +#define SIZEOF_VOID_P 4 +#endif +#ifndef SIZEOF_INT +#define SIZEOF_INT 4 +#endif +#ifndef SIZEOF_LONG +#define SIZEOF_LONG 4 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#define CUDD_VALUE_TYPE double +#define CUDD_OUT_OF_MEM -1 +/* The sizes of the subtables and the cache must be powers of two. */ +#define CUDD_UNIQUE_SLOTS 256 /* initial size of subtables */ +#define CUDD_CACHE_SLOTS 262144 /* default size of the cache */ + +/* Constants for residue functions. */ +#define CUDD_RESIDUE_DEFAULT 0 +#define CUDD_RESIDUE_MSB 1 +#define CUDD_RESIDUE_TC 2 + +/* CUDD_MAXINDEX is defined in such a way that on 32-bit and 64-bit +** machines one can cast an index to (int) without generating a negative +** number. +*/ +#if SIZEOF_VOID_P == 8 && SIZEOF_INT == 4 +#define CUDD_MAXINDEX (((DdHalfWord) ~0) >> 1) +#else +#define CUDD_MAXINDEX ((DdHalfWord) ~0) +#endif + +/* CUDD_CONST_INDEX is the index of constant nodes. Currently this +** is a synonim for CUDD_MAXINDEX. */ +#define CUDD_CONST_INDEX CUDD_MAXINDEX + +/* These constants define the digits used in the representation of +** arbitrary precision integers. The two configurations tested use 8 +** and 16 bits for each digit. The typedefs should be in agreement +** with these definitions. +*/ +#define DD_APA_BITS 16 +#define DD_APA_BASE (1 << DD_APA_BITS) +#define DD_APA_MASK (DD_APA_BASE - 1) +#define DD_APA_HEXPRINT "%04x" + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/**Enum************************************************************************ + + Synopsis [Type of reordering algorithm.] + + Description [Type of reordering algorithm.] + +******************************************************************************/ +typedef enum { + CUDD_REORDER_SAME, + CUDD_REORDER_NONE, + CUDD_REORDER_RANDOM, + CUDD_REORDER_RANDOM_PIVOT, + CUDD_REORDER_SIFT, + CUDD_REORDER_SIFT_CONVERGE, + CUDD_REORDER_SYMM_SIFT, + CUDD_REORDER_SYMM_SIFT_CONV, + CUDD_REORDER_WINDOW2, + CUDD_REORDER_WINDOW3, + CUDD_REORDER_WINDOW4, + CUDD_REORDER_WINDOW2_CONV, + CUDD_REORDER_WINDOW3_CONV, + CUDD_REORDER_WINDOW4_CONV, + CUDD_REORDER_GROUP_SIFT, + CUDD_REORDER_GROUP_SIFT_CONV, + CUDD_REORDER_ANNEALING, + CUDD_REORDER_GENETIC, + CUDD_REORDER_LINEAR, + CUDD_REORDER_LINEAR_CONVERGE, + CUDD_REORDER_LAZY_SIFT, + CUDD_REORDER_EXACT +} Cudd_ReorderingType; + + +/**Enum************************************************************************ + + Synopsis [Type of aggregation methods.] + + Description [Type of aggregation methods.] + +******************************************************************************/ +typedef enum { + CUDD_NO_CHECK, + CUDD_GROUP_CHECK, + CUDD_GROUP_CHECK2, + CUDD_GROUP_CHECK3, + CUDD_GROUP_CHECK4, + CUDD_GROUP_CHECK5, + CUDD_GROUP_CHECK6, + CUDD_GROUP_CHECK7, + CUDD_GROUP_CHECK8, + CUDD_GROUP_CHECK9 +} Cudd_AggregationType; + + +/**Enum************************************************************************ + + Synopsis [Type of hooks.] + + Description [Type of hooks.] + +******************************************************************************/ +typedef enum { + CUDD_PRE_GC_HOOK, + CUDD_POST_GC_HOOK, + CUDD_PRE_REORDERING_HOOK, + CUDD_POST_REORDERING_HOOK +} Cudd_HookType; + + +/**Enum************************************************************************ + + Synopsis [Type of error codes.] + + Description [Type of error codes.] + +******************************************************************************/ +typedef enum { + CUDD_NO_ERROR, + CUDD_MEMORY_OUT, + CUDD_TOO_MANY_NODES, + CUDD_MAX_MEM_EXCEEDED, + CUDD_INVALID_ARG, + CUDD_INTERNAL_ERROR +} Cudd_ErrorType; + + +/**Enum************************************************************************ + + Synopsis [Group type for lazy sifting.] + + Description [Group type for lazy sifting.] + +******************************************************************************/ +typedef enum { + CUDD_LAZY_NONE, + CUDD_LAZY_SOFT_GROUP, + CUDD_LAZY_HARD_GROUP, + CUDD_LAZY_UNGROUP +} Cudd_LazyGroupType; + + +/**Enum************************************************************************ + + Synopsis [Variable type.] + + Description [Variable type. Currently used only in lazy sifting.] + +******************************************************************************/ +typedef enum { + CUDD_VAR_PRIMARY_INPUT, + CUDD_VAR_PRESENT_STATE, + CUDD_VAR_NEXT_STATE +} Cudd_VariableType; + + +#if SIZEOF_VOID_P == 8 && SIZEOF_INT == 4 +typedef unsigned int DdHalfWord; +#else +typedef unsigned short DdHalfWord; +#endif + +#ifdef __osf__ +#pragma pointer_size save +#pragma pointer_size short +#endif + +typedef struct DdNode DdNode; + +typedef struct DdChildren { + struct DdNode *T; + struct DdNode *E; +} DdChildren; + +/* The DdNode structure is the only one exported out of the package */ +struct DdNode { + DdHalfWord index; + DdHalfWord ref; /* reference count */ + DdNode *next; /* next pointer for unique table */ + union { + CUDD_VALUE_TYPE value; /* for constant nodes */ + DdChildren kids; /* for internal nodes */ + } type; +}; + +#ifdef __osf__ +#pragma pointer_size restore +#endif + +typedef struct DdManager DdManager; + +typedef struct DdGen DdGen; + +/* These typedefs for arbitrary precision arithmetic should agree with +** the corresponding constant definitions above. */ +typedef unsigned short int DdApaDigit; +typedef unsigned long int DdApaDoubleDigit; +typedef DdApaDigit * DdApaNumber; + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**Macro*********************************************************************** + + Synopsis [Returns 1 if the node is a constant node.] + + Description [Returns 1 if the node is a constant node (rather than an + internal node). All constant nodes have the same index + (CUDD_CONST_INDEX). The pointer passed to Cudd_IsConstant may be either + regular or complemented.] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +#define Cudd_IsConstant(node) ((Cudd_Regular(node))->index == CUDD_CONST_INDEX) + + +/**Macro*********************************************************************** + + Synopsis [Complements a DD.] + + Description [Complements a DD by flipping the complement attribute of + the pointer (the least significant bit).] + + SideEffects [none] + + SeeAlso [Cudd_NotCond] + +******************************************************************************/ +#define Cudd_Not(node) ((DdNode *)((long)(node) ^ 01)) + + +/**Macro*********************************************************************** + + Synopsis [Complements a DD if a condition is true.] + + Description [Complements a DD if condition c is true; c should be + either 0 or 1, because it is used directly (for efficiency). If in + doubt on the values c may take, use "(c) ? Cudd_Not(node) : node".] + + SideEffects [none] + + SeeAlso [Cudd_Not] + +******************************************************************************/ +#define Cudd_NotCond(node,c) ((DdNode *)((long)(node) ^ (c))) + + +/**Macro*********************************************************************** + + Synopsis [Returns the regular version of a pointer.] + + Description [] + + SideEffects [none] + + SeeAlso [Cudd_Complement Cudd_IsComplement] + +******************************************************************************/ +#define Cudd_Regular(node) ((DdNode *)((unsigned long)(node) & ~01)) + + +/**Macro*********************************************************************** + + Synopsis [Returns the complemented version of a pointer.] + + Description [] + + SideEffects [none] + + SeeAlso [Cudd_Regular Cudd_IsComplement] + +******************************************************************************/ +#define Cudd_Complement(node) ((DdNode *)((unsigned long)(node) | 01)) + + +/**Macro*********************************************************************** + + Synopsis [Returns 1 if a pointer is complemented.] + + Description [] + + SideEffects [none] + + SeeAlso [Cudd_Regular Cudd_Complement] + +******************************************************************************/ +#define Cudd_IsComplement(node) ((int) ((long) (node) & 01)) + + +/**Macro*********************************************************************** + + Synopsis [Returns the then child of an internal node.] + + Description [Returns the then child of an internal node. If + node is a constant node, the result is unpredictable.] + + SideEffects [none] + + SeeAlso [Cudd_E Cudd_V] + +******************************************************************************/ +#define Cudd_T(node) ((Cudd_Regular(node))->type.kids.T) + + +/**Macro*********************************************************************** + + Synopsis [Returns the else child of an internal node.] + + Description [Returns the else child of an internal node. If + node is a constant node, the result is unpredictable.] + + SideEffects [none] + + SeeAlso [Cudd_T Cudd_V] + +******************************************************************************/ +#define Cudd_E(node) ((Cudd_Regular(node))->type.kids.E) + + +/**Macro*********************************************************************** + + Synopsis [Returns the value of a constant node.] + + Description [Returns the value of a constant node. If + node is an internal node, the result is unpredictable.] + + SideEffects [none] + + SeeAlso [Cudd_T Cudd_E] + +******************************************************************************/ +#define Cudd_V(node) ((Cudd_Regular(node))->type.value) + + +/**Macro*********************************************************************** + + Synopsis [Returns the current position in the order of variable + index.] + + Description [Returns the current position in the order of variable + index. This macro is obsolete and is kept for compatibility. New + applications should use Cudd_ReadPerm instead.] + + SideEffects [none] + + SeeAlso [Cudd_ReadPerm] + +******************************************************************************/ +#define Cudd_ReadIndex(dd,index) (Cudd_ReadPerm(dd,index)) + + +/**Macro*********************************************************************** + + Synopsis [Iterates over the cubes of a decision diagram.] + + Description [Iterates over the cubes of a decision diagram f. +
    +
  • DdManager *manager; +
  • DdNode *f; +
  • DdGen *gen; +
  • int *cube; +
  • CUDD_VALUE_TYPE value; +
+ Cudd_ForeachCube allocates and frees the generator. Therefore the + application should not try to do that. Also, the cube is freed at the + end of Cudd_ForeachCube and hence is not available outside of the loop.

+ CAUTION: It is assumed that dynamic reordering will not occur while + there are open generators. It is the user's responsibility to make sure + that dynamic reordering does not occur. As long as new nodes are not created + during generation, and dynamic reordering is not called explicitly, + dynamic reordering will not occur. Alternatively, it is sufficient to + disable dynamic reordering. It is a mistake to dispose of a diagram + on which generation is ongoing.] + + SideEffects [none] + + SeeAlso [Cudd_ForeachNode Cudd_FirstCube Cudd_NextCube Cudd_GenFree + Cudd_IsGenEmpty Cudd_AutodynDisable] + +******************************************************************************/ +#define Cudd_ForeachCube(manager, f, gen, cube, value)\ + for((gen) = Cudd_FirstCube(manager, f, &cube, &value);\ + Cudd_IsGenEmpty(gen) ? Cudd_GenFree(gen) : TRUE;\ + (void) Cudd_NextCube(gen, &cube, &value)) + + +/**Macro*********************************************************************** + + Synopsis [Iterates over the nodes of a decision diagram.] + + Description [Iterates over the nodes of a decision diagram f. +

    +
  • DdManager *manager; +
  • DdNode *f; +
  • DdGen *gen; +
  • DdNode *node; +
+ The nodes are returned in a seemingly random order. + Cudd_ForeachNode allocates and frees the generator. Therefore the + application should not try to do that.

+ CAUTION: It is assumed that dynamic reordering will not occur while + there are open generators. It is the user's responsibility to make sure + that dynamic reordering does not occur. As long as new nodes are not created + during generation, and dynamic reordering is not called explicitly, + dynamic reordering will not occur. Alternatively, it is sufficient to + disable dynamic reordering. It is a mistake to dispose of a diagram + on which generation is ongoing.] + + SideEffects [none] + + SeeAlso [Cudd_ForeachCube Cudd_FirstNode Cudd_NextNode Cudd_GenFree + Cudd_IsGenEmpty Cudd_AutodynDisable] + +******************************************************************************/ +#define Cudd_ForeachNode(manager, f, gen, node)\ + for((gen) = Cudd_FirstNode(manager, f, &node);\ + Cudd_IsGenEmpty(gen) ? Cudd_GenFree(gen) : TRUE;\ + (void) Cudd_NextNode(gen, &node)) + + +/**Macro*********************************************************************** + + Synopsis [Iterates over the paths of a ZDD.] + + Description [Iterates over the paths of a ZDD f. +

    +
  • DdManager *manager; +
  • DdNode *f; +
  • DdGen *gen; +
  • int *path; +
+ Cudd_zddForeachPath allocates and frees the generator. Therefore the + application should not try to do that. Also, the path is freed at the + end of Cudd_zddForeachPath and hence is not available outside of the loop.

+ CAUTION: It is assumed that dynamic reordering will not occur while + there are open generators. It is the user's responsibility to make sure + that dynamic reordering does not occur. As long as new nodes are not created + during generation, and dynamic reordering is not called explicitly, + dynamic reordering will not occur. Alternatively, it is sufficient to + disable dynamic reordering. It is a mistake to dispose of a diagram + on which generation is ongoing.] + + SideEffects [none] + + SeeAlso [Cudd_zddFirstPath Cudd_zddNextPath Cudd_GenFree + Cudd_IsGenEmpty Cudd_AutodynDisable] + +******************************************************************************/ +#define Cudd_zddForeachPath(manager, f, gen, path)\ + for((gen) = Cudd_zddFirstPath(manager, f, &path);\ + Cudd_IsGenEmpty(gen) ? Cudd_GenFree(gen) : TRUE;\ + (void) Cudd_zddNextPath(gen, &path)) + + +/* These are potential duplicates. */ +#ifndef EXTERN +# ifdef __cplusplus +# define EXTERN extern "C" +# else +# define EXTERN extern +# endif +#endif +#ifndef ARGS +# if defined(__STDC__) || defined(__cplusplus) +# define ARGS(protos) protos /* ANSI C */ +# else /* !(__STDC__ || __cplusplus) */ +# define ARGS(protos) () /* K&R C */ +# endif /* !(__STDC__ || __cplusplus) */ +#endif + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Function prototypes */ +/*---------------------------------------------------------------------------*/ + +EXTERN DdNode * Cudd_addNewVar ARGS((DdManager *dd)); +EXTERN DdNode * Cudd_addNewVarAtLevel ARGS((DdManager *dd, int level)); +EXTERN DdNode * Cudd_bddNewVar ARGS((DdManager *dd)); +EXTERN DdNode * Cudd_bddNewVarAtLevel ARGS((DdManager *dd, int level)); +EXTERN DdNode * Cudd_addIthVar ARGS((DdManager *dd, int i)); +EXTERN DdNode * Cudd_bddIthVar ARGS((DdManager *dd, int i)); +EXTERN DdNode * Cudd_zddIthVar ARGS((DdManager *dd, int i)); +EXTERN int Cudd_zddVarsFromBddVars ARGS((DdManager *dd, int multiplicity)); +EXTERN DdNode * Cudd_addConst ARGS((DdManager *dd, CUDD_VALUE_TYPE c)); +EXTERN int Cudd_IsNonConstant ARGS((DdNode *f)); +EXTERN void Cudd_AutodynEnable ARGS((DdManager *unique, Cudd_ReorderingType method)); +EXTERN void Cudd_AutodynDisable ARGS((DdManager *unique)); +EXTERN int Cudd_ReorderingStatus ARGS((DdManager *unique, Cudd_ReorderingType *method)); +EXTERN void Cudd_AutodynEnableZdd ARGS((DdManager *unique, Cudd_ReorderingType method)); +EXTERN void Cudd_AutodynDisableZdd ARGS((DdManager *unique)); +EXTERN int Cudd_ReorderingStatusZdd ARGS((DdManager *unique, Cudd_ReorderingType *method)); +EXTERN int Cudd_zddRealignmentEnabled ARGS((DdManager *unique)); +EXTERN void Cudd_zddRealignEnable ARGS((DdManager *unique)); +EXTERN void Cudd_zddRealignDisable ARGS((DdManager *unique)); +EXTERN int Cudd_bddRealignmentEnabled ARGS((DdManager *unique)); +EXTERN void Cudd_bddRealignEnable ARGS((DdManager *unique)); +EXTERN void Cudd_bddRealignDisable ARGS((DdManager *unique)); +EXTERN DdNode * Cudd_ReadOne ARGS((DdManager *dd)); +EXTERN DdNode * Cudd_ReadZddOne ARGS((DdManager *dd, int i)); +EXTERN DdNode * Cudd_ReadZero ARGS((DdManager *dd)); +EXTERN DdNode * Cudd_ReadLogicZero ARGS((DdManager *dd)); +EXTERN DdNode * Cudd_ReadPlusInfinity ARGS((DdManager *dd)); +EXTERN DdNode * Cudd_ReadMinusInfinity ARGS((DdManager *dd)); +EXTERN DdNode * Cudd_ReadBackground ARGS((DdManager *dd)); +EXTERN void Cudd_SetBackground ARGS((DdManager *dd, DdNode *bck)); +EXTERN unsigned int Cudd_ReadCacheSlots ARGS((DdManager *dd)); +EXTERN double Cudd_ReadCacheUsedSlots ARGS((DdManager * dd)); +EXTERN double Cudd_ReadCacheLookUps ARGS((DdManager *dd)); +EXTERN double Cudd_ReadCacheHits ARGS((DdManager *dd)); +EXTERN double Cudd_ReadRecursiveCalls ARGS ((DdManager * dd)); +EXTERN unsigned int Cudd_ReadMinHit ARGS((DdManager *dd)); +EXTERN void Cudd_SetMinHit ARGS((DdManager *dd, unsigned int hr)); +EXTERN unsigned int Cudd_ReadLooseUpTo ARGS((DdManager *dd)); +EXTERN void Cudd_SetLooseUpTo ARGS((DdManager *dd, unsigned int lut)); +EXTERN unsigned int Cudd_ReadMaxCache ARGS((DdManager *dd)); +EXTERN unsigned int Cudd_ReadMaxCacheHard ARGS((DdManager *dd)); +EXTERN void Cudd_SetMaxCacheHard ARGS((DdManager *dd, unsigned int mc)); +EXTERN int Cudd_ReadSize ARGS((DdManager *dd)); +EXTERN int Cudd_ReadZddSize ARGS((DdManager *dd)); +EXTERN unsigned int Cudd_ReadSlots ARGS((DdManager *dd)); +EXTERN double Cudd_ReadUsedSlots ARGS((DdManager * dd)); +EXTERN double Cudd_ExpectedUsedSlots ARGS((DdManager * dd)); +EXTERN unsigned int Cudd_ReadKeys ARGS((DdManager *dd)); +EXTERN unsigned int Cudd_ReadDead ARGS((DdManager *dd)); +EXTERN unsigned int Cudd_ReadMinDead ARGS((DdManager *dd)); +EXTERN int Cudd_ReadReorderings ARGS((DdManager *dd)); +EXTERN long Cudd_ReadReorderingTime ARGS((DdManager * dd)); +EXTERN int Cudd_ReadGarbageCollections ARGS((DdManager * dd)); +EXTERN long Cudd_ReadGarbageCollectionTime ARGS((DdManager * dd)); +EXTERN double Cudd_ReadNodesFreed ARGS((DdManager * dd)); +EXTERN double Cudd_ReadNodesDropped ARGS((DdManager * dd)); +EXTERN double Cudd_ReadUniqueLookUps ARGS((DdManager * dd)); +EXTERN double Cudd_ReadUniqueLinks ARGS((DdManager * dd)); +EXTERN int Cudd_ReadSiftMaxVar ARGS((DdManager *dd)); +EXTERN void Cudd_SetSiftMaxVar ARGS((DdManager *dd, int smv)); +EXTERN int Cudd_ReadSiftMaxSwap ARGS((DdManager *dd)); +EXTERN void Cudd_SetSiftMaxSwap ARGS((DdManager *dd, int sms)); +EXTERN double Cudd_ReadMaxGrowth ARGS((DdManager *dd)); +EXTERN void Cudd_SetMaxGrowth ARGS((DdManager *dd, double mg)); +EXTERN double Cudd_ReadMaxGrowthAlternate ARGS((DdManager * dd)); +EXTERN void Cudd_SetMaxGrowthAlternate ARGS((DdManager * dd, double mg)); +EXTERN int Cudd_ReadReorderingCycle ARGS((DdManager * dd)); +EXTERN void Cudd_SetReorderingCycle ARGS((DdManager * dd, int cycle)); +EXTERN MtrNode * Cudd_ReadTree ARGS((DdManager *dd)); +EXTERN void Cudd_SetTree ARGS((DdManager *dd, MtrNode *tree)); +EXTERN void Cudd_FreeTree ARGS((DdManager *dd)); +EXTERN MtrNode * Cudd_ReadZddTree ARGS((DdManager *dd)); +EXTERN void Cudd_SetZddTree ARGS((DdManager *dd, MtrNode *tree)); +EXTERN void Cudd_FreeZddTree ARGS((DdManager *dd)); +EXTERN unsigned int Cudd_NodeReadIndex ARGS((DdNode *node)); +EXTERN int Cudd_ReadPerm ARGS((DdManager *dd, int i)); +EXTERN int Cudd_ReadPermZdd ARGS((DdManager *dd, int i)); +EXTERN int Cudd_ReadInvPerm ARGS((DdManager *dd, int i)); +EXTERN int Cudd_ReadInvPermZdd ARGS((DdManager *dd, int i)); +EXTERN DdNode * Cudd_ReadVars ARGS((DdManager *dd, int i)); +EXTERN CUDD_VALUE_TYPE Cudd_ReadEpsilon ARGS((DdManager *dd)); +EXTERN void Cudd_SetEpsilon ARGS((DdManager *dd, CUDD_VALUE_TYPE ep)); +EXTERN Cudd_AggregationType Cudd_ReadGroupcheck ARGS((DdManager *dd)); +EXTERN void Cudd_SetGroupcheck ARGS((DdManager *dd, Cudd_AggregationType gc)); +EXTERN int Cudd_GarbageCollectionEnabled ARGS((DdManager *dd)); +EXTERN void Cudd_EnableGarbageCollection ARGS((DdManager *dd)); +EXTERN void Cudd_DisableGarbageCollection ARGS((DdManager *dd)); +EXTERN int Cudd_DeadAreCounted ARGS((DdManager *dd)); +EXTERN void Cudd_TurnOnCountDead ARGS((DdManager *dd)); +EXTERN void Cudd_TurnOffCountDead ARGS((DdManager *dd)); +EXTERN int Cudd_ReadRecomb ARGS((DdManager *dd)); +EXTERN void Cudd_SetRecomb ARGS((DdManager *dd, int recomb)); +EXTERN int Cudd_ReadSymmviolation ARGS((DdManager *dd)); +EXTERN void Cudd_SetSymmviolation ARGS((DdManager *dd, int symmviolation)); +EXTERN int Cudd_ReadArcviolation ARGS((DdManager *dd)); +EXTERN void Cudd_SetArcviolation ARGS((DdManager *dd, int arcviolation)); +EXTERN int Cudd_ReadPopulationSize ARGS((DdManager *dd)); +EXTERN void Cudd_SetPopulationSize ARGS((DdManager *dd, int populationSize)); +EXTERN int Cudd_ReadNumberXovers ARGS((DdManager *dd)); +EXTERN void Cudd_SetNumberXovers ARGS((DdManager *dd, int numberXovers)); +EXTERN long Cudd_ReadMemoryInUse ARGS((DdManager *dd)); +EXTERN int Cudd_PrintInfo ARGS((DdManager *dd, FILE *fp)); +EXTERN long Cudd_ReadPeakNodeCount ARGS((DdManager *dd)); +EXTERN int Cudd_ReadPeakLiveNodeCount ARGS((DdManager * dd)); +EXTERN long Cudd_ReadNodeCount ARGS((DdManager *dd)); +EXTERN long Cudd_zddReadNodeCount ARGS((DdManager *dd)); +EXTERN int Cudd_AddHook ARGS((DdManager *dd, int (*f)(DdManager *, char *, void *), Cudd_HookType where)); +EXTERN int Cudd_RemoveHook ARGS((DdManager *dd, int (*f)(DdManager *, char *, void *), Cudd_HookType where)); +EXTERN int Cudd_IsInHook ARGS((DdManager * dd, int (*f)(DdManager *, char *, void *), Cudd_HookType where)); +EXTERN int Cudd_StdPreReordHook ARGS((DdManager *dd, char *str, void *data)); +EXTERN int Cudd_StdPostReordHook ARGS((DdManager *dd, char *str, void *data)); +EXTERN int Cudd_EnableReorderingReporting ARGS((DdManager *dd)); +EXTERN int Cudd_DisableReorderingReporting ARGS((DdManager *dd)); +EXTERN int Cudd_ReorderingReporting ARGS((DdManager *dd)); +EXTERN Cudd_ErrorType Cudd_ReadErrorCode ARGS((DdManager *dd)); +EXTERN void Cudd_ClearErrorCode ARGS((DdManager *dd)); +EXTERN FILE * Cudd_ReadStdout ARGS((DdManager *dd)); +EXTERN void Cudd_SetStdout ARGS((DdManager *dd, FILE *fp)); +EXTERN FILE * Cudd_ReadStderr ARGS((DdManager *dd)); +EXTERN void Cudd_SetStderr ARGS((DdManager *dd, FILE *fp)); +EXTERN unsigned int Cudd_ReadNextReordering ARGS((DdManager *dd)); +EXTERN void Cudd_SetNextReordering ARGS((DdManager *dd, unsigned int next)); +EXTERN double Cudd_ReadSwapSteps ARGS((DdManager *dd)); +EXTERN unsigned int Cudd_ReadMaxLive ARGS((DdManager *dd)); +EXTERN void Cudd_SetMaxLive ARGS((DdManager *dd, unsigned int maxLive)); +EXTERN long Cudd_ReadMaxMemory ARGS((DdManager *dd)); +EXTERN void Cudd_SetMaxMemory ARGS((DdManager *dd, long maxMemory)); +EXTERN int Cudd_bddBindVar ARGS((DdManager *dd, int index)); +EXTERN int Cudd_bddUnbindVar ARGS((DdManager *dd, int index)); +EXTERN int Cudd_bddVarIsBound ARGS((DdManager *dd, int index)); +EXTERN DdNode * Cudd_addExistAbstract ARGS((DdManager *manager, DdNode *f, DdNode *cube)); +EXTERN DdNode * Cudd_addUnivAbstract ARGS((DdManager *manager, DdNode *f, DdNode *cube)); +EXTERN DdNode * Cudd_addOrAbstract ARGS((DdManager *manager, DdNode *f, DdNode *cube)); +EXTERN DdNode * Cudd_addApply ARGS((DdManager *dd, DdNode * (*)(DdManager *, DdNode **, DdNode **), DdNode *f, DdNode *g)); +EXTERN DdNode * Cudd_addPlus ARGS((DdManager *dd, DdNode **f, DdNode **g)); +EXTERN DdNode * Cudd_addTimes ARGS((DdManager *dd, DdNode **f, DdNode **g)); +EXTERN DdNode * Cudd_addThreshold ARGS((DdManager *dd, DdNode **f, DdNode **g)); +EXTERN DdNode * Cudd_addSetNZ ARGS((DdManager *dd, DdNode **f, DdNode **g)); +EXTERN DdNode * Cudd_addDivide ARGS((DdManager *dd, DdNode **f, DdNode **g)); +EXTERN DdNode * Cudd_addMinus ARGS((DdManager *dd, DdNode **f, DdNode **g)); +EXTERN DdNode * Cudd_addMinimum ARGS((DdManager *dd, DdNode **f, DdNode **g)); +EXTERN DdNode * Cudd_addMaximum ARGS((DdManager *dd, DdNode **f, DdNode **g)); +EXTERN DdNode * Cudd_addOneZeroMaximum ARGS((DdManager *dd, DdNode **f, DdNode **g)); +EXTERN DdNode * Cudd_addDiff ARGS((DdManager *dd, DdNode **f, DdNode **g)); +EXTERN DdNode * Cudd_addAgreement ARGS((DdManager *dd, DdNode **f, DdNode **g)); +EXTERN DdNode * Cudd_addOr ARGS((DdManager *dd, DdNode **f, DdNode **g)); +EXTERN DdNode * Cudd_addNand ARGS((DdManager *dd, DdNode **f, DdNode **g)); +EXTERN DdNode * Cudd_addNor ARGS((DdManager *dd, DdNode **f, DdNode **g)); +EXTERN DdNode * Cudd_addXor ARGS((DdManager *dd, DdNode **f, DdNode **g)); +EXTERN DdNode * Cudd_addXnor ARGS((DdManager *dd, DdNode **f, DdNode **g)); +EXTERN DdNode * Cudd_addMonadicApply ARGS((DdManager * dd, DdNode * (*op)(DdManager *, DdNode *), DdNode * f)); +EXTERN DdNode * Cudd_addLog ARGS((DdManager * dd, DdNode * f)); +EXTERN DdNode * Cudd_addFindMax ARGS((DdManager *dd, DdNode *f)); +EXTERN DdNode * Cudd_addFindMin ARGS((DdManager *dd, DdNode *f)); +EXTERN DdNode * Cudd_addIthBit ARGS((DdManager *dd, DdNode *f, int bit)); +EXTERN DdNode * Cudd_addScalarInverse ARGS((DdManager *dd, DdNode *f, DdNode *epsilon)); +EXTERN DdNode * Cudd_addIte ARGS((DdManager *dd, DdNode *f, DdNode *g, DdNode *h)); +EXTERN DdNode * Cudd_addIteConstant ARGS((DdManager *dd, DdNode *f, DdNode *g, DdNode *h)); +EXTERN DdNode * Cudd_addEvalConst ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN int Cudd_addLeq ARGS((DdManager * dd, DdNode * f, DdNode * g)); +EXTERN DdNode * Cudd_addCmpl ARGS((DdManager *dd, DdNode *f)); +EXTERN DdNode * Cudd_addNegate ARGS((DdManager *dd, DdNode *f)); +EXTERN DdNode * Cudd_addRoundOff ARGS((DdManager *dd, DdNode *f, int N)); +EXTERN DdNode * Cudd_addWalsh ARGS((DdManager *dd, DdNode **x, DdNode **y, int n)); +EXTERN DdNode * Cudd_addResidue ARGS((DdManager *dd, int n, int m, int options, int top)); +EXTERN DdNode * Cudd_bddAndAbstract ARGS((DdManager *manager, DdNode *f, DdNode *g, DdNode *cube)); +EXTERN int Cudd_ApaNumberOfDigits ARGS((int binaryDigits)); +EXTERN DdApaNumber Cudd_NewApaNumber ARGS((int digits)); +EXTERN void Cudd_ApaCopy ARGS((int digits, DdApaNumber source, DdApaNumber dest)); +EXTERN DdApaDigit Cudd_ApaAdd ARGS((int digits, DdApaNumber a, DdApaNumber b, DdApaNumber sum)); +EXTERN DdApaDigit Cudd_ApaSubtract ARGS((int digits, DdApaNumber a, DdApaNumber b, DdApaNumber diff)); +EXTERN DdApaDigit Cudd_ApaShortDivision ARGS((int digits, DdApaNumber dividend, DdApaDigit divisor, DdApaNumber quotient)); +EXTERN unsigned int Cudd_ApaIntDivision ARGS((int digits, DdApaNumber dividend, unsigned int divisor, DdApaNumber quotient)); +EXTERN void Cudd_ApaShiftRight ARGS((int digits, DdApaDigit in, DdApaNumber a, DdApaNumber b)); +EXTERN void Cudd_ApaSetToLiteral ARGS((int digits, DdApaNumber number, DdApaDigit literal)); +EXTERN void Cudd_ApaPowerOfTwo ARGS((int digits, DdApaNumber number, int power)); +EXTERN int Cudd_ApaCompare ARGS((int digitsFirst, DdApaNumber first, int digitsSecond, DdApaNumber second)); +EXTERN int Cudd_ApaCompareRatios ARGS ((int digitsFirst, DdApaNumber firstNum, unsigned int firstDen, int digitsSecond, DdApaNumber secondNum, unsigned int secondDen)); +EXTERN int Cudd_ApaPrintHex ARGS((FILE *fp, int digits, DdApaNumber number)); +EXTERN int Cudd_ApaPrintDecimal ARGS((FILE *fp, int digits, DdApaNumber number)); +EXTERN int Cudd_ApaPrintExponential ARGS((FILE * fp, int digits, DdApaNumber number, int precision)); +EXTERN DdApaNumber Cudd_ApaCountMinterm ARGS((DdManager *manager, DdNode *node, int nvars, int *digits)); +EXTERN int Cudd_ApaPrintMinterm ARGS((FILE *fp, DdManager *dd, DdNode *node, int nvars)); +EXTERN int Cudd_ApaPrintMintermExp ARGS((FILE * fp, DdManager * dd, DdNode * node, int nvars, int precision)); +EXTERN int Cudd_ApaPrintDensity ARGS((FILE * fp, DdManager * dd, DdNode * node, int nvars)); +EXTERN DdNode * Cudd_UnderApprox ARGS((DdManager *dd, DdNode *f, int numVars, int threshold, int safe, double quality)); +EXTERN DdNode * Cudd_OverApprox ARGS((DdManager *dd, DdNode *f, int numVars, int threshold, int safe, double quality)); +EXTERN DdNode * Cudd_RemapUnderApprox ARGS((DdManager *dd, DdNode *f, int numVars, int threshold, double quality)); +EXTERN DdNode * Cudd_RemapOverApprox ARGS((DdManager *dd, DdNode *f, int numVars, int threshold, double quality)); +EXTERN DdNode * Cudd_BiasedUnderApprox ARGS((DdManager *dd, DdNode *f, DdNode *b, int numVars, int threshold, double quality1, double quality0)); +EXTERN DdNode * Cudd_BiasedOverApprox ARGS((DdManager *dd, DdNode *f, DdNode *b, int numVars, int threshold, double quality1, double quality0)); +EXTERN DdNode * Cudd_bddExistAbstract ARGS((DdManager *manager, DdNode *f, DdNode *cube)); +EXTERN DdNode * Cudd_bddXorExistAbstract ARGS((DdManager *manager, DdNode *f, DdNode *g, DdNode *cube)); +EXTERN DdNode * Cudd_bddUnivAbstract ARGS((DdManager *manager, DdNode *f, DdNode *cube)); +EXTERN DdNode * Cudd_bddBooleanDiff ARGS((DdManager *manager, DdNode *f, int x)); +EXTERN int Cudd_bddVarIsDependent ARGS((DdManager *dd, DdNode *f, DdNode *var)); +EXTERN double Cudd_bddCorrelation ARGS((DdManager *manager, DdNode *f, DdNode *g)); +EXTERN double Cudd_bddCorrelationWeights ARGS((DdManager *manager, DdNode *f, DdNode *g, double *prob)); +EXTERN DdNode * Cudd_bddIte ARGS((DdManager *dd, DdNode *f, DdNode *g, DdNode *h)); +EXTERN DdNode * Cudd_bddIteConstant ARGS((DdManager *dd, DdNode *f, DdNode *g, DdNode *h)); +EXTERN DdNode * Cudd_bddIntersect ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * Cudd_bddAnd ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * Cudd_bddOr ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * Cudd_bddNand ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * Cudd_bddNor ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * Cudd_bddXor ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * Cudd_bddXnor ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN int Cudd_bddLeq ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * Cudd_addBddThreshold ARGS((DdManager *dd, DdNode *f, CUDD_VALUE_TYPE value)); +EXTERN DdNode * Cudd_addBddStrictThreshold ARGS((DdManager *dd, DdNode *f, CUDD_VALUE_TYPE value)); +EXTERN DdNode * Cudd_addBddInterval ARGS((DdManager *dd, DdNode *f, CUDD_VALUE_TYPE lower, CUDD_VALUE_TYPE upper)); +EXTERN DdNode * Cudd_addBddIthBit ARGS((DdManager *dd, DdNode *f, int bit)); +EXTERN DdNode * Cudd_BddToAdd ARGS((DdManager *dd, DdNode *B)); +EXTERN DdNode * Cudd_addBddPattern ARGS((DdManager *dd, DdNode *f)); +EXTERN DdNode * Cudd_bddTransfer ARGS((DdManager *ddSource, DdManager *ddDestination, DdNode *f)); +EXTERN int Cudd_DebugCheck ARGS((DdManager *table)); +EXTERN int Cudd_CheckKeys ARGS((DdManager *table)); +EXTERN DdNode * Cudd_bddClippingAnd ARGS((DdManager *dd, DdNode *f, DdNode *g, int maxDepth, int direction)); +EXTERN DdNode * Cudd_bddClippingAndAbstract ARGS((DdManager *dd, DdNode *f, DdNode *g, DdNode *cube, int maxDepth, int direction)); +EXTERN DdNode * Cudd_Cofactor ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * Cudd_bddCompose ARGS((DdManager *dd, DdNode *f, DdNode *g, int v)); +EXTERN DdNode * Cudd_addCompose ARGS((DdManager *dd, DdNode *f, DdNode *g, int v)); +EXTERN DdNode * Cudd_addPermute ARGS((DdManager *manager, DdNode *node, int *permut)); +EXTERN DdNode * Cudd_addSwapVariables ARGS((DdManager *dd, DdNode *f, DdNode **x, DdNode **y, int n)); +EXTERN DdNode * Cudd_bddPermute ARGS((DdManager *manager, DdNode *node, int *permut)); +EXTERN DdNode * Cudd_bddVarMap ARGS((DdManager *manager, DdNode *f)); +EXTERN int Cudd_SetVarMap ARGS((DdManager *manager, DdNode **x, DdNode **y, int n)); +EXTERN DdNode * Cudd_bddSwapVariables ARGS((DdManager *dd, DdNode *f, DdNode **x, DdNode **y, int n)); +EXTERN DdNode * Cudd_bddAdjPermuteX ARGS((DdManager *dd, DdNode *B, DdNode **x, int n)); +EXTERN DdNode * Cudd_addVectorCompose ARGS((DdManager *dd, DdNode *f, DdNode **vector)); +EXTERN DdNode * Cudd_addGeneralVectorCompose ARGS((DdManager *dd, DdNode *f, DdNode **vectorOn, DdNode **vectorOff)); +EXTERN DdNode * Cudd_addNonSimCompose ARGS((DdManager *dd, DdNode *f, DdNode **vector)); +EXTERN DdNode * Cudd_bddVectorCompose ARGS((DdManager *dd, DdNode *f, DdNode **vector)); +EXTERN int Cudd_bddApproxConjDecomp ARGS((DdManager *dd, DdNode *f, DdNode ***conjuncts)); +EXTERN int Cudd_bddApproxDisjDecomp ARGS((DdManager *dd, DdNode *f, DdNode ***disjuncts)); +EXTERN int Cudd_bddIterConjDecomp ARGS((DdManager *dd, DdNode *f, DdNode ***conjuncts)); +EXTERN int Cudd_bddIterDisjDecomp ARGS((DdManager *dd, DdNode *f, DdNode ***disjuncts)); +EXTERN int Cudd_bddGenConjDecomp ARGS((DdManager *dd, DdNode *f, DdNode ***conjuncts)); +EXTERN int Cudd_bddGenDisjDecomp ARGS((DdManager *dd, DdNode *f, DdNode ***disjuncts)); +EXTERN int Cudd_bddVarConjDecomp ARGS((DdManager *dd, DdNode * f, DdNode ***conjuncts)); +EXTERN int Cudd_bddVarDisjDecomp ARGS((DdManager *dd, DdNode * f, DdNode ***disjuncts)); +EXTERN DdNode * Cudd_FindEssential ARGS((DdManager *dd, DdNode *f)); +EXTERN int Cudd_bddIsVarEssential ARGS((DdManager *manager, DdNode *f, int id, int phase)); +EXTERN int Cudd_DumpBlif ARGS((DdManager *dd, int n, DdNode **f, char **inames, char **onames, char *mname, FILE *fp)); +EXTERN int Cudd_DumpBlifBody ARGS((DdManager *dd, int n, DdNode **f, char **inames, char **onames, FILE *fp)); +EXTERN int Cudd_DumpDot ARGS((DdManager *dd, int n, DdNode **f, char **inames, char **onames, FILE *fp)); +EXTERN int Cudd_DumpDaVinci ARGS((DdManager *dd, int n, DdNode **f, char **inames, char **onames, FILE *fp)); +EXTERN int Cudd_DumpDDcal ARGS((DdManager *dd, int n, DdNode **f, char **inames, char **onames, FILE *fp)); +EXTERN int Cudd_DumpFactoredForm ARGS((DdManager *dd, int n, DdNode **f, char **inames, char **onames, FILE *fp)); +EXTERN DdNode * Cudd_bddConstrain ARGS((DdManager *dd, DdNode *f, DdNode *c)); +EXTERN DdNode * Cudd_bddRestrict ARGS((DdManager *dd, DdNode *f, DdNode *c)); +EXTERN DdNode * Cudd_addConstrain ARGS((DdManager *dd, DdNode *f, DdNode *c)); +EXTERN DdNode ** Cudd_bddConstrainDecomp ARGS((DdManager *dd, DdNode *f)); +EXTERN DdNode * Cudd_addRestrict ARGS((DdManager *dd, DdNode *f, DdNode *c)); +EXTERN DdNode ** Cudd_bddCharToVect ARGS((DdManager *dd, DdNode *f)); +EXTERN DdNode * Cudd_bddLICompaction ARGS((DdManager *dd, DdNode *f, DdNode *c)); +EXTERN DdNode * Cudd_bddSqueeze ARGS((DdManager *dd, DdNode *l, DdNode *u)); +EXTERN DdNode * Cudd_bddMinimize ARGS((DdManager *dd, DdNode *f, DdNode *c)); +EXTERN DdNode * Cudd_SubsetCompress ARGS((DdManager *dd, DdNode *f, int nvars, int threshold)); +EXTERN DdNode * Cudd_SupersetCompress ARGS((DdManager *dd, DdNode *f, int nvars, int threshold)); +EXTERN MtrNode * Cudd_MakeTreeNode ARGS((DdManager *dd, unsigned int low, unsigned int size, unsigned int type)); +EXTERN int Cudd_addHarwell ARGS((FILE *fp, DdManager *dd, DdNode **E, DdNode ***x, DdNode ***y, DdNode ***xn, DdNode ***yn_, int *nx, int *ny, int *m, int *n, int bx, int sx, int by, int sy, int pr)); +EXTERN DdManager * Cudd_Init ARGS((unsigned int numVars, unsigned int numVarsZ, unsigned int numSlots, unsigned int cacheSize, unsigned long maxMemory)); +EXTERN void Cudd_Quit ARGS((DdManager *unique)); +EXTERN int Cudd_PrintLinear ARGS((DdManager *table)); +EXTERN int Cudd_ReadLinear ARGS((DdManager *table, int x, int y)); +EXTERN DdNode * Cudd_bddLiteralSetIntersection ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * Cudd_addMatrixMultiply ARGS((DdManager *dd, DdNode *A, DdNode *B, DdNode **z, int nz)); +EXTERN DdNode * Cudd_addTimesPlus ARGS((DdManager *dd, DdNode *A, DdNode *B, DdNode **z, int nz)); +EXTERN DdNode * Cudd_addTriangle ARGS((DdManager *dd, DdNode *f, DdNode *g, DdNode **z, int nz)); +EXTERN DdNode * Cudd_addOuterSum ARGS((DdManager *dd, DdNode *M, DdNode *r, DdNode *c)); +EXTERN DdNode * Cudd_PrioritySelect ARGS((DdManager *dd, DdNode *R, DdNode **x, DdNode **y, DdNode **z, DdNode *Pi, int n, DdNode * (*)(DdManager *, int, DdNode **, DdNode **, DdNode **))); +EXTERN DdNode * Cudd_Xgty ARGS((DdManager *dd, int N, DdNode **z, DdNode **x, DdNode **y)); +EXTERN DdNode * Cudd_Xeqy ARGS((DdManager *dd, int N, DdNode **x, DdNode **y)); +EXTERN DdNode * Cudd_addXeqy ARGS((DdManager *dd, int N, DdNode **x, DdNode **y)); +EXTERN DdNode * Cudd_Dxygtdxz ARGS((DdManager *dd, int N, DdNode **x, DdNode **y, DdNode **z)); +EXTERN DdNode * Cudd_Dxygtdyz ARGS((DdManager *dd, int N, DdNode **x, DdNode **y, DdNode **z)); +EXTERN DdNode * Cudd_CProjection ARGS((DdManager *dd, DdNode *R, DdNode *Y)); +EXTERN DdNode * Cudd_addHamming ARGS((DdManager *dd, DdNode **xVars, DdNode **yVars, int nVars)); +EXTERN int Cudd_MinHammingDist ARGS((DdManager *dd, DdNode *f, int *minterm, int upperBound)); +EXTERN DdNode * Cudd_bddClosestCube ARGS((DdManager *dd, DdNode * f, DdNode *g, int *distance)); +EXTERN int Cudd_addRead ARGS((FILE *fp, DdManager *dd, DdNode **E, DdNode ***x, DdNode ***y, DdNode ***xn, DdNode ***yn_, int *nx, int *ny, int *m, int *n, int bx, int sx, int by, int sy)); +EXTERN int Cudd_bddRead ARGS((FILE *fp, DdManager *dd, DdNode **E, DdNode ***x, DdNode ***y, int *nx, int *ny, int *m, int *n, int bx, int sx, int by, int sy)); +EXTERN void Cudd_Ref ARGS((DdNode *n)); +EXTERN void Cudd_RecursiveDeref ARGS((DdManager *table, DdNode *n)); +EXTERN void Cudd_IterDerefBdd ARGS((DdManager *table, DdNode *n)); +EXTERN void Cudd_DelayedDerefBdd ARGS((DdManager * table, DdNode * n)); +EXTERN void Cudd_RecursiveDerefZdd ARGS((DdManager *table, DdNode *n)); +EXTERN void Cudd_Deref ARGS((DdNode *node)); +EXTERN int Cudd_CheckZeroRef ARGS((DdManager *manager)); +EXTERN int Cudd_ReduceHeap ARGS((DdManager *table, Cudd_ReorderingType heuristic, int minsize)); +EXTERN int Cudd_ShuffleHeap ARGS((DdManager *table, int *permutation)); +EXTERN DdNode * Cudd_Eval ARGS((DdManager *dd, DdNode *f, int *inputs)); +EXTERN DdNode * Cudd_ShortestPath ARGS((DdManager *manager, DdNode *f, int *weight, int *support, int *length)); +EXTERN DdNode * Cudd_LargestCube ARGS((DdManager *manager, DdNode *f, int *length)); +EXTERN int Cudd_ShortestLength ARGS((DdManager *manager, DdNode *f, int *weight)); +EXTERN DdNode * Cudd_Decreasing ARGS((DdManager *dd, DdNode *f, int i)); +EXTERN DdNode * Cudd_Increasing ARGS((DdManager *dd, DdNode *f, int i)); +EXTERN int Cudd_EquivDC ARGS((DdManager *dd, DdNode *F, DdNode *G, DdNode *D)); +EXTERN int Cudd_bddLeqUnless ARGS((DdManager *dd, DdNode *f, DdNode *g, DdNode *D)); +EXTERN int Cudd_EqualSupNorm ARGS((DdManager *dd, DdNode *f, DdNode *g, CUDD_VALUE_TYPE tolerance, int pr)); +EXTERN DdNode * Cudd_bddMakePrime ARGS ((DdManager *dd, DdNode *cube, DdNode *f)); +EXTERN double * Cudd_CofMinterm ARGS((DdManager *dd, DdNode *node)); +EXTERN DdNode * Cudd_SolveEqn ARGS((DdManager * bdd, DdNode *F, DdNode *Y, DdNode **G, int **yIndex, int n)); +EXTERN DdNode * Cudd_VerifySol ARGS((DdManager * bdd, DdNode *F, DdNode **G, int *yIndex, int n)); +EXTERN DdNode * Cudd_SplitSet ARGS((DdManager *manager, DdNode *S, DdNode **xVars, int n, double m)); +EXTERN DdNode * Cudd_SubsetHeavyBranch ARGS((DdManager *dd, DdNode *f, int numVars, int threshold)); +EXTERN DdNode * Cudd_SupersetHeavyBranch ARGS((DdManager *dd, DdNode *f, int numVars, int threshold)); +EXTERN DdNode * Cudd_SubsetShortPaths ARGS((DdManager *dd, DdNode *f, int numVars, int threshold, int hardlimit)); +EXTERN DdNode * Cudd_SupersetShortPaths ARGS((DdManager *dd, DdNode *f, int numVars, int threshold, int hardlimit)); +EXTERN void Cudd_SymmProfile ARGS((DdManager *table, int lower, int upper)); +EXTERN unsigned int Cudd_Prime ARGS((unsigned int p)); +EXTERN int Cudd_PrintMinterm ARGS((DdManager *manager, DdNode *node)); +EXTERN int Cudd_bddPrintCover ARGS((DdManager *dd, DdNode *l, DdNode *u)); +EXTERN int Cudd_PrintDebug ARGS((DdManager *dd, DdNode *f, int n, int pr)); +EXTERN int Cudd_DagSize ARGS((DdNode *node)); +EXTERN int Cudd_EstimateCofactor ARGS((DdManager *dd, DdNode * node, int i, int phase)); +EXTERN int Cudd_EstimateCofactorSimple ARGS((DdNode * node, int i)); +EXTERN int Cudd_SharingSize ARGS((DdNode **nodeArray, int n)); +EXTERN double Cudd_CountMinterm ARGS((DdManager *manager, DdNode *node, int nvars)); +EXTERN int Cudd_EpdCountMinterm ARGS((DdManager *manager, DdNode *node, int nvars, EpDouble *epd)); +EXTERN double Cudd_CountPath ARGS((DdNode *node)); +EXTERN double Cudd_CountPathsToNonZero ARGS((DdNode *node)); +EXTERN DdNode * Cudd_Support ARGS((DdManager *dd, DdNode *f)); +EXTERN int * Cudd_SupportIndex ARGS((DdManager *dd, DdNode *f)); +EXTERN int Cudd_SupportSize ARGS((DdManager *dd, DdNode *f)); +EXTERN DdNode * Cudd_VectorSupport ARGS((DdManager *dd, DdNode **F, int n)); +EXTERN int * Cudd_VectorSupportIndex ARGS((DdManager *dd, DdNode **F, int n)); +EXTERN int Cudd_VectorSupportSize ARGS((DdManager *dd, DdNode **F, int n)); +EXTERN int Cudd_ClassifySupport ARGS((DdManager *dd, DdNode *f, DdNode *g, DdNode **common, DdNode **onlyF, DdNode **onlyG)); +EXTERN int Cudd_CountLeaves ARGS((DdNode *node)); +EXTERN int Cudd_bddPickOneCube ARGS((DdManager *ddm, DdNode *node, char *string)); +EXTERN DdNode * Cudd_bddPickOneMinterm ARGS((DdManager *dd, DdNode *f, DdNode **vars, int n)); +EXTERN DdNode ** Cudd_bddPickArbitraryMinterms ARGS((DdManager *dd, DdNode *f, DdNode **vars, int n, int k)); +EXTERN DdNode * Cudd_SubsetWithMaskVars ARGS((DdManager *dd, DdNode *f, DdNode **vars, int nvars, DdNode **maskVars, int mvars)); +EXTERN DdGen * Cudd_FirstCube ARGS((DdManager *dd, DdNode *f, int **cube, CUDD_VALUE_TYPE *value)); +EXTERN int Cudd_NextCube ARGS((DdGen *gen, int **cube, CUDD_VALUE_TYPE *value)); +EXTERN DdNode * Cudd_bddComputeCube ARGS((DdManager *dd, DdNode **vars, int *phase, int n)); +EXTERN DdNode * Cudd_addComputeCube ARGS((DdManager *dd, DdNode **vars, int *phase, int n)); +EXTERN DdNode * Cudd_CubeArrayToBdd ARGS((DdManager *dd, int *array)); +EXTERN int Cudd_BddToCubeArray ARGS((DdManager *dd, DdNode *cube, int *array)); +EXTERN DdGen * Cudd_FirstNode ARGS((DdManager *dd, DdNode *f, DdNode **node)); +EXTERN int Cudd_NextNode ARGS((DdGen *gen, DdNode **node)); +EXTERN int Cudd_GenFree ARGS((DdGen *gen)); +EXTERN int Cudd_IsGenEmpty ARGS((DdGen *gen)); +EXTERN DdNode * Cudd_IndicesToCube ARGS((DdManager *dd, int *array, int n)); +EXTERN void Cudd_PrintVersion ARGS((FILE *fp)); +EXTERN double Cudd_AverageDistance ARGS((DdManager *dd)); +EXTERN long Cudd_Random ARGS(()); +EXTERN void Cudd_Srandom ARGS((long seed)); +EXTERN double Cudd_Density ARGS((DdManager *dd, DdNode *f, int nvars)); +EXTERN void Cudd_OutOfMem ARGS((long size)); +EXTERN int Cudd_zddCount ARGS((DdManager *zdd, DdNode *P)); +EXTERN double Cudd_zddCountDouble ARGS((DdManager *zdd, DdNode *P)); +EXTERN DdNode * Cudd_zddProduct ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * Cudd_zddUnateProduct ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * Cudd_zddWeakDiv ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * Cudd_zddDivide ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * Cudd_zddWeakDivF ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * Cudd_zddDivideF ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * Cudd_zddComplement ARGS((DdManager *dd, DdNode *node)); +EXTERN MtrNode * Cudd_MakeZddTreeNode ARGS((DdManager *dd, unsigned int low, unsigned int size, unsigned int type)); +EXTERN DdNode * Cudd_zddIsop ARGS((DdManager *dd, DdNode *L, DdNode *U, DdNode **zdd_I)); +EXTERN DdNode * Cudd_bddIsop ARGS((DdManager *dd, DdNode *L, DdNode *U)); +EXTERN DdNode * Cudd_MakeBddFromZddCover ARGS((DdManager *dd, DdNode *node)); +EXTERN int Cudd_zddDagSize ARGS((DdNode *p_node)); +EXTERN double Cudd_zddCountMinterm ARGS((DdManager *zdd, DdNode *node, int path)); +EXTERN void Cudd_zddPrintSubtable ARGS((DdManager *table)); +EXTERN DdNode * Cudd_zddPortFromBdd ARGS((DdManager *dd, DdNode *B)); +EXTERN DdNode * Cudd_zddPortToBdd ARGS((DdManager *dd, DdNode *f)); +EXTERN int Cudd_zddReduceHeap ARGS((DdManager *table, Cudd_ReorderingType heuristic, int minsize)); +EXTERN int Cudd_zddShuffleHeap ARGS((DdManager *table, int *permutation)); +EXTERN DdNode * Cudd_zddIte ARGS((DdManager *dd, DdNode *f, DdNode *g, DdNode *h)); +EXTERN DdNode * Cudd_zddUnion ARGS((DdManager *dd, DdNode *P, DdNode *Q)); +EXTERN DdNode * Cudd_zddIntersect ARGS((DdManager *dd, DdNode *P, DdNode *Q)); +EXTERN DdNode * Cudd_zddDiff ARGS((DdManager *dd, DdNode *P, DdNode *Q)); +EXTERN DdNode * Cudd_zddDiffConst ARGS((DdManager *zdd, DdNode *P, DdNode *Q)); +EXTERN DdNode * Cudd_zddSubset1 ARGS((DdManager *dd, DdNode *P, int var)); +EXTERN DdNode * Cudd_zddSubset0 ARGS((DdManager *dd, DdNode *P, int var)); +EXTERN DdNode * Cudd_zddChange ARGS((DdManager *dd, DdNode *P, int var)); +EXTERN void Cudd_zddSymmProfile ARGS((DdManager *table, int lower, int upper)); +EXTERN int Cudd_zddPrintMinterm ARGS((DdManager *zdd, DdNode *node)); +EXTERN int Cudd_zddPrintCover ARGS((DdManager *zdd, DdNode *node)); +EXTERN int Cudd_zddPrintDebug ARGS((DdManager *zdd, DdNode *f, int n, int pr)); +EXTERN DdGen * Cudd_zddFirstPath ARGS((DdManager *zdd, DdNode *f, int **path)); +EXTERN int Cudd_zddNextPath ARGS((DdGen *gen, int **path)); +EXTERN char * Cudd_zddCoverPathToString ARGS((DdManager *zdd, int *path, char *str)); +EXTERN int Cudd_zddDumpDot ARGS((DdManager *dd, int n, DdNode **f, char **inames, char **onames, FILE *fp)); +EXTERN int Cudd_bddSetPiVar ARGS((DdManager *dd, int index)); +EXTERN int Cudd_bddSetPsVar ARGS((DdManager *dd, int index)); +EXTERN int Cudd_bddSetNsVar ARGS((DdManager *dd, int index)); +EXTERN int Cudd_bddIsPiVar ARGS((DdManager *dd, int index)); +EXTERN int Cudd_bddIsPsVar ARGS((DdManager *dd, int index)); +EXTERN int Cudd_bddIsNsVar ARGS((DdManager *dd, int index)); +EXTERN int Cudd_bddSetPairIndex ARGS((DdManager *dd, int index, int pairIndex)); +EXTERN int Cudd_bddReadPairIndex ARGS((DdManager *dd, int index)); +EXTERN int Cudd_bddSetVarToBeGrouped ARGS((DdManager *dd, int index)); +EXTERN int Cudd_bddSetVarHardGroup ARGS((DdManager *dd, int index)); +EXTERN int Cudd_bddResetVarToBeGrouped ARGS((DdManager *dd, int index)); +EXTERN int Cudd_bddIsVarToBeGrouped ARGS((DdManager *dd, int index)); +EXTERN int Cudd_bddSetVarToBeUngrouped ARGS((DdManager *dd, int index)); +EXTERN int Cudd_bddIsVarToBeUngrouped ARGS((DdManager *dd, int index)); +EXTERN int Cudd_bddIsVarHardGroup ARGS((DdManager *dd, int index)); + +/**AutomaticEnd***************************************************************/ + +#endif /* _CUDD */ diff --git a/abc_with_bb_support/src/bdd/cudd/cudd.make b/abc_with_bb_support/src/bdd/cudd/cudd.make new file mode 100644 index 000000000..05f5cc5f5 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cudd.make @@ -0,0 +1,42 @@ +CSRC += cuddAPI.c cuddAddAbs.c cuddAddApply.c cuddAddFind.c cuddAddIte.c \ + cuddAddInv.c cuddAddNeg.c cuddAddWalsh.c cuddAndAbs.c \ + cuddAnneal.c cuddApa.c cuddApprox.c cuddBddAbs.c cuddBddCorr.c\ + cuddBddIte.c cuddBridge.c cuddCache.c cuddCheck.c cuddClip.c \ + cuddCof.c cuddCompose.c cuddDecomp.c cuddEssent.c cuddExact.c \ + cuddExport.c cuddGenCof.c cuddGenetic.c \ + cuddGroup.c cuddHarwell.c cuddInit.c cuddInteract.c \ + cuddLCache.c cuddLevelQ.c \ + cuddLinear.c cuddLiteral.c cuddMatMult.c cuddPriority.c \ + cuddRead.c cuddRef.c cuddReorder.c cuddSat.c cuddSign.c \ + cuddSolve.c cuddSplit.c cuddSubsetHB.c cuddSubsetSP.c cuddSymmetry.c \ + cuddTable.c cuddUtil.c cuddWindow.c cuddZddCount.c cuddZddFuncs.c \ + cuddZddGroup.c cuddZddIsop.c cuddZddLin.c cuddZddMisc.c cuddZddPort.c \ + cuddZddReord.c cuddZddSetop.c cuddZddSymm.c cuddZddUtil.c + +HEADERS += cudd.h cuddInt.h +MISC += testcudd.c r7x8.1.mat doc/cudd.ps doc/cuddAllAbs.html doc/cuddAllDet.html \ + doc/cuddExtAbs.html doc/cuddExtDet.html doc/cuddIntro.css \ + doc/cuddIntro.html doc/footnode.html doc/img1.gif doc/img2.gif \ + doc/img3.gif doc/img4.gif doc/img5.gif doc/index.html \ + doc/node1.html doc/node2.html doc/node3.html doc/node4.html \ + doc/node5.html doc/node6.html doc/node7.html doc/node8.html \ + doc/icons/change_begin.gif \ + doc/icons/change_delete.gif \ + doc/icons/change_end.gif \ + doc/icons/contents_motif.gif \ + doc/icons/cross_ref_motif.gif \ + doc/icons/foot_motif.gif \ + doc/icons/image.gif \ + doc/icons/index_motif.gif \ + doc/icons/next_group_motif.gif \ + doc/icons/next_group_motif_gr.gif \ + doc/icons/next_motif.gif \ + doc/icons/next_motif_gr.gif \ + doc/icons/previous_group_motif.gif \ + doc/icons/previous_group_motif_gr.gif \ + doc/icons/previous_motif.gif \ + doc/icons/previous_motif_gr.gif \ + doc/icons/up_motif.gif \ + doc/icons/up_motif_gr.gif + +DEPENDENCYFILES = $(CSRC) diff --git a/abc_with_bb_support/src/bdd/cudd/cuddAPI.c b/abc_with_bb_support/src/bdd/cudd/cuddAPI.c new file mode 100644 index 000000000..6c2714775 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddAPI.c @@ -0,0 +1,4409 @@ +/**CFile*********************************************************************** + + FileName [cuddAPI.c] + + PackageName [cudd] + + Synopsis [Application interface functions.] + + Description [External procedures included in this module: +

    +
  • Cudd_addNewVar() +
  • Cudd_addNewVarAtLevel() +
  • Cudd_bddNewVar() +
  • Cudd_bddNewVarAtLevel() +
  • Cudd_addIthVar() +
  • Cudd_bddIthVar() +
  • Cudd_zddIthVar() +
  • Cudd_zddVarsFromBddVars() +
  • Cudd_addConst() +
  • Cudd_IsNonConstant() +
  • Cudd_AutodynEnable() +
  • Cudd_AutodynDisable() +
  • Cudd_ReorderingStatus() +
  • Cudd_AutodynEnableZdd() +
  • Cudd_AutodynDisableZdd() +
  • Cudd_ReorderingStatusZdd() +
  • Cudd_zddRealignmentEnabled() +
  • Cudd_zddRealignEnable() +
  • Cudd_zddRealignDisable() +
  • Cudd_bddRealignmentEnabled() +
  • Cudd_bddRealignEnable() +
  • Cudd_bddRealignDisable() +
  • Cudd_ReadOne() +
  • Cudd_ReadZddOne() +
  • Cudd_ReadZero() +
  • Cudd_ReadLogicZero() +
  • Cudd_ReadPlusInfinity() +
  • Cudd_ReadMinusInfinity() +
  • Cudd_ReadBackground() +
  • Cudd_SetBackground() +
  • Cudd_ReadCacheSlots() +
  • Cudd_ReadCacheUsedSlots() +
  • Cudd_ReadCacheLookUps() +
  • Cudd_ReadCacheHits() +
  • Cudd_ReadMinHit() +
  • Cudd_SetMinHit() +
  • Cudd_ReadLooseUpTo() +
  • Cudd_SetLooseUpTo() +
  • Cudd_ReadMaxCache() +
  • Cudd_ReadMaxCacheHard() +
  • Cudd_SetMaxCacheHard() +
  • Cudd_ReadSize() +
  • Cudd_ReadSlots() +
  • Cudd_ReadUsedSlots() +
  • Cudd_ExpectedUsedSlots() +
  • Cudd_ReadKeys() +
  • Cudd_ReadDead() +
  • Cudd_ReadMinDead() +
  • Cudd_ReadReorderings() +
  • Cudd_ReadReorderingTime() +
  • Cudd_ReadGarbageCollections() +
  • Cudd_ReadGarbageCollectionTime() +
  • Cudd_ReadNodesFreed() +
  • Cudd_ReadNodesDropped() +
  • Cudd_ReadUniqueLookUps() +
  • Cudd_ReadUniqueLinks() +
  • Cudd_ReadSiftMaxVar() +
  • Cudd_SetSiftMaxVar() +
  • Cudd_ReadMaxGrowth() +
  • Cudd_SetMaxGrowth() +
  • Cudd_ReadMaxGrowthAlternate() +
  • Cudd_SetMaxGrowthAlternate() +
  • Cudd_ReadReorderingCycle() +
  • Cudd_SetReorderingCycle() +
  • Cudd_ReadTree() +
  • Cudd_SetTree() +
  • Cudd_FreeTree() +
  • Cudd_ReadZddTree() +
  • Cudd_SetZddTree() +
  • Cudd_FreeZddTree() +
  • Cudd_NodeReadIndex() +
  • Cudd_ReadPerm() +
  • Cudd_ReadInvPerm() +
  • Cudd_ReadVars() +
  • Cudd_ReadEpsilon() +
  • Cudd_SetEpsilon() +
  • Cudd_ReadGroupCheck() +
  • Cudd_SetGroupcheck() +
  • Cudd_GarbageCollectionEnabled() +
  • Cudd_EnableGarbageCollection() +
  • Cudd_DisableGarbageCollection() +
  • Cudd_DeadAreCounted() +
  • Cudd_TurnOnCountDead() +
  • Cudd_TurnOffCountDead() +
  • Cudd_ReadRecomb() +
  • Cudd_SetRecomb() +
  • Cudd_ReadSymmviolation() +
  • Cudd_SetSymmviolation() +
  • Cudd_ReadArcviolation() +
  • Cudd_SetArcviolation() +
  • Cudd_ReadPopulationSize() +
  • Cudd_SetPopulationSize() +
  • Cudd_ReadNumberXovers() +
  • Cudd_SetNumberXovers() +
  • Cudd_ReadMemoryInUse() +
  • Cudd_PrintInfo() +
  • Cudd_ReadPeakNodeCount() +
  • Cudd_ReadPeakLiveNodeCount() +
  • Cudd_ReadNodeCount() +
  • Cudd_zddReadNodeCount() +
  • Cudd_AddHook() +
  • Cudd_RemoveHook() +
  • Cudd_IsInHook() +
  • Cudd_StdPreReordHook() +
  • Cudd_StdPostReordHook() +
  • Cudd_EnableReorderingReporting() +
  • Cudd_DisableReorderingReporting() +
  • Cudd_ReorderingReporting() +
  • Cudd_ReadErrorCode() +
  • Cudd_ClearErrorCode() +
  • Cudd_ReadStdout() +
  • Cudd_SetStdout() +
  • Cudd_ReadStderr() +
  • Cudd_SetStderr() +
  • Cudd_ReadNextReordering() +
  • Cudd_SetNextReordering() +
  • Cudd_ReadSwapSteps() +
  • Cudd_ReadMaxLive() +
  • Cudd_SetMaxLive() +
  • Cudd_ReadMaxMemory() +
  • Cudd_SetMaxMemory() +
  • Cudd_bddBindVar() +
  • Cudd_bddUnbindVar() +
  • Cudd_bddVarIsBound() +
  • Cudd_bddSetPiVar() +
  • Cudd_bddSetPsVar() +
  • Cudd_bddSetNsVar() +
  • Cudd_bddIsPiVar() +
  • Cudd_bddIsPsVar() +
  • Cudd_bddIsNsVar() +
  • Cudd_bddSetPairIndex() +
  • Cudd_bddReadPairIndex() +
  • Cudd_bddSetVarToBeGrouped() +
  • Cudd_bddSetVarHardGroup() +
  • Cudd_bddResetVarToBeGrouped() +
  • Cudd_bddIsVarToBeGrouped() +
  • Cudd_bddSetVarToBeUngrouped() +
  • Cudd_bddIsVarToBeUngrouped() +
  • Cudd_bddIsVarHardGroup() +
+ Static procedures included in this module: +
    +
  • fixVarTree() +
] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddAPI.c,v 1.1.1.1 2003/02/24 22:23:51 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static void fixVarTree ARGS((MtrNode *treenode, int *perm, int size)); +static int addMultiplicityGroups ARGS((DdManager *dd, MtrNode *treenode, int multiplicity, char *vmask, char *lmask)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Returns a new ADD variable.] + + Description [Creates a new ADD variable. The new variable has an + index equal to the largest previous index plus 1. Returns a + pointer to the new variable if successful; NULL otherwise. + An ADD variable differs from a BDD variable because it points to the + arithmetic zero, instead of having a complement pointer to 1. ] + + SideEffects [None] + + SeeAlso [Cudd_bddNewVar Cudd_addIthVar Cudd_addConst + Cudd_addNewVarAtLevel] + +******************************************************************************/ +DdNode * +Cudd_addNewVar( + DdManager * dd) +{ + DdNode *res; + + if ((unsigned int) dd->size >= CUDD_MAXINDEX - 1) return(NULL); + do { + dd->reordered = 0; + res = cuddUniqueInter(dd,dd->size,DD_ONE(dd),DD_ZERO(dd)); + } while (dd->reordered == 1); + + return(res); + +} /* end of Cudd_addNewVar */ + + +/**Function******************************************************************** + + Synopsis [Returns a new ADD variable at a specified level.] + + Description [Creates a new ADD variable. The new variable has an + index equal to the largest previous index plus 1 and is positioned at + the specified level in the order. Returns a pointer to the new + variable if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addNewVar Cudd_addIthVar Cudd_bddNewVarAtLevel] + +******************************************************************************/ +DdNode * +Cudd_addNewVarAtLevel( + DdManager * dd, + int level) +{ + DdNode *res; + + if ((unsigned int) dd->size >= CUDD_MAXINDEX - 1) return(NULL); + if (level >= dd->size) return(Cudd_addIthVar(dd,level)); + if (!cuddInsertSubtables(dd,1,level)) return(NULL); + do { + dd->reordered = 0; + res = cuddUniqueInter(dd,dd->size - 1,DD_ONE(dd),DD_ZERO(dd)); + } while (dd->reordered == 1); + + return(res); + +} /* end of Cudd_addNewVarAtLevel */ + + +/**Function******************************************************************** + + Synopsis [Returns a new BDD variable.] + + Description [Creates a new BDD variable. The new variable has an + index equal to the largest previous index plus 1. Returns a + pointer to the new variable if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addNewVar Cudd_bddIthVar Cudd_bddNewVarAtLevel] + +******************************************************************************/ +DdNode * +Cudd_bddNewVar( + DdManager * dd) +{ + DdNode *res; + + if ((unsigned int) dd->size >= CUDD_MAXINDEX - 1) return(NULL); + res = cuddUniqueInter(dd,dd->size,dd->one,Cudd_Not(dd->one)); + + return(res); + +} /* end of Cudd_bddNewVar */ + + +/**Function******************************************************************** + + Synopsis [Returns a new BDD variable at a specified level.] + + Description [Creates a new BDD variable. The new variable has an + index equal to the largest previous index plus 1 and is positioned at + the specified level in the order. Returns a pointer to the new + variable if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddNewVar Cudd_bddIthVar Cudd_addNewVarAtLevel] + +******************************************************************************/ +DdNode * +Cudd_bddNewVarAtLevel( + DdManager * dd, + int level) +{ + DdNode *res; + + if ((unsigned int) dd->size >= CUDD_MAXINDEX - 1) return(NULL); + if (level >= dd->size) return(Cudd_bddIthVar(dd,level)); + if (!cuddInsertSubtables(dd,1,level)) return(NULL); + res = dd->vars[dd->size - 1]; + + return(res); + +} /* end of Cudd_bddNewVarAtLevel */ + + +/**Function******************************************************************** + + Synopsis [Returns the ADD variable with index i.] + + Description [Retrieves the ADD variable with index i if it already + exists, or creates a new ADD variable. Returns a pointer to the + variable if successful; NULL otherwise. An ADD variable differs from + a BDD variable because it points to the arithmetic zero, instead of + having a complement pointer to 1. ] + + SideEffects [None] + + SeeAlso [Cudd_addNewVar Cudd_bddIthVar Cudd_addConst + Cudd_addNewVarAtLevel] + +******************************************************************************/ +DdNode * +Cudd_addIthVar( + DdManager * dd, + int i) +{ + DdNode *res; + + if ((unsigned int) i >= CUDD_MAXINDEX - 1) return(NULL); + do { + dd->reordered = 0; + res = cuddUniqueInter(dd,i,DD_ONE(dd),DD_ZERO(dd)); + } while (dd->reordered == 1); + + return(res); + +} /* end of Cudd_addIthVar */ + + +/**Function******************************************************************** + + Synopsis [Returns the BDD variable with index i.] + + Description [Retrieves the BDD variable with index i if it already + exists, or creates a new BDD variable. Returns a pointer to the + variable if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddNewVar Cudd_addIthVar Cudd_bddNewVarAtLevel + Cudd_ReadVars] + +******************************************************************************/ +DdNode * +Cudd_bddIthVar( + DdManager * dd, + int i) +{ + DdNode *res; + + if ((unsigned int) i >= CUDD_MAXINDEX - 1) return(NULL); + if (i < dd->size) { + res = dd->vars[i]; + } else { + res = cuddUniqueInter(dd,i,dd->one,Cudd_Not(dd->one)); + } + + return(res); + +} /* end of Cudd_bddIthVar */ + + +/**Function******************************************************************** + + Synopsis [Returns the ZDD variable with index i.] + + Description [Retrieves the ZDD variable with index i if it already + exists, or creates a new ZDD variable. Returns a pointer to the + variable if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddIthVar Cudd_addIthVar] + +******************************************************************************/ +DdNode * +Cudd_zddIthVar( + DdManager * dd, + int i) +{ + DdNode *res; + DdNode *zvar; + DdNode *lower; + int j; + + if ((unsigned int) i >= CUDD_MAXINDEX - 1) return(NULL); + + /* The i-th variable function has the following structure: + ** at the level corresponding to index i there is a node whose "then" + ** child points to the universe, and whose "else" child points to zero. + ** Above that level there are nodes with identical children. + */ + + /* First we build the node at the level of index i. */ + lower = (i < dd->sizeZ - 1) ? dd->univ[dd->permZ[i]+1] : DD_ONE(dd); + do { + dd->reordered = 0; + zvar = cuddUniqueInterZdd(dd, i, lower, DD_ZERO(dd)); + } while (dd->reordered == 1); + + if (zvar == NULL) + return(NULL); + cuddRef(zvar); + + /* Now we add the "filler" nodes above the level of index i. */ + for (j = dd->permZ[i] - 1; j >= 0; j--) { + do { + dd->reordered = 0; + res = cuddUniqueInterZdd(dd, dd->invpermZ[j], zvar, zvar); + } while (dd->reordered == 1); + if (res == NULL) { + Cudd_RecursiveDerefZdd(dd,zvar); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDerefZdd(dd,zvar); + zvar = res; + } + cuddDeref(zvar); + return(zvar); + +} /* end of Cudd_zddIthVar */ + + +/**Function******************************************************************** + + Synopsis [Creates one or more ZDD variables for each BDD variable.] + + Description [Creates one or more ZDD variables for each BDD + variable. If some ZDD variables already exist, only the missing + variables are created. Parameter multiplicity allows the caller to + control how many variables are created for each BDD variable in + existence. For instance, if ZDDs are used to represent covers, two + ZDD variables are required for each BDD variable. The order of the + BDD variables is transferred to the ZDD variables. If a variable + group tree exists for the BDD variables, a corresponding ZDD + variable group tree is created by expanding the BDD variable + tree. In any case, the ZDD variables derived from the same BDD + variable are merged in a ZDD variable group. If a ZDD variable group + tree exists, it is freed. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddNewVar Cudd_bddIthVar Cudd_bddNewVarAtLevel] + +******************************************************************************/ +int +Cudd_zddVarsFromBddVars( + DdManager * dd /* DD manager */, + int multiplicity /* how many ZDD variables are created for each BDD variable */) +{ + int res; + int i, j; + int allnew; + int *permutation; + + if (multiplicity < 1) return(0); + allnew = dd->sizeZ == 0; + if (dd->size * multiplicity > dd->sizeZ) { + res = cuddResizeTableZdd(dd,dd->size * multiplicity - 1); + if (res == 0) return(0); + } + /* Impose the order of the BDD variables to the ZDD variables. */ + if (allnew) { + for (i = 0; i < dd->size; i++) { + for (j = 0; j < multiplicity; j++) { + dd->permZ[i * multiplicity + j] = + dd->perm[i] * multiplicity + j; + dd->invpermZ[dd->permZ[i * multiplicity + j]] = + i * multiplicity + j; + } + } + for (i = 0; i < dd->sizeZ; i++) { + dd->univ[i]->index = dd->invpermZ[i]; + } + } else { + permutation = ALLOC(int,dd->sizeZ); + if (permutation == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (i = 0; i < dd->size; i++) { + for (j = 0; j < multiplicity; j++) { + permutation[i * multiplicity + j] = + dd->invperm[i] * multiplicity + j; + } + } + for (i = dd->size * multiplicity; i < dd->sizeZ; i++) { + permutation[i] = i; + } + res = Cudd_zddShuffleHeap(dd, permutation); + FREE(permutation); + if (res == 0) return(0); + } + /* Copy and expand the variable group tree if it exists. */ + if (dd->treeZ != NULL) { + Cudd_FreeZddTree(dd); + } + if (dd->tree != NULL) { + dd->treeZ = Mtr_CopyTree(dd->tree, multiplicity); + if (dd->treeZ == NULL) return(0); + } else if (multiplicity > 1) { + dd->treeZ = Mtr_InitGroupTree(0, dd->sizeZ); + if (dd->treeZ == NULL) return(0); + dd->treeZ->index = dd->invpermZ[0]; + } + /* Create groups for the ZDD variables derived from the same BDD variable. + */ + if (multiplicity > 1) { + char *vmask, *lmask; + + vmask = ALLOC(char, dd->size); + if (vmask == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + lmask = ALLOC(char, dd->size); + if (lmask == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (i = 0; i < dd->size; i++) { + vmask[i] = lmask[i] = 0; + } + res = addMultiplicityGroups(dd,dd->treeZ,multiplicity,vmask,lmask); + FREE(vmask); + FREE(lmask); + if (res == 0) return(0); + } + return(1); + +} /* end of Cudd_zddVarsFromBddVars */ + + +/**Function******************************************************************** + + Synopsis [Returns the ADD for constant c.] + + Description [Retrieves the ADD for constant c if it already + exists, or creates a new ADD. Returns a pointer to the + ADD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addNewVar Cudd_addIthVar] + +******************************************************************************/ +DdNode * +Cudd_addConst( + DdManager * dd, + CUDD_VALUE_TYPE c) +{ + return(cuddUniqueConst(dd,c)); + +} /* end of Cudd_addConst */ + + +/**Function******************************************************************** + + Synopsis [Returns 1 if a DD node is not constant.] + + Description [Returns 1 if a DD node is not constant. This function is + useful to test the results of Cudd_bddIteConstant, Cudd_addIteConstant, + Cudd_addEvalConst. These results may be a special value signifying + non-constant. In the other cases the macro Cudd_IsConstant can be used.] + + SideEffects [None] + + SeeAlso [Cudd_IsConstant Cudd_bddIteConstant Cudd_addIteConstant + Cudd_addEvalConst] + +******************************************************************************/ +int +Cudd_IsNonConstant( + DdNode *f) +{ + return(f == DD_NON_CONSTANT || !Cudd_IsConstant(f)); + +} /* end of Cudd_IsNonConstant */ + + +/**Function******************************************************************** + + Synopsis [Enables automatic dynamic reordering of BDDs and ADDs.] + + Description [Enables automatic dynamic reordering of BDDs and + ADDs. Parameter method is used to determine the method used for + reordering. If CUDD_REORDER_SAME is passed, the method is + unchanged.] + + SideEffects [None] + + SeeAlso [Cudd_AutodynDisable Cudd_ReorderingStatus + Cudd_AutodynEnableZdd] + +******************************************************************************/ +void +Cudd_AutodynEnable( + DdManager * unique, + Cudd_ReorderingType method) +{ + unique->autoDyn = 1; + if (method != CUDD_REORDER_SAME) { + unique->autoMethod = method; + } +#ifndef DD_NO_DEATH_ROW + /* If reordering is enabled, using the death row causes too many + ** invocations. Hence, we shrink the death row to just one entry. + */ + cuddClearDeathRow(unique); + unique->deathRowDepth = 1; + unique->deadMask = unique->deathRowDepth - 1; + if ((unsigned) unique->nextDead > unique->deadMask) { + unique->nextDead = 0; + } + unique->deathRow = REALLOC(DdNodePtr, unique->deathRow, + unique->deathRowDepth); +#endif + return; + +} /* end of Cudd_AutodynEnable */ + + +/**Function******************************************************************** + + Synopsis [Disables automatic dynamic reordering.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_AutodynEnable Cudd_ReorderingStatus + Cudd_AutodynDisableZdd] + +******************************************************************************/ +void +Cudd_AutodynDisable( + DdManager * unique) +{ + unique->autoDyn = 0; + return; + +} /* end of Cudd_AutodynDisable */ + + +/**Function******************************************************************** + + Synopsis [Reports the status of automatic dynamic reordering of BDDs + and ADDs.] + + Description [Reports the status of automatic dynamic reordering of + BDDs and ADDs. Parameter method is set to the reordering method + currently selected. Returns 1 if automatic reordering is enabled; 0 + otherwise.] + + SideEffects [Parameter method is set to the reordering method currently + selected.] + + SeeAlso [Cudd_AutodynEnable Cudd_AutodynDisable + Cudd_ReorderingStatusZdd] + +******************************************************************************/ +int +Cudd_ReorderingStatus( + DdManager * unique, + Cudd_ReorderingType * method) +{ + *method = unique->autoMethod; + return(unique->autoDyn); + +} /* end of Cudd_ReorderingStatus */ + + +/**Function******************************************************************** + + Synopsis [Enables automatic dynamic reordering of ZDDs.] + + Description [Enables automatic dynamic reordering of ZDDs. Parameter + method is used to determine the method used for reordering ZDDs. If + CUDD_REORDER_SAME is passed, the method is unchanged.] + + SideEffects [None] + + SeeAlso [Cudd_AutodynDisableZdd Cudd_ReorderingStatusZdd + Cudd_AutodynEnable] + +******************************************************************************/ +void +Cudd_AutodynEnableZdd( + DdManager * unique, + Cudd_ReorderingType method) +{ + unique->autoDynZ = 1; + if (method != CUDD_REORDER_SAME) { + unique->autoMethodZ = method; + } + return; + +} /* end of Cudd_AutodynEnableZdd */ + + +/**Function******************************************************************** + + Synopsis [Disables automatic dynamic reordering of ZDDs.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_AutodynEnableZdd Cudd_ReorderingStatusZdd + Cudd_AutodynDisable] + +******************************************************************************/ +void +Cudd_AutodynDisableZdd( + DdManager * unique) +{ + unique->autoDynZ = 0; + return; + +} /* end of Cudd_AutodynDisableZdd */ + + +/**Function******************************************************************** + + Synopsis [Reports the status of automatic dynamic reordering of ZDDs.] + + Description [Reports the status of automatic dynamic reordering of + ZDDs. Parameter method is set to the ZDD reordering method currently + selected. Returns 1 if automatic reordering is enabled; 0 + otherwise.] + + SideEffects [Parameter method is set to the ZDD reordering method currently + selected.] + + SeeAlso [Cudd_AutodynEnableZdd Cudd_AutodynDisableZdd + Cudd_ReorderingStatus] + +******************************************************************************/ +int +Cudd_ReorderingStatusZdd( + DdManager * unique, + Cudd_ReorderingType * method) +{ + *method = unique->autoMethodZ; + return(unique->autoDynZ); + +} /* end of Cudd_ReorderingStatusZdd */ + + +/**Function******************************************************************** + + Synopsis [Tells whether the realignment of ZDD order to BDD order is + enabled.] + + Description [Returns 1 if the realignment of ZDD order to BDD order is + enabled; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_zddRealignEnable Cudd_zddRealignDisable + Cudd_bddRealignEnable Cudd_bddRealignDisable] + +******************************************************************************/ +int +Cudd_zddRealignmentEnabled( + DdManager * unique) +{ + return(unique->realign); + +} /* end of Cudd_zddRealignmentEnabled */ + + +/**Function******************************************************************** + + Synopsis [Enables realignment of ZDD order to BDD order.] + + Description [Enables realignment of the ZDD variable order to the + BDD variable order after the BDDs and ADDs have been reordered. The + number of ZDD variables must be a multiple of the number of BDD + variables for realignment to make sense. If this condition is not met, + Cudd_ReduceHeap will return 0. Let M be the + ratio of the two numbers. For the purpose of realignment, the ZDD + variables from M*i to (M+1)*i-1 are + reagarded as corresponding to BDD variable i. Realignment + is initially disabled.] + + SideEffects [None] + + SeeAlso [Cudd_ReduceHeap Cudd_zddRealignDisable + Cudd_zddRealignmentEnabled Cudd_bddRealignDisable + Cudd_bddRealignmentEnabled] + +******************************************************************************/ +void +Cudd_zddRealignEnable( + DdManager * unique) +{ + unique->realign = 1; + return; + +} /* end of Cudd_zddRealignEnable */ + + +/**Function******************************************************************** + + Synopsis [Disables realignment of ZDD order to BDD order.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_zddRealignEnable Cudd_zddRealignmentEnabled + Cudd_bddRealignEnable Cudd_bddRealignmentEnabled] + +******************************************************************************/ +void +Cudd_zddRealignDisable( + DdManager * unique) +{ + unique->realign = 0; + return; + +} /* end of Cudd_zddRealignDisable */ + + +/**Function******************************************************************** + + Synopsis [Tells whether the realignment of BDD order to ZDD order is + enabled.] + + Description [Returns 1 if the realignment of BDD order to ZDD order is + enabled; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddRealignEnable Cudd_bddRealignDisable + Cudd_zddRealignEnable Cudd_zddRealignDisable] + +******************************************************************************/ +int +Cudd_bddRealignmentEnabled( + DdManager * unique) +{ + return(unique->realignZ); + +} /* end of Cudd_bddRealignmentEnabled */ + + +/**Function******************************************************************** + + Synopsis [Enables realignment of BDD order to ZDD order.] + + Description [Enables realignment of the BDD variable order to the + ZDD variable order after the ZDDs have been reordered. The + number of ZDD variables must be a multiple of the number of BDD + variables for realignment to make sense. If this condition is not met, + Cudd_zddReduceHeap will return 0. Let M be the + ratio of the two numbers. For the purpose of realignment, the ZDD + variables from M*i to (M+1)*i-1 are + reagarded as corresponding to BDD variable i. Realignment + is initially disabled.] + + SideEffects [None] + + SeeAlso [Cudd_zddReduceHeap Cudd_bddRealignDisable + Cudd_bddRealignmentEnabled Cudd_zddRealignDisable + Cudd_zddRealignmentEnabled] + +******************************************************************************/ +void +Cudd_bddRealignEnable( + DdManager * unique) +{ + unique->realignZ = 1; + return; + +} /* end of Cudd_bddRealignEnable */ + + +/**Function******************************************************************** + + Synopsis [Disables realignment of ZDD order to BDD order.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_bddRealignEnable Cudd_bddRealignmentEnabled + Cudd_zddRealignEnable Cudd_zddRealignmentEnabled] + +******************************************************************************/ +void +Cudd_bddRealignDisable( + DdManager * unique) +{ + unique->realignZ = 0; + return; + +} /* end of Cudd_bddRealignDisable */ + + +/**Function******************************************************************** + + Synopsis [Returns the one constant of the manager.] + + Description [Returns the one constant of the manager. The one + constant is common to ADDs and BDDs.] + + SideEffects [None] + + SeeAlso [Cudd_ReadZero Cudd_ReadLogicZero Cudd_ReadZddOne] + +******************************************************************************/ +DdNode * +Cudd_ReadOne( + DdManager * dd) +{ + return(dd->one); + +} /* end of Cudd_ReadOne */ + + +/**Function******************************************************************** + + Synopsis [Returns the ZDD for the constant 1 function.] + + Description [Returns the ZDD for the constant 1 function. + The representation of the constant 1 function as a ZDD depends on + how many variables it (nominally) depends on. The index of the + topmost variable in the support is given as argument i.] + + SideEffects [None] + + SeeAlso [Cudd_ReadOne] + +******************************************************************************/ +DdNode * +Cudd_ReadZddOne( + DdManager * dd, + int i) +{ + if (i < 0) + return(NULL); + return(i < dd->sizeZ ? dd->univ[i] : DD_ONE(dd)); + +} /* end of Cudd_ReadZddOne */ + + + +/**Function******************************************************************** + + Synopsis [Returns the zero constant of the manager.] + + Description [Returns the zero constant of the manager. The zero + constant is the arithmetic zero, rather than the logic zero. The + latter is the complement of the one constant.] + + SideEffects [None] + + SeeAlso [Cudd_ReadOne Cudd_ReadLogicZero] + +******************************************************************************/ +DdNode * +Cudd_ReadZero( + DdManager * dd) +{ + return(DD_ZERO(dd)); + +} /* end of Cudd_ReadZero */ + + +/**Function******************************************************************** + + Synopsis [Returns the logic zero constant of the manager.] + + Description [Returns the zero constant of the manager. The logic zero + constant is the complement of the one constant, and is distinct from + the arithmetic zero.] + + SideEffects [None] + + SeeAlso [Cudd_ReadOne Cudd_ReadZero] + +******************************************************************************/ +DdNode * +Cudd_ReadLogicZero( + DdManager * dd) +{ + return(Cudd_Not(DD_ONE(dd))); + +} /* end of Cudd_ReadLogicZero */ + + +/**Function******************************************************************** + + Synopsis [Reads the plus-infinity constant from the manager.] + + Description [] + + SideEffects [None] + +******************************************************************************/ +DdNode * +Cudd_ReadPlusInfinity( + DdManager * dd) +{ + return(dd->plusinfinity); + +} /* end of Cudd_ReadPlusInfinity */ + + +/**Function******************************************************************** + + Synopsis [Reads the minus-infinity constant from the manager.] + + Description [] + + SideEffects [None] + +******************************************************************************/ +DdNode * +Cudd_ReadMinusInfinity( + DdManager * dd) +{ + return(dd->minusinfinity); + +} /* end of Cudd_ReadMinusInfinity */ + + +/**Function******************************************************************** + + Synopsis [Reads the background constant of the manager.] + + Description [] + + SideEffects [None] + +******************************************************************************/ +DdNode * +Cudd_ReadBackground( + DdManager * dd) +{ + return(dd->background); + +} /* end of Cudd_ReadBackground */ + + +/**Function******************************************************************** + + Synopsis [Sets the background constant of the manager.] + + Description [Sets the background constant of the manager. It assumes + that the DdNode pointer bck is already referenced.] + + SideEffects [None] + +******************************************************************************/ +void +Cudd_SetBackground( + DdManager * dd, + DdNode * bck) +{ + dd->background = bck; + +} /* end of Cudd_SetBackground */ + + +/**Function******************************************************************** + + Synopsis [Reads the number of slots in the cache.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_ReadCacheUsedSlots] + +******************************************************************************/ +unsigned int +Cudd_ReadCacheSlots( + DdManager * dd) +{ + return(dd->cacheSlots); + +} /* end of Cudd_ReadCacheSlots */ + + +/**Function******************************************************************** + + Synopsis [Reads the fraction of used slots in the cache.] + + Description [Reads the fraction of used slots in the cache. The unused + slots are those in which no valid data is stored. Garbage collection, + variable reordering, and cache resizing may cause used slots to become + unused.] + + SideEffects [None] + + SeeAlso [Cudd_ReadCacheSlots] + +******************************************************************************/ +double +Cudd_ReadCacheUsedSlots( + DdManager * dd) +{ + unsigned long used = 0; + int slots = dd->cacheSlots; + DdCache *cache = dd->cache; + int i; + + for (i = 0; i < slots; i++) { + used += cache[i].h != 0; + } + + return((double)used / (double) dd->cacheSlots); + +} /* end of Cudd_ReadCacheUsedSlots */ + + +/**Function******************************************************************** + + Synopsis [Returns the number of cache look-ups.] + + Description [Returns the number of cache look-ups.] + + SideEffects [None] + + SeeAlso [Cudd_ReadCacheHits] + +******************************************************************************/ +double +Cudd_ReadCacheLookUps( + DdManager * dd) +{ + return(dd->cacheHits + dd->cacheMisses + + dd->totCachehits + dd->totCacheMisses); + +} /* end of Cudd_ReadCacheLookUps */ + + +/**Function******************************************************************** + + Synopsis [Returns the number of cache hits.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_ReadCacheLookUps] + +******************************************************************************/ +double +Cudd_ReadCacheHits( + DdManager * dd) +{ + return(dd->cacheHits + dd->totCachehits); + +} /* end of Cudd_ReadCacheHits */ + + +/**Function******************************************************************** + + Synopsis [Returns the number of recursive calls.] + + Description [Returns the number of recursive calls if the package is + compiled with DD_COUNT defined.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +double +Cudd_ReadRecursiveCalls( + DdManager * dd) +{ +#ifdef DD_COUNT + return(dd->recursiveCalls); +#else + return(-1.0); +#endif + +} /* end of Cudd_ReadRecursiveCalls */ + + + +/**Function******************************************************************** + + Synopsis [Reads the hit rate that causes resizinig of the computed + table.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_SetMinHit] + +******************************************************************************/ +unsigned int +Cudd_ReadMinHit( + DdManager * dd) +{ + /* Internally, the package manipulates the ratio of hits to + ** misses instead of the ratio of hits to accesses. */ + return((unsigned int) (0.5 + 100 * dd->minHit / (1 + dd->minHit))); + +} /* end of Cudd_ReadMinHit */ + + +/**Function******************************************************************** + + Synopsis [Sets the hit rate that causes resizinig of the computed + table.] + + Description [Sets the minHit parameter of the manager. This + parameter controls the resizing of the computed table. If the hit + rate is larger than the specified value, and the cache is not + already too large, then its size is doubled.] + + SideEffects [None] + + SeeAlso [Cudd_ReadMinHit] + +******************************************************************************/ +void +Cudd_SetMinHit( + DdManager * dd, + unsigned int hr) +{ + /* Internally, the package manipulates the ratio of hits to + ** misses instead of the ratio of hits to accesses. */ + dd->minHit = (double) hr / (100.0 - (double) hr); + +} /* end of Cudd_SetMinHit */ + + +/**Function******************************************************************** + + Synopsis [Reads the looseUpTo parameter of the manager.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_SetLooseUpTo Cudd_ReadMinHit Cudd_ReadMinDead] + +******************************************************************************/ +unsigned int +Cudd_ReadLooseUpTo( + DdManager * dd) +{ + return(dd->looseUpTo); + +} /* end of Cudd_ReadLooseUpTo */ + + +/**Function******************************************************************** + + Synopsis [Sets the looseUpTo parameter of the manager.] + + Description [Sets the looseUpTo parameter of the manager. This + parameter of the manager controls the threshold beyond which no fast + growth of the unique table is allowed. The threshold is given as a + number of slots. If the value passed to this function is 0, the + function determines a suitable value based on the available memory.] + + SideEffects [None] + + SeeAlso [Cudd_ReadLooseUpTo Cudd_SetMinHit] + +******************************************************************************/ +void +Cudd_SetLooseUpTo( + DdManager * dd, + unsigned int lut) +{ + if (lut == 0) { + long datalimit = getSoftDataLimit(); + lut = (unsigned int) (datalimit / (sizeof(DdNode) * + DD_MAX_LOOSE_FRACTION)); + } + dd->looseUpTo = lut; + +} /* end of Cudd_SetLooseUpTo */ + + +/**Function******************************************************************** + + Synopsis [Returns the soft limit for the cache size.] + + Description [Returns the soft limit for the cache size. The soft limit] + + SideEffects [None] + + SeeAlso [Cudd_ReadMaxCache] + +******************************************************************************/ +unsigned int +Cudd_ReadMaxCache( + DdManager * dd) +{ + return(2 * dd->cacheSlots + dd->cacheSlack); + +} /* end of Cudd_ReadMaxCache */ + + +/**Function******************************************************************** + + Synopsis [Reads the maxCacheHard parameter of the manager.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_SetMaxCacheHard Cudd_ReadMaxCache] + +******************************************************************************/ +unsigned int +Cudd_ReadMaxCacheHard( + DdManager * dd) +{ + return(dd->maxCacheHard); + +} /* end of Cudd_ReadMaxCache */ + + +/**Function******************************************************************** + + Synopsis [Sets the maxCacheHard parameter of the manager.] + + Description [Sets the maxCacheHard parameter of the manager. The + cache cannot grow larger than maxCacheHard entries. This parameter + allows an application to control the trade-off of memory versus + speed. If the value passed to this function is 0, the function + determines a suitable maximum cache size based on the available memory.] + + SideEffects [None] + + SeeAlso [Cudd_ReadMaxCacheHard Cudd_SetMaxCache] + +******************************************************************************/ +void +Cudd_SetMaxCacheHard( + DdManager * dd, + unsigned int mc) +{ + if (mc == 0) { + long datalimit = getSoftDataLimit(); + mc = (unsigned int) (datalimit / (sizeof(DdCache) * + DD_MAX_CACHE_FRACTION)); + } + dd->maxCacheHard = mc; + +} /* end of Cudd_SetMaxCacheHard */ + + +/**Function******************************************************************** + + Synopsis [Returns the number of BDD variables in existance.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_ReadZddSize] + +******************************************************************************/ +int +Cudd_ReadSize( + DdManager * dd) +{ + return(dd->size); + +} /* end of Cudd_ReadSize */ + + +/**Function******************************************************************** + + Synopsis [Returns the number of ZDD variables in existance.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_ReadSize] + +******************************************************************************/ +int +Cudd_ReadZddSize( + DdManager * dd) +{ + return(dd->sizeZ); + +} /* end of Cudd_ReadZddSize */ + + +/**Function******************************************************************** + + Synopsis [Returns the total number of slots of the unique table.] + + Description [Returns the total number of slots of the unique table. + This number ismainly for diagnostic purposes.] + + SideEffects [None] + +******************************************************************************/ +unsigned int +Cudd_ReadSlots( + DdManager * dd) +{ + return(dd->slots); + +} /* end of Cudd_ReadSlots */ + + +/**Function******************************************************************** + + Synopsis [Reads the fraction of used slots in the unique table.] + + Description [Reads the fraction of used slots in the unique + table. The unused slots are those in which no valid data is + stored. Garbage collection, variable reordering, and subtable + resizing may cause used slots to become unused.] + + SideEffects [None] + + SeeAlso [Cudd_ReadSlots] + +******************************************************************************/ +double +Cudd_ReadUsedSlots( + DdManager * dd) +{ + unsigned long used = 0; + int i, j; + int size = dd->size; + DdNodePtr *nodelist; + DdSubtable *subtable; + DdNode *node; + DdNode *sentinel = &(dd->sentinel); + + /* Scan each BDD/ADD subtable. */ + for (i = 0; i < size; i++) { + subtable = &(dd->subtables[i]); + nodelist = subtable->nodelist; + for (j = 0; (unsigned) j < subtable->slots; j++) { + node = nodelist[j]; + if (node != sentinel) { + used++; + } + } + } + + /* Scan the ZDD subtables. */ + size = dd->sizeZ; + + for (i = 0; i < size; i++) { + subtable = &(dd->subtableZ[i]); + nodelist = subtable->nodelist; + for (j = 0; (unsigned) j < subtable->slots; j++) { + node = nodelist[j]; + if (node != NULL) { + used++; + } + } + } + + /* Constant table. */ + subtable = &(dd->constants); + nodelist = subtable->nodelist; + for (j = 0; (unsigned) j < subtable->slots; j++) { + node = nodelist[j]; + if (node != NULL) { + used++; + } + } + + return((double)used / (double) dd->slots); + +} /* end of Cudd_ReadUsedSlots */ + + +/**Function******************************************************************** + + Synopsis [Computes the expected fraction of used slots in the unique + table.] + + Description [Computes the fraction of slots in the unique table that + should be in use. This expected value is based on the assumption + that the hash function distributes the keys randomly; it can be + compared with the result of Cudd_ReadUsedSlots to monitor the + performance of the unique table hash function.] + + SideEffects [None] + + SeeAlso [Cudd_ReadSlots Cudd_ReadUsedSlots] + +******************************************************************************/ +double +Cudd_ExpectedUsedSlots( + DdManager * dd) +{ + int i; + int size = dd->size; + DdSubtable *subtable; + double empty = 0.0; + + /* To each subtable we apply the corollary to Theorem 8.5 (occupancy + ** distribution) from Sedgewick and Flajolet's Analysis of Algorithms. + ** The corollary says that for a a table with M buckets and a load ratio + ** of r, the expected number of empty buckets is asymptotically given + ** by M * exp(-r). + */ + + /* Scan each BDD/ADD subtable. */ + for (i = 0; i < size; i++) { + subtable = &(dd->subtables[i]); + empty += (double) subtable->slots * + exp(-(double) subtable->keys / (double) subtable->slots); + } + + /* Scan the ZDD subtables. */ + size = dd->sizeZ; + + for (i = 0; i < size; i++) { + subtable = &(dd->subtableZ[i]); + empty += (double) subtable->slots * + exp(-(double) subtable->keys / (double) subtable->slots); + } + + /* Constant table. */ + subtable = &(dd->constants); + empty += (double) subtable->slots * + exp(-(double) subtable->keys / (double) subtable->slots); + + return(1.0 - empty / (double) dd->slots); + +} /* end of Cudd_ExpectedUsedSlots */ + + +/**Function******************************************************************** + + Synopsis [Returns the number of nodes in the unique table.] + + Description [Returns the total number of nodes currently in the unique + table, including the dead nodes.] + + SideEffects [None] + + SeeAlso [Cudd_ReadDead] + +******************************************************************************/ +unsigned int +Cudd_ReadKeys( + DdManager * dd) +{ + return(dd->keys); + +} /* end of Cudd_ReadKeys */ + + +/**Function******************************************************************** + + Synopsis [Returns the number of dead nodes in the unique table.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_ReadKeys] + +******************************************************************************/ +unsigned int +Cudd_ReadDead( + DdManager * dd) +{ + return(dd->dead); + +} /* end of Cudd_ReadDead */ + + +/**Function******************************************************************** + + Synopsis [Reads the minDead parameter of the manager.] + + Description [Reads the minDead parameter of the manager. The minDead + parameter is used by the package to decide whether to collect garbage + or resize a subtable of the unique table when the subtable becomes + too full. The application can indirectly control the value of minDead + by setting the looseUpTo parameter.] + + SideEffects [None] + + SeeAlso [Cudd_ReadDead Cudd_ReadLooseUpTo Cudd_SetLooseUpTo] + +******************************************************************************/ +unsigned int +Cudd_ReadMinDead( + DdManager * dd) +{ + return(dd->minDead); + +} /* end of Cudd_ReadMinDead */ + + +/**Function******************************************************************** + + Synopsis [Returns the number of times reordering has occurred.] + + Description [Returns the number of times reordering has occurred in the + manager. The number includes both the calls to Cudd_ReduceHeap from + the application program and those automatically performed by the + package. However, calls that do not even initiate reordering are not + counted. A call may not initiate reordering if there are fewer than + minsize live nodes in the manager, or if CUDD_REORDER_NONE is specified + as reordering method. The calls to Cudd_ShuffleHeap are not counted.] + + SideEffects [None] + + SeeAlso [Cudd_ReduceHeap Cudd_ReadReorderingTime] + +******************************************************************************/ +int +Cudd_ReadReorderings( + DdManager * dd) +{ + return(dd->reorderings); + +} /* end of Cudd_ReadReorderings */ + + +/**Function******************************************************************** + + Synopsis [Returns the time spent in reordering.] + + Description [Returns the number of milliseconds spent reordering + variables since the manager was initialized. The time spent in collecting + garbage before reordering is included.] + + SideEffects [None] + + SeeAlso [Cudd_ReadReorderings] + +******************************************************************************/ +long +Cudd_ReadReorderingTime( + DdManager * dd) +{ + return(dd->reordTime); + +} /* end of Cudd_ReadReorderingTime */ + + +/**Function******************************************************************** + + Synopsis [Returns the number of times garbage collection has occurred.] + + Description [Returns the number of times garbage collection has + occurred in the manager. The number includes both the calls from + reordering procedures and those caused by requests to create new + nodes.] + + SideEffects [None] + + SeeAlso [Cudd_ReadGarbageCollectionTime] + +******************************************************************************/ +int +Cudd_ReadGarbageCollections( + DdManager * dd) +{ + return(dd->garbageCollections); + +} /* end of Cudd_ReadGarbageCollections */ + + +/**Function******************************************************************** + + Synopsis [Returns the time spent in garbage collection.] + + Description [Returns the number of milliseconds spent doing garbage + collection since the manager was initialized.] + + SideEffects [None] + + SeeAlso [Cudd_ReadGarbageCollections] + +******************************************************************************/ +long +Cudd_ReadGarbageCollectionTime( + DdManager * dd) +{ + return(dd->GCTime); + +} /* end of Cudd_ReadGarbageCollectionTime */ + + +/**Function******************************************************************** + + Synopsis [Returns the number of nodes freed.] + + Description [Returns the number of nodes returned to the free list if the + keeping of this statistic is enabled; -1 otherwise. This statistic is + enabled only if the package is compiled with DD_STATS defined.] + + SideEffects [None] + + SeeAlso [Cudd_ReadNodesDropped] + +******************************************************************************/ +double +Cudd_ReadNodesFreed( + DdManager * dd) +{ +#ifdef DD_STATS + return(dd->nodesFreed); +#else + return(-1.0); +#endif + +} /* end of Cudd_ReadNodesFreed */ + + +/**Function******************************************************************** + + Synopsis [Returns the number of nodes dropped.] + + Description [Returns the number of nodes killed by dereferencing if the + keeping of this statistic is enabled; -1 otherwise. This statistic is + enabled only if the package is compiled with DD_STATS defined.] + + SideEffects [None] + + SeeAlso [Cudd_ReadNodesFreed] + +******************************************************************************/ +double +Cudd_ReadNodesDropped( + DdManager * dd) +{ +#ifdef DD_STATS + return(dd->nodesDropped); +#else + return(-1.0); +#endif + +} /* end of Cudd_ReadNodesDropped */ + + +/**Function******************************************************************** + + Synopsis [Returns the number of look-ups in the unique table.] + + Description [Returns the number of look-ups in the unique table if the + keeping of this statistic is enabled; -1 otherwise. This statistic is + enabled only if the package is compiled with DD_UNIQUE_PROFILE defined.] + + SideEffects [None] + + SeeAlso [Cudd_ReadUniqueLinks] + +******************************************************************************/ +double +Cudd_ReadUniqueLookUps( + DdManager * dd) +{ +#ifdef DD_UNIQUE_PROFILE + return(dd->uniqueLookUps); +#else + return(-1.0); +#endif + +} /* end of Cudd_ReadUniqueLookUps */ + + +/**Function******************************************************************** + + Synopsis [Returns the number of links followed in the unique table.] + + Description [Returns the number of links followed during look-ups in the + unique table if the keeping of this statistic is enabled; -1 otherwise. + If an item is found in the first position of its collision list, the + number of links followed is taken to be 0. If it is in second position, + the number of links is 1, and so on. This statistic is enabled only if + the package is compiled with DD_UNIQUE_PROFILE defined.] + + SideEffects [None] + + SeeAlso [Cudd_ReadUniqueLookUps] + +******************************************************************************/ +double +Cudd_ReadUniqueLinks( + DdManager * dd) +{ +#ifdef DD_UNIQUE_PROFILE + return(dd->uniqueLinks); +#else + return(-1.0); +#endif + +} /* end of Cudd_ReadUniqueLinks */ + + +/**Function******************************************************************** + + Synopsis [Reads the siftMaxVar parameter of the manager.] + + Description [Reads the siftMaxVar parameter of the manager. This + parameter gives the maximum number of variables that will be sifted + for each invocation of sifting.] + + SideEffects [None] + + SeeAlso [Cudd_ReadSiftMaxSwap Cudd_SetSiftMaxVar] + +******************************************************************************/ +int +Cudd_ReadSiftMaxVar( + DdManager * dd) +{ + return(dd->siftMaxVar); + +} /* end of Cudd_ReadSiftMaxVar */ + + +/**Function******************************************************************** + + Synopsis [Sets the siftMaxVar parameter of the manager.] + + Description [Sets the siftMaxVar parameter of the manager. This + parameter gives the maximum number of variables that will be sifted + for each invocation of sifting.] + + SideEffects [None] + + SeeAlso [Cudd_SetSiftMaxSwap Cudd_ReadSiftMaxVar] + +******************************************************************************/ +void +Cudd_SetSiftMaxVar( + DdManager * dd, + int smv) +{ + dd->siftMaxVar = smv; + +} /* end of Cudd_SetSiftMaxVar */ + + +/**Function******************************************************************** + + Synopsis [Reads the siftMaxSwap parameter of the manager.] + + Description [Reads the siftMaxSwap parameter of the manager. This + parameter gives the maximum number of swaps that will be attempted + for each invocation of sifting. The real number of swaps may exceed + the set limit because the package will always complete the sifting + of the variable that causes the limit to be reached.] + + SideEffects [None] + + SeeAlso [Cudd_ReadSiftMaxVar Cudd_SetSiftMaxSwap] + +******************************************************************************/ +int +Cudd_ReadSiftMaxSwap( + DdManager * dd) +{ + return(dd->siftMaxSwap); + +} /* end of Cudd_ReadSiftMaxSwap */ + + +/**Function******************************************************************** + + Synopsis [Sets the siftMaxSwap parameter of the manager.] + + Description [Sets the siftMaxSwap parameter of the manager. This + parameter gives the maximum number of swaps that will be attempted + for each invocation of sifting. The real number of swaps may exceed + the set limit because the package will always complete the sifting + of the variable that causes the limit to be reached.] + + SideEffects [None] + + SeeAlso [Cudd_SetSiftMaxVar Cudd_ReadSiftMaxSwap] + +******************************************************************************/ +void +Cudd_SetSiftMaxSwap( + DdManager * dd, + int sms) +{ + dd->siftMaxSwap = sms; + +} /* end of Cudd_SetSiftMaxSwap */ + + +/**Function******************************************************************** + + Synopsis [Reads the maxGrowth parameter of the manager.] + + Description [Reads the maxGrowth parameter of the manager. This + parameter determines how much the number of nodes can grow during + sifting of a variable. Overall, sifting never increases the size of + the decision diagrams. This parameter only refers to intermediate + results. A lower value will speed up sifting, possibly at the + expense of quality.] + + SideEffects [None] + + SeeAlso [Cudd_SetMaxGrowth Cudd_ReadMaxGrowthAlternate] + +******************************************************************************/ +double +Cudd_ReadMaxGrowth( + DdManager * dd) +{ + return(dd->maxGrowth); + +} /* end of Cudd_ReadMaxGrowth */ + + +/**Function******************************************************************** + + Synopsis [Sets the maxGrowth parameter of the manager.] + + Description [Sets the maxGrowth parameter of the manager. This + parameter determines how much the number of nodes can grow during + sifting of a variable. Overall, sifting never increases the size of + the decision diagrams. This parameter only refers to intermediate + results. A lower value will speed up sifting, possibly at the + expense of quality.] + + SideEffects [None] + + SeeAlso [Cudd_ReadMaxGrowth Cudd_SetMaxGrowthAlternate] + +******************************************************************************/ +void +Cudd_SetMaxGrowth( + DdManager * dd, + double mg) +{ + dd->maxGrowth = mg; + +} /* end of Cudd_SetMaxGrowth */ + + +/**Function******************************************************************** + + Synopsis [Reads the maxGrowthAlt parameter of the manager.] + + Description [Reads the maxGrowthAlt parameter of the manager. This + parameter is analogous to the maxGrowth paramter, and is used every + given number of reorderings instead of maxGrowth. The number of + reorderings is set with Cudd_SetReorderingCycle. If the number of + reorderings is 0 (default) maxGrowthAlt is never used.] + + SideEffects [None] + + SeeAlso [Cudd_ReadMaxGrowth Cudd_SetMaxGrowthAlternate + Cudd_SetReorderingCycle Cudd_ReadReorderingCycle] + +******************************************************************************/ +double +Cudd_ReadMaxGrowthAlternate( + DdManager * dd) +{ + return(dd->maxGrowthAlt); + +} /* end of Cudd_ReadMaxGrowthAlternate */ + + +/**Function******************************************************************** + + Synopsis [Sets the maxGrowthAlt parameter of the manager.] + + Description [Sets the maxGrowthAlt parameter of the manager. This + parameter is analogous to the maxGrowth paramter, and is used every + given number of reorderings instead of maxGrowth. The number of + reorderings is set with Cudd_SetReorderingCycle. If the number of + reorderings is 0 (default) maxGrowthAlt is never used.] + + SideEffects [None] + + SeeAlso [Cudd_ReadMaxGrowthAlternate Cudd_SetMaxGrowth + Cudd_SetReorderingCycle Cudd_ReadReorderingCycle] + +******************************************************************************/ +void +Cudd_SetMaxGrowthAlternate( + DdManager * dd, + double mg) +{ + dd->maxGrowthAlt = mg; + +} /* end of Cudd_SetMaxGrowthAlternate */ + + +/**Function******************************************************************** + + Synopsis [Reads the reordCycle parameter of the manager.] + + Description [Reads the reordCycle parameter of the manager. This + parameter determines how often the alternate threshold on maximum + growth is used in reordering.] + + SideEffects [None] + + SeeAlso [Cudd_ReadMaxGrowthAlternate Cudd_SetMaxGrowthAlternate + Cudd_SetReorderingCycle] + +******************************************************************************/ +int +Cudd_ReadReorderingCycle( + DdManager * dd) +{ + return(dd->reordCycle); + +} /* end of Cudd_ReadReorderingCycle */ + + +/**Function******************************************************************** + + Synopsis [Sets the reordCycle parameter of the manager.] + + Description [Sets the reordCycle parameter of the manager. This + parameter determines how often the alternate threshold on maximum + growth is used in reordering.] + + SideEffects [None] + + SeeAlso [Cudd_ReadMaxGrowthAlternate Cudd_SetMaxGrowthAlternate + Cudd_ReadReorderingCycle] + +******************************************************************************/ +void +Cudd_SetReorderingCycle( + DdManager * dd, + int cycle) +{ + dd->reordCycle = cycle; + +} /* end of Cudd_SetReorderingCycle */ + + +/**Function******************************************************************** + + Synopsis [Returns the variable group tree of the manager.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_SetTree Cudd_FreeTree Cudd_ReadZddTree] + +******************************************************************************/ +MtrNode * +Cudd_ReadTree( + DdManager * dd) +{ + return(dd->tree); + +} /* end of Cudd_ReadTree */ + + +/**Function******************************************************************** + + Synopsis [Sets the variable group tree of the manager.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_FreeTree Cudd_ReadTree Cudd_SetZddTree] + +******************************************************************************/ +void +Cudd_SetTree( + DdManager * dd, + MtrNode * tree) +{ + if (dd->tree != NULL) { + Mtr_FreeTree(dd->tree); + } + dd->tree = tree; + if (tree == NULL) return; + + fixVarTree(tree, dd->perm, dd->size); + return; + +} /* end of Cudd_SetTree */ + + +/**Function******************************************************************** + + Synopsis [Frees the variable group tree of the manager.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_SetTree Cudd_ReadTree Cudd_FreeZddTree] + +******************************************************************************/ +void +Cudd_FreeTree( + DdManager * dd) +{ + if (dd->tree != NULL) { + Mtr_FreeTree(dd->tree); + dd->tree = NULL; + } + return; + +} /* end of Cudd_FreeTree */ + + +/**Function******************************************************************** + + Synopsis [Returns the variable group tree of the manager.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_SetZddTree Cudd_FreeZddTree Cudd_ReadTree] + +******************************************************************************/ +MtrNode * +Cudd_ReadZddTree( + DdManager * dd) +{ + return(dd->treeZ); + +} /* end of Cudd_ReadZddTree */ + + +/**Function******************************************************************** + + Synopsis [Sets the ZDD variable group tree of the manager.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_FreeZddTree Cudd_ReadZddTree Cudd_SetTree] + +******************************************************************************/ +void +Cudd_SetZddTree( + DdManager * dd, + MtrNode * tree) +{ + if (dd->treeZ != NULL) { + Mtr_FreeTree(dd->treeZ); + } + dd->treeZ = tree; + if (tree == NULL) return; + + fixVarTree(tree, dd->permZ, dd->sizeZ); + return; + +} /* end of Cudd_SetZddTree */ + + +/**Function******************************************************************** + + Synopsis [Frees the variable group tree of the manager.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_SetZddTree Cudd_ReadZddTree Cudd_FreeTree] + +******************************************************************************/ +void +Cudd_FreeZddTree( + DdManager * dd) +{ + if (dd->treeZ != NULL) { + Mtr_FreeTree(dd->treeZ); + dd->treeZ = NULL; + } + return; + +} /* end of Cudd_FreeZddTree */ + + +/**Function******************************************************************** + + Synopsis [Returns the index of the node.] + + Description [Returns the index of the node. The node pointer can be + either regular or complemented.] + + SideEffects [None] + + SeeAlso [Cudd_ReadIndex] + +******************************************************************************/ +unsigned int +Cudd_NodeReadIndex( + DdNode * node) +{ + return((unsigned int) Cudd_Regular(node)->index); + +} /* end of Cudd_NodeReadIndex */ + + +/**Function******************************************************************** + + Synopsis [Returns the current position of the i-th variable in the + order.] + + Description [Returns the current position of the i-th variable in + the order. If the index is CUDD_CONST_INDEX, returns + CUDD_CONST_INDEX; otherwise, if the index is out of bounds returns + -1.] + + SideEffects [None] + + SeeAlso [Cudd_ReadInvPerm Cudd_ReadPermZdd] + +******************************************************************************/ +int +Cudd_ReadPerm( + DdManager * dd, + int i) +{ + if (i == CUDD_CONST_INDEX) return(CUDD_CONST_INDEX); + if (i < 0 || i >= dd->size) return(-1); + return(dd->perm[i]); + +} /* end of Cudd_ReadPerm */ + + +/**Function******************************************************************** + + Synopsis [Returns the current position of the i-th ZDD variable in the + order.] + + Description [Returns the current position of the i-th ZDD variable + in the order. If the index is CUDD_CONST_INDEX, returns + CUDD_CONST_INDEX; otherwise, if the index is out of bounds returns + -1.] + + SideEffects [None] + + SeeAlso [Cudd_ReadInvPermZdd Cudd_ReadPerm] + +******************************************************************************/ +int +Cudd_ReadPermZdd( + DdManager * dd, + int i) +{ + if (i == CUDD_CONST_INDEX) return(CUDD_CONST_INDEX); + if (i < 0 || i >= dd->sizeZ) return(-1); + return(dd->permZ[i]); + +} /* end of Cudd_ReadPermZdd */ + + +/**Function******************************************************************** + + Synopsis [Returns the index of the variable currently in the i-th + position of the order.] + + Description [Returns the index of the variable currently in the i-th + position of the order. If the index is CUDD_CONST_INDEX, returns + CUDD_CONST_INDEX; otherwise, if the index is out of bounds returns -1.] + + SideEffects [None] + + SeeAlso [Cudd_ReadPerm Cudd_ReadInvPermZdd] + +******************************************************************************/ +int +Cudd_ReadInvPerm( + DdManager * dd, + int i) +{ + if (i == CUDD_CONST_INDEX) return(CUDD_CONST_INDEX); + if (i < 0 || i >= dd->size) return(-1); + return(dd->invperm[i]); + +} /* end of Cudd_ReadInvPerm */ + + +/**Function******************************************************************** + + Synopsis [Returns the index of the ZDD variable currently in the i-th + position of the order.] + + Description [Returns the index of the ZDD variable currently in the + i-th position of the order. If the index is CUDD_CONST_INDEX, returns + CUDD_CONST_INDEX; otherwise, if the index is out of bounds returns -1.] + + SideEffects [None] + + SeeAlso [Cudd_ReadPerm Cudd_ReadInvPermZdd] + +******************************************************************************/ +int +Cudd_ReadInvPermZdd( + DdManager * dd, + int i) +{ + if (i == CUDD_CONST_INDEX) return(CUDD_CONST_INDEX); + if (i < 0 || i >= dd->sizeZ) return(-1); + return(dd->invpermZ[i]); + +} /* end of Cudd_ReadInvPermZdd */ + + +/**Function******************************************************************** + + Synopsis [Returns the i-th element of the vars array.] + + Description [Returns the i-th element of the vars array if it falls + within the array bounds; NULL otherwise. If i is the index of an + existing variable, this function produces the same result as + Cudd_bddIthVar. However, if the i-th var does not exist yet, + Cudd_bddIthVar will create it, whereas Cudd_ReadVars will not.] + + SideEffects [None] + + SeeAlso [Cudd_bddIthVar] + +******************************************************************************/ +DdNode * +Cudd_ReadVars( + DdManager * dd, + int i) +{ + if (i < 0 || i > dd->size) return(NULL); + return(dd->vars[i]); + +} /* end of Cudd_ReadVars */ + + +/**Function******************************************************************** + + Synopsis [Reads the epsilon parameter of the manager.] + + Description [Reads the epsilon parameter of the manager. The epsilon + parameter control the comparison between floating point numbers.] + + SideEffects [None] + + SeeAlso [Cudd_SetEpsilon] + +******************************************************************************/ +CUDD_VALUE_TYPE +Cudd_ReadEpsilon( + DdManager * dd) +{ + return(dd->epsilon); + +} /* end of Cudd_ReadEpsilon */ + + +/**Function******************************************************************** + + Synopsis [Sets the epsilon parameter of the manager to ep.] + + Description [Sets the epsilon parameter of the manager to ep. The epsilon + parameter control the comparison between floating point numbers.] + + SideEffects [None] + + SeeAlso [Cudd_ReadEpsilon] + +******************************************************************************/ +void +Cudd_SetEpsilon( + DdManager * dd, + CUDD_VALUE_TYPE ep) +{ + dd->epsilon = ep; + +} /* end of Cudd_SetEpsilon */ + + +/**Function******************************************************************** + + Synopsis [Reads the groupcheck parameter of the manager.] + + Description [Reads the groupcheck parameter of the manager. The + groupcheck parameter determines the aggregation criterion in group + sifting.] + + SideEffects [None] + + SeeAlso [Cudd_SetGroupcheck] + +******************************************************************************/ +Cudd_AggregationType +Cudd_ReadGroupcheck( + DdManager * dd) +{ + return(dd->groupcheck); + +} /* end of Cudd_ReadGroupCheck */ + + +/**Function******************************************************************** + + Synopsis [Sets the parameter groupcheck of the manager to gc.] + + Description [Sets the parameter groupcheck of the manager to gc. The + groupcheck parameter determines the aggregation criterion in group + sifting.] + + SideEffects [None] + + SeeAlso [Cudd_ReadGroupCheck] + +******************************************************************************/ +void +Cudd_SetGroupcheck( + DdManager * dd, + Cudd_AggregationType gc) +{ + dd->groupcheck = gc; + +} /* end of Cudd_SetGroupcheck */ + + +/**Function******************************************************************** + + Synopsis [Tells whether garbage collection is enabled.] + + Description [Returns 1 if garbage collection is enabled; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_EnableGarbageCollection Cudd_DisableGarbageCollection] + +******************************************************************************/ +int +Cudd_GarbageCollectionEnabled( + DdManager * dd) +{ + return(dd->gcEnabled); + +} /* end of Cudd_GarbageCollectionEnabled */ + + +/**Function******************************************************************** + + Synopsis [Enables garbage collection.] + + Description [Enables garbage collection. Garbage collection is + initially enabled. Therefore it is necessary to call this function + only if garbage collection has been explicitly disabled.] + + SideEffects [None] + + SeeAlso [Cudd_DisableGarbageCollection Cudd_GarbageCollectionEnabled] + +******************************************************************************/ +void +Cudd_EnableGarbageCollection( + DdManager * dd) +{ + dd->gcEnabled = 1; + +} /* end of Cudd_EnableGarbageCollection */ + + +/**Function******************************************************************** + + Synopsis [Disables garbage collection.] + + Description [Disables garbage collection. Garbage collection is + initially enabled. This function may be called to disable it. + However, garbage collection will still occur when a new node must be + created and no memory is left, or when garbage collection is required + for correctness. (E.g., before reordering.)] + + SideEffects [None] + + SeeAlso [Cudd_EnableGarbageCollection Cudd_GarbageCollectionEnabled] + +******************************************************************************/ +void +Cudd_DisableGarbageCollection( + DdManager * dd) +{ + dd->gcEnabled = 0; + +} /* end of Cudd_DisableGarbageCollection */ + + +/**Function******************************************************************** + + Synopsis [Tells whether dead nodes are counted towards triggering + reordering.] + + Description [Tells whether dead nodes are counted towards triggering + reordering. Returns 1 if dead nodes are counted; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_TurnOnCountDead Cudd_TurnOffCountDead] + +******************************************************************************/ +int +Cudd_DeadAreCounted( + DdManager * dd) +{ + return(dd->countDead == 0 ? 1 : 0); + +} /* end of Cudd_DeadAreCounted */ + + +/**Function******************************************************************** + + Synopsis [Causes the dead nodes to be counted towards triggering + reordering.] + + Description [Causes the dead nodes to be counted towards triggering + reordering. This causes more frequent reorderings. By default dead + nodes are not counted.] + + SideEffects [Changes the manager.] + + SeeAlso [Cudd_TurnOffCountDead Cudd_DeadAreCounted] + +******************************************************************************/ +void +Cudd_TurnOnCountDead( + DdManager * dd) +{ + dd->countDead = 0; + +} /* end of Cudd_TurnOnCountDead */ + + +/**Function******************************************************************** + + Synopsis [Causes the dead nodes not to be counted towards triggering + reordering.] + + Description [Causes the dead nodes not to be counted towards + triggering reordering. This causes less frequent reorderings. By + default dead nodes are not counted. Therefore there is no need to + call this function unless Cudd_TurnOnCountDead has been previously + called.] + + SideEffects [Changes the manager.] + + SeeAlso [Cudd_TurnOnCountDead Cudd_DeadAreCounted] + +******************************************************************************/ +void +Cudd_TurnOffCountDead( + DdManager * dd) +{ + dd->countDead = ~0; + +} /* end of Cudd_TurnOffCountDead */ + + +/**Function******************************************************************** + + Synopsis [Returns the current value of the recombination parameter used + in group sifting.] + + Description [Returns the current value of the recombination + parameter used in group sifting. A larger (positive) value makes the + aggregation of variables due to the second difference criterion more + likely. A smaller (negative) value makes aggregation less likely.] + + SideEffects [None] + + SeeAlso [Cudd_SetRecomb] + +******************************************************************************/ +int +Cudd_ReadRecomb( + DdManager * dd) +{ + return(dd->recomb); + +} /* end of Cudd_ReadRecomb */ + + +/**Function******************************************************************** + + Synopsis [Sets the value of the recombination parameter used in group + sifting.] + + Description [Sets the value of the recombination parameter used in + group sifting. A larger (positive) value makes the aggregation of + variables due to the second difference criterion more likely. A + smaller (negative) value makes aggregation less likely. The default + value is 0.] + + SideEffects [Changes the manager.] + + SeeAlso [Cudd_ReadRecomb] + +******************************************************************************/ +void +Cudd_SetRecomb( + DdManager * dd, + int recomb) +{ + dd->recomb = recomb; + +} /* end of Cudd_SetRecomb */ + + +/**Function******************************************************************** + + Synopsis [Returns the current value of the symmviolation parameter used + in group sifting.] + + Description [Returns the current value of the symmviolation + parameter. This parameter is used in group sifting to decide how + many violations to the symmetry conditions f10 = f01 or + f11 = f00 are tolerable when checking for aggregation + due to extended symmetry. The value should be between 0 and 100. A + small value causes fewer variables to be aggregated. The default + value is 0.] + + SideEffects [None] + + SeeAlso [Cudd_SetSymmviolation] + +******************************************************************************/ +int +Cudd_ReadSymmviolation( + DdManager * dd) +{ + return(dd->symmviolation); + +} /* end of Cudd_ReadSymmviolation */ + + +/**Function******************************************************************** + + Synopsis [Sets the value of the symmviolation parameter used + in group sifting.] + + Description [Sets the value of the symmviolation + parameter. This parameter is used in group sifting to decide how + many violations to the symmetry conditions f10 = f01 or + f11 = f00 are tolerable when checking for aggregation + due to extended symmetry. The value should be between 0 and 100. A + small value causes fewer variables to be aggregated. The default + value is 0.] + + SideEffects [Changes the manager.] + + SeeAlso [Cudd_ReadSymmviolation] + +******************************************************************************/ +void +Cudd_SetSymmviolation( + DdManager * dd, + int symmviolation) +{ + dd->symmviolation = symmviolation; + +} /* end of Cudd_SetSymmviolation */ + + +/**Function******************************************************************** + + Synopsis [Returns the current value of the arcviolation parameter used + in group sifting.] + + Description [Returns the current value of the arcviolation + parameter. This parameter is used in group sifting to decide how + many arcs into y not coming from x are + tolerable when checking for aggregation due to extended + symmetry. The value should be between 0 and 100. A small value + causes fewer variables to be aggregated. The default value is 0.] + + SideEffects [None] + + SeeAlso [Cudd_SetArcviolation] + +******************************************************************************/ +int +Cudd_ReadArcviolation( + DdManager * dd) +{ + return(dd->arcviolation); + +} /* end of Cudd_ReadArcviolation */ + + +/**Function******************************************************************** + + Synopsis [Sets the value of the arcviolation parameter used + in group sifting.] + + Description [Sets the value of the arcviolation + parameter. This parameter is used in group sifting to decide how + many arcs into y not coming from x are + tolerable when checking for aggregation due to extended + symmetry. The value should be between 0 and 100. A small value + causes fewer variables to be aggregated. The default value is 0.] + + SideEffects [None] + + SeeAlso [Cudd_ReadArcviolation] + +******************************************************************************/ +void +Cudd_SetArcviolation( + DdManager * dd, + int arcviolation) +{ + dd->arcviolation = arcviolation; + +} /* end of Cudd_SetArcviolation */ + + +/**Function******************************************************************** + + Synopsis [Reads the current size of the population used by the + genetic algorithm for reordering.] + + Description [Reads the current size of the population used by the + genetic algorithm for variable reordering. A larger population size will + cause the genetic algorithm to take more time, but will generally + produce better results. The default value is 0, in which case the + package uses three times the number of variables as population size, + with a maximum of 120.] + + SideEffects [None] + + SeeAlso [Cudd_SetPopulationSize] + +******************************************************************************/ +int +Cudd_ReadPopulationSize( + DdManager * dd) +{ + return(dd->populationSize); + +} /* end of Cudd_ReadPopulationSize */ + + +/**Function******************************************************************** + + Synopsis [Sets the size of the population used by the + genetic algorithm for reordering.] + + Description [Sets the size of the population used by the + genetic algorithm for variable reordering. A larger population size will + cause the genetic algorithm to take more time, but will generally + produce better results. The default value is 0, in which case the + package uses three times the number of variables as population size, + with a maximum of 120.] + + SideEffects [Changes the manager.] + + SeeAlso [Cudd_ReadPopulationSize] + +******************************************************************************/ +void +Cudd_SetPopulationSize( + DdManager * dd, + int populationSize) +{ + dd->populationSize = populationSize; + +} /* end of Cudd_SetPopulationSize */ + + +/**Function******************************************************************** + + Synopsis [Reads the current number of crossovers used by the + genetic algorithm for reordering.] + + Description [Reads the current number of crossovers used by the + genetic algorithm for variable reordering. A larger number of crossovers will + cause the genetic algorithm to take more time, but will generally + produce better results. The default value is 0, in which case the + package uses three times the number of variables as number of crossovers, + with a maximum of 60.] + + SideEffects [None] + + SeeAlso [Cudd_SetNumberXovers] + +******************************************************************************/ +int +Cudd_ReadNumberXovers( + DdManager * dd) +{ + return(dd->numberXovers); + +} /* end of Cudd_ReadNumberXovers */ + + +/**Function******************************************************************** + + Synopsis [Sets the number of crossovers used by the + genetic algorithm for reordering.] + + Description [Sets the number of crossovers used by the genetic + algorithm for variable reordering. A larger number of crossovers + will cause the genetic algorithm to take more time, but will + generally produce better results. The default value is 0, in which + case the package uses three times the number of variables as number + of crossovers, with a maximum of 60.] + + SideEffects [None] + + SeeAlso [Cudd_ReadNumberXovers] + +******************************************************************************/ +void +Cudd_SetNumberXovers( + DdManager * dd, + int numberXovers) +{ + dd->numberXovers = numberXovers; + +} /* end of Cudd_SetNumberXovers */ + +/**Function******************************************************************** + + Synopsis [Returns the memory in use by the manager measured in bytes.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +long +Cudd_ReadMemoryInUse( + DdManager * dd) +{ + return(dd->memused); + +} /* end of Cudd_ReadMemoryInUse */ + + +/**Function******************************************************************** + + Synopsis [Prints out statistics and settings for a CUDD manager.] + + Description [Prints out statistics and settings for a CUDD manager. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +Cudd_PrintInfo( + DdManager * dd, + FILE * fp) +{ + int retval; + Cudd_ReorderingType autoMethod, autoMethodZ; + + /* Modifiable parameters. */ + retval = fprintf(fp,"**** CUDD modifiable parameters ****\n"); + if (retval == EOF) return(0); + retval = fprintf(fp,"Hard limit for cache size: %u\n", + Cudd_ReadMaxCacheHard(dd)); + if (retval == EOF) return(0); + retval = fprintf(fp,"Cache hit threshold for resizing: %u%%\n", + Cudd_ReadMinHit(dd)); + if (retval == EOF) return(0); + retval = fprintf(fp,"Garbage collection enabled: %s\n", + Cudd_GarbageCollectionEnabled(dd) ? "yes" : "no"); + if (retval == EOF) return(0); + retval = fprintf(fp,"Limit for fast unique table growth: %u\n", + Cudd_ReadLooseUpTo(dd)); + if (retval == EOF) return(0); + retval = fprintf(fp, + "Maximum number of variables sifted per reordering: %d\n", + Cudd_ReadSiftMaxVar(dd)); + if (retval == EOF) return(0); + retval = fprintf(fp, + "Maximum number of variable swaps per reordering: %d\n", + Cudd_ReadSiftMaxSwap(dd)); + if (retval == EOF) return(0); + retval = fprintf(fp,"Maximum growth while sifting a variable: %g\n", + Cudd_ReadMaxGrowth(dd)); + if (retval == EOF) return(0); + retval = fprintf(fp,"Dynamic reordering of BDDs enabled: %s\n", + Cudd_ReorderingStatus(dd,&autoMethod) ? "yes" : "no"); + if (retval == EOF) return(0); + retval = fprintf(fp,"Default BDD reordering method: %d\n", autoMethod); + if (retval == EOF) return(0); + retval = fprintf(fp,"Dynamic reordering of ZDDs enabled: %s\n", + Cudd_ReorderingStatusZdd(dd,&autoMethodZ) ? "yes" : "no"); + if (retval == EOF) return(0); + retval = fprintf(fp,"Default ZDD reordering method: %d\n", autoMethodZ); + if (retval == EOF) return(0); + retval = fprintf(fp,"Realignment of ZDDs to BDDs enabled: %s\n", + Cudd_zddRealignmentEnabled(dd) ? "yes" : "no"); + if (retval == EOF) return(0); + retval = fprintf(fp,"Realignment of BDDs to ZDDs enabled: %s\n", + Cudd_bddRealignmentEnabled(dd) ? "yes" : "no"); + if (retval == EOF) return(0); + retval = fprintf(fp,"Dead nodes counted in triggering reordering: %s\n", + Cudd_DeadAreCounted(dd) ? "yes" : "no"); + if (retval == EOF) return(0); + retval = fprintf(fp,"Group checking criterion: %d\n", + Cudd_ReadGroupcheck(dd)); + if (retval == EOF) return(0); + retval = fprintf(fp,"Recombination threshold: %d\n", Cudd_ReadRecomb(dd)); + if (retval == EOF) return(0); + retval = fprintf(fp,"Symmetry violation threshold: %d\n", + Cudd_ReadSymmviolation(dd)); + if (retval == EOF) return(0); + retval = fprintf(fp,"Arc violation threshold: %d\n", + Cudd_ReadArcviolation(dd)); + if (retval == EOF) return(0); + retval = fprintf(fp,"GA population size: %d\n", + Cudd_ReadPopulationSize(dd)); + if (retval == EOF) return(0); + retval = fprintf(fp,"Number of crossovers for GA: %d\n", + Cudd_ReadNumberXovers(dd)); + if (retval == EOF) return(0); + retval = fprintf(fp,"Next reordering threshold: %u\n", + Cudd_ReadNextReordering(dd)); + if (retval == EOF) return(0); + + /* Non-modifiable parameters. */ + retval = fprintf(fp,"**** CUDD non-modifiable parameters ****\n"); + if (retval == EOF) return(0); + retval = fprintf(fp,"Memory in use: %ld\n", Cudd_ReadMemoryInUse(dd)); + if (retval == EOF) return(0); + retval = fprintf(fp,"Peak number of nodes: %ld\n", + Cudd_ReadPeakNodeCount(dd)); + if (retval == EOF) return(0); + retval = fprintf(fp,"Peak number of live nodes: %d\n", + Cudd_ReadPeakLiveNodeCount(dd)); + if (retval == EOF) return(0); + retval = fprintf(fp,"Number of BDD variables: %d\n", dd->size); + if (retval == EOF) return(0); + retval = fprintf(fp,"Number of ZDD variables: %d\n", dd->sizeZ); + if (retval == EOF) return(0); + retval = fprintf(fp,"Number of cache entries: %u\n", dd->cacheSlots); + if (retval == EOF) return(0); + retval = fprintf(fp,"Number of cache look-ups: %.0f\n", + Cudd_ReadCacheLookUps(dd)); + if (retval == EOF) return(0); + retval = fprintf(fp,"Number of cache hits: %.0f\n", + Cudd_ReadCacheHits(dd)); + if (retval == EOF) return(0); + retval = fprintf(fp,"Number of cache insertions: %.0f\n", + dd->cacheinserts); + if (retval == EOF) return(0); + retval = fprintf(fp,"Number of cache collisions: %.0f\n", + dd->cachecollisions); + if (retval == EOF) return(0); + retval = fprintf(fp,"Number of cache deletions: %.0f\n", + dd->cachedeletions); + if (retval == EOF) return(0); + retval = cuddCacheProfile(dd,fp); + if (retval == 0) return(0); + retval = fprintf(fp,"Soft limit for cache size: %u\n", + Cudd_ReadMaxCache(dd)); + if (retval == EOF) return(0); + retval = fprintf(fp,"Number of buckets in unique table: %u\n", dd->slots); + if (retval == EOF) return(0); + retval = fprintf(fp,"Used buckets in unique table: %.2f%% (expected %.2f%%)\n", + 100.0 * Cudd_ReadUsedSlots(dd), + 100.0 * Cudd_ExpectedUsedSlots(dd)); + if (retval == EOF) return(0); +#ifdef DD_UNIQUE_PROFILE + retval = fprintf(fp,"Unique lookups: %.0f\n", dd->uniqueLookUps); + if (retval == EOF) return(0); + retval = fprintf(fp,"Unique links: %.0f (%g per lookup)\n", + dd->uniqueLinks, dd->uniqueLinks / dd->uniqueLookUps); + if (retval == EOF) return(0); +#endif + retval = fprintf(fp,"Number of BDD and ADD nodes: %u\n", dd->keys); + if (retval == EOF) return(0); + retval = fprintf(fp,"Number of ZDD nodes: %u\n", dd->keysZ); + if (retval == EOF) return(0); + retval = fprintf(fp,"Number of dead BDD and ADD nodes: %u\n", dd->dead); + if (retval == EOF) return(0); + retval = fprintf(fp,"Number of dead ZDD nodes: %u\n", dd->deadZ); + if (retval == EOF) return(0); + retval = fprintf(fp,"Total number of nodes allocated: %.0f\n", + dd->allocated); + if (retval == EOF) return(0); + retval = fprintf(fp,"Total number of nodes reclaimed: %.0f\n", + dd->reclaimed); + if (retval == EOF) return(0); +#if DD_STATS + retval = fprintf(fp,"Nodes freed: %.0f\n", dd->nodesFreed); + if (retval == EOF) return(0); + retval = fprintf(fp,"Nodes dropped: %.0f\n", dd->nodesDropped); + if (retval == EOF) return(0); +#endif +#if DD_COUNT + retval = fprintf(fp,"Number of recursive calls: %.0f\n", + Cudd_ReadRecursiveCalls(dd)); + if (retval == EOF) return(0); +#endif + retval = fprintf(fp,"Garbage collections so far: %d\n", + Cudd_ReadGarbageCollections(dd)); + if (retval == EOF) return(0); + retval = fprintf(fp,"Time for garbage collection: %.2f sec\n", + ((double)Cudd_ReadGarbageCollectionTime(dd)/1000.0)); + if (retval == EOF) return(0); + retval = fprintf(fp,"Reorderings so far: %d\n", dd->reorderings); + if (retval == EOF) return(0); + retval = fprintf(fp,"Time for reordering: %.2f sec\n", + ((double)Cudd_ReadReorderingTime(dd)/1000.0)); + if (retval == EOF) return(0); +#if DD_COUNT + retval = fprintf(fp,"Node swaps in reordering: %.0f\n", + Cudd_ReadSwapSteps(dd)); + if (retval == EOF) return(0); +#endif + + return(1); + +} /* end of Cudd_PrintInfo */ + + +/**Function******************************************************************** + + Synopsis [Reports the peak number of nodes.] + + Description [Reports the peak number of nodes. This number includes + node on the free list. At the peak, the number of nodes on the free + list is guaranteed to be less than DD_MEM_CHUNK.] + + SideEffects [None] + + SeeAlso [Cudd_ReadNodeCount Cudd_PrintInfo] + +******************************************************************************/ +long +Cudd_ReadPeakNodeCount( + DdManager * dd) +{ + long count = 0; + DdNodePtr *scan = dd->memoryList; + + while (scan != NULL) { + count += DD_MEM_CHUNK; + scan = (DdNodePtr *) *scan; + } + return(count); + +} /* end of Cudd_ReadPeakNodeCount */ + + +/**Function******************************************************************** + + Synopsis [Reports the peak number of live nodes.] + + Description [Reports the peak number of live nodes. This count is kept + only if CUDD is compiled with DD_STATS defined. If DD_STATS is not + defined, this function returns -1.] + + SideEffects [None] + + SeeAlso [Cudd_ReadNodeCount Cudd_PrintInfo Cudd_ReadPeakNodeCount] + +******************************************************************************/ +int +Cudd_ReadPeakLiveNodeCount( + DdManager * dd) +{ + unsigned int live = dd->keys - dd->dead; + + if (live > dd->peakLiveNodes) { + dd->peakLiveNodes = live; + } + return((int)dd->peakLiveNodes); + +} /* end of Cudd_ReadPeakLiveNodeCount */ + + +/**Function******************************************************************** + + Synopsis [Reports the number of nodes in BDDs and ADDs.] + + Description [Reports the number of live nodes in BDDs and ADDs. This + number does not include the isolated projection functions and the + unused constants. These nodes that are not counted are not part of + the DDs manipulated by the application.] + + SideEffects [None] + + SeeAlso [Cudd_ReadPeakNodeCount Cudd_zddReadNodeCount] + +******************************************************************************/ +long +Cudd_ReadNodeCount( + DdManager * dd) +{ + long count; + int i; + +#ifndef DD_NO_DEATH_ROW + cuddClearDeathRow(dd); +#endif + + count = dd->keys - dd->dead; + + /* Count isolated projection functions. Their number is subtracted + ** from the node count because they are not part of the BDDs. + */ + for (i=0; i < dd->size; i++) { + if (dd->vars[i]->ref == 1) count--; + } + /* Subtract from the count the unused constants. */ + if (DD_ZERO(dd)->ref == 1) count--; + if (DD_PLUS_INFINITY(dd)->ref == 1) count--; + if (DD_MINUS_INFINITY(dd)->ref == 1) count--; + + return(count); + +} /* end of Cudd_ReadNodeCount */ + + + +/**Function******************************************************************** + + Synopsis [Reports the number of nodes in ZDDs.] + + Description [Reports the number of nodes in ZDDs. This + number always includes the two constants 1 and 0.] + + SideEffects [None] + + SeeAlso [Cudd_ReadPeakNodeCount Cudd_ReadNodeCount] + +******************************************************************************/ +long +Cudd_zddReadNodeCount( + DdManager * dd) +{ + return(dd->keysZ - dd->deadZ + 2); + +} /* end of Cudd_zddReadNodeCount */ + + +/**Function******************************************************************** + + Synopsis [Adds a function to a hook.] + + Description [Adds a function to a hook. A hook is a list of + application-provided functions called on certain occasions by the + package. Returns 1 if the function is successfully added; 2 if the + function was already in the list; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_RemoveHook] + +******************************************************************************/ +int +Cudd_AddHook( + DdManager * dd, + int (*f)(DdManager *, char *, void *) , + Cudd_HookType where) +{ + DdHook **hook, *nextHook, *newHook; + + switch (where) { + case CUDD_PRE_GC_HOOK: + hook = &(dd->preGCHook); + break; + case CUDD_POST_GC_HOOK: + hook = &(dd->postGCHook); + break; + case CUDD_PRE_REORDERING_HOOK: + hook = &(dd->preReorderingHook); + break; + case CUDD_POST_REORDERING_HOOK: + hook = &(dd->postReorderingHook); + break; + default: + return(0); + } + /* Scan the list and find whether the function is already there. + ** If so, just return. */ + nextHook = *hook; + while (nextHook != NULL) { + if (nextHook->f == f) { + return(2); + } + hook = &(nextHook->next); + nextHook = nextHook->next; + } + /* The function was not in the list. Create a new item and append it + ** to the end of the list. */ + newHook = ALLOC(DdHook,1); + if (newHook == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + newHook->next = NULL; + newHook->f = f; + *hook = newHook; + return(1); + +} /* end of Cudd_AddHook */ + + +/**Function******************************************************************** + + Synopsis [Removes a function from a hook.] + + Description [Removes a function from a hook. A hook is a list of + application-provided functions called on certain occasions by the + package. Returns 1 if successful; 0 the function was not in the list.] + + SideEffects [None] + + SeeAlso [Cudd_AddHook] + +******************************************************************************/ +int +Cudd_RemoveHook( + DdManager * dd, + int (*f)(DdManager *, char *, void *) , + Cudd_HookType where) +{ + DdHook **hook, *nextHook; + + switch (where) { + case CUDD_PRE_GC_HOOK: + hook = &(dd->preGCHook); + break; + case CUDD_POST_GC_HOOK: + hook = &(dd->postGCHook); + break; + case CUDD_PRE_REORDERING_HOOK: + hook = &(dd->preReorderingHook); + break; + case CUDD_POST_REORDERING_HOOK: + hook = &(dd->postReorderingHook); + break; + default: + return(0); + } + nextHook = *hook; + while (nextHook != NULL) { + if (nextHook->f == f) { + *hook = nextHook->next; + FREE(nextHook); + return(1); + } + hook = &(nextHook->next); + nextHook = nextHook->next; + } + + return(0); + +} /* end of Cudd_RemoveHook */ + + +/**Function******************************************************************** + + Synopsis [Checks whether a function is in a hook.] + + Description [Checks whether a function is in a hook. A hook is a list of + application-provided functions called on certain occasions by the + package. Returns 1 if the function is found; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_AddHook Cudd_RemoveHook] + +******************************************************************************/ +int +Cudd_IsInHook( + DdManager * dd, + int (*f)(DdManager *, char *, void *) , + Cudd_HookType where) +{ + DdHook *hook; + + switch (where) { + case CUDD_PRE_GC_HOOK: + hook = dd->preGCHook; + break; + case CUDD_POST_GC_HOOK: + hook = dd->postGCHook; + break; + case CUDD_PRE_REORDERING_HOOK: + hook = dd->preReorderingHook; + break; + case CUDD_POST_REORDERING_HOOK: + hook = dd->postReorderingHook; + break; + default: + return(0); + } + /* Scan the list and find whether the function is already there. */ + while (hook != NULL) { + if (hook->f == f) { + return(1); + } + hook = hook->next; + } + return(0); + +} /* end of Cudd_IsInHook */ + + +/**Function******************************************************************** + + Synopsis [Sample hook function to call before reordering.] + + Description [Sample hook function to call before reordering. + Prints on the manager's stdout reordering method and initial size. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_StdPostReordHook] + +******************************************************************************/ +int +Cudd_StdPreReordHook( + DdManager *dd, + char *str, + void *data) +{ + Cudd_ReorderingType method = (Cudd_ReorderingType) (ptruint) data; + int retval; + + retval = fprintf(dd->out,"%s reordering with ", str); + if (retval == EOF) return(0); + switch (method) { + case CUDD_REORDER_SIFT_CONVERGE: + case CUDD_REORDER_SYMM_SIFT_CONV: + case CUDD_REORDER_GROUP_SIFT_CONV: + case CUDD_REORDER_WINDOW2_CONV: + case CUDD_REORDER_WINDOW3_CONV: + case CUDD_REORDER_WINDOW4_CONV: + case CUDD_REORDER_LINEAR_CONVERGE: + retval = fprintf(dd->out,"converging "); + if (retval == EOF) return(0); + break; + default: + break; + } + switch (method) { + case CUDD_REORDER_RANDOM: + case CUDD_REORDER_RANDOM_PIVOT: + retval = fprintf(dd->out,"random"); + break; + case CUDD_REORDER_SIFT: + case CUDD_REORDER_SIFT_CONVERGE: + retval = fprintf(dd->out,"sifting"); + break; + case CUDD_REORDER_SYMM_SIFT: + case CUDD_REORDER_SYMM_SIFT_CONV: + retval = fprintf(dd->out,"symmetric sifting"); + break; + case CUDD_REORDER_LAZY_SIFT: + retval = fprintf(dd->out,"lazy sifting"); + break; + case CUDD_REORDER_GROUP_SIFT: + case CUDD_REORDER_GROUP_SIFT_CONV: + retval = fprintf(dd->out,"group sifting"); + break; + case CUDD_REORDER_WINDOW2: + case CUDD_REORDER_WINDOW3: + case CUDD_REORDER_WINDOW4: + case CUDD_REORDER_WINDOW2_CONV: + case CUDD_REORDER_WINDOW3_CONV: + case CUDD_REORDER_WINDOW4_CONV: + retval = fprintf(dd->out,"window"); + break; + case CUDD_REORDER_ANNEALING: + retval = fprintf(dd->out,"annealing"); + break; + case CUDD_REORDER_GENETIC: + retval = fprintf(dd->out,"genetic"); + break; + case CUDD_REORDER_LINEAR: + case CUDD_REORDER_LINEAR_CONVERGE: + retval = fprintf(dd->out,"linear sifting"); + break; + case CUDD_REORDER_EXACT: + retval = fprintf(dd->out,"exact"); + break; + default: + return(0); + } + if (retval == EOF) return(0); + + retval = fprintf(dd->out,": from %ld to ... ", strcmp(str, "BDD") == 0 ? + Cudd_ReadNodeCount(dd) : Cudd_zddReadNodeCount(dd)); + if (retval == EOF) return(0); + fflush(dd->out); + return(1); + +} /* end of Cudd_StdPreReordHook */ + + +/**Function******************************************************************** + + Synopsis [Sample hook function to call after reordering.] + + Description [Sample hook function to call after reordering. + Prints on the manager's stdout final size and reordering time. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_StdPreReordHook] + +******************************************************************************/ +int +Cudd_StdPostReordHook( + DdManager *dd, + char *str, + void *data) +{ + long initialTime = (long) data; + int retval; + long finalTime = util_cpu_time(); + double totalTimeSec = (double)(finalTime - initialTime) / 1000.0; + + retval = fprintf(dd->out,"%ld nodes in %g sec\n", strcmp(str, "BDD") == 0 ? + Cudd_ReadNodeCount(dd) : Cudd_zddReadNodeCount(dd), + totalTimeSec); + if (retval == EOF) return(0); + retval = fflush(dd->out); + if (retval == EOF) return(0); + return(1); + +} /* end of Cudd_StdPostReordHook */ + + +/**Function******************************************************************** + + Synopsis [Enables reporting of reordering stats.] + + Description [Enables reporting of reordering stats. + Returns 1 if successful; 0 otherwise.] + + SideEffects [Installs functions in the pre-reordering and post-reordering + hooks.] + + SeeAlso [Cudd_DisableReorderingReporting Cudd_ReorderingReporting] + +******************************************************************************/ +int +Cudd_EnableReorderingReporting( + DdManager *dd) +{ + if (!Cudd_AddHook(dd, Cudd_StdPreReordHook, CUDD_PRE_REORDERING_HOOK)) { + return(0); + } + if (!Cudd_AddHook(dd, Cudd_StdPostReordHook, CUDD_POST_REORDERING_HOOK)) { + return(0); + } + return(1); + +} /* end of Cudd_EnableReorderingReporting */ + + +/**Function******************************************************************** + + Synopsis [Disables reporting of reordering stats.] + + Description [Disables reporting of reordering stats. + Returns 1 if successful; 0 otherwise.] + + SideEffects [Removes functions from the pre-reordering and post-reordering + hooks.] + + SeeAlso [Cudd_EnableReorderingReporting Cudd_ReorderingReporting] + +******************************************************************************/ +int +Cudd_DisableReorderingReporting( + DdManager *dd) +{ + if (!Cudd_RemoveHook(dd, Cudd_StdPreReordHook, CUDD_PRE_REORDERING_HOOK)) { + return(0); + } + if (!Cudd_RemoveHook(dd, Cudd_StdPostReordHook, CUDD_POST_REORDERING_HOOK)) { + return(0); + } + return(1); + +} /* end of Cudd_DisableReorderingReporting */ + + +/**Function******************************************************************** + + Synopsis [Returns 1 if reporting of reordering stats is enabled.] + + Description [Returns 1 if reporting of reordering stats is enabled; + 0 otherwise.] + + SideEffects [none] + + SeeAlso [Cudd_EnableReorderingReporting Cudd_DisableReorderingReporting] + +******************************************************************************/ +int +Cudd_ReorderingReporting( + DdManager *dd) +{ + return(Cudd_IsInHook(dd, Cudd_StdPreReordHook, CUDD_PRE_REORDERING_HOOK)); + +} /* end of Cudd_ReorderingReporting */ + + +/**Function******************************************************************** + + Synopsis [Returns the code of the last error.] + + Description [Returns the code of the last error. The error codes are + defined in cudd.h.] + + SideEffects [None] + + SeeAlso [Cudd_ClearErrorCode] + +******************************************************************************/ +Cudd_ErrorType +Cudd_ReadErrorCode( + DdManager *dd) +{ + return(dd->errorCode); + +} /* end of Cudd_ReadErrorCode */ + + +/**Function******************************************************************** + + Synopsis [Clear the error code of a manager.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_ReadErrorCode] + +******************************************************************************/ +void +Cudd_ClearErrorCode( + DdManager *dd) +{ + dd->errorCode = CUDD_NO_ERROR; + +} /* end of Cudd_ClearErrorCode */ + + +/**Function******************************************************************** + + Synopsis [Reads the stdout of a manager.] + + Description [Reads the stdout of a manager. This is the file pointer to + which messages normally going to stdout are written. It is initialized + to stdout. Cudd_SetStdout allows the application to redirect it.] + + SideEffects [None] + + SeeAlso [Cudd_SetStdout Cudd_ReadStderr] + +******************************************************************************/ +FILE * +Cudd_ReadStdout( + DdManager *dd) +{ + return(dd->out); + +} /* end of Cudd_ReadStdout */ + + +/**Function******************************************************************** + + Synopsis [Sets the stdout of a manager.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_ReadStdout Cudd_SetStderr] + +******************************************************************************/ +void +Cudd_SetStdout( + DdManager *dd, + FILE *fp) +{ + dd->out = fp; + +} /* end of Cudd_SetStdout */ + + +/**Function******************************************************************** + + Synopsis [Reads the stderr of a manager.] + + Description [Reads the stderr of a manager. This is the file pointer to + which messages normally going to stderr are written. It is initialized + to stderr. Cudd_SetStderr allows the application to redirect it.] + + SideEffects [None] + + SeeAlso [Cudd_SetStderr Cudd_ReadStdout] + +******************************************************************************/ +FILE * +Cudd_ReadStderr( + DdManager *dd) +{ + return(dd->err); + +} /* end of Cudd_ReadStderr */ + + +/**Function******************************************************************** + + Synopsis [Sets the stderr of a manager.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_ReadStderr Cudd_SetStdout] + +******************************************************************************/ +void +Cudd_SetStderr( + DdManager *dd, + FILE *fp) +{ + dd->err = fp; + +} /* end of Cudd_SetStderr */ + + +/**Function******************************************************************** + + Synopsis [Returns the threshold for the next dynamic reordering.] + + Description [Returns the threshold for the next dynamic reordering. + The threshold is in terms of number of nodes and is in effect only + if reordering is enabled. The count does not include the dead nodes, + unless the countDead parameter of the manager has been changed from + its default setting.] + + SideEffects [None] + + SeeAlso [Cudd_SetNextReordering] + +******************************************************************************/ +unsigned int +Cudd_ReadNextReordering( + DdManager *dd) +{ + return(dd->nextDyn); + +} /* end of Cudd_ReadNextReordering */ + + +/**Function******************************************************************** + + Synopsis [Sets the threshold for the next dynamic reordering.] + + Description [Sets the threshold for the next dynamic reordering. + The threshold is in terms of number of nodes and is in effect only + if reordering is enabled. The count does not include the dead nodes, + unless the countDead parameter of the manager has been changed from + its default setting.] + + SideEffects [None] + + SeeAlso [Cudd_ReadNextReordering] + +******************************************************************************/ +void +Cudd_SetNextReordering( + DdManager *dd, + unsigned int next) +{ + dd->nextDyn = next; + +} /* end of Cudd_SetNextReordering */ + + +/**Function******************************************************************** + + Synopsis [Reads the number of elementary reordering steps.] + + Description [] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +double +Cudd_ReadSwapSteps( + DdManager *dd) +{ +#ifdef DD_COUNT + return(dd->swapSteps); +#else + return(-1); +#endif + +} /* end of Cudd_ReadSwapSteps */ + + +/**Function******************************************************************** + + Synopsis [Reads the maximum allowed number of live nodes.] + + Description [Reads the maximum allowed number of live nodes. When this + number is exceeded, the package returns NULL.] + + SideEffects [none] + + SeeAlso [Cudd_SetMaxLive] + +******************************************************************************/ +unsigned int +Cudd_ReadMaxLive( + DdManager *dd) +{ + return(dd->maxLive); + +} /* end of Cudd_ReadMaxLive */ + + +/**Function******************************************************************** + + Synopsis [Sets the maximum allowed number of live nodes.] + + Description [Sets the maximum allowed number of live nodes. When this + number is exceeded, the package returns NULL.] + + SideEffects [none] + + SeeAlso [Cudd_ReadMaxLive] + +******************************************************************************/ +void +Cudd_SetMaxLive( + DdManager *dd, + unsigned int maxLive) +{ + dd->maxLive = maxLive; + +} /* end of Cudd_SetMaxLive */ + + +/**Function******************************************************************** + + Synopsis [Reads the maximum allowed memory.] + + Description [Reads the maximum allowed memory. When this + number is exceeded, the package returns NULL.] + + SideEffects [none] + + SeeAlso [Cudd_SetMaxMemory] + +******************************************************************************/ +long +Cudd_ReadMaxMemory( + DdManager *dd) +{ + return(dd->maxmemhard); + +} /* end of Cudd_ReadMaxMemory */ + + +/**Function******************************************************************** + + Synopsis [Sets the maximum allowed memory.] + + Description [Sets the maximum allowed memory. When this + number is exceeded, the package returns NULL.] + + SideEffects [none] + + SeeAlso [Cudd_ReadMaxMemory] + +******************************************************************************/ +void +Cudd_SetMaxMemory( + DdManager *dd, + long maxMemory) +{ + dd->maxmemhard = maxMemory; + +} /* end of Cudd_SetMaxMemory */ + + +/**Function******************************************************************** + + Synopsis [Prevents sifting of a variable.] + + Description [This function sets a flag to prevent sifting of a + variable. Returns 1 if successful; 0 otherwise (i.e., invalid + variable index).] + + SideEffects [Changes the "bindVar" flag in DdSubtable.] + + SeeAlso [Cudd_bddUnbindVar] + +******************************************************************************/ +int +Cudd_bddBindVar( + DdManager *dd /* manager */, + int index /* variable index */) +{ + if (index >= dd->size || index < 0) return(0); + dd->subtables[dd->perm[index]].bindVar = 1; + return(1); + +} /* end of Cudd_bddBindVar */ + + +/**Function******************************************************************** + + Synopsis [Allows the sifting of a variable.] + + Description [This function resets the flag that prevents the sifting + of a variable. In successive variable reorderings, the variable will + NOT be skipped, that is, sifted. Initially all variables can be + sifted. It is necessary to call this function only to re-enable + sifting after a call to Cudd_bddBindVar. Returns 1 if successful; 0 + otherwise (i.e., invalid variable index).] + + SideEffects [Changes the "bindVar" flag in DdSubtable.] + + SeeAlso [Cudd_bddBindVar] + +******************************************************************************/ +int +Cudd_bddUnbindVar( + DdManager *dd /* manager */, + int index /* variable index */) +{ + if (index >= dd->size || index < 0) return(0); + dd->subtables[dd->perm[index]].bindVar = 0; + return(1); + +} /* end of Cudd_bddUnbindVar */ + + +/**Function******************************************************************** + + Synopsis [Tells whether a variable can be sifted.] + + Description [This function returns 1 if a variable is enabled for + sifting. Initially all variables can be sifted. This function returns + 0 only if there has been a previous call to Cudd_bddBindVar for that + variable not followed by a call to Cudd_bddUnbindVar. The function returns + 0 also in the case in which the index of the variable is out of bounds.] + + SideEffects [none] + + SeeAlso [Cudd_bddBindVar Cudd_bddUnbindVar] + +******************************************************************************/ +int +Cudd_bddVarIsBound( + DdManager *dd /* manager */, + int index /* variable index */) +{ + if (index >= dd->size || index < 0) return(0); + return(dd->subtables[dd->perm[index]].bindVar); + +} /* end of Cudd_bddVarIsBound */ + + +/**Function******************************************************************** + + Synopsis [Sets a variable type to primary input.] + + Description [Sets a variable type to primary input. The variable type is + used by lazy sifting. Returns 1 if successful; 0 otherwise.] + + SideEffects [modifies the manager] + + SeeAlso [Cudd_bddSetPsVar Cudd_bddSetNsVar Cudd_bddIsPiVar] + +******************************************************************************/ +int +Cudd_bddSetPiVar( + DdManager *dd /* manager */, + int index /* variable index */) +{ + if (index >= dd->size || index < 0) return (0); + dd->subtables[dd->perm[index]].varType = CUDD_VAR_PRIMARY_INPUT; + return(1); + +} /* end of Cudd_bddSetPiVar */ + + +/**Function******************************************************************** + + Synopsis [Sets a variable type to present state.] + + Description [Sets a variable type to present state. The variable type is + used by lazy sifting. Returns 1 if successful; 0 otherwise.] + + SideEffects [modifies the manager] + + SeeAlso [Cudd_bddSetPiVar Cudd_bddSetNsVar Cudd_bddIsPsVar] + +******************************************************************************/ +int +Cudd_bddSetPsVar( + DdManager *dd /* manager */, + int index /* variable index */) +{ + if (index >= dd->size || index < 0) return (0); + dd->subtables[dd->perm[index]].varType = CUDD_VAR_PRESENT_STATE; + return(1); + +} /* end of Cudd_bddSetPsVar */ + + +/**Function******************************************************************** + + Synopsis [Sets a variable type to next state.] + + Description [Sets a variable type to next state. The variable type is + used by lazy sifting. Returns 1 if successful; 0 otherwise.] + + SideEffects [modifies the manager] + + SeeAlso [Cudd_bddSetPiVar Cudd_bddSetPsVar Cudd_bddIsNsVar] + +******************************************************************************/ +int +Cudd_bddSetNsVar( + DdManager *dd /* manager */, + int index /* variable index */) +{ + if (index >= dd->size || index < 0) return (0); + dd->subtables[dd->perm[index]].varType = CUDD_VAR_NEXT_STATE; + return(1); + +} /* end of Cudd_bddSetNsVar */ + + +/**Function******************************************************************** + + Synopsis [Checks whether a variable is primary input.] + + Description [Checks whether a variable is primary input. Returns 1 if + the variable's type is primary input; 0 if the variable exists but is + not a primary input; -1 if the variable does not exist.] + + SideEffects [none] + + SeeAlso [Cudd_bddSetPiVar Cudd_bddIsPsVar Cudd_bddIsNsVar] + +******************************************************************************/ +int +Cudd_bddIsPiVar( + DdManager *dd /* manager */, + int index /* variable index */) +{ + if (index >= dd->size || index < 0) return -1; + return (dd->subtables[dd->perm[index]].varType == CUDD_VAR_PRIMARY_INPUT); + +} /* end of Cudd_bddIsPiVar */ + + +/**Function******************************************************************** + + Synopsis [Checks whether a variable is present state.] + + Description [Checks whether a variable is present state. Returns 1 if + the variable's type is present state; 0 if the variable exists but is + not a present state; -1 if the variable does not exist.] + + SideEffects [none] + + SeeAlso [Cudd_bddSetPsVar Cudd_bddIsPiVar Cudd_bddIsNsVar] + +******************************************************************************/ +int +Cudd_bddIsPsVar( + DdManager *dd, + int index) +{ + if (index >= dd->size || index < 0) return -1; + return (dd->subtables[dd->perm[index]].varType == CUDD_VAR_PRESENT_STATE); + +} /* end of Cudd_bddIsPsVar */ + + +/**Function******************************************************************** + + Synopsis [Checks whether a variable is next state.] + + Description [Checks whether a variable is next state. Returns 1 if + the variable's type is present state; 0 if the variable exists but is + not a present state; -1 if the variable does not exist.] + + SideEffects [none] + + SeeAlso [Cudd_bddSetNsVar Cudd_bddIsPiVar Cudd_bddIsPsVar] + +******************************************************************************/ +int +Cudd_bddIsNsVar( + DdManager *dd, + int index) +{ + if (index >= dd->size || index < 0) return -1; + return (dd->subtables[dd->perm[index]].varType == CUDD_VAR_NEXT_STATE); + +} /* end of Cudd_bddIsNsVar */ + + +/**Function******************************************************************** + + Synopsis [Sets a corresponding pair index for a given index.] + + Description [Sets a corresponding pair index for a given index. + These pair indices are present and next state variable. Returns 1 if + successful; 0 otherwise.] + + SideEffects [modifies the manager] + + SeeAlso [Cudd_bddReadPairIndex] + +******************************************************************************/ +int +Cudd_bddSetPairIndex( + DdManager *dd /* manager */, + int index /* variable index */, + int pairIndex /* corresponding variable index */) +{ + if (index >= dd->size || index < 0) return(0); + dd->subtables[dd->perm[index]].pairIndex = pairIndex; + return(1); + +} /* end of Cudd_bddSetPairIndex */ + + +/**Function******************************************************************** + + Synopsis [Reads a corresponding pair index for a given index.] + + Description [Reads a corresponding pair index for a given index. + These pair indices are present and next state variable. Returns the + corresponding variable index if the variable exists; -1 otherwise.] + + SideEffects [modifies the manager] + + SeeAlso [Cudd_bddSetPairIndex] + +******************************************************************************/ +int +Cudd_bddReadPairIndex( + DdManager *dd, + int index) +{ + if (index >= dd->size || index < 0) return -1; + return dd->subtables[dd->perm[index]].pairIndex; + +} /* end of Cudd_bddReadPairIndex */ + + +/**Function******************************************************************** + + Synopsis [Sets a variable to be grouped.] + + Description [Sets a variable to be grouped. This function is used for + lazy sifting. Returns 1 if successful; 0 otherwise.] + + SideEffects [modifies the manager] + + SeeAlso [Cudd_bddSetVarHardGroup Cudd_bddResetVarToBeGrouped] + +******************************************************************************/ +int +Cudd_bddSetVarToBeGrouped( + DdManager *dd, + int index) +{ + if (index >= dd->size || index < 0) return(0); + if (dd->subtables[dd->perm[index]].varToBeGrouped <= CUDD_LAZY_SOFT_GROUP) { + dd->subtables[dd->perm[index]].varToBeGrouped = CUDD_LAZY_SOFT_GROUP; + } + return(1); + +} /* end of Cudd_bddSetVarToBeGrouped */ + + +/**Function******************************************************************** + + Synopsis [Sets a variable to be a hard group.] + + Description [Sets a variable to be a hard group. This function is used + for lazy sifting. Returns 1 if successful; 0 otherwise.] + + SideEffects [modifies the manager] + + SeeAlso [Cudd_bddSetVarToBeGrouped Cudd_bddResetVarToBeGrouped + Cudd_bddIsVarHardGroup] + +******************************************************************************/ +int +Cudd_bddSetVarHardGroup( + DdManager *dd, + int index) +{ + if (index >= dd->size || index < 0) return(0); + dd->subtables[dd->perm[index]].varToBeGrouped = CUDD_LAZY_HARD_GROUP; + return(1); + +} /* end of Cudd_bddSetVarHardGrouped */ + + +/**Function******************************************************************** + + Synopsis [Resets a variable not to be grouped.] + + Description [Resets a variable not to be grouped. This function is + used for lazy sifting. Returns 1 if successful; 0 otherwise.] + + SideEffects [modifies the manager] + + SeeAlso [Cudd_bddSetVarToBeGrouped Cudd_bddSetVarHardGroup] + +******************************************************************************/ +int +Cudd_bddResetVarToBeGrouped( + DdManager *dd, + int index) +{ + if (index >= dd->size || index < 0) return(0); + if (dd->subtables[dd->perm[index]].varToBeGrouped <= + CUDD_LAZY_SOFT_GROUP) { + dd->subtables[dd->perm[index]].varToBeGrouped = CUDD_LAZY_NONE; + } + return(1); + +} /* end of Cudd_bddResetVarToBeGrouped */ + + +/**Function******************************************************************** + + Synopsis [Checks whether a variable is set to be grouped.] + + Description [Checks whether a variable is set to be grouped. This + function is used for lazy sifting.] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +int +Cudd_bddIsVarToBeGrouped( + DdManager *dd, + int index) +{ + if (index >= dd->size || index < 0) return(-1); + if (dd->subtables[dd->perm[index]].varToBeGrouped == CUDD_LAZY_UNGROUP) + return(0); + else + return(dd->subtables[dd->perm[index]].varToBeGrouped); + +} /* end of Cudd_bddIsVarToBeGrouped */ + + +/**Function******************************************************************** + + Synopsis [Sets a variable to be ungrouped.] + + Description [Sets a variable to be ungrouped. This function is used + for lazy sifting. Returns 1 if successful; 0 otherwise.] + + SideEffects [modifies the manager] + + SeeAlso [Cudd_bddIsVarToBeUngrouped] + +******************************************************************************/ +int +Cudd_bddSetVarToBeUngrouped( + DdManager *dd, + int index) +{ + if (index >= dd->size || index < 0) return(0); + dd->subtables[dd->perm[index]].varToBeGrouped = CUDD_LAZY_UNGROUP; + return(1); + +} /* end of Cudd_bddSetVarToBeGrouped */ + + +/**Function******************************************************************** + + Synopsis [Checks whether a variable is set to be ungrouped.] + + Description [Checks whether a variable is set to be ungrouped. This + function is used for lazy sifting. Returns 1 if the variable is marked + to be ungrouped; 0 if the variable exists, but it is not marked to be + ungrouped; -1 if the variable does not exist.] + + SideEffects [none] + + SeeAlso [Cudd_bddSetVarToBeUngrouped] + +******************************************************************************/ +int +Cudd_bddIsVarToBeUngrouped( + DdManager *dd, + int index) +{ + if (index >= dd->size || index < 0) return(-1); + return dd->subtables[dd->perm[index]].varToBeGrouped == CUDD_LAZY_UNGROUP; + +} /* end of Cudd_bddIsVarToBeGrouped */ + + +/**Function******************************************************************** + + Synopsis [Checks whether a variable is set to be in a hard group.] + + Description [Checks whether a variable is set to be in a hard group. This + function is used for lazy sifting. Returns 1 if the variable is marked + to be in a hard group; 0 if the variable exists, but it is not marked to be + in a hard group; -1 if the variable does not exist.] + + SideEffects [none] + + SeeAlso [Cudd_bddSetVarHardGroup] + +******************************************************************************/ +int +Cudd_bddIsVarHardGroup( + DdManager *dd, + int index) +{ + if (index >= dd->size || index < 0) return(-1); + if (dd->subtables[dd->perm[index]].varToBeGrouped == CUDD_LAZY_HARD_GROUP) + return(1); + return(0); + +} /* end of Cudd_bddIsVarToBeGrouped */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Fixes a variable group tree.] + + Description [] + + SideEffects [Changes the variable group tree.] + + SeeAlso [] + +******************************************************************************/ +static void +fixVarTree( + MtrNode * treenode, + int * perm, + int size) +{ + treenode->index = treenode->low; + treenode->low = ((int) treenode->index < size) ? + perm[treenode->index] : treenode->index; + if (treenode->child != NULL) + fixVarTree(treenode->child, perm, size); + if (treenode->younger != NULL) + fixVarTree(treenode->younger, perm, size); + return; + +} /* end of fixVarTree */ + + +/**Function******************************************************************** + + Synopsis [Adds multiplicity groups to a ZDD variable group tree.] + + Description [Adds multiplicity groups to a ZDD variable group tree. + Returns 1 if successful; 0 otherwise. This function creates the groups + for set of ZDD variables (whose cardinality is given by parameter + multiplicity) that are created for each BDD variable in + Cudd_zddVarsFromBddVars. The crux of the matter is to determine the index + each new group. (The index of the first variable in the group.) + We first build all the groups for the children of a node, and then deal + with the ZDD variables that are directly attached to the node. The problem + for these is that the tree itself does not provide information on their + position inside the group. While we deal with the children of the node, + therefore, we keep track of all the positions they occupy. The remaining + positions in the tree can be freely used. Also, we keep track of all the + variables placed in the children. All the remaining variables are directly + attached to the group. We can then place any pair of variables not yet + grouped in any pair of available positions in the node.] + + SideEffects [Changes the variable group tree.] + + SeeAlso [Cudd_zddVarsFromBddVars] + +******************************************************************************/ +static int +addMultiplicityGroups( + DdManager *dd /* manager */, + MtrNode *treenode /* current tree node */, + int multiplicity /* how many ZDD vars per BDD var */, + char *vmask /* variable pairs for which a group has been already built */, + char *lmask /* levels for which a group has already been built*/) +{ + int startV, stopV, startL; + int i, j; + MtrNode *auxnode = treenode; + + while (auxnode != NULL) { + if (auxnode->child != NULL) { + addMultiplicityGroups(dd,auxnode->child,multiplicity,vmask,lmask); + } + /* Build remaining groups. */ + startV = dd->permZ[auxnode->index] / multiplicity; + startL = auxnode->low / multiplicity; + stopV = startV + auxnode->size / multiplicity; + /* Walk down vmask starting at startV and build missing groups. */ + for (i = startV, j = startL; i < stopV; i++) { + if (vmask[i] == 0) { + MtrNode *node; + while (lmask[j] == 1) j++; + node = Mtr_MakeGroup(auxnode, j * multiplicity, multiplicity, + MTR_FIXED); + if (node == NULL) { + return(0); + } + node->index = dd->invpermZ[i * multiplicity]; + vmask[i] = 1; + lmask[j] = 1; + } + } + auxnode = auxnode->younger; + } + return(1); + +} /* end of addMultiplicityGroups */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddAddAbs.c b/abc_with_bb_support/src/bdd/cudd/cuddAddAbs.c new file mode 100644 index 000000000..c8d18ca05 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddAddAbs.c @@ -0,0 +1,566 @@ +/**CFile*********************************************************************** + + FileName [cuddAddAbs.c] + + PackageName [cudd] + + Synopsis [Quantification functions for ADDs.] + + Description [External procedures included in this module: +
    +
  • Cudd_addExistAbstract() +
  • Cudd_addUnivAbstract() +
  • Cudd_addOrAbstract() +
+ Internal procedures included in this module: +
    +
  • cuddAddExistAbstractRecur() +
  • cuddAddUnivAbstractRecur() +
  • cuddAddOrAbstractRecur() +
+ Static procedures included in this module: +
    +
  • addCheckPositiveCube() +
] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddAddAbs.c,v 1.1.1.1 2003/02/24 22:23:50 wjiang Exp $"; +#endif + +static DdNode *two; + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int addCheckPositiveCube ARGS((DdManager *manager, DdNode *cube)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Existentially Abstracts all the variables in cube from f.] + + Description [Abstracts all the variables in cube from f by summing + over all possible values taken by the variables. Returns the + abstracted ADD.] + + SideEffects [None] + + SeeAlso [Cudd_addUnivAbstract Cudd_bddExistAbstract + Cudd_addOrAbstract] + +******************************************************************************/ +DdNode * +Cudd_addExistAbstract( + DdManager * manager, + DdNode * f, + DdNode * cube) +{ + DdNode *res; + + two = cuddUniqueConst(manager,(CUDD_VALUE_TYPE) 2); + if (two == NULL) return(NULL); + cuddRef(two); + + if (addCheckPositiveCube(manager, cube) == 0) { + (void) fprintf(manager->err,"Error: Can only abstract cubes"); + return(NULL); + } + + do { + manager->reordered = 0; + res = cuddAddExistAbstractRecur(manager, f, cube); + } while (manager->reordered == 1); + + if (res == NULL) { + Cudd_RecursiveDeref(manager,two); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(manager,two); + cuddDeref(res); + + return(res); + +} /* end of Cudd_addExistAbstract */ + + +/**Function******************************************************************** + + Synopsis [Universally Abstracts all the variables in cube from f.] + + Description [Abstracts all the variables in cube from f by taking + the product over all possible values taken by the variable. Returns + the abstracted ADD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addExistAbstract Cudd_bddUnivAbstract + Cudd_addOrAbstract] + +******************************************************************************/ +DdNode * +Cudd_addUnivAbstract( + DdManager * manager, + DdNode * f, + DdNode * cube) +{ + DdNode *res; + + if (addCheckPositiveCube(manager, cube) == 0) { + (void) fprintf(manager->err,"Error: Can only abstract cubes"); + return(NULL); + } + + do { + manager->reordered = 0; + res = cuddAddUnivAbstractRecur(manager, f, cube); + } while (manager->reordered == 1); + + return(res); + +} /* end of Cudd_addUnivAbstract */ + + +/**Function******************************************************************** + + Synopsis [Disjunctively abstracts all the variables in cube from the + 0-1 ADD f.] + + Description [Abstracts all the variables in cube from the 0-1 ADD f + by taking the disjunction over all possible values taken by the + variables. Returns the abstracted ADD if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addUnivAbstract Cudd_addExistAbstract] + +******************************************************************************/ +DdNode * +Cudd_addOrAbstract( + DdManager * manager, + DdNode * f, + DdNode * cube) +{ + DdNode *res; + + if (addCheckPositiveCube(manager, cube) == 0) { + (void) fprintf(manager->err,"Error: Can only abstract cubes"); + return(NULL); + } + + do { + manager->reordered = 0; + res = cuddAddOrAbstractRecur(manager, f, cube); + } while (manager->reordered == 1); + return(res); + +} /* end of Cudd_addOrAbstract */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_addExistAbstract.] + + Description [Performs the recursive step of Cudd_addExistAbstract. + Returns the ADD obtained by abstracting the variables of cube from f, + if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddAddExistAbstractRecur( + DdManager * manager, + DdNode * f, + DdNode * cube) +{ + DdNode *T, *E, *res, *res1, *res2, *zero; + + statLine(manager); + zero = DD_ZERO(manager); + + /* Cube is guaranteed to be a cube at this point. */ + if (f == zero || cuddIsConstant(cube)) { + return(f); + } + + /* Abstract a variable that does not appear in f => multiply by 2. */ + if (cuddI(manager,f->index) > cuddI(manager,cube->index)) { + res1 = cuddAddExistAbstractRecur(manager, f, cuddT(cube)); + if (res1 == NULL) return(NULL); + cuddRef(res1); + /* Use the "internal" procedure to be alerted in case of + ** dynamic reordering. If dynamic reordering occurs, we + ** have to abort the entire abstraction. + */ + res = cuddAddApplyRecur(manager,Cudd_addTimes,res1,two); + if (res == NULL) { + Cudd_RecursiveDeref(manager,res1); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(manager,res1); + cuddDeref(res); + return(res); + } + + if ((res = cuddCacheLookup2(manager, Cudd_addExistAbstract, f, cube)) != NULL) { + return(res); + } + + T = cuddT(f); + E = cuddE(f); + + /* If the two indices are the same, so are their levels. */ + if (f->index == cube->index) { + res1 = cuddAddExistAbstractRecur(manager, T, cuddT(cube)); + if (res1 == NULL) return(NULL); + cuddRef(res1); + res2 = cuddAddExistAbstractRecur(manager, E, cuddT(cube)); + if (res2 == NULL) { + Cudd_RecursiveDeref(manager,res1); + return(NULL); + } + cuddRef(res2); + res = cuddAddApplyRecur(manager, Cudd_addPlus, res1, res2); + if (res == NULL) { + Cudd_RecursiveDeref(manager,res1); + Cudd_RecursiveDeref(manager,res2); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(manager,res1); + Cudd_RecursiveDeref(manager,res2); + cuddCacheInsert2(manager, Cudd_addExistAbstract, f, cube, res); + cuddDeref(res); + return(res); + } else { /* if (cuddI(manager,f->index) < cuddI(manager,cube->index)) */ + res1 = cuddAddExistAbstractRecur(manager, T, cube); + if (res1 == NULL) return(NULL); + cuddRef(res1); + res2 = cuddAddExistAbstractRecur(manager, E, cube); + if (res2 == NULL) { + Cudd_RecursiveDeref(manager,res1); + return(NULL); + } + cuddRef(res2); + res = (res1 == res2) ? res1 : + cuddUniqueInter(manager, (int) f->index, res1, res2); + if (res == NULL) { + Cudd_RecursiveDeref(manager,res1); + Cudd_RecursiveDeref(manager,res2); + return(NULL); + } + cuddDeref(res1); + cuddDeref(res2); + cuddCacheInsert2(manager, Cudd_addExistAbstract, f, cube, res); + return(res); + } + +} /* end of cuddAddExistAbstractRecur */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_addUnivAbstract.] + + Description [Performs the recursive step of Cudd_addUnivAbstract. + Returns the ADD obtained by abstracting the variables of cube from f, + if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddAddUnivAbstractRecur( + DdManager * manager, + DdNode * f, + DdNode * cube) +{ + DdNode *T, *E, *res, *res1, *res2, *one, *zero; + + statLine(manager); + one = DD_ONE(manager); + zero = DD_ZERO(manager); + + /* Cube is guaranteed to be a cube at this point. + ** zero and one are the only constatnts c such that c*c=c. + */ + if (f == zero || f == one || cube == one) { + return(f); + } + + /* Abstract a variable that does not appear in f. */ + if (cuddI(manager,f->index) > cuddI(manager,cube->index)) { + res1 = cuddAddUnivAbstractRecur(manager, f, cuddT(cube)); + if (res1 == NULL) return(NULL); + cuddRef(res1); + /* Use the "internal" procedure to be alerted in case of + ** dynamic reordering. If dynamic reordering occurs, we + ** have to abort the entire abstraction. + */ + res = cuddAddApplyRecur(manager, Cudd_addTimes, res1, res1); + if (res == NULL) { + Cudd_RecursiveDeref(manager,res1); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(manager,res1); + cuddDeref(res); + return(res); + } + + if ((res = cuddCacheLookup2(manager, Cudd_addUnivAbstract, f, cube)) != NULL) { + return(res); + } + + T = cuddT(f); + E = cuddE(f); + + /* If the two indices are the same, so are their levels. */ + if (f->index == cube->index) { + res1 = cuddAddUnivAbstractRecur(manager, T, cuddT(cube)); + if (res1 == NULL) return(NULL); + cuddRef(res1); + res2 = cuddAddUnivAbstractRecur(manager, E, cuddT(cube)); + if (res2 == NULL) { + Cudd_RecursiveDeref(manager,res1); + return(NULL); + } + cuddRef(res2); + res = cuddAddApplyRecur(manager, Cudd_addTimes, res1, res2); + if (res == NULL) { + Cudd_RecursiveDeref(manager,res1); + Cudd_RecursiveDeref(manager,res2); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(manager,res1); + Cudd_RecursiveDeref(manager,res2); + cuddCacheInsert2(manager, Cudd_addUnivAbstract, f, cube, res); + cuddDeref(res); + return(res); + } else { /* if (cuddI(manager,f->index) < cuddI(manager,cube->index)) */ + res1 = cuddAddUnivAbstractRecur(manager, T, cube); + if (res1 == NULL) return(NULL); + cuddRef(res1); + res2 = cuddAddUnivAbstractRecur(manager, E, cube); + if (res2 == NULL) { + Cudd_RecursiveDeref(manager,res1); + return(NULL); + } + cuddRef(res2); + res = (res1 == res2) ? res1 : + cuddUniqueInter(manager, (int) f->index, res1, res2); + if (res == NULL) { + Cudd_RecursiveDeref(manager,res1); + Cudd_RecursiveDeref(manager,res2); + return(NULL); + } + cuddDeref(res1); + cuddDeref(res2); + cuddCacheInsert2(manager, Cudd_addUnivAbstract, f, cube, res); + return(res); + } + +} /* end of cuddAddUnivAbstractRecur */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_addOrAbstract.] + + Description [Performs the recursive step of Cudd_addOrAbstract. + Returns the ADD obtained by abstracting the variables of cube from f, + if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddAddOrAbstractRecur( + DdManager * manager, + DdNode * f, + DdNode * cube) +{ + DdNode *T, *E, *res, *res1, *res2, *one; + + statLine(manager); + one = DD_ONE(manager); + + /* Cube is guaranteed to be a cube at this point. */ + if (cuddIsConstant(f) || cube == one) { + return(f); + } + + /* Abstract a variable that does not appear in f. */ + if (cuddI(manager,f->index) > cuddI(manager,cube->index)) { + res1 = cuddAddOrAbstractRecur(manager, f, cuddT(cube)); + if (res1 == NULL) return(NULL); + cuddRef(res1); + /* Use the "internal" procedure to be alerted in case of + ** dynamic reordering. If dynamic reordering occurs, we + ** have to abort the entire abstraction. + */ + res = cuddAddApplyRecur(manager, Cudd_addOr, res1, res1); + if (res == NULL) { + Cudd_RecursiveDeref(manager,res1); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(manager,res1); + cuddDeref(res); + return(res); + } + + if ((res = cuddCacheLookup2(manager, Cudd_addOrAbstract, f, cube)) != NULL) { + return(res); + } + + T = cuddT(f); + E = cuddE(f); + + /* If the two indices are the same, so are their levels. */ + if (f->index == cube->index) { + res1 = cuddAddOrAbstractRecur(manager, T, cuddT(cube)); + if (res1 == NULL) return(NULL); + cuddRef(res1); + if (res1 != one) { + res2 = cuddAddOrAbstractRecur(manager, E, cuddT(cube)); + if (res2 == NULL) { + Cudd_RecursiveDeref(manager,res1); + return(NULL); + } + cuddRef(res2); + res = cuddAddApplyRecur(manager, Cudd_addOr, res1, res2); + if (res == NULL) { + Cudd_RecursiveDeref(manager,res1); + Cudd_RecursiveDeref(manager,res2); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(manager,res1); + Cudd_RecursiveDeref(manager,res2); + } else { + res = res1; + } + cuddCacheInsert2(manager, Cudd_addOrAbstract, f, cube, res); + cuddDeref(res); + return(res); + } else { /* if (cuddI(manager,f->index) < cuddI(manager,cube->index)) */ + res1 = cuddAddOrAbstractRecur(manager, T, cube); + if (res1 == NULL) return(NULL); + cuddRef(res1); + res2 = cuddAddOrAbstractRecur(manager, E, cube); + if (res2 == NULL) { + Cudd_RecursiveDeref(manager,res1); + return(NULL); + } + cuddRef(res2); + res = (res1 == res2) ? res1 : + cuddUniqueInter(manager, (int) f->index, res1, res2); + if (res == NULL) { + Cudd_RecursiveDeref(manager,res1); + Cudd_RecursiveDeref(manager,res2); + return(NULL); + } + cuddDeref(res1); + cuddDeref(res2); + cuddCacheInsert2(manager, Cudd_addOrAbstract, f, cube, res); + return(res); + } + +} /* end of cuddAddOrAbstractRecur */ + + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Checks whether cube is an ADD representing the product + of positive literals.] + + Description [Checks whether cube is an ADD representing the product of + positive literals. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +addCheckPositiveCube( + DdManager * manager, + DdNode * cube) +{ + if (Cudd_IsComplement(cube)) return(0); + if (cube == DD_ONE(manager)) return(1); + if (cuddIsConstant(cube)) return(0); + if (cuddE(cube) == DD_ZERO(manager)) { + return(addCheckPositiveCube(manager, cuddT(cube))); + } + return(0); + +} /* end of addCheckPositiveCube */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddAddApply.c b/abc_with_bb_support/src/bdd/cudd/cuddAddApply.c new file mode 100644 index 000000000..f05ce08f1 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddAddApply.c @@ -0,0 +1,917 @@ +/**CFile*********************************************************************** + + FileName [cuddAddApply.c] + + PackageName [cudd] + + Synopsis [Apply functions for ADDs and their operators.] + + Description [External procedures included in this module: +
    +
  • Cudd_addApply() +
  • Cudd_addMonadicApply() +
  • Cudd_addPlus() +
  • Cudd_addTimes() +
  • Cudd_addThreshold() +
  • Cudd_addSetNZ() +
  • Cudd_addDivide() +
  • Cudd_addMinus() +
  • Cudd_addMinimum() +
  • Cudd_addMaximum() +
  • Cudd_addOneZeroMaximum() +
  • Cudd_addDiff() +
  • Cudd_addAgreement() +
  • Cudd_addOr() +
  • Cudd_addNand() +
  • Cudd_addNor() +
  • Cudd_addXor() +
  • Cudd_addXnor() +
+ Internal procedures included in this module: +
    +
  • cuddAddApplyRecur() +
  • cuddAddMonadicApplyRecur() +
] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at Boulder. + The University of Colorado at Boulder makes no warranty about the + suitability of this software for any purpose. It is presented on an + AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddAddApply.c,v 1.1.1.1 2003/02/24 22:23:50 wjiang Exp $"; +#endif + + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Applies op to the corresponding discriminants of f and g.] + + Description [Applies op to the corresponding discriminants of f and g. + Returns a pointer to the result if succssful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addMonadicApply Cudd_addPlus Cudd_addTimes + Cudd_addThreshold Cudd_addSetNZ Cudd_addDivide Cudd_addMinus Cudd_addMinimum + Cudd_addMaximum Cudd_addOneZeroMaximum Cudd_addDiff Cudd_addAgreement + Cudd_addOr Cudd_addNand Cudd_addNor Cudd_addXor Cudd_addXnor] + +******************************************************************************/ +DdNode * +Cudd_addApply( + DdManager * dd, + DdNode * (*op)(DdManager *, DdNode **, DdNode **), + DdNode * f, + DdNode * g) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddAddApplyRecur(dd,op,f,g); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_addApply */ + + +/**Function******************************************************************** + + Synopsis [Integer and floating point addition.] + + Description [Integer and floating point addition. Returns NULL if not + a terminal case; f+g otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addApply] + +******************************************************************************/ +DdNode * +Cudd_addPlus( + DdManager * dd, + DdNode ** f, + DdNode ** g) +{ + DdNode *res; + DdNode *F, *G; + CUDD_VALUE_TYPE value; + + F = *f; G = *g; + if (F == DD_ZERO(dd)) return(G); + if (G == DD_ZERO(dd)) return(F); + if (cuddIsConstant(F) && cuddIsConstant(G)) { + value = cuddV(F)+cuddV(G); + res = cuddUniqueConst(dd,value); + return(res); + } + if (F > G) { /* swap f and g */ + *f = G; + *g = F; + } + return(NULL); + +} /* end of Cudd_addPlus */ + + +/**Function******************************************************************** + + Synopsis [Integer and floating point multiplication.] + + Description [Integer and floating point multiplication. Returns NULL + if not a terminal case; f * g otherwise. This function can be used also + to take the AND of two 0-1 ADDs.] + + SideEffects [None] + + SeeAlso [Cudd_addApply] + +******************************************************************************/ +DdNode * +Cudd_addTimes( + DdManager * dd, + DdNode ** f, + DdNode ** g) +{ + DdNode *res; + DdNode *F, *G; + CUDD_VALUE_TYPE value; + + F = *f; G = *g; + if (F == DD_ZERO(dd) || G == DD_ZERO(dd)) return(DD_ZERO(dd)); + if (F == DD_ONE(dd)) return(G); + if (G == DD_ONE(dd)) return(F); + if (cuddIsConstant(F) && cuddIsConstant(G)) { + value = cuddV(F)*cuddV(G); + res = cuddUniqueConst(dd,value); + return(res); + } + if (F > G) { /* swap f and g */ + *f = G; + *g = F; + } + return(NULL); + +} /* end of Cudd_addTimes */ + + +/**Function******************************************************************** + + Synopsis [f if f>=g; 0 if f<g.] + + Description [Threshold operator for Apply (f if f >=g; 0 if f<g). + Returns NULL if not a terminal case; f op g otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addApply] + +******************************************************************************/ +DdNode * +Cudd_addThreshold( + DdManager * dd, + DdNode ** f, + DdNode ** g) +{ + DdNode *F, *G; + + F = *f; G = *g; + if (F == G || F == DD_PLUS_INFINITY(dd)) return(F); + if (cuddIsConstant(F) && cuddIsConstant(G)) { + if (cuddV(F) >= cuddV(G)) { + return(F); + } else { + return(DD_ZERO(dd)); + } + } + return(NULL); + +} /* end of Cudd_addThreshold */ + + +/**Function******************************************************************** + + Synopsis [This operator sets f to the value of g wherever g != 0.] + + Description [This operator sets f to the value of g wherever g != 0. + Returns NULL if not a terminal case; f op g otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addApply] + +******************************************************************************/ +DdNode * +Cudd_addSetNZ( + DdManager * dd, + DdNode ** f, + DdNode ** g) +{ + DdNode *F, *G; + + F = *f; G = *g; + if (F == G) return(F); + if (F == DD_ZERO(dd)) return(G); + if (G == DD_ZERO(dd)) return(F); + if (cuddIsConstant(G)) return(G); + return(NULL); + +} /* end of Cudd_addSetNZ */ + + +/**Function******************************************************************** + + Synopsis [Integer and floating point division.] + + Description [Integer and floating point division. Returns NULL if not + a terminal case; f / g otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addApply] + +******************************************************************************/ +DdNode * +Cudd_addDivide( + DdManager * dd, + DdNode ** f, + DdNode ** g) +{ + DdNode *res; + DdNode *F, *G; + CUDD_VALUE_TYPE value; + + F = *f; G = *g; + /* We would like to use F == G -> F/G == 1, but F and G may + ** contain zeroes. */ + if (F == DD_ZERO(dd)) return(DD_ZERO(dd)); + if (G == DD_ONE(dd)) return(F); + if (cuddIsConstant(F) && cuddIsConstant(G)) { + value = cuddV(F)/cuddV(G); + res = cuddUniqueConst(dd,value); + return(res); + } + return(NULL); + +} /* end of Cudd_addDivide */ + + +/**Function******************************************************************** + + Synopsis [Integer and floating point subtraction.] + + Description [Integer and floating point subtraction. Returns NULL if + not a terminal case; f - g otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addApply] + +******************************************************************************/ +DdNode * +Cudd_addMinus( + DdManager * dd, + DdNode ** f, + DdNode ** g) +{ + DdNode *res; + DdNode *F, *G; + CUDD_VALUE_TYPE value; + + F = *f; G = *g; + if (F == G) return(DD_ZERO(dd)); + if (F == DD_ZERO(dd)) return(cuddAddNegateRecur(dd,G)); + if (G == DD_ZERO(dd)) return(F); + if (cuddIsConstant(F) && cuddIsConstant(G)) { + value = cuddV(F)-cuddV(G); + res = cuddUniqueConst(dd,value); + return(res); + } + return(NULL); + +} /* end of Cudd_addMinus */ + + +/**Function******************************************************************** + + Synopsis [Integer and floating point min.] + + Description [Integer and floating point min for Cudd_addApply. + Returns NULL if not a terminal case; min(f,g) otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addApply] + +******************************************************************************/ +DdNode * +Cudd_addMinimum( + DdManager * dd, + DdNode ** f, + DdNode ** g) +{ + DdNode *F, *G; + + F = *f; G = *g; + if (F == DD_PLUS_INFINITY(dd)) return(G); + if (G == DD_PLUS_INFINITY(dd)) return(F); + if (F == G) return(F); +#if 0 + /* These special cases probably do not pay off. */ + if (F == DD_MINUS_INFINITY(dd)) return(F); + if (G == DD_MINUS_INFINITY(dd)) return(G); +#endif + if (cuddIsConstant(F) && cuddIsConstant(G)) { + if (cuddV(F) <= cuddV(G)) { + return(F); + } else { + return(G); + } + } + if (F > G) { /* swap f and g */ + *f = G; + *g = F; + } + return(NULL); + +} /* end of Cudd_addMinimum */ + + +/**Function******************************************************************** + + Synopsis [Integer and floating point max.] + + Description [Integer and floating point max for Cudd_addApply. + Returns NULL if not a terminal case; max(f,g) otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addApply] + +******************************************************************************/ +DdNode * +Cudd_addMaximum( + DdManager * dd, + DdNode ** f, + DdNode ** g) +{ + DdNode *F, *G; + + F = *f; G = *g; + if (F == G) return(F); + if (F == DD_MINUS_INFINITY(dd)) return(G); + if (G == DD_MINUS_INFINITY(dd)) return(F); +#if 0 + /* These special cases probably do not pay off. */ + if (F == DD_PLUS_INFINITY(dd)) return(F); + if (G == DD_PLUS_INFINITY(dd)) return(G); +#endif + if (cuddIsConstant(F) && cuddIsConstant(G)) { + if (cuddV(F) >= cuddV(G)) { + return(F); + } else { + return(G); + } + } + if (F > G) { /* swap f and g */ + *f = G; + *g = F; + } + return(NULL); + +} /* end of Cudd_addMaximum */ + + +/**Function******************************************************************** + + Synopsis [Returns 1 if f > g and 0 otherwise.] + + Description [Returns 1 if f > g and 0 otherwise. Used in + conjunction with Cudd_addApply. Returns NULL if not a terminal + case.] + + SideEffects [None] + + SeeAlso [Cudd_addApply] + +******************************************************************************/ +DdNode * +Cudd_addOneZeroMaximum( + DdManager * dd, + DdNode ** f, + DdNode ** g) +{ + + if (*f == *g) return(DD_ZERO(dd)); + if (*g == DD_PLUS_INFINITY(dd)) + return DD_ZERO(dd); + if (cuddIsConstant(*f) && cuddIsConstant(*g)) { + if (cuddV(*f) > cuddV(*g)) { + return(DD_ONE(dd)); + } else { + return(DD_ZERO(dd)); + } + } + + return(NULL); + +} /* end of Cudd_addOneZeroMaximum */ + + +/**Function******************************************************************** + + Synopsis [Returns plusinfinity if f=g; returns min(f,g) if f!=g.] + + Description [Returns NULL if not a terminal case; f op g otherwise, + where f op g is plusinfinity if f=g; min(f,g) if f!=g.] + + SideEffects [None] + + SeeAlso [Cudd_addApply] + +******************************************************************************/ +DdNode * +Cudd_addDiff( + DdManager * dd, + DdNode ** f, + DdNode ** g) +{ + DdNode *F, *G; + + F = *f; G = *g; + if (F == G) return(DD_PLUS_INFINITY(dd)); + if (F == DD_PLUS_INFINITY(dd)) return(G); + if (G == DD_PLUS_INFINITY(dd)) return(F); + if (cuddIsConstant(F) && cuddIsConstant(G)) { + if (cuddV(F) != cuddV(G)) { + if (cuddV(F) < cuddV(G)) { + return(F); + } else { + return(G); + } + } else { + return(DD_PLUS_INFINITY(dd)); + } + } + return(NULL); + +} /* end of Cudd_addDiff */ + + +/**Function******************************************************************** + + Synopsis [f if f==g; background if f!=g.] + + Description [Returns NULL if not a terminal case; f op g otherwise, + where f op g is f if f==g; background if f!=g.] + + SideEffects [None] + + SeeAlso [Cudd_addApply] + +******************************************************************************/ +DdNode * +Cudd_addAgreement( + DdManager * dd, + DdNode ** f, + DdNode ** g) +{ + DdNode *F, *G; + + F = *f; G = *g; + if (F == G) return(F); + if (F == dd->background) return(F); + if (G == dd->background) return(G); + if (cuddIsConstant(F) && cuddIsConstant(G)) return(dd->background); + return(NULL); + +} /* end of Cudd_addAgreement */ + + +/**Function******************************************************************** + + Synopsis [Disjunction of two 0-1 ADDs.] + + Description [Disjunction of two 0-1 ADDs. Returns NULL + if not a terminal case; f OR g otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addApply] + +******************************************************************************/ +DdNode * +Cudd_addOr( + DdManager * dd, + DdNode ** f, + DdNode ** g) +{ + DdNode *F, *G; + + F = *f; G = *g; + if (F == DD_ONE(dd) || G == DD_ONE(dd)) return(DD_ONE(dd)); + if (cuddIsConstant(F)) return(G); + if (cuddIsConstant(G)) return(F); + if (F == G) return(F); + if (F > G) { /* swap f and g */ + *f = G; + *g = F; + } + return(NULL); + +} /* end of Cudd_addOr */ + + +/**Function******************************************************************** + + Synopsis [NAND of two 0-1 ADDs.] + + Description [NAND of two 0-1 ADDs. Returns NULL + if not a terminal case; f NAND g otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addApply] + +******************************************************************************/ +DdNode * +Cudd_addNand( + DdManager * dd, + DdNode ** f, + DdNode ** g) +{ + DdNode *F, *G; + + F = *f; G = *g; + if (F == DD_ZERO(dd) || G == DD_ZERO(dd)) return(DD_ONE(dd)); + if (cuddIsConstant(F) && cuddIsConstant(G)) return(DD_ZERO(dd)); + if (F > G) { /* swap f and g */ + *f = G; + *g = F; + } + return(NULL); + +} /* end of Cudd_addNand */ + + +/**Function******************************************************************** + + Synopsis [NOR of two 0-1 ADDs.] + + Description [NOR of two 0-1 ADDs. Returns NULL + if not a terminal case; f NOR g otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addApply] + +******************************************************************************/ +DdNode * +Cudd_addNor( + DdManager * dd, + DdNode ** f, + DdNode ** g) +{ + DdNode *F, *G; + + F = *f; G = *g; + if (F == DD_ONE(dd) || G == DD_ONE(dd)) return(DD_ZERO(dd)); + if (cuddIsConstant(F) && cuddIsConstant(G)) return(DD_ONE(dd)); + if (F > G) { /* swap f and g */ + *f = G; + *g = F; + } + return(NULL); + +} /* end of Cudd_addNor */ + + +/**Function******************************************************************** + + Synopsis [XOR of two 0-1 ADDs.] + + Description [XOR of two 0-1 ADDs. Returns NULL + if not a terminal case; f XOR g otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addApply] + +******************************************************************************/ +DdNode * +Cudd_addXor( + DdManager * dd, + DdNode ** f, + DdNode ** g) +{ + DdNode *F, *G; + + F = *f; G = *g; + if (F == G) return(DD_ZERO(dd)); + if (F == DD_ONE(dd) && G == DD_ZERO(dd)) return(DD_ONE(dd)); + if (G == DD_ONE(dd) && F == DD_ZERO(dd)) return(DD_ONE(dd)); + if (cuddIsConstant(F) && cuddIsConstant(G)) return(DD_ZERO(dd)); + if (F > G) { /* swap f and g */ + *f = G; + *g = F; + } + return(NULL); + +} /* end of Cudd_addXor */ + + +/**Function******************************************************************** + + Synopsis [XNOR of two 0-1 ADDs.] + + Description [XNOR of two 0-1 ADDs. Returns NULL + if not a terminal case; f XNOR g otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addApply] + +******************************************************************************/ +DdNode * +Cudd_addXnor( + DdManager * dd, + DdNode ** f, + DdNode ** g) +{ + DdNode *F, *G; + + F = *f; G = *g; + if (F == G) return(DD_ONE(dd)); + if (F == DD_ONE(dd) && G == DD_ONE(dd)) return(DD_ONE(dd)); + if (G == DD_ZERO(dd) && F == DD_ZERO(dd)) return(DD_ONE(dd)); + if (cuddIsConstant(F) && cuddIsConstant(G)) return(DD_ZERO(dd)); + if (F > G) { /* swap f and g */ + *f = G; + *g = F; + } + return(NULL); + +} /* end of Cudd_addXnor */ + + +/**Function******************************************************************** + + Synopsis [Applies op to the discriminants of f.] + + Description [Applies op to the discriminants of f. + Returns a pointer to the result if succssful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addApply Cudd_addLog] + +******************************************************************************/ +DdNode * +Cudd_addMonadicApply( + DdManager * dd, + DdNode * (*op)(DdManager *, DdNode *), + DdNode * f) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddAddMonadicApplyRecur(dd,op,f); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_addMonadicApply */ + + +/**Function******************************************************************** + + Synopsis [Natural logarithm of an ADD.] + + Description [Natural logarithm of an ADDs. Returns NULL + if not a terminal case; log(f) otherwise. The discriminants of f must + be positive double's.] + + SideEffects [None] + + SeeAlso [Cudd_addMonadicApply] + +******************************************************************************/ +DdNode * +Cudd_addLog( + DdManager * dd, + DdNode * f) +{ + if (cuddIsConstant(f)) { + CUDD_VALUE_TYPE value = log(cuddV(f)); + DdNode *res = cuddUniqueConst(dd,value); + return(res); + } + return(NULL); + +} /* end of Cudd_addLog */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_addApply.] + + Description [Performs the recursive step of Cudd_addApply. Returns a + pointer to the result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [cuddAddMonadicApplyRecur] + +******************************************************************************/ +DdNode * +cuddAddApplyRecur( + DdManager * dd, + DdNode * (*op)(DdManager *, DdNode **, DdNode **), + DdNode * f, + DdNode * g) +{ + DdNode *res, + *fv, *fvn, *gv, *gvn, + *T, *E; + unsigned int ford, gord; + unsigned int index; + DdNode *(*cacheOp)(DdManager *, DdNode *, DdNode *); + + /* Check terminal cases. Op may swap f and g to increase the + * cache hit rate. + */ + statLine(dd); + res = (*op)(dd,&f,&g); + if (res != NULL) return(res); + + /* Check cache. */ + cacheOp = (DdNode *(*)(DdManager *, DdNode *, DdNode *)) op; + res = cuddCacheLookup2(dd,cacheOp,f,g); + if (res != NULL) return(res); + + /* Recursive step. */ + ford = cuddI(dd,f->index); + gord = cuddI(dd,g->index); + if (ford <= gord) { + index = f->index; + fv = cuddT(f); + fvn = cuddE(f); + } else { + index = g->index; + fv = fvn = f; + } + if (gord <= ford) { + gv = cuddT(g); + gvn = cuddE(g); + } else { + gv = gvn = g; + } + + T = cuddAddApplyRecur(dd,op,fv,gv); + if (T == NULL) return(NULL); + cuddRef(T); + + E = cuddAddApplyRecur(dd,op,fvn,gvn); + if (E == NULL) { + Cudd_RecursiveDeref(dd,T); + return(NULL); + } + cuddRef(E); + + res = (T == E) ? T : cuddUniqueInter(dd,(int)index,T,E); + if (res == NULL) { + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + return(NULL); + } + cuddDeref(T); + cuddDeref(E); + + /* Store result. */ + cuddCacheInsert2(dd,cacheOp,f,g,res); + + return(res); + +} /* end of cuddAddApplyRecur */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_addMonadicApply.] + + Description [Performs the recursive step of Cudd_addMonadicApply. Returns a + pointer to the result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [cuddAddApplyRecur] + +******************************************************************************/ +DdNode * +cuddAddMonadicApplyRecur( + DdManager * dd, + DdNode * (*op)(DdManager *, DdNode *), + DdNode * f) +{ + DdNode *res, *ft, *fe, *T, *E; + unsigned int ford; + unsigned int index; + + /* Check terminal cases. */ + statLine(dd); + res = (*op)(dd,f); + if (res != NULL) return(res); + + /* Check cache. */ + res = cuddCacheLookup1(dd,op,f); + if (res != NULL) return(res); + + /* Recursive step. */ + ford = cuddI(dd,f->index); + index = f->index; + ft = cuddT(f); + fe = cuddE(f); + + T = cuddAddMonadicApplyRecur(dd,op,ft); + if (T == NULL) return(NULL); + cuddRef(T); + + E = cuddAddMonadicApplyRecur(dd,op,fe); + if (E == NULL) { + Cudd_RecursiveDeref(dd,T); + return(NULL); + } + cuddRef(E); + + res = (T == E) ? T : cuddUniqueInter(dd,(int)index,T,E); + if (res == NULL) { + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + return(NULL); + } + cuddDeref(T); + cuddDeref(E); + + /* Store result. */ + cuddCacheInsert1(dd,op,f,res); + + return(res); + +} /* end of cuddAddMonadicApplyRecur */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddAddFind.c b/abc_with_bb_support/src/bdd/cudd/cuddAddFind.c new file mode 100644 index 000000000..4cf81294c --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddAddFind.c @@ -0,0 +1,283 @@ +/**CFile*********************************************************************** + + FileName [cuddAddFind.c] + + PackageName [cudd] + + Synopsis [Functions to find maximum and minimum in an ADD and to + extract the i-th bit.] + + Description [External procedures included in this module: +
    +
  • Cudd_addFindMax() +
  • Cudd_addFindMin() +
  • Cudd_addIthBit() +
+ Static functions included in this module: +
    +
  • addDoIthBit() +
] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddAddFind.c,v 1.1.1.1 2003/02/24 22:23:50 wjiang Exp $"; +#endif + + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static DdNode * addDoIthBit ARGS((DdManager *dd, DdNode *f, DdNode *index)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Finds the maximum discriminant of f.] + + Description [Returns a pointer to a constant ADD.] + + SideEffects [None] + +******************************************************************************/ +DdNode * +Cudd_addFindMax( + DdManager * dd, + DdNode * f) +{ + DdNode *t, *e, *res; + + statLine(dd); + if (cuddIsConstant(f)) { + return(f); + } + + res = cuddCacheLookup1(dd,Cudd_addFindMax,f); + if (res != NULL) { + return(res); + } + + t = Cudd_addFindMax(dd,cuddT(f)); + if (t == DD_PLUS_INFINITY(dd)) return(t); + + e = Cudd_addFindMax(dd,cuddE(f)); + + res = (cuddV(t) >= cuddV(e)) ? t : e; + + cuddCacheInsert1(dd,Cudd_addFindMax,f,res); + + return(res); + +} /* end of Cudd_addFindMax */ + + +/**Function******************************************************************** + + Synopsis [Finds the minimum discriminant of f.] + + Description [Returns a pointer to a constant ADD.] + + SideEffects [None] + +******************************************************************************/ +DdNode * +Cudd_addFindMin( + DdManager * dd, + DdNode * f) +{ + DdNode *t, *e, *res; + + statLine(dd); + if (cuddIsConstant(f)) { + return(f); + } + + res = cuddCacheLookup1(dd,Cudd_addFindMin,f); + if (res != NULL) { + return(res); + } + + t = Cudd_addFindMin(dd,cuddT(f)); + if (t == DD_MINUS_INFINITY(dd)) return(t); + + e = Cudd_addFindMin(dd,cuddE(f)); + + res = (cuddV(t) <= cuddV(e)) ? t : e; + + cuddCacheInsert1(dd,Cudd_addFindMin,f,res); + + return(res); + +} /* end of Cudd_addFindMin */ + + +/**Function******************************************************************** + + Synopsis [Extracts the i-th bit from an ADD.] + + Description [Produces an ADD from another ADD by replacing all + discriminants whose i-th bit is equal to 1 with 1, and all other + discriminants with 0. The i-th bit refers to the integer + representation of the leaf value. If the value is has a fractional + part, it is ignored. Repeated calls to this procedure allow one to + transform an integer-valued ADD into an array of ADDs, one for each + bit of the leaf values. Returns a pointer to the resulting ADD if + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addBddIthBit] + +******************************************************************************/ +DdNode * +Cudd_addIthBit( + DdManager * dd, + DdNode * f, + int bit) +{ + DdNode *res; + DdNode *index; + + /* Use a constant node to remember the bit, so that we can use the + ** global cache. + */ + index = cuddUniqueConst(dd,(CUDD_VALUE_TYPE) bit); + if (index == NULL) return(NULL); + cuddRef(index); + + do { + dd->reordered = 0; + res = addDoIthBit(dd, f, index); + } while (dd->reordered == 1); + + if (res == NULL) { + Cudd_RecursiveDeref(dd, index); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(dd, index); + cuddDeref(res); + return(res); + +} /* end of Cudd_addIthBit */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step for Cudd_addIthBit.] + + Description [Performs the recursive step for Cudd_addIthBit. + Returns a pointer to the BDD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static DdNode * +addDoIthBit( + DdManager * dd, + DdNode * f, + DdNode * index) +{ + DdNode *res, *T, *E; + DdNode *fv, *fvn; + int mask, value; + int v; + + statLine(dd); + /* Check terminal case. */ + if (cuddIsConstant(f)) { + mask = 1 << ((int) cuddV(index)); + value = (int) cuddV(f); + return((value & mask) == 0 ? DD_ZERO(dd) : DD_ONE(dd)); + } + + /* Check cache. */ + res = cuddCacheLookup2(dd,addDoIthBit,f,index); + if (res != NULL) return(res); + + /* Recursive step. */ + v = f->index; + fv = cuddT(f); fvn = cuddE(f); + + T = addDoIthBit(dd,fv,index); + if (T == NULL) return(NULL); + cuddRef(T); + + E = addDoIthBit(dd,fvn,index); + if (E == NULL) { + Cudd_RecursiveDeref(dd, T); + return(NULL); + } + cuddRef(E); + + res = (T == E) ? T : cuddUniqueInter(dd,v,T,E); + if (res == NULL) { + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + return(NULL); + } + cuddDeref(T); + cuddDeref(E); + + /* Store result. */ + cuddCacheInsert2(dd,addDoIthBit,f,index,res); + + return(res); + +} /* end of addDoIthBit */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddAddInv.c b/abc_with_bb_support/src/bdd/cudd/cuddAddInv.c new file mode 100644 index 000000000..fc9bccaca --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddAddInv.c @@ -0,0 +1,172 @@ +/**CFile*********************************************************************** + + FileName [cuddAddInv.c] + + PackageName [cudd] + + Synopsis [Function to compute the scalar inverse of an ADD.] + + Description [External procedures included in this module: +
    +
  • Cudd_addScalarInverse() +
+ Internal procedures included in this module: +
    +
  • cuddAddScalarInverseRecur() +
] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddAddInv.c,v 1.1.1.1 2003/02/24 22:23:50 wjiang Exp $"; +#endif + + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Computes the scalar inverse of an ADD.] + + Description [Computes an n ADD where the discriminants are the + multiplicative inverses of the corresponding discriminants of the + argument ADD. Returns a pointer to the resulting ADD in case of + success. Returns NULL if any discriminants smaller than epsilon is + encountered.] + + SideEffects [None] + +******************************************************************************/ +DdNode * +Cudd_addScalarInverse( + DdManager * dd, + DdNode * f, + DdNode * epsilon) +{ + DdNode *res; + + if (!cuddIsConstant(epsilon)) { + (void) fprintf(dd->err,"Invalid epsilon\n"); + return(NULL); + } + do { + dd->reordered = 0; + res = cuddAddScalarInverseRecur(dd,f,epsilon); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_addScalarInverse */ + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of addScalarInverse.] + + Description [Returns a pointer to the resulting ADD in case of + success. Returns NULL if any discriminants smaller than epsilon is + encountered.] + + SideEffects [None] + +******************************************************************************/ +DdNode * +cuddAddScalarInverseRecur( + DdManager * dd, + DdNode * f, + DdNode * epsilon) +{ + DdNode *t, *e, *res; + CUDD_VALUE_TYPE value; + + statLine(dd); + if (cuddIsConstant(f)) { + if (ddAbs(cuddV(f)) < cuddV(epsilon)) return(NULL); + value = 1.0 / cuddV(f); + res = cuddUniqueConst(dd,value); + return(res); + } + + res = cuddCacheLookup2(dd,Cudd_addScalarInverse,f,epsilon); + if (res != NULL) return(res); + + t = cuddAddScalarInverseRecur(dd,cuddT(f),epsilon); + if (t == NULL) return(NULL); + cuddRef(t); + + e = cuddAddScalarInverseRecur(dd,cuddE(f),epsilon); + if (e == NULL) { + Cudd_RecursiveDeref(dd, t); + return(NULL); + } + cuddRef(e); + + res = (t == e) ? t : cuddUniqueInter(dd,(int)f->index,t,e); + if (res == NULL) { + Cudd_RecursiveDeref(dd, t); + Cudd_RecursiveDeref(dd, e); + return(NULL); + } + + cuddCacheInsert2(dd,Cudd_addScalarInverse,f,epsilon,res); + + return(res); + +} /* end of cuddAddScalarInverseRecur */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddAddIte.c b/abc_with_bb_support/src/bdd/cudd/cuddAddIte.c new file mode 100644 index 000000000..ddd95e125 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddAddIte.c @@ -0,0 +1,613 @@ +/**CFile*********************************************************************** + + FileName [cuddAddIte.c] + + PackageName [cudd] + + Synopsis [ADD ITE function and satellites.] + + Description [External procedures included in this module: +
    +
  • Cudd_addIte() +
  • Cudd_addIteConstant() +
  • Cudd_addEvalConst() +
  • Cudd_addCmpl() +
  • Cudd_addLeq() +
+ Internal procedures included in this module: +
    +
  • cuddAddIteRecur() +
  • cuddAddCmplRecur() +
+ Static procedures included in this module: +
    +
  • addVarToConst() +
] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddAddIte.c,v 1.1.1.1 2003/02/24 22:23:50 wjiang Exp $"; +#endif + + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static void addVarToConst ARGS((DdNode *f, DdNode **gp, DdNode **hp, DdNode *one, DdNode *zero)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Implements ITE(f,g,h).] + + Description [Implements ITE(f,g,h). This procedure assumes that f is + a 0-1 ADD. Returns a pointer to the resulting ADD if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddIte Cudd_addIteConstant Cudd_addApply] + +******************************************************************************/ +DdNode * +Cudd_addIte( + DdManager * dd, + DdNode * f, + DdNode * g, + DdNode * h) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddAddIteRecur(dd,f,g,h); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_addIte */ + + +/**Function******************************************************************** + + Synopsis [Implements ITEconstant for ADDs.] + + Description [Implements ITEconstant for ADDs. f must be a 0-1 ADD. + Returns a pointer to the resulting ADD (which may or may not be + constant) or DD_NON_CONSTANT. No new nodes are created. This function + can be used, for instance, to check that g has a constant value + (specified by h) whenever f is 1. If the constant value is unknown, + then one should use Cudd_addEvalConst.] + + SideEffects [None] + + SeeAlso [Cudd_addIte Cudd_addEvalConst Cudd_bddIteConstant] + +******************************************************************************/ +DdNode * +Cudd_addIteConstant( + DdManager * dd, + DdNode * f, + DdNode * g, + DdNode * h) +{ + DdNode *one,*zero; + DdNode *Fv,*Fnv,*Gv,*Gnv,*Hv,*Hnv,*r,*t,*e; + unsigned int topf,topg,toph,v; + + statLine(dd); + /* Trivial cases. */ + if (f == (one = DD_ONE(dd))) { /* ITE(1,G,H) = G */ + return(g); + } + if (f == (zero = DD_ZERO(dd))) { /* ITE(0,G,H) = H */ + return(h); + } + + /* From now on, f is known not to be a constant. */ + addVarToConst(f,&g,&h,one,zero); + + /* Check remaining one variable cases. */ + if (g == h) { /* ITE(F,G,G) = G */ + return(g); + } + if (cuddIsConstant(g) && cuddIsConstant(h)) { + return(DD_NON_CONSTANT); + } + + topf = cuddI(dd,f->index); + topg = cuddI(dd,g->index); + toph = cuddI(dd,h->index); + v = ddMin(topg,toph); + + /* ITE(F,G,H) = (x,G,H) (non constant) if F = (x,1,0), x < top(G,H). */ + if (topf < v && cuddIsConstant(cuddT(f)) && cuddIsConstant(cuddE(f))) { + return(DD_NON_CONSTANT); + } + + /* Check cache. */ + r = cuddConstantLookup(dd,DD_ADD_ITE_CONSTANT_TAG,f,g,h); + if (r != NULL) { + return(r); + } + + /* Compute cofactors. */ + if (topf <= v) { + v = ddMin(topf,v); /* v = top_var(F,G,H) */ + Fv = cuddT(f); Fnv = cuddE(f); + } else { + Fv = Fnv = f; + } + if (topg == v) { + Gv = cuddT(g); Gnv = cuddE(g); + } else { + Gv = Gnv = g; + } + if (toph == v) { + Hv = cuddT(h); Hnv = cuddE(h); + } else { + Hv = Hnv = h; + } + + /* Recursive step. */ + t = Cudd_addIteConstant(dd,Fv,Gv,Hv); + if (t == DD_NON_CONSTANT || !cuddIsConstant(t)) { + cuddCacheInsert(dd, DD_ADD_ITE_CONSTANT_TAG, f, g, h, DD_NON_CONSTANT); + return(DD_NON_CONSTANT); + } + e = Cudd_addIteConstant(dd,Fnv,Gnv,Hnv); + if (e == DD_NON_CONSTANT || !cuddIsConstant(e) || t != e) { + cuddCacheInsert(dd, DD_ADD_ITE_CONSTANT_TAG, f, g, h, DD_NON_CONSTANT); + return(DD_NON_CONSTANT); + } + cuddCacheInsert(dd, DD_ADD_ITE_CONSTANT_TAG, f, g, h, t); + return(t); + +} /* end of Cudd_addIteConstant */ + + +/**Function******************************************************************** + + Synopsis [Checks whether ADD g is constant whenever ADD f is 1.] + + Description [Checks whether ADD g is constant whenever ADD f is 1. f + must be a 0-1 ADD. Returns a pointer to the resulting ADD (which may + or may not be constant) or DD_NON_CONSTANT. If f is identically 0, + the check is assumed to be successful, and the background value is + returned. No new nodes are created.] + + SideEffects [None] + + SeeAlso [Cudd_addIteConstant Cudd_addLeq] + +******************************************************************************/ +DdNode * +Cudd_addEvalConst( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *zero; + DdNode *Fv,*Fnv,*Gv,*Gnv,*r,*t,*e; + unsigned int topf,topg; + +#ifdef DD_DEBUG + assert(!Cudd_IsComplement(f)); +#endif + + statLine(dd); + /* Terminal cases. */ + if (f == DD_ONE(dd) || cuddIsConstant(g)) { + return(g); + } + if (f == (zero = DD_ZERO(dd))) { + return(dd->background); + } + +#ifdef DD_DEBUG + assert(!cuddIsConstant(f)); +#endif + /* From now on, f and g are known not to be constants. */ + + topf = cuddI(dd,f->index); + topg = cuddI(dd,g->index); + + /* Check cache. */ + r = cuddConstantLookup(dd,DD_ADD_EVAL_CONST_TAG,f,g,g); + if (r != NULL) { + return(r); + } + + /* Compute cofactors. */ + if (topf <= topg) { + Fv = cuddT(f); Fnv = cuddE(f); + } else { + Fv = Fnv = f; + } + if (topg <= topf) { + Gv = cuddT(g); Gnv = cuddE(g); + } else { + Gv = Gnv = g; + } + + /* Recursive step. */ + if (Fv != zero) { + t = Cudd_addEvalConst(dd,Fv,Gv); + if (t == DD_NON_CONSTANT || !cuddIsConstant(t)) { + cuddCacheInsert2(dd, Cudd_addEvalConst, f, g, DD_NON_CONSTANT); + return(DD_NON_CONSTANT); + } + if (Fnv != zero) { + e = Cudd_addEvalConst(dd,Fnv,Gnv); + if (e == DD_NON_CONSTANT || !cuddIsConstant(e) || t != e) { + cuddCacheInsert2(dd, Cudd_addEvalConst, f, g, DD_NON_CONSTANT); + return(DD_NON_CONSTANT); + } + } + cuddCacheInsert2(dd,Cudd_addEvalConst,f,g,t); + return(t); + } else { /* Fnv must be != zero */ + e = Cudd_addEvalConst(dd,Fnv,Gnv); + cuddCacheInsert2(dd, Cudd_addEvalConst, f, g, e); + return(e); + } + +} /* end of Cudd_addEvalConst */ + + +/**Function******************************************************************** + + Synopsis [Computes the complement of an ADD a la C language.] + + Description [Computes the complement of an ADD a la C language: The + complement of 0 is 1 and the complement of everything else is 0. + Returns a pointer to the resulting ADD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addNegate] + +******************************************************************************/ +DdNode * +Cudd_addCmpl( + DdManager * dd, + DdNode * f) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddAddCmplRecur(dd,f); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_addCmpl */ + + +/**Function******************************************************************** + + Synopsis [Determines whether f is less than or equal to g.] + + Description [Returns 1 if f is less than or equal to g; 0 otherwise. + No new nodes are created. This procedure works for arbitrary ADDs. + For 0-1 ADDs Cudd_addEvalConst is more efficient.] + + SideEffects [None] + + SeeAlso [Cudd_addIteConstant Cudd_addEvalConst Cudd_bddLeq] + +******************************************************************************/ +int +Cudd_addLeq( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *tmp, *fv, *fvn, *gv, *gvn; + unsigned int topf, topg, res; + + /* Terminal cases. */ + if (f == g) return(1); + + statLine(dd); + if (cuddIsConstant(f)) { + if (cuddIsConstant(g)) return(cuddV(f) <= cuddV(g)); + if (f == DD_MINUS_INFINITY(dd)) return(1); + if (f == DD_PLUS_INFINITY(dd)) return(0); /* since f != g */ + } + if (g == DD_PLUS_INFINITY(dd)) return(1); + if (g == DD_MINUS_INFINITY(dd)) return(0); /* since f != g */ + + /* Check cache. */ + tmp = cuddCacheLookup2(dd,(DdNode * (*)(DdManager *, DdNode *, + DdNode *))Cudd_addLeq,f,g); + if (tmp != NULL) { + return(tmp == DD_ONE(dd)); + } + + /* Compute cofactors. One of f and g is not constant. */ + topf = cuddI(dd,f->index); + topg = cuddI(dd,g->index); + if (topf <= topg) { + fv = cuddT(f); fvn = cuddE(f); + } else { + fv = fvn = f; + } + if (topg <= topf) { + gv = cuddT(g); gvn = cuddE(g); + } else { + gv = gvn = g; + } + + res = Cudd_addLeq(dd,fvn,gvn) && Cudd_addLeq(dd,fv,gv); + + /* Store result in cache and return. */ + cuddCacheInsert2(dd,(DdNode * (*)(DdManager *, DdNode *, DdNode *)) + Cudd_addLeq,f,g,Cudd_NotCond(DD_ONE(dd),res==0)); + return(res); + +} /* end of Cudd_addLeq */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_addIte(f,g,h).] + + Description [Implements the recursive step of Cudd_addIte(f,g,h). + Returns a pointer to the resulting ADD if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addIte] + +******************************************************************************/ +DdNode * +cuddAddIteRecur( + DdManager * dd, + DdNode * f, + DdNode * g, + DdNode * h) +{ + DdNode *one,*zero; + DdNode *r,*Fv,*Fnv,*Gv,*Gnv,*Hv,*Hnv,*t,*e; + unsigned int topf,topg,toph,v; + int index; + + statLine(dd); + /* Trivial cases. */ + + /* One variable cases. */ + if (f == (one = DD_ONE(dd))) { /* ITE(1,G,H) = G */ + return(g); + } + if (f == (zero = DD_ZERO(dd))) { /* ITE(0,G,H) = H */ + return(h); + } + + /* From now on, f is known to not be a constant. */ + addVarToConst(f,&g,&h,one,zero); + + /* Check remaining one variable cases. */ + if (g == h) { /* ITE(F,G,G) = G */ + return(g); + } + + if (g == one) { /* ITE(F,1,0) = F */ + if (h == zero) return(f); + } + + topf = cuddI(dd,f->index); + topg = cuddI(dd,g->index); + toph = cuddI(dd,h->index); + v = ddMin(topg,toph); + + /* A shortcut: ITE(F,G,H) = (x,G,H) if F=(x,1,0), x < top(G,H). */ + if (topf < v && cuddT(f) == one && cuddE(f) == zero) { + r = cuddUniqueInter(dd,(int)f->index,g,h); + return(r); + } + if (topf < v && cuddT(f) == zero && cuddE(f) == one) { + r = cuddUniqueInter(dd,(int)f->index,h,g); + return(r); + } + + /* Check cache. */ + r = cuddCacheLookup(dd,DD_ADD_ITE_TAG,f,g,h); + if (r != NULL) { + return(r); + } + + /* Compute cofactors. */ + if (topf <= v) { + v = ddMin(topf,v); /* v = top_var(F,G,H) */ + index = f->index; + Fv = cuddT(f); Fnv = cuddE(f); + } else { + Fv = Fnv = f; + } + if (topg == v) { + index = g->index; + Gv = cuddT(g); Gnv = cuddE(g); + } else { + Gv = Gnv = g; + } + if (toph == v) { + index = h->index; + Hv = cuddT(h); Hnv = cuddE(h); + } else { + Hv = Hnv = h; + } + + /* Recursive step. */ + t = cuddAddIteRecur(dd,Fv,Gv,Hv); + if (t == NULL) return(NULL); + cuddRef(t); + + e = cuddAddIteRecur(dd,Fnv,Gnv,Hnv); + if (e == NULL) { + Cudd_RecursiveDeref(dd,t); + return(NULL); + } + cuddRef(e); + + r = (t == e) ? t : cuddUniqueInter(dd,index,t,e); + if (r == NULL) { + Cudd_RecursiveDeref(dd,t); + Cudd_RecursiveDeref(dd,e); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + + cuddCacheInsert(dd,DD_ADD_ITE_TAG,f,g,h,r); + + return(r); + +} /* end of cuddAddIteRecur */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_addCmpl.] + + Description [Performs the recursive step of Cudd_addCmpl. Returns a + pointer to the resulting ADD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addCmpl] + +******************************************************************************/ +DdNode * +cuddAddCmplRecur( + DdManager * dd, + DdNode * f) +{ + DdNode *one,*zero; + DdNode *r,*Fv,*Fnv,*t,*e; + + statLine(dd); + one = DD_ONE(dd); + zero = DD_ZERO(dd); + + if (cuddIsConstant(f)) { + if (f == zero) { + return(one); + } else { + return(zero); + } + } + r = cuddCacheLookup1(dd,Cudd_addCmpl,f); + if (r != NULL) { + return(r); + } + Fv = cuddT(f); + Fnv = cuddE(f); + t = cuddAddCmplRecur(dd,Fv); + if (t == NULL) return(NULL); + cuddRef(t); + e = cuddAddCmplRecur(dd,Fnv); + if (e == NULL) { + Cudd_RecursiveDeref(dd,t); + return(NULL); + } + cuddRef(e); + r = (t == e) ? t : cuddUniqueInter(dd,(int)f->index,t,e); + if (r == NULL) { + Cudd_RecursiveDeref(dd, t); + Cudd_RecursiveDeref(dd, e); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + cuddCacheInsert1(dd,Cudd_addCmpl,f,r); + return(r); + +} /* end of cuddAddCmplRecur */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Replaces variables with constants if possible (part of + canonical form).] + + Description [] + + SideEffects [None] + +******************************************************************************/ +static void +addVarToConst( + DdNode * f, + DdNode ** gp, + DdNode ** hp, + DdNode * one, + DdNode * zero) +{ + DdNode *g = *gp; + DdNode *h = *hp; + + if (f == g) { /* ITE(F,F,H) = ITE(F,1,H) = F + H */ + *gp = one; + } + + if (f == h) { /* ITE(F,G,F) = ITE(F,G,0) = F * G */ + *hp = zero; + } + +} /* end of addVarToConst */ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddAddNeg.c b/abc_with_bb_support/src/bdd/cudd/cuddAddNeg.c new file mode 100644 index 000000000..b7ff676fd --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddAddNeg.c @@ -0,0 +1,262 @@ +/**CFile*********************************************************************** + + FileName [cuddAddNeg.c] + + PackageName [cudd] + + Synopsis [function to compute the negation of an ADD.] + + Description [External procedures included in this module: +
    +
  • Cudd_addNegate() +
  • Cudd_addRoundOff() +
+ Internal procedures included in this module: +
    +
  • cuddAddNegateRecur() +
  • cuddAddRoundOffRecur() +
] + + Author [Fabio Somenzi, Balakrishna Kumthekar] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddAddNeg.c,v 1.1.1.1 2003/02/24 22:23:50 wjiang Exp $"; +#endif + + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Computes the additive inverse of an ADD.] + + Description [Computes the additive inverse of an ADD. Returns a pointer + to the result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addCmpl] + +******************************************************************************/ +DdNode * +Cudd_addNegate( + DdManager * dd, + DdNode * f) +{ + DdNode *res; + + do { + res = cuddAddNegateRecur(dd,f); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_addNegate */ + + +/**Function******************************************************************** + + Synopsis [Rounds off the discriminants of an ADD.] + + Description [Rounds off the discriminants of an ADD. The discriminants are + rounded off to N digits after the decimal. Returns a pointer to the result + ADD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +Cudd_addRoundOff( + DdManager * dd, + DdNode * f, + int N) +{ + DdNode *res; + double trunc = pow(10.0,(double)N); + + do { + res = cuddAddRoundOffRecur(dd,f,trunc); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_addRoundOff */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_addNegate.] + + Description [Implements the recursive step of Cudd_addNegate. + Returns a pointer to the result.] + + SideEffects [None] + +******************************************************************************/ +DdNode * +cuddAddNegateRecur( + DdManager * dd, + DdNode * f) +{ + DdNode *res, + *fv, *fvn, + *T, *E; + + statLine(dd); + /* Check terminal cases. */ + if (cuddIsConstant(f)) { + res = cuddUniqueConst(dd,-cuddV(f)); + return(res); + } + + /* Check cache */ + res = cuddCacheLookup1(dd,Cudd_addNegate,f); + if (res != NULL) return(res); + + /* Recursive Step */ + fv = cuddT(f); + fvn = cuddE(f); + T = cuddAddNegateRecur(dd,fv); + if (T == NULL) return(NULL); + cuddRef(T); + + E = cuddAddNegateRecur(dd,fvn); + if (E == NULL) { + Cudd_RecursiveDeref(dd,T); + return(NULL); + } + cuddRef(E); + res = (T == E) ? T : cuddUniqueInter(dd,(int)f->index,T,E); + if (res == NULL) { + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + return(NULL); + } + cuddDeref(T); + cuddDeref(E); + + /* Store result. */ + cuddCacheInsert1(dd,Cudd_addNegate,f,res); + + return(res); + +} /* end of cuddAddNegateRecur */ + + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_addRoundOff.] + + Description [Implements the recursive step of Cudd_addRoundOff. + Returns a pointer to the result.] + + SideEffects [None] + +******************************************************************************/ +DdNode * +cuddAddRoundOffRecur( + DdManager * dd, + DdNode * f, + double trunc) +{ + + DdNode *res, *fv, *fvn, *T, *E; + double n; + DdNode *(*cacheOp)(DdManager *, DdNode *); + + statLine(dd); + if (cuddIsConstant(f)) { + n = ceil(cuddV(f)*trunc)/trunc; + res = cuddUniqueConst(dd,n); + return(res); + } + cacheOp = (DdNode *(*)(DdManager *, DdNode *)) Cudd_addRoundOff; + res = cuddCacheLookup1(dd,cacheOp,f); + if (res != NULL) { + return(res); + } + /* Recursive Step */ + fv = cuddT(f); + fvn = cuddE(f); + T = cuddAddRoundOffRecur(dd,fv,trunc); + if (T == NULL) { + return(NULL); + } + cuddRef(T); + E = cuddAddRoundOffRecur(dd,fvn,trunc); + if (E == NULL) { + Cudd_RecursiveDeref(dd,T); + return(NULL); + } + cuddRef(E); + res = (T == E) ? T : cuddUniqueInter(dd,(int)f->index,T,E); + if (res == NULL) { + Cudd_RecursiveDeref(dd,T); + Cudd_RecursiveDeref(dd,E); + return(NULL); + } + cuddDeref(T); + cuddDeref(E); + + /* Store result. */ + cuddCacheInsert1(dd,cacheOp,f,res); + return(res); + +} /* end of cuddAddRoundOffRecur */ + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddAddWalsh.c b/abc_with_bb_support/src/bdd/cudd/cuddAddWalsh.c new file mode 100644 index 000000000..f30d329b3 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddAddWalsh.c @@ -0,0 +1,364 @@ +/**CFile*********************************************************************** + + FileName [cuddAddWalsh.c] + + PackageName [cudd] + + Synopsis [Functions that generate Walsh matrices and residue + functions in ADD form.] + + Description [External procedures included in this module: +
    +
  • Cudd_addWalsh() +
  • Cudd_addResidue() +
+ Static procedures included in this module: +
    +
  • addWalshInt() +
] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddAddWalsh.c,v 1.1.1.1 2003/02/24 22:23:50 wjiang Exp $"; +#endif + + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static DdNode * addWalshInt ARGS((DdManager *dd, DdNode **x, DdNode **y, int n)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Generates a Walsh matrix in ADD form.] + + Description [Generates a Walsh matrix in ADD form. Returns a pointer + to the matrixi if successful; NULL otherwise.] + + SideEffects [None] + +******************************************************************************/ +DdNode * +Cudd_addWalsh( + DdManager * dd, + DdNode ** x, + DdNode ** y, + int n) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = addWalshInt(dd, x, y, n); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_addWalsh */ + + +/**Function******************************************************************** + + Synopsis [Builds an ADD for the residue modulo m of an n-bit + number.] + + Description [Builds an ADD for the residue modulo m of an n-bit + number. The modulus must be at least 2, and the number of bits at + least 1. Parameter options specifies whether the MSB should be on top + or the LSB; and whther the number whose residue is computed is in + two's complement notation or not. The macro CUDD_RESIDUE_DEFAULT + specifies LSB on top and unsigned number. The macro CUDD_RESIDUE_MSB + specifies MSB on top, and the macro CUDD_RESIDUE_TC specifies two's + complement residue. To request MSB on top and two's complement residue + simultaneously, one can OR the two macros: + CUDD_RESIDUE_MSB | CUDD_RESIDUE_TC. + Cudd_addResidue returns a pointer to the resulting ADD if successful; + NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +Cudd_addResidue( + DdManager * dd /* manager */, + int n /* number of bits */, + int m /* modulus */, + int options /* options */, + int top /* index of top variable */) +{ + int msbLsb; /* MSB on top (1) or LSB on top (0) */ + int tc; /* two's complement (1) or unsigned (0) */ + int i, j, k, t, residue, thisOne, previous, index; + DdNode **array[2], *var, *tmp, *res; + + /* Sanity check. */ + if (n < 1 && m < 2) return(NULL); + + msbLsb = options & CUDD_RESIDUE_MSB; + tc = options & CUDD_RESIDUE_TC; + + /* Allocate and initialize working arrays. */ + array[0] = ALLOC(DdNode *,m); + if (array[0] == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + array[1] = ALLOC(DdNode *,m); + if (array[1] == NULL) { + FREE(array[0]); + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + for (i = 0; i < m; i++) { + array[0][i] = array[1][i] = NULL; + } + + /* Initialize residues. */ + for (i = 0; i < m; i++) { + tmp = cuddUniqueConst(dd,(CUDD_VALUE_TYPE) i); + if (tmp == NULL) { + for (j = 0; j < i; j++) { + Cudd_RecursiveDeref(dd,array[1][j]); + } + FREE(array[0]); + FREE(array[1]); + return(NULL); + } + cuddRef(tmp); + array[1][i] = tmp; + } + + /* Main iteration. */ + residue = 1; /* residue of 2**0 */ + for (k = 0; k < n; k++) { + /* Choose current and previous arrays. */ + thisOne = k & 1; + previous = thisOne ^ 1; + /* Build an ADD projection function. */ + if (msbLsb) { + index = top+n-k-1; + } else { + index = top+k; + } + var = cuddUniqueInter(dd,index,DD_ONE(dd),DD_ZERO(dd)); + if (var == NULL) { + for (j = 0; j < m; j++) { + Cudd_RecursiveDeref(dd,array[previous][j]); + } + FREE(array[0]); + FREE(array[1]); + return(NULL); + } + cuddRef(var); + for (i = 0; i < m; i ++) { + t = (i + residue) % m; + tmp = Cudd_addIte(dd,var,array[previous][t],array[previous][i]); + if (tmp == NULL) { + for (j = 0; j < i; j++) { + Cudd_RecursiveDeref(dd,array[thisOne][j]); + } + for (j = 0; j < m; j++) { + Cudd_RecursiveDeref(dd,array[previous][j]); + } + FREE(array[0]); + FREE(array[1]); + return(NULL); + } + cuddRef(tmp); + array[thisOne][i] = tmp; + } + /* One layer completed. Free the other array for the next iteration. */ + for (i = 0; i < m; i++) { + Cudd_RecursiveDeref(dd,array[previous][i]); + } + Cudd_RecursiveDeref(dd,var); + /* Update residue of 2**k. */ + residue = (2 * residue) % m; + /* Adjust residue for MSB, if this is a two's complement number. */ + if (tc && (k == n - 1)) { + residue = (m - residue) % m; + } + } + + /* We are only interested in the 0-residue node of the top layer. */ + for (i = 1; i < m; i++) { + Cudd_RecursiveDeref(dd,array[(n - 1) & 1][i]); + } + res = array[(n - 1) & 1][0]; + + FREE(array[0]); + FREE(array[1]); + + cuddDeref(res); + return(res); + +} /* end of Cudd_addResidue */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_addWalsh.] + + Description [Generates a Walsh matrix in ADD form. Returns a pointer + to the matrixi if successful; NULL otherwise.] + + SideEffects [None] + +******************************************************************************/ +static DdNode * +addWalshInt( + DdManager * dd, + DdNode ** x, + DdNode ** y, + int n) +{ + DdNode *one, *minusone; + DdNode *t, *u, *t1, *u1, *v, *w; + int i; + + one = DD_ONE(dd); + if (n == 0) return(one); + + /* Build bottom part of ADD outside loop */ + minusone = cuddUniqueConst(dd,(CUDD_VALUE_TYPE) -1); + if (minusone == NULL) return(NULL); + cuddRef(minusone); + v = Cudd_addIte(dd, y[n-1], minusone, one); + if (v == NULL) { + Cudd_RecursiveDeref(dd, minusone); + return(NULL); + } + cuddRef(v); + u = Cudd_addIte(dd, x[n-1], v, one); + if (u == NULL) { + Cudd_RecursiveDeref(dd, minusone); + Cudd_RecursiveDeref(dd, v); + return(NULL); + } + cuddRef(u); + Cudd_RecursiveDeref(dd, v); + if (n>1) { + w = Cudd_addIte(dd, y[n-1], one, minusone); + if (w == NULL) { + Cudd_RecursiveDeref(dd, minusone); + Cudd_RecursiveDeref(dd, u); + return(NULL); + } + cuddRef(w); + t = Cudd_addIte(dd, x[n-1], w, minusone); + if (t == NULL) { + Cudd_RecursiveDeref(dd, minusone); + Cudd_RecursiveDeref(dd, u); + Cudd_RecursiveDeref(dd, w); + return(NULL); + } + cuddRef(t); + Cudd_RecursiveDeref(dd, w); + } + cuddDeref(minusone); /* minusone is in the result; it won't die */ + + /* Loop to build the rest of the ADD */ + for (i=n-2; i>=0; i--) { + t1 = t; u1 = u; + v = Cudd_addIte(dd, y[i], t1, u1); + if (v == NULL) { + Cudd_RecursiveDeref(dd, u1); + Cudd_RecursiveDeref(dd, t1); + return(NULL); + } + cuddRef(v); + u = Cudd_addIte(dd, x[i], v, u1); + if (u == NULL) { + Cudd_RecursiveDeref(dd, u1); + Cudd_RecursiveDeref(dd, t1); + Cudd_RecursiveDeref(dd, v); + return(NULL); + } + cuddRef(u); + Cudd_RecursiveDeref(dd, v); + if (i>0) { + w = Cudd_addIte(dd, y[i], u1, t1); + if (u == NULL) { + Cudd_RecursiveDeref(dd, u1); + Cudd_RecursiveDeref(dd, t1); + Cudd_RecursiveDeref(dd, u); + return(NULL); + } + cuddRef(w); + t = Cudd_addIte(dd, x[i], w, t1); + if (u == NULL) { + Cudd_RecursiveDeref(dd, u1); + Cudd_RecursiveDeref(dd, t1); + Cudd_RecursiveDeref(dd, u); + Cudd_RecursiveDeref(dd, w); + return(NULL); + } + cuddRef(t); + Cudd_RecursiveDeref(dd, w); + } + Cudd_RecursiveDeref(dd, u1); + Cudd_RecursiveDeref(dd, t1); + } + + cuddDeref(u); + return(u); + +} /* end of addWalshInt */ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddAndAbs.c b/abc_with_bb_support/src/bdd/cudd/cuddAndAbs.c new file mode 100644 index 000000000..393cdfcb3 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddAndAbs.c @@ -0,0 +1,306 @@ +/**CFile*********************************************************************** + + FileName [cuddAndAbs.c] + + PackageName [cudd] + + Synopsis [Combined AND and existential abstraction for BDDs] + + Description [External procedures included in this module: +
    +
  • Cudd_bddAndAbstract() +
+ Internal procedures included in this module: +
    +
  • cuddBddAndAbstractRecur() +
] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddAndAbs.c,v 1.1.1.1 2003/02/24 22:23:51 wjiang Exp $"; +#endif + + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Takes the AND of two BDDs and simultaneously abstracts the + variables in cube.] + + Description [Takes the AND of two BDDs and simultaneously abstracts + the variables in cube. The variables are existentially abstracted. + Returns a pointer to the result is successful; NULL otherwise. + Cudd_bddAndAbstract implements the semiring matrix multiplication + algorithm for the boolean semiring.] + + SideEffects [None] + + SeeAlso [Cudd_addMatrixMultiply Cudd_addTriangle Cudd_bddAnd] + +******************************************************************************/ +DdNode * +Cudd_bddAndAbstract( + DdManager * manager, + DdNode * f, + DdNode * g, + DdNode * cube) +{ + DdNode *res; + + do { + manager->reordered = 0; + res = cuddBddAndAbstractRecur(manager, f, g, cube); + } while (manager->reordered == 1); + return(res); + +} /* end of Cudd_bddAndAbstract */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Takes the AND of two BDDs and simultaneously abstracts the + variables in cube.] + + Description [Takes the AND of two BDDs and simultaneously abstracts + the variables in cube. The variables are existentially abstracted. + Returns a pointer to the result is successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddAndAbstract] + +******************************************************************************/ +DdNode * +cuddBddAndAbstractRecur( + DdManager * manager, + DdNode * f, + DdNode * g, + DdNode * cube) +{ + DdNode *F, *ft, *fe, *G, *gt, *ge; + DdNode *one, *zero, *r, *t, *e; + unsigned int topf, topg, topcube, top, index; + + statLine(manager); + one = DD_ONE(manager); + zero = Cudd_Not(one); + + /* Terminal cases. */ + if (f == zero || g == zero || f == Cudd_Not(g)) return(zero); + if (f == one && g == one) return(one); + + if (cube == one) { + return(cuddBddAndRecur(manager, f, g)); + } + if (f == one || f == g) { + return(cuddBddExistAbstractRecur(manager, g, cube)); + } + if (g == one) { + return(cuddBddExistAbstractRecur(manager, f, cube)); + } + /* At this point f, g, and cube are not constant. */ + + if (f > g) { /* Try to increase cache efficiency. */ + DdNode *tmp = f; + f = g; + g = tmp; + } + + /* Here we can skip the use of cuddI, because the operands are known + ** to be non-constant. + */ + F = Cudd_Regular(f); + G = Cudd_Regular(g); + topf = manager->perm[F->index]; + topg = manager->perm[G->index]; + top = ddMin(topf, topg); + topcube = manager->perm[cube->index]; + + while (topcube < top) { + cube = cuddT(cube); + if (cube == one) { + return(cuddBddAndRecur(manager, f, g)); + } + topcube = manager->perm[cube->index]; + } + /* Now, topcube >= top. */ + + /* Check cache. */ + if (F->ref != 1 || G->ref != 1) { + r = cuddCacheLookup(manager, DD_BDD_AND_ABSTRACT_TAG, f, g, cube); + if (r != NULL) { + return(r); + } + } + + if (topf == top) { + index = F->index; + ft = cuddT(F); + fe = cuddE(F); + if (Cudd_IsComplement(f)) { + ft = Cudd_Not(ft); + fe = Cudd_Not(fe); + } + } else { + index = G->index; + ft = fe = f; + } + + if (topg == top) { + gt = cuddT(G); + ge = cuddE(G); + if (Cudd_IsComplement(g)) { + gt = Cudd_Not(gt); + ge = Cudd_Not(ge); + } + } else { + gt = ge = g; + } + + if (topcube == top) { /* quantify */ + DdNode *Cube = cuddT(cube); + t = cuddBddAndAbstractRecur(manager, ft, gt, Cube); + if (t == NULL) return(NULL); + /* Special case: 1 OR anything = 1. Hence, no need to compute + ** the else branch if t is 1. Likewise t + t * anything == t. + ** Notice that t == fe implies that fe does not depend on the + ** variables in Cube. Likewise for t == ge. + */ + if (t == one || t == fe || t == ge) { + if (F->ref != 1 || G->ref != 1) + cuddCacheInsert(manager, DD_BDD_AND_ABSTRACT_TAG, + f, g, cube, t); + return(t); + } + cuddRef(t); + /* Special case: t + !t * anything == t + anything. */ + if (t == Cudd_Not(fe)) { + e = cuddBddExistAbstractRecur(manager, ge, Cube); + } else if (t == Cudd_Not(ge)) { + e = cuddBddExistAbstractRecur(manager, fe, Cube); + } else { + e = cuddBddAndAbstractRecur(manager, fe, ge, Cube); + } + if (e == NULL) { + Cudd_IterDerefBdd(manager, t); + return(NULL); + } + if (t == e) { + r = t; + cuddDeref(t); + } else { + cuddRef(e); + r = cuddBddAndRecur(manager, Cudd_Not(t), Cudd_Not(e)); + if (r == NULL) { + Cudd_IterDerefBdd(manager, t); + Cudd_IterDerefBdd(manager, e); + return(NULL); + } + r = Cudd_Not(r); + cuddRef(r); + Cudd_DelayedDerefBdd(manager, t); + Cudd_DelayedDerefBdd(manager, e); + cuddDeref(r); + } + } else { + t = cuddBddAndAbstractRecur(manager, ft, gt, cube); + if (t == NULL) return(NULL); + cuddRef(t); + e = cuddBddAndAbstractRecur(manager, fe, ge, cube); + if (e == NULL) { + Cudd_IterDerefBdd(manager, t); + return(NULL); + } + if (t == e) { + r = t; + cuddDeref(t); + } else { + cuddRef(e); + if (Cudd_IsComplement(t)) { + r = cuddUniqueInter(manager, (int) index, + Cudd_Not(t), Cudd_Not(e)); + if (r == NULL) { + Cudd_IterDerefBdd(manager, t); + Cudd_IterDerefBdd(manager, e); + return(NULL); + } + r = Cudd_Not(r); + } else { + r = cuddUniqueInter(manager,(int)index,t,e); + if (r == NULL) { + Cudd_IterDerefBdd(manager, t); + Cudd_IterDerefBdd(manager, e); + return(NULL); + } + } + cuddDeref(e); + cuddDeref(t); + } + } + + if (F->ref != 1 || G->ref != 1) + cuddCacheInsert(manager, DD_BDD_AND_ABSTRACT_TAG, f, g, cube, r); + return (r); + +} /* end of cuddBddAndAbstractRecur */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddAnneal.c b/abc_with_bb_support/src/bdd/cudd/cuddAnneal.c new file mode 100644 index 000000000..313224e81 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddAnneal.c @@ -0,0 +1,788 @@ +/**CFile*********************************************************************** + + FileName [cuddAnneal.c] + + PackageName [cudd] + + Synopsis [Reordering of DDs based on simulated annealing] + + Description [Internal procedures included in this file: +
    +
  • cuddAnnealing() +
+ Static procedures included in this file: +
    +
  • stopping_criterion() +
  • random_generator() +
  • ddExchange() +
  • ddJumpingAux() +
  • ddJumpingUp() +
  • ddJumpingDown() +
  • siftBackwardProb() +
  • copyOrder() +
  • restoreOrder() +
+ ] + + SeeAlso [] + + Author [Jae-Young Jang, Jorgen Sivesind] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/* Annealing parameters */ +#define BETA 0.6 +#define ALPHA 0.90 +#define EXC_PROB 0.4 +#define JUMP_UP_PROB 0.36 +#define MAXGEN_RATIO 15.0 +#define STOP_TEMP 1.0 + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddAnneal.c,v 1.1.1.1 2003/02/24 22:23:51 wjiang Exp $"; +#endif + +#ifdef DD_STATS +extern int ddTotalNumberSwapping; +extern int ddTotalNISwaps; +static int tosses; +static int acceptances; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int stopping_criterion ARGS((int c1, int c2, int c3, int c4, double temp)); +static double random_generator ARGS(()); +static int ddExchange ARGS((DdManager *table, int x, int y, double temp)); +static int ddJumpingAux ARGS((DdManager *table, int x, int x_low, int x_high, double temp)); +static Move * ddJumpingUp ARGS((DdManager *table, int x, int x_low, int initial_size)); +static Move * ddJumpingDown ARGS((DdManager *table, int x, int x_high, int initial_size)); +static int siftBackwardProb ARGS((DdManager *table, Move *moves, int size, double temp)); +static void copyOrder ARGS((DdManager *table, int *array, int lower, int upper)); +static int restoreOrder ARGS((DdManager *table, int *array, int lower, int upper)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Get new variable-order by simulated annealing algorithm.] + + Description [Get x, y by random selection. Choose either + exchange or jump randomly. In case of jump, choose between jump_up + and jump_down randomly. Do exchange or jump and get optimal case. + Loop until there is no improvement or temperature reaches + minimum. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddAnnealing( + DdManager * table, + int lower, + int upper) +{ + int nvars; + int size; + int x,y; + int result; + int c1, c2, c3, c4; + int BestCost; + int *BestOrder; + double NewTemp, temp; + double rand1; + int innerloop, maxGen; + int ecount, ucount, dcount; + + nvars = upper - lower + 1; + + result = cuddSifting(table,lower,upper); +#ifdef DD_STATS + (void) fprintf(table->out,"\n"); +#endif + if (result == 0) return(0); + + size = table->keys - table->isolated; + + /* Keep track of the best order. */ + BestCost = size; + BestOrder = ALLOC(int,nvars); + if (BestOrder == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } + copyOrder(table,BestOrder,lower,upper); + + temp = BETA * size; + maxGen = (int) (MAXGEN_RATIO * nvars); + + c1 = size + 10; + c2 = c1 + 10; + c3 = size; + c4 = c2 + 10; + ecount = ucount = dcount = 0; + + while (!stopping_criterion(c1, c2, c3, c4, temp)) { +#ifdef DD_STATS + (void) fprintf(table->out,"temp=%f\tsize=%d\tgen=%d\t", + temp,size,maxGen); + tosses = acceptances = 0; +#endif + for (innerloop = 0; innerloop < maxGen; innerloop++) { + /* Choose x, y randomly. */ + x = (int) Cudd_Random() % nvars; + do { + y = (int) Cudd_Random() % nvars; + } while (x == y); + x += lower; + y += lower; + if (x > y) { + int tmp = x; + x = y; + y = tmp; + } + + /* Choose move with roulette wheel. */ + rand1 = random_generator(); + if (rand1 < EXC_PROB) { + result = ddExchange(table,x,y,temp); /* exchange */ + ecount++; +#if 0 + (void) fprintf(table->out, + "Exchange of %d and %d: size = %d\n", + x,y,table->keys - table->isolated); +#endif + } else if (rand1 < EXC_PROB + JUMP_UP_PROB) { + result = ddJumpingAux(table,y,x,y,temp); /* jumping_up */ + ucount++; +#if 0 + (void) fprintf(table->out, + "Jump up of %d to %d: size = %d\n", + y,x,table->keys - table->isolated); +#endif + } else { + result = ddJumpingAux(table,x,x,y,temp); /* jumping_down */ + dcount++; +#if 0 + (void) fprintf(table->out, + "Jump down of %d to %d: size = %d\n", + x,y,table->keys - table->isolated); +#endif + } + + if (!result) { + FREE(BestOrder); + return(0); + } + + size = table->keys - table->isolated; /* keep current size */ + if (size < BestCost) { /* update best order */ + BestCost = size; + copyOrder(table,BestOrder,lower,upper); + } + } + c1 = c2; + c2 = c3; + c3 = c4; + c4 = size; + NewTemp = ALPHA * temp; + if (NewTemp >= 1.0) { + maxGen = (int)(log(NewTemp) / log(temp) * maxGen); + } + temp = NewTemp; /* control variable */ +#ifdef DD_STATS + (void) fprintf(table->out,"uphill = %d\taccepted = %d\n", + tosses,acceptances); + fflush(table->out); +#endif + } + + result = restoreOrder(table,BestOrder,lower,upper); + FREE(BestOrder); + if (!result) return(0); +#ifdef DD_STATS + fprintf(table->out,"#:N_EXCHANGE %8d : total exchanges\n",ecount); + fprintf(table->out,"#:N_JUMPUP %8d : total jumps up\n",ucount); + fprintf(table->out,"#:N_JUMPDOWN %8d : total jumps down",dcount); +#endif + return(1); + +} /* end of cuddAnnealing */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Checks termination condition.] + + Description [If temperature is STOP_TEMP or there is no improvement + then terminates. Returns 1 if the termination criterion is met; 0 + otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +stopping_criterion( + int c1, + int c2, + int c3, + int c4, + double temp) +{ + if (STOP_TEMP < temp) { + return(0); + } else if ((c1 == c2) && (c1 == c3) && (c1 == c4)) { + return(1); + } else { + return(0); + } + +} /* end of stopping_criterion */ + + +/**Function******************************************************************** + + Synopsis [Random number generator.] + + Description [Returns a double precision value between 0.0 and 1.0.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static double +random_generator( + ) +{ + return((double)(Cudd_Random() / 2147483561.0)); + +} /* end of random_generator */ + + +/**Function******************************************************************** + + Synopsis [This function is for exchanging two variables, x and y.] + + Description [This is the same funcion as ddSwapping except for + comparison expression. Use probability function, exp(-size_change/temp).] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +ddExchange( + DdManager * table, + int x, + int y, + double temp) +{ + Move *move,*moves; + int tmp; + int x_ref,y_ref; + int x_next,y_next; + int size, result; + int initial_size, limit_size; + + x_ref = x; + y_ref = y; + + x_next = cuddNextHigh(table,x); + y_next = cuddNextLow(table,y); + moves = NULL; + initial_size = limit_size = table->keys - table->isolated; + + for (;;) { + if (x_next == y_next) { + size = cuddSwapInPlace(table,x,x_next); + if (size == 0) goto ddExchangeOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddExchangeOutOfMem; + move->x = x; + move->y = x_next; + move->size = size; + move->next = moves; + moves = move; + size = cuddSwapInPlace(table,y_next,y); + if (size == 0) goto ddExchangeOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddExchangeOutOfMem; + move->x = y_next; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + size = cuddSwapInPlace(table,x,x_next); + if (size == 0) goto ddExchangeOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddExchangeOutOfMem; + move->x = x; + move->y = x_next; + move->size = size; + move->next = moves; + moves = move; + + tmp = x; + x = y; + y = tmp; + } else if (x == y_next) { + size = cuddSwapInPlace(table,x,x_next); + if (size == 0) goto ddExchangeOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddExchangeOutOfMem; + move->x = x; + move->y = x_next; + move->size = size; + move->next = moves; + moves = move; + tmp = x; + x = y; + y = tmp; + } else { + size = cuddSwapInPlace(table,x,x_next); + if (size == 0) goto ddExchangeOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddExchangeOutOfMem; + move->x = x; + move->y = x_next; + move->size = size; + move->next = moves; + moves = move; + size = cuddSwapInPlace(table,y_next,y); + if (size == 0) goto ddExchangeOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddExchangeOutOfMem; + move->x = y_next; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + x = x_next; + y = y_next; + } + + x_next = cuddNextHigh(table,x); + y_next = cuddNextLow(table,y); + if (x_next > y_ref) break; + + if ((double) size > DD_MAX_REORDER_GROWTH * (double) limit_size) { + break; + } else if (size < limit_size) { + limit_size = size; + } + } + + if (y_next>=x_ref) { + size = cuddSwapInPlace(table,y_next,y); + if (size == 0) goto ddExchangeOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddExchangeOutOfMem; + move->x = y_next; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + } + + /* move backward and stop at best position or accept uphill move */ + result = siftBackwardProb(table,moves,initial_size,temp); + if (!result) goto ddExchangeOutOfMem; + + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *) moves); + moves = move; + } + return(1); + +ddExchangeOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table,(DdNode *) moves); + moves = move; + } + return(0); + +} /* end of ddExchange */ + + +/**Function******************************************************************** + + Synopsis [Moves a variable to a specified position.] + + Description [If x==x_low, it executes jumping_down. If x==x_high, it + executes jumping_up. This funcion is similar to ddSiftingAux. Returns + 1 in case of success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +ddJumpingAux( + DdManager * table, + int x, + int x_low, + int x_high, + double temp) +{ + Move *move; + Move *moves; /* list of moves */ + int initial_size; + int result; + + initial_size = table->keys - table->isolated; + +#ifdef DD_DEBUG + assert(table->subtables[x].keys > 0); +#endif + + moves = NULL; + + if (cuddNextLow(table,x) < x_low) { + if (cuddNextHigh(table,x) > x_high) return(1); + moves = ddJumpingDown(table,x,x_high,initial_size); + /* after that point x --> x_high unless early termination */ + if (moves == NULL) goto ddJumpingAuxOutOfMem; + /* move backward and stop at best position or accept uphill move */ + result = siftBackwardProb(table,moves,initial_size,temp); + if (!result) goto ddJumpingAuxOutOfMem; + } else if (cuddNextHigh(table,x) > x_high) { + moves = ddJumpingUp(table,x,x_low,initial_size); + /* after that point x --> x_low unless early termination */ + if (moves == NULL) goto ddJumpingAuxOutOfMem; + /* move backward and stop at best position or accept uphill move */ + result = siftBackwardProb(table,moves,initial_size,temp); + if (!result) goto ddJumpingAuxOutOfMem; + } else { + (void) fprintf(table->err,"Unexpected condition in ddJumping\n"); + goto ddJumpingAuxOutOfMem; + } + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *) moves); + moves = move; + } + return(1); + +ddJumpingAuxOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *) moves); + moves = move; + } + return(0); + +} /* end of ddJumpingAux */ + + +/**Function******************************************************************** + + Synopsis [This function is for jumping up.] + + Description [This is a simplified version of ddSiftingUp. It does not + use lower bounding. Returns the set of moves in case of success; NULL + if memory is full.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static Move * +ddJumpingUp( + DdManager * table, + int x, + int x_low, + int initial_size) +{ + Move *moves; + Move *move; + int y; + int size; + int limit_size = initial_size; + + moves = NULL; + y = cuddNextLow(table,x); + while (y >= x_low) { + size = cuddSwapInPlace(table,y,x); + if (size == 0) goto ddJumpingUpOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddJumpingUpOutOfMem; + move->x = y; + move->y = x; + move->size = size; + move->next = moves; + moves = move; + if ((double) size > table->maxGrowth * (double) limit_size) { + break; + } else if (size < limit_size) { + limit_size = size; + } + x = y; + y = cuddNextLow(table,x); + } + return(moves); + +ddJumpingUpOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *) moves); + moves = move; + } + return(NULL); + +} /* end of ddJumpingUp */ + + +/**Function******************************************************************** + + Synopsis [This function is for jumping down.] + + Description [This is a simplified version of ddSiftingDown. It does not + use lower bounding. Returns the set of moves in case of success; NULL + if memory is full.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static Move * +ddJumpingDown( + DdManager * table, + int x, + int x_high, + int initial_size) +{ + Move *moves; + Move *move; + int y; + int size; + int limit_size = initial_size; + + moves = NULL; + y = cuddNextHigh(table,x); + while (y <= x_high) { + size = cuddSwapInPlace(table,x,y); + if (size == 0) goto ddJumpingDownOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddJumpingDownOutOfMem; + move->x = x; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + if ((double) size > table->maxGrowth * (double) limit_size) { + break; + } else if (size < limit_size) { + limit_size = size; + } + x = y; + y = cuddNextHigh(table,x); + } + return(moves); + +ddJumpingDownOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *) moves); + moves = move; + } + return(NULL); + +} /* end of ddJumpingDown */ + + +/**Function******************************************************************** + + Synopsis [Returns the DD to the best position encountered during + sifting if there was improvement.] + + Description [Otherwise, "tosses a coin" to decide whether to keep + the current configuration or return the DD to the original + one. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +siftBackwardProb( + DdManager * table, + Move * moves, + int size, + double temp) +{ + Move *move; + int res; + int best_size = size; + double coin, threshold; + + /* Look for best size during the last sifting */ + for (move = moves; move != NULL; move = move->next) { + if (move->size < best_size) { + best_size = move->size; + } + } + + /* If best_size equals size, the last sifting did not produce any + ** improvement. We now toss a coin to decide whether to retain + ** this change or not. + */ + if (best_size == size) { + coin = random_generator(); +#ifdef DD_STATS + tosses++; +#endif + threshold = exp(-((double)(table->keys - table->isolated - size))/temp); + if (coin < threshold) { +#ifdef DD_STATS + acceptances++; +#endif + return(1); + } + } + + /* Either there was improvement, or we have decided not to + ** accept the uphill move. Go to best position. + */ + res = table->keys - table->isolated; + for (move = moves; move != NULL; move = move->next) { + if (res == best_size) return(1); + res = cuddSwapInPlace(table,(int)move->x,(int)move->y); + if (!res) return(0); + } + + return(1); + +} /* end of sift_backward_prob */ + + +/**Function******************************************************************** + + Synopsis [Copies the current variable order to array.] + + Description [Copies the current variable order to array. + At the same time inverts the permutation.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +copyOrder( + DdManager * table, + int * array, + int lower, + int upper) +{ + int i; + int nvars; + + nvars = upper - lower + 1; + for (i = 0; i < nvars; i++) { + array[i] = table->invperm[i+lower]; + } + +} /* end of copyOrder */ + + +/**Function******************************************************************** + + Synopsis [Restores the variable order in array by a series of sifts up.] + + Description [Restores the variable order in array by a series of sifts up. + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +restoreOrder( + DdManager * table, + int * array, + int lower, + int upper) +{ + int i, x, y, size; + int nvars = upper - lower + 1; + + for (i = 0; i < nvars; i++) { + x = table->perm[array[i]]; +#ifdef DD_DEBUG + assert(x >= lower && x <= upper); +#endif + y = cuddNextLow(table,x); + while (y >= i + lower) { + size = cuddSwapInPlace(table,y,x); + if (size == 0) return(0); + x = y; + y = cuddNextLow(table,x); + } + } + + return(1); + +} /* end of restoreOrder */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddApa.c b/abc_with_bb_support/src/bdd/cudd/cuddApa.c new file mode 100644 index 000000000..ead3af1db --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddApa.c @@ -0,0 +1,930 @@ +/**CFile*********************************************************************** + + FileName [cuddApa.c] + + PackageName [cudd] + + Synopsis [Arbitrary precision arithmetic functions.] + + Description [External procedures included in this module: +
    +
  • +
+ Internal procedures included in this module: +
    +
  • () +
+ Static procedures included in this module: +
    +
  • () +
] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddApa.c,v 1.1.1.1 2003/02/24 22:23:51 wjiang Exp $"; +#endif + +static DdNode *background, *zero; + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static DdApaNumber cuddApaCountMintermAux ARGS((DdNode * node, int digits, DdApaNumber max, DdApaNumber min, st_table * table)); +static enum st_retval cuddApaStCountfree ARGS((char * key, char * value, char * arg)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Finds the number of digits for an arbitrary precision + integer.] + + Description [Finds the number of digits for an arbitrary precision + integer given the maximum number of binary digits. The number of + binary digits should be positive. Returns the number of digits if + successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +Cudd_ApaNumberOfDigits( + int binaryDigits) +{ + int digits; + + digits = binaryDigits / DD_APA_BITS; + if ((digits * DD_APA_BITS) != binaryDigits) + digits++; + return(digits); + +} /* end of Cudd_ApaNumberOfDigits */ + + +/**Function******************************************************************** + + Synopsis [Allocates memory for an arbitrary precision integer.] + + Description [Allocates memory for an arbitrary precision + integer. Returns a pointer to the allocated memory if successful; + NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdApaNumber +Cudd_NewApaNumber( + int digits) +{ + return(ALLOC(DdApaDigit, digits)); + +} /* end of Cudd_NewApaNumber */ + + +/**Function******************************************************************** + + Synopsis [Makes a copy of an arbitrary precision integer.] + + Description [Makes a copy of an arbitrary precision integer.] + + SideEffects [Changes parameter dest.] + + SeeAlso [] + +******************************************************************************/ +void +Cudd_ApaCopy( + int digits, + DdApaNumber source, + DdApaNumber dest) +{ + int i; + + for (i = 0; i < digits; i++) { + dest[i] = source[i]; + } + +} /* end of Cudd_ApaCopy */ + + +/**Function******************************************************************** + + Synopsis [Adds two arbitrary precision integers.] + + Description [Adds two arbitrary precision integers. Returns the + carry out of the most significant digit.] + + SideEffects [The result of the sum is stored in parameter sum.] + + SeeAlso [] + +******************************************************************************/ +DdApaDigit +Cudd_ApaAdd( + int digits, + DdApaNumber a, + DdApaNumber b, + DdApaNumber sum) +{ + int i; + DdApaDoubleDigit partial = 0; + + for (i = digits - 1; i >= 0; i--) { + partial = a[i] + b[i] + DD_MSDIGIT(partial); + sum[i] = (DdApaDigit) DD_LSDIGIT(partial); + } + return(DD_MSDIGIT(partial)); + +} /* end of Cudd_ApaAdd */ + + +/**Function******************************************************************** + + Synopsis [Subtracts two arbitrary precision integers.] + + Description [Subtracts two arbitrary precision integers. Returns the + borrow out of the most significant digit.] + + SideEffects [The result of the subtraction is stored in parameter + diff.] + + SeeAlso [] + +******************************************************************************/ +DdApaDigit +Cudd_ApaSubtract( + int digits, + DdApaNumber a, + DdApaNumber b, + DdApaNumber diff) +{ + int i; + DdApaDoubleDigit partial = DD_APA_BASE; + + for (i = digits - 1; i >= 0; i--) { + partial = a[i] - b[i] + DD_MSDIGIT(partial) + DD_APA_MASK; + diff[i] = (DdApaDigit) DD_LSDIGIT(partial); + } + return(DD_MSDIGIT(partial) - 1); + +} /* end of Cudd_ApaSubtract */ + + +/**Function******************************************************************** + + Synopsis [Divides an arbitrary precision integer by a digit.] + + Description [Divides an arbitrary precision integer by a digit.] + + SideEffects [The quotient is returned in parameter quotient.] + + SeeAlso [] + +******************************************************************************/ +DdApaDigit +Cudd_ApaShortDivision( + int digits, + DdApaNumber dividend, + DdApaDigit divisor, + DdApaNumber quotient) +{ + int i; + DdApaDigit remainder; + DdApaDoubleDigit partial; + + remainder = 0; + for (i = 0; i < digits; i++) { + partial = remainder * DD_APA_BASE + dividend[i]; + quotient[i] = (DdApaDigit) (partial/(DdApaDoubleDigit)divisor); + remainder = (DdApaDigit) (partial % divisor); + } + + return(remainder); + +} /* end of Cudd_ApaShortDivision */ + + +/**Function******************************************************************** + + Synopsis [Divides an arbitrary precision integer by an integer.] + + Description [Divides an arbitrary precision integer by a 32-bit + unsigned integer. Returns the remainder of the division. This + procedure relies on the assumption that the number of bits of a + DdApaDigit plus the number of bits of an unsigned int is less the + number of bits of the mantissa of a double. This guarantees that the + product of a DdApaDigit and an unsigned int can be represented + without loss of precision by a double. On machines where this + assumption is not satisfied, this procedure will malfunction.] + + SideEffects [The quotient is returned in parameter quotient.] + + SeeAlso [Cudd_ApaShortDivision] + +******************************************************************************/ +unsigned int +Cudd_ApaIntDivision( + int digits, + DdApaNumber dividend, + unsigned int divisor, + DdApaNumber quotient) +{ + int i; + double partial; + unsigned int remainder = 0; + double ddiv = (double) divisor; + + for (i = 0; i < digits; i++) { + partial = (double) remainder * DD_APA_BASE + dividend[i]; + quotient[i] = (DdApaDigit) (partial / ddiv); + remainder = (unsigned int) (partial - ((double)quotient[i] * ddiv)); + } + + return(remainder); + +} /* end of Cudd_ApaIntDivision */ + + +/**Function******************************************************************** + + Synopsis [Shifts right an arbitrary precision integer by one binary + place.] + + Description [Shifts right an arbitrary precision integer by one + binary place. The most significant binary digit of the result is + taken from parameter in.] + + SideEffects [The result is returned in parameter b.] + + SeeAlso [] + +******************************************************************************/ +void +Cudd_ApaShiftRight( + int digits, + DdApaDigit in, + DdApaNumber a, + DdApaNumber b) +{ + int i; + + for (i = digits - 1; i > 0; i--) { + b[i] = (a[i] >> 1) | ((a[i-1] & 1) << (DD_APA_BITS - 1)); + } + b[0] = (a[0] >> 1) | (in << (DD_APA_BITS - 1)); + +} /* end of Cudd_ApaShiftRight */ + + +/**Function******************************************************************** + + Synopsis [Sets an arbitrary precision integer to a one-digit literal.] + + Description [Sets an arbitrary precision integer to a one-digit literal.] + + SideEffects [The result is returned in parameter number.] + + SeeAlso [] + +******************************************************************************/ +void +Cudd_ApaSetToLiteral( + int digits, + DdApaNumber number, + DdApaDigit literal) +{ + int i; + + for (i = 0; i < digits - 1; i++) + number[i] = 0; + number[digits - 1] = literal; + +} /* end of Cudd_ApaSetToLiteral */ + + +/**Function******************************************************************** + + Synopsis [Sets an arbitrary precision integer to a power of two.] + + Description [Sets an arbitrary precision integer to a power of + two. If the power of two is too large to be represented, the number + is set to 0.] + + SideEffects [The result is returned in parameter number.] + + SeeAlso [] + +******************************************************************************/ +void +Cudd_ApaPowerOfTwo( + int digits, + DdApaNumber number, + int power) +{ + int i; + int index; + + for (i = 0; i < digits; i++) + number[i] = 0; + i = digits - 1 - power / DD_APA_BITS; + if (i < 0) return; + index = power & (DD_APA_BITS - 1); + number[i] = 1 << index; + +} /* end of Cudd_ApaPowerOfTwo */ + + +/**Function******************************************************************** + + Synopsis [Compares two arbitrary precision integers.] + + Description [Compares two arbitrary precision integers. Returns 1 if + the first number is larger; 0 if they are equal; -1 if the second + number is larger.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +Cudd_ApaCompare( + int digitsFirst, + DdApaNumber first, + int digitsSecond, + DdApaNumber second) +{ + int i; + int firstNZ, secondNZ; + + /* Find first non-zero in both numbers. */ + for (firstNZ = 0; firstNZ < digitsFirst; firstNZ++) + if (first[firstNZ] != 0) break; + for (secondNZ = 0; secondNZ < digitsSecond; secondNZ++) + if (second[secondNZ] != 0) break; + if (digitsFirst - firstNZ > digitsSecond - secondNZ) return(1); + else if (digitsFirst - firstNZ < digitsSecond - secondNZ) return(-1); + for (i = 0; i < digitsFirst - firstNZ; i++) { + if (first[firstNZ + i] > second[secondNZ + i]) return(1); + else if (first[firstNZ + i] < second[secondNZ + i]) return(-1); + } + return(0); + +} /* end of Cudd_ApaCompare */ + + +/**Function******************************************************************** + + Synopsis [Compares the ratios of two arbitrary precision integers to two + unsigned ints.] + + Description [Compares the ratios of two arbitrary precision integers + to two unsigned ints. Returns 1 if the first number is larger; 0 if + they are equal; -1 if the second number is larger.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +Cudd_ApaCompareRatios( + int digitsFirst, + DdApaNumber firstNum, + unsigned int firstDen, + int digitsSecond, + DdApaNumber secondNum, + unsigned int secondDen) +{ + int result; + DdApaNumber first, second; + unsigned int firstRem, secondRem; + + first = Cudd_NewApaNumber(digitsFirst); + firstRem = Cudd_ApaIntDivision(digitsFirst,firstNum,firstDen,first); + second = Cudd_NewApaNumber(digitsSecond); + secondRem = Cudd_ApaIntDivision(digitsSecond,secondNum,secondDen,second); + result = Cudd_ApaCompare(digitsFirst,first,digitsSecond,second); + if (result == 0) { + if ((double)firstRem/firstDen > (double)secondRem/secondDen) + return(1); + else if ((double)firstRem/firstDen < (double)secondRem/secondDen) + return(-1); + } + return(result); + +} /* end of Cudd_ApaCompareRatios */ + + +/**Function******************************************************************** + + Synopsis [Prints an arbitrary precision integer in hexadecimal format.] + + Description [Prints an arbitrary precision integer in hexadecimal format. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_ApaPrintDecimal Cudd_ApaPrintExponential] + +******************************************************************************/ +int +Cudd_ApaPrintHex( + FILE * fp, + int digits, + DdApaNumber number) +{ + int i, result; + + for (i = 0; i < digits; i++) { + result = fprintf(fp,DD_APA_HEXPRINT,number[i]); + if (result == EOF) + return(0); + } + return(1); + +} /* end of Cudd_ApaPrintHex */ + + +/**Function******************************************************************** + + Synopsis [Prints an arbitrary precision integer in decimal format.] + + Description [Prints an arbitrary precision integer in decimal format. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_ApaPrintHex Cudd_ApaPrintExponential] + +******************************************************************************/ +int +Cudd_ApaPrintDecimal( + FILE * fp, + int digits, + DdApaNumber number) +{ + int i, result; + DdApaDigit remainder; + DdApaNumber work; + unsigned char *decimal; + int leadingzero; + int decimalDigits = (int) (digits * log10((double) DD_APA_BASE)) + 1; + + work = Cudd_NewApaNumber(digits); + if (work == NULL) + return(0); + decimal = ALLOC(unsigned char, decimalDigits); + if (decimal == NULL) { + FREE(work); + return(0); + } + Cudd_ApaCopy(digits,number,work); + for (i = decimalDigits - 1; i >= 0; i--) { + remainder = Cudd_ApaShortDivision(digits,work,(DdApaDigit) 10,work); + decimal[i] = remainder; + } + FREE(work); + + leadingzero = 1; + for (i = 0; i < decimalDigits; i++) { + leadingzero = leadingzero && (decimal[i] == 0); + if ((!leadingzero) || (i == (decimalDigits - 1))) { + result = fprintf(fp,"%1d",decimal[i]); + if (result == EOF) { + FREE(decimal); + return(0); + } + } + } + FREE(decimal); + return(1); + +} /* end of Cudd_ApaPrintDecimal */ + + +/**Function******************************************************************** + + Synopsis [Prints an arbitrary precision integer in exponential format.] + + Description [Prints an arbitrary precision integer in exponential format. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_ApaPrintHex Cudd_ApaPrintDecimal] + +******************************************************************************/ +int +Cudd_ApaPrintExponential( + FILE * fp, + int digits, + DdApaNumber number, + int precision) +{ + int i, first, last, result; + DdApaDigit remainder; + DdApaNumber work; + unsigned char *decimal; + int decimalDigits = (int) (digits * log10((double) DD_APA_BASE)) + 1; + + work = Cudd_NewApaNumber(digits); + if (work == NULL) + return(0); + decimal = ALLOC(unsigned char, decimalDigits); + if (decimal == NULL) { + FREE(work); + return(0); + } + Cudd_ApaCopy(digits,number,work); + first = decimalDigits - 1; + for (i = decimalDigits - 1; i >= 0; i--) { + remainder = Cudd_ApaShortDivision(digits,work,(DdApaDigit) 10,work); + decimal[i] = remainder; + if (remainder != 0) first = i; /* keep track of MS non-zero */ + } + FREE(work); + last = ddMin(first + precision, decimalDigits); + + for (i = first; i < last; i++) { + result = fprintf(fp,"%s%1d",i == first+1 ? "." : "", decimal[i]); + if (result == EOF) { + FREE(decimal); + return(0); + } + } + FREE(decimal); + result = fprintf(fp,"e+%d",decimalDigits - first - 1); + if (result == EOF) { + return(0); + } + return(1); + +} /* end of Cudd_ApaPrintExponential */ + + +/**Function******************************************************************** + + Synopsis [Counts the number of minterms of a DD.] + + Description [Counts the number of minterms of a DD. The function is + assumed to depend on nvars variables. The minterm count is + represented as an arbitrary precision unsigned integer, to allow for + any number of variables CUDD supports. Returns a pointer to the + array representing the number of minterms of the function rooted at + node if successful; NULL otherwise.] + + SideEffects [The number of digits of the result is returned in + parameter digits.] + + SeeAlso [Cudd_CountMinterm] + +******************************************************************************/ +DdApaNumber +Cudd_ApaCountMinterm( + DdManager * manager, + DdNode * node, + int nvars, + int * digits) +{ + DdApaNumber max, min; + st_table *table; + DdApaNumber i,count; + + background = manager->background; + zero = Cudd_Not(manager->one); + + *digits = Cudd_ApaNumberOfDigits(nvars+1); + max = Cudd_NewApaNumber(*digits); + if (max == NULL) { + return(NULL); + } + Cudd_ApaPowerOfTwo(*digits,max,nvars); + min = Cudd_NewApaNumber(*digits); + if (min == NULL) { + FREE(max); + return(NULL); + } + Cudd_ApaSetToLiteral(*digits,min,0); + table = st_init_table(st_ptrcmp,st_ptrhash); + if (table == NULL) { + FREE(max); + FREE(min); + return(NULL); + } + i = cuddApaCountMintermAux(Cudd_Regular(node),*digits,max,min,table); + if (i == NULL) { + FREE(max); + FREE(min); + st_foreach(table, cuddApaStCountfree, NULL); + st_free_table(table); + return(NULL); + } + count = Cudd_NewApaNumber(*digits); + if (count == NULL) { + FREE(max); + FREE(min); + st_foreach(table, cuddApaStCountfree, NULL); + st_free_table(table); + if (Cudd_Regular(node)->ref == 1) FREE(i); + return(NULL); + } + if (Cudd_IsComplement(node)) { + (void) Cudd_ApaSubtract(*digits,max,i,count); + } else { + Cudd_ApaCopy(*digits,i,count); + } + FREE(max); + FREE(min); + st_foreach(table, cuddApaStCountfree, NULL); + st_free_table(table); + if (Cudd_Regular(node)->ref == 1) FREE(i); + return(count); + +} /* end of Cudd_ApaCountMinterm */ + + +/**Function******************************************************************** + + Synopsis [Prints the number of minterms of a BDD or ADD using + arbitrary precision arithmetic.] + + Description [Prints the number of minterms of a BDD or ADD using + arbitrary precision arithmetic. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_ApaPrintMintermExp] + +******************************************************************************/ +int +Cudd_ApaPrintMinterm( + FILE * fp, + DdManager * dd, + DdNode * node, + int nvars) +{ + int digits; + int result; + DdApaNumber count; + + count = Cudd_ApaCountMinterm(dd,node,nvars,&digits); + if (count == NULL) + return(0); + result = Cudd_ApaPrintDecimal(fp,digits,count); + FREE(count); + if (fprintf(fp,"\n") == EOF) { + return(0); + } + return(result); + +} /* end of Cudd_ApaPrintMinterm */ + + +/**Function******************************************************************** + + Synopsis [Prints the number of minterms of a BDD or ADD in exponential + format using arbitrary precision arithmetic.] + + Description [Prints the number of minterms of a BDD or ADD in + exponential format using arbitrary precision arithmetic. Parameter + precision controls the number of signficant digits printed. Returns + 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_ApaPrintMinterm] + +******************************************************************************/ +int +Cudd_ApaPrintMintermExp( + FILE * fp, + DdManager * dd, + DdNode * node, + int nvars, + int precision) +{ + int digits; + int result; + DdApaNumber count; + + count = Cudd_ApaCountMinterm(dd,node,nvars,&digits); + if (count == NULL) + return(0); + result = Cudd_ApaPrintExponential(fp,digits,count,precision); + FREE(count); + if (fprintf(fp,"\n") == EOF) { + return(0); + } + return(result); + +} /* end of Cudd_ApaPrintMintermExp */ + + +/**Function******************************************************************** + + Synopsis [Prints the density of a BDD or ADD using + arbitrary precision arithmetic.] + + Description [Prints the density of a BDD or ADD using + arbitrary precision arithmetic. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +Cudd_ApaPrintDensity( + FILE * fp, + DdManager * dd, + DdNode * node, + int nvars) +{ + int digits; + int result; + DdApaNumber count,density; + unsigned int size, remainder, fractional; + + count = Cudd_ApaCountMinterm(dd,node,nvars,&digits); + if (count == NULL) + return(0); + size = Cudd_DagSize(node); + density = Cudd_NewApaNumber(digits); + remainder = Cudd_ApaIntDivision(digits,count,size,density); + result = Cudd_ApaPrintDecimal(fp,digits,density); + FREE(count); + FREE(density); + fractional = (unsigned int)((double)remainder / size * 1000000); + if (fprintf(fp,".%u\n", fractional) == EOF) { + return(0); + } + return(result); + +} /* end of Cudd_ApaPrintDensity */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_ApaCountMinterm.] + + Description [Performs the recursive step of Cudd_ApaCountMinterm. + It is based on the following identity. Let |f| be the + number of minterms of f. Then: + + |f| = (|f0|+|f1|)/2 + + where f0 and f1 are the two cofactors of f. + Uses the identity |f'| = max - |f|. + The procedure expects the argument "node" to be a regular pointer, and + guarantees this condition is met in the recursive calls. + For efficiency, the result of a call is cached only if the node has + a reference count greater than 1. + Returns the number of minterms of the function rooted at node.] + + SideEffects [None] + +******************************************************************************/ +static DdApaNumber +cuddApaCountMintermAux( + DdNode * node, + int digits, + DdApaNumber max, + DdApaNumber min, + st_table * table) +{ + DdNode *Nt, *Ne; + DdApaNumber mint, mint1, mint2; + DdApaDigit carryout; + + if (cuddIsConstant(node)) { + if (node == background || node == zero) { + return(min); + } else { + return(max); + } + } + if (node->ref > 1 && st_lookup(table, (char *)node, (char **)&mint)) { + return(mint); + } + + Nt = cuddT(node); Ne = cuddE(node); + + mint1 = cuddApaCountMintermAux(Nt, digits, max, min, table); + if (mint1 == NULL) return(NULL); + mint2 = cuddApaCountMintermAux(Cudd_Regular(Ne), digits, max, min, table); + if (mint2 == NULL) { + if (Nt->ref == 1) FREE(mint1); + return(NULL); + } + mint = Cudd_NewApaNumber(digits); + if (mint == NULL) { + if (Nt->ref == 1) FREE(mint1); + if (Cudd_Regular(Ne)->ref == 1) FREE(mint2); + return(NULL); + } + if (Cudd_IsComplement(Ne)) { + (void) Cudd_ApaSubtract(digits,max,mint2,mint); + carryout = Cudd_ApaAdd(digits,mint1,mint,mint); + } else { + carryout = Cudd_ApaAdd(digits,mint1,mint2,mint); + } + Cudd_ApaShiftRight(digits,carryout,mint,mint); + /* If the refernce count of a child is 1, its minterm count + ** hasn't been stored in table. Therefore, it must be explicitly + ** freed here. */ + if (Nt->ref == 1) FREE(mint1); + if (Cudd_Regular(Ne)->ref == 1) FREE(mint2); + + if (node->ref > 1) { + if (st_insert(table, (char *)node, (char *)mint) == ST_OUT_OF_MEM) { + FREE(mint); + return(NULL); + } + } + return(mint); + +} /* end of cuddApaCountMintermAux */ + + +/**Function******************************************************************** + + Synopsis [Frees the memory used to store the minterm counts recorded + in the visited table.] + + Description [Frees the memory used to store the minterm counts + recorded in the visited table. Returns ST_CONTINUE.] + + SideEffects [None] + +******************************************************************************/ +static enum st_retval +cuddApaStCountfree( + char * key, + char * value, + char * arg) +{ + DdApaNumber d; + + d = (DdApaNumber) value; + FREE(d); + return(ST_CONTINUE); + +} /* end of cuddApaStCountfree */ + + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddApprox.c b/abc_with_bb_support/src/bdd/cudd/cuddApprox.c new file mode 100644 index 000000000..8e068719f --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddApprox.c @@ -0,0 +1,2192 @@ +/**CFile*********************************************************************** + + FileName [cuddApprox.c] + + PackageName [cudd] + + Synopsis [Procedures to approximate a given BDD.] + + Description [External procedures provided by this module: +
    +
  • Cudd_UnderApprox() +
  • Cudd_OverApprox() +
  • Cudd_RemapUnderApprox() +
  • Cudd_RemapOverApprox() +
  • Cudd_BiasedUnderApprox() +
  • Cudd_BiasedOverApprox() +
+ Internal procedures included in this module: +
    +
  • cuddUnderApprox() +
  • cuddRemapUnderApprox() +
  • cuddBiasedUnderApprox() +
+ Static procedures included in this module: +
    +
  • gatherInfoAux() +
  • gatherInfo() +
  • computeSavings() +
  • UAmarkNodes() +
  • UAbuildSubset() +
  • updateRefs() +
  • RAmarkNodes() +
  • BAmarkNodes() +
  • RAbuildSubset() +
+ ] + + SeeAlso [cuddSubsetHB.c cuddSubsetSP.c cuddGenCof.c] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no + warranty about the suitability of this software for any + purpose. It is presented on an AS IS basis.] + +******************************************************************************/ + +#ifdef __STDC__ +#include +#else +#define DBL_MAX_EXP 1024 +#endif +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define NOTHING 0 +#define REPLACE_T 1 +#define REPLACE_E 2 +#define REPLACE_N 3 +#define REPLACE_TT 4 +#define REPLACE_TE 5 + +#define DONT_CARE 0 +#define CARE 1 +#define TOTAL_CARE 2 +#define CARE_ERROR 3 + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/* Data structure to store the information on each node. It keeps the +** number of minterms of the function rooted at this node in terms of +** the number of variables specified by the user; the number of +** minterms of the complement; the impact of the number of minterms of +** this function on the number of minterms of the root function; the +** reference count of the node from within the root function; the +** reference count of the node from an internal node; and the flag +** that says whether the node should be replaced and how. */ +typedef struct NodeData { + double mintermsP; /* minterms for the regular node */ + double mintermsN; /* minterms for the complemented node */ + int functionRef; /* references from within this function */ + char care; /* node intersects care set */ + char replace; /* replacement decision */ + short int parity; /* 1: even; 2: odd; 3: both */ + DdNode *resultP; /* result for even parity */ + DdNode *resultN; /* result for odd parity */ +} NodeData; + +typedef struct ApproxInfo { + DdNode *one; /* one constant */ + DdNode *zero; /* BDD zero constant */ + NodeData *page; /* per-node information */ + st_table *table; /* hash table to access the per-node info */ + int index; /* index of the current node */ + double max; /* max number of minterms */ + int size; /* how many nodes are left */ + double minterms; /* how many minterms are left */ +} ApproxInfo; + +/* Item of the queue used in the levelized traversal of the BDD. */ +#ifdef __osf__ +#pragma pointer_size save +#pragma pointer_size short +#endif +typedef struct GlobalQueueItem { + struct GlobalQueueItem *next; + struct GlobalQueueItem *cnext; + DdNode *node; + double impactP; + double impactN; +} GlobalQueueItem; + +typedef struct LocalQueueItem { + struct LocalQueueItem *next; + struct LocalQueueItem *cnext; + DdNode *node; + int localRef; +} LocalQueueItem; +#ifdef __osf__ +#pragma pointer_size restore +#endif + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddApprox.c,v 1.1.1.1 2003/02/24 22:23:51 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static void updateParity ARGS((DdNode *node, ApproxInfo *info, int newparity)); +static NodeData * gatherInfoAux ARGS((DdNode *node, ApproxInfo *info, int parity)); +static ApproxInfo * gatherInfo ARGS((DdManager *dd, DdNode *node, int numVars, int parity)); +static int computeSavings ARGS((DdManager *dd, DdNode *f, DdNode *skip, ApproxInfo *info, DdLevelQueue *queue)); +static int updateRefs ARGS((DdManager *dd, DdNode *f, DdNode *skip, ApproxInfo *info, DdLevelQueue *queue)); +static int UAmarkNodes ARGS((DdManager *dd, DdNode *f, ApproxInfo *info, int threshold, int safe, double quality)); +static DdNode * UAbuildSubset ARGS((DdManager *dd, DdNode *node, ApproxInfo *info)); +static int RAmarkNodes ARGS((DdManager *dd, DdNode *f, ApproxInfo *info, int threshold, double quality)); +static int BAmarkNodes ARGS((DdManager *dd, DdNode *f, ApproxInfo *info, int threshold, double quality1, double quality0)); +static DdNode * RAbuildSubset ARGS((DdManager *dd, DdNode *node, ApproxInfo *info)); +static int BAapplyBias ARGS((DdManager *dd, DdNode *f, DdNode *b, ApproxInfo *info, DdHashTable *cache)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Extracts a dense subset from a BDD with Shiple's + underapproximation method.] + + Description [Extracts a dense subset from a BDD. This procedure uses + a variant of Tom Shiple's underapproximation method. The main + difference from the original method is that density is used as cost + function. Returns a pointer to the BDD of the subset if + successful. NULL if the procedure runs out of memory. The parameter + numVars is the maximum number of variables to be used in minterm + calculation. The optimal number should be as close as possible to + the size of the support of f. However, it is safe to pass the value + returned by Cudd_ReadSize for numVars when the number of variables + is under 1023. If numVars is larger than 1023, it will cause + overflow. If a 0 parameter is passed then the procedure will compute + a value which will avoid overflow but will cause underflow with 2046 + variables or more.] + + SideEffects [None] + + SeeAlso [Cudd_SubsetShortPaths Cudd_SubsetHeavyBranch Cudd_ReadSize] + +******************************************************************************/ +DdNode * +Cudd_UnderApprox( + DdManager * dd /* manager */, + DdNode * f /* function to be subset */, + int numVars /* number of variables in the support of f */, + int threshold /* when to stop approximation */, + int safe /* enforce safe approximation */, + double quality /* minimum improvement for accepted changes */) +{ + DdNode *subset; + + do { + dd->reordered = 0; + subset = cuddUnderApprox(dd, f, numVars, threshold, safe, quality); + } while (dd->reordered == 1); + + return(subset); + +} /* end of Cudd_UnderApprox */ + + +/**Function******************************************************************** + + Synopsis [Extracts a dense superset from a BDD with Shiple's + underapproximation method.] + + Description [Extracts a dense superset from a BDD. The procedure is + identical to the underapproximation procedure except for the fact that it + works on the complement of the given function. Extracting the subset + of the complement function is equivalent to extracting the superset + of the function. + Returns a pointer to the BDD of the superset if successful. NULL if + intermediate result causes the procedure to run out of memory. The + parameter numVars is the maximum number of variables to be used in + minterm calculation. The optimal number + should be as close as possible to the size of the support of f. + However, it is safe to pass the value returned by Cudd_ReadSize for + numVars when the number of variables is under 1023. If numVars is + larger than 1023, it will overflow. If a 0 parameter is passed then + the procedure will compute a value which will avoid overflow but + will cause underflow with 2046 variables or more.] + + SideEffects [None] + + SeeAlso [Cudd_SupersetHeavyBranch Cudd_SupersetShortPaths Cudd_ReadSize] + +******************************************************************************/ +DdNode * +Cudd_OverApprox( + DdManager * dd /* manager */, + DdNode * f /* function to be superset */, + int numVars /* number of variables in the support of f */, + int threshold /* when to stop approximation */, + int safe /* enforce safe approximation */, + double quality /* minimum improvement for accepted changes */) +{ + DdNode *subset, *g; + + g = Cudd_Not(f); + do { + dd->reordered = 0; + subset = cuddUnderApprox(dd, g, numVars, threshold, safe, quality); + } while (dd->reordered == 1); + + return(Cudd_NotCond(subset, (subset != NULL))); + +} /* end of Cudd_OverApprox */ + + +/**Function******************************************************************** + + Synopsis [Extracts a dense subset from a BDD with the remapping + underapproximation method.] + + Description [Extracts a dense subset from a BDD. This procedure uses + a remapping technique and density as the cost function. + Returns a pointer to the BDD of the subset if + successful. NULL if the procedure runs out of memory. The parameter + numVars is the maximum number of variables to be used in minterm + calculation. The optimal number should be as close as possible to + the size of the support of f. However, it is safe to pass the value + returned by Cudd_ReadSize for numVars when the number of variables + is under 1023. If numVars is larger than 1023, it will cause + overflow. If a 0 parameter is passed then the procedure will compute + a value which will avoid overflow but will cause underflow with 2046 + variables or more.] + + SideEffects [None] + + SeeAlso [Cudd_SubsetShortPaths Cudd_SubsetHeavyBranch Cudd_UnderApprox Cudd_ReadSize] + +******************************************************************************/ +DdNode * +Cudd_RemapUnderApprox( + DdManager * dd /* manager */, + DdNode * f /* function to be subset */, + int numVars /* number of variables in the support of f */, + int threshold /* when to stop approximation */, + double quality /* minimum improvement for accepted changes */) +{ + DdNode *subset; + + do { + dd->reordered = 0; + subset = cuddRemapUnderApprox(dd, f, numVars, threshold, quality); + } while (dd->reordered == 1); + + return(subset); + +} /* end of Cudd_RemapUnderApprox */ + + +/**Function******************************************************************** + + Synopsis [Extracts a dense superset from a BDD with the remapping + underapproximation method.] + + Description [Extracts a dense superset from a BDD. The procedure is + identical to the underapproximation procedure except for the fact that it + works on the complement of the given function. Extracting the subset + of the complement function is equivalent to extracting the superset + of the function. + Returns a pointer to the BDD of the superset if successful. NULL if + intermediate result causes the procedure to run out of memory. The + parameter numVars is the maximum number of variables to be used in + minterm calculation. The optimal number + should be as close as possible to the size of the support of f. + However, it is safe to pass the value returned by Cudd_ReadSize for + numVars when the number of variables is under 1023. If numVars is + larger than 1023, it will overflow. If a 0 parameter is passed then + the procedure will compute a value which will avoid overflow but + will cause underflow with 2046 variables or more.] + + SideEffects [None] + + SeeAlso [Cudd_SupersetHeavyBranch Cudd_SupersetShortPaths Cudd_ReadSize] + +******************************************************************************/ +DdNode * +Cudd_RemapOverApprox( + DdManager * dd /* manager */, + DdNode * f /* function to be superset */, + int numVars /* number of variables in the support of f */, + int threshold /* when to stop approximation */, + double quality /* minimum improvement for accepted changes */) +{ + DdNode *subset, *g; + + g = Cudd_Not(f); + do { + dd->reordered = 0; + subset = cuddRemapUnderApprox(dd, g, numVars, threshold, quality); + } while (dd->reordered == 1); + + return(Cudd_NotCond(subset, (subset != NULL))); + +} /* end of Cudd_RemapOverApprox */ + + +/**Function******************************************************************** + + Synopsis [Extracts a dense subset from a BDD with the biased + underapproximation method.] + + Description [Extracts a dense subset from a BDD. This procedure uses + a biased remapping technique and density as the cost function. The bias + is a function. This procedure tries to approximate where the bias is 0 + and preserve the given function where the bias is 1. + Returns a pointer to the BDD of the subset if + successful. NULL if the procedure runs out of memory. The parameter + numVars is the maximum number of variables to be used in minterm + calculation. The optimal number should be as close as possible to + the size of the support of f. However, it is safe to pass the value + returned by Cudd_ReadSize for numVars when the number of variables + is under 1023. If numVars is larger than 1023, it will cause + overflow. If a 0 parameter is passed then the procedure will compute + a value which will avoid overflow but will cause underflow with 2046 + variables or more.] + + SideEffects [None] + + SeeAlso [Cudd_SubsetShortPaths Cudd_SubsetHeavyBranch Cudd_UnderApprox + Cudd_RemapUnderApprox Cudd_ReadSize] + +******************************************************************************/ +DdNode * +Cudd_BiasedUnderApprox( + DdManager *dd /* manager */, + DdNode *f /* function to be subset */, + DdNode *b /* bias function */, + int numVars /* number of variables in the support of f */, + int threshold /* when to stop approximation */, + double quality1 /* minimum improvement for accepted changes when b=1 */, + double quality0 /* minimum improvement for accepted changes when b=0 */) +{ + DdNode *subset; + + do { + dd->reordered = 0; + subset = cuddBiasedUnderApprox(dd, f, b, numVars, threshold, quality1, + quality0); + } while (dd->reordered == 1); + + return(subset); + +} /* end of Cudd_BiasedUnderApprox */ + + +/**Function******************************************************************** + + Synopsis [Extracts a dense superset from a BDD with the biased + underapproximation method.] + + Description [Extracts a dense superset from a BDD. The procedure is + identical to the underapproximation procedure except for the fact that it + works on the complement of the given function. Extracting the subset + of the complement function is equivalent to extracting the superset + of the function. + Returns a pointer to the BDD of the superset if successful. NULL if + intermediate result causes the procedure to run out of memory. The + parameter numVars is the maximum number of variables to be used in + minterm calculation. The optimal number + should be as close as possible to the size of the support of f. + However, it is safe to pass the value returned by Cudd_ReadSize for + numVars when the number of variables is under 1023. If numVars is + larger than 1023, it will overflow. If a 0 parameter is passed then + the procedure will compute a value which will avoid overflow but + will cause underflow with 2046 variables or more.] + + SideEffects [None] + + SeeAlso [Cudd_SupersetHeavyBranch Cudd_SupersetShortPaths + Cudd_RemapOverApprox Cudd_BiasedUnderApprox Cudd_ReadSize] + +******************************************************************************/ +DdNode * +Cudd_BiasedOverApprox( + DdManager *dd /* manager */, + DdNode *f /* function to be superset */, + DdNode *b /* bias function */, + int numVars /* number of variables in the support of f */, + int threshold /* when to stop approximation */, + double quality1 /* minimum improvement for accepted changes when b=1*/, + double quality0 /* minimum improvement for accepted changes when b=0 */) +{ + DdNode *subset, *g; + + g = Cudd_Not(f); + do { + dd->reordered = 0; + subset = cuddBiasedUnderApprox(dd, g, b, numVars, threshold, quality1, + quality0); + } while (dd->reordered == 1); + + return(Cudd_NotCond(subset, (subset != NULL))); + +} /* end of Cudd_BiasedOverApprox */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Applies Tom Shiple's underappoximation algorithm.] + + Description [Applies Tom Shiple's underappoximation algorithm. Proceeds + in three phases: +
    +
  • collect information on each node in the BDD; this is done via DFS. +
  • traverse the BDD in top-down fashion and compute for each node + whether its elimination increases density. +
  • traverse the BDD via DFS and actually perform the elimination. +
+ Returns the approximated BDD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_UnderApprox] + +******************************************************************************/ +DdNode * +cuddUnderApprox( + DdManager * dd /* DD manager */, + DdNode * f /* current DD */, + int numVars /* maximum number of variables */, + int threshold /* threshold under which approximation stops */, + int safe /* enforce safe approximation */, + double quality /* minimum improvement for accepted changes */) +{ + ApproxInfo *info; + DdNode *subset; + int result; + + if (f == NULL) { + fprintf(dd->err, "Cannot subset, nil object\n"); + return(NULL); + } + + if (Cudd_IsConstant(f)) { + return(f); + } + + /* Create table where node data are accessible via a hash table. */ + info = gatherInfo(dd, f, numVars, safe); + if (info == NULL) { + (void) fprintf(dd->err, "Out-of-memory; Cannot subset\n"); + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + + /* Mark nodes that should be replaced by zero. */ + result = UAmarkNodes(dd, f, info, threshold, safe, quality); + if (result == 0) { + (void) fprintf(dd->err, "Out-of-memory; Cannot subset\n"); + FREE(info->page); + st_free_table(info->table); + FREE(info); + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + + /* Build the result. */ + subset = UAbuildSubset(dd, f, info); +#if 1 + if (subset && info->size < Cudd_DagSize(subset)) + (void) fprintf(dd->err, "Wrong prediction: %d versus actual %d\n", + info->size, Cudd_DagSize(subset)); +#endif + FREE(info->page); + st_free_table(info->table); + FREE(info); + +#ifdef DD_DEBUG + if (subset != NULL) { + cuddRef(subset); +#if 0 + (void) Cudd_DebugCheck(dd); + (void) Cudd_CheckKeys(dd); +#endif + if (!Cudd_bddLeq(dd, subset, f)) { + (void) fprintf(dd->err, "Wrong subset\n"); + dd->errorCode = CUDD_INTERNAL_ERROR; + } + cuddDeref(subset); + } +#endif + return(subset); + +} /* end of cuddUnderApprox */ + + +/**Function******************************************************************** + + Synopsis [Applies the remapping underappoximation algorithm.] + + Description [Applies the remapping underappoximation algorithm. + Proceeds in three phases: +
    +
  • collect information on each node in the BDD; this is done via DFS. +
  • traverse the BDD in top-down fashion and compute for each node + whether remapping increases density. +
  • traverse the BDD via DFS and actually perform the elimination. +
+ Returns the approximated BDD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_RemapUnderApprox] + +******************************************************************************/ +DdNode * +cuddRemapUnderApprox( + DdManager * dd /* DD manager */, + DdNode * f /* current DD */, + int numVars /* maximum number of variables */, + int threshold /* threshold under which approximation stops */, + double quality /* minimum improvement for accepted changes */) +{ + ApproxInfo *info; + DdNode *subset; + int result; + + if (f == NULL) { + fprintf(dd->err, "Cannot subset, nil object\n"); + dd->errorCode = CUDD_INVALID_ARG; + return(NULL); + } + + if (Cudd_IsConstant(f)) { + return(f); + } + + /* Create table where node data are accessible via a hash table. */ + info = gatherInfo(dd, f, numVars, TRUE); + if (info == NULL) { + (void) fprintf(dd->err, "Out-of-memory; Cannot subset\n"); + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + + /* Mark nodes that should be replaced by zero. */ + result = RAmarkNodes(dd, f, info, threshold, quality); + if (result == 0) { + (void) fprintf(dd->err, "Out-of-memory; Cannot subset\n"); + FREE(info->page); + st_free_table(info->table); + FREE(info); + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + + /* Build the result. */ + subset = RAbuildSubset(dd, f, info); +#if 1 + if (subset && info->size < Cudd_DagSize(subset)) + (void) fprintf(dd->err, "Wrong prediction: %d versus actual %d\n", + info->size, Cudd_DagSize(subset)); +#endif + FREE(info->page); + st_free_table(info->table); + FREE(info); + +#ifdef DD_DEBUG + if (subset != NULL) { + cuddRef(subset); +#if 0 + (void) Cudd_DebugCheck(dd); + (void) Cudd_CheckKeys(dd); +#endif + if (!Cudd_bddLeq(dd, subset, f)) { + (void) fprintf(dd->err, "Wrong subset\n"); + } + cuddDeref(subset); + dd->errorCode = CUDD_INTERNAL_ERROR; + } +#endif + return(subset); + +} /* end of cuddRemapUnderApprox */ + + +/**Function******************************************************************** + + Synopsis [Applies the biased remapping underappoximation algorithm.] + + Description [Applies the biased remapping underappoximation algorithm. + Proceeds in three phases: +
    +
  • collect information on each node in the BDD; this is done via DFS. +
  • traverse the BDD in top-down fashion and compute for each node + whether remapping increases density. +
  • traverse the BDD via DFS and actually perform the elimination. +
+ Returns the approximated BDD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_BiasedUnderApprox] + +******************************************************************************/ +DdNode * +cuddBiasedUnderApprox( + DdManager *dd /* DD manager */, + DdNode *f /* current DD */, + DdNode *b /* bias function */, + int numVars /* maximum number of variables */, + int threshold /* threshold under which approximation stops */, + double quality1 /* minimum improvement for accepted changes when b=1 */, + double quality0 /* minimum improvement for accepted changes when b=1 */) +{ + ApproxInfo *info; + DdNode *subset; + int result; + DdHashTable *cache; + + if (f == NULL) { + fprintf(dd->err, "Cannot subset, nil object\n"); + dd->errorCode = CUDD_INVALID_ARG; + return(NULL); + } + + if (Cudd_IsConstant(f)) { + return(f); + } + + /* Create table where node data are accessible via a hash table. */ + info = gatherInfo(dd, f, numVars, TRUE); + if (info == NULL) { + (void) fprintf(dd->err, "Out-of-memory; Cannot subset\n"); + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + + cache = cuddHashTableInit(dd,2,2); + result = BAapplyBias(dd, Cudd_Regular(f), b, info, cache); + if (result == CARE_ERROR) { + (void) fprintf(dd->err, "Out-of-memory; Cannot subset\n"); + cuddHashTableQuit(cache); + FREE(info->page); + st_free_table(info->table); + FREE(info); + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + cuddHashTableQuit(cache); + + /* Mark nodes that should be replaced by zero. */ + result = BAmarkNodes(dd, f, info, threshold, quality1, quality0); + if (result == 0) { + (void) fprintf(dd->err, "Out-of-memory; Cannot subset\n"); + FREE(info->page); + st_free_table(info->table); + FREE(info); + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + + /* Build the result. */ + subset = RAbuildSubset(dd, f, info); +#if 1 + if (subset && info->size < Cudd_DagSize(subset)) + (void) fprintf(dd->err, "Wrong prediction: %d versus actual %d\n", + info->size, Cudd_DagSize(subset)); +#endif + FREE(info->page); + st_free_table(info->table); + FREE(info); + +#ifdef DD_DEBUG + if (subset != NULL) { + cuddRef(subset); +#if 0 + (void) Cudd_DebugCheck(dd); + (void) Cudd_CheckKeys(dd); +#endif + if (!Cudd_bddLeq(dd, subset, f)) { + (void) fprintf(dd->err, "Wrong subset\n"); + } + cuddDeref(subset); + dd->errorCode = CUDD_INTERNAL_ERROR; + } +#endif + return(subset); + +} /* end of cuddBiasedUnderApprox */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Recursively update the parity of the paths reaching a node.] + + Description [Recursively update the parity of the paths reaching a node. + Assumes that node is regular and propagates the invariant.] + + SideEffects [None] + + SeeAlso [gatherInfoAux] + +******************************************************************************/ +static void +updateParity( + DdNode * node /* function to analyze */, + ApproxInfo * info /* info on BDD */, + int newparity /* new parity for node */) +{ + NodeData *infoN; + DdNode *E; + + if (!st_lookup(info->table, (char *)node, (char **)&infoN)) return; + if ((infoN->parity & newparity) != 0) return; + infoN->parity |= newparity; + if (Cudd_IsConstant(node)) return; + updateParity(cuddT(node),info,newparity); + E = cuddE(node); + if (Cudd_IsComplement(E)) { + updateParity(Cudd_Not(E),info,3-newparity); + } else { + updateParity(E,info,newparity); + } + return; + +} /* end of updateParity */ + + +/**Function******************************************************************** + + Synopsis [Recursively counts minterms and computes reference counts + of each node in the BDD.] + + Description [Recursively counts minterms and computes reference + counts of each node in the BDD. Similar to the cuddCountMintermAux + which recursively counts the number of minterms for the dag rooted + at each node in terms of the total number of variables (max). It assumes + that the node pointer passed to it is regular and it maintains the + invariant.] + + SideEffects [None] + + SeeAlso [gatherInfo] + +******************************************************************************/ +static NodeData * +gatherInfoAux( + DdNode * node /* function to analyze */, + ApproxInfo * info /* info on BDD */, + int parity /* gather parity information */) +{ + DdNode *N, *Nt, *Ne; + NodeData *infoN, *infoT, *infoE; + + N = Cudd_Regular(node); + + /* Check whether entry for this node exists. */ + if (st_lookup(info->table, (char *)N, (char **)&infoN)) { + if (parity) { + /* Update parity and propagate. */ + updateParity(N, info, 1 + (int) Cudd_IsComplement(node)); + } + return(infoN); + } + + /* Compute the cofactors. */ + Nt = Cudd_NotCond(cuddT(N), N != node); + Ne = Cudd_NotCond(cuddE(N), N != node); + + infoT = gatherInfoAux(Nt, info, parity); + if (infoT == NULL) return(NULL); + infoE = gatherInfoAux(Ne, info, parity); + if (infoE == NULL) return(NULL); + + infoT->functionRef++; + infoE->functionRef++; + + /* Point to the correct location in the page. */ + infoN = &(info->page[info->index++]); + infoN->parity |= 1 + (short) Cudd_IsComplement(node); + + infoN->mintermsP = infoT->mintermsP/2; + infoN->mintermsN = infoT->mintermsN/2; + if (Cudd_IsComplement(Ne) ^ Cudd_IsComplement(node)) { + infoN->mintermsP += infoE->mintermsN/2; + infoN->mintermsN += infoE->mintermsP/2; + } else { + infoN->mintermsP += infoE->mintermsP/2; + infoN->mintermsN += infoE->mintermsN/2; + } + + /* Insert entry for the node in the table. */ + if (st_insert(info->table,(char *)N, (char *)infoN) == ST_OUT_OF_MEM) { + return(NULL); + } + return(infoN); + +} /* end of gatherInfoAux */ + + +/**Function******************************************************************** + + Synopsis [Gathers information about each node.] + + Description [Counts minterms and computes reference counts of each + node in the BDD . The minterm count is separately computed for the + node and its complement. This is to avoid cancellation + errors. Returns a pointer to the data structure holding the + information gathered if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [cuddUnderApprox gatherInfoAux] + +******************************************************************************/ +static ApproxInfo * +gatherInfo( + DdManager * dd /* manager */, + DdNode * node /* function to be analyzed */, + int numVars /* number of variables node depends on */, + int parity /* gather parity information */) +{ + ApproxInfo *info; + NodeData *infoTop; + + /* If user did not give numVars value, set it to the maximum + ** exponent that the pow function can take. The -1 is due to the + ** discrepancy in the value that pow takes and the value that + ** log gives. + */ + if (numVars == 0) { + numVars = DBL_MAX_EXP - 1; + } + + info = ALLOC(ApproxInfo,1); + if (info == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + info->max = pow(2.0,(double) numVars); + info->one = DD_ONE(dd); + info->zero = Cudd_Not(info->one); + info->size = Cudd_DagSize(node); + /* All the information gathered will be stored in a contiguous + ** piece of memory, which is allocated here. This can be done + ** efficiently because we have counted the number of nodes of the + ** BDD. info->index points to the next available entry in the array + ** that stores the per-node information. */ + info->page = ALLOC(NodeData,info->size); + if (info->page == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + FREE(info); + return(NULL); + } + memset(info->page, 0, info->size * sizeof(NodeData)); /* clear all page */ + info->table = st_init_table(st_ptrcmp,st_ptrhash); + if (info->table == NULL) { + FREE(info->page); + FREE(info); + return(NULL); + } + /* We visit the DAG in post-order DFS. Hence, the constant node is + ** in first position, and the root of the DAG is in last position. */ + + /* Info for the constant node: Initialize only fields different from 0. */ + if (st_insert(info->table, (char *)info->one, (char *)info->page) == ST_OUT_OF_MEM) { + FREE(info->page); + FREE(info); + st_free_table(info->table); + return(NULL); + } + info->page[0].mintermsP = info->max; + info->index = 1; + + infoTop = gatherInfoAux(node,info,parity); + if (infoTop == NULL) { + FREE(info->page); + st_free_table(info->table); + FREE(info); + return(NULL); + } + if (Cudd_IsComplement(node)) { + info->minterms = infoTop->mintermsN; + } else { + info->minterms = infoTop->mintermsP; + } + + infoTop->functionRef = 1; + return(info); + +} /* end of gatherInfo */ + + +/**Function******************************************************************** + + Synopsis [Counts the nodes that would be eliminated if a given node + were replaced by zero.] + + Description [Counts the nodes that would be eliminated if a given + node were replaced by zero. This procedure uses a queue passed by + the caller for efficiency: since the queue is left empty at the + endof the search, it can be reused as is by the next search. Returns + the count (always striclty positive) if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [cuddUnderApprox] + +******************************************************************************/ +static int +computeSavings( + DdManager * dd, + DdNode * f, + DdNode * skip, + ApproxInfo * info, + DdLevelQueue * queue) +{ + NodeData *infoN; + LocalQueueItem *item; + DdNode *node; + int savings = 0; + + node = Cudd_Regular(f); + skip = Cudd_Regular(skip); + /* Insert the given node in the level queue. Its local reference + ** count is set equal to the function reference count so that the + ** search will continue from it when it is retrieved. */ + item = (LocalQueueItem *) + cuddLevelQueueEnqueue(queue,node,cuddI(dd,node->index)); + if (item == NULL) + return(0); + (void) st_lookup(info->table, (char *)node, (char **)&infoN); + item->localRef = infoN->functionRef; + + /* Process the queue. */ + while (queue->first != NULL) { + item = (LocalQueueItem *) queue->first; + node = item->node; + cuddLevelQueueDequeue(queue,cuddI(dd,node->index)); + if (node == skip) continue; + (void) st_lookup(info->table, (char *)node, (char **)&infoN); + if (item->localRef != infoN->functionRef) { + /* This node is shared. */ + continue; + } + savings++; + if (!cuddIsConstant(cuddT(node))) { + item = (LocalQueueItem *) cuddLevelQueueEnqueue(queue,cuddT(node), + cuddI(dd,cuddT(node)->index)); + if (item == NULL) return(0); + item->localRef++; + } + if (!Cudd_IsConstant(cuddE(node))) { + item = (LocalQueueItem *) cuddLevelQueueEnqueue(queue,Cudd_Regular(cuddE(node)), + cuddI(dd,Cudd_Regular(cuddE(node))->index)); + if (item == NULL) return(0); + item->localRef++; + } + } + +#ifdef DD_DEBUG + /* At the end of a local search the queue should be empty. */ + assert(queue->size == 0); +#endif + return(savings); + +} /* end of computeSavings */ + + +/**Function******************************************************************** + + Synopsis [Update function reference counts.] + + Description [Update function reference counts to account for replacement. + Returns the number of nodes saved if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [UAmarkNodes RAmarkNodes] + +******************************************************************************/ +static int +updateRefs( + DdManager * dd, + DdNode * f, + DdNode * skip, + ApproxInfo * info, + DdLevelQueue * queue) +{ + NodeData *infoN; + LocalQueueItem *item; + DdNode *node; + int savings = 0; + + node = Cudd_Regular(f); + /* Insert the given node in the level queue. Its function reference + ** count is set equal to 0 so that the search will continue from it + ** when it is retrieved. */ + item = (LocalQueueItem *) cuddLevelQueueEnqueue(queue,node,cuddI(dd,node->index)); + if (item == NULL) + return(0); + (void) st_lookup(info->table, (char *)node, (char **)&infoN); + infoN->functionRef = 0; + + if (skip != NULL) { + /* Increase the function reference count of the node to be skipped + ** by 1 to account for the node pointing to it that will be created. */ + skip = Cudd_Regular(skip); + (void) st_lookup(info->table, (char *)skip, (char **)&infoN); + infoN->functionRef++; + } + + /* Process the queue. */ + while (queue->first != NULL) { + item = (LocalQueueItem *) queue->first; + node = item->node; + cuddLevelQueueDequeue(queue,cuddI(dd,node->index)); + (void) st_lookup(info->table, (char *)node, (char **)&infoN); + if (infoN->functionRef != 0) { + /* This node is shared or must be skipped. */ + continue; + } + savings++; + if (!cuddIsConstant(cuddT(node))) { + item = (LocalQueueItem *) cuddLevelQueueEnqueue(queue,cuddT(node), + cuddI(dd,cuddT(node)->index)); + if (item == NULL) return(0); + (void) st_lookup(info->table, (char *)cuddT(node), + (char **)&infoN); + infoN->functionRef--; + } + if (!Cudd_IsConstant(cuddE(node))) { + item = (LocalQueueItem *) cuddLevelQueueEnqueue(queue,Cudd_Regular(cuddE(node)), + cuddI(dd,Cudd_Regular(cuddE(node))->index)); + if (item == NULL) return(0); + (void) st_lookup(info->table, (char *)Cudd_Regular(cuddE(node)), + (char **)&infoN); + infoN->functionRef--; + } + } + +#ifdef DD_DEBUG + /* At the end of a local search the queue should be empty. */ + assert(queue->size == 0); +#endif + return(savings); + +} /* end of updateRefs */ + + +/**Function******************************************************************** + + Synopsis [Marks nodes for replacement by zero.] + + Description [Marks nodes for replacement by zero. Returns 1 if successful; + 0 otherwise.] + + SideEffects [None] + + SeeAlso [cuddUnderApprox] + +******************************************************************************/ +static int +UAmarkNodes( + DdManager * dd /* manager */, + DdNode * f /* function to be analyzed */, + ApproxInfo * info /* info on BDD */, + int threshold /* when to stop approximating */, + int safe /* enforce safe approximation */, + double quality /* minimum improvement for accepted changes */) +{ + DdLevelQueue *queue; + DdLevelQueue *localQueue; + NodeData *infoN; + GlobalQueueItem *item; + DdNode *node; + double numOnset; + double impactP, impactN; + int savings; + +#if 0 + (void) printf("initial size = %d initial minterms = %g\n", + info->size, info->minterms); +#endif + queue = cuddLevelQueueInit(dd->size,sizeof(GlobalQueueItem),info->size); + if (queue == NULL) { + return(0); + } + localQueue = cuddLevelQueueInit(dd->size,sizeof(LocalQueueItem), + dd->initSlots); + if (localQueue == NULL) { + cuddLevelQueueQuit(queue); + return(0); + } + node = Cudd_Regular(f); + item = (GlobalQueueItem *) cuddLevelQueueEnqueue(queue,node,cuddI(dd,node->index)); + if (item == NULL) { + cuddLevelQueueQuit(queue); + cuddLevelQueueQuit(localQueue); + return(0); + } + if (Cudd_IsComplement(f)) { + item->impactP = 0.0; + item->impactN = 1.0; + } else { + item->impactP = 1.0; + item->impactN = 0.0; + } + while (queue->first != NULL) { + /* If the size of the subset is below the threshold, quit. */ + if (info->size <= threshold) + break; + item = (GlobalQueueItem *) queue->first; + node = item->node; + node = Cudd_Regular(node); + (void) st_lookup(info->table, (char *)node, (char **)&infoN); + if (safe && infoN->parity == 3) { + cuddLevelQueueDequeue(queue,cuddI(dd,node->index)); + continue; + } + impactP = item->impactP; + impactN = item->impactN; + numOnset = infoN->mintermsP * impactP + infoN->mintermsN * impactN; + savings = computeSavings(dd,node,NULL,info,localQueue); + if (savings == 0) { + cuddLevelQueueQuit(queue); + cuddLevelQueueQuit(localQueue); + return(0); + } + cuddLevelQueueDequeue(queue,cuddI(dd,node->index)); +#if 0 + (void) printf("node %p: impact = %g/%g numOnset = %g savings %d\n", + node, impactP, impactN, numOnset, savings); +#endif + if ((1 - numOnset / info->minterms) > + quality * (1 - (double) savings / info->size)) { + infoN->replace = TRUE; + info->size -= savings; + info->minterms -=numOnset; +#if 0 + (void) printf("replace: new size = %d new minterms = %g\n", + info->size, info->minterms); +#endif + savings -= updateRefs(dd,node,NULL,info,localQueue); + assert(savings == 0); + continue; + } + if (!cuddIsConstant(cuddT(node))) { + item = (GlobalQueueItem *) cuddLevelQueueEnqueue(queue,cuddT(node), + cuddI(dd,cuddT(node)->index)); + item->impactP += impactP/2.0; + item->impactN += impactN/2.0; + } + if (!Cudd_IsConstant(cuddE(node))) { + item = (GlobalQueueItem *) cuddLevelQueueEnqueue(queue,Cudd_Regular(cuddE(node)), + cuddI(dd,Cudd_Regular(cuddE(node))->index)); + if (Cudd_IsComplement(cuddE(node))) { + item->impactP += impactN/2.0; + item->impactN += impactP/2.0; + } else { + item->impactP += impactP/2.0; + item->impactN += impactN/2.0; + } + } + } + + cuddLevelQueueQuit(queue); + cuddLevelQueueQuit(localQueue); + return(1); + +} /* end of UAmarkNodes */ + + +/**Function******************************************************************** + + Synopsis [Builds the subset BDD.] + + Description [Builds the subset BDD. Based on the info table, + replaces selected nodes by zero. Returns a pointer to the result if + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [cuddUnderApprox] + +******************************************************************************/ +static DdNode * +UAbuildSubset( + DdManager * dd /* DD manager */, + DdNode * node /* current node */, + ApproxInfo * info /* node info */) +{ + + DdNode *Nt, *Ne, *N, *t, *e, *r; + NodeData *infoN; + + if (Cudd_IsConstant(node)) + return(node); + + N = Cudd_Regular(node); + + if (st_lookup(info->table, (char *)N, (char **)&infoN)) { + if (infoN->replace == TRUE) { + return(info->zero); + } + if (N == node ) { + if (infoN->resultP != NULL) { + return(infoN->resultP); + } + } else { + if (infoN->resultN != NULL) { + return(infoN->resultN); + } + } + } else { + (void) fprintf(dd->err, + "Something is wrong, ought to be in info table\n"); + dd->errorCode = CUDD_INTERNAL_ERROR; + return(NULL); + } + + Nt = Cudd_NotCond(cuddT(N), Cudd_IsComplement(node)); + Ne = Cudd_NotCond(cuddE(N), Cudd_IsComplement(node)); + + t = UAbuildSubset(dd, Nt, info); + if (t == NULL) { + return(NULL); + } + cuddRef(t); + + e = UAbuildSubset(dd, Ne, info); + if (e == NULL) { + Cudd_RecursiveDeref(dd,t); + return(NULL); + } + cuddRef(e); + + if (Cudd_IsComplement(t)) { + t = Cudd_Not(t); + e = Cudd_Not(e); + r = (t == e) ? t : cuddUniqueInter(dd, N->index, t, e); + if (r == NULL) { + Cudd_RecursiveDeref(dd, e); + Cudd_RecursiveDeref(dd, t); + return(NULL); + } + r = Cudd_Not(r); + } else { + r = (t == e) ? t : cuddUniqueInter(dd, N->index, t, e); + if (r == NULL) { + Cudd_RecursiveDeref(dd, e); + Cudd_RecursiveDeref(dd, t); + return(NULL); + } + } + cuddDeref(t); + cuddDeref(e); + + if (N == node) { + infoN->resultP = r; + } else { + infoN->resultN = r; + } + + return(r); + +} /* end of UAbuildSubset */ + + +/**Function******************************************************************** + + Synopsis [Marks nodes for remapping.] + + Description [Marks nodes for remapping. Returns 1 if successful; 0 + otherwise.] + + SideEffects [None] + + SeeAlso [cuddRemapUnderApprox] + +******************************************************************************/ +static int +RAmarkNodes( + DdManager * dd /* manager */, + DdNode * f /* function to be analyzed */, + ApproxInfo * info /* info on BDD */, + int threshold /* when to stop approximating */, + double quality /* minimum improvement for accepted changes */) +{ + DdLevelQueue *queue; + DdLevelQueue *localQueue; + NodeData *infoN, *infoT, *infoE; + GlobalQueueItem *item; + DdNode *node, *T, *E; + DdNode *shared; /* grandchild shared by the two children of node */ + double numOnset; + double impact, impactP, impactN; + double minterms; + int savings; + int replace; + +#if 0 + (void) fprintf(dd->out,"initial size = %d initial minterms = %g\n", + info->size, info->minterms); +#endif + queue = cuddLevelQueueInit(dd->size,sizeof(GlobalQueueItem),info->size); + if (queue == NULL) { + return(0); + } + localQueue = cuddLevelQueueInit(dd->size,sizeof(LocalQueueItem), + dd->initSlots); + if (localQueue == NULL) { + cuddLevelQueueQuit(queue); + return(0); + } + /* Enqueue regular pointer to root and initialize impact. */ + node = Cudd_Regular(f); + item = (GlobalQueueItem *) + cuddLevelQueueEnqueue(queue,node,cuddI(dd,node->index)); + if (item == NULL) { + cuddLevelQueueQuit(queue); + cuddLevelQueueQuit(localQueue); + return(0); + } + if (Cudd_IsComplement(f)) { + item->impactP = 0.0; + item->impactN = 1.0; + } else { + item->impactP = 1.0; + item->impactN = 0.0; + } + /* The nodes retrieved here are guaranteed to be non-terminal. + ** The initial node is not terminal because constant nodes are + ** dealt with in the calling procedure. Subsequent nodes are inserted + ** only if they are not terminal. */ + while (queue->first != NULL) { + /* If the size of the subset is below the threshold, quit. */ + if (info->size <= threshold) + break; + item = (GlobalQueueItem *) queue->first; + node = item->node; +#ifdef DD_DEBUG + assert(item->impactP >= 0 && item->impactP <= 1.0); + assert(item->impactN >= 0 && item->impactN <= 1.0); + assert(!Cudd_IsComplement(node)); + assert(!Cudd_IsConstant(node)); +#endif + if (!st_lookup(info->table, (char *)node, (char **)&infoN)) { + cuddLevelQueueQuit(queue); + cuddLevelQueueQuit(localQueue); + return(0); + } +#ifdef DD_DEBUG + assert(infoN->parity >= 1 && infoN->parity <= 3); +#endif + if (infoN->parity == 3) { + /* This node can be reached through paths of different parity. + ** It is not safe to replace it, because remapping will give + ** an incorrect result, while replacement by 0 may cause node + ** splitting. */ + cuddLevelQueueDequeue(queue,cuddI(dd,node->index)); + continue; + } + T = cuddT(node); + E = cuddE(node); + shared = NULL; + impactP = item->impactP; + impactN = item->impactN; + if (Cudd_bddLeq(dd,T,E)) { + /* Here we know that E is regular. */ +#ifdef DD_DEBUG + assert(!Cudd_IsComplement(E)); +#endif + (void) st_lookup(info->table, (char *)T, (char **)&infoT); + (void) st_lookup(info->table, (char *)E, (char **)&infoE); + if (infoN->parity == 1) { + impact = impactP; + minterms = infoE->mintermsP/2.0 - infoT->mintermsP/2.0; + if (infoE->functionRef == 1 && !Cudd_IsConstant(E)) { + savings = 1 + computeSavings(dd,E,NULL,info,localQueue); + if (savings == 1) { + cuddLevelQueueQuit(queue); + cuddLevelQueueQuit(localQueue); + return(0); + } + } else { + savings = 1; + } + replace = REPLACE_E; + } else { +#ifdef DD_DEBUG + assert(infoN->parity == 2); +#endif + impact = impactN; + minterms = infoT->mintermsN/2.0 - infoE->mintermsN/2.0; + if (infoT->functionRef == 1 && !Cudd_IsConstant(T)) { + savings = 1 + computeSavings(dd,T,NULL,info,localQueue); + if (savings == 1) { + cuddLevelQueueQuit(queue); + cuddLevelQueueQuit(localQueue); + return(0); + } + } else { + savings = 1; + } + replace = REPLACE_T; + } + numOnset = impact * minterms; + } else if (Cudd_bddLeq(dd,E,T)) { + /* Here E may be complemented. */ + DdNode *Ereg = Cudd_Regular(E); + (void) st_lookup(info->table, (char *)T, (char **)&infoT); + (void) st_lookup(info->table, (char *)Ereg, (char **)&infoE); + if (infoN->parity == 1) { + impact = impactP; + minterms = infoT->mintermsP/2.0 - + ((E == Ereg) ? infoE->mintermsP : infoE->mintermsN)/2.0; + if (infoT->functionRef == 1 && !Cudd_IsConstant(T)) { + savings = 1 + computeSavings(dd,T,NULL,info,localQueue); + if (savings == 1) { + cuddLevelQueueQuit(queue); + cuddLevelQueueQuit(localQueue); + return(0); + } + } else { + savings = 1; + } + replace = REPLACE_T; + } else { +#ifdef DD_DEBUG + assert(infoN->parity == 2); +#endif + impact = impactN; + minterms = ((E == Ereg) ? infoE->mintermsN : + infoE->mintermsP)/2.0 - infoT->mintermsN/2.0; + if (infoE->functionRef == 1 && !Cudd_IsConstant(E)) { + savings = 1 + computeSavings(dd,E,NULL,info,localQueue); + if (savings == 1) { + cuddLevelQueueQuit(queue); + cuddLevelQueueQuit(localQueue); + return(0); + } + } else { + savings = 1; + } + replace = REPLACE_E; + } + numOnset = impact * minterms; + } else { + DdNode *Ereg = Cudd_Regular(E); + DdNode *TT = cuddT(T); + DdNode *ET = Cudd_NotCond(cuddT(Ereg), Cudd_IsComplement(E)); + if (T->index == Ereg->index && TT == ET) { + shared = TT; + replace = REPLACE_TT; + } else { + DdNode *TE = cuddE(T); + DdNode *EE = Cudd_NotCond(cuddE(Ereg), Cudd_IsComplement(E)); + if (T->index == Ereg->index && TE == EE) { + shared = TE; + replace = REPLACE_TE; + } else { + replace = REPLACE_N; + } + } + numOnset = infoN->mintermsP * impactP + infoN->mintermsN * impactN; + savings = computeSavings(dd,node,shared,info,localQueue); + if (shared != NULL) { + NodeData *infoS; + (void) st_lookup(info->table, (char *)Cudd_Regular(shared), + (char **)&infoS); + if (Cudd_IsComplement(shared)) { + numOnset -= (infoS->mintermsN * impactP + + infoS->mintermsP * impactN)/2.0; + } else { + numOnset -= (infoS->mintermsP * impactP + + infoS->mintermsN * impactN)/2.0; + } + savings--; + } + } + + cuddLevelQueueDequeue(queue,cuddI(dd,node->index)); +#if 0 + if (replace == REPLACE_T || replace == REPLACE_E) + (void) printf("node %p: impact = %g numOnset = %g savings %d\n", + node, impact, numOnset, savings); + else + (void) printf("node %p: impact = %g/%g numOnset = %g savings %d\n", + node, impactP, impactN, numOnset, savings); +#endif + if ((1 - numOnset / info->minterms) > + quality * (1 - (double) savings / info->size)) { + infoN->replace = replace; + info->size -= savings; + info->minterms -=numOnset; +#if 0 + (void) printf("remap(%d): new size = %d new minterms = %g\n", + replace, info->size, info->minterms); +#endif + if (replace == REPLACE_N) { + savings -= updateRefs(dd,node,NULL,info,localQueue); + } else if (replace == REPLACE_T) { + savings -= updateRefs(dd,node,E,info,localQueue); + } else if (replace == REPLACE_E) { + savings -= updateRefs(dd,node,T,info,localQueue); + } else { +#ifdef DD_DEBUG + assert(replace == REPLACE_TT || replace == REPLACE_TE); +#endif + savings -= updateRefs(dd,node,shared,info,localQueue) - 1; + } + assert(savings == 0); + } else { + replace = NOTHING; + } + if (replace == REPLACE_N) continue; + if ((replace == REPLACE_E || replace == NOTHING) && + !cuddIsConstant(cuddT(node))) { + item = (GlobalQueueItem *) cuddLevelQueueEnqueue(queue,cuddT(node), + cuddI(dd,cuddT(node)->index)); + if (replace == REPLACE_E) { + item->impactP += impactP; + item->impactN += impactN; + } else { + item->impactP += impactP/2.0; + item->impactN += impactN/2.0; + } + } + if ((replace == REPLACE_T || replace == NOTHING) && + !Cudd_IsConstant(cuddE(node))) { + item = (GlobalQueueItem *) cuddLevelQueueEnqueue(queue,Cudd_Regular(cuddE(node)), + cuddI(dd,Cudd_Regular(cuddE(node))->index)); + if (Cudd_IsComplement(cuddE(node))) { + if (replace == REPLACE_T) { + item->impactP += impactN; + item->impactN += impactP; + } else { + item->impactP += impactN/2.0; + item->impactN += impactP/2.0; + } + } else { + if (replace == REPLACE_T) { + item->impactP += impactP; + item->impactN += impactN; + } else { + item->impactP += impactP/2.0; + item->impactN += impactN/2.0; + } + } + } + if ((replace == REPLACE_TT || replace == REPLACE_TE) && + !Cudd_IsConstant(shared)) { + item = (GlobalQueueItem *) cuddLevelQueueEnqueue(queue,Cudd_Regular(shared), + cuddI(dd,Cudd_Regular(shared)->index)); + if (Cudd_IsComplement(shared)) { + if (replace == REPLACE_T) { + item->impactP += impactN; + item->impactN += impactP; + } else { + item->impactP += impactN/2.0; + item->impactN += impactP/2.0; + } + } else { + if (replace == REPLACE_T) { + item->impactP += impactP; + item->impactN += impactN; + } else { + item->impactP += impactP/2.0; + item->impactN += impactN/2.0; + } + } + } + } + + cuddLevelQueueQuit(queue); + cuddLevelQueueQuit(localQueue); + return(1); + +} /* end of RAmarkNodes */ + + +/**Function******************************************************************** + + Synopsis [Marks nodes for remapping.] + + Description [Marks nodes for remapping. Returns 1 if successful; 0 + otherwise.] + + SideEffects [None] + + SeeAlso [cuddRemapUnderApprox] + +******************************************************************************/ +static int +BAmarkNodes( + DdManager *dd /* manager */, + DdNode *f /* function to be analyzed */, + ApproxInfo *info /* info on BDD */, + int threshold /* when to stop approximating */, + double quality1 /* minimum improvement for accepted changes when b=1 */, + double quality0 /* minimum improvement for accepted changes when b=0 */) +{ + DdLevelQueue *queue; + DdLevelQueue *localQueue; + NodeData *infoN, *infoT, *infoE; + GlobalQueueItem *item; + DdNode *node, *T, *E; + DdNode *shared; /* grandchild shared by the two children of node */ + double numOnset; + double impact, impactP, impactN; + double minterms; + double quality; + int savings; + int replace; + +#if 0 + (void) fprintf(dd->out,"initial size = %d initial minterms = %g\n", + info->size, info->minterms); +#endif + queue = cuddLevelQueueInit(dd->size,sizeof(GlobalQueueItem),info->size); + if (queue == NULL) { + return(0); + } + localQueue = cuddLevelQueueInit(dd->size,sizeof(LocalQueueItem), + dd->initSlots); + if (localQueue == NULL) { + cuddLevelQueueQuit(queue); + return(0); + } + /* Enqueue regular pointer to root and initialize impact. */ + node = Cudd_Regular(f); + item = (GlobalQueueItem *) + cuddLevelQueueEnqueue(queue,node,cuddI(dd,node->index)); + if (item == NULL) { + cuddLevelQueueQuit(queue); + cuddLevelQueueQuit(localQueue); + return(0); + } + if (Cudd_IsComplement(f)) { + item->impactP = 0.0; + item->impactN = 1.0; + } else { + item->impactP = 1.0; + item->impactN = 0.0; + } + /* The nodes retrieved here are guaranteed to be non-terminal. + ** The initial node is not terminal because constant nodes are + ** dealt with in the calling procedure. Subsequent nodes are inserted + ** only if they are not terminal. */ + while (queue->first != NULL) { + /* If the size of the subset is below the threshold, quit. */ + if (info->size <= threshold) + break; + item = (GlobalQueueItem *) queue->first; + node = item->node; +#ifdef DD_DEBUG + assert(item->impactP >= 0 && item->impactP <= 1.0); + assert(item->impactN >= 0 && item->impactN <= 1.0); + assert(!Cudd_IsComplement(node)); + assert(!Cudd_IsConstant(node)); +#endif + if (!st_lookup(info->table, (char *)node, (char **)&infoN)) { + cuddLevelQueueQuit(queue); + cuddLevelQueueQuit(localQueue); + return(0); + } + quality = infoN->care ? quality1 : quality0; +#ifdef DD_DEBUG + assert(infoN->parity >= 1 && infoN->parity <= 3); +#endif + if (infoN->parity == 3) { + /* This node can be reached through paths of different parity. + ** It is not safe to replace it, because remapping will give + ** an incorrect result, while replacement by 0 may cause node + ** splitting. */ + cuddLevelQueueDequeue(queue,cuddI(dd,node->index)); + continue; + } + T = cuddT(node); + E = cuddE(node); + shared = NULL; + impactP = item->impactP; + impactN = item->impactN; + if (Cudd_bddLeq(dd,T,E)) { + /* Here we know that E is regular. */ +#ifdef DD_DEBUG + assert(!Cudd_IsComplement(E)); +#endif + (void) st_lookup(info->table, (char *)T, (char **)&infoT); + (void) st_lookup(info->table, (char *)E, (char **)&infoE); + if (infoN->parity == 1) { + impact = impactP; + minterms = infoE->mintermsP/2.0 - infoT->mintermsP/2.0; + if (infoE->functionRef == 1 && !Cudd_IsConstant(E)) { + savings = 1 + computeSavings(dd,E,NULL,info,localQueue); + if (savings == 1) { + cuddLevelQueueQuit(queue); + cuddLevelQueueQuit(localQueue); + return(0); + } + } else { + savings = 1; + } + replace = REPLACE_E; + } else { +#ifdef DD_DEBUG + assert(infoN->parity == 2); +#endif + impact = impactN; + minterms = infoT->mintermsN/2.0 - infoE->mintermsN/2.0; + if (infoT->functionRef == 1 && !Cudd_IsConstant(T)) { + savings = 1 + computeSavings(dd,T,NULL,info,localQueue); + if (savings == 1) { + cuddLevelQueueQuit(queue); + cuddLevelQueueQuit(localQueue); + return(0); + } + } else { + savings = 1; + } + replace = REPLACE_T; + } + numOnset = impact * minterms; + } else if (Cudd_bddLeq(dd,E,T)) { + /* Here E may be complemented. */ + DdNode *Ereg = Cudd_Regular(E); + (void) st_lookup(info->table, (char *)T, (char **)&infoT); + (void) st_lookup(info->table, (char *)Ereg, (char **)&infoE); + if (infoN->parity == 1) { + impact = impactP; + minterms = infoT->mintermsP/2.0 - + ((E == Ereg) ? infoE->mintermsP : infoE->mintermsN)/2.0; + if (infoT->functionRef == 1 && !Cudd_IsConstant(T)) { + savings = 1 + computeSavings(dd,T,NULL,info,localQueue); + if (savings == 1) { + cuddLevelQueueQuit(queue); + cuddLevelQueueQuit(localQueue); + return(0); + } + } else { + savings = 1; + } + replace = REPLACE_T; + } else { +#ifdef DD_DEBUG + assert(infoN->parity == 2); +#endif + impact = impactN; + minterms = ((E == Ereg) ? infoE->mintermsN : + infoE->mintermsP)/2.0 - infoT->mintermsN/2.0; + if (infoE->functionRef == 1 && !Cudd_IsConstant(E)) { + savings = 1 + computeSavings(dd,E,NULL,info,localQueue); + if (savings == 1) { + cuddLevelQueueQuit(queue); + cuddLevelQueueQuit(localQueue); + return(0); + } + } else { + savings = 1; + } + replace = REPLACE_E; + } + numOnset = impact * minterms; + } else { + DdNode *Ereg = Cudd_Regular(E); + DdNode *TT = cuddT(T); + DdNode *ET = Cudd_NotCond(cuddT(Ereg), Cudd_IsComplement(E)); + if (T->index == Ereg->index && TT == ET) { + shared = TT; + replace = REPLACE_TT; + } else { + DdNode *TE = cuddE(T); + DdNode *EE = Cudd_NotCond(cuddE(Ereg), Cudd_IsComplement(E)); + if (T->index == Ereg->index && TE == EE) { + shared = TE; + replace = REPLACE_TE; + } else { + replace = REPLACE_N; + } + } + numOnset = infoN->mintermsP * impactP + infoN->mintermsN * impactN; + savings = computeSavings(dd,node,shared,info,localQueue); + if (shared != NULL) { + NodeData *infoS; + (void) st_lookup(info->table, (char *)Cudd_Regular(shared), + (char **)&infoS); + if (Cudd_IsComplement(shared)) { + numOnset -= (infoS->mintermsN * impactP + + infoS->mintermsP * impactN)/2.0; + } else { + numOnset -= (infoS->mintermsP * impactP + + infoS->mintermsN * impactN)/2.0; + } + savings--; + } + } + + cuddLevelQueueDequeue(queue,cuddI(dd,node->index)); +#if 0 + if (replace == REPLACE_T || replace == REPLACE_E) + (void) printf("node %p: impact = %g numOnset = %g savings %d\n", + node, impact, numOnset, savings); + else + (void) printf("node %p: impact = %g/%g numOnset = %g savings %d\n", + node, impactP, impactN, numOnset, savings); +#endif + if ((1 - numOnset / info->minterms) > + quality * (1 - (double) savings / info->size)) { + infoN->replace = replace; + info->size -= savings; + info->minterms -=numOnset; +#if 0 + (void) printf("remap(%d): new size = %d new minterms = %g\n", + replace, info->size, info->minterms); +#endif + if (replace == REPLACE_N) { + savings -= updateRefs(dd,node,NULL,info,localQueue); + } else if (replace == REPLACE_T) { + savings -= updateRefs(dd,node,E,info,localQueue); + } else if (replace == REPLACE_E) { + savings -= updateRefs(dd,node,T,info,localQueue); + } else { +#ifdef DD_DEBUG + assert(replace == REPLACE_TT || replace == REPLACE_TE); +#endif + savings -= updateRefs(dd,node,shared,info,localQueue) - 1; + } + assert(savings == 0); + } else { + replace = NOTHING; + } + if (replace == REPLACE_N) continue; + if ((replace == REPLACE_E || replace == NOTHING) && + !cuddIsConstant(cuddT(node))) { + item = (GlobalQueueItem *) cuddLevelQueueEnqueue(queue,cuddT(node), + cuddI(dd,cuddT(node)->index)); + if (replace == REPLACE_E) { + item->impactP += impactP; + item->impactN += impactN; + } else { + item->impactP += impactP/2.0; + item->impactN += impactN/2.0; + } + } + if ((replace == REPLACE_T || replace == NOTHING) && + !Cudd_IsConstant(cuddE(node))) { + item = (GlobalQueueItem *) cuddLevelQueueEnqueue(queue,Cudd_Regular(cuddE(node)), + cuddI(dd,Cudd_Regular(cuddE(node))->index)); + if (Cudd_IsComplement(cuddE(node))) { + if (replace == REPLACE_T) { + item->impactP += impactN; + item->impactN += impactP; + } else { + item->impactP += impactN/2.0; + item->impactN += impactP/2.0; + } + } else { + if (replace == REPLACE_T) { + item->impactP += impactP; + item->impactN += impactN; + } else { + item->impactP += impactP/2.0; + item->impactN += impactN/2.0; + } + } + } + if ((replace == REPLACE_TT || replace == REPLACE_TE) && + !Cudd_IsConstant(shared)) { + item = (GlobalQueueItem *) cuddLevelQueueEnqueue(queue,Cudd_Regular(shared), + cuddI(dd,Cudd_Regular(shared)->index)); + if (Cudd_IsComplement(shared)) { + if (replace == REPLACE_T) { + item->impactP += impactN; + item->impactN += impactP; + } else { + item->impactP += impactN/2.0; + item->impactN += impactP/2.0; + } + } else { + if (replace == REPLACE_T) { + item->impactP += impactP; + item->impactN += impactN; + } else { + item->impactP += impactP/2.0; + item->impactN += impactN/2.0; + } + } + } + } + + cuddLevelQueueQuit(queue); + cuddLevelQueueQuit(localQueue); + return(1); + +} /* end of BAmarkNodes */ + + +/**Function******************************************************************** + + Synopsis [Builds the subset BDD for cuddRemapUnderApprox.] + + Description [Builds the subset BDDfor cuddRemapUnderApprox. Based + on the info table, performs remapping or replacement at selected + nodes. Returns a pointer to the result if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [cuddRemapUnderApprox] + +******************************************************************************/ +static DdNode * +RAbuildSubset( + DdManager * dd /* DD manager */, + DdNode * node /* current node */, + ApproxInfo * info /* node info */) +{ + DdNode *Nt, *Ne, *N, *t, *e, *r; + NodeData *infoN; + + if (Cudd_IsConstant(node)) + return(node); + + N = Cudd_Regular(node); + + Nt = Cudd_NotCond(cuddT(N), Cudd_IsComplement(node)); + Ne = Cudd_NotCond(cuddE(N), Cudd_IsComplement(node)); + + if (st_lookup(info->table, (char *)N, (char **)&infoN)) { + if (N == node ) { + if (infoN->resultP != NULL) { + return(infoN->resultP); + } + } else { + if (infoN->resultN != NULL) { + return(infoN->resultN); + } + } + if (infoN->replace == REPLACE_T) { + r = RAbuildSubset(dd, Ne, info); + return(r); + } else if (infoN->replace == REPLACE_E) { + r = RAbuildSubset(dd, Nt, info); + return(r); + } else if (infoN->replace == REPLACE_N) { + return(info->zero); + } else if (infoN->replace == REPLACE_TT) { + DdNode *Ntt = Cudd_NotCond(cuddT(cuddT(N)), + Cudd_IsComplement(node)); + int index = cuddT(N)->index; + DdNode *e = info->zero; + DdNode *t = RAbuildSubset(dd, Ntt, info); + if (t == NULL) { + return(NULL); + } + cuddRef(t); + if (Cudd_IsComplement(t)) { + t = Cudd_Not(t); + e = Cudd_Not(e); + r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); + if (r == NULL) { + Cudd_RecursiveDeref(dd, t); + return(NULL); + } + r = Cudd_Not(r); + } else { + r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); + if (r == NULL) { + Cudd_RecursiveDeref(dd, t); + return(NULL); + } + } + cuddDeref(t); + return(r); + } else if (infoN->replace == REPLACE_TE) { + DdNode *Nte = Cudd_NotCond(cuddE(cuddT(N)), + Cudd_IsComplement(node)); + int index = cuddT(N)->index; + DdNode *t = info->one; + DdNode *e = RAbuildSubset(dd, Nte, info); + if (e == NULL) { + return(NULL); + } + cuddRef(e); + e = Cudd_Not(e); + r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); + if (r == NULL) { + Cudd_RecursiveDeref(dd, e); + return(NULL); + } + r =Cudd_Not(r); + cuddDeref(e); + return(r); + } + } else { + (void) fprintf(dd->err, + "Something is wrong, ought to be in info table\n"); + dd->errorCode = CUDD_INTERNAL_ERROR; + return(NULL); + } + + t = RAbuildSubset(dd, Nt, info); + if (t == NULL) { + return(NULL); + } + cuddRef(t); + + e = RAbuildSubset(dd, Ne, info); + if (e == NULL) { + Cudd_RecursiveDeref(dd,t); + return(NULL); + } + cuddRef(e); + + if (Cudd_IsComplement(t)) { + t = Cudd_Not(t); + e = Cudd_Not(e); + r = (t == e) ? t : cuddUniqueInter(dd, N->index, t, e); + if (r == NULL) { + Cudd_RecursiveDeref(dd, e); + Cudd_RecursiveDeref(dd, t); + return(NULL); + } + r = Cudd_Not(r); + } else { + r = (t == e) ? t : cuddUniqueInter(dd, N->index, t, e); + if (r == NULL) { + Cudd_RecursiveDeref(dd, e); + Cudd_RecursiveDeref(dd, t); + return(NULL); + } + } + cuddDeref(t); + cuddDeref(e); + + if (N == node) { + infoN->resultP = r; + } else { + infoN->resultN = r; + } + + return(r); + +} /* end of RAbuildSubset */ + + +/**Function******************************************************************** + + Synopsis [Finds don't care nodes.] + + Description [Finds don't care nodes by traversing f and b in parallel. + Returns the care status of the visited f node if successful; CARE_ERROR + otherwise.] + + SideEffects [None] + + SeeAlso [cuddBiasedUnderApprox] + +******************************************************************************/ +static int +BAapplyBias( + DdManager *dd, + DdNode *f, + DdNode *b, + ApproxInfo *info, + DdHashTable *cache) +{ + DdNode *one, *zero, *res; + DdNode *Ft, *Fe, *B, *Bt, *Be; + unsigned int topf, topb; + NodeData *infoF; + int careT, careE; + + one = DD_ONE(dd); + zero = Cudd_Not(one); + + if (!st_lookup(info->table, (char *) f, (char **)&infoF)) + return(CARE_ERROR); + if (f == one) return(TOTAL_CARE); + if (b == zero) return(infoF->care); + if (infoF->care == TOTAL_CARE) return(TOTAL_CARE); + + if ((f->ref != 1 || Cudd_Regular(b)->ref != 1) && + (res = cuddHashTableLookup2(cache,f,b)) != NULL) { + if (res->ref == 0) { + cache->manager->dead++; + cache->manager->constants.dead++; + } + return(infoF->care); + } + + topf = dd->perm[f->index]; + B = Cudd_Regular(b); + topb = cuddI(dd,B->index); + if (topf <= topb) { + Ft = cuddT(f); Fe = cuddE(f); + } else { + Ft = Fe = f; + } + if (topb <= topf) { + /* We know that b is not constant because f is not. */ + Bt = cuddT(B); Be = cuddE(B); + if (Cudd_IsComplement(b)) { + Bt = Cudd_Not(Bt); + Be = Cudd_Not(Be); + } + } else { + Bt = Be = b; + } + + careT = BAapplyBias(dd, Ft, Bt, info, cache); + if (careT == CARE_ERROR) + return(CARE_ERROR); + careE = BAapplyBias(dd, Cudd_Regular(Fe), Be, info, cache); + if (careE == CARE_ERROR) + return(CARE_ERROR); + if (careT == TOTAL_CARE && careE == TOTAL_CARE) { + infoF->care = TOTAL_CARE; + } else { + infoF->care = CARE; + } + + if (f->ref != 1 || Cudd_Regular(b)->ref != 1) { + ptrint fanout = (ptrint) f->ref * Cudd_Regular(b)->ref; + cuddSatDec(fanout); + if (!cuddHashTableInsert2(cache,f,b,one,fanout)) { + return(CARE_ERROR); + } + } + return(infoF->care); + +} /* end of BAapplyBias */ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddBddAbs.c b/abc_with_bb_support/src/bdd/cudd/cuddBddAbs.c new file mode 100644 index 000000000..270e326a1 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddBddAbs.c @@ -0,0 +1,689 @@ +/**CFile*********************************************************************** + + FileName [cuddBddAbs.c] + + PackageName [cudd] + + Synopsis [Quantification functions for BDDs.] + + Description [External procedures included in this module: +
    +
  • Cudd_bddExistAbstract() +
  • Cudd_bddXorExistAbstract() +
  • Cudd_bddUnivAbstract() +
  • Cudd_bddBooleanDiff() +
  • Cudd_bddVarIsDependent() +
+ Internal procedures included in this module: +
    +
  • cuddBddExistAbstractRecur() +
  • cuddBddXorExistAbstractRecur() +
  • cuddBddBooleanDiffRecur() +
+ Static procedures included in this module: +
    +
  • bddCheckPositiveCube() +
+ ] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddBddAbs.c,v 1.1.1.1 2003/02/24 22:23:51 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int bddCheckPositiveCube ARGS((DdManager *manager, DdNode *cube)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Existentially abstracts all the variables in cube from f.] + + Description [Existentially abstracts all the variables in cube from f. + Returns the abstracted BDD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddUnivAbstract Cudd_addExistAbstract] + +******************************************************************************/ +DdNode * +Cudd_bddExistAbstract( + DdManager * manager, + DdNode * f, + DdNode * cube) +{ + DdNode *res; + + if (bddCheckPositiveCube(manager, cube) == 0) { + (void) fprintf(manager->err, + "Error: Can only abstract positive cubes\n"); + manager->errorCode = CUDD_INVALID_ARG; + return(NULL); + } + + do { + manager->reordered = 0; + res = cuddBddExistAbstractRecur(manager, f, cube); + } while (manager->reordered == 1); + + return(res); + +} /* end of Cudd_bddExistAbstract */ + + +/**Function******************************************************************** + + Synopsis [Takes the exclusive OR of two BDDs and simultaneously abstracts the + variables in cube.] + + Description [Takes the exclusive OR of two BDDs and simultaneously abstracts + the variables in cube. The variables are existentially abstracted. Returns a + pointer to the result is successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddUnivAbstract Cudd_bddExistAbstract Cudd_bddAndAbstract] + +******************************************************************************/ +DdNode * +Cudd_bddXorExistAbstract( + DdManager * manager, + DdNode * f, + DdNode * g, + DdNode * cube) +{ + DdNode *res; + + if (bddCheckPositiveCube(manager, cube) == 0) { + (void) fprintf(manager->err, + "Error: Can only abstract positive cubes\n"); + manager->errorCode = CUDD_INVALID_ARG; + return(NULL); + } + + do { + manager->reordered = 0; + res = cuddBddXorExistAbstractRecur(manager, f, g, cube); + } while (manager->reordered == 1); + + return(res); + +} /* end of Cudd_bddXorExistAbstract */ + + +/**Function******************************************************************** + + Synopsis [Universally abstracts all the variables in cube from f.] + + Description [Universally abstracts all the variables in cube from f. + Returns the abstracted BDD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddExistAbstract Cudd_addUnivAbstract] + +******************************************************************************/ +DdNode * +Cudd_bddUnivAbstract( + DdManager * manager, + DdNode * f, + DdNode * cube) +{ + DdNode *res; + + if (bddCheckPositiveCube(manager, cube) == 0) { + (void) fprintf(manager->err, + "Error: Can only abstract positive cubes\n"); + manager->errorCode = CUDD_INVALID_ARG; + return(NULL); + } + + do { + manager->reordered = 0; + res = cuddBddExistAbstractRecur(manager, Cudd_Not(f), cube); + } while (manager->reordered == 1); + if (res != NULL) res = Cudd_Not(res); + + return(res); + +} /* end of Cudd_bddUnivAbstract */ + + +/**Function******************************************************************** + + Synopsis [Computes the boolean difference of f with respect to x.] + + Description [Computes the boolean difference of f with respect to the + variable with index x. Returns the BDD of the boolean difference if + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +Cudd_bddBooleanDiff( + DdManager * manager, + DdNode * f, + int x) +{ + DdNode *res, *var; + + /* If the variable is not currently in the manager, f cannot + ** depend on it. + */ + if (x >= manager->size) return(Cudd_Not(DD_ONE(manager))); + var = manager->vars[x]; + + do { + manager->reordered = 0; + res = cuddBddBooleanDiffRecur(manager, Cudd_Regular(f), var); + } while (manager->reordered == 1); + + return(res); + +} /* end of Cudd_bddBooleanDiff */ + + +/**Function******************************************************************** + + Synopsis [Checks whether a variable is dependent on others in a + function.] + + Description [Checks whether a variable is dependent on others in a + function. Returns 1 if the variable is dependent; 0 otherwise. No + new nodes are created.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +Cudd_bddVarIsDependent( + DdManager *dd, /* manager */ + DdNode *f, /* function */ + DdNode *var /* variable */) +{ + DdNode *F, *res, *zero, *ft, *fe; + unsigned topf, level; + DdNode *(*cacheOp)(DdManager *, DdNode *, DdNode *); + int retval; + + zero = Cudd_Not(DD_ONE(dd)); + if (Cudd_IsConstant(f)) return(f == zero); + + /* From now on f is not constant. */ + F = Cudd_Regular(f); + topf = (unsigned) dd->perm[F->index]; + level = (unsigned) dd->perm[var->index]; + + /* Check terminal case. If topf > index of var, f does not depend on var. + ** Therefore, var is not dependent in f. */ + if (topf > level) { + return(0); + } + + cacheOp = + (DdNode *(*)(DdManager *, DdNode *, DdNode *)) Cudd_bddVarIsDependent; + res = cuddCacheLookup2(dd,cacheOp,f,var); + if (res != NULL) { + return(res != zero); + } + + /* Compute cofactors. */ + ft = Cudd_NotCond(cuddT(F), f != F); + fe = Cudd_NotCond(cuddE(F), f != F); + + if (topf == level) { + retval = Cudd_bddLeq(dd,ft,Cudd_Not(fe)); + } else { + retval = Cudd_bddVarIsDependent(dd,ft,var) && + Cudd_bddVarIsDependent(dd,fe,var); + } + + cuddCacheInsert2(dd,cacheOp,f,var,Cudd_NotCond(zero,retval)); + + return(retval); + +} /* Cudd_bddVarIsDependent */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive steps of Cudd_bddExistAbstract.] + + Description [Performs the recursive steps of Cudd_bddExistAbstract. + Returns the BDD obtained by abstracting the variables + of cube from f if successful; NULL otherwise. It is also used by + Cudd_bddUnivAbstract.] + + SideEffects [None] + + SeeAlso [Cudd_bddExistAbstract Cudd_bddUnivAbstract] + +******************************************************************************/ +DdNode * +cuddBddExistAbstractRecur( + DdManager * manager, + DdNode * f, + DdNode * cube) +{ + DdNode *F, *T, *E, *res, *res1, *res2, *one; + + statLine(manager); + one = DD_ONE(manager); + F = Cudd_Regular(f); + + /* Cube is guaranteed to be a cube at this point. */ + if (cube == one || F == one) { + return(f); + } + /* From now on, f and cube are non-constant. */ + + /* Abstract a variable that does not appear in f. */ + while (manager->perm[F->index] > manager->perm[cube->index]) { + cube = cuddT(cube); + if (cube == one) return(f); + } + + /* Check the cache. */ + if (F->ref != 1 && (res = cuddCacheLookup2(manager, Cudd_bddExistAbstract, f, cube)) != NULL) { + return(res); + } + + /* Compute the cofactors of f. */ + T = cuddT(F); E = cuddE(F); + if (f != F) { + T = Cudd_Not(T); E = Cudd_Not(E); + } + + /* If the two indices are the same, so are their levels. */ + if (F->index == cube->index) { + if (T == one || E == one || T == Cudd_Not(E)) { + return(one); + } + res1 = cuddBddExistAbstractRecur(manager, T, cuddT(cube)); + if (res1 == NULL) return(NULL); + if (res1 == one) { + if (F->ref != 1) + cuddCacheInsert2(manager, Cudd_bddExistAbstract, f, cube, one); + return(one); + } + cuddRef(res1); + res2 = cuddBddExistAbstractRecur(manager, E, cuddT(cube)); + if (res2 == NULL) { + Cudd_IterDerefBdd(manager,res1); + return(NULL); + } + cuddRef(res2); + res = cuddBddAndRecur(manager, Cudd_Not(res1), Cudd_Not(res2)); + if (res == NULL) { + Cudd_IterDerefBdd(manager, res1); + Cudd_IterDerefBdd(manager, res2); + return(NULL); + } + res = Cudd_Not(res); + cuddRef(res); + Cudd_IterDerefBdd(manager, res1); + Cudd_IterDerefBdd(manager, res2); + if (F->ref != 1) + cuddCacheInsert2(manager, Cudd_bddExistAbstract, f, cube, res); + cuddDeref(res); + return(res); + } else { /* if (cuddI(manager,F->index) < cuddI(manager,cube->index)) */ + res1 = cuddBddExistAbstractRecur(manager, T, cube); + if (res1 == NULL) return(NULL); + cuddRef(res1); + res2 = cuddBddExistAbstractRecur(manager, E, cube); + if (res2 == NULL) { + Cudd_IterDerefBdd(manager, res1); + return(NULL); + } + cuddRef(res2); + /* ITE takes care of possible complementation of res1 and of the + ** case in which res1 == res2. */ + res = cuddBddIteRecur(manager, manager->vars[F->index], res1, res2); + if (res == NULL) { + Cudd_IterDerefBdd(manager, res1); + Cudd_IterDerefBdd(manager, res2); + return(NULL); + } + cuddDeref(res1); + cuddDeref(res2); + if (F->ref != 1) + cuddCacheInsert2(manager, Cudd_bddExistAbstract, f, cube, res); + return(res); + } + +} /* end of cuddBddExistAbstractRecur */ + + +/**Function******************************************************************** + + Synopsis [Takes the exclusive OR of two BDDs and simultaneously abstracts the + variables in cube.] + + Description [Takes the exclusive OR of two BDDs and simultaneously abstracts + the variables in cube. The variables are existentially abstracted. Returns a + pointer to the result is successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddAndAbstract] + +******************************************************************************/ +DdNode * +cuddBddXorExistAbstractRecur( + DdManager * manager, + DdNode * f, + DdNode * g, + DdNode * cube) +{ + DdNode *F, *fv, *fnv, *G, *gv, *gnv; + DdNode *one, *zero, *r, *t, *e, *Cube; + unsigned int topf, topg, topcube, top, index; + + statLine(manager); + one = DD_ONE(manager); + zero = Cudd_Not(one); + + /* Terminal cases. */ + if (f == g) { + return(zero); + } + if (f == Cudd_Not(g)) { + return(one); + } + if (cube == one) { + return(cuddBddXorRecur(manager, f, g)); + } + if (f == one) { + return(cuddBddExistAbstractRecur(manager, Cudd_Not(g), cube)); + } + if (g == one) { + return(cuddBddExistAbstractRecur(manager, Cudd_Not(f), cube)); + } + if (f == zero) { + return(cuddBddExistAbstractRecur(manager, g, cube)); + } + if (g == zero) { + return(cuddBddExistAbstractRecur(manager, f, cube)); + } + + /* At this point f, g, and cube are not constant. */ + + if (f > g) { /* Try to increase cache efficiency. */ + DdNode *tmp = f; + f = g; + g = tmp; + } + + /* Check cache. */ + r = cuddCacheLookup(manager, DD_BDD_XOR_EXIST_ABSTRACT_TAG, f, g, cube); + if (r != NULL) { + return(r); + } + + /* Here we can skip the use of cuddI, because the operands are known + ** to be non-constant. + */ + F = Cudd_Regular(f); + topf = manager->perm[F->index]; + G = Cudd_Regular(g); + topg = manager->perm[G->index]; + top = ddMin(topf, topg); + topcube = manager->perm[cube->index]; + + if (topcube < top) { + return(cuddBddXorExistAbstractRecur(manager, f, g, cuddT(cube))); + } + /* Now, topcube >= top. */ + + if (topf == top) { + index = F->index; + fv = cuddT(F); + fnv = cuddE(F); + if (Cudd_IsComplement(f)) { + fv = Cudd_Not(fv); + fnv = Cudd_Not(fnv); + } + } else { + index = G->index; + fv = fnv = f; + } + + if (topg == top) { + gv = cuddT(G); + gnv = cuddE(G); + if (Cudd_IsComplement(g)) { + gv = Cudd_Not(gv); + gnv = Cudd_Not(gnv); + } + } else { + gv = gnv = g; + } + + if (topcube == top) { + Cube = cuddT(cube); + } else { + Cube = cube; + } + + t = cuddBddXorExistAbstractRecur(manager, fv, gv, Cube); + if (t == NULL) return(NULL); + + /* Special case: 1 OR anything = 1. Hence, no need to compute + ** the else branch if t is 1. + */ + if (t == one && topcube == top) { + cuddCacheInsert(manager, DD_BDD_XOR_EXIST_ABSTRACT_TAG, f, g, cube, one); + return(one); + } + cuddRef(t); + + e = cuddBddXorExistAbstractRecur(manager, fnv, gnv, Cube); + if (e == NULL) { + Cudd_IterDerefBdd(manager, t); + return(NULL); + } + cuddRef(e); + + if (topcube == top) { /* abstract */ + r = cuddBddAndRecur(manager, Cudd_Not(t), Cudd_Not(e)); + if (r == NULL) { + Cudd_IterDerefBdd(manager, t); + Cudd_IterDerefBdd(manager, e); + return(NULL); + } + r = Cudd_Not(r); + cuddRef(r); + Cudd_IterDerefBdd(manager, t); + Cudd_IterDerefBdd(manager, e); + cuddDeref(r); + } else if (t == e) { + r = t; + cuddDeref(t); + cuddDeref(e); + } else { + if (Cudd_IsComplement(t)) { + r = cuddUniqueInter(manager,(int)index,Cudd_Not(t),Cudd_Not(e)); + if (r == NULL) { + Cudd_IterDerefBdd(manager, t); + Cudd_IterDerefBdd(manager, e); + return(NULL); + } + r = Cudd_Not(r); + } else { + r = cuddUniqueInter(manager,(int)index,t,e); + if (r == NULL) { + Cudd_IterDerefBdd(manager, t); + Cudd_IterDerefBdd(manager, e); + return(NULL); + } + } + cuddDeref(e); + cuddDeref(t); + } + cuddCacheInsert(manager, DD_BDD_XOR_EXIST_ABSTRACT_TAG, f, g, cube, r); + return (r); + +} /* end of cuddBddXorExistAbstractRecur */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive steps of Cudd_bddBoleanDiff.] + + Description [Performs the recursive steps of Cudd_bddBoleanDiff. + Returns the BDD obtained by XORing the cofactors of f with respect to + var if successful; NULL otherwise. Exploits the fact that dF/dx = + dF'/dx.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddBddBooleanDiffRecur( + DdManager * manager, + DdNode * f, + DdNode * var) +{ + DdNode *T, *E, *res, *res1, *res2; + + statLine(manager); + if (cuddI(manager,f->index) > manager->perm[var->index]) { + /* f does not depend on var. */ + return(Cudd_Not(DD_ONE(manager))); + } + + /* From now on, f is non-constant. */ + + /* If the two indices are the same, so are their levels. */ + if (f->index == var->index) { + res = cuddBddXorRecur(manager, cuddT(f), cuddE(f)); + return(res); + } + + /* From now on, cuddI(manager,f->index) < cuddI(manager,cube->index). */ + + /* Check the cache. */ + res = cuddCacheLookup2(manager, cuddBddBooleanDiffRecur, f, var); + if (res != NULL) { + return(res); + } + + /* Compute the cofactors of f. */ + T = cuddT(f); E = cuddE(f); + + res1 = cuddBddBooleanDiffRecur(manager, T, var); + if (res1 == NULL) return(NULL); + cuddRef(res1); + res2 = cuddBddBooleanDiffRecur(manager, Cudd_Regular(E), var); + if (res2 == NULL) { + Cudd_IterDerefBdd(manager, res1); + return(NULL); + } + cuddRef(res2); + /* ITE takes care of possible complementation of res1 and of the + ** case in which res1 == res2. */ + res = cuddBddIteRecur(manager, manager->vars[f->index], res1, res2); + if (res == NULL) { + Cudd_IterDerefBdd(manager, res1); + Cudd_IterDerefBdd(manager, res2); + return(NULL); + } + cuddDeref(res1); + cuddDeref(res2); + cuddCacheInsert2(manager, cuddBddBooleanDiffRecur, f, var, res); + return(res); + +} /* end of cuddBddBooleanDiffRecur */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Checks whether cube is an BDD representing the product of + positive literals.] + + Description [Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +bddCheckPositiveCube( + DdManager * manager, + DdNode * cube) +{ + if (Cudd_IsComplement(cube)) return(0); + if (cube == DD_ONE(manager)) return(1); + if (cuddIsConstant(cube)) return(0); + if (cuddE(cube) == Cudd_Not(DD_ONE(manager))) { + return(bddCheckPositiveCube(manager, cuddT(cube))); + } + return(0); + +} /* end of bddCheckPositiveCube */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddBddCorr.c b/abc_with_bb_support/src/bdd/cudd/cuddBddCorr.c new file mode 100644 index 000000000..856b34ee2 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddBddCorr.c @@ -0,0 +1,481 @@ +/**CFile*********************************************************************** + + FileName [cuddBddCorr.c] + + PackageName [cudd] + + Synopsis [Correlation between BDDs.] + + Description [External procedures included in this module: +
    +
  • Cudd_bddCorrelation() +
  • Cudd_bddCorrelationWeights() +
+ Static procedures included in this module: +
    +
  • bddCorrelationAux() +
  • bddCorrelationWeightsAux() +
  • CorrelCompare() +
  • CorrelHash() +
  • CorrelCleanUp() +
+ ] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +typedef struct hashEntry { + DdNode *f; + DdNode *g; +} HashEntry; + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddBddCorr.c,v 1.1.1.1 2003/02/24 22:23:51 wjiang Exp $"; +#endif + +#ifdef CORREL_STATS +static int num_calls; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static double bddCorrelationAux ARGS((DdManager *dd, DdNode *f, DdNode *g, st_table *table)); +static double bddCorrelationWeightsAux ARGS((DdManager *dd, DdNode *f, DdNode *g, double *prob, st_table *table)); +static int CorrelCompare ARGS((const char *key1, const char *key2)); +static int CorrelHash ARGS((char *key, int modulus)); +static enum st_retval CorrelCleanUp ARGS((char *key, char *value, char *arg)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Computes the correlation of f and g.] + + Description [Computes the correlation of f and g. If f == g, their + correlation is 1. If f == g', their correlation is 0. Returns the + fraction of minterms in the ON-set of the EXNOR of f and g. If it + runs out of memory, returns (double)CUDD_OUT_OF_MEM.] + + SideEffects [None] + + SeeAlso [Cudd_bddCorrelationWeights] + +******************************************************************************/ +double +Cudd_bddCorrelation( + DdManager * manager, + DdNode * f, + DdNode * g) +{ + + st_table *table; + double correlation; + +#ifdef CORREL_STATS + num_calls = 0; +#endif + + table = st_init_table(CorrelCompare,CorrelHash); + if (table == NULL) return((double)CUDD_OUT_OF_MEM); + correlation = bddCorrelationAux(manager,f,g,table); + st_foreach(table, CorrelCleanUp, NIL(char)); + st_free_table(table); + return(correlation); + +} /* end of Cudd_bddCorrelation */ + + +/**Function******************************************************************** + + Synopsis [Computes the correlation of f and g for given input + probabilities.] + + Description [Computes the correlation of f and g for given input + probabilities. On input, prob\[i\] is supposed to contain the + probability of the i-th input variable to be 1. + If f == g, their correlation is 1. If f == g', their + correlation is 0. Returns the probability that f and g have the same + value. If it runs out of memory, returns (double)CUDD_OUT_OF_MEM. The + correlation of f and the constant one gives the probability of f.] + + SideEffects [None] + + SeeAlso [Cudd_bddCorrelation] + +******************************************************************************/ +double +Cudd_bddCorrelationWeights( + DdManager * manager, + DdNode * f, + DdNode * g, + double * prob) +{ + + st_table *table; + double correlation; + +#ifdef CORREL_STATS + num_calls = 0; +#endif + + table = st_init_table(CorrelCompare,CorrelHash); + if (table == NULL) return((double)CUDD_OUT_OF_MEM); + correlation = bddCorrelationWeightsAux(manager,f,g,prob,table); + st_foreach(table, CorrelCleanUp, NIL(char)); + st_free_table(table); + return(correlation); + +} /* end of Cudd_bddCorrelationWeights */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_bddCorrelation.] + + Description [Performs the recursive step of Cudd_bddCorrelation. + Returns the fraction of minterms in the ON-set of the EXNOR of f and + g.] + + SideEffects [None] + + SeeAlso [bddCorrelationWeightsAux] + +******************************************************************************/ +static double +bddCorrelationAux( + DdManager * dd, + DdNode * f, + DdNode * g, + st_table * table) +{ + DdNode *Fv, *Fnv, *G, *Gv, *Gnv; + double min, *pmin, min1, min2, *dummy; + HashEntry *entry; + unsigned int topF, topG; + + statLine(dd); +#ifdef CORREL_STATS + num_calls++; +#endif + + /* Terminal cases: only work for BDDs. */ + if (f == g) return(1.0); + if (f == Cudd_Not(g)) return(0.0); + + /* Standardize call using the following properties: + ** (f EXNOR g) = (g EXNOR f) + ** (f' EXNOR g') = (f EXNOR g). + */ + if (f > g) { + DdNode *tmp = f; + f = g; g = tmp; + } + if (Cudd_IsComplement(f)) { + f = Cudd_Not(f); + g = Cudd_Not(g); + } + /* From now on, f is regular. */ + + entry = ALLOC(HashEntry,1); + if (entry == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(CUDD_OUT_OF_MEM); + } + entry->f = f; entry->g = g; + + /* We do not use the fact that + ** correlation(f,g') = 1 - correlation(f,g) + ** to minimize the risk of cancellation. + */ + if (st_lookup(table, (char *)entry, (char **)&dummy)) { + min = *dummy; + FREE(entry); + return(min); + } + + G = Cudd_Regular(g); + topF = cuddI(dd,f->index); topG = cuddI(dd,G->index); + if (topF <= topG) { Fv = cuddT(f); Fnv = cuddE(f); } else { Fv = Fnv = f; } + if (topG <= topF) { Gv = cuddT(G); Gnv = cuddE(G); } else { Gv = Gnv = G; } + + if (g != G) { + Gv = Cudd_Not(Gv); + Gnv = Cudd_Not(Gnv); + } + + min1 = bddCorrelationAux(dd, Fv, Gv, table) / 2.0; + if (min1 == (double)CUDD_OUT_OF_MEM) { + FREE(entry); + return(CUDD_OUT_OF_MEM); + } + min2 = bddCorrelationAux(dd, Fnv, Gnv, table) / 2.0; + if (min2 == (double)CUDD_OUT_OF_MEM) { + FREE(entry); + return(CUDD_OUT_OF_MEM); + } + min = (min1+min2); + + pmin = ALLOC(double,1); + if (pmin == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return((double)CUDD_OUT_OF_MEM); + } + *pmin = min; + + if (st_insert(table,(char *)entry, (char *)pmin) == ST_OUT_OF_MEM) { + FREE(entry); + FREE(pmin); + return((double)CUDD_OUT_OF_MEM); + } + return(min); + +} /* end of bddCorrelationAux */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_bddCorrelationWeigths.] + + Description [] + + SideEffects [None] + + SeeAlso [bddCorrelationAux] + +******************************************************************************/ +static double +bddCorrelationWeightsAux( + DdManager * dd, + DdNode * f, + DdNode * g, + double * prob, + st_table * table) +{ + DdNode *Fv, *Fnv, *G, *Gv, *Gnv; + double min, *pmin, min1, min2, *dummy; + HashEntry *entry; + int topF, topG, index; + + statLine(dd); +#ifdef CORREL_STATS + num_calls++; +#endif + + /* Terminal cases: only work for BDDs. */ + if (f == g) return(1.0); + if (f == Cudd_Not(g)) return(0.0); + + /* Standardize call using the following properties: + ** (f EXNOR g) = (g EXNOR f) + ** (f' EXNOR g') = (f EXNOR g). + */ + if (f > g) { + DdNode *tmp = f; + f = g; g = tmp; + } + if (Cudd_IsComplement(f)) { + f = Cudd_Not(f); + g = Cudd_Not(g); + } + /* From now on, f is regular. */ + + entry = ALLOC(HashEntry,1); + if (entry == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return((double)CUDD_OUT_OF_MEM); + } + entry->f = f; entry->g = g; + + /* We do not use the fact that + ** correlation(f,g') = 1 - correlation(f,g) + ** to minimize the risk of cancellation. + */ + if (st_lookup(table, (char *)entry, (char **)&dummy)) { + min = *dummy; + FREE(entry); + return(min); + } + + G = Cudd_Regular(g); + topF = cuddI(dd,f->index); topG = cuddI(dd,G->index); + if (topF <= topG) { + Fv = cuddT(f); Fnv = cuddE(f); + index = f->index; + } else { + Fv = Fnv = f; + index = G->index; + } + if (topG <= topF) { Gv = cuddT(G); Gnv = cuddE(G); } else { Gv = Gnv = G; } + + if (g != G) { + Gv = Cudd_Not(Gv); + Gnv = Cudd_Not(Gnv); + } + + min1 = bddCorrelationWeightsAux(dd, Fv, Gv, prob, table) * prob[index]; + if (min1 == (double)CUDD_OUT_OF_MEM) { + FREE(entry); + return((double)CUDD_OUT_OF_MEM); + } + min2 = bddCorrelationWeightsAux(dd, Fnv, Gnv, prob, table) * (1.0 - prob[index]); + if (min2 == (double)CUDD_OUT_OF_MEM) { + FREE(entry); + return((double)CUDD_OUT_OF_MEM); + } + min = (min1+min2); + + pmin = ALLOC(double,1); + if (pmin == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return((double)CUDD_OUT_OF_MEM); + } + *pmin = min; + + if (st_insert(table,(char *)entry, (char *)pmin) == ST_OUT_OF_MEM) { + FREE(entry); + FREE(pmin); + return((double)CUDD_OUT_OF_MEM); + } + return(min); + +} /* end of bddCorrelationWeightsAux */ + + +/**Function******************************************************************** + + Synopsis [Compares two hash table entries.] + + Description [Compares two hash table entries. Returns 0 if they are + identical; 1 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +CorrelCompare( + const char * key1, + const char * key2) +{ + HashEntry *entry1; + HashEntry *entry2; + + entry1 = (HashEntry *) key1; + entry2 = (HashEntry *) key2; + if (entry1->f != entry2->f || entry1->g != entry2->g) return(1); + + return(0); + +} /* end of CorrelCompare */ + + +/**Function******************************************************************** + + Synopsis [Hashes a hash table entry.] + + Description [Hashes a hash table entry. It is patterned after + st_strhash. Returns a value between 0 and modulus.] + + SideEffects [None] + +******************************************************************************/ +static int +CorrelHash( + char * key, + int modulus) +{ + HashEntry *entry; + int val = 0; + + entry = (HashEntry *) key; +#if SIZEOF_VOID_P == 8 && SIZEOF_INT == 4 + val = ((int) ((long)entry->f))*997 + ((int) ((long)entry->g)); +#else + val = ((int) entry->f)*997 + ((int) entry->g); +#endif + + return ((val < 0) ? -val : val) % modulus; + +} /* end of CorrelHash */ + + +/**Function******************************************************************** + + Synopsis [Frees memory associated with hash table.] + + Description [Frees memory associated with hash table. Returns + ST_CONTINUE.] + + SideEffects [None] + +******************************************************************************/ +static enum st_retval +CorrelCleanUp( + char * key, + char * value, + char * arg) +{ + double *d; + HashEntry *entry; + + entry = (HashEntry *) key; + FREE(entry); + d = (double *)value; + FREE(d); + return ST_CONTINUE; + +} /* end of CorrelCleanUp */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddBddIte.c b/abc_with_bb_support/src/bdd/cudd/cuddBddIte.c new file mode 100644 index 000000000..b16158486 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddBddIte.c @@ -0,0 +1,1254 @@ +/**CFile*********************************************************************** + + FileName [cuddBddIte.c] + + PackageName [cudd] + + Synopsis [BDD ITE function and satellites.] + + Description [External procedures included in this module: +
    +
  • Cudd_bddIte() +
  • Cudd_bddIteConstant() +
  • Cudd_bddIntersect() +
  • Cudd_bddAnd() +
  • Cudd_bddOr() +
  • Cudd_bddNand() +
  • Cudd_bddNor() +
  • Cudd_bddXor() +
  • Cudd_bddXnor() +
  • Cudd_bddLeq() +
+ Internal procedures included in this module: +
    +
  • cuddBddIteRecur() +
  • cuddBddIntersectRecur() +
  • cuddBddAndRecur() +
  • cuddBddXorRecur() +
+ Static procedures included in this module: +
    +
  • bddVarToConst() +
  • bddVarToCanonical() +
  • bddVarToCanonicalSimple() +
] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddBddIte.c,v 1.1.1.1 2003/02/24 22:23:51 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static void bddVarToConst ARGS((DdNode *f, DdNode **gp, DdNode **hp, DdNode *one)); +static int bddVarToCanonical ARGS((DdManager *dd, DdNode **fp, DdNode **gp, DdNode **hp, unsigned int *topfp, unsigned int *topgp, unsigned int *tophp)); +static int bddVarToCanonicalSimple ARGS((DdManager *dd, DdNode **fp, DdNode **gp, DdNode **hp, unsigned int *topfp, unsigned int *topgp, unsigned int *tophp)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Implements ITE(f,g,h).] + + Description [Implements ITE(f,g,h). Returns a pointer to the + resulting BDD if successful; NULL if the intermediate result blows + up.] + + SideEffects [None] + + SeeAlso [Cudd_addIte Cudd_bddIteConstant Cudd_bddIntersect] + +******************************************************************************/ +DdNode * +Cudd_bddIte( + DdManager * dd, + DdNode * f, + DdNode * g, + DdNode * h) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddBddIteRecur(dd,f,g,h); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_bddIte */ + + +/**Function******************************************************************** + + Synopsis [Implements ITEconstant(f,g,h).] + + Description [Implements ITEconstant(f,g,h). Returns a pointer to the + resulting BDD (which may or may not be constant) or DD_NON_CONSTANT. + No new nodes are created.] + + SideEffects [None] + + SeeAlso [Cudd_bddIte Cudd_bddIntersect Cudd_bddLeq Cudd_addIteConstant] + +******************************************************************************/ +DdNode * +Cudd_bddIteConstant( + DdManager * dd, + DdNode * f, + DdNode * g, + DdNode * h) +{ + DdNode *r, *Fv, *Fnv, *Gv, *Gnv, *H, *Hv, *Hnv, *t, *e; + DdNode *one = DD_ONE(dd); + DdNode *zero = Cudd_Not(one); + int comple; + unsigned int topf, topg, toph, v; + + statLine(dd); + /* Trivial cases. */ + if (f == one) /* ITE(1,G,H) => G */ + return(g); + + if (f == zero) /* ITE(0,G,H) => H */ + return(h); + + /* f now not a constant. */ + bddVarToConst(f, &g, &h, one); /* possibly convert g or h */ + /* to constants */ + + if (g == h) /* ITE(F,G,G) => G */ + return(g); + + if (Cudd_IsConstant(g) && Cudd_IsConstant(h)) + return(DD_NON_CONSTANT); /* ITE(F,1,0) or ITE(F,0,1) */ + /* => DD_NON_CONSTANT */ + + if (g == Cudd_Not(h)) + return(DD_NON_CONSTANT); /* ITE(F,G,G') => DD_NON_CONSTANT */ + /* if F != G and F != G' */ + + comple = bddVarToCanonical(dd, &f, &g, &h, &topf, &topg, &toph); + + /* Cache lookup. */ + r = cuddConstantLookup(dd, DD_BDD_ITE_CONSTANT_TAG, f, g, h); + if (r != NULL) { + return(Cudd_NotCond(r,comple && r != DD_NON_CONSTANT)); + } + + v = ddMin(topg, toph); + + /* ITE(F,G,H) = (v,G,H) (non constant) if F = (v,1,0), v < top(G,H). */ + if (topf < v && cuddT(f) == one && cuddE(f) == zero) { + return(DD_NON_CONSTANT); + } + + /* Compute cofactors. */ + if (topf <= v) { + v = ddMin(topf, v); /* v = top_var(F,G,H) */ + Fv = cuddT(f); Fnv = cuddE(f); + } else { + Fv = Fnv = f; + } + + if (topg == v) { + Gv = cuddT(g); Gnv = cuddE(g); + } else { + Gv = Gnv = g; + } + + if (toph == v) { + H = Cudd_Regular(h); + Hv = cuddT(H); Hnv = cuddE(H); + if (Cudd_IsComplement(h)) { + Hv = Cudd_Not(Hv); + Hnv = Cudd_Not(Hnv); + } + } else { + Hv = Hnv = h; + } + + /* Recursion. */ + t = Cudd_bddIteConstant(dd, Fv, Gv, Hv); + if (t == DD_NON_CONSTANT || !Cudd_IsConstant(t)) { + cuddCacheInsert(dd, DD_BDD_ITE_CONSTANT_TAG, f, g, h, DD_NON_CONSTANT); + return(DD_NON_CONSTANT); + } + e = Cudd_bddIteConstant(dd, Fnv, Gnv, Hnv); + if (e == DD_NON_CONSTANT || !Cudd_IsConstant(e) || t != e) { + cuddCacheInsert(dd, DD_BDD_ITE_CONSTANT_TAG, f, g, h, DD_NON_CONSTANT); + return(DD_NON_CONSTANT); + } + cuddCacheInsert(dd, DD_BDD_ITE_CONSTANT_TAG, f, g, h, t); + return(Cudd_NotCond(t,comple)); + +} /* end of Cudd_bddIteConstant */ + + +/**Function******************************************************************** + + Synopsis [Returns a function included in the intersection of f and g.] + + Description [Computes a function included in the intersection of f and + g. (That is, a witness that the intersection is not empty.) + Cudd_bddIntersect tries to build as few new nodes as possible. If the + only result of interest is whether f and g intersect, + Cudd_bddLeq should be used instead.] + + SideEffects [None] + + SeeAlso [Cudd_bddLeq Cudd_bddIteConstant] + +******************************************************************************/ +DdNode * +Cudd_bddIntersect( + DdManager * dd /* manager */, + DdNode * f /* first operand */, + DdNode * g /* second operand */) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddBddIntersectRecur(dd,f,g); + } while (dd->reordered == 1); + + return(res); + +} /* end of Cudd_bddIntersect */ + + +/**Function******************************************************************** + + Synopsis [Computes the conjunction of two BDDs f and g.] + + Description [Computes the conjunction of two BDDs f and g. Returns a + pointer to the resulting BDD if successful; NULL if the intermediate + result blows up.] + + SideEffects [None] + + SeeAlso [Cudd_bddIte Cudd_addApply Cudd_bddAndAbstract Cudd_bddIntersect + Cudd_bddOr Cudd_bddNand Cudd_bddNor Cudd_bddXor Cudd_bddXnor] + +******************************************************************************/ +DdNode * +Cudd_bddAnd( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddBddAndRecur(dd,f,g); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_bddAnd */ + + +/**Function******************************************************************** + + Synopsis [Computes the disjunction of two BDDs f and g.] + + Description [Computes the disjunction of two BDDs f and g. Returns a + pointer to the resulting BDD if successful; NULL if the intermediate + result blows up.] + + SideEffects [None] + + SeeAlso [Cudd_bddIte Cudd_addApply Cudd_bddAnd Cudd_bddNand Cudd_bddNor + Cudd_bddXor Cudd_bddXnor] + +******************************************************************************/ +DdNode * +Cudd_bddOr( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddBddAndRecur(dd,Cudd_Not(f),Cudd_Not(g)); + } while (dd->reordered == 1); + res = Cudd_NotCond(res,res != NULL); + return(res); + +} /* end of Cudd_bddOr */ + + +/**Function******************************************************************** + + Synopsis [Computes the NAND of two BDDs f and g.] + + Description [Computes the NAND of two BDDs f and g. Returns a + pointer to the resulting BDD if successful; NULL if the intermediate + result blows up.] + + SideEffects [None] + + SeeAlso [Cudd_bddIte Cudd_addApply Cudd_bddAnd Cudd_bddOr Cudd_bddNor + Cudd_bddXor Cudd_bddXnor] + +******************************************************************************/ +DdNode * +Cudd_bddNand( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddBddAndRecur(dd,f,g); + } while (dd->reordered == 1); + res = Cudd_NotCond(res,res != NULL); + return(res); + +} /* end of Cudd_bddNand */ + + +/**Function******************************************************************** + + Synopsis [Computes the NOR of two BDDs f and g.] + + Description [Computes the NOR of two BDDs f and g. Returns a + pointer to the resulting BDD if successful; NULL if the intermediate + result blows up.] + + SideEffects [None] + + SeeAlso [Cudd_bddIte Cudd_addApply Cudd_bddAnd Cudd_bddOr Cudd_bddNand + Cudd_bddXor Cudd_bddXnor] + +******************************************************************************/ +DdNode * +Cudd_bddNor( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddBddAndRecur(dd,Cudd_Not(f),Cudd_Not(g)); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_bddNor */ + + +/**Function******************************************************************** + + Synopsis [Computes the exclusive OR of two BDDs f and g.] + + Description [Computes the exclusive OR of two BDDs f and g. Returns a + pointer to the resulting BDD if successful; NULL if the intermediate + result blows up.] + + SideEffects [None] + + SeeAlso [Cudd_bddIte Cudd_addApply Cudd_bddAnd Cudd_bddOr + Cudd_bddNand Cudd_bddNor Cudd_bddXnor] + +******************************************************************************/ +DdNode * +Cudd_bddXor( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddBddXorRecur(dd,f,g); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_bddXor */ + + +/**Function******************************************************************** + + Synopsis [Computes the exclusive NOR of two BDDs f and g.] + + Description [Computes the exclusive NOR of two BDDs f and g. Returns a + pointer to the resulting BDD if successful; NULL if the intermediate + result blows up.] + + SideEffects [None] + + SeeAlso [Cudd_bddIte Cudd_addApply Cudd_bddAnd Cudd_bddOr + Cudd_bddNand Cudd_bddNor Cudd_bddXor] + +******************************************************************************/ +DdNode * +Cudd_bddXnor( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddBddXorRecur(dd,f,Cudd_Not(g)); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_bddXnor */ + + +/**Function******************************************************************** + + Synopsis [Determines whether f is less than or equal to g.] + + Description [Returns 1 if f is less than or equal to g; 0 otherwise. + No new nodes are created.] + + SideEffects [None] + + SeeAlso [Cudd_bddIteConstant Cudd_addEvalConst] + +******************************************************************************/ +int +Cudd_bddLeq( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *one, *zero, *tmp, *F, *fv, *fvn, *gv, *gvn; + unsigned int topf, topg, res; + + statLine(dd); + /* Terminal cases and normalization. */ + if (f == g) return(1); + + if (Cudd_IsComplement(g)) { + /* Special case: if f is regular and g is complemented, + ** f(1,...,1) = 1 > 0 = g(1,...,1). + */ + if (!Cudd_IsComplement(f)) return(0); + /* Both are complemented: Swap and complement because + ** f <= g <=> g' <= f' and we want the second argument to be regular. + */ + tmp = g; + g = Cudd_Not(f); + f = Cudd_Not(tmp); + } else if (Cudd_IsComplement(f) && g < f) { + tmp = g; + g = Cudd_Not(f); + f = Cudd_Not(tmp); + } + + /* Now g is regular and, if f is not regular, f < g. */ + one = DD_ONE(dd); + if (g == one) return(1); /* no need to test against zero */ + if (f == one) return(0); /* since at this point g != one */ + if (Cudd_Not(f) == g) return(0); /* because neither is constant */ + zero = Cudd_Not(one); + if (f == zero) return(1); + + /* Here neither f nor g is constant. */ + + /* Check cache. */ + tmp = cuddCacheLookup2(dd,(DdNode * (*)(DdManager *, DdNode *, + DdNode *))Cudd_bddLeq,f,g); + if (tmp != NULL) { + return(tmp == one); + } + + /* Compute cofactors. */ + F = Cudd_Regular(f); + topf = dd->perm[F->index]; + topg = dd->perm[g->index]; + if (topf <= topg) { + fv = cuddT(F); fvn = cuddE(F); + if (f != F) { + fv = Cudd_Not(fv); + fvn = Cudd_Not(fvn); + } + } else { + fv = fvn = f; + } + if (topg <= topf) { + gv = cuddT(g); gvn = cuddE(g); + } else { + gv = gvn = g; + } + + /* Recursive calls. Since we want to maximize the probability of + ** the special case f(1,...,1) > g(1,...,1), we consider the negative + ** cofactors first. Indeed, the complementation parity of the positive + ** cofactors is the same as the one of the parent functions. + */ + res = Cudd_bddLeq(dd,fvn,gvn) && Cudd_bddLeq(dd,fv,gv); + + /* Store result in cache and return. */ + cuddCacheInsert2(dd,(DdNode * (*)(DdManager *, DdNode *, DdNode *))Cudd_bddLeq,f,g,(res ? one : zero)); + return(res); + +} /* end of Cudd_bddLeq */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_bddIte.] + + Description [Implements the recursive step of Cudd_bddIte. Returns a + pointer to the resulting BDD. NULL if the intermediate result blows + up or if reordering occurs.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddBddIteRecur( + DdManager * dd, + DdNode * f, + DdNode * g, + DdNode * h) +{ + DdNode *one, *zero, *res; + DdNode *r, *Fv, *Fnv, *Gv, *Gnv, *H, *Hv, *Hnv, *t, *e; + unsigned int topf, topg, toph, v; + int index; + int comple; + + statLine(dd); + /* Terminal cases. */ + + /* One variable cases. */ + if (f == (one = DD_ONE(dd))) /* ITE(1,G,H) = G */ + return(g); + + if (f == (zero = Cudd_Not(one))) /* ITE(0,G,H) = H */ + return(h); + + /* From now on, f is known not to be a constant. */ + if (g == one || f == g) { /* ITE(F,F,H) = ITE(F,1,H) = F + H */ + if (h == zero) { /* ITE(F,1,0) = F */ + return(f); + } else { + res = cuddBddAndRecur(dd,Cudd_Not(f),Cudd_Not(h)); + return(Cudd_NotCond(res,res != NULL)); + } + } else if (g == zero || f == Cudd_Not(g)) { /* ITE(F,!F,H) = ITE(F,0,H) = !F * H */ + if (h == one) { /* ITE(F,0,1) = !F */ + return(Cudd_Not(f)); + } else { + res = cuddBddAndRecur(dd,Cudd_Not(f),h); + return(res); + } + } + if (h == zero || f == h) { /* ITE(F,G,F) = ITE(F,G,0) = F * G */ + res = cuddBddAndRecur(dd,f,g); + return(res); + } else if (h == one || f == Cudd_Not(h)) { /* ITE(F,G,!F) = ITE(F,G,1) = !F + G */ + res = cuddBddAndRecur(dd,f,Cudd_Not(g)); + return(Cudd_NotCond(res,res != NULL)); + } + + /* Check remaining one variable case. */ + if (g == h) { /* ITE(F,G,G) = G */ + return(g); + } else if (g == Cudd_Not(h)) { /* ITE(F,G,!G) = F <-> G */ + res = cuddBddXorRecur(dd,f,h); + return(res); + } + + /* From here, there are no constants. */ + comple = bddVarToCanonicalSimple(dd, &f, &g, &h, &topf, &topg, &toph); + + /* f & g are now regular pointers */ + + v = ddMin(topg, toph); + + /* A shortcut: ITE(F,G,H) = (v,G,H) if F = (v,1,0), v < top(G,H). */ + if (topf < v && cuddT(f) == one && cuddE(f) == zero) { + r = cuddUniqueInter(dd, (int) f->index, g, h); + return(Cudd_NotCond(r,comple && r != NULL)); + } + + /* Check cache. */ + r = cuddCacheLookup(dd, DD_BDD_ITE_TAG, f, g, h); + if (r != NULL) { + return(Cudd_NotCond(r,comple)); + } + + /* Compute cofactors. */ + if (topf <= v) { + v = ddMin(topf, v); /* v = top_var(F,G,H) */ + index = f->index; + Fv = cuddT(f); Fnv = cuddE(f); + } else { + Fv = Fnv = f; + } + if (topg == v) { + index = g->index; + Gv = cuddT(g); Gnv = cuddE(g); + } else { + Gv = Gnv = g; + } + if (toph == v) { + H = Cudd_Regular(h); + index = H->index; + Hv = cuddT(H); Hnv = cuddE(H); + if (Cudd_IsComplement(h)) { + Hv = Cudd_Not(Hv); + Hnv = Cudd_Not(Hnv); + } + } else { + Hv = Hnv = h; + } + + /* Recursive step. */ + t = cuddBddIteRecur(dd,Fv,Gv,Hv); + if (t == NULL) return(NULL); + cuddRef(t); + + e = cuddBddIteRecur(dd,Fnv,Gnv,Hnv); + if (e == NULL) { + Cudd_IterDerefBdd(dd,t); + return(NULL); + } + cuddRef(e); + + r = (t == e) ? t : cuddUniqueInter(dd,index,t,e); + if (r == NULL) { + Cudd_IterDerefBdd(dd,t); + Cudd_IterDerefBdd(dd,e); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + + cuddCacheInsert(dd, DD_BDD_ITE_TAG, f, g, h, r); + return(Cudd_NotCond(r,comple)); + +} /* end of cuddBddIteRecur */ + + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_bddIntersect.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_bddIntersect] + +******************************************************************************/ +DdNode * +cuddBddIntersectRecur( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *res; + DdNode *F, *G, *t, *e; + DdNode *fv, *fnv, *gv, *gnv; + DdNode *one, *zero; + unsigned int index, topf, topg; + + statLine(dd); + one = DD_ONE(dd); + zero = Cudd_Not(one); + + /* Terminal cases. */ + if (f == zero || g == zero || f == Cudd_Not(g)) return(zero); + if (f == g || g == one) return(f); + if (f == one) return(g); + + /* At this point f and g are not constant. */ + if (f > g) { DdNode *tmp = f; f = g; g = tmp; } + res = cuddCacheLookup2(dd,Cudd_bddIntersect,f,g); + if (res != NULL) return(res); + + /* Find splitting variable. Here we can skip the use of cuddI, + ** because the operands are known to be non-constant. + */ + F = Cudd_Regular(f); + topf = dd->perm[F->index]; + G = Cudd_Regular(g); + topg = dd->perm[G->index]; + + /* Compute cofactors. */ + if (topf <= topg) { + index = F->index; + fv = cuddT(F); + fnv = cuddE(F); + if (Cudd_IsComplement(f)) { + fv = Cudd_Not(fv); + fnv = Cudd_Not(fnv); + } + } else { + index = G->index; + fv = fnv = f; + } + + if (topg <= topf) { + gv = cuddT(G); + gnv = cuddE(G); + if (Cudd_IsComplement(g)) { + gv = Cudd_Not(gv); + gnv = Cudd_Not(gnv); + } + } else { + gv = gnv = g; + } + + /* Compute partial results. */ + t = cuddBddIntersectRecur(dd,fv,gv); + if (t == NULL) return(NULL); + cuddRef(t); + if (t != zero) { + e = zero; + } else { + e = cuddBddIntersectRecur(dd,fnv,gnv); + if (e == NULL) { + Cudd_IterDerefBdd(dd, t); + return(NULL); + } + } + cuddRef(e); + + if (t == e) { /* both equal zero */ + res = t; + } else if (Cudd_IsComplement(t)) { + res = cuddUniqueInter(dd,(int)index,Cudd_Not(t),Cudd_Not(e)); + if (res == NULL) { + Cudd_IterDerefBdd(dd, t); + Cudd_IterDerefBdd(dd, e); + return(NULL); + } + res = Cudd_Not(res); + } else { + res = cuddUniqueInter(dd,(int)index,t,e); + if (res == NULL) { + Cudd_IterDerefBdd(dd, t); + Cudd_IterDerefBdd(dd, e); + return(NULL); + } + } + cuddDeref(e); + cuddDeref(t); + + cuddCacheInsert2(dd,Cudd_bddIntersect,f,g,res); + + return(res); + +} /* end of cuddBddIntersectRecur */ + + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_bddAnd.] + + Description [Implements the recursive step of Cudd_bddAnd by taking + the conjunction of two BDDs. Returns a pointer to the result is + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddAnd] + +******************************************************************************/ +DdNode * +cuddBddAndRecur( + DdManager * manager, + DdNode * f, + DdNode * g) +{ + DdNode *F, *fv, *fnv, *G, *gv, *gnv; + DdNode *one, *r, *t, *e; + unsigned int topf, topg, index; + + statLine(manager); + one = DD_ONE(manager); + + /* Terminal cases. */ + F = Cudd_Regular(f); + G = Cudd_Regular(g); + if (F == G) { + if (f == g) return(f); + else return(Cudd_Not(one)); + } + if (F == one) { + if (f == one) return(g); + else return(f); + } + if (G == one) { + if (g == one) return(f); + else return(g); + } + + /* At this point f and g are not constant. */ + if (f > g) { /* Try to increase cache efficiency. */ + DdNode *tmp = f; + f = g; + g = tmp; + F = Cudd_Regular(f); + G = Cudd_Regular(g); + } + + /* Check cache. */ + if (F->ref != 1 || G->ref != 1) { + r = cuddCacheLookup2(manager, Cudd_bddAnd, f, g); + if (r != NULL) return(r); + } + + /* Here we can skip the use of cuddI, because the operands are known + ** to be non-constant. + */ + topf = manager->perm[F->index]; + topg = manager->perm[G->index]; + + /* Compute cofactors. */ + if (topf <= topg) { + index = F->index; + fv = cuddT(F); + fnv = cuddE(F); + if (Cudd_IsComplement(f)) { + fv = Cudd_Not(fv); + fnv = Cudd_Not(fnv); + } + } else { + index = G->index; + fv = fnv = f; + } + + if (topg <= topf) { + gv = cuddT(G); + gnv = cuddE(G); + if (Cudd_IsComplement(g)) { + gv = Cudd_Not(gv); + gnv = Cudd_Not(gnv); + } + } else { + gv = gnv = g; + } + + t = cuddBddAndRecur(manager, fv, gv); + if (t == NULL) return(NULL); + cuddRef(t); + + e = cuddBddAndRecur(manager, fnv, gnv); + if (e == NULL) { + Cudd_IterDerefBdd(manager, t); + return(NULL); + } + cuddRef(e); + + if (t == e) { + r = t; + } else { + if (Cudd_IsComplement(t)) { + r = cuddUniqueInter(manager,(int)index,Cudd_Not(t),Cudd_Not(e)); + if (r == NULL) { + Cudd_IterDerefBdd(manager, t); + Cudd_IterDerefBdd(manager, e); + return(NULL); + } + r = Cudd_Not(r); + } else { + r = cuddUniqueInter(manager,(int)index,t,e); + if (r == NULL) { + Cudd_IterDerefBdd(manager, t); + Cudd_IterDerefBdd(manager, e); + return(NULL); + } + } + } + cuddDeref(e); + cuddDeref(t); + if (F->ref != 1 || G->ref != 1) + cuddCacheInsert2(manager, Cudd_bddAnd, f, g, r); + return(r); + +} /* end of cuddBddAndRecur */ + + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_bddXor.] + + Description [Implements the recursive step of Cudd_bddXor by taking + the exclusive OR of two BDDs. Returns a pointer to the result is + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddXor] + +******************************************************************************/ +DdNode * +cuddBddXorRecur( + DdManager * manager, + DdNode * f, + DdNode * g) +{ + DdNode *fv, *fnv, *G, *gv, *gnv; + DdNode *one, *zero, *r, *t, *e; + unsigned int topf, topg, index; + + statLine(manager); + one = DD_ONE(manager); + zero = Cudd_Not(one); + + /* Terminal cases. */ + if (f == g) return(zero); + if (f == Cudd_Not(g)) return(one); + if (f > g) { /* Try to increase cache efficiency and simplify tests. */ + DdNode *tmp = f; + f = g; + g = tmp; + } + if (g == zero) return(f); + if (g == one) return(Cudd_Not(f)); + if (Cudd_IsComplement(f)) { + f = Cudd_Not(f); + g = Cudd_Not(g); + } + /* Now the first argument is regular. */ + if (f == one) return(Cudd_Not(g)); + + /* At this point f and g are not constant. */ + + /* Check cache. */ + r = cuddCacheLookup2(manager, Cudd_bddXor, f, g); + if (r != NULL) return(r); + + /* Here we can skip the use of cuddI, because the operands are known + ** to be non-constant. + */ + topf = manager->perm[f->index]; + G = Cudd_Regular(g); + topg = manager->perm[G->index]; + + /* Compute cofactors. */ + if (topf <= topg) { + index = f->index; + fv = cuddT(f); + fnv = cuddE(f); + } else { + index = G->index; + fv = fnv = f; + } + + if (topg <= topf) { + gv = cuddT(G); + gnv = cuddE(G); + if (Cudd_IsComplement(g)) { + gv = Cudd_Not(gv); + gnv = Cudd_Not(gnv); + } + } else { + gv = gnv = g; + } + + t = cuddBddXorRecur(manager, fv, gv); + if (t == NULL) return(NULL); + cuddRef(t); + + e = cuddBddXorRecur(manager, fnv, gnv); + if (e == NULL) { + Cudd_IterDerefBdd(manager, t); + return(NULL); + } + cuddRef(e); + + if (t == e) { + r = t; + } else { + if (Cudd_IsComplement(t)) { + r = cuddUniqueInter(manager,(int)index,Cudd_Not(t),Cudd_Not(e)); + if (r == NULL) { + Cudd_IterDerefBdd(manager, t); + Cudd_IterDerefBdd(manager, e); + return(NULL); + } + r = Cudd_Not(r); + } else { + r = cuddUniqueInter(manager,(int)index,t,e); + if (r == NULL) { + Cudd_IterDerefBdd(manager, t); + Cudd_IterDerefBdd(manager, e); + return(NULL); + } + } + } + cuddDeref(e); + cuddDeref(t); + cuddCacheInsert2(manager, Cudd_bddXor, f, g, r); + return(r); + +} /* end of cuddBddXorRecur */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Replaces variables with constants if possible.] + + Description [This function performs part of the transformation to + standard form by replacing variables with constants if possible.] + + SideEffects [None] + + SeeAlso [bddVarToCanonical bddVarToCanonicalSimple] + +******************************************************************************/ +static void +bddVarToConst( + DdNode * f, + DdNode ** gp, + DdNode ** hp, + DdNode * one) +{ + DdNode *g = *gp; + DdNode *h = *hp; + + if (f == g) { /* ITE(F,F,H) = ITE(F,1,H) = F + H */ + *gp = one; + } else if (f == Cudd_Not(g)) { /* ITE(F,!F,H) = ITE(F,0,H) = !F * H */ + *gp = Cudd_Not(one); + } + if (f == h) { /* ITE(F,G,F) = ITE(F,G,0) = F * G */ + *hp = Cudd_Not(one); + } else if (f == Cudd_Not(h)) { /* ITE(F,G,!F) = ITE(F,G,1) = !F + G */ + *hp = one; + } + +} /* end of bddVarToConst */ + + +/**Function******************************************************************** + + Synopsis [Picks unique member from equiv expressions.] + + Description [Reduces 2 variable expressions to canonical form.] + + SideEffects [None] + + SeeAlso [bddVarToConst bddVarToCanonicalSimple] + +******************************************************************************/ +static int +bddVarToCanonical( + DdManager * dd, + DdNode ** fp, + DdNode ** gp, + DdNode ** hp, + unsigned int * topfp, + unsigned int * topgp, + unsigned int * tophp) +{ + register DdNode *F, *G, *H, *r, *f, *g, *h; + register unsigned int topf, topg, toph; + DdNode *one = dd->one; + int comple, change; + + f = *fp; + g = *gp; + h = *hp; + F = Cudd_Regular(f); + G = Cudd_Regular(g); + H = Cudd_Regular(h); + topf = cuddI(dd,F->index); + topg = cuddI(dd,G->index); + toph = cuddI(dd,H->index); + + change = 0; + + if (G == one) { /* ITE(F,c,H) */ + if ((topf > toph) || (topf == toph && f > h)) { + r = h; + h = f; + f = r; /* ITE(F,1,H) = ITE(H,1,F) */ + if (g != one) { /* g == zero */ + f = Cudd_Not(f); /* ITE(F,0,H) = ITE(!H,0,!F) */ + h = Cudd_Not(h); + } + change = 1; + } + } else if (H == one) { /* ITE(F,G,c) */ + if ((topf > topg) || (topf == topg && f > g)) { + r = g; + g = f; + f = r; /* ITE(F,G,0) = ITE(G,F,0) */ + if (h == one) { + f = Cudd_Not(f); /* ITE(F,G,1) = ITE(!G,!F,1) */ + g = Cudd_Not(g); + } + change = 1; + } + } else if (g == Cudd_Not(h)) { /* ITE(F,G,!G) = ITE(G,F,!F) */ + if ((topf > topg) || (topf == topg && f > g)) { + r = f; + f = g; + g = r; + h = Cudd_Not(r); + change = 1; + } + } + /* adjust pointers so that the first 2 arguments to ITE are regular */ + if (Cudd_IsComplement(f) != 0) { /* ITE(!F,G,H) = ITE(F,H,G) */ + f = Cudd_Not(f); + r = g; + g = h; + h = r; + change = 1; + } + comple = 0; + if (Cudd_IsComplement(g) != 0) { /* ITE(F,!G,H) = !ITE(F,G,!H) */ + g = Cudd_Not(g); + h = Cudd_Not(h); + change = 1; + comple = 1; + } + if (change != 0) { + *fp = f; + *gp = g; + *hp = h; + } + *topfp = cuddI(dd,f->index); + *topgp = cuddI(dd,g->index); + *tophp = cuddI(dd,Cudd_Regular(h)->index); + + return(comple); + +} /* end of bddVarToCanonical */ + + +/**Function******************************************************************** + + Synopsis [Picks unique member from equiv expressions.] + + Description [Makes sure the first two pointers are regular. This + mat require the complementation of the result, which is signaled by + returning 1 instead of 0. This function is simpler than the general + case because it assumes that no two arguments are the same or + complementary, and no argument is constant.] + + SideEffects [None] + + SeeAlso [bddVarToConst bddVarToCanonical] + +******************************************************************************/ +static int +bddVarToCanonicalSimple( + DdManager * dd, + DdNode ** fp, + DdNode ** gp, + DdNode ** hp, + unsigned int * topfp, + unsigned int * topgp, + unsigned int * tophp) +{ + register DdNode *r, *f, *g, *h; + int comple, change; + + f = *fp; + g = *gp; + h = *hp; + + change = 0; + + /* adjust pointers so that the first 2 arguments to ITE are regular */ + if (Cudd_IsComplement(f)) { /* ITE(!F,G,H) = ITE(F,H,G) */ + f = Cudd_Not(f); + r = g; + g = h; + h = r; + change = 1; + } + comple = 0; + if (Cudd_IsComplement(g)) { /* ITE(F,!G,H) = !ITE(F,G,!H) */ + g = Cudd_Not(g); + h = Cudd_Not(h); + change = 1; + comple = 1; + } + if (change) { + *fp = f; + *gp = g; + *hp = h; + } + + /* Here we can skip the use of cuddI, because the operands are known + ** to be non-constant. + */ + *topfp = dd->perm[f->index]; + *topgp = dd->perm[g->index]; + *tophp = dd->perm[Cudd_Regular(h)->index]; + + return(comple); + +} /* end of bddVarToCanonicalSimple */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddBridge.c b/abc_with_bb_support/src/bdd/cudd/cuddBridge.c new file mode 100644 index 000000000..d43bd5a3f --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddBridge.c @@ -0,0 +1,981 @@ +/**CFile*********************************************************************** + + FileName [cuddBridge.c] + + PackageName [cudd] + + Synopsis [Translation from BDD to ADD and vice versa and transfer between + different managers.] + + Description [External procedures included in this file: +
    +
  • Cudd_addBddThreshold() +
  • Cudd_addBddStrictThreshold() +
  • Cudd_addBddInterval() +
  • Cudd_addBddIthBit() +
  • Cudd_BddToAdd() +
  • Cudd_addBddPattern() +
  • Cudd_bddTransfer() +
+ Internal procedures included in this file: +
    +
  • cuddBddTransfer() +
  • cuddAddBddDoPattern() +
+ Static procedures included in this file: +
    +
  • addBddDoThreshold() +
  • addBddDoStrictThreshold() +
  • addBddDoInterval() +
  • addBddDoIthBit() +
  • ddBddToAddRecur() +
  • cuddBddTransferRecur() +
+ ] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [ This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddBridge.c,v 1.1.1.1 2003/02/24 22:23:51 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static DdNode * addBddDoThreshold ARGS((DdManager *dd, DdNode *f, DdNode *val)); +static DdNode * addBddDoStrictThreshold ARGS((DdManager *dd, DdNode *f, DdNode *val)); +static DdNode * addBddDoInterval ARGS((DdManager *dd, DdNode *f, DdNode *l, DdNode *u)); +static DdNode * addBddDoIthBit ARGS((DdManager *dd, DdNode *f, DdNode *index)); +static DdNode * ddBddToAddRecur ARGS((DdManager *dd, DdNode *B)); +static DdNode * cuddBddTransferRecur ARGS((DdManager *ddS, DdManager *ddD, DdNode *f, st_table *table)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Converts an ADD to a BDD.] + + Description [Converts an ADD to a BDD by replacing all + discriminants greater than or equal to value with 1, and all other + discriminants with 0. Returns a pointer to the resulting BDD if + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addBddInterval Cudd_addBddPattern Cudd_BddToAdd + Cudd_addBddStrictThreshold] + +******************************************************************************/ +DdNode * +Cudd_addBddThreshold( + DdManager * dd, + DdNode * f, + CUDD_VALUE_TYPE value) +{ + DdNode *res; + DdNode *val; + + val = cuddUniqueConst(dd,value); + if (val == NULL) return(NULL); + cuddRef(val); + + do { + dd->reordered = 0; + res = addBddDoThreshold(dd, f, val); + } while (dd->reordered == 1); + + if (res == NULL) { + Cudd_RecursiveDeref(dd, val); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(dd, val); + cuddDeref(res); + return(res); + +} /* end of Cudd_addBddThreshold */ + + +/**Function******************************************************************** + + Synopsis [Converts an ADD to a BDD.] + + Description [Converts an ADD to a BDD by replacing all + discriminants STRICTLY greater than value with 1, and all other + discriminants with 0. Returns a pointer to the resulting BDD if + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addBddInterval Cudd_addBddPattern Cudd_BddToAdd + Cudd_addBddThreshold] + +******************************************************************************/ +DdNode * +Cudd_addBddStrictThreshold( + DdManager * dd, + DdNode * f, + CUDD_VALUE_TYPE value) +{ + DdNode *res; + DdNode *val; + + val = cuddUniqueConst(dd,value); + if (val == NULL) return(NULL); + cuddRef(val); + + do { + dd->reordered = 0; + res = addBddDoStrictThreshold(dd, f, val); + } while (dd->reordered == 1); + + if (res == NULL) { + Cudd_RecursiveDeref(dd, val); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(dd, val); + cuddDeref(res); + return(res); + +} /* end of Cudd_addBddStrictThreshold */ + + +/**Function******************************************************************** + + Synopsis [Converts an ADD to a BDD.] + + Description [Converts an ADD to a BDD by replacing all + discriminants greater than or equal to lower and less than or equal to + upper with 1, and all other discriminants with 0. Returns a pointer to + the resulting BDD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addBddThreshold Cudd_addBddStrictThreshold + Cudd_addBddPattern Cudd_BddToAdd] + +******************************************************************************/ +DdNode * +Cudd_addBddInterval( + DdManager * dd, + DdNode * f, + CUDD_VALUE_TYPE lower, + CUDD_VALUE_TYPE upper) +{ + DdNode *res; + DdNode *l; + DdNode *u; + + /* Create constant nodes for the interval bounds, so that we can use + ** the global cache. + */ + l = cuddUniqueConst(dd,lower); + if (l == NULL) return(NULL); + cuddRef(l); + u = cuddUniqueConst(dd,upper); + if (u == NULL) { + Cudd_RecursiveDeref(dd,l); + return(NULL); + } + cuddRef(u); + + do { + dd->reordered = 0; + res = addBddDoInterval(dd, f, l, u); + } while (dd->reordered == 1); + + if (res == NULL) { + Cudd_RecursiveDeref(dd, l); + Cudd_RecursiveDeref(dd, u); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(dd, l); + Cudd_RecursiveDeref(dd, u); + cuddDeref(res); + return(res); + +} /* end of Cudd_addBddInterval */ + + +/**Function******************************************************************** + + Synopsis [Converts an ADD to a BDD by extracting the i-th bit from + the leaves.] + + Description [Converts an ADD to a BDD by replacing all + discriminants whose i-th bit is equal to 1 with 1, and all other + discriminants with 0. The i-th bit refers to the integer + representation of the leaf value. If the value is has a fractional + part, it is ignored. Repeated calls to this procedure allow one to + transform an integer-valued ADD into an array of BDDs, one for each + bit of the leaf values. Returns a pointer to the resulting BDD if + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addBddInterval Cudd_addBddPattern Cudd_BddToAdd] + +******************************************************************************/ +DdNode * +Cudd_addBddIthBit( + DdManager * dd, + DdNode * f, + int bit) +{ + DdNode *res; + DdNode *index; + + index = cuddUniqueConst(dd,(CUDD_VALUE_TYPE) bit); + if (index == NULL) return(NULL); + cuddRef(index); + + do { + dd->reordered = 0; + res = addBddDoIthBit(dd, f, index); + } while (dd->reordered == 1); + + if (res == NULL) { + Cudd_RecursiveDeref(dd, index); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(dd, index); + cuddDeref(res); + return(res); + +} /* end of Cudd_addBddIthBit */ + + +/**Function******************************************************************** + + Synopsis [Converts a BDD to a 0-1 ADD.] + + Description [Converts a BDD to a 0-1 ADD. Returns a pointer to the + resulting ADD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addBddPattern Cudd_addBddThreshold Cudd_addBddInterval + Cudd_addBddStrictThreshold] + +******************************************************************************/ +DdNode * +Cudd_BddToAdd( + DdManager * dd, + DdNode * B) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = ddBddToAddRecur(dd, B); + } while (dd->reordered ==1); + return(res); + +} /* end of Cudd_BddToAdd */ + + +/**Function******************************************************************** + + Synopsis [Converts an ADD to a BDD.] + + Description [Converts an ADD to a BDD by replacing all + discriminants different from 0 with 1. Returns a pointer to the + resulting BDD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_BddToAdd Cudd_addBddThreshold Cudd_addBddInterval + Cudd_addBddStrictThreshold] + +******************************************************************************/ +DdNode * +Cudd_addBddPattern( + DdManager * dd, + DdNode * f) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddAddBddDoPattern(dd, f); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_addBddPattern */ + + +/**Function******************************************************************** + + Synopsis [Convert a BDD from a manager to another one.] + + Description [Convert a BDD from a manager to another one. The orders of the + variables in the two managers may be different. Returns a + pointer to the BDD in the destination manager if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +Cudd_bddTransfer( + DdManager * ddSource, + DdManager * ddDestination, + DdNode * f) +{ + DdNode *res; + do { + ddDestination->reordered = 0; + res = cuddBddTransfer(ddSource, ddDestination, f); + } while (ddDestination->reordered == 1); + return(res); + +} /* end of Cudd_bddTransfer */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Convert a BDD from a manager to another one.] + + Description [Convert a BDD from a manager to another one. Returns a + pointer to the BDD in the destination manager if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddTransfer] + +******************************************************************************/ +DdNode * +cuddBddTransfer( + DdManager * ddS, + DdManager * ddD, + DdNode * f) +{ + DdNode *res; + st_table *table = NULL; + st_generator *gen = NULL; + DdNode *key, *value; + + table = st_init_table(st_ptrcmp,st_ptrhash); + if (table == NULL) goto failure; + res = cuddBddTransferRecur(ddS, ddD, f, table); + if (res != NULL) cuddRef(res); + + /* Dereference all elements in the table and dispose of the table. + ** This must be done also if res is NULL to avoid leaks in case of + ** reordering. */ + gen = st_init_gen(table); + if (gen == NULL) goto failure; + while (st_gen(gen, (char **) &key, (char **) &value)) { + Cudd_RecursiveDeref(ddD, value); + } + st_free_gen(gen); gen = NULL; + st_free_table(table); table = NULL; + + if (res != NULL) cuddDeref(res); + return(res); + +failure: + if (table != NULL) st_free_table(table); + if (gen != NULL) st_free_gen(gen); + return(NULL); + +} /* end of cuddBddTransfer */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step for Cudd_addBddPattern.] + + Description [Performs the recursive step for Cudd_addBddPattern. Returns a + pointer to the resulting BDD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddAddBddDoPattern( + DdManager * dd, + DdNode * f) +{ + DdNode *res, *T, *E; + DdNode *fv, *fvn; + int v; + + statLine(dd); + /* Check terminal case. */ + if (cuddIsConstant(f)) { + return(Cudd_NotCond(DD_ONE(dd),f == DD_ZERO(dd))); + } + + /* Check cache. */ + res = cuddCacheLookup1(dd,Cudd_addBddPattern,f); + if (res != NULL) return(res); + + /* Recursive step. */ + v = f->index; + fv = cuddT(f); fvn = cuddE(f); + + T = cuddAddBddDoPattern(dd,fv); + if (T == NULL) return(NULL); + cuddRef(T); + + E = cuddAddBddDoPattern(dd,fvn); + if (E == NULL) { + Cudd_RecursiveDeref(dd, T); + return(NULL); + } + cuddRef(E); + if (Cudd_IsComplement(T)) { + res = (T == E) ? Cudd_Not(T) : cuddUniqueInter(dd,v,Cudd_Not(T),Cudd_Not(E)); + if (res == NULL) { + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + return(NULL); + } + res = Cudd_Not(res); + } else { + res = (T == E) ? T : cuddUniqueInter(dd,v,T,E); + if (res == NULL) { + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + return(NULL); + } + } + cuddDeref(T); + cuddDeref(E); + + /* Store result. */ + cuddCacheInsert1(dd,Cudd_addBddPattern,f,res); + + return(res); + +} /* end of cuddAddBddDoPattern */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step for Cudd_addBddThreshold.] + + Description [Performs the recursive step for Cudd_addBddThreshold. + Returns a pointer to the BDD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [addBddDoStrictThreshold] + +******************************************************************************/ +static DdNode * +addBddDoThreshold( + DdManager * dd, + DdNode * f, + DdNode * val) +{ + DdNode *res, *T, *E; + DdNode *fv, *fvn; + int v; + + statLine(dd); + /* Check terminal case. */ + if (cuddIsConstant(f)) { + return(Cudd_NotCond(DD_ONE(dd),cuddV(f) < cuddV(val))); + } + + /* Check cache. */ + res = cuddCacheLookup2(dd,addBddDoThreshold,f,val); + if (res != NULL) return(res); + + /* Recursive step. */ + v = f->index; + fv = cuddT(f); fvn = cuddE(f); + + T = addBddDoThreshold(dd,fv,val); + if (T == NULL) return(NULL); + cuddRef(T); + + E = addBddDoThreshold(dd,fvn,val); + if (E == NULL) { + Cudd_RecursiveDeref(dd, T); + return(NULL); + } + cuddRef(E); + if (Cudd_IsComplement(T)) { + res = (T == E) ? Cudd_Not(T) : cuddUniqueInter(dd,v,Cudd_Not(T),Cudd_Not(E)); + if (res == NULL) { + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + return(NULL); + } + res = Cudd_Not(res); + } else { + res = (T == E) ? T : cuddUniqueInter(dd,v,T,E); + if (res == NULL) { + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + return(NULL); + } + } + cuddDeref(T); + cuddDeref(E); + + /* Store result. */ + cuddCacheInsert2(dd,addBddDoThreshold,f,val,res); + + return(res); + +} /* end of addBddDoThreshold */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step for Cudd_addBddStrictThreshold.] + + Description [Performs the recursive step for Cudd_addBddStrictThreshold. + Returns a pointer to the BDD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [addBddDoThreshold] + +******************************************************************************/ +static DdNode * +addBddDoStrictThreshold( + DdManager * dd, + DdNode * f, + DdNode * val) +{ + DdNode *res, *T, *E; + DdNode *fv, *fvn; + int v; + + statLine(dd); + /* Check terminal case. */ + if (cuddIsConstant(f)) { + return(Cudd_NotCond(DD_ONE(dd),cuddV(f) <= cuddV(val))); + } + + /* Check cache. */ + res = cuddCacheLookup2(dd,addBddDoStrictThreshold,f,val); + if (res != NULL) return(res); + + /* Recursive step. */ + v = f->index; + fv = cuddT(f); fvn = cuddE(f); + + T = addBddDoStrictThreshold(dd,fv,val); + if (T == NULL) return(NULL); + cuddRef(T); + + E = addBddDoStrictThreshold(dd,fvn,val); + if (E == NULL) { + Cudd_RecursiveDeref(dd, T); + return(NULL); + } + cuddRef(E); + if (Cudd_IsComplement(T)) { + res = (T == E) ? Cudd_Not(T) : cuddUniqueInter(dd,v,Cudd_Not(T),Cudd_Not(E)); + if (res == NULL) { + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + return(NULL); + } + res = Cudd_Not(res); + } else { + res = (T == E) ? T : cuddUniqueInter(dd,v,T,E); + if (res == NULL) { + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + return(NULL); + } + } + cuddDeref(T); + cuddDeref(E); + + /* Store result. */ + cuddCacheInsert2(dd,addBddDoStrictThreshold,f,val,res); + + return(res); + +} /* end of addBddDoStrictThreshold */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step for Cudd_addBddInterval.] + + Description [Performs the recursive step for Cudd_addBddInterval. + Returns a pointer to the BDD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [addBddDoThreshold addBddDoStrictThreshold] + +******************************************************************************/ +static DdNode * +addBddDoInterval( + DdManager * dd, + DdNode * f, + DdNode * l, + DdNode * u) +{ + DdNode *res, *T, *E; + DdNode *fv, *fvn; + int v; + + statLine(dd); + /* Check terminal case. */ + if (cuddIsConstant(f)) { + return(Cudd_NotCond(DD_ONE(dd),cuddV(f) < cuddV(l) || cuddV(f) > cuddV(u))); + } + + /* Check cache. */ + res = cuddCacheLookup(dd,DD_ADD_BDD_DO_INTERVAL_TAG,f,l,u); + if (res != NULL) return(res); + + /* Recursive step. */ + v = f->index; + fv = cuddT(f); fvn = cuddE(f); + + T = addBddDoInterval(dd,fv,l,u); + if (T == NULL) return(NULL); + cuddRef(T); + + E = addBddDoInterval(dd,fvn,l,u); + if (E == NULL) { + Cudd_RecursiveDeref(dd, T); + return(NULL); + } + cuddRef(E); + if (Cudd_IsComplement(T)) { + res = (T == E) ? Cudd_Not(T) : cuddUniqueInter(dd,v,Cudd_Not(T),Cudd_Not(E)); + if (res == NULL) { + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + return(NULL); + } + res = Cudd_Not(res); + } else { + res = (T == E) ? T : cuddUniqueInter(dd,v,T,E); + if (res == NULL) { + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + return(NULL); + } + } + cuddDeref(T); + cuddDeref(E); + + /* Store result. */ + cuddCacheInsert(dd,DD_ADD_BDD_DO_INTERVAL_TAG,f,l,u,res); + + return(res); + +} /* end of addBddDoInterval */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step for Cudd_addBddIthBit.] + + Description [Performs the recursive step for Cudd_addBddIthBit. + Returns a pointer to the BDD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static DdNode * +addBddDoIthBit( + DdManager * dd, + DdNode * f, + DdNode * index) +{ + DdNode *res, *T, *E; + DdNode *fv, *fvn; + int mask, value; + int v; + + statLine(dd); + /* Check terminal case. */ + if (cuddIsConstant(f)) { + mask = 1 << ((int) cuddV(index)); + value = (int) cuddV(f); + return(Cudd_NotCond(DD_ONE(dd),(value & mask) == 0)); + } + + /* Check cache. */ + res = cuddCacheLookup2(dd,addBddDoIthBit,f,index); + if (res != NULL) return(res); + + /* Recursive step. */ + v = f->index; + fv = cuddT(f); fvn = cuddE(f); + + T = addBddDoIthBit(dd,fv,index); + if (T == NULL) return(NULL); + cuddRef(T); + + E = addBddDoIthBit(dd,fvn,index); + if (E == NULL) { + Cudd_RecursiveDeref(dd, T); + return(NULL); + } + cuddRef(E); + if (Cudd_IsComplement(T)) { + res = (T == E) ? Cudd_Not(T) : cuddUniqueInter(dd,v,Cudd_Not(T),Cudd_Not(E)); + if (res == NULL) { + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + return(NULL); + } + res = Cudd_Not(res); + } else { + res = (T == E) ? T : cuddUniqueInter(dd,v,T,E); + if (res == NULL) { + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + return(NULL); + } + } + cuddDeref(T); + cuddDeref(E); + + /* Store result. */ + cuddCacheInsert2(dd,addBddDoIthBit,f,index,res); + + return(res); + +} /* end of addBddDoIthBit */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step for Cudd_BddToAdd.] + + Description [Performs the recursive step for Cudd_BddToAdd. Returns a + pointer to the resulting ADD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static DdNode * +ddBddToAddRecur( + DdManager * dd, + DdNode * B) +{ + DdNode *one; + DdNode *res, *res1, *T, *E, *Bt, *Be; + int complement = 0; + + statLine(dd); + one = DD_ONE(dd); + + if (Cudd_IsConstant(B)) { + if (B == one) { + res = one; + } else { + res = DD_ZERO(dd); + } + return(res); + } + /* Check visited table */ + res = cuddCacheLookup1(dd,ddBddToAddRecur,B); + if (res != NULL) return(res); + + if (Cudd_IsComplement(B)) { + complement = 1; + Bt = cuddT(Cudd_Regular(B)); + Be = cuddE(Cudd_Regular(B)); + } else { + Bt = cuddT(B); + Be = cuddE(B); + } + + T = ddBddToAddRecur(dd, Bt); + if (T == NULL) return(NULL); + cuddRef(T); + + E = ddBddToAddRecur(dd, Be); + if (E == NULL) { + Cudd_RecursiveDeref(dd, T); + return(NULL); + } + cuddRef(E); + + /* No need to check for T == E, because it is guaranteed not to happen. */ + res = cuddUniqueInter(dd, (int) Cudd_Regular(B)->index, T, E); + if (res == NULL) { + Cudd_RecursiveDeref(dd ,T); + Cudd_RecursiveDeref(dd ,E); + return(NULL); + } + cuddDeref(T); + cuddDeref(E); + + if (complement) { + cuddRef(res); + res1 = cuddAddCmplRecur(dd, res); + if (res1 == NULL) { + Cudd_RecursiveDeref(dd, res); + return(NULL); + } + cuddRef(res1); + Cudd_RecursiveDeref(dd, res); + res = res1; + cuddDeref(res); + } + + /* Store result. */ + cuddCacheInsert1(dd,ddBddToAddRecur,B,res); + + return(res); + +} /* end of ddBddToAddRecur */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_bddTransfer.] + + Description [Performs the recursive step of Cudd_bddTransfer. + Returns a pointer to the result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [cuddBddTransfer] + +******************************************************************************/ +static DdNode * +cuddBddTransferRecur( + DdManager * ddS, + DdManager * ddD, + DdNode * f, + st_table * table) +{ + DdNode *ft, *fe, *t, *e, *var, *res; + DdNode *one, *zero; + int index; + int comple = 0; + + statLine(ddD); + one = DD_ONE(ddD); + comple = Cudd_IsComplement(f); + + /* Trivial cases. */ + if (Cudd_IsConstant(f)) return(Cudd_NotCond(one, comple)); + + /* Make canonical to increase the utilization of the cache. */ + f = Cudd_NotCond(f,comple); + /* Now f is a regular pointer to a non-constant node. */ + + /* Check the cache. */ + if(st_lookup(table, (char *)f, (char **) &res)) + return(Cudd_NotCond(res,comple)); + + /* Recursive step. */ + index = f->index; + ft = cuddT(f); fe = cuddE(f); + + t = cuddBddTransferRecur(ddS, ddD, ft, table); + if (t == NULL) { + return(NULL); + } + cuddRef(t); + + e = cuddBddTransferRecur(ddS, ddD, fe, table); + if (e == NULL) { + Cudd_RecursiveDeref(ddD, t); + return(NULL); + } + cuddRef(e); + + zero = Cudd_Not(one); + var = cuddUniqueInter(ddD,index,one,zero); + if (var == NULL) { + Cudd_RecursiveDeref(ddD, t); + Cudd_RecursiveDeref(ddD, e); + return(NULL); + } + res = cuddBddIteRecur(ddD,var,t,e); + if (res == NULL) { + Cudd_RecursiveDeref(ddD, t); + Cudd_RecursiveDeref(ddD, e); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(ddD, t); + Cudd_RecursiveDeref(ddD, e); + + if (st_add_direct(table, (char *) f, (char *) res) == ST_OUT_OF_MEM) { + Cudd_RecursiveDeref(ddD, res); + return(NULL); + } + return(Cudd_NotCond(res,comple)); + +} /* end of cuddBddTransferRecur */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddCache.c b/abc_with_bb_support/src/bdd/cudd/cuddCache.c new file mode 100644 index 000000000..bf123e98d --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddCache.c @@ -0,0 +1,1023 @@ +/**CFile*********************************************************************** + + FileName [cuddCache.c] + + PackageName [cudd] + + Synopsis [Functions for cache insertion and lookup.] + + Description [Internal procedures included in this module: +
    +
  • cuddInitCache() +
  • cuddCacheInsert() +
  • cuddCacheInsert2() +
  • cuddCacheLookup() +
  • cuddCacheLookupZdd() +
  • cuddCacheLookup2() +
  • cuddCacheLookup2Zdd() +
  • cuddConstantLookup() +
  • cuddCacheProfile() +
  • cuddCacheResize() +
  • cuddCacheFlush() +
  • cuddComputeFloorLog2() +
+ Static procedures included in this module: +
    +
] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [ This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#ifdef DD_CACHE_PROFILE +#define DD_HYSTO_BINS 8 +#endif + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddCache.c,v 1.1.1.1 2003/02/24 22:23:51 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Initializes the computed table.] + + Description [Initializes the computed table. It is called by + Cudd_Init. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_Init] + +******************************************************************************/ +int +cuddInitCache( + DdManager * unique /* unique table */, + unsigned int cacheSize /* initial size of the cache */, + unsigned int maxCacheSize /* cache size beyond which no resizing occurs */) +{ + int i; + unsigned int logSize; +#ifndef DD_CACHE_PROFILE + DdNodePtr *mem; + ptruint offset; +#endif + + /* Round cacheSize to largest power of 2 not greater than the requested + ** initial cache size. */ + logSize = cuddComputeFloorLog2(ddMax(cacheSize,unique->slots/2)); + cacheSize = 1 << logSize; + unique->acache = ALLOC(DdCache,cacheSize+1); + if (unique->acache == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + /* If the size of the cache entry is a power of 2, we want to + ** enforce alignment to that power of two. This happens when + ** DD_CACHE_PROFILE is not defined. */ +#ifdef DD_CACHE_PROFILE + unique->cache = unique->acache; + unique->memused += (cacheSize) * sizeof(DdCache); +#else + mem = (DdNodePtr *) unique->acache; + offset = (ptruint) mem & (sizeof(DdCache) - 1); + mem += (sizeof(DdCache) - offset) / sizeof(DdNodePtr); + unique->cache = (DdCache *) mem; + assert(((ptruint) unique->cache & (sizeof(DdCache) - 1)) == 0); + unique->memused += (cacheSize+1) * sizeof(DdCache); +#endif + unique->cacheSlots = cacheSize; + unique->cacheShift = sizeof(int) * 8 - logSize; + unique->maxCacheHard = maxCacheSize; + /* If cacheSlack is non-negative, we can resize. */ + unique->cacheSlack = (int) ddMin(maxCacheSize, + DD_MAX_CACHE_TO_SLOTS_RATIO*unique->slots) - + 2 * (int) cacheSize; + Cudd_SetMinHit(unique,DD_MIN_HIT); + /* Initialize to avoid division by 0 and immediate resizing. */ + unique->cacheMisses = (double) (int) (cacheSize * unique->minHit + 1); + unique->cacheHits = 0; + unique->totCachehits = 0; + /* The sum of cacheMisses and totCacheMisses is always correct, + ** even though cacheMisses is larger than it should for the reasons + ** explained above. */ + unique->totCacheMisses = -unique->cacheMisses; + unique->cachecollisions = 0; + unique->cacheinserts = 0; + unique->cacheLastInserts = 0; + unique->cachedeletions = 0; + + /* Initialize the cache */ + for (i = 0; (unsigned) i < cacheSize; i++) { + unique->cache[i].h = 0; /* unused slots */ + unique->cache[i].data = NULL; /* invalid entry */ +#ifdef DD_CACHE_PROFILE + unique->cache[i].count = 0; +#endif + } + + return(1); + +} /* end of cuddInitCache */ + + +/**Function******************************************************************** + + Synopsis [Inserts a result in the cache.] + + Description [] + + SideEffects [None] + + SeeAlso [cuddCacheInsert2 cuddCacheInsert1] + +******************************************************************************/ +void +cuddCacheInsert( + DdManager * table, + ptruint op, + DdNode * f, + DdNode * g, + DdNode * h, + DdNode * data) +{ + int posn; + register DdCache *entry; + ptruint uf, ug, uh; + + uf = (ptruint) f | (op & 0xe); + ug = (ptruint) g | (op >> 4); + uh = (ptruint) h; + + posn = ddCHash2(uh,uf,ug,table->cacheShift); + entry = &table->cache[posn]; + + table->cachecollisions += entry->data != NULL; + table->cacheinserts++; + + entry->f = (DdNode *) uf; + entry->g = (DdNode *) ug; + entry->h = uh; + entry->data = data; +#ifdef DD_CACHE_PROFILE + entry->count++; +#endif + +} /* end of cuddCacheInsert */ + + +/**Function******************************************************************** + + Synopsis [Inserts a result in the cache for a function with two + operands.] + + Description [] + + SideEffects [None] + + SeeAlso [cuddCacheInsert cuddCacheInsert1] + +******************************************************************************/ +void +cuddCacheInsert2( + DdManager * table, + DdNode * (*op)(DdManager *, DdNode *, DdNode *), + DdNode * f, + DdNode * g, + DdNode * data) +{ + int posn; + register DdCache *entry; + + posn = ddCHash2(op,f,g,table->cacheShift); + entry = &table->cache[posn]; + + if (entry->data != NULL) { + table->cachecollisions++; + } + table->cacheinserts++; + + entry->f = f; + entry->g = g; + entry->h = (ptruint) op; + entry->data = data; +#ifdef DD_CACHE_PROFILE + entry->count++; +#endif + +} /* end of cuddCacheInsert2 */ + + +/**Function******************************************************************** + + Synopsis [Inserts a result in the cache for a function with two + operands.] + + Description [] + + SideEffects [None] + + SeeAlso [cuddCacheInsert cuddCacheInsert2] + +******************************************************************************/ +void +cuddCacheInsert1( + DdManager * table, + DdNode * (*op)(DdManager *, DdNode *), + DdNode * f, + DdNode * data) +{ + int posn; + register DdCache *entry; + + posn = ddCHash2(op,f,f,table->cacheShift); + entry = &table->cache[posn]; + + if (entry->data != NULL) { + table->cachecollisions++; + } + table->cacheinserts++; + + entry->f = f; + entry->g = f; + entry->h = (ptruint) op; + entry->data = data; +#ifdef DD_CACHE_PROFILE + entry->count++; +#endif + +} /* end of cuddCacheInsert1 */ + + +/**Function******************************************************************** + + Synopsis [Looks up in the cache for the result of op applied to f, + g, and h.] + + Description [Returns the result if found; it returns NULL if no + result is found.] + + SideEffects [None] + + SeeAlso [cuddCacheLookup2 cuddCacheLookup1] + +******************************************************************************/ +DdNode * +cuddCacheLookup( + DdManager * table, + ptruint op, + DdNode * f, + DdNode * g, + DdNode * h) +{ + int posn; + DdCache *en,*cache; + DdNode *data; + ptruint uf, ug, uh; + + uf = (ptruint) f | (op & 0xe); + ug = (ptruint) g | (op >> 4); + uh = (ptruint) h; + + cache = table->cache; +#ifdef DD_DEBUG + if (cache == NULL) { + return(NULL); + } +#endif + + posn = ddCHash2(uh,uf,ug,table->cacheShift); + en = &cache[posn]; + if (en->data != NULL && en->f==(DdNodePtr)uf && en->g==(DdNodePtr)ug && + en->h==uh) { + data = Cudd_Regular(en->data); + table->cacheHits++; + if (data->ref == 0) { + cuddReclaim(table,data); + } + return(en->data); + } + + /* Cache miss: decide whether to resize. */ + table->cacheMisses++; + + if (table->cacheSlack >= 0 && + table->cacheHits > table->cacheMisses * table->minHit) { + cuddCacheResize(table); + } + + return(NULL); + +} /* end of cuddCacheLookup */ + + +/**Function******************************************************************** + + Synopsis [Looks up in the cache for the result of op applied to f, + g, and h.] + + Description [Returns the result if found; it returns NULL if no + result is found.] + + SideEffects [None] + + SeeAlso [cuddCacheLookup2Zdd cuddCacheLookup1Zdd] + +******************************************************************************/ +DdNode * +cuddCacheLookupZdd( + DdManager * table, + ptruint op, + DdNode * f, + DdNode * g, + DdNode * h) +{ + int posn; + DdCache *en,*cache; + DdNode *data; + ptruint uf, ug, uh; + + uf = (ptruint) f | (op & 0xe); + ug = (ptruint) g | (op >> 4); + uh = (ptruint) h; + + cache = table->cache; +#ifdef DD_DEBUG + if (cache == NULL) { + return(NULL); + } +#endif + + posn = ddCHash2(uh,uf,ug,table->cacheShift); + en = &cache[posn]; + if (en->data != NULL && en->f==(DdNodePtr)uf && en->g==(DdNodePtr)ug && + en->h==uh) { + data = Cudd_Regular(en->data); + table->cacheHits++; + if (data->ref == 0) { + cuddReclaimZdd(table,data); + } + return(en->data); + } + + /* Cache miss: decide whether to resize. */ + table->cacheMisses++; + + if (table->cacheSlack >= 0 && + table->cacheHits > table->cacheMisses * table->minHit) { + cuddCacheResize(table); + } + + return(NULL); + +} /* end of cuddCacheLookupZdd */ + + +/**Function******************************************************************** + + Synopsis [Looks up in the cache for the result of op applied to f + and g.] + + Description [Returns the result if found; it returns NULL if no + result is found.] + + SideEffects [None] + + SeeAlso [cuddCacheLookup cuddCacheLookup1] + +******************************************************************************/ +DdNode * +cuddCacheLookup2( + DdManager * table, + DdNode * (*op)(DdManager *, DdNode *, DdNode *), + DdNode * f, + DdNode * g) +{ + int posn; + DdCache *en,*cache; + DdNode *data; + + cache = table->cache; +#ifdef DD_DEBUG + if (cache == NULL) { + return(NULL); + } +#endif + + posn = ddCHash2(op,f,g,table->cacheShift); + en = &cache[posn]; + if (en->data != NULL && en->f==f && en->g==g && en->h==(ptruint)op) { + data = Cudd_Regular(en->data); + table->cacheHits++; + if (data->ref == 0) { + cuddReclaim(table,data); + } + return(en->data); + } + + /* Cache miss: decide whether to resize. */ + table->cacheMisses++; + + if (table->cacheSlack >= 0 && + table->cacheHits > table->cacheMisses * table->minHit) { + cuddCacheResize(table); + } + + return(NULL); + +} /* end of cuddCacheLookup2 */ + + +/**Function******************************************************************** + + Synopsis [Looks up in the cache for the result of op applied to f.] + + Description [Returns the result if found; it returns NULL if no + result is found.] + + SideEffects [None] + + SeeAlso [cuddCacheLookup cuddCacheLookup2] + +******************************************************************************/ +DdNode * +cuddCacheLookup1( + DdManager * table, + DdNode * (*op)(DdManager *, DdNode *), + DdNode * f) +{ + int posn; + DdCache *en,*cache; + DdNode *data; + + cache = table->cache; +#ifdef DD_DEBUG + if (cache == NULL) { + return(NULL); + } +#endif + + posn = ddCHash2(op,f,f,table->cacheShift); + en = &cache[posn]; + if (en->data != NULL && en->f==f && en->h==(ptruint)op) { + data = Cudd_Regular(en->data); + table->cacheHits++; + if (data->ref == 0) { + cuddReclaim(table,data); + } + return(en->data); + } + + /* Cache miss: decide whether to resize. */ + table->cacheMisses++; + + if (table->cacheSlack >= 0 && + table->cacheHits > table->cacheMisses * table->minHit) { + cuddCacheResize(table); + } + + return(NULL); + +} /* end of cuddCacheLookup1 */ + + +/**Function******************************************************************** + + Synopsis [Looks up in the cache for the result of op applied to f + and g.] + + Description [Returns the result if found; it returns NULL if no + result is found.] + + SideEffects [None] + + SeeAlso [cuddCacheLookupZdd cuddCacheLookup1Zdd] + +******************************************************************************/ +DdNode * +cuddCacheLookup2Zdd( + DdManager * table, + DdNode * (*op)(DdManager *, DdNode *, DdNode *), + DdNode * f, + DdNode * g) +{ + int posn; + DdCache *en,*cache; + DdNode *data; + + cache = table->cache; +#ifdef DD_DEBUG + if (cache == NULL) { + return(NULL); + } +#endif + + posn = ddCHash2(op,f,g,table->cacheShift); + en = &cache[posn]; + if (en->data != NULL && en->f==f && en->g==g && en->h==(ptruint)op) { + data = Cudd_Regular(en->data); + table->cacheHits++; + if (data->ref == 0) { + cuddReclaimZdd(table,data); + } + return(en->data); + } + + /* Cache miss: decide whether to resize. */ + table->cacheMisses++; + + if (table->cacheSlack >= 0 && + table->cacheHits > table->cacheMisses * table->minHit) { + cuddCacheResize(table); + } + + return(NULL); + +} /* end of cuddCacheLookup2Zdd */ + + +/**Function******************************************************************** + + Synopsis [Looks up in the cache for the result of op applied to f.] + + Description [Returns the result if found; it returns NULL if no + result is found.] + + SideEffects [None] + + SeeAlso [cuddCacheLookupZdd cuddCacheLookup2Zdd] + +******************************************************************************/ +DdNode * +cuddCacheLookup1Zdd( + DdManager * table, + DdNode * (*op)(DdManager *, DdNode *), + DdNode * f) +{ + int posn; + DdCache *en,*cache; + DdNode *data; + + cache = table->cache; +#ifdef DD_DEBUG + if (cache == NULL) { + return(NULL); + } +#endif + + posn = ddCHash2(op,f,f,table->cacheShift); + en = &cache[posn]; + if (en->data != NULL && en->f==f && en->h==(ptruint)op) { + data = Cudd_Regular(en->data); + table->cacheHits++; + if (data->ref == 0) { + cuddReclaimZdd(table,data); + } + return(en->data); + } + + /* Cache miss: decide whether to resize. */ + table->cacheMisses++; + + if (table->cacheSlack >= 0 && + table->cacheHits > table->cacheMisses * table->minHit) { + cuddCacheResize(table); + } + + return(NULL); + +} /* end of cuddCacheLookup1Zdd */ + + +/**Function******************************************************************** + + Synopsis [Looks up in the cache for the result of op applied to f, + g, and h.] + + Description [Looks up in the cache for the result of op applied to f, + g, and h. Assumes that the calling procedure (e.g., + Cudd_bddIteConstant) is only interested in whether the result is + constant or not. Returns the result if found (possibly + DD_NON_CONSTANT); otherwise it returns NULL.] + + SideEffects [None] + + SeeAlso [cuddCacheLookup] + +******************************************************************************/ +DdNode * +cuddConstantLookup( + DdManager * table, + ptruint op, + DdNode * f, + DdNode * g, + DdNode * h) +{ + int posn; + DdCache *en,*cache; + ptruint uf, ug, uh; + + uf = (ptruint) f | (op & 0xe); + ug = (ptruint) g | (op >> 4); + uh = (ptruint) h; + + cache = table->cache; +#ifdef DD_DEBUG + if (cache == NULL) { + return(NULL); + } +#endif + posn = ddCHash2(uh,uf,ug,table->cacheShift); + en = &cache[posn]; + + /* We do not reclaim here because the result should not be + * referenced, but only tested for being a constant. + */ + if (en->data != NULL && + en->f == (DdNodePtr)uf && en->g == (DdNodePtr)ug && en->h == uh) { + table->cacheHits++; + return(en->data); + } + + /* Cache miss: decide whether to resize. */ + table->cacheMisses++; + + if (table->cacheSlack >= 0 && + table->cacheHits > table->cacheMisses * table->minHit) { + cuddCacheResize(table); + } + + return(NULL); + +} /* end of cuddConstantLookup */ + + +/**Function******************************************************************** + + Synopsis [Computes and prints a profile of the cache usage.] + + Description [Computes and prints a profile of the cache usage. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddCacheProfile( + DdManager * table, + FILE * fp) +{ + DdCache *cache = table->cache; + int slots = table->cacheSlots; + int nzeroes = 0; + int i, retval; + double exUsed; + +#ifdef DD_CACHE_PROFILE + double count, mean, meansq, stddev, expected; + long max, min; + int imax, imin; + double *hystogramQ, *hystogramR; /* histograms by quotient and remainder */ + int nbins = DD_HYSTO_BINS; + int bin; + long thiscount; + double totalcount, exStddev; + + meansq = mean = expected = 0.0; + max = min = (long) cache[0].count; + imax = imin = 0; + totalcount = 0.0; + + hystogramQ = ALLOC(double, nbins); + if (hystogramQ == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } + hystogramR = ALLOC(double, nbins); + if (hystogramR == NULL) { + FREE(hystogramQ); + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (i = 0; i < nbins; i++) { + hystogramQ[i] = 0; + hystogramR[i] = 0; + } + + for (i = 0; i < slots; i++) { + thiscount = (long) cache[i].count; + if (thiscount > max) { + max = thiscount; + imax = i; + } + if (thiscount < min) { + min = thiscount; + imin = i; + } + if (thiscount == 0) { + nzeroes++; + } + count = (double) thiscount; + mean += count; + meansq += count * count; + totalcount += count; + expected += count * (double) i; + bin = (i * nbins) / slots; + hystogramQ[bin] += (double) thiscount; + bin = i % nbins; + hystogramR[bin] += (double) thiscount; + } + mean /= (double) slots; + meansq /= (double) slots; + + /* Compute the standard deviation from both the data and the + ** theoretical model for a random distribution. */ + stddev = sqrt(meansq - mean*mean); + exStddev = sqrt((1 - 1/(double) slots) * totalcount / (double) slots); + + retval = fprintf(fp,"Cache average accesses = %g\n", mean); + if (retval == EOF) return(0); + retval = fprintf(fp,"Cache access standard deviation = %g ", stddev); + if (retval == EOF) return(0); + retval = fprintf(fp,"(expected = %g)\n", exStddev); + if (retval == EOF) return(0); + retval = fprintf(fp,"Cache max accesses = %ld for slot %d\n", max, imax); + if (retval == EOF) return(0); + retval = fprintf(fp,"Cache min accesses = %ld for slot %d\n", min, imin); + if (retval == EOF) return(0); + exUsed = 100.0 * (1.0 - exp(-totalcount / (double) slots)); + retval = fprintf(fp,"Cache used slots = %.2f%% (expected %.2f%%)\n", + 100.0 - (double) nzeroes * 100.0 / (double) slots, + exUsed); + if (retval == EOF) return(0); + + if (totalcount > 0) { + expected /= totalcount; + retval = fprintf(fp,"Cache access hystogram for %d bins", nbins); + if (retval == EOF) return(0); + retval = fprintf(fp," (expected bin value = %g)\nBy quotient:", + expected); + if (retval == EOF) return(0); + for (i = nbins - 1; i>=0; i--) { + retval = fprintf(fp," %.0f", hystogramQ[i]); + if (retval == EOF) return(0); + } + retval = fprintf(fp,"\nBy residue: "); + if (retval == EOF) return(0); + for (i = nbins - 1; i>=0; i--) { + retval = fprintf(fp," %.0f", hystogramR[i]); + if (retval == EOF) return(0); + } + retval = fprintf(fp,"\n"); + if (retval == EOF) return(0); + } + + FREE(hystogramQ); + FREE(hystogramR); +#else + for (i = 0; i < slots; i++) { + nzeroes += cache[i].h == 0; + } + exUsed = 100.0 * + (1.0 - exp(-(table->cacheinserts - table->cacheLastInserts) / + (double) slots)); + retval = fprintf(fp,"Cache used slots = %.2f%% (expected %.2f%%)\n", + 100.0 - (double) nzeroes * 100.0 / (double) slots, + exUsed); + if (retval == EOF) return(0); +#endif + return(1); + +} /* end of cuddCacheProfile */ + + +/**Function******************************************************************** + + Synopsis [Resizes the cache.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +cuddCacheResize( + DdManager * table) +{ + DdCache *cache, *oldcache, *oldacache, *entry, *old; + int i; + int posn, shift; + unsigned int slots, oldslots; + double offset; + int moved = 0; + extern void (*MMoutOfMemory)(long); + void (*saveHandler)(long); +#ifndef DD_CACHE_PROFILE + ptruint misalignment; + DdNodePtr *mem; +#endif + + oldcache = table->cache; + oldacache = table->acache; + oldslots = table->cacheSlots; + slots = table->cacheSlots = oldslots << 1; + +#ifdef DD_VERBOSE + (void) fprintf(table->err,"Resizing the cache from %d to %d entries\n", + oldslots, slots); + (void) fprintf(table->err, + "\thits = %g\tmisses = %g\thit ratio = %5.3f\n", + table->cacheHits, table->cacheMisses, + table->cacheHits / (table->cacheHits + table->cacheMisses)); +#endif + + saveHandler = MMoutOfMemory; + MMoutOfMemory = Cudd_OutOfMem; + table->acache = cache = ALLOC(DdCache,slots+1); + MMoutOfMemory = saveHandler; + /* If we fail to allocate the new table we just give up. */ + if (cache == NULL) { +#ifdef DD_VERBOSE + (void) fprintf(table->err,"Resizing failed. Giving up.\n"); +#endif + table->cacheSlots = oldslots; + table->acache = oldacache; + /* Do not try to resize again. */ + table->maxCacheHard = oldslots - 1; + table->cacheSlack = - (oldslots + 1); + return; + } + /* If the size of the cache entry is a power of 2, we want to + ** enforce alignment to that power of two. This happens when + ** DD_CACHE_PROFILE is not defined. */ +#ifdef DD_CACHE_PROFILE + table->cache = cache; +#else + mem = (DdNodePtr *) cache; + misalignment = (ptruint) mem & (sizeof(DdCache) - 1); + mem += (sizeof(DdCache) - misalignment) / sizeof(DdNodePtr); + table->cache = cache = (DdCache *) mem; + assert(((ptruint) table->cache & (sizeof(DdCache) - 1)) == 0); +#endif + shift = --(table->cacheShift); + table->memused += (slots - oldslots) * sizeof(DdCache); + table->cacheSlack -= slots; /* need these many slots to double again */ + + /* Clear new cache. */ + for (i = 0; (unsigned) i < slots; i++) { + cache[i].data = NULL; + cache[i].h = 0; +#ifdef DD_CACHE_PROFILE + cache[i].count = 0; +#endif + } + + /* Copy from old cache to new one. */ + for (i = 0; (unsigned) i < oldslots; i++) { + old = &oldcache[i]; + if (old->data != NULL) { + posn = ddCHash2(old->h,old->f,old->g,shift); + entry = &cache[posn]; + entry->f = old->f; + entry->g = old->g; + entry->h = old->h; + entry->data = old->data; +#ifdef DD_CACHE_PROFILE + entry->count = 1; +#endif + moved++; + } + } + + FREE(oldacache); + + /* Reinitialize measurements so as to avoid division by 0 and + ** immediate resizing. + */ + offset = (double) (int) (slots * table->minHit + 1); + table->totCacheMisses += table->cacheMisses - offset; + table->cacheMisses = offset; + table->totCachehits += table->cacheHits; + table->cacheHits = 0; + table->cacheLastInserts = table->cacheinserts - (double) moved; + +} /* end of cuddCacheResize */ + + +/**Function******************************************************************** + + Synopsis [Flushes the cache.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +cuddCacheFlush( + DdManager * table) +{ + int i, slots; + DdCache *cache; + + slots = table->cacheSlots; + cache = table->cache; + for (i = 0; i < slots; i++) { + table->cachedeletions += cache[i].data != NULL; + cache[i].data = NULL; + } + table->cacheLastInserts = table->cacheinserts; + + return; + +} /* end of cuddCacheFlush */ + + +/**Function******************************************************************** + + Synopsis [Returns the floor of the logarithm to the base 2.] + + Description [Returns the floor of the logarithm to the base 2. + The input value is assumed to be greater than 0.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddComputeFloorLog2( + unsigned int value) +{ + int floorLog = 0; +#ifdef DD_DEBUG + assert(value > 0); +#endif + while (value > 1) { + floorLog++; + value >>= 1; + } + return(floorLog); + +} /* end of cuddComputeFloorLog2 */ + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddCheck.c b/abc_with_bb_support/src/bdd/cudd/cuddCheck.c new file mode 100644 index 000000000..71d49f500 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddCheck.c @@ -0,0 +1,851 @@ +/**CFile*********************************************************************** + + FileName [cuddCheck.c] + + PackageName [cudd] + + Synopsis [Functions to check consistency of data structures.] + + Description [External procedures included in this module: +
    +
  • Cudd_DebugCheck() +
  • Cudd_CheckKeys() +
+ Internal procedures included in this module: +
    +
  • cuddHeapProfile() +
  • cuddPrintNode() +
  • cuddPrintVarGroups() +
+ Static procedures included in this module: +
    +
  • debugFindParent() +
+ ] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddCheck.c,v 1.1.1.1 2003/02/24 22:23:51 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static void debugFindParent ARGS((DdManager *table, DdNode *node)); +#if 0 +static void debugCheckParent ARGS((DdManager *table, DdNode *node)); +#endif + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Checks for inconsistencies in the DD heap.] + + Description [Checks for inconsistencies in the DD heap: +
    +
  • node has illegal index +
  • live node has dead children +
  • node has illegal Then or Else pointers +
  • BDD/ADD node has identical children +
  • ZDD node has zero then child +
  • wrong number of total nodes +
  • wrong number of dead nodes +
  • ref count error at node +
+ Returns 0 if no inconsistencies are found; DD_OUT_OF_MEM if there is + not enough memory; 1 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_CheckKeys] + +******************************************************************************/ +int +Cudd_DebugCheck( + DdManager * table) +{ + unsigned int i; + int j,count; + int slots; + DdNodePtr *nodelist; + DdNode *f; + DdNode *sentinel = &(table->sentinel); + st_table *edgeTable; /* stores internal ref count for each node */ + st_generator *gen; + int flag = 0; + int totalNode; + int deadNode; + int index; + + + edgeTable = st_init_table(st_ptrcmp,st_ptrhash); + if (edgeTable == NULL) return(CUDD_OUT_OF_MEM); + + /* Check the BDD/ADD subtables. */ + for (i = 0; i < (unsigned) table->size; i++) { + index = table->invperm[i]; + if (i != (unsigned) table->perm[index]) { + (void) fprintf(table->err, + "Permutation corrupted: invperm[%d] = %d\t perm[%d] = %d\n", + i, index, index, table->perm[index]); + } + nodelist = table->subtables[i].nodelist; + slots = table->subtables[i].slots; + + totalNode = 0; + deadNode = 0; + for (j = 0; j < slots; j++) { /* for each subtable slot */ + f = nodelist[j]; + while (f != sentinel) { + totalNode++; + if (cuddT(f) != NULL && cuddE(f) != NULL && f->ref != 0) { + if ((int) f->index != index) { + (void) fprintf(table->err, + "Error: node has illegal index\n"); + cuddPrintNode(f,table->err); + flag = 1; + } + if ((unsigned) cuddI(table,cuddT(f)->index) <= i || + (unsigned) cuddI(table,Cudd_Regular(cuddE(f))->index) + <= i) { + (void) fprintf(table->err, + "Error: node has illegal children\n"); + cuddPrintNode(f,table->err); + flag = 1; + } + if (Cudd_Regular(cuddT(f)) != cuddT(f)) { + (void) fprintf(table->err, + "Error: node has illegal form\n"); + cuddPrintNode(f,table->err); + flag = 1; + } + if (cuddT(f) == cuddE(f)) { + (void) fprintf(table->err, + "Error: node has identical children\n"); + cuddPrintNode(f,table->err); + flag = 1; + } + if (cuddT(f)->ref == 0 || Cudd_Regular(cuddE(f))->ref == 0) { + (void) fprintf(table->err, + "Error: live node has dead children\n"); + cuddPrintNode(f,table->err); + flag =1; + } + /* Increment the internal reference count for the + ** then child of the current node. + */ + if (st_lookup(edgeTable,(char *)cuddT(f),(char **)&count)) { + count++; + } else { + count = 1; + } + if (st_insert(edgeTable,(char *)cuddT(f), + (char *)(long)count) == ST_OUT_OF_MEM) { + st_free_table(edgeTable); + return(CUDD_OUT_OF_MEM); + } + + /* Increment the internal reference count for the + ** else child of the current node. + */ + if (st_lookup(edgeTable,(char *)Cudd_Regular(cuddE(f)),(char **)&count)) { + count++; + } else { + count = 1; + } + if (st_insert(edgeTable,(char *)Cudd_Regular(cuddE(f)), + (char *)(long)count) == ST_OUT_OF_MEM) { + st_free_table(edgeTable); + return(CUDD_OUT_OF_MEM); + } + } else if (cuddT(f) != NULL && cuddE(f) != NULL && f->ref == 0) { + deadNode++; +#if 0 + debugCheckParent(table,f); +#endif + } else { + fprintf(table->err, + "Error: node has illegal Then or Else pointers\n"); + cuddPrintNode(f,table->err); + flag = 1; + } + + f = f->next; + } /* for each element of the collision list */ + } /* for each subtable slot */ + + if ((unsigned) totalNode != table->subtables[i].keys) { + fprintf(table->err,"Error: wrong number of total nodes\n"); + flag = 1; + } + if ((unsigned) deadNode != table->subtables[i].dead) { + fprintf(table->err,"Error: wrong number of dead nodes\n"); + flag = 1; + } + } /* for each BDD/ADD subtable */ + + /* Check the ZDD subtables. */ + for (i = 0; i < (unsigned) table->sizeZ; i++) { + index = table->invpermZ[i]; + if (i != (unsigned) table->permZ[index]) { + (void) fprintf(table->err, + "Permutation corrupted: invpermZ[%d] = %d\t permZ[%d] = %d in ZDD\n", + i, index, index, table->permZ[index]); + } + nodelist = table->subtableZ[i].nodelist; + slots = table->subtableZ[i].slots; + + totalNode = 0; + deadNode = 0; + for (j = 0; j < slots; j++) { /* for each subtable slot */ + f = nodelist[j]; + while (f != NULL) { + totalNode++; + if (cuddT(f) != NULL && cuddE(f) != NULL && f->ref != 0) { + if ((int) f->index != index) { + (void) fprintf(table->err, + "Error: ZDD node has illegal index\n"); + cuddPrintNode(f,table->err); + flag = 1; + } + if (Cudd_IsComplement(cuddT(f)) || + Cudd_IsComplement(cuddE(f))) { + (void) fprintf(table->err, + "Error: ZDD node has complemented children\n"); + cuddPrintNode(f,table->err); + flag = 1; + } + if ((unsigned) cuddIZ(table,cuddT(f)->index) <= i || + (unsigned) cuddIZ(table,cuddE(f)->index) <= i) { + (void) fprintf(table->err, + "Error: ZDD node has illegal children\n"); + cuddPrintNode(f,table->err); + cuddPrintNode(cuddT(f),table->err); + cuddPrintNode(cuddE(f),table->err); + flag = 1; + } + if (cuddT(f) == DD_ZERO(table)) { + (void) fprintf(table->err, + "Error: ZDD node has zero then child\n"); + cuddPrintNode(f,table->err); + flag = 1; + } + if (cuddT(f)->ref == 0 || cuddE(f)->ref == 0) { + (void) fprintf(table->err, + "Error: ZDD live node has dead children\n"); + cuddPrintNode(f,table->err); + flag =1; + } + /* Increment the internal reference count for the + ** then child of the current node. + */ + if (st_lookup(edgeTable,(char *)cuddT(f),(char **)&count)) { + count++; + } else { + count = 1; + } + if (st_insert(edgeTable,(char *)cuddT(f), + (char *)(long)count) == ST_OUT_OF_MEM) { + st_free_table(edgeTable); + return(CUDD_OUT_OF_MEM); + } + + /* Increment the internal reference count for the + ** else child of the current node. + */ + if (st_lookup(edgeTable,(char *)cuddE(f),(char **)&count)) { + count++; + } else { + count = 1; + } + if (st_insert(edgeTable,(char *)cuddE(f), + (char *)(long)count) == ST_OUT_OF_MEM) { + st_free_table(edgeTable); + table->errorCode = CUDD_MEMORY_OUT; + return(CUDD_OUT_OF_MEM); + } + } else if (cuddT(f) != NULL && cuddE(f) != NULL && f->ref == 0) { + deadNode++; +#if 0 + debugCheckParent(table,f); +#endif + } else { + fprintf(table->err, + "Error: ZDD node has illegal Then or Else pointers\n"); + cuddPrintNode(f,table->err); + flag = 1; + } + + f = f->next; + } /* for each element of the collision list */ + } /* for each subtable slot */ + + if ((unsigned) totalNode != table->subtableZ[i].keys) { + fprintf(table->err, + "Error: wrong number of total nodes in ZDD\n"); + flag = 1; + } + if ((unsigned) deadNode != table->subtableZ[i].dead) { + fprintf(table->err, + "Error: wrong number of dead nodes in ZDD\n"); + flag = 1; + } + } /* for each ZDD subtable */ + + /* Check the constant table. */ + nodelist = table->constants.nodelist; + slots = table->constants.slots; + + totalNode = 0; + deadNode = 0; + for (j = 0; j < slots; j++) { + f = nodelist[j]; + while (f != NULL) { + totalNode++; + if (f->ref != 0) { + if (f->index != CUDD_CONST_INDEX) { + fprintf(table->err,"Error: node has illegal index\n"); +#if SIZEOF_VOID_P == 8 + fprintf(table->err, + " node 0x%lx, id = %d, ref = %d, value = %g\n", + (unsigned long)f,f->index,f->ref,cuddV(f)); +#else + fprintf(table->err, + " node 0x%x, id = %d, ref = %d, value = %g\n", + (unsigned)f,f->index,f->ref,cuddV(f)); +#endif + flag = 1; + } + } else { + deadNode++; + } + f = f->next; + } + } + if ((unsigned) totalNode != table->constants.keys) { + (void) fprintf(table->err, + "Error: wrong number of total nodes in constants\n"); + flag = 1; + } + if ((unsigned) deadNode != table->constants.dead) { + (void) fprintf(table->err, + "Error: wrong number of dead nodes in constants\n"); + flag = 1; + } + gen = st_init_gen(edgeTable); + while (st_gen(gen,(char **)&f,(char **)&count)) { + if (count > (int)(f->ref) && f->ref != DD_MAXREF) { +#if SIZEOF_VOID_P == 8 + fprintf(table->err,"ref count error at node 0x%lx, count = %d, id = %d, ref = %d, then = 0x%lx, else = 0x%lx\n",(unsigned long)f,count,f->index,f->ref,(unsigned long)cuddT(f),(unsigned long)cuddE(f)); +#else + fprintf(table->err,"ref count error at node 0x%x, count = %d, id = %d, ref = %d, then = 0x%x, else = 0x%x\n",(unsigned)f,count,f->index,f->ref,(unsigned)cuddT(f),(unsigned)cuddE(f)); +#endif + debugFindParent(table,f); + flag = 1; + } + } + st_free_gen(gen); + st_free_table(edgeTable); + + return (flag); + +} /* end of Cudd_DebugCheck */ + + +/**Function******************************************************************** + + Synopsis [Checks for several conditions that should not occur.] + + Description [Checks for the following conditions: +
    +
  • Wrong sizes of subtables. +
  • Wrong number of keys found in unique subtable. +
  • Wrong number of dead found in unique subtable. +
  • Wrong number of keys found in the constant table +
  • Wrong number of dead found in the constant table +
  • Wrong number of total slots found +
  • Wrong number of maximum keys found +
  • Wrong number of total dead found +
+ Reports the average length of non-empty lists. Returns the number of + subtables for which the number of keys is wrong.] + + SideEffects [None] + + SeeAlso [Cudd_DebugCheck] + +******************************************************************************/ +int +Cudd_CheckKeys( + DdManager * table) +{ + int size; + int i,j; + DdNodePtr *nodelist; + DdNode *node; + DdNode *sentinel = &(table->sentinel); + DdSubtable *subtable; + int keys; + int dead; + int count = 0; + int totalKeys = 0; + int totalSlots = 0; + int totalDead = 0; + int nonEmpty = 0; + unsigned int slots; + int logSlots; + int shift; + + size = table->size; + + for (i = 0; i < size; i++) { + subtable = &(table->subtables[i]); + nodelist = subtable->nodelist; + keys = subtable->keys; + dead = subtable->dead; + totalKeys += keys; + slots = subtable->slots; + shift = subtable->shift; + logSlots = sizeof(int) * 8 - shift; + if (((slots >> logSlots) << logSlots) != slots) { + (void) fprintf(table->err, + "Unique table %d is not the right power of 2\n", i); + (void) fprintf(table->err, + " slots = %u shift = %d\n", slots, shift); + } + totalSlots += slots; + totalDead += dead; + for (j = 0; (unsigned) j < slots; j++) { + node = nodelist[j]; + if (node != sentinel) { + nonEmpty++; + } + while (node != sentinel) { + keys--; + if (node->ref == 0) { + dead--; + } + node = node->next; + } + } + if (keys != 0) { + (void) fprintf(table->err, "Wrong number of keys found \ +in unique table %d (difference=%d)\n", i, keys); + count++; + } + if (dead != 0) { + (void) fprintf(table->err, "Wrong number of dead found \ +in unique table no. %d (difference=%d)\n", i, dead); + } + } /* for each BDD/ADD subtable */ + + /* Check the ZDD subtables. */ + size = table->sizeZ; + + for (i = 0; i < size; i++) { + subtable = &(table->subtableZ[i]); + nodelist = subtable->nodelist; + keys = subtable->keys; + dead = subtable->dead; + totalKeys += keys; + totalSlots += subtable->slots; + totalDead += dead; + for (j = 0; (unsigned) j < subtable->slots; j++) { + node = nodelist[j]; + if (node != NULL) { + nonEmpty++; + } + while (node != NULL) { + keys--; + if (node->ref == 0) { + dead--; + } + node = node->next; + } + } + if (keys != 0) { + (void) fprintf(table->err, "Wrong number of keys found \ +in ZDD unique table no. %d (difference=%d)\n", i, keys); + count++; + } + if (dead != 0) { + (void) fprintf(table->err, "Wrong number of dead found \ +in ZDD unique table no. %d (difference=%d)\n", i, dead); + } + } /* for each ZDD subtable */ + + /* Check the constant table. */ + subtable = &(table->constants); + nodelist = subtable->nodelist; + keys = subtable->keys; + dead = subtable->dead; + totalKeys += keys; + totalSlots += subtable->slots; + totalDead += dead; + for (j = 0; (unsigned) j < subtable->slots; j++) { + node = nodelist[j]; + if (node != NULL) { + nonEmpty++; + } + while (node != NULL) { + keys--; + if (node->ref == 0) { + dead--; + } + node = node->next; + } + } + if (keys != 0) { + (void) fprintf(table->err, "Wrong number of keys found \ +in the constant table (difference=%d)\n", keys); + count++; + } + if (dead != 0) { + (void) fprintf(table->err, "Wrong number of dead found \ +in the constant table (difference=%d)\n", dead); + } + if ((unsigned) totalKeys != table->keys + table->keysZ) { + (void) fprintf(table->err, "Wrong number of total keys found \ +(difference=%d)\n", totalKeys-table->keys); + } + if ((unsigned) totalSlots != table->slots) { + (void) fprintf(table->err, "Wrong number of total slots found \ +(difference=%d)\n", totalSlots-table->slots); + } + if (table->minDead != (unsigned) (table->gcFrac * table->slots)) { + (void) fprintf(table->err, "Wrong number of minimum dead found \ +(%d vs. %d)\n", table->minDead, + (unsigned) (table->gcFrac * (double) table->slots)); + } + if ((unsigned) totalDead != table->dead + table->deadZ) { + (void) fprintf(table->err, "Wrong number of total dead found \ +(difference=%d)\n", totalDead-table->dead); + } + (void)printf("Average length of non-empty lists = %g\n", + (double) table->keys / (double) nonEmpty); + + return(count); + +} /* end of Cudd_CheckKeys */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Prints information about the heap.] + + Description [Prints to the manager's stdout the number of live nodes for each + level of the DD heap that contains at least one live node. It also + prints a summary containing: +
    +
  • total number of tables; +
  • number of tables with live nodes; +
  • table with the largest number of live nodes; +
  • number of nodes in that table. +
+ If more than one table contains the maximum number of live nodes, + only the one of lowest index is reported. Returns 1 in case of success + and 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddHeapProfile( + DdManager * dd) +{ + int ntables = dd->size; + DdSubtable *subtables = dd->subtables; + int i, /* loop index */ + nodes, /* live nodes in i-th layer */ + retval, /* return value of fprintf */ + largest = -1, /* index of the table with most live nodes */ + maxnodes = -1, /* maximum number of live nodes in a table */ + nonempty = 0; /* number of tables with live nodes */ + + /* Print header. */ +#if SIZEOF_VOID_P == 8 + retval = fprintf(dd->out,"*** DD heap profile for 0x%lx ***\n", + (unsigned long) dd); +#else + retval = fprintf(dd->out,"*** DD heap profile for 0x%x ***\n", + (unsigned) dd); +#endif + if (retval == EOF) return 0; + + /* Print number of live nodes for each nonempty table. */ + for (i=0; iout,"%5d: %5d nodes\n", i, nodes); + if (retval == EOF) return 0; + if (nodes > maxnodes) { + maxnodes = nodes; + largest = i; + } + } + } + + nodes = dd->constants.keys - dd->constants.dead; + if (nodes) { + nonempty++; + retval = fprintf(dd->out,"const: %5d nodes\n", nodes); + if (retval == EOF) return 0; + if (nodes > maxnodes) { + maxnodes = nodes; + largest = CUDD_CONST_INDEX; + } + } + + /* Print summary. */ + retval = fprintf(dd->out,"Summary: %d tables, %d non-empty, largest: %d ", + ntables+1, nonempty, largest); + if (retval == EOF) return 0; + retval = fprintf(dd->out,"(with %d nodes)\n", maxnodes); + if (retval == EOF) return 0; + + return(1); + +} /* end of cuddHeapProfile */ + + +/**Function******************************************************************** + + Synopsis [Prints out information on a node.] + + Description [Prints out information on a node.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +cuddPrintNode( + DdNode * f, + FILE *fp) +{ + f = Cudd_Regular(f); +#if SIZEOF_VOID_P == 8 + (void) fprintf(fp," node 0x%lx, id = %d, ref = %d, then = 0x%lx, else = 0x%lx\n",(unsigned long)f,f->index,f->ref,(unsigned long)cuddT(f),(unsigned long)cuddE(f)); +#else + (void) fprintf(fp," node 0x%x, id = %d, ref = %d, then = 0x%x, else = 0x%x\n",(unsigned)f,f->index,f->ref,(unsigned)cuddT(f),(unsigned)cuddE(f)); +#endif + +} /* end of cuddPrintNode */ + + + +/**Function******************************************************************** + + Synopsis [Prints the variable groups as a parenthesized list.] + + Description [Prints the variable groups as a parenthesized list. + For each group the level range that it represents is printed. After + each group, the group's flags are printed, preceded by a `|'. For + each flag (except MTR_TERMINAL) a character is printed. +
    +
  • F: MTR_FIXED +
  • N: MTR_NEWNODE +
  • S: MTR_SOFT +
+ The second argument, silent, if different from 0, causes + Cudd_PrintVarGroups to only check the syntax of the group tree.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +cuddPrintVarGroups( + DdManager * dd /* manager */, + MtrNode * root /* root of the group tree */, + int zdd /* 0: BDD; 1: ZDD */, + int silent /* flag to check tree syntax only */) +{ + MtrNode *node; + int level; + + assert(root != NULL); + assert(root->younger == NULL || root->younger->elder == root); + assert(root->elder == NULL || root->elder->younger == root); + if (zdd) { + level = dd->permZ[root->index]; + } else { + level = dd->perm[root->index]; + } + if (!silent) (void) printf("(%d",level); + if (MTR_TEST(root,MTR_TERMINAL) || root->child == NULL) { + if (!silent) (void) printf(","); + } else { + node = root->child; + while (node != NULL) { + assert(node->low >= root->low && (int) (node->low + node->size) <= (int) (root->low + root->size)); + assert(node->parent == root); + cuddPrintVarGroups(dd,node,zdd,silent); + node = node->younger; + } + } + if (!silent) { + (void) printf("%d", level + root->size - 1); + if (root->flags != MTR_DEFAULT) { + (void) printf("|"); + if (MTR_TEST(root,MTR_FIXED)) (void) printf("F"); + if (MTR_TEST(root,MTR_NEWNODE)) (void) printf("N"); + if (MTR_TEST(root,MTR_SOFT)) (void) printf("S"); + } + (void) printf(")"); + if (root->parent == NULL) (void) printf("\n"); + } + assert((root->flags &~(MTR_TERMINAL | MTR_SOFT | MTR_FIXED | MTR_NEWNODE)) == 0); + return; + +} /* end of cuddPrintVarGroups */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Searches the subtables above node for its parents.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +debugFindParent( + DdManager * table, + DdNode * node) +{ + int i,j; + int slots; + DdNodePtr *nodelist; + DdNode *f; + + for (i = 0; i < cuddI(table,node->index); i++) { + nodelist = table->subtables[i].nodelist; + slots = table->subtables[i].slots; + + for (j=0;jout,"parent is at 0x%lx, id = %d, ref = %d, then = 0x%lx, else = 0x%lx\n", + (unsigned long)f,f->index,f->ref,(unsigned long)cuddT(f),(unsigned long)cuddE(f)); +#else + (void) fprintf(table->out,"parent is at 0x%x, id = %d, ref = %d, then = 0x%x, else = 0x%x\n", + (unsigned)f,f->index,f->ref,(unsigned)cuddT(f),(unsigned)cuddE(f)); +#endif + } + f = f->next; + } + } + } + +} /* end of debugFindParent */ + + +#if 0 +/**Function******************************************************************** + + Synopsis [Reports an error if a (dead) node has a non-dead parent.] + + Description [Searches all the subtables above node. Very expensive. + The same check is now implemented more efficiently in ddDebugCheck.] + + SideEffects [None] + + SeeAlso [debugFindParent] + +******************************************************************************/ +static void +debugCheckParent( + DdManager * table, + DdNode * node) +{ + int i,j; + int slots; + DdNode **nodelist,*f; + + for (i = 0; i < cuddI(table,node->index); i++) { + nodelist = table->subtables[i].nodelist; + slots = table->subtables[i].slots; + + for (j=0;jref != 0) { + (void) fprintf(table->err, + "error with zero ref count\n"); + (void) fprintf(table->err,"parent is 0x%x, id = %d, ref = %d, then = 0x%x, else = 0x%x\n",f,f->index,f->ref,cuddT(f),cuddE(f)); + (void) fprintf(table->err,"child is 0x%x, id = %d, ref = %d, then = 0x%x, else = 0x%x\n",node,node->index,node->ref,cuddT(node),cuddE(node)); + } + f = f->next; + } + } + } +} +#endif diff --git a/abc_with_bb_support/src/bdd/cudd/cuddClip.c b/abc_with_bb_support/src/bdd/cudd/cuddClip.c new file mode 100644 index 000000000..d3e1adfd3 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddClip.c @@ -0,0 +1,531 @@ +/**CFile*********************************************************************** + + FileName [cuddClip.c] + + PackageName [cudd] + + Synopsis [Clipping functions.] + + Description [External procedures included in this module: +
    +
  • Cudd_bddClippingAnd() +
  • Cudd_bddClippingAndAbstract() +
+ Internal procedures included in this module: +
    +
  • cuddBddClippingAnd() +
  • cuddBddClippingAndAbstract() +
+ Static procedures included in this module: +
    +
  • cuddBddClippingAndRecur() +
  • cuddBddClipAndAbsRecur() +
+ + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddClip.c,v 1.1.1.1 2003/02/24 22:23:51 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static DdNode * cuddBddClippingAndRecur ARGS((DdManager *manager, DdNode *f, DdNode *g, int distance, int direction)); +static DdNode * cuddBddClipAndAbsRecur ARGS((DdManager *manager, DdNode *f, DdNode *g, DdNode *cube, int distance, int direction)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Approximates the conjunction of two BDDs f and g.] + + Description [Approximates the conjunction of two BDDs f and g. Returns a + pointer to the resulting BDD if successful; NULL if the intermediate + result blows up.] + + SideEffects [None] + + SeeAlso [Cudd_bddAnd] + +******************************************************************************/ +DdNode * +Cudd_bddClippingAnd( + DdManager * dd /* manager */, + DdNode * f /* first conjunct */, + DdNode * g /* second conjunct */, + int maxDepth /* maximum recursion depth */, + int direction /* under (0) or over (1) approximation */) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddBddClippingAnd(dd,f,g,maxDepth,direction); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_bddClippingAnd */ + + +/**Function******************************************************************** + + Synopsis [Approximates the conjunction of two BDDs f and g and + simultaneously abstracts the variables in cube.] + + Description [Approximates the conjunction of two BDDs f and g and + simultaneously abstracts the variables in cube. The variables are + existentially abstracted. Returns a pointer to the resulting BDD if + successful; NULL if the intermediate result blows up.] + + SideEffects [None] + + SeeAlso [Cudd_bddAndAbstract Cudd_bddClippingAnd] + +******************************************************************************/ +DdNode * +Cudd_bddClippingAndAbstract( + DdManager * dd /* manager */, + DdNode * f /* first conjunct */, + DdNode * g /* second conjunct */, + DdNode * cube /* cube of variables to be abstracted */, + int maxDepth /* maximum recursion depth */, + int direction /* under (0) or over (1) approximation */) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddBddClippingAndAbstract(dd,f,g,cube,maxDepth,direction); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_bddClippingAndAbstract */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Approximates the conjunction of two BDDs f and g.] + + Description [Approximates the conjunction of two BDDs f and g. Returns a + pointer to the resulting BDD if successful; NULL if the intermediate + result blows up.] + + SideEffects [None] + + SeeAlso [Cudd_bddClippingAnd] + +******************************************************************************/ +DdNode * +cuddBddClippingAnd( + DdManager * dd /* manager */, + DdNode * f /* first conjunct */, + DdNode * g /* second conjunct */, + int maxDepth /* maximum recursion depth */, + int direction /* under (0) or over (1) approximation */) +{ + DdNode *res; + + res = cuddBddClippingAndRecur(dd,f,g,maxDepth,direction); + + return(res); + +} /* end of cuddBddClippingAnd */ + + +/**Function******************************************************************** + + Synopsis [Approximates the conjunction of two BDDs f and g and + simultaneously abstracts the variables in cube.] + + Description [Approximates the conjunction of two BDDs f and g and + simultaneously abstracts the variables in cube. Returns a + pointer to the resulting BDD if successful; NULL if the intermediate + result blows up.] + + SideEffects [None] + + SeeAlso [Cudd_bddClippingAndAbstract] + +******************************************************************************/ +DdNode * +cuddBddClippingAndAbstract( + DdManager * dd /* manager */, + DdNode * f /* first conjunct */, + DdNode * g /* second conjunct */, + DdNode * cube /* cube of variables to be abstracted */, + int maxDepth /* maximum recursion depth */, + int direction /* under (0) or over (1) approximation */) +{ + DdNode *res; + + res = cuddBddClipAndAbsRecur(dd,f,g,cube,maxDepth,direction); + + return(res); + +} /* end of cuddBddClippingAndAbstract */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_bddClippingAnd.] + + Description [Implements the recursive step of Cudd_bddClippingAnd by taking + the conjunction of two BDDs. Returns a pointer to the result is + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [cuddBddClippingAnd] + +******************************************************************************/ +static DdNode * +cuddBddClippingAndRecur( + DdManager * manager, + DdNode * f, + DdNode * g, + int distance, + int direction) +{ + DdNode *F, *ft, *fe, *G, *gt, *ge; + DdNode *one, *zero, *r, *t, *e; + unsigned int topf, topg, index; + DdNode *(*cacheOp)(DdManager *, DdNode *, DdNode *); + + statLine(manager); + one = DD_ONE(manager); + zero = Cudd_Not(one); + + /* Terminal cases. */ + if (f == zero || g == zero || f == Cudd_Not(g)) return(zero); + if (f == g || g == one) return(f); + if (f == one) return(g); + if (distance == 0) { + /* One last attempt at returning the right result. We sort of + ** cheat by calling Cudd_bddLeq. */ + if (Cudd_bddLeq(manager,f,g)) return(f); + if (Cudd_bddLeq(manager,g,f)) return(g); + if (direction == 1) { + if (Cudd_bddLeq(manager,f,Cudd_Not(g)) || + Cudd_bddLeq(manager,g,Cudd_Not(f))) return(zero); + } + return(Cudd_NotCond(one,(direction == 0))); + } + + /* At this point f and g are not constant. */ + distance--; + + /* Check cache. Try to increase cache efficiency by sorting the + ** pointers. */ + if (f > g) { + DdNode *tmp = f; + f = g; g = tmp; + } + F = Cudd_Regular(f); + G = Cudd_Regular(g); + cacheOp = (DdNode *(*)(DdManager *, DdNode *, DdNode *)) + (direction ? Cudd_bddClippingAnd : cuddBddClippingAnd); + if (F->ref != 1 || G->ref != 1) { + r = cuddCacheLookup2(manager, cacheOp, f, g); + if (r != NULL) return(r); + } + + /* Here we can skip the use of cuddI, because the operands are known + ** to be non-constant. + */ + topf = manager->perm[F->index]; + topg = manager->perm[G->index]; + + /* Compute cofactors. */ + if (topf <= topg) { + index = F->index; + ft = cuddT(F); + fe = cuddE(F); + if (Cudd_IsComplement(f)) { + ft = Cudd_Not(ft); + fe = Cudd_Not(fe); + } + } else { + index = G->index; + ft = fe = f; + } + + if (topg <= topf) { + gt = cuddT(G); + ge = cuddE(G); + if (Cudd_IsComplement(g)) { + gt = Cudd_Not(gt); + ge = Cudd_Not(ge); + } + } else { + gt = ge = g; + } + + t = cuddBddClippingAndRecur(manager, ft, gt, distance, direction); + if (t == NULL) return(NULL); + cuddRef(t); + e = cuddBddClippingAndRecur(manager, fe, ge, distance, direction); + if (e == NULL) { + Cudd_RecursiveDeref(manager, t); + return(NULL); + } + cuddRef(e); + + if (t == e) { + r = t; + } else { + if (Cudd_IsComplement(t)) { + r = cuddUniqueInter(manager,(int)index,Cudd_Not(t),Cudd_Not(e)); + if (r == NULL) { + Cudd_RecursiveDeref(manager, t); + Cudd_RecursiveDeref(manager, e); + return(NULL); + } + r = Cudd_Not(r); + } else { + r = cuddUniqueInter(manager,(int)index,t,e); + if (r == NULL) { + Cudd_RecursiveDeref(manager, t); + Cudd_RecursiveDeref(manager, e); + return(NULL); + } + } + } + cuddDeref(e); + cuddDeref(t); + if (F->ref != 1 || G->ref != 1) + cuddCacheInsert2(manager, cacheOp, f, g, r); + return(r); + +} /* end of cuddBddClippingAndRecur */ + + +/**Function******************************************************************** + + Synopsis [Approximates the AND of two BDDs and simultaneously abstracts the + variables in cube.] + + Description [Approximates the AND of two BDDs and simultaneously + abstracts the variables in cube. The variables are existentially + abstracted. Returns a pointer to the result is successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddClippingAndAbstract] + +******************************************************************************/ +static DdNode * +cuddBddClipAndAbsRecur( + DdManager * manager, + DdNode * f, + DdNode * g, + DdNode * cube, + int distance, + int direction) +{ + DdNode *F, *ft, *fe, *G, *gt, *ge; + DdNode *one, *zero, *r, *t, *e, *Cube; + unsigned int topf, topg, topcube, top, index; + ptruint cacheTag; + + statLine(manager); + one = DD_ONE(manager); + zero = Cudd_Not(one); + + /* Terminal cases. */ + if (f == zero || g == zero || f == Cudd_Not(g)) return(zero); + if (f == one && g == one) return(one); + if (cube == one) { + return(cuddBddClippingAndRecur(manager, f, g, distance, direction)); + } + if (f == one || f == g) { + return (cuddBddExistAbstractRecur(manager, g, cube)); + } + if (g == one) { + return (cuddBddExistAbstractRecur(manager, f, cube)); + } + if (distance == 0) return(Cudd_NotCond(one,(direction == 0))); + + /* At this point f, g, and cube are not constant. */ + distance--; + + /* Check cache. */ + if (f > g) { /* Try to increase cache efficiency. */ + DdNode *tmp = f; + f = g; g = tmp; + } + F = Cudd_Regular(f); + G = Cudd_Regular(g); + cacheTag = direction ? DD_BDD_CLIPPING_AND_ABSTRACT_UP_TAG : + DD_BDD_CLIPPING_AND_ABSTRACT_DOWN_TAG; + if (F->ref != 1 || G->ref != 1) { + r = cuddCacheLookup(manager, cacheTag, + f, g, cube); + if (r != NULL) { + return(r); + } + } + + /* Here we can skip the use of cuddI, because the operands are known + ** to be non-constant. + */ + topf = manager->perm[F->index]; + topg = manager->perm[G->index]; + top = ddMin(topf, topg); + topcube = manager->perm[cube->index]; + + if (topcube < top) { + return(cuddBddClipAndAbsRecur(manager, f, g, cuddT(cube), + distance, direction)); + } + /* Now, topcube >= top. */ + + if (topf == top) { + index = F->index; + ft = cuddT(F); + fe = cuddE(F); + if (Cudd_IsComplement(f)) { + ft = Cudd_Not(ft); + fe = Cudd_Not(fe); + } + } else { + index = G->index; + ft = fe = f; + } + + if (topg == top) { + gt = cuddT(G); + ge = cuddE(G); + if (Cudd_IsComplement(g)) { + gt = Cudd_Not(gt); + ge = Cudd_Not(ge); + } + } else { + gt = ge = g; + } + + if (topcube == top) { + Cube = cuddT(cube); + } else { + Cube = cube; + } + + t = cuddBddClipAndAbsRecur(manager, ft, gt, Cube, distance, direction); + if (t == NULL) return(NULL); + + /* Special case: 1 OR anything = 1. Hence, no need to compute + ** the else branch if t is 1. + */ + if (t == one && topcube == top) { + if (F->ref != 1 || G->ref != 1) + cuddCacheInsert(manager, cacheTag, f, g, cube, one); + return(one); + } + cuddRef(t); + + e = cuddBddClipAndAbsRecur(manager, fe, ge, Cube, distance, direction); + if (e == NULL) { + Cudd_RecursiveDeref(manager, t); + return(NULL); + } + cuddRef(e); + + if (topcube == top) { /* abstract */ + r = cuddBddClippingAndRecur(manager, Cudd_Not(t), Cudd_Not(e), + distance, (direction == 0)); + if (r == NULL) { + Cudd_RecursiveDeref(manager, t); + Cudd_RecursiveDeref(manager, e); + return(NULL); + } + r = Cudd_Not(r); + cuddRef(r); + Cudd_RecursiveDeref(manager, t); + Cudd_RecursiveDeref(manager, e); + cuddDeref(r); + } else if (t == e) { + r = t; + cuddDeref(t); + cuddDeref(e); + } else { + if (Cudd_IsComplement(t)) { + r = cuddUniqueInter(manager,(int)index,Cudd_Not(t),Cudd_Not(e)); + if (r == NULL) { + Cudd_RecursiveDeref(manager, t); + Cudd_RecursiveDeref(manager, e); + return(NULL); + } + r = Cudd_Not(r); + } else { + r = cuddUniqueInter(manager,(int)index,t,e); + if (r == NULL) { + Cudd_RecursiveDeref(manager, t); + Cudd_RecursiveDeref(manager, e); + return(NULL); + } + } + cuddDeref(e); + cuddDeref(t); + } + if (F->ref != 1 || G->ref != 1) + cuddCacheInsert(manager, cacheTag, f, g, cube, r); + return (r); + +} /* end of cuddBddClipAndAbsRecur */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddCof.c b/abc_with_bb_support/src/bdd/cudd/cuddCof.c new file mode 100644 index 000000000..b006aaccb --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddCof.c @@ -0,0 +1,300 @@ +/**CFile*********************************************************************** + + FileName [cuddCof.c] + + PackageName [cudd] + + Synopsis [Cofactoring functions.] + + Description [External procedures included in this module: +
    +
  • Cudd_Cofactor() +
+ Internal procedures included in this module: +
    +
  • cuddGetBranches() +
  • cuddCheckCube() +
  • cuddCofactorRecur() +
+ ] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [ This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddCof.c,v 1.1.1.1 2003/02/24 22:23:51 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Computes the cofactor of f with respect to g.] + + Description [Computes the cofactor of f with respect to g; g must be + the BDD or the ADD of a cube. Returns a pointer to the cofactor if + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddConstrain Cudd_bddRestrict] + +******************************************************************************/ +DdNode * +Cudd_Cofactor( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *res,*zero; + + zero = Cudd_Not(DD_ONE(dd)); + if (g == zero || g == DD_ZERO(dd)) { + (void) fprintf(dd->err,"Cudd_Cofactor: Invalid restriction 1\n"); + dd->errorCode = CUDD_INVALID_ARG; + return(NULL); + } + do { + dd->reordered = 0; + res = cuddCofactorRecur(dd,f,g); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_Cofactor */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Computes the children of g.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +cuddGetBranches( + DdNode * g, + DdNode ** g1, + DdNode ** g0) +{ + DdNode *G = Cudd_Regular(g); + + *g1 = cuddT(G); + *g0 = cuddE(G); + if (Cudd_IsComplement(g)) { + *g1 = Cudd_Not(*g1); + *g0 = Cudd_Not(*g0); + } + +} /* end of cuddGetBranches */ + + +/**Function******************************************************************** + + Synopsis [Checks whether g is the BDD of a cube.] + + Description [Checks whether g is the BDD of a cube. Returns 1 in case + of success; 0 otherwise. The constant 1 is a valid cube, but all other + constant functions cause cuddCheckCube to return 0.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddCheckCube( + DdManager * dd, + DdNode * g) +{ + DdNode *g1,*g0,*one,*zero; + + one = DD_ONE(dd); + if (g == one) return(1); + if (Cudd_IsConstant(g)) return(0); + + zero = Cudd_Not(one); + cuddGetBranches(g,&g1,&g0); + + if (g0 == zero) { + return(cuddCheckCube(dd, g1)); + } + if (g1 == zero) { + return(cuddCheckCube(dd, g0)); + } + return(0); + +} /* end of cuddCheckCube */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_Cofactor.] + + Description [Performs the recursive step of Cudd_Cofactor. Returns a + pointer to the cofactor if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_Cofactor] + +******************************************************************************/ +DdNode * +cuddCofactorRecur( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *one,*zero,*F,*G,*g1,*g0,*f1,*f0,*t,*e,*r; + unsigned int topf,topg; + int comple; + + statLine(dd); + F = Cudd_Regular(f); + if (cuddIsConstant(F)) return(f); + + one = DD_ONE(dd); + + /* The invariant g != 0 is true on entry to this procedure and is + ** recursively maintained by it. Therefore it suffices to test g + ** against one to make sure it is not constant. + */ + if (g == one) return(f); + /* From now on, f and g are known not to be constants. */ + + comple = f != F; + r = cuddCacheLookup2(dd,Cudd_Cofactor,F,g); + if (r != NULL) { + return(Cudd_NotCond(r,comple)); + } + + topf = dd->perm[F->index]; + G = Cudd_Regular(g); + topg = dd->perm[G->index]; + + /* We take the cofactors of F because we are going to rely on + ** the fact that the cofactors of the complement are the complements + ** of the cofactors to better utilize the cache. Variable comple + ** remembers whether we have to complement the result or not. + */ + if (topf <= topg) { + f1 = cuddT(F); f0 = cuddE(F); + } else { + f1 = f0 = F; + } + if (topg <= topf) { + g1 = cuddT(G); g0 = cuddE(G); + if (g != G) { g1 = Cudd_Not(g1); g0 = Cudd_Not(g0); } + } else { + g1 = g0 = g; + } + + zero = Cudd_Not(one); + if (topf >= topg) { + if (g0 == zero || g0 == DD_ZERO(dd)) { + r = cuddCofactorRecur(dd, f1, g1); + } else if (g1 == zero || g1 == DD_ZERO(dd)) { + r = cuddCofactorRecur(dd, f0, g0); + } else { + (void) fprintf(dd->out, + "Cudd_Cofactor: Invalid restriction 2\n"); + dd->errorCode = CUDD_INVALID_ARG; + return(NULL); + } + if (r == NULL) return(NULL); + } else /* if (topf < topg) */ { + t = cuddCofactorRecur(dd, f1, g); + if (t == NULL) return(NULL); + cuddRef(t); + e = cuddCofactorRecur(dd, f0, g); + if (e == NULL) { + Cudd_RecursiveDeref(dd, t); + return(NULL); + } + cuddRef(e); + + if (t == e) { + r = t; + } else if (Cudd_IsComplement(t)) { + r = cuddUniqueInter(dd,(int)F->index,Cudd_Not(t),Cudd_Not(e)); + if (r != NULL) + r = Cudd_Not(r); + } else { + r = cuddUniqueInter(dd,(int)F->index,t,e); + } + if (r == NULL) { + Cudd_RecursiveDeref(dd ,e); + Cudd_RecursiveDeref(dd ,t); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + } + + cuddCacheInsert2(dd,Cudd_Cofactor,F,g,r); + + return(Cudd_NotCond(r,comple)); + +} /* end of cuddCofactorRecur */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddCompose.c b/abc_with_bb_support/src/bdd/cudd/cuddCompose.c new file mode 100644 index 000000000..f7b0285a2 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddCompose.c @@ -0,0 +1,1722 @@ +/**CFile*********************************************************************** + + FileName [cuddCompose.c] + + PackageName [cudd] + + Synopsis [Functional composition and variable permutation of DDs.] + + Description [External procedures included in this module: +
    +
  • Cudd_bddCompose() +
  • Cudd_addCompose() +
  • Cudd_addPermute() +
  • Cudd_addSwapVariables() +
  • Cudd_bddPermute() +
  • Cudd_bddVarMap() +
  • Cudd_SetVarMap() +
  • Cudd_bddSwapVariables() +
  • Cudd_bddAdjPermuteX() +
  • Cudd_addVectorCompose() +
  • Cudd_addGeneralVectorCompose() +
  • Cudd_addNonSimCompose() +
  • Cudd_bddVectorCompose() +
+ Internal procedures included in this module: +
    +
  • cuddBddComposeRecur() +
  • cuddAddComposeRecur() +
+ Static procedures included in this module: +
    +
  • cuddAddPermuteRecur() +
  • cuddBddPermuteRecur() +
  • cuddBddVarMapRecur() +
  • cuddAddVectorComposeRecur() +
  • cuddAddGeneralVectorComposeRecur() +
  • cuddAddNonSimComposeRecur() +
  • cuddBddVectorComposeRecur() +
  • ddIsIthAddVar() +
  • ddIsIthAddVarPair() +
+ The permutation functions use a local cache because the results to + be remembered depend on the permutation being applied. Since the + permutation is just an array, it cannot be stored in the global + cache. There are different procedured for BDDs and ADDs. This is + because bddPermuteRecur uses cuddBddIteRecur. If this were changed, + the procedures could be merged.] + + Author [Fabio Somenzi and Kavita Ravi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddCompose.c,v 1.1.1.1 2003/02/24 22:23:51 wjiang Exp $"; +#endif + +#ifdef DD_DEBUG +static int addPermuteRecurHits; +static int bddPermuteRecurHits; +static int bddVectorComposeHits; +static int addVectorComposeHits; + +static int addGeneralVectorComposeHits; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static DdNode * cuddAddPermuteRecur ARGS((DdManager *manager, DdHashTable *table, DdNode *node, int *permut)); +static DdNode * cuddBddPermuteRecur ARGS((DdManager *manager, DdHashTable *table, DdNode *node, int *permut)); +static DdNode * cuddBddVarMapRecur ARGS((DdManager *manager, DdNode *f)); +static DdNode * cuddAddVectorComposeRecur ARGS((DdManager *dd, DdHashTable *table, DdNode *f, DdNode **vector, int deepest)); +static DdNode * cuddAddNonSimComposeRecur ARGS((DdManager *dd, DdNode *f, DdNode **vector, DdNode *key, DdNode *cube, int lastsub)); +static DdNode * cuddBddVectorComposeRecur ARGS((DdManager *dd, DdHashTable *table, DdNode *f, DdNode **vector, int deepest)); +DD_INLINE static int ddIsIthAddVar ARGS((DdManager *dd, DdNode *f, unsigned int i)); + +static DdNode * cuddAddGeneralVectorComposeRecur ARGS((DdManager *dd, DdHashTable *table, DdNode *f, DdNode **vectorOn, DdNode **vectorOff, int deepest)); +DD_INLINE static int ddIsIthAddVarPair ARGS((DdManager *dd, DdNode *f, DdNode *g, unsigned int i)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Substitutes g for x_v in the BDD for f.] + + Description [Substitutes g for x_v in the BDD for f. v is the index of the + variable to be substituted. Cudd_bddCompose passes the corresponding + projection function to the recursive procedure, so that the cache may + be used. Returns the composed BDD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addCompose] + +******************************************************************************/ +DdNode * +Cudd_bddCompose( + DdManager * dd, + DdNode * f, + DdNode * g, + int v) +{ + DdNode *proj, *res; + + /* Sanity check. */ + if (v < 0 || v > dd->size) return(NULL); + + proj = dd->vars[v]; + do { + dd->reordered = 0; + res = cuddBddComposeRecur(dd,f,g,proj); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_bddCompose */ + + +/**Function******************************************************************** + + Synopsis [Substitutes g for x_v in the ADD for f.] + + Description [Substitutes g for x_v in the ADD for f. v is the index of the + variable to be substituted. g must be a 0-1 ADD. Cudd_bddCompose passes + the corresponding projection function to the recursive procedure, so + that the cache may be used. Returns the composed ADD if successful; + NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddCompose] + +******************************************************************************/ +DdNode * +Cudd_addCompose( + DdManager * dd, + DdNode * f, + DdNode * g, + int v) +{ + DdNode *proj, *res; + + /* Sanity check. */ + if (v < 0 || v > dd->size) return(NULL); + + proj = dd->vars[v]; + do { + dd->reordered = 0; + res = cuddAddComposeRecur(dd,f,g,proj); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_addCompose */ + + +/**Function******************************************************************** + + Synopsis [Permutes the variables of an ADD.] + + Description [Given a permutation in array permut, creates a new ADD + with permuted variables. There should be an entry in array permut + for each variable in the manager. The i-th entry of permut holds the + index of the variable that is to substitute the i-th + variable. Returns a pointer to the resulting ADD if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddPermute Cudd_addSwapVariables] + +******************************************************************************/ +DdNode * +Cudd_addPermute( + DdManager * manager, + DdNode * node, + int * permut) +{ + DdHashTable *table; + DdNode *res; + + do { + manager->reordered = 0; + table = cuddHashTableInit(manager,1,2); + if (table == NULL) return(NULL); + /* Recursively solve the problem. */ + res = cuddAddPermuteRecur(manager,table,node,permut); + if (res != NULL) cuddRef(res); + /* Dispose of local cache. */ + cuddHashTableQuit(table); + } while (manager->reordered == 1); + + if (res != NULL) cuddDeref(res); + return(res); + +} /* end of Cudd_addPermute */ + + +/**Function******************************************************************** + + Synopsis [Swaps two sets of variables of the same size (x and y) in + the ADD f.] + + Description [Swaps two sets of variables of the same size (x and y) in + the ADD f. The size is given by n. The two sets of variables are + assumed to be disjoint. Returns a pointer to the resulting ADD if + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addPermute Cudd_bddSwapVariables] + +******************************************************************************/ +DdNode * +Cudd_addSwapVariables( + DdManager * dd, + DdNode * f, + DdNode ** x, + DdNode ** y, + int n) +{ + DdNode *swapped; + int i, j, k; + int *permut; + + permut = ALLOC(int,dd->size); + if (permut == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + for (i = 0; i < dd->size; i++) permut[i] = i; + for (i = 0; i < n; i++) { + j = x[i]->index; + k = y[i]->index; + permut[j] = k; + permut[k] = j; + } + + swapped = Cudd_addPermute(dd,f,permut); + FREE(permut); + + return(swapped); + +} /* end of Cudd_addSwapVariables */ + + +/**Function******************************************************************** + + Synopsis [Permutes the variables of a BDD.] + + Description [Given a permutation in array permut, creates a new BDD + with permuted variables. There should be an entry in array permut + for each variable in the manager. The i-th entry of permut holds the + index of the variable that is to substitute the i-th variable. + Returns a pointer to the resulting BDD if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addPermute Cudd_bddSwapVariables] + +******************************************************************************/ +DdNode * +Cudd_bddPermute( + DdManager * manager, + DdNode * node, + int * permut) +{ + DdHashTable *table; + DdNode *res; + + do { + manager->reordered = 0; + table = cuddHashTableInit(manager,1,2); + if (table == NULL) return(NULL); + res = cuddBddPermuteRecur(manager,table,node,permut); + if (res != NULL) cuddRef(res); + /* Dispose of local cache. */ + cuddHashTableQuit(table); + + } while (manager->reordered == 1); + + if (res != NULL) cuddDeref(res); + return(res); + +} /* end of Cudd_bddPermute */ + + +/**Function******************************************************************** + + Synopsis [Remaps the variables of a BDD using the default variable map.] + + Description [Remaps the variables of a BDD using the default + variable map. A typical use of this function is to swap two sets of + variables. The variable map must be registered with Cudd_SetVarMap. + Returns a pointer to the resulting BDD if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddPermute Cudd_bddSwapVariables Cudd_SetVarMap] + +******************************************************************************/ +DdNode * +Cudd_bddVarMap( + DdManager * manager /* DD manager */, + DdNode * f /* function in which to remap variables */) +{ + DdNode *res; + + if (manager->map == NULL) return(NULL); + do { + manager->reordered = 0; + res = cuddBddVarMapRecur(manager, f); + } while (manager->reordered == 1); + + return(res); + +} /* end of Cudd_bddVarMap */ + + +/**Function******************************************************************** + + Synopsis [Registers a variable mapping with the manager.] + + Description [Registers with the manager a variable mapping described + by two sets of variables. This variable mapping is then used by + functions like Cudd_bddVarMap. This function is convenient for + those applications that perform the same mapping several times. + However, if several different permutations are used, it may be more + efficient not to rely on the registered mapping, because changing + mapping causes the cache to be cleared. (The initial setting, + however, does not clear the cache.) The two sets of variables (x and + y) must have the same size (x and y). The size is given by n. The + two sets of variables are normally disjoint, but this restriction is + not imposeded by the function. When new variables are created, the + map is automatically extended (each new variable maps to + itself). The typical use, however, is to wait until all variables + are created, and then create the map. Returns 1 if the mapping is + successfully registered with the manager; 0 otherwise.] + + SideEffects [Modifies the manager. May clear the cache.] + + SeeAlso [Cudd_bddVarMap Cudd_bddPermute Cudd_bddSwapVariables] + +******************************************************************************/ +int +Cudd_SetVarMap ( + DdManager *manager /* DD manager */, + DdNode **x /* first array of variables */, + DdNode **y /* second array of variables */, + int n /* length of both arrays */) +{ + int i; + + if (manager->map != NULL) { + cuddCacheFlush(manager); + } else { + manager->map = ALLOC(int,manager->maxSize); + if (manager->map == NULL) { + manager->errorCode = CUDD_MEMORY_OUT; + return(0); + } + manager->memused += sizeof(int) * manager->maxSize; + } + /* Initialize the map to the identity. */ + for (i = 0; i < manager->size; i++) { + manager->map[i] = i; + } + /* Create the map. */ + for (i = 0; i < n; i++) { + manager->map[x[i]->index] = y[i]->index; + manager->map[y[i]->index] = x[i]->index; + } + return(1); + +} /* end of Cudd_SetVarMap */ + + +/**Function******************************************************************** + + Synopsis [Swaps two sets of variables of the same size (x and y) in + the BDD f.] + + Description [Swaps two sets of variables of the same size (x and y) + in the BDD f. The size is given by n. The two sets of variables are + assumed to be disjoint. Returns a pointer to the resulting BDD if + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddPermute Cudd_addSwapVariables] + +******************************************************************************/ +DdNode * +Cudd_bddSwapVariables( + DdManager * dd, + DdNode * f, + DdNode ** x, + DdNode ** y, + int n) +{ + DdNode *swapped; + int i, j, k; + int *permut; + + permut = ALLOC(int,dd->size); + if (permut == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + for (i = 0; i < dd->size; i++) permut[i] = i; + for (i = 0; i < n; i++) { + j = x[i]->index; + k = y[i]->index; + permut[j] = k; + permut[k] = j; + } + + swapped = Cudd_bddPermute(dd,f,permut); + FREE(permut); + + return(swapped); + +} /* end of Cudd_bddSwapVariables */ + + +/**Function******************************************************************** + + Synopsis [Rearranges a set of variables in the BDD B.] + + Description [Rearranges a set of variables in the BDD B. The size of + the set is given by n. This procedure is intended for the + `randomization' of the priority functions. Returns a pointer to the + BDD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddPermute Cudd_bddSwapVariables + Cudd_Dxygtdxz Cudd_Dxygtdyz Cudd_PrioritySelect] + +******************************************************************************/ +DdNode * +Cudd_bddAdjPermuteX( + DdManager * dd, + DdNode * B, + DdNode ** x, + int n) +{ + DdNode *swapped; + int i, j, k; + int *permut; + + permut = ALLOC(int,dd->size); + if (permut == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + for (i = 0; i < dd->size; i++) permut[i] = i; + for (i = 0; i < n-2; i += 3) { + j = x[i]->index; + k = x[i+1]->index; + permut[j] = k; + permut[k] = j; + } + + swapped = Cudd_bddPermute(dd,B,permut); + FREE(permut); + + return(swapped); + +} /* end of Cudd_bddAdjPermuteX */ + + +/**Function******************************************************************** + + Synopsis [Composes an ADD with a vector of 0-1 ADDs.] + + Description [Given a vector of 0-1 ADDs, creates a new ADD by + substituting the 0-1 ADDs for the variables of the ADD f. There + should be an entry in vector for each variable in the manager. + If no substitution is sought for a given variable, the corresponding + projection function should be specified in the vector. + This function implements simultaneous composition. + Returns a pointer to the resulting ADD if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addNonSimCompose Cudd_addPermute Cudd_addCompose + Cudd_bddVectorCompose] + +******************************************************************************/ +DdNode * +Cudd_addVectorCompose( + DdManager * dd, + DdNode * f, + DdNode ** vector) +{ + DdHashTable *table; + DdNode *res; + int deepest; + int i; + + do { + dd->reordered = 0; + /* Initialize local cache. */ + table = cuddHashTableInit(dd,1,2); + if (table == NULL) return(NULL); + + /* Find deepest real substitution. */ + for (deepest = dd->size - 1; deepest >= 0; deepest--) { + i = dd->invperm[deepest]; + if (!ddIsIthAddVar(dd,vector[i],i)) { + break; + } + } + + /* Recursively solve the problem. */ + res = cuddAddVectorComposeRecur(dd,table,f,vector,deepest); + if (res != NULL) cuddRef(res); + + /* Dispose of local cache. */ + cuddHashTableQuit(table); + } while (dd->reordered == 1); + + if (res != NULL) cuddDeref(res); + return(res); + +} /* end of Cudd_addVectorCompose */ + + +/**Function******************************************************************** + + Synopsis [Composes an ADD with a vector of ADDs.] + + Description [Given a vector of ADDs, creates a new ADD by substituting the + ADDs for the variables of the ADD f. vectorOn contains ADDs to be substituted + for the x_v and vectorOff the ADDs to be substituted for x_v'. There should + be an entry in vector for each variable in the manager. If no substitution + is sought for a given variable, the corresponding projection function should + be specified in the vector. This function implements simultaneous + composition. Returns a pointer to the resulting ADD if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addVectorCompose Cudd_addNonSimCompose Cudd_addPermute + Cudd_addCompose Cudd_bddVectorCompose] + +******************************************************************************/ +DdNode * +Cudd_addGeneralVectorCompose( + DdManager * dd, + DdNode * f, + DdNode ** vectorOn, + DdNode ** vectorOff) +{ + DdHashTable *table; + DdNode *res; + int deepest; + int i; + + do { + dd->reordered = 0; + /* Initialize local cache. */ + table = cuddHashTableInit(dd,1,2); + if (table == NULL) return(NULL); + + /* Find deepest real substitution. */ + for (deepest = dd->size - 1; deepest >= 0; deepest--) { + i = dd->invperm[deepest]; + if (!ddIsIthAddVarPair(dd,vectorOn[i],vectorOff[i],i)) { + break; + } + } + + /* Recursively solve the problem. */ + res = cuddAddGeneralVectorComposeRecur(dd,table,f,vectorOn, + vectorOff,deepest); + if (res != NULL) cuddRef(res); + + /* Dispose of local cache. */ + cuddHashTableQuit(table); + } while (dd->reordered == 1); + + if (res != NULL) cuddDeref(res); + return(res); + +} /* end of Cudd_addGeneralVectorCompose */ + + +/**Function******************************************************************** + + Synopsis [Composes an ADD with a vector of 0-1 ADDs.] + + Description [Given a vector of 0-1 ADDs, creates a new ADD by + substituting the 0-1 ADDs for the variables of the ADD f. There + should be an entry in vector for each variable in the manager. + This function implements non-simultaneous composition. If any of the + functions being composed depends on any of the variables being + substituted, then the result depends on the order of composition, + which in turn depends on the variable order: The variables farther from + the roots in the order are substituted first. + Returns a pointer to the resulting ADD if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addVectorCompose Cudd_addPermute Cudd_addCompose] + +******************************************************************************/ +DdNode * +Cudd_addNonSimCompose( + DdManager * dd, + DdNode * f, + DdNode ** vector) +{ + DdNode *cube, *key, *var, *tmp, *piece; + DdNode *res; + int i, lastsub; + + /* The cache entry for this function is composed of three parts: + ** f itself, the replacement relation, and the cube of the + ** variables being substituted. + ** The replacement relation is the product of the terms (yi EXNOR gi). + ** This apporach allows us to use the global cache for this function, + ** with great savings in memory with respect to using arrays for the + ** cache entries. + ** First we build replacement relation and cube of substituted + ** variables from the vector specifying the desired composition. + */ + key = DD_ONE(dd); + cuddRef(key); + cube = DD_ONE(dd); + cuddRef(cube); + for (i = (int) dd->size - 1; i >= 0; i--) { + if (ddIsIthAddVar(dd,vector[i],(unsigned int)i)) { + continue; + } + var = Cudd_addIthVar(dd,i); + if (var == NULL) { + Cudd_RecursiveDeref(dd,key); + Cudd_RecursiveDeref(dd,cube); + return(NULL); + } + cuddRef(var); + /* Update cube. */ + tmp = Cudd_addApply(dd,Cudd_addTimes,var,cube); + if (tmp == NULL) { + Cudd_RecursiveDeref(dd,key); + Cudd_RecursiveDeref(dd,cube); + Cudd_RecursiveDeref(dd,var); + return(NULL); + } + cuddRef(tmp); + Cudd_RecursiveDeref(dd,cube); + cube = tmp; + /* Update replacement relation. */ + piece = Cudd_addApply(dd,Cudd_addXnor,var,vector[i]); + if (piece == NULL) { + Cudd_RecursiveDeref(dd,key); + Cudd_RecursiveDeref(dd,var); + return(NULL); + } + cuddRef(piece); + Cudd_RecursiveDeref(dd,var); + tmp = Cudd_addApply(dd,Cudd_addTimes,key,piece); + if (tmp == NULL) { + Cudd_RecursiveDeref(dd,key); + Cudd_RecursiveDeref(dd,piece); + return(NULL); + } + cuddRef(tmp); + Cudd_RecursiveDeref(dd,key); + Cudd_RecursiveDeref(dd,piece); + key = tmp; + } + + /* Now try composition, until no reordering occurs. */ + do { + /* Find real substitution with largest index. */ + for (lastsub = dd->size - 1; lastsub >= 0; lastsub--) { + if (!ddIsIthAddVar(dd,vector[lastsub],(unsigned int)lastsub)) { + break; + } + } + + /* Recursively solve the problem. */ + dd->reordered = 0; + res = cuddAddNonSimComposeRecur(dd,f,vector,key,cube,lastsub+1); + if (res != NULL) cuddRef(res); + + } while (dd->reordered == 1); + + Cudd_RecursiveDeref(dd,key); + Cudd_RecursiveDeref(dd,cube); + if (res != NULL) cuddDeref(res); + return(res); + +} /* end of Cudd_addNonSimCompose */ + + +/**Function******************************************************************** + + Synopsis [Composes a BDD with a vector of BDDs.] + + Description [Given a vector of BDDs, creates a new BDD by + substituting the BDDs for the variables of the BDD f. There + should be an entry in vector for each variable in the manager. + If no substitution is sought for a given variable, the corresponding + projection function should be specified in the vector. + This function implements simultaneous composition. + Returns a pointer to the resulting BDD if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddPermute Cudd_bddCompose Cudd_addVectorCompose] + +******************************************************************************/ +DdNode * +Cudd_bddVectorCompose( + DdManager * dd, + DdNode * f, + DdNode ** vector) +{ + DdHashTable *table; + DdNode *res; + int deepest; + int i; + + do { + dd->reordered = 0; + /* Initialize local cache. */ + table = cuddHashTableInit(dd,1,2); + if (table == NULL) return(NULL); + + /* Find deepest real substitution. */ + for (deepest = dd->size - 1; deepest >= 0; deepest--) { + i = dd->invperm[deepest]; + if (vector[i] != dd->vars[i]) { + break; + } + } + + /* Recursively solve the problem. */ + res = cuddBddVectorComposeRecur(dd,table,f,vector, deepest); + if (res != NULL) cuddRef(res); + + /* Dispose of local cache. */ + cuddHashTableQuit(table); + } while (dd->reordered == 1); + + if (res != NULL) cuddDeref(res); + return(res); + +} /* end of Cudd_bddVectorCompose */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_bddCompose.] + + Description [Performs the recursive step of Cudd_bddCompose. + Exploits the fact that the composition of f' with g + produces the complement of the composition of f with g to better + utilize the cache. Returns the composed BDD if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddCompose] + +******************************************************************************/ +DdNode * +cuddBddComposeRecur( + DdManager * dd, + DdNode * f, + DdNode * g, + DdNode * proj) +{ + DdNode *F, *G, *f1, *f0, *g1, *g0, *r, *t, *e; + unsigned int v, topf, topg, topindex; + int comple; + + statLine(dd); + v = dd->perm[proj->index]; + F = Cudd_Regular(f); + topf = cuddI(dd,F->index); + + /* Terminal case. Subsumes the test for constant f. */ + if (topf > v) return(f); + + /* We solve the problem for a regular pointer, and then complement + ** the result if the pointer was originally complemented. + */ + comple = Cudd_IsComplement(f); + + /* Check cache. */ + r = cuddCacheLookup(dd,DD_BDD_COMPOSE_RECUR_TAG,F,g,proj); + if (r != NULL) { + return(Cudd_NotCond(r,comple)); + } + + if (topf == v) { + /* Compose. */ + f1 = cuddT(F); + f0 = cuddE(F); + r = cuddBddIteRecur(dd, g, f1, f0); + if (r == NULL) return(NULL); + } else { + /* Compute cofactors of f and g. Remember the index of the top + ** variable. + */ + G = Cudd_Regular(g); + topg = cuddI(dd,G->index); + if (topf > topg) { + topindex = G->index; + f1 = f0 = F; + } else { + topindex = F->index; + f1 = cuddT(F); + f0 = cuddE(F); + } + if (topg > topf) { + g1 = g0 = g; + } else { + g1 = cuddT(G); + g0 = cuddE(G); + if (g != G) { + g1 = Cudd_Not(g1); + g0 = Cudd_Not(g0); + } + } + /* Recursive step. */ + t = cuddBddComposeRecur(dd, f1, g1, proj); + if (t == NULL) return(NULL); + cuddRef(t); + e = cuddBddComposeRecur(dd, f0, g0, proj); + if (e == NULL) { + Cudd_IterDerefBdd(dd, t); + return(NULL); + } + cuddRef(e); + + r = cuddBddIteRecur(dd, dd->vars[topindex], t, e); + if (r == NULL) { + Cudd_IterDerefBdd(dd, t); + Cudd_IterDerefBdd(dd, e); + return(NULL); + } + cuddRef(r); + Cudd_IterDerefBdd(dd, t); /* t & e not necessarily part of r */ + Cudd_IterDerefBdd(dd, e); + cuddDeref(r); + } + + cuddCacheInsert(dd,DD_BDD_COMPOSE_RECUR_TAG,F,g,proj,r); + + return(Cudd_NotCond(r,comple)); + +} /* end of cuddBddComposeRecur */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_addCompose.] + + Description [Performs the recursive step of Cudd_addCompose. + Returns the composed BDD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addCompose] + +******************************************************************************/ +DdNode * +cuddAddComposeRecur( + DdManager * dd, + DdNode * f, + DdNode * g, + DdNode * proj) +{ + DdNode *f1, *f0, *g1, *g0, *r, *t, *e; + unsigned int v, topf, topg, topindex; + + statLine(dd); + v = dd->perm[proj->index]; + topf = cuddI(dd,f->index); + + /* Terminal case. Subsumes the test for constant f. */ + if (topf > v) return(f); + + /* Check cache. */ + r = cuddCacheLookup(dd,DD_ADD_COMPOSE_RECUR_TAG,f,g,proj); + if (r != NULL) { + return(r); + } + + if (topf == v) { + /* Compose. */ + f1 = cuddT(f); + f0 = cuddE(f); + r = cuddAddIteRecur(dd, g, f1, f0); + if (r == NULL) return(NULL); + } else { + /* Compute cofactors of f and g. Remember the index of the top + ** variable. + */ + topg = cuddI(dd,g->index); + if (topf > topg) { + topindex = g->index; + f1 = f0 = f; + } else { + topindex = f->index; + f1 = cuddT(f); + f0 = cuddE(f); + } + if (topg > topf) { + g1 = g0 = g; + } else { + g1 = cuddT(g); + g0 = cuddE(g); + } + /* Recursive step. */ + t = cuddAddComposeRecur(dd, f1, g1, proj); + if (t == NULL) return(NULL); + cuddRef(t); + e = cuddAddComposeRecur(dd, f0, g0, proj); + if (e == NULL) { + Cudd_RecursiveDeref(dd, t); + return(NULL); + } + cuddRef(e); + + if (t == e) { + r = t; + } else { + r = cuddUniqueInter(dd, (int) topindex, t, e); + if (r == NULL) { + Cudd_RecursiveDeref(dd, t); + Cudd_RecursiveDeref(dd, e); + return(NULL); + } + } + cuddDeref(t); + cuddDeref(e); + } + + cuddCacheInsert(dd,DD_ADD_COMPOSE_RECUR_TAG,f,g,proj,r); + + return(r); + +} /* end of cuddAddComposeRecur */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_addPermute.] + + Description [ Recursively puts the ADD in the order given in the + array permut. Checks for trivial cases to terminate recursion, then + splits on the children of this node. Once the solutions for the + children are obtained, it puts into the current position the node + from the rest of the ADD that should be here. Then returns this ADD. + The key here is that the node being visited is NOT put in its proper + place by this instance, but rather is switched when its proper + position is reached in the recursion tree.

+ The DdNode * that is returned is the same ADD as passed in as node, + but in the new order.] + + SideEffects [None] + + SeeAlso [Cudd_addPermute cuddBddPermuteRecur] + +******************************************************************************/ +static DdNode * +cuddAddPermuteRecur( + DdManager * manager /* DD manager */, + DdHashTable * table /* computed table */, + DdNode * node /* ADD to be reordered */, + int * permut /* permutation array */) +{ + DdNode *T,*E; + DdNode *res,*var; + int index; + + statLine(manager); + /* Check for terminal case of constant node. */ + if (cuddIsConstant(node)) { + return(node); + } + + /* If problem already solved, look up answer and return. */ + if (node->ref != 1 && (res = cuddHashTableLookup1(table,node)) != NULL) { +#ifdef DD_DEBUG + addPermuteRecurHits++; +#endif + return(res); + } + + /* Split and recur on children of this node. */ + T = cuddAddPermuteRecur(manager,table,cuddT(node),permut); + if (T == NULL) return(NULL); + cuddRef(T); + E = cuddAddPermuteRecur(manager,table,cuddE(node),permut); + if (E == NULL) { + Cudd_RecursiveDeref(manager, T); + return(NULL); + } + cuddRef(E); + + /* Move variable that should be in this position to this position + ** by creating a single var ADD for that variable, and calling + ** cuddAddIteRecur with the T and E we just created. + */ + index = permut[node->index]; + var = cuddUniqueInter(manager,index,DD_ONE(manager),DD_ZERO(manager)); + if (var == NULL) return(NULL); + cuddRef(var); + res = cuddAddIteRecur(manager,var,T,E); + if (res == NULL) { + Cudd_RecursiveDeref(manager,var); + Cudd_RecursiveDeref(manager, T); + Cudd_RecursiveDeref(manager, E); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(manager,var); + Cudd_RecursiveDeref(manager, T); + Cudd_RecursiveDeref(manager, E); + + /* Do not keep the result if the reference count is only 1, since + ** it will not be visited again. + */ + if (node->ref != 1) { + ptrint fanout = (ptrint) node->ref; + cuddSatDec(fanout); + if (!cuddHashTableInsert1(table,node,res,fanout)) { + Cudd_RecursiveDeref(manager, res); + return(NULL); + } + } + cuddDeref(res); + return(res); + +} /* end of cuddAddPermuteRecur */ + + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_bddPermute.] + + Description [ Recursively puts the BDD in the order given in the array permut. + Checks for trivial cases to terminate recursion, then splits on the + children of this node. Once the solutions for the children are + obtained, it puts into the current position the node from the rest of + the BDD that should be here. Then returns this BDD. + The key here is that the node being visited is NOT put in its proper + place by this instance, but rather is switched when its proper position + is reached in the recursion tree.

+ The DdNode * that is returned is the same BDD as passed in as node, + but in the new order.] + + SideEffects [None] + + SeeAlso [Cudd_bddPermute cuddAddPermuteRecur] + +******************************************************************************/ +static DdNode * +cuddBddPermuteRecur( + DdManager * manager /* DD manager */, + DdHashTable * table /* computed table */, + DdNode * node /* BDD to be reordered */, + int * permut /* permutation array */) +{ + DdNode *N,*T,*E; + DdNode *res; + int index; + + statLine(manager); + N = Cudd_Regular(node); + + /* Check for terminal case of constant node. */ + if (cuddIsConstant(N)) { + return(node); + } + + /* If problem already solved, look up answer and return. */ + if (N->ref != 1 && (res = cuddHashTableLookup1(table,N)) != NULL) { +#ifdef DD_DEBUG + bddPermuteRecurHits++; +#endif + return(Cudd_NotCond(res,N != node)); + } + + /* Split and recur on children of this node. */ + T = cuddBddPermuteRecur(manager,table,cuddT(N),permut); + if (T == NULL) return(NULL); + cuddRef(T); + E = cuddBddPermuteRecur(manager,table,cuddE(N),permut); + if (E == NULL) { + Cudd_IterDerefBdd(manager, T); + return(NULL); + } + cuddRef(E); + + /* Move variable that should be in this position to this position + ** by retrieving the single var BDD for that variable, and calling + ** cuddBddIteRecur with the T and E we just created. + */ + index = permut[N->index]; + res = cuddBddIteRecur(manager,manager->vars[index],T,E); + if (res == NULL) { + Cudd_IterDerefBdd(manager, T); + Cudd_IterDerefBdd(manager, E); + return(NULL); + } + cuddRef(res); + Cudd_IterDerefBdd(manager, T); + Cudd_IterDerefBdd(manager, E); + + /* Do not keep the result if the reference count is only 1, since + ** it will not be visited again. + */ + if (N->ref != 1) { + ptrint fanout = (ptrint) N->ref; + cuddSatDec(fanout); + if (!cuddHashTableInsert1(table,N,res,fanout)) { + Cudd_IterDerefBdd(manager, res); + return(NULL); + } + } + cuddDeref(res); + return(Cudd_NotCond(res,N != node)); + +} /* end of cuddBddPermuteRecur */ + + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_bddVarMap.] + + Description [Implements the recursive step of Cudd_bddVarMap. + Returns a pointer to the result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddVarMap] + +******************************************************************************/ +static DdNode * +cuddBddVarMapRecur( + DdManager *manager /* DD manager */, + DdNode *f /* BDD to be remapped */) +{ + DdNode *F, *T, *E; + DdNode *res; + int index; + + statLine(manager); + F = Cudd_Regular(f); + + /* Check for terminal case of constant node. */ + if (cuddIsConstant(F)) { + return(f); + } + + /* If problem already solved, look up answer and return. */ + if (F->ref != 1 && + (res = cuddCacheLookup1(manager,Cudd_bddVarMap,F)) != NULL) { + return(Cudd_NotCond(res,F != f)); + } + + /* Split and recur on children of this node. */ + T = cuddBddVarMapRecur(manager,cuddT(F)); + if (T == NULL) return(NULL); + cuddRef(T); + E = cuddBddVarMapRecur(manager,cuddE(F)); + if (E == NULL) { + Cudd_IterDerefBdd(manager, T); + return(NULL); + } + cuddRef(E); + + /* Move variable that should be in this position to this position + ** by retrieving the single var BDD for that variable, and calling + ** cuddBddIteRecur with the T and E we just created. + */ + index = manager->map[F->index]; + res = cuddBddIteRecur(manager,manager->vars[index],T,E); + if (res == NULL) { + Cudd_IterDerefBdd(manager, T); + Cudd_IterDerefBdd(manager, E); + return(NULL); + } + cuddRef(res); + Cudd_IterDerefBdd(manager, T); + Cudd_IterDerefBdd(manager, E); + + /* Do not keep the result if the reference count is only 1, since + ** it will not be visited again. + */ + if (F->ref != 1) { + cuddCacheInsert1(manager,Cudd_bddVarMap,F,res); + } + cuddDeref(res); + return(Cudd_NotCond(res,F != f)); + +} /* end of cuddBddVarMapRecur */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_addVectorCompose.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static DdNode * +cuddAddVectorComposeRecur( + DdManager * dd /* DD manager */, + DdHashTable * table /* computed table */, + DdNode * f /* ADD in which to compose */, + DdNode ** vector /* functions to substitute */, + int deepest /* depth of deepest substitution */) +{ + DdNode *T,*E; + DdNode *res; + + statLine(dd); + /* If we are past the deepest substitution, return f. */ + if (cuddI(dd,f->index) > deepest) { + return(f); + } + + if ((res = cuddHashTableLookup1(table,f)) != NULL) { +#ifdef DD_DEBUG + addVectorComposeHits++; +#endif + return(res); + } + + /* Split and recur on children of this node. */ + T = cuddAddVectorComposeRecur(dd,table,cuddT(f),vector,deepest); + if (T == NULL) return(NULL); + cuddRef(T); + E = cuddAddVectorComposeRecur(dd,table,cuddE(f),vector,deepest); + if (E == NULL) { + Cudd_RecursiveDeref(dd, T); + return(NULL); + } + cuddRef(E); + + /* Retrieve the 0-1 ADD for the current top variable and call + ** cuddAddIteRecur with the T and E we just created. + */ + res = cuddAddIteRecur(dd,vector[f->index],T,E); + if (res == NULL) { + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + + /* Do not keep the result if the reference count is only 1, since + ** it will not be visited again + */ + if (f->ref != 1) { + ptrint fanout = (ptrint) f->ref; + cuddSatDec(fanout); + if (!cuddHashTableInsert1(table,f,res,fanout)) { + Cudd_RecursiveDeref(dd, res); + return(NULL); + } + } + cuddDeref(res); + return(res); + +} /* end of cuddAddVectorComposeRecur */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_addGeneralVectorCompose.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static DdNode * +cuddAddGeneralVectorComposeRecur( + DdManager * dd /* DD manager */, + DdHashTable * table /* computed table */, + DdNode * f /* ADD in which to compose */, + DdNode ** vectorOn /* functions to substitute for x_i */, + DdNode ** vectorOff /* functions to substitute for x_i' */, + int deepest /* depth of deepest substitution */) +{ + DdNode *T,*E,*t,*e; + DdNode *res; + + /* If we are past the deepest substitution, return f. */ + if (cuddI(dd,f->index) > deepest) { + return(f); + } + + if ((res = cuddHashTableLookup1(table,f)) != NULL) { +#ifdef DD_DEBUG + addGeneralVectorComposeHits++; +#endif + return(res); + } + + /* Split and recur on children of this node. */ + T = cuddAddGeneralVectorComposeRecur(dd,table,cuddT(f), + vectorOn,vectorOff,deepest); + if (T == NULL) return(NULL); + cuddRef(T); + E = cuddAddGeneralVectorComposeRecur(dd,table,cuddE(f), + vectorOn,vectorOff,deepest); + if (E == NULL) { + Cudd_RecursiveDeref(dd, T); + return(NULL); + } + cuddRef(E); + + /* Retrieve the compose ADDs for the current top variable and call + ** cuddAddApplyRecur with the T and E we just created. + */ + t = cuddAddApplyRecur(dd,Cudd_addTimes,vectorOn[f->index],T); + if (t == NULL) { + Cudd_RecursiveDeref(dd,T); + Cudd_RecursiveDeref(dd,E); + return(NULL); + } + cuddRef(t); + e = cuddAddApplyRecur(dd,Cudd_addTimes,vectorOff[f->index],E); + if (e == NULL) { + Cudd_RecursiveDeref(dd,T); + Cudd_RecursiveDeref(dd,E); + Cudd_RecursiveDeref(dd,t); + return(NULL); + } + cuddRef(e); + res = cuddAddApplyRecur(dd,Cudd_addPlus,t,e); + if (res == NULL) { + Cudd_RecursiveDeref(dd,T); + Cudd_RecursiveDeref(dd,E); + Cudd_RecursiveDeref(dd,t); + Cudd_RecursiveDeref(dd,e); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(dd,T); + Cudd_RecursiveDeref(dd,E); + Cudd_RecursiveDeref(dd,t); + Cudd_RecursiveDeref(dd,e); + + /* Do not keep the result if the reference count is only 1, since + ** it will not be visited again + */ + if (f->ref != 1) { + ptrint fanout = (ptrint) f->ref; + cuddSatDec(fanout); + if (!cuddHashTableInsert1(table,f,res,fanout)) { + Cudd_RecursiveDeref(dd, res); + return(NULL); + } + } + cuddDeref(res); + return(res); + +} /* end of cuddAddGeneralVectorComposeRecur */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_addNonSimCompose.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static DdNode * +cuddAddNonSimComposeRecur( + DdManager * dd, + DdNode * f, + DdNode ** vector, + DdNode * key, + DdNode * cube, + int lastsub) +{ + DdNode *f1, *f0, *key1, *key0, *cube1, *var; + DdNode *T,*E; + DdNode *r; + unsigned int top, topf, topk, topc; + unsigned int index; + int i; + DdNode **vect1; + DdNode **vect0; + + statLine(dd); + /* If we are past the deepest substitution, return f. */ + if (cube == DD_ONE(dd) || cuddIsConstant(f)) { + return(f); + } + + /* If problem already solved, look up answer and return. */ + r = cuddCacheLookup(dd,DD_ADD_NON_SIM_COMPOSE_TAG,f,key,cube); + if (r != NULL) { + return(r); + } + + /* Find top variable. we just need to look at f, key, and cube, + ** because all the varibles in the gi are in key. + */ + topf = cuddI(dd,f->index); + topk = cuddI(dd,key->index); + top = ddMin(topf,topk); + topc = cuddI(dd,cube->index); + top = ddMin(top,topc); + index = dd->invperm[top]; + + /* Compute the cofactors. */ + if (topf == top) { + f1 = cuddT(f); + f0 = cuddE(f); + } else { + f1 = f0 = f; + } + if (topc == top) { + cube1 = cuddT(cube); + /* We want to eliminate vector[index] from key. Otherwise + ** cache performance is severely affected. Hence we + ** existentially quantify the variable with index "index" from key. + */ + var = Cudd_addIthVar(dd, (int) index); + if (var == NULL) { + return(NULL); + } + cuddRef(var); + key1 = cuddAddExistAbstractRecur(dd, key, var); + if (key1 == NULL) { + Cudd_RecursiveDeref(dd,var); + return(NULL); + } + cuddRef(key1); + Cudd_RecursiveDeref(dd,var); + key0 = key1; + } else { + cube1 = cube; + if (topk == top) { + key1 = cuddT(key); + key0 = cuddE(key); + } else { + key1 = key0 = key; + } + cuddRef(key1); + } + + /* Allocate two new vectors for the cofactors of vector. */ + vect1 = ALLOC(DdNode *,lastsub); + if (vect1 == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + Cudd_RecursiveDeref(dd,key1); + return(NULL); + } + vect0 = ALLOC(DdNode *,lastsub); + if (vect0 == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + Cudd_RecursiveDeref(dd,key1); + FREE(vect1); + return(NULL); + } + + /* Cofactor the gi. Eliminate vect1[index] and vect0[index], because + ** we do not need them. + */ + for (i = 0; i < lastsub; i++) { + DdNode *gi = vector[i]; + if (gi == NULL) { + vect1[i] = vect0[i] = NULL; + } else if (gi->index == index) { + vect1[i] = cuddT(gi); + vect0[i] = cuddE(gi); + } else { + vect1[i] = vect0[i] = gi; + } + } + vect1[index] = vect0[index] = NULL; + + /* Recur on children. */ + T = cuddAddNonSimComposeRecur(dd,f1,vect1,key1,cube1,lastsub); + FREE(vect1); + if (T == NULL) { + Cudd_RecursiveDeref(dd,key1); + FREE(vect0); + return(NULL); + } + cuddRef(T); + E = cuddAddNonSimComposeRecur(dd,f0,vect0,key0,cube1,lastsub); + FREE(vect0); + if (E == NULL) { + Cudd_RecursiveDeref(dd,key1); + Cudd_RecursiveDeref(dd,T); + return(NULL); + } + cuddRef(E); + Cudd_RecursiveDeref(dd,key1); + + /* Retrieve the 0-1 ADD for the current top variable from vector, + ** and call cuddAddIteRecur with the T and E we just created. + */ + r = cuddAddIteRecur(dd,vector[index],T,E); + if (r == NULL) { + Cudd_RecursiveDeref(dd,T); + Cudd_RecursiveDeref(dd,E); + return(NULL); + } + cuddRef(r); + Cudd_RecursiveDeref(dd,T); + Cudd_RecursiveDeref(dd,E); + cuddDeref(r); + + /* Store answer to trim recursion. */ + cuddCacheInsert(dd,DD_ADD_NON_SIM_COMPOSE_TAG,f,key,cube,r); + + return(r); + +} /* end of cuddAddNonSimComposeRecur */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_bddVectorCompose.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static DdNode * +cuddBddVectorComposeRecur( + DdManager * dd /* DD manager */, + DdHashTable * table /* computed table */, + DdNode * f /* BDD in which to compose */, + DdNode ** vector /* functions to be composed */, + int deepest /* depth of the deepest substitution */) +{ + DdNode *F,*T,*E; + DdNode *res; + + statLine(dd); + F = Cudd_Regular(f); + + /* If we are past the deepest substitution, return f. */ + if (cuddI(dd,F->index) > deepest) { + return(f); + } + + /* If problem already solved, look up answer and return. */ + if ((res = cuddHashTableLookup1(table,F)) != NULL) { +#ifdef DD_DEBUG + bddVectorComposeHits++; +#endif + return(Cudd_NotCond(res,F != f)); + } + + /* Split and recur on children of this node. */ + T = cuddBddVectorComposeRecur(dd,table,cuddT(F),vector, deepest); + if (T == NULL) return(NULL); + cuddRef(T); + E = cuddBddVectorComposeRecur(dd,table,cuddE(F),vector, deepest); + if (E == NULL) { + Cudd_IterDerefBdd(dd, T); + return(NULL); + } + cuddRef(E); + + /* Call cuddBddIteRecur with the BDD that replaces the current top + ** variable and the T and E we just created. + */ + res = cuddBddIteRecur(dd,vector[F->index],T,E); + if (res == NULL) { + Cudd_IterDerefBdd(dd, T); + Cudd_IterDerefBdd(dd, E); + return(NULL); + } + cuddRef(res); + Cudd_IterDerefBdd(dd, T); + Cudd_IterDerefBdd(dd, E); + + /* Do not keep the result if the reference count is only 1, since + ** it will not be visited again. + */ + if (F->ref != 1) { + ptrint fanout = (ptrint) F->ref; + cuddSatDec(fanout); + if (!cuddHashTableInsert1(table,F,res,fanout)) { + Cudd_IterDerefBdd(dd, res); + return(NULL); + } + } + cuddDeref(res); + return(Cudd_NotCond(res,F != f)); + +} /* end of cuddBddVectorComposeRecur */ + + +/**Function******************************************************************** + + Synopsis [Comparison of a function to the i-th ADD variable.] + + Description [Comparison of a function to the i-th ADD variable. Returns 1 if + the function is the i-th ADD variable; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DD_INLINE +static int +ddIsIthAddVar( + DdManager * dd, + DdNode * f, + unsigned int i) +{ + return(f->index == i && cuddT(f) == DD_ONE(dd) && cuddE(f) == DD_ZERO(dd)); + +} /* end of ddIsIthAddVar */ + + +/**Function******************************************************************** + + Synopsis [Comparison of a pair of functions to the i-th ADD variable.] + + Description [Comparison of a pair of functions to the i-th ADD + variable. Returns 1 if the functions are the i-th ADD variable and its + complement; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DD_INLINE +static int +ddIsIthAddVarPair( + DdManager * dd, + DdNode * f, + DdNode * g, + unsigned int i) +{ + return(f->index == i && g->index == i && + cuddT(f) == DD_ONE(dd) && cuddE(f) == DD_ZERO(dd) && + cuddT(g) == DD_ZERO(dd) && cuddE(g) == DD_ONE(dd)); + +} /* end of ddIsIthAddVarPair */ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddDecomp.c b/abc_with_bb_support/src/bdd/cudd/cuddDecomp.c new file mode 100644 index 000000000..10b8ee3dd --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddDecomp.c @@ -0,0 +1,2150 @@ +/**CFile*********************************************************************** + + FileName [cuddDecomp.c] + + PackageName [cudd] + + Synopsis [Functions for BDD decomposition.] + + Description [External procedures included in this file: +

    +
  • Cudd_bddApproxConjDecomp() +
  • Cudd_bddApproxDisjDecomp() +
  • Cudd_bddIterConjDecomp() +
  • Cudd_bddIterDisjDecomp() +
  • Cudd_bddGenConjDecomp() +
  • Cudd_bddGenDisjDecomp() +
  • Cudd_bddVarConjDecomp() +
  • Cudd_bddVarDisjDecomp() +
+ Static procedures included in this module: +
    +
  • cuddConjunctsAux() +
  • CreateBotDist() +
  • BuildConjuncts() +
  • ConjunctsFree() +
] + + Author [Kavita Ravi, Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ +#define DEPTH 5 +#define THRESHOLD 10 +#define NONE 0 +#define PAIR_ST 1 +#define PAIR_CR 2 +#define G_ST 3 +#define G_CR 4 +#define H_ST 5 +#define H_CR 6 +#define BOTH_G 7 +#define BOTH_H 8 + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ +typedef struct Conjuncts { + DdNode *g; + DdNode *h; +} Conjuncts; + +typedef struct NodeStat { + int distance; + int localRef; +} NodeStat; + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddDecomp.c,v 1.1.1.1 2003/02/24 22:23:51 wjiang Exp $"; +#endif + +static DdNode *one, *zero; +long lastTimeG; + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +#define FactorsNotStored(factors) ((int)((long)(factors) & 01)) + +#define FactorsComplement(factors) ((Conjuncts *)((long)(factors) | 01)) + +#define FactorsUncomplement(factors) ((Conjuncts *)((long)(factors) ^ 01)) + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static NodeStat * CreateBotDist ARGS((DdNode * node, st_table * distanceTable)); +static double CountMinterms ARGS((DdNode * node, double max, st_table * mintermTable, FILE *fp)); +static void ConjunctsFree ARGS((DdManager * dd, Conjuncts * factors)); +static int PairInTables ARGS((DdNode * g, DdNode * h, st_table * ghTable)); +static Conjuncts * CheckTablesCacheAndReturn ARGS((DdNode * node, DdNode * g, DdNode * h, st_table * ghTable, st_table * cacheTable)); +static Conjuncts * PickOnePair ARGS((DdNode * node, DdNode * g1, DdNode * h1, DdNode * g2, DdNode * h2, st_table * ghTable, st_table * cacheTable)); +static Conjuncts * CheckInTables ARGS((DdNode * node, DdNode * g1, DdNode * h1, DdNode * g2, DdNode * h2, st_table * ghTable, st_table * cacheTable, int * outOfMem)); +static Conjuncts * ZeroCase ARGS((DdManager * dd, DdNode * node, Conjuncts * factorsNv, st_table * ghTable, st_table * cacheTable, int switched)); +static Conjuncts * BuildConjuncts ARGS((DdManager * dd, DdNode * node, st_table * distanceTable, st_table * cacheTable, int approxDistance, int maxLocalRef, st_table * ghTable, st_table * mintermTable)); +static int cuddConjunctsAux ARGS((DdManager * dd, DdNode * f, DdNode ** c1, DdNode ** c2)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs two-way conjunctive decomposition of a BDD.] + + Description [Performs two-way conjunctive decomposition of a + BDD. This procedure owes its name to the use of supersetting to + obtain an initial factor of the given function. Returns the number + of conjuncts produced, that is, 2 if successful; 1 if no meaningful + decomposition was found; 0 otherwise. The conjuncts produced by this + procedure tend to be imbalanced.] + + SideEffects [The factors are returned in an array as side effects. + The array is allocated by this function. It is the caller's responsibility + to free it. On successful completion, the conjuncts are already + referenced. If the function returns 0, the array for the conjuncts is + not allocated. If the function returns 1, the only factor equals the + function to be decomposed.] + + SeeAlso [Cudd_bddApproxDisjDecomp Cudd_bddIterConjDecomp + Cudd_bddGenConjDecomp Cudd_bddVarConjDecomp Cudd_RemapOverApprox + Cudd_bddSqueeze Cudd_bddLICompaction] + +******************************************************************************/ +int +Cudd_bddApproxConjDecomp( + DdManager * dd /* manager */, + DdNode * f /* function to be decomposed */, + DdNode *** conjuncts /* address of the first factor */) +{ + DdNode *superset1, *superset2, *glocal, *hlocal; + int nvars = Cudd_SupportSize(dd,f); + + /* Find a tentative first factor by overapproximation and minimization. */ + superset1 = Cudd_RemapOverApprox(dd,f,nvars,0,1.0); + if (superset1 == NULL) return(0); + cuddRef(superset1); + superset2 = Cudd_bddSqueeze(dd,f,superset1); + if (superset2 == NULL) { + Cudd_RecursiveDeref(dd,superset1); + return(0); + } + cuddRef(superset2); + Cudd_RecursiveDeref(dd,superset1); + + /* Compute the second factor by minimization. */ + hlocal = Cudd_bddLICompaction(dd,f,superset2); + if (hlocal == NULL) { + Cudd_RecursiveDeref(dd,superset2); + return(0); + } + cuddRef(hlocal); + + /* Refine the first factor by minimization. If h turns out to be f, this + ** step guarantees that g will be 1. */ + glocal = Cudd_bddLICompaction(dd,superset2,hlocal); + if (glocal == NULL) { + Cudd_RecursiveDeref(dd,superset2); + Cudd_RecursiveDeref(dd,hlocal); + return(0); + } + cuddRef(glocal); + Cudd_RecursiveDeref(dd,superset2); + + if (glocal != DD_ONE(dd)) { + if (hlocal != DD_ONE(dd)) { + *conjuncts = ALLOC(DdNode *,2); + if (*conjuncts == NULL) { + Cudd_RecursiveDeref(dd,glocal); + Cudd_RecursiveDeref(dd,hlocal); + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + (*conjuncts)[0] = glocal; + (*conjuncts)[1] = hlocal; + return(2); + } else { + Cudd_RecursiveDeref(dd,hlocal); + *conjuncts = ALLOC(DdNode *,1); + if (*conjuncts == NULL) { + Cudd_RecursiveDeref(dd,glocal); + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + (*conjuncts)[0] = glocal; + return(1); + } + } else { + Cudd_RecursiveDeref(dd,glocal); + *conjuncts = ALLOC(DdNode *,1); + if (*conjuncts == NULL) { + Cudd_RecursiveDeref(dd,hlocal); + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + (*conjuncts)[0] = hlocal; + return(1); + } + +} /* end of Cudd_bddApproxConjDecomp */ + + +/**Function******************************************************************** + + Synopsis [Performs two-way disjunctive decomposition of a BDD.] + + Description [Performs two-way disjunctive decomposition of a BDD. + Returns the number of disjuncts produced, that is, 2 if successful; + 1 if no meaningful decomposition was found; 0 otherwise. The + disjuncts produced by this procedure tend to be imbalanced.] + + SideEffects [The two disjuncts are returned in an array as side effects. + The array is allocated by this function. It is the caller's responsibility + to free it. On successful completion, the disjuncts are already + referenced. If the function returns 0, the array for the disjuncts is + not allocated. If the function returns 1, the only factor equals the + function to be decomposed.] + + SeeAlso [Cudd_bddApproxConjDecomp Cudd_bddIterDisjDecomp + Cudd_bddGenDisjDecomp Cudd_bddVarDisjDecomp] + +******************************************************************************/ +int +Cudd_bddApproxDisjDecomp( + DdManager * dd /* manager */, + DdNode * f /* function to be decomposed */, + DdNode *** disjuncts /* address of the array of the disjuncts */) +{ + int result, i; + + result = Cudd_bddApproxConjDecomp(dd,Cudd_Not(f),disjuncts); + for (i = 0; i < result; i++) { + (*disjuncts)[i] = Cudd_Not((*disjuncts)[i]); + } + return(result); + +} /* end of Cudd_bddApproxDisjDecomp */ + + +/**Function******************************************************************** + + Synopsis [Performs two-way conjunctive decomposition of a BDD.] + + Description [Performs two-way conjunctive decomposition of a + BDD. This procedure owes its name to the iterated use of + supersetting to obtain a factor of the given function. Returns the + number of conjuncts produced, that is, 2 if successful; 1 if no + meaningful decomposition was found; 0 otherwise. The conjuncts + produced by this procedure tend to be imbalanced.] + + SideEffects [The factors are returned in an array as side effects. + The array is allocated by this function. It is the caller's responsibility + to free it. On successful completion, the conjuncts are already + referenced. If the function returns 0, the array for the conjuncts is + not allocated. If the function returns 1, the only factor equals the + function to be decomposed.] + + SeeAlso [Cudd_bddIterDisjDecomp Cudd_bddApproxConjDecomp + Cudd_bddGenConjDecomp Cudd_bddVarConjDecomp Cudd_RemapOverApprox + Cudd_bddSqueeze Cudd_bddLICompaction] + +******************************************************************************/ +int +Cudd_bddIterConjDecomp( + DdManager * dd /* manager */, + DdNode * f /* function to be decomposed */, + DdNode *** conjuncts /* address of the array of conjuncts */) +{ + DdNode *superset1, *superset2, *old[2], *res[2]; + int sizeOld, sizeNew; + int nvars = Cudd_SupportSize(dd,f); + + old[0] = DD_ONE(dd); + cuddRef(old[0]); + old[1] = f; + cuddRef(old[1]); + sizeOld = Cudd_SharingSize(old,2); + + do { + /* Find a tentative first factor by overapproximation and + ** minimization. */ + superset1 = Cudd_RemapOverApprox(dd,old[1],nvars,0,1.0); + if (superset1 == NULL) { + Cudd_RecursiveDeref(dd,old[0]); + Cudd_RecursiveDeref(dd,old[1]); + return(0); + } + cuddRef(superset1); + superset2 = Cudd_bddSqueeze(dd,old[1],superset1); + if (superset2 == NULL) { + Cudd_RecursiveDeref(dd,old[0]); + Cudd_RecursiveDeref(dd,old[1]); + Cudd_RecursiveDeref(dd,superset1); + return(0); + } + cuddRef(superset2); + Cudd_RecursiveDeref(dd,superset1); + res[0] = Cudd_bddAnd(dd,old[0],superset2); + if (res[0] == NULL) { + Cudd_RecursiveDeref(dd,superset2); + Cudd_RecursiveDeref(dd,old[0]); + Cudd_RecursiveDeref(dd,old[1]); + return(0); + } + cuddRef(res[0]); + Cudd_RecursiveDeref(dd,superset2); + if (res[0] == old[0]) { + Cudd_RecursiveDeref(dd,res[0]); + break; /* avoid infinite loop */ + } + + /* Compute the second factor by minimization. */ + res[1] = Cudd_bddLICompaction(dd,old[1],res[0]); + if (res[1] == NULL) { + Cudd_RecursiveDeref(dd,old[0]); + Cudd_RecursiveDeref(dd,old[1]); + return(0); + } + cuddRef(res[1]); + + sizeNew = Cudd_SharingSize(res,2); + if (sizeNew <= sizeOld) { + Cudd_RecursiveDeref(dd,old[0]); + old[0] = res[0]; + Cudd_RecursiveDeref(dd,old[1]); + old[1] = res[1]; + sizeOld = sizeNew; + } else { + Cudd_RecursiveDeref(dd,res[0]); + Cudd_RecursiveDeref(dd,res[1]); + break; + } + + } while (1); + + /* Refine the first factor by minimization. If h turns out to + ** be f, this step guarantees that g will be 1. */ + superset1 = Cudd_bddLICompaction(dd,old[0],old[1]); + if (superset1 == NULL) { + Cudd_RecursiveDeref(dd,old[0]); + Cudd_RecursiveDeref(dd,old[1]); + return(0); + } + cuddRef(superset1); + Cudd_RecursiveDeref(dd,old[0]); + old[0] = superset1; + + if (old[0] != DD_ONE(dd)) { + if (old[1] != DD_ONE(dd)) { + *conjuncts = ALLOC(DdNode *,2); + if (*conjuncts == NULL) { + Cudd_RecursiveDeref(dd,old[0]); + Cudd_RecursiveDeref(dd,old[1]); + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + (*conjuncts)[0] = old[0]; + (*conjuncts)[1] = old[1]; + return(2); + } else { + Cudd_RecursiveDeref(dd,old[1]); + *conjuncts = ALLOC(DdNode *,1); + if (*conjuncts == NULL) { + Cudd_RecursiveDeref(dd,old[0]); + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + (*conjuncts)[0] = old[0]; + return(1); + } + } else { + Cudd_RecursiveDeref(dd,old[0]); + *conjuncts = ALLOC(DdNode *,1); + if (*conjuncts == NULL) { + Cudd_RecursiveDeref(dd,old[1]); + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + (*conjuncts)[0] = old[1]; + return(1); + } + +} /* end of Cudd_bddIterConjDecomp */ + + +/**Function******************************************************************** + + Synopsis [Performs two-way disjunctive decomposition of a BDD.] + + Description [Performs two-way disjunctive decomposition of a BDD. + Returns the number of disjuncts produced, that is, 2 if successful; + 1 if no meaningful decomposition was found; 0 otherwise. The + disjuncts produced by this procedure tend to be imbalanced.] + + SideEffects [The two disjuncts are returned in an array as side effects. + The array is allocated by this function. It is the caller's responsibility + to free it. On successful completion, the disjuncts are already + referenced. If the function returns 0, the array for the disjuncts is + not allocated. If the function returns 1, the only factor equals the + function to be decomposed.] + + SeeAlso [Cudd_bddIterConjDecomp Cudd_bddApproxDisjDecomp + Cudd_bddGenDisjDecomp Cudd_bddVarDisjDecomp] + +******************************************************************************/ +int +Cudd_bddIterDisjDecomp( + DdManager * dd /* manager */, + DdNode * f /* function to be decomposed */, + DdNode *** disjuncts /* address of the array of the disjuncts */) +{ + int result, i; + + result = Cudd_bddIterConjDecomp(dd,Cudd_Not(f),disjuncts); + for (i = 0; i < result; i++) { + (*disjuncts)[i] = Cudd_Not((*disjuncts)[i]); + } + return(result); + +} /* end of Cudd_bddIterDisjDecomp */ + + +/**Function******************************************************************** + + Synopsis [Performs two-way conjunctive decomposition of a BDD.] + + Description [Performs two-way conjunctive decomposition of a + BDD. This procedure owes its name to the fact tht it generalizes the + decomposition based on the cofactors with respect to one + variable. Returns the number of conjuncts produced, that is, 2 if + successful; 1 if no meaningful decomposition was found; 0 + otherwise. The conjuncts produced by this procedure tend to be + balanced.] + + SideEffects [The two factors are returned in an array as side effects. + The array is allocated by this function. It is the caller's responsibility + to free it. On successful completion, the conjuncts are already + referenced. If the function returns 0, the array for the conjuncts is + not allocated. If the function returns 1, the only factor equals the + function to be decomposed.] + + SeeAlso [Cudd_bddGenDisjDecomp Cudd_bddApproxConjDecomp + Cudd_bddIterConjDecomp Cudd_bddVarConjDecomp] + +******************************************************************************/ +int +Cudd_bddGenConjDecomp( + DdManager * dd /* manager */, + DdNode * f /* function to be decomposed */, + DdNode *** conjuncts /* address of the array of conjuncts */) +{ + int result; + DdNode *glocal, *hlocal; + + one = DD_ONE(dd); + zero = Cudd_Not(one); + + do { + dd->reordered = 0; + result = cuddConjunctsAux(dd, f, &glocal, &hlocal); + } while (dd->reordered == 1); + + if (result == 0) { + return(0); + } + + if (glocal != one) { + if (hlocal != one) { + *conjuncts = ALLOC(DdNode *,2); + if (*conjuncts == NULL) { + Cudd_RecursiveDeref(dd,glocal); + Cudd_RecursiveDeref(dd,hlocal); + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + (*conjuncts)[0] = glocal; + (*conjuncts)[1] = hlocal; + return(2); + } else { + Cudd_RecursiveDeref(dd,hlocal); + *conjuncts = ALLOC(DdNode *,1); + if (*conjuncts == NULL) { + Cudd_RecursiveDeref(dd,glocal); + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + (*conjuncts)[0] = glocal; + return(1); + } + } else { + Cudd_RecursiveDeref(dd,glocal); + *conjuncts = ALLOC(DdNode *,1); + if (*conjuncts == NULL) { + Cudd_RecursiveDeref(dd,hlocal); + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + (*conjuncts)[0] = hlocal; + return(1); + } + +} /* end of Cudd_bddGenConjDecomp */ + + +/**Function******************************************************************** + + Synopsis [Performs two-way disjunctive decomposition of a BDD.] + + Description [Performs two-way disjunctive decomposition of a BDD. + Returns the number of disjuncts produced, that is, 2 if successful; + 1 if no meaningful decomposition was found; 0 otherwise. The + disjuncts produced by this procedure tend to be balanced.] + + SideEffects [The two disjuncts are returned in an array as side effects. + The array is allocated by this function. It is the caller's responsibility + to free it. On successful completion, the disjuncts are already + referenced. If the function returns 0, the array for the disjuncts is + not allocated. If the function returns 1, the only factor equals the + function to be decomposed.] + + SeeAlso [Cudd_bddGenConjDecomp Cudd_bddApproxDisjDecomp + Cudd_bddIterDisjDecomp Cudd_bddVarDisjDecomp] + +******************************************************************************/ +int +Cudd_bddGenDisjDecomp( + DdManager * dd /* manager */, + DdNode * f /* function to be decomposed */, + DdNode *** disjuncts /* address of the array of the disjuncts */) +{ + int result, i; + + result = Cudd_bddGenConjDecomp(dd,Cudd_Not(f),disjuncts); + for (i = 0; i < result; i++) { + (*disjuncts)[i] = Cudd_Not((*disjuncts)[i]); + } + return(result); + +} /* end of Cudd_bddGenDisjDecomp */ + + +/**Function******************************************************************** + + Synopsis [Performs two-way conjunctive decomposition of a BDD.] + + Description [Conjunctively decomposes one BDD according to a + variable. If f is the function of the BDD and + x is the variable, the decomposition is + (f+x)(f+x'). The variable is chosen so as to balance + the sizes of the two conjuncts and to keep them small. Returns the + number of conjuncts produced, that is, 2 if successful; 1 if no + meaningful decomposition was found; 0 otherwise.] + + SideEffects [The two factors are returned in an array as side effects. + The array is allocated by this function. It is the caller's responsibility + to free it. On successful completion, the conjuncts are already + referenced. If the function returns 0, the array for the conjuncts is + not allocated. If the function returns 1, the only factor equals the + function to be decomposed.] + + SeeAlso [Cudd_bddVarDisjDecomp Cudd_bddGenConjDecomp + Cudd_bddApproxConjDecomp Cudd_bddIterConjDecomp] + +*****************************************************************************/ +int +Cudd_bddVarConjDecomp( + DdManager * dd /* manager */, + DdNode * f /* function to be decomposed */, + DdNode *** conjuncts /* address of the array of conjuncts */) +{ + int best; + int min; + DdNode *support, *scan, *var, *glocal, *hlocal; + + /* Find best cofactoring variable. */ + support = Cudd_Support(dd,f); + if (support == NULL) return(0); + if (Cudd_IsConstant(support)) { + *conjuncts = ALLOC(DdNode *,1); + if (*conjuncts == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + (*conjuncts)[0] = f; + cuddRef((*conjuncts)[0]); + return(1); + } + cuddRef(support); + min = 1000000000; + best = -1; + scan = support; + while (!Cudd_IsConstant(scan)) { + int i = scan->index; + int est1 = Cudd_EstimateCofactor(dd,f,i,1); + int est0 = Cudd_EstimateCofactor(dd,f,i,0); + /* Minimize the size of the larger of the two cofactors. */ + int est = (est1 > est0) ? est1 : est0; + if (est < min) { + min = est; + best = i; + } + scan = cuddT(scan); + } +#ifdef DD_DEBUG + assert(best >= 0 && best < dd->size); +#endif + Cudd_RecursiveDeref(dd,support); + + var = Cudd_bddIthVar(dd,best); + glocal = Cudd_bddOr(dd,f,var); + if (glocal == NULL) { + return(0); + } + cuddRef(glocal); + hlocal = Cudd_bddOr(dd,f,Cudd_Not(var)); + if (hlocal == NULL) { + Cudd_RecursiveDeref(dd,glocal); + return(0); + } + cuddRef(hlocal); + + if (glocal != DD_ONE(dd)) { + if (hlocal != DD_ONE(dd)) { + *conjuncts = ALLOC(DdNode *,2); + if (*conjuncts == NULL) { + Cudd_RecursiveDeref(dd,glocal); + Cudd_RecursiveDeref(dd,hlocal); + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + (*conjuncts)[0] = glocal; + (*conjuncts)[1] = hlocal; + return(2); + } else { + Cudd_RecursiveDeref(dd,hlocal); + *conjuncts = ALLOC(DdNode *,1); + if (*conjuncts == NULL) { + Cudd_RecursiveDeref(dd,glocal); + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + (*conjuncts)[0] = glocal; + return(1); + } + } else { + Cudd_RecursiveDeref(dd,glocal); + *conjuncts = ALLOC(DdNode *,1); + if (*conjuncts == NULL) { + Cudd_RecursiveDeref(dd,hlocal); + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + (*conjuncts)[0] = hlocal; + return(1); + } + +} /* end of Cudd_bddVarConjDecomp */ + + +/**Function******************************************************************** + + Synopsis [Performs two-way disjunctive decomposition of a BDD.] + + Description [Performs two-way disjunctive decomposition of a BDD + according to a variable. If f is the function of the + BDD and x is the variable, the decomposition is + f*x + f*x'. The variable is chosen so as to balance + the sizes of the two disjuncts and to keep them small. Returns the + number of disjuncts produced, that is, 2 if successful; 1 if no + meaningful decomposition was found; 0 otherwise.] + + SideEffects [The two disjuncts are returned in an array as side effects. + The array is allocated by this function. It is the caller's responsibility + to free it. On successful completion, the disjuncts are already + referenced. If the function returns 0, the array for the disjuncts is + not allocated. If the function returns 1, the only factor equals the + function to be decomposed.] + + SeeAlso [Cudd_bddVarConjDecomp Cudd_bddApproxDisjDecomp + Cudd_bddIterDisjDecomp Cudd_bddGenDisjDecomp] + +******************************************************************************/ +int +Cudd_bddVarDisjDecomp( + DdManager * dd /* manager */, + DdNode * f /* function to be decomposed */, + DdNode *** disjuncts /* address of the array of the disjuncts */) +{ + int result, i; + + result = Cudd_bddVarConjDecomp(dd,Cudd_Not(f),disjuncts); + for (i = 0; i < result; i++) { + (*disjuncts)[i] = Cudd_Not((*disjuncts)[i]); + } + return(result); + +} /* end of Cudd_bddVarDisjDecomp */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Get longest distance of node from constant.] + + Description [Get longest distance of node from constant. Returns the + distance of the root from the constant if successful; CUDD_OUT_OF_MEM + otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static NodeStat * +CreateBotDist( + DdNode * node, + st_table * distanceTable) +{ + DdNode *N, *Nv, *Nnv; + int distance, distanceNv, distanceNnv; + NodeStat *nodeStat, *nodeStatNv, *nodeStatNnv; + +#if 0 + if (Cudd_IsConstant(node)) { + return(0); + } +#endif + + /* Return the entry in the table if found. */ + N = Cudd_Regular(node); + if (st_lookup(distanceTable, (char *)N, (char **)&nodeStat)) { + nodeStat->localRef++; + return(nodeStat); + } + + Nv = cuddT(N); + Nnv = cuddE(N); + Nv = Cudd_NotCond(Nv, Cudd_IsComplement(node)); + Nnv = Cudd_NotCond(Nnv, Cudd_IsComplement(node)); + + /* Recur on the children. */ + nodeStatNv = CreateBotDist(Nv, distanceTable); + if (nodeStatNv == NULL) return(NULL); + distanceNv = nodeStatNv->distance; + + nodeStatNnv = CreateBotDist(Nnv, distanceTable); + if (nodeStatNnv == NULL) return(NULL); + distanceNnv = nodeStatNnv->distance; + /* Store max distance from constant; note sometimes this distance + ** may be to 0. + */ + distance = (distanceNv > distanceNnv) ? (distanceNv+1) : (distanceNnv + 1); + + nodeStat = ALLOC(NodeStat, 1); + if (nodeStat == NULL) { + return(0); + } + nodeStat->distance = distance; + nodeStat->localRef = 1; + + if (st_insert(distanceTable, (char *)N, (char *)nodeStat) == + ST_OUT_OF_MEM) { + return(0); + + } + return(nodeStat); + +} /* end of CreateBotDist */ + + +/**Function******************************************************************** + + Synopsis [Count the number of minterms of each node ina a BDD and + store it in a hash table.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static double +CountMinterms( + DdNode * node, + double max, + st_table * mintermTable, + FILE *fp) +{ + DdNode *N, *Nv, *Nnv; + double min, minNv, minNnv; + double *dummy; + + N = Cudd_Regular(node); + + if (cuddIsConstant(N)) { + if (node == zero) { + return(0); + } else { + return(max); + } + } + + /* Return the entry in the table if found. */ + if (st_lookup(mintermTable, (char *)node, (char **)&dummy)) { + min = *dummy; + return(min); + } + + Nv = cuddT(N); + Nnv = cuddE(N); + Nv = Cudd_NotCond(Nv, Cudd_IsComplement(node)); + Nnv = Cudd_NotCond(Nnv, Cudd_IsComplement(node)); + + /* Recur on the children. */ + minNv = CountMinterms(Nv, max, mintermTable, fp); + if (minNv == -1.0) return(-1.0); + minNnv = CountMinterms(Nnv, max, mintermTable, fp); + if (minNnv == -1.0) return(-1.0); + min = minNv / 2.0 + minNnv / 2.0; + /* store + */ + + dummy = ALLOC(double, 1); + if (dummy == NULL) return(-1.0); + *dummy = min; + if (st_insert(mintermTable, (char *)node, (char *)dummy) == ST_OUT_OF_MEM) { + (void) fprintf(fp, "st table insert failed\n"); + } + return(min); + +} /* end of CountMinterms */ + + +/**Function******************************************************************** + + Synopsis [Free factors structure] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +ConjunctsFree( + DdManager * dd, + Conjuncts * factors) +{ + Cudd_RecursiveDeref(dd, factors->g); + Cudd_RecursiveDeref(dd, factors->h); + FREE(factors); + return; + +} /* end of ConjunctsFree */ + + +/**Function******************************************************************** + + Synopsis [Check whether the given pair is in the tables.] + + Description [.Check whether the given pair is in the tables. gTable + and hTable are combined. + absence in both is indicated by 0, + presence in gTable is indicated by 1, + presence in hTable by 2 and + presence in both by 3. + The values returned by this function are PAIR_ST, + PAIR_CR, G_ST, G_CR, H_ST, H_CR, BOTH_G, BOTH_H, NONE. + PAIR_ST implies g in gTable and h in hTable + PAIR_CR implies g in hTable and h in gTable + G_ST implies g in gTable and h not in any table + G_CR implies g in hTable and h not in any table + H_ST implies h in hTable and g not in any table + H_CR implies h in gTable and g not in any table + BOTH_G implies both in gTable + BOTH_H implies both in hTable + NONE implies none in table; ] + + SideEffects [] + + SeeAlso [CheckTablesCacheAndReturn CheckInTables] + +******************************************************************************/ +static int +PairInTables( + DdNode * g, + DdNode * h, + st_table * ghTable) +{ + int valueG, valueH, gPresent, hPresent; + + valueG = valueH = gPresent = hPresent = 0; + + gPresent = st_lookup_int(ghTable, (char *)Cudd_Regular(g), &valueG); + hPresent = st_lookup_int(ghTable, (char *)Cudd_Regular(h), &valueH); + + if (!gPresent && !hPresent) return(NONE); + + if (!hPresent) { + if (valueG & 1) return(G_ST); + if (valueG & 2) return(G_CR); + } + if (!gPresent) { + if (valueH & 1) return(H_CR); + if (valueH & 2) return(H_ST); + } + /* both in tables */ + if ((valueG & 1) && (valueH & 2)) return(PAIR_ST); + if ((valueG & 2) && (valueH & 1)) return(PAIR_CR); + + if (valueG & 1) { + return(BOTH_G); + } else { + return(BOTH_H); + } + +} /* end of PairInTables */ + + +/**Function******************************************************************** + + Synopsis [Check the tables for the existence of pair and return one + combination, cache the result.] + + Description [Check the tables for the existence of pair and return + one combination, cache the result. The assumption is that one of the + conjuncts is already in the tables.] + + SideEffects [g and h referenced for the cache] + + SeeAlso [ZeroCase] + +******************************************************************************/ +static Conjuncts * +CheckTablesCacheAndReturn( + DdNode * node, + DdNode * g, + DdNode * h, + st_table * ghTable, + st_table * cacheTable) +{ + int pairValue; + int value; + Conjuncts *factors; + + value = 0; + /* check tables */ + pairValue = PairInTables(g, h, ghTable); + assert(pairValue != NONE); + /* if both dont exist in table, we know one exists(either g or h). + * Therefore store the other and proceed + */ + factors = ALLOC(Conjuncts, 1); + if (factors == NULL) return(NULL); + if ((pairValue == BOTH_H) || (pairValue == H_ST)) { + if (g != one) { + value = 0; + if (st_lookup_int(ghTable, (char *)Cudd_Regular(g), &value)) { + value |= 1; + } else { + value = 1; + } + if (st_insert(ghTable, (char *)Cudd_Regular(g), + (char *)(long)value) == ST_OUT_OF_MEM) { + return(NULL); + } + } + factors->g = g; + factors->h = h; + } else if ((pairValue == BOTH_G) || (pairValue == G_ST)) { + if (h != one) { + value = 0; + if (st_lookup_int(ghTable, (char *)Cudd_Regular(h), &value)) { + value |= 2; + } else { + value = 2; + } + if (st_insert(ghTable, (char *)Cudd_Regular(h), + (char *)(long)value) == ST_OUT_OF_MEM) { + return(NULL); + } + } + factors->g = g; + factors->h = h; + } else if (pairValue == H_CR) { + if (g != one) { + value = 2; + if (st_insert(ghTable, (char *)Cudd_Regular(g), + (char *)(long)value) == ST_OUT_OF_MEM) { + return(NULL); + } + } + factors->g = h; + factors->h = g; + } else if (pairValue == G_CR) { + if (h != one) { + value = 1; + if (st_insert(ghTable, (char *)Cudd_Regular(h), + (char *)(long)value) == ST_OUT_OF_MEM) { + return(NULL); + } + } + factors->g = h; + factors->h = g; + } else if (pairValue == PAIR_CR) { + /* pair exists in table */ + factors->g = h; + factors->h = g; + } else if (pairValue == PAIR_ST) { + factors->g = g; + factors->h = h; + } + + /* cache the result for this node */ + if (st_insert(cacheTable, (char *)node, (char *)factors) == ST_OUT_OF_MEM) { + FREE(factors); + return(NULL); + } + + return(factors); + +} /* end of CheckTablesCacheAndReturn */ + +/**Function******************************************************************** + + Synopsis [Check the tables for the existence of pair and return one + combination, store in cache.] + + Description [Check the tables for the existence of pair and return + one combination, store in cache. The pair that has more pointers to + it is picked. An approximation of the number of local pointers is + made by taking the reference count of the pairs sent. ] + + SideEffects [] + + SeeAlso [ZeroCase BuildConjuncts] + +******************************************************************************/ +static Conjuncts * +PickOnePair( + DdNode * node, + DdNode * g1, + DdNode * h1, + DdNode * g2, + DdNode * h2, + st_table * ghTable, + st_table * cacheTable) +{ + int value; + Conjuncts *factors; + int oneRef, twoRef; + + factors = ALLOC(Conjuncts, 1); + if (factors == NULL) return(NULL); + + /* count the number of pointers to pair 2 */ + if (h2 == one) { + twoRef = (Cudd_Regular(g2))->ref; + } else if (g2 == one) { + twoRef = (Cudd_Regular(h2))->ref; + } else { + twoRef = ((Cudd_Regular(g2))->ref + (Cudd_Regular(h2))->ref)/2; + } + + /* count the number of pointers to pair 1 */ + if (h1 == one) { + oneRef = (Cudd_Regular(g1))->ref; + } else if (g1 == one) { + oneRef = (Cudd_Regular(h1))->ref; + } else { + oneRef = ((Cudd_Regular(g1))->ref + (Cudd_Regular(h1))->ref)/2; + } + + /* pick the pair with higher reference count */ + if (oneRef >= twoRef) { + factors->g = g1; + factors->h = h1; + } else { + factors->g = g2; + factors->h = h2; + } + + /* + * Store computed factors in respective tables to encourage + * recombination. + */ + if (factors->g != one) { + /* insert g in htable */ + value = 0; + if (st_lookup_int(ghTable, (char *)Cudd_Regular(factors->g), &value)) { + if (value == 2) { + value |= 1; + if (st_insert(ghTable, (char *)Cudd_Regular(factors->g), + (char *)(long)value) == ST_OUT_OF_MEM) { + FREE(factors); + return(NULL); + } + } + } else { + value = 1; + if (st_insert(ghTable, (char *)Cudd_Regular(factors->g), + (char *)(long)value) == ST_OUT_OF_MEM) { + FREE(factors); + return(NULL); + } + } + } + + if (factors->h != one) { + /* insert h in htable */ + value = 0; + if (st_lookup_int(ghTable, (char *)Cudd_Regular(factors->h), &value)) { + if (value == 1) { + value |= 2; + if (st_insert(ghTable, (char *)Cudd_Regular(factors->h), + (char *)(long)value) == ST_OUT_OF_MEM) { + FREE(factors); + return(NULL); + } + } + } else { + value = 2; + if (st_insert(ghTable, (char *)Cudd_Regular(factors->h), + (char *)(long)value) == ST_OUT_OF_MEM) { + FREE(factors); + return(NULL); + } + } + } + + /* Store factors in cache table for later use. */ + if (st_insert(cacheTable, (char *)node, (char *)factors) == + ST_OUT_OF_MEM) { + FREE(factors); + return(NULL); + } + + return(factors); + +} /* end of PickOnePair */ + + +/**Function******************************************************************** + + Synopsis [Check if the two pairs exist in the table, If any of the + conjuncts do exist, store in the cache and return the corresponding pair.] + + Description [Check if the two pairs exist in the table. If any of + the conjuncts do exist, store in the cache and return the + corresponding pair.] + + SideEffects [] + + SeeAlso [ZeroCase BuildConjuncts] + +******************************************************************************/ +static Conjuncts * +CheckInTables( + DdNode * node, + DdNode * g1, + DdNode * h1, + DdNode * g2, + DdNode * h2, + st_table * ghTable, + st_table * cacheTable, + int * outOfMem) +{ + int pairValue1, pairValue2; + Conjuncts *factors; + int value; + + *outOfMem = 0; + + /* check existence of pair in table */ + pairValue1 = PairInTables(g1, h1, ghTable); + pairValue2 = PairInTables(g2, h2, ghTable); + + /* if none of the 4 exist in the gh tables, return NULL */ + if ((pairValue1 == NONE) && (pairValue2 == NONE)) { + return NULL; + } + + factors = ALLOC(Conjuncts, 1); + if (factors == NULL) { + *outOfMem = 1; + return NULL; + } + + /* pairs that already exist in the table get preference. */ + if (pairValue1 == PAIR_ST) { + factors->g = g1; + factors->h = h1; + } else if (pairValue2 == PAIR_ST) { + factors->g = g2; + factors->h = h2; + } else if (pairValue1 == PAIR_CR) { + factors->g = h1; + factors->h = g1; + } else if (pairValue2 == PAIR_CR) { + factors->g = h2; + factors->h = g2; + } else if (pairValue1 == G_ST) { + /* g exists in the table, h is not found in either table */ + factors->g = g1; + factors->h = h1; + if (h1 != one) { + value = 2; + if (st_insert(ghTable, (char *)Cudd_Regular(h1), + (char *)(long)value) == ST_OUT_OF_MEM) { + *outOfMem = 1; + FREE(factors); + return(NULL); + } + } + } else if (pairValue1 == BOTH_G) { + /* g and h are found in the g table */ + factors->g = g1; + factors->h = h1; + if (h1 != one) { + value = 3; + if (st_insert(ghTable, (char *)Cudd_Regular(h1), + (char *)(long)value) == ST_OUT_OF_MEM) { + *outOfMem = 1; + FREE(factors); + return(NULL); + } + } + } else if (pairValue1 == H_ST) { + /* h exists in the table, g is not found in either table */ + factors->g = g1; + factors->h = h1; + if (g1 != one) { + value = 1; + if (st_insert(ghTable, (char *)Cudd_Regular(g1), + (char *)(long)value) == ST_OUT_OF_MEM) { + *outOfMem = 1; + FREE(factors); + return(NULL); + } + } + } else if (pairValue1 == BOTH_H) { + /* g and h are found in the h table */ + factors->g = g1; + factors->h = h1; + if (g1 != one) { + value = 3; + if (st_insert(ghTable, (char *)Cudd_Regular(g1), + (char *)(long)value) == ST_OUT_OF_MEM) { + *outOfMem = 1; + FREE(factors); + return(NULL); + } + } + } else if (pairValue2 == G_ST) { + /* g exists in the table, h is not found in either table */ + factors->g = g2; + factors->h = h2; + if (h2 != one) { + value = 2; + if (st_insert(ghTable, (char *)Cudd_Regular(h2), + (char *)(long)value) == ST_OUT_OF_MEM) { + *outOfMem = 1; + FREE(factors); + return(NULL); + } + } + } else if (pairValue2 == BOTH_G) { + /* g and h are found in the g table */ + factors->g = g2; + factors->h = h2; + if (h2 != one) { + value = 3; + if (st_insert(ghTable, (char *)Cudd_Regular(h2), + (char *)(long)value) == ST_OUT_OF_MEM) { + *outOfMem = 1; + FREE(factors); + return(NULL); + } + } + } else if (pairValue2 == H_ST) { + /* h exists in the table, g is not found in either table */ + factors->g = g2; + factors->h = h2; + if (g2 != one) { + value = 1; + if (st_insert(ghTable, (char *)Cudd_Regular(g2), + (char *)(long)value) == ST_OUT_OF_MEM) { + *outOfMem = 1; + FREE(factors); + return(NULL); + } + } + } else if (pairValue2 == BOTH_H) { + /* g and h are found in the h table */ + factors->g = g2; + factors->h = h2; + if (g2 != one) { + value = 3; + if (st_insert(ghTable, (char *)Cudd_Regular(g2), + (char *)(long)value) == ST_OUT_OF_MEM) { + *outOfMem = 1; + FREE(factors); + return(NULL); + } + } + } else if (pairValue1 == G_CR) { + /* g found in h table and h in none */ + factors->g = h1; + factors->h = g1; + if (h1 != one) { + value = 1; + if (st_insert(ghTable, (char *)Cudd_Regular(h1), + (char *)(long)value) == ST_OUT_OF_MEM) { + *outOfMem = 1; + FREE(factors); + return(NULL); + } + } + } else if (pairValue1 == H_CR) { + /* h found in g table and g in none */ + factors->g = h1; + factors->h = g1; + if (g1 != one) { + value = 2; + if (st_insert(ghTable, (char *)Cudd_Regular(g1), + (char *)(long)value) == ST_OUT_OF_MEM) { + *outOfMem = 1; + FREE(factors); + return(NULL); + } + } + } else if (pairValue2 == G_CR) { + /* g found in h table and h in none */ + factors->g = h2; + factors->h = g2; + if (h2 != one) { + value = 1; + if (st_insert(ghTable, (char *)Cudd_Regular(h2), + (char *)(long)value) == ST_OUT_OF_MEM) { + *outOfMem = 1; + FREE(factors); + return(NULL); + } + } + } else if (pairValue2 == H_CR) { + /* h found in g table and g in none */ + factors->g = h2; + factors->h = g2; + if (g2 != one) { + value = 2; + if (st_insert(ghTable, (char *)Cudd_Regular(g2), + (char *)(long)value) == ST_OUT_OF_MEM) { + *outOfMem = 1; + FREE(factors); + return(NULL); + } + } + } + + /* Store factors in cache table for later use. */ + if (st_insert(cacheTable, (char *)node, (char *)factors) == + ST_OUT_OF_MEM) { + *outOfMem = 1; + FREE(factors); + return(NULL); + } + return factors; +} /* end of CheckInTables */ + + + +/**Function******************************************************************** + + Synopsis [If one child is zero, do explicitly what Restrict does or better] + + Description [If one child is zero, do explicitly what Restrict does or better. + First separate a variable and its child in the base case. In case of a cube + times a function, separate the cube and function. As a last resort, look in + tables.] + + SideEffects [Frees the BDDs in factorsNv. factorsNv itself is not freed + because it is freed above.] + + SeeAlso [BuildConjuncts] + +******************************************************************************/ +static Conjuncts * +ZeroCase( + DdManager * dd, + DdNode * node, + Conjuncts * factorsNv, + st_table * ghTable, + st_table * cacheTable, + int switched) +{ + int topid; + DdNode *g, *h, *g1, *g2, *h1, *h2, *x, *N, *G, *H, *Gv, *Gnv; + DdNode *Hv, *Hnv; + int value; + int outOfMem; + Conjuncts *factors; + + /* get var at this node */ + N = Cudd_Regular(node); + topid = N->index; + x = dd->vars[topid]; + x = (switched) ? Cudd_Not(x): x; + cuddRef(x); + + /* Seprate variable and child */ + if (factorsNv->g == one) { + Cudd_RecursiveDeref(dd, factorsNv->g); + factors = ALLOC(Conjuncts, 1); + if (factors == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + Cudd_RecursiveDeref(dd, factorsNv->h); + Cudd_RecursiveDeref(dd, x); + return(NULL); + } + factors->g = x; + factors->h = factorsNv->h; + /* cache the result*/ + if (st_insert(cacheTable, (char *)node, (char *)factors) == ST_OUT_OF_MEM) { + dd->errorCode = CUDD_MEMORY_OUT; + Cudd_RecursiveDeref(dd, factorsNv->h); + Cudd_RecursiveDeref(dd, x); + FREE(factors); + return NULL; + } + + /* store x in g table, the other node is already in the table */ + if (st_lookup_int(ghTable, (char *)Cudd_Regular(x), &value)) { + value |= 1; + } else { + value = 1; + } + if (st_insert(ghTable, (char *)Cudd_Regular(x), (char *)(long)value) == ST_OUT_OF_MEM) { + dd->errorCode = CUDD_MEMORY_OUT; + return NULL; + } + return(factors); + } + + /* Seprate variable and child */ + if (factorsNv->h == one) { + Cudd_RecursiveDeref(dd, factorsNv->h); + factors = ALLOC(Conjuncts, 1); + if (factors == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + Cudd_RecursiveDeref(dd, factorsNv->g); + Cudd_RecursiveDeref(dd, x); + return(NULL); + } + factors->g = factorsNv->g; + factors->h = x; + /* cache the result. */ + if (st_insert(cacheTable, (char *)node, (char *)factors) == ST_OUT_OF_MEM) { + dd->errorCode = CUDD_MEMORY_OUT; + Cudd_RecursiveDeref(dd, factorsNv->g); + Cudd_RecursiveDeref(dd, x); + FREE(factors); + return(NULL); + } + /* store x in h table, the other node is already in the table */ + if (st_lookup_int(ghTable, (char *)Cudd_Regular(x), &value)) { + value |= 2; + } else { + value = 2; + } + if (st_insert(ghTable, (char *)Cudd_Regular(x), (char *)(long)value) == ST_OUT_OF_MEM) { + dd->errorCode = CUDD_MEMORY_OUT; + return NULL; + } + return(factors); + } + + G = Cudd_Regular(factorsNv->g); + Gv = cuddT(G); + Gnv = cuddE(G); + Gv = Cudd_NotCond(Gv, Cudd_IsComplement(node)); + Gnv = Cudd_NotCond(Gnv, Cudd_IsComplement(node)); + /* if the child below is a variable */ + if ((Gv == zero) || (Gnv == zero)) { + h = factorsNv->h; + g = cuddBddAndRecur(dd, x, factorsNv->g); + if (g != NULL) cuddRef(g); + Cudd_RecursiveDeref(dd, factorsNv->g); + Cudd_RecursiveDeref(dd, x); + if (g == NULL) { + Cudd_RecursiveDeref(dd, factorsNv->h); + return NULL; + } + /* CheckTablesCacheAndReturn responsible for allocating + * factors structure., g,h referenced for cache store the + */ + factors = CheckTablesCacheAndReturn(node, + g, + h, + ghTable, + cacheTable); + if (factors == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + Cudd_RecursiveDeref(dd, g); + Cudd_RecursiveDeref(dd, h); + } + return(factors); + } + + H = Cudd_Regular(factorsNv->h); + Hv = cuddT(H); + Hnv = cuddE(H); + Hv = Cudd_NotCond(Hv, Cudd_IsComplement(node)); + Hnv = Cudd_NotCond(Hnv, Cudd_IsComplement(node)); + /* if the child below is a variable */ + if ((Hv == zero) || (Hnv == zero)) { + g = factorsNv->g; + h = cuddBddAndRecur(dd, x, factorsNv->h); + if (h!= NULL) cuddRef(h); + Cudd_RecursiveDeref(dd, factorsNv->h); + Cudd_RecursiveDeref(dd, x); + if (h == NULL) { + Cudd_RecursiveDeref(dd, factorsNv->g); + return NULL; + } + /* CheckTablesCacheAndReturn responsible for allocating + * factors structure.g,h referenced for table store + */ + factors = CheckTablesCacheAndReturn(node, + g, + h, + ghTable, + cacheTable); + if (factors == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + Cudd_RecursiveDeref(dd, g); + Cudd_RecursiveDeref(dd, h); + } + return(factors); + } + + /* build g1 = x*g; h1 = h */ + /* build g2 = g; h2 = x*h */ + Cudd_RecursiveDeref(dd, x); + h1 = factorsNv->h; + g1 = cuddBddAndRecur(dd, x, factorsNv->g); + if (g1 != NULL) cuddRef(g1); + if (g1 == NULL) { + Cudd_RecursiveDeref(dd, factorsNv->g); + Cudd_RecursiveDeref(dd, factorsNv->h); + return NULL; + } + + g2 = factorsNv->g; + h2 = cuddBddAndRecur(dd, x, factorsNv->h); + if (h2 != NULL) cuddRef(h2); + if (h2 == NULL) { + Cudd_RecursiveDeref(dd, factorsNv->h); + Cudd_RecursiveDeref(dd, factorsNv->g); + return NULL; + } + + /* check whether any pair is in tables */ + factors = CheckInTables(node, g1, h1, g2, h2, ghTable, cacheTable, &outOfMem); + if (outOfMem) { + dd->errorCode = CUDD_MEMORY_OUT; + Cudd_RecursiveDeref(dd, g1); + Cudd_RecursiveDeref(dd, h1); + Cudd_RecursiveDeref(dd, g2); + Cudd_RecursiveDeref(dd, h2); + return NULL; + } + if (factors != NULL) { + if ((factors->g == g1) || (factors->g == h1)) { + Cudd_RecursiveDeref(dd, g2); + Cudd_RecursiveDeref(dd, h2); + } else { + Cudd_RecursiveDeref(dd, g1); + Cudd_RecursiveDeref(dd, h1); + } + return factors; + } + + /* check for each pair in tables and choose one */ + factors = PickOnePair(node,g1, h1, g2, h2, ghTable, cacheTable); + if (factors == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + Cudd_RecursiveDeref(dd, g1); + Cudd_RecursiveDeref(dd, h1); + Cudd_RecursiveDeref(dd, g2); + Cudd_RecursiveDeref(dd, h2); + } else { + /* now free what was created and not used */ + if ((factors->g == g1) || (factors->g == h1)) { + Cudd_RecursiveDeref(dd, g2); + Cudd_RecursiveDeref(dd, h2); + } else { + Cudd_RecursiveDeref(dd, g1); + Cudd_RecursiveDeref(dd, h1); + } + } + + return(factors); +} /* end of ZeroCase */ + + +/**Function******************************************************************** + + Synopsis [Builds the conjuncts recursively, bottom up.] + + Description [Builds the conjuncts recursively, bottom up. Constants + are returned as (f, f). The cache is checked for previously computed + result. The decomposition points are determined by the local + reference count of this node and the longest distance from the + constant. At the decomposition point, the factors returned are (f, + 1). Recur on the two children. The order is determined by the + heavier branch. Combine the factors of the two children and pick the + one that already occurs in the gh table. Occurence in g is indicated + by value 1, occurence in h by 2, occurence in both 3.] + + SideEffects [] + + SeeAlso [cuddConjunctsAux] + +******************************************************************************/ +static Conjuncts * +BuildConjuncts( + DdManager * dd, + DdNode * node, + st_table * distanceTable, + st_table * cacheTable, + int approxDistance, + int maxLocalRef, + st_table * ghTable, + st_table * mintermTable) +{ + int topid, distance; + Conjuncts *factorsNv, *factorsNnv, *factors; + Conjuncts *dummy; + DdNode *N, *Nv, *Nnv, *temp, *g1, *g2, *h1, *h2, *topv; + double minNv = 0.0, minNnv = 0.0; + double *doubleDummy; + int switched =0; + int outOfMem; + int freeNv = 0, freeNnv = 0, freeTemp; + NodeStat *nodeStat; + int value; + + /* if f is constant, return (f,f) */ + if (Cudd_IsConstant(node)) { + factors = ALLOC(Conjuncts, 1); + if (factors == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + factors->g = node; + factors->h = node; + return(FactorsComplement(factors)); + } + + /* If result (a pair of conjuncts) in cache, return the factors. */ + if (st_lookup(cacheTable, (char *)node, (char **)&dummy)) { + factors = dummy; + return(factors); + } + + /* check distance and local reference count of this node */ + N = Cudd_Regular(node); + if (!st_lookup(distanceTable, (char *)N, (char **)&nodeStat)) { + (void) fprintf(dd->err, "Not in table, Something wrong\n"); + dd->errorCode = CUDD_INTERNAL_ERROR; + return(NULL); + } + distance = nodeStat->distance; + + /* at or below decomposition point, return (f, 1) */ + if (((nodeStat->localRef > maxLocalRef*2/3) && + (distance < approxDistance*2/3)) || + (distance <= approxDistance/4)) { + factors = ALLOC(Conjuncts, 1); + if (factors == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + /* alternate assigning (f,1) */ + value = 0; + if (st_lookup_int(ghTable, (char *)Cudd_Regular(node), &value)) { + if (value == 3) { + if (!lastTimeG) { + factors->g = node; + factors->h = one; + lastTimeG = 1; + } else { + factors->g = one; + factors->h = node; + lastTimeG = 0; + } + } else if (value == 1) { + factors->g = node; + factors->h = one; + } else { + factors->g = one; + factors->h = node; + } + } else if (!lastTimeG) { + factors->g = node; + factors->h = one; + lastTimeG = 1; + value = 1; + if (st_insert(ghTable, (char *)Cudd_Regular(node), (char *)(long)value) == ST_OUT_OF_MEM) { + dd->errorCode = CUDD_MEMORY_OUT; + FREE(factors); + return NULL; + } + } else { + factors->g = one; + factors->h = node; + lastTimeG = 0; + value = 2; + if (st_insert(ghTable, (char *)Cudd_Regular(node), (char *)(long)value) == ST_OUT_OF_MEM) { + dd->errorCode = CUDD_MEMORY_OUT; + FREE(factors); + return NULL; + } + } + return(FactorsComplement(factors)); + } + + /* get the children and recur */ + Nv = cuddT(N); + Nnv = cuddE(N); + Nv = Cudd_NotCond(Nv, Cudd_IsComplement(node)); + Nnv = Cudd_NotCond(Nnv, Cudd_IsComplement(node)); + + /* Choose which subproblem to solve first based on the number of + * minterms. We go first where there are more minterms. + */ + if (!Cudd_IsConstant(Nv)) { + if (!st_lookup(mintermTable, (char *)Nv, (char **)&doubleDummy)) { + (void) fprintf(dd->err, "Not in table: Something wrong\n"); + dd->errorCode = CUDD_INTERNAL_ERROR; + return(NULL); + } + minNv = *doubleDummy; + } + + if (!Cudd_IsConstant(Nnv)) { + if (!st_lookup(mintermTable, (char *)Nnv, (char **)&doubleDummy)) { + (void) fprintf(dd->err, "Not in table: Something wrong\n"); + dd->errorCode = CUDD_INTERNAL_ERROR; + return(NULL); + } + minNnv = *doubleDummy; + } + + if (minNv < minNnv) { + temp = Nv; + Nv = Nnv; + Nnv = temp; + switched = 1; + } + + /* build gt, ht recursively */ + if (Nv != zero) { + factorsNv = BuildConjuncts(dd, Nv, distanceTable, + cacheTable, approxDistance, maxLocalRef, + ghTable, mintermTable); + if (factorsNv == NULL) return(NULL); + freeNv = FactorsNotStored(factorsNv); + factorsNv = (freeNv) ? FactorsUncomplement(factorsNv) : factorsNv; + cuddRef(factorsNv->g); + cuddRef(factorsNv->h); + + /* Deal with the zero case */ + if (Nnv == zero) { + /* is responsible for freeing factorsNv */ + factors = ZeroCase(dd, node, factorsNv, ghTable, + cacheTable, switched); + if (freeNv) FREE(factorsNv); + return(factors); + } + } + + /* build ge, he recursively */ + if (Nnv != zero) { + factorsNnv = BuildConjuncts(dd, Nnv, distanceTable, + cacheTable, approxDistance, maxLocalRef, + ghTable, mintermTable); + if (factorsNnv == NULL) { + Cudd_RecursiveDeref(dd, factorsNv->g); + Cudd_RecursiveDeref(dd, factorsNv->h); + if (freeNv) FREE(factorsNv); + return(NULL); + } + freeNnv = FactorsNotStored(factorsNnv); + factorsNnv = (freeNnv) ? FactorsUncomplement(factorsNnv) : factorsNnv; + cuddRef(factorsNnv->g); + cuddRef(factorsNnv->h); + + /* Deal with the zero case */ + if (Nv == zero) { + /* is responsible for freeing factorsNv */ + factors = ZeroCase(dd, node, factorsNnv, ghTable, + cacheTable, switched); + if (freeNnv) FREE(factorsNnv); + return(factors); + } + } + + /* construct the 2 pairs */ + /* g1 = x*gt + x'*ge; h1 = x*ht + x'*he; */ + /* g2 = x*gt + x'*he; h2 = x*ht + x'*ge */ + if (switched) { + factors = factorsNnv; + factorsNnv = factorsNv; + factorsNv = factors; + freeTemp = freeNv; + freeNv = freeNnv; + freeNnv = freeTemp; + } + + /* Build the factors for this node. */ + topid = N->index; + topv = dd->vars[topid]; + + g1 = cuddBddIteRecur(dd, topv, factorsNv->g, factorsNnv->g); + if (g1 == NULL) { + Cudd_RecursiveDeref(dd, factorsNv->g); + Cudd_RecursiveDeref(dd, factorsNv->h); + Cudd_RecursiveDeref(dd, factorsNnv->g); + Cudd_RecursiveDeref(dd, factorsNnv->h); + if (freeNv) FREE(factorsNv); + if (freeNnv) FREE(factorsNnv); + return(NULL); + } + + cuddRef(g1); + + h1 = cuddBddIteRecur(dd, topv, factorsNv->h, factorsNnv->h); + if (h1 == NULL) { + Cudd_RecursiveDeref(dd, factorsNv->g); + Cudd_RecursiveDeref(dd, factorsNv->h); + Cudd_RecursiveDeref(dd, factorsNnv->g); + Cudd_RecursiveDeref(dd, factorsNnv->h); + Cudd_RecursiveDeref(dd, g1); + if (freeNv) FREE(factorsNv); + if (freeNnv) FREE(factorsNnv); + return(NULL); + } + + cuddRef(h1); + + g2 = cuddBddIteRecur(dd, topv, factorsNv->g, factorsNnv->h); + if (g2 == NULL) { + Cudd_RecursiveDeref(dd, factorsNv->h); + Cudd_RecursiveDeref(dd, factorsNv->g); + Cudd_RecursiveDeref(dd, factorsNnv->g); + Cudd_RecursiveDeref(dd, factorsNnv->h); + Cudd_RecursiveDeref(dd, g1); + Cudd_RecursiveDeref(dd, h1); + if (freeNv) FREE(factorsNv); + if (freeNnv) FREE(factorsNnv); + return(NULL); + } + cuddRef(g2); + Cudd_RecursiveDeref(dd, factorsNv->g); + Cudd_RecursiveDeref(dd, factorsNnv->h); + + h2 = cuddBddIteRecur(dd, topv, factorsNv->h, factorsNnv->g); + if (h2 == NULL) { + Cudd_RecursiveDeref(dd, factorsNv->g); + Cudd_RecursiveDeref(dd, factorsNv->h); + Cudd_RecursiveDeref(dd, factorsNnv->g); + Cudd_RecursiveDeref(dd, factorsNnv->h); + Cudd_RecursiveDeref(dd, g1); + Cudd_RecursiveDeref(dd, h1); + Cudd_RecursiveDeref(dd, g2); + if (freeNv) FREE(factorsNv); + if (freeNnv) FREE(factorsNnv); + return(NULL); + } + cuddRef(h2); + Cudd_RecursiveDeref(dd, factorsNv->h); + Cudd_RecursiveDeref(dd, factorsNnv->g); + if (freeNv) FREE(factorsNv); + if (freeNnv) FREE(factorsNnv); + + /* check for each pair in tables and choose one */ + factors = CheckInTables(node, g1, h1, g2, h2, ghTable, cacheTable, &outOfMem); + if (outOfMem) { + dd->errorCode = CUDD_MEMORY_OUT; + Cudd_RecursiveDeref(dd, g1); + Cudd_RecursiveDeref(dd, h1); + Cudd_RecursiveDeref(dd, g2); + Cudd_RecursiveDeref(dd, h2); + return(NULL); + } + if (factors != NULL) { + if ((factors->g == g1) || (factors->g == h1)) { + Cudd_RecursiveDeref(dd, g2); + Cudd_RecursiveDeref(dd, h2); + } else { + Cudd_RecursiveDeref(dd, g1); + Cudd_RecursiveDeref(dd, h1); + } + return(factors); + } + + /* if not in tables, pick one pair */ + factors = PickOnePair(node,g1, h1, g2, h2, ghTable, cacheTable); + if (factors == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + Cudd_RecursiveDeref(dd, g1); + Cudd_RecursiveDeref(dd, h1); + Cudd_RecursiveDeref(dd, g2); + Cudd_RecursiveDeref(dd, h2); + } else { + /* now free what was created and not used */ + if ((factors->g == g1) || (factors->g == h1)) { + Cudd_RecursiveDeref(dd, g2); + Cudd_RecursiveDeref(dd, h2); + } else { + Cudd_RecursiveDeref(dd, g1); + Cudd_RecursiveDeref(dd, h1); + } + } + + return(factors); + +} /* end of BuildConjuncts */ + + +/**Function******************************************************************** + + Synopsis [Procedure to compute two conjunctive factors of f and place in *c1 and *c2.] + + Description [Procedure to compute two conjunctive factors of f and + place in *c1 and *c2. Sets up the required data - table of distances + from the constant and local reference count. Also minterm table. ] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +static int +cuddConjunctsAux( + DdManager * dd, + DdNode * f, + DdNode ** c1, + DdNode ** c2) +{ + st_table *distanceTable = NULL; + st_table *cacheTable = NULL; + st_table *mintermTable = NULL; + st_table *ghTable = NULL; + st_generator *stGen; + char *key, *value; + Conjuncts *factors; + int distance, approxDistance; + double max, minterms; + int freeFactors; + NodeStat *nodeStat; + int maxLocalRef; + + /* initialize */ + *c1 = NULL; + *c2 = NULL; + + /* initialize distances table */ + distanceTable = st_init_table(st_ptrcmp,st_ptrhash); + if (distanceTable == NULL) goto outOfMem; + + /* make the entry for the constant */ + nodeStat = ALLOC(NodeStat, 1); + if (nodeStat == NULL) goto outOfMem; + nodeStat->distance = 0; + nodeStat->localRef = 1; + if (st_insert(distanceTable, (char *)one, (char *)nodeStat) == ST_OUT_OF_MEM) { + goto outOfMem; + } + + /* Count node distances from constant. */ + nodeStat = CreateBotDist(f, distanceTable); + if (nodeStat == NULL) goto outOfMem; + + /* set the distance for the decomposition points */ + approxDistance = (DEPTH < nodeStat->distance) ? nodeStat->distance : DEPTH; + distance = nodeStat->distance; + + if (distance < approxDistance) { + /* Too small to bother. */ + *c1 = f; + *c2 = DD_ONE(dd); + cuddRef(*c1); cuddRef(*c2); + stGen = st_init_gen(distanceTable); + if (stGen == NULL) goto outOfMem; + while(st_gen(stGen, (char **)&key, (char **)&value)) { + FREE(value); + } + st_free_gen(stGen); stGen = NULL; + st_free_table(distanceTable); + return(1); + } + + /* record the maximum local reference count */ + maxLocalRef = 0; + stGen = st_init_gen(distanceTable); + if (stGen == NULL) goto outOfMem; + while(st_gen(stGen, (char **)&key, (char **)&value)) { + nodeStat = (NodeStat *)value; + maxLocalRef = (nodeStat->localRef > maxLocalRef) ? + nodeStat->localRef : maxLocalRef; + } + st_free_gen(stGen); stGen = NULL; + + + /* Count minterms for each node. */ + max = pow(2.0, (double)Cudd_SupportSize(dd,f)); /* potential overflow */ + mintermTable = st_init_table(st_ptrcmp,st_ptrhash); + if (mintermTable == NULL) goto outOfMem; + minterms = CountMinterms(f, max, mintermTable, dd->err); + if (minterms == -1.0) goto outOfMem; + + lastTimeG = Cudd_Random() & 1; + cacheTable = st_init_table(st_ptrcmp, st_ptrhash); + if (cacheTable == NULL) goto outOfMem; + ghTable = st_init_table(st_ptrcmp, st_ptrhash); + if (ghTable == NULL) goto outOfMem; + + /* Build conjuncts. */ + factors = BuildConjuncts(dd, f, distanceTable, cacheTable, + approxDistance, maxLocalRef, ghTable, mintermTable); + if (factors == NULL) goto outOfMem; + + /* free up tables */ + stGen = st_init_gen(distanceTable); + if (stGen == NULL) goto outOfMem; + while(st_gen(stGen, (char **)&key, (char **)&value)) { + FREE(value); + } + st_free_gen(stGen); stGen = NULL; + st_free_table(distanceTable); distanceTable = NULL; + st_free_table(ghTable); ghTable = NULL; + + stGen = st_init_gen(mintermTable); + if (stGen == NULL) goto outOfMem; + while(st_gen(stGen, (char **)&key, (char **)&value)) { + FREE(value); + } + st_free_gen(stGen); stGen = NULL; + st_free_table(mintermTable); mintermTable = NULL; + + freeFactors = FactorsNotStored(factors); + factors = (freeFactors) ? FactorsUncomplement(factors) : factors; + if (factors != NULL) { + *c1 = factors->g; + *c2 = factors->h; + cuddRef(*c1); + cuddRef(*c2); + if (freeFactors) FREE(factors); + +#if 0 + if ((*c1 == f) && (!Cudd_IsConstant(f))) { + assert(*c2 == one); + } + if ((*c2 == f) && (!Cudd_IsConstant(f))) { + assert(*c1 == one); + } + + if ((*c1 != one) && (!Cudd_IsConstant(f))) { + assert(!Cudd_bddLeq(dd, *c2, *c1)); + } + if ((*c2 != one) && (!Cudd_IsConstant(f))) { + assert(!Cudd_bddLeq(dd, *c1, *c2)); + } +#endif + } + + stGen = st_init_gen(cacheTable); + if (stGen == NULL) goto outOfMem; + while(st_gen(stGen, (char **)&key, (char **)&value)) { + ConjunctsFree(dd, (Conjuncts *)value); + } + st_free_gen(stGen); stGen = NULL; + + st_free_table(cacheTable); cacheTable = NULL; + + return(1); + +outOfMem: + if (distanceTable != NULL) { + stGen = st_init_gen(distanceTable); + if (stGen == NULL) goto outOfMem; + while(st_gen(stGen, (char **)&key, (char **)&value)) { + FREE(value); + } + st_free_gen(stGen); stGen = NULL; + st_free_table(distanceTable); distanceTable = NULL; + } + if (mintermTable != NULL) { + stGen = st_init_gen(mintermTable); + if (stGen == NULL) goto outOfMem; + while(st_gen(stGen, (char **)&key, (char **)&value)) { + FREE(value); + } + st_free_gen(stGen); stGen = NULL; + st_free_table(mintermTable); mintermTable = NULL; + } + if (ghTable != NULL) st_free_table(ghTable); + if (cacheTable != NULL) { + stGen = st_init_gen(cacheTable); + if (stGen == NULL) goto outOfMem; + while(st_gen(stGen, (char **)&key, (char **)&value)) { + ConjunctsFree(dd, (Conjuncts *)value); + } + st_free_gen(stGen); stGen = NULL; + st_free_table(cacheTable); cacheTable = NULL; + } + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + +} /* end of cuddConjunctsAux */ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddEssent.c b/abc_with_bb_support/src/bdd/cudd/cuddEssent.c new file mode 100644 index 000000000..df69bbe5a --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddEssent.c @@ -0,0 +1,279 @@ +/**CFile*********************************************************************** + + FileName [cuddEssent.c] + + PackageName [cudd] + + Synopsis [Functions for the detection of essential variables.] + + Description [External procedures included in this file: +
    +
  • Cudd_FindEssential() +
  • Cudd_bddIsVarEssential() +
+ Static procedures included in this module: +
    +
  • ddFindEssentialRecur() +
] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddEssent.c,v 1.1.1.1 2003/02/24 22:23:51 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static DdNode * ddFindEssentialRecur ARGS((DdManager *dd, DdNode *f)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Finds the essential variables of a DD.] + + Description [Returns the cube of the essential variables. A positive + literal means that the variable must be set to 1 for the function to be + 1. A negative literal means that the variable must be set to 0 for the + function to be 1. Returns a pointer to the cube BDD if successful; + NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddIsVarEssential] + +******************************************************************************/ +DdNode * +Cudd_FindEssential( + DdManager * dd, + DdNode * f) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = ddFindEssentialRecur(dd,f); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_FindEssential */ + + +/**Function******************************************************************** + + Synopsis [Determines whether a given variable is essential with a + given phase in a BDD.] + + Description [Determines whether a given variable is essential with a + given phase in a BDD. Uses Cudd_bddIteConstant. Returns 1 if phase == 1 + and f-->x_id, or if phase == 0 and f-->x_id'.] + + SideEffects [None] + + SeeAlso [Cudd_FindEssential] + +******************************************************************************/ +int +Cudd_bddIsVarEssential( + DdManager * manager, + DdNode * f, + int id, + int phase) +{ + DdNode *var; + int res; + DdNode *one, *zero; + + one = DD_ONE(manager); + zero = Cudd_Not(one); + + var = cuddUniqueInter(manager, id, one, zero); + + var = Cudd_NotCond(var,phase == 0); + + res = Cudd_bddIteConstant(manager, Cudd_Not(f), one, var) == one; + + return(res); + +} /* end of Cudd_bddIsVarEssential */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_FindEssential.] + + Description [Implements the recursive step of Cudd_FindEssential. + Returns a pointer to the cube BDD if successful; NULL otherwise.] + + SideEffects [None] + +******************************************************************************/ +static DdNode * +ddFindEssentialRecur( + DdManager * dd, + DdNode * f) +{ + DdNode *T, *E, *F; + DdNode *essT, *essE, *res; + int index; + DdNode *one, *lzero, *azero; + + one = DD_ONE(dd); + F = Cudd_Regular(f); + /* If f is constant the set of essential variables is empty. */ + if (cuddIsConstant(F)) return(one); + + res = cuddCacheLookup1(dd,Cudd_FindEssential,f); + if (res != NULL) { + return(res); + } + + lzero = Cudd_Not(one); + azero = DD_ZERO(dd); + /* Find cofactors: here f is non-constant. */ + T = cuddT(F); + E = cuddE(F); + if (Cudd_IsComplement(f)) { + T = Cudd_Not(T); E = Cudd_Not(E); + } + + index = F->index; + if (Cudd_IsConstant(T) && T != lzero && T != azero) { + /* if E is zero, index is essential, otherwise there are no + ** essentials, because index is not essential and no other variable + ** can be, since setting index = 1 makes the function constant and + ** different from 0. + */ + if (E == lzero || E == azero) { + res = dd->vars[index]; + } else { + res = one; + } + } else if (T == lzero || T == azero) { + if (Cudd_IsConstant(E)) { /* E cannot be zero here */ + res = Cudd_Not(dd->vars[index]); + } else { /* E == non-constant */ + /* find essentials in the else branch */ + essE = ddFindEssentialRecur(dd,E); + if (essE == NULL) { + return(NULL); + } + cuddRef(essE); + + /* add index to the set with negative phase */ + res = cuddUniqueInter(dd,index,one,Cudd_Not(essE)); + if (res == NULL) { + Cudd_RecursiveDeref(dd,essE); + return(NULL); + } + res = Cudd_Not(res); + cuddDeref(essE); + } + } else { /* T == non-const */ + if (E == lzero || E == azero) { + /* find essentials in the then branch */ + essT = ddFindEssentialRecur(dd,T); + if (essT == NULL) { + return(NULL); + } + cuddRef(essT); + + /* add index to the set with positive phase */ + /* use And because essT may be complemented */ + res = cuddBddAndRecur(dd,dd->vars[index],essT); + if (res == NULL) { + Cudd_RecursiveDeref(dd,essT); + return(NULL); + } + cuddDeref(essT); + } else if (!Cudd_IsConstant(E)) { + /* if E is a non-zero constant there are no essentials + ** because T is non-constant. + */ + essT = ddFindEssentialRecur(dd,T); + if (essT == NULL) { + return(NULL); + } + if (essT == one) { + res = one; + } else { + cuddRef(essT); + essE = ddFindEssentialRecur(dd,E); + if (essE == NULL) { + Cudd_RecursiveDeref(dd,essT); + return(NULL); + } + cuddRef(essE); + + /* res = intersection(essT, essE) */ + res = cuddBddLiteralSetIntersectionRecur(dd,essT,essE); + if (res == NULL) { + Cudd_RecursiveDeref(dd,essT); + Cudd_RecursiveDeref(dd,essE); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(dd,essT); + Cudd_RecursiveDeref(dd,essE); + cuddDeref(res); + } + } else { /* E is a non-zero constant */ + res = one; + } + } + + cuddCacheInsert1(dd,Cudd_FindEssential, f, res); + return(res); + +} /* end of ddFindEssentialRecur */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddExact.c b/abc_with_bb_support/src/bdd/cudd/cuddExact.c new file mode 100644 index 000000000..58ffbc974 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddExact.c @@ -0,0 +1,1004 @@ +/**CFile*********************************************************************** + + FileName [cuddExact.c] + + PackageName [cudd] + + Synopsis [Functions for exact variable reordering.] + + Description [External procedures included in this file: +
    +
+ Internal procedures included in this module: +
    +
  • cuddExact() +
+ Static procedures included in this module: +
    +
  • getMaxBinomial() +
  • gcd() +
  • getMatrix() +
  • freeMatrix() +
  • getLevelKeys() +
  • ddShuffle() +
  • ddSiftUp() +
  • updateUB() +
  • ddCountRoots() +
  • ddClearGlobal() +
  • computeLB() +
  • updateEntry() +
  • pushDown() +
  • initSymmInfo() +
] + + Author [Cheng Hua, Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddExact.c,v 1.1.1.1 2003/02/24 22:23:52 wjiang Exp $"; +#endif + +#ifdef DD_STATS +static int ddTotalShuffles; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int getMaxBinomial ARGS((int n)); +static int gcd ARGS((int x, int y)); +static DdHalfWord ** getMatrix ARGS((int rows, int cols)); +static void freeMatrix ARGS((DdHalfWord **matrix)); +static int getLevelKeys ARGS((DdManager *table, int l)); +static int ddShuffle ARGS((DdManager *table, DdHalfWord *permutation, int lower, int upper)); +static int ddSiftUp ARGS((DdManager *table, int x, int xLow)); +static int updateUB ARGS((DdManager *table, int oldBound, DdHalfWord *bestOrder, int lower, int upper)); +static int ddCountRoots ARGS((DdManager *table, int lower, int upper)); +static void ddClearGlobal ARGS((DdManager *table, int lower, int maxlevel)); +static int computeLB ARGS((DdManager *table, DdHalfWord *order, int roots, int cost, int lower, int upper, int level)); +static int updateEntry ARGS((DdManager *table, DdHalfWord *order, int level, int cost, DdHalfWord **orders, int *costs, int subsets, char *mask, int lower, int upper)); +static void pushDown ARGS((DdHalfWord *order, int j, int level)); +static DdHalfWord * initSymmInfo ARGS((DdManager *table, int lower, int upper)); +static int checkSymmInfo ARGS((DdManager *table, DdHalfWord *symmInfo, int index, int level)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Exact variable ordering algorithm.] + + Description [Exact variable ordering algorithm. Finds an optimum + order for the variables between lower and upper. Returns 1 if + successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddExact( + DdManager * table, + int lower, + int upper) +{ + int k, i, j; + int maxBinomial, oldSubsets, newSubsets; + int subsetCost; + int size; /* number of variables to be reordered */ + int unused, nvars, level, result; + int upperBound, lowerBound, cost; + int roots; + char *mask = NULL; + DdHalfWord *symmInfo = NULL; + DdHalfWord **newOrder = NULL; + DdHalfWord **oldOrder = NULL; + int *newCost = NULL; + int *oldCost = NULL; + DdHalfWord **tmpOrder; + int *tmpCost; + DdHalfWord *bestOrder = NULL; + DdHalfWord *order; +#ifdef DD_STATS + int ddTotalSubsets; +#endif + + /* Restrict the range to be reordered by excluding unused variables + ** at the two ends. */ + while (table->subtables[lower].keys == 1 && + table->vars[table->invperm[lower]]->ref == 1 && + lower < upper) + lower++; + while (table->subtables[upper].keys == 1 && + table->vars[table->invperm[upper]]->ref == 1 && + lower < upper) + upper--; + if (lower == upper) return(1); /* trivial problem */ + + /* Apply symmetric sifting to get a good upper bound and to extract + ** symmetry information. */ + result = cuddSymmSiftingConv(table,lower,upper); + if (result == 0) goto cuddExactOutOfMem; + +#ifdef DD_STATS + (void) fprintf(table->out,"\n"); + ddTotalShuffles = 0; + ddTotalSubsets = 0; +#endif + + /* Initialization. */ + nvars = table->size; + size = upper - lower + 1; + /* Count unused variable among those to be reordered. This is only + ** used to compute maxBinomial. */ + unused = 0; + for (i = lower + 1; i < upper; i++) { + if (table->subtables[i].keys == 1 && + table->vars[table->invperm[i]]->ref == 1) + unused++; + } + + /* Find the maximum number of subsets we may have to store. */ + maxBinomial = getMaxBinomial(size - unused); + if (maxBinomial == -1) goto cuddExactOutOfMem; + + newOrder = getMatrix(maxBinomial, size); + if (newOrder == NULL) goto cuddExactOutOfMem; + + newCost = ALLOC(int, maxBinomial); + if (newCost == NULL) goto cuddExactOutOfMem; + + oldOrder = getMatrix(maxBinomial, size); + if (oldOrder == NULL) goto cuddExactOutOfMem; + + oldCost = ALLOC(int, maxBinomial); + if (oldCost == NULL) goto cuddExactOutOfMem; + + bestOrder = ALLOC(DdHalfWord, size); + if (bestOrder == NULL) goto cuddExactOutOfMem; + + mask = ALLOC(char, nvars); + if (mask == NULL) goto cuddExactOutOfMem; + + symmInfo = initSymmInfo(table, lower, upper); + if (symmInfo == NULL) goto cuddExactOutOfMem; + + roots = ddCountRoots(table, lower, upper); + + /* Initialize the old order matrix for the empty subset and the best + ** order to the current order. The cost for the empty subset includes + ** the cost of the levels between upper and the constants. These levels + ** are not going to change. Hence, we count them only once. + */ + oldSubsets = 1; + for (i = 0; i < size; i++) { + oldOrder[0][i] = bestOrder[i] = (DdHalfWord) table->invperm[i+lower]; + } + subsetCost = table->constants.keys; + for (i = upper + 1; i < nvars; i++) + subsetCost += getLevelKeys(table,i); + oldCost[0] = subsetCost; + /* The upper bound is initialized to the current size of the BDDs. */ + upperBound = table->keys - table->isolated; + + /* Now consider subsets of increasing size. */ + for (k = 1; k <= size; k++) { +#if DD_STATS + (void) fprintf(table->out,"Processing subsets of size %d\n", k); + fflush(table->out); +#endif + newSubsets = 0; + level = size - k; /* offset of first bottom variable */ + + for (i = 0; i < oldSubsets; i++) { /* for each subset of size k-1 */ + order = oldOrder[i]; + cost = oldCost[i]; + lowerBound = computeLB(table, order, roots, cost, lower, upper, + level); + if (lowerBound >= upperBound) + continue; + /* Impose new order. */ + result = ddShuffle(table, order, lower, upper); + if (result == 0) goto cuddExactOutOfMem; + upperBound = updateUB(table,upperBound,bestOrder,lower,upper); + /* For each top bottom variable. */ + for (j = level; j >= 0; j--) { + /* Skip unused variables. */ + if (table->subtables[j+lower-1].keys == 1 && + table->vars[table->invperm[j+lower-1]]->ref == 1) continue; + /* Find cost under this order. */ + subsetCost = cost + getLevelKeys(table, lower + level); + newSubsets = updateEntry(table, order, level, subsetCost, + newOrder, newCost, newSubsets, mask, + lower, upper); + if (j == 0) + break; + if (checkSymmInfo(table, symmInfo, order[j-1], level) == 0) + continue; + pushDown(order,j-1,level); + /* Impose new order. */ + result = ddShuffle(table, order, lower, upper); + if (result == 0) goto cuddExactOutOfMem; + upperBound = updateUB(table,upperBound,bestOrder,lower,upper); + } /* for each bottom variable */ + } /* for each subset of size k */ + + /* New orders become old orders in preparation for next iteration. */ + tmpOrder = oldOrder; tmpCost = oldCost; + oldOrder = newOrder; oldCost = newCost; + newOrder = tmpOrder; newCost = tmpCost; +#ifdef DD_STATS + ddTotalSubsets += newSubsets; +#endif + oldSubsets = newSubsets; + } + result = ddShuffle(table, bestOrder, lower, upper); + if (result == 0) goto cuddExactOutOfMem; +#ifdef DD_STATS +#ifdef DD_VERBOSE + (void) fprintf(table->out,"\n"); +#endif + (void) fprintf(table->out,"#:S_EXACT %8d: total subsets\n", + ddTotalSubsets); + (void) fprintf(table->out,"#:H_EXACT %8d: total shuffles", + ddTotalShuffles); +#endif + + freeMatrix(newOrder); + freeMatrix(oldOrder); + FREE(bestOrder); + FREE(oldCost); + FREE(newCost); + FREE(symmInfo); + FREE(mask); + return(1); + +cuddExactOutOfMem: + + if (newOrder != NULL) freeMatrix(newOrder); + if (oldOrder != NULL) freeMatrix(oldOrder); + if (bestOrder != NULL) FREE(bestOrder); + if (oldCost != NULL) FREE(oldCost); + if (newCost != NULL) FREE(newCost); + if (symmInfo != NULL) FREE(symmInfo); + if (mask != NULL) FREE(mask); + table->errorCode = CUDD_MEMORY_OUT; + return(0); + +} /* end of cuddExact */ + + +/**Function******************************************************************** + + Synopsis [Returns the maximum value of (n choose k) for a given n.] + + Description [Computes the maximum value of (n choose k) for a given + n. The maximum value occurs for k = n/2 when n is even, or k = + (n-1)/2 when n is odd. The algorithm used in this procedure is + quite inefficient, but it avoids intermediate overflow problems. + Returns the computed value if successful; -1 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +getMaxBinomial( + int n) +{ + int *numerator; + int i, j, k, y, g, result; + + k = (n & ~1) >> 1; + + numerator = ALLOC(int,k); + if (numerator == NULL) return(-1); + + for (i = 0; i < k; i++) + numerator[i] = n - i; + + for (i = k; i > 1; i--) { + y = i; + for (j = 0; j < k; j++) { + if (numerator[j] == 1) continue; + g = gcd(numerator[j], y); + if (g != 1) { + numerator[j] /= g; + if (y == g) break; + y /= g; + } + } + } + + result = 1; + for (i = 0; i < k; i++) + result *= numerator[i]; + + FREE(numerator); + return(result); + +} /* end of getMaxBinomial */ + + +/**Function******************************************************************** + + Synopsis [Returns the gcd of two integers.] + + Description [Returns the gcd of two integers. Uses the binary GCD + algorithm described in Cormen, Leiserson, and Rivest.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +gcd( + int x, + int y) +{ + int a; + int b; + int lsbMask; + + /* GCD(n,0) = n. */ + if (x == 0) return(y); + if (y == 0) return(x); + + a = x; b = y; lsbMask = 1; + + /* Here both a and b are != 0. The iteration maintains this invariant. + ** Hence, we only need to check for when they become equal. + */ + while (a != b) { + if (a & lsbMask) { + if (b & lsbMask) { /* both odd */ + if (a < b) { + b = (b - a) >> 1; + } else { + a = (a - b) >> 1; + } + } else { /* a odd, b even */ + b >>= 1; + } + } else { + if (b & lsbMask) { /* a even, b odd */ + a >>= 1; + } else { /* both even */ + lsbMask <<= 1; + } + } + } + + return(a); + +} /* end of gcd */ + + +/**Function******************************************************************** + + Synopsis [Allocates a two-dimensional matrix of ints.] + + Description [Allocates a two-dimensional matrix of ints. + Returns the pointer to the matrix if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [freeMatrix] + +******************************************************************************/ +static DdHalfWord ** +getMatrix( + int rows /* number of rows */, + int cols /* number of columns */) +{ + DdHalfWord **matrix; + int i; + + if (cols*rows == 0) return(NULL); + matrix = ALLOC(DdHalfWord *, rows); + if (matrix == NULL) return(NULL); + matrix[0] = ALLOC(DdHalfWord, cols*rows); + if (matrix[0] == NULL) return(NULL); + for (i = 1; i < rows; i++) { + matrix[i] = matrix[i-1] + cols; + } + return(matrix); + +} /* end of getMatrix */ + + +/**Function******************************************************************** + + Synopsis [Frees a two-dimensional matrix allocated by getMatrix.] + + Description [] + + SideEffects [None] + + SeeAlso [getMatrix] + +******************************************************************************/ +static void +freeMatrix( + DdHalfWord ** matrix) +{ + FREE(matrix[0]); + FREE(matrix); + return; + +} /* end of freeMatrix */ + + +/**Function******************************************************************** + + Synopsis [Returns the number of nodes at one level of a unique table.] + + Description [Returns the number of nodes at one level of a unique table. + The projection function, if isolated, is not counted.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +getLevelKeys( + DdManager * table, + int l) +{ + int isolated; + int x; /* x is an index */ + + x = table->invperm[l]; + isolated = table->vars[x]->ref == 1; + + return(table->subtables[l].keys - isolated); + +} /* end of getLevelKeys */ + + +/**Function******************************************************************** + + Synopsis [Reorders variables according to a given permutation.] + + Description [Reorders variables according to a given permutation. + The i-th permutation array contains the index of the variable that + should be brought to the i-th level. ddShuffle assumes that no + dead nodes are present and that the interaction matrix is properly + initialized. The reordering is achieved by a series of upward sifts. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +ddShuffle( + DdManager * table, + DdHalfWord * permutation, + int lower, + int upper) +{ + DdHalfWord index; + int level; + int position; + int numvars; + int result; +#ifdef DD_STATS + long localTime; + int initialSize; +#ifdef DD_VERBOSE + int finalSize; +#endif + int previousSize; +#endif + +#ifdef DD_STATS + localTime = util_cpu_time(); + initialSize = table->keys - table->isolated; +#endif + + numvars = table->size; + +#if 0 + (void) fprintf(table->out,"%d:", ddTotalShuffles); + for (level = 0; level < numvars; level++) { + (void) fprintf(table->out," %d", table->invperm[level]); + } + (void) fprintf(table->out,"\n"); +#endif + + for (level = 0; level <= upper - lower; level++) { + index = permutation[level]; + position = table->perm[index]; +#ifdef DD_STATS + previousSize = table->keys - table->isolated; +#endif + result = ddSiftUp(table,position,level+lower); + if (!result) return(0); + } + +#ifdef DD_STATS + ddTotalShuffles++; +#ifdef DD_VERBOSE + finalSize = table->keys - table->isolated; + if (finalSize < initialSize) { + (void) fprintf(table->out,"-"); + } else if (finalSize > initialSize) { + (void) fprintf(table->out,"+"); + } else { + (void) fprintf(table->out,"="); + } + if ((ddTotalShuffles & 63) == 0) (void) fprintf(table->out,"\n"); + fflush(table->out); +#endif +#endif + + return(1); + +} /* end of ddShuffle */ + + +/**Function******************************************************************** + + Synopsis [Moves one variable up.] + + Description [Takes a variable from position x and sifts it up to + position xLow; xLow should be less than or equal to x. + Returns 1 if successful; 0 otherwise] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +ddSiftUp( + DdManager * table, + int x, + int xLow) +{ + int y; + int size; + + y = cuddNextLow(table,x); + while (y >= xLow) { + size = cuddSwapInPlace(table,y,x); + if (size == 0) { + return(0); + } + x = y; + y = cuddNextLow(table,x); + } + return(1); + +} /* end of ddSiftUp */ + + +/**Function******************************************************************** + + Synopsis [Updates the upper bound and saves the best order seen so far.] + + Description [Updates the upper bound and saves the best order seen so far. + Returns the current value of the upper bound.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +updateUB( + DdManager * table, + int oldBound, + DdHalfWord * bestOrder, + int lower, + int upper) +{ + int i; + int newBound = table->keys - table->isolated; + + if (newBound < oldBound) { +#ifdef DD_STATS + (void) fprintf(table->out,"New upper bound = %d\n", newBound); + fflush(table->out); +#endif + for (i = lower; i <= upper; i++) + bestOrder[i-lower] = (DdHalfWord) table->invperm[i]; + return(newBound); + } else { + return(oldBound); + } + +} /* end of updateUB */ + + +/**Function******************************************************************** + + Synopsis [Counts the number of roots.] + + Description [Counts the number of roots at the levels between lower and + upper. The computation is based on breadth-first search. + A node is a root if it is not reachable from any previously visited node. + (All the nodes at level lower are therefore considered roots.) + The visited flag uses the LSB of the next pointer. Returns the root + count. The roots that are constant nodes are always ignored.] + + SideEffects [None] + + SeeAlso [ddClearGlobal] + +******************************************************************************/ +static int +ddCountRoots( + DdManager * table, + int lower, + int upper) +{ + int i,j; + DdNode *f; + DdNodePtr *nodelist; + DdNode *sentinel = &(table->sentinel); + int slots; + int roots = 0; + int maxlevel = lower; + + for (i = lower; i <= upper; i++) { + nodelist = table->subtables[i].nodelist; + slots = table->subtables[i].slots; + for (j = 0; j < slots; j++) { + f = nodelist[j]; + while (f != sentinel) { + /* A node is a root of the DAG if it cannot be + ** reached by nodes above it. If a node was never + ** reached during the previous depth-first searches, + ** then it is a root, and we start a new depth-first + ** search from it. + */ + if (!Cudd_IsComplement(f->next)) { + if (f != table->vars[f->index]) { + roots++; + } + } + if (!Cudd_IsConstant(cuddT(f))) { + cuddT(f)->next = Cudd_Complement(cuddT(f)->next); + if (table->perm[cuddT(f)->index] > maxlevel) + maxlevel = table->perm[cuddT(f)->index]; + } + if (!Cudd_IsConstant(cuddE(f))) { + Cudd_Regular(cuddE(f))->next = + Cudd_Complement(Cudd_Regular(cuddE(f))->next); + if (table->perm[Cudd_Regular(cuddE(f))->index] > maxlevel) + maxlevel = table->perm[Cudd_Regular(cuddE(f))->index]; + } + f = Cudd_Regular(f->next); + } + } + } + ddClearGlobal(table, lower, maxlevel); + + return(roots); + +} /* end of ddCountRoots */ + + +/**Function******************************************************************** + + Synopsis [Scans the DD and clears the LSB of the next pointers.] + + Description [Scans the DD and clears the LSB of the next pointers. + The LSB of the next pointers are used as markers to tell whether a + node was reached. Once the roots are counted, these flags are + reset.] + + SideEffects [None] + + SeeAlso [ddCountRoots] + +******************************************************************************/ +static void +ddClearGlobal( + DdManager * table, + int lower, + int maxlevel) +{ + int i,j; + DdNode *f; + DdNodePtr *nodelist; + DdNode *sentinel = &(table->sentinel); + int slots; + + for (i = lower; i <= maxlevel; i++) { + nodelist = table->subtables[i].nodelist; + slots = table->subtables[i].slots; + for (j = 0; j < slots; j++) { + f = nodelist[j]; + while (f != sentinel) { + f->next = Cudd_Regular(f->next); + f = f->next; + } + } + } + +} /* end of ddClearGlobal */ + + +/**Function******************************************************************** + + Synopsis [Computes a lower bound on the size of a BDD.] + + Description [Computes a lower bound on the size of a BDD from the + following factors: +
    +
  • size of the lower part of it; +
  • size of the part of the upper part not subjected to reordering; +
  • number of roots in the part of the BDD subjected to reordering; +
  • variable in the support of the roots in the upper part of the + BDD subjected to reordering. +
      ] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +computeLB( + DdManager * table /* manager */, + DdHalfWord * order /* optimal order for the subset */, + int roots /* roots between lower and upper */, + int cost /* minimum cost for the subset */, + int lower /* lower level to be reordered */, + int upper /* upper level to be reordered */, + int level /* offset for the current top bottom var */ + ) +{ + int i; + int lb = cost; + int lb1 = 0; + int lb2; + int support; + DdHalfWord ref; + + /* The levels not involved in reordering are not going to change. + ** Add their sizes to the lower bound. + */ + for (i = 0; i < lower; i++) { + lb += getLevelKeys(table,i); + } + /* If a variable is in the support, then there is going + ** to be at least one node labeled by that variable. + */ + for (i = lower; i <= lower+level; i++) { + support = table->subtables[i].keys > 1 || + table->vars[order[i-lower]]->ref > 1; + lb1 += support; + } + + /* Estimate the number of nodes required to connect the roots to + ** the nodes in the bottom part. */ + if (lower+level+1 < table->size) { + if (lower+level < upper) + ref = table->vars[order[level+1]]->ref; + else + ref = table->vars[table->invperm[upper+1]]->ref; + lb2 = table->subtables[lower+level+1].keys - + (ref > (DdHalfWord) 1) - roots; + } else { + lb2 = 0; + } + + lb += lb1 > lb2 ? lb1 : lb2; + + return(lb); + +} /* end of computeLB */ + + +/**Function******************************************************************** + + Synopsis [Updates entry for a subset.] + + Description [Updates entry for a subset. Finds the subset, if it exists. + If the new order for the subset has lower cost, or if the subset did not + exist, it stores the new order and cost. Returns the number of subsets + currently in the table.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +updateEntry( + DdManager * table, + DdHalfWord * order, + int level, + int cost, + DdHalfWord ** orders, + int * costs, + int subsets, + char * mask, + int lower, + int upper) +{ + int i, j; + int size = upper - lower + 1; + + /* Build a mask that says what variables are in this subset. */ + for (i = lower; i <= upper; i++) + mask[table->invperm[i]] = 0; + for (i = level; i < size; i++) + mask[order[i]] = 1; + + /* Check each subset until a match is found or all subsets are examined. */ + for (i = 0; i < subsets; i++) { + DdHalfWord *subset = orders[i]; + for (j = level; j < size; j++) { + if (mask[subset[j]] == 0) + break; + } + if (j == size) /* no mismatches: success */ + break; + } + if (i == subsets || cost < costs[i]) { /* add or replace */ + for (j = 0; j < size; j++) + orders[i][j] = order[j]; + costs[i] = cost; + subsets += (i == subsets); + } + return(subsets); + +} /* end of updateEntry */ + + +/**Function******************************************************************** + + Synopsis [Pushes a variable in the order down to position "level."] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +pushDown( + DdHalfWord * order, + int j, + int level) +{ + int i; + DdHalfWord tmp; + + tmp = order[j]; + for (i = j; i < level; i++) { + order[i] = order[i+1]; + } + order[level] = tmp; + return; + +} /* end of pushDown */ + + +/**Function******************************************************************** + + Synopsis [Gathers symmetry information.] + + Description [Translates the symmetry information stored in the next + field of each subtable from level to indices. This procedure is called + immediately after symmetric sifting, so that the next fields are correct. + By translating this informaton in terms of indices, we make it independent + of subsequent reorderings. The format used is that of the next fields: + a circular list where each variable points to the next variable in the + same symmetry group. Only the entries between lower and upper are + considered. The procedure returns a pointer to an array + holding the symmetry information if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [checkSymmInfo] + +******************************************************************************/ +static DdHalfWord * +initSymmInfo( + DdManager * table, + int lower, + int upper) +{ + int level, index, next, nextindex; + DdHalfWord *symmInfo; + + symmInfo = ALLOC(DdHalfWord, table->size); + if (symmInfo == NULL) return(NULL); + + for (level = lower; level <= upper; level++) { + index = table->invperm[level]; + next = table->subtables[level].next; + nextindex = table->invperm[next]; + symmInfo[index] = nextindex; + } + return(symmInfo); + +} /* end of initSymmInfo */ + + +/**Function******************************************************************** + + Synopsis [Check symmetry condition.] + + Description [Returns 1 if a variable is the one with the highest index + among those belonging to a symmetry group that are in the top part of + the BDD. The top part is given by level.] + + SideEffects [None] + + SeeAlso [initSymmInfo] + +******************************************************************************/ +static int +checkSymmInfo( + DdManager * table, + DdHalfWord * symmInfo, + int index, + int level) +{ + int i; + + i = symmInfo[index]; + while (i != index) { + if (index < i && table->perm[i] <= level) + return(0); + i = symmInfo[i]; + } + return(1); + +} /* end of checkSymmInfo */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddExport.c b/abc_with_bb_support/src/bdd/cudd/cuddExport.c new file mode 100644 index 000000000..eafb951a3 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddExport.c @@ -0,0 +1,1289 @@ +/**CFile*********************************************************************** + + FileName [cuddExport.c] + + PackageName [cudd] + + Synopsis [Export functions.] + + Description [External procedures included in this module: +
        +
      • Cudd_DumpBlif() +
      • Cudd_DumpBlifBody() +
      • Cudd_DumpDot() +
      • Cudd_DumpDaVinci() +
      • Cudd_DumpDDcal() +
      • Cudd_DumpFactoredForm() +
      + Internal procedures included in this module: +
        +
      + Static procedures included in this module: +
        +
      • ddDoDumpBlif() +
      • ddDoDumpDaVinci() +
      • ddDoDumpDDcal() +
      • ddDoDumpFactoredForm() +
      ] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddExport.c,v 1.1.1.1 2003/02/24 22:23:52 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int ddDoDumpBlif ARGS((DdManager *dd, DdNode *f, FILE *fp, st_table *visited, char **names)); +static int ddDoDumpDaVinci ARGS((DdManager *dd, DdNode *f, FILE *fp, st_table *visited, char **names, long mask)); +static int ddDoDumpDDcal ARGS((DdManager *dd, DdNode *f, FILE *fp, st_table *visited, char **names, long mask)); +static int ddDoDumpFactoredForm ARGS((DdManager *dd, DdNode *f, FILE *fp, char **names)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Writes a blif file representing the argument BDDs.] + + Description [Writes a blif file representing the argument BDDs as a + network of multiplexers. One multiplexer is written for each BDD + node. It returns 1 in case of success; 0 otherwise (e.g., + out-of-memory, file system full, or an ADD with constants different + from 0 and 1). Cudd_DumpBlif does not close the file: This is the + caller responsibility. Cudd_DumpBlif uses a minimal unique subset of + the hexadecimal address of a node as name for it. If the argument + inames is non-null, it is assumed to hold the pointers to the names + of the inputs. Similarly for onames.] + + SideEffects [None] + + SeeAlso [Cudd_DumpBlifBody Cudd_DumpDot Cudd_PrintDebug Cudd_DumpDDcal + Cudd_DumpDaVinci Cudd_DumpFactoredForm] + +******************************************************************************/ +int +Cudd_DumpBlif( + DdManager * dd /* manager */, + int n /* number of output nodes to be dumped */, + DdNode ** f /* array of output nodes to be dumped */, + char ** inames /* array of input names (or NULL) */, + char ** onames /* array of output names (or NULL) */, + char * mname /* model name (or NULL) */, + FILE * fp /* pointer to the dump file */) +{ + DdNode *support = NULL; + DdNode *scan; + int *sorted = NULL; + int nvars = dd->size; + int retval; + int i; + + /* Build a bit array with the support of f. */ + sorted = ALLOC(int,nvars); + if (sorted == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + goto failure; + } + for (i = 0; i < nvars; i++) sorted[i] = 0; + + /* Take the union of the supports of each output function. */ + support = Cudd_VectorSupport(dd,f,n); + if (support == NULL) goto failure; + cuddRef(support); + scan = support; + while (!cuddIsConstant(scan)) { + sorted[scan->index] = 1; + scan = cuddT(scan); + } + Cudd_RecursiveDeref(dd,support); + support = NULL; /* so that we do not try to free it in case of failure */ + + /* Write the header (.model .inputs .outputs). */ + if (mname == NULL) { + retval = fprintf(fp,".model DD\n.inputs"); + } else { + retval = fprintf(fp,".model %s\n.inputs",mname); + } + if (retval == EOF) return(0); + + /* Write the input list by scanning the support array. */ + for (i = 0; i < nvars; i++) { + if (sorted[i]) { + if (inames == NULL) { + retval = fprintf(fp," %d", i); + } else { + retval = fprintf(fp," %s", inames[i]); + } + if (retval == EOF) goto failure; + } + } + FREE(sorted); + sorted = NULL; + + /* Write the .output line. */ + retval = fprintf(fp,"\n.outputs"); + if (retval == EOF) goto failure; + for (i = 0; i < n; i++) { + if (onames == NULL) { + retval = fprintf(fp," f%d", i); + } else { + retval = fprintf(fp," %s", onames[i]); + } + if (retval == EOF) goto failure; + } + retval = fprintf(fp,"\n"); + if (retval == EOF) goto failure; + + retval = Cudd_DumpBlifBody(dd, n, f, inames, onames, fp); + if (retval == 0) goto failure; + + /* Write trailer and return. */ + retval = fprintf(fp,".end\n"); + if (retval == EOF) goto failure; + + return(1); + +failure: + if (sorted != NULL) FREE(sorted); + if (support != NULL) Cudd_RecursiveDeref(dd,support); + return(0); + +} /* end of Cudd_DumpBlif */ + + +/**Function******************************************************************** + + Synopsis [Writes a blif body representing the argument BDDs.] + + Description [Writes a blif body representing the argument BDDs as a + network of multiplexers. One multiplexer is written for each BDD + node. It returns 1 in case of success; 0 otherwise (e.g., + out-of-memory, file system full, or an ADD with constants different + from 0 and 1). Cudd_DumpBlif does not close the file: This is the + caller responsibility. Cudd_DumpBlif uses a minimal unique subset of + the hexadecimal address of a node as name for it. If the argument + inames is non-null, it is assumed to hold the pointers to the names + of the inputs. Similarly for onames. This function prints out only + .names part.] + + SideEffects [None] + + SeeAlso [Cudd_DumpDot Cudd_PrintDebug Cudd_DumpDDcal + Cudd_DumpDaVinci Cudd_DumpFactoredForm] + +******************************************************************************/ +int +Cudd_DumpBlifBody( + DdManager * dd /* manager */, + int n /* number of output nodes to be dumped */, + DdNode ** f /* array of output nodes to be dumped */, + char ** inames /* array of input names (or NULL) */, + char ** onames /* array of output names (or NULL) */, + FILE * fp /* pointer to the dump file */) +{ + st_table *visited = NULL; + int retval; + int i; + + /* Initialize symbol table for visited nodes. */ + visited = st_init_table(st_ptrcmp, st_ptrhash); + if (visited == NULL) goto failure; + + /* Call the function that really gets the job done. */ + for (i = 0; i < n; i++) { + retval = ddDoDumpBlif(dd,Cudd_Regular(f[i]),fp,visited,inames); + if (retval == 0) goto failure; + } + + /* To account for the possible complement on the root, + ** we put either a buffer or an inverter at the output of + ** the multiplexer representing the top node. + */ + for (i = 0; i < n; i++) { + if (onames == NULL) { + retval = fprintf(fp, +#if SIZEOF_VOID_P == 8 + ".names %lx f%d\n", (unsigned long) f[i] / (unsigned long) sizeof(DdNode), i); +#else + ".names %x f%d\n", (unsigned) f[i] / (unsigned) sizeof(DdNode), i); +#endif + } else { + retval = fprintf(fp, +#if SIZEOF_VOID_P == 8 + ".names %lx %s\n", (unsigned long) f[i] / (unsigned long) sizeof(DdNode), onames[i]); +#else + ".names %x %s\n", (unsigned) f[i] / (unsigned) sizeof(DdNode), onames[i]); +#endif + } + if (retval == EOF) goto failure; + if (Cudd_IsComplement(f[i])) { + retval = fprintf(fp,"0 1\n"); + } else { + retval = fprintf(fp,"1 1\n"); + } + if (retval == EOF) goto failure; + } + + st_free_table(visited); + return(1); + +failure: + if (visited != NULL) st_free_table(visited); + return(0); + +} /* end of Cudd_DumpBlifBody */ + + +/**Function******************************************************************** + + Synopsis [Writes a dot file representing the argument DDs.] + + Description [Writes a file representing the argument DDs in a format + suitable for the graph drawing program dot. + It returns 1 in case of success; 0 otherwise (e.g., out-of-memory, + file system full). + Cudd_DumpDot does not close the file: This is the caller + responsibility. Cudd_DumpDot uses a minimal unique subset of the + hexadecimal address of a node as name for it. + If the argument inames is non-null, it is assumed to hold the pointers + to the names of the inputs. Similarly for onames. + Cudd_DumpDot uses the following convention to draw arcs: +
        +
      • solid line: THEN arcs; +
      • dotted line: complement arcs; +
      • dashed line: regular ELSE arcs. +
      + The dot options are chosen so that the drawing fits on a letter-size + sheet. + ] + + SideEffects [None] + + SeeAlso [Cudd_DumpBlif Cudd_PrintDebug Cudd_DumpDDcal + Cudd_DumpDaVinci Cudd_DumpFactoredForm] + +******************************************************************************/ +int +Cudd_DumpDot( + DdManager * dd /* manager */, + int n /* number of output nodes to be dumped */, + DdNode ** f /* array of output nodes to be dumped */, + char ** inames /* array of input names (or NULL) */, + char ** onames /* array of output names (or NULL) */, + FILE * fp /* pointer to the dump file */) +{ + DdNode *support = NULL; + DdNode *scan; + int *sorted = NULL; + int nvars = dd->size; + st_table *visited = NULL; + st_generator *gen = NULL; + int retval; + int i, j; + int slots; + DdNodePtr *nodelist; + long refAddr, diff, mask; + + /* Build a bit array with the support of f. */ + sorted = ALLOC(int,nvars); + if (sorted == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + goto failure; + } + for (i = 0; i < nvars; i++) sorted[i] = 0; + + /* Take the union of the supports of each output function. */ + support = Cudd_VectorSupport(dd,f,n); + if (support == NULL) goto failure; + cuddRef(support); + scan = support; + while (!cuddIsConstant(scan)) { + sorted[scan->index] = 1; + scan = cuddT(scan); + } + Cudd_RecursiveDeref(dd,support); + support = NULL; /* so that we do not try to free it in case of failure */ + + /* Initialize symbol table for visited nodes. */ + visited = st_init_table(st_ptrcmp, st_ptrhash); + if (visited == NULL) goto failure; + + /* Collect all the nodes of this DD in the symbol table. */ + for (i = 0; i < n; i++) { + retval = cuddCollectNodes(Cudd_Regular(f[i]),visited); + if (retval == 0) goto failure; + } + + /* Find how many most significant hex digits are identical + ** in the addresses of all the nodes. Build a mask based + ** on this knowledge, so that digits that carry no information + ** will not be printed. This is done in two steps. + ** 1. We scan the symbol table to find the bits that differ + ** in at least 2 addresses. + ** 2. We choose one of the possible masks. There are 8 possible + ** masks for 32-bit integer, and 16 possible masks for 64-bit + ** integers. + */ + + /* Find the bits that are different. */ + refAddr = (long) Cudd_Regular(f[0]); + diff = 0; + gen = st_init_gen(visited); + if (gen == NULL) goto failure; + while (st_gen(gen, (char **) &scan, NULL)) { + diff |= refAddr ^ (long) scan; + } + st_free_gen(gen); gen = NULL; + + /* Choose the mask. */ + for (i = 0; (unsigned) i < 8 * sizeof(long); i += 4) { + mask = (1 << i) - 1; + if (diff <= mask) break; + } + + /* Write the header and the global attributes. */ + retval = fprintf(fp,"digraph \"DD\" {\n"); + if (retval == EOF) return(0); + retval = fprintf(fp, + "size = \"7.5,10\"\ncenter = true;\nedge [dir = none];\n"); + if (retval == EOF) return(0); + + /* Write the input name subgraph by scanning the support array. */ + retval = fprintf(fp,"{ node [shape = plaintext];\n"); + if (retval == EOF) goto failure; + retval = fprintf(fp," edge [style = invis];\n"); + if (retval == EOF) goto failure; + /* We use a name ("CONST NODES") with an embedded blank, because + ** it is unlikely to appear as an input name. + */ + retval = fprintf(fp," \"CONST NODES\" [style = invis];\n"); + if (retval == EOF) goto failure; + for (i = 0; i < nvars; i++) { + if (sorted[dd->invperm[i]]) { + if (inames == NULL || inames[dd->invperm[i]] == NULL) { + retval = fprintf(fp,"\" %d \" -> ", dd->invperm[i]); + } else { + retval = fprintf(fp,"\" %s \" -> ", inames[dd->invperm[i]]); + } + if (retval == EOF) goto failure; + } + } + retval = fprintf(fp,"\"CONST NODES\"; \n}\n"); + if (retval == EOF) goto failure; + + /* Write the output node subgraph. */ + retval = fprintf(fp,"{ rank = same; node [shape = box]; edge [style = invis];\n"); + if (retval == EOF) goto failure; + for (i = 0; i < n; i++) { + if (onames == NULL) { + retval = fprintf(fp,"\"F%d\"", i); + } else { + retval = fprintf(fp,"\" %s \"", onames[i]); + } + if (retval == EOF) goto failure; + if (i == n - 1) { + retval = fprintf(fp,"; }\n"); + } else { + retval = fprintf(fp," -> "); + } + if (retval == EOF) goto failure; + } + + /* Write rank info: All nodes with the same index have the same rank. */ + for (i = 0; i < nvars; i++) { + if (sorted[dd->invperm[i]]) { + retval = fprintf(fp,"{ rank = same; "); + if (retval == EOF) goto failure; + if (inames == NULL || inames[dd->invperm[i]] == NULL) { + retval = fprintf(fp,"\" %d \";\n", dd->invperm[i]); + } else { + retval = fprintf(fp,"\" %s \";\n", inames[dd->invperm[i]]); + } + if (retval == EOF) goto failure; + nodelist = dd->subtables[i].nodelist; + slots = dd->subtables[i].slots; + for (j = 0; j < slots; j++) { + scan = nodelist[j]; + while (scan != NULL) { + if (st_is_member(visited,(char *) scan)) { + retval = fprintf(fp,"\"%lx\";\n", (mask & (long) scan) / sizeof(DdNode)); + if (retval == EOF) goto failure; + } + scan = scan->next; + } + } + retval = fprintf(fp,"}\n"); + if (retval == EOF) goto failure; + } + } + + /* All constants have the same rank. */ + retval = fprintf(fp, + "{ rank = same; \"CONST NODES\";\n{ node [shape = box]; "); + if (retval == EOF) goto failure; + nodelist = dd->constants.nodelist; + slots = dd->constants.slots; + for (j = 0; j < slots; j++) { + scan = nodelist[j]; + while (scan != NULL) { + if (st_is_member(visited,(char *) scan)) { + retval = fprintf(fp,"\"%lx\";\n", (mask & (long) scan) / sizeof(DdNode)); + if (retval == EOF) goto failure; + } + scan = scan->next; + } + } + retval = fprintf(fp,"}\n}\n"); + if (retval == EOF) goto failure; + + /* Write edge info. */ + /* Edges from the output nodes. */ + for (i = 0; i < n; i++) { + if (onames == NULL) { + retval = fprintf(fp,"\"F%d\"", i); + } else { + retval = fprintf(fp,"\" %s \"", onames[i]); + } + if (retval == EOF) goto failure; + /* Account for the possible complement on the root. */ + if (Cudd_IsComplement(f[i])) { + retval = fprintf(fp," -> \"%lx\" [style = dotted];\n", + (mask & (long) f[i]) / sizeof(DdNode)); + } else { + retval = fprintf(fp," -> \"%lx\" [style = solid];\n", + (mask & (long) f[i]) / sizeof(DdNode)); + } + if (retval == EOF) goto failure; + } + + /* Edges from internal nodes. */ + for (i = 0; i < nvars; i++) { + if (sorted[dd->invperm[i]]) { + nodelist = dd->subtables[i].nodelist; + slots = dd->subtables[i].slots; + for (j = 0; j < slots; j++) { + scan = nodelist[j]; + while (scan != NULL) { + if (st_is_member(visited,(char *) scan)) { + retval = fprintf(fp, + "\"%lx\" -> \"%lx\";\n", + (mask & (long) scan) / sizeof(DdNode), + (mask & (long) cuddT(scan)) / sizeof(DdNode)); + if (retval == EOF) goto failure; + if (Cudd_IsComplement(cuddE(scan))) { + retval = fprintf(fp, + "\"%lx\" -> \"%lx\" [style = dotted];\n", + (mask & (long) scan) / sizeof(DdNode), + (mask & (long) cuddE(scan)) / sizeof(DdNode)); + } else { + retval = fprintf(fp, + "\"%lx\" -> \"%lx\" [style = dashed];\n", + (mask & (long) scan) / sizeof(DdNode), + (mask & (long) cuddE(scan)) / sizeof(DdNode)); + } + if (retval == EOF) goto failure; + } + scan = scan->next; + } + } + } + } + + /* Write constant labels. */ + nodelist = dd->constants.nodelist; + slots = dd->constants.slots; + for (j = 0; j < slots; j++) { + scan = nodelist[j]; + while (scan != NULL) { + if (st_is_member(visited,(char *) scan)) { + retval = fprintf(fp,"\"%lx\" [label = \"%g\"];\n", + (mask & (long) scan) / sizeof(DdNode), cuddV(scan)); + if (retval == EOF) goto failure; + } + scan = scan->next; + } + } + + /* Write trailer and return. */ + retval = fprintf(fp,"}\n"); + if (retval == EOF) goto failure; + + st_free_table(visited); + FREE(sorted); + return(1); + +failure: + if (sorted != NULL) FREE(sorted); + if (support != NULL) Cudd_RecursiveDeref(dd,support); + if (visited != NULL) st_free_table(visited); + return(0); + +} /* end of Cudd_DumpDot */ + + +/**Function******************************************************************** + + Synopsis [Writes a daVinci file representing the argument BDDs.] + + Description [Writes a daVinci file representing the argument BDDs. + It returns 1 in case of success; 0 otherwise (e.g., out-of-memory or + file system full). Cudd_DumpDaVinci does not close the file: This + is the caller responsibility. Cudd_DumpDaVinci uses a minimal unique + subset of the hexadecimal address of a node as name for it. If the + argument inames is non-null, it is assumed to hold the pointers to + the names of the inputs. Similarly for onames.] + + SideEffects [None] + + SeeAlso [Cudd_DumpDot Cudd_PrintDebug Cudd_DumpBlif Cudd_DumpDDcal + Cudd_DumpFactoredForm] + +******************************************************************************/ +int +Cudd_DumpDaVinci( + DdManager * dd /* manager */, + int n /* number of output nodes to be dumped */, + DdNode ** f /* array of output nodes to be dumped */, + char ** inames /* array of input names (or NULL) */, + char ** onames /* array of output names (or NULL) */, + FILE * fp /* pointer to the dump file */) +{ + DdNode *support = NULL; + DdNode *scan; + st_table *visited = NULL; + int retval; + int i; + st_generator *gen; + long refAddr, diff, mask; + + /* Initialize symbol table for visited nodes. */ + visited = st_init_table(st_ptrcmp, st_ptrhash); + if (visited == NULL) goto failure; + + /* Collect all the nodes of this DD in the symbol table. */ + for (i = 0; i < n; i++) { + retval = cuddCollectNodes(Cudd_Regular(f[i]),visited); + if (retval == 0) goto failure; + } + + /* Find how many most significant hex digits are identical + ** in the addresses of all the nodes. Build a mask based + ** on this knowledge, so that digits that carry no information + ** will not be printed. This is done in two steps. + ** 1. We scan the symbol table to find the bits that differ + ** in at least 2 addresses. + ** 2. We choose one of the possible masks. There are 8 possible + ** masks for 32-bit integer, and 16 possible masks for 64-bit + ** integers. + */ + + /* Find the bits that are different. */ + refAddr = (long) Cudd_Regular(f[0]); + diff = 0; + gen = st_init_gen(visited); + while (st_gen(gen, (char **) &scan, NULL)) { + diff |= refAddr ^ (long) scan; + } + st_free_gen(gen); + + /* Choose the mask. */ + for (i = 0; (unsigned) i < 8 * sizeof(long); i += 4) { + mask = (1 << i) - 1; + if (diff <= mask) break; + } + st_free_table(visited); + + /* Initialize symbol table for visited nodes. */ + visited = st_init_table(st_ptrcmp, st_ptrhash); + if (visited == NULL) goto failure; + + retval = fprintf(fp, "["); + if (retval == EOF) goto failure; + /* Call the function that really gets the job done. */ + for (i = 0; i < n; i++) { + if (onames == NULL) { + retval = fprintf(fp, + "l(\"f%d\",n(\"root\",[a(\"OBJECT\",\"f%d\")],", + i,i); + } else { + retval = fprintf(fp, + "l(\"%s\",n(\"root\",[a(\"OBJECT\",\"%s\")],", + onames[i], onames[i]); + } + if (retval == EOF) goto failure; + retval = fprintf(fp, "[e(\"edge\",[a(\"EDGECOLOR\",\"%s\"),a(\"_DIR\",\"none\")],", + Cudd_IsComplement(f[i]) ? "red" : "blue"); + if (retval == EOF) goto failure; + retval = ddDoDumpDaVinci(dd,Cudd_Regular(f[i]),fp,visited,inames,mask); + if (retval == 0) goto failure; + retval = fprintf(fp, ")]))%s", i == n-1 ? "" : ","); + if (retval == EOF) goto failure; + } + + /* Write trailer and return. */ + retval = fprintf(fp, "]\n"); + if (retval == EOF) goto failure; + + st_free_table(visited); + return(1); + +failure: + if (support != NULL) Cudd_RecursiveDeref(dd,support); + if (visited != NULL) st_free_table(visited); + return(0); + +} /* end of Cudd_DumpDaVinci */ + + +/**Function******************************************************************** + + Synopsis [Writes a DDcal file representing the argument BDDs.] + + Description [Writes a DDcal file representing the argument BDDs. + It returns 1 in case of success; 0 otherwise (e.g., out-of-memory or + file system full). Cudd_DumpDDcal does not close the file: This + is the caller responsibility. Cudd_DumpDDcal uses a minimal unique + subset of the hexadecimal address of a node as name for it. If the + argument inames is non-null, it is assumed to hold the pointers to + the names of the inputs. Similarly for onames.] + + SideEffects [None] + + SeeAlso [Cudd_DumpDot Cudd_PrintDebug Cudd_DumpBlif Cudd_DumpDaVinci + Cudd_DumpFactoredForm] + +******************************************************************************/ +int +Cudd_DumpDDcal( + DdManager * dd /* manager */, + int n /* number of output nodes to be dumped */, + DdNode ** f /* array of output nodes to be dumped */, + char ** inames /* array of input names (or NULL) */, + char ** onames /* array of output names (or NULL) */, + FILE * fp /* pointer to the dump file */) +{ + DdNode *support = NULL; + DdNode *scan; + int *sorted = NULL; + int nvars = dd->size; + st_table *visited = NULL; + int retval; + int i; + st_generator *gen; + long refAddr, diff, mask; + + /* Initialize symbol table for visited nodes. */ + visited = st_init_table(st_ptrcmp, st_ptrhash); + if (visited == NULL) goto failure; + + /* Collect all the nodes of this DD in the symbol table. */ + for (i = 0; i < n; i++) { + retval = cuddCollectNodes(Cudd_Regular(f[i]),visited); + if (retval == 0) goto failure; + } + + /* Find how many most significant hex digits are identical + ** in the addresses of all the nodes. Build a mask based + ** on this knowledge, so that digits that carry no information + ** will not be printed. This is done in two steps. + ** 1. We scan the symbol table to find the bits that differ + ** in at least 2 addresses. + ** 2. We choose one of the possible masks. There are 8 possible + ** masks for 32-bit integer, and 16 possible masks for 64-bit + ** integers. + */ + + /* Find the bits that are different. */ + refAddr = (long) Cudd_Regular(f[0]); + diff = 0; + gen = st_init_gen(visited); + while (st_gen(gen, (char **) &scan, NULL)) { + diff |= refAddr ^ (long) scan; + } + st_free_gen(gen); + + /* Choose the mask. */ + for (i = 0; (unsigned) i < 8 * sizeof(long); i += 4) { + mask = (1 << i) - 1; + if (diff <= mask) break; + } + st_free_table(visited); + + /* Build a bit array with the support of f. */ + sorted = ALLOC(int,nvars); + if (sorted == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + goto failure; + } + for (i = 0; i < nvars; i++) sorted[i] = 0; + + /* Take the union of the supports of each output function. */ + support = Cudd_VectorSupport(dd,f,n); + if (support == NULL) goto failure; + cuddRef(support); + scan = support; + while (!cuddIsConstant(scan)) { + sorted[scan->index] = 1; + scan = cuddT(scan); + } + Cudd_RecursiveDeref(dd,support); + support = NULL; /* so that we do not try to free it in case of failure */ + for (i = 0; i < nvars; i++) { + if (sorted[dd->invperm[i]]) { + if (inames == NULL || inames[dd->invperm[i]] == NULL) { + retval = fprintf(fp,"v%d", dd->invperm[i]); + } else { + retval = fprintf(fp,"%s", inames[dd->invperm[i]]); + } + if (retval == EOF) goto failure; + } + retval = fprintf(fp,"%s", i == nvars - 1 ? "\n" : " * "); + if (retval == EOF) goto failure; + } + FREE(sorted); + sorted = NULL; + + /* Initialize symbol table for visited nodes. */ + visited = st_init_table(st_ptrcmp, st_ptrhash); + if (visited == NULL) goto failure; + + /* Call the function that really gets the job done. */ + for (i = 0; i < n; i++) { + retval = ddDoDumpDDcal(dd,Cudd_Regular(f[i]),fp,visited,inames,mask); + if (retval == 0) goto failure; + if (onames == NULL) { + retval = fprintf(fp, "f%d = ", i); + } else { + retval = fprintf(fp, "%s = ", onames[i]); + } + if (retval == EOF) goto failure; + retval = fprintf(fp, "n%lx%s\n", + ((long) f[i] & mask) / sizeof(DdNode), + Cudd_IsComplement(f[i]) ? "'" : ""); + if (retval == EOF) goto failure; + } + + /* Write trailer and return. */ + retval = fprintf(fp, "["); + if (retval == EOF) goto failure; + for (i = 0; i < n; i++) { + if (onames == NULL) { + retval = fprintf(fp, "f%d", i); + } else { + retval = fprintf(fp, "%s", onames[i]); + } + retval = fprintf(fp, "%s", i == n-1 ? "" : " "); + if (retval == EOF) goto failure; + } + retval = fprintf(fp, "]\n"); + if (retval == EOF) goto failure; + + st_free_table(visited); + return(1); + +failure: + if (sorted != NULL) FREE(sorted); + if (support != NULL) Cudd_RecursiveDeref(dd,support); + if (visited != NULL) st_free_table(visited); + return(0); + +} /* end of Cudd_DumpDDcal */ + + +/**Function******************************************************************** + + Synopsis [Writes factored forms representing the argument BDDs.] + + Description [Writes factored forms representing the argument BDDs. + The format of the factored form is the one used in the genlib files + for technology mapping in sis. It returns 1 in case of success; 0 + otherwise (e.g., file system full). Cudd_DumpFactoredForm does not + close the file: This is the caller responsibility. Caution must be + exercised because a factored form may be exponentially larger than + the argument BDD. If the argument inames is non-null, it is assumed + to hold the pointers to the names of the inputs. Similarly for + onames.] + + SideEffects [None] + + SeeAlso [Cudd_DumpDot Cudd_PrintDebug Cudd_DumpBlif Cudd_DumpDaVinci + Cudd_DumpDDcal] + +******************************************************************************/ +int +Cudd_DumpFactoredForm( + DdManager * dd /* manager */, + int n /* number of output nodes to be dumped */, + DdNode ** f /* array of output nodes to be dumped */, + char ** inames /* array of input names (or NULL) */, + char ** onames /* array of output names (or NULL) */, + FILE * fp /* pointer to the dump file */) +{ + int retval; + int i; + + /* Call the function that really gets the job done. */ + for (i = 0; i < n; i++) { + if (onames == NULL) { + retval = fprintf(fp, "f%d = ", i); + } else { + retval = fprintf(fp, "%s = ", onames[i]); + } + if (retval == EOF) return(0); + if (f[i] == DD_ONE(dd)) { + retval = fprintf(fp, "CONST1"); + if (retval == EOF) return(0); + } else if (f[i] == Cudd_Not(DD_ONE(dd)) || f[i] == DD_ZERO(dd)) { + retval = fprintf(fp, "CONST0"); + if (retval == EOF) return(0); + } else { + retval = fprintf(fp, "%s", Cudd_IsComplement(f[i]) ? "!(" : ""); + if (retval == EOF) return(0); + retval = ddDoDumpFactoredForm(dd,Cudd_Regular(f[i]),fp,inames); + if (retval == 0) return(0); + retval = fprintf(fp, "%s", Cudd_IsComplement(f[i]) ? ")" : ""); + if (retval == EOF) return(0); + } + retval = fprintf(fp, "%s", i == n-1 ? "" : "\n"); + if (retval == EOF) return(0); + } + + return(1); + +} /* end of Cudd_DumpFactoredForm */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_DumpBlif.] + + Description [Performs the recursive step of Cudd_DumpBlif. Traverses + the BDD f and writes a multiplexer-network description to the file + pointed by fp in blif format. f is assumed to be a regular pointer + and ddDoDumpBlif guarantees this assumption in the recursive calls.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +ddDoDumpBlif( + DdManager * dd, + DdNode * f, + FILE * fp, + st_table * visited, + char ** names) +{ + DdNode *T, *E; + int retval; + +#ifdef DD_DEBUG + assert(!Cudd_IsComplement(f)); +#endif + + /* If already visited, nothing to do. */ + if (st_is_member(visited, (char *) f) == 1) + return(1); + + /* Check for abnormal condition that should never happen. */ + if (f == NULL) + return(0); + + /* Mark node as visited. */ + if (st_insert(visited, (char *) f, NULL) == ST_OUT_OF_MEM) + return(0); + + /* Check for special case: If constant node, generate constant 1. */ + if (f == DD_ONE(dd)) { +#if SIZEOF_VOID_P == 8 + retval = fprintf(fp, ".names %lx\n1\n",(unsigned long) f / (unsigned long) sizeof(DdNode)); +#else + retval = fprintf(fp, ".names %x\n1\n",(unsigned) f / (unsigned) sizeof(DdNode)); +#endif + if (retval == EOF) { + return(0); + } else { + return(1); + } + } + + /* Check whether this is an ADD. We deal with 0-1 ADDs, but not + ** with the general case. + */ + if (f == DD_ZERO(dd)) { +#if SIZEOF_VOID_P == 8 + retval = fprintf(fp, ".names %lx\n",(unsigned long) f / (unsigned long) sizeof(DdNode)); +#else + retval = fprintf(fp, ".names %x\n",(unsigned) f / (unsigned) sizeof(DdNode)); +#endif + if (retval == EOF) { + return(0); + } else { + return(1); + } + } + if (cuddIsConstant(f)) + return(0); + + /* Recursive calls. */ + T = cuddT(f); + retval = ddDoDumpBlif(dd,T,fp,visited,names); + if (retval != 1) return(retval); + E = Cudd_Regular(cuddE(f)); + retval = ddDoDumpBlif(dd,E,fp,visited,names); + if (retval != 1) return(retval); + + /* Write multiplexer taking complement arc into account. */ + if (names != NULL) { + retval = fprintf(fp,".names %s", names[f->index]); + } else { + retval = fprintf(fp,".names %d", f->index); + } + if (retval == EOF) + return(0); + +#if SIZEOF_VOID_P == 8 + if (Cudd_IsComplement(cuddE(f))) { + retval = fprintf(fp," %lx %lx %lx\n11- 1\n0-0 1\n", + (unsigned long) T / (unsigned long) sizeof(DdNode), + (unsigned long) E / (unsigned long) sizeof(DdNode), + (unsigned long) f / (unsigned long) sizeof(DdNode)); + } else { + retval = fprintf(fp," %lx %lx %lx\n11- 1\n0-1 1\n", + (unsigned long) T / (unsigned long) sizeof(DdNode), + (unsigned long) E / (unsigned long) sizeof(DdNode), + (unsigned long) f / (unsigned long) sizeof(DdNode)); + } +#else + if (Cudd_IsComplement(cuddE(f))) { + retval = fprintf(fp," %x %x %x\n11- 1\n0-0 1\n", + (unsigned) T / (unsigned) sizeof(DdNode), + (unsigned) E / (unsigned) sizeof(DdNode), + (unsigned) f / (unsigned) sizeof(DdNode)); + } else { + retval = fprintf(fp," %x %x %x\n11- 1\n0-1 1\n", + (unsigned) T / (unsigned) sizeof(DdNode), + (unsigned) E / (unsigned) sizeof(DdNode), + (unsigned) f / (unsigned) sizeof(DdNode)); + } +#endif + if (retval == EOF) { + return(0); + } else { + return(1); + } + +} /* end of ddDoDumpBlif */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_DumpDaVinci.] + + Description [Performs the recursive step of Cudd_DumpDaVinci. Traverses + the BDD f and writes a term expression to the file + pointed by fp in daVinci format. f is assumed to be a regular pointer + and ddDoDumpDaVinci guarantees this assumption in the recursive calls.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +ddDoDumpDaVinci( + DdManager * dd, + DdNode * f, + FILE * fp, + st_table * visited, + char ** names, + long mask) +{ + DdNode *T, *E; + int retval; + long id; + +#ifdef DD_DEBUG + assert(!Cudd_IsComplement(f)); +#endif + + id = ((long) f & mask) / sizeof(DdNode); + + /* If already visited, insert a reference. */ + if (st_is_member(visited, (char *) f) == 1) { + retval = fprintf(fp,"r(\"%lx\")", id); + if (retval == EOF) { + return(0); + } else { + return(1); + } + } + + /* Check for abnormal condition that should never happen. */ + if (f == NULL) + return(0); + + /* Mark node as visited. */ + if (st_insert(visited, (char *) f, NULL) == ST_OUT_OF_MEM) + return(0); + + /* Check for special case: If constant node, generate constant 1. */ + if (Cudd_IsConstant(f)) { + retval = fprintf(fp, "l(\"%lx\",n(\"constant\",[a(\"OBJECT\",\"%g\")],[]))", id, cuddV(f)); + if (retval == EOF) { + return(0); + } else { + return(1); + } + } + + /* Recursive calls. */ + if (names != NULL) { + retval = fprintf(fp, + "l(\"%lx\",n(\"internal\",[a(\"OBJECT\",\"%s\"),", + id, names[f->index]); + } else { + retval = fprintf(fp, + "l(\"%lx\",n(\"internal\",[a(\"OBJECT\",\"%d\"),", + id, f->index); + } + retval = fprintf(fp, "a(\"_GO\",\"ellipse\")],[e(\"then\",[a(\"EDGECOLOR\",\"blue\"),a(\"_DIR\",\"none\")],"); + if (retval == EOF) return(0); + T = cuddT(f); + retval = ddDoDumpDaVinci(dd,T,fp,visited,names,mask); + if (retval != 1) return(retval); + retval = fprintf(fp, "),e(\"else\",[a(\"EDGECOLOR\",\"%s\"),a(\"_DIR\",\"none\")],", + Cudd_IsComplement(cuddE(f)) ? "red" : "green"); + if (retval == EOF) return(0); + E = Cudd_Regular(cuddE(f)); + retval = ddDoDumpDaVinci(dd,E,fp,visited,names,mask); + if (retval != 1) return(retval); + + retval = fprintf(fp,")]))"); + if (retval == EOF) { + return(0); + } else { + return(1); + } + +} /* end of ddDoDumpDaVinci */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_DumpDDcal.] + + Description [Performs the recursive step of Cudd_DumpDDcal. Traverses + the BDD f and writes a line for each node to the file + pointed by fp in DDcal format. f is assumed to be a regular pointer + and ddDoDumpDDcal guarantees this assumption in the recursive calls.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +ddDoDumpDDcal( + DdManager * dd, + DdNode * f, + FILE * fp, + st_table * visited, + char ** names, + long mask) +{ + DdNode *T, *E; + int retval; + long id, idT, idE; + +#ifdef DD_DEBUG + assert(!Cudd_IsComplement(f)); +#endif + + id = ((long) f & mask) / sizeof(DdNode); + + /* If already visited, do nothing. */ + if (st_is_member(visited, (char *) f) == 1) { + return(1); + } + + /* Check for abnormal condition that should never happen. */ + if (f == NULL) + return(0); + + /* Mark node as visited. */ + if (st_insert(visited, (char *) f, NULL) == ST_OUT_OF_MEM) + return(0); + + /* Check for special case: If constant node, assign constant. */ + if (Cudd_IsConstant(f)) { + if (f != DD_ONE(dd) && f != DD_ZERO(dd)) + return(0); + retval = fprintf(fp, "n%lx = %g\n", id, cuddV(f)); + if (retval == EOF) { + return(0); + } else { + return(1); + } + } + + /* Recursive calls. */ + T = cuddT(f); + retval = ddDoDumpDDcal(dd,T,fp,visited,names,mask); + if (retval != 1) return(retval); + E = Cudd_Regular(cuddE(f)); + retval = ddDoDumpDDcal(dd,E,fp,visited,names,mask); + if (retval != 1) return(retval); + idT = ((long) T & mask) / sizeof(DdNode); + idE = ((long) E & mask) / sizeof(DdNode); + if (names != NULL) { + retval = fprintf(fp, "n%lx = %s * n%lx + %s' * n%lx%s\n", + id, names[f->index], idT, names[f->index], + idE, Cudd_IsComplement(cuddE(f)) ? "'" : ""); + } else { + retval = fprintf(fp, "n%lx = v%d * n%lx + v%d' * n%lx%s\n", + id, f->index, idT, f->index, + idE, Cudd_IsComplement(cuddE(f)) ? "'" : ""); + } + if (retval == EOF) { + return(0); + } else { + return(1); + } + +} /* end of ddDoDumpDDcal */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_DumpFactoredForm.] + + Description [Performs the recursive step of + Cudd_DumpFactoredForm. Traverses the BDD f and writes a factored + form for each node to the file pointed by fp in terms of the + factored forms of the children. Constants are propagated, and + absorption is applied. f is assumed to be a regular pointer and + ddDoDumpFActoredForm guarantees this assumption in the recursive + calls.] + + SideEffects [None] + + SeeAlso [Cudd_DumpFactoredForm] + +******************************************************************************/ +static int +ddDoDumpFactoredForm( + DdManager * dd, + DdNode * f, + FILE * fp, + char ** names) +{ + DdNode *T, *E; + int retval; + +#ifdef DD_DEBUG + assert(!Cudd_IsComplement(f)); + assert(!Cudd_IsConstant(f)); +#endif + + /* Check for abnormal condition that should never happen. */ + if (f == NULL) + return(0); + + /* Recursive calls. */ + T = cuddT(f); + E = cuddE(f); + if (T != DD_ZERO(dd)) { + if (E != DD_ONE(dd)) { + if (names != NULL) { + retval = fprintf(fp, "%s", names[f->index]); + } else { + retval = fprintf(fp, "x%d", f->index); + } + if (retval == EOF) return(0); + } + if (T != DD_ONE(dd)) { + retval = fprintf(fp, "%s(", E != DD_ONE(dd) ? " * " : ""); + if (retval == EOF) return(0); + retval = ddDoDumpFactoredForm(dd,T,fp,names); + if (retval != 1) return(retval); + retval = fprintf(fp, ")"); + if (retval == EOF) return(0); + } + if (E == Cudd_Not(DD_ONE(dd)) || E == DD_ZERO(dd)) return(1); + retval = fprintf(fp, " + "); + if (retval == EOF) return(0); + } + E = Cudd_Regular(E); + if (T != DD_ONE(dd)) { + if (names != NULL) { + retval = fprintf(fp, "!%s", names[f->index]); + } else { + retval = fprintf(fp, "!x%d", f->index); + } + if (retval == EOF) return(0); + } + if (E != DD_ONE(dd)) { + retval = fprintf(fp, "%s%s(", T != DD_ONE(dd) ? " * " : "", + E != cuddE(f) ? "!" : ""); + if (retval == EOF) return(0); + retval = ddDoDumpFactoredForm(dd,E,fp,names); + if (retval != 1) return(retval); + retval = fprintf(fp, ")"); + if (retval == EOF) return(0); + } + return(1); + +} /* end of ddDoDumpFactoredForm */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddGenCof.c b/abc_with_bb_support/src/bdd/cudd/cuddGenCof.c new file mode 100644 index 000000000..8d60ddda2 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddGenCof.c @@ -0,0 +1,1968 @@ +/**CFile*********************************************************************** + + FileName [cuddGenCof.c] + + PackageName [cudd] + + Synopsis [Generalized cofactors for BDDs and ADDs.] + + Description [External procedures included in this module: +
        +
      • Cudd_bddConstrain() +
      • Cudd_bddRestrict() +
      • Cudd_addConstrain() +
      • Cudd_bddConstrainDecomp() +
      • Cudd_addRestrict() +
      • Cudd_bddCharToVect() +
      • Cudd_bddLICompaction() +
      • Cudd_bddSqueeze() +
      • Cudd_SubsetCompress() +
      • Cudd_SupersetCompress() +
      + Internal procedures included in this module: +
        +
      • cuddBddConstrainRecur() +
      • cuddBddRestrictRecur() +
      • cuddAddConstrainRecur() +
      • cuddAddRestrictRecur() +
      • cuddBddLICompaction() +
      + Static procedures included in this module: +
        +
      • cuddBddConstrainDecomp() +
      • cuddBddCharToVect() +
      • cuddBddLICMarkEdges() +
      • cuddBddLICBuildResult() +
      • cuddBddSqueeze() +
      + ] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/* Codes for edge markings in Cudd_bddLICompaction. The codes are defined +** so that they can be bitwise ORed to implement the code priority scheme. +*/ +#define DD_LIC_DC 0 +#define DD_LIC_1 1 +#define DD_LIC_0 2 +#define DD_LIC_NL 3 + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/* Key for the cache used in the edge marking phase. */ +typedef struct MarkCacheKey { + DdNode *f; + DdNode *c; +} MarkCacheKey; + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddGenCof.c,v 1.1.1.1 2003/02/24 22:23:52 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int cuddBddConstrainDecomp ARGS((DdManager *dd, DdNode *f, DdNode **decomp)); +static DdNode * cuddBddCharToVect ARGS((DdManager *dd, DdNode *f, DdNode *x)); +static int cuddBddLICMarkEdges ARGS((DdManager *dd, DdNode *f, DdNode *c, st_table *table, st_table *cache)); +static DdNode * cuddBddLICBuildResult ARGS((DdManager *dd, DdNode *f, st_table *cache, st_table *table)); +static int MarkCacheHash ARGS((char *ptr, int modulus)); +static int MarkCacheCompare ARGS((const char *ptr1, const char *ptr2)); +static enum st_retval MarkCacheCleanUp ARGS((char *key, char *value, char *arg)); +static DdNode * cuddBddSqueeze ARGS((DdManager *dd, DdNode *l, DdNode *u)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Computes f constrain c.] + + Description [Computes f constrain c (f @ c). + Uses a canonical form: (f' @ c) = ( f @ c)'. (Note: this is not true + for c.) List of special cases: +
        +
      • f @ 0 = 0 +
      • f @ 1 = f +
      • 0 @ c = 0 +
      • 1 @ c = 1 +
      • f @ f = 1 +
      • f @ f'= 0 +
      + Returns a pointer to the result if successful; NULL otherwise. Note that if + F=(f1,...,fn) and reordering takes place while computing F @ c, then the + image restriction property (Img(F,c) = Img(F @ c)) is lost.] + + SideEffects [None] + + SeeAlso [Cudd_bddRestrict Cudd_addConstrain] + +******************************************************************************/ +DdNode * +Cudd_bddConstrain( + DdManager * dd, + DdNode * f, + DdNode * c) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddBddConstrainRecur(dd,f,c); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_bddConstrain */ + + +/**Function******************************************************************** + + Synopsis [BDD restrict according to Coudert and Madre's algorithm + (ICCAD90).] + + Description [BDD restrict according to Coudert and Madre's algorithm + (ICCAD90). Returns the restricted BDD if successful; otherwise NULL. + If application of restrict results in a BDD larger than the input + BDD, the input BDD is returned.] + + SideEffects [None] + + SeeAlso [Cudd_bddConstrain Cudd_addRestrict] + +******************************************************************************/ +DdNode * +Cudd_bddRestrict( + DdManager * dd, + DdNode * f, + DdNode * c) +{ + DdNode *suppF, *suppC, *commonSupport; + DdNode *cplus, *res; + int retval; + int sizeF, sizeRes; + + /* Check terminal cases here to avoid computing supports in trivial cases. + ** This also allows us notto check later for the case c == 0, in which + ** there is no common support. */ + if (c == Cudd_Not(DD_ONE(dd))) return(Cudd_Not(DD_ONE(dd))); + if (Cudd_IsConstant(f)) return(f); + if (f == c) return(DD_ONE(dd)); + if (f == Cudd_Not(c)) return(Cudd_Not(DD_ONE(dd))); + + /* Check if supports intersect. */ + retval = Cudd_ClassifySupport(dd,f,c,&commonSupport,&suppF,&suppC); + if (retval == 0) { + return(NULL); + } + cuddRef(commonSupport); cuddRef(suppF); cuddRef(suppC); + Cudd_IterDerefBdd(dd,suppF); + + if (commonSupport == DD_ONE(dd)) { + Cudd_IterDerefBdd(dd,commonSupport); + Cudd_IterDerefBdd(dd,suppC); + return(f); + } + Cudd_IterDerefBdd(dd,commonSupport); + + /* Abstract from c the variables that do not appear in f. */ + cplus = Cudd_bddExistAbstract(dd, c, suppC); + if (cplus == NULL) { + Cudd_IterDerefBdd(dd,suppC); + return(NULL); + } + cuddRef(cplus); + Cudd_IterDerefBdd(dd,suppC); + + do { + dd->reordered = 0; + res = cuddBddRestrictRecur(dd, f, cplus); + } while (dd->reordered == 1); + if (res == NULL) { + Cudd_IterDerefBdd(dd,cplus); + return(NULL); + } + cuddRef(res); + Cudd_IterDerefBdd(dd,cplus); + /* Make restric safe by returning the smaller of the input and the + ** result. */ + sizeF = Cudd_DagSize(f); + sizeRes = Cudd_DagSize(res); + if (sizeF <= sizeRes) { + Cudd_IterDerefBdd(dd, res); + return(f); + } else { + cuddDeref(res); + return(res); + } + +} /* end of Cudd_bddRestrict */ + + +/**Function******************************************************************** + + Synopsis [Computes f constrain c for ADDs.] + + Description [Computes f constrain c (f @ c), for f an ADD and c a 0-1 + ADD. List of special cases: +
        +
      • F @ 0 = 0 +
      • F @ 1 = F +
      • 0 @ c = 0 +
      • 1 @ c = 1 +
      • F @ F = 1 +
      + Returns a pointer to the result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddConstrain] + +******************************************************************************/ +DdNode * +Cudd_addConstrain( + DdManager * dd, + DdNode * f, + DdNode * c) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddAddConstrainRecur(dd,f,c); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_addConstrain */ + + +/**Function******************************************************************** + + Synopsis [BDD conjunctive decomposition as in McMillan's CAV96 paper.] + + Description [BDD conjunctive decomposition as in McMillan's CAV96 + paper. The decomposition is canonical only for a given variable + order. If canonicity is required, variable ordering must be disabled + after the decomposition has been computed. Returns an array with one + entry for each BDD variable in the manager if successful; otherwise + NULL. The components of the solution have their reference counts + already incremented (unlike the results of most other functions in + the package.] + + SideEffects [None] + + SeeAlso [Cudd_bddConstrain Cudd_bddExistAbstract] + +******************************************************************************/ +DdNode ** +Cudd_bddConstrainDecomp( + DdManager * dd, + DdNode * f) +{ + DdNode **decomp; + int res; + int i; + + /* Create an initialize decomposition array. */ + decomp = ALLOC(DdNode *,dd->size); + if (decomp == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + for (i = 0; i < dd->size; i++) { + decomp[i] = NULL; + } + do { + dd->reordered = 0; + /* Clean up the decomposition array in case reordering took place. */ + for (i = 0; i < dd->size; i++) { + if (decomp[i] != NULL) { + Cudd_IterDerefBdd(dd, decomp[i]); + decomp[i] = NULL; + } + } + res = cuddBddConstrainDecomp(dd,f,decomp); + } while (dd->reordered == 1); + if (res == 0) { + FREE(decomp); + return(NULL); + } + /* Missing components are constant ones. */ + for (i = 0; i < dd->size; i++) { + if (decomp[i] == NULL) { + decomp[i] = DD_ONE(dd); + cuddRef(decomp[i]); + } + } + return(decomp); + +} /* end of Cudd_bddConstrainDecomp */ + + +/**Function******************************************************************** + + Synopsis [ADD restrict according to Coudert and Madre's algorithm + (ICCAD90).] + + Description [ADD restrict according to Coudert and Madre's algorithm + (ICCAD90). Returns the restricted ADD if successful; otherwise NULL. + If application of restrict results in an ADD larger than the input + ADD, the input ADD is returned.] + + SideEffects [None] + + SeeAlso [Cudd_addConstrain Cudd_bddRestrict] + +******************************************************************************/ +DdNode * +Cudd_addRestrict( + DdManager * dd, + DdNode * f, + DdNode * c) +{ + DdNode *supp_f, *supp_c; + DdNode *res, *commonSupport; + int intersection; + int sizeF, sizeRes; + + /* Check if supports intersect. */ + supp_f = Cudd_Support(dd, f); + if (supp_f == NULL) { + return(NULL); + } + cuddRef(supp_f); + supp_c = Cudd_Support(dd, c); + if (supp_c == NULL) { + Cudd_RecursiveDeref(dd,supp_f); + return(NULL); + } + cuddRef(supp_c); + commonSupport = Cudd_bddLiteralSetIntersection(dd, supp_f, supp_c); + if (commonSupport == NULL) { + Cudd_RecursiveDeref(dd,supp_f); + Cudd_RecursiveDeref(dd,supp_c); + return(NULL); + } + cuddRef(commonSupport); + Cudd_RecursiveDeref(dd,supp_f); + Cudd_RecursiveDeref(dd,supp_c); + intersection = commonSupport != DD_ONE(dd); + Cudd_RecursiveDeref(dd,commonSupport); + + if (intersection) { + do { + dd->reordered = 0; + res = cuddAddRestrictRecur(dd, f, c); + } while (dd->reordered == 1); + sizeF = Cudd_DagSize(f); + sizeRes = Cudd_DagSize(res); + if (sizeF <= sizeRes) { + cuddRef(res); + Cudd_RecursiveDeref(dd, res); + return(f); + } else { + return(res); + } + } else { + return(f); + } + +} /* end of Cudd_addRestrict */ + + +/**Function******************************************************************** + + Synopsis [Computes a vector whose image equals a non-zero function.] + + Description [Computes a vector of BDDs whose image equals a non-zero + function. + The result depends on the variable order. The i-th component of the vector + depends only on the first i variables in the order. Each BDD in the vector + is not larger than the BDD of the given characteristic function. This + function is based on the description of char-to-vect in "Verification of + Sequential Machines Using Boolean Functional Vectors" by O. Coudert, C. + Berthet and J. C. Madre. + Returns a pointer to an array containing the result if successful; NULL + otherwise. The size of the array equals the number of variables in the + manager. The components of the solution have their reference counts + already incremented (unlike the results of most other functions in + the package.] + + SideEffects [None] + + SeeAlso [Cudd_bddConstrain] + +******************************************************************************/ +DdNode ** +Cudd_bddCharToVect( + DdManager * dd, + DdNode * f) +{ + int i, j; + DdNode **vect; + DdNode *res = NULL; + + if (f == Cudd_Not(DD_ONE(dd))) return(NULL); + + vect = ALLOC(DdNode *, dd->size); + if (vect == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + + do { + dd->reordered = 0; + for (i = 0; i < dd->size; i++) { + res = cuddBddCharToVect(dd,f,dd->vars[dd->invperm[i]]); + if (res == NULL) { + /* Clean up the vector array in case reordering took place. */ + for (j = 0; j < i; j++) { + Cudd_IterDerefBdd(dd, vect[dd->invperm[j]]); + } + break; + } + cuddRef(res); + vect[dd->invperm[i]] = res; + } + } while (dd->reordered == 1); + if (res == NULL) { + FREE(vect); + return(NULL); + } + return(vect); + +} /* end of Cudd_bddCharToVect */ + + +/**Function******************************************************************** + + Synopsis [Performs safe minimization of a BDD.] + + Description [Performs safe minimization of a BDD. Given the BDD + f of a function to be minimized and a BDD + c representing the care set, Cudd_bddLICompaction + produces the BDD of a function that agrees with f + wherever c is 1. Safe minimization means that the size + of the result is guaranteed not to exceed the size of + f. This function is based on the DAC97 paper by Hong et + al.. Returns a pointer to the result if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddRestrict] + +******************************************************************************/ +DdNode * +Cudd_bddLICompaction( + DdManager * dd /* manager */, + DdNode * f /* function to be minimized */, + DdNode * c /* constraint (care set) */) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddBddLICompaction(dd,f,c); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_bddLICompaction */ + + +/**Function******************************************************************** + + Synopsis [Finds a small BDD in a function interval.] + + Description [Finds a small BDD in a function interval. Given BDDs + l and u, representing the lower bound and + upper bound of a function interval, Cudd_bddSqueeze produces the BDD + of a function within the interval with a small BDD. Returns a + pointer to the result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddRestrict Cudd_bddLICompaction] + +******************************************************************************/ +DdNode * +Cudd_bddSqueeze( + DdManager * dd /* manager */, + DdNode * l /* lower bound */, + DdNode * u /* upper bound */) +{ + DdNode *res; + int sizeRes, sizeL, sizeU; + + do { + dd->reordered = 0; + res = cuddBddSqueeze(dd,l,u); + } while (dd->reordered == 1); + if (res == NULL) return(NULL); + /* We now compare the result with the bounds and return the smallest. + ** We first compare to u, so that in case l == 0 and u == 1, we return + ** 0 as in other minimization algorithms. */ + sizeRes = Cudd_DagSize(res); + sizeU = Cudd_DagSize(u); + if (sizeU <= sizeRes) { + cuddRef(res); + Cudd_IterDerefBdd(dd,res); + res = u; + sizeRes = sizeU; + } + sizeL = Cudd_DagSize(l); + if (sizeL <= sizeRes) { + cuddRef(res); + Cudd_IterDerefBdd(dd,res); + res = l; + sizeRes = sizeL; + } + return(res); + +} /* end of Cudd_bddSqueeze */ + + +/**Function******************************************************************** + + Synopsis [Finds a small BDD that agrees with f over + c.] + + Description [Finds a small BDD that agrees with f over + c. Returns a pointer to the result if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddRestrict Cudd_bddLICompaction Cudd_bddSqueeze] + +******************************************************************************/ +DdNode * +Cudd_bddMinimize( + DdManager * dd, + DdNode * f, + DdNode * c) +{ + DdNode *cplus, *res; + + if (c == Cudd_Not(DD_ONE(dd))) return(c); + if (Cudd_IsConstant(f)) return(f); + if (f == c) return(DD_ONE(dd)); + if (f == Cudd_Not(c)) return(Cudd_Not(DD_ONE(dd))); + + cplus = Cudd_RemapOverApprox(dd,c,0,0,1.0); + if (cplus == NULL) return(NULL); + cuddRef(cplus); + res = Cudd_bddLICompaction(dd,f,cplus); + if (res == NULL) { + Cudd_IterDerefBdd(dd,cplus); + return(NULL); + } + cuddRef(res); + Cudd_IterDerefBdd(dd,cplus); + cuddDeref(res); + return(res); + +} /* end of Cudd_bddMinimize */ + + +/**Function******************************************************************** + + Synopsis [Find a dense subset of BDD f.] + + Description [Finds a dense subset of BDD f. Density is + the ratio of number of minterms to number of nodes. Uses several + techniques in series. It is more expensive than other subsetting + procedures, but often produces better results. See + Cudd_SubsetShortPaths for a description of the threshold and nvars + parameters. Returns a pointer to the result if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_SubsetRemap Cudd_SubsetShortPaths Cudd_SubsetHeavyBranch + Cudd_bddSqueeze] + +******************************************************************************/ +DdNode * +Cudd_SubsetCompress( + DdManager * dd /* manager */, + DdNode * f /* BDD whose subset is sought */, + int nvars /* number of variables in the support of f */, + int threshold /* maximum number of nodes in the subset */) +{ + DdNode *res, *tmp1, *tmp2; + + tmp1 = Cudd_SubsetShortPaths(dd, f, nvars, threshold, 0); + if (tmp1 == NULL) return(NULL); + cuddRef(tmp1); + tmp2 = Cudd_RemapUnderApprox(dd,tmp1,nvars,0,1.0); + if (tmp2 == NULL) { + Cudd_IterDerefBdd(dd,tmp1); + return(NULL); + } + cuddRef(tmp2); + Cudd_IterDerefBdd(dd,tmp1); + res = Cudd_bddSqueeze(dd,tmp2,f); + if (res == NULL) { + Cudd_IterDerefBdd(dd,tmp2); + return(NULL); + } + cuddRef(res); + Cudd_IterDerefBdd(dd,tmp2); + cuddDeref(res); + return(res); + +} /* end of Cudd_SubsetCompress */ + + +/**Function******************************************************************** + + Synopsis [Find a dense superset of BDD f.] + + Description [Finds a dense superset of BDD f. Density is + the ratio of number of minterms to number of nodes. Uses several + techniques in series. It is more expensive than other supersetting + procedures, but often produces better results. See + Cudd_SupersetShortPaths for a description of the threshold and nvars + parameters. Returns a pointer to the result if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_SubsetCompress Cudd_SupersetRemap Cudd_SupersetShortPaths + Cudd_SupersetHeavyBranch Cudd_bddSqueeze] + +******************************************************************************/ +DdNode * +Cudd_SupersetCompress( + DdManager * dd /* manager */, + DdNode * f /* BDD whose superset is sought */, + int nvars /* number of variables in the support of f */, + int threshold /* maximum number of nodes in the superset */) +{ + DdNode *subset; + + subset = Cudd_SubsetCompress(dd, Cudd_Not(f),nvars,threshold); + + return(Cudd_NotCond(subset, (subset != NULL))); + +} /* end of Cudd_SupersetCompress */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_bddConstrain.] + + Description [Performs the recursive step of Cudd_bddConstrain. + Returns a pointer to the result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddConstrain] + +******************************************************************************/ +DdNode * +cuddBddConstrainRecur( + DdManager * dd, + DdNode * f, + DdNode * c) +{ + DdNode *Fv, *Fnv, *Cv, *Cnv, *t, *e, *r; + DdNode *one, *zero; + unsigned int topf, topc; + int index; + int comple = 0; + + statLine(dd); + one = DD_ONE(dd); + zero = Cudd_Not(one); + + /* Trivial cases. */ + if (c == one) return(f); + if (c == zero) return(zero); + if (Cudd_IsConstant(f)) return(f); + if (f == c) return(one); + if (f == Cudd_Not(c)) return(zero); + + /* Make canonical to increase the utilization of the cache. */ + if (Cudd_IsComplement(f)) { + f = Cudd_Not(f); + comple = 1; + } + /* Now f is a regular pointer to a non-constant node; c is also + ** non-constant, but may be complemented. + */ + + /* Check the cache. */ + r = cuddCacheLookup2(dd, Cudd_bddConstrain, f, c); + if (r != NULL) { + return(Cudd_NotCond(r,comple)); + } + + /* Recursive step. */ + topf = dd->perm[f->index]; + topc = dd->perm[Cudd_Regular(c)->index]; + if (topf <= topc) { + index = f->index; + Fv = cuddT(f); Fnv = cuddE(f); + } else { + index = Cudd_Regular(c)->index; + Fv = Fnv = f; + } + if (topc <= topf) { + Cv = cuddT(Cudd_Regular(c)); Cnv = cuddE(Cudd_Regular(c)); + if (Cudd_IsComplement(c)) { + Cv = Cudd_Not(Cv); + Cnv = Cudd_Not(Cnv); + } + } else { + Cv = Cnv = c; + } + + if (!Cudd_IsConstant(Cv)) { + t = cuddBddConstrainRecur(dd, Fv, Cv); + if (t == NULL) + return(NULL); + } else if (Cv == one) { + t = Fv; + } else { /* Cv == zero: return Fnv @ Cnv */ + if (Cnv == one) { + r = Fnv; + } else { + r = cuddBddConstrainRecur(dd, Fnv, Cnv); + if (r == NULL) + return(NULL); + } + return(Cudd_NotCond(r,comple)); + } + cuddRef(t); + + if (!Cudd_IsConstant(Cnv)) { + e = cuddBddConstrainRecur(dd, Fnv, Cnv); + if (e == NULL) { + Cudd_IterDerefBdd(dd, t); + return(NULL); + } + } else if (Cnv == one) { + e = Fnv; + } else { /* Cnv == zero: return Fv @ Cv previously computed */ + cuddDeref(t); + return(Cudd_NotCond(t,comple)); + } + cuddRef(e); + + if (Cudd_IsComplement(t)) { + t = Cudd_Not(t); + e = Cudd_Not(e); + r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); + if (r == NULL) { + Cudd_IterDerefBdd(dd, e); + Cudd_IterDerefBdd(dd, t); + return(NULL); + } + r = Cudd_Not(r); + } else { + r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); + if (r == NULL) { + Cudd_IterDerefBdd(dd, e); + Cudd_IterDerefBdd(dd, t); + return(NULL); + } + } + cuddDeref(t); + cuddDeref(e); + + cuddCacheInsert2(dd, Cudd_bddConstrain, f, c, r); + return(Cudd_NotCond(r,comple)); + +} /* end of cuddBddConstrainRecur */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_bddRestrict.] + + Description [Performs the recursive step of Cudd_bddRestrict. + Returns the restricted BDD if successful; otherwise NULL.] + + SideEffects [None] + + SeeAlso [Cudd_bddRestrict] + +******************************************************************************/ +DdNode * +cuddBddRestrictRecur( + DdManager * dd, + DdNode * f, + DdNode * c) +{ + DdNode *Fv, *Fnv, *Cv, *Cnv, *t, *e, *r, *one, *zero; + unsigned int topf, topc; + int index; + int comple = 0; + + statLine(dd); + one = DD_ONE(dd); + zero = Cudd_Not(one); + + /* Trivial cases */ + if (c == one) return(f); + if (c == zero) return(zero); + if (Cudd_IsConstant(f)) return(f); + if (f == c) return(one); + if (f == Cudd_Not(c)) return(zero); + + /* Make canonical to increase the utilization of the cache. */ + if (Cudd_IsComplement(f)) { + f = Cudd_Not(f); + comple = 1; + } + /* Now f is a regular pointer to a non-constant node; c is also + ** non-constant, but may be complemented. + */ + + /* Check the cache. */ + r = cuddCacheLookup2(dd, Cudd_bddRestrict, f, c); + if (r != NULL) { + return(Cudd_NotCond(r,comple)); + } + + topf = dd->perm[f->index]; + topc = dd->perm[Cudd_Regular(c)->index]; + + if (topc < topf) { /* abstract top variable from c */ + DdNode *d, *s1, *s2; + + /* Find complements of cofactors of c. */ + if (Cudd_IsComplement(c)) { + s1 = cuddT(Cudd_Regular(c)); + s2 = cuddE(Cudd_Regular(c)); + } else { + s1 = Cudd_Not(cuddT(c)); + s2 = Cudd_Not(cuddE(c)); + } + /* Take the OR by applying DeMorgan. */ + d = cuddBddAndRecur(dd, s1, s2); + if (d == NULL) return(NULL); + d = Cudd_Not(d); + cuddRef(d); + r = cuddBddRestrictRecur(dd, f, d); + if (r == NULL) { + Cudd_IterDerefBdd(dd, d); + return(NULL); + } + cuddRef(r); + Cudd_IterDerefBdd(dd, d); + cuddCacheInsert2(dd, Cudd_bddRestrict, f, c, r); + cuddDeref(r); + return(Cudd_NotCond(r,comple)); + } + + /* Recursive step. Here topf <= topc. */ + index = f->index; + Fv = cuddT(f); Fnv = cuddE(f); + if (topc == topf) { + Cv = cuddT(Cudd_Regular(c)); Cnv = cuddE(Cudd_Regular(c)); + if (Cudd_IsComplement(c)) { + Cv = Cudd_Not(Cv); + Cnv = Cudd_Not(Cnv); + } + } else { + Cv = Cnv = c; + } + + if (!Cudd_IsConstant(Cv)) { + t = cuddBddRestrictRecur(dd, Fv, Cv); + if (t == NULL) return(NULL); + } else if (Cv == one) { + t = Fv; + } else { /* Cv == zero: return(Fnv @ Cnv) */ + if (Cnv == one) { + r = Fnv; + } else { + r = cuddBddRestrictRecur(dd, Fnv, Cnv); + if (r == NULL) return(NULL); + } + return(Cudd_NotCond(r,comple)); + } + cuddRef(t); + + if (!Cudd_IsConstant(Cnv)) { + e = cuddBddRestrictRecur(dd, Fnv, Cnv); + if (e == NULL) { + Cudd_IterDerefBdd(dd, t); + return(NULL); + } + } else if (Cnv == one) { + e = Fnv; + } else { /* Cnv == zero: return (Fv @ Cv) previously computed */ + cuddDeref(t); + return(Cudd_NotCond(t,comple)); + } + cuddRef(e); + + if (Cudd_IsComplement(t)) { + t = Cudd_Not(t); + e = Cudd_Not(e); + r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); + if (r == NULL) { + Cudd_IterDerefBdd(dd, e); + Cudd_IterDerefBdd(dd, t); + return(NULL); + } + r = Cudd_Not(r); + } else { + r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); + if (r == NULL) { + Cudd_IterDerefBdd(dd, e); + Cudd_IterDerefBdd(dd, t); + return(NULL); + } + } + cuddDeref(t); + cuddDeref(e); + + cuddCacheInsert2(dd, Cudd_bddRestrict, f, c, r); + return(Cudd_NotCond(r,comple)); + +} /* end of cuddBddRestrictRecur */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_addConstrain.] + + Description [Performs the recursive step of Cudd_addConstrain. + Returns a pointer to the result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addConstrain] + +******************************************************************************/ +DdNode * +cuddAddConstrainRecur( + DdManager * dd, + DdNode * f, + DdNode * c) +{ + DdNode *Fv, *Fnv, *Cv, *Cnv, *t, *e, *r; + DdNode *one, *zero; + unsigned int topf, topc; + int index; + + statLine(dd); + one = DD_ONE(dd); + zero = DD_ZERO(dd); + + /* Trivial cases. */ + if (c == one) return(f); + if (c == zero) return(zero); + if (Cudd_IsConstant(f)) return(f); + if (f == c) return(one); + + /* Now f and c are non-constant. */ + + /* Check the cache. */ + r = cuddCacheLookup2(dd, Cudd_addConstrain, f, c); + if (r != NULL) { + return(r); + } + + /* Recursive step. */ + topf = dd->perm[f->index]; + topc = dd->perm[c->index]; + if (topf <= topc) { + index = f->index; + Fv = cuddT(f); Fnv = cuddE(f); + } else { + index = c->index; + Fv = Fnv = f; + } + if (topc <= topf) { + Cv = cuddT(c); Cnv = cuddE(c); + } else { + Cv = Cnv = c; + } + + if (!Cudd_IsConstant(Cv)) { + t = cuddAddConstrainRecur(dd, Fv, Cv); + if (t == NULL) + return(NULL); + } else if (Cv == one) { + t = Fv; + } else { /* Cv == zero: return Fnv @ Cnv */ + if (Cnv == one) { + r = Fnv; + } else { + r = cuddAddConstrainRecur(dd, Fnv, Cnv); + if (r == NULL) + return(NULL); + } + return(r); + } + cuddRef(t); + + if (!Cudd_IsConstant(Cnv)) { + e = cuddAddConstrainRecur(dd, Fnv, Cnv); + if (e == NULL) { + Cudd_RecursiveDeref(dd, t); + return(NULL); + } + } else if (Cnv == one) { + e = Fnv; + } else { /* Cnv == zero: return Fv @ Cv previously computed */ + cuddDeref(t); + return(t); + } + cuddRef(e); + + r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); + if (r == NULL) { + Cudd_RecursiveDeref(dd, e); + Cudd_RecursiveDeref(dd, t); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + + cuddCacheInsert2(dd, Cudd_addConstrain, f, c, r); + return(r); + +} /* end of cuddAddConstrainRecur */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_addRestrict.] + + Description [Performs the recursive step of Cudd_addRestrict. + Returns the restricted ADD if successful; otherwise NULL.] + + SideEffects [None] + + SeeAlso [Cudd_addRestrict] + +******************************************************************************/ +DdNode * +cuddAddRestrictRecur( + DdManager * dd, + DdNode * f, + DdNode * c) +{ + DdNode *Fv, *Fnv, *Cv, *Cnv, *t, *e, *r, *one, *zero; + unsigned int topf, topc; + int index; + + statLine(dd); + one = DD_ONE(dd); + zero = DD_ZERO(dd); + + /* Trivial cases */ + if (c == one) return(f); + if (c == zero) return(zero); + if (Cudd_IsConstant(f)) return(f); + if (f == c) return(one); + + /* Now f and c are non-constant. */ + + /* Check the cache. */ + r = cuddCacheLookup2(dd, Cudd_addRestrict, f, c); + if (r != NULL) { + return(r); + } + + topf = dd->perm[f->index]; + topc = dd->perm[c->index]; + + if (topc < topf) { /* abstract top variable from c */ + DdNode *d, *s1, *s2; + + /* Find cofactors of c. */ + s1 = cuddT(c); + s2 = cuddE(c); + /* Take the OR by applying DeMorgan. */ + d = cuddAddApplyRecur(dd, Cudd_addOr, s1, s2); + if (d == NULL) return(NULL); + cuddRef(d); + r = cuddAddRestrictRecur(dd, f, d); + if (r == NULL) { + Cudd_RecursiveDeref(dd, d); + return(NULL); + } + cuddRef(r); + Cudd_RecursiveDeref(dd, d); + cuddCacheInsert2(dd, Cudd_addRestrict, f, c, r); + cuddDeref(r); + return(r); + } + + /* Recursive step. Here topf <= topc. */ + index = f->index; + Fv = cuddT(f); Fnv = cuddE(f); + if (topc == topf) { + Cv = cuddT(c); Cnv = cuddE(c); + } else { + Cv = Cnv = c; + } + + if (!Cudd_IsConstant(Cv)) { + t = cuddAddRestrictRecur(dd, Fv, Cv); + if (t == NULL) return(NULL); + } else if (Cv == one) { + t = Fv; + } else { /* Cv == zero: return(Fnv @ Cnv) */ + if (Cnv == one) { + r = Fnv; + } else { + r = cuddAddRestrictRecur(dd, Fnv, Cnv); + if (r == NULL) return(NULL); + } + return(r); + } + cuddRef(t); + + if (!Cudd_IsConstant(Cnv)) { + e = cuddAddRestrictRecur(dd, Fnv, Cnv); + if (e == NULL) { + Cudd_RecursiveDeref(dd, t); + return(NULL); + } + } else if (Cnv == one) { + e = Fnv; + } else { /* Cnv == zero: return (Fv @ Cv) previously computed */ + cuddDeref(t); + return(t); + } + cuddRef(e); + + r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); + if (r == NULL) { + Cudd_RecursiveDeref(dd, e); + Cudd_RecursiveDeref(dd, t); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + + cuddCacheInsert2(dd, Cudd_addRestrict, f, c, r); + return(r); + +} /* end of cuddAddRestrictRecur */ + + + +/**Function******************************************************************** + + Synopsis [Performs safe minimization of a BDD.] + + Description [Performs safe minimization of a BDD. Given the BDD + f of a function to be minimized and a BDD + c representing the care set, Cudd_bddLICompaction + produces the BDD of a function that agrees with f + wherever c is 1. Safe minimization means that the size + of the result is guaranteed not to exceed the size of + f. This function is based on the DAC97 paper by Hong et + al.. Returns a pointer to the result if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddLICompaction] + +******************************************************************************/ +DdNode * +cuddBddLICompaction( + DdManager * dd /* manager */, + DdNode * f /* function to be minimized */, + DdNode * c /* constraint (care set) */) +{ + st_table *marktable, *markcache, *buildcache; + DdNode *res, *zero; + + zero = Cudd_Not(DD_ONE(dd)); + if (c == zero) return(zero); + + /* We need to use local caches for both steps of this operation. + ** The results of the edge marking step are only valid as long as the + ** edge markings themselves are available. However, the edge markings + ** are lost at the end of one invocation of Cudd_bddLICompaction. + ** Hence, the cache entries for the edge marking step must be + ** invalidated at the end of this function. + ** For the result of the building step we argue as follows. The result + ** for a node and a given constrain depends on the BDD in which the node + ** appears. Hence, the same node and constrain may give different results + ** in successive invocations. + */ + marktable = st_init_table(st_ptrcmp,st_ptrhash); + if (marktable == NULL) { + return(NULL); + } + markcache = st_init_table(MarkCacheCompare,MarkCacheHash); + if (markcache == NULL) { + st_free_table(marktable); + return(NULL); + } + if (cuddBddLICMarkEdges(dd,f,c,marktable,markcache) == CUDD_OUT_OF_MEM) { + st_foreach(markcache, MarkCacheCleanUp, NULL); + st_free_table(marktable); + st_free_table(markcache); + return(NULL); + } + st_foreach(markcache, MarkCacheCleanUp, NULL); + st_free_table(markcache); + buildcache = st_init_table(st_ptrcmp,st_ptrhash); + if (buildcache == NULL) { + st_free_table(marktable); + return(NULL); + } + res = cuddBddLICBuildResult(dd,f,buildcache,marktable); + st_free_table(buildcache); + st_free_table(marktable); + return(res); + +} /* end of cuddBddLICompaction */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_bddConstrainDecomp.] + + Description [Performs the recursive step of Cudd_bddConstrainDecomp. + Returns f super (i) if successful; otherwise NULL.] + + SideEffects [None] + + SeeAlso [Cudd_bddConstrainDecomp] + +******************************************************************************/ +static int +cuddBddConstrainDecomp( + DdManager * dd, + DdNode * f, + DdNode ** decomp) +{ + DdNode *F, *fv, *fvn; + DdNode *fAbs; + DdNode *result; + int ok; + + if (Cudd_IsConstant(f)) return(1); + /* Compute complements of cofactors. */ + F = Cudd_Regular(f); + fv = cuddT(F); + fvn = cuddE(F); + if (F == f) { + fv = Cudd_Not(fv); + fvn = Cudd_Not(fvn); + } + /* Compute abstraction of top variable. */ + fAbs = cuddBddAndRecur(dd, fv, fvn); + if (fAbs == NULL) { + return(0); + } + cuddRef(fAbs); + fAbs = Cudd_Not(fAbs); + /* Recursively find the next abstraction and the components of the + ** decomposition. */ + ok = cuddBddConstrainDecomp(dd, fAbs, decomp); + if (ok == 0) { + Cudd_IterDerefBdd(dd,fAbs); + return(0); + } + /* Compute the component of the decomposition corresponding to the + ** top variable and store it in the decomposition array. */ + result = cuddBddConstrainRecur(dd, f, fAbs); + if (result == NULL) { + Cudd_IterDerefBdd(dd,fAbs); + return(0); + } + cuddRef(result); + decomp[F->index] = result; + Cudd_IterDerefBdd(dd, fAbs); + return(1); + +} /* end of cuddBddConstrainDecomp */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_bddCharToVect.] + + Description [Performs the recursive step of Cudd_bddCharToVect. + This function maintains the invariant that f is non-zero. + Returns the i-th component of the vector if successful; otherwise NULL.] + + SideEffects [None] + + SeeAlso [Cudd_bddCharToVect] + +******************************************************************************/ +static DdNode * +cuddBddCharToVect( + DdManager * dd, + DdNode * f, + DdNode * x) +{ + unsigned int topf; + unsigned int level; + int comple; + + DdNode *one, *zero, *res, *F, *fT, *fE, *T, *E; + + statLine(dd); + /* Check the cache. */ + res = cuddCacheLookup2(dd, cuddBddCharToVect, f, x); + if (res != NULL) { + return(res); + } + + F = Cudd_Regular(f); + + topf = cuddI(dd,F->index); + level = dd->perm[x->index]; + + if (topf > level) return(x); + + one = DD_ONE(dd); + zero = Cudd_Not(one); + + comple = F != f; + fT = Cudd_NotCond(cuddT(F),comple); + fE = Cudd_NotCond(cuddE(F),comple); + + if (topf == level) { + if (fT == zero) return(zero); + if (fE == zero) return(one); + return(x); + } + + /* Here topf < level. */ + if (fT == zero) return(cuddBddCharToVect(dd, fE, x)); + if (fE == zero) return(cuddBddCharToVect(dd, fT, x)); + + T = cuddBddCharToVect(dd, fT, x); + if (T == NULL) { + return(NULL); + } + cuddRef(T); + E = cuddBddCharToVect(dd, fE, x); + if (E == NULL) { + Cudd_IterDerefBdd(dd,T); + return(NULL); + } + cuddRef(E); + res = cuddBddIteRecur(dd, dd->vars[F->index], T, E); + if (res == NULL) { + Cudd_IterDerefBdd(dd,T); + Cudd_IterDerefBdd(dd,E); + return(NULL); + } + cuddDeref(T); + cuddDeref(E); + cuddCacheInsert2(dd, cuddBddCharToVect, f, x, res); + return(res); + +} /* end of cuddBddCharToVect */ + + +/**Function******************************************************************** + + Synopsis [Performs the edge marking step of Cudd_bddLICompaction.] + + Description [Performs the edge marking step of Cudd_bddLICompaction. + Returns the LUB of the markings of the two outgoing edges of f + if successful; otherwise CUDD_OUT_OF_MEM.] + + SideEffects [None] + + SeeAlso [Cudd_bddLICompaction cuddBddLICBuildResult] + +******************************************************************************/ +static int +cuddBddLICMarkEdges( + DdManager * dd, + DdNode * f, + DdNode * c, + st_table * table, + st_table * cache) +{ + DdNode *Fv, *Fnv, *Cv, *Cnv; + DdNode *one, *zero; + unsigned int topf, topc; + int index; + int comple; + int resT, resE, res, retval; + char **slot; + MarkCacheKey *key; + + one = DD_ONE(dd); + zero = Cudd_Not(one); + + /* Terminal cases. */ + if (c == zero) return(DD_LIC_DC); + if (f == one) return(DD_LIC_1); + if (f == zero) return(DD_LIC_0); + + /* Make canonical to increase the utilization of the cache. */ + comple = Cudd_IsComplement(f); + f = Cudd_Regular(f); + /* Now f is a regular pointer to a non-constant node; c may be + ** constant, or it may be complemented. + */ + + /* Check the cache. */ + key = ALLOC(MarkCacheKey, 1); + if (key == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(CUDD_OUT_OF_MEM); + } + key->f = f; key->c = c; + if (st_lookup(cache, (char *)key, (char **)&res)) { + FREE(key); + if (comple) { + if (res == DD_LIC_0) res = DD_LIC_1; + else if (res == DD_LIC_1) res = DD_LIC_0; + } + return(res); + } + + /* Recursive step. */ + topf = dd->perm[f->index]; + topc = cuddI(dd,Cudd_Regular(c)->index); + if (topf <= topc) { + index = f->index; + Fv = cuddT(f); Fnv = cuddE(f); + } else { + index = Cudd_Regular(c)->index; + Fv = Fnv = f; + } + if (topc <= topf) { + /* We know that c is not constant because f is not. */ + Cv = cuddT(Cudd_Regular(c)); Cnv = cuddE(Cudd_Regular(c)); + if (Cudd_IsComplement(c)) { + Cv = Cudd_Not(Cv); + Cnv = Cudd_Not(Cnv); + } + } else { + Cv = Cnv = c; + } + + resT = cuddBddLICMarkEdges(dd, Fv, Cv, table, cache); + if (resT == CUDD_OUT_OF_MEM) { + FREE(key); + return(CUDD_OUT_OF_MEM); + } + resE = cuddBddLICMarkEdges(dd, Fnv, Cnv, table, cache); + if (resE == CUDD_OUT_OF_MEM) { + FREE(key); + return(CUDD_OUT_OF_MEM); + } + + /* Update edge markings. */ + if (topf <= topc) { + retval = st_find_or_add(table, (char *)f, (char ***)&slot); + if (retval == 0) { + *slot = (char *) (ptrint)((resT << 2) | resE); + } else if (retval == 1) { + *slot = (char *) (ptrint)((int)((ptrint) *slot) | (resT << 2) | resE); + } else { + FREE(key); + return(CUDD_OUT_OF_MEM); + } + } + + /* Cache result. */ + res = resT | resE; + if (st_insert(cache, (char *)key, (char *)(ptrint)res) == ST_OUT_OF_MEM) { + FREE(key); + return(CUDD_OUT_OF_MEM); + } + + /* Take into account possible complementation. */ + if (comple) { + if (res == DD_LIC_0) res = DD_LIC_1; + else if (res == DD_LIC_1) res = DD_LIC_0; + } + return(res); + +} /* end of cuddBddLICMarkEdges */ + + +/**Function******************************************************************** + + Synopsis [Builds the result of Cudd_bddLICompaction.] + + Description [Builds the results of Cudd_bddLICompaction. + Returns a pointer to the minimized BDD if successful; otherwise NULL.] + + SideEffects [None] + + SeeAlso [Cudd_bddLICompaction cuddBddLICMarkEdges] + +******************************************************************************/ +static DdNode * +cuddBddLICBuildResult( + DdManager * dd, + DdNode * f, + st_table * cache, + st_table * table) +{ + DdNode *Fv, *Fnv, *r, *t, *e; + DdNode *one, *zero; + unsigned int topf; + int index; + int comple; + int markT, markE, markings; + + one = DD_ONE(dd); + zero = Cudd_Not(one); + + if (Cudd_IsConstant(f)) return(f); + /* Make canonical to increase the utilization of the cache. */ + comple = Cudd_IsComplement(f); + f = Cudd_Regular(f); + + /* Check the cache. */ + if (st_lookup(cache, (char *)f, (char **)&r)) { + return(Cudd_NotCond(r,comple)); + } + + /* Retrieve the edge markings. */ + if (st_lookup(table, (char *)f, (char **)&markings) == 0) + return(NULL); + markT = markings >> 2; + markE = markings & 3; + + topf = dd->perm[f->index]; + index = f->index; + Fv = cuddT(f); Fnv = cuddE(f); + + if (markT == DD_LIC_NL) { + t = cuddBddLICBuildResult(dd,Fv,cache,table); + if (t == NULL) { + return(NULL); + } + } else if (markT == DD_LIC_1) { + t = one; + } else { + t = zero; + } + cuddRef(t); + if (markE == DD_LIC_NL) { + e = cuddBddLICBuildResult(dd,Fnv,cache,table); + if (e == NULL) { + Cudd_IterDerefBdd(dd,t); + return(NULL); + } + } else if (markE == DD_LIC_1) { + e = one; + } else { + e = zero; + } + cuddRef(e); + + if (markT == DD_LIC_DC && markE != DD_LIC_DC) { + r = e; + } else if (markT != DD_LIC_DC && markE == DD_LIC_DC) { + r = t; + } else { + if (Cudd_IsComplement(t)) { + t = Cudd_Not(t); + e = Cudd_Not(e); + r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); + if (r == NULL) { + Cudd_IterDerefBdd(dd, e); + Cudd_IterDerefBdd(dd, t); + return(NULL); + } + r = Cudd_Not(r); + } else { + r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); + if (r == NULL) { + Cudd_IterDerefBdd(dd, e); + Cudd_IterDerefBdd(dd, t); + return(NULL); + } + } + } + cuddDeref(t); + cuddDeref(e); + + if (st_insert(cache, (char *)f, (char *)r) == ST_OUT_OF_MEM) { + cuddRef(r); + Cudd_IterDerefBdd(dd,r); + return(NULL); + } + + return(Cudd_NotCond(r,comple)); + +} /* end of cuddBddLICBuildResult */ + + +/**Function******************************************************************** + + Synopsis [Hash function for the computed table of cuddBddLICMarkEdges.] + + Description [Hash function for the computed table of + cuddBddLICMarkEdges. Returns the bucket number.] + + SideEffects [None] + + SeeAlso [Cudd_bddLICompaction] + +******************************************************************************/ +static int +MarkCacheHash( + char * ptr, + int modulus) +{ + int val = 0; + MarkCacheKey *entry; + + entry = (MarkCacheKey *) ptr; + + val = (int) (ptrint) entry->f; + val = val * 997 + (int) (ptrint) entry->c; + + return ((val < 0) ? -val : val) % modulus; + +} /* end of MarkCacheHash */ + + +/**Function******************************************************************** + + Synopsis [Comparison function for the computed table of + cuddBddLICMarkEdges.] + + Description [Comparison function for the computed table of + cuddBddLICMarkEdges. Returns 0 if the two nodes of the key are equal; 1 + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddLICompaction] + +******************************************************************************/ +static int +MarkCacheCompare( + const char * ptr1, + const char * ptr2) +{ + MarkCacheKey *entry1, *entry2; + + entry1 = (MarkCacheKey *) ptr1; + entry2 = (MarkCacheKey *) ptr2; + + return((entry1->f != entry2->f) || (entry1->c != entry2->c)); + +} /* end of MarkCacheCompare */ + + + +/**Function******************************************************************** + + Synopsis [Frees memory associated with computed table of + cuddBddLICMarkEdges.] + + Description [Frees memory associated with computed table of + cuddBddLICMarkEdges. Returns ST_CONTINUE.] + + SideEffects [None] + + SeeAlso [Cudd_bddLICompaction] + +******************************************************************************/ +static enum st_retval +MarkCacheCleanUp( + char * key, + char * value, + char * arg) +{ + MarkCacheKey *entry; + + entry = (MarkCacheKey *) key; + FREE(entry); + return ST_CONTINUE; + +} /* end of MarkCacheCleanUp */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_bddSqueeze.] + + Description [Performs the recursive step of Cudd_bddSqueeze. This + procedure exploits the fact that if we complement and swap the + bounds of the interval we obtain a valid solution by taking the + complement of the solution to the original problem. Therefore, we + can enforce the condition that the upper bound is always regular. + Returns a pointer to the result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddSqueeze] + +******************************************************************************/ +static DdNode * +cuddBddSqueeze( + DdManager * dd, + DdNode * l, + DdNode * u) +{ + DdNode *one, *zero, *r, *lt, *le, *ut, *ue, *t, *e; +#if 0 + DdNode *ar; +#endif + int comple = 0; + unsigned int topu, topl; + int index; + + statLine(dd); + if (l == u) { + return(l); + } + one = DD_ONE(dd); + zero = Cudd_Not(one); + /* The only case when l == zero && u == one is at the top level, + ** where returning either one or zero is OK. In all other cases + ** the procedure will detect such a case and will perform + ** remapping. Therefore the order in which we test l and u at this + ** point is immaterial. */ + if (l == zero) return(l); + if (u == one) return(u); + + /* Make canonical to increase the utilization of the cache. */ + if (Cudd_IsComplement(u)) { + DdNode *temp; + temp = Cudd_Not(l); + l = Cudd_Not(u); + u = temp; + comple = 1; + } + /* At this point u is regular and non-constant; l is non-constant, but + ** may be complemented. */ + + /* Here we could check the relative sizes. */ + + /* Check the cache. */ + r = cuddCacheLookup2(dd, Cudd_bddSqueeze, l, u); + if (r != NULL) { + return(Cudd_NotCond(r,comple)); + } + + /* Recursive step. */ + topu = dd->perm[u->index]; + topl = dd->perm[Cudd_Regular(l)->index]; + if (topu <= topl) { + index = u->index; + ut = cuddT(u); ue = cuddE(u); + } else { + index = Cudd_Regular(l)->index; + ut = ue = u; + } + if (topl <= topu) { + lt = cuddT(Cudd_Regular(l)); le = cuddE(Cudd_Regular(l)); + if (Cudd_IsComplement(l)) { + lt = Cudd_Not(lt); + le = Cudd_Not(le); + } + } else { + lt = le = l; + } + + /* If one interval is contained in the other, use the smaller + ** interval. This corresponds to one-sided matching. */ + if ((lt == zero || Cudd_bddLeq(dd,lt,le)) && + (ut == one || Cudd_bddLeq(dd,ue,ut))) { /* remap */ + r = cuddBddSqueeze(dd, le, ue); + if (r == NULL) + return(NULL); + return(Cudd_NotCond(r,comple)); + } else if ((le == zero || Cudd_bddLeq(dd,le,lt)) && + (ue == one || Cudd_bddLeq(dd,ut,ue))) { /* remap */ + r = cuddBddSqueeze(dd, lt, ut); + if (r == NULL) + return(NULL); + return(Cudd_NotCond(r,comple)); + } else if ((le == zero || Cudd_bddLeq(dd,le,Cudd_Not(ut))) && + (ue == one || Cudd_bddLeq(dd,Cudd_Not(lt),ue))) { /* c-remap */ + t = cuddBddSqueeze(dd, lt, ut); + cuddRef(t); + if (Cudd_IsComplement(t)) { + r = cuddUniqueInter(dd, index, Cudd_Not(t), t); + if (r == NULL) { + Cudd_IterDerefBdd(dd, t); + return(NULL); + } + r = Cudd_Not(r); + } else { + r = cuddUniqueInter(dd, index, t, Cudd_Not(t)); + if (r == NULL) { + Cudd_IterDerefBdd(dd, t); + return(NULL); + } + } + cuddDeref(t); + if (r == NULL) + return(NULL); + cuddCacheInsert2(dd, Cudd_bddSqueeze, l, u, r); + return(Cudd_NotCond(r,comple)); + } else if ((lt == zero || Cudd_bddLeq(dd,lt,Cudd_Not(ue))) && + (ut == one || Cudd_bddLeq(dd,Cudd_Not(le),ut))) { /* c-remap */ + e = cuddBddSqueeze(dd, le, ue); + cuddRef(e); + if (Cudd_IsComplement(e)) { + r = cuddUniqueInter(dd, index, Cudd_Not(e), e); + if (r == NULL) { + Cudd_IterDerefBdd(dd, e); + return(NULL); + } + } else { + r = cuddUniqueInter(dd, index, e, Cudd_Not(e)); + if (r == NULL) { + Cudd_IterDerefBdd(dd, e); + return(NULL); + } + r = Cudd_Not(r); + } + cuddDeref(e); + if (r == NULL) + return(NULL); + cuddCacheInsert2(dd, Cudd_bddSqueeze, l, u, r); + return(Cudd_NotCond(r,comple)); + } + +#if 0 + /* If the two intervals intersect, take a solution from + ** the intersection of the intervals. This guarantees that the + ** splitting variable will not appear in the result. + ** This approach corresponds to two-sided matching, and is very + ** expensive. */ + if (Cudd_bddLeq(dd,lt,ue) && Cudd_bddLeq(dd,le,ut)) { + DdNode *au, *al; + au = cuddBddAndRecur(dd,ut,ue); + if (au == NULL) + return(NULL); + cuddRef(au); + al = cuddBddAndRecur(dd,Cudd_Not(lt),Cudd_Not(le)); + if (al == NULL) { + Cudd_IterDerefBdd(dd,au); + return(NULL); + } + cuddRef(al); + al = Cudd_Not(al); + ar = cuddBddSqueeze(dd, al, au); + if (ar == NULL) { + Cudd_IterDerefBdd(dd,au); + Cudd_IterDerefBdd(dd,al); + return(NULL); + } + cuddRef(ar); + Cudd_IterDerefBdd(dd,au); + Cudd_IterDerefBdd(dd,al); + } else { + ar = NULL; + } +#endif + + t = cuddBddSqueeze(dd, lt, ut); + if (t == NULL) { + return(NULL); + } + cuddRef(t); + e = cuddBddSqueeze(dd, le, ue); + if (e == NULL) { + Cudd_IterDerefBdd(dd,t); + return(NULL); + } + cuddRef(e); + + if (Cudd_IsComplement(t)) { + t = Cudd_Not(t); + e = Cudd_Not(e); + r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); + if (r == NULL) { + Cudd_IterDerefBdd(dd, e); + Cudd_IterDerefBdd(dd, t); + return(NULL); + } + r = Cudd_Not(r); + } else { + r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); + if (r == NULL) { + Cudd_IterDerefBdd(dd, e); + Cudd_IterDerefBdd(dd, t); + return(NULL); + } + } + cuddDeref(t); + cuddDeref(e); + +#if 0 + /* Check whether there is a result obtained by abstraction and whether + ** it is better than the one obtained by recursion. */ + cuddRef(r); + if (ar != NULL) { + if (Cudd_DagSize(ar) <= Cudd_DagSize(r)) { + Cudd_IterDerefBdd(dd, r); + r = ar; + } else { + Cudd_IterDerefBdd(dd, ar); + } + } + cuddDeref(r); +#endif + + cuddCacheInsert2(dd, Cudd_bddSqueeze, l, u, r); + return(Cudd_NotCond(r,comple)); + +} /* end of cuddBddSqueeze */ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddGenetic.c b/abc_with_bb_support/src/bdd/cudd/cuddGenetic.c new file mode 100644 index 000000000..ad32ac8b0 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddGenetic.c @@ -0,0 +1,921 @@ +/**CFile*********************************************************************** + + FileName [cuddGenetic.c] + + PackageName [cudd] + + Synopsis [Genetic algorithm for variable reordering.] + + Description [Internal procedures included in this file: +
        +
      • cuddGa() +
      + Static procedures included in this module: +
        +
      • make_random() +
      • sift_up() +
      • build_dd() +
      • largest() +
      • rand_int() +
      • array_hash() +
      • array_compare() +
      • find_best() +
      • find_average_fitness() +
      • PMX() +
      • roulette() +
      + + The genetic algorithm implemented here is as follows. We start with + the current DD order. We sift this order and use this as the + reference DD. We only keep 1 DD around for the entire process and + simply rearrange the order of this DD, storing the various orders + and their corresponding DD sizes. We generate more random orders to + build an initial population. This initial population is 3 times the + number of variables, with a maximum of 120. Each random order is + built (from the reference DD) and its size stored. Each random + order is also sifted to keep the DD sizes fairly small. Then a + crossover is performed between two orders (picked randomly) and the + two resulting DDs are built and sifted. For each new order, if its + size is smaller than any DD in the population, it is inserted into + the population and the DD with the largest number of nodes is thrown + out. The crossover process happens up to 50 times, and at this point + the DD in the population with the smallest size is chosen as the + result. This DD must then be built from the reference DD.] + + SeeAlso [] + + Author [Curt Musfeldt, Alan Shuler, Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddGenetic.c,v 1.1.1.1 2003/02/24 22:23:52 wjiang Exp $"; +#endif + +static int popsize; /* the size of the population */ +static int numvars; /* the number of input variables in the ckt. */ +/* storedd stores the population orders and sizes. This table has two +** extra rows and one extras column. The two extra rows are used for the +** offspring produced by a crossover. Each row stores one order and its +** size. The order is stored by storing the indices of variables in the +** order in which they appear in the order. The table is in reality a +** one-dimensional array which is accessed via a macro to give the illusion +** it is a two-dimensional structure. +*/ +static int *storedd; +static st_table *computed; /* hash table to identify existing orders */ +static int *repeat; /* how many times an order is present */ +static int large; /* stores the index of the population with + ** the largest number of nodes in the DD */ +static int result; +static int cross; /* the number of crossovers to perform */ + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/* macro used to access the population table as if it were a +** two-dimensional structure. +*/ +#define STOREDD(i,j) storedd[(i)*(numvars+1)+(j)] + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int make_random ARGS((DdManager *table, int lower)); +static int sift_up ARGS((DdManager *table, int x, int x_low)); +static int build_dd ARGS((DdManager *table, int num, int lower, int upper)); +static int largest ARGS(()); +static int rand_int ARGS((int a)); +static int array_hash ARGS((char *array, int modulus)); +static int array_compare ARGS((const char *array1, const char *array2)); +static int find_best ARGS(()); +static double find_average_fitness ARGS(()); +static int PMX ARGS((int maxvar)); +static int roulette ARGS((int *p1, int *p2)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Genetic algorithm for DD reordering.] + + Description [Genetic algorithm for DD reordering. + The two children of a crossover will be stored in + storedd[popsize] and storedd[popsize+1] --- the last two slots in the + storedd array. (This will make comparisons and replacement easy.) + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddGa( + DdManager * table /* manager */, + int lower /* lowest level to be reordered */, + int upper /* highest level to be reorderded */) +{ + int i,n,m; /* dummy/loop vars */ + int index; + double average_fitness; + int small; /* index of smallest DD in population */ + + /* Do an initial sifting to produce at least one reasonable individual. */ + if (!cuddSifting(table,lower,upper)) return(0); + + /* Get the initial values. */ + numvars = upper - lower + 1; /* number of variables to be reordered */ + if (table->populationSize == 0) { + popsize = 3 * numvars; /* population size is 3 times # of vars */ + if (popsize > 120) { + popsize = 120; /* Maximum population size is 120 */ + } + } else { + popsize = table->populationSize; /* user specified value */ + } + if (popsize < 4) popsize = 4; /* enforce minimum population size */ + + /* Allocate population table. */ + storedd = ALLOC(int,(popsize+2)*(numvars+1)); + if (storedd == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } + + /* Initialize the computed table. This table is made up of two data + ** structures: A hash table with the key given by the order, which says + ** if a given order is present in the population; and the repeat + ** vector, which says how many copies of a given order are stored in + ** the population table. If there are multiple copies of an order, only + ** one has a repeat count greater than 1. This copy is the one pointed + ** by the computed table. + */ + repeat = ALLOC(int,popsize); + if (repeat == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + FREE(storedd); + return(0); + } + for (i = 0; i < popsize; i++) { + repeat[i] = 0; + } + computed = st_init_table(array_compare,array_hash); + if (computed == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + FREE(storedd); + FREE(repeat); + return(0); + } + + /* Copy the current DD and its size to the population table. */ + for (i = 0; i < numvars; i++) { + STOREDD(0,i) = table->invperm[i+lower]; /* order of initial DD */ + } + STOREDD(0,numvars) = table->keys - table->isolated; /* size of initial DD */ + + /* Store the initial order in the computed table. */ + if (st_insert(computed,(char *)storedd,(char *) 0) == ST_OUT_OF_MEM) { + FREE(storedd); + FREE(repeat); + st_free_table(computed); + return(0); + } + repeat[0]++; + + /* Insert the reverse order as second element of the population. */ + for (i = 0; i < numvars; i++) { + STOREDD(1,numvars-1-i) = table->invperm[i+lower]; /* reverse order */ + } + + /* Now create the random orders. make_random fills the population + ** table with random permutations. The successive loop builds and sifts + ** the DDs for the reverse order and each random permutation, and stores + ** the results in the computed table. + */ + if (!make_random(table,lower)) { + table->errorCode = CUDD_MEMORY_OUT; + FREE(storedd); + FREE(repeat); + st_free_table(computed); + return(0); + } + for (i = 1; i < popsize; i++) { + result = build_dd(table,i,lower,upper); /* build and sift order */ + if (!result) { + FREE(storedd); + FREE(repeat); + st_free_table(computed); + return(0); + } + if (st_lookup(computed,(char *)&STOREDD(i,0),(char **)&index)) { + repeat[index]++; + } else { + if (st_insert(computed,(char *)&STOREDD(i,0),(char *)(long)i) == + ST_OUT_OF_MEM) { + FREE(storedd); + FREE(repeat); + st_free_table(computed); + return(0); + } + repeat[i]++; + } + } + +#if 0 +#ifdef DD_STATS + /* Print the initial population. */ + (void) fprintf(table->out,"Initial population after sifting\n"); + for (m = 0; m < popsize; m++) { + for (i = 0; i < numvars; i++) { + (void) fprintf(table->out," %2d",STOREDD(m,i)); + } + (void) fprintf(table->out," : %3d (%d)\n", + STOREDD(m,numvars),repeat[m]); + } +#endif +#endif + + small = find_best(); + average_fitness = find_average_fitness(); +#ifdef DD_STATS + (void) fprintf(table->out,"\nInitial population: best fitness = %d, average fitness %8.3f",STOREDD(small,numvars),average_fitness); +#endif + + /* Decide how many crossovers should be tried. */ + if (table->numberXovers == 0) { + cross = 3*numvars; + if (cross > 60) { /* do a maximum of 50 crossovers */ + cross = 60; + } + } else { + cross = table->numberXovers; /* use user specified value */ + } + + /* Perform the crossovers to get the best order. */ + for (m = 0; m < cross; m++) { + if (!PMX(table->size)) { /* perform one crossover */ + table->errorCode = CUDD_MEMORY_OUT; + FREE(storedd); + FREE(repeat); + st_free_table(computed); + return(0); + } + /* The offsprings are left in the last two entries of the + ** population table. These are now considered in turn. + */ + for (i = popsize; i <= popsize+1; i++) { + result = build_dd(table,i,lower,upper); /* build and sift child */ + if (!result) { + FREE(storedd); + FREE(repeat); + st_free_table(computed); + return(0); + } + large = largest(); /* find the largest DD in population */ + + /* If the new child is smaller than the largest DD in the current + ** population, enter it into the population in place of the + ** largest DD. + */ + if (STOREDD(i,numvars) < STOREDD(large,numvars)) { + /* Look up the largest DD in the computed table. + ** Decrease its repetition count. If the repetition count + ** goes to 0, remove the largest DD from the computed table. + */ + result = st_lookup(computed,(char *)&STOREDD(large,0),(char + **)&index); + if (!result) { + FREE(storedd); + FREE(repeat); + st_free_table(computed); + return(0); + } + repeat[index]--; + if (repeat[index] == 0) { + int *pointer = &STOREDD(index,0); + result = st_delete(computed, (char **)&pointer,NULL); + if (!result) { + FREE(storedd); + FREE(repeat); + st_free_table(computed); + return(0); + } + } + /* Copy the new individual to the entry of the + ** population table just made available and update the + ** computed table. + */ + for (n = 0; n <= numvars; n++) { + STOREDD(large,n) = STOREDD(i,n); + } + if (st_lookup(computed,(char *)&STOREDD(large,0),(char + **)&index)) { + repeat[index]++; + } else { + if (st_insert(computed,(char *)&STOREDD(large,0), + (char *)(long)large) == ST_OUT_OF_MEM) { + FREE(storedd); + FREE(repeat); + st_free_table(computed); + return(0); + } + repeat[large]++; + } + } + } + } + + /* Find the smallest DD in the population and build it; + ** that will be the result. + */ + small = find_best(); + + /* Print stats on the final population. */ +#ifdef DD_STATS + average_fitness = find_average_fitness(); + (void) fprintf(table->out,"\nFinal population: best fitness = %d, average fitness %8.3f",STOREDD(small,numvars),average_fitness); +#endif + + /* Clean up, build the result DD, and return. */ + st_free_table(computed); + computed = NULL; + result = build_dd(table,small,lower,upper); + FREE(storedd); + FREE(repeat); + return(result); + +} /* end of cuddGa */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Generates the random sequences for the initial population.] + + Description [Generates the random sequences for the initial population. + The sequences are permutations of the indices between lower and + upper in the current order.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +make_random( + DdManager * table, + int lower) +{ + int i,j; /* loop variables */ + int *used; /* is a number already in a permutation */ + int next; /* next random number without repetitions */ + + used = ALLOC(int,numvars); + if (used == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } +#if 0 +#ifdef DD_STATS + (void) fprintf(table->out,"Initial population before sifting\n"); + for (i = 0; i < 2; i++) { + for (j = 0; j < numvars; j++) { + (void) fprintf(table->out," %2d",STOREDD(i,j)); + } + (void) fprintf(table->out,"\n"); + } +#endif +#endif + for (i = 2; i < popsize; i++) { + for (j = 0; j < numvars; j++) { + used[j] = 0; + } + /* Generate a permutation of {0...numvars-1} and use it to + ** permute the variables in the layesr from lower to upper. + */ + for (j = 0; j < numvars; j++) { + do { + next = rand_int(numvars-1); + } while (used[next] != 0); + used[next] = 1; + STOREDD(i,j) = table->invperm[next+lower]; + } +#if 0 +#ifdef DD_STATS + /* Print the order just generated. */ + for (j = 0; j < numvars; j++) { + (void) fprintf(table->out," %2d",STOREDD(i,j)); + } + (void) fprintf(table->out,"\n"); +#endif +#endif + } + FREE(used); + return(1); + +} /* end of make_random */ + + +/**Function******************************************************************** + + Synopsis [Moves one variable up.] + + Description [Takes a variable from position x and sifts it up to + position x_low; x_low should be less than x. Returns 1 if successful; + 0 otherwise] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +sift_up( + DdManager * table, + int x, + int x_low) +{ + int y; + int size; + + y = cuddNextLow(table,x); + while (y >= x_low) { + size = cuddSwapInPlace(table,y,x); + if (size == 0) { + return(0); + } + x = y; + y = cuddNextLow(table,x); + } + return(1); + +} /* end of sift_up */ + + +/**Function******************************************************************** + + Synopsis [Builds a DD from a given order.] + + Description [Builds a DD from a given order. This procedure also + sifts the final order and inserts into the array the size in nodes + of the result. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +build_dd( + DdManager * table, + int num /* the index of the individual to be built */, + int lower, + int upper) +{ + int i,j; /* loop vars */ + int position; + int index; + int limit; /* how large the DD for this order can grow */ + int size; + + /* Check the computed table. If the order already exists, it + ** suffices to copy the size from the existing entry. + */ + if (computed && st_lookup(computed,(char *)&STOREDD(num,0),(char **)&index)) { + STOREDD(num,numvars) = STOREDD(index,numvars); +#ifdef DD_STATS + (void) fprintf(table->out,"\nCache hit for index %d", index); +#endif + return(1); + } + + /* Stop if the DD grows 20 times larges than the reference size. */ + limit = 20 * STOREDD(0,numvars); + + /* Sift up the variables so as to build the desired permutation. + ** First the variable that has to be on top is sifted to the top. + ** Then the variable that has to occupy the secon position is sifted + ** up to the second position, and so on. + */ + for (j = 0; j < numvars; j++) { + i = STOREDD(num,j); + position = table->perm[i]; + result = sift_up(table,position,j+lower); + if (!result) return(0); + size = table->keys - table->isolated; + if (size > limit) break; + } + + /* Sift the DD just built. */ +#ifdef DD_STATS + (void) fprintf(table->out,"\n"); +#endif + result = cuddSifting(table,lower,upper); + if (!result) return(0); + + /* Copy order and size to table. */ + for (j = 0; j < numvars; j++) { + STOREDD(num,j) = table->invperm[lower+j]; + } + STOREDD(num,numvars) = table->keys - table->isolated; /* size of new DD */ + return(1); + +} /* end of build_dd */ + + +/**Function******************************************************************** + + Synopsis [Finds the largest DD in the population.] + + Description [Finds the largest DD in the population. If an order is + repeated, it avoids choosing the copy that is in the computed table + (it has repeat[i] > 1).] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +largest( + ) +{ + int i; /* loop var */ + int big; /* temporary holder to return result */ + + big = 0; + while (repeat[big] > 1) big++; + for (i = big + 1; i < popsize; i++) { + if (STOREDD(i,numvars) >= STOREDD(big,numvars) && repeat[i] <= 1) { + big = i; + } + } + return(big); + +} /* end of largest */ + + +/**Function******************************************************************** + + Synopsis [Generates a random number between 0 and the integer a.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +rand_int( + int a) +{ + return(Cudd_Random() % (a+1)); + +} /* end of rand_int */ + + +/**Function******************************************************************** + + Synopsis [Hash function for the computed table.] + + Description [Hash function for the computed table. Returns the bucket + number.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +array_hash( + char * array, + int modulus) +{ + int val = 0; + int i; + int *intarray; + + intarray = (int *) array; + + for (i = 0; i < numvars; i++) { + val = val * 997 + intarray[i]; + } + + return ((val < 0) ? -val : val) % modulus; + +} /* end of array_hash */ + + +/**Function******************************************************************** + + Synopsis [Comparison function for the computed table.] + + Description [Comparison function for the computed table. Returns 0 if + the two arrays are equal; 1 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +array_compare( + const char * array1, + const char * array2) +{ + int i; + int *intarray1, *intarray2; + + intarray1 = (int *) array1; + intarray2 = (int *) array2; + + for (i = 0; i < numvars; i++) { + if (intarray1[i] != intarray2[i]) return(1); + } + return(0); + +} /* end of array_compare */ + + +/**Function******************************************************************** + + Synopsis [Returns the index of the fittest individual.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +find_best( + ) +{ + int i,small; + + small = 0; + for (i = 1; i < popsize; i++) { + if (STOREDD(i,numvars) < STOREDD(small,numvars)) { + small = i; + } + } + return(small); + +} /* end of find_best */ + + +/**Function******************************************************************** + + Synopsis [Returns the average fitness of the population.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static double +find_average_fitness( + ) +{ + int i; + int total_fitness = 0; + double average_fitness; + + for (i = 0; i < popsize; i++) { + total_fitness += STOREDD(i,numvars); + } + average_fitness = (double) total_fitness / (double) popsize; + return(average_fitness); + +} /* end of find_average_fitness */ + + +/**Function******************************************************************** + + Synopsis [Performs the crossover between two parents.] + + Description [Performs the crossover between two randomly chosen + parents, and creates two children, x1 and x2. Uses the Partially + Matched Crossover operator.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +PMX( + int maxvar) +{ + int cut1,cut2; /* the two cut positions (random) */ + int mom,dad; /* the two randomly chosen parents */ + int *inv1; /* inverse permutations for repair algo */ + int *inv2; + int i; /* loop vars */ + int u,v; /* aux vars */ + + inv1 = ALLOC(int,maxvar); + if (inv1 == NULL) { + return(0); + } + inv2 = ALLOC(int,maxvar); + if (inv2 == NULL) { + FREE(inv1); + return(0); + } + + /* Choose two orders from the population using roulette wheel. */ + if (!roulette(&mom,&dad)) { + FREE(inv1); + FREE(inv2); + return(0); + } + + /* Choose two random cut positions. A cut in position i means that + ** the cut immediately precedes position i. If cut1 < cut2, we + ** exchange the middle of the two orderings; otherwise, we + ** exchange the beginnings and the ends. + */ + cut1 = rand_int(numvars-1); + do { + cut2 = rand_int(numvars-1); + } while (cut1 == cut2); + +#if 0 + /* Print out the parents. */ + (void) fprintf(table->out, + "Crossover of %d (mom) and %d (dad) between %d and %d\n", + mom,dad,cut1,cut2); + for (i = 0; i < numvars; i++) { + if (i == cut1 || i == cut2) (void) fprintf(table->out,"|"); + (void) fprintf(table->out,"%2d ",STOREDD(mom,i)); + } + (void) fprintf(table->out,"\n"); + for (i = 0; i < numvars; i++) { + if (i == cut1 || i == cut2) (void) fprintf(table->out,"|"); + (void) fprintf(table->out,"%2d ",STOREDD(dad,i)); + } + (void) fprintf(table->out,"\n"); +#endif + + /* Initialize the inverse permutations: -1 means yet undetermined. */ + for (i = 0; i < maxvar; i++) { + inv1[i] = -1; + inv2[i] = -1; + } + + /* Copy the portions whithin the cuts. */ + for (i = cut1; i != cut2; i = (i == numvars-1) ? 0 : i+1) { + STOREDD(popsize,i) = STOREDD(dad,i); + inv1[STOREDD(popsize,i)] = i; + STOREDD(popsize+1,i) = STOREDD(mom,i); + inv2[STOREDD(popsize+1,i)] = i; + } + + /* Now apply the repair algorithm outside the cuts. */ + for (i = cut2; i != cut1; i = (i == numvars-1 ) ? 0 : i+1) { + v = i; + do { + u = STOREDD(mom,v); + v = inv1[u]; + } while (v != -1); + STOREDD(popsize,i) = u; + inv1[u] = i; + v = i; + do { + u = STOREDD(dad,v); + v = inv2[u]; + } while (v != -1); + STOREDD(popsize+1,i) = u; + inv2[u] = i; + } + +#if 0 + /* Print the results of crossover. */ + for (i = 0; i < numvars; i++) { + if (i == cut1 || i == cut2) (void) fprintf(table->out,"|"); + (void) fprintf(table->out,"%2d ",STOREDD(popsize,i)); + } + (void) fprintf(table->out,"\n"); + for (i = 0; i < numvars; i++) { + if (i == cut1 || i == cut2) (void) fprintf(table->out,"|"); + (void) fprintf(table->out,"%2d ",STOREDD(popsize+1,i)); + } + (void) fprintf(table->out,"\n"); +#endif + + FREE(inv1); + FREE(inv2); + return(1); + +} /* end of PMX */ + + +/**Function******************************************************************** + + Synopsis [Selects two parents with the roulette wheel method.] + + Description [Selects two distinct parents with the roulette wheel method.] + + SideEffects [The indices of the selected parents are returned as side + effects.] + + SeeAlso [] + +******************************************************************************/ +static int +roulette( + int * p1, + int * p2) +{ + double *wheel; + double spin; + int i; + + wheel = ALLOC(double,popsize); + if (wheel == NULL) { + return(0); + } + + /* The fitness of an individual is the reciprocal of its size. */ + wheel[0] = 1.0 / (double) STOREDD(0,numvars); + + for (i = 1; i < popsize; i++) { + wheel[i] = wheel[i-1] + 1.0 / (double) STOREDD(i,numvars); + } + + /* Get a random number between 0 and wheel[popsize-1] (that is, + ** the sum of all fitness values. 2147483561 is the largest number + ** returned by Cudd_Random. + */ + spin = wheel[numvars-1] * (double) Cudd_Random() / 2147483561.0; + + /* Find the lucky element by scanning the wheel. */ + for (i = 0; i < popsize; i++) { + if (spin <= wheel[i]) break; + } + *p1 = i; + + /* Repeat the process for the second parent, making sure it is + ** distinct from the first. + */ + do { + spin = wheel[popsize-1] * (double) Cudd_Random() / 2147483561.0; + for (i = 0; i < popsize; i++) { + if (spin <= wheel[i]) break; + } + } while (i == *p1); + *p2 = i; + + FREE(wheel); + return(1); + +} /* end of roulette */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddGroup.c b/abc_with_bb_support/src/bdd/cudd/cuddGroup.c new file mode 100644 index 000000000..dd8e3a853 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddGroup.c @@ -0,0 +1,2142 @@ +/**CFile*********************************************************************** + + FileName [cuddGroup.c] + + PackageName [cudd] + + Synopsis [Functions for group sifting.] + + Description [External procedures included in this file: +
        +
      • Cudd_MakeTreeNode() +
      + Internal procedures included in this file: +
        +
      • cuddTreeSifting() +
      + Static procedures included in this module: +
        +
      • ddTreeSiftingAux() +
      • ddCountInternalMtrNodes() +
      • ddReorderChildren() +
      • ddFindNodeHiLo() +
      • ddUniqueCompareGroup() +
      • ddGroupSifting() +
      • ddCreateGroup() +
      • ddGroupSiftingAux() +
      • ddGroupSiftingUp() +
      • ddGroupSiftingDown() +
      • ddGroupMove() +
      • ddGroupMoveBackward() +
      • ddGroupSiftingBackward() +
      • ddMergeGroups() +
      • ddDissolveGroup() +
      • ddNoCheck() +
      • ddSecDiffCheck() +
      • ddExtSymmCheck() +
      • ddVarGroupCheck() +
      • ddSetVarHandled() +
      • ddResetVarHandled() +
      • ddIsVarHandled() +
      ] + + Author [Shipra Panda, Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/* Constants for lazy sifting */ +#define DD_NORMAL_SIFT 0 +#define DD_LAZY_SIFT 1 + +/* Constants for sifting up and down */ +#define DD_SIFT_DOWN 0 +#define DD_SIFT_UP 1 + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddGroup.c,v 1.1.1.1 2003/02/24 22:23:52 wjiang Exp $"; +#endif + +static int *entry; +extern int ddTotalNumberSwapping; +#ifdef DD_STATS +extern int ddTotalNISwaps; +static int extsymmcalls; +static int extsymm; +static int secdiffcalls; +static int secdiff; +static int secdiffmisfire; +#endif +#ifdef DD_DEBUG +static int pr = 0; /* flag to enable printing while debugging */ + /* by depositing a 1 into it */ +#endif +static int originalSize; +static int originalLevel; + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int ddTreeSiftingAux ARGS((DdManager *table, MtrNode *treenode, Cudd_ReorderingType method)); +#ifdef DD_STATS +static int ddCountInternalMtrNodes ARGS((DdManager *table, MtrNode *treenode)); +#endif +static int ddReorderChildren ARGS((DdManager *table, MtrNode *treenode, Cudd_ReorderingType method)); +static void ddFindNodeHiLo ARGS((DdManager *table, MtrNode *treenode, int *lower, int *upper)); +static int ddUniqueCompareGroup ARGS((int *ptrX, int *ptrY)); +static int ddGroupSifting ARGS((DdManager *table, int lower, int upper, int (*checkFunction)(DdManager *, int, int), int lazyFlag)); +static void ddCreateGroup ARGS((DdManager *table, int x, int y)); +static int ddGroupSiftingAux ARGS((DdManager *table, int x, int xLow, int xHigh, int (*checkFunction)(DdManager *, int, int), int lazyFlag)); +static int ddGroupSiftingUp ARGS((DdManager *table, int y, int xLow, int (*checkFunction)(DdManager *, int, int), Move **moves)); +static int ddGroupSiftingDown ARGS((DdManager *table, int x, int xHigh, int (*checkFunction)(DdManager *, int, int), Move **moves)); +static int ddGroupMove ARGS((DdManager *table, int x, int y, Move **moves)); +static int ddGroupMoveBackward ARGS((DdManager *table, int x, int y)); +static int ddGroupSiftingBackward ARGS((DdManager *table, Move *moves, int size, int upFlag, int lazyFlag)); +static void ddMergeGroups ARGS((DdManager *table, MtrNode *treenode, int low, int high)); +static void ddDissolveGroup ARGS((DdManager *table, int x, int y)); +static int ddNoCheck ARGS((DdManager *table, int x, int y)); +static int ddSecDiffCheck ARGS((DdManager *table, int x, int y)); +static int ddExtSymmCheck ARGS((DdManager *table, int x, int y)); +static int ddVarGroupCheck ARGS((DdManager * table, int x, int y)); +static int ddSetVarHandled ARGS((DdManager *dd, int index)); +static int ddResetVarHandled ARGS((DdManager *dd, int index)); +static int ddIsVarHandled ARGS((DdManager *dd, int index)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Creates a new variable group.] + + Description [Creates a new variable group. The group starts at + variable and contains size variables. The parameter low is the index + of the first variable. If the variable already exists, its current + position in the order is known to the manager. If the variable does + not exist yet, the position is assumed to be the same as the index. + The group tree is created if it does not exist yet. + Returns a pointer to the group if successful; NULL otherwise.] + + SideEffects [The variable tree is changed.] + + SeeAlso [Cudd_MakeZddTreeNode] + +******************************************************************************/ +MtrNode * +Cudd_MakeTreeNode( + DdManager * dd /* manager */, + unsigned int low /* index of the first group variable */, + unsigned int size /* number of variables in the group */, + unsigned int type /* MTR_DEFAULT or MTR_FIXED */) +{ + MtrNode *group; + MtrNode *tree; + unsigned int level; + + /* If the variable does not exist yet, the position is assumed to be + ** the same as the index. Therefore, applications that rely on + ** Cudd_bddNewVarAtLevel or Cudd_addNewVarAtLevel to create new + ** variables have to create the variables before they group them. + */ + level = (low < (unsigned int) dd->size) ? dd->perm[low] : low; + + if (level + size - 1> (int) MTR_MAXHIGH) + return(NULL); + + /* If the tree does not exist yet, create it. */ + tree = dd->tree; + if (tree == NULL) { + dd->tree = tree = Mtr_InitGroupTree(0, dd->size); + if (tree == NULL) + return(NULL); + tree->index = dd->invperm[0]; + } + + /* Extend the upper bound of the tree if necessary. This allows the + ** application to create groups even before the variables are created. + */ + tree->size = ddMax(tree->size, ddMax(level + size, (unsigned) dd->size)); + + /* Create the group. */ + group = Mtr_MakeGroup(tree, level, size, type); + if (group == NULL) + return(NULL); + + /* Initialize the index field to the index of the variable currently + ** in position low. This field will be updated by the reordering + ** procedure to provide a handle to the group once it has been moved. + */ + group->index = (MtrHalfWord) low; + + return(group); + +} /* end of Cudd_MakeTreeNode */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Tree sifting algorithm.] + + Description [Tree sifting algorithm. Assumes that a tree representing + a group hierarchy is passed as a parameter. It then reorders each + group in postorder fashion by calling ddTreeSiftingAux. Assumes that + no dead nodes are present. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +int +cuddTreeSifting( + DdManager * table /* DD table */, + Cudd_ReorderingType method /* reordering method for the groups of leaves */) +{ + int i; + int nvars; + int result; + int tempTree; + + /* If no tree is provided we create a temporary one in which all + ** variables are in a single group. After reordering this tree is + ** destroyed. + */ + tempTree = table->tree == NULL; + if (tempTree) { + table->tree = Mtr_InitGroupTree(0,table->size); + table->tree->index = table->invperm[0]; + } + nvars = table->size; + +#ifdef DD_DEBUG + if (pr > 0 && !tempTree) (void) fprintf(table->out,"cuddTreeSifting:"); + Mtr_PrintGroups(table->tree,pr <= 0); +#endif + +#ifdef DD_STATS + extsymmcalls = 0; + extsymm = 0; + secdiffcalls = 0; + secdiff = 0; + secdiffmisfire = 0; + + (void) fprintf(table->out,"\n"); + if (!tempTree) + (void) fprintf(table->out,"#:IM_NODES %8d: group tree nodes\n", + ddCountInternalMtrNodes(table,table->tree)); +#endif + + /* Initialize the group of each subtable to itself. Initially + ** there are no groups. Groups are created according to the tree + ** structure in postorder fashion. + */ + for (i = 0; i < nvars; i++) + table->subtables[i].next = i; + + + /* Reorder. */ + result = ddTreeSiftingAux(table, table->tree, method); + +#ifdef DD_STATS /* print stats */ + if (!tempTree && method == CUDD_REORDER_GROUP_SIFT && + (table->groupcheck == CUDD_GROUP_CHECK7 || + table->groupcheck == CUDD_GROUP_CHECK5)) { + (void) fprintf(table->out,"\nextsymmcalls = %d\n",extsymmcalls); + (void) fprintf(table->out,"extsymm = %d",extsymm); + } + if (!tempTree && method == CUDD_REORDER_GROUP_SIFT && + table->groupcheck == CUDD_GROUP_CHECK7) { + (void) fprintf(table->out,"\nsecdiffcalls = %d\n",secdiffcalls); + (void) fprintf(table->out,"secdiff = %d\n",secdiff); + (void) fprintf(table->out,"secdiffmisfire = %d",secdiffmisfire); + } +#endif + + if (tempTree) + Cudd_FreeTree(table); + return(result); + +} /* end of cuddTreeSifting */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Visits the group tree and reorders each group.] + + Description [Recursively visits the group tree and reorders each + group in postorder fashion. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddTreeSiftingAux( + DdManager * table, + MtrNode * treenode, + Cudd_ReorderingType method) +{ + MtrNode *auxnode; + int res; + Cudd_AggregationType saveCheck; + +#ifdef DD_DEBUG + Mtr_PrintGroups(treenode,1); +#endif + + auxnode = treenode; + while (auxnode != NULL) { + if (auxnode->child != NULL) { + if (!ddTreeSiftingAux(table, auxnode->child, method)) + return(0); + saveCheck = table->groupcheck; + table->groupcheck = CUDD_NO_CHECK; + if (method != CUDD_REORDER_LAZY_SIFT) + res = ddReorderChildren(table, auxnode, CUDD_REORDER_GROUP_SIFT); + else + res = ddReorderChildren(table, auxnode, CUDD_REORDER_LAZY_SIFT); + table->groupcheck = saveCheck; + + if (res == 0) + return(0); + } else if (auxnode->size > 1) { + if (!ddReorderChildren(table, auxnode, method)) + return(0); + } + auxnode = auxnode->younger; + } + + return(1); + +} /* end of ddTreeSiftingAux */ + + +#ifdef DD_STATS +/**Function******************************************************************** + + Synopsis [Counts the number of internal nodes of the group tree.] + + Description [Counts the number of internal nodes of the group tree. + Returns the count.] + + SideEffects [None] + +******************************************************************************/ +static int +ddCountInternalMtrNodes( + DdManager * table, + MtrNode * treenode) +{ + MtrNode *auxnode; + int count,nodeCount; + + + nodeCount = 0; + auxnode = treenode; + while (auxnode != NULL) { + if (!(MTR_TEST(auxnode,MTR_TERMINAL))) { + nodeCount++; + count = ddCountInternalMtrNodes(table,auxnode->child); + nodeCount += count; + } + auxnode = auxnode->younger; + } + + return(nodeCount); + +} /* end of ddCountInternalMtrNodes */ +#endif + + +/**Function******************************************************************** + + Synopsis [Reorders the children of a group tree node according to + the options.] + + Description [Reorders the children of a group tree node according to + the options. After reordering puts all the variables in the group + and/or its descendents in a single group. This allows hierarchical + reordering. If the variables in the group do not exist yet, simply + does nothing. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddReorderChildren( + DdManager * table, + MtrNode * treenode, + Cudd_ReorderingType method) +{ + int lower; + int upper; + int result; + unsigned int initialSize; + + ddFindNodeHiLo(table,treenode,&lower,&upper); + /* If upper == -1 these variables do not exist yet. */ + if (upper == -1) + return(1); + + if (treenode->flags == MTR_FIXED) { + result = 1; + } else { +#ifdef DD_STATS + (void) fprintf(table->out," "); +#endif + switch (method) { + case CUDD_REORDER_RANDOM: + case CUDD_REORDER_RANDOM_PIVOT: + result = cuddSwapping(table,lower,upper,method); + break; + case CUDD_REORDER_SIFT: + result = cuddSifting(table,lower,upper); + break; + case CUDD_REORDER_SIFT_CONVERGE: + do { + initialSize = table->keys - table->isolated; + result = cuddSifting(table,lower,upper); + if (initialSize <= table->keys - table->isolated) + break; +#ifdef DD_STATS + else + (void) fprintf(table->out,"\n"); +#endif + } while (result != 0); + break; + case CUDD_REORDER_SYMM_SIFT: + result = cuddSymmSifting(table,lower,upper); + break; + case CUDD_REORDER_SYMM_SIFT_CONV: + result = cuddSymmSiftingConv(table,lower,upper); + break; + case CUDD_REORDER_GROUP_SIFT: + if (table->groupcheck == CUDD_NO_CHECK) { + result = ddGroupSifting(table,lower,upper,ddNoCheck, + DD_NORMAL_SIFT); + } else if (table->groupcheck == CUDD_GROUP_CHECK5) { + result = ddGroupSifting(table,lower,upper,ddExtSymmCheck, + DD_NORMAL_SIFT); + } else if (table->groupcheck == CUDD_GROUP_CHECK7) { + result = ddGroupSifting(table,lower,upper,ddExtSymmCheck, + DD_NORMAL_SIFT); + } else { + (void) fprintf(table->err, + "Unknown group ckecking method\n"); + result = 0; + } + break; + case CUDD_REORDER_GROUP_SIFT_CONV: + do { + initialSize = table->keys - table->isolated; + if (table->groupcheck == CUDD_NO_CHECK) { + result = ddGroupSifting(table,lower,upper,ddNoCheck, + DD_NORMAL_SIFT); + } else if (table->groupcheck == CUDD_GROUP_CHECK5) { + result = ddGroupSifting(table,lower,upper,ddExtSymmCheck, + DD_NORMAL_SIFT); + } else if (table->groupcheck == CUDD_GROUP_CHECK7) { + result = ddGroupSifting(table,lower,upper,ddExtSymmCheck, + DD_NORMAL_SIFT); + } else { + (void) fprintf(table->err, + "Unknown group ckecking method\n"); + result = 0; + } +#ifdef DD_STATS + (void) fprintf(table->out,"\n"); +#endif + result = cuddWindowReorder(table,lower,upper, + CUDD_REORDER_WINDOW4); + if (initialSize <= table->keys - table->isolated) + break; +#ifdef DD_STATS + else + (void) fprintf(table->out,"\n"); +#endif + } while (result != 0); + break; + case CUDD_REORDER_WINDOW2: + case CUDD_REORDER_WINDOW3: + case CUDD_REORDER_WINDOW4: + case CUDD_REORDER_WINDOW2_CONV: + case CUDD_REORDER_WINDOW3_CONV: + case CUDD_REORDER_WINDOW4_CONV: + result = cuddWindowReorder(table,lower,upper,method); + break; + case CUDD_REORDER_ANNEALING: + result = cuddAnnealing(table,lower,upper); + break; + case CUDD_REORDER_GENETIC: + result = cuddGa(table,lower,upper); + break; + case CUDD_REORDER_LINEAR: + result = cuddLinearAndSifting(table,lower,upper); + break; + case CUDD_REORDER_LINEAR_CONVERGE: + do { + initialSize = table->keys - table->isolated; + result = cuddLinearAndSifting(table,lower,upper); + if (initialSize <= table->keys - table->isolated) + break; +#ifdef DD_STATS + else + (void) fprintf(table->out,"\n"); +#endif + } while (result != 0); + break; + case CUDD_REORDER_EXACT: + result = cuddExact(table,lower,upper); + break; + case CUDD_REORDER_LAZY_SIFT: + result = ddGroupSifting(table,lower,upper,ddVarGroupCheck, + DD_LAZY_SIFT); + break; + default: + return(0); + } + } + + /* Create a single group for all the variables that were sifted, + ** so that they will be treated as a single block by successive + ** invocations of ddGroupSifting. + */ + ddMergeGroups(table,treenode,lower,upper); + +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"ddReorderChildren:"); +#endif + + return(result); + +} /* end of ddReorderChildren */ + + +/**Function******************************************************************** + + Synopsis [Finds the lower and upper bounds of the group represented + by treenode.] + + Description [Finds the lower and upper bounds of the group + represented by treenode. From the index and size fields we need to + derive the current positions, and find maximum and minimum.] + + SideEffects [The bounds are returned as side effects.] + + SeeAlso [] + +******************************************************************************/ +static void +ddFindNodeHiLo( + DdManager * table, + MtrNode * treenode, + int * lower, + int * upper) +{ + int low; + int high; + + /* Check whether no variables in this group already exist. + ** If so, return immediately. The calling procedure will know from + ** the values of upper that no reordering is needed. + */ + if ((int) treenode->low >= table->size) { + *lower = table->size; + *upper = -1; + return; + } + + *lower = low = (unsigned int) table->perm[treenode->index]; + high = (int) (low + treenode->size - 1); + + if (high >= table->size) { + /* This is the case of a partially existing group. The aim is to + ** reorder as many variables as safely possible. If the tree + ** node is terminal, we just reorder the subset of the group + ** that is currently in existence. If the group has + ** subgroups, then we only reorder those subgroups that are + ** fully instantiated. This way we avoid breaking up a group. + */ + MtrNode *auxnode = treenode->child; + if (auxnode == NULL) { + *upper = (unsigned int) table->size - 1; + } else { + /* Search the subgroup that strands the table->size line. + ** If the first group starts at 0 and goes past table->size + ** upper will get -1, thus correctly signaling that no reordering + ** should take place. + */ + while (auxnode != NULL) { + int thisLower = table->perm[auxnode->low]; + int thisUpper = thisLower + auxnode->size - 1; + if (thisUpper >= table->size && thisLower < table->size) + *upper = (unsigned int) thisLower - 1; + auxnode = auxnode->younger; + } + } + } else { + /* Normal case: All the variables of the group exist. */ + *upper = (unsigned int) high; + } + +#ifdef DD_DEBUG + /* Make sure that all variables in group are contiguous. */ + assert(treenode->size >= *upper - *lower + 1); +#endif + + return; + +} /* end of ddFindNodeHiLo */ + + +/**Function******************************************************************** + + Synopsis [Comparison function used by qsort.] + + Description [Comparison function used by qsort to order the variables + according to the number of keys in the subtables. Returns the + difference in number of keys between the two variables being + compared.] + + SideEffects [None] + +******************************************************************************/ +static int +ddUniqueCompareGroup( + int * ptrX, + int * ptrY) +{ +#if 0 + if (entry[*ptrY] == entry[*ptrX]) { + return((*ptrX) - (*ptrY)); + } +#endif + return(entry[*ptrY] - entry[*ptrX]); + +} /* end of ddUniqueCompareGroup */ + + +/**Function******************************************************************** + + Synopsis [Sifts from treenode->low to treenode->high.] + + Description [Sifts from treenode->low to treenode->high. If + croupcheck == CUDD_GROUP_CHECK7, it checks for group creation at the + end of the initial sifting. If a group is created, it is then sifted + again. After sifting one variable, the group that contains it is + dissolved. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddGroupSifting( + DdManager * table, + int lower, + int upper, + int (*checkFunction)(DdManager *, int, int), + int lazyFlag) +{ + int *var; + int i,j,x,xInit; + int nvars; + int classes; + int result; + int *sifted; + int merged; + int dissolve; +#ifdef DD_STATS + unsigned previousSize; +#endif + int xindex; + + nvars = table->size; + + /* Order variables to sift. */ + entry = NULL; + sifted = NULL; + var = ALLOC(int,nvars); + if (var == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto ddGroupSiftingOutOfMem; + } + entry = ALLOC(int,nvars); + if (entry == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto ddGroupSiftingOutOfMem; + } + sifted = ALLOC(int,nvars); + if (sifted == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto ddGroupSiftingOutOfMem; + } + + /* Here we consider only one representative for each group. */ + for (i = 0, classes = 0; i < nvars; i++) { + sifted[i] = 0; + x = table->perm[i]; + if ((unsigned) x >= table->subtables[x].next) { + entry[i] = table->subtables[x].keys; + var[classes] = i; + classes++; + } + } + + qsort((void *)var,classes,sizeof(int), + (int (*)(const void *, const void *)) ddUniqueCompareGroup); + + if (lazyFlag) { + for (i = 0; i < nvars; i ++) { + ddResetVarHandled(table, i); + } + } + + /* Now sift. */ + for (i = 0; i < ddMin(table->siftMaxVar,classes); i++) { + if (ddTotalNumberSwapping >= table->siftMaxSwap) + break; + xindex = var[i]; + if (sifted[xindex] == 1) /* variable already sifted as part of group */ + continue; + x = table->perm[xindex]; /* find current level of this variable */ + + if (x < lower || x > upper || table->subtables[x].bindVar == 1) + continue; +#ifdef DD_STATS + previousSize = table->keys - table->isolated; +#endif +#ifdef DD_DEBUG + /* x is bottom of group */ + assert((unsigned) x >= table->subtables[x].next); +#endif + if ((unsigned) x == table->subtables[x].next) { + dissolve = 1; + result = ddGroupSiftingAux(table,x,lower,upper,checkFunction, + lazyFlag); + } else { + dissolve = 0; + result = ddGroupSiftingAux(table,x,lower,upper,ddNoCheck,lazyFlag); + } + if (!result) goto ddGroupSiftingOutOfMem; + + /* check for aggregation */ + merged = 0; + if (lazyFlag == 0 && table->groupcheck == CUDD_GROUP_CHECK7) { + x = table->perm[xindex]; /* find current level */ + if ((unsigned) x == table->subtables[x].next) { /* not part of a group */ + if (x != upper && sifted[table->invperm[x+1]] == 0 && + (unsigned) x+1 == table->subtables[x+1].next) { + if (ddSecDiffCheck(table,x,x+1)) { + merged =1; + ddCreateGroup(table,x,x+1); + } + } + if (x != lower && sifted[table->invperm[x-1]] == 0 && + (unsigned) x-1 == table->subtables[x-1].next) { + if (ddSecDiffCheck(table,x-1,x)) { + merged =1; + ddCreateGroup(table,x-1,x); + } + } + } + } + + if (merged) { /* a group was created */ + /* move x to bottom of group */ + while ((unsigned) x < table->subtables[x].next) + x = table->subtables[x].next; + /* sift */ + result = ddGroupSiftingAux(table,x,lower,upper,ddNoCheck,lazyFlag); + if (!result) goto ddGroupSiftingOutOfMem; +#ifdef DD_STATS + if (table->keys < previousSize + table->isolated) { + (void) fprintf(table->out,"_"); + } else if (table->keys > previousSize + table->isolated) { + (void) fprintf(table->out,"^"); + } else { + (void) fprintf(table->out,"*"); + } + fflush(table->out); + } else { + if (table->keys < previousSize + table->isolated) { + (void) fprintf(table->out,"-"); + } else if (table->keys > previousSize + table->isolated) { + (void) fprintf(table->out,"+"); + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + + /* Mark variables in the group just sifted. */ + x = table->perm[xindex]; + if ((unsigned) x != table->subtables[x].next) { + xInit = x; + do { + j = table->invperm[x]; + sifted[j] = 1; + x = table->subtables[x].next; + } while (x != xInit); + + /* Dissolve the group if it was created. */ + if (lazyFlag == 0 && dissolve) { + do { + j = table->subtables[x].next; + table->subtables[x].next = x; + x = j; + } while (x != xInit); + } + } + +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"ddGroupSifting:"); +#endif + + if (lazyFlag) ddSetVarHandled(table, xindex); + } /* for */ + + FREE(sifted); + FREE(var); + FREE(entry); + + return(1); + +ddGroupSiftingOutOfMem: + if (entry != NULL) FREE(entry); + if (var != NULL) FREE(var); + if (sifted != NULL) FREE(sifted); + + return(0); + +} /* end of ddGroupSifting */ + + +/**Function******************************************************************** + + Synopsis [Creates a group encompassing variables from x to y in the + DD table.] + + Description [Creates a group encompassing variables from x to y in the + DD table. In the current implementation it must be y == x+1. + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static void +ddCreateGroup( + DdManager * table, + int x, + int y) +{ + int gybot; + +#ifdef DD_DEBUG + assert(y == x+1); +#endif + + /* Find bottom of second group. */ + gybot = y; + while ((unsigned) gybot < table->subtables[gybot].next) + gybot = table->subtables[gybot].next; + + /* Link groups. */ + table->subtables[x].next = y; + table->subtables[gybot].next = x; + + return; + +} /* ddCreateGroup */ + + +/**Function******************************************************************** + + Synopsis [Sifts one variable up and down until it has taken all + positions. Checks for aggregation.] + + Description [Sifts one variable up and down until it has taken all + positions. Checks for aggregation. There may be at most two sweeps, + even if the group grows. Assumes that x is either an isolated + variable, or it is the bottom of a group. All groups may not have + been found. The variable being moved is returned to the best position + seen during sifting. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddGroupSiftingAux( + DdManager * table, + int x, + int xLow, + int xHigh, + int (*checkFunction)(DdManager *, int, int), + int lazyFlag) +{ + Move *move; + Move *moves; /* list of moves */ + int initialSize; + int result; + int y; + int topbot; + +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out, + "ddGroupSiftingAux from %d to %d\n",xLow,xHigh); + assert((unsigned) x >= table->subtables[x].next); /* x is bottom of group */ +#endif + + initialSize = table->keys - table->isolated; + moves = NULL; + + originalSize = initialSize; /* for lazy sifting */ + + /* If we have a singleton, we check for aggregation in both + ** directions before we sift. + */ + if ((unsigned) x == table->subtables[x].next) { + /* Will go down first, unless x == xHigh: + ** Look for aggregation above x. + */ + for (y = x; y > xLow; y--) { + if (!checkFunction(table,y-1,y)) + break; + topbot = table->subtables[y-1].next; /* find top of y-1's group */ + table->subtables[y-1].next = y; + table->subtables[x].next = topbot; /* x is bottom of group so its */ + /* next is top of y-1's group */ + y = topbot + 1; /* add 1 for y--; new y is top of group */ + } + /* Will go up first unless x == xlow: + ** Look for aggregation below x. + */ + for (y = x; y < xHigh; y++) { + if (!checkFunction(table,y,y+1)) + break; + /* find bottom of y+1's group */ + topbot = y + 1; + while ((unsigned) topbot < table->subtables[topbot].next) { + topbot = table->subtables[topbot].next; + } + table->subtables[topbot].next = table->subtables[y].next; + table->subtables[y].next = y + 1; + y = topbot - 1; /* subtract 1 for y++; new y is bottom of group */ + } + } + + /* Now x may be in the middle of a group. + ** Find bottom of x's group. + */ + while ((unsigned) x < table->subtables[x].next) + x = table->subtables[x].next; + + originalLevel = x; /* for lazy sifting */ + + if (x == xLow) { /* Sift down */ +#ifdef DD_DEBUG + /* x must be a singleton */ + assert((unsigned) x == table->subtables[x].next); +#endif + if (x == xHigh) return(1); /* just one variable */ + + if (!ddGroupSiftingDown(table,x,xHigh,checkFunction,&moves)) + goto ddGroupSiftingAuxOutOfMem; + /* at this point x == xHigh, unless early term */ + + /* move backward and stop at best position */ + result = ddGroupSiftingBackward(table,moves,initialSize, + DD_SIFT_DOWN,lazyFlag); +#ifdef DD_DEBUG + assert(table->keys - table->isolated <= (unsigned) initialSize); +#endif + if (!result) goto ddGroupSiftingAuxOutOfMem; + + } else if (cuddNextHigh(table,x) > xHigh) { /* Sift up */ +#ifdef DD_DEBUG + /* x is bottom of group */ + assert((unsigned) x >= table->subtables[x].next); +#endif + /* Find top of x's group */ + x = table->subtables[x].next; + + if (!ddGroupSiftingUp(table,x,xLow,checkFunction,&moves)) + goto ddGroupSiftingAuxOutOfMem; + /* at this point x == xLow, unless early term */ + + /* move backward and stop at best position */ + result = ddGroupSiftingBackward(table,moves,initialSize, + DD_SIFT_UP,lazyFlag); +#ifdef DD_DEBUG + assert(table->keys - table->isolated <= (unsigned) initialSize); +#endif + if (!result) goto ddGroupSiftingAuxOutOfMem; + + } else if (x - xLow > xHigh - x) { /* must go down first: shorter */ + if (!ddGroupSiftingDown(table,x,xHigh,checkFunction,&moves)) + goto ddGroupSiftingAuxOutOfMem; + /* at this point x == xHigh, unless early term */ + + /* Find top of group */ + if (moves) { + x = moves->y; + } + while ((unsigned) x < table->subtables[x].next) + x = table->subtables[x].next; + x = table->subtables[x].next; +#ifdef DD_DEBUG + /* x should be the top of a group */ + assert((unsigned) x <= table->subtables[x].next); +#endif + + if (!ddGroupSiftingUp(table,x,xLow,checkFunction,&moves)) + goto ddGroupSiftingAuxOutOfMem; + + /* move backward and stop at best position */ + result = ddGroupSiftingBackward(table,moves,initialSize, + DD_SIFT_UP,lazyFlag); +#ifdef DD_DEBUG + assert(table->keys - table->isolated <= (unsigned) initialSize); +#endif + if (!result) goto ddGroupSiftingAuxOutOfMem; + + } else { /* moving up first: shorter */ + /* Find top of x's group */ + x = table->subtables[x].next; + + if (!ddGroupSiftingUp(table,x,xLow,checkFunction,&moves)) + goto ddGroupSiftingAuxOutOfMem; + /* at this point x == xHigh, unless early term */ + + if (moves) { + x = moves->x; + } + while ((unsigned) x < table->subtables[x].next) + x = table->subtables[x].next; +#ifdef DD_DEBUG + /* x is bottom of a group */ + assert((unsigned) x >= table->subtables[x].next); +#endif + + if (!ddGroupSiftingDown(table,x,xHigh,checkFunction,&moves)) + goto ddGroupSiftingAuxOutOfMem; + + /* move backward and stop at best position */ + result = ddGroupSiftingBackward(table,moves,initialSize, + DD_SIFT_DOWN,lazyFlag); +#ifdef DD_DEBUG + assert(table->keys - table->isolated <= (unsigned) initialSize); +#endif + if (!result) goto ddGroupSiftingAuxOutOfMem; + } + + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *) moves); + moves = move; + } + + return(1); + +ddGroupSiftingAuxOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *) moves); + moves = move; + } + + return(0); + +} /* end of ddGroupSiftingAux */ + + +/**Function******************************************************************** + + Synopsis [Sifts up a variable until either it reaches position xLow + or the size of the DD heap increases too much.] + + Description [Sifts up a variable until either it reaches position + xLow or the size of the DD heap increases too much. Assumes that y is + the top of a group (or a singleton). Checks y for aggregation to the + adjacent variables. Records all the moves that are appended to the + list of moves received as input and returned as a side effect. + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddGroupSiftingUp( + DdManager * table, + int y, + int xLow, + int (*checkFunction)(DdManager *, int, int), + Move ** moves) +{ + Move *move; + int x; + int size; + int i; + int gxtop,gybot; + int limitSize; + int xindex, yindex; + int zindex; + int z; + int isolated; + int L; /* lower bound on DD size */ +#ifdef DD_DEBUG + int checkL; +#endif + + yindex = table->invperm[y]; + + /* Initialize the lower bound. + ** The part of the DD below the bottom of y's group will not change. + ** The part of the DD above y that does not interact with any + ** variable of y's group will not change. + ** The rest may vanish in the best case, except for + ** the nodes at level xLow, which will not vanish, regardless. + ** What we use here is not really a lower bound, because we ignore + ** the interactions with all variables except y. + */ + limitSize = L = table->keys - table->isolated; + gybot = y; + while ((unsigned) gybot < table->subtables[gybot].next) + gybot = table->subtables[gybot].next; + for (z = xLow + 1; z <= gybot; z++) { + zindex = table->invperm[z]; + if (zindex == yindex || cuddTestInteract(table,zindex,yindex)) { + isolated = table->vars[zindex]->ref == 1; + L -= table->subtables[z].keys - isolated; + } + } + + originalLevel = y; /* for lazy sifting */ + + x = cuddNextLow(table,y); + while (x >= xLow && L <= limitSize) { +#ifdef DD_DEBUG + gybot = y; + while ((unsigned) gybot < table->subtables[gybot].next) + gybot = table->subtables[gybot].next; + checkL = table->keys - table->isolated; + for (z = xLow + 1; z <= gybot; z++) { + zindex = table->invperm[z]; + if (zindex == yindex || cuddTestInteract(table,zindex,yindex)) { + isolated = table->vars[zindex]->ref == 1; + checkL -= table->subtables[z].keys - isolated; + } + } + if (pr > 0 && L != checkL) { + (void) fprintf(table->out, + "Inaccurate lower bound: L = %d checkL = %d\n", + L, checkL); + } +#endif + gxtop = table->subtables[x].next; + if (checkFunction(table,x,y)) { + /* Group found, attach groups */ + table->subtables[x].next = y; + i = table->subtables[y].next; + while (table->subtables[i].next != (unsigned) y) + i = table->subtables[i].next; + table->subtables[i].next = gxtop; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddGroupSiftingUpOutOfMem; + move->x = x; + move->y = y; + move->flags = MTR_NEWNODE; + move->size = table->keys - table->isolated; + move->next = *moves; + *moves = move; + } else if (table->subtables[x].next == (unsigned) x && + table->subtables[y].next == (unsigned) y) { + /* x and y are self groups */ + xindex = table->invperm[x]; + size = cuddSwapInPlace(table,x,y); +#ifdef DD_DEBUG + assert(table->subtables[x].next == (unsigned) x); + assert(table->subtables[y].next == (unsigned) y); +#endif + if (size == 0) goto ddGroupSiftingUpOutOfMem; + /* Update the lower bound. */ + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[xindex]->ref == 1; + L += table->subtables[y].keys - isolated; + } + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddGroupSiftingUpOutOfMem; + move->x = x; + move->y = y; + move->flags = MTR_DEFAULT; + move->size = size; + move->next = *moves; + *moves = move; + +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out, + "ddGroupSiftingUp (2 single groups):\n"); +#endif + if ((double) size > (double) limitSize * table->maxGrowth) + return(1); + if (size < limitSize) limitSize = size; + } else { /* Group move */ + size = ddGroupMove(table,x,y,moves); + if (size == 0) goto ddGroupSiftingUpOutOfMem; + /* Update the lower bound. */ + z = (*moves)->y; + do { + zindex = table->invperm[z]; + if (cuddTestInteract(table,zindex,yindex)) { + isolated = table->vars[zindex]->ref == 1; + L += table->subtables[z].keys - isolated; + } + z = table->subtables[z].next; + } while (z != (int) (*moves)->y); + if ((double) size > (double) limitSize * table->maxGrowth) + return(1); + if (size < limitSize) limitSize = size; + } + y = gxtop; + x = cuddNextLow(table,y); + } + + return(1); + +ddGroupSiftingUpOutOfMem: + while (*moves != NULL) { + move = (*moves)->next; + cuddDeallocNode(table, (DdNode *) *moves); + *moves = move; + } + return(0); + +} /* end of ddGroupSiftingUp */ + + +/**Function******************************************************************** + + Synopsis [Sifts down a variable until it reaches position xHigh.] + + Description [Sifts down a variable until it reaches position xHigh. + Assumes that x is the bottom of a group (or a singleton). Records + all the moves. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddGroupSiftingDown( + DdManager * table, + int x, + int xHigh, + int (*checkFunction)(DdManager *, int, int), + Move ** moves) +{ + Move *move; + int y; + int size; + int limitSize; + int gxtop,gybot; + int R; /* upper bound on node decrease */ + int xindex, yindex; + int isolated, allVars; + int z; + int zindex; +#ifdef DD_DEBUG + int checkR; +#endif + + /* If the group consists of simple variables, there is no point in + ** sifting it down. This check is redundant if the projection functions + ** do not have external references, because the computation of the + ** lower bound takes care of the problem. It is necessary otherwise to + ** prevent the sifting down of simple variables. */ + y = x; + allVars = 1; + do { + if (table->subtables[y].keys != 1) { + allVars = 0; + break; + } + y = table->subtables[y].next; + } while (table->subtables[y].next != (unsigned) x); + if (allVars) + return(1); + + /* Initialize R. */ + xindex = table->invperm[x]; + gxtop = table->subtables[x].next; + limitSize = size = table->keys - table->isolated; + R = 0; + for (z = xHigh; z > gxtop; z--) { + zindex = table->invperm[z]; + if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) { + isolated = table->vars[zindex]->ref == 1; + R += table->subtables[z].keys - isolated; + } + } + + originalLevel = x; /* for lazy sifting */ + + y = cuddNextHigh(table,x); + while (y <= xHigh && size - R < limitSize) { +#ifdef DD_DEBUG + gxtop = table->subtables[x].next; + checkR = 0; + for (z = xHigh; z > gxtop; z--) { + zindex = table->invperm[z]; + if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) { + isolated = table->vars[zindex]->ref == 1; + checkR += table->subtables[z].keys - isolated; + } + } + assert(R >= checkR); +#endif + /* Find bottom of y group. */ + gybot = table->subtables[y].next; + while (table->subtables[gybot].next != (unsigned) y) + gybot = table->subtables[gybot].next; + + if (checkFunction(table,x,y)) { + /* Group found: attach groups and record move. */ + gxtop = table->subtables[x].next; + table->subtables[x].next = y; + table->subtables[gybot].next = gxtop; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddGroupSiftingDownOutOfMem; + move->x = x; + move->y = y; + move->flags = MTR_NEWNODE; + move->size = table->keys - table->isolated; + move->next = *moves; + *moves = move; + } else if (table->subtables[x].next == (unsigned) x && + table->subtables[y].next == (unsigned) y) { + /* x and y are self groups */ + /* Update upper bound on node decrease. */ + yindex = table->invperm[y]; + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[yindex]->ref == 1; + R -= table->subtables[y].keys - isolated; + } + size = cuddSwapInPlace(table,x,y); +#ifdef DD_DEBUG + assert(table->subtables[x].next == (unsigned) x); + assert(table->subtables[y].next == (unsigned) y); +#endif + if (size == 0) goto ddGroupSiftingDownOutOfMem; + + /* Record move. */ + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddGroupSiftingDownOutOfMem; + move->x = x; + move->y = y; + move->flags = MTR_DEFAULT; + move->size = size; + move->next = *moves; + *moves = move; + +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out, + "ddGroupSiftingDown (2 single groups):\n"); +#endif + if ((double) size > (double) limitSize * table->maxGrowth) + return(1); + if (size < limitSize) limitSize = size; + + x = y; + y = cuddNextHigh(table,x); + } else { /* Group move */ + /* Update upper bound on node decrease: first phase. */ + gxtop = table->subtables[x].next; + z = gxtop + 1; + do { + zindex = table->invperm[z]; + if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) { + isolated = table->vars[zindex]->ref == 1; + R -= table->subtables[z].keys - isolated; + } + z++; + } while (z <= gybot); + size = ddGroupMove(table,x,y,moves); + if (size == 0) goto ddGroupSiftingDownOutOfMem; + if ((double) size > (double) limitSize * table->maxGrowth) + return(1); + if (size < limitSize) limitSize = size; + + /* Update upper bound on node decrease: second phase. */ + gxtop = table->subtables[gybot].next; + for (z = gxtop + 1; z <= gybot; z++) { + zindex = table->invperm[z]; + if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) { + isolated = table->vars[zindex]->ref == 1; + R += table->subtables[z].keys - isolated; + } + } + } + x = gybot; + y = cuddNextHigh(table,x); + } + + return(1); + +ddGroupSiftingDownOutOfMem: + while (*moves != NULL) { + move = (*moves)->next; + cuddDeallocNode(table, (DdNode *) *moves); + *moves = move; + } + + return(0); + +} /* end of ddGroupSiftingDown */ + + +/**Function******************************************************************** + + Synopsis [Swaps two groups and records the move.] + + Description [Swaps two groups and records the move. Returns the + number of keys in the DD table in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddGroupMove( + DdManager * table, + int x, + int y, + Move ** moves) +{ + Move *move; + int size; + int i,j,xtop,xbot,xsize,ytop,ybot,ysize,newxtop; + int swapx,swapy; +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + int initialSize,bestSize; +#endif + +#if DD_DEBUG + /* We assume that x < y */ + assert(x < y); +#endif + /* Find top, bottom, and size for the two groups. */ + xbot = x; + xtop = table->subtables[x].next; + xsize = xbot - xtop + 1; + ybot = y; + while ((unsigned) ybot < table->subtables[ybot].next) + ybot = table->subtables[ybot].next; + ytop = y; + ysize = ybot - ytop + 1; + +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + initialSize = bestSize = table->keys - table->isolated; +#endif + /* Sift the variables of the second group up through the first group */ + for (i = 1; i <= ysize; i++) { + for (j = 1; j <= xsize; j++) { + size = cuddSwapInPlace(table,x,y); + if (size == 0) goto ddGroupMoveOutOfMem; +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + if (size < bestSize) + bestSize = size; +#endif + swapx = x; swapy = y; + y = x; + x = cuddNextLow(table,y); + } + y = ytop + i; + x = cuddNextLow(table,y); + } +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + if ((bestSize < initialSize) && (bestSize < size)) + (void) fprintf(table->out,"Missed local minimum: initialSize:%d bestSize:%d finalSize:%d\n",initialSize,bestSize,size); +#endif + + /* fix groups */ + y = xtop; /* ytop is now where xtop used to be */ + for (i = 0; i < ysize - 1; i++) { + table->subtables[y].next = cuddNextHigh(table,y); + y = cuddNextHigh(table,y); + } + table->subtables[y].next = xtop; /* y is bottom of its group, join */ + /* it to top of its group */ + x = cuddNextHigh(table,y); + newxtop = x; + for (i = 0; i < xsize - 1; i++) { + table->subtables[x].next = cuddNextHigh(table,x); + x = cuddNextHigh(table,x); + } + table->subtables[x].next = newxtop; /* x is bottom of its group, join */ + /* it to top of its group */ +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"ddGroupMove:\n"); +#endif + + /* Store group move */ + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddGroupMoveOutOfMem; + move->x = swapx; + move->y = swapy; + move->flags = MTR_DEFAULT; + move->size = table->keys - table->isolated; + move->next = *moves; + *moves = move; + + return(table->keys - table->isolated); + +ddGroupMoveOutOfMem: + while (*moves != NULL) { + move = (*moves)->next; + cuddDeallocNode(table, (DdNode *) *moves); + *moves = move; + } + return(0); + +} /* end of ddGroupMove */ + + +/**Function******************************************************************** + + Synopsis [Undoes the swap two groups.] + + Description [Undoes the swap two groups. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddGroupMoveBackward( + DdManager * table, + int x, + int y) +{ + int size; + int i,j,xtop,xbot,xsize,ytop,ybot,ysize,newxtop; + + +#if DD_DEBUG + /* We assume that x < y */ + assert(x < y); +#endif + + /* Find top, bottom, and size for the two groups. */ + xbot = x; + xtop = table->subtables[x].next; + xsize = xbot - xtop + 1; + ybot = y; + while ((unsigned) ybot < table->subtables[ybot].next) + ybot = table->subtables[ybot].next; + ytop = y; + ysize = ybot - ytop + 1; + + /* Sift the variables of the second group up through the first group */ + for (i = 1; i <= ysize; i++) { + for (j = 1; j <= xsize; j++) { + size = cuddSwapInPlace(table,x,y); + if (size == 0) + return(0); + y = x; + x = cuddNextLow(table,y); + } + y = ytop + i; + x = cuddNextLow(table,y); + } + + /* fix groups */ + y = xtop; + for (i = 0; i < ysize - 1; i++) { + table->subtables[y].next = cuddNextHigh(table,y); + y = cuddNextHigh(table,y); + } + table->subtables[y].next = xtop; /* y is bottom of its group, join */ + /* to its top */ + x = cuddNextHigh(table,y); + newxtop = x; + for (i = 0; i < xsize - 1; i++) { + table->subtables[x].next = cuddNextHigh(table,x); + x = cuddNextHigh(table,x); + } + table->subtables[x].next = newxtop; /* x is bottom of its group, join */ + /* to its top */ +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"ddGroupMoveBackward:\n"); +#endif + + return(1); + +} /* end of ddGroupMoveBackward */ + + +/**Function******************************************************************** + + Synopsis [Determines the best position for a variables and returns + it there.] + + Description [Determines the best position for a variables and returns + it there. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddGroupSiftingBackward( + DdManager * table, + Move * moves, + int size, + int upFlag, + int lazyFlag) +{ + Move *move; + int res; + Move *end_move; + int diff, tmp_diff; + int index, pairlev; + + if (lazyFlag) { + end_move = NULL; + + /* Find the minimum size, and the earliest position at which it + ** was achieved. */ + for (move = moves; move != NULL; move = move->next) { + if (move->size < size) { + size = move->size; + end_move = move; + } else if (move->size == size) { + if (end_move == NULL) end_move = move; + } + } + + /* Find among the moves that give minimum size the one that + ** minimizes the distance from the corresponding variable. */ + if (moves != NULL) { + diff = Cudd_ReadSize(table) + 1; + index = (upFlag == 1) ? + table->invperm[moves->x] : table->invperm[moves->y]; + pairlev = table->perm[Cudd_bddReadPairIndex(table, index)]; + + for (move = moves; move != NULL; move = move->next) { + if (move->size == size) { + if (upFlag == 1) { + tmp_diff = (move->x > pairlev) ? + move->x - pairlev : pairlev - move->x; + } else { + tmp_diff = (move->y > pairlev) ? + move->y - pairlev : pairlev - move->y; + } + if (tmp_diff < diff) { + diff = tmp_diff; + end_move = move; + } + } + } + } + } else { + /* Find the minimum size. */ + for (move = moves; move != NULL; move = move->next) { + if (move->size < size) { + size = move->size; + } + } + } + + /* In case of lazy sifting, end_move identifies the position at + ** which we want to stop. Otherwise, we stop as soon as we meet + ** the minimum size. */ + for (move = moves; move != NULL; move = move->next) { + if (lazyFlag) { + if (move == end_move) return(1); + } else { + if (move->size == size) return(1); + } + if ((table->subtables[move->x].next == move->x) && + (table->subtables[move->y].next == move->y)) { + res = cuddSwapInPlace(table,(int)move->x,(int)move->y); + if (!res) return(0); +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"ddGroupSiftingBackward:\n"); + assert(table->subtables[move->x].next == move->x); + assert(table->subtables[move->y].next == move->y); +#endif + } else { /* Group move necessary */ + if (move->flags == MTR_NEWNODE) { + ddDissolveGroup(table,(int)move->x,(int)move->y); + } else { + res = ddGroupMoveBackward(table,(int)move->x,(int)move->y); + if (!res) return(0); + } + } + + } + + return(1); + +} /* end of ddGroupSiftingBackward */ + + +/**Function******************************************************************** + + Synopsis [Merges groups in the DD table.] + + Description [Creates a single group from low to high and adjusts the + index field of the tree node.] + + SideEffects [None] + +******************************************************************************/ +static void +ddMergeGroups( + DdManager * table, + MtrNode * treenode, + int low, + int high) +{ + int i; + MtrNode *auxnode; + int saveindex; + int newindex; + + /* Merge all variables from low to high in one group, unless + ** this is the topmost group. In such a case we do not merge lest + ** we lose the symmetry information. */ + if (treenode != table->tree) { + for (i = low; i < high; i++) + table->subtables[i].next = i+1; + table->subtables[high].next = low; + } + + /* Adjust the index fields of the tree nodes. If a node is the + ** first child of its parent, then the parent may also need adjustment. */ + saveindex = treenode->index; + newindex = table->invperm[low]; + auxnode = treenode; + do { + auxnode->index = newindex; + if (auxnode->parent == NULL || + (int) auxnode->parent->index != saveindex) + break; + auxnode = auxnode->parent; + } while (1); + return; + +} /* end of ddMergeGroups */ + + +/**Function******************************************************************** + + Synopsis [Dissolves a group in the DD table.] + + Description [x and y are variables in a group to be cut in two. The cut + is to pass between x and y.] + + SideEffects [None] + +******************************************************************************/ +static void +ddDissolveGroup( + DdManager * table, + int x, + int y) +{ + int topx; + int boty; + + /* find top and bottom of the two groups */ + boty = y; + while ((unsigned) boty < table->subtables[boty].next) + boty = table->subtables[boty].next; + + topx = table->subtables[boty].next; + + table->subtables[boty].next = y; + table->subtables[x].next = topx; + + return; + +} /* end of ddDissolveGroup */ + + +/**Function******************************************************************** + + Synopsis [Pretends to check two variables for aggregation.] + + Description [Pretends to check two variables for aggregation. Always + returns 0.] + + SideEffects [None] + +******************************************************************************/ +static int +ddNoCheck( + DdManager * table, + int x, + int y) +{ + return(0); + +} /* end of ddNoCheck */ + + +/**Function******************************************************************** + + Synopsis [Checks two variables for aggregation.] + + Description [Checks two variables for aggregation. The check is based + on the second difference of the number of nodes as a function of the + layer. If the second difference is lower than a given threshold + (typically negative) then the two variables should be aggregated. + Returns 1 if the two variables pass the test; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddSecDiffCheck( + DdManager * table, + int x, + int y) +{ + double Nx,Nx_1; + double Sx; + double threshold; + int xindex,yindex; + + if (x==0) return(0); + +#ifdef DD_STATS + secdiffcalls++; +#endif + Nx = (double) table->subtables[x].keys; + Nx_1 = (double) table->subtables[x-1].keys; + Sx = (table->subtables[y].keys/Nx) - (Nx/Nx_1); + + threshold = table->recomb / 100.0; + if (Sx < threshold) { + xindex = table->invperm[x]; + yindex = table->invperm[y]; + if (cuddTestInteract(table,xindex,yindex)) { +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + (void) fprintf(table->out, + "Second difference for %d = %g Pos(%d)\n", + table->invperm[x],Sx,x); +#endif +#ifdef DD_STATS + secdiff++; +#endif + return(1); + } else { +#ifdef DD_STATS + secdiffmisfire++; +#endif + return(0); + } + + } + return(0); + +} /* end of ddSecDiffCheck */ + + +/**Function******************************************************************** + + Synopsis [Checks for extended symmetry of x and y.] + + Description [Checks for extended symmetry of x and y. Returns 1 in + case of extended symmetry; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddExtSymmCheck( + DdManager * table, + int x, + int y) +{ + DdNode *f,*f0,*f1,*f01,*f00,*f11,*f10; + DdNode *one; + int comple; /* f0 is complemented */ + int notproj; /* f is not a projection function */ + int arccount; /* number of arcs from layer x to layer y */ + int TotalRefCount; /* total reference count of layer y minus 1 */ + int counter; /* number of nodes of layer x that are allowed */ + /* to violate extended symmetry conditions */ + int arccounter; /* number of arcs into layer y that are allowed */ + /* to come from layers other than x */ + int i; + int xindex; + int yindex; + int res; + int slots; + DdNodePtr *list; + DdNode *sentinel = &(table->sentinel); + + xindex = table->invperm[x]; + yindex = table->invperm[y]; + + /* If the two variables do not interact, we do not want to merge them. */ + if (!cuddTestInteract(table,xindex,yindex)) + return(0); + +#ifdef DD_DEBUG + /* Checks that x and y do not contain just the projection functions. + ** With the test on interaction, these test become redundant, + ** because an isolated projection function does not interact with + ** any other variable. + */ + if (table->subtables[x].keys == 1) { + assert(table->vars[xindex]->ref != 1); + } + if (table->subtables[y].keys == 1) { + assert(table->vars[yindex]->ref != 1); + } +#endif + +#ifdef DD_STATS + extsymmcalls++; +#endif + + arccount = 0; + counter = (int) (table->subtables[x].keys * + (table->symmviolation/100.0) + 0.5); + one = DD_ONE(table); + + slots = table->subtables[x].slots; + list = table->subtables[x].nodelist; + for (i = 0; i < slots; i++) { + f = list[i]; + while (f != sentinel) { + /* Find f1, f0, f11, f10, f01, f00. */ + f1 = cuddT(f); + f0 = Cudd_Regular(cuddE(f)); + comple = Cudd_IsComplement(cuddE(f)); + notproj = f1 != one || f0 != one || f->ref != (DdHalfWord) 1; + if (f1->index == yindex) { + arccount++; + f11 = cuddT(f1); f10 = cuddE(f1); + } else { + if ((int) f0->index != yindex) { + /* If f is an isolated projection function it is + ** allowed to bypass layer y. + */ + if (notproj) { + if (counter == 0) + return(0); + counter--; /* f bypasses layer y */ + } + } + f11 = f10 = f1; + } + if ((int) f0->index == yindex) { + arccount++; + f01 = cuddT(f0); f00 = cuddE(f0); + } else { + f01 = f00 = f0; + } + if (comple) { + f01 = Cudd_Not(f01); + f00 = Cudd_Not(f00); + } + + /* Unless we are looking at a projection function + ** without external references except the one from the + ** table, we insist that f01 == f10 or f11 == f00 + */ + if (notproj) { + if (f01 != f10 && f11 != f00) { + if (counter == 0) + return(0); + counter--; + } + } + + f = f->next; + } /* while */ + } /* for */ + + /* Calculate the total reference counts of y */ + TotalRefCount = -1; /* -1 for projection function */ + slots = table->subtables[y].slots; + list = table->subtables[y].nodelist; + for (i = 0; i < slots; i++) { + f = list[i]; + while (f != sentinel) { + TotalRefCount += f->ref; + f = f->next; + } + } + + arccounter = (int) (table->subtables[y].keys * + (table->arcviolation/100.0) + 0.5); + res = arccount >= TotalRefCount - arccounter; + +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + if (res) { + (void) fprintf(table->out, + "Found extended symmetry! x = %d\ty = %d\tPos(%d,%d)\n", + xindex,yindex,x,y); + } +#endif + +#ifdef DD_STATS + if (res) + extsymm++; +#endif + return(res); + +} /* end ddExtSymmCheck */ + + +/**Function******************************************************************** + + Synopsis [Checks for grouping of x and y.] + + Description [Checks for grouping of x and y. Returns 1 in + case of grouping; 0 otherwise. This function is used for lazy sifting.] + + SideEffects [None] + +******************************************************************************/ +static int +ddVarGroupCheck( + DdManager * table, + int x, + int y) +{ + int xindex = table->invperm[x]; + int yindex = table->invperm[y]; + + if (Cudd_bddIsVarToBeUngrouped(table, xindex)) return(0); + + if (Cudd_bddReadPairIndex(table, xindex) == yindex) { + if (ddIsVarHandled(table, xindex) || + ddIsVarHandled(table, yindex)) { + if (Cudd_bddIsVarToBeGrouped(table, xindex) || + Cudd_bddIsVarToBeGrouped(table, yindex) ) { + if (table->keys - table->isolated <= originalSize) { + return(1); + } + } + } + } + + return(0); + +} /* end of ddVarGroupCheck */ + + +/**Function******************************************************************** + + Synopsis [Sets a variable to already handled.] + + Description [Sets a variable to already handled. This function is used + for lazy sifting.] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +static int +ddSetVarHandled( + DdManager *dd, + int index) +{ + if (index >= dd->size || index < 0) return(0); + dd->subtables[dd->perm[index]].varHandled = 1; + return(1); + +} /* end of ddSetVarHandled */ + + +/**Function******************************************************************** + + Synopsis [Resets a variable to be processed.] + + Description [Resets a variable to be processed. This function is used + for lazy sifting.] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +static int +ddResetVarHandled( + DdManager *dd, + int index) +{ + if (index >= dd->size || index < 0) return(0); + dd->subtables[dd->perm[index]].varHandled = 0; + return(1); + +} /* end of ddResetVarHandled */ + + +/**Function******************************************************************** + + Synopsis [Checks whether a variables is already handled.] + + Description [Checks whether a variables is already handled. This + function is used for lazy sifting.] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +static int +ddIsVarHandled( + DdManager *dd, + int index) +{ + if (index >= dd->size || index < 0) return(-1); + return dd->subtables[dd->perm[index]].varHandled; + +} /* end of ddIsVarHandled */ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddHarwell.c b/abc_with_bb_support/src/bdd/cudd/cuddHarwell.c new file mode 100644 index 000000000..a1fb1e7a0 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddHarwell.c @@ -0,0 +1,541 @@ +/**CFile*********************************************************************** + + FileName [cuddHarwell.c] + + PackageName [cudd] + + Synopsis [Function to read a matrix in Harwell format.] + + Description [External procedures included in this module: +
        +
      • Cudd_addHarwell() +
      + ] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddHarwell.c,v 1.1.1.1 2003/02/24 22:23:52 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Reads in a matrix in the format of the Harwell-Boeing + benchmark suite.] + + Description [Reads in a matrix in the format of the Harwell-Boeing + benchmark suite. The variables are ordered as follows: +
      + x\[0\] y\[0\] x\[1\] y\[1\] ... +
      + 0 is the most significant bit. On input, nx and ny hold the numbers + of row and column variables already in existence. On output, they + hold the numbers of row and column variables actually used by the + matrix. m and n are set to the numbers of rows and columns of the + matrix. Their values on input are immaterial. Returns 1 on + success; 0 otherwise. The ADD for the sparse matrix is returned in + E, and its reference count is > 0.] + + SideEffects [None] + + SeeAlso [Cudd_addRead Cudd_bddRead] + +******************************************************************************/ +int +Cudd_addHarwell( + FILE * fp /* pointer to the input file */, + DdManager * dd /* DD manager */, + DdNode ** E /* characteristic function of the graph */, + DdNode *** x /* array of row variables */, + DdNode *** y /* array of column variables */, + DdNode *** xn /* array of complemented row variables */, + DdNode *** yn_ /* array of complemented column variables */, + int * nx /* number or row variables */, + int * ny /* number or column variables */, + int * m /* number of rows */, + int * n /* number of columns */, + int bx /* first index of row variables */, + int sx /* step of row variables */, + int by /* first index of column variables */, + int sy /* step of column variables */, + int pr /* verbosity level */) +{ + DdNode *one, *zero; + DdNode *w; + DdNode *cubex, *cubey, *minterm1; + int u, v, err, i, j, nv; + double val; + DdNode **lx, **ly, **lxn, **lyn; /* local copies of x, y, xn, yn_ */ + int lnx, lny; /* local copies of nx and ny */ + char title[73], key[9], mxtype[4], rhstyp[4]; + int totcrd, ptrcrd, indcrd, valcrd, rhscrd, + nrow, ncol, nnzero, neltvl, + nrhs, nrhsix; + int *colptr, *rowind; +#if 0 + int nguess, nexact; + int *rhsptr, *rhsind; +#endif + + if (*nx < 0 || *ny < 0) return(0); + + one = DD_ONE(dd); + zero = DD_ZERO(dd); + + /* Read the header */ + err = fscanf(fp, "%72c %8c", title, key); + if (err == EOF) { + return(0); + } else if (err != 2) { + return(0); + } + title[72] = (char) 0; + key[8] = (char) 0; + + err = fscanf(fp, "%d %d %d %d %d", &totcrd, &ptrcrd, &indcrd, + &valcrd, &rhscrd); + if (err == EOF) { + return(0); + } else if (err != 5) { + return(0); + } + + err = fscanf(fp, "%3s %d %d %d %d", mxtype, &nrow, &ncol, + &nnzero, &neltvl); + if (err == EOF) { + return(0); + } else if (err != 5) { + return(0); + } + + /* Skip FORTRAN formats */ + if (rhscrd == 0) { + err = fscanf(fp, "%*s %*s %*s \n"); + } else { + err = fscanf(fp, "%*s %*s %*s %*s \n"); + } + if (err == EOF) { + return(0); + } else if (err != 0) { + return(0); + } + + /* Print out some stuff if requested to be verbose */ + if (pr>0) { + (void) fprintf(dd->out,"%s: type %s, %d rows, %d columns, %d entries\n", key, + mxtype, nrow, ncol, nnzero); + if (pr>1) (void) fprintf(dd->out,"%s\n", title); + } + + /* Check matrix type */ + if (mxtype[0] != 'R' || mxtype[1] != 'U' || mxtype[2] != 'A') { + (void) fprintf(dd->err,"%s: Illegal matrix type: %s\n", + key, mxtype); + return(0); + } + if (neltvl != 0) return(0); + + /* Read optional 5-th line */ + if (rhscrd != 0) { + err = fscanf(fp, "%3c %d %d", rhstyp, &nrhs, &nrhsix); + if (err == EOF) { + return(0); + } else if (err != 3) { + return(0); + } + rhstyp[3] = (char) 0; + if (rhstyp[0] != 'F') { + (void) fprintf(dd->err, + "%s: Sparse right-hand side not yet supported\n", key); + return(0); + } + if (pr>0) (void) fprintf(dd->out,"%d right-hand side(s)\n", nrhs); + } else { + nrhs = 0; + } + + /* Compute the number of variables */ + + /* row and column numbers start from 0 */ + u = nrow - 1; + for (i=0; u > 0; i++) { + u >>= 1; + } + lnx = i; + if (nrhs == 0) { + v = ncol - 1; + } else { + v = 2* (ddMax(ncol, nrhs) - 1); + } + for (i=0; v > 0; i++) { + v >>= 1; + } + lny = i; + + /* Allocate or reallocate arrays for variables as needed */ + if (*nx == 0) { + if (lnx > 0) { + *x = lx = ALLOC(DdNode *,lnx); + if (lx == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + *xn = lxn = ALLOC(DdNode *,lnx); + if (lxn == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + } else { + *x = *xn = NULL; + } + } else if (lnx > *nx) { + *x = lx = REALLOC(DdNode *, *x, lnx); + if (lx == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + *xn = lxn = REALLOC(DdNode *, *xn, lnx); + if (lxn == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + } else { + lx = *x; + lxn = *xn; + } + if (*ny == 0) { + if (lny >0) { + *y = ly = ALLOC(DdNode *,lny); + if (ly == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + *yn_ = lyn = ALLOC(DdNode *,lny); + if (lyn == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + } else { + *y = *yn_ = NULL; + } + } else if (lny > *ny) { + *y = ly = REALLOC(DdNode *, *y, lny); + if (ly == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + *yn_ = lyn = REALLOC(DdNode *, *yn_, lny); + if (lyn == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + } else { + ly = *y; + lyn = *yn_; + } + + /* Create new variables as needed */ + for (i= *nx,nv=bx+(*nx)*sx; i < lnx; i++,nv+=sx) { + do { + dd->reordered = 0; + lx[i] = cuddUniqueInter(dd, nv, one, zero); + } while (dd->reordered == 1); + if (lx[i] == NULL) return(0); + cuddRef(lx[i]); + do { + dd->reordered = 0; + lxn[i] = cuddUniqueInter(dd, nv, zero, one); + } while (dd->reordered == 1); + if (lxn[i] == NULL) return(0); + cuddRef(lxn[i]); + } + for (i= *ny,nv=by+(*ny)*sy; i < lny; i++,nv+=sy) { + do { + dd->reordered = 0; + ly[i] = cuddUniqueInter(dd, nv, one, zero); + } while (dd->reordered == 1); + if (ly[i] == NULL) return(0); + cuddRef(ly[i]); + do { + dd->reordered = 0; + lyn[i] = cuddUniqueInter(dd, nv, zero, one); + } while (dd->reordered == 1); + if (lyn[i] == NULL) return(0); + cuddRef(lyn[i]); + } + + /* Update matrix parameters */ + *nx = lnx; + *ny = lny; + *m = nrow; + if (nrhs == 0) { + *n = ncol; + } else { + *n = (1 << (lny - 1)) + nrhs; + } + + /* Read structure data */ + colptr = ALLOC(int, ncol+1); + if (colptr == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + rowind = ALLOC(int, nnzero); + if (rowind == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + + for (i=0; ierr,"%s: Unexpected colptr[0] (%d)\n", + key,colptr[0]); + FREE(colptr); + FREE(rowind); + return(0); + } + for (i=0; i=0; nv--) { + if (v & 1) { + w = Cudd_addApply(dd, Cudd_addTimes, cubey, ly[nv]); + } else { + w = Cudd_addApply(dd, Cudd_addTimes, cubey, lyn[nv]); + } + if (w == NULL) { + Cudd_RecursiveDeref(dd, cubey); + FREE(colptr); + FREE(rowind); + return(0); + } + cuddRef(w); + Cudd_RecursiveDeref(dd, cubey); + cubey = w; + v >>= 1; + } + for (i=colptr[j]; i=0; nv--) { + if (u & 1) { + w = Cudd_addApply(dd, Cudd_addTimes, cubex, lx[nv]); + } else { + w = Cudd_addApply(dd, Cudd_addTimes, cubex, lxn[nv]); + } + if (w == NULL) { + Cudd_RecursiveDeref(dd, cubey); + Cudd_RecursiveDeref(dd, cubex); + FREE(colptr); + FREE(rowind); + return(0); + } + cuddRef(w); + Cudd_RecursiveDeref(dd, cubex); + cubex = w; + u >>= 1; + } + minterm1 = Cudd_addApply(dd, Cudd_addTimes, cubey, cubex); + if (minterm1 == NULL) { + Cudd_RecursiveDeref(dd, cubey); + Cudd_RecursiveDeref(dd, cubex); + FREE(colptr); + FREE(rowind); + return(0); + } + cuddRef(minterm1); + Cudd_RecursiveDeref(dd, cubex); + w = Cudd_addApply(dd, Cudd_addPlus, *E, minterm1); + if (w == NULL) { + Cudd_RecursiveDeref(dd, cubey); + FREE(colptr); + FREE(rowind); + return(0); + } + cuddRef(w); + Cudd_RecursiveDeref(dd, minterm1); + Cudd_RecursiveDeref(dd, *E); + *E = w; + } + Cudd_RecursiveDeref(dd, cubey); + } + FREE(colptr); + FREE(rowind); + + /* Read right-hand sides */ + for (j=0; j=0; nv--) { + if (v & 1) { + w = Cudd_addApply(dd, Cudd_addTimes, cubey, ly[nv]); + } else { + w = Cudd_addApply(dd, Cudd_addTimes, cubey, lyn[nv]); + } + if (w == NULL) { + Cudd_RecursiveDeref(dd, cubey); + return(0); + } + cuddRef(w); + Cudd_RecursiveDeref(dd, cubey); + cubey = w; + v >>= 1; + } + for (i=0; i=0; nv--) { + if (u & 1) { + w = Cudd_addApply(dd, Cudd_addTimes, cubex, lx[nv]); + } else { + w = Cudd_addApply(dd, Cudd_addTimes, cubex, lxn[nv]); + } + if (w == NULL) { + Cudd_RecursiveDeref(dd, cubey); + Cudd_RecursiveDeref(dd, cubex); + return(0); + } + cuddRef(w); + Cudd_RecursiveDeref(dd, cubex); + cubex = w; + u >>= 1; + } + minterm1 = Cudd_addApply(dd, Cudd_addTimes, cubey, cubex); + if (minterm1 == NULL) { + Cudd_RecursiveDeref(dd, cubey); + Cudd_RecursiveDeref(dd, cubex); + return(0); + } + cuddRef(minterm1); + Cudd_RecursiveDeref(dd, cubex); + w = Cudd_addApply(dd, Cudd_addPlus, *E, minterm1); + if (w == NULL) { + Cudd_RecursiveDeref(dd, cubey); + return(0); + } + cuddRef(w); + Cudd_RecursiveDeref(dd, minterm1); + Cudd_RecursiveDeref(dd, *E); + *E = w; + } + Cudd_RecursiveDeref(dd, cubey); + } + + return(1); + +} /* end of Cudd_addHarwell */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddInit.c b/abc_with_bb_support/src/bdd/cudd/cuddInit.c new file mode 100644 index 000000000..14f985b27 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddInit.c @@ -0,0 +1,283 @@ +/**CFile*********************************************************************** + + FileName [cuddInit.c] + + PackageName [cudd] + + Synopsis [Functions to initialize and shut down the DD manager.] + + Description [External procedures included in this module: +
        +
      • Cudd_Init() +
      • Cudd_Quit() +
      + Internal procedures included in this module: +
        +
      • cuddZddInitUniv() +
      • cuddZddFreeUniv() +
      + ] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [ This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#define CUDD_MAIN +#include "cuddInt.h" +#undef CUDD_MAIN + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddInit.c,v 1.1.1.1 2003/02/24 22:23:52 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Creates a new DD manager.] + + Description [Creates a new DD manager, initializes the table, the + basic constants and the projection functions. If maxMemory is 0, + Cudd_Init decides suitable values for the maximum size of the cache + and for the limit for fast unique table growth based on the available + memory. Returns a pointer to the manager if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_Quit] + +******************************************************************************/ +DdManager * +Cudd_Init( + unsigned int numVars /* initial number of BDD variables (i.e., subtables) */, + unsigned int numVarsZ /* initial number of ZDD variables (i.e., subtables) */, + unsigned int numSlots /* initial size of the unique tables */, + unsigned int cacheSize /* initial size of the cache */, + unsigned long maxMemory /* target maximum memory occupation */) +{ + DdManager *unique; + int i,result; + DdNode *one, *zero; + unsigned int maxCacheSize; + unsigned int looseUpTo; + extern void (*MMoutOfMemory)(long); + void (*saveHandler)(long); + + if (maxMemory == 0) { + maxMemory = getSoftDataLimit(); + } + looseUpTo = (unsigned int) ((maxMemory / sizeof(DdNode)) / + DD_MAX_LOOSE_FRACTION); + unique = cuddInitTable(numVars,numVarsZ,numSlots,looseUpTo); + unique->maxmem = (unsigned) maxMemory / 10 * 9; + if (unique == NULL) return(NULL); + maxCacheSize = (unsigned int) ((maxMemory / sizeof(DdCache)) / + DD_MAX_CACHE_FRACTION); + result = cuddInitCache(unique,cacheSize,maxCacheSize); + if (result == 0) return(NULL); + + saveHandler = MMoutOfMemory; + MMoutOfMemory = Cudd_OutOfMem; + unique->stash = ALLOC(char,(maxMemory / DD_STASH_FRACTION) + 4); + MMoutOfMemory = saveHandler; + if (unique->stash == NULL) { + (void) fprintf(unique->err,"Unable to set aside memory\n"); + } + + /* Initialize constants. */ + unique->one = cuddUniqueConst(unique,1.0); + if (unique->one == NULL) return(0); + cuddRef(unique->one); + unique->zero = cuddUniqueConst(unique,0.0); + if (unique->zero == NULL) return(0); + cuddRef(unique->zero); +#ifdef HAVE_IEEE_754 + if (DD_PLUS_INF_VAL != DD_PLUS_INF_VAL * 3 || + DD_PLUS_INF_VAL != DD_PLUS_INF_VAL / 3) { + (void) fprintf(unique->err,"Warning: Crippled infinite values\n"); + (void) fprintf(unique->err,"Recompile without -DHAVE_IEEE_754\n"); + } +#endif + unique->plusinfinity = cuddUniqueConst(unique,DD_PLUS_INF_VAL); + if (unique->plusinfinity == NULL) return(0); + cuddRef(unique->plusinfinity); + unique->minusinfinity = cuddUniqueConst(unique,DD_MINUS_INF_VAL); + if (unique->minusinfinity == NULL) return(0); + cuddRef(unique->minusinfinity); + unique->background = unique->zero; + + /* The logical zero is different from the CUDD_VALUE_TYPE zero! */ + one = unique->one; + zero = Cudd_Not(one); + /* Create the projection functions. */ + unique->vars = ALLOC(DdNodePtr,unique->maxSize); + if (unique->vars == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + for (i = 0; i < unique->size; i++) { + unique->vars[i] = cuddUniqueInter(unique,i,one,zero); + if (unique->vars[i] == NULL) return(0); + cuddRef(unique->vars[i]); + } + + if (unique->sizeZ) + cuddZddInitUniv(unique); + + unique->memused += sizeof(DdNode *) * unique->maxSize; + + return(unique); + +} /* end of Cudd_Init */ + + +/**Function******************************************************************** + + Synopsis [Deletes resources associated with a DD manager.] + + Description [Deletes resources associated with a DD manager and + resets the global statistical counters. (Otherwise, another manaqger + subsequently created would inherit the stats of this one.)] + + SideEffects [None] + + SeeAlso [Cudd_Init] + +******************************************************************************/ +void +Cudd_Quit( + DdManager * unique) +{ + if (unique->stash != NULL) FREE(unique->stash); + cuddFreeTable(unique); + +} /* end of Cudd_Quit */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Initializes the ZDD universe.] + + Description [Initializes the ZDD universe. Returns 1 if successful; 0 + otherwise.] + + SideEffects [None] + + SeeAlso [cuddZddFreeUniv] + +******************************************************************************/ +int +cuddZddInitUniv( + DdManager * zdd) +{ + DdNode *p, *res; + int i; + + zdd->univ = ALLOC(DdNodePtr, zdd->sizeZ); + if (zdd->univ == NULL) { + zdd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + + res = DD_ONE(zdd); + cuddRef(res); + for (i = zdd->sizeZ - 1; i >= 0; i--) { + unsigned int index = zdd->invpermZ[i]; + p = res; + res = cuddUniqueInterZdd(zdd, index, p, p); + if (res == NULL) { + Cudd_RecursiveDerefZdd(zdd,p); + FREE(zdd->univ); + return(0); + } + cuddRef(res); + cuddDeref(p); + zdd->univ[i] = res; + } + +#ifdef DD_VERBOSE + cuddZddP(zdd, zdd->univ[0]); +#endif + + return(1); + +} /* end of cuddZddInitUniv */ + + +/**Function******************************************************************** + + Synopsis [Frees the ZDD universe.] + + Description [Frees the ZDD universe.] + + SideEffects [None] + + SeeAlso [cuddZddInitUniv] + +******************************************************************************/ +void +cuddZddFreeUniv( + DdManager * zdd) +{ + if (zdd->univ) { + Cudd_RecursiveDerefZdd(zdd, zdd->univ[0]); + FREE(zdd->univ); + } + +} /* end of cuddZddFreeUniv */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddInt.h b/abc_with_bb_support/src/bdd/cudd/cuddInt.h new file mode 100644 index 000000000..a0a65b51c --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddInt.h @@ -0,0 +1,1133 @@ +/**CHeaderFile***************************************************************** + + FileName [cuddInt.h] + + PackageName [cudd] + + Synopsis [Internal data structures of the CUDD package.] + + Description [] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + + Revision [$Id: cuddInt.h,v 1.1.1.1 2003/02/24 22:23:52 wjiang Exp $] + +******************************************************************************/ + +#ifndef _CUDDINT +#define _CUDDINT + + +/*---------------------------------------------------------------------------*/ +/* Nested includes */ +/*---------------------------------------------------------------------------*/ + +#ifdef DD_MIS +#include "array.h" +#include "list.h" +#include "st.h" +#include "espresso.h" +#include "node.h" +#ifdef SIS +#include "graph.h" +#include "astg.h" +#endif +#include "network.h" +#endif + +#include +#include "cudd.h" +#include "st.h" + +#if defined(__GNUC__) +# define DD_INLINE __inline__ +# if (__GNUC__ >2 || __GNUC_MINOR__ >=7) +# define DD_UNUSED __attribute__ ((__unused__)) +# else +# define DD_UNUSED +# endif +#else +# if defined(__cplusplus) +# define DD_INLINE inline +# else +# define DD_INLINE +# endif +# define DD_UNUSED +#endif + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define DD_MAXREF ((DdHalfWord) ~0) + +#define DD_DEFAULT_RESIZE 10 /* how many extra variables */ + /* should be added when resizing */ +#define DD_MEM_CHUNK 1022 + +/* These definitions work for CUDD_VALUE_TYPE == double */ +#define DD_ONE_VAL (1.0) +#define DD_ZERO_VAL (0.0) +#define DD_EPSILON (1.0e-12) + +/* The definitions of +/- infinity in terms of HUGE_VAL work on +** the DECstations and on many other combinations of OS/compiler. +*/ +#ifdef HAVE_IEEE_754 +# define DD_PLUS_INF_VAL (HUGE_VAL) +#else +# define DD_PLUS_INF_VAL (10e301) +# define DD_CRI_HI_MARK (10e150) +# define DD_CRI_LO_MARK (-(DD_CRI_HI_MARK)) +#endif +#define DD_MINUS_INF_VAL (-(DD_PLUS_INF_VAL)) + +#define DD_NON_CONSTANT ((DdNode *) 1) /* for Cudd_bddIteConstant */ + +/* Unique table and cache management constants. */ +#define DD_MAX_SUBTABLE_DENSITY 4 /* tells when to resize a subtable */ +/* gc when this percent are dead (measured w.r.t. slots, not keys) +** The first limit (LO) applies normally. The second limit applies when +** the package believes more space for the unique table (i.e., more dead +** nodes) would improve performance, and the unique table is not already +** too large. The third limit applies when memory is low. +*/ +#define DD_GC_FRAC_LO DD_MAX_SUBTABLE_DENSITY * 0.25 +#define DD_GC_FRAC_HI DD_MAX_SUBTABLE_DENSITY * 1.0 +#define DD_GC_FRAC_MIN 0.2 +#define DD_MIN_HIT 30 /* resize cache when hit ratio + above this percentage (default) */ +#define DD_MAX_LOOSE_FRACTION 5 /* 1 / (max fraction of memory used for + unique table in fast growth mode) */ +#define DD_MAX_CACHE_FRACTION 3 /* 1 / (max fraction of memory used for + computed table if resizing enabled) */ +#define DD_STASH_FRACTION 64 /* 1 / (fraction of memory set + aside for emergencies) */ +#define DD_MAX_CACHE_TO_SLOTS_RATIO 4 /* used to limit the cache size */ + +/* Variable ordering default parameter values. */ +#define DD_SIFT_MAX_VAR 1000 +#define DD_SIFT_MAX_SWAPS 2000000 +#define DD_DEFAULT_RECOMB 0 +#define DD_MAX_REORDER_GROWTH 1.2 +#define DD_FIRST_REORDER 4004 /* 4 for the constants */ +#define DD_DYN_RATIO 2 /* when to dynamically reorder */ + +/* Primes for cache hash functions. */ +#define DD_P1 12582917 +#define DD_P2 4256249 +#define DD_P3 741457 +#define DD_P4 1618033999 + +/* Cache tags for 3-operand operators. These tags are stored in the +** least significant bits of the cache operand pointers according to +** the following scheme. The tag consists of two hex digits. Both digits +** must be even, so that they do not interfere with complementation bits. +** The least significant one is stored in Bits 3:1 of the f operand in the +** cache entry. Bit 1 is always 1, so that we can differentiate +** three-operand operations from one- and two-operand operations. +** Therefore, the least significant digit is one of {2,6,a,e}. The most +** significant digit occupies Bits 3:1 of the g operand in the cache +** entry. It can by any even digit between 0 and e. This gives a total +** of 5 bits for the tag proper, which means a maximum of 32 three-operand +** operations. */ +#define DD_ADD_ITE_TAG 0x02 +#define DD_BDD_AND_ABSTRACT_TAG 0x06 +#define DD_BDD_XOR_EXIST_ABSTRACT_TAG 0x0a +#define DD_BDD_ITE_TAG 0x0e +#define DD_ADD_BDD_DO_INTERVAL_TAG 0x22 +#define DD_BDD_CLIPPING_AND_ABSTRACT_UP_TAG 0x26 +#define DD_BDD_CLIPPING_AND_ABSTRACT_DOWN_TAG 0x2a +#define DD_BDD_COMPOSE_RECUR_TAG 0x2e +#define DD_ADD_COMPOSE_RECUR_TAG 0x42 +#define DD_ADD_NON_SIM_COMPOSE_TAG 0x46 +#define DD_EQUIV_DC_TAG 0x4a +#define DD_ZDD_ITE_TAG 0x4e +#define DD_ADD_ITE_CONSTANT_TAG 0x62 +#define DD_ADD_EVAL_CONST_TAG 0x66 +#define DD_BDD_ITE_CONSTANT_TAG 0x6a +#define DD_ADD_OUT_SUM_TAG 0x6e +#define DD_BDD_LEQ_UNLESS_TAG 0x82 +#define DD_ADD_TRIANGLE_TAG 0x86 + +/* Generator constants. */ +#define CUDD_GEN_CUBES 0 +#define CUDD_GEN_NODES 1 +#define CUDD_GEN_ZDD_PATHS 2 +#define CUDD_GEN_EMPTY 0 +#define CUDD_GEN_NONEMPTY 1 + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +struct DdGen { + DdManager *manager; + int type; + int status; + union { + struct { + int *cube; + CUDD_VALUE_TYPE value; + } cubes; + struct { + st_table *visited; + st_generator *stGen; + } nodes; + } gen; + struct { + int sp; + DdNode **stack; + } stack; + DdNode *node; +}; + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/* Hooks in CUDD are functions that the application registers with the +** manager so that they are called at appropriate times. The functions +** are passed the manager as argument; they should return 1 if +** successful and 0 otherwise. +*/ +typedef struct DdHook { /* hook list element */ + int (*f) ARGS((DdManager *, char *, void *)); /* function to be called */ + struct DdHook *next; /* next element in the list */ +} DdHook; + +#if SIZEOF_VOID_P == 8 && SIZEOF_INT == 4 +typedef long ptrint; +typedef unsigned long ptruint; +#else +typedef int ptrint; +typedef unsigned int ptruint; +#endif + +#ifdef __osf__ +#pragma pointer_size save +#pragma pointer_size short +#endif + +typedef DdNode *DdNodePtr; + +/* Generic local cache item. */ +typedef struct DdLocalCacheItem { + DdNode *value; +#ifdef DD_CACHE_PROFILE + ptrint count; +#endif + DdNode *key[1]; +} DdLocalCacheItem; + +/* Local cache. */ +typedef struct DdLocalCache { + DdLocalCacheItem *item; + unsigned int itemsize; + unsigned int keysize; + unsigned int slots; + int shift; + double lookUps; + double minHit; + double hits; + unsigned int maxslots; + DdManager *manager; + struct DdLocalCache *next; +} DdLocalCache; + +/* Generic hash item. */ +typedef struct DdHashItem { + struct DdHashItem *next; + ptrint count; + DdNode *value; + DdNode *key[1]; +} DdHashItem; + +/* Local hash table */ +typedef struct DdHashTable { + unsigned int keysize; + unsigned int itemsize; + DdHashItem **bucket; + DdHashItem *nextFree; + DdHashItem **memoryList; + unsigned int numBuckets; + int shift; + unsigned int size; + unsigned int maxsize; + DdManager *manager; +} DdHashTable; + +typedef struct DdCache { + DdNode *f,*g; /* DDs */ + ptruint h; /* either operator or DD */ + DdNode *data; /* already constructed DD */ +#ifdef DD_CACHE_PROFILE + ptrint count; +#endif +} DdCache; + +typedef struct DdSubtable { /* subtable for one index */ + DdNode **nodelist; /* hash table */ + int shift; /* shift for hash function */ + unsigned int slots; /* size of the hash table */ + unsigned int keys; /* number of nodes stored in this table */ + unsigned int maxKeys; /* slots * DD_MAX_SUBTABLE_DENSITY */ + unsigned int dead; /* number of dead nodes in this table */ + unsigned int next; /* index of next variable in group */ + int bindVar; /* flag to bind this variable to its level */ + /* Fields for lazy sifting. */ + Cudd_VariableType varType; /* variable type (ps, ns, pi) */ + int pairIndex; /* corresponding variable index (ps <-> ns) */ + int varHandled; /* flag: 1 means variable is already handled */ + Cudd_LazyGroupType varToBeGrouped; /* tells what grouping to apply */ +} DdSubtable; + +struct DdManager { /* specialized DD symbol table */ + /* Constants */ + DdNode sentinel; /* for collision lists */ + DdNode *one; /* constant 1 */ + DdNode *zero; /* constant 0 */ + DdNode *plusinfinity; /* plus infinity */ + DdNode *minusinfinity; /* minus infinity */ + DdNode *background; /* background value */ + /* Computed Table */ + DdCache *acache; /* address of allocated memory for cache */ + DdCache *cache; /* the cache-based computed table */ + unsigned int cacheSlots; /* total number of cache entries */ + int cacheShift; /* shift value for cache hash function */ + double cacheMisses; /* number of cache misses (since resizing) */ + double cacheHits; /* number of cache hits (since resizing) */ + double minHit; /* hit percentage above which to resize */ + int cacheSlack; /* slots still available for resizing */ + unsigned int maxCacheHard; /* hard limit for cache size */ + /* Unique Table */ + int size; /* number of unique subtables */ + int sizeZ; /* for ZDD */ + int maxSize; /* max number of subtables before resizing */ + int maxSizeZ; /* for ZDD */ + DdSubtable *subtables; /* array of unique subtables */ + DdSubtable *subtableZ; /* for ZDD */ + DdSubtable constants; /* unique subtable for the constants */ + unsigned int slots; /* total number of hash buckets */ + unsigned int keys; /* total number of BDD and ADD nodes */ + unsigned int keysZ; /* total number of ZDD nodes */ + unsigned int dead; /* total number of dead BDD and ADD nodes */ + unsigned int deadZ; /* total number of dead ZDD nodes */ + unsigned int maxLive; /* maximum number of live nodes */ + unsigned int minDead; /* do not GC if fewer than these dead */ + double gcFrac; /* gc when this fraction is dead */ + int gcEnabled; /* gc is enabled */ + unsigned int looseUpTo; /* slow growth beyond this limit */ + /* (measured w.r.t. slots, not keys) */ + unsigned int initSlots; /* initial size of a subtable */ + DdNode **stack; /* stack for iterative procedures */ + double allocated; /* number of nodes allocated */ + /* (not during reordering) */ + double reclaimed; /* number of nodes brought back from the dead */ + int isolated; /* isolated projection functions */ + int *perm; /* current variable perm. (index to level) */ + int *permZ; /* for ZDD */ + int *invperm; /* current inv. var. perm. (level to index) */ + int *invpermZ; /* for ZDD */ + DdNode **vars; /* projection functions */ + int *map; /* variable map for fast swap */ + DdNode **univ; /* ZDD 1 for each variable */ + int linearSize; /* number of rows and columns of linear */ + long *interact; /* interacting variable matrix */ + long *linear; /* linear transform matrix */ + /* Memory Management */ + DdNode **memoryList; /* memory manager for symbol table */ + DdNode *nextFree; /* list of free nodes */ + char *stash; /* memory reserve */ +#ifndef DD_NO_DEATH_ROW + DdNode **deathRow; /* queue for dereferencing */ + int deathRowDepth; /* number of slots in the queue */ + int nextDead; /* index in the queue */ + unsigned deadMask; /* mask for circular index update */ +#endif + /* General Parameters */ + CUDD_VALUE_TYPE epsilon; /* tolerance on comparisons */ + /* Dynamic Reordering Parameters */ + int reordered; /* flag set at the end of reordering */ + int reorderings; /* number of calls to Cudd_ReduceHeap */ + int siftMaxVar; /* maximum number of vars sifted */ + int siftMaxSwap; /* maximum number of swaps per sifting */ + double maxGrowth; /* maximum growth during reordering */ + double maxGrowthAlt; /* alternate maximum growth for reordering */ + int reordCycle; /* how often to apply alternate threshold */ + int autoDyn; /* automatic dynamic reordering flag (BDD) */ + int autoDynZ; /* automatic dynamic reordering flag (ZDD) */ + Cudd_ReorderingType autoMethod; /* default reordering method */ + Cudd_ReorderingType autoMethodZ; /* default reordering method (ZDD) */ + int realign; /* realign ZDD order after BDD reordering */ + int realignZ; /* realign BDD order after ZDD reordering */ + unsigned int nextDyn; /* reorder if this size is reached */ + unsigned int countDead; /* if 0, count deads to trigger reordering */ + MtrNode *tree; /* Variable group tree (BDD) */ + MtrNode *treeZ; /* Variable group tree (ZDD) */ + Cudd_AggregationType groupcheck; /* Used during group sifting */ + int recomb; /* Used during group sifting */ + int symmviolation; /* Used during group sifting */ + int arcviolation; /* Used during group sifting */ + int populationSize; /* population size for GA */ + int numberXovers; /* number of crossovers for GA */ + DdLocalCache *localCaches; /* local caches currently in existence */ +#ifdef __osf__ +#pragma pointer_size restore +#endif + char *hooks; /* application-specific field (used by vis) */ + DdHook *preGCHook; /* hooks to be called before GC */ + DdHook *postGCHook; /* hooks to be called after GC */ + DdHook *preReorderingHook; /* hooks to be called before reordering */ + DdHook *postReorderingHook; /* hooks to be called after reordering */ + FILE *out; /* stdout for this manager */ + FILE *err; /* stderr for this manager */ +#ifdef __osf__ +#pragma pointer_size save +#pragma pointer_size short +#endif + Cudd_ErrorType errorCode; /* info on last error */ + /* Statistical counters. */ + long memused; /* total memory allocated for the manager */ + long maxmem; /* target maximum memory */ + long maxmemhard; /* hard limit for maximum memory */ + int garbageCollections; /* number of garbage collections */ + long GCTime; /* total time spent in garbage collection */ + long reordTime; /* total time spent in reordering */ + double totCachehits; /* total number of cache hits */ + double totCacheMisses; /* total number of cache misses */ + double cachecollisions; /* number of cache collisions */ + double cacheinserts; /* number of cache insertions */ + double cacheLastInserts; /* insertions at the last cache resizing */ + double cachedeletions; /* number of deletions during garbage coll. */ +#ifdef DD_STATS + double nodesFreed; /* number of nodes returned to the free list */ + double nodesDropped; /* number of nodes killed by dereferencing */ +#endif + unsigned int peakLiveNodes; /* maximum number of live nodes */ +#ifdef DD_UNIQUE_PROFILE + double uniqueLookUps; /* number of unique table lookups */ + double uniqueLinks; /* total distance traveled in coll. chains */ +#endif +#ifdef DD_COUNT + double recursiveCalls; /* number of recursive calls */ +#ifdef DD_STATS + double nextSample; /* when to write next line of stats */ +#endif + double swapSteps; /* number of elementary reordering steps */ +#endif +#ifdef DD_MIS + /* mis/verif compatibility fields */ + array_t *iton; /* maps ids in ddNode to node_t */ + array_t *order; /* copy of order_list */ + lsHandle handle; /* where it is in network BDD list */ + network_t *network; + st_table *local_order; /* for local BDDs */ + int nvars; /* variables used so far */ + int threshold; /* for pseudo var threshold value*/ +#endif +}; + +typedef struct Move { + DdHalfWord x; + DdHalfWord y; + unsigned int flags; + int size; + struct Move *next; +} Move; + +/* Generic level queue item. */ +typedef struct DdQueueItem { + struct DdQueueItem *next; + struct DdQueueItem *cnext; + void *key; +} DdQueueItem; + +/* Level queue. */ +typedef struct DdLevelQueue { + void *first; + DdQueueItem **last; + DdQueueItem *freelist; + DdQueueItem **buckets; + int levels; + int itemsize; + int size; + int maxsize; + int numBuckets; + int shift; +} DdLevelQueue; + +#ifdef __osf__ +#pragma pointer_size restore +#endif + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**Macro*********************************************************************** + + Synopsis [Adds node to the head of the free list.] + + Description [Adds node to the head of the free list. Does not + deallocate memory chunks that become free. This function is also + used by the dynamic reordering functions.] + + SideEffects [None] + + SeeAlso [cuddAllocNode cuddDynamicAllocNode] + +******************************************************************************/ +#define cuddDeallocNode(unique,node) \ + (node)->next = (unique)->nextFree; \ + (unique)->nextFree = node; + + +/**Macro*********************************************************************** + + Synopsis [Increases the reference count of a node, if it is not + saturated.] + + Description [Increases the reference count of a node, if it is not + saturated. This being a macro, it is faster than Cudd_Ref, but it + cannot be used in constructs like cuddRef(a = b()).] + + SideEffects [none] + + SeeAlso [Cudd_Ref] + +******************************************************************************/ +#define cuddRef(n) cuddSatInc(Cudd_Regular(n)->ref) + + +/**Macro*********************************************************************** + + Synopsis [Decreases the reference count of a node, if it is not + saturated.] + + Description [Decreases the reference count of node. It is primarily + used in recursive procedures to decrease the ref count of a result + node before returning it. This accomplishes the goal of removing the + protection applied by a previous cuddRef. This being a macro, it is + faster than Cudd_Deref, but it cannot be used in constructs like + cuddDeref(a = b()).] + + SideEffects [none] + + SeeAlso [Cudd_Deref] + +******************************************************************************/ +#define cuddDeref(n) cuddSatDec(Cudd_Regular(n)->ref) + + +/**Macro*********************************************************************** + + Synopsis [Returns 1 if the node is a constant node.] + + Description [Returns 1 if the node is a constant node (rather than an + internal node). All constant nodes have the same index + (CUDD_CONST_INDEX). The pointer passed to cuddIsConstant must be regular.] + + SideEffects [none] + + SeeAlso [Cudd_IsConstant] + +******************************************************************************/ +#define cuddIsConstant(node) ((node)->index == CUDD_CONST_INDEX) + + +/**Macro*********************************************************************** + + Synopsis [Returns the then child of an internal node.] + + Description [Returns the then child of an internal node. If + node is a constant node, the result is unpredictable. + The pointer passed to cuddT must be regular.] + + SideEffects [none] + + SeeAlso [Cudd_T] + +******************************************************************************/ +#define cuddT(node) ((node)->type.kids.T) + + +/**Macro*********************************************************************** + + Synopsis [Returns the else child of an internal node.] + + Description [Returns the else child of an internal node. If + node is a constant node, the result is unpredictable. + The pointer passed to cuddE must be regular.] + + SideEffects [none] + + SeeAlso [Cudd_E] + +******************************************************************************/ +#define cuddE(node) ((node)->type.kids.E) + + +/**Macro*********************************************************************** + + Synopsis [Returns the value of a constant node.] + + Description [Returns the value of a constant node. If + node is an internal node, the result is unpredictable. + The pointer passed to cuddV must be regular.] + + SideEffects [none] + + SeeAlso [Cudd_V] + +******************************************************************************/ +#define cuddV(node) ((node)->type.value) + + +/**Macro*********************************************************************** + + Synopsis [Finds the current position of variable index in the + order.] + + Description [Finds the current position of variable index in the + order. This macro duplicates the functionality of Cudd_ReadPerm, + but it does not check for out-of-bounds indices and it is more + efficient.] + + SideEffects [none] + + SeeAlso [Cudd_ReadPerm] + +******************************************************************************/ +#define cuddI(dd,index) (((index)==CUDD_CONST_INDEX)?(int)(index):(dd)->perm[(index)]) + + +/**Macro*********************************************************************** + + Synopsis [Finds the current position of ZDD variable index in the + order.] + + Description [Finds the current position of ZDD variable index in the + order. This macro duplicates the functionality of Cudd_ReadPermZdd, + but it does not check for out-of-bounds indices and it is more + efficient.] + + SideEffects [none] + + SeeAlso [Cudd_ReadPermZdd] + +******************************************************************************/ +#define cuddIZ(dd,index) (((index)==CUDD_CONST_INDEX)?(int)(index):(dd)->permZ[(index)]) + + +/**Macro*********************************************************************** + + Synopsis [Hash function for the unique table.] + + Description [] + + SideEffects [none] + + SeeAlso [ddCHash ddCHash2] + +******************************************************************************/ +#if SIZEOF_VOID_P == 8 && SIZEOF_INT == 4 +#define ddHash(f,g,s) \ +((((unsigned)(unsigned long)(f) * DD_P1 + \ + (unsigned)(unsigned long)(g)) * DD_P2) >> (s)) +#else +#define ddHash(f,g,s) \ +((((unsigned)(f) * DD_P1 + (unsigned)(g)) * DD_P2) >> (s)) +#endif + + +/**Macro*********************************************************************** + + Synopsis [Hash function for the cache.] + + Description [] + + SideEffects [none] + + SeeAlso [ddHash ddCHash2] + +******************************************************************************/ +#if SIZEOF_VOID_P == 8 && SIZEOF_INT == 4 +#define ddCHash(o,f,g,h,s) \ +((((((unsigned)(unsigned long)(f) + (unsigned)(unsigned long)(o)) * DD_P1 + \ + (unsigned)(unsigned long)(g)) * DD_P2 + \ + (unsigned)(unsigned long)(h)) * DD_P3) >> (s)) +#else +#define ddCHash(o,f,g,h,s) \ +((((((unsigned)(f) + (unsigned)(o)) * DD_P1 + (unsigned)(g)) * DD_P2 + \ + (unsigned)(h)) * DD_P3) >> (s)) +#endif + + +/**Macro*********************************************************************** + + Synopsis [Hash function for the cache for functions with two + operands.] + + Description [] + + SideEffects [none] + + SeeAlso [ddHash ddCHash] + +******************************************************************************/ +#if SIZEOF_VOID_P == 8 && SIZEOF_INT == 4 +#define ddCHash2(o,f,g,s) \ +(((((unsigned)(unsigned long)(f) + (unsigned)(unsigned long)(o)) * DD_P1 + \ + (unsigned)(unsigned long)(g)) * DD_P2) >> (s)) +#else +#define ddCHash2(o,f,g,s) \ +(((((unsigned)(f) + (unsigned)(o)) * DD_P1 + (unsigned)(g)) * DD_P2) >> (s)) +#endif + + +/**Macro*********************************************************************** + + Synopsis [Clears the 4 least significant bits of a pointer.] + + Description [] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +#define cuddClean(p) ((DdNode *)((ptruint)(p) & ~0xf)) + + +/**Macro*********************************************************************** + + Synopsis [Computes the minimum of two numbers.] + + Description [] + + SideEffects [none] + + SeeAlso [ddMax] + +******************************************************************************/ +#define ddMin(x,y) (((y) < (x)) ? (y) : (x)) + + +/**Macro*********************************************************************** + + Synopsis [Computes the maximum of two numbers.] + + Description [] + + SideEffects [none] + + SeeAlso [ddMin] + +******************************************************************************/ +#define ddMax(x,y) (((y) > (x)) ? (y) : (x)) + + +/**Macro*********************************************************************** + + Synopsis [Computes the absolute value of a number.] + + Description [] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +#define ddAbs(x) (((x)<0) ? -(x) : (x)) + + +/**Macro*********************************************************************** + + Synopsis [Returns 1 if the absolute value of the difference of the two + arguments x and y is less than e.] + + Description [] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +#define ddEqualVal(x,y,e) (ddAbs((x)-(y))<(e)) + + +/**Macro*********************************************************************** + + Synopsis [Saturating increment operator.] + + Description [] + + SideEffects [none] + + SeeAlso [cuddSatDec] + +******************************************************************************/ +#if SIZEOF_VOID_P == 8 && SIZEOF_INT == 4 +#define cuddSatInc(x) ((x)++) +#else +#define cuddSatInc(x) ((x) += (x) != (DdHalfWord)DD_MAXREF) +#endif + + +/**Macro*********************************************************************** + + Synopsis [Saturating decrement operator.] + + Description [] + + SideEffects [none] + + SeeAlso [cuddSatInc] + +******************************************************************************/ +#if SIZEOF_VOID_P == 8 && SIZEOF_INT == 4 +#define cuddSatDec(x) ((x)--) +#else +#define cuddSatDec(x) ((x) -= (x) != (DdHalfWord)DD_MAXREF) +#endif + + +/**Macro*********************************************************************** + + Synopsis [Returns the constant 1 node.] + + Description [] + + SideEffects [none] + + SeeAlso [DD_ZERO DD_PLUS_INFINITY DD_MINUS_INFINITY] + +******************************************************************************/ +#define DD_ONE(dd) ((dd)->one) + + +/**Macro*********************************************************************** + + Synopsis [Returns the arithmetic 0 constant node.] + + Description [Returns the arithmetic 0 constant node. This is different + from the logical zero. The latter is obtained by + Cudd_Not(DD_ONE(dd)).] + + SideEffects [none] + + SeeAlso [DD_ONE Cudd_Not DD_PLUS_INFINITY DD_MINUS_INFINITY] + +******************************************************************************/ +#define DD_ZERO(dd) ((dd)->zero) + + +/**Macro*********************************************************************** + + Synopsis [Returns the plus infinity constant node.] + + Description [] + + SideEffects [none] + + SeeAlso [DD_ONE DD_ZERO DD_MINUS_INFINITY] + +******************************************************************************/ +#define DD_PLUS_INFINITY(dd) ((dd)->plusinfinity) + + +/**Macro*********************************************************************** + + Synopsis [Returns the minus infinity constant node.] + + Description [] + + SideEffects [none] + + SeeAlso [DD_ONE DD_ZERO DD_PLUS_INFINITY] + +******************************************************************************/ +#define DD_MINUS_INFINITY(dd) ((dd)->minusinfinity) + + +/**Macro*********************************************************************** + + Synopsis [Enforces DD_MINUS_INF_VAL <= x <= DD_PLUS_INF_VAL.] + + Description [Enforces DD_MINUS_INF_VAL <= x <= DD_PLUS_INF_VAL. + Furthermore, if x <= DD_MINUS_INF_VAL/2, x is set to + DD_MINUS_INF_VAL. Similarly, if DD_PLUS_INF_VAL/2 <= x, x is set to + DD_PLUS_INF_VAL. Normally this macro is a NOOP. However, if + HAVE_IEEE_754 is not defined, it makes sure that a value does not + get larger than infinity in absolute value, and once it gets to + infinity, stays there. If the value overflows before this macro is + applied, no recovery is possible.] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +#ifdef HAVE_IEEE_754 +#define cuddAdjust(x) +#else +#define cuddAdjust(x) ((x) = ((x) >= DD_CRI_HI_MARK) ? DD_PLUS_INF_VAL : (((x) <= DD_CRI_LO_MARK) ? DD_MINUS_INF_VAL : (x))) +#endif + + +/**Macro*********************************************************************** + + Synopsis [Extract the least significant digit of a double digit.] + + Description [Extract the least significant digit of a double digit. Used + in the manipulation of arbitrary precision integers.] + + SideEffects [None] + + SeeAlso [DD_MSDIGIT] + +******************************************************************************/ +#define DD_LSDIGIT(x) ((x) & DD_APA_MASK) + + +/**Macro*********************************************************************** + + Synopsis [Extract the most significant digit of a double digit.] + + Description [Extract the most significant digit of a double digit. Used + in the manipulation of arbitrary precision integers.] + + SideEffects [None] + + SeeAlso [DD_LSDIGIT] + +******************************************************************************/ +#define DD_MSDIGIT(x) ((x) >> DD_APA_BITS) + + +/**Macro*********************************************************************** + + Synopsis [Outputs a line of stats.] + + Description [Outputs a line of stats if DD_COUNT and DD_STATS are + defined. Increments the number of recursive calls if DD_COUNT is + defined.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +#ifdef DD_COUNT +#ifdef DD_STATS +#define statLine(dd) dd->recursiveCalls++; \ +if (dd->recursiveCalls == dd->nextSample) {(void) fprintf(dd->err, \ +"@%.0f: %u nodes %u live %.0f dropped %.0f reclaimed\n", dd->recursiveCalls, \ +dd->keys, dd->keys - dd->dead, dd->nodesDropped, dd->reclaimed); \ +dd->nextSample += 250000;} +#else +#define statLine(dd) dd->recursiveCalls++; +#endif +#else +#define statLine(dd) +#endif + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Function prototypes */ +/*---------------------------------------------------------------------------*/ + +EXTERN DdNode * cuddAddExistAbstractRecur ARGS((DdManager *manager, DdNode *f, DdNode *cube)); +EXTERN DdNode * cuddAddUnivAbstractRecur ARGS((DdManager *manager, DdNode *f, DdNode *cube)); +EXTERN DdNode * cuddAddOrAbstractRecur ARGS((DdManager *manager, DdNode *f, DdNode *cube)); +EXTERN DdNode * cuddAddApplyRecur ARGS((DdManager *dd, DdNode * (*)(DdManager *, DdNode **, DdNode **), DdNode *f, DdNode *g)); +EXTERN DdNode * cuddAddMonadicApplyRecur ARGS((DdManager * dd, DdNode * (*op)(DdManager *, DdNode *), DdNode * f)); +EXTERN DdNode * cuddAddScalarInverseRecur ARGS((DdManager *dd, DdNode *f, DdNode *epsilon)); +EXTERN DdNode * cuddAddIteRecur ARGS((DdManager *dd, DdNode *f, DdNode *g, DdNode *h)); +EXTERN DdNode * cuddAddCmplRecur ARGS((DdManager *dd, DdNode *f)); +EXTERN DdNode * cuddAddNegateRecur ARGS((DdManager *dd, DdNode *f)); +EXTERN DdNode * cuddAddRoundOffRecur ARGS((DdManager *dd, DdNode *f, double trunc)); +EXTERN DdNode * cuddUnderApprox ARGS((DdManager *dd, DdNode *f, int numVars, int threshold, int safe, double quality)); +EXTERN DdNode * cuddRemapUnderApprox ARGS((DdManager *dd, DdNode *f, int numVars, int threshold, double quality)); +EXTERN DdNode * cuddBiasedUnderApprox ARGS((DdManager *dd, DdNode *f, DdNode *b, int numVars, int threshold, double quality1, double quality0)); +EXTERN DdNode * cuddBddAndAbstractRecur ARGS((DdManager *manager, DdNode *f, DdNode *g, DdNode *cube)); +EXTERN int cuddAnnealing ARGS((DdManager *table, int lower, int upper)); +EXTERN DdNode * cuddBddExistAbstractRecur ARGS((DdManager *manager, DdNode *f, DdNode *cube)); +EXTERN DdNode * cuddBddXorExistAbstractRecur ARGS((DdManager *manager, DdNode *f, DdNode *g, DdNode *cube)); +EXTERN DdNode * cuddBddBooleanDiffRecur ARGS((DdManager *manager, DdNode *f, DdNode *var)); +EXTERN DdNode * cuddBddIteRecur ARGS((DdManager *dd, DdNode *f, DdNode *g, DdNode *h)); +EXTERN DdNode * cuddBddIntersectRecur ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * cuddBddAndRecur ARGS((DdManager *manager, DdNode *f, DdNode *g)); +EXTERN DdNode * cuddBddXorRecur ARGS((DdManager *manager, DdNode *f, DdNode *g)); +EXTERN DdNode * cuddBddTransfer ARGS((DdManager *ddS, DdManager *ddD, DdNode *f)); +EXTERN DdNode * cuddAddBddDoPattern ARGS((DdManager *dd, DdNode *f)); +EXTERN int cuddInitCache ARGS((DdManager *unique, unsigned int cacheSize, unsigned int maxCacheSize)); +EXTERN void cuddCacheInsert ARGS((DdManager *table, ptruint op, DdNode *f, DdNode *g, DdNode *h, DdNode *data)); +EXTERN void cuddCacheInsert2 ARGS((DdManager *table, DdNode * (*)(DdManager *, DdNode *, DdNode *), DdNode *f, DdNode *g, DdNode *data)); +EXTERN void cuddCacheInsert1 ARGS((DdManager *table, DdNode * (*)(DdManager *, DdNode *), DdNode *f, DdNode *data)); +EXTERN DdNode * cuddCacheLookup ARGS((DdManager *table, ptruint op, DdNode *f, DdNode *g, DdNode *h)); +EXTERN DdNode * cuddCacheLookupZdd ARGS((DdManager *table, ptruint op, DdNode *f, DdNode *g, DdNode *h)); +EXTERN DdNode * cuddCacheLookup2 ARGS((DdManager *table, DdNode * (*)(DdManager *, DdNode *, DdNode *), DdNode *f, DdNode *g)); +EXTERN DdNode * cuddCacheLookup1 ARGS((DdManager *table, DdNode * (*)(DdManager *, DdNode *), DdNode *f)); +EXTERN DdNode * cuddCacheLookup2Zdd ARGS((DdManager *table, DdNode * (*)(DdManager *, DdNode *, DdNode *), DdNode *f, DdNode *g)); +EXTERN DdNode * cuddCacheLookup1Zdd ARGS((DdManager *table, DdNode * (*)(DdManager *, DdNode *), DdNode *f)); +EXTERN DdNode * cuddConstantLookup ARGS((DdManager *table, ptruint op, DdNode *f, DdNode *g, DdNode *h)); +EXTERN int cuddCacheProfile ARGS((DdManager *table, FILE *fp)); +EXTERN void cuddCacheResize ARGS((DdManager *table)); +EXTERN void cuddCacheFlush ARGS((DdManager *table)); +EXTERN int cuddComputeFloorLog2 ARGS((unsigned int value)); +EXTERN int cuddHeapProfile ARGS((DdManager *dd)); +EXTERN void cuddPrintNode ARGS((DdNode *f, FILE *fp)); +EXTERN void cuddPrintVarGroups ARGS((DdManager * dd, MtrNode * root, int zdd, int silent)); +EXTERN DdNode * cuddBddClippingAnd ARGS((DdManager *dd, DdNode *f, DdNode *g, int maxDepth, int direction)); +EXTERN DdNode * cuddBddClippingAndAbstract ARGS((DdManager *dd, DdNode *f, DdNode *g, DdNode *cube, int maxDepth, int direction)); +EXTERN void cuddGetBranches ARGS((DdNode *g, DdNode **g1, DdNode **g0)); +EXTERN int cuddCheckCube ARGS((DdManager *dd, DdNode *g)); +EXTERN DdNode * cuddCofactorRecur ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * cuddBddComposeRecur ARGS((DdManager *dd, DdNode *f, DdNode *g, DdNode *proj)); +EXTERN DdNode * cuddAddComposeRecur ARGS((DdManager *dd, DdNode *f, DdNode *g, DdNode *proj)); +EXTERN int cuddExact ARGS((DdManager *table, int lower, int upper)); +EXTERN DdNode * cuddBddConstrainRecur ARGS((DdManager *dd, DdNode *f, DdNode *c)); +EXTERN DdNode * cuddBddRestrictRecur ARGS((DdManager *dd, DdNode *f, DdNode *c)); +EXTERN DdNode * cuddAddConstrainRecur ARGS((DdManager *dd, DdNode *f, DdNode *c)); +EXTERN DdNode * cuddAddRestrictRecur ARGS((DdManager *dd, DdNode *f, DdNode *c)); +EXTERN DdNode * cuddBddLICompaction ARGS((DdManager *dd, DdNode *f, DdNode *c)); +EXTERN int cuddGa ARGS((DdManager *table, int lower, int upper)); +EXTERN int cuddTreeSifting ARGS((DdManager *table, Cudd_ReorderingType method)); +EXTERN int cuddZddInitUniv ARGS((DdManager *zdd)); +EXTERN void cuddZddFreeUniv ARGS((DdManager *zdd)); +EXTERN void cuddSetInteract ARGS((DdManager *table, int x, int y)); +EXTERN int cuddTestInteract ARGS((DdManager *table, int x, int y)); +EXTERN int cuddInitInteract ARGS((DdManager *table)); +EXTERN DdLocalCache * cuddLocalCacheInit ARGS((DdManager *manager, unsigned int keySize, unsigned int cacheSize, unsigned int maxCacheSize)); +EXTERN void cuddLocalCacheQuit ARGS((DdLocalCache *cache)); +EXTERN void cuddLocalCacheInsert ARGS((DdLocalCache *cache, DdNodePtr *key, DdNode *value)); +EXTERN DdNode * cuddLocalCacheLookup ARGS((DdLocalCache *cache, DdNodePtr *key)); +EXTERN void cuddLocalCacheClearDead ARGS((DdManager *manager)); +EXTERN int cuddIsInDeathRow ARGS((DdManager *dd, DdNode *f)); +EXTERN int cuddTimesInDeathRow ARGS((DdManager *dd, DdNode *f)); +EXTERN void cuddLocalCacheClearAll ARGS((DdManager *manager)); +#ifdef DD_CACHE_PROFILE +EXTERN int cuddLocalCacheProfile ARGS((DdLocalCache *cache)); +#endif +EXTERN DdHashTable * cuddHashTableInit ARGS((DdManager *manager, unsigned int keySize, unsigned int initSize)); +EXTERN void cuddHashTableQuit ARGS((DdHashTable *hash)); +EXTERN int cuddHashTableInsert ARGS((DdHashTable *hash, DdNodePtr *key, DdNode *value, ptrint count)); +EXTERN DdNode * cuddHashTableLookup ARGS((DdHashTable *hash, DdNodePtr *key)); +EXTERN int cuddHashTableInsert1 ARGS((DdHashTable *hash, DdNode *f, DdNode *value, ptrint count)); +EXTERN DdNode * cuddHashTableLookup1 ARGS((DdHashTable *hash, DdNode *f)); +EXTERN int cuddHashTableInsert2 ARGS((DdHashTable *hash, DdNode *f, DdNode *g, DdNode *value, ptrint count)); +EXTERN DdNode * cuddHashTableLookup2 ARGS((DdHashTable *hash, DdNode *f, DdNode *g)); +EXTERN int cuddHashTableInsert3 ARGS((DdHashTable *hash, DdNode *f, DdNode *g, DdNode *h, DdNode *value, ptrint count)); +EXTERN DdNode * cuddHashTableLookup3 ARGS((DdHashTable *hash, DdNode *f, DdNode *g, DdNode *h)); +EXTERN DdLevelQueue * cuddLevelQueueInit ARGS((int levels, int itemSize, int numBuckets)); +EXTERN void cuddLevelQueueQuit ARGS((DdLevelQueue *queue)); +EXTERN void * cuddLevelQueueEnqueue ARGS((DdLevelQueue *queue, void *key, int level)); +EXTERN void cuddLevelQueueDequeue ARGS((DdLevelQueue *queue, int level)); +EXTERN int cuddLinearAndSifting ARGS((DdManager *table, int lower, int upper)); +EXTERN DdNode * cuddBddLiteralSetIntersectionRecur ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * cuddCProjectionRecur ARGS((DdManager *dd, DdNode *R, DdNode *Y, DdNode *Ysupp)); +EXTERN DdNode * cuddBddClosestCube ARGS((DdManager *dd, DdNode *f, DdNode *g, CUDD_VALUE_TYPE bound)); +EXTERN void cuddReclaim ARGS((DdManager *table, DdNode *n)); +EXTERN void cuddReclaimZdd ARGS((DdManager *table, DdNode *n)); +EXTERN void cuddClearDeathRow ARGS((DdManager *table)); +EXTERN void cuddShrinkDeathRow ARGS((DdManager *table)); +EXTERN DdNode * cuddDynamicAllocNode ARGS((DdManager *table)); +EXTERN int cuddSifting ARGS((DdManager *table, int lower, int upper)); +EXTERN int cuddSwapping ARGS((DdManager *table, int lower, int upper, Cudd_ReorderingType heuristic)); +EXTERN int cuddNextHigh ARGS((DdManager *table, int x)); +EXTERN int cuddNextLow ARGS((DdManager *table, int x)); +EXTERN int cuddSwapInPlace ARGS((DdManager *table, int x, int y)); +EXTERN int cuddBddAlignToZdd ARGS((DdManager *table)); +EXTERN DdNode * cuddBddMakePrime ARGS((DdManager *dd, DdNode *cube, DdNode *f)); +EXTERN DdNode * cuddSolveEqnRecur ARGS((DdManager *bdd, DdNode *F, DdNode *Y, DdNode **G, int n, int *yIndex, int i)); +EXTERN DdNode * cuddVerifySol ARGS((DdManager *bdd, DdNode *F, DdNode **G, int *yIndex, int n)); +#ifdef ST_INCLUDED +EXTERN DdNode* cuddSplitSetRecur ARGS((DdManager *manager, st_table *mtable, int *varSeen, DdNode *p, double n, double max, int index)); +#endif +EXTERN DdNode * cuddSubsetHeavyBranch ARGS((DdManager *dd, DdNode *f, int numVars, int threshold)); +EXTERN DdNode * cuddSubsetShortPaths ARGS((DdManager *dd, DdNode *f, int numVars, int threshold, int hardlimit)); +EXTERN int cuddSymmCheck ARGS((DdManager *table, int x, int y)); +EXTERN int cuddSymmSifting ARGS((DdManager *table, int lower, int upper)); +EXTERN int cuddSymmSiftingConv ARGS((DdManager *table, int lower, int upper)); +EXTERN DdNode * cuddAllocNode ARGS((DdManager *unique)); +EXTERN DdManager * cuddInitTable ARGS((unsigned int numVars, unsigned int numVarsZ, unsigned int numSlots, unsigned int looseUpTo)); +EXTERN void cuddFreeTable ARGS((DdManager *unique)); +EXTERN int cuddGarbageCollect ARGS((DdManager *unique, int clearCache)); +EXTERN int cuddGarbageCollectZdd ARGS((DdManager *unique, int clearCache)); +EXTERN DdNode * cuddZddGetNode ARGS((DdManager *zdd, int id, DdNode *T, DdNode *E)); +EXTERN DdNode * cuddZddGetNodeIVO ARGS((DdManager *dd, int index, DdNode *g, DdNode *h)); +EXTERN DdNode * cuddUniqueInter ARGS((DdManager *unique, int index, DdNode *T, DdNode *E)); +EXTERN DdNode * cuddUniqueInterIVO ARGS((DdManager *unique, int index, DdNode *T, DdNode *E)); +EXTERN DdNode * cuddUniqueInterZdd ARGS((DdManager *unique, int index, DdNode *T, DdNode *E)); +EXTERN DdNode * cuddUniqueConst ARGS((DdManager *unique, CUDD_VALUE_TYPE value)); +EXTERN void cuddRehash ARGS((DdManager *unique, int i)); +EXTERN void cuddShrinkSubtable ARGS((DdManager *unique, int i)); +EXTERN int cuddInsertSubtables ARGS((DdManager *unique, int n, int level)); +EXTERN int cuddDestroySubtables ARGS((DdManager *unique, int n)); +EXTERN int cuddResizeTableZdd ARGS((DdManager *unique, int index)); +EXTERN void cuddSlowTableGrowth ARGS((DdManager *unique)); +EXTERN int cuddP ARGS((DdManager *dd, DdNode *f)); +#ifdef ST_INCLUDED +EXTERN enum st_retval cuddStCountfree ARGS((char *key, char *value, char *arg)); +EXTERN int cuddCollectNodes ARGS((DdNode *f, st_table *visited)); +#endif +EXTERN int cuddWindowReorder ARGS((DdManager *table, int low, int high, Cudd_ReorderingType submethod)); +EXTERN DdNode * cuddZddProduct ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * cuddZddUnateProduct ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * cuddZddWeakDiv ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * cuddZddWeakDivF ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * cuddZddDivide ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN DdNode * cuddZddDivideF ARGS((DdManager *dd, DdNode *f, DdNode *g)); +EXTERN int cuddZddGetCofactors3 ARGS((DdManager *dd, DdNode *f, int v, DdNode **f1, DdNode **f0, DdNode **fd)); +EXTERN int cuddZddGetCofactors2 ARGS((DdManager *dd, DdNode *f, int v, DdNode **f1, DdNode **f0)); +EXTERN DdNode * cuddZddComplement ARGS((DdManager *dd, DdNode *node)); +EXTERN int cuddZddGetPosVarIndex(DdManager * dd, int index); +EXTERN int cuddZddGetNegVarIndex(DdManager * dd, int index); +EXTERN int cuddZddGetPosVarLevel(DdManager * dd, int index); +EXTERN int cuddZddGetNegVarLevel(DdManager * dd, int index); +EXTERN int cuddZddTreeSifting ARGS((DdManager *table, Cudd_ReorderingType method)); +EXTERN DdNode * cuddZddIsop ARGS((DdManager *dd, DdNode *L, DdNode *U, DdNode **zdd_I)); +EXTERN DdNode * cuddBddIsop ARGS((DdManager *dd, DdNode *L, DdNode *U)); +EXTERN DdNode * cuddMakeBddFromZddCover ARGS((DdManager *dd, DdNode *node)); +EXTERN int cuddZddLinearSifting ARGS((DdManager *table, int lower, int upper)); +EXTERN int cuddZddAlignToBdd ARGS((DdManager *table)); +EXTERN int cuddZddNextHigh ARGS((DdManager *table, int x)); +EXTERN int cuddZddNextLow ARGS((DdManager *table, int x)); +EXTERN int cuddZddUniqueCompare ARGS((int *ptr_x, int *ptr_y)); +EXTERN int cuddZddSwapInPlace ARGS((DdManager *table, int x, int y)); +EXTERN int cuddZddSwapping ARGS((DdManager *table, int lower, int upper, Cudd_ReorderingType heuristic)); +EXTERN int cuddZddSifting ARGS((DdManager *table, int lower, int upper)); +EXTERN DdNode * cuddZddIte ARGS((DdManager *dd, DdNode *f, DdNode *g, DdNode *h)); +EXTERN DdNode * cuddZddUnion ARGS((DdManager *zdd, DdNode *P, DdNode *Q)); +EXTERN DdNode * cuddZddIntersect ARGS((DdManager *zdd, DdNode *P, DdNode *Q)); +EXTERN DdNode * cuddZddDiff ARGS((DdManager *zdd, DdNode *P, DdNode *Q)); +EXTERN DdNode * cuddZddChangeAux ARGS((DdManager *zdd, DdNode *P, DdNode *zvar)); +EXTERN DdNode * cuddZddSubset1 ARGS((DdManager *dd, DdNode *P, int var)); +EXTERN DdNode * cuddZddSubset0 ARGS((DdManager *dd, DdNode *P, int var)); +EXTERN DdNode * cuddZddChange ARGS((DdManager *dd, DdNode *P, int var)); +EXTERN int cuddZddSymmCheck ARGS((DdManager *table, int x, int y)); +EXTERN int cuddZddSymmSifting ARGS((DdManager *table, int lower, int upper)); +EXTERN int cuddZddSymmSiftingConv ARGS((DdManager *table, int lower, int upper)); +EXTERN int cuddZddP ARGS((DdManager *zdd, DdNode *f)); + +/**AutomaticEnd***************************************************************/ + +#endif /* _CUDDINT */ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddInteract.c b/abc_with_bb_support/src/bdd/cudd/cuddInteract.c new file mode 100644 index 000000000..3c1c15722 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddInteract.c @@ -0,0 +1,402 @@ +/**CFile*********************************************************************** + + FileName [cuddInteract.c] + + PackageName [cudd] + + Synopsis [Functions to manipulate the variable interaction matrix.] + + Description [Internal procedures included in this file: +
        +
      • cuddSetInteract() +
      • cuddTestInteract() +
      • cuddInitInteract() +
      + Static procedures included in this file: +
        +
      • ddSuppInteract() +
      • ddClearLocal() +
      • ddUpdateInteract() +
      • ddClearGlobal() +
      + The interaction matrix tells whether two variables are + both in the support of some function of the DD. The main use of the + interaction matrix is in the in-place swapping. Indeed, if two + variables do not interact, there is no arc connecting the two layers; + therefore, the swap can be performed in constant time, without + scanning the subtables. Another use of the interaction matrix is in + the computation of the lower bounds for sifting. Finally, the + interaction matrix can be used to speed up aggregation checks in + symmetric and group sifting.

      + The computation of the interaction matrix is done with a series of + depth-first searches. The searches start from those nodes that have + only external references. The matrix is stored as a packed array of bits; + since it is symmetric, only the upper triangle is kept in memory. + As a final remark, we note that there may be variables that do + intercat, but that for a given variable order have no arc connecting + their layers when they are adjacent.] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [ This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#if SIZEOF_LONG == 8 +#define BPL 64 +#define LOGBPL 6 +#else +#define BPL 32 +#define LOGBPL 5 +#endif + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddInteract.c,v 1.1.1.1 2003/02/24 22:23:52 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static void ddSuppInteract ARGS((DdNode *f, int *support)); +static void ddClearLocal ARGS((DdNode *f)); +static void ddUpdateInteract ARGS((DdManager *table, int *support)); +static void ddClearGlobal ARGS((DdManager *table)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Set interaction matrix entries.] + + Description [Given a pair of variables 0 <= x < y < table->size, + sets the corresponding bit of the interaction matrix to 1.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +cuddSetInteract( + DdManager * table, + int x, + int y) +{ + int posn, word, bit; + +#ifdef DD_DEBUG + assert(x < y); + assert(y < table->size); + assert(x >= 0); +#endif + + posn = ((((table->size << 1) - x - 3) * x) >> 1) + y - 1; + word = posn >> LOGBPL; + bit = posn & (BPL-1); + table->interact[word] |= 1L << bit; + +} /* end of cuddSetInteract */ + + +/**Function******************************************************************** + + Synopsis [Test interaction matrix entries.] + + Description [Given a pair of variables 0 <= x < y < table->size, + tests whether the corresponding bit of the interaction matrix is 1. + Returns the value of the bit.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddTestInteract( + DdManager * table, + int x, + int y) +{ + int posn, word, bit, result; + + if (x > y) { + int tmp = x; + x = y; + y = tmp; + } +#ifdef DD_DEBUG + assert(x < y); + assert(y < table->size); + assert(x >= 0); +#endif + + posn = ((((table->size << 1) - x - 3) * x) >> 1) + y - 1; + word = posn >> LOGBPL; + bit = posn & (BPL-1); + result = (table->interact[word] >> bit) & 1L; + return(result); + +} /* end of cuddTestInteract */ + + +/**Function******************************************************************** + + Synopsis [Initializes the interaction matrix.] + + Description [Initializes the interaction matrix. The interaction + matrix is implemented as a bit vector storing the upper triangle of + the symmetric interaction matrix. The bit vector is kept in an array + of long integers. The computation is based on a series of depth-first + searches, one for each root of the DAG. Two flags are needed: The + local visited flag uses the LSB of the then pointer. The global + visited flag uses the LSB of the next pointer. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddInitInteract( + DdManager * table) +{ + int i,j,k; + int words; + long *interact; + int *support; + DdNode *f; + DdNode *sentinel = &(table->sentinel); + DdNodePtr *nodelist; + int slots; + int n = table->size; + + words = ((n * (n-1)) >> (1 + LOGBPL)) + 1; + table->interact = interact = ALLOC(long,words); + if (interact == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (i = 0; i < words; i++) { + interact[i] = 0; + } + + support = ALLOC(int,n); + if (support == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + FREE(interact); + return(0); + } + + for (i = 0; i < n; i++) { + nodelist = table->subtables[i].nodelist; + slots = table->subtables[i].slots; + for (j = 0; j < slots; j++) { + f = nodelist[j]; + while (f != sentinel) { + /* A node is a root of the DAG if it cannot be + ** reached by nodes above it. If a node was never + ** reached during the previous depth-first searches, + ** then it is a root, and we start a new depth-first + ** search from it. + */ + if (!Cudd_IsComplement(f->next)) { + for (k = 0; k < n; k++) { + support[k] = 0; + } + ddSuppInteract(f,support); + ddClearLocal(f); + ddUpdateInteract(table,support); + } + f = Cudd_Regular(f->next); + } + } + } + ddClearGlobal(table); + + FREE(support); + return(1); + +} /* end of cuddInitInteract */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Find the support of f.] + + Description [Performs a DFS from f. Uses the LSB of the then pointer + as visited flag.] + + SideEffects [Accumulates in support the variables on which f depends.] + + SeeAlso [] + +******************************************************************************/ +static void +ddSuppInteract( + DdNode * f, + int * support) +{ + if (cuddIsConstant(f) || Cudd_IsComplement(cuddT(f))) { + return; + } + + support[f->index] = 1; + ddSuppInteract(cuddT(f),support); + ddSuppInteract(Cudd_Regular(cuddE(f)),support); + /* mark as visited */ + cuddT(f) = Cudd_Complement(cuddT(f)); + f->next = Cudd_Complement(f->next); + return; + +} /* end of ddSuppInteract */ + + +/**Function******************************************************************** + + Synopsis [Performs a DFS from f, clearing the LSB of the then pointers.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +ddClearLocal( + DdNode * f) +{ + if (cuddIsConstant(f) || !Cudd_IsComplement(cuddT(f))) { + return; + } + /* clear visited flag */ + cuddT(f) = Cudd_Regular(cuddT(f)); + ddClearLocal(cuddT(f)); + ddClearLocal(Cudd_Regular(cuddE(f))); + return; + +} /* end of ddClearLocal */ + + +/**Function******************************************************************** + + Synopsis [Marks as interacting all pairs of variables that appear in + support.] + + Description [If support[i] == support[j] == 1, sets the (i,j) entry + of the interaction matrix to 1.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +ddUpdateInteract( + DdManager * table, + int * support) +{ + int i,j; + int n = table->size; + + for (i = 0; i < n-1; i++) { + if (support[i] == 1) { + for (j = i+1; j < n; j++) { + if (support[j] == 1) { + cuddSetInteract(table,i,j); + } + } + } + } + +} /* end of ddUpdateInteract */ + + +/**Function******************************************************************** + + Synopsis [Scans the DD and clears the LSB of the next pointers.] + + Description [The LSB of the next pointers are used as markers to tell + whether a node was reached by at least one DFS. Once the interaction + matrix is built, these flags are reset.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +ddClearGlobal( + DdManager * table) +{ + int i,j; + DdNode *f; + DdNode *sentinel = &(table->sentinel); + DdNodePtr *nodelist; + int slots; + + for (i = 0; i < table->size; i++) { + nodelist = table->subtables[i].nodelist; + slots = table->subtables[i].slots; + for (j = 0; j < slots; j++) { + f = nodelist[j]; + while (f != sentinel) { + f->next = Cudd_Regular(f->next); + f = f->next; + } + } + } + +} /* end of ddClearGlobal */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddLCache.c b/abc_with_bb_support/src/bdd/cudd/cuddLCache.c new file mode 100644 index 000000000..c427641a8 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddLCache.c @@ -0,0 +1,1428 @@ +/**CFile*********************************************************************** + + FileName [cuddLCache.c] + + PackageName [cudd] + + Synopsis [Functions for local caches.] + + Description [Internal procedures included in this module: +

        +
      • cuddLocalCacheInit() +
      • cuddLocalCacheQuit() +
      • cuddLocalCacheInsert() +
      • cuddLocalCacheLookup() +
      • cuddLocalCacheClearDead() +
      • cuddLocalCacheClearAll() +
      • cuddLocalCacheProfile() +
      • cuddHashTableInit() +
      • cuddHashTableQuit() +
      • cuddHashTableInsert() +
      • cuddHashTableLookup() +
      • cuddHashTableInsert2() +
      • cuddHashTableLookup2() +
      • cuddHashTableInsert3() +
      • cuddHashTableLookup3() +
      + Static procedures included in this module: +
        +
      • cuddLocalCacheResize() +
      • ddLCHash() +
      • cuddLocalCacheAddToList() +
      • cuddLocalCacheRemoveFromList() +
      • cuddHashTableResize() +
      • cuddHashTableAlloc() +
      ] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [ This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define DD_MAX_HASHTABLE_DENSITY 2 /* tells when to resize a table */ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddLCache.c,v 1.1.1.1 2003/02/24 22:23:52 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**Macro*********************************************************************** + + Synopsis [Computes hash function for keys of two operands.] + + Description [] + + SideEffects [None] + + SeeAlso [ddLCHash3 ddLCHash] + +******************************************************************************/ +#if SIZEOF_VOID_P == 8 && SIZEOF_INT == 4 +#define ddLCHash2(f,g,shift) \ +((((unsigned)(unsigned long)(f) * DD_P1 + \ + (unsigned)(unsigned long)(g)) * DD_P2) >> (shift)) +#else +#define ddLCHash2(f,g,shift) \ +((((unsigned)(f) * DD_P1 + (unsigned)(g)) * DD_P2) >> (shift)) +#endif + + +/**Macro*********************************************************************** + + Synopsis [Computes hash function for keys of three operands.] + + Description [] + + SideEffects [None] + + SeeAlso [ddLCHash2 ddLCHash] + +******************************************************************************/ +#define ddLCHash3(f,g,h,shift) ddCHash2(f,g,h,shift) + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static void cuddLocalCacheResize ARGS((DdLocalCache *cache)); +DD_INLINE static unsigned int ddLCHash ARGS((DdNodePtr *key, unsigned int keysize, int shift)); +static void cuddLocalCacheAddToList ARGS((DdLocalCache *cache)); +static void cuddLocalCacheRemoveFromList ARGS((DdLocalCache *cache)); +static int cuddHashTableResize ARGS((DdHashTable *hash)); +DD_INLINE static DdHashItem * cuddHashTableAlloc ARGS((DdHashTable *hash)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Initializes a local computed table.] + + Description [Initializes a computed table. Returns a pointer the + the new local cache in case of success; NULL otherwise.] + + SideEffects [None] + + SeeAlso [cuddInitCache] + +******************************************************************************/ +DdLocalCache * +cuddLocalCacheInit( + DdManager * manager /* manager */, + unsigned int keySize /* size of the key (number of operands) */, + unsigned int cacheSize /* Initial size of the cache */, + unsigned int maxCacheSize /* Size of the cache beyond which no resizing occurs */) +{ + DdLocalCache *cache; + int logSize; + + cache = ALLOC(DdLocalCache,1); + if (cache == NULL) { + manager->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + cache->manager = manager; + cache->keysize = keySize; + cache->itemsize = (keySize + 1) * sizeof(DdNode *); +#ifdef DD_CACHE_PROFILE + cache->itemsize += sizeof(ptrint); +#endif + logSize = cuddComputeFloorLog2(ddMax(cacheSize,manager->slots/2)); + cacheSize = 1 << logSize; + cache->item = (DdLocalCacheItem *) + ALLOC(char, cacheSize * cache->itemsize); + if (cache->item == NULL) { + manager->errorCode = CUDD_MEMORY_OUT; + FREE(cache); + return(NULL); + } + cache->slots = cacheSize; + cache->shift = sizeof(int) * 8 - logSize; + cache->maxslots = ddMin(maxCacheSize,manager->slots); + cache->minHit = manager->minHit; + /* Initialize to avoid division by 0 and immediate resizing. */ + cache->lookUps = (double) (int) (cacheSize * cache->minHit + 1); + cache->hits = 0; + manager->memused += cacheSize * cache->itemsize + sizeof(DdLocalCache); + + /* Initialize the cache. */ + memset(cache->item, 0, cacheSize * cache->itemsize); + + /* Add to manager's list of local caches for GC. */ + cuddLocalCacheAddToList(cache); + + return(cache); + +} /* end of cuddLocalCacheInit */ + + +/**Function******************************************************************** + + Synopsis [Shuts down a local computed table.] + + Description [Initializes the computed table. It is called by + Cudd_Init. Returns a pointer the the new local cache in case of + success; NULL otherwise.] + + SideEffects [None] + + SeeAlso [cuddLocalCacheInit] + +******************************************************************************/ +void +cuddLocalCacheQuit( + DdLocalCache * cache /* cache to be shut down */) +{ + cache->manager->memused -= + cache->slots * cache->itemsize + sizeof(DdLocalCache); + cuddLocalCacheRemoveFromList(cache); + FREE(cache->item); + FREE(cache); + + return; + +} /* end of cuddLocalCacheQuit */ + + +/**Function******************************************************************** + + Synopsis [Inserts a result in a local cache.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +cuddLocalCacheInsert( + DdLocalCache * cache, + DdNodePtr * key, + DdNode * value) +{ + unsigned int posn; + DdLocalCacheItem *entry; + + posn = ddLCHash(key,cache->keysize,cache->shift); + entry = (DdLocalCacheItem *) ((char *) cache->item + + posn * cache->itemsize); + memcpy(entry->key,key,cache->keysize * sizeof(DdNode *)); + entry->value = value; +#ifdef DD_CACHE_PROFILE + entry->count++; +#endif + +} /* end of cuddLocalCacheInsert */ + + +/**Function******************************************************************** + + Synopsis [Looks up in a local cache.] + + Description [Looks up in a local cache. Returns the result if found; + it returns NULL if no result is found.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddLocalCacheLookup( + DdLocalCache * cache, + DdNodePtr * key) +{ + unsigned int posn; + DdLocalCacheItem *entry; + DdNode *value; + + cache->lookUps++; + posn = ddLCHash(key,cache->keysize,cache->shift); + entry = (DdLocalCacheItem *) ((char *) cache->item + + posn * cache->itemsize); + if (entry->value != NULL && + memcmp(key,entry->key,cache->keysize*sizeof(DdNode *)) == 0) { + cache->hits++; + value = Cudd_Regular(entry->value); + if (value->ref == 0) { + cuddReclaim(cache->manager,value); + } + return(entry->value); + } + + /* Cache miss: decide whether to resize */ + + if (cache->slots < cache->maxslots && + cache->hits > cache->lookUps * cache->minHit) { + cuddLocalCacheResize(cache); + } + + return(NULL); + +} /* end of cuddLocalCacheLookup */ + + +/**Function******************************************************************** + + Synopsis [Clears the dead entries of the local caches of a manager.] + + Description [Clears the dead entries of the local caches of a manager. + Used during garbage collection.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +cuddLocalCacheClearDead( + DdManager * manager) +{ + DdLocalCache *cache = manager->localCaches; + unsigned int keysize; + unsigned int itemsize; + unsigned int slots; + DdLocalCacheItem *item; + DdNodePtr *key; + unsigned int i, j; + + while (cache != NULL) { + keysize = cache->keysize; + itemsize = cache->itemsize; + slots = cache->slots; + item = cache->item; + for (i = 0; i < slots; i++) { + if (item->value != NULL && Cudd_Regular(item->value)->ref == 0) { + item->value = NULL; + } else { + key = item->key; + for (j = 0; j < keysize; j++) { + if (Cudd_Regular(key[j])->ref == 0) { + item->value = NULL; + break; + } + } + } + item = (DdLocalCacheItem *) ((char *) item + itemsize); + } + cache = cache->next; + } + return; + +} /* end of cuddLocalCacheClearDead */ + + +/**Function******************************************************************** + + Synopsis [Clears the local caches of a manager.] + + Description [Clears the local caches of a manager. + Used before reordering.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +cuddLocalCacheClearAll( + DdManager * manager) +{ + DdLocalCache *cache = manager->localCaches; + + while (cache != NULL) { + memset(cache->item, 0, cache->slots * cache->itemsize); + cache = cache->next; + } + return; + +} /* end of cuddLocalCacheClearAll */ + + +#ifdef DD_CACHE_PROFILE + +#define DD_HYSTO_BINS 8 + +/**Function******************************************************************** + + Synopsis [Computes and prints a profile of a local cache usage.] + + Description [Computes and prints a profile of a local cache usage. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddLocalCacheProfile( + DdLocalCache * cache) +{ + double count, mean, meansq, stddev, expected; + long max, min; + int imax, imin; + int i, retval, slots; + long *hystogram; + int nbins = DD_HYSTO_BINS; + int bin; + long thiscount; + double totalcount; + int nzeroes; + DdLocalCacheItem *entry; + FILE *fp = cache->manager->out; + + slots = cache->slots; + + meansq = mean = expected = 0.0; + max = min = (long) cache->item[0].count; + imax = imin = nzeroes = 0; + totalcount = 0.0; + + hystogram = ALLOC(long, nbins); + if (hystogram == NULL) { + return(0); + } + for (i = 0; i < nbins; i++) { + hystogram[i] = 0; + } + + for (i = 0; i < slots; i++) { + entry = (DdLocalCacheItem *) ((char *) cache->item + + i * cache->itemsize); + thiscount = (long) entry->count; + if (thiscount > max) { + max = thiscount; + imax = i; + } + if (thiscount < min) { + min = thiscount; + imin = i; + } + if (thiscount == 0) { + nzeroes++; + } + count = (double) thiscount; + mean += count; + meansq += count * count; + totalcount += count; + expected += count * (double) i; + bin = (i * nbins) / slots; + hystogram[bin] += thiscount; + } + mean /= (double) slots; + meansq /= (double) slots; + stddev = sqrt(meansq - mean*mean); + + retval = fprintf(fp,"Cache stats: slots = %d average = %g ", slots, mean); + if (retval == EOF) return(0); + retval = fprintf(fp,"standard deviation = %g\n", stddev); + if (retval == EOF) return(0); + retval = fprintf(fp,"Cache max accesses = %ld for slot %d\n", max, imax); + if (retval == EOF) return(0); + retval = fprintf(fp,"Cache min accesses = %ld for slot %d\n", min, imin); + if (retval == EOF) return(0); + retval = fprintf(fp,"Cache unused slots = %d\n", nzeroes); + if (retval == EOF) return(0); + + if (totalcount) { + expected /= totalcount; + retval = fprintf(fp,"Cache access hystogram for %d bins", nbins); + if (retval == EOF) return(0); + retval = fprintf(fp," (expected bin value = %g)\n# ", expected); + if (retval == EOF) return(0); + for (i = nbins - 1; i>=0; i--) { + retval = fprintf(fp,"%ld ", hystogram[i]); + if (retval == EOF) return(0); + } + retval = fprintf(fp,"\n"); + if (retval == EOF) return(0); + } + + FREE(hystogram); + return(1); + +} /* end of cuddLocalCacheProfile */ +#endif + + +/**Function******************************************************************** + + Synopsis [Initializes a hash table.] + + Description [Initializes a hash table. Returns a pointer to the new + table if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [cuddHashTableQuit] + +******************************************************************************/ +DdHashTable * +cuddHashTableInit( + DdManager * manager, + unsigned int keySize, + unsigned int initSize) +{ + DdHashTable *hash; + int logSize; + +#ifdef __osf__ +#pragma pointer_size save +#pragma pointer_size short +#endif + hash = ALLOC(DdHashTable, 1); + if (hash == NULL) { + manager->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + hash->keysize = keySize; + hash->manager = manager; + hash->memoryList = NULL; + hash->nextFree = NULL; + hash->itemsize = (keySize + 1) * sizeof(DdNode *) + + sizeof(ptrint) + sizeof(DdHashItem *); + /* We have to guarantee that the shift be < 32. */ + if (initSize < 2) initSize = 2; + logSize = cuddComputeFloorLog2(initSize); + hash->numBuckets = 1 << logSize; + hash->shift = sizeof(int) * 8 - logSize; + hash->bucket = ALLOC(DdHashItem *, hash->numBuckets); + if (hash->bucket == NULL) { + manager->errorCode = CUDD_MEMORY_OUT; + FREE(hash); + return(NULL); + } + memset(hash->bucket, 0, hash->numBuckets * sizeof(DdHashItem *)); + hash->size = 0; + hash->maxsize = hash->numBuckets * DD_MAX_HASHTABLE_DENSITY; +#ifdef __osf__ +#pragma pointer_size restore +#endif + return(hash); + +} /* end of cuddHashTableInit */ + + +/**Function******************************************************************** + + Synopsis [Shuts down a hash table.] + + Description [Shuts down a hash table, dereferencing all the values.] + + SideEffects [None] + + SeeAlso [cuddHashTableInit] + +******************************************************************************/ +void +cuddHashTableQuit( + DdHashTable * hash) +{ +#ifdef __osf__ +#pragma pointer_size save +#pragma pointer_size short +#endif + unsigned int i; + DdManager *dd = hash->manager; + DdHashItem *bucket; + DdHashItem **memlist, **nextmem; + unsigned int numBuckets = hash->numBuckets; + + for (i = 0; i < numBuckets; i++) { + bucket = hash->bucket[i]; + while (bucket != NULL) { + Cudd_RecursiveDeref(dd, bucket->value); + bucket = bucket->next; + } + } + + memlist = hash->memoryList; + while (memlist != NULL) { + nextmem = (DdHashItem **) memlist[0]; + FREE(memlist); + memlist = nextmem; + } + + FREE(hash->bucket); + FREE(hash); +#ifdef __osf__ +#pragma pointer_size restore +#endif + + return; + +} /* end of cuddHashTableQuit */ + + +/**Function******************************************************************** + + Synopsis [Inserts an item in a hash table.] + + Description [Inserts an item in a hash table when the key has more than + three pointers. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [[cuddHashTableInsert1 cuddHashTableInsert2 cuddHashTableInsert3 + cuddHashTableLookup] + +******************************************************************************/ +int +cuddHashTableInsert( + DdHashTable * hash, + DdNodePtr * key, + DdNode * value, + ptrint count) +{ + int result; + unsigned int posn; + DdHashItem *item; + unsigned int i; + +#ifdef DD_DEBUG + assert(hash->keysize > 3); +#endif + + if (hash->size > hash->maxsize) { + result = cuddHashTableResize(hash); + if (result == 0) return(0); + } + item = cuddHashTableAlloc(hash); + if (item == NULL) return(0); + hash->size++; + item->value = value; + cuddRef(value); + item->count = count; + for (i = 0; i < hash->keysize; i++) { + item->key[i] = key[i]; + } + posn = ddLCHash(key,hash->keysize,hash->shift); + item->next = hash->bucket[posn]; + hash->bucket[posn] = item; + + return(1); + +} /* end of cuddHashTableInsert */ + + +/**Function******************************************************************** + + Synopsis [Looks up a key in a hash table.] + + Description [Looks up a key consisting of more than three pointers + in a hash table. Returns the value associated to the key if there + is an entry for the given key in the table; NULL otherwise. If the + entry is present, its reference counter is decremented if not + saturated. If the counter reaches 0, the value of the entry is + dereferenced, and the entry is returned to the free list.] + + SideEffects [None] + + SeeAlso [cuddHashTableLookup1 cuddHashTableLookup2 cuddHashTableLookup3 + cuddHashTableInsert] + +******************************************************************************/ +DdNode * +cuddHashTableLookup( + DdHashTable * hash, + DdNodePtr * key) +{ + unsigned int posn; + DdHashItem *item, *prev; + unsigned int i, keysize; + +#ifdef DD_DEBUG + assert(hash->keysize > 3); +#endif + + posn = ddLCHash(key,hash->keysize,hash->shift); + item = hash->bucket[posn]; + prev = NULL; + + keysize = hash->keysize; + while (item != NULL) { + DdNodePtr *key2 = item->key; + int equal = 1; + for (i = 0; i < keysize; i++) { + if (key[i] != key2[i]) { + equal = 0; + break; + } + } + if (equal) { + DdNode *value = item->value; + cuddSatDec(item->count); + if (item->count == 0) { + cuddDeref(value); + if (prev == NULL) { + hash->bucket[posn] = item->next; + } else { + prev->next = item->next; + } + item->next = hash->nextFree; + hash->nextFree = item; + hash->size--; + } + return(value); + } + prev = item; + item = item->next; + } + return(NULL); + +} /* end of cuddHashTableLookup */ + + +/**Function******************************************************************** + + Synopsis [Inserts an item in a hash table.] + + Description [Inserts an item in a hash table when the key is one pointer. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [cuddHashTableInsert cuddHashTableInsert2 cuddHashTableInsert3 + cuddHashTableLookup1] + +******************************************************************************/ +int +cuddHashTableInsert1( + DdHashTable * hash, + DdNode * f, + DdNode * value, + ptrint count) +{ + int result; + unsigned int posn; + DdHashItem *item; + +#ifdef DD_DEBUG + assert(hash->keysize == 1); +#endif + + if (hash->size > hash->maxsize) { + result = cuddHashTableResize(hash); + if (result == 0) return(0); + } + item = cuddHashTableAlloc(hash); + if (item == NULL) return(0); + hash->size++; + item->value = value; + cuddRef(value); + item->count = count; + item->key[0] = f; + posn = ddLCHash2(f,f,hash->shift); + item->next = hash->bucket[posn]; + hash->bucket[posn] = item; + + return(1); + +} /* end of cuddHashTableInsert1 */ + + +/**Function******************************************************************** + + Synopsis [Looks up a key consisting of one pointer in a hash table.] + + Description [Looks up a key consisting of one pointer in a hash table. + Returns the value associated to the key if there is an entry for the given + key in the table; NULL otherwise. If the entry is present, its reference + counter is decremented if not saturated. If the counter reaches 0, the + value of the entry is dereferenced, and the entry is returned to the free + list.] + + SideEffects [None] + + SeeAlso [cuddHashTableLookup cuddHashTableLookup2 cuddHashTableLookup3 + cuddHashTableInsert1] + +******************************************************************************/ +DdNode * +cuddHashTableLookup1( + DdHashTable * hash, + DdNode * f) +{ + unsigned int posn; + DdHashItem *item, *prev; + +#ifdef DD_DEBUG + assert(hash->keysize == 1); +#endif + + posn = ddLCHash2(f,f,hash->shift); + item = hash->bucket[posn]; + prev = NULL; + + while (item != NULL) { + DdNodePtr *key = item->key; + if (f == key[0]) { + DdNode *value = item->value; + cuddSatDec(item->count); + if (item->count == 0) { + cuddDeref(value); + if (prev == NULL) { + hash->bucket[posn] = item->next; + } else { + prev->next = item->next; + } + item->next = hash->nextFree; + hash->nextFree = item; + hash->size--; + } + return(value); + } + prev = item; + item = item->next; + } + return(NULL); + +} /* end of cuddHashTableLookup1 */ + + +/**Function******************************************************************** + + Synopsis [Inserts an item in a hash table.] + + Description [Inserts an item in a hash table when the key is + composed of two pointers. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [cuddHashTableInsert cuddHashTableInsert1 cuddHashTableInsert3 + cuddHashTableLookup2] + +******************************************************************************/ +int +cuddHashTableInsert2( + DdHashTable * hash, + DdNode * f, + DdNode * g, + DdNode * value, + ptrint count) +{ + int result; + unsigned int posn; + DdHashItem *item; + +#ifdef DD_DEBUG + assert(hash->keysize == 2); +#endif + + if (hash->size > hash->maxsize) { + result = cuddHashTableResize(hash); + if (result == 0) return(0); + } + item = cuddHashTableAlloc(hash); + if (item == NULL) return(0); + hash->size++; + item->value = value; + cuddRef(value); + item->count = count; + item->key[0] = f; + item->key[1] = g; + posn = ddLCHash2(f,g,hash->shift); + item->next = hash->bucket[posn]; + hash->bucket[posn] = item; + + return(1); + +} /* end of cuddHashTableInsert2 */ + + +/**Function******************************************************************** + + Synopsis [Looks up a key consisting of two pointers in a hash table.] + + Description [Looks up a key consisting of two pointer in a hash table. + Returns the value associated to the key if there is an entry for the given + key in the table; NULL otherwise. If the entry is present, its reference + counter is decremented if not saturated. If the counter reaches 0, the + value of the entry is dereferenced, and the entry is returned to the free + list.] + + SideEffects [None] + + SeeAlso [cuddHashTableLookup cuddHashTableLookup1 cuddHashTableLookup3 + cuddHashTableInsert2] + +******************************************************************************/ +DdNode * +cuddHashTableLookup2( + DdHashTable * hash, + DdNode * f, + DdNode * g) +{ + unsigned int posn; + DdHashItem *item, *prev; + +#ifdef DD_DEBUG + assert(hash->keysize == 2); +#endif + + posn = ddLCHash2(f,g,hash->shift); + item = hash->bucket[posn]; + prev = NULL; + + while (item != NULL) { + DdNodePtr *key = item->key; + if ((f == key[0]) && (g == key[1])) { + DdNode *value = item->value; + cuddSatDec(item->count); + if (item->count == 0) { + cuddDeref(value); + if (prev == NULL) { + hash->bucket[posn] = item->next; + } else { + prev->next = item->next; + } + item->next = hash->nextFree; + hash->nextFree = item; + hash->size--; + } + return(value); + } + prev = item; + item = item->next; + } + return(NULL); + +} /* end of cuddHashTableLookup2 */ + + +/**Function******************************************************************** + + Synopsis [Inserts an item in a hash table.] + + Description [Inserts an item in a hash table when the key is + composed of three pointers. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [cuddHashTableInsert cuddHashTableInsert1 cuddHashTableInsert2 + cuddHashTableLookup3] + +******************************************************************************/ +int +cuddHashTableInsert3( + DdHashTable * hash, + DdNode * f, + DdNode * g, + DdNode * h, + DdNode * value, + ptrint count) +{ + int result; + unsigned int posn; + DdHashItem *item; + +#ifdef DD_DEBUG + assert(hash->keysize == 3); +#endif + + if (hash->size > hash->maxsize) { + result = cuddHashTableResize(hash); + if (result == 0) return(0); + } + item = cuddHashTableAlloc(hash); + if (item == NULL) return(0); + hash->size++; + item->value = value; + cuddRef(value); + item->count = count; + item->key[0] = f; + item->key[1] = g; + item->key[2] = h; + posn = ddLCHash3(f,g,h,hash->shift); + item->next = hash->bucket[posn]; + hash->bucket[posn] = item; + + return(1); + +} /* end of cuddHashTableInsert3 */ + + +/**Function******************************************************************** + + Synopsis [Looks up a key consisting of three pointers in a hash table.] + + Description [Looks up a key consisting of three pointers in a hash table. + Returns the value associated to the key if there is an entry for the given + key in the table; NULL otherwise. If the entry is present, its reference + counter is decremented if not saturated. If the counter reaches 0, the + value of the entry is dereferenced, and the entry is returned to the free + list.] + + SideEffects [None] + + SeeAlso [cuddHashTableLookup cuddHashTableLookup1 cuddHashTableLookup2 + cuddHashTableInsert3] + +******************************************************************************/ +DdNode * +cuddHashTableLookup3( + DdHashTable * hash, + DdNode * f, + DdNode * g, + DdNode * h) +{ + unsigned int posn; + DdHashItem *item, *prev; + +#ifdef DD_DEBUG + assert(hash->keysize == 3); +#endif + + posn = ddLCHash3(f,g,h,hash->shift); + item = hash->bucket[posn]; + prev = NULL; + + while (item != NULL) { + DdNodePtr *key = item->key; + if ((f == key[0]) && (g == key[1]) && (h == key[2])) { + DdNode *value = item->value; + cuddSatDec(item->count); + if (item->count == 0) { + cuddDeref(value); + if (prev == NULL) { + hash->bucket[posn] = item->next; + } else { + prev->next = item->next; + } + item->next = hash->nextFree; + hash->nextFree = item; + hash->size--; + } + return(value); + } + prev = item; + item = item->next; + } + return(NULL); + +} /* end of cuddHashTableLookup3 */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Resizes a local cache.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +cuddLocalCacheResize( + DdLocalCache * cache) +{ + DdLocalCacheItem *item, *olditem, *entry, *old; + int i, shift; + unsigned int posn; + unsigned int slots, oldslots; + extern void (*MMoutOfMemory)(long); + void (*saveHandler)(long); + + olditem = cache->item; + oldslots = cache->slots; + slots = cache->slots = oldslots << 1; + +#ifdef DD_VERBOSE + (void) fprintf(cache->manager->err, + "Resizing local cache from %d to %d entries\n", + oldslots, slots); + (void) fprintf(cache->manager->err, + "\thits = %.0f\tlookups = %.0f\thit ratio = %5.3f\n", + cache->hits, cache->lookUps, cache->hits / cache->lookUps); +#endif + + saveHandler = MMoutOfMemory; + MMoutOfMemory = Cudd_OutOfMem; + cache->item = item = + (DdLocalCacheItem *) ALLOC(char, slots * cache->itemsize); + MMoutOfMemory = saveHandler; + /* If we fail to allocate the new table we just give up. */ + if (item == NULL) { +#ifdef DD_VERBOSE + (void) fprintf(cache->manager->err,"Resizing failed. Giving up.\n"); +#endif + cache->slots = oldslots; + cache->item = olditem; + /* Do not try to resize again. */ + cache->maxslots = oldslots - 1; + return; + } + shift = --(cache->shift); + cache->manager->memused += (slots - oldslots) * cache->itemsize; + + /* Clear new cache. */ + memset(item, 0, slots * cache->itemsize); + + /* Copy from old cache to new one. */ + for (i = 0; (unsigned) i < oldslots; i++) { + old = (DdLocalCacheItem *) ((char *) olditem + i * cache->itemsize); + if (old->value != NULL) { + posn = ddLCHash(old->key,cache->keysize,slots); + entry = (DdLocalCacheItem *) ((char *) item + + posn * cache->itemsize); + memcpy(entry->key,old->key,cache->keysize*sizeof(DdNode *)); + entry->value = old->value; + } + } + + FREE(olditem); + + /* Reinitialize measurements so as to avoid division by 0 and + ** immediate resizing. + */ + cache->lookUps = (double) (int) (slots * cache->minHit + 1); + cache->hits = 0; + +} /* end of cuddLocalCacheResize */ + + +/**Function******************************************************************** + + Synopsis [Computes the hash value for a local cache.] + + Description [Computes the hash value for a local cache. Returns the + bucket index.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DD_INLINE +static unsigned int +ddLCHash( + DdNodePtr * key, + unsigned int keysize, + int shift) +{ + unsigned int val = (unsigned int) (ptrint) key[0]; + unsigned int i; + + for (i = 1; i < keysize; i++) { + val = val * DD_P1 + (int) (ptrint) key[i]; + } + + return(val >> shift); + +} /* end of ddLCHash */ + + +/**Function******************************************************************** + + Synopsis [Inserts a local cache in the manager list.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +cuddLocalCacheAddToList( + DdLocalCache * cache) +{ + DdManager *manager = cache->manager; + + cache->next = manager->localCaches; + manager->localCaches = cache; + return; + +} /* end of cuddLocalCacheAddToList */ + + +/**Function******************************************************************** + + Synopsis [Removes a local cache from the manager list.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +cuddLocalCacheRemoveFromList( + DdLocalCache * cache) +{ + DdManager *manager = cache->manager; +#ifdef __osf__ +#pragma pointer_size save +#pragma pointer_size short +#endif + DdLocalCache **prevCache, *nextCache; +#ifdef __osf__ +#pragma pointer_size restore +#endif + + prevCache = &(manager->localCaches); + nextCache = manager->localCaches; + + while (nextCache != NULL) { + if (nextCache == cache) { + *prevCache = nextCache->next; + return; + } + prevCache = &(nextCache->next); + nextCache = nextCache->next; + } + return; /* should never get here */ + +} /* end of cuddLocalCacheRemoveFromList */ + + +/**Function******************************************************************** + + Synopsis [Resizes a hash table.] + + Description [Resizes a hash table. Returns 1 if successful; 0 + otherwise.] + + SideEffects [None] + + SeeAlso [cuddHashTableInsert] + +******************************************************************************/ +static int +cuddHashTableResize( + DdHashTable * hash) +{ + int j; + unsigned int posn; + DdHashItem *item; + DdHashItem *next; +#ifdef __osf__ +#pragma pointer_size save +#pragma pointer_size short +#endif + DdNode **key; + int numBuckets; + DdHashItem **buckets; + DdHashItem **oldBuckets = hash->bucket; +#ifdef __osf__ +#pragma pointer_size restore +#endif + int shift; + int oldNumBuckets = hash->numBuckets; + extern void (*MMoutOfMemory)(long); + void (*saveHandler)(long); + + /* Compute the new size of the table. */ + numBuckets = oldNumBuckets << 1; + saveHandler = MMoutOfMemory; + MMoutOfMemory = Cudd_OutOfMem; +#ifdef __osf__ +#pragma pointer_size save +#pragma pointer_size short +#endif + buckets = ALLOC(DdHashItem *, numBuckets); + MMoutOfMemory = saveHandler; + if (buckets == NULL) { + hash->maxsize <<= 1; + return(1); + } + + hash->bucket = buckets; + hash->numBuckets = numBuckets; + shift = --(hash->shift); + hash->maxsize <<= 1; + memset(buckets, 0, numBuckets * sizeof(DdHashItem *)); +#ifdef __osf__ +#pragma pointer_size restore +#endif + if (hash->keysize == 1) { + for (j = 0; j < oldNumBuckets; j++) { + item = oldBuckets[j]; + while (item != NULL) { + next = item->next; + key = item->key; + posn = ddLCHash2(key[0], key[0], shift); + item->next = buckets[posn]; + buckets[posn] = item; + item = next; + } + } + } else if (hash->keysize == 2) { + for (j = 0; j < oldNumBuckets; j++) { + item = oldBuckets[j]; + while (item != NULL) { + next = item->next; + key = item->key; + posn = ddLCHash2(key[0], key[1], shift); + item->next = buckets[posn]; + buckets[posn] = item; + item = next; + } + } + } else if (hash->keysize == 3) { + for (j = 0; j < oldNumBuckets; j++) { + item = oldBuckets[j]; + while (item != NULL) { + next = item->next; + key = item->key; + posn = ddLCHash3(key[0], key[1], key[2], shift); + item->next = buckets[posn]; + buckets[posn] = item; + item = next; + } + } + } else { + for (j = 0; j < oldNumBuckets; j++) { + item = oldBuckets[j]; + while (item != NULL) { + next = item->next; + posn = ddLCHash(item->key, hash->keysize, shift); + item->next = buckets[posn]; + buckets[posn] = item; + item = next; + } + } + } + FREE(oldBuckets); + return(1); + +} /* end of cuddHashTableResize */ + + +/**Function******************************************************************** + + Synopsis [Fast storage allocation for items in a hash table.] + + Description [Fast storage allocation for items in a hash table. The + first 4 bytes of a chunk contain a pointer to the next block; the + rest contains DD_MEM_CHUNK spaces for hash items. Returns a pointer to + a new item if successful; NULL is memory is full.] + + SideEffects [None] + + SeeAlso [cuddAllocNode cuddDynamicAllocNode] + +******************************************************************************/ +DD_INLINE +static DdHashItem * +cuddHashTableAlloc( + DdHashTable * hash) +{ + int i; + unsigned int itemsize = hash->itemsize; + extern void (*MMoutOfMemory)(long); + void (*saveHandler)(long); +#ifdef __osf__ +#pragma pointer_size save +#pragma pointer_size short +#endif + DdHashItem **mem, *thisOne, *next, *item; + + if (hash->nextFree == NULL) { + saveHandler = MMoutOfMemory; + MMoutOfMemory = Cudd_OutOfMem; + mem = (DdHashItem **) ALLOC(char,(DD_MEM_CHUNK+1) * itemsize); + MMoutOfMemory = saveHandler; +#ifdef __osf__ +#pragma pointer_size restore +#endif + if (mem == NULL) { + if (hash->manager->stash != NULL) { + FREE(hash->manager->stash); + hash->manager->stash = NULL; + /* Inhibit resizing of tables. */ + hash->manager->maxCacheHard = hash->manager->cacheSlots - 1; + hash->manager->cacheSlack = -(hash->manager->cacheSlots + 1); + for (i = 0; i < hash->manager->size; i++) { + hash->manager->subtables[i].maxKeys <<= 2; + } + hash->manager->gcFrac = 0.2; + hash->manager->minDead = + (unsigned) (0.2 * (double) hash->manager->slots); +#ifdef __osf__ +#pragma pointer_size save +#pragma pointer_size short +#endif + mem = (DdHashItem **) ALLOC(char,(DD_MEM_CHUNK+1) * itemsize); +#ifdef __osf__ +#pragma pointer_size restore +#endif + } + if (mem == NULL) { + (*MMoutOfMemory)((DD_MEM_CHUNK + 1) * itemsize); + hash->manager->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + } + + mem[0] = (DdHashItem *) hash->memoryList; + hash->memoryList = mem; + + thisOne = (DdHashItem *) ((char *) mem + itemsize); + hash->nextFree = thisOne; + for (i = 1; i < DD_MEM_CHUNK; i++) { + next = (DdHashItem *) ((char *) thisOne + itemsize); + thisOne->next = next; + thisOne = next; + } + + thisOne->next = NULL; + + } + item = hash->nextFree; + hash->nextFree = item->next; + return(item); + +} /* end of cuddHashTableAlloc */ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddLevelQ.c b/abc_with_bb_support/src/bdd/cudd/cuddLevelQ.c new file mode 100644 index 000000000..e3c06c44f --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddLevelQ.c @@ -0,0 +1,533 @@ +/**CFile*********************************************************************** + + FileName [cuddLevelQ.c] + + PackageName [cudd] + + Synopsis [Procedure to manage level queues.] + + Description [The functions in this file allow an application to + easily manipulate a queue where nodes are prioritized by level. The + emphasis is on efficiency. Therefore, the queue items can have + variable size. If the application does not need to attach + information to the nodes, it can declare the queue items to be of + type DdQueueItem. Otherwise, it can declare them to be of a + structure type such that the first three fields are data + pointers. The third pointer points to the node. The first two + pointers are used by the level queue functions. The remaining fields + are initialized to 0 when a new item is created, and are then left + to the exclusive use of the application. On the DEC Alphas the three + pointers must be 32-bit pointers when CUDD is compiled with 32-bit + pointers. The level queue functions make sure that each node + appears at most once in the queue. They do so by keeping a hash + table where the node is used as key. Queue items are recycled via a + free list for efficiency. + + Internal procedures provided by this module: +
        +
      • cuddLevelQueueInit() +
      • cuddLevelQueueQuit() +
      • cuddLevelQueueEnqueue() +
      • cuddLevelQueueDequeue() +
      + Static procedures included in this module: +
        +
      • hashLookup() +
      • hashInsert() +
      • hashDelete() +
      • hashResize() +
      + ] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no + warranty about the suitability of this software for any + purpose. It is presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddLevelQ.c,v 1.1.1.1 2003/02/24 22:23:52 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**Macro*********************************************************************** + + Synopsis [Hash function for the table of a level queue.] + + Description [Hash function for the table of a level queue.] + + SideEffects [None] + + SeeAlso [hashInsert hashLookup hashDelete] + +******************************************************************************/ +#if SIZEOF_VOID_P == 8 && SIZEOF_INT == 4 +#define lqHash(key,shift) \ +(((unsigned)(unsigned long)(key) * DD_P1) >> (shift)) +#else +#define lqHash(key,shift) \ +(((unsigned)(key) * DD_P1) >> (shift)) +#endif + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static DdQueueItem * hashLookup ARGS((DdLevelQueue *queue, void *key)); +static int hashInsert ARGS((DdLevelQueue *queue, DdQueueItem *item)); +static void hashDelete ARGS((DdLevelQueue *queue, DdQueueItem *item)); +static int hashResize ARGS((DdLevelQueue *queue)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Initializes a level queue.] + + Description [Initializes a level queue. A level queue is a queue + where inserts are based on the levels of the nodes. Within each + level the policy is FIFO. Level queues are useful in traversing a + BDD top-down. Queue items are kept in a free list when dequeued for + efficiency. Returns a pointer to the new queue if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [cuddLevelQueueQuit cuddLevelQueueEnqueue cuddLevelQueueDequeue] + +******************************************************************************/ +DdLevelQueue * +cuddLevelQueueInit( + int levels /* number of levels */, + int itemSize /* size of the item */, + int numBuckets /* initial number of hash buckets */) +{ + DdLevelQueue *queue; + int logSize; + + queue = ALLOC(DdLevelQueue,1); + if (queue == NULL) + return(NULL); +#ifdef __osf__ +#pragma pointer_size save +#pragma pointer_size short +#endif + /* Keep pointers to the insertion points for all levels. */ + queue->last = ALLOC(DdQueueItem *, levels); +#ifdef __osf__ +#pragma pointer_size restore +#endif + if (queue->last == NULL) { + FREE(queue); + return(NULL); + } + /* Use a hash table to test for uniqueness. */ + if (numBuckets < 2) numBuckets = 2; + logSize = cuddComputeFloorLog2(numBuckets); + queue->numBuckets = 1 << logSize; + queue->shift = sizeof(int) * 8 - logSize; +#ifdef __osf__ +#pragma pointer_size save +#pragma pointer_size short +#endif + queue->buckets = ALLOC(DdQueueItem *, queue->numBuckets); +#ifdef __osf__ +#pragma pointer_size restore +#endif + if (queue->buckets == NULL) { + FREE(queue->last); + FREE(queue); + return(NULL); + } +#ifdef __osf__ +#pragma pointer_size save +#pragma pointer_size short +#endif + memset(queue->last, 0, levels * sizeof(DdQueueItem *)); + memset(queue->buckets, 0, queue->numBuckets * sizeof(DdQueueItem *)); +#ifdef __osf__ +#pragma pointer_size restore +#endif + queue->first = NULL; + queue->freelist = NULL; + queue->levels = levels; + queue->itemsize = itemSize; + queue->size = 0; + queue->maxsize = queue->numBuckets * DD_MAX_SUBTABLE_DENSITY; + return(queue); + +} /* end of cuddLevelQueueInit */ + + +/**Function******************************************************************** + + Synopsis [Shuts down a level queue.] + + Description [Shuts down a level queue and releases all the + associated memory.] + + SideEffects [None] + + SeeAlso [cuddLevelQueueInit] + +******************************************************************************/ +void +cuddLevelQueueQuit( + DdLevelQueue * queue) +{ + DdQueueItem *item; + + while (queue->freelist != NULL) { + item = queue->freelist; + queue->freelist = item->next; + FREE(item); + } + while (queue->first != NULL) { + item = (DdQueueItem *) queue->first; + queue->first = item->next; + FREE(item); + } + FREE(queue->buckets); + FREE(queue->last); + FREE(queue); + return; + +} /* end of cuddLevelQueueQuit */ + + +/**Function******************************************************************** + + Synopsis [Inserts a new key in a level queue.] + + Description [Inserts a new key in a level queue. A new entry is + created in the queue only if the node is not already + enqueued. Returns a pointer to the queue item if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [cuddLevelQueueInit cuddLevelQueueDequeue] + +******************************************************************************/ +void * +cuddLevelQueueEnqueue( + DdLevelQueue * queue /* level queue */, + void * key /* key to be enqueued */, + int level /* level at which to insert */) +{ + int plevel; + DdQueueItem *item; + +#ifdef DD_DEBUG + assert(level < queue->levels); +#endif + /* Check whether entry for this node exists. */ + item = hashLookup(queue,key); + if (item != NULL) return(item); + + /* Get a free item from either the free list or the memory manager. */ + if (queue->freelist == NULL) { + item = (DdQueueItem *) ALLOC(char, queue->itemsize); + if (item == NULL) + return(NULL); + } else { + item = queue->freelist; + queue->freelist = item->next; + } + /* Initialize. */ + memset(item, 0, queue->itemsize); + item->key = key; + /* Update stats. */ + queue->size++; + + if (queue->last[level]) { + /* There are already items for this level in the queue. */ + item->next = queue->last[level]->next; + queue->last[level]->next = item; + } else { + /* There are no items at the current level. Look for the first + ** non-empty level preceeding this one. */ + plevel = level; + while (plevel != 0 && queue->last[plevel] == NULL) + plevel--; + if (queue->last[plevel] == NULL) { + /* No element precedes this one in the queue. */ + item->next = (DdQueueItem *) queue->first; + queue->first = item; + } else { + item->next = queue->last[plevel]->next; + queue->last[plevel]->next = item; + } + } + queue->last[level] = item; + + /* Insert entry for the key in the hash table. */ + if (hashInsert(queue,item) == 0) { + return(NULL); + } + return(item); + +} /* end of cuddLevelQueueEnqueue */ + + +/**Function******************************************************************** + + Synopsis [Remove an item from the front of a level queue.] + + Description [Remove an item from the front of a level queue.] + + SideEffects [None] + + SeeAlso [cuddLevelQueueEnqueue] + +******************************************************************************/ +void +cuddLevelQueueDequeue( + DdLevelQueue * queue, + int level) +{ + DdQueueItem *item = (DdQueueItem *) queue->first; + + /* Delete from the hash table. */ + hashDelete(queue,item); + + /* Since we delete from the front, if this is the last item for + ** its level, there are no other items for the same level. */ + if (queue->last[level] == item) + queue->last[level] = NULL; + + queue->first = item->next; + /* Put item on the free list. */ + item->next = queue->freelist; + queue->freelist = item; + /* Update stats. */ + queue->size--; + return; + +} /* end of cuddLevelQueueDequeue */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Looks up a key in the hash table of a level queue.] + + Description [Looks up a key in the hash table of a level queue. Returns + a pointer to the item with the given key if the key is found; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [cuddLevelQueueEnqueue hashInsert] + +******************************************************************************/ +static DdQueueItem * +hashLookup( + DdLevelQueue * queue, + void * key) +{ + int posn; + DdQueueItem *item; + + posn = lqHash(key,queue->shift); + item = queue->buckets[posn]; + + while (item != NULL) { + if (item->key == key) { + return(item); + } + item = item->cnext; + } + return(NULL); + +} /* end of hashLookup */ + + +/**Function******************************************************************** + + Synopsis [Inserts an item in the hash table of a level queue.] + + Description [Inserts an item in the hash table of a level queue. Returns + 1 if successful; 0 otherwise. No check is performed to see if an item with + the same key is already in the hash table.] + + SideEffects [None] + + SeeAlso [cuddLevelQueueEnqueue] + +******************************************************************************/ +static int +hashInsert( + DdLevelQueue * queue, + DdQueueItem * item) +{ + int result; + int posn; + + if (queue->size > queue->maxsize) { + result = hashResize(queue); + if (result == 0) return(0); + } + + posn = lqHash(item->key,queue->shift); + item->cnext = queue->buckets[posn]; + queue->buckets[posn] = item; + + return(1); + +} /* end of hashInsert */ + + +/**Function******************************************************************** + + Synopsis [Removes an item from the hash table of a level queue.] + + Description [Removes an item from the hash table of a level queue. + Nothing is done if the item is not in the table.] + + SideEffects [None] + + SeeAlso [cuddLevelQueueDequeue hashInsert] + +******************************************************************************/ +static void +hashDelete( + DdLevelQueue * queue, + DdQueueItem * item) +{ + int posn; + DdQueueItem *prevItem; + + posn = lqHash(item->key,queue->shift); + prevItem = queue->buckets[posn]; + + if (prevItem == NULL) return; + if (prevItem == item) { + queue->buckets[posn] = prevItem->cnext; + return; + } + + while (prevItem->cnext != NULL) { + if (prevItem->cnext == item) { + prevItem->cnext = item->cnext; + return; + } + prevItem = prevItem->cnext; + } + return; + +} /* end of hashDelete */ + + +/**Function******************************************************************** + + Synopsis [Resizes the hash table of a level queue.] + + Description [Resizes the hash table of a level queue. Returns 1 if + successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [hashInsert] + +******************************************************************************/ +static int +hashResize( + DdLevelQueue * queue) +{ + int j; + int posn; + DdQueueItem *item; + DdQueueItem *next; + int numBuckets; +#ifdef __osf__ +#pragma pointer_size save +#pragma pointer_size short +#endif + DdQueueItem **buckets; + DdQueueItem **oldBuckets = queue->buckets; +#ifdef __osf__ +#pragma pointer_size restore +#endif + int shift; + int oldNumBuckets = queue->numBuckets; + extern void (*MMoutOfMemory)(long); + void (*saveHandler)(long); + + /* Compute the new size of the subtable. */ + numBuckets = oldNumBuckets << 1; + saveHandler = MMoutOfMemory; + MMoutOfMemory = Cudd_OutOfMem; +#ifdef __osf__ +#pragma pointer_size save +#pragma pointer_size short +#endif + buckets = queue->buckets = ALLOC(DdQueueItem *, numBuckets); + if (buckets == NULL) { + queue->maxsize <<= 1; + return(1); + } + + queue->numBuckets = numBuckets; + shift = --(queue->shift); + queue->maxsize <<= 1; + memset(buckets, 0, numBuckets * sizeof(DdQueueItem *)); +#ifdef __osf__ +#pragma pointer_size restore +#endif + for (j = 0; j < oldNumBuckets; j++) { + item = oldBuckets[j]; + while (item != NULL) { + next = item->cnext; + posn = lqHash(item->key, shift); + item->cnext = buckets[posn]; + buckets[posn] = item; + item = next; + } + } + FREE(oldBuckets); + return(1); + +} /* end of hashResize */ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddLinear.c b/abc_with_bb_support/src/bdd/cudd/cuddLinear.c new file mode 100644 index 000000000..ade3db0e4 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddLinear.c @@ -0,0 +1,1333 @@ +/**CFile*********************************************************************** + + FileName [cuddLinear.c] + + PackageName [cudd] + + Synopsis [Functions for DD reduction by linear transformations.] + + Description [ Internal procedures included in this module: +
        +
      • cuddLinearAndSifting() +
      + Static procedures included in this module: +
        +
      • ddLinearUniqueCompare() +
      • ddLinearAndSiftingAux() +
      • ddLinearAndSiftingUp() +
      • ddLinearAndSiftingDown() +
      • ddLinearAndSiftingBackward() +
      • ddUndoMoves() +
      • ddUpdateInteractionMatrix() +
      • cuddLinearInPlace() +
      • cuddInitLinear() +
      • cuddResizeLinear() +
      • cuddXorLinear() +
      ] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define CUDD_SWAP_MOVE 0 +#define CUDD_LINEAR_TRANSFORM_MOVE 1 +#define CUDD_INVERSE_TRANSFORM_MOVE 2 +#if SIZEOF_LONG == 8 +#define BPL 64 +#define LOGBPL 6 +#else +#define BPL 32 +#define LOGBPL 5 +#endif + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddLinear.c,v 1.1.1.1 2003/02/24 22:23:52 wjiang Exp $"; +#endif + +static int *entry; + +#ifdef DD_STATS +extern int ddTotalNumberSwapping; +extern int ddTotalNISwaps; +static int ddTotalNumberLinearTr; +#endif + +#ifdef DD_DEBUG +static int zero = 0; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int ddLinearUniqueCompare ARGS((int *ptrX, int *ptrY)); +static int ddLinearAndSiftingAux ARGS((DdManager *table, int x, int xLow, int xHigh)); +static Move * ddLinearAndSiftingUp ARGS((DdManager *table, int y, int xLow, Move *prevMoves)); +static Move * ddLinearAndSiftingDown ARGS((DdManager *table, int x, int xHigh, Move *prevMoves)); +static int ddLinearAndSiftingBackward ARGS((DdManager *table, int size, Move *moves)); +static Move* ddUndoMoves ARGS((DdManager *table, Move *moves)); +static int cuddLinearInPlace ARGS((DdManager *table, int x, int y)); +static void ddUpdateInteractionMatrix ARGS((DdManager *table, int xindex, int yindex)); +static int cuddInitLinear ARGS((DdManager *table)); +static int cuddResizeLinear ARGS((DdManager *table)); +static void cuddXorLinear ARGS((DdManager *table, int x, int y)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Prints the linear transform matrix.] + + Description [Prints the linear transform matrix. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +int +Cudd_PrintLinear( + DdManager * table) +{ + int i,j,k; + int retval; + int nvars = table->linearSize; + int wordsPerRow = ((nvars - 1) >> LOGBPL) + 1; + long word; + + for (i = 0; i < nvars; i++) { + for (j = 0; j < wordsPerRow; j++) { + word = table->linear[i*wordsPerRow + j]; + for (k = 0; k < BPL; k++) { + retval = fprintf(table->out,"%ld",word & 1); + if (retval == 0) return(0); + word >>= 1; + } + } + retval = fprintf(table->out,"\n"); + if (retval == 0) return(0); + } + return(1); + +} /* end of Cudd_PrintLinear */ + + +/**Function******************************************************************** + + Synopsis [Reads an entry of the linear transform matrix.] + + Description [Reads an entry of the linear transform matrix.] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +int +Cudd_ReadLinear( + DdManager * table /* CUDD manager */, + int x /* row index */, + int y /* column index */) +{ + int nvars = table->size; + int wordsPerRow = ((nvars - 1) >> LOGBPL) + 1; + long word; + int bit; + int result; + + assert(table->size == table->linearSize); + + word = wordsPerRow * x + (y >> LOGBPL); + bit = y & (BPL-1); + result = (int) ((table->linear[word] >> bit) & 1); + return(result); + +} /* end of Cudd_ReadLinear */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [BDD reduction based on combination of sifting and linear + transformations.] + + Description [BDD reduction based on combination of sifting and linear + transformations. Assumes that no dead nodes are present. +
        +
      1. Order all the variables according to the number of entries + in each unique table. +
      2. Sift the variable up and down, remembering each time the + total size of the DD heap. At each position, linear transformation + of the two adjacent variables is tried and is accepted if it reduces + the size of the DD. +
      3. Select the best permutation. +
      4. Repeat 3 and 4 for all variables. +
      + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +int +cuddLinearAndSifting( + DdManager * table, + int lower, + int upper) +{ + int i; + int *var; + int size; + int x; + int result; +#ifdef DD_STATS + int previousSize; +#endif + +#ifdef DD_STATS + ddTotalNumberLinearTr = 0; +#endif + + size = table->size; + + var = NULL; + entry = NULL; + if (table->linear == NULL) { + result = cuddInitLinear(table); + if (result == 0) goto cuddLinearAndSiftingOutOfMem; +#if 0 + (void) fprintf(table->out,"\n"); + result = Cudd_PrintLinear(table); + if (result == 0) goto cuddLinearAndSiftingOutOfMem; +#endif + } else if (table->size != table->linearSize) { + result = cuddResizeLinear(table); + if (result == 0) goto cuddLinearAndSiftingOutOfMem; +#if 0 + (void) fprintf(table->out,"\n"); + result = Cudd_PrintLinear(table); + if (result == 0) goto cuddLinearAndSiftingOutOfMem; +#endif + } + + /* Find order in which to sift variables. */ + entry = ALLOC(int,size); + if (entry == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddLinearAndSiftingOutOfMem; + } + var = ALLOC(int,size); + if (var == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddLinearAndSiftingOutOfMem; + } + + for (i = 0; i < size; i++) { + x = table->perm[i]; + entry[i] = table->subtables[x].keys; + var[i] = i; + } + + qsort((void *)var,size,sizeof(int),(int (*)(const void *, const void *))ddLinearUniqueCompare); + + /* Now sift. */ + for (i = 0; i < ddMin(table->siftMaxVar,size); i++) { + x = table->perm[var[i]]; + if (x < lower || x > upper) continue; +#ifdef DD_STATS + previousSize = table->keys - table->isolated; +#endif + result = ddLinearAndSiftingAux(table,x,lower,upper); + if (!result) goto cuddLinearAndSiftingOutOfMem; +#ifdef DD_STATS + if (table->keys < (unsigned) previousSize + table->isolated) { + (void) fprintf(table->out,"-"); + } else if (table->keys > (unsigned) previousSize + table->isolated) { + (void) fprintf(table->out,"+"); /* should never happen */ + (void) fprintf(table->out,"\nSize increased from %d to %d while sifting variable %d\n", previousSize, table->keys - table->isolated, var[i]); + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif +#ifdef DD_DEBUG + (void) Cudd_DebugCheck(table); +#endif + } + + FREE(var); + FREE(entry); + +#ifdef DD_STATS + (void) fprintf(table->out,"\n#:L_LINSIFT %8d: linear trans.", + ddTotalNumberLinearTr); +#endif + + return(1); + +cuddLinearAndSiftingOutOfMem: + + if (entry != NULL) FREE(entry); + if (var != NULL) FREE(var); + + return(0); + +} /* end of cuddLinearAndSifting */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Comparison function used by qsort.] + + Description [Comparison function used by qsort to order the + variables according to the number of keys in the subtables. + Returns the difference in number of keys between the two + variables being compared.] + + SideEffects [None] + +******************************************************************************/ +static int +ddLinearUniqueCompare( + int * ptrX, + int * ptrY) +{ +#if 0 + if (entry[*ptrY] == entry[*ptrX]) { + return((*ptrX) - (*ptrY)); + } +#endif + return(entry[*ptrY] - entry[*ptrX]); + +} /* end of ddLinearUniqueCompare */ + + +/**Function******************************************************************** + + Synopsis [Given xLow <= x <= xHigh moves x up and down between the + boundaries.] + + Description [Given xLow <= x <= xHigh moves x up and down between the + boundaries. At each step a linear transformation is tried, and, if it + decreases the size of the DD, it is accepted. Finds the best position + and does the required changes. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddLinearAndSiftingAux( + DdManager * table, + int x, + int xLow, + int xHigh) +{ + + Move *move; + Move *moveUp; /* list of up moves */ + Move *moveDown; /* list of down moves */ + int initialSize; + int result; + + initialSize = table->keys - table->isolated; + + moveDown = NULL; + moveUp = NULL; + + if (x == xLow) { + moveDown = ddLinearAndSiftingDown(table,x,xHigh,NULL); + /* At this point x --> xHigh unless bounding occurred. */ + if (moveDown == (Move *) CUDD_OUT_OF_MEM) goto ddLinearAndSiftingAuxOutOfMem; + /* Move backward and stop at best position. */ + result = ddLinearAndSiftingBackward(table,initialSize,moveDown); + if (!result) goto ddLinearAndSiftingAuxOutOfMem; + + } else if (x == xHigh) { + moveUp = ddLinearAndSiftingUp(table,x,xLow,NULL); + /* At this point x --> xLow unless bounding occurred. */ + if (moveUp == (Move *) CUDD_OUT_OF_MEM) goto ddLinearAndSiftingAuxOutOfMem; + /* Move backward and stop at best position. */ + result = ddLinearAndSiftingBackward(table,initialSize,moveUp); + if (!result) goto ddLinearAndSiftingAuxOutOfMem; + + } else if ((x - xLow) > (xHigh - x)) { /* must go down first: shorter */ + moveDown = ddLinearAndSiftingDown(table,x,xHigh,NULL); + /* At this point x --> xHigh unless bounding occurred. */ + if (moveDown == (Move *) CUDD_OUT_OF_MEM) goto ddLinearAndSiftingAuxOutOfMem; + moveUp = ddUndoMoves(table,moveDown); +#ifdef DD_DEBUG + assert(moveUp == NULL || moveUp->x == x); +#endif + moveUp = ddLinearAndSiftingUp(table,x,xLow,moveUp); + if (moveUp == (Move *) CUDD_OUT_OF_MEM) goto ddLinearAndSiftingAuxOutOfMem; + /* Move backward and stop at best position. */ + result = ddLinearAndSiftingBackward(table,initialSize,moveUp); + if (!result) goto ddLinearAndSiftingAuxOutOfMem; + + } else { /* must go up first: shorter */ + moveUp = ddLinearAndSiftingUp(table,x,xLow,NULL); + /* At this point x --> xLow unless bounding occurred. */ + if (moveUp == (Move *) CUDD_OUT_OF_MEM) goto ddLinearAndSiftingAuxOutOfMem; + moveDown = ddUndoMoves(table,moveUp); +#ifdef DD_DEBUG + assert(moveDown == NULL || moveDown->y == x); +#endif + moveDown = ddLinearAndSiftingDown(table,x,xHigh,moveDown); + if (moveDown == (Move *) CUDD_OUT_OF_MEM) goto ddLinearAndSiftingAuxOutOfMem; + /* Move backward and stop at best position. */ + result = ddLinearAndSiftingBackward(table,initialSize,moveDown); + if (!result) goto ddLinearAndSiftingAuxOutOfMem; + } + + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocNode(table, (DdNode *) moveDown); + moveDown = move; + } + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocNode(table, (DdNode *) moveUp); + moveUp = move; + } + + return(1); + +ddLinearAndSiftingAuxOutOfMem: + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocNode(table, (DdNode *) moveDown); + moveDown = move; + } + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocNode(table, (DdNode *) moveUp); + moveUp = move; + } + + return(0); + +} /* end of ddLinearAndSiftingAux */ + + +/**Function******************************************************************** + + Synopsis [Sifts a variable up and applies linear transformations.] + + Description [Sifts a variable up and applies linear transformations. + Moves y up until either it reaches the bound (xLow) or the size of + the DD heap increases too much. Returns the set of moves in case of + success; NULL if memory is full.] + + SideEffects [None] + +******************************************************************************/ +static Move * +ddLinearAndSiftingUp( + DdManager * table, + int y, + int xLow, + Move * prevMoves) +{ + Move *moves; + Move *move; + int x; + int size, newsize; + int limitSize; + int xindex, yindex; + int isolated; + int L; /* lower bound on DD size */ +#ifdef DD_DEBUG + int checkL; + int z; + int zindex; +#endif + + moves = prevMoves; + yindex = table->invperm[y]; + + /* Initialize the lower bound. + ** The part of the DD below y will not change. + ** The part of the DD above y that does not interact with y will not + ** change. The rest may vanish in the best case, except for + ** the nodes at level xLow, which will not vanish, regardless. + */ + limitSize = L = table->keys - table->isolated; + for (x = xLow + 1; x < y; x++) { + xindex = table->invperm[x]; + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[xindex]->ref == 1; + L -= table->subtables[x].keys - isolated; + } + } + isolated = table->vars[yindex]->ref == 1; + L -= table->subtables[y].keys - isolated; + + x = cuddNextLow(table,y); + while (x >= xLow && L <= limitSize) { + xindex = table->invperm[x]; +#ifdef DD_DEBUG + checkL = table->keys - table->isolated; + for (z = xLow + 1; z < y; z++) { + zindex = table->invperm[z]; + if (cuddTestInteract(table,zindex,yindex)) { + isolated = table->vars[zindex]->ref == 1; + checkL -= table->subtables[z].keys - isolated; + } + } + isolated = table->vars[yindex]->ref == 1; + checkL -= table->subtables[y].keys - isolated; + if (L != checkL) { + (void) fprintf(table->out, "checkL(%d) != L(%d)\n",checkL,L); + } +#endif + size = cuddSwapInPlace(table,x,y); + if (size == 0) goto ddLinearAndSiftingUpOutOfMem; + newsize = cuddLinearInPlace(table,x,y); + if (newsize == 0) goto ddLinearAndSiftingUpOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddLinearAndSiftingUpOutOfMem; + move->x = x; + move->y = y; + move->next = moves; + moves = move; + move->flags = CUDD_SWAP_MOVE; + if (newsize >= size) { + /* Undo transformation. The transformation we apply is + ** its own inverse. Hence, we just apply the transformation + ** again. + */ + newsize = cuddLinearInPlace(table,x,y); + if (newsize == 0) goto ddLinearAndSiftingUpOutOfMem; +#ifdef DD_DEBUG + if (newsize != size) { + (void) fprintf(table->out,"Change in size after identity transformation! From %d to %d\n",size,newsize); + } +#endif + } else if (cuddTestInteract(table,xindex,yindex)) { + size = newsize; + move->flags = CUDD_LINEAR_TRANSFORM_MOVE; + ddUpdateInteractionMatrix(table,xindex,yindex); + } + move->size = size; + /* Update the lower bound. */ + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[xindex]->ref == 1; + L += table->subtables[y].keys - isolated; + } + if ((double) size > (double) limitSize * table->maxGrowth) break; + if (size < limitSize) limitSize = size; + y = x; + x = cuddNextLow(table,y); + } + return(moves); + +ddLinearAndSiftingUpOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *) moves); + moves = move; + } + return((Move *) CUDD_OUT_OF_MEM); + +} /* end of ddLinearAndSiftingUp */ + + +/**Function******************************************************************** + + Synopsis [Sifts a variable down and applies linear transformations.] + + Description [Sifts a variable down and applies linear + transformations. Moves x down until either it reaches the bound + (xHigh) or the size of the DD heap increases too much. Returns the + set of moves in case of success; NULL if memory is full.] + + SideEffects [None] + +******************************************************************************/ +static Move * +ddLinearAndSiftingDown( + DdManager * table, + int x, + int xHigh, + Move * prevMoves) +{ + Move *moves; + Move *move; + int y; + int size, newsize; + int R; /* upper bound on node decrease */ + int limitSize; + int xindex, yindex; + int isolated; +#ifdef DD_DEBUG + int checkR; + int z; + int zindex; +#endif + + moves = prevMoves; + /* Initialize R */ + xindex = table->invperm[x]; + limitSize = size = table->keys - table->isolated; + R = 0; + for (y = xHigh; y > x; y--) { + yindex = table->invperm[y]; + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[yindex]->ref == 1; + R += table->subtables[y].keys - isolated; + } + } + + y = cuddNextHigh(table,x); + while (y <= xHigh && size - R < limitSize) { +#ifdef DD_DEBUG + checkR = 0; + for (z = xHigh; z > x; z--) { + zindex = table->invperm[z]; + if (cuddTestInteract(table,xindex,zindex)) { + isolated = table->vars[zindex]->ref == 1; + checkR += table->subtables[z].keys - isolated; + } + } + if (R != checkR) { + (void) fprintf(table->out, "checkR(%d) != R(%d)\n",checkR,R); + } +#endif + /* Update upper bound on node decrease. */ + yindex = table->invperm[y]; + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[yindex]->ref == 1; + R -= table->subtables[y].keys - isolated; + } + size = cuddSwapInPlace(table,x,y); + if (size == 0) goto ddLinearAndSiftingDownOutOfMem; + newsize = cuddLinearInPlace(table,x,y); + if (newsize == 0) goto ddLinearAndSiftingDownOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddLinearAndSiftingDownOutOfMem; + move->x = x; + move->y = y; + move->next = moves; + moves = move; + move->flags = CUDD_SWAP_MOVE; + if (newsize >= size) { + /* Undo transformation. The transformation we apply is + ** its own inverse. Hence, we just apply the transformation + ** again. + */ + newsize = cuddLinearInPlace(table,x,y); + if (newsize == 0) goto ddLinearAndSiftingDownOutOfMem; + if (newsize != size) { + (void) fprintf(table->out,"Change in size after identity transformation! From %d to %d\n",size,newsize); + } + } else if (cuddTestInteract(table,xindex,yindex)) { + size = newsize; + move->flags = CUDD_LINEAR_TRANSFORM_MOVE; + ddUpdateInteractionMatrix(table,xindex,yindex); + } + move->size = size; + if ((double) size > (double) limitSize * table->maxGrowth) break; + if (size < limitSize) limitSize = size; + x = y; + y = cuddNextHigh(table,x); + } + return(moves); + +ddLinearAndSiftingDownOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *) moves); + moves = move; + } + return((Move *) CUDD_OUT_OF_MEM); + +} /* end of ddLinearAndSiftingDown */ + + +/**Function******************************************************************** + + Synopsis [Given a set of moves, returns the DD heap to the order + giving the minimum size.] + + Description [Given a set of moves, returns the DD heap to the + position giving the minimum size. In case of ties, returns to the + closest position giving the minimum size. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddLinearAndSiftingBackward( + DdManager * table, + int size, + Move * moves) +{ + Move *move; + int res; + + for (move = moves; move != NULL; move = move->next) { + if (move->size < size) { + size = move->size; + } + } + + for (move = moves; move != NULL; move = move->next) { + if (move->size == size) return(1); + if (move->flags == CUDD_LINEAR_TRANSFORM_MOVE) { + res = cuddLinearInPlace(table,(int)move->x,(int)move->y); + if (!res) return(0); + } + res = cuddSwapInPlace(table,(int)move->x,(int)move->y); + if (!res) return(0); + if (move->flags == CUDD_INVERSE_TRANSFORM_MOVE) { + res = cuddLinearInPlace(table,(int)move->x,(int)move->y); + if (!res) return(0); + } + } + + return(1); + +} /* end of ddLinearAndSiftingBackward */ + + +/**Function******************************************************************** + + Synopsis [Given a set of moves, returns the DD heap to the order + in effect before the moves.] + + Description [Given a set of moves, returns the DD heap to the + order in effect before the moves. Returns 1 in case of success; + 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static Move* +ddUndoMoves( + DdManager * table, + Move * moves) +{ + Move *invmoves = NULL; + Move *move; + Move *invmove; + int size; + + for (move = moves; move != NULL; move = move->next) { + invmove = (Move *) cuddDynamicAllocNode(table); + if (invmove == NULL) goto ddUndoMovesOutOfMem; + invmove->x = move->x; + invmove->y = move->y; + invmove->next = invmoves; + invmoves = invmove; + if (move->flags == CUDD_SWAP_MOVE) { + invmove->flags = CUDD_SWAP_MOVE; + size = cuddSwapInPlace(table,(int)move->x,(int)move->y); + if (!size) goto ddUndoMovesOutOfMem; + } else if (move->flags == CUDD_LINEAR_TRANSFORM_MOVE) { + invmove->flags = CUDD_INVERSE_TRANSFORM_MOVE; + size = cuddLinearInPlace(table,(int)move->x,(int)move->y); + if (!size) goto ddUndoMovesOutOfMem; + size = cuddSwapInPlace(table,(int)move->x,(int)move->y); + if (!size) goto ddUndoMovesOutOfMem; + } else { /* must be CUDD_INVERSE_TRANSFORM_MOVE */ +#ifdef DD_DEBUG + (void) fprintf(table->err,"Unforseen event in ddUndoMoves!\n"); +#endif + invmove->flags = CUDD_LINEAR_TRANSFORM_MOVE; + size = cuddSwapInPlace(table,(int)move->x,(int)move->y); + if (!size) goto ddUndoMovesOutOfMem; + size = cuddLinearInPlace(table,(int)move->x,(int)move->y); + if (!size) goto ddUndoMovesOutOfMem; + } + invmove->size = size; + } + + return(invmoves); + +ddUndoMovesOutOfMem: + while (invmoves != NULL) { + move = invmoves->next; + cuddDeallocNode(table, (DdNode *) invmoves); + invmoves = move; + } + return((Move *) CUDD_OUT_OF_MEM); + +} /* end of ddUndoMoves */ + + +/**Function******************************************************************** + + Synopsis [Linearly combines two adjacent variables.] + + Description [Linearly combines two adjacent variables. Specifically, + replaces the top variable with the exclusive nor of the two variables. + It assumes that no dead nodes are present on entry to this + procedure. The procedure then guarantees that no dead nodes will be + present when it terminates. cuddLinearInPlace assumes that x < + y. Returns the number of keys in the table if successful; 0 + otherwise.] + + SideEffects [The two subtables corrresponding to variables x and y are + modified. The global counters of the unique table are also affected.] + + SeeAlso [cuddSwapInPlace] + +******************************************************************************/ +static int +cuddLinearInPlace( + DdManager * table, + int x, + int y) +{ + DdNodePtr *xlist, *ylist; + int xindex, yindex; + int xslots, yslots; + int xshift, yshift; + int oldxkeys, oldykeys; + int newxkeys, newykeys; + int comple, newcomplement; + int i; + int posn; + int isolated; + DdNode *f,*f0,*f1,*f01,*f00,*f11,*f10,*newf1,*newf0; + DdNode *g,*next,*last; + DdNodePtr *previousP; + DdNode *tmp; + DdNode *sentinel = &(table->sentinel); +#if DD_DEBUG + int count, idcheck; +#endif + +#ifdef DD_DEBUG + assert(x < y); + assert(cuddNextHigh(table,x) == y); + assert(table->subtables[x].keys != 0); + assert(table->subtables[y].keys != 0); + assert(table->subtables[x].dead == 0); + assert(table->subtables[y].dead == 0); +#endif + + xindex = table->invperm[x]; + yindex = table->invperm[y]; + + if (cuddTestInteract(table,xindex,yindex)) { +#ifdef DD_STATS + ddTotalNumberLinearTr++; +#endif + /* Get parameters of x subtable. */ + xlist = table->subtables[x].nodelist; + oldxkeys = table->subtables[x].keys; + xslots = table->subtables[x].slots; + xshift = table->subtables[x].shift; + + /* Get parameters of y subtable. */ + ylist = table->subtables[y].nodelist; + oldykeys = table->subtables[y].keys; + yslots = table->subtables[y].slots; + yshift = table->subtables[y].shift; + + newxkeys = 0; + newykeys = oldykeys; + + /* Check whether the two projection functions involved in this + ** swap are isolated. At the end, we'll be able to tell how many + ** isolated projection functions are there by checking only these + ** two functions again. This is done to eliminate the isolated + ** projection functions from the node count. + */ + isolated = - ((table->vars[xindex]->ref == 1) + + (table->vars[yindex]->ref == 1)); + + /* The nodes in the x layer are put in a chain. + ** The chain is handled as a FIFO; g points to the beginning and + ** last points to the end. + */ + g = NULL; + for (i = 0; i < xslots; i++) { + f = xlist[i]; + if (f == sentinel) continue; + xlist[i] = sentinel; + if (g == NULL) { + g = f; + } else { + last->next = f; + } + while ((next = f->next) != sentinel) { + f = next; + } /* while there are elements in the collision chain */ + last = f; + } /* for each slot of the x subtable */ + last->next = NULL; + +#ifdef DD_COUNT + table->swapSteps += oldxkeys; +#endif + /* Take care of the x nodes that must be re-expressed. + ** They form a linked list pointed by g. + */ + f = g; + while (f != NULL) { + next = f->next; + /* Find f1, f0, f11, f10, f01, f00. */ + f1 = cuddT(f); +#ifdef DD_DEBUG + assert(!(Cudd_IsComplement(f1))); +#endif + if ((int) f1->index == yindex) { + f11 = cuddT(f1); f10 = cuddE(f1); + } else { + f11 = f10 = f1; + } +#ifdef DD_DEBUG + assert(!(Cudd_IsComplement(f11))); +#endif + f0 = cuddE(f); + comple = Cudd_IsComplement(f0); + f0 = Cudd_Regular(f0); + if ((int) f0->index == yindex) { + f01 = cuddT(f0); f00 = cuddE(f0); + } else { + f01 = f00 = f0; + } + if (comple) { + f01 = Cudd_Not(f01); + f00 = Cudd_Not(f00); + } + /* Decrease ref count of f1. */ + cuddSatDec(f1->ref); + /* Create the new T child. */ + if (f11 == f00) { + newf1 = f11; + cuddSatInc(newf1->ref); + } else { + /* Check ylist for triple (yindex,f11,f00). */ + posn = ddHash(f11, f00, yshift); + /* For each element newf1 in collision list ylist[posn]. */ + previousP = &(ylist[posn]); + newf1 = *previousP; + while (f11 < cuddT(newf1)) { + previousP = &(newf1->next); + newf1 = *previousP; + } + while (f11 == cuddT(newf1) && f00 < cuddE(newf1)) { + previousP = &(newf1->next); + newf1 = *previousP; + } + if (cuddT(newf1) == f11 && cuddE(newf1) == f00) { + cuddSatInc(newf1->ref); + } else { /* no match */ + newf1 = cuddDynamicAllocNode(table); + if (newf1 == NULL) + goto cuddLinearOutOfMem; + newf1->index = yindex; newf1->ref = 1; + cuddT(newf1) = f11; + cuddE(newf1) = f00; + /* Insert newf1 in the collision list ylist[posn]; + ** increase the ref counts of f11 and f00. + */ + newykeys++; + newf1->next = *previousP; + *previousP = newf1; + cuddSatInc(f11->ref); + tmp = Cudd_Regular(f00); + cuddSatInc(tmp->ref); + } + } + cuddT(f) = newf1; +#ifdef DD_DEBUG + assert(!(Cudd_IsComplement(newf1))); +#endif + + /* Do the same for f0, keeping complement dots into account. */ + /* decrease ref count of f0 */ + tmp = Cudd_Regular(f0); + cuddSatDec(tmp->ref); + /* create the new E child */ + if (f01 == f10) { + newf0 = f01; + tmp = Cudd_Regular(newf0); + cuddSatInc(tmp->ref); + } else { + /* make sure f01 is regular */ + newcomplement = Cudd_IsComplement(f01); + if (newcomplement) { + f01 = Cudd_Not(f01); + f10 = Cudd_Not(f10); + } + /* Check ylist for triple (yindex,f01,f10). */ + posn = ddHash(f01, f10, yshift); + /* For each element newf0 in collision list ylist[posn]. */ + previousP = &(ylist[posn]); + newf0 = *previousP; + while (f01 < cuddT(newf0)) { + previousP = &(newf0->next); + newf0 = *previousP; + } + while (f01 == cuddT(newf0) && f10 < cuddE(newf0)) { + previousP = &(newf0->next); + newf0 = *previousP; + } + if (cuddT(newf0) == f01 && cuddE(newf0) == f10) { + cuddSatInc(newf0->ref); + } else { /* no match */ + newf0 = cuddDynamicAllocNode(table); + if (newf0 == NULL) + goto cuddLinearOutOfMem; + newf0->index = yindex; newf0->ref = 1; + cuddT(newf0) = f01; + cuddE(newf0) = f10; + /* Insert newf0 in the collision list ylist[posn]; + ** increase the ref counts of f01 and f10. + */ + newykeys++; + newf0->next = *previousP; + *previousP = newf0; + cuddSatInc(f01->ref); + tmp = Cudd_Regular(f10); + cuddSatInc(tmp->ref); + } + if (newcomplement) { + newf0 = Cudd_Not(newf0); + } + } + cuddE(f) = newf0; + + /* Re-insert the modified f in xlist. + ** The modified f does not already exists in xlist. + ** (Because of the uniqueness of the cofactors.) + */ + posn = ddHash(newf1, newf0, xshift); + newxkeys++; + previousP = &(xlist[posn]); + tmp = *previousP; + while (newf1 < cuddT(tmp)) { + previousP = &(tmp->next); + tmp = *previousP; + } + while (newf1 == cuddT(tmp) && newf0 < cuddE(tmp)) { + previousP = &(tmp->next); + tmp = *previousP; + } + f->next = *previousP; + *previousP = f; + f = next; + } /* while f != NULL */ + + /* GC the y layer. */ + + /* For each node f in ylist. */ + for (i = 0; i < yslots; i++) { + previousP = &(ylist[i]); + f = *previousP; + while (f != sentinel) { + next = f->next; + if (f->ref == 0) { + tmp = cuddT(f); + cuddSatDec(tmp->ref); + tmp = Cudd_Regular(cuddE(f)); + cuddSatDec(tmp->ref); + cuddDeallocNode(table,f); + newykeys--; + } else { + *previousP = f; + previousP = &(f->next); + } + f = next; + } /* while f */ + *previousP = sentinel; + } /* for every collision list */ + +#if DD_DEBUG +#if 0 + (void) fprintf(table->out,"Linearly combining %d and %d\n",x,y); +#endif + count = 0; + idcheck = 0; + for (i = 0; i < yslots; i++) { + f = ylist[i]; + while (f != sentinel) { + count++; + if (f->index != (DdHalfWord) yindex) + idcheck++; + f = f->next; + } + } + if (count != newykeys) { + fprintf(table->err,"Error in finding newykeys\toldykeys = %d\tnewykeys = %d\tactual = %d\n",oldykeys,newykeys,count); + } + if (idcheck != 0) + fprintf(table->err,"Error in id's of ylist\twrong id's = %d\n",idcheck); + count = 0; + idcheck = 0; + for (i = 0; i < xslots; i++) { + f = xlist[i]; + while (f != sentinel) { + count++; + if (f->index != (DdHalfWord) xindex) + idcheck++; + f = f->next; + } + } + if (count != newxkeys || newxkeys != oldxkeys) { + fprintf(table->err,"Error in finding newxkeys\toldxkeys = %d \tnewxkeys = %d \tactual = %d\n",oldxkeys,newxkeys,count); + } + if (idcheck != 0) + fprintf(table->err,"Error in id's of xlist\twrong id's = %d\n",idcheck); +#endif + + isolated += (table->vars[xindex]->ref == 1) + + (table->vars[yindex]->ref == 1); + table->isolated += isolated; + + /* Set the appropriate fields in table. */ + table->subtables[y].keys = newykeys; + + /* Here we should update the linear combination table + ** to record that x <- x EXNOR y. This is done by complementing + ** the (x,y) entry of the table. + */ + + table->keys += newykeys - oldykeys; + + cuddXorLinear(table,xindex,yindex); + } + +#ifdef DD_DEBUG + if (zero) { + (void) Cudd_DebugCheck(table); + } +#endif + + return(table->keys - table->isolated); + +cuddLinearOutOfMem: + (void) fprintf(table->err,"Error: cuddLinearInPlace out of memory\n"); + + return (0); + +} /* end of cuddLinearInPlace */ + + +/**Function******************************************************************** + + Synopsis [Updates the interaction matrix.] + + Description [] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +static void +ddUpdateInteractionMatrix( + DdManager * table, + int xindex, + int yindex) +{ + int i; + for (i = 0; i < yindex; i++) { + if (i != xindex && cuddTestInteract(table,i,yindex)) { + if (i < xindex) { + cuddSetInteract(table,i,xindex); + } else { + cuddSetInteract(table,xindex,i); + } + } + } + for (i = yindex+1; i < table->size; i++) { + if (i != xindex && cuddTestInteract(table,yindex,i)) { + if (i < xindex) { + cuddSetInteract(table,i,xindex); + } else { + cuddSetInteract(table,xindex,i); + } + } + } + +} /* end of ddUpdateInteractionMatrix */ + + +/**Function******************************************************************** + + Synopsis [Initializes the linear transform matrix.] + + Description [] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +static int +cuddInitLinear( + DdManager * table) +{ + int words; + int wordsPerRow; + int nvars; + int word; + int bit; + int i; + long *linear; + + nvars = table->size; + wordsPerRow = ((nvars - 1) >> LOGBPL) + 1; + words = wordsPerRow * nvars; + table->linear = linear = ALLOC(long,words); + if (linear == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } + table->memused += words * sizeof(long); + table->linearSize = nvars; + for (i = 0; i < words; i++) linear[i] = 0; + for (i = 0; i < nvars; i++) { + word = wordsPerRow * i + (i >> LOGBPL); + bit = i & (BPL-1); + linear[word] = 1 << bit; + } + return(1); + +} /* end of cuddInitLinear */ + + +/**Function******************************************************************** + + Synopsis [Resizes the linear transform matrix.] + + Description [] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +static int +cuddResizeLinear( + DdManager * table) +{ + int words,oldWords; + int wordsPerRow,oldWordsPerRow; + int nvars,oldNvars; + int word,oldWord; + int bit; + int i,j; + long *linear,*oldLinear; + + oldNvars = table->linearSize; + oldWordsPerRow = ((oldNvars - 1) >> LOGBPL) + 1; + oldWords = oldWordsPerRow * oldNvars; + oldLinear = table->linear; + + nvars = table->size; + wordsPerRow = ((nvars - 1) >> LOGBPL) + 1; + words = wordsPerRow * nvars; + table->linear = linear = ALLOC(long,words); + if (linear == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } + table->memused += (words - oldWords) * sizeof(long); + for (i = 0; i < words; i++) linear[i] = 0; + + /* Copy old matrix. */ + for (i = 0; i < oldNvars; i++) { + for (j = 0; j < oldWordsPerRow; j++) { + oldWord = oldWordsPerRow * i + j; + word = wordsPerRow * i + j; + linear[word] = oldLinear[oldWord]; + } + } + FREE(oldLinear); + + /* Add elements to the diagonal. */ + for (i = oldNvars; i < nvars; i++) { + word = wordsPerRow * i + (i >> LOGBPL); + bit = i & (BPL-1); + linear[word] = 1 << bit; + } + table->linearSize = nvars; + + return(1); + +} /* end of cuddResizeLinear */ + + +/**Function******************************************************************** + + Synopsis [XORs two rows of the linear transform matrix.] + + Description [XORs two rows of the linear transform matrix and replaces + the first row with the result.] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +static void +cuddXorLinear( + DdManager * table, + int x, + int y) +{ + int i; + int nvars = table->size; + int wordsPerRow = ((nvars - 1) >> LOGBPL) + 1; + int xstart = wordsPerRow * x; + int ystart = wordsPerRow * y; + long *linear = table->linear; + + for (i = 0; i < wordsPerRow; i++) { + linear[xstart+i] ^= linear[ystart+i]; + } + +} /* end of cuddXorLinear */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddLiteral.c b/abc_with_bb_support/src/bdd/cudd/cuddLiteral.c new file mode 100644 index 000000000..89ebd8025 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddLiteral.c @@ -0,0 +1,237 @@ +/**CFile*********************************************************************** + + FileName [cuddLiteral.c] + + PackageName [cudd] + + Synopsis [Functions for manipulation of literal sets represented by + BDDs.] + + Description [External procedures included in this file: +
        +
      • Cudd_bddLiteralSetIntersection() +
      + Internal procedures included in this file: +
        +
      • cuddBddLiteralSetIntersectionRecur() +
      ] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddLiteral.c,v 1.1.1.1 2003/02/24 22:23:52 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Computes the intesection of two sets of literals + represented as BDDs.] + + Description [Computes the intesection of two sets of literals + represented as BDDs. Each set is represented as a cube of the + literals in the set. The empty set is represented by the constant 1. + No variable can be simultaneously present in both phases in a set. + Returns a pointer to the BDD representing the intersected sets, if + successful; NULL otherwise.] + + SideEffects [None] + +******************************************************************************/ +DdNode * +Cudd_bddLiteralSetIntersection( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddBddLiteralSetIntersectionRecur(dd,f,g); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_bddLiteralSetIntersection */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of + Cudd_bddLiteralSetIntersection.] + + Description [Performs the recursive step of + Cudd_bddLiteralSetIntersection. Scans the cubes for common variables, + and checks whether they agree in phase. Returns a pointer to the + resulting cube if successful; NULL otherwise.] + + SideEffects [None] + +******************************************************************************/ +DdNode * +cuddBddLiteralSetIntersectionRecur( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *res, *tmp; + DdNode *F, *G; + DdNode *fc, *gc; + DdNode *one; + DdNode *zero; + unsigned int topf, topg, comple; + int phasef, phaseg; + + statLine(dd); + if (f == g) return(f); + + F = Cudd_Regular(f); + G = Cudd_Regular(g); + one = DD_ONE(dd); + + /* Here f != g. If F == G, then f and g are complementary. + ** Since they are two cubes, this case only occurs when f == v, + ** g == v', and v is a variable or its complement. + */ + if (F == G) return(one); + + zero = Cudd_Not(one); + topf = cuddI(dd,F->index); + topg = cuddI(dd,G->index); + /* Look for a variable common to both cubes. If there are none, this + ** loop will stop when the constant node is reached in both cubes. + */ + while (topf != topg) { + if (topf < topg) { /* move down on f */ + comple = f != F; + f = cuddT(F); + if (comple) f = Cudd_Not(f); + if (f == zero) { + f = cuddE(F); + if (comple) f = Cudd_Not(f); + } + F = Cudd_Regular(f); + topf = cuddI(dd,F->index); + } else if (topg < topf) { + comple = g != G; + g = cuddT(G); + if (comple) g = Cudd_Not(g); + if (g == zero) { + g = cuddE(G); + if (comple) g = Cudd_Not(g); + } + G = Cudd_Regular(g); + topg = cuddI(dd,G->index); + } + } + + /* At this point, f == one <=> g == 1. It suffices to test one of them. */ + if (f == one) return(one); + + res = cuddCacheLookup2(dd,Cudd_bddLiteralSetIntersection,f,g); + if (res != NULL) { + return(res); + } + + /* Here f and g are both non constant and have the same top variable. */ + comple = f != F; + fc = cuddT(F); + phasef = 1; + if (comple) fc = Cudd_Not(fc); + if (fc == zero) { + fc = cuddE(F); + phasef = 0; + if (comple) fc = Cudd_Not(fc); + } + comple = g != G; + gc = cuddT(G); + phaseg = 1; + if (comple) gc = Cudd_Not(gc); + if (gc == zero) { + gc = cuddE(G); + phaseg = 0; + if (comple) gc = Cudd_Not(gc); + } + + tmp = cuddBddLiteralSetIntersectionRecur(dd,fc,gc); + if (tmp == NULL) { + return(NULL); + } + + if (phasef != phaseg) { + res = tmp; + } else { + cuddRef(tmp); + if (phasef == 0) { + res = cuddBddAndRecur(dd,Cudd_Not(dd->vars[F->index]),tmp); + } else { + res = cuddBddAndRecur(dd,dd->vars[F->index],tmp); + } + if (res == NULL) { + Cudd_RecursiveDeref(dd,tmp); + return(NULL); + } + cuddDeref(tmp); /* Just cuddDeref, because it is included in result */ + } + + cuddCacheInsert2(dd,Cudd_bddLiteralSetIntersection,f,g,res); + + return(res); + +} /* end of cuddBddLiteralSetIntersectionRecur */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddMatMult.c b/abc_with_bb_support/src/bdd/cudd/cuddMatMult.c new file mode 100644 index 000000000..18d7a303a --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddMatMult.c @@ -0,0 +1,680 @@ +/**CFile*********************************************************************** + + FileName [cuddMatMult.c] + + PackageName [cudd] + + Synopsis [Matrix multiplication functions.] + + Description [External procedures included in this module: +
        +
      • Cudd_addMatrixMultiply() +
      • Cudd_addTimesPlus() +
      • Cudd_addTriangle() +
      • Cudd_addOuterSum() +
      + Static procedures included in this module: +
        +
      • addMMRecur() +
      • addTriangleRecur() +
      • cuddAddOuterSumRecur() +
      ] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddMatMult.c,v 1.1.1.1 2003/02/24 22:23:52 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static DdNode * addMMRecur ARGS((DdManager *dd, DdNode *A, DdNode *B, int topP, int *vars)); +static DdNode * addTriangleRecur ARGS((DdManager *dd, DdNode *f, DdNode *g, int *vars, DdNode *cube)); +static DdNode * cuddAddOuterSumRecur ARGS((DdManager *dd, DdNode *M, DdNode *r, DdNode *c)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Calculates the product of two matrices represented as + ADDs.] + + Description [Calculates the product of two matrices, A and B, + represented as ADDs. This procedure implements the quasiring multiplication + algorithm. A is assumed to depend on variables x (rows) and z + (columns). B is assumed to depend on variables z (rows) and y + (columns). The product of A and B then depends on x (rows) and y + (columns). Only the z variables have to be explicitly identified; + they are the "summation" variables. Returns a pointer to the + result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addTimesPlus Cudd_addTriangle Cudd_bddAndAbstract] + +******************************************************************************/ +DdNode * +Cudd_addMatrixMultiply( + DdManager * dd, + DdNode * A, + DdNode * B, + DdNode ** z, + int nz) +{ + int i, nvars, *vars; + DdNode *res; + + /* Array vars says what variables are "summation" variables. */ + nvars = dd->size; + vars = ALLOC(int,nvars); + if (vars == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + for (i = 0; i < nvars; i++) { + vars[i] = 0; + } + for (i = 0; i < nz; i++) { + vars[z[i]->index] = 1; + } + + do { + dd->reordered = 0; + res = addMMRecur(dd,A,B,-1,vars); + } while (dd->reordered == 1); + FREE(vars); + return(res); + +} /* end of Cudd_addMatrixMultiply */ + + +/**Function******************************************************************** + + Synopsis [Calculates the product of two matrices represented as + ADDs.] + + Description [Calculates the product of two matrices, A and B, + represented as ADDs, using the CMU matrix by matrix multiplication + procedure by Clarke et al.. Matrix A has x's as row variables and z's + as column variables, while matrix B has z's as row variables and y's + as column variables. Returns the pointer to the result if successful; + NULL otherwise. The resulting matrix has x's as row variables and y's + as column variables.] + + SideEffects [None] + + SeeAlso [Cudd_addMatrixMultiply] + +******************************************************************************/ +DdNode * +Cudd_addTimesPlus( + DdManager * dd, + DdNode * A, + DdNode * B, + DdNode ** z, + int nz) +{ + DdNode *w, *cube, *tmp, *res; + int i; + tmp = Cudd_addApply(dd,Cudd_addTimes,A,B); + if (tmp == NULL) return(NULL); + Cudd_Ref(tmp); + Cudd_Ref(cube = DD_ONE(dd)); + for (i = nz-1; i >= 0; i--) { + w = Cudd_addIte(dd,z[i],cube,DD_ZERO(dd)); + if (w == NULL) { + Cudd_RecursiveDeref(dd,tmp); + return(NULL); + } + Cudd_Ref(w); + Cudd_RecursiveDeref(dd,cube); + cube = w; + } + res = Cudd_addExistAbstract(dd,tmp,cube); + if (res == NULL) { + Cudd_RecursiveDeref(dd,tmp); + Cudd_RecursiveDeref(dd,cube); + return(NULL); + } + Cudd_Ref(res); + Cudd_RecursiveDeref(dd,cube); + Cudd_RecursiveDeref(dd,tmp); + Cudd_Deref(res); + return(res); + +} /* end of Cudd_addTimesPlus */ + + +/**Function******************************************************************** + + Synopsis [Performs the triangulation step for the shortest path + computation.] + + Description [Implements the semiring multiplication algorithm used in + the triangulation step for the shortest path computation. f + is assumed to depend on variables x (rows) and z (columns). g is + assumed to depend on variables z (rows) and y (columns). The product + of f and g then depends on x (rows) and y (columns). Only the z + variables have to be explicitly identified; they are the + "abstraction" variables. Returns a pointer to the result if + successful; NULL otherwise. ] + + SideEffects [None] + + SeeAlso [Cudd_addMatrixMultiply Cudd_bddAndAbstract] + +******************************************************************************/ +DdNode * +Cudd_addTriangle( + DdManager * dd, + DdNode * f, + DdNode * g, + DdNode ** z, + int nz) +{ + int i, nvars, *vars; + DdNode *res, *cube; + + nvars = dd->size; + vars = ALLOC(int, nvars); + if (vars == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + for (i = 0; i < nvars; i++) vars[i] = -1; + for (i = 0; i < nz; i++) vars[z[i]->index] = i; + cube = Cudd_addComputeCube(dd, z, NULL, nz); + if (cube == NULL) { + FREE(vars); + return(NULL); + } + cuddRef(cube); + + do { + dd->reordered = 0; + res = addTriangleRecur(dd, f, g, vars, cube); + } while (dd->reordered == 1); + if (res != NULL) cuddRef(res); + Cudd_RecursiveDeref(dd,cube); + if (res != NULL) cuddDeref(res); + FREE(vars); + return(res); + +} /* end of Cudd_addTriangle */ + + +/**Function******************************************************************** + + Synopsis [Takes the minimum of a matrix and the outer sum of two vectors.] + + Description [Takes the pointwise minimum of a matrix and the outer + sum of two vectors. This procedure is used in the Floyd-Warshall + all-pair shortest path algorithm. Returns a pointer to the result if + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +Cudd_addOuterSum( + DdManager *dd, + DdNode *M, + DdNode *r, + DdNode *c) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddAddOuterSumRecur(dd, M, r, c); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_addOuterSum */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_addMatrixMultiply.] + + Description [Performs the recursive step of Cudd_addMatrixMultiply. + Returns a pointer to the result if successful; NULL otherwise.] + + SideEffects [None] + +******************************************************************************/ +static DdNode * +addMMRecur( + DdManager * dd, + DdNode * A, + DdNode * B, + int topP, + int * vars) +{ + DdNode *zero, + *At, /* positive cofactor of first operand */ + *Ae, /* negative cofactor of first operand */ + *Bt, /* positive cofactor of second operand */ + *Be, /* negative cofactor of second operand */ + *t, /* positive cofactor of result */ + *e, /* negative cofactor of result */ + *scaled, /* scaled result */ + *add_scale, /* ADD representing the scaling factor */ + *res; + int i; /* loop index */ + double scale; /* scaling factor */ + int index; /* index of the top variable */ + CUDD_VALUE_TYPE value; + unsigned int topA, topB, topV; + DdNode *(*cacheOp)(DdManager *, DdNode *, DdNode *); + + statLine(dd); + zero = DD_ZERO(dd); + + if (A == zero || B == zero) { + return(zero); + } + + if (cuddIsConstant(A) && cuddIsConstant(B)) { + /* Compute the scaling factor. It is 2^k, where k is the + ** number of summation variables below the current variable. + ** Indeed, these constants represent blocks of 2^k identical + ** constant values in both A and B. + */ + value = cuddV(A) * cuddV(B); + for (i = 0; i < dd->size; i++) { + if (vars[i]) { + if (dd->perm[i] > topP) { + value *= (CUDD_VALUE_TYPE) 2; + } + } + } + res = cuddUniqueConst(dd, value); + return(res); + } + + /* Standardize to increase cache efficiency. Clearly, A*B != B*A + ** in matrix multiplication. However, which matrix is which is + ** determined by the variables appearing in the ADDs and not by + ** which one is passed as first argument. + */ + if (A > B) { + DdNode *tmp = A; + A = B; + B = tmp; + } + + topA = cuddI(dd,A->index); topB = cuddI(dd,B->index); + topV = ddMin(topA,topB); + + cacheOp = (DdNode *(*)(DdManager *, DdNode *, DdNode *)) addMMRecur; + res = cuddCacheLookup2(dd,cacheOp,A,B); + if (res != NULL) { + /* If the result is 0, there is no need to normalize. + ** Otherwise we count the number of z variables between + ** the current depth and the top of the ADDs. These are + ** the missing variables that determine the size of the + ** constant blocks. + */ + if (res == zero) return(res); + scale = 1.0; + for (i = 0; i < dd->size; i++) { + if (vars[i]) { + if (dd->perm[i] > topP && (unsigned) dd->perm[i] < topV) { + scale *= 2; + } + } + } + if (scale > 1.0) { + cuddRef(res); + add_scale = cuddUniqueConst(dd,(CUDD_VALUE_TYPE)scale); + if (add_scale == NULL) { + Cudd_RecursiveDeref(dd, res); + return(NULL); + } + cuddRef(add_scale); + scaled = cuddAddApplyRecur(dd,Cudd_addTimes,res,add_scale); + if (scaled == NULL) { + Cudd_RecursiveDeref(dd, add_scale); + Cudd_RecursiveDeref(dd, res); + return(NULL); + } + cuddRef(scaled); + Cudd_RecursiveDeref(dd, add_scale); + Cudd_RecursiveDeref(dd, res); + res = scaled; + cuddDeref(res); + } + return(res); + } + + /* compute the cofactors */ + if (topV == topA) { + At = cuddT(A); + Ae = cuddE(A); + } else { + At = Ae = A; + } + if (topV == topB) { + Bt = cuddT(B); + Be = cuddE(B); + } else { + Bt = Be = B; + } + + t = addMMRecur(dd, At, Bt, (int)topV, vars); + if (t == NULL) return(NULL); + cuddRef(t); + e = addMMRecur(dd, Ae, Be, (int)topV, vars); + if (e == NULL) { + Cudd_RecursiveDeref(dd, t); + return(NULL); + } + cuddRef(e); + + index = dd->invperm[topV]; + if (vars[index] == 0) { + /* We have split on either the rows of A or the columns + ** of B. We just need to connect the two subresults, + ** which correspond to two submatrices of the result. + */ + res = (t == e) ? t : cuddUniqueInter(dd,index,t,e); + if (res == NULL) { + Cudd_RecursiveDeref(dd, t); + Cudd_RecursiveDeref(dd, e); + return(NULL); + } + cuddRef(res); + cuddDeref(t); + cuddDeref(e); + } else { + /* we have simultaneously split on the columns of A and + ** the rows of B. The two subresults must be added. + */ + res = cuddAddApplyRecur(dd,Cudd_addPlus,t,e); + if (res == NULL) { + Cudd_RecursiveDeref(dd, t); + Cudd_RecursiveDeref(dd, e); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(dd, t); + Cudd_RecursiveDeref(dd, e); + } + + cuddCacheInsert2(dd,cacheOp,A,B,res); + + /* We have computed (and stored in the computed table) a minimal + ** result; that is, a result that assumes no summation variables + ** between the current depth of the recursion and its top + ** variable. We now take into account the z variables by properly + ** scaling the result. + */ + if (res != zero) { + scale = 1.0; + for (i = 0; i < dd->size; i++) { + if (vars[i]) { + if (dd->perm[i] > topP && (unsigned) dd->perm[i] < topV) { + scale *= 2; + } + } + } + if (scale > 1.0) { + add_scale = cuddUniqueConst(dd,(CUDD_VALUE_TYPE)scale); + if (add_scale == NULL) { + Cudd_RecursiveDeref(dd, res); + return(NULL); + } + cuddRef(add_scale); + scaled = cuddAddApplyRecur(dd,Cudd_addTimes,res,add_scale); + if (scaled == NULL) { + Cudd_RecursiveDeref(dd, res); + Cudd_RecursiveDeref(dd, add_scale); + return(NULL); + } + cuddRef(scaled); + Cudd_RecursiveDeref(dd, add_scale); + Cudd_RecursiveDeref(dd, res); + res = scaled; + } + } + cuddDeref(res); + return(res); + +} /* end of addMMRecur */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_addTriangle.] + + Description [Performs the recursive step of Cudd_addTriangle. Returns + a pointer to the result if successful; NULL otherwise.] + + SideEffects [None] + +******************************************************************************/ +static DdNode * +addTriangleRecur( + DdManager * dd, + DdNode * f, + DdNode * g, + int * vars, + DdNode *cube) +{ + DdNode *fv, *fvn, *gv, *gvn, *t, *e, *res; + CUDD_VALUE_TYPE value; + int top, topf, topg, index; + + statLine(dd); + if (f == DD_PLUS_INFINITY(dd) || g == DD_PLUS_INFINITY(dd)) { + return(DD_PLUS_INFINITY(dd)); + } + + if (cuddIsConstant(f) && cuddIsConstant(g)) { + value = cuddV(f) + cuddV(g); + res = cuddUniqueConst(dd, value); + return(res); + } + if (f < g) { + DdNode *tmp = f; + f = g; + g = tmp; + } + + if (f->ref != 1 || g->ref != 1) { + res = cuddCacheLookup(dd, DD_ADD_TRIANGLE_TAG, f, g, cube); + if (res != NULL) { + return(res); + } + } + + topf = cuddI(dd,f->index); topg = cuddI(dd,g->index); + top = ddMin(topf,topg); + + if (top == topf) {fv = cuddT(f); fvn = cuddE(f);} else {fv = fvn = f;} + if (top == topg) {gv = cuddT(g); gvn = cuddE(g);} else {gv = gvn = g;} + + t = addTriangleRecur(dd, fv, gv, vars, cube); + if (t == NULL) return(NULL); + cuddRef(t); + e = addTriangleRecur(dd, fvn, gvn, vars, cube); + if (e == NULL) { + Cudd_RecursiveDeref(dd, t); + return(NULL); + } + cuddRef(e); + + index = dd->invperm[top]; + if (vars[index] < 0) { + res = (t == e) ? t : cuddUniqueInter(dd,index,t,e); + if (res == NULL) { + Cudd_RecursiveDeref(dd, t); + Cudd_RecursiveDeref(dd, e); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + } else { + res = cuddAddApplyRecur(dd,Cudd_addMinimum,t,e); + if (res == NULL) { + Cudd_RecursiveDeref(dd, t); + Cudd_RecursiveDeref(dd, e); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(dd, t); + Cudd_RecursiveDeref(dd, e); + cuddDeref(res); + } + + if (f->ref != 1 || g->ref != 1) { + cuddCacheInsert(dd, DD_ADD_TRIANGLE_TAG, f, g, cube, res); + } + + return(res); + +} /* end of addTriangleRecur */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_addOuterSum.] + + Description [Performs the recursive step of Cudd_addOuterSum. + Returns a pointer to the result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static DdNode * +cuddAddOuterSumRecur( + DdManager *dd, + DdNode *M, + DdNode *r, + DdNode *c) +{ + DdNode *P, *R, *Mt, *Me, *rt, *re, *ct, *ce, *Rt, *Re; + int topM, topc, topr; + int v, index; + + statLine(dd); + /* Check special cases. */ + if (r == DD_PLUS_INFINITY(dd) || c == DD_PLUS_INFINITY(dd)) return(M); + + if (cuddIsConstant(c) && cuddIsConstant(r)) { + R = cuddUniqueConst(dd,Cudd_V(c)+Cudd_V(r)); + cuddRef(R); + if (cuddIsConstant(M)) { + if (cuddV(R) <= cuddV(M)) { + cuddDeref(R); + return(R); + } else { + Cudd_RecursiveDeref(dd,R); + return(M); + } + } else { + P = Cudd_addApply(dd,Cudd_addMinimum,R,M); + cuddRef(P); + Cudd_RecursiveDeref(dd,R); + cuddDeref(P); + return(P); + } + } + + /* Check the cache. */ + R = cuddCacheLookup(dd,DD_ADD_OUT_SUM_TAG,M,r,c); + if (R != NULL) return(R); + + topM = cuddI(dd,M->index); topr = cuddI(dd,r->index); + topc = cuddI(dd,c->index); + v = ddMin(topM,ddMin(topr,topc)); + + /* Compute cofactors. */ + if (topM == v) { Mt = cuddT(M); Me = cuddE(M); } else { Mt = Me = M; } + if (topr == v) { rt = cuddT(r); re = cuddE(r); } else { rt = re = r; } + if (topc == v) { ct = cuddT(c); ce = cuddE(c); } else { ct = ce = c; } + + /* Recursively solve. */ + Rt = cuddAddOuterSumRecur(dd,Mt,rt,ct); + if (Rt == NULL) return(NULL); + cuddRef(Rt); + Re = cuddAddOuterSumRecur(dd,Me,re,ce); + if (Re == NULL) { + Cudd_RecursiveDeref(dd, Rt); + return(NULL); + } + cuddRef(Re); + index = dd->invperm[v]; + R = (Rt == Re) ? Rt : cuddUniqueInter(dd,index,Rt,Re); + if (R == NULL) { + Cudd_RecursiveDeref(dd, Rt); + Cudd_RecursiveDeref(dd, Re); + return(NULL); + } + cuddDeref(Rt); + cuddDeref(Re); + + /* Store the result in the cache. */ + cuddCacheInsert(dd,DD_ADD_OUT_SUM_TAG,M,r,c,R); + + return(R); + +} /* end of cuddAddOuterSumRecur */ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddPriority.c b/abc_with_bb_support/src/bdd/cudd/cuddPriority.c new file mode 100644 index 000000000..e5ed6df52 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddPriority.c @@ -0,0 +1,1475 @@ +/**CFile*********************************************************************** + + FileName [cuddPriority.c] + + PackageName [cudd] + + Synopsis [Priority functions.] + + Description [External procedures included in this file: +
        +
      • Cudd_PrioritySelect() +
      • Cudd_Xgty() +
      • Cudd_Xeqy() +
      • Cudd_addXeqy() +
      • Cudd_Dxygtdxz() +
      • Cudd_Dxygtdyz() +
      • Cudd_CProjection() +
      • Cudd_addHamming() +
      • Cudd_MinHammingDist() +
      • Cudd_bddClosestCube() +
      + Internal procedures included in this module: +
        +
      • cuddCProjectionRecur() +
      • cuddBddClosestCube() +
      + Static procedures included in this module: +
        +
      • cuddMinHammingDistRecur() +
      • separateCube() +
      • createResult() +
      + ] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [ This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddPriority.c,v 1.1.1.1 2003/02/24 22:23:52 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ +static int cuddMinHammingDistRecur ARGS((DdNode * f, int *minterm, DdHashTable * table, int upperBound)); +static DdNode * separateCube ARGS((DdManager *dd, DdNode *f, CUDD_VALUE_TYPE *distance)); +static DdNode * createResult ARGS((DdManager *dd, unsigned int index, unsigned int phase, DdNode *cube, CUDD_VALUE_TYPE distance)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Selects pairs from R using a priority function.] + + Description [Selects pairs from a relation R(x,y) (given as a BDD) + in such a way that a given x appears in one pair only. Uses a + priority function to determine which y should be paired to a given x. + Cudd_PrioritySelect returns a pointer to + the selected function if successful; NULL otherwise. + Three of the arguments--x, y, and z--are vectors of BDD variables. + The first two are the variables on which R depends. The third vectore + is a vector of auxiliary variables, used during the computation. This + vector is optional. If a NULL value is passed instead, + Cudd_PrioritySelect will create the working variables on the fly. + The sizes of x and y (and z if it is not NULL) should equal n. + The priority function Pi can be passed as a BDD, or can be built by + Cudd_PrioritySelect. If NULL is passed instead of a DdNode *, + parameter Pifunc is used by Cudd_PrioritySelect to build a BDD for the + priority function. (Pifunc is a pointer to a C function.) If Pi is not + NULL, then Pifunc is ignored. Pifunc should have the same interface as + the standard priority functions (e.g., Cudd_Dxygtdxz). + Cudd_PrioritySelect and Cudd_CProjection can sometimes be used + interchangeably. Specifically, calling Cudd_PrioritySelect with + Cudd_Xgty as Pifunc produces the same result as calling + Cudd_CProjection with the all-zero minterm as reference minterm. + However, depending on the application, one or the other may be + preferable: +
        +
      • When extracting representatives from an equivalence relation, + Cudd_CProjection has the advantage of nor requiring the auxiliary + variables. +
      • When computing matchings in general bipartite graphs, + Cudd_PrioritySelect normally obtains better results because it can use + more powerful matching schemes (e.g., Cudd_Dxygtdxz). +
      + ] + + SideEffects [If called with z == NULL, will create new variables in + the manager.] + + SeeAlso [Cudd_Dxygtdxz Cudd_Dxygtdyz Cudd_Xgty + Cudd_bddAdjPermuteX Cudd_CProjection] + +******************************************************************************/ +DdNode * +Cudd_PrioritySelect( + DdManager * dd /* manager */, + DdNode * R /* BDD of the relation */, + DdNode ** x /* array of x variables */, + DdNode ** y /* array of y variables */, + DdNode ** z /* array of z variables (optional: may be NULL) */, + DdNode * Pi /* BDD of the priority function (optional: may be NULL) */, + int n /* size of x, y, and z */, + DdNode * (*Pifunc)(DdManager *, int, DdNode **, DdNode **, DdNode **) /* function used to build Pi if it is NULL */) +{ + DdNode *res = NULL; + DdNode *zcube = NULL; + DdNode *Rxz, *Q; + int createdZ = 0; + int createdPi = 0; + int i; + + /* Create z variables if needed. */ + if (z == NULL) { + if (Pi != NULL) return(NULL); + z = ALLOC(DdNode *,n); + if (z == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + createdZ = 1; + for (i = 0; i < n; i++) { + if (dd->size >= (int) CUDD_MAXINDEX - 1) goto endgame; + z[i] = cuddUniqueInter(dd,dd->size,dd->one,Cudd_Not(dd->one)); + if (z[i] == NULL) goto endgame; + } + } + + /* Create priority function BDD if needed. */ + if (Pi == NULL) { + Pi = Pifunc(dd,n,x,y,z); + if (Pi == NULL) goto endgame; + createdPi = 1; + cuddRef(Pi); + } + + /* Initialize abstraction cube. */ + zcube = DD_ONE(dd); + cuddRef(zcube); + for (i = n - 1; i >= 0; i--) { + DdNode *tmpp; + tmpp = Cudd_bddAnd(dd,z[i],zcube); + if (tmpp == NULL) goto endgame; + cuddRef(tmpp); + Cudd_RecursiveDeref(dd,zcube); + zcube = tmpp; + } + + /* Compute subset of (x,y) pairs. */ + Rxz = Cudd_bddSwapVariables(dd,R,y,z,n); + if (Rxz == NULL) goto endgame; + cuddRef(Rxz); + Q = Cudd_bddAndAbstract(dd,Rxz,Pi,zcube); + if (Q == NULL) { + Cudd_RecursiveDeref(dd,Rxz); + goto endgame; + } + cuddRef(Q); + Cudd_RecursiveDeref(dd,Rxz); + res = Cudd_bddAnd(dd,R,Cudd_Not(Q)); + if (res == NULL) { + Cudd_RecursiveDeref(dd,Q); + goto endgame; + } + cuddRef(res); + Cudd_RecursiveDeref(dd,Q); + +endgame: + if (zcube != NULL) Cudd_RecursiveDeref(dd,zcube); + if (createdZ) { + FREE(z); + } + if (createdPi) { + Cudd_RecursiveDeref(dd,Pi); + } + if (res != NULL) cuddDeref(res); + return(res); + +} /* Cudd_PrioritySelect */ + + +/**Function******************************************************************** + + Synopsis [Generates a BDD for the function x > y.] + + Description [This function generates a BDD for the function x > y. + Both x and y are N-bit numbers, x\[0\] x\[1\] ... x\[N-1\] and + y\[0\] y\[1\] ... y\[N-1\], with 0 the most significant bit. + The BDD is built bottom-up. + It has 3*N-1 internal nodes, if the variables are ordered as follows: + x\[0\] y\[0\] x\[1\] y\[1\] ... x\[N-1\] y\[N-1\]. + Argument z is not used by Cudd_Xgty: it is included to make it + call-compatible to Cudd_Dxygtdxz and Cudd_Dxygtdyz.] + + SideEffects [None] + + SeeAlso [Cudd_PrioritySelect Cudd_Dxygtdxz Cudd_Dxygtdyz] + +******************************************************************************/ +DdNode * +Cudd_Xgty( + DdManager * dd /* DD manager */, + int N /* number of x and y variables */, + DdNode ** z /* array of z variables: unused */, + DdNode ** x /* array of x variables */, + DdNode ** y /* array of y variables */) +{ + DdNode *u, *v, *w; + int i; + + /* Build bottom part of BDD outside loop. */ + u = Cudd_bddAnd(dd, x[N-1], Cudd_Not(y[N-1])); + if (u == NULL) return(NULL); + cuddRef(u); + + /* Loop to build the rest of the BDD. */ + for (i = N-2; i >= 0; i--) { + v = Cudd_bddAnd(dd, y[i], Cudd_Not(u)); + if (v == NULL) { + Cudd_RecursiveDeref(dd, u); + return(NULL); + } + cuddRef(v); + w = Cudd_bddAnd(dd, Cudd_Not(y[i]), u); + if (w == NULL) { + Cudd_RecursiveDeref(dd, u); + Cudd_RecursiveDeref(dd, v); + return(NULL); + } + cuddRef(w); + Cudd_RecursiveDeref(dd, u); + u = Cudd_bddIte(dd, x[i], Cudd_Not(v), w); + if (u == NULL) { + Cudd_RecursiveDeref(dd, v); + Cudd_RecursiveDeref(dd, w); + return(NULL); + } + cuddRef(u); + Cudd_RecursiveDeref(dd, v); + Cudd_RecursiveDeref(dd, w); + + } + cuddDeref(u); + return(u); + +} /* end of Cudd_Xgty */ + + +/**Function******************************************************************** + + Synopsis [Generates a BDD for the function x==y.] + + Description [This function generates a BDD for the function x==y. + Both x and y are N-bit numbers, x\[0\] x\[1\] ... x\[N-1\] and + y\[0\] y\[1\] ... y\[N-1\], with 0 the most significant bit. + The BDD is built bottom-up. + It has 3*N-1 internal nodes, if the variables are ordered as follows: + x\[0\] y\[0\] x\[1\] y\[1\] ... x\[N-1\] y\[N-1\]. ] + + SideEffects [None] + + SeeAlso [Cudd_addXeqy] + +******************************************************************************/ +DdNode * +Cudd_Xeqy( + DdManager * dd /* DD manager */, + int N /* number of x and y variables */, + DdNode ** x /* array of x variables */, + DdNode ** y /* array of y variables */) +{ + DdNode *u, *v, *w; + int i; + + /* Build bottom part of BDD outside loop. */ + u = Cudd_bddIte(dd, x[N-1], y[N-1], Cudd_Not(y[N-1])); + if (u == NULL) return(NULL); + cuddRef(u); + + /* Loop to build the rest of the BDD. */ + for (i = N-2; i >= 0; i--) { + v = Cudd_bddAnd(dd, y[i], u); + if (v == NULL) { + Cudd_RecursiveDeref(dd, u); + return(NULL); + } + cuddRef(v); + w = Cudd_bddAnd(dd, Cudd_Not(y[i]), u); + if (w == NULL) { + Cudd_RecursiveDeref(dd, u); + Cudd_RecursiveDeref(dd, v); + return(NULL); + } + cuddRef(w); + Cudd_RecursiveDeref(dd, u); + u = Cudd_bddIte(dd, x[i], v, w); + if (u == NULL) { + Cudd_RecursiveDeref(dd, v); + Cudd_RecursiveDeref(dd, w); + return(NULL); + } + cuddRef(u); + Cudd_RecursiveDeref(dd, v); + Cudd_RecursiveDeref(dd, w); + } + cuddDeref(u); + return(u); + +} /* end of Cudd_Xeqy */ + + +/**Function******************************************************************** + + Synopsis [Generates an ADD for the function x==y.] + + Description [This function generates an ADD for the function x==y. + Both x and y are N-bit numbers, x\[0\] x\[1\] ... x\[N-1\] and + y\[0\] y\[1\] ... y\[N-1\], with 0 the most significant bit. + The ADD is built bottom-up. + It has 3*N-1 internal nodes, if the variables are ordered as follows: + x\[0\] y\[0\] x\[1\] y\[1\] ... x\[N-1\] y\[N-1\]. ] + + SideEffects [None] + + SeeAlso [Cudd_Xeqy] + +******************************************************************************/ +DdNode * +Cudd_addXeqy( + DdManager * dd /* DD manager */, + int N /* number of x and y variables */, + DdNode ** x /* array of x variables */, + DdNode ** y /* array of y variables */) +{ + DdNode *one, *zero; + DdNode *u, *v, *w; + int i; + + one = DD_ONE(dd); + zero = DD_ZERO(dd); + + /* Build bottom part of ADD outside loop. */ + v = Cudd_addIte(dd, y[N-1], one, zero); + if (v == NULL) return(NULL); + cuddRef(v); + w = Cudd_addIte(dd, y[N-1], zero, one); + if (w == NULL) { + Cudd_RecursiveDeref(dd, v); + return(NULL); + } + cuddRef(w); + u = Cudd_addIte(dd, x[N-1], v, w); + if (w == NULL) { + Cudd_RecursiveDeref(dd, v); + Cudd_RecursiveDeref(dd, w); + return(NULL); + } + cuddRef(u); + Cudd_RecursiveDeref(dd, v); + Cudd_RecursiveDeref(dd, w); + + /* Loop to build the rest of the ADD. */ + for (i = N-2; i >= 0; i--) { + v = Cudd_addIte(dd, y[i], u, zero); + if (v == NULL) { + Cudd_RecursiveDeref(dd, u); + return(NULL); + } + cuddRef(v); + w = Cudd_addIte(dd, y[i], zero, u); + if (w == NULL) { + Cudd_RecursiveDeref(dd, u); + Cudd_RecursiveDeref(dd, v); + return(NULL); + } + cuddRef(w); + Cudd_RecursiveDeref(dd, u); + u = Cudd_addIte(dd, x[i], v, w); + if (w == NULL) { + Cudd_RecursiveDeref(dd, v); + Cudd_RecursiveDeref(dd, w); + return(NULL); + } + cuddRef(u); + Cudd_RecursiveDeref(dd, v); + Cudd_RecursiveDeref(dd, w); + } + cuddDeref(u); + return(u); + +} /* end of Cudd_addXeqy */ + + +/**Function******************************************************************** + + Synopsis [Generates a BDD for the function d(x,y) > d(x,z).] + + Description [This function generates a BDD for the function d(x,y) + > d(x,z); + x, y, and z are N-bit numbers, x\[0\] x\[1\] ... x\[N-1\], + y\[0\] y\[1\] ... y\[N-1\], and z\[0\] z\[1\] ... z\[N-1\], + with 0 the most significant bit. + The distance d(x,y) is defined as: + \sum_{i=0}^{N-1}(|x_i - y_i| \cdot 2^{N-i-1}). + The BDD is built bottom-up. + It has 7*N-3 internal nodes, if the variables are ordered as follows: + x\[0\] y\[0\] z\[0\] x\[1\] y\[1\] z\[1\] ... x\[N-1\] y\[N-1\] z\[N-1\]. ] + + SideEffects [None] + + SeeAlso [Cudd_PrioritySelect Cudd_Dxygtdyz Cudd_Xgty Cudd_bddAdjPermuteX] + +******************************************************************************/ +DdNode * +Cudd_Dxygtdxz( + DdManager * dd /* DD manager */, + int N /* number of x, y, and z variables */, + DdNode ** x /* array of x variables */, + DdNode ** y /* array of y variables */, + DdNode ** z /* array of z variables */) +{ + DdNode *one, *zero; + DdNode *z1, *z2, *z3, *z4, *y1_, *y2, *x1; + int i; + + one = DD_ONE(dd); + zero = Cudd_Not(one); + + /* Build bottom part of BDD outside loop. */ + y1_ = Cudd_bddIte(dd, y[N-1], one, Cudd_Not(z[N-1])); + if (y1_ == NULL) return(NULL); + cuddRef(y1_); + y2 = Cudd_bddIte(dd, y[N-1], z[N-1], one); + if (y2 == NULL) { + Cudd_RecursiveDeref(dd, y1_); + return(NULL); + } + cuddRef(y2); + x1 = Cudd_bddIte(dd, x[N-1], y1_, y2); + if (x1 == NULL) { + Cudd_RecursiveDeref(dd, y1_); + Cudd_RecursiveDeref(dd, y2); + return(NULL); + } + cuddRef(x1); + Cudd_RecursiveDeref(dd, y1_); + Cudd_RecursiveDeref(dd, y2); + + /* Loop to build the rest of the BDD. */ + for (i = N-2; i >= 0; i--) { + z1 = Cudd_bddIte(dd, z[i], one, Cudd_Not(x1)); + if (z1 == NULL) { + Cudd_RecursiveDeref(dd, x1); + return(NULL); + } + cuddRef(z1); + z2 = Cudd_bddIte(dd, z[i], x1, one); + if (z2 == NULL) { + Cudd_RecursiveDeref(dd, x1); + Cudd_RecursiveDeref(dd, z1); + return(NULL); + } + cuddRef(z2); + z3 = Cudd_bddIte(dd, z[i], one, x1); + if (z3 == NULL) { + Cudd_RecursiveDeref(dd, x1); + Cudd_RecursiveDeref(dd, z1); + Cudd_RecursiveDeref(dd, z2); + return(NULL); + } + cuddRef(z3); + z4 = Cudd_bddIte(dd, z[i], x1, zero); + if (z4 == NULL) { + Cudd_RecursiveDeref(dd, x1); + Cudd_RecursiveDeref(dd, z1); + Cudd_RecursiveDeref(dd, z2); + Cudd_RecursiveDeref(dd, z3); + return(NULL); + } + cuddRef(z4); + Cudd_RecursiveDeref(dd, x1); + y1_ = Cudd_bddIte(dd, y[i], z2, Cudd_Not(z1)); + if (y1_ == NULL) { + Cudd_RecursiveDeref(dd, z1); + Cudd_RecursiveDeref(dd, z2); + Cudd_RecursiveDeref(dd, z3); + Cudd_RecursiveDeref(dd, z4); + return(NULL); + } + cuddRef(y1_); + y2 = Cudd_bddIte(dd, y[i], z4, z3); + if (y2 == NULL) { + Cudd_RecursiveDeref(dd, z1); + Cudd_RecursiveDeref(dd, z2); + Cudd_RecursiveDeref(dd, z3); + Cudd_RecursiveDeref(dd, z4); + Cudd_RecursiveDeref(dd, y1_); + return(NULL); + } + cuddRef(y2); + Cudd_RecursiveDeref(dd, z1); + Cudd_RecursiveDeref(dd, z2); + Cudd_RecursiveDeref(dd, z3); + Cudd_RecursiveDeref(dd, z4); + x1 = Cudd_bddIte(dd, x[i], y1_, y2); + if (x1 == NULL) { + Cudd_RecursiveDeref(dd, y1_); + Cudd_RecursiveDeref(dd, y2); + return(NULL); + } + cuddRef(x1); + Cudd_RecursiveDeref(dd, y1_); + Cudd_RecursiveDeref(dd, y2); + } + cuddDeref(x1); + return(Cudd_Not(x1)); + +} /* end of Cudd_Dxygtdxz */ + + +/**Function******************************************************************** + + Synopsis [Generates a BDD for the function d(x,y) > d(y,z).] + + Description [This function generates a BDD for the function d(x,y) + > d(y,z); + x, y, and z are N-bit numbers, x\[0\] x\[1\] ... x\[N-1\], + y\[0\] y\[1\] ... y\[N-1\], and z\[0\] z\[1\] ... z\[N-1\], + with 0 the most significant bit. + The distance d(x,y) is defined as: + \sum_{i=0}^{N-1}(|x_i - y_i| \cdot 2^{N-i-1}). + The BDD is built bottom-up. + It has 7*N-3 internal nodes, if the variables are ordered as follows: + x\[0\] y\[0\] z\[0\] x\[1\] y\[1\] z\[1\] ... x\[N-1\] y\[N-1\] z\[N-1\]. ] + + SideEffects [None] + + SeeAlso [Cudd_PrioritySelect Cudd_Dxygtdxz Cudd_Xgty Cudd_bddAdjPermuteX] + +******************************************************************************/ +DdNode * +Cudd_Dxygtdyz( + DdManager * dd /* DD manager */, + int N /* number of x, y, and z variables */, + DdNode ** x /* array of x variables */, + DdNode ** y /* array of y variables */, + DdNode ** z /* array of z variables */) +{ + DdNode *one, *zero; + DdNode *z1, *z2, *z3, *z4, *y1_, *y2, *x1; + int i; + + one = DD_ONE(dd); + zero = Cudd_Not(one); + + /* Build bottom part of BDD outside loop. */ + y1_ = Cudd_bddIte(dd, y[N-1], one, z[N-1]); + if (y1_ == NULL) return(NULL); + cuddRef(y1_); + y2 = Cudd_bddIte(dd, y[N-1], z[N-1], zero); + if (y2 == NULL) { + Cudd_RecursiveDeref(dd, y1_); + return(NULL); + } + cuddRef(y2); + x1 = Cudd_bddIte(dd, x[N-1], y1_, Cudd_Not(y2)); + if (x1 == NULL) { + Cudd_RecursiveDeref(dd, y1_); + Cudd_RecursiveDeref(dd, y2); + return(NULL); + } + cuddRef(x1); + Cudd_RecursiveDeref(dd, y1_); + Cudd_RecursiveDeref(dd, y2); + + /* Loop to build the rest of the BDD. */ + for (i = N-2; i >= 0; i--) { + z1 = Cudd_bddIte(dd, z[i], x1, zero); + if (z1 == NULL) { + Cudd_RecursiveDeref(dd, x1); + return(NULL); + } + cuddRef(z1); + z2 = Cudd_bddIte(dd, z[i], x1, one); + if (z2 == NULL) { + Cudd_RecursiveDeref(dd, x1); + Cudd_RecursiveDeref(dd, z1); + return(NULL); + } + cuddRef(z2); + z3 = Cudd_bddIte(dd, z[i], one, x1); + if (z3 == NULL) { + Cudd_RecursiveDeref(dd, x1); + Cudd_RecursiveDeref(dd, z1); + Cudd_RecursiveDeref(dd, z2); + return(NULL); + } + cuddRef(z3); + z4 = Cudd_bddIte(dd, z[i], one, Cudd_Not(x1)); + if (z4 == NULL) { + Cudd_RecursiveDeref(dd, x1); + Cudd_RecursiveDeref(dd, z1); + Cudd_RecursiveDeref(dd, z2); + Cudd_RecursiveDeref(dd, z3); + return(NULL); + } + cuddRef(z4); + Cudd_RecursiveDeref(dd, x1); + y1_ = Cudd_bddIte(dd, y[i], z2, z1); + if (y1_ == NULL) { + Cudd_RecursiveDeref(dd, z1); + Cudd_RecursiveDeref(dd, z2); + Cudd_RecursiveDeref(dd, z3); + Cudd_RecursiveDeref(dd, z4); + return(NULL); + } + cuddRef(y1_); + y2 = Cudd_bddIte(dd, y[i], z4, Cudd_Not(z3)); + if (y2 == NULL) { + Cudd_RecursiveDeref(dd, z1); + Cudd_RecursiveDeref(dd, z2); + Cudd_RecursiveDeref(dd, z3); + Cudd_RecursiveDeref(dd, z4); + Cudd_RecursiveDeref(dd, y1_); + return(NULL); + } + cuddRef(y2); + Cudd_RecursiveDeref(dd, z1); + Cudd_RecursiveDeref(dd, z2); + Cudd_RecursiveDeref(dd, z3); + Cudd_RecursiveDeref(dd, z4); + x1 = Cudd_bddIte(dd, x[i], y1_, Cudd_Not(y2)); + if (x1 == NULL) { + Cudd_RecursiveDeref(dd, y1_); + Cudd_RecursiveDeref(dd, y2); + return(NULL); + } + cuddRef(x1); + Cudd_RecursiveDeref(dd, y1_); + Cudd_RecursiveDeref(dd, y2); + } + cuddDeref(x1); + return(Cudd_Not(x1)); + +} /* end of Cudd_Dxygtdyz */ + + +/**Function******************************************************************** + + Synopsis [Computes the compatible projection of R w.r.t. cube Y.] + + Description [Computes the compatible projection of relation R with + respect to cube Y. Returns a pointer to the c-projection if + successful; NULL otherwise. For a comparison between Cudd_CProjection + and Cudd_PrioritySelect, see the documentation of the latter.] + + SideEffects [None] + + SeeAlso [Cudd_PrioritySelect] + +******************************************************************************/ +DdNode * +Cudd_CProjection( + DdManager * dd, + DdNode * R, + DdNode * Y) +{ + DdNode *res; + DdNode *support; + + if (cuddCheckCube(dd,Y) == 0) { + (void) fprintf(dd->err, + "Error: The third argument of Cudd_CProjection should be a cube\n"); + dd->errorCode = CUDD_INVALID_ARG; + return(NULL); + } + + /* Compute the support of Y, which is used by the abstraction step + ** in cuddCProjectionRecur. + */ + support = Cudd_Support(dd,Y); + if (support == NULL) return(NULL); + cuddRef(support); + + do { + dd->reordered = 0; + res = cuddCProjectionRecur(dd,R,Y,support); + } while (dd->reordered == 1); + + if (res == NULL) { + Cudd_RecursiveDeref(dd,support); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(dd,support); + cuddDeref(res); + + return(res); + +} /* end of Cudd_CProjection */ + + +/**Function******************************************************************** + + Synopsis [Computes the Hamming distance ADD.] + + Description [Computes the Hamming distance ADD. Returns an ADD that + gives the Hamming distance between its two arguments if successful; + NULL otherwise. The two vectors xVars and yVars identify the variables + that form the two arguments.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +Cudd_addHamming( + DdManager * dd, + DdNode ** xVars, + DdNode ** yVars, + int nVars) +{ + DdNode *result,*tempBdd; + DdNode *tempAdd,*temp; + int i; + + result = DD_ZERO(dd); + cuddRef(result); + + for (i = 0; i < nVars; i++) { + tempBdd = Cudd_bddIte(dd,xVars[i],Cudd_Not(yVars[i]),yVars[i]); + if (tempBdd == NULL) { + Cudd_RecursiveDeref(dd,result); + return(NULL); + } + cuddRef(tempBdd); + tempAdd = Cudd_BddToAdd(dd,tempBdd); + if (tempAdd == NULL) { + Cudd_RecursiveDeref(dd,tempBdd); + Cudd_RecursiveDeref(dd,result); + return(NULL); + } + cuddRef(tempAdd); + Cudd_RecursiveDeref(dd,tempBdd); + temp = Cudd_addApply(dd,Cudd_addPlus,tempAdd,result); + if (temp == NULL) { + Cudd_RecursiveDeref(dd,tempAdd); + Cudd_RecursiveDeref(dd,result); + return(NULL); + } + cuddRef(temp); + Cudd_RecursiveDeref(dd,tempAdd); + Cudd_RecursiveDeref(dd,result); + result = temp; + } + + cuddDeref(result); + return(result); + +} /* end of Cudd_addHamming */ + + +/**Function******************************************************************** + + Synopsis [Returns the minimum Hamming distance between f and minterm.] + + Description [Returns the minimum Hamming distance between the + minterms of a function f and a reference minterm. The function is + given as a BDD; the minterm is given as an array of integers, one + for each variable in the manager. Returns the minimum distance if + it is less than the upper bound; the upper bound if the minimum + distance is at least as large; CUDD_OUT_OF_MEM in case of failure.] + + SideEffects [None] + + SeeAlso [Cudd_addHamming Cudd_bddClosestCube] + +******************************************************************************/ +int +Cudd_MinHammingDist( + DdManager *dd /* DD manager */, + DdNode *f /* function to examine */, + int *minterm /* reference minterm */, + int upperBound /* distance above which an approximate answer is OK */) +{ + DdHashTable *table; + CUDD_VALUE_TYPE epsilon; + int res; + + table = cuddHashTableInit(dd,1,2); + if (table == NULL) { + return(CUDD_OUT_OF_MEM); + } + epsilon = Cudd_ReadEpsilon(dd); + Cudd_SetEpsilon(dd,(CUDD_VALUE_TYPE)0.0); + res = cuddMinHammingDistRecur(f,minterm,table,upperBound); + cuddHashTableQuit(table); + Cudd_SetEpsilon(dd,epsilon); + + return(res); + +} /* end of Cudd_MinHammingDist */ + + +/**Function******************************************************************** + + Synopsis [Finds a cube of f at minimum Hamming distance from g.] + + Description [Finds a cube of f at minimum Hamming distance from the + minterms of g. All the minterms of the cube are at the minimum + distance. If the distance is 0, the cube belongs to the + intersection of f and g. Returns the cube if successful; NULL + otherwise.] + + SideEffects [The distance is returned as a side effect.] + + SeeAlso [Cudd_MinHammingDist] + +******************************************************************************/ +DdNode * +Cudd_bddClosestCube( + DdManager *dd, + DdNode * f, + DdNode *g, + int *distance) +{ + DdNode *res, *acube; + CUDD_VALUE_TYPE rdist; + + /* Compute the cube and distance as a single ADD. */ + do { + dd->reordered = 0; + res = cuddBddClosestCube(dd,f,g,CUDD_CONST_INDEX + 1.0); + } while (dd->reordered == 1); + if (res == NULL) return(NULL); + cuddRef(res); + + /* Unpack distance and cube. */ + do { + dd->reordered = 0; + acube = separateCube(dd, res, &rdist); + } while (dd->reordered == 1); + if (acube == NULL) { + Cudd_RecursiveDeref(dd, res); + return(NULL); + } + cuddRef(acube); + Cudd_RecursiveDeref(dd, res); + + /* Convert cube from ADD to BDD. */ + do { + dd->reordered = 0; + res = cuddAddBddDoPattern(dd, acube); + } while (dd->reordered == 1); + if (res == NULL) { + Cudd_RecursiveDeref(dd, acube); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(dd, acube); + + *distance = (int) rdist; + cuddDeref(res); + return(res); + +} /* end of Cudd_bddClosestCube */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_CProjection.] + + Description [Performs the recursive step of Cudd_CProjection. Returns + the projection if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_CProjection] + +******************************************************************************/ +DdNode * +cuddCProjectionRecur( + DdManager * dd, + DdNode * R, + DdNode * Y, + DdNode * Ysupp) +{ + DdNode *res, *res1, *res2, *resA; + DdNode *r, *y, *RT, *RE, *YT, *YE, *Yrest, *Ra, *Ran, *Gamma, *Alpha; + unsigned int topR, topY, top, index; + DdNode *one = DD_ONE(dd); + + statLine(dd); + if (Y == one) return(R); + +#ifdef DD_DEBUG + assert(!Cudd_IsConstant(Y)); +#endif + + if (R == Cudd_Not(one)) return(R); + + res = cuddCacheLookup2(dd, Cudd_CProjection, R, Y); + if (res != NULL) return(res); + + r = Cudd_Regular(R); + topR = cuddI(dd,r->index); + y = Cudd_Regular(Y); + topY = cuddI(dd,y->index); + + top = ddMin(topR, topY); + + /* Compute the cofactors of R */ + if (topR == top) { + index = r->index; + RT = cuddT(r); + RE = cuddE(r); + if (r != R) { + RT = Cudd_Not(RT); RE = Cudd_Not(RE); + } + } else { + RT = RE = R; + } + + if (topY > top) { + /* Y does not depend on the current top variable. + ** We just need to compute the results on the two cofactors of R + ** and make them the children of a node labeled r->index. + */ + res1 = cuddCProjectionRecur(dd,RT,Y,Ysupp); + if (res1 == NULL) return(NULL); + cuddRef(res1); + res2 = cuddCProjectionRecur(dd,RE,Y,Ysupp); + if (res2 == NULL) { + Cudd_RecursiveDeref(dd,res1); + return(NULL); + } + cuddRef(res2); + res = cuddBddIteRecur(dd, dd->vars[index], res1, res2); + if (res == NULL) { + Cudd_RecursiveDeref(dd,res1); + Cudd_RecursiveDeref(dd,res2); + return(NULL); + } + /* If we have reached this point, res1 and res2 are now + ** incorporated in res. cuddDeref is therefore sufficient. + */ + cuddDeref(res1); + cuddDeref(res2); + } else { + /* Compute the cofactors of Y */ + index = y->index; + YT = cuddT(y); + YE = cuddE(y); + if (y != Y) { + YT = Cudd_Not(YT); YE = Cudd_Not(YE); + } + if (YT == Cudd_Not(one)) { + Alpha = Cudd_Not(dd->vars[index]); + Yrest = YE; + Ra = RE; + Ran = RT; + } else { + Alpha = dd->vars[index]; + Yrest = YT; + Ra = RT; + Ran = RE; + } + Gamma = cuddBddExistAbstractRecur(dd,Ra,cuddT(Ysupp)); + if (Gamma == NULL) return(NULL); + if (Gamma == one) { + res1 = cuddCProjectionRecur(dd,Ra,Yrest,cuddT(Ysupp)); + if (res1 == NULL) return(NULL); + cuddRef(res1); + res = cuddBddAndRecur(dd, Alpha, res1); + if (res == NULL) { + Cudd_RecursiveDeref(dd,res1); + return(NULL); + } + cuddDeref(res1); + } else if (Gamma == Cudd_Not(one)) { + res1 = cuddCProjectionRecur(dd,Ran,Yrest,cuddT(Ysupp)); + if (res1 == NULL) return(NULL); + cuddRef(res1); + res = cuddBddAndRecur(dd, Cudd_Not(Alpha), res1); + if (res == NULL) { + Cudd_RecursiveDeref(dd,res1); + return(NULL); + } + cuddDeref(res1); + } else { + cuddRef(Gamma); + resA = cuddCProjectionRecur(dd,Ran,Yrest,cuddT(Ysupp)); + if (resA == NULL) { + Cudd_RecursiveDeref(dd,Gamma); + return(NULL); + } + cuddRef(resA); + res2 = cuddBddAndRecur(dd, Cudd_Not(Gamma), resA); + if (res2 == NULL) { + Cudd_RecursiveDeref(dd,Gamma); + Cudd_RecursiveDeref(dd,resA); + return(NULL); + } + cuddRef(res2); + Cudd_RecursiveDeref(dd,Gamma); + Cudd_RecursiveDeref(dd,resA); + res1 = cuddCProjectionRecur(dd,Ra,Yrest,cuddT(Ysupp)); + if (res1 == NULL) { + Cudd_RecursiveDeref(dd,res2); + return(NULL); + } + cuddRef(res1); + res = cuddBddIteRecur(dd, Alpha, res1, res2); + if (res == NULL) { + Cudd_RecursiveDeref(dd,res1); + Cudd_RecursiveDeref(dd,res2); + return(NULL); + } + cuddDeref(res1); + cuddDeref(res2); + } + } + + cuddCacheInsert2(dd,Cudd_CProjection,R,Y,res); + + return(res); + +} /* end of cuddCProjectionRecur */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_bddClosestCube.] + + Description [Performs the recursive step of Cudd_bddClosestCube. + Returns the cube if succesful; NULL otherwise. The procedure uses a + four-way recursion to examine all four combinations of cofactors of + f and g. The most interesting feature of this function is the + scheme used for caching the results in the global computed table. + Since we have a cube and a distance, we combine them to form an ADD. + The combination replaces the zero child of the top node of the cube + with the negative of the distance. (The use of the negative is to + avoid ambiguity with 1.) The degenerate cases (zero and one) are + treated specially because the distance is known (0 for one, and + infinity for zero).] + + SideEffects [None] + + SeeAlso [Cudd_bddClosestCube] + +******************************************************************************/ +DdNode * +cuddBddClosestCube( + DdManager *dd, + DdNode *f, + DdNode *g, + CUDD_VALUE_TYPE bound) +{ + DdNode *res, *F, *G, *ft, *fe, *gt, *ge, *tt, *ee; + DdNode *ctt, *cee, *cte, *cet; + CUDD_VALUE_TYPE minD, dtt, dee, dte, det; + DdNode *one = DD_ONE(dd); + DdNode *lzero = Cudd_Not(one); + DdNode *azero = DD_ZERO(dd); + unsigned int topf, topg, index; + + statLine(dd); + if (bound < (f == Cudd_Not(g))) return(azero); + /* Terminal cases. */ + if (g == lzero || f == lzero) return(azero); + if (f == one && g == one) return(one); + + /* Check cache. */ + F = Cudd_Regular(f); + G = Cudd_Regular(g); + if (F->ref != 1 || G->ref != 1) { + res = cuddCacheLookup2(dd,(DdNode * (*)(DdManager *, DdNode *, + DdNode *)) Cudd_bddClosestCube, f, g); + if (res != NULL) return(res); + } + + topf = cuddI(dd,F->index); + topg = cuddI(dd,G->index); + + /* Compute cofactors. */ + if (topf <= topg) { + index = F->index; + ft = cuddT(F); + fe = cuddE(F); + if (Cudd_IsComplement(f)) { + ft = Cudd_Not(ft); + fe = Cudd_Not(fe); + } + } else { + index = G->index; + ft = fe = f; + } + + if (topg <= topf) { + gt = cuddT(G); + ge = cuddE(G); + if (Cudd_IsComplement(g)) { + gt = Cudd_Not(gt); + ge = Cudd_Not(ge); + } + } else { + gt = ge = g; + } + + tt = cuddBddClosestCube(dd,ft,gt,bound); + if (tt == NULL) return(NULL); + cuddRef(tt); + ctt = separateCube(dd,tt,&dtt); + if (ctt == NULL) { + Cudd_RecursiveDeref(dd, tt); + return(NULL); + } + cuddRef(ctt); + Cudd_RecursiveDeref(dd, tt); + minD = dtt; + bound = ddMin(bound,minD); + + ee = cuddBddClosestCube(dd,fe,ge,bound); + if (ee == NULL) { + Cudd_RecursiveDeref(dd, ctt); + return(NULL); + } + cuddRef(ee); + cee = separateCube(dd,ee,&dee); + if (cee == NULL) { + Cudd_RecursiveDeref(dd, ctt); + Cudd_RecursiveDeref(dd, ee); + return(NULL); + } + cuddRef(cee); + Cudd_RecursiveDeref(dd, ee); + minD = ddMin(dtt, dee); + bound = ddMin(bound,minD-1); + + if (minD > 0 && topf == topg) { + DdNode *te = cuddBddClosestCube(dd,ft,ge,bound-1); + if (te == NULL) { + Cudd_RecursiveDeref(dd, ctt); + Cudd_RecursiveDeref(dd, cee); + return(NULL); + } + cuddRef(te); + cte = separateCube(dd,te,&dte); + if (cte == NULL) { + Cudd_RecursiveDeref(dd, ctt); + Cudd_RecursiveDeref(dd, cee); + Cudd_RecursiveDeref(dd, te); + return(NULL); + } + cuddRef(cte); + Cudd_RecursiveDeref(dd, te); + dte += 1.0; + minD = ddMin(minD, dte); + } else { + cte = azero; + cuddRef(cte); + dte = CUDD_CONST_INDEX + 1.0; + } + bound = ddMin(bound,minD-1); + + if (minD > 0 && topf == topg) { + DdNode *et = cuddBddClosestCube(dd,fe,gt,bound-1); + if (et == NULL) { + Cudd_RecursiveDeref(dd, ctt); + Cudd_RecursiveDeref(dd, cee); + Cudd_RecursiveDeref(dd, cte); + return(NULL); + } + cuddRef(et); + cet = separateCube(dd,et,&det); + if (cet == NULL) { + Cudd_RecursiveDeref(dd, ctt); + Cudd_RecursiveDeref(dd, cee); + Cudd_RecursiveDeref(dd, cte); + Cudd_RecursiveDeref(dd, et); + return(NULL); + } + cuddRef(cet); + Cudd_RecursiveDeref(dd, et); + det += 1.0; + minD = ddMin(minD, det); + } else { + cet = azero; + cuddRef(cet); + det = CUDD_CONST_INDEX + 1.0; + } + + if (minD == dtt) { + if (dtt == dee && ctt == cee) { + res = createResult(dd,CUDD_CONST_INDEX,1,ctt,dtt); + } else { + res = createResult(dd,index,1,ctt,dtt); + } + } else if (minD == dee) { + res = createResult(dd,index,0,cee,dee); + } else if (minD == dte) { + res = createResult(dd,index,(topf <= topg),cte,dte); + } else { + res = createResult(dd,index,(topf > topg),cet,det); + } + cuddRef(res); + Cudd_RecursiveDeref(dd, ctt); + Cudd_RecursiveDeref(dd, cee); + Cudd_RecursiveDeref(dd, cte); + Cudd_RecursiveDeref(dd, cet); + + if (F->ref != 1 || G->ref != 1) + cuddCacheInsert2(dd,(DdNode * (*)(DdManager *, DdNode *, + DdNode *)) Cudd_bddClosestCube, f, g, res); + + cuddDeref(res); + return(res); + +} /* end of cuddBddClosestCube */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_MinHammingDist.] + + Description [Performs the recursive step of Cudd_MinHammingDist. + It is based on the following identity. Let H(f) be the + minimum Hamming distance of the minterms of f from the reference + minterm. Then: + + H(f) = min(H(f0)+h0,H(f1)+h1) + + where f0 and f1 are the two cofactors of f with respect to its top + variable; h0 is 1 if the minterm assigns 1 to the top variable of f; + h1 is 1 if the minterm assigns 0 to the top variable of f. + The upper bound on the distance is used to bound the depth of the + recursion. + Returns the minimum distance unless it exceeds the upper bound or + computation fails.] + + SideEffects [None] + + SeeAlso [Cudd_MinHammingDist] + +******************************************************************************/ +static int +cuddMinHammingDistRecur( + DdNode * f, + int *minterm, + DdHashTable * table, + int upperBound) +{ + DdNode *F, *Ft, *Fe; + double h, hT, hE; + DdNode *zero, *res; + DdManager *dd = table->manager; + + statLine(dd); + if (upperBound == 0) return(0); + + F = Cudd_Regular(f); + + if (cuddIsConstant(F)) { + zero = Cudd_Not(DD_ONE(dd)); + if (f == dd->background || f == zero) { + return(upperBound); + } else { + return(0); + } + } + if ((res = cuddHashTableLookup1(table,f)) != NULL) { + h = cuddV(res); + if (res->ref == 0) { + dd->dead++; + dd->constants.dead++; + } + return((int) h); + } + + Ft = cuddT(F); Fe = cuddE(F); + if (Cudd_IsComplement(f)) { + Ft = Cudd_Not(Ft); Fe = Cudd_Not(Fe); + } + if (minterm[F->index] == 0) { + DdNode *temp = Ft; + Ft = Fe; Fe = temp; + } + + hT = cuddMinHammingDistRecur(Ft,minterm,table,upperBound); + if (hT == CUDD_OUT_OF_MEM) return(CUDD_OUT_OF_MEM); + if (hT == 0) { + hE = upperBound; + } else { + hE = cuddMinHammingDistRecur(Fe,minterm,table,upperBound - 1); + if (hE == CUDD_OUT_OF_MEM) return(CUDD_OUT_OF_MEM); + } + h = ddMin(hT, hE + 1); + + if (F->ref != 1) { + ptrint fanout = (ptrint) F->ref; + cuddSatDec(fanout); + res = cuddUniqueConst(dd, (CUDD_VALUE_TYPE) h); + if (!cuddHashTableInsert1(table,f,res,fanout)) { + cuddRef(res); Cudd_RecursiveDeref(dd, res); + return(CUDD_OUT_OF_MEM); + } + } + + return((int) h); + +} /* end of cuddMinHammingDistRecur */ + + +/**Function******************************************************************** + + Synopsis [Separates cube from distance.] + + Description [Separates cube from distance. Returns the cube if + successful; NULL otherwise.] + + SideEffects [The distance is returned as a side effect.] + + SeeAlso [cuddBddClosestCube createResult] + +******************************************************************************/ +static DdNode * +separateCube( + DdManager *dd, + DdNode *f, + CUDD_VALUE_TYPE *distance) +{ + DdNode *cube, *t; + + /* One and zero are special cases because the distance is implied. */ + if (Cudd_IsConstant(f)) { + *distance = (f == DD_ONE(dd)) ? 0.0 : + (1.0 + (CUDD_VALUE_TYPE) CUDD_CONST_INDEX); + return(f); + } + + /* Find out which branch points to the distance and replace the top + ** node with one pointing to zero instead. */ + t = cuddT(f); + if (Cudd_IsConstant(t) && cuddV(t) <= 0) { +#ifdef DD_DEBUG + assert(!Cudd_IsConstant(cuddE(f)) || cuddE(f) == DD_ONE(dd)); +#endif + *distance = -cuddV(t); + cube = cuddUniqueInter(dd, f->index, DD_ZERO(dd), cuddE(f)); + } else { +#ifdef DD_DEBUG + assert(!Cudd_IsConstant(t) || t == DD_ONE(dd)); +#endif + *distance = -cuddV(cuddE(f)); + cube = cuddUniqueInter(dd, f->index, t, DD_ZERO(dd)); + } + + return(cube); + +} /* end of separateCube */ + + +/**Function******************************************************************** + + Synopsis [Builds a result for cache storage.] + + Description [Builds a result for cache storage. Returns a pointer + to the resulting ADD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [cuddBddClosestCube separateCube] + +******************************************************************************/ +static DdNode * +createResult( + DdManager *dd, + unsigned int index, + unsigned int phase, + DdNode *cube, + CUDD_VALUE_TYPE distance) +{ + DdNode *res, *constant; + + /* Special case. The cube is either one or zero, and we do not + ** add any variables. Hence, the result is also one or zero, + ** and the distance remains implied by teh value of the constant. */ + if (index == CUDD_CONST_INDEX && Cudd_IsConstant(cube)) return(cube); + + constant = cuddUniqueConst(dd,-distance); + if (constant == NULL) return(NULL); + cuddRef(constant); + + if (index == CUDD_CONST_INDEX) { + /* Replace the top node. */ + if (cuddT(cube) == DD_ZERO(dd)) { + res = cuddUniqueInter(dd,cube->index,constant,cuddE(cube)); + } else { + res = cuddUniqueInter(dd,cube->index,cuddT(cube),constant); + } + } else { + /* Add a new top node. */ +#ifdef DD_DEBUG + assert(cuddI(dd,index) < cuddI(dd,cube->index)); +#endif + if (phase) { + res = cuddUniqueInter(dd,index,cube,constant); + } else { + res = cuddUniqueInter(dd,index,constant,cube); + } + } + if (res == NULL) { + Cudd_RecursiveDeref(dd, constant); + return(NULL); + } + cuddDeref(constant); /* safe because constant is part of res */ + + return(res); + +} /* end of createResult */ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddRead.c b/abc_with_bb_support/src/bdd/cudd/cuddRead.c new file mode 100644 index 000000000..69eac14b6 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddRead.c @@ -0,0 +1,490 @@ +/**CFile*********************************************************************** + + FileName [cuddRead.c] + + PackageName [cudd] + + Synopsis [Functions to read in a matrix] + + Description [External procedures included in this module: +
        +
      • Cudd_addRead() +
      • Cudd_bddRead() +
      ] + + SeeAlso [cudd_addHarwell.c] + + Author [Fabio Somenzi] + + Copyright [ This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddRead.c,v 1.1.1.1 2003/02/24 22:23:52 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Reads in a sparse matrix.] + + Description [Reads in a sparse matrix specified in a simple format. + The first line of the input contains the numbers of rows and columns. + The remaining lines contain the elements of the matrix, one per line. + Given a background value + (specified by the background field of the manager), only the values + different from it are explicitly listed. Each foreground element is + described by two integers, i.e., the row and column number, and a + real number, i.e., the value.

      + Cudd_addRead produces an ADD that depends on two sets of variables: x + and y. The x variables (x\[0\] ... x\[nx-1\]) encode the row index and + the y variables (y\[0\] ... y\[ny-1\]) encode the column index. + x\[0\] and y\[0\] are the most significant bits in the indices. + The variables may already exist or may be created by the function. + The index of x\[i\] is bx+i*sx, and the index of y\[i\] is by+i*sy.

      + On input, nx and ny hold the numbers + of row and column variables already in existence. On output, they + hold the numbers of row and column variables actually used by the + matrix. When Cudd_addRead creates the variable arrays, + the index of x\[i\] is bx+i*sx, and the index of y\[i\] is by+i*sy. + When some variables already exist Cudd_addRead expects the indices + of the existing x variables to be bx+i*sx, and the indices of the + existing y variables to be by+i*sy.

      + m and n are set to the numbers of rows and columns of the + matrix. Their values on input are immaterial. + The ADD for the + sparse matrix is returned in E, and its reference count is > 0. + Cudd_addRead returns 1 in case of success; 0 otherwise.] + + SideEffects [nx and ny are set to the numbers of row and column + variables. m and n are set to the numbers of rows and columns. x and y + are possibly extended to represent the array of row and column + variables. Similarly for xn and yn_, which hold on return from + Cudd_addRead the complements of the row and column variables.] + + SeeAlso [Cudd_addHarwell Cudd_bddRead] + +******************************************************************************/ +int +Cudd_addRead( + FILE * fp /* input file pointer */, + DdManager * dd /* DD manager */, + DdNode ** E /* characteristic function of the graph */, + DdNode *** x /* array of row variables */, + DdNode *** y /* array of column variables */, + DdNode *** xn /* array of complemented row variables */, + DdNode *** yn_ /* array of complemented column variables */, + int * nx /* number or row variables */, + int * ny /* number or column variables */, + int * m /* number of rows */, + int * n /* number of columns */, + int bx /* first index of row variables */, + int sx /* step of row variables */, + int by /* first index of column variables */, + int sy /* step of column variables */) +{ + DdNode *one, *zero; + DdNode *w, *neW; + DdNode *minterm1; + int u, v, err, i, nv; + int lnx, lny; + CUDD_VALUE_TYPE val; + DdNode **lx, **ly, **lxn, **lyn; + + one = DD_ONE(dd); + zero = DD_ZERO(dd); + + err = fscanf(fp, "%d %d", &u, &v); + if (err == EOF) { + return(0); + } else if (err != 2) { + return(0); + } + + *m = u; + /* Compute the number of x variables. */ + lx = *x; lxn = *xn; + u--; /* row and column numbers start from 0 */ + for (lnx=0; u > 0; lnx++) { + u >>= 1; + } + /* Here we rely on the fact that REALLOC of a null pointer is + ** translates to an ALLOC. + */ + if (lnx > *nx) { + *x = lx = REALLOC(DdNode *, *x, lnx); + if (lx == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + *xn = lxn = REALLOC(DdNode *, *xn, lnx); + if (lxn == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + } + + *n = v; + /* Compute the number of y variables. */ + ly = *y; lyn = *yn_; + v--; /* row and column numbers start from 0 */ + for (lny=0; v > 0; lny++) { + v >>= 1; + } + /* Here we rely on the fact that REALLOC of a null pointer is + ** translates to an ALLOC. + */ + if (lny > *ny) { + *y = ly = REALLOC(DdNode *, *y, lny); + if (ly == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + *yn_ = lyn = REALLOC(DdNode *, *yn_, lny); + if (lyn == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + } + + /* Create all new variables. */ + for (i = *nx, nv = bx + (*nx) * sx; i < lnx; i++, nv += sx) { + do { + dd->reordered = 0; + lx[i] = cuddUniqueInter(dd, nv, one, zero); + } while (dd->reordered == 1); + if (lx[i] == NULL) return(0); + cuddRef(lx[i]); + do { + dd->reordered = 0; + lxn[i] = cuddUniqueInter(dd, nv, zero, one); + } while (dd->reordered == 1); + if (lxn[i] == NULL) return(0); + cuddRef(lxn[i]); + } + for (i = *ny, nv = by + (*ny) * sy; i < lny; i++, nv += sy) { + do { + dd->reordered = 0; + ly[i] = cuddUniqueInter(dd, nv, one, zero); + } while (dd->reordered == 1); + if (ly[i] == NULL) return(0); + cuddRef(ly[i]); + do { + dd->reordered = 0; + lyn[i] = cuddUniqueInter(dd, nv, zero, one); + } while (dd->reordered == 1); + if (lyn[i] == NULL) return(0); + cuddRef(lyn[i]); + } + *nx = lnx; + *ny = lny; + + *E = dd->background; /* this call will never cause reordering */ + cuddRef(*E); + + while (! feof(fp)) { + err = fscanf(fp, "%d %d %lf", &u, &v, &val); + if (err == EOF) { + break; + } else if (err != 3) { + return(0); + } else if (u >= *m || v >= *n || u < 0 || v < 0) { + return(0); + } + + minterm1 = one; cuddRef(minterm1); + + /* Build minterm1 corresponding to this arc */ + for (i = lnx - 1; i>=0; i--) { + if (u & 1) { + w = Cudd_addApply(dd, Cudd_addTimes, minterm1, lx[i]); + } else { + w = Cudd_addApply(dd, Cudd_addTimes, minterm1, lxn[i]); + } + if (w == NULL) { + Cudd_RecursiveDeref(dd, minterm1); + return(0); + } + cuddRef(w); + Cudd_RecursiveDeref(dd, minterm1); + minterm1 = w; + u >>= 1; + } + for (i = lny - 1; i>=0; i--) { + if (v & 1) { + w = Cudd_addApply(dd, Cudd_addTimes, minterm1, ly[i]); + } else { + w = Cudd_addApply(dd, Cudd_addTimes, minterm1, lyn[i]); + } + if (w == NULL) { + Cudd_RecursiveDeref(dd, minterm1); + return(0); + } + cuddRef(w); + Cudd_RecursiveDeref(dd, minterm1); + minterm1 = w; + v >>= 1; + } + /* Create new constant node if necessary. + ** This call will never cause reordering. + */ + neW = cuddUniqueConst(dd, val); + if (neW == NULL) { + Cudd_RecursiveDeref(dd, minterm1); + return(0); + } + cuddRef(neW); + + w = Cudd_addIte(dd, minterm1, neW, *E); + if (w == NULL) { + Cudd_RecursiveDeref(dd, minterm1); + Cudd_RecursiveDeref(dd, neW); + return(0); + } + cuddRef(w); + Cudd_RecursiveDeref(dd, minterm1); + Cudd_RecursiveDeref(dd, neW); + Cudd_RecursiveDeref(dd, *E); + *E = w; + } + return(1); + +} /* end of Cudd_addRead */ + + +/**Function******************************************************************** + + Synopsis [Reads in a graph (without labels) given as a list of arcs.] + + Description [Reads in a graph (without labels) given as an adjacency + matrix. The first line of the input contains the numbers of rows and + columns of the adjacency matrix. The remaining lines contain the arcs + of the graph, one per line. Each arc is described by two integers, + i.e., the row and column number, or the indices of the two endpoints. + Cudd_bddRead produces a BDD that depends on two sets of variables: x + and y. The x variables (x\[0\] ... x\[nx-1\]) encode + the row index and the y variables (y\[0\] ... y\[ny-1\]) encode the + column index. x\[0\] and y\[0\] are the most significant bits in the + indices. + The variables may already exist or may be created by the function. + The index of x\[i\] is bx+i*sx, and the index of y\[i\] is by+i*sy.

      + On input, nx and ny hold the numbers of row and column variables already + in existence. On output, they hold the numbers of row and column + variables actually used by the matrix. When Cudd_bddRead creates the + variable arrays, the index of x\[i\] is bx+i*sx, and the index of + y\[i\] is by+i*sy. When some variables already exist, Cudd_bddRead + expects the indices of the existing x variables to be bx+i*sx, and the + indices of the existing y variables to be by+i*sy.

      + m and n are set to the numbers of rows and columns of the + matrix. Their values on input are immaterial. The BDD for the graph + is returned in E, and its reference count is > 0. Cudd_bddRead returns + 1 in case of success; 0 otherwise.] + + SideEffects [nx and ny are set to the numbers of row and column + variables. m and n are set to the numbers of rows and columns. x and y + are possibly extended to represent the array of row and column + variables.] + + SeeAlso [Cudd_addHarwell Cudd_addRead] + +******************************************************************************/ +int +Cudd_bddRead( + FILE * fp /* input file pointer */, + DdManager * dd /* DD manager */, + DdNode ** E /* characteristic function of the graph */, + DdNode *** x /* array of row variables */, + DdNode *** y /* array of column variables */, + int * nx /* number or row variables */, + int * ny /* number or column variables */, + int * m /* number of rows */, + int * n /* number of columns */, + int bx /* first index of row variables */, + int sx /* step of row variables */, + int by /* first index of column variables */, + int sy /* step of column variables */) +{ + DdNode *one, *zero; + DdNode *w; + DdNode *minterm1; + int u, v, err, i, nv; + int lnx, lny; + DdNode **lx, **ly; + + one = DD_ONE(dd); + zero = Cudd_Not(one); + + err = fscanf(fp, "%d %d", &u, &v); + if (err == EOF) { + return(0); + } else if (err != 2) { + return(0); + } + + *m = u; + /* Compute the number of x variables. */ + lx = *x; + u--; /* row and column numbers start from 0 */ + for (lnx=0; u > 0; lnx++) { + u >>= 1; + } + if (lnx > *nx) { + *x = lx = REALLOC(DdNode *, *x, lnx); + if (lx == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + } + + *n = v; + /* Compute the number of y variables. */ + ly = *y; + v--; /* row and column numbers start from 0 */ + for (lny=0; v > 0; lny++) { + v >>= 1; + } + if (lny > *ny) { + *y = ly = REALLOC(DdNode *, *y, lny); + if (ly == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + } + + /* Create all new variables. */ + for (i = *nx, nv = bx + (*nx) * sx; i < lnx; i++, nv += sx) { + do { + dd->reordered = 0; + lx[i] = cuddUniqueInter(dd, nv, one, zero); + } while (dd->reordered == 1); + if (lx[i] == NULL) return(0); + cuddRef(lx[i]); + } + for (i = *ny, nv = by + (*ny) * sy; i < lny; i++, nv += sy) { + do { + dd->reordered = 0; + ly[i] = cuddUniqueInter(dd, nv, one, zero); + } while (dd->reordered == 1); + if (ly[i] == NULL) return(0); + cuddRef(ly[i]); + } + *nx = lnx; + *ny = lny; + + *E = zero; /* this call will never cause reordering */ + cuddRef(*E); + + while (! feof(fp)) { + err = fscanf(fp, "%d %d", &u, &v); + if (err == EOF) { + break; + } else if (err != 2) { + return(0); + } else if (u >= *m || v >= *n || u < 0 || v < 0) { + return(0); + } + + minterm1 = one; cuddRef(minterm1); + + /* Build minterm1 corresponding to this arc. */ + for (i = lnx - 1; i>=0; i--) { + if (u & 1) { + w = Cudd_bddAnd(dd, minterm1, lx[i]); + } else { + w = Cudd_bddAnd(dd, minterm1, Cudd_Not(lx[i])); + } + if (w == NULL) { + Cudd_RecursiveDeref(dd, minterm1); + return(0); + } + cuddRef(w); + Cudd_RecursiveDeref(dd,minterm1); + minterm1 = w; + u >>= 1; + } + for (i = lny - 1; i>=0; i--) { + if (v & 1) { + w = Cudd_bddAnd(dd, minterm1, ly[i]); + } else { + w = Cudd_bddAnd(dd, minterm1, Cudd_Not(ly[i])); + } + if (w == NULL) { + Cudd_RecursiveDeref(dd, minterm1); + return(0); + } + cuddRef(w); + Cudd_RecursiveDeref(dd, minterm1); + minterm1 = w; + v >>= 1; + } + + w = Cudd_bddAnd(dd, Cudd_Not(minterm1), Cudd_Not(*E)); + if (w == NULL) { + Cudd_RecursiveDeref(dd, minterm1); + return(0); + } + w = Cudd_Not(w); + cuddRef(w); + Cudd_RecursiveDeref(dd, minterm1); + Cudd_RecursiveDeref(dd, *E); + *E = w; + } + return(1); + +} /* end of Cudd_bddRead */ + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddRef.c b/abc_with_bb_support/src/bdd/cudd/cuddRef.c new file mode 100644 index 000000000..d580dd288 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddRef.c @@ -0,0 +1,781 @@ +/**CFile*********************************************************************** + + FileName [cuddRef.c] + + PackageName [cudd] + + Synopsis [Functions that manipulate the reference counts.] + + Description [External procedures included in this module: +

        +
      • Cudd_Ref() +
      • Cudd_RecursiveDeref() +
      • Cudd_IterDerefBdd() +
      • Cudd_DelayedDerefBdd() +
      • Cudd_RecursiveDerefZdd() +
      • Cudd_Deref() +
      • Cudd_CheckZeroRef() +
      + Internal procedures included in this module: +
        +
      • cuddReclaim() +
      • cuddReclaimZdd() +
      • cuddClearDeathRow() +
      • cuddShrinkDeathRow() +
      • cuddIsInDeathRow() +
      • cuddTimesInDeathRow() +
      + ] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [ This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddRef.c,v 1.1.1.1 2003/02/24 22:23:53 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Increases the reference count of a node, if it is not + saturated.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_RecursiveDeref Cudd_Deref] + +******************************************************************************/ +void +Cudd_Ref( + DdNode * n) +{ + + n = Cudd_Regular(n); + + cuddSatInc(n->ref); + +} /* end of Cudd_Ref */ + + +/**Function******************************************************************** + + Synopsis [Decreases the reference count of node n.] + + Description [Decreases the reference count of node n. If n dies, + recursively decreases the reference counts of its children. It is + used to dispose of a DD that is no longer needed.] + + SideEffects [None] + + SeeAlso [Cudd_Deref Cudd_Ref Cudd_RecursiveDerefZdd] + +******************************************************************************/ +void +Cudd_RecursiveDeref( + DdManager * table, + DdNode * n) +{ + DdNode *N; + int ord; + DdNodePtr *stack = table->stack; + int SP = 1; + + unsigned int live = table->keys - table->dead; + if (live > table->peakLiveNodes) { + table->peakLiveNodes = live; + } + + N = Cudd_Regular(n); + + do { +#ifdef DD_DEBUG + assert(N->ref != 0); +#endif + + if (N->ref == 1) { + N->ref = 0; + table->dead++; +#ifdef DD_STATS + table->nodesDropped++; +#endif + if (cuddIsConstant(N)) { + table->constants.dead++; + N = stack[--SP]; + } else { + ord = table->perm[N->index]; + stack[SP++] = Cudd_Regular(cuddE(N)); + table->subtables[ord].dead++; + N = cuddT(N); + } + } else { + cuddSatDec(N->ref); + N = stack[--SP]; + } + } while (SP != 0); + +} /* end of Cudd_RecursiveDeref */ + + +/**Function******************************************************************** + + Synopsis [Decreases the reference count of BDD node n.] + + Description [Decreases the reference count of node n. If n dies, + recursively decreases the reference counts of its children. It is + used to dispose of a BDD that is no longer needed. It is more + efficient than Cudd_RecursiveDeref, but it cannot be used on + ADDs. The greater efficiency comes from being able to assume that no + constant node will ever die as a result of a call to this + procedure.] + + SideEffects [None] + + SeeAlso [Cudd_RecursiveDeref Cudd_DelayedDerefBdd] + +******************************************************************************/ +void +Cudd_IterDerefBdd( + DdManager * table, + DdNode * n) +{ + DdNode *N; + int ord; + DdNodePtr *stack = table->stack; + int SP = 1; + + unsigned int live = table->keys - table->dead; + if (live > table->peakLiveNodes) { + table->peakLiveNodes = live; + } + + N = Cudd_Regular(n); + + do { +#ifdef DD_DEBUG + assert(N->ref != 0); +#endif + + if (N->ref == 1) { + N->ref = 0; + table->dead++; +#ifdef DD_STATS + table->nodesDropped++; +#endif + ord = table->perm[N->index]; + stack[SP++] = Cudd_Regular(cuddE(N)); + table->subtables[ord].dead++; + N = cuddT(N); + } else { + cuddSatDec(N->ref); + N = stack[--SP]; + } + } while (SP != 0); + +} /* end of Cudd_IterDerefBdd */ + + +/**Function******************************************************************** + + Synopsis [Decreases the reference count of BDD node n.] + + Description [Enqueues node n for later dereferencing. If the queue + is full decreases the reference count of the oldest node N to make + room for n. If N dies, recursively decreases the reference counts of + its children. It is used to dispose of a BDD that is currently not + needed, but may be useful again in the near future. The dereferencing + proper is done as in Cudd_IterDerefBdd.] + + SideEffects [None] + + SeeAlso [Cudd_RecursiveDeref Cudd_IterDerefBdd] + +******************************************************************************/ +void +Cudd_DelayedDerefBdd( + DdManager * table, + DdNode * n) +{ + DdNode *N; + int ord; + DdNodePtr *stack; + int SP; + + unsigned int live = table->keys - table->dead; + if (live > table->peakLiveNodes) { + table->peakLiveNodes = live; + } + + n = Cudd_Regular(n); +#ifdef DD_DEBUG + assert(n->ref != 0); +#endif + +#ifdef DD_NO_DEATH_ROW + N = n; +#else + if (cuddIsConstant(n) || n->ref > 1) { +#ifdef DD_DEBUG + assert(n->ref != 1 && (!cuddIsConstant(n) || n == DD_ONE(table))); +#endif + cuddSatDec(n->ref); + return; + } + + N = table->deathRow[table->nextDead]; + + if (N != NULL) { +#endif +#ifdef DD_DEBUG + assert(!Cudd_IsComplement(N)); +#endif + stack = table->stack; + SP = 1; + do { +#ifdef DD_DEBUG + assert(N->ref != 0); +#endif + if (N->ref == 1) { + N->ref = 0; + table->dead++; +#ifdef DD_STATS + table->nodesDropped++; +#endif + ord = table->perm[N->index]; + stack[SP++] = Cudd_Regular(cuddE(N)); + table->subtables[ord].dead++; + N = cuddT(N); + } else { + cuddSatDec(N->ref); + N = stack[--SP]; + } + } while (SP != 0); +#ifndef DD_NO_DEATH_ROW + } + table->deathRow[table->nextDead] = n; + + /* Udate insertion point. */ + table->nextDead++; + table->nextDead &= table->deadMask; +#if 0 + if (table->nextDead == table->deathRowDepth) { + if (table->deathRowDepth < table->looseUpTo / 2) { + extern void (*MMoutOfMemory)(long); + void (*saveHandler)(long) = MMoutOfMemory; + DdNodePtr *newRow; + MMoutOfMemory = Cudd_OutOfMem; + newRow = REALLOC(DdNodePtr,table->deathRow,2*table->deathRowDepth); + MMoutOfMemory = saveHandler; + if (newRow == NULL) { + table->nextDead = 0; + } else { + int i; + table->memused += table->deathRowDepth; + i = table->deathRowDepth; + table->deathRowDepth <<= 1; + for (; i < table->deathRowDepth; i++) { + newRow[i] = NULL; + } + table->deadMask = table->deathRowDepth - 1; + table->deathRow = newRow; + } + } else { + table->nextDead = 0; + } + } +#endif +#endif + +} /* end of Cudd_DelayedDerefBdd */ + + +/**Function******************************************************************** + + Synopsis [Decreases the reference count of ZDD node n.] + + Description [Decreases the reference count of ZDD node n. If n dies, + recursively decreases the reference counts of its children. It is + used to dispose of a ZDD that is no longer needed.] + + SideEffects [None] + + SeeAlso [Cudd_Deref Cudd_Ref Cudd_RecursiveDeref] + +******************************************************************************/ +void +Cudd_RecursiveDerefZdd( + DdManager * table, + DdNode * n) +{ + DdNode *N; + int ord; + DdNodePtr *stack = table->stack; + int SP = 1; + + N = n; + + do { +#ifdef DD_DEBUG + assert(N->ref != 0); +#endif + + cuddSatDec(N->ref); + + if (N->ref == 0) { + table->deadZ++; +#ifdef DD_STATS + table->nodesDropped++; +#endif +#ifdef DD_DEBUG + assert(!cuddIsConstant(N)); +#endif + ord = table->permZ[N->index]; + stack[SP++] = cuddE(N); + table->subtableZ[ord].dead++; + N = cuddT(N); + } else { + N = stack[--SP]; + } + } while (SP != 0); + +} /* end of Cudd_RecursiveDerefZdd */ + + +/**Function******************************************************************** + + Synopsis [Decreases the reference count of node.] + + Description [Decreases the reference count of node. It is primarily + used in recursive procedures to decrease the ref count of a result + node before returning it. This accomplishes the goal of removing the + protection applied by a previous Cudd_Ref.] + + SideEffects [None] + + SeeAlso [Cudd_RecursiveDeref Cudd_RecursiveDerefZdd Cudd_Ref] + +******************************************************************************/ +void +Cudd_Deref( + DdNode * node) +{ + node = Cudd_Regular(node); + cuddSatDec(node->ref); + +} /* end of Cudd_Deref */ + + +/**Function******************************************************************** + + Synopsis [Checks the unique table for nodes with non-zero reference + counts.] + + Description [Checks the unique table for nodes with non-zero + reference counts. It is normally called before Cudd_Quit to make sure + that there are no memory leaks due to missing Cudd_RecursiveDeref's. + Takes into account that reference counts may saturate and that the + basic constants and the projection functions are referenced by the + manager. Returns the number of nodes with non-zero reference count. + (Except for the cases mentioned above.)] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +Cudd_CheckZeroRef( + DdManager * manager) +{ + int size; + int i, j; + int remain; /* the expected number of remaining references to one */ + DdNodePtr *nodelist; + DdNode *node; + DdNode *sentinel = &(manager->sentinel); + DdSubtable *subtable; + int count = 0; + int index; + +#ifndef DD_NO_DEATH_ROW + cuddClearDeathRow(manager); +#endif + + /* First look at the BDD/ADD subtables. */ + remain = 1; /* reference from the manager */ + size = manager->size; + remain += 2 * size; /* reference from the BDD projection functions */ + + for (i = 0; i < size; i++) { + subtable = &(manager->subtables[i]); + nodelist = subtable->nodelist; + for (j = 0; (unsigned) j < subtable->slots; j++) { + node = nodelist[j]; + while (node != sentinel) { + if (node->ref != 0 && node->ref != DD_MAXREF) { + index = (int) node->index; + if (node != manager->vars[index]) { + count++; + } else { + if (node->ref != 1) { + count++; + } + } + } + node = node->next; + } + } + } + + /* Then look at the ZDD subtables. */ + size = manager->sizeZ; + if (size) /* references from ZDD universe */ + remain += 2; + + for (i = 0; i < size; i++) { + subtable = &(manager->subtableZ[i]); + nodelist = subtable->nodelist; + for (j = 0; (unsigned) j < subtable->slots; j++) { + node = nodelist[j]; + while (node != NULL) { + if (node->ref != 0 && node->ref != DD_MAXREF) { + index = (int) node->index; + if (node == manager->univ[manager->permZ[index]]) { + if (node->ref > 2) { + count++; + } + } else { + count++; + } + } + node = node->next; + } + } + } + + /* Now examine the constant table. Plusinfinity, minusinfinity, and + ** zero are referenced by the manager. One is referenced by the + ** manager, by the ZDD universe, and by all projection functions. + ** All other nodes should have no references. + */ + nodelist = manager->constants.nodelist; + for (j = 0; (unsigned) j < manager->constants.slots; j++) { + node = nodelist[j]; + while (node != NULL) { + if (node->ref != 0 && node->ref != DD_MAXREF) { + if (node == manager->one) { + if ((int) node->ref != remain) { + count++; + } + } else if (node == manager->zero || + node == manager->plusinfinity || + node == manager->minusinfinity) { + if (node->ref != 1) { + count++; + } + } else { + count++; + } + } + node = node->next; + } + } + return(count); + +} /* end of Cudd_CheckZeroRef */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Brings children of a dead node back.] + + Description [] + + SideEffects [None] + + SeeAlso [cuddReclaimZdd] + +******************************************************************************/ +void +cuddReclaim( + DdManager * table, + DdNode * n) +{ + DdNode *N; + int ord; + DdNodePtr *stack = table->stack; + int SP = 1; + double initialDead = table->dead; + + N = Cudd_Regular(n); + +#ifdef DD_DEBUG + assert(N->ref == 0); +#endif + + do { + if (N->ref == 0) { + N->ref = 1; + table->dead--; + if (cuddIsConstant(N)) { + table->constants.dead--; + N = stack[--SP]; + } else { + ord = table->perm[N->index]; + stack[SP++] = Cudd_Regular(cuddE(N)); + table->subtables[ord].dead--; + N = cuddT(N); + } + } else { + cuddSatInc(N->ref); + N = stack[--SP]; + } + } while (SP != 0); + + N = Cudd_Regular(n); + cuddSatDec(N->ref); + table->reclaimed += initialDead - table->dead; + +} /* end of cuddReclaim */ + + +/**Function******************************************************************** + + Synopsis [Brings children of a dead ZDD node back.] + + Description [] + + SideEffects [None] + + SeeAlso [cuddReclaim] + +******************************************************************************/ +void +cuddReclaimZdd( + DdManager * table, + DdNode * n) +{ + DdNode *N; + int ord; + DdNodePtr *stack = table->stack; + int SP = 1; + + N = n; + +#ifdef DD_DEBUG + assert(N->ref == 0); +#endif + + do { + cuddSatInc(N->ref); + + if (N->ref == 1) { + table->deadZ--; + table->reclaimed++; +#ifdef DD_DEBUG + assert(!cuddIsConstant(N)); +#endif + ord = table->permZ[N->index]; + stack[SP++] = cuddE(N); + table->subtableZ[ord].dead--; + N = cuddT(N); + } else { + N = stack[--SP]; + } + } while (SP != 0); + + cuddSatDec(n->ref); + +} /* end of cuddReclaimZdd */ + + +/**Function******************************************************************** + + Synopsis [Shrinks the death row.] + + Description [Shrinks the death row by a factor of four.] + + SideEffects [None] + + SeeAlso [cuddClearDeathRow] + +******************************************************************************/ +void +cuddShrinkDeathRow( + DdManager *table) +{ +#ifndef DD_NO_DEATH_ROW + int i; + + if (table->deathRowDepth > 3) { + for (i = table->deathRowDepth/4; i < table->deathRowDepth; i++) { + if (table->deathRow[i] == NULL) break; + Cudd_IterDerefBdd(table,table->deathRow[i]); + table->deathRow[i] = NULL; + } + table->deathRowDepth /= 4; + table->deadMask = table->deathRowDepth - 2; + if ((unsigned) table->nextDead > table->deadMask) { + table->nextDead = 0; + } + table->deathRow = REALLOC(DdNodePtr, table->deathRow, + table->deathRowDepth); + } +#endif + +} /* end of cuddShrinkDeathRow */ + + +/**Function******************************************************************** + + Synopsis [Clears the death row.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_DelayedDerefBdd Cudd_IterDerefBdd Cudd_CheckZeroRef + cuddGarbageCollect] + +******************************************************************************/ +void +cuddClearDeathRow( + DdManager *table) +{ +#ifndef DD_NO_DEATH_ROW + int i; + + for (i = 0; i < table->deathRowDepth; i++) { + if (table->deathRow[i] == NULL) break; + Cudd_IterDerefBdd(table,table->deathRow[i]); + table->deathRow[i] = NULL; + } +#ifdef DD_DEBUG + for (; i < table->deathRowDepth; i++) { + assert(table->deathRow[i] == NULL); + } +#endif + table->nextDead = 0; +#endif + +} /* end of cuddClearDeathRow */ + + +/**Function******************************************************************** + + Synopsis [Checks whether a node is in the death row.] + + Description [Checks whether a node is in the death row. Returns the + position of the first occurrence if the node is present; -1 + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_DelayedDerefBdd cuddClearDeathRow] + +******************************************************************************/ +int +cuddIsInDeathRow( + DdManager *dd, + DdNode *f) +{ +#ifndef DD_NO_DEATH_ROW + int i; + + for (i = 0; i < dd->deathRowDepth; i++) { + if (f == dd->deathRow[i]) { + return(i); + } + } +#endif + + return(-1); + +} /* end of cuddIsInDeathRow */ + + +/**Function******************************************************************** + + Synopsis [Counts how many times a node is in the death row.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_DelayedDerefBdd cuddClearDeathRow cuddIsInDeathRow] + +******************************************************************************/ +int +cuddTimesInDeathRow( + DdManager *dd, + DdNode *f) +{ + int count = 0; +#ifndef DD_NO_DEATH_ROW + int i; + + for (i = 0; i < dd->deathRowDepth; i++) { + count += f == dd->deathRow[i]; + } +#endif + + return(count); + +} /* end of cuddTimesInDeathRow */ + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddReorder.c b/abc_with_bb_support/src/bdd/cudd/cuddReorder.c new file mode 100644 index 000000000..6b3372626 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddReorder.c @@ -0,0 +1,2090 @@ +/**CFile*********************************************************************** + + FileName [cuddReorder.c] + + PackageName [cudd] + + Synopsis [Functions for dynamic variable reordering.] + + Description [External procedures included in this file: +
        +
      • Cudd_ReduceHeap() +
      • Cudd_ShuffleHeap() +
      + Internal procedures included in this module: +
        +
      • cuddDynamicAllocNode() +
      • cuddSifting() +
      • cuddSwapping() +
      • cuddNextHigh() +
      • cuddNextLow() +
      • cuddSwapInPlace() +
      • cuddBddAlignToZdd() +
      + Static procedures included in this module: +
        +
      • ddUniqueCompare() +
      • ddSwapAny() +
      • ddSiftingAux() +
      • ddSiftingUp() +
      • ddSiftingDown() +
      • ddSiftingBackward() +
      • ddReorderPreprocess() +
      • ddReorderPostprocess() +
      • ddShuffle() +
      • ddSiftUp() +
      • bddFixTree() +
      ] + + Author [Shipra Panda, Bernard Plessier, Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define DD_MAX_SUBTABLE_SPARSITY 8 +#define DD_SHRINK_FACTOR 2 + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddReorder.c,v 1.1.1.1 2003/02/24 22:23:53 wjiang Exp $"; +#endif + +static int *entry; + +int ddTotalNumberSwapping; +#ifdef DD_STATS +int ddTotalNISwaps; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int ddUniqueCompare ARGS((int *ptrX, int *ptrY)); +static Move * ddSwapAny ARGS((DdManager *table, int x, int y)); +static int ddSiftingAux ARGS((DdManager *table, int x, int xLow, int xHigh)); +static Move * ddSiftingUp ARGS((DdManager *table, int y, int xLow)); +static Move * ddSiftingDown ARGS((DdManager *table, int x, int xHigh)); +static int ddSiftingBackward ARGS((DdManager *table, int size, Move *moves)); +static int ddReorderPreprocess ARGS((DdManager *table)); +static int ddReorderPostprocess ARGS((DdManager *table)); +static int ddShuffle ARGS((DdManager *table, int *permutation)); +static int ddSiftUp ARGS((DdManager *table, int x, int xLow)); +static void bddFixTree ARGS((DdManager *table, MtrNode *treenode)); +static int ddUpdateMtrTree ARGS((DdManager *table, MtrNode *treenode, int *perm, int *invperm)); +static int ddCheckPermuation ARGS((DdManager *table, MtrNode *treenode, int *perm, int *invperm)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Main dynamic reordering routine.] + + Description [Main dynamic reordering routine. + Calls one of the possible reordering procedures: +
        +
      • Swapping +
      • Sifting +
      • Symmetric Sifting +
      • Group Sifting +
      • Window Permutation +
      • Simulated Annealing +
      • Genetic Algorithm +
      • Dynamic Programming (exact) +
      + + For sifting, symmetric sifting, group sifting, and window + permutation it is possible to request reordering to convergence.

      + + The core of all methods is the reordering procedure + cuddSwapInPlace() which swaps two adjacent variables and is based + on Rudell's paper. + Returns 1 in case of success; 0 otherwise. In the case of symmetric + sifting (with and without convergence) returns 1 plus the number of + symmetric variables, in case of success.] + + SideEffects [Changes the variable order for all diagrams and clears + the cache.] + +******************************************************************************/ +int +Cudd_ReduceHeap( + DdManager * table /* DD manager */, + Cudd_ReorderingType heuristic /* method used for reordering */, + int minsize /* bound below which no reordering occurs */) +{ + DdHook *hook; + int result; + unsigned int nextDyn; +#ifdef DD_STATS + unsigned int initialSize; + unsigned int finalSize; +#endif + long localTime; + + /* Don't reorder if there are too many dead nodes. */ + if (table->keys - table->dead < (unsigned) minsize) + return(1); + + if (heuristic == CUDD_REORDER_SAME) { + heuristic = table->autoMethod; + } + if (heuristic == CUDD_REORDER_NONE) { + return(1); + } + + /* This call to Cudd_ReduceHeap does initiate reordering. Therefore + ** we count it. + */ + table->reorderings++; + + localTime = util_cpu_time(); + + /* Run the hook functions. */ + hook = table->preReorderingHook; + while (hook != NULL) { + int res = (hook->f)(table, "BDD", (void *)heuristic); + if (res == 0) return(0); + hook = hook->next; + } + + if (!ddReorderPreprocess(table)) return(0); + ddTotalNumberSwapping = 0; + + if (table->keys > table->peakLiveNodes) { + table->peakLiveNodes = table->keys; + } +#ifdef DD_STATS + initialSize = table->keys - table->isolated; + ddTotalNISwaps = 0; + + switch(heuristic) { + case CUDD_REORDER_RANDOM: + case CUDD_REORDER_RANDOM_PIVOT: + (void) fprintf(table->out,"#:I_RANDOM "); + break; + case CUDD_REORDER_SIFT: + case CUDD_REORDER_SIFT_CONVERGE: + case CUDD_REORDER_SYMM_SIFT: + case CUDD_REORDER_SYMM_SIFT_CONV: + case CUDD_REORDER_GROUP_SIFT: + case CUDD_REORDER_GROUP_SIFT_CONV: + (void) fprintf(table->out,"#:I_SIFTING "); + break; + case CUDD_REORDER_WINDOW2: + case CUDD_REORDER_WINDOW3: + case CUDD_REORDER_WINDOW4: + case CUDD_REORDER_WINDOW2_CONV: + case CUDD_REORDER_WINDOW3_CONV: + case CUDD_REORDER_WINDOW4_CONV: + (void) fprintf(table->out,"#:I_WINDOW "); + break; + case CUDD_REORDER_ANNEALING: + (void) fprintf(table->out,"#:I_ANNEAL "); + break; + case CUDD_REORDER_GENETIC: + (void) fprintf(table->out,"#:I_GENETIC "); + break; + case CUDD_REORDER_LINEAR: + case CUDD_REORDER_LINEAR_CONVERGE: + (void) fprintf(table->out,"#:I_LINSIFT "); + break; + case CUDD_REORDER_EXACT: + (void) fprintf(table->out,"#:I_EXACT "); + break; + default: + return(0); + } + (void) fprintf(table->out,"%8d: initial size",initialSize); +#endif + + /* See if we should use alternate threshold for maximum growth. */ + if (table->reordCycle && table->reorderings % table->reordCycle == 0) { + double saveGrowth = table->maxGrowth; + table->maxGrowth = table->maxGrowthAlt; + result = cuddTreeSifting(table,heuristic); + table->maxGrowth = saveGrowth; + } else { + result = cuddTreeSifting(table,heuristic); + } + +#ifdef DD_STATS + (void) fprintf(table->out,"\n"); + finalSize = table->keys - table->isolated; + (void) fprintf(table->out,"#:F_REORDER %8d: final size\n",finalSize); + (void) fprintf(table->out,"#:T_REORDER %8g: total time (sec)\n", + ((double)(util_cpu_time() - localTime)/1000.0)); + (void) fprintf(table->out,"#:N_REORDER %8d: total swaps\n", + ddTotalNumberSwapping); + (void) fprintf(table->out,"#:M_REORDER %8d: NI swaps\n",ddTotalNISwaps); +#endif + + if (result == 0) + return(0); + + if (!ddReorderPostprocess(table)) + return(0); + + if (table->realign) { + if (!cuddZddAlignToBdd(table)) + return(0); + } + + nextDyn = (table->keys - table->constants.keys + 1) * + DD_DYN_RATIO + table->constants.keys; + if (table->reorderings < 20 || nextDyn > table->nextDyn) + table->nextDyn = nextDyn; + else + table->nextDyn += 20; + table->reordered = 1; + + /* Run hook functions. */ + hook = table->postReorderingHook; + while (hook != NULL) { + int res = (hook->f)(table, "BDD", (void *)localTime); + if (res == 0) return(0); + hook = hook->next; + } + /* Update cumulative reordering time. */ + table->reordTime += util_cpu_time() - localTime; + + return(result); + +} /* end of Cudd_ReduceHeap */ + + +/**Function******************************************************************** + + Synopsis [Reorders variables according to given permutation.] + + Description [Reorders variables according to given permutation. + The i-th entry of the permutation array contains the index of the variable + that should be brought to the i-th level. The size of the array should be + equal or greater to the number of variables currently in use. + Returns 1 in case of success; 0 otherwise.] + + SideEffects [Changes the variable order for all diagrams and clears + the cache.] + + SeeAlso [Cudd_ReduceHeap] + +******************************************************************************/ +int +Cudd_ShuffleHeap( + DdManager * table /* DD manager */, + int * permutation /* required variable permutation */) +{ + + int result; + int i; + int identity = 1; + int *perm; + + /* Don't waste time in case of identity permutation. */ + for (i = 0; i < table->size; i++) { + if (permutation[i] != table->invperm[i]) { + identity = 0; + break; + } + } + if (identity == 1) { + return(1); + } + if (!ddReorderPreprocess(table)) return(0); + if (table->keys > table->peakLiveNodes) { + table->peakLiveNodes = table->keys; + } + + perm = ALLOC(int, table->size); + for (i = 0; i < table->size; i++) + perm[permutation[i]] = i; + if (!ddCheckPermuation(table,table->tree,perm,permutation)) { + FREE(perm); + return(0); + } + if (!ddUpdateMtrTree(table,table->tree,perm,permutation)) { + FREE(perm); + return(0); + } + FREE(perm); + + result = ddShuffle(table,permutation); + + if (!ddReorderPostprocess(table)) return(0); + + return(result); + +} /* end of Cudd_ShuffleHeap */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Dynamically allocates a Node.] + + Description [Dynamically allocates a Node. This procedure is similar + to cuddAllocNode in Cudd_Table.c, but it does not attempt garbage + collection, because during reordering there are no dead nodes. + Returns a pointer to a new node if successful; NULL is memory is + full.] + + SideEffects [None] + + SeeAlso [cuddAllocNode] + +******************************************************************************/ +DdNode * +cuddDynamicAllocNode( + DdManager * table) +{ + int i; + DdNodePtr *mem; + DdNode *list, *node; + extern void (*MMoutOfMemory)(long); + void (*saveHandler)(long); + + if (table->nextFree == NULL) { /* free list is empty */ + /* Try to allocate a new block. */ + saveHandler = MMoutOfMemory; + MMoutOfMemory = Cudd_OutOfMem; + mem = (DdNodePtr *) ALLOC(DdNode, DD_MEM_CHUNK + 1); + MMoutOfMemory = saveHandler; + if (mem == NULL && table->stash != NULL) { + FREE(table->stash); + table->stash = NULL; + /* Inhibit resizing of tables. */ + table->maxCacheHard = table->cacheSlots - 1; + table->cacheSlack = -(table->cacheSlots + 1); + for (i = 0; i < table->size; i++) { + table->subtables[i].maxKeys <<= 2; + } + mem = (DdNodePtr *) ALLOC(DdNode,DD_MEM_CHUNK + 1); + } + if (mem == NULL) { + /* Out of luck. Call the default handler to do + ** whatever it specifies for a failed malloc. If this + ** handler returns, then set error code, print + ** warning, and return. */ + (*MMoutOfMemory)(sizeof(DdNode)*(DD_MEM_CHUNK + 1)); + table->errorCode = CUDD_MEMORY_OUT; +#ifdef DD_VERBOSE + (void) fprintf(table->err, + "cuddDynamicAllocNode: out of memory"); + (void) fprintf(table->err,"Memory in use = %ld\n", + table->memused); +#endif + return(NULL); + } else { /* successful allocation; slice memory */ + unsigned long offset; + table->memused += (DD_MEM_CHUNK + 1) * sizeof(DdNode); + mem[0] = (DdNode *) table->memoryList; + table->memoryList = mem; + + /* Here we rely on the fact that the size of a DdNode is a + ** power of 2 and a multiple of the size of a pointer. + ** If we align one node, all the others will be aligned + ** as well. */ + offset = (unsigned long) mem & (sizeof(DdNode) - 1); + mem += (sizeof(DdNode) - offset) / sizeof(DdNodePtr); +#ifdef DD_DEBUG + assert(((unsigned long) mem & (sizeof(DdNode) - 1)) == 0); +#endif + list = (DdNode *) mem; + + i = 1; + do { + list[i - 1].next = &list[i]; + } while (++i < DD_MEM_CHUNK); + + list[DD_MEM_CHUNK - 1].next = NULL; + + table->nextFree = &list[0]; + } + } /* if free list empty */ + + node = table->nextFree; + table->nextFree = node->next; + return (node); + +} /* end of cuddDynamicAllocNode */ + + +/**Function******************************************************************** + + Synopsis [Implementation of Rudell's sifting algorithm.] + + Description [Implementation of Rudell's sifting algorithm. + Assumes that no dead nodes are present. +

        +
      1. Order all the variables according to the number of entries + in each unique table. +
      2. Sift the variable up and down, remembering each time the + total size of the DD heap. +
      3. Select the best permutation. +
      4. Repeat 3 and 4 for all variables. +
      + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +int +cuddSifting( + DdManager * table, + int lower, + int upper) +{ + int i; + int *var; + int size; + int x; + int result; +#ifdef DD_STATS + int previousSize; +#endif + + size = table->size; + + /* Find order in which to sift variables. */ + var = NULL; + entry = ALLOC(int,size); + if (entry == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddSiftingOutOfMem; + } + var = ALLOC(int,size); + if (var == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddSiftingOutOfMem; + } + + for (i = 0; i < size; i++) { + x = table->perm[i]; + entry[i] = table->subtables[x].keys; + var[i] = i; + } + + qsort((void *)var,size,sizeof(int),(int (*)(const void *, const void *))ddUniqueCompare); + + /* Now sift. */ + for (i = 0; i < ddMin(table->siftMaxVar,size); i++) { + if (ddTotalNumberSwapping >= table->siftMaxSwap) + break; + x = table->perm[var[i]]; + + if (x < lower || x > upper || table->subtables[x].bindVar == 1) + continue; +#ifdef DD_STATS + previousSize = table->keys - table->isolated; +#endif + result = ddSiftingAux(table, x, lower, upper); + if (!result) goto cuddSiftingOutOfMem; +#ifdef DD_STATS + if (table->keys < (unsigned) previousSize + table->isolated) { + (void) fprintf(table->out,"-"); + } else if (table->keys > (unsigned) previousSize + table->isolated) { + (void) fprintf(table->out,"+"); /* should never happen */ + (void) fprintf(table->err,"\nSize increased from %d to %d while sifting variable %d\n", previousSize, table->keys - table->isolated, var[i]); + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + + FREE(var); + FREE(entry); + + return(1); + +cuddSiftingOutOfMem: + + if (entry != NULL) FREE(entry); + if (var != NULL) FREE(var); + + return(0); + +} /* end of cuddSifting */ + + +/**Function******************************************************************** + + Synopsis [Reorders variables by a sequence of (non-adjacent) swaps.] + + Description [Implementation of Plessier's algorithm that reorders + variables by a sequence of (non-adjacent) swaps. +
        +
      1. Select two variables (RANDOM or HEURISTIC). +
      2. Permute these variables. +
      3. If the nodes have decreased accept the permutation. +
      4. Otherwise reconstruct the original heap. +
      5. Loop. +
      + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +int +cuddSwapping( + DdManager * table, + int lower, + int upper, + Cudd_ReorderingType heuristic) +{ + int i, j; + int max, keys; + int nvars; + int x, y; + int iterate; + int previousSize; + Move *moves, *move; + int pivot; + int modulo; + int result; + +#ifdef DD_DEBUG + /* Sanity check */ + assert(lower >= 0 && upper < table->size && lower <= upper); +#endif + + nvars = upper - lower + 1; + iterate = nvars; + + for (i = 0; i < iterate; i++) { + if (ddTotalNumberSwapping >= table->siftMaxSwap) + break; + if (heuristic == CUDD_REORDER_RANDOM_PIVOT) { + max = -1; + for (j = lower; j <= upper; j++) { + if ((keys = table->subtables[j].keys) > max) { + max = keys; + pivot = j; + } + } + + modulo = upper - pivot; + if (modulo == 0) { + y = pivot; + } else{ + y = pivot + 1 + ((int) Cudd_Random() % modulo); + } + + modulo = pivot - lower - 1; + if (modulo < 1) { + x = lower; + } else{ + do { + x = (int) Cudd_Random() % modulo; + } while (x == y); + } + } else { + x = ((int) Cudd_Random() % nvars) + lower; + do { + y = ((int) Cudd_Random() % nvars) + lower; + } while (x == y); + } + previousSize = table->keys - table->isolated; + moves = ddSwapAny(table,x,y); + if (moves == NULL) goto cuddSwappingOutOfMem; + result = ddSiftingBackward(table,previousSize,moves); + if (!result) goto cuddSwappingOutOfMem; + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *) moves); + moves = move; + } +#ifdef DD_STATS + if (table->keys < (unsigned) previousSize + table->isolated) { + (void) fprintf(table->out,"-"); + } else if (table->keys > (unsigned) previousSize + table->isolated) { + (void) fprintf(table->out,"+"); /* should never happen */ + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif +#if 0 + (void) fprintf(table->out,"#:t_SWAPPING %8d: tmp size\n", + table->keys - table->isolated); +#endif + } + + return(1); + +cuddSwappingOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *) moves); + moves = move; + } + + return(0); + +} /* end of cuddSwapping */ + + +/**Function******************************************************************** + + Synopsis [Finds the next subtable with a larger index.] + + Description [Finds the next subtable with a larger index. Returns the + index.] + + SideEffects [None] + + SeeAlso [cuddNextLow] + +******************************************************************************/ +int +cuddNextHigh( + DdManager * table, + int x) +{ + return(x+1); + +} /* end of cuddNextHigh */ + + +/**Function******************************************************************** + + Synopsis [Finds the next subtable with a smaller index.] + + Description [Finds the next subtable with a smaller index. Returns the + index.] + + SideEffects [None] + + SeeAlso [cuddNextHigh] + +******************************************************************************/ +int +cuddNextLow( + DdManager * table, + int x) +{ + return(x-1); + +} /* end of cuddNextLow */ + + +/**Function******************************************************************** + + Synopsis [Swaps two adjacent variables.] + + Description [Swaps two adjacent variables. It assumes that no dead + nodes are present on entry to this procedure. The procedure then + guarantees that no dead nodes will be present when it terminates. + cuddSwapInPlace assumes that x < y. Returns the number of keys in + the table if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +int +cuddSwapInPlace( + DdManager * table, + int x, + int y) +{ + DdNodePtr *xlist, *ylist; + int xindex, yindex; + int xslots, yslots; + int xshift, yshift; + int oldxkeys, oldykeys; + int newxkeys, newykeys; + int comple, newcomplement; + int i; + Cudd_VariableType varType; + Cudd_LazyGroupType groupType; + int posn; + int isolated; + DdNode *f,*f0,*f1,*f01,*f00,*f11,*f10,*newf1,*newf0; + DdNode *g,*next; + DdNodePtr *previousP; + DdNode *tmp; + DdNode *sentinel = &(table->sentinel); + extern void (*MMoutOfMemory)(long); + void (*saveHandler)(long); + +#if DD_DEBUG + int count,idcheck; +#endif + +#ifdef DD_DEBUG + assert(x < y); + assert(cuddNextHigh(table,x) == y); + assert(table->subtables[x].keys != 0); + assert(table->subtables[y].keys != 0); + assert(table->subtables[x].dead == 0); + assert(table->subtables[y].dead == 0); +#endif + + ddTotalNumberSwapping++; + + /* Get parameters of x subtable. */ + xindex = table->invperm[x]; + xlist = table->subtables[x].nodelist; + oldxkeys = table->subtables[x].keys; + xslots = table->subtables[x].slots; + xshift = table->subtables[x].shift; + + /* Get parameters of y subtable. */ + yindex = table->invperm[y]; + ylist = table->subtables[y].nodelist; + oldykeys = table->subtables[y].keys; + yslots = table->subtables[y].slots; + yshift = table->subtables[y].shift; + + if (!cuddTestInteract(table,xindex,yindex)) { +#ifdef DD_STATS + ddTotalNISwaps++; +#endif + newxkeys = oldxkeys; + newykeys = oldykeys; + } else { + newxkeys = 0; + newykeys = oldykeys; + + /* Check whether the two projection functions involved in this + ** swap are isolated. At the end, we'll be able to tell how many + ** isolated projection functions are there by checking only these + ** two functions again. This is done to eliminate the isolated + ** projection functions from the node count. + */ + isolated = - ((table->vars[xindex]->ref == 1) + + (table->vars[yindex]->ref == 1)); + + /* The nodes in the x layer that do not depend on + ** y will stay there; the others are put in a chain. + ** The chain is handled as a LIFO; g points to the beginning. + */ + g = NULL; + if ((oldxkeys >= xslots || (unsigned) xslots == table->initSlots) && + oldxkeys <= DD_MAX_SUBTABLE_DENSITY * xslots) { + for (i = 0; i < xslots; i++) { + previousP = &(xlist[i]); + f = *previousP; + while (f != sentinel) { + next = f->next; + f1 = cuddT(f); f0 = cuddE(f); + if (f1->index != (DdHalfWord) yindex && + Cudd_Regular(f0)->index != (DdHalfWord) yindex) { + /* stays */ + newxkeys++; + *previousP = f; + previousP = &(f->next); + } else { + f->index = yindex; + f->next = g; + g = f; + } + f = next; + } /* while there are elements in the collision chain */ + *previousP = sentinel; + } /* for each slot of the x subtable */ + } else { /* resize xlist */ + DdNode *h = NULL; + DdNodePtr *newxlist; + unsigned int newxslots; + int newxshift; + /* Empty current xlist. Nodes that stay go to list h; + ** nodes that move go to list g. */ + for (i = 0; i < xslots; i++) { + f = xlist[i]; + while (f != sentinel) { + next = f->next; + f1 = cuddT(f); f0 = cuddE(f); + if (f1->index != (DdHalfWord) yindex && + Cudd_Regular(f0)->index != (DdHalfWord) yindex) { + /* stays */ + f->next = h; + h = f; + newxkeys++; + } else { + f->index = yindex; + f->next = g; + g = f; + } + f = next; + } /* while there are elements in the collision chain */ + } /* for each slot of the x subtable */ + /* Decide size of new subtable. */ + if (oldxkeys > DD_MAX_SUBTABLE_DENSITY * xslots) { + newxshift = xshift - 1; + newxslots = xslots << 1; + } else { + newxshift = xshift + 1; + newxslots = xslots >> 1; + } + /* Try to allocate new table. Be ready to back off. */ + saveHandler = MMoutOfMemory; + MMoutOfMemory = Cudd_OutOfMem; + newxlist = ALLOC(DdNodePtr, newxslots); + MMoutOfMemory = saveHandler; + if (newxlist == NULL) { + (void) fprintf(table->err, "Unable to resize subtable %d for lack of memory\n", i); + newxlist = xlist; + newxslots = xslots; + newxshift = xshift; + } else { + table->slots += (newxslots - xslots); + table->minDead = (unsigned) + (table->gcFrac * (double) table->slots); + table->cacheSlack = (int) + ddMin(table->maxCacheHard, DD_MAX_CACHE_TO_SLOTS_RATIO + * table->slots) - 2 * (int) table->cacheSlots; + table->memused += (newxslots - xslots) * sizeof(DdNodePtr); + FREE(xlist); + xslots = newxslots; + xshift = newxshift; + xlist = newxlist; + } + /* Initialize new subtable. */ + for (i = 0; i < xslots; i++) { + xlist[i] = sentinel; + } + /* Move nodes that were parked in list h to their new home. */ + f = h; + while (f != NULL) { + next = f->next; + f1 = cuddT(f); + f0 = cuddE(f); + /* Check xlist for pair (f11,f01). */ + posn = ddHash(f1, f0, xshift); + /* For each element tmp in collision list xlist[posn]. */ + previousP = &(xlist[posn]); + tmp = *previousP; + while (f1 < cuddT(tmp)) { + previousP = &(tmp->next); + tmp = *previousP; + } + while (f1 == cuddT(tmp) && f0 < cuddE(tmp)) { + previousP = &(tmp->next); + tmp = *previousP; + } + f->next = *previousP; + *previousP = f; + f = next; + } + } + +#ifdef DD_COUNT + table->swapSteps += oldxkeys - newxkeys; +#endif + /* Take care of the x nodes that must be re-expressed. + ** They form a linked list pointed by g. Their index has been + ** already changed to yindex. + */ + f = g; + while (f != NULL) { + next = f->next; + /* Find f1, f0, f11, f10, f01, f00. */ + f1 = cuddT(f); +#ifdef DD_DEBUG + assert(!(Cudd_IsComplement(f1))); +#endif + if ((int) f1->index == yindex) { + f11 = cuddT(f1); f10 = cuddE(f1); + } else { + f11 = f10 = f1; + } +#ifdef DD_DEBUG + assert(!(Cudd_IsComplement(f11))); +#endif + f0 = cuddE(f); + comple = Cudd_IsComplement(f0); + f0 = Cudd_Regular(f0); + if ((int) f0->index == yindex) { + f01 = cuddT(f0); f00 = cuddE(f0); + } else { + f01 = f00 = f0; + } + if (comple) { + f01 = Cudd_Not(f01); + f00 = Cudd_Not(f00); + } + /* Decrease ref count of f1. */ + cuddSatDec(f1->ref); + /* Create the new T child. */ + if (f11 == f01) { + newf1 = f11; + cuddSatInc(newf1->ref); + } else { + /* Check xlist for triple (xindex,f11,f01). */ + posn = ddHash(f11, f01, xshift); + /* For each element newf1 in collision list xlist[posn]. */ + previousP = &(xlist[posn]); + newf1 = *previousP; + while (f11 < cuddT(newf1)) { + previousP = &(newf1->next); + newf1 = *previousP; + } + while (f11 == cuddT(newf1) && f01 < cuddE(newf1)) { + previousP = &(newf1->next); + newf1 = *previousP; + } + if (cuddT(newf1) == f11 && cuddE(newf1) == f01) { + cuddSatInc(newf1->ref); + } else { /* no match */ + newf1 = cuddDynamicAllocNode(table); + if (newf1 == NULL) + goto cuddSwapOutOfMem; + newf1->index = xindex; newf1->ref = 1; + cuddT(newf1) = f11; + cuddE(newf1) = f01; + /* Insert newf1 in the collision list xlist[posn]; + ** increase the ref counts of f11 and f01. + */ + newxkeys++; + newf1->next = *previousP; + *previousP = newf1; + cuddSatInc(f11->ref); + tmp = Cudd_Regular(f01); + cuddSatInc(tmp->ref); + } + } + cuddT(f) = newf1; +#ifdef DD_DEBUG + assert(!(Cudd_IsComplement(newf1))); +#endif + + /* Do the same for f0, keeping complement dots into account. */ + /* Decrease ref count of f0. */ + tmp = Cudd_Regular(f0); + cuddSatDec(tmp->ref); + /* Create the new E child. */ + if (f10 == f00) { + newf0 = f00; + tmp = Cudd_Regular(newf0); + cuddSatInc(tmp->ref); + } else { + /* make sure f10 is regular */ + newcomplement = Cudd_IsComplement(f10); + if (newcomplement) { + f10 = Cudd_Not(f10); + f00 = Cudd_Not(f00); + } + /* Check xlist for triple (xindex,f10,f00). */ + posn = ddHash(f10, f00, xshift); + /* For each element newf0 in collision list xlist[posn]. */ + previousP = &(xlist[posn]); + newf0 = *previousP; + while (f10 < cuddT(newf0)) { + previousP = &(newf0->next); + newf0 = *previousP; + } + while (f10 == cuddT(newf0) && f00 < cuddE(newf0)) { + previousP = &(newf0->next); + newf0 = *previousP; + } + if (cuddT(newf0) == f10 && cuddE(newf0) == f00) { + cuddSatInc(newf0->ref); + } else { /* no match */ + newf0 = cuddDynamicAllocNode(table); + if (newf0 == NULL) + goto cuddSwapOutOfMem; + newf0->index = xindex; newf0->ref = 1; + cuddT(newf0) = f10; + cuddE(newf0) = f00; + /* Insert newf0 in the collision list xlist[posn]; + ** increase the ref counts of f10 and f00. + */ + newxkeys++; + newf0->next = *previousP; + *previousP = newf0; + cuddSatInc(f10->ref); + tmp = Cudd_Regular(f00); + cuddSatInc(tmp->ref); + } + if (newcomplement) { + newf0 = Cudd_Not(newf0); + } + } + cuddE(f) = newf0; + + /* Insert the modified f in ylist. + ** The modified f does not already exists in ylist. + ** (Because of the uniqueness of the cofactors.) + */ + posn = ddHash(newf1, newf0, yshift); + newykeys++; + previousP = &(ylist[posn]); + tmp = *previousP; + while (newf1 < cuddT(tmp)) { + previousP = &(tmp->next); + tmp = *previousP; + } + while (newf1 == cuddT(tmp) && newf0 < cuddE(tmp)) { + previousP = &(tmp->next); + tmp = *previousP; + } + f->next = *previousP; + *previousP = f; + f = next; + } /* while f != NULL */ + + /* GC the y layer. */ + + /* For each node f in ylist. */ + for (i = 0; i < yslots; i++) { + previousP = &(ylist[i]); + f = *previousP; + while (f != sentinel) { + next = f->next; + if (f->ref == 0) { + tmp = cuddT(f); + cuddSatDec(tmp->ref); + tmp = Cudd_Regular(cuddE(f)); + cuddSatDec(tmp->ref); + cuddDeallocNode(table,f); + newykeys--; + } else { + *previousP = f; + previousP = &(f->next); + } + f = next; + } /* while f */ + *previousP = sentinel; + } /* for i */ + +#if DD_DEBUG +#if 0 + (void) fprintf(table->out,"Swapping %d and %d\n",x,y); +#endif + count = 0; + idcheck = 0; + for (i = 0; i < yslots; i++) { + f = ylist[i]; + while (f != sentinel) { + count++; + if (f->index != (DdHalfWord) yindex) + idcheck++; + f = f->next; + } + } + if (count != newykeys) { + (void) fprintf(table->out, + "Error in finding newykeys\toldykeys = %d\tnewykeys = %d\tactual = %d\n", + oldykeys,newykeys,count); + } + if (idcheck != 0) + (void) fprintf(table->out, + "Error in id's of ylist\twrong id's = %d\n", + idcheck); + count = 0; + idcheck = 0; + for (i = 0; i < xslots; i++) { + f = xlist[i]; + while (f != sentinel) { + count++; + if (f->index != (DdHalfWord) xindex) + idcheck++; + f = f->next; + } + } + if (count != newxkeys) { + (void) fprintf(table->out, + "Error in finding newxkeys\toldxkeys = %d \tnewxkeys = %d \tactual = %d\n", + oldxkeys,newxkeys,count); + } + if (idcheck != 0) + (void) fprintf(table->out, + "Error in id's of xlist\twrong id's = %d\n", + idcheck); +#endif + + isolated += (table->vars[xindex]->ref == 1) + + (table->vars[yindex]->ref == 1); + table->isolated += isolated; + } + + /* Set the appropriate fields in table. */ + table->subtables[x].nodelist = ylist; + table->subtables[x].slots = yslots; + table->subtables[x].shift = yshift; + table->subtables[x].keys = newykeys; + table->subtables[x].maxKeys = yslots * DD_MAX_SUBTABLE_DENSITY; + i = table->subtables[x].bindVar; + table->subtables[x].bindVar = table->subtables[y].bindVar; + table->subtables[y].bindVar = i; + /* Adjust filds for lazy sifting. */ + varType = table->subtables[x].varType; + table->subtables[x].varType = table->subtables[y].varType; + table->subtables[y].varType = varType; + i = table->subtables[x].pairIndex; + table->subtables[x].pairIndex = table->subtables[y].pairIndex; + table->subtables[y].pairIndex = i; + i = table->subtables[x].varHandled; + table->subtables[x].varHandled = table->subtables[y].varHandled; + table->subtables[y].varHandled = i; + groupType = table->subtables[x].varToBeGrouped; + table->subtables[x].varToBeGrouped = table->subtables[y].varToBeGrouped; + table->subtables[y].varToBeGrouped = groupType; + + table->subtables[y].nodelist = xlist; + table->subtables[y].slots = xslots; + table->subtables[y].shift = xshift; + table->subtables[y].keys = newxkeys; + table->subtables[y].maxKeys = xslots * DD_MAX_SUBTABLE_DENSITY; + + table->perm[xindex] = y; table->perm[yindex] = x; + table->invperm[x] = yindex; table->invperm[y] = xindex; + + table->keys += newxkeys + newykeys - oldxkeys - oldykeys; + + return(table->keys - table->isolated); + +cuddSwapOutOfMem: + (void) fprintf(table->err,"Error: cuddSwapInPlace out of memory\n"); + + return (0); + +} /* end of cuddSwapInPlace */ + + +/**Function******************************************************************** + + Synopsis [Reorders BDD variables according to the order of the ZDD + variables.] + + Description [Reorders BDD variables according to the order of the + ZDD variables. This function can be called at the end of ZDD + reordering to insure that the order of the BDD variables is + consistent with the order of the ZDD variables. The number of ZDD + variables must be a multiple of the number of BDD variables. Let + M be the ratio of the two numbers. cuddBddAlignToZdd + then considers the ZDD variables from M*i to + (M+1)*i-1 as corresponding to BDD variable + i. This function should be normally called from + Cudd_zddReduceHeap, which clears the cache. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [Changes the BDD variable order for all diagrams and performs + garbage collection of the BDD unique table.] + + SeeAlso [Cudd_ShuffleHeap Cudd_zddReduceHeap] + +******************************************************************************/ +int +cuddBddAlignToZdd( + DdManager * table /* DD manager */) +{ + int *invperm; /* permutation array */ + int M; /* ratio of ZDD variables to BDD variables */ + int i; /* loop index */ + int result; /* return value */ + + /* We assume that a ratio of 0 is OK. */ + if (table->size == 0) + return(1); + + M = table->sizeZ / table->size; + /* Check whether the number of ZDD variables is a multiple of the + ** number of BDD variables. + */ + if (M * table->size != table->sizeZ) + return(0); + /* Create and initialize the inverse permutation array. */ + invperm = ALLOC(int,table->size); + if (invperm == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (i = 0; i < table->sizeZ; i += M) { + int indexZ = table->invpermZ[i]; + int index = indexZ / M; + invperm[i / M] = index; + } + /* Eliminate dead nodes. Do not scan the cache again, because we + ** assume that Cudd_zddReduceHeap has already cleared it. + */ + cuddGarbageCollect(table,0); + + /* Initialize number of isolated projection functions. */ + table->isolated = 0; + for (i = 0; i < table->size; i++) { + if (table->vars[i]->ref == 1) table->isolated++; + } + + /* Initialize the interaction matrix. */ + result = cuddInitInteract(table); + if (result == 0) return(0); + + result = ddShuffle(table, invperm); + FREE(invperm); + /* Free interaction matrix. */ + FREE(table->interact); + /* Fix the BDD variable group tree. */ + bddFixTree(table,table->tree); + return(result); + +} /* end of cuddBddAlignToZdd */ + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Comparison function used by qsort.] + + Description [Comparison function used by qsort to order the + variables according to the number of keys in the subtables. + Returns the difference in number of keys between the two + variables being compared.] + + SideEffects [None] + +******************************************************************************/ +static int +ddUniqueCompare( + int * ptrX, + int * ptrY) +{ +#if 0 + if (entry[*ptrY] == entry[*ptrX]) { + return((*ptrX) - (*ptrY)); + } +#endif + return(entry[*ptrY] - entry[*ptrX]); + +} /* end of ddUniqueCompare */ + + +/**Function******************************************************************** + + Synopsis [Swaps any two variables.] + + Description [Swaps any two variables. Returns the set of moves.] + + SideEffects [None] + +******************************************************************************/ +static Move * +ddSwapAny( + DdManager * table, + int x, + int y) +{ + Move *move, *moves; + int xRef,yRef; + int xNext,yNext; + int size; + int limitSize; + int tmp; + + if (x >y) { + tmp = x; x = y; y = tmp; + } + + xRef = x; yRef = y; + + xNext = cuddNextHigh(table,x); + yNext = cuddNextLow(table,y); + moves = NULL; + limitSize = table->keys - table->isolated; + + for (;;) { + if ( xNext == yNext) { + size = cuddSwapInPlace(table,x,xNext); + if (size == 0) goto ddSwapAnyOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddSwapAnyOutOfMem; + move->x = x; + move->y = xNext; + move->size = size; + move->next = moves; + moves = move; + + size = cuddSwapInPlace(table,yNext,y); + if (size == 0) goto ddSwapAnyOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddSwapAnyOutOfMem; + move->x = yNext; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + + size = cuddSwapInPlace(table,x,xNext); + if (size == 0) goto ddSwapAnyOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddSwapAnyOutOfMem; + move->x = x; + move->y = xNext; + move->size = size; + move->next = moves; + moves = move; + + tmp = x; x = y; y = tmp; + + } else if (x == yNext) { + + size = cuddSwapInPlace(table,x,xNext); + if (size == 0) goto ddSwapAnyOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddSwapAnyOutOfMem; + move->x = x; + move->y = xNext; + move->size = size; + move->next = moves; + moves = move; + + tmp = x; x = y; y = tmp; + + } else { + size = cuddSwapInPlace(table,x,xNext); + if (size == 0) goto ddSwapAnyOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddSwapAnyOutOfMem; + move->x = x; + move->y = xNext; + move->size = size; + move->next = moves; + moves = move; + + size = cuddSwapInPlace(table,yNext,y); + if (size == 0) goto ddSwapAnyOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddSwapAnyOutOfMem; + move->x = yNext; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + + x = xNext; + y = yNext; + } + + xNext = cuddNextHigh(table,x); + yNext = cuddNextLow(table,y); + if (xNext > yRef) break; + + if ((double) size > table->maxGrowth * (double) limitSize) break; + if (size < limitSize) limitSize = size; + } + if (yNext>=xRef) { + size = cuddSwapInPlace(table,yNext,y); + if (size == 0) goto ddSwapAnyOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddSwapAnyOutOfMem; + move->x = yNext; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + } + + return(moves); + +ddSwapAnyOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *) moves); + moves = move; + } + return(NULL); + +} /* end of ddSwapAny */ + + +/**Function******************************************************************** + + Synopsis [Given xLow <= x <= xHigh moves x up and down between the + boundaries.] + + Description [Given xLow <= x <= xHigh moves x up and down between the + boundaries. Finds the best position and does the required changes. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddSiftingAux( + DdManager * table, + int x, + int xLow, + int xHigh) +{ + + Move *move; + Move *moveUp; /* list of up moves */ + Move *moveDown; /* list of down moves */ + int initialSize; + int result; + + initialSize = table->keys - table->isolated; + + moveDown = NULL; + moveUp = NULL; + + if (x == xLow) { + moveDown = ddSiftingDown(table,x,xHigh); + /* At this point x --> xHigh unless bounding occurred. */ + if (moveDown == (Move *) CUDD_OUT_OF_MEM) goto ddSiftingAuxOutOfMem; + /* Move backward and stop at best position. */ + result = ddSiftingBackward(table,initialSize,moveDown); + if (!result) goto ddSiftingAuxOutOfMem; + + } else if (x == xHigh) { + moveUp = ddSiftingUp(table,x,xLow); + /* At this point x --> xLow unless bounding occurred. */ + if (moveUp == (Move *) CUDD_OUT_OF_MEM) goto ddSiftingAuxOutOfMem; + /* Move backward and stop at best position. */ + result = ddSiftingBackward(table,initialSize,moveUp); + if (!result) goto ddSiftingAuxOutOfMem; + + } else if ((x - xLow) > (xHigh - x)) { /* must go down first: shorter */ + moveDown = ddSiftingDown(table,x,xHigh); + /* At this point x --> xHigh unless bounding occurred. */ + if (moveDown == (Move *) CUDD_OUT_OF_MEM) goto ddSiftingAuxOutOfMem; + if (moveDown != NULL) { + x = moveDown->y; + } + moveUp = ddSiftingUp(table,x,xLow); + if (moveUp == (Move *) CUDD_OUT_OF_MEM) goto ddSiftingAuxOutOfMem; + /* Move backward and stop at best position */ + result = ddSiftingBackward(table,initialSize,moveUp); + if (!result) goto ddSiftingAuxOutOfMem; + + } else { /* must go up first: shorter */ + moveUp = ddSiftingUp(table,x,xLow); + /* At this point x --> xLow unless bounding occurred. */ + if (moveUp == (Move *) CUDD_OUT_OF_MEM) goto ddSiftingAuxOutOfMem; + if (moveUp != NULL) { + x = moveUp->x; + } + moveDown = ddSiftingDown(table,x,xHigh); + if (moveDown == (Move *) CUDD_OUT_OF_MEM) goto ddSiftingAuxOutOfMem; + /* Move backward and stop at best position. */ + result = ddSiftingBackward(table,initialSize,moveDown); + if (!result) goto ddSiftingAuxOutOfMem; + } + + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocNode(table, (DdNode *) moveDown); + moveDown = move; + } + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocNode(table, (DdNode *) moveUp); + moveUp = move; + } + + return(1); + +ddSiftingAuxOutOfMem: + if (moveDown != (Move *) CUDD_OUT_OF_MEM) { + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocNode(table, (DdNode *) moveDown); + moveDown = move; + } + } + if (moveUp != (Move *) CUDD_OUT_OF_MEM) { + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocNode(table, (DdNode *) moveUp); + moveUp = move; + } + } + + return(0); + +} /* end of ddSiftingAux */ + + +/**Function******************************************************************** + + Synopsis [Sifts a variable up.] + + Description [Sifts a variable up. Moves y up until either it reaches + the bound (xLow) or the size of the DD heap increases too much. + Returns the set of moves in case of success; NULL if memory is full.] + + SideEffects [None] + +******************************************************************************/ +static Move * +ddSiftingUp( + DdManager * table, + int y, + int xLow) +{ + Move *moves; + Move *move; + int x; + int size; + int limitSize; + int xindex, yindex; + int isolated; + int L; /* lower bound on DD size */ +#ifdef DD_DEBUG + int checkL; + int z; + int zindex; +#endif + + moves = NULL; + yindex = table->invperm[y]; + + /* Initialize the lower bound. + ** The part of the DD below y will not change. + ** The part of the DD above y that does not interact with y will not + ** change. The rest may vanish in the best case, except for + ** the nodes at level xLow, which will not vanish, regardless. + */ + limitSize = L = table->keys - table->isolated; + for (x = xLow + 1; x < y; x++) { + xindex = table->invperm[x]; + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[xindex]->ref == 1; + L -= table->subtables[x].keys - isolated; + } + } + isolated = table->vars[yindex]->ref == 1; + L -= table->subtables[y].keys - isolated; + + x = cuddNextLow(table,y); + while (x >= xLow && L <= limitSize) { + xindex = table->invperm[x]; +#ifdef DD_DEBUG + checkL = table->keys - table->isolated; + for (z = xLow + 1; z < y; z++) { + zindex = table->invperm[z]; + if (cuddTestInteract(table,zindex,yindex)) { + isolated = table->vars[zindex]->ref == 1; + checkL -= table->subtables[z].keys - isolated; + } + } + isolated = table->vars[yindex]->ref == 1; + checkL -= table->subtables[y].keys - isolated; + assert(L == checkL); +#endif + size = cuddSwapInPlace(table,x,y); + if (size == 0) goto ddSiftingUpOutOfMem; + /* Update the lower bound. */ + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[xindex]->ref == 1; + L += table->subtables[y].keys - isolated; + } + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddSiftingUpOutOfMem; + move->x = x; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + if ((double) size > (double) limitSize * table->maxGrowth) break; + if (size < limitSize) limitSize = size; + y = x; + x = cuddNextLow(table,y); + } + return(moves); + +ddSiftingUpOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *) moves); + moves = move; + } + return((Move *) CUDD_OUT_OF_MEM); + +} /* end of ddSiftingUp */ + + +/**Function******************************************************************** + + Synopsis [Sifts a variable down.] + + Description [Sifts a variable down. Moves x down until either it + reaches the bound (xHigh) or the size of the DD heap increases too + much. Returns the set of moves in case of success; NULL if memory is + full.] + + SideEffects [None] + +******************************************************************************/ +static Move * +ddSiftingDown( + DdManager * table, + int x, + int xHigh) +{ + Move *moves; + Move *move; + int y; + int size; + int R; /* upper bound on node decrease */ + int limitSize; + int xindex, yindex; + int isolated; +#ifdef DD_DEBUG + int checkR; + int z; + int zindex; +#endif + + moves = NULL; + /* Initialize R */ + xindex = table->invperm[x]; + limitSize = size = table->keys - table->isolated; + R = 0; + for (y = xHigh; y > x; y--) { + yindex = table->invperm[y]; + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[yindex]->ref == 1; + R += table->subtables[y].keys - isolated; + } + } + + y = cuddNextHigh(table,x); + while (y <= xHigh && size - R < limitSize) { +#ifdef DD_DEBUG + checkR = 0; + for (z = xHigh; z > x; z--) { + zindex = table->invperm[z]; + if (cuddTestInteract(table,xindex,zindex)) { + isolated = table->vars[zindex]->ref == 1; + checkR += table->subtables[z].keys - isolated; + } + } + assert(R == checkR); +#endif + /* Update upper bound on node decrease. */ + yindex = table->invperm[y]; + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[yindex]->ref == 1; + R -= table->subtables[y].keys - isolated; + } + size = cuddSwapInPlace(table,x,y); + if (size == 0) goto ddSiftingDownOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddSiftingDownOutOfMem; + move->x = x; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + if ((double) size > (double) limitSize * table->maxGrowth) break; + if (size < limitSize) limitSize = size; + x = y; + y = cuddNextHigh(table,x); + } + return(moves); + +ddSiftingDownOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *) moves); + moves = move; + } + return((Move *) CUDD_OUT_OF_MEM); + +} /* end of ddSiftingDown */ + + +/**Function******************************************************************** + + Synopsis [Given a set of moves, returns the DD heap to the position + giving the minimum size.] + + Description [Given a set of moves, returns the DD heap to the + position giving the minimum size. In case of ties, returns to the + closest position giving the minimum size. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddSiftingBackward( + DdManager * table, + int size, + Move * moves) +{ + Move *move; + int res; + + for (move = moves; move != NULL; move = move->next) { + if (move->size < size) { + size = move->size; + } + } + + for (move = moves; move != NULL; move = move->next) { + if (move->size == size) return(1); + res = cuddSwapInPlace(table,(int)move->x,(int)move->y); + if (!res) return(0); + } + + return(1); + +} /* end of ddSiftingBackward */ + + +/**Function******************************************************************** + + Synopsis [Prepares the DD heap for dynamic reordering.] + + Description [Prepares the DD heap for dynamic reordering. Does + garbage collection, to guarantee that there are no dead nodes; + clears the cache, which is invalidated by dynamic reordering; initializes + the number of isolated projection functions; and initializes the + interaction matrix. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddReorderPreprocess( + DdManager * table) +{ + int i; + int res; + + /* Clear the cache. */ + cuddCacheFlush(table); + cuddLocalCacheClearAll(table); + + /* Eliminate dead nodes. Do not scan the cache again. */ + cuddGarbageCollect(table,0); + + /* Initialize number of isolated projection functions. */ + table->isolated = 0; + for (i = 0; i < table->size; i++) { + if (table->vars[i]->ref == 1) table->isolated++; + } + + /* Initialize the interaction matrix. */ + res = cuddInitInteract(table); + if (res == 0) return(0); + + return(1); + +} /* end of ddReorderPreprocess */ + + +/**Function******************************************************************** + + Synopsis [Cleans up at the end of reordering.] + + Description [] + + SideEffects [None] + +******************************************************************************/ +static int +ddReorderPostprocess( + DdManager * table) +{ + +#ifdef DD_VERBOSE + (void) fflush(table->out); +#endif + + /* Free interaction matrix. */ + FREE(table->interact); + + return(1); + +} /* end of ddReorderPostprocess */ + + +/**Function******************************************************************** + + Synopsis [Reorders variables according to a given permutation.] + + Description [Reorders variables according to a given permutation. + The i-th permutation array contains the index of the variable that + should be brought to the i-th level. ddShuffle assumes that no + dead nodes are present and that the interaction matrix is properly + initialized. The reordering is achieved by a series of upward sifts. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +ddShuffle( + DdManager * table, + int * permutation) +{ + int index; + int level; + int position; + int numvars; + int result; +#ifdef DD_STATS + long localTime; + int initialSize; + int finalSize; + int previousSize; +#endif + + ddTotalNumberSwapping = 0; +#ifdef DD_STATS + localTime = util_cpu_time(); + initialSize = table->keys - table->isolated; + (void) fprintf(table->out,"#:I_SHUFFLE %8d: initial size\n", + initialSize); + ddTotalNISwaps = 0; +#endif + + numvars = table->size; + + for (level = 0; level < numvars; level++) { + index = permutation[level]; + position = table->perm[index]; +#ifdef DD_STATS + previousSize = table->keys - table->isolated; +#endif + result = ddSiftUp(table,position,level); + if (!result) return(0); +#ifdef DD_STATS + if (table->keys < (unsigned) previousSize + table->isolated) { + (void) fprintf(table->out,"-"); + } else if (table->keys > (unsigned) previousSize + table->isolated) { + (void) fprintf(table->out,"+"); /* should never happen */ + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + +#ifdef DD_STATS + (void) fprintf(table->out,"\n"); + finalSize = table->keys - table->isolated; + (void) fprintf(table->out,"#:F_SHUFFLE %8d: final size\n",finalSize); + (void) fprintf(table->out,"#:T_SHUFFLE %8g: total time (sec)\n", + ((double)(util_cpu_time() - localTime)/1000.0)); + (void) fprintf(table->out,"#:N_SHUFFLE %8d: total swaps\n", + ddTotalNumberSwapping); + (void) fprintf(table->out,"#:M_SHUFFLE %8d: NI swaps\n",ddTotalNISwaps); +#endif + + return(1); + +} /* end of ddShuffle */ + + +/**Function******************************************************************** + + Synopsis [Moves one variable up.] + + Description [Takes a variable from position x and sifts it up to + position xLow; xLow should be less than or equal to x. + Returns 1 if successful; 0 otherwise] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +ddSiftUp( + DdManager * table, + int x, + int xLow) +{ + int y; + int size; + + y = cuddNextLow(table,x); + while (y >= xLow) { + size = cuddSwapInPlace(table,y,x); + if (size == 0) { + return(0); + } + x = y; + y = cuddNextLow(table,x); + } + return(1); + +} /* end of ddSiftUp */ + + +/**Function******************************************************************** + + Synopsis [Fixes the BDD variable group tree after a shuffle.] + + Description [Fixes the BDD variable group tree after a + shuffle. Assumes that the order of the variables in a terminal node + has not been changed.] + + SideEffects [Changes the BDD variable group tree.] + + SeeAlso [] + +******************************************************************************/ +static void +bddFixTree( + DdManager * table, + MtrNode * treenode) +{ + if (treenode == NULL) return; + treenode->low = ((int) treenode->index < table->size) ? + table->perm[treenode->index] : treenode->index; + if (treenode->child != NULL) { + bddFixTree(table, treenode->child); + } + if (treenode->younger != NULL) + bddFixTree(table, treenode->younger); + if (treenode->parent != NULL && treenode->low < treenode->parent->low) { + treenode->parent->low = treenode->low; + treenode->parent->index = treenode->index; + } + return; + +} /* end of bddFixTree */ + + +/**Function******************************************************************** + + Synopsis [Updates the BDD variable group tree before a shuffle.] + + Description [Updates the BDD variable group tree before a shuffle. + Returns 1 if successful; 0 otherwise.] + + SideEffects [Changes the BDD variable group tree.] + + SeeAlso [] + +******************************************************************************/ +static int +ddUpdateMtrTree( + DdManager * table, + MtrNode * treenode, + int * perm, + int * invperm) +{ + int i, size, index, level; + int minLevel, maxLevel, minIndex; + + if (treenode == NULL) return(1); + + /* i : level */ + for (i = treenode->low; i < treenode->low + treenode->size; i++) { + index = table->invperm[i]; + level = perm[index]; + if (level < minLevel) { + minLevel = level; + minIndex = index; + } + if (level > maxLevel) + maxLevel = level; + } + size = maxLevel - minLevel + 1; + if (size == treenode->size) { + treenode->low = minLevel; + treenode->index = minIndex; + } else + return(0); + + if (treenode->child != NULL) { + if (!ddUpdateMtrTree(table, treenode->child, perm, invperm)) + return(0); + } + if (treenode->younger != NULL) { + if (!ddUpdateMtrTree(table, treenode->younger, perm, invperm)) + return(0); + } + return(1); +} + + +/**Function******************************************************************** + + Synopsis [Checks the BDD variable group tree before a shuffle.] + + Description [Checks the BDD variable group tree before a shuffle. + Returns 1 if successful; 0 otherwise.] + + SideEffects [Changes the BDD variable group tree.] + + SeeAlso [] + +******************************************************************************/ +static int +ddCheckPermuation( + DdManager * table, + MtrNode * treenode, + int * perm, + int * invperm) +{ + int i, size, index, level; + int minLevel, maxLevel; + + if (treenode == NULL) return(1); + + minLevel = table->size; + maxLevel = 0; + /* i : level */ + for (i = treenode->low; i < treenode->low + treenode->size; i++) { + index = table->invperm[i]; + level = perm[index]; + if (level < minLevel) + minLevel = level; + if (level > maxLevel) + maxLevel = level; + } + size = maxLevel - minLevel + 1; + if (size != treenode->size) + return(0); + + if (treenode->child != NULL) { + if (!ddCheckPermuation(table, treenode->child, perm, invperm)) + return(0); + } + if (treenode->younger != NULL) { + if (!ddCheckPermuation(table, treenode->younger, perm, invperm)) + return(0); + } + return(1); +} diff --git a/abc_with_bb_support/src/bdd/cudd/cuddSat.c b/abc_with_bb_support/src/bdd/cudd/cuddSat.c new file mode 100644 index 000000000..56a3b5cd2 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddSat.c @@ -0,0 +1,1305 @@ +/**CFile*********************************************************************** + + FileName [cuddSat.c] + + PackageName [cudd] + + Synopsis [Functions for the solution of satisfiability related + problems.] + + Description [External procedures included in this file: +
        +
      • Cudd_Eval() +
      • Cudd_ShortestPath() +
      • Cudd_LargestCube() +
      • Cudd_ShortestLength() +
      • Cudd_Decreasing() +
      • Cudd_Increasing() +
      • Cudd_EquivDC() +
      • Cudd_bddLeqUnless() +
      • Cudd_EqualSupNorm() +
      • Cudd_bddMakePrime() +
      + Internal procedures included in this module: +
        +
      • cuddBddMakePrime() +
      + Static procedures included in this module: +
        +
      • freePathPair() +
      • getShortest() +
      • getPath() +
      • getLargest() +
      • getCube() +
      ] + + Author [Seh-Woong Jeong, Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define DD_BIGGY 1000000 + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +typedef struct cuddPathPair { + int pos; + int neg; +} cuddPathPair; + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddSat.c,v 1.1.1.1 2003/02/24 22:23:53 wjiang Exp $"; +#endif + +static DdNode *one, *zero; + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +#define WEIGHT(weight, col) ((weight) == NULL ? 1 : weight[col]) + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static enum st_retval freePathPair ARGS((char *key, char *value, char *arg)); +static cuddPathPair getShortest ARGS((DdNode *root, int *cost, int *support, st_table *visited)); +static DdNode * getPath ARGS((DdManager *manager, st_table *visited, DdNode *f, int *weight, int cost)); +static cuddPathPair getLargest ARGS((DdNode *root, st_table *visited)); +static DdNode * getCube ARGS((DdManager *manager, st_table *visited, DdNode *f, int cost)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Returns the value of a DD for a given variable assignment.] + + Description [Finds the value of a DD for a given variable + assignment. The variable assignment is passed in an array of int's, + that should specify a zero or a one for each variable in the support + of the function. Returns a pointer to a constant node. No new nodes + are produced.] + + SideEffects [None] + + SeeAlso [Cudd_bddLeq Cudd_addEvalConst] + +******************************************************************************/ +DdNode * +Cudd_Eval( + DdManager * dd, + DdNode * f, + int * inputs) +{ + int comple; + DdNode *ptr; + + comple = Cudd_IsComplement(f); + ptr = Cudd_Regular(f); + + while (!cuddIsConstant(ptr)) { + if (inputs[ptr->index] == 1) { + ptr = cuddT(ptr); + } else { + comple ^= Cudd_IsComplement(cuddE(ptr)); + ptr = Cudd_Regular(cuddE(ptr)); + } + } + return(Cudd_NotCond(ptr,comple)); + +} /* end of Cudd_Eval */ + + +/**Function******************************************************************** + + Synopsis [Finds a shortest path in a DD.] + + Description [Finds a shortest path in a DD. f is the DD we want to + get the shortest path for; weight\[i\] is the weight of the THEN arc + coming from the node whose index is i. If weight is NULL, then unit + weights are assumed for all THEN arcs. All ELSE arcs have 0 weight. + If non-NULL, both weight and support should point to arrays with at + least as many entries as there are variables in the manager. + Returns the shortest path as the BDD of a cube.] + + SideEffects [support contains on return the true support of f. + If support is NULL on entry, then Cudd_ShortestPath does not compute + the true support info. length contains the length of the path.] + + SeeAlso [Cudd_ShortestLength Cudd_LargestCube] + +******************************************************************************/ +DdNode * +Cudd_ShortestPath( + DdManager * manager, + DdNode * f, + int * weight, + int * support, + int * length) +{ + register DdNode *F; + st_table *visited; + DdNode *sol; + cuddPathPair *rootPair; + int complement, cost; + int i; + + one = DD_ONE(manager); + zero = DD_ZERO(manager); + + /* Initialize support. */ + if (support) { + for (i = 0; i < manager->size; i++) { + support[i] = 0; + } + } + + if (f == Cudd_Not(one) || f == zero) { + *length = DD_BIGGY; + return(Cudd_Not(one)); + } + /* From this point on, a path exists. */ + + /* Initialize visited table. */ + visited = st_init_table(st_ptrcmp, st_ptrhash); + + /* Now get the length of the shortest path(s) from f to 1. */ + (void) getShortest(f, weight, support, visited); + + complement = Cudd_IsComplement(f); + + F = Cudd_Regular(f); + + st_lookup(visited, (char *)F, (char **)&rootPair); + + if (complement) { + cost = rootPair->neg; + } else { + cost = rootPair->pos; + } + + /* Recover an actual shortest path. */ + do { + manager->reordered = 0; + sol = getPath(manager,visited,f,weight,cost); + } while (manager->reordered == 1); + + st_foreach(visited, freePathPair, NULL); + st_free_table(visited); + + *length = cost; + return(sol); + +} /* end of Cudd_ShortestPath */ + + +/**Function******************************************************************** + + Synopsis [Finds a largest cube in a DD.] + + Description [Finds a largest cube in a DD. f is the DD we want to + get the largest cube for. The problem is translated into the one of + finding a shortest path in f, when both THEN and ELSE arcs are assumed to + have unit length. This yields a largest cube in the disjoint cover + corresponding to the DD. Therefore, it is not necessarily the largest + implicant of f. Returns the largest cube as a BDD.] + + SideEffects [The number of literals of the cube is returned in length.] + + SeeAlso [Cudd_ShortestPath] + +******************************************************************************/ +DdNode * +Cudd_LargestCube( + DdManager * manager, + DdNode * f, + int * length) +{ + register DdNode *F; + st_table *visited; + DdNode *sol; + cuddPathPair *rootPair; + int complement, cost; + + one = DD_ONE(manager); + zero = DD_ZERO(manager); + + if (f == Cudd_Not(one) || f == zero) { + *length = DD_BIGGY; + return(Cudd_Not(one)); + } + /* From this point on, a path exists. */ + + /* Initialize visited table. */ + visited = st_init_table(st_ptrcmp, st_ptrhash); + + /* Now get the length of the shortest path(s) from f to 1. */ + (void) getLargest(f, visited); + + complement = Cudd_IsComplement(f); + + F = Cudd_Regular(f); + + st_lookup(visited, (char *)F, (char **)&rootPair); + + if (complement) { + cost = rootPair->neg; + } else { + cost = rootPair->pos; + } + + /* Recover an actual shortest path. */ + do { + manager->reordered = 0; + sol = getCube(manager,visited,f,cost); + } while (manager->reordered == 1); + + st_foreach(visited, freePathPair, NULL); + st_free_table(visited); + + *length = cost; + return(sol); + +} /* end of Cudd_LargestCube */ + + +/**Function******************************************************************** + + Synopsis [Find the length of the shortest path(s) in a DD.] + + Description [Find the length of the shortest path(s) in a DD. f is + the DD we want to get the shortest path for; weight\[i\] is the + weight of the THEN edge coming from the node whose index is i. All + ELSE edges have 0 weight. Returns the length of the shortest + path(s) if successful; CUDD_OUT_OF_MEM otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_ShortestPath] + +******************************************************************************/ +int +Cudd_ShortestLength( + DdManager * manager, + DdNode * f, + int * weight) +{ + register DdNode *F; + st_table *visited; + cuddPathPair *my_pair; + int complement, cost; + + one = DD_ONE(manager); + zero = DD_ZERO(manager); + + if (f == Cudd_Not(one) || f == zero) { + return(DD_BIGGY); + } + + /* From this point on, a path exists. */ + /* Initialize visited table and support. */ + visited = st_init_table(st_ptrcmp, st_ptrhash); + + /* Now get the length of the shortest path(s) from f to 1. */ + (void) getShortest(f, weight, NULL, visited); + + complement = Cudd_IsComplement(f); + + F = Cudd_Regular(f); + + st_lookup(visited, (char *)F, (char **)&my_pair); + + if (complement) { + cost = my_pair->neg; + } else { + cost = my_pair->pos; + } + + st_foreach(visited, freePathPair, NULL); + st_free_table(visited); + + return(cost); + +} /* end of Cudd_ShortestLength */ + + +/**Function******************************************************************** + + Synopsis [Determines whether a BDD is negative unate in a + variable.] + + Description [Determines whether the function represented by BDD f is + negative unate (monotonic decreasing) in variable i. Returns the + constant one is f is unate and the (logical) constant zero if it is not. + This function does not generate any new nodes.] + + SideEffects [None] + + SeeAlso [Cudd_Increasing] + +******************************************************************************/ +DdNode * +Cudd_Decreasing( + DdManager * dd, + DdNode * f, + int i) +{ + unsigned int topf, level; + DdNode *F, *fv, *fvn, *res; + DdNode *(*cacheOp)(DdManager *, DdNode *, DdNode *); + + statLine(dd); +#ifdef DD_DEBUG + assert(0 <= i && i < dd->size); +#endif + + F = Cudd_Regular(f); + topf = cuddI(dd,F->index); + + /* Check terminal case. If topf > i, f does not depend on var. + ** Therefore, f is unate in i. + */ + level = (unsigned) dd->perm[i]; + if (topf > level) { + return(DD_ONE(dd)); + } + + /* From now on, f is not constant. */ + + /* Check cache. */ + cacheOp = (DdNode *(*)(DdManager *, DdNode *, DdNode *)) Cudd_Decreasing; + res = cuddCacheLookup2(dd,cacheOp,f,dd->vars[i]); + if (res != NULL) { + return(res); + } + + /* Compute cofactors. */ + fv = cuddT(F); fvn = cuddE(F); + if (F != f) { + fv = Cudd_Not(fv); + fvn = Cudd_Not(fvn); + } + + if (topf == (unsigned) level) { + /* Special case: if fv is regular, fv(1,...,1) = 1; + ** If in addition fvn is complemented, fvn(1,...,1) = 0. + ** But then f(1,1,...,1) > f(0,1,...,1). Hence f is not + ** monotonic decreasing in i. + */ + if (!Cudd_IsComplement(fv) && Cudd_IsComplement(fvn)) { + return(Cudd_Not(DD_ONE(dd))); + } + res = Cudd_bddLeq(dd,fv,fvn) ? DD_ONE(dd) : Cudd_Not(DD_ONE(dd)); + } else { + res = Cudd_Decreasing(dd,fv,i); + if (res == DD_ONE(dd)) { + res = Cudd_Decreasing(dd,fvn,i); + } + } + + cuddCacheInsert2(dd,cacheOp,f,dd->vars[i],res); + return(res); + +} /* end of Cudd_Decreasing */ + + +/**Function******************************************************************** + + Synopsis [Determines whether a BDD is positive unate in a + variable.] + + Description [Determines whether the function represented by BDD f is + positive unate (monotonic decreasing) in variable i. It is based on + Cudd_Decreasing and the fact that f is monotonic increasing in i if + and only if its complement is monotonic decreasing in i.] + + SideEffects [None] + + SeeAlso [Cudd_Decreasing] + +******************************************************************************/ +DdNode * +Cudd_Increasing( + DdManager * dd, + DdNode * f, + int i) +{ + return(Cudd_Decreasing(dd,Cudd_Not(f),i)); + +} /* end of Cudd_Increasing */ + + +/**Function******************************************************************** + + Synopsis [Tells whether F and G are identical wherever D is 0.] + + Description [Tells whether F and G are identical wherever D is 0. F + and G are either two ADDs or two BDDs. D is either a 0-1 ADD or a + BDD. The function returns 1 if F and G are equivalent, and 0 + otherwise. No new nodes are created.] + + SideEffects [None] + + SeeAlso [Cudd_bddLeqUnless] + +******************************************************************************/ +int +Cudd_EquivDC( + DdManager * dd, + DdNode * F, + DdNode * G, + DdNode * D) +{ + DdNode *tmp, *One, *Gr, *Dr; + DdNode *Fv, *Fvn, *Gv, *Gvn, *Dv, *Dvn; + int res; + unsigned int flevel, glevel, dlevel, top; + + One = DD_ONE(dd); + + statLine(dd); + /* Check terminal cases. */ + if (D == One || F == G) return(1); + if (D == Cudd_Not(One) || D == DD_ZERO(dd) || F == Cudd_Not(G)) return(0); + + /* From now on, D is non-constant. */ + + /* Normalize call to increase cache efficiency. */ + if (F > G) { + tmp = F; + F = G; + G = tmp; + } + if (Cudd_IsComplement(F)) { + F = Cudd_Not(F); + G = Cudd_Not(G); + } + + /* From now on, F is regular. */ + + /* Check cache. */ + tmp = cuddCacheLookup(dd,DD_EQUIV_DC_TAG,F,G,D); + if (tmp != NULL) return(tmp == One); + + /* Find splitting variable. */ + flevel = cuddI(dd,F->index); + Gr = Cudd_Regular(G); + glevel = cuddI(dd,Gr->index); + top = ddMin(flevel,glevel); + Dr = Cudd_Regular(D); + dlevel = dd->perm[Dr->index]; + top = ddMin(top,dlevel); + + /* Compute cofactors. */ + if (top == flevel) { + Fv = cuddT(F); + Fvn = cuddE(F); + } else { + Fv = Fvn = F; + } + if (top == glevel) { + Gv = cuddT(Gr); + Gvn = cuddE(Gr); + if (G != Gr) { + Gv = Cudd_Not(Gv); + Gvn = Cudd_Not(Gvn); + } + } else { + Gv = Gvn = G; + } + if (top == dlevel) { + Dv = cuddT(Dr); + Dvn = cuddE(Dr); + if (D != Dr) { + Dv = Cudd_Not(Dv); + Dvn = Cudd_Not(Dvn); + } + } else { + Dv = Dvn = D; + } + + /* Solve recursively. */ + res = Cudd_EquivDC(dd,Fv,Gv,Dv); + if (res != 0) { + res = Cudd_EquivDC(dd,Fvn,Gvn,Dvn); + } + cuddCacheInsert(dd,DD_EQUIV_DC_TAG,F,G,D,(res) ? One : Cudd_Not(One)); + + return(res); + +} /* end of Cudd_EquivDC */ + + +/**Function******************************************************************** + + Synopsis [Tells whether f is less than of equal to G unless D is 1.] + + Description [Tells whether f is less than of equal to G unless D is + 1. f, g, and D are BDDs. The function returns 1 if f is less than + of equal to G, and 0 otherwise. No new nodes are created.] + + SideEffects [None] + + SeeAlso [Cudd_EquivDC Cudd_bddLeq Cudd_bddIteConstant] + +******************************************************************************/ +int +Cudd_bddLeqUnless( + DdManager *dd, + DdNode *f, + DdNode *g, + DdNode *D) +{ + DdNode *tmp, *One, *F, *G; + DdNode *Ft, *Fe, *Gt, *Ge, *Dt, *De; + int res; + unsigned int flevel, glevel, dlevel, top; + + statLine(dd); + + One = DD_ONE(dd); + + /* Check terminal cases. */ + if (f == g || g == One || f == Cudd_Not(One) || D == One || + D == f || D == Cudd_Not(g)) return(1); + /* Check for two-operand cases. */ + if (D == Cudd_Not(One) || D == g || D == Cudd_Not(f)) + return(Cudd_bddLeq(dd,f,g)); + if (g == Cudd_Not(One) || g == Cudd_Not(f)) return(Cudd_bddLeq(dd,f,D)); + if (f == One) return(Cudd_bddLeq(dd,Cudd_Not(g),D)); + + /* From now on, f, g, and D are non-constant, distinct, and + ** non-complementary. */ + + /* Normalize call to increase cache efficiency. We rely on the + ** fact that f <= g unless D is equivalent to not(g) <= not(f) + ** unless D and to f <= D unless g. We make sure that D is + ** regular, and that at most one of f and g is complemented. We also + ** ensure that when two operands can be swapped, the one with the + ** lowest address comes first. */ + + if (Cudd_IsComplement(D)) { + if (Cudd_IsComplement(g)) { + /* Special case: if f is regular and g is complemented, + ** f(1,...,1) = 1 > 0 = g(1,...,1). If D(1,...,1) = 0, return 0. + */ + if (!Cudd_IsComplement(f)) return(0); + /* !g <= D unless !f or !D <= g unless !f */ + tmp = D; + D = Cudd_Not(f); + if (g < tmp) { + f = Cudd_Not(g); + g = tmp; + } else { + f = Cudd_Not(tmp); + } + } else { + if (Cudd_IsComplement(f)) { + /* !D <= !f unless g or !D <= g unless !f */ + tmp = f; + f = Cudd_Not(D); + if (tmp < g) { + D = g; + g = Cudd_Not(tmp); + } else { + D = Cudd_Not(tmp); + } + } else { + /* f <= D unless g or !D <= !f unless g */ + tmp = D; + D = g; + if (tmp < f) { + g = Cudd_Not(f); + f = Cudd_Not(tmp); + } else { + g = tmp; + } + } + } + } else { + if (Cudd_IsComplement(g)) { + if (Cudd_IsComplement(f)) { + /* !g <= !f unless D or !g <= D unless !f */ + tmp = f; + f = Cudd_Not(g); + if (D < tmp) { + g = D; + D = Cudd_Not(tmp); + } else { + g = Cudd_Not(tmp); + } + } else { + /* f <= g unless D or !g <= !f unless D */ + if (g < f) { + tmp = g; + g = Cudd_Not(f); + f = Cudd_Not(tmp); + } + } + } else { + /* f <= g unless D or f <= D unless g */ + if (D < g) { + tmp = D; + D = g; + g = tmp; + } + } + } + + /* From now on, D is regular. */ + + /* Check cache. */ + tmp = cuddCacheLookup(dd,DD_BDD_LEQ_UNLESS_TAG,f,g,D); + if (tmp != NULL) return(tmp == One); + + /* Find splitting variable. */ + F = Cudd_Regular(f); + flevel = dd->perm[F->index]; + G = Cudd_Regular(g); + glevel = dd->perm[G->index]; + top = ddMin(flevel,glevel); + dlevel = dd->perm[D->index]; + top = ddMin(top,dlevel); + + /* Compute cofactors. */ + if (top == flevel) { + Ft = cuddT(F); + Fe = cuddE(F); + if (F != f) { + Ft = Cudd_Not(Ft); + Fe = Cudd_Not(Fe); + } + } else { + Ft = Fe = f; + } + if (top == glevel) { + Gt = cuddT(G); + Ge = cuddE(G); + if (G != g) { + Gt = Cudd_Not(Gt); + Ge = Cudd_Not(Ge); + } + } else { + Gt = Ge = g; + } + if (top == dlevel) { + Dt = cuddT(D); + De = cuddE(D); + } else { + Dt = De = D; + } + + /* Solve recursively. */ + res = Cudd_bddLeqUnless(dd,Ft,Gt,Dt); + if (res != 0) { + res = Cudd_bddLeqUnless(dd,Fe,Ge,De); + } + cuddCacheInsert(dd,DD_BDD_LEQ_UNLESS_TAG,f,g,D,Cudd_NotCond(One,!res)); + + return(res); + +} /* end of Cudd_bddLeqUnless */ + + +/**Function******************************************************************** + + Synopsis [Compares two ADDs for equality within tolerance.] + + Description [Compares two ADDs for equality within tolerance. Two + ADDs are reported to be equal if the maximum difference between them + (the sup norm of their difference) is less than or equal to the + tolerance parameter. Returns 1 if the two ADDs are equal (within + tolerance); 0 otherwise. If parameter pr is positive + the first failure is reported to the standard output.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +Cudd_EqualSupNorm( + DdManager * dd /* manager */, + DdNode * f /* first ADD */, + DdNode * g /* second ADD */, + CUDD_VALUE_TYPE tolerance /* maximum allowed difference */, + int pr /* verbosity level */) +{ + DdNode *fv, *fvn, *gv, *gvn, *r; + unsigned int topf, topg; + + statLine(dd); + /* Check terminal cases. */ + if (f == g) return(1); + if (Cudd_IsConstant(f) && Cudd_IsConstant(g)) { + if (ddEqualVal(cuddV(f),cuddV(g),tolerance)) { + return(1); + } else { + if (pr>0) { + (void) fprintf(dd->out,"Offending nodes:\n"); +#if SIZEOF_VOID_P == 8 + (void) fprintf(dd->out, + "f: address = %lx\t value = %40.30f\n", + (unsigned long) f, cuddV(f)); + (void) fprintf(dd->out, + "g: address = %lx\t value = %40.30f\n", + (unsigned long) g, cuddV(g)); +#else + (void) fprintf(dd->out, + "f: address = %x\t value = %40.30f\n", + (unsigned) f, cuddV(f)); + (void) fprintf(dd->out, + "g: address = %x\t value = %40.30f\n", + (unsigned) g, cuddV(g)); +#endif + } + return(0); + } + } + + /* We only insert the result in the cache if the comparison is + ** successful. Therefore, if we hit we return 1. */ + r = cuddCacheLookup2(dd,(DdNode * (*)(DdManager *, DdNode *, DdNode *))Cudd_EqualSupNorm,f,g); + if (r != NULL) { + return(1); + } + + /* Compute the cofactors and solve the recursive subproblems. */ + topf = cuddI(dd,f->index); + topg = cuddI(dd,g->index); + + if (topf <= topg) {fv = cuddT(f); fvn = cuddE(f);} else {fv = fvn = f;} + if (topg <= topf) {gv = cuddT(g); gvn = cuddE(g);} else {gv = gvn = g;} + + if (!Cudd_EqualSupNorm(dd,fv,gv,tolerance,pr)) return(0); + if (!Cudd_EqualSupNorm(dd,fvn,gvn,tolerance,pr)) return(0); + + cuddCacheInsert2(dd,(DdNode * (*)(DdManager *, DdNode *, DdNode *))Cudd_EqualSupNorm,f,g,DD_ONE(dd)); + + return(1); + +} /* end of Cudd_EqualSupNorm */ + + +/**Function******************************************************************** + + Synopsis [Expands cube to a prime implicant of f.] + + Description [Expands cube to a prime implicant of f. Returns the prime + if successful; NULL otherwise. In particular, NULL is returned if cube + is not a real cube or is not an implicant of f.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +Cudd_bddMakePrime( + DdManager *dd /* manager */, + DdNode *cube /* cube to be expanded */, + DdNode *f /* function of which the cube is to be made a prime */) +{ + DdNode *res; + + if (!Cudd_bddLeq(dd,cube,f)) return(NULL); + + do { + dd->reordered = 0; + res = cuddBddMakePrime(dd,cube,f); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_bddMakePrime */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_bddMakePrime.] + + Description [Performs the recursive step of Cudd_bddMakePrime. + Returns the prime if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddBddMakePrime( + DdManager *dd /* manager */, + DdNode *cube /* cube to be expanded */, + DdNode *f /* function of which the cube is to be made a prime */) +{ + DdNode *scan; + DdNode *t, *e; + DdNode *res = cube; + DdNode *zero = Cudd_Not(DD_ONE(dd)); + + Cudd_Ref(res); + scan = cube; + while (!Cudd_IsConstant(scan)) { + DdNode *reg = Cudd_Regular(scan); + DdNode *var = dd->vars[reg->index]; + DdNode *expanded = Cudd_bddExistAbstract(dd,res,var); + if (expanded == NULL) { + return(NULL); + } + Cudd_Ref(expanded); + if (Cudd_bddLeq(dd,expanded,f)) { + Cudd_RecursiveDeref(dd,res); + res = expanded; + } else { + Cudd_RecursiveDeref(dd,expanded); + } + cuddGetBranches(scan,&t,&e); + if (t == zero) { + scan = e; + } else if (e == zero) { + scan = t; + } else { + Cudd_RecursiveDeref(dd,res); + return(NULL); /* cube is not a cube */ + } + } + + if (scan == DD_ONE(dd)) { + Cudd_Deref(res); + return(res); + } else { + Cudd_RecursiveDeref(dd,res); + return(NULL); + } + +} /* end of cuddBddMakePrime */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Frees the entries of the visited symbol table.] + + Description [Frees the entries of the visited symbol table. Returns + ST_CONTINUE.] + + SideEffects [None] + +******************************************************************************/ +static enum st_retval +freePathPair( + char * key, + char * value, + char * arg) +{ + cuddPathPair *pair; + + pair = (cuddPathPair *) value; + FREE(pair); + return(ST_CONTINUE); + +} /* end of freePathPair */ + + +/**Function******************************************************************** + + Synopsis [Finds the length of the shortest path(s) in a DD.] + + Description [Finds the length of the shortest path(s) in a DD. + Uses a local symbol table to store the lengths for each + node. Only the lengths for the regular nodes are entered in the table, + because those for the complement nodes are simply obtained by swapping + the two lenghts. + Returns a pair of lengths: the length of the shortest path to 1; + and the length of the shortest path to 0. This is done so as to take + complement arcs into account.] + + SideEffects [Accumulates the support of the DD in support.] + + SeeAlso [] + +******************************************************************************/ +static cuddPathPair +getShortest( + DdNode * root, + int * cost, + int * support, + st_table * visited) +{ + cuddPathPair *my_pair, res_pair, pair_T, pair_E; + DdNode *my_root, *T, *E; + int weight; + + my_root = Cudd_Regular(root); + + if (st_lookup(visited, (char *)my_root, (char **)&my_pair)) { + if (Cudd_IsComplement(root)) { + res_pair.pos = my_pair->neg; + res_pair.neg = my_pair->pos; + } else { + res_pair.pos = my_pair->pos; + res_pair.neg = my_pair->neg; + } + return(res_pair); + } + + /* In the case of a BDD the following test is equivalent to + ** testing whether the BDD is the constant 1. This formulation, + ** however, works for ADDs as well, by assuming the usual + ** dichotomy of 0 and != 0. + */ + if (cuddIsConstant(my_root)) { + if (my_root != zero) { + res_pair.pos = 0; + res_pair.neg = DD_BIGGY; + } else { + res_pair.pos = DD_BIGGY; + res_pair.neg = 0; + } + } else { + T = cuddT(my_root); + E = cuddE(my_root); + + pair_T = getShortest(T, cost, support, visited); + pair_E = getShortest(E, cost, support, visited); + weight = WEIGHT(cost, my_root->index); + res_pair.pos = ddMin(pair_T.pos+weight, pair_E.pos); + res_pair.neg = ddMin(pair_T.neg+weight, pair_E.neg); + + /* Update support. */ + if (support != NULL) { + support[my_root->index] = 1; + } + } + + my_pair = ALLOC(cuddPathPair, 1); + if (my_pair == NULL) { + if (Cudd_IsComplement(root)) { + int tmp = res_pair.pos; + res_pair.pos = res_pair.neg; + res_pair.neg = tmp; + } + return(res_pair); + } + my_pair->pos = res_pair.pos; + my_pair->neg = res_pair.neg; + + st_insert(visited, (char *)my_root, (char *)my_pair); + if (Cudd_IsComplement(root)) { + res_pair.pos = my_pair->neg; + res_pair.neg = my_pair->pos; + } else { + res_pair.pos = my_pair->pos; + res_pair.neg = my_pair->neg; + } + return(res_pair); + +} /* end of getShortest */ + + +/**Function******************************************************************** + + Synopsis [Build a BDD for a shortest path of f.] + + Description [Build a BDD for a shortest path of f. + Given the minimum length from the root, and the minimum + lengths for each node (in visited), apply triangulation at each node. + Of the two children of each node on a shortest path, at least one is + on a shortest path. In case of ties the procedure chooses the THEN + children. + Returns a pointer to the cube BDD representing the path if + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static DdNode * +getPath( + DdManager * manager, + st_table * visited, + DdNode * f, + int * weight, + int cost) +{ + DdNode *sol, *tmp; + DdNode *my_dd, *T, *E; + cuddPathPair *T_pair, *E_pair; + int Tcost, Ecost; + int complement; + + my_dd = Cudd_Regular(f); + complement = Cudd_IsComplement(f); + + sol = one; + cuddRef(sol); + + while (!cuddIsConstant(my_dd)) { + Tcost = cost - WEIGHT(weight, my_dd->index); + Ecost = cost; + + T = cuddT(my_dd); + E = cuddE(my_dd); + + if (complement) {T = Cudd_Not(T); E = Cudd_Not(E);} + + st_lookup(visited, (char *)Cudd_Regular(T), (char **)&T_pair); + if ((Cudd_IsComplement(T) && T_pair->neg == Tcost) || + (!Cudd_IsComplement(T) && T_pair->pos == Tcost)) { + tmp = cuddBddAndRecur(manager,manager->vars[my_dd->index],sol); + if (tmp == NULL) { + Cudd_RecursiveDeref(manager,sol); + return(NULL); + } + cuddRef(tmp); + Cudd_RecursiveDeref(manager,sol); + sol = tmp; + + complement = Cudd_IsComplement(T); + my_dd = Cudd_Regular(T); + cost = Tcost; + continue; + } + st_lookup(visited, (char *)Cudd_Regular(E), (char **)&E_pair); + if ((Cudd_IsComplement(E) && E_pair->neg == Ecost) || + (!Cudd_IsComplement(E) && E_pair->pos == Ecost)) { + tmp = cuddBddAndRecur(manager,Cudd_Not(manager->vars[my_dd->index]),sol); + if (tmp == NULL) { + Cudd_RecursiveDeref(manager,sol); + return(NULL); + } + cuddRef(tmp); + Cudd_RecursiveDeref(manager,sol); + sol = tmp; + complement = Cudd_IsComplement(E); + my_dd = Cudd_Regular(E); + cost = Ecost; + continue; + } + (void) fprintf(manager->err,"We shouldn't be here!!\n"); + manager->errorCode = CUDD_INTERNAL_ERROR; + return(NULL); + } + + cuddDeref(sol); + return(sol); + +} /* end of getPath */ + + +/**Function******************************************************************** + + Synopsis [Finds the size of the largest cube(s) in a DD.] + + Description [Finds the size of the largest cube(s) in a DD. + This problem is translated into finding the shortest paths from a node + when both THEN and ELSE arcs have unit lengths. + Uses a local symbol table to store the lengths for each + node. Only the lengths for the regular nodes are entered in the table, + because those for the complement nodes are simply obtained by swapping + the two lenghts. + Returns a pair of lengths: the length of the shortest path to 1; + and the length of the shortest path to 0. This is done so as to take + complement arcs into account.] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +static cuddPathPair +getLargest( + DdNode * root, + st_table * visited) +{ + cuddPathPair *my_pair, res_pair, pair_T, pair_E; + DdNode *my_root, *T, *E; + + my_root = Cudd_Regular(root); + + if (st_lookup(visited, (char *)my_root, (char **)&my_pair)) { + if (Cudd_IsComplement(root)) { + res_pair.pos = my_pair->neg; + res_pair.neg = my_pair->pos; + } else { + res_pair.pos = my_pair->pos; + res_pair.neg = my_pair->neg; + } + return(res_pair); + } + + /* In the case of a BDD the following test is equivalent to + ** testing whether the BDD is the constant 1. This formulation, + ** however, works for ADDs as well, by assuming the usual + ** dichotomy of 0 and != 0. + */ + if (cuddIsConstant(my_root)) { + if (my_root != zero) { + res_pair.pos = 0; + res_pair.neg = DD_BIGGY; + } else { + res_pair.pos = DD_BIGGY; + res_pair.neg = 0; + } + } else { + T = cuddT(my_root); + E = cuddE(my_root); + + pair_T = getLargest(T, visited); + pair_E = getLargest(E, visited); + res_pair.pos = ddMin(pair_T.pos, pair_E.pos) + 1; + res_pair.neg = ddMin(pair_T.neg, pair_E.neg) + 1; + } + + my_pair = ALLOC(cuddPathPair, 1); + if (my_pair == NULL) { /* simlpy do not cache this result */ + if (Cudd_IsComplement(root)) { + int tmp = res_pair.pos; + res_pair.pos = res_pair.neg; + res_pair.neg = tmp; + } + return(res_pair); + } + my_pair->pos = res_pair.pos; + my_pair->neg = res_pair.neg; + + st_insert(visited, (char *)my_root, (char *)my_pair); + if (Cudd_IsComplement(root)) { + res_pair.pos = my_pair->neg; + res_pair.neg = my_pair->pos; + } else { + res_pair.pos = my_pair->pos; + res_pair.neg = my_pair->neg; + } + return(res_pair); + +} /* end of getLargest */ + + +/**Function******************************************************************** + + Synopsis [Build a BDD for a largest cube of f.] + + Description [Build a BDD for a largest cube of f. + Given the minimum length from the root, and the minimum + lengths for each node (in visited), apply triangulation at each node. + Of the two children of each node on a shortest path, at least one is + on a shortest path. In case of ties the procedure chooses the THEN + children. + Returns a pointer to the cube BDD representing the path if + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static DdNode * +getCube( + DdManager * manager, + st_table * visited, + DdNode * f, + int cost) +{ + DdNode *sol, *tmp; + DdNode *my_dd, *T, *E; + cuddPathPair *T_pair, *E_pair; + int Tcost, Ecost; + int complement; + + my_dd = Cudd_Regular(f); + complement = Cudd_IsComplement(f); + + sol = one; + cuddRef(sol); + + while (!cuddIsConstant(my_dd)) { + Tcost = cost - 1; + Ecost = cost - 1; + + T = cuddT(my_dd); + E = cuddE(my_dd); + + if (complement) {T = Cudd_Not(T); E = Cudd_Not(E);} + + st_lookup(visited, (char *)Cudd_Regular(T), (char **)&T_pair); + if ((Cudd_IsComplement(T) && T_pair->neg == Tcost) || + (!Cudd_IsComplement(T) && T_pair->pos == Tcost)) { + tmp = cuddBddAndRecur(manager,manager->vars[my_dd->index],sol); + if (tmp == NULL) { + Cudd_RecursiveDeref(manager,sol); + return(NULL); + } + cuddRef(tmp); + Cudd_RecursiveDeref(manager,sol); + sol = tmp; + + complement = Cudd_IsComplement(T); + my_dd = Cudd_Regular(T); + cost = Tcost; + continue; + } + st_lookup(visited, (char *)Cudd_Regular(E), (char **)&E_pair); + if ((Cudd_IsComplement(E) && E_pair->neg == Ecost) || + (!Cudd_IsComplement(E) && E_pair->pos == Ecost)) { + tmp = cuddBddAndRecur(manager,Cudd_Not(manager->vars[my_dd->index]),sol); + if (tmp == NULL) { + Cudd_RecursiveDeref(manager,sol); + return(NULL); + } + cuddRef(tmp); + Cudd_RecursiveDeref(manager,sol); + sol = tmp; + complement = Cudd_IsComplement(E); + my_dd = Cudd_Regular(E); + cost = Ecost; + continue; + } + (void) fprintf(manager->err,"We shouldn't be here!\n"); + manager->errorCode = CUDD_INTERNAL_ERROR; + return(NULL); + } + + cuddDeref(sol); + return(sol); + +} /* end of getCube */ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddSign.c b/abc_with_bb_support/src/bdd/cudd/cuddSign.c new file mode 100644 index 000000000..d2d4d4299 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddSign.c @@ -0,0 +1,292 @@ +/**CFile*********************************************************************** + + FileName [cuddSign.c] + + PackageName [cudd] + + Synopsis [Computation of signatures] + + Description [External procedures included in this module: +
        +
      • Cudd_CofMinterm(); +
      + Static procedures included in this module: +
        +
      • ddCofMintermAux() +
      + ] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddSign.c,v 1.1.1.1 2003/02/24 22:23:53 wjiang Exp $"; +#endif + +static int size; + +#ifdef DD_STATS +static int num_calls; /* should equal 2n-1 (n is the # of nodes) */ +static int table_mem; +#endif + + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static double * ddCofMintermAux ARGS((DdManager *dd, DdNode *node, st_table *table)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Computes the fraction of minterms in the on-set of all the + positive cofactors of a BDD or ADD.] + + Description [Computes the fraction of minterms in the on-set of all + the positive cofactors of DD. Returns the pointer to an array of + doubles if successful; NULL otherwise. The array hs as many + positions as there are BDD variables in the manager plus one. The + last position of the array contains the fraction of the minterms in + the ON-set of the function represented by the BDD or ADD. The other + positions of the array hold the variable signatures.] + + SideEffects [None] + +******************************************************************************/ +double * +Cudd_CofMinterm( + DdManager * dd, + DdNode * node) +{ + st_table *table; + double *values; + double *result = NULL; + int i, firstLevel; + +#ifdef DD_STATS + long startTime; + startTime = util_cpu_time(); + num_calls = 0; + table_mem = sizeof(st_table); +#endif + + table = st_init_table(st_ptrcmp, st_ptrhash); + if (table == NULL) { + (void) fprintf(dd->err, + "out-of-memory, couldn't measure DD cofactors.\n"); + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + size = dd->size; + values = ddCofMintermAux(dd, node, table); + if (values != NULL) { + result = ALLOC(double,size + 1); + if (result != NULL) { +#ifdef DD_STATS + table_mem += (size + 1) * sizeof(double); +#endif + if (Cudd_IsConstant(node)) + firstLevel = 1; + else + firstLevel = cuddI(dd,Cudd_Regular(node)->index); + for (i = 0; i < size; i++) { + if (i >= cuddI(dd,Cudd_Regular(node)->index)) { + result[dd->invperm[i]] = values[i - firstLevel]; + } else { + result[dd->invperm[i]] = values[size - firstLevel]; + } + } + result[size] = values[size - firstLevel]; + } else { + dd->errorCode = CUDD_MEMORY_OUT; + } + } + +#ifdef DD_STATS + table_mem += table->num_bins * sizeof(st_table_entry *); +#endif + if (Cudd_Regular(node)->ref == 1) FREE(values); + st_foreach(table, cuddStCountfree, NULL); + st_free_table(table); +#ifdef DD_STATS + (void) fprintf(dd->out,"Number of calls: %d\tTable memory: %d bytes\n", + num_calls, table_mem); + (void) fprintf(dd->out,"Time to compute measures: %s\n", + util_print_time(util_cpu_time() - startTime)); +#endif + if (result == NULL) { + (void) fprintf(dd->out, + "out-of-memory, couldn't measure DD cofactors.\n"); + dd->errorCode = CUDD_MEMORY_OUT; + } + return(result); + +} /* end of Cudd_CofMinterm */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Recursive Step for Cudd_CofMinterm function.] + + Description [Traverses the DD node and computes the fraction of + minterms in the on-set of all positive cofactors simultaneously. + It allocates an array with two more entries than there are + variables below the one labeling the node. One extra entry (the + first in the array) is for the variable labeling the node. The other + entry (the last one in the array) holds the fraction of minterms of + the function rooted at node. Each other entry holds the value for + one cofactor. The array is put in a symbol table, to avoid repeated + computation, and its address is returned by the procedure, for use + by the caller. Returns a pointer to the array of cofactor measures.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static double * +ddCofMintermAux( + DdManager * dd, + DdNode * node, + st_table * table) +{ + DdNode *N; /* regular version of node */ + DdNode *Nv, *Nnv; + double *values; + double *valuesT, *valuesE; + int i; + int localSize, localSizeT, localSizeE; + double vT, vE; + + statLine(dd); +#ifdef DD_STATS + num_calls++; +#endif + + if (st_lookup(table, (char *) node, (char **) &values)) { + return(values); + } + + N = Cudd_Regular(node); + if (cuddIsConstant(N)) { + localSize = 1; + } else { + localSize = size - cuddI(dd,N->index) + 1; + } + values = ALLOC(double, localSize); + if (values == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + + if (cuddIsConstant(N)) { + if (node == DD_ZERO(dd) || node == Cudd_Not(DD_ONE(dd))) { + values[0] = 0.0; + } else { + values[0] = 1.0; + } + } else { + Nv = Cudd_NotCond(cuddT(N),N!=node); + Nnv = Cudd_NotCond(cuddE(N),N!=node); + + valuesT = ddCofMintermAux(dd, Nv, table); + if (valuesT == NULL) return(NULL); + valuesE = ddCofMintermAux(dd, Nnv, table); + if (valuesE == NULL) return(NULL); + + if (Cudd_IsConstant(Nv)) { + localSizeT = 1; + } else { + localSizeT = size - cuddI(dd,Cudd_Regular(Nv)->index) + 1; + } + if (Cudd_IsConstant(Nnv)) { + localSizeE = 1; + } else { + localSizeE = size - cuddI(dd,Cudd_Regular(Nnv)->index) + 1; + } + values[0] = valuesT[localSizeT - 1]; + for (i = 1; i < localSize; i++) { + if (i >= cuddI(dd,Cudd_Regular(Nv)->index) - cuddI(dd,N->index)) { + vT = valuesT[i - cuddI(dd,Cudd_Regular(Nv)->index) + + cuddI(dd,N->index)]; + } else { + vT = valuesT[localSizeT - 1]; + } + if (i >= cuddI(dd,Cudd_Regular(Nnv)->index) - cuddI(dd,N->index)) { + vE = valuesE[i - cuddI(dd,Cudd_Regular(Nnv)->index) + + cuddI(dd,N->index)]; + } else { + vE = valuesE[localSizeE - 1]; + } + values[i] = (vT + vE) / 2.0; + } + if (Cudd_Regular(Nv)->ref == 1) FREE(valuesT); + if (Cudd_Regular(Nnv)->ref == 1) FREE(valuesE); + } + + if (N->ref > 1) { + if (st_add_direct(table, (char *) node, (char *) values) == ST_OUT_OF_MEM) { + FREE(values); + return(NULL); + } +#ifdef DD_STATS + table_mem += localSize * sizeof(double) + sizeof(st_table_entry); +#endif + } + return(values); + +} /* end of ddCofMintermAux */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddSolve.c b/abc_with_bb_support/src/bdd/cudd/cuddSolve.c new file mode 100644 index 000000000..f16fb1603 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddSolve.c @@ -0,0 +1,339 @@ +/**CFile*********************************************************************** + + FileName [cuddSolve.c] + + PackageName [cudd] + + Synopsis [Boolean equation solver and related functions.] + + Description [External functions included in this modoule: +
        +
      • Cudd_SolveEqn() +
      • Cudd_VerifySol() +
      + Internal functions included in this module: +
        +
      • cuddSolveEqnRecur() +
      • cuddVerifySol() +
      ] + + SeeAlso [] + + Author [Balakrishna Kumthekar] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Structure declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddSolve.c,v 1.1.1.1 2003/02/24 22:23:53 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Implements the solution of F(x,y) = 0.] + + Description [Implements the solution for F(x,y) = 0. The return + value is the consistency condition. The y variables are the unknowns + and the remaining variables are the parameters. Returns the + consistency condition if successful; NULL otherwise. Cudd_SolveEqn + allocates an array and fills it with the indices of the + unknowns. This array is used by Cudd_VerifySol.] + + SideEffects [The solution is returned in G; the indices of the y + variables are returned in yIndex.] + + SeeAlso [Cudd_VerifySol] + +******************************************************************************/ +DdNode * +Cudd_SolveEqn( + DdManager * bdd, + DdNode * F /* the left-hand side of the equation */, + DdNode * Y /* the cube of the y variables */, + DdNode ** G /* the array of solutions (return parameter) */, + int ** yIndex /* index of y variables */, + int n /* numbers of unknowns */) +{ + DdNode *res; + int *temp; + + *yIndex = temp = ALLOC(int, n); + if (temp == NULL) { + bdd->errorCode = CUDD_MEMORY_OUT; + (void) fprintf(bdd->out, + "Cudd_SolveEqn: Out of memory for yIndex\n"); + return(NULL); + } + + do { + bdd->reordered = 0; + res = cuddSolveEqnRecur(bdd, F, Y, G, n, temp, 0); + } while (bdd->reordered == 1); + + return(res); + +} /* end of Cudd_SolveEqn */ + + +/**Function******************************************************************** + + Synopsis [Checks the solution of F(x,y) = 0.] + + Description [Checks the solution of F(x,y) = 0. This procedure + substitutes the solution components for the unknowns of F and returns + the resulting BDD for F.] + + SideEffects [Frees the memory pointed by yIndex.] + + SeeAlso [Cudd_SolveEqn] + +******************************************************************************/ +DdNode * +Cudd_VerifySol( + DdManager * bdd, + DdNode * F /* the left-hand side of the equation */, + DdNode ** G /* the array of solutions */, + int * yIndex /* index of y variables */, + int n /* numbers of unknowns */) +{ + DdNode *res; + + do { + bdd->reordered = 0; + res = cuddVerifySol(bdd, F, G, yIndex, n); + } while (bdd->reordered == 1); + + FREE(yIndex); + + return(res); + +} /* end of Cudd_VerifySol */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_SolveEqn.] + + Description [Implements the recursive step of Cudd_SolveEqn. + Returns NULL if the intermediate solution blows up + or reordering occurs. The parametric solutions are + stored in the array G.] + + SideEffects [none] + + SeeAlso [Cudd_SolveEqn, Cudd_VerifySol] + +******************************************************************************/ +DdNode * +cuddSolveEqnRecur( + DdManager * bdd, + DdNode * F /* the left-hand side of the equation */, + DdNode * Y /* the cube of remaining y variables */, + DdNode ** G /* the array of solutions */, + int n /* number of unknowns */, + int * yIndex /* array holding the y variable indices */, + int i /* level of recursion */) +{ + DdNode *Fn, *Fm1, *Fv, *Fvbar, *T, *w, *nextY, *one; + DdNodePtr *variables; + + int j; + + statLine(bdd); + variables = bdd->vars; + one = DD_ONE(bdd); + + /* Base condition. */ + if (Y == one) { + return F; + } + + /* Cofactor of Y. */ + yIndex[i] = Y->index; + nextY = Cudd_T(Y); + + /* Universal abstraction of F with respect to the top variable index. */ + Fm1 = cuddBddExistAbstractRecur(bdd, Cudd_Not(F), variables[yIndex[i]]); + if (Fm1) { + Fm1 = Cudd_Not(Fm1); + cuddRef(Fm1); + } else { + return(NULL); + } + + Fn = cuddSolveEqnRecur(bdd, Fm1, nextY, G, n, yIndex, i+1); + if (Fn) { + cuddRef(Fn); + } else { + Cudd_RecursiveDeref(bdd, Fm1); + return(NULL); + } + + Fv = cuddCofactorRecur(bdd, F, variables[yIndex[i]]); + if (Fv) { + cuddRef(Fv); + } else { + Cudd_RecursiveDeref(bdd, Fm1); + Cudd_RecursiveDeref(bdd, Fn); + return(NULL); + } + + Fvbar = cuddCofactorRecur(bdd, F, Cudd_Not(variables[yIndex[i]])); + if (Fvbar) { + cuddRef(Fvbar); + } else { + Cudd_RecursiveDeref(bdd, Fm1); + Cudd_RecursiveDeref(bdd, Fn); + Cudd_RecursiveDeref(bdd, Fv); + return(NULL); + } + + /* Build i-th component of the solution. */ + w = cuddBddIteRecur(bdd, variables[yIndex[i]], Cudd_Not(Fv), Fvbar); + if (w) { + cuddRef(w); + } else { + Cudd_RecursiveDeref(bdd, Fm1); + Cudd_RecursiveDeref(bdd, Fn); + Cudd_RecursiveDeref(bdd, Fv); + Cudd_RecursiveDeref(bdd, Fvbar); + return(NULL); + } + + T = cuddBddRestrictRecur(bdd, w, Cudd_Not(Fm1)); + if(T) { + cuddRef(T); + } else { + Cudd_RecursiveDeref(bdd, Fm1); + Cudd_RecursiveDeref(bdd, Fn); + Cudd_RecursiveDeref(bdd, Fv); + Cudd_RecursiveDeref(bdd, Fvbar); + Cudd_RecursiveDeref(bdd, w); + return(NULL); + } + + Cudd_RecursiveDeref(bdd,Fm1); + Cudd_RecursiveDeref(bdd,w); + Cudd_RecursiveDeref(bdd,Fv); + Cudd_RecursiveDeref(bdd,Fvbar); + + /* Substitute components of solution already found into solution. */ + for (j = n-1; j > i; j--) { + w = cuddBddComposeRecur(bdd,T, G[j], variables[yIndex[j]]); + if(w) { + cuddRef(w); + } else { + Cudd_RecursiveDeref(bdd, Fn); + Cudd_RecursiveDeref(bdd, T); + return(NULL); + } + Cudd_RecursiveDeref(bdd,T); + T = w; + } + G[i] = T; + + Cudd_Deref(Fn); + + return(Fn); + +} /* end of cuddSolveEqnRecur */ + + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_VerifySol. ] + + Description [] + + SideEffects [none] + + SeeAlso [Cudd_VerifySol] + +******************************************************************************/ +DdNode * +cuddVerifySol( + DdManager * bdd, + DdNode * F /* the left-hand side of the equation */, + DdNode ** G /* the array of solutions */, + int * yIndex /* array holding the y variable indices */, + int n /* number of unknowns */) +{ + DdNode *w, *R; + + int j; + + R = F; + cuddRef(R); + for(j = n - 1; j >= 0; j--) { + w = Cudd_bddCompose(bdd, R, G[j], yIndex[j]); + if (w) { + cuddRef(w); + } else { + return(NULL); + } + Cudd_RecursiveDeref(bdd,R); + R = w; + } + + cuddDeref(R); + + return(R); + +} /* end of cuddVerifySol */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddSplit.c b/abc_with_bb_support/src/bdd/cudd/cuddSplit.c new file mode 100644 index 000000000..bcf77eb2c --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddSplit.c @@ -0,0 +1,657 @@ +/**CFile*********************************************************************** + + FileName [cuddSplit.c] + + PackageName [cudd] + + Synopsis [Returns a subset of minterms from a boolean function.] + + Description [External functions included in this modoule: +
        +
      • Cudd_SplitSet() +
      + Internal functions included in this module: +
        +
      • cuddSplitSetRecur() + + Static functions included in this module: +
          +
        • selectMintermsFromUniverse() +
        • mintermsFromUniverse() +
        • bddAnnotateMintermCount() +
        ] + + SeeAlso [] + + Author [Balakrishna Kumthekar] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Structure declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static DdNode * selectMintermsFromUniverse ARGS((DdManager *manager, int *varSeen, double n)); +static DdNode * mintermsFromUniverse ARGS((DdManager *manager, DdNode **vars, int numVars, double n, int index)); +static double bddAnnotateMintermCount ARGS((DdManager *manager, DdNode *node, double max, st_table *table)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Returns m minterms from a BDD.] + + Description [Returns m minterms from a BDD whose + support has n variables at most. The procedure tries + to create as few extra nodes as possible. The function represented + by S depends on at most n of the variables + in xVars. Returns a BDD with m minterms + of the on-set of S if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +Cudd_SplitSet( + DdManager * manager, + DdNode * S, + DdNode ** xVars, + int n, + double m) +{ + DdNode *result; + DdNode *zero, *one; + double max, num; + st_table *mtable; + int *varSeen; + int i,index, size; + + size = manager->size; + one = DD_ONE(manager); + zero = Cudd_Not(one); + + /* Trivial cases. */ + if (m == 0.0) { + return(zero); + } + if (S == zero) { + return(NULL); + } + + max = pow(2.0,(double)n); + if (m > max) + return(NULL); + + do { + manager->reordered = 0; + /* varSeen is used to mark the variables that are encountered + ** while traversing the BDD S. + */ + varSeen = ALLOC(int, size); + if (varSeen == NULL) { + manager->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + for (i = 0; i < size; i++) { + varSeen[i] = -1; + } + for (i = 0; i < n; i++) { + index = (xVars[i])->index; + varSeen[manager->invperm[index]] = 0; + } + + if (S == one) { + if (m == max) + return(S); + result = selectMintermsFromUniverse(manager,varSeen,m); + if (result) + cuddRef(result); + FREE(varSeen); + } else { + mtable = st_init_table(st_ptrcmp,st_ptrhash); + if (mtable == NULL) { + (void) fprintf(manager->out, + "Cudd_SplitSet: out-of-memory.\n"); + FREE(varSeen); + manager->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + /* The nodes of BDD S are annotated by the number of minterms + ** in their onset. The node and the number of minterms in its + ** onset are stored in mtable. + */ + num = bddAnnotateMintermCount(manager,S,max,mtable); + if (m == num) { + st_foreach(mtable,cuddStCountfree,NIL(char)); + st_free_table(mtable); + FREE(varSeen); + return(S); + } + + result = cuddSplitSetRecur(manager,mtable,varSeen,S,m,max,0); + if (result) + cuddRef(result); + st_foreach(mtable,cuddStCountfree,NULL); + st_free_table(mtable); + FREE(varSeen); + } + } while (manager->reordered == 1); + + cuddDeref(result); + return(result); + +} /* end of Cudd_SplitSet */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_SplitSet.] + + Description [Implements the recursive step of Cudd_SplitSet. The + procedure recursively traverses the BDD and checks to see if any + node satisfies the minterm requirements as specified by 'n'. At any + node X, n is compared to the number of minterms in the onset of X's + children. If either of the child nodes have exactly n minterms, then + that node is returned; else, if n is greater than the onset of one + of the child nodes, that node is retained and the difference in the + number of minterms is extracted from the other child. In case n + minterms can be extracted from constant 1, the algorithm returns the + result with at most log(n) nodes.] + + SideEffects [The array 'varSeen' is updated at every recursive call + to set the variables traversed by the procedure.] + + SeeAlso [] + +******************************************************************************/ +DdNode* +cuddSplitSetRecur( + DdManager * manager, + st_table * mtable, + int * varSeen, + DdNode * p, + double n, + double max, + int index) +{ + DdNode *one, *zero, *N, *Nv; + DdNode *Nnv, *q, *r, *v; + DdNode *result; + double *dummy, numT, numE; + int variable, positive; + + statLine(manager); + one = DD_ONE(manager); + zero = Cudd_Not(one); + + /* If p is constant, extract n minterms from constant 1. The procedure by + ** construction guarantees that minterms will not be extracted from + ** constant 0. + */ + if (Cudd_IsConstant(p)) { + q = selectMintermsFromUniverse(manager,varSeen,n); + return(q); + } + + N = Cudd_Regular(p); + + /* Set variable as seen. */ + variable = N->index; + varSeen[manager->invperm[variable]] = -1; + + Nv = cuddT(N); + Nnv = cuddE(N); + if (Cudd_IsComplement(p)) { + Nv = Cudd_Not(Nv); + Nnv = Cudd_Not(Nnv); + } + + /* If both the children of 'p' are constants, extract n minterms from a + ** constant node. + */ + if (Cudd_IsConstant(Nv) && Cudd_IsConstant(Nnv)) { + q = selectMintermsFromUniverse(manager,varSeen,n); + if (q == NULL) { + return(NULL); + } + cuddRef(q); + r = cuddBddAndRecur(manager,p,q); + if (r == NULL) { + Cudd_RecursiveDeref(manager,q); + return(NULL); + } + cuddRef(r); + Cudd_RecursiveDeref(manager,q); + cuddDeref(r); + return(r); + } + + /* Lookup the # of minterms in the onset of the node from the table. */ + if (!Cudd_IsConstant(Nv)) { + st_lookup(mtable,(char *)Nv, (char **)&dummy); + numT = *dummy/(2*(1<size; + one = DD_ONE(manager); + zero = Cudd_Not(one); + + /* Count the number of variables not encountered so far in procedure + ** cuddSplitSetRecur. + */ + for (i = size-1; i >= 0; i--) { + if(varSeen[i] == 0) + numVars++; + } + vars = ALLOC(DdNode *, numVars); + if (!vars) { + manager->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + + j = 0; + for (i = size-1; i >= 0; i--) { + if(varSeen[i] == 0) { + vars[j] = cuddUniqueInter(manager,manager->perm[i],one,zero); + cuddRef(vars[j]); + j++; + } + } + + /* Compute a function which has n minterms and depends on at most + ** numVars variables. + */ + result = mintermsFromUniverse(manager,vars,numVars,n, 0); + if (result) + cuddRef(result); + + for (i = 0; i < numVars; i++) + Cudd_RecursiveDeref(manager,vars[i]); + FREE(vars); + + return(result); + +} /* end of selectMintermsFromUniverse */ + + +/**Function******************************************************************** + + Synopsis [Recursive procedure to extract n mintems from constant 1.] + + Description [Recursive procedure to extract n mintems from constant 1.] + + SideEffects [None] + +******************************************************************************/ +static DdNode * +mintermsFromUniverse( + DdManager * manager, + DdNode ** vars, + int numVars, + double n, + int index) +{ + DdNode *one, *zero; + DdNode *q, *result; + double max, max2; + + statLine(manager); + one = DD_ONE(manager); + zero = Cudd_Not(one); + + max = pow(2.0, (double)numVars); + max2 = max / 2.0; + + if (n == max) + return(one); + if (n == 0.0) + return(zero); + /* if n == 2^(numVars-1), return a single variable */ + if (n == max2) + return vars[index]; + else if (n > max2) { + /* When n > 2^(numVars-1), a single variable vars[index] + ** contains 2^(numVars-1) minterms. The rest are extracted + ** from a constant with 1 less variable. + */ + q = mintermsFromUniverse(manager,vars,numVars-1,(n-max2),index+1); + if (q == NULL) + return(NULL); + cuddRef(q); + result = cuddBddIteRecur(manager,vars[index],one,q); + } else { + /* When n < 2^(numVars-1), a literal of variable vars[index] + ** is selected. The required n minterms are extracted from a + ** constant with 1 less variable. + */ + q = mintermsFromUniverse(manager,vars,numVars-1,n,index+1); + if (q == NULL) + return(NULL); + cuddRef(q); + result = cuddBddAndRecur(manager,vars[index],q); + } + + if (result == NULL) { + Cudd_RecursiveDeref(manager,q); + return(NULL); + } + cuddRef(result); + Cudd_RecursiveDeref(manager,q); + cuddDeref(result); + return(result); + +} /* end of mintermsFromUniverse */ + + +/**Function******************************************************************** + + Synopsis [Annotates every node in the BDD node with its minterm count.] + + Description [Annotates every node in the BDD node with its minterm count. + In this function, every node and the minterm count represented by it are + stored in a hash table.] + + SideEffects [Fills up 'table' with the pair .] + +******************************************************************************/ +static double +bddAnnotateMintermCount( + DdManager * manager, + DdNode * node, + double max, + st_table * table) +{ + + DdNode *N,*Nv,*Nnv; + register double min_v,min_nv; + register double min_N; + double *pmin; + double *dummy; + + statLine(manager); + N = Cudd_Regular(node); + if (cuddIsConstant(N)) { + if (node == DD_ONE(manager)) { + return(max); + } else { + return(0.0); + } + } + + if (st_lookup(table,(char *)node,(char **)&dummy)) { + return(*dummy); + } + + Nv = cuddT(N); + Nnv = cuddE(N); + if (N != node) { + Nv = Cudd_Not(Nv); + Nnv = Cudd_Not(Nnv); + } + + /* Recur on the two branches. */ + min_v = bddAnnotateMintermCount(manager,Nv,max,table) / 2.0; + if (min_v == (double)CUDD_OUT_OF_MEM) + return ((double)CUDD_OUT_OF_MEM); + min_nv = bddAnnotateMintermCount(manager,Nnv,max,table) / 2.0; + if (min_nv == (double)CUDD_OUT_OF_MEM) + return ((double)CUDD_OUT_OF_MEM); + min_N = min_v + min_nv; + + pmin = ALLOC(double,1); + if (pmin == NULL) { + manager->errorCode = CUDD_MEMORY_OUT; + return((double)CUDD_OUT_OF_MEM); + } + *pmin = min_N; + + if (st_insert(table,(char *)node, (char *)pmin) == ST_OUT_OF_MEM) { + FREE(pmin); + return((double)CUDD_OUT_OF_MEM); + } + + return(min_N); + +} /* end of bddAnnotateMintermCount */ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddSubsetHB.c b/abc_with_bb_support/src/bdd/cudd/cuddSubsetHB.c new file mode 100644 index 000000000..d10113c33 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddSubsetHB.c @@ -0,0 +1,1311 @@ +/**CFile*********************************************************************** + + FileName [cuddSubsetHB.c] + + PackageName [cudd] + + Synopsis [Procedure to subset the given BDD by choosing the heavier + branches] + + + Description [External procedures provided by this module: +
          +
        • Cudd_SubsetHeavyBranch() +
        • Cudd_SupersetHeavyBranch() +
        + Internal procedures included in this module: +
          +
        • cuddSubsetHeavyBranch() +
        + Static procedures included in this module: +
          +
        • ResizeCountMintermPages(); +
        • ResizeNodeDataPages() +
        • ResizeCountNodePages() +
        • SubsetCountMintermAux() +
        • SubsetCountMinterm() +
        • SubsetCountNodesAux() +
        • SubsetCountNodes() +
        • BuildSubsetBdd() +
        + ] + + SeeAlso [cuddSubsetSP.c] + + Author [Kavita Ravi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no + warranty about the suitability of this software for any + purpose. It is presented on an AS IS basis.] + +******************************************************************************/ + +#ifdef __STDC__ +#include +#else +#define DBL_MAX_EXP 1024 +#endif +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define DEFAULT_PAGE_SIZE 2048 +#define DEFAULT_NODE_DATA_PAGE_SIZE 1024 +#define INITIAL_PAGES 128 + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/* data structure to store the information on each node. It keeps + * the number of minterms represented by the DAG rooted at this node + * in terms of the number of variables specified by the user, number + * of nodes in this DAG and the number of nodes of its child with + * lesser number of minterms that are not shared by the child with + * more minterms + */ +struct NodeData { + double *mintermPointer; + int *nodesPointer; + int *lightChildNodesPointer; +}; + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +typedef struct NodeData NodeData_t; + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddSubsetHB.c,v 1.1.1.1 2003/02/24 22:23:53 wjiang Exp $"; +#endif + +static int memOut; +#ifdef DEBUG +static int num_calls; +#endif + +static DdNode *zero, *one; /* constant functions */ +static double **mintermPages; /* pointers to the pages */ +static int **nodePages; /* pointers to the pages */ +static int **lightNodePages; /* pointers to the pages */ +static double *currentMintermPage; /* pointer to the current + page */ +static double max; /* to store the 2^n value of the number + * of variables */ + +static int *currentNodePage; /* pointer to the current + page */ +static int *currentLightNodePage; /* pointer to the + * current page */ +static int pageIndex; /* index to next element */ +static int page; /* index to current page */ +static int pageSize = DEFAULT_PAGE_SIZE; /* page size */ +static int maxPages; /* number of page pointers */ + +static NodeData_t *currentNodeDataPage; /* pointer to the current + page */ +static int nodeDataPage; /* index to next element */ +static int nodeDataPageIndex; /* index to next element */ +static NodeData_t **nodeDataPages; /* index to current page */ +static int nodeDataPageSize = DEFAULT_NODE_DATA_PAGE_SIZE; + /* page size */ +static int maxNodeDataPages; /* number of page pointers */ + + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static void ResizeNodeDataPages ARGS(()); +static void ResizeCountMintermPages ARGS(()); +static void ResizeCountNodePages ARGS(()); +static double SubsetCountMintermAux ARGS((DdNode *node, double max, st_table *table)); +static st_table * SubsetCountMinterm ARGS((DdNode *node, int nvars)); +static int SubsetCountNodesAux ARGS((DdNode *node, st_table *table, double max)); +static int SubsetCountNodes ARGS((DdNode *node, st_table *table, int nvars)); +static void StoreNodes ARGS((st_table *storeTable, DdManager *dd, DdNode *node)); +static DdNode * BuildSubsetBdd ARGS((DdManager *dd, DdNode *node, int *size, st_table *visitedTable, int threshold, st_table *storeTable, st_table *approxTable)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Extracts a dense subset from a BDD with the heavy branch + heuristic.] + + Description [Extracts a dense subset from a BDD. This procedure + builds a subset by throwing away one of the children of each node, + starting from the root, until the result is small enough. The child + that is eliminated from the result is the one that contributes the + fewer minterms. Returns a pointer to the BDD of the subset if + successful. NULL if the procedure runs out of memory. The parameter + numVars is the maximum number of variables to be used in minterm + calculation and node count calculation. The optimal number should + be as close as possible to the size of the support of f. However, + it is safe to pass the value returned by Cudd_ReadSize for numVars + when the number of variables is under 1023. If numVars is larger + than 1023, it will overflow. If a 0 parameter is passed then the + procedure will compute a value which will avoid overflow but will + cause underflow with 2046 variables or more.] + + SideEffects [None] + + SeeAlso [Cudd_SubsetShortPaths Cudd_SupersetHeavyBranch Cudd_ReadSize] + +******************************************************************************/ +DdNode * +Cudd_SubsetHeavyBranch( + DdManager * dd /* manager */, + DdNode * f /* function to be subset */, + int numVars /* number of variables in the support of f */, + int threshold /* maximum number of nodes in the subset */) +{ + DdNode *subset; + + memOut = 0; + do { + dd->reordered = 0; + subset = cuddSubsetHeavyBranch(dd, f, numVars, threshold); + } while ((dd->reordered == 1) && (!memOut)); + + return(subset); + +} /* end of Cudd_SubsetHeavyBranch */ + + +/**Function******************************************************************** + + Synopsis [Extracts a dense superset from a BDD with the heavy branch + heuristic.] + + Description [Extracts a dense superset from a BDD. The procedure is + identical to the subset procedure except for the fact that it + receives the complement of the given function. Extracting the subset + of the complement function is equivalent to extracting the superset + of the function. This procedure builds a superset by throwing away + one of the children of each node starting from the root of the + complement function, until the result is small enough. The child + that is eliminated from the result is the one that contributes the + fewer minterms. + Returns a pointer to the BDD of the superset if successful. NULL if + intermediate result causes the procedure to run out of memory. The + parameter numVars is the maximum number of variables to be used in + minterm calculation and node count calculation. The optimal number + should be as close as possible to the size of the support of f. + However, it is safe to pass the value returned by Cudd_ReadSize for + numVars when the number of variables is under 1023. If numVars is + larger than 1023, it will overflow. If a 0 parameter is passed then + the procedure will compute a value which will avoid overflow but + will cause underflow with 2046 variables or more.] + + SideEffects [None] + + SeeAlso [Cudd_SubsetHeavyBranch Cudd_SupersetShortPaths Cudd_ReadSize] + +******************************************************************************/ +DdNode * +Cudd_SupersetHeavyBranch( + DdManager * dd /* manager */, + DdNode * f /* function to be superset */, + int numVars /* number of variables in the support of f */, + int threshold /* maximum number of nodes in the superset */) +{ + DdNode *subset, *g; + + g = Cudd_Not(f); + memOut = 0; + do { + dd->reordered = 0; + subset = cuddSubsetHeavyBranch(dd, g, numVars, threshold); + } while ((dd->reordered == 1) && (!memOut)); + + return(Cudd_NotCond(subset, (subset != NULL))); + +} /* end of Cudd_SupersetHeavyBranch */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [The main procedure that returns a subset by choosing the heavier + branch in the BDD.] + + Description [Here a subset BDD is built by throwing away one of the + children. Starting at root, annotate each node with the number of + minterms (in terms of the total number of variables specified - + numVars), number of nodes taken by the DAG rooted at this node and + number of additional nodes taken by the child that has the lesser + minterms. The child with the lower number of minterms is thrown away + and a dyanmic count of the nodes of the subset is kept. Once the + threshold is reached the subset is returned to the calling + procedure.] + + SideEffects [None] + + SeeAlso [Cudd_SubsetHeavyBranch] + +******************************************************************************/ +DdNode * +cuddSubsetHeavyBranch( + DdManager * dd /* DD manager */, + DdNode * f /* current DD */, + int numVars /* maximum number of variables */, + int threshold /* threshold size for the subset */) +{ + + int i, *size; + st_table *visitedTable; + int numNodes; + NodeData_t *currNodeQual; + DdNode *subset; + double minN; + st_table *storeTable, *approxTable; + char *key, *value; + st_generator *stGen; + + if (f == NULL) { + fprintf(dd->err, "Cannot subset, nil object\n"); + dd->errorCode = CUDD_INVALID_ARG; + return(NULL); + } + + one = Cudd_ReadOne(dd); + zero = Cudd_Not(one); + + /* If user does not know numVars value, set it to the maximum + * exponent that the pow function can take. The -1 is due to the + * discrepancy in the value that pow takes and the value that + * log gives. + */ + if (numVars == 0) { + /* set default value */ + numVars = DBL_MAX_EXP - 1; + } + + if (Cudd_IsConstant(f)) { + return(f); + } + + max = pow(2.0, (double)numVars); + + /* Create visited table where structures for node data are allocated and + stored in a st_table */ + visitedTable = SubsetCountMinterm(f, numVars); + if ((visitedTable == NULL) || memOut) { + (void) fprintf(dd->err, "Out-of-memory; Cannot subset\n"); + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + numNodes = SubsetCountNodes(f, visitedTable, numVars); + if (memOut) { + (void) fprintf(dd->err, "Out-of-memory; Cannot subset\n"); + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + + if (st_lookup(visitedTable, (char *)f, (char **)&currNodeQual)) { + minN = *(((NodeData_t *)currNodeQual)->mintermPointer); + } else { + fprintf(dd->err, + "Something is wrong, ought to be node quality table\n"); + dd->errorCode = CUDD_INTERNAL_ERROR; + } + + size = ALLOC(int, 1); + if (size == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + *size = numNodes; + +#ifdef DEBUG + num_calls = 0; +#endif + /* table to store nodes being created. */ + storeTable = st_init_table(st_ptrcmp, st_ptrhash); + /* insert the constant */ + cuddRef(one); + if (st_insert(storeTable, (char *)Cudd_ReadOne(dd), NIL(char)) == + ST_OUT_OF_MEM) { + fprintf(dd->out, "Something wrong, st_table insert failed\n"); + } + /* table to store approximations of nodes */ + approxTable = st_init_table(st_ptrcmp, st_ptrhash); + subset = (DdNode *)BuildSubsetBdd(dd, f, size, visitedTable, threshold, + storeTable, approxTable); + if (subset != NULL) { + cuddRef(subset); + } + + stGen = st_init_gen(approxTable); + if (stGen == NULL) { + st_free_table(approxTable); + return(NULL); + } + while(st_gen(stGen, (char **)&key, (char **)&value)) { + Cudd_RecursiveDeref(dd, (DdNode *)value); + } + st_free_gen(stGen); stGen = NULL; + st_free_table(approxTable); + + stGen = st_init_gen(storeTable); + if (stGen == NULL) { + st_free_table(storeTable); + return(NULL); + } + while(st_gen(stGen, (char **)&key, (char **)&value)) { + Cudd_RecursiveDeref(dd, (DdNode *)key); + } + st_free_gen(stGen); stGen = NULL; + st_free_table(storeTable); + + for (i = 0; i <= page; i++) { + FREE(mintermPages[i]); + } + FREE(mintermPages); + for (i = 0; i <= page; i++) { + FREE(nodePages[i]); + } + FREE(nodePages); + for (i = 0; i <= page; i++) { + FREE(lightNodePages[i]); + } + FREE(lightNodePages); + for (i = 0; i <= nodeDataPage; i++) { + FREE(nodeDataPages[i]); + } + FREE(nodeDataPages); + st_free_table(visitedTable); + FREE(size); +#if 0 + (void) Cudd_DebugCheck(dd); + (void) Cudd_CheckKeys(dd); +#endif + + if (subset != NULL) { +#ifdef DD_DEBUG + if (!Cudd_bddLeq(dd, subset, f)) { + fprintf(dd->err, "Wrong subset\n"); + dd->errorCode = CUDD_INTERNAL_ERROR; + return(NULL); + } +#endif + cuddDeref(subset); + return(subset); + } else { + return(NULL); + } +} /* end of cuddSubsetHeavyBranch */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Resize the number of pages allocated to store the node data.] + + Description [Resize the number of pages allocated to store the node data + The procedure moves the counter to the next page when the end of + the page is reached and allocates new pages when necessary.] + + SideEffects [Changes the size of pages, page, page index, maximum + number of pages freeing stuff in case of memory out. ] + + SeeAlso [] + +******************************************************************************/ +static void +ResizeNodeDataPages( + ) +{ + int i; + NodeData_t **newNodeDataPages; + + nodeDataPage++; + /* If the current page index is larger than the number of pages + * allocated, allocate a new page array. Page numbers are incremented by + * INITIAL_PAGES + */ + if (nodeDataPage == maxNodeDataPages) { + newNodeDataPages = ALLOC(NodeData_t *,maxNodeDataPages + INITIAL_PAGES); + if (newNodeDataPages == NULL) { + for (i = 0; i < nodeDataPage; i++) FREE(nodeDataPages[i]); + FREE(nodeDataPages); + memOut = 1; + return; + } else { + for (i = 0; i < maxNodeDataPages; i++) { + newNodeDataPages[i] = nodeDataPages[i]; + } + /* Increase total page count */ + maxNodeDataPages += INITIAL_PAGES; + FREE(nodeDataPages); + nodeDataPages = newNodeDataPages; + } + } + /* Allocate a new page */ + currentNodeDataPage = nodeDataPages[nodeDataPage] = + ALLOC(NodeData_t ,nodeDataPageSize); + if (currentNodeDataPage == NULL) { + for (i = 0; i < nodeDataPage; i++) FREE(nodeDataPages[i]); + FREE(nodeDataPages); + memOut = 1; + return; + } + /* reset page index */ + nodeDataPageIndex = 0; + return; + +} /* end of ResizeNodeDataPages */ + + +/**Function******************************************************************** + + Synopsis [Resize the number of pages allocated to store the minterm + counts. ] + + Description [Resize the number of pages allocated to store the minterm + counts. The procedure moves the counter to the next page when the + end of the page is reached and allocates new pages when necessary.] + + SideEffects [Changes the size of minterm pages, page, page index, maximum + number of pages freeing stuff in case of memory out. ] + + SeeAlso [] + +******************************************************************************/ +static void +ResizeCountMintermPages( + ) +{ + int i; + double **newMintermPages; + + page++; + /* If the current page index is larger than the number of pages + * allocated, allocate a new page array. Page numbers are incremented by + * INITIAL_PAGES + */ + if (page == maxPages) { + newMintermPages = ALLOC(double *,maxPages + INITIAL_PAGES); + if (newMintermPages == NULL) { + for (i = 0; i < page; i++) FREE(mintermPages[i]); + FREE(mintermPages); + memOut = 1; + return; + } else { + for (i = 0; i < maxPages; i++) { + newMintermPages[i] = mintermPages[i]; + } + /* Increase total page count */ + maxPages += INITIAL_PAGES; + FREE(mintermPages); + mintermPages = newMintermPages; + } + } + /* Allocate a new page */ + currentMintermPage = mintermPages[page] = ALLOC(double,pageSize); + if (currentMintermPage == NULL) { + for (i = 0; i < page; i++) FREE(mintermPages[i]); + FREE(mintermPages); + memOut = 1; + return; + } + /* reset page index */ + pageIndex = 0; + return; + +} /* end of ResizeCountMintermPages */ + + +/**Function******************************************************************** + + Synopsis [Resize the number of pages allocated to store the node counts.] + + Description [Resize the number of pages allocated to store the node counts. + The procedure moves the counter to the next page when the end of + the page is reached and allocates new pages when necessary.] + + SideEffects [Changes the size of pages, page, page index, maximum + number of pages freeing stuff in case of memory out.] + + SeeAlso [] + +******************************************************************************/ +static void +ResizeCountNodePages( + ) +{ + int i; + int **newNodePages; + + page++; + + /* If the current page index is larger than the number of pages + * allocated, allocate a new page array. The number of pages is incremented + * by INITIAL_PAGES. + */ + if (page == maxPages) { + newNodePages = ALLOC(int *,maxPages + INITIAL_PAGES); + if (newNodePages == NULL) { + for (i = 0; i < page; i++) FREE(nodePages[i]); + FREE(nodePages); + for (i = 0; i < page; i++) FREE(lightNodePages[i]); + FREE(lightNodePages); + memOut = 1; + return; + } else { + for (i = 0; i < maxPages; i++) { + newNodePages[i] = nodePages[i]; + } + FREE(nodePages); + nodePages = newNodePages; + } + + newNodePages = ALLOC(int *,maxPages + INITIAL_PAGES); + if (newNodePages == NULL) { + for (i = 0; i < page; i++) FREE(nodePages[i]); + FREE(nodePages); + for (i = 0; i < page; i++) FREE(lightNodePages[i]); + FREE(lightNodePages); + memOut = 1; + return; + } else { + for (i = 0; i < maxPages; i++) { + newNodePages[i] = lightNodePages[i]; + } + FREE(lightNodePages); + lightNodePages = newNodePages; + } + /* Increase total page count */ + maxPages += INITIAL_PAGES; + } + /* Allocate a new page */ + currentNodePage = nodePages[page] = ALLOC(int,pageSize); + if (currentNodePage == NULL) { + for (i = 0; i < page; i++) FREE(nodePages[i]); + FREE(nodePages); + for (i = 0; i < page; i++) FREE(lightNodePages[i]); + FREE(lightNodePages); + memOut = 1; + return; + } + /* Allocate a new page */ + currentLightNodePage = lightNodePages[page] = ALLOC(int,pageSize); + if (currentLightNodePage == NULL) { + for (i = 0; i <= page; i++) FREE(nodePages[i]); + FREE(nodePages); + for (i = 0; i < page; i++) FREE(lightNodePages[i]); + FREE(lightNodePages); + memOut = 1; + return; + } + /* reset page index */ + pageIndex = 0; + return; + +} /* end of ResizeCountNodePages */ + + +/**Function******************************************************************** + + Synopsis [Recursively counts minterms of each node in the DAG.] + + Description [Recursively counts minterms of each node in the DAG. + Similar to the cuddCountMintermAux which recursively counts the + number of minterms for the dag rooted at each node in terms of the + total number of variables (max). This procedure creates the node + data structure and stores the minterm count as part of the node + data structure. ] + + SideEffects [Creates structures of type node quality and fills the st_table] + + SeeAlso [SubsetCountMinterm] + +******************************************************************************/ +static double +SubsetCountMintermAux( + DdNode * node /* function to analyze */, + double max /* number of minterms of constant 1 */, + st_table * table /* visitedTable table */) +{ + + DdNode *N,*Nv,*Nnv; /* nodes to store cofactors */ + double min,*pmin; /* minterm count */ + double min1, min2; /* minterm count */ + NodeData_t *dummy; + NodeData_t *newEntry; + int i; + +#ifdef DEBUG + num_calls++; +#endif + + /* Constant case */ + if (Cudd_IsConstant(node)) { + if (node == zero) { + return(0.0); + } else { + return(max); + } + } else { + + /* check if entry for this node exists */ + if (st_lookup(table,(char *)node, (char **)&dummy)) { + min = *(dummy->mintermPointer); + return(min); + } + + /* Make the node regular to extract cofactors */ + N = Cudd_Regular(node); + + /* store the cofactors */ + Nv = Cudd_T(N); + Nnv = Cudd_E(N); + + Nv = Cudd_NotCond(Nv, Cudd_IsComplement(node)); + Nnv = Cudd_NotCond(Nnv, Cudd_IsComplement(node)); + + min1 = SubsetCountMintermAux(Nv, max,table)/2.0; + if (memOut) return(0.0); + min2 = SubsetCountMintermAux(Nnv,max,table)/2.0; + if (memOut) return(0.0); + min = (min1+min2); + + /* if page index is at the bottom, then create a new page */ + if (pageIndex == pageSize) ResizeCountMintermPages(); + if (memOut) { + for (i = 0; i <= nodeDataPage; i++) FREE(nodeDataPages[i]); + FREE(nodeDataPages); + st_free_table(table); + return(0.0); + } + + /* point to the correct location in the page */ + pmin = currentMintermPage+pageIndex; + pageIndex++; + + /* store the minterm count of this node in the page */ + *pmin = min; + + /* Note I allocate the struct here. Freeing taken care of later */ + if (nodeDataPageIndex == nodeDataPageSize) ResizeNodeDataPages(); + if (memOut) { + for (i = 0; i <= page; i++) FREE(mintermPages[i]); + FREE(mintermPages); + st_free_table(table); + return(0.0); + } + + newEntry = currentNodeDataPage + nodeDataPageIndex; + nodeDataPageIndex++; + + /* points to the correct location in the page */ + newEntry->mintermPointer = pmin; + /* initialize this field of the Node Quality structure */ + newEntry->nodesPointer = NULL; + + /* insert entry for the node in the table */ + if (st_insert(table,(char *)node, (char *)newEntry) == ST_OUT_OF_MEM) { + memOut = 1; + for (i = 0; i <= page; i++) FREE(mintermPages[i]); + FREE(mintermPages); + for (i = 0; i <= nodeDataPage; i++) FREE(nodeDataPages[i]); + FREE(nodeDataPages); + st_free_table(table); + return(0.0); + } + return(min); + } + +} /* end of SubsetCountMintermAux */ + + +/**Function******************************************************************** + + Synopsis [Counts minterms of each node in the DAG] + + Description [Counts minterms of each node in the DAG. Similar to the + Cudd_CountMinterm procedure except this returns the minterm count for + all the nodes in the bdd in an st_table.] + + SideEffects [none] + + SeeAlso [SubsetCountMintermAux] + +******************************************************************************/ +static st_table * +SubsetCountMinterm( + DdNode * node /* function to be analyzed */, + int nvars /* number of variables node depends on */) +{ + st_table *table; + double num; + int i; + + +#ifdef DEBUG + num_calls = 0; +#endif + + max = pow(2.0,(double) nvars); + table = st_init_table(st_ptrcmp,st_ptrhash); + if (table == NULL) goto OUT_OF_MEM; + maxPages = INITIAL_PAGES; + mintermPages = ALLOC(double *,maxPages); + if (mintermPages == NULL) { + st_free_table(table); + goto OUT_OF_MEM; + } + page = 0; + currentMintermPage = ALLOC(double,pageSize); + mintermPages[page] = currentMintermPage; + if (currentMintermPage == NULL) { + FREE(mintermPages); + st_free_table(table); + goto OUT_OF_MEM; + } + pageIndex = 0; + maxNodeDataPages = INITIAL_PAGES; + nodeDataPages = ALLOC(NodeData_t *, maxNodeDataPages); + if (nodeDataPages == NULL) { + for (i = 0; i <= page ; i++) FREE(mintermPages[i]); + FREE(mintermPages); + st_free_table(table); + goto OUT_OF_MEM; + } + nodeDataPage = 0; + currentNodeDataPage = ALLOC(NodeData_t ,nodeDataPageSize); + nodeDataPages[nodeDataPage] = currentNodeDataPage; + if (currentNodeDataPage == NULL) { + for (i = 0; i <= page ; i++) FREE(mintermPages[i]); + FREE(mintermPages); + FREE(nodeDataPages); + st_free_table(table); + goto OUT_OF_MEM; + } + nodeDataPageIndex = 0; + + num = SubsetCountMintermAux(node,max,table); + if (memOut) goto OUT_OF_MEM; + return(table); + +OUT_OF_MEM: + memOut = 1; + return(NULL); + +} /* end of SubsetCountMinterm */ + + +/**Function******************************************************************** + + Synopsis [Recursively counts the number of nodes under the dag. + Also counts the number of nodes under the lighter child of + this node.] + + Description [Recursively counts the number of nodes under the dag. + Also counts the number of nodes under the lighter child of + this node. . Note that the same dag may be the lighter child of two + different nodes and have different counts. As with the minterm counts, + the node counts are stored in pages to be space efficient and the + address for these node counts are stored in an st_table associated + to each node. ] + + SideEffects [Updates the node data table with node counts] + + SeeAlso [SubsetCountNodes] + +******************************************************************************/ +static int +SubsetCountNodesAux( + DdNode * node /* current node */, + st_table * table /* table to update node count, also serves as visited table. */, + double max /* maximum number of variables */) +{ + int tval, eval, i; + DdNode *N, *Nv, *Nnv; + double minNv, minNnv; + NodeData_t *dummyN, *dummyNv, *dummyNnv, *dummyNBar; + int *pmin, *pminBar, *val; + + if ((node == NULL) || Cudd_IsConstant(node)) + return(0); + + /* if this node has been processed do nothing */ + if (st_lookup(table, (char *)node, (char **)&dummyN) == 1) { + val = dummyN->nodesPointer; + if (val != NULL) + return(0); + } else { + return(0); + } + + N = Cudd_Regular(node); + Nv = Cudd_T(N); + Nnv = Cudd_E(N); + + Nv = Cudd_NotCond(Nv, Cudd_IsComplement(node)); + Nnv = Cudd_NotCond(Nnv, Cudd_IsComplement(node)); + + /* find the minterm counts for the THEN and ELSE branches */ + if (Cudd_IsConstant(Nv)) { + if (Nv == zero) { + minNv = 0.0; + } else { + minNv = max; + } + } else { + if (st_lookup(table, (char *)Nv, (char **)&dummyNv) == 1) + minNv = *(dummyNv->mintermPointer); + else { + return(0); + } + } + if (Cudd_IsConstant(Nnv)) { + if (Nnv == zero) { + minNnv = 0.0; + } else { + minNnv = max; + } + } else { + if (st_lookup(table, (char *)Nnv, (char **)&dummyNnv) == 1) { + minNnv = *(dummyNnv->mintermPointer); + } + else { + return(0); + } + } + + + /* recur based on which has larger minterm, */ + if (minNv >= minNnv) { + tval = SubsetCountNodesAux(Nv, table, max); + if (memOut) return(0); + eval = SubsetCountNodesAux(Nnv, table, max); + if (memOut) return(0); + + /* store the node count of the lighter child. */ + if (pageIndex == pageSize) ResizeCountNodePages(); + if (memOut) { + for (i = 0; i <= page; i++) FREE(mintermPages[i]); + FREE(mintermPages); + for (i = 0; i <= nodeDataPage; i++) FREE(nodeDataPages[i]); + FREE(nodeDataPages); + st_free_table(table); + return(0); + } + pmin = currentLightNodePage + pageIndex; + *pmin = eval; /* Here the ELSE child is lighter */ + dummyN->lightChildNodesPointer = pmin; + + } else { + eval = SubsetCountNodesAux(Nnv, table, max); + if (memOut) return(0); + tval = SubsetCountNodesAux(Nv, table, max); + if (memOut) return(0); + + /* store the node count of the lighter child. */ + if (pageIndex == pageSize) ResizeCountNodePages(); + if (memOut) { + for (i = 0; i <= page; i++) FREE(mintermPages[i]); + FREE(mintermPages); + for (i = 0; i <= nodeDataPage; i++) FREE(nodeDataPages[i]); + FREE(nodeDataPages); + st_free_table(table); + return(0); + } + pmin = currentLightNodePage + pageIndex; + *pmin = tval; /* Here the THEN child is lighter */ + dummyN->lightChildNodesPointer = pmin; + + } + /* updating the page index for node count storage. */ + pmin = currentNodePage + pageIndex; + *pmin = tval + eval + 1; + dummyN->nodesPointer = pmin; + + /* pageIndex is parallel page index for count_nodes and count_lightNodes */ + pageIndex++; + + /* if this node has been reached first, it belongs to a heavier + branch. Its complement will be reached later on a lighter branch. + Hence the complement has zero node count. */ + + if (st_lookup(table, (char *)Cudd_Not(node), (char **)&dummyNBar) == 1) { + if (pageIndex == pageSize) ResizeCountNodePages(); + if (memOut) { + for (i = 0; i < page; i++) FREE(mintermPages[i]); + FREE(mintermPages); + for (i = 0; i < nodeDataPage; i++) FREE(nodeDataPages[i]); + FREE(nodeDataPages); + st_free_table(table); + return(0); + } + pminBar = currentLightNodePage + pageIndex; + *pminBar = 0; + dummyNBar->lightChildNodesPointer = pminBar; + /* The lighter child has less nodes than the parent. + * So if parent 0 then lighter child zero + */ + if (pageIndex == pageSize) ResizeCountNodePages(); + if (memOut) { + for (i = 0; i < page; i++) FREE(mintermPages[i]); + FREE(mintermPages); + for (i = 0; i < nodeDataPage; i++) FREE(nodeDataPages[i]); + FREE(nodeDataPages); + st_free_table(table); + return(0); + } + pminBar = currentNodePage + pageIndex; + *pminBar = 0; + dummyNBar->nodesPointer = pminBar ; /* maybe should point to zero */ + + pageIndex++; + } + return(*pmin); +} /*end of SubsetCountNodesAux */ + + +/**Function******************************************************************** + + Synopsis [Counts the nodes under the current node and its lighter child] + + Description [Counts the nodes under the current node and its lighter + child. Calls a recursive procedure to count the number of nodes of + a DAG rooted at a particular node and the number of nodes taken by its + lighter child.] + + SideEffects [None] + + SeeAlso [SubsetCountNodesAux] + +******************************************************************************/ +static int +SubsetCountNodes( + DdNode * node /* function to be analyzed */, + st_table * table /* node quality table */, + int nvars /* number of variables node depends on */) +{ + int num; + int i; + +#ifdef DEBUG + num_calls = 0; +#endif + + max = pow(2.0,(double) nvars); + maxPages = INITIAL_PAGES; + nodePages = ALLOC(int *,maxPages); + if (nodePages == NULL) { + goto OUT_OF_MEM; + } + + lightNodePages = ALLOC(int *,maxPages); + if (lightNodePages == NULL) { + for (i = 0; i <= page; i++) FREE(mintermPages[i]); + FREE(mintermPages); + for (i = 0; i <= nodeDataPage; i++) FREE(nodeDataPages[i]); + FREE(nodeDataPages); + FREE(nodePages); + goto OUT_OF_MEM; + } + + page = 0; + currentNodePage = nodePages[page] = ALLOC(int,pageSize); + if (currentNodePage == NULL) { + for (i = 0; i <= page; i++) FREE(mintermPages[i]); + FREE(mintermPages); + for (i = 0; i <= nodeDataPage; i++) FREE(nodeDataPages[i]); + FREE(nodeDataPages); + FREE(lightNodePages); + FREE(nodePages); + goto OUT_OF_MEM; + } + + currentLightNodePage = lightNodePages[page] = ALLOC(int,pageSize); + if (currentLightNodePage == NULL) { + for (i = 0; i <= page; i++) FREE(mintermPages[i]); + FREE(mintermPages); + for (i = 0; i <= nodeDataPage; i++) FREE(nodeDataPages[i]); + FREE(nodeDataPages); + FREE(currentNodePage); + FREE(lightNodePages); + FREE(nodePages); + goto OUT_OF_MEM; + } + + pageIndex = 0; + num = SubsetCountNodesAux(node,table,max); + if (memOut) goto OUT_OF_MEM; + return(num); + +OUT_OF_MEM: + memOut = 1; + return(0); + +} /* end of SubsetCountNodes */ + + +/**Function******************************************************************** + + Synopsis [Procedure to recursively store nodes that are retained in the subset.] + + Description [rocedure to recursively store nodes that are retained in the subset.] + + SideEffects [None] + + SeeAlso [StoreNodes] + +******************************************************************************/ +static void +StoreNodes( + st_table * storeTable, + DdManager * dd, + DdNode * node) +{ + char *dummy; + DdNode *N, *Nt, *Ne; + if (Cudd_IsConstant(dd)) { + return; + } + N = Cudd_Regular(node); + if (st_lookup(storeTable, (char *)N, (char **)&dummy)) { + return; + } + cuddRef(N); + if (st_insert(storeTable, (char *)N, NIL(char)) == ST_OUT_OF_MEM) { + fprintf(dd->err,"Something wrong, st_table insert failed\n"); + } + + Nt = Cudd_T(N); + Ne = Cudd_E(N); + + StoreNodes(storeTable, dd, Nt); + StoreNodes(storeTable, dd, Ne); + return; + +} + + +/**Function******************************************************************** + + Synopsis [Builds the subset BDD using the heavy branch method.] + + Description [The procedure carries out the building of the subset BDD + starting at the root. Using the three different counts labelling each node, + the procedure chooses the heavier branch starting from the root and keeps + track of the number of nodes it discards at each step, thus keeping count + of the size of the subset BDD dynamically. Once the threshold is satisfied, + the procedure then calls ITE to build the BDD.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static DdNode * +BuildSubsetBdd( + DdManager * dd /* DD manager */, + DdNode * node /* current node */, + int * size /* current size of the subset */, + st_table * visitedTable /* visited table storing all node data */, + int threshold, + st_table * storeTable, + st_table * approxTable) +{ + + DdNode *Nv, *Nnv, *N, *topv, *neW; + double minNv, minNnv; + NodeData_t *currNodeQual; + NodeData_t *currNodeQualT; + NodeData_t *currNodeQualE; + DdNode *ThenBranch, *ElseBranch; + unsigned int topid; + char *dummy; + +#ifdef DEBUG + num_calls++; +#endif + /*If the size of the subset is below the threshold, dont do + anything. */ + if ((*size) <= threshold) { + /* store nodes below this, so we can recombine if possible */ + StoreNodes(storeTable, dd, node); + return(node); + } + + if (Cudd_IsConstant(node)) + return(node); + + /* Look up minterm count for this node. */ + if (!st_lookup(visitedTable, (char *)node, (char **)&currNodeQual)) { + fprintf(dd->err, + "Something is wrong, ought to be in node quality table\n"); + } + + /* Get children. */ + N = Cudd_Regular(node); + Nv = Cudd_T(N); + Nnv = Cudd_E(N); + + /* complement if necessary */ + Nv = Cudd_NotCond(Nv, Cudd_IsComplement(node)); + Nnv = Cudd_NotCond(Nnv, Cudd_IsComplement(node)); + + if (!Cudd_IsConstant(Nv)) { + /* find out minterms and nodes contributed by then child */ + if (!st_lookup(visitedTable, (char *)Nv, + (char **)&currNodeQualT)) { + fprintf(dd->out,"Something wrong, couldnt find nodes in node quality table\n"); + dd->errorCode = CUDD_INTERNAL_ERROR; + return(NULL); + } + else { + minNv = *(((NodeData_t *)currNodeQualT)->mintermPointer); + } + } else { + if (Nv == zero) { + minNv = 0; + } else { + minNv = max; + } + } + if (!Cudd_IsConstant(Nnv)) { + /* find out minterms and nodes contributed by else child */ + if (!st_lookup(visitedTable, (char *)Nnv, (char **)&currNodeQualE)) { + fprintf(dd->out,"Something wrong, couldnt find nodes in node quality table\n"); + dd->errorCode = CUDD_INTERNAL_ERROR; + return(NULL); + } else { + minNnv = *(((NodeData_t *)currNodeQualE)->mintermPointer); + } + } else { + if (Nnv == zero) { + minNnv = 0; + } else { + minNnv = max; + } + } + + /* keep track of size of subset by subtracting the number of + * differential nodes contributed by lighter child + */ + *size = (*(size)) - (int)*(currNodeQual->lightChildNodesPointer); + if (minNv >= minNnv) { /*SubsetCountNodesAux procedure takes + the Then branch in case of a tie */ + + /* recur with the Then branch */ + ThenBranch = (DdNode *)BuildSubsetBdd(dd, Nv, size, + visitedTable, threshold, storeTable, approxTable); + if (ThenBranch == NULL) { + return(NULL); + } + cuddRef(ThenBranch); + /* The Else branch is either a node that already exists in the + * subset, or one whose approximation has been computed, or + * Zero. + */ + if (st_lookup(storeTable, (char *)Cudd_Regular(Nnv), (char **)&dummy)) { + ElseBranch = Nnv; + cuddRef(ElseBranch); + } else { + if (st_lookup(approxTable, (char *)Nnv, (char **)&dummy)) { + ElseBranch = (DdNode *)dummy; + cuddRef(ElseBranch); + } else { + ElseBranch = zero; + cuddRef(ElseBranch); + } + } + + } + else { + /* recur with the Else branch */ + ElseBranch = (DdNode *)BuildSubsetBdd(dd, Nnv, size, + visitedTable, threshold, storeTable, approxTable); + if (ElseBranch == NULL) { + return(NULL); + } + cuddRef(ElseBranch); + /* The Then branch is either a node that already exists in the + * subset, or one whose approximation has been computed, or + * Zero. + */ + if (st_lookup(storeTable, (char *)Cudd_Regular(Nv), (char **)&dummy)) { + ThenBranch = Nv; + cuddRef(ThenBranch); + } else { + if (st_lookup(approxTable, (char *)Nv, (char **)&dummy)) { + ThenBranch = (DdNode *)dummy; + cuddRef(ThenBranch); + } else { + ThenBranch = zero; + cuddRef(ThenBranch); + } + } + } + + /* construct the Bdd with the top variable and the two children */ + topid = Cudd_NodeReadIndex(N); + topv = Cudd_ReadVars(dd, topid); + cuddRef(topv); + neW = cuddBddIteRecur(dd, topv, ThenBranch, ElseBranch); + if (neW != NULL) { + cuddRef(neW); + } + Cudd_RecursiveDeref(dd, topv); + Cudd_RecursiveDeref(dd, ThenBranch); + Cudd_RecursiveDeref(dd, ElseBranch); + + + if (neW == NULL) + return(NULL); + else { + /* store this node in the store table */ + if (!st_lookup(storeTable, (char *)Cudd_Regular(neW), (char **)&dummy)) { + cuddRef(neW); + st_insert(storeTable, (char *)Cudd_Regular(neW), (char *)NIL(char)); + + } + /* store the approximation for this node */ + if (N != Cudd_Regular(neW)) { + if (st_lookup(approxTable, (char *)node, (char **)&dummy)) { + fprintf(dd->err, "This node should not be in the approximated table\n"); + } else { + cuddRef(neW); + st_insert(approxTable, (char *)node, (char *)neW); + } + } + cuddDeref(neW); + return(neW); + } +} /* end of BuildSubsetBdd */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddSubsetSP.c b/abc_with_bb_support/src/bdd/cudd/cuddSubsetSP.c new file mode 100644 index 000000000..de041b8d0 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddSubsetSP.c @@ -0,0 +1,1624 @@ +/**CFile*********************************************************************** + + FileName [cuddSubsetSP.c] + + PackageName [cudd] + + Synopsis [Procedure to subset the given BDD choosing the shortest paths + (largest cubes) in the BDD.] + + + Description [External procedures included in this module: +
          +
        • Cudd_SubsetShortPaths() +
        • Cudd_SupersetShortPaths() +
        + Internal procedures included in this module: +
          +
        • cuddSubsetShortPaths() +
        + Static procedures included in this module: +
          +
        • BuildSubsetBdd() +
        • CreatePathTable() +
        • AssessPathLength() +
        • CreateTopDist() +
        • CreateBotDist() +
        • ResizeNodeDistPages() +
        • ResizeQueuePages() +
        • stPathTableDdFree() +
        + ] + + SeeAlso [cuddSubsetHB.c] + + Author [Kavita Ravi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define DEFAULT_PAGE_SIZE 2048 /* page size to store the BFS queue element type */ +#define DEFAULT_NODE_DIST_PAGE_SIZE 2048 /* page sizesto store NodeDist_t type */ +#define MAXSHORTINT ((DdHalfWord) ~0) /* constant defined to store + * maximum distance of a node + * from the root or the + * constant + */ +#define INITIAL_PAGES 128 /* number of initial pages for the + * queue/NodeDist_t type */ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/* structure created to store subset results for each node and distances with + * odd and even parity of the node from the root and sink. Main data structure + * in this procedure. + */ +struct NodeDist{ + DdHalfWord oddTopDist; + DdHalfWord evenTopDist; + DdHalfWord oddBotDist; + DdHalfWord evenBotDist; + DdNode *regResult; + DdNode *compResult; +}; + +/* assorted information needed by the BuildSubsetBdd procedure. */ +struct AssortedInfo { + unsigned int maxpath; + int findShortestPath; + int thresholdReached; + st_table *maxpathTable; + int threshold; +}; + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +typedef struct NodeDist NodeDist_t; + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddSubsetSP.c,v 1.1.1.1 2003/02/24 22:23:53 wjiang Exp $"; +#endif + +#ifdef DD_DEBUG +static int numCalls; +static int hits; +static int thishit; +#endif + + +static int memOut; /* flag to indicate out of memory */ +static DdNode *zero, *one; /* constant functions */ + +static NodeDist_t **nodeDistPages; /* pointers to the pages */ +static int nodeDistPageIndex; /* index to next element */ +static int nodeDistPage; /* index to current page */ +static int nodeDistPageSize = DEFAULT_NODE_DIST_PAGE_SIZE; /* page size */ +static int maxNodeDistPages; /* number of page pointers */ +static NodeDist_t *currentNodeDistPage; /* current page */ + +static DdNode ***queuePages; /* pointers to the pages */ +static int queuePageIndex; /* index to next element */ +static int queuePage; /* index to current page */ +static int queuePageSize = DEFAULT_PAGE_SIZE; /* page size */ +static int maxQueuePages; /* number of page pointers */ +static DdNode **currentQueuePage; /* current page */ + + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static void ResizeNodeDistPages ARGS(()); +static void ResizeQueuePages ARGS(()); +static void CreateTopDist ARGS((st_table *pathTable, int parentPage, int parentQueueIndex, int topLen, DdNode **childPage, int childQueueIndex, int numParents, FILE *fp)); +static int CreateBotDist ARGS((DdNode *node, st_table *pathTable, unsigned int *pathLengthArray, FILE *fp)); +static st_table * CreatePathTable ARGS((DdNode *node, unsigned int *pathLengthArray, FILE *fp)); +static unsigned int AssessPathLength ARGS((unsigned int *pathLengthArray, int threshold, int numVars, unsigned int *excess, FILE *fp)); +static DdNode * BuildSubsetBdd ARGS((DdManager *dd, st_table *pathTable, DdNode *node, struct AssortedInfo *info, st_table *subsetNodeTable)); +static enum st_retval stPathTableDdFree ARGS((char *key, char *value, char *arg)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of Exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Extracts a dense subset from a BDD with the shortest paths + heuristic.] + + Description [Extracts a dense subset from a BDD. This procedure + tries to preserve the shortest paths of the input BDD, because they + give many minterms and contribute few nodes. This procedure may + increase the number of nodes in trying to create the subset or + reduce the number of nodes due to recombination as compared to the + original BDD. Hence the threshold may not be strictly adhered to. In + practice, recombination overshadows the increase in the number of + nodes and results in small BDDs as compared to the threshold. The + hardlimit specifies whether threshold needs to be strictly adhered + to. If it is set to 1, the procedure ensures that result is never + larger than the specified limit but may be considerably less than + the threshold. Returns a pointer to the BDD for the subset if + successful; NULL otherwise. The value for numVars should be as + close as possible to the size of the support of f for better + efficiency. However, it is safe to pass the value returned by + Cudd_ReadSize for numVars. If 0 is passed, then the value returned + by Cudd_ReadSize is used.] + + SideEffects [None] + + SeeAlso [Cudd_SupersetShortPaths Cudd_SubsetHeavyBranch Cudd_ReadSize] + +******************************************************************************/ +DdNode * +Cudd_SubsetShortPaths( + DdManager * dd /* manager */, + DdNode * f /* function to be subset */, + int numVars /* number of variables in the support of f */, + int threshold /* maximum number of nodes in the subset */, + int hardlimit /* flag: 1 if threshold is a hard limit */) +{ + DdNode *subset; + + memOut = 0; + do { + dd->reordered = 0; + subset = cuddSubsetShortPaths(dd, f, numVars, threshold, hardlimit); + } while((dd->reordered ==1) && (!memOut)); + + return(subset); + +} /* end of Cudd_SubsetShortPaths */ + + +/**Function******************************************************************** + + Synopsis [Extracts a dense superset from a BDD with the shortest paths + heuristic.] + + Description [Extracts a dense superset from a BDD. The procedure is + identical to the subset procedure except for the fact that it + receives the complement of the given function. Extracting the subset + of the complement function is equivalent to extracting the superset + of the function. This procedure tries to preserve the shortest + paths of the complement BDD, because they give many minterms and + contribute few nodes. This procedure may increase the number of + nodes in trying to create the superset or reduce the number of nodes + due to recombination as compared to the original BDD. Hence the + threshold may not be strictly adhered to. In practice, recombination + overshadows the increase in the number of nodes and results in small + BDDs as compared to the threshold. The hardlimit specifies whether + threshold needs to be strictly adhered to. If it is set to 1, the + procedure ensures that result is never larger than the specified + limit but may be considerably less than the threshold. Returns a + pointer to the BDD for the superset if successful; NULL + otherwise. The value for numVars should be as close as possible to + the size of the support of f for better efficiency. However, it is + safe to pass the value returned by Cudd_ReadSize for numVar. If 0 + is passed, then the value returned by Cudd_ReadSize is used.] + + SideEffects [None] + + SeeAlso [Cudd_SubsetShortPaths Cudd_SupersetHeavyBranch Cudd_ReadSize] + +******************************************************************************/ +DdNode * +Cudd_SupersetShortPaths( + DdManager * dd /* manager */, + DdNode * f /* function to be superset */, + int numVars /* number of variables in the support of f */, + int threshold /* maximum number of nodes in the subset */, + int hardlimit /* flag: 1 if threshold is a hard limit */) +{ + DdNode *subset, *g; + + g = Cudd_Not(f); + memOut = 0; + do { + dd->reordered = 0; + subset = cuddSubsetShortPaths(dd, g, numVars, threshold, hardlimit); + } while((dd->reordered ==1) && (!memOut)); + + return(Cudd_NotCond(subset, (subset != NULL))); + +} /* end of Cudd_SupersetShortPaths */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [The outermost procedure to return a subset of the given BDD + with the shortest path lengths.] + + Description [The outermost procedure to return a subset of the given + BDD with the largest cubes. The path lengths are calculated, the maximum + allowable path length is determined and the number of nodes of this + path length that can be used to build a subset. If the threshold is + larger than the size of the original BDD, the original BDD is + returned. ] + + SideEffects [None] + + SeeAlso [Cudd_SubsetShortPaths] + +******************************************************************************/ +DdNode * +cuddSubsetShortPaths( + DdManager * dd /* DD manager */, + DdNode * f /* function to be subset */, + int numVars /* total number of variables in consideration */, + int threshold /* maximum number of nodes allowed in the subset */, + int hardlimit /* flag determining whether thershold should be respected strictly */) +{ + st_table *pathTable; + DdNode *N, *subset; + + unsigned int *pathLengthArray; + unsigned int maxpath, oddLen, evenLen, pathLength, *excess; + int i; + NodeDist_t *nodeStat; + struct AssortedInfo *info; + st_table *subsetNodeTable; + + one = DD_ONE(dd); + zero = Cudd_Not(one); + + if (numVars == 0) { + /* set default value */ + numVars = Cudd_ReadSize(dd); + } + + if (threshold > numVars) { + threshold = threshold - numVars; + } + if (f == NULL) { + fprintf(dd->err, "Cannot partition, nil object\n"); + dd->errorCode = CUDD_INVALID_ARG; + return(NULL); + } + if (Cudd_IsConstant(f)) + return (f); + + pathLengthArray = ALLOC(unsigned int, numVars+1); + for (i = 0; i < numVars+1; i++) pathLengthArray[i] = 0; + + +#ifdef DD_DEBUG + numCalls = 0; +#endif + + pathTable = CreatePathTable(f, pathLengthArray, dd->err); + + if ((pathTable == NULL) || (memOut)) { + if (pathTable != NULL) + st_free_table(pathTable); + FREE(pathLengthArray); + return (NIL(DdNode)); + } + + excess = ALLOC(unsigned int, 1); + *excess = 0; + maxpath = AssessPathLength(pathLengthArray, threshold, numVars, excess, + dd->err); + + if (maxpath != (unsigned) (numVars + 1)) { + + info = ALLOC(struct AssortedInfo, 1); + info->maxpath = maxpath; + info->findShortestPath = 0; + info->thresholdReached = *excess; + info->maxpathTable = st_init_table(st_ptrcmp, st_ptrhash); + info->threshold = threshold; + +#ifdef DD_DEBUG + (void) fprintf(dd->out, "Path length array\n"); + for (i = 0; i < (numVars+1); i++) { + if (pathLengthArray[i]) + (void) fprintf(dd->out, "%d ",i); + } + (void) fprintf(dd->out, "\n"); + for (i = 0; i < (numVars+1); i++) { + if (pathLengthArray[i]) + (void) fprintf(dd->out, "%d ",pathLengthArray[i]); + } + (void) fprintf(dd->out, "\n"); + (void) fprintf(dd->out, "Maxpath = %d, Thresholdreached = %d\n", + maxpath, info->thresholdReached); +#endif + + N = Cudd_Regular(f); + if (!st_lookup(pathTable, (char *)N, (char **)&nodeStat)) { + fprintf(dd->err, "Something wrong, root node must be in table\n"); + dd->errorCode = CUDD_INTERNAL_ERROR; + return(NULL); + } else { + if ((nodeStat->oddTopDist != MAXSHORTINT) && + (nodeStat->oddBotDist != MAXSHORTINT)) + oddLen = (nodeStat->oddTopDist + nodeStat->oddBotDist); + else + oddLen = MAXSHORTINT; + + if ((nodeStat->evenTopDist != MAXSHORTINT) && + (nodeStat->evenBotDist != MAXSHORTINT)) + evenLen = (nodeStat->evenTopDist +nodeStat->evenBotDist); + else + evenLen = MAXSHORTINT; + + pathLength = (oddLen <= evenLen) ? oddLen : evenLen; + if (pathLength > maxpath) { + (void) fprintf(dd->err, "All computations are bogus, since root has path length greater than max path length within threshold %d, %d\n", maxpath, pathLength); + dd->errorCode = CUDD_INTERNAL_ERROR; + return(NULL); + } + } + +#ifdef DD_DEBUG + numCalls = 0; + hits = 0; + thishit = 0; +#endif + /* initialize a table to store computed nodes */ + if (hardlimit) { + subsetNodeTable = st_init_table(st_ptrcmp, st_ptrhash); + } else { + subsetNodeTable = NIL(st_table); + } + subset = BuildSubsetBdd(dd, pathTable, f, info, subsetNodeTable); + if (subset != NULL) { + cuddRef(subset); + } + /* record the number of times a computed result for a node is hit */ + +#ifdef DD_DEBUG + (void) fprintf(dd->out, "Hits = %d, New==Node = %d, NumCalls = %d\n", + hits, thishit, numCalls); +#endif + + if (subsetNodeTable != NIL(st_table)) { + st_free_table(subsetNodeTable); + } + st_free_table(info->maxpathTable); + st_foreach(pathTable, stPathTableDdFree, (char *)dd); + + FREE(info); + + } else {/* if threshold larger than size of dd */ + subset = f; + cuddRef(subset); + } + FREE(excess); + st_free_table(pathTable); + FREE(pathLengthArray); + for (i = 0; i <= nodeDistPage; i++) FREE(nodeDistPages[i]); + FREE(nodeDistPages); + +#ifdef DD_DEBUG + /* check containment of subset in f */ + if (subset != NULL) { + DdNode *check; + check = Cudd_bddIteConstant(dd, subset, f, one); + if (check != one) { + (void) fprintf(dd->err, "Wrong partition\n"); + dd->errorCode = CUDD_INTERNAL_ERROR; + return(NULL); + } + } +#endif + + if (subset != NULL) { + cuddDeref(subset); + return(subset); + } else { + return(NULL); + } + +} /* end of cuddSubsetShortPaths */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Resize the number of pages allocated to store the distances + related to each node.] + + Description [Resize the number of pages allocated to store the distances + related to each node. The procedure moves the counter to the + next page when the end of the page is reached and allocates new + pages when necessary. ] + + SideEffects [Changes the size of pages, page, page index, maximum + number of pages freeing stuff in case of memory out. ] + + SeeAlso [] + +******************************************************************************/ +static void +ResizeNodeDistPages( + ) +{ + int i; + NodeDist_t **newNodeDistPages; + + /* move to next page */ + nodeDistPage++; + + /* If the current page index is larger than the number of pages + * allocated, allocate a new page array. Page numbers are incremented by + * INITIAL_PAGES + */ + if (nodeDistPage == maxNodeDistPages) { + newNodeDistPages = ALLOC(NodeDist_t *,maxNodeDistPages + INITIAL_PAGES); + if (newNodeDistPages == NULL) { + for (i = 0; i < nodeDistPage; i++) FREE(nodeDistPages[i]); + FREE(nodeDistPages); + memOut = 1; + return; + } else { + for (i = 0; i < maxNodeDistPages; i++) { + newNodeDistPages[i] = nodeDistPages[i]; + } + /* Increase total page count */ + maxNodeDistPages += INITIAL_PAGES; + FREE(nodeDistPages); + nodeDistPages = newNodeDistPages; + } + } + /* Allocate a new page */ + currentNodeDistPage = nodeDistPages[nodeDistPage] = ALLOC(NodeDist_t, + nodeDistPageSize); + if (currentNodeDistPage == NULL) { + for (i = 0; i < nodeDistPage; i++) FREE(nodeDistPages[i]); + FREE(nodeDistPages); + memOut = 1; + return; + } + /* reset page index */ + nodeDistPageIndex = 0; + return; + +} /* end of ResizeNodeDistPages */ + + +/**Function******************************************************************** + + Synopsis [Resize the number of pages allocated to store nodes in the BFS + traversal of the Bdd .] + + Description [Resize the number of pages allocated to store nodes in the BFS + traversal of the Bdd. The procedure moves the counter to the + next page when the end of the page is reached and allocates new + pages when necessary.] + + SideEffects [Changes the size of pages, page, page index, maximum + number of pages freeing stuff in case of memory out. ] + + SeeAlso [] + +******************************************************************************/ +static void +ResizeQueuePages( + ) +{ + int i; + DdNode ***newQueuePages; + + queuePage++; + /* If the current page index is larger than the number of pages + * allocated, allocate a new page array. Page numbers are incremented by + * INITIAL_PAGES + */ + if (queuePage == maxQueuePages) { + newQueuePages = ALLOC(DdNode **,maxQueuePages + INITIAL_PAGES); + if (newQueuePages == NULL) { + for (i = 0; i < queuePage; i++) FREE(queuePages[i]); + FREE(queuePages); + memOut = 1; + return; + } else { + for (i = 0; i < maxQueuePages; i++) { + newQueuePages[i] = queuePages[i]; + } + /* Increase total page count */ + maxQueuePages += INITIAL_PAGES; + FREE(queuePages); + queuePages = newQueuePages; + } + } + /* Allocate a new page */ + currentQueuePage = queuePages[queuePage] = ALLOC(DdNode *,queuePageSize); + if (currentQueuePage == NULL) { + for (i = 0; i < queuePage; i++) FREE(queuePages[i]); + FREE(queuePages); + memOut = 1; + return; + } + /* reset page index */ + queuePageIndex = 0; + return; + +} /* end of ResizeQueuePages */ + + +/**Function******************************************************************** + + Synopsis [ Labels each node with its shortest distance from the root] + + Description [ Labels each node with its shortest distance from the root. + This is done in a BFS search of the BDD. The nodes are processed + in a queue implemented as pages(array) to reduce memory fragmentation. + An entry is created for each node visited. The distance from the root + to the node with the corresponding parity is updated. The procedure + is called recursively each recusion level handling nodes at a given + level from the root.] + + + SideEffects [Creates entries in the pathTable] + + SeeAlso [CreatePathTable CreateBotDist] + +******************************************************************************/ +static void +CreateTopDist( + st_table * pathTable /* hast table to store path lengths */, + int parentPage /* the pointer to the page on which the first parent in the queue is to be found. */, + int parentQueueIndex /* pointer to the first parent on the page */, + int topLen /* current distance from the root */, + DdNode ** childPage /* pointer to the page on which the first child is to be added. */, + int childQueueIndex /* pointer to the first child */, + int numParents /* number of parents to process in this recursive call */, + FILE *fp /* where to write messages */) +{ + NodeDist_t *nodeStat; + DdNode *N, *Nv, *Nnv, *node, *child, *regChild; + int i; + int processingDone, childrenCount; + +#ifdef DD_DEBUG + numCalls++; + + /* assume this procedure comes in with only the root node*/ + /* set queue index to the next available entry for addition */ + /* set queue page to page of addition */ + if ((queuePages[parentPage] == childPage) && (parentQueueIndex == + childQueueIndex)) { + fprintf(fp, "Should not happen that they are equal\n"); + } + assert(queuePageIndex == childQueueIndex); + assert(currentQueuePage == childPage); +#endif + /* number children added to queue is initialized , needed for + * numParents in the next call + */ + childrenCount = 0; + /* process all the nodes in this level */ + while (numParents) { + numParents--; + if (parentQueueIndex == queuePageSize) { + parentPage++; + parentQueueIndex = 0; + } + /* a parent to process */ + node = *(queuePages[parentPage] + parentQueueIndex); + parentQueueIndex++; + /* get its children */ + N = Cudd_Regular(node); + Nv = Cudd_T(N); + Nnv = Cudd_E(N); + + Nv = Cudd_NotCond(Nv, Cudd_IsComplement(node)); + Nnv = Cudd_NotCond(Nnv, Cudd_IsComplement(node)); + + processingDone = 2; + while (processingDone) { + /* processing the THEN and the ELSE children, the THEN + * child first + */ + if (processingDone == 2) { + child = Nv; + } else { + child = Nnv; + } + + regChild = Cudd_Regular(child); + /* dont process if the child is a constant */ + if (!Cudd_IsConstant(child)) { + /* check is already visited, if not add a new entry in + * the path Table + */ + if (!st_lookup(pathTable, (char *)regChild, (char **)&nodeStat)) { + /* if not in table, has never been visited */ + /* create entry for table */ + if (nodeDistPageIndex == nodeDistPageSize) + ResizeNodeDistPages(); + if (memOut) { + for (i = 0; i <= queuePage; i++) FREE(queuePages[i]); + FREE(queuePages); + st_free_table(pathTable); + return; + } + /* New entry for child in path Table is created here */ + nodeStat = currentNodeDistPage + nodeDistPageIndex; + nodeDistPageIndex++; + + /* Initialize fields of the node data */ + nodeStat->oddTopDist = MAXSHORTINT; + nodeStat->evenTopDist = MAXSHORTINT; + nodeStat->evenBotDist = MAXSHORTINT; + nodeStat->oddBotDist = MAXSHORTINT; + nodeStat->regResult = NULL; + nodeStat->compResult = NULL; + /* update the table entry element, the distance keeps + * track of the parity of the path from the root + */ + if (Cudd_IsComplement(child)) { + nodeStat->oddTopDist = (DdHalfWord) topLen + 1; + } else { + nodeStat->evenTopDist = (DdHalfWord) topLen + 1; + } + + /* insert entry element for child in the table */ + if (st_insert(pathTable, (char *)regChild, + (char *)nodeStat) == ST_OUT_OF_MEM) { + memOut = 1; + for (i = 0; i <= nodeDistPage; i++) + FREE(nodeDistPages[i]); + FREE(nodeDistPages); + for (i = 0; i <= queuePage; i++) FREE(queuePages[i]); + FREE(queuePages); + st_free_table(pathTable); + return; + } + + /* Create list element for this child to process its children. + * If this node has been processed already, then it appears + * in the path table and hence is never added to the list + * again. + */ + + if (queuePageIndex == queuePageSize) ResizeQueuePages(); + if (memOut) { + for (i = 0; i <= nodeDistPage; i++) + FREE(nodeDistPages[i]); + FREE(nodeDistPages); + st_free_table(pathTable); + return; + } + *(currentQueuePage + queuePageIndex) = child; + queuePageIndex++; + + childrenCount++; + } else { + /* if not been met in a path with this parity before */ + /* put in list */ + if (((Cudd_IsComplement(child)) && (nodeStat->oddTopDist == + MAXSHORTINT)) || ((!Cudd_IsComplement(child)) && + (nodeStat->evenTopDist == MAXSHORTINT))) { + + if (queuePageIndex == queuePageSize) ResizeQueuePages(); + if (memOut) { + for (i = 0; i <= nodeDistPage; i++) + FREE(nodeDistPages[i]); + FREE(nodeDistPages); + st_free_table(pathTable); + return; + + } + *(currentQueuePage + queuePageIndex) = child; + queuePageIndex++; + + /* update the distance with the appropriate parity */ + if (Cudd_IsComplement(child)) { + nodeStat->oddTopDist = (DdHalfWord) topLen + 1; + } else { + nodeStat->evenTopDist = (DdHalfWord) topLen + 1; + } + childrenCount++; + } + + } /* end of else (not found in st_table) */ + } /*end of if Not constant child */ + processingDone--; + } /*end of while processing Nv, Nnv */ + } /*end of while numParents */ + +#ifdef DD_DEBUG + assert(queuePages[parentPage] == childPage); + assert(parentQueueIndex == childQueueIndex); +#endif + + if (childrenCount != 0) { + topLen++; + childPage = currentQueuePage; + childQueueIndex = queuePageIndex; + CreateTopDist(pathTable, parentPage, parentQueueIndex, topLen, + childPage, childQueueIndex, childrenCount, fp); + } + + return; + +} /* end of CreateTopDist */ + + +/**Function******************************************************************** + + Synopsis [ Labels each node with the shortest distance from the constant.] + + Description [Labels each node with the shortest distance from the constant. + This is done in a DFS search of the BDD. Each node has an odd + and even parity distance from the sink (since there exists paths to both + zero and one) which is less than MAXSHORTINT. At each node these distances + are updated using the minimum distance of its children from the constant. + SInce now both the length from the root and child is known, the minimum path + length(length of the shortest path between the root and the constant that + this node lies on) of this node can be calculated and used to update the + pathLengthArray] + + SideEffects [Updates Path Table and path length array] + + SeeAlso [CreatePathTable CreateTopDist AssessPathLength] + +******************************************************************************/ +static int +CreateBotDist( + DdNode * node /* current node */, + st_table * pathTable /* path table with path lengths */, + unsigned int * pathLengthArray /* array that stores number of nodes belonging to a particular path length. */, + FILE *fp /* where to write messages */) +{ + DdNode *N, *Nv, *Nnv; + DdNode *realChild; + DdNode *child, *regChild; + NodeDist_t *nodeStat, *nodeStatChild; + unsigned int oddLen, evenLen, pathLength; + DdHalfWord botDist; + int processingDone; + + if (Cudd_IsConstant(node)) + return(1); + N = Cudd_Regular(node); + /* each node has one table entry */ + /* update as you go down the min dist of each node from + the root in each (odd and even) parity */ + if (!st_lookup(pathTable, (char *)N, (char **)&nodeStat)) { + fprintf(fp, "Something wrong, the entry doesn't exist\n"); + return(0); + } + + /* compute length of odd parity distances */ + if ((nodeStat->oddTopDist != MAXSHORTINT) && + (nodeStat->oddBotDist != MAXSHORTINT)) + oddLen = (nodeStat->oddTopDist + nodeStat->oddBotDist); + else + oddLen = MAXSHORTINT; + + /* compute length of even parity distances */ + if (!((nodeStat->evenTopDist == MAXSHORTINT) || + (nodeStat->evenBotDist == MAXSHORTINT))) + evenLen = (nodeStat->evenTopDist +nodeStat->evenBotDist); + else + evenLen = MAXSHORTINT; + + /* assign pathlength to minimum of the two */ + pathLength = (oddLen <= evenLen) ? oddLen : evenLen; + + Nv = Cudd_T(N); + Nnv = Cudd_E(N); + + /* process each child */ + processingDone = 0; + while (processingDone != 2) { + if (!processingDone) { + child = Nv; + } else { + child = Nnv; + } + + realChild = Cudd_NotCond(child, Cudd_IsComplement(node)); + regChild = Cudd_Regular(child); + if (Cudd_IsConstant(realChild)) { + /* Found a minterm; count parity and shortest distance + ** from the constant. + */ + if (Cudd_IsComplement(child)) + nodeStat->oddBotDist = 1; + else + nodeStat->evenBotDist = 1; + } else { + /* If node not in table, recur. */ + if (!st_lookup(pathTable, (char *) regChild, + (char **)&nodeStatChild)) { + fprintf(fp, "Something wrong, node in table should have been created in top dist proc.\n"); + return(0); + } + + if (nodeStatChild->oddBotDist == MAXSHORTINT) { + if (nodeStatChild->evenBotDist == MAXSHORTINT) { + if (!CreateBotDist(realChild, pathTable, pathLengthArray, fp)) + return(0); + } else { + fprintf(fp, "Something wrong, both bot nodeStats should be there\n"); + return(0); + } + } + + /* Update shortest distance from the constant depending on + ** parity. */ + + if (Cudd_IsComplement(child)) { + /* If parity on the edge then add 1 to even distance + ** of child to get odd parity distance and add 1 to + ** odd distance of child to get even parity + ** distance. Change distance of current node only if + ** the calculated distance is less than existing + ** distance. */ + if (nodeStatChild->oddBotDist != MAXSHORTINT) + botDist = nodeStatChild->oddBotDist + 1; + else + botDist = MAXSHORTINT; + if (nodeStat->evenBotDist > botDist ) + nodeStat->evenBotDist = botDist; + + if (nodeStatChild->evenBotDist != MAXSHORTINT) + botDist = nodeStatChild->evenBotDist + 1; + else + botDist = MAXSHORTINT; + if (nodeStat->oddBotDist > botDist) + nodeStat->oddBotDist = botDist; + + } else { + /* If parity on the edge then add 1 to even distance + ** of child to get even parity distance and add 1 to + ** odd distance of child to get odd parity distance. + ** Change distance of current node only if the + ** calculated distance is lesser than existing + ** distance. */ + if (nodeStatChild->evenBotDist != MAXSHORTINT) + botDist = nodeStatChild->evenBotDist + 1; + else + botDist = MAXSHORTINT; + if (nodeStat->evenBotDist > botDist) + nodeStat->evenBotDist = botDist; + + if (nodeStatChild->oddBotDist != MAXSHORTINT) + botDist = nodeStatChild->oddBotDist + 1; + else + botDist = MAXSHORTINT; + if (nodeStat->oddBotDist > botDist) + nodeStat->oddBotDist = botDist; + } + } /* end of else (if not constant child ) */ + processingDone++; + } /* end of while processing Nv, Nnv */ + + /* Compute shortest path length on the fly. */ + if ((nodeStat->oddTopDist != MAXSHORTINT) && + (nodeStat->oddBotDist != MAXSHORTINT)) + oddLen = (nodeStat->oddTopDist + nodeStat->oddBotDist); + else + oddLen = MAXSHORTINT; + + if ((nodeStat->evenTopDist != MAXSHORTINT) && + (nodeStat->evenBotDist != MAXSHORTINT)) + evenLen = (nodeStat->evenTopDist +nodeStat->evenBotDist); + else + evenLen = MAXSHORTINT; + + /* Update path length array that has number of nodes of a particular + ** path length. */ + if (oddLen < pathLength ) { + if (pathLength != MAXSHORTINT) + pathLengthArray[pathLength]--; + if (oddLen != MAXSHORTINT) + pathLengthArray[oddLen]++; + pathLength = oddLen; + } + if (evenLen < pathLength ) { + if (pathLength != MAXSHORTINT) + pathLengthArray[pathLength]--; + if (evenLen != MAXSHORTINT) + pathLengthArray[evenLen]++; + } + + return(1); + +} /*end of CreateBotDist */ + + +/**Function******************************************************************** + + Synopsis [ The outer procedure to label each node with its shortest + distance from the root and constant] + + Description [ The outer procedure to label each node with its shortest + distance from the root and constant. Calls CreateTopDist and CreateBotDist. + The basis for computing the distance between root and constant is that + the distance may be the sum of even distances from the node to the root + and constant or the sum of odd distances from the node to the root and + constant. Both CreateTopDist and CreateBotDist create the odd and + even parity distances from the root and constant respectively.] + + SideEffects [None] + + SeeAlso [CreateTopDist CreateBotDist] + +******************************************************************************/ +static st_table * +CreatePathTable( + DdNode * node /* root of function */, + unsigned int * pathLengthArray /* array of path lengths to store nodes labeled with the various path lengths */, + FILE *fp /* where to write messages */) +{ + + st_table *pathTable; + NodeDist_t *nodeStat; + DdHalfWord topLen; + DdNode *N; + int i, numParents; + int insertValue; + DdNode **childPage; + int parentPage; + int childQueueIndex, parentQueueIndex; + + /* Creating path Table for storing data about nodes */ + pathTable = st_init_table(st_ptrcmp,st_ptrhash); + + /* initializing pages for info about each node */ + maxNodeDistPages = INITIAL_PAGES; + nodeDistPages = ALLOC(NodeDist_t *, maxNodeDistPages); + if (nodeDistPages == NULL) { + goto OUT_OF_MEM; + } + nodeDistPage = 0; + currentNodeDistPage = nodeDistPages[nodeDistPage] = + ALLOC(NodeDist_t, nodeDistPageSize); + if (currentNodeDistPage == NULL) { + for (i = 0; i <= nodeDistPage; i++) FREE(nodeDistPages[i]); + FREE(nodeDistPages); + goto OUT_OF_MEM; + } + nodeDistPageIndex = 0; + + /* Initializing pages for the BFS search queue, implemented as an array. */ + maxQueuePages = INITIAL_PAGES; + queuePages = ALLOC(DdNode **, maxQueuePages); + if (queuePages == NULL) { + goto OUT_OF_MEM; + } + queuePage = 0; + currentQueuePage = queuePages[queuePage] = ALLOC(DdNode *, queuePageSize); + if (currentQueuePage == NULL) { + for (i = 0; i <= queuePage; i++) FREE(queuePages[i]); + FREE(queuePages); + goto OUT_OF_MEM; + } + queuePageIndex = 0; + + /* Enter the root node into the queue to start with. */ + parentPage = queuePage; + parentQueueIndex = queuePageIndex; + topLen = 0; + *(currentQueuePage + queuePageIndex) = node; + queuePageIndex++; + childPage = currentQueuePage; + childQueueIndex = queuePageIndex; + + N = Cudd_Regular(node); + + if (nodeDistPageIndex == nodeDistPageSize) ResizeNodeDistPages(); + if (memOut) { + for (i = 0; i <= nodeDistPage; i++) FREE(nodeDistPages[i]); + FREE(nodeDistPages); + for (i = 0; i <= queuePage; i++) FREE(queuePages[i]); + FREE(queuePages); + st_free_table(pathTable); + goto OUT_OF_MEM; + } + + nodeStat = currentNodeDistPage + nodeDistPageIndex; + nodeDistPageIndex++; + + nodeStat->oddTopDist = MAXSHORTINT; + nodeStat->evenTopDist = MAXSHORTINT; + nodeStat->evenBotDist = MAXSHORTINT; + nodeStat->oddBotDist = MAXSHORTINT; + nodeStat->regResult = NULL; + nodeStat->compResult = NULL; + + insertValue = st_insert(pathTable, (char *)N, (char *)nodeStat); + if (insertValue == ST_OUT_OF_MEM) { + memOut = 1; + for (i = 0; i <= nodeDistPage; i++) FREE(nodeDistPages[i]); + FREE(nodeDistPages); + for (i = 0; i <= queuePage; i++) FREE(queuePages[i]); + FREE(queuePages); + st_free_table(pathTable); + goto OUT_OF_MEM; + } else if (insertValue == 1) { + fprintf(fp, "Something wrong, the entry exists but didnt show up in st_lookup\n"); + return(NULL); + } + + if (Cudd_IsComplement(node)) { + nodeStat->oddTopDist = 0; + } else { + nodeStat->evenTopDist = 0; + } + numParents = 1; + /* call the function that counts the distance of each node from the + * root + */ +#ifdef DD_DEBUG + numCalls = 0; +#endif + CreateTopDist(pathTable, parentPage, parentQueueIndex, (int) topLen, + childPage, childQueueIndex, numParents, fp); + if (memOut) { + fprintf(fp, "Out of Memory and cant count path lengths\n"); + goto OUT_OF_MEM; + } + +#ifdef DD_DEBUG + numCalls = 0; +#endif + /* call the function that counts the distance of each node from the + * constant + */ + if (!CreateBotDist(node, pathTable, pathLengthArray, fp)) return(NULL); + + /* free BFS queue pages as no longer required */ + for (i = 0; i <= queuePage; i++) FREE(queuePages[i]); + FREE(queuePages); + return(pathTable); + +OUT_OF_MEM: + (void) fprintf(fp, "Out of Memory, cannot allocate pages\n"); + memOut = 1; + return(NULL); + +} /*end of CreatePathTable */ + + +/**Function******************************************************************** + + Synopsis [Chooses the maximum allowable path length of nodes under the + threshold.] + + Description [Chooses the maximum allowable path length under each node. + The corner cases are when the threshold is larger than the number + of nodes in the BDD iself, in which case 'numVars + 1' is returned. + If all nodes of a particular path length are needed, then the + maxpath returned is the next one with excess nodes = 0;] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static unsigned int +AssessPathLength( + unsigned int * pathLengthArray /* array determining number of nodes belonging to the different path lengths */, + int threshold /* threshold to determine maximum allowable nodes in the subset */, + int numVars /* maximum number of variables */, + unsigned int * excess /* number of nodes labeled maxpath required in the subset */, + FILE *fp /* where to write messages */) +{ + unsigned int i, maxpath; + int temp; + + temp = threshold; + i = 0; + maxpath = 0; + /* quit loop if i reaches max number of variables or if temp reaches + * below zero + */ + while ((i < (unsigned) numVars+1) && (temp > 0)) { + if (pathLengthArray[i] > 0) { + maxpath = i; + temp = temp - pathLengthArray[i]; + } + i++; + } + /* if all nodes of max path are needed */ + if (temp >= 0) { + maxpath++; /* now maxpath becomes the next maxppath or max number + of variables */ + *excess = 0; + } else { /* normal case when subset required is less than size of + original BDD */ + *excess = temp + pathLengthArray[maxpath]; + } + + if (maxpath == 0) { + fprintf(fp, "Path Length array seems to be all zeroes, check\n"); + } + return(maxpath); + +} /* end of AssessPathLength */ + + +/**Function******************************************************************** + + Synopsis [Builds the BDD with nodes labeled with path length less than or equal to maxpath] + + Description [Builds the BDD with nodes labeled with path length + under maxpath and as many nodes labeled maxpath as determined by the + threshold. The procedure uses the path table to determine which nodes + in the original bdd need to be retained. This procedure picks a + shortest path (tie break decided by taking the child with the shortest + distance to the constant) and recurs down the path till it reaches the + constant. the procedure then starts building the subset upward from + the constant. All nodes labeled by path lengths less than the given + maxpath are used to build the subset. However, in the case of nodes + that have label equal to maxpath, as many are chosen as required by + the threshold. This number is stored in the info structure in the + field thresholdReached. This field is decremented whenever a node + labeled maxpath is encountered and the nodes labeled maxpath are + aggregated in a maxpath table. As soon as the thresholdReached count + goes to 0, the shortest path from this node to the constant is found. + The extraction of nodes with the above labeling is based on the fact + that each node, labeled with a path length, P, has at least one child + labeled P or less. So extracting all nodes labeled a given path length + P ensures complete paths between the root and the constant. Extraction + of a partial number of nodes with a given path length may result in + incomplete paths and hence the additional number of nodes are grabbed + to complete the path. Since the Bdd is built bottom-up, other nodes + labeled maxpath do lie on complete paths. The procedure may cause the + subset to have a larger or smaller number of nodes than the specified + threshold. The increase in the number of nodes is caused by the + building of a subset and the reduction by recombination. However in + most cases, the recombination overshadows the increase and the + procedure returns a result with lower number of nodes than specified. + The subsetNodeTable is NIL when there is no hard limit on the number + of nodes. Further efforts towards keeping the subset closer to the + threshold number were abandoned in favour of keeping the procedure + simple and fast.] + + SideEffects [SubsetNodeTable is changed if it is not NIL.] + + SeeAlso [] + +******************************************************************************/ +static DdNode * +BuildSubsetBdd( + DdManager * dd /* DD manager */, + st_table * pathTable /* path table with path lengths and computed results */, + DdNode * node /* current node */, + struct AssortedInfo * info /* assorted information structure */, + st_table * subsetNodeTable /* table storing computed results */) +{ + DdNode *N, *Nv, *Nnv; + DdNode *ThenBranch, *ElseBranch, *childBranch; + DdNode *child, *regChild, *regNnv, *regNv; + NodeDist_t *nodeStatNv, *nodeStat, *nodeStatNnv; + DdNode *neW, *topv, *regNew; + char *entry; + unsigned int topid; + unsigned int childPathLength, oddLen, evenLen, NnvPathLength, NvPathLength; + unsigned int NvBotDist, NnvBotDist; + int tiebreakChild; + int processingDone, thenDone, elseDone; + + +#ifdef DD_DEBUG + numCalls++; +#endif + if (Cudd_IsConstant(node)) + return(node); + + N = Cudd_Regular(node); + /* Find node in table. */ + if (!st_lookup(pathTable, (char *)N, (char **)&nodeStat)) { + (void) fprintf(dd->err, "Something wrong, node must be in table \n"); + dd->errorCode = CUDD_INTERNAL_ERROR; + return(NULL); + } + /* If the node in the table has been visited, then return the corresponding + ** Dd. Since a node can become a subset of itself, its + ** complement (that is te same node reached by a different parity) will + ** become a superset of the original node and result in some minterms + ** that were not in the original set. Hence two different results are + ** maintained, corresponding to the odd and even parities. + */ + + /* If this node is reached with an odd parity, get odd parity results. */ + if (Cudd_IsComplement(node)) { + if (nodeStat->compResult != NULL) { +#ifdef DD_DEBUG + hits++; +#endif + return(nodeStat->compResult); + } + } else { + /* if this node is reached with an even parity, get even parity + * results + */ + if (nodeStat->regResult != NULL) { +#ifdef DD_DEBUG + hits++; +#endif + return(nodeStat->regResult); + } + } + + + /* get children */ + Nv = Cudd_T(N); + Nnv = Cudd_E(N); + + Nv = Cudd_NotCond(Nv, Cudd_IsComplement(node)); + Nnv = Cudd_NotCond(Nnv, Cudd_IsComplement(node)); + + /* no child processed */ + processingDone = 0; + /* then child not processed */ + thenDone = 0; + ThenBranch = NULL; + /* else child not processed */ + elseDone = 0; + ElseBranch = NULL; + /* if then child constant, branch is the child */ + if (Cudd_IsConstant(Nv)) { + /*shortest path found */ + if ((Nv == DD_ONE(dd)) && (info->findShortestPath)) { + info->findShortestPath = 0; + } + + ThenBranch = Nv; + cuddRef(ThenBranch); + if (ThenBranch == NULL) { + return(NULL); + } + + thenDone++; + processingDone++; + NvBotDist = MAXSHORTINT; + } else { + /* Derive regular child for table lookup. */ + regNv = Cudd_Regular(Nv); + /* Get node data for shortest path length. */ + if (!st_lookup(pathTable, (char *)regNv, (char **)&nodeStatNv) ) { + (void) fprintf(dd->err, "Something wrong, node must be in table\n"); + dd->errorCode = CUDD_INTERNAL_ERROR; + return(NULL); + } + /* Derive shortest path length for child. */ + if ((nodeStatNv->oddTopDist != MAXSHORTINT) && + (nodeStatNv->oddBotDist != MAXSHORTINT)) { + oddLen = (nodeStatNv->oddTopDist + nodeStatNv->oddBotDist); + } else { + oddLen = MAXSHORTINT; + } + + if ((nodeStatNv->evenTopDist != MAXSHORTINT) && + (nodeStatNv->evenBotDist != MAXSHORTINT)) { + evenLen = (nodeStatNv->evenTopDist +nodeStatNv->evenBotDist); + } else { + evenLen = MAXSHORTINT; + } + + NvPathLength = (oddLen <= evenLen) ? oddLen : evenLen; + NvBotDist = (oddLen <= evenLen) ? nodeStatNv->oddBotDist: + nodeStatNv->evenBotDist; + } + /* if else child constant, branch is the child */ + if (Cudd_IsConstant(Nnv)) { + /*shortest path found */ + if ((Nnv == DD_ONE(dd)) && (info->findShortestPath)) { + info->findShortestPath = 0; + } + + ElseBranch = Nnv; + cuddRef(ElseBranch); + if (ElseBranch == NULL) { + return(NULL); + } + + elseDone++; + processingDone++; + NnvBotDist = MAXSHORTINT; + } else { + /* Derive regular child for table lookup. */ + regNnv = Cudd_Regular(Nnv); + /* Get node data for shortest path length. */ + if (!st_lookup(pathTable, (char *)regNnv, (char **)&nodeStatNnv) ) { + (void) fprintf(dd->err, "Something wrong, node must be in table\n"); + dd->errorCode = CUDD_INTERNAL_ERROR; + return(NULL); + } + /* Derive shortest path length for child. */ + if ((nodeStatNnv->oddTopDist != MAXSHORTINT) && + (nodeStatNnv->oddBotDist != MAXSHORTINT)) { + oddLen = (nodeStatNnv->oddTopDist + nodeStatNnv->oddBotDist); + } else { + oddLen = MAXSHORTINT; + } + + if ((nodeStatNnv->evenTopDist != MAXSHORTINT) && + (nodeStatNnv->evenBotDist != MAXSHORTINT)) { + evenLen = (nodeStatNnv->evenTopDist +nodeStatNnv->evenBotDist); + } else { + evenLen = MAXSHORTINT; + } + + NnvPathLength = (oddLen <= evenLen) ? oddLen : evenLen; + NnvBotDist = (oddLen <= evenLen) ? nodeStatNnv->oddBotDist : + nodeStatNnv->evenBotDist; + } + + tiebreakChild = (NvBotDist <= NnvBotDist) ? 1 : 0; + /* while both children not processed */ + while (processingDone != 2) { + if (!processingDone) { + /* if no child processed */ + /* pick the child with shortest path length and record which one + * picked + */ + if ((NvPathLength < NnvPathLength) || + ((NvPathLength == NnvPathLength) && (tiebreakChild == 1))) { + child = Nv; + regChild = regNv; + thenDone = 1; + childPathLength = NvPathLength; + } else { + child = Nnv; + regChild = regNnv; + elseDone = 1; + childPathLength = NnvPathLength; + } /* then path length less than else path length */ + } else { + /* if one child processed, process the other */ + if (thenDone) { + child = Nnv; + regChild = regNnv; + elseDone = 1; + childPathLength = NnvPathLength; + } else { + child = Nv; + regChild = regNv; + thenDone = 1; + childPathLength = NvPathLength; + } /* end of else pick the Then child if ELSE child processed */ + } /* end of else one child has been processed */ + + /* ignore (replace with constant 0) all nodes which lie on paths larger + * than the maximum length of the path required + */ + if (childPathLength > info->maxpath) { + /* record nodes visited */ + childBranch = zero; + } else { + if (childPathLength < info->maxpath) { + if (info->findShortestPath) { + info->findShortestPath = 0; + } + childBranch = BuildSubsetBdd(dd, pathTable, child, info, + subsetNodeTable); + + } else { /* Case: path length of node = maxpath */ + /* If the node labeled with maxpath is found in the + ** maxpathTable, use it to build the subset BDD. */ + if (st_lookup(info->maxpathTable, (char *)regChild, + (char **)&entry)) { + /* When a node that is already been chosen is hit, + ** the quest for a complete path is over. */ + if (info->findShortestPath) { + info->findShortestPath = 0; + } + childBranch = BuildSubsetBdd(dd, pathTable, child, info, + subsetNodeTable); + } else { + /* If node is not found in the maxpathTable and + ** the threshold has been reached, then if the + ** path needs to be completed, continue. Else + ** replace the node with a zero. */ + if (info->thresholdReached <= 0) { + if (info->findShortestPath) { + if (st_insert(info->maxpathTable, (char *)regChild, + (char *)NIL(char)) == ST_OUT_OF_MEM) { + memOut = 1; + (void) fprintf(dd->err, "OUT of memory\n"); + info->thresholdReached = 0; + childBranch = zero; + } else { + info->thresholdReached--; + childBranch = BuildSubsetBdd(dd, pathTable, + child, info,subsetNodeTable); + } + } else { /* not find shortest path, we dont need this + node */ + childBranch = zero; + } + } else { /* Threshold hasn't been reached, + ** need the node. */ + if (st_insert(info->maxpathTable, (char *)regChild, + (char *)NIL(char)) == ST_OUT_OF_MEM) { + memOut = 1; + (void) fprintf(dd->err, "OUT of memory\n"); + info->thresholdReached = 0; + childBranch = zero; + } else { + info->thresholdReached--; + if (info->thresholdReached <= 0) { + info->findShortestPath = 1; + } + childBranch = BuildSubsetBdd(dd, pathTable, + child, info, subsetNodeTable); + + } /* end of st_insert successful */ + } /* end of threshold hasnt been reached yet */ + } /* end of else node not found in maxpath table */ + } /* end of if (path length of node = maxpath) */ + } /* end if !(childPathLength > maxpath) */ + if (childBranch == NULL) { + /* deref other stuff incase reordering has taken place */ + if (ThenBranch != NULL) { + Cudd_RecursiveDeref(dd, ThenBranch); + ThenBranch = NULL; + } + if (ElseBranch != NULL) { + Cudd_RecursiveDeref(dd, ElseBranch); + ElseBranch = NULL; + } + return(NULL); + } + + cuddRef(childBranch); + + if (child == Nv) { + ThenBranch = childBranch; + } else { + ElseBranch = childBranch; + } + processingDone++; + + } /*end of while processing Nv, Nnv */ + + info->findShortestPath = 0; + topid = Cudd_NodeReadIndex(N); + topv = Cudd_ReadVars(dd, topid); + cuddRef(topv); + neW = cuddBddIteRecur(dd, topv, ThenBranch, ElseBranch); + if (neW != NULL) { + cuddRef(neW); + } + Cudd_RecursiveDeref(dd, topv); + Cudd_RecursiveDeref(dd, ThenBranch); + Cudd_RecursiveDeref(dd, ElseBranch); + + + /* Hard Limit of threshold has been imposed */ + if (subsetNodeTable != NIL(st_table)) { + /* check if a new node is created */ + regNew = Cudd_Regular(neW); + /* subset node table keeps all new nodes that have been created to keep + * a running count of how many nodes have been built in the subset. + */ + if (!st_lookup(subsetNodeTable, (char *)regNew, (char **)&entry)) { + if (!Cudd_IsConstant(regNew)) { + if (st_insert(subsetNodeTable, (char *)regNew, + (char *)NULL) == ST_OUT_OF_MEM) { + (void) fprintf(dd->err, "Out of memory\n"); + return (NULL); + } + if (st_count(subsetNodeTable) > info->threshold) { + info->thresholdReached = 0; + } + } + } + } + + + if (neW == NULL) { + return(NULL); + } else { + /*store computed result in regular form*/ + if (Cudd_IsComplement(node)) { + nodeStat->compResult = neW; + cuddRef(nodeStat->compResult); + /* if the new node is the same as the corresponding node in the + * original bdd then its complement need not be computed as it + * cannot be larger than the node itself + */ + if (neW == node) { +#ifdef DD_DEBUG + thishit++; +#endif + /* if a result for the node has already been computed, then + * it can only be smaller than teh node itself. hence store + * the node result in order not to break recombination + */ + if (nodeStat->regResult != NULL) { + Cudd_RecursiveDeref(dd, nodeStat->regResult); + } + nodeStat->regResult = Cudd_Not(neW); + cuddRef(nodeStat->regResult); + } + + } else { + nodeStat->regResult = neW; + cuddRef(nodeStat->regResult); + if (neW == node) { +#ifdef DD_DEBUG + thishit++; +#endif + if (nodeStat->compResult != NULL) { + Cudd_RecursiveDeref(dd, nodeStat->compResult); + } + nodeStat->compResult = Cudd_Not(neW); + cuddRef(nodeStat->compResult); + } + } + + cuddDeref(neW); + return(neW); + } /* end of else i.e. Subset != NULL */ +} /* end of BuildSubsetBdd */ + + +/**Function******************************************************************** + + Synopsis [Procedure to free te result dds stored in the NodeDist pages.] + + Description [None] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static enum st_retval +stPathTableDdFree( + char * key, + char * value, + char * arg) +{ + NodeDist_t *nodeStat; + DdManager *dd; + + nodeStat = (NodeDist_t *)value; + dd = (DdManager *)arg; + if (nodeStat->regResult != NULL) { + Cudd_RecursiveDeref(dd, nodeStat->regResult); + } + if (nodeStat->compResult != NULL) { + Cudd_RecursiveDeref(dd, nodeStat->compResult); + } + return(ST_CONTINUE); + +} /* end of stPathTableFree */ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddSymmetry.c b/abc_with_bb_support/src/bdd/cudd/cuddSymmetry.c new file mode 100644 index 000000000..667a1a16a --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddSymmetry.c @@ -0,0 +1,1668 @@ +/**CFile*********************************************************************** + + FileName [cuddSymmetry.c] + + PackageName [cudd] + + Synopsis [Functions for symmetry-based variable reordering.] + + Description [External procedures included in this file: +
          +
        • Cudd_SymmProfile() +
        + Internal procedures included in this module: +
          +
        • cuddSymmCheck() +
        • cuddSymmSifting() +
        • cuddSymmSiftingConv() +
        + Static procedures included in this module: +
          +
        • ddSymmUniqueCompare() +
        • ddSymmSiftingAux() +
        • ddSymmSiftingConvAux() +
        • ddSymmSiftingUp() +
        • ddSymmSiftingDown() +
        • ddSymmGroupMove() +
        • ddSymmGroupMoveBackward() +
        • ddSymmSiftingBackward() +
        • ddSymmSummary() +
        ] + + Author [Shipra Panda, Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define MV_OOM (Move *)1 + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddSymmetry.c,v 1.1.1.1 2003/02/24 22:23:53 wjiang Exp $"; +#endif + +static int *entry; + +extern int ddTotalNumberSwapping; +#ifdef DD_STATS +extern int ddTotalNISwaps; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int ddSymmUniqueCompare ARGS((int *ptrX, int *ptrY)); +static int ddSymmSiftingAux ARGS((DdManager *table, int x, int xLow, int xHigh)); +static int ddSymmSiftingConvAux ARGS((DdManager *table, int x, int xLow, int xHigh)); +static Move * ddSymmSiftingUp ARGS((DdManager *table, int y, int xLow)); +static Move * ddSymmSiftingDown ARGS((DdManager *table, int x, int xHigh)); +static int ddSymmGroupMove ARGS((DdManager *table, int x, int y, Move **moves)); +static int ddSymmGroupMoveBackward ARGS((DdManager *table, int x, int y)); +static int ddSymmSiftingBackward ARGS((DdManager *table, Move *moves, int size)); +static void ddSymmSummary ARGS((DdManager *table, int lower, int upper, int *symvars, int *symgroups)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Prints statistics on symmetric variables.] + + Description [] + + SideEffects [None] + +******************************************************************************/ +void +Cudd_SymmProfile( + DdManager * table, + int lower, + int upper) +{ + int i,x,gbot; + int TotalSymm = 0; + int TotalSymmGroups = 0; + + for (i = lower; i <= upper; i++) { + if (table->subtables[i].next != (unsigned) i) { + x = i; + (void) fprintf(table->out,"Group:"); + do { + (void) fprintf(table->out," %d",table->invperm[x]); + TotalSymm++; + gbot = x; + x = table->subtables[x].next; + } while (x != i); + TotalSymmGroups++; +#ifdef DD_DEBUG + assert(table->subtables[gbot].next == (unsigned) i); +#endif + i = gbot; + (void) fprintf(table->out,"\n"); + } + } + (void) fprintf(table->out,"Total Symmetric = %d\n",TotalSymm); + (void) fprintf(table->out,"Total Groups = %d\n",TotalSymmGroups); + +} /* end of Cudd_SymmProfile */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Checks for symmetry of x and y.] + + Description [Checks for symmetry of x and y. Ignores projection + functions, unless they are isolated. Returns 1 in case of symmetry; 0 + otherwise.] + + SideEffects [None] + +******************************************************************************/ +int +cuddSymmCheck( + DdManager * table, + int x, + int y) +{ + DdNode *f,*f0,*f1,*f01,*f00,*f11,*f10; + int comple; /* f0 is complemented */ + int xsymmy; /* x and y may be positively symmetric */ + int xsymmyp; /* x and y may be negatively symmetric */ + int arccount; /* number of arcs from layer x to layer y */ + int TotalRefCount; /* total reference count of layer y minus 1 */ + int yindex; + int i; + DdNodePtr *list; + int slots; + DdNode *sentinel = &(table->sentinel); +#ifdef DD_DEBUG + int xindex; +#endif + + /* Checks that x and y are not the projection functions. + ** For x it is sufficient to check whether there is only one + ** node; indeed, if there is one node, it is the projection function + ** and it cannot point to y. Hence, if y isn't just the projection + ** function, it has one arc coming from a layer different from x. + */ + if (table->subtables[x].keys == 1) { + return(0); + } + yindex = table->invperm[y]; + if (table->subtables[y].keys == 1) { + if (table->vars[yindex]->ref == 1) + return(0); + } + + xsymmy = xsymmyp = 1; + arccount = 0; + slots = table->subtables[x].slots; + list = table->subtables[x].nodelist; + for (i = 0; i < slots; i++) { + f = list[i]; + while (f != sentinel) { + /* Find f1, f0, f11, f10, f01, f00. */ + f1 = cuddT(f); + f0 = Cudd_Regular(cuddE(f)); + comple = Cudd_IsComplement(cuddE(f)); + if ((int) f1->index == yindex) { + arccount++; + f11 = cuddT(f1); f10 = cuddE(f1); + } else { + if ((int) f0->index != yindex) { + /* If f is an isolated projection function it is + ** allowed to bypass layer y. + */ + if (f1 != DD_ONE(table) || f0 != DD_ONE(table) || f->ref != 1) + return(0); /* f bypasses layer y */ + } + f11 = f10 = f1; + } + if ((int) f0->index == yindex) { + arccount++; + f01 = cuddT(f0); f00 = cuddE(f0); + } else { + f01 = f00 = f0; + } + if (comple) { + f01 = Cudd_Not(f01); + f00 = Cudd_Not(f00); + } + + if (f1 != DD_ONE(table) || f0 != DD_ONE(table) || f->ref != 1) { + xsymmy &= f01 == f10; + xsymmyp &= f11 == f00; + if ((xsymmy == 0) && (xsymmyp == 0)) + return(0); + } + + f = f->next; + } /* while */ + } /* for */ + + /* Calculate the total reference counts of y */ + TotalRefCount = -1; /* -1 for projection function */ + slots = table->subtables[y].slots; + list = table->subtables[y].nodelist; + for (i = 0; i < slots; i++) { + f = list[i]; + while (f != sentinel) { + TotalRefCount += f->ref; + f = f->next; + } + } + +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + if (arccount == TotalRefCount) { + xindex = table->invperm[x]; + (void) fprintf(table->out, + "Found symmetry! x =%d\ty = %d\tPos(%d,%d)\n", + xindex,yindex,x,y); + } +#endif + + return(arccount == TotalRefCount); + +} /* end of cuddSymmCheck */ + + +/**Function******************************************************************** + + Synopsis [Symmetric sifting algorithm.] + + Description [Symmetric sifting algorithm. + Assumes that no dead nodes are present. +
          +
        1. Order all the variables according to the number of entries in + each unique subtable. +
        2. Sift the variable up and down, remembering each time the total + size of the DD heap and grouping variables that are symmetric. +
        3. Select the best permutation. +
        4. Repeat 3 and 4 for all variables. +
        + Returns 1 plus the number of symmetric variables if successful; 0 + otherwise.] + + SideEffects [None] + + SeeAlso [cuddSymmSiftingConv] + +******************************************************************************/ +int +cuddSymmSifting( + DdManager * table, + int lower, + int upper) +{ + int i; + int *var; + int size; + int x; + int result; + int symvars; + int symgroups; +#ifdef DD_STATS + int previousSize; +#endif + + size = table->size; + + /* Find order in which to sift variables. */ + var = NULL; + entry = ALLOC(int,size); + if (entry == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto ddSymmSiftingOutOfMem; + } + var = ALLOC(int,size); + if (var == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto ddSymmSiftingOutOfMem; + } + + for (i = 0; i < size; i++) { + x = table->perm[i]; + entry[i] = table->subtables[x].keys; + var[i] = i; + } + + qsort((void *)var,size,sizeof(int),(int (*)(const void *, const void *))ddSymmUniqueCompare); + + /* Initialize the symmetry of each subtable to itself. */ + for (i = lower; i <= upper; i++) { + table->subtables[i].next = i; + } + + for (i = 0; i < ddMin(table->siftMaxVar,size); i++) { + if (ddTotalNumberSwapping >= table->siftMaxSwap) + break; + x = table->perm[var[i]]; +#ifdef DD_STATS + previousSize = table->keys - table->isolated; +#endif + if (x < lower || x > upper) continue; + if (table->subtables[x].next == (unsigned) x) { + result = ddSymmSiftingAux(table,x,lower,upper); + if (!result) goto ddSymmSiftingOutOfMem; +#ifdef DD_STATS + if (table->keys < (unsigned) previousSize + table->isolated) { + (void) fprintf(table->out,"-"); + } else if (table->keys > (unsigned) previousSize + + table->isolated) { + (void) fprintf(table->out,"+"); /* should never happen */ + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + } + + FREE(var); + FREE(entry); + + ddSymmSummary(table, lower, upper, &symvars, &symgroups); + +#ifdef DD_STATS + (void) fprintf(table->out, "\n#:S_SIFTING %8d: symmetric variables\n", + symvars); + (void) fprintf(table->out, "#:G_SIFTING %8d: symmetric groups", + symgroups); +#endif + + return(1+symvars); + +ddSymmSiftingOutOfMem: + + if (entry != NULL) FREE(entry); + if (var != NULL) FREE(var); + + return(0); + +} /* end of cuddSymmSifting */ + + +/**Function******************************************************************** + + Synopsis [Symmetric sifting to convergence algorithm.] + + Description [Symmetric sifting to convergence algorithm. + Assumes that no dead nodes are present. +
          +
        1. Order all the variables according to the number of entries in + each unique subtable. +
        2. Sift the variable up and down, remembering each time the total + size of the DD heap and grouping variables that are symmetric. +
        3. Select the best permutation. +
        4. Repeat 3 and 4 for all variables. +
        5. Repeat 1-4 until no further improvement. +
        + Returns 1 plus the number of symmetric variables if successful; 0 + otherwise.] + + SideEffects [None] + + SeeAlso [cuddSymmSifting] + +******************************************************************************/ +int +cuddSymmSiftingConv( + DdManager * table, + int lower, + int upper) +{ + int i; + int *var; + int size; + int x; + int result; + int symvars; + int symgroups; + int classes; + int initialSize; +#ifdef DD_STATS + int previousSize; +#endif + + initialSize = table->keys - table->isolated; + + size = table->size; + + /* Find order in which to sift variables. */ + var = NULL; + entry = ALLOC(int,size); + if (entry == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto ddSymmSiftingConvOutOfMem; + } + var = ALLOC(int,size); + if (var == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto ddSymmSiftingConvOutOfMem; + } + + for (i = 0; i < size; i++) { + x = table->perm[i]; + entry[i] = table->subtables[x].keys; + var[i] = i; + } + + qsort((void *)var,size,sizeof(int),(int (*)(const void *, const void *))ddSymmUniqueCompare); + + /* Initialize the symmetry of each subtable to itself + ** for first pass of converging symmetric sifting. + */ + for (i = lower; i <= upper; i++) { + table->subtables[i].next = i; + } + + for (i = 0; i < ddMin(table->siftMaxVar, table->size); i++) { + if (ddTotalNumberSwapping >= table->siftMaxSwap) + break; + x = table->perm[var[i]]; + if (x < lower || x > upper) continue; + /* Only sift if not in symmetry group already. */ + if (table->subtables[x].next == (unsigned) x) { +#ifdef DD_STATS + previousSize = table->keys - table->isolated; +#endif + result = ddSymmSiftingAux(table,x,lower,upper); + if (!result) goto ddSymmSiftingConvOutOfMem; +#ifdef DD_STATS + if (table->keys < (unsigned) previousSize + table->isolated) { + (void) fprintf(table->out,"-"); + } else if (table->keys > (unsigned) previousSize + + table->isolated) { + (void) fprintf(table->out,"+"); + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + } + + /* Sifting now until convergence. */ + while ((unsigned) initialSize > table->keys - table->isolated) { + initialSize = table->keys - table->isolated; +#ifdef DD_STATS + (void) fprintf(table->out,"\n"); +#endif + /* Here we consider only one representative for each symmetry class. */ + for (x = lower, classes = 0; x <= upper; x++, classes++) { + while ((unsigned) x < table->subtables[x].next) { + x = table->subtables[x].next; + } + /* Here x is the largest index in a group. + ** Groups consist of adjacent variables. + ** Hence, the next increment of x will move it to a new group. + */ + i = table->invperm[x]; + entry[i] = table->subtables[x].keys; + var[classes] = i; + } + + qsort((void *)var,classes,sizeof(int),(int (*)(const void *, const void *))ddSymmUniqueCompare); + + /* Now sift. */ + for (i = 0; i < ddMin(table->siftMaxVar,classes); i++) { + if (ddTotalNumberSwapping >= table->siftMaxSwap) + break; + x = table->perm[var[i]]; + if ((unsigned) x >= table->subtables[x].next) { +#ifdef DD_STATS + previousSize = table->keys - table->isolated; +#endif + result = ddSymmSiftingConvAux(table,x,lower,upper); + if (!result ) goto ddSymmSiftingConvOutOfMem; +#ifdef DD_STATS + if (table->keys < (unsigned) previousSize + table->isolated) { + (void) fprintf(table->out,"-"); + } else if (table->keys > (unsigned) previousSize + + table->isolated) { + (void) fprintf(table->out,"+"); + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + } /* for */ + } + + ddSymmSummary(table, lower, upper, &symvars, &symgroups); + +#ifdef DD_STATS + (void) fprintf(table->out, "\n#:S_SIFTING %8d: symmetric variables\n", + symvars); + (void) fprintf(table->out, "#:G_SIFTING %8d: symmetric groups", + symgroups); +#endif + + FREE(var); + FREE(entry); + + return(1+symvars); + +ddSymmSiftingConvOutOfMem: + + if (entry != NULL) FREE(entry); + if (var != NULL) FREE(var); + + return(0); + +} /* end of cuddSymmSiftingConv */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Comparison function used by qsort.] + + Description [Comparison function used by qsort to order the variables + according to the number of keys in the subtables. + Returns the difference in number of keys between the two + variables being compared.] + + SideEffects [None] + +******************************************************************************/ +static int +ddSymmUniqueCompare( + int * ptrX, + int * ptrY) +{ +#if 0 + if (entry[*ptrY] == entry[*ptrX]) { + return((*ptrX) - (*ptrY)); + } +#endif + return(entry[*ptrY] - entry[*ptrX]); + +} /* end of ddSymmUniqueCompare */ + + +/**Function******************************************************************** + + Synopsis [Given xLow <= x <= xHigh moves x up and down between the + boundaries.] + + Description [Given xLow <= x <= xHigh moves x up and down between the + boundaries. Finds the best position and does the required changes. + Assumes that x is not part of a symmetry group. Returns 1 if + successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddSymmSiftingAux( + DdManager * table, + int x, + int xLow, + int xHigh) +{ + Move *move; + Move *moveUp; /* list of up moves */ + Move *moveDown; /* list of down moves */ + int initialSize; + int result; + int i; + int topbot; /* index to either top or bottom of symmetry group */ + int initGroupSize, finalGroupSize; + + +#ifdef DD_DEBUG + /* check for previously detected symmetry */ + assert(table->subtables[x].next == (unsigned) x); +#endif + + initialSize = table->keys - table->isolated; + + moveDown = NULL; + moveUp = NULL; + + if ((x - xLow) > (xHigh - x)) { + /* Will go down first, unless x == xHigh: + ** Look for consecutive symmetries above x. + */ + for (i = x; i > xLow; i--) { + if (!cuddSymmCheck(table,i-1,i)) + break; + topbot = table->subtables[i-1].next; /* find top of i-1's group */ + table->subtables[i-1].next = i; + table->subtables[x].next = topbot; /* x is bottom of group so its */ + /* next is top of i-1's group */ + i = topbot + 1; /* add 1 for i--; new i is top of symm group */ + } + } else { + /* Will go up first unless x == xlow: + ** Look for consecutive symmetries below x. + */ + for (i = x; i < xHigh; i++) { + if (!cuddSymmCheck(table,i,i+1)) + break; + /* find bottom of i+1's symm group */ + topbot = i + 1; + while ((unsigned) topbot < table->subtables[topbot].next) { + topbot = table->subtables[topbot].next; + } + table->subtables[topbot].next = table->subtables[i].next; + table->subtables[i].next = i + 1; + i = topbot - 1; /* subtract 1 for i++; new i is bottom of group */ + } + } + + /* Now x may be in the middle of a symmetry group. + ** Find bottom of x's symm group. + */ + while ((unsigned) x < table->subtables[x].next) + x = table->subtables[x].next; + + if (x == xLow) { /* Sift down */ + +#ifdef DD_DEBUG + /* x must be a singleton */ + assert((unsigned) x == table->subtables[x].next); +#endif + if (x == xHigh) return(1); /* just one variable */ + + initGroupSize = 1; + + moveDown = ddSymmSiftingDown(table,x,xHigh); + /* after this point x --> xHigh, unless early term */ + if (moveDown == MV_OOM) goto ddSymmSiftingAuxOutOfMem; + if (moveDown == NULL) return(1); + + x = moveDown->y; + /* Find bottom of x's group */ + i = x; + while ((unsigned) i < table->subtables[i].next) { + i = table->subtables[i].next; + } +#ifdef DD_DEBUG + /* x should be the top of the symmetry group and i the bottom */ + assert((unsigned) i >= table->subtables[i].next); + assert((unsigned) x == table->subtables[i].next); +#endif + finalGroupSize = i - x + 1; + + if (initGroupSize == finalGroupSize) { + /* No new symmetry groups detected, return to best position */ + result = ddSymmSiftingBackward(table,moveDown,initialSize); + } else { + initialSize = table->keys - table->isolated; + moveUp = ddSymmSiftingUp(table,x,xLow); + result = ddSymmSiftingBackward(table,moveUp,initialSize); + } + if (!result) goto ddSymmSiftingAuxOutOfMem; + + } else if (cuddNextHigh(table,x) > xHigh) { /* Sift up */ + /* Find top of x's symm group */ + i = x; /* bottom */ + x = table->subtables[x].next; /* top */ + + if (x == xLow) return(1); /* just one big group */ + + initGroupSize = i - x + 1; + + moveUp = ddSymmSiftingUp(table,x,xLow); + /* after this point x --> xLow, unless early term */ + if (moveUp == MV_OOM) goto ddSymmSiftingAuxOutOfMem; + if (moveUp == NULL) return(1); + + x = moveUp->x; + /* Find top of x's group */ + i = table->subtables[x].next; +#ifdef DD_DEBUG + /* x should be the bottom of the symmetry group and i the top */ + assert((unsigned) x >= table->subtables[x].next); + assert((unsigned) i == table->subtables[x].next); +#endif + finalGroupSize = x - i + 1; + + if (initGroupSize == finalGroupSize) { + /* No new symmetry groups detected, return to best position */ + result = ddSymmSiftingBackward(table,moveUp,initialSize); + } else { + initialSize = table->keys - table->isolated; + moveDown = ddSymmSiftingDown(table,x,xHigh); + result = ddSymmSiftingBackward(table,moveDown,initialSize); + } + if (!result) goto ddSymmSiftingAuxOutOfMem; + + } else if ((x - xLow) > (xHigh - x)) { /* must go down first: shorter */ + + moveDown = ddSymmSiftingDown(table,x,xHigh); + /* at this point x == xHigh, unless early term */ + if (moveDown == MV_OOM) goto ddSymmSiftingAuxOutOfMem; + + if (moveDown != NULL) { + x = moveDown->y; /* x is top here */ + i = x; + while ((unsigned) i < table->subtables[i].next) { + i = table->subtables[i].next; + } + } else { + i = x; + while ((unsigned) i < table->subtables[i].next) { + i = table->subtables[i].next; + } + x = table->subtables[i].next; + } +#ifdef DD_DEBUG + /* x should be the top of the symmetry group and i the bottom */ + assert((unsigned) i >= table->subtables[i].next); + assert((unsigned) x == table->subtables[i].next); +#endif + initGroupSize = i - x + 1; + + moveUp = ddSymmSiftingUp(table,x,xLow); + if (moveUp == MV_OOM) goto ddSymmSiftingAuxOutOfMem; + + if (moveUp != NULL) { + x = moveUp->x; + i = table->subtables[x].next; + } else { + i = x; + while ((unsigned) x < table->subtables[x].next) + x = table->subtables[x].next; + } +#ifdef DD_DEBUG + /* x should be the bottom of the symmetry group and i the top */ + assert((unsigned) x >= table->subtables[x].next); + assert((unsigned) i == table->subtables[x].next); +#endif + finalGroupSize = x - i + 1; + + if (initGroupSize == finalGroupSize) { + /* No new symmetry groups detected, return to best position */ + result = ddSymmSiftingBackward(table,moveUp,initialSize); + } else { + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocNode(table, (DdNode *) moveDown); + moveDown = move; + } + initialSize = table->keys - table->isolated; + moveDown = ddSymmSiftingDown(table,x,xHigh); + result = ddSymmSiftingBackward(table,moveDown,initialSize); + } + if (!result) goto ddSymmSiftingAuxOutOfMem; + + } else { /* moving up first: shorter */ + /* Find top of x's symmetry group */ + x = table->subtables[x].next; + + moveUp = ddSymmSiftingUp(table,x,xLow); + /* at this point x == xHigh, unless early term */ + if (moveUp == MV_OOM) goto ddSymmSiftingAuxOutOfMem; + + if (moveUp != NULL) { + x = moveUp->x; + i = table->subtables[x].next; + } else { + while ((unsigned) x < table->subtables[x].next) + x = table->subtables[x].next; + i = table->subtables[x].next; + } +#ifdef DD_DEBUG + /* x is bottom of the symmetry group and i is top */ + assert((unsigned) x >= table->subtables[x].next); + assert((unsigned) i == table->subtables[x].next); +#endif + initGroupSize = x - i + 1; + + moveDown = ddSymmSiftingDown(table,x,xHigh); + if (moveDown == MV_OOM) goto ddSymmSiftingAuxOutOfMem; + + if (moveDown != NULL) { + x = moveDown->y; + i = x; + while ((unsigned) i < table->subtables[i].next) { + i = table->subtables[i].next; + } + } else { + i = x; + x = table->subtables[x].next; + } +#ifdef DD_DEBUG + /* x should be the top of the symmetry group and i the bottom */ + assert((unsigned) i >= table->subtables[i].next); + assert((unsigned) x == table->subtables[i].next); +#endif + finalGroupSize = i - x + 1; + + if (initGroupSize == finalGroupSize) { + /* No new symmetries detected, go back to best position */ + result = ddSymmSiftingBackward(table,moveDown,initialSize); + } else { + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocNode(table, (DdNode *) moveUp); + moveUp = move; + } + initialSize = table->keys - table->isolated; + moveUp = ddSymmSiftingUp(table,x,xLow); + result = ddSymmSiftingBackward(table,moveUp,initialSize); + } + if (!result) goto ddSymmSiftingAuxOutOfMem; + } + + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocNode(table, (DdNode *) moveDown); + moveDown = move; + } + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocNode(table, (DdNode *) moveUp); + moveUp = move; + } + + return(1); + +ddSymmSiftingAuxOutOfMem: + if (moveDown != MV_OOM) { + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocNode(table, (DdNode *) moveDown); + moveDown = move; + } + } + if (moveUp != MV_OOM) { + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocNode(table, (DdNode *) moveUp); + moveUp = move; + } + } + + return(0); + +} /* end of ddSymmSiftingAux */ + + +/**Function******************************************************************** + + Synopsis [Given xLow <= x <= xHigh moves x up and down between the + boundaries.] + + Description [Given xLow <= x <= xHigh moves x up and down between the + boundaries. Finds the best position and does the required changes. + Assumes that x is either an isolated variable, or it is the bottom of + a symmetry group. All symmetries may not have been found, because of + exceeded growth limit. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddSymmSiftingConvAux( + DdManager * table, + int x, + int xLow, + int xHigh) +{ + Move *move; + Move *moveUp; /* list of up moves */ + Move *moveDown; /* list of down moves */ + int initialSize; + int result; + int i; + int initGroupSize, finalGroupSize; + + + initialSize = table->keys - table->isolated; + + moveDown = NULL; + moveUp = NULL; + + if (x == xLow) { /* Sift down */ +#ifdef DD_DEBUG + /* x is bottom of symmetry group */ + assert((unsigned) x >= table->subtables[x].next); +#endif + i = table->subtables[x].next; + initGroupSize = x - i + 1; + + moveDown = ddSymmSiftingDown(table,x,xHigh); + /* at this point x == xHigh, unless early term */ + if (moveDown == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem; + if (moveDown == NULL) return(1); + + x = moveDown->y; + i = x; + while ((unsigned) i < table->subtables[i].next) { + i = table->subtables[i].next; + } +#ifdef DD_DEBUG + /* x should be the top of the symmetric group and i the bottom */ + assert((unsigned) i >= table->subtables[i].next); + assert((unsigned) x == table->subtables[i].next); +#endif + finalGroupSize = i - x + 1; + + if (initGroupSize == finalGroupSize) { + /* No new symmetries detected, go back to best position */ + result = ddSymmSiftingBackward(table,moveDown,initialSize); + } else { + initialSize = table->keys - table->isolated; + moveUp = ddSymmSiftingUp(table,x,xLow); + result = ddSymmSiftingBackward(table,moveUp,initialSize); + } + if (!result) goto ddSymmSiftingConvAuxOutOfMem; + + } else if (cuddNextHigh(table,x) > xHigh) { /* Sift up */ + /* Find top of x's symm group */ + while ((unsigned) x < table->subtables[x].next) + x = table->subtables[x].next; + i = x; /* bottom */ + x = table->subtables[x].next; /* top */ + + if (x == xLow) return(1); + + initGroupSize = i - x + 1; + + moveUp = ddSymmSiftingUp(table,x,xLow); + /* at this point x == xLow, unless early term */ + if (moveUp == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem; + if (moveUp == NULL) return(1); + + x = moveUp->x; + i = table->subtables[x].next; +#ifdef DD_DEBUG + /* x should be the bottom of the symmetry group and i the top */ + assert((unsigned) x >= table->subtables[x].next); + assert((unsigned) i == table->subtables[x].next); +#endif + finalGroupSize = x - i + 1; + + if (initGroupSize == finalGroupSize) { + /* No new symmetry groups detected, return to best position */ + result = ddSymmSiftingBackward(table,moveUp,initialSize); + } else { + initialSize = table->keys - table->isolated; + moveDown = ddSymmSiftingDown(table,x,xHigh); + result = ddSymmSiftingBackward(table,moveDown,initialSize); + } + if (!result) + goto ddSymmSiftingConvAuxOutOfMem; + + } else if ((x - xLow) > (xHigh - x)) { /* must go down first: shorter */ + moveDown = ddSymmSiftingDown(table,x,xHigh); + /* at this point x == xHigh, unless early term */ + if (moveDown == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem; + + if (moveDown != NULL) { + x = moveDown->y; + i = x; + while ((unsigned) i < table->subtables[i].next) { + i = table->subtables[i].next; + } + } else { + while ((unsigned) x < table->subtables[x].next) + x = table->subtables[x].next; + i = x; + x = table->subtables[x].next; + } +#ifdef DD_DEBUG + /* x should be the top of the symmetry group and i the bottom */ + assert((unsigned) i >= table->subtables[i].next); + assert((unsigned) x == table->subtables[i].next); +#endif + initGroupSize = i - x + 1; + + moveUp = ddSymmSiftingUp(table,x,xLow); + if (moveUp == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem; + + if (moveUp != NULL) { + x = moveUp->x; + i = table->subtables[x].next; + } else { + i = x; + while ((unsigned) x < table->subtables[x].next) + x = table->subtables[x].next; + } +#ifdef DD_DEBUG + /* x should be the bottom of the symmetry group and i the top */ + assert((unsigned) x >= table->subtables[x].next); + assert((unsigned) i == table->subtables[x].next); +#endif + finalGroupSize = x - i + 1; + + if (initGroupSize == finalGroupSize) { + /* No new symmetry groups detected, return to best position */ + result = ddSymmSiftingBackward(table,moveUp,initialSize); + } else { + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocNode(table, (DdNode *) moveDown); + moveDown = move; + } + initialSize = table->keys - table->isolated; + moveDown = ddSymmSiftingDown(table,x,xHigh); + result = ddSymmSiftingBackward(table,moveDown,initialSize); + } + if (!result) goto ddSymmSiftingConvAuxOutOfMem; + + } else { /* moving up first: shorter */ + /* Find top of x's symmetry group */ + x = table->subtables[x].next; + + moveUp = ddSymmSiftingUp(table,x,xLow); + /* at this point x == xHigh, unless early term */ + if (moveUp == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem; + + if (moveUp != NULL) { + x = moveUp->x; + i = table->subtables[x].next; + } else { + i = x; + while ((unsigned) x < table->subtables[x].next) + x = table->subtables[x].next; + } +#ifdef DD_DEBUG + /* x is bottom of the symmetry group and i is top */ + assert((unsigned) x >= table->subtables[x].next); + assert((unsigned) i == table->subtables[x].next); +#endif + initGroupSize = x - i + 1; + + moveDown = ddSymmSiftingDown(table,x,xHigh); + if (moveDown == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem; + + if (moveDown != NULL) { + x = moveDown->y; + i = x; + while ((unsigned) i < table->subtables[i].next) { + i = table->subtables[i].next; + } + } else { + i = x; + x = table->subtables[x].next; + } +#ifdef DD_DEBUG + /* x should be the top of the symmetry group and i the bottom */ + assert((unsigned) i >= table->subtables[i].next); + assert((unsigned) x == table->subtables[i].next); +#endif + finalGroupSize = i - x + 1; + + if (initGroupSize == finalGroupSize) { + /* No new symmetries detected, go back to best position */ + result = ddSymmSiftingBackward(table,moveDown,initialSize); + } else { + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocNode(table, (DdNode *) moveUp); + moveUp = move; + } + initialSize = table->keys - table->isolated; + moveUp = ddSymmSiftingUp(table,x,xLow); + result = ddSymmSiftingBackward(table,moveUp,initialSize); + } + if (!result) goto ddSymmSiftingConvAuxOutOfMem; + } + + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocNode(table, (DdNode *) moveDown); + moveDown = move; + } + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocNode(table, (DdNode *) moveUp); + moveUp = move; + } + + return(1); + +ddSymmSiftingConvAuxOutOfMem: + if (moveDown != MV_OOM) { + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocNode(table, (DdNode *) moveDown); + moveDown = move; + } + } + if (moveUp != MV_OOM) { + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocNode(table, (DdNode *) moveUp); + moveUp = move; + } + } + + return(0); + +} /* end of ddSymmSiftingConvAux */ + + +/**Function******************************************************************** + + Synopsis [Moves x up until either it reaches the bound (xLow) or + the size of the DD heap increases too much.] + + Description [Moves x up until either it reaches the bound (xLow) or + the size of the DD heap increases too much. Assumes that x is the top + of a symmetry group. Checks x for symmetry to the adjacent + variables. If symmetry is found, the symmetry group of x is merged + with the symmetry group of the other variable. Returns the set of + moves in case of success; MV_OOM if memory is full.] + + SideEffects [None] + +******************************************************************************/ +static Move * +ddSymmSiftingUp( + DdManager * table, + int y, + int xLow) +{ + Move *moves; + Move *move; + int x; + int size; + int i; + int gxtop,gybot; + int limitSize; + int xindex, yindex; + int zindex; + int z; + int isolated; + int L; /* lower bound on DD size */ +#ifdef DD_DEBUG + int checkL; +#endif + + + moves = NULL; + yindex = table->invperm[y]; + + /* Initialize the lower bound. + ** The part of the DD below the bottom of y' group will not change. + ** The part of the DD above y that does not interact with y will not + ** change. The rest may vanish in the best case, except for + ** the nodes at level xLow, which will not vanish, regardless. + */ + limitSize = L = table->keys - table->isolated; + gybot = y; + while ((unsigned) gybot < table->subtables[gybot].next) + gybot = table->subtables[gybot].next; + for (z = xLow + 1; z <= gybot; z++) { + zindex = table->invperm[z]; + if (zindex == yindex || cuddTestInteract(table,zindex,yindex)) { + isolated = table->vars[zindex]->ref == 1; + L -= table->subtables[z].keys - isolated; + } + } + + x = cuddNextLow(table,y); + while (x >= xLow && L <= limitSize) { +#ifdef DD_DEBUG + gybot = y; + while ((unsigned) gybot < table->subtables[gybot].next) + gybot = table->subtables[gybot].next; + checkL = table->keys - table->isolated; + for (z = xLow + 1; z <= gybot; z++) { + zindex = table->invperm[z]; + if (zindex == yindex || cuddTestInteract(table,zindex,yindex)) { + isolated = table->vars[zindex]->ref == 1; + checkL -= table->subtables[z].keys - isolated; + } + } + assert(L == checkL); +#endif + gxtop = table->subtables[x].next; + if (cuddSymmCheck(table,x,y)) { + /* Symmetry found, attach symm groups */ + table->subtables[x].next = y; + i = table->subtables[y].next; + while (table->subtables[i].next != (unsigned) y) + i = table->subtables[i].next; + table->subtables[i].next = gxtop; + } else if (table->subtables[x].next == (unsigned) x && + table->subtables[y].next == (unsigned) y) { + /* x and y have self symmetry */ + xindex = table->invperm[x]; + size = cuddSwapInPlace(table,x,y); +#ifdef DD_DEBUG + assert(table->subtables[x].next == (unsigned) x); + assert(table->subtables[y].next == (unsigned) y); +#endif + if (size == 0) goto ddSymmSiftingUpOutOfMem; + /* Update the lower bound. */ + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[xindex]->ref == 1; + L += table->subtables[y].keys - isolated; + } + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddSymmSiftingUpOutOfMem; + move->x = x; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + if ((double) size > (double) limitSize * table->maxGrowth) + return(moves); + if (size < limitSize) limitSize = size; + } else { /* Group move */ + size = ddSymmGroupMove(table,x,y,&moves); + if (size == 0) goto ddSymmSiftingUpOutOfMem; + /* Update the lower bound. */ + z = moves->y; + do { + zindex = table->invperm[z]; + if (cuddTestInteract(table,zindex,yindex)) { + isolated = table->vars[zindex]->ref == 1; + L += table->subtables[z].keys - isolated; + } + z = table->subtables[z].next; + } while (z != (int) moves->y); + if ((double) size > (double) limitSize * table->maxGrowth) + return(moves); + if (size < limitSize) limitSize = size; + } + y = gxtop; + x = cuddNextLow(table,y); + } + + return(moves); + +ddSymmSiftingUpOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *) moves); + moves = move; + } + return(MV_OOM); + +} /* end of ddSymmSiftingUp */ + + +/**Function******************************************************************** + + Synopsis [Moves x down until either it reaches the bound (xHigh) or + the size of the DD heap increases too much.] + + Description [Moves x down until either it reaches the bound (xHigh) + or the size of the DD heap increases too much. Assumes that x is the + bottom of a symmetry group. Checks x for symmetry to the adjacent + variables. If symmetry is found, the symmetry group of x is merged + with the symmetry group of the other variable. Returns the set of + moves in case of success; MV_OOM if memory is full.] + + SideEffects [None] + +******************************************************************************/ +static Move * +ddSymmSiftingDown( + DdManager * table, + int x, + int xHigh) +{ + Move *moves; + Move *move; + int y; + int size; + int limitSize; + int gxtop,gybot; + int R; /* upper bound on node decrease */ + int xindex, yindex; + int isolated; + int z; + int zindex; +#ifdef DD_DEBUG + int checkR; +#endif + + moves = NULL; + /* Initialize R */ + xindex = table->invperm[x]; + gxtop = table->subtables[x].next; + limitSize = size = table->keys - table->isolated; + R = 0; + for (z = xHigh; z > gxtop; z--) { + zindex = table->invperm[z]; + if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) { + isolated = table->vars[zindex]->ref == 1; + R += table->subtables[z].keys - isolated; + } + } + + y = cuddNextHigh(table,x); + while (y <= xHigh && size - R < limitSize) { +#ifdef DD_DEBUG + gxtop = table->subtables[x].next; + checkR = 0; + for (z = xHigh; z > gxtop; z--) { + zindex = table->invperm[z]; + if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) { + isolated = table->vars[zindex]->ref == 1; + checkR += table->subtables[z].keys - isolated; + } + } + assert(R == checkR); +#endif + gybot = table->subtables[y].next; + while (table->subtables[gybot].next != (unsigned) y) + gybot = table->subtables[gybot].next; + if (cuddSymmCheck(table,x,y)) { + /* Symmetry found, attach symm groups */ + gxtop = table->subtables[x].next; + table->subtables[x].next = y; + table->subtables[gybot].next = gxtop; + } else if (table->subtables[x].next == (unsigned) x && + table->subtables[y].next == (unsigned) y) { + /* x and y have self symmetry */ + /* Update upper bound on node decrease. */ + yindex = table->invperm[y]; + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[yindex]->ref == 1; + R -= table->subtables[y].keys - isolated; + } + size = cuddSwapInPlace(table,x,y); +#ifdef DD_DEBUG + assert(table->subtables[x].next == (unsigned) x); + assert(table->subtables[y].next == (unsigned) y); +#endif + if (size == 0) goto ddSymmSiftingDownOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddSymmSiftingDownOutOfMem; + move->x = x; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + if ((double) size > (double) limitSize * table->maxGrowth) + return(moves); + if (size < limitSize) limitSize = size; + } else { /* Group move */ + /* Update upper bound on node decrease: first phase. */ + gxtop = table->subtables[x].next; + z = gxtop + 1; + do { + zindex = table->invperm[z]; + if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) { + isolated = table->vars[zindex]->ref == 1; + R -= table->subtables[z].keys - isolated; + } + z++; + } while (z <= gybot); + size = ddSymmGroupMove(table,x,y,&moves); + if (size == 0) goto ddSymmSiftingDownOutOfMem; + if ((double) size > (double) limitSize * table->maxGrowth) + return(moves); + if (size < limitSize) limitSize = size; + /* Update upper bound on node decrease: second phase. */ + gxtop = table->subtables[gybot].next; + for (z = gxtop + 1; z <= gybot; z++) { + zindex = table->invperm[z]; + if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) { + isolated = table->vars[zindex]->ref == 1; + R += table->subtables[z].keys - isolated; + } + } + } + x = gybot; + y = cuddNextHigh(table,x); + } + + return(moves); + +ddSymmSiftingDownOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *) moves); + moves = move; + } + return(MV_OOM); + +} /* end of ddSymmSiftingDown */ + + +/**Function******************************************************************** + + Synopsis [Swaps two groups.] + + Description [Swaps two groups. x is assumed to be the bottom variable + of the first group. y is assumed to be the top variable of the second + group. Updates the list of moves. Returns the number of keys in the + table if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddSymmGroupMove( + DdManager * table, + int x, + int y, + Move ** moves) +{ + Move *move; + int size; + int i,j; + int xtop,xbot,xsize,ytop,ybot,ysize,newxtop; + int swapx,swapy; + +#if DD_DEBUG + assert(x < y); /* we assume that x < y */ +#endif + /* Find top, bottom, and size for the two groups. */ + xbot = x; + xtop = table->subtables[x].next; + xsize = xbot - xtop + 1; + ybot = y; + while ((unsigned) ybot < table->subtables[ybot].next) + ybot = table->subtables[ybot].next; + ytop = y; + ysize = ybot - ytop + 1; + + /* Sift the variables of the second group up through the first group. */ + for (i = 1; i <= ysize; i++) { + for (j = 1; j <= xsize; j++) { + size = cuddSwapInPlace(table,x,y); + if (size == 0) return(0); + swapx = x; swapy = y; + y = x; + x = y - 1; + } + y = ytop + i; + x = y - 1; + } + + /* fix symmetries */ + y = xtop; /* ytop is now where xtop used to be */ + for (i = 0; i < ysize-1 ; i++) { + table->subtables[y].next = y + 1; + y = y + 1; + } + table->subtables[y].next = xtop; /* y is bottom of its group, join */ + /* its symmetry to top of its group */ + x = y + 1; + newxtop = x; + for (i = 0; i < xsize - 1 ; i++) { + table->subtables[x].next = x + 1; + x = x + 1; + } + table->subtables[x].next = newxtop; /* x is bottom of its group, join */ + /* its symmetry to top of its group */ + /* Store group move */ + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) return(0); + move->x = swapx; + move->y = swapy; + move->size = size; + move->next = *moves; + *moves = move; + + return(size); + +} /* end of ddSymmGroupMove */ + + +/**Function******************************************************************** + + Synopsis [Undoes the swap of two groups.] + + Description [Undoes the swap of two groups. x is assumed to be the + bottom variable of the first group. y is assumed to be the top + variable of the second group. Returns the number of keys in the table + if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddSymmGroupMoveBackward( + DdManager * table, + int x, + int y) +{ + int size; + int i,j; + int xtop,xbot,xsize,ytop,ybot,ysize,newxtop; + +#if DD_DEBUG + assert(x < y); /* We assume that x < y */ +#endif + + /* Find top, bottom, and size for the two groups. */ + xbot = x; + xtop = table->subtables[x].next; + xsize = xbot - xtop + 1; + ybot = y; + while ((unsigned) ybot < table->subtables[ybot].next) + ybot = table->subtables[ybot].next; + ytop = y; + ysize = ybot - ytop + 1; + + /* Sift the variables of the second group up through the first group. */ + for (i = 1; i <= ysize; i++) { + for (j = 1; j <= xsize; j++) { + size = cuddSwapInPlace(table,x,y); + if (size == 0) return(0); + y = x; + x = cuddNextLow(table,y); + } + y = ytop + i; + x = y - 1; + } + + /* Fix symmetries. */ + y = xtop; + for (i = 0; i < ysize-1 ; i++) { + table->subtables[y].next = y + 1; + y = y + 1; + } + table->subtables[y].next = xtop; /* y is bottom of its group, join */ + /* its symmetry to top of its group */ + x = y + 1; + newxtop = x; + for (i = 0; i < xsize-1 ; i++) { + table->subtables[x].next = x + 1; + x = x + 1; + } + table->subtables[x].next = newxtop; /* x is bottom of its group, join */ + /* its symmetry to top of its group */ + + return(size); + +} /* end of ddSymmGroupMoveBackward */ + + +/**Function******************************************************************** + + Synopsis [Given a set of moves, returns the DD heap to the position + giving the minimum size.] + + Description [Given a set of moves, returns the DD heap to the + position giving the minimum size. In case of ties, returns to the + closest position giving the minimum size. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddSymmSiftingBackward( + DdManager * table, + Move * moves, + int size) +{ + Move *move; + int res; + + for (move = moves; move != NULL; move = move->next) { + if (move->size < size) { + size = move->size; + } + } + + for (move = moves; move != NULL; move = move->next) { + if (move->size == size) return(1); + if (table->subtables[move->x].next == move->x && table->subtables[move->y].next == move->y) { + res = cuddSwapInPlace(table,(int)move->x,(int)move->y); +#ifdef DD_DEBUG + assert(table->subtables[move->x].next == move->x); + assert(table->subtables[move->y].next == move->y); +#endif + } else { /* Group move necessary */ + res = ddSymmGroupMoveBackward(table,(int)move->x,(int)move->y); + } + if (!res) return(0); + } + + return(1); + +} /* end of ddSymmSiftingBackward */ + + +/**Function******************************************************************** + + Synopsis [Counts numbers of symmetric variables and symmetry + groups.] + + Description [] + + SideEffects [None] + +******************************************************************************/ +static void +ddSymmSummary( + DdManager * table, + int lower, + int upper, + int * symvars, + int * symgroups) +{ + int i,x,gbot; + int TotalSymm = 0; + int TotalSymmGroups = 0; + + for (i = lower; i <= upper; i++) { + if (table->subtables[i].next != (unsigned) i) { + TotalSymmGroups++; + x = i; + do { + TotalSymm++; + gbot = x; + x = table->subtables[x].next; + } while (x != i); +#ifdef DD_DEBUG + assert(table->subtables[gbot].next == (unsigned) i); +#endif + i = gbot; + } + } + *symvars = TotalSymm; + *symgroups = TotalSymmGroups; + + return; + +} /* end of ddSymmSummary */ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddTable.c b/abc_with_bb_support/src/bdd/cudd/cuddTable.c new file mode 100644 index 000000000..94775acdd --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddTable.c @@ -0,0 +1,3141 @@ +/**CFile*********************************************************************** + + FileName [cuddTable.c] + + PackageName [cudd] + + Synopsis [Unique table management functions.] + + Description [External procedures included in this module: +
          +
        • Cudd_Prime() +
        + Internal procedures included in this module: +
          +
        • cuddAllocNode() +
        • cuddInitTable() +
        • cuddFreeTable() +
        • cuddGarbageCollect() +
        • cuddGarbageCollectZdd() +
        • cuddZddGetNode() +
        • cuddZddGetNodeIVO() +
        • cuddUniqueInter() +
        • cuddUniqueInterIVO() +
        • cuddUniqueInterZdd() +
        • cuddUniqueConst() +
        • cuddRehash() +
        • cuddShrinkSubtable() +
        • cuddInsertSubtables() +
        • cuddDestroySubtables() +
        • cuddResizeTableZdd() +
        • cuddSlowTableGrowth() +
        + Static procedures included in this module: +
          +
        • ddRehashZdd() +
        • ddResizeTable() +
        • cuddFindParent() +
        • cuddOrderedInsert() +
        • cuddOrderedThread() +
        • cuddRotateLeft() +
        • cuddRotateRight() +
        • cuddDoRebalance() +
        • cuddCheckCollisionOrdering() +
        ] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef DD_UNSORTED_FREE_LIST +/* Constants for red/black trees. */ +#define DD_STACK_SIZE 128 +#define DD_RED 0 +#define DD_BLACK 1 +#define DD_PAGE_SIZE 8192 +#define DD_PAGE_MASK ~(DD_PAGE_SIZE - 1) +#define DD_INSERT_COMPARE(x,y) \ + (((ptruint) (x) & DD_PAGE_MASK) - ((ptruint) (y) & DD_PAGE_MASK)) +#endif + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/* This is a hack for when CUDD_VALUE_TYPE is double */ +typedef union hack { + CUDD_VALUE_TYPE value; + unsigned int bits[2]; +} hack; + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddTable.c,v 1.1.1.1 2003/02/24 22:23:53 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +#ifndef DD_UNSORTED_FREE_LIST +/* Macros for red/black trees. */ +#define DD_COLOR(p) ((p)->index) +#define DD_IS_BLACK(p) ((p)->index == DD_BLACK) +#define DD_IS_RED(p) ((p)->index == DD_RED) +#define DD_LEFT(p) cuddT(p) +#define DD_RIGHT(p) cuddE(p) +#define DD_NEXT(p) ((p)->next) +#endif + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static void ddRehashZdd ARGS((DdManager *unique, int i)); +static int ddResizeTable ARGS((DdManager *unique, int index)); +static int cuddFindParent ARGS((DdManager *table, DdNode *node)); +DD_INLINE static void ddFixLimits ARGS((DdManager *unique)); +static void cuddOrderedInsert ARGS((DdNodePtr *root, DdNodePtr node)); +static DdNode * cuddOrderedThread ARGS((DdNode *root, DdNode *list)); +static void cuddRotateLeft ARGS((DdNodePtr *nodeP)); +static void cuddRotateRight ARGS((DdNodePtr *nodeP)); +static void cuddDoRebalance ARGS((DdNodePtr **stack, int stackN)); +static void ddPatchTree ARGS((DdManager *dd, MtrNode *treenode)); +#ifdef DD_DEBUG +static int cuddCheckCollisionOrdering ARGS((DdManager *unique, int i, int j)); +#endif +static void ddReportRefMess ARGS((DdManager *unique, int i, char *caller)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Returns the next prime >= p.] + + Description [] + + SideEffects [None] + +******************************************************************************/ +unsigned int +Cudd_Prime( + unsigned int p) +{ + int i,pn; + + p--; + do { + p++; + if (p&1) { + pn = 1; + i = 3; + while ((unsigned) (i * i) <= p) { + if (p % i == 0) { + pn = 0; + break; + } + i += 2; + } + } else { + pn = 0; + } + } while (!pn); + return(p); + +} /* end of Cudd_Prime */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Fast storage allocation for DdNodes in the table.] + + Description [Fast storage allocation for DdNodes in the table. The + first 4 bytes of a chunk contain a pointer to the next block; the + rest contains DD_MEM_CHUNK spaces for DdNodes. Returns a pointer to + a new node if successful; NULL is memory is full.] + + SideEffects [None] + + SeeAlso [cuddDynamicAllocNode] + +******************************************************************************/ +DdNode * +cuddAllocNode( + DdManager * unique) +{ + int i; + DdNodePtr *mem; + DdNode *list, *node; + extern void (*MMoutOfMemory)(long); + void (*saveHandler)(long); + + if (unique->nextFree == NULL) { /* free list is empty */ + /* Check for exceeded limits. */ + if ((unique->keys - unique->dead) + (unique->keysZ - unique->deadZ) > + unique->maxLive) { + unique->errorCode = CUDD_TOO_MANY_NODES; + return(NULL); + } + if (unique->stash == NULL || unique->memused > unique->maxmemhard) { + (void) cuddGarbageCollect(unique,1); + mem = NULL; + } + if (unique->nextFree == NULL) { + if (unique->memused > unique->maxmemhard) { + unique->errorCode = CUDD_MAX_MEM_EXCEEDED; + return(NULL); + } + /* Try to allocate a new block. */ + saveHandler = MMoutOfMemory; + MMoutOfMemory = Cudd_OutOfMem; + mem = (DdNodePtr *) ALLOC(DdNode,DD_MEM_CHUNK + 1); + MMoutOfMemory = saveHandler; + if (mem == NULL) { + /* No more memory: Try collecting garbage. If this succeeds, + ** we end up with mem still NULL, but unique->nextFree != + ** NULL. */ + if (cuddGarbageCollect(unique,1) == 0) { + /* Last resort: Free the memory stashed away, if there + ** any. If this succeeeds, mem != NULL and + ** unique->nextFree still NULL. */ + if (unique->stash != NULL) { + FREE(unique->stash); + unique->stash = NULL; + /* Inhibit resizing of tables. */ + cuddSlowTableGrowth(unique); + /* Now try again. */ + mem = (DdNodePtr *) ALLOC(DdNode,DD_MEM_CHUNK + 1); + } + if (mem == NULL) { + /* Out of luck. Call the default handler to do + ** whatever it specifies for a failed malloc. + ** If this handler returns, then set error code, + ** print warning, and return. */ + (*MMoutOfMemory)(sizeof(DdNode)*(DD_MEM_CHUNK + 1)); + unique->errorCode = CUDD_MEMORY_OUT; +#ifdef DD_VERBOSE + (void) fprintf(unique->err, + "cuddAllocNode: out of memory"); + (void) fprintf(unique->err, "Memory in use = %ld\n", + unique->memused); +#endif + return(NULL); + } + } + } + if (mem != NULL) { /* successful allocation; slice memory */ + ptruint offset; + unique->memused += (DD_MEM_CHUNK + 1) * sizeof(DdNode); + mem[0] = (DdNodePtr) unique->memoryList; + unique->memoryList = mem; + + /* Here we rely on the fact that a DdNode is as large + ** as 4 pointers. */ + offset = (ptruint) mem & (sizeof(DdNode) - 1); + mem += (sizeof(DdNode) - offset) / sizeof(DdNodePtr); + assert(((ptruint) mem & (sizeof(DdNode) - 1)) == 0); + list = (DdNode *) mem; + + i = 1; + do { + list[i - 1].next = &list[i]; + } while (++i < DD_MEM_CHUNK); + + list[DD_MEM_CHUNK-1].next = NULL; + + unique->nextFree = &list[0]; + } + } + } + unique->allocated++; + node = unique->nextFree; + unique->nextFree = node->next; + return(node); + +} /* end of cuddAllocNode */ + + +/**Function******************************************************************** + + Synopsis [Creates and initializes the unique table.] + + Description [Creates and initializes the unique table. Returns a pointer + to the table if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_Init cuddFreeTable] + +******************************************************************************/ +DdManager * +cuddInitTable( + unsigned int numVars /* Initial number of BDD variables (and subtables) */, + unsigned int numVarsZ /* Initial number of ZDD variables (and subtables) */, + unsigned int numSlots /* Initial size of the BDD subtables */, + unsigned int looseUpTo /* Limit for fast table growth */) +{ + DdManager *unique = ALLOC(DdManager,1); + int i, j; + DdNodePtr *nodelist; + DdNode *sentinel; + unsigned int slots; + int shift; + + if (unique == NULL) { + return(NULL); + } + sentinel = &(unique->sentinel); + sentinel->ref = 0; + sentinel->index = 0; + cuddT(sentinel) = NULL; + cuddE(sentinel) = NULL; + sentinel->next = NULL; + unique->epsilon = DD_EPSILON; + unique->maxGrowth = DD_MAX_REORDER_GROWTH; + unique->maxGrowthAlt = 2.0 * DD_MAX_REORDER_GROWTH; + unique->reordCycle = 0; /* do not use alternate threshold */ + unique->size = numVars; + unique->sizeZ = numVarsZ; + unique->maxSize = ddMax(DD_DEFAULT_RESIZE, numVars); + unique->maxSizeZ = ddMax(DD_DEFAULT_RESIZE, numVarsZ); + + /* Adjust the requested number of slots to a power of 2. */ + slots = 8; + while (slots < numSlots) { + slots <<= 1; + } + unique->initSlots = slots; + shift = sizeof(int) * 8 - cuddComputeFloorLog2(slots); + + unique->slots = (numVars + numVarsZ + 1) * slots; + unique->keys = 0; + unique->maxLive = ~0; /* very large number */ + unique->keysZ = 0; + unique->dead = 0; + unique->deadZ = 0; + unique->gcFrac = DD_GC_FRAC_HI; + unique->minDead = (unsigned) (DD_GC_FRAC_HI * (double) unique->slots); + unique->looseUpTo = looseUpTo; + unique->gcEnabled = 1; + unique->allocated = 0; + unique->reclaimed = 0; + unique->subtables = ALLOC(DdSubtable,unique->maxSize); + if (unique->subtables == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + unique->subtableZ = ALLOC(DdSubtable,unique->maxSizeZ); + if (unique->subtableZ == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + unique->perm = ALLOC(int,unique->maxSize); + if (unique->perm == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + unique->invperm = ALLOC(int,unique->maxSize); + if (unique->invperm == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + unique->permZ = ALLOC(int,unique->maxSizeZ); + if (unique->permZ == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + unique->invpermZ = ALLOC(int,unique->maxSizeZ); + if (unique->invpermZ == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + unique->map = NULL; + unique->stack = ALLOC(DdNodePtr,ddMax(unique->maxSize,unique->maxSizeZ)+1); + if (unique->stack == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + unique->stack[0] = NULL; /* to suppress harmless UMR */ + +#ifndef DD_NO_DEATH_ROW + unique->deathRowDepth = 1 << cuddComputeFloorLog2(unique->looseUpTo >> 2); + unique->deathRow = ALLOC(DdNodePtr,unique->deathRowDepth); + if (unique->deathRow == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (i = 0; i < unique->deathRowDepth; i++) { + unique->deathRow[i] = NULL; + } + unique->nextDead = 0; + unique->deadMask = unique->deathRowDepth - 1; +#endif + + for (i = 0; (unsigned) i < numVars; i++) { + unique->subtables[i].slots = slots; + unique->subtables[i].shift = shift; + unique->subtables[i].keys = 0; + unique->subtables[i].dead = 0; + unique->subtables[i].maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; + unique->subtables[i].bindVar = 0; + unique->subtables[i].varType = CUDD_VAR_PRIMARY_INPUT; + unique->subtables[i].pairIndex = 0; + unique->subtables[i].varHandled = 0; + unique->subtables[i].varToBeGrouped = CUDD_LAZY_NONE; + + nodelist = unique->subtables[i].nodelist = ALLOC(DdNodePtr,slots); + if (nodelist == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + for (j = 0; (unsigned) j < slots; j++) { + nodelist[j] = sentinel; + } + unique->perm[i] = i; + unique->invperm[i] = i; + } + for (i = 0; (unsigned) i < numVarsZ; i++) { + unique->subtableZ[i].slots = slots; + unique->subtableZ[i].shift = shift; + unique->subtableZ[i].keys = 0; + unique->subtableZ[i].dead = 0; + unique->subtableZ[i].maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; + nodelist = unique->subtableZ[i].nodelist = ALLOC(DdNodePtr,slots); + if (nodelist == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + for (j = 0; (unsigned) j < slots; j++) { + nodelist[j] = NULL; + } + unique->permZ[i] = i; + unique->invpermZ[i] = i; + } + unique->constants.slots = slots; + unique->constants.shift = shift; + unique->constants.keys = 0; + unique->constants.dead = 0; + unique->constants.maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; + nodelist = unique->constants.nodelist = ALLOC(DdNodePtr,slots); + if (nodelist == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + for (j = 0; (unsigned) j < slots; j++) { + nodelist[j] = NULL; + } + + unique->memoryList = NULL; + unique->nextFree = NULL; + + unique->memused = sizeof(DdManager) + (unique->maxSize + unique->maxSizeZ) + * (sizeof(DdSubtable) + 2 * sizeof(int)) + (numVars + 1) * + slots * sizeof(DdNodePtr) + + (ddMax(unique->maxSize,unique->maxSizeZ) + 1) * sizeof(DdNodePtr); +#ifndef DD_NO_DEATH_ROW + unique->memused += unique->deathRowDepth * sizeof(DdNodePtr); +#endif + + /* Initialize fields concerned with automatic dynamic reordering */ + unique->reorderings = 0; + unique->autoDyn = 0; /* initially disabled */ + unique->autoDynZ = 0; /* initially disabled */ + unique->realign = 0; /* initially disabled */ + unique->realignZ = 0; /* initially disabled */ + unique->reordered = 0; + unique->autoMethod = CUDD_REORDER_SIFT; + unique->autoMethodZ = CUDD_REORDER_SIFT; + unique->nextDyn = DD_FIRST_REORDER; + unique->countDead = ~0; + unique->siftMaxVar = DD_SIFT_MAX_VAR; + unique->siftMaxSwap = DD_SIFT_MAX_SWAPS; + unique->tree = NULL; + unique->treeZ = NULL; + unique->groupcheck = CUDD_GROUP_CHECK7; + unique->recomb = DD_DEFAULT_RECOMB; + unique->symmviolation = 0; + unique->arcviolation = 0; + unique->populationSize = 0; + unique->numberXovers = 0; + unique->linear = NULL; + unique->linearSize = 0; + + /* Initialize ZDD universe. */ + unique->univ = (DdNodePtr *)NULL; + + /* Initialize auxiliary fields. */ + unique->localCaches = NULL; + unique->preGCHook = NULL; + unique->postGCHook = NULL; + unique->preReorderingHook = NULL; + unique->postReorderingHook = NULL; + unique->out = stdout; + unique->err = stderr; + unique->errorCode = CUDD_NO_ERROR; + + /* Initialize statistical counters. */ + unique->maxmemhard = (long) ((~ (unsigned long) 0) >> 1); + unique->garbageCollections = 0; + unique->GCTime = 0; + unique->reordTime = 0; +#ifdef DD_STATS + unique->nodesDropped = 0; + unique->nodesFreed = 0; +#endif + unique->peakLiveNodes = 0; +#ifdef DD_UNIQUE_PROFILE + unique->uniqueLookUps = 0; + unique->uniqueLinks = 0; +#endif +#ifdef DD_COUNT + unique->recursiveCalls = 0; + unique->swapSteps = 0; +#ifdef DD_STATS + unique->nextSample = 250000; +#endif +#endif + + return(unique); + +} /* end of cuddInitTable */ + + +/**Function******************************************************************** + + Synopsis [Frees the resources associated to a unique table.] + + Description [] + + SideEffects [None] + + SeeAlso [cuddInitTable] + +******************************************************************************/ +void +cuddFreeTable( + DdManager * unique) +{ + DdNodePtr *next; + DdNodePtr *memlist = unique->memoryList; + int i; + + if (unique->univ != NULL) cuddZddFreeUniv(unique); + while (memlist != NULL) { + next = (DdNodePtr *) memlist[0]; /* link to next block */ + FREE(memlist); + memlist = next; + } + unique->nextFree = NULL; + unique->memoryList = NULL; + + for (i = 0; i < unique->size; i++) { + FREE(unique->subtables[i].nodelist); + } + for (i = 0; i < unique->sizeZ; i++) { + FREE(unique->subtableZ[i].nodelist); + } + FREE(unique->constants.nodelist); + FREE(unique->subtables); + FREE(unique->subtableZ); + FREE(unique->acache); + FREE(unique->perm); + FREE(unique->permZ); + FREE(unique->invperm); + FREE(unique->invpermZ); + FREE(unique->vars); + if (unique->map != NULL) FREE(unique->map); + FREE(unique->stack); +#ifndef DD_NO_DEATH_ROW + FREE(unique->deathRow); +#endif + if (unique->tree != NULL) Mtr_FreeTree(unique->tree); + if (unique->treeZ != NULL) Mtr_FreeTree(unique->treeZ); + if (unique->linear != NULL) FREE(unique->linear); + while (unique->preGCHook != NULL) + Cudd_RemoveHook(unique,unique->preGCHook->f,CUDD_PRE_GC_HOOK); + while (unique->postGCHook != NULL) + Cudd_RemoveHook(unique,unique->postGCHook->f,CUDD_POST_GC_HOOK); + while (unique->preReorderingHook != NULL) + Cudd_RemoveHook(unique,unique->preReorderingHook->f, + CUDD_PRE_REORDERING_HOOK); + while (unique->postReorderingHook != NULL) + Cudd_RemoveHook(unique,unique->postReorderingHook->f, + CUDD_POST_REORDERING_HOOK); + FREE(unique); + +} /* end of cuddFreeTable */ + + +/**Function******************************************************************** + + Synopsis [Performs garbage collection on a unique table.] + + Description [Performs garbage collection on a unique table. + If clearCache is 0, the cache is not cleared. This should only be + specified if the cache has been cleared right before calling + cuddGarbageCollect. (As in the case of dynamic reordering.) + Returns the total number of deleted nodes.] + + SideEffects [None] + + SeeAlso [cuddGarbageCollectZdd] + +******************************************************************************/ +int +cuddGarbageCollect( + DdManager * unique, + int clearCache) +{ + DdHook *hook; + DdCache *cache = unique->cache; + DdNode *sentinel = &(unique->sentinel); + DdNodePtr *nodelist; + int i, j, deleted, totalDeleted; + DdCache *c; + DdNode *node,*next; + DdNodePtr *lastP; + int slots; + long localTime; +#ifndef DD_UNSORTED_FREE_LIST + DdNodePtr tree; +#endif + +#ifndef DD_NO_DEATH_ROW + cuddClearDeathRow(unique); +#endif + + hook = unique->preGCHook; + while (hook != NULL) { + int res = (hook->f)(unique,"BDD",NULL); + if (res == 0) return(0); + hook = hook->next; + } + + if (unique->dead == 0) { + hook = unique->postGCHook; + while (hook != NULL) { + int res = (hook->f)(unique,"BDD",NULL); + if (res == 0) return(0); + hook = hook->next; + } + return(0); + } + + /* If many nodes are being reclaimed, we want to resize the tables + ** more aggressively, to reduce the frequency of garbage collection. + */ + if (clearCache && unique->gcFrac == DD_GC_FRAC_LO && + unique->slots <= unique->looseUpTo && unique->stash != NULL) { + unique->minDead = (unsigned) (DD_GC_FRAC_HI * (double) unique->slots); +#ifdef DD_VERBOSE + (void) fprintf(unique->err,"GC fraction = %.2f\t", DD_GC_FRAC_HI); + (void) fprintf(unique->err,"minDead = %d\n", unique->minDead); +#endif + unique->gcFrac = DD_GC_FRAC_HI; + return(0); + } + + localTime = util_cpu_time(); + + unique->garbageCollections++; +#ifdef DD_VERBOSE + (void) fprintf(unique->err, + "garbage collecting (%d dead out of %d, min %d)...", + unique->dead, unique->keys, unique->minDead); +#endif + + /* Remove references to garbage collected nodes from the cache. */ + if (clearCache) { + slots = unique->cacheSlots; + for (i = 0; i < slots; i++) { + c = &cache[i]; + if (c->data != NULL) { + if (cuddClean(c->f)->ref == 0 || + cuddClean(c->g)->ref == 0 || + (((ptruint)c->f & 0x2) && Cudd_Regular(c->h)->ref == 0) || + (c->data != DD_NON_CONSTANT && + Cudd_Regular(c->data)->ref == 0)) { + c->data = NULL; + unique->cachedeletions++; + } + } + } + cuddLocalCacheClearDead(unique); + } + + /* Now return dead nodes to free list. Count them for sanity check. */ + totalDeleted = 0; +#ifndef DD_UNSORTED_FREE_LIST + tree = NULL; +#endif + + for (i = 0; i < unique->size; i++) { + if (unique->subtables[i].dead == 0) continue; + nodelist = unique->subtables[i].nodelist; + + deleted = 0; + slots = unique->subtables[i].slots; + for (j = 0; j < slots; j++) { + lastP = &(nodelist[j]); + node = *lastP; + while (node != sentinel) { + next = node->next; + if (node->ref == 0) { + deleted++; +#ifndef DD_UNSORTED_FREE_LIST +#ifdef __osf__ +#pragma pointer_size save +#pragma pointer_size short +#endif + cuddOrderedInsert(&tree,node); +#ifdef __osf__ +#pragma pointer_size restore +#endif +#else + cuddDeallocNode(unique,node); +#endif + } else { + *lastP = node; + lastP = &(node->next); + } + node = next; + } + *lastP = sentinel; + } + if ((unsigned) deleted != unique->subtables[i].dead) { + ddReportRefMess(unique, i, "cuddGarbageCollect"); + } + totalDeleted += deleted; + unique->subtables[i].keys -= deleted; + unique->subtables[i].dead = 0; + } + if (unique->constants.dead != 0) { + nodelist = unique->constants.nodelist; + deleted = 0; + slots = unique->constants.slots; + for (j = 0; j < slots; j++) { + lastP = &(nodelist[j]); + node = *lastP; + while (node != NULL) { + next = node->next; + if (node->ref == 0) { + deleted++; +#ifndef DD_UNSORTED_FREE_LIST +#ifdef __osf__ +#pragma pointer_size save +#pragma pointer_size short +#endif + cuddOrderedInsert(&tree,node); +#ifdef __osf__ +#pragma pointer_size restore +#endif +#else + cuddDeallocNode(unique,node); +#endif + } else { + *lastP = node; + lastP = &(node->next); + } + node = next; + } + *lastP = NULL; + } + if ((unsigned) deleted != unique->constants.dead) { + ddReportRefMess(unique, CUDD_CONST_INDEX, "cuddGarbageCollect"); + } + totalDeleted += deleted; + unique->constants.keys -= deleted; + unique->constants.dead = 0; + } + if ((unsigned) totalDeleted != unique->dead) { + ddReportRefMess(unique, -1, "cuddGarbageCollect"); + } + unique->keys -= totalDeleted; + unique->dead = 0; +#ifdef DD_STATS + unique->nodesFreed += (double) totalDeleted; +#endif + +#ifndef DD_UNSORTED_FREE_LIST + unique->nextFree = cuddOrderedThread(tree,unique->nextFree); +#endif + + unique->GCTime += util_cpu_time() - localTime; + + hook = unique->postGCHook; + while (hook != NULL) { + int res = (hook->f)(unique,"BDD",NULL); + if (res == 0) return(0); + hook = hook->next; + } + +#ifdef DD_VERBOSE + (void) fprintf(unique->err," done\n"); +#endif + + return(totalDeleted); + +} /* end of cuddGarbageCollect */ + + +/**Function******************************************************************** + + Synopsis [Performs garbage collection on a ZDD unique table.] + + Description [Performs garbage collection on a ZDD unique table. + If clearCache is 0, the cache is not cleared. This should only be + specified if the cache has been cleared right before calling + cuddGarbageCollectZdd. (As in the case of dynamic reordering.) + Returns the total number of deleted nodes.] + + SideEffects [None] + + SeeAlso [cuddGarbageCollect] + +******************************************************************************/ +int +cuddGarbageCollectZdd( + DdManager * unique, + int clearCache) +{ + DdHook *hook; + DdCache *cache = unique->cache; + DdNodePtr *nodelist; + int i, j, deleted, totalDeleted; + DdCache *c; + DdNode *node,*next; + DdNodePtr *lastP; + int slots; + long localTime; +#ifndef DD_UNSORTED_FREE_LIST + DdNodePtr tree; +#endif + + hook = unique->preGCHook; + while (hook != NULL) { + int res = (hook->f)(unique,"ZDD",NULL); + if (res == 0) return(0); + hook = hook->next; + } + + if (unique->deadZ == 0) { + hook = unique->postGCHook; + while (hook != NULL) { + int res = (hook->f)(unique,"ZDD",NULL); + if (res == 0) return(0); + hook = hook->next; + } + return(0); + } + + /* If many nodes are being reclaimed, we want to resize the tables + ** more aggressively, to reduce the frequency of garbage collection. + */ + if (clearCache && unique->slots <= unique->looseUpTo) { + unique->minDead = (unsigned) (DD_GC_FRAC_HI * (double) unique->slots); +#ifdef DD_VERBOSE + if (unique->gcFrac == DD_GC_FRAC_LO) { + (void) fprintf(unique->err,"GC fraction = %.2f\t", + DD_GC_FRAC_HI); + (void) fprintf(unique->err,"minDead = %d\n", unique->minDead); + } +#endif + unique->gcFrac = DD_GC_FRAC_HI; + } + + localTime = util_cpu_time(); + + unique->garbageCollections++; +#ifdef DD_VERBOSE + (void) fprintf(unique->err,"garbage collecting (%d dead out of %d)...", + unique->deadZ,unique->keysZ); +#endif + + /* Remove references to garbage collected nodes from the cache. */ + if (clearCache) { + slots = unique->cacheSlots; + for (i = 0; i < slots; i++) { + c = &cache[i]; + if (c->data != NULL) { + if (cuddClean(c->f)->ref == 0 || + cuddClean(c->g)->ref == 0 || + (((ptruint)c->f & 0x2) && Cudd_Regular(c->h)->ref == 0) || + (c->data != DD_NON_CONSTANT && + Cudd_Regular(c->data)->ref == 0)) { + c->data = NULL; + unique->cachedeletions++; + } + } + } + } + + /* Now return dead nodes to free list. Count them for sanity check. */ + totalDeleted = 0; +#ifndef DD_UNSORTED_FREE_LIST + tree = NULL; +#endif + + for (i = 0; i < unique->sizeZ; i++) { + if (unique->subtableZ[i].dead == 0) continue; + nodelist = unique->subtableZ[i].nodelist; + + deleted = 0; + slots = unique->subtableZ[i].slots; + for (j = 0; j < slots; j++) { + lastP = &(nodelist[j]); + node = *lastP; + while (node != NULL) { + next = node->next; + if (node->ref == 0) { + deleted++; +#ifndef DD_UNSORTED_FREE_LIST +#ifdef __osf__ +#pragma pointer_size save +#pragma pointer_size short +#endif + cuddOrderedInsert(&tree,node); +#ifdef __osf__ +#pragma pointer_size restore +#endif +#else + cuddDeallocNode(unique,node); +#endif + } else { + *lastP = node; + lastP = &(node->next); + } + node = next; + } + *lastP = NULL; + } + if ((unsigned) deleted != unique->subtableZ[i].dead) { + ddReportRefMess(unique, i, "cuddGarbageCollectZdd"); + } + totalDeleted += deleted; + unique->subtableZ[i].keys -= deleted; + unique->subtableZ[i].dead = 0; + } + + /* No need to examine the constant table for ZDDs. + ** If we did we should be careful not to count whatever dead + ** nodes we found there among the dead ZDD nodes. */ + if ((unsigned) totalDeleted != unique->deadZ) { + ddReportRefMess(unique, -1, "cuddGarbageCollectZdd"); + } + unique->keysZ -= totalDeleted; + unique->deadZ = 0; +#ifdef DD_STATS + unique->nodesFreed += (double) totalDeleted; +#endif + +#ifndef DD_UNSORTED_FREE_LIST + unique->nextFree = cuddOrderedThread(tree,unique->nextFree); +#endif + + unique->GCTime += util_cpu_time() - localTime; + + hook = unique->postGCHook; + while (hook != NULL) { + int res = (hook->f)(unique,"ZDD",NULL); + if (res == 0) return(0); + hook = hook->next; + } + +#ifdef DD_VERBOSE + (void) fprintf(unique->err," done\n"); +#endif + + return(totalDeleted); + +} /* end of cuddGarbageCollectZdd */ + + +/**Function******************************************************************** + + Synopsis [Wrapper for cuddUniqueInterZdd.] + + Description [Wrapper for cuddUniqueInterZdd, which applies the ZDD + reduction rule. Returns a pointer to the result node under normal + conditions; NULL if reordering occurred or memory was exhausted.] + + SideEffects [None] + + SeeAlso [cuddUniqueInterZdd] + +******************************************************************************/ +DdNode * +cuddZddGetNode( + DdManager * zdd, + int id, + DdNode * T, + DdNode * E) +{ + DdNode *node; + + if (T == DD_ZERO(zdd)) + return(E); + node = cuddUniqueInterZdd(zdd, id, T, E); + return(node); + +} /* end of cuddZddGetNode */ + + +/**Function******************************************************************** + + Synopsis [Wrapper for cuddUniqueInterZdd that is independent of variable + ordering.] + + Description [Wrapper for cuddUniqueInterZdd that is independent of + variable ordering (IVO). This function does not require parameter + index to precede the indices of the top nodes of g and h in the + variable order. Returns a pointer to the result node under normal + conditions; NULL if reordering occurred or memory was exhausted.] + + SideEffects [None] + + SeeAlso [cuddZddGetNode cuddZddIsop] + +******************************************************************************/ +DdNode * +cuddZddGetNodeIVO( + DdManager * dd, + int index, + DdNode * g, + DdNode * h) +{ + DdNode *f, *r, *t; + DdNode *zdd_one = DD_ONE(dd); + DdNode *zdd_zero = DD_ZERO(dd); + + f = cuddUniqueInterZdd(dd, index, zdd_one, zdd_zero); + if (f == NULL) { + return(NULL); + } + cuddRef(f); + t = cuddZddProduct(dd, f, g); + if (t == NULL) { + Cudd_RecursiveDerefZdd(dd, f); + return(NULL); + } + cuddRef(t); + Cudd_RecursiveDerefZdd(dd, f); + r = cuddZddUnion(dd, t, h); + if (r == NULL) { + Cudd_RecursiveDerefZdd(dd, t); + return(NULL); + } + cuddRef(r); + Cudd_RecursiveDerefZdd(dd, t); + + cuddDeref(r); + return(r); + +} /* end of cuddZddGetNodeIVO */ + + +/**Function******************************************************************** + + Synopsis [Checks the unique table for the existence of an internal node.] + + Description [Checks the unique table for the existence of an internal + node. If it does not exist, it creates a new one. Does not + modify the reference count of whatever is returned. A newly created + internal node comes back with a reference count 0. For a newly + created node, increments the reference counts of what T and E point + to. Returns a pointer to the new node if successful; NULL if memory + is exhausted or if reordering took place.] + + SideEffects [None] + + SeeAlso [cuddUniqueInterZdd] + +******************************************************************************/ +DdNode * +cuddUniqueInter( + DdManager * unique, + int index, + DdNode * T, + DdNode * E) +{ + int pos; + unsigned int level; + int retval; + DdNodePtr *nodelist; + DdNode *looking; + DdNodePtr *previousP; + DdSubtable *subtable; + int gcNumber; + +#ifdef DD_UNIQUE_PROFILE + unique->uniqueLookUps++; +#endif + + if (index >= unique->size) { + if (!ddResizeTable(unique,index)) return(NULL); + } + + level = unique->perm[index]; + subtable = &(unique->subtables[level]); + +#ifdef DD_DEBUG + assert(level < (unsigned) cuddI(unique,T->index)); + assert(level < (unsigned) cuddI(unique,Cudd_Regular(E)->index)); +#endif + + pos = ddHash(T, E, subtable->shift); + nodelist = subtable->nodelist; + previousP = &(nodelist[pos]); + looking = *previousP; + + while (T < cuddT(looking)) { + previousP = &(looking->next); + looking = *previousP; +#ifdef DD_UNIQUE_PROFILE + unique->uniqueLinks++; +#endif + } + while (T == cuddT(looking) && E < cuddE(looking)) { + previousP = &(looking->next); + looking = *previousP; +#ifdef DD_UNIQUE_PROFILE + unique->uniqueLinks++; +#endif + } + if (T == cuddT(looking) && E == cuddE(looking)) { + if (looking->ref == 0) { + cuddReclaim(unique,looking); + } + return(looking); + } + + /* countDead is 0 if deads should be counted and ~0 if they should not. */ + if (unique->autoDyn && + unique->keys - (unique->dead & unique->countDead) >= unique->nextDyn) { +#ifdef DD_DEBUG + retval = Cudd_DebugCheck(unique); + if (retval != 0) return(NULL); + retval = Cudd_CheckKeys(unique); + if (retval != 0) return(NULL); +#endif + retval = Cudd_ReduceHeap(unique,unique->autoMethod,10); /* 10 = whatever */ + if (retval == 0) unique->reordered = 2; +#ifdef DD_DEBUG + retval = Cudd_DebugCheck(unique); + if (retval != 0) unique->reordered = 2; + retval = Cudd_CheckKeys(unique); + if (retval != 0) unique->reordered = 2; +#endif + return(NULL); + } + + if (subtable->keys > subtable->maxKeys) { + if (unique->gcEnabled && + ((unique->dead > unique->minDead) || + ((unique->dead > unique->minDead / 2) && + (subtable->dead > subtable->keys * 0.95)))) { /* too many dead */ + (void) cuddGarbageCollect(unique,1); + } else { + cuddRehash(unique,(int)level); + } + /* Update pointer to insertion point. In the case of rehashing, + ** the slot may have changed. In the case of garbage collection, + ** the predecessor may have been dead. */ + pos = ddHash(T, E, subtable->shift); + nodelist = subtable->nodelist; + previousP = &(nodelist[pos]); + looking = *previousP; + + while (T < cuddT(looking)) { + previousP = &(looking->next); + looking = *previousP; +#ifdef DD_UNIQUE_PROFILE + unique->uniqueLinks++; +#endif + } + while (T == cuddT(looking) && E < cuddE(looking)) { + previousP = &(looking->next); + looking = *previousP; +#ifdef DD_UNIQUE_PROFILE + unique->uniqueLinks++; +#endif + } + } + + gcNumber = unique->garbageCollections; + looking = cuddAllocNode(unique); + if (looking == NULL) { + return(NULL); + } + unique->keys++; + subtable->keys++; + + if (gcNumber != unique->garbageCollections) { + DdNode *looking2; + pos = ddHash(T, E, subtable->shift); + nodelist = subtable->nodelist; + previousP = &(nodelist[pos]); + looking2 = *previousP; + + while (T < cuddT(looking2)) { + previousP = &(looking2->next); + looking2 = *previousP; +#ifdef DD_UNIQUE_PROFILE + unique->uniqueLinks++; +#endif + } + while (T == cuddT(looking2) && E < cuddE(looking2)) { + previousP = &(looking2->next); + looking2 = *previousP; +#ifdef DD_UNIQUE_PROFILE + unique->uniqueLinks++; +#endif + } + } + looking->ref = 0; + looking->index = index; + cuddT(looking) = T; + cuddE(looking) = E; + looking->next = *previousP; + *previousP = looking; + cuddSatInc(T->ref); /* we know T is a regular pointer */ + cuddRef(E); + +#ifdef DD_DEBUG + cuddCheckCollisionOrdering(unique,level,pos); +#endif + + return(looking); + +} /* end of cuddUniqueInter */ + + +/**Function******************************************************************** + + Synopsis [Wrapper for cuddUniqueInter that is independent of variable + ordering.] + + Description [Wrapper for cuddUniqueInter that is independent of + variable ordering (IVO). This function does not require parameter + index to precede the indices of the top nodes of T and E in the + variable order. Returns a pointer to the result node under normal + conditions; NULL if reordering occurred or memory was exhausted.] + + SideEffects [None] + + SeeAlso [cuddUniqueInter Cudd_MakeBddFromZddCover] + +******************************************************************************/ +DdNode * +cuddUniqueInterIVO( + DdManager * unique, + int index, + DdNode * T, + DdNode * E) +{ + DdNode *result; + DdNode *v; + + v = cuddUniqueInter(unique, index, DD_ONE(unique), + Cudd_Not(DD_ONE(unique))); + if (v == NULL) + return(NULL); + cuddRef(v); + result = cuddBddIteRecur(unique, v, T, E); + Cudd_RecursiveDeref(unique, v); + return(result); +} + + +/**Function******************************************************************** + + Synopsis [Checks the unique table for the existence of an internal + ZDD node.] + + Description [Checks the unique table for the existence of an internal + ZDD node. If it does not exist, it creates a new one. Does not + modify the reference count of whatever is returned. A newly created + internal node comes back with a reference count 0. For a newly + created node, increments the reference counts of what T and E point + to. Returns a pointer to the new node if successful; NULL if memory + is exhausted or if reordering took place.] + + SideEffects [None] + + SeeAlso [cuddUniqueInter] + +******************************************************************************/ +DdNode * +cuddUniqueInterZdd( + DdManager * unique, + int index, + DdNode * T, + DdNode * E) +{ + int pos; + unsigned int level; + int retval; + DdNodePtr *nodelist; + DdNode *looking; + DdSubtable *subtable; + +#ifdef DD_UNIQUE_PROFILE + unique->uniqueLookUps++; +#endif + + if (index >= unique->sizeZ) { + if (!cuddResizeTableZdd(unique,index)) return(NULL); + } + + level = unique->permZ[index]; + subtable = &(unique->subtableZ[level]); + +#ifdef DD_DEBUG + assert(level < (unsigned) cuddIZ(unique,T->index)); + assert(level < (unsigned) cuddIZ(unique,Cudd_Regular(E)->index)); +#endif + + if (subtable->keys > subtable->maxKeys) { + if (unique->gcEnabled && ((unique->deadZ > unique->minDead) || + (10 * subtable->dead > 9 * subtable->keys))) { /* too many dead */ + (void) cuddGarbageCollectZdd(unique,1); + } else { + ddRehashZdd(unique,(int)level); + } + } + + pos = ddHash(T, E, subtable->shift); + nodelist = subtable->nodelist; + looking = nodelist[pos]; + + while (looking != NULL) { + if (cuddT(looking) == T && cuddE(looking) == E) { + if (looking->ref == 0) { + cuddReclaimZdd(unique,looking); + } + return(looking); + } + looking = looking->next; +#ifdef DD_UNIQUE_PROFILE + unique->uniqueLinks++; +#endif + } + + /* countDead is 0 if deads should be counted and ~0 if they should not. */ + if (unique->autoDynZ && + unique->keysZ - (unique->deadZ & unique->countDead) >= unique->nextDyn) { +#ifdef DD_DEBUG + retval = Cudd_DebugCheck(unique); + if (retval != 0) return(NULL); + retval = Cudd_CheckKeys(unique); + if (retval != 0) return(NULL); +#endif + retval = Cudd_zddReduceHeap(unique,unique->autoMethodZ,10); /* 10 = whatever */ + if (retval == 0) unique->reordered = 2; +#ifdef DD_DEBUG + retval = Cudd_DebugCheck(unique); + if (retval != 0) unique->reordered = 2; + retval = Cudd_CheckKeys(unique); + if (retval != 0) unique->reordered = 2; +#endif + return(NULL); + } + + unique->keysZ++; + subtable->keys++; + + looking = cuddAllocNode(unique); + if (looking == NULL) return(NULL); + looking->ref = 0; + looking->index = index; + cuddT(looking) = T; + cuddE(looking) = E; + looking->next = nodelist[pos]; + nodelist[pos] = looking; + cuddRef(T); + cuddRef(E); + + return(looking); + +} /* end of cuddUniqueInterZdd */ + + +/**Function******************************************************************** + + Synopsis [Checks the unique table for the existence of a constant node.] + + Description [Checks the unique table for the existence of a constant node. + If it does not exist, it creates a new one. Does not + modify the reference count of whatever is returned. A newly created + internal node comes back with a reference count 0. Returns a + pointer to the new node.] + + SideEffects [None] + +******************************************************************************/ +DdNode * +cuddUniqueConst( + DdManager * unique, + CUDD_VALUE_TYPE value) +{ + int pos; + DdNodePtr *nodelist; + DdNode *looking; + hack split; + +#ifdef DD_UNIQUE_PROFILE + unique->uniqueLookUps++; +#endif + + if (unique->constants.keys > unique->constants.maxKeys) { + if (unique->gcEnabled && ((unique->dead > unique->minDead) || + (10 * unique->constants.dead > 9 * unique->constants.keys))) { /* too many dead */ + (void) cuddGarbageCollect(unique,1); + } else { + cuddRehash(unique,CUDD_CONST_INDEX); + } + } + + cuddAdjust(value); /* for the case of crippled infinities */ + + if (ddAbs(value) < unique->epsilon) { + value = 0.0; + } + split.value = value; + + pos = ddHash(split.bits[0], split.bits[1], unique->constants.shift); + nodelist = unique->constants.nodelist; + looking = nodelist[pos]; + + /* Here we compare values both for equality and for difference less + * than epsilon. The first comparison is required when values are + * infinite, since Infinity - Infinity is NaN and NaN < X is 0 for + * every X. + */ + while (looking != NULL) { + if (looking->type.value == value || + ddEqualVal(looking->type.value,value,unique->epsilon)) { + if (looking->ref == 0) { + cuddReclaim(unique,looking); + } + return(looking); + } + looking = looking->next; +#ifdef DD_UNIQUE_PROFILE + unique->uniqueLinks++; +#endif + } + + unique->keys++; + unique->constants.keys++; + + looking = cuddAllocNode(unique); + if (looking == NULL) return(NULL); + looking->ref = 0; + looking->index = CUDD_CONST_INDEX; + looking->type.value = value; + looking->next = nodelist[pos]; + nodelist[pos] = looking; + + return(looking); + +} /* end of cuddUniqueConst */ + + +/**Function******************************************************************** + + Synopsis [Rehashes a unique subtable.] + + Description [Doubles the size of a unique subtable and rehashes its + contents.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +cuddRehash( + DdManager * unique, + int i) +{ + unsigned int slots, oldslots; + int shift, oldshift; + int j, pos; + DdNodePtr *nodelist, *oldnodelist; + DdNode *node, *next; + DdNode *sentinel = &(unique->sentinel); + hack split; + extern void (*MMoutOfMemory)(long); + void (*saveHandler)(long); + + if (unique->gcFrac == DD_GC_FRAC_HI && unique->slots > unique->looseUpTo) { + unique->gcFrac = DD_GC_FRAC_LO; + unique->minDead = (unsigned) (DD_GC_FRAC_LO * (double) unique->slots); +#ifdef DD_VERBOSE + (void) fprintf(unique->err,"GC fraction = %.2f\t", DD_GC_FRAC_LO); + (void) fprintf(unique->err,"minDead = %d\n", unique->minDead); +#endif + } + + if (unique->gcFrac != DD_GC_FRAC_MIN && unique->memused > unique->maxmem) { + unique->gcFrac = DD_GC_FRAC_MIN; + unique->minDead = (unsigned) (DD_GC_FRAC_MIN * (double) unique->slots); +#ifdef DD_VERBOSE + (void) fprintf(unique->err,"GC fraction = %.2f\t", DD_GC_FRAC_MIN); + (void) fprintf(unique->err,"minDead = %d\n", unique->minDead); +#endif + cuddShrinkDeathRow(unique); + if (cuddGarbageCollect(unique,1) > 0) return; + } + + if (i != CUDD_CONST_INDEX) { + oldslots = unique->subtables[i].slots; + oldshift = unique->subtables[i].shift; + oldnodelist = unique->subtables[i].nodelist; + + /* Compute the new size of the subtable. */ + slots = oldslots << 1; + shift = oldshift - 1; + + saveHandler = MMoutOfMemory; + MMoutOfMemory = Cudd_OutOfMem; + nodelist = ALLOC(DdNodePtr, slots); + MMoutOfMemory = saveHandler; + if (nodelist == NULL) { + (void) fprintf(unique->err, + "Unable to resize subtable %d for lack of memory\n", + i); + /* Prevent frequent resizing attempts. */ + (void) cuddGarbageCollect(unique,1); + if (unique->stash != NULL) { + FREE(unique->stash); + unique->stash = NULL; + /* Inhibit resizing of tables. */ + cuddSlowTableGrowth(unique); + } + return; + } + unique->subtables[i].nodelist = nodelist; + unique->subtables[i].slots = slots; + unique->subtables[i].shift = shift; + unique->subtables[i].maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; + + /* Move the nodes from the old table to the new table. + ** This code depends on the type of hash function. + ** It assumes that the effect of doubling the size of the table + ** is to retain one more bit of the 32-bit hash value. + ** The additional bit is the LSB. */ + for (j = 0; (unsigned) j < oldslots; j++) { + DdNodePtr *evenP, *oddP; + node = oldnodelist[j]; + evenP = &(nodelist[j<<1]); + oddP = &(nodelist[(j<<1)+1]); + while (node != sentinel) { + next = node->next; + pos = ddHash(cuddT(node), cuddE(node), shift); + if (pos & 1) { + *oddP = node; + oddP = &(node->next); + } else { + *evenP = node; + evenP = &(node->next); + } + node = next; + } + *evenP = *oddP = sentinel; + } + FREE(oldnodelist); + +#ifdef DD_VERBOSE + (void) fprintf(unique->err, + "rehashing layer %d: keys %d dead %d new size %d\n", + i, unique->subtables[i].keys, + unique->subtables[i].dead, slots); +#endif + } else { + oldslots = unique->constants.slots; + oldshift = unique->constants.shift; + oldnodelist = unique->constants.nodelist; + + /* The constant subtable is never subjected to reordering. + ** Therefore, when it is resized, it is because it has just + ** reached the maximum load. We can safely just double the size, + ** with no need for the loop we use for the other tables. + */ + slots = oldslots << 1; + shift = oldshift - 1; + saveHandler = MMoutOfMemory; + MMoutOfMemory = Cudd_OutOfMem; + nodelist = ALLOC(DdNodePtr, slots); + MMoutOfMemory = saveHandler; + if (nodelist == NULL) { + int j; + (void) fprintf(unique->err, + "Unable to resize constant subtable for lack of memory\n"); + (void) cuddGarbageCollect(unique,1); + for (j = 0; j < unique->size; j++) { + unique->subtables[j].maxKeys <<= 1; + } + unique->constants.maxKeys <<= 1; + return; + } + unique->constants.slots = slots; + unique->constants.shift = shift; + unique->constants.maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; + unique->constants.nodelist = nodelist; + for (j = 0; (unsigned) j < slots; j++) { + nodelist[j] = NULL; + } + for (j = 0; (unsigned) j < oldslots; j++) { + node = oldnodelist[j]; + while (node != NULL) { + next = node->next; + split.value = cuddV(node); + pos = ddHash(split.bits[0], split.bits[1], shift); + node->next = nodelist[pos]; + nodelist[pos] = node; + node = next; + } + } + FREE(oldnodelist); + +#ifdef DD_VERBOSE + (void) fprintf(unique->err, + "rehashing constants: keys %d dead %d new size %d\n", + unique->constants.keys,unique->constants.dead,slots); +#endif + } + + /* Update global data */ + + unique->memused += (slots - oldslots) * sizeof(DdNodePtr); + unique->slots += (slots - oldslots); + ddFixLimits(unique); + +} /* end of cuddRehash */ + + +/**Function******************************************************************** + + Synopsis [Shrinks a subtable.] + + Description [Shrinks a subtable.] + + SideEffects [None] + + SeeAlso [cuddRehash] + +******************************************************************************/ +void +cuddShrinkSubtable( + DdManager *unique, + int i) +{ + int j; + int shift, posn; + DdNodePtr *nodelist, *oldnodelist; + DdNode *node, *next; + DdNode *sentinel = &(unique->sentinel); + unsigned int slots, oldslots; + extern void (*MMoutOfMemory)(long); + void (*saveHandler)(long); + + oldnodelist = unique->subtables[i].nodelist; + oldslots = unique->subtables[i].slots; + slots = oldslots >> 1; + saveHandler = MMoutOfMemory; + MMoutOfMemory = Cudd_OutOfMem; + nodelist = ALLOC(DdNodePtr, slots); + MMoutOfMemory = saveHandler; + if (nodelist == NULL) { + return; + } + unique->subtables[i].nodelist = nodelist; + unique->subtables[i].slots = slots; + unique->subtables[i].shift++; + unique->subtables[i].maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; +#ifdef DD_VERBOSE + (void) fprintf(unique->err, + "shrunk layer %d (%d keys) from %d to %d slots\n", + i, unique->subtables[i].keys, oldslots, slots); +#endif + + for (j = 0; (unsigned) j < slots; j++) { + nodelist[j] = sentinel; + } + shift = unique->subtables[i].shift; + for (j = 0; (unsigned) j < oldslots; j++) { + node = oldnodelist[j]; + while (node != sentinel) { + DdNode *looking, *T, *E; + DdNodePtr *previousP; + next = node->next; + posn = ddHash(cuddT(node), cuddE(node), shift); + previousP = &(nodelist[posn]); + looking = *previousP; + T = cuddT(node); + E = cuddE(node); + while (T < cuddT(looking)) { + previousP = &(looking->next); + looking = *previousP; +#ifdef DD_UNIQUE_PROFILE + unique->uniqueLinks++; +#endif + } + while (T == cuddT(looking) && E < cuddE(looking)) { + previousP = &(looking->next); + looking = *previousP; +#ifdef DD_UNIQUE_PROFILE + unique->uniqueLinks++; +#endif + } + node->next = *previousP; + *previousP = node; + node = next; + } + } + FREE(oldnodelist); + + unique->memused += ((long) slots - (long) oldslots) * sizeof(DdNode *); + unique->slots += slots - oldslots; + unique->minDead = (unsigned) (unique->gcFrac * (double) unique->slots); + unique->cacheSlack = (int) + ddMin(unique->maxCacheHard,DD_MAX_CACHE_TO_SLOTS_RATIO * unique->slots) + - 2 * (int) unique->cacheSlots; + +} /* end of cuddShrinkSubtable */ + + +/**Function******************************************************************** + + Synopsis [Inserts n new subtables in a unique table at level.] + + Description [Inserts n new subtables in a unique table at level. + The number n should be positive, and level should be an existing level. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [cuddDestroySubtables] + +******************************************************************************/ +int +cuddInsertSubtables( + DdManager * unique, + int n, + int level) +{ + DdSubtable *newsubtables; + DdNodePtr *newnodelist; + DdNodePtr *newvars; + DdNode *sentinel = &(unique->sentinel); + int oldsize,newsize; + int i,j,index,reorderSave; + unsigned int numSlots = unique->initSlots; + int *newperm, *newinvperm, *newmap; + DdNode *one, *zero; + +#ifdef DD_DEBUG + assert(n > 0 && level < unique->size); +#endif + + oldsize = unique->size; + /* Easy case: there is still room in the current table. */ + if (oldsize + n <= unique->maxSize) { + /* Shift the tables at and below level. */ + for (i = oldsize - 1; i >= level; i--) { + unique->subtables[i+n].slots = unique->subtables[i].slots; + unique->subtables[i+n].shift = unique->subtables[i].shift; + unique->subtables[i+n].keys = unique->subtables[i].keys; + unique->subtables[i+n].maxKeys = unique->subtables[i].maxKeys; + unique->subtables[i+n].dead = unique->subtables[i].dead; + unique->subtables[i+n].nodelist = unique->subtables[i].nodelist; + unique->subtables[i+n].bindVar = unique->subtables[i].bindVar; + unique->subtables[i+n].varType = unique->subtables[i].varType; + unique->subtables[i+n].pairIndex = unique->subtables[i].pairIndex; + unique->subtables[i+n].varHandled = unique->subtables[i].varHandled; + unique->subtables[i+n].varToBeGrouped = + unique->subtables[i].varToBeGrouped; + + index = unique->invperm[i]; + unique->invperm[i+n] = index; + unique->perm[index] += n; + } + /* Create new subtables. */ + for (i = 0; i < n; i++) { + unique->subtables[level+i].slots = numSlots; + unique->subtables[level+i].shift = sizeof(int) * 8 - + cuddComputeFloorLog2(numSlots); + unique->subtables[level+i].keys = 0; + unique->subtables[level+i].maxKeys = numSlots * DD_MAX_SUBTABLE_DENSITY; + unique->subtables[level+i].dead = 0; + unique->subtables[level+i].bindVar = 0; + unique->subtables[level+i].varType = CUDD_VAR_PRIMARY_INPUT; + unique->subtables[level+i].pairIndex = 0; + unique->subtables[level+i].varHandled = 0; + unique->subtables[level+i].varToBeGrouped = CUDD_LAZY_NONE; + + unique->perm[oldsize+i] = level + i; + unique->invperm[level+i] = oldsize + i; + newnodelist = unique->subtables[level+i].nodelist = + ALLOC(DdNodePtr, numSlots); + if (newnodelist == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (j = 0; j < numSlots; j++) { + newnodelist[j] = sentinel; + } + } + if (unique->map != NULL) { + for (i = 0; i < n; i++) { + unique->map[oldsize+i] = oldsize + i; + } + } + } else { + /* The current table is too small: we need to allocate a new, + ** larger one; move all old subtables, and initialize the new + ** subtables. + */ + newsize = oldsize + n + DD_DEFAULT_RESIZE; +#ifdef DD_VERBOSE + (void) fprintf(unique->err, + "Increasing the table size from %d to %d\n", + unique->maxSize, newsize); +#endif + /* Allocate memory for new arrays (except nodelists). */ + newsubtables = ALLOC(DdSubtable,newsize); + if (newsubtables == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + newvars = ALLOC(DdNodePtr,newsize); + if (newvars == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + FREE(newsubtables); + return(0); + } + newperm = ALLOC(int,newsize); + if (newperm == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + FREE(newsubtables); + FREE(newvars); + return(0); + } + newinvperm = ALLOC(int,newsize); + if (newinvperm == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + FREE(newsubtables); + FREE(newvars); + FREE(newperm); + return(0); + } + if (unique->map != NULL) { + newmap = ALLOC(int,newsize); + if (newmap == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + FREE(newsubtables); + FREE(newvars); + FREE(newperm); + FREE(newinvperm); + return(0); + } + unique->memused += (newsize - unique->maxSize) * sizeof(int); + } + unique->memused += (newsize - unique->maxSize) * ((numSlots+1) * + sizeof(DdNode *) + 2 * sizeof(int) + sizeof(DdSubtable)); + /* Copy levels before insertion points from old tables. */ + for (i = 0; i < level; i++) { + newsubtables[i].slots = unique->subtables[i].slots; + newsubtables[i].shift = unique->subtables[i].shift; + newsubtables[i].keys = unique->subtables[i].keys; + newsubtables[i].maxKeys = unique->subtables[i].maxKeys; + newsubtables[i].dead = unique->subtables[i].dead; + newsubtables[i].nodelist = unique->subtables[i].nodelist; + newsubtables[i].bindVar = unique->subtables[i].bindVar; + newsubtables[i].varType = unique->subtables[i].varType; + newsubtables[i].pairIndex = unique->subtables[i].pairIndex; + newsubtables[i].varHandled = unique->subtables[i].varHandled; + newsubtables[i].varToBeGrouped = unique->subtables[i].varToBeGrouped; + + newvars[i] = unique->vars[i]; + newperm[i] = unique->perm[i]; + newinvperm[i] = unique->invperm[i]; + } + /* Finish initializing permutation for new table to old one. */ + for (i = level; i < oldsize; i++) { + newperm[i] = unique->perm[i]; + } + /* Initialize new levels. */ + for (i = level; i < level + n; i++) { + newsubtables[i].slots = numSlots; + newsubtables[i].shift = sizeof(int) * 8 - + cuddComputeFloorLog2(numSlots); + newsubtables[i].keys = 0; + newsubtables[i].maxKeys = numSlots * DD_MAX_SUBTABLE_DENSITY; + newsubtables[i].dead = 0; + newsubtables[i].bindVar = 0; + newsubtables[i].varType = CUDD_VAR_PRIMARY_INPUT; + newsubtables[i].pairIndex = 0; + newsubtables[i].varHandled = 0; + newsubtables[i].varToBeGrouped = CUDD_LAZY_NONE; + + newperm[oldsize + i - level] = i; + newinvperm[i] = oldsize + i - level; + newnodelist = newsubtables[i].nodelist = ALLOC(DdNodePtr, numSlots); + if (newnodelist == NULL) { + /* We are going to leak some memory. We should clean up. */ + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (j = 0; j < numSlots; j++) { + newnodelist[j] = sentinel; + } + } + /* Copy the old tables for levels past the insertion point. */ + for (i = level; i < oldsize; i++) { + newsubtables[i+n].slots = unique->subtables[i].slots; + newsubtables[i+n].shift = unique->subtables[i].shift; + newsubtables[i+n].keys = unique->subtables[i].keys; + newsubtables[i+n].maxKeys = unique->subtables[i].maxKeys; + newsubtables[i+n].dead = unique->subtables[i].dead; + newsubtables[i+n].nodelist = unique->subtables[i].nodelist; + newsubtables[i+n].bindVar = unique->subtables[i].bindVar; + newsubtables[i+n].varType = unique->subtables[i].varType; + newsubtables[i+n].pairIndex = unique->subtables[i].pairIndex; + newsubtables[i+n].varHandled = unique->subtables[i].varHandled; + newsubtables[i+n].varToBeGrouped = + unique->subtables[i].varToBeGrouped; + + newvars[i] = unique->vars[i]; + index = unique->invperm[i]; + newinvperm[i+n] = index; + newperm[index] += n; + } + /* Update the map. */ + if (unique->map != NULL) { + for (i = 0; i < oldsize; i++) { + newmap[i] = unique->map[i]; + } + for (i = oldsize; i < oldsize + n; i++) { + newmap[i] = i; + } + FREE(unique->map); + unique->map = newmap; + } + /* Install the new tables and free the old ones. */ + FREE(unique->subtables); + unique->subtables = newsubtables; + unique->maxSize = newsize; + FREE(unique->vars); + unique->vars = newvars; + FREE(unique->perm); + unique->perm = newperm; + FREE(unique->invperm); + unique->invperm = newinvperm; + /* Update the stack for iterative procedures. */ + if (newsize > unique->maxSizeZ) { + FREE(unique->stack); + unique->stack = ALLOC(DdNodePtr,newsize + 1); + if (unique->stack == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + unique->stack[0] = NULL; /* to suppress harmless UMR */ + unique->memused += + (newsize - ddMax(unique->maxSize,unique->maxSizeZ)) + * sizeof(DdNode *); + } + } + /* Update manager parameters to account for the new subtables. */ + unique->slots += n * numSlots; + ddFixLimits(unique); + unique->size += n; + + /* Now that the table is in a coherent state, create the new + ** projection functions. We need to temporarily disable reordering, + ** because we cannot reorder without projection functions in place. + **/ + one = unique->one; + zero = Cudd_Not(one); + + reorderSave = unique->autoDyn; + unique->autoDyn = 0; + for (i = oldsize; i < oldsize + n; i++) { + unique->vars[i] = cuddUniqueInter(unique,i,one,zero); + if (unique->vars[i] == NULL) { + unique->autoDyn = reorderSave; + /* Shift everything back so table remains coherent. */ + for (j = oldsize; j < i; j++) { + Cudd_IterDerefBdd(unique,unique->vars[j]); + cuddDeallocNode(unique,unique->vars[j]); + unique->vars[j] = NULL; + } + for (j = level; j < oldsize; j++) { + unique->subtables[j].slots = unique->subtables[j+n].slots; + unique->subtables[j].slots = unique->subtables[j+n].slots; + unique->subtables[j].shift = unique->subtables[j+n].shift; + unique->subtables[j].keys = unique->subtables[j+n].keys; + unique->subtables[j].maxKeys = + unique->subtables[j+n].maxKeys; + unique->subtables[j].dead = unique->subtables[j+n].dead; + FREE(unique->subtables[j].nodelist); + unique->subtables[j].nodelist = + unique->subtables[j+n].nodelist; + unique->subtables[j+n].nodelist = NULL; + unique->subtables[j].bindVar = + unique->subtables[j+n].bindVar; + unique->subtables[j].varType = + unique->subtables[j+n].varType; + unique->subtables[j].pairIndex = + unique->subtables[j+n].pairIndex; + unique->subtables[j].varHandled = + unique->subtables[j+n].varHandled; + unique->subtables[j].varToBeGrouped = + unique->subtables[j+n].varToBeGrouped; + index = unique->invperm[j+n]; + unique->invperm[j] = index; + unique->perm[index] -= n; + } + unique->size = oldsize; + unique->slots -= n * numSlots; + ddFixLimits(unique); + (void) Cudd_DebugCheck(unique); + return(0); + } + cuddRef(unique->vars[i]); + } + if (unique->tree != NULL) { + unique->tree->size += n; + unique->tree->index = unique->invperm[0]; + ddPatchTree(unique,unique->tree); + } + unique->autoDyn = reorderSave; + + return(1); + +} /* end of cuddInsertSubtables */ + + +/**Function******************************************************************** + + Synopsis [Destroys the n most recently created subtables in a unique table.] + + Description [Destroys the n most recently created subtables in a unique + table. n should be positive. The subtables should not contain any live + nodes, except the (isolated) projection function. The projection + functions are freed. Returns 1 if successful; 0 otherwise.] + + SideEffects [The variable map used for fast variable substitution is + destroyed if it exists. In this case the cache is also cleared.] + + SeeAlso [cuddInsertSubtables Cudd_SetVarMap] + +******************************************************************************/ +int +cuddDestroySubtables( + DdManager * unique, + int n) +{ + DdSubtable *subtables; + DdNodePtr *nodelist; + DdNodePtr *vars; + int firstIndex, lastIndex; + int index, level, newlevel; + int lowestLevel; + int shift; + int found; + + /* Sanity check and set up. */ + if (n <= 0) return(0); + if (n > unique->size) n = unique->size; + + subtables = unique->subtables; + vars = unique->vars; + firstIndex = unique->size - n; + lastIndex = unique->size; + + /* Check for nodes labeled by the variables being destroyed + ** that may still be in use. It is allowed to destroy a variable + ** only if there are no such nodes. Also, find the lowest level + ** among the variables being destroyed. This will make further + ** processing more efficient. + */ + lowestLevel = unique->size; + for (index = firstIndex; index < lastIndex; index++) { + level = unique->perm[index]; + if (level < lowestLevel) lowestLevel = level; + nodelist = subtables[level].nodelist; + if (subtables[level].keys - subtables[level].dead != 1) return(0); + /* The projection function should be isolated. If the ref count + ** is 1, everything is OK. If the ref count is saturated, then + ** we need to make sure that there are no nodes pointing to it. + ** As for the external references, we assume the application is + ** responsible for them. + */ + if (vars[index]->ref != 1) { + if (vars[index]->ref != DD_MAXREF) return(0); + found = cuddFindParent(unique,vars[index]); + if (found) { + return(0); + } else { + vars[index]->ref = 1; + } + } + Cudd_RecursiveDeref(unique,vars[index]); + } + + /* Collect garbage, because we cannot afford having dead nodes pointing + ** to the dead nodes in the subtables being destroyed. + */ + (void) cuddGarbageCollect(unique,1); + + /* Here we know we can destroy our subtables. */ + for (index = firstIndex; index < lastIndex; index++) { + level = unique->perm[index]; + nodelist = subtables[level].nodelist; +#ifdef DD_DEBUG + assert(subtables[level].keys == 0); +#endif + FREE(nodelist); + unique->memused -= sizeof(DdNodePtr) * subtables[level].slots; + unique->slots -= subtables[level].slots; + unique->dead -= subtables[level].dead; + } + + /* Here all subtables to be destroyed have their keys field == 0 and + ** their hash tables have been freed. + ** We now scan the subtables from level lowestLevel + 1 to level size - 1, + ** shifting the subtables as required. We keep a running count of + ** how many subtables have been moved, so that we know by how many + ** positions each subtable should be shifted. + */ + shift = 1; + for (level = lowestLevel + 1; level < unique->size; level++) { + if (subtables[level].keys == 0) { + shift++; + continue; + } + newlevel = level - shift; + subtables[newlevel].slots = subtables[level].slots; + subtables[newlevel].shift = subtables[level].shift; + subtables[newlevel].keys = subtables[level].keys; + subtables[newlevel].maxKeys = subtables[level].maxKeys; + subtables[newlevel].dead = subtables[level].dead; + subtables[newlevel].nodelist = subtables[level].nodelist; + index = unique->invperm[level]; + unique->perm[index] = newlevel; + unique->invperm[newlevel] = index; + subtables[newlevel].bindVar = subtables[level].bindVar; + subtables[newlevel].varType = subtables[level].varType; + subtables[newlevel].pairIndex = subtables[level].pairIndex; + subtables[newlevel].varHandled = subtables[level].varHandled; + subtables[newlevel].varToBeGrouped = subtables[level].varToBeGrouped; + } + /* Destroy the map. If a surviving variable is + ** mapped to a dying variable, and the map were used again, + ** an out-of-bounds access to unique->vars would result. */ + if (unique->map != NULL) { + cuddCacheFlush(unique); + FREE(unique->map); + unique->map = NULL; + } + + unique->minDead = (unsigned) (unique->gcFrac * (double) unique->slots); + unique->size -= n; + + return(1); + +} /* end of cuddDestroySubtables */ + + +/**Function******************************************************************** + + Synopsis [Increases the number of ZDD subtables in a unique table so + that it meets or exceeds index.] + + Description [Increases the number of ZDD subtables in a unique table so + that it meets or exceeds index. When new ZDD variables are created, it + is possible to preserve the functions unchanged, or it is possible to + preserve the covers unchanged, but not both. cuddResizeTableZdd preserves + the covers. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [ddResizeTable] + +******************************************************************************/ +int +cuddResizeTableZdd( + DdManager * unique, + int index) +{ + DdSubtable *newsubtables; + DdNodePtr *newnodelist; + int oldsize,newsize; + int i,j,reorderSave; + unsigned int numSlots = unique->initSlots; + int *newperm, *newinvperm; + DdNode *one, *zero; + + oldsize = unique->sizeZ; + /* Easy case: there is still room in the current table. */ + if (index < unique->maxSizeZ) { + for (i = oldsize; i <= index; i++) { + unique->subtableZ[i].slots = numSlots; + unique->subtableZ[i].shift = sizeof(int) * 8 - + cuddComputeFloorLog2(numSlots); + unique->subtableZ[i].keys = 0; + unique->subtableZ[i].maxKeys = numSlots * DD_MAX_SUBTABLE_DENSITY; + unique->subtableZ[i].dead = 0; + unique->permZ[i] = i; + unique->invpermZ[i] = i; + newnodelist = unique->subtableZ[i].nodelist = + ALLOC(DdNodePtr, numSlots); + if (newnodelist == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (j = 0; j < numSlots; j++) { + newnodelist[j] = NULL; + } + } + } else { + /* The current table is too small: we need to allocate a new, + ** larger one; move all old subtables, and initialize the new + ** subtables up to index included. + */ + newsize = index + DD_DEFAULT_RESIZE; +#ifdef DD_VERBOSE + (void) fprintf(unique->err, + "Increasing the ZDD table size from %d to %d\n", + unique->maxSizeZ, newsize); +#endif + newsubtables = ALLOC(DdSubtable,newsize); + if (newsubtables == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + newperm = ALLOC(int,newsize); + if (newperm == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + newinvperm = ALLOC(int,newsize); + if (newinvperm == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + unique->memused += (newsize - unique->maxSizeZ) * ((numSlots+1) * + sizeof(DdNode *) + 2 * sizeof(int) + sizeof(DdSubtable)); + if (newsize > unique->maxSize) { + FREE(unique->stack); + unique->stack = ALLOC(DdNodePtr,newsize + 1); + if (unique->stack == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + unique->stack[0] = NULL; /* to suppress harmless UMR */ + unique->memused += + (newsize - ddMax(unique->maxSize,unique->maxSizeZ)) + * sizeof(DdNode *); + } + for (i = 0; i < oldsize; i++) { + newsubtables[i].slots = unique->subtableZ[i].slots; + newsubtables[i].shift = unique->subtableZ[i].shift; + newsubtables[i].keys = unique->subtableZ[i].keys; + newsubtables[i].maxKeys = unique->subtableZ[i].maxKeys; + newsubtables[i].dead = unique->subtableZ[i].dead; + newsubtables[i].nodelist = unique->subtableZ[i].nodelist; + newperm[i] = unique->permZ[i]; + newinvperm[i] = unique->invpermZ[i]; + } + for (i = oldsize; i <= index; i++) { + newsubtables[i].slots = numSlots; + newsubtables[i].shift = sizeof(int) * 8 - + cuddComputeFloorLog2(numSlots); + newsubtables[i].keys = 0; + newsubtables[i].maxKeys = numSlots * DD_MAX_SUBTABLE_DENSITY; + newsubtables[i].dead = 0; + newperm[i] = i; + newinvperm[i] = i; + newnodelist = newsubtables[i].nodelist = ALLOC(DdNodePtr, numSlots); + if (newnodelist == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (j = 0; j < numSlots; j++) { + newnodelist[j] = NULL; + } + } + FREE(unique->subtableZ); + unique->subtableZ = newsubtables; + unique->maxSizeZ = newsize; + FREE(unique->permZ); + unique->permZ = newperm; + FREE(unique->invpermZ); + unique->invpermZ = newinvperm; + } + unique->slots += (index + 1 - unique->sizeZ) * numSlots; + ddFixLimits(unique); + unique->sizeZ = index + 1; + + /* Now that the table is in a coherent state, update the ZDD + ** universe. We need to temporarily disable reordering, + ** because we cannot reorder without universe in place. + */ + one = unique->one; + zero = unique->zero; + + reorderSave = unique->autoDynZ; + unique->autoDynZ = 0; + cuddZddFreeUniv(unique); + if (!cuddZddInitUniv(unique)) { + unique->autoDynZ = reorderSave; + return(0); + } + unique->autoDynZ = reorderSave; + + return(1); + +} /* end of cuddResizeTableZdd */ + + +/**Function******************************************************************** + + Synopsis [Adjusts parameters of a table to slow down its growth.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +cuddSlowTableGrowth( + DdManager *unique) +{ + int i; + + unique->maxCacheHard = unique->cacheSlots - 1; + unique->cacheSlack = -(unique->cacheSlots + 1); + for (i = 0; i < unique->size; i++) { + unique->subtables[i].maxKeys <<= 2; + } + unique->gcFrac = DD_GC_FRAC_MIN; + unique->minDead = (unsigned) (DD_GC_FRAC_MIN * (double) unique->slots); + cuddShrinkDeathRow(unique); + (void) fprintf(unique->err,"Slowing down table growth: "); + (void) fprintf(unique->err,"GC fraction = %.2f\t", unique->gcFrac); + (void) fprintf(unique->err,"minDead = %d\n", unique->minDead); + +} /* end of cuddSlowTableGrowth */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Rehashes a ZDD unique subtable.] + + Description [] + + SideEffects [None] + + SeeAlso [cuddRehash] + +******************************************************************************/ +static void +ddRehashZdd( + DdManager * unique, + int i) +{ + unsigned int slots, oldslots; + int shift, oldshift; + int j, pos; + DdNodePtr *nodelist, *oldnodelist; + DdNode *node, *next; + extern void (*MMoutOfMemory)(long); + void (*saveHandler)(long); + + if (unique->slots > unique->looseUpTo) { + unique->minDead = (unsigned) (DD_GC_FRAC_LO * (double) unique->slots); +#ifdef DD_VERBOSE + if (unique->gcFrac == DD_GC_FRAC_HI) { + (void) fprintf(unique->err,"GC fraction = %.2f\t", + DD_GC_FRAC_LO); + (void) fprintf(unique->err,"minDead = %d\n", unique->minDead); + } +#endif + unique->gcFrac = DD_GC_FRAC_LO; + } + + assert(i != CUDD_MAXINDEX); + oldslots = unique->subtableZ[i].slots; + oldshift = unique->subtableZ[i].shift; + oldnodelist = unique->subtableZ[i].nodelist; + + /* Compute the new size of the subtable. Normally, we just + ** double. However, after reordering, a table may be severely + ** overloaded. Therefore, we iterate. */ + slots = oldslots; + shift = oldshift; + do { + slots <<= 1; + shift--; + } while (slots * DD_MAX_SUBTABLE_DENSITY < unique->subtableZ[i].keys); + + saveHandler = MMoutOfMemory; + MMoutOfMemory = Cudd_OutOfMem; + nodelist = ALLOC(DdNodePtr, slots); + MMoutOfMemory = saveHandler; + if (nodelist == NULL) { + int j; + (void) fprintf(unique->err, + "Unable to resize ZDD subtable %d for lack of memory.\n", + i); + (void) cuddGarbageCollectZdd(unique,1); + for (j = 0; j < unique->sizeZ; j++) { + unique->subtableZ[j].maxKeys <<= 1; + } + return; + } + unique->subtableZ[i].nodelist = nodelist; + unique->subtableZ[i].slots = slots; + unique->subtableZ[i].shift = shift; + unique->subtableZ[i].maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; + for (j = 0; (unsigned) j < slots; j++) { + nodelist[j] = NULL; + } + for (j = 0; (unsigned) j < oldslots; j++) { + node = oldnodelist[j]; + while (node != NULL) { + next = node->next; + pos = ddHash(cuddT(node), cuddE(node), shift); + node->next = nodelist[pos]; + nodelist[pos] = node; + node = next; + } + } + FREE(oldnodelist); + +#ifdef DD_VERBOSE + (void) fprintf(unique->err, + "rehashing layer %d: keys %d dead %d new size %d\n", + i, unique->subtableZ[i].keys, + unique->subtableZ[i].dead, slots); +#endif + + /* Update global data. */ + unique->memused += (slots - oldslots) * sizeof(DdNode *); + unique->slots += (slots - oldslots); + ddFixLimits(unique); + +} /* end of ddRehashZdd */ + + +/**Function******************************************************************** + + Synopsis [Increases the number of subtables in a unique table so + that it meets or exceeds index.] + + Description [Increases the number of subtables in a unique table so + that it meets or exceeds index. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [cuddResizeTableZdd] + +******************************************************************************/ +static int +ddResizeTable( + DdManager * unique, + int index) +{ + DdSubtable *newsubtables; + DdNodePtr *newnodelist; + DdNodePtr *newvars; + DdNode *sentinel = &(unique->sentinel); + int oldsize,newsize; + int i,j,reorderSave; + int numSlots = unique->initSlots; + int *newperm, *newinvperm, *newmap; + DdNode *one, *zero; + + oldsize = unique->size; + /* Easy case: there is still room in the current table. */ + if (index < unique->maxSize) { + for (i = oldsize; i <= index; i++) { + unique->subtables[i].slots = numSlots; + unique->subtables[i].shift = sizeof(int) * 8 - + cuddComputeFloorLog2(numSlots); + unique->subtables[i].keys = 0; + unique->subtables[i].maxKeys = numSlots * DD_MAX_SUBTABLE_DENSITY; + unique->subtables[i].dead = 0; + unique->subtables[i].bindVar = 0; + unique->subtables[i].varType = CUDD_VAR_PRIMARY_INPUT; + unique->subtables[i].pairIndex = 0; + unique->subtables[i].varHandled = 0; + unique->subtables[i].varToBeGrouped = CUDD_LAZY_NONE; + + unique->perm[i] = i; + unique->invperm[i] = i; + newnodelist = unique->subtables[i].nodelist = + ALLOC(DdNodePtr, numSlots); + if (newnodelist == NULL) { + for (j = oldsize; j < i; j++) { + FREE(unique->subtables[j].nodelist); + } + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (j = 0; j < numSlots; j++) { + newnodelist[j] = sentinel; + } + } + if (unique->map != NULL) { + for (i = oldsize; i <= index; i++) { + unique->map[i] = i; + } + } + } else { + /* The current table is too small: we need to allocate a new, + ** larger one; move all old subtables, and initialize the new + ** subtables up to index included. + */ + newsize = index + DD_DEFAULT_RESIZE; +#ifdef DD_VERBOSE + (void) fprintf(unique->err, + "Increasing the table size from %d to %d\n", + unique->maxSize, newsize); +#endif + newsubtables = ALLOC(DdSubtable,newsize); + if (newsubtables == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + newvars = ALLOC(DdNodePtr,newsize); + if (newvars == NULL) { + FREE(newsubtables); + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + newperm = ALLOC(int,newsize); + if (newperm == NULL) { + FREE(newsubtables); + FREE(newvars); + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + newinvperm = ALLOC(int,newsize); + if (newinvperm == NULL) { + FREE(newsubtables); + FREE(newvars); + FREE(newperm); + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + if (unique->map != NULL) { + newmap = ALLOC(int,newsize); + if (newmap == NULL) { + FREE(newsubtables); + FREE(newvars); + FREE(newperm); + FREE(newinvperm); + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + unique->memused += (newsize - unique->maxSize) * sizeof(int); + } + unique->memused += (newsize - unique->maxSize) * ((numSlots+1) * + sizeof(DdNode *) + 2 * sizeof(int) + sizeof(DdSubtable)); + if (newsize > unique->maxSizeZ) { + FREE(unique->stack); + unique->stack = ALLOC(DdNodePtr,newsize + 1); + if (unique->stack == NULL) { + FREE(newsubtables); + FREE(newvars); + FREE(newperm); + FREE(newinvperm); + if (unique->map != NULL) { + FREE(newmap); + } + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + unique->stack[0] = NULL; /* to suppress harmless UMR */ + unique->memused += + (newsize - ddMax(unique->maxSize,unique->maxSizeZ)) + * sizeof(DdNode *); + } + for (i = 0; i < oldsize; i++) { + newsubtables[i].slots = unique->subtables[i].slots; + newsubtables[i].shift = unique->subtables[i].shift; + newsubtables[i].keys = unique->subtables[i].keys; + newsubtables[i].maxKeys = unique->subtables[i].maxKeys; + newsubtables[i].dead = unique->subtables[i].dead; + newsubtables[i].nodelist = unique->subtables[i].nodelist; + newsubtables[i].bindVar = unique->subtables[i].bindVar; + newsubtables[i].varType = unique->subtables[i].varType; + newsubtables[i].pairIndex = unique->subtables[i].pairIndex; + newsubtables[i].varHandled = unique->subtables[i].varHandled; + newsubtables[i].varToBeGrouped = unique->subtables[i].varToBeGrouped; + + newvars[i] = unique->vars[i]; + newperm[i] = unique->perm[i]; + newinvperm[i] = unique->invperm[i]; + } + for (i = oldsize; i <= index; i++) { + newsubtables[i].slots = numSlots; + newsubtables[i].shift = sizeof(int) * 8 - + cuddComputeFloorLog2(numSlots); + newsubtables[i].keys = 0; + newsubtables[i].maxKeys = numSlots * DD_MAX_SUBTABLE_DENSITY; + newsubtables[i].dead = 0; + newsubtables[i].bindVar = 0; + newsubtables[i].varType = CUDD_VAR_PRIMARY_INPUT; + newsubtables[i].pairIndex = 0; + newsubtables[i].varHandled = 0; + newsubtables[i].varToBeGrouped = CUDD_LAZY_NONE; + + newperm[i] = i; + newinvperm[i] = i; + newnodelist = newsubtables[i].nodelist = ALLOC(DdNodePtr, numSlots); + if (newnodelist == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (j = 0; j < numSlots; j++) { + newnodelist[j] = sentinel; + } + } + if (unique->map != NULL) { + for (i = 0; i < oldsize; i++) { + newmap[i] = unique->map[i]; + } + for (i = oldsize; i <= index; i++) { + newmap[i] = i; + } + FREE(unique->map); + unique->map = newmap; + } + FREE(unique->subtables); + unique->subtables = newsubtables; + unique->maxSize = newsize; + FREE(unique->vars); + unique->vars = newvars; + FREE(unique->perm); + unique->perm = newperm; + FREE(unique->invperm); + unique->invperm = newinvperm; + } + + /* Now that the table is in a coherent state, create the new + ** projection functions. We need to temporarily disable reordering, + ** because we cannot reorder without projection functions in place. + **/ + one = unique->one; + zero = Cudd_Not(one); + + unique->size = index + 1; + unique->slots += (index + 1 - oldsize) * numSlots; + ddFixLimits(unique); + + reorderSave = unique->autoDyn; + unique->autoDyn = 0; + for (i = oldsize; i <= index; i++) { + unique->vars[i] = cuddUniqueInter(unique,i,one,zero); + if (unique->vars[i] == NULL) { + unique->autoDyn = reorderSave; + for (j = oldsize; j < i; j++) { + Cudd_IterDerefBdd(unique,unique->vars[j]); + cuddDeallocNode(unique,unique->vars[j]); + unique->vars[j] = NULL; + } + for (j = oldsize; j <= index; j++) { + FREE(unique->subtables[j].nodelist); + unique->subtables[j].nodelist = NULL; + } + unique->size = oldsize; + unique->slots -= (index + 1 - oldsize) * numSlots; + ddFixLimits(unique); + return(0); + } + cuddRef(unique->vars[i]); + } + unique->autoDyn = reorderSave; + + return(1); + +} /* end of ddResizeTable */ + + +/**Function******************************************************************** + + Synopsis [Searches the subtables above node for a parent.] + + Description [Searches the subtables above node for a parent. Returns 1 + as soon as one parent is found. Returns 0 is the search is fruitless.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddFindParent( + DdManager * table, + DdNode * node) +{ + int i,j; + int slots; + DdNodePtr *nodelist; + DdNode *f; + + for (i = cuddI(table,node->index) - 1; i >= 0; i--) { + nodelist = table->subtables[i].nodelist; + slots = table->subtables[i].slots; + + for (j = 0; j < slots; j++) { + f = nodelist[j]; + while (cuddT(f) > node) { + f = f->next; + } + while (cuddT(f) == node && Cudd_Regular(cuddE(f)) > node) { + f = f->next; + } + if (cuddT(f) == node && Cudd_Regular(cuddE(f)) == node) { + return(1); + } + } + } + + return(0); + +} /* end of cuddFindParent */ + + +/**Function******************************************************************** + + Synopsis [Adjusts the values of table limits.] + + Description [Adjusts the values of table fields controlling the. + sizes of subtables and computed table. If the computed table is too small + according to the new values, it is resized.] + + SideEffects [Modifies manager fields. May resize computed table.] + + SeeAlso [] + +******************************************************************************/ +DD_INLINE +static void +ddFixLimits( + DdManager *unique) +{ + unique->minDead = (unsigned) (unique->gcFrac * (double) unique->slots); + unique->cacheSlack = (int) ddMin(unique->maxCacheHard, + DD_MAX_CACHE_TO_SLOTS_RATIO * unique->slots) - + 2 * (int) unique->cacheSlots; + if (unique->cacheSlots < unique->slots/2 && unique->cacheSlack >= 0) + cuddCacheResize(unique); + return; + +} /* end of ddFixLimits */ + + +#ifndef DD_UNSORTED_FREE_LIST +/**Function******************************************************************** + + Synopsis [Inserts a DdNode in a red/black search tree.] + + Description [Inserts a DdNode in a red/black search tree. Nodes from + the same "page" (defined by DD_PAGE_MASK) are linked in a LIFO list.] + + SideEffects [None] + + SeeAlso [cuddOrderedThread] + +******************************************************************************/ +static void +cuddOrderedInsert( + DdNodePtr * root, + DdNodePtr node) +{ + DdNode *scan; + DdNodePtr *scanP; + DdNodePtr *stack[DD_STACK_SIZE]; + int stackN = 0; + + scanP = root; + while ((scan = *scanP) != NULL) { + stack[stackN++] = scanP; + if (DD_INSERT_COMPARE(node, scan) == 0) { /* add to page list */ + DD_NEXT(node) = DD_NEXT(scan); + DD_NEXT(scan) = node; + return; + } + scanP = (node < scan) ? &DD_LEFT(scan) : &DD_RIGHT(scan); + } + DD_RIGHT(node) = DD_LEFT(node) = DD_NEXT(node) = NULL; + DD_COLOR(node) = DD_RED; + *scanP = node; + stack[stackN] = &node; + cuddDoRebalance(stack,stackN); + +} /* end of cuddOrderedInsert */ + + +/**Function******************************************************************** + + Synopsis [Threads all the nodes of a search tree into a linear list.] + + Description [Threads all the nodes of a search tree into a linear + list. For each node of the search tree, the "left" child, if non-null, has + a lower address than its parent, and the "right" child, if non-null, has a + higher address than its parent. + The list is sorted in order of increasing addresses. The search + tree is destroyed as a result of this operation. The last element of + the linear list is made to point to the address passed in list. Each + node if the search tree is a linearly-linked list of nodes from the + same memory page (as defined in DD_PAGE_MASK). When a node is added to + the linear list, all the elements of the linked list are added.] + + SideEffects [The search tree is destroyed as a result of this operation.] + + SeeAlso [cuddOrderedInsert] + +******************************************************************************/ +static DdNode * +cuddOrderedThread( + DdNode * root, + DdNode * list) +{ + DdNode *current, *next, *prev, *end; + + current = root; + /* The first word in the node is used to implement a stack that holds + ** the nodes from the root of the tree to the current node. Here we + ** put the root of the tree at the bottom of the stack. + */ + *((DdNodePtr *) current) = NULL; + + while (current != NULL) { + if (DD_RIGHT(current) != NULL) { + /* If possible, we follow the "right" link. Eventually we'll + ** find the node with the largest address in the current tree. + ** In this phase we use the first word of a node to implemen + ** a stack of the nodes on the path from the root to "current". + ** Also, we disconnect the "right" pointers to indicate that + ** we have already followed them. + */ + next = DD_RIGHT(current); + DD_RIGHT(current) = NULL; + *((DdNodePtr *)next) = current; + current = next; + } else { + /* We can't proceed along the "right" links any further. + ** Hence "current" is the largest element in the current tree. + ** We make this node the new head of "list". (Repeating this + ** operation until the tree is empty yields the desired linear + ** threading of all nodes.) + */ + prev = *((DdNodePtr *) current); /* save prev node on stack in prev */ + /* Traverse the linked list of current until the end. */ + for (end = current; DD_NEXT(end) != NULL; end = DD_NEXT(end)); + DD_NEXT(end) = list; /* attach "list" at end and make */ + list = current; /* "current" the new head of "list" */ + /* Now, if current has a "left" child, we push it on the stack. + ** Otherwise, we just continue with the parent of "current". + */ + if (DD_LEFT(current) != NULL) { + next = DD_LEFT(current); + *((DdNodePtr *) next) = prev; + current = next; + } else { + current = prev; + } + } + } + + return(list); + +} /* end of cuddOrderedThread */ + + +/**Function******************************************************************** + + Synopsis [Performs the left rotation for red/black trees.] + + Description [] + + SideEffects [None] + + SeeAlso [cuddRotateRight] + +******************************************************************************/ +DD_INLINE +static void +cuddRotateLeft( + DdNodePtr * nodeP) +{ + DdNode *newRoot; + DdNode *oldRoot = *nodeP; + + *nodeP = newRoot = DD_RIGHT(oldRoot); + DD_RIGHT(oldRoot) = DD_LEFT(newRoot); + DD_LEFT(newRoot) = oldRoot; + +} /* end of cuddRotateLeft */ + + +/**Function******************************************************************** + + Synopsis [Performs the right rotation for red/black trees.] + + Description [] + + SideEffects [None] + + SeeAlso [cuddRotateLeft] + +******************************************************************************/ +DD_INLINE +static void +cuddRotateRight( + DdNodePtr * nodeP) +{ + DdNode *newRoot; + DdNode *oldRoot = *nodeP; + + *nodeP = newRoot = DD_LEFT(oldRoot); + DD_LEFT(oldRoot) = DD_RIGHT(newRoot); + DD_RIGHT(newRoot) = oldRoot; + +} /* end of cuddRotateRight */ + + +/**Function******************************************************************** + + Synopsis [Rebalances a red/black tree.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +cuddDoRebalance( + DdNodePtr ** stack, + int stackN) +{ + DdNodePtr *xP, *parentP, *grandpaP; + DdNode *x, *y, *parent, *grandpa; + + xP = stack[stackN]; + x = *xP; + /* Work our way back up, re-balancing the tree. */ + while (--stackN >= 0) { + parentP = stack[stackN]; + parent = *parentP; + if (DD_IS_BLACK(parent)) break; + /* Since the root is black, here a non-null grandparent exists. */ + grandpaP = stack[stackN-1]; + grandpa = *grandpaP; + if (parent == DD_LEFT(grandpa)) { + y = DD_RIGHT(grandpa); + if (y != NULL && DD_IS_RED(y)) { + DD_COLOR(parent) = DD_BLACK; + DD_COLOR(y) = DD_BLACK; + DD_COLOR(grandpa) = DD_RED; + x = grandpa; + stackN--; + } else { + if (x == DD_RIGHT(parent)) { + cuddRotateLeft(parentP); + DD_COLOR(x) = DD_BLACK; + } else { + DD_COLOR(parent) = DD_BLACK; + } + DD_COLOR(grandpa) = DD_RED; + cuddRotateRight(grandpaP); + break; + } + } else { + y = DD_LEFT(grandpa); + if (y != NULL && DD_IS_RED(y)) { + DD_COLOR(parent) = DD_BLACK; + DD_COLOR(y) = DD_BLACK; + DD_COLOR(grandpa) = DD_RED; + x = grandpa; + stackN--; + } else { + if (x == DD_LEFT(parent)) { + cuddRotateRight(parentP); + DD_COLOR(x) = DD_BLACK; + } else { + DD_COLOR(parent) = DD_BLACK; + } + DD_COLOR(grandpa) = DD_RED; + cuddRotateLeft(grandpaP); + } + } + } + DD_COLOR(*(stack[0])) = DD_BLACK; + +} /* end of cuddDoRebalance */ +#endif + + +/**Function******************************************************************** + + Synopsis [Fixes a variable tree after the insertion of new subtables.] + + Description [Fixes a variable tree after the insertion of new subtables. + After such an insertion, the low fields of the tree below the insertion + point are inconsistent.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +ddPatchTree( + DdManager *dd, + MtrNode *treenode) +{ + MtrNode *auxnode = treenode; + + while (auxnode != NULL) { + auxnode->low = dd->perm[auxnode->index]; + if (auxnode->child != NULL) { + ddPatchTree(dd, auxnode->child); + } + auxnode = auxnode->younger; + } + + return; + +} /* end of ddPatchTree */ + + +#ifdef DD_DEBUG +/**Function******************************************************************** + + Synopsis [Checks whether a collision list is ordered.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddCheckCollisionOrdering( + DdManager *unique, + int i, + int j) +{ + int slots; + DdNode *node, *next; + DdNodePtr *nodelist; + DdNode *sentinel = &(unique->sentinel); + + nodelist = unique->subtables[i].nodelist; + slots = unique->subtables[i].slots; + node = nodelist[j]; + if (node == sentinel) return(1); + next = node->next; + while (next != sentinel) { + if (cuddT(node) < cuddT(next) || + (cuddT(node) == cuddT(next) && cuddE(node) < cuddE(next))) { + (void) fprintf(unique->err, + "Unordered list: index %u, position %d\n", i, j); + return(0); + } + node = next; + next = node->next; + } + return(1); + +} /* end of cuddCheckCollisionOrdering */ +#endif + + + + +/**Function******************************************************************** + + Synopsis [Reports problem in garbage collection.] + + Description [] + + SideEffects [None] + + SeeAlso [cuddGarbageCollect cuddGarbageCollectZdd] + +******************************************************************************/ +static void +ddReportRefMess( + DdManager *unique /* manager */, + int i /* table in which the problem occurred */, + char *caller /* procedure that detected the problem */) +{ + if (i == CUDD_CONST_INDEX) { + (void) fprintf(unique->err, + "%s: problem in constants\n", caller); + } else if (i != -1) { + (void) fprintf(unique->err, + "%s: problem in table %d\n", caller, i); + } + (void) fprintf(unique->err, " dead count != deleted\n"); + (void) fprintf(unique->err, " This problem is often due to a missing \ +call to Cudd_Ref\n or to an extra call to Cudd_RecursiveDeref.\n \ +See the CUDD Programmer's Guide for additional details."); + abort(); + +} /* end of ddReportRefMess */ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddUtil.c b/abc_with_bb_support/src/bdd/cudd/cuddUtil.c new file mode 100644 index 000000000..7d43d0296 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddUtil.c @@ -0,0 +1,3633 @@ +/**CFile*********************************************************************** + + FileName [cuddUtil.c] + + PackageName [cudd] + + Synopsis [Utility functions.] + + Description [External procedures included in this module: +
          +
        • Cudd_PrintMinterm() +
        • Cudd_PrintDebug() +
        • Cudd_DagSize() +
        • Cudd_EstimateCofactor() +
        • Cudd_EstimateCofactorSimple() +
        • Cudd_SharingSize() +
        • Cudd_CountMinterm() +
        • Cudd_EpdCountMinterm() +
        • Cudd_CountPath() +
        • Cudd_CountPathsToNonZero() +
        • Cudd_Support() +
        • Cudd_SupportIndex() +
        • Cudd_SupportSize() +
        • Cudd_VectorSupport() +
        • Cudd_VectorSupportIndex() +
        • Cudd_VectorSupportSize() +
        • Cudd_ClassifySupport() +
        • Cudd_CountLeaves() +
        • Cudd_bddPickOneCube() +
        • Cudd_bddPickOneMinterm() +
        • Cudd_bddPickArbitraryMinterms() +
        • Cudd_SubsetWithMaskVars() +
        • Cudd_FirstCube() +
        • Cudd_NextCube() +
        • Cudd_bddComputeCube() +
        • Cudd_addComputeCube() +
        • Cudd_FirstNode() +
        • Cudd_NextNode() +
        • Cudd_GenFree() +
        • Cudd_IsGenEmpty() +
        • Cudd_IndicesToCube() +
        • Cudd_PrintVersion() +
        • Cudd_AverageDistance() +
        • Cudd_Random() +
        • Cudd_Srandom() +
        • Cudd_Density() +
        + Internal procedures included in this module: +
          +
        • cuddP() +
        • cuddStCountfree() +
        • cuddCollectNodes() +
        + Static procedures included in this module: +
          +
        • dp2() +
        • ddPrintMintermAux() +
        • ddDagInt() +
        • ddCountMintermAux() +
        • ddEpdCountMintermAux() +
        • ddCountPathAux() +
        • ddSupportStep() +
        • ddClearFlag() +
        • ddLeavesInt() +
        • ddPickArbitraryMinterms() +
        • ddPickRepresentativeCube() +
        • ddEpdFree() +
        ] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/* Random generator constants. */ +#define MODULUS1 2147483563 +#define LEQA1 40014 +#define LEQQ1 53668 +#define LEQR1 12211 +#define MODULUS2 2147483399 +#define LEQA2 40692 +#define LEQQ2 52774 +#define LEQR2 3791 +#define STAB_SIZE 64 +#define STAB_DIV (1 + (MODULUS1 - 1) / STAB_SIZE) + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddUtil.c,v 1.1.1.1 2003/02/24 22:23:53 wjiang Exp $"; +#endif + +static DdNode *background, *zero; + +static long cuddRand = 0; +static long cuddRand2; +static long shuffleSelect; +static long shuffleTable[STAB_SIZE]; + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +#define bang(f) ((Cudd_IsComplement(f)) ? '!' : ' ') + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int dp2 ARGS((DdManager *dd, DdNode *f, st_table *t)); +static void ddPrintMintermAux ARGS((DdManager *dd, DdNode *node, int *list)); +static int ddDagInt ARGS((DdNode *n)); +static int cuddEstimateCofactor ARGS((DdManager *dd, st_table *table, DdNode * node, int i, int phase, DdNode ** ptr)); +static DdNode * cuddUniqueLookup ARGS((DdManager * unique, int index, DdNode * T, DdNode * E)); +static int cuddEstimateCofactorSimple ARGS((DdNode * node, int i)); +static double ddCountMintermAux ARGS((DdNode *node, double max, DdHashTable *table)); +static int ddEpdCountMintermAux ARGS((DdNode *node, EpDouble *max, EpDouble *epd, st_table *table)); +static double ddCountPathAux ARGS((DdNode *node, st_table *table)); +static double ddCountPathsToNonZero ARGS((DdNode * N, st_table * table)); +static void ddSupportStep ARGS((DdNode *f, int *support)); +static void ddClearFlag ARGS((DdNode *f)); +static int ddLeavesInt ARGS((DdNode *n)); +static int ddPickArbitraryMinterms ARGS((DdManager *dd, DdNode *node, int nvars, int nminterms, char **string)); +static int ddPickRepresentativeCube ARGS((DdManager *dd, DdNode *node, int nvars, double *weight, char *string)); +static enum st_retval ddEpdFree ARGS((char * key, char * value, char * arg)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Prints a disjoint sum of products.] + + Description [Prints a disjoint sum of product cover for the function + rooted at node. Each product corresponds to a path from node to a + leaf node different from the logical zero, and different from the + background value. Uses the package default output file. Returns 1 + if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_PrintDebug Cudd_bddPrintCover] + +******************************************************************************/ +int +Cudd_PrintMinterm( + DdManager * manager, + DdNode * node) +{ + int i, *list; + + background = manager->background; + zero = Cudd_Not(manager->one); + list = ALLOC(int,manager->size); + if (list == NULL) { + manager->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (i = 0; i < manager->size; i++) list[i] = 2; + ddPrintMintermAux(manager,node,list); + FREE(list); + return(1); + +} /* end of Cudd_PrintMinterm */ + + +/**Function******************************************************************** + + Synopsis [Prints a sum of prime implicants of a BDD.] + + Description [Prints a sum of product cover for an incompletely + specified function given by a lower bound and an upper bound. Each + product is a prime implicant obtained by expanding the product + corresponding to a path from node to the constant one. Uses the + package default output file. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_PrintMinterm] + +******************************************************************************/ +int +Cudd_bddPrintCover( + DdManager *dd, + DdNode *l, + DdNode *u) +{ + int *array; + int q, result; + DdNode *lb; +#ifdef DD_DEBUG + DdNode *cover; +#endif + + array = ALLOC(int, Cudd_ReadSize(dd)); + if (array == NULL) return(0); + lb = l; + cuddRef(lb); +#ifdef DD_DEBUG + cover = Cudd_ReadLogicZero(dd); + cuddRef(cover); +#endif + while (lb != Cudd_ReadLogicZero(dd)) { + DdNode *implicant, *prime, *tmp; + int length; + implicant = Cudd_LargestCube(dd,lb,&length); + if (implicant == NULL) { + Cudd_RecursiveDeref(dd,lb); + FREE(array); + return(0); + } + cuddRef(implicant); + prime = Cudd_bddMakePrime(dd,implicant,u); + if (prime == NULL) { + Cudd_RecursiveDeref(dd,lb); + Cudd_RecursiveDeref(dd,implicant); + FREE(array); + return(0); + } + cuddRef(prime); + Cudd_RecursiveDeref(dd,implicant); + tmp = Cudd_bddAnd(dd,lb,Cudd_Not(prime)); + if (tmp == NULL) { + Cudd_RecursiveDeref(dd,lb); + Cudd_RecursiveDeref(dd,prime); + FREE(array); + return(0); + } + cuddRef(tmp); + Cudd_RecursiveDeref(dd,lb); + lb = tmp; + result = Cudd_BddToCubeArray(dd,prime,array); + if (result == 0) { + Cudd_RecursiveDeref(dd,lb); + Cudd_RecursiveDeref(dd,prime); + FREE(array); + return(0); + } + for (q = 0; q < dd->size; q++) { + switch (array[q]) { + case 0: + (void) fprintf(dd->out, "0"); + break; + case 1: + (void) fprintf(dd->out, "1"); + break; + case 2: + (void) fprintf(dd->out, "-"); + break; + default: + (void) fprintf(dd->out, "?"); + } + } + (void) fprintf(dd->out, " 1\n"); +#ifdef DD_DEBUG + tmp = Cudd_bddOr(dd,prime,cover); + if (tmp == NULL) { + Cudd_RecursiveDeref(dd,cover); + Cudd_RecursiveDeref(dd,lb); + Cudd_RecursiveDeref(dd,prime); + FREE(array); + return(0); + } + cuddRef(tmp); + Cudd_RecursiveDeref(dd,cover); + cover = tmp; +#endif + Cudd_RecursiveDeref(dd,prime); + } + (void) fprintf(dd->out, "\n"); + Cudd_RecursiveDeref(dd,lb); + FREE(array); +#ifdef DD_DEBUG + if (!Cudd_bddLeq(dd,cover,u) || !Cudd_bddLeq(dd,l,cover)) { + Cudd_RecursiveDeref(dd,cover); + return(0); + } + Cudd_RecursiveDeref(dd,cover); +#endif + return(1); + +} /* end of Cudd_bddPrintCover */ + + +/**Function******************************************************************** + + Synopsis [Prints to the standard output a DD and its statistics.] + + Description [Prints to the standard output a DD and its statistics. + The statistics include the number of nodes, the number of leaves, and + the number of minterms. (The number of minterms is the number of + assignments to the variables that cause the function to be different + from the logical zero (for BDDs) and from the background value (for + ADDs.) The statistics are printed if pr > 0. Specifically: +
          +
        • pr = 0 : prints nothing +
        • pr = 1 : prints counts of nodes and minterms +
        • pr = 2 : prints counts + disjoint sum of product +
        • pr = 3 : prints counts + list of nodes +
        • pr > 3 : prints counts + disjoint sum of product + list of nodes +
        + For the purpose of counting the number of minterms, the function is + supposed to depend on n variables. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_DagSize Cudd_CountLeaves Cudd_CountMinterm + Cudd_PrintMinterm] + +******************************************************************************/ +int +Cudd_PrintDebug( + DdManager * dd, + DdNode * f, + int n, + int pr) +{ + DdNode *azero, *bzero; + int nodes; + int leaves; + double minterms; + int retval = 1; + + if (f == NULL) { + (void) fprintf(dd->out,": is the NULL DD\n"); + (void) fflush(dd->out); + return(0); + } + azero = DD_ZERO(dd); + bzero = Cudd_Not(DD_ONE(dd)); + if ((f == azero || f == bzero) && pr > 0){ + (void) fprintf(dd->out,": is the zero DD\n"); + (void) fflush(dd->out); + return(1); + } + if (pr > 0) { + nodes = Cudd_DagSize(f); + if (nodes == CUDD_OUT_OF_MEM) retval = 0; + leaves = Cudd_CountLeaves(f); + if (leaves == CUDD_OUT_OF_MEM) retval = 0; + minterms = Cudd_CountMinterm(dd, f, n); + if (minterms == (double)CUDD_OUT_OF_MEM) retval = 0; + (void) fprintf(dd->out,": %d nodes %d leaves %g minterms\n", + nodes, leaves, minterms); + if (pr > 2) { + if (!cuddP(dd, f)) retval = 0; + } + if (pr == 2 || pr > 3) { + if (!Cudd_PrintMinterm(dd,f)) retval = 0; + (void) fprintf(dd->out,"\n"); + } + (void) fflush(dd->out); + } + return(retval); + +} /* end of Cudd_PrintDebug */ + + +/**Function******************************************************************** + + Synopsis [Counts the number of nodes in a DD.] + + Description [Counts the number of nodes in a DD. Returns the number + of nodes in the graph rooted at node.] + + SideEffects [None] + + SeeAlso [Cudd_SharingSize Cudd_PrintDebug] + +******************************************************************************/ +int +Cudd_DagSize( + DdNode * node) +{ + int i; + + i = ddDagInt(Cudd_Regular(node)); + ddClearFlag(Cudd_Regular(node)); + + return(i); + +} /* end of Cudd_DagSize */ + + +/**Function******************************************************************** + + Synopsis [Estimates the number of nodes in a cofactor of a DD.] + + Description [Estimates the number of nodes in a cofactor of a DD. + Returns an estimate of the number of nodes in a cofactor of + the graph rooted at node with respect to the variable whose index is i. + In case of failure, returns CUDD_OUT_OF_MEM. + This function uses a refinement of the algorithm of Cabodi et al. + (ICCAD96). The refinement allows the procedure to account for part + of the recombination that may occur in the part of the cofactor above + the cofactoring variable. This procedure does no create any new node. + It does keep a small table of results; therefore itmay run out of memory. + If this is a concern, one should use Cudd_EstimateCofactorSimple, which + is faster, does not allocate any memory, but is less accurate.] + + SideEffects [None] + + SeeAlso [Cudd_DagSize Cudd_EstimateCofactorSimple] + +******************************************************************************/ +int +Cudd_EstimateCofactor( + DdManager *dd /* manager */, + DdNode * f /* function */, + int i /* index of variable */, + int phase /* 1: positive; 0: negative */ + ) +{ + int val; + DdNode *ptr; + st_table *table; + + table = st_init_table(st_ptrcmp,st_ptrhash); + if (table == NULL) return(CUDD_OUT_OF_MEM); + val = cuddEstimateCofactor(dd,table,Cudd_Regular(f),i,phase,&ptr); + ddClearFlag(Cudd_Regular(f)); + st_free_table(table); + + return(val); + +} /* end of Cudd_EstimateCofactor */ + + +/**Function******************************************************************** + + Synopsis [Estimates the number of nodes in a cofactor of a DD.] + + Description [Estimates the number of nodes in a cofactor of a DD. + Returns an estimate of the number of nodes in the positive cofactor of + the graph rooted at node with respect to the variable whose index is i. + This procedure implements with minor changes the algorithm of Cabodi et al. + (ICCAD96). It does not allocate any memory, it does not change the + state of the manager, and it is fast. However, it has been observed to + overestimate the size of the cofactor by as much as a factor of 2.] + + SideEffects [None] + + SeeAlso [Cudd_DagSize] + +******************************************************************************/ +int +Cudd_EstimateCofactorSimple( + DdNode * node, + int i) +{ + int val; + + val = cuddEstimateCofactorSimple(Cudd_Regular(node),i); + ddClearFlag(Cudd_Regular(node)); + + return(val); + +} /* end of Cudd_EstimateCofactorSimple */ + + +/**Function******************************************************************** + + Synopsis [Counts the number of nodes in an array of DDs.] + + Description [Counts the number of nodes in an array of DDs. Shared + nodes are counted only once. Returns the total number of nodes.] + + SideEffects [None] + + SeeAlso [Cudd_DagSize] + +******************************************************************************/ +int +Cudd_SharingSize( + DdNode ** nodeArray, + int n) +{ + int i,j; + + i = 0; + for (j = 0; j < n; j++) { + i += ddDagInt(Cudd_Regular(nodeArray[j])); + } + for (j = 0; j < n; j++) { + ddClearFlag(Cudd_Regular(nodeArray[j])); + } + return(i); + +} /* end of Cudd_SharingSize */ + + +/**Function******************************************************************** + + Synopsis [Counts the number of minterms of a DD.] + + Description [Counts the number of minterms of a DD. The function is + assumed to depend on nvars variables. The minterm count is + represented as a double, to allow for a larger number of variables. + Returns the number of minterms of the function rooted at node if + successful; (double) CUDD_OUT_OF_MEM otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_PrintDebug Cudd_CountPath] + +******************************************************************************/ +double +Cudd_CountMinterm( + DdManager * manager, + DdNode * node, + int nvars) +{ + double max; + DdHashTable *table; + double res; + CUDD_VALUE_TYPE epsilon; + + background = manager->background; + zero = Cudd_Not(manager->one); + + max = pow(2.0,(double)nvars); + table = cuddHashTableInit(manager,1,2); + if (table == NULL) { + return((double)CUDD_OUT_OF_MEM); + } + epsilon = Cudd_ReadEpsilon(manager); + Cudd_SetEpsilon(manager,(CUDD_VALUE_TYPE)0.0); + res = ddCountMintermAux(node,max,table); + cuddHashTableQuit(table); + Cudd_SetEpsilon(manager,epsilon); + + return(res); + +} /* end of Cudd_CountMinterm */ + + +/**Function******************************************************************** + + Synopsis [Counts the number of paths of a DD.] + + Description [Counts the number of paths of a DD. Paths to all + terminal nodes are counted. The path count is represented as a + double, to allow for a larger number of variables. Returns the + number of paths of the function rooted at node if successful; + (double) CUDD_OUT_OF_MEM otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_CountMinterm] + +******************************************************************************/ +double +Cudd_CountPath( + DdNode * node) +{ + + st_table *table; + double i; + + table = st_init_table(st_ptrcmp,st_ptrhash); + if (table == NULL) { + return((double)CUDD_OUT_OF_MEM); + } + i = ddCountPathAux(Cudd_Regular(node),table); + st_foreach(table, cuddStCountfree, NULL); + st_free_table(table); + return(i); + +} /* end of Cudd_CountPath */ + + +/**Function******************************************************************** + + Synopsis [Counts the number of minterms of a DD with extended precision.] + + Description [Counts the number of minterms of a DD with extended precision. + The function is assumed to depend on nvars variables. The minterm count is + represented as an EpDouble, to allow any number of variables. + Returns 0 if successful; CUDD_OUT_OF_MEM otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_PrintDebug Cudd_CountPath] + +******************************************************************************/ +int +Cudd_EpdCountMinterm( + DdManager * manager, + DdNode * node, + int nvars, + EpDouble * epd) +{ + EpDouble max, tmp; + st_table *table; + int status; + + background = manager->background; + zero = Cudd_Not(manager->one); + + EpdPow2(nvars, &max); + table = st_init_table(EpdCmp, st_ptrhash); + if (table == NULL) { + EpdMakeZero(epd, 0); + return(CUDD_OUT_OF_MEM); + } + status = ddEpdCountMintermAux(Cudd_Regular(node),&max,epd,table); + st_foreach(table, ddEpdFree, NULL); + st_free_table(table); + if (status == CUDD_OUT_OF_MEM) { + EpdMakeZero(epd, 0); + return(CUDD_OUT_OF_MEM); + } + if (Cudd_IsComplement(node)) { + EpdSubtract3(&max, epd, &tmp); + EpdCopy(&tmp, epd); + } + return(0); + +} /* end of Cudd_EpdCountMinterm */ + + +/**Function******************************************************************** + + Synopsis [Counts the number of paths to a non-zero terminal of a DD.] + + Description [Counts the number of paths to a non-zero terminal of a + DD. The path count is + represented as a double, to allow for a larger number of variables. + Returns the number of paths of the function rooted at node.] + + SideEffects [None] + + SeeAlso [Cudd_CountMinterm Cudd_CountPath] + +******************************************************************************/ +double +Cudd_CountPathsToNonZero( + DdNode * node) +{ + + st_table *table; + double i; + + table = st_init_table(st_ptrcmp,st_ptrhash); + if (table == NULL) { + return((double)CUDD_OUT_OF_MEM); + } + i = ddCountPathsToNonZero(node,table); + st_foreach(table, cuddStCountfree, NULL); + st_free_table(table); + return(i); + +} /* end of Cudd_CountPathsToNonZero */ + + +/**Function******************************************************************** + + Synopsis [Finds the variables on which a DD depends.] + + Description [Finds the variables on which a DD depends. + Returns a BDD consisting of the product of the variables if + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_VectorSupport Cudd_ClassifySupport] + +******************************************************************************/ +DdNode * +Cudd_Support( + DdManager * dd /* manager */, + DdNode * f /* DD whose support is sought */) +{ + int *support; + DdNode *res, *tmp, *var; + int i,j; + int size; + + /* Allocate and initialize support array for ddSupportStep. */ + size = ddMax(dd->size, dd->sizeZ); + support = ALLOC(int,size); + if (support == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + for (i = 0; i < size; i++) { + support[i] = 0; + } + + /* Compute support and clean up markers. */ + ddSupportStep(Cudd_Regular(f),support); + ddClearFlag(Cudd_Regular(f)); + + /* Transform support from array to cube. */ + do { + dd->reordered = 0; + res = DD_ONE(dd); + cuddRef(res); + for (j = size - 1; j >= 0; j--) { /* for each level bottom-up */ + i = (j >= dd->size) ? j : dd->invperm[j]; + if (support[i] == 1) { + var = cuddUniqueInter(dd,i,dd->one,Cudd_Not(dd->one)); + cuddRef(var); + tmp = cuddBddAndRecur(dd,res,var); + if (tmp == NULL) { + Cudd_RecursiveDeref(dd,res); + Cudd_RecursiveDeref(dd,var); + res = NULL; + break; + } + cuddRef(tmp); + Cudd_RecursiveDeref(dd,res); + Cudd_RecursiveDeref(dd,var); + res = tmp; + } + } + } while (dd->reordered == 1); + + FREE(support); + if (res != NULL) cuddDeref(res); + return(res); + +} /* end of Cudd_Support */ + + +/**Function******************************************************************** + + Synopsis [Finds the variables on which a DD depends.] + + Description [Finds the variables on which a DD depends. + Returns an index array of the variables if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_Support Cudd_VectorSupport Cudd_ClassifySupport] + +******************************************************************************/ +int * +Cudd_SupportIndex( + DdManager * dd /* manager */, + DdNode * f /* DD whose support is sought */) +{ + int *support; + int i; + int size; + + /* Allocate and initialize support array for ddSupportStep. */ + size = ddMax(dd->size, dd->sizeZ); + support = ALLOC(int,size); + if (support == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + for (i = 0; i < size; i++) { + support[i] = 0; + } + + /* Compute support and clean up markers. */ + ddSupportStep(Cudd_Regular(f),support); + ddClearFlag(Cudd_Regular(f)); + + return(support); + +} /* end of Cudd_SupportIndex */ + + +/**Function******************************************************************** + + Synopsis [Counts the variables on which a DD depends.] + + Description [Counts the variables on which a DD depends. + Returns the number of the variables if successful; CUDD_OUT_OF_MEM + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_Support] + +******************************************************************************/ +int +Cudd_SupportSize( + DdManager * dd /* manager */, + DdNode * f /* DD whose support size is sought */) +{ + int *support; + int i; + int size; + int count; + + /* Allocate and initialize support array for ddSupportStep. */ + size = ddMax(dd->size, dd->sizeZ); + support = ALLOC(int,size); + if (support == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(CUDD_OUT_OF_MEM); + } + for (i = 0; i < size; i++) { + support[i] = 0; + } + + /* Compute support and clean up markers. */ + ddSupportStep(Cudd_Regular(f),support); + ddClearFlag(Cudd_Regular(f)); + + /* Count support variables. */ + count = 0; + for (i = 0; i < size; i++) { + if (support[i] == 1) count++; + } + + FREE(support); + return(count); + +} /* end of Cudd_SupportSize */ + + +/**Function******************************************************************** + + Synopsis [Finds the variables on which a set of DDs depends.] + + Description [Finds the variables on which a set of DDs depends. + The set must contain either BDDs and ADDs, or ZDDs. + Returns a BDD consisting of the product of the variables if + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_Support Cudd_ClassifySupport] + +******************************************************************************/ +DdNode * +Cudd_VectorSupport( + DdManager * dd /* manager */, + DdNode ** F /* array of DDs whose support is sought */, + int n /* size of the array */) +{ + int *support; + DdNode *res, *tmp, *var; + int i,j; + int size; + + /* Allocate and initialize support array for ddSupportStep. */ + size = ddMax(dd->size, dd->sizeZ); + support = ALLOC(int,size); + if (support == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + for (i = 0; i < size; i++) { + support[i] = 0; + } + + /* Compute support and clean up markers. */ + for (i = 0; i < n; i++) { + ddSupportStep(Cudd_Regular(F[i]),support); + } + for (i = 0; i < n; i++) { + ddClearFlag(Cudd_Regular(F[i])); + } + + /* Transform support from array to cube. */ + res = DD_ONE(dd); + cuddRef(res); + for (j = size - 1; j >= 0; j--) { /* for each level bottom-up */ + i = (j >= dd->size) ? j : dd->invperm[j]; + if (support[i] == 1) { + var = cuddUniqueInter(dd,i,dd->one,Cudd_Not(dd->one)); + cuddRef(var); + tmp = Cudd_bddAnd(dd,res,var); + if (tmp == NULL) { + Cudd_RecursiveDeref(dd,res); + Cudd_RecursiveDeref(dd,var); + FREE(support); + return(NULL); + } + cuddRef(tmp); + Cudd_RecursiveDeref(dd,res); + Cudd_RecursiveDeref(dd,var); + res = tmp; + } + } + + FREE(support); + cuddDeref(res); + return(res); + +} /* end of Cudd_VectorSupport */ + + +/**Function******************************************************************** + + Synopsis [Finds the variables on which a set of DDs depends.] + + Description [Finds the variables on which a set of DDs depends. + The set must contain either BDDs and ADDs, or ZDDs. + Returns an index array of the variables if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_SupportIndex Cudd_VectorSupport Cudd_ClassifySupport] + +******************************************************************************/ +int * +Cudd_VectorSupportIndex( + DdManager * dd /* manager */, + DdNode ** F /* array of DDs whose support is sought */, + int n /* size of the array */) +{ + int *support; + int i; + int size; + + /* Allocate and initialize support array for ddSupportStep. */ + size = ddMax(dd->size, dd->sizeZ); + support = ALLOC(int,size); + if (support == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + for (i = 0; i < size; i++) { + support[i] = 0; + } + + /* Compute support and clean up markers. */ + for (i = 0; i < n; i++) { + ddSupportStep(Cudd_Regular(F[i]),support); + } + for (i = 0; i < n; i++) { + ddClearFlag(Cudd_Regular(F[i])); + } + + return(support); + +} /* end of Cudd_VectorSupportIndex */ + + +/**Function******************************************************************** + + Synopsis [Counts the variables on which a set of DDs depends.] + + Description [Counts the variables on which a set of DDs depends. + The set must contain either BDDs and ADDs, or ZDDs. + Returns the number of the variables if successful; CUDD_OUT_OF_MEM + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_VectorSupport Cudd_SupportSize] + +******************************************************************************/ +int +Cudd_VectorSupportSize( + DdManager * dd /* manager */, + DdNode ** F /* array of DDs whose support is sought */, + int n /* size of the array */) +{ + int *support; + int i; + int size; + int count; + + /* Allocate and initialize support array for ddSupportStep. */ + size = ddMax(dd->size, dd->sizeZ); + support = ALLOC(int,size); + if (support == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(CUDD_OUT_OF_MEM); + } + for (i = 0; i < size; i++) { + support[i] = 0; + } + + /* Compute support and clean up markers. */ + for (i = 0; i < n; i++) { + ddSupportStep(Cudd_Regular(F[i]),support); + } + for (i = 0; i < n; i++) { + ddClearFlag(Cudd_Regular(F[i])); + } + + /* Count vriables in support. */ + count = 0; + for (i = 0; i < size; i++) { + if (support[i] == 1) count++; + } + + FREE(support); + return(count); + +} /* end of Cudd_VectorSupportSize */ + + +/**Function******************************************************************** + + Synopsis [Classifies the variables in the support of two DDs.] + + Description [Classifies the variables in the support of two DDs + f and g, depending on whther they appear + in both DDs, only in f, or only in g. + Returns 1 if successful; 0 otherwise.] + + SideEffects [The cubes of the three classes of variables are + returned as side effects.] + + SeeAlso [Cudd_Support Cudd_VectorSupport] + +******************************************************************************/ +int +Cudd_ClassifySupport( + DdManager * dd /* manager */, + DdNode * f /* first DD */, + DdNode * g /* second DD */, + DdNode ** common /* cube of shared variables */, + DdNode ** onlyF /* cube of variables only in f */, + DdNode ** onlyG /* cube of variables only in g */) +{ + int *supportF, *supportG; + DdNode *tmp, *var; + int i,j; + int size; + + /* Allocate and initialize support arrays for ddSupportStep. */ + size = ddMax(dd->size, dd->sizeZ); + supportF = ALLOC(int,size); + if (supportF == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + supportG = ALLOC(int,size); + if (supportG == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + FREE(supportF); + return(0); + } + for (i = 0; i < size; i++) { + supportF[i] = 0; + supportG[i] = 0; + } + + /* Compute supports and clean up markers. */ + ddSupportStep(Cudd_Regular(f),supportF); + ddClearFlag(Cudd_Regular(f)); + ddSupportStep(Cudd_Regular(g),supportG); + ddClearFlag(Cudd_Regular(g)); + + /* Classify variables and create cubes. */ + *common = *onlyF = *onlyG = DD_ONE(dd); + cuddRef(*common); cuddRef(*onlyF); cuddRef(*onlyG); + for (j = size - 1; j >= 0; j--) { /* for each level bottom-up */ + i = (j >= dd->size) ? j : dd->invperm[j]; + if (supportF[i] == 0 && supportG[i] == 0) continue; + var = cuddUniqueInter(dd,i,dd->one,Cudd_Not(dd->one)); + cuddRef(var); + if (supportG[i] == 0) { + tmp = Cudd_bddAnd(dd,*onlyF,var); + if (tmp == NULL) { + Cudd_RecursiveDeref(dd,*common); + Cudd_RecursiveDeref(dd,*onlyF); + Cudd_RecursiveDeref(dd,*onlyG); + Cudd_RecursiveDeref(dd,var); + FREE(supportF); FREE(supportG); + return(0); + } + cuddRef(tmp); + Cudd_RecursiveDeref(dd,*onlyF); + *onlyF = tmp; + } else if (supportF[i] == 0) { + tmp = Cudd_bddAnd(dd,*onlyG,var); + if (tmp == NULL) { + Cudd_RecursiveDeref(dd,*common); + Cudd_RecursiveDeref(dd,*onlyF); + Cudd_RecursiveDeref(dd,*onlyG); + Cudd_RecursiveDeref(dd,var); + FREE(supportF); FREE(supportG); + return(0); + } + cuddRef(tmp); + Cudd_RecursiveDeref(dd,*onlyG); + *onlyG = tmp; + } else { + tmp = Cudd_bddAnd(dd,*common,var); + if (tmp == NULL) { + Cudd_RecursiveDeref(dd,*common); + Cudd_RecursiveDeref(dd,*onlyF); + Cudd_RecursiveDeref(dd,*onlyG); + Cudd_RecursiveDeref(dd,var); + FREE(supportF); FREE(supportG); + return(0); + } + cuddRef(tmp); + Cudd_RecursiveDeref(dd,*common); + *common = tmp; + } + Cudd_RecursiveDeref(dd,var); + } + + FREE(supportF); FREE(supportG); + cuddDeref(*common); cuddDeref(*onlyF); cuddDeref(*onlyG); + return(1); + +} /* end of Cudd_ClassifySupport */ + + +/**Function******************************************************************** + + Synopsis [Counts the number of leaves in a DD.] + + Description [Counts the number of leaves in a DD. Returns the number + of leaves in the DD rooted at node if successful; CUDD_OUT_OF_MEM + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_PrintDebug] + +******************************************************************************/ +int +Cudd_CountLeaves( + DdNode * node) +{ + int i; + + i = ddLeavesInt(Cudd_Regular(node)); + ddClearFlag(Cudd_Regular(node)); + return(i); + +} /* end of Cudd_CountLeaves */ + + +/**Function******************************************************************** + + Synopsis [Picks one on-set cube randomly from the given DD.] + + Description [Picks one on-set cube randomly from the given DD. The + cube is written into an array of characters. The array must have at + least as many entries as there are variables. Returns 1 if + successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddPickOneMinterm] + +******************************************************************************/ +int +Cudd_bddPickOneCube( + DdManager * ddm, + DdNode * node, + char * string) +{ + DdNode *N, *T, *E; + DdNode *one, *bzero; + char dir; + int i; + + if (string == NULL || node == NULL) return(0); + + /* The constant 0 function has no on-set cubes. */ + one = DD_ONE(ddm); + bzero = Cudd_Not(one); + if (node == bzero) return(0); + + for (i = 0; i < ddm->size; i++) string[i] = 2; + + for (;;) { + + if (node == one) break; + + N = Cudd_Regular(node); + + T = cuddT(N); E = cuddE(N); + if (Cudd_IsComplement(node)) { + T = Cudd_Not(T); E = Cudd_Not(E); + } + if (T == bzero) { + string[N->index] = 0; + node = E; + } else if (E == bzero) { + string[N->index] = 1; + node = T; + } else { + dir = (char) ((Cudd_Random() & 0x2000) >> 13); + string[N->index] = dir; + node = dir ? T : E; + } + } + return(1); + +} /* end of Cudd_bddPickOneCube */ + + +/**Function******************************************************************** + + Synopsis [Picks one on-set minterm randomly from the given DD.] + + Description [Picks one on-set minterm randomly from the given + DD. The minterm is in terms of vars. The array + vars should contain at least all variables in the + support of f; if this condition is not met the minterm + built by this procedure may not be contained in + f. Builds a BDD for the minterm and returns a pointer + to it if successful; NULL otherwise. There are three reasons why the + procedure may fail: +
          +
        • It may run out of memory; +
        • the function f may be the constant 0; +
        • the minterm may not be contained in f. +
        ] + + SideEffects [None] + + SeeAlso [Cudd_bddPickOneCube] + +******************************************************************************/ +DdNode * +Cudd_bddPickOneMinterm( + DdManager * dd /* manager */, + DdNode * f /* function from which to pick one minterm */, + DdNode ** vars /* array of variables */, + int n /* size of vars */) +{ + char *string; + int i, size; + int *indices; + int result; + DdNode *old, *neW; + + size = dd->size; + string = ALLOC(char, size); + if (string == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + indices = ALLOC(int,n); + if (indices == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + FREE(string); + return(NULL); + } + + for (i = 0; i < n; i++) { + indices[i] = vars[i]->index; + } + + result = Cudd_bddPickOneCube(dd,f,string); + if (result == 0) { + FREE(string); + FREE(indices); + return(NULL); + } + + /* Randomize choice for don't cares. */ + for (i = 0; i < n; i++) { + if (string[indices[i]] == 2) + string[indices[i]] = (char) ((Cudd_Random() & 0x20) >> 5); + } + + /* Build result BDD. */ + old = Cudd_ReadOne(dd); + cuddRef(old); + + for (i = n-1; i >= 0; i--) { + neW = Cudd_bddAnd(dd,old,Cudd_NotCond(vars[i],string[indices[i]]==0)); + if (neW == NULL) { + FREE(string); + FREE(indices); + Cudd_RecursiveDeref(dd,old); + return(NULL); + } + cuddRef(neW); + Cudd_RecursiveDeref(dd,old); + old = neW; + } + +#ifdef DD_DEBUG + /* Test. */ + if (Cudd_bddLeq(dd,old,f)) { + cuddDeref(old); + } else { + Cudd_RecursiveDeref(dd,old); + old = NULL; + } +#else + cuddDeref(old); +#endif + + FREE(string); + FREE(indices); + return(old); + +} /* end of Cudd_bddPickOneMinterm */ + + +/**Function******************************************************************** + + Synopsis [Picks k on-set minterms evenly distributed from given DD.] + + Description [Picks k on-set minterms evenly distributed from given DD. + The minterms are in terms of vars. The array + vars should contain at least all variables in the + support of f; if this condition is not met the minterms + built by this procedure may not be contained in + f. Builds an array of BDDs for the minterms and returns a + pointer to it if successful; NULL otherwise. There are three reasons + why the procedure may fail: +
          +
        • It may run out of memory; +
        • the function f may be the constant 0; +
        • the minterms may not be contained in f. +
        ] + + SideEffects [None] + + SeeAlso [Cudd_bddPickOneMinterm Cudd_bddPickOneCube] + +******************************************************************************/ +DdNode ** +Cudd_bddPickArbitraryMinterms( + DdManager * dd /* manager */, + DdNode * f /* function from which to pick k minterms */, + DdNode ** vars /* array of variables */, + int n /* size of vars */, + int k /* number of minterms to find */) +{ + char **string; + int i, j, l, size; + int *indices; + int result; + DdNode **old, *neW; + double minterms; + char *saveString; + int saveFlag, savePoint, isSame; + + minterms = Cudd_CountMinterm(dd,f,n); + if ((double)k > minterms) { + return(NULL); + } + + size = dd->size; + string = ALLOC(char *, k); + if (string == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + for (i = 0; i < k; i++) { + string[i] = ALLOC(char, size + 1); + if (string[i] == NULL) { + for (j = 0; j < i; j++) + FREE(string[i]); + FREE(string); + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + for (j = 0; j < size; j++) string[i][j] = '2'; + string[i][size] = '\0'; + } + indices = ALLOC(int,n); + if (indices == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + for (i = 0; i < k; i++) + FREE(string[i]); + FREE(string); + return(NULL); + } + + for (i = 0; i < n; i++) { + indices[i] = vars[i]->index; + } + + result = ddPickArbitraryMinterms(dd,f,n,k,string); + if (result == 0) { + for (i = 0; i < k; i++) + FREE(string[i]); + FREE(string); + FREE(indices); + return(NULL); + } + + old = ALLOC(DdNode *, k); + if (old == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + for (i = 0; i < k; i++) + FREE(string[i]); + FREE(string); + FREE(indices); + return(NULL); + } + saveString = ALLOC(char, size + 1); + if (saveString == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + for (i = 0; i < k; i++) + FREE(string[i]); + FREE(string); + FREE(indices); + FREE(old); + return(NULL); + } + saveFlag = 0; + + /* Build result BDD array. */ + for (i = 0; i < k; i++) { + isSame = 0; + if (!saveFlag) { + for (j = i + 1; j < k; j++) { + if (strcmp(string[i], string[j]) == 0) { + savePoint = i; + strcpy(saveString, string[i]); + saveFlag = 1; + break; + } + } + } else { + if (strcmp(string[i], saveString) == 0) { + isSame = 1; + } else { + saveFlag = 0; + for (j = i + 1; j < k; j++) { + if (strcmp(string[i], string[j]) == 0) { + savePoint = i; + strcpy(saveString, string[i]); + saveFlag = 1; + break; + } + } + } + } + /* Randomize choice for don't cares. */ + for (j = 0; j < n; j++) { + if (string[i][indices[j]] == '2') + string[i][indices[j]] = (Cudd_Random() & 0x20) ? '1' : '0'; + } + + while (isSame) { + isSame = 0; + for (j = savePoint; j < i; j++) { + if (strcmp(string[i], string[j]) == 0) { + isSame = 1; + break; + } + } + if (isSame) { + strcpy(string[i], saveString); + /* Randomize choice for don't cares. */ + for (j = 0; j < n; j++) { + if (string[i][indices[j]] == '2') + string[i][indices[j]] = (Cudd_Random() & 0x20) ? + '1' : '0'; + } + } + } + + old[i] = Cudd_ReadOne(dd); + cuddRef(old[i]); + + for (j = 0; j < n; j++) { + if (string[i][indices[j]] == '0') { + neW = Cudd_bddAnd(dd,old[i],Cudd_Not(vars[j])); + } else { + neW = Cudd_bddAnd(dd,old[i],vars[j]); + } + if (neW == NULL) { + FREE(saveString); + for (l = 0; l < k; l++) + FREE(string[l]); + FREE(string); + FREE(indices); + for (l = 0; l <= i; l++) + Cudd_RecursiveDeref(dd,old[l]); + FREE(old); + return(NULL); + } + cuddRef(neW); + Cudd_RecursiveDeref(dd,old[i]); + old[i] = neW; + } + + /* Test. */ + if (!Cudd_bddLeq(dd,old[i],f)) { + FREE(saveString); + for (l = 0; l < k; l++) + FREE(string[l]); + FREE(string); + FREE(indices); + for (l = 0; l <= i; l++) + Cudd_RecursiveDeref(dd,old[l]); + FREE(old); + return(NULL); + } + } + + FREE(saveString); + for (i = 0; i < k; i++) { + cuddDeref(old[i]); + FREE(string[i]); + } + FREE(string); + FREE(indices); + return(old); + +} /* end of Cudd_bddPickArbitraryMinterms */ + + +/**Function******************************************************************** + + Synopsis [Extracts a subset from a BDD.] + + Description [Extracts a subset from a BDD in the following procedure. + 1. Compute the weight for each mask variable by counting the number of + minterms for both positive and negative cofactors of the BDD with + respect to each mask variable. (weight = #positive - #negative) + 2. Find a representative cube of the BDD by using the weight. From the + top variable of the BDD, for each variable, if the weight is greater + than 0.0, choose THEN branch, othereise ELSE branch, until meeting + the constant 1. + 3. Quantify out the variables not in maskVars from the representative + cube and if a variable in maskVars is don't care, replace the + variable with a constant(1 or 0) depending on the weight. + 4. Make a subset of the BDD by multiplying with the modified cube.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +Cudd_SubsetWithMaskVars( + DdManager * dd /* manager */, + DdNode * f /* function from which to pick a cube */, + DdNode ** vars /* array of variables */, + int nvars /* size of vars */, + DdNode ** maskVars /* array of variables */, + int mvars /* size of maskVars */) +{ + double *weight; + char *string; + int i, size; + int *indices, *mask; + int result; + DdNode *zero, *cube, *newCube, *subset; + DdNode *cof; + + DdNode *support; + support = Cudd_Support(dd,f); + cuddRef(support); + Cudd_RecursiveDeref(dd,support); + + zero = Cudd_Not(dd->one); + size = dd->size; + + weight = ALLOC(double,size); + if (weight == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + for (i = 0; i < size; i++) { + weight[i] = 0.0; + } + for (i = 0; i < mvars; i++) { + cof = Cudd_Cofactor(dd, f, maskVars[i]); + cuddRef(cof); + weight[i] = Cudd_CountMinterm(dd, cof, nvars); + Cudd_RecursiveDeref(dd,cof); + + cof = Cudd_Cofactor(dd, f, Cudd_Not(maskVars[i])); + cuddRef(cof); + weight[i] -= Cudd_CountMinterm(dd, cof, nvars); + Cudd_RecursiveDeref(dd,cof); + } + + string = ALLOC(char, size + 1); + if (string == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + mask = ALLOC(int, size); + if (mask == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + FREE(string); + return(NULL); + } + for (i = 0; i < size; i++) { + string[i] = '2'; + mask[i] = 0; + } + string[size] = '\0'; + indices = ALLOC(int,nvars); + if (indices == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + FREE(string); + FREE(mask); + return(NULL); + } + for (i = 0; i < nvars; i++) { + indices[i] = vars[i]->index; + } + + result = ddPickRepresentativeCube(dd,f,nvars,weight,string); + if (result == 0) { + FREE(string); + FREE(mask); + FREE(indices); + return(NULL); + } + + cube = Cudd_ReadOne(dd); + cuddRef(cube); + zero = Cudd_Not(Cudd_ReadOne(dd)); + for (i = 0; i < nvars; i++) { + if (string[indices[i]] == '0') { + newCube = Cudd_bddIte(dd,cube,Cudd_Not(vars[i]),zero); + } else if (string[indices[i]] == '1') { + newCube = Cudd_bddIte(dd,cube,vars[i],zero); + } else + continue; + if (newCube == NULL) { + FREE(string); + FREE(mask); + FREE(indices); + Cudd_RecursiveDeref(dd,cube); + return(NULL); + } + cuddRef(newCube); + Cudd_RecursiveDeref(dd,cube); + cube = newCube; + } + Cudd_RecursiveDeref(dd,cube); + + for (i = 0; i < mvars; i++) { + mask[maskVars[i]->index] = 1; + } + for (i = 0; i < nvars; i++) { + if (mask[indices[i]]) { + if (string[indices[i]] == '2') { + if (weight[indices[i]] >= 0.0) + string[indices[i]] = '1'; + else + string[indices[i]] = '0'; + } + } else { + string[indices[i]] = '2'; + } + } + + cube = Cudd_ReadOne(dd); + cuddRef(cube); + zero = Cudd_Not(Cudd_ReadOne(dd)); + + /* Build result BDD. */ + for (i = 0; i < nvars; i++) { + if (string[indices[i]] == '0') { + newCube = Cudd_bddIte(dd,cube,Cudd_Not(vars[i]),zero); + } else if (string[indices[i]] == '1') { + newCube = Cudd_bddIte(dd,cube,vars[i],zero); + } else + continue; + if (newCube == NULL) { + FREE(string); + FREE(mask); + FREE(indices); + Cudd_RecursiveDeref(dd,cube); + return(NULL); + } + cuddRef(newCube); + Cudd_RecursiveDeref(dd,cube); + cube = newCube; + } + + subset = Cudd_bddAnd(dd,f,cube); + cuddRef(subset); + Cudd_RecursiveDeref(dd,cube); + + /* Test. */ + if (Cudd_bddLeq(dd,subset,f)) { + cuddDeref(subset); + } else { + Cudd_RecursiveDeref(dd,subset); + subset = NULL; + } + + FREE(string); + FREE(mask); + FREE(indices); + FREE(weight); + return(subset); + +} /* end of Cudd_SubsetWithMaskVars */ + + +/**Function******************************************************************** + + Synopsis [Finds the first cube of a decision diagram.] + + Description [Defines an iterator on the onset of a decision diagram + and finds its first cube. Returns a generator that contains the + information necessary to continue the enumeration if successful; NULL + otherwise.

        + A cube is represented as an array of literals, which are integers in + {0, 1, 2}; 0 represents a complemented literal, 1 represents an + uncomplemented literal, and 2 stands for don't care. The enumeration + produces a disjoint cover of the function associated with the diagram. + The size of the array equals the number of variables in the manager at + the time Cudd_FirstCube is called.

        + For each cube, a value is also returned. This value is always 1 for a + BDD, while it may be different from 1 for an ADD. + For BDDs, the offset is the set of cubes whose value is the logical zero. + For ADDs, the offset is the set of cubes whose value is the + background value. The cubes of the offset are not enumerated.] + + SideEffects [The first cube and its value are returned as side effects.] + + SeeAlso [Cudd_ForeachCube Cudd_NextCube Cudd_GenFree Cudd_IsGenEmpty + Cudd_FirstNode] + +******************************************************************************/ +DdGen * +Cudd_FirstCube( + DdManager * dd, + DdNode * f, + int ** cube, + CUDD_VALUE_TYPE * value) +{ + DdGen *gen; + DdNode *top, *treg, *next, *nreg, *prev, *preg; + int i; + int nvars; + + /* Sanity Check. */ + if (dd == NULL || f == NULL) return(NULL); + + /* Allocate generator an initialize it. */ + gen = ALLOC(DdGen,1); + if (gen == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + + gen->manager = dd; + gen->type = CUDD_GEN_CUBES; + gen->status = CUDD_GEN_EMPTY; + gen->gen.cubes.cube = NULL; + gen->gen.cubes.value = DD_ZERO_VAL; + gen->stack.sp = 0; + gen->stack.stack = NULL; + gen->node = NULL; + + nvars = dd->size; + gen->gen.cubes.cube = ALLOC(int,nvars); + if (gen->gen.cubes.cube == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + FREE(gen); + return(NULL); + } + for (i = 0; i < nvars; i++) gen->gen.cubes.cube[i] = 2; + + /* The maximum stack depth is one plus the number of variables. + ** because a path may have nodes at all levels, including the + ** constant level. + */ + gen->stack.stack = ALLOC(DdNode *, nvars+1); + if (gen->stack.stack == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + FREE(gen->gen.cubes.cube); + FREE(gen); + return(NULL); + } + for (i = 0; i <= nvars; i++) gen->stack.stack[i] = NULL; + + /* Find the first cube of the onset. */ + gen->stack.stack[gen->stack.sp] = f; gen->stack.sp++; + + while (1) { + top = gen->stack.stack[gen->stack.sp-1]; + treg = Cudd_Regular(top); + if (!cuddIsConstant(treg)) { + /* Take the else branch first. */ + gen->gen.cubes.cube[treg->index] = 0; + next = cuddE(treg); + if (top != treg) next = Cudd_Not(next); + gen->stack.stack[gen->stack.sp] = next; gen->stack.sp++; + } else if (top == Cudd_Not(DD_ONE(dd)) || top == dd->background) { + /* Backtrack */ + while (1) { + if (gen->stack.sp == 1) { + /* The current node has no predecessor. */ + gen->status = CUDD_GEN_EMPTY; + gen->stack.sp--; + goto done; + } + prev = gen->stack.stack[gen->stack.sp-2]; + preg = Cudd_Regular(prev); + nreg = cuddT(preg); + if (prev != preg) {next = Cudd_Not(nreg);} else {next = nreg;} + if (next != top) { /* follow the then branch next */ + gen->gen.cubes.cube[preg->index] = 1; + gen->stack.stack[gen->stack.sp-1] = next; + break; + } + /* Pop the stack and try again. */ + gen->gen.cubes.cube[preg->index] = 2; + gen->stack.sp--; + top = gen->stack.stack[gen->stack.sp-1]; + treg = Cudd_Regular(top); + } + } else { + gen->status = CUDD_GEN_NONEMPTY; + gen->gen.cubes.value = cuddV(top); + goto done; + } + } + +done: + *cube = gen->gen.cubes.cube; + *value = gen->gen.cubes.value; + return(gen); + +} /* end of Cudd_FirstCube */ + + +/**Function******************************************************************** + + Synopsis [Generates the next cube of a decision diagram onset.] + + Description [Generates the next cube of a decision diagram onset, + using generator gen. Returns 0 if the enumeration is completed; 1 + otherwise.] + + SideEffects [The cube and its value are returned as side effects. The + generator is modified.] + + SeeAlso [Cudd_ForeachCube Cudd_FirstCube Cudd_GenFree Cudd_IsGenEmpty + Cudd_NextNode] + +******************************************************************************/ +int +Cudd_NextCube( + DdGen * gen, + int ** cube, + CUDD_VALUE_TYPE * value) +{ + DdNode *top, *treg, *next, *nreg, *prev, *preg; + DdManager *dd = gen->manager; + + /* Backtrack from previously reached terminal node. */ + while (1) { + if (gen->stack.sp == 1) { + /* The current node has no predecessor. */ + gen->status = CUDD_GEN_EMPTY; + gen->stack.sp--; + goto done; + } + top = gen->stack.stack[gen->stack.sp-1]; + treg = Cudd_Regular(top); + prev = gen->stack.stack[gen->stack.sp-2]; + preg = Cudd_Regular(prev); + nreg = cuddT(preg); + if (prev != preg) {next = Cudd_Not(nreg);} else {next = nreg;} + if (next != top) { /* follow the then branch next */ + gen->gen.cubes.cube[preg->index] = 1; + gen->stack.stack[gen->stack.sp-1] = next; + break; + } + /* Pop the stack and try again. */ + gen->gen.cubes.cube[preg->index] = 2; + gen->stack.sp--; + } + + while (1) { + top = gen->stack.stack[gen->stack.sp-1]; + treg = Cudd_Regular(top); + if (!cuddIsConstant(treg)) { + /* Take the else branch first. */ + gen->gen.cubes.cube[treg->index] = 0; + next = cuddE(treg); + if (top != treg) next = Cudd_Not(next); + gen->stack.stack[gen->stack.sp] = next; gen->stack.sp++; + } else if (top == Cudd_Not(DD_ONE(dd)) || top == dd->background) { + /* Backtrack */ + while (1) { + if (gen->stack.sp == 1) { + /* The current node has no predecessor. */ + gen->status = CUDD_GEN_EMPTY; + gen->stack.sp--; + goto done; + } + prev = gen->stack.stack[gen->stack.sp-2]; + preg = Cudd_Regular(prev); + nreg = cuddT(preg); + if (prev != preg) {next = Cudd_Not(nreg);} else {next = nreg;} + if (next != top) { /* follow the then branch next */ + gen->gen.cubes.cube[preg->index] = 1; + gen->stack.stack[gen->stack.sp-1] = next; + break; + } + /* Pop the stack and try again. */ + gen->gen.cubes.cube[preg->index] = 2; + gen->stack.sp--; + top = gen->stack.stack[gen->stack.sp-1]; + treg = Cudd_Regular(top); + } + } else { + gen->status = CUDD_GEN_NONEMPTY; + gen->gen.cubes.value = cuddV(top); + goto done; + } + } + +done: + if (gen->status == CUDD_GEN_EMPTY) return(0); + *cube = gen->gen.cubes.cube; + *value = gen->gen.cubes.value; + return(1); + +} /* end of Cudd_NextCube */ + + +/**Function******************************************************************** + + Synopsis [Computes the cube of an array of BDD variables.] + + Description [Computes the cube of an array of BDD variables. If + non-null, the phase argument indicates which literal of each + variable should appear in the cube. If phase\[i\] is nonzero, then the + positive literal is used. If phase is NULL, the cube is positive unate. + Returns a pointer to the result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addComputeCube Cudd_IndicesToCube Cudd_CubeArrayToBdd] + +******************************************************************************/ +DdNode * +Cudd_bddComputeCube( + DdManager * dd, + DdNode ** vars, + int * phase, + int n) +{ + DdNode *cube; + DdNode *fn; + int i; + + cube = DD_ONE(dd); + cuddRef(cube); + + for (i = n - 1; i >= 0; i--) { + if (phase == NULL || phase[i] != 0) { + fn = Cudd_bddAnd(dd,vars[i],cube); + } else { + fn = Cudd_bddAnd(dd,Cudd_Not(vars[i]),cube); + } + if (fn == NULL) { + Cudd_RecursiveDeref(dd,cube); + return(NULL); + } + cuddRef(fn); + Cudd_RecursiveDeref(dd,cube); + cube = fn; + } + cuddDeref(cube); + + return(cube); + +} /* end of Cudd_bddComputeCube */ + + +/**Function******************************************************************** + + Synopsis [Computes the cube of an array of ADD variables.] + + Description [Computes the cube of an array of ADD variables. If + non-null, the phase argument indicates which literal of each + variable should appear in the cube. If phase\[i\] is nonzero, then the + positive literal is used. If phase is NULL, the cube is positive unate. + Returns a pointer to the result if successful; NULL otherwise.] + + SideEffects [none] + + SeeAlso [Cudd_bddComputeCube] + +******************************************************************************/ +DdNode * +Cudd_addComputeCube( + DdManager * dd, + DdNode ** vars, + int * phase, + int n) +{ + DdNode *cube, *zero; + DdNode *fn; + int i; + + cube = DD_ONE(dd); + cuddRef(cube); + zero = DD_ZERO(dd); + + for (i = n - 1; i >= 0; i--) { + if (phase == NULL || phase[i] != 0) { + fn = Cudd_addIte(dd,vars[i],cube,zero); + } else { + fn = Cudd_addIte(dd,vars[i],zero,cube); + } + if (fn == NULL) { + Cudd_RecursiveDeref(dd,cube); + return(NULL); + } + cuddRef(fn); + Cudd_RecursiveDeref(dd,cube); + cube = fn; + } + cuddDeref(cube); + + return(cube); + +} /* end of Cudd_addComputeCube */ + + +/**Function******************************************************************** + + Synopsis [Builds the BDD of a cube from a positional array.] + + Description [Builds a cube from a positional array. The array must + have one integer entry for each BDD variable. If the i-th entry is + 1, the variable of index i appears in true form in the cube; If the + i-th entry is 0, the variable of index i appears complemented in the + cube; otherwise the variable does not appear in the cube. Returns a + pointer to the BDD for the cube if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddComputeCube Cudd_IndicesToCube Cudd_BddToCubeArray] + +******************************************************************************/ +DdNode * +Cudd_CubeArrayToBdd( + DdManager *dd, + int *array) +{ + DdNode *cube, *var, *tmp; + int i; + int size = Cudd_ReadSize(dd); + + cube = DD_ONE(dd); + cuddRef(cube); + for (i = size - 1; i >= 0; i--) { + if ((array[i] & ~1) == 0) { + var = Cudd_bddIthVar(dd,i); + tmp = Cudd_bddAnd(dd,cube,Cudd_NotCond(var,array[i]==0)); + if (tmp == NULL) { + Cudd_RecursiveDeref(dd,cube); + return(NULL); + } + cuddRef(tmp); + Cudd_RecursiveDeref(dd,cube); + cube = tmp; + } + } + cuddDeref(cube); + return(cube); + +} /* end of Cudd_CubeArrayToBdd */ + + +/**Function******************************************************************** + + Synopsis [Builds a positional array from the BDD of a cube.] + + Description [Builds a positional array from the BDD of a cube. + Array must have one entry for each BDD variable. The positional + array has 1 in i-th position if the variable of index i appears in + true form in the cube; it has 0 in i-th position if the variable of + index i appears in complemented form in the cube; finally, it has 2 + in i-th position if the variable of index i does not appear in the + cube. Returns 1 if successful (the BDD is indeed a cube); 0 + otherwise.] + + SideEffects [The result is in the array passed by reference.] + + SeeAlso [Cudd_CubeArrayToBdd] + +******************************************************************************/ +int +Cudd_BddToCubeArray( + DdManager *dd, + DdNode *cube, + int *array) +{ + DdNode *scan, *t, *e; + int i; + int size = Cudd_ReadSize(dd); + DdNode *zero = Cudd_Not(DD_ONE(dd)); + + for (i = size-1; i >= 0; i--) { + array[i] = 2; + } + scan = cube; + while (!Cudd_IsConstant(scan)) { + int index = Cudd_Regular(scan)->index; + cuddGetBranches(scan,&t,&e); + if (t == zero) { + array[index] = 0; + scan = e; + } else if (e == zero) { + array[index] = 1; + scan = t; + } else { + return(0); /* cube is not a cube */ + } + } + if (scan == zero) { + return(0); + } else { + return(1); + } + +} /* end of Cudd_BddToCubeArray */ + + +/**Function******************************************************************** + + Synopsis [Finds the first node of a decision diagram.] + + Description [Defines an iterator on the nodes of a decision diagram + and finds its first node. Returns a generator that contains the + information necessary to continue the enumeration if successful; NULL + otherwise.] + + SideEffects [The first node is returned as a side effect.] + + SeeAlso [Cudd_ForeachNode Cudd_NextNode Cudd_GenFree Cudd_IsGenEmpty + Cudd_FirstCube] + +******************************************************************************/ +DdGen * +Cudd_FirstNode( + DdManager * dd, + DdNode * f, + DdNode ** node) +{ + DdGen *gen; + int retval; + + /* Sanity Check. */ + if (dd == NULL || f == NULL) return(NULL); + + /* Allocate generator an initialize it. */ + gen = ALLOC(DdGen,1); + if (gen == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + + gen->manager = dd; + gen->type = CUDD_GEN_NODES; + gen->status = CUDD_GEN_EMPTY; + gen->gen.nodes.visited = NULL; + gen->gen.nodes.stGen = NULL; + gen->stack.sp = 0; + gen->stack.stack = NULL; + gen->node = NULL; + + gen->gen.nodes.visited = st_init_table(st_ptrcmp,st_ptrhash); + if (gen->gen.nodes.visited == NULL) { + FREE(gen); + return(NULL); + } + + /* Collect all the nodes in a st table for later perusal. */ + retval = cuddCollectNodes(Cudd_Regular(f),gen->gen.nodes.visited); + if (retval == 0) { + st_free_table(gen->gen.nodes.visited); + FREE(gen); + return(NULL); + } + + /* Initialize the st table generator. */ + gen->gen.nodes.stGen = st_init_gen(gen->gen.nodes.visited); + if (gen->gen.nodes.stGen == NULL) { + st_free_table(gen->gen.nodes.visited); + FREE(gen); + return(NULL); + } + + /* Find the first node. */ + retval = st_gen(gen->gen.nodes.stGen, (char **) &(gen->node), NULL); + if (retval != 0) { + gen->status = CUDD_GEN_NONEMPTY; + *node = gen->node; + } + + return(gen); + +} /* end of Cudd_FirstNode */ + + +/**Function******************************************************************** + + Synopsis [Finds the next node of a decision diagram.] + + Description [Finds the node of a decision diagram, using generator + gen. Returns 0 if the enumeration is completed; 1 otherwise.] + + SideEffects [The next node is returned as a side effect.] + + SeeAlso [Cudd_ForeachNode Cudd_FirstNode Cudd_GenFree Cudd_IsGenEmpty + Cudd_NextCube] + +******************************************************************************/ +int +Cudd_NextNode( + DdGen * gen, + DdNode ** node) +{ + int retval; + + /* Find the next node. */ + retval = st_gen(gen->gen.nodes.stGen, (char **) &(gen->node), NULL); + if (retval == 0) { + gen->status = CUDD_GEN_EMPTY; + } else { + *node = gen->node; + } + + return(retval); + +} /* end of Cudd_NextNode */ + + +/**Function******************************************************************** + + Synopsis [Frees a CUDD generator.] + + Description [Frees a CUDD generator. Always returns 0, so that it can + be used in mis-like foreach constructs.] + + SideEffects [None] + + SeeAlso [Cudd_ForeachCube Cudd_ForeachNode Cudd_FirstCube Cudd_NextCube + Cudd_FirstNode Cudd_NextNode Cudd_IsGenEmpty] + +******************************************************************************/ +int +Cudd_GenFree( + DdGen * gen) +{ + + if (gen == NULL) return(0); + switch (gen->type) { + case CUDD_GEN_CUBES: + case CUDD_GEN_ZDD_PATHS: + FREE(gen->gen.cubes.cube); + FREE(gen->stack.stack); + break; + case CUDD_GEN_NODES: + st_free_gen(gen->gen.nodes.stGen); + st_free_table(gen->gen.nodes.visited); + break; + default: + return(0); + } + FREE(gen); + return(0); + +} /* end of Cudd_GenFree */ + + +/**Function******************************************************************** + + Synopsis [Queries the status of a generator.] + + Description [Queries the status of a generator. Returns 1 if the + generator is empty or NULL; 0 otherswise.] + + SideEffects [None] + + SeeAlso [Cudd_ForeachCube Cudd_ForeachNode Cudd_FirstCube Cudd_NextCube + Cudd_FirstNode Cudd_NextNode Cudd_GenFree] + +******************************************************************************/ +int +Cudd_IsGenEmpty( + DdGen * gen) +{ + if (gen == NULL) return(1); + return(gen->status == CUDD_GEN_EMPTY); + +} /* end of Cudd_IsGenEmpty */ + + +/**Function******************************************************************** + + Synopsis [Builds a cube of BDD variables from an array of indices.] + + Description [Builds a cube of BDD variables from an array of indices. + Returns a pointer to the result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddComputeCube Cudd_CubeArrayToBdd] + +******************************************************************************/ +DdNode * +Cudd_IndicesToCube( + DdManager * dd, + int * array, + int n) +{ + DdNode *cube, *tmp; + int i; + + cube = DD_ONE(dd); + cuddRef(cube); + for (i = n - 1; i >= 0; i--) { + tmp = Cudd_bddAnd(dd,Cudd_bddIthVar(dd,array[i]),cube); + if (tmp == NULL) { + Cudd_RecursiveDeref(dd,cube); + return(NULL); + } + cuddRef(tmp); + Cudd_RecursiveDeref(dd,cube); + cube = tmp; + } + + cuddDeref(cube); + return(cube); + +} /* end of Cudd_IndicesToCube */ + + +/**Function******************************************************************** + + Synopsis [Prints the package version number.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +Cudd_PrintVersion( + FILE * fp) +{ + (void) fprintf(fp, "%s\n", CUDD_VERSION); + +} /* end of Cudd_PrintVersion */ + + +/**Function******************************************************************** + + Synopsis [Computes the average distance between adjacent nodes.] + + Description [Computes the average distance between adjacent nodes in + the manager. Adjacent nodes are node pairs such that the second node + is the then child, else child, or next node in the collision list.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +double +Cudd_AverageDistance( + DdManager * dd) +{ + double tetotal, nexttotal; + double tesubtotal, nextsubtotal; + double temeasured, nextmeasured; + int i, j; + int slots, nvars; + long diff; + DdNode *scan; + DdNodePtr *nodelist; + DdNode *sentinel = &(dd->sentinel); + + nvars = dd->size; + if (nvars == 0) return(0.0); + + /* Initialize totals. */ + tetotal = 0.0; + nexttotal = 0.0; + temeasured = 0.0; + nextmeasured = 0.0; + + /* Scan the variable subtables. */ + for (i = 0; i < nvars; i++) { + nodelist = dd->subtables[i].nodelist; + tesubtotal = 0.0; + nextsubtotal = 0.0; + slots = dd->subtables[i].slots; + for (j = 0; j < slots; j++) { + scan = nodelist[j]; + while (scan != sentinel) { + diff = (long) scan - (long) cuddT(scan); + tesubtotal += (double) ddAbs(diff); + diff = (long) scan - (long) Cudd_Regular(cuddE(scan)); + tesubtotal += (double) ddAbs(diff); + temeasured += 2.0; + if (scan->next != NULL) { + diff = (long) scan - (long) scan->next; + nextsubtotal += (double) ddAbs(diff); + nextmeasured += 1.0; + } + scan = scan->next; + } + } + tetotal += tesubtotal; + nexttotal += nextsubtotal; + } + + /* Scan the constant table. */ + nodelist = dd->constants.nodelist; + nextsubtotal = 0.0; + slots = dd->constants.slots; + for (j = 0; j < slots; j++) { + scan = nodelist[j]; + while (scan != NULL) { + if (scan->next != NULL) { + diff = (long) scan - (long) scan->next; + nextsubtotal += (double) ddAbs(diff); + nextmeasured += 1.0; + } + scan = scan->next; + } + } + nexttotal += nextsubtotal; + + return((tetotal + nexttotal) / (temeasured + nextmeasured)); + +} /* end of Cudd_AverageDistance */ + + +/**Function******************************************************************** + + Synopsis [Portable random number generator.] + + Description [Portable number generator based on ran2 from "Numerical + Recipes in C." It is a long period (> 2 * 10^18) random number generator + of L'Ecuyer with Bays-Durham shuffle. Returns a long integer uniformly + distributed between 0 and 2147483561 (inclusive of the endpoint values). + The random generator can be explicitly initialized by calling + Cudd_Srandom. If no explicit initialization is performed, then the + seed 1 is assumed.] + + SideEffects [None] + + SeeAlso [Cudd_Srandom] + +******************************************************************************/ +long +Cudd_Random( + ) +{ + int i; /* index in the shuffle table */ + long int w; /* work variable */ + + /* cuddRand == 0 if the geneartor has not been initialized yet. */ + if (cuddRand == 0) Cudd_Srandom(1); + + /* Compute cuddRand = (cuddRand * LEQA1) % MODULUS1 avoiding + ** overflows by Schrage's method. + */ + w = cuddRand / LEQQ1; + cuddRand = LEQA1 * (cuddRand - w * LEQQ1) - w * LEQR1; + cuddRand += (cuddRand < 0) * MODULUS1; + + /* Compute cuddRand2 = (cuddRand2 * LEQA2) % MODULUS2 avoiding + ** overflows by Schrage's method. + */ + w = cuddRand2 / LEQQ2; + cuddRand2 = LEQA2 * (cuddRand2 - w * LEQQ2) - w * LEQR2; + cuddRand2 += (cuddRand2 < 0) * MODULUS2; + + /* cuddRand is shuffled with the Bays-Durham algorithm. + ** shuffleSelect and cuddRand2 are combined to generate the output. + */ + + /* Pick one element from the shuffle table; "i" will be in the range + ** from 0 to STAB_SIZE-1. + */ + i = (int) (shuffleSelect / STAB_DIV); + /* Mix the element of the shuffle table with the current iterate of + ** the second sub-generator, and replace the chosen element of the + ** shuffle table with the current iterate of the first sub-generator. + */ + shuffleSelect = shuffleTable[i] - cuddRand2; + shuffleTable[i] = cuddRand; + shuffleSelect += (shuffleSelect < 1) * (MODULUS1 - 1); + /* Since shuffleSelect != 0, and we want to be able to return 0, + ** here we subtract 1 before returning. + */ + return(shuffleSelect - 1); + +} /* end of Cudd_Random */ + + +/**Function******************************************************************** + + Synopsis [Initializer for the portable random number generator.] + + Description [Initializer for the portable number generator based on + ran2 in "Numerical Recipes in C." The input is the seed for the + generator. If it is negative, its absolute value is taken as seed. + If it is 0, then 1 is taken as seed. The initialized sets up the two + recurrences used to generate a long-period stream, and sets up the + shuffle table.] + + SideEffects [None] + + SeeAlso [Cudd_Random] + +******************************************************************************/ +void +Cudd_Srandom( + long seed) +{ + int i; + + if (seed < 0) cuddRand = -seed; + else if (seed == 0) cuddRand = 1; + else cuddRand = seed; + cuddRand2 = cuddRand; + /* Load the shuffle table (after 11 warm-ups). */ + for (i = 0; i < STAB_SIZE + 11; i++) { + long int w; + w = cuddRand / LEQQ1; + cuddRand = LEQA1 * (cuddRand - w * LEQQ1) - w * LEQR1; + cuddRand += (cuddRand < 0) * MODULUS1; + shuffleTable[i % STAB_SIZE] = cuddRand; + } + shuffleSelect = shuffleTable[1 % STAB_SIZE]; + +} /* end of Cudd_Srandom */ + + +/**Function******************************************************************** + + Synopsis [Computes the density of a BDD or ADD.] + + Description [Computes the density of a BDD or ADD. The density is + the ratio of the number of minterms to the number of nodes. If 0 is + passed as number of variables, the number of variables existing in + the manager is used. Returns the density if successful; (double) + CUDD_OUT_OF_MEM otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_CountMinterm Cudd_DagSize] + +******************************************************************************/ +double +Cudd_Density( + DdManager * dd /* manager */, + DdNode * f /* function whose density is sought */, + int nvars /* size of the support of f */) +{ + double minterms; + int nodes; + double density; + + if (nvars == 0) nvars = dd->size; + minterms = Cudd_CountMinterm(dd,f,nvars); + if (minterms == (double) CUDD_OUT_OF_MEM) return(minterms); + nodes = Cudd_DagSize(f); + density = minterms / (double) nodes; + return(density); + +} /* end of Cudd_Density */ + + +/**Function******************************************************************** + + Synopsis [Warns that a memory allocation failed.] + + Description [Warns that a memory allocation failed. + This function can be used as replacement of MMout_of_memory to prevent + the safe_mem functions of the util package from exiting when malloc + returns NULL. One possible use is in case of discretionary allocations; + for instance, the allocation of memory to enlarge the computed table.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +Cudd_OutOfMem( + long size /* size of the allocation that failed */) +{ + (void) fflush(stdout); + (void) fprintf(stderr, "\nunable to allocate %ld bytes\n", size); + return; + +} /* end of Cudd_OutOfMem */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Prints a DD to the standard output. One line per node is + printed.] + + Description [Prints a DD to the standard output. One line per node is + printed. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_PrintDebug] + +******************************************************************************/ +int +cuddP( + DdManager * dd, + DdNode * f) +{ + int retval; + st_table *table = st_init_table(st_ptrcmp,st_ptrhash); + + if (table == NULL) return(0); + + retval = dp2(dd,f,table); + st_free_table(table); + (void) fputc('\n',dd->out); + return(retval); + +} /* end of cuddP */ + + +/**Function******************************************************************** + + Synopsis [Frees the memory used to store the minterm counts recorded + in the visited table.] + + Description [Frees the memory used to store the minterm counts + recorded in the visited table. Returns ST_CONTINUE.] + + SideEffects [None] + +******************************************************************************/ +enum st_retval +cuddStCountfree( + char * key, + char * value, + char * arg) +{ + double *d; + + d = (double *)value; + FREE(d); + return(ST_CONTINUE); + +} /* end of cuddStCountfree */ + + +/**Function******************************************************************** + + Synopsis [Recursively collects all the nodes of a DD in a symbol + table.] + + Description [Traverses the BDD f and collects all its nodes in a + symbol table. f is assumed to be a regular pointer and + cuddCollectNodes guarantees this assumption in the recursive calls. + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddCollectNodes( + DdNode * f, + st_table * visited) +{ + DdNode *T, *E; + int retval; + +#ifdef DD_DEBUG + assert(!Cudd_IsComplement(f)); +#endif + + /* If already visited, nothing to do. */ + if (st_is_member(visited, (char *) f) == 1) + return(1); + + /* Check for abnormal condition that should never happen. */ + if (f == NULL) + return(0); + + /* Mark node as visited. */ + if (st_add_direct(visited, (char *) f, NULL) == ST_OUT_OF_MEM) + return(0); + + /* Check terminal case. */ + if (cuddIsConstant(f)) + return(1); + + /* Recursive calls. */ + T = cuddT(f); + retval = cuddCollectNodes(T,visited); + if (retval != 1) return(retval); + E = Cudd_Regular(cuddE(f)); + retval = cuddCollectNodes(E,visited); + return(retval); + +} /* end of cuddCollectNodes */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of cuddP.] + + Description [Performs the recursive step of cuddP. Returns 1 in case + of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +dp2( + DdManager *dd, + DdNode * f, + st_table * t) +{ + DdNode *g, *n, *N; + int T,E; + + if (f == NULL) { + return(0); + } + g = Cudd_Regular(f); + if (cuddIsConstant(g)) { +#if SIZEOF_VOID_P == 8 + (void) fprintf(dd->out,"ID = %c0x%lx\tvalue = %-9g\n", bang(f), + (unsigned long) g / (unsigned long) sizeof(DdNode),cuddV(g)); +#else + (void) fprintf(dd->out,"ID = %c0x%x\tvalue = %-9g\n", bang(f), + (unsigned) g / (unsigned) sizeof(DdNode),cuddV(g)); +#endif + return(1); + } + if (st_is_member(t,(char *) g) == 1) { + return(1); + } + if (st_add_direct(t,(char *) g,NULL) == ST_OUT_OF_MEM) + return(0); +#ifdef DD_STATS +#if SIZEOF_VOID_P == 8 + (void) fprintf(dd->out,"ID = %c0x%lx\tindex = %d\tr = %d\t", bang(f), + (unsigned long) g / (unsigned long) sizeof(DdNode), g->index, g->ref); +#else + (void) fprintf(dd->out,"ID = %c0x%x\tindex = %d\tr = %d\t", bang(f), + (unsigned) g / (unsigned) sizeof(DdNode),g->index,g->ref); +#endif +#else +#if SIZEOF_VOID_P == 8 + (void) fprintf(dd->out,"ID = %c0x%lx\tindex = %d\t", bang(f), + (unsigned long) g / (unsigned long) sizeof(DdNode),g->index); +#else + (void) fprintf(dd->out,"ID = %c0x%x\tindex = %d\t", bang(f), + (unsigned) g / (unsigned) sizeof(DdNode),g->index); +#endif +#endif + n = cuddT(g); + if (cuddIsConstant(n)) { + (void) fprintf(dd->out,"T = %-9g\t",cuddV(n)); + T = 1; + } else { +#if SIZEOF_VOID_P == 8 + (void) fprintf(dd->out,"T = 0x%lx\t",(unsigned long) n / (unsigned long) sizeof(DdNode)); +#else + (void) fprintf(dd->out,"T = 0x%x\t",(unsigned) n / (unsigned) sizeof(DdNode)); +#endif + T = 0; + } + + n = cuddE(g); + N = Cudd_Regular(n); + if (cuddIsConstant(N)) { + (void) fprintf(dd->out,"E = %c%-9g\n",bang(n),cuddV(N)); + E = 1; + } else { +#if SIZEOF_VOID_P == 8 + (void) fprintf(dd->out,"E = %c0x%lx\n", bang(n), (unsigned long) N/(unsigned long) sizeof(DdNode)); +#else + (void) fprintf(dd->out,"E = %c0x%x\n", bang(n), (unsigned) N/(unsigned) sizeof(DdNode)); +#endif + E = 0; + } + if (E == 0) { + if (dp2(dd,N,t) == 0) + return(0); + } + if (T == 0) { + if (dp2(dd,cuddT(g),t) == 0) + return(0); + } + return(1); + +} /* end of dp2 */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_PrintMinterm.] + + Description [] + + SideEffects [None] + +******************************************************************************/ +static void +ddPrintMintermAux( + DdManager * dd /* manager */, + DdNode * node /* current node */, + int * list /* current recursion path */) +{ + DdNode *N,*Nv,*Nnv; + int i,v,index; + + N = Cudd_Regular(node); + + if (cuddIsConstant(N)) { + /* Terminal case: Print one cube based on the current recursion + ** path, unless we have reached the background value (ADDs) or + ** the logical zero (BDDs). + */ + if (node != background && node != zero) { + for (i = 0; i < dd->size; i++) { + v = list[i]; + if (v == 0) (void) fprintf(dd->out,"0"); + else if (v == 1) (void) fprintf(dd->out,"1"); + else (void) fprintf(dd->out,"-"); + } + (void) fprintf(dd->out," % g\n", cuddV(node)); + } + } else { + Nv = cuddT(N); + Nnv = cuddE(N); + if (Cudd_IsComplement(node)) { + Nv = Cudd_Not(Nv); + Nnv = Cudd_Not(Nnv); + } + index = N->index; + list[index] = 0; + ddPrintMintermAux(dd,Nnv,list); + list[index] = 1; + ddPrintMintermAux(dd,Nv,list); + list[index] = 2; + } + return; + +} /* end of ddPrintMintermAux */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_DagSize.] + + Description [Performs the recursive step of Cudd_DagSize. Returns the + number of nodes in the graph rooted at n.] + + SideEffects [None] + +******************************************************************************/ +static int +ddDagInt( + DdNode * n) +{ + int tval, eval; + + if (Cudd_IsComplement(n->next)) { + return(0); + } + n->next = Cudd_Not(n->next); + if (cuddIsConstant(n)) { + return(1); + } + tval = ddDagInt(cuddT(n)); + eval = ddDagInt(Cudd_Regular(cuddE(n))); + return(1 + tval + eval); + +} /* end of ddDagInt */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_CofactorEstimate.] + + Description [Performs the recursive step of Cudd_CofactorEstimate. + Returns an estimate of the number of nodes in the DD of a + cofactor of node. Uses the least significant bit of the next field as + visited flag. node is supposed to be regular; the invariant is maintained + by this procedure.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddEstimateCofactor( + DdManager *dd, + st_table *table, + DdNode * node, + int i, + int phase, + DdNode ** ptr) +{ + int tval, eval, val; + DdNode *ptrT, *ptrE; + + if (Cudd_IsComplement(node->next)) { + if (!st_lookup(table,(char *)node,(char **)ptr)) { + st_add_direct(table,(char *)node,(char *)node); + *ptr = node; + } + return(0); + } + node->next = Cudd_Not(node->next); + if (cuddIsConstant(node)) { + *ptr = node; + if (st_add_direct(table,(char *)node,(char *)node) == ST_OUT_OF_MEM) + return(CUDD_OUT_OF_MEM); + return(1); + } + if ((int) node->index == i) { + if (phase == 1) { + *ptr = cuddT(node); + val = ddDagInt(cuddT(node)); + } else { + *ptr = cuddE(node); + val = ddDagInt(Cudd_Regular(cuddE(node))); + } + if (node->ref > 1) { + if (st_add_direct(table,(char *)node,(char *)*ptr) == + ST_OUT_OF_MEM) + return(CUDD_OUT_OF_MEM); + } + return(val); + } + if (dd->perm[node->index] > dd->perm[i]) { + *ptr = node; + tval = ddDagInt(cuddT(node)); + eval = ddDagInt(Cudd_Regular(cuddE(node))); + if (node->ref > 1) { + if (st_add_direct(table,(char *)node,(char *)node) == + ST_OUT_OF_MEM) + return(CUDD_OUT_OF_MEM); + } + val = 1 + tval + eval; + return(val); + } + tval = cuddEstimateCofactor(dd,table,cuddT(node),i,phase,&ptrT); + eval = cuddEstimateCofactor(dd,table,Cudd_Regular(cuddE(node)),i, + phase,&ptrE); + ptrE = Cudd_NotCond(ptrE,Cudd_IsComplement(cuddE(node))); + if (ptrT == ptrE) { /* recombination */ + *ptr = ptrT; + val = tval; + if (node->ref > 1) { + if (st_add_direct(table,(char *)node,(char *)*ptr) == + ST_OUT_OF_MEM) + return(CUDD_OUT_OF_MEM); + } + } else if ((ptrT != cuddT(node) || ptrE != cuddE(node)) && + (*ptr = cuddUniqueLookup(dd,node->index,ptrT,ptrE)) != NULL) { + if (Cudd_IsComplement((*ptr)->next)) { + val = 0; + } else { + val = 1 + tval + eval; + } + if (node->ref > 1) { + if (st_add_direct(table,(char *)node,(char *)*ptr) == + ST_OUT_OF_MEM) + return(CUDD_OUT_OF_MEM); + } + } else { + *ptr = node; + val = 1 + tval + eval; + } + return(val); + +} /* end of cuddEstimateCofactor */ + + +/**Function******************************************************************** + + Synopsis [Checks the unique table for the existence of an internal node.] + + Description [Checks the unique table for the existence of an internal + node. Returns a pointer to the node if it is in the table; NULL otherwise.] + + SideEffects [None] + + SeeAlso [cuddUniqueInter] + +******************************************************************************/ +static DdNode * +cuddUniqueLookup( + DdManager * unique, + int index, + DdNode * T, + DdNode * E) +{ + int posn; + unsigned int level; + DdNodePtr *nodelist; + DdNode *looking; + DdSubtable *subtable; + + if (index >= unique->size) { + return(NULL); + } + + level = unique->perm[index]; + subtable = &(unique->subtables[level]); + +#ifdef DD_DEBUG + assert(level < (unsigned) cuddI(unique,T->index)); + assert(level < (unsigned) cuddI(unique,Cudd_Regular(E)->index)); +#endif + + posn = ddHash(T, E, subtable->shift); + nodelist = subtable->nodelist; + looking = nodelist[posn]; + + while (T < cuddT(looking)) { + looking = Cudd_Regular(looking->next); + } + while (T == cuddT(looking) && E < cuddE(looking)) { + looking = Cudd_Regular(looking->next); + } + if (cuddT(looking) == T && cuddE(looking) == E) { + return(looking); + } + + return(NULL); + +} /* end of cuddUniqueLookup */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_CofactorEstimateSimple.] + + Description [Performs the recursive step of Cudd_CofactorEstimateSimple. + Returns an estimate of the number of nodes in the DD of the positive + cofactor of node. Uses the least significant bit of the next field as + visited flag. node is supposed to be regular; the invariant is maintained + by this procedure.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddEstimateCofactorSimple( + DdNode * node, + int i) +{ + int tval, eval; + + if (Cudd_IsComplement(node->next)) { + return(0); + } + node->next = Cudd_Not(node->next); + if (cuddIsConstant(node)) { + return(1); + } + tval = cuddEstimateCofactorSimple(cuddT(node),i); + if ((int) node->index == i) return(tval); + eval = cuddEstimateCofactorSimple(Cudd_Regular(cuddE(node)),i); + return(1 + tval + eval); + +} /* end of cuddEstimateCofactorSimple */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_CountMinterm.] + + Description [Performs the recursive step of Cudd_CountMinterm. + It is based on the following identity. Let |f| be the + number of minterms of f. Then: +

        + |f| = (|f0|+|f1|)/2 + + where f0 and f1 are the two cofactors of f. Does not use the + identity |f'| = max - |f|, to minimize loss of accuracy due to + roundoff. Returns the number of minterms of the function rooted at + node.] + + SideEffects [None] + +******************************************************************************/ +static double +ddCountMintermAux( + DdNode * node, + double max, + DdHashTable * table) +{ + DdNode *N, *Nt, *Ne; + double min, minT, minE; + DdNode *res; + + N = Cudd_Regular(node); + + if (cuddIsConstant(N)) { + if (node == background || node == zero) { + return(0.0); + } else { + return(max); + } + } + if (N->ref != 1 && (res = cuddHashTableLookup1(table,node)) != NULL) { + min = cuddV(res); + if (res->ref == 0) { + table->manager->dead++; + table->manager->constants.dead++; + } + return(min); + } + + Nt = cuddT(N); Ne = cuddE(N); + if (Cudd_IsComplement(node)) { + Nt = Cudd_Not(Nt); Ne = Cudd_Not(Ne); + } + + minT = ddCountMintermAux(Nt,max,table); + if (minT == (double)CUDD_OUT_OF_MEM) return((double)CUDD_OUT_OF_MEM); + minT *= 0.5; + minE = ddCountMintermAux(Ne,max,table); + if (minE == (double)CUDD_OUT_OF_MEM) return((double)CUDD_OUT_OF_MEM); + minE *= 0.5; + min = minT + minE; + + if (N->ref != 1) { + ptrint fanout = (ptrint) N->ref; + cuddSatDec(fanout); + res = cuddUniqueConst(table->manager,min); + if (!cuddHashTableInsert1(table,node,res,fanout)) { + cuddRef(res); Cudd_RecursiveDeref(table->manager, res); + return((double)CUDD_OUT_OF_MEM); + } + } + + return(min); + +} /* end of ddCountMintermAux */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_CountPath.] + + Description [Performs the recursive step of Cudd_CountPath. + It is based on the following identity. Let |f| be the + number of paths of f. Then: + + |f| = |f0|+|f1| + + where f0 and f1 are the two cofactors of f. Uses the + identity |f'| = |f|, to improve the utilization of the (local) cache. + Returns the number of paths of the function rooted at node.] + + SideEffects [None] + +******************************************************************************/ +static double +ddCountPathAux( + DdNode * node, + st_table * table) +{ + + DdNode *Nv, *Nnv; + double paths, *ppaths, paths1, paths2; + double *dummy; + + + if (cuddIsConstant(node)) { + return(1.0); + } + if (st_lookup(table, (char *)node, (char **)&dummy)) { + paths = *dummy; + return(paths); + } + + Nv = cuddT(node); Nnv = cuddE(node); + + paths1 = ddCountPathAux(Nv,table); + if (paths1 == (double)CUDD_OUT_OF_MEM) return((double)CUDD_OUT_OF_MEM); + paths2 = ddCountPathAux(Cudd_Regular(Nnv),table); + if (paths2 == (double)CUDD_OUT_OF_MEM) return((double)CUDD_OUT_OF_MEM); + paths = paths1 + paths2; + + ppaths = ALLOC(double,1); + if (ppaths == NULL) { + return((double)CUDD_OUT_OF_MEM); + } + + *ppaths = paths; + + if (st_add_direct(table,(char *)node, (char *)ppaths) == ST_OUT_OF_MEM) { + FREE(ppaths); + return((double)CUDD_OUT_OF_MEM); + } + return(paths); + +} /* end of ddCountPathAux */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_CountMinterm.] + + Description [Performs the recursive step of Cudd_CountMinterm. + It is based on the following identity. Let |f| be the + number of minterms of f. Then: + + |f| = (|f0|+|f1|)/2 + + where f0 and f1 are the two cofactors of f. Does not use the + identity |f'| = max - |f|, to minimize loss of accuracy due to + roundoff. Returns the number of minterms of the function rooted at + node.] + + SideEffects [None] + +******************************************************************************/ +static int +ddEpdCountMintermAux( + DdNode * node, + EpDouble * max, + EpDouble * epd, + st_table * table) +{ + DdNode *Nt, *Ne; + EpDouble *min, minT, minE; + EpDouble *res; + int status; + + if (cuddIsConstant(node)) { + if (node == background || node == zero) { + EpdMakeZero(epd, 0); + } else { + EpdCopy(max, epd); + } + return(0); + } + if (node->ref != 1 && st_lookup(table, (char *)node, (char **)&res)) { + EpdCopy(res, epd); + return(0); + } + + Nt = cuddT(node); Ne = cuddE(node); + if (Cudd_IsComplement(node)) { + Nt = Cudd_Not(Nt); Ne = Cudd_Not(Ne); + } + + status = ddEpdCountMintermAux(Nt,max,&minT,table); + if (status == CUDD_OUT_OF_MEM) return(CUDD_OUT_OF_MEM); + EpdMultiply(&minT, (double)0.5); + status = ddEpdCountMintermAux(Ne,max,&minE,table); + if (status == CUDD_OUT_OF_MEM) return(CUDD_OUT_OF_MEM); + if (Cudd_IsComplement(Ne)) { + EpdSubtract3(max, &minE, epd); + EpdCopy(epd, &minE); + } + EpdMultiply(&minE, (double)0.5); + EpdAdd3(&minT, &minE, epd); + + if (node->ref > 1) { + min = EpdAlloc(); + if (!min) + return(CUDD_OUT_OF_MEM); + EpdCopy(epd, min); + if (st_insert(table, (char *)node, (char *)min) == ST_OUT_OF_MEM) { + EpdFree(min); + return(CUDD_OUT_OF_MEM); + } + } + + return(0); + +} /* end of ddEpdCountMintermAux */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_CountPathsToNonZero.] + + Description [Performs the recursive step of Cudd_CountPathsToNonZero. + It is based on the following identity. Let |f| be the + number of paths of f. Then: + + |f| = |f0|+|f1| + + where f0 and f1 are the two cofactors of f. Returns the number of + paths of the function rooted at node.] + + SideEffects [None] + +******************************************************************************/ +static double +ddCountPathsToNonZero( + DdNode * N, + st_table * table) +{ + + DdNode *node, *Nt, *Ne; + double paths, *ppaths, paths1, paths2; + double *dummy; + + node = Cudd_Regular(N); + if (cuddIsConstant(node)) { + return((double) !(Cudd_IsComplement(N) || cuddV(node)==DD_ZERO_VAL)); + } + if (st_lookup(table, (char *)N, (char **)&dummy)) { + paths = *dummy; + return(paths); + } + + Nt = cuddT(node); Ne = cuddE(node); + if (node != N) { + Nt = Cudd_Not(Nt); Ne = Cudd_Not(Ne); + } + + paths1 = ddCountPathsToNonZero(Nt,table); + if (paths1 == (double)CUDD_OUT_OF_MEM) return((double)CUDD_OUT_OF_MEM); + paths2 = ddCountPathsToNonZero(Ne,table); + if (paths2 == (double)CUDD_OUT_OF_MEM) return((double)CUDD_OUT_OF_MEM); + paths = paths1 + paths2; + + ppaths = ALLOC(double,1); + if (ppaths == NULL) { + return((double)CUDD_OUT_OF_MEM); + } + + *ppaths = paths; + + if (st_add_direct(table,(char *)N, (char *)ppaths) == ST_OUT_OF_MEM) { + FREE(ppaths); + return((double)CUDD_OUT_OF_MEM); + } + return(paths); + +} /* end of ddCountPathsToNonZero */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_Support.] + + Description [Performs the recursive step of Cudd_Support. Performs a + DFS from f. The support is accumulated in supp as a side effect. Uses + the LSB of the then pointer as visited flag.] + + SideEffects [None] + + SeeAlso [ddClearFlag] + +******************************************************************************/ +static void +ddSupportStep( + DdNode * f, + int * support) +{ + if (cuddIsConstant(f) || Cudd_IsComplement(f->next)) { + return; + } + + support[f->index] = 1; + ddSupportStep(cuddT(f),support); + ddSupportStep(Cudd_Regular(cuddE(f)),support); + /* Mark as visited. */ + f->next = Cudd_Not(f->next); + return; + +} /* end of ddSupportStep */ + + +/**Function******************************************************************** + + Synopsis [Performs a DFS from f, clearing the LSB of the next + pointers.] + + Description [] + + SideEffects [None] + + SeeAlso [ddSupportStep ddDagInt] + +******************************************************************************/ +static void +ddClearFlag( + DdNode * f) +{ + if (!Cudd_IsComplement(f->next)) { + return; + } + /* Clear visited flag. */ + f->next = Cudd_Regular(f->next); + if (cuddIsConstant(f)) { + return; + } + ddClearFlag(cuddT(f)); + ddClearFlag(Cudd_Regular(cuddE(f))); + return; + +} /* end of ddClearFlag */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_CountLeaves.] + + Description [Performs the recursive step of Cudd_CountLeaves. Returns + the number of leaves in the DD rooted at n.] + + SideEffects [None] + + SeeAlso [Cudd_CountLeaves] + +******************************************************************************/ +static int +ddLeavesInt( + DdNode * n) +{ + int tval, eval; + + if (Cudd_IsComplement(n->next)) { + return(0); + } + n->next = Cudd_Not(n->next); + if (cuddIsConstant(n)) { + return(1); + } + tval = ddLeavesInt(cuddT(n)); + eval = ddLeavesInt(Cudd_Regular(cuddE(n))); + return(tval + eval); + +} /* end of ddLeavesInt */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_bddPickArbitraryMinterms.] + + Description [Performs the recursive step of Cudd_bddPickArbitraryMinterms. + Returns 1 if successful; 0 otherwise.] + + SideEffects [none] + + SeeAlso [Cudd_bddPickArbitraryMinterms] + +******************************************************************************/ +static int +ddPickArbitraryMinterms( + DdManager *dd, + DdNode *node, + int nvars, + int nminterms, + char **string) +{ + DdNode *N, *T, *E; + DdNode *one, *bzero; + int i, t, result; + double min1, min2; + + if (string == NULL || node == NULL) return(0); + + /* The constant 0 function has no on-set cubes. */ + one = DD_ONE(dd); + bzero = Cudd_Not(one); + if (nminterms == 0 || node == bzero) return(1); + if (node == one) { + return(1); + } + + N = Cudd_Regular(node); + T = cuddT(N); E = cuddE(N); + if (Cudd_IsComplement(node)) { + T = Cudd_Not(T); E = Cudd_Not(E); + } + + min1 = Cudd_CountMinterm(dd, T, nvars) / 2.0; + if (min1 == (double)CUDD_OUT_OF_MEM) return(0); + min2 = Cudd_CountMinterm(dd, E, nvars) / 2.0; + if (min2 == (double)CUDD_OUT_OF_MEM) return(0); + + t = (int)((double)nminterms * min1 / (min1 + min2) + 0.5); + for (i = 0; i < t; i++) + string[i][N->index] = '1'; + for (i = t; i < nminterms; i++) + string[i][N->index] = '0'; + + result = ddPickArbitraryMinterms(dd,T,nvars,t,&string[0]); + if (result == 0) + return(0); + result = ddPickArbitraryMinterms(dd,E,nvars,nminterms-t,&string[t]); + return(result); + +} /* end of ddPickArbitraryMinterms */ + + +/**Function******************************************************************** + + Synopsis [Finds a representative cube of a BDD.] + + Description [Finds a representative cube of a BDD with the weight of + each variable. From the top variable, if the weight is greater than or + equal to 0.0, choose THEN branch unless the child is the constant 0. + Otherwise, choose ELSE branch unless the child is the constant 0.] + + SideEffects [Cudd_SubsetWithMaskVars Cudd_bddPickOneCube] + +******************************************************************************/ +static int +ddPickRepresentativeCube( + DdManager *dd, + DdNode *node, + int nvars, + double *weight, + char *string) +{ + DdNode *N, *T, *E; + DdNode *one, *bzero; + + if (string == NULL || node == NULL) return(0); + + /* The constant 0 function has no on-set cubes. */ + one = DD_ONE(dd); + bzero = Cudd_Not(one); + if (node == bzero) return(0); + + if (node == DD_ONE(dd)) return(1); + + for (;;) { + N = Cudd_Regular(node); + if (N == one) + break; + T = cuddT(N); + E = cuddE(N); + if (Cudd_IsComplement(node)) { + T = Cudd_Not(T); + E = Cudd_Not(E); + } + if (weight[N->index] >= 0.0) { + if (T == bzero) { + node = E; + string[N->index] = '0'; + } else { + node = T; + string[N->index] = '1'; + } + } else { + if (E == bzero) { + node = T; + string[N->index] = '1'; + } else { + node = E; + string[N->index] = '0'; + } + } + } + return(1); + +} /* end of ddPickRepresentativeCube */ + + +/**Function******************************************************************** + + Synopsis [Frees the memory used to store the minterm counts recorded + in the visited table.] + + Description [Frees the memory used to store the minterm counts + recorded in the visited table. Returns ST_CONTINUE.] + + SideEffects [None] + +******************************************************************************/ +static enum st_retval +ddEpdFree( + char * key, + char * value, + char * arg) +{ + EpDouble *epd; + + epd = (EpDouble *) value; + EpdFree(epd); + return(ST_CONTINUE); + +} /* end of ddEpdFree */ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddWindow.c b/abc_with_bb_support/src/bdd/cudd/cuddWindow.c new file mode 100644 index 000000000..99bf487eb --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddWindow.c @@ -0,0 +1,997 @@ +/**CFile*********************************************************************** + + FileName [cuddWindow.c] + + PackageName [cudd] + + Synopsis [Functions for window permutation] + + Description [Internal procedures included in this module: +
          +
        • cuddWindowReorder() +
        + Static procedures included in this module: +
          +
        • ddWindow2() +
        • ddWindowConv2() +
        • ddPermuteWindow3() +
        • ddWindow3() +
        • ddWindowConv3() +
        • ddPermuteWindow4() +
        • ddWindow4() +
        • ddWindowConv4() +
        ] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddWindow.c,v 1.1.1.1 2003/02/24 22:23:53 wjiang Exp $"; +#endif + +#ifdef DD_STATS +extern int ddTotalNumberSwapping; +extern int ddTotalNISwaps; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int ddWindow2 ARGS((DdManager *table, int low, int high)); +static int ddWindowConv2 ARGS((DdManager *table, int low, int high)); +static int ddPermuteWindow3 ARGS((DdManager *table, int x)); +static int ddWindow3 ARGS((DdManager *table, int low, int high)); +static int ddWindowConv3 ARGS((DdManager *table, int low, int high)); +static int ddPermuteWindow4 ARGS((DdManager *table, int w)); +static int ddWindow4 ARGS((DdManager *table, int low, int high)); +static int ddWindowConv4 ARGS((DdManager *table, int low, int high)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Reorders by applying the method of the sliding window.] + + Description [Reorders by applying the method of the sliding window. + Tries all possible permutations to the variables in a window that + slides from low to high. The size of the window is determined by + submethod. Assumes that no dead nodes are present. Returns 1 in + case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +int +cuddWindowReorder( + DdManager * table /* DD table */, + int low /* lowest index to reorder */, + int high /* highest index to reorder */, + Cudd_ReorderingType submethod /* window reordering option */) +{ + + int res; +#ifdef DD_DEBUG + int supposedOpt; +#endif + + switch (submethod) { + case CUDD_REORDER_WINDOW2: + res = ddWindow2(table,low,high); + break; + case CUDD_REORDER_WINDOW3: + res = ddWindow3(table,low,high); + break; + case CUDD_REORDER_WINDOW4: + res = ddWindow4(table,low,high); + break; + case CUDD_REORDER_WINDOW2_CONV: + res = ddWindowConv2(table,low,high); + break; + case CUDD_REORDER_WINDOW3_CONV: + res = ddWindowConv3(table,low,high); +#ifdef DD_DEBUG + supposedOpt = table->keys - table->isolated; + res = ddWindow3(table,low,high); + if (table->keys - table->isolated != (unsigned) supposedOpt) { + (void) fprintf(table->err, "Convergence failed! (%d != %d)\n", + table->keys - table->isolated, supposedOpt); + } +#endif + break; + case CUDD_REORDER_WINDOW4_CONV: + res = ddWindowConv4(table,low,high); +#ifdef DD_DEBUG + supposedOpt = table->keys - table->isolated; + res = ddWindow4(table,low,high); + if (table->keys - table->isolated != (unsigned) supposedOpt) { + (void) fprintf(table->err,"Convergence failed! (%d != %d)\n", + table->keys - table->isolated, supposedOpt); + } +#endif + break; + default: return(0); + } + + return(res); + +} /* end of cuddWindowReorder */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Reorders by applying a sliding window of width 2.] + + Description [Reorders by applying a sliding window of width 2. + Tries both permutations of the variables in a window + that slides from low to high. Assumes that no dead nodes are + present. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddWindow2( + DdManager * table, + int low, + int high) +{ + + int x; + int res; + int size; + +#ifdef DD_DEBUG + assert(low >= 0 && high < table->size); +#endif + + if (high-low < 1) return(0); + + res = table->keys - table->isolated; + for (x = low; x < high; x++) { + size = res; + res = cuddSwapInPlace(table,x,x+1); + if (res == 0) return(0); + if (res >= size) { /* no improvement: undo permutation */ + res = cuddSwapInPlace(table,x,x+1); + if (res == 0) return(0); + } +#ifdef DD_STATS + if (res < size) { + (void) fprintf(table->out,"-"); + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + + return(1); + +} /* end of ddWindow2 */ + + +/**Function******************************************************************** + + Synopsis [Reorders by repeatedly applying a sliding window of width 2.] + + Description [Reorders by repeatedly applying a sliding window of width + 2. Tries both permutations of the variables in a window + that slides from low to high. Assumes that no dead nodes are + present. Uses an event-driven approach to determine convergence. + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddWindowConv2( + DdManager * table, + int low, + int high) +{ + int x; + int res; + int nwin; + int newevent; + int *events; + int size; + +#ifdef DD_DEBUG + assert(low >= 0 && high < table->size); +#endif + + if (high-low < 1) return(ddWindowConv2(table,low,high)); + + nwin = high-low; + events = ALLOC(int,nwin); + if (events == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (x=0; xkeys - table->isolated; + do { + newevent = 0; + for (x=0; x= size) { /* no improvement: undo permutation */ + res = cuddSwapInPlace(table,x+low,x+low+1); + if (res == 0) { + FREE(events); + return(0); + } + } + if (res < size) { + if (x < nwin-1) events[x+1] = 1; + if (x > 0) events[x-1] = 1; + newevent = 1; + } + events[x] = 0; +#ifdef DD_STATS + if (res < size) { + (void) fprintf(table->out,"-"); + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + } +#ifdef DD_STATS + if (newevent) { + (void) fprintf(table->out,"|"); + fflush(table->out); + } +#endif + } while (newevent); + + FREE(events); + + return(1); + +} /* end of ddWindowConv3 */ + + +/**Function******************************************************************** + + Synopsis [Tries all the permutations of the three variables between + x and x+2 and retains the best.] + + Description [Tries all the permutations of the three variables between + x and x+2 and retains the best. Assumes that no dead nodes are + present. Returns the index of the best permutation (1-6) in case of + success; 0 otherwise.Assumes that no dead nodes are present. Returns + the index of the best permutation (1-6) in case of success; 0 + otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddPermuteWindow3( + DdManager * table, + int x) +{ + int y,z; + int size,sizeNew; + int best; + +#ifdef DD_DEBUG + assert(table->dead == 0); + assert(x+2 < table->size); +#endif + + size = table->keys - table->isolated; + y = x+1; z = y+1; + + /* The permutation pattern is: + ** (x,y)(y,z) + ** repeated three times to get all 3! = 6 permutations. + */ +#define ABC 1 + best = ABC; + +#define BAC 2 + sizeNew = cuddSwapInPlace(table,x,y); + if (sizeNew < size) { + if (sizeNew == 0) return(0); + best = BAC; + size = sizeNew; + } +#define BCA 3 + sizeNew = cuddSwapInPlace(table,y,z); + if (sizeNew < size) { + if (sizeNew == 0) return(0); + best = BCA; + size = sizeNew; + } +#define CBA 4 + sizeNew = cuddSwapInPlace(table,x,y); + if (sizeNew < size) { + if (sizeNew == 0) return(0); + best = CBA; + size = sizeNew; + } +#define CAB 5 + sizeNew = cuddSwapInPlace(table,y,z); + if (sizeNew < size) { + if (sizeNew == 0) return(0); + best = CAB; + size = sizeNew; + } +#define ACB 6 + sizeNew = cuddSwapInPlace(table,x,y); + if (sizeNew < size) { + if (sizeNew == 0) return(0); + best = ACB; + size = sizeNew; + } + + /* Now take the shortest route to the best permuytation. + ** The initial permutation is ACB. + */ + switch(best) { + case BCA: if (!cuddSwapInPlace(table,y,z)) return(0); + case CBA: if (!cuddSwapInPlace(table,x,y)) return(0); + case ABC: if (!cuddSwapInPlace(table,y,z)) return(0); + case ACB: break; + case BAC: if (!cuddSwapInPlace(table,y,z)) return(0); + case CAB: if (!cuddSwapInPlace(table,x,y)) return(0); + break; + default: return(0); + } + +#ifdef DD_DEBUG + assert(table->keys - table->isolated == (unsigned) size); +#endif + + return(best); + +} /* end of ddPermuteWindow3 */ + + +/**Function******************************************************************** + + Synopsis [Reorders by applying a sliding window of width 3.] + + Description [Reorders by applying a sliding window of width 3. + Tries all possible permutations to the variables in a + window that slides from low to high. Assumes that no dead nodes are + present. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddWindow3( + DdManager * table, + int low, + int high) +{ + + int x; + int res; + +#ifdef DD_DEBUG + assert(low >= 0 && high < table->size); +#endif + + if (high-low < 2) return(ddWindow2(table,low,high)); + + for (x = low; x+1 < high; x++) { + res = ddPermuteWindow3(table,x); + if (res == 0) return(0); +#ifdef DD_STATS + if (res == ABC) { + (void) fprintf(table->out,"="); + } else { + (void) fprintf(table->out,"-"); + } + fflush(table->out); +#endif + } + + return(1); + +} /* end of ddWindow3 */ + + +/**Function******************************************************************** + + Synopsis [Reorders by repeatedly applying a sliding window of width 3.] + + Description [Reorders by repeatedly applying a sliding window of width + 3. Tries all possible permutations to the variables in a + window that slides from low to high. Assumes that no dead nodes are + present. Uses an event-driven approach to determine convergence. + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddWindowConv3( + DdManager * table, + int low, + int high) +{ + int x; + int res; + int nwin; + int newevent; + int *events; + +#ifdef DD_DEBUG + assert(low >= 0 && high < table->size); +#endif + + if (high-low < 2) return(ddWindowConv2(table,low,high)); + + nwin = high-low-1; + events = ALLOC(int,nwin); + if (events == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (x=0; x 1) events[x-2] = 1; + newevent = 1; + break; + case BCA: + case CBA: + case CAB: + if (x < nwin-2) events[x+2] = 1; + if (x < nwin-1) events[x+1] = 1; + if (x > 0) events[x-1] = 1; + if (x > 1) events[x-2] = 1; + newevent = 1; + break; + case ACB: + if (x < nwin-2) events[x+2] = 1; + if (x > 0) events[x-1] = 1; + newevent = 1; + break; + default: + FREE(events); + return(0); + } + events[x] = 0; +#ifdef DD_STATS + if (res == ABC) { + (void) fprintf(table->out,"="); + } else { + (void) fprintf(table->out,"-"); + } + fflush(table->out); +#endif + } + } +#ifdef DD_STATS + if (newevent) { + (void) fprintf(table->out,"|"); + fflush(table->out); + } +#endif + } while (newevent); + + FREE(events); + + return(1); + +} /* end of ddWindowConv3 */ + + +/**Function******************************************************************** + + Synopsis [Tries all the permutations of the four variables between w + and w+3 and retains the best.] + + Description [Tries all the permutations of the four variables between + w and w+3 and retains the best. Assumes that no dead nodes are + present. Returns the index of the best permutation (1-24) in case of + success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddPermuteWindow4( + DdManager * table, + int w) +{ + int x,y,z; + int size,sizeNew; + int best; + +#ifdef DD_DEBUG + assert(table->dead == 0); + assert(w+3 < table->size); +#endif + + size = table->keys - table->isolated; + x = w+1; y = x+1; z = y+1; + + /* The permutation pattern is: + * (w,x)(y,z)(w,x)(x,y) + * (y,z)(w,x)(y,z)(x,y) + * repeated three times to get all 4! = 24 permutations. + * This gives a hamiltonian circuit of Cayley's graph. + * The codes to the permutation are assigned in topological order. + * The permutations at lower distance from the final permutation are + * assigned lower codes. This way we can choose, between + * permutations that give the same size, one that requires the minimum + * number of swaps from the final permutation of the hamiltonian circuit. + * There is an exception to this rule: ABCD is given Code 1, to + * avoid oscillation when convergence is sought. + */ +#define ABCD 1 + best = ABCD; + +#define BACD 7 + sizeNew = cuddSwapInPlace(table,w,x); + if (sizeNew < size) { + if (sizeNew == 0) return(0); + best = BACD; + size = sizeNew; + } +#define BADC 13 + sizeNew = cuddSwapInPlace(table,y,z); + if (sizeNew < size) { + if (sizeNew == 0) return(0); + best = BADC; + size = sizeNew; + } +#define ABDC 8 + sizeNew = cuddSwapInPlace(table,w,x); + if (sizeNew < size || (sizeNew == size && ABDC < best)) { + if (sizeNew == 0) return(0); + best = ABDC; + size = sizeNew; + } +#define ADBC 14 + sizeNew = cuddSwapInPlace(table,x,y); + if (sizeNew < size) { + if (sizeNew == 0) return(0); + best = ADBC; + size = sizeNew; + } +#define ADCB 9 + sizeNew = cuddSwapInPlace(table,y,z); + if (sizeNew < size || (sizeNew == size && ADCB < best)) { + if (sizeNew == 0) return(0); + best = ADCB; + size = sizeNew; + } +#define DACB 15 + sizeNew = cuddSwapInPlace(table,w,x); + if (sizeNew < size) { + if (sizeNew == 0) return(0); + best = DACB; + size = sizeNew; + } +#define DABC 20 + sizeNew = cuddSwapInPlace(table,y,z); + if (sizeNew < size) { + if (sizeNew == 0) return(0); + best = DABC; + size = sizeNew; + } +#define DBAC 23 + sizeNew = cuddSwapInPlace(table,x,y); + if (sizeNew < size) { + if (sizeNew == 0) return(0); + best = DBAC; + size = sizeNew; + } +#define BDAC 19 + sizeNew = cuddSwapInPlace(table,w,x); + if (sizeNew < size || (sizeNew == size && BDAC < best)) { + if (sizeNew == 0) return(0); + best = BDAC; + size = sizeNew; + } +#define BDCA 21 + sizeNew = cuddSwapInPlace(table,y,z); + if (sizeNew < size || (sizeNew == size && BDCA < best)) { + if (sizeNew == 0) return(0); + best = BDCA; + size = sizeNew; + } +#define DBCA 24 + sizeNew = cuddSwapInPlace(table,w,x); + if (sizeNew < size) { + if (sizeNew == 0) return(0); + best = DBCA; + size = sizeNew; + } +#define DCBA 22 + sizeNew = cuddSwapInPlace(table,x,y); + if (sizeNew < size || (sizeNew == size && DCBA < best)) { + if (sizeNew == 0) return(0); + best = DCBA; + size = sizeNew; + } +#define DCAB 18 + sizeNew = cuddSwapInPlace(table,y,z); + if (sizeNew < size || (sizeNew == size && DCAB < best)) { + if (sizeNew == 0) return(0); + best = DCAB; + size = sizeNew; + } +#define CDAB 12 + sizeNew = cuddSwapInPlace(table,w,x); + if (sizeNew < size || (sizeNew == size && CDAB < best)) { + if (sizeNew == 0) return(0); + best = CDAB; + size = sizeNew; + } +#define CDBA 17 + sizeNew = cuddSwapInPlace(table,y,z); + if (sizeNew < size || (sizeNew == size && CDBA < best)) { + if (sizeNew == 0) return(0); + best = CDBA; + size = sizeNew; + } +#define CBDA 11 + sizeNew = cuddSwapInPlace(table,x,y); + if (sizeNew < size || (sizeNew == size && CBDA < best)) { + if (sizeNew == 0) return(0); + best = CBDA; + size = sizeNew; + } +#define BCDA 16 + sizeNew = cuddSwapInPlace(table,w,x); + if (sizeNew < size || (sizeNew == size && BCDA < best)) { + if (sizeNew == 0) return(0); + best = BCDA; + size = sizeNew; + } +#define BCAD 10 + sizeNew = cuddSwapInPlace(table,y,z); + if (sizeNew < size || (sizeNew == size && BCAD < best)) { + if (sizeNew == 0) return(0); + best = BCAD; + size = sizeNew; + } +#define CBAD 5 + sizeNew = cuddSwapInPlace(table,w,x); + if (sizeNew < size || (sizeNew == size && CBAD < best)) { + if (sizeNew == 0) return(0); + best = CBAD; + size = sizeNew; + } +#define CABD 3 + sizeNew = cuddSwapInPlace(table,x,y); + if (sizeNew < size || (sizeNew == size && CABD < best)) { + if (sizeNew == 0) return(0); + best = CABD; + size = sizeNew; + } +#define CADB 6 + sizeNew = cuddSwapInPlace(table,y,z); + if (sizeNew < size || (sizeNew == size && CADB < best)) { + if (sizeNew == 0) return(0); + best = CADB; + size = sizeNew; + } +#define ACDB 4 + sizeNew = cuddSwapInPlace(table,w,x); + if (sizeNew < size || (sizeNew == size && ACDB < best)) { + if (sizeNew == 0) return(0); + best = ACDB; + size = sizeNew; + } +#define ACBD 2 + sizeNew = cuddSwapInPlace(table,y,z); + if (sizeNew < size || (sizeNew == size && ACBD < best)) { + if (sizeNew == 0) return(0); + best = ACBD; + size = sizeNew; + } + + /* Now take the shortest route to the best permutation. + ** The initial permutation is ACBD. + */ + switch(best) { + case DBCA: if (!cuddSwapInPlace(table,y,z)) return(0); + case BDCA: if (!cuddSwapInPlace(table,x,y)) return(0); + case CDBA: if (!cuddSwapInPlace(table,w,x)) return(0); + case ADBC: if (!cuddSwapInPlace(table,y,z)) return(0); + case ABDC: if (!cuddSwapInPlace(table,x,y)) return(0); + case ACDB: if (!cuddSwapInPlace(table,y,z)) return(0); + case ACBD: break; + case DCBA: if (!cuddSwapInPlace(table,y,z)) return(0); + case BCDA: if (!cuddSwapInPlace(table,x,y)) return(0); + case CBDA: if (!cuddSwapInPlace(table,w,x)) return(0); + if (!cuddSwapInPlace(table,x,y)) return(0); + if (!cuddSwapInPlace(table,y,z)) return(0); + break; + case DBAC: if (!cuddSwapInPlace(table,x,y)) return(0); + case DCAB: if (!cuddSwapInPlace(table,w,x)) return(0); + case DACB: if (!cuddSwapInPlace(table,y,z)) return(0); + case BACD: if (!cuddSwapInPlace(table,x,y)) return(0); + case CABD: if (!cuddSwapInPlace(table,w,x)) return(0); + break; + case DABC: if (!cuddSwapInPlace(table,y,z)) return(0); + case BADC: if (!cuddSwapInPlace(table,x,y)) return(0); + case CADB: if (!cuddSwapInPlace(table,w,x)) return(0); + if (!cuddSwapInPlace(table,y,z)) return(0); + break; + case BDAC: if (!cuddSwapInPlace(table,x,y)) return(0); + case CDAB: if (!cuddSwapInPlace(table,w,x)) return(0); + case ADCB: if (!cuddSwapInPlace(table,y,z)) return(0); + case ABCD: if (!cuddSwapInPlace(table,x,y)) return(0); + break; + case BCAD: if (!cuddSwapInPlace(table,x,y)) return(0); + case CBAD: if (!cuddSwapInPlace(table,w,x)) return(0); + if (!cuddSwapInPlace(table,x,y)) return(0); + break; + default: return(0); + } + +#ifdef DD_DEBUG + assert(table->keys - table->isolated == (unsigned) size); +#endif + + return(best); + +} /* end of ddPermuteWindow4 */ + + +/**Function******************************************************************** + + Synopsis [Reorders by applying a sliding window of width 4.] + + Description [Reorders by applying a sliding window of width 4. + Tries all possible permutations to the variables in a + window that slides from low to high. Assumes that no dead nodes are + present. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddWindow4( + DdManager * table, + int low, + int high) +{ + + int w; + int res; + +#ifdef DD_DEBUG + assert(low >= 0 && high < table->size); +#endif + + if (high-low < 3) return(ddWindow3(table,low,high)); + + for (w = low; w+2 < high; w++) { + res = ddPermuteWindow4(table,w); + if (res == 0) return(0); +#ifdef DD_STATS + if (res == ABCD) { + (void) fprintf(table->out,"="); + } else { + (void) fprintf(table->out,"-"); + } + fflush(table->out); +#endif + } + + return(1); + +} /* end of ddWindow4 */ + + +/**Function******************************************************************** + + Synopsis [Reorders by repeatedly applying a sliding window of width 4.] + + Description [Reorders by repeatedly applying a sliding window of width + 4. Tries all possible permutations to the variables in a + window that slides from low to high. Assumes that no dead nodes are + present. Uses an event-driven approach to determine convergence. + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddWindowConv4( + DdManager * table, + int low, + int high) +{ + int x; + int res; + int nwin; + int newevent; + int *events; + +#ifdef DD_DEBUG + assert(low >= 0 && high < table->size); +#endif + + if (high-low < 3) return(ddWindowConv3(table,low,high)); + + nwin = high-low-2; + events = ALLOC(int,nwin); + if (events == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (x=0; x 2) events[x-3] = 1; + newevent = 1; + break; + case BADC: + if (x < nwin-3) events[x+3] = 1; + if (x < nwin-1) events[x+1] = 1; + if (x > 0) events[x-1] = 1; + if (x > 2) events[x-3] = 1; + newevent = 1; + break; + case ABDC: + if (x < nwin-3) events[x+3] = 1; + if (x > 0) events[x-1] = 1; + newevent = 1; + break; + case ADBC: + case ADCB: + case ACDB: + if (x < nwin-3) events[x+3] = 1; + if (x < nwin-2) events[x+2] = 1; + if (x > 0) events[x-1] = 1; + if (x > 1) events[x-2] = 1; + newevent = 1; + break; + case DACB: + case DABC: + case DBAC: + case BDAC: + case BDCA: + case DBCA: + case DCBA: + case DCAB: + case CDAB: + case CDBA: + case CBDA: + case BCDA: + case CADB: + if (x < nwin-3) events[x+3] = 1; + if (x < nwin-2) events[x+2] = 1; + if (x < nwin-1) events[x+1] = 1; + if (x > 0) events[x-1] = 1; + if (x > 1) events[x-2] = 1; + if (x > 2) events[x-3] = 1; + newevent = 1; + break; + case BCAD: + case CBAD: + case CABD: + if (x < nwin-2) events[x+2] = 1; + if (x < nwin-1) events[x+1] = 1; + if (x > 1) events[x-2] = 1; + if (x > 2) events[x-3] = 1; + newevent = 1; + break; + case ACBD: + if (x < nwin-2) events[x+2] = 1; + if (x > 1) events[x-2] = 1; + newevent = 1; + break; + default: + FREE(events); + return(0); + } + events[x] = 0; +#ifdef DD_STATS + if (res == ABCD) { + (void) fprintf(table->out,"="); + } else { + (void) fprintf(table->out,"-"); + } + fflush(table->out); +#endif + } + } +#ifdef DD_STATS + if (newevent) { + (void) fprintf(table->out,"|"); + fflush(table->out); + } +#endif + } while (newevent); + + FREE(events); + + return(1); + +} /* end of ddWindowConv4 */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddZddCount.c b/abc_with_bb_support/src/bdd/cudd/cuddZddCount.c new file mode 100644 index 000000000..fd489ef3a --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddZddCount.c @@ -0,0 +1,324 @@ +/**CFile*********************************************************************** + + FileName [cuddZddCount.c] + + PackageName [cudd] + + Synopsis [Procedures to count the number of minterms of a ZDD.] + + Description [External procedures included in this module: +
          +
        • Cudd_zddCount(); +
        • Cudd_zddCountDouble(); +
        + Internal procedures included in this module: +
          +
        + Static procedures included in this module: +
          +
        • cuddZddCountStep(); +
        • cuddZddCountDoubleStep(); +
        • st_zdd_count_dbl_free() +
        • st_zdd_countfree() +
        + ] + + SeeAlso [] + + Author [Hyong-Kyoon Shin, In-Ho Moon] + + Copyright [ This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddZddCount.c,v 1.1.1.1 2003/02/24 22:23:53 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int cuddZddCountStep ARGS((DdNode *P, st_table *table, DdNode *base, DdNode *empty)); +static double cuddZddCountDoubleStep ARGS((DdNode *P, st_table *table, DdNode *base, DdNode *empty)); +static enum st_retval st_zdd_countfree ARGS((char *key, char *value, char *arg)); +static enum st_retval st_zdd_count_dbl_free ARGS((char *key, char *value, char *arg)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Counts the number of minterms in a ZDD.] + + Description [Returns an integer representing the number of minterms + in a ZDD.] + + SideEffects [None] + + SeeAlso [Cudd_zddCountDouble] + +******************************************************************************/ +int +Cudd_zddCount( + DdManager * zdd, + DdNode * P) +{ + st_table *table; + int res; + DdNode *base, *empty; + + base = DD_ONE(zdd); + empty = DD_ZERO(zdd); + table = st_init_table(st_ptrcmp, st_ptrhash); + if (table == NULL) return(CUDD_OUT_OF_MEM); + res = cuddZddCountStep(P, table, base, empty); + if (res == CUDD_OUT_OF_MEM) { + zdd->errorCode = CUDD_MEMORY_OUT; + } + st_foreach(table, st_zdd_countfree, NIL(char)); + st_free_table(table); + + return(res); + +} /* end of Cudd_zddCount */ + + +/**Function******************************************************************** + + Synopsis [Counts the number of minterms of a ZDD.] + + Description [Counts the number of minterms of a ZDD. The result is + returned as a double. If the procedure runs out of memory, it + returns (double) CUDD_OUT_OF_MEM. This procedure is used in + Cudd_zddCountMinterm.] + + SideEffects [None] + + SeeAlso [Cudd_zddCountMinterm Cudd_zddCount] + +******************************************************************************/ +double +Cudd_zddCountDouble( + DdManager * zdd, + DdNode * P) +{ + st_table *table; + double res; + DdNode *base, *empty; + + base = DD_ONE(zdd); + empty = DD_ZERO(zdd); + table = st_init_table(st_ptrcmp, st_ptrhash); + if (table == NULL) return((double)CUDD_OUT_OF_MEM); + res = cuddZddCountDoubleStep(P, table, base, empty); + if (res == (double)CUDD_OUT_OF_MEM) { + zdd->errorCode = CUDD_MEMORY_OUT; + } + st_foreach(table, st_zdd_count_dbl_free, NIL(char)); + st_free_table(table); + + return(res); + +} /* end of Cudd_zddCountDouble */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddCount.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddZddCountStep( + DdNode * P, + st_table * table, + DdNode * base, + DdNode * empty) +{ + int res; + int *dummy; + + if (P == empty) + return(0); + if (P == base) + return(1); + + /* Check cache. */ + if (st_lookup(table, (char *)P, (char **)(&dummy))) { + res = *dummy; + return(res); + } + + res = cuddZddCountStep(cuddE(P), table, base, empty) + + cuddZddCountStep(cuddT(P), table, base, empty); + + dummy = ALLOC(int, 1); + if (dummy == NULL) { + return(CUDD_OUT_OF_MEM); + } + *dummy = res; + if (st_insert(table, (char *)P, (char *)dummy) == ST_OUT_OF_MEM) { + FREE(dummy); + return(CUDD_OUT_OF_MEM); + } + + return(res); + +} /* end of cuddZddCountStep */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddCountDouble.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static double +cuddZddCountDoubleStep( + DdNode * P, + st_table * table, + DdNode * base, + DdNode * empty) +{ + double res; + double *dummy; + + if (P == empty) + return((double)0.0); + if (P == base) + return((double)1.0); + + /* Check cache */ + if (st_lookup(table, (char *)P, (char **)(&dummy))) { + res = *dummy; + return(res); + } + + res = cuddZddCountDoubleStep(cuddE(P), table, base, empty) + + cuddZddCountDoubleStep(cuddT(P), table, base, empty); + + dummy = ALLOC(double, 1); + if (dummy == NULL) { + return((double)CUDD_OUT_OF_MEM); + } + *dummy = res; + if (st_insert(table, (char *)P, (char *)dummy) == ST_OUT_OF_MEM) { + FREE(dummy); + return((double)CUDD_OUT_OF_MEM); + } + + return(res); + +} /* end of cuddZddCountDoubleStep */ + + +/**Function******************************************************************** + + Synopsis [Frees the memory associated with the computed table of + Cudd_zddCount.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static enum st_retval +st_zdd_countfree( + char * key, + char * value, + char * arg) +{ + int *d; + + d = (int *)value; + FREE(d); + return(ST_CONTINUE); + +} /* end of st_zdd_countfree */ + + +/**Function******************************************************************** + + Synopsis [Frees the memory associated with the computed table of + Cudd_zddCountDouble.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static enum st_retval +st_zdd_count_dbl_free( + char * key, + char * value, + char * arg) +{ + double *d; + + d = (double *)value; + FREE(d); + return(ST_CONTINUE); + +} /* end of st_zdd_count_dbl_free */ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddZddFuncs.c b/abc_with_bb_support/src/bdd/cudd/cuddZddFuncs.c new file mode 100644 index 000000000..0ae488068 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddZddFuncs.c @@ -0,0 +1,1603 @@ +/**CFile*********************************************************************** + + FileName [cuddZddFuncs.c] + + PackageName [cudd] + + Synopsis [Functions to manipulate covers represented as ZDDs.] + + Description [External procedures included in this module: +
          +
        • Cudd_zddProduct(); +
        • Cudd_zddUnateProduct(); +
        • Cudd_zddWeakDiv(); +
        • Cudd_zddWeakDivF(); +
        • Cudd_zddDivide(); +
        • Cudd_zddDivideF(); +
        • Cudd_zddComplement(); +
        + Internal procedures included in this module: +
          +
        • cuddZddProduct(); +
        • cuddZddUnateProduct(); +
        • cuddZddWeakDiv(); +
        • cuddZddWeakDivF(); +
        • cuddZddDivide(); +
        • cuddZddDivideF(); +
        • cuddZddGetCofactors3() +
        • cuddZddGetCofactors2() +
        • cuddZddComplement(); +
        • cuddZddGetPosVarIndex(); +
        • cuddZddGetNegVarIndex(); +
        • cuddZddGetPosVarLevel(); +
        • cuddZddGetNegVarLevel(); +
        + Static procedures included in this module: +
          +
        + ] + + SeeAlso [] + + Author [In-Ho Moon] + + Copyright [ This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddZddFuncs.c,v 1.1.1.1 2003/02/24 22:23:53 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Computes the product of two covers represented by ZDDs.] + + Description [Computes the product of two covers represented by + ZDDs. The result is also a ZDD. Returns a pointer to the result if + successful; NULL otherwise. The covers on which Cudd_zddProduct + operates use two ZDD variables for each function variable (one ZDD + variable for each literal of the variable). Those two ZDD variables + should be adjacent in the order.] + + SideEffects [None] + + SeeAlso [Cudd_zddUnateProduct] + +******************************************************************************/ +DdNode * +Cudd_zddProduct( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddZddProduct(dd, f, g); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_zddProduct */ + + +/**Function******************************************************************** + + Synopsis [Computes the product of two unate covers.] + + Description [Computes the product of two unate covers represented as + ZDDs. Unate covers use one ZDD variable for each BDD + variable. Returns a pointer to the result if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_zddProduct] + +******************************************************************************/ +DdNode * +Cudd_zddUnateProduct( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddZddUnateProduct(dd, f, g); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_zddUnateProduct */ + + +/**Function******************************************************************** + + Synopsis [Applies weak division to two covers.] + + Description [Applies weak division to two ZDDs representing two + covers. Returns a pointer to the ZDD representing the result if + successful; NULL otherwise. The result of weak division depends on + the variable order. The covers on which Cudd_zddWeakDiv operates use + two ZDD variables for each function variable (one ZDD variable for + each literal of the variable). Those two ZDD variables should be + adjacent in the order.] + + SideEffects [None] + + SeeAlso [Cudd_zddDivide] + +******************************************************************************/ +DdNode * +Cudd_zddWeakDiv( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddZddWeakDiv(dd, f, g); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_zddWeakDiv */ + + +/**Function******************************************************************** + + Synopsis [Computes the quotient of two unate covers.] + + Description [Computes the quotient of two unate covers represented + by ZDDs. Unate covers use one ZDD variable for each BDD + variable. Returns a pointer to the resulting ZDD if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_zddWeakDiv] + +******************************************************************************/ +DdNode * +Cudd_zddDivide( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddZddDivide(dd, f, g); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_zddDivide */ + + +/**Function******************************************************************** + + Synopsis [Modified version of Cudd_zddWeakDiv.] + + Description [Modified version of Cudd_zddWeakDiv. This function may + disappear in future releases.] + + SideEffects [None] + + SeeAlso [Cudd_zddWeakDiv] + +******************************************************************************/ +DdNode * +Cudd_zddWeakDivF( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddZddWeakDivF(dd, f, g); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_zddWeakDivF */ + + +/**Function******************************************************************** + + Synopsis [Modified version of Cudd_zddDivide.] + + Description [Modified version of Cudd_zddDivide. This function may + disappear in future releases.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +Cudd_zddDivideF( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddZddDivideF(dd, f, g); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_zddDivideF */ + + +/**Function******************************************************************** + + Synopsis [Computes a complement cover for a ZDD node.] + + Description [Computes a complement cover for a ZDD node. For lack of a + better method, we first extract the function BDD from the ZDD cover, + then make the complement of the ZDD cover from the complement of the + BDD node by using ISOP. Returns a pointer to the resulting cover if + successful; NULL otherwise. The result depends on current variable + order.] + + SideEffects [The result depends on current variable order.] + + SeeAlso [] + +******************************************************************************/ +DdNode * +Cudd_zddComplement( + DdManager *dd, + DdNode *node) +{ + DdNode *b, *isop, *zdd_I; + + /* Check cache */ + zdd_I = cuddCacheLookup1Zdd(dd, cuddZddComplement, node); + if (zdd_I) + return(zdd_I); + + b = Cudd_MakeBddFromZddCover(dd, node); + if (!b) + return(NULL); + Cudd_Ref(b); + isop = Cudd_zddIsop(dd, Cudd_Not(b), Cudd_Not(b), &zdd_I); + if (!isop) { + Cudd_RecursiveDeref(dd, b); + return(NULL); + } + Cudd_Ref(isop); + Cudd_Ref(zdd_I); + Cudd_RecursiveDeref(dd, b); + Cudd_RecursiveDeref(dd, isop); + + cuddCacheInsert1(dd, cuddZddComplement, node, zdd_I); + Cudd_Deref(zdd_I); + return(zdd_I); +} /* end of Cudd_zddComplement */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddProduct.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_zddProduct] + +******************************************************************************/ +DdNode * +cuddZddProduct( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + int v, top_f, top_g; + DdNode *tmp, *term1, *term2, *term3; + DdNode *f0, *f1, *fd, *g0, *g1, *gd; + DdNode *R0, *R1, *Rd, *N0, *N1; + DdNode *r; + DdNode *one = DD_ONE(dd); + DdNode *zero = DD_ZERO(dd); + int flag; + int pv, nv; + + statLine(dd); + if (f == zero || g == zero) + return(zero); + if (f == one) + return(g); + if (g == one) + return(f); + + top_f = dd->permZ[f->index]; + top_g = dd->permZ[g->index]; + + if (top_f > top_g) + return(cuddZddProduct(dd, g, f)); + + /* Check cache */ + r = cuddCacheLookup2Zdd(dd, cuddZddProduct, f, g); + if (r) + return(r); + + v = f->index; /* either yi or zi */ + flag = cuddZddGetCofactors3(dd, f, v, &f1, &f0, &fd); + if (flag == 1) + return(NULL); + Cudd_Ref(f1); + Cudd_Ref(f0); + Cudd_Ref(fd); + flag = cuddZddGetCofactors3(dd, g, v, &g1, &g0, &gd); + if (flag == 1) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + return(NULL); + } + Cudd_Ref(g1); + Cudd_Ref(g0); + Cudd_Ref(gd); + pv = cuddZddGetPosVarIndex(dd, v); + nv = cuddZddGetNegVarIndex(dd, v); + + Rd = cuddZddProduct(dd, fd, gd); + if (Rd == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, gd); + return(NULL); + } + Cudd_Ref(Rd); + + term1 = cuddZddProduct(dd, f0, g0); + if (term1 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, gd); + Cudd_RecursiveDerefZdd(dd, Rd); + return(NULL); + } + Cudd_Ref(term1); + term2 = cuddZddProduct(dd, f0, gd); + if (term2 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, gd); + Cudd_RecursiveDerefZdd(dd, Rd); + Cudd_RecursiveDerefZdd(dd, term1); + return(NULL); + } + Cudd_Ref(term2); + term3 = cuddZddProduct(dd, fd, g0); + if (term3 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, gd); + Cudd_RecursiveDerefZdd(dd, Rd); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term2); + return(NULL); + } + Cudd_Ref(term3); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g0); + tmp = cuddZddUnion(dd, term1, term2); + if (tmp == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, gd); + Cudd_RecursiveDerefZdd(dd, Rd); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term2); + Cudd_RecursiveDerefZdd(dd, term3); + return(NULL); + } + Cudd_Ref(tmp); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term2); + R0 = cuddZddUnion(dd, tmp, term3); + if (R0 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, gd); + Cudd_RecursiveDerefZdd(dd, Rd); + Cudd_RecursiveDerefZdd(dd, term3); + Cudd_RecursiveDerefZdd(dd, tmp); + return(NULL); + } + Cudd_Ref(R0); + Cudd_RecursiveDerefZdd(dd, tmp); + Cudd_RecursiveDerefZdd(dd, term3); + N0 = cuddZddGetNode(dd, nv, R0, Rd); /* nv = zi */ + if (N0 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, gd); + Cudd_RecursiveDerefZdd(dd, Rd); + Cudd_RecursiveDerefZdd(dd, R0); + return(NULL); + } + Cudd_Ref(N0); + Cudd_RecursiveDerefZdd(dd, R0); + Cudd_RecursiveDerefZdd(dd, Rd); + + term1 = cuddZddProduct(dd, f1, g1); + if (term1 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, gd); + Cudd_RecursiveDerefZdd(dd, N0); + return(NULL); + } + Cudd_Ref(term1); + term2 = cuddZddProduct(dd, f1, gd); + if (term2 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, gd); + Cudd_RecursiveDerefZdd(dd, N0); + Cudd_RecursiveDerefZdd(dd, term1); + return(NULL); + } + Cudd_Ref(term2); + term3 = cuddZddProduct(dd, fd, g1); + if (term3 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, gd); + Cudd_RecursiveDerefZdd(dd, N0); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term2); + return(NULL); + } + Cudd_Ref(term3); + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + tmp = cuddZddUnion(dd, term1, term2); + if (tmp == NULL) { + Cudd_RecursiveDerefZdd(dd, N0); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term2); + Cudd_RecursiveDerefZdd(dd, term3); + return(NULL); + } + Cudd_Ref(tmp); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term2); + R1 = cuddZddUnion(dd, tmp, term3); + if (R1 == NULL) { + Cudd_RecursiveDerefZdd(dd, N0); + Cudd_RecursiveDerefZdd(dd, term3); + Cudd_RecursiveDerefZdd(dd, tmp); + return(NULL); + } + Cudd_Ref(R1); + Cudd_RecursiveDerefZdd(dd, tmp); + Cudd_RecursiveDerefZdd(dd, term3); + N1 = cuddZddGetNode(dd, pv, R1, N0); /* pv = yi */ + if (N1 == NULL) { + Cudd_RecursiveDerefZdd(dd, N0); + Cudd_RecursiveDerefZdd(dd, R1); + return(NULL); + } + Cudd_Ref(N1); + Cudd_RecursiveDerefZdd(dd, R1); + Cudd_RecursiveDerefZdd(dd, N0); + + cuddCacheInsert2(dd, cuddZddProduct, f, g, N1); + Cudd_Deref(N1); + return(N1); + +} /* end of cuddZddProduct */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddUnateProduct.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_zddUnateProduct] + +******************************************************************************/ +DdNode * +cuddZddUnateProduct( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + int v, top_f, top_g; + DdNode *term1, *term2, *term3, *term4; + DdNode *sum1, *sum2; + DdNode *f0, *f1, *g0, *g1; + DdNode *r; + DdNode *one = DD_ONE(dd); + DdNode *zero = DD_ZERO(dd); + int flag; + + statLine(dd); + if (f == zero || g == zero) + return(zero); + if (f == one) + return(g); + if (g == one) + return(f); + + top_f = dd->permZ[f->index]; + top_g = dd->permZ[g->index]; + + if (top_f > top_g) + return(cuddZddUnateProduct(dd, g, f)); + + /* Check cache */ + r = cuddCacheLookup2Zdd(dd, cuddZddUnateProduct, f, g); + if (r) + return(r); + + v = f->index; /* either yi or zi */ + flag = cuddZddGetCofactors2(dd, f, v, &f1, &f0); + if (flag == 1) + return(NULL); + Cudd_Ref(f1); + Cudd_Ref(f0); + flag = cuddZddGetCofactors2(dd, g, v, &g1, &g0); + if (flag == 1) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + return(NULL); + } + Cudd_Ref(g1); + Cudd_Ref(g0); + + term1 = cuddZddUnateProduct(dd, f1, g1); + if (term1 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + return(NULL); + } + Cudd_Ref(term1); + term2 = cuddZddUnateProduct(dd, f1, g0); + if (term2 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, term1); + return(NULL); + } + Cudd_Ref(term2); + term3 = cuddZddUnateProduct(dd, f0, g1); + if (term3 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term2); + return(NULL); + } + Cudd_Ref(term3); + term4 = cuddZddUnateProduct(dd, f0, g0); + if (term4 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term2); + Cudd_RecursiveDerefZdd(dd, term3); + return(NULL); + } + Cudd_Ref(term4); + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + sum1 = cuddZddUnion(dd, term1, term2); + if (sum1 == NULL) { + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term2); + Cudd_RecursiveDerefZdd(dd, term3); + Cudd_RecursiveDerefZdd(dd, term4); + return(NULL); + } + Cudd_Ref(sum1); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term2); + sum2 = cuddZddUnion(dd, sum1, term3); + if (sum2 == NULL) { + Cudd_RecursiveDerefZdd(dd, term3); + Cudd_RecursiveDerefZdd(dd, term4); + Cudd_RecursiveDerefZdd(dd, sum1); + return(NULL); + } + Cudd_Ref(sum2); + Cudd_RecursiveDerefZdd(dd, sum1); + Cudd_RecursiveDerefZdd(dd, term3); + r = cuddZddGetNode(dd, v, sum2, term4); + if (r == NULL) { + Cudd_RecursiveDerefZdd(dd, term4); + Cudd_RecursiveDerefZdd(dd, sum2); + return(NULL); + } + Cudd_Ref(r); + Cudd_RecursiveDerefZdd(dd, sum2); + Cudd_RecursiveDerefZdd(dd, term4); + + cuddCacheInsert2(dd, cuddZddUnateProduct, f, g, r); + Cudd_Deref(r); + return(r); + +} /* end of cuddZddUnateProduct */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddWeakDiv.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_zddWeakDiv] + +******************************************************************************/ +DdNode * +cuddZddWeakDiv( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + int v; + DdNode *one = DD_ONE(dd); + DdNode *zero = DD_ZERO(dd); + DdNode *f0, *f1, *fd, *g0, *g1, *gd; + DdNode *q, *tmp; + DdNode *r; + int flag; + + statLine(dd); + if (g == one) + return(f); + if (f == zero || f == one) + return(zero); + if (f == g) + return(one); + + /* Check cache. */ + r = cuddCacheLookup2Zdd(dd, cuddZddWeakDiv, f, g); + if (r) + return(r); + + v = g->index; + + flag = cuddZddGetCofactors3(dd, f, v, &f1, &f0, &fd); + if (flag == 1) + return(NULL); + Cudd_Ref(f1); + Cudd_Ref(f0); + Cudd_Ref(fd); + flag = cuddZddGetCofactors3(dd, g, v, &g1, &g0, &gd); + if (flag == 1) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + return(NULL); + } + Cudd_Ref(g1); + Cudd_Ref(g0); + Cudd_Ref(gd); + + q = g; + + if (g0 != zero) { + q = cuddZddWeakDiv(dd, f0, g0); + if (q == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, gd); + return(NULL); + } + Cudd_Ref(q); + } + else + Cudd_Ref(q); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g0); + + if (q == zero) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + cuddCacheInsert2(dd, cuddZddWeakDiv, f, g, zero); + Cudd_Deref(q); + return(zero); + } + + if (g1 != zero) { + Cudd_RecursiveDerefZdd(dd, q); + tmp = cuddZddWeakDiv(dd, f1, g1); + if (tmp == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + return(NULL); + } + Cudd_Ref(tmp); + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, g1); + if (q == g) + q = tmp; + else { + q = cuddZddIntersect(dd, q, tmp); + if (q == NULL) { + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + return(NULL); + } + Cudd_Ref(q); + Cudd_RecursiveDerefZdd(dd, tmp); + } + } + else { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, g1); + } + + if (q == zero) { + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + cuddCacheInsert2(dd, cuddZddWeakDiv, f, g, zero); + Cudd_Deref(q); + return(zero); + } + + if (gd != zero) { + Cudd_RecursiveDerefZdd(dd, q); + tmp = cuddZddWeakDiv(dd, fd, gd); + if (tmp == NULL) { + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + return(NULL); + } + Cudd_Ref(tmp); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + if (q == g) + q = tmp; + else { + q = cuddZddIntersect(dd, q, tmp); + if (q == NULL) { + Cudd_RecursiveDerefZdd(dd, tmp); + return(NULL); + } + Cudd_Ref(q); + Cudd_RecursiveDerefZdd(dd, tmp); + } + } + else { + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + } + + cuddCacheInsert2(dd, cuddZddWeakDiv, f, g, q); + Cudd_Deref(q); + return(q); + +} /* end of cuddZddWeakDiv */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddWeakDivF.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_zddWeakDivF] + +******************************************************************************/ +DdNode * +cuddZddWeakDivF( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + int v, top_f, top_g, vf, vg; + DdNode *one = DD_ONE(dd); + DdNode *zero = DD_ZERO(dd); + DdNode *f0, *f1, *fd, *g0, *g1, *gd; + DdNode *q, *tmp; + DdNode *r; + DdNode *term1, *term0, *termd; + int flag; + int pv, nv; + + statLine(dd); + if (g == one) + return(f); + if (f == zero || f == one) + return(zero); + if (f == g) + return(one); + + /* Check cache. */ + r = cuddCacheLookup2Zdd(dd, cuddZddWeakDivF, f, g); + if (r) + return(r); + + top_f = dd->permZ[f->index]; + top_g = dd->permZ[g->index]; + vf = top_f >> 1; + vg = top_g >> 1; + v = ddMin(top_f, top_g); + + if (v == top_f && vf < vg) { + v = f->index; + flag = cuddZddGetCofactors3(dd, f, v, &f1, &f0, &fd); + if (flag == 1) + return(NULL); + Cudd_Ref(f1); + Cudd_Ref(f0); + Cudd_Ref(fd); + + pv = cuddZddGetPosVarIndex(dd, v); + nv = cuddZddGetNegVarIndex(dd, v); + + term1 = cuddZddWeakDivF(dd, f1, g); + if (term1 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + return(NULL); + } + Cudd_Ref(term1); + Cudd_RecursiveDerefZdd(dd, f1); + term0 = cuddZddWeakDivF(dd, f0, g); + if (term0 == NULL) { + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, term1); + return(NULL); + } + Cudd_Ref(term0); + Cudd_RecursiveDerefZdd(dd, f0); + termd = cuddZddWeakDivF(dd, fd, g); + if (termd == NULL) { + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term0); + return(NULL); + } + Cudd_Ref(termd); + Cudd_RecursiveDerefZdd(dd, fd); + + tmp = cuddZddGetNode(dd, nv, term0, termd); /* nv = zi */ + if (tmp == NULL) { + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term0); + Cudd_RecursiveDerefZdd(dd, termd); + return(NULL); + } + Cudd_Ref(tmp); + Cudd_RecursiveDerefZdd(dd, term0); + Cudd_RecursiveDerefZdd(dd, termd); + q = cuddZddGetNode(dd, pv, term1, tmp); /* pv = yi */ + if (q == NULL) { + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, tmp); + return(NULL); + } + Cudd_Ref(q); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, tmp); + + cuddCacheInsert2(dd, cuddZddWeakDivF, f, g, q); + Cudd_Deref(q); + return(q); + } + + if (v == top_f) + v = f->index; + else + v = g->index; + + flag = cuddZddGetCofactors3(dd, f, v, &f1, &f0, &fd); + if (flag == 1) + return(NULL); + Cudd_Ref(f1); + Cudd_Ref(f0); + Cudd_Ref(fd); + flag = cuddZddGetCofactors3(dd, g, v, &g1, &g0, &gd); + if (flag == 1) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + return(NULL); + } + Cudd_Ref(g1); + Cudd_Ref(g0); + Cudd_Ref(gd); + + q = g; + + if (g0 != zero) { + q = cuddZddWeakDivF(dd, f0, g0); + if (q == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, gd); + return(NULL); + } + Cudd_Ref(q); + } + else + Cudd_Ref(q); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g0); + + if (q == zero) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + cuddCacheInsert2(dd, cuddZddWeakDivF, f, g, zero); + Cudd_Deref(q); + return(zero); + } + + if (g1 != zero) { + Cudd_RecursiveDerefZdd(dd, q); + tmp = cuddZddWeakDivF(dd, f1, g1); + if (tmp == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + return(NULL); + } + Cudd_Ref(tmp); + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, g1); + if (q == g) + q = tmp; + else { + q = cuddZddIntersect(dd, q, tmp); + if (q == NULL) { + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + return(NULL); + } + Cudd_Ref(q); + Cudd_RecursiveDerefZdd(dd, tmp); + } + } + else { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, g1); + } + + if (q == zero) { + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + cuddCacheInsert2(dd, cuddZddWeakDivF, f, g, zero); + Cudd_Deref(q); + return(zero); + } + + if (gd != zero) { + Cudd_RecursiveDerefZdd(dd, q); + tmp = cuddZddWeakDivF(dd, fd, gd); + if (tmp == NULL) { + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + return(NULL); + } + Cudd_Ref(tmp); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + if (q == g) + q = tmp; + else { + q = cuddZddIntersect(dd, q, tmp); + if (q == NULL) { + Cudd_RecursiveDerefZdd(dd, tmp); + return(NULL); + } + Cudd_Ref(q); + Cudd_RecursiveDerefZdd(dd, tmp); + } + } + else { + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + } + + cuddCacheInsert2(dd, cuddZddWeakDivF, f, g, q); + Cudd_Deref(q); + return(q); + +} /* end of cuddZddWeakDivF */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddDivide.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_zddDivide] + +******************************************************************************/ +DdNode * +cuddZddDivide( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + int v; + DdNode *one = DD_ONE(dd); + DdNode *zero = DD_ZERO(dd); + DdNode *f0, *f1, *g0, *g1; + DdNode *q, *r, *tmp; + int flag; + + statLine(dd); + if (g == one) + return(f); + if (f == zero || f == one) + return(zero); + if (f == g) + return(one); + + /* Check cache. */ + r = cuddCacheLookup2Zdd(dd, cuddZddDivide, f, g); + if (r) + return(r); + + v = g->index; + + flag = cuddZddGetCofactors2(dd, f, v, &f1, &f0); + if (flag == 1) + return(NULL); + Cudd_Ref(f1); + Cudd_Ref(f0); + flag = cuddZddGetCofactors2(dd, g, v, &g1, &g0); /* g1 != zero */ + if (flag == 1) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + return(NULL); + } + Cudd_Ref(g1); + Cudd_Ref(g0); + + r = cuddZddDivide(dd, f1, g1); + if (r == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + return(NULL); + } + Cudd_Ref(r); + + if (r != zero && g0 != zero) { + tmp = r; + q = cuddZddDivide(dd, f0, g0); + if (q == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + return(NULL); + } + Cudd_Ref(q); + r = cuddZddIntersect(dd, r, q); + if (r == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, q); + return(NULL); + } + Cudd_Ref(r); + Cudd_RecursiveDerefZdd(dd, q); + Cudd_RecursiveDerefZdd(dd, tmp); + } + + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + + cuddCacheInsert2(dd, cuddZddDivide, f, g, r); + Cudd_Deref(r); + return(r); + +} /* end of cuddZddDivide */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddDivideF.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_zddDivideF] + +******************************************************************************/ +DdNode * +cuddZddDivideF( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + int v; + DdNode *one = DD_ONE(dd); + DdNode *zero = DD_ZERO(dd); + DdNode *f0, *f1, *g0, *g1; + DdNode *q, *r, *tmp; + int flag; + + statLine(dd); + if (g == one) + return(f); + if (f == zero || f == one) + return(zero); + if (f == g) + return(one); + + /* Check cache. */ + r = cuddCacheLookup2Zdd(dd, cuddZddDivideF, f, g); + if (r) + return(r); + + v = g->index; + + flag = cuddZddGetCofactors2(dd, f, v, &f1, &f0); + if (flag == 1) + return(NULL); + Cudd_Ref(f1); + Cudd_Ref(f0); + flag = cuddZddGetCofactors2(dd, g, v, &g1, &g0); /* g1 != zero */ + if (flag == 1) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + return(NULL); + } + Cudd_Ref(g1); + Cudd_Ref(g0); + + r = cuddZddDivideF(dd, f1, g1); + if (r == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + return(NULL); + } + Cudd_Ref(r); + + if (r != zero && g0 != zero) { + tmp = r; + q = cuddZddDivideF(dd, f0, g0); + if (q == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + return(NULL); + } + Cudd_Ref(q); + r = cuddZddIntersect(dd, r, q); + if (r == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, q); + return(NULL); + } + Cudd_Ref(r); + Cudd_RecursiveDerefZdd(dd, q); + Cudd_RecursiveDerefZdd(dd, tmp); + } + + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + + cuddCacheInsert2(dd, cuddZddDivideF, f, g, r); + Cudd_Deref(r); + return(r); + +} /* end of cuddZddDivideF */ + + +/**Function******************************************************************** + + Synopsis [Computes the three-way decomposition of f w.r.t. v.] + + Description [Computes the three-way decomposition of function f (represented + by a ZDD) wit respect to variable v.] + + SideEffects [The results are returned in f1, f0, and fd.] + + SeeAlso [cuddZddGetCofactors2] + +******************************************************************************/ +int +cuddZddGetCofactors3( + DdManager * dd, + DdNode * f, + int v, + DdNode ** f1, + DdNode ** f0, + DdNode ** fd) +{ + DdNode *pc, *nc; + DdNode *zero = DD_ZERO(dd); + int top, hv, ht, pv, nv; + int level; + + top = dd->permZ[f->index]; + level = dd->permZ[v]; + hv = level >> 1; + ht = top >> 1; + + if (hv < ht) { + *f1 = zero; + *f0 = zero; + *fd = f; + } + else { + pv = cuddZddGetPosVarIndex(dd, v); + nv = cuddZddGetNegVarIndex(dd, v); + + /* not to create intermediate ZDD node */ + if (cuddZddGetPosVarLevel(dd, v) < cuddZddGetNegVarLevel(dd, v)) { + pc = cuddZddSubset1(dd, f, pv); + if (pc == NULL) + return(1); + Cudd_Ref(pc); + nc = cuddZddSubset0(dd, f, pv); + if (nc == NULL) { + Cudd_RecursiveDerefZdd(dd, pc); + return(1); + } + Cudd_Ref(nc); + + *f1 = cuddZddSubset0(dd, pc, nv); + if (*f1 == NULL) { + Cudd_RecursiveDerefZdd(dd, pc); + Cudd_RecursiveDerefZdd(dd, nc); + return(1); + } + Cudd_Ref(*f1); + *f0 = cuddZddSubset1(dd, nc, nv); + if (*f0 == NULL) { + Cudd_RecursiveDerefZdd(dd, pc); + Cudd_RecursiveDerefZdd(dd, nc); + Cudd_RecursiveDerefZdd(dd, *f1); + return(1); + } + Cudd_Ref(*f0); + + *fd = cuddZddSubset0(dd, nc, nv); + if (*fd == NULL) { + Cudd_RecursiveDerefZdd(dd, pc); + Cudd_RecursiveDerefZdd(dd, nc); + Cudd_RecursiveDerefZdd(dd, *f1); + Cudd_RecursiveDerefZdd(dd, *f0); + return(1); + } + Cudd_Ref(*fd); + } else { + pc = cuddZddSubset1(dd, f, nv); + if (pc == NULL) + return(1); + Cudd_Ref(pc); + nc = cuddZddSubset0(dd, f, nv); + if (nc == NULL) { + Cudd_RecursiveDerefZdd(dd, pc); + return(1); + } + Cudd_Ref(nc); + + *f0 = cuddZddSubset0(dd, pc, pv); + if (*f0 == NULL) { + Cudd_RecursiveDerefZdd(dd, pc); + Cudd_RecursiveDerefZdd(dd, nc); + return(1); + } + Cudd_Ref(*f0); + *f1 = cuddZddSubset1(dd, nc, pv); + if (*f1 == NULL) { + Cudd_RecursiveDerefZdd(dd, pc); + Cudd_RecursiveDerefZdd(dd, nc); + Cudd_RecursiveDerefZdd(dd, *f1); + return(1); + } + Cudd_Ref(*f1); + + *fd = cuddZddSubset0(dd, nc, pv); + if (*fd == NULL) { + Cudd_RecursiveDerefZdd(dd, pc); + Cudd_RecursiveDerefZdd(dd, nc); + Cudd_RecursiveDerefZdd(dd, *f1); + Cudd_RecursiveDerefZdd(dd, *f0); + return(1); + } + Cudd_Ref(*fd); + } + + Cudd_RecursiveDerefZdd(dd, pc); + Cudd_RecursiveDerefZdd(dd, nc); + Cudd_Deref(*f1); + Cudd_Deref(*f0); + Cudd_Deref(*fd); + } + return(0); + +} /* end of cuddZddGetCofactors3 */ + + +/**Function******************************************************************** + + Synopsis [Computes the two-way decomposition of f w.r.t. v.] + + Description [] + + SideEffects [The results are returned in f1 and f0.] + + SeeAlso [cuddZddGetCofactors3] + +******************************************************************************/ +int +cuddZddGetCofactors2( + DdManager * dd, + DdNode * f, + int v, + DdNode ** f1, + DdNode ** f0) +{ + *f1 = cuddZddSubset1(dd, f, v); + if (*f1 == NULL) + return(1); + *f0 = cuddZddSubset0(dd, f, v); + if (*f0 == NULL) { + Cudd_RecursiveDerefZdd(dd, *f1); + return(1); + } + return(0); + +} /* end of cuddZddGetCofactors2 */ + + +/**Function******************************************************************** + + Synopsis [Computes a complement of a ZDD node.] + + Description [Computes the complement of a ZDD node. So far, since we + couldn't find a direct way to get the complement of a ZDD cover, we first + convert a ZDD cover to a BDD, then make the complement of the ZDD cover + from the complement of the BDD node by using ISOP.] + + SideEffects [The result depends on current variable order.] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddZddComplement( + DdManager * dd, + DdNode *node) +{ + DdNode *b, *isop, *zdd_I; + + /* Check cache */ + zdd_I = cuddCacheLookup1Zdd(dd, cuddZddComplement, node); + if (zdd_I) + return(zdd_I); + + b = cuddMakeBddFromZddCover(dd, node); + if (!b) + return(NULL); + cuddRef(b); + isop = cuddZddIsop(dd, Cudd_Not(b), Cudd_Not(b), &zdd_I); + if (!isop) { + Cudd_RecursiveDeref(dd, b); + return(NULL); + } + cuddRef(isop); + cuddRef(zdd_I); + Cudd_RecursiveDeref(dd, b); + Cudd_RecursiveDeref(dd, isop); + + cuddCacheInsert1(dd, cuddZddComplement, node, zdd_I); + cuddDeref(zdd_I); + return(zdd_I); +} /* end of cuddZddComplement */ + + +/**Function******************************************************************** + + Synopsis [Returns the index of positive ZDD variable.] + + Description [Returns the index of positive ZDD variable.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddGetPosVarIndex( + DdManager * dd, + int index) +{ + int pv = (index >> 1) << 1; + return(pv); +} /* end of cuddZddGetPosVarIndex */ + + +/**Function******************************************************************** + + Synopsis [Returns the index of negative ZDD variable.] + + Description [Returns the index of negative ZDD variable.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddGetNegVarIndex( + DdManager * dd, + int index) +{ + int nv = index | 0x1; + return(nv); +} /* end of cuddZddGetPosVarIndex */ + + +/**Function******************************************************************** + + Synopsis [Returns the level of positive ZDD variable.] + + Description [Returns the level of positive ZDD variable.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddGetPosVarLevel( + DdManager * dd, + int index) +{ + int pv = cuddZddGetPosVarIndex(dd, index); + return(dd->permZ[pv]); +} /* end of cuddZddGetPosVarLevel */ + + +/**Function******************************************************************** + + Synopsis [Returns the level of negative ZDD variable.] + + Description [Returns the level of negative ZDD variable.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddGetNegVarLevel( + DdManager * dd, + int index) +{ + int nv = cuddZddGetNegVarIndex(dd, index); + return(dd->permZ[nv]); +} /* end of cuddZddGetNegVarLevel */ diff --git a/abc_with_bb_support/src/bdd/cudd/cuddZddGroup.c b/abc_with_bb_support/src/bdd/cudd/cuddZddGroup.c new file mode 100644 index 000000000..d6b2b96ac --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddZddGroup.c @@ -0,0 +1,1317 @@ +/**CFile*********************************************************************** + + FileName [cuddZddGroup.c] + + PackageName [cudd] + + Synopsis [Functions for ZDD group sifting.] + + Description [External procedures included in this file: +
          +
        • Cudd_MakeZddTreeNode() +
        + Internal procedures included in this file: +
          +
        • cuddZddTreeSifting() +
        + Static procedures included in this module: +
          +
        • zddTreeSiftingAux() +
        • zddCountInternalMtrNodes() +
        • zddReorderChildren() +
        • zddFindNodeHiLo() +
        • zddUniqueCompareGroup() +
        • zddGroupSifting() +
        • zddGroupSiftingAux() +
        • zddGroupSiftingUp() +
        • zddGroupSiftingDown() +
        • zddGroupMove() +
        • zddGroupMoveBackward() +
        • zddGroupSiftingBackward() +
        • zddMergeGroups() +
        ] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddZddGroup.c,v 1.1.1.1 2003/02/24 22:23:54 wjiang Exp $"; +#endif + +static int *entry; +extern int zddTotalNumberSwapping; +#ifdef DD_STATS +static int extsymmcalls; +static int extsymm; +static int secdiffcalls; +static int secdiff; +static int secdiffmisfire; +#endif +#ifdef DD_DEBUG +static int pr = 0; /* flag to enable printing while debugging */ + /* by depositing a 1 into it */ +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int zddTreeSiftingAux ARGS((DdManager *table, MtrNode *treenode, Cudd_ReorderingType method)); +#ifdef DD_STATS +static int zddCountInternalMtrNodes ARGS((DdManager *table, MtrNode *treenode)); +#endif +static int zddReorderChildren ARGS((DdManager *table, MtrNode *treenode, Cudd_ReorderingType method)); +static void zddFindNodeHiLo ARGS((DdManager *table, MtrNode *treenode, int *lower, int *upper)); +static int zddUniqueCompareGroup ARGS((int *ptrX, int *ptrY)); +static int zddGroupSifting ARGS((DdManager *table, int lower, int upper)); +static int zddGroupSiftingAux ARGS((DdManager *table, int x, int xLow, int xHigh)); +static int zddGroupSiftingUp ARGS((DdManager *table, int y, int xLow, Move **moves)); +static int zddGroupSiftingDown ARGS((DdManager *table, int x, int xHigh, Move **moves)); +static int zddGroupMove ARGS((DdManager *table, int x, int y, Move **moves)); +static int zddGroupMoveBackward ARGS((DdManager *table, int x, int y)); +static int zddGroupSiftingBackward ARGS((DdManager *table, Move *moves, int size)); +static void zddMergeGroups ARGS((DdManager *table, MtrNode *treenode, int low, int high)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Creates a new ZDD variable group.] + + Description [Creates a new ZDD variable group. The group starts at + variable and contains size variables. The parameter low is the index + of the first variable. If the variable already exists, its current + position in the order is known to the manager. If the variable does + not exist yet, the position is assumed to be the same as the index. + The group tree is created if it does not exist yet. + Returns a pointer to the group if successful; NULL otherwise.] + + SideEffects [The ZDD variable tree is changed.] + + SeeAlso [Cudd_MakeTreeNode] + +******************************************************************************/ +MtrNode * +Cudd_MakeZddTreeNode( + DdManager * dd /* manager */, + unsigned int low /* index of the first group variable */, + unsigned int size /* number of variables in the group */, + unsigned int type /* MTR_DEFAULT or MTR_FIXED */) +{ + MtrNode *group; + MtrNode *tree; + unsigned int level; + + /* If the variable does not exist yet, the position is assumed to be + ** the same as the index. Therefore, applications that rely on + ** Cudd_bddNewVarAtLevel or Cudd_addNewVarAtLevel to create new + ** variables have to create the variables before they group them. + */ + level = (low < (unsigned int) dd->sizeZ) ? dd->permZ[low] : low; + + if (level + size - 1> (int) MTR_MAXHIGH) + return(NULL); + + /* If the tree does not exist yet, create it. */ + tree = dd->treeZ; + if (tree == NULL) { + dd->treeZ = tree = Mtr_InitGroupTree(0, dd->sizeZ); + if (tree == NULL) + return(NULL); + tree->index = dd->invpermZ[0]; + } + + /* Extend the upper bound of the tree if necessary. This allows the + ** application to create groups even before the variables are created. + */ + tree->size = ddMax(tree->size, level + size); + + /* Create the group. */ + group = Mtr_MakeGroup(tree, level, size, type); + if (group == NULL) + return(NULL); + + /* Initialize the index field to the index of the variable currently + ** in position low. This field will be updated by the reordering + ** procedure to provide a handle to the group once it has been moved. + */ + group->index = (MtrHalfWord) low; + + return(group); + +} /* end of Cudd_MakeZddTreeNode */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Tree sifting algorithm for ZDDs.] + + Description [Tree sifting algorithm for ZDDs. Assumes that a tree + representing a group hierarchy is passed as a parameter. It then + reorders each group in postorder fashion by calling + zddTreeSiftingAux. Assumes that no dead nodes are present. Returns + 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +int +cuddZddTreeSifting( + DdManager * table /* DD table */, + Cudd_ReorderingType method /* reordering method for the groups of leaves */) +{ + int i; + int nvars; + int result; + int tempTree; + + /* If no tree is provided we create a temporary one in which all + ** variables are in a single group. After reordering this tree is + ** destroyed. + */ + tempTree = table->treeZ == NULL; + if (tempTree) { + table->treeZ = Mtr_InitGroupTree(0,table->sizeZ); + table->treeZ->index = table->invpermZ[0]; + } + nvars = table->sizeZ; + +#ifdef DD_DEBUG + if (pr > 0 && !tempTree) + (void) fprintf(table->out,"cuddZddTreeSifting:"); + Mtr_PrintGroups(table->treeZ,pr <= 0); +#endif +#if 0 + /* Debugging code. */ + if (table->tree && table->treeZ) { + (void) fprintf(table->out,"\n"); + Mtr_PrintGroups(table->tree, 0); + cuddPrintVarGroups(table,table->tree,0,0); + for (i = 0; i < table->size; i++) { + (void) fprintf(table->out,"%s%d", + (i == 0) ? "" : ",", table->invperm[i]); + } + (void) fprintf(table->out,"\n"); + for (i = 0; i < table->size; i++) { + (void) fprintf(table->out,"%s%d", + (i == 0) ? "" : ",", table->perm[i]); + } + (void) fprintf(table->out,"\n\n"); + Mtr_PrintGroups(table->treeZ,0); + cuddPrintVarGroups(table,table->treeZ,1,0); + for (i = 0; i < table->sizeZ; i++) { + (void) fprintf(table->out,"%s%d", + (i == 0) ? "" : ",", table->invpermZ[i]); + } + (void) fprintf(table->out,"\n"); + for (i = 0; i < table->sizeZ; i++) { + (void) fprintf(table->out,"%s%d", + (i == 0) ? "" : ",", table->permZ[i]); + } + (void) fprintf(table->out,"\n"); + } + /* End of debugging code. */ +#endif +#ifdef DD_STATS + extsymmcalls = 0; + extsymm = 0; + secdiffcalls = 0; + secdiff = 0; + secdiffmisfire = 0; + + (void) fprintf(table->out,"\n"); + if (!tempTree) + (void) fprintf(table->out,"#:IM_NODES %8d: group tree nodes\n", + zddCountInternalMtrNodes(table,table->treeZ)); +#endif + + /* Initialize the group of each subtable to itself. Initially + ** there are no groups. Groups are created according to the tree + ** structure in postorder fashion. + */ + for (i = 0; i < nvars; i++) + table->subtableZ[i].next = i; + + /* Reorder. */ + result = zddTreeSiftingAux(table, table->treeZ, method); + +#ifdef DD_STATS /* print stats */ + if (!tempTree && method == CUDD_REORDER_GROUP_SIFT && + (table->groupcheck == CUDD_GROUP_CHECK7 || + table->groupcheck == CUDD_GROUP_CHECK5)) { + (void) fprintf(table->out,"\nextsymmcalls = %d\n",extsymmcalls); + (void) fprintf(table->out,"extsymm = %d",extsymm); + } + if (!tempTree && method == CUDD_REORDER_GROUP_SIFT && + table->groupcheck == CUDD_GROUP_CHECK7) { + (void) fprintf(table->out,"\nsecdiffcalls = %d\n",secdiffcalls); + (void) fprintf(table->out,"secdiff = %d\n",secdiff); + (void) fprintf(table->out,"secdiffmisfire = %d",secdiffmisfire); + } +#endif + + if (tempTree) + Cudd_FreeZddTree(table); + return(result); + +} /* end of cuddZddTreeSifting */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Visits the group tree and reorders each group.] + + Description [Recursively visits the group tree and reorders each + group in postorder fashion. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +zddTreeSiftingAux( + DdManager * table, + MtrNode * treenode, + Cudd_ReorderingType method) +{ + MtrNode *auxnode; + int res; + +#ifdef DD_DEBUG + Mtr_PrintGroups(treenode,1); +#endif + + auxnode = treenode; + while (auxnode != NULL) { + if (auxnode->child != NULL) { + if (!zddTreeSiftingAux(table, auxnode->child, method)) + return(0); + res = zddReorderChildren(table, auxnode, CUDD_REORDER_GROUP_SIFT); + if (res == 0) + return(0); + } else if (auxnode->size > 1) { + if (!zddReorderChildren(table, auxnode, method)) + return(0); + } + auxnode = auxnode->younger; + } + + return(1); + +} /* end of zddTreeSiftingAux */ + + +#ifdef DD_STATS +/**Function******************************************************************** + + Synopsis [Counts the number of internal nodes of the group tree.] + + Description [Counts the number of internal nodes of the group tree. + Returns the count.] + + SideEffects [None] + +******************************************************************************/ +static int +zddCountInternalMtrNodes( + DdManager * table, + MtrNode * treenode) +{ + MtrNode *auxnode; + int count,nodeCount; + + + nodeCount = 0; + auxnode = treenode; + while (auxnode != NULL) { + if (!(MTR_TEST(auxnode,MTR_TERMINAL))) { + nodeCount++; + count = zddCountInternalMtrNodes(table,auxnode->child); + nodeCount += count; + } + auxnode = auxnode->younger; + } + + return(nodeCount); + +} /* end of zddCountInternalMtrNodes */ +#endif + + +/**Function******************************************************************** + + Synopsis [Reorders the children of a group tree node according to + the options.] + + Description [Reorders the children of a group tree node according to + the options. After reordering puts all the variables in the group + and/or its descendents in a single group. This allows hierarchical + reordering. If the variables in the group do not exist yet, simply + does nothing. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +zddReorderChildren( + DdManager * table, + MtrNode * treenode, + Cudd_ReorderingType method) +{ + int lower; + int upper; + int result; + unsigned int initialSize; + + zddFindNodeHiLo(table,treenode,&lower,&upper); + /* If upper == -1 these variables do not exist yet. */ + if (upper == -1) + return(1); + + if (treenode->flags == MTR_FIXED) { + result = 1; + } else { +#ifdef DD_STATS + (void) fprintf(table->out," "); +#endif + switch (method) { + case CUDD_REORDER_RANDOM: + case CUDD_REORDER_RANDOM_PIVOT: + result = cuddZddSwapping(table,lower,upper,method); + break; + case CUDD_REORDER_SIFT: + result = cuddZddSifting(table,lower,upper); + break; + case CUDD_REORDER_SIFT_CONVERGE: + do { + initialSize = table->keysZ; + result = cuddZddSifting(table,lower,upper); + if (initialSize <= table->keysZ) + break; +#ifdef DD_STATS + else + (void) fprintf(table->out,"\n"); +#endif + } while (result != 0); + break; + case CUDD_REORDER_SYMM_SIFT: + result = cuddZddSymmSifting(table,lower,upper); + break; + case CUDD_REORDER_SYMM_SIFT_CONV: + result = cuddZddSymmSiftingConv(table,lower,upper); + break; + case CUDD_REORDER_GROUP_SIFT: + result = zddGroupSifting(table,lower,upper); + break; + case CUDD_REORDER_LINEAR: + result = cuddZddLinearSifting(table,lower,upper); + break; + case CUDD_REORDER_LINEAR_CONVERGE: + do { + initialSize = table->keysZ; + result = cuddZddLinearSifting(table,lower,upper); + if (initialSize <= table->keysZ) + break; +#ifdef DD_STATS + else + (void) fprintf(table->out,"\n"); +#endif + } while (result != 0); + break; + default: + return(0); + } + } + + /* Create a single group for all the variables that were sifted, + ** so that they will be treated as a single block by successive + ** invocations of zddGroupSifting. + */ + zddMergeGroups(table,treenode,lower,upper); + +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"zddReorderChildren:"); +#endif + + return(result); + +} /* end of zddReorderChildren */ + + +/**Function******************************************************************** + + Synopsis [Finds the lower and upper bounds of the group represented + by treenode.] + + Description [Finds the lower and upper bounds of the group represented + by treenode. The high and low fields of treenode are indices. From + those we need to derive the current positions, and find maximum and + minimum.] + + SideEffects [The bounds are returned as side effects.] + + SeeAlso [] + +******************************************************************************/ +static void +zddFindNodeHiLo( + DdManager * table, + MtrNode * treenode, + int * lower, + int * upper) +{ + int low; + int high; + + /* Check whether no variables in this group already exist. + ** If so, return immediately. The calling procedure will know from + ** the values of upper that no reordering is needed. + */ + if ((int) treenode->low >= table->sizeZ) { + *lower = table->sizeZ; + *upper = -1; + return; + } + + *lower = low = (unsigned int) table->permZ[treenode->index]; + high = (int) (low + treenode->size - 1); + + if (high >= table->sizeZ) { + /* This is the case of a partially existing group. The aim is to + ** reorder as many variables as safely possible. If the tree + ** node is terminal, we just reorder the subset of the group + ** that is currently in existence. If the group has + ** subgroups, then we only reorder those subgroups that are + ** fully instantiated. This way we avoid breaking up a group. + */ + MtrNode *auxnode = treenode->child; + if (auxnode == NULL) { + *upper = (unsigned int) table->sizeZ - 1; + } else { + /* Search the subgroup that strands the table->sizeZ line. + ** If the first group starts at 0 and goes past table->sizeZ + ** upper will get -1, thus correctly signaling that no reordering + ** should take place. + */ + while (auxnode != NULL) { + int thisLower = table->permZ[auxnode->low]; + int thisUpper = thisLower + auxnode->size - 1; + if (thisUpper >= table->sizeZ && thisLower < table->sizeZ) + *upper = (unsigned int) thisLower - 1; + auxnode = auxnode->younger; + } + } + } else { + /* Normal case: All the variables of the group exist. */ + *upper = (unsigned int) high; + } + +#ifdef DD_DEBUG + /* Make sure that all variables in group are contiguous. */ + assert(treenode->size >= *upper - *lower + 1); +#endif + + return; + +} /* end of zddFindNodeHiLo */ + + +/**Function******************************************************************** + + Synopsis [Comparison function used by qsort.] + + Description [Comparison function used by qsort to order the variables + according to the number of keys in the subtables. Returns the + difference in number of keys between the two variables being + compared.] + + SideEffects [None] + +******************************************************************************/ +static int +zddUniqueCompareGroup( + int * ptrX, + int * ptrY) +{ +#if 0 + if (entry[*ptrY] == entry[*ptrX]) { + return((*ptrX) - (*ptrY)); + } +#endif + return(entry[*ptrY] - entry[*ptrX]); + +} /* end of zddUniqueCompareGroup */ + + +/**Function******************************************************************** + + Synopsis [Sifts from treenode->low to treenode->high.] + + Description [Sifts from treenode->low to treenode->high. If + croupcheck == CUDD_GROUP_CHECK7, it checks for group creation at the + end of the initial sifting. If a group is created, it is then sifted + again. After sifting one variable, the group that contains it is + dissolved. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +zddGroupSifting( + DdManager * table, + int lower, + int upper) +{ + int *var; + int i,j,x,xInit; + int nvars; + int classes; + int result; + int *sifted; +#ifdef DD_STATS + unsigned previousSize; +#endif + int xindex; + + nvars = table->sizeZ; + + /* Order variables to sift. */ + entry = NULL; + sifted = NULL; + var = ALLOC(int,nvars); + if (var == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto zddGroupSiftingOutOfMem; + } + entry = ALLOC(int,nvars); + if (entry == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto zddGroupSiftingOutOfMem; + } + sifted = ALLOC(int,nvars); + if (sifted == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto zddGroupSiftingOutOfMem; + } + + /* Here we consider only one representative for each group. */ + for (i = 0, classes = 0; i < nvars; i++) { + sifted[i] = 0; + x = table->permZ[i]; + if ((unsigned) x >= table->subtableZ[x].next) { + entry[i] = table->subtableZ[x].keys; + var[classes] = i; + classes++; + } + } + + qsort((void *)var,classes,sizeof(int),(int (*)(const void *, const void *))zddUniqueCompareGroup); + + /* Now sift. */ + for (i = 0; i < ddMin(table->siftMaxVar,classes); i++) { + if (zddTotalNumberSwapping >= table->siftMaxSwap) + break; + xindex = var[i]; + if (sifted[xindex] == 1) /* variable already sifted as part of group */ + continue; + x = table->permZ[xindex]; /* find current level of this variable */ + if (x < lower || x > upper) + continue; +#ifdef DD_STATS + previousSize = table->keysZ; +#endif +#ifdef DD_DEBUG + /* x is bottom of group */ + assert((unsigned) x >= table->subtableZ[x].next); +#endif + result = zddGroupSiftingAux(table,x,lower,upper); + if (!result) goto zddGroupSiftingOutOfMem; + +#ifdef DD_STATS + if (table->keysZ < previousSize) { + (void) fprintf(table->out,"-"); + } else if (table->keysZ > previousSize) { + (void) fprintf(table->out,"+"); + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + + /* Mark variables in the group just sifted. */ + x = table->permZ[xindex]; + if ((unsigned) x != table->subtableZ[x].next) { + xInit = x; + do { + j = table->invpermZ[x]; + sifted[j] = 1; + x = table->subtableZ[x].next; + } while (x != xInit); + } + +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"zddGroupSifting:"); +#endif + } /* for */ + + FREE(sifted); + FREE(var); + FREE(entry); + + return(1); + +zddGroupSiftingOutOfMem: + if (entry != NULL) FREE(entry); + if (var != NULL) FREE(var); + if (sifted != NULL) FREE(sifted); + + return(0); + +} /* end of zddGroupSifting */ + + +/**Function******************************************************************** + + Synopsis [Sifts one variable up and down until it has taken all + positions. Checks for aggregation.] + + Description [Sifts one variable up and down until it has taken all + positions. Checks for aggregation. There may be at most two sweeps, + even if the group grows. Assumes that x is either an isolated + variable, or it is the bottom of a group. All groups may not have + been found. The variable being moved is returned to the best position + seen during sifting. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +zddGroupSiftingAux( + DdManager * table, + int x, + int xLow, + int xHigh) +{ + Move *move; + Move *moves; /* list of moves */ + int initialSize; + int result; + + +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"zddGroupSiftingAux from %d to %d\n",xLow,xHigh); + assert((unsigned) x >= table->subtableZ[x].next); /* x is bottom of group */ +#endif + + initialSize = table->keysZ; + moves = NULL; + + if (x == xLow) { /* Sift down */ +#ifdef DD_DEBUG + /* x must be a singleton */ + assert((unsigned) x == table->subtableZ[x].next); +#endif + if (x == xHigh) return(1); /* just one variable */ + + if (!zddGroupSiftingDown(table,x,xHigh,&moves)) + goto zddGroupSiftingAuxOutOfMem; + /* at this point x == xHigh, unless early term */ + + /* move backward and stop at best position */ + result = zddGroupSiftingBackward(table,moves,initialSize); +#ifdef DD_DEBUG + assert(table->keysZ <= (unsigned) initialSize); +#endif + if (!result) goto zddGroupSiftingAuxOutOfMem; + + } else if (cuddZddNextHigh(table,x) > xHigh) { /* Sift up */ +#ifdef DD_DEBUG + /* x is bottom of group */ + assert((unsigned) x >= table->subtableZ[x].next); +#endif + /* Find top of x's group */ + x = table->subtableZ[x].next; + + if (!zddGroupSiftingUp(table,x,xLow,&moves)) + goto zddGroupSiftingAuxOutOfMem; + /* at this point x == xLow, unless early term */ + + /* move backward and stop at best position */ + result = zddGroupSiftingBackward(table,moves,initialSize); +#ifdef DD_DEBUG + assert(table->keysZ <= (unsigned) initialSize); +#endif + if (!result) goto zddGroupSiftingAuxOutOfMem; + + } else if (x - xLow > xHigh - x) { /* must go down first: shorter */ + if (!zddGroupSiftingDown(table,x,xHigh,&moves)) + goto zddGroupSiftingAuxOutOfMem; + /* at this point x == xHigh, unless early term */ + + /* Find top of group */ + if (moves) { + x = moves->y; + } + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + x = table->subtableZ[x].next; +#ifdef DD_DEBUG + /* x should be the top of a group */ + assert((unsigned) x <= table->subtableZ[x].next); +#endif + + if (!zddGroupSiftingUp(table,x,xLow,&moves)) + goto zddGroupSiftingAuxOutOfMem; + + /* move backward and stop at best position */ + result = zddGroupSiftingBackward(table,moves,initialSize); +#ifdef DD_DEBUG + assert(table->keysZ <= (unsigned) initialSize); +#endif + if (!result) goto zddGroupSiftingAuxOutOfMem; + + } else { /* moving up first: shorter */ + /* Find top of x's group */ + x = table->subtableZ[x].next; + + if (!zddGroupSiftingUp(table,x,xLow,&moves)) + goto zddGroupSiftingAuxOutOfMem; + /* at this point x == xHigh, unless early term */ + + if (moves) { + x = moves->x; + } + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; +#ifdef DD_DEBUG + /* x is bottom of a group */ + assert((unsigned) x >= table->subtableZ[x].next); +#endif + + if (!zddGroupSiftingDown(table,x,xHigh,&moves)) + goto zddGroupSiftingAuxOutOfMem; + + /* move backward and stop at best position */ + result = zddGroupSiftingBackward(table,moves,initialSize); +#ifdef DD_DEBUG + assert(table->keysZ <= (unsigned) initialSize); +#endif + if (!result) goto zddGroupSiftingAuxOutOfMem; + } + + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *) moves); + moves = move; + } + + return(1); + +zddGroupSiftingAuxOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *) moves); + moves = move; + } + + return(0); + +} /* end of zddGroupSiftingAux */ + + +/**Function******************************************************************** + + Synopsis [Sifts up a variable until either it reaches position xLow + or the size of the DD heap increases too much.] + + Description [Sifts up a variable until either it reaches position + xLow or the size of the DD heap increases too much. Assumes that y is + the top of a group (or a singleton). Checks y for aggregation to the + adjacent variables. Records all the moves that are appended to the + list of moves received as input and returned as a side effect. + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +zddGroupSiftingUp( + DdManager * table, + int y, + int xLow, + Move ** moves) +{ + Move *move; + int x; + int size; + int gxtop; + int limitSize; + int xindex, yindex; + + yindex = table->invpermZ[y]; + + limitSize = table->keysZ; + + x = cuddZddNextLow(table,y); + while (x >= xLow) { + gxtop = table->subtableZ[x].next; + if (table->subtableZ[x].next == (unsigned) x && + table->subtableZ[y].next == (unsigned) y) { + /* x and y are self groups */ + xindex = table->invpermZ[x]; + size = cuddZddSwapInPlace(table,x,y); +#ifdef DD_DEBUG + assert(table->subtableZ[x].next == (unsigned) x); + assert(table->subtableZ[y].next == (unsigned) y); +#endif + if (size == 0) goto zddGroupSiftingUpOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto zddGroupSiftingUpOutOfMem; + move->x = x; + move->y = y; + move->flags = MTR_DEFAULT; + move->size = size; + move->next = *moves; + *moves = move; + +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"zddGroupSiftingUp (2 single groups):\n"); +#endif + if ((double) size > (double) limitSize * table->maxGrowth) + return(1); + if (size < limitSize) limitSize = size; + } else { /* group move */ + size = zddGroupMove(table,x,y,moves); + if (size == 0) goto zddGroupSiftingUpOutOfMem; + if ((double) size > (double) limitSize * table->maxGrowth) + return(1); + if (size < limitSize) limitSize = size; + } + y = gxtop; + x = cuddZddNextLow(table,y); + } + + return(1); + +zddGroupSiftingUpOutOfMem: + while (*moves != NULL) { + move = (*moves)->next; + cuddDeallocNode(table, (DdNode *) *moves); + *moves = move; + } + return(0); + +} /* end of zddGroupSiftingUp */ + + +/**Function******************************************************************** + + Synopsis [Sifts down a variable until it reaches position xHigh.] + + Description [Sifts down a variable until it reaches position xHigh. + Assumes that x is the bottom of a group (or a singleton). Records + all the moves. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +zddGroupSiftingDown( + DdManager * table, + int x, + int xHigh, + Move ** moves) +{ + Move *move; + int y; + int size; + int limitSize; + int gxtop,gybot; + int xindex; + + + /* Initialize R */ + xindex = table->invpermZ[x]; + gxtop = table->subtableZ[x].next; + limitSize = size = table->keysZ; + y = cuddZddNextHigh(table,x); + while (y <= xHigh) { + /* Find bottom of y group. */ + gybot = table->subtableZ[y].next; + while (table->subtableZ[gybot].next != (unsigned) y) + gybot = table->subtableZ[gybot].next; + + if (table->subtableZ[x].next == (unsigned) x && + table->subtableZ[y].next == (unsigned) y) { + /* x and y are self groups */ + size = cuddZddSwapInPlace(table,x,y); +#ifdef DD_DEBUG + assert(table->subtableZ[x].next == (unsigned) x); + assert(table->subtableZ[y].next == (unsigned) y); +#endif + if (size == 0) goto zddGroupSiftingDownOutOfMem; + + /* Record move. */ + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto zddGroupSiftingDownOutOfMem; + move->x = x; + move->y = y; + move->flags = MTR_DEFAULT; + move->size = size; + move->next = *moves; + *moves = move; + +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"zddGroupSiftingDown (2 single groups):\n"); +#endif + if ((double) size > (double) limitSize * table->maxGrowth) + return(1); + if (size < limitSize) limitSize = size; + x = y; + y = cuddZddNextHigh(table,x); + } else { /* Group move */ + size = zddGroupMove(table,x,y,moves); + if (size == 0) goto zddGroupSiftingDownOutOfMem; + if ((double) size > (double) limitSize * table->maxGrowth) + return(1); + if (size < limitSize) limitSize = size; + } + x = gybot; + y = cuddZddNextHigh(table,x); + } + + return(1); + +zddGroupSiftingDownOutOfMem: + while (*moves != NULL) { + move = (*moves)->next; + cuddDeallocNode(table, (DdNode *) *moves); + *moves = move; + } + + return(0); + +} /* end of zddGroupSiftingDown */ + + +/**Function******************************************************************** + + Synopsis [Swaps two groups and records the move.] + + Description [Swaps two groups and records the move. Returns the + number of keys in the DD table in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +zddGroupMove( + DdManager * table, + int x, + int y, + Move ** moves) +{ + Move *move; + int size; + int i,j,xtop,xbot,xsize,ytop,ybot,ysize,newxtop; + int swapx,swapy; +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + int initialSize,bestSize; +#endif + +#if DD_DEBUG + /* We assume that x < y */ + assert(x < y); +#endif + /* Find top, bottom, and size for the two groups. */ + xbot = x; + xtop = table->subtableZ[x].next; + xsize = xbot - xtop + 1; + ybot = y; + while ((unsigned) ybot < table->subtableZ[ybot].next) + ybot = table->subtableZ[ybot].next; + ytop = y; + ysize = ybot - ytop + 1; + +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + initialSize = bestSize = table->keysZ; +#endif + /* Sift the variables of the second group up through the first group */ + for (i = 1; i <= ysize; i++) { + for (j = 1; j <= xsize; j++) { + size = cuddZddSwapInPlace(table,x,y); + if (size == 0) goto zddGroupMoveOutOfMem; +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + if (size < bestSize) + bestSize = size; +#endif + swapx = x; swapy = y; + y = x; + x = cuddZddNextLow(table,y); + } + y = ytop + i; + x = cuddZddNextLow(table,y); + } +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + if ((bestSize < initialSize) && (bestSize < size)) + (void) fprintf(table->out,"Missed local minimum: initialSize:%d bestSize:%d finalSize:%d\n",initialSize,bestSize,size); +#endif + + /* fix groups */ + y = xtop; /* ytop is now where xtop used to be */ + for (i = 0; i < ysize - 1; i++) { + table->subtableZ[y].next = cuddZddNextHigh(table,y); + y = cuddZddNextHigh(table,y); + } + table->subtableZ[y].next = xtop; /* y is bottom of its group, join */ + /* it to top of its group */ + x = cuddZddNextHigh(table,y); + newxtop = x; + for (i = 0; i < xsize - 1; i++) { + table->subtableZ[x].next = cuddZddNextHigh(table,x); + x = cuddZddNextHigh(table,x); + } + table->subtableZ[x].next = newxtop; /* x is bottom of its group, join */ + /* it to top of its group */ +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"zddGroupMove:\n"); +#endif + + /* Store group move */ + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto zddGroupMoveOutOfMem; + move->x = swapx; + move->y = swapy; + move->flags = MTR_DEFAULT; + move->size = table->keysZ; + move->next = *moves; + *moves = move; + + return(table->keysZ); + +zddGroupMoveOutOfMem: + while (*moves != NULL) { + move = (*moves)->next; + cuddDeallocNode(table, (DdNode *) *moves); + *moves = move; + } + return(0); + +} /* end of zddGroupMove */ + + +/**Function******************************************************************** + + Synopsis [Undoes the swap two groups.] + + Description [Undoes the swap two groups. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +zddGroupMoveBackward( + DdManager * table, + int x, + int y) +{ + int size; + int i,j,xtop,xbot,xsize,ytop,ybot,ysize,newxtop; + + +#if DD_DEBUG + /* We assume that x < y */ + assert(x < y); +#endif + + /* Find top, bottom, and size for the two groups. */ + xbot = x; + xtop = table->subtableZ[x].next; + xsize = xbot - xtop + 1; + ybot = y; + while ((unsigned) ybot < table->subtableZ[ybot].next) + ybot = table->subtableZ[ybot].next; + ytop = y; + ysize = ybot - ytop + 1; + + /* Sift the variables of the second group up through the first group */ + for (i = 1; i <= ysize; i++) { + for (j = 1; j <= xsize; j++) { + size = cuddZddSwapInPlace(table,x,y); + if (size == 0) + return(0); + y = x; + x = cuddZddNextLow(table,y); + } + y = ytop + i; + x = cuddZddNextLow(table,y); + } + + /* fix groups */ + y = xtop; + for (i = 0; i < ysize - 1; i++) { + table->subtableZ[y].next = cuddZddNextHigh(table,y); + y = cuddZddNextHigh(table,y); + } + table->subtableZ[y].next = xtop; /* y is bottom of its group, join */ + /* to its top */ + x = cuddZddNextHigh(table,y); + newxtop = x; + for (i = 0; i < xsize - 1; i++) { + table->subtableZ[x].next = cuddZddNextHigh(table,x); + x = cuddZddNextHigh(table,x); + } + table->subtableZ[x].next = newxtop; /* x is bottom of its group, join */ + /* to its top */ +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"zddGroupMoveBackward:\n"); +#endif + + return(1); + +} /* end of zddGroupMoveBackward */ + + +/**Function******************************************************************** + + Synopsis [Determines the best position for a variables and returns + it there.] + + Description [Determines the best position for a variables and returns + it there. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +zddGroupSiftingBackward( + DdManager * table, + Move * moves, + int size) +{ + Move *move; + int res; + + + for (move = moves; move != NULL; move = move->next) { + if (move->size < size) { + size = move->size; + } + } + + for (move = moves; move != NULL; move = move->next) { + if (move->size == size) return(1); + if ((table->subtableZ[move->x].next == move->x) && + (table->subtableZ[move->y].next == move->y)) { + res = cuddZddSwapInPlace(table,(int)move->x,(int)move->y); + if (!res) return(0); +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"zddGroupSiftingBackward:\n"); + assert(table->subtableZ[move->x].next == move->x); + assert(table->subtableZ[move->y].next == move->y); +#endif + } else { /* Group move necessary */ + res = zddGroupMoveBackward(table,(int)move->x,(int)move->y); + if (!res) return(0); + } + } + + return(1); + +} /* end of zddGroupSiftingBackward */ + + +/**Function******************************************************************** + + Synopsis [Merges groups in the DD table.] + + Description [Creates a single group from low to high and adjusts the + idex field of the tree node.] + + SideEffects [None] + +******************************************************************************/ +static void +zddMergeGroups( + DdManager * table, + MtrNode * treenode, + int low, + int high) +{ + int i; + MtrNode *auxnode; + int saveindex; + int newindex; + + /* Merge all variables from low to high in one group, unless + ** this is the topmost group. In such a case we do not merge lest + ** we lose the symmetry information. */ + if (treenode != table->treeZ) { + for (i = low; i < high; i++) + table->subtableZ[i].next = i+1; + table->subtableZ[high].next = low; + } + + /* Adjust the index fields of the tree nodes. If a node is the + ** first child of its parent, then the parent may also need adjustment. */ + saveindex = treenode->index; + newindex = table->invpermZ[low]; + auxnode = treenode; + do { + auxnode->index = newindex; + if (auxnode->parent == NULL || + (int) auxnode->parent->index != saveindex) + break; + auxnode = auxnode->parent; + } while (1); + return; + +} /* end of zddMergeGroups */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddZddIsop.c b/abc_with_bb_support/src/bdd/cudd/cuddZddIsop.c new file mode 100644 index 000000000..1289f0db3 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddZddIsop.c @@ -0,0 +1,885 @@ +/**CFile*********************************************************************** + + FileName [cuddZddIsop.c] + + PackageName [cudd] + + Synopsis [Functions to find irredundant SOP covers as ZDDs from BDDs.] + + Description [External procedures included in this module: +
          +
        • Cudd_bddIsop() +
        • Cudd_zddIsop() +
        • Cudd_MakeBddFromZddCover() +
        + Internal procedures included in this module: +
          +
        • cuddBddIsop() +
        • cuddZddIsop() +
        • cuddMakeBddFromZddCover() +
        + Static procedures included in this module: +
          +
        + ] + + SeeAlso [] + + Author [In-Ho Moon] + + Copyright [ This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddZddIsop.c,v 1.1.1.1 2003/02/24 22:23:54 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticEnd***************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Computes an ISOP in ZDD form from BDDs.] + + Description [Computes an irredundant sum of products (ISOP) in ZDD + form from BDDs. The two BDDs L and U represent the lower bound and + the upper bound, respectively, of the function. The ISOP uses two + ZDD variables for each BDD variable: One for the positive literal, + and one for the negative literal. These two variables should be + adjacent in the ZDD order. The two ZDD variables corresponding to + BDD variable i should have indices 2i and + 2i+1. The result of this procedure depends on the + variable order. If successful, Cudd_zddIsop returns the BDD for + the function chosen from the interval. The ZDD representing the + irredundant cover is returned as a side effect in zdd_I. In case of + failure, NULL is returned.] + + SideEffects [zdd_I holds the pointer to the ZDD for the ISOP on + successful return.] + + SeeAlso [Cudd_bddIsop Cudd_zddVarsFromBddVars] + +******************************************************************************/ +DdNode * +Cudd_zddIsop( + DdManager * dd, + DdNode * L, + DdNode * U, + DdNode ** zdd_I) +{ + DdNode *res; + int autoDynZ; + + autoDynZ = dd->autoDynZ; + dd->autoDynZ = 0; + + do { + dd->reordered = 0; + res = cuddZddIsop(dd, L, U, zdd_I); + } while (dd->reordered == 1); + dd->autoDynZ = autoDynZ; + return(res); + +} /* end of Cudd_zddIsop */ + + +/**Function******************************************************************** + + Synopsis [Computes a BDD in the interval between L and U with a + simple sum-of-produuct cover.] + + Description [Computes a BDD in the interval between L and U with a + simple sum-of-produuct cover. This procedure is similar to + Cudd_zddIsop, but it does not return the ZDD for the cover. Returns + a pointer to the BDD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_zddIsop] + +******************************************************************************/ +DdNode * +Cudd_bddIsop( + DdManager * dd, + DdNode * L, + DdNode * U) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddBddIsop(dd, L, U); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_bddIsop */ + + +/**Function******************************************************************** + + Synopsis [Converts a ZDD cover to a BDD graph.] + + Description [Converts a ZDD cover to a BDD graph. If successful, it + returns a BDD node, otherwise it returns NULL.] + + SideEffects [] + + SeeAlso [cuddMakeBddFromZddCover] + +******************************************************************************/ +DdNode * +Cudd_MakeBddFromZddCover( + DdManager * dd, + DdNode * node) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddMakeBddFromZddCover(dd, node); + } while (dd->reordered == 1); + return(res); +} /* end of Cudd_MakeBddFromZddCover */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddIsop.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_zddIsop] + +******************************************************************************/ +DdNode * +cuddZddIsop( + DdManager * dd, + DdNode * L, + DdNode * U, + DdNode ** zdd_I) +{ + DdNode *one = DD_ONE(dd); + DdNode *zero = Cudd_Not(one); + DdNode *zdd_one = DD_ONE(dd); + DdNode *zdd_zero = DD_ZERO(dd); + int v, top_l, top_u; + DdNode *Lsub0, *Usub0, *Lsub1, *Usub1, *Ld, *Ud; + DdNode *Lsuper0, *Usuper0, *Lsuper1, *Usuper1; + DdNode *Isub0, *Isub1, *Id; + DdNode *zdd_Isub0, *zdd_Isub1, *zdd_Id; + DdNode *x; + DdNode *term0, *term1, *sum; + DdNode *Lv, *Uv, *Lnv, *Unv; + DdNode *r, *y, *z; + int index; + DdNode *(*cacheOp)(DdManager *, DdNode *, DdNode *); + + statLine(dd); + if (L == zero) { + *zdd_I = zdd_zero; + return(zero); + } + if (U == one) { + *zdd_I = zdd_one; + return(one); + } + + if (U == zero || L == one) { + printf("*** ERROR : illegal condition for ISOP (U < L).\n"); + exit(1); + } + + /* Check the cache. We store two results for each recursive call. + ** One is the BDD, and the other is the ZDD. Both are needed. + ** Hence we need a double hit in the cache to terminate the + ** recursion. Clearly, collisions may evict only one of the two + ** results. */ + cacheOp = (DdNode *(*)(DdManager *, DdNode *, DdNode *)) cuddZddIsop; + r = cuddCacheLookup2(dd, cuddBddIsop, L, U); + if (r) { + *zdd_I = cuddCacheLookup2Zdd(dd, cacheOp, L, U); + if (*zdd_I) + return(r); + else { + /* The BDD result may have been dead. In that case + ** cuddCacheLookup2 would have called cuddReclaim, + ** whose effects we now have to undo. */ + cuddRef(r); + Cudd_RecursiveDeref(dd, r); + } + } + + top_l = dd->perm[Cudd_Regular(L)->index]; + top_u = dd->perm[Cudd_Regular(U)->index]; + v = ddMin(top_l, top_u); + + /* Compute cofactors. */ + if (top_l == v) { + index = Cudd_Regular(L)->index; + Lv = Cudd_T(L); + Lnv = Cudd_E(L); + if (Cudd_IsComplement(L)) { + Lv = Cudd_Not(Lv); + Lnv = Cudd_Not(Lnv); + } + } + else { + index = Cudd_Regular(U)->index; + Lv = Lnv = L; + } + + if (top_u == v) { + Uv = Cudd_T(U); + Unv = Cudd_E(U); + if (Cudd_IsComplement(U)) { + Uv = Cudd_Not(Uv); + Unv = Cudd_Not(Unv); + } + } + else { + Uv = Unv = U; + } + + Lsub0 = cuddBddAndRecur(dd, Lnv, Cudd_Not(Uv)); + if (Lsub0 == NULL) + return(NULL); + Cudd_Ref(Lsub0); + Usub0 = Unv; + Lsub1 = cuddBddAndRecur(dd, Lv, Cudd_Not(Unv)); + if (Lsub1 == NULL) { + Cudd_RecursiveDeref(dd, Lsub0); + return(NULL); + } + Cudd_Ref(Lsub1); + Usub1 = Uv; + + Isub0 = cuddZddIsop(dd, Lsub0, Usub0, &zdd_Isub0); + if (Isub0 == NULL) { + Cudd_RecursiveDeref(dd, Lsub0); + Cudd_RecursiveDeref(dd, Lsub1); + return(NULL); + } + /* + if ((!cuddIsConstant(Cudd_Regular(Isub0))) && + (Cudd_Regular(Isub0)->index != zdd_Isub0->index / 2 || + dd->permZ[index * 2] > dd->permZ[zdd_Isub0->index])) { + printf("*** ERROR : illegal permutation in ZDD. ***\n"); + } + */ + Cudd_Ref(Isub0); + Cudd_Ref(zdd_Isub0); + Isub1 = cuddZddIsop(dd, Lsub1, Usub1, &zdd_Isub1); + if (Isub1 == NULL) { + Cudd_RecursiveDeref(dd, Lsub0); + Cudd_RecursiveDeref(dd, Lsub1); + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + return(NULL); + } + /* + if ((!cuddIsConstant(Cudd_Regular(Isub1))) && + (Cudd_Regular(Isub1)->index != zdd_Isub1->index / 2 || + dd->permZ[index * 2] > dd->permZ[zdd_Isub1->index])) { + printf("*** ERROR : illegal permutation in ZDD. ***\n"); + } + */ + Cudd_Ref(Isub1); + Cudd_Ref(zdd_Isub1); + Cudd_RecursiveDeref(dd, Lsub0); + Cudd_RecursiveDeref(dd, Lsub1); + + Lsuper0 = cuddBddAndRecur(dd, Lnv, Cudd_Not(Isub0)); + if (Lsuper0 == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + return(NULL); + } + Cudd_Ref(Lsuper0); + Lsuper1 = cuddBddAndRecur(dd, Lv, Cudd_Not(Isub1)); + if (Lsuper1 == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDeref(dd, Lsuper0); + return(NULL); + } + Cudd_Ref(Lsuper1); + Usuper0 = Unv; + Usuper1 = Uv; + + /* Ld = Lsuper0 + Lsuper1 */ + Ld = cuddBddAndRecur(dd, Cudd_Not(Lsuper0), Cudd_Not(Lsuper1)); + if (Ld == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDeref(dd, Lsuper0); + Cudd_RecursiveDeref(dd, Lsuper1); + return(NULL); + } + Ld = Cudd_Not(Ld); + Cudd_Ref(Ld); + /* Ud = Usuper0 * Usuper1 */ + Ud = cuddBddAndRecur(dd, Usuper0, Usuper1); + if (Ud == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDeref(dd, Lsuper0); + Cudd_RecursiveDeref(dd, Lsuper1); + Cudd_RecursiveDeref(dd, Ld); + return(NULL); + } + Cudd_Ref(Ud); + Cudd_RecursiveDeref(dd, Lsuper0); + Cudd_RecursiveDeref(dd, Lsuper1); + + Id = cuddZddIsop(dd, Ld, Ud, &zdd_Id); + if (Id == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDeref(dd, Ld); + Cudd_RecursiveDeref(dd, Ud); + return(NULL); + } + /* + if ((!cuddIsConstant(Cudd_Regular(Id))) && + (Cudd_Regular(Id)->index != zdd_Id->index / 2 || + dd->permZ[index * 2] > dd->permZ[zdd_Id->index])) { + printf("*** ERROR : illegal permutation in ZDD. ***\n"); + } + */ + Cudd_Ref(Id); + Cudd_Ref(zdd_Id); + Cudd_RecursiveDeref(dd, Ld); + Cudd_RecursiveDeref(dd, Ud); + + x = cuddUniqueInter(dd, index, one, zero); + if (x == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDeref(dd, Id); + Cudd_RecursiveDerefZdd(dd, zdd_Id); + return(NULL); + } + Cudd_Ref(x); + /* term0 = x * Isub0 */ + term0 = cuddBddAndRecur(dd, Cudd_Not(x), Isub0); + if (term0 == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDeref(dd, Id); + Cudd_RecursiveDerefZdd(dd, zdd_Id); + Cudd_RecursiveDeref(dd, x); + return(NULL); + } + Cudd_Ref(term0); + Cudd_RecursiveDeref(dd, Isub0); + /* term1 = x * Isub1 */ + term1 = cuddBddAndRecur(dd, x, Isub1); + if (term1 == NULL) { + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDeref(dd, Id); + Cudd_RecursiveDerefZdd(dd, zdd_Id); + Cudd_RecursiveDeref(dd, x); + Cudd_RecursiveDeref(dd, term0); + return(NULL); + } + Cudd_Ref(term1); + Cudd_RecursiveDeref(dd, x); + Cudd_RecursiveDeref(dd, Isub1); + /* sum = term0 + term1 */ + sum = cuddBddAndRecur(dd, Cudd_Not(term0), Cudd_Not(term1)); + if (sum == NULL) { + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDeref(dd, Id); + Cudd_RecursiveDerefZdd(dd, zdd_Id); + Cudd_RecursiveDeref(dd, term0); + Cudd_RecursiveDeref(dd, term1); + return(NULL); + } + sum = Cudd_Not(sum); + Cudd_Ref(sum); + Cudd_RecursiveDeref(dd, term0); + Cudd_RecursiveDeref(dd, term1); + /* r = sum + Id */ + r = cuddBddAndRecur(dd, Cudd_Not(sum), Cudd_Not(Id)); + r = Cudd_NotCond(r, r != NULL); + if (r == NULL) { + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDeref(dd, Id); + Cudd_RecursiveDerefZdd(dd, zdd_Id); + Cudd_RecursiveDeref(dd, sum); + return(NULL); + } + Cudd_Ref(r); + Cudd_RecursiveDeref(dd, sum); + Cudd_RecursiveDeref(dd, Id); + + if (zdd_Isub0 != zdd_zero) { + z = cuddZddGetNodeIVO(dd, index * 2 + 1, zdd_Isub0, zdd_Id); + if (z == NULL) { + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Id); + Cudd_RecursiveDeref(dd, r); + return(NULL); + } + } + else { + z = zdd_Id; + } + Cudd_Ref(z); + if (zdd_Isub1 != zdd_zero) { + y = cuddZddGetNodeIVO(dd, index * 2, zdd_Isub1, z); + if (y == NULL) { + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Id); + Cudd_RecursiveDeref(dd, r); + Cudd_RecursiveDerefZdd(dd, z); + return(NULL); + } + } + else + y = z; + Cudd_Ref(y); + + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Id); + Cudd_RecursiveDerefZdd(dd, z); + + cuddCacheInsert2(dd, cuddBddIsop, L, U, r); + cuddCacheInsert2(dd, cacheOp, L, U, y); + + Cudd_Deref(r); + Cudd_Deref(y); + *zdd_I = y; + /* + if (Cudd_Regular(r)->index != y->index / 2) { + printf("*** ERROR : mismatch in indices between BDD and ZDD. ***\n"); + } + */ + return(r); + +} /* end of cuddZddIsop */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_bddIsop.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_bddIsop] + +******************************************************************************/ +DdNode * +cuddBddIsop( + DdManager * dd, + DdNode * L, + DdNode * U) +{ + DdNode *one = DD_ONE(dd); + DdNode *zero = Cudd_Not(one); + int v, top_l, top_u; + DdNode *Lsub0, *Usub0, *Lsub1, *Usub1, *Ld, *Ud; + DdNode *Lsuper0, *Usuper0, *Lsuper1, *Usuper1; + DdNode *Isub0, *Isub1, *Id; + DdNode *x; + DdNode *term0, *term1, *sum; + DdNode *Lv, *Uv, *Lnv, *Unv; + DdNode *r; + int index; + + statLine(dd); + if (L == zero) + return(zero); + if (U == one) + return(one); + + /* Check cache */ + r = cuddCacheLookup2(dd, cuddBddIsop, L, U); + if (r) + return(r); + + top_l = dd->perm[Cudd_Regular(L)->index]; + top_u = dd->perm[Cudd_Regular(U)->index]; + v = ddMin(top_l, top_u); + + /* Compute cofactors */ + if (top_l == v) { + index = Cudd_Regular(L)->index; + Lv = Cudd_T(L); + Lnv = Cudd_E(L); + if (Cudd_IsComplement(L)) { + Lv = Cudd_Not(Lv); + Lnv = Cudd_Not(Lnv); + } + } + else { + index = Cudd_Regular(U)->index; + Lv = Lnv = L; + } + + if (top_u == v) { + Uv = Cudd_T(U); + Unv = Cudd_E(U); + if (Cudd_IsComplement(U)) { + Uv = Cudd_Not(Uv); + Unv = Cudd_Not(Unv); + } + } + else { + Uv = Unv = U; + } + + Lsub0 = cuddBddAndRecur(dd, Lnv, Cudd_Not(Uv)); + if (Lsub0 == NULL) + return(NULL); + Cudd_Ref(Lsub0); + Usub0 = Unv; + Lsub1 = cuddBddAndRecur(dd, Lv, Cudd_Not(Unv)); + if (Lsub1 == NULL) { + Cudd_RecursiveDeref(dd, Lsub0); + return(NULL); + } + Cudd_Ref(Lsub1); + Usub1 = Uv; + + Isub0 = cuddBddIsop(dd, Lsub0, Usub0); + if (Isub0 == NULL) { + Cudd_RecursiveDeref(dd, Lsub0); + Cudd_RecursiveDeref(dd, Lsub1); + return(NULL); + } + Cudd_Ref(Isub0); + Isub1 = cuddBddIsop(dd, Lsub1, Usub1); + if (Isub1 == NULL) { + Cudd_RecursiveDeref(dd, Lsub0); + Cudd_RecursiveDeref(dd, Lsub1); + Cudd_RecursiveDeref(dd, Isub0); + return(NULL); + } + Cudd_Ref(Isub1); + Cudd_RecursiveDeref(dd, Lsub0); + Cudd_RecursiveDeref(dd, Lsub1); + + Lsuper0 = cuddBddAndRecur(dd, Lnv, Cudd_Not(Isub0)); + if (Lsuper0 == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDeref(dd, Isub1); + return(NULL); + } + Cudd_Ref(Lsuper0); + Lsuper1 = cuddBddAndRecur(dd, Lv, Cudd_Not(Isub1)); + if (Lsuper1 == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDeref(dd, Lsuper0); + return(NULL); + } + Cudd_Ref(Lsuper1); + Usuper0 = Unv; + Usuper1 = Uv; + + /* Ld = Lsuper0 + Lsuper1 */ + Ld = cuddBddAndRecur(dd, Cudd_Not(Lsuper0), Cudd_Not(Lsuper1)); + Ld = Cudd_NotCond(Ld, Ld != NULL); + if (Ld == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDeref(dd, Lsuper0); + Cudd_RecursiveDeref(dd, Lsuper1); + return(NULL); + } + Cudd_Ref(Ld); + Ud = cuddBddAndRecur(dd, Usuper0, Usuper1); + if (Ud == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDeref(dd, Lsuper0); + Cudd_RecursiveDeref(dd, Lsuper1); + Cudd_RecursiveDeref(dd, Ld); + return(NULL); + } + Cudd_Ref(Ud); + Cudd_RecursiveDeref(dd, Lsuper0); + Cudd_RecursiveDeref(dd, Lsuper1); + + Id = cuddBddIsop(dd, Ld, Ud); + if (Id == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDeref(dd, Ld); + Cudd_RecursiveDeref(dd, Ud); + return(NULL); + } + Cudd_Ref(Id); + Cudd_RecursiveDeref(dd, Ld); + Cudd_RecursiveDeref(dd, Ud); + + x = cuddUniqueInter(dd, index, one, zero); + if (x == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDeref(dd, Id); + return(NULL); + } + Cudd_Ref(x); + term0 = cuddBddAndRecur(dd, Cudd_Not(x), Isub0); + if (term0 == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDeref(dd, Id); + Cudd_RecursiveDeref(dd, x); + return(NULL); + } + Cudd_Ref(term0); + Cudd_RecursiveDeref(dd, Isub0); + term1 = cuddBddAndRecur(dd, x, Isub1); + if (term1 == NULL) { + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDeref(dd, Id); + Cudd_RecursiveDeref(dd, x); + Cudd_RecursiveDeref(dd, term0); + return(NULL); + } + Cudd_Ref(term1); + Cudd_RecursiveDeref(dd, x); + Cudd_RecursiveDeref(dd, Isub1); + /* sum = term0 + term1 */ + sum = cuddBddAndRecur(dd, Cudd_Not(term0), Cudd_Not(term1)); + sum = Cudd_NotCond(sum, sum != NULL); + if (sum == NULL) { + Cudd_RecursiveDeref(dd, Id); + Cudd_RecursiveDeref(dd, term0); + Cudd_RecursiveDeref(dd, term1); + return(NULL); + } + Cudd_Ref(sum); + Cudd_RecursiveDeref(dd, term0); + Cudd_RecursiveDeref(dd, term1); + /* r = sum + Id */ + r = cuddBddAndRecur(dd, Cudd_Not(sum), Cudd_Not(Id)); + r = Cudd_NotCond(r, r != NULL); + if (r == NULL) { + Cudd_RecursiveDeref(dd, Id); + Cudd_RecursiveDeref(dd, sum); + return(NULL); + } + Cudd_Ref(r); + Cudd_RecursiveDeref(dd, sum); + Cudd_RecursiveDeref(dd, Id); + + cuddCacheInsert2(dd, cuddBddIsop, L, U, r); + + Cudd_Deref(r); + return(r); + +} /* end of cuddBddIsop */ + + +/**Function******************************************************************** + + Synopsis [Converts a ZDD cover to a BDD graph.] + + Description [Converts a ZDD cover to a BDD graph. If successful, it + returns a BDD node, otherwise it returns NULL. It is a recursive + algorithm as the following. First computes 3 cofactors of a ZDD cover; + f1, f0 and fd. Second, compute BDDs(b1, b0 and bd) of f1, f0 and fd. + Third, compute T=b1+bd and E=b0+bd. Fourth, compute ITE(v,T,E) where v + is the variable which has the index of the top node of the ZDD cover. + In this case, since the index of v can be larger than either one of T or + one of E, cuddUniqueInterIVO is called, here IVO stands for + independent variable ordering.] + + SideEffects [] + + SeeAlso [Cudd_MakeBddFromZddCover] + +******************************************************************************/ +DdNode * +cuddMakeBddFromZddCover( + DdManager * dd, + DdNode * node) +{ + DdNode *neW; + int v; + DdNode *f1, *f0, *fd; + DdNode *b1, *b0, *bd; + DdNode *T, *E; + + statLine(dd); + if (node == dd->one) + return(dd->one); + if (node == dd->zero) + return(Cudd_Not(dd->one)); + + /* Check cache */ + neW = cuddCacheLookup1(dd, cuddMakeBddFromZddCover, node); + if (neW) + return(neW); + + v = Cudd_Regular(node)->index; /* either yi or zi */ + cuddZddGetCofactors3(dd, node, v, &f1, &f0, &fd); + Cudd_Ref(f1); + Cudd_Ref(f0); + Cudd_Ref(fd); + + b1 = cuddMakeBddFromZddCover(dd, f1); + if (!b1) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + return(NULL); + } + Cudd_Ref(b1); + b0 = cuddMakeBddFromZddCover(dd, f0); + if (!b1) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDeref(dd, b1); + return(NULL); + } + Cudd_Ref(b0); + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + if (fd != dd->zero) { + bd = cuddMakeBddFromZddCover(dd, fd); + if (!bd) { + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDeref(dd, b1); + Cudd_RecursiveDeref(dd, b0); + return(NULL); + } + Cudd_Ref(bd); + Cudd_RecursiveDerefZdd(dd, fd); + + T = cuddBddAndRecur(dd, Cudd_Not(b1), Cudd_Not(bd)); + if (!T) { + Cudd_RecursiveDeref(dd, b1); + Cudd_RecursiveDeref(dd, b0); + Cudd_RecursiveDeref(dd, bd); + return(NULL); + } + T = Cudd_NotCond(T, T != NULL); + Cudd_Ref(T); + Cudd_RecursiveDeref(dd, b1); + E = cuddBddAndRecur(dd, Cudd_Not(b0), Cudd_Not(bd)); + if (!E) { + Cudd_RecursiveDeref(dd, b0); + Cudd_RecursiveDeref(dd, bd); + Cudd_RecursiveDeref(dd, T); + return(NULL); + } + E = Cudd_NotCond(E, E != NULL); + Cudd_Ref(E); + Cudd_RecursiveDeref(dd, b0); + Cudd_RecursiveDeref(dd, bd); + } + else { + Cudd_RecursiveDerefZdd(dd, fd); + T = b1; + E = b0; + } + + if (Cudd_IsComplement(T)) { + neW = cuddUniqueInterIVO(dd, v / 2, Cudd_Not(T), Cudd_Not(E)); + if (!neW) { + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + return(NULL); + } + neW = Cudd_Not(neW); + } + else { + neW = cuddUniqueInterIVO(dd, v / 2, T, E); + if (!neW) { + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + return(NULL); + } + } + Cudd_Ref(neW); + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + + cuddCacheInsert1(dd, cuddMakeBddFromZddCover, node, neW); + Cudd_Deref(neW); + return(neW); + +} /* end of cuddMakeBddFromZddCover */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddZddLin.c b/abc_with_bb_support/src/bdd/cudd/cuddZddLin.c new file mode 100644 index 000000000..7a41a1d93 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddZddLin.c @@ -0,0 +1,939 @@ +/**CFile*********************************************************************** + + FileName [cuddZddLin.c] + + PackageName [cudd] + + Synopsis [Procedures for dynamic variable ordering of ZDDs.] + + Description [Internal procedures included in this module: +
          +
        • cuddZddLinearSifting() +
        + Static procedures included in this module: +
          +
        • cuddZddLinearInPlace() +
        • cuddZddLinerAux() +
        • cuddZddLinearUp() +
        • cuddZddLinearDown() +
        • cuddZddLinearBackward() +
        • cuddZddUndoMoves() +
        + ] + + SeeAlso [cuddLinear.c cuddZddReord.c] + + Author [Fabio Somenzi] + + Copyright [ This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define CUDD_SWAP_MOVE 0 +#define CUDD_LINEAR_TRANSFORM_MOVE 1 +#define CUDD_INVERSE_TRANSFORM_MOVE 2 + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddZddLin.c,v 1.1.1.1 2003/02/24 22:23:54 wjiang Exp $"; +#endif + +extern int *zdd_entry; +extern int zddTotalNumberSwapping; +static int zddTotalNumberLinearTr; +static DdNode *empty; + + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int cuddZddLinearAux ARGS((DdManager *table, int x, int xLow, int xHigh)); +static Move * cuddZddLinearUp ARGS((DdManager *table, int y, int xLow, Move *prevMoves)); +static Move * cuddZddLinearDown ARGS((DdManager *table, int x, int xHigh, Move *prevMoves)); +static int cuddZddLinearBackward ARGS((DdManager *table, int size, Move *moves)); +static Move* cuddZddUndoMoves ARGS((DdManager *table, Move *moves)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + + + +/**Function******************************************************************** + + Synopsis [Implementation of the linear sifting algorithm for ZDDs.] + + Description [Implementation of the linear sifting algorithm for ZDDs. + Assumes that no dead nodes are present. +
          +
        1. Order all the variables according to the number of entries + in each unique table. +
        2. Sift the variable up and down and applies the XOR transformation, + remembering each time the total size of the DD heap. +
        3. Select the best permutation. +
        4. Repeat 3 and 4 for all variables. +
        + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddLinearSifting( + DdManager * table, + int lower, + int upper) +{ + int i; + int *var; + int size; + int x; + int result; +#ifdef DD_STATS + int previousSize; +#endif + + size = table->sizeZ; + empty = table->zero; + + /* Find order in which to sift variables. */ + var = NULL; + zdd_entry = ALLOC(int, size); + if (zdd_entry == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddZddSiftingOutOfMem; + } + var = ALLOC(int, size); + if (var == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddZddSiftingOutOfMem; + } + + for (i = 0; i < size; i++) { + x = table->permZ[i]; + zdd_entry[i] = table->subtableZ[x].keys; + var[i] = i; + } + + qsort((void *)var, size, sizeof(int), (int (*)(const void *, const void *))cuddZddUniqueCompare); + + /* Now sift. */ + for (i = 0; i < ddMin(table->siftMaxVar, size); i++) { + if (zddTotalNumberSwapping >= table->siftMaxSwap) + break; + x = table->permZ[var[i]]; + if (x < lower || x > upper) continue; +#ifdef DD_STATS + previousSize = table->keysZ; +#endif + result = cuddZddLinearAux(table, x, lower, upper); + if (!result) + goto cuddZddSiftingOutOfMem; +#ifdef DD_STATS + if (table->keysZ < (unsigned) previousSize) { + (void) fprintf(table->out,"-"); + } else if (table->keysZ > (unsigned) previousSize) { + (void) fprintf(table->out,"+"); /* should never happen */ + (void) fprintf(table->out,"\nSize increased from %d to %d while sifting variable %d\n", previousSize, table->keysZ , var[i]); + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + + FREE(var); + FREE(zdd_entry); + + return(1); + +cuddZddSiftingOutOfMem: + + if (zdd_entry != NULL) FREE(zdd_entry); + if (var != NULL) FREE(var); + + return(0); + +} /* end of cuddZddLinearSifting */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Linearly combines two adjacent variables.] + + Description [Linearly combines two adjacent variables. It assumes + that no dead nodes are present on entry to this procedure. The + procedure then guarantees that no dead nodes will be present when it + terminates. cuddZddLinearInPlace assumes that x < y. Returns the + number of keys in the table if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [cuddZddSwapInPlace cuddLinearInPlace] + +******************************************************************************/ +int +cuddZddLinearInPlace( + DdManager * table, + int x, + int y) +{ + DdNodePtr *xlist, *ylist; + int xindex, yindex; + int xslots, yslots; + int xshift, yshift; + int oldxkeys, oldykeys; + int newxkeys, newykeys; + int i; + int posn; + DdNode *f, *f1, *f0, *f11, *f10, *f01, *f00; + DdNode *newf1, *newf0, *g, *next, *previous; + DdNode *special; + +#ifdef DD_DEBUG + assert(x < y); + assert(cuddZddNextHigh(table,x) == y); + assert(table->subtableZ[x].keys != 0); + assert(table->subtableZ[y].keys != 0); + assert(table->subtableZ[x].dead == 0); + assert(table->subtableZ[y].dead == 0); +#endif + + zddTotalNumberLinearTr++; + + /* Get parameters of x subtable. */ + xindex = table->invpermZ[x]; + xlist = table->subtableZ[x].nodelist; + oldxkeys = table->subtableZ[x].keys; + xslots = table->subtableZ[x].slots; + xshift = table->subtableZ[x].shift; + newxkeys = 0; + + /* Get parameters of y subtable. */ + yindex = table->invpermZ[y]; + ylist = table->subtableZ[y].nodelist; + oldykeys = table->subtableZ[y].keys; + yslots = table->subtableZ[y].slots; + yshift = table->subtableZ[y].shift; + newykeys = oldykeys; + + /* The nodes in the x layer are put in two chains. The chain + ** pointed by g holds the normal nodes. When re-expressed they stay + ** in the x list. The chain pointed by special holds the elements + ** that will move to the y list. + */ + g = special = NULL; + for (i = 0; i < xslots; i++) { + f = xlist[i]; + if (f == NULL) continue; + xlist[i] = NULL; + while (f != NULL) { + next = f->next; + f1 = cuddT(f); + /* if (f1->index == yindex) */ cuddSatDec(f1->ref); + f0 = cuddE(f); + /* if (f0->index == yindex) */ cuddSatDec(f0->ref); + if ((int) f1->index == yindex && cuddE(f1) == empty && + (int) f0->index != yindex) { + f->next = special; + special = f; + } else { + f->next = g; + g = f; + } + f = next; + } /* while there are elements in the collision chain */ + } /* for each slot of the x subtable */ + + /* Mark y nodes with pointers from above x. We mark them by + ** changing their index to x. + */ + for (i = 0; i < yslots; i++) { + f = ylist[i]; + while (f != NULL) { + if (f->ref != 0) { + f->index = xindex; + } + f = f->next; + } /* while there are elements in the collision chain */ + } /* for each slot of the y subtable */ + + /* Move special nodes to the y list. */ + f = special; + while (f != NULL) { + next = f->next; + f1 = cuddT(f); + f11 = cuddT(f1); + cuddT(f) = f11; + cuddSatInc(f11->ref); + f0 = cuddE(f); + cuddSatInc(f0->ref); + f->index = yindex; + /* Insert at the beginning of the list so that it will be + ** found first if there is a duplicate. The duplicate will + ** eventually be moved or garbage collected. No node + ** re-expression will add a pointer to it. + */ + posn = ddHash(f11, f0, yshift); + f->next = ylist[posn]; + ylist[posn] = f; + newykeys++; + f = next; + } + + /* Take care of the remaining x nodes that must be re-expressed. + ** They form a linked list pointed by g. + */ + f = g; + while (f != NULL) { +#ifdef DD_COUNT + table->swapSteps++; +#endif + next = f->next; + /* Find f1, f0, f11, f10, f01, f00. */ + f1 = cuddT(f); + if ((int) f1->index == yindex || (int) f1->index == xindex) { + f11 = cuddT(f1); f10 = cuddE(f1); + } else { + f11 = empty; f10 = f1; + } + f0 = cuddE(f); + if ((int) f0->index == yindex || (int) f0->index == xindex) { + f01 = cuddT(f0); f00 = cuddE(f0); + } else { + f01 = empty; f00 = f0; + } + /* Create the new T child. */ + if (f01 == empty) { + newf1 = f10; + cuddSatInc(newf1->ref); + } else { + /* Check ylist for triple (yindex, f01, f10). */ + posn = ddHash(f01, f10, yshift); + /* For each element newf1 in collision list ylist[posn]. */ + newf1 = ylist[posn]; + /* Search the collision chain skipping the marked nodes. */ + while (newf1 != NULL) { + if (cuddT(newf1) == f01 && cuddE(newf1) == f10 && + (int) newf1->index == yindex) { + cuddSatInc(newf1->ref); + break; /* match */ + } + newf1 = newf1->next; + } /* while newf1 */ + if (newf1 == NULL) { /* no match */ + newf1 = cuddDynamicAllocNode(table); + if (newf1 == NULL) + goto zddSwapOutOfMem; + newf1->index = yindex; newf1->ref = 1; + cuddT(newf1) = f01; + cuddE(newf1) = f10; + /* Insert newf1 in the collision list ylist[pos]; + ** increase the ref counts of f01 and f10 + */ + newykeys++; + newf1->next = ylist[posn]; + ylist[posn] = newf1; + cuddSatInc(f01->ref); + cuddSatInc(f10->ref); + } + } + cuddT(f) = newf1; + + /* Do the same for f0. */ + /* Create the new E child. */ + if (f11 == empty) { + newf0 = f00; + cuddSatInc(newf0->ref); + } else { + /* Check ylist for triple (yindex, f11, f00). */ + posn = ddHash(f11, f00, yshift); + /* For each element newf0 in collision list ylist[posn]. */ + newf0 = ylist[posn]; + while (newf0 != NULL) { + if (cuddT(newf0) == f11 && cuddE(newf0) == f00 && + (int) newf0->index == yindex) { + cuddSatInc(newf0->ref); + break; /* match */ + } + newf0 = newf0->next; + } /* while newf0 */ + if (newf0 == NULL) { /* no match */ + newf0 = cuddDynamicAllocNode(table); + if (newf0 == NULL) + goto zddSwapOutOfMem; + newf0->index = yindex; newf0->ref = 1; + cuddT(newf0) = f11; cuddE(newf0) = f00; + /* Insert newf0 in the collision list ylist[posn]; + ** increase the ref counts of f11 and f00. + */ + newykeys++; + newf0->next = ylist[posn]; + ylist[posn] = newf0; + cuddSatInc(f11->ref); + cuddSatInc(f00->ref); + } + } + cuddE(f) = newf0; + + /* Re-insert the modified f in xlist. + ** The modified f does not already exists in xlist. + ** (Because of the uniqueness of the cofactors.) + */ + posn = ddHash(newf1, newf0, xshift); + newxkeys++; + f->next = xlist[posn]; + xlist[posn] = f; + f = next; + } /* while f != NULL */ + + /* GC the y layer and move the marked nodes to the x list. */ + + /* For each node f in ylist. */ + for (i = 0; i < yslots; i++) { + previous = NULL; + f = ylist[i]; + while (f != NULL) { + next = f->next; + if (f->ref == 0) { + cuddSatDec(cuddT(f)->ref); + cuddSatDec(cuddE(f)->ref); + cuddDeallocNode(table, f); + newykeys--; + if (previous == NULL) + ylist[i] = next; + else + previous->next = next; + } else if ((int) f->index == xindex) { /* move marked node */ + if (previous == NULL) + ylist[i] = next; + else + previous->next = next; + f1 = cuddT(f); + cuddSatDec(f1->ref); + /* Check ylist for triple (yindex, f1, empty). */ + posn = ddHash(f1, empty, yshift); + /* For each element newf1 in collision list ylist[posn]. */ + newf1 = ylist[posn]; + while (newf1 != NULL) { + if (cuddT(newf1) == f1 && cuddE(newf1) == empty && + (int) newf1->index == yindex) { + cuddSatInc(newf1->ref); + break; /* match */ + } + newf1 = newf1->next; + } /* while newf1 */ + if (newf1 == NULL) { /* no match */ + newf1 = cuddDynamicAllocNode(table); + if (newf1 == NULL) + goto zddSwapOutOfMem; + newf1->index = yindex; newf1->ref = 1; + cuddT(newf1) = f1; cuddE(newf1) = empty; + /* Insert newf1 in the collision list ylist[posn]; + ** increase the ref counts of f1 and empty. + */ + newykeys++; + newf1->next = ylist[posn]; + ylist[posn] = newf1; + if (posn == i && previous == NULL) + previous = newf1; + cuddSatInc(f1->ref); + cuddSatInc(empty->ref); + } + cuddT(f) = newf1; + f0 = cuddE(f); + /* Insert f in x list. */ + posn = ddHash(newf1, f0, xshift); + newxkeys++; + newykeys--; + f->next = xlist[posn]; + xlist[posn] = f; + } else { + previous = f; + } + f = next; + } /* while f */ + } /* for i */ + + /* Set the appropriate fields in table. */ + table->subtableZ[x].keys = newxkeys; + table->subtableZ[y].keys = newykeys; + + table->keysZ += newxkeys + newykeys - oldxkeys - oldykeys; + + /* Update univ section; univ[x] remains the same. */ + table->univ[y] = cuddT(table->univ[x]); + +#if 0 + (void) fprintf(table->out,"x = %d y = %d\n", x, y); + (void) Cudd_DebugCheck(table); + (void) Cudd_CheckKeys(table); +#endif + + return (table->keysZ); + +zddSwapOutOfMem: + (void) fprintf(table->err, "Error: cuddZddSwapInPlace out of memory\n"); + + return (0); + +} /* end of cuddZddLinearInPlace */ + + +/**Function******************************************************************** + + Synopsis [Given xLow <= x <= xHigh moves x up and down between the + boundaries.] + + Description [Given xLow <= x <= xHigh moves x up and down between the + boundaries. Finds the best position and does the required changes. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddZddLinearAux( + DdManager * table, + int x, + int xLow, + int xHigh) +{ + Move *move; + Move *moveUp; /* list of up move */ + Move *moveDown; /* list of down move */ + + int initial_size; + int result; + + initial_size = table->keysZ; + +#ifdef DD_DEBUG + assert(table->subtableZ[x].keys > 0); +#endif + + moveDown = NULL; + moveUp = NULL; + + if (x == xLow) { + moveDown = cuddZddLinearDown(table, x, xHigh, NULL); + /* At this point x --> xHigh. */ + if (moveDown == (Move *) CUDD_OUT_OF_MEM) + goto cuddZddLinearAuxOutOfMem; + /* Move backward and stop at best position. */ + result = cuddZddLinearBackward(table, initial_size, moveDown); + if (!result) + goto cuddZddLinearAuxOutOfMem; + + } else if (x == xHigh) { + moveUp = cuddZddLinearUp(table, x, xLow, NULL); + /* At this point x --> xLow. */ + if (moveUp == (Move *) CUDD_OUT_OF_MEM) + goto cuddZddLinearAuxOutOfMem; + /* Move backward and stop at best position. */ + result = cuddZddLinearBackward(table, initial_size, moveUp); + if (!result) + goto cuddZddLinearAuxOutOfMem; + + } else if ((x - xLow) > (xHigh - x)) { /* must go down first: shorter */ + moveDown = cuddZddLinearDown(table, x, xHigh, NULL); + /* At this point x --> xHigh. */ + if (moveDown == (Move *) CUDD_OUT_OF_MEM) + goto cuddZddLinearAuxOutOfMem; + moveUp = cuddZddUndoMoves(table,moveDown); +#ifdef DD_DEBUG + assert(moveUp == NULL || moveUp->x == x); +#endif + moveUp = cuddZddLinearUp(table, x, xLow, moveUp); + if (moveUp == (Move *) CUDD_OUT_OF_MEM) + goto cuddZddLinearAuxOutOfMem; + /* Move backward and stop at best position. */ + result = cuddZddLinearBackward(table, initial_size, moveUp); + if (!result) + goto cuddZddLinearAuxOutOfMem; + + } else { + moveUp = cuddZddLinearUp(table, x, xLow, NULL); + /* At this point x --> xHigh. */ + if (moveUp == (Move *) CUDD_OUT_OF_MEM) + goto cuddZddLinearAuxOutOfMem; + /* Then move up. */ + moveDown = cuddZddUndoMoves(table,moveUp); +#ifdef DD_DEBUG + assert(moveDown == NULL || moveDown->y == x); +#endif + moveDown = cuddZddLinearDown(table, x, xHigh, moveDown); + if (moveDown == (Move *) CUDD_OUT_OF_MEM) + goto cuddZddLinearAuxOutOfMem; + /* Move backward and stop at best position. */ + result = cuddZddLinearBackward(table, initial_size, moveDown); + if (!result) + goto cuddZddLinearAuxOutOfMem; + } + + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocNode(table, (DdNode *)moveDown); + moveDown = move; + } + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocNode(table, (DdNode *)moveUp); + moveUp = move; + } + + return(1); + +cuddZddLinearAuxOutOfMem: + if (moveDown != (Move *) CUDD_OUT_OF_MEM) { + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocNode(table, (DdNode *)moveDown); + moveDown = move; + } + } + if (moveUp != (Move *) CUDD_OUT_OF_MEM) { + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocNode(table, (DdNode *)moveUp); + moveUp = move; + } + } + + return(0); + +} /* end of cuddZddLinearAux */ + + +/**Function******************************************************************** + + Synopsis [Sifts a variable up applying the XOR transformation.] + + Description [Sifts a variable up applying the XOR + transformation. Moves y up until either it reaches the bound (xLow) + or the size of the ZDD heap increases too much. Returns the set of + moves in case of success; NULL if memory is full.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static Move * +cuddZddLinearUp( + DdManager * table, + int y, + int xLow, + Move * prevMoves) +{ + Move *moves; + Move *move; + int x; + int size, newsize; + int limitSize; + + moves = prevMoves; + limitSize = table->keysZ; + + x = cuddZddNextLow(table, y); + while (x >= xLow) { + size = cuddZddSwapInPlace(table, x, y); + if (size == 0) + goto cuddZddLinearUpOutOfMem; + newsize = cuddZddLinearInPlace(table, x, y); + if (newsize == 0) + goto cuddZddLinearUpOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) + goto cuddZddLinearUpOutOfMem; + move->x = x; + move->y = y; + move->next = moves; + moves = move; + move->flags = CUDD_SWAP_MOVE; + if (newsize > size) { + /* Undo transformation. The transformation we apply is + ** its own inverse. Hence, we just apply the transformation + ** again. + */ + newsize = cuddZddLinearInPlace(table,x,y); + if (newsize == 0) goto cuddZddLinearUpOutOfMem; +#ifdef DD_DEBUG + if (newsize != size) { + (void) fprintf(table->err,"Change in size after identity transformation! From %d to %d\n",size,newsize); + } +#endif + } else { + size = newsize; + move->flags = CUDD_LINEAR_TRANSFORM_MOVE; + } + move->size = size; + + if ((double)size > (double)limitSize * table->maxGrowth) + break; + if (size < limitSize) + limitSize = size; + + y = x; + x = cuddZddNextLow(table, y); + } + return(moves); + +cuddZddLinearUpOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *)moves); + moves = move; + } + return((Move *) CUDD_OUT_OF_MEM); + +} /* end of cuddZddLinearUp */ + + +/**Function******************************************************************** + + Synopsis [Sifts a variable down and applies the XOR transformation.] + + Description [Sifts a variable down. Moves x down until either it + reaches the bound (xHigh) or the size of the ZDD heap increases too + much. Returns the set of moves in case of success; NULL if memory is + full.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static Move * +cuddZddLinearDown( + DdManager * table, + int x, + int xHigh, + Move * prevMoves) +{ + Move *moves; + Move *move; + int y; + int size, newsize; + int limitSize; + + moves = prevMoves; + limitSize = table->keysZ; + + y = cuddZddNextHigh(table, x); + while (y <= xHigh) { + size = cuddZddSwapInPlace(table, x, y); + if (size == 0) + goto cuddZddLinearDownOutOfMem; + newsize = cuddZddLinearInPlace(table, x, y); + if (newsize == 0) + goto cuddZddLinearDownOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) + goto cuddZddLinearDownOutOfMem; + move->x = x; + move->y = y; + move->next = moves; + moves = move; + move->flags = CUDD_SWAP_MOVE; + if (newsize > size) { + /* Undo transformation. The transformation we apply is + ** its own inverse. Hence, we just apply the transformation + ** again. + */ + newsize = cuddZddLinearInPlace(table,x,y); + if (newsize == 0) goto cuddZddLinearDownOutOfMem; + if (newsize != size) { + (void) fprintf(table->err,"Change in size after identity transformation! From %d to %d\n",size,newsize); + } + } else { + size = newsize; + move->flags = CUDD_LINEAR_TRANSFORM_MOVE; + } + move->size = size; + + if ((double)size > (double)limitSize * table->maxGrowth) + break; + if (size < limitSize) + limitSize = size; + + x = y; + y = cuddZddNextHigh(table, x); + } + return(moves); + +cuddZddLinearDownOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *)moves); + moves = move; + } + return((Move *) CUDD_OUT_OF_MEM); + +} /* end of cuddZddLinearDown */ + + +/**Function******************************************************************** + + Synopsis [Given a set of moves, returns the ZDD heap to the position + giving the minimum size.] + + Description [Given a set of moves, returns the ZDD heap to the + position giving the minimum size. In case of ties, returns to the + closest position giving the minimum size. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddZddLinearBackward( + DdManager * table, + int size, + Move * moves) +{ + Move *move; + int res; + + /* Find the minimum size among moves. */ + for (move = moves; move != NULL; move = move->next) { + if (move->size < size) { + size = move->size; + } + } + + for (move = moves; move != NULL; move = move->next) { + if (move->size == size) return(1); + if (move->flags == CUDD_LINEAR_TRANSFORM_MOVE) { + res = cuddZddLinearInPlace(table,(int)move->x,(int)move->y); + if (!res) return(0); + } + res = cuddZddSwapInPlace(table, move->x, move->y); + if (!res) + return(0); + if (move->flags == CUDD_INVERSE_TRANSFORM_MOVE) { + res = cuddZddLinearInPlace(table,(int)move->x,(int)move->y); + if (!res) return(0); + } + } + + return(1); + +} /* end of cuddZddLinearBackward */ + + +/**Function******************************************************************** + + Synopsis [Given a set of moves, returns the ZDD heap to the order + in effect before the moves.] + + Description [Given a set of moves, returns the ZDD heap to the + order in effect before the moves. Returns 1 in case of success; + 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static Move* +cuddZddUndoMoves( + DdManager * table, + Move * moves) +{ + Move *invmoves = NULL; + Move *move; + Move *invmove; + int size; + + for (move = moves; move != NULL; move = move->next) { + invmove = (Move *) cuddDynamicAllocNode(table); + if (invmove == NULL) goto cuddZddUndoMovesOutOfMem; + invmove->x = move->x; + invmove->y = move->y; + invmove->next = invmoves; + invmoves = invmove; + if (move->flags == CUDD_SWAP_MOVE) { + invmove->flags = CUDD_SWAP_MOVE; + size = cuddZddSwapInPlace(table,(int)move->x,(int)move->y); + if (!size) goto cuddZddUndoMovesOutOfMem; + } else if (move->flags == CUDD_LINEAR_TRANSFORM_MOVE) { + invmove->flags = CUDD_INVERSE_TRANSFORM_MOVE; + size = cuddZddLinearInPlace(table,(int)move->x,(int)move->y); + if (!size) goto cuddZddUndoMovesOutOfMem; + size = cuddZddSwapInPlace(table,(int)move->x,(int)move->y); + if (!size) goto cuddZddUndoMovesOutOfMem; + } else { /* must be CUDD_INVERSE_TRANSFORM_MOVE */ +#ifdef DD_DEBUG + (void) fprintf(table->err,"Unforseen event in ddUndoMoves!\n"); +#endif + invmove->flags = CUDD_LINEAR_TRANSFORM_MOVE; + size = cuddZddSwapInPlace(table,(int)move->x,(int)move->y); + if (!size) goto cuddZddUndoMovesOutOfMem; + size = cuddZddLinearInPlace(table,(int)move->x,(int)move->y); + if (!size) goto cuddZddUndoMovesOutOfMem; + } + invmove->size = size; + } + + return(invmoves); + +cuddZddUndoMovesOutOfMem: + while (invmoves != NULL) { + move = invmoves->next; + cuddDeallocNode(table, (DdNode *) invmoves); + invmoves = move; + } + return((Move *) CUDD_OUT_OF_MEM); + +} /* end of cuddZddUndoMoves */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddZddMisc.c b/abc_with_bb_support/src/bdd/cudd/cuddZddMisc.c new file mode 100644 index 000000000..8076f65d8 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddZddMisc.c @@ -0,0 +1,252 @@ +/**CFile*********************************************************************** + + FileName [cuddZddMisc.c] + + PackageName [cudd] + + Synopsis [.] + + Description [External procedures included in this module: +
          +
        • Cudd_zddDagSize() +
        • Cudd_zddCountMinterm() +
        • Cudd_zddPrintSubtable() +
        + Internal procedures included in this module: +
          +
        + Static procedures included in this module: +
          +
        • cuddZddDagInt() +
        + ] + + SeeAlso [] + + Author [Hyong-Kyoon Shin, In-Ho Moon] + + Copyright [ This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddZddMisc.c,v 1.1.1.1 2003/02/24 22:23:54 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int cuddZddDagInt ARGS((DdNode *n, st_table *tab)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Counts the number of nodes in a ZDD.] + + Description [Counts the number of nodes in a ZDD. This function + duplicates Cudd_DagSize and is only retained for compatibility.] + + SideEffects [None] + + SeeAlso [Cudd_DagSize] + +******************************************************************************/ +int +Cudd_zddDagSize( + DdNode * p_node) +{ + + int i; + st_table *table; + + table = st_init_table(st_ptrcmp, st_ptrhash); + i = cuddZddDagInt(p_node, table); + st_free_table(table); + return(i); + +} /* end of Cudd_zddDagSize */ + + +/**Function******************************************************************** + + Synopsis [Counts the number of minterms of a ZDD.] + + Description [Counts the number of minterms of the ZDD rooted at + node. This procedure takes a parameter + path that specifies how many variables are in the + support of the function. If the procedure runs out of memory, it + returns (double) CUDD_OUT_OF_MEM.] + + SideEffects [None] + + SeeAlso [Cudd_zddCountDouble] + +******************************************************************************/ +double +Cudd_zddCountMinterm( + DdManager * zdd, + DdNode * node, + int path) +{ + double dc_var, minterms; + + dc_var = (double)((double)(zdd->sizeZ) - (double)path); + minterms = Cudd_zddCountDouble(zdd, node) / pow(2.0, dc_var); + return(minterms); + +} /* end of Cudd_zddCountMinterm */ + + +/**Function******************************************************************** + + Synopsis [Prints the ZDD table.] + + Description [Prints the ZDD table for debugging purposes.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +Cudd_zddPrintSubtable( + DdManager * table) +{ + int i, j; + DdNode *z1, *z1_next, *base; + DdSubtable *ZSubTable; + + base = table->one; + for (i = table->sizeZ - 1; i >= 0; i--) { + ZSubTable = &(table->subtableZ[i]); + printf("subtable[%d]:\n", i); + for (j = ZSubTable->slots - 1; j >= 0; j--) { + z1 = ZSubTable->nodelist[j]; + while (z1 != NIL(DdNode)) { + (void) fprintf(table->out, +#if SIZEOF_VOID_P == 8 + "ID = 0x%lx\tindex = %d\tr = %d\t", + (unsigned long) z1 / (unsigned long) sizeof(DdNode), + z1->index, z1->ref); +#else + "ID = 0x%x\tindex = %d\tr = %d\t", + (unsigned) z1 / (unsigned) sizeof(DdNode), + z1->index, z1->ref); +#endif + z1_next = cuddT(z1); + if (Cudd_IsConstant(z1_next)) { + (void) fprintf(table->out, "T = %d\t\t", + (z1_next == base)); + } + else { +#if SIZEOF_VOID_P == 8 + (void) fprintf(table->out, "T = 0x%lx\t", + (unsigned long) z1_next / (unsigned long) sizeof(DdNode)); +#else + (void) fprintf(table->out, "T = 0x%x\t", + (unsigned) z1_next / (unsigned) sizeof(DdNode)); +#endif + } + z1_next = cuddE(z1); + if (Cudd_IsConstant(z1_next)) { + (void) fprintf(table->out, "E = %d\n", + (z1_next == base)); + } + else { +#if SIZEOF_VOID_P == 8 + (void) fprintf(table->out, "E = 0x%lx\n", + (unsigned long) z1_next / (unsigned long) sizeof(DdNode)); +#else + (void) fprintf(table->out, "E = 0x%x\n", + (unsigned) z1_next / (unsigned) sizeof(DdNode)); +#endif + } + + z1_next = z1->next; + z1 = z1_next; + } + } + } + putchar('\n'); + +} /* Cudd_zddPrintSubtable */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddDagSize.] + + Description [Performs the recursive step of Cudd_zddDagSize. Does + not check for out-of-memory conditions.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddZddDagInt( + DdNode * n, + st_table * tab) +{ + if (n == NIL(DdNode)) + return(0); + + if (st_is_member(tab, (char *)n) == 1) + return(0); + + if (Cudd_IsConstant(n)) + return(0); + + (void)st_insert(tab, (char *)n, NIL(char)); + return(1 + cuddZddDagInt(cuddT(n), tab) + + cuddZddDagInt(cuddE(n), tab)); + +} /* cuddZddDagInt */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddZddPort.c b/abc_with_bb_support/src/bdd/cudd/cuddZddPort.c new file mode 100644 index 000000000..2a16d8388 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddZddPort.c @@ -0,0 +1,354 @@ +/**CFile*********************************************************************** + + FileName [cuddZddPort.c] + + PackageName [cudd] + + Synopsis [Functions that translate BDDs to ZDDs.] + + Description [External procedures included in this module: +
          +
        • Cudd_zddPortFromBdd() +
        • Cudd_zddPortToBdd() +
        + Internal procedures included in this module: +
          +
        + Static procedures included in this module: +
          +
        • zddPortFromBddStep() +
        • zddPortToBddStep() +
        + ] + + SeeAlso [] + + Author [Hyong-kyoon Shin, In-Ho Moon] + + Copyright [ This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddZddPort.c,v 1.1.1.1 2003/02/24 22:23:54 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static DdNode * zddPortFromBddStep ARGS((DdManager *dd, DdNode *B, int expected)); +static DdNode * zddPortToBddStep ARGS((DdManager *dd, DdNode *f, int depth)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Converts a BDD into a ZDD.] + + Description [Converts a BDD into a ZDD. This function assumes that + there is a one-to-one correspondence between the BDD variables and the + ZDD variables, and that the variable order is the same for both types + of variables. These conditions are established if the ZDD variables + are created by one call to Cudd_zddVarsFromBddVars with multiplicity = + 1. Returns a pointer to the resulting ZDD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_zddVarsFromBddVars] + +******************************************************************************/ +DdNode * +Cudd_zddPortFromBdd( + DdManager * dd, + DdNode * B) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = zddPortFromBddStep(dd,B,0); + } while (dd->reordered == 1); + + return(res); + +} /* end of Cudd_zddPortFromBdd */ + + +/**Function******************************************************************** + + Synopsis [Converts a ZDD into a BDD.] + + Description [Converts a ZDD into a BDD. Returns a pointer to the resulting + ZDD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_zddPortFromBdd] + +******************************************************************************/ +DdNode * +Cudd_zddPortToBdd( + DdManager * dd, + DdNode * f) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = zddPortToBddStep(dd,f,0); + } while (dd->reordered == 1); + + return(res); + +} /* end of Cudd_zddPortToBdd */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddPortFromBdd.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static DdNode * +zddPortFromBddStep( + DdManager * dd, + DdNode * B, + int expected) +{ + DdNode *res, *prevZdd, *t, *e; + DdNode *Breg, *Bt, *Be; + int id, level; + + statLine(dd); + /* Terminal cases. */ + if (B == Cudd_Not(DD_ONE(dd))) + return(DD_ZERO(dd)); + if (B == DD_ONE(dd)) { + if (expected >= dd->sizeZ) { + return(DD_ONE(dd)); + } else { + return(dd->univ[expected]); + } + } + + Breg = Cudd_Regular(B); + + /* Computed table look-up. */ + res = cuddCacheLookup1Zdd(dd,Cudd_zddPortFromBdd,B); + if (res != NULL) { + level = cuddI(dd,Breg->index); + /* Adding DC vars. */ + if (expected < level) { + /* Add suppressed variables. */ + cuddRef(res); + for (level--; level >= expected; level--) { + prevZdd = res; + id = dd->invperm[level]; + res = cuddZddGetNode(dd, id, prevZdd, prevZdd); + if (res == NULL) { + Cudd_RecursiveDerefZdd(dd, prevZdd); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDerefZdd(dd, prevZdd); + } + cuddDeref(res); + } + return(res); + } /* end of cache look-up */ + + if (Cudd_IsComplement(B)) { + Bt = Cudd_Not(cuddT(Breg)); + Be = Cudd_Not(cuddE(Breg)); + } else { + Bt = cuddT(Breg); + Be = cuddE(Breg); + } + + id = Breg->index; + level = cuddI(dd,id); + t = zddPortFromBddStep(dd, Bt, level+1); + if (t == NULL) return(NULL); + cuddRef(t); + e = zddPortFromBddStep(dd, Be, level+1); + if (e == NULL) { + Cudd_RecursiveDerefZdd(dd, t); + return(NULL); + } + cuddRef(e); + res = cuddZddGetNode(dd, id, t, e); + if (res == NULL) { + Cudd_RecursiveDerefZdd(dd, t); + Cudd_RecursiveDerefZdd(dd, e); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDerefZdd(dd, t); + Cudd_RecursiveDerefZdd(dd, e); + + cuddCacheInsert1(dd,Cudd_zddPortFromBdd,B,res); + + for (level--; level >= expected; level--) { + prevZdd = res; + id = dd->invperm[level]; + res = cuddZddGetNode(dd, id, prevZdd, prevZdd); + if (res == NULL) { + Cudd_RecursiveDerefZdd(dd, prevZdd); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDerefZdd(dd, prevZdd); + } + + cuddDeref(res); + return(res); + +} /* end of zddPortFromBddStep */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddPortToBdd.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static DdNode * +zddPortToBddStep( + DdManager * dd /* manager */, + DdNode * f /* ZDD to be converted */, + int depth /* recursion depth */) +{ + DdNode *one, *zero, *T, *E, *res, *var; + unsigned int index; + unsigned int level; + + statLine(dd); + one = DD_ONE(dd); + zero = DD_ZERO(dd); + if (f == zero) return(Cudd_Not(one)); + + if (depth == dd->sizeZ) return(one); + + index = dd->invpermZ[depth]; + level = cuddIZ(dd,f->index); + var = cuddUniqueInter(dd,index,one,Cudd_Not(one)); + if (var == NULL) return(NULL); + cuddRef(var); + + if (level > (unsigned) depth) { + E = zddPortToBddStep(dd,f,depth+1); + if (E == NULL) { + Cudd_RecursiveDeref(dd,var); + return(NULL); + } + cuddRef(E); + res = cuddBddIteRecur(dd,var,Cudd_Not(one),E); + if (res == NULL) { + Cudd_RecursiveDeref(dd,var); + Cudd_RecursiveDeref(dd,E); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(dd,var); + Cudd_RecursiveDeref(dd,E); + cuddDeref(res); + return(res); + } + + res = cuddCacheLookup1(dd,Cudd_zddPortToBdd,f); + if (res != NULL) { + Cudd_RecursiveDeref(dd,var); + return(res); + } + + T = zddPortToBddStep(dd,cuddT(f),depth+1); + if (T == NULL) { + Cudd_RecursiveDeref(dd,var); + return(NULL); + } + cuddRef(T); + E = zddPortToBddStep(dd,cuddE(f),depth+1); + if (E == NULL) { + Cudd_RecursiveDeref(dd,var); + Cudd_RecursiveDeref(dd,T); + return(NULL); + } + cuddRef(E); + + res = cuddBddIteRecur(dd,var,T,E); + if (res == NULL) { + Cudd_RecursiveDeref(dd,var); + Cudd_RecursiveDeref(dd,T); + Cudd_RecursiveDeref(dd,E); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(dd,var); + Cudd_RecursiveDeref(dd,T); + Cudd_RecursiveDeref(dd,E); + cuddDeref(res); + + cuddCacheInsert1(dd,Cudd_zddPortToBdd,f,res); + + return(res); + +} /* end of zddPortToBddStep */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddZddReord.c b/abc_with_bb_support/src/bdd/cudd/cuddZddReord.c new file mode 100644 index 000000000..951b5895f --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddZddReord.c @@ -0,0 +1,1633 @@ +/**CFile*********************************************************************** + + FileName [cuddZddReord.c] + + PackageName [cudd] + + Synopsis [Procedures for dynamic variable ordering of ZDDs.] + + Description [External procedures included in this module: +
          +
        • Cudd_zddReduceHeap() +
        • Cudd_zddShuffleHeap() +
        + Internal procedures included in this module: +
          +
        • cuddZddAlignToBdd() +
        • cuddZddNextHigh() +
        • cuddZddNextLow() +
        • cuddZddUniqueCompare() +
        • cuddZddSwapInPlace() +
        • cuddZddSwapping() +
        • cuddZddSifting() +
        + Static procedures included in this module: +
          +
        • zddSwapAny() +
        • cuddZddSiftingAux() +
        • cuddZddSiftingUp() +
        • cuddZddSiftingDown() +
        • cuddZddSiftingBackward() +
        • zddReorderPreprocess() +
        • zddReorderPostprocess() +
        • zddShuffle() +
        • zddSiftUp() +
        + ] + + SeeAlso [] + + Author [Hyong-Kyoon Shin, In-Ho Moon] + + Copyright [ This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define DD_MAX_SUBTABLE_SPARSITY 8 +#define DD_SHRINK_FACTOR 2 + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddZddReord.c,v 1.1.1.1 2003/02/24 22:23:54 wjiang Exp $"; +#endif + +int *zdd_entry; + +int zddTotalNumberSwapping; + +static DdNode *empty; + + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static Move * zddSwapAny ARGS((DdManager *table, int x, int y)); +static int cuddZddSiftingAux ARGS((DdManager *table, int x, int x_low, int x_high)); +static Move * cuddZddSiftingUp ARGS((DdManager *table, int x, int x_low, int initial_size)); +static Move * cuddZddSiftingDown ARGS((DdManager *table, int x, int x_high, int initial_size)); +static int cuddZddSiftingBackward ARGS((DdManager *table, Move *moves, int size)); +static void zddReorderPreprocess ARGS((DdManager *table)); +static int zddReorderPostprocess ARGS((DdManager *table)); +static int zddShuffle ARGS((DdManager *table, int *permutation)); +static int zddSiftUp ARGS((DdManager *table, int x, int xLow)); +static void zddFixTree ARGS((DdManager *table, MtrNode *treenode)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Main dynamic reordering routine for ZDDs.] + + Description [Main dynamic reordering routine for ZDDs. + Calls one of the possible reordering procedures: +
          +
        • Swapping +
        • Sifting +
        • Symmetric Sifting +
        + + For sifting and symmetric sifting it is possible to request reordering + to convergence.

        + + The core of all methods is the reordering procedure + cuddZddSwapInPlace() which swaps two adjacent variables. + Returns 1 in case of success; 0 otherwise. In the case of symmetric + sifting (with and without convergence) returns 1 plus the number of + symmetric variables, in case of success.] + + SideEffects [Changes the variable order for all ZDDs and clears + the cache.] + +******************************************************************************/ +int +Cudd_zddReduceHeap( + DdManager * table /* DD manager */, + Cudd_ReorderingType heuristic /* method used for reordering */, + int minsize /* bound below which no reordering occurs */) +{ + DdHook *hook; + int result; + unsigned int nextDyn; +#ifdef DD_STATS + unsigned int initialSize; + unsigned int finalSize; +#endif + long localTime; + + /* Don't reorder if there are too many dead nodes. */ + if (table->keysZ - table->deadZ < (unsigned) minsize) + return(1); + + if (heuristic == CUDD_REORDER_SAME) { + heuristic = table->autoMethodZ; + } + if (heuristic == CUDD_REORDER_NONE) { + return(1); + } + + /* This call to Cudd_zddReduceHeap does initiate reordering. Therefore + ** we count it. + */ + table->reorderings++; + empty = table->zero; + + localTime = util_cpu_time(); + + /* Run the hook functions. */ + hook = table->preReorderingHook; + while (hook != NULL) { + int res = (hook->f)(table, "ZDD", (void *)heuristic); + if (res == 0) return(0); + hook = hook->next; + } + + /* Clear the cache and collect garbage. */ + zddReorderPreprocess(table); + zddTotalNumberSwapping = 0; + +#ifdef DD_STATS + initialSize = table->keysZ; + + switch(heuristic) { + case CUDD_REORDER_RANDOM: + case CUDD_REORDER_RANDOM_PIVOT: + (void) fprintf(table->out,"#:I_RANDOM "); + break; + case CUDD_REORDER_SIFT: + case CUDD_REORDER_SIFT_CONVERGE: + case CUDD_REORDER_SYMM_SIFT: + case CUDD_REORDER_SYMM_SIFT_CONV: + (void) fprintf(table->out,"#:I_SIFTING "); + break; + case CUDD_REORDER_LINEAR: + case CUDD_REORDER_LINEAR_CONVERGE: + (void) fprintf(table->out,"#:I_LINSIFT "); + break; + default: + (void) fprintf(table->err,"Unsupported ZDD reordering method\n"); + return(0); + } + (void) fprintf(table->out,"%8d: initial size",initialSize); +#endif + + result = cuddZddTreeSifting(table,heuristic); + +#ifdef DD_STATS + (void) fprintf(table->out,"\n"); + finalSize = table->keysZ; + (void) fprintf(table->out,"#:F_REORDER %8d: final size\n",finalSize); + (void) fprintf(table->out,"#:T_REORDER %8g: total time (sec)\n", + ((double)(util_cpu_time() - localTime)/1000.0)); + (void) fprintf(table->out,"#:N_REORDER %8d: total swaps\n", + zddTotalNumberSwapping); +#endif + + if (result == 0) + return(0); + + if (!zddReorderPostprocess(table)) + return(0); + + if (table->realignZ) { + if (!cuddBddAlignToZdd(table)) + return(0); + } + + nextDyn = table->keysZ * DD_DYN_RATIO; + if (table->reorderings < 20 || nextDyn > table->nextDyn) + table->nextDyn = nextDyn; + else + table->nextDyn += 20; + + table->reordered = 1; + + /* Run hook functions. */ + hook = table->postReorderingHook; + while (hook != NULL) { + int res = (hook->f)(table, "ZDD", (void *)localTime); + if (res == 0) return(0); + hook = hook->next; + } + /* Update cumulative reordering time. */ + table->reordTime += util_cpu_time() - localTime; + + return(result); + +} /* end of Cudd_zddReduceHeap */ + + +/**Function******************************************************************** + + Synopsis [Reorders ZDD variables according to given permutation.] + + Description [Reorders ZDD variables according to given permutation. + The i-th entry of the permutation array contains the index of the variable + that should be brought to the i-th level. The size of the array should be + equal or greater to the number of variables currently in use. + Returns 1 in case of success; 0 otherwise.] + + SideEffects [Changes the ZDD variable order for all diagrams and clears + the cache.] + + SeeAlso [Cudd_zddReduceHeap] + +******************************************************************************/ +int +Cudd_zddShuffleHeap( + DdManager * table /* DD manager */, + int * permutation /* required variable permutation */) +{ + + int result; + + empty = table->zero; + zddReorderPreprocess(table); + + result = zddShuffle(table,permutation); + + if (!zddReorderPostprocess(table)) return(0); + + return(result); + +} /* end of Cudd_zddShuffleHeap */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Reorders ZDD variables according to the order of the BDD + variables.] + + Description [Reorders ZDD variables according to the order of the + BDD variables. This function can be called at the end of BDD + reordering to insure that the order of the ZDD variables is + consistent with the order of the BDD variables. The number of ZDD + variables must be a multiple of the number of BDD variables. Let + M be the ratio of the two numbers. cuddZddAlignToBdd + then considers the ZDD variables from M*i to + (M+1)*i-1 as corresponding to BDD variable + i. This function should be normally called from + Cudd_ReduceHeap, which clears the cache. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [Changes the ZDD variable order for all diagrams and performs + garbage collection of the ZDD unique table.] + + SeeAlso [Cudd_zddShuffleHeap Cudd_ReduceHeap] + +******************************************************************************/ +int +cuddZddAlignToBdd( + DdManager * table /* DD manager */) +{ + int *invpermZ; /* permutation array */ + int M; /* ratio of ZDD variables to BDD variables */ + int i,j; /* loop indices */ + int result; /* return value */ + + /* We assume that a ratio of 0 is OK. */ + if (table->sizeZ == 0) + return(1); + + empty = table->zero; + M = table->sizeZ / table->size; + /* Check whether the number of ZDD variables is a multiple of the + ** number of BDD variables. + */ + if (M * table->size != table->sizeZ) + return(0); + /* Create and initialize the inverse permutation array. */ + invpermZ = ALLOC(int,table->sizeZ); + if (invpermZ == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (i = 0; i < table->size; i++) { + int index = table->invperm[i]; + int indexZ = index * M; + int levelZ = table->permZ[indexZ]; + levelZ = (levelZ / M) * M; + for (j = 0; j < M; j++) { + invpermZ[M * i + j] = table->invpermZ[levelZ + j]; + } + } + /* Eliminate dead nodes. Do not scan the cache again, because we + ** assume that Cudd_ReduceHeap has already cleared it. + */ + cuddGarbageCollectZdd(table,0); + + result = zddShuffle(table, invpermZ); + FREE(invpermZ); + /* Fix the ZDD variable group tree. */ + zddFixTree(table,table->treeZ); + return(result); + +} /* end of cuddZddAlignToBdd */ + + +/**Function******************************************************************** + + Synopsis [Finds the next subtable with a larger index.] + + Description [Finds the next subtable with a larger index. Returns the + index.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddNextHigh( + DdManager * table, + int x) +{ + return(x + 1); + +} /* end of cuddZddNextHigh */ + + +/**Function******************************************************************** + + Synopsis [Finds the next subtable with a smaller index.] + + Description [Finds the next subtable with a smaller index. Returns the + index.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddNextLow( + DdManager * table, + int x) +{ + return(x - 1); + +} /* end of cuddZddNextLow */ + + +/**Function******************************************************************** + + Synopsis [Comparison function used by qsort.] + + Description [Comparison function used by qsort to order the + variables according to the number of keys in the subtables. + Returns the difference in number of keys between the two + variables being compared.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddUniqueCompare( + int * ptr_x, + int * ptr_y) +{ + return(zdd_entry[*ptr_y] - zdd_entry[*ptr_x]); + +} /* end of cuddZddUniqueCompare */ + + +/**Function******************************************************************** + + Synopsis [Swaps two adjacent variables.] + + Description [Swaps two adjacent variables. It assumes that no dead + nodes are present on entry to this procedure. The procedure then + guarantees that no dead nodes will be present when it terminates. + cuddZddSwapInPlace assumes that x < y. Returns the number of keys in + the table if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddSwapInPlace( + DdManager * table, + int x, + int y) +{ + DdNodePtr *xlist, *ylist; + int xindex, yindex; + int xslots, yslots; + int xshift, yshift; + int oldxkeys, oldykeys; + int newxkeys, newykeys; + int i; + int posn; + DdNode *f, *f1, *f0, *f11, *f10, *f01, *f00; + DdNode *newf1, *newf0, *next; + DdNodePtr g, *lastP, *previousP; + +#ifdef DD_DEBUG + assert(x < y); + assert(cuddZddNextHigh(table,x) == y); + assert(table->subtableZ[x].keys != 0); + assert(table->subtableZ[y].keys != 0); + assert(table->subtableZ[x].dead == 0); + assert(table->subtableZ[y].dead == 0); +#endif + + zddTotalNumberSwapping++; + + /* Get parameters of x subtable. */ + xindex = table->invpermZ[x]; + xlist = table->subtableZ[x].nodelist; + oldxkeys = table->subtableZ[x].keys; + xslots = table->subtableZ[x].slots; + xshift = table->subtableZ[x].shift; + newxkeys = 0; + + yindex = table->invpermZ[y]; + ylist = table->subtableZ[y].nodelist; + oldykeys = table->subtableZ[y].keys; + yslots = table->subtableZ[y].slots; + yshift = table->subtableZ[y].shift; + newykeys = oldykeys; + + /* The nodes in the x layer that don't depend on y directly + ** will stay there; the others are put in a chain. + ** The chain is handled as a FIFO; g points to the beginning and + ** last points to the end. + */ + + g = NULL; + lastP = &g; + for (i = 0; i < xslots; i++) { + previousP = &(xlist[i]); + f = *previousP; + while (f != NULL) { + next = f->next; + f1 = cuddT(f); f0 = cuddE(f); + if ((f1->index != (DdHalfWord) yindex) && + (f0->index != (DdHalfWord) yindex)) { /* stays */ + newxkeys++; + *previousP = f; + previousP = &(f->next); + } else { + f->index = yindex; + *lastP = f; + lastP = &(f->next); + } + f = next; + } /* while there are elements in the collision chain */ + *previousP = NULL; + } /* for each slot of the x subtable */ + *lastP = NULL; + + +#ifdef DD_COUNT + table->swapSteps += oldxkeys - newxkeys; +#endif + /* Take care of the x nodes that must be re-expressed. + ** They form a linked list pointed by g. Their index has been + ** changed to yindex already. + */ + f = g; + while (f != NULL) { + next = f->next; + /* Find f1, f0, f11, f10, f01, f00. */ + f1 = cuddT(f); + if ((int) f1->index == yindex) { + f11 = cuddT(f1); f10 = cuddE(f1); + } else { + f11 = empty; f10 = f1; + } + f0 = cuddE(f); + if ((int) f0->index == yindex) { + f01 = cuddT(f0); f00 = cuddE(f0); + } else { + f01 = empty; f00 = f0; + } + + /* Decrease ref count of f1. */ + cuddSatDec(f1->ref); + /* Create the new T child. */ + if (f11 == empty) { + if (f01 != empty) { + newf1 = f01; + cuddSatInc(newf1->ref); + } + /* else case was already handled when finding nodes + ** with both children below level y + */ + } else { + /* Check xlist for triple (xindex, f11, f01). */ + posn = ddHash(f11, f01, xshift); + /* For each element newf1 in collision list xlist[posn]. */ + newf1 = xlist[posn]; + while (newf1 != NULL) { + if (cuddT(newf1) == f11 && cuddE(newf1) == f01) { + cuddSatInc(newf1->ref); + break; /* match */ + } + newf1 = newf1->next; + } /* while newf1 */ + if (newf1 == NULL) { /* no match */ + newf1 = cuddDynamicAllocNode(table); + if (newf1 == NULL) + goto zddSwapOutOfMem; + newf1->index = xindex; newf1->ref = 1; + cuddT(newf1) = f11; + cuddE(newf1) = f01; + /* Insert newf1 in the collision list xlist[pos]; + ** increase the ref counts of f11 and f01 + */ + newxkeys++; + newf1->next = xlist[posn]; + xlist[posn] = newf1; + cuddSatInc(f11->ref); + cuddSatInc(f01->ref); + } + } + cuddT(f) = newf1; + + /* Do the same for f0. */ + /* Decrease ref count of f0. */ + cuddSatDec(f0->ref); + /* Create the new E child. */ + if (f10 == empty) { + newf0 = f00; + cuddSatInc(newf0->ref); + } else { + /* Check xlist for triple (xindex, f10, f00). */ + posn = ddHash(f10, f00, xshift); + /* For each element newf0 in collision list xlist[posn]. */ + newf0 = xlist[posn]; + while (newf0 != NULL) { + if (cuddT(newf0) == f10 && cuddE(newf0) == f00) { + cuddSatInc(newf0->ref); + break; /* match */ + } + newf0 = newf0->next; + } /* while newf0 */ + if (newf0 == NULL) { /* no match */ + newf0 = cuddDynamicAllocNode(table); + if (newf0 == NULL) + goto zddSwapOutOfMem; + newf0->index = xindex; newf0->ref = 1; + cuddT(newf0) = f10; cuddE(newf0) = f00; + /* Insert newf0 in the collision list xlist[posn]; + ** increase the ref counts of f10 and f00. + */ + newxkeys++; + newf0->next = xlist[posn]; + xlist[posn] = newf0; + cuddSatInc(f10->ref); + cuddSatInc(f00->ref); + } + } + cuddE(f) = newf0; + + /* Insert the modified f in ylist. + ** The modified f does not already exists in ylist. + ** (Because of the uniqueness of the cofactors.) + */ + posn = ddHash(newf1, newf0, yshift); + newykeys++; + f->next = ylist[posn]; + ylist[posn] = f; + f = next; + } /* while f != NULL */ + + /* GC the y layer. */ + + /* For each node f in ylist. */ + for (i = 0; i < yslots; i++) { + previousP = &(ylist[i]); + f = *previousP; + while (f != NULL) { + next = f->next; + if (f->ref == 0) { + cuddSatDec(cuddT(f)->ref); + cuddSatDec(cuddE(f)->ref); + cuddDeallocNode(table, f); + newykeys--; + } else { + *previousP = f; + previousP = &(f->next); + } + f = next; + } /* while f */ + *previousP = NULL; + } /* for i */ + + /* Set the appropriate fields in table. */ + table->subtableZ[x].nodelist = ylist; + table->subtableZ[x].slots = yslots; + table->subtableZ[x].shift = yshift; + table->subtableZ[x].keys = newykeys; + table->subtableZ[x].maxKeys = yslots * DD_MAX_SUBTABLE_DENSITY; + + table->subtableZ[y].nodelist = xlist; + table->subtableZ[y].slots = xslots; + table->subtableZ[y].shift = xshift; + table->subtableZ[y].keys = newxkeys; + table->subtableZ[y].maxKeys = xslots * DD_MAX_SUBTABLE_DENSITY; + + table->permZ[xindex] = y; table->permZ[yindex] = x; + table->invpermZ[x] = yindex; table->invpermZ[y] = xindex; + + table->keysZ += newxkeys + newykeys - oldxkeys - oldykeys; + + /* Update univ section; univ[x] remains the same. */ + table->univ[y] = cuddT(table->univ[x]); + + return (table->keysZ); + +zddSwapOutOfMem: + (void) fprintf(table->err, "Error: cuddZddSwapInPlace out of memory\n"); + + return (0); + +} /* end of cuddZddSwapInPlace */ + + +/**Function******************************************************************** + + Synopsis [Reorders variables by a sequence of (non-adjacent) swaps.] + + Description [Implementation of Plessier's algorithm that reorders + variables by a sequence of (non-adjacent) swaps. +

          +
        1. Select two variables (RANDOM or HEURISTIC). +
        2. Permute these variables. +
        3. If the nodes have decreased accept the permutation. +
        4. Otherwise reconstruct the original heap. +
        5. Loop. +
        + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddSwapping( + DdManager * table, + int lower, + int upper, + Cudd_ReorderingType heuristic) +{ + int i, j; + int max, keys; + int nvars; + int x, y; + int iterate; + int previousSize; + Move *moves, *move; + int pivot; + int modulo; + int result; + +#ifdef DD_DEBUG + /* Sanity check */ + assert(lower >= 0 && upper < table->sizeZ && lower <= upper); +#endif + + nvars = upper - lower + 1; + iterate = nvars; + + for (i = 0; i < iterate; i++) { + if (heuristic == CUDD_REORDER_RANDOM_PIVOT) { + /* Find pivot <= id with maximum keys. */ + for (max = -1, j = lower; j <= upper; j++) { + if ((keys = table->subtableZ[j].keys) > max) { + max = keys; + pivot = j; + } + } + + modulo = upper - pivot; + if (modulo == 0) { + y = pivot; /* y = nvars-1 */ + } else { + /* y = random # from {pivot+1 .. nvars-1} */ + y = pivot + 1 + (int) (Cudd_Random() % modulo); + } + + modulo = pivot - lower - 1; + if (modulo < 1) { /* if pivot = 1 or 0 */ + x = lower; + } else { + do { /* x = random # from {0 .. pivot-2} */ + x = (int) Cudd_Random() % modulo; + } while (x == y); + /* Is this condition really needed, since x and y + are in regions separated by pivot? */ + } + } else { + x = (int) (Cudd_Random() % nvars) + lower; + do { + y = (int) (Cudd_Random() % nvars) + lower; + } while (x == y); + } + + previousSize = table->keysZ; + moves = zddSwapAny(table, x, y); + if (moves == NULL) + goto cuddZddSwappingOutOfMem; + + result = cuddZddSiftingBackward(table, moves, previousSize); + if (!result) + goto cuddZddSwappingOutOfMem; + + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *) moves); + moves = move; + } +#ifdef DD_STATS + if (table->keysZ < (unsigned) previousSize) { + (void) fprintf(table->out,"-"); + } else if (table->keysZ > (unsigned) previousSize) { + (void) fprintf(table->out,"+"); /* should never happen */ + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + + return(1); + +cuddZddSwappingOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *) moves); + moves = move; + } + return(0); + +} /* end of cuddZddSwapping */ + + +/**Function******************************************************************** + + Synopsis [Implementation of Rudell's sifting algorithm.] + + Description [Implementation of Rudell's sifting algorithm. + Assumes that no dead nodes are present. +
          +
        1. Order all the variables according to the number of entries + in each unique table. +
        2. Sift the variable up and down, remembering each time the + total size of the DD heap. +
        3. Select the best permutation. +
        4. Repeat 3 and 4 for all variables. +
        + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddSifting( + DdManager * table, + int lower, + int upper) +{ + int i; + int *var; + int size; + int x; + int result; +#ifdef DD_STATS + int previousSize; +#endif + + size = table->sizeZ; + + /* Find order in which to sift variables. */ + var = NULL; + zdd_entry = ALLOC(int, size); + if (zdd_entry == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddZddSiftingOutOfMem; + } + var = ALLOC(int, size); + if (var == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddZddSiftingOutOfMem; + } + + for (i = 0; i < size; i++) { + x = table->permZ[i]; + zdd_entry[i] = table->subtableZ[x].keys; + var[i] = i; + } + + qsort((void *)var, size, sizeof(int), (int (*)(const void *, const void *))cuddZddUniqueCompare); + + /* Now sift. */ + for (i = 0; i < ddMin(table->siftMaxVar, size); i++) { + if (zddTotalNumberSwapping >= table->siftMaxSwap) + break; + x = table->permZ[var[i]]; + if (x < lower || x > upper) continue; +#ifdef DD_STATS + previousSize = table->keysZ; +#endif + result = cuddZddSiftingAux(table, x, lower, upper); + if (!result) + goto cuddZddSiftingOutOfMem; +#ifdef DD_STATS + if (table->keysZ < (unsigned) previousSize) { + (void) fprintf(table->out,"-"); + } else if (table->keysZ > (unsigned) previousSize) { + (void) fprintf(table->out,"+"); /* should never happen */ + (void) fprintf(table->out,"\nSize increased from %d to %d while sifting variable %d\n", previousSize, table->keysZ , var[i]); + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + + FREE(var); + FREE(zdd_entry); + + return(1); + +cuddZddSiftingOutOfMem: + + if (zdd_entry != NULL) FREE(zdd_entry); + if (var != NULL) FREE(var); + + return(0); + +} /* end of cuddZddSifting */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Swaps any two variables.] + + Description [Swaps any two variables. Returns the set of moves.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static Move * +zddSwapAny( + DdManager * table, + int x, + int y) +{ + Move *move, *moves; + int tmp, size; + int x_ref, y_ref; + int x_next, y_next; + int limit_size; + + if (x > y) { /* make x precede y */ + tmp = x; x = y; y = tmp; + } + + x_ref = x; y_ref = y; + + x_next = cuddZddNextHigh(table, x); + y_next = cuddZddNextLow(table, y); + moves = NULL; + limit_size = table->keysZ; + + for (;;) { + if (x_next == y_next) { /* x < x_next = y_next < y */ + size = cuddZddSwapInPlace(table, x, x_next); + if (size == 0) + goto zddSwapAnyOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) + goto zddSwapAnyOutOfMem; + move->x = x; + move->y = x_next; + move->size = size; + move->next = moves; + moves = move; + + size = cuddZddSwapInPlace(table, y_next, y); + if (size == 0) + goto zddSwapAnyOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto zddSwapAnyOutOfMem; + move->x = y_next; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + + size = cuddZddSwapInPlace(table, x, x_next); + if (size == 0) + goto zddSwapAnyOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto zddSwapAnyOutOfMem; + move->x = x; + move->y = x_next; + move->size = size; + move->next = moves; + moves = move; + + tmp = x; x = y; y = tmp; + + } else if (x == y_next) { /* x = y_next < y = x_next */ + size = cuddZddSwapInPlace(table, x, x_next); + if (size == 0) + goto zddSwapAnyOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto zddSwapAnyOutOfMem; + move->x = x; + move->y = x_next; + move->size = size; + move->next = moves; + moves = move; + + tmp = x; x = y; y = tmp; + } else { + size = cuddZddSwapInPlace(table, x, x_next); + if (size == 0) + goto zddSwapAnyOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto zddSwapAnyOutOfMem; + move->x = x; + move->y = x_next; + move->size = size; + move->next = moves; + moves = move; + + size = cuddZddSwapInPlace(table, y_next, y); + if (size == 0) + goto zddSwapAnyOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto zddSwapAnyOutOfMem; + move->x = y_next; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + + x = x_next; y = y_next; + } + + x_next = cuddZddNextHigh(table, x); + y_next = cuddZddNextLow(table, y); + if (x_next > y_ref) + break; /* if x == y_ref */ + + if ((double) size > table->maxGrowth * (double) limit_size) + break; + if (size < limit_size) + limit_size = size; + } + if (y_next >= x_ref) { + size = cuddZddSwapInPlace(table, y_next, y); + if (size == 0) + goto zddSwapAnyOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto zddSwapAnyOutOfMem; + move->x = y_next; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + } + + return(moves); + +zddSwapAnyOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *)moves); + moves = move; + } + return(NULL); + +} /* end of zddSwapAny */ + + +/**Function******************************************************************** + + Synopsis [Given xLow <= x <= xHigh moves x up and down between the + boundaries.] + + Description [Given xLow <= x <= xHigh moves x up and down between the + boundaries. Finds the best position and does the required changes. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddZddSiftingAux( + DdManager * table, + int x, + int x_low, + int x_high) +{ + Move *move; + Move *moveUp; /* list of up move */ + Move *moveDown; /* list of down move */ + + int initial_size; + int result; + + initial_size = table->keysZ; + +#ifdef DD_DEBUG + assert(table->subtableZ[x].keys > 0); +#endif + + moveDown = NULL; + moveUp = NULL; + + if (x == x_low) { + moveDown = cuddZddSiftingDown(table, x, x_high, initial_size); + /* after that point x --> x_high */ + if (moveDown == NULL) + goto cuddZddSiftingAuxOutOfMem; + result = cuddZddSiftingBackward(table, moveDown, + initial_size); + /* move backward and stop at best position */ + if (!result) + goto cuddZddSiftingAuxOutOfMem; + + } + else if (x == x_high) { + moveUp = cuddZddSiftingUp(table, x, x_low, initial_size); + /* after that point x --> x_low */ + if (moveUp == NULL) + goto cuddZddSiftingAuxOutOfMem; + result = cuddZddSiftingBackward(table, moveUp, initial_size); + /* move backward and stop at best position */ + if (!result) + goto cuddZddSiftingAuxOutOfMem; + } + else if ((x - x_low) > (x_high - x)) { + /* must go down first:shorter */ + moveDown = cuddZddSiftingDown(table, x, x_high, initial_size); + /* after that point x --> x_high */ + if (moveDown == NULL) + goto cuddZddSiftingAuxOutOfMem; + moveUp = cuddZddSiftingUp(table, moveDown->y, x_low, + initial_size); + if (moveUp == NULL) + goto cuddZddSiftingAuxOutOfMem; + result = cuddZddSiftingBackward(table, moveUp, initial_size); + /* move backward and stop at best position */ + if (!result) + goto cuddZddSiftingAuxOutOfMem; + } + else { + moveUp = cuddZddSiftingUp(table, x, x_low, initial_size); + /* after that point x --> x_high */ + if (moveUp == NULL) + goto cuddZddSiftingAuxOutOfMem; + moveDown = cuddZddSiftingDown(table, moveUp->x, x_high, + initial_size); + /* then move up */ + if (moveDown == NULL) + goto cuddZddSiftingAuxOutOfMem; + result = cuddZddSiftingBackward(table, moveDown, + initial_size); + /* move backward and stop at best position */ + if (!result) + goto cuddZddSiftingAuxOutOfMem; + } + + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocNode(table, (DdNode *)moveDown); + moveDown = move; + } + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocNode(table, (DdNode *)moveUp); + moveUp = move; + } + + return(1); + +cuddZddSiftingAuxOutOfMem: + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocNode(table, (DdNode *)moveDown); + moveDown = move; + } + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocNode(table, (DdNode *)moveUp); + moveUp = move; + } + + return(0); + +} /* end of cuddZddSiftingAux */ + + +/**Function******************************************************************** + + Synopsis [Sifts a variable up.] + + Description [Sifts a variable up. Moves y up until either it reaches + the bound (x_low) or the size of the ZDD heap increases too much. + Returns the set of moves in case of success; NULL if memory is full.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static Move * +cuddZddSiftingUp( + DdManager * table, + int x, + int x_low, + int initial_size) +{ + Move *moves; + Move *move; + int y; + int size; + int limit_size = initial_size; + + moves = NULL; + y = cuddZddNextLow(table, x); + while (y >= x_low) { + size = cuddZddSwapInPlace(table, y, x); + if (size == 0) + goto cuddZddSiftingUpOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto cuddZddSiftingUpOutOfMem; + move->x = y; + move->y = x; + move->size = size; + move->next = moves; + moves = move; + + if ((double)size > (double)limit_size * table->maxGrowth) + break; + if (size < limit_size) + limit_size = size; + + x = y; + y = cuddZddNextLow(table, x); + } + return(moves); + +cuddZddSiftingUpOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *)moves); + moves = move; + } + return(NULL); + +} /* end of cuddZddSiftingUp */ + + +/**Function******************************************************************** + + Synopsis [Sifts a variable down.] + + Description [Sifts a variable down. Moves x down until either it + reaches the bound (x_high) or the size of the ZDD heap increases too + much. Returns the set of moves in case of success; NULL if memory is + full.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static Move * +cuddZddSiftingDown( + DdManager * table, + int x, + int x_high, + int initial_size) +{ + Move *moves; + Move *move; + int y; + int size; + int limit_size = initial_size; + + moves = NULL; + y = cuddZddNextHigh(table, x); + while (y <= x_high) { + size = cuddZddSwapInPlace(table, x, y); + if (size == 0) + goto cuddZddSiftingDownOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto cuddZddSiftingDownOutOfMem; + move->x = x; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + + if ((double)size > (double)limit_size * table->maxGrowth) + break; + if (size < limit_size) + limit_size = size; + + x = y; + y = cuddZddNextHigh(table, x); + } + return(moves); + +cuddZddSiftingDownOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *)moves); + moves = move; + } + return(NULL); + +} /* end of cuddZddSiftingDown */ + + +/**Function******************************************************************** + + Synopsis [Given a set of moves, returns the ZDD heap to the position + giving the minimum size.] + + Description [Given a set of moves, returns the ZDD heap to the + position giving the minimum size. In case of ties, returns to the + closest position giving the minimum size. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddZddSiftingBackward( + DdManager * table, + Move * moves, + int size) +{ + int i; + int i_best; + Move *move; + int res; + + /* Find the minimum size among moves. */ + i_best = -1; + for (move = moves, i = 0; move != NULL; move = move->next, i++) { + if (move->size < size) { + i_best = i; + size = move->size; + } + } + + for (move = moves, i = 0; move != NULL; move = move->next, i++) { + if (i == i_best) + break; + res = cuddZddSwapInPlace(table, move->x, move->y); + if (!res) + return(0); + if (i_best == -1 && res == size) + break; + } + + return(1); + +} /* end of cuddZddSiftingBackward */ + + +/**Function******************************************************************** + + Synopsis [Prepares the ZDD heap for dynamic reordering.] + + Description [Prepares the ZDD heap for dynamic reordering. Does + garbage collection, to guarantee that there are no dead nodes; + and clears the cache, which is invalidated by dynamic reordering.] + + SideEffects [None] + +******************************************************************************/ +static void +zddReorderPreprocess( + DdManager * table) +{ + + /* Clear the cache. */ + cuddCacheFlush(table); + + /* Eliminate dead nodes. Do not scan the cache again. */ + cuddGarbageCollectZdd(table,0); + + return; + +} /* end of ddReorderPreprocess */ + + +/**Function******************************************************************** + + Synopsis [Shrinks almost empty ZDD subtables at the end of reordering + to guarantee that they have a reasonable load factor.] + + Description [Shrinks almost empty subtables at the end of reordering to + guarantee that they have a reasonable load factor. However, if there many + nodes are being reclaimed, then no resizing occurs. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +zddReorderPostprocess( + DdManager * table) +{ + int i, j, posn; + DdNodePtr *nodelist, *oldnodelist; + DdNode *node, *next; + unsigned int slots, oldslots; + extern void (*MMoutOfMemory)(long); + void (*saveHandler)(long); + +#ifdef DD_VERBOSE + (void) fflush(table->out); +#endif + + /* If we have very many reclaimed nodes, we do not want to shrink + ** the subtables, because this will lead to more garbage + ** collections. More garbage collections mean shorter mean life for + ** nodes with zero reference count; hence lower probability of finding + ** a result in the cache. + */ + if (table->reclaimed > table->allocated * 0.5) return(1); + + /* Resize subtables. */ + for (i = 0; i < table->sizeZ; i++) { + int shift; + oldslots = table->subtableZ[i].slots; + if (oldslots < table->subtableZ[i].keys * DD_MAX_SUBTABLE_SPARSITY || + oldslots <= table->initSlots) continue; + oldnodelist = table->subtableZ[i].nodelist; + slots = oldslots >> 1; + saveHandler = MMoutOfMemory; + MMoutOfMemory = Cudd_OutOfMem; + nodelist = ALLOC(DdNodePtr, slots); + MMoutOfMemory = saveHandler; + if (nodelist == NULL) { + return(1); + } + table->subtableZ[i].nodelist = nodelist; + table->subtableZ[i].slots = slots; + table->subtableZ[i].shift++; + table->subtableZ[i].maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; +#ifdef DD_VERBOSE + (void) fprintf(table->err, + "shrunk layer %d (%d keys) from %d to %d slots\n", + i, table->subtableZ[i].keys, oldslots, slots); +#endif + + for (j = 0; (unsigned) j < slots; j++) { + nodelist[j] = NULL; + } + shift = table->subtableZ[i].shift; + for (j = 0; (unsigned) j < oldslots; j++) { + node = oldnodelist[j]; + while (node != NULL) { + next = node->next; + posn = ddHash(cuddT(node), cuddE(node), shift); + node->next = nodelist[posn]; + nodelist[posn] = node; + node = next; + } + } + FREE(oldnodelist); + + table->memused += (slots - oldslots) * sizeof(DdNode *); + table->slots += slots - oldslots; + table->minDead = (unsigned) (table->gcFrac * (double) table->slots); + table->cacheSlack = (int) ddMin(table->maxCacheHard, + DD_MAX_CACHE_TO_SLOTS_RATIO*table->slots) - + 2 * (int) table->cacheSlots; + } + /* We don't look at the constant subtable, because it is not + ** affected by reordering. + */ + + return(1); + +} /* end of zddReorderPostprocess */ + + +/**Function******************************************************************** + + Synopsis [Reorders ZDD variables according to a given permutation.] + + Description [Reorders ZDD variables according to a given permutation. + The i-th permutation array contains the index of the variable that + should be brought to the i-th level. zddShuffle assumes that no + dead nodes are present. The reordering is achieved by a series of + upward sifts. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +zddShuffle( + DdManager * table, + int * permutation) +{ + int index; + int level; + int position; + int numvars; + int result; +#ifdef DD_STATS + long localTime; + int initialSize; + int finalSize; + int previousSize; +#endif + + zddTotalNumberSwapping = 0; +#ifdef DD_STATS + localTime = util_cpu_time(); + initialSize = table->keysZ; + (void) fprintf(table->out,"#:I_SHUFFLE %8d: initial size\n", + initialSize); +#endif + + numvars = table->sizeZ; + + for (level = 0; level < numvars; level++) { + index = permutation[level]; + position = table->permZ[index]; +#ifdef DD_STATS + previousSize = table->keysZ; +#endif + result = zddSiftUp(table,position,level); + if (!result) return(0); +#ifdef DD_STATS + if (table->keysZ < (unsigned) previousSize) { + (void) fprintf(table->out,"-"); + } else if (table->keysZ > (unsigned) previousSize) { + (void) fprintf(table->out,"+"); /* should never happen */ + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + +#ifdef DD_STATS + (void) fprintf(table->out,"\n"); + finalSize = table->keysZ; + (void) fprintf(table->out,"#:F_SHUFFLE %8d: final size\n",finalSize); + (void) fprintf(table->out,"#:T_SHUFFLE %8g: total time (sec)\n", + ((double)(util_cpu_time() - localTime)/1000.0)); + (void) fprintf(table->out,"#:N_SHUFFLE %8d: total swaps\n", + zddTotalNumberSwapping); +#endif + + return(1); + +} /* end of zddShuffle */ + + +/**Function******************************************************************** + + Synopsis [Moves one ZDD variable up.] + + Description [Takes a ZDD variable from position x and sifts it up to + position xLow; xLow should be less than or equal to x. + Returns 1 if successful; 0 otherwise] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +zddSiftUp( + DdManager * table, + int x, + int xLow) +{ + int y; + int size; + + y = cuddZddNextLow(table,x); + while (y >= xLow) { + size = cuddZddSwapInPlace(table,y,x); + if (size == 0) { + return(0); + } + x = y; + y = cuddZddNextLow(table,x); + } + return(1); + +} /* end of zddSiftUp */ + + +/**Function******************************************************************** + + Synopsis [Fixes the ZDD variable group tree after a shuffle.] + + Description [Fixes the ZDD variable group tree after a + shuffle. Assumes that the order of the variables in a terminal node + has not been changed.] + + SideEffects [Changes the ZDD variable group tree.] + + SeeAlso [] + +******************************************************************************/ +static void +zddFixTree( + DdManager * table, + MtrNode * treenode) +{ + if (treenode == NULL) return; + treenode->low = ((int) treenode->index < table->sizeZ) ? + table->permZ[treenode->index] : treenode->index; + if (treenode->child != NULL) { + zddFixTree(table, treenode->child); + } + if (treenode->younger != NULL) + zddFixTree(table, treenode->younger); + if (treenode->parent != NULL && treenode->low < treenode->parent->low) { + treenode->parent->low = treenode->low; + treenode->parent->index = treenode->index; + } + return; + +} /* end of zddFixTree */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddZddSetop.c b/abc_with_bb_support/src/bdd/cudd/cuddZddSetop.c new file mode 100644 index 000000000..00da12189 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddZddSetop.c @@ -0,0 +1,1137 @@ +/**CFile*********************************************************************** + + FileName [cuddZddSetop.c] + + PackageName [cudd] + + Synopsis [Set operations on ZDDs.] + + Description [External procedures included in this module: +
          +
        • Cudd_zddIte() +
        • Cudd_zddUnion() +
        • Cudd_zddIntersect() +
        • Cudd_zddDiff() +
        • Cudd_zddDiffConst() +
        • Cudd_zddSubset1() +
        • Cudd_zddSubset0() +
        • Cudd_zddChange() +
        + Internal procedures included in this module: +
          +
        • cuddZddIte() +
        • cuddZddUnion() +
        • cuddZddIntersect() +
        • cuddZddDiff() +
        • cuddZddChangeAux() +
        • cuddZddSubset1() +
        • cuddZddSubset0() +
        + Static procedures included in this module: +
          +
        • zdd_subset1_aux() +
        • zdd_subset0_aux() +
        • zddVarToConst() +
        + ] + + SeeAlso [] + + Author [Hyong-Kyoon Shin, In-Ho Moon] + + Copyright [ This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddZddSetop.c,v 1.1.1.1 2003/02/24 22:23:54 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static DdNode * zdd_subset1_aux ARGS((DdManager *zdd, DdNode *P, DdNode *zvar)); +static DdNode * zdd_subset0_aux ARGS((DdManager *zdd, DdNode *P, DdNode *zvar)); +static void zddVarToConst ARGS((DdNode *f, DdNode **gp, DdNode **hp, DdNode *base, DdNode *empty)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Computes the ITE of three ZDDs.] + + Description [Computes the ITE of three ZDDs. Returns a pointer to the + result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +Cudd_zddIte( + DdManager * dd, + DdNode * f, + DdNode * g, + DdNode * h) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddZddIte(dd, f, g, h); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_zddIte */ + + +/**Function******************************************************************** + + Synopsis [Computes the union of two ZDDs.] + + Description [Computes the union of two ZDDs. Returns a pointer to the + result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +Cudd_zddUnion( + DdManager * dd, + DdNode * P, + DdNode * Q) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddZddUnion(dd, P, Q); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_zddUnion */ + + +/**Function******************************************************************** + + Synopsis [Computes the intersection of two ZDDs.] + + Description [Computes the intersection of two ZDDs. Returns a pointer to + the result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +Cudd_zddIntersect( + DdManager * dd, + DdNode * P, + DdNode * Q) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddZddIntersect(dd, P, Q); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_zddIntersect */ + + +/**Function******************************************************************** + + Synopsis [Computes the difference of two ZDDs.] + + Description [Computes the difference of two ZDDs. Returns a pointer to the + result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_zddDiffConst] + +******************************************************************************/ +DdNode * +Cudd_zddDiff( + DdManager * dd, + DdNode * P, + DdNode * Q) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddZddDiff(dd, P, Q); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_zddDiff */ + + +/**Function******************************************************************** + + Synopsis [Performs the inclusion test for ZDDs (P implies Q).] + + Description [Inclusion test for ZDDs (P implies Q). No new nodes are + generated by this procedure. Returns empty if true; + a valid pointer different from empty or DD_NON_CONSTANT otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_zddDiff] + +******************************************************************************/ +DdNode * +Cudd_zddDiffConst( + DdManager * zdd, + DdNode * P, + DdNode * Q) +{ + int p_top, q_top; + DdNode *empty = DD_ZERO(zdd), *t, *res; + DdManager *table = zdd; + + statLine(zdd); + if (P == empty) + return(empty); + if (Q == empty) + return(P); + if (P == Q) + return(empty); + + /* Check cache. The cache is shared by cuddZddDiff(). */ + res = cuddCacheLookup2Zdd(table, cuddZddDiff, P, Q); + if (res != NULL) + return(res); + + if (cuddIsConstant(P)) + p_top = P->index; + else + p_top = zdd->permZ[P->index]; + if (cuddIsConstant(Q)) + q_top = Q->index; + else + q_top = zdd->permZ[Q->index]; + if (p_top < q_top) { + res = DD_NON_CONSTANT; + } else if (p_top > q_top) { + res = Cudd_zddDiffConst(zdd, P, cuddE(Q)); + } else { + t = Cudd_zddDiffConst(zdd, cuddT(P), cuddT(Q)); + if (t != empty) + res = DD_NON_CONSTANT; + else + res = Cudd_zddDiffConst(zdd, cuddE(P), cuddE(Q)); + } + + cuddCacheInsert2(table, cuddZddDiff, P, Q, res); + + return(res); + +} /* end of Cudd_zddDiffConst */ + + +/**Function******************************************************************** + + Synopsis [Computes the positive cofactor of a ZDD w.r.t. a variable.] + + Description [Computes the positive cofactor of a ZDD w.r.t. a + variable. In terms of combinations, the result is the set of all + combinations in which the variable is asserted. Returns a pointer to + the result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_zddSubset0] + +******************************************************************************/ +DdNode * +Cudd_zddSubset1( + DdManager * dd, + DdNode * P, + int var) +{ + DdNode *r; + + do { + dd->reordered = 0; + r = cuddZddSubset1(dd, P, var); + } while (dd->reordered == 1); + + return(r); + +} /* end of Cudd_zddSubset1 */ + + +/**Function******************************************************************** + + Synopsis [Computes the negative cofactor of a ZDD w.r.t. a variable.] + + Description [Computes the negative cofactor of a ZDD w.r.t. a + variable. In terms of combinations, the result is the set of all + combinations in which the variable is negated. Returns a pointer to + the result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_zddSubset1] + +******************************************************************************/ +DdNode * +Cudd_zddSubset0( + DdManager * dd, + DdNode * P, + int var) +{ + DdNode *r; + + do { + dd->reordered = 0; + r = cuddZddSubset0(dd, P, var); + } while (dd->reordered == 1); + + return(r); + +} /* end of Cudd_zddSubset0 */ + + +/**Function******************************************************************** + + Synopsis [Substitutes a variable with its complement in a ZDD.] + + Description [Substitutes a variable with its complement in a ZDD. + returns a pointer to the result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +Cudd_zddChange( + DdManager * dd, + DdNode * P, + int var) +{ + DdNode *res; + + if ((unsigned int) var >= CUDD_MAXINDEX - 1) return(NULL); + + do { + dd->reordered = 0; + res = cuddZddChange(dd, P, var); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_zddChange */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddIte.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddZddIte( + DdManager * dd, + DdNode * f, + DdNode * g, + DdNode * h) +{ + DdNode *tautology, *empty; + DdNode *r,*Gv,*Gvn,*Hv,*Hvn,*t,*e; + unsigned int topf,topg,toph,v,top; + int index; + + statLine(dd); + /* Trivial cases. */ + /* One variable cases. */ + if (f == (empty = DD_ZERO(dd))) { /* ITE(0,G,H) = H */ + return(h); + } + topf = cuddIZ(dd,f->index); + topg = cuddIZ(dd,g->index); + toph = cuddIZ(dd,h->index); + v = ddMin(topg,toph); + top = ddMin(topf,v); + + tautology = (top == CUDD_MAXINDEX) ? DD_ONE(dd) : dd->univ[top]; + if (f == tautology) { /* ITE(1,G,H) = G */ + return(g); + } + + /* From now on, f is known to not be a constant. */ + zddVarToConst(f,&g,&h,tautology,empty); + + /* Check remaining one variable cases. */ + if (g == h) { /* ITE(F,G,G) = G */ + return(g); + } + + if (g == tautology) { /* ITE(F,1,0) = F */ + if (h == empty) return(f); + } + + /* Check cache. */ + r = cuddCacheLookupZdd(dd,DD_ZDD_ITE_TAG,f,g,h); + if (r != NULL) { + return(r); + } + + /* Recompute these because they may have changed in zddVarToConst. */ + topg = cuddIZ(dd,g->index); + toph = cuddIZ(dd,h->index); + v = ddMin(topg,toph); + + if (topf < v) { + r = cuddZddIte(dd,cuddE(f),g,h); + if (r == NULL) return(NULL); + } else if (topf > v) { + if (topg > v) { + Gvn = g; + index = h->index; + } else { + Gvn = cuddE(g); + index = g->index; + } + if (toph > v) { + Hv = empty; Hvn = h; + } else { + Hv = cuddT(h); Hvn = cuddE(h); + } + e = cuddZddIte(dd,f,Gvn,Hvn); + if (e == NULL) return(NULL); + cuddRef(e); + r = cuddZddGetNode(dd,index,Hv,e); + if (r == NULL) { + Cudd_RecursiveDerefZdd(dd,e); + return(NULL); + } + cuddDeref(e); + } else { + index = f->index; + if (topg > v) { + Gv = empty; Gvn = g; + } else { + Gv = cuddT(g); Gvn = cuddE(g); + } + if (toph > v) { + Hv = empty; Hvn = h; + } else { + Hv = cuddT(h); Hvn = cuddE(h); + } + e = cuddZddIte(dd,cuddE(f),Gvn,Hvn); + if (e == NULL) return(NULL); + cuddRef(e); + t = cuddZddIte(dd,cuddT(f),Gv,Hv); + if (t == NULL) { + Cudd_RecursiveDerefZdd(dd,e); + return(NULL); + } + cuddRef(t); + r = cuddZddGetNode(dd,index,t,e); + if (r == NULL) { + Cudd_RecursiveDerefZdd(dd,e); + Cudd_RecursiveDerefZdd(dd,t); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + } + + cuddCacheInsert(dd,DD_ZDD_ITE_TAG,f,g,h,r); + + return(r); + +} /* end of cuddZddIte */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddUnion.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddZddUnion( + DdManager * zdd, + DdNode * P, + DdNode * Q) +{ + int p_top, q_top; + DdNode *empty = DD_ZERO(zdd), *t, *e, *res; + DdManager *table = zdd; + + statLine(zdd); + if (P == empty) + return(Q); + if (Q == empty) + return(P); + if (P == Q) + return(P); + + /* Check cache */ + res = cuddCacheLookup2Zdd(table, cuddZddUnion, P, Q); + if (res != NULL) + return(res); + + if (cuddIsConstant(P)) + p_top = P->index; + else + p_top = zdd->permZ[P->index]; + if (cuddIsConstant(Q)) + q_top = Q->index; + else + q_top = zdd->permZ[Q->index]; + if (p_top < q_top) { + e = cuddZddUnion(zdd, cuddE(P), Q); + if (e == NULL) return (NULL); + cuddRef(e); + res = cuddZddGetNode(zdd, P->index, cuddT(P), e); + if (res == NULL) { + Cudd_RecursiveDerefZdd(table, e); + return(NULL); + } + cuddDeref(e); + } else if (p_top > q_top) { + e = cuddZddUnion(zdd, P, cuddE(Q)); + if (e == NULL) return(NULL); + cuddRef(e); + res = cuddZddGetNode(zdd, Q->index, cuddT(Q), e); + if (res == NULL) { + Cudd_RecursiveDerefZdd(table, e); + return(NULL); + } + cuddDeref(e); + } else { + t = cuddZddUnion(zdd, cuddT(P), cuddT(Q)); + if (t == NULL) return(NULL); + cuddRef(t); + e = cuddZddUnion(zdd, cuddE(P), cuddE(Q)); + if (e == NULL) { + Cudd_RecursiveDerefZdd(table, t); + return(NULL); + } + cuddRef(e); + res = cuddZddGetNode(zdd, P->index, t, e); + if (res == NULL) { + Cudd_RecursiveDerefZdd(table, t); + Cudd_RecursiveDerefZdd(table, e); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + } + + cuddCacheInsert2(table, cuddZddUnion, P, Q, res); + + return(res); + +} /* end of cuddZddUnion */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddIntersect.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddZddIntersect( + DdManager * zdd, + DdNode * P, + DdNode * Q) +{ + int p_top, q_top; + DdNode *empty = DD_ZERO(zdd), *t, *e, *res; + DdManager *table = zdd; + + statLine(zdd); + if (P == empty) + return(empty); + if (Q == empty) + return(empty); + if (P == Q) + return(P); + + /* Check cache. */ + res = cuddCacheLookup2Zdd(table, cuddZddIntersect, P, Q); + if (res != NULL) + return(res); + + if (cuddIsConstant(P)) + p_top = P->index; + else + p_top = zdd->permZ[P->index]; + if (cuddIsConstant(Q)) + q_top = Q->index; + else + q_top = zdd->permZ[Q->index]; + if (p_top < q_top) { + res = cuddZddIntersect(zdd, cuddE(P), Q); + if (res == NULL) return(NULL); + } else if (p_top > q_top) { + res = cuddZddIntersect(zdd, P, cuddE(Q)); + if (res == NULL) return(NULL); + } else { + t = cuddZddIntersect(zdd, cuddT(P), cuddT(Q)); + if (t == NULL) return(NULL); + cuddRef(t); + e = cuddZddIntersect(zdd, cuddE(P), cuddE(Q)); + if (e == NULL) { + Cudd_RecursiveDerefZdd(table, t); + return(NULL); + } + cuddRef(e); + res = cuddZddGetNode(zdd, P->index, t, e); + if (res == NULL) { + Cudd_RecursiveDerefZdd(table, t); + Cudd_RecursiveDerefZdd(table, e); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + } + + cuddCacheInsert2(table, cuddZddIntersect, P, Q, res); + + return(res); + +} /* end of cuddZddIntersect */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddDiff.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddZddDiff( + DdManager * zdd, + DdNode * P, + DdNode * Q) +{ + int p_top, q_top; + DdNode *empty = DD_ZERO(zdd), *t, *e, *res; + DdManager *table = zdd; + + statLine(zdd); + if (P == empty) + return(empty); + if (Q == empty) + return(P); + if (P == Q) + return(empty); + + /* Check cache. The cache is shared by Cudd_zddDiffConst(). */ + res = cuddCacheLookup2Zdd(table, cuddZddDiff, P, Q); + if (res != NULL && res != DD_NON_CONSTANT) + return(res); + + if (cuddIsConstant(P)) + p_top = P->index; + else + p_top = zdd->permZ[P->index]; + if (cuddIsConstant(Q)) + q_top = Q->index; + else + q_top = zdd->permZ[Q->index]; + if (p_top < q_top) { + e = cuddZddDiff(zdd, cuddE(P), Q); + if (e == NULL) return(NULL); + cuddRef(e); + res = cuddZddGetNode(zdd, P->index, cuddT(P), e); + if (res == NULL) { + Cudd_RecursiveDerefZdd(table, e); + return(NULL); + } + cuddDeref(e); + } else if (p_top > q_top) { + res = cuddZddDiff(zdd, P, cuddE(Q)); + if (res == NULL) return(NULL); + } else { + t = cuddZddDiff(zdd, cuddT(P), cuddT(Q)); + if (t == NULL) return(NULL); + cuddRef(t); + e = cuddZddDiff(zdd, cuddE(P), cuddE(Q)); + if (e == NULL) { + Cudd_RecursiveDerefZdd(table, t); + return(NULL); + } + cuddRef(e); + res = cuddZddGetNode(zdd, P->index, t, e); + if (res == NULL) { + Cudd_RecursiveDerefZdd(table, t); + Cudd_RecursiveDerefZdd(table, e); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + } + + cuddCacheInsert2(table, cuddZddDiff, P, Q, res); + + return(res); + +} /* end of cuddZddDiff */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddChange.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddZddChangeAux( + DdManager * zdd, + DdNode * P, + DdNode * zvar) +{ + int top_var, level; + DdNode *res, *t, *e; + DdNode *base = DD_ONE(zdd); + DdNode *empty = DD_ZERO(zdd); + + statLine(zdd); + if (P == empty) + return(empty); + if (P == base) + return(zvar); + + /* Check cache. */ + res = cuddCacheLookup2Zdd(zdd, cuddZddChangeAux, P, zvar); + if (res != NULL) + return(res); + + top_var = zdd->permZ[P->index]; + level = zdd->permZ[zvar->index]; + + if (top_var > level) { + res = cuddZddGetNode(zdd, zvar->index, P, DD_ZERO(zdd)); + if (res == NULL) return(NULL); + } else if (top_var == level) { + res = cuddZddGetNode(zdd, zvar->index, cuddE(P), cuddT(P)); + if (res == NULL) return(NULL); + } else { + t = cuddZddChangeAux(zdd, cuddT(P), zvar); + if (t == NULL) return(NULL); + cuddRef(t); + e = cuddZddChangeAux(zdd, cuddE(P), zvar); + if (e == NULL) { + Cudd_RecursiveDerefZdd(zdd, t); + return(NULL); + } + cuddRef(e); + res = cuddZddGetNode(zdd, P->index, t, e); + if (res == NULL) { + Cudd_RecursiveDerefZdd(zdd, t); + Cudd_RecursiveDerefZdd(zdd, e); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + } + + cuddCacheInsert2(zdd, cuddZddChangeAux, P, zvar, res); + + return(res); + +} /* end of cuddZddChangeAux */ + + +/**Function******************************************************************** + + Synopsis [Computes the positive cofactor of a ZDD w.r.t. a variable.] + + Description [Computes the positive cofactor of a ZDD w.r.t. a + variable. In terms of combinations, the result is the set of all + combinations in which the variable is asserted. Returns a pointer to + the result if successful; NULL otherwise. cuddZddSubset1 performs + the same function as Cudd_zddSubset1, but does not restart if + reordering has taken place. Therefore it can be called from within a + recursive procedure.] + + SideEffects [None] + + SeeAlso [cuddZddSubset0 Cudd_zddSubset1] + +******************************************************************************/ +DdNode * +cuddZddSubset1( + DdManager * dd, + DdNode * P, + int var) +{ + DdNode *zvar, *r; + DdNode *base, *empty; + + base = DD_ONE(dd); + empty = DD_ZERO(dd); + + zvar = cuddUniqueInterZdd(dd, var, base, empty); + if (zvar == NULL) { + return(NULL); + } else { + cuddRef(zvar); + r = zdd_subset1_aux(dd, P, zvar); + if (r == NULL) { + Cudd_RecursiveDerefZdd(dd, zvar); + return(NULL); + } + cuddRef(r); + Cudd_RecursiveDerefZdd(dd, zvar); + } + + cuddDeref(r); + return(r); + +} /* end of cuddZddSubset1 */ + + +/**Function******************************************************************** + + Synopsis [Computes the negative cofactor of a ZDD w.r.t. a variable.] + + Description [Computes the negative cofactor of a ZDD w.r.t. a + variable. In terms of combinations, the result is the set of all + combinations in which the variable is negated. Returns a pointer to + the result if successful; NULL otherwise. cuddZddSubset0 performs + the same function as Cudd_zddSubset0, but does not restart if + reordering has taken place. Therefore it can be called from within a + recursive procedure.] + + SideEffects [None] + + SeeAlso [cuddZddSubset1 Cudd_zddSubset0] + +******************************************************************************/ +DdNode * +cuddZddSubset0( + DdManager * dd, + DdNode * P, + int var) +{ + DdNode *zvar, *r; + DdNode *base, *empty; + + base = DD_ONE(dd); + empty = DD_ZERO(dd); + + zvar = cuddUniqueInterZdd(dd, var, base, empty); + if (zvar == NULL) { + return(NULL); + } else { + cuddRef(zvar); + r = zdd_subset0_aux(dd, P, zvar); + if (r == NULL) { + Cudd_RecursiveDerefZdd(dd, zvar); + return(NULL); + } + cuddRef(r); + Cudd_RecursiveDerefZdd(dd, zvar); + } + + cuddDeref(r); + return(r); + +} /* end of cuddZddSubset0 */ + + +/**Function******************************************************************** + + Synopsis [Substitutes a variable with its complement in a ZDD.] + + Description [Substitutes a variable with its complement in a ZDD. + returns a pointer to the result if successful; NULL + otherwise. cuddZddChange performs the same function as + Cudd_zddChange, but does not restart if reordering has taken + place. Therefore it can be called from within a recursive + procedure.] + + SideEffects [None] + + SeeAlso [Cudd_zddChange] + +******************************************************************************/ +DdNode * +cuddZddChange( + DdManager * dd, + DdNode * P, + int var) +{ + DdNode *zvar, *res; + + zvar = cuddUniqueInterZdd(dd, var, DD_ONE(dd), DD_ZERO(dd)); + if (zvar == NULL) return(NULL); + cuddRef(zvar); + + res = cuddZddChangeAux(dd, P, zvar); + if (res == NULL) { + Cudd_RecursiveDerefZdd(dd,zvar); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDerefZdd(dd,zvar); + cuddDeref(res); + return(res); + +} /* end of cuddZddChange */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddSubset1.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static DdNode * +zdd_subset1_aux( + DdManager * zdd, + DdNode * P, + DdNode * zvar) +{ + int top_var, level; + DdNode *res, *t, *e; + DdNode *base, *empty; + + statLine(zdd); + base = DD_ONE(zdd); + empty = DD_ZERO(zdd); + + /* Check cache. */ + res = cuddCacheLookup2Zdd(zdd, zdd_subset1_aux, P, zvar); + if (res != NULL) + return(res); + + if (cuddIsConstant(P)) { + res = empty; + cuddCacheInsert2(zdd, zdd_subset1_aux, P, zvar, res); + return(res); + } + + top_var = zdd->permZ[P->index]; + level = zdd->permZ[zvar->index]; + + if (top_var > level) { + res = empty; + } else if (top_var == level) { + res = cuddT(P); + } else { + t = zdd_subset1_aux(zdd, cuddT(P), zvar); + if (t == NULL) return(NULL); + cuddRef(t); + e = zdd_subset1_aux(zdd, cuddE(P), zvar); + if (e == NULL) { + Cudd_RecursiveDerefZdd(zdd, t); + return(NULL); + } + cuddRef(e); + res = cuddZddGetNode(zdd, P->index, t, e); + if (res == NULL) { + Cudd_RecursiveDerefZdd(zdd, t); + Cudd_RecursiveDerefZdd(zdd, e); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + } + + cuddCacheInsert2(zdd, zdd_subset1_aux, P, zvar, res); + + return(res); + +} /* end of zdd_subset1_aux */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddSubset0.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static DdNode * +zdd_subset0_aux( + DdManager * zdd, + DdNode * P, + DdNode * zvar) +{ + int top_var, level; + DdNode *res, *t, *e; + DdNode *base, *empty; + + statLine(zdd); + base = DD_ONE(zdd); + empty = DD_ZERO(zdd); + + /* Check cache. */ + res = cuddCacheLookup2Zdd(zdd, zdd_subset0_aux, P, zvar); + if (res != NULL) + return(res); + + if (cuddIsConstant(P)) { + res = P; + cuddCacheInsert2(zdd, zdd_subset0_aux, P, zvar, res); + return(res); + } + + top_var = zdd->permZ[P->index]; + level = zdd->permZ[zvar->index]; + + if (top_var > level) { + res = P; + } + else if (top_var == level) { + res = cuddE(P); + } + else { + t = zdd_subset0_aux(zdd, cuddT(P), zvar); + if (t == NULL) return(NULL); + cuddRef(t); + e = zdd_subset0_aux(zdd, cuddE(P), zvar); + if (e == NULL) { + Cudd_RecursiveDerefZdd(zdd, t); + return(NULL); + } + cuddRef(e); + res = cuddZddGetNode(zdd, P->index, t, e); + if (res == NULL) { + Cudd_RecursiveDerefZdd(zdd, t); + Cudd_RecursiveDerefZdd(zdd, e); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + } + + cuddCacheInsert2(zdd, zdd_subset0_aux, P, zvar, res); + + return(res); + +} /* end of zdd_subset0_aux */ + + +/**Function******************************************************************** + + Synopsis [Replaces variables with constants if possible (part of + canonical form).] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +zddVarToConst( + DdNode * f, + DdNode ** gp, + DdNode ** hp, + DdNode * base, + DdNode * empty) +{ + DdNode *g = *gp; + DdNode *h = *hp; + + if (f == g) { /* ITE(F,F,H) = ITE(F,1,H) = F + H */ + *gp = base; + } + + if (f == h) { /* ITE(F,G,F) = ITE(F,G,0) = F * G */ + *hp = empty; + } + +} /* end of zddVarToConst */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddZddSymm.c b/abc_with_bb_support/src/bdd/cudd/cuddZddSymm.c new file mode 100644 index 000000000..a793ef7ac --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddZddSymm.c @@ -0,0 +1,1677 @@ +/**CFile*********************************************************************** + + FileName [cuddZddSymm.c] + + PackageName [cudd] + + Synopsis [Functions for symmetry-based ZDD variable reordering.] + + Description [External procedures included in this module: +
          +
        • Cudd_zddSymmProfile() +
        + Internal procedures included in this module: +
          +
        • cuddZddSymmCheck() +
        • cuddZddSymmSifting() +
        • cuddZddSymmSiftingConv() +
        + Static procedures included in this module: +
          +
        • cuddZddUniqueCompare() +
        • cuddZddSymmSiftingAux() +
        • cuddZddSymmSiftingConvAux() +
        • cuddZddSymmSifting_up() +
        • cuddZddSymmSifting_down() +
        • zdd_group_move() +
        • cuddZddSymmSiftingBackward() +
        • zdd_group_move_backward() +
        + ] + + SeeAlso [cuddSymmetry.c] + + Author [Hyong-Kyoon Shin, In-Ho Moon] + + Copyright [ This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define ZDD_MV_OOM (Move *)1 + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddZddSymm.c,v 1.1.1.1 2003/02/24 22:23:54 wjiang Exp $"; +#endif + +extern int *zdd_entry; + +extern int zddTotalNumberSwapping; + +static DdNode *empty; + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int cuddZddSymmSiftingAux ARGS((DdManager *table, int x, int x_low, int x_high)); +static int cuddZddSymmSiftingConvAux ARGS((DdManager *table, int x, int x_low, int x_high)); +static Move * cuddZddSymmSifting_up ARGS((DdManager *table, int x, int x_low, int initial_size)); +static Move * cuddZddSymmSifting_down ARGS((DdManager *table, int x, int x_high, int initial_size)); +static int cuddZddSymmSiftingBackward ARGS((DdManager *table, Move *moves, int size)); +static int zdd_group_move ARGS((DdManager *table, int x, int y, Move **moves)); +static int zdd_group_move_backward ARGS((DdManager *table, int x, int y)); +static void cuddZddSymmSummary ARGS((DdManager *table, int lower, int upper, int *symvars, int *symgroups)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Prints statistics on symmetric ZDD variables.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +Cudd_zddSymmProfile( + DdManager * table, + int lower, + int upper) +{ + int i, x, gbot; + int TotalSymm = 0; + int TotalSymmGroups = 0; + int nvars; + + nvars = table->sizeZ; + + for (i = lower; i < upper; i++) { + if (table->subtableZ[i].next != (unsigned) i) { + x = i; + (void) fprintf(table->out,"Group:"); + do { + (void) fprintf(table->out," %d", table->invpermZ[x]); + TotalSymm++; + gbot = x; + x = table->subtableZ[x].next; + } while (x != i); + TotalSymmGroups++; +#ifdef DD_DEBUG + assert(table->subtableZ[gbot].next == (unsigned) i); +#endif + i = gbot; + (void) fprintf(table->out,"\n"); + } + } + (void) fprintf(table->out,"Total Symmetric = %d\n", TotalSymm); + (void) fprintf(table->out,"Total Groups = %d\n", TotalSymmGroups); + +} /* end of Cudd_zddSymmProfile */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Checks for symmetry of x and y.] + + Description [Checks for symmetry of x and y. Ignores projection + functions, unless they are isolated. Returns 1 in case of + symmetry; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddSymmCheck( + DdManager * table, + int x, + int y) +{ + int i; + DdNode *f, *f0, *f1, *f01, *f00, *f11, *f10; + int yindex; + int xsymmy = 1; + int xsymmyp = 1; + int arccount = 0; + int TotalRefCount = 0; + int symm_found; + + empty = table->zero; + + yindex = table->invpermZ[y]; + for (i = table->subtableZ[x].slots - 1; i >= 0; i--) { + f = table->subtableZ[x].nodelist[i]; + while (f != NULL) { + /* Find f1, f0, f11, f10, f01, f00 */ + f1 = cuddT(f); + f0 = cuddE(f); + if ((int) f1->index == yindex) { + f11 = cuddT(f1); + f10 = cuddE(f1); + if (f10 != empty) + arccount++; + } else { + if ((int) f0->index != yindex) { + return(0); /* f bypasses layer y */ + } + f11 = empty; + f10 = f1; + } + if ((int) f0->index == yindex) { + f01 = cuddT(f0); + f00 = cuddE(f0); + if (f00 != empty) + arccount++; + } else { + f01 = empty; + f00 = f0; + } + if (f01 != f10) + xsymmy = 0; + if (f11 != f00) + xsymmyp = 0; + if ((xsymmy == 0) && (xsymmyp == 0)) + return(0); + + f = f->next; + } /* for each element of the collision list */ + } /* for each slot of the subtable */ + + /* Calculate the total reference counts of y + ** whose else arc is not empty. + */ + for (i = table->subtableZ[y].slots - 1; i >= 0; i--) { + f = table->subtableZ[y].nodelist[i]; + while (f != NIL(DdNode)) { + if (cuddE(f) != empty) + TotalRefCount += f->ref; + f = f->next; + } + } + + symm_found = (arccount == TotalRefCount); +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + if (symm_found) { + int xindex = table->invpermZ[x]; + (void) fprintf(table->out, + "Found symmetry! x =%d\ty = %d\tPos(%d,%d)\n", + xindex,yindex,x,y); + } +#endif + + return(symm_found); + +} /* end cuddZddSymmCheck */ + + +/**Function******************************************************************** + + Synopsis [Symmetric sifting algorithm for ZDDs.] + + Description [Symmetric sifting algorithm. + Assumes that no dead nodes are present. +
          +
        1. Order all the variables according to the number of entries in + each unique subtable. +
        2. Sift the variable up and down, remembering each time the total + size of the ZDD heap and grouping variables that are symmetric. +
        3. Select the best permutation. +
        4. Repeat 3 and 4 for all variables. +
        + Returns 1 plus the number of symmetric variables if successful; 0 + otherwise.] + + SideEffects [None] + + SeeAlso [cuddZddSymmSiftingConv] + +******************************************************************************/ +int +cuddZddSymmSifting( + DdManager * table, + int lower, + int upper) +{ + int i; + int *var; + int nvars; + int x; + int result; + int symvars; + int symgroups; + int iteration; +#ifdef DD_STATS + int previousSize; +#endif + + nvars = table->sizeZ; + + /* Find order in which to sift variables. */ + var = NULL; + zdd_entry = ALLOC(int, nvars); + if (zdd_entry == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddZddSymmSiftingOutOfMem; + } + var = ALLOC(int, nvars); + if (var == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddZddSymmSiftingOutOfMem; + } + + for (i = 0; i < nvars; i++) { + x = table->permZ[i]; + zdd_entry[i] = table->subtableZ[x].keys; + var[i] = i; + } + + qsort((void *)var, nvars, sizeof(int), (int (*)(const void *, const void *))cuddZddUniqueCompare); + + /* Initialize the symmetry of each subtable to itself. */ + for (i = lower; i <= upper; i++) + table->subtableZ[i].next = i; + + iteration = ddMin(table->siftMaxVar, nvars); + for (i = 0; i < iteration; i++) { + if (zddTotalNumberSwapping >= table->siftMaxSwap) + break; + x = table->permZ[var[i]]; +#ifdef DD_STATS + previousSize = table->keysZ; +#endif + if (x < lower || x > upper) continue; + if (table->subtableZ[x].next == (unsigned) x) { + result = cuddZddSymmSiftingAux(table, x, lower, upper); + if (!result) + goto cuddZddSymmSiftingOutOfMem; +#ifdef DD_STATS + if (table->keysZ < (unsigned) previousSize) { + (void) fprintf(table->out,"-"); + } else if (table->keysZ > (unsigned) previousSize) { + (void) fprintf(table->out,"+"); +#ifdef DD_VERBOSE + (void) fprintf(table->out,"\nSize increased from %d to %d while sifting variable %d\n", previousSize, table->keysZ, var[i]); +#endif + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + } + + FREE(var); + FREE(zdd_entry); + + cuddZddSymmSummary(table, lower, upper, &symvars, &symgroups); + +#ifdef DD_STATS + (void) fprintf(table->out,"\n#:S_SIFTING %8d: symmetric variables\n",symvars); + (void) fprintf(table->out,"#:G_SIFTING %8d: symmetric groups\n",symgroups); +#endif + + return(1+symvars); + +cuddZddSymmSiftingOutOfMem: + + if (zdd_entry != NULL) + FREE(zdd_entry); + if (var != NULL) + FREE(var); + + return(0); + +} /* end of cuddZddSymmSifting */ + + +/**Function******************************************************************** + + Synopsis [Symmetric sifting to convergence algorithm for ZDDs.] + + Description [Symmetric sifting to convergence algorithm for ZDDs. + Assumes that no dead nodes are present. +
          +
        1. Order all the variables according to the number of entries in + each unique subtable. +
        2. Sift the variable up and down, remembering each time the total + size of the ZDD heap and grouping variables that are symmetric. +
        3. Select the best permutation. +
        4. Repeat 3 and 4 for all variables. +
        5. Repeat 1-4 until no further improvement. +
        + Returns 1 plus the number of symmetric variables if successful; 0 + otherwise.] + + SideEffects [None] + + SeeAlso [cuddZddSymmSifting] + +******************************************************************************/ +int +cuddZddSymmSiftingConv( + DdManager * table, + int lower, + int upper) +{ + int i; + int *var; + int nvars; + int initialSize; + int x; + int result; + int symvars; + int symgroups; + int classes; + int iteration; +#ifdef DD_STATS + int previousSize; +#endif + + initialSize = table->keysZ; + + nvars = table->sizeZ; + + /* Find order in which to sift variables. */ + var = NULL; + zdd_entry = ALLOC(int, nvars); + if (zdd_entry == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddZddSymmSiftingConvOutOfMem; + } + var = ALLOC(int, nvars); + if (var == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddZddSymmSiftingConvOutOfMem; + } + + for (i = 0; i < nvars; i++) { + x = table->permZ[i]; + zdd_entry[i] = table->subtableZ[x].keys; + var[i] = i; + } + + qsort((void *)var, nvars, sizeof(int), (int (*)(const void *, const void *))cuddZddUniqueCompare); + + /* Initialize the symmetry of each subtable to itself + ** for first pass of converging symmetric sifting. + */ + for (i = lower; i <= upper; i++) + table->subtableZ[i].next = i; + + iteration = ddMin(table->siftMaxVar, table->sizeZ); + for (i = 0; i < iteration; i++) { + if (zddTotalNumberSwapping >= table->siftMaxSwap) + break; + x = table->permZ[var[i]]; + if (x < lower || x > upper) continue; + /* Only sift if not in symmetry group already. */ + if (table->subtableZ[x].next == (unsigned) x) { +#ifdef DD_STATS + previousSize = table->keysZ; +#endif + result = cuddZddSymmSiftingAux(table, x, lower, upper); + if (!result) + goto cuddZddSymmSiftingConvOutOfMem; +#ifdef DD_STATS + if (table->keysZ < (unsigned) previousSize) { + (void) fprintf(table->out,"-"); + } else if (table->keysZ > (unsigned) previousSize) { + (void) fprintf(table->out,"+"); +#ifdef DD_VERBOSE + (void) fprintf(table->out,"\nSize increased from %d to %d while sifting variable %d\n", previousSize, table->keysZ, var[i]); +#endif + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + } + + /* Sifting now until convergence. */ + while ((unsigned) initialSize > table->keysZ) { + initialSize = table->keysZ; +#ifdef DD_STATS + (void) fprintf(table->out,"\n"); +#endif + /* Here we consider only one representative for each symmetry class. */ + for (x = lower, classes = 0; x <= upper; x++, classes++) { + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + /* Here x is the largest index in a group. + ** Groups consists of adjacent variables. + ** Hence, the next increment of x will move it to a new group. + */ + i = table->invpermZ[x]; + zdd_entry[i] = table->subtableZ[x].keys; + var[classes] = i; + } + + qsort((void *)var,classes,sizeof(int),(int (*)(const void *, const void *))cuddZddUniqueCompare); + + /* Now sift. */ + iteration = ddMin(table->siftMaxVar, nvars); + for (i = 0; i < iteration; i++) { + if (zddTotalNumberSwapping >= table->siftMaxSwap) + break; + x = table->permZ[var[i]]; + if ((unsigned) x >= table->subtableZ[x].next) { +#ifdef DD_STATS + previousSize = table->keysZ; +#endif + result = cuddZddSymmSiftingConvAux(table, x, lower, upper); + if (!result) + goto cuddZddSymmSiftingConvOutOfMem; +#ifdef DD_STATS + if (table->keysZ < (unsigned) previousSize) { + (void) fprintf(table->out,"-"); + } else if (table->keysZ > (unsigned) previousSize) { + (void) fprintf(table->out,"+"); +#ifdef DD_VERBOSE + (void) fprintf(table->out,"\nSize increased from %d to %d while sifting variable %d\n", previousSize, table->keysZ, var[i]); +#endif + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + } /* for */ + } + + cuddZddSymmSummary(table, lower, upper, &symvars, &symgroups); + +#ifdef DD_STATS + (void) fprintf(table->out,"\n#:S_SIFTING %8d: symmetric variables\n", + symvars); + (void) fprintf(table->out,"#:G_SIFTING %8d: symmetric groups\n", + symgroups); +#endif + + FREE(var); + FREE(zdd_entry); + + return(1+symvars); + +cuddZddSymmSiftingConvOutOfMem: + + if (zdd_entry != NULL) + FREE(zdd_entry); + if (var != NULL) + FREE(var); + + return(0); + +} /* end of cuddZddSymmSiftingConv */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Given x_low <= x <= x_high moves x up and down between the + boundaries.] + + Description [Given x_low <= x <= x_high moves x up and down between the + boundaries. Finds the best position and does the required changes. + Assumes that x is not part of a symmetry group. Returns 1 if + successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddZddSymmSiftingAux( + DdManager * table, + int x, + int x_low, + int x_high) +{ + Move *move; + Move *move_up; /* list of up move */ + Move *move_down; /* list of down move */ + int initial_size; + int result; + int i; + int topbot; /* index to either top or bottom of symmetry group */ + int init_group_size, final_group_size; + + initial_size = table->keysZ; + + move_down = NULL; + move_up = NULL; + + /* Look for consecutive symmetries above x. */ + for (i = x; i > x_low; i--) { + if (!cuddZddSymmCheck(table, i - 1, i)) + break; + /* find top of i-1's symmetry */ + topbot = table->subtableZ[i - 1].next; + table->subtableZ[i - 1].next = i; + table->subtableZ[x].next = topbot; + /* x is bottom of group so its symmetry is top of i-1's + group */ + i = topbot + 1; /* add 1 for i--, new i is top of symm group */ + } + /* Look for consecutive symmetries below x. */ + for (i = x; i < x_high; i++) { + if (!cuddZddSymmCheck(table, i, i + 1)) + break; + /* find bottom of i+1's symm group */ + topbot = i + 1; + while ((unsigned) topbot < table->subtableZ[topbot].next) + topbot = table->subtableZ[topbot].next; + + table->subtableZ[topbot].next = table->subtableZ[i].next; + table->subtableZ[i].next = i + 1; + i = topbot - 1; /* add 1 for i++, + new i is bottom of symm group */ + } + + /* Now x maybe in the middle of a symmetry group. */ + if (x == x_low) { /* Sift down */ + /* Find bottom of x's symm group */ + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + + i = table->subtableZ[x].next; + init_group_size = x - i + 1; + + move_down = cuddZddSymmSifting_down(table, x, x_high, + initial_size); + /* after that point x --> x_high, unless early term */ + if (move_down == ZDD_MV_OOM) + goto cuddZddSymmSiftingAuxOutOfMem; + + if (move_down == NULL || + table->subtableZ[move_down->y].next != move_down->y) { + /* symmetry detected may have to make another complete + pass */ + if (move_down != NULL) + x = move_down->y; + else + x = table->subtableZ[x].next; + i = x; + while ((unsigned) i < table->subtableZ[i].next) { + i = table->subtableZ[i].next; + } + final_group_size = i - x + 1; + + if (init_group_size == final_group_size) { + /* No new symmetry groups detected, + return to best position */ + result = cuddZddSymmSiftingBackward(table, + move_down, initial_size); + } + else { + initial_size = table->keysZ; + move_up = cuddZddSymmSifting_up(table, x, x_low, + initial_size); + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + } + } + else { + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + /* move backward and stop at best position */ + } + if (!result) + goto cuddZddSymmSiftingAuxOutOfMem; + } + else if (x == x_high) { /* Sift up */ + /* Find top of x's symm group */ + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + x = table->subtableZ[x].next; + + i = x; + while ((unsigned) i < table->subtableZ[i].next) { + i = table->subtableZ[i].next; + } + init_group_size = i - x + 1; + + move_up = cuddZddSymmSifting_up(table, x, x_low, initial_size); + /* after that point x --> x_low, unless early term */ + if (move_up == ZDD_MV_OOM) + goto cuddZddSymmSiftingAuxOutOfMem; + + if (move_up == NULL || + table->subtableZ[move_up->x].next != move_up->x) { + /* symmetry detected may have to make another complete + pass */ + if (move_up != NULL) + x = move_up->x; + else { + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + } + i = table->subtableZ[x].next; + final_group_size = x - i + 1; + + if (init_group_size == final_group_size) { + /* No new symmetry groups detected, + return to best position */ + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + } + else { + initial_size = table->keysZ; + move_down = cuddZddSymmSifting_down(table, x, x_high, + initial_size); + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + } + } + else { + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + /* move backward and stop at best position */ + } + if (!result) + goto cuddZddSymmSiftingAuxOutOfMem; + } + else if ((x - x_low) > (x_high - x)) { /* must go down first: + shorter */ + /* Find bottom of x's symm group */ + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + + move_down = cuddZddSymmSifting_down(table, x, x_high, + initial_size); + /* after that point x --> x_high, unless early term */ + if (move_down == ZDD_MV_OOM) + goto cuddZddSymmSiftingAuxOutOfMem; + + if (move_down != NULL) { + x = move_down->y; + } + else { + x = table->subtableZ[x].next; + } + i = x; + while ((unsigned) i < table->subtableZ[i].next) { + i = table->subtableZ[i].next; + } + init_group_size = i - x + 1; + + move_up = cuddZddSymmSifting_up(table, x, x_low, initial_size); + if (move_up == ZDD_MV_OOM) + goto cuddZddSymmSiftingAuxOutOfMem; + + if (move_up == NULL || + table->subtableZ[move_up->x].next != move_up->x) { + /* symmetry detected may have to make another complete + pass */ + if (move_up != NULL) { + x = move_up->x; + } + else { + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + } + i = table->subtableZ[x].next; + final_group_size = x - i + 1; + + if (init_group_size == final_group_size) { + /* No new symmetry groups detected, + return to best position */ + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + } + else { + while (move_down != NULL) { + move = move_down->next; + cuddDeallocNode(table, (DdNode *)move_down); + move_down = move; + } + initial_size = table->keysZ; + move_down = cuddZddSymmSifting_down(table, x, x_high, + initial_size); + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + } + } + else { + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + /* move backward and stop at best position */ + } + if (!result) + goto cuddZddSymmSiftingAuxOutOfMem; + } + else { /* moving up first:shorter */ + /* Find top of x's symmetry group */ + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + x = table->subtableZ[x].next; + + move_up = cuddZddSymmSifting_up(table, x, x_low, initial_size); + /* after that point x --> x_high, unless early term */ + if (move_up == ZDD_MV_OOM) + goto cuddZddSymmSiftingAuxOutOfMem; + + if (move_up != NULL) { + x = move_up->x; + } + else { + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + } + i = table->subtableZ[x].next; + init_group_size = x - i + 1; + + move_down = cuddZddSymmSifting_down(table, x, x_high, + initial_size); + if (move_down == ZDD_MV_OOM) + goto cuddZddSymmSiftingAuxOutOfMem; + + if (move_down == NULL || + table->subtableZ[move_down->y].next != move_down->y) { + /* symmetry detected may have to make another complete + pass */ + if (move_down != NULL) { + x = move_down->y; + } + else { + x = table->subtableZ[x].next; + } + i = x; + while ((unsigned) i < table->subtableZ[i].next) { + i = table->subtableZ[i].next; + } + final_group_size = i - x + 1; + + if (init_group_size == final_group_size) { + /* No new symmetries detected, + go back to best position */ + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + } + else { + while (move_up != NULL) { + move = move_up->next; + cuddDeallocNode(table, (DdNode *)move_up); + move_up = move; + } + initial_size = table->keysZ; + move_up = cuddZddSymmSifting_up(table, x, x_low, + initial_size); + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + } + } + else { + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + /* move backward and stop at best position */ + } + if (!result) + goto cuddZddSymmSiftingAuxOutOfMem; + } + + while (move_down != NULL) { + move = move_down->next; + cuddDeallocNode(table, (DdNode *)move_down); + move_down = move; + } + while (move_up != NULL) { + move = move_up->next; + cuddDeallocNode(table, (DdNode *)move_up); + move_up = move; + } + + return(1); + +cuddZddSymmSiftingAuxOutOfMem: + if (move_down != ZDD_MV_OOM) { + while (move_down != NULL) { + move = move_down->next; + cuddDeallocNode(table, (DdNode *)move_down); + move_down = move; + } + } + if (move_up != ZDD_MV_OOM) { + while (move_up != NULL) { + move = move_up->next; + cuddDeallocNode(table, (DdNode *)move_up); + move_up = move; + } + } + + return(0); + +} /* end of cuddZddSymmSiftingAux */ + + +/**Function******************************************************************** + + Synopsis [Given x_low <= x <= x_high moves x up and down between the + boundaries.] + + Description [Given x_low <= x <= x_high moves x up and down between the + boundaries. Finds the best position and does the required changes. + Assumes that x is either an isolated variable, or it is the bottom of + a symmetry group. All symmetries may not have been found, because of + exceeded growth limit. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddZddSymmSiftingConvAux( + DdManager * table, + int x, + int x_low, + int x_high) +{ + Move *move; + Move *move_up; /* list of up move */ + Move *move_down; /* list of down move */ + int initial_size; + int result; + int i; + int init_group_size, final_group_size; + + initial_size = table->keysZ; + + move_down = NULL; + move_up = NULL; + + if (x == x_low) { /* Sift down */ + i = table->subtableZ[x].next; + init_group_size = x - i + 1; + + move_down = cuddZddSymmSifting_down(table, x, x_high, + initial_size); + /* after that point x --> x_high, unless early term */ + if (move_down == ZDD_MV_OOM) + goto cuddZddSymmSiftingConvAuxOutOfMem; + + if (move_down == NULL || + table->subtableZ[move_down->y].next != move_down->y) { + /* symmetry detected may have to make another complete + pass */ + if (move_down != NULL) + x = move_down->y; + else { + while ((unsigned) x < table->subtableZ[x].next); + x = table->subtableZ[x].next; + x = table->subtableZ[x].next; + } + i = x; + while ((unsigned) i < table->subtableZ[i].next) { + i = table->subtableZ[i].next; + } + final_group_size = i - x + 1; + + if (init_group_size == final_group_size) { + /* No new symmetries detected, + go back to best position */ + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + } + else { + initial_size = table->keysZ; + move_up = cuddZddSymmSifting_up(table, x, x_low, + initial_size); + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + } + } + else { + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + /* move backward and stop at best position */ + } + if (!result) + goto cuddZddSymmSiftingConvAuxOutOfMem; + } + else if (x == x_high) { /* Sift up */ + /* Find top of x's symm group */ + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + x = table->subtableZ[x].next; + + i = x; + while ((unsigned) i < table->subtableZ[i].next) { + i = table->subtableZ[i].next; + } + init_group_size = i - x + 1; + + move_up = cuddZddSymmSifting_up(table, x, x_low, initial_size); + /* after that point x --> x_low, unless early term */ + if (move_up == ZDD_MV_OOM) + goto cuddZddSymmSiftingConvAuxOutOfMem; + + if (move_up == NULL || + table->subtableZ[move_up->x].next != move_up->x) { + /* symmetry detected may have to make another complete + pass */ + if (move_up != NULL) + x = move_up->x; + else { + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + } + i = table->subtableZ[x].next; + final_group_size = x - i + 1; + + if (init_group_size == final_group_size) { + /* No new symmetry groups detected, + return to best position */ + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + } + else { + initial_size = table->keysZ; + move_down = cuddZddSymmSifting_down(table, x, x_high, + initial_size); + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + } + } + else { + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + /* move backward and stop at best position */ + } + if (!result) + goto cuddZddSymmSiftingConvAuxOutOfMem; + } + else if ((x - x_low) > (x_high - x)) { /* must go down first: + shorter */ + move_down = cuddZddSymmSifting_down(table, x, x_high, + initial_size); + /* after that point x --> x_high */ + if (move_down == ZDD_MV_OOM) + goto cuddZddSymmSiftingConvAuxOutOfMem; + + if (move_down != NULL) { + x = move_down->y; + } + else { + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + x = table->subtableZ[x].next; + } + i = x; + while ((unsigned) i < table->subtableZ[i].next) { + i = table->subtableZ[i].next; + } + init_group_size = i - x + 1; + + move_up = cuddZddSymmSifting_up(table, x, x_low, initial_size); + if (move_up == ZDD_MV_OOM) + goto cuddZddSymmSiftingConvAuxOutOfMem; + + if (move_up == NULL || + table->subtableZ[move_up->x].next != move_up->x) { + /* symmetry detected may have to make another complete + pass */ + if (move_up != NULL) { + x = move_up->x; + } + else { + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + } + i = table->subtableZ[x].next; + final_group_size = x - i + 1; + + if (init_group_size == final_group_size) { + /* No new symmetry groups detected, + return to best position */ + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + } + else { + while (move_down != NULL) { + move = move_down->next; + cuddDeallocNode(table, (DdNode *)move_down); + move_down = move; + } + initial_size = table->keysZ; + move_down = cuddZddSymmSifting_down(table, x, x_high, + initial_size); + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + } + } + else { + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + /* move backward and stop at best position */ + } + if (!result) + goto cuddZddSymmSiftingConvAuxOutOfMem; + } + else { /* moving up first:shorter */ + /* Find top of x's symmetry group */ + x = table->subtableZ[x].next; + + move_up = cuddZddSymmSifting_up(table, x, x_low, initial_size); + /* after that point x --> x_high, unless early term */ + if (move_up == ZDD_MV_OOM) + goto cuddZddSymmSiftingConvAuxOutOfMem; + + if (move_up != NULL) { + x = move_up->x; + } + else { + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + } + i = table->subtableZ[x].next; + init_group_size = x - i + 1; + + move_down = cuddZddSymmSifting_down(table, x, x_high, + initial_size); + if (move_down == ZDD_MV_OOM) + goto cuddZddSymmSiftingConvAuxOutOfMem; + + if (move_down == NULL || + table->subtableZ[move_down->y].next != move_down->y) { + /* symmetry detected may have to make another complete + pass */ + if (move_down != NULL) { + x = move_down->y; + } + else { + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + x = table->subtableZ[x].next; + } + i = x; + while ((unsigned) i < table->subtableZ[i].next) { + i = table->subtableZ[i].next; + } + final_group_size = i - x + 1; + + if (init_group_size == final_group_size) { + /* No new symmetries detected, + go back to best position */ + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + } + else { + while (move_up != NULL) { + move = move_up->next; + cuddDeallocNode(table, (DdNode *)move_up); + move_up = move; + } + initial_size = table->keysZ; + move_up = cuddZddSymmSifting_up(table, x, x_low, + initial_size); + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + } + } + else { + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + /* move backward and stop at best position */ + } + if (!result) + goto cuddZddSymmSiftingConvAuxOutOfMem; + } + + while (move_down != NULL) { + move = move_down->next; + cuddDeallocNode(table, (DdNode *)move_down); + move_down = move; + } + while (move_up != NULL) { + move = move_up->next; + cuddDeallocNode(table, (DdNode *)move_up); + move_up = move; + } + + return(1); + +cuddZddSymmSiftingConvAuxOutOfMem: + if (move_down != ZDD_MV_OOM) { + while (move_down != NULL) { + move = move_down->next; + cuddDeallocNode(table, (DdNode *)move_down); + move_down = move; + } + } + if (move_up != ZDD_MV_OOM) { + while (move_up != NULL) { + move = move_up->next; + cuddDeallocNode(table, (DdNode *)move_up); + move_up = move; + } + } + + return(0); + +} /* end of cuddZddSymmSiftingConvAux */ + + +/**Function******************************************************************** + + Synopsis [Moves x up until either it reaches the bound (x_low) or + the size of the ZDD heap increases too much.] + + Description [Moves x up until either it reaches the bound (x_low) or + the size of the ZDD heap increases too much. Assumes that x is the top + of a symmetry group. Checks x for symmetry to the adjacent + variables. If symmetry is found, the symmetry group of x is merged + with the symmetry group of the other variable. Returns the set of + moves in case of success; ZDD_MV_OOM if memory is full.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static Move * +cuddZddSymmSifting_up( + DdManager * table, + int x, + int x_low, + int initial_size) +{ + Move *moves; + Move *move; + int y; + int size; + int limit_size = initial_size; + int i, gytop; + + moves = NULL; + y = cuddZddNextLow(table, x); + while (y >= x_low) { + gytop = table->subtableZ[y].next; + if (cuddZddSymmCheck(table, y, x)) { + /* Symmetry found, attach symm groups */ + table->subtableZ[y].next = x; + i = table->subtableZ[x].next; + while (table->subtableZ[i].next != (unsigned) x) + i = table->subtableZ[i].next; + table->subtableZ[i].next = gytop; + } + else if ((table->subtableZ[x].next == (unsigned) x) && + (table->subtableZ[y].next == (unsigned) y)) { + /* x and y have self symmetry */ + size = cuddZddSwapInPlace(table, y, x); + if (size == 0) + goto cuddZddSymmSifting_upOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto cuddZddSymmSifting_upOutOfMem; + move->x = y; + move->y = x; + move->size = size; + move->next = moves; + moves = move; + if ((double)size > + (double)limit_size * table->maxGrowth) + return(moves); + if (size < limit_size) + limit_size = size; + } + else { /* Group move */ + size = zdd_group_move(table, y, x, &moves); + if ((double)size > + (double)limit_size * table->maxGrowth) + return(moves); + if (size < limit_size) + limit_size = size; + } + x = gytop; + y = cuddZddNextLow(table, x); + } + + return(moves); + +cuddZddSymmSifting_upOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *)moves); + moves = move; + } + return(ZDD_MV_OOM); + +} /* end of cuddZddSymmSifting_up */ + + +/**Function******************************************************************** + + Synopsis [Moves x down until either it reaches the bound (x_high) or + the size of the ZDD heap increases too much.] + + Description [Moves x down until either it reaches the bound (x_high) + or the size of the ZDD heap increases too much. Assumes that x is the + bottom of a symmetry group. Checks x for symmetry to the adjacent + variables. If symmetry is found, the symmetry group of x is merged + with the symmetry group of the other variable. Returns the set of + moves in case of success; ZDD_MV_OOM if memory is full.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static Move * +cuddZddSymmSifting_down( + DdManager * table, + int x, + int x_high, + int initial_size) +{ + Move *moves; + Move *move; + int y; + int size; + int limit_size = initial_size; + int i, gxtop, gybot; + + moves = NULL; + y = cuddZddNextHigh(table, x); + while (y <= x_high) { + gybot = table->subtableZ[y].next; + while (table->subtableZ[gybot].next != (unsigned) y) + gybot = table->subtableZ[gybot].next; + if (cuddZddSymmCheck(table, x, y)) { + /* Symmetry found, attach symm groups */ + gxtop = table->subtableZ[x].next; + table->subtableZ[x].next = y; + i = table->subtableZ[y].next; + while (table->subtableZ[i].next != (unsigned) y) + i = table->subtableZ[i].next; + table->subtableZ[i].next = gxtop; + } + else if ((table->subtableZ[x].next == (unsigned) x) && + (table->subtableZ[y].next == (unsigned) y)) { + /* x and y have self symmetry */ + size = cuddZddSwapInPlace(table, x, y); + if (size == 0) + goto cuddZddSymmSifting_downOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto cuddZddSymmSifting_downOutOfMem; + move->x = x; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + if ((double)size > + (double)limit_size * table->maxGrowth) + return(moves); + if (size < limit_size) + limit_size = size; + x = y; + y = cuddZddNextHigh(table, x); + } + else { /* Group move */ + size = zdd_group_move(table, x, y, &moves); + if ((double)size > + (double)limit_size * table->maxGrowth) + return(moves); + if (size < limit_size) + limit_size = size; + } + x = gybot; + y = cuddZddNextHigh(table, x); + } + + return(moves); + +cuddZddSymmSifting_downOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocNode(table, (DdNode *)moves); + moves = move; + } + return(ZDD_MV_OOM); + +} /* end of cuddZddSymmSifting_down */ + + +/**Function******************************************************************** + + Synopsis [Given a set of moves, returns the ZDD heap to the position + giving the minimum size.] + + Description [Given a set of moves, returns the ZDD heap to the + position giving the minimum size. In case of ties, returns to the + closest position giving the minimum size. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddZddSymmSiftingBackward( + DdManager * table, + Move * moves, + int size) +{ + int i; + int i_best; + Move *move; + int res; + + i_best = -1; + for (move = moves, i = 0; move != NULL; move = move->next, i++) { + if (move->size < size) { + i_best = i; + size = move->size; + } + } + + for (move = moves, i = 0; move != NULL; move = move->next, i++) { + if (i == i_best) break; + if ((table->subtableZ[move->x].next == move->x) && + (table->subtableZ[move->y].next == move->y)) { + res = cuddZddSwapInPlace(table, move->x, move->y); + if (!res) return(0); + } + else { /* Group move necessary */ + res = zdd_group_move_backward(table, move->x, move->y); + } + if (i_best == -1 && res == size) + break; + } + + return(1); + +} /* end of cuddZddSymmSiftingBackward */ + + +/**Function******************************************************************** + + Synopsis [Swaps two groups.] + + Description [Swaps two groups. x is assumed to be the bottom variable + of the first group. y is assumed to be the top variable of the second + group. Updates the list of moves. Returns the number of keys in the + table if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +zdd_group_move( + DdManager * table, + int x, + int y, + Move ** moves) +{ + Move *move; + int size; + int i, temp, gxtop, gxbot, gytop, gybot, yprev; + int swapx, swapy; + +#ifdef DD_DEBUG + assert(x < y); /* we assume that x < y */ +#endif + /* Find top and bottom for the two groups. */ + gxtop = table->subtableZ[x].next; + gytop = y; + gxbot = x; + gybot = table->subtableZ[y].next; + while (table->subtableZ[gybot].next != (unsigned) y) + gybot = table->subtableZ[gybot].next; + yprev = gybot; + + while (x <= y) { + while (y > gxtop) { + /* Set correct symmetries. */ + temp = table->subtableZ[x].next; + if (temp == x) + temp = y; + i = gxtop; + for (;;) { + if (table->subtableZ[i].next == (unsigned) x) { + table->subtableZ[i].next = y; + break; + } else { + i = table->subtableZ[i].next; + } + } + if (table->subtableZ[y].next != (unsigned) y) { + table->subtableZ[x].next = table->subtableZ[y].next; + } else { + table->subtableZ[x].next = x; + } + + if (yprev != y) { + table->subtableZ[yprev].next = x; + } else { + yprev = x; + } + table->subtableZ[y].next = temp; + + size = cuddZddSwapInPlace(table, x, y); + if (size == 0) + goto zdd_group_moveOutOfMem; + swapx = x; + swapy = y; + y = x; + x--; + } /* while y > gxtop */ + + /* Trying to find the next y. */ + if (table->subtableZ[y].next <= (unsigned) y) { + gybot = y; + } else { + y = table->subtableZ[y].next; + } + + yprev = gxtop; + gxtop++; + gxbot++; + x = gxbot; + } /* while x <= y, end of group movement */ + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto zdd_group_moveOutOfMem; + move->x = swapx; + move->y = swapy; + move->size = table->keysZ; + move->next = *moves; + *moves = move; + + return(table->keysZ); + +zdd_group_moveOutOfMem: + while (*moves != NULL) { + move = (*moves)->next; + cuddDeallocNode(table, (DdNode *)(*moves)); + *moves = move; + } + return(0); + +} /* end of zdd_group_move */ + + +/**Function******************************************************************** + + Synopsis [Undoes the swap of two groups.] + + Description [Undoes the swap of two groups. x is assumed to be the + bottom variable of the first group. y is assumed to be the top + variable of the second group. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +zdd_group_move_backward( + DdManager * table, + int x, + int y) +{ + int size; + int i, temp, gxtop, gxbot, gytop, gybot, yprev; + +#ifdef DD_DEBUG + assert(x < y); /* we assume that x < y */ +#endif + /* Find top and bottom of the two groups. */ + gxtop = table->subtableZ[x].next; + gytop = y; + gxbot = x; + gybot = table->subtableZ[y].next; + while (table->subtableZ[gybot].next != (unsigned) y) + gybot = table->subtableZ[gybot].next; + yprev = gybot; + + while (x <= y) { + while (y > gxtop) { + /* Set correct symmetries. */ + temp = table->subtableZ[x].next; + if (temp == x) + temp = y; + i = gxtop; + for (;;) { + if (table->subtableZ[i].next == (unsigned) x) { + table->subtableZ[i].next = y; + break; + } else { + i = table->subtableZ[i].next; + } + } + if (table->subtableZ[y].next != (unsigned) y) { + table->subtableZ[x].next = table->subtableZ[y].next; + } else { + table->subtableZ[x].next = x; + } + + if (yprev != y) { + table->subtableZ[yprev].next = x; + } else { + yprev = x; + } + table->subtableZ[y].next = temp; + + size = cuddZddSwapInPlace(table, x, y); + if (size == 0) + return(0); + y = x; + x--; + } /* while y > gxtop */ + + /* Trying to find the next y. */ + if (table->subtableZ[y].next <= (unsigned) y) { + gybot = y; + } else { + y = table->subtableZ[y].next; + } + + yprev = gxtop; + gxtop++; + gxbot++; + x = gxbot; + } /* while x <= y, end of group movement backward */ + + return(size); + +} /* end of zdd_group_move_backward */ + + +/**Function******************************************************************** + + Synopsis [Counts numbers of symmetric variables and symmetry + groups.] + + Description [] + + SideEffects [None] + +******************************************************************************/ +static void +cuddZddSymmSummary( + DdManager * table, + int lower, + int upper, + int * symvars, + int * symgroups) +{ + int i,x,gbot; + int TotalSymm = 0; + int TotalSymmGroups = 0; + + for (i = lower; i <= upper; i++) { + if (table->subtableZ[i].next != (unsigned) i) { + TotalSymmGroups++; + x = i; + do { + TotalSymm++; + gbot = x; + x = table->subtableZ[x].next; + } while (x != i); +#ifdef DD_DEBUG + assert(table->subtableZ[gbot].next == (unsigned) i); +#endif + i = gbot; + } + } + *symvars = TotalSymm; + *symgroups = TotalSymmGroups; + + return; + +} /* end of cuddZddSymmSummary */ + diff --git a/abc_with_bb_support/src/bdd/cudd/cuddZddUtil.c b/abc_with_bb_support/src/bdd/cudd/cuddZddUtil.c new file mode 100644 index 000000000..d2408db98 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/cuddZddUtil.c @@ -0,0 +1,1021 @@ +/**CFile*********************************************************************** + + FileName [cuddZddUtil.c] + + PackageName [cudd] + + Synopsis [Utility functions for ZDDs.] + + Description [External procedures included in this module: +
          +
        • Cudd_zddPrintMinterm() +
        • Cudd_zddPrintCover() +
        • Cudd_zddPrintDebug() +
        • Cudd_zddDumpDot() +
        + Internal procedures included in this module: +
          +
        • cuddZddP() +
        + Static procedures included in this module: +
          +
        • zp2() +
        • zdd_print_minterm_aux() +
        + ] + + SeeAlso [] + + Author [Hyong-Kyoon Shin, In-Ho Moon] + + Copyright [ This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddZddUtil.c,v 1.1.1.1 2003/02/24 22:23:54 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int zp2 ARGS((DdManager *zdd, DdNode *f, st_table *t)); +static void zdd_print_minterm_aux ARGS((DdManager *zdd, DdNode *node, int level, int *list)); +static void zddPrintCoverAux ARGS((DdManager *zdd, DdNode *node, int level, int *list)); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Prints a disjoint sum of product form for a ZDD.] + + Description [Prints a disjoint sum of product form for a ZDD. Returns 1 + if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_zddPrintDebug Cudd_zddPrintCover] + +******************************************************************************/ +int +Cudd_zddPrintMinterm( + DdManager * zdd, + DdNode * node) +{ + int i, size; + int *list; + + size = (int)zdd->sizeZ; + list = ALLOC(int, size); + if (list == NULL) { + zdd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (i = 0; i < size; i++) list[i] = 3; /* bogus value should disappear */ + zdd_print_minterm_aux(zdd, node, 0, list); + FREE(list); + return(1); + +} /* end of Cudd_zddPrintMinterm */ + + +/**Function******************************************************************** + + Synopsis [Prints a sum of products from a ZDD representing a cover.] + + Description [Prints a sum of products from a ZDD representing a cover. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_zddPrintMinterm] + +******************************************************************************/ +int +Cudd_zddPrintCover( + DdManager * zdd, + DdNode * node) +{ + int i, size; + int *list; + + size = (int)zdd->sizeZ; + if (size % 2 != 0) return(0); /* number of variables should be even */ + list = ALLOC(int, size); + if (list == NULL) { + zdd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (i = 0; i < size; i++) list[i] = 3; /* bogus value should disappear */ + zddPrintCoverAux(zdd, node, 0, list); + FREE(list); + return(1); + +} /* end of Cudd_zddPrintCover */ + + +/**Function******************************************************************** + + Synopsis [Prints to the standard output a ZDD and its statistics.] + + Description [Prints to the standard output a DD and its statistics. + The statistics include the number of nodes and the number of minterms. + (The number of minterms is also the number of combinations in the set.) + The statistics are printed if pr > 0. Specifically: +
          +
        • pr = 0 : prints nothing +
        • pr = 1 : prints counts of nodes and minterms +
        • pr = 2 : prints counts + disjoint sum of products +
        • pr = 3 : prints counts + list of nodes +
        • pr > 3 : prints counts + disjoint sum of products + list of nodes +
        + Returns 1 if successful; 0 otherwise. + ] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +Cudd_zddPrintDebug( + DdManager * zdd, + DdNode * f, + int n, + int pr) +{ + DdNode *empty = DD_ZERO(zdd); + int nodes; + double minterms; + int retval = 1; + + if (f == empty && pr > 0) { + (void) fprintf(zdd->out,": is the empty ZDD\n"); + (void) fflush(zdd->out); + return(1); + } + + if (pr > 0) { + nodes = Cudd_zddDagSize(f); + if (nodes == CUDD_OUT_OF_MEM) retval = 0; + minterms = Cudd_zddCountMinterm(zdd, f, n); + if (minterms == (double)CUDD_OUT_OF_MEM) retval = 0; + (void) fprintf(zdd->out,": %d nodes %g minterms\n", + nodes, minterms); + if (pr > 2) + if (!cuddZddP(zdd, f)) retval = 0; + if (pr == 2 || pr > 3) { + if (!Cudd_zddPrintMinterm(zdd, f)) retval = 0; + (void) fprintf(zdd->out,"\n"); + } + (void) fflush(zdd->out); + } + return(retval); + +} /* end of Cudd_zddPrintDebug */ + + + +/**Function******************************************************************** + + Synopsis [Finds the first path of a ZDD.] + + Description [Defines an iterator on the paths of a ZDD + and finds its first path. Returns a generator that contains the + information necessary to continue the enumeration if successful; NULL + otherwise.

        + A path is represented as an array of literals, which are integers in + {0, 1, 2}; 0 represents an else arc out of a node, 1 represents a then arc + out of a node, and 2 stands for the absence of a node. + The size of the array equals the number of variables in the manager at + the time Cudd_zddFirstCube is called.

        + The paths that end in the empty terminal are not enumerated.] + + SideEffects [The first path is returned as a side effect.] + + SeeAlso [Cudd_zddForeachPath Cudd_zddNextPath Cudd_GenFree + Cudd_IsGenEmpty] + +******************************************************************************/ +DdGen * +Cudd_zddFirstPath( + DdManager * zdd, + DdNode * f, + int ** path) +{ + DdGen *gen; + DdNode *top, *next, *prev; + int i; + int nvars; + + /* Sanity Check. */ + if (zdd == NULL || f == NULL) return(NULL); + + /* Allocate generator an initialize it. */ + gen = ALLOC(DdGen,1); + if (gen == NULL) { + zdd->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + + gen->manager = zdd; + gen->type = CUDD_GEN_ZDD_PATHS; + gen->status = CUDD_GEN_EMPTY; + gen->gen.cubes.cube = NULL; + gen->gen.cubes.value = DD_ZERO_VAL; + gen->stack.sp = 0; + gen->stack.stack = NULL; + gen->node = NULL; + + nvars = zdd->sizeZ; + gen->gen.cubes.cube = ALLOC(int,nvars); + if (gen->gen.cubes.cube == NULL) { + zdd->errorCode = CUDD_MEMORY_OUT; + FREE(gen); + return(NULL); + } + for (i = 0; i < nvars; i++) gen->gen.cubes.cube[i] = 2; + + /* The maximum stack depth is one plus the number of variables. + ** because a path may have nodes at all levels, including the + ** constant level. + */ + gen->stack.stack = ALLOC(DdNode *, nvars+1); + if (gen->stack.stack == NULL) { + zdd->errorCode = CUDD_MEMORY_OUT; + FREE(gen->gen.cubes.cube); + FREE(gen); + return(NULL); + } + for (i = 0; i <= nvars; i++) gen->stack.stack[i] = NULL; + + /* Find the first path of the ZDD. */ + gen->stack.stack[gen->stack.sp] = f; gen->stack.sp++; + + while (1) { + top = gen->stack.stack[gen->stack.sp-1]; + if (!cuddIsConstant(top)) { + /* Take the else branch first. */ + gen->gen.cubes.cube[top->index] = 0; + next = cuddE(top); + gen->stack.stack[gen->stack.sp] = next; gen->stack.sp++; + } else if (top == DD_ZERO(zdd)) { + /* Backtrack. */ + while (1) { + if (gen->stack.sp == 1) { + /* The current node has no predecessor. */ + gen->status = CUDD_GEN_EMPTY; + gen->stack.sp--; + goto done; + } + prev = gen->stack.stack[gen->stack.sp-2]; + next = cuddT(prev); + if (next != top) { /* follow the then branch next */ + gen->gen.cubes.cube[prev->index] = 1; + gen->stack.stack[gen->stack.sp-1] = next; + break; + } + /* Pop the stack and try again. */ + gen->gen.cubes.cube[prev->index] = 2; + gen->stack.sp--; + top = gen->stack.stack[gen->stack.sp-1]; + } + } else { + gen->status = CUDD_GEN_NONEMPTY; + gen->gen.cubes.value = cuddV(top); + goto done; + } + } + +done: + *path = gen->gen.cubes.cube; + return(gen); + +} /* end of Cudd_zddFirstPath */ + + +/**Function******************************************************************** + + Synopsis [Generates the next path of a ZDD.] + + Description [Generates the next path of a ZDD onset, + using generator gen. Returns 0 if the enumeration is completed; 1 + otherwise.] + + SideEffects [The path is returned as a side effect. The + generator is modified.] + + SeeAlso [Cudd_zddForeachPath Cudd_zddFirstPath Cudd_GenFree + Cudd_IsGenEmpty] + +******************************************************************************/ +int +Cudd_zddNextPath( + DdGen * gen, + int ** path) +{ + DdNode *top, *next, *prev; + DdManager *zdd = gen->manager; + + /* Backtrack from previously reached terminal node. */ + while (1) { + if (gen->stack.sp == 1) { + /* The current node has no predecessor. */ + gen->status = CUDD_GEN_EMPTY; + gen->stack.sp--; + goto done; + } + top = gen->stack.stack[gen->stack.sp-1]; + prev = gen->stack.stack[gen->stack.sp-2]; + next = cuddT(prev); + if (next != top) { /* follow the then branch next */ + gen->gen.cubes.cube[prev->index] = 1; + gen->stack.stack[gen->stack.sp-1] = next; + break; + } + /* Pop the stack and try again. */ + gen->gen.cubes.cube[prev->index] = 2; + gen->stack.sp--; + } + + while (1) { + top = gen->stack.stack[gen->stack.sp-1]; + if (!cuddIsConstant(top)) { + /* Take the else branch first. */ + gen->gen.cubes.cube[top->index] = 0; + next = cuddE(top); + gen->stack.stack[gen->stack.sp] = next; gen->stack.sp++; + } else if (top == DD_ZERO(zdd)) { + /* Backtrack. */ + while (1) { + if (gen->stack.sp == 1) { + /* The current node has no predecessor. */ + gen->status = CUDD_GEN_EMPTY; + gen->stack.sp--; + goto done; + } + prev = gen->stack.stack[gen->stack.sp-2]; + next = cuddT(prev); + if (next != top) { /* follow the then branch next */ + gen->gen.cubes.cube[prev->index] = 1; + gen->stack.stack[gen->stack.sp-1] = next; + break; + } + /* Pop the stack and try again. */ + gen->gen.cubes.cube[prev->index] = 2; + gen->stack.sp--; + top = gen->stack.stack[gen->stack.sp-1]; + } + } else { + gen->status = CUDD_GEN_NONEMPTY; + gen->gen.cubes.value = cuddV(top); + goto done; + } + } + +done: + if (gen->status == CUDD_GEN_EMPTY) return(0); + *path = gen->gen.cubes.cube; + return(1); + +} /* end of Cudd_zddNextPath */ + + +/**Function******************************************************************** + + Synopsis [Converts a path of a ZDD representing a cover to a string.] + + Description [Converts a path of a ZDD representing a cover to a + string. The string represents an implicant of the cover. The path + is typically produced by Cudd_zddForeachPath. Returns a pointer to + the string if successful; NULL otherwise. If the str input is NULL, + it allocates a new string. The string passed to this function must + have enough room for all variables and for the terminator.] + + SideEffects [None] + + SeeAlso [Cudd_zddForeachPath] + +******************************************************************************/ +char * +Cudd_zddCoverPathToString( + DdManager *zdd /* DD manager */, + int *path /* path of ZDD representing a cover */, + char *str /* pointer to string to use if != NULL */ + ) +{ + int nvars = zdd->sizeZ; + int i; + char *res; + + if (nvars & 1) return(NULL); + nvars >>= 1; + if (str == NULL) { + res = ALLOC(char, nvars+1); + if (res == NULL) return(NULL); + } else { + res = str; + } + for (i = 0; i < nvars; i++) { + int v = (path[2*i] << 2) | path[2*i+1]; + switch (v) { + case 0: + case 2: + case 8: + case 10: + res[i] = '-'; + break; + case 1: + case 9: + res[i] = '0'; + break; + case 4: + case 6: + res[i] = '1'; + break; + default: + res[i] = '?'; + } + } + res[nvars] = 0; + + return(res); + +} /* end of Cudd_zddCoverPathToString */ + + +/**Function******************************************************************** + + Synopsis [Writes a dot file representing the argument ZDDs.] + + Description [Writes a file representing the argument ZDDs in a format + suitable for the graph drawing program dot. + It returns 1 in case of success; 0 otherwise (e.g., out-of-memory, + file system full). + Cudd_zddDumpDot does not close the file: This is the caller + responsibility. Cudd_zddDumpDot uses a minimal unique subset of the + hexadecimal address of a node as name for it. + If the argument inames is non-null, it is assumed to hold the pointers + to the names of the inputs. Similarly for onames. + Cudd_zddDumpDot uses the following convention to draw arcs: +

          +
        • solid line: THEN arcs; +
        • dashed line: ELSE arcs. +
        + The dot options are chosen so that the drawing fits on a letter-size + sheet. + ] + + SideEffects [None] + + SeeAlso [Cudd_DumpDot Cudd_zddPrintDebug] + +******************************************************************************/ +int +Cudd_zddDumpDot( + DdManager * dd /* manager */, + int n /* number of output nodes to be dumped */, + DdNode ** f /* array of output nodes to be dumped */, + char ** inames /* array of input names (or NULL) */, + char ** onames /* array of output names (or NULL) */, + FILE * fp /* pointer to the dump file */) +{ + DdNode *support = NULL; + DdNode *scan; + int *sorted = NULL; + int nvars = dd->sizeZ; + st_table *visited = NULL; + st_generator *gen; + int retval; + int i, j; + int slots; + DdNodePtr *nodelist; + long refAddr, diff, mask; + + /* Build a bit array with the support of f. */ + sorted = ALLOC(int,nvars); + if (sorted == NULL) { + dd->errorCode = CUDD_MEMORY_OUT; + goto failure; + } + for (i = 0; i < nvars; i++) sorted[i] = 0; + + /* Take the union of the supports of each output function. */ + for (i = 0; i < n; i++) { + support = Cudd_Support(dd,f[i]); + if (support == NULL) goto failure; + cuddRef(support); + scan = support; + while (!cuddIsConstant(scan)) { + sorted[scan->index] = 1; + scan = cuddT(scan); + } + Cudd_RecursiveDeref(dd,support); + } + support = NULL; /* so that we do not try to free it in case of failure */ + + /* Initialize symbol table for visited nodes. */ + visited = st_init_table(st_ptrcmp, st_ptrhash); + if (visited == NULL) goto failure; + + /* Collect all the nodes of this DD in the symbol table. */ + for (i = 0; i < n; i++) { + retval = cuddCollectNodes(f[i],visited); + if (retval == 0) goto failure; + } + + /* Find how many most significant hex digits are identical + ** in the addresses of all the nodes. Build a mask based + ** on this knowledge, so that digits that carry no information + ** will not be printed. This is done in two steps. + ** 1. We scan the symbol table to find the bits that differ + ** in at least 2 addresses. + ** 2. We choose one of the possible masks. There are 8 possible + ** masks for 32-bit integer, and 16 possible masks for 64-bit + ** integers. + */ + + /* Find the bits that are different. */ + refAddr = (long) f[0]; + diff = 0; + gen = st_init_gen(visited); + while (st_gen(gen, (char **) &scan, NULL)) { + diff |= refAddr ^ (long) scan; + } + st_free_gen(gen); + + /* Choose the mask. */ + for (i = 0; (unsigned) i < 8 * sizeof(long); i += 4) { + mask = (1 << i) - 1; + if (diff <= mask) break; + } + + /* Write the header and the global attributes. */ + retval = fprintf(fp,"digraph \"ZDD\" {\n"); + if (retval == EOF) return(0); + retval = fprintf(fp, + "size = \"7.5,10\"\ncenter = true;\nedge [dir = none];\n"); + if (retval == EOF) return(0); + + /* Write the input name subgraph by scanning the support array. */ + retval = fprintf(fp,"{ node [shape = plaintext];\n"); + if (retval == EOF) goto failure; + retval = fprintf(fp," edge [style = invis];\n"); + if (retval == EOF) goto failure; + /* We use a name ("CONST NODES") with an embedded blank, because + ** it is unlikely to appear as an input name. + */ + retval = fprintf(fp," \"CONST NODES\" [style = invis];\n"); + if (retval == EOF) goto failure; + for (i = 0; i < nvars; i++) { + if (sorted[dd->invpermZ[i]]) { + if (inames == NULL) { + retval = fprintf(fp,"\" %d \" -> ", dd->invpermZ[i]); + } else { + retval = fprintf(fp,"\" %s \" -> ", inames[dd->invpermZ[i]]); + } + if (retval == EOF) goto failure; + } + } + retval = fprintf(fp,"\"CONST NODES\"; \n}\n"); + if (retval == EOF) goto failure; + + /* Write the output node subgraph. */ + retval = fprintf(fp,"{ rank = same; node [shape = box]; edge [style = invis];\n"); + if (retval == EOF) goto failure; + for (i = 0; i < n; i++) { + if (onames == NULL) { + retval = fprintf(fp,"\"F%d\"", i); + } else { + retval = fprintf(fp,"\" %s \"", onames[i]); + } + if (retval == EOF) goto failure; + if (i == n - 1) { + retval = fprintf(fp,"; }\n"); + } else { + retval = fprintf(fp," -> "); + } + if (retval == EOF) goto failure; + } + + /* Write rank info: All nodes with the same index have the same rank. */ + for (i = 0; i < nvars; i++) { + if (sorted[dd->invpermZ[i]]) { + retval = fprintf(fp,"{ rank = same; "); + if (retval == EOF) goto failure; + if (inames == NULL) { + retval = fprintf(fp,"\" %d \";\n", dd->invpermZ[i]); + } else { + retval = fprintf(fp,"\" %s \";\n", inames[dd->invpermZ[i]]); + } + if (retval == EOF) goto failure; + nodelist = dd->subtableZ[i].nodelist; + slots = dd->subtableZ[i].slots; + for (j = 0; j < slots; j++) { + scan = nodelist[j]; + while (scan != NULL) { + if (st_is_member(visited,(char *) scan)) { + retval = fprintf(fp,"\"%lx\";\n", (mask & (long) scan) / sizeof(DdNode)); + if (retval == EOF) goto failure; + } + scan = scan->next; + } + } + retval = fprintf(fp,"}\n"); + if (retval == EOF) goto failure; + } + } + + /* All constants have the same rank. */ + retval = fprintf(fp, + "{ rank = same; \"CONST NODES\";\n{ node [shape = box]; "); + if (retval == EOF) goto failure; + nodelist = dd->constants.nodelist; + slots = dd->constants.slots; + for (j = 0; j < slots; j++) { + scan = nodelist[j]; + while (scan != NULL) { + if (st_is_member(visited,(char *) scan)) { + retval = fprintf(fp,"\"%lx\";\n", (mask & (long) scan) / sizeof(DdNode)); + if (retval == EOF) goto failure; + } + scan = scan->next; + } + } + retval = fprintf(fp,"}\n}\n"); + if (retval == EOF) goto failure; + + /* Write edge info. */ + /* Edges from the output nodes. */ + for (i = 0; i < n; i++) { + if (onames == NULL) { + retval = fprintf(fp,"\"F%d\"", i); + } else { + retval = fprintf(fp,"\" %s \"", onames[i]); + } + if (retval == EOF) goto failure; + retval = fprintf(fp," -> \"%lx\" [style = solid];\n", + (mask & (long) f[i]) / sizeof(DdNode)); + if (retval == EOF) goto failure; + } + + /* Edges from internal nodes. */ + for (i = 0; i < nvars; i++) { + if (sorted[dd->invpermZ[i]]) { + nodelist = dd->subtableZ[i].nodelist; + slots = dd->subtableZ[i].slots; + for (j = 0; j < slots; j++) { + scan = nodelist[j]; + while (scan != NULL) { + if (st_is_member(visited,(char *) scan)) { + retval = fprintf(fp, + "\"%lx\" -> \"%lx\";\n", + (mask & (long) scan) / sizeof(DdNode), + (mask & (long) cuddT(scan)) / sizeof(DdNode)); + if (retval == EOF) goto failure; + retval = fprintf(fp, + "\"%lx\" -> \"%lx\" [style = dashed];\n", + (mask & (long) scan) / sizeof(DdNode), + (mask & (long) cuddE(scan)) / sizeof(DdNode)); + if (retval == EOF) goto failure; + } + scan = scan->next; + } + } + } + } + + /* Write constant labels. */ + nodelist = dd->constants.nodelist; + slots = dd->constants.slots; + for (j = 0; j < slots; j++) { + scan = nodelist[j]; + while (scan != NULL) { + if (st_is_member(visited,(char *) scan)) { + retval = fprintf(fp,"\"%lx\" [label = \"%g\"];\n", + (mask & (long) scan) / sizeof(DdNode), cuddV(scan)); + if (retval == EOF) goto failure; + } + scan = scan->next; + } + } + + /* Write trailer and return. */ + retval = fprintf(fp,"}\n"); + if (retval == EOF) goto failure; + + st_free_table(visited); + FREE(sorted); + return(1); + +failure: + if (sorted != NULL) FREE(sorted); + if (support != NULL) Cudd_RecursiveDeref(dd,support); + if (visited != NULL) st_free_table(visited); + return(0); + +} /* end of Cudd_zddDumpBlif */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Prints a ZDD to the standard output. One line per node is + printed.] + + Description [Prints a ZDD to the standard output. One line per node is + printed. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_zddPrintDebug] + +******************************************************************************/ +int +cuddZddP( + DdManager * zdd, + DdNode * f) +{ + int retval; + st_table *table = st_init_table(st_ptrcmp, st_ptrhash); + + if (table == NULL) return(0); + + retval = zp2(zdd, f, table); + st_free_table(table); + (void) fputc('\n', zdd->out); + return(retval); + +} /* end of cuddZddP */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of cuddZddP.] + + Description [Performs the recursive step of cuddZddP. Returns 1 in + case of success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +zp2( + DdManager * zdd, + DdNode * f, + st_table * t) +{ + DdNode *n; + int T, E; + DdNode *base = DD_ONE(zdd); + + if (f == NULL) + return(0); + + if (Cudd_IsConstant(f)) { + (void)fprintf(zdd->out, "ID = %d\n", (f == base)); + return(1); + } + if (st_is_member(t, (char *)f) == 1) + return(1); + + if (st_insert(t, (char *) f, NULL) == ST_OUT_OF_MEM) + return(0); + +#if SIZEOF_VOID_P == 8 + (void) fprintf(zdd->out, "ID = 0x%lx\tindex = %d\tr = %d\t", + (unsigned long)f / (unsigned long) sizeof(DdNode), f->index, f->ref); +#else + (void) fprintf(zdd->out, "ID = 0x%x\tindex = %d\tr = %d\t", + (unsigned)f / (unsigned) sizeof(DdNode), f->index, f->ref); +#endif + + n = cuddT(f); + if (Cudd_IsConstant(n)) { + (void) fprintf(zdd->out, "T = %d\t\t", (n == base)); + T = 1; + } else { +#if SIZEOF_VOID_P == 8 + (void) fprintf(zdd->out, "T = 0x%lx\t", (unsigned long) n / + (unsigned long) sizeof(DdNode)); +#else + (void) fprintf(zdd->out, "T = 0x%x\t", (unsigned) n / (unsigned) sizeof(DdNode)); +#endif + T = 0; + } + + n = cuddE(f); + if (Cudd_IsConstant(n)) { + (void) fprintf(zdd->out, "E = %d\n", (n == base)); + E = 1; + } else { +#if SIZEOF_VOID_P == 8 + (void) fprintf(zdd->out, "E = 0x%lx\n", (unsigned long) n / + (unsigned long) sizeof(DdNode)); +#else + (void) fprintf(zdd->out, "E = 0x%x\n", (unsigned) n / (unsigned) sizeof(DdNode)); +#endif + E = 0; + } + + if (E == 0) + if (zp2(zdd, cuddE(f), t) == 0) return(0); + if (T == 0) + if (zp2(zdd, cuddT(f), t) == 0) return(0); + return(1); + +} /* end of zp2 */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddPrintMinterm.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +zdd_print_minterm_aux( + DdManager * zdd /* manager */, + DdNode * node /* current node */, + int level /* depth in the recursion */, + int * list /* current recursion path */) +{ + DdNode *Nv, *Nnv; + int i, v; + DdNode *base = DD_ONE(zdd); + + if (Cudd_IsConstant(node)) { + if (node == base) { + /* Check for missing variable. */ + if (level != zdd->sizeZ) { + list[zdd->invpermZ[level]] = 0; + zdd_print_minterm_aux(zdd, node, level + 1, list); + return; + } + /* Terminal case: Print one cube based on the current recursion + ** path. + */ + for (i = 0; i < zdd->sizeZ; i++) { + v = list[i]; + if (v == 0) + (void) fprintf(zdd->out,"0"); + else if (v == 1) + (void) fprintf(zdd->out,"1"); + else if (v == 3) + (void) fprintf(zdd->out,"@"); /* should never happen */ + else + (void) fprintf(zdd->out,"-"); + } + (void) fprintf(zdd->out," 1\n"); + } + } else { + /* Check for missing variable. */ + if (level != cuddIZ(zdd,node->index)) { + list[zdd->invpermZ[level]] = 0; + zdd_print_minterm_aux(zdd, node, level + 1, list); + return; + } + + Nnv = cuddE(node); + Nv = cuddT(node); + if (Nv == Nnv) { + list[node->index] = 2; + zdd_print_minterm_aux(zdd, Nnv, level + 1, list); + return; + } + + list[node->index] = 1; + zdd_print_minterm_aux(zdd, Nv, level + 1, list); + list[node->index] = 0; + zdd_print_minterm_aux(zdd, Nnv, level + 1, list); + } + return; + +} /* end of zdd_print_minterm_aux */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddPrintCover.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +zddPrintCoverAux( + DdManager * zdd /* manager */, + DdNode * node /* current node */, + int level /* depth in the recursion */, + int * list /* current recursion path */) +{ + DdNode *Nv, *Nnv; + int i, v; + DdNode *base = DD_ONE(zdd); + + if (Cudd_IsConstant(node)) { + if (node == base) { + /* Check for missing variable. */ + if (level != zdd->sizeZ) { + list[zdd->invpermZ[level]] = 0; + zddPrintCoverAux(zdd, node, level + 1, list); + return; + } + /* Terminal case: Print one cube based on the current recursion + ** path. + */ + for (i = 0; i < zdd->sizeZ; i += 2) { + v = list[i] * 4 + list[i+1]; + if (v == 0) + (void) putc('-',zdd->out); + else if (v == 4) + (void) putc('1',zdd->out); + else if (v == 1) + (void) putc('0',zdd->out); + else + (void) putc('@',zdd->out); /* should never happen */ + } + (void) fprintf(zdd->out," 1\n"); + } + } else { + /* Check for missing variable. */ + if (level != cuddIZ(zdd,node->index)) { + list[zdd->invpermZ[level]] = 0; + zddPrintCoverAux(zdd, node, level + 1, list); + return; + } + + Nnv = cuddE(node); + Nv = cuddT(node); + if (Nv == Nnv) { + list[node->index] = 2; + zddPrintCoverAux(zdd, Nnv, level + 1, list); + return; + } + + list[node->index] = 1; + zddPrintCoverAux(zdd, Nv, level + 1, list); + list[node->index] = 0; + zddPrintCoverAux(zdd, Nnv, level + 1, list); + } + return; + +} /* end of zddPrintCoverAux */ diff --git a/abc_with_bb_support/src/bdd/cudd/module.make b/abc_with_bb_support/src/bdd/cudd/module.make new file mode 100644 index 000000000..275c0547a --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/module.make @@ -0,0 +1,61 @@ +SRC += src/bdd/cudd/cuddAPI.c \ + src/bdd/cudd/cuddAddAbs.c \ + src/bdd/cudd/cuddAddApply.c \ + src/bdd/cudd/cuddAddFind.c \ + src/bdd/cudd/cuddAddInv.c \ + src/bdd/cudd/cuddAddIte.c \ + src/bdd/cudd/cuddAddNeg.c \ + src/bdd/cudd/cuddAddWalsh.c \ + src/bdd/cudd/cuddAndAbs.c \ + src/bdd/cudd/cuddAnneal.c \ + src/bdd/cudd/cuddApa.c \ + src/bdd/cudd/cuddApprox.c \ + src/bdd/cudd/cuddBddAbs.c \ + src/bdd/cudd/cuddBddCorr.c \ + src/bdd/cudd/cuddBddIte.c \ + src/bdd/cudd/cuddBridge.c \ + src/bdd/cudd/cuddCache.c \ + src/bdd/cudd/cuddCheck.c \ + src/bdd/cudd/cuddClip.c \ + src/bdd/cudd/cuddCof.c \ + src/bdd/cudd/cuddCompose.c \ + src/bdd/cudd/cuddDecomp.c \ + src/bdd/cudd/cuddEssent.c \ + src/bdd/cudd/cuddExact.c \ + src/bdd/cudd/cuddExport.c \ + src/bdd/cudd/cuddGenCof.c \ + src/bdd/cudd/cuddGenetic.c \ + src/bdd/cudd/cuddGroup.c \ + src/bdd/cudd/cuddHarwell.c \ + src/bdd/cudd/cuddInit.c \ + src/bdd/cudd/cuddInteract.c \ + src/bdd/cudd/cuddLCache.c \ + src/bdd/cudd/cuddLevelQ.c \ + src/bdd/cudd/cuddLinear.c \ + src/bdd/cudd/cuddLiteral.c \ + src/bdd/cudd/cuddMatMult.c \ + src/bdd/cudd/cuddPriority.c \ + src/bdd/cudd/cuddRead.c \ + src/bdd/cudd/cuddRef.c \ + src/bdd/cudd/cuddReorder.c \ + src/bdd/cudd/cuddSat.c \ + src/bdd/cudd/cuddSign.c \ + src/bdd/cudd/cuddSolve.c \ + src/bdd/cudd/cuddSplit.c \ + src/bdd/cudd/cuddSubsetHB.c \ + src/bdd/cudd/cuddSubsetSP.c \ + src/bdd/cudd/cuddSymmetry.c \ + src/bdd/cudd/cuddTable.c \ + src/bdd/cudd/cuddUtil.c \ + src/bdd/cudd/cuddWindow.c \ + src/bdd/cudd/cuddZddCount.c \ + src/bdd/cudd/cuddZddFuncs.c \ + src/bdd/cudd/cuddZddGroup.c \ + src/bdd/cudd/cuddZddIsop.c \ + src/bdd/cudd/cuddZddLin.c \ + src/bdd/cudd/cuddZddMisc.c \ + src/bdd/cudd/cuddZddPort.c \ + src/bdd/cudd/cuddZddReord.c \ + src/bdd/cudd/cuddZddSetop.c \ + src/bdd/cudd/cuddZddSymm.c \ + src/bdd/cudd/cuddZddUtil.c diff --git a/abc_with_bb_support/src/bdd/cudd/r7x8.1.mat b/abc_with_bb_support/src/bdd/cudd/r7x8.1.mat new file mode 100644 index 000000000..55bcbc42b --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/r7x8.1.mat @@ -0,0 +1,53 @@ +7 9 +0 0 1 +0 1 1 +0 2 1 +0 3 4 +0 4 3 +0 5 3 +0 6 3 +0 8 3 +1 0 4 +1 1 3 +1 2 2 +1 3 4 +1 4 1 +1 5 2 +1 6 4 +1 8 3 +2 0 1 +2 1 1 +2 2 4 +2 4 2 +2 5 3 +2 6 3 +2 8 3 +3 0 2 +3 1 1 +3 3 4 +3 4 4 +3 5 1 +3 8 1 +4 0 2 +4 1 3 +4 2 2 +4 3 4 +4 4 1 +4 5 1 +4 6 2 +4 8 2 +5 0 3 +5 1 3 +5 2 4 +5 3 4 +5 4 1 +5 5 3 +5 6 3 +5 8 4 +6 1 1 +6 2 1 +6 3 4 +6 4 2 +6 5 4 +6 6 4 +6 8 2 diff --git a/abc_with_bb_support/src/bdd/cudd/testcudd.c b/abc_with_bb_support/src/bdd/cudd/testcudd.c new file mode 100644 index 000000000..b9e1547f6 --- /dev/null +++ b/abc_with_bb_support/src/bdd/cudd/testcudd.c @@ -0,0 +1,988 @@ +/**CFile*********************************************************************** + + FileName [testcudd.c] + + PackageName [cudd] + + Synopsis [Sanity check tests for some CUDD functions.] + + Description [testcudd reads a matrix with real coefficients and + transforms it into an ADD. It then performs various operations on + the ADD and on the BDD corresponding to the ADD pattern. Finally, + testcudd tests functions relate to Walsh matrices and matrix + multiplication.] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [ This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "cuddInt.h" + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define TESTCUDD_VERSION "TestCudd Version #1.0, Release date 3/17/01" + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: testcudd.c,v 1.1.1.1 2003/02/24 22:23:54 wjiang Exp $"; +#endif + +static char *onames[] = { "C", "M" }; /* names of functions to be dumped */ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static void usage ARGS((char * prog)); +static FILE *open_file ARGS((char *filename, char *mode)); +static int testIterators ARGS((DdManager *dd, DdNode *M, DdNode *C, int pr)); +static int testXor ARGS((DdManager *dd, DdNode *f, int pr, int nvars)); +static int testHamming ARGS((DdManager *dd, DdNode *f, int pr, int nvars)); +static int testWalsh ARGS((DdManager *dd, int N, int cmu, int approach, int pr)); + +/**AutomaticEnd***************************************************************/ + + +/**Function******************************************************************** + + Synopsis [Main function for testcudd.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +main(int argc, char **argv) +{ + FILE *fp; /* pointer to input file */ + char *file = ""; /* input file name */ + FILE *dfp = NULL; /* pointer to dump file */ + char *dfile; /* file for DD dump */ + DdNode *dfunc[2]; /* addresses of the functions to be dumped */ + DdManager *dd; /* pointer to DD manager */ + DdNode *one, *zero; /* fast access to constant functions */ + DdNode *M; + DdNode **x; /* pointers to variables */ + DdNode **y; /* pointers to variables */ + DdNode **xn; /* complements of row variables */ + DdNode **yn_; /* complements of column variables */ + DdNode **xvars; + DdNode **yvars; + DdNode *C; /* result of converting from ADD to BDD */ + DdNode *ess; /* cube of essential variables */ + DdNode *shortP; /* BDD cube of shortest path */ + DdNode *largest; /* BDD of largest cube */ + DdNode *shortA; /* ADD cube of shortest path */ + DdNode *constN; /* value returned by evaluation of ADD */ + DdNode *ycube; /* cube of the negated y vars for c-proj */ + DdNode *CP; /* C-Projection of C */ + DdNode *CPr; /* C-Selection of C */ + int length; /* length of the shortest path */ + int nx; /* number of variables */ + int ny; + int maxnx; + int maxny; + int m; + int n; + int N; + int cmu; /* use CMU multiplication */ + int pr; /* verbose printout level */ + int harwell; + int multiple; /* read multiple matrices */ + int ok; + int c; /* variable to read in options */ + int approach; /* reordering approach */ + int autodyn; /* automatic reordering */ + int groupcheck; /* option for group sifting */ + int profile; /* print heap profile if != 0 */ + int keepperm; /* keep track of permutation */ + int clearcache; /* clear the cache after each matrix */ + int blifOrDot; /* dump format: 0 -> dot, 1 -> blif, ... */ + int retval; /* return value */ + int i; /* loop index */ + long startTime; /* initial time */ + long lapTime; + int size; + unsigned int cacheSize, maxMemory; + unsigned int nvars,nslots; + + startTime = util_cpu_time(); + + approach = CUDD_REORDER_NONE; + autodyn = 0; + pr = 0; + harwell = 0; + multiple = 0; + profile = 0; + keepperm = 0; + cmu = 0; + N = 4; + nvars = 4; + cacheSize = 127; + maxMemory = 0; + nslots = CUDD_UNIQUE_SLOTS; + clearcache = 0; + groupcheck = CUDD_GROUP_CHECK7; + dfile = NULL; + blifOrDot = 0; /* dot format */ + + /* Parse command line. */ + while ((c = util_getopt(argc, argv, "CDHMPS:a:bcd:g:hkmn:p:v:x:X:")) + != EOF) { + switch(c) { + case 'C': + cmu = 1; + break; + case 'D': + autodyn = 1; + break; + case 'H': + harwell = 1; + break; + case 'M': +#ifdef MNEMOSYNE + (void) mnem_setrecording(0); +#endif + break; + case 'P': + profile = 1; + break; + case 'S': + nslots = atoi(util_optarg); + break; + case 'X': + maxMemory = atoi(util_optarg); + break; + case 'a': + approach = atoi(util_optarg); + break; + case 'b': + blifOrDot = 1; /* blif format */ + break; + case 'c': + clearcache = 1; + break; + case 'd': + dfile = util_optarg; + break; + case 'g': + groupcheck = atoi(util_optarg); + break; + case 'k': + keepperm = 1; + break; + case 'm': + multiple = 1; + break; + case 'n': + N = atoi(util_optarg); + break; + case 'p': + pr = atoi(util_optarg); + break; + case 'v': + nvars = atoi(util_optarg); + break; + case 'x': + cacheSize = atoi(util_optarg); + break; + case 'h': + default: + usage(argv[0]); + break; + } + } + + if (argc - util_optind == 0) { + file = "-"; + } else if (argc - util_optind == 1) { + file = argv[util_optind]; + } else { + usage(argv[0]); + } + if ((approach<0) || (approach>17)) { + (void) fprintf(stderr,"Invalid approach: %d \n",approach); + usage(argv[0]); + } + + if (pr >= 0) { + (void) printf("# %s\n", TESTCUDD_VERSION); + /* Echo command line and arguments. */ + (void) printf("#"); + for (i = 0; i < argc; i++) { + (void) printf(" %s", argv[i]); + } + (void) printf("\n"); + (void) fflush(stdout); + } + + /* Initialize manager and provide easy reference to terminals. */ + dd = Cudd_Init(nvars,0,nslots,cacheSize,maxMemory); + one = DD_ONE(dd); + zero = DD_ZERO(dd); + dd->groupcheck = (Cudd_AggregationType) groupcheck; + if (autodyn) Cudd_AutodynEnable(dd,CUDD_REORDER_SAME); + + /* Open input file. */ + fp = open_file(file, "r"); + + /* Open dump file if requested */ + if (dfile != NULL) { + dfp = open_file(dfile, "w"); + } + + x = y = xn = yn_ = NULL; + do { + /* We want to start anew for every matrix. */ + maxnx = maxny = 0; + nx = maxnx; ny = maxny; + if (pr>0) lapTime = util_cpu_time(); + if (harwell) { + if (pr >= 0) (void) printf(":name: "); + ok = Cudd_addHarwell(fp, dd, &M, &x, &y, &xn, &yn_, &nx, &ny, + &m, &n, 0, 2, 1, 2, pr); + } else { + ok = Cudd_addRead(fp, dd, &M, &x, &y, &xn, &yn_, &nx, &ny, + &m, &n, 0, 2, 1, 2); + if (pr >= 0) + (void) printf(":name: %s: %d rows %d columns\n", file, m, n); + } + if (!ok) { + (void) fprintf(stderr, "Error reading matrix\n"); + exit(1); + } + + if (nx > maxnx) maxnx = nx; + if (ny > maxny) maxny = ny; + + /* Build cube of negated y's. */ + ycube = DD_ONE(dd); + Cudd_Ref(ycube); + for (i = maxny - 1; i >= 0; i--) { + DdNode *tmpp; + tmpp = Cudd_bddAnd(dd,Cudd_Not(dd->vars[y[i]->index]),ycube); + if (tmpp == NULL) exit(2); + Cudd_Ref(tmpp); + Cudd_RecursiveDeref(dd,ycube); + ycube = tmpp; + } + /* Initialize vectors of BDD variables used by priority func. */ + xvars = ALLOC(DdNode *, nx); + if (xvars == NULL) exit(2); + for (i = 0; i < nx; i++) { + xvars[i] = dd->vars[x[i]->index]; + } + yvars = ALLOC(DdNode *, ny); + if (yvars == NULL) exit(2); + for (i = 0; i < ny; i++) { + yvars[i] = dd->vars[y[i]->index]; + } + + /* Clean up */ + for (i=0; i < maxnx; i++) { + Cudd_RecursiveDeref(dd, x[i]); + Cudd_RecursiveDeref(dd, xn[i]); + } + FREE(x); + FREE(xn); + for (i=0; i < maxny; i++) { + Cudd_RecursiveDeref(dd, y[i]); + Cudd_RecursiveDeref(dd, yn_[i]); + } + FREE(y); + FREE(yn_); + + if (pr>0) {(void) printf(":1: M"); Cudd_PrintDebug(dd,M,nx+ny,pr);} + + if (pr>0) (void) printf(":2: time to read the matrix = %s\n", + util_print_time(util_cpu_time() - lapTime)); + + C = Cudd_addBddPattern(dd, M); + if (C == 0) exit(2); + Cudd_Ref(C); + if (pr>0) {(void) printf(":3: C"); Cudd_PrintDebug(dd,C,nx+ny,pr);} + + /* Test iterators. */ + retval = testIterators(dd,M,C,pr); + if (retval == 0) exit(2); + + cuddCacheProfile(dd,stdout); + + /* Test XOR */ + retval = testXor(dd,C,pr,nx+ny); + if (retval == 0) exit(2); + + /* Test Hamming distance functions. */ + retval = testHamming(dd,C,pr,nx+ny); + if (retval == 0) exit(2); + + /* Test selection functions. */ + CP = Cudd_CProjection(dd,C,ycube); + if (CP == NULL) exit(2); + Cudd_Ref(CP); + if (pr>0) {(void) printf("ycube"); Cudd_PrintDebug(dd,ycube,nx+ny,pr);} + if (pr>0) {(void) printf("CP"); Cudd_PrintDebug(dd,CP,nx+ny,pr);} + + if (nx == ny) { + CPr = Cudd_PrioritySelect(dd,C,xvars,yvars,(DdNode **)NULL, + (DdNode *)NULL,ny,Cudd_Xgty); + if (CPr == NULL) exit(2); + Cudd_Ref(CPr); + if (pr>0) {(void) printf(":4: CPr"); Cudd_PrintDebug(dd,CPr,nx+ny,pr);} + if (CP != CPr) { + (void) printf("CP != CPr!\n"); + } + Cudd_RecursiveDeref(dd, CPr); + } + FREE(xvars); FREE(yvars); + + Cudd_RecursiveDeref(dd, CP); + Cudd_RecursiveDeref(dd, ycube); + + /* Test functions for essential variables. */ + ess = Cudd_FindEssential(dd,C); + if (ess == NULL) exit(2); + Cudd_Ref(ess); + if (pr>0) {(void) printf(":4: ess"); Cudd_PrintDebug(dd,ess,nx+ny,pr);} + Cudd_RecursiveDeref(dd, ess); + + /* Test functions for shortest paths. */ + shortP = Cudd_ShortestPath(dd, M, NULL, NULL, &length); + if (shortP == NULL) exit(2); + Cudd_Ref(shortP); + if (pr>0) { + (void) printf(":5: shortP"); Cudd_PrintDebug(dd,shortP,nx+ny,pr); + } + /* Test functions for largest cubes. */ + largest = Cudd_LargestCube(dd, Cudd_Not(C), &length); + if (largest == NULL) exit(2); + Cudd_Ref(largest); + if (pr>0) { + (void) printf(":5b: largest"); + Cudd_PrintDebug(dd,largest,nx+ny,pr); + } + Cudd_RecursiveDeref(dd, largest); + + /* Test Cudd_addEvalConst and Cudd_addIteConstant. */ + shortA = Cudd_BddToAdd(dd,shortP); + if (shortA == NULL) exit(2); + Cudd_Ref(shortA); + Cudd_RecursiveDeref(dd, shortP); + constN = Cudd_addEvalConst(dd,shortA,M); + if (constN == DD_NON_CONSTANT) exit(2); + if (Cudd_addIteConstant(dd,shortA,M,constN) != constN) exit(2); + if (pr>0) {(void) printf("The value of M along the chosen shortest path is %g\n", cuddV(constN));} + Cudd_RecursiveDeref(dd, shortA); + + shortP = Cudd_ShortestPath(dd, C, NULL, NULL, &length); + if (shortP == NULL) exit(2); + Cudd_Ref(shortP); + if (pr>0) { + (void) printf(":6: shortP"); Cudd_PrintDebug(dd,shortP,nx+ny,pr); + } + + /* Test Cudd_bddIteConstant and Cudd_bddLeq. */ + if (!Cudd_bddLeq(dd,shortP,C)) exit(2); + if (Cudd_bddIteConstant(dd,Cudd_Not(shortP),one,C) != one) exit(2); + Cudd_RecursiveDeref(dd, shortP); + + if (profile) { + retval = cuddHeapProfile(dd); + } + + size = dd->size; + + if (pr>0) { + (void) printf("Average distance: %g\n", Cudd_AverageDistance(dd)); + } + + /* Reorder if so requested. */ + if (approach != CUDD_REORDER_NONE) { +#ifndef DD_STATS + retval = Cudd_EnableReorderingReporting(dd); + if (retval == 0) { + (void) fprintf(stderr,"Error reported by Cudd_EnableReorderingReporting\n"); + exit(3); + } +#endif +#ifdef DD_DEBUG + retval = Cudd_DebugCheck(dd); + if (retval != 0) { + (void) fprintf(stderr,"Error reported by Cudd_DebugCheck\n"); + exit(3); + } + retval = Cudd_CheckKeys(dd); + if (retval != 0) { + (void) fprintf(stderr,"Error reported by Cudd_CheckKeys\n"); + exit(3); + } +#endif + retval = Cudd_ReduceHeap(dd,(Cudd_ReorderingType)approach,5); + if (retval == 0) { + (void) fprintf(stderr,"Error reported by Cudd_ReduceHeap\n"); + exit(3); + } +#ifndef DD_STATS + retval = Cudd_DisableReorderingReporting(dd); + if (retval == 0) { + (void) fprintf(stderr,"Error reported by Cudd_DisableReorderingReporting\n"); + exit(3); + } +#endif +#ifdef DD_DEBUG + retval = Cudd_DebugCheck(dd); + if (retval != 0) { + (void) fprintf(stderr,"Error reported by Cudd_DebugCheck\n"); + exit(3); + } + retval = Cudd_CheckKeys(dd); + if (retval != 0) { + (void) fprintf(stderr,"Error reported by Cudd_CheckKeys\n"); + exit(3); + } +#endif + if (approach == CUDD_REORDER_SYMM_SIFT || + approach == CUDD_REORDER_SYMM_SIFT_CONV) { + Cudd_SymmProfile(dd,0,dd->size-1); + } + + if (pr>0) { + (void) printf("Average distance: %g\n", Cudd_AverageDistance(dd)); + } + + if (keepperm) { + /* Print variable permutation. */ + (void) printf("Variable Permutation:"); + for (i=0; iinvperm[i]); + } + (void) printf("\n"); + (void) printf("Inverse Permutation:"); + for (i=0; iperm[i]); + } + (void) printf("\n"); + } + + if (pr>0) {(void) printf("M"); Cudd_PrintDebug(dd,M,nx+ny,pr);} + + if (profile) { + retval = cuddHeapProfile(dd); + } + + } + + /* Dump DDs of C and M if so requested. */ + if (dfile != NULL) { + dfunc[0] = C; + dfunc[1] = M; + if (blifOrDot == 1) { + /* Only dump C because blif cannot handle ADDs */ + retval = Cudd_DumpBlif(dd,1,dfunc,NULL,onames,NULL,dfp); + } else { + retval = Cudd_DumpDot(dd,2,dfunc,NULL,onames,dfp); + } + if (retval != 1) { + (void) fprintf(stderr,"abnormal termination\n"); + exit(2); + } + } + + Cudd_RecursiveDeref(dd, C); + Cudd_RecursiveDeref(dd, M); + + if (clearcache) { + if (pr>0) {(void) printf("Clearing the cache... ");} + for (i = dd->cacheSlots - 1; i>=0; i--) { + dd->cache[i].data = NIL(DdNode); + } + if (pr>0) {(void) printf("done\n");} + } + if (pr>0) { + (void) printf("Number of variables = %6d\t",dd->size); + (void) printf("Number of slots = %6d\n",dd->slots); + (void) printf("Number of keys = %6d\t",dd->keys); + (void) printf("Number of min dead = %6d\n",dd->minDead); + } + + } while (multiple && !feof(fp)); + + fclose(fp); + if (dfile != NULL) { + fclose(dfp); + } + + /* Second phase: experiment with Walsh matrices. */ + if (!testWalsh(dd,N,cmu,approach,pr)) { + exit(2); + } + + /* Check variable destruction. */ + assert(cuddDestroySubtables(dd,3)); + assert(Cudd_DebugCheck(dd) == 0); + assert(Cudd_CheckKeys(dd) == 0); + + retval = Cudd_CheckZeroRef(dd); + ok = retval != 0; /* ok == 0 means O.K. */ + if (retval != 0) { + (void) fprintf(stderr, + "%d non-zero DD reference counts after dereferencing\n", retval); + } + + if (pr >= 0) { + (void) Cudd_PrintInfo(dd,stdout); + } + + Cudd_Quit(dd); + +#ifdef MNEMOSYNE + mnem_writestats(); +#endif + + if (pr>0) (void) printf("total time = %s\n", + util_print_time(util_cpu_time() - startTime)); + + if (pr >= 0) util_print_cpu_stats(stdout); + exit(ok); + /* NOTREACHED */ + +} /* end of main */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Prints usage info for testcudd.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +usage(char *prog) +{ + (void) fprintf(stderr, "usage: %s [options] [file]\n", prog); + (void) fprintf(stderr, " -C\t\tuse CMU multiplication algorithm\n"); + (void) fprintf(stderr, " -D\t\tenable automatic dynamic reordering\n"); + (void) fprintf(stderr, " -H\t\tread matrix in Harwell format\n"); + (void) fprintf(stderr, " -M\t\tturns off memory allocation recording\n"); + (void) fprintf(stderr, " -P\t\tprint BDD heap profile\n"); + (void) fprintf(stderr, " -S n\t\tnumber of slots for each subtable\n"); + (void) fprintf(stderr, " -X n\t\ttarget maximum memory in bytes\n"); + (void) fprintf(stderr, " -a n\t\tchoose reordering approach (0-13)\n"); + (void) fprintf(stderr, " \t\t\t0: same as autoMethod\n"); + (void) fprintf(stderr, " \t\t\t1: no reordering (default)\n"); + (void) fprintf(stderr, " \t\t\t2: random\n"); + (void) fprintf(stderr, " \t\t\t3: pivot\n"); + (void) fprintf(stderr, " \t\t\t4: sifting\n"); + (void) fprintf(stderr, " \t\t\t5: sifting to convergence\n"); + (void) fprintf(stderr, " \t\t\t6: symmetric sifting\n"); + (void) fprintf(stderr, " \t\t\t7: symmetric sifting to convergence\n"); + (void) fprintf(stderr, " \t\t\t8-10: window of size 2-4\n"); + (void) fprintf(stderr, " \t\t\t11-13: window of size 2-4 to conv.\n"); + (void) fprintf(stderr, " \t\t\t14: group sifting\n"); + (void) fprintf(stderr, " \t\t\t15: group sifting to convergence\n"); + (void) fprintf(stderr, " \t\t\t16: simulated annealing\n"); + (void) fprintf(stderr, " \t\t\t17: genetic algorithm\n"); + (void) fprintf(stderr, " -b\t\tuse blif as format for dumps\n"); + (void) fprintf(stderr, " -c\t\tclear the cache after each matrix\n"); + (void) fprintf(stderr, " -d file\tdump DDs to file\n"); + (void) fprintf(stderr, " -g\t\tselect aggregation criterion (0,5,7)\n"); + (void) fprintf(stderr, " -h\t\tprints this message\n"); + (void) fprintf(stderr, " -k\t\tprint the variable permutation\n"); + (void) fprintf(stderr, " -m\t\tread multiple matrices (only with -H)\n"); + (void) fprintf(stderr, " -n n\t\tnumber of variables\n"); + (void) fprintf(stderr, " -p n\t\tcontrol verbosity\n"); + (void) fprintf(stderr, " -v n\t\tinitial variables in the unique table\n"); + (void) fprintf(stderr, " -x n\t\tinitial size of the cache\n"); + exit(2); +} /* end of usage */ + + +/**Function******************************************************************** + + Synopsis [Opens a file.] + + Description [Opens a file, or fails with an error message and exits. + Allows '-' as a synonym for standard input.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static FILE * +open_file(char *filename, char *mode) +{ + FILE *fp; + + if (strcmp(filename, "-") == 0) { + return mode[0] == 'r' ? stdin : stdout; + } else if ((fp = fopen(filename, mode)) == NULL) { + perror(filename); + exit(1); + } + return fp; + +} /* end of open_file */ + + +/**Function******************************************************************** + + Synopsis [Tests Walsh matrix multiplication.] + + Description [Tests Walsh matrix multiplication. Return 1 if successful; + 0 otherwise.] + + SideEffects [May create new variables in the manager.] + + SeeAlso [] + +******************************************************************************/ +static int +testWalsh( + DdManager *dd /* manager */, + int N /* number of variables */, + int cmu /* use CMU approach to matrix multiplication */, + int approach /* reordering approach */, + int pr /* verbosity level */) +{ + DdNode *walsh1, *walsh2, *wtw; + DdNode **x, **v, **z; + int i, retval; + DdNode *one = DD_ONE(dd); + DdNode *zero = DD_ZERO(dd); + + if (N > 3) { + x = ALLOC(DdNode *,N); + v = ALLOC(DdNode *,N); + z = ALLOC(DdNode *,N); + + for (i = N-1; i >= 0; i--) { + Cudd_Ref(x[i]=cuddUniqueInter(dd,3*i,one,zero)); + Cudd_Ref(v[i]=cuddUniqueInter(dd,3*i+1,one,zero)); + Cudd_Ref(z[i]=cuddUniqueInter(dd,3*i+2,one,zero)); + } + Cudd_Ref(walsh1 = Cudd_addWalsh(dd,v,z,N)); + if (pr>0) {(void) printf("walsh1"); Cudd_PrintDebug(dd,walsh1,2*N,pr);} + Cudd_Ref(walsh2 = Cudd_addWalsh(dd,x,v,N)); + if (cmu) { + Cudd_Ref(wtw = Cudd_addTimesPlus(dd,walsh2,walsh1,v,N)); + } else { + Cudd_Ref(wtw = Cudd_addMatrixMultiply(dd,walsh2,walsh1,v,N)); + } + if (pr>0) {(void) printf("wtw"); Cudd_PrintDebug(dd,wtw,2*N,pr);} + + if (approach != CUDD_REORDER_NONE) { +#ifdef DD_DEBUG + retval = Cudd_DebugCheck(dd); + if (retval != 0) { + (void) fprintf(stderr,"Error reported by Cudd_DebugCheck\n"); + return(0); + } +#endif + retval = Cudd_ReduceHeap(dd,(Cudd_ReorderingType)approach,5); + if (retval == 0) { + (void) fprintf(stderr,"Error reported by Cudd_ReduceHeap\n"); + return(0); + } +#ifdef DD_DEBUG + retval = Cudd_DebugCheck(dd); + if (retval != 0) { + (void) fprintf(stderr,"Error reported by Cudd_DebugCheck\n"); + return(0); + } +#endif + if (approach == CUDD_REORDER_SYMM_SIFT || + approach == CUDD_REORDER_SYMM_SIFT_CONV) { + Cudd_SymmProfile(dd,0,dd->size-1); + } + } + /* Clean up. */ + Cudd_RecursiveDeref(dd, wtw); + Cudd_RecursiveDeref(dd, walsh1); + Cudd_RecursiveDeref(dd, walsh2); + for (i=0; i < N; i++) { + Cudd_RecursiveDeref(dd, x[i]); + Cudd_RecursiveDeref(dd, v[i]); + Cudd_RecursiveDeref(dd, z[i]); + } + FREE(x); + FREE(v); + FREE(z); + } + return(1); + +} /* end of testWalsh */ + +/**Function******************************************************************** + + Synopsis [Tests iterators.] + + Description [Tests iterators on cubes and nodes.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +testIterators( + DdManager *dd, + DdNode *M, + DdNode *C, + int pr) +{ + int *cube; + CUDD_VALUE_TYPE value; + DdGen *gen; + int q; + + /* Test iterator for cubes. */ + if (pr>1) { + (void) printf("Testing iterator on cubes:\n"); + Cudd_ForeachCube(dd,M,gen,cube,value) { + for (q = 0; q < dd->size; q++) { + switch (cube[q]) { + case 0: + (void) printf("0"); + break; + case 1: + (void) printf("1"); + break; + case 2: + (void) printf("-"); + break; + default: + (void) printf("?"); + } + } + (void) printf(" %g\n",value); + } + (void) printf("\n"); + } + + if (pr>1) { + (void) printf("Testing prime expansion of cubes:\n"); + if (!Cudd_bddPrintCover(dd,C,C)) return(0); + } + + /* Test iterator on nodes. */ + if (pr>2) { + DdGen *gen; + DdNode *node; + (void) printf("Testing iterator on nodes:\n"); + Cudd_ForeachNode(dd,M,gen,node) { + if (Cudd_IsConstant(node)) { +#if SIZEOF_VOID_P == 8 + (void) printf("ID = 0x%lx\tvalue = %-9g\n", + (unsigned long) node / + (unsigned long) sizeof(DdNode), + Cudd_V(node)); +#else + (void) printf("ID = 0x%x\tvalue = %-9g\n", + (unsigned int) node / + (unsigned int) sizeof(DdNode), + Cudd_V(node)); +#endif + } else { +#if SIZEOF_VOID_P == 8 + (void) printf("ID = 0x%lx\tindex = %d\tr = %d\n", + (unsigned long) node / + (unsigned long) sizeof(DdNode), + node->index, node->ref); +#else + (void) printf("ID = 0x%x\tindex = %d\tr = %d\n", + (unsigned int) node / + (unsigned int) sizeof(DdNode), + node->index, node->ref); +#endif + } + } + (void) printf("\n"); + } + return(1); + +} /* end of testIterators */ + + +/**Function******************************************************************** + + Synopsis [Tests the functions related to the exclusive OR.] + + Description [Tests the functions related to the exclusive OR. It + builds the boolean difference of the given function in three + different ways and checks that the results is the same. Returns 1 if + successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +testXor(DdManager *dd, DdNode *f, int pr, int nvars) +{ + DdNode *f1, *f0, *res1, *res2; + int x; + + /* Extract cofactors w.r.t. mid variable. */ + x = nvars / 2; + f1 = Cudd_Cofactor(dd,f,dd->vars[x]); + if (f1 == NULL) return(0); + Cudd_Ref(f1); + + f0 = Cudd_Cofactor(dd,f,Cudd_Not(dd->vars[x])); + if (f0 == NULL) { + Cudd_RecursiveDeref(dd,f1); + return(0); + } + Cudd_Ref(f0); + + /* Compute XOR of cofactors with ITE. */ + res1 = Cudd_bddIte(dd,f1,Cudd_Not(f0),f0); + if (res1 == NULL) return(0); + Cudd_Ref(res1); + + if (pr>0) {(void) printf("xor1"); Cudd_PrintDebug(dd,res1,nvars,pr);} + + /* Compute XOR of cofactors with XOR. */ + res2 = Cudd_bddXor(dd,f1,f0); + if (res2 == NULL) { + Cudd_RecursiveDeref(dd,res1); + return(0); + } + Cudd_Ref(res2); + + if (res1 != res2) { + if (pr>0) {(void) printf("xor2"); Cudd_PrintDebug(dd,res2,nvars,pr);} + Cudd_RecursiveDeref(dd,res1); + Cudd_RecursiveDeref(dd,res2); + return(0); + } + Cudd_RecursiveDeref(dd,res1); + Cudd_RecursiveDeref(dd,f1); + Cudd_RecursiveDeref(dd,f0); + + /* Compute boolean difference directly. */ + res1 = Cudd_bddBooleanDiff(dd,f,x); + if (res1 == NULL) { + Cudd_RecursiveDeref(dd,res2); + return(0); + } + Cudd_Ref(res1); + + if (res1 != res2) { + if (pr>0) {(void) printf("xor3"); Cudd_PrintDebug(dd,res1,nvars,pr);} + Cudd_RecursiveDeref(dd,res1); + Cudd_RecursiveDeref(dd,res2); + return(0); + } + Cudd_RecursiveDeref(dd,res1); + Cudd_RecursiveDeref(dd,res2); + return(1); + +} /* end of testXor */ + + +/**Function******************************************************************** + + Synopsis [Tests the Hamming distance functions.] + + Description [Tests the Hammming distance functions. Returns + 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +testHamming( + DdManager *dd, + DdNode *f, + int pr, + int nvars) +{ + DdNode **vars, *minBdd, *zero, *scan; + int i; + int d; + int *minterm; + int size = Cudd_ReadSize(dd); + + vars = ALLOC(DdNode *, size); + if (vars == NULL) return(0); + for (i = 0; i < size; i++) { + vars[i] = Cudd_bddIthVar(dd,i); + } + + minBdd = Cudd_bddPickOneMinterm(dd,Cudd_Not(f),vars,size); + Cudd_Ref(minBdd); + if (pr > 0) { + (void) printf("Chosen minterm for Hamming distance test: "); + Cudd_PrintDebug(dd,minBdd,size,pr); + } + + minterm = ALLOC(int,size); + if (minterm == NULL) { + FREE(vars); + Cudd_RecursiveDeref(dd,minBdd); + return(0); + } + scan = minBdd; + zero = Cudd_Not(DD_ONE(dd)); + while (!Cudd_IsConstant(scan)) { + DdNode *R = Cudd_Regular(scan); + DdNode *T = Cudd_T(R); + DdNode *E = Cudd_E(R); + if (R != scan) { + T = Cudd_Not(T); + E = Cudd_Not(E); + } + if (T == zero) { + minterm[R->index] = 0; + scan = E; + } else { + minterm[R->index] = 1; + scan = T; + } + } + Cudd_RecursiveDeref(dd,minBdd); + + d = Cudd_MinHammingDist(dd,f,minterm,size); + + (void) printf("Minimum Hamming distance = %d\n", d); + + FREE(vars); + FREE(minterm); + return(1); + +} /* end of testHamming */ diff --git a/abc_with_bb_support/src/bdd/dsd/dsd.h b/abc_with_bb_support/src/bdd/dsd/dsd.h new file mode 100644 index 000000000..c4c8afc46 --- /dev/null +++ b/abc_with_bb_support/src/bdd/dsd/dsd.h @@ -0,0 +1,129 @@ +/**CFile**************************************************************** + + FileName [dsd.h] + + PackageName [DSD: Disjoint-support decomposition package.] + + Synopsis [External declarations of the package. + This fast BDD-based recursive algorithm for simple + (single-output) DSD is based on the following papers: + (1) V. Bertacco and M. Damiani, "Disjunctive decomposition of + logic functions," Proc. ICCAD '97, pp. 78-82. + (2) Y. Matsunaga, "An exact and efficient algorithm for disjunctive + decomposition", Proc. SASIMI '98, pp. 44-50. + The scope of detected decompositions is the same as in the paper: + T. Sasao and M. Matsuura, "DECOMPOS: An integrated system for + functional decomposition," Proc. IWLS '98, pp. 471-477.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 8.0. Started - September 22, 2003.] + + Revision [$Id: dsd.h,v 1.0 2002/22/09 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __DSD_H__ +#define __DSD_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// TYPEDEF DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Dsd_Manager_t_ Dsd_Manager_t; +typedef struct Dsd_Node_t_ Dsd_Node_t; +typedef enum Dsd_Type_t_ Dsd_Type_t; + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +// types of DSD nodes +enum Dsd_Type_t_ { + DSD_NODE_NONE = 0, + DSD_NODE_CONST1 = 1, + DSD_NODE_BUF = 2, + DSD_NODE_OR = 3, + DSD_NODE_EXOR = 4, + DSD_NODE_PRIME = 5, +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// complementation and testing for pointers for decomposition entries +#define Dsd_IsComplement(p) (((int)((unsigned long) (p) & 01))) +#define Dsd_Regular(p) ((Dsd_Node_t *)((unsigned long)(p) & ~01)) +#define Dsd_Not(p) ((Dsd_Node_t *)((unsigned long)(p) ^ 01)) +#define Dsd_NotCond(p,c) ((Dsd_Node_t *)((unsigned long)(p) ^ (c))) + +//////////////////////////////////////////////////////////////////////// +/// ITERATORS /// +//////////////////////////////////////////////////////////////////////// + +// iterator through the transitions +#define Dsd_NodeForEachChild( Node, Index, Child ) \ + for ( Index = 0; \ + Index < Dsd_NodeReadDecsNum(Node) && \ + ((Child = Dsd_NodeReadDec(Node,Index))>=0); \ + Index++ ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== dsdApi.c =======================================================*/ +extern Dsd_Type_t Dsd_NodeReadType( Dsd_Node_t * p ); +extern DdNode * Dsd_NodeReadFunc( Dsd_Node_t * p ); +extern DdNode * Dsd_NodeReadSupp( Dsd_Node_t * p ); +extern Dsd_Node_t ** Dsd_NodeReadDecs( Dsd_Node_t * p ); +extern Dsd_Node_t * Dsd_NodeReadDec ( Dsd_Node_t * p, int i ); +extern int Dsd_NodeReadDecsNum( Dsd_Node_t * p ); +extern int Dsd_NodeReadMark( Dsd_Node_t * p ); +extern void Dsd_NodeSetMark( Dsd_Node_t * p, int Mark ); +extern DdManager * Dsd_ManagerReadDd( Dsd_Manager_t * pMan ); +extern Dsd_Node_t * Dsd_ManagerReadRoot( Dsd_Manager_t * pMan, int i ); +extern Dsd_Node_t * Dsd_ManagerReadInput( Dsd_Manager_t * pMan, int i ); +extern Dsd_Node_t * Dsd_ManagerReadConst1( Dsd_Manager_t * pMan ); +/*=== dsdMan.c =======================================================*/ +extern Dsd_Manager_t * Dsd_ManagerStart( DdManager * dd, int nSuppMax, int fVerbose ); +extern void Dsd_ManagerStop( Dsd_Manager_t * dMan ); +/*=== dsdProc.c =======================================================*/ +extern void Dsd_Decompose( Dsd_Manager_t * dMan, DdNode ** pbFuncs, int nFuncs ); +extern Dsd_Node_t * Dsd_DecomposeOne( Dsd_Manager_t * pDsdMan, DdNode * bFunc ); +/*=== dsdTree.c =======================================================*/ +extern void Dsd_TreeNodeGetInfo( Dsd_Manager_t * dMan, int * DepthMax, int * GateSizeMax ); +extern void Dsd_TreeNodeGetInfoOne( Dsd_Node_t * pNode, int * DepthMax, int * GateSizeMax ); +extern int Dsd_TreeGetAigCost( Dsd_Node_t * pNode ); +extern int Dsd_TreeCountNonTerminalNodes( Dsd_Manager_t * dMan ); +extern int Dsd_TreeCountNonTerminalNodesOne( Dsd_Node_t * pRoot ); +extern int Dsd_TreeCountPrimeNodes( Dsd_Manager_t * pDsdMan ); +extern int Dsd_TreeCountPrimeNodesOne( Dsd_Node_t * pRoot ); +extern int Dsd_TreeCollectDecomposableVars( Dsd_Manager_t * dMan, int * pVars ); +extern Dsd_Node_t ** Dsd_TreeCollectNodesDfs( Dsd_Manager_t * dMan, int * pnNodes ); +extern Dsd_Node_t ** Dsd_TreeCollectNodesDfsOne( Dsd_Manager_t * pDsdMan, Dsd_Node_t * pNode, int * pnNodes ); +extern void Dsd_TreePrint( FILE * pFile, Dsd_Manager_t * dMan, char * pInputNames[], char * pOutputNames[], int fShortNames, int Output ); +extern void Dsd_NodePrint( FILE * pFile, Dsd_Node_t * pNode ); +/*=== dsdLocal.c =======================================================*/ +extern DdNode * Dsd_TreeGetPrimeFunction( DdManager * dd, Dsd_Node_t * pNode ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/bdd/dsd/dsdApi.c b/abc_with_bb_support/src/bdd/dsd/dsdApi.c new file mode 100644 index 000000000..3e75170e5 --- /dev/null +++ b/abc_with_bb_support/src/bdd/dsd/dsdApi.c @@ -0,0 +1,97 @@ +/**CFile**************************************************************** + + FileName [dsdApi.c] + + PackageName [DSD: Disjoint-support decomposition package.] + + Synopsis [Implementation of API functions.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 8.0. Started - September 22, 2003.] + + Revision [$Id: dsdApi.c,v 1.0 2002/22/09 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "dsdInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [APIs of the DSD node.] + + Description [The node's type can be retrieved by calling + Dsd_NodeReadType(). The type is one of the following: constant 1 node, + the buffer (or the elementary variable), OR gate, EXOR gate, or + PRIME function (a non-DSD-decomposable function with more than two + inputs). The return value of Dsd_NodeReadFunc() is the global function + of the DSD node. The return value of Dsd_NodeReadSupp() is the support + of the global function of the DSD node. The array of DSD nodes + returned by Dsd_NodeReadDecs() is the array of decomposition nodes for + the formal inputs of the given node. The number of decomposition entries + returned by Dsd_NodeReadDecsNum() is the number of formal inputs. + The mark is explained below.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dsd_Type_t Dsd_NodeReadType( Dsd_Node_t * p ) { return p->Type; } +DdNode * Dsd_NodeReadFunc( Dsd_Node_t * p ) { return p->G; } +DdNode * Dsd_NodeReadSupp( Dsd_Node_t * p ) { return p->S; } +Dsd_Node_t ** Dsd_NodeReadDecs( Dsd_Node_t * p ) { return p->pDecs; } +Dsd_Node_t * Dsd_NodeReadDec ( Dsd_Node_t * p, int i ) { return p->pDecs[i]; } +int Dsd_NodeReadDecsNum( Dsd_Node_t * p ) { return p->nDecs; } +int Dsd_NodeReadMark( Dsd_Node_t * p ) { return p->Mark; } + +/**Function************************************************************* + + Synopsis [APIs of the DSD node.] + + Description [This API allows the user to set the integer mark in the + given DSD node. The mark is guaranteed to persist as long as the + calls to the decomposition are not performed. In any case, the mark + is useful to associate the node with some temporary information, such + as its number in the DFS ordered list of the DSD nodes or its number in + the BLIF file that it being written.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dsd_NodeSetMark( Dsd_Node_t * p, int Mark ){ p->Mark = Mark; } + +/**Function************************************************************* + + Synopsis [APIs of the DSD manager.] + + Description [Allows the use to get hold of an individual leave of + the DSD tree (Dsd_ManagerReadInput) or an individual root of the + decomposition tree (Dsd_ManagerReadRoot). The root may have the + complemented attribute.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dsd_Node_t * Dsd_ManagerReadRoot( Dsd_Manager_t * pMan, int i ) { return pMan->pRoots[i]; } +Dsd_Node_t * Dsd_ManagerReadInput( Dsd_Manager_t * pMan, int i ) { return pMan->pInputs[i]; } +Dsd_Node_t * Dsd_ManagerReadConst1( Dsd_Manager_t * pMan ) { return pMan->pConst1; } +DdManager * Dsd_ManagerReadDd( Dsd_Manager_t * pMan ) { return pMan->dd; } + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/bdd/dsd/dsdCheck.c b/abc_with_bb_support/src/bdd/dsd/dsdCheck.c new file mode 100644 index 000000000..9d32f7b7d --- /dev/null +++ b/abc_with_bb_support/src/bdd/dsd/dsdCheck.c @@ -0,0 +1,314 @@ +/**CFile**************************************************************** + + FileName [dsdCheck.c] + + PackageName [DSD: Disjoint-support decomposition package.] + + Synopsis [Procedures to check the identity of root functions.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 8.0. Started - September 22, 2003.] + + Revision [$Id: dsdCheck.c,v 1.0 2002/22/09 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "dsdInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Dsd_Cache_t_ Dds_Cache_t; +typedef struct Dsd_Entry_t_ Dsd_Entry_t; + +struct Dsd_Cache_t_ +{ + Dsd_Entry_t * pTable; + int nTableSize; + int nSuccess; + int nFailure; +}; + +struct Dsd_Entry_t_ +{ + DdNode * bX[5]; +}; + +static Dds_Cache_t * pCache; + +static int Dsd_CheckRootFunctionIdentity_rec( DdManager * dd, DdNode * bF1, DdNode * bF2, DdNode * bC1, DdNode * bC2 ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function******************************************************************** + + Synopsis [(Re)allocates the local cache.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void Dsd_CheckCacheAllocate( int nEntries ) +{ + int nRequested; + + pCache = ALLOC( Dds_Cache_t, 1 ); + memset( pCache, 0, sizeof(Dds_Cache_t) ); + + // check what is the size of the current cache + nRequested = Cudd_Prime( nEntries ); + if ( pCache->nTableSize != nRequested ) + { // the current size is different + // deallocate the old, allocate the new + if ( pCache->nTableSize ) + Dsd_CheckCacheDeallocate(); + // allocate memory for the hash table + pCache->nTableSize = nRequested; + pCache->pTable = ALLOC( Dsd_Entry_t, nRequested ); + } + // otherwise, there is no need to allocate, just clean + Dsd_CheckCacheClear(); +// printf( "\nThe number of allocated cache entries = %d.\n\n", pCache->nTableSize ); +} + +/**Function******************************************************************** + + Synopsis [Deallocates the local cache.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void Dsd_CheckCacheDeallocate() +{ + free( pCache->pTable ); + free( pCache ); +} + +/**Function******************************************************************** + + Synopsis [Clears the local cache.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void Dsd_CheckCacheClear() +{ + int i; + for ( i = 0; i < pCache->nTableSize; i++ ) + pCache->pTable[0].bX[0] = NULL; +} + + +/**Function******************************************************************** + + Synopsis [Checks whether it is true that bF1(bC1=0) == bF2(bC2=0).] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int Dsd_CheckRootFunctionIdentity( DdManager * dd, DdNode * bF1, DdNode * bF2, DdNode * bC1, DdNode * bC2 ) +{ + int RetValue; +// pCache->nSuccess = 0; +// pCache->nFailure = 0; + RetValue = Dsd_CheckRootFunctionIdentity_rec(dd, bF1, bF2, bC1, bC2); +// printf( "Cache success = %d. Cache failure = %d.\n", pCache->nSuccess, pCache->nFailure ); + return RetValue; +} + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Dsd_CheckRootFunctionIdentity().] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int Dsd_CheckRootFunctionIdentity_rec( DdManager * dd, DdNode * bF1, DdNode * bF2, DdNode * bC1, DdNode * bC2 ) +{ + unsigned HKey; + + // if either bC1 or bC2 is zero, the test is true +// if ( bC1 == b0 || bC2 == b0 ) return 1; + assert( bC1 != b0 ); + assert( bC2 != b0 ); + + // if both bC1 and bC2 are one - perform comparison + if ( bC1 == b1 && bC2 == b1 ) return (int)( bF1 == bF2 ); + + if ( bF1 == b0 ) + return Cudd_bddLeq( dd, bC2, Cudd_Not(bF2) ); + + if ( bF1 == b1 ) + return Cudd_bddLeq( dd, bC2, bF2 ); + + if ( bF2 == b0 ) + return Cudd_bddLeq( dd, bC1, Cudd_Not(bF1) ); + + if ( bF2 == b1 ) + return Cudd_bddLeq( dd, bC1, bF1 ); + + // otherwise, keep expanding + + // check cache +// HKey = _Hash( ((unsigned)bF1), ((unsigned)bF2), ((unsigned)bC1), ((unsigned)bC2) ); + HKey = hashKey4( bF1, bF2, bC1, bC2, pCache->nTableSize ); + if ( pCache->pTable[HKey].bX[0] == bF1 && + pCache->pTable[HKey].bX[1] == bF2 && + pCache->pTable[HKey].bX[2] == bC1 && + pCache->pTable[HKey].bX[3] == bC2 ) + { + pCache->nSuccess++; + return (int)pCache->pTable[HKey].bX[4]; // the last bit records the result (yes/no) + } + else + { + + // determine the top variables + int RetValue; + DdNode * bA[4] = { bF1, bF2, bC1, bC2 }; // arguments + DdNode * bAR[4] = { Cudd_Regular(bF1), Cudd_Regular(bF2), Cudd_Regular(bC1), Cudd_Regular(bC2) }; // regular arguments + int CurLevel[4] = { cuddI(dd,bAR[0]->index), cuddI(dd,bAR[1]->index), cuddI(dd,bAR[2]->index), cuddI(dd,bAR[3]->index) }; + int TopLevel = CUDD_CONST_INDEX; + int i; + DdNode * bE[4], * bT[4]; + DdNode * bF1next, * bF2next, * bC1next, * bC2next; + + pCache->nFailure++; + + // determine the top level + for ( i = 0; i < 4; i++ ) + if ( TopLevel > CurLevel[i] ) + TopLevel = CurLevel[i]; + + // compute the cofactors + for ( i = 0; i < 4; i++ ) + if ( TopLevel == CurLevel[i] ) + { + if ( bA[i] != bAR[i] ) // complemented + { + bE[i] = Cudd_Not(cuddE(bAR[i])); + bT[i] = Cudd_Not(cuddT(bAR[i])); + } + else + { + bE[i] = cuddE(bAR[i]); + bT[i] = cuddT(bAR[i]); + } + } + else + bE[i] = bT[i] = bA[i]; + + // solve subproblems + // three cases are possible + + // (1) the top var belongs to both C1 and C2 + // in this case, any cofactor of F1 and F2 will do, + // as long as the corresponding cofactor of C1 and C2 is not equal to 0 + if ( TopLevel == CurLevel[2] && TopLevel == CurLevel[3] ) + { + if ( bE[2] != b0 ) // C1 + { + bF1next = bE[0]; + bC1next = bE[2]; + } + else + { + bF1next = bT[0]; + bC1next = bT[2]; + } + if ( bE[3] != b0 ) // C2 + { + bF2next = bE[1]; + bC2next = bE[3]; + } + else + { + bF2next = bT[1]; + bC2next = bT[3]; + } + RetValue = Dsd_CheckRootFunctionIdentity_rec( dd, bF1next, bF2next, bC1next, bC2next ); + } + // (2) the top var belongs to either C1 or C2 + // in this case normal splitting of cofactors + else if ( TopLevel == CurLevel[2] && TopLevel != CurLevel[3] ) + { + if ( bE[2] != b0 ) // C1 + { + bF1next = bE[0]; + bC1next = bE[2]; + } + else + { + bF1next = bT[0]; + bC1next = bT[2]; + } + // split around this variable + RetValue = Dsd_CheckRootFunctionIdentity_rec( dd, bF1next, bE[1], bC1next, bE[3] ); + if ( RetValue == 1 ) // test another branch; otherwise, there is no need to test + RetValue = Dsd_CheckRootFunctionIdentity_rec( dd, bF1next, bT[1], bC1next, bT[3] ); + } + else if ( TopLevel != CurLevel[2] && TopLevel == CurLevel[3] ) + { + if ( bE[3] != b0 ) // C2 + { + bF2next = bE[1]; + bC2next = bE[3]; + } + else + { + bF2next = bT[1]; + bC2next = bT[3]; + } + // split around this variable + RetValue = Dsd_CheckRootFunctionIdentity_rec( dd, bE[0], bF2next, bE[2], bC2next ); + if ( RetValue == 1 ) // test another branch; otherwise, there is no need to test + RetValue = Dsd_CheckRootFunctionIdentity_rec( dd, bT[0], bF2next, bT[2], bC2next ); + } + // (3) the top var does not belong to C1 and C2 + // in this case normal splitting of cofactors + else // if ( TopLevel != CurLevel[2] && TopLevel != CurLevel[3] ) + { + // split around this variable + RetValue = Dsd_CheckRootFunctionIdentity_rec( dd, bE[0], bE[1], bE[2], bE[3] ); + if ( RetValue == 1 ) // test another branch; otherwise, there is no need to test + RetValue = Dsd_CheckRootFunctionIdentity_rec( dd, bT[0], bT[1], bT[2], bT[3] ); + } + + // set cache + for ( i = 0; i < 4; i++ ) + pCache->pTable[HKey].bX[i] = bA[i]; + pCache->pTable[HKey].bX[4] = (DdNode*)RetValue; + + return RetValue; + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/bdd/dsd/dsdInt.h b/abc_with_bb_support/src/bdd/dsd/dsdInt.h new file mode 100644 index 000000000..69364509a --- /dev/null +++ b/abc_with_bb_support/src/bdd/dsd/dsdInt.h @@ -0,0 +1,91 @@ +/**CFile**************************************************************** + + FileName [dsdInt.h] + + PackageName [DSD: Disjoint-support decomposition package.] + + Synopsis [Internal declarations of the package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 8.0. Started - September 22, 2003.] + + Revision [$Id: dsdInt.h,v 1.0 2002/22/09 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __DSD_INT_H__ +#define __DSD_INT_H__ + +#include "extra.h" +#include "dsd.h" + +//////////////////////////////////////////////////////////////////////// +/// TYPEDEF DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef unsigned char byte; + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// DSD manager +struct Dsd_Manager_t_ +{ + DdManager * dd; // the BDD manager + st_table * Table; // the mapping of BDDs into their DEs + int nInputs; // the number of primary inputs + int nRoots; // the number of primary outputs + int nRootsAlloc;// the number of primary outputs + Dsd_Node_t ** pInputs; // the primary input nodes + Dsd_Node_t ** pRoots; // the primary output nodes + Dsd_Node_t * pConst1; // the constant node + int fVerbose; // the verbosity level +}; + +// DSD node +struct Dsd_Node_t_ +{ + Dsd_Type_t Type; // decomposition type + DdNode * G; // function of the node + DdNode * S; // support of this function + Dsd_Node_t ** pDecs; // pointer to structures for formal inputs + int Mark; // the mark used by CASE 4 of disjoint decomposition + short nDecs; // the number of formal inputs + short nVisits; // the counter of visits +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define MAXINPUTS 1000 + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== dsdCheck.c =======================================================*/ +extern void Dsd_CheckCacheAllocate( int nEntries ); +extern void Dsd_CheckCacheDeallocate(); +extern void Dsd_CheckCacheClear(); +extern int Dsd_CheckRootFunctionIdentity( DdManager * dd, DdNode * bF1, DdNode * bF2, DdNode * bC1, DdNode * bC2 ); +/*=== dsdTree.c =======================================================*/ +extern Dsd_Node_t * Dsd_TreeNodeCreate( int Type, int nDecs, int BlockNum ); +extern void Dsd_TreeNodeDelete( DdManager * dd, Dsd_Node_t * pNode ); +extern void Dsd_TreeUnmark( Dsd_Manager_t * dMan ); +extern DdNode * Dsd_TreeGetPrimeFunctionOld( DdManager * dd, Dsd_Node_t * pNode, int fRemap ); + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/bdd/dsd/dsdLocal.c b/abc_with_bb_support/src/bdd/dsd/dsdLocal.c new file mode 100644 index 000000000..f94ea3e57 --- /dev/null +++ b/abc_with_bb_support/src/bdd/dsd/dsdLocal.c @@ -0,0 +1,337 @@ +/**CFile**************************************************************** + + FileName [dsdLocal.c] + + PackageName [DSD: Disjoint-support decomposition package.] + + Synopsis [Deriving the local function of the DSD node.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 8.0. Started - September 22, 2003.] + + Revision [$Id: dsdLocal.c,v 1.0 2002/22/09 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "dsdInt.h" + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// STATIC VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +static DdNode * Extra_dsdRemap( DdManager * dd, DdNode * bFunc, st_table * pCache, + int * pVar2Form, int * pForm2Var, DdNode * pbCube0[], DdNode * pbCube1[] ); +static DdNode * Extra_bddNodePointedByCube( DdManager * dd, DdNode * bF, DdNode * bC ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Returns the local function of the DSD node. ] + + Description [The local function is computed using the global function + of the node and the global functions of the formal inputs. The resulting + local function is mapped using the topmost N variables of the manager. + The number of variables N is equal to the number of formal inputs.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Dsd_TreeGetPrimeFunction( DdManager * dd, Dsd_Node_t * pNode ) +{ + int * pForm2Var; // the mapping of each formal input into its first var + int * pVar2Form; // the mapping of each var into its formal inputs + int i, iVar, iLev, * pPermute; + DdNode ** pbCube0, ** pbCube1; + DdNode * bFunc, * bRes, * bTemp; + st_table * pCache; + + pPermute = ALLOC( int, dd->size ); + pVar2Form = ALLOC( int, dd->size ); + pForm2Var = ALLOC( int, dd->size ); + + pbCube0 = ALLOC( DdNode *, dd->size ); + pbCube1 = ALLOC( DdNode *, dd->size ); + + // remap the global function in such a way that + // the support variables of each formal input are adjacent + iLev = 0; + for ( i = 0; i < pNode->nDecs; i++ ) + { + pForm2Var[i] = dd->invperm[i]; + for ( bTemp = pNode->pDecs[i]->S; bTemp != b1; bTemp = cuddT(bTemp) ) + { + iVar = dd->invperm[iLev]; + pPermute[bTemp->index] = iVar; + pVar2Form[iVar] = i; + iLev++; + } + + // collect the cubes representing each assignment + pbCube0[i] = Extra_bddGetOneCube( dd, Cudd_Not(pNode->pDecs[i]->G) ); + Cudd_Ref( pbCube0[i] ); + pbCube1[i] = Extra_bddGetOneCube( dd, pNode->pDecs[i]->G ); + Cudd_Ref( pbCube1[i] ); + } + + // remap the function + bFunc = Cudd_bddPermute( dd, pNode->G, pPermute ); Cudd_Ref( bFunc ); + // remap the cube + for ( i = 0; i < pNode->nDecs; i++ ) + { + pbCube0[i] = Cudd_bddPermute( dd, bTemp = pbCube0[i], pPermute ); Cudd_Ref( pbCube0[i] ); + Cudd_RecursiveDeref( dd, bTemp ); + pbCube1[i] = Cudd_bddPermute( dd, bTemp = pbCube1[i], pPermute ); Cudd_Ref( pbCube1[i] ); + Cudd_RecursiveDeref( dd, bTemp ); + } + + // remap the function + pCache = st_init_table(st_ptrcmp,st_ptrhash); + bRes = Extra_dsdRemap( dd, bFunc, pCache, pVar2Form, pForm2Var, pbCube0, pbCube1 ); Cudd_Ref( bRes ); + st_free_table( pCache ); + + Cudd_RecursiveDeref( dd, bFunc ); + for ( i = 0; i < pNode->nDecs; i++ ) + { + Cudd_RecursiveDeref( dd, pbCube0[i] ); + Cudd_RecursiveDeref( dd, pbCube1[i] ); + } +/* +//////////// + // permute the function once again + // in such a way that i-th var stood for i-th formal input + for ( i = 0; i < dd->size; i++ ) + pPermute[i] = -1; + for ( i = 0; i < pNode->nDecs; i++ ) + pPermute[dd->invperm[i]] = i; + bRes = Cudd_bddPermute( dd, bTemp = bRes, pPermute ); Cudd_Ref( bRes ); + Cudd_RecursiveDeref( dd, bTemp ); +//////////// +*/ + FREE(pPermute); + FREE(pVar2Form); + FREE(pForm2Var); + FREE(pbCube0); + FREE(pbCube1); + + Cudd_Deref( bRes ); + return bRes; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Extra_dsdRemap( DdManager * dd, DdNode * bF, st_table * pCache, + int * pVar2Form, int * pForm2Var, DdNode * pbCube0[], DdNode * pbCube1[] ) +{ + DdNode * bFR, * bF0, * bF1; + DdNode * bRes0, * bRes1, * bRes; + int iForm; + + bFR = Cudd_Regular(bF); + if ( cuddIsConstant(bFR) ) + return bF; + + // check the hash-table + if ( bFR->ref != 1 ) + { + if ( st_lookup( pCache, (char *)bF, (char **)&bRes ) ) + return bRes; + } + + // get the formal input + iForm = pVar2Form[bFR->index]; + + // get the nodes pointed to by the cube + bF0 = Extra_bddNodePointedByCube( dd, bF, pbCube0[iForm] ); + bF1 = Extra_bddNodePointedByCube( dd, bF, pbCube1[iForm] ); + + // call recursively for these nodes + bRes0 = Extra_dsdRemap( dd, bF0, pCache, pVar2Form, pForm2Var, pbCube0, pbCube1 ); Cudd_Ref( bRes0 ); + bRes1 = Extra_dsdRemap( dd, bF1, pCache, pVar2Form, pForm2Var, pbCube0, pbCube1 ); Cudd_Ref( bRes1 ); + + // derive the result using ITE + bRes = Cudd_bddIte( dd, dd->vars[ pForm2Var[iForm] ], bRes1, bRes0 ); Cudd_Ref( bRes ); + Cudd_RecursiveDeref( dd, bRes0 ); + Cudd_RecursiveDeref( dd, bRes1 ); + + // add to the hash table + if ( bFR->ref != 1 ) + st_insert( pCache, (char *)bF, (char *)bRes ); + Cudd_Deref( bRes ); + return bRes; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Extra_bddNodePointedByCube( DdManager * dd, DdNode * bF, DdNode * bC ) +{ + DdNode * bFR, * bCR; + DdNode * bF0, * bF1; + DdNode * bC0, * bC1; + int LevelF, LevelC; + + assert( bC != b0 ); + if ( bC == b1 ) + return bF; + +// bRes = cuddCacheLookup2( dd, Extra_bddNodePointedByCube, bF, bC ); +// if ( bRes ) +// return bRes; + // there is no need for caching because this operation is very fast + // there will no gain reusing the results of this operations + // instead, it will flush CUDD cache of other useful entries + + + bFR = Cudd_Regular( bF ); + bCR = Cudd_Regular( bC ); + assert( !cuddIsConstant( bFR ) ); + + LevelF = dd->perm[bFR->index]; + LevelC = dd->perm[bCR->index]; + + if ( LevelF <= LevelC ) + { + if ( bFR != bF ) + { + bF0 = Cudd_Not( cuddE(bFR) ); + bF1 = Cudd_Not( cuddT(bFR) ); + } + else + { + bF0 = cuddE(bFR); + bF1 = cuddT(bFR); + } + } + else + { + bF0 = bF1 = bF; + } + + if ( LevelC <= LevelF ) + { + if ( bCR != bC ) + { + bC0 = Cudd_Not( cuddE(bCR) ); + bC1 = Cudd_Not( cuddT(bCR) ); + } + else + { + bC0 = cuddE(bCR); + bC1 = cuddT(bCR); + } + } + else + { + bC0 = bC1 = bC; + } + + assert( bC0 == b0 || bC1 == b0 ); + if ( bC0 == b0 ) + return Extra_bddNodePointedByCube( dd, bF1, bC1 ); + return Extra_bddNodePointedByCube( dd, bF0, bC0 ); +} + +#if 0 + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * dsdTreeGetPrimeFunction( DdManager * dd, Dsd_Node_t * pNode, int fRemap ) +{ + DdNode * bCof0, * bCof1, * bCube0, * bCube1, * bNewFunc, * bTemp; + int i; + int fAllBuffs = 1; + static int Permute[MAXINPUTS]; + + assert( pNode ); + assert( !Dsd_IsComplement( pNode ) ); + assert( pNode->Type == DT_PRIME ); + + // transform the function of this block to depend on inputs + // corresponding to the formal inputs + + // first, substitute those inputs that have some blocks associated with them + // second, remap the inputs to the top of the manager (then, it is easy to output them) + + // start the function + bNewFunc = pNode->G; Cudd_Ref( bNewFunc ); + // go over all primary inputs + for ( i = 0; i < pNode->nDecs; i++ ) + if ( pNode->pDecs[i]->Type != DT_BUF ) // remap only if it is not the buffer + { + bCube0 = Extra_bddFindOneCube( dd, Cudd_Not(pNode->pDecs[i]->G) ); Cudd_Ref( bCube0 ); + bCof0 = Cudd_Cofactor( dd, bNewFunc, bCube0 ); Cudd_Ref( bCof0 ); + Cudd_RecursiveDeref( dd, bCube0 ); + + bCube1 = Extra_bddFindOneCube( dd, pNode->pDecs[i]->G ); Cudd_Ref( bCube1 ); + bCof1 = Cudd_Cofactor( dd, bNewFunc, bCube1 ); Cudd_Ref( bCof1 ); + Cudd_RecursiveDeref( dd, bCube1 ); + + Cudd_RecursiveDeref( dd, bNewFunc ); + + // use the variable in the i-th level of the manager +// bNewFunc = Cudd_bddIte( dd, dd->vars[dd->invperm[i]],bCof1,bCof0 ); Cudd_Ref( bNewFunc ); + // use the first variale in the support of the component + bNewFunc = Cudd_bddIte( dd, dd->vars[pNode->pDecs[i]->S->index],bCof1,bCof0 ); Cudd_Ref( bNewFunc ); + Cudd_RecursiveDeref( dd, bCof0 ); + Cudd_RecursiveDeref( dd, bCof1 ); + } + + if ( fRemap ) + { + // remap the function to the top of the manager + // remap the function to the first variables of the manager + for ( i = 0; i < pNode->nDecs; i++ ) + // Permute[ pNode->pDecs[i]->S->index ] = dd->invperm[i]; + Permute[ pNode->pDecs[i]->S->index ] = i; + + bNewFunc = Cudd_bddPermute( dd, bTemp = bNewFunc, Permute ); Cudd_Ref( bNewFunc ); + Cudd_RecursiveDeref( dd, bTemp ); + } + + Cudd_Deref( bNewFunc ); + return bNewFunc; +} + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/bdd/dsd/dsdMan.c b/abc_with_bb_support/src/bdd/dsd/dsdMan.c new file mode 100644 index 000000000..7712ac076 --- /dev/null +++ b/abc_with_bb_support/src/bdd/dsd/dsdMan.c @@ -0,0 +1,114 @@ +/**CFile**************************************************************** + + FileName [dsdMan.c] + + PackageName [DSD: Disjoint-support decomposition package.] + + Synopsis [APIs of the DSD manager.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 8.0. Started - September 22, 2003.] + + Revision [$Id: dsdMan.c,v 1.0 2002/22/09 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "dsdInt.h" + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// API OF DSD MANAGER /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the DSD manager.] + + Description [Takes the started BDD manager and the maximum support size + of the function to be DSD-decomposed. The manager should have at least as + many variables as there are variables in the support. The functions should + be expressed using the first nSuppSizeMax variables in the manager (these + may be ordered not necessarily on top of the manager).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dsd_Manager_t * Dsd_ManagerStart( DdManager * dd, int nSuppMax, int fVerbose ) +{ + Dsd_Manager_t * dMan; + Dsd_Node_t * pNode; + int i; + + assert( nSuppMax <= dd->size ); + + dMan = ALLOC( Dsd_Manager_t, 1 ); + memset( dMan, 0, sizeof(Dsd_Manager_t) ); + dMan->dd = dd; + dMan->nInputs = nSuppMax; + dMan->fVerbose = fVerbose; + dMan->nRoots = 0; + dMan->nRootsAlloc = 50; + dMan->pRoots = (Dsd_Node_t **) malloc( dMan->nRootsAlloc * sizeof(Dsd_Node_t *) ); + dMan->pInputs = (Dsd_Node_t **) malloc( dMan->nInputs * sizeof(Dsd_Node_t *) ); + + // create the primary inputs and insert them into the table + dMan->Table = st_init_table(st_ptrcmp, st_ptrhash); + for ( i = 0; i < dMan->nInputs; i++ ) + { + pNode = Dsd_TreeNodeCreate( DSD_NODE_BUF, 1, 0 ); + pNode->G = dd->vars[i]; Cudd_Ref( pNode->G ); + pNode->S = dd->vars[i]; Cudd_Ref( pNode->S ); + st_insert( dMan->Table, (char*)dd->vars[i], (char*)pNode ); + dMan->pInputs[i] = pNode; + } + pNode = Dsd_TreeNodeCreate( DSD_NODE_CONST1, 0, 0 ); + pNode->G = b1; Cudd_Ref( pNode->G ); + pNode->S = b1; Cudd_Ref( pNode->S ); + st_insert( dMan->Table, (char*)b1, (char*)pNode ); + dMan->pConst1 = pNode; + + Dsd_CheckCacheAllocate( 5000 ); + return dMan; +} + +/**Function************************************************************* + + Synopsis [Stops the DSD manager.] + + Description [Stopping the DSD manager automatically derefereces and + deallocates all the DSD nodes that were created during the life time + of the DSD manager. As a result, the user does not need to deref or + deallocate any DSD nodes or trees that are derived and placed in + the manager while it exists.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dsd_ManagerStop( Dsd_Manager_t * dMan ) +{ + st_generator * gen; + Dsd_Node_t * pNode; + DdNode * bFunc; + // delete the nodes + st_foreach_item( dMan->Table, gen, (char**)&bFunc, (char**)&pNode ) + Dsd_TreeNodeDelete( dMan->dd, Dsd_Regular(pNode) ); + st_free_table(dMan->Table); + free( dMan->pInputs ); + free( dMan->pRoots ); + free( dMan ); + Dsd_CheckCacheDeallocate(); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/bdd/dsd/dsdProc.c b/abc_with_bb_support/src/bdd/dsd/dsdProc.c new file mode 100644 index 000000000..6677ee7f8 --- /dev/null +++ b/abc_with_bb_support/src/bdd/dsd/dsdProc.c @@ -0,0 +1,1617 @@ +/**CFile**************************************************************** + + FileName [dsdProc.c] + + PackageName [DSD: Disjoint-support decomposition package.] + + Synopsis [The core procedures of the package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 8.0. Started - September 22, 2003.] + + Revision [$Id: dsdProc.c,v 1.0 2002/22/09 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "dsdInt.h" + + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// the most important procedures +void dsdKernelDecompose( Dsd_Manager_t * pDsdMan, DdNode ** pbFuncs, int nFuncs ); +static Dsd_Node_t * dsdKernelDecompose_rec( Dsd_Manager_t * pDsdMan, DdNode * F ); + +// additional procedures +static Dsd_Node_t * dsdKernelFindContainingComponent( Dsd_Manager_t * pDsdMan, Dsd_Node_t * pWhere, DdNode * Var, int * fPolarity ); +static int dsdKernelFindCommonComponents( Dsd_Manager_t * pDsdMan, Dsd_Node_t * pL, Dsd_Node_t * pH, Dsd_Node_t *** pCommon, Dsd_Node_t ** pLastDiffL, Dsd_Node_t ** pLastDiffH ); +static void dsdKernelComputeSumOfComponents( Dsd_Manager_t * pDsdMan, Dsd_Node_t ** pCommon, int nCommon, DdNode ** pCompF, DdNode ** pCompS, int fExor ); +static int dsdKernelCheckContainment( Dsd_Manager_t * pDsdMan, Dsd_Node_t * pL, Dsd_Node_t * pH, Dsd_Node_t ** pLarge, Dsd_Node_t ** pSmall ); + +// list copying +static void dsdKernelCopyListPlusOne( Dsd_Node_t * p, Dsd_Node_t * First, Dsd_Node_t ** ppList, int nListSize ); +static void dsdKernelCopyListPlusOneMinusOne( Dsd_Node_t * p, Dsd_Node_t * First, Dsd_Node_t ** ppList, int nListSize, int Skipped ); + +// debugging procedures +static int dsdKernelVerifyDecomposition( Dsd_Manager_t * pDsdMan, Dsd_Node_t * pDE ); + +//////////////////////////////////////////////////////////////////////// +/// STATIC VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +// the counter of marks +static int s_Mark; + +// debugging flag +static int s_Show = 0; +// temporary var used for debugging +static int Depth = 0; + +static int s_Loops1; +static int s_Loops2; +static int s_Loops3; +static int s_Pivot; +static int s_PivotNo; +static int s_Common; +static int s_CommonNo; + +static int s_Case4Calls; +static int s_Case4CallsSpecial; + +static int s_Case5; +static int s_Loops2Useless; + + +static int s_DecNodesTotal; +static int s_DecNodesUsed; + +// statistical variables +static int s_nDecBlocks; +static int s_nLiterals; +static int s_nExorGates; +static int s_nReusedBlocks; +static int s_nCascades; +static float s_nArea; +static float s_MaxDelay; +static long s_Time; +static int s_nInvertors; +static int s_nPrimeBlocks; + +static int HashSuccess = 0; +static int HashFailure = 0; + +static int s_CacheEntries; + + +//////////////////////////////////////////////////////////////////////// +/// DECOMPOSITION FUNCTIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs DSD for the array of functions represented by BDDs.] + + Description [This function takes the DSD manager, which should be + previously allocated by the call to Dsd_ManagerStart(). The resulting + DSD tree is stored in the DSD manager (pDsdMan->pRoots, pDsdMan->nRoots). + Access to the tree is through the APIs of the manager. The resulting + tree is a shared DSD DAG for the functions given in the array. For one + function the resulting DAG is always a tree. The root node pointers can + be complemented, as discussed in the literature referred to in "dsd.h". + This procedure can be called repeatedly for different functions. There is + no need to remove the decomposition tree after it is returned, because + the next call to the DSD manager will "recycle" the tree. The user should + not modify or dereference any data associated with the nodes of the + DSD trees (the user can only change the contents of a temporary + mark associated with each node by the calling to Dsd_NodeSetMark()). + All the decomposition trees and intermediate nodes will be removed when + the DSD manager is deallocated at the end by calling Dsd_ManagerStop().] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dsd_Decompose( Dsd_Manager_t * pDsdMan, DdNode ** pbFuncs, int nFuncs ) +{ + DdManager * dd = pDsdMan->dd; + int i; + long clk; + Dsd_Node_t * pTemp; + int SumMaxGateSize = 0; + int nDecOutputs = 0; + int nCBFOutputs = 0; +/* +s_Loops1 = 0; +s_Loops2 = 0; +s_Loops3 = 0; +s_Case4Calls = 0; +s_Case4CallsSpecial = 0; +s_Case5 = 0; +s_Loops2Useless = 0; +*/ + // resize the number of roots in the manager + if ( pDsdMan->nRootsAlloc < nFuncs ) + { + if ( pDsdMan->nRootsAlloc > 0 ) + free( pDsdMan->pRoots ); + pDsdMan->nRootsAlloc = nFuncs; + pDsdMan->pRoots = (Dsd_Node_t **) malloc( pDsdMan->nRootsAlloc * sizeof(Dsd_Node_t *) ); + } + + if ( pDsdMan->fVerbose ) + printf( "\nDecomposability statistics for individual outputs:\n" ); + + // set the counter of decomposition nodes + s_nDecBlocks = 0; + + // perform decomposition for all outputs + clk = clock(); + pDsdMan->nRoots = 0; + s_nCascades = 0; + for ( i = 0; i < nFuncs; i++ ) + { + int nLiteralsPrev; + int nDecBlocksPrev; + int nExorGatesPrev; + int nReusedBlocksPres; + int nCascades; + int MaxBlock; + int nPrimeBlocks; + long clk; + + clk = clock(); + nLiteralsPrev = s_nLiterals; + nDecBlocksPrev = s_nDecBlocks; + nExorGatesPrev = s_nExorGates; + nReusedBlocksPres = s_nReusedBlocks; + nPrimeBlocks = s_nPrimeBlocks; + + pDsdMan->pRoots[ pDsdMan->nRoots++ ] = dsdKernelDecompose_rec( pDsdMan, pbFuncs[i] ); + + Dsd_TreeNodeGetInfoOne( pDsdMan->pRoots[i], &nCascades, &MaxBlock ); + s_nCascades = ddMax( s_nCascades, nCascades ); + pTemp = Dsd_Regular(pDsdMan->pRoots[i]); + if ( pTemp->Type != DSD_NODE_PRIME || pTemp->nDecs != Extra_bddSuppSize(dd,pTemp->S) ) + nDecOutputs++; + if ( MaxBlock < 3 ) + nCBFOutputs++; + SumMaxGateSize += MaxBlock; + + if ( pDsdMan->fVerbose ) + { + printf("#%02d: ", i ); + printf("Ins=%2d. ", Cudd_SupportSize(dd,pbFuncs[i]) ); + printf("Gts=%3d. ", Dsd_TreeCountNonTerminalNodesOne( pDsdMan->pRoots[i] ) ); + printf("Pri=%3d. ", Dsd_TreeCountPrimeNodesOne( pDsdMan->pRoots[i] ) ); + printf("Max=%3d. ", MaxBlock ); + printf("Reuse=%2d. ", s_nReusedBlocks-nReusedBlocksPres ); + printf("Csc=%2d. ", nCascades ); + printf("T= %.2f s. ", (float)(clock()-clk)/(float)(CLOCKS_PER_SEC) ) ; + printf("Bdd=%2d. ", Cudd_DagSize(pbFuncs[i]) ); + printf("\n"); + fflush( stdout ); + } + } + assert( pDsdMan->nRoots == nFuncs ); + + if ( pDsdMan->fVerbose ) + { + printf( "\n" ); + printf( "The cumulative decomposability statistics:\n" ); + printf( " Total outputs = %5d\n", nFuncs ); + printf( " Decomposable outputs = %5d\n", nDecOutputs ); + printf( " Completely decomposable outputs = %5d\n", nCBFOutputs ); + printf( " The sum of max gate sizes = %5d\n", SumMaxGateSize ); + printf( " Shared BDD size = %5d\n", Cudd_SharingSize( pbFuncs, nFuncs ) ); + printf( " Decomposition entries = %5d\n", st_count( pDsdMan->Table ) ); + printf( " Pure decomposition time = %.2f sec\n", (float)(clock() - clk)/(float)(CLOCKS_PER_SEC) ); + } +/* + printf( "s_Loops1 = %d.\n", s_Loops1 ); + printf( "s_Loops2 = %d.\n", s_Loops2 ); + printf( "s_Loops3 = %d.\n", s_Loops3 ); + printf( "s_Case4Calls = %d.\n", s_Case4Calls ); + printf( "s_Case4CallsSpecial = %d.\n", s_Case4CallsSpecial ); + printf( "s_Case5 = %d.\n", s_Case5 ); + printf( "s_Loops2Useless = %d.\n", s_Loops2Useless ); +*/ +} + +/**Function************************************************************* + + Synopsis [Performs decomposition for one function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dsd_Node_t * Dsd_DecomposeOne( Dsd_Manager_t * pDsdMan, DdNode * bFunc ) +{ + return dsdKernelDecompose_rec( pDsdMan, bFunc ); +} + +/**Function************************************************************* + + Synopsis [The main function of this module. Recursive implementation of DSD.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dsd_Node_t * dsdKernelDecompose_rec( Dsd_Manager_t * pDsdMan, DdNode * bFunc0 ) +{ + DdManager * dd = pDsdMan->dd; + DdNode * bLow; + DdNode * bLowR; + DdNode * bHigh; + + int VarInt; + DdNode * bVarCur; + Dsd_Node_t * pVarCurDE; + // works only if var indices start from 0!!! + DdNode * bSuppNew = NULL, * bTemp; + + int fContained; + int nSuppLH; + int nSuppL; + int nSuppH; + + + + // various decomposition nodes + Dsd_Node_t * pThis, * pL, * pH, * pLR, * pHR; + + Dsd_Node_t * pSmallR, * pLargeR; + Dsd_Node_t * pTableEntry; + + + // treat the complemented case + DdNode * bF = Cudd_Regular(bFunc0); + int fCompF = (int)(bF != bFunc0); + + // check cache + if ( st_lookup( pDsdMan->Table, (char*)bF, (char**)&pTableEntry ) ) + { // the entry is present + HashSuccess++; + return Dsd_NotCond( pTableEntry, fCompF ); + } + HashFailure++; + Depth++; + + // proceed to consider "four cases" + ////////////////////////////////////////////////////////////////////// + // TERMINAL CASES - CASES 1 and 2 + ////////////////////////////////////////////////////////////////////// + bLow = cuddE(bF); + bLowR = Cudd_Regular(bLow); + bHigh = cuddT(bF); + VarInt = bF->index; + bVarCur = dd->vars[VarInt]; + pVarCurDE = pDsdMan->pInputs[VarInt]; + // works only if var indices start from 0!!! + bSuppNew = NULL; + + if ( bLowR->index == CUDD_CONST_INDEX || bHigh->index == CUDD_CONST_INDEX ) + { // one of the cofactors in the constant + if ( bHigh == b1 ) // bHigh cannot be equal to b0, because then it will be complemented + if ( bLow == b0 ) // bLow cannot be equal to b1, because then the node will have bLow == bHigh + ///////////////////////////////////////////////////////////////// + // bLow == 0, bHigh == 1, F = x'&0 + x&1 = x + ///////////////////////////////////////////////////////////////// + { // create the elementary variable node + assert(0); // should be already in the hash table + pThis = Dsd_TreeNodeCreate( DSD_NODE_BUF, 1, s_nDecBlocks++ ); + pThis->pDecs[0] = NULL; + } + else // if ( bLow != constant ) + ///////////////////////////////////////////////////////////////// + // bLow != const, bHigh == 1, F = x'&bLow + x&1 = bLow + x --- DSD_NODE_OR(x,bLow) + ///////////////////////////////////////////////////////////////// + { + pL = dsdKernelDecompose_rec( pDsdMan, bLow ); + pLR = Dsd_Regular( pL ); + bSuppNew = Cudd_bddAnd( dd, bVarCur, pLR->S ); Cudd_Ref(bSuppNew); + if ( pLR->Type == DSD_NODE_OR && pL == pLR ) // OR and no complement + { // add to the components + pThis = Dsd_TreeNodeCreate( DSD_NODE_OR, pL->nDecs+1, s_nDecBlocks++ ); + dsdKernelCopyListPlusOne( pThis, pVarCurDE, pL->pDecs, pL->nDecs ); + } + else // all other cases + { // create a new 2-input OR-gate + pThis = Dsd_TreeNodeCreate( DSD_NODE_OR, 2, s_nDecBlocks++ ); + dsdKernelCopyListPlusOne( pThis, pVarCurDE, &pL, 1 ); + } + } + else // if ( bHigh != const ) // meaning that bLow should be a constant + { + pH = dsdKernelDecompose_rec( pDsdMan, bHigh ); + pHR = Dsd_Regular( pH ); + bSuppNew = Cudd_bddAnd( dd, bVarCur, pHR->S ); Cudd_Ref(bSuppNew); + if ( bLow == b0 ) + ///////////////////////////////////////////////////////////////// + // Low == 0, High != 1, F = x'&0+x&High = (x'+High')'--- NOR(x',High') + ///////////////////////////////////////////////////////////////// + if ( pHR->Type == DSD_NODE_OR && pH != pHR ) // DSD_NODE_OR and complement + { // add to the components + pThis = Dsd_TreeNodeCreate( DSD_NODE_OR, pHR->nDecs+1, s_nDecBlocks++ ); + dsdKernelCopyListPlusOne( pThis, Dsd_Not(pVarCurDE), pHR->pDecs, pHR->nDecs ); + pThis = Dsd_Not(pThis); + } + else // all other cases + { // create a new 2-input NOR gate + pThis = Dsd_TreeNodeCreate( DSD_NODE_OR, 2, s_nDecBlocks++ ); + pH = Dsd_Not(pH); + dsdKernelCopyListPlusOne( pThis, Dsd_Not(pVarCurDE), &pH, 1 ); + pThis = Dsd_Not(pThis); + } + else // if ( bLow == b1 ) + ///////////////////////////////////////////////////////////////// + // Low == 1, High != 1, F = x'&1 + x&High = x' + High --- DSD_NODE_OR(x',High) + ///////////////////////////////////////////////////////////////// + if ( pHR->Type == DSD_NODE_OR && pH == pHR ) // OR and no complement + { // add to the components + pThis = Dsd_TreeNodeCreate( DSD_NODE_OR, pH->nDecs+1, s_nDecBlocks++ ); + dsdKernelCopyListPlusOne( pThis, Dsd_Not(pVarCurDE), pH->pDecs, pH->nDecs ); + } + else // all other cases + { // create a new 2-input OR-gate + pThis = Dsd_TreeNodeCreate( DSD_NODE_OR, 2, s_nDecBlocks++ ); + dsdKernelCopyListPlusOne( pThis, Dsd_Not(pVarCurDE), &pH, 1 ); + } + } + goto EXIT; + } + // else if ( bLow != const && bHigh != const ) + + // the case of equal cofactors (up to complementation) + if ( bLowR == bHigh ) + ///////////////////////////////////////////////////////////////// + // Low == G, High == G', F = x'&G + x&G' = (x(+)G) --- EXOR(x,Low) + ///////////////////////////////////////////////////////////////// + { + pL = dsdKernelDecompose_rec( pDsdMan, bLow ); + pLR = Dsd_Regular( pL ); + bSuppNew = Cudd_bddAnd( dd, bVarCur, pLR->S ); Cudd_Ref(bSuppNew); + if ( pLR->Type == DSD_NODE_EXOR ) // complemented or not - does not matter! + { // add to the components + pThis = Dsd_TreeNodeCreate( DSD_NODE_EXOR, pLR->nDecs+1, s_nDecBlocks++ ); + dsdKernelCopyListPlusOne( pThis, pVarCurDE, pLR->pDecs, pLR->nDecs ); + if ( pL != pLR ) + pThis = Dsd_Not( pThis ); + } + else // all other cases + { // create a new 2-input EXOR-gate + pThis = Dsd_TreeNodeCreate( DSD_NODE_EXOR, 2, s_nDecBlocks++ ); + if ( pL != pLR ) // complemented + { + dsdKernelCopyListPlusOne( pThis, pVarCurDE, &pLR, 1 ); + pThis = Dsd_Not( pThis ); + } + else // non-complemented + dsdKernelCopyListPlusOne( pThis, pVarCurDE, &pL, 1 ); + } + goto EXIT; + } + + ////////////////////////////////////////////////////////////////////// + // solve subproblems + ////////////////////////////////////////////////////////////////////// + pL = dsdKernelDecompose_rec( pDsdMan, bLow ); + pH = dsdKernelDecompose_rec( pDsdMan, bHigh ); + pLR = Dsd_Regular( pL ); + pHR = Dsd_Regular( pH ); + + assert( pLR->Type == DSD_NODE_BUF || pLR->Type == DSD_NODE_OR || pLR->Type == DSD_NODE_EXOR || pLR->Type == DSD_NODE_PRIME ); + assert( pHR->Type == DSD_NODE_BUF || pHR->Type == DSD_NODE_OR || pHR->Type == DSD_NODE_EXOR || pHR->Type == DSD_NODE_PRIME ); + +/* +if ( Depth == 1 ) +{ +// PRK(bLow,pDecTreeTotal->nInputs); +// PRK(bHigh,pDecTreeTotal->nInputs); +if ( s_Show ) +{ + PRD( pL ); + PRD( pH ); +} +} +*/ + // compute the new support + bTemp = Cudd_bddAnd( dd, pLR->S, pHR->S ); Cudd_Ref( bTemp ); + nSuppL = Extra_bddSuppSize( dd, pLR->S ); + nSuppH = Extra_bddSuppSize( dd, pHR->S ); + nSuppLH = Extra_bddSuppSize( dd, bTemp ); + bSuppNew = Cudd_bddAnd( dd, bTemp, bVarCur ); Cudd_Ref( bSuppNew ); + Cudd_RecursiveDeref( dd, bTemp ); + + + // several possibilities are possible + // (1) support of one component contains another + // (2) none of the supports is contained in another + fContained = dsdKernelCheckContainment( pDsdMan, pLR, pHR, &pLargeR, &pSmallR ); + + ////////////////////////////////////////////////////////////////////// + // CASE 3.b One of the cofactors in a constant (OR and EXOR) + ////////////////////////////////////////////////////////////////////// + // the support of the larger component should contain the support of the smaller + // it is possible to have PRIME function in this role + // for example: F = ITE( a+b, c(+)d, e+f ), F0 = ITE( b, c(+)d, e+f ), F1 = c(+)d + if ( fContained ) + { + Dsd_Node_t * pSmall, * pLarge; + int c, iCompLarge; // the number of the component is Large is equal to the whole of Small + int fLowIsLarge; + + DdNode * bFTemp; // the changed input function + Dsd_Node_t * pDETemp, * pDENew; + + Dsd_Node_t * pComp = NULL; + int nComp; + + if ( pSmallR == pLR ) + { // Low is Small => High is Large + pSmall = pL; + pLarge = pH; + fLowIsLarge = 0; + } + else + { // vice versa + pSmall = pH; + pLarge = pL; + fLowIsLarge = 1; + } + + // treat the situation when the larger is PRIME + if ( pLargeR->Type == DSD_NODE_PRIME ) //&& pLargeR->nDecs != pSmallR->nDecs ) + { + // QUESTION: Is it possible for pLargeR->nDecs > 3 + // and pSmall contained as one of input in pLarge? + // Yes, for example F = a'c + a & MUX(b,c',d) = a'c + abc' + ab'd is non-decomposable + // Consider the function H(a->xy) = F( xy, b, c, d ) + // H0 = H(x=0) = F(0,b,c,d) = c + // H1 = F(x=1) = F(y,b,c,d) - non-decomposable + // + // QUESTION: Is it possible that pLarge is PRIME(3) and pSmall is OR(2), + // which is not contained in PRIME as one input? + // Yes, for example F = abcd + b'c'd' + a'c'd' = PRIME(ab, c, d) + // F(a=0) = c'd' = NOT(OR(a,d)) F(a=1) = bcd + b'c'd' = PRIME(b,c,d) + // To find decomposition, we have to prove that F(a=1)|b=0 = F(a=0) + + // Is it possible that (pLargeR->nDecs == pSmallR->nDecs) and yet this case holds? + // Yes, consider the function such that F(a=0) = PRIME(a,b+c,d,e) and F(a=1) = OR(b,c,d,e) + // They have the same number of inputs and it is possible that they will be the cofactors + // as discribed in the previous example. + + // find the component, which when substituted for 0 or 1, produces the desired result + int g, fFoundComp; // {0,1} depending on whether setting cofactor to 0 or 1 worked out + + DdNode * bLarge, * bSmall; + if ( fLowIsLarge ) + { + bLarge = bLow; + bSmall = bHigh; + } + else + { + bLarge = bHigh; + bSmall = bLow; + } + + for ( g = 0; g < pLargeR->nDecs; g++ ) +// if ( g != c ) + { + pDETemp = pLargeR->pDecs[g]; // cannot be complemented + if ( Dsd_CheckRootFunctionIdentity( dd, bLarge, bSmall, pDETemp->G, b1 ) ) + { + fFoundComp = 1; + break; + } + + s_Loops1++; + + if ( Dsd_CheckRootFunctionIdentity( dd, bLarge, bSmall, Cudd_Not(pDETemp->G), b1 ) ) + { + fFoundComp = 0; + break; + } + + s_Loops1++; + } + + if ( g != pLargeR->nDecs ) + { // decomposition is found + if ( fFoundComp ) + if ( fLowIsLarge ) + bFTemp = Cudd_bddOr( dd, bVarCur, pLargeR->pDecs[g]->G ); + else + bFTemp = Cudd_bddOr( dd, Cudd_Not(bVarCur), pLargeR->pDecs[g]->G ); + else + if ( fLowIsLarge ) + bFTemp = Cudd_bddAnd( dd, Cudd_Not(bVarCur), pLargeR->pDecs[g]->G ); + else + bFTemp = Cudd_bddAnd( dd, bVarCur, pLargeR->pDecs[g]->G ); + Cudd_Ref( bFTemp ); + + pDENew = dsdKernelDecompose_rec( pDsdMan, bFTemp ); + pDENew = Dsd_Regular( pDENew ); + Cudd_RecursiveDeref( dd, bFTemp ); + + // get the new gate + pThis = Dsd_TreeNodeCreate( DSD_NODE_PRIME, pLargeR->nDecs, s_nDecBlocks++ ); + dsdKernelCopyListPlusOneMinusOne( pThis, pDENew, pLargeR->pDecs, pLargeR->nDecs, g ); + goto EXIT; + } + } + + // try to find one component in the pLarger that is equal to the whole of pSmaller + for ( c = 0; c < pLargeR->nDecs; c++ ) + if ( pLargeR->pDecs[c] == pSmall || pLargeR->pDecs[c] == Dsd_Not(pSmall) ) + { + iCompLarge = c; + break; + } + + // assign the equal component + if ( c != pLargeR->nDecs ) // the decomposition is possible! + { + pComp = pLargeR->pDecs[iCompLarge]; + nComp = 1; + } + else // the decomposition is still possible + { // for example F = OR(ab,c,d), F(a=0) = OR(c,d), F(a=1) = OR(b,c,d) + // supp(F0) is contained in supp(F1), Polarity(F(a=0)) == Polarity(F(a=1)) + + // try to find a group of common components + if ( pLargeR->Type == pSmallR->Type && + (pLargeR->Type == DSD_NODE_EXOR || pSmallR->Type == DSD_NODE_OR&& ((pLarge==pLargeR) == (pSmall==pSmallR))) ) + { + Dsd_Node_t ** pCommon, * pLastDiffL = NULL, * pLastDiffH = NULL; + int nCommon = dsdKernelFindCommonComponents( pDsdMan, pLargeR, pSmallR, &pCommon, &pLastDiffL, &pLastDiffH ); + // if all the components of pSmall are contained in pLarge, + // then the decomposition exists + if ( nCommon == pSmallR->nDecs ) + { + pComp = pSmallR; + nComp = pSmallR->nDecs; + } + } + } + + if ( pComp ) // the decomposition is possible! + { +// Dsd_Node_t * pComp = pLargeR->pDecs[iCompLarge]; + Dsd_Node_t * pCompR = Dsd_Regular( pComp ); + int fComp1 = (int)( pLarge != pLargeR ); + int fComp2 = (int)( pComp != pCompR ); + int fComp3 = (int)( pSmall != pSmallR ); + + DdNode * bFuncComp; // the function of the given component + DdNode * bFuncNew; // the function of the input component + + if ( pLargeR->Type == DSD_NODE_OR ) // Figure 4 of Matsunaga's paper + { + // the decomposition exists only if the polarity assignment + // along the paths is the same + if ( (fComp1 ^ fComp2) == fComp3 ) + { // decomposition exists = consider 4 cases + // consideration of cases leads to the following conclusion + // fComp1 gives the polarity of the resulting DSD_NODE_OR gate + // fComp2 gives the polarity of the common component feeding into the DSD_NODE_OR gate + // + // | fComp1 pL/ |pS + // <> .........<=>....... <> | + // | / | + // [OR] [OR] | fComp3 + // / \ fComp2 / | \ | + // <> <> .......<=>... /..|..<> | + // / \ / | \| + // [OR] [C] S1 S2 C + // / \ + // <> \ + // / \ + // [OR] [x] + // / \ + // S1 S2 + // + + + // at this point we have the function F (bFTemp) and the common component C (bFuncComp) + // to get the remainder, R, in the relationship F = R + C, supp(R) & supp(C) = 0 + // we compute the following R = Exist( F - C, supp(C) ) + bFTemp = (fComp1)? Cudd_Not( bF ): bF; + bFuncComp = (fComp2)? Cudd_Not( pCompR->G ): pCompR->G; + bFuncNew = Cudd_bddAndAbstract( dd, bFTemp, Cudd_Not(bFuncComp), pCompR->S ); Cudd_Ref( bFuncNew ); + + // there is no need to copy the dec entry list first, because pComp is a component + // which will not be destroyed by the recursive call to decomposition + pDENew = dsdKernelDecompose_rec( pDsdMan, bFuncNew ); + assert( Dsd_IsComplement(pDENew) ); // follows from the consideration of cases + Cudd_RecursiveDeref( dd, bFuncNew ); + + // get the new gate + if ( nComp == 1 ) + { + pThis = Dsd_TreeNodeCreate( DSD_NODE_OR, 2, s_nDecBlocks++ ); + pThis->pDecs[0] = pDENew; + pThis->pDecs[1] = pComp; // takes the complement + } + else + { // pComp is not complemented + pThis = Dsd_TreeNodeCreate( DSD_NODE_OR, nComp+1, s_nDecBlocks++ ); + dsdKernelCopyListPlusOne( pThis, pDENew, pComp->pDecs, nComp ); + } + + if ( fComp1 ) + pThis = Dsd_Not( pThis ); + goto EXIT; + } + } + else if ( pLargeR->Type == DSD_NODE_EXOR ) // Figure 5 of Matsunaga's paper (with correction) + { // decomposition always exists = consider 4 cases + + // consideration of cases leads to the following conclusion + // fComp3 gives the COMPLEMENT of the polarity of the resulting EXOR gate + // (if fComp3 is 0, the EXOR gate is complemented, and vice versa) + // + // | fComp1 pL/ |pS + // <> .........<=>....... /....| fComp3 + // | / | + // [XOR] [XOR] | + // / \ fComp2==0 / | \ | + // / \ / | \ | + // / \ / | \| + // [OR] [C] S1 S2 C + // / \ + // <> \ + // / \ + // [XOR] [x] + // / \ + // S1 S2 + // + + assert( fComp2 == 0 ); + // find the functionality of the lower gates + bFTemp = (fComp3)? bF: Cudd_Not( bF ); + bFuncNew = Cudd_bddXor( dd, bFTemp, pComp->G ); Cudd_Ref( bFuncNew ); + + pDENew = dsdKernelDecompose_rec( pDsdMan, bFuncNew ); + assert( !Dsd_IsComplement(pDENew) ); // follows from the consideration of cases + Cudd_RecursiveDeref( dd, bFuncNew ); + + // get the new gate + if ( nComp == 1 ) + { + pThis = Dsd_TreeNodeCreate( DSD_NODE_EXOR, 2, s_nDecBlocks++ ); + pThis->pDecs[0] = pDENew; + pThis->pDecs[1] = pComp; + } + else + { // pComp is not complemented + pThis = Dsd_TreeNodeCreate( DSD_NODE_EXOR, nComp+1, s_nDecBlocks++ ); + dsdKernelCopyListPlusOne( pThis, pDENew, pComp->pDecs, nComp ); + } + + if ( !fComp3 ) + pThis = Dsd_Not( pThis ); + goto EXIT; + } + } + } + + // this case was added to fix the trivial bug found November 4, 2002 in Japan + // by running the example provided by T. Sasao + if ( nSuppLH == nSuppL + nSuppH ) // the supports of the components are disjoint + { + // create a new component of the type ITE( a, pH, pL ) + pThis = Dsd_TreeNodeCreate( DSD_NODE_PRIME, 3, s_nDecBlocks++ ); + if ( dd->perm[pLR->S->index] < dd->perm[pHR->S->index] ) // pLR is higher in the varible order + { + pThis->pDecs[1] = pLR; + pThis->pDecs[2] = pHR; + } + else // pHR is higher in the varible order + { + pThis->pDecs[1] = pHR; + pThis->pDecs[2] = pLR; + } + // add the first component + pThis->pDecs[0] = pVarCurDE; + goto EXIT; + } + + + ////////////////////////////////////////////////////////////////////// + // CASE 3.a Neither of the cofactors is a constant (OR, EXOR, PRIME) + ////////////////////////////////////////////////////////////////////// + // the component types are identical + // and if they are OR, they are either both complemented or both not complemented + // and if they are PRIME, their dec numbers should be the same + if ( pLR->Type == pHR->Type && + pLR->Type != DSD_NODE_BUF && + (pLR->Type != DSD_NODE_OR || ( pL == pLR && pH == pHR || pL != pLR && pH != pHR ) ) && + (pLR->Type != DSD_NODE_PRIME || pLR->nDecs == pHR->nDecs) ) + { + // array to store common comps in pL and pH + Dsd_Node_t ** pCommon, * pLastDiffL = NULL, * pLastDiffH = NULL; + int nCommon = dsdKernelFindCommonComponents( pDsdMan, pLR, pHR, &pCommon, &pLastDiffL, &pLastDiffH ); + if ( nCommon ) + { + if ( pLR->Type == DSD_NODE_OR ) // Figure 2 of Matsunaga's paper + { // at this point we have the function F and the group of common components C + // to get the remainder, R, in the relationship F = R + C, supp(R) & supp(C) = 0 + // we compute the following R = Exist( F - C, supp(C) ) + + // compute the sum total of the common components and the union of their supports + DdNode * bCommF, * bCommS, * bFTemp, * bFuncNew; + Dsd_Node_t * pDENew; + + dsdKernelComputeSumOfComponents( pDsdMan, pCommon, nCommon, &bCommF, &bCommS, 0 ); + Cudd_Ref( bCommF ); + Cudd_Ref( bCommS ); + bFTemp = ( pL != pLR )? Cudd_Not(bF): bF; + + bFuncNew = Cudd_bddAndAbstract( dd, bFTemp, Cudd_Not(bCommF), bCommS ); Cudd_Ref( bFuncNew ); + Cudd_RecursiveDeref( dd, bCommF ); + Cudd_RecursiveDeref( dd, bCommS ); + + // get the new gate + + // copy the components first, then call the decomposition + // because decomposition will distroy the list used for copying + pThis = Dsd_TreeNodeCreate( DSD_NODE_OR, nCommon + 1, s_nDecBlocks++ ); + dsdKernelCopyListPlusOne( pThis, NULL, pCommon, nCommon ); + + // call the decomposition recursively + pDENew = dsdKernelDecompose_rec( pDsdMan, bFuncNew ); +// assert( !Dsd_IsComplement(pDENew) ); // follows from the consideration of cases + Cudd_RecursiveDeref( dd, bFuncNew ); + + // add the first component + pThis->pDecs[0] = pDENew; + + if ( pL != pLR ) + pThis = Dsd_Not( pThis ); + goto EXIT; + } + else + if ( pLR->Type == DSD_NODE_EXOR ) // Figure 3 of Matsunaga's paper + { + // compute the sum total of the common components and the union of their supports + DdNode * bCommF, * bFuncNew; + Dsd_Node_t * pDENew; + int fCompExor; + + dsdKernelComputeSumOfComponents( pDsdMan, pCommon, nCommon, &bCommF, NULL, 1 ); + Cudd_Ref( bCommF ); + + bFuncNew = Cudd_bddXor( dd, bF, bCommF ); Cudd_Ref( bFuncNew ); + Cudd_RecursiveDeref( dd, bCommF ); + + // get the new gate + + // copy the components first, then call the decomposition + // because decomposition will distroy the list used for copying + pThis = Dsd_TreeNodeCreate( DSD_NODE_EXOR, nCommon + 1, s_nDecBlocks++ ); + dsdKernelCopyListPlusOne( pThis, NULL, pCommon, nCommon ); + + // call the decomposition recursively + pDENew = dsdKernelDecompose_rec( pDsdMan, bFuncNew ); + Cudd_RecursiveDeref( dd, bFuncNew ); + + // remember the fact that it was complemented + fCompExor = Dsd_IsComplement(pDENew); + pDENew = Dsd_Regular(pDENew); + + // add the first component + pThis->pDecs[0] = pDENew; + + + if ( fCompExor ) + pThis = Dsd_Not( pThis ); + goto EXIT; + } + else + if ( pLR->Type == DSD_NODE_PRIME && (nCommon == pLR->nDecs-1 || nCommon == pLR->nDecs) ) + { + // for example the function F(a,b,c,d) = ITE(b,c,a(+)d) produces + // two cofactors F(a=0) = PRIME(b,c,d) and F(a=1) = PRIME(b,c,d) + // with exactly the same list of common components + + Dsd_Node_t * pDENew; + DdNode * bFuncNew; + int fCompComp = 0; // this flag can be {0,1,2} + // if it is 0 there is no identity + // if it is 1/2, the cofactored functions are equal in the direct/complemented polarity + + if ( nCommon == pLR->nDecs ) + { // all the components are the same + // find the formal input, in which pLow and pHigh differ (if such input exists) + int m; + Dsd_Node_t * pTempL, * pTempH; + + s_Common++; + for ( m = 0; m < pLR->nDecs; m++ ) + { + pTempL = pLR->pDecs[m]; // cannot be complemented + pTempH = pHR->pDecs[m]; // cannot be complemented + + if ( Dsd_CheckRootFunctionIdentity( dd, bLow, bHigh, pTempL->G, Cudd_Not(pTempH->G) ) && + Dsd_CheckRootFunctionIdentity( dd, bLow, bHigh, Cudd_Not(pTempL->G), pTempH->G ) ) + { + pLastDiffL = pTempL; + pLastDiffH = pTempH; + assert( pLastDiffL == pLastDiffH ); + fCompComp = 2; + break; + } + + s_Loops2++; + s_Loops2++; +/* + if ( s_Loops2 % 10000 == 0 ) + { + int i; + for ( i = 0; i < pLR->nDecs; i++ ) + printf( " %d(s=%d)", pLR->pDecs[i]->Type, + Extra_bddSuppSize(dd, pLR->pDecs[i]->S) ); + printf( "\n" ); + } +*/ + + } +// if ( pLR->nDecs == Extra_bddSuppSize(dd, pLR->S) ) +// s_Loops2Useless += pLR->nDecs * 2; + + if ( fCompComp ) + { // put the equal components into pCommon, so that they could be copied into the new dec entry + nCommon = 0; + for ( m = 0; m < pLR->nDecs; m++ ) + if ( pLR->pDecs[m] != pLastDiffL ) + pCommon[nCommon++] = pLR->pDecs[m]; + assert( nCommon = pLR->nDecs-1 ); + } + } + else + { // the differing components are known - check that they have compatible PRIME function + + s_CommonNo++; + + // find the numbers of different components + assert( pLastDiffL ); + assert( pLastDiffH ); + // also, they cannot be complemented, because the decomposition type is PRIME + + if ( Dsd_CheckRootFunctionIdentity( dd, bLow, bHigh, Cudd_Not(pLastDiffL->G), Cudd_Not(pLastDiffH->G) ) && + Dsd_CheckRootFunctionIdentity( dd, bLow, bHigh, pLastDiffL->G, pLastDiffH->G ) ) + fCompComp = 1; + else if ( Dsd_CheckRootFunctionIdentity( dd, bLow, bHigh, pLastDiffL->G, Cudd_Not(pLastDiffH->G) ) && + Dsd_CheckRootFunctionIdentity( dd, bLow, bHigh, Cudd_Not(pLastDiffL->G), pLastDiffH->G ) ) + fCompComp = 2; + + s_Loops3 += 4; + } + + if ( fCompComp ) + { + if ( fCompComp == 1 ) // it is true that bLow(G=0) == bHigh(H=0) && bLow(G=1) == bHigh(H=1) + bFuncNew = Cudd_bddIte( dd, bVarCur, pLastDiffH->G, pLastDiffL->G ); + else // it is true that bLow(G=0) == bHigh(H=1) && bLow(G=1) == bHigh(H=0) + bFuncNew = Cudd_bddIte( dd, bVarCur, Cudd_Not(pLastDiffH->G), pLastDiffL->G ); + Cudd_Ref( bFuncNew ); + + // get the new gate + + // copy the components first, then call the decomposition + // because decomposition will distroy the list used for copying + pThis = Dsd_TreeNodeCreate( DSD_NODE_PRIME, pLR->nDecs, s_nDecBlocks++ ); + dsdKernelCopyListPlusOne( pThis, NULL, pCommon, nCommon ); + + // create a new component + pDENew = dsdKernelDecompose_rec( pDsdMan, bFuncNew ); + Cudd_RecursiveDeref( dd, bFuncNew ); + // the BDD of the argument function in PRIME decomposition, should be regular + pDENew = Dsd_Regular(pDENew); + + // add the first component + pThis->pDecs[0] = pDENew; + goto EXIT; + } + } // end of PRIME type + } // end of existing common components + } // end of CASE 3.a + +// if ( Depth != 1) +// { + +//CASE4: + ////////////////////////////////////////////////////////////////////// + // CASE 4 + ////////////////////////////////////////////////////////////////////// + { + // estimate the number of entries in the list + int nEntriesMax = pDsdMan->nInputs - dd->perm[VarInt]; + + // create the new decomposition entry + int nEntries = 0; + + DdNode * SuppL, * SuppH, * SuppL_init, * SuppH_init; + Dsd_Node_t *pHigher, *pLower, * pTemp, * pDENew; + + + int levTopSuppL; + int levTopSuppH; + int levTop; + + pThis = Dsd_TreeNodeCreate( DSD_NODE_PRIME, nEntriesMax, s_nDecBlocks++ ); + pThis->pDecs[ nEntries++ ] = pVarCurDE; + // other entries will be added to this list one-by-one during analysis + + // count how many times does it happen that the decomposition entries are + s_Case4Calls++; + + // consider the simplest case: when the supports are equal + // and at least one of the components + // is the PRIME without decompositions, or + // when both of them are without decomposition + if ( (((pLR->Type == DSD_NODE_PRIME && nSuppL == pLR->nDecs) || (pHR->Type == DSD_NODE_PRIME && nSuppH == pHR->nDecs)) && pLR->S == pHR->S) || + ((pLR->Type == DSD_NODE_PRIME && nSuppL == pLR->nDecs) && (pHR->Type == DSD_NODE_PRIME && nSuppH == pHR->nDecs)) ) + { + + s_Case4CallsSpecial++; + // walk through both supports and create the decomposition list composed of simple entries + SuppL = pLR->S; + SuppH = pHR->S; + do + { + // determine levels + levTopSuppL = cuddI(dd,SuppL->index); + levTopSuppH = cuddI(dd,SuppH->index); + + // skip the topmost variable in both supports + if ( levTopSuppL <= levTopSuppH ) + { + levTop = levTopSuppL; + SuppL = cuddT(SuppL); + } + else + levTop = levTopSuppH; + + if ( levTopSuppH <= levTopSuppL ) + SuppH = cuddT(SuppH); + + // set the new decomposition entry + pThis->pDecs[ nEntries++ ] = pDsdMan->pInputs[ dd->invperm[levTop] ]; + } + while ( SuppL != b1 || SuppH != b1 ); + } + else + { + + // compare two different decomposition lists + SuppL_init = pLR->S; + SuppH_init = pHR->S; + // start references (because these supports will change) + SuppL = pLR->S; Cudd_Ref( SuppL ); + SuppH = pHR->S; Cudd_Ref( SuppH ); + while ( SuppL != b1 || SuppH != b1 ) + { + // determine the top level in cofactors and + // whether they have the same top level + int TopLevL = cuddI(dd,SuppL->index); + int TopLevH = cuddI(dd,SuppH->index); + int TopLevel = TopLevH; + int fEqualLevel = 0; + + DdNode * bVarTop; + DdNode * bSuppSubract; + + + if ( TopLevL < TopLevH ) + { + pHigher = pLR; + pLower = pHR; + TopLevel = TopLevL; + } + else if ( TopLevL > TopLevH ) + { + pHigher = pHR; + pLower = pLR; + } + else + fEqualLevel = 1; + assert( TopLevel != CUDD_CONST_INDEX ); + + + // find the currently top variable in the decomposition lists + bVarTop = dd->vars[dd->invperm[TopLevel]]; + + if ( !fEqualLevel ) + { + // find the lower support + DdNode * bSuppLower = (TopLevL < TopLevH)? SuppH_init: SuppL_init; + + // find the first component in pHigher + // whose support does not overlap with supp(Lower) + // and remember the previous component + int fPolarity; + Dsd_Node_t * pPrev = NULL; // the pointer to the component proceeding pCur + Dsd_Node_t * pCur = pHigher; // the first component not contained in supp(Lower) + while ( Extra_bddSuppOverlapping( dd, pCur->S, bSuppLower ) ) + { // get the next component + pPrev = pCur; + pCur = dsdKernelFindContainingComponent( pDsdMan, pCur, bVarTop, &fPolarity ); + }; + + // look for the possibility to subtract more than one component + if ( pPrev == NULL || pPrev->Type == DSD_NODE_PRIME ) + { // if there is no previous component, or if the previous component is PRIME + // there is no way to subtract more than one component + + // add the new decomposition entry (it is already regular) + pThis->pDecs[ nEntries++ ] = pCur; + // assign the support to be subtracted from both components + bSuppSubract = pCur->S; + } + else // all other types + { + // go through the decomposition list of pPrev and find components + // whose support does not overlap with supp(Lower) + + static Dsd_Node_t * pNonOverlap[MAXINPUTS]; + int i, nNonOverlap = 0; + for ( i = 0; i < pPrev->nDecs; i++ ) + { + pTemp = Dsd_Regular( pPrev->pDecs[i] ); + if ( !Extra_bddSuppOverlapping( dd, pTemp->S, bSuppLower ) ) + pNonOverlap[ nNonOverlap++ ] = pPrev->pDecs[i]; + } + assert( nNonOverlap > 0 ); + + if ( nNonOverlap == 1 ) + { // one one component was found, which is the original one + assert( Dsd_Regular(pNonOverlap[0]) == pCur); + // add the new decomposition entry + pThis->pDecs[ nEntries++ ] = pCur; + // assign the support to be subtracted from both components + bSuppSubract = pCur->S; + } + else // more than one components was found + { + // find the OR (EXOR) of the non-overlapping components + DdNode * bCommF; + dsdKernelComputeSumOfComponents( pDsdMan, pNonOverlap, nNonOverlap, &bCommF, NULL, (int)(pPrev->Type==DSD_NODE_EXOR) ); + Cudd_Ref( bCommF ); + + // create a new gated + pDENew = dsdKernelDecompose_rec( pDsdMan, bCommF ); + Cudd_RecursiveDeref(dd, bCommF); + // make it regular... it must be regular already + assert( !Dsd_IsComplement(pDENew) ); + + // add the new decomposition entry + pThis->pDecs[ nEntries++ ] = pDENew; + // assign the support to be subtracted from both components + bSuppSubract = pDENew->S; + } + } + + // subtract its support from the support of upper component + if ( TopLevL < TopLevH ) + { + SuppL = Cudd_bddExistAbstract( dd, bTemp = SuppL, bSuppSubract ); Cudd_Ref( SuppL ); + Cudd_RecursiveDeref(dd, bTemp); + } + else + { + SuppH = Cudd_bddExistAbstract( dd, bTemp = SuppH, bSuppSubract ); Cudd_Ref( SuppH ); + Cudd_RecursiveDeref(dd, bTemp); + } + } // end of if ( !fEqualLevel ) + else // if ( fEqualLevel ) -- they have the same top level var + { + static Dsd_Node_t * pMarkedLeft[MAXINPUTS]; // the pointers to the marked blocks + static char pMarkedPols[MAXINPUTS]; // polarities of the marked blocks + int nMarkedLeft = 0; + + int fPolarity = 0; + Dsd_Node_t * pTempL = pLR; + + int fPolarityCurH = 0; + Dsd_Node_t * pPrevH = NULL, * pCurH = pHR; + + int fPolarityCurL = 0; + Dsd_Node_t * pPrevL = NULL, * pCurL = pLR; // = pMarkedLeft[0]; + int index = 1; + + // set the new mark + s_Mark++; + + // go over the dec list of pL, mark all components that contain the given variable + assert( Extra_bddSuppContainVar( dd, pLR->S, bVarTop ) ); + assert( Extra_bddSuppContainVar( dd, pHR->S, bVarTop ) ); + do { + pTempL->Mark = s_Mark; + pMarkedLeft[ nMarkedLeft ] = pTempL; + pMarkedPols[ nMarkedLeft ] = fPolarity; + nMarkedLeft++; + } while ( pTempL = dsdKernelFindContainingComponent( pDsdMan, pTempL, bVarTop, &fPolarity ) ); + + // go over the dec list of pH, and find the component that is marked and the previos one + // (such component always exists, because they have common variables) + while ( pCurH->Mark != s_Mark ) + { + pPrevH = pCurH; + pCurH = dsdKernelFindContainingComponent( pDsdMan, pCurH, bVarTop, &fPolarityCurH ); + assert( pCurH ); + } + + // go through the first list once again and find + // the component proceeding the one marked found in the second list + while ( pCurL != pCurH ) + { + pPrevL = pCurL; + pCurL = pMarkedLeft[index]; + fPolarityCurL = pMarkedPols[index]; + index++; + } + + // look for the possibility to subtract more than one component + if ( !pPrevL || !pPrevH || pPrevL->Type != pPrevH->Type || pPrevL->Type == DSD_NODE_PRIME || fPolarityCurL != fPolarityCurH ) + { // there is no way to extract more than one + pThis->pDecs[ nEntries++ ] = pCurH; + // assign the support to be subtracted from both components + bSuppSubract = pCurH->S; + } + else + { + // find the equal components in two decomposition lists + Dsd_Node_t ** pCommon, * pLastDiffL = NULL, * pLastDiffH = NULL; + int nCommon = dsdKernelFindCommonComponents( pDsdMan, pPrevL, pPrevH, &pCommon, &pLastDiffL, &pLastDiffH ); + + if ( nCommon == 0 || nCommon == 1 ) + { // one one component was found, which is the original one + // assert( Dsd_Regular(pCommon[0]) == pCurL); + // add the new decomposition entry + pThis->pDecs[ nEntries++ ] = pCurL; + // assign the support to be subtracted from both components + bSuppSubract = pCurL->S; + } + else // more than one components was found + { + // find the OR (EXOR) of the non-overlapping components + DdNode * bCommF; + dsdKernelComputeSumOfComponents( pDsdMan, pCommon, nCommon, &bCommF, NULL, (int)(pPrevL->Type==DSD_NODE_EXOR) ); + Cudd_Ref( bCommF ); + + pDENew = dsdKernelDecompose_rec( pDsdMan, bCommF ); + assert( !Dsd_IsComplement(pDENew) ); // cannot be complemented because of construction + Cudd_RecursiveDeref( dd, bCommF ); + + // add the new decomposition entry + pThis->pDecs[ nEntries++ ] = pDENew; + + // assign the support to be subtracted from both components + bSuppSubract = pDENew->S; + } + } + + SuppL = Cudd_bddExistAbstract( dd, bTemp = SuppL, bSuppSubract ), Cudd_Ref( SuppL ); + Cudd_RecursiveDeref(dd, bTemp); + + SuppH = Cudd_bddExistAbstract( dd, bTemp = SuppH, bSuppSubract ), Cudd_Ref( SuppH ); + Cudd_RecursiveDeref(dd, bTemp); + + } // end of if ( fEqualLevel ) + + } // end of decomposition list comparison + Cudd_RecursiveDeref( dd, SuppL ); + Cudd_RecursiveDeref( dd, SuppH ); + + } + + // check that the estimation of the number of entries was okay + assert( nEntries <= nEntriesMax ); + +// if ( nEntries != Extra_bddSuppSize(dd, bSuppNew) ) +// s_Case5++; + + // update the number of entries in the new decomposition list + pThis->nDecs = nEntries; + } +//} +EXIT: + + { + // if the component created is complemented, it represents a function without complement + // therefore, as it is, without complement, it should recieve the complemented function + Dsd_Node_t * pThisR = Dsd_Regular( pThis ); + assert( pThisR->G == NULL ); + assert( pThisR->S == NULL ); + + if ( pThisR == pThis ) // set regular function + pThisR->G = bF; + else // set complemented function + pThisR->G = Cudd_Not(bF); + Cudd_Ref(bF); // reference the function in the component + + assert( bSuppNew ); + pThisR->S = bSuppNew; // takes the reference from the new support + if ( st_insert( pDsdMan->Table, (char*)bF, (char*)pThis ) ) + { + assert( 0 ); + } + s_CacheEntries++; + + +/* + if ( dsdKernelVerifyDecomposition(dd, pThis) == 0 ) + { + // write the function, for which verification does not work + cout << endl << "Internal verification failed!"" ); + + // create the variable mask + static int s_pVarMask[MAXINPUTS]; + int nInputCounter = 0; + + Cudd_SupportArray( dd, bF, s_pVarMask ); + int k; + for ( k = 0; k < dd->size; k++ ) + if ( s_pVarMask[k] ) + nInputCounter++; + + cout << endl << "The problem function is "" ); + + DdNode * zNewFunc = Cudd_zddIsopCover( dd, bF, bF ); Cudd_Ref( zNewFunc ); + cuddWriteFunctionSop( stdout, dd, zNewFunc, -1, dd->size, "1", s_pVarMask ); + Cudd_RecursiveDerefZdd( dd, zNewFunc ); + } +*/ + + } + + Depth--; + return Dsd_NotCond( pThis, fCompF ); +} + + +//////////////////////////////////////////////////////////////////////// +/// OTHER FUNCTIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Finds the corresponding decomposition entry.] + + Description [This function returns the non-complemented pointer to the + DecEntry of that component which contains the given variable in its + support, or NULL if no such component exists] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dsd_Node_t * dsdKernelFindContainingComponent( Dsd_Manager_t * pDsdMan, Dsd_Node_t * pWhere, DdNode * Var, int * fPolarity ) + +{ + Dsd_Node_t * pTemp; + int i; + +// assert( !Dsd_IsComplement( pWhere ) ); +// assert( Extra_bddSuppContainVar( pDsdMan->dd, pWhere->S, Var ) ); + + if ( pWhere->nDecs == 1 ) + return NULL; + + for( i = 0; i < pWhere->nDecs; i++ ) + { + pTemp = Dsd_Regular( pWhere->pDecs[i] ); + if ( Extra_bddSuppContainVar( pDsdMan->dd, pTemp->S, Var ) ) + { + *fPolarity = (int)( pTemp != pWhere->pDecs[i] ); + return pTemp; + } + } + assert( 0 ); + return NULL; +} + +/**Function************************************************************* + + Synopsis [Find the common decomposition components.] + + Description [This function determines the common components. It counts + the number of common components in the decomposition lists of pL and pH + and returns their number and the lists of common components. It assumes + that pL and pH are regular pointers. It retuns also the pointers to the + last different components encountered in pL and pH.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int dsdKernelFindCommonComponents( Dsd_Manager_t * pDsdMan, Dsd_Node_t * pL, Dsd_Node_t * pH, Dsd_Node_t *** pCommon, Dsd_Node_t ** pLastDiffL, Dsd_Node_t ** pLastDiffH ) +{ + static Dsd_Node_t * Common[MAXINPUTS]; + int nCommon = 0; + + // pointers to the current decomposition entries + Dsd_Node_t * pLcur; + Dsd_Node_t * pHcur; + + // the pointers to their supports + DdNode * bSLcur; + DdNode * bSHcur; + + // the top variable in the supports + int TopVar; + + // the indices running through the components + int iCurL = 0; + int iCurH = 0; + while ( iCurL < pL->nDecs && iCurH < pH->nDecs ) + { // both did not run out + + pLcur = Dsd_Regular(pL->pDecs[iCurL]); + pHcur = Dsd_Regular(pH->pDecs[iCurH]); + + bSLcur = pLcur->S; + bSHcur = pHcur->S; + + // find out what component is higher in the BDD + if ( pDsdMan->dd->perm[bSLcur->index] < pDsdMan->dd->perm[bSHcur->index] ) + TopVar = bSLcur->index; + else + TopVar = bSHcur->index; + + if ( TopVar == bSLcur->index && TopVar == bSHcur->index ) + { + // the components may be equal - should match exactly! + if ( pL->pDecs[iCurL] == pH->pDecs[iCurH] ) + Common[nCommon++] = pL->pDecs[iCurL]; + else + { + *pLastDiffL = pL->pDecs[iCurL]; + *pLastDiffH = pH->pDecs[iCurH]; + } + + // skip both + iCurL++; + iCurH++; + } + else if ( TopVar == bSLcur->index ) + { // the components cannot be equal + // skip the top-most one + *pLastDiffL = pL->pDecs[iCurL++]; + } + else // if ( TopVar == bSHcur->index ) + { // the components cannot be equal + // skip the top-most one + *pLastDiffH = pH->pDecs[iCurH++]; + } + } + + // if one of the lists still has components, write the first one down + if ( iCurL < pL->nDecs ) + *pLastDiffL = pL->pDecs[iCurL]; + + if ( iCurH < pH->nDecs ) + *pLastDiffH = pH->pDecs[iCurH]; + + // return the pointer to the array + *pCommon = Common; + // return the number of common components + return nCommon; +} + +/**Function************************************************************* + + Synopsis [Computes the sum (OR or EXOR) of the functions of the components.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void dsdKernelComputeSumOfComponents( Dsd_Manager_t * pDsdMan, Dsd_Node_t ** pCommon, int nCommon, DdNode ** pCompF, DdNode ** pCompS, int fExor ) +{ + DdManager * dd = pDsdMan->dd; + DdNode * bF, * bS, * bFadd, * bTemp; + Dsd_Node_t * pDE, * pDER; + int i; + + // start the function + bF = b0; Cudd_Ref( bF ); + // start the support + if ( pCompS ) + bS = b1, Cudd_Ref( bS ); + + assert( nCommon > 0 ); + for ( i = 0; i < nCommon; i++ ) + { + pDE = pCommon[i]; + pDER = Dsd_Regular( pDE ); + bFadd = (pDE != pDER)? Cudd_Not(pDER->G): pDER->G; + // add to the function + if ( fExor ) + bF = Cudd_bddXor( dd, bTemp = bF, bFadd ); + else + bF = Cudd_bddOr( dd, bTemp = bF, bFadd ); + Cudd_Ref( bF ); + Cudd_RecursiveDeref( dd, bTemp ); + if ( pCompS ) + { + // add to the support + bS = Cudd_bddAnd( dd, bTemp = bS, pDER->S ); Cudd_Ref( bS ); + Cudd_RecursiveDeref( dd, bTemp ); + } + } + // return the function + Cudd_Deref( bF ); + *pCompF = bF; + + // return the support + if ( pCompS ) + Cudd_Deref( bS ), *pCompS = bS; +} + +/**Function************************************************************* + + Synopsis [Checks support containment of the decomposition components.] + + Description [This function returns 1 if support of one component is contained + in that of another. In this case, pLarge (pSmall) is assigned to point to the + larger (smaller) support. If the supports are identical return 0, and does not + assign the components.] +] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int dsdKernelCheckContainment( Dsd_Manager_t * pDsdMan, Dsd_Node_t * pL, Dsd_Node_t * pH, Dsd_Node_t ** pLarge, Dsd_Node_t ** pSmall ) +{ + DdManager * dd = pDsdMan->dd; + DdNode * bSuppLarge, * bSuppSmall; + int RetValue; + + RetValue = Extra_bddSuppCheckContainment( dd, pL->S, pH->S, &bSuppLarge, &bSuppSmall ); + + if ( RetValue == 0 ) + return 0; + + if ( pH->S == bSuppLarge ) + { + *pLarge = pH; + *pSmall = pL; + } + else // if ( pL->S == bSuppLarge ) + { + *pLarge = pL; + *pSmall = pH; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Copies the list of components plus one.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void dsdKernelCopyListPlusOne( Dsd_Node_t * p, Dsd_Node_t * First, Dsd_Node_t ** ppList, int nListSize ) +{ + int i; + assert( nListSize+1 == p->nDecs ); + p->pDecs[0] = First; + for( i = 0; i < nListSize; i++ ) + p->pDecs[i+1] = ppList[i]; +} + +/**Function************************************************************* + + Synopsis [Copies the list of components plus one, and skips one.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void dsdKernelCopyListPlusOneMinusOne( Dsd_Node_t * p, Dsd_Node_t * First, Dsd_Node_t ** ppList, int nListSize, int iSkipped ) +{ + int i, Counter; + assert( nListSize == p->nDecs ); + p->pDecs[0] = First; + for( i = 0, Counter = 1; i < nListSize; i++ ) + if ( i != iSkipped ) + p->pDecs[Counter++] = ppList[i]; +} + +/**Function************************************************************* + + Synopsis [Debugging procedure to compute the functionality of the decomposed structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int dsdKernelVerifyDecomposition( Dsd_Manager_t * pDsdMan, Dsd_Node_t * pDE ) +{ + DdManager * dd = pDsdMan->dd; + Dsd_Node_t * pR = Dsd_Regular(pDE); + int fCompP = (int)( pDE != pR ); + int RetValue; + + DdNode * bRes; + if ( pR->Type == DSD_NODE_CONST1 ) + bRes = b1; + else if ( pR->Type == DSD_NODE_BUF ) + bRes = pR->G; + else if ( pR->Type == DSD_NODE_OR || pR->Type == DSD_NODE_EXOR ) + dsdKernelComputeSumOfComponents( pDsdMan, pR->pDecs, pR->nDecs, &bRes, NULL, (int)(pR->Type == DSD_NODE_EXOR) ); + else if ( pR->Type == DSD_NODE_PRIME ) + { + int i; + static DdNode * bGVars[MAXINPUTS]; + // transform the function of this block, so that it depended on inputs + // corresponding to the formal inputs + DdNode * bNewFunc = Dsd_TreeGetPrimeFunctionOld( dd, pR, 1 ); Cudd_Ref( bNewFunc ); + + // compose this function with the inputs + // create the elementary permutation + for ( i = 0; i < dd->size; i++ ) + bGVars[i] = dd->vars[i]; + + // assign functions to be composed + for ( i = 0; i < pR->nDecs; i++ ) + bGVars[dd->invperm[i]] = pR->pDecs[i]->G; + + // perform the composition + bRes = Cudd_bddVectorCompose( dd, bNewFunc, bGVars ); Cudd_Ref( bRes ); + Cudd_RecursiveDeref( dd, bNewFunc ); + + ///////////////////////////////////////////////////////// + RetValue = (int)( bRes == pR->G );//|| bRes == Cudd_Not(pR->G) ); + ///////////////////////////////////////////////////////// + Cudd_Deref( bRes ); + } + else + { + assert(0); + } + + Cudd_Ref( bRes ); + RetValue = (int)( bRes == pR->G );//|| bRes == Cudd_Not(pR->G) ); + Cudd_RecursiveDeref( dd, bRes ); + return RetValue; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/bdd/dsd/dsdTree.c b/abc_with_bb_support/src/bdd/dsd/dsdTree.c new file mode 100644 index 000000000..a1d1d25b9 --- /dev/null +++ b/abc_with_bb_support/src/bdd/dsd/dsdTree.c @@ -0,0 +1,1068 @@ +/**CFile**************************************************************** + + FileName [dsdTree.c] + + PackageName [DSD: Disjoint-support decomposition package.] + + Synopsis [Managing the decomposition tree.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 8.0. Started - September 22, 2003.] + + Revision [$Id: dsdTree.c,v 1.0 2002/22/09 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "dsdInt.h" + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Dsd_TreeUnmark_rec( Dsd_Node_t * pNode ); +static void Dsd_TreeGetInfo_rec( Dsd_Node_t * pNode, int RankCur ); +static int Dsd_TreeCountNonTerminalNodes_rec( Dsd_Node_t * pNode ); +static int Dsd_TreeCountPrimeNodes_rec( Dsd_Node_t * pNode ); +static int Dsd_TreeCollectDecomposableVars_rec( DdManager * dd, Dsd_Node_t * pNode, int * pVars, int * nVars ); +static void Dsd_TreeCollectNodesDfs_rec( Dsd_Node_t * pNode, Dsd_Node_t * ppNodes[], int * pnNodes ); +static void Dsd_TreePrint_rec( FILE * pFile, Dsd_Node_t * pNode, int fCcmp, char * pInputNames[], char * pOutputName, int nOffset, int * pSigCounter, int fShortNames ); +static void Dsd_NodePrint_rec( FILE * pFile, Dsd_Node_t * pNode, int fComp, char * pOutputName, int nOffset, int * pSigCounter ); + +//////////////////////////////////////////////////////////////////////// +/// STATIC VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +static int s_DepthMax; +static int s_GateSizeMax; + +static int s_CounterBlocks; +static int s_CounterPos; +static int s_CounterNeg; +static int s_CounterNo; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Create the DSD node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dsd_Node_t * Dsd_TreeNodeCreate( int Type, int nDecs, int BlockNum ) +{ + // allocate memory for this node + Dsd_Node_t * p = (Dsd_Node_t *) malloc( sizeof(Dsd_Node_t) ); + memset( p, 0, sizeof(Dsd_Node_t) ); + p->Type = Type; // the type of this block + p->nDecs = nDecs; // the number of decompositions + if ( p->nDecs ) + { + p->pDecs = (Dsd_Node_t **) malloc( p->nDecs * sizeof(Dsd_Node_t *) ); + p->pDecs[0] = NULL; + } + return p; +} + +/**Function************************************************************* + + Synopsis [Frees the DSD node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dsd_TreeNodeDelete( DdManager * dd, Dsd_Node_t * pNode ) +{ + if ( pNode->G ) Cudd_RecursiveDeref( dd, pNode->G ); + if ( pNode->S ) Cudd_RecursiveDeref( dd, pNode->S ); + FREE( pNode->pDecs ); + FREE( pNode ); +} + +/**Function************************************************************* + + Synopsis [Unmarks the decomposition tree.] + + Description [This function assumes that originally pNode->nVisits are + set to zero!] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dsd_TreeUnmark( Dsd_Manager_t * pDsdMan ) +{ + int i; + for ( i = 0; i < pDsdMan->nRoots; i++ ) + Dsd_TreeUnmark_rec( Dsd_Regular( pDsdMan->pRoots[i] ) ); +} + + +/**Function************************************************************* + + Synopsis [Recursive unmarking.] + + Description [This function should be called with a non-complemented + pointer.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dsd_TreeUnmark_rec( Dsd_Node_t * pNode ) +{ + int i; + + assert( pNode ); + assert( !Dsd_IsComplement( pNode ) ); + assert( pNode->nVisits > 0 ); + + if ( --pNode->nVisits ) // if this is not the last visit, return + return; + + // upon the last visit, go through the list of successors and call recursively + if ( pNode->Type != DSD_NODE_BUF && pNode->Type != DSD_NODE_CONST1 ) + for ( i = 0; i < pNode->nDecs; i++ ) + Dsd_TreeUnmark_rec( Dsd_Regular(pNode->pDecs[i]) ); +} + +/**Function************************************************************* + + Synopsis [Getting information about the node.] + + Description [This function computes the max depth and the max gate size + of the tree rooted at the node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dsd_TreeNodeGetInfo( Dsd_Manager_t * pDsdMan, int * DepthMax, int * GateSizeMax ) +{ + int i; + s_DepthMax = 0; + s_GateSizeMax = 0; + + for ( i = 0; i < pDsdMan->nRoots; i++ ) + Dsd_TreeGetInfo_rec( Dsd_Regular( pDsdMan->pRoots[i] ), 0 ); + + if ( DepthMax ) + *DepthMax = s_DepthMax; + if ( GateSizeMax ) + *GateSizeMax = s_GateSizeMax; +} + +/**Function************************************************************* + + Synopsis [Getting information about the node.] + + Description [This function computes the max depth and the max gate size + of the tree rooted at the node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dsd_TreeNodeGetInfoOne( Dsd_Node_t * pNode, int * DepthMax, int * GateSizeMax ) +{ + s_DepthMax = 0; + s_GateSizeMax = 0; + + Dsd_TreeGetInfo_rec( Dsd_Regular(pNode), 0 ); + + if ( DepthMax ) + *DepthMax = s_DepthMax; + if ( GateSizeMax ) + *GateSizeMax = s_GateSizeMax; +} + + +/**Function************************************************************* + + Synopsis [Performs the recursive step of Dsd_TreeNodeGetInfo().] + + Description [pNode is the node, for the tree rooted in which we are + determining info. RankCur is the current rank to assign to the node. + fSetRank is the flag saying whether the rank will be written in the + node. s_DepthMax is the maximum depths of the tree. s_GateSizeMax is + the maximum gate size.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dsd_TreeGetInfo_rec( Dsd_Node_t * pNode, int RankCur ) +{ + int i; + int GateSize; + + assert( pNode ); + assert( !Dsd_IsComplement( pNode ) ); + assert( pNode->nVisits >= 0 ); + + // we don't want the two-input gates to count for non-decomposable blocks + if ( pNode->Type == DSD_NODE_OR || + pNode->Type == DSD_NODE_EXOR ) + GateSize = 2; + else + GateSize = pNode->nDecs; + + // update the max size of the node + if ( s_GateSizeMax < GateSize ) + s_GateSizeMax = GateSize; + + if ( pNode->nDecs < 2 ) + return; + + // update the max rank + if ( s_DepthMax < RankCur+1 ) + s_DepthMax = RankCur+1; + + // call recursively + for ( i = 0; i < pNode->nDecs; i++ ) + Dsd_TreeGetInfo_rec( Dsd_Regular(pNode->pDecs[i]), RankCur+1 ); +} + +/**Function************************************************************* + + Synopsis [Counts AIG nodes needed to implement this node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dsd_TreeGetAigCost_rec( Dsd_Node_t * pNode ) +{ + int i, Counter = 0; + + assert( pNode ); + assert( !Dsd_IsComplement( pNode ) ); + assert( pNode->nVisits >= 0 ); + + if ( pNode->nDecs < 2 ) + return 0; + + // we don't want the two-input gates to count for non-decomposable blocks + if ( pNode->Type == DSD_NODE_OR ) + Counter += pNode->nDecs - 1; + else if ( pNode->Type == DSD_NODE_EXOR ) + Counter += 3*(pNode->nDecs - 1); + else if ( pNode->Type == DSD_NODE_PRIME && pNode->nDecs == 3 ) + Counter += 3; + + // call recursively + for ( i = 0; i < pNode->nDecs; i++ ) + Counter += Dsd_TreeGetAigCost_rec( Dsd_Regular(pNode->pDecs[i]) ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Counts AIG nodes needed to implement this node.] + + Description [Assumes that the only primes of the DSD tree are MUXes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dsd_TreeGetAigCost( Dsd_Node_t * pNode ) +{ + return Dsd_TreeGetAigCost_rec( Dsd_Regular(pNode) ); +} + +/**Function************************************************************* + + Synopsis [Counts non-terminal nodes of the DSD tree.] + + Description [Nonterminal nodes include all the nodes with the + support more than 1. These are OR, EXOR, and PRIME nodes. They + do not include the elementary variable nodes and the constant 1 + node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dsd_TreeCountNonTerminalNodes( Dsd_Manager_t * pDsdMan ) +{ + int Counter, i; + Counter = 0; + for ( i = 0; i < pDsdMan->nRoots; i++ ) + Counter += Dsd_TreeCountNonTerminalNodes_rec( Dsd_Regular( pDsdMan->pRoots[i] ) ); + Dsd_TreeUnmark( pDsdMan ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dsd_TreeCountNonTerminalNodesOne( Dsd_Node_t * pRoot ) +{ + int Counter = 0; + + // go through the list of successors and call recursively + Counter = Dsd_TreeCountNonTerminalNodes_rec( Dsd_Regular(pRoot) ); + + Dsd_TreeUnmark_rec( Dsd_Regular(pRoot) ); + return Counter; +} + + +/**Function************************************************************* + + Synopsis [Counts non-terminal nodes for one root.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dsd_TreeCountNonTerminalNodes_rec( Dsd_Node_t * pNode ) +{ + int i; + int Counter = 0; + + assert( pNode ); + assert( !Dsd_IsComplement( pNode ) ); + assert( pNode->nVisits >= 0 ); + + if ( pNode->nVisits++ ) // if this is not the first visit, return zero + return 0; + + if ( pNode->nDecs <= 1 ) + return 0; + + // upon the first visit, go through the list of successors and call recursively + for ( i = 0; i < pNode->nDecs; i++ ) + Counter += Dsd_TreeCountNonTerminalNodes_rec( Dsd_Regular(pNode->pDecs[i]) ); + + return Counter + 1; +} + + +/**Function************************************************************* + + Synopsis [Counts prime nodes of the DSD tree.] + + Description [Prime nodes are nodes with the support more than 2, + that is not an OR or EXOR gate.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dsd_TreeCountPrimeNodes( Dsd_Manager_t * pDsdMan ) +{ + int Counter, i; + Counter = 0; + for ( i = 0; i < pDsdMan->nRoots; i++ ) + Counter += Dsd_TreeCountPrimeNodes_rec( Dsd_Regular( pDsdMan->pRoots[i] ) ); + Dsd_TreeUnmark( pDsdMan ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Counts prime nodes for one root.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dsd_TreeCountPrimeNodesOne( Dsd_Node_t * pRoot ) +{ + int Counter = 0; + + // go through the list of successors and call recursively + Counter = Dsd_TreeCountPrimeNodes_rec( Dsd_Regular(pRoot) ); + + Dsd_TreeUnmark_rec( Dsd_Regular(pRoot) ); + return Counter; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dsd_TreeCountPrimeNodes_rec( Dsd_Node_t * pNode ) +{ + int i; + int Counter = 0; + + assert( pNode ); + assert( !Dsd_IsComplement( pNode ) ); + assert( pNode->nVisits >= 0 ); + + if ( pNode->nVisits++ ) // if this is not the first visit, return zero + return 0; + + if ( pNode->nDecs <= 1 ) + return 0; + + // upon the first visit, go through the list of successors and call recursively + for ( i = 0; i < pNode->nDecs; i++ ) + Counter += Dsd_TreeCountPrimeNodes_rec( Dsd_Regular(pNode->pDecs[i]) ); + + if ( pNode->Type == DSD_NODE_PRIME ) + Counter++; + + return Counter; +} + + +/**Function************************************************************* + + Synopsis [Collects the decomposable vars on the PI side.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dsd_TreeCollectDecomposableVars( Dsd_Manager_t * pDsdMan, int * pVars ) +{ + int nVars; + + // set the vars collected to 0 + nVars = 0; + Dsd_TreeCollectDecomposableVars_rec( pDsdMan->dd, Dsd_Regular(pDsdMan->pRoots[0]), pVars, &nVars ); + // return the number of collected vars + return nVars; +} + +/**Function************************************************************* + + Synopsis [Implements the recursive part of Dsd_TreeCollectDecomposableVars().] + + Description [Adds decomposable variables as they are found to pVars and increments + nVars. Returns 1 if a non-dec node with more than 4 inputs was encountered + in the processed subtree. Returns 0, otherwise. ] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dsd_TreeCollectDecomposableVars_rec( DdManager * dd, Dsd_Node_t * pNode, int * pVars, int * nVars ) +{ + int fSkipThisNode, i; + Dsd_Node_t * pTemp; + int fVerbose = 0; + + assert( pNode ); + assert( !Dsd_IsComplement( pNode ) ); + + if ( pNode->nDecs <= 1 ) + return 0; + + // go through the list of successors and call recursively + fSkipThisNode = 0; + for ( i = 0; i < pNode->nDecs; i++ ) + if ( Dsd_TreeCollectDecomposableVars_rec(dd, Dsd_Regular(pNode->pDecs[i]), pVars, nVars) ) + fSkipThisNode = 1; + + if ( !fSkipThisNode && (pNode->Type == DSD_NODE_OR || pNode->Type == DSD_NODE_EXOR || pNode->nDecs <= 4) ) + { +if ( fVerbose ) +printf( "Node of type <%d> (OR=6,EXOR=8,RAND=1): ", pNode->Type ); + + for ( i = 0; i < pNode->nDecs; i++ ) + { + pTemp = Dsd_Regular(pNode->pDecs[i]); + if ( pTemp->Type == DSD_NODE_BUF ) + { + if ( pVars ) + pVars[ (*nVars)++ ] = pTemp->S->index; + else + (*nVars)++; + +if ( fVerbose ) +printf( "%d ", pTemp->S->index ); + } + } +if ( fVerbose ) +printf( "\n" ); + } + else + fSkipThisNode = 1; + + + return fSkipThisNode; +} + + +/**Function************************************************************* + + Synopsis [Creates the DFS ordered array of DSD nodes in the tree.] + + Description [The collected nodes do not include the terminal nodes + and the constant 1 node. The array of nodes is returned. The number + of entries in the array is returned in the variale pnNodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dsd_Node_t ** Dsd_TreeCollectNodesDfs( Dsd_Manager_t * pDsdMan, int * pnNodes ) +{ + Dsd_Node_t ** ppNodes; + int nNodes, nNodesAlloc; + int i; + + nNodesAlloc = Dsd_TreeCountNonTerminalNodes(pDsdMan); + nNodes = 0; + ppNodes = ALLOC( Dsd_Node_t *, nNodesAlloc ); + for ( i = 0; i < pDsdMan->nRoots; i++ ) + Dsd_TreeCollectNodesDfs_rec( Dsd_Regular(pDsdMan->pRoots[i]), ppNodes, &nNodes ); + Dsd_TreeUnmark( pDsdMan ); + assert( nNodesAlloc == nNodes ); + *pnNodes = nNodes; + return ppNodes; +} + +/**Function************************************************************* + + Synopsis [Creates the DFS ordered array of DSD nodes in the tree.] + + Description [The collected nodes do not include the terminal nodes + and the constant 1 node. The array of nodes is returned. The number + of entries in the array is returned in the variale pnNodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dsd_Node_t ** Dsd_TreeCollectNodesDfsOne( Dsd_Manager_t * pDsdMan, Dsd_Node_t * pNode, int * pnNodes ) +{ + Dsd_Node_t ** ppNodes; + int nNodes, nNodesAlloc; + nNodesAlloc = Dsd_TreeCountNonTerminalNodesOne(pNode); + nNodes = 0; + ppNodes = ALLOC( Dsd_Node_t *, nNodesAlloc ); + Dsd_TreeCollectNodesDfs_rec( Dsd_Regular(pNode), ppNodes, &nNodes ); + Dsd_TreeUnmark_rec(Dsd_Regular(pNode)); + assert( nNodesAlloc == nNodes ); + *pnNodes = nNodes; + return ppNodes; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dsd_TreeCollectNodesDfs_rec( Dsd_Node_t * pNode, Dsd_Node_t * ppNodes[], int * pnNodes ) +{ + int i; + assert( pNode ); + assert( !Dsd_IsComplement(pNode) ); + assert( pNode->nVisits >= 0 ); + + if ( pNode->nVisits++ ) // if this is not the first visit, return zero + return; + if ( pNode->nDecs <= 1 ) + return; + + // upon the first visit, go through the list of successors and call recursively + for ( i = 0; i < pNode->nDecs; i++ ) + Dsd_TreeCollectNodesDfs_rec( Dsd_Regular(pNode->pDecs[i]), ppNodes, pnNodes ); + + ppNodes[ (*pnNodes)++ ] = pNode; +} + +/**Function************************************************************* + + Synopsis [Prints the decompostion tree into file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dsd_TreePrint( FILE * pFile, Dsd_Manager_t * pDsdMan, char * pInputNames[], char * pOutputNames[], int fShortNames, int Output ) +{ + Dsd_Node_t * pNode; + int SigCounter; + int i; + SigCounter = 1; + + if ( Output == -1 ) + { + for ( i = 0; i < pDsdMan->nRoots; i++ ) + { + pNode = Dsd_Regular( pDsdMan->pRoots[i] ); + Dsd_TreePrint_rec( pFile, pNode, (pNode != pDsdMan->pRoots[i]), pInputNames, pOutputNames[i], 0, &SigCounter, fShortNames ); + } + } + else + { + assert( Output >= 0 && Output < pDsdMan->nRoots ); + pNode = Dsd_Regular( pDsdMan->pRoots[Output] ); + Dsd_TreePrint_rec( pFile, pNode, (pNode != pDsdMan->pRoots[Output]), pInputNames, pOutputNames[Output], 0, &SigCounter, fShortNames ); + } +} + +/**Function************************************************************* + + Synopsis [Prints the decompostion tree into file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dsd_TreePrint_rec( FILE * pFile, Dsd_Node_t * pNode, int fComp, char * pInputNames[], char * pOutputName, int nOffset, int * pSigCounter, int fShortNames ) +{ + char Buffer[100]; + Dsd_Node_t * pInput; + int * pInputNums; + int fCompNew, i; + + assert( pNode->Type == DSD_NODE_BUF || pNode->Type == DSD_NODE_CONST1 || + pNode->Type == DSD_NODE_PRIME || pNode->Type == DSD_NODE_OR || pNode->Type == DSD_NODE_EXOR ); + + Extra_PrintSymbols( pFile, ' ', nOffset, 0 ); + if ( !fComp ) + fprintf( pFile, "%s = ", pOutputName ); + else + fprintf( pFile, "NOT(%s) = ", pOutputName ); + pInputNums = ALLOC( int, pNode->nDecs ); + if ( pNode->Type == DSD_NODE_CONST1 ) + { + fprintf( pFile, " Constant 1.\n" ); + } + else if ( pNode->Type == DSD_NODE_BUF ) + { + if ( fShortNames ) + fprintf( pFile, "%d", 'a' + pNode->S->index ); + else + fprintf( pFile, "%s", pInputNames[pNode->S->index] ); + fprintf( pFile, "\n" ); + } + else if ( pNode->Type == DSD_NODE_PRIME ) + { + // print the line + fprintf( pFile, "PRIME(" ); + for ( i = 0; i < pNode->nDecs; i++ ) + { + pInput = Dsd_Regular( pNode->pDecs[i] ); + fCompNew = (int)( pInput != pNode->pDecs[i] ); + if ( i ) + fprintf( pFile, "," ); + if ( fCompNew ) + fprintf( pFile, " NOT(" ); + else + fprintf( pFile, " " ); + if ( pInput->Type == DSD_NODE_BUF ) + { + pInputNums[i] = 0; + if ( fShortNames ) + fprintf( pFile, "%d", pInput->S->index ); + else + fprintf( pFile, "%s", pInputNames[pInput->S->index] ); + } + else + { + pInputNums[i] = (*pSigCounter)++; + fprintf( pFile, "<%d>", pInputNums[i] ); + } + if ( fCompNew ) + fprintf( pFile, ")" ); + } + fprintf( pFile, " )\n" ); + // call recursively for the following blocks + for ( i = 0; i < pNode->nDecs; i++ ) + if ( pInputNums[i] ) + { + pInput = Dsd_Regular( pNode->pDecs[i] ); + sprintf( Buffer, "<%d>", pInputNums[i] ); + Dsd_TreePrint_rec( pFile, Dsd_Regular( pNode->pDecs[i] ), 0, pInputNames, Buffer, nOffset + 6, pSigCounter, fShortNames ); + } + } + else if ( pNode->Type == DSD_NODE_OR ) + { + // print the line + fprintf( pFile, "OR(" ); + for ( i = 0; i < pNode->nDecs; i++ ) + { + pInput = Dsd_Regular( pNode->pDecs[i] ); + fCompNew = (int)( pInput != pNode->pDecs[i] ); + if ( i ) + fprintf( pFile, "," ); + if ( fCompNew ) + fprintf( pFile, " NOT(" ); + else + fprintf( pFile, " " ); + if ( pInput->Type == DSD_NODE_BUF ) + { + pInputNums[i] = 0; + if ( fShortNames ) + fprintf( pFile, "%c", 'a' + pInput->S->index ); + else + fprintf( pFile, "%s", pInputNames[pInput->S->index] ); + } + else + { + pInputNums[i] = (*pSigCounter)++; + fprintf( pFile, "<%d>", pInputNums[i] ); + } + if ( fCompNew ) + fprintf( pFile, ")" ); + } + fprintf( pFile, " )\n" ); + // call recursively for the following blocks + for ( i = 0; i < pNode->nDecs; i++ ) + if ( pInputNums[i] ) + { + pInput = Dsd_Regular( pNode->pDecs[i] ); + sprintf( Buffer, "<%d>", pInputNums[i] ); + Dsd_TreePrint_rec( pFile, Dsd_Regular( pNode->pDecs[i] ), 0, pInputNames, Buffer, nOffset + 6, pSigCounter, fShortNames ); + } + } + else if ( pNode->Type == DSD_NODE_EXOR ) + { + // print the line + fprintf( pFile, "EXOR(" ); + for ( i = 0; i < pNode->nDecs; i++ ) + { + pInput = Dsd_Regular( pNode->pDecs[i] ); + fCompNew = (int)( pInput != pNode->pDecs[i] ); + if ( i ) + fprintf( pFile, "," ); + if ( fCompNew ) + fprintf( pFile, " NOT(" ); + else + fprintf( pFile, " " ); + if ( pInput->Type == DSD_NODE_BUF ) + { + pInputNums[i] = 0; + if ( fShortNames ) + fprintf( pFile, "%c", 'a' + pInput->S->index ); + else + fprintf( pFile, "%s", pInputNames[pInput->S->index] ); + } + else + { + pInputNums[i] = (*pSigCounter)++; + fprintf( pFile, "<%d>", pInputNums[i] ); + } + if ( fCompNew ) + fprintf( pFile, ")" ); + } + fprintf( pFile, " )\n" ); + // call recursively for the following blocks + for ( i = 0; i < pNode->nDecs; i++ ) + if ( pInputNums[i] ) + { + pInput = Dsd_Regular( pNode->pDecs[i] ); + sprintf( Buffer, "<%d>", pInputNums[i] ); + Dsd_TreePrint_rec( pFile, Dsd_Regular( pNode->pDecs[i] ), 0, pInputNames, Buffer, nOffset + 6, pSigCounter, fShortNames ); + } + } + free( pInputNums ); +} + +/**Function************************************************************* + + Synopsis [Prints the decompostion tree into file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dsd_NodePrint( FILE * pFile, Dsd_Node_t * pNode ) +{ + Dsd_Node_t * pNodeR; + int SigCounter = 1; + pNodeR = Dsd_Regular(pNode); + Dsd_NodePrint_rec( pFile, pNodeR, pNodeR != pNode, "F", 0, &SigCounter ); +} + +/**Function************************************************************* + + Synopsis [Prints one node of the decomposition tree.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dsd_NodePrint_rec( FILE * pFile, Dsd_Node_t * pNode, int fComp, char * pOutputName, int nOffset, int * pSigCounter ) +{ + char Buffer[100]; + Dsd_Node_t * pInput; + int * pInputNums; + int fCompNew, i; + + assert( pNode->Type == DSD_NODE_BUF || pNode->Type == DSD_NODE_CONST1 || + pNode->Type == DSD_NODE_PRIME || pNode->Type == DSD_NODE_OR || pNode->Type == DSD_NODE_EXOR ); + + Extra_PrintSymbols( pFile, ' ', nOffset, 0 ); + if ( !fComp ) + fprintf( pFile, "%s = ", pOutputName ); + else + fprintf( pFile, "NOT(%s) = ", pOutputName ); + pInputNums = ALLOC( int, pNode->nDecs ); + if ( pNode->Type == DSD_NODE_CONST1 ) + { + fprintf( pFile, " Constant 1.\n" ); + } + else if ( pNode->Type == DSD_NODE_BUF ) + { + fprintf( pFile, " " ); + fprintf( pFile, "%c", 'a' + pNode->S->index ); + fprintf( pFile, "\n" ); + } + else if ( pNode->Type == DSD_NODE_PRIME ) + { + // print the line + fprintf( pFile, "PRIME(" ); + for ( i = 0; i < pNode->nDecs; i++ ) + { + pInput = Dsd_Regular( pNode->pDecs[i] ); + fCompNew = (int)( pInput != pNode->pDecs[i] ); + assert( fCompNew == 0 ); + if ( i ) + fprintf( pFile, "," ); + if ( pInput->Type == DSD_NODE_BUF ) + { + pInputNums[i] = 0; + fprintf( pFile, " %c", 'a' + pInput->S->index ); + } + else + { + pInputNums[i] = (*pSigCounter)++; + fprintf( pFile, " <%d>", pInputNums[i] ); + } + if ( fCompNew ) + fprintf( pFile, "\'" ); + } + fprintf( pFile, " )\n" ); +/* + fprintf( pFile, " ) " ); + { + DdNode * bLocal; + bLocal = Dsd_TreeGetPrimeFunction( dd, pNodeDsd ); Cudd_Ref( bLocal ); + Extra_bddPrint( dd, bLocal ); + Cudd_RecursiveDeref( dd, bLocal ); + } +*/ + // call recursively for the following blocks + for ( i = 0; i < pNode->nDecs; i++ ) + if ( pInputNums[i] ) + { + pInput = Dsd_Regular( pNode->pDecs[i] ); + sprintf( Buffer, "<%d>", pInputNums[i] ); + Dsd_NodePrint_rec( pFile, Dsd_Regular( pNode->pDecs[i] ), 0, Buffer, nOffset + 6, pSigCounter ); + } + } + else if ( pNode->Type == DSD_NODE_OR ) + { + // print the line + fprintf( pFile, "OR(" ); + for ( i = 0; i < pNode->nDecs; i++ ) + { + pInput = Dsd_Regular( pNode->pDecs[i] ); + fCompNew = (int)( pInput != pNode->pDecs[i] ); + if ( i ) + fprintf( pFile, "," ); + if ( pInput->Type == DSD_NODE_BUF ) + { + pInputNums[i] = 0; + fprintf( pFile, " %c", 'a' + pInput->S->index ); + } + else + { + pInputNums[i] = (*pSigCounter)++; + fprintf( pFile, " <%d>", pInputNums[i] ); + } + if ( fCompNew ) + fprintf( pFile, "\'" ); + } + fprintf( pFile, " )\n" ); + // call recursively for the following blocks + for ( i = 0; i < pNode->nDecs; i++ ) + if ( pInputNums[i] ) + { + pInput = Dsd_Regular( pNode->pDecs[i] ); + sprintf( Buffer, "<%d>", pInputNums[i] ); + Dsd_NodePrint_rec( pFile, Dsd_Regular( pNode->pDecs[i] ), 0, Buffer, nOffset + 6, pSigCounter ); + } + } + else if ( pNode->Type == DSD_NODE_EXOR ) + { + // print the line + fprintf( pFile, "EXOR(" ); + for ( i = 0; i < pNode->nDecs; i++ ) + { + pInput = Dsd_Regular( pNode->pDecs[i] ); + fCompNew = (int)( pInput != pNode->pDecs[i] ); + assert( fCompNew == 0 ); + if ( i ) + fprintf( pFile, "," ); + if ( pInput->Type == DSD_NODE_BUF ) + { + pInputNums[i] = 0; + fprintf( pFile, " %c", 'a' + pInput->S->index ); + } + else + { + pInputNums[i] = (*pSigCounter)++; + fprintf( pFile, " <%d>", pInputNums[i] ); + } + if ( fCompNew ) + fprintf( pFile, "\'" ); + } + fprintf( pFile, " )\n" ); + // call recursively for the following blocks + for ( i = 0; i < pNode->nDecs; i++ ) + if ( pInputNums[i] ) + { + pInput = Dsd_Regular( pNode->pDecs[i] ); + sprintf( Buffer, "<%d>", pInputNums[i] ); + Dsd_NodePrint_rec( pFile, Dsd_Regular( pNode->pDecs[i] ), 0, Buffer, nOffset + 6, pSigCounter ); + } + } + free( pInputNums ); +} + + +/**Function************************************************************* + + Synopsis [Retuns the function of one node of the decomposition tree.] + + Description [This is the old procedure. It is now superceded by the + procedure Dsd_TreeGetPrimeFunction() found in "dsdLocal.c".] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Dsd_TreeGetPrimeFunctionOld( DdManager * dd, Dsd_Node_t * pNode, int fRemap ) +{ + DdNode * bCof0, * bCof1, * bCube0, * bCube1, * bNewFunc, * bTemp; + int i; + int fAllBuffs = 1; + static int Permute[MAXINPUTS]; + + assert( pNode ); + assert( !Dsd_IsComplement( pNode ) ); + assert( pNode->Type == DSD_NODE_PRIME ); + + // transform the function of this block to depend on inputs + // corresponding to the formal inputs + + // first, substitute those inputs that have some blocks associated with them + // second, remap the inputs to the top of the manager (then, it is easy to output them) + + // start the function + bNewFunc = pNode->G; Cudd_Ref( bNewFunc ); + // go over all primary inputs + for ( i = 0; i < pNode->nDecs; i++ ) + if ( pNode->pDecs[i]->Type != DSD_NODE_BUF ) // remap only if it is not the buffer + { + bCube0 = Extra_bddFindOneCube( dd, Cudd_Not(pNode->pDecs[i]->G) ); Cudd_Ref( bCube0 ); + bCof0 = Cudd_Cofactor( dd, bNewFunc, bCube0 ); Cudd_Ref( bCof0 ); + Cudd_RecursiveDeref( dd, bCube0 ); + + bCube1 = Extra_bddFindOneCube( dd, pNode->pDecs[i]->G ); Cudd_Ref( bCube1 ); + bCof1 = Cudd_Cofactor( dd, bNewFunc, bCube1 ); Cudd_Ref( bCof1 ); + Cudd_RecursiveDeref( dd, bCube1 ); + + Cudd_RecursiveDeref( dd, bNewFunc ); + + // use the variable in the i-th level of the manager +// bNewFunc = Cudd_bddIte( dd, dd->vars[dd->invperm[i]],bCof1,bCof0 ); Cudd_Ref( bNewFunc ); + // use the first variale in the support of the component + bNewFunc = Cudd_bddIte( dd, dd->vars[pNode->pDecs[i]->S->index],bCof1,bCof0 ); Cudd_Ref( bNewFunc ); + Cudd_RecursiveDeref( dd, bCof0 ); + Cudd_RecursiveDeref( dd, bCof1 ); + } + + if ( fRemap ) + { + // remap the function to the top of the manager + // remap the function to the first variables of the manager + for ( i = 0; i < pNode->nDecs; i++ ) + // Permute[ pNode->pDecs[i]->S->index ] = dd->invperm[i]; + Permute[ pNode->pDecs[i]->S->index ] = i; + + bNewFunc = Cudd_bddPermute( dd, bTemp = bNewFunc, Permute ); Cudd_Ref( bNewFunc ); + Cudd_RecursiveDeref( dd, bTemp ); + } + + Cudd_Deref( bNewFunc ); + return bNewFunc; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/bdd/dsd/module.make b/abc_with_bb_support/src/bdd/dsd/module.make new file mode 100644 index 000000000..ed1f9a48e --- /dev/null +++ b/abc_with_bb_support/src/bdd/dsd/module.make @@ -0,0 +1,6 @@ +SRC += src/bdd/dsd/dsdApi.c \ + src/bdd/dsd/dsdCheck.c \ + src/bdd/dsd/dsdLocal.c \ + src/bdd/dsd/dsdMan.c \ + src/bdd/dsd/dsdProc.c \ + src/bdd/dsd/dsdTree.c diff --git a/abc_with_bb_support/src/bdd/epd/epd.c b/abc_with_bb_support/src/bdd/epd/epd.c new file mode 100644 index 000000000..c78af7f46 --- /dev/null +++ b/abc_with_bb_support/src/bdd/epd/epd.c @@ -0,0 +1,1314 @@ +/**CFile*********************************************************************** + + FileName [epd.c] + + PackageName [epd] + + Synopsis [Arithmetic functions with extended double precision.] + + Description [] + + SeeAlso [] + + Author [In-Ho Moon] + + Copyright [ This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + + Revision [$Id: epd.c,v 1.1.1.1 2003/02/24 22:23:57 wjiang Exp $] + +******************************************************************************/ + +#include +#include +#include +#include +#include "util_hack.h" +#include "epd.h" + + +/**Function******************************************************************** + + Synopsis [Allocates an EpDouble struct.] + + Description [Allocates an EpDouble struct.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +EpDouble * +EpdAlloc() +{ + EpDouble *epd; + + epd = ALLOC(EpDouble, 1); + return(epd); +} + + +/**Function******************************************************************** + + Synopsis [Compares two EpDouble struct.] + + Description [Compares two EpDouble struct.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +EpdCmp(const char *key1, const char *key2) +{ + EpDouble *epd1 = (EpDouble *) key1; + EpDouble *epd2 = (EpDouble *) key2; + if (epd1->type.value != epd2->type.value || + epd1->exponent != epd2->exponent) { + return(1); + } + return(0); +} + + +/**Function******************************************************************** + + Synopsis [Frees an EpDouble struct.] + + Description [Frees an EpDouble struct.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdFree(EpDouble *epd) +{ + FREE(epd); +} + + +/**Function******************************************************************** + + Synopsis [Multiplies two arbitrary precision double values.] + + Description [Multiplies two arbitrary precision double values.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdGetString(EpDouble *epd, char *str) +{ + double value; + int exponent; + char *pos; + + if (IsNanDouble(epd->type.value)) { + sprintf(str, "NaN"); + return; + } else if (IsInfDouble(epd->type.value)) { + if (epd->type.bits.sign == 1) + sprintf(str, "-Inf"); + else + sprintf(str, "Inf"); + return; + } + + assert(epd->type.bits.exponent == EPD_MAX_BIN || + epd->type.bits.exponent == 0); + + EpdGetValueAndDecimalExponent(epd, &value, &exponent); + sprintf(str, "%e", value); + pos = strstr(str, "e"); + if (exponent >= 0) { + if (exponent < 10) + sprintf(pos + 1, "+0%d", exponent); + else + sprintf(pos + 1, "+%d", exponent); + } else { + exponent *= -1; + if (exponent < 10) + sprintf(pos + 1, "-0%d", exponent); + else + sprintf(pos + 1, "-%d", exponent); + } +} + + +/**Function******************************************************************** + + Synopsis [Converts double to EpDouble struct.] + + Description [Converts double to EpDouble struct.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdConvert(double value, EpDouble *epd) +{ + epd->type.value = value; + epd->exponent = 0; + EpdNormalize(epd); +} + + +/**Function******************************************************************** + + Synopsis [Multiplies two arbitrary precision double values.] + + Description [Multiplies two arbitrary precision double values.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdMultiply(EpDouble *epd1, double value) +{ + EpDouble epd2; + double tmp; + int exponent; + + if (EpdIsNan(epd1) || IsNanDouble(value)) { + EpdMakeNan(epd1); + return; + } else if (EpdIsInf(epd1) || IsInfDouble(value)) { + int sign; + + EpdConvert(value, &epd2); + sign = epd1->type.bits.sign ^ epd2.type.bits.sign; + EpdMakeInf(epd1, sign); + return; + } + + assert(epd1->type.bits.exponent == EPD_MAX_BIN); + + EpdConvert(value, &epd2); + tmp = epd1->type.value * epd2.type.value; + exponent = epd1->exponent + epd2.exponent; + epd1->type.value = tmp; + epd1->exponent = exponent; + EpdNormalize(epd1); +} + + +/**Function******************************************************************** + + Synopsis [Multiplies two arbitrary precision double values.] + + Description [Multiplies two arbitrary precision double values.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdMultiply2(EpDouble *epd1, EpDouble *epd2) +{ + double value; + int exponent; + + if (EpdIsNan(epd1) || EpdIsNan(epd2)) { + EpdMakeNan(epd1); + return; + } else if (EpdIsInf(epd1) || EpdIsInf(epd2)) { + int sign; + + sign = epd1->type.bits.sign ^ epd2->type.bits.sign; + EpdMakeInf(epd1, sign); + return; + } + + assert(epd1->type.bits.exponent == EPD_MAX_BIN); + assert(epd2->type.bits.exponent == EPD_MAX_BIN); + + value = epd1->type.value * epd2->type.value; + exponent = epd1->exponent + epd2->exponent; + epd1->type.value = value; + epd1->exponent = exponent; + EpdNormalize(epd1); +} + + +/**Function******************************************************************** + + Synopsis [Multiplies two arbitrary precision double values.] + + Description [Multiplies two arbitrary precision double values.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdMultiply2Decimal(EpDouble *epd1, EpDouble *epd2) +{ + double value; + int exponent; + + if (EpdIsNan(epd1) || EpdIsNan(epd2)) { + EpdMakeNan(epd1); + return; + } else if (EpdIsInf(epd1) || EpdIsInf(epd2)) { + int sign; + + sign = epd1->type.bits.sign ^ epd2->type.bits.sign; + EpdMakeInf(epd1, sign); + return; + } + + value = epd1->type.value * epd2->type.value; + exponent = epd1->exponent + epd2->exponent; + epd1->type.value = value; + epd1->exponent = exponent; + EpdNormalizeDecimal(epd1); +} + + +/**Function******************************************************************** + + Synopsis [Multiplies two arbitrary precision double values.] + + Description [Multiplies two arbitrary precision double values.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdMultiply3(EpDouble *epd1, EpDouble *epd2, EpDouble *epd3) +{ + if (EpdIsNan(epd1) || EpdIsNan(epd2)) { + EpdMakeNan(epd1); + return; + } else if (EpdIsInf(epd1) || EpdIsInf(epd2)) { + int sign; + + sign = epd1->type.bits.sign ^ epd2->type.bits.sign; + EpdMakeInf(epd3, sign); + return; + } + + assert(epd1->type.bits.exponent == EPD_MAX_BIN); + assert(epd2->type.bits.exponent == EPD_MAX_BIN); + + epd3->type.value = epd1->type.value * epd2->type.value; + epd3->exponent = epd1->exponent + epd2->exponent; + EpdNormalize(epd3); +} + + +/**Function******************************************************************** + + Synopsis [Multiplies two arbitrary precision double values.] + + Description [Multiplies two arbitrary precision double values.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdMultiply3Decimal(EpDouble *epd1, EpDouble *epd2, EpDouble *epd3) +{ + if (EpdIsNan(epd1) || EpdIsNan(epd2)) { + EpdMakeNan(epd1); + return; + } else if (EpdIsInf(epd1) || EpdIsInf(epd2)) { + int sign; + + sign = epd1->type.bits.sign ^ epd2->type.bits.sign; + EpdMakeInf(epd3, sign); + return; + } + + epd3->type.value = epd1->type.value * epd2->type.value; + epd3->exponent = epd1->exponent + epd2->exponent; + EpdNormalizeDecimal(epd3); +} + + +/**Function******************************************************************** + + Synopsis [Divides two arbitrary precision double values.] + + Description [Divides two arbitrary precision double values.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdDivide(EpDouble *epd1, double value) +{ + EpDouble epd2; + double tmp; + int exponent; + + if (EpdIsNan(epd1) || IsNanDouble(value)) { + EpdMakeNan(epd1); + return; + } else if (EpdIsInf(epd1) || IsInfDouble(value)) { + int sign; + + EpdConvert(value, &epd2); + if (EpdIsInf(epd1) && IsInfDouble(value)) { + EpdMakeNan(epd1); + } else if (EpdIsInf(epd1)) { + sign = epd1->type.bits.sign ^ epd2.type.bits.sign; + EpdMakeInf(epd1, sign); + } else { + sign = epd1->type.bits.sign ^ epd2.type.bits.sign; + EpdMakeZero(epd1, sign); + } + return; + } + + if (value == 0.0) { + EpdMakeNan(epd1); + return; + } + + assert(epd1->type.bits.exponent == EPD_MAX_BIN); + + EpdConvert(value, &epd2); + tmp = epd1->type.value / epd2.type.value; + exponent = epd1->exponent - epd2.exponent; + epd1->type.value = tmp; + epd1->exponent = exponent; + EpdNormalize(epd1); +} + + +/**Function******************************************************************** + + Synopsis [Divides two arbitrary precision double values.] + + Description [Divides two arbitrary precision double values.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdDivide2(EpDouble *epd1, EpDouble *epd2) +{ + double value; + int exponent; + + if (EpdIsNan(epd1) || EpdIsNan(epd2)) { + EpdMakeNan(epd1); + return; + } else if (EpdIsInf(epd1) || EpdIsInf(epd2)) { + int sign; + + if (EpdIsInf(epd1) && EpdIsInf(epd2)) { + EpdMakeNan(epd1); + } else if (EpdIsInf(epd1)) { + sign = epd1->type.bits.sign ^ epd2->type.bits.sign; + EpdMakeInf(epd1, sign); + } else { + sign = epd1->type.bits.sign ^ epd2->type.bits.sign; + EpdMakeZero(epd1, sign); + } + return; + } + + if (epd2->type.value == 0.0) { + EpdMakeNan(epd1); + return; + } + + assert(epd1->type.bits.exponent == EPD_MAX_BIN); + assert(epd2->type.bits.exponent == EPD_MAX_BIN); + + value = epd1->type.value / epd2->type.value; + exponent = epd1->exponent - epd2->exponent; + epd1->type.value = value; + epd1->exponent = exponent; + EpdNormalize(epd1); +} + + +/**Function******************************************************************** + + Synopsis [Divides two arbitrary precision double values.] + + Description [Divides two arbitrary precision double values.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdDivide3(EpDouble *epd1, EpDouble *epd2, EpDouble *epd3) +{ + if (EpdIsNan(epd1) || EpdIsNan(epd2)) { + EpdMakeNan(epd3); + return; + } else if (EpdIsInf(epd1) || EpdIsInf(epd2)) { + int sign; + + if (EpdIsInf(epd1) && EpdIsInf(epd2)) { + EpdMakeNan(epd3); + } else if (EpdIsInf(epd1)) { + sign = epd1->type.bits.sign ^ epd2->type.bits.sign; + EpdMakeInf(epd3, sign); + } else { + sign = epd1->type.bits.sign ^ epd2->type.bits.sign; + EpdMakeZero(epd3, sign); + } + return; + } + + if (epd2->type.value == 0.0) { + EpdMakeNan(epd3); + return; + } + + assert(epd1->type.bits.exponent == EPD_MAX_BIN); + assert(epd2->type.bits.exponent == EPD_MAX_BIN); + + epd3->type.value = epd1->type.value / epd2->type.value; + epd3->exponent = epd1->exponent - epd2->exponent; + EpdNormalize(epd3); +} + + +/**Function******************************************************************** + + Synopsis [Adds two arbitrary precision double values.] + + Description [Adds two arbitrary precision double values.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdAdd(EpDouble *epd1, double value) +{ + EpDouble epd2; + double tmp; + int exponent, diff; + + if (EpdIsNan(epd1) || IsNanDouble(value)) { + EpdMakeNan(epd1); + return; + } else if (EpdIsInf(epd1) || IsInfDouble(value)) { + int sign; + + EpdConvert(value, &epd2); + if (EpdIsInf(epd1) && IsInfDouble(value)) { + sign = epd1->type.bits.sign ^ epd2.type.bits.sign; + if (sign == 1) + EpdMakeNan(epd1); + } else if (EpdIsInf(&epd2)) { + EpdCopy(&epd2, epd1); + } + return; + } + + assert(epd1->type.bits.exponent == EPD_MAX_BIN); + + EpdConvert(value, &epd2); + if (epd1->exponent > epd2.exponent) { + diff = epd1->exponent - epd2.exponent; + if (diff <= EPD_MAX_BIN) + tmp = epd1->type.value + epd2.type.value / pow((double)2.0, (double)diff); + else + tmp = epd1->type.value; + exponent = epd1->exponent; + } else if (epd1->exponent < epd2.exponent) { + diff = epd2.exponent - epd1->exponent; + if (diff <= EPD_MAX_BIN) + tmp = epd1->type.value / pow((double)2.0, (double)diff) + epd2.type.value; + else + tmp = epd2.type.value; + exponent = epd2.exponent; + } else { + tmp = epd1->type.value + epd2.type.value; + exponent = epd1->exponent; + } + epd1->type.value = tmp; + epd1->exponent = exponent; + EpdNormalize(epd1); +} + + +/**Function******************************************************************** + + Synopsis [Adds two arbitrary precision double values.] + + Description [Adds two arbitrary precision double values.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdAdd2(EpDouble *epd1, EpDouble *epd2) +{ + double value; + int exponent, diff; + + if (EpdIsNan(epd1) || EpdIsNan(epd2)) { + EpdMakeNan(epd1); + return; + } else if (EpdIsInf(epd1) || EpdIsInf(epd2)) { + int sign; + + if (EpdIsInf(epd1) && EpdIsInf(epd2)) { + sign = epd1->type.bits.sign ^ epd2->type.bits.sign; + if (sign == 1) + EpdMakeNan(epd1); + } else if (EpdIsInf(epd2)) { + EpdCopy(epd2, epd1); + } + return; + } + + assert(epd1->type.bits.exponent == EPD_MAX_BIN); + assert(epd2->type.bits.exponent == EPD_MAX_BIN); + + if (epd1->exponent > epd2->exponent) { + diff = epd1->exponent - epd2->exponent; + if (diff <= EPD_MAX_BIN) { + value = epd1->type.value + + epd2->type.value / pow((double)2.0, (double)diff); + } else + value = epd1->type.value; + exponent = epd1->exponent; + } else if (epd1->exponent < epd2->exponent) { + diff = epd2->exponent - epd1->exponent; + if (diff <= EPD_MAX_BIN) { + value = epd1->type.value / pow((double)2.0, (double)diff) + + epd2->type.value; + } else + value = epd2->type.value; + exponent = epd2->exponent; + } else { + value = epd1->type.value + epd2->type.value; + exponent = epd1->exponent; + } + epd1->type.value = value; + epd1->exponent = exponent; + EpdNormalize(epd1); +} + + +/**Function******************************************************************** + + Synopsis [Adds two arbitrary precision double values.] + + Description [Adds two arbitrary precision double values.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdAdd3(EpDouble *epd1, EpDouble *epd2, EpDouble *epd3) +{ + double value; + int exponent, diff; + + if (EpdIsNan(epd1) || EpdIsNan(epd2)) { + EpdMakeNan(epd3); + return; + } else if (EpdIsInf(epd1) || EpdIsInf(epd2)) { + int sign; + + if (EpdIsInf(epd1) && EpdIsInf(epd2)) { + sign = epd1->type.bits.sign ^ epd2->type.bits.sign; + if (sign == 1) + EpdMakeNan(epd3); + else + EpdCopy(epd1, epd3); + } else if (EpdIsInf(epd1)) { + EpdCopy(epd1, epd3); + } else { + EpdCopy(epd2, epd3); + } + return; + } + + assert(epd1->type.bits.exponent == EPD_MAX_BIN); + assert(epd2->type.bits.exponent == EPD_MAX_BIN); + + if (epd1->exponent > epd2->exponent) { + diff = epd1->exponent - epd2->exponent; + if (diff <= EPD_MAX_BIN) { + value = epd1->type.value + + epd2->type.value / pow((double)2.0, (double)diff); + } else + value = epd1->type.value; + exponent = epd1->exponent; + } else if (epd1->exponent < epd2->exponent) { + diff = epd2->exponent - epd1->exponent; + if (diff <= EPD_MAX_BIN) { + value = epd1->type.value / pow((double)2.0, (double)diff) + + epd2->type.value; + } else + value = epd2->type.value; + exponent = epd2->exponent; + } else { + value = epd1->type.value + epd2->type.value; + exponent = epd1->exponent; + } + epd3->type.value = value; + epd3->exponent = exponent; + EpdNormalize(epd3); +} + + +/**Function******************************************************************** + + Synopsis [Subtracts two arbitrary precision double values.] + + Description [Subtracts two arbitrary precision double values.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdSubtract(EpDouble *epd1, double value) +{ + EpDouble epd2; + double tmp; + int exponent, diff; + + if (EpdIsNan(epd1) || IsNanDouble(value)) { + EpdMakeNan(epd1); + return; + } else if (EpdIsInf(epd1) || IsInfDouble(value)) { + int sign; + + EpdConvert(value, &epd2); + if (EpdIsInf(epd1) && IsInfDouble(value)) { + sign = epd1->type.bits.sign ^ epd2.type.bits.sign; + if (sign == 0) + EpdMakeNan(epd1); + } else if (EpdIsInf(&epd2)) { + EpdCopy(&epd2, epd1); + } + return; + } + + assert(epd1->type.bits.exponent == EPD_MAX_BIN); + + EpdConvert(value, &epd2); + if (epd1->exponent > epd2.exponent) { + diff = epd1->exponent - epd2.exponent; + if (diff <= EPD_MAX_BIN) + tmp = epd1->type.value - epd2.type.value / pow((double)2.0, (double)diff); + else + tmp = epd1->type.value; + exponent = epd1->exponent; + } else if (epd1->exponent < epd2.exponent) { + diff = epd2.exponent - epd1->exponent; + if (diff <= EPD_MAX_BIN) + tmp = epd1->type.value / pow((double)2.0, (double)diff) - epd2.type.value; + else + tmp = epd2.type.value * (double)(-1.0); + exponent = epd2.exponent; + } else { + tmp = epd1->type.value - epd2.type.value; + exponent = epd1->exponent; + } + epd1->type.value = tmp; + epd1->exponent = exponent; + EpdNormalize(epd1); +} + + +/**Function******************************************************************** + + Synopsis [Subtracts two arbitrary precision double values.] + + Description [Subtracts two arbitrary precision double values.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdSubtract2(EpDouble *epd1, EpDouble *epd2) +{ + double value; + int exponent, diff; + + if (EpdIsNan(epd1) || EpdIsNan(epd2)) { + EpdMakeNan(epd1); + return; + } else if (EpdIsInf(epd1) || EpdIsInf(epd2)) { + int sign; + + if (EpdIsInf(epd1) && EpdIsInf(epd2)) { + sign = epd1->type.bits.sign ^ epd2->type.bits.sign; + if (sign == 0) + EpdMakeNan(epd1); + } else if (EpdIsInf(epd2)) { + EpdCopy(epd2, epd1); + } + return; + } + + assert(epd1->type.bits.exponent == EPD_MAX_BIN); + assert(epd2->type.bits.exponent == EPD_MAX_BIN); + + if (epd1->exponent > epd2->exponent) { + diff = epd1->exponent - epd2->exponent; + if (diff <= EPD_MAX_BIN) { + value = epd1->type.value - + epd2->type.value / pow((double)2.0, (double)diff); + } else + value = epd1->type.value; + exponent = epd1->exponent; + } else if (epd1->exponent < epd2->exponent) { + diff = epd2->exponent - epd1->exponent; + if (diff <= EPD_MAX_BIN) { + value = epd1->type.value / pow((double)2.0, (double)diff) - + epd2->type.value; + } else + value = epd2->type.value * (double)(-1.0); + exponent = epd2->exponent; + } else { + value = epd1->type.value - epd2->type.value; + exponent = epd1->exponent; + } + epd1->type.value = value; + epd1->exponent = exponent; + EpdNormalize(epd1); +} + + +/**Function******************************************************************** + + Synopsis [Subtracts two arbitrary precision double values.] + + Description [Subtracts two arbitrary precision double values.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdSubtract3(EpDouble *epd1, EpDouble *epd2, EpDouble *epd3) +{ + double value; + int exponent, diff; + + if (EpdIsNan(epd1) || EpdIsNan(epd2)) { + EpdMakeNan(epd3); + return; + } else if (EpdIsInf(epd1) || EpdIsInf(epd2)) { + int sign; + + if (EpdIsInf(epd1) && EpdIsInf(epd2)) { + sign = epd1->type.bits.sign ^ epd2->type.bits.sign; + if (sign == 0) + EpdCopy(epd1, epd3); + else + EpdMakeNan(epd3); + } else if (EpdIsInf(epd1)) { + EpdCopy(epd1, epd1); + } else { + sign = epd2->type.bits.sign ^ 0x1; + EpdMakeInf(epd3, sign); + } + return; + } + + assert(epd1->type.bits.exponent == EPD_MAX_BIN); + assert(epd2->type.bits.exponent == EPD_MAX_BIN); + + if (epd1->exponent > epd2->exponent) { + diff = epd1->exponent - epd2->exponent; + if (diff <= EPD_MAX_BIN) { + value = epd1->type.value - + epd2->type.value / pow((double)2.0, (double)diff); + } else + value = epd1->type.value; + exponent = epd1->exponent; + } else if (epd1->exponent < epd2->exponent) { + diff = epd2->exponent - epd1->exponent; + if (diff <= EPD_MAX_BIN) { + value = epd1->type.value / pow((double)2.0, (double)diff) - + epd2->type.value; + } else + value = epd2->type.value * (double)(-1.0); + exponent = epd2->exponent; + } else { + value = epd1->type.value - epd2->type.value; + exponent = epd1->exponent; + } + epd3->type.value = value; + epd3->exponent = exponent; + EpdNormalize(epd3); +} + + +/**Function******************************************************************** + + Synopsis [Computes arbitrary precision pow of base 2.] + + Description [Computes arbitrary precision pow of base 2.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdPow2(int n, EpDouble *epd) +{ + if (n <= EPD_MAX_BIN) { + EpdConvert(pow((double)2.0, (double)n), epd); + } else { + EpDouble epd1, epd2; + int n1, n2; + + n1 = n / 2; + n2 = n - n1; + EpdPow2(n1, &epd1); + EpdPow2(n2, &epd2); + EpdMultiply3(&epd1, &epd2, epd); + } +} + + +/**Function******************************************************************** + + Synopsis [Computes arbitrary precision pow of base 2.] + + Description [Computes arbitrary precision pow of base 2.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdPow2Decimal(int n, EpDouble *epd) +{ + if (n <= EPD_MAX_BIN) { + epd->type.value = pow((double)2.0, (double)n); + epd->exponent = 0; + EpdNormalizeDecimal(epd); + } else { + EpDouble epd1, epd2; + int n1, n2; + + n1 = n / 2; + n2 = n - n1; + EpdPow2Decimal(n1, &epd1); + EpdPow2Decimal(n2, &epd2); + EpdMultiply3Decimal(&epd1, &epd2, epd); + } +} + + +/**Function******************************************************************** + + Synopsis [Normalize an arbitrary precision double value.] + + Description [Normalize an arbitrary precision double value.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdNormalize(EpDouble *epd) +{ + int exponent; + + if (IsNanOrInfDouble(epd->type.value)) { + epd->exponent = 0; + return; + } + + exponent = EpdGetExponent(epd->type.value); + if (exponent == EPD_MAX_BIN) + return; + exponent -= EPD_MAX_BIN; + epd->type.bits.exponent = EPD_MAX_BIN; + epd->exponent += exponent; +} + + +/**Function******************************************************************** + + Synopsis [Normalize an arbitrary precision double value.] + + Description [Normalize an arbitrary precision double value.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdNormalizeDecimal(EpDouble *epd) +{ + int exponent; + + if (IsNanOrInfDouble(epd->type.value)) { + epd->exponent = 0; + return; + } + + exponent = EpdGetExponentDecimal(epd->type.value); + epd->type.value /= pow((double)10.0, (double)exponent); + epd->exponent += exponent; +} + + +/**Function******************************************************************** + + Synopsis [Returns value and decimal exponent of EpDouble.] + + Description [Returns value and decimal exponent of EpDouble.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdGetValueAndDecimalExponent(EpDouble *epd, double *value, int *exponent) +{ + EpDouble epd1, epd2; + + if (EpdIsNanOrInf(epd)) + return; + + if (EpdIsZero(epd)) { + *value = 0.0; + *exponent = 0; + return; + } + + epd1.type.value = epd->type.value; + epd1.exponent = 0; + EpdPow2Decimal(epd->exponent, &epd2); + EpdMultiply2Decimal(&epd1, &epd2); + + *value = epd1.type.value; + *exponent = epd1.exponent; +} + +/**Function******************************************************************** + + Synopsis [Returns the exponent value of a double.] + + Description [Returns the exponent value of a double.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +EpdGetExponent(double value) +{ + int exponent; + EpDouble epd; + + epd.type.value = value; + exponent = epd.type.bits.exponent; + return(exponent); +} + + +/**Function******************************************************************** + + Synopsis [Returns the decimal exponent value of a double.] + + Description [Returns the decimal exponent value of a double.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +EpdGetExponentDecimal(double value) +{ + char *pos, str[24]; + int exponent; + + sprintf(str, "%E", value); + pos = strstr(str, "E"); + sscanf(pos, "E%d", &exponent); + return(exponent); +} + + +/**Function******************************************************************** + + Synopsis [Makes EpDouble Inf.] + + Description [Makes EpDouble Inf.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdMakeInf(EpDouble *epd, int sign) +{ + epd->type.bits.mantissa1 = 0; + epd->type.bits.mantissa0 = 0; + epd->type.bits.exponent = EPD_EXP_INF; + epd->type.bits.sign = sign; + epd->exponent = 0; +} + + +/**Function******************************************************************** + + Synopsis [Makes EpDouble Zero.] + + Description [Makes EpDouble Zero.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdMakeZero(EpDouble *epd, int sign) +{ + epd->type.bits.mantissa1 = 0; + epd->type.bits.mantissa0 = 0; + epd->type.bits.exponent = 0; + epd->type.bits.sign = sign; + epd->exponent = 0; +} + + +/**Function******************************************************************** + + Synopsis [Makes EpDouble NaN.] + + Description [Makes EpDouble NaN.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdMakeNan(EpDouble *epd) +{ + epd->type.nan.mantissa1 = 0; + epd->type.nan.mantissa0 = 0; + epd->type.nan.quiet_bit = 1; + epd->type.nan.exponent = EPD_EXP_INF; + epd->type.nan.sign = 1; + epd->exponent = 0; +} + + +/**Function******************************************************************** + + Synopsis [Copies a EpDouble struct.] + + Description [Copies a EpDouble struct.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdCopy(EpDouble *from, EpDouble *to) +{ + to->type.value = from->type.value; + to->exponent = from->exponent; +} + + +/**Function******************************************************************** + + Synopsis [Checks whether the value is Inf.] + + Description [Checks whether the value is Inf.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +EpdIsInf(EpDouble *epd) +{ + return(IsInfDouble(epd->type.value)); +} + + +/**Function******************************************************************** + + Synopsis [Checks whether the value is Zero.] + + Description [Checks whether the value is Zero.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +EpdIsZero(EpDouble *epd) +{ + if (epd->type.value == 0.0) + return(1); + else + return(0); +} + + +/**Function******************************************************************** + + Synopsis [Checks whether the value is NaN.] + + Description [Checks whether the value is NaN.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +EpdIsNan(EpDouble *epd) +{ + return(IsNanDouble(epd->type.value)); +} + + +/**Function******************************************************************** + + Synopsis [Checks whether the value is NaN or Inf.] + + Description [Checks whether the value is NaN or Inf.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +EpdIsNanOrInf(EpDouble *epd) +{ + return(IsNanOrInfDouble(epd->type.value)); +} + + +/**Function******************************************************************** + + Synopsis [Checks whether the value is Inf.] + + Description [Checks whether the value is Inf.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +IsInfDouble(double value) +{ + IeeeDouble *ptr = (IeeeDouble *)(&value); + + if (ptr->exponent == EPD_EXP_INF && + ptr->mantissa0 == 0 && + ptr->mantissa1 == 0) { + if (ptr->sign == 0) + return(1); + else + return(-1); + } + return(0); +} + + +/**Function******************************************************************** + + Synopsis [Checks whether the value is NaN.] + + Description [Checks whether the value is NaN.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +IsNanDouble(double value) +{ + IeeeNan *ptr = (IeeeNan *)(&value); + + if (ptr->exponent == EPD_EXP_INF && + ptr->sign == 1 && + ptr->quiet_bit == 1 && + ptr->mantissa0 == 0 && + ptr->mantissa1 == 0) { + return(1); + } + return(0); +} + + +/**Function******************************************************************** + + Synopsis [Checks whether the value is NaN or Inf.] + + Description [Checks whether the value is NaN or Inf.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +IsNanOrInfDouble(double value) +{ + IeeeNan *ptr = (IeeeNan *)(&value); + + if (ptr->exponent == EPD_EXP_INF && + ptr->mantissa0 == 0 && + ptr->mantissa1 == 0 && + (ptr->sign == 1 || ptr->quiet_bit == 0)) { + return(1); + } + return(0); +} diff --git a/abc_with_bb_support/src/bdd/epd/epd.h b/abc_with_bb_support/src/bdd/epd/epd.h new file mode 100644 index 000000000..b8c5f14aa --- /dev/null +++ b/abc_with_bb_support/src/bdd/epd/epd.h @@ -0,0 +1,160 @@ +/**CHeaderFile***************************************************************** + + FileName [epd.h] + + PackageName [epd] + + Synopsis [The University of Colorado extended double precision package.] + + Description [arithmetic functions with extended double precision.] + + SeeAlso [] + + Author [In-Ho Moon] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + + Revision [$Id: epd.h,v 1.1.1.1 2003/02/24 22:23:57 wjiang Exp $] + +******************************************************************************/ + +#ifndef _EPD +#define _EPD + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define EPD_MAX_BIN 1023 +#define EPD_MAX_DEC 308 +#define EPD_EXP_INF 0x7ff + +/*---------------------------------------------------------------------------*/ +/* Structure declarations */ +/*---------------------------------------------------------------------------*/ + +/**Struct********************************************************************** + + Synopsis [IEEE double struct.] + + Description [IEEE double struct.] + + SeeAlso [] + +******************************************************************************/ +#ifdef EPD_BIG_ENDIAN +struct IeeeDoubleStruct { /* BIG_ENDIAN */ + unsigned int sign: 1; + unsigned int exponent: 11; + unsigned int mantissa0: 20; + unsigned int mantissa1: 32; +}; +#else +struct IeeeDoubleStruct { /* LITTLE_ENDIAN */ + unsigned int mantissa1: 32; + unsigned int mantissa0: 20; + unsigned int exponent: 11; + unsigned int sign: 1; +}; +#endif + +/**Struct********************************************************************** + + Synopsis [IEEE double NaN struct.] + + Description [IEEE double NaN struct.] + + SeeAlso [] + +******************************************************************************/ +#ifdef EPD_BIG_ENDIAN +struct IeeeNanStruct { /* BIG_ENDIAN */ + unsigned int sign: 1; + unsigned int exponent: 11; + unsigned int quiet_bit: 1; + unsigned int mantissa0: 19; + unsigned int mantissa1: 32; +}; +#else +struct IeeeNanStruct { /* LITTLE_ENDIAN */ + unsigned int mantissa1: 32; + unsigned int mantissa0: 19; + unsigned int quiet_bit: 1; + unsigned int exponent: 11; + unsigned int sign: 1; +}; +#endif + +/**Struct********************************************************************** + + Synopsis [Extended precision double to keep very large value.] + + Description [Extended precision double to keep very large value.] + + SeeAlso [] + +******************************************************************************/ +struct EpDoubleStruct { + union { + double value; + struct IeeeDoubleStruct bits; + struct IeeeNanStruct nan; + } type; + int exponent; +}; + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ +typedef struct EpDoubleStruct EpDouble; +typedef struct IeeeDoubleStruct IeeeDouble; +typedef struct IeeeNanStruct IeeeNan; + + +/*---------------------------------------------------------------------------*/ +/* Function prototypes */ +/*---------------------------------------------------------------------------*/ + +EpDouble *EpdAlloc(); +int EpdCmp(const char *key1, const char *key2); +void EpdFree(EpDouble *epd); +void EpdGetString(EpDouble *epd, char *str); +void EpdConvert(double value, EpDouble *epd); +void EpdMultiply(EpDouble *epd1, double value); +void EpdMultiply2(EpDouble *epd1, EpDouble *epd2); +void EpdMultiply2Decimal(EpDouble *epd1, EpDouble *epd2); +void EpdMultiply3(EpDouble *epd1, EpDouble *epd2, EpDouble *epd3); +void EpdMultiply3Decimal(EpDouble *epd1, EpDouble *epd2, EpDouble *epd3); +void EpdDivide(EpDouble *epd1, double value); +void EpdDivide2(EpDouble *epd1, EpDouble *epd2); +void EpdDivide3(EpDouble *epd1, EpDouble *epd2, EpDouble *epd3); +void EpdAdd(EpDouble *epd1, double value); +void EpdAdd2(EpDouble *epd1, EpDouble *epd2); +void EpdAdd3(EpDouble *epd1, EpDouble *epd2, EpDouble *epd3); +void EpdSubtract(EpDouble *epd1, double value); +void EpdSubtract2(EpDouble *epd1, EpDouble *epd2); +void EpdSubtract3(EpDouble *epd1, EpDouble *epd2, EpDouble *epd3); +void EpdPow2(int n, EpDouble *epd); +void EpdPow2Decimal(int n, EpDouble *epd); +void EpdNormalize(EpDouble *epd); +void EpdNormalizeDecimal(EpDouble *epd); +void EpdGetValueAndDecimalExponent(EpDouble *epd, double *value, int *exponent); +int EpdGetExponent(double value); +int EpdGetExponentDecimal(double value); +void EpdMakeInf(EpDouble *epd, int sign); +void EpdMakeZero(EpDouble *epd, int sign); +void EpdMakeNan(EpDouble *epd); +void EpdCopy(EpDouble *from, EpDouble *to); +int EpdIsInf(EpDouble *epd); +int EpdIsZero(EpDouble *epd); +int EpdIsNan(EpDouble *epd); +int EpdIsNanOrInf(EpDouble *epd); +int IsInfDouble(double value); +int IsNanDouble(double value); +int IsNanOrInfDouble(double value); + +#endif /* _EPD */ diff --git a/abc_with_bb_support/src/bdd/epd/module.make b/abc_with_bb_support/src/bdd/epd/module.make new file mode 100644 index 000000000..a8084db11 --- /dev/null +++ b/abc_with_bb_support/src/bdd/epd/module.make @@ -0,0 +1 @@ +SRC += src/bdd/epd/epd.c diff --git a/abc_with_bb_support/src/bdd/mtr/module.make b/abc_with_bb_support/src/bdd/mtr/module.make new file mode 100644 index 000000000..d72c29a6d --- /dev/null +++ b/abc_with_bb_support/src/bdd/mtr/module.make @@ -0,0 +1,2 @@ +SRC += src/bdd/mtr/mtrBasic.c \ + src/bdd/mtr/mtrGroup.c diff --git a/abc_with_bb_support/src/bdd/mtr/mtr.h b/abc_with_bb_support/src/bdd/mtr/mtr.h new file mode 100644 index 000000000..1b897c53f --- /dev/null +++ b/abc_with_bb_support/src/bdd/mtr/mtr.h @@ -0,0 +1,173 @@ +/**CHeaderFile***************************************************************** + + FileName [mtr.h] + + PackageName [mtr] + + Synopsis [Multiway-branch tree manipulation] + + Description [This package provides two layers of functions. Functions + of the lower level manipulate multiway-branch trees, implemented + according to the classical scheme whereby each node points to its + first child and its previous and next siblings. These functions are + collected in mtrBasic.c.

        + Functions of the upper layer deal with group trees, that is the trees + used by group sifting to represent the grouping of variables. These + functions are collected in mtrGroup.c.] + + SeeAlso [The CUDD package documentation; specifically on group + sifting.] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + + Revision [$Id: mtr.h,v 1.1.1.1 2003/02/24 22:24:02 wjiang Exp $] + +******************************************************************************/ + +#ifndef __MTR +#define __MTR + +/*---------------------------------------------------------------------------*/ +/* Nested includes */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef SIZEOF_VOID_P +#define SIZEOF_VOID_P 4 +#endif +#ifndef SIZEOF_INT +#define SIZEOF_INT 4 +#endif + +#undef CONST +#if defined(__STDC__) || defined(__cplusplus) +#define CONST const +#else /* !(__STDC__ || __cplusplus) */ +#define CONST +#endif /* !(__STDC__ || __cplusplus) */ + +/* These are potential duplicates. */ +#ifndef EXTERN +# ifdef __cplusplus +# define EXTERN extern "C" +# else +# define EXTERN extern +# endif +#endif +#ifndef ARGS +# if defined(__STDC__) || defined(__cplusplus) +# define ARGS(protos) protos /* ANSI C */ +# else /* !(__STDC__ || __cplusplus) */ +# define ARGS(protos) () /* K&R C */ +# endif /* !(__STDC__ || __cplusplus) */ +#endif + +#if defined(__GNUC__) +#define MTR_INLINE __inline__ +# if (__GNUC__ >2 || __GNUC_MINOR__ >=7) +# define MTR_UNUSED __attribute__ ((unused)) +# else +# define MTR_UNUSED +# endif +#else +#define MTR_INLINE +#define MTR_UNUSED +#endif + +/* Flag definitions */ +#define MTR_DEFAULT 0x00000000 +#define MTR_TERMINAL 0x00000001 +#define MTR_SOFT 0x00000002 +#define MTR_FIXED 0x00000004 +#define MTR_NEWNODE 0x00000008 + +/* MTR_MAXHIGH is defined in such a way that on 32-bit and 64-bit +** machines one can cast a value to (int) without generating a negative +** number. +*/ +#if SIZEOF_VOID_P == 8 && SIZEOF_INT == 4 +#define MTR_MAXHIGH (((MtrHalfWord) ~0) >> 1) +#else +#define MTR_MAXHIGH ((MtrHalfWord) ~0) +#endif + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +#if SIZEOF_VOID_P == 8 && SIZEOF_INT == 4 +typedef unsigned int MtrHalfWord; +#else +typedef unsigned short MtrHalfWord; +#endif + +typedef struct MtrNode { + MtrHalfWord flags; + MtrHalfWord low; + MtrHalfWord size; + MtrHalfWord index; + struct MtrNode *parent; + struct MtrNode *child; + struct MtrNode *elder; + struct MtrNode *younger; +} MtrNode; + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/* Flag manipulation macros */ +#define MTR_SET(node, flag) (node->flags |= (flag)) +#define MTR_RESET(node, flag) (node->flags &= ~ (flag)) +#define MTR_TEST(node, flag) (node->flags & (flag)) + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Function prototypes */ +/*---------------------------------------------------------------------------*/ + +EXTERN MtrNode * Mtr_AllocNode ARGS(()); +EXTERN void Mtr_DeallocNode ARGS((MtrNode *node)); +EXTERN MtrNode * Mtr_InitTree ARGS(()); +EXTERN void Mtr_FreeTree ARGS((MtrNode *node)); +EXTERN MtrNode * Mtr_CopyTree ARGS((MtrNode *node, int expansion)); +EXTERN void Mtr_MakeFirstChild ARGS((MtrNode *parent, MtrNode *child)); +EXTERN void Mtr_MakeLastChild ARGS((MtrNode *parent, MtrNode *child)); +EXTERN MtrNode * Mtr_CreateFirstChild ARGS((MtrNode *parent)); +EXTERN MtrNode * Mtr_CreateLastChild ARGS((MtrNode *parent)); +EXTERN void Mtr_MakeNextSibling ARGS((MtrNode *first, MtrNode *second)); +EXTERN void Mtr_PrintTree ARGS((MtrNode *node)); +EXTERN MtrNode * Mtr_InitGroupTree ARGS((int lower, int size)); +EXTERN MtrNode * Mtr_MakeGroup ARGS((MtrNode *root, unsigned int low, unsigned int high, unsigned int flags)); +EXTERN MtrNode * Mtr_DissolveGroup ARGS((MtrNode *group)); +EXTERN MtrNode * Mtr_FindGroup ARGS((MtrNode *root, unsigned int low, unsigned int high)); +EXTERN int Mtr_SwapGroups ARGS((MtrNode *first, MtrNode *second)); +EXTERN void Mtr_PrintGroups ARGS((MtrNode *root, int silent)); +EXTERN MtrNode * Mtr_ReadGroups ARGS((FILE *fp, int nleaves)); + +/**AutomaticEnd***************************************************************/ + +#endif /* __MTR */ diff --git a/abc_with_bb_support/src/bdd/mtr/mtrBasic.c b/abc_with_bb_support/src/bdd/mtr/mtrBasic.c new file mode 100644 index 000000000..07cba675f --- /dev/null +++ b/abc_with_bb_support/src/bdd/mtr/mtrBasic.c @@ -0,0 +1,426 @@ +/**CFile*********************************************************************** + + FileName [mtrBasic.c] + + PackageName [mtr] + + Synopsis [Basic manipulation of multiway branching trees.] + + Description [External procedures included in this module: +

          +
        • Mtr_AllocNode() +
        • Mtr_DeallocNode() +
        • Mtr_InitTree() +
        • Mtr_FreeTree() +
        • Mtr_CopyTree() +
        • Mtr_MakeFirstChild() +
        • Mtr_MakeLastChild() +
        • Mtr_CreateFirstChild() +
        • Mtr_CreateLastChild() +
        • Mtr_MakeNextSibling() +
        • Mtr_PrintTree() +
        + ] + + SeeAlso [cudd package] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "mtrInt.h" + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] MTR_UNUSED = "$Id: mtrBasic.c,v 1.1.1.1 2003/02/24 22:24:02 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Allocates new tree node.] + + Description [Allocates new tree node. Returns pointer to node.] + + SideEffects [None] + + SeeAlso [Mtr_DeallocNode] + +******************************************************************************/ +MtrNode * +Mtr_AllocNode( + ) +{ + MtrNode *node; + + node = ALLOC(MtrNode,1); + return node; + +} /* Mtr_AllocNode */ + + +/**Function******************************************************************** + + Synopsis [Deallocates tree node.] + + Description [] + + SideEffects [None] + + SeeAlso [Mtr_AllocNode] + +******************************************************************************/ +void +Mtr_DeallocNode( + MtrNode * node /* node to be deallocated */) +{ + FREE(node); + return; + +} /* end of Mtr_DeallocNode */ + + +/**Function******************************************************************** + + Synopsis [Initializes tree with one node.] + + Description [Initializes tree with one node. Returns pointer to node.] + + SideEffects [None] + + SeeAlso [Mtr_FreeTree Mtr_InitGroupTree] + +******************************************************************************/ +MtrNode * +Mtr_InitTree( + ) +{ + MtrNode *node; + + node = Mtr_AllocNode(); + if (node == NULL) return(NULL); + + node->parent = node->child = node->elder = node->younger = NULL; + node->flags = 0; + + return(node); + +} /* end of Mtr_InitTree */ + + +/**Function******************************************************************** + + Synopsis [Disposes of tree rooted at node.] + + Description [] + + SideEffects [None] + + SeeAlso [Mtr_InitTree] + +******************************************************************************/ +void +Mtr_FreeTree( + MtrNode * node) +{ + if (node == NULL) return; + if (! MTR_TEST(node,MTR_TERMINAL)) Mtr_FreeTree(node->child); + Mtr_FreeTree(node->younger); + Mtr_DeallocNode(node); + return; + +} /* end of Mtr_FreeTree */ + + +/**Function******************************************************************** + + Synopsis [Makes a copy of tree.] + + Description [Makes a copy of tree. If parameter expansion is greater + than 1, it will expand the tree by that factor. It is an error for + expansion to be less than 1. Returns a pointer to the copy if + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Mtr_InitTree] + +******************************************************************************/ +MtrNode * +Mtr_CopyTree( + MtrNode * node, + int expansion) +{ + MtrNode *copy; + + if (node == NULL) return(NULL); + if (expansion < 1) return(NULL); + copy = Mtr_AllocNode(); + if (copy == NULL) return(NULL); + copy->parent = copy->elder = copy->child = copy->younger = NULL; + if (node->child != NULL) { + copy->child = Mtr_CopyTree(node->child, expansion); + if (copy->child == NULL) { + Mtr_DeallocNode(copy); + return(NULL); + } + } + if (node->younger != NULL) { + copy->younger = Mtr_CopyTree(node->younger, expansion); + if (copy->younger == NULL) { + Mtr_FreeTree(copy); + return(NULL); + } + } + copy->flags = node->flags; + copy->low = node->low * expansion; + copy->size = node->size * expansion; + copy->index = node->index * expansion; + if (copy->younger) copy->younger->elder = copy; + if (copy->child) { + MtrNode *auxnode = copy->child; + while (auxnode != NULL) { + auxnode->parent = copy; + auxnode = auxnode->younger; + } + } + return(copy); + +} /* end of Mtr_CopyTree */ + + +/**Function******************************************************************** + + Synopsis [Makes child the first child of parent.] + + Description [] + + SideEffects [None] + + SeeAlso [Mtr_MakeLastChild Mtr_CreateFirstChild] + +******************************************************************************/ +void +Mtr_MakeFirstChild( + MtrNode * parent, + MtrNode * child) +{ + child->parent = parent; + child->younger = parent->child; + child->elder = NULL; + if (parent->child != NULL) { +#ifdef MTR_DEBUG + assert(parent->child->elder == NULL); +#endif + parent->child->elder = child; + } + parent->child = child; + return; + +} /* end of Mtr_MakeFirstChild */ + + +/**Function******************************************************************** + + Synopsis [Makes child the last child of parent.] + + Description [] + + SideEffects [None] + + SeeAlso [Mtr_MakeFirstChild Mtr_CreateLastChild] + +******************************************************************************/ +void +Mtr_MakeLastChild( + MtrNode * parent, + MtrNode * child) +{ + MtrNode *node; + + child->younger = NULL; + + if (parent->child == NULL) { + parent->child = child; + child->elder = NULL; + } else { + for (node = parent->child; + node->younger != NULL; + node = node->younger); + node->younger = child; + child->elder = node; + } + child->parent = parent; + return; + +} /* end of Mtr_MakeLastChild */ + + +/**Function******************************************************************** + + Synopsis [Creates a new node and makes it the first child of parent.] + + Description [Creates a new node and makes it the first child of + parent. Returns pointer to new child.] + + SideEffects [None] + + SeeAlso [Mtr_MakeFirstChild Mtr_CreateLastChild] + +******************************************************************************/ +MtrNode * +Mtr_CreateFirstChild( + MtrNode * parent) +{ + MtrNode *child; + + child = Mtr_AllocNode(); + if (child == NULL) return(NULL); + + child->child = child->younger = child-> elder = NULL; + child->flags = 0; + Mtr_MakeFirstChild(parent,child); + return(child); + +} /* end of Mtr_CreateFirstChild */ + + +/**Function******************************************************************** + + Synopsis [Creates a new node and makes it the last child of parent.] + + Description [Creates a new node and makes it the last child of parent. + Returns pointer to new child.] + + SideEffects [None] + + SeeAlso [Mtr_MakeLastChild Mtr_CreateFirstChild] + +******************************************************************************/ +MtrNode * +Mtr_CreateLastChild( + MtrNode * parent) +{ + MtrNode *child; + + child = Mtr_AllocNode(); + if (child == NULL) return(NULL); + + child->child = child->younger = child->elder = NULL; + child->flags = 0; + Mtr_MakeLastChild(parent,child); + return(child); + +} /* end of Mtr_CreateLastChild */ + + +/**Function******************************************************************** + + Synopsis [Makes second the next sibling of first.] + + Description [Makes second the next sibling of first. Second becomes a + child of the parent of first.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +Mtr_MakeNextSibling( + MtrNode * first, + MtrNode * second) +{ + second->younger = first->younger; + if (first->younger != NULL) { + first->younger->elder = second; + } + second->parent = first->parent; + first->younger = second; + second->elder = first; + return; + +} /* end of Mtr_MakeNextSibling */ + + +/**Function******************************************************************** + + Synopsis [Prints a tree, one node per line.] + + Description [] + + SideEffects [None] + + SeeAlso [Mtr_PrintGroups] + +******************************************************************************/ +void +Mtr_PrintTree( + MtrNode * node) +{ + if (node == NULL) return; + (void) fprintf(stdout, +#if SIZEOF_VOID_P == 8 + "N=0x%-8lx C=0x%-8lx Y=0x%-8lx E=0x%-8lx P=0x%-8lx F=%x L=%d S=%d\n", + (unsigned long) node, (unsigned long) node->child, + (unsigned long) node->younger, (unsigned long) node->elder, + (unsigned long) node->parent, node->flags, node->low, node->size); +#else + "N=0x%-8x C=0x%-8x Y=0x%-8x E=0x%-8x P=0x%-8x F=%x L=%d S=%d\n", + (unsigned) node, (unsigned) node->child, + (unsigned) node->younger, (unsigned) node->elder, + (unsigned) node->parent, node->flags, node->low, node->size); +#endif + if (!MTR_TEST(node,MTR_TERMINAL)) Mtr_PrintTree(node->child); + Mtr_PrintTree(node->younger); + return; + +} /* end of Mtr_PrintTree */ + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + diff --git a/abc_with_bb_support/src/bdd/mtr/mtrGroup.c b/abc_with_bb_support/src/bdd/mtr/mtrGroup.c new file mode 100644 index 000000000..85fe804df --- /dev/null +++ b/abc_with_bb_support/src/bdd/mtr/mtrGroup.c @@ -0,0 +1,690 @@ +/**CFile*********************************************************************** + + FileName [mtrGroup.c] + + PackageName [mtr] + + Synopsis [Functions to support group specification for reordering.] + + Description [External procedures included in this module: +
          +
        • Mtr_InitGroupTree() +
        • Mtr_MakeGroup() +
        • Mtr_DissolveGroup() +
        • Mtr_FindGroup() +
        • Mtr_SwapGroups() +
        • Mtr_PrintGroups() +
        • Mtr_ReadGroups() +
        + Static procedures included in this module: +
          +
        • mtrShiftHL +
        + ] + + SeeAlso [cudd package] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + +******************************************************************************/ + +#include "util_hack.h" +#include "mtrInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] MTR_UNUSED = "$Id: mtrGroup.c,v 1.1.1.1 2003/02/24 22:24:02 wjiang Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int mtrShiftHL ARGS((MtrNode *node, int shift)); + +/**AutomaticEnd***************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Allocate new tree.] + + Description [Allocate new tree with one node, whose low and size + fields are specified by the lower and size parameters. + Returns pointer to tree root.] + + SideEffects [None] + + SeeAlso [Mtr_InitTree Mtr_FreeTree] + +******************************************************************************/ +MtrNode * +Mtr_InitGroupTree( + int lower, + int size) +{ + MtrNode *root; + + root = Mtr_InitTree(); + if (root == NULL) return(NULL); + root->flags = MTR_DEFAULT; + root->low = lower; + root->size = size; + return(root); + +} /* end of Mtr_InitGroupTree */ + + +/**Function******************************************************************** + + Synopsis [Makes a new group with size leaves starting at low.] + + Description [Makes a new group with size leaves starting at low. + If the new group intersects an existing group, it must + either contain it or be contained by it. This procedure relies on + the low and size fields of each node. It also assumes that the + children of each node are sorted in order of increasing low. In + case of a valid request, the flags of the new group are set to the + value passed in `flags.' This can also be used to change the flags + of an existing group. Returns the pointer to the root of the new + group upon successful termination; NULL otherwise. If the group + already exists, the pointer to its root is returned.] + + SideEffects [None] + + SeeAlso [Mtr_DissolveGroup Mtr_ReadGroups Mtr_FindGroup] + +******************************************************************************/ +MtrNode * +Mtr_MakeGroup( + MtrNode * root /* root of the group tree */, + unsigned int low /* lower bound of the group */, + unsigned int size /* upper bound of the group */, + unsigned int flags /* flags for the new group */) +{ + MtrNode *node, + *first, + *last, + *previous, + *newn; + + /* Sanity check. */ + if (size == 0) + return(NULL); + + /* Check whether current group includes new group. This check is + ** necessary at the top-level call. In the subsequent calls it is + ** redundant. */ + if (low < (unsigned int) root->low || + low + size > (unsigned int) (root->low + root->size)) + return(NULL); + + /* Trying to create an existing group has the effect of updating + ** the flags. */ + if (root->size == size && root->low == low) { + root->flags = flags; + return(root); + } + + /* At this point we know that the new group is properly contained + ** in the group of root. We have two possible cases here: - root + ** is a terminal node; - root has children. */ + + /* Root has no children: create a new group. */ + if (root->child == NULL) { + newn = Mtr_AllocNode(); + if (newn == NULL) return(NULL); /* out of memory */ + newn->low = low; + newn->size = size; + newn->flags = flags; + newn->parent = root; + newn->elder = newn->younger = newn->child = NULL; + root->child = newn; + return(newn); + } + + /* Root has children: Find all chidren of root that are included + ** in the new group. If the group of any child entirely contains + ** the new group, call Mtr_MakeGroup recursively. */ + previous = NULL; + first = root->child; /* guaranteed to be non-NULL */ + while (first != NULL && low >= (unsigned int) (first->low + first->size)) { + previous = first; + first = first->younger; + } + if (first == NULL) { + /* We have scanned the entire list and we need to append a new + ** child at the end of it. Previous points to the last child + ** of root. */ + newn = Mtr_AllocNode(); + if (newn == NULL) return(NULL); /* out of memory */ + newn->low = low; + newn->size = size; + newn->flags = flags; + newn->parent = root; + newn->elder = previous; + previous->younger = newn; + newn->younger = newn->child = NULL; + return(newn); + } + /* Here first is non-NULL and low < first->low + first->size. */ + if (low >= (unsigned int) first->low && + low + size <= (unsigned int) (first->low + first->size)) { + /* The new group is contained in the group of first. */ + newn = Mtr_MakeGroup(first, low, size, flags); + return(newn); + } else if (low + size <= first->low) { + /* The new group is entirely contained in the gap between + ** previous and first. */ + newn = Mtr_AllocNode(); + if (newn == NULL) return(NULL); /* out of memory */ + newn->low = low; + newn->size = size; + newn->flags = flags; + newn->child = NULL; + newn->parent = root; + newn->elder = previous; + newn->younger = first; + first->elder = newn; + if (previous != NULL) { + previous->younger = newn; + } else { + root->child = newn; + } + return(newn); + } else if (low < (unsigned int) first->low && + low + size < (unsigned int) (first->low + first->size)) { + /* Trying to cut an existing group: not allowed. */ + return(NULL); + } else if (low > first->low) { + /* The new group neither is contained in the group of first + ** (this was tested above) nor contains it. It is therefore + ** trying to cut an existing group: not allowed. */ + return(NULL); + } + + /* First holds the pointer to the first child contained in the new + ** group. Here low <= first->low and low + size >= first->low + + ** first->size. One of the two inequalities is strict. */ + last = first->younger; + while (last != NULL && + (unsigned int) (last->low + last->size) < low + size) { + last = last->younger; + } + if (last == NULL) { + /* All the chilren of root from first onward become children + ** of the new group. */ + newn = Mtr_AllocNode(); + if (newn == NULL) return(NULL); /* out of memory */ + newn->low = low; + newn->size = size; + newn->flags = flags; + newn->child = first; + newn->parent = root; + newn->elder = previous; + newn->younger = NULL; + first->elder = NULL; + if (previous != NULL) { + previous->younger = newn; + } else { + root->child = newn; + } + last = first; + while (last != NULL) { + last->parent = newn; + last = last->younger; + } + return(newn); + } + + /* Here last != NULL and low + size <= last->low + last->size. */ + if (low + size - 1 >= (unsigned int) last->low && + low + size < (unsigned int) (last->low + last->size)) { + /* Trying to cut an existing group: not allowed. */ + return(NULL); + } + + /* First and last point to the first and last of the children of + ** root that are included in the new group. Allocate a new node + ** and make all children of root between first and last chidren of + ** the new node. Previous points to the child of root immediately + ** preceeding first. If it is NULL, then first is the first child + ** of root. */ + newn = Mtr_AllocNode(); + if (newn == NULL) return(NULL); /* out of memory */ + newn->low = low; + newn->size = size; + newn->flags = flags; + newn->child = first; + newn->parent = root; + if (previous == NULL) { + root->child = newn; + } else { + previous->younger = newn; + } + newn->elder = previous; + newn->younger = last->younger; + if (last->younger != NULL) { + last->younger->elder = newn; + } + last->younger = NULL; + first->elder = NULL; + for (node = first; node != NULL; node = node->younger) { + node->parent = newn; + } + + return(newn); + +} /* end of Mtr_MakeGroup */ + + +/**Function******************************************************************** + + Synopsis [Merges the children of `group' with the children of its + parent.] + + Description [Merges the children of `group' with the children of its + parent. Disposes of the node pointed by group. If group is the + root of the group tree, this procedure leaves the tree unchanged. + Returns the pointer to the parent of `group' upon successful + termination; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Mtr_MakeGroup] + +******************************************************************************/ +MtrNode * +Mtr_DissolveGroup( + MtrNode * group /* group to be dissolved */) +{ + MtrNode *parent; + MtrNode *last; + + parent = group->parent; + + if (parent == NULL) return(NULL); + if (MTR_TEST(group,MTR_TERMINAL) || group->child == NULL) return(NULL); + + /* Make all children of group children of its parent, and make + ** last point to the last child of group. */ + for (last = group->child; last->younger != NULL; last = last->younger) { + last->parent = parent; + } + last->parent = parent; + + last->younger = group->younger; + if (group->younger != NULL) { + group->younger->elder = last; + } + + group->child->elder = group->elder; + if (group == parent->child) { + parent->child = group->child; + } else { + group->elder->younger = group->child; + } + + Mtr_DeallocNode(group); + return(parent); + +} /* end of Mtr_DissolveGroup */ + + +/**Function******************************************************************** + + Synopsis [Finds a group with size leaves starting at low, if it exists.] + + Description [Finds a group with size leaves starting at low, if it + exists. This procedure relies on the low and size fields of each + node. It also assumes that the children of each node are sorted in + order of increasing low. Returns the pointer to the root of the + group upon successful termination; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +MtrNode * +Mtr_FindGroup( + MtrNode * root /* root of the group tree */, + unsigned int low /* lower bound of the group */, + unsigned int size /* upper bound of the group */) +{ + MtrNode *node; + +#ifdef MTR_DEBUG + /* We cannot have a non-empty proper subgroup of a singleton set. */ + assert(!MTR_TEST(root,MTR_TERMINAL)); +#endif + + /* Sanity check. */ + if (size < 1) return(NULL); + + /* Check whether current group includes the group sought. This + ** check is necessary at the top-level call. In the subsequent + ** calls it is redundant. */ + if (low < (unsigned int) root->low || + low + size > (unsigned int) (root->low + root->size)) + return(NULL); + + if (root->size == size && root->low == low) + return(root); + + if (root->child == NULL) + return(NULL); + + /* Find all chidren of root that are included in the new group. If + ** the group of any child entirely contains the new group, call + ** Mtr_MakeGroup recursively. */ + node = root->child; + while (low >= (unsigned int) (node->low + node->size)) { + node = node->younger; + } + if (low + size <= (unsigned int) (node->low + node->size)) { + /* The group is contained in the group of node. */ + node = Mtr_FindGroup(node, low, size); + return(node); + } else { + return(NULL); + } + +} /* end of Mtr_FindGroup */ + + +/**Function******************************************************************** + + Synopsis [Swaps two children of a tree node.] + + Description [Swaps two children of a tree node. Adjusts the high and + low fields of the two nodes and their descendants. The two children + must be adjacent. However, first may be the younger sibling of second. + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +Mtr_SwapGroups( + MtrNode * first /* first node to be swapped */, + MtrNode * second /* second node to be swapped */) +{ + MtrNode *node; + MtrNode *parent; + int sizeFirst; + int sizeSecond; + + if (second->younger == first) { /* make first first */ + node = first; + first = second; + second = node; + } else if (first->younger != second) { /* non-adjacent */ + return(0); + } + + sizeFirst = first->size; + sizeSecond = second->size; + + /* Swap the two nodes. */ + parent = first->parent; + if (parent == NULL || second->parent != parent) return(0); + if (parent->child == first) { + parent->child = second; + } else { /* first->elder != NULL */ + first->elder->younger = second; + } + if (second->younger != NULL) { + second->younger->elder = first; + } + first->younger = second->younger; + second->elder = first->elder; + first->elder = second; + second->younger = first; + + /* Adjust the high and low fields. */ + if (!mtrShiftHL(first,sizeSecond)) return(0); + if (!mtrShiftHL(second,-sizeFirst)) return(0); + + return(1); + +} /* end of Mtr_SwapGroups */ + + +/**Function******************************************************************** + + Synopsis [Prints the groups as a parenthesized list.] + + Description [Prints the groups as a parenthesized list. After each + group, the group's flag are printed, preceded by a `|'. For each + flag (except MTR_TERMINAL) a character is printed. +
          +
        • F: MTR_FIXED +
        • N: MTR_NEWNODE +
        • S: MTR_SOFT +
        + The second argument, silent, if different from 0, causes + Mtr_PrintGroups to only check the syntax of the group tree. + ] + + SideEffects [None] + + SeeAlso [Mtr_PrintTree] + +******************************************************************************/ +void +Mtr_PrintGroups( + MtrNode * root /* root of the group tree */, + int silent /* flag to check tree syntax only */) +{ + MtrNode *node; + + assert(root != NULL); + assert(root->younger == NULL || root->younger->elder == root); + assert(root->elder == NULL || root->elder->younger == root); + if (!silent) (void) printf("(%d",root->low); + if (MTR_TEST(root,MTR_TERMINAL) || root->child == NULL) { + if (!silent) (void) printf(","); + } else { + node = root->child; + while (node != NULL) { + assert(node->low >= root->low && (int) (node->low + node->size) <= (int) (root->low + root->size)); + assert(node->parent == root); + Mtr_PrintGroups(node,silent); + node = node->younger; + } + } + if (!silent) { + (void) printf("%d", root->low + root->size - 1); + if (root->flags != MTR_DEFAULT) { + (void) printf("|"); + if (MTR_TEST(root,MTR_FIXED)) (void) printf("F"); + if (MTR_TEST(root,MTR_NEWNODE)) (void) printf("N"); + if (MTR_TEST(root,MTR_SOFT)) (void) printf("S"); + } + (void) printf(")"); + if (root->parent == NULL) (void) printf("\n"); + } + assert((root->flags &~(MTR_TERMINAL | MTR_SOFT | MTR_FIXED | MTR_NEWNODE)) == 0); + return; + +} /* end of Mtr_PrintGroups */ + + +/**Function******************************************************************** + + Synopsis [Reads groups from a file and creates a group tree.] + + Description [Reads groups from a file and creates a group tree. + Each group is specified by three fields: + + low size flags. + + Low and size are (short) integers. Flags is a string composed of the + following characters (with associated translation): +
          +
        • D: MTR_DEFAULT +
        • F: MTR_FIXED +
        • N: MTR_NEWNODE +
        • S: MTR_SOFT +
        • T: MTR_TERMINAL +
        + Normally, the only flags that are needed are D and F. Groups and + fields are separated by white space (spaces, tabs, and newlines). + Returns a pointer to the group tree if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Mtr_InitGroupTree Mtr_MakeGroup] + +******************************************************************************/ +MtrNode * +Mtr_ReadGroups( + FILE * fp /* file pointer */, + int nleaves /* number of leaves of the new tree */) +{ + int low; + int size; + int err; + unsigned int flags; + MtrNode *root; + MtrNode *node; + char attrib[8*sizeof(unsigned int)+1]; + char *c; + + root = Mtr_InitGroupTree(0,nleaves); + if (root == NULL) return NULL; + + while (! feof(fp)) { + /* Read a triple and check for consistency. */ + err = fscanf(fp, "%d %d %s", &low, &size, attrib); + if (err == EOF) { + break; + } else if (err != 3) { + return(NULL); + } else if (low < 0 || low+size > nleaves || size < 1) { + return(NULL); + } else if (strlen(attrib) > 8 * sizeof(MtrHalfWord)) { + /* Not enough bits in the flags word to store these many + ** attributes. */ + return(NULL); + } + + /* Parse the flag string. Currently all flags are permitted, + ** to make debugging easier. Normally, specifying NEWNODE + ** wouldn't be allowed. */ + flags = MTR_DEFAULT; + for (c=attrib; *c != 0; c++) { + switch (*c) { + case 'D': + break; + case 'F': + flags |= MTR_FIXED; + break; + case 'N': + flags |= MTR_NEWNODE; + break; + case 'S': + flags |= MTR_SOFT; + break; + case 'T': + flags |= MTR_TERMINAL; + break; + default: + return NULL; + } + } + node = Mtr_MakeGroup(root, (MtrHalfWord) low, (MtrHalfWord) size, + flags); + if (node == NULL) return(NULL); + } + + return(root); + +} /* end of Mtr_ReadGroups */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Adjusts the low fields of a node and its descendants.] + + Description [Adjusts the low fields of a node and its + descendants. Adds shift to low of each node. Checks that no + out-of-bounds values result. Returns 1 in case of success; 0 + otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +mtrShiftHL( + MtrNode * node /* group tree node */, + int shift /* amount by which low should be changed */) +{ + MtrNode *auxnode; + int low; + + low = (int) node->low; + + + low += shift; + + if (low < 0 || low + (int) (node->size - 1) > (int) MTR_MAXHIGH) return(0); + + node->low = (MtrHalfWord) low; + + if (!MTR_TEST(node,MTR_TERMINAL) && node->child != NULL) { + auxnode = node->child; + do { + if (!mtrShiftHL(auxnode,shift)) return(0); + auxnode = auxnode->younger; + } while (auxnode != NULL); + } + + return(1); + +} /* end of mtrShiftHL */ + diff --git a/abc_with_bb_support/src/bdd/mtr/mtrInt.h b/abc_with_bb_support/src/bdd/mtr/mtrInt.h new file mode 100644 index 000000000..269689df0 --- /dev/null +++ b/abc_with_bb_support/src/bdd/mtr/mtrInt.h @@ -0,0 +1,65 @@ +/**CHeaderFile***************************************************************** + + FileName [mtrInt.h] + + PackageName [mtr] + + Synopsis [Internal data structures of the mtr package] + + Description [In this package all definitions are external.] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [This file was created at the University of Colorado at + Boulder. The University of Colorado at Boulder makes no warranty + about the suitability of this software for any purpose. It is + presented on an AS IS basis.] + + Revision [$Id: mtrInt.h,v 1.1.1.1 2003/02/24 22:24:02 wjiang Exp $] + +******************************************************************************/ + +#ifndef _MTRINT +#define _MTRINT + +#include "mtr.h" + +/*---------------------------------------------------------------------------*/ +/* Nested includes */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Function prototypes */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticEnd***************************************************************/ + +#endif /* _MTRINT */ diff --git a/abc_with_bb_support/src/bdd/parse/module.make b/abc_with_bb_support/src/bdd/parse/module.make new file mode 100644 index 000000000..825775925 --- /dev/null +++ b/abc_with_bb_support/src/bdd/parse/module.make @@ -0,0 +1,3 @@ +SRC += src/bdd/parse/parseCore.c \ + src/bdd/parse/parseEqn.c \ + src/bdd/parse/parseStack.c diff --git a/abc_with_bb_support/src/bdd/parse/parse.h b/abc_with_bb_support/src/bdd/parse/parse.h new file mode 100644 index 000000000..d1e53cb5c --- /dev/null +++ b/abc_with_bb_support/src/bdd/parse/parse.h @@ -0,0 +1,54 @@ +/**CFile**************************************************************** + + FileName [parse.h] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Parsing symbolic Boolean formulas into BDDs.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 8, 2003.] + + Revision [$Id: parse.h,v 1.0 2003/09/08 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __PARSE_H__ +#define __PARSE_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// GLOBAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== parseCore.c =============================================================*/ +extern DdNode * Parse_FormulaParser( FILE * pOutput, char * pFormula, int nVars, int nRanks, + char * ppVarNames[], DdManager * dd, DdNode * pbVars[] ); + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/bdd/parse/parseCore.c b/abc_with_bb_support/src/bdd/parse/parseCore.c new file mode 100644 index 000000000..a0bb150a2 --- /dev/null +++ b/abc_with_bb_support/src/bdd/parse/parseCore.c @@ -0,0 +1,504 @@ +/**CFile**************************************************************** + + FileNameIn [parseCore.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Boolean formula parser.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: parseCore.c,v 1.0 2003/02/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +/* + Some aspects of Boolean Formula Parser: + + 1) The names in the boolean formulas can be any strings of symbols + that start with char or underscore and contain chars, digits + and underscores: For example: 1) a&b <+> c'&d => a + b; + 2) a1 b2 c3' dummy' + (a2+b2')c3 dummy + 2) Constant values 0 and 1 can be used just like normal variables + 3) Any boolean operator (listed below) and parantheses can be used + any number of times provided there are equal number of opening + and closing parantheses. + 4) By default, absence of an operator between vars and before and + after parantheses is taken for AND. + 5) Both complementation prefix and complementation suffix can be + used at the same time (but who needs this?) + 6) Spaces (tabs, end-of-lines) may be inserted anywhere, + except between characters of the operations: <=>, =>, <=, <+> + 7) The stack size is defined by macro STACKSIZE and is used by the + stack constructor. +*/ + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#include "parseInt.h" + +// the list of operation symbols to be used in expressions +#define PARSE_SYM_OPEN '(' // opening paranthesis +#define PARSE_SYM_CLOSE ')' // closing paranthesis +#define PARSE_SYM_LOWER '[' // shifts one rank down +#define PARSE_SYM_RAISE ']' // shifts one rank up +#define PARSE_SYM_CONST0 '0' // constant 0 +#define PARSE_SYM_CONST1 '1' // constant 1 +#define PARSE_SYM_NEGBEF1 '!' // negation before the variable +#define PARSE_SYM_NEGBEF2 '~' // negation before the variable +#define PARSE_SYM_NEGAFT '\'' // negation after the variable +#define PARSE_SYM_AND1 '&' // logic AND +#define PARSE_SYM_AND2 '*' // logic AND +#define PARSE_SYM_XOR1 '<' // logic EXOR (the 1st symbol) +#define PARSE_SYM_XOR2 '+' // logic EXOR (the 2nd symbol) +#define PARSE_SYM_XOR3 '>' // logic EXOR (the 3rd symbol) +#define PARSE_SYM_OR '+' // logic OR +#define PARSE_SYM_EQU1 '<' // equvalence (the 1st symbol) +#define PARSE_SYM_EQU2 '=' // equvalence (the 2nd symbol) +#define PARSE_SYM_EQU3 '>' // equvalence (the 3rd symbol) +#define PARSE_SYM_FLR1 '=' // implication (the 1st symbol) +#define PARSE_SYM_FLR2 '>' // implication (the 2nd symbol) +#define PARSE_SYM_FLL1 '<' // backward imp (the 1st symbol) +#define PARSE_SYM_FLL2 '=' // backward imp (the 2nd symbol) +// PARSE_SYM_FLR1 and PARSE_SYM_FLR2 should be the same as PARSE_SYM_EQU2 and PARSE_SYM_EQU3! + +// the list of opcodes (also specifying operation precedence) +#define PARSE_OPER_NEG 10 // negation +#define PARSE_OPER_AND 9 // logic AND +#define PARSE_OPER_XOR 8 // logic EXOR (a'b | ab') +#define PARSE_OPER_OR 7 // logic OR +#define PARSE_OPER_EQU 6 // equvalence (a'b'| ab ) +#define PARSE_OPER_FLR 5 // implication ( a' | b ) +#define PARSE_OPER_FLL 4 // backward imp ( 'b | a ) +#define PARSE_OPER_MARK 1 // OpStack token standing for an opening paranthesis + +// these are values of the internal Flag +#define PARSE_FLAG_START 1 // after the opening parenthesis +#define PARSE_FLAG_VAR 2 // after operation is received +#define PARSE_FLAG_OPER 3 // after operation symbol is received +#define PARSE_FLAG_ERROR 4 // when error is detected + +#define STACKSIZE 1000 + +static DdNode * Parse_ParserPerformTopOp( DdManager * dd, Parse_StackFn_t * pStackFn, int Oper ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Derives the BDD corresponding to the formula in language L.] + + Description [Takes the stream to output messages, the formula, the number + variables and the rank in the formula. The array of variable names is also + given. The BDD manager and the elementary 0-rank variable are the last two + arguments. The manager should have at least as many variables as + nVars * (nRanks + 1). The 0-rank variables should have numbers larger + than the variables of other ranks.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Parse_FormulaParser( FILE * pOutput, char * pFormulaInit, int nVars, int nRanks, + char * ppVarNames[], DdManager * dd, DdNode * pbVars[] ) +{ + char * pFormula; + Parse_StackFn_t * pStackFn; + Parse_StackOp_t * pStackOp; + DdNode * bFunc, * bTemp; + char * pTemp; + int nParans, fFound, Flag; + int Oper, Oper1, Oper2; + int i, v, fLower; + + // make sure that the number of vars and ranks is correct + if ( nVars * (nRanks + 1) > dd->size ) + { + printf( "Parse_FormulaParser(): The BDD manager does not have enough variables.\n" ); + return NULL; + } + + // make sure that the number of opening and closing parantheses is the same + nParans = 0; + for ( pTemp = pFormulaInit; *pTemp; pTemp++ ) + if ( *pTemp == '(' ) + nParans++; + else if ( *pTemp == ')' ) + nParans--; + if ( nParans != 0 ) + { + fprintf( pOutput, "Parse_FormulaParser(): Different number of opening and closing parantheses ().\n" ); + return NULL; + } + + nParans = 0; + for ( pTemp = pFormulaInit; *pTemp; pTemp++ ) + if ( *pTemp == '[' ) + nParans++; + else if ( *pTemp == ']' ) + nParans--; + if ( nParans != 0 ) + { + fprintf( pOutput, "Parse_FormulaParser(): Different number of opening and closing brackets [].\n" ); + return NULL; + } + + // copy the formula + pFormula = ALLOC( char, strlen(pFormulaInit) + 3 ); + sprintf( pFormula, "(%s)", pFormulaInit ); + + // start the stacks + pStackFn = Parse_StackFnStart( STACKSIZE ); + pStackOp = Parse_StackOpStart( STACKSIZE ); + + Flag = PARSE_FLAG_START; + fLower = 0; + for ( pTemp = pFormula; *pTemp; pTemp++ ) + { + switch ( *pTemp ) + { + // skip all spaces, tabs, and end-of-lines + case ' ': + case '\t': + case '\r': + case '\n': + continue; + + // treat Constant 0 as a variable + case PARSE_SYM_CONST0: + Parse_StackFnPush( pStackFn, b0 ); Cudd_Ref( b0 ); + if ( Flag == PARSE_FLAG_VAR ) + { + fprintf( pOutput, "Parse_FormulaParser(): No operation symbol before constant 0.\n" ); + Flag = PARSE_FLAG_ERROR; + break; + } + Flag = PARSE_FLAG_VAR; + break; + + // the same for Constant 1 + case PARSE_SYM_CONST1: + Parse_StackFnPush( pStackFn, b1 ); Cudd_Ref( b1 ); + if ( Flag == PARSE_FLAG_VAR ) + { + fprintf( pOutput, "Parse_FormulaParser(): No operation symbol before constant 1.\n" ); + Flag = PARSE_FLAG_ERROR; + break; + } + Flag = PARSE_FLAG_VAR; + break; + + case PARSE_SYM_NEGBEF1: + case PARSE_SYM_NEGBEF2: + if ( Flag == PARSE_FLAG_VAR ) + {// if NEGBEF follows a variable, AND is assumed + Parse_StackOpPush( pStackOp, PARSE_OPER_AND ); + Flag = PARSE_FLAG_OPER; + } + Parse_StackOpPush( pStackOp, PARSE_OPER_NEG ); + break; + + case PARSE_SYM_NEGAFT: + if ( Flag != PARSE_FLAG_VAR ) + {// if there is no variable before NEGAFT, it is an error + fprintf( pOutput, "Parse_FormulaParser(): No variable is specified before the negation suffix.\n" ); + Flag = PARSE_FLAG_ERROR; + break; + } + else // if ( Flag == PARSE_FLAG_VAR ) + Parse_StackFnPush( pStackFn, Cudd_Not( Parse_StackFnPop(pStackFn) ) ); + break; + + case PARSE_SYM_AND1: + case PARSE_SYM_AND2: + case PARSE_SYM_OR: + if ( Flag != PARSE_FLAG_VAR ) + { + fprintf( pOutput, "Parse_FormulaParser(): There is no variable before AND, EXOR, or OR.\n" ); + Flag = PARSE_FLAG_ERROR; + break; + } + if ( *pTemp == PARSE_SYM_AND1 || *pTemp == PARSE_SYM_AND2 ) + Parse_StackOpPush( pStackOp, PARSE_OPER_AND ); + else //if ( Str[Pos] == PARSE_SYM_OR ) + Parse_StackOpPush( pStackOp, PARSE_OPER_OR ); + Flag = PARSE_FLAG_OPER; + break; + + case PARSE_SYM_EQU1: + if ( Flag != PARSE_FLAG_VAR ) + { + fprintf( pOutput, "Parse_FormulaParser(): There is no variable before Equivalence or Implication\n" ); + Flag = PARSE_FLAG_ERROR; break; + } + if ( pTemp[1] == PARSE_SYM_EQU2 ) + { // check what is the next symbol in the string + pTemp++; + if ( pTemp[1] == PARSE_SYM_EQU3 ) + { + pTemp++; + Parse_StackOpPush( pStackOp, PARSE_OPER_EQU ); + } + else + { + Parse_StackOpPush( pStackOp, PARSE_OPER_FLL ); + } + } + else if ( pTemp[1] == PARSE_SYM_XOR2 ) + { + pTemp++; + if ( pTemp[1] == PARSE_SYM_XOR3 ) + { + pTemp++; + Parse_StackOpPush( pStackOp, PARSE_OPER_XOR ); + } + else + { + fprintf( pOutput, "Parse_FormulaParser(): Wrong symbol after \"%c%c\"\n", PARSE_SYM_EQU1, PARSE_SYM_XOR2 ); + Flag = PARSE_FLAG_ERROR; + break; + } + } + else + { + fprintf( pOutput, "Parse_FormulaParser(): Wrong symbol after \"%c\"\n", PARSE_SYM_EQU1 ); + Flag = PARSE_FLAG_ERROR; + break; + } + Flag = PARSE_FLAG_OPER; + break; + + case PARSE_SYM_EQU2: + if ( Flag != PARSE_FLAG_VAR ) + { + fprintf( pOutput, "Parse_FormulaParser(): There is no variable before Reverse Implication\n" ); + Flag = PARSE_FLAG_ERROR; + break; + } + if ( pTemp[1] == PARSE_SYM_EQU3 ) + { + pTemp++; + Parse_StackOpPush( pStackOp, PARSE_OPER_FLR ); + } + else + { + fprintf( pOutput, "Parse_FormulaParser(): Wrong symbol after \"%c\"\n", PARSE_SYM_EQU2 ); + Flag = PARSE_FLAG_ERROR; + break; + } + Flag = PARSE_FLAG_OPER; + break; + + case PARSE_SYM_LOWER: + case PARSE_SYM_OPEN: + if ( Flag == PARSE_FLAG_VAR ) + Parse_StackOpPush( pStackOp, PARSE_OPER_AND ); + Parse_StackOpPush( pStackOp, PARSE_OPER_MARK ); + // after an opening bracket, it feels like starting over again + Flag = PARSE_FLAG_START; + break; + + case PARSE_SYM_RAISE: + fLower = 1; + case PARSE_SYM_CLOSE: + if ( !Parse_StackOpIsEmpty( pStackOp ) ) + { + while ( 1 ) + { + if ( Parse_StackOpIsEmpty( pStackOp ) ) + { + fprintf( pOutput, "Parse_FormulaParser(): There is no opening paranthesis\n" ); + Flag = PARSE_FLAG_ERROR; + break; + } + Oper = Parse_StackOpPop( pStackOp ); + if ( Oper == PARSE_OPER_MARK ) + break; + + // perform the given operation + if ( Parse_ParserPerformTopOp( dd, pStackFn, Oper ) == NULL ) + { + fprintf( pOutput, "Parse_FormulaParser(): Unknown operation\n" ); + free( pFormula ); + return NULL; + } + } + + if ( fLower ) + { + bFunc = Parse_StackFnPop( pStackFn ); + bFunc = Extra_bddMove( dd, bTemp = bFunc, -nVars ); Cudd_Ref( bFunc ); + Cudd_RecursiveDeref( dd, bTemp ); + Parse_StackFnPush( pStackFn, bFunc ); + } + } + else + { + fprintf( pOutput, "Parse_FormulaParser(): There is no opening paranthesis\n" ); + Flag = PARSE_FLAG_ERROR; + break; + } + if ( Flag != PARSE_FLAG_ERROR ) + Flag = PARSE_FLAG_VAR; + fLower = 0; + break; + + + default: + // scan the next name + fFound = 0; + for ( i = 0; pTemp[i] && pTemp[i] != ' ' && pTemp[i] != '\t' && pTemp[i] != '\r' && pTemp[i] != '\n'; i++ ) + { + for ( v = 0; v < nVars; v++ ) + if ( strncmp( pTemp, ppVarNames[v], i+1 ) == 0 && strlen(ppVarNames[v]) == (unsigned)(i+1) ) + { + pTemp += i; + fFound = 1; + break; + } + if ( fFound ) + break; + } + if ( !fFound ) + { + fprintf( pOutput, "Parse_FormulaParser(): The parser cannot find var \"%s\" in the input var list.\n", pTemp ); + Flag = PARSE_FLAG_ERROR; + break; + } + + // assume operation AND, if vars follow one another + if ( Flag == PARSE_FLAG_VAR ) + Parse_StackOpPush( pStackOp, PARSE_OPER_AND ); + Parse_StackFnPush( pStackFn, pbVars[v] ); Cudd_Ref( pbVars[v] ); + Flag = PARSE_FLAG_VAR; + break; + } + + if ( Flag == PARSE_FLAG_ERROR ) + break; // error exit + else if ( Flag == PARSE_FLAG_START ) + continue; // go on parsing + else if ( Flag == PARSE_FLAG_VAR ) + while ( 1 ) + { // check if there are negations in the OpStack + if ( Parse_StackOpIsEmpty(pStackOp) ) + break; + Oper = Parse_StackOpPop( pStackOp ); + if ( Oper != PARSE_OPER_NEG ) + { + Parse_StackOpPush( pStackOp, Oper ); + break; + } + else + { + Parse_StackFnPush( pStackFn, Cudd_Not(Parse_StackFnPop(pStackFn)) ); + } + } + else // if ( Flag == PARSE_FLAG_OPER ) + while ( 1 ) + { // execute all the operations in the OpStack + // with precedence higher or equal than the last one + Oper1 = Parse_StackOpPop( pStackOp ); // the last operation + if ( Parse_StackOpIsEmpty(pStackOp) ) + { // if it is the only operation, push it back + Parse_StackOpPush( pStackOp, Oper1 ); + break; + } + Oper2 = Parse_StackOpPop( pStackOp ); // the operation before the last one + if ( Oper2 >= Oper1 ) + { // if Oper2 precedence is higher or equal, execute it +// Parse_StackPush( pStackFn, Operation( FunStack.Pop(), FunStack.Pop(), Oper2 ) ); + if ( Parse_ParserPerformTopOp( dd, pStackFn, Oper2 ) == NULL ) + { + fprintf( pOutput, "Parse_FormulaParser(): Unknown operation\n" ); + free( pFormula ); + return NULL; + } + Parse_StackOpPush( pStackOp, Oper1 ); // push the last operation back + } + else + { // if Oper2 precedence is lower, push them back and done + Parse_StackOpPush( pStackOp, Oper2 ); + Parse_StackOpPush( pStackOp, Oper1 ); + break; + } + } + } + + if ( Flag != PARSE_FLAG_ERROR ) + { + if ( !Parse_StackFnIsEmpty(pStackFn) ) + { + bFunc = Parse_StackFnPop(pStackFn); + if ( Parse_StackFnIsEmpty(pStackFn) ) + if ( Parse_StackOpIsEmpty(pStackOp) ) + { + Parse_StackFnFree(pStackFn); + Parse_StackOpFree(pStackOp); + Cudd_Deref( bFunc ); + free( pFormula ); + return bFunc; + } + else + fprintf( pOutput, "Parse_FormulaParser(): Something is left in the operation stack\n" ); + else + fprintf( pOutput, "Parse_FormulaParser(): Something is left in the function stack\n" ); + } + else + fprintf( pOutput, "Parse_FormulaParser(): The input string is empty\n" ); + } + free( pFormula ); + return NULL; +} + +/**Function************************************************************* + + Synopsis [Performs the operation on the top entries in the stack.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Parse_ParserPerformTopOp( DdManager * dd, Parse_StackFn_t * pStackFn, int Oper ) +{ + DdNode * bArg1, * bArg2, * bFunc; + // perform the given operation + bArg2 = Parse_StackFnPop( pStackFn ); + bArg1 = Parse_StackFnPop( pStackFn ); + if ( Oper == PARSE_OPER_AND ) + bFunc = Cudd_bddAnd( dd, bArg1, bArg2 ); + else if ( Oper == PARSE_OPER_XOR ) + bFunc = Cudd_bddXor( dd, bArg1, bArg2 ); + else if ( Oper == PARSE_OPER_OR ) + bFunc = Cudd_bddOr( dd, bArg1, bArg2 ); + else if ( Oper == PARSE_OPER_EQU ) + bFunc = Cudd_bddXnor( dd, bArg1, bArg2 ); + else if ( Oper == PARSE_OPER_FLR ) + bFunc = Cudd_bddOr( dd, Cudd_Not(bArg1), bArg2 ); + else if ( Oper == PARSE_OPER_FLL ) + bFunc = Cudd_bddOr( dd, Cudd_Not(bArg2), bArg1 ); + else + return NULL; + Cudd_Ref( bFunc ); + Cudd_RecursiveDeref( dd, bArg1 ); + Cudd_RecursiveDeref( dd, bArg2 ); + Parse_StackFnPush( pStackFn, bFunc ); + return bFunc; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/bdd/parse/parseEqn.c b/abc_with_bb_support/src/bdd/parse/parseEqn.c new file mode 100644 index 000000000..837d7be71 --- /dev/null +++ b/abc_with_bb_support/src/bdd/parse/parseEqn.c @@ -0,0 +1,349 @@ +/**CFile**************************************************************** + + FileNameIn [parseEqn.c] + + PackageName [ABC: Logic synthesis and verification system.] + + Synopsis [Boolean formula parser.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - December 18, 2006.] + + Revision [$Id: parseEqn.c,v 1.0 2006/12/18 00:00:00 alanmi Exp $] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#include "parseInt.h" +#include "vec.h" +#include "hop.h" + +// the list of operation symbols to be used in expressions +#define PARSE_EQN_SYM_OPEN '(' // opening paranthesis +#define PARSE_EQN_SYM_CLOSE ')' // closing paranthesis +#define PARSE_EQN_SYM_CONST0 '0' // constant 0 +#define PARSE_EQN_SYM_CONST1 '1' // constant 1 +#define PARSE_EQN_SYM_NEG '!' // negation before the variable +#define PARSE_EQN_SYM_AND '*' // logic AND +#define PARSE_EQN_SYM_OR '+' // logic OR + +// the list of opcodes (also specifying operation precedence) +#define PARSE_EQN_OPER_NEG 10 // negation +#define PARSE_EQN_OPER_AND 9 // logic AND +#define PARSE_EQN_OPER_OR 7 // logic OR +#define PARSE_EQN_OPER_MARK 1 // OpStack token standing for an opening paranthesis + +// these are values of the internal Flag +#define PARSE_EQN_FLAG_START 1 // after the opening parenthesis +#define PARSE_EQN_FLAG_VAR 2 // after operation is received +#define PARSE_EQN_FLAG_OPER 3 // after operation symbol is received +#define PARSE_EQN_FLAG_ERROR 4 // when error is detected + +#define PARSE_EQN_STACKSIZE 1000 + +static Hop_Obj_t * Parse_ParserPerformTopOp( Hop_Man_t * pMan, Parse_StackFn_t * pStackFn, int Oper ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Derives the AIG corresponding to the equation.] + + Description [Takes the stream to output messages, the formula, the vector + of variable names and the AIG manager.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Parse_FormulaParserEqn( FILE * pOutput, char * pFormInit, Vec_Ptr_t * vVarNames, Hop_Man_t * pMan ) +{ + char * pFormula; + Parse_StackFn_t * pStackFn; + Parse_StackOp_t * pStackOp; + Hop_Obj_t * gFunc; + char * pTemp, * pName; + int nParans, fFound, Flag; + int Oper, Oper1, Oper2; + int i, v; + + // make sure that the number of opening and closing parantheses is the same + nParans = 0; + for ( pTemp = pFormInit; *pTemp; pTemp++ ) + if ( *pTemp == '(' ) + nParans++; + else if ( *pTemp == ')' ) + nParans--; + if ( nParans != 0 ) + { + fprintf( pOutput, "Parse_FormulaParserEqn(): Different number of opening and closing parantheses ().\n" ); + return NULL; + } + + // copy the formula + pFormula = ALLOC( char, strlen(pFormInit) + 3 ); + sprintf( pFormula, "(%s)", pFormInit ); + + // start the stacks + pStackFn = Parse_StackFnStart( PARSE_EQN_STACKSIZE ); + pStackOp = Parse_StackOpStart( PARSE_EQN_STACKSIZE ); + + Flag = PARSE_EQN_FLAG_START; + for ( pTemp = pFormula; *pTemp; pTemp++ ) + { + switch ( *pTemp ) + { + // skip all spaces, tabs, and end-of-lines + case ' ': + case '\t': + case '\r': + case '\n': + continue; + case PARSE_EQN_SYM_CONST0: + Parse_StackFnPush( pStackFn, Hop_ManConst0(pMan) ); // Cudd_Ref( b0 ); + if ( Flag == PARSE_EQN_FLAG_VAR ) + { + fprintf( pOutput, "Parse_FormulaParserEqn(): No operation symbol before constant 0.\n" ); + Flag = PARSE_EQN_FLAG_ERROR; + break; + } + Flag = PARSE_EQN_FLAG_VAR; + break; + case PARSE_EQN_SYM_CONST1: + Parse_StackFnPush( pStackFn, Hop_ManConst1(pMan) ); // Cudd_Ref( b1 ); + if ( Flag == PARSE_EQN_FLAG_VAR ) + { + fprintf( pOutput, "Parse_FormulaParserEqn(): No operation symbol before constant 1.\n" ); + Flag = PARSE_EQN_FLAG_ERROR; + break; + } + Flag = PARSE_EQN_FLAG_VAR; + break; + case PARSE_EQN_SYM_NEG: + if ( Flag == PARSE_EQN_FLAG_VAR ) + {// if NEGBEF follows a variable, AND is assumed + Parse_StackOpPush( pStackOp, PARSE_EQN_OPER_AND ); + Flag = PARSE_EQN_FLAG_OPER; + } + Parse_StackOpPush( pStackOp, PARSE_EQN_OPER_NEG ); + break; + case PARSE_EQN_SYM_AND: + case PARSE_EQN_SYM_OR: + if ( Flag != PARSE_EQN_FLAG_VAR ) + { + fprintf( pOutput, "Parse_FormulaParserEqn(): There is no variable before AND, EXOR, or OR.\n" ); + Flag = PARSE_EQN_FLAG_ERROR; + break; + } + if ( *pTemp == PARSE_EQN_SYM_AND ) + Parse_StackOpPush( pStackOp, PARSE_EQN_OPER_AND ); + else //if ( *pTemp == PARSE_EQN_SYM_OR ) + Parse_StackOpPush( pStackOp, PARSE_EQN_OPER_OR ); + Flag = PARSE_EQN_FLAG_OPER; + break; + case PARSE_EQN_SYM_OPEN: + if ( Flag == PARSE_EQN_FLAG_VAR ) + { +// Parse_StackOpPush( pStackOp, PARSE_EQN_OPER_AND ); + fprintf( pOutput, "Parse_FormulaParserEqn(): An opening paranthesis follows a var without operation sign.\n" ); + Flag = PARSE_EQN_FLAG_ERROR; + break; + } + Parse_StackOpPush( pStackOp, PARSE_EQN_OPER_MARK ); + // after an opening bracket, it feels like starting over again + Flag = PARSE_EQN_FLAG_START; + break; + case PARSE_EQN_SYM_CLOSE: + if ( !Parse_StackOpIsEmpty( pStackOp ) ) + { + while ( 1 ) + { + if ( Parse_StackOpIsEmpty( pStackOp ) ) + { + fprintf( pOutput, "Parse_FormulaParserEqn(): There is no opening paranthesis\n" ); + Flag = PARSE_EQN_FLAG_ERROR; + break; + } + Oper = Parse_StackOpPop( pStackOp ); + if ( Oper == PARSE_EQN_OPER_MARK ) + break; + + // perform the given operation + if ( Parse_ParserPerformTopOp( pMan, pStackFn, Oper ) == NULL ) + { + fprintf( pOutput, "Parse_FormulaParserEqn(): Unknown operation\n" ); + free( pFormula ); + return NULL; + } + } + } + else + { + fprintf( pOutput, "Parse_FormulaParserEqn(): There is no opening paranthesis\n" ); + Flag = PARSE_EQN_FLAG_ERROR; + break; + } + if ( Flag != PARSE_EQN_FLAG_ERROR ) + Flag = PARSE_EQN_FLAG_VAR; + break; + + + default: + // scan the next name + for ( i = 0; pTemp[i] && + pTemp[i] != ' ' && pTemp[i] != '\t' && pTemp[i] != '\r' && pTemp[i] != '\n' && + pTemp[i] != PARSE_EQN_SYM_AND && pTemp[i] != PARSE_EQN_SYM_OR && pTemp[i] != PARSE_EQN_SYM_CLOSE; i++ ) + { + if ( pTemp[i] == PARSE_EQN_SYM_NEG || pTemp[i] == PARSE_EQN_SYM_OPEN ) + { + fprintf( pOutput, "Parse_FormulaParserEqn(): The negation sign or an opening paranthesis inside the variable name.\n" ); + Flag = PARSE_EQN_FLAG_ERROR; + break; + } + } + // variable name is found + fFound = 0; + Vec_PtrForEachEntry( vVarNames, pName, v ) + if ( strncmp(pTemp, pName, i) == 0 && strlen(pName) == (unsigned)i ) + { + pTemp += i-1; + fFound = 1; + break; + } + if ( !fFound ) + { + fprintf( pOutput, "Parse_FormulaParserEqn(): The parser cannot find var \"%s\" in the input var list.\n", pTemp ); + Flag = PARSE_EQN_FLAG_ERROR; + break; + } + if ( Flag == PARSE_EQN_FLAG_VAR ) + { + fprintf( pOutput, "Parse_FormulaParserEqn(): The variable name \"%s\" follows another var without operation sign.\n", pTemp ); + Flag = PARSE_EQN_FLAG_ERROR; + break; + } + Parse_StackFnPush( pStackFn, Hop_IthVar( pMan, v ) ); // Cudd_Ref( pbVars[v] ); + Flag = PARSE_EQN_FLAG_VAR; + break; + } + + if ( Flag == PARSE_EQN_FLAG_ERROR ) + break; // error exit + else if ( Flag == PARSE_EQN_FLAG_START ) + continue; // go on parsing + else if ( Flag == PARSE_EQN_FLAG_VAR ) + while ( 1 ) + { // check if there are negations in the OpStack + if ( Parse_StackOpIsEmpty(pStackOp) ) + break; + Oper = Parse_StackOpPop( pStackOp ); + if ( Oper != PARSE_EQN_OPER_NEG ) + { + Parse_StackOpPush( pStackOp, Oper ); + break; + } + else + { + Parse_StackFnPush( pStackFn, Hop_Not(Parse_StackFnPop(pStackFn)) ); + } + } + else // if ( Flag == PARSE_EQN_FLAG_OPER ) + while ( 1 ) + { // execute all the operations in the OpStack + // with precedence higher or equal than the last one + Oper1 = Parse_StackOpPop( pStackOp ); // the last operation + if ( Parse_StackOpIsEmpty(pStackOp) ) + { // if it is the only operation, push it back + Parse_StackOpPush( pStackOp, Oper1 ); + break; + } + Oper2 = Parse_StackOpPop( pStackOp ); // the operation before the last one + if ( Oper2 >= Oper1 ) + { // if Oper2 precedence is higher or equal, execute it + if ( Parse_ParserPerformTopOp( pMan, pStackFn, Oper2 ) == NULL ) + { + fprintf( pOutput, "Parse_FormulaParserEqn(): Unknown operation\n" ); + free( pFormula ); + return NULL; + } + Parse_StackOpPush( pStackOp, Oper1 ); // push the last operation back + } + else + { // if Oper2 precedence is lower, push them back and done + Parse_StackOpPush( pStackOp, Oper2 ); + Parse_StackOpPush( pStackOp, Oper1 ); + break; + } + } + } + + if ( Flag != PARSE_EQN_FLAG_ERROR ) + { + if ( !Parse_StackFnIsEmpty(pStackFn) ) + { + gFunc = Parse_StackFnPop(pStackFn); + if ( Parse_StackFnIsEmpty(pStackFn) ) + if ( Parse_StackOpIsEmpty(pStackOp) ) + { + Parse_StackFnFree(pStackFn); + Parse_StackOpFree(pStackOp); +// Cudd_Deref( gFunc ); + free( pFormula ); + return gFunc; + } + else + fprintf( pOutput, "Parse_FormulaParserEqn(): Something is left in the operation stack\n" ); + else + fprintf( pOutput, "Parse_FormulaParserEqn(): Something is left in the function stack\n" ); + } + else + fprintf( pOutput, "Parse_FormulaParserEqn(): The input string is empty\n" ); + } + free( pFormula ); + return NULL; +} + +/**Function************************************************************* + + Synopsis [Performs the operation on the top entries in the stack.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Parse_ParserPerformTopOp( Hop_Man_t * pMan, Parse_StackFn_t * pStackFn, int Oper ) +{ + Hop_Obj_t * gArg1, * gArg2, * gFunc; + // perform the given operation + gArg2 = Parse_StackFnPop( pStackFn ); + gArg1 = Parse_StackFnPop( pStackFn ); + if ( Oper == PARSE_EQN_OPER_AND ) + gFunc = Hop_And( pMan, gArg1, gArg2 ); + else if ( Oper == PARSE_EQN_OPER_OR ) + gFunc = Hop_Or( pMan, gArg1, gArg2 ); + else + return NULL; +// Cudd_Ref( gFunc ); +// Cudd_RecursiveDeref( dd, gArg1 ); +// Cudd_RecursiveDeref( dd, gArg2 ); + Parse_StackFnPush( pStackFn, gFunc ); + return gFunc; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/bdd/parse/parseInt.h b/abc_with_bb_support/src/bdd/parse/parseInt.h new file mode 100644 index 000000000..8c8a42066 --- /dev/null +++ b/abc_with_bb_support/src/bdd/parse/parseInt.h @@ -0,0 +1,74 @@ +/**CFile**************************************************************** + + FileName [parseInt.h] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Parsing symbolic Boolean formulas into BDDs.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 8, 2003.] + + Revision [$Id: parseInt.h,v 1.0 2003/09/08 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __PARSE_INT_H__ +#define __PARSE_INT_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + + +#include +#include "cuddInt.h" +#include "extra.h" +#include "parse.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef int bool; + +typedef struct ParseStackFnStruct Parse_StackFn_t; // the function stack +typedef struct ParseStackOpStruct Parse_StackOp_t; // the operation stack + +//////////////////////////////////////////////////////////////////////// +/// GLOBAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== parseStack.c =============================================================*/ +extern Parse_StackFn_t * Parse_StackFnStart ( int nDepth ); +extern bool Parse_StackFnIsEmpty( Parse_StackFn_t * p ); +extern void Parse_StackFnPush ( Parse_StackFn_t * p, void * bFunc ); +extern void * Parse_StackFnPop ( Parse_StackFn_t * p ); +extern void Parse_StackFnFree ( Parse_StackFn_t * p ); + +extern Parse_StackOp_t * Parse_StackOpStart ( int nDepth ); +extern bool Parse_StackOpIsEmpty( Parse_StackOp_t * p ); +extern void Parse_StackOpPush ( Parse_StackOp_t * p, int Oper ); +extern int Parse_StackOpPop ( Parse_StackOp_t * p ); +extern void Parse_StackOpFree ( Parse_StackOp_t * p ); + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/bdd/parse/parseStack.c b/abc_with_bb_support/src/bdd/parse/parseStack.c new file mode 100644 index 000000000..0aa6696f5 --- /dev/null +++ b/abc_with_bb_support/src/bdd/parse/parseStack.c @@ -0,0 +1,243 @@ +/**CFile**************************************************************** + + FileName [parseStack.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Stacks used by the formula parser.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - August 18, 2003.] + + Revision [$Id: parseStack.c,v 1.0 2003/02/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "parseInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +struct ParseStackFnStruct +{ + void ** pData; // the array of elements + int Top; // the index + int Size; // the stack size +}; + +struct ParseStackOpStruct +{ + int * pData; // the array of elements + int Top; // the index + int Size; // the stack size +}; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the stack.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Parse_StackFn_t * Parse_StackFnStart( int nDepth ) +{ + Parse_StackFn_t * p; + p = ALLOC( Parse_StackFn_t, 1 ); + memset( p, 0, sizeof(Parse_StackFn_t) ); + p->pData = ALLOC( void *, nDepth ); + p->Size = nDepth; + return p; +} + +/**Function************************************************************* + + Synopsis [Checks whether the stack is empty.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Parse_StackFnIsEmpty( Parse_StackFn_t * p ) +{ + return (bool)(p->Top == 0); +} + +/**Function************************************************************* + + Synopsis [Pushes an entry into the stack.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Parse_StackFnPush( Parse_StackFn_t * p, void * bFunc ) +{ + if ( p->Top >= p->Size ) + { + printf( "Parse_StackFnPush(): Stack size is too small!\n" ); + return; + } + p->pData[ p->Top++ ] = bFunc; +} + +/**Function************************************************************* + + Synopsis [Pops an entry out of the stack.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void * Parse_StackFnPop( Parse_StackFn_t * p ) +{ + if ( p->Top == 0 ) + { + printf( "Parse_StackFnPush(): Trying to extract data from the empty stack!\n" ); + return NULL; + } + return p->pData[ --p->Top ]; +} + +/**Function************************************************************* + + Synopsis [Deletes the stack.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Parse_StackFnFree( Parse_StackFn_t * p ) +{ + FREE( p->pData ); + FREE( p ); +} + + + + +/**Function************************************************************* + + Synopsis [Starts the stack.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Parse_StackOp_t * Parse_StackOpStart( int nDepth ) +{ + Parse_StackOp_t * p; + p = ALLOC( Parse_StackOp_t, 1 ); + memset( p, 0, sizeof(Parse_StackOp_t) ); + p->pData = ALLOC( int, nDepth ); + p->Size = nDepth; + return p; +} + +/**Function************************************************************* + + Synopsis [Checks whether the stack is empty.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Parse_StackOpIsEmpty( Parse_StackOp_t * p ) +{ + return (bool)(p->Top == 0); +} + +/**Function************************************************************* + + Synopsis [Pushes an entry into the stack.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Parse_StackOpPush( Parse_StackOp_t * p, int Oper ) +{ + if ( p->Top >= p->Size ) + { + printf( "Parse_StackOpPush(): Stack size is too small!\n" ); + return; + } + p->pData[ p->Top++ ] = Oper; +} + +/**Function************************************************************* + + Synopsis [Pops an entry out of the stack.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Parse_StackOpPop( Parse_StackOp_t * p ) +{ + if ( p->Top == 0 ) + { + printf( "Parse_StackOpPush(): Trying to extract data from the empty stack!\n" ); + return -1; + } + return p->pData[ --p->Top ]; +} + +/**Function************************************************************* + + Synopsis [Deletes the stack.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Parse_StackOpFree( Parse_StackOp_t * p ) +{ + FREE( p->pData ); + FREE( p ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/bdd/reo/module.make b/abc_with_bb_support/src/bdd/reo/module.make new file mode 100644 index 000000000..b9621fefd --- /dev/null +++ b/abc_with_bb_support/src/bdd/reo/module.make @@ -0,0 +1,7 @@ +SRC += src/bdd/reo/reoApi.c \ + src/bdd/reo/reoCore.c \ + src/bdd/reo/reoProfile.c \ + src/bdd/reo/reoSift.c \ + src/bdd/reo/reoSwap.c \ + src/bdd/reo/reoTransfer.c \ + src/bdd/reo/reoUnits.c diff --git a/abc_with_bb_support/src/bdd/reo/reo.h b/abc_with_bb_support/src/bdd/reo/reo.h new file mode 100644 index 000000000..a3b3e03b5 --- /dev/null +++ b/abc_with_bb_support/src/bdd/reo/reo.h @@ -0,0 +1,232 @@ +/**CFile**************************************************************** + + FileName [reo.h] + + PackageName [REO: A specialized DD reordering engine.] + + Synopsis [External and internal declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - October 15, 2002.] + + Revision [$Id: reo.h,v 1.0 2002/15/10 03:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __REO_H__ +#define __REO_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "extra.h" + +//#pragma warning( disable : 4514 ) + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// reordering parameters +#define REO_REORDER_LIMIT 1.15 // determines the quality/runtime trade-off +#define REO_QUAL_PAR 3 // the quality [1 = simple lower bound, 2 = strict, larger = heuristic] +// internal parameters +#define REO_CONST_LEVEL 30000 // the number of the constant level +#define REO_TOPREF_UNDEF 30000 // the undefined top reference +#define REO_CHUNK_SIZE 5000 // the number of units allocated at one time +#define REO_COST_EPSILON 0.0000001 // difference in cost large enough so that it counted as an error +#define REO_HIGH_VALUE 10000000 // a large value used to initialize some variables +// interface parameters +#define REO_ENABLE 1 // the value of the enable flag +#define REO_DISABLE 0 // the value of the disable flag + +// the types of minimization currently supported +typedef enum { + REO_MINIMIZE_NODES, + REO_MINIMIZE_WIDTH, // may not work for BDDs with complemented edges + REO_MINIMIZE_APL +} reo_min_type; + +//////////////////////////////////////////////////////////////////////// +/// DATA STRUCTURES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct _reo_unit reo_unit; // the unit representing one DD node during reordering +typedef struct _reo_plane reo_plane; // the set of nodes on one level +typedef struct _reo_hash reo_hash; // the entry in the hash table +typedef struct _reo_man reo_man; // the reordering manager +typedef struct _reo_test reo_test; // + +struct _reo_unit +{ + short lev; // the level of this node at the beginning + short TopRef; // the top level from which this node is refed (used to update BDD width) + short TopRefNew; // the new top level from which this node is refed (used to update BDD width) + short n; // the number of incoming edges (similar to ref count in the BDD) + int Sign; // the signature + + reo_unit * pE; // the pointer to the "else" branch + reo_unit * pT; // the pointer to the "then" branch + reo_unit * Next; // the link to the next one in the list + double Weight; // the probability of traversing this node +}; + +struct _reo_plane +{ + int fSifted; // to mark the sifted variables + int statsNodes; // the number of nodes in the current level + int statsWidth; // the width on the current level + double statsApl; // the sum of node probabilities on this level + double statsCost; // the current cost is stored here + double statsCostAbove; // the current cost is stored here + double statsCostBelow; // the current cost is stored here + + reo_unit * pHead; // the pointer to the beginning of the unit list +}; + +struct _reo_hash +{ + int Sign; // signature of the current cache operation + reo_unit * Arg1; // the first argument + reo_unit * Arg2; // the second argument + reo_unit * Arg3; // the third argument +}; + +struct _reo_man +{ + // these paramaters can be set by the API functions + int fMinWidth; // the flag to enable reordering for minimum width + int fMinApl; // the flag to enable reordering for minimum APL + int fVerbose; // the verbosity level + int fVerify; // the flag toggling verification + int fRemapUp; // the flag to enable remapping + int nIters; // the number of interations of sifting to perform + + // parameters given by the user when reordering is called + DdManager * dd; // the CUDD BDD manager + int * pOrder; // the resulting variable order will be returned here + + // derived parameters + int fThisIsAdd; // this flag is one if the function is the ADD + int * pSupp; // the support of the given function + int nSuppAlloc; // the max allowed number of support variables + int nSupp; // the number of support variables + int * pOrderInt; // the array storing the internal variable permutation + double * pVarCosts; // other arrays + int * pLevelOrder; // other arrays + reo_unit ** pWidthCofs; // temporary storage for cofactors used during reordering for width + + // parameters related to cost + int nNodesBeg; + int nNodesCur; + int nNodesEnd; + int nWidthCur; + int nWidthBeg; + int nWidthEnd; + double nAplCur; + double nAplBeg; + double nAplEnd; + + // mapping of the function into planes and back + int * pMapToPlanes; // the mapping of var indexes into plane levels + int * pMapToDdVarsOrig;// the mapping of plane levels into the original indexes + int * pMapToDdVarsFinal;// the mapping of plane levels into the final indexes + + // the planes table + reo_plane * pPlanes; + int nPlanes; + reo_unit ** pTops; + int nTops; + int nTopsAlloc; + + // the hash table + reo_hash * HTable; // the table itself + int nTableSize; // the size of the hash table + int Signature; // the signature counter + + // the referenced node list + int nNodesMaxAlloc; // this parameters determins how much memory is allocated + DdNode ** pRefNodes; + int nRefNodes; + int nRefNodesAlloc; + + // unit memory management + reo_unit * pUnitFreeList; + reo_unit ** pMemChunks; + int nMemChunks; + int nMemChunksAlloc; + int nUnitsUsed; + + // statistic variables + int HashSuccess; + int HashFailure; + int nSwaps; // the number of swaps + int nNISwaps; // the number of swaps without interaction +}; + +// used to manipulate units +#define Unit_Regular(u) ((reo_unit *)((unsigned long)(u) & ~01)) +#define Unit_Not(u) ((reo_unit *)((unsigned long)(u) ^ 01)) +#define Unit_NotCond(u,c) ((reo_unit *)((unsigned long)(u) ^ (c))) +#define Unit_IsConstant(u) ((int)((u)->lev == REO_CONST_LEVEL)) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// ======================= reoApi.c ======================================== +extern reo_man * Extra_ReorderInit( int nDdVarsMax, int nNodesMax ); +extern void Extra_ReorderQuit( reo_man * p ); +extern void Extra_ReorderSetMinimizationType( reo_man * p, reo_min_type fMinType ); +extern void Extra_ReorderSetRemapping( reo_man * p, int fRemapUp ); +extern void Extra_ReorderSetIterations( reo_man * p, int nIters ); +extern void Extra_ReorderSetVerbosity( reo_man * p, int fVerbose ); +extern void Extra_ReorderSetVerification( reo_man * p, int fVerify ); +extern DdNode * Extra_Reorder( reo_man * p, DdManager * dd, DdNode * Func, int * pOrder ); +extern void Extra_ReorderArray( reo_man * p, DdManager * dd, DdNode * Funcs[], DdNode * FuncsRes[], int nFuncs, int * pOrder ); +// ======================= reoCore.c ======================================= +extern void reoReorderArray( reo_man * p, DdManager * dd, DdNode * Funcs[], DdNode * FuncsRes[], int nFuncs, int * pOrder ); +extern void reoResizeStructures( reo_man * p, int nDdVarsMax, int nNodesMax, int nFuncs ); +// ======================= reoProfile.c ====================================== +extern void reoProfileNodesStart( reo_man * p ); +extern void reoProfileAplStart( reo_man * p ); +extern void reoProfileWidthStart( reo_man * p ); +extern void reoProfileWidthStart2( reo_man * p ); +extern void reoProfileAplPrint( reo_man * p ); +extern void reoProfileNodesPrint( reo_man * p ); +extern void reoProfileWidthPrint( reo_man * p ); +extern void reoProfileWidthVerifyLevel( reo_plane * pPlane, int Level ); +// ======================= reoSift.c ======================================= +extern void reoReorderSift( reo_man * p ); +// ======================= reoSwap.c ======================================= +extern double reoReorderSwapAdjacentVars( reo_man * p, int Level, int fMovingUp ); +// ======================= reoTransfer.c =================================== +extern reo_unit * reoTransferNodesToUnits_rec( reo_man * p, DdNode * F ); +extern DdNode * reoTransferUnitsToNodes_rec( reo_man * p, reo_unit * pUnit ); +// ======================= reoUnits.c ====================================== +extern reo_unit * reoUnitsGetNextUnit(reo_man * p ); +extern void reoUnitsRecycleUnit( reo_man * p, reo_unit * pUnit ); +extern void reoUnitsRecycleUnitList( reo_man * p, reo_plane * pPlane ); +extern void reoUnitsAddUnitToPlane( reo_plane * pPlane, reo_unit * pUnit ); +extern void reoUnitsStopDispenser( reo_man * p ); +// ======================= reoTest.c ======================================= +extern void Extra_ReorderTest( DdManager * dd, DdNode * Func ); +extern DdNode * Extra_ReorderCudd( DdManager * dd, DdNode * aFunc, int pPermuteReo[] ); +extern int Extra_bddReorderTest( DdManager * dd, DdNode * bF ); +extern int Extra_addReorderTest( DdManager * dd, DdNode * aF ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/bdd/reo/reoApi.c b/abc_with_bb_support/src/bdd/reo/reoApi.c new file mode 100644 index 000000000..633d3fef4 --- /dev/null +++ b/abc_with_bb_support/src/bdd/reo/reoApi.c @@ -0,0 +1,289 @@ +/**CFile**************************************************************** + + FileName [reoApi.c] + + PackageName [REO: A specialized DD reordering engine.] + + Synopsis [Implementation of API functions.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - October 15, 2002.] + + Revision [$Id: reoApi.c,v 1.0 2002/15/10 03:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "reo.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Initializes the reordering engine.] + + Description [The first argument is the max number of variables in the + CUDD DD manager which will be used with the reordering engine + (this number of should be the maximum of BDD and ZDD parts). + The second argument is the maximum number of BDD nodes in the BDDs + to be reordered. These limits are soft. Setting lower limits will later + cause the reordering manager to resize internal data structures. + However, setting the exact values will make reordering more efficient + because resizing will be not necessary.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +reo_man * Extra_ReorderInit( int nDdVarsMax, int nNodesMax ) +{ + reo_man * p; + // allocate and clean the data structure + p = ALLOC( reo_man, 1 ); + memset( p, 0, sizeof(reo_man) ); + // resize the manager to meet user's needs + reoResizeStructures( p, nDdVarsMax, nNodesMax, 100 ); + // set the defaults + p->fMinApl = 0; + p->fMinWidth = 0; + p->fRemapUp = 0; + p->fVerbose = 0; + p->fVerify = 0; + p->nIters = 1; + return p; +} + +/**Function************************************************************* + + Synopsis [Disposes of the reordering engine.] + + Description [Removes all memory associated with the reordering engine.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_ReorderQuit( reo_man * p ) +{ + free( p->pTops ); + free( p->pSupp ); + free( p->pOrderInt ); + free( p->pWidthCofs ); + free( p->pMapToPlanes ); + free( p->pMapToDdVarsOrig ); + free( p->pMapToDdVarsFinal ); + free( p->pPlanes ); + free( p->pVarCosts ); + free( p->pLevelOrder ); + free( p->HTable ); + free( p->pRefNodes ); + reoUnitsStopDispenser( p ); + free( p->pMemChunks ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Sets the type of DD minimizationl that will be performed.] + + Description [Currently, three different types of minimization are supported. + It is possible to minimize the number of BDD nodes. This is a classical type + of minimization, which is attempting to reduce the total number of nodes in + the (shared) BDD of the given Boolean functions. It is also possible to + minimize the BDD width, defined as the sum total of the number of cofactors + on each level in the (shared) BDD (note that the number of cofactors on the + given level may be larger than the number of nodes appearing on the given level). + It is also possible to minimize the average path length in the (shared) BDD + defined as the sum of products, for all BDD paths from the top node to any + terminal node, of the number of minterms on the path by the number of nodes + on the path. The default reordering type is minimization for the number of + BDD nodes. Calling this function with REO_MINIMIZE_WIDTH or REO_MINIMIZE_APL + as the second argument, changes the default minimization option for all the + reorder calls performed afterwards.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_ReorderSetMinimizationType( reo_man * p, reo_min_type fMinType ) +{ + if ( fMinType == REO_MINIMIZE_NODES ) + { + p->fMinWidth = 0; + p->fMinApl = 0; + } + else if ( fMinType == REO_MINIMIZE_WIDTH ) + { + p->fMinWidth = 1; + p->fMinApl = 0; + } + else if ( fMinType == REO_MINIMIZE_APL ) + { + p->fMinWidth = 0; + p->fMinApl = 1; + } + else + { + assert( 0 ); + } +} + +/**Function************************************************************* + + Synopsis [Sets the type of remapping performed by the engine.] + + Description [The remapping refers to the way the resulting BDD + is expressed using the elementary variables of the CUDD BDD manager. + Currently, two types possibilities are supported: remapping and no + remapping. Remapping means that the function(s) after reordering + depend on the topmost variables in the manager. No remapping means + that the function(s) after reordering depend on the same variables + as before. Consider the following example. Suppose the initial four + variable function depends on variables 2,4,5, and 9 on the CUDD BDD + manager, which may be found anywhere in the current variable order. + If remapping is set, the function after ordering depends on the + topmost variables in the manager, which may or may not be the same + as the variables 2,4,5, and 9. If no remapping is set, then the + reordered function depend on the same variables 2,4,5, and 9, but + the meaning of each variale has changed according to the new ordering. + The resulting ordering is returned in the array "pOrder" filled out + by the reordering engine in the call to Extra_Reorder(). The default + is no remapping.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_ReorderSetRemapping( reo_man * p, int fRemapUp ) +{ + p->fRemapUp = fRemapUp; +} + +/**Function************************************************************* + + Synopsis [Sets the number of iterations of sifting performed.] + + Description [The default is one iteration. But a higher minimization + quality is desired, it is possible to set the number of iterations + to any number larger than 1. Convergence is often reached after + several iterations, so typically it make no sense to set the number + of iterations higher than 3.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_ReorderSetIterations( reo_man * p, int nIters ) +{ + p->nIters = nIters; +} + +/**Function************************************************************* + + Synopsis [Sets the verification mode.] + + Description [Setting the level to 1 results in verifying the results + of variable reordering. Verification is performed by remapping the + resulting functions into the original variable order and comparing + them with the original functions given by the user. Enabling verification + typically leads to 20-30% increase in the total runtime of REO.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_ReorderSetVerification( reo_man * p, int fVerify ) +{ + p->fVerify = fVerify; +} + +/**Function************************************************************* + + Synopsis [Sets the verbosity level.] + + Description [Setting the level to 1 results in printing statistics + before and after the reordering.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_ReorderSetVerbosity( reo_man * p, int fVerbose ) +{ + p->fVerbose = fVerbose; +} + +/**Function************************************************************* + + Synopsis [Performs reordering of the function.] + + Description [Returns the DD minimized by variable reordering in the REO + engine. Takes the CUDD decision diagram manager (dd) and the function (Func) + represented as a BDD or ADD (MTBDD). If the variable array (pOrder) is not NULL, + returns the resulting variable permutation. The permutation is such that if the resulting + function is permuted by Cudd_(add,bdd)Permute() using pOrder as the permutation + array, the initial function (Func) results. + Several flag set by other interface functions specify reordering options: + - Remappig can be set by Extra_ReorderSetRemapping(). Then the resulting DD after + reordering is remapped into the topmost levels of the DD manager. Otherwise, + the resulting DD after reordering is mapped using the same variables, on which it + originally depended, only (possibly) permuted as a result of reordering. + - Minimization type can be set by Extra_ReorderSetMinimizationType(). Note + that when the BDD is minimized for the total width of the total APL, the number + BDD nodes can increase. The total width is defines as sum total of widths on each + level. The width on one level is defined as the number of distinct BDD nodes + pointed by the nodes situated above the given level. + - The number of iterations of sifting can be set by Extra_ReorderSetIterations(). + The decision diagram returned by this procedure is not referenced.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Extra_Reorder( reo_man * p, DdManager * dd, DdNode * Func, int * pOrder ) +{ + DdNode * FuncRes; + Extra_ReorderArray( p, dd, &Func, &FuncRes, 1, pOrder ); + Cudd_Deref( FuncRes ); + return FuncRes; +} + +/**Function************************************************************* + + Synopsis [Performs reordering of the array of functions.] + + Description [The options are similar to the procedure Extra_Reorder(), except that + the user should also provide storage for the resulting DDs, which are returned + referenced.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_ReorderArray( reo_man * p, DdManager * dd, DdNode * Funcs[], DdNode * FuncsRes[], int nFuncs, int * pOrder ) +{ + reoReorderArray( p, dd, Funcs, FuncsRes, nFuncs, pOrder ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/bdd/reo/reoCore.c b/abc_with_bb_support/src/bdd/reo/reoCore.c new file mode 100644 index 000000000..3b3823c52 --- /dev/null +++ b/abc_with_bb_support/src/bdd/reo/reoCore.c @@ -0,0 +1,438 @@ +/**CFile**************************************************************** + + FileName [reoCore.c] + + PackageName [REO: A specialized DD reordering engine.] + + Synopsis [Implementation of the core reordering procedure.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - October 15, 2002.] + + Revision [$Id: reoCore.c,v 1.0 2002/15/10 03:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "reo.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define CALLOC(type, num) ((type *) calloc((long)(num), (long)sizeof(type))) + +static int reoRecursiveDeref( reo_unit * pUnit ); +static int reoCheckZeroRefs( reo_plane * pPlane ); +static int reoCheckLevels( reo_man * p ); + +double s_AplBefore; +double s_AplAfter; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void reoReorderArray( reo_man * p, DdManager * dd, DdNode * Funcs[], DdNode * FuncsRes[], int nFuncs, int * pOrder ) +{ + int Counter, i; + + // set the initial parameters + p->dd = dd; + p->pOrder = pOrder; + p->nTops = nFuncs; + // get the initial number of nodes + p->nNodesBeg = Cudd_SharingSize( Funcs, nFuncs ); + // resize the internal data structures of the manager if necessary + reoResizeStructures( p, ddMax(dd->size,dd->sizeZ), p->nNodesBeg, nFuncs ); + // compute the support + p->pSupp = Extra_VectorSupportArray( dd, Funcs, nFuncs, p->pSupp ); + // get the number of support variables + p->nSupp = 0; + for ( i = 0; i < dd->size; i++ ) + p->nSupp += p->pSupp[i]; + + // if it is the constant function, no need to reorder + if ( p->nSupp == 0 ) + { + for ( i = 0; i < nFuncs; i++ ) + { + FuncsRes[i] = Funcs[i]; Cudd_Ref( FuncsRes[i] ); + } + return; + } + + // create the internal variable maps + // go through variable levels in the manager + Counter = 0; + for ( i = 0; i < dd->size; i++ ) + if ( p->pSupp[ dd->invperm[i] ] ) + { + p->pMapToPlanes[ dd->invperm[i] ] = Counter; + p->pMapToDdVarsOrig[Counter] = dd->invperm[i]; + if ( !p->fRemapUp ) + p->pMapToDdVarsFinal[Counter] = dd->invperm[i]; + else + p->pMapToDdVarsFinal[Counter] = dd->invperm[Counter]; + p->pOrderInt[Counter] = Counter; + Counter++; + } + + // set the initial parameters + p->nUnitsUsed = 0; + p->nNodesCur = 0; + p->fThisIsAdd = 0; + p->Signature++; + // transfer the function from the CUDD package into REO"s internal data structure + for ( i = 0; i < nFuncs; i++ ) + p->pTops[i] = reoTransferNodesToUnits_rec( p, Funcs[i] ); + assert( p->nNodesBeg == p->nNodesCur ); + + if ( !p->fThisIsAdd && p->fMinWidth ) + { + printf( "An important message from the REO reordering engine:\n" ); + printf( "The BDD given to the engine for reordering contains complemented edges.\n" ); + printf( "Currently, such BDDs cannot be reordered for the minimum width.\n" ); + printf( "Therefore, minimization for the number of BDD nodes is performed.\n" ); + fflush( stdout ); + p->fMinApl = 0; + p->fMinWidth = 0; + } + + if ( p->fMinWidth ) + reoProfileWidthStart(p); + else if ( p->fMinApl ) + reoProfileAplStart(p); + else + reoProfileNodesStart(p); + + if ( p->fVerbose ) + { + printf( "INITIAL: " ); + if ( p->fMinWidth ) + reoProfileWidthPrint(p); + else if ( p->fMinApl ) + reoProfileAplPrint(p); + else + reoProfileNodesPrint(p); + } + + /////////////////////////////////////////////////////////////////// + // performs the reordering + p->nSwaps = 0; + p->nNISwaps = 0; + for ( i = 0; i < p->nIters; i++ ) + { + reoReorderSift( p ); + // print statistics after each iteration + if ( p->fVerbose ) + { + printf( "ITER #%d: ", i+1 ); + if ( p->fMinWidth ) + reoProfileWidthPrint(p); + else if ( p->fMinApl ) + reoProfileAplPrint(p); + else + reoProfileNodesPrint(p); + } + // if the cost function did not change, stop iterating + if ( p->fMinWidth ) + { + p->nWidthEnd = p->nWidthCur; + assert( p->nWidthEnd <= p->nWidthBeg ); + if ( p->nWidthEnd == p->nWidthBeg ) + break; + } + else if ( p->fMinApl ) + { + p->nAplEnd = p->nAplCur; + assert( p->nAplEnd <= p->nAplBeg ); + if ( p->nAplEnd == p->nAplBeg ) + break; + } + else + { + p->nNodesEnd = p->nNodesCur; + assert( p->nNodesEnd <= p->nNodesBeg ); + if ( p->nNodesEnd == p->nNodesBeg ) + break; + } + } + assert( reoCheckLevels( p ) ); + /////////////////////////////////////////////////////////////////// + +s_AplBefore = p->nAplBeg; +s_AplAfter = p->nAplEnd; + + // set the initial parameters + p->nRefNodes = 0; + p->nNodesCur = 0; + p->Signature++; + // transfer the BDDs from REO's internal data structure to CUDD + for ( i = 0; i < nFuncs; i++ ) + { + FuncsRes[i] = reoTransferUnitsToNodes_rec( p, p->pTops[i] ); Cudd_Ref( FuncsRes[i] ); + } + // undo the DDs referenced for storing in the cache + for ( i = 0; i < p->nRefNodes; i++ ) + Cudd_RecursiveDeref( dd, p->pRefNodes[i] ); + // verify zero refs of the terminal nodes + for ( i = 0; i < nFuncs; i++ ) + { + assert( reoRecursiveDeref( p->pTops[i] ) ); + } + assert( reoCheckZeroRefs( &(p->pPlanes[p->nSupp]) ) ); + + // prepare the variable map to return to the user + if ( p->pOrder ) + { + // i is the current level in the planes data structure + // p->pOrderInt[i] is the original level in the planes data structure + // p->pMapToDdVarsOrig[i] is the variable, into which we remap when we construct the BDD from planes + // p->pMapToDdVarsOrig[ p->pOrderInt[i] ] is the original BDD variable corresponding to this level + // Therefore, p->pOrder[ p->pMapToDdVarsFinal[i] ] = p->pMapToDdVarsOrig[ p->pOrderInt[i] ] + // creates the permutation, which remaps the resulting BDD variable into the original BDD variable + for ( i = 0; i < p->nSupp; i++ ) + p->pOrder[ p->pMapToDdVarsFinal[i] ] = p->pMapToDdVarsOrig[ p->pOrderInt[i] ]; + } + + if ( p->fVerify ) + { + int fVerification; + DdNode * FuncRemapped; + int * pOrder; + + if ( p->pOrder == NULL ) + { + pOrder = ALLOC( int, p->nSupp ); + for ( i = 0; i < p->nSupp; i++ ) + pOrder[ p->pMapToDdVarsFinal[i] ] = p->pMapToDdVarsOrig[ p->pOrderInt[i] ]; + } + else + pOrder = p->pOrder; + + fVerification = 1; + for ( i = 0; i < nFuncs; i++ ) + { + // verify the result + if ( p->fThisIsAdd ) + FuncRemapped = Cudd_addPermute( dd, FuncsRes[i], pOrder ); + else + FuncRemapped = Cudd_bddPermute( dd, FuncsRes[i], pOrder ); + Cudd_Ref( FuncRemapped ); + + if ( FuncRemapped != Funcs[i] ) + { + fVerification = 0; + printf( "REO: Internal verification has failed!\n" ); + fflush( stdout ); + } + Cudd_RecursiveDeref( dd, FuncRemapped ); + } + if ( fVerification ) + printf( "REO: Internal verification is okay!\n" ); + + if ( p->pOrder == NULL ) + free( pOrder ); + } + + // recycle the data structure + for ( i = 0; i <= p->nSupp; i++ ) + reoUnitsRecycleUnitList( p, p->pPlanes + i ); +} + +/**Function************************************************************* + + Synopsis [Resizes the internal manager data structures.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void reoResizeStructures( reo_man * p, int nDdVarsMax, int nNodesMax, int nFuncs ) +{ + // resize data structures depending on the number of variables in the DD manager + if ( p->nSuppAlloc == 0 ) + { + p->pSupp = ALLOC( int, nDdVarsMax + 1 ); + p->pOrderInt = ALLOC( int, nDdVarsMax + 1 ); + p->pMapToPlanes = ALLOC( int, nDdVarsMax + 1 ); + p->pMapToDdVarsOrig = ALLOC( int, nDdVarsMax + 1 ); + p->pMapToDdVarsFinal = ALLOC( int, nDdVarsMax + 1 ); + p->pPlanes = CALLOC( reo_plane, nDdVarsMax + 1 ); + p->pVarCosts = ALLOC( double, nDdVarsMax + 1 ); + p->pLevelOrder = ALLOC( int, nDdVarsMax + 1 ); + p->nSuppAlloc = nDdVarsMax + 1; + } + else if ( p->nSuppAlloc < nDdVarsMax ) + { + free( p->pSupp ); + free( p->pOrderInt ); + free( p->pMapToPlanes ); + free( p->pMapToDdVarsOrig ); + free( p->pMapToDdVarsFinal ); + free( p->pPlanes ); + free( p->pVarCosts ); + free( p->pLevelOrder ); + + p->pSupp = ALLOC( int, nDdVarsMax + 1 ); + p->pOrderInt = ALLOC( int, nDdVarsMax + 1 ); + p->pMapToPlanes = ALLOC( int, nDdVarsMax + 1 ); + p->pMapToDdVarsOrig = ALLOC( int, nDdVarsMax + 1 ); + p->pMapToDdVarsFinal = ALLOC( int, nDdVarsMax + 1 ); + p->pPlanes = CALLOC( reo_plane, nDdVarsMax + 1 ); + p->pVarCosts = ALLOC( double, nDdVarsMax + 1 ); + p->pLevelOrder = ALLOC( int, nDdVarsMax + 1 ); + p->nSuppAlloc = nDdVarsMax + 1; + } + + // resize the data structures depending on the number of nodes + if ( p->nRefNodesAlloc == 0 ) + { + p->nNodesMaxAlloc = nNodesMax; + p->nTableSize = 3*nNodesMax + 1; + p->nRefNodesAlloc = 3*nNodesMax + 1; + p->nMemChunksAlloc = (10*nNodesMax + 1)/REO_CHUNK_SIZE + 1; + + p->HTable = CALLOC( reo_hash, p->nTableSize ); + p->pRefNodes = ALLOC( DdNode *, p->nRefNodesAlloc ); + p->pWidthCofs = ALLOC( reo_unit *, p->nRefNodesAlloc ); + p->pMemChunks = ALLOC( reo_unit *, p->nMemChunksAlloc ); + } + else if ( p->nNodesMaxAlloc < nNodesMax ) + { + void * pTemp; + int nMemChunksAllocPrev = p->nMemChunksAlloc; + + p->nNodesMaxAlloc = nNodesMax; + p->nTableSize = 3*nNodesMax + 1; + p->nRefNodesAlloc = 3*nNodesMax + 1; + p->nMemChunksAlloc = (10*nNodesMax + 1)/REO_CHUNK_SIZE + 1; + + free( p->HTable ); + free( p->pRefNodes ); + free( p->pWidthCofs ); + p->HTable = CALLOC( reo_hash, p->nTableSize ); + p->pRefNodes = ALLOC( DdNode *, p->nRefNodesAlloc ); + p->pWidthCofs = ALLOC( reo_unit *, p->nRefNodesAlloc ); + // p->pMemChunks should be reallocated because it contains pointers currently in use + pTemp = ALLOC( reo_unit *, p->nMemChunksAlloc ); + memmove( pTemp, p->pMemChunks, sizeof(reo_unit *) * nMemChunksAllocPrev ); + free( p->pMemChunks ); + p->pMemChunks = pTemp; + } + + // resize the data structures depending on the number of functions + if ( p->nTopsAlloc == 0 ) + { + p->pTops = ALLOC( reo_unit *, nFuncs ); + p->nTopsAlloc = nFuncs; + } + else if ( p->nTopsAlloc < nFuncs ) + { + free( p->pTops ); + p->pTops = ALLOC( reo_unit *, nFuncs ); + p->nTopsAlloc = nFuncs; + } +} + + +/**Function************************************************************* + + Synopsis [Dereferences units the data structure after reordering.] + + Description [This function is only useful for debugging.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int reoRecursiveDeref( reo_unit * pUnit ) +{ + reo_unit * pUnitR; + pUnitR = Unit_Regular(pUnit); + pUnitR->n--; + if ( Unit_IsConstant(pUnitR) ) + return 1; + if ( pUnitR->n == 0 ) + { + reoRecursiveDeref( pUnitR->pE ); + reoRecursiveDeref( pUnitR->pT ); + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Checks the zero references for the given plane.] + + Description [This function is only useful for debugging.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int reoCheckZeroRefs( reo_plane * pPlane ) +{ + reo_unit * pUnit; + for ( pUnit = pPlane->pHead; pUnit; pUnit = pUnit->Next ) + { + if ( pUnit->n != 0 ) + { + assert( 0 ); + } + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Checks the zero references for the given plane.] + + Description [This function is only useful for debugging.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int reoCheckLevels( reo_man * p ) +{ + reo_unit * pUnit; + int i; + + for ( i = 0; i < p->nSupp; i++ ) + { + // there are some nodes left on each level + assert( p->pPlanes[i].statsNodes ); + for ( pUnit = p->pPlanes[i].pHead; pUnit; pUnit = pUnit->Next ) + { + // the level is properly set + assert( pUnit->lev == i ); + } + } + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/bdd/reo/reoProfile.c b/abc_with_bb_support/src/bdd/reo/reoProfile.c new file mode 100644 index 000000000..e56cbdf8c --- /dev/null +++ b/abc_with_bb_support/src/bdd/reo/reoProfile.c @@ -0,0 +1,365 @@ +/**CFile**************************************************************** + + FileName [reoProfile.c] + + PackageName [REO: A specialized DD reordering engine.] + + Synopsis [Procudures that compute variables profiles (nodes, width, APL).] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - October 15, 2002.] + + Revision [$Id: reoProfile.c,v 1.0 2002/15/10 03:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "reo.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + + +/**Function******************************************************************** + + Synopsis [Start the profile for the BDD nodes.] + + Description [TopRef is the first level, on this the given node counts towards + the width of the BDDs. (In other words, it is the level of the referencing node plus 1.)] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void reoProfileNodesStart( reo_man * p ) +{ + int Total, i; + Total = 0; + for ( i = 0; i <= p->nSupp; i++ ) + { + p->pPlanes[i].statsCost = p->pPlanes[i].statsNodes; + Total += p->pPlanes[i].statsNodes; + } + assert( Total == p->nNodesCur ); + p->nNodesBeg = p->nNodesCur; +} + +/**Function************************************************************* + + Synopsis [Start the profile for the APL.] + + Description [Computes the total path length. The path length is normalized + by dividing it by 2^|supp(f)|. To get the "real" APL, multiply by 2^|supp(f)|. + This procedure assumes that Weight field of all nodes has been set to 0.0 + before the call, except for the weight of the topmost node, which is set to 1.0 + (1.0 is the probability of traversing the topmost node). This procedure + assigns the edge weights. Because of the equal probability of selecting 0 and 1 + assignment at a node, the edge weights are the same for the node. + Instead of storing them, we store the weight of the node, which is the probability + of traversing the node (pUnit->Weight) during the top down evalation of the BDD. ] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void reoProfileAplStart( reo_man * p ) +{ + reo_unit * pER, * pTR; + reo_unit * pUnit; + double Res, Half; + int i; + + // clean the weights of all nodes + for ( i = 0; i < p->nSupp; i++ ) + for ( pUnit = p->pPlanes[i].pHead; pUnit; pUnit = pUnit->Next ) + pUnit->Weight = 0.0; + // to assign the node weights (the probability of visiting each node) + // we visit the node after visiting its predecessors + + // set the probability of visits to the top nodes + for ( i = 0; i < p->nTops; i++ ) + Unit_Regular(p->pTops[i])->Weight += 1.0; + + // to compute the path length (the sum of products of edge weight by edge length) + // we visit the nodes in any order (the above order will do) + Res = 0.0; + for ( i = 0; i < p->nSupp; i++ ) + { + p->pPlanes[i].statsCost = 0.0; + for ( pUnit = p->pPlanes[i].pHead; pUnit; pUnit = pUnit->Next ) + { + pER = Unit_Regular(pUnit->pE); + pTR = Unit_Regular(pUnit->pT); + Half = 0.5 * pUnit->Weight; + pER->Weight += Half; + pTR->Weight += Half; + // add to the path length + p->pPlanes[i].statsCost += pUnit->Weight; + } + Res += p->pPlanes[i].statsCost; + } + p->pPlanes[p->nSupp].statsCost = 0.0; + p->nAplBeg = p->nAplCur = Res; +} + +/**Function******************************************************************** + + Synopsis [Start the profile for the BDD width. Complexity of the algorithm is O(N + n).] + + Description [TopRef is the first level, on which the given node counts towards + the width of the BDDs. (In other words, it is the level of the referencing node plus 1.)] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void reoProfileWidthStart( reo_man * p ) +{ + reo_unit * pUnit; + int * pWidthStart; + int * pWidthStop; + int v; + + // allocate and clean the storage for starting and stopping levels + pWidthStart = ALLOC( int, p->nSupp + 1 ); + pWidthStop = ALLOC( int, p->nSupp + 1 ); + memset( pWidthStart, 0, sizeof(int) * (p->nSupp + 1) ); + memset( pWidthStop, 0, sizeof(int) * (p->nSupp + 1) ); + + // go through the non-constant nodes and set the topmost level of their cofactors + for ( v = 0; v <= p->nSupp; v++ ) + for ( pUnit = p->pPlanes[v].pHead; pUnit; pUnit = pUnit->Next ) + { + pUnit->TopRef = REO_TOPREF_UNDEF; + pUnit->Sign = 0; + } + + // add the topmost level of the width profile + for ( v = 0; v < p->nTops; v++ ) + { + pUnit = Unit_Regular(p->pTops[v]); + if ( pUnit->TopRef == REO_TOPREF_UNDEF ) + { + // set the starting level + pUnit->TopRef = 0; + pWidthStart[pUnit->TopRef]++; + // set the stopping level + if ( pUnit->lev != REO_CONST_LEVEL ) + pWidthStop[pUnit->lev+1]++; + } + } + + for ( v = 0; v < p->nSupp; v++ ) + for ( pUnit = p->pPlanes[v].pHead; pUnit; pUnit = pUnit->Next ) + { + if ( pUnit->pE->TopRef == REO_TOPREF_UNDEF ) + { + // set the starting level + pUnit->pE->TopRef = pUnit->lev + 1; + pWidthStart[pUnit->pE->TopRef]++; + // set the stopping level + if ( pUnit->pE->lev != REO_CONST_LEVEL ) + pWidthStop[pUnit->pE->lev+1]++; + } + if ( pUnit->pT->TopRef == REO_TOPREF_UNDEF ) + { + // set the starting level + pUnit->pT->TopRef = pUnit->lev + 1; + pWidthStart[pUnit->pT->TopRef]++; + // set the stopping level + if ( pUnit->pT->lev != REO_CONST_LEVEL ) + pWidthStop[pUnit->pT->lev+1]++; + } + } + + // verify the top reference + for ( v = 0; v < p->nSupp; v++ ) + reoProfileWidthVerifyLevel( p->pPlanes + v, v ); + + // derive the profile + p->nWidthCur = 0; + for ( v = 0; v <= p->nSupp; v++ ) + { + if ( v == 0 ) + p->pPlanes[v].statsWidth = pWidthStart[v] - pWidthStop[v]; + else + p->pPlanes[v].statsWidth = p->pPlanes[v-1].statsWidth + pWidthStart[v] - pWidthStop[v]; + p->pPlanes[v].statsCost = p->pPlanes[v].statsWidth; + p->nWidthCur += p->pPlanes[v].statsWidth; +// printf( "Level %2d: Width = %5d. Correct = %d.\n", v, Temp, p->pPlanes[v].statsWidth ); + } + p->nWidthBeg = p->nWidthCur; + free( pWidthStart ); + free( pWidthStop ); +} + +/**Function******************************************************************** + + Synopsis [Start the profile for the BDD width. Complexity of the algorithm is O(N * n).] + + Description [TopRef is the first level, on which the given node counts towards + the width of the BDDs. (In other words, it is the level of the referencing node plus 1.)] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void reoProfileWidthStart2( reo_man * p ) +{ + reo_unit * pUnit; + int i, v; + + // clean the profile + for ( i = 0; i <= p->nSupp; i++ ) + p->pPlanes[i].statsWidth = 0; + + // clean the node structures + for ( v = 0; v <= p->nSupp; v++ ) + for ( pUnit = p->pPlanes[v].pHead; pUnit; pUnit = pUnit->Next ) + { + pUnit->TopRef = REO_TOPREF_UNDEF; + pUnit->Sign = 0; + } + + // set the topref to the topmost nodes + for ( i = 0; i < p->nTops; i++ ) + Unit_Regular(p->pTops[i])->TopRef = 0; + + // go through the non-constant nodes and set the topmost level of their cofactors + for ( i = 0; i < p->nSupp; i++ ) + for ( pUnit = p->pPlanes[i].pHead; pUnit; pUnit = pUnit->Next ) + { + if ( pUnit->pE->TopRef > i+1 ) + pUnit->pE->TopRef = i+1; + if ( pUnit->pT->TopRef > i+1 ) + pUnit->pT->TopRef = i+1; + } + + // verify the top reference + for ( i = 0; i < p->nSupp; i++ ) + reoProfileWidthVerifyLevel( p->pPlanes + i, i ); + + // compute the profile for the internal nodes + for ( i = 0; i < p->nSupp; i++ ) + for ( pUnit = p->pPlanes[i].pHead; pUnit; pUnit = pUnit->Next ) + for ( v = pUnit->TopRef; v <= pUnit->lev; v++ ) + p->pPlanes[v].statsWidth++; + + // compute the profile for the constant nodes + for ( pUnit = p->pPlanes[p->nSupp].pHead; pUnit; pUnit = pUnit->Next ) + for ( v = pUnit->TopRef; v <= p->nSupp; v++ ) + p->pPlanes[v].statsWidth++; + + // get the width cost + p->nWidthCur = 0; + for ( i = 0; i <= p->nSupp; i++ ) + { + p->pPlanes[i].statsCost = p->pPlanes[i].statsWidth; + p->nWidthCur += p->pPlanes[i].statsWidth; + } + p->nWidthBeg = p->nWidthCur; +} + +/**Function******************************************************************** + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void reoProfileNodesPrint( reo_man * p ) +{ + printf( "NODES: Total = %6d. Average = %6.2f.\n", p->nNodesCur, p->nNodesCur / (float)p->nSupp ); +} + +/**Function******************************************************************** + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void reoProfileAplPrint( reo_man * p ) +{ + printf( "APL: Total = %8.2f. Average =%6.2f.\n", p->nAplCur, p->nAplCur / (float)p->nSupp ); +} + +/**Function******************************************************************** + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void reoProfileWidthPrint( reo_man * p ) +{ + int WidthMax; + int TotalWidth; + int i; + + WidthMax = 0; + TotalWidth = 0; + for ( i = 0; i <= p->nSupp; i++ ) + { +// printf( "Level = %2d. Width = %3d.\n", i, p->pProfile[i] ); + if ( WidthMax < p->pPlanes[i].statsWidth ) + WidthMax = p->pPlanes[i].statsWidth; + TotalWidth += p->pPlanes[i].statsWidth; + } + assert( p->nWidthCur == TotalWidth ); + printf( "WIDTH: " ); + printf( "Maximum = %5d. ", WidthMax ); + printf( "Total = %7d. ", p->nWidthCur ); + printf( "Average = %6.2f.\n", TotalWidth / (float)p->nSupp ); +} + +/**Function******************************************************************** + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void reoProfileWidthVerifyLevel( reo_plane * pPlane, int Level ) +{ + reo_unit * pUnit; + for ( pUnit = pPlane->pHead; pUnit; pUnit = pUnit->Next ) + { + assert( pUnit->TopRef <= Level ); + assert( pUnit->pE->TopRef <= Level + 1 ); + assert( pUnit->pT->TopRef <= Level + 1 ); + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/bdd/reo/reoSift.c b/abc_with_bb_support/src/bdd/reo/reoSift.c new file mode 100644 index 000000000..01e046c19 --- /dev/null +++ b/abc_with_bb_support/src/bdd/reo/reoSift.c @@ -0,0 +1,341 @@ +/**CFile**************************************************************** + + FileName [reoSift.c] + + PackageName [REO: A specialized DD reordering engine.] + + Synopsis [Implementation of the sifting algorihtm.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - October 15, 2002.] + + Revision [$Id: reoSift.c,v 1.0 2002/15/10 03:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "reo.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Implements the variable sifting algorithm.] + + Description [Performs a sequence of adjacent variable swaps known as "sifting". + Uses the cost functions determined by the flag.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void reoReorderSift( reo_man * p ) +{ + double CostCurrent; // the cost of the current permutation + double CostLimit; // the maximum increase in cost that can be tolerated + double CostBest; // the best cost + int BestQ; // the best position + int VarCurrent; // the current variable to move + int q; // denotes the current position of the variable + int c; // performs the loops over variables until all of them are sifted + int v; // used for other purposes + + assert( p->nSupp > 0 ); + + // set the current cost depending on the minimization criteria + if ( p->fMinWidth ) + CostCurrent = p->nWidthCur; + else if ( p->fMinApl ) + CostCurrent = p->nAplCur; + else + CostCurrent = p->nNodesCur; + + // find the upper bound on tbe cost growth + CostLimit = 1 + (int)(REO_REORDER_LIMIT * CostCurrent); + + // perform sifting for each of p->nSupp variables + for ( c = 0; c < p->nSupp; c++ ) + { + // select the current variable to be the one with the largest number of nodes that is not sifted yet + VarCurrent = -1; + CostBest = -1.0; + for ( v = 0; v < p->nSupp; v++ ) + { + p->pVarCosts[v] = REO_HIGH_VALUE; + if ( !p->pPlanes[v].fSifted ) + { +// VarCurrent = v; +// if ( CostBest < p->pPlanes[v].statsCost ) + if ( CostBest < p->pPlanes[v].statsNodes ) + { +// CostBest = p->pPlanes[v].statsCost; + CostBest = p->pPlanes[v].statsNodes; + VarCurrent = v; + } + + } + } + assert( VarCurrent != -1 ); + // mark this variable as sifted + p->pPlanes[VarCurrent].fSifted = 1; + + // set the current value + p->pVarCosts[VarCurrent] = CostCurrent; + + // set the best cost + CostBest = CostCurrent; + BestQ = VarCurrent; + + // determine which way to move the variable first (up or down) + // the rationale is that if we move the shorter way first + // it is more likely that the best position will be found on the longer way + // and the reverse movement (to take the best position) will be faster + if ( VarCurrent < p->nSupp/2 ) // move up first, then down + { + // set the total cost on all levels above the current level + p->pPlanes[0].statsCostAbove = 0; + for ( v = 1; v <= VarCurrent; v++ ) + p->pPlanes[v].statsCostAbove = p->pPlanes[v-1].statsCostAbove + p->pPlanes[v-1].statsCost; + // set the total cost on all levels below the current level + p->pPlanes[p->nSupp].statsCostBelow = 0; + for ( v = p->nSupp - 1; v >= VarCurrent; v-- ) + p->pPlanes[v].statsCostBelow = p->pPlanes[v+1].statsCostBelow + p->pPlanes[v+1].statsCost; + + assert( CostCurrent == p->pPlanes[VarCurrent].statsCostAbove + + p->pPlanes[VarCurrent].statsCost + + p->pPlanes[VarCurrent].statsCostBelow ); + + // move up + for ( q = VarCurrent-1; q >= 0; q-- ) + { + CostCurrent -= reoReorderSwapAdjacentVars( p, q, 1 ); + // now q points to the position of this var in the order + p->pVarCosts[q] = CostCurrent; + // update the lower bound (assuming that for level q+1 it is set correctly) + p->pPlanes[q].statsCostBelow = p->pPlanes[q+1].statsCostBelow + p->pPlanes[q+1].statsCost; + // check the upper bound + if ( CostCurrent >= CostLimit ) + break; + // check the lower bound + if ( p->pPlanes[q].statsCostBelow + (REO_QUAL_PAR-1)*p->pPlanes[q].statsCostAbove/REO_QUAL_PAR >= CostBest ) + break; + // update the best cost + if ( CostBest > CostCurrent ) + { + CostBest = CostCurrent; + BestQ = q; + // adjust node limit + CostLimit = ddMin( CostLimit, 1 + (int)(REO_REORDER_LIMIT * CostCurrent) ); + } + + // when we are reordering for width or APL, it may happen that + // the number of nodes has grown above certain limit, + // in which case we have to resize the data structures + if ( p->fMinWidth || p->fMinApl ) + { + if ( p->nNodesCur >= 2 * p->nNodesMaxAlloc ) + { +// printf( "Resizing data structures. Old size = %6d. New size = %6d.\n", p->nNodesMaxAlloc, p->nNodesCur ); + reoResizeStructures( p, 0, p->nNodesCur, 0 ); + } + } + } + // fix the plane index + if ( q == -1 ) + q++; + // now p points to the position of this var in the order + + // move down + for ( ; q < p->nSupp-1; ) + { + CostCurrent -= reoReorderSwapAdjacentVars( p, q, 0 ); + q++; // change q to point to the position of this var in the order + // sanity check: the number of nodes on the back pass should be the same + if ( p->pVarCosts[q] != REO_HIGH_VALUE && fabs( p->pVarCosts[q] - CostCurrent ) > REO_COST_EPSILON ) + printf("reoReorderSift(): Error! On the backward move, the costs are different.\n"); + p->pVarCosts[q] = CostCurrent; + // update the lower bound (assuming that for level q-1 it is set correctly) + p->pPlanes[q].statsCostAbove = p->pPlanes[q-1].statsCostAbove + p->pPlanes[q-1].statsCost; + // check the bounds only if the variable already reached its previous position + if ( q >= BestQ ) + { + // check the upper bound + if ( CostCurrent >= CostLimit ) + break; + // check the lower bound + if ( p->pPlanes[q].statsCostAbove + (REO_QUAL_PAR-1)*p->pPlanes[q].statsCostBelow/REO_QUAL_PAR >= CostBest ) + break; + } + // update the best cost + if ( CostBest >= CostCurrent ) + { + CostBest = CostCurrent; + BestQ = q; + // adjust node limit + CostLimit = ddMin( CostLimit, 1 + (int)(REO_REORDER_LIMIT * CostCurrent) ); + } + + // when we are reordering for width or APL, it may happen that + // the number of nodes has grown above certain limit, + // in which case we have to resize the data structures + if ( p->fMinWidth || p->fMinApl ) + { + if ( p->nNodesCur >= 2 * p->nNodesMaxAlloc ) + { +// printf( "Resizing data structures. Old size = %6d. New size = %6d.\n", p->nNodesMaxAlloc, p->nNodesCur ); + reoResizeStructures( p, 0, p->nNodesCur, 0 ); + } + } + } + // move the variable up from the given position (q) to the best position (BestQ) + assert( q >= BestQ ); + for ( ; q > BestQ; q-- ) + { + CostCurrent -= reoReorderSwapAdjacentVars( p, q-1, 1 ); + // sanity check: the number of nodes on the back pass should be the same + if ( fabs( p->pVarCosts[q-1] - CostCurrent ) > REO_COST_EPSILON ) + { + printf("reoReorderSift(): Error! On the return move, the costs are different.\n" ); + fflush(stdout); + } + } + } + else // move down first, then up + { + // set the current number of nodes on all levels above the given level + p->pPlanes[0].statsCostAbove = 0; + for ( v = 1; v <= VarCurrent; v++ ) + p->pPlanes[v].statsCostAbove = p->pPlanes[v-1].statsCostAbove + p->pPlanes[v-1].statsCost; + // set the current number of nodes on all levels below the given level + p->pPlanes[p->nSupp].statsCostBelow = 0; + for ( v = p->nSupp - 1; v >= VarCurrent; v-- ) + p->pPlanes[v].statsCostBelow = p->pPlanes[v+1].statsCostBelow + p->pPlanes[v+1].statsCost; + + assert( CostCurrent == p->pPlanes[VarCurrent].statsCostAbove + + p->pPlanes[VarCurrent].statsCost + + p->pPlanes[VarCurrent].statsCostBelow ); + + // move down + for ( q = VarCurrent; q < p->nSupp-1; ) + { + CostCurrent -= reoReorderSwapAdjacentVars( p, q, 0 ); + q++; // change q to point to the position of this var in the order + p->pVarCosts[q] = CostCurrent; + // update the lower bound (assuming that for level q-1 it is set correctly) + p->pPlanes[q].statsCostAbove = p->pPlanes[q-1].statsCostAbove + p->pPlanes[q-1].statsCost; + // check the upper bound + if ( CostCurrent >= CostLimit ) + break; + // check the lower bound + if ( p->pPlanes[q].statsCostAbove + (REO_QUAL_PAR-1)*p->pPlanes[q].statsCostBelow/REO_QUAL_PAR >= CostBest ) + break; + // update the best cost + if ( CostBest > CostCurrent ) + { + CostBest = CostCurrent; + BestQ = q; + // adjust node limit + CostLimit = ddMin( CostLimit, 1 + (int)(REO_REORDER_LIMIT * CostCurrent) ); + } + + // when we are reordering for width or APL, it may happen that + // the number of nodes has grown above certain limit, + // in which case we have to resize the data structures + if ( p->fMinWidth || p->fMinApl ) + { + if ( p->nNodesCur >= 2 * p->nNodesMaxAlloc ) + { +// printf( "Resizing data structures. Old size = %6d. New size = %6d.\n", p->nNodesMaxAlloc, p->nNodesCur ); + reoResizeStructures( p, 0, p->nNodesCur, 0 ); + } + } + } + + // move up + for ( --q; q >= 0; q-- ) + { + CostCurrent -= reoReorderSwapAdjacentVars( p, q, 1 ); + // now q points to the position of this var in the order + // sanity check: the number of nodes on the back pass should be the same + if ( p->pVarCosts[q] != REO_HIGH_VALUE && fabs( p->pVarCosts[q] - CostCurrent ) > REO_COST_EPSILON ) + printf("reoReorderSift(): Error! On the backward move, the costs are different.\n"); + p->pVarCosts[q] = CostCurrent; + // update the lower bound (assuming that for level q+1 it is set correctly) + p->pPlanes[q].statsCostBelow = p->pPlanes[q+1].statsCostBelow + p->pPlanes[q+1].statsCost; + // check the bounds only if the variable already reached its previous position + if ( q <= BestQ ) + { + // check the upper bound + if ( CostCurrent >= CostLimit ) + break; + // check the lower bound + if ( p->pPlanes[q].statsCostBelow + (REO_QUAL_PAR-1)*p->pPlanes[q].statsCostAbove/REO_QUAL_PAR >= CostBest ) + break; + } + // update the best cost + if ( CostBest >= CostCurrent ) + { + CostBest = CostCurrent; + BestQ = q; + // adjust node limit + CostLimit = ddMin( CostLimit, 1 + (int)(REO_REORDER_LIMIT * CostCurrent) ); + } + + // when we are reordering for width or APL, it may happen that + // the number of nodes has grown above certain limit, + // in which case we have to resize the data structures + if ( p->fMinWidth || p->fMinApl ) + { + if ( p->nNodesCur >= 2 * p->nNodesMaxAlloc ) + { +// printf( "Resizing data structures. Old size = %6d. New size = %6d.\n", p->nNodesMaxAlloc, p->nNodesCur ); + reoResizeStructures( p, 0, p->nNodesCur, 0 ); + } + } + } + // fix the plane index + if ( q == -1 ) + q++; + // now q points to the position of this var in the order + // move the variable down from the given position (q) to the best position (BestQ) + assert( q <= BestQ ); + for ( ; q < BestQ; q++ ) + { + CostCurrent -= reoReorderSwapAdjacentVars( p, q, 0 ); + // sanity check: the number of nodes on the back pass should be the same + if ( fabs( p->pVarCosts[q+1] - CostCurrent ) > REO_COST_EPSILON ) + { + printf("reoReorderSift(): Error! On the return move, the costs are different.\n" ); + fflush(stdout); + } + } + } + assert( fabs( CostBest - CostCurrent ) < REO_COST_EPSILON ); + + // update the cost + if ( p->fMinWidth ) + p->nWidthCur = (int)CostBest; + else if ( p->fMinApl ) + p->nAplCur = CostCurrent; + else + p->nNodesCur = (int)CostBest; + } + + // remove the sifted attributes if any + for ( v = 0; v < p->nSupp; v++ ) + p->pPlanes[v].fSifted = 0; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/bdd/reo/reoSwap.c b/abc_with_bb_support/src/bdd/reo/reoSwap.c new file mode 100644 index 000000000..d85108bb0 --- /dev/null +++ b/abc_with_bb_support/src/bdd/reo/reoSwap.c @@ -0,0 +1,898 @@ +/**CFile**************************************************************** + + FileName [reoSwap.c] + + PackageName [REO: A specialized DD reordering engine.] + + Synopsis [Implementation of the two-variable swap.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - October 15, 2002.] + + Revision [$Id: reoSwap.c,v 1.0 2002/15/10 03:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "reo.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define AddToLinkedList( ppList, pLink ) (((pLink)->Next = *(ppList)), (*(ppList) = (pLink))) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [Takes the level (lev0) of the plane, which should be swapped + with the next plane. Returns the gain using the current cost function.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +double reoReorderSwapAdjacentVars( reo_man * p, int lev0, int fMovingUp ) +{ + // the levels in the decision diagram + int lev1 = lev0 + 1, lev2 = lev0 + 2; + // the new nodes on lev0 + reo_unit * pLoop, * pUnit; + // the new nodes on lev1 + reo_unit * pNewPlane20, * pNewPlane21, * pNewPlane20R; + reo_unit * pUnitE, * pUnitER, * pUnitT; + // the nodes below lev1 + reo_unit * pNew1E, * pNew1T, * pNew2E, * pNew2T; + reo_unit * pNew1ER, * pNew2ER; + // the old linked lists + reo_unit * pListOld0 = p->pPlanes[lev0].pHead; + reo_unit * pListOld1 = p->pPlanes[lev1].pHead; + // working planes and one more temporary plane + reo_unit * pListNew0 = NULL, ** ppListNew0 = &pListNew0; + reo_unit * pListNew1 = NULL, ** ppListNew1 = &pListNew1; + reo_unit * pListTemp = NULL, ** ppListTemp = &pListTemp; + // various integer variables + int fComp, fCompT, fFound, nWidthCofs, HKey, fInteract, temp, c; + // statistical variables + int nNodesUpMovedDown = 0; + int nNodesDownMovedUp = 0; + int nNodesUnrefRemoved = 0; + int nNodesUnrefAdded = 0; + int nWidthReduction = 0; + double AplWeightTotalLev0; + double AplWeightTotalLev1; + double AplWeightHalf; + double AplWeightPrev; + double AplWeightAfter; + double nCostGain; + + // set the old lists + assert( lev0 >= 0 && lev1 < p->nSupp ); + pListOld0 = p->pPlanes[lev0].pHead; + pListOld1 = p->pPlanes[lev1].pHead; + + // make sure the planes have nodes + assert( p->pPlanes[lev0].statsNodes && p->pPlanes[lev1].statsNodes ); + assert( pListOld0 && pListOld1 ); + + if ( p->fMinWidth ) + { + // verify that the width parameters are set correctly + reoProfileWidthVerifyLevel( p->pPlanes + lev0, lev0 ); + reoProfileWidthVerifyLevel( p->pPlanes + lev1, lev1 ); + // start the storage for cofactors + nWidthCofs = 0; + } + else if ( p->fMinApl ) + { + AplWeightPrev = p->nAplCur; + AplWeightAfter = p->nAplCur; + AplWeightTotalLev0 = 0.0; + AplWeightTotalLev1 = 0.0; + } + + // check if the planes interact + fInteract = 0; // assume that they do not interact + for ( pUnit = pListOld0; pUnit; pUnit = pUnit->Next ) + { + if ( pUnit->pT->lev == lev1 || Unit_Regular(pUnit->pE)->lev == lev1 ) + { + fInteract = 1; + break; + } + // change the level now, this is done for efficiency reasons + pUnit->lev = lev1; + } + + // set the new signature for hashing + p->nSwaps++; + if ( !fInteract ) +// if ( 0 ) + { + // perform the swap without interaction + p->nNISwaps++; + + // change the levels + if ( p->fMinWidth ) + { + // go through the current lower level, which will become upper + for ( pUnit = pListOld1; pUnit; pUnit = pUnit->Next ) + { + pUnit->lev = lev0; + + pUnitER = Unit_Regular(pUnit->pE); + if ( pUnitER->TopRef > lev0 ) + { + if ( pUnitER->Sign != p->nSwaps ) + { + if ( pUnitER->TopRef == lev2 ) + { + pUnitER->TopRef = lev1; + nWidthReduction--; + } + else + { + assert( pUnitER->TopRef == lev1 ); + } + pUnitER->Sign = p->nSwaps; + } + } + + pUnitT = pUnit->pT; + if ( pUnitT->TopRef > lev0 ) + { + if ( pUnitT->Sign != p->nSwaps ) + { + if ( pUnitT->TopRef == lev2 ) + { + pUnitT->TopRef = lev1; + nWidthReduction--; + } + else + { + assert( pUnitT->TopRef == lev1 ); + } + pUnitT->Sign = p->nSwaps; + } + } + + } + + // go through the current upper level, which will become lower + for ( pUnit = pListOld0; pUnit; pUnit = pUnit->Next ) + { + pUnit->lev = lev1; + + pUnitER = Unit_Regular(pUnit->pE); + if ( pUnitER->TopRef > lev0 ) + { + if ( pUnitER->Sign != p->nSwaps ) + { + assert( pUnitER->TopRef == lev1 ); + pUnitER->TopRef = lev2; + pUnitER->Sign = p->nSwaps; + nWidthReduction++; + } + } + + pUnitT = pUnit->pT; + if ( pUnitT->TopRef > lev0 ) + { + if ( pUnitT->Sign != p->nSwaps ) + { + assert( pUnitT->TopRef == lev1 ); + pUnitT->TopRef = lev2; + pUnitT->Sign = p->nSwaps; + nWidthReduction++; + } + } + } + } + else + { +// for ( pUnit = pListOld0; pUnit; pUnit = pUnit->Next ) +// pUnit->lev = lev1; + for ( pUnit = pListOld1; pUnit; pUnit = pUnit->Next ) + pUnit->lev = lev0; + } + + // set the new linked lists, which will be attached to the planes + pListNew0 = pListOld1; + pListNew1 = pListOld0; + + if ( p->fMinApl ) + { + AplWeightTotalLev0 = p->pPlanes[lev1].statsCost; + AplWeightTotalLev1 = p->pPlanes[lev0].statsCost; + } + + // set the changes in terms of nodes + nNodesUpMovedDown = p->pPlanes[lev0].statsNodes; + nNodesDownMovedUp = p->pPlanes[lev1].statsNodes; + goto finish; + } + p->Signature++; + + + // two-variable swap is done in three easy steps + // previously I thought that steps (1) and (2) can be merged into one step + // now it is clear that this cannot be done without changing a lot of other stuff... + + // (1) walk through the upper level, find units without cofactors in the lower level + // and move them to the new lower level (while adding to the cache) + // (2) walk through the uppoer level, and tranform all the remaning nodes + // while employing cache for the new lower level + // (3) walk through the old lower level, find those nodes whose ref counters are not zero, + // and move them to the new uppoer level, free other nodes + + // (1) walk through the upper level, find units without cofactors in the lower level + // and move them to the new lower level (while adding to the cache) + for ( pLoop = pListOld0; pLoop; ) + { + pUnit = pLoop; + pLoop = pLoop->Next; + + pUnitE = pUnit->pE; + pUnitER = Unit_Regular(pUnitE); + pUnitT = pUnit->pT; + + if ( pUnitER->lev != lev1 && pUnitT->lev != lev1 ) + { + // before after + // + // + // 0 / \ 1 + // / \ + // / \ + // / \ + // / \ 0 / \ 1 + // / \ / \ + // / \ / \ + // F0 F1 F0 F1 + + // move to plane-2-new + // nothing changes in the process (cofactors, ref counter, APL weight) + pUnit->lev = lev1; + AddToLinkedList( ppListNew1, pUnit ); + if ( p->fMinApl ) + AplWeightTotalLev1 += pUnit->Weight; + + // add to cache - find the cell with different signature (not the current one!) + for ( HKey = hashKey3(p->Signature, pUnitE, pUnitT, p->nTableSize); + p->HTable[HKey].Sign == p->Signature; + HKey = (HKey+1) % p->nTableSize ); + assert( p->HTable[HKey].Sign != p->Signature ); + p->HTable[HKey].Sign = p->Signature; + p->HTable[HKey].Arg1 = pUnitE; + p->HTable[HKey].Arg2 = pUnitT; + p->HTable[HKey].Arg3 = pUnit; + + nNodesUpMovedDown++; + + if ( p->fMinWidth ) + { + // update the cofactors's top ref + if ( pUnitER->TopRef > lev0 ) // the cofactor's top ref level is one of the current two levels + { + assert( pUnitER->TopRef == lev1 ); + pUnitER->TopRefNew = lev2; + if ( pUnitER->Sign != p->nSwaps ) + { + pUnitER->Sign = p->nSwaps; // set the current signature + p->pWidthCofs[ nWidthCofs++ ] = pUnitER; + } + } + if ( pUnitT->TopRef > lev0 ) // the cofactor's top ref level is one of the current two levels + { + assert( pUnitT->TopRef == lev1 ); + pUnitT->TopRefNew = lev2; + if ( pUnitT->Sign != p->nSwaps ) + { + pUnitT->Sign = p->nSwaps; // set the current signature + p->pWidthCofs[ nWidthCofs++ ] = pUnitT; + } + } + } + } + else + { + // add to the temporary plane + AddToLinkedList( ppListTemp, pUnit ); + } + } + + + // (2) walk through the uppoer level, and tranform all the remaning nodes + // while employing cache for the new lower level + for ( pLoop = pListTemp; pLoop; ) + { + pUnit = pLoop; + pLoop = pLoop->Next; + + pUnitE = pUnit->pE; + pUnitER = Unit_Regular(pUnitE); + pUnitT = pUnit->pT; + fComp = (int)(pUnitER != pUnitE); + + // count the amount of weight to reduce the APL of the children of this node + if ( p->fMinApl ) + AplWeightHalf = 0.5 * pUnit->Weight; + + // determine what situation is this + if ( pUnitER->lev == lev1 && pUnitT->lev == lev1 ) + { + if ( fComp == 0 ) + { + // before after + // + // + // 0 / \ 1 0 / \ 1 + // / \ / \ + // / \ / \ + // + // 0 / \ 1 0 / \ 1 0 / \ 1 0 / \ 1 + // / \ / \ / \ / \ + // / \ / \ / \ / \ + // F0 F1 F2 F3 F0 F2 F1 F3 + // pNew1E pNew1T pNew2E pNew2T + // + pNew1E = pUnitE->pE; // F0 + pNew1T = pUnitT->pE; // F2 + + pNew2E = pUnitE->pT; // F1 + pNew2T = pUnitT->pT; // F3 + } + else + { + // before after + // + // + // 0 . \ 1 0 / \ 1 + // . \ / \ + // . \ / \ + // + // 0 / \ 1 0 / \ 1 0 . \ 1 0 . \ 1 + // / \ / \ . \ . \ + // / \ / \ . \ . \ + // F0 F1 F2 F3 F0 F2 F1 F3 + // pNew1E pNew1T pNew2E pNew2T + // + pNew1E = Unit_Not(pUnitER->pE); // F0 + pNew1T = pUnitT->pE; // F2 + + pNew2E = Unit_Not(pUnitER->pT); // F1 + pNew2T = pUnitT->pT; // F3 + } + // subtract ref counters - on the level P2 + pUnitER->n--; + pUnitT->n--; + + // mark the change in the APL weights + if ( p->fMinApl ) + { + pUnitER->Weight -= AplWeightHalf; + pUnitT->Weight -= AplWeightHalf; + AplWeightAfter -= pUnit->Weight; + } + } + else if ( pUnitER->lev == lev1 ) + { + if ( fComp == 0 ) + { + // before after + // + // + // 0 / \ 1 0 / \ 1 + // / \ / \ + // / \ / \ + // \ + // 0 / \ 1 \ 0 / \ 1 0 / \ 1 + // / \ \ / \ / \ + // / \ \ / \ / \ + // F0 F1 F3 F0 F3 F1 F3 + // pNew1E pNew1T pNew2E pNew2T + // + pNew1E = pUnitER->pE; // F0 + pNew1T = pUnitT; // F3 + + pNew2E = pUnitER->pT; // F1 + pNew2T = pUnitT; // F3 + } + else + { + // before after + // + // + // 0 . \ 1 0 / \ 1 + // . \ / \ + // . \ / \ + // \ + // 0 / \ 1 \ 0 . \ 1 0 . \ 1 + // / \ \ . \ . \ + // / \ \ . \ . \ + // F0 F1 F3 F0 F3 F1 F3 + // pNew1E pNew1T pNew2E pNew2T + // + pNew1E = Unit_Not(pUnitER->pE); // F0 + pNew1T = pUnitT; // F3 + + pNew2E = Unit_Not(pUnitER->pT); // F1 + pNew2T = pUnitT; // F3 + } + // subtract ref counter - on the level P2 + pUnitER->n--; + // subtract ref counter - on other levels + pUnitT->n--; /// + + // mark the change in the APL weights + if ( p->fMinApl ) + { + pUnitER->Weight -= AplWeightHalf; + AplWeightAfter -= AplWeightHalf; + } + } + else if ( pUnitT->lev == lev1 ) + { + // before after + // + // + // 0 / \ 1 0 / \ 1 + // / \ / \ + // / \ / \ + // / + // / 0 / \ 1 0 / \ 1 0 / \ 1 + // / / \ / \ / \ + // / / \ / \ / \ + // F0 F2 F3 F0 F2 F0 F3 + // pNew1E pNew1T pNew2E pNew2T + // + pNew1E = pUnitE; // F0 + pNew1T = pUnitT->pE; // F2 + + pNew2E = pUnitE; // F0 + pNew2T = pUnitT->pT; // F3 + + // subtract incoming edge counter - on the level P2 + pUnitT->n--; + // subtract ref counter - on other levels + pUnitER->n--; /// + + // mark the change in the APL weights + if ( p->fMinApl ) + { + pUnitT->Weight -= AplWeightHalf; + AplWeightAfter -= AplWeightHalf; + } + } + else + { + assert( 0 ); // should never happen + } + + + // consider all the cases except the last one + if ( pNew1E == pNew1T ) + { + pNewPlane20 = pNew1T; + + if ( p->fMinWidth ) + { + // update the cofactors's top ref + if ( pNew1T->TopRef > lev0 ) // the cofactor's top ref level is one of the current two levels + { + pNew1T->TopRefNew = lev1; + if ( pNew1T->Sign != p->nSwaps ) + { + pNew1T->Sign = p->nSwaps; // set the current signature + p->pWidthCofs[ nWidthCofs++ ] = pNew1T; + } + } + } + } + else + { + // pNew1T can be complemented + fCompT = Cudd_IsComplement(pNew1T); + if ( fCompT ) + { + pNew1E = Unit_Not(pNew1E); + pNew1T = Unit_Not(pNew1T); + } + + // check the hash-table + fFound = 0; + for ( HKey = hashKey3(p->Signature, pNew1E, pNew1T, p->nTableSize); + p->HTable[HKey].Sign == p->Signature; + HKey = (HKey+1) % p->nTableSize ) + if ( p->HTable[HKey].Arg1 == pNew1E && p->HTable[HKey].Arg2 == pNew1T ) + { // the entry is present + // assign this entry + pNewPlane20 = p->HTable[HKey].Arg3; + assert( pNewPlane20->lev == lev1 ); + fFound = 1; + p->HashSuccess++; + break; + } + + if ( !fFound ) + { // create the new entry + pNewPlane20 = reoUnitsGetNextUnit( p ); // increments the unit counter + pNewPlane20->pE = pNew1E; + pNewPlane20->pT = pNew1T; + pNewPlane20->n = 0; // ref will be added later + pNewPlane20->lev = lev1; + if ( p->fMinWidth ) + { + pNewPlane20->TopRef = lev1; + pNewPlane20->Sign = 0; + } + // set the weight of this node + if ( p->fMinApl ) + pNewPlane20->Weight = 0.0; + + // increment ref counters of children + pNew1ER = Unit_Regular(pNew1E); + pNew1ER->n++; // + pNew1T->n++; // + + // insert into the data structure + AddToLinkedList( ppListNew1, pNewPlane20 ); + + // add this entry to cache + assert( p->HTable[HKey].Sign != p->Signature ); + p->HTable[HKey].Sign = p->Signature; + p->HTable[HKey].Arg1 = pNew1E; + p->HTable[HKey].Arg2 = pNew1T; + p->HTable[HKey].Arg3 = pNewPlane20; + + nNodesUnrefAdded++; + + if ( p->fMinWidth ) + { + // update the cofactors's top ref + if ( pNew1ER->TopRef > lev0 ) // the cofactor's top ref level is one of the current two levels + { + if ( pNew1ER->Sign != p->nSwaps ) + { + pNew1ER->TopRefNew = lev2; + if ( pNew1ER->Sign != p->nSwaps ) + { + pNew1ER->Sign = p->nSwaps; // set the current signature + p->pWidthCofs[ nWidthCofs++ ] = pNew1ER; + } + } + // otherwise the level is already set correctly + else + { + assert( pNew1ER->TopRefNew == lev1 || pNew1ER->TopRefNew == lev2 ); + } + } + // update the cofactors's top ref + if ( pNew1T->TopRef > lev0 ) // the cofactor's top ref level is one of the current two levels + { + if ( pNew1T->Sign != p->nSwaps ) + { + pNew1T->TopRefNew = lev2; + if ( pNew1T->Sign != p->nSwaps ) + { + pNew1T->Sign = p->nSwaps; // set the current signature + p->pWidthCofs[ nWidthCofs++ ] = pNew1T; + } + } + // otherwise the level is already set correctly + else + { + assert( pNew1T->TopRefNew == lev1 || pNew1T->TopRefNew == lev2 ); + } + } + } + } + + if ( p->fMinApl ) + { + // increment the weight of this node + pNewPlane20->Weight += AplWeightHalf; + // mark the change in the APL weight + AplWeightAfter += AplWeightHalf; + // update the total weight of this level + AplWeightTotalLev1 += AplWeightHalf; + } + + if ( fCompT ) + pNewPlane20 = Unit_Not(pNewPlane20); + } + + if ( pNew2E == pNew2T ) + { + pNewPlane21 = pNew2T; + + if ( p->fMinWidth ) + { + // update the cofactors's top ref + if ( pNew2T->TopRef > lev0 ) // the cofactor's top ref level is one of the current two levels + { + pNew2T->TopRefNew = lev1; + if ( pNew2T->Sign != p->nSwaps ) + { + pNew2T->Sign = p->nSwaps; // set the current signature + p->pWidthCofs[ nWidthCofs++ ] = pNew2T; + } + } + } + } + else + { + assert( !Cudd_IsComplement(pNew2T) ); + + // check the hash-table + fFound = 0; + for ( HKey = hashKey3(p->Signature, pNew2E, pNew2T, p->nTableSize); + p->HTable[HKey].Sign == p->Signature; + HKey = (HKey+1) % p->nTableSize ) + if ( p->HTable[HKey].Arg1 == pNew2E && p->HTable[HKey].Arg2 == pNew2T ) + { // the entry is present + // assign this entry + pNewPlane21 = p->HTable[HKey].Arg3; + assert( pNewPlane21->lev == lev1 ); + fFound = 1; + p->HashSuccess++; + break; + } + + if ( !fFound ) + { // create the new entry + pNewPlane21 = reoUnitsGetNextUnit( p ); // increments the unit counter + pNewPlane21->pE = pNew2E; + pNewPlane21->pT = pNew2T; + pNewPlane21->n = 0; // ref will be added later + pNewPlane21->lev = lev1; + if ( p->fMinWidth ) + { + pNewPlane21->TopRef = lev1; + pNewPlane21->Sign = 0; + } + // set the weight of this node + if ( p->fMinApl ) + pNewPlane21->Weight = 0.0; + + // increment ref counters of children + pNew2ER = Unit_Regular(pNew2E); + pNew2ER->n++; // + pNew2T->n++; // + + // insert into the data structure +// reoUnitsAddUnitToPlane( &P2new, pNewPlane21 ); + AddToLinkedList( ppListNew1, pNewPlane21 ); + + // add this entry to cache + assert( p->HTable[HKey].Sign != p->Signature ); + p->HTable[HKey].Sign = p->Signature; + p->HTable[HKey].Arg1 = pNew2E; + p->HTable[HKey].Arg2 = pNew2T; + p->HTable[HKey].Arg3 = pNewPlane21; + + nNodesUnrefAdded++; + + + if ( p->fMinWidth ) + { + // update the cofactors's top ref + if ( pNew2ER->TopRef > lev0 ) // the cofactor's top ref level is one of the current two levels + { + if ( pNew2ER->Sign != p->nSwaps ) + { + pNew2ER->TopRefNew = lev2; + if ( pNew2ER->Sign != p->nSwaps ) + { + pNew2ER->Sign = p->nSwaps; // set the current signature + p->pWidthCofs[ nWidthCofs++ ] = pNew2ER; + } + } + // otherwise the level is already set correctly + else + { + assert( pNew2ER->TopRefNew == lev1 || pNew2ER->TopRefNew == lev2 ); + } + } + // update the cofactors's top ref + if ( pNew2T->TopRef > lev0 ) // the cofactor's top ref level is one of the current two levels + { + if ( pNew2T->Sign != p->nSwaps ) + { + pNew2T->TopRefNew = lev2; + if ( pNew2T->Sign != p->nSwaps ) + { + pNew2T->Sign = p->nSwaps; // set the current signature + p->pWidthCofs[ nWidthCofs++ ] = pNew2T; + } + } + // otherwise the level is already set correctly + else + { + assert( pNew2T->TopRefNew == lev1 || pNew2T->TopRefNew == lev2 ); + } + } + } + } + + if ( p->fMinApl ) + { + // increment the weight of this node + pNewPlane21->Weight += AplWeightHalf; + // mark the change in the APL weight + AplWeightAfter += AplWeightHalf; + // update the total weight of this level + AplWeightTotalLev1 += AplWeightHalf; + } + } + // in all cases, the node will be added to the plane-1 + // this should be the same node (pUnit) as was originally there + // because it is referenced by the above nodes + + assert( !Cudd_IsComplement(pNewPlane21) ); + // should be the case; otherwise reordering is not a local operation + + pUnit->pE = pNewPlane20; + pUnit->pT = pNewPlane21; + assert( pUnit->lev == lev0 ); + // reference counter remains the same; the APL weight remains the same + + // increment ref counters of children + pNewPlane20R = Unit_Regular(pNewPlane20); + pNewPlane20R->n++; /// + pNewPlane21->n++; /// + + // insert into the data structure + AddToLinkedList( ppListNew0, pUnit ); + if ( p->fMinApl ) + AplWeightTotalLev0 += pUnit->Weight; + } + + // (3) walk through the old lower level, find those nodes whose ref counters are not zero, + // and move them to the new uppoer level, free other nodes + for ( pLoop = pListOld1; pLoop; ) + { + pUnit = pLoop; + pLoop = pLoop->Next; + if ( pUnit->n ) + { + assert( !p->fMinApl || pUnit->Weight > 0.0 ); + // the node should be added to the new level + // no need to check the hash table + pUnit->lev = lev0; + AddToLinkedList( ppListNew0, pUnit ); + if ( p->fMinApl ) + AplWeightTotalLev0 += pUnit->Weight; + + nNodesDownMovedUp++; + + if ( p->fMinWidth ) + { + pUnitER = Unit_Regular(pUnit->pE); + pUnitT = pUnit->pT; + + // update the cofactors's top ref + if ( pUnitER->TopRef > lev0 ) // the cofactor's top ref level is one of the current two levels + { + pUnitER->TopRefNew = lev1; + if ( pUnitER->Sign != p->nSwaps ) + { + pUnitER->Sign = p->nSwaps; // set the current signature + p->pWidthCofs[ nWidthCofs++ ] = pUnitER; + } + } + if ( pUnitT->TopRef > lev0 ) // the cofactor's top ref level is one of the current two levels + { + pUnitT->TopRefNew = lev1; + if ( pUnitT->Sign != p->nSwaps ) + { + pUnitT->Sign = p->nSwaps; // set the current signature + p->pWidthCofs[ nWidthCofs++ ] = pUnitT; + } + } + } + } + else + { + assert( !p->fMinApl || pUnit->Weight == 0.0 ); + // decrement reference counters of children + pUnitER = Unit_Regular(pUnit->pE); + pUnitT = pUnit->pT; + pUnitER->n--; /// + pUnitT->n--; /// + // the node should be thrown away + reoUnitsRecycleUnit( p, pUnit ); + nNodesUnrefRemoved++; + } + } + +finish: + + // attach the new levels to the planes + p->pPlanes[lev0].pHead = pListNew0; + p->pPlanes[lev1].pHead = pListNew1; + + // swap the sift status + temp = p->pPlanes[lev0].fSifted; + p->pPlanes[lev0].fSifted = p->pPlanes[lev1].fSifted; + p->pPlanes[lev1].fSifted = temp; + + // swap variables in the variable map + if ( p->pOrderInt ) + { + temp = p->pOrderInt[lev0]; + p->pOrderInt[lev0] = p->pOrderInt[lev1]; + p->pOrderInt[lev1] = temp; + } + + // adjust the node profile + p->pPlanes[lev0].statsNodes -= (nNodesUpMovedDown - nNodesDownMovedUp); + p->pPlanes[lev1].statsNodes -= (nNodesDownMovedUp - nNodesUpMovedDown) + nNodesUnrefRemoved - nNodesUnrefAdded; + p->nNodesCur -= nNodesUnrefRemoved - nNodesUnrefAdded; + + // adjust the node profile on this level + if ( p->fMinWidth ) + { + for ( c = 0; c < nWidthCofs; c++ ) + { + if ( p->pWidthCofs[c]->TopRefNew < p->pWidthCofs[c]->TopRef ) + { + p->pWidthCofs[c]->TopRef = p->pWidthCofs[c]->TopRefNew; + nWidthReduction--; + } + else if ( p->pWidthCofs[c]->TopRefNew > p->pWidthCofs[c]->TopRef ) + { + p->pWidthCofs[c]->TopRef = p->pWidthCofs[c]->TopRefNew; + nWidthReduction++; + } + } + // verify that the profile is okay + reoProfileWidthVerifyLevel( p->pPlanes + lev0, lev0 ); + reoProfileWidthVerifyLevel( p->pPlanes + lev1, lev1 ); + + // compute the total gain in terms of width + nCostGain = (nNodesDownMovedUp - nNodesUpMovedDown + nNodesUnrefRemoved - nNodesUnrefAdded) + nWidthReduction; + // adjust the width on this level + p->pPlanes[lev1].statsWidth -= (int)nCostGain; + // set the cost + p->pPlanes[lev1].statsCost = p->pPlanes[lev1].statsWidth; + } + else if ( p->fMinApl ) + { + // compute the total gain in terms of APL + nCostGain = AplWeightPrev - AplWeightAfter; + // make sure that the ALP is updated correctly +// assert( p->pPlanes[lev0].statsCost + p->pPlanes[lev1].statsCost - nCostGain == +// AplWeightTotalLev0 + AplWeightTotalLev1 ); + // adjust the profile + p->pPlanes[lev0].statsApl = AplWeightTotalLev0; + p->pPlanes[lev1].statsApl = AplWeightTotalLev1; + // set the cost + p->pPlanes[lev0].statsCost = p->pPlanes[lev0].statsApl; + p->pPlanes[lev1].statsCost = p->pPlanes[lev1].statsApl; + } + else + { + // compute the total gain in terms of the number of nodes + nCostGain = nNodesUnrefRemoved - nNodesUnrefAdded; + // adjust the profile (adjusted above) + // set the cost + p->pPlanes[lev0].statsCost = p->pPlanes[lev0].statsNodes; + p->pPlanes[lev1].statsCost = p->pPlanes[lev1].statsNodes; + } + + return nCostGain; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/bdd/reo/reoTest.c b/abc_with_bb_support/src/bdd/reo/reoTest.c new file mode 100644 index 000000000..105812239 --- /dev/null +++ b/abc_with_bb_support/src/bdd/reo/reoTest.c @@ -0,0 +1,251 @@ +/**CFile**************************************************************** + + FileName [reoTest.c] + + PackageName [REO: A specialized DD reordering engine.] + + Synopsis [Various testing procedures (may be outdated).] + + Author [Alan Mishchenko ] + + Affiliation [ECE Department. Portland State University, Portland, Oregon.] + + Date [Ver. 1.0. Started - October 15, 2002.] + + Revision [$Id: reoTest.c,v 1.0 2002/15/10 03:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "reo.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Reorders the DD using REO and CUDD.] + + Description [This function can be used to test the performance of the reordering package.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_ReorderTest( DdManager * dd, DdNode * Func ) +{ + reo_man * pReo; + DdNode * Temp, * Temp1; + int pOrder[1000]; + + pReo = Extra_ReorderInit( 100, 100 ); + +//Extra_DumpDot( dd, &Func, 1, "beforReo.dot", 0 ); + Temp = Extra_Reorder( pReo, dd, Func, pOrder ); Cudd_Ref( Temp ); +//Extra_DumpDot( dd, &Temp, 1, "afterReo.dot", 0 ); + + Temp1 = Extra_ReorderCudd(dd, Func, NULL ); Cudd_Ref( Temp1 ); +printf( "Initial = %d. Final = %d. Cudd = %d.\n", Cudd_DagSize(Func), Cudd_DagSize(Temp), Cudd_DagSize(Temp1) ); + Cudd_RecursiveDeref( dd, Temp1 ); + Cudd_RecursiveDeref( dd, Temp ); + + Extra_ReorderQuit( pReo ); +} + + +/**Function************************************************************* + + Synopsis [Reorders the DD using REO and CUDD.] + + Description [This function can be used to test the performance of the reordering package.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_ReorderTestArray( DdManager * dd, DdNode * Funcs[], int nFuncs ) +{ + reo_man * pReo; + DdNode * FuncsRes[1000]; + int pOrder[1000]; + int i; + + pReo = Extra_ReorderInit( 100, 100 ); + Extra_ReorderArray( pReo, dd, Funcs, FuncsRes, nFuncs, pOrder ); + Extra_ReorderQuit( pReo ); + +printf( "Initial = %d. Final = %d.\n", Cudd_SharingSize(Funcs,nFuncs), Cudd_SharingSize(FuncsRes,nFuncs) ); + + for ( i = 0; i < nFuncs; i++ ) + Cudd_RecursiveDeref( dd, FuncsRes[i] ); + +} + +/**Function************************************************************* + + Synopsis [Reorders the DD using CUDD package.] + + Description [Transfers the DD into a temporary manager in such a way + that the level correspondence is preserved. Reorders the manager + and transfers the DD back into the original manager using the topmost + levels of the manager, in such a way that the ordering of levels is + preserved. The resulting permutation is returned in the array + given by the user.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Extra_ReorderCudd( DdManager * dd, DdNode * aFunc, int pPermuteReo[] ) +{ + static DdManager * ddReorder = NULL; + static int * Permute = NULL; + static int * PermuteReo1 = NULL; + static int * PermuteReo2 = NULL; + DdNode * aFuncReorder, * aFuncNew; + int lev, var; + + // start the reordering manager + if ( ddReorder == NULL ) + { + Permute = ALLOC( int, dd->size ); + PermuteReo1 = ALLOC( int, dd->size ); + PermuteReo2 = ALLOC( int, dd->size ); + ddReorder = Cudd_Init( dd->size, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 ); + Cudd_AutodynDisable(ddReorder); + } + + // determine the permutation of variable to make sure that var order in bFunc + // will not change when this function is transfered into the new manager + for ( lev = 0; lev < dd->size; lev++ ) + { + Permute[ dd->invperm[lev] ] = ddReorder->invperm[lev]; + PermuteReo1[ ddReorder->invperm[lev] ] = dd->invperm[lev]; + } + // transfer this function into the new manager in such a way that ordering of vars does not change + aFuncReorder = Extra_TransferPermute( dd, ddReorder, aFunc, Permute ); Cudd_Ref( aFuncReorder ); +// assert( Cudd_DagSize(aFunc) == Cudd_DagSize(aFuncReorder) ); + + // perform the reordering +printf( "Nodes before = %d.\n", Cudd_DagSize(aFuncReorder) ); + Cudd_ReduceHeap( ddReorder, CUDD_REORDER_SYMM_SIFT, 1 ); +printf( "Nodes before = %d.\n", Cudd_DagSize(aFuncReorder) ); + + // determine the reverse variable permutation + for ( lev = 0; lev < dd->size; lev++ ) + { + Permute[ ddReorder->invperm[lev] ] = dd->invperm[lev]; + PermuteReo2[ dd->invperm[lev] ] = ddReorder->invperm[lev]; + } + + // transfer this function into the new manager in such a way that ordering of vars does not change + aFuncNew = Extra_TransferPermute( ddReorder, dd, aFuncReorder, Permute ); Cudd_Ref( aFuncNew ); +// assert( Cudd_DagSize(aFuncNew) == Cudd_DagSize(aFuncReorder) ); + Cudd_RecursiveDeref( ddReorder, aFuncReorder ); + + // derive the resulting variable ordering + if ( pPermuteReo ) + for ( var = 0; var < dd->size; var++ ) + pPermuteReo[var] = PermuteReo1[ PermuteReo2[var] ]; + + Cudd_Deref( aFuncNew ); + return aFuncNew; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [Transfers the BDD into another manager minimizes it and + returns the min number of nodes; disposes of the BDD in the new manager. + Useful for debugging or comparing the performance of other reordering + procedures.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_bddReorderTest( DdManager * dd, DdNode * bF ) +{ + static DdManager * s_ddmin; + DdNode * bFmin; + int nNodes; +// int clk1; + + if ( s_ddmin == NULL ) + s_ddmin = Cudd_Init( dd->size, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0); + +// Cudd_ShuffleHeap( s_ddmin, dd->invperm ); + +// clk1 = clock(); + bFmin = Cudd_bddTransfer( dd, s_ddmin, bF ); Cudd_Ref( bFmin ); + Cudd_ReduceHeap(s_ddmin,CUDD_REORDER_SIFT,1); +// Cudd_ReduceHeap(s_ddmin,CUDD_REORDER_SYMM_SIFT,1); + nNodes = Cudd_DagSize( bFmin ); + Cudd_RecursiveDeref( s_ddmin, bFmin ); + +// printf( "Classical variable reordering time = %.2f sec\n", (float)(clock() - clk1)/(float)(CLOCKS_PER_SEC) ); + return nNodes; +} + +/**Function************************************************************* + + Synopsis [] + + Description [Transfers the ADD into another manager minimizes it and + returns the min number of nodes; disposes of the BDD in the new manager. + Useful for debugging or comparing the performance of other reordering + procedures.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_addReorderTest( DdManager * dd, DdNode * aF ) +{ + static DdManager * s_ddmin; + DdNode * bF; + DdNode * bFmin; + DdNode * aFmin; + int nNodesBeg; + int nNodesEnd; + int clk1; + + if ( s_ddmin == NULL ) + s_ddmin = Cudd_Init( dd->size, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0); + +// Cudd_ShuffleHeap( s_ddmin, dd->invperm ); + + clk1 = clock(); + bF = Cudd_addBddPattern( dd, aF ); Cudd_Ref( bF ); + bFmin = Cudd_bddTransfer( dd, s_ddmin, bF ); Cudd_Ref( bFmin ); + Cudd_RecursiveDeref( dd, bF ); + aFmin = Cudd_BddToAdd( s_ddmin, bFmin ); Cudd_Ref( aFmin ); + Cudd_RecursiveDeref( s_ddmin, bFmin ); + + nNodesBeg = Cudd_DagSize( aFmin ); + Cudd_ReduceHeap(s_ddmin,CUDD_REORDER_SIFT,1); +// Cudd_ReduceHeap(s_ddmin,CUDD_REORDER_SYMM_SIFT,1); + nNodesEnd = Cudd_DagSize( aFmin ); + Cudd_RecursiveDeref( s_ddmin, aFmin ); + + printf( "Classical reordering of ADDs: Before = %d. After = %d.\n", nNodesBeg, nNodesEnd ); + printf( "Classical variable reordering time = %.2f sec\n", (float)(clock() - clk1)/(float)(CLOCKS_PER_SEC) ); + return nNodesEnd; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/bdd/reo/reoTransfer.c b/abc_with_bb_support/src/bdd/reo/reoTransfer.c new file mode 100644 index 000000000..f4fe9e2ab --- /dev/null +++ b/abc_with_bb_support/src/bdd/reo/reoTransfer.c @@ -0,0 +1,199 @@ +/**CFile**************************************************************** + + FileName [reoTransfer.c] + + PackageName [REO: A specialized DD reordering engine.] + + Synopsis [Transfering a DD from the CUDD manager into REO"s internal data structures and back.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - October 15, 2002.] + + Revision [$Id: reoTransfer.c,v 1.0 2002/15/10 03:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "reo.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Transfers the DD into the internal reordering data structure.] + + Description [It is important that the hash table is lossless.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +reo_unit * reoTransferNodesToUnits_rec( reo_man * p, DdNode * F ) +{ + DdManager * dd = p->dd; + reo_unit * pUnit; + int HKey, fComp; + + fComp = Cudd_IsComplement(F); + F = Cudd_Regular(F); + + // check the hash-table + if ( F->ref != 1 ) + { + // search cache - use linear probing + for ( HKey = hashKey2(p->Signature,F,p->nTableSize); p->HTable[HKey].Sign == p->Signature; HKey = (HKey+1) % p->nTableSize ) + if ( p->HTable[HKey].Arg1 == (reo_unit *)F ) + { + pUnit = p->HTable[HKey].Arg2; + assert( pUnit ); + // increment the edge counter + pUnit->n++; + return Unit_NotCond( pUnit, fComp ); + } + } + // the entry in not found in the cache + + // create a new entry + pUnit = reoUnitsGetNextUnit( p ); + pUnit->n = 1; + if ( cuddIsConstant(F) ) + { + pUnit->lev = REO_CONST_LEVEL; + pUnit->pE = (reo_unit*)((int)(cuddV(F))); + pUnit->pT = NULL; + // check if the diagram that is being reordering has complement edges + if ( F != dd->one ) + p->fThisIsAdd = 1; + // insert the unit into the corresponding plane + reoUnitsAddUnitToPlane( &(p->pPlanes[p->nSupp]), pUnit ); // increments the unit counter + } + else + { + pUnit->lev = p->pMapToPlanes[F->index]; + pUnit->pE = reoTransferNodesToUnits_rec( p, cuddE(F) ); + pUnit->pT = reoTransferNodesToUnits_rec( p, cuddT(F) ); + // insert the unit into the corresponding plane + reoUnitsAddUnitToPlane( &(p->pPlanes[pUnit->lev]), pUnit ); // increments the unit counter + } + + // add to the hash table + if ( F->ref != 1 ) + { + // the next free entry is already found - it is pointed to by HKey + // while we traversed the diagram, the hash entry to which HKey points, + // might have been used. Make sure that its signature is different. + for ( ; p->HTable[HKey].Sign == p->Signature; HKey = (HKey+1) % p->nTableSize ); + p->HTable[HKey].Sign = p->Signature; + p->HTable[HKey].Arg1 = (reo_unit *)F; + p->HTable[HKey].Arg2 = pUnit; + } + + // increment the counter of nodes + p->nNodesCur++; + return Unit_NotCond( pUnit, fComp ); +} + +/**Function************************************************************* + + Synopsis [Creates the DD from the internal reordering data structure.] + + Description [It is important that the hash table is lossless.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * reoTransferUnitsToNodes_rec( reo_man * p, reo_unit * pUnit ) +{ + DdManager * dd = p->dd; + DdNode * bRes, * E, * T; + int HKey, fComp; + + fComp = Cudd_IsComplement(pUnit); + pUnit = Unit_Regular(pUnit); + + // check the hash-table + if ( pUnit->n != 1 ) + { + for ( HKey = hashKey2(p->Signature,pUnit,p->nTableSize); p->HTable[HKey].Sign == p->Signature; HKey = (HKey+1) % p->nTableSize ) + if ( p->HTable[HKey].Arg1 == pUnit ) + { + bRes = (DdNode*) p->HTable[HKey].Arg2; + assert( bRes ); + return Cudd_NotCond( bRes, fComp ); + } + } + + // treat the case of constants + if ( Unit_IsConstant(pUnit) ) + { + bRes = cuddUniqueConst( dd, ((double)((int)(pUnit->pE))) ); + cuddRef( bRes ); + } + else + { + // split and recur on children of this node + E = reoTransferUnitsToNodes_rec( p, pUnit->pE ); + if ( E == NULL ) + return NULL; + cuddRef(E); + + T = reoTransferUnitsToNodes_rec( p, pUnit->pT ); + if ( T == NULL ) + { + Cudd_RecursiveDeref(dd, E); + return NULL; + } + cuddRef(T); + + // consider the case when Res0 and Res1 are the same node + assert( E != T ); + assert( !Cudd_IsComplement(T) ); + + bRes = cuddUniqueInter( dd, p->pMapToDdVarsFinal[pUnit->lev], T, E ); + if ( bRes == NULL ) + { + Cudd_RecursiveDeref(dd,E); + Cudd_RecursiveDeref(dd,T); + return NULL; + } + cuddRef( bRes ); + cuddDeref( E ); + cuddDeref( T ); + } + + // do not keep the result if the ref count is only 1, since it will not be visited again + if ( pUnit->n != 1 ) + { + // while we traversed the diagram, the hash entry to which HKey points, + // might have been used. Make sure that its signature is different. + for ( ; p->HTable[HKey].Sign == p->Signature; HKey = (HKey+1) % p->nTableSize ); + p->HTable[HKey].Sign = p->Signature; + p->HTable[HKey].Arg1 = pUnit; + p->HTable[HKey].Arg2 = (reo_unit *)bRes; + + // add the DD to the referenced DD list in order to be able to store it in cache + p->pRefNodes[p->nRefNodes++] = bRes; Cudd_Ref( bRes ); + // no need to do this, because the garbage collection will not take bRes away + // it is held by the diagram in the making + } + // increment the counter of nodes + p->nNodesCur++; + cuddDeref( bRes ); + return Cudd_NotCond( bRes, fComp ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/bdd/reo/reoUnits.c b/abc_with_bb_support/src/bdd/reo/reoUnits.c new file mode 100644 index 000000000..c7dbc8316 --- /dev/null +++ b/abc_with_bb_support/src/bdd/reo/reoUnits.c @@ -0,0 +1,184 @@ +/**CFile**************************************************************** + + FileName [reoUnits.c] + + PackageName [REO: A specialized DD reordering engine.] + + Synopsis [Procedures which support internal data structures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - October 15, 2002.] + + Revision [$Id: reoUnits.c,v 1.0 2002/15/10 03:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "reo.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void reoUnitsAddToFreeUnitList( reo_man * p ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Extract the next unit from the free unit list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +reo_unit * reoUnitsGetNextUnit(reo_man * p ) +{ + reo_unit * pUnit; + // check there are stil units to extract + if ( p->pUnitFreeList == NULL ) + reoUnitsAddToFreeUnitList( p ); + // extract the next unit from the linked list + pUnit = p->pUnitFreeList; + p->pUnitFreeList = pUnit->Next; + p->nUnitsUsed++; + return pUnit; +} + +/**Function************************************************************* + + Synopsis [Returns the unit to the free unit list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void reoUnitsRecycleUnit( reo_man * p, reo_unit * pUnit ) +{ + pUnit->Next = p->pUnitFreeList; + p->pUnitFreeList = pUnit; + p->nUnitsUsed--; +} + +/**Function************************************************************* + + Synopsis [Returns the list of units to the free unit list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void reoUnitsRecycleUnitList( reo_man * p, reo_plane * pPlane ) +{ + reo_unit * pUnit; + reo_unit * pTail; + + if ( pPlane->pHead == NULL ) + return; + + // find the tail + for ( pUnit = pPlane->pHead; pUnit; pUnit = pUnit->Next ) + pTail = pUnit; + pTail->Next = p->pUnitFreeList; + p->pUnitFreeList = pPlane->pHead; + memset( pPlane, 0, sizeof(reo_plane) ); +} + +/**Function************************************************************* + + Synopsis [Stops the unit dispenser.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void reoUnitsStopDispenser( reo_man * p ) +{ + int i; + for ( i = 0; i < p->nMemChunks; i++ ) + free( p->pMemChunks[i] ); +// printf("\nThe number of chunks used is %d, each of them %d units\n", p->nMemChunks, REO_CHUNK_SIZE ); + p->nMemChunks = 0; +} + +/**Function************************************************************* + + Synopsis [Adds one unit to the list of units which constitutes the plane.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void reoUnitsAddUnitToPlane( reo_plane * pPlane, reo_unit * pUnit ) +{ + if ( pPlane->pHead == NULL ) + { + pPlane->pHead = pUnit; + pUnit->Next = NULL; + } + else + { + pUnit->Next = pPlane->pHead; + pPlane->pHead = pUnit; + } + pPlane->statsNodes++; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void reoUnitsAddToFreeUnitList( reo_man * p ) +{ + int c; + // check that we still have chunks left + if ( p->nMemChunks == p->nMemChunksAlloc ) + { + printf( "reoUnitsAddToFreeUnitList(): Memory manager ran out of memory!\n" ); + fflush( stdout ); + return; + } + // allocate the next chunk + assert( p->pUnitFreeList == NULL ); + p->pUnitFreeList = ALLOC( reo_unit, REO_CHUNK_SIZE ); + // split chunks into list-connected units + for ( c = 0; c < REO_CHUNK_SIZE-1; c++ ) + (p->pUnitFreeList + c)->Next = p->pUnitFreeList + c + 1; + // set the last pointer to NULL + (p->pUnitFreeList + REO_CHUNK_SIZE-1)->Next = NULL; + // add the chunk to the array of chunks + p->pMemChunks[p->nMemChunks++] = p->pUnitFreeList; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/generic.c b/abc_with_bb_support/src/generic.c new file mode 100644 index 000000000..a8b1d8c28 --- /dev/null +++ b/abc_with_bb_support/src/generic.c @@ -0,0 +1,47 @@ +/**CFile**************************************************************** + + FileName [.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: .c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "__Int.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/generic.h b/abc_with_bb_support/src/generic.h new file mode 100644 index 000000000..2a09df71a --- /dev/null +++ b/abc_with_bb_support/src/generic.h @@ -0,0 +1,59 @@ +/**CFile**************************************************************** + + FileName [.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: .h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __zzz_H__ +#define __zzz_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== zzz.c ==========================================================*/ + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/map/fpga/fpga.c b/abc_with_bb_support/src/map/fpga/fpga.c new file mode 100644 index 000000000..6dc24a1d9 --- /dev/null +++ b/abc_with_bb_support/src/map/fpga/fpga.c @@ -0,0 +1,283 @@ +/**CFile**************************************************************** + + FileName [fpga.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Command file for the FPGA package.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - August 18, 2004.] + + Revision [$Id: fpga.c,v 1.4 2004/10/28 17:36:07 alanmi Exp $] + +***********************************************************************/ + +#include "fpgaInt.h" +#include "main.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Fpga_CommandReadLibrary( Abc_Frame_t * pAbc, int argc, char **argv ); +static int Fpga_CommandPrintLibrary( Abc_Frame_t * pAbc, int argc, char **argv ); + +// the library file format should be as follows: +/* +# The area/delay of k-variable LUTs: +# k area delay +1 1 1 +2 2 2 +3 4 3 +4 8 4 +5 16 5 +6 32 6 +*/ + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Package initialization procedure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_Init( Abc_Frame_t * pAbc ) +{ + // set the default library + //Fpga_LutLib_t s_LutLib = { "lutlib", 6, 0, {0,1,2,4,8,16,32}, {{0},{1},{2},{3},{4},{5},{6}} }; +// Fpga_LutLib_t s_LutLib = { "lutlib", 5, 0, {0,1,1,1,1,1}, {{0},{1},{1},{1},{1},{1}} }; + Fpga_LutLib_t s_LutLib = { "lutlib", 4, 0, {0,1,1,1,1}, {{0},{1},{1},{1},{1}} }; + //Fpga_LutLib_t s_LutLib = { "lutlib", 3, 0, {0,1,1,1}, {{0},{1},{1},{1}} }; + + Abc_FrameSetLibLut( Fpga_LutLibDup(&s_LutLib) ); + + Cmd_CommandAdd( pAbc, "FPGA mapping", "read_lut", Fpga_CommandReadLibrary, 0 ); + Cmd_CommandAdd( pAbc, "FPGA mapping", "print_lut", Fpga_CommandPrintLibrary, 0 ); +} + +/**Function************************************************************* + + Synopsis [Package ending procedure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_End() +{ + Fpga_LutLibFree( Abc_FrameReadLibLut() ); +} + + +/**Function************************************************************* + + Synopsis [Command procedure to read LUT libraries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_CommandReadLibrary( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + FILE * pFile; + FILE * pOut, * pErr; + Fpga_LutLib_t * pLib; + Abc_Ntk_t * pNet; + char * FileName; + int fVerbose; + int c; + + pNet = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set the defaults + fVerbose = 1; + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "vh")) != EOF ) + { + switch (c) + { + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + break; + default: + goto usage; + } + } + + + if ( argc != globalUtilOptind + 1 ) + { + goto usage; + } + + // get the input file name + FileName = argv[globalUtilOptind]; + if ( (pFile = fopen( FileName, "r" )) == NULL ) + { + fprintf( pErr, "Cannot open input file \"%s\". ", FileName ); + if ( FileName = Extra_FileGetSimilarName( FileName, ".genlib", ".lib", ".gen", ".g", NULL ) ) + fprintf( pErr, "Did you mean \"%s\"?", FileName ); + fprintf( pErr, "\n" ); + return 1; + } + fclose( pFile ); + + // set the new network + pLib = Fpga_LutLibCreate( FileName, fVerbose ); + if ( pLib == NULL ) + { + fprintf( pErr, "Reading LUT library has failed.\n" ); + goto usage; + } + // replace the current library + Fpga_LutLibFree( Abc_FrameReadLibLut() ); + Abc_FrameSetLibLut( pLib ); + return 0; + +usage: + fprintf( pErr, "\nusage: read_lut [-vh]\n"); + fprintf( pErr, "\t read the LUT library from the file\n" ); + fprintf( pErr, "\t-v : toggles enabling of verbose output [default = %s]\n", (fVerbose? "yes" : "no") ); + fprintf( pErr, "\t-h : print the command usage\n"); + fprintf( pErr, "\t \n"); + fprintf( pErr, "\t File format for a LUT library:\n"); + fprintf( pErr, "\t (the default library is shown)\n"); + fprintf( pErr, "\t \n"); + fprintf( pErr, "\t # The area/delay of k-variable LUTs:\n"); + fprintf( pErr, "\t # k area delay\n"); + fprintf( pErr, "\t 1 1 1\n"); + fprintf( pErr, "\t 2 2 2\n"); + fprintf( pErr, "\t 3 4 3\n"); + fprintf( pErr, "\t 4 8 4\n"); + fprintf( pErr, "\t 5 16 5\n"); + fprintf( pErr, "\t 6 32 6\n"); + return 1; /* error exit */ +} + +/**Function************************************************************* + + Synopsis [Command procedure to read LUT libraries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_CommandPrintLibrary( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNet; + int fVerbose; + int c; + + pNet = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set the defaults + fVerbose = 1; + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "vh")) != EOF ) + { + switch (c) + { + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + break; + default: + goto usage; + } + } + + + if ( argc != globalUtilOptind ) + { + goto usage; + } + + // set the new network + Fpga_LutLibPrint( Abc_FrameReadLibLut() ); + return 0; + +usage: + fprintf( pErr, "\nusage: read_print [-vh]\n"); + fprintf( pErr, "\t print the current LUT library\n" ); + fprintf( pErr, "\t-v : toggles enabling of verbose output [default = %s]\n", (fVerbose? "yes" : "no") ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; /* error exit */ +} + +/**Function************************************************************* + + Synopsis [Sets simple LUT library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_SetSimpleLutLib( int nLutSize ) +{ + Fpga_LutLib_t s_LutLib10= { "lutlib",10, 0, {0,1,1,1,1,1,1,1,1,1,1}, {{0},{1},{1},{1},{1},{1},{1},{1},{1},{1},{1}} }; + Fpga_LutLib_t s_LutLib9 = { "lutlib", 9, 0, {0,1,1,1,1,1,1,1,1,1}, {{0},{1},{1},{1},{1},{1},{1},{1},{1},{1}} }; + Fpga_LutLib_t s_LutLib8 = { "lutlib", 8, 0, {0,1,1,1,1,1,1,1,1}, {{0},{1},{1},{1},{1},{1},{1},{1},{1}} }; + Fpga_LutLib_t s_LutLib7 = { "lutlib", 7, 0, {0,1,1,1,1,1,1,1}, {{0},{1},{1},{1},{1},{1},{1},{1}} }; + Fpga_LutLib_t s_LutLib6 = { "lutlib", 6, 0, {0,1,1,1,1,1,1}, {{0},{1},{1},{1},{1},{1},{1}} }; + Fpga_LutLib_t s_LutLib5 = { "lutlib", 5, 0, {0,1,1,1,1,1}, {{0},{1},{1},{1},{1},{1}} }; + Fpga_LutLib_t s_LutLib4 = { "lutlib", 4, 0, {0,1,1,1,1}, {{0},{1},{1},{1},{1}} }; + Fpga_LutLib_t s_LutLib3 = { "lutlib", 3, 0, {0,1,1,1}, {{0},{1},{1},{1}} }; + Fpga_LutLib_t * pLutLib; + assert( nLutSize >= 3 && nLutSize <= 10 ); + switch ( nLutSize ) + { + case 3: pLutLib = &s_LutLib3; break; + case 4: pLutLib = &s_LutLib4; break; + case 5: pLutLib = &s_LutLib5; break; + case 6: pLutLib = &s_LutLib6; break; + case 7: pLutLib = &s_LutLib7; break; + case 8: pLutLib = &s_LutLib8; break; + case 9: pLutLib = &s_LutLib9; break; + case 10: pLutLib = &s_LutLib10; break; + default: pLutLib = NULL; break; + } + if ( pLutLib == NULL ) + return; + Fpga_LutLibFree( Abc_FrameReadLibLut() ); + Abc_FrameSetLibLut( Fpga_LutLibDup(pLutLib) ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/fpga/fpga.h b/abc_with_bb_support/src/map/fpga/fpga.h new file mode 100644 index 000000000..53dbb5ac6 --- /dev/null +++ b/abc_with_bb_support/src/map/fpga/fpga.h @@ -0,0 +1,172 @@ +/**CFile**************************************************************** + + FileName [fpga.h] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Technology mapping for variable-size-LUT FPGAs.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - August 18, 2004.] + + Revision [$Id: fpga.h,v 1.7 2004/09/30 21:18:09 satrajit Exp $] + +***********************************************************************/ + +#ifndef __FPGA_H__ +#define __FPGA_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +// the maximum size of LUTs used for mapping +#define FPGA_MAX_LUTSIZE 32 + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Fpga_ManStruct_t_ Fpga_Man_t; +typedef struct Fpga_NodeStruct_t_ Fpga_Node_t; +typedef struct Fpga_NodeVecStruct_t_ Fpga_NodeVec_t; +typedef struct Fpga_CutStruct_t_ Fpga_Cut_t; +typedef struct Fpga_LutLibStruct_t_ Fpga_LutLib_t; + +//////////////////////////////////////////////////////////////////////// +/// GLOBAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define Fpga_IsComplement(p) (((int)((unsigned long) (p) & 01))) +#define Fpga_Regular(p) ((Fpga_Node_t *)((unsigned long)(p) & ~01)) +#define Fpga_Not(p) ((Fpga_Node_t *)((unsigned long)(p) ^ 01)) +#define Fpga_NotCond(p,c) ((Fpga_Node_t *)((unsigned long)(p) ^ (c))) + +#define Fpga_Ref(p) +#define Fpga_Deref(p) +#define Fpga_RecursiveDeref(p,c) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== fpgaCreate.c =============================================================*/ +extern Fpga_Man_t * Fpga_ManCreate( int nInputs, int nOutputs, int fVerbose ); +extern Fpga_Node_t * Fpga_NodeCreate( Fpga_Man_t * p, Fpga_Node_t * p1, Fpga_Node_t * p2 ); +extern void Fpga_ManFree( Fpga_Man_t * pMan ); +extern void Fpga_ManPrintTimeStats( Fpga_Man_t * p ); + +extern int Fpga_ManReadInputNum( Fpga_Man_t * p ); +extern int Fpga_ManReadOutputNum( Fpga_Man_t * p ); +extern Fpga_Node_t ** Fpga_ManReadInputs ( Fpga_Man_t * p ); +extern Fpga_Node_t ** Fpga_ManReadOutputs( Fpga_Man_t * p ); +extern Fpga_Node_t * Fpga_ManReadConst1 ( Fpga_Man_t * p ); +extern float * Fpga_ManReadInputArrivals( Fpga_Man_t * p ); +extern int Fpga_ManReadVerbose( Fpga_Man_t * p ); +extern float * Fpga_ManReadLutAreas( Fpga_Man_t * p ); +extern void Fpga_ManSetTimeToMap( Fpga_Man_t * p, int Time ); +extern void Fpga_ManSetTimeToNet( Fpga_Man_t * p, int Time ); +extern void Fpga_ManSetTimeTotal( Fpga_Man_t * p, int Time ); +extern void Fpga_ManSetOutputNames( Fpga_Man_t * p, char ** ppNames ); +extern void Fpga_ManSetInputArrivals( Fpga_Man_t * p, float * pArrivals ); +extern void Fpga_ManSetAreaRecovery( Fpga_Man_t * p, int fAreaRecovery ); +extern void Fpga_ManSetDelayLimit( Fpga_Man_t * p, float DelayLimit ); +extern void Fpga_ManSetAreaLimit( Fpga_Man_t * p, float AreaLimit ); +extern void Fpga_ManSetTimeLimit( Fpga_Man_t * p, float TimeLimit ); +extern void Fpga_ManSetObeyFanoutLimits( Fpga_Man_t * p, int fObeyFanoutLimits ); +extern void Fpga_ManSetNumIterations( Fpga_Man_t * p, int nNumIterations ); +extern int Fpga_ManReadFanoutViolations( Fpga_Man_t * p ); +extern void Fpga_ManSetFanoutViolations( Fpga_Man_t * p, int nVio ); +extern void Fpga_ManSetChoiceNodeNum( Fpga_Man_t * p, int nChoiceNodes ); +extern void Fpga_ManSetChoiceNum( Fpga_Man_t * p, int nChoices ); +extern void Fpga_ManSetVerbose( Fpga_Man_t * p, int fVerbose ); +extern void Fpga_ManSetSwitching( Fpga_Man_t * p, int fSwitching ); +extern void Fpga_ManSetLatchPaths( Fpga_Man_t * p, int fLatchPaths ); +extern void Fpga_ManSetLatchNum( Fpga_Man_t * p, int nLatches ); +extern void Fpga_ManSetDelayTarget( Fpga_Man_t * p, float DelayTarget ); +extern void Fpga_ManSetName( Fpga_Man_t * p, char * pFileName ); + +extern int Fpga_LibReadLutMax( Fpga_LutLib_t * pLib ); + +extern char * Fpga_NodeReadData0( Fpga_Node_t * p ); +extern Fpga_Node_t * Fpga_NodeReadData1( Fpga_Node_t * p ); +extern int Fpga_NodeReadRefs( Fpga_Node_t * p ); +extern int Fpga_NodeReadNum( Fpga_Node_t * p ); +extern int Fpga_NodeReadLevel( Fpga_Node_t * p ); +extern Fpga_Cut_t * Fpga_NodeReadCuts( Fpga_Node_t * p ); +extern Fpga_Cut_t * Fpga_NodeReadCutBest( Fpga_Node_t * p ); +extern Fpga_Node_t * Fpga_NodeReadOne( Fpga_Node_t * p ); +extern Fpga_Node_t * Fpga_NodeReadTwo( Fpga_Node_t * p ); +extern void Fpga_NodeSetLevel( Fpga_Node_t * p, Fpga_Node_t * pNode ); +extern void Fpga_NodeSetData0( Fpga_Node_t * p, char * pData ); +extern void Fpga_NodeSetData1( Fpga_Node_t * p, Fpga_Node_t * pNode ); +extern void Fpga_NodeSetArrival( Fpga_Node_t * p, float Time ); +extern void Fpga_NodeSetNextE( Fpga_Node_t * p, Fpga_Node_t * pNextE ); +extern void Fpga_NodeSetRepr( Fpga_Node_t * p, Fpga_Node_t * pRepr ); +extern void Fpga_NodeSetSwitching( Fpga_Node_t * p, float Switching ); + +extern int Fpga_NodeIsConst( Fpga_Node_t * p ); +extern int Fpga_NodeIsVar( Fpga_Node_t * p ); +extern int Fpga_NodeIsAnd( Fpga_Node_t * p ); +extern int Fpga_NodeComparePhase( Fpga_Node_t * p1, Fpga_Node_t * p2 ); + +extern int Fpga_CutReadLeavesNum( Fpga_Cut_t * p ); +extern Fpga_Node_t ** Fpga_CutReadLeaves( Fpga_Cut_t * p ); + +extern Fpga_Node_t * Fpga_NodeAnd( Fpga_Man_t * p, Fpga_Node_t * p1, Fpga_Node_t * p2 ); +extern Fpga_Node_t * Fpga_NodeOr( Fpga_Man_t * p, Fpga_Node_t * p1, Fpga_Node_t * p2 ); +extern Fpga_Node_t * Fpga_NodeExor( Fpga_Man_t * p, Fpga_Node_t * p1, Fpga_Node_t * p2 ); +extern Fpga_Node_t * Fpga_NodeMux( Fpga_Man_t * p, Fpga_Node_t * pNode, Fpga_Node_t * pNodeT, Fpga_Node_t * pNodeE ); +extern void Fpga_NodeSetChoice( Fpga_Man_t * pMan, Fpga_Node_t * pNodeOld, Fpga_Node_t * pNodeNew ); + +extern void Fpga_ManStats( Fpga_Man_t * p ); + +/*=== fpgaCore.c =============================================================*/ +extern int Fpga_Mapping( Fpga_Man_t * p ); +/*=== fpgaCut.c ===============================================================*/ +extern void Fpga_MappingCreatePiCuts( Fpga_Man_t * p ); +extern void Fpga_CutsCleanSign( Fpga_Man_t * pMan ); +/*=== fpgaCutUtils.c =============================================================*/ +extern void Fpga_CutCreateFromNode( Fpga_Man_t * p, int iRoot, int * pLeaves, int nLeaves ); +extern void Fpga_MappingSetUsedCuts( Fpga_Man_t * p ); +/*=== fpgaLib.c =============================================================*/ +extern Fpga_LutLib_t * Fpga_LutLibDup( Fpga_LutLib_t * p ); +extern int Fpga_LutLibReadVarMax( Fpga_LutLib_t * p ); +extern float * Fpga_LutLibReadLutAreas( Fpga_LutLib_t * p ); +extern float * Fpga_LutLibReadLutDelays( Fpga_LutLib_t * p ); +extern float Fpga_LutLibReadLutArea( Fpga_LutLib_t * p, int Size ); +extern float Fpga_LutLibReadLutDelay( Fpga_LutLib_t * p, int Size ); +/*=== fpgaTruth.c =============================================================*/ +extern void * Fpga_TruthsCutBdd( void * dd, Fpga_Cut_t * pCut ); +extern int Fpga_CutVolume( Fpga_Cut_t * pCut ); +/*=== fpgaUtil.c =============================================================*/ +extern int Fpga_ManCheckConsistency( Fpga_Man_t * p ); +extern void Fpga_ManCleanData0( Fpga_Man_t * pMan ); +extern Fpga_NodeVec_t * Fpga_CollectNodeTfo( Fpga_Man_t * pMan, Fpga_Node_t * pNode ); +/*=== fpga.c =============================================================*/ +extern void Fpga_SetSimpleLutLib( int nLutSize ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/map/fpga/fpgaCore.c b/abc_with_bb_support/src/map/fpga/fpgaCore.c new file mode 100644 index 000000000..5cac05a5c --- /dev/null +++ b/abc_with_bb_support/src/map/fpga/fpgaCore.c @@ -0,0 +1,188 @@ +/**CFile**************************************************************** + + FileName [fpgaCore.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Technology mapping for variable-size-LUT FPGAs.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - August 18, 2004.] + + Revision [$Id: fpgaCore.c,v 1.7 2004/10/01 23:41:04 satrajit Exp $] + +***********************************************************************/ + +#include "fpgaInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Fpga_MappingPostProcess( Fpga_Man_t * p ); + +extern int s_MappingTime; +extern int s_MappingMem; + + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs technology mapping for the given object graph.] + + Description [The object graph is stored in the mapping manager. + First, all the AND-nodes, which fanout into the POs, are collected + in the DFS fashion. Next, three steps are performed: the k-feasible + cuts are computed for each node, the truth tables are computed for + each cut, and the delay-optimal matches are assigned for each node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_Mapping( Fpga_Man_t * p ) +{ + int clk, clkTotal = clock(); + + // collect the nodes reachable from POs in the DFS order (including the choices) + p->vAnds = Fpga_MappingDfs( p, 1 ); + Fpga_ManReportChoices( p ); // recomputes levels + Fpga_MappingSetChoiceLevels( p ); + + // compute the cuts of nodes in the DFS order + clk = clock(); + Fpga_MappingCuts( p ); + p->timeCuts = clock() - clk; + + // match the truth tables to the supergates + clk = clock(); + if ( !Fpga_MappingMatches( p, 1 ) ) + return 0; + p->timeMatch = clock() - clk; + + // perform area recovery + clk = clock(); + if ( !Fpga_MappingPostProcess( p ) ) + return 0; + p->timeRecover = clock() - clk; +//PRT( "Total mapping time", clock() - clkTotal ); + + s_MappingTime = clock() - clkTotal; + s_MappingMem = Fpga_CutCountAll(p) * (sizeof(Fpga_Cut_t) - sizeof(int) * (FPGA_MAX_LEAVES - p->nVarsMax)); + + // print the AI-graph used for mapping + //Fpga_ManShow( p, "test" ); +// if ( p->fVerbose ) +// Fpga_MappingPrintOutputArrivals( p ); + if ( p->fVerbose ) + { + PRT( "Total time", clock() - clkTotal ); + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Postprocesses the mapped network for area recovery.] + + Description [This procedure assumes that the mapping is assigned. + It iterates the loop, in which the required times are computed and + the mapping is updated. It is conceptually similar to the paper: + V. Manohararajah, S. D. Brown, Z. G. Vranesic, Heuristics for area + minimization in LUT-based FPGA technology mapping. Proc. IWLS '04.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_MappingPostProcess( Fpga_Man_t * p ) +{ + int fShowSwitching = 0; + int fRecoverAreaFlow = 1; + int fRecoverArea = 1; + float aAreaTotalCur, aAreaTotalCur2; + int Iter, clk; + +//if ( p->fVerbose ) +// printf( "Best clock period = %5.2f\n", Fpga_TimeComputeArrivalMax(p) ); + + // compute area, set references, and collect nodes used in the mapping + Iter = 1; + aAreaTotalCur = Fpga_MappingSetRefsAndArea( p ); +if ( p->fVerbose ) +{ +printf( "Iteration %dD : Area = %8.1f ", Iter++, aAreaTotalCur ); +if ( fShowSwitching ) +printf( "Switch = %8.1f ", Fpga_MappingGetSwitching(p,p->vMapping) ); +else +printf( "Delay = %5.2f ", Fpga_TimeComputeArrivalMax(p) ); + +PRT( "Time", p->timeMatch ); +} + + if ( !p->fAreaRecovery ) + return 1; + + if ( fRecoverAreaFlow ) + { +clk = clock(); + // compute the required times and the fanouts + Fpga_TimeComputeRequiredGlobal( p, 1 ); + // remap topologically + Fpga_MappingMatches( p, 0 ); + // get the resulting area +// aAreaTotalCur = Fpga_MappingSetRefsAndArea( p ); + aAreaTotalCur = Fpga_MappingAreaTrav( p ); + // note that here we do not update the reference counter + // for some reason, this works better on benchmarks +if ( p->fVerbose ) +{ +printf( "Iteration %dF : Area = %8.1f ", Iter++, aAreaTotalCur ); +if ( fShowSwitching ) +printf( "Switch = %8.1f ", Fpga_MappingGetSwitching(p,p->vMapping) ); +else +printf( "Delay = %5.2f ", Fpga_TimeComputeArrivalMax(p) ); +PRT( "Time", clock() - clk ); +} + } + + // update reference counters + aAreaTotalCur2 = Fpga_MappingSetRefsAndArea( p ); + assert( aAreaTotalCur == aAreaTotalCur2 ); + + if ( fRecoverArea ) + { +clk = clock(); + // compute the required times and the fanouts + Fpga_TimeComputeRequiredGlobal( p, 0 ); + // remap topologically + if ( p->fSwitching ) + Fpga_MappingMatchesSwitch( p ); + else + Fpga_MappingMatchesArea( p ); + // get the resulting area + aAreaTotalCur = Fpga_MappingSetRefsAndArea( p ); +if ( p->fVerbose ) +{ +printf( "Iteration %d%s : Area = %8.1f ", Iter++, (p->fSwitching?"S":"A"), aAreaTotalCur ); +if ( fShowSwitching ) +printf( "Switch = %8.1f ", Fpga_MappingGetSwitching(p,p->vMapping) ); +else +printf( "Delay = %5.2f ", Fpga_TimeComputeArrivalMax(p) ); +PRT( "Time", clock() - clk ); +} + } + + p->fAreaGlo = aAreaTotalCur; + return 1; +} + + diff --git a/abc_with_bb_support/src/map/fpga/fpgaCreate.c b/abc_with_bb_support/src/map/fpga/fpgaCreate.c new file mode 100644 index 000000000..5f874fb16 --- /dev/null +++ b/abc_with_bb_support/src/map/fpga/fpgaCreate.c @@ -0,0 +1,580 @@ +/**CFile**************************************************************** + + FileName [fpgaCreate.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Technology mapping for variable-size-LUT FPGAs.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - August 18, 2004.] + + Revision [$Id: fpgaCreate.c,v 1.8 2004/09/30 21:18:09 satrajit Exp $] + +***********************************************************************/ + +#include "fpgaInt.h" +#include "main.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Fpga_TableCreate( Fpga_Man_t * p ); +static void Fpga_TableResize( Fpga_Man_t * p ); +static Fpga_Node_t * Fpga_TableLookup( Fpga_Man_t * p, Fpga_Node_t * p1, Fpga_Node_t * p2 ); + +// hash key for the structural hash table +static inline unsigned Fpga_HashKey2( Fpga_Node_t * p0, Fpga_Node_t * p1, int TableSize ) { return ((unsigned)(p0) + (unsigned)(p1) * 12582917) % TableSize; } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Reads parameters of the mapping manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_ManReadInputNum( Fpga_Man_t * p ) { return p->nInputs; } +int Fpga_ManReadOutputNum( Fpga_Man_t * p ) { return p->nOutputs; } +Fpga_Node_t ** Fpga_ManReadInputs ( Fpga_Man_t * p ) { return p->pInputs; } +Fpga_Node_t ** Fpga_ManReadOutputs( Fpga_Man_t * p ) { return p->pOutputs; } +Fpga_Node_t * Fpga_ManReadConst1 ( Fpga_Man_t * p ) { return p->pConst1; } +float * Fpga_ManReadInputArrivals( Fpga_Man_t * p ) { return p->pInputArrivals;} +int Fpga_ManReadVerbose( Fpga_Man_t * p ) { return p->fVerbose; } +float * Fpga_ManReadLutAreas( Fpga_Man_t * p ) { return p->pLutLib->pLutAreas; } +void Fpga_ManSetTimeToMap( Fpga_Man_t * p, int Time ) { p->timeToMap = Time; } +void Fpga_ManSetTimeToNet( Fpga_Man_t * p, int Time ) { p->timeToNet = Time; } +void Fpga_ManSetTimeTotal( Fpga_Man_t * p, int Time ) { p->timeTotal = Time; } +void Fpga_ManSetOutputNames( Fpga_Man_t * p, char ** ppNames ) { p->ppOutputNames = ppNames; } +void Fpga_ManSetInputArrivals( Fpga_Man_t * p, float * pArrivals ) { p->pInputArrivals = pArrivals; } +void Fpga_ManSetAreaRecovery( Fpga_Man_t * p, int fAreaRecovery ) { p->fAreaRecovery = fAreaRecovery;} +void Fpga_ManSetDelayLimit( Fpga_Man_t * p, float DelayLimit ) { p->DelayLimit = DelayLimit; } +void Fpga_ManSetAreaLimit( Fpga_Man_t * p, float AreaLimit ) { p->AreaLimit = AreaLimit; } +void Fpga_ManSetTimeLimit( Fpga_Man_t * p, float TimeLimit ) { p->TimeLimit = TimeLimit; } +void Fpga_ManSetChoiceNodeNum( Fpga_Man_t * p, int nChoiceNodes ) { p->nChoiceNodes = nChoiceNodes; } +void Fpga_ManSetChoiceNum( Fpga_Man_t * p, int nChoices ) { p->nChoices = nChoices; } +void Fpga_ManSetVerbose( Fpga_Man_t * p, int fVerbose ) { p->fVerbose = fVerbose; } +void Fpga_ManSetSwitching( Fpga_Man_t * p, int fSwitching ) { p->fSwitching = fSwitching; } +void Fpga_ManSetLatchPaths( Fpga_Man_t * p, int fLatchPaths ) { p->fLatchPaths = fLatchPaths; } +void Fpga_ManSetLatchNum( Fpga_Man_t * p, int nLatches ) { p->nLatches = nLatches; } +void Fpga_ManSetDelayTarget( Fpga_Man_t * p, float DelayTarget ) { p->DelayTarget = DelayTarget; } +void Fpga_ManSetName( Fpga_Man_t * p, char * pFileName ) { p->pFileName = pFileName; } + +/**Function************************************************************* + + Synopsis [Reads the parameters of the LUT library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_LibReadLutMax( Fpga_LutLib_t * pLib ) { return pLib->LutMax; } + +/**Function************************************************************* + + Synopsis [Reads parameters of the mapping node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Fpga_NodeReadData0( Fpga_Node_t * p ) { return p->pData0; } +Fpga_Node_t * Fpga_NodeReadData1( Fpga_Node_t * p ) { return p->pLevel; } +int Fpga_NodeReadRefs( Fpga_Node_t * p ) { return p->nRefs; } +int Fpga_NodeReadNum( Fpga_Node_t * p ) { return p->Num; } +int Fpga_NodeReadLevel( Fpga_Node_t * p ) { return Fpga_Regular(p)->Level; } +Fpga_Cut_t * Fpga_NodeReadCuts( Fpga_Node_t * p ) { return p->pCuts; } +Fpga_Cut_t * Fpga_NodeReadCutBest( Fpga_Node_t * p ) { return p->pCutBest; } +Fpga_Node_t * Fpga_NodeReadOne( Fpga_Node_t * p ) { return p->p1; } +Fpga_Node_t * Fpga_NodeReadTwo( Fpga_Node_t * p ) { return p->p2; } +void Fpga_NodeSetData0( Fpga_Node_t * p, char * pData ) { p->pData0 = pData; } +void Fpga_NodeSetData1( Fpga_Node_t * p, Fpga_Node_t * pNode ) { p->pLevel = pNode; } +void Fpga_NodeSetNextE( Fpga_Node_t * p, Fpga_Node_t * pNextE ) { p->pNextE = pNextE; } +void Fpga_NodeSetRepr( Fpga_Node_t * p, Fpga_Node_t * pRepr ) { p->pRepr = pRepr; } +void Fpga_NodeSetSwitching( Fpga_Node_t * p, float Switching ) { p->Switching = Switching; } + +/**Function************************************************************* + + Synopsis [Checks the type of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_NodeIsConst( Fpga_Node_t * p ) { return (Fpga_Regular(p))->Num == -1; } +int Fpga_NodeIsVar( Fpga_Node_t * p ) { return (Fpga_Regular(p))->p1 == NULL && (Fpga_Regular(p))->Num >= 0; } +int Fpga_NodeIsAnd( Fpga_Node_t * p ) { return (Fpga_Regular(p))->p1 != NULL; } +int Fpga_NodeComparePhase( Fpga_Node_t * p1, Fpga_Node_t * p2 ) { assert( !Fpga_IsComplement(p1) ); assert( !Fpga_IsComplement(p2) ); return p1->fInv ^ p2->fInv; } + +/**Function************************************************************* + + Synopsis [Reads parameters from the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_CutReadLeavesNum( Fpga_Cut_t * p ) { return p->nLeaves; } +Fpga_Node_t ** Fpga_CutReadLeaves( Fpga_Cut_t * p ) { return p->ppLeaves; } + + +/**Function************************************************************* + + Synopsis [Create the mapping manager.] + + Description [The number of inputs and outputs is assumed to be + known is advance. It is much simpler to have them fixed upfront. + When it comes to representing the object graph in the form of + AIG, the resulting manager is similar to the regular AIG manager, + except that it does not use reference counting (and therefore + does not have garbage collections). It does have table resizing. + The data structure is more flexible to represent additional + information needed for mapping.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Man_t * Fpga_ManCreate( int nInputs, int nOutputs, int fVerbose ) +{ + Fpga_Man_t * p; + int i; + + // start the manager + p = ALLOC( Fpga_Man_t, 1 ); + memset( p, 0, sizeof(Fpga_Man_t) ); + p->pLutLib = Abc_FrameReadLibLut(); + p->nVarsMax = p->pLutLib->LutMax; + p->fVerbose = fVerbose; + p->fAreaRecovery = 1; + p->fEpsilon = (float)0.001; + + Fpga_TableCreate( p ); +//if ( p->fVerbose ) +// printf( "Node = %d (%d) bytes. Cut = %d bytes.\n", sizeof(Fpga_Node_t), FPGA_NUM_BYTES(sizeof(Fpga_Node_t)), sizeof(Fpga_Cut_t) ); + p->mmNodes = Extra_MmFixedStart( FPGA_NUM_BYTES(sizeof(Fpga_Node_t)) ); + p->mmCuts = Extra_MmFixedStart( sizeof(Fpga_Cut_t) ); + + assert( p->nVarsMax > 0 ); +// Fpga_MappingSetupTruthTables( p->uTruths ); + + // make sure the constant node will get index -1 + p->nNodes = -1; + // create the constant node + p->pConst1 = Fpga_NodeCreate( p, NULL, NULL ); + p->vNodesAll = Fpga_NodeVecAlloc( 1000 ); + p->vMapping = Fpga_NodeVecAlloc( 1000 ); + + // create the PI nodes + p->nInputs = nInputs; + p->pInputs = ALLOC( Fpga_Node_t *, nInputs ); + for ( i = 0; i < nInputs; i++ ) + p->pInputs[i] = Fpga_NodeCreate( p, NULL, NULL ); + + // create the place for the output nodes + p->nOutputs = nOutputs; + p->pOutputs = ALLOC( Fpga_Node_t *, nOutputs ); + memset( p->pOutputs, 0, sizeof(Fpga_Node_t *) * nOutputs ); + return p; +} + +/**Function************************************************************* + + Synopsis [Deallocates the mapping manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_ManFree( Fpga_Man_t * p ) +{ +// Fpga_ManStats( p ); +// int i; +// for ( i = 0; i < p->vNodesAll->nSize; i++ ) +// Fpga_NodeVecFree( p->vNodesAll->pArray[i]->vFanouts ); +// Fpga_NodeVecFree( p->pConst1->vFanouts ); + if ( p->vMapping ) + Fpga_NodeVecFree( p->vMapping ); + if ( p->vAnds ) + Fpga_NodeVecFree( p->vAnds ); + if ( p->vNodesAll ) + Fpga_NodeVecFree( p->vNodesAll ); + Extra_MmFixedStop( p->mmNodes ); + Extra_MmFixedStop( p->mmCuts ); + FREE( p->ppOutputNames ); + FREE( p->pInputArrivals ); + FREE( p->pInputs ); + FREE( p->pOutputs ); + FREE( p->pBins ); + FREE( p ); +} + + +/**Function************************************************************* + + Synopsis [Prints runtime statistics of the mapping manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_ManPrintTimeStats( Fpga_Man_t * p ) +{ + extern char * pNetName; + extern int TotalLuts; +// FILE * pTable; + + +/* + pTable = fopen( "stats.txt", "a+" ); + fprintf( pTable, "%s ", pNetName ); + fprintf( pTable, "%.0f ", p->fRequiredGlo ); +// fprintf( pTable, "%.0f ", p->fAreaGlo );//+ (float)nOutputInvs ); + fprintf( pTable, "%.0f ", (float)TotalLuts ); + fprintf( pTable, "%4.2f\n", (float)(p->timeTotal-p->timeToMap)/(float)(CLOCKS_PER_SEC) ); + fclose( pTable ); +*/ + +// printf( "N-canonical = %d. Matchings = %d. ", p->nCanons, p->nMatches ); +// printf( "Choice nodes = %d. Choices = %d.\n", p->nChoiceNodes, p->nChoices ); + PRT( "ToMap", p->timeToMap ); + PRT( "Cuts ", p->timeCuts ); + PRT( "Match", p->timeMatch ); + PRT( "Area ", p->timeRecover ); + PRT( "ToNet", p->timeToNet ); + PRT( "TOTAL", p->timeTotal ); + if ( p->time1 ) { PRT( "time1", p->time1 ); } + if ( p->time2 ) { PRT( "time2", p->time2 ); } +} + +/**Function************************************************************* + + Synopsis [Creates a new node.] + + Description [This procedure should be called to create the constant + node and the PI nodes first.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Node_t * Fpga_NodeCreate( Fpga_Man_t * p, Fpga_Node_t * p1, Fpga_Node_t * p2 ) +{ + Fpga_Node_t * pNode; + // create the node + pNode = (Fpga_Node_t *)Extra_MmFixedEntryFetch( p->mmNodes ); + memset( pNode, 0, sizeof(Fpga_Node_t) ); + // set very large required time + pNode->tRequired = FPGA_FLOAT_LARGE; + pNode->aEstFanouts = -1; + pNode->p1 = p1; + pNode->p2 = p2; + // set the number of this node + pNode->Num = p->nNodes++; + // place to store the fanouts +// pNode->vFanouts = Fpga_NodeVecAlloc( 5 ); + // store this node in the internal array + if ( pNode->Num >= 0 ) + Fpga_NodeVecPush( p->vNodesAll, pNode ); + else + pNode->fInv = 1; + // set the level of this node + if ( p1 ) + { +#ifdef FPGA_ALLOCATE_FANOUT + // create the fanout info + Fpga_NodeAddFaninFanout( Fpga_Regular(p1), pNode ); + Fpga_NodeAddFaninFanout( Fpga_Regular(p2), pNode ); +#endif + // compute the level + pNode->Level = 1 + FPGA_MAX(Fpga_Regular(p1)->Level, Fpga_Regular(p2)->Level); + pNode->fInv = Fpga_NodeIsSimComplement(p1) & Fpga_NodeIsSimComplement(p2); + } + // reference the inputs + if ( p1 ) Fpga_NodeRef(p1); + if ( p2 ) Fpga_NodeRef(p2); + return pNode; +} + +/**Function************************************************************* + + Synopsis [Create the unique table of AND gates.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_TableCreate( Fpga_Man_t * pMan ) +{ + assert( pMan->pBins == NULL ); + pMan->nBins = Cudd_Prime(50000); + pMan->pBins = ALLOC( Fpga_Node_t *, pMan->nBins ); + memset( pMan->pBins, 0, sizeof(Fpga_Node_t *) * pMan->nBins ); + pMan->nNodes = 0; +} + +/**Function************************************************************* + + Synopsis [Looks up the AND2 node in the unique table.] + + Description [This procedure implements one-level hashing. All the nodes + are hashed by their children. If the node with the same children was already + created, it is returned by the call to this procedure. If it does not exist, + this procedure creates a new node with these children. ] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Node_t * Fpga_TableLookup( Fpga_Man_t * pMan, Fpga_Node_t * p1, Fpga_Node_t * p2 ) +{ + Fpga_Node_t * pEnt; + unsigned Key; + + if ( p1 == p2 ) + return p1; + if ( p1 == Fpga_Not(p2) ) + return Fpga_Not(pMan->pConst1); + if ( Fpga_NodeIsConst(p1) ) + { + if ( p1 == pMan->pConst1 ) + return p2; + return Fpga_Not(pMan->pConst1); + } + if ( Fpga_NodeIsConst(p2) ) + { + if ( p2 == pMan->pConst1 ) + return p1; + return Fpga_Not(pMan->pConst1); + } + + if ( Fpga_Regular(p1)->Num > Fpga_Regular(p2)->Num ) + pEnt = p1, p1 = p2, p2 = pEnt; + + Key = Fpga_HashKey2( p1, p2, pMan->nBins ); + for ( pEnt = pMan->pBins[Key]; pEnt; pEnt = pEnt->pNext ) + if ( pEnt->p1 == p1 && pEnt->p2 == p2 ) + return pEnt; + // resize the table + if ( pMan->nNodes >= 2 * pMan->nBins ) + { + Fpga_TableResize( pMan ); + Key = Fpga_HashKey2( p1, p2, pMan->nBins ); + } + // create the new node + pEnt = Fpga_NodeCreate( pMan, p1, p2 ); + // add the node to the corresponding linked list in the table + pEnt->pNext = pMan->pBins[Key]; + pMan->pBins[Key] = pEnt; + return pEnt; +} + + +/**Function************************************************************* + + Synopsis [Resizes the table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_TableResize( Fpga_Man_t * pMan ) +{ + Fpga_Node_t ** pBinsNew; + Fpga_Node_t * pEnt, * pEnt2; + int nBinsNew, Counter, i, clk; + unsigned Key; + +clk = clock(); + // get the new table size + nBinsNew = Cudd_Prime(2 * pMan->nBins); + // allocate a new array + pBinsNew = ALLOC( Fpga_Node_t *, nBinsNew ); + memset( pBinsNew, 0, sizeof(Fpga_Node_t *) * nBinsNew ); + // rehash the entries from the old table + Counter = 0; + for ( i = 0; i < pMan->nBins; i++ ) + for ( pEnt = pMan->pBins[i], pEnt2 = pEnt? pEnt->pNext: NULL; pEnt; + pEnt = pEnt2, pEnt2 = pEnt? pEnt->pNext: NULL ) + { + Key = Fpga_HashKey2( pEnt->p1, pEnt->p2, nBinsNew ); + pEnt->pNext = pBinsNew[Key]; + pBinsNew[Key] = pEnt; + Counter++; + } + assert( Counter == pMan->nNodes - pMan->nInputs ); + if ( pMan->fVerbose ) + { +// printf( "Increasing the unique table size from %6d to %6d. ", pMan->nBins, nBinsNew ); +// PRT( "Time", clock() - clk ); + } + // replace the table and the parameters + free( pMan->pBins ); + pMan->pBins = pBinsNew; + pMan->nBins = nBinsNew; +} + + + +/**Function************************************************************* + + Synopsis [Elementary AND operation on the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Node_t * Fpga_NodeAnd( Fpga_Man_t * p, Fpga_Node_t * p1, Fpga_Node_t * p2 ) +{ + Fpga_Node_t * pNode; + pNode = Fpga_TableLookup( p, p1, p2 ); + return pNode; +} + +/**Function************************************************************* + + Synopsis [Elementary OR operation on the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Node_t * Fpga_NodeOr( Fpga_Man_t * p, Fpga_Node_t * p1, Fpga_Node_t * p2 ) +{ + Fpga_Node_t * pNode; + pNode = Fpga_Not( Fpga_TableLookup( p, Fpga_Not(p1), Fpga_Not(p2) ) ); + return pNode; +} + +/**Function************************************************************* + + Synopsis [Elementary EXOR operation on the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Node_t * Fpga_NodeExor( Fpga_Man_t * p, Fpga_Node_t * p1, Fpga_Node_t * p2 ) +{ + return Fpga_NodeMux( p, p1, Fpga_Not(p2), p2 ); +} + +/**Function************************************************************* + + Synopsis [Elementary MUX operation on the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Node_t * Fpga_NodeMux( Fpga_Man_t * p, Fpga_Node_t * pC, Fpga_Node_t * pT, Fpga_Node_t * pE ) +{ + Fpga_Node_t * pAnd1, * pAnd2, * pRes; + pAnd1 = Fpga_TableLookup( p, pC, pT ); + pAnd2 = Fpga_TableLookup( p, Fpga_Not(pC), pE ); + pRes = Fpga_NodeOr( p, pAnd1, pAnd2 ); + return pRes; +} + + +/**Function************************************************************* + + Synopsis [Sets the node to be equivalent to the given one.] + + Description [This procedure is a work-around for the equivalence check. + Does not verify the equivalence. Use at the user's risk.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_NodeSetChoice( Fpga_Man_t * pMan, Fpga_Node_t * pNodeOld, Fpga_Node_t * pNodeNew ) +{ + pNodeNew->pNextE = pNodeOld->pNextE; + pNodeOld->pNextE = pNodeNew; + pNodeNew->pRepr = pNodeOld; +} + + + +/**Function************************************************************* + + Synopsis [Prints some interesting stats.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_ManStats( Fpga_Man_t * p ) +{ + FILE * pTable; + pTable = fopen( "stats.txt", "a+" ); + fprintf( pTable, "%s ", p->pFileName ); + fprintf( pTable, "%4d ", p->nInputs - p->nLatches ); + fprintf( pTable, "%4d ", p->nOutputs - p->nLatches ); + fprintf( pTable, "%4d ", p->nLatches ); + fprintf( pTable, "%7d ", p->vAnds->nSize ); + fprintf( pTable, "%7d ", Fpga_CutCountAll(p) ); + fprintf( pTable, "%2d\n", (int)p->fRequiredGlo ); + fclose( pTable ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/map/fpga/fpgaCut.c b/abc_with_bb_support/src/map/fpga/fpgaCut.c new file mode 100644 index 000000000..3b2a006d9 --- /dev/null +++ b/abc_with_bb_support/src/map/fpga/fpgaCut.c @@ -0,0 +1,1159 @@ +/**CFile**************************************************************** + + FileName [fpgaCut.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - August 18, 2004.] + + Revision [$Id: fpgaCut.c,v 1.3 2004/07/06 04:55:57 alanmi Exp $] + +***********************************************************************/ + +#include "fpgaInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Fpga_CutTableStrutct_t Fpga_CutTable_t; +struct Fpga_CutTableStrutct_t +{ + Fpga_Cut_t ** pBins; // the table used for linear probing + int nBins; // the size of the table + int * pCuts; // the array of cuts currently stored + int nCuts; // the number of cuts currently stored + Fpga_Cut_t ** pArray; // the temporary array of cuts + Fpga_Cut_t ** pCuts1; // the temporary array of cuts + Fpga_Cut_t ** pCuts2; // the temporary array of cuts +}; + +// the largest number of cuts considered +//#define FPGA_CUTS_MAX_COMPUTE 500 +#define FPGA_CUTS_MAX_COMPUTE 2000 +// the largest number of cuts used +//#define FPGA_CUTS_MAX_USE 200 +#define FPGA_CUTS_MAX_USE 1000 + +// primes used to compute the hash key +static int s_HashPrimes[10] = { 109, 499, 557, 619, 631, 709, 797, 881, 907, 991 }; + +static int bit_count[256] = { + 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 +}; + +#define FPGA_COUNT_ONES(u) (bit_count[(u)&255]+bit_count[((u)>>8)&255]+bit_count[((u)>>16)&255]+bit_count[(u)>>24]) + +static Fpga_Cut_t * Fpga_CutCompute( Fpga_Man_t * p, Fpga_CutTable_t * pTable, Fpga_Node_t * pNode ); +static void Fpga_CutFilter( Fpga_Man_t * p, Fpga_Node_t * pNode ); +static Fpga_Cut_t * Fpga_CutMergeLists( Fpga_Man_t * p, Fpga_CutTable_t * pTable, Fpga_Cut_t * pList1, Fpga_Cut_t * pList2, int fComp1, int fComp2, int fPivot1, int fPivot2 ); +static int Fpga_CutMergeTwo( Fpga_Cut_t * pCut1, Fpga_Cut_t * pCut2, Fpga_Node_t * ppNodes[], int nNodesMax ); +static Fpga_Cut_t * Fpga_CutUnionLists( Fpga_Cut_t * pList1, Fpga_Cut_t * pList2 ); +static int Fpga_CutBelongsToList( Fpga_Cut_t * pList, Fpga_Node_t * ppNodes[], int nNodes ); +extern Fpga_Cut_t * Fpga_CutAlloc( Fpga_Man_t * p ); +extern int Fpga_CutCountAll( Fpga_Man_t * pMan ); + +static void Fpga_CutListPrint( Fpga_Man_t * pMan, Fpga_Node_t * pRoot ); +static void Fpga_CutListPrint2( Fpga_Man_t * pMan, Fpga_Node_t * pRoot ); +static void Fpga_CutPrint_( Fpga_Man_t * pMan, Fpga_Cut_t * pCut, Fpga_Node_t * pRoot ); + +static Fpga_CutTable_t * Fpga_CutTableStart( Fpga_Man_t * pMan ); +static void Fpga_CutTableStop( Fpga_CutTable_t * p ); +static unsigned Fpga_CutTableHash( Fpga_Node_t * ppNodes[], int nNodes ); +static int Fpga_CutTableLookup( Fpga_CutTable_t * p, Fpga_Node_t * ppNodes[], int nNodes ); +static Fpga_Cut_t * Fpga_CutTableConsider( Fpga_Man_t * pMan, Fpga_CutTable_t * p, Fpga_Node_t * ppNodes[], int nNodes ); +static void Fpga_CutTableRestart( Fpga_CutTable_t * p ); + +static int Fpga_CutSortCutsCompare( Fpga_Cut_t ** pC1, Fpga_Cut_t ** pC2 ); +static Fpga_Cut_t * Fpga_CutSortCuts( Fpga_Man_t * pMan, Fpga_CutTable_t * p, Fpga_Cut_t * pList ); +static int Fpga_CutList2Array( Fpga_Cut_t ** pArray, Fpga_Cut_t * pList ); +static Fpga_Cut_t * Fpga_CutArray2List( Fpga_Cut_t ** pArray, int nCuts ); + + +// iterator through all the cuts of the list +#define Fpga_ListForEachCut( pList, pCut ) \ + for ( pCut = pList; \ + pCut; \ + pCut = pCut->pNext ) +#define Fpga_ListForEachCutSafe( pList, pCut, pCut2 ) \ + for ( pCut = pList, \ + pCut2 = pCut? pCut->pNext: NULL; \ + pCut; \ + pCut = pCut2, \ + pCut2 = pCut? pCut->pNext: NULL ) + + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes the cuts for each node in the object graph.] + + Description [The cuts are computed in one sweep over the mapping graph. + First, the elementary cuts, which include the node itself, are assigned + to the PI nodes. The internal nodes are considered in the DFS order. + Each node is two-input AND-gate. So to compute the cuts at a node, we + need to merge the sets of cuts of its two predecessors. The merged set + contains only unique cuts with the number of inputs equal to k or less. + Finally, the elementary cut, composed of the node itself, is added to + the set of cuts for the node. + + This procedure is pretty fast for 5-feasible cuts, but it dramatically + slows down on some "dense" networks when computing 6-feasible cuts. + The problem is that there are too many cuts in this case. We should + think how to heuristically trim the number of cuts in such cases, + to have reasonable runtime.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_MappingCuts( Fpga_Man_t * p ) +{ + ProgressBar * pProgress; + Fpga_CutTable_t * pTable; + Fpga_Node_t * pNode; + int nCuts, nNodes, i; + int clk = clock(); + + // set the elementary cuts for the PI variables + assert( p->nVarsMax > 1 && p->nVarsMax < 11 ); + Fpga_MappingCreatePiCuts( p ); + + // compute the cuts for the internal nodes + nNodes = p->vAnds->nSize; + pProgress = Extra_ProgressBarStart( stdout, nNodes ); + pTable = Fpga_CutTableStart( p ); + for ( i = 0; i < nNodes; i++ ) + { + Extra_ProgressBarUpdate( pProgress, i, "Cuts ..." ); + pNode = p->vAnds->pArray[i]; + if ( !Fpga_NodeIsAnd( pNode ) ) + continue; + Fpga_CutCompute( p, pTable, pNode ); + } + Extra_ProgressBarStop( pProgress ); + Fpga_CutTableStop( pTable ); + + // report the stats + if ( p->fVerbose ) + { + nCuts = Fpga_CutCountAll(p); + printf( "Nodes = %6d. Total %d-cuts = %d. Cuts per node = %.1f. ", + p->nNodes, p->nVarsMax, nCuts, ((float)nCuts)/p->nNodes ); + PRT( "Time", clock() - clk ); + } + + // print the cuts for the first primary output +// Fpga_CutListPrint( p, Fpga_Regular(p->pOutputs[0]) ); +} + +/**Function************************************************************* + + Synopsis [Performs technology mapping for variable-size-LUTs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_MappingCreatePiCuts( Fpga_Man_t * p ) +{ + Fpga_Cut_t * pCut; + int i; + + // set the elementary cuts for the PI variables + for ( i = 0; i < p->nInputs; i++ ) + { + pCut = Fpga_CutAlloc( p ); + pCut->nLeaves = 1; + pCut->ppLeaves[0] = p->pInputs[i]; + pCut->uSign = (1 << (i%31)); + p->pInputs[i]->pCuts = pCut; + p->pInputs[i]->pCutBest = pCut; + // set the input arrival times +// p->pInputs[i]->pCut[1]->tArrival = p->pInputArrivals[i]; + } +} + +/**Function************************************************************* + + Synopsis [Computes the cuts for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Cut_t * Fpga_CutCompute( Fpga_Man_t * p, Fpga_CutTable_t * pTable, Fpga_Node_t * pNode ) +{ + Fpga_Node_t * pTemp; + Fpga_Cut_t * pList, * pList1, * pList2; + Fpga_Cut_t * pCut; + int fTree = 0; + int fPivot1 = fTree && (Fpga_NodeReadRef(pNode->p1)>2); + int fPivot2 = fTree && (Fpga_NodeReadRef(pNode->p2)>2); + + // if the cuts are computed return them + if ( pNode->pCuts ) + return pNode->pCuts; + + // compute the cuts for the children + pList1 = Fpga_Regular(pNode->p1)->pCuts; + pList2 = Fpga_Regular(pNode->p2)->pCuts; + // merge the lists + pList = Fpga_CutMergeLists( p, pTable, pList1, pList2, + Fpga_IsComplement(pNode->p1), Fpga_IsComplement(pNode->p2), + fPivot1, fPivot2 ); + // if there are functionally equivalent nodes, union them with this list + assert( pList ); + // only add to the list of cuts if the node is a representative one + if ( pNode->pRepr == NULL ) + { + for ( pTemp = pNode->pNextE; pTemp; pTemp = pTemp->pNextE ) + { + assert( pTemp->pCuts ); + pList = Fpga_CutUnionLists( pList, pTemp->pCuts ); + assert( pTemp->pCuts ); + pList = Fpga_CutSortCuts( p, pTable, pList ); + } + } + // add the new cut + pCut = Fpga_CutAlloc( p ); + pCut->nLeaves = 1; + pCut->ppLeaves[0] = pNode; + pCut->uSign = (1 << (pNode->Num%31)); + pCut->fLevel = (float)pCut->ppLeaves[0]->Level; + // append (it is important that the elementary cut is appended first) + pCut->pNext = pList; + // set at the node + pNode->pCuts = pCut; + // remove the dominated cuts +// Fpga_CutFilter( p, pNode ); + // set the phase correctly + if ( pNode->pRepr && Fpga_NodeComparePhase(pNode, pNode->pRepr) ) + { + Fpga_ListForEachCut( pNode->pCuts, pCut ) + pCut->Phase = 1; + } + + +/* + { + Fpga_Cut_t * pPrev; + int i, Counter = 0; + for ( pCut = pNode->pCuts->pNext, pPrev = pNode->pCuts; pCut; pCut = pCut->pNext ) + { + for ( i = 0; i < pCut->nLeaves; i++ ) + if ( pCut->ppLeaves[i]->Level >= pNode->Level ) + break; + if ( i != pCut->nLeaves ) + pPrev->pNext = pCut->pNext; + else + pPrev = pCut; + } + } + { + int i, Counter = 0;; + for ( pCut = pNode->pCuts->pNext; pCut; pCut = pCut->pNext ) + for ( i = 0; i < pCut->nLeaves; i++ ) + Counter += (pCut->ppLeaves[i]->Level >= pNode->Level); + if ( Counter ) + printf( " %d", Counter ); + } +*/ + + return pCut; +} + +/**Function************************************************************* + + Synopsis [Filter the cuts using dominance.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_CutFilter( Fpga_Man_t * p, Fpga_Node_t * pNode ) +{ + Fpga_Cut_t * pTemp, * pPrev, * pCut, * pCut2; + int i, k, Counter; + + Counter = 0; + pPrev = pNode->pCuts; + Fpga_ListForEachCutSafe( pNode->pCuts->pNext, pCut, pCut2 ) + { + // go through all the previous cuts up to pCut + for ( pTemp = pNode->pCuts->pNext; pTemp != pCut; pTemp = pTemp->pNext ) + { + // check if every node in pTemp is contained in pCut + for ( i = 0; i < pTemp->nLeaves; i++ ) + { + for ( k = 0; k < pCut->nLeaves; k++ ) + if ( pTemp->ppLeaves[i] == pCut->ppLeaves[k] ) + break; + if ( k == pCut->nLeaves ) // node i in pTemp is not contained in pCut + break; + } + if ( i == pTemp->nLeaves ) // every node in pTemp is contained in pCut + { + Counter++; + break; + } + } + if ( pTemp != pCut ) // pTemp contain pCut + { + pPrev->pNext = pCut->pNext; // skip pCut + // recycle pCut + Fpga_CutFree( p, pCut ); + } + else + pPrev = pCut; + } +// printf( "Dominated = %3d. \n", Counter ); +} + + +/**Function************************************************************* + + Synopsis [Merges two lists of cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Cut_t * Fpga_CutMergeLists( Fpga_Man_t * p, Fpga_CutTable_t * pTable, + Fpga_Cut_t * pList1, Fpga_Cut_t * pList2, int fComp1, int fComp2, int fPivot1, int fPivot2 ) +{ + Fpga_Node_t * ppNodes[FPGA_MAX_LEAVES]; + Fpga_Cut_t * pListNew, ** ppListNew, * pLists[FPGA_MAX_LEAVES+1] = { NULL }; + Fpga_Cut_t * pCut, * pPrev, * pTemp1, * pTemp2; + int nNodes, Counter, i; + Fpga_Cut_t ** ppArray1, ** ppArray2, ** ppArray3; + int nCuts1, nCuts2, nCuts3, k, fComp3; + + ppArray1 = pTable->pCuts1; + ppArray2 = pTable->pCuts2; + nCuts1 = Fpga_CutList2Array( ppArray1, pList1 ); + nCuts2 = Fpga_CutList2Array( ppArray2, pList2 ); + if ( fPivot1 ) + nCuts1 = 1; + if ( fPivot2 ) + nCuts2 = 1; + // swap the lists based on their length + if ( nCuts1 > nCuts2 ) + { + ppArray3 = ppArray1; + ppArray1 = ppArray2; + ppArray2 = ppArray3; + + nCuts3 = nCuts1; + nCuts1 = nCuts2; + nCuts2 = nCuts3; + + fComp3 = fComp1; + fComp1 = fComp2; + fComp2 = fComp3; + } + // pList1 is shorter or equal length compared to pList2 + + // prepare the manager for the cut computation + Fpga_CutTableRestart( pTable ); + // go through the cut pairs + Counter = 0; +// for ( pTemp1 = pList1; pTemp1; pTemp1 = fPivot1? NULL: pTemp1->pNext ) +// for ( pTemp2 = pList2; pTemp2; pTemp2 = fPivot2? NULL: pTemp2->pNext ) + for ( i = 0; i < nCuts1; i++ ) + { + for ( k = 0; k <= i; k++ ) + { + pTemp1 = ppArray1[i]; + pTemp2 = ppArray2[k]; + + if ( pTemp1->nLeaves == p->nVarsMax && pTemp2->nLeaves == p->nVarsMax ) + { + if ( pTemp1->ppLeaves[0] != pTemp2->ppLeaves[0] ) + continue; + if ( pTemp1->ppLeaves[1] != pTemp2->ppLeaves[1] ) + continue; + } + + // check if k-feasible cut exists + nNodes = Fpga_CutMergeTwo( pTemp1, pTemp2, ppNodes, p->nVarsMax ); + if ( nNodes == 0 ) + continue; + // consider the cut for possible addition to the set of new cuts + pCut = Fpga_CutTableConsider( p, pTable, ppNodes, nNodes ); + if ( pCut == NULL ) + continue; + // add data to the cut + pCut->pOne = Fpga_CutNotCond( pTemp1, fComp1 ); + pCut->pTwo = Fpga_CutNotCond( pTemp2, fComp2 ); + // create the signature + pCut->uSign = pTemp1->uSign | pTemp2->uSign; + // add it to the corresponding list + pCut->pNext = pLists[pCut->nLeaves]; + pLists[pCut->nLeaves] = pCut; + // count this cut and quit if limit is reached + Counter++; + if ( Counter == FPGA_CUTS_MAX_COMPUTE ) + goto QUITS; + } + for ( k = 0; k < i; k++ ) + { + pTemp1 = ppArray1[k]; + pTemp2 = ppArray2[i]; + + if ( pTemp1->nLeaves == p->nVarsMax && pTemp2->nLeaves == p->nVarsMax ) + { + if ( pTemp1->ppLeaves[0] != pTemp2->ppLeaves[0] ) + continue; + if ( pTemp1->ppLeaves[1] != pTemp2->ppLeaves[1] ) + continue; + } + + + // check if k-feasible cut exists + nNodes = Fpga_CutMergeTwo( pTemp1, pTemp2, ppNodes, p->nVarsMax ); + if ( nNodes == 0 ) + continue; + // consider the cut for possible addition to the set of new cuts + pCut = Fpga_CutTableConsider( p, pTable, ppNodes, nNodes ); + if ( pCut == NULL ) + continue; + // add data to the cut + pCut->pOne = Fpga_CutNotCond( pTemp1, fComp1 ); + pCut->pTwo = Fpga_CutNotCond( pTemp2, fComp2 ); + // create the signature + pCut->uSign = pTemp1->uSign | pTemp2->uSign; + // add it to the corresponding list + pCut->pNext = pLists[pCut->nLeaves]; + pLists[pCut->nLeaves] = pCut; + // count this cut and quit if limit is reached + Counter++; + if ( Counter == FPGA_CUTS_MAX_COMPUTE ) + goto QUITS; + } + } + // consider the rest of them + for ( i = nCuts1; i < nCuts2; i++ ) + for ( k = 0; k < nCuts1; k++ ) + { + pTemp1 = ppArray1[k]; + pTemp2 = ppArray2[i]; + + if ( pTemp1->nLeaves == p->nVarsMax && pTemp2->nLeaves == p->nVarsMax ) + { + if ( pTemp1->ppLeaves[0] != pTemp2->ppLeaves[0] ) + continue; + if ( pTemp1->ppLeaves[1] != pTemp2->ppLeaves[1] ) + continue; + if ( pTemp1->ppLeaves[2] != pTemp2->ppLeaves[2] ) + continue; + } + + + // check if k-feasible cut exists + nNodes = Fpga_CutMergeTwo( pTemp1, pTemp2, ppNodes, p->nVarsMax ); + if ( nNodes == 0 ) + continue; + // consider the cut for possible addition to the set of new cuts + pCut = Fpga_CutTableConsider( p, pTable, ppNodes, nNodes ); + if ( pCut == NULL ) + continue; + // add data to the cut + pCut->pOne = Fpga_CutNotCond( pTemp1, fComp1 ); + pCut->pTwo = Fpga_CutNotCond( pTemp2, fComp2 ); + // create the signature + pCut->uSign = pTemp1->uSign | pTemp2->uSign; + // add it to the corresponding list + pCut->pNext = pLists[pCut->nLeaves]; + pLists[pCut->nLeaves] = pCut; + // count this cut and quit if limit is reached + Counter++; + if ( Counter == FPGA_CUTS_MAX_COMPUTE ) + goto QUITS; + } +QUITS : + // combine all the lists into one + pListNew = NULL; + ppListNew = &pListNew; + for ( i = 1; i <= p->nVarsMax; i++ ) + { + if ( pLists[i] == NULL ) + continue; + // find the last entry + for ( pPrev = pLists[i], pCut = pPrev->pNext; pCut; + pPrev = pCut, pCut = pCut->pNext ); + // connect these lists + *ppListNew = pLists[i]; + ppListNew = &pPrev->pNext; + } + *ppListNew = NULL; + // sort the cuts by arrival times and use only the first FPGA_CUTS_MAX_USE + pListNew = Fpga_CutSortCuts( p, pTable, pListNew ); + return pListNew; +} + + +/**Function************************************************************* + + Synopsis [Merges two lists of cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Cut_t * Fpga_CutMergeLists2( Fpga_Man_t * p, Fpga_CutTable_t * pTable, + Fpga_Cut_t * pList1, Fpga_Cut_t * pList2, int fComp1, int fComp2, int fPivot1, int fPivot2 ) +{ + Fpga_Node_t * ppNodes[FPGA_MAX_LEAVES]; + Fpga_Cut_t * pListNew, ** ppListNew, * pLists[FPGA_MAX_LEAVES+1] = { NULL }; + Fpga_Cut_t * pCut, * pPrev, * pTemp1, * pTemp2; + int nNodes, Counter, i; + + // prepare the manager for the cut computation + Fpga_CutTableRestart( pTable ); + // go through the cut pairs + Counter = 0; + for ( pTemp1 = pList1; pTemp1; pTemp1 = fPivot1? NULL: pTemp1->pNext ) + for ( pTemp2 = pList2; pTemp2; pTemp2 = fPivot2? NULL: pTemp2->pNext ) + { + // check if k-feasible cut exists + nNodes = Fpga_CutMergeTwo( pTemp1, pTemp2, ppNodes, p->nVarsMax ); + if ( nNodes == 0 ) + continue; + // consider the cut for possible addition to the set of new cuts + pCut = Fpga_CutTableConsider( p, pTable, ppNodes, nNodes ); + if ( pCut == NULL ) + continue; + // add data to the cut + pCut->pOne = Fpga_CutNotCond( pTemp1, fComp1 ); + pCut->pTwo = Fpga_CutNotCond( pTemp2, fComp2 ); + // add it to the corresponding list + pCut->pNext = pLists[pCut->nLeaves]; + pLists[pCut->nLeaves] = pCut; + // count this cut and quit if limit is reached + Counter++; + if ( Counter == FPGA_CUTS_MAX_COMPUTE ) + goto QUITS; + } +QUITS : + // combine all the lists into one + pListNew = NULL; + ppListNew = &pListNew; + for ( i = 1; i <= p->nVarsMax; i++ ) + { + if ( pLists[i] == NULL ) + continue; + // find the last entry + for ( pPrev = pLists[i], pCut = pPrev->pNext; pCut; + pPrev = pCut, pCut = pCut->pNext ); + // connect these lists + *ppListNew = pLists[i]; + ppListNew = &pPrev->pNext; + } + *ppListNew = NULL; + // sort the cuts by arrival times and use only the first FPGA_CUTS_MAX_USE + pListNew = Fpga_CutSortCuts( p, pTable, pListNew ); + return pListNew; +} + +/**Function************************************************************* + + Synopsis [Merges two cuts.] + + Description [Returns the number of nodes in the resulting cut, or 0 if the + cut is infeasible. Returns the resulting nodes in the array ppNodes[].] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_CutMergeTwo( Fpga_Cut_t * pCut1, Fpga_Cut_t * pCut2, Fpga_Node_t * ppNodes[], int nNodesMax ) +{ + Fpga_Node_t * pNodeTemp; + int nTotal, i, k, min, Counter; + unsigned uSign; + + // use quick prefiltering + uSign = pCut1->uSign | pCut2->uSign; + Counter = FPGA_COUNT_ONES(uSign); + if ( Counter > nNodesMax ) + return 0; +/* + // check the special case when at least of the cuts is the largest + if ( pCut1->nLeaves == nNodesMax ) + { + if ( pCut2->nLeaves == nNodesMax ) + { + // return 0 if the cuts are different + for ( i = 0; i < nNodesMax; i++ ) + if ( pCut1->ppLeaves[i] != pCut2->ppLeaves[i] ) + return 0; + // return nNodesMax if they are the same + for ( i = 0; i < nNodesMax; i++ ) + ppNodes[i] = pCut1->ppLeaves[i]; + return nNodesMax; + } + else if ( pCut2->nLeaves == nNodesMax - 1 ) + { + // return 0 if the cuts are different + fMismatch = 0; + for ( i = 0; i < nNodesMax; i++ ) + if ( pCut1->ppLeaves[i] != pCut2->ppLeaves[i - fMismatch] ) + { + if ( fMismatch == 1 ) + return 0; + fMismatch = 1; + } + // return nNodesMax if they are the same + for ( i = 0; i < nNodesMax; i++ ) + ppNodes[i] = pCut1->ppLeaves[i]; + return nNodesMax; + } + } + else if ( pCut1->nLeaves == nNodesMax - 1 && pCut2->nLeaves == nNodesMax ) + { + // return 0 if the cuts are different + fMismatch = 0; + for ( i = 0; i < nNodesMax; i++ ) + if ( pCut1->ppLeaves[i - fMismatch] != pCut2->ppLeaves[i] ) + { + if ( fMismatch == 1 ) + return 0; + fMismatch = 1; + } + // return nNodesMax if they are the same + for ( i = 0; i < nNodesMax; i++ ) + ppNodes[i] = pCut2->ppLeaves[i]; + return nNodesMax; + } +*/ + // count the number of unique entries in pCut2 + nTotal = pCut1->nLeaves; + for ( i = 0; i < pCut2->nLeaves; i++ ) + { + // try to find this entry among the leaves of pCut1 + for ( k = 0; k < pCut1->nLeaves; k++ ) + if ( pCut2->ppLeaves[i] == pCut1->ppLeaves[k] ) + break; + if ( k < pCut1->nLeaves ) // found + continue; + // we found a new entry to add + if ( nTotal == nNodesMax ) + return 0; + ppNodes[nTotal++] = pCut2->ppLeaves[i]; + } + // we know that the feasible cut exists + + // add the starting entries + for ( k = 0; k < pCut1->nLeaves; k++ ) + ppNodes[k] = pCut1->ppLeaves[k]; + + // selection-sort the entries + for ( i = 0; i < nTotal - 1; i++ ) + { + min = i; + for ( k = i+1; k < nTotal; k++ ) +// if ( ppNodes[k] < ppNodes[min] ) // reported bug fix (non-determinism!) + if ( ppNodes[k]->Num < ppNodes[min]->Num ) + min = k; + pNodeTemp = ppNodes[i]; + ppNodes[i] = ppNodes[min]; + ppNodes[min] = pNodeTemp; + } + + return nTotal; +} + +/**Function************************************************************* + + Synopsis [Computes the union of the two lists of cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Cut_t * Fpga_CutUnionLists( Fpga_Cut_t * pList1, Fpga_Cut_t * pList2 ) +{ + Fpga_Cut_t * pTemp, * pRoot; + // find the last cut in the first list + pRoot = pList1; + Fpga_ListForEachCut( pList1, pTemp ) + pRoot = pTemp; + // attach the non-trival part of the second cut to the end of the first + assert( pRoot->pNext == NULL ); + pRoot->pNext = pList2->pNext; + pList2->pNext = NULL; + return pList1; +} + + +/**Function************************************************************* + + Synopsis [Checks whether the given cut belongs to the list.] + + Description [This procedure takes most of the runtime in the cut + computation.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_CutBelongsToList( Fpga_Cut_t * pList, Fpga_Node_t * ppNodes[], int nNodes ) +{ + Fpga_Cut_t * pTemp; + int i; + for ( pTemp = pList; pTemp; pTemp = pTemp->pNext ) + { + for ( i = 0; i < nNodes; i++ ) + if ( pTemp->ppLeaves[i] != ppNodes[i] ) + break; + if ( i == nNodes ) + return 1; + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Counts all the cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_CutCountAll( Fpga_Man_t * pMan ) +{ + Fpga_Node_t * pNode; + Fpga_Cut_t * pCut; + int i, nCuts; + // go through all the nodes in the unique table of the manager + nCuts = 0; + for ( i = 0; i < pMan->nBins; i++ ) + for ( pNode = pMan->pBins[i]; pNode; pNode = pNode->pNext ) + for ( pCut = pNode->pCuts; pCut; pCut = pCut->pNext ) + if ( pCut->nLeaves > 1 ) // skip the elementary cuts + { +// Fpga_CutVolume( pCut ); + nCuts++; + } + return nCuts; +} + + +/**Function************************************************************* + + Synopsis [Clean the signatures.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_CutsCleanSign( Fpga_Man_t * pMan ) +{ + Fpga_Node_t * pNode; + Fpga_Cut_t * pCut; + int i; + for ( i = 0; i < pMan->nBins; i++ ) + for ( pNode = pMan->pBins[i]; pNode; pNode = pNode->pNext ) + for ( pCut = pNode->pCuts; pCut; pCut = pCut->pNext ) + pCut->uSign = 0; +} + + + +/**Function************************************************************* + + Synopsis [Prints the cuts in the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_CutListPrint( Fpga_Man_t * pMan, Fpga_Node_t * pRoot ) +{ + Fpga_Cut_t * pTemp; + int Counter; + for ( Counter = 0, pTemp = pRoot->pCuts; pTemp; pTemp = pTemp->pNext, Counter++ ) + { + printf( "%2d : ", Counter + 1 ); + Fpga_CutPrint_( pMan, pTemp, pRoot ); + } +} + +/**Function************************************************************* + + Synopsis [Prints the cuts in the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_CutListPrint2( Fpga_Man_t * pMan, Fpga_Node_t * pRoot ) +{ + Fpga_Cut_t * pTemp; + int Counter; + for ( Counter = 0, pTemp = pRoot->pCuts; pTemp; pTemp = pTemp->pNext, Counter++ ) + { + printf( "%2d : ", Counter + 1 ); + Fpga_CutPrint_( pMan, pTemp, pRoot ); + } +} + +/**Function************************************************************* + + Synopsis [Prints the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_CutPrint_( Fpga_Man_t * pMan, Fpga_Cut_t * pCut, Fpga_Node_t * pRoot ) +{ + int i; + printf( "(%3d) {", pRoot->Num ); + for ( i = 0; i < pMan->nVarsMax; i++ ) + if ( pCut->ppLeaves[i] ) + printf( " %3d", pCut->ppLeaves[i]->Num ); + printf( " }\n" ); +} + + + + + + + + +/**Function************************************************************* + + Synopsis [Starts the hash table to canonicize cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_CutTable_t * Fpga_CutTableStart( Fpga_Man_t * pMan ) +{ + Fpga_CutTable_t * p; + // allocate the table + p = ALLOC( Fpga_CutTable_t, 1 ); + memset( p, 0, sizeof(Fpga_CutTable_t) ); + p->nBins = Cudd_Prime( 10 * FPGA_CUTS_MAX_COMPUTE ); + p->pBins = ALLOC( Fpga_Cut_t *, p->nBins ); + memset( p->pBins, 0, sizeof(Fpga_Cut_t *) * p->nBins ); + p->pCuts = ALLOC( int, 2 * FPGA_CUTS_MAX_COMPUTE ); + p->pArray = ALLOC( Fpga_Cut_t *, 2 * FPGA_CUTS_MAX_COMPUTE ); + p->pCuts1 = ALLOC( Fpga_Cut_t *, 2 * FPGA_CUTS_MAX_COMPUTE ); + p->pCuts2 = ALLOC( Fpga_Cut_t *, 2 * FPGA_CUTS_MAX_COMPUTE ); + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_CutTableStop( Fpga_CutTable_t * p ) +{ + free( p->pCuts1 ); + free( p->pCuts2 ); + free( p->pArray ); + free( p->pBins ); + free( p->pCuts ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Computes the hash value of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Fpga_CutTableHash( Fpga_Node_t * ppNodes[], int nNodes ) +{ + unsigned uRes; + int i; + uRes = 0; + for ( i = 0; i < nNodes; i++ ) + uRes += s_HashPrimes[i] * ppNodes[i]->Num; + return uRes; +} + +/**Function************************************************************* + + Synopsis [Looks up the table for the available cut.] + + Description [Returns -1 if the same cut is found. Returns the index + of the cell where the cut should be added, if it does not exist.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_CutTableLookup( Fpga_CutTable_t * p, Fpga_Node_t * ppNodes[], int nNodes ) +{ + Fpga_Cut_t * pCut; + unsigned Key; + int b, i; + + Key = Fpga_CutTableHash(ppNodes, nNodes) % p->nBins; + for ( b = Key; p->pBins[b]; b = (b+1) % p->nBins ) + { + pCut = p->pBins[b]; + if ( pCut->nLeaves != nNodes ) + continue; + for ( i = 0; i < nNodes; i++ ) + if ( pCut->ppLeaves[i] != ppNodes[i] ) + break; + if ( i == nNodes ) + return -1; + } + return b; +} + + +/**Function************************************************************* + + Synopsis [Starts the hash table to canonicize cuts.] + + Description [Considers addition of the cut to the hash table.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Cut_t * Fpga_CutTableConsider( Fpga_Man_t * pMan, Fpga_CutTable_t * p, Fpga_Node_t * ppNodes[], int nNodes ) +{ + Fpga_Cut_t * pCut; + int Place, i; + // check the cut + Place = Fpga_CutTableLookup( p, ppNodes, nNodes ); + if ( Place == -1 ) + return NULL; + assert( nNodes > 0 ); + // create the new cut + pCut = Fpga_CutAlloc( pMan ); + pCut->nLeaves = nNodes; + pCut->fLevel = 0.0; + for ( i = 0; i < nNodes; i++ ) + { + pCut->ppLeaves[i] = ppNodes[i]; + pCut->fLevel += ppNodes[i]->Level; + } + pCut->fLevel /= nNodes; + // add the cut to the table + assert( p->pBins[Place] == NULL ); + p->pBins[Place] = pCut; + // add the cut to the new list + p->pCuts[ p->nCuts++ ] = Place; + return pCut; +} + +/**Function************************************************************* + + Synopsis [Prepares the table to be used with other cuts.] + + Description [Restarts the table by cleaning the info about cuts stored + when the previous node was considered.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_CutTableRestart( Fpga_CutTable_t * p ) +{ + int i; + for ( i = 0; i < p->nCuts; i++ ) + { + assert( p->pBins[ p->pCuts[i] ] ); + p->pBins[ p->pCuts[i] ] = NULL; + } + p->nCuts = 0; +} + + + +/**Function************************************************************* + + Synopsis [Compares the cuts by the number of leaves and then by delay.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_CutSortCutsCompare( Fpga_Cut_t ** pC1, Fpga_Cut_t ** pC2 ) +{ + if ( (*pC1)->nLeaves < (*pC2)->nLeaves ) + return -1; + if ( (*pC1)->nLeaves > (*pC2)->nLeaves ) + return 1; +/* + if ( (*pC1)->fLevel > (*pC2)->fLevel ) + return -1; + if ( (*pC1)->fLevel < (*pC2)->fLevel ) + return 1; +*/ + return 0; +} + +/**Function************************************************************* + + Synopsis [Sorts the cuts by average arrival time.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Cut_t * Fpga_CutSortCuts( Fpga_Man_t * pMan, Fpga_CutTable_t * p, Fpga_Cut_t * pList ) +{ + Fpga_Cut_t * pListNew; + int nCuts, i; + // move the cuts from the list into the array + nCuts = Fpga_CutList2Array( p->pCuts1, pList ); + assert( nCuts <= FPGA_CUTS_MAX_COMPUTE ); + // sort the cuts + qsort( (void *)p->pCuts1, nCuts, sizeof(void *), + (int (*)(const void *, const void *)) Fpga_CutSortCutsCompare ); + // move them back into the list + if ( nCuts > FPGA_CUTS_MAX_USE - 1 ) + { +// printf( "*" ); + // free the remaining cuts + for ( i = FPGA_CUTS_MAX_USE - 1; i < nCuts; i++ ) + Extra_MmFixedEntryRecycle( pMan->mmCuts, (char *)p->pCuts1[i] ); + // update the number of cuts + nCuts = FPGA_CUTS_MAX_USE - 1; + } + pListNew = Fpga_CutArray2List( p->pCuts1, nCuts ); + return pListNew; +} + +/**Function************************************************************* + + Synopsis [Moves the nodes from the list into the array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_CutList2Array( Fpga_Cut_t ** pArray, Fpga_Cut_t * pList ) +{ + int i; + for ( i = 0; pList; pList = pList->pNext, i++ ) + pArray[i] = pList; + return i; +} + +/**Function************************************************************* + + Synopsis [Moves the nodes from the array into the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Cut_t * Fpga_CutArray2List( Fpga_Cut_t ** pArray, int nCuts ) +{ + Fpga_Cut_t * pListNew, ** ppListNew; + int i; + pListNew = NULL; + ppListNew = &pListNew; + for ( i = 0; i < nCuts; i++ ) + { + // connect these lists + *ppListNew = pArray[i]; + ppListNew = &pArray[i]->pNext; +//printf( " %d(%.2f)", pArray[i]->nLeaves, pArray[i]->fLevel ); + } +//printf( "\n" ); + + *ppListNew = NULL; + return pListNew; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/map/fpga/fpgaCutUtils.c b/abc_with_bb_support/src/map/fpga/fpgaCutUtils.c new file mode 100644 index 000000000..4fe2d3b2c --- /dev/null +++ b/abc_with_bb_support/src/map/fpga/fpgaCutUtils.c @@ -0,0 +1,470 @@ +/**CFile**************************************************************** + + FileName [fpgaCutUtils.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - August 18, 2004.] + + Revision [$Id: fpgaCutUtils.h,v 1.0 2003/09/08 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fpgaInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Cut_t * Fpga_CutAlloc( Fpga_Man_t * p ) +{ + Fpga_Cut_t * pCut; + pCut = (Fpga_Cut_t *)Extra_MmFixedEntryFetch( p->mmCuts ); + memset( pCut, 0, sizeof(Fpga_Cut_t) ); + return pCut; +} + +/**Function************************************************************* + + Synopsis [Duplicates the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Cut_t * Fpga_CutDup( Fpga_Man_t * p, Fpga_Cut_t * pCutOld ) +{ + Fpga_Cut_t * pCutNew; + int i; + pCutNew = Fpga_CutAlloc( p ); + pCutNew->pRoot = pCutOld->pRoot; + pCutNew->nLeaves = pCutOld->nLeaves; + for ( i = 0; i < pCutOld->nLeaves; i++ ) + pCutNew->ppLeaves[i] = pCutOld->ppLeaves[i]; + return pCutNew; +} + +/**Function************************************************************* + + Synopsis [Deallocates the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_CutFree( Fpga_Man_t * p, Fpga_Cut_t * pCut ) +{ + if ( pCut ) + Extra_MmFixedEntryRecycle( p->mmCuts, (char *)pCut ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_CutPrint( Fpga_Man_t * p, Fpga_Node_t * pRoot, Fpga_Cut_t * pCut ) +{ + int i; + printf( "CUT: Delay = %4.2f. Area = %4.2f. Nodes = %d -> {", + pCut->tArrival, pCut->aFlow, pRoot->Num ); + for ( i = 0; i < pCut->nLeaves; i++ ) + printf( " %d", pCut->ppLeaves[i]->Num ); + printf( " } \n" ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Cut_t * Fpga_CutCreateSimple( Fpga_Man_t * p, Fpga_Node_t * pNode ) +{ + Fpga_Cut_t * pCut; + pCut = Fpga_CutAlloc( p ); + pCut->pRoot = pNode; + pCut->nLeaves = 1; + pCut->ppLeaves[0] = pNode; + pCut->uSign = FPGA_SEQ_SIGN(pCut->ppLeaves[0]); + return pCut; +} + + +/**function************************************************************* + + synopsis [Computes the exact area associated with the cut.] + + description [] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Fpga_CutGetRootArea( Fpga_Man_t * p, Fpga_Cut_t * pCut ) +{ + return p->pLutLib->pLutAreas[pCut->nLeaves]; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Cut_t * Fpga_CutListAppend( Fpga_Cut_t * pSetAll, Fpga_Cut_t * pSets ) +{ + Fpga_Cut_t * pPrev, * pTemp; + if ( pSetAll == NULL ) + return pSets; + if ( pSets == NULL ) + return pSetAll; + // find the last one + for ( pTemp = pSets; pTemp; pTemp = pTemp->pNext ) + pPrev = pTemp; + // append all the end of the current set + assert( pPrev->pNext == NULL ); + pPrev->pNext = pSetAll; + return pSets; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_CutListRecycle( Fpga_Man_t * p, Fpga_Cut_t * pSetList, Fpga_Cut_t * pSave ) +{ + Fpga_Cut_t * pNext, * pTemp; + for ( pTemp = pSetList, pNext = pTemp? pTemp->pNext : NULL; + pTemp; + pTemp = pNext, pNext = pNext? pNext->pNext : NULL ) + if ( pTemp != pSave ) + Extra_MmFixedEntryRecycle( p->mmCuts, (char *)pTemp ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_CutListCount( Fpga_Cut_t * pSets ) +{ + Fpga_Cut_t * pTemp; + int i; + for ( i = 0, pTemp = pSets; pTemp; pTemp = pTemp->pNext, i++ ); + return i; +} + +#if 0 + +/**function************************************************************* + + synopsis [Removes the fanouts of the cut.] + + description [] + + sideeffects [] + + seealso [] + +***********************************************************************/ +void Fpga_CutRemoveFanouts( Fpga_Man_t * p, Fpga_Node_t * pNode, Fpga_Cut_t * pCut ) +{ + Fpga_NodeVec_t * vFanouts; + int i, k; + for ( i = 0; i < pCut->nLeaves; i++ ) + { + vFanouts = pCut->ppLeaves[i]->vFanouts; + for ( k = 0; k < vFanouts->nSize; k++ ) + if ( vFanouts->pArray[k] == pNode ) + break; + assert( k != vFanouts->nSize ); + for ( k++; k < vFanouts->nSize; k++ ) + vFanouts->pArray[k-1] = vFanouts->pArray[k]; + vFanouts->nSize--; + } +} + +/**function************************************************************* + + synopsis [Removes the fanouts of the cut.] + + description [] + + sideeffects [] + + seealso [] + +***********************************************************************/ +void Fpga_CutInsertFanouts( Fpga_Man_t * p, Fpga_Node_t * pNode, Fpga_Cut_t * pCut ) +{ + int i; + for ( i = 0; i < pCut->nLeaves; i++ ) + Fpga_NodeVecPush( pCut->ppLeaves[i]->vFanouts, pNode ); +} +#endif + +/**Function************************************************************* + + Synopsis [Computes the arrival time and the area flow of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_CutGetParameters( Fpga_Man_t * pMan, Fpga_Cut_t * pCut ) +{ + Fpga_Cut_t * pFaninCut; + int i; + pCut->tArrival = -FPGA_FLOAT_LARGE; + pCut->aFlow = pMan->pLutLib->pLutAreas[pCut->nLeaves]; + for ( i = 0; i < pCut->nLeaves; i++ ) + { + pFaninCut = pCut->ppLeaves[i]->pCutBest; + if ( pCut->tArrival < pFaninCut->tArrival ) + pCut->tArrival = pFaninCut->tArrival; + // if the fanout count is not set, assume it to be 1 + if ( pCut->ppLeaves[i]->nRefs == 0 ) + pCut->aFlow += pFaninCut->aFlow; + else +// pCut->aFlow += pFaninCut->aFlow / pCut->ppLeaves[i]->nRefs; + pCut->aFlow += pFaninCut->aFlow / pCut->ppLeaves[i]->aEstFanouts; + } + // use the first pin to compute the delay of the LUT + // (this mapper does not support the variable pin delay model) + pCut->tArrival += pMan->pLutLib->pLutDelays[pCut->nLeaves][0]; +} + + +/**function************************************************************* + + synopsis [Computes the area flow of the cut.] + + description [] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Fpga_CutGetAreaFlow( Fpga_Man_t * pMan, Fpga_Cut_t * pCut ) +{ + Fpga_Cut_t * pCutFanin; + int i; + pCut->aFlow = pMan->pLutLib->pLutAreas[pCut->nLeaves]; + for ( i = 0; i < pCut->nLeaves; i++ ) + { + // get the cut implementing this phase of the fanin + pCutFanin = pCut->ppLeaves[i]->pCutBest; + assert( pCutFanin ); + pCut->aFlow += pCutFanin->aFlow / pCut->ppLeaves[i]->nRefs; + } + return pCut->aFlow; +} + +/**function************************************************************* + + synopsis [Computes the exact area associated with the cut.] + + description [] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Fpga_CutGetAreaRefed( Fpga_Man_t * pMan, Fpga_Cut_t * pCut ) +{ + float aResult, aResult2; + if ( pCut->nLeaves == 1 ) + return 0; + aResult = Fpga_CutDeref( pMan, NULL, pCut, 0 ); + aResult2 = Fpga_CutRef( pMan, NULL, pCut, 0 ); + assert( Fpga_FloatEqual( pMan, aResult, aResult2 ) ); + return aResult; +} + +/**function************************************************************* + + synopsis [Computes the exact area associated with the cut.] + + description [] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Fpga_CutGetAreaDerefed( Fpga_Man_t * pMan, Fpga_Cut_t * pCut ) +{ + float aResult, aResult2; + if ( pCut->nLeaves == 1 ) + return 0; + aResult2 = Fpga_CutRef( pMan, NULL, pCut, 0 ); + aResult = Fpga_CutDeref( pMan, NULL, pCut, 0 ); + assert( Fpga_FloatEqual( pMan, aResult, aResult2 ) ); + return aResult; +} + +/**function************************************************************* + + synopsis [References the cut.] + + description [This procedure is similar to the procedure NodeReclaim.] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Fpga_CutRef( Fpga_Man_t * pMan, Fpga_Node_t * pNode, Fpga_Cut_t * pCut, int fFanouts ) +{ + Fpga_Node_t * pNodeChild; + float aArea; + int i; + + // deref the fanouts +// if ( fFanouts ) +// Fpga_CutInsertFanouts( pMan, pNode, pCut ); + + // start the area of this cut + aArea = pMan->pLutLib->pLutAreas[pCut->nLeaves]; + // go through the children + for ( i = 0; i < pCut->nLeaves; i++ ) + { + pNodeChild = pCut->ppLeaves[i]; + assert( pNodeChild->nRefs >= 0 ); + if ( pNodeChild->nRefs++ > 0 ) + continue; + if ( !Fpga_NodeIsAnd(pNodeChild) ) + continue; + aArea += Fpga_CutRef( pMan, pNodeChild, pNodeChild->pCutBest, fFanouts ); + } + return aArea; +} + +/**function************************************************************* + + synopsis [Dereferences the cut.] + + description [This procedure is similar to the procedure NodeRecusiveDeref.] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Fpga_CutDeref( Fpga_Man_t * pMan, Fpga_Node_t * pNode, Fpga_Cut_t * pCut, int fFanouts ) +{ + Fpga_Node_t * pNodeChild; + float aArea; + int i; + + // deref the fanouts +// if ( fFanouts ) +// Fpga_CutRemoveFanouts( pMan, pNode, pCut ); + + // start the area of this cut + aArea = pMan->pLutLib->pLutAreas[pCut->nLeaves]; + // go through the children + for ( i = 0; i < pCut->nLeaves; i++ ) + { + pNodeChild = pCut->ppLeaves[i]; + assert( pNodeChild->nRefs > 0 ); + if ( --pNodeChild->nRefs > 0 ) + continue; + if ( !Fpga_NodeIsAnd(pNodeChild) ) + continue; + aArea += Fpga_CutDeref( pMan, pNodeChild, pNodeChild->pCutBest, fFanouts ); + } + return aArea; +} + + +/**Function************************************************************* + + Synopsis [Sets the used cuts to be the currently selected ones.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_MappingSetUsedCuts( Fpga_Man_t * pMan ) +{ + int i; + for ( i = 0; i < pMan->vNodesAll->nSize; i++ ) + if ( pMan->vNodesAll->pArray[i]->pCutOld ) + { + pMan->vNodesAll->pArray[i]->pCutBest = pMan->vNodesAll->pArray[i]->pCutOld; + pMan->vNodesAll->pArray[i]->pCutOld = NULL; + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/fpga/fpgaFanout.c b/abc_with_bb_support/src/map/fpga/fpgaFanout.c new file mode 100644 index 000000000..9041a659d --- /dev/null +++ b/abc_with_bb_support/src/map/fpga/fpgaFanout.c @@ -0,0 +1,141 @@ +/**CFile**************************************************************** + + FileName [fpgaFanout.c] + + PackageName [FRAIG: Functionally reduced AND-INV graphs.] + + Synopsis [Procedures to manipulate fanouts of the FRAIG nodes.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - August 18, 2004.] + + Revision [$Id: fpgaFanout.c,v 1.1 2005/01/23 06:59:41 alanmi Exp $] + +***********************************************************************/ + +#include "fpgaInt.h" + +#ifdef MAP_ALLOCATE_FANOUT + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Add the fanout to the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_NodeAddFaninFanout( Fpga_Node_t * pFanin, Fpga_Node_t * pFanout ) +{ + Fpga_Node_t * pPivot; + + // pFanins is a fanin of pFanout + assert( !Fpga_IsComplement(pFanin) ); + assert( !Fpga_IsComplement(pFanout) ); + assert( Fpga_Regular(pFanout->p1) == pFanin || Fpga_Regular(pFanout->p2) == pFanin ); + + pPivot = pFanin->pFanPivot; + if ( pPivot == NULL ) + { + pFanin->pFanPivot = pFanout; + return; + } + + if ( Fpga_Regular(pPivot->p1) == pFanin ) + { + if ( Fpga_Regular(pFanout->p1) == pFanin ) + { + pFanout->pFanFanin1 = pPivot->pFanFanin1; + pPivot->pFanFanin1 = pFanout; + } + else // if ( Fpga_Regular(pFanout->p2) == pFanin ) + { + pFanout->pFanFanin2 = pPivot->pFanFanin1; + pPivot->pFanFanin1 = pFanout; + } + } + else // if ( Fpga_Regular(pPivot->p2) == pFanin ) + { + assert( Fpga_Regular(pPivot->p2) == pFanin ); + if ( Fpga_Regular(pFanout->p1) == pFanin ) + { + pFanout->pFanFanin1 = pPivot->pFanFanin2; + pPivot->pFanFanin2 = pFanout; + } + else // if ( Fpga_Regular(pFanout->p2) == pFanin ) + { + pFanout->pFanFanin2 = pPivot->pFanFanin2; + pPivot->pFanFanin2 = pFanout; + } + } +} + +/**Function************************************************************* + + Synopsis [Add the fanout to the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_NodeRemoveFaninFanout( Fpga_Node_t * pFanin, Fpga_Node_t * pFanoutToRemove ) +{ + Fpga_Node_t * pFanout, * pFanout2, ** ppFanList; + // start the linked list of fanouts + ppFanList = &pFanin->pFanPivot; + // go through the fanouts + Fpga_NodeForEachFanoutSafe( pFanin, pFanout, pFanout2 ) + { + // skip the fanout-to-remove + if ( pFanout == pFanoutToRemove ) + continue; + // add useful fanouts to the list + *ppFanList = pFanout; + ppFanList = Fpga_NodeReadNextFanoutPlace( pFanin, pFanout ); + } + *ppFanList = NULL; +} + +/**Function************************************************************* + + Synopsis [Returns the number of fanouts of a node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_NodeGetFanoutNum( Fpga_Node_t * pNode ) +{ + Fpga_Node_t * pFanout; + int Counter = 0; + Fpga_NodeForEachFanout( pNode, pFanout ) + Counter++; + return Counter; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + +#endif + diff --git a/abc_with_bb_support/src/map/fpga/fpgaGENERIC.c b/abc_with_bb_support/src/map/fpga/fpgaGENERIC.c new file mode 100644 index 000000000..4b59e814f --- /dev/null +++ b/abc_with_bb_support/src/map/fpga/fpgaGENERIC.c @@ -0,0 +1,46 @@ +/**CFile**************************************************************** + + FileName [fpga__.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 8, 2003.] + + Revision [$Id: fpga__.h,v 1.0 2003/09/08 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fpgaInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/fpga/fpgaInt.h b/abc_with_bb_support/src/map/fpga/fpgaInt.h new file mode 100644 index 000000000..991317507 --- /dev/null +++ b/abc_with_bb_support/src/map/fpga/fpgaInt.h @@ -0,0 +1,388 @@ +/**CFile**************************************************************** + + FileName [fpgaInt.h] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Technology mapping for variable-size-LUT FPGAs.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - August 18, 2004.] + + Revision [$Id: fpgaInt.h,v 1.8 2004/09/30 21:18:10 satrajit Exp $] + +***********************************************************************/ + +#ifndef __FPGA_INT_H__ +#define __FPGA_INT_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//#include "leaks.h" +#include +#include +#include +#include "extra.h" +#include "fpga.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +// uncomment to have fanouts represented in the mapping graph +//#define FPGA_ALLOCATE_FANOUT 1 + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#ifdef _WIN32 +#define inline __inline // compatible with MS VS 6.0 +#endif + +// the maximum number of cut leaves (currently does not work for 7) +#define FPGA_MAX_LEAVES 6 + +// the bit masks +#define FPGA_MASK(n) ((~((unsigned)0)) >> (32-(n))) +#define FPGA_FULL (~((unsigned)0)) +#define FPGA_NO_VAR (-9999.0) +#define FPGA_NUM_BYTES(n) (((n)/16 + (((n)%16) > 0))*16) + +// maximum/minimum operators +#define FPGA_MIN(a,b) (((a) < (b))? (a) : (b)) +#define FPGA_MAX(a,b) (((a) > (b))? (a) : (b)) + +// the small and large numbers (min/max float are 1.17e-38/3.40e+38) +#define FPGA_FLOAT_LARGE ((float)1.0e+20) +#define FPGA_FLOAT_SMALL ((float)1.0e-20) +#define FPGA_INT_LARGE (10000000) + +// the macro to compute the signature +#define FPGA_SEQ_SIGN(p) (1 << (((unsigned)p)%31)); + +// internal macros to work with cuts +#define Fpga_CutIsComplement(p) (((int)((unsigned long) (p) & 01))) +#define Fpga_CutRegular(p) ((Fpga_Cut_t *)((unsigned long)(p) & ~01)) +#define Fpga_CutNot(p) ((Fpga_Cut_t *)((unsigned long)(p) ^ 01)) +#define Fpga_CutNotCond(p,c) ((Fpga_Cut_t *)((unsigned long)(p) ^ (c))) + +// the cut nodes +#define Fpga_SeqIsComplement( p ) (((int)((unsigned long) (p) & 01))) +#define Fpga_SeqRegular( p ) ((Fpga_Node_t *)((unsigned long)(p) & ~015)) +#define Fpga_SeqIndex( p ) ((((unsigned long)(p)) >> 1) & 07) +#define Fpga_SeqIndexCreate( p, Ind ) (((unsigned long)(p)) | (1 << (((unsigned)(Ind)) & 07))) + +// internal macros for referencing of nodes +#define Fpga_NodeReadRef(p) ((Fpga_Regular(p))->nRefs) +#define Fpga_NodeRef(p) ((Fpga_Regular(p))->nRefs++) + +// returns the complemented attribute of the node +#define Fpga_NodeIsSimComplement(p) (Fpga_IsComplement(p)? !(Fpga_Regular(p)->fInv) : (p)->fInv) + +// generating random unsigned (#define RAND_MAX 0x7fff) +#define FPGA_RANDOM_UNSIGNED ((((unsigned)rand()) << 24) ^ (((unsigned)rand()) << 12) ^ ((unsigned)rand())) + +// outputs the runtime in seconds +#define PRT(a,t) printf("%s = ", (a)); printf("%6.2f sec\n", (float)(t)/(float)(CLOCKS_PER_SEC)) + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// the mapping manager +struct Fpga_ManStruct_t_ +{ + // the mapping graph + Fpga_Node_t ** pBins; // the table of nodes hashed by their children + int nBins; // the size of the table + Fpga_Node_t ** pInputs; // the array of inputs + int nInputs; // the number of inputs + Fpga_Node_t ** pOutputs; // the array of outputs + int nOutputs; // the number of outputs + int nNodes; // the total number of nodes + int nLatches; // the number of latches in the circuit + Fpga_Node_t * pConst1; // the constant 1 node + Fpga_NodeVec_t * vNodesAll; // the nodes by number + Fpga_NodeVec_t * vAnds; // the nodes reachable from COs + Fpga_NodeVec_t * vMapping; // the nodes used in the current mapping + + // info about the original circuit + char * pFileName; // the file name + char ** ppOutputNames; // the primary output names + float * pInputArrivals;// the PI arrival times + + // mapping parameters + int nVarsMax; // the max number of variables + int fAreaRecovery; // the flag to use area flow as the first parameter + int fVerbose; // the verbosiness flag + int fSwitching; // minimize the switching activity (instead of area) + int fLatchPaths; // optimize latch paths for delay, other paths for area + int nTravIds; // the counter of traversal IDs + float DelayTarget; // the target required times + + // support of choice nodes + int nChoiceNodes; // the number of choice nodes + int nChoices; // the number of all choices + + int nCanons; + int nMatches; + + // the supergate library + Fpga_LutLib_t * pLutLib; // the current LUT library + + // the memory managers + Extra_MmFixed_t * mmNodes; // the memory manager for nodes + Extra_MmFixed_t * mmCuts; // the memory manager for cuts + + // resynthesis parameters + int fResynthesis; // the resynthesis flag + float fRequiredGlo; // the global required times + float fRequiredShift;// the shift of the required times + float fRequiredStart;// the starting global required times + float fRequiredGain; // the reduction in delay + float fAreaGlo; // the total area + float fAreaGain; // the reduction in area + float fEpsilon; // the epsilon used to compare floats + float fDelayWindow; // the delay window for delay-oriented resynthesis + float DelayLimit; // for resynthesis + float AreaLimit; // for resynthesis + float TimeLimit; // for resynthesis + + // runtime statistics + int timeToMap; // time to transfer to the mapping structure + int timeCuts; // time to compute k-feasible cuts + int timeTruth; // time to compute the truth table for each cut + int timeMatch; // time to perform matching for each node + int timeRecover; // time to perform area recovery + int timeToNet; // time to transfer back to the network + int timeTotal; // the total mapping time + int time1; // time to transfer to the mapping structure + int time2; // time to transfer to the mapping structure +}; + +// the LUT library +struct Fpga_LutLibStruct_t_ +{ + char * pName; // the name of the LUT library + int LutMax; // the maximum LUT size + int fVarPinDelays; // set to 1 if variable pin delays are specified + float pLutAreas[FPGA_MAX_LUTSIZE+1]; // the areas of LUTs + float pLutDelays[FPGA_MAX_LUTSIZE+1][FPGA_MAX_LUTSIZE+1];// the delays of LUTs +}; + +// the mapping node +struct Fpga_NodeStruct_t_ +{ + // general information about the node + Fpga_Node_t * pNext; // the next node in the hash table + Fpga_Node_t * pLevel; // the next node in the linked list by level + int Num; // the unique number of this node + int NumA; // the unique number of this node + short Num2; // the temporary number of this node + short nRefs; // the number of references (fanouts) of the given node + unsigned fMark0 : 1; // the mark used for traversals + unsigned fMark1 : 1; // the mark used for traversals + unsigned fInv : 1; // the complemented attribute for the equivalent nodes + unsigned Value : 2; // the value of the nodes + unsigned fUsed : 1; // the flag indicating that the node is used in the mapping + unsigned fTemp : 1; // unused + unsigned Level :11; // the level of the given node + unsigned uData :14; // used to mark the fanins, for which resynthesis was tried + int TravId; + + // the successors of this node + Fpga_Node_t * p1; // the first child + Fpga_Node_t * p2; // the second child + Fpga_Node_t * pNextE; // the next functionally equivalent node + Fpga_Node_t * pRepr; // the representative of the functionally equivalent class + +#ifdef FPGA_ALLOCATE_FANOUT + // representation of node's fanouts + Fpga_Node_t * pFanPivot; // the first fanout of this node + Fpga_Node_t * pFanFanin1; // the next fanout of p1 + Fpga_Node_t * pFanFanin2; // the next fanout of p2 +// Fpga_NodeVec_t * vFanouts; // the array of fanouts of the gate +#endif + + // the delay information + float tRequired; // the best area flow + float aEstFanouts; // the fanout estimation + float Switching; // the probability of switching + int LValue; // the l-value of the node + short nLatches1; // the number of latches on the first edge + short nLatches2; // the number of latches on the second edge + + // cut information + Fpga_Cut_t * pCutBest; // the best mapping + Fpga_Cut_t * pCutOld; // the old mapping + Fpga_Cut_t * pCuts; // mapping choices for the node (elementary comes first) + Fpga_Cut_t * pCutsN; // mapping choices for the node (elementary comes first) + + // misc information + char * pData0; // temporary storage for the corresponding network node +}; + +// the cuts used for matching +struct Fpga_CutStruct_t_ +{ + Fpga_Cut_t * pOne; // the father of this cut + Fpga_Cut_t * pTwo; // the mother of this cut + Fpga_Node_t * pRoot; // the root of the cut + Fpga_Node_t * ppLeaves[FPGA_MAX_LEAVES+1]; // the leaves of this cut + float fLevel; // the average level of the fanins + unsigned uSign; // signature for quick comparison + char fMark; // the mark to denote visited cut + char Phase; // the mark to denote complemented cut + char nLeaves; // the number of leaves of this cut + char nVolume; // the volume of this cut + float tArrival; // the arrival time + float aFlow; // the area flow of the cut + Fpga_Cut_t * pNext; // the pointer to the next cut in the list +}; + +// the vector of nodes +struct Fpga_NodeVecStruct_t_ +{ + Fpga_Node_t ** pArray; // the array of nodes + int nSize; // the number of entries in the array + int nCap; // the number of allocated entries +}; + +// getting hold of the next fanout of the node +#define Fpga_NodeReadNextFanout( pNode, pFanout ) \ + ( ( pFanout == NULL )? NULL : \ + ((Fpga_Regular((pFanout)->p1) == (pNode))? \ + (pFanout)->pFanFanin1 : (pFanout)->pFanFanin2) ) + +// getting hold of the place where the next fanout will be attached +#define Fpga_NodeReadNextFanoutPlace( pNode, pFanout ) \ + ( (Fpga_Regular((pFanout)->p1) == (pNode))? \ + &(pFanout)->pFanFanin1 : &(pFanout)->pFanFanin2 ) + +// iterator through the fanouts of the node +#define Fpga_NodeForEachFanout( pNode, pFanout ) \ + for ( pFanout = (pNode)->pFanPivot; pFanout; \ + pFanout = Fpga_NodeReadNextFanout(pNode, pFanout) ) + +// safe iterator through the fanouts of the node +#define Fpga_NodeForEachFanoutSafe( pNode, pFanout, pFanout2 ) \ + for ( pFanout = (pNode)->pFanPivot, \ + pFanout2 = Fpga_NodeReadNextFanout(pNode, pFanout); \ + pFanout; \ + pFanout = pFanout2, \ + pFanout2 = Fpga_NodeReadNextFanout(pNode, pFanout) ) + +static inline Fpga_FloatMoreThan( Fpga_Man_t * p, float Arg1, float Arg2 ) { return Arg1 > Arg2 + p->fEpsilon; } +static inline Fpga_FloatLessThan( Fpga_Man_t * p, float Arg1, float Arg2 ) { return Arg1 < Arg2 - p->fEpsilon; } +static inline Fpga_FloatEqual( Fpga_Man_t * p, float Arg1, float Arg2 ) { return Arg1 > Arg2 - p->fEpsilon && Arg1 < Arg2 + p->fEpsilon; } + +//////////////////////////////////////////////////////////////////////// +/// GLOBAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== fpgaCut.c ===============================================================*/ +extern void Fpga_MappingCuts( Fpga_Man_t * p ); +extern void Fpga_MappingCreatePiCuts( Fpga_Man_t * p ); +extern int Fpga_CutCountAll( Fpga_Man_t * pMan ); +/*=== fpgaCutUtils.c ===============================================================*/ +extern Fpga_Cut_t * Fpga_CutAlloc( Fpga_Man_t * p ); +extern Fpga_Cut_t * Fpga_CutDup( Fpga_Man_t * p, Fpga_Cut_t * pCutOld ); +extern void Fpga_CutFree( Fpga_Man_t * p, Fpga_Cut_t * pCut ); +extern void Fpga_CutPrint( Fpga_Man_t * p, Fpga_Node_t * pRoot, Fpga_Cut_t * pCut ); +extern Fpga_Cut_t * Fpga_CutCreateSimple( Fpga_Man_t * p, Fpga_Node_t * pNode ); +extern float Fpga_CutGetRootArea( Fpga_Man_t * p, Fpga_Cut_t * pCut ); +extern Fpga_Cut_t * Fpga_CutListAppend( Fpga_Cut_t * pSetAll, Fpga_Cut_t * pSets ); +extern void Fpga_CutListRecycle( Fpga_Man_t * p, Fpga_Cut_t * pSetList, Fpga_Cut_t * pSave ); +extern int Fpga_CutListCount( Fpga_Cut_t * pSets ); +extern void Fpga_CutRemoveFanouts( Fpga_Man_t * p, Fpga_Node_t * pNode, Fpga_Cut_t * pCut ); +extern void Fpga_CutInsertFanouts( Fpga_Man_t * p, Fpga_Node_t * pNode, Fpga_Cut_t * pCut ); +extern float Fpga_CutGetAreaRefed( Fpga_Man_t * pMan, Fpga_Cut_t * pCut ); +extern float Fpga_CutGetAreaDerefed( Fpga_Man_t * pMan, Fpga_Cut_t * pCut ); +extern float Fpga_CutRef( Fpga_Man_t * pMan, Fpga_Node_t * pNode, Fpga_Cut_t * pCut, int fFanouts ); +extern float Fpga_CutDeref( Fpga_Man_t * pMan, Fpga_Node_t * pNode, Fpga_Cut_t * pCut, int fFanouts ); +extern float Fpga_CutGetAreaFlow( Fpga_Man_t * pMan, Fpga_Cut_t * pCut ); +extern void Fpga_CutGetParameters( Fpga_Man_t * pMan, Fpga_Cut_t * pCut ); +/*=== fraigFanout.c =============================================================*/ +extern void Fpga_NodeAddFaninFanout( Fpga_Node_t * pFanin, Fpga_Node_t * pFanout ); +extern void Fpga_NodeRemoveFaninFanout( Fpga_Node_t * pFanin, Fpga_Node_t * pFanoutToRemove ); +extern int Fpga_NodeGetFanoutNum( Fpga_Node_t * pNode ); +/*=== fpgaLib.c ============================================================*/ +extern Fpga_LutLib_t * Fpga_LutLibCreate( char * FileName, int fVerbose ); +extern void Fpga_LutLibFree( Fpga_LutLib_t * p ); +extern void Fpga_LutLibPrint( Fpga_LutLib_t * pLutLib ); +extern int Fpga_LutLibDelaysAreDiscrete( Fpga_LutLib_t * pLutLib ); +/*=== fpgaMatch.c ===============================================================*/ +extern int Fpga_MappingMatches( Fpga_Man_t * p, int fDelayOriented ); +extern int Fpga_MappingMatchesArea( Fpga_Man_t * p ); +extern int Fpga_MappingMatchesSwitch( Fpga_Man_t * p ); +/*=== fpgaShow.c =============================================================*/ +extern void Fpga_MappingShow( Fpga_Man_t * pMan, char * pFileName ); +extern void Fpga_MappingShowNodes( Fpga_Man_t * pMan, Fpga_Node_t ** ppRoots, int nRoots, char * pFileName ); +/*=== fpgaSwitch.c =============================================================*/ +extern float Fpga_CutGetSwitchDerefed( Fpga_Man_t * pMan, Fpga_Node_t * pNode, Fpga_Cut_t * pCut ); +extern float Fpga_CutRefSwitch( Fpga_Man_t * pMan, Fpga_Node_t * pNode, Fpga_Cut_t * pCut, int fFanouts ); +extern float Fpga_CutDerefSwitch( Fpga_Man_t * pMan, Fpga_Node_t * pNode, Fpga_Cut_t * pCut, int fFanouts ); +extern float Fpga_MappingGetSwitching( Fpga_Man_t * pMan, Fpga_NodeVec_t * vMapping ); +/*=== fpgaTime.c ===============================================================*/ +extern float Fpga_TimeCutComputeArrival( Fpga_Man_t * pMan, Fpga_Cut_t * pCut ); +extern float Fpga_TimeCutComputeArrival_rec( Fpga_Man_t * pMan, Fpga_Cut_t * pCut ); +extern float Fpga_TimeComputeArrivalMax( Fpga_Man_t * p ); +extern void Fpga_TimeComputeRequiredGlobal( Fpga_Man_t * p, int fFirstTime ); +extern void Fpga_TimeComputeRequired( Fpga_Man_t * p, float fRequired ); +extern void Fpga_TimePropagateRequired( Fpga_Man_t * p, Fpga_NodeVec_t * vNodes ); +extern void Fpga_TimePropagateArrival( Fpga_Man_t * p ); +/*=== fpgaVec.c =============================================================*/ +extern Fpga_NodeVec_t * Fpga_NodeVecAlloc( int nCap ); +extern void Fpga_NodeVecFree( Fpga_NodeVec_t * p ); +extern Fpga_Node_t ** Fpga_NodeVecReadArray( Fpga_NodeVec_t * p ); +extern int Fpga_NodeVecReadSize( Fpga_NodeVec_t * p ); +extern void Fpga_NodeVecGrow( Fpga_NodeVec_t * p, int nCapMin ); +extern void Fpga_NodeVecShrink( Fpga_NodeVec_t * p, int nSizeNew ); +extern void Fpga_NodeVecClear( Fpga_NodeVec_t * p ); +extern void Fpga_NodeVecPush( Fpga_NodeVec_t * p, Fpga_Node_t * Entry ); +extern int Fpga_NodeVecPushUnique( Fpga_NodeVec_t * p, Fpga_Node_t * Entry ); +extern Fpga_Node_t * Fpga_NodeVecPop( Fpga_NodeVec_t * p ); +extern void Fpga_NodeVecWriteEntry( Fpga_NodeVec_t * p, int i, Fpga_Node_t * Entry ); +extern Fpga_Node_t * Fpga_NodeVecReadEntry( Fpga_NodeVec_t * p, int i ); +extern void Fpga_NodeVecSortByLevel( Fpga_NodeVec_t * p ); +extern void Fpga_SortNodesByArrivalTimes( Fpga_NodeVec_t * p ); +extern void Fpga_NodeVecUnion( Fpga_NodeVec_t * p, Fpga_NodeVec_t * p1, Fpga_NodeVec_t * p2 ); +extern void Fpga_NodeVecPushOrder( Fpga_NodeVec_t * vNodes, Fpga_Node_t * pNode, int fIncreasing ); +extern void Fpga_NodeVecReverse( Fpga_NodeVec_t * vNodes ); + +/*=== fpgaUtils.c ===============================================================*/ +extern Fpga_NodeVec_t * Fpga_MappingDfs( Fpga_Man_t * pMan, int fCollectEquiv ); +extern Fpga_NodeVec_t * Fpga_MappingDfsNodes( Fpga_Man_t * pMan, Fpga_Node_t ** ppNodes, int nNodes, int fEquiv ); +extern int Fpga_CountLevels( Fpga_Man_t * pMan ); +extern float Fpga_MappingGetAreaFlow( Fpga_Man_t * p ); +extern float Fpga_MappingArea( Fpga_Man_t * pMan ); +extern float Fpga_MappingAreaTrav( Fpga_Man_t * pMan ); +extern float Fpga_MappingSetRefsAndArea( Fpga_Man_t * pMan ); +extern void Fpga_MappingPrintOutputArrivals( Fpga_Man_t * p ); +extern void Fpga_MappingSetupTruthTables( unsigned uTruths[][2] ); +extern void Fpga_MappingSetupMask( unsigned uMask[], int nVarsMax ); +extern void Fpga_MappingSortByLevel( Fpga_Man_t * pMan, Fpga_NodeVec_t * vNodes, int fIncreasing ); +extern Fpga_NodeVec_t * Fpga_DfsLim( Fpga_Man_t * pMan, Fpga_Node_t * pNode, int nLevels ); +extern Fpga_NodeVec_t * Fpga_MappingLevelize( Fpga_Man_t * pMan, Fpga_NodeVec_t * vNodes ); +extern int Fpga_MappingMaxLevel( Fpga_Man_t * pMan ); +extern void Fpga_ManReportChoices( Fpga_Man_t * pMan ); +extern void Fpga_MappingSetChoiceLevels( Fpga_Man_t * pMan ); + +/*=== CUDD package.c ===============================================================*/ +extern unsigned int Cudd_Prime( unsigned int p ); + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/map/fpga/fpgaLib.c b/abc_with_bb_support/src/map/fpga/fpgaLib.c new file mode 100644 index 000000000..686aedac4 --- /dev/null +++ b/abc_with_bb_support/src/map/fpga/fpgaLib.c @@ -0,0 +1,249 @@ +/**CFile**************************************************************** + + FileName [fpgaLib.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Technology mapping for variable-size-LUT FPGAs.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - August 18, 2004.] + + Revision [$Id: fpgaLib.c,v 1.4 2005/01/23 06:59:41 alanmi Exp $] + +***********************************************************************/ + +#include "fpgaInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [APIs to access LUT library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_LutLibReadVarMax( Fpga_LutLib_t * p ) { return p->LutMax; } +float * Fpga_LutLibReadLutAreas( Fpga_LutLib_t * p ) { return p->pLutAreas; } +float Fpga_LutLibReadLutArea( Fpga_LutLib_t * p, int Size ) { assert( Size <= p->LutMax ); return p->pLutAreas[Size]; } + +/**Function************************************************************* + + Synopsis [Reads the description of LUTs from the LUT library file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_LutLib_t * Fpga_LutLibCreate( char * FileName, int fVerbose ) +{ + char pBuffer[1000], * pToken; + Fpga_LutLib_t * p; + FILE * pFile; + int i, k; + + pFile = fopen( FileName, "r" ); + if ( pFile == NULL ) + { + printf( "Cannot open LUT library file \"%s\".\n", FileName ); + return NULL; + } + + p = ALLOC( Fpga_LutLib_t, 1 ); + memset( p, 0, sizeof(Fpga_LutLib_t) ); + p->pName = Extra_UtilStrsav( FileName ); + + i = 1; + while ( fgets( pBuffer, 1000, pFile ) != NULL ) + { + pToken = strtok( pBuffer, " \t\n" ); + if ( pToken == NULL ) + continue; + if ( pToken[0] == '#' ) + continue; + if ( i != atoi(pToken) ) + { + printf( "Error in the LUT library file \"%s\".\n", FileName ); + free( p ); + return NULL; + } + + // read area + pToken = strtok( NULL, " \t\n" ); + p->pLutAreas[i] = (float)atof(pToken); + + // read delays + k = 0; + while ( pToken = strtok( NULL, " \t\n" ) ) + p->pLutDelays[i][k++] = (float)atof(pToken); + + // check for out-of-bound + if ( k > i ) + { + printf( "LUT %d has too many pins (%d). Max allowed is %d.\n", i, k, i ); + return NULL; + } + + // check if var delays are specifies + if ( k > 1 ) + p->fVarPinDelays = 1; + + if ( i == FPGA_MAX_LUTSIZE ) + { + printf( "Skipping LUTs of size more than %d.\n", i ); + return NULL; + } + i++; + } + p->LutMax = i-1; + if ( p->LutMax > FPGA_MAX_LEAVES ) + { + p->LutMax = FPGA_MAX_LEAVES; + printf( "Warning: LUTs with more than %d input will not be used.\n", FPGA_MAX_LEAVES ); + } + + // check the library + if ( p->fVarPinDelays ) + { + for ( i = 1; i <= p->LutMax; i++ ) + for ( k = 0; k < i; k++ ) + { + if ( p->pLutDelays[i][k] <= 0.0 ) + printf( "Warning: Pin %d of LUT %d has delay %f. Pin delays should be non-negative numbers. Technology mapping may not work correctly.\n", + k, i, p->pLutDelays[i][k] ); + if ( k && p->pLutDelays[i][k-1] > p->pLutDelays[i][k] ) + printf( "Warning: Pin %d of LUT %d has delay %f. Pin %d of LUT %d has delay %f. Pin delays should be in non-degreasing order. Technology mapping may not work correctly.\n", + k-1, i, p->pLutDelays[i][k-1], + k, i, p->pLutDelays[i][k] ); + } + } + else + { + for ( i = 1; i <= p->LutMax; i++ ) + { + if ( p->pLutDelays[i][0] <= 0.0 ) + printf( "Warning: LUT %d has delay %f. Pin delays should be non-negative numbers. Technology mapping may not work correctly.\n", + k, i, p->pLutDelays[i][0] ); + } + } + + return p; +} + +/**Function************************************************************* + + Synopsis [Duplicates the LUT library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_LutLib_t * Fpga_LutLibDup( Fpga_LutLib_t * p ) +{ + Fpga_LutLib_t * pNew; + pNew = ALLOC( Fpga_LutLib_t, 1 ); + *pNew = *p; + pNew->pName = Extra_UtilStrsav( pNew->pName ); + return pNew; +} + +/**Function************************************************************* + + Synopsis [Frees the LUT library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_LutLibFree( Fpga_LutLib_t * pLutLib ) +{ + if ( pLutLib == NULL ) + return; + FREE( pLutLib->pName ); + FREE( pLutLib ); +} + + +/**Function************************************************************* + + Synopsis [Prints the LUT library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_LutLibPrint( Fpga_LutLib_t * pLutLib ) +{ + int i, k; + printf( "# The area/delay of k-variable LUTs:\n" ); + printf( "# k area delay\n" ); + if ( pLutLib->fVarPinDelays ) + { + for ( i = 1; i <= pLutLib->LutMax; i++ ) + { + printf( "%d %7.2f ", i, pLutLib->pLutAreas[i] ); + for ( k = 0; k < i; k++ ) + printf( " %7.2f", pLutLib->pLutDelays[i][k] ); + printf( "\n" ); + } + } + else + for ( i = 1; i <= pLutLib->LutMax; i++ ) + printf( "%d %7.2f %7.2f\n", i, pLutLib->pLutAreas[i], pLutLib->pLutDelays[i][0] ); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the delays are discrete.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_LutLibDelaysAreDiscrete( Fpga_LutLib_t * pLutLib ) +{ + float Delay; + int i; + for ( i = 1; i <= pLutLib->LutMax; i++ ) + { + Delay = pLutLib->pLutDelays[i][0]; + if ( ((float)((int)Delay)) != Delay ) + return 0; + } + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/fpga/fpgaMatch.c b/abc_with_bb_support/src/map/fpga/fpgaMatch.c new file mode 100644 index 000000000..7faf846ca --- /dev/null +++ b/abc_with_bb_support/src/map/fpga/fpgaMatch.c @@ -0,0 +1,794 @@ +/**CFile**************************************************************** + + FileName [fpgaMatch.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Technology mapping for variable-size-LUT FPGAs.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - August 18, 2004.] + + Revision [$Id: fpgaMatch.c,v 1.7 2004/09/30 21:18:10 satrajit Exp $] + +***********************************************************************/ + +#include "fpgaInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Fpga_MatchNode( Fpga_Man_t * p, Fpga_Node_t * pNode, int fDelayOriented ); +static int Fpga_MatchNodeArea( Fpga_Man_t * p, Fpga_Node_t * pNode ); +static int Fpga_MatchNodeSwitch( Fpga_Man_t * p, Fpga_Node_t * pNode ); + +static Fpga_Cut_t * Fpga_MappingAreaWithoutNode( Fpga_Man_t * p, Fpga_Node_t * pFanout, Fpga_Node_t * pNodeNo ); +static int Fpga_MappingMatchesAreaArray( Fpga_Man_t * p, Fpga_NodeVec_t * vNodes ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Finds the best delay assignment of LUTs.] + + Description [This procedure iterates through all the nodes + of the object graph reachable from the POs and assigns the best + match to each of them. If the flag fDelayOriented is set to 1, it + tries to minimize the arrival time and uses the area flow as a + tie-breaker. If the flag is set to 0, it considers all the cuts, + whose arrival times matches the required time at the node, and + minimizes the area flow using the arrival time as a tie-breaker. + + Before this procedure is called, the required times should be set + and the fanout counts should be computed. In the first iteration, + the required times are set to very large number (by NodeCreate) + and the fanout counts are set to the number of fanouts in the AIG. + In the following iterations, the required times are set by the + backward traversal, while the fanouts are estimated approximately. + + If the arrival times of the PI nodes are given, they should be + assigned to the PIs after the cuts are computed and before this + procedure is called for the first time.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_MappingMatches( Fpga_Man_t * p, int fDelayOriented ) +{ + ProgressBar * pProgress; + Fpga_Node_t * pNode; + int i, nNodes; + + // assign the arrival times of the PIs + for ( i = 0; i < p->nInputs; i++ ) + p->pInputs[i]->pCutBest->tArrival = p->pInputArrivals[i]; + + // match LUTs with nodes in the topological order + nNodes = p->vAnds->nSize; + pProgress = Extra_ProgressBarStart( stdout, nNodes ); + for ( i = 0; i < nNodes; i++ ) + { + pNode = p->vAnds->pArray[i]; + if ( !Fpga_NodeIsAnd( pNode ) ) + continue; + // skip a secondary node + if ( pNode->pRepr ) + continue; + // match the node + Fpga_MatchNode( p, pNode, fDelayOriented ); + Extra_ProgressBarUpdate( pProgress, i, "Matches ..." ); + } + Extra_ProgressBarStop( pProgress ); +/* + if ( !fDelayOriented ) + { + float Area = 0.0; + for ( i = 0; i < p->nOutputs; i++ ) + { + printf( "%5.2f ", Fpga_Regular(p->pOutputs[i])->pCutBest->aFlow ); + Area += Fpga_Regular(p->pOutputs[i])->pCutBest->aFlow; + } + printf( "\nTotal = %5.2f\n", Area ); + } +*/ + return 1; +} + +/**Function************************************************************* + + Synopsis [Computes the best matching for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_MatchNode( Fpga_Man_t * p, Fpga_Node_t * pNode, int fDelayOriented ) +{ + Fpga_Cut_t * pCut, * pCutBestOld; + int clk; + // make sure that at least one cut other than the trivial is present + if ( pNode->pCuts->pNext == NULL ) + { + printf( "\nError: A node in the mapping graph does not have feasible cuts.\n" ); + return 0; + } + + // estimate the fanouts of the node + if ( pNode->aEstFanouts < 0 ) + pNode->aEstFanouts = (float)pNode->nRefs; + else + pNode->aEstFanouts = (float)((2.0 * pNode->aEstFanouts + pNode->nRefs) / 3.0); +// pNode->aEstFanouts = (float)pNode->nRefs; + + pCutBestOld = pNode->pCutBest; + pNode->pCutBest = NULL; + for ( pCut = pNode->pCuts->pNext; pCut; pCut = pCut->pNext ) + { + // compute the arrival time of the cut and its area flow +clk = clock(); + Fpga_CutGetParameters( p, pCut ); +//p->time2 += clock() - clk; + // drop the cut if it does not meet the required times + if ( Fpga_FloatMoreThan(p, pCut->tArrival, pNode->tRequired) ) + continue; + // if no cut is assigned, use the current one + if ( pNode->pCutBest == NULL ) + { + pNode->pCutBest = pCut; + continue; + } + // choose the best cut using one of the two criteria: + // (1) delay oriented mapping (first traversal), delay first, area-flow as a tie-breaker + // (2) area recovery (subsequent traversals), area-flow first, delay as a tie-breaker + if ( (fDelayOriented && + (Fpga_FloatMoreThan(p, pNode->pCutBest->tArrival, pCut->tArrival) || + Fpga_FloatEqual(p, pNode->pCutBest->tArrival, pCut->tArrival) && Fpga_FloatMoreThan(p, pNode->pCutBest->aFlow, pCut->aFlow) )) || + (!fDelayOriented && + (Fpga_FloatMoreThan(p, pNode->pCutBest->aFlow, pCut->aFlow) || + Fpga_FloatEqual(p, pNode->pCutBest->aFlow, pCut->aFlow) && Fpga_FloatMoreThan(p, pNode->pCutBest->tArrival, pCut->tArrival))) ) + { + pNode->pCutBest = pCut; + } + } + + // make sure the match is found + if ( pNode->pCutBest == NULL ) + { + if ( pCutBestOld == NULL ) + { +// printf( "\nError: Could not match a node in the object graph.\n" ); + return 0; + } + pNode->pCutBest = pCutBestOld; + } + return 1; +} + + + + + +/**Function************************************************************* + + Synopsis [Finds the best area assignment of LUTs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_MappingMatchesArea( Fpga_Man_t * p ) +{ + ProgressBar * pProgress; + Fpga_Node_t * pNode; + int i, nNodes; + + // assign the arrival times of the PIs + for ( i = 0; i < p->nInputs; i++ ) + p->pInputs[i]->pCutBest->tArrival = p->pInputArrivals[i]; + + // match LUTs with nodes in the topological order + nNodes = p->vAnds->nSize; + pProgress = Extra_ProgressBarStart( stdout, nNodes ); + for ( i = 0; i < nNodes; i++ ) + { + pNode = p->vAnds->pArray[i]; + if ( !Fpga_NodeIsAnd( pNode ) ) + continue; + // skip a secondary node + if ( pNode->pRepr ) + continue; + // match the node + Fpga_MatchNodeArea( p, pNode ); + Extra_ProgressBarUpdate( pProgress, i, "Matches ..." ); + } + Extra_ProgressBarStop( pProgress ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Finds the best area assignment of LUTs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_MappingMatchesAreaArray( Fpga_Man_t * p, Fpga_NodeVec_t * vNodes ) +{ + Fpga_Node_t * pNode; + int i; + + // match LUTs with nodes in the topological order + for ( i = 0; i < vNodes->nSize; i++ ) + { + pNode = vNodes->pArray[i]; + if ( !Fpga_NodeIsAnd( pNode ) ) + continue; + // skip a secondary node + if ( pNode->pRepr ) + continue; + // match the node + if ( !Fpga_MatchNodeArea( p, pNode ) ) + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Computes the best matching for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_MatchNodeArea( Fpga_Man_t * p, Fpga_Node_t * pNode ) +{ + Fpga_Cut_t * pCut, * pCutBestOld; + float aAreaCutBest; + int clk; + // make sure that at least one cut other than the trivial is present + if ( pNode->pCuts->pNext == NULL ) + { + printf( "\nError: A node in the mapping graph does not have feasible cuts.\n" ); + return 0; + } + + // remember the old cut + pCutBestOld = pNode->pCutBest; + // deref the old cut + if ( pNode->nRefs ) + aAreaCutBest = Fpga_CutDeref( p, pNode, pNode->pCutBest, 0 ); + + // search for a better cut + pNode->pCutBest = NULL; + for ( pCut = pNode->pCuts->pNext; pCut; pCut = pCut->pNext ) + { + // compute the arrival time of the cut and its area flow +clk = clock(); + pCut->tArrival = Fpga_TimeCutComputeArrival( p, pCut ); +//p->time2 += clock() - clk; + // drop the cut if it does not meet the required times + if ( Fpga_FloatMoreThan( p, pCut->tArrival, pNode->tRequired ) ) + continue; + // get the area of this cut + pCut->aFlow = Fpga_CutGetAreaDerefed( p, pCut ); + // if no cut is assigned, use the current one + if ( pNode->pCutBest == NULL ) + { + pNode->pCutBest = pCut; + continue; + } + // choose the best cut as follows: exact area first, delay as a tie-breaker + if ( Fpga_FloatMoreThan(p, pNode->pCutBest->aFlow, pCut->aFlow) || + Fpga_FloatEqual(p, pNode->pCutBest->aFlow, pCut->aFlow) && Fpga_FloatMoreThan(p, pNode->pCutBest->tArrival, pCut->tArrival) ) + { + pNode->pCutBest = pCut; + } + } + + // make sure the match is found + if ( pNode->pCutBest == NULL ) + { + pNode->pCutBest = pCutBestOld; + // insert the new cut + if ( pNode->nRefs ) + pNode->pCutBest->aFlow = Fpga_CutRef( p, pNode, pNode->pCutBest, 0 ); +// printf( "\nError: Could not match a node in the object graph.\n" ); + return 0; + } + + // insert the new cut + // make sure the area selected is not worse then the original area + if ( pNode->nRefs ) + { + pNode->pCutBest->aFlow = Fpga_CutRef( p, pNode, pNode->pCutBest, 0 ); +// assert( pNode->pCutBest->aFlow <= aAreaCutBest ); +// assert( pNode->tRequired < FPGA_FLOAT_LARGE ); + } + return 1; +} + + + + +/**Function************************************************************* + + Synopsis [Finds the best area assignment of LUTs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_MappingMatchesSwitch( Fpga_Man_t * p ) +{ + ProgressBar * pProgress; + Fpga_Node_t * pNode; + int i, nNodes; + + // assign the arrival times of the PIs + for ( i = 0; i < p->nInputs; i++ ) + p->pInputs[i]->pCutBest->tArrival = p->pInputArrivals[i]; + + // match LUTs with nodes in the topological order + nNodes = p->vAnds->nSize; + pProgress = Extra_ProgressBarStart( stdout, nNodes ); + for ( i = 0; i < nNodes; i++ ) + { + pNode = p->vAnds->pArray[i]; + if ( !Fpga_NodeIsAnd( pNode ) ) + continue; + // skip a secondary node + if ( pNode->pRepr ) + continue; + // match the node + Fpga_MatchNodeSwitch( p, pNode ); + Extra_ProgressBarUpdate( pProgress, i, "Matches ..." ); + } + Extra_ProgressBarStop( pProgress ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Computes the best matching for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_MatchNodeSwitch( Fpga_Man_t * p, Fpga_Node_t * pNode ) +{ + Fpga_Cut_t * pCut, * pCutBestOld; + float aAreaCutBest; + int clk; + // make sure that at least one cut other than the trivial is present + if ( pNode->pCuts->pNext == NULL ) + { + printf( "\nError: A node in the mapping graph does not have feasible cuts.\n" ); + return 0; + } + + // remember the old cut + pCutBestOld = pNode->pCutBest; + // deref the old cut + if ( pNode->nRefs ) + aAreaCutBest = Fpga_CutDerefSwitch( p, pNode, pNode->pCutBest, 0 ); + + // search for a better cut + pNode->pCutBest = NULL; + for ( pCut = pNode->pCuts->pNext; pCut; pCut = pCut->pNext ) + { + // compute the arrival time of the cut and its area flow +clk = clock(); + pCut->tArrival = Fpga_TimeCutComputeArrival( p, pCut ); +//p->time2 += clock() - clk; + // drop the cut if it does not meet the required times + if ( Fpga_FloatMoreThan( p, pCut->tArrival, pNode->tRequired ) ) + continue; + // get the area of this cut + pCut->aFlow = Fpga_CutGetSwitchDerefed( p, pNode, pCut ); + // if no cut is assigned, use the current one + if ( pNode->pCutBest == NULL ) + { + pNode->pCutBest = pCut; + continue; + } + // choose the best cut as follows: exact area first, delay as a tie-breaker + if ( Fpga_FloatMoreThan(p, pNode->pCutBest->aFlow, pCut->aFlow) || + Fpga_FloatEqual(p, pNode->pCutBest->aFlow, pCut->aFlow) && Fpga_FloatMoreThan(p, pNode->pCutBest->tArrival, pCut->tArrival) ) + { + pNode->pCutBest = pCut; + } + } + + // make sure the match is found + if ( pNode->pCutBest == NULL ) + { + pNode->pCutBest = pCutBestOld; + // insert the new cut + if ( pNode->nRefs ) + pNode->pCutBest->aFlow = Fpga_CutRefSwitch( p, pNode, pNode->pCutBest, 0 ); +// printf( "\nError: Could not match a node in the object graph.\n" ); + return 0; + } + + // insert the new cut + // make sure the area selected is not worse then the original area + if ( pNode->nRefs ) + { + pNode->pCutBest->aFlow = Fpga_CutRefSwitch( p, pNode, pNode->pCutBest, 0 ); + assert( pNode->pCutBest->aFlow <= aAreaCutBest + 0.001 ); +// assert( pNode->tRequired < FPGA_FLOAT_LARGE ); + } + return 1; +} + + +#if 0 +/**function************************************************************* + + synopsis [References the cut.] + + description [This procedure is similar to the procedure NodeReclaim.] + + sideeffects [] + + seealso [] + +***********************************************************************/ +void Fpga_Experiment( Fpga_Man_t * p ) +{ + int Counter[10] = {0}; + Fpga_Node_t * pNode; + int i; + + for ( i = 0; i < p->nOutputs; i++ ) + { + pNode = Fpga_Regular(p->pOutputs[i]); + pNode->vFanouts = NULL; + } + + for ( i = 0; i < p->vAnds->nSize; i++ ) + { + pNode = p->vAnds->pArray[i]; + if ( !Fpga_NodeIsAnd( pNode ) ) + continue; + if ( pNode->vFanouts == NULL ) + continue; + if ( pNode->vFanouts->nSize >= 10 ) + continue; + Counter[pNode->vFanouts->nSize]++; + } + + printf( "Fanout stats: " ); + for ( i = 0; i < 10; i++ ) + printf( " %d=%d", i, Counter[i] ); + printf( "\n" ); + printf( "Area before = %4.2f.\n", Fpga_MappingArea(p) ); + + for ( i = 0; i < p->vAnds->nSize; i++ ) + { + Fpga_NodeVec_t * vNodesTfo; + float AreaBefore; + + pNode = p->vAnds->pArray[i]; + if ( !Fpga_NodeIsAnd( pNode ) ) + continue; + if ( pNode->vFanouts == NULL ) + continue; + if ( pNode->vFanouts->nSize != 1 && pNode->vFanouts->nSize != 2 && pNode->vFanouts->nSize != 3 ) + continue; + +// assert( pNode->nRefs > 0 ); + if ( pNode->nRefs == 0 ) + continue; + + AreaBefore = pNode->pCutBest->aFlow; + pNode->pCutBest->aFlow = FPGA_FLOAT_LARGE; + + Fpga_TimeComputeRequiredGlobal( p, 0 ); + + vNodesTfo = Fpga_CollectNodeTfo( p, pNode ); + if ( Fpga_MappingMatchesAreaArray( p, vNodesTfo ) == 0 ) + printf( "attempt failed\n" ); + else + printf( "attempt succeeded\n" ); + Fpga_NodeVecFree( vNodesTfo ); + + pNode->pCutBest->aFlow = AreaBefore; +// break; + } + printf( "Area after = %4.2f.\n", Fpga_MappingArea(p) ); +// printf( "AREA GAIN = %4.2f (%.2f %%)\n", GainTotal, 100.0 * GainTotal / Fpga_MappingArea(p) ); +} + + + +/**function************************************************************* + + synopsis [References the cut.] + + description [This procedure is similar to the procedure NodeReclaim.] + + sideeffects [] + + seealso [] + +***********************************************************************/ +void Fpga_Experiment2( Fpga_Man_t * p ) +{ + int Counter[10] = {0}; + Fpga_Cut_t * ppCutsNew[10]; + Fpga_Cut_t * ppCutsOld[10]; + Fpga_Node_t * pFanout, * pNode; + float Gain, Loss, GainTotal, Area1, Area2; + int i, k; + + for ( i = 0; i < p->nOutputs; i++ ) + { + pNode = Fpga_Regular(p->pOutputs[i]); + pNode->vFanouts = NULL; + } + + for ( i = 0; i < p->vAnds->nSize; i++ ) + { + pNode = p->vAnds->pArray[i]; + if ( !Fpga_NodeIsAnd( pNode ) ) + continue; + if ( pNode->vFanouts == NULL ) + continue; + if ( pNode->vFanouts->nSize >= 10 ) + continue; + Counter[pNode->vFanouts->nSize]++; + } + + printf( "Fanout stats: " ); + for ( i = 0; i < 10; i++ ) + printf( " %d=%d", i, Counter[i] ); + printf( "\n" ); + printf( "Area before = %4.2f.\n", Fpga_MappingArea(p) ); + + GainTotal = 0; + for ( i = 0; i < p->vAnds->nSize; i++ ) + { + pNode = p->vAnds->pArray[i]; + if ( !Fpga_NodeIsAnd( pNode ) ) + continue; + if ( pNode->vFanouts == NULL ) + continue; + if ( pNode->vFanouts->nSize != 2 )//&& pNode->vFanouts->nSize != 2 && pNode->vFanouts->nSize != 3 ) + continue; + + assert( pNode->nRefs > 0 ); + + // for all fanouts, find the best cut without this node + for ( k = 0; k < pNode->vFanouts->nSize; k++ ) + { + pFanout = pNode->vFanouts->pArray[k]; + ppCutsOld[k] = pFanout->pCutBest; + ppCutsNew[k] = Fpga_MappingAreaWithoutNode( p, pFanout, pNode ); + if ( ppCutsNew[k] == NULL ) + break; + } + if ( k != pNode->vFanouts->nSize ) + { + printf( "Node %4d: Skipped.\n", pNode->Num ); + continue; + } + + + // compute the area after replacing all the cuts + Gain = 0; + for ( k = 0; k < pNode->vFanouts->nSize; k++ ) + { + pFanout = pNode->vFanouts->pArray[k]; + // deref old cut + Area1 = Fpga_MatchAreaDeref( p, ppCutsOld[k] ); + // assign new cut + pFanout->pCutBest = ppCutsNew[k]; + // ref new cut + Area2 = Fpga_MatchAreaRef( p, ppCutsNew[k] ); + // compute the gain + Gain += Area1 - Area2; + } + + printf( "%d ", pNode->nRefs ); + + // undo the whole thing + Loss = 0; + for ( k = 0; k < pNode->vFanouts->nSize; k++ ) + { + pFanout = pNode->vFanouts->pArray[k]; + // deref old cut + Area1 = Fpga_MatchAreaDeref( p, ppCutsNew[k] ); + // assign new cut + pFanout->pCutBest = ppCutsOld[k]; + // ref new cut + Area2 = Fpga_MatchAreaRef( p, ppCutsOld[k] ); + // compute the gain + Loss += Area2 - Area1; + } + assert( Gain == Loss ); + + + printf( "Node %4d: Fanouts = %d. Cut area = %4.2f. Gain = %4.2f.\n", + pNode->Num, pNode->nRefs, pNode->pCutBest->aFlow, Gain ); + + if ( Gain > 0 ) + GainTotal += Gain; + } + printf( "Area after = %4.2f.\n", Fpga_MappingArea(p) ); + printf( "AREA GAIN = %4.2f (%.2f %%)\n", GainTotal, 100.0 * GainTotal / Fpga_MappingArea(p) ); +} + + +/**function************************************************************* + + synopsis [Computes the loss of area when node is not allowed.] + + description [Returning FPGA_FLOAT_LARGE means it does not exist.] + + sideeffects [] + + seealso [] + +***********************************************************************/ +Fpga_Cut_t * Fpga_MappingAreaWithoutNode( Fpga_Man_t * p, Fpga_Node_t * pNode, Fpga_Node_t * pNodeNo ) +{ + Fpga_Cut_t * pCut, * pCutBestOld, * pCutRes; + float aAreaCutBest; + int i, clk; + // make sure that at least one cut other than the trivial is present + if ( pNode->pCuts->pNext == NULL ) + { + printf( "\nError: A node in the mapping graph does not have feasible cuts.\n" ); + return 0; + } + + assert( pNode->nRefs > 0 ); + + // remember the old cut + pCutBestOld = pNode->pCutBest; + // deref the old cut + aAreaCutBest = Fpga_MatchAreaDeref( p, pNode->pCutBest ); + + // search for a better cut + pNode->pCutBest = NULL; + for ( pCut = pNode->pCuts->pNext; pCut; pCut = pCut->pNext ) + { + // compute the arrival time of the cut and its area flow +clk = clock(); + Fpga_MatchCutGetArrTime( p, pCut ); +//p->time2 += clock() - clk; + // drop the cut if it does not meet the required times + if ( pCut->tArrival > pNode->tRequired ) + continue; + + // skip the cut if it contains the no-node + for ( i = 0; i < pCut->nLeaves; i++ ) + if ( pCut->ppLeaves[i] == pNodeNo ) + break; + if ( i != pCut->nLeaves ) + continue; + + // get the area of this cut + pCut->aFlow = Fpga_MatchAreaCount( p, pCut ); + // if no cut is assigned, use the current one + if ( pNode->pCutBest == NULL ) + { + pNode->pCutBest = pCut; + continue; + } + // choose the best cut as follows: exact area first, delay as a tie-breaker + if ( pNode->pCutBest->aFlow > pCut->aFlow || + pNode->pCutBest->aFlow == pCut->aFlow && pNode->pCutBest->tArrival > pCut->tArrival ) + { + pNode->pCutBest = pCut; + } + } + + // make sure the match is found + if ( pNode->pCutBest == NULL ) + { + pNode->pCutBest = pCutBestOld; + // insert the new cut + pNode->pCutBest->aFlow = Fpga_MatchAreaRef( p, pNode->pCutBest ); + return NULL; + } + + pCutRes = pNode->pCutBest; + pNode->pCutBest = pCutBestOld; + + // insert the new cut + pNode->pCutBest->aFlow = Fpga_MatchAreaRef( p, pNode->pCutBest ); + + // make sure the area selected is not worse then the original area + assert( pNode->pCutBest->aFlow == aAreaCutBest ); + assert( pNode->tRequired < FPGA_FLOAT_LARGE ); + return pCutRes; +} + +#endif + + +/**function************************************************************* + + synopsis [Performs area minimization using a heuristic algorithm.] + + description [] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Fpga_FindBestNode( Fpga_Man_t * p, Fpga_NodeVec_t * vNodes, Fpga_Node_t ** ppNode, Fpga_Cut_t ** ppCutBest ) +{ + Fpga_Node_t * pNode; + Fpga_Cut_t * pCut; + float Gain, CutArea1, CutArea2, CutArea3; + int i; + + Gain = 0; + for ( i = 0; i < vNodes->nSize; i++ ) + { + pNode = vNodes->pArray[i]; + // deref the current cut + CutArea1 = Fpga_CutDeref( p, pNode, pNode->pCutBest, 0 ); + + // ref all the cuts + for ( pCut = pNode->pCuts->pNext; pCut; pCut = pCut->pNext ) + { + if ( pCut == pNode->pCutBest ) + continue; + if ( pCut->tArrival > pNode->tRequired ) + continue; + + CutArea2 = Fpga_CutGetAreaDerefed( p, pCut ); + if ( Gain < CutArea1 - CutArea2 ) + { + *ppNode = pNode; + *ppCutBest = pCut; + Gain = CutArea1 - CutArea2; + } + } + // ref the old cut + CutArea3 = Fpga_CutRef( p, pNode, pNode->pCutBest, 0 ); + assert( CutArea1 == CutArea3 ); + } + if ( Gain == 0 ) + printf( "Returning no gain.\n" ); + + return Gain; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/map/fpga/fpgaSwitch.c b/abc_with_bb_support/src/map/fpga/fpgaSwitch.c new file mode 100644 index 000000000..79c68692c --- /dev/null +++ b/abc_with_bb_support/src/map/fpga/fpgaSwitch.c @@ -0,0 +1,151 @@ +/**CFile**************************************************************** + + FileName [fpgaSwitch.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 8, 2003.] + + Revision [$Id: fpgaSwitch.h,v 1.0 2003/09/08 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fpgaInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**function************************************************************* + + synopsis [Computes the exact area associated with the cut.] + + description [] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Fpga_CutGetSwitchDerefed( Fpga_Man_t * pMan, Fpga_Node_t * pNode, Fpga_Cut_t * pCut ) +{ + float aResult, aResult2; + aResult2 = Fpga_CutRefSwitch( pMan, pNode, pCut, 0 ); + aResult = Fpga_CutDerefSwitch( pMan, pNode, pCut, 0 ); +// assert( aResult == aResult2 ); + return aResult; +} + +/**function************************************************************* + + synopsis [References the cut.] + + description [This procedure is similar to the procedure NodeReclaim.] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Fpga_CutRefSwitch( Fpga_Man_t * pMan, Fpga_Node_t * pNode, Fpga_Cut_t * pCut, int fFanouts ) +{ + Fpga_Node_t * pNodeChild; + float aArea; + int i; + // start the area of this cut + aArea = pNode->Switching; + if ( pCut->nLeaves == 1 ) + return aArea; + // go through the children + for ( i = 0; i < pCut->nLeaves; i++ ) + { + pNodeChild = pCut->ppLeaves[i]; + assert( pNodeChild->nRefs >= 0 ); + if ( pNodeChild->nRefs++ > 0 ) + continue; + aArea += Fpga_CutRefSwitch( pMan, pNodeChild, pNodeChild->pCutBest, fFanouts ); + } + return aArea; +} + +/**function************************************************************* + + synopsis [Dereferences the cut.] + + description [This procedure is similar to the procedure NodeRecusiveDeref.] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Fpga_CutDerefSwitch( Fpga_Man_t * pMan, Fpga_Node_t * pNode, Fpga_Cut_t * pCut, int fFanouts ) +{ + Fpga_Node_t * pNodeChild; + float aArea; + int i; + // start the area of this cut + aArea = pNode->Switching; + if ( pCut->nLeaves == 1 ) + return aArea; + // go through the children + for ( i = 0; i < pCut->nLeaves; i++ ) + { + pNodeChild = pCut->ppLeaves[i]; + assert( pNodeChild->nRefs > 0 ); + if ( --pNodeChild->nRefs > 0 ) + continue; + aArea += Fpga_CutDerefSwitch( pMan, pNodeChild, pNodeChild->pCutBest, fFanouts ); + } + return aArea; +} + +/**Function************************************************************* + + Synopsis [Computes the array of mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Fpga_MappingGetSwitching( Fpga_Man_t * pMan, Fpga_NodeVec_t * vMapping ) +{ + Fpga_Node_t * pNode; + float Switch; + int i; + Switch = 0.0; + for ( i = 0; i < vMapping->nSize; i++ ) + { + pNode = vMapping->pArray[i]; + // at least one phase has the best cut assigned + assert( !Fpga_NodeIsAnd(pNode) || pNode->pCutBest != NULL ); + // at least one phase is used in the mapping + assert( pNode->nRefs > 0 ); + // compute the array due to the supergate + Switch += pNode->Switching; + } + // add buffer for each CO driven by a CI + for ( i = 0; i < pMan->nOutputs; i++ ) + if ( Fpga_NodeIsVar(pMan->pOutputs[i]) && !Fpga_IsComplement(pMan->pOutputs[i]) ) + Switch += pMan->pOutputs[i]->Switching; + return Switch; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/fpga/fpgaTime.c b/abc_with_bb_support/src/map/fpga/fpgaTime.c new file mode 100644 index 000000000..d278886f7 --- /dev/null +++ b/abc_with_bb_support/src/map/fpga/fpgaTime.c @@ -0,0 +1,262 @@ +/**CFile**************************************************************** + + FileName [fpgaTime.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Technology mapping for variable-size-LUT FPGAs.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - August 18, 2004.] + + Revision [$Id: fpgaTime.c,v 1.1 2005/01/23 06:59:42 alanmi Exp $] + +***********************************************************************/ + +#include "fpgaInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes the arrival times of the cut.] + + Description [Computes the maximum arrival time of the cut leaves and + adds the delay of the LUT.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Fpga_TimeCutComputeArrival( Fpga_Man_t * pMan, Fpga_Cut_t * pCut ) +{ + int i; + float tArrival; + tArrival = -FPGA_FLOAT_LARGE; + for ( i = 0; i < pCut->nLeaves; i++ ) + if ( tArrival < pCut->ppLeaves[i]->pCutBest->tArrival ) + tArrival = pCut->ppLeaves[i]->pCutBest->tArrival; + tArrival += pMan->pLutLib->pLutDelays[pCut->nLeaves][0]; + return tArrival; +} + +/**Function************************************************************* + + Synopsis [Computes the arrival times of the cut recursively.] + + Description [When computing the arrival time for the previously unused + cuts, their arrival time may be incorrect because their fanins have + incorrect arrival time. This procedure is called to fix this problem.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Fpga_TimeCutComputeArrival_rec( Fpga_Man_t * pMan, Fpga_Cut_t * pCut ) +{ + int i; + for ( i = 0; i < pCut->nLeaves; i++ ) + if ( pCut->ppLeaves[i]->nRefs == 0 ) + Fpga_TimeCutComputeArrival_rec( pMan, pCut->ppLeaves[i]->pCutBest ); + return Fpga_TimeCutComputeArrival( pMan, pCut ); +} + +/**Function************************************************************* + + Synopsis [Computes the maximum arrival times.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Fpga_TimeComputeArrivalMax( Fpga_Man_t * p ) +{ + float fRequired; + int i; + if ( p->fLatchPaths && p->nLatches == 0 ) + { + printf( "Delay optimization of latch path is not performed because there is no latches.\n" ); + p->fLatchPaths = 0; + } + // get the critical PO arrival time + fRequired = -FPGA_FLOAT_LARGE; + if ( p->fLatchPaths ) + { + for ( i = p->nOutputs - p->nLatches; i < p->nOutputs; i++ ) + { + if ( Fpga_NodeIsConst(p->pOutputs[i]) ) + continue; + fRequired = FPGA_MAX( fRequired, Fpga_Regular(p->pOutputs[i])->pCutBest->tArrival ); +// printf( " %5.1f", Fpga_Regular(p->pOutputs[i])->pCutBest->tArrival ); + } +// printf( "Required latches = %5.1f\n", fRequired ); + } + else + { + for ( i = 0; i < p->nOutputs; i++ ) + { + if ( Fpga_NodeIsConst(p->pOutputs[i]) ) + continue; + fRequired = FPGA_MAX( fRequired, Fpga_Regular(p->pOutputs[i])->pCutBest->tArrival ); +// printf( " %5.1f", Fpga_Regular(p->pOutputs[i])->pCutBest->tArrival ); + } +// printf( "Required outputs = %5.1f\n", fRequired ); + } + return fRequired; +} + +/**Function************************************************************* + + Synopsis [Computes the required times of all nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_TimeComputeRequiredGlobal( Fpga_Man_t * p, int fFirstTime ) +{ + p->fRequiredGlo = Fpga_TimeComputeArrivalMax( p ); + // update the required times according to the target + if ( p->DelayTarget != -1 ) + { + if ( p->fRequiredGlo > p->DelayTarget + p->fEpsilon ) + { + if ( fFirstTime ) + printf( "Cannot meet the target required times (%4.2f). Mapping continues anyway.\n", p->DelayTarget ); + } + else if ( p->fRequiredGlo < p->DelayTarget - p->fEpsilon ) + { + if ( fFirstTime ) + printf( "Relaxing the required times from (%4.2f) to the target (%4.2f).\n", p->fRequiredGlo, p->DelayTarget ); + p->fRequiredGlo = p->DelayTarget; + } + } + Fpga_TimeComputeRequired( p, p->fRequiredGlo ); +} + +/**Function************************************************************* + + Synopsis [Computes the required times of all nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_TimeComputeRequired( Fpga_Man_t * p, float fRequired ) +{ + int i; + // clean the required times and the fanout counts for all nodes + for ( i = 0; i < p->vAnds->nSize; i++ ) + p->vAnds->pArray[i]->tRequired = FPGA_FLOAT_LARGE; + // set the required times for the POs + if ( p->fLatchPaths ) + for ( i = p->nOutputs - p->nLatches; i < p->nOutputs; i++ ) + Fpga_Regular(p->pOutputs[i])->tRequired = fRequired; + else + for ( i = 0; i < p->nOutputs; i++ ) + Fpga_Regular(p->pOutputs[i])->tRequired = fRequired; + // collect nodes reachable from POs in the DFS order through the best cuts + Fpga_TimePropagateRequired( p, p->vMapping ); +/* + { + int Counter = 0; + for ( i = 0; i < p->vAnds->nSize; i++ ) + if ( p->vAnds->pArray[i]->tRequired > FPGA_FLOAT_LARGE - 100 ) + Counter++; + printf( "The number of nodes with large required times = %d.\n", Counter ); + } +*/ +} + +/**Function************************************************************* + + Synopsis [Computes the required times of the given nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_TimePropagateRequired( Fpga_Man_t * p, Fpga_NodeVec_t * vNodes ) +{ + Fpga_Node_t * pNode, * pChild; + float fRequired; + int i, k; + + // sorts the nodes in the decreasing order of levels +// Fpga_MappingSortByLevel( p, vNodes, 0 ); + // the nodes area already sorted in Fpga_MappingSetRefsAndArea() + + // go through the nodes in the reverse topological order + for ( k = 0; k < vNodes->nSize; k++ ) + { + pNode = vNodes->pArray[k]; + if ( !Fpga_NodeIsAnd(pNode) ) + continue; + // get the required time for children + fRequired = pNode->tRequired - p->pLutLib->pLutDelays[pNode->pCutBest->nLeaves][0]; + // update the required time of the children + for ( i = 0; i < pNode->pCutBest->nLeaves; i++ ) + { + pChild = pNode->pCutBest->ppLeaves[i]; + pChild->tRequired = FPGA_MIN( pChild->tRequired, fRequired ); + } + } +} + + + +/**Function************************************************************* + + Synopsis [Computes the required times of all nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_TimePropagateArrival( Fpga_Man_t * p ) +{ + Fpga_Node_t * pNode; + Fpga_Cut_t * pCut; + int i; + + // clean the required times and the fanout counts for all nodes + for ( i = 0; i < p->vAnds->nSize; i++ ) + { + pNode = p->vAnds->pArray[i]; + for ( pCut = pNode->pCuts->pNext; pCut; pCut = pCut->pNext ) + pCut->tArrival = Fpga_TimeCutComputeArrival( p, pCut ); + } +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/fpga/fpgaTruth.c b/abc_with_bb_support/src/map/fpga/fpgaTruth.c new file mode 100644 index 000000000..569bf8747 --- /dev/null +++ b/abc_with_bb_support/src/map/fpga/fpgaTruth.c @@ -0,0 +1,166 @@ +/**CFile**************************************************************** + + FileName [fpgaTruth.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Technology mapping for variable-size-LUT FPGAs.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - August 18, 2004.] + + Revision [$Id: fpgaTruth.c,v 1.4 2005/01/23 06:59:42 alanmi Exp $] + +***********************************************************************/ + +#include "fpgaInt.h" +#include "cudd.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Recursively derives the truth table for the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Fpga_TruthsCutBdd_rec( DdManager * dd, Fpga_Cut_t * pCut, Fpga_NodeVec_t * vVisited ) +{ + DdNode * bFunc, * bFunc0, * bFunc1; + assert( !Fpga_IsComplement(pCut) ); + // if the cut is visited, return the result + if ( pCut->uSign ) + return (DdNode *)pCut->uSign; + // compute the functions of the children + bFunc0 = Fpga_TruthsCutBdd_rec( dd, Fpga_CutRegular(pCut->pOne), vVisited ); Cudd_Ref( bFunc0 ); + bFunc0 = Cudd_NotCond( bFunc0, Fpga_CutIsComplement(pCut->pOne) ); + bFunc1 = Fpga_TruthsCutBdd_rec( dd, Fpga_CutRegular(pCut->pTwo), vVisited ); Cudd_Ref( bFunc1 ); + bFunc1 = Cudd_NotCond( bFunc1, Fpga_CutIsComplement(pCut->pTwo) ); + // get the function of the cut + bFunc = Cudd_bddAnd( dd, bFunc0, bFunc1 ); Cudd_Ref( bFunc ); + bFunc = Cudd_NotCond( bFunc, pCut->Phase ); + Cudd_RecursiveDeref( dd, bFunc0 ); + Cudd_RecursiveDeref( dd, bFunc1 ); + assert( pCut->uSign == 0 ); + pCut->uSign = (unsigned)bFunc; + // add this cut to the visited list + Fpga_NodeVecPush( vVisited, (Fpga_Node_t *)pCut ); + return bFunc; +} + +/**Function************************************************************* + + Synopsis [Derives the truth table for one cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void * Fpga_TruthsCutBdd( void * dd, Fpga_Cut_t * pCut ) +{ + Fpga_NodeVec_t * vVisited; + DdNode * bFunc; + int i; + assert( pCut->nLeaves > 1 ); + // set the leaf variables + for ( i = 0; i < pCut->nLeaves; i++ ) + pCut->ppLeaves[i]->pCuts->uSign = (unsigned)Cudd_bddIthVar( dd, i ); + // recursively compute the function + vVisited = Fpga_NodeVecAlloc( 10 ); + bFunc = Fpga_TruthsCutBdd_rec( dd, pCut, vVisited ); Cudd_Ref( bFunc ); + // clean the intermediate BDDs + for ( i = 0; i < pCut->nLeaves; i++ ) + pCut->ppLeaves[i]->pCuts->uSign = 0; + for ( i = 0; i < vVisited->nSize; i++ ) + { + pCut = (Fpga_Cut_t *)vVisited->pArray[i]; + Cudd_RecursiveDeref( dd, (DdNode*)pCut->uSign ); + pCut->uSign = 0; + } +// printf( "%d ", vVisited->nSize ); + Fpga_NodeVecFree( vVisited ); + Cudd_Deref( bFunc ); + return bFunc; +} + + +/**Function************************************************************* + + Synopsis [Recursively derives the truth table for the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_CutVolume_rec( Fpga_Cut_t * pCut, Fpga_NodeVec_t * vVisited ) +{ + assert( !Fpga_IsComplement(pCut) ); + if ( pCut->fMark ) + return; + pCut->fMark = 1; + Fpga_CutVolume_rec( Fpga_CutRegular(pCut->pOne), vVisited ); + Fpga_CutVolume_rec( Fpga_CutRegular(pCut->pTwo), vVisited ); + Fpga_NodeVecPush( vVisited, (Fpga_Node_t *)pCut ); +} + +/**Function************************************************************* + + Synopsis [Derives the truth table for one cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_CutVolume( Fpga_Cut_t * pCut ) +{ + Fpga_NodeVec_t * vVisited; + int Volume, i; + assert( pCut->nLeaves > 1 ); + // set the leaf variables + for ( i = 0; i < pCut->nLeaves; i++ ) + pCut->ppLeaves[i]->pCuts->fMark = 1; + // recursively compute the function + vVisited = Fpga_NodeVecAlloc( 10 ); + Fpga_CutVolume_rec( pCut, vVisited ); + // clean the marks + for ( i = 0; i < pCut->nLeaves; i++ ) + pCut->ppLeaves[i]->pCuts->fMark = 0; + for ( i = 0; i < vVisited->nSize; i++ ) + { + pCut = (Fpga_Cut_t *)vVisited->pArray[i]; + pCut->fMark = 0; + } + Volume = vVisited->nSize; + printf( "%d ", Volume ); + Fpga_NodeVecFree( vVisited ); + return Volume; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/fpga/fpgaUtils.c b/abc_with_bb_support/src/map/fpga/fpgaUtils.c new file mode 100644 index 000000000..cf9a91d3d --- /dev/null +++ b/abc_with_bb_support/src/map/fpga/fpgaUtils.c @@ -0,0 +1,986 @@ +/**CFile**************************************************************** + + FileName [fpgaUtils.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Technology mapping for variable-size-LUT FPGAs.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - August 18, 2004.] + + Revision [$Id: fpgaUtils.c,v 1.3 2004/07/06 04:55:58 alanmi Exp $] + +***********************************************************************/ + +#include "fpgaInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define FPGA_CO_LIST_SIZE 5 + +static void Fpga_MappingDfs_rec( Fpga_Node_t * pNode, Fpga_NodeVec_t * vNodes, int fCollectEquiv ); +static void Fpga_MappingDfsCuts_rec( Fpga_Node_t * pNode, Fpga_NodeVec_t * vNodes ); +static int Fpga_MappingCompareOutputDelay( Fpga_Node_t ** ppNode1, Fpga_Node_t ** ppNode2 ); +static void Fpga_MappingFindLatest( Fpga_Man_t * p, int * pNodes, int nNodesMax ); +static void Fpga_DfsLim_rec( Fpga_Node_t * pNode, int Level, Fpga_NodeVec_t * vNodes ); +static int Fpga_CollectNodeTfo_rec( Fpga_Node_t * pNode, Fpga_Node_t * pPivot, Fpga_NodeVec_t * vVisited, Fpga_NodeVec_t * vTfo ); +static Fpga_NodeVec_t * Fpga_MappingOrderCosByLevel( Fpga_Man_t * pMan ); +static Fpga_Man_t * s_pMan = NULL; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + + +/**Function************************************************************* + + Synopsis [Computes the DFS ordering of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_NodeVec_t * Fpga_MappingDfs( Fpga_Man_t * pMan, int fCollectEquiv ) +{ + Fpga_NodeVec_t * vNodes;//, * vNodesCo; + Fpga_Node_t * pNode; + int i; + // collect the CO nodes by level +// vNodesCo = Fpga_MappingOrderCosByLevel( pMan ); + // start the array + vNodes = Fpga_NodeVecAlloc( 100 ); + // collect the PIs + for ( i = 0; i < pMan->nInputs; i++ ) + { + pNode = pMan->pInputs[i]; + Fpga_NodeVecPush( vNodes, pNode ); + pNode->fMark0 = 1; + } + // perform the traversal + for ( i = 0; i < pMan->nOutputs; i++ ) + Fpga_MappingDfs_rec( Fpga_Regular(pMan->pOutputs[i]), vNodes, fCollectEquiv ); +// for ( i = vNodesCo->nSize - 1; i >= 0 ; i-- ) +// for ( pNode = vNodesCo->pArray[i]; pNode; pNode = (Fpga_Node_t *)pNode->pData0 ) +// Fpga_MappingDfs_rec( pNode, vNodes, fCollectEquiv ); + // clean the node marks + for ( i = 0; i < vNodes->nSize; i++ ) + vNodes->pArray[i]->fMark0 = 0; +// for ( i = 0; i < pMan->nOutputs; i++ ) +// Fpga_MappingUnmark_rec( Fpga_Regular(pMan->pOutputs[i]) ); +// Fpga_NodeVecFree( vNodesCo ); + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Recursively computes the DFS ordering of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_MappingDfs_rec( Fpga_Node_t * pNode, Fpga_NodeVec_t * vNodes, int fCollectEquiv ) +{ + assert( !Fpga_IsComplement(pNode) ); + if ( pNode->fMark0 ) + return; + // visit the transitive fanin + if ( Fpga_NodeIsAnd(pNode) ) + { + Fpga_MappingDfs_rec( Fpga_Regular(pNode->p1), vNodes, fCollectEquiv ); + Fpga_MappingDfs_rec( Fpga_Regular(pNode->p2), vNodes, fCollectEquiv ); + } + // visit the equivalent nodes + if ( fCollectEquiv && pNode->pNextE ) + Fpga_MappingDfs_rec( pNode->pNextE, vNodes, fCollectEquiv ); + // make sure the node is not visited through the equivalent nodes + assert( pNode->fMark0 == 0 ); + // mark the node as visited + pNode->fMark0 = 1; + // add the node to the list + Fpga_NodeVecPush( vNodes, pNode ); +} + +/**Function************************************************************* + + Synopsis [Computes the DFS ordering of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_NodeVec_t * Fpga_MappingDfsNodes( Fpga_Man_t * pMan, Fpga_Node_t ** ppNodes, int nNodes, int fEquiv ) +{ + Fpga_NodeVec_t * vNodes; + int i; + // perform the traversal + vNodes = Fpga_NodeVecAlloc( 200 ); + for ( i = 0; i < nNodes; i++ ) + Fpga_MappingDfs_rec( ppNodes[i], vNodes, fEquiv ); + for ( i = 0; i < vNodes->nSize; i++ ) + vNodes->pArray[i]->fMark0 = 0; + return vNodes; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Fpga_MappingGetAreaFlow( Fpga_Man_t * p ) +{ + float aFlowFlowTotal = 0; + int i; + for ( i = 0; i < p->nOutputs; i++ ) + { + if ( Fpga_NodeIsConst(p->pOutputs[i]) ) + continue; + aFlowFlowTotal += Fpga_Regular(p->pOutputs[i])->pCutBest->aFlow; + } + return aFlowFlowTotal; +} + +/**Function************************************************************* + + Synopsis [Computes the area of the current mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Fpga_MappingArea( Fpga_Man_t * pMan ) +{ + Fpga_Node_t * pNode; + float aTotal; + int i; + // perform the traversal + aTotal = 0; + for ( i = 0; i < pMan->vMapping->nSize; i++ ) + { + pNode = pMan->vMapping->pArray[i]; + aTotal += pMan->pLutLib->pLutAreas[pNode->pCutBest->nLeaves]; + } + return aTotal; +} + +/**Function************************************************************* + + Synopsis [Recursively computes the DFS ordering of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Fpga_MappingArea_rec( Fpga_Man_t * pMan, Fpga_Node_t * pNode, Fpga_NodeVec_t * vNodes ) +{ + float aArea; + int i; + assert( !Fpga_IsComplement(pNode) ); + if ( !Fpga_NodeIsAnd(pNode) ) + return 0; + if ( pNode->fMark0 ) + return 0; + assert( pNode->pCutBest != NULL ); + // visit the transitive fanin of the selected cut + aArea = 0; + for ( i = 0; i < pNode->pCutBest->nLeaves; i++ ) + aArea += Fpga_MappingArea_rec( pMan, pNode->pCutBest->ppLeaves[i], vNodes ); + // make sure the node is not visited through the fanin nodes + assert( pNode->fMark0 == 0 ); + // mark the node as visited + pNode->fMark0 = 1; + // add the node to the list + aArea += pMan->pLutLib->pLutAreas[pNode->pCutBest->nLeaves]; + // add the node to the list + Fpga_NodeVecPush( vNodes, pNode ); + return aArea; +} + +/**Function************************************************************* + + Synopsis [Computes the area of the current mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Fpga_MappingAreaTrav( Fpga_Man_t * pMan ) +{ + Fpga_NodeVec_t * vNodes; + float aTotal; + int i; + // perform the traversal + aTotal = 0; + vNodes = Fpga_NodeVecAlloc( 100 ); + for ( i = 0; i < pMan->nOutputs; i++ ) + aTotal += Fpga_MappingArea_rec( pMan, Fpga_Regular(pMan->pOutputs[i]), vNodes ); + for ( i = 0; i < vNodes->nSize; i++ ) + vNodes->pArray[i]->fMark0 = 0; + Fpga_NodeVecFree( vNodes ); + return aTotal; +} + + +/**Function************************************************************* + + Synopsis [Recursively computes the DFS ordering of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Fpga_MappingSetRefsAndArea_rec( Fpga_Man_t * pMan, Fpga_Node_t * pNode, Fpga_Node_t ** ppStore ) +{ + float aArea; + int i; + assert( !Fpga_IsComplement(pNode) ); + if ( pNode->nRefs++ ) + return 0; + if ( !Fpga_NodeIsAnd(pNode) ) + return 0; + assert( pNode->pCutBest != NULL ); + // store the node in the structure by level + pNode->pData0 = (char *)ppStore[pNode->Level]; + ppStore[pNode->Level] = pNode; + // visit the transitive fanin of the selected cut + aArea = pMan->pLutLib->pLutAreas[pNode->pCutBest->nLeaves]; + for ( i = 0; i < pNode->pCutBest->nLeaves; i++ ) + aArea += Fpga_MappingSetRefsAndArea_rec( pMan, pNode->pCutBest->ppLeaves[i], ppStore ); + return aArea; +} + +/**Function************************************************************* + + Synopsis [Sets the correct reference counts for the mapping.] + + Description [Collects the nodes in reverse topological order + and places in them in array pMan->vMapping.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Fpga_MappingSetRefsAndArea( Fpga_Man_t * pMan ) +{ + Fpga_Node_t * pNode, ** ppStore; + float aArea; + int i, LevelMax; + + // clean all references + for ( i = 0; i < pMan->vNodesAll->nSize; i++ ) + pMan->vNodesAll->pArray[i]->nRefs = 0; + + // allocate place to store the nodes + LevelMax = Fpga_MappingMaxLevel( pMan ); + ppStore = ALLOC( Fpga_Node_t *, LevelMax + 1 ); + memset( ppStore, 0, sizeof(Fpga_Node_t *) * (LevelMax + 1) ); + + // collect nodes reachable from POs in the DFS order through the best cuts + aArea = 0; + for ( i = 0; i < pMan->nOutputs; i++ ) + { + pNode = Fpga_Regular(pMan->pOutputs[i]); + if ( pNode == pMan->pConst1 ) + continue; + aArea += Fpga_MappingSetRefsAndArea_rec( pMan, pNode, ppStore ); + pNode->nRefs++; + } + + // reconnect the nodes in reverse topological order + pMan->vMapping->nSize = 0; + for ( i = LevelMax; i >= 0; i-- ) + for ( pNode = ppStore[i]; pNode; pNode = (Fpga_Node_t *)pNode->pData0 ) + Fpga_NodeVecPush( pMan->vMapping, pNode ); + free( ppStore ); + return aArea; +} + + +/**Function************************************************************* + + Synopsis [Compares the outputs by their arrival times.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_MappingCompareOutputDelay( Fpga_Node_t ** ppNode1, Fpga_Node_t ** ppNode2 ) +{ + Fpga_Node_t * pNode1 = Fpga_Regular(*ppNode1); + Fpga_Node_t * pNode2 = Fpga_Regular(*ppNode2); + float Arrival1 = pNode1->pCutBest? pNode1->pCutBest->tArrival : 0; + float Arrival2 = pNode2->pCutBest? pNode2->pCutBest->tArrival : 0; + if ( Arrival1 < Arrival2 ) + return -1; + if ( Arrival1 > Arrival2 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Finds given number of latest arriving COs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_MappingFindLatest( Fpga_Man_t * p, int * pNodes, int nNodesMax ) +{ + int nNodes, i, k, v; + assert( p->nOutputs >= nNodesMax ); + pNodes[0] = 0; + nNodes = 1; + for ( i = 1; i < p->nOutputs; i++ ) + { + for ( k = nNodes - 1; k >= 0; k-- ) + if ( Fpga_MappingCompareOutputDelay( &p->pOutputs[pNodes[k]], &p->pOutputs[i] ) >= 0 ) + break; + if ( k == nNodesMax - 1 ) + continue; + if ( nNodes < nNodesMax ) + nNodes++; + for ( v = nNodes - 1; v > k+1; v-- ) + pNodes[v] = pNodes[v-1]; + pNodes[k+1] = i; + } +} + +/**Function************************************************************* + + Synopsis [Prints a bunch of latest arriving outputs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_MappingPrintOutputArrivals( Fpga_Man_t * p ) +{ + Fpga_Node_t * pNode; + int pSorted[FPGA_CO_LIST_SIZE]; + int fCompl, Limit, MaxNameSize, i; + + // determine the number of nodes to print + Limit = (p->nOutputs > FPGA_CO_LIST_SIZE)? FPGA_CO_LIST_SIZE : p->nOutputs; + + // determine the order + Fpga_MappingFindLatest( p, pSorted, Limit ); + + // determine max size of the node's name + MaxNameSize = 0; + for ( i = 0; i < Limit; i++ ) + if ( MaxNameSize < (int)strlen(p->ppOutputNames[pSorted[i]]) ) + MaxNameSize = strlen(p->ppOutputNames[pSorted[i]]); + + // print the latest outputs + for ( i = 0; i < Limit; i++ ) + { + // get the i-th latest output + pNode = Fpga_Regular(p->pOutputs[pSorted[i]]); + fCompl = Fpga_IsComplement(p->pOutputs[pSorted[i]]); + // print out the best arrival time + printf( "Output %-*s : ", MaxNameSize + 3, p->ppOutputNames[pSorted[i]] ); + printf( "Delay = %8.2f ", (double)pNode->pCutBest->tArrival ); + if ( fCompl ) + printf( "NEG" ); + else + printf( "POS" ); + printf( "\n" ); + } +} + + +/**Function************************************************************* + + Synopsis [Sets up the truth tables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_MappingSetupTruthTables( unsigned uTruths[][2] ) +{ + int m, v; + // set up the truth tables + for ( m = 0; m < 32; m++ ) + for ( v = 0; v < 5; v++ ) + if ( m & (1 << v) ) + uTruths[v][0] |= (1 << m); + // make adjustments for the case of 6 variables + for ( v = 0; v < 5; v++ ) + uTruths[v][1] = uTruths[v][0]; + uTruths[5][0] = 0; + uTruths[5][1] = FPGA_FULL; +} + +/**Function************************************************************* + + Synopsis [Sets up the mask.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_MappingSetupMask( unsigned uMask[], int nVarsMax ) +{ + if ( nVarsMax == 6 ) + uMask[0] = uMask[1] = FPGA_FULL; + else + { + uMask[0] = FPGA_MASK(1 << nVarsMax); + uMask[1] = 0; + } +} + +/**Function************************************************************* + + Synopsis [Verify one useful property.] + + Description [This procedure verifies one useful property. After + the FRAIG construction with choice nodes is over, each primary node + should have fanins that are primary nodes. The primary nodes is the + one that does not have pNode->pRepr set to point to another node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_ManCheckConsistency( Fpga_Man_t * p ) +{ + Fpga_Node_t * pNode; + Fpga_NodeVec_t * pVec; + int i; + pVec = Fpga_MappingDfs( p, 0 ); + for ( i = 0; i < pVec->nSize; i++ ) + { + pNode = pVec->pArray[i]; + if ( Fpga_NodeIsVar(pNode) ) + { + if ( pNode->pRepr ) + printf( "Primary input %d is a secondary node.\n", pNode->Num ); + } + else if ( Fpga_NodeIsConst(pNode) ) + { + if ( pNode->pRepr ) + printf( "Constant 1 %d is a secondary node.\n", pNode->Num ); + } + else + { + if ( pNode->pRepr ) + printf( "Internal node %d is a secondary node.\n", pNode->Num ); + if ( Fpga_Regular(pNode->p1)->pRepr ) + printf( "Internal node %d has first fanin that is a secondary node.\n", pNode->Num ); + if ( Fpga_Regular(pNode->p2)->pRepr ) + printf( "Internal node %d has second fanin that is a secondary node.\n", pNode->Num ); + } + } + Fpga_NodeVecFree( pVec ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Compares the supergates by their level.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_CompareNodesByLevelDecreasing( Fpga_Node_t ** ppS1, Fpga_Node_t ** ppS2 ) +{ + if ( Fpga_Regular(*ppS1)->Level > Fpga_Regular(*ppS2)->Level ) + return -1; + if ( Fpga_Regular(*ppS1)->Level < Fpga_Regular(*ppS2)->Level ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Compares the supergates by their level.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_CompareNodesByLevelIncreasing( Fpga_Node_t ** ppS1, Fpga_Node_t ** ppS2 ) +{ + if ( Fpga_Regular(*ppS1)->Level < Fpga_Regular(*ppS2)->Level ) + return -1; + if ( Fpga_Regular(*ppS1)->Level > Fpga_Regular(*ppS2)->Level ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Orders the nodes in the decreasing order of levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_MappingSortByLevel( Fpga_Man_t * pMan, Fpga_NodeVec_t * vNodes, int fIncreasing ) +{ + if ( fIncreasing ) + qsort( (void *)vNodes->pArray, vNodes->nSize, sizeof(Fpga_Node_t *), + (int (*)(const void *, const void *)) Fpga_CompareNodesByLevelIncreasing ); + else + qsort( (void *)vNodes->pArray, vNodes->nSize, sizeof(Fpga_Node_t *), + (int (*)(const void *, const void *)) Fpga_CompareNodesByLevelDecreasing ); +// assert( Fpga_CompareNodesByLevel( vNodes->pArray, vNodes->pArray + vNodes->nSize - 1 ) <= 0 ); +} + +/**Function************************************************************* + + Synopsis [Computes the limited DFS ordering for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_NodeVec_t * Fpga_DfsLim( Fpga_Man_t * pMan, Fpga_Node_t * pNode, int nLevels ) +{ + Fpga_NodeVec_t * vNodes; + int i; + // perform the traversal + vNodes = Fpga_NodeVecAlloc( 100 ); + Fpga_DfsLim_rec( pNode, nLevels, vNodes ); + for ( i = 0; i < vNodes->nSize; i++ ) + vNodes->pArray[i]->fMark0 = 0; + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Recursively computes the DFS ordering of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_DfsLim_rec( Fpga_Node_t * pNode, int Level, Fpga_NodeVec_t * vNodes ) +{ + assert( !Fpga_IsComplement(pNode) ); + if ( pNode->fMark0 ) + return; + pNode->fMark0 = 1; + // visit the transitive fanin + Level--; + if ( Level > 0 && Fpga_NodeIsAnd(pNode) ) + { + Fpga_DfsLim_rec( Fpga_Regular(pNode->p1), Level, vNodes ); + Fpga_DfsLim_rec( Fpga_Regular(pNode->p2), Level, vNodes ); + } + // add the node to the list + Fpga_NodeVecPush( vNodes, pNode ); +} + +/**Function************************************************************* + + Synopsis [Computes the limited DFS ordering for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_ManCleanData0( Fpga_Man_t * pMan ) +{ + int i; + for ( i = 0; i < pMan->vNodesAll->nSize; i++ ) + pMan->vNodesAll->pArray[i]->pData0 = 0; +} + +/**Function************************************************************* + + Synopsis [Collects the TFO of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_NodeVec_t * Fpga_CollectNodeTfo( Fpga_Man_t * pMan, Fpga_Node_t * pNode ) +{ + Fpga_NodeVec_t * vVisited, * vTfo; + int i; + // perform the traversal + vVisited = Fpga_NodeVecAlloc( 100 ); + vTfo = Fpga_NodeVecAlloc( 100 ); + for ( i = 0; i < pMan->nOutputs; i++ ) + Fpga_CollectNodeTfo_rec( Fpga_Regular(pMan->pOutputs[i]), pNode, vVisited, vTfo ); + for ( i = 0; i < vVisited->nSize; i++ ) + vVisited->pArray[i]->fMark0 = vVisited->pArray[i]->fMark1 = 0; + Fpga_NodeVecFree( vVisited ); + return vTfo; +} + +/**Function************************************************************* + + Synopsis [Collects the TFO of the node.] + + Description [Returns 1 if the node should be collected.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_CollectNodeTfo_rec( Fpga_Node_t * pNode, Fpga_Node_t * pPivot, Fpga_NodeVec_t * vVisited, Fpga_NodeVec_t * vTfo ) +{ + int Ret1, Ret2; + assert( !Fpga_IsComplement(pNode) ); + // skip visited nodes + if ( pNode->fMark0 ) + return pNode->fMark1; + pNode->fMark0 = 1; + Fpga_NodeVecPush( vVisited, pNode ); + + // return the pivot node + if ( pNode == pPivot ) + { + pNode->fMark1 = 1; + return 1; + } + if ( pNode->Level < pPivot->Level ) + { + pNode->fMark1 = 0; + return 0; + } + // visit the transitive fanin + assert( Fpga_NodeIsAnd(pNode) ); + Ret1 = Fpga_CollectNodeTfo_rec( Fpga_Regular(pNode->p1), pPivot, vVisited, vTfo ); + Ret2 = Fpga_CollectNodeTfo_rec( Fpga_Regular(pNode->p2), pPivot, vVisited, vTfo ); + if ( Ret1 || Ret2 ) + { + pNode->fMark1 = 1; + Fpga_NodeVecPush( vTfo, pNode ); + } + else + pNode->fMark1 = 0; + return pNode->fMark1; +} + +/**Function************************************************************* + + Synopsis [Levelizes the nodes accessible from the POs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_NodeVec_t * Fpga_MappingLevelize( Fpga_Man_t * pMan, Fpga_NodeVec_t * vNodes ) +{ + Fpga_NodeVec_t * vLevels; + Fpga_Node_t ** ppNodes; + Fpga_Node_t * pNode; + int nNodes, nLevelsMax, i; + + // reassign the levels (this may be necessary for networks which choices) + ppNodes = vNodes->pArray; + nNodes = vNodes->nSize; + for ( i = 0; i < nNodes; i++ ) + { + pNode = ppNodes[i]; + if ( !Fpga_NodeIsAnd(pNode) ) + { + pNode->Level = 0; + continue; + } + pNode->Level = 1 + FPGA_MAX( Fpga_Regular(pNode->p1)->Level, Fpga_Regular(pNode->p2)->Level ); + } + + // get the max levels + nLevelsMax = 0; + for ( i = 0; i < pMan->nOutputs; i++ ) + nLevelsMax = FPGA_MAX( nLevelsMax, (int)Fpga_Regular(pMan->pOutputs[i])->Level ); + nLevelsMax++; + + // allocate storage for levels + vLevels = Fpga_NodeVecAlloc( nLevelsMax ); + for ( i = 0; i < nLevelsMax; i++ ) + Fpga_NodeVecPush( vLevels, NULL ); + + // go through the nodes and add them to the levels + for ( i = 0; i < nNodes; i++ ) + { + pNode = ppNodes[i]; + pNode->pLevel = NULL; + if ( !Fpga_NodeIsAnd(pNode) ) + continue; + // attach the node to this level + pNode->pLevel = Fpga_NodeVecReadEntry( vLevels, pNode->Level ); + Fpga_NodeVecWriteEntry( vLevels, pNode->Level, pNode ); + } + return vLevels; +} + +/**Function************************************************************* + + Synopsis [Sets up the mask.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_MappingMaxLevel( Fpga_Man_t * pMan ) +{ + int nLevelMax, i; + nLevelMax = 0; + for ( i = 0; i < pMan->nOutputs; i++ ) + nLevelMax = nLevelMax > (int)Fpga_Regular(pMan->pOutputs[i])->Level? + nLevelMax : (int)Fpga_Regular(pMan->pOutputs[i])->Level; + return nLevelMax; +} + + +/**Function************************************************************* + + Synopsis [Analyses choice nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_MappingUpdateLevel_rec( Fpga_Man_t * pMan, Fpga_Node_t * pNode, int fMaximum ) +{ + Fpga_Node_t * pTemp; + int Level1, Level2, LevelE; + assert( !Fpga_IsComplement(pNode) ); + if ( !Fpga_NodeIsAnd(pNode) ) + return pNode->Level; + // skip the visited node + if ( pNode->TravId == pMan->nTravIds ) + return pNode->Level; + pNode->TravId = pMan->nTravIds; + // compute levels of the children nodes + Level1 = Fpga_MappingUpdateLevel_rec( pMan, Fpga_Regular(pNode->p1), fMaximum ); + Level2 = Fpga_MappingUpdateLevel_rec( pMan, Fpga_Regular(pNode->p2), fMaximum ); + pNode->Level = 1 + FPGA_MAX( Level1, Level2 ); + if ( pNode->pNextE ) + { + LevelE = Fpga_MappingUpdateLevel_rec( pMan, pNode->pNextE, fMaximum ); + if ( fMaximum ) + { + if ( pNode->Level < (unsigned)LevelE ) + pNode->Level = LevelE; + } + else + { + if ( pNode->Level > (unsigned)LevelE ) + pNode->Level = LevelE; + } + // set the level of all equivalent nodes to be the same minimum + if ( pNode->pRepr == NULL ) // the primary node + for ( pTemp = pNode->pNextE; pTemp; pTemp = pTemp->pNextE ) + pTemp->Level = pNode->Level; + } + return pNode->Level; +} + +/**Function************************************************************* + + Synopsis [Resets the levels of the nodes in the choice graph.] + + Description [Makes the level of the choice nodes to be equal to the + maximum of the level of the nodes in the equivalence class. This way + sorting by level leads to the reverse topological order, which is + needed for the required time computation.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_MappingSetChoiceLevels( Fpga_Man_t * pMan ) +{ + int i; + pMan->nTravIds++; + for ( i = 0; i < pMan->nOutputs; i++ ) + Fpga_MappingUpdateLevel_rec( pMan, Fpga_Regular(pMan->pOutputs[i]), 1 ); +} + +/**Function************************************************************* + + Synopsis [Reports statistics on choice nodes.] + + Description [The number of choice nodes is the number of primary nodes, + which has pNextE set to a pointer. The number of choices is the number + of entries in the equivalent-node lists of the primary nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_ManReportChoices( Fpga_Man_t * pMan ) +{ + Fpga_Node_t * pNode, * pTemp; + int nChoiceNodes, nChoices; + int i, LevelMax1, LevelMax2; + + // report the number of levels + LevelMax1 = Fpga_MappingMaxLevel( pMan ); + pMan->nTravIds++; + for ( i = 0; i < pMan->nOutputs; i++ ) + Fpga_MappingUpdateLevel_rec( pMan, Fpga_Regular(pMan->pOutputs[i]), 0 ); + LevelMax2 = Fpga_MappingMaxLevel( pMan ); + + // report statistics about choices + nChoiceNodes = nChoices = 0; + for ( i = 0; i < pMan->vAnds->nSize; i++ ) + { + pNode = pMan->vAnds->pArray[i]; + if ( pNode->pRepr == NULL && pNode->pNextE != NULL ) + { // this is a choice node = the primary node that has equivalent nodes + nChoiceNodes++; + for ( pTemp = pNode; pTemp; pTemp = pTemp->pNextE ) + nChoices++; + } + } + if ( pMan->fVerbose ) + { + printf( "Maximum level: Original = %d. Reduced due to choices = %d.\n", LevelMax1, LevelMax2 ); + printf( "Choice stats: Choice nodes = %d. Total choices = %d.\n", nChoiceNodes, nChoices ); + } +/* + { + FILE * pTable; + pTable = fopen( "stats_choice.txt", "a+" ); + fprintf( pTable, "%s ", pMan->pFileName ); + fprintf( pTable, "%4d ", LevelMax1 ); + fprintf( pTable, "%4d ", pMan->vAnds->nSize - pMan->nInputs ); + fprintf( pTable, "%4d ", LevelMax2 ); + fprintf( pTable, "%7d ", nChoiceNodes ); + fprintf( pTable, "%7d ", nChoices + nChoiceNodes ); + fprintf( pTable, "\n" ); + fclose( pTable ); + } +*/ +} + +/**Function************************************************************* + + Synopsis [Returns the array of CO nodes sorted by level.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_NodeVec_t * Fpga_MappingOrderCosByLevel( Fpga_Man_t * pMan ) +{ + Fpga_Node_t * pNode; + Fpga_NodeVec_t * vNodes; + int i, nLevels; + // get the largest level of a CO + nLevels = Fpga_MappingMaxLevel( pMan ); + // allocate the array of nodes + vNodes = Fpga_NodeVecAlloc( nLevels + 1 ); + for ( i = 0; i <= nLevels; i++ ) + Fpga_NodeVecPush( vNodes, NULL ); + // clean the marks + for ( i = 0; i < pMan->nOutputs; i++ ) + Fpga_Regular(pMan->pOutputs[i])->fMark0 = 0; + // put the nodes into the structure + for ( i = 0; i < pMan->nOutputs; i++ ) + { + pNode = Fpga_Regular(pMan->pOutputs[i]); + if ( pNode->fMark0 ) + continue; + pNode->fMark0 = 1; + pNode->pData0 = (char *)Fpga_NodeVecReadEntry( vNodes, pNode->Level ); + Fpga_NodeVecWriteEntry( vNodes, pNode->Level, pNode ); + } + for ( i = 0; i < pMan->nOutputs; i++ ) + Fpga_Regular(pMan->pOutputs[i])->fMark0 = 0; + return vNodes; + +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/fpga/fpgaVec.c b/abc_with_bb_support/src/map/fpga/fpgaVec.c new file mode 100644 index 000000000..4be6d9996 --- /dev/null +++ b/abc_with_bb_support/src/map/fpga/fpgaVec.c @@ -0,0 +1,408 @@ +/**CFile**************************************************************** + + FileName [fpgaVec.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Technology mapping for variable-size-LUT FPGAs.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - August 18, 2004.] + + Revision [$Id: fpgaVec.c,v 1.3 2005/01/23 06:59:42 alanmi Exp $] + +***********************************************************************/ + +#include "fpgaInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Fpga_NodeVecCompareLevels( Fpga_Node_t ** pp1, Fpga_Node_t ** pp2 ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_NodeVec_t * Fpga_NodeVecAlloc( int nCap ) +{ + Fpga_NodeVec_t * p; + p = ALLOC( Fpga_NodeVec_t, 1 ); + if ( nCap > 0 && nCap < 16 ) + nCap = 16; + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ALLOC( Fpga_Node_t *, p->nCap ) : NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_NodeVecFree( Fpga_NodeVec_t * p ) +{ + FREE( p->pArray ); + FREE( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Node_t ** Fpga_NodeVecReadArray( Fpga_NodeVec_t * p ) +{ + return p->pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_NodeVecReadSize( Fpga_NodeVec_t * p ) +{ + return p->nSize; +} + +/**Function************************************************************* + + Synopsis [Resizes the vector to the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_NodeVecGrow( Fpga_NodeVec_t * p, int nCapMin ) +{ + if ( p->nCap >= nCapMin ) + return; + p->pArray = REALLOC( Fpga_Node_t *, p->pArray, nCapMin ); + p->nCap = nCapMin; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_NodeVecShrink( Fpga_NodeVec_t * p, int nSizeNew ) +{ + assert( p->nSize >= nSizeNew ); + p->nSize = nSizeNew; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_NodeVecClear( Fpga_NodeVec_t * p ) +{ + p->nSize = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_NodeVecPush( Fpga_NodeVec_t * p, Fpga_Node_t * Entry ) +{ + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Fpga_NodeVecGrow( p, 16 ); + else + Fpga_NodeVecGrow( p, 2 * p->nCap ); + } + p->pArray[p->nSize++] = Entry; +} + +/**Function************************************************************* + + Synopsis [Add the element while ensuring uniqueness.] + + Description [Returns 1 if the element was found, and 0 if it was new. ] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_NodeVecPushUnique( Fpga_NodeVec_t * p, Fpga_Node_t * Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return 1; + Fpga_NodeVecPush( p, Entry ); + return 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Node_t * Fpga_NodeVecPop( Fpga_NodeVec_t * p ) +{ + return p->pArray[--p->nSize]; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_NodeVecWriteEntry( Fpga_NodeVec_t * p, int i, Fpga_Node_t * Entry ) +{ + assert( i >= 0 && i < p->nSize ); + p->pArray[i] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fpga_Node_t * Fpga_NodeVecReadEntry( Fpga_NodeVec_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray[i]; +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_NodeVecCompareLevels( Fpga_Node_t ** pp1, Fpga_Node_t ** pp2 ) +{ + int Level1 = Fpga_Regular(*pp1)->Level; + int Level2 = Fpga_Regular(*pp2)->Level; + if ( Level1 < Level2 ) + return -1; + if ( Level1 > Level2 ) + return 1; + if ( Fpga_Regular(*pp1)->Num < Fpga_Regular(*pp2)->Num ) + return -1; + if ( Fpga_Regular(*pp1)->Num > Fpga_Regular(*pp2)->Num ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Sorting the entries by their integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_NodeVecSortByLevel( Fpga_NodeVec_t * p ) +{ + qsort( (void *)p->pArray, p->nSize, sizeof(Fpga_Node_t *), + (int (*)(const void *, const void *)) Fpga_NodeVecCompareLevels ); +} + +/**Function************************************************************* + + Synopsis [Compares the arrival times.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fpga_NodeVecCompareArrivals( Fpga_Node_t ** ppS1, Fpga_Node_t ** ppS2 ) +{ + if ( (*ppS1)->pCutBest->tArrival < (*ppS2)->pCutBest->tArrival ) + return -1; + if ( (*ppS1)->pCutBest->tArrival > (*ppS2)->pCutBest->tArrival ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Orders the nodes in the increasing order of the arrival times.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_SortNodesByArrivalTimes( Fpga_NodeVec_t * p ) +{ + qsort( (void *)p->pArray, p->nSize, sizeof(Fpga_Node_t *), + (int (*)(const void *, const void *)) Fpga_NodeVecCompareArrivals ); +// assert( Fpga_CompareNodesByLevel( p->pArray, p->pArray + p->nSize - 1 ) <= 0 ); +} + + +/**Function************************************************************* + + Synopsis [Computes the union of nodes in two arrays.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_NodeVecUnion( Fpga_NodeVec_t * p, Fpga_NodeVec_t * p1, Fpga_NodeVec_t * p2 ) +{ + int i; + Fpga_NodeVecClear( p ); + for ( i = 0; i < p1->nSize; i++ ) + Fpga_NodeVecPush( p, p1->pArray[i] ); + for ( i = 0; i < p2->nSize; i++ ) + Fpga_NodeVecPush( p, p2->pArray[i] ); +} + +/**Function************************************************************* + + Synopsis [Inserts a new node in the order by arrival times.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_NodeVecPushOrder( Fpga_NodeVec_t * vNodes, Fpga_Node_t * pNode, int fIncreasing ) +{ + Fpga_Node_t * pNode1, * pNode2; + int i; + Fpga_NodeVecPush( vNodes, pNode ); + // find the place of the node + for ( i = vNodes->nSize-1; i > 0; i-- ) + { + pNode1 = vNodes->pArray[i ]; + pNode2 = vNodes->pArray[i-1]; + if ( fIncreasing && pNode1->pCutBest->tArrival >= pNode2->pCutBest->tArrival || + !fIncreasing && pNode1->pCutBest->tArrival <= pNode2->pCutBest->tArrival ) + break; + vNodes->pArray[i ] = pNode2; + vNodes->pArray[i-1] = pNode1; + } +} + +/**Function************************************************************* + + Synopsis [Inserts a new node in the order by arrival times.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fpga_NodeVecReverse( Fpga_NodeVec_t * vNodes ) +{ + Fpga_Node_t * pNode1, * pNode2; + int i; + for ( i = 0; i < vNodes->nSize/2; i++ ) + { + pNode1 = vNodes->pArray[i]; + pNode2 = vNodes->pArray[vNodes->nSize-1-i]; + vNodes->pArray[i] = pNode2; + vNodes->pArray[vNodes->nSize-1-i] = pNode1; + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/map/fpga/module.make b/abc_with_bb_support/src/map/fpga/module.make new file mode 100644 index 000000000..d3d3de8a4 --- /dev/null +++ b/abc_with_bb_support/src/map/fpga/module.make @@ -0,0 +1,13 @@ +SRC += src/map/fpga/fpga.c \ + src/map/fpga/fpgaCore.c \ + src/map/fpga/fpgaCreate.c \ + src/map/fpga/fpgaCut.c \ + src/map/fpga/fpgaCutUtils.c \ + src/map/fpga/fpgaFanout.c \ + src/map/fpga/fpgaLib.c \ + src/map/fpga/fpgaMatch.c \ + src/map/fpga/fpgaSwitch.c \ + src/map/fpga/fpgaTime.c \ + src/map/fpga/fpgaTruth.c \ + src/map/fpga/fpgaUtils.c \ + src/map/fpga/fpgaVec.c diff --git a/abc_with_bb_support/src/map/if/if.h b/abc_with_bb_support/src/map/if/if.h new file mode 100644 index 000000000..86a637414 --- /dev/null +++ b/abc_with_bb_support/src/map/if/if.h @@ -0,0 +1,386 @@ +/**CFile**************************************************************** + + FileName [if.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [FPGA mapping based on priority cuts.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - November 21, 2006.] + + Revision [$Id: if.h,v 1.00 2006/11/21 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __IF_H__ +#define __IF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include "vec.h" +#include "mem.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +// the maximum size of LUTs used for mapping (should be the same as FPGA_MAX_LUTSIZE defined in "fpga.h"!!!) +#define IF_MAX_LUTSIZE 32 +// the largest possible number of LUT inputs when funtionality of the LUTs are computed +#define IF_MAX_FUNC_LUTSIZE 15 +// a very large number +#define IF_INFINITY 100000000 +// the largest possible user cut cost +#define IF_COST_MAX ((1<<14)-1) + +// object types +typedef enum { + IF_NONE, // 0: non-existent object + IF_CONST1, // 1: constant 1 + IF_CI, // 2: combinational input + IF_CO, // 3: combinational output + IF_AND, // 4: AND node + IF_VOID // 5: unused object +} If_Type_t; + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct If_Man_t_ If_Man_t; +typedef struct If_Par_t_ If_Par_t; +typedef struct If_Lib_t_ If_Lib_t; +typedef struct If_Obj_t_ If_Obj_t; +typedef struct If_Cut_t_ If_Cut_t; +typedef struct If_Set_t_ If_Set_t; + +// parameters +struct If_Par_t_ +{ + // user-controlable paramters + int nLutSize; // the LUT size + int nCutsMax; // the max number of cuts + int nFlowIters; // the number of iterations of area recovery + int nAreaIters; // the number of iterations of area recovery + float DelayTarget; // delay target + int fPreprocess; // preprossing + int fArea; // area-oriented mapping + int fFancy; // a fancy feature + int fExpRed; // expand/reduce of the best cuts + int fLatchPaths; // reset timing on latch paths + int fSeqMap; // sequential mapping + int fVerbose; // the verbosity flag + // internal parameters + int fAreaOnly; // area only mode + int fTruth; // truth table computation enabled + int fUsePerm; // use permutation (delay info) + int fUseBdds; // use local BDDs as a cost function + int fUseSops; // use local SOPs as a cost function + int fUseCnfs; // use local CNFs as a cost function + int fUseMv; // use local MV-SOPs as a cost function + int nLatches; // the number of latches in seq mapping + int fLiftLeaves; // shift the leaves for seq mapping + If_Lib_t * pLutLib; // the LUT library + float * pTimesArr; // arrival times + float * pTimesReq; // required times + int (* pFuncCost) (If_Cut_t *); // procedure to compute the user's cost of a cut + int (* pFuncUser) (If_Man_t *, If_Obj_t *, If_Cut_t *); // procedure called for each cut when cut computation is finished + void * pReoMan; // reordering manager +}; + +// the LUT library +struct If_Lib_t_ +{ + char * pName; // the name of the LUT library + int LutMax; // the maximum LUT size + int fVarPinDelays; // set to 1 if variable pin delays are specified + float pLutAreas[IF_MAX_LUTSIZE+1]; // the areas of LUTs + float pLutDelays[IF_MAX_LUTSIZE+1][IF_MAX_LUTSIZE+1];// the delays of LUTs +}; + +// manager +struct If_Man_t_ +{ + // mapping parameters + If_Par_t * pPars; + // mapping nodes + If_Obj_t * pConst1; // the constant 1 node + Vec_Ptr_t * vCis; // the primary inputs + Vec_Ptr_t * vCos; // the primary outputs + Vec_Ptr_t * vObjs; // all objects + Vec_Ptr_t * vMapped; // objects used in the mapping + Vec_Ptr_t * vTemp; // temporary array + int nObjs[IF_VOID];// the number of objects by type + // various data + int nLevelMax; // the max number of AIG levels + float fEpsilon; // epsilon used for comparison + float RequiredGlo; // global required times + float RequiredGlo2; // global required times + float AreaGlo; // global area + int nNets; // the sum total of fanins of all LUTs in the mapping + int nCutsUsed; // the number of cuts currently used + int nCutsMerged; // the total number of cuts merged + unsigned * puTemp[4]; // used for the truth table computation + int SortMode; // one of the three sorting modes + int fNextRound; // set to 1 after the first round + int nChoices; // the number of choice nodes + // sequential mapping + Vec_Ptr_t * vLatchOrder; // topological ordering of latches + Vec_Int_t * vLags; // sequentail lags of all nodes + int nAttempts; // the number of attempts in binary search + int nMaxIters; // the maximum number of iterations + int Period; // the current value of the clock period (for seq mapping) + // memory management + int nTruthWords; // the size of the truth table if allocated + int nPermWords; // the size of the permutation array (in words) + int nObjBytes; // the size of the object + int nCutBytes; // the size of the cut + int nSetBytes; // the size of the cut set + Mem_Fixed_t * pMemObj; // memory manager for objects (entrysize = nEntrySize) + Mem_Fixed_t * pMemSet; // memory manager for sets of cuts (entrysize = nCutSize*(nCutsMax+1)) + If_Set_t * pMemCi; // memory for CI cutsets + If_Set_t * pMemAnd; // memory for AND cutsets + If_Set_t * pFreeList; // the list of free cutsets +}; + +// priority cut +struct If_Cut_t_ +{ + float Delay; // delay of the cut + float Area; // area (or area-flow) of the cut + float AveRefs; // the average number of leaf references + unsigned uSign; // cut signature + unsigned Cost : 14; // the user's cost of the cut + unsigned fCompl : 1; // the complemented attribute + unsigned fUser : 1; // using the user's area and delay + unsigned nLimit : 8; // the maximum number of leaves + unsigned nLeaves : 8; // the number of leaves + int * pLeaves; // array of fanins + char * pPerm; // permutation + unsigned * pTruth; // the truth table +}; + +// set of priority cut +struct If_Set_t_ +{ + short nCutsMax; // the max number of cuts + short nCuts; // the current number of cuts + If_Set_t * pNext; // next cutset in the free list + If_Cut_t ** ppCuts; // the array of pointers to the cuts +}; + +// node extension +struct If_Obj_t_ +{ + unsigned Type : 4; // object + unsigned fCompl0 : 1; // complemented attribute + unsigned fCompl1 : 1; // complemented attribute + unsigned fPhase : 1; // phase of the node + unsigned fRepr : 1; // representative of the equivalence class + unsigned fMark : 1; // multipurpose mark + unsigned fVisit : 1; // multipurpose mark + unsigned Level : 22; // logic level of the node + int Id; // integer ID + int nRefs; // the number of references + int nVisits; // the number of visits to this node + int nVisitsCopy; // the number of visits to this node + If_Obj_t * pFanin0; // the first fanin + If_Obj_t * pFanin1; // the second fanin + If_Obj_t * pEquiv; // the choice node + float EstRefs; // estimated reference counter + float Required; // required time of the onde + float LValue; // sequential arrival time of the node + void * pCopy; // used for object duplication + If_Set_t * pCutSet; // the pointer to the cutset + If_Cut_t CutBest; // the best cut selected +}; + +static inline If_Obj_t * If_Regular( If_Obj_t * p ) { return (If_Obj_t *)((unsigned long)(p) & ~01); } +static inline If_Obj_t * If_Not( If_Obj_t * p ) { return (If_Obj_t *)((unsigned long)(p) ^ 01); } +static inline If_Obj_t * If_NotCond( If_Obj_t * p, int c ) { return (If_Obj_t *)((unsigned long)(p) ^ (c)); } +static inline int If_IsComplement( If_Obj_t * p ) { return (int )(((unsigned long)p) & 01); } + +static inline int If_ManCiNum( If_Man_t * p ) { return p->nObjs[IF_CI]; } +static inline int If_ManCoNum( If_Man_t * p ) { return p->nObjs[IF_CO]; } +static inline int If_ManAndNum( If_Man_t * p ) { return p->nObjs[IF_AND]; } +static inline int If_ManObjNum( If_Man_t * p ) { return Vec_PtrSize(p->vObjs); } + +static inline If_Obj_t * If_ManConst1( If_Man_t * p ) { return p->pConst1; } +static inline If_Obj_t * If_ManCi( If_Man_t * p, int i ) { return (If_Obj_t *)Vec_PtrEntry( p->vCis, i ); } +static inline If_Obj_t * If_ManCo( If_Man_t * p, int i ) { return (If_Obj_t *)Vec_PtrEntry( p->vCos, i ); } +static inline If_Obj_t * If_ManLi( If_Man_t * p, int i ) { return (If_Obj_t *)Vec_PtrEntry( p->vCos, If_ManCoNum(p) - p->pPars->nLatches + i ); } +static inline If_Obj_t * If_ManLo( If_Man_t * p, int i ) { return (If_Obj_t *)Vec_PtrEntry( p->vCis, If_ManCiNum(p) - p->pPars->nLatches + i ); } +static inline If_Obj_t * If_ManObj( If_Man_t * p, int i ) { return (If_Obj_t *)Vec_PtrEntry( p->vObjs, i ); } + +static inline int If_ObjIsConst1( If_Obj_t * pObj ) { return pObj->Type == IF_CONST1; } +static inline int If_ObjIsCi( If_Obj_t * pObj ) { return pObj->Type == IF_CI; } +static inline int If_ObjIsCo( If_Obj_t * pObj ) { return pObj->Type == IF_CO; } +static inline int If_ObjIsPi( If_Obj_t * pObj ) { return If_ObjIsCi(pObj) && pObj->pFanin0 == NULL; } +static inline int If_ObjIsLatch( If_Obj_t * pObj ) { return If_ObjIsCi(pObj) && pObj->pFanin0 != NULL; } +static inline int If_ObjIsAnd( If_Obj_t * pObj ) { return pObj->Type == IF_AND; } + +static inline If_Obj_t * If_ObjFanin0( If_Obj_t * pObj ) { return pObj->pFanin0; } +static inline If_Obj_t * If_ObjFanin1( If_Obj_t * pObj ) { return pObj->pFanin1; } +static inline int If_ObjFaninC0( If_Obj_t * pObj ) { return pObj->fCompl0; } +static inline int If_ObjFaninC1( If_Obj_t * pObj ) { return pObj->fCompl1; } +static inline void * If_ObjCopy( If_Obj_t * pObj ) { return pObj->pCopy; } +static inline void If_ObjSetCopy( If_Obj_t * pObj, void * pCopy ) { pObj->pCopy = pCopy; } +static inline void If_ObjSetChoice( If_Obj_t * pObj, If_Obj_t * pEqu ) { pObj->pEquiv = pEqu; } + +static inline If_Cut_t * If_ObjCutBest( If_Obj_t * pObj ) { return &pObj->CutBest; } +static inline unsigned If_ObjCutSign( unsigned ObjId ) { return (1 << (ObjId % 31)); } + +static inline float If_ObjArrTime( If_Obj_t * pObj ) { return If_ObjCutBest(pObj)->Delay; } +static inline void If_ObjSetArrTime( If_Obj_t * pObj, float ArrTime ) { If_ObjCutBest(pObj)->Delay = ArrTime; } + +static inline float If_ObjLValue( If_Obj_t * pObj ) { return pObj->LValue; } +static inline void If_ObjSetLValue( If_Obj_t * pObj, float LValue ) { pObj->LValue = LValue; } + +static inline void * If_CutData( If_Cut_t * pCut ) { return *(void **)pCut; } +static inline void If_CutSetData( If_Cut_t * pCut, void * pData ) { *(void **)pCut = pData; } + +static inline int If_CutLeaveNum( If_Cut_t * pCut ) { return pCut->nLeaves; } +static inline unsigned * If_CutTruth( If_Cut_t * pCut ) { return pCut->pTruth; } +static inline int If_CutTruthWords( int nVarsMax ) { return nVarsMax <= 5 ? 1 : (1 << (nVarsMax - 5)); } +static inline int If_CutPermWords( int nVarsMax ) { return nVarsMax / sizeof(int) + ((nVarsMax % sizeof(int)) > 0); } + +static inline float If_CutLutArea( If_Man_t * p, If_Cut_t * pCut ) { return pCut->fUser? (float)pCut->Cost : (p->pPars->pLutLib? p->pPars->pLutLib->pLutAreas[pCut->nLeaves] : (float)1.0); } + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define IF_MIN(a,b) (((a) < (b))? (a) : (b)) +#define IF_MAX(a,b) (((a) > (b))? (a) : (b)) + +// the small and large numbers (min/max float are 1.17e-38/3.40e+38) +#define IF_FLOAT_LARGE ((float)1.0e+20) +#define IF_FLOAT_SMALL ((float)1.0e-20) +#define IF_INT_LARGE (10000000) + +// iterator over the primary inputs +#define If_ManForEachCi( p, pObj, i ) \ + Vec_PtrForEachEntry( p->vCis, pObj, i ) +// iterator over the primary outputs +#define If_ManForEachCo( p, pObj, i ) \ + Vec_PtrForEachEntry( p->vCos, pObj, i ) +// iterator over the primary inputs +#define If_ManForEachPi( p, pObj, i ) \ + Vec_PtrForEachEntryStop( p->vCis, pObj, i, If_ManCiNum(p) - p->pPars->nLatches ) +// iterator over the primary outputs +#define If_ManForEachPo( p, pObj, i ) \ + Vec_PtrForEachEntryStop( p->vCos, pObj, i, If_ManCoNum(p) - p->pPars->nLatches ) +// iterator over the latches +#define If_ManForEachLatchInput( p, pObj, i ) \ + Vec_PtrForEachEntryStart( p->vCos, pObj, i, If_ManCoNum(p) - p->pPars->nLatches ) +#define If_ManForEachLatchOutput( p, pObj, i ) \ + Vec_PtrForEachEntryStart( p->vCis, pObj, i, If_ManCiNum(p) - p->pPars->nLatches ) +// iterator over all objects, including those currently not used +#define If_ManForEachObj( p, pObj, i ) \ + Vec_PtrForEachEntry( p->vObjs, pObj, i ) +// iterator over logic nodes +#define If_ManForEachNode( p, pObj, i ) \ + If_ManForEachObj( p, pObj, i ) if ( pObj->Type != IF_AND ) {} else +// iterator over cuts of the node +#define If_ObjForEachCut( pObj, pCut, i ) \ + for ( i = 0; (i < (pObj)->pCutSet->nCuts) && ((pCut) = (pObj)->pCutSet->ppCuts[i]); i++ ) +// iterator over the leaves of the cut +#define If_CutForEachLeaf( p, pCut, pLeaf, i ) \ + for ( i = 0; (i < (int)(pCut)->nLeaves) && ((pLeaf) = If_ManObj(p, (pCut)->pLeaves[i])); i++ ) +#define If_CutForEachLeafReverse( p, pCut, pLeaf, i ) \ + for ( i = (int)(pCut)->nLeaves - 1; (i >= 0) && ((pLeaf) = If_ManObj(p, (pCut)->pLeaves[i])); i-- ) +//#define If_CutForEachLeaf( p, pCut, pLeaf, i ) \ +// for ( i = 0; (i < (int)(pCut)->nLeaves) && ((pLeaf) = If_ManObj(p, p->pPars->fLiftLeaves? (pCut)->pLeaves[i] >> 8 : (pCut)->pLeaves[i])); i++ ) +// iterator over the leaves of the sequential cut +#define If_CutForEachLeafSeq( p, pCut, pLeaf, Shift, i ) \ + for ( i = 0; (i < (int)(pCut)->nLeaves) && ((pLeaf) = If_ManObj(p, (pCut)->pLeaves[i] >> 8)) && (((Shift) = ((pCut)->pLeaves[i] & 255)) >= 0); i++ ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== ifCore.c ===========================================================*/ +extern int If_ManPerformMapping( If_Man_t * p ); +extern int If_ManPerformMappingComb( If_Man_t * p ); +/*=== ifCut.c ============================================================*/ +extern float If_CutAreaDerefed( If_Man_t * p, If_Cut_t * pCut, int nLevels ); +extern float If_CutAreaRefed( If_Man_t * p, If_Cut_t * pCut, int nLevels ); +extern float If_CutDeref( If_Man_t * p, If_Cut_t * pCut, int nLevels ); +extern float If_CutRef( If_Man_t * p, If_Cut_t * pCut, int nLevels ); +extern void If_CutPrint( If_Man_t * p, If_Cut_t * pCut ); +extern void If_CutPrintTiming( If_Man_t * p, If_Cut_t * pCut ); +extern float If_CutFlow( If_Man_t * p, If_Cut_t * pCut ); +extern float If_CutAverageRefs( If_Man_t * p, If_Cut_t * pCut ); +extern int If_CutFilter( If_Set_t * pCutSet, If_Cut_t * pCut ); +extern void If_CutSort( If_Man_t * p, If_Set_t * pCutSet, If_Cut_t * pCut ); +extern int If_CutMerge( If_Cut_t * pCut0, If_Cut_t * pCut1, If_Cut_t * pCut ); +extern void If_CutLift( If_Cut_t * pCut ); +extern void If_CutCopy( If_Man_t * p, If_Cut_t * pCutDest, If_Cut_t * pCutSrc ); +extern void If_ManSortCuts( If_Man_t * p, int Mode ); +/*=== ifMan.c =============================================================*/ +extern If_Man_t * If_ManStart( If_Par_t * pPars ); +extern void If_ManRestart( If_Man_t * p ); +extern void If_ManStop( If_Man_t * p ); +extern If_Obj_t * If_ManCreateCi( If_Man_t * p ); +extern If_Obj_t * If_ManCreateCo( If_Man_t * p, If_Obj_t * pDriver ); +extern If_Obj_t * If_ManCreateAnd( If_Man_t * p, If_Obj_t * pFan0, If_Obj_t * pFan1 ); +extern If_Obj_t * If_ManCreateXor( If_Man_t * p, If_Obj_t * pFan0, If_Obj_t * pFan1 ); +extern If_Obj_t * If_ManCreateMux( If_Man_t * p, If_Obj_t * pFan0, If_Obj_t * pFan1, If_Obj_t * pCtrl ); +extern void If_ManCreateChoice( If_Man_t * p, If_Obj_t * pRepr ); +extern void If_ManSetupCutTriv( If_Man_t * p, If_Cut_t * pCut, int ObjId ); +extern void If_ManSetupCiCutSets( If_Man_t * p ); +extern If_Set_t * If_ManSetupNodeCutSet( If_Man_t * p, If_Obj_t * pObj ); +extern void If_ManDerefNodeCutSet( If_Man_t * p, If_Obj_t * pObj ); +extern void If_ManDerefChoiceCutSet( If_Man_t * p, If_Obj_t * pObj ); +extern void If_ManSetupSetAll( If_Man_t * p, int nCrossCut ); +/*=== ifMap.c =============================================================*/ +extern void If_ObjPerformMappingAnd( If_Man_t * p, If_Obj_t * pObj, int Mode, int fPreprocess ); +extern void If_ObjPerformMappingChoice( If_Man_t * p, If_Obj_t * pObj, int Mode, int fPreprocess ); +extern int If_ManPerformMappingRound( If_Man_t * p, int nCutsUsed, int Mode, int fPreprocess, char * pLabel ); +/*=== ifReduce.c ==========================================================*/ +extern void If_ManImproveMapping( If_Man_t * p ); +/*=== ifSeq.c =============================================================*/ +extern int If_ManPerformMappingSeq( If_Man_t * p ); +/*=== ifTime.c ============================================================*/ +extern float If_CutDelay( If_Man_t * p, If_Cut_t * pCut ); +extern void If_CutPropagateRequired( If_Man_t * p, If_Cut_t * pCut, float Required ); +/*=== ifTruth.c ===========================================================*/ +extern void If_CutComputeTruth( If_Man_t * p, If_Cut_t * pCut, If_Cut_t * pCut0, If_Cut_t * pCut1, int fCompl0, int fCompl1 ); +/*=== ifUtil.c ============================================================*/ +extern void If_ManCleanNodeCopy( If_Man_t * p ); +extern void If_ManCleanCutData( If_Man_t * p ); +extern void If_ManCleanMarkV( If_Man_t * p ); +extern float If_ManDelayMax( If_Man_t * p, int fSeq ); +extern void If_ManComputeRequired( If_Man_t * p ); +extern float If_ManScanMapping( If_Man_t * p ); +extern float If_ManScanMappingSeq( If_Man_t * p ); +extern void If_ManResetOriginalRefs( If_Man_t * p ); +extern int If_ManCrossCut( If_Man_t * p ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/map/if/ifCore.c b/abc_with_bb_support/src/map/if/ifCore.c new file mode 100644 index 000000000..557c5e207 --- /dev/null +++ b/abc_with_bb_support/src/map/if/ifCore.c @@ -0,0 +1,146 @@ +/**CFile**************************************************************** + + FileName [ifCore.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [FPGA mapping based on priority cuts.] + + Synopsis [The central part of the mapper.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - November 21, 2006.] + + Revision [$Id: ifCore.c,v 1.00 2006/11/21 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "if.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +extern int s_MappingTime; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_ManPerformMapping( If_Man_t * p ) +{ + p->pPars->fAreaOnly = p->pPars->fArea; // temporary + + // create the CI cutsets + If_ManSetupCiCutSets( p ); + // allocate memory for other cutsets + If_ManSetupSetAll( p, If_ManCrossCut(p) ); + + // try sequential mapping + if ( p->pPars->fSeqMap ) + { + int RetValue; +// printf( "Currently sequential mapping is not performed.\n" ); + RetValue = If_ManPerformMappingSeq( p ); + return RetValue; +// return 1; + } + + return If_ManPerformMappingComb( p ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_ManPerformMappingComb( If_Man_t * p ) +{ + If_Obj_t * pObj; + int clkTotal = clock(); + int i; + + // set arrival times and fanout estimates + If_ManForEachCi( p, pObj, i ) + { + If_ObjSetArrTime( pObj, p->pPars->pTimesArr[i] ); + pObj->EstRefs = (float)1.0; + } + + // delay oriented mapping + if ( p->pPars->fPreprocess && !p->pPars->fArea ) + { + // map for delay + If_ManPerformMappingRound( p, p->pPars->nCutsMax, 0, 1, "Delay" ); + // map for delay second option + p->pPars->fFancy = 1; + If_ManResetOriginalRefs( p ); + If_ManPerformMappingRound( p, p->pPars->nCutsMax, 0, 1, "Delay-2" ); + p->pPars->fFancy = 0; + // map for area + p->pPars->fArea = 1; + If_ManResetOriginalRefs( p ); + If_ManPerformMappingRound( p, p->pPars->nCutsMax, 0, 1, "Area" ); + p->pPars->fArea = 0; + } + else + If_ManPerformMappingRound( p, p->pPars->nCutsMax, 0, 0, "Delay" ); + + // try to improve area by expanding and reducing the cuts + if ( p->pPars->fExpRed && !p->pPars->fTruth ) + If_ManImproveMapping( p ); + + // area flow oriented mapping + for ( i = 0; i < p->pPars->nFlowIters; i++ ) + { + If_ManPerformMappingRound( p, p->pPars->nCutsMax, 1, 0, "Flow" ); + if ( p->pPars->fExpRed && !p->pPars->fTruth ) + If_ManImproveMapping( p ); + } + + // area oriented mapping + for ( i = 0; i < p->pPars->nAreaIters; i++ ) + { + If_ManPerformMappingRound( p, p->pPars->nCutsMax, 2, 0, "Area" ); + if ( p->pPars->fExpRed && !p->pPars->fTruth ) + If_ManImproveMapping( p ); + } + + if ( p->pPars->fVerbose ) + { +// printf( "Total memory = %7.2f Mb. Peak cut memory = %7.2f Mb. ", +// 1.0 * (p->nObjBytes + 2*sizeof(void *)) * If_ManObjNum(p) / (1<<20), +// 1.0 * p->nSetBytes * Mem_FixedReadMaxEntriesUsed(p->pMemSet) / (1<<20) ); + PRT( "Total time", clock() - clkTotal ); + } +// printf( "Cross cut memory = %d.\n", Mem_FixedReadMaxEntriesUsed(p->pMemSet) ); + s_MappingTime = clock() - clkTotal; + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/if/ifCut.c b/abc_with_bb_support/src/map/if/ifCut.c new file mode 100644 index 000000000..8405fb13e --- /dev/null +++ b/abc_with_bb_support/src/map/if/ifCut.c @@ -0,0 +1,820 @@ +/**CFile**************************************************************** + + FileName [ifCut.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [FPGA mapping based on priority cuts.] + + Synopsis [Cut computation.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - November 21, 2006.] + + Revision [$Id: ifCut.c,v 1.00 2006/11/21 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "if.h" + +static int wiremap = 1; // set to 1 for WIREMAP janders + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + + +/**Function************************************************************* + + Synopsis [Returns 1 if pDom is contained in pCut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int If_CutCheckDominance( If_Cut_t * pDom, If_Cut_t * pCut ) +{ + int i, k; + for ( i = 0; i < (int)pDom->nLeaves; i++ ) + { + for ( k = 0; k < (int)pCut->nLeaves; k++ ) + if ( pDom->pLeaves[i] == pCut->pLeaves[k] ) + break; + if ( k == (int)pCut->nLeaves ) // node i in pDom is not contained in pCut + return 0; + } + // every node in pDom is contained in pCut + return 1; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if pDom is equal to pCut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int If_CutCheckEquality( If_Cut_t * pDom, If_Cut_t * pCut ) +{ + int i; + if ( (int)pDom->nLeaves != (int)pCut->nLeaves ) + return 0; + for ( i = 0; i < (int)pDom->nLeaves; i++ ) + if ( pDom->pLeaves[i] != pCut->pLeaves[i] ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the cut is contained.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_CutFilter( If_Set_t * pCutSet, If_Cut_t * pCut ) +{ + If_Cut_t * pTemp; + int i, k; + assert( pCutSet->ppCuts[pCutSet->nCuts] == pCut ); + for ( i = 0; i < pCutSet->nCuts; i++ ) + { + pTemp = pCutSet->ppCuts[i]; + if ( pTemp->nLeaves > pCut->nLeaves ) + { + // do not fiter the first cut + if ( i == 0 ) + continue; + // skip the non-contained cuts + if ( (pTemp->uSign & pCut->uSign) != pCut->uSign ) + continue; + // check containment seriously + if ( If_CutCheckDominance( pCut, pTemp ) ) + { +// p->ppCuts[i] = p->ppCuts[p->nCuts-1]; +// p->ppCuts[p->nCuts-1] = pTemp; +// p->nCuts--; +// i--; + // remove contained cut + for ( k = i; k < pCutSet->nCuts; k++ ) + pCutSet->ppCuts[k] = pCutSet->ppCuts[k+1]; + pCutSet->ppCuts[pCutSet->nCuts] = pTemp; + pCutSet->nCuts--; + i--; + } + } + else + { + // skip the non-contained cuts + if ( (pTemp->uSign & pCut->uSign) != pTemp->uSign ) + continue; + // check containment seriously + if ( If_CutCheckDominance( pTemp, pCut ) ) + return 1; + } + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Merges two cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int If_CutMergeOrdered( If_Cut_t * pC0, If_Cut_t * pC1, If_Cut_t * pC ) +{ + int i, k, c; + assert( pC0->nLeaves >= pC1->nLeaves ); + // the case of the largest cut sizes + if ( pC0->nLeaves == pC->nLimit && pC1->nLeaves == pC->nLimit ) + { + for ( i = 0; i < (int)pC0->nLeaves; i++ ) + if ( pC0->pLeaves[i] != pC1->pLeaves[i] ) + return 0; + for ( i = 0; i < (int)pC0->nLeaves; i++ ) + pC->pLeaves[i] = pC0->pLeaves[i]; + pC->nLeaves = pC0->nLeaves; + return 1; + } + // the case when one of the cuts is the largest + if ( pC0->nLeaves == pC->nLimit ) + { + for ( i = 0; i < (int)pC1->nLeaves; i++ ) + { + for ( k = (int)pC0->nLeaves - 1; k >= 0; k-- ) + if ( pC0->pLeaves[k] == pC1->pLeaves[i] ) + break; + if ( k == -1 ) // did not find + return 0; + } + for ( i = 0; i < (int)pC0->nLeaves; i++ ) + pC->pLeaves[i] = pC0->pLeaves[i]; + pC->nLeaves = pC0->nLeaves; + return 1; + } + + // compare two cuts with different numbers + i = k = 0; + for ( c = 0; c < (int)pC->nLimit; c++ ) + { + if ( k == (int)pC1->nLeaves ) + { + if ( i == (int)pC0->nLeaves ) + { + pC->nLeaves = c; + return 1; + } + pC->pLeaves[c] = pC0->pLeaves[i++]; + continue; + } + if ( i == (int)pC0->nLeaves ) + { + if ( k == (int)pC1->nLeaves ) + { + pC->nLeaves = c; + return 1; + } + pC->pLeaves[c] = pC1->pLeaves[k++]; + continue; + } + if ( pC0->pLeaves[i] < pC1->pLeaves[k] ) + { + pC->pLeaves[c] = pC0->pLeaves[i++]; + continue; + } + if ( pC0->pLeaves[i] > pC1->pLeaves[k] ) + { + pC->pLeaves[c] = pC1->pLeaves[k++]; + continue; + } + pC->pLeaves[c] = pC0->pLeaves[i++]; + k++; + } + if ( i < (int)pC0->nLeaves || k < (int)pC1->nLeaves ) + return 0; + pC->nLeaves = c; + return 1; +} + +/**Function************************************************************* + + Synopsis [Merges two cuts.] + + Description [Special case when the cut is known to exist.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int If_CutMergeOrdered2( If_Cut_t * pC0, If_Cut_t * pC1, If_Cut_t * pC ) +{ + int i, k, c; + assert( pC0->nLeaves >= pC1->nLeaves ); + // copy the first cut + for ( i = 0; i < (int)pC0->nLeaves; i++ ) + pC->pLeaves[i] = pC0->pLeaves[i]; + pC->nLeaves = pC0->nLeaves; + // the case when one of the cuts is the largest + if ( pC0->nLeaves == pC->nLimit ) + return 1; + // add nodes of the second cut + k = 0; + for ( i = 0; i < (int)pC1->nLeaves; i++ ) + { + // find k-th node before which i-th node should be added + for ( ; k < (int)pC->nLeaves; k++ ) + if ( pC->pLeaves[k] >= pC1->pLeaves[i] ) + break; + // check the case when this should be the last node + if ( k == (int)pC->nLeaves ) + { + pC->pLeaves[k++] = pC1->pLeaves[i]; + pC->nLeaves++; + continue; + } + // check the case when equal node is found + if ( pC1->pLeaves[i] == pC->pLeaves[k] ) + continue; + // add the node + for ( c = (int)pC->nLeaves; c > k; c-- ) + pC->pLeaves[c] = pC->pLeaves[c-1]; + pC->pLeaves[k++] = pC1->pLeaves[i]; + pC->nLeaves++; + } +/* + assert( pC->nLeaves <= pC->nLimit ); + for ( i = 1; i < (int)pC->nLeaves; i++ ) + assert( pC->pLeaves[i-1] < pC->pLeaves[i] ); +*/ + return 1; +} + +/**Function************************************************************* + + Synopsis [Prepares the object for FPGA mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_CutMerge( If_Cut_t * pCut0, If_Cut_t * pCut1, If_Cut_t * pCut ) +{ + assert( pCut->nLimit > 0 ); + // merge the nodes + if ( pCut0->nLeaves < pCut1->nLeaves ) + { + if ( !If_CutMergeOrdered( pCut1, pCut0, pCut ) ) + return 0; + } + else + { + if ( !If_CutMergeOrdered( pCut0, pCut1, pCut ) ) + return 0; + } + pCut->uSign = pCut0->uSign | pCut1->uSign; + return 1; +} + +/**Function************************************************************* + + Synopsis [Prepares the object for FPGA mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_CutCompareDelay( If_Cut_t ** ppC0, If_Cut_t ** ppC1 ) +{ + If_Cut_t * pC0 = *ppC0; + If_Cut_t * pC1 = *ppC1; + if ( pC0->Delay < pC1->Delay - 0.0001 ) + return -1; + if ( pC0->Delay > pC1->Delay + 0.0001 ) + return 1; + if ( pC0->nLeaves < pC1->nLeaves ) + return -1; + if ( pC0->nLeaves > pC1->nLeaves ) + return 1; + if ( pC0->Area < pC1->Area - 0.0001 ) + return -1; + if ( pC0->Area > pC1->Area + 0.0001 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Prepares the object for FPGA mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_CutCompareDelayOld( If_Cut_t ** ppC0, If_Cut_t ** ppC1 ) +{ + If_Cut_t * pC0 = *ppC0; + If_Cut_t * pC1 = *ppC1; + if ( pC0->Delay < pC1->Delay - 0.0001 ) + return -1; + if ( pC0->Delay > pC1->Delay + 0.0001 ) + return 1; + if ( pC0->Area < pC1->Area - 0.0001 ) + return -1; + if ( pC0->Area > pC1->Area + 0.0001 ) + return 1; + if ( pC0->nLeaves < pC1->nLeaves ) + return -1; + if ( pC0->nLeaves > pC1->nLeaves ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Prepares the object for FPGA mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_CutCompareArea( If_Cut_t ** ppC0, If_Cut_t ** ppC1 ) +{ + If_Cut_t * pC0 = *ppC0; + If_Cut_t * pC1 = *ppC1; + if ( pC0->Area < pC1->Area - 0.0001 ) + return -1; + if ( pC0->Area > pC1->Area + 0.0001 ) + return 1; + if ( pC0->AveRefs > pC1->AveRefs ) + return -1; + if ( pC0->AveRefs < pC1->AveRefs ) + return 1; + if ( pC0->nLeaves < pC1->nLeaves ) + return -1; + if ( pC0->nLeaves > pC1->nLeaves ) + return 1; + if ( pC0->Delay < pC1->Delay - 0.0001 ) + return -1; + if ( pC0->Delay > pC1->Delay + 0.0001 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Sorts the cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManSortCuts( If_Man_t * p, int Mode ) +{ +/* + // sort the cuts + if ( Mode || p->pPars->fArea ) // area + qsort( p->ppCuts, p->nCuts, sizeof(If_Cut_t *), (int (*)(const void *, const void *))If_CutCompareArea ); + else if ( p->pPars->fFancy ) + qsort( p->ppCuts, p->nCuts, sizeof(If_Cut_t *), (int (*)(const void *, const void *))If_CutCompareDelayOld ); + else + qsort( p->ppCuts, p->nCuts, sizeof(If_Cut_t *), (int (*)(const void *, const void *))If_CutCompareDelay ); +*/ +} + +/**Function************************************************************* + + Synopsis [Comparison function for two cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int If_ManSortCompare( If_Man_t * p, If_Cut_t * pC0, If_Cut_t * pC1 ) +{ + if ( p->SortMode == 1 ) // area + { + if ( pC0->Area < pC1->Area - 0.0001 ) + return -1; + if ( pC0->Area > pC1->Area + 0.0001 ) + return 1; + if ( pC0->AveRefs > pC1->AveRefs ) + return -1; + if ( pC0->AveRefs < pC1->AveRefs ) + return 1; + if ( pC0->nLeaves < pC1->nLeaves ) + return -1; + if ( pC0->nLeaves > pC1->nLeaves ) + return 1; + if ( pC0->Delay < pC1->Delay - 0.0001 ) + return -1; + if ( pC0->Delay > pC1->Delay + 0.0001 ) + return 1; + return 0; + } + if ( p->SortMode == 0 ) // delay + { + if ( pC0->Delay < pC1->Delay - 0.0001 ) + return -1; + if ( pC0->Delay > pC1->Delay + 0.0001 ) + return 1; + if ( pC0->nLeaves < pC1->nLeaves ) + return -1; + if ( pC0->nLeaves > pC1->nLeaves ) + return 1; + if ( pC0->Area < pC1->Area - 0.0001 ) + return -1; + if ( pC0->Area > pC1->Area + 0.0001 ) + return 1; + return 0; + } + assert( p->SortMode == 2 ); // delay old + if ( pC0->Delay < pC1->Delay - 0.0001 ) + return -1; + if ( pC0->Delay > pC1->Delay + 0.0001 ) + return 1; + if ( pC0->Area < pC1->Area - 0.0001 ) + return -1; + if ( pC0->Area > pC1->Area + 0.0001 ) + return 1; + if ( pC0->nLeaves < pC1->nLeaves ) + return -1; + if ( pC0->nLeaves > pC1->nLeaves ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Performs incremental sorting of cuts.] + + Description [Currently only the trivial sorting is implemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_CutSort( If_Man_t * p, If_Set_t * pCutSet, If_Cut_t * pCut ) +{ +// int Counter = 0; + int i; + + // the new cut is the last one + assert( pCutSet->ppCuts[pCutSet->nCuts] == pCut ); + assert( pCutSet->nCuts <= pCutSet->nCutsMax ); + + // cut structure is empty + if ( pCutSet->nCuts == 0 ) + { + pCutSet->nCuts++; + return; + } + + // the cut will be added - find its place + for ( i = pCutSet->nCuts-1; i >= 0; i-- ) + { +// Counter++; + if ( If_ManSortCompare( p, pCutSet->ppCuts[i], pCut ) <= 0 ) + break; + pCutSet->ppCuts[i+1] = pCutSet->ppCuts[i]; + pCutSet->ppCuts[i] = pCut; + } +// printf( "%d ", Counter ); + + // update the number of cuts + if ( pCutSet->nCuts < pCutSet->nCutsMax ) + pCutSet->nCuts++; +} + +/**Function************************************************************* + + Synopsis [Computes area flow.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float If_CutFlow( If_Man_t * p, If_Cut_t * pCut ) +{ + If_Obj_t * pLeaf; + float Flow; + int i; + assert( p->pPars->fSeqMap || pCut->nLeaves > 1 ); + Flow = If_CutLutArea(p, pCut); + if (wiremap) Flow += pCut->nLeaves*0.01; // janders + If_CutForEachLeaf( p, pCut, pLeaf, i ) + { + if ( pLeaf->nRefs == 0 ) { + Flow += If_ObjCutBest(pLeaf)->Area; + if (wiremap) Flow += 0.01*If_ObjCutBest(pLeaf)->nLeaves; // janders + } + else if ( p->pPars->fSeqMap ) // seq + Flow += If_ObjCutBest(pLeaf)->Area / pLeaf->nRefs; + else + { + assert( pLeaf->EstRefs > p->fEpsilon ); + Flow += If_ObjCutBest(pLeaf)->Area / pLeaf->EstRefs; + if (wiremap) Flow += (0.01*If_ObjCutBest(pLeaf)->nLeaves / pLeaf->EstRefs); // janders + } + } + return Flow; +} + +/**Function************************************************************* + + Synopsis [Average number of references of the leaves.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float If_CutAverageRefs( If_Man_t * p, If_Cut_t * pCut ) +{ + If_Obj_t * pLeaf; + int nRefsTotal, i; + assert( p->pPars->fSeqMap || pCut->nLeaves > 1 ); + nRefsTotal = 0; + If_CutForEachLeaf( p, pCut, pLeaf, i ) + nRefsTotal += pLeaf->nRefs; + return ((float)nRefsTotal)/pCut->nLeaves; +} + +/**Function************************************************************* + + Synopsis [Computes area of the first level.] + + Description [The cut need to be derefed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float If_CutDeref( If_Man_t * p, If_Cut_t * pCut, int nLevels ) +{ + If_Obj_t * pLeaf; + float Area; + int i; + Area = If_CutLutArea(p, pCut); + If_CutForEachLeaf( p, pCut, pLeaf, i ) + { + assert( pLeaf->nRefs > 0 ); + if ( --pLeaf->nRefs > 0 || !If_ObjIsAnd(pLeaf) || nLevels == 1 ) + continue; + Area += If_CutDeref( p, If_ObjCutBest(pLeaf), nLevels - 1 ); + } + return Area; +} + +/**Function************************************************************* + + Synopsis [Computes area of the first level.] + + Description [The cut need to be derefed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float If_CutRef( If_Man_t * p, If_Cut_t * pCut, int nLevels ) +{ + If_Obj_t * pLeaf; + float Area; + int i; + Area = If_CutLutArea(p, pCut); + If_CutForEachLeaf( p, pCut, pLeaf, i ) + { + assert( pLeaf->nRefs >= 0 ); + if ( pLeaf->nRefs++ > 0 || !If_ObjIsAnd(pLeaf) || nLevels == 1 ) + continue; + Area += If_CutRef( p, If_ObjCutBest(pLeaf), nLevels - 1 ); + } + return Area; +} + +float If_CutDerefEdge( If_Man_t * p, If_Cut_t * pCut, int nLevels ) +{ + If_Obj_t * pLeaf; + float Area; + int i; + Area = pCut->nLeaves; + If_CutForEachLeaf( p, pCut, pLeaf, i ) + { + assert( pLeaf->nRefs > 0 ); + if ( --pLeaf->nRefs > 0 || !If_ObjIsAnd(pLeaf) || nLevels == 1 ) + continue; + Area += If_CutDeref( p, If_ObjCutBest(pLeaf), nLevels - 1 ); + } + return Area; +} + + +float If_CutRefEdge( If_Man_t * p, If_Cut_t * pCut, int nLevels ) +{ + If_Obj_t * pLeaf; + float Area; + int i; + Area = pCut->nLeaves; + If_CutForEachLeaf( p, pCut, pLeaf, i ) + { + assert( pLeaf->nRefs >= 0 ); + if ( pLeaf->nRefs++ > 0 || !If_ObjIsAnd(pLeaf) || nLevels == 1 ) + continue; + Area += If_CutRef( p, If_ObjCutBest(pLeaf), nLevels - 1 ); + } + return Area; +} + +/**Function************************************************************* + + Synopsis [Prints one cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_CutPrint( If_Man_t * p, If_Cut_t * pCut ) +{ + unsigned i; + printf( "{" ); + for ( i = 0; i < pCut->nLeaves; i++ ) + printf( " %d", pCut->pLeaves[i] ); + printf( " }\n" ); +} + +/**Function************************************************************* + + Synopsis [Prints one cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_CutPrintTiming( If_Man_t * p, If_Cut_t * pCut ) +{ + If_Obj_t * pLeaf; + unsigned i; + printf( "{" ); + If_CutForEachLeaf( p, pCut, pLeaf, i ) + printf( " %d(%.2f/%.2f)", pLeaf->Id, If_ObjCutBest(pLeaf)->Delay, pLeaf->Required ); + printf( " }\n" ); +} + +/**Function************************************************************* + + Synopsis [Computes area of the first level.] + + Description [The cut need to be derefed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float If_CutAreaDerefed( If_Man_t * p, If_Cut_t * pCut, int nLevels ) +{ + float aResult, aResult2; + assert( p->pPars->fSeqMap || pCut->nLeaves > 1 ); + aResult2 = If_CutRef( p, pCut, nLevels ); + aResult = If_CutDeref( p, pCut, nLevels ); + assert( aResult > aResult2 - p->fEpsilon ); + assert( aResult < aResult2 + p->fEpsilon ); + if (wiremap) { // janders + aResult += 0.01*If_CutRefEdge(p, pCut, nLevels); + If_CutDerefEdge(p, pCut, nLevels); + } + return aResult; +} + +/**Function************************************************************* + + Synopsis [Computes area of the first level.] + + Description [The cut need to be derefed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float If_CutAreaRefed( If_Man_t * p, If_Cut_t * pCut, int nLevels ) +{ + float aResult, aResult2; + assert( p->pPars->fSeqMap || pCut->nLeaves > 1 ); + aResult2 = If_CutDeref( p, pCut, nLevels ); + aResult = If_CutRef( p, pCut, nLevels ); + assert( aResult > aResult2 - p->fEpsilon ); + assert( aResult < aResult2 + p->fEpsilon ); + return aResult; +} + +/**Function************************************************************* + + Synopsis [Moves the cut over the latch.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_CutLift( If_Cut_t * pCut ) +{ + unsigned i; + for ( i = 0; i < pCut->nLeaves; i++ ) + { + assert( (pCut->pLeaves[i] & 255) < 255 ); + pCut->pLeaves[i]++; + } +} + +/**Function************************************************************* + + Synopsis [Computes area of the first level.] + + Description [The cut need to be derefed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_CutCopy( If_Man_t * p, If_Cut_t * pCutDest, If_Cut_t * pCutSrc ) +{ + int * pLeaves; + char * pPerm; + unsigned * pTruth; + // save old arrays + pLeaves = pCutDest->pLeaves; + pPerm = pCutDest->pPerm; + pTruth = pCutDest->pTruth; + // copy the cut info + memcpy( pCutDest, pCutSrc, p->nCutBytes ); + // restore the arrays + pCutDest->pLeaves = pLeaves; + pCutDest->pPerm = pPerm; + pCutDest->pTruth = pTruth; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/if/ifMan.c b/abc_with_bb_support/src/map/if/ifMan.c new file mode 100644 index 000000000..fb49b7bcf --- /dev/null +++ b/abc_with_bb_support/src/map/if/ifMan.c @@ -0,0 +1,570 @@ +/**CFile**************************************************************** + + FileName [ifMan.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [FPGA mapping based on priority cuts.] + + Synopsis [Mapping manager.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - November 21, 2006.] + + Revision [$Id: ifMan.c,v 1.00 2006/11/21 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "if.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static If_Obj_t * If_ManSetupObj( If_Man_t * p ); + +static void If_ManCutSetRecycle( If_Man_t * p, If_Set_t * pSet ) { pSet->pNext = p->pFreeList; p->pFreeList = pSet; } +static If_Set_t * If_ManCutSetFetch( If_Man_t * p ) { If_Set_t * pTemp = p->pFreeList; p->pFreeList = p->pFreeList->pNext; return pTemp; } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the AIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +If_Man_t * If_ManStart( If_Par_t * pPars ) +{ + If_Man_t * p; + // start the manager + p = ALLOC( If_Man_t, 1 ); + memset( p, 0, sizeof(If_Man_t) ); + p->pPars = pPars; + p->fEpsilon = (float)0.001; + // allocate arrays for nodes + p->vCis = Vec_PtrAlloc( 100 ); + p->vCos = Vec_PtrAlloc( 100 ); + p->vObjs = Vec_PtrAlloc( 100 ); + p->vMapped = Vec_PtrAlloc( 100 ); + p->vTemp = Vec_PtrAlloc( 100 ); + // prepare the memory manager + p->nTruthWords = p->pPars->fTruth? If_CutTruthWords( p->pPars->nLutSize ) : 0; + p->nPermWords = p->pPars->fUsePerm? If_CutPermWords( p->pPars->nLutSize ) : 0; + p->nObjBytes = sizeof(If_Obj_t) + sizeof(int) * (p->pPars->nLutSize + p->nPermWords + p->nTruthWords); + p->nCutBytes = sizeof(If_Cut_t) + sizeof(int) * (p->pPars->nLutSize + p->nPermWords + p->nTruthWords); + p->nSetBytes = sizeof(If_Set_t) + (sizeof(If_Cut_t *) + p->nCutBytes) * (p->pPars->nCutsMax + 1); + p->pMemObj = Mem_FixedStart( p->nObjBytes ); +// p->pMemSet = Mem_FixedStart( p->nSetBytes ); + // report expected memory usage + if ( p->pPars->fVerbose ) + printf( "Memory (bytes): Truth = %4d. Cut = %4d. Obj = %4d. Set = %4d.\n", + 4 * p->nTruthWords, p->nCutBytes, p->nObjBytes, p->nSetBytes ); + // room for temporary truth tables + p->puTemp[0] = p->pPars->fTruth? ALLOC( unsigned, 4 * p->nTruthWords ) : NULL; + p->puTemp[1] = p->puTemp[0] + p->nTruthWords; + p->puTemp[2] = p->puTemp[1] + p->nTruthWords; + p->puTemp[3] = p->puTemp[2] + p->nTruthWords; + // create the constant node + p->pConst1 = If_ManSetupObj( p ); + p->pConst1->Type = IF_CONST1; + p->pConst1->fPhase = 1; + p->nObjs[IF_CONST1]++; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManRestart( If_Man_t * p ) +{ + FREE( p->pMemCi ); + Vec_PtrClear( p->vCis ); + Vec_PtrClear( p->vCos ); + Vec_PtrClear( p->vObjs ); + Vec_PtrClear( p->vMapped ); + Vec_PtrClear( p->vTemp ); + Mem_FixedRestart( p->pMemObj ); + // create the constant node + p->pConst1 = If_ManSetupObj( p ); + p->pConst1->Type = IF_CONST1; + p->pConst1->fPhase = 1; + // reset the counter of other nodes + p->nObjs[IF_CI] = p->nObjs[IF_CO] = p->nObjs[IF_AND] = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManStop( If_Man_t * p ) +{ + Vec_PtrFree( p->vCis ); + Vec_PtrFree( p->vCos ); + Vec_PtrFree( p->vObjs ); + Vec_PtrFree( p->vMapped ); + Vec_PtrFree( p->vTemp ); + if ( p->vLatchOrder ) Vec_PtrFree( p->vLatchOrder ); + if ( p->vLags ) Vec_IntFree( p->vLags ); + Mem_FixedStop( p->pMemObj, 0 ); + FREE( p->pMemCi ); + FREE( p->pMemAnd ); + FREE( p->puTemp[0] ); + // free pars memory + if ( p->pPars->pTimesArr ) + FREE( p->pPars->pTimesArr ); + if ( p->pPars->pTimesReq ) + FREE( p->pPars->pTimesReq ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Creates primary input.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +If_Obj_t * If_ManCreateCi( If_Man_t * p ) +{ + If_Obj_t * pObj; + pObj = If_ManSetupObj( p ); + pObj->Type = IF_CI; + Vec_PtrPush( p->vCis, pObj ); + p->nObjs[IF_CI]++; + return pObj; +} + +/**Function************************************************************* + + Synopsis [Creates primary output with the given driver.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +If_Obj_t * If_ManCreateCo( If_Man_t * p, If_Obj_t * pDriver ) +{ + If_Obj_t * pObj; + pObj = If_ManSetupObj( p ); + Vec_PtrPush( p->vCos, pObj ); + pObj->Type = IF_CO; + pObj->fCompl0 = If_IsComplement(pDriver); pDriver = If_Regular(pDriver); + pObj->pFanin0 = pDriver; pDriver->nRefs++; + p->nObjs[IF_CO]++; + return pObj; +} + +/**Function************************************************************* + + Synopsis [Create the new node assuming it does not exist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +If_Obj_t * If_ManCreateAnd( If_Man_t * p, If_Obj_t * pFan0, If_Obj_t * pFan1 ) +{ + If_Obj_t * pObj; + // perform constant propagation + if ( pFan0 == pFan1 ) + return pFan0; + if ( pFan0 == If_Not(pFan1) ) + return If_Not(p->pConst1); + if ( If_Regular(pFan0) == p->pConst1 ) + return pFan0 == p->pConst1 ? pFan1 : If_Not(p->pConst1); + if ( If_Regular(pFan1) == p->pConst1 ) + return pFan1 == p->pConst1 ? pFan0 : If_Not(p->pConst1); + // get memory for the new object + pObj = If_ManSetupObj( p ); + pObj->Type = IF_AND; + pObj->fCompl0 = If_IsComplement(pFan0); pFan0 = If_Regular(pFan0); + pObj->fCompl1 = If_IsComplement(pFan1); pFan1 = If_Regular(pFan1); + pObj->pFanin0 = pFan0; pFan0->nRefs++; pFan0->nVisits++; pFan0->nVisitsCopy++; + pObj->pFanin1 = pFan1; pFan1->nRefs++; pFan1->nVisits++; pFan1->nVisitsCopy++; + pObj->fPhase = (pObj->fCompl0 ^ pFan0->fPhase) & (pObj->fCompl1 ^ pFan1->fPhase); + pObj->Level = 1 + IF_MAX( pFan0->Level, pFan1->Level ); + if ( p->nLevelMax < (int)pObj->Level ) + p->nLevelMax = (int)pObj->Level; + p->nObjs[IF_AND]++; + return pObj; +} + +/**Function************************************************************* + + Synopsis [Create the new node assuming it does not exist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +If_Obj_t * If_ManCreateXor( If_Man_t * p, If_Obj_t * pFan0, If_Obj_t * pFan1 ) +{ + If_Obj_t * pRes1, * pRes2; + pRes1 = If_ManCreateAnd( p, If_Not(pFan0), pFan1 ); + pRes2 = If_ManCreateAnd( p, pFan0, If_Not(pFan1) ); + return If_Not( If_ManCreateAnd( p, If_Not(pRes1), If_Not(pRes2) ) ); +} + +/**Function************************************************************* + + Synopsis [Create the new node assuming it does not exist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +If_Obj_t * If_ManCreateMux( If_Man_t * p, If_Obj_t * pFan0, If_Obj_t * pFan1, If_Obj_t * pCtrl ) +{ + If_Obj_t * pRes1, * pRes2; + pRes1 = If_ManCreateAnd( p, pFan0, If_Not(pCtrl) ); + pRes2 = If_ManCreateAnd( p, pFan1, pCtrl ); + return If_Not( If_ManCreateAnd( p, If_Not(pRes1), If_Not(pRes2) ) ); +} + +/**Function************************************************************* + + Synopsis [Creates the choice node.] + + Description [Should be called after the equivalence class nodes are linked.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManCreateChoice( If_Man_t * p, If_Obj_t * pObj ) +{ + If_Obj_t * pTemp; + // mark the node as a representative if its class + assert( pObj->fRepr == 0 ); + pObj->fRepr = 1; + // update the level of this node (needed for correct required time computation) + for ( pTemp = pObj; pTemp; pTemp = pTemp->pEquiv ) + { + pObj->Level = IF_MAX( pObj->Level, pTemp->Level ); + pTemp->nVisits++; pTemp->nVisitsCopy++; + } + // mark the largest level + if ( p->nLevelMax < (int)pObj->Level ) + p->nLevelMax = (int)pObj->Level; +} + +/**Function************************************************************* + + Synopsis [Prepares memory for one cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManSetupCut( If_Man_t * p, If_Cut_t * pCut ) +{ + memset( pCut, 0, sizeof(If_Cut_t) ); + pCut->nLimit = p->pPars->nLutSize; + pCut->pLeaves = (int *)(pCut + 1); + if ( p->pPars->fUsePerm ) + pCut->pPerm = (char *)(pCut->pLeaves + p->pPars->nLutSize); + if ( p->pPars->fTruth ) + pCut->pTruth = pCut->pLeaves + p->pPars->nLutSize + p->nPermWords; +} + +/**Function************************************************************* + + Synopsis [Prepares memory for one cutset.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManSetupSet( If_Man_t * p, If_Set_t * pSet ) +{ + char * pArray; + int i; + pSet->nCuts = 0; + pSet->nCutsMax = p->pPars->nCutsMax; + pSet->ppCuts = (If_Cut_t **)(pSet + 1); + pArray = (char *)pSet->ppCuts + sizeof(If_Cut_t *) * (pSet->nCutsMax+1); + for ( i = 0; i <= pSet->nCutsMax; i++ ) + { + pSet->ppCuts[i] = (If_Cut_t *)(pArray + i * p->nCutBytes); + If_ManSetupCut( p, pSet->ppCuts[i] ); + } +// pArray += (pSet->nCutsMax + 1) * p->nCutBytes; +// assert( ((char *)pArray) - ((char *)pSet) == p->nSetBytes ); +} + +/**Function************************************************************* + + Synopsis [Prepares memory for one cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManSetupCutTriv( If_Man_t * p, If_Cut_t * pCut, int ObjId ) +{ + pCut->fCompl = 0; + pCut->nLimit = p->pPars->nLutSize; + pCut->nLeaves = 1; + pCut->pLeaves[0] = p->pPars->fLiftLeaves? (ObjId << 8) : ObjId; + pCut->uSign = If_ObjCutSign( pCut->pLeaves[0] ); + // set up elementary truth table of the unit cut + if ( p->pPars->fTruth ) + { + int i, nTruthWords; + nTruthWords = Extra_TruthWordNum( pCut->nLimit ); + for ( i = 0; i < nTruthWords; i++ ) + If_CutTruth(pCut)[i] = 0xAAAAAAAA; + } +} + +/**Function************************************************************* + + Synopsis [Prepares memory for the node with cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +If_Obj_t * If_ManSetupObj( If_Man_t * p ) +{ + If_Obj_t * pObj; + // get memory for the object + pObj = (If_Obj_t *)Mem_FixedEntryFetch( p->pMemObj ); + memset( pObj, 0, sizeof(If_Obj_t) ); + If_ManSetupCut( p, &pObj->CutBest ); + // assign ID and save + pObj->Id = Vec_PtrSize(p->vObjs); + Vec_PtrPush( p->vObjs, pObj ); + // set the required times + pObj->Required = IF_FLOAT_LARGE; + return pObj; +} + +/**Function************************************************************* + + Synopsis [Prepares memory for one cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManSetupCiCutSets( If_Man_t * p ) +{ + If_Obj_t * pObj; + int i; + assert( p->pMemCi == NULL ); + // create elementary cuts for the CIs + If_ManForEachCi( p, pObj, i ) + If_ManSetupCutTriv( p, &pObj->CutBest, pObj->Id ); + // create elementary cutsets for the CIs + p->pMemCi = (If_Set_t *)malloc( If_ManCiNum(p) * (sizeof(If_Set_t) + sizeof(void *)) ); + If_ManForEachCi( p, pObj, i ) + { + pObj->pCutSet = (If_Set_t *)((char *)p->pMemCi + i * (sizeof(If_Set_t) + sizeof(void *))); + pObj->pCutSet->nCuts = 1; + pObj->pCutSet->nCutsMax = p->pPars->nCutsMax; + pObj->pCutSet->ppCuts = (If_Cut_t **)(pObj->pCutSet + 1); + pObj->pCutSet->ppCuts[0] = &pObj->CutBest; + } +} + +/**Function************************************************************* + + Synopsis [Prepares cutset of the node.] + + Description [Elementary cutset will be added last.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +If_Set_t * If_ManSetupNodeCutSet( If_Man_t * p, If_Obj_t * pObj ) +{ + assert( If_ObjIsAnd(pObj) ); + assert( pObj->pCutSet == NULL ); +// pObj->pCutSet = (If_Set_t *)Mem_FixedEntryFetch( p->pMemSet ); +// If_ManSetupSet( p, pObj->pCutSet ); + + pObj->pCutSet = If_ManCutSetFetch( p ); + pObj->pCutSet->nCuts = 0; + pObj->pCutSet->nCutsMax = p->pPars->nCutsMax; + + return pObj->pCutSet; +} + +/**Function************************************************************* + + Synopsis [Dereferences cutset of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManDerefNodeCutSet( If_Man_t * p, If_Obj_t * pObj ) +{ + If_Obj_t * pFanin; + assert( If_ObjIsAnd(pObj) ); + // consider the node + assert( pObj->nVisits >= 0 ); + if ( pObj->nVisits == 0 ) + { +// Mem_FixedEntryRecycle( p->pMemSet, (char *)pObj->pCutSet ); + If_ManCutSetRecycle( p, pObj->pCutSet ); + pObj->pCutSet = NULL; + } + // consider the first fanin + pFanin = If_ObjFanin0(pObj); + assert( pFanin->nVisits > 0 ); + if ( !If_ObjIsCi(pFanin) && --pFanin->nVisits == 0 ) + { +// Mem_FixedEntryRecycle( p->pMemSet, (char *)pFanin->pCutSet ); + If_ManCutSetRecycle( p, pFanin->pCutSet ); + pFanin->pCutSet = NULL; + } + // consider the second fanin + pFanin = If_ObjFanin1(pObj); + assert( pFanin->nVisits > 0 ); + if ( !If_ObjIsCi(pFanin) && --pFanin->nVisits == 0 ) + { +// Mem_FixedEntryRecycle( p->pMemSet, (char *)pFanin->pCutSet ); + If_ManCutSetRecycle( p, pFanin->pCutSet ); + pFanin->pCutSet = NULL; + } +} + +/**Function************************************************************* + + Synopsis [Dereferences cutset of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManDerefChoiceCutSet( If_Man_t * p, If_Obj_t * pObj ) +{ + If_Obj_t * pTemp; + assert( If_ObjIsAnd(pObj) ); + assert( pObj->fRepr ); + assert( pObj->nVisits > 0 ); + // consider the nodes in the choice class + for ( pTemp = pObj; pTemp; pTemp = pTemp->pEquiv ) + { + assert( pTemp == pObj || pTemp->nVisits == 1 ); + if ( --pTemp->nVisits == 0 ) + { +// Mem_FixedEntryRecycle( p->pMemSet, (char *)pTemp->pCutSet ); + If_ManCutSetRecycle( p, pTemp->pCutSet ); + pTemp->pCutSet = NULL; + } + } +} + +/**Function************************************************************* + + Synopsis [Dereferences cutset of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManSetupSetAll( If_Man_t * p, int nCrossCut ) +{ + If_Set_t * pCutSet; + int i, nCutSets; + nCutSets = 128 + nCrossCut; + p->pFreeList = p->pMemAnd = pCutSet = (If_Set_t *)malloc( nCutSets * p->nSetBytes ); + for ( i = 0; i < nCutSets; i++ ) + { + If_ManSetupSet( p, pCutSet ); + if ( i == nCutSets - 1 ) + pCutSet->pNext = NULL; + else + pCutSet->pNext = (If_Set_t *)( (char *)pCutSet + p->nSetBytes ); + pCutSet = pCutSet->pNext; + } + assert( pCutSet == NULL ); + + if ( p->pPars->fVerbose ) + { + printf( "Node = %7d. Ch = %5d. Total mem = %7.2f Mb. Peak cut mem = %7.2f Mb.\n", + If_ManAndNum(p), p->nChoices, + 1.0 * (p->nObjBytes + 2*sizeof(void *)) * If_ManObjNum(p) / (1<<20), + 1.0 * p->nSetBytes * nCrossCut / (1<<20) ); + } +// printf( "Cross cut = %d.\n", nCrossCut ); + +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/if/ifMap.c b/abc_with_bb_support/src/map/if/ifMap.c new file mode 100644 index 000000000..2865ce8c2 --- /dev/null +++ b/abc_with_bb_support/src/map/if/ifMap.c @@ -0,0 +1,300 @@ +/**CFile**************************************************************** + + FileName [ifMap.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [FPGA mapping based on priority cuts.] + + Synopsis [Mapping procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - November 21, 2006.] + + Revision [$Id: ifMap.c,v 1.00 2006/11/21 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "if.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Counts the number of 1s in the signature.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int If_WordCountOnes( unsigned uWord ) +{ + uWord = (uWord & 0x55555555) + ((uWord>>1) & 0x55555555); + uWord = (uWord & 0x33333333) + ((uWord>>2) & 0x33333333); + uWord = (uWord & 0x0F0F0F0F) + ((uWord>>4) & 0x0F0F0F0F); + uWord = (uWord & 0x00FF00FF) + ((uWord>>8) & 0x00FF00FF); + return (uWord & 0x0000FFFF) + (uWord>>16); +} + +/**Function************************************************************* + + Synopsis [Finds the best cut for the given node.] + + Description [Mapping modes: delay (0), area flow (1), area (2).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ObjPerformMappingAnd( If_Man_t * p, If_Obj_t * pObj, int Mode, int fPreprocess ) +{ + If_Set_t * pCutSet; + If_Cut_t * pCut0, * pCut1, * pCut; + int i, k; + + assert( p->pPars->fSeqMap || !If_ObjIsAnd(pObj->pFanin0) || pObj->pFanin0->pCutSet->nCuts > 1 ); + assert( p->pPars->fSeqMap || !If_ObjIsAnd(pObj->pFanin1) || pObj->pFanin1->pCutSet->nCuts > 1 ); + + // prepare + if ( !p->pPars->fSeqMap ) + { + if ( Mode == 0 ) + pObj->EstRefs = (float)pObj->nRefs; + else if ( Mode == 1 ) + pObj->EstRefs = (float)((2.0 * pObj->EstRefs + pObj->nRefs) / 3.0); + } + if ( Mode && pObj->nRefs > 0 ) + If_CutDeref( p, If_ObjCutBest(pObj), IF_INFINITY ); + + // prepare the cutset + pCutSet = If_ManSetupNodeCutSet( p, pObj ); + + // get the current assigned best cut + pCut = If_ObjCutBest(pObj); + if ( pCut->nLeaves > 0 ) + { + // recompute the parameters of the best cut + pCut->Delay = If_CutDelay( p, pCut ); + assert( pCut->Delay <= pObj->Required + p->fEpsilon ); + pCut->Area = (Mode == 2)? If_CutAreaDerefed( p, pCut, IF_INFINITY ) : If_CutFlow( p, pCut ); + // save the best cut from the previous iteration + if ( !fPreprocess ) + If_CutCopy( p, pCutSet->ppCuts[pCutSet->nCuts++], pCut ); + } + + // generate cuts + If_ObjForEachCut( pObj->pFanin0, pCut0, i ) + If_ObjForEachCut( pObj->pFanin1, pCut1, k ) + { + // get the next free cut + assert( pCutSet->nCuts <= pCutSet->nCutsMax ); + pCut = pCutSet->ppCuts[pCutSet->nCuts]; + // make sure K-feasible cut exists + if ( If_WordCountOnes(pCut0->uSign | pCut1->uSign) > p->pPars->nLutSize ) + continue; + // merge the nodes + if ( !If_CutMerge( pCut0, pCut1, pCut ) ) + continue; + assert( p->pPars->fSeqMap || pCut->nLeaves > 1 ); + p->nCutsMerged++; + // check if this cut is contained in any of the available cuts +// if ( p->pPars->pFuncCost == NULL && If_CutFilter( p, pCut ) ) // do not filter functionality cuts + if ( If_CutFilter( pCutSet, pCut ) ) + continue; + // compute the truth table + pCut->fCompl = 0; + if ( p->pPars->fTruth ) + If_CutComputeTruth( p, pCut, pCut0, pCut1, pObj->fCompl0, pObj->fCompl1 ); + // compute the application-specific cost and depth + pCut->fUser = (p->pPars->pFuncCost != NULL); + pCut->Cost = p->pPars->pFuncCost? p->pPars->pFuncCost(pCut) : 0; + if ( pCut->Cost == IF_COST_MAX ) + continue; + // check if the cut satisfies the required times + pCut->Delay = If_CutDelay( p, pCut ); +// printf( "%.2f ", pCut->Delay ); + if ( Mode && pCut->Delay > pObj->Required + p->fEpsilon ) + continue; + // compute area of the cut (this area may depend on the application specific cost) + pCut->Area = (Mode == 2)? If_CutAreaDerefed( p, pCut, IF_INFINITY ) : If_CutFlow( p, pCut ); + pCut->AveRefs = (Mode == 0)? (float)0.0 : If_CutAverageRefs( p, pCut ); + // insert the cut into storage + If_CutSort( p, pCutSet, pCut ); + } + assert( pCutSet->nCuts > 0 ); + + // add the trivial cut to the set + If_ManSetupCutTriv( p, pCutSet->ppCuts[pCutSet->nCuts++], pObj->Id ); + assert( pCutSet->nCuts <= pCutSet->nCutsMax+1 ); + + // update the best cut + if ( !fPreprocess || pCutSet->ppCuts[0]->Delay <= pObj->Required + p->fEpsilon ) + If_CutCopy( p, If_ObjCutBest(pObj), pCutSet->ppCuts[0] ); + assert( p->pPars->fSeqMap || If_ObjCutBest(pObj)->nLeaves > 1 ); + + // ref the selected cut + if ( Mode && pObj->nRefs > 0 ) + If_CutRef( p, If_ObjCutBest(pObj), IF_INFINITY ); + + // call the user specified function for each cut + if ( p->pPars->pFuncUser ) + If_ObjForEachCut( pObj, pCut, i ) + p->pPars->pFuncUser( p, pObj, pCut ); + + // free the cuts + If_ManDerefNodeCutSet( p, pObj ); +} + +/**Function************************************************************* + + Synopsis [Finds the best cut for the choice node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ObjPerformMappingChoice( If_Man_t * p, If_Obj_t * pObj, int Mode, int fPreprocess ) +{ + If_Set_t * pCutSet; + If_Obj_t * pTemp; + If_Cut_t * pCutTemp, * pCut; + int i; + assert( pObj->pEquiv != NULL ); + + // prepare + if ( Mode && pObj->nRefs > 0 ) + If_CutDeref( p, If_ObjCutBest(pObj), IF_INFINITY ); + + // remove elementary cuts + for ( pTemp = pObj; pTemp; pTemp = pTemp->pEquiv ) + pTemp->pCutSet->nCuts--; + + // update the cutset of the node + pCutSet = pObj->pCutSet; + + // generate cuts + for ( pTemp = pObj->pEquiv; pTemp; pTemp = pTemp->pEquiv ) + { + assert( pTemp->nRefs == 0 ); + assert( p->pPars->fSeqMap || pTemp->pCutSet->nCuts > 0 ); + // go through the cuts of this node + If_ObjForEachCut( pTemp, pCutTemp, i ) + { + assert( p->pPars->fSeqMap || pCutTemp->nLeaves > 1 ); + // get the next free cut + assert( pCutSet->nCuts <= pCutSet->nCutsMax ); + pCut = pCutSet->ppCuts[pCutSet->nCuts]; + // copy the cut into storage + If_CutCopy( p, pCut, pCutTemp ); + // check if this cut is contained in any of the available cuts + if ( If_CutFilter( pCutSet, pCut ) ) + continue; + // check if the cut satisfies the required times + assert( pCut->Delay == If_CutDelay( p, pCut ) ); + if ( Mode && pCut->Delay > pObj->Required + p->fEpsilon ) + continue; + // set the phase attribute + assert( pCut->fCompl == 0 ); + pCut->fCompl ^= (pObj->fPhase ^ pTemp->fPhase); // why ^= ? + // compute area of the cut (this area may depend on the application specific cost) + pCut->Area = (Mode == 2)? If_CutAreaDerefed( p, pCut, IF_INFINITY ) : If_CutFlow( p, pCut ); + pCut->AveRefs = (Mode == 0)? (float)0.0 : If_CutAverageRefs( p, pCut ); + // insert the cut into storage + If_CutSort( p, pCutSet, pCut ); + } + } + assert( pCutSet->nCuts > 0 ); + + // add the trivial cut to the set + If_ManSetupCutTriv( p, pCutSet->ppCuts[pCutSet->nCuts++], pObj->Id ); + assert( pCutSet->nCuts <= pCutSet->nCutsMax+1 ); + + // update the best cut + if ( !fPreprocess || pCutSet->ppCuts[0]->Delay <= pObj->Required + p->fEpsilon ) + If_CutCopy( p, If_ObjCutBest(pObj), pCutSet->ppCuts[0] ); + assert( p->pPars->fSeqMap || If_ObjCutBest(pObj)->nLeaves > 1 ); + + // ref the selected cut + if ( Mode && pObj->nRefs > 0 ) + If_CutRef( p, If_ObjCutBest(pObj), IF_INFINITY ); + + // free the cuts + If_ManDerefChoiceCutSet( p, pObj ); +} + +/**Function************************************************************* + + Synopsis [Performs one mapping pass over all nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_ManPerformMappingRound( If_Man_t * p, int nCutsUsed, int Mode, int fPreprocess, char * pLabel ) +{ +// ProgressBar * pProgress; + If_Obj_t * pObj; + int i, clk = clock(); + assert( Mode >= 0 && Mode <= 2 ); + // set the sorting function + if ( Mode || p->pPars->fArea ) // area + p->SortMode = 1; + else if ( p->pPars->fFancy ) + p->SortMode = 2; + else + p->SortMode = 0; + // set the cut number + p->nCutsUsed = nCutsUsed; + p->nCutsMerged = 0; + // map the internal nodes +// pProgress = Extra_ProgressBarStart( stdout, If_ManObjNum(p) ); + If_ManForEachNode( p, pObj, i ) + { +// Extra_ProgressBarUpdate( pProgress, i, pLabel ); + If_ObjPerformMappingAnd( p, pObj, Mode, fPreprocess ); + if ( pObj->fRepr ) + If_ObjPerformMappingChoice( p, pObj, Mode, fPreprocess ); + } +// Extra_ProgressBarStop( pProgress ); + // make sure the visit counters are all zero + If_ManForEachNode( p, pObj, i ) + assert( pObj->nVisits == 0 ); + // compute required times and stats + If_ManComputeRequired( p ); + if ( p->pPars->fVerbose ) + { + char Symb = fPreprocess? 'P' : ((Mode == 0)? 'D' : ((Mode == 1)? 'F' : 'A')); + printf( "%c: Del = %7.2f. Ar = %9.1f. Net = %8d. Cut = %8d. ", + Symb, p->RequiredGlo, p->AreaGlo, p->nNets, p->nCutsMerged ); + PRT( "T", clock() - clk ); +// printf( "Max number of cuts = %d. Average number of cuts = %5.2f.\n", +// p->nCutsMax, 1.0 * p->nCutsMerged / If_ManAndNum(p) ); + } + return 1; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/if/ifReduce.c b/abc_with_bb_support/src/map/if/ifReduce.c new file mode 100644 index 000000000..d63b4ece9 --- /dev/null +++ b/abc_with_bb_support/src/map/if/ifReduce.c @@ -0,0 +1,574 @@ +/**CFile**************************************************************** + + FileName [ifExpand.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [FPGA mapping based on priority cuts.] + + Synopsis [Incremental improvement of current mapping.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - November 21, 2006.] + + Revision [$Id: ifExpand.c,v 1.00 2006/11/21 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "if.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void If_ManImproveReduce( If_Man_t * p, int nLimit ); +static void If_ManImproveExpand( If_Man_t * p, int nLimit ); +static void If_ManImproveNodeExpand( If_Man_t * p, If_Obj_t * pObj, int nLimit, Vec_Ptr_t * vFront, Vec_Ptr_t * vFrontOld, Vec_Ptr_t * vVisited ); +static void If_ManImproveNodePrepare( If_Man_t * p, If_Obj_t * pObj, int nLimit, Vec_Ptr_t * vFront, Vec_Ptr_t * vFrontOld, Vec_Ptr_t * vVisited ); +static void If_ManImproveNodeUpdate( If_Man_t * p, If_Obj_t * pObj, Vec_Ptr_t * vFront ); +static void If_ManImproveNodeFaninCompact( If_Man_t * p, If_Obj_t * pObj, int nLimit, Vec_Ptr_t * vFront, Vec_Ptr_t * vVisited ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Improves current mapping using expand/Expand of one cut.] + + Description [Assumes current mapping assigned and required times computed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManImproveMapping( If_Man_t * p ) +{ + int clk; + + clk = clock(); + If_ManImproveExpand( p, p->pPars->nLutSize ); + If_ManComputeRequired( p ); + if ( p->pPars->fVerbose ) + { + printf( "E: Del = %7.2f. Ar = %9.1f. Net = %8d. Cut = %8d. ", + p->RequiredGlo, p->AreaGlo, p->nNets, p->nCutsMerged ); + PRT( "T", clock() - clk ); + } + +/* + clk = clock(); + If_ManImproveReduce( p, p->pPars->nLutSize ); + If_ManComputeRequired( p, 0 ); + if ( p->pPars->fVerbose ) + { + printf( "R: Del = %6.2f. Area = %8.2f. Nets = %6d. Cuts = %8d. Lim = %2d. Ave = %5.2f. ", + p->RequiredGlo, p->AreaGlo, p->nNets, p->nCutsMerged, p->nCutsUsed, 1.0 * p->nCutsMerged / If_ManAndNum(p) ); + PRT( "T", clock() - clk ); + } +*/ +/* + clk = clock(); + If_ManImproveExpand( p, p->pPars->nLutSize ); + If_ManComputeRequired( p, 0 ); + if ( p->pPars->fVerbose ) + { + printf( "E: Del = %6.2f. Area = %8.2f. Nets = %6d. Cuts = %8d. Lim = %2d. Ave = %5.2f. ", + p->RequiredGlo, p->AreaGlo, p->nNets, p->nCutsMerged, p->nCutsUsed, 1.0 * p->nCutsMerged / If_ManAndNum(p) ); + PRT( "T", clock() - clk ); + } +*/ +} + +/**Function************************************************************* + + Synopsis [Performs area recovery for each node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManImproveExpand( If_Man_t * p, int nLimit ) +{ + Vec_Ptr_t * vFront, * vFrontOld, * vVisited; + If_Obj_t * pObj; + int i; + vFront = Vec_PtrAlloc( nLimit ); + vFrontOld = Vec_PtrAlloc( nLimit ); + vVisited = Vec_PtrAlloc( 100 ); + // iterate through all nodes in the topological order + If_ManForEachNode( p, pObj, i ) + If_ManImproveNodeExpand( p, pObj, nLimit, vFront, vFrontOld, vVisited ); + Vec_PtrFree( vFront ); + Vec_PtrFree( vFrontOld ); + Vec_PtrFree( vVisited ); +} + +/**Function************************************************************* + + Synopsis [Counts the number of nodes with no external fanout.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_ManImproveCutCost( If_Man_t * p, Vec_Ptr_t * vFront ) +{ + If_Obj_t * pFanin; + int i, Counter = 0; + Vec_PtrForEachEntry( vFront, pFanin, i ) + if ( pFanin->nRefs == 0 ) + Counter++; + return Counter; +} + +/**Function************************************************************* + + Synopsis [Performs area recovery for each node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManImproveNodeExpand( If_Man_t * p, If_Obj_t * pObj, int nLimit, Vec_Ptr_t * vFront, Vec_Ptr_t * vFrontOld, Vec_Ptr_t * vVisited ) +{ + If_Obj_t * pFanin; + If_Cut_t * pCut; + int CostBef, CostAft, i; + float DelayOld, AreaBef, AreaAft; + pCut = If_ObjCutBest(pObj); + assert( pCut->Delay <= pObj->Required + p->fEpsilon ); + if ( pObj->nRefs == 0 ) + return; + // get the delay + DelayOld = pCut->Delay; + // get the area + AreaBef = If_CutAreaRefed( p, pCut, IF_INFINITY ); +// if ( AreaBef == 1 ) +// return; + // the cut is non-trivial + If_ManImproveNodePrepare( p, pObj, nLimit, vFront, vFrontOld, vVisited ); + // iteratively modify the cut + If_CutDeref( p, pCut, IF_INFINITY ); + CostBef = If_ManImproveCutCost( p, vFront ); + If_ManImproveNodeFaninCompact( p, pObj, nLimit, vFront, vVisited ); + CostAft = If_ManImproveCutCost( p, vFront ); + If_CutRef( p, pCut, IF_INFINITY ); + assert( CostBef >= CostAft ); + // clean up + Vec_PtrForEachEntry( vVisited, pFanin, i ) + pFanin->fMark = 0; + // update the node + If_ManImproveNodeUpdate( p, pObj, vFront ); + pCut->Delay = If_CutDelay( p, pCut ); + // get the new area + AreaAft = If_CutAreaRefed( p, pCut, IF_INFINITY ); + if ( AreaAft > AreaBef || pCut->Delay > pObj->Required + p->fEpsilon ) + { + If_ManImproveNodeUpdate( p, pObj, vFrontOld ); + AreaAft = If_CutAreaRefed( p, pCut, IF_INFINITY ); + assert( AreaAft == AreaBef ); + pCut->Delay = DelayOld; + } +} + +/**Function************************************************************* + + Synopsis [Performs area recovery for each node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManImproveMark_rec( If_Man_t * p, If_Obj_t * pObj, Vec_Ptr_t * vVisited ) +{ + if ( pObj->fMark ) + return; + assert( If_ObjIsAnd(pObj) ); + If_ManImproveMark_rec( p, If_ObjFanin0(pObj), vVisited ); + If_ManImproveMark_rec( p, If_ObjFanin1(pObj), vVisited ); + Vec_PtrPush( vVisited, pObj ); + pObj->fMark = 1; +} + +/**Function************************************************************* + + Synopsis [Prepares node mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManImproveNodePrepare( If_Man_t * p, If_Obj_t * pObj, int nLimit, Vec_Ptr_t * vFront, Vec_Ptr_t * vFrontOld, Vec_Ptr_t * vVisited ) +{ + If_Cut_t * pCut; + If_Obj_t * pLeaf; + int i; + Vec_PtrClear( vFront ); + Vec_PtrClear( vFrontOld ); + Vec_PtrClear( vVisited ); + // expand the cut downwards from the given place + pCut = If_ObjCutBest(pObj); + If_CutForEachLeaf( p, pCut, pLeaf, i ) + { + Vec_PtrPush( vFront, pLeaf ); + Vec_PtrPush( vFrontOld, pLeaf ); + Vec_PtrPush( vVisited, pLeaf ); + pLeaf->fMark = 1; + } + // mark the nodes in the cone + If_ManImproveMark_rec( p, pObj, vVisited ); +} + +/**Function************************************************************* + + Synopsis [Updates the frontier.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManImproveNodeUpdate( If_Man_t * p, If_Obj_t * pObj, Vec_Ptr_t * vFront ) +{ + If_Cut_t * pCut; + If_Obj_t * pFanin; + int i; + pCut = If_ObjCutBest(pObj); + // deref node's cut + If_CutDeref( p, pCut, IF_INFINITY ); + // update the node's cut + pCut->nLeaves = Vec_PtrSize(vFront); + Vec_PtrForEachEntry( vFront, pFanin, i ) + pCut->pLeaves[i] = pFanin->Id; + // ref the new cut + If_CutRef( p, pCut, IF_INFINITY ); +} + + +/**Function************************************************************* + + Synopsis [Returns 1 if the number of fanins will grow.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_ManImproveNodeWillGrow( If_Man_t * p, If_Obj_t * pObj ) +{ + If_Obj_t * pFanin0, * pFanin1; + assert( If_ObjIsAnd(pObj) ); + pFanin0 = If_ObjFanin0(pObj); + pFanin1 = If_ObjFanin1(pObj); + return !pFanin0->fMark && !pFanin1->fMark; +} + +/**Function************************************************************* + + Synopsis [Returns the increase in the number of fanins with no external refs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_ManImproveNodeFaninCost( If_Man_t * p, If_Obj_t * pObj ) +{ + int Counter = 0; + assert( If_ObjIsAnd(pObj) ); + // check if the node has external refs + if ( pObj->nRefs == 0 ) + Counter--; + // increment the number of fanins without external refs + if ( !If_ObjFanin0(pObj)->fMark && If_ObjFanin0(pObj)->nRefs == 0 ) + Counter++; + // increment the number of fanins without external refs + if ( !If_ObjFanin1(pObj)->fMark && If_ObjFanin1(pObj)->nRefs == 0 ) + Counter++; + return Counter; +} + +/**Function************************************************************* + + Synopsis [Updates the frontier.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManImproveNodeFaninUpdate( If_Man_t * p, If_Obj_t * pObj, Vec_Ptr_t * vFront, Vec_Ptr_t * vVisited ) +{ + If_Obj_t * pFanin; + assert( If_ObjIsAnd(pObj) ); + Vec_PtrRemove( vFront, pObj ); + pFanin = If_ObjFanin0(pObj); + if ( !pFanin->fMark ) + { + Vec_PtrPush( vFront, pFanin ); + Vec_PtrPush( vVisited, pFanin ); + pFanin->fMark = 1; + } + pFanin = If_ObjFanin1(pObj); + if ( !pFanin->fMark ) + { + Vec_PtrPush( vFront, pFanin ); + Vec_PtrPush( vVisited, pFanin ); + pFanin->fMark = 1; + } +} + +/**Function************************************************************* + + Synopsis [Compacts the number of external refs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_ManImproveNodeFaninCompact0( If_Man_t * p, If_Obj_t * pObj, int nLimit, Vec_Ptr_t * vFront, Vec_Ptr_t * vVisited ) +{ + If_Obj_t * pFanin; + int i; + Vec_PtrForEachEntry( vFront, pFanin, i ) + { + if ( If_ObjIsCi(pFanin) ) + continue; + if ( If_ManImproveNodeWillGrow(p, pFanin) ) + continue; + if ( If_ManImproveNodeFaninCost(p, pFanin) <= 0 ) + { + If_ManImproveNodeFaninUpdate( p, pFanin, vFront, vVisited ); + return 1; + } + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Compacts the number of external refs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_ManImproveNodeFaninCompact1( If_Man_t * p, If_Obj_t * pObj, int nLimit, Vec_Ptr_t * vFront, Vec_Ptr_t * vVisited ) +{ + If_Obj_t * pFanin; + int i; + Vec_PtrForEachEntry( vFront, pFanin, i ) + { + if ( If_ObjIsCi(pFanin) ) + continue; + if ( If_ManImproveNodeFaninCost(p, pFanin) < 0 ) + { + If_ManImproveNodeFaninUpdate( p, pFanin, vFront, vVisited ); + return 1; + } + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Compacts the number of external refs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_ManImproveNodeFaninCompact2( If_Man_t * p, If_Obj_t * pObj, int nLimit, Vec_Ptr_t * vFront, Vec_Ptr_t * vVisited ) +{ + If_Obj_t * pFanin; + int i; + Vec_PtrForEachEntry( vFront, pFanin, i ) + { + if ( If_ObjIsCi(pFanin) ) + continue; + if ( If_ManImproveNodeFaninCost(p, pFanin) <= 0 ) + { + If_ManImproveNodeFaninUpdate( p, pFanin, vFront, vVisited ); + return 1; + } + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Compacts the number of external refs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_ManImproveNodeFaninCompact_int( If_Man_t * p, If_Obj_t * pObj, int nLimit, Vec_Ptr_t * vFront, Vec_Ptr_t * vVisited ) +{ + if ( If_ManImproveNodeFaninCompact0(p, pObj, nLimit, vFront, vVisited) ) + return 1; + if ( Vec_PtrSize(vFront) < nLimit && If_ManImproveNodeFaninCompact1(p, pObj, nLimit, vFront, vVisited) ) + return 1; + if ( Vec_PtrSize(vFront) < nLimit && If_ManImproveNodeFaninCompact2(p, pObj, nLimit, vFront, vVisited) ) + return 1; + assert( Vec_PtrSize(vFront) <= nLimit ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Compacts the number of external refs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManImproveNodeFaninCompact( If_Man_t * p, If_Obj_t * pObj, int nLimit, Vec_Ptr_t * vFront, Vec_Ptr_t * vVisited ) +{ + while ( If_ManImproveNodeFaninCompact_int( p, pObj, nLimit, vFront, vVisited ) ); +} + + + + + +/**Function************************************************************* + + Synopsis [Performs fast mapping for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManImproveNodeReduce( If_Man_t * p, If_Obj_t * pObj, int nLimit ) +{ +/* + If_Cut_t * pCut, * pCut0, * pCut1, * pCutR; + If_Obj_t * pFanin0, * pFanin1; + float AreaBef, AreaAft; + int RetValue; + + assert( nLimit <= 32 ); + assert( If_ObjIsAnd(pObj) ); + // get the fanins + pFanin0 = If_ObjFanin0(pObj); + pFanin1 = If_ObjFanin1(pObj); + // get the cuts + pCut = If_ObjCutBest(pObj); + pCut0 = If_ObjIsCi(pFanin0) ? If_ObjCutTriv(pFanin0) : If_ObjCutBest(pFanin0); + pCut1 = If_ObjIsCi(pFanin1) ? If_ObjCutTriv(pFanin1) : If_ObjCutBest(pFanin1); + assert( pCut->Delay <= pObj->Required + p->fEpsilon ); + + // deref the cut if the node is refed + if ( pObj->nRefs > 0 ) + If_CutDeref( p, pCut, IF_INFINITY ); + // get the area + AreaBef = If_CutAreaDerefed( p, pCut, IF_INFINITY ); + // get the fanin support + if ( pFanin0->nRefs > 2 && pCut0->Delay < pObj->Required + p->fEpsilon ) +// if ( pSupp0->nRefs > 0 && pSupp0->Delay < pSupp->DelayR ) // this leads to 2% worse results + { + pCut0 = If_ObjCutTriv(pFanin0); + } + // get the fanin support + if ( pFanin1->nRefs > 2 && pCut1->Delay < pObj->Required + p->fEpsilon ) +// if ( pSupp1->nRefs > 0 && pSupp1->Delay < pSupp->DelayR ) + { + pCut1 = If_ObjCutTriv(pFanin1); + } + + // merge the cuts + pCutR = p->ppCuts[0]; + RetValue = If_CutMerge( pCut0, pCut1, pCutR ); + // try very simple cut + if ( !RetValue ) + { + RetValue = If_CutMerge( If_ObjCutTriv(pFanin0), If_ObjCutTriv(pFanin1), pCutR ); + assert( RetValue == 1 ); + } + if ( RetValue ) + { + pCutR->Delay = If_CutDelay( p, pCutR ); + AreaAft = If_CutAreaDerefed( p, pCutR, IF_INFINITY ); + // update the best cut + if ( AreaAft < AreaBef - p->fEpsilon && pCutR->Delay < pObj->Required + p->fEpsilon ) + If_CutCopy( p, pCut, pCutR ); + } + // recompute the delay of the best cut + pCut->Delay = If_CutDelay( p, pCut ); + // ref the cut if the node is refed + if ( pObj->nRefs > 0 ) + If_CutRef( p, pCut, IF_INFINITY ); +*/ +} + +/**Function************************************************************* + + Synopsis [Performs area recovery for each node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManImproveReduce( If_Man_t * p, int nLimit ) +{ + If_Obj_t * pObj; + int i; + If_ManForEachNode( p, pObj, i ) + If_ManImproveNodeReduce( p, pObj, nLimit ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/if/ifSeq.c b/abc_with_bb_support/src/map/if/ifSeq.c new file mode 100644 index 000000000..9e3391106 --- /dev/null +++ b/abc_with_bb_support/src/map/if/ifSeq.c @@ -0,0 +1,405 @@ +/**CFile**************************************************************** + + FileName [ifSeq.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [FPGA mapping based on priority cuts.] + + Synopsis [Sequential mapping.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - November 21, 2006.] + + Revision [$Id: ifSeq.c,v 1.00 2006/11/21 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "if.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +extern int s_MappingTime; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Prepares for sequential mapping by linking the latches.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManPrepareMappingSeq( If_Man_t * p ) +{ + If_Obj_t * pObjLi, * pObjLo; + int i; + + // link the latch outputs (CIs) directly to the drivers of latch inputs (COs) + for ( i = 0; i < p->pPars->nLatches; i++ ) + { + pObjLi = If_ManLi( p, i ); + pObjLo = If_ManLo( p, i ); + pObjLo->pFanin0 = If_ObjFanin0( pObjLi ); + pObjLo->fCompl0 = If_ObjFaninC0( pObjLi ); + } +} + +/**Function************************************************************* + + Synopsis [Collects latches in the topological order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManCollectLatches_rec( If_Obj_t * pObj, Vec_Ptr_t * vLatches ) +{ + if ( !If_ObjIsLatch(pObj) ) + return; + if ( pObj->fMark ) + return; + pObj->fMark = 1; + If_ManCollectLatches_rec( pObj->pFanin0, vLatches ); + Vec_PtrPush( vLatches, pObj ); +} + +/**Function************************************************************* + + Synopsis [Collects latches in the topological order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * If_ManCollectLatches( If_Man_t * p ) +{ + Vec_Ptr_t * vLatches; + If_Obj_t * pObj; + int i; + // collect latches + vLatches = Vec_PtrAlloc( p->pPars->nLatches ); + If_ManForEachLatchOutput( p, pObj, i ) + If_ManCollectLatches_rec( pObj, vLatches ); + // clean marks + Vec_PtrForEachEntry( vLatches, pObj, i ) + pObj->fMark = 0; + assert( Vec_PtrSize(vLatches) == p->pPars->nLatches ); + return vLatches; +} + +/**Function************************************************************* + + Synopsis [Performs one pass of l-value computation over all nodes.] + + Description [Experimentally it was found that checking POs changes + is not enough to detect the convergence of l-values in the network.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_ManPerformMappingRoundSeq( If_Man_t * p, int nIter ) +{ + If_Obj_t * pObj; + int i, clk = clock(); + int fVeryVerbose = 0; + int fChange = 0; + + // map the internal nodes + p->nCutsMerged = 0; + If_ManForEachNode( p, pObj, i ) + { + If_ObjPerformMappingAnd( p, pObj, 0, 0 ); + if ( pObj->fRepr ) + If_ObjPerformMappingChoice( p, pObj, 0, 0 ); + } + + // postprocess the mapping +//printf( "Itereation %d: \n", nIter ); + If_ManForEachNode( p, pObj, i ) + { + // update the LValues stored separately + if ( If_ObjLValue(pObj) < If_ObjCutBest(pObj)->Delay - p->fEpsilon ) + { + If_ObjSetLValue( pObj, If_ObjCutBest(pObj)->Delay ); + fChange = 1; + } +//printf( "%d ", (int)If_ObjLValue(pObj) ); + // reset the visit counters + assert( pObj->nVisits == 0 ); + pObj->nVisits = pObj->nVisitsCopy; + } +//printf( "\n" ); + + // propagate LValues over the registers + Vec_PtrForEachEntry( p->vLatchOrder, pObj, i ) + { + If_ObjSetLValue( pObj, If_ObjLValue(If_ObjFanin0(pObj)) - p->Period ); + If_ObjSetArrTime( pObj, If_ObjLValue(pObj) ); + } + + // compute area and delay + if ( fVeryVerbose ) + { + p->RequiredGlo = If_ManDelayMax( p, 1 ); + p->AreaGlo = If_ManScanMapping(p); + printf( "S%d: Fi = %6.2f. Del = %6.2f. Area = %8.2f. Cuts = %8d. ", + nIter, (float)p->Period, p->RequiredGlo, p->AreaGlo, p->nCutsMerged ); + PRT( "T", clock() - clk ); + } + return fChange; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if retiming with this clock period is feasible.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_ManBinarySearchPeriod( If_Man_t * p ) +{ + If_Obj_t * pObj; + int i, c, fConverged; + int fResetRefs = 0; + + p->nAttempts++; + + // reset initial LValues (PIs to 0; others to -inf) + If_ManForEachObj( p, pObj, i ) + { + if ( If_ObjIsPi(pObj) || If_ObjIsConst1(pObj) ) + { + If_ObjSetLValue( pObj, (float)0.0 ); + If_ObjSetArrTime( pObj, (float)0.0 ); + } + else + { + If_ObjSetLValue( pObj, (float)-IF_INFINITY ); + If_ObjSetArrTime( pObj, (float)-IF_INFINITY ); + } + // undo any previous mapping, except for CIs + if ( If_ObjIsAnd(pObj) ) + If_ObjCutBest(pObj)->nLeaves = 0; + } + + // update all values iteratively + fConverged = 0; + for ( c = 1; c <= p->nMaxIters; c++ ) + { + if ( !If_ManPerformMappingRoundSeq( p, c ) ) + { + p->RequiredGlo = If_ManDelayMax( p, 1 ); + fConverged = 1; + break; + } + p->RequiredGlo = If_ManDelayMax( p, 1 ); +//printf( "Global = %d \n", (int)p->RequiredGlo ); + if ( p->RequiredGlo > p->Period + p->fEpsilon ) + break; + } + + // report the results + if ( p->pPars->fVerbose ) + { + p->AreaGlo = If_ManScanMapping(p); + printf( "Attempt = %2d. Iters = %3d. Area = %10.2f. Fi = %6.2f. ", p->nAttempts, c, p->AreaGlo, (float)p->Period ); + if ( fConverged ) + printf( " Feasible" ); + else if ( c > p->nMaxIters ) + printf( "Infeasible (timeout)" ); + else + printf( "Infeasible" ); + printf( "\n" ); + } + return fConverged; +} + + +/**Function************************************************************* + + Synopsis [Performs binary search for the optimal clock period.] + + Description [Assumes that FiMin is infeasible while FiMax is feasible.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_ManBinarySearch_rec( If_Man_t * p, int FiMin, int FiMax ) +{ + assert( FiMin < FiMax ); + if ( FiMin + 1 == FiMax ) + return FiMax; + // compute the median + p->Period = FiMin + (FiMax - FiMin)/2; + if ( If_ManBinarySearchPeriod( p ) ) + return If_ManBinarySearch_rec( p, FiMin, p->Period ); // Median is feasible + else + return If_ManBinarySearch_rec( p, p->Period, FiMax ); // Median is infeasible +} + +/**Function************************************************************* + + Synopsis [Performs sequential mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManPerformMappingSeqPost( If_Man_t * p ) +{ + If_Obj_t * pObjLi, * pObjLo, * pObj; + int i; + + // link the latch outputs (CIs) directly to the drivers of latch inputs (COs) + for ( i = 0; i < p->pPars->nLatches; i++ ) + { + pObjLi = If_ManLi( p, i ); + pObjLo = If_ManLo( p, i ); +// printf( "%3d : %2d -> %2d \n", i, +// (int)If_ObjLValue(If_ObjFanin0(pObjLo)), (int)If_ObjLValue(pObjLo) ); + } + + // set arrival times + assert( p->pPars->pTimesArr != NULL ); + If_ManForEachLatchOutput( p, pObjLo, i ) + p->pPars->pTimesArr[i] = If_ObjLValue(pObjLo); + + // set the required times + assert( p->pPars->pTimesReq == NULL ); + p->pPars->pTimesReq = ALLOC( float, If_ManCoNum(p) ); + If_ManForEachPo( p, pObj, i ) + { + p->pPars->pTimesReq[i] = p->RequiredGlo2; +// printf( "Out %3d : %2d \n", i, (int)p->pPars->pTimesReq[i] ); + } + If_ManForEachLatchInput( p, pObjLi, i ) + { + p->pPars->pTimesReq[i] = If_ObjLValue(If_ObjFanin0(pObjLi)); +// printf( "Out %3d : %2d \n", i, (int)p->pPars->pTimesReq[i] ); + } + + // undo previous mapping + If_ManForEachObj( p, pObj, i ) + if ( If_ObjIsAnd(pObj) ) + If_ObjCutBest(pObj)->nLeaves = 0; + + // map again combinationally + p->pPars->fSeqMap = 0; + If_ManPerformMappingComb( p ); + p->pPars->fSeqMap = 1; +} + +/**Function************************************************************* + + Synopsis [Performs sequential mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_ManPerformMappingSeq( If_Man_t * p ) +{ + int clkTotal = clock(); + int PeriodBest; + + p->SortMode = 0; + + // perform combinational mapping to get the upper bound on the clock period + If_ManPerformMappingRound( p, 1, 0, 0, NULL ); + p->RequiredGlo = If_ManDelayMax( p, 0 ); + p->RequiredGlo2 = p->RequiredGlo; + + // set direct linking of latches with their inputs + If_ManPrepareMappingSeq( p ); + + // collect latches + p->vLatchOrder = If_ManCollectLatches( p ); + + // set parameters + p->nCutsUsed = p->pPars->nCutsMax; + p->nAttempts = 0; + p->nMaxIters = 50; + p->Period = (int)p->RequiredGlo; + + // make sure the clock period works + if ( !If_ManBinarySearchPeriod( p ) ) + { + printf( "If_ManPerformMappingSeq(): The upper bound on the clock period cannot be computed.\n" ); + return 0; + } + + // perform binary search + PeriodBest = If_ManBinarySearch_rec( p, 0, p->Period ); + + // recompute the best l-values + if ( p->Period != PeriodBest ) + { + p->Period = PeriodBest; + if ( !If_ManBinarySearchPeriod( p ) ) + { + printf( "If_ManPerformMappingSeq(): The final clock period cannot be confirmed.\n" ); + return 0; + } + } + if ( p->pPars->fVerbose ) + { +/* + { + FILE * pTable; + pTable = fopen( "iscas/stats_new.txt", "a+" ); +// fprintf( pTable, "%s ", pNtk->pName ); + fprintf( pTable, "%d ", p->Period ); + // fprintf( pTable, "%.2f ", (float)(s_MappingMem)/(float)(1<<20) ); +// fprintf( pTable, "%.2f", (float)(s_MappingTime)/(float)(CLOCKS_PER_SEC) ); +// fprintf( pTable, "\n" ); + fclose( pTable ); + } +*/ + printf( "The best clock period is %3d. ", p->Period ); + PRT( "Sequential time", clock() - clkTotal ); + } + p->RequiredGlo = (float)PeriodBest; + + // postprocess it using combinational mapping + If_ManPerformMappingSeqPost( p ); + s_MappingTime = clock() - clkTotal; + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/if/ifTime.c b/abc_with_bb_support/src/map/if/ifTime.c new file mode 100644 index 000000000..15840c69c --- /dev/null +++ b/abc_with_bb_support/src/map/if/ifTime.c @@ -0,0 +1,221 @@ +/**CFile**************************************************************** + + FileName [ifTime.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [FPGA mapping based on priority cuts.] + + Synopsis [Computation of delay paramters depending on the library.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - November 21, 2006.] + + Revision [$Id: ifTime.c,v 1.00 2006/11/21 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "if.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void If_CutSortInputPins( If_Man_t * p, If_Cut_t * pCut, int * pPinPerm, float * pPinDelays ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes delay.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float If_CutDelay( If_Man_t * p, If_Cut_t * pCut ) +{ + static int pPinPerm[IF_MAX_LUTSIZE]; + static float pPinDelays[IF_MAX_LUTSIZE]; + If_Obj_t * pLeaf; + float Delay, DelayCur; + float * pLutDelays; + int i, Shift; + assert( p->pPars->fSeqMap || pCut->nLeaves > 1 ); + Delay = -IF_FLOAT_LARGE; + if ( p->pPars->pLutLib ) + { + assert( !p->pPars->fLiftLeaves ); + pLutDelays = p->pPars->pLutLib->pLutDelays[pCut->nLeaves]; + if ( p->pPars->pLutLib->fVarPinDelays ) + { + // compute the delay using sorted pins + If_CutSortInputPins( p, pCut, pPinPerm, pPinDelays ); + for ( i = 0; i < (int)pCut->nLeaves; i++ ) + { + DelayCur = pPinDelays[pPinPerm[i]] + pLutDelays[i]; + Delay = IF_MAX( Delay, DelayCur ); + } + } + else + { + If_CutForEachLeaf( p, pCut, pLeaf, i ) + { + DelayCur = If_ObjCutBest(pLeaf)->Delay + pLutDelays[0]; + Delay = IF_MAX( Delay, DelayCur ); + } + } + } + else + { + if ( pCut->fUser ) + { + assert( !p->pPars->fLiftLeaves ); + If_CutForEachLeaf( p, pCut, pLeaf, i ) + { + DelayCur = If_ObjCutBest(pLeaf)->Delay + (float)pCut->pPerm[i]; + Delay = IF_MAX( Delay, DelayCur ); + } + } + else + { + if ( p->pPars->fLiftLeaves ) + { + If_CutForEachLeafSeq( p, pCut, pLeaf, Shift, i ) + { + DelayCur = If_ObjCutBest(pLeaf)->Delay - Shift * p->Period; + Delay = IF_MAX( Delay, DelayCur ); + } + } + else + { + If_CutForEachLeaf( p, pCut, pLeaf, i ) + { + DelayCur = If_ObjCutBest(pLeaf)->Delay; + Delay = IF_MAX( Delay, DelayCur ); + } + } + Delay += 1.0; + } + } + return Delay; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_CutPropagateRequired( If_Man_t * p, If_Cut_t * pCut, float ObjRequired ) +{ + static int pPinPerm[IF_MAX_LUTSIZE]; + static float pPinDelays[IF_MAX_LUTSIZE]; + If_Obj_t * pLeaf; + float * pLutDelays; + float Required; + int i; + assert( !p->pPars->fLiftLeaves ); + // compute the pins + if ( p->pPars->pLutLib ) + { + pLutDelays = p->pPars->pLutLib->pLutDelays[pCut->nLeaves]; + if ( p->pPars->pLutLib->fVarPinDelays ) + { + // compute the delay using sorted pins + If_CutSortInputPins( p, pCut, pPinPerm, pPinDelays ); + for ( i = 0; i < (int)pCut->nLeaves; i++ ) + { + Required = ObjRequired - pLutDelays[i]; + pLeaf = If_ManObj( p, pCut->pLeaves[pPinPerm[i]] ); + pLeaf->Required = IF_MIN( pLeaf->Required, Required ); + } + } + else + { + Required = ObjRequired - pLutDelays[0]; + If_CutForEachLeaf( p, pCut, pLeaf, i ) + pLeaf->Required = IF_MIN( pLeaf->Required, Required ); + } + } + else + { + if ( pCut->fUser ) + { + If_CutForEachLeaf( p, pCut, pLeaf, i ) + { + Required = ObjRequired - (float)pCut->pPerm[i]; + pLeaf->Required = IF_MIN( pLeaf->Required, Required ); + } + } + else + { + Required = ObjRequired - (float)1.0; + If_CutForEachLeaf( p, pCut, pLeaf, i ) + pLeaf->Required = IF_MIN( pLeaf->Required, Required ); + } + } +} + +/**Function************************************************************* + + Synopsis [Sorts the pins in the degreasing order of delays.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_CutSortInputPins( If_Man_t * p, If_Cut_t * pCut, int * pPinPerm, float * pPinDelays ) +{ + If_Obj_t * pLeaf; + int i, j, best_i, temp; + // start the trivial permutation and collect pin delays + If_CutForEachLeaf( p, pCut, pLeaf, i ) + { + pPinPerm[i] = i; + pPinDelays[i] = If_ObjCutBest(pLeaf)->Delay; + } + // selection sort the pins in the decreasible order of delays + // this order will match the increasing order of LUT input pins + for ( i = 0; i < (int)pCut->nLeaves-1; i++ ) + { + best_i = i; + for ( j = i+1; j < (int)pCut->nLeaves; j++ ) + if ( pPinDelays[pPinPerm[j]] > pPinDelays[pPinPerm[best_i]] ) + best_i = j; + if ( best_i == i ) + continue; + temp = pPinPerm[i]; + pPinPerm[i] = pPinPerm[best_i]; + pPinPerm[best_i] = temp; + } + // verify + assert( pPinPerm[0] < (int)pCut->nLeaves ); + for ( i = 1; i < (int)pCut->nLeaves; i++ ) + { + assert( pPinPerm[i] < (int)pCut->nLeaves ); + assert( pPinDelays[pPinPerm[i-1]] >= pPinDelays[pPinPerm[i]] ); + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/if/ifTruth.c b/abc_with_bb_support/src/map/if/ifTruth.c new file mode 100644 index 000000000..5242814e4 --- /dev/null +++ b/abc_with_bb_support/src/map/if/ifTruth.c @@ -0,0 +1,102 @@ +/**CFile**************************************************************** + + FileName [ifTruth.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [FPGA mapping based on priority cuts.] + + Synopsis [Computation of truth tables of the cuts.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - November 21, 2006.] + + Revision [$Id: ifTruth.c,v 1.00 2006/11/21 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "if.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes the stretching phase of the cut w.r.t. the merged cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Cut_TruthPhase( If_Cut_t * pCut, If_Cut_t * pCut1 ) +{ + unsigned uPhase = 0; + int i, k; + for ( i = k = 0; i < (int)pCut->nLeaves; i++ ) + { + if ( k == (int)pCut1->nLeaves ) + break; + if ( pCut->pLeaves[i] < pCut1->pLeaves[k] ) + continue; + assert( pCut->pLeaves[i] == pCut1->pLeaves[k] ); + uPhase |= (1 << i); + k++; + } + return uPhase; +} + +/**Function************************************************************* + + Synopsis [Performs truth table computation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_CutComputeTruth( If_Man_t * p, If_Cut_t * pCut, If_Cut_t * pCut0, If_Cut_t * pCut1, int fCompl0, int fCompl1 ) +{ + extern void Kit_FactorTest( unsigned * pTruth, int nVars ); + + // permute the first table + if ( fCompl0 ^ pCut0->fCompl ) + Extra_TruthNot( p->puTemp[0], If_CutTruth(pCut0), pCut->nLimit ); + else + Extra_TruthCopy( p->puTemp[0], If_CutTruth(pCut0), pCut->nLimit ); + Extra_TruthStretch( p->puTemp[2], p->puTemp[0], pCut0->nLeaves, pCut->nLimit, Cut_TruthPhase(pCut, pCut0) ); + // permute the second table + if ( fCompl1 ^ pCut1->fCompl ) + Extra_TruthNot( p->puTemp[1], If_CutTruth(pCut1), pCut->nLimit ); + else + Extra_TruthCopy( p->puTemp[1], If_CutTruth(pCut1), pCut->nLimit ); + Extra_TruthStretch( p->puTemp[3], p->puTemp[1], pCut1->nLeaves, pCut->nLimit, Cut_TruthPhase(pCut, pCut1) ); + // produce the resulting table + assert( pCut->fCompl == 0 ); + if ( pCut->fCompl ) + Extra_TruthNand( If_CutTruth(pCut), p->puTemp[2], p->puTemp[3], pCut->nLimit ); + else + Extra_TruthAnd( If_CutTruth(pCut), p->puTemp[2], p->puTemp[3], pCut->nLimit ); + + // perform +// Kit_FactorTest( If_CutTruth(pCut), pCut->nLimit ); +// printf( "%d ", If_CutLeaveNum(pCut) - Kit_TruthSupportSize(If_CutTruth(pCut), If_CutLeaveNum(pCut)) ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/if/ifUtil.c b/abc_with_bb_support/src/map/if/ifUtil.c new file mode 100644 index 000000000..a64707003 --- /dev/null +++ b/abc_with_bb_support/src/map/if/ifUtil.c @@ -0,0 +1,454 @@ +/**CFile**************************************************************** + + FileName [ifUtil.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [FPGA mapping based on priority cuts.] + + Synopsis [Various utilities.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - November 21, 2006.] + + Revision [$Id: ifUtil.c,v 1.00 2006/11/21 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "if.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Sets all the node copy to NULL.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManCleanNodeCopy( If_Man_t * p ) +{ + If_Obj_t * pObj; + int i; + If_ManForEachObj( p, pObj, i ) + If_ObjSetCopy( pObj, NULL ); +} + +/**Function************************************************************* + + Synopsis [Sets all the cut data to NULL.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManCleanCutData( If_Man_t * p ) +{ + If_Obj_t * pObj; + int i; + If_ManForEachObj( p, pObj, i ) + If_CutSetData( If_ObjCutBest(pObj), NULL ); +} + +/**Function************************************************************* + + Synopsis [Sets all visited marks to 0.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManCleanMarkV( If_Man_t * p ) +{ + If_Obj_t * pObj; + int i; + If_ManForEachObj( p, pObj, i ) + pObj->fVisit = 0; +} + +/**Function************************************************************* + + Synopsis [Returns the max delay of the POs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float If_ManDelayMax( If_Man_t * p, int fSeq ) +{ + If_Obj_t * pObj; + float DelayBest; + int i; + if ( p->pPars->fLatchPaths && p->pPars->nLatches == 0 ) + { + printf( "Delay optimization of latch path is not performed because there is no latches.\n" ); + p->pPars->fLatchPaths = 0; + } + DelayBest = -IF_FLOAT_LARGE; + if ( fSeq ) + { + assert( p->pPars->nLatches > 0 ); + If_ManForEachPo( p, pObj, i ) + if ( DelayBest < If_ObjArrTime(If_ObjFanin0(pObj)) ) + DelayBest = If_ObjArrTime(If_ObjFanin0(pObj)); + } + else if ( p->pPars->fLatchPaths ) + { + If_ManForEachLatchInput( p, pObj, i ) + if ( DelayBest < If_ObjArrTime(If_ObjFanin0(pObj)) ) + DelayBest = If_ObjArrTime(If_ObjFanin0(pObj)); + } + else + { + If_ManForEachCo( p, pObj, i ) + if ( DelayBest < If_ObjArrTime(If_ObjFanin0(pObj)) ) + DelayBest = If_ObjArrTime(If_ObjFanin0(pObj)); + } + return DelayBest; +} + +/**Function************************************************************* + + Synopsis [Computes the required times of all nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManComputeRequired( If_Man_t * p ) +{ + If_Obj_t * pObj; + int i, Counter; + + // compute area, clean required times, collect nodes used in the mapping + p->nNets = 0; + p->AreaGlo = If_ManScanMapping( p ); + + // consider the case when the required times are given + if ( p->pPars->pTimesReq ) + { + assert( !p->pPars->fAreaOnly ); + // make sure that the required time hold + Counter = 0; + If_ManForEachCo( p, pObj, i ) + { + if ( If_ObjArrTime(If_ObjFanin0(pObj)) > p->pPars->pTimesReq[i] + p->fEpsilon ) + { + Counter++; +// printf( "Required times are violated for output %d (arr = %d; req = %d).\n", +// i, (int)If_ObjArrTime(If_ObjFanin0(pObj)), (int)p->pPars->pTimesReq[i] ); + } + If_ObjFanin0(pObj)->Required = p->pPars->pTimesReq[i]; + } + if ( Counter ) + printf( "Required times are violated for %d outputs.\n", Counter ); + } + else + { + // get the global required times + p->RequiredGlo = If_ManDelayMax( p, 0 ); + // update the required times according to the target + if ( p->pPars->DelayTarget != -1 ) + { + if ( p->RequiredGlo > p->pPars->DelayTarget + p->fEpsilon ) + { + if ( p->fNextRound == 0 ) + { + p->fNextRound = 1; + printf( "Cannot meet the target required times (%4.2f). Mapping continues anyway.\n", p->pPars->DelayTarget ); + } + } + else if ( p->RequiredGlo < p->pPars->DelayTarget - p->fEpsilon ) + { + if ( p->fNextRound == 0 ) + { + p->fNextRound = 1; + printf( "Relaxing the required times from (%4.2f) to the target (%4.2f).\n", p->RequiredGlo, p->pPars->DelayTarget ); + } + p->RequiredGlo = p->pPars->DelayTarget; + } + } + // do not propagate required times if area minimization is requested + if ( p->pPars->fAreaOnly ) + return; + // set the required times for the POs + if ( p->pPars->fLatchPaths ) + { + If_ManForEachLatchInput( p, pObj, i ) + If_ObjFanin0(pObj)->Required = p->RequiredGlo; + } + else + { + If_ManForEachCo( p, pObj, i ) + If_ObjFanin0(pObj)->Required = p->RequiredGlo; + } + } + // go through the nodes in the reverse topological order + Vec_PtrForEachEntry( p->vMapped, pObj, i ) + If_CutPropagateRequired( p, If_ObjCutBest(pObj), pObj->Required ); +} + +/**Function************************************************************* + + Synopsis [Computes area, references, and nodes used in the mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float If_ManScanMapping_rec( If_Man_t * p, If_Obj_t * pObj, If_Obj_t ** ppStore ) +{ + If_Obj_t * pLeaf; + If_Cut_t * pCutBest; + float aArea; + int i; + if ( pObj->nRefs++ || If_ObjIsCi(pObj) || If_ObjIsConst1(pObj) ) + return 0.0; + // store the node in the structure by level + assert( If_ObjIsAnd(pObj) ); + pObj->pCopy = (char *)ppStore[pObj->Level]; + ppStore[pObj->Level] = pObj; + // visit the transitive fanin of the selected cut + pCutBest = If_ObjCutBest(pObj); + p->nNets += pCutBest->nLeaves; + aArea = If_CutLutArea( p, pCutBest ); + If_CutForEachLeaf( p, pCutBest, pLeaf, i ) + aArea += If_ManScanMapping_rec( p, pLeaf, ppStore ); + return aArea; +} + +/**Function************************************************************* + + Synopsis [Computes area, references, and nodes used in the mapping.] + + Description [Collects the nodes in reverse topological order in array + p->vMapping.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float If_ManScanMapping( If_Man_t * p ) +{ + If_Obj_t * pObj, ** ppStore; + float aArea; + int i; + assert( !p->pPars->fLiftLeaves ); + // clean all references + If_ManForEachObj( p, pObj, i ) + { + pObj->Required = IF_FLOAT_LARGE; + pObj->nVisits = pObj->nVisitsCopy; + pObj->nRefs = 0; + } + // allocate place to store the nodes + ppStore = ALLOC( If_Obj_t *, p->nLevelMax + 1 ); + memset( ppStore, 0, sizeof(If_Obj_t *) * (p->nLevelMax + 1) ); + // collect nodes reachable from POs in the DFS order through the best cuts + aArea = 0; + If_ManForEachCo( p, pObj, i ) + aArea += If_ManScanMapping_rec( p, If_ObjFanin0(pObj), ppStore ); + // reconnect the nodes in reverse topological order + Vec_PtrClear( p->vMapped ); + for ( i = p->nLevelMax; i >= 0; i-- ) + for ( pObj = ppStore[i]; pObj; pObj = pObj->pCopy ) + Vec_PtrPush( p->vMapped, pObj ); + free( ppStore ); + return aArea; +} + +/**Function************************************************************* + + Synopsis [Computes area, references, and nodes used in the mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float If_ManScanMappingSeq_rec( If_Man_t * p, If_Obj_t * pObj, Vec_Ptr_t * vMapped ) +{ + If_Obj_t * pLeaf; + If_Cut_t * pCutBest; + float aArea; + int i, Shift; + // treat latches transparently + if ( If_ObjIsLatch(pObj) ) + return If_ManScanMappingSeq_rec( p, If_ObjFanin0(pObj), vMapped ); + // consider trivial cases + if ( pObj->nRefs++ || If_ObjIsPi(pObj) || If_ObjIsConst1(pObj) ) + return 0.0; + // store the node in the structure by level + assert( If_ObjIsAnd(pObj) ); + // visit the transitive fanin of the selected cut + pCutBest = If_ObjCutBest(pObj); + aArea = If_ObjIsAnd(pObj)? If_CutLutArea(p, pCutBest) : (float)0.0; + If_CutForEachLeafSeq( p, pCutBest, pLeaf, Shift, i ) + aArea += If_ManScanMappingSeq_rec( p, pLeaf, vMapped ); + // add the node + Vec_PtrPush( vMapped, pObj ); + return aArea; +} + +/**Function************************************************************* + + Synopsis [Computes area, references, and nodes used in the mapping.] + + Description [Collects the nodes in reverse topological order in array + p->vMapping.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float If_ManScanMappingSeq( If_Man_t * p ) +{ + If_Obj_t * pObj; + float aArea; + int i; + assert( p->pPars->fLiftLeaves ); + // clean all references + If_ManForEachObj( p, pObj, i ) + pObj->nRefs = 0; + // collect nodes reachable from POs in the DFS order through the best cuts + aArea = 0; + Vec_PtrClear( p->vMapped ); + If_ManForEachPo( p, pObj, i ) + aArea += If_ManScanMappingSeq_rec( p, If_ObjFanin0(pObj), p->vMapped ); + return aArea; +} + +/**Function************************************************************* + + Synopsis [Computes area, references, and nodes used in the mapping.] + + Description [Collects the nodes in reverse topological order in array + p->vMapping.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void If_ManResetOriginalRefs( If_Man_t * p ) +{ + If_Obj_t * pObj; + int i; + If_ManForEachObj( p, pObj, i ) + pObj->nRefs = 0; + If_ManForEachObj( p, pObj, i ) + { + if ( If_ObjIsAnd(pObj) ) + { + pObj->pFanin0->nRefs++; + pObj->pFanin1->nRefs++; + } + else if ( If_ObjIsCo(pObj) ) + pObj->pFanin0->nRefs++; + } +} + +/**Function************************************************************* + + Synopsis [Computes cross-cut of the circuit.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_ManCrossCut( If_Man_t * p ) +{ + If_Obj_t * pObj, * pFanin; + int i, nCutSize = 0, nCutSizeMax = 0; + If_ManForEachObj( p, pObj, i ) + { + if ( !If_ObjIsAnd(pObj) ) + continue; + // consider the node + if ( nCutSizeMax < ++nCutSize ) + nCutSizeMax = nCutSize; + if ( pObj->nVisits == 0 ) + nCutSize--; + // consider the fanins + pFanin = If_ObjFanin0(pObj); + if ( !If_ObjIsCi(pFanin) && --pFanin->nVisits == 0 ) + nCutSize--; + pFanin = If_ObjFanin1(pObj); + if ( !If_ObjIsCi(pFanin) && --pFanin->nVisits == 0 ) + nCutSize--; + // consider the choice class + if ( pObj->fRepr ) + for ( pFanin = pObj; pFanin; pFanin = pFanin->pEquiv ) + if ( !If_ObjIsCi(pFanin) && --pFanin->nVisits == 0 ) + nCutSize--; + } + If_ManForEachObj( p, pObj, i ) + { + assert( If_ObjIsCi(pObj) || pObj->fVisit == 0 ); + pObj->nVisits = pObj->nVisitsCopy; + } + assert( nCutSize == 0 ); +// printf( "Max cross cut size = %6d.\n", nCutSizeMax ); + return nCutSizeMax; +} + +/**Function************************************************************* + + Synopsis [Computes cross-cut of the circuit.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_ManCountTrueArea( If_Man_t * p ) +{ + If_Obj_t * pObj; + int i, Area = 0; + Vec_PtrForEachEntry( p->vMapped, pObj, i ) + Area += 1 + (If_ObjCutBest(pObj)->nLeaves > (unsigned)p->pPars->nLutSize / 2); + return Area; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/if/if_.c b/abc_with_bb_support/src/map/if/if_.c new file mode 100644 index 000000000..59bd46ceb --- /dev/null +++ b/abc_with_bb_support/src/map/if/if_.c @@ -0,0 +1,47 @@ +/**CFile**************************************************************** + + FileName [if_.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [FPGA mapping based on priority cuts.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - November 21, 2006.] + + Revision [$Id: if_.c,v 1.00 2006/11/21 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "if.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/if/module.make b/abc_with_bb_support/src/map/if/module.make new file mode 100644 index 000000000..aa295ada8 --- /dev/null +++ b/abc_with_bb_support/src/map/if/module.make @@ -0,0 +1,9 @@ +SRC += src/map/if/ifCore.c \ + src/map/if/ifCut.c \ + src/map/if/ifMan.c \ + src/map/if/ifMap.c \ + src/map/if/ifReduce.c \ + src/map/if/ifSeq.c \ + src/map/if/ifTime.c \ + src/map/if/ifTruth.c \ + src/map/if/ifUtil.c diff --git a/abc_with_bb_support/src/map/mapper/mapper.c b/abc_with_bb_support/src/map/mapper/mapper.c new file mode 100644 index 000000000..f77d04a43 --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/mapper.c @@ -0,0 +1,176 @@ +/**CFile**************************************************************** + + FileName [mapper.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Command file for the mapper package.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - June 1, 2004.] + + Revision [$Id: mapper.c,v 1.7 2005/01/23 06:59:42 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "mainInt.h" +#include "mio.h" +#include "mapperInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Map_CommandReadLibrary ( Abc_Frame_t * pAbc, int argc, char **argv ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_Init( Abc_Frame_t * pAbc ) +{ + Cmd_CommandAdd( pAbc, "SC mapping", "read_super", Map_CommandReadLibrary, 0 ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_End() +{ +// Map_SuperLibFree( s_pSuperLib ); + Map_SuperLibFree( Abc_FrameReadLibSuper() ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_CommandReadLibrary( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + FILE * pFile; + FILE * pOut, * pErr; + Map_SuperLib_t * pLib; + Abc_Ntk_t * pNet; + char * FileName, * ExcludeFile; + int fVerbose; + int fAlgorithm; + int c; + + pNet = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set the defaults + fVerbose = 1; + fAlgorithm = 1; + ExcludeFile = 0; + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "eovh")) != EOF ) + { + switch (c) + { + case 'e': + ExcludeFile = argv[globalUtilOptind]; + if ( ExcludeFile == 0 ) + goto usage; + globalUtilOptind++; + break; + case 'o': + fAlgorithm ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + break; + default: + goto usage; + } + } + + + if ( argc != globalUtilOptind + 1 ) + { + goto usage; + } + + // get the input file name + FileName = argv[globalUtilOptind]; + if ( (pFile = Io_FileOpen( FileName, "open_path", "r", 0 )) == NULL ) +// if ( (pFile = fopen( FileName, "r" )) == NULL ) + { + fprintf( pErr, "Cannot open input file \"%s\". ", FileName ); + if ( FileName = Extra_FileGetSimilarName( FileName, ".genlib", ".lib", ".gen", ".g", NULL ) ) + fprintf( pErr, "Did you mean \"%s\"?", FileName ); + fprintf( pErr, "\n" ); + return 1; + } + fclose( pFile ); + + // set the new network + pLib = Map_SuperLibCreate( FileName, ExcludeFile, fAlgorithm, fVerbose ); + if ( pLib == NULL ) + { + fprintf( pErr, "Reading supergate library has failed.\n" ); + goto usage; + } + // replace the current library +// Map_SuperLibFree( s_pSuperLib ); +// s_pSuperLib = pLib; + Map_SuperLibFree( Abc_FrameReadLibSuper() ); + Abc_FrameSetLibSuper( pLib ); + // replace the current genlib library +// if ( s_pLib ) Mio_LibraryDelete( s_pLib ); +// s_pLib = s_pSuperLib->pGenlib; + Mio_LibraryDelete( Abc_FrameReadLibGen() ); + Abc_FrameSetLibGen( pLib->pGenlib ); + return 0; + +usage: + fprintf( pErr, "\nusage: read_super [-ovh]\n"); + fprintf( pErr, "\t read the supergate library from the file\n" ); + fprintf( pErr, "\t-e file : file contains list of genlib gates to exclude\n" ); + fprintf( pErr, "\t-o : toggles the use of old file format [default = %s]\n", (fAlgorithm? "new" : "old") ); + fprintf( pErr, "\t-v : toggles enabling of verbose output [default = %s]\n", (fVerbose? "yes" : "no") ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; /* error exit */ +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/mapper/mapper.h b/abc_with_bb_support/src/map/mapper/mapper.h new file mode 100644 index 000000000..f5ede4057 --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/mapper.h @@ -0,0 +1,195 @@ +/**CFile**************************************************************** + + FileName [mapper.h] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - June 1, 2004.] + + Revision [$Id: mapper.h,v 1.11 2005/02/28 05:34:26 alanmi Exp $] + +***********************************************************************/ + +#ifndef __MAPPER_H__ +#define __MAPPER_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Map_ManStruct_t_ Map_Man_t; +typedef struct Map_NodeStruct_t_ Map_Node_t; +typedef struct Map_NodeVecStruct_t_ Map_NodeVec_t; +typedef struct Map_CutStruct_t_ Map_Cut_t; +typedef struct Map_MatchStruct_t_ Map_Match_t; +typedef struct Map_SuperStruct_t_ Map_Super_t; +typedef struct Map_SuperLibStruct_t_ Map_SuperLib_t; +typedef struct Map_HashTableStruct_t_ Map_HashTable_t; +typedef struct Map_HashEntryStruct_t_ Map_HashEntry_t; +typedef struct Map_TimeStruct_t_ Map_Time_t; + +// the pair of rise/fall time parameters +struct Map_TimeStruct_t_ +{ + float Rise; + float Fall; + float Worst; +}; + +//////////////////////////////////////////////////////////////////////// +/// GLOBAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define Map_IsComplement(p) (((int)((unsigned long) (p) & 01))) +#define Map_Regular(p) ((Map_Node_t *)((unsigned long)(p) & ~01)) +#define Map_Not(p) ((Map_Node_t *)((unsigned long)(p) ^ 01)) +#define Map_NotCond(p,c) ((Map_Node_t *)((unsigned long)(p) ^ (c))) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== mapperCreate.c =============================================================*/ +extern Map_Man_t * Map_ManCreate( int nInputs, int nOutputs, int fVerbose ); +extern Map_Node_t * Map_NodeCreate( Map_Man_t * p, Map_Node_t * p1, Map_Node_t * p2 ); +extern void Map_ManFree( Map_Man_t * pMan ); +extern void Map_ManPrintTimeStats( Map_Man_t * p ); +extern void Map_ManPrintStatsToFile( char * pName, float Area, float Delay, int Time ); +extern int Map_ManReadInputNum( Map_Man_t * p ); +extern int Map_ManReadOutputNum( Map_Man_t * p ); +extern Map_Node_t ** Map_ManReadInputs ( Map_Man_t * p ); +extern Map_Node_t ** Map_ManReadOutputs( Map_Man_t * p ); +extern Map_Node_t * Map_ManReadConst1 ( Map_Man_t * p ); +extern Map_Time_t * Map_ManReadInputArrivals( Map_Man_t * p ); +extern Mio_Library_t * Map_ManReadGenLib ( Map_Man_t * p ); +extern bool Map_ManReadVerbose( Map_Man_t * p ); +extern float Map_ManReadAreaFinal( Map_Man_t * p ); +extern float Map_ManReadRequiredGlo( Map_Man_t * p ); +extern void Map_ManSetTimeToMap( Map_Man_t * p, int Time ); +extern void Map_ManSetTimeToNet( Map_Man_t * p, int Time ); +extern void Map_ManSetTimeSweep( Map_Man_t * p, int Time ); +extern void Map_ManSetTimeTotal( Map_Man_t * p, int Time ); +extern void Map_ManSetOutputNames( Map_Man_t * p, char ** ppNames ); +extern void Map_ManSetAreaRecovery( Map_Man_t * p, int fAreaRecovery ); +extern void Map_ManSetDelayTarget( Map_Man_t * p, float DelayTarget ); +extern void Map_ManSetInputArrivals( Map_Man_t * p, Map_Time_t * pArrivals ); +extern void Map_ManSetObeyFanoutLimits( Map_Man_t * p, bool fObeyFanoutLimits ); +extern void Map_ManSetNumIterations( Map_Man_t * p, int nNumIterations ); +extern int Map_ManReadPass( Map_Man_t * p ); +extern void Map_ManSetPass( Map_Man_t * p, int nPass ); +extern int Map_ManReadFanoutViolations( Map_Man_t * p ); +extern void Map_ManSetFanoutViolations( Map_Man_t * p, int nVio ); +extern void Map_ManSetChoiceNodeNum( Map_Man_t * p, int nChoiceNodes ); +extern void Map_ManSetChoiceNum( Map_Man_t * p, int nChoices ); +extern void Map_ManSetVerbose( Map_Man_t * p, int fVerbose ); +extern void Map_ManSetSwitching( Map_Man_t * p, int fSwitching ); + +extern Map_Man_t * Map_NodeReadMan( Map_Node_t * p ); +extern char * Map_NodeReadData( Map_Node_t * p, int fPhase ); +extern int Map_NodeReadNum( Map_Node_t * p ); +extern int Map_NodeReadLevel( Map_Node_t * p ); +extern Map_Cut_t * Map_NodeReadCuts( Map_Node_t * p ); +extern Map_Cut_t * Map_NodeReadCutBest( Map_Node_t * p, int fPhase ); +extern Map_Node_t * Map_NodeReadOne( Map_Node_t * p ); +extern Map_Node_t * Map_NodeReadTwo( Map_Node_t * p ); +extern void Map_NodeSetData( Map_Node_t * p, int fPhase, char * pData ); +extern void Map_NodeSetNextE( Map_Node_t * p, Map_Node_t * pNextE ); +extern void Map_NodeSetRepr( Map_Node_t * p, Map_Node_t * pRepr ); +extern void Map_NodeSetSwitching( Map_Node_t * p, float Switching ); + +extern int Map_NodeIsConst( Map_Node_t * p ); +extern int Map_NodeIsVar( Map_Node_t * p ); +extern int Map_NodeIsAnd( Map_Node_t * p ); +extern int Map_NodeComparePhase( Map_Node_t * p1, Map_Node_t * p2 ); + +extern Map_Super_t * Map_CutReadSuperBest( Map_Cut_t * p, int fPhase ); +extern Map_Super_t * Map_CutReadSuper0( Map_Cut_t * p ); +extern Map_Super_t * Map_CutReadSuper1( Map_Cut_t * p ); +extern int Map_CutReadLeavesNum( Map_Cut_t * p ); +extern Map_Node_t ** Map_CutReadLeaves( Map_Cut_t * p ); +extern unsigned Map_CutReadPhaseBest( Map_Cut_t * p, int fPhase ); +extern unsigned Map_CutReadPhase0( Map_Cut_t * p ); +extern unsigned Map_CutReadPhase1( Map_Cut_t * p ); +extern Map_Cut_t * Map_CutReadNext( Map_Cut_t * p ); + +extern char * Map_SuperReadFormula( Map_Super_t * p ); +extern Mio_Gate_t * Map_SuperReadRoot( Map_Super_t * p ); +extern int Map_SuperReadNum( Map_Super_t * p ); +extern Map_Super_t ** Map_SuperReadFanins( Map_Super_t * p ); +extern int Map_SuperReadFaninNum( Map_Super_t * p ); +extern Map_Super_t * Map_SuperReadNext( Map_Super_t * p ); +extern int Map_SuperReadNumPhases( Map_Super_t * p ); +extern unsigned char * Map_SuperReadPhases( Map_Super_t * p ); +extern int Map_SuperReadFanoutLimit( Map_Super_t * p ); + +extern Mio_Library_t * Map_SuperLibReadGenLib( Map_SuperLib_t * p ); +extern float Map_SuperLibReadAreaInv( Map_SuperLib_t * p ); +extern Map_Time_t Map_SuperLibReadDelayInv( Map_SuperLib_t * p ); +extern int Map_SuperLibReadVarsMax( Map_SuperLib_t * p ); + +extern Map_Node_t * Map_NodeAnd( Map_Man_t * p, Map_Node_t * p1, Map_Node_t * p2 ); +extern Map_Node_t * Map_NodeOr( Map_Man_t * p, Map_Node_t * p1, Map_Node_t * p2 ); +extern Map_Node_t * Map_NodeExor( Map_Man_t * p, Map_Node_t * p1, Map_Node_t * p2 ); +extern Map_Node_t * Map_NodeMux( Map_Man_t * p, Map_Node_t * pNode, Map_Node_t * pNodeT, Map_Node_t * pNodeE ); +extern void Map_NodeSetChoice( Map_Man_t * pMan, Map_Node_t * pNodeOld, Map_Node_t * pNodeNew ); + +/*=== resmCanon.c =============================================================*/ +extern int Map_CanonComputeSlow( unsigned uTruths[][2], int nVarsMax, int nVarsReal, unsigned uTruth[], unsigned char * puPhases, unsigned uTruthRes[] ); +extern int Map_CanonComputeFast( Map_Man_t * p, int nVarsMax, int nVarsReal, unsigned uTruth[], unsigned char * puPhases, unsigned uTruthRes[] ); +/*=== mapperCut.c =============================================================*/ +extern Map_Cut_t * Map_CutAlloc( Map_Man_t * p ); +/*=== mapperCutUtils.c =============================================================*/ +extern void Map_CutCreateFromNode( Map_Man_t * p, Map_Super_t * pSuper, int iRoot, unsigned uPhaseRoot, + int * pLeaves, int nLeaves, unsigned uPhaseLeaves ); +/*=== mapperCore.c =============================================================*/ +extern int Map_Mapping( Map_Man_t * p ); +/*=== mapperLib.c =============================================================*/ +extern int Map_SuperLibDeriveFromGenlib( Mio_Library_t * pLib ); +/*=== mapperMntk.c =============================================================*/ +//extern Mntk_Man_t * Map_ConvertMappingToMntk( Map_Man_t * pMan ); +/*=== mapperSuper.c =============================================================*/ +extern char * Map_LibraryReadFormulaStep( char * pFormula, char * pStrings[], int * pnStrings ); +/*=== mapperSweep.c =============================================================*/ +extern void Map_NetworkSweep( Abc_Ntk_t * pNet ); +/*=== mapperTable.c =============================================================*/ +extern Map_Super_t * Map_SuperTableLookupC( Map_SuperLib_t * pLib, unsigned uTruth[] ); +/*=== mapperTime.c =============================================================*/ +/*=== mapperUtil.c =============================================================*/ +extern int Map_ManCheckConsistency( Map_Man_t * p ); +extern st_table * Map_CreateTableGate2Super( Map_Man_t * p ); +extern void Map_ManCleanData( Map_Man_t * p ); +extern void Map_MappingSetupTruthTables( unsigned uTruths[][2] ); +extern void Map_MappingSetupTruthTablesLarge( unsigned uTruths[][32] ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/map/mapper/mapperCanon.c b/abc_with_bb_support/src/map/mapper/mapperCanon.c new file mode 100644 index 000000000..79ac27924 --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/mapperCanon.c @@ -0,0 +1,271 @@ +/**CFile**************************************************************** + + FileName [mapperCanon.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - June 1, 2004.] + + Revision [$Id: mapperCanon.c,v 1.2 2005/01/23 06:59:42 alanmi Exp $] + +***********************************************************************/ + +#include "mapperInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static unsigned Map_CanonComputePhase( unsigned uTruths[][2], int nVars, unsigned uTruth, unsigned uPhase ); +static void Map_CanonComputePhase6( unsigned uTruths[][2], int nVars, unsigned uTruth[], unsigned uPhase, unsigned uTruthRes[] ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes the N-canonical form of the Boolean function.] + + Description [The N-canonical form is defined as the truth table with + the minimum integer value. This function exhaustively enumerates + through the complete set of 2^N phase assignments.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_CanonComputeSlow( unsigned uTruths[][2], int nVarsMax, int nVarsReal, unsigned uTruth[], unsigned char * puPhases, unsigned uTruthRes[] ) +{ + unsigned uTruthPerm[2]; + int nMints, nPhases, m; + + nPhases = 0; + nMints = (1 << nVarsReal); + if ( nVarsMax < 6 ) + { + uTruthRes[0] = MAP_MASK(32); + for ( m = 0; m < nMints; m++ ) + { + uTruthPerm[0] = Map_CanonComputePhase( uTruths, nVarsMax, uTruth[0], m ); + if ( uTruthRes[0] > uTruthPerm[0] ) + { + uTruthRes[0] = uTruthPerm[0]; + nPhases = 0; + puPhases[nPhases++] = (unsigned char)m; + } + else if ( uTruthRes[0] == uTruthPerm[0] ) + { + if ( nPhases < 4 ) // the max number of phases in Map_Super_t + puPhases[nPhases++] = (unsigned char)m; + } + } + uTruthRes[1] = uTruthRes[0]; + } + else + { + uTruthRes[0] = MAP_MASK(32); + uTruthRes[1] = MAP_MASK(32); + for ( m = 0; m < nMints; m++ ) + { + Map_CanonComputePhase6( uTruths, nVarsMax, uTruth, m, uTruthPerm ); + if ( uTruthRes[1] > uTruthPerm[1] || uTruthRes[1] == uTruthPerm[1] && uTruthRes[0] > uTruthPerm[0] ) + { + uTruthRes[0] = uTruthPerm[0]; + uTruthRes[1] = uTruthPerm[1]; + nPhases = 0; + puPhases[nPhases++] = (unsigned char)m; + } + else if ( uTruthRes[1] == uTruthPerm[1] && uTruthRes[0] == uTruthPerm[0] ) + { + if ( nPhases < 4 ) // the max number of phases in Map_Super_t + puPhases[nPhases++] = (unsigned char)m; + } + } + } + assert( nPhases > 0 ); +// printf( "%d ", nPhases ); + return nPhases; +} + +/**Function************************************************************* + + Synopsis [Performs phase transformation for one function of less than 6 variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Map_CanonComputePhase( unsigned uTruths[][2], int nVars, unsigned uTruth, unsigned uPhase ) +{ + int v, Shift; + for ( v = 0, Shift = 1; v < nVars; v++, Shift <<= 1 ) + if ( uPhase & Shift ) + uTruth = (((uTruth & ~uTruths[v][0]) << Shift) | ((uTruth & uTruths[v][0]) >> Shift)); + return uTruth; +} + +/**Function************************************************************* + + Synopsis [Performs phase transformation for one function of 6 variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_CanonComputePhase6( unsigned uTruths[][2], int nVars, unsigned uTruth[], unsigned uPhase, unsigned uTruthRes[] ) +{ + unsigned uTemp; + int v, Shift; + + // initialize the result + uTruthRes[0] = uTruth[0]; + uTruthRes[1] = uTruth[1]; + if ( uPhase == 0 ) + return; + // compute the phase + for ( v = 0, Shift = 1; v < nVars; v++, Shift <<= 1 ) + if ( uPhase & Shift ) + { + if ( Shift < 32 ) + { + uTruthRes[0] = (((uTruthRes[0] & ~uTruths[v][0]) << Shift) | ((uTruthRes[0] & uTruths[v][0]) >> Shift)); + uTruthRes[1] = (((uTruthRes[1] & ~uTruths[v][1]) << Shift) | ((uTruthRes[1] & uTruths[v][1]) >> Shift)); + } + else + { + uTemp = uTruthRes[0]; + uTruthRes[0] = uTruthRes[1]; + uTruthRes[1] = uTemp; + } + } +} + +/**Function************************************************************* + + Synopsis [Computes the N-canonical form of the Boolean function.] + + Description [The N-canonical form is defined as the truth table with + the minimum integer value. This function exhaustively enumerates + through the complete set of 2^N phase assignments.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_CanonComputeFast( Map_Man_t * p, int nVarsMax, int nVarsReal, unsigned uTruth[], unsigned char * puPhases, unsigned uTruthRes[] ) +{ + unsigned uTruth0, uTruth1; + unsigned uCanon0, uCanon1, uCanonBest, uPhaseBest; + int i, Limit; + + if ( nVarsMax == 6 ) + return Map_CanonComputeSlow( p->uTruths, nVarsMax, nVarsReal, uTruth, puPhases, uTruthRes ); + + if ( nVarsReal < 5 ) + { +// return Map_CanonComputeSlow( p->uTruths, nVarsMax, nVarsReal, uTruth, puPhases, uTruthRes ); + + uTruth0 = uTruth[0] & 0xFFFF; + assert( p->pCounters[uTruth0] > 0 ); + uTruthRes[0] = (p->uCanons[uTruth0] << 16) | p->uCanons[uTruth0]; + uTruthRes[1] = uTruthRes[0]; + puPhases[0] = p->uPhases[uTruth0][0]; + return 1; + } + + assert( nVarsMax == 5 ); + assert( nVarsReal == 5 ); + uTruth0 = uTruth[0] & 0xFFFF; + uTruth1 = (uTruth[0] >> 16); + if ( uTruth1 == 0 ) + { + uTruthRes[0] = p->uCanons[uTruth0]; + uTruthRes[1] = uTruthRes[0]; + Limit = (p->pCounters[uTruth0] > 4)? 4 : p->pCounters[uTruth0]; + for ( i = 0; i < Limit; i++ ) + puPhases[i] = p->uPhases[uTruth0][i]; + return Limit; + } + else if ( uTruth0 == 0 ) + { + uTruthRes[0] = p->uCanons[uTruth1]; + uTruthRes[1] = uTruthRes[0]; + Limit = (p->pCounters[uTruth1] > 4)? 4 : p->pCounters[uTruth1]; + for ( i = 0; i < Limit; i++ ) + { + puPhases[i] = p->uPhases[uTruth1][i]; + puPhases[i] |= (1 << 4); + } + return Limit; + } + uCanon0 = p->uCanons[uTruth0]; + uCanon1 = p->uCanons[uTruth1]; + if ( uCanon0 >= uCanon1 ) // using nCanon1 as the main one + { + assert( p->pCounters[uTruth1] > 0 ); + uCanonBest = 0xFFFFFFFF; + for ( i = 0; i < p->pCounters[uTruth1]; i++ ) + { + uCanon0 = Extra_TruthPolarize( uTruth0, p->uPhases[uTruth1][i], 4 ); + if ( uCanonBest > uCanon0 ) + { + uCanonBest = uCanon0; + uPhaseBest = p->uPhases[uTruth1][i]; + assert( uPhaseBest < 16 ); + } + } + uTruthRes[0] = (uCanon1 << 16) | uCanonBest; + uTruthRes[1] = uTruthRes[0]; + puPhases[0] = uPhaseBest; + return 1; + } + else if ( uCanon0 < uCanon1 ) + { + assert( p->pCounters[uTruth0] > 0 ); + uCanonBest = 0xFFFFFFFF; + for ( i = 0; i < p->pCounters[uTruth0]; i++ ) + { + uCanon1 = Extra_TruthPolarize( uTruth1, p->uPhases[uTruth0][i], 4 ); + if ( uCanonBest > uCanon1 ) + { + uCanonBest = uCanon1; + uPhaseBest = p->uPhases[uTruth0][i]; + assert( uPhaseBest < 16 ); + } + } + uTruthRes[0] = (uCanon0 << 16) | uCanonBest; + uTruthRes[1] = uTruthRes[0]; + puPhases[0] = uPhaseBest | (1 << 4); + return 1; + } + else + { + assert( 0 ); + return Map_CanonComputeSlow( p->uTruths, nVarsMax, nVarsReal, uTruth, puPhases, uTruthRes ); + } +} + + + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/mapper/mapperCore.c b/abc_with_bb_support/src/map/mapper/mapperCore.c new file mode 100644 index 000000000..1b977c82d --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/mapperCore.c @@ -0,0 +1,228 @@ +/**CFile**************************************************************** + + FileName [mapperCore.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - June 1, 2004.] + + Revision [$Id: mapperCore.c,v 1.7 2004/10/01 23:41:04 satrajit Exp $] + +***********************************************************************/ + +#include "mapperInt.h" +//#include "resm.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs technology mapping for the given object graph.] + + Description [The object graph is stored in the mapping manager. + First, the AND nodes that fanout into POs are collected in the DFS order. + Two preprocessing steps are performed: the k-feasible cuts are computed + for each node and the truth tables are computed for each cut. Next, the + delay-optimal matches are assigned for each node, followed by several + iterations of area recoveryd: using area flow (global optimization) + and using exact area at a node (local optimization).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_Mapping( Map_Man_t * p ) +{ + int fShowSwitching = 1; + int fUseAreaFlow = 1; + int fUseExactArea = !p->fSwitching; + int fUseExactAreaWithPhase = !p->fSwitching; + int clk; + + ////////////////////////////////////////////////////////////////////// + // perform pre-mapping computations + // collect the nodes reachable from POs in the DFS order (including the choices) + p->vAnds = Map_MappingDfs( p, 1 ); + if ( p->fVerbose ) + Map_MappingReportChoices( p ); + Map_MappingSetChoiceLevels( p ); // should always be called before mapping! +// return 1; + + // compute the cuts of nodes in the DFS order + clk = clock(); + Map_MappingCuts( p ); + p->timeCuts = clock() - clk; + // derive the truth tables + clk = clock(); + Map_MappingTruths( p ); + p->timeTruth = clock() - clk; + ////////////////////////////////////////////////////////////////////// +//PRT( "Truths", clock() - clk ); + + ////////////////////////////////////////////////////////////////////// + // compute the minimum-delay mapping + clk = clock(); + p->fMappingMode = 0; + if ( !Map_MappingMatches( p ) ) + return 0; + p->timeMatch = clock() - clk; + // compute the references and collect the nodes used in the mapping + Map_MappingSetRefs( p ); + p->AreaBase = Map_MappingGetArea( p, p->vMapping ); +if ( p->fVerbose ) +{ +printf( "Delay : %s = %8.2f Flow = %11.1f Area = %11.1f %4.1f %% ", + fShowSwitching? "Switch" : "Delay", + fShowSwitching? Map_MappingGetSwitching(p,p->vMapping) : p->fRequiredGlo, + Map_MappingGetAreaFlow(p), p->AreaBase, 0.0 ); +PRT( "Time", p->timeMatch ); +} + ////////////////////////////////////////////////////////////////////// + + if ( !p->fAreaRecovery ) + { + if ( p->fVerbose ) + Map_MappingPrintOutputArrivals( p ); + return 1; + } + + ////////////////////////////////////////////////////////////////////// + // perform area recovery using area flow + clk = clock(); + if ( fUseAreaFlow ) + { + // compute the required times + Map_TimeComputeRequiredGlobal( p ); + // recover area flow + p->fMappingMode = 1; + Map_MappingMatches( p ); + // compute the references and collect the nodes used in the mapping + Map_MappingSetRefs( p ); + p->AreaFinal = Map_MappingGetArea( p, p->vMapping ); +if ( p->fVerbose ) +{ +printf( "AreaFlow : %s = %8.2f Flow = %11.1f Area = %11.1f %4.1f %% ", + fShowSwitching? "Switch" : "Delay", + fShowSwitching? Map_MappingGetSwitching(p,p->vMapping) : p->fRequiredGlo, + Map_MappingGetAreaFlow(p), p->AreaFinal, + 100.0*(p->AreaBase-p->AreaFinal)/p->AreaBase ); +PRT( "Time", clock() - clk ); +} + } + p->timeArea += clock() - clk; + ////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////// + // perform area recovery using exact area + clk = clock(); + if ( fUseExactArea ) + { + // compute the required times + Map_TimeComputeRequiredGlobal( p ); + // recover area + p->fMappingMode = 2; + Map_MappingMatches( p ); + // compute the references and collect the nodes used in the mapping + Map_MappingSetRefs( p ); + p->AreaFinal = Map_MappingGetArea( p, p->vMapping ); +if ( p->fVerbose ) +{ +printf( "Area : %s = %8.2f Flow = %11.1f Area = %11.1f %4.1f %% ", + fShowSwitching? "Switch" : "Delay", + fShowSwitching? Map_MappingGetSwitching(p,p->vMapping) : p->fRequiredGlo, + 0.0, p->AreaFinal, + 100.0*(p->AreaBase-p->AreaFinal)/p->AreaBase ); +PRT( "Time", clock() - clk ); +} + } + p->timeArea += clock() - clk; + ////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////// + // perform area recovery using exact area + clk = clock(); + if ( fUseExactAreaWithPhase ) + { + // compute the required times + Map_TimeComputeRequiredGlobal( p ); + // recover area + p->fMappingMode = 3; + Map_MappingMatches( p ); + // compute the references and collect the nodes used in the mapping + Map_MappingSetRefs( p ); + p->AreaFinal = Map_MappingGetArea( p, p->vMapping ); +if ( p->fVerbose ) +{ +printf( "Area : %s = %8.2f Flow = %11.1f Area = %11.1f %4.1f %% ", + fShowSwitching? "Switch" : "Delay", + fShowSwitching? Map_MappingGetSwitching(p,p->vMapping) : p->fRequiredGlo, + 0.0, p->AreaFinal, + 100.0*(p->AreaBase-p->AreaFinal)/p->AreaBase ); +PRT( "Time", clock() - clk ); +} + } + p->timeArea += clock() - clk; + ////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////// + // perform area recovery using exact area + clk = clock(); + if ( p->fSwitching ) + { + // compute the required times + Map_TimeComputeRequiredGlobal( p ); + // recover switching activity + p->fMappingMode = 4; + Map_MappingMatches( p ); + // compute the references and collect the nodes used in the mapping + Map_MappingSetRefs( p ); + p->AreaFinal = Map_MappingGetArea( p, p->vMapping ); +if ( p->fVerbose ) +{ +printf( "Switching: %s = %8.2f Flow = %11.1f Area = %11.1f %4.1f %% ", + fShowSwitching? "Switch" : "Delay", + fShowSwitching? Map_MappingGetSwitching(p,p->vMapping) : p->fRequiredGlo, + 0.0, p->AreaFinal, + 100.0*(p->AreaBase-p->AreaFinal)/p->AreaBase ); +PRT( "Time", clock() - clk ); +} + + // compute the required times + Map_TimeComputeRequiredGlobal( p ); + // recover switching activity + p->fMappingMode = 4; + Map_MappingMatches( p ); + // compute the references and collect the nodes used in the mapping + Map_MappingSetRefs( p ); + p->AreaFinal = Map_MappingGetArea( p, p->vMapping ); +if ( p->fVerbose ) +{ +printf( "Switching: %s = %8.2f Flow = %11.1f Area = %11.1f %4.1f %% ", + fShowSwitching? "Switch" : "Delay", + fShowSwitching? Map_MappingGetSwitching(p,p->vMapping) : p->fRequiredGlo, + 0.0, p->AreaFinal, + 100.0*(p->AreaBase-p->AreaFinal)/p->AreaBase ); +PRT( "Time", clock() - clk ); +} + } + p->timeArea += clock() - clk; + ////////////////////////////////////////////////////////////////////// + + // print the arrival times of the latest outputs + if ( p->fVerbose ) + Map_MappingPrintOutputArrivals( p ); + return 1; +} diff --git a/abc_with_bb_support/src/map/mapper/mapperCreate.c b/abc_with_bb_support/src/map/mapper/mapperCreate.c new file mode 100644 index 000000000..fe3bb987a --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/mapperCreate.c @@ -0,0 +1,600 @@ +/**CFile**************************************************************** + + FileName [mapperCreate.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - June 1, 2004.] + + Revision [$Id: mapperCreate.c,v 1.15 2005/02/28 05:34:26 alanmi Exp $] + +***********************************************************************/ + +#include "mapperInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Map_TableCreate( Map_Man_t * p ); +static void Map_TableResize( Map_Man_t * p ); +static Map_Node_t * Map_TableLookup( Map_Man_t * p, Map_Node_t * p1, Map_Node_t * p2 ); + +// hash key for the structural hash table +static inline unsigned Map_HashKey2( Map_Node_t * p0, Map_Node_t * p1, int TableSize ) { return ((unsigned)(p0) + (unsigned)(p1) * 12582917) % TableSize; } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Reads parameters from the mapping manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_ManReadInputNum( Map_Man_t * p ) { return p->nInputs; } +int Map_ManReadOutputNum( Map_Man_t * p ) { return p->nOutputs; } +Map_Node_t ** Map_ManReadInputs ( Map_Man_t * p ) { return p->pInputs; } +Map_Node_t ** Map_ManReadOutputs( Map_Man_t * p ) { return p->pOutputs; } +Map_Node_t * Map_ManReadConst1 ( Map_Man_t * p ) { return p->pConst1; } +Map_Time_t * Map_ManReadInputArrivals( Map_Man_t * p ) { return p->pInputArrivals;} +Mio_Library_t * Map_ManReadGenLib ( Map_Man_t * p ) { return p->pSuperLib->pGenlib; } +bool Map_ManReadVerbose( Map_Man_t * p ) { return p->fVerbose; } +float Map_ManReadAreaFinal( Map_Man_t * p ) { return p->AreaFinal; } +float Map_ManReadRequiredGlo( Map_Man_t * p ) { return p->fRequiredGlo; } +void Map_ManSetTimeToMap( Map_Man_t * p, int Time ) { p->timeToMap = Time; } +void Map_ManSetTimeToNet( Map_Man_t * p, int Time ) { p->timeToNet = Time; } +void Map_ManSetTimeSweep( Map_Man_t * p, int Time ) { p->timeSweep = Time; } +void Map_ManSetTimeTotal( Map_Man_t * p, int Time ) { p->timeTotal = Time; } +void Map_ManSetOutputNames( Map_Man_t * p, char ** ppNames ) { p->ppOutputNames = ppNames; } +void Map_ManSetAreaRecovery( Map_Man_t * p, int fAreaRecovery ) { p->fAreaRecovery = fAreaRecovery;} +void Map_ManSetDelayTarget( Map_Man_t * p, float DelayTarget ) { p->DelayTarget = DelayTarget;} +void Map_ManSetInputArrivals( Map_Man_t * p, Map_Time_t * pArrivals ) { p->pInputArrivals = pArrivals;} +void Map_ManSetObeyFanoutLimits( Map_Man_t * p, bool fObeyFanoutLimits ) { p->fObeyFanoutLimits = fObeyFanoutLimits; } +void Map_ManSetNumIterations( Map_Man_t * p, int nIterations ) { p->nIterations = nIterations; } +int Map_ManReadFanoutViolations( Map_Man_t * p ) { return p->nFanoutViolations; } +void Map_ManSetFanoutViolations( Map_Man_t * p, int nVio ) { p->nFanoutViolations = nVio; } +void Map_ManSetChoiceNodeNum( Map_Man_t * p, int nChoiceNodes ) { p->nChoiceNodes = nChoiceNodes; } +void Map_ManSetChoiceNum( Map_Man_t * p, int nChoices ) { p->nChoices = nChoices; } +void Map_ManSetVerbose( Map_Man_t * p, int fVerbose ) { p->fVerbose = fVerbose; } +void Map_ManSetSwitching( Map_Man_t * p, int fSwitching ) { p->fSwitching = fSwitching; } + +/**Function************************************************************* + + Synopsis [Reads parameters from the mapping node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Man_t * Map_NodeReadMan( Map_Node_t * p ) { return p->p; } +char * Map_NodeReadData( Map_Node_t * p, int fPhase ) { return fPhase? p->pData1 : p->pData0; } +int Map_NodeReadNum( Map_Node_t * p ) { return p->Num; } +int Map_NodeReadLevel( Map_Node_t * p ) { return Map_Regular(p)->Level; } +Map_Cut_t * Map_NodeReadCuts( Map_Node_t * p ) { return p->pCuts; } +Map_Cut_t * Map_NodeReadCutBest( Map_Node_t * p, int fPhase ) { return p->pCutBest[fPhase]; } +Map_Node_t * Map_NodeReadOne( Map_Node_t * p ) { return p->p1; } +Map_Node_t * Map_NodeReadTwo( Map_Node_t * p ) { return p->p2; } +void Map_NodeSetData( Map_Node_t * p, int fPhase, char * pData ) { if (fPhase) p->pData1 = pData; else p->pData0 = pData; } +void Map_NodeSetNextE( Map_Node_t * p, Map_Node_t * pNextE ) { p->pNextE = pNextE; } +void Map_NodeSetRepr( Map_Node_t * p, Map_Node_t * pRepr ) { p->pRepr = pRepr; } +void Map_NodeSetSwitching( Map_Node_t * p, float Switching ) { p->Switching = Switching; } + +/**Function************************************************************* + + Synopsis [Checks the type of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_NodeIsConst( Map_Node_t * p ) { return (Map_Regular(p))->Num == -1; } +int Map_NodeIsVar( Map_Node_t * p ) { return (Map_Regular(p))->p1 == NULL && (Map_Regular(p))->Num >= 0; } +int Map_NodeIsAnd( Map_Node_t * p ) { return (Map_Regular(p))->p1 != NULL; } +int Map_NodeComparePhase( Map_Node_t * p1, Map_Node_t * p2 ) { assert( !Map_IsComplement(p1) ); assert( !Map_IsComplement(p2) ); return p1->fInv ^ p2->fInv; } + +/**Function************************************************************* + + Synopsis [Reads parameters from the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Super_t * Map_CutReadSuperBest( Map_Cut_t * p, int fPhase ) { return p->M[fPhase].pSuperBest;} +Map_Super_t * Map_CutReadSuper0( Map_Cut_t * p ) { return p->M[0].pSuperBest;} +Map_Super_t * Map_CutReadSuper1( Map_Cut_t * p ) { return p->M[1].pSuperBest;} +int Map_CutReadLeavesNum( Map_Cut_t * p ) { return p->nLeaves; } +Map_Node_t ** Map_CutReadLeaves( Map_Cut_t * p ) { return p->ppLeaves; } +unsigned Map_CutReadPhaseBest( Map_Cut_t * p, int fPhase ) { return p->M[fPhase].uPhaseBest;} +unsigned Map_CutReadPhase0( Map_Cut_t * p ) { return p->M[0].uPhaseBest;} +unsigned Map_CutReadPhase1( Map_Cut_t * p ) { return p->M[1].uPhaseBest;} +Map_Cut_t * Map_CutReadNext( Map_Cut_t * p ) { return p->pNext; } + +/**Function************************************************************* + + Synopsis [Reads parameters from the supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Map_SuperReadFormula( Map_Super_t * p ) { return p->pFormula; } +Mio_Gate_t * Map_SuperReadRoot( Map_Super_t * p ) { return p->pRoot; } +int Map_SuperReadNum( Map_Super_t * p ) { return p->Num; } +Map_Super_t ** Map_SuperReadFanins( Map_Super_t * p ) { return p->pFanins; } +int Map_SuperReadFaninNum( Map_Super_t * p ) { return p->nFanins; } +Map_Super_t * Map_SuperReadNext( Map_Super_t * p ) { return p->pNext; } +int Map_SuperReadNumPhases( Map_Super_t * p ) { return p->nPhases; } +unsigned char * Map_SuperReadPhases( Map_Super_t * p ) { return p->uPhases; } +int Map_SuperReadFanoutLimit( Map_Super_t * p ) { return p->nFanLimit;} + +Mio_Library_t * Map_SuperLibReadGenLib( Map_SuperLib_t * p ) { return p->pGenlib; } +float Map_SuperLibReadAreaInv( Map_SuperLib_t * p ) { return p->AreaInv; } +Map_Time_t Map_SuperLibReadDelayInv( Map_SuperLib_t * p ) { return p->tDelayInv;} +int Map_SuperLibReadVarsMax( Map_SuperLib_t * p ) { return p->nVarsMax; } + + +/**Function************************************************************* + + Synopsis [Create the mapping manager.] + + Description [The number of inputs and outputs is assumed to be + known is advance. It is much simpler to have them fixed upfront. + When it comes to representing the object graph in the form of + AIG, the resulting manager is similar to the regular AIG manager, + except that it does not use reference counting (and therefore + does not have garbage collections). It does have table resizing. + The data structure is more flexible to represent additional + information needed for mapping.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Man_t * Map_ManCreate( int nInputs, int nOutputs, int fVerbose ) +{ + Map_Man_t * p; + int i; + + // derive the supergate library + if ( Abc_FrameReadLibSuper() == NULL ) + { + printf( "The supergate library is not specified. Use \"read_library\" or \"read_super\".\n" ); + return NULL; + } + + // start the manager + p = ALLOC( Map_Man_t, 1 ); + memset( p, 0, sizeof(Map_Man_t) ); + p->pSuperLib = Abc_FrameReadLibSuper(); + p->nVarsMax = p->pSuperLib->nVarsMax; + p->fVerbose = fVerbose; + p->fEpsilon = (float)0.001; + assert( p->nVarsMax > 0 ); + + if ( p->nVarsMax == 5 ) + Extra_Truth4VarN( &p->uCanons, &p->uPhases, &p->pCounters, 8 ); + + // start various data structures + Map_TableCreate( p ); + Map_MappingSetupTruthTables( p->uTruths ); + Map_MappingSetupTruthTablesLarge( p->uTruthsLarge ); +// printf( "Node = %d bytes. Cut = %d bytes. Super = %d bytes.\n", sizeof(Map_Node_t), sizeof(Map_Cut_t), sizeof(Map_Super_t) ); + p->mmNodes = Extra_MmFixedStart( sizeof(Map_Node_t) ); + p->mmCuts = Extra_MmFixedStart( sizeof(Map_Cut_t) ); + + // make sure the constant node will get index -1 + p->nNodes = -1; + // create the constant node + p->pConst1 = Map_NodeCreate( p, NULL, NULL ); + p->vNodesAll = Map_NodeVecAlloc( 100 ); + p->vNodesTemp = Map_NodeVecAlloc( 100 ); + p->vMapping = Map_NodeVecAlloc( 100 ); + p->vVisited = Map_NodeVecAlloc( 100 ); + + // create the PI nodes + p->nInputs = nInputs; + p->pInputs = ALLOC( Map_Node_t *, nInputs ); + for ( i = 0; i < nInputs; i++ ) + p->pInputs[i] = Map_NodeCreate( p, NULL, NULL ); + + // create the place for the output nodes + p->nOutputs = nOutputs; + p->pOutputs = ALLOC( Map_Node_t *, nOutputs ); + memset( p->pOutputs, 0, sizeof(Map_Node_t *) * nOutputs ); + return p; +} + +/**Function************************************************************* + + Synopsis [Deallocates the mapping manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_ManFree( Map_Man_t * p ) +{ +// int i; +// for ( i = 0; i < p->vNodesAll->nSize; i++ ) +// Map_NodeVecFree( p->vNodesAll->pArray[i]->vFanouts ); +// Map_NodeVecFree( p->pConst1->vFanouts ); + if ( p->vAnds ) + Map_NodeVecFree( p->vAnds ); + if ( p->vNodesAll ) + Map_NodeVecFree( p->vNodesAll ); + if ( p->vNodesTemp ) + Map_NodeVecFree( p->vNodesTemp ); + if ( p->vMapping ) + Map_NodeVecFree( p->vMapping ); + if ( p->vVisited ) + Map_NodeVecFree( p->vVisited ); + if ( p->uCanons ) free( p->uCanons ); + if ( p->uPhases ) free( p->uPhases ); + if ( p->pCounters ) free( p->pCounters ); + Extra_MmFixedStop( p->mmNodes ); + Extra_MmFixedStop( p->mmCuts ); + FREE( p->pInputArrivals ); + FREE( p->pInputs ); + FREE( p->pOutputs ); + FREE( p->pBins ); + FREE( p->ppOutputNames ); + FREE( p ); +} + + +/**Function************************************************************* + + Synopsis [Deallocates the mapping manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_ManPrintTimeStats( Map_Man_t * p ) +{ + printf( "N-canonical = %d. Matchings = %d. Phases = %d. ", p->nCanons, p->nMatches, p->nPhases ); + printf( "Choice nodes = %d. Choices = %d.\n", p->nChoiceNodes, p->nChoices ); + PRT( "ToMap", p->timeToMap ); + PRT( "Cuts ", p->timeCuts ); + PRT( "Truth", p->timeTruth ); + PRT( "Match", p->timeMatch ); + PRT( "Area ", p->timeArea ); + PRT( "Sweep", p->timeSweep ); + PRT( "ToNet", p->timeToNet ); + PRT( "TOTAL", p->timeTotal ); + if ( p->time1 ) { PRT( "time1", p->time1 ); } + if ( p->time2 ) { PRT( "time2", p->time2 ); } + if ( p->time3 ) { PRT( "time3", p->time3 ); } +} + +/**Function************************************************************* + + Synopsis [Prints the mapping stats.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_ManPrintStatsToFile( char * pName, float Area, float Delay, int Time ) +{ + FILE * pTable; + pTable = fopen( "map_stats.txt", "a+" ); + fprintf( pTable, "%s ", pName ); + fprintf( pTable, "%4.2f ", Area ); + fprintf( pTable, "%4.2f ", Delay ); + fprintf( pTable, "%4.2f\n", (float)(Time)/(float)(CLOCKS_PER_SEC) ); + fclose( pTable ); +} + +/**Function************************************************************* + + Synopsis [Creates a new node.] + + Description [This procedure should be called to create the constant + node and the PI nodes first.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Node_t * Map_NodeCreate( Map_Man_t * p, Map_Node_t * p1, Map_Node_t * p2 ) +{ + Map_Node_t * pNode; + // create the node + pNode = (Map_Node_t *)Extra_MmFixedEntryFetch( p->mmNodes ); + memset( pNode, 0, sizeof(Map_Node_t) ); + pNode->tRequired[0].Rise = pNode->tRequired[0].Fall = pNode->tRequired[0].Worst = MAP_FLOAT_LARGE; + pNode->tRequired[1].Rise = pNode->tRequired[1].Fall = pNode->tRequired[1].Worst = MAP_FLOAT_LARGE; + pNode->p1 = p1; + pNode->p2 = p2; + pNode->p = p; + // set the number of this node + pNode->Num = p->nNodes++; + // place to store the fanouts +// pNode->vFanouts = Map_NodeVecAlloc( 5 ); + // store this node in the internal array + if ( pNode->Num >= 0 ) + Map_NodeVecPush( p->vNodesAll, pNode ); + else + pNode->fInv = 1; + // set the level of this node + if ( p1 ) + { +#ifdef MAP_ALLOCATE_FANOUT + // create the fanout info + Map_NodeAddFaninFanout( Map_Regular(p1), pNode ); + Map_NodeAddFaninFanout( Map_Regular(p2), pNode ); +#endif + pNode->Level = 1 + MAP_MAX(Map_Regular(pNode->p1)->Level, Map_Regular(pNode->p2)->Level); + pNode->fInv = Map_NodeIsSimComplement(p1) & Map_NodeIsSimComplement(p2); + } + // reference the inputs (will be used to compute the number of fanouts) + if ( p1 ) Map_NodeRef(p1); + if ( p2 ) Map_NodeRef(p2); + + pNode->nRefEst[0] = pNode->nRefEst[1] = -1; + return pNode; +} + +/**Function************************************************************* + + Synopsis [Create the unique table of AND gates.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_TableCreate( Map_Man_t * pMan ) +{ + assert( pMan->pBins == NULL ); + pMan->nBins = Cudd_Prime(5000); + pMan->pBins = ALLOC( Map_Node_t *, pMan->nBins ); + memset( pMan->pBins, 0, sizeof(Map_Node_t *) * pMan->nBins ); + pMan->nNodes = 0; +} + +/**Function************************************************************* + + Synopsis [Looks up the AND2 node in the unique table.] + + Description [This procedure implements one-level hashing. All the nodes + are hashed by their children. If the node with the same children was already + created, it is returned by the call to this procedure. If it does not exist, + this procedure creates a new node with these children. ] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Node_t * Map_TableLookup( Map_Man_t * pMan, Map_Node_t * p1, Map_Node_t * p2 ) +{ + Map_Node_t * pEnt; + unsigned Key; + + if ( p1 == p2 ) + return p1; + if ( p1 == Map_Not(p2) ) + return Map_Not(pMan->pConst1); + if ( Map_NodeIsConst(p1) ) + { + if ( p1 == pMan->pConst1 ) + return p2; + return Map_Not(pMan->pConst1); + } + if ( Map_NodeIsConst(p2) ) + { + if ( p2 == pMan->pConst1 ) + return p1; + return Map_Not(pMan->pConst1); + } + + if ( Map_Regular(p1)->Num > Map_Regular(p2)->Num ) + pEnt = p1, p1 = p2, p2 = pEnt; + + Key = Map_HashKey2( p1, p2, pMan->nBins ); + for ( pEnt = pMan->pBins[Key]; pEnt; pEnt = pEnt->pNext ) + if ( pEnt->p1 == p1 && pEnt->p2 == p2 ) + return pEnt; + // resize the table + if ( pMan->nNodes >= 2 * pMan->nBins ) + { + Map_TableResize( pMan ); + Key = Map_HashKey2( p1, p2, pMan->nBins ); + } + // create the new node + pEnt = Map_NodeCreate( pMan, p1, p2 ); + // add the node to the corresponding linked list in the table + pEnt->pNext = pMan->pBins[Key]; + pMan->pBins[Key] = pEnt; + return pEnt; +} + + +/**Function************************************************************* + + Synopsis [Resizes the table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_TableResize( Map_Man_t * pMan ) +{ + Map_Node_t ** pBinsNew; + Map_Node_t * pEnt, * pEnt2; + int nBinsNew, Counter, i, clk; + unsigned Key; + +clk = clock(); + // get the new table size + nBinsNew = Cudd_Prime(2 * pMan->nBins); + // allocate a new array + pBinsNew = ALLOC( Map_Node_t *, nBinsNew ); + memset( pBinsNew, 0, sizeof(Map_Node_t *) * nBinsNew ); + // rehash the entries from the old table + Counter = 0; + for ( i = 0; i < pMan->nBins; i++ ) + for ( pEnt = pMan->pBins[i], pEnt2 = pEnt? pEnt->pNext: NULL; pEnt; + pEnt = pEnt2, pEnt2 = pEnt? pEnt->pNext: NULL ) + { + Key = Map_HashKey2( pEnt->p1, pEnt->p2, nBinsNew ); + pEnt->pNext = pBinsNew[Key]; + pBinsNew[Key] = pEnt; + Counter++; + } + assert( Counter == pMan->nNodes - pMan->nInputs ); + if ( pMan->fVerbose ) + { +// printf( "Increasing the unique table size from %6d to %6d. ", pMan->nBins, nBinsNew ); +// PRT( "Time", clock() - clk ); + } + // replace the table and the parameters + free( pMan->pBins ); + pMan->pBins = pBinsNew; + pMan->nBins = nBinsNew; +} + + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Node_t * Map_NodeAnd( Map_Man_t * p, Map_Node_t * p1, Map_Node_t * p2 ) +{ + Map_Node_t * pNode; + pNode = Map_TableLookup( p, p1, p2 ); + return pNode; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Node_t * Map_NodeOr( Map_Man_t * p, Map_Node_t * p1, Map_Node_t * p2 ) +{ + Map_Node_t * pNode; + pNode = Map_Not( Map_TableLookup( p, Map_Not(p1), Map_Not(p2) ) ); + return pNode; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Node_t * Map_NodeExor( Map_Man_t * p, Map_Node_t * p1, Map_Node_t * p2 ) +{ + return Map_NodeMux( p, p1, Map_Not(p2), p2 ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Node_t * Map_NodeMux( Map_Man_t * p, Map_Node_t * pC, Map_Node_t * pT, Map_Node_t * pE ) +{ + Map_Node_t * pAnd1, * pAnd2, * pRes; + pAnd1 = Map_TableLookup( p, pC, pT ); + pAnd2 = Map_TableLookup( p, Map_Not(pC), pE ); + pRes = Map_NodeOr( p, pAnd1, pAnd2 ); + return pRes; +} + + +/**Function************************************************************* + + Synopsis [Sets the node to be equivalent to the given one.] + + Description [This procedure is a work-around for the equivalence check. + Does not verify the equivalence. Use at the user's risk.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_NodeSetChoice( Map_Man_t * pMan, Map_Node_t * pNodeOld, Map_Node_t * pNodeNew ) +{ + pNodeNew->pNextE = pNodeOld->pNextE; + pNodeOld->pNextE = pNodeNew; + pNodeNew->pRepr = pNodeOld; +} + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/map/mapper/mapperCut.c b/abc_with_bb_support/src/map/mapper/mapperCut.c new file mode 100644 index 000000000..3e6783641 --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/mapperCut.c @@ -0,0 +1,1168 @@ +/**CFile**************************************************************** + + FileName [mapperCut.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - June 1, 2004.] + + Revision [$Id: mapperCut.c,v 1.12 2005/02/28 05:34:27 alanmi Exp $] + +***********************************************************************/ + +#include "mapperInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// the largest number of cuts considered +#define MAP_CUTS_MAX_COMPUTE 1000 +// the largest number of cuts used +#define MAP_CUTS_MAX_USE 250 + +// temporary hash table to store the cuts +typedef struct Map_CutTableStrutct_t Map_CutTable_t; +struct Map_CutTableStrutct_t +{ + Map_Cut_t ** pBins; // the table used for linear probing + int nBins; // the size of the table + int * pCuts; // the array of cuts currently stored + int nCuts; // the number of cuts currently stored + Map_Cut_t ** pArray; // the temporary array of cuts + Map_Cut_t ** pCuts1; // the temporary array of cuts + Map_Cut_t ** pCuts2; // the temporary array of cuts +}; + +// primes used to compute the hash key +static int s_HashPrimes[10] = { 109, 499, 557, 619, 631, 709, 797, 881, 907, 991 }; + +static Map_Cut_t * Map_CutCompute( Map_Man_t * p, Map_CutTable_t * pTable, Map_Node_t * pNode ); +static void Map_CutFilter( Map_Man_t * p, Map_Node_t * pNode ); +static Map_Cut_t * Map_CutMergeLists( Map_Man_t * p, Map_CutTable_t * pTable, Map_Cut_t * pList1, Map_Cut_t * pList2, int fComp1, int fComp2 ); +static int Map_CutMergeTwo( Map_Cut_t * pCut1, Map_Cut_t * pCut2, Map_Node_t * ppNodes[], int nNodesMax ); +static Map_Cut_t * Map_CutUnionLists( Map_Cut_t * pList1, Map_Cut_t * pList2 ); +static int Map_CutBelongsToList( Map_Cut_t * pList, Map_Node_t * ppNodes[], int nNodes ); + +static void Map_CutListPrint( Map_Man_t * pMan, Map_Node_t * pRoot ); +static void Map_CutListPrint2( Map_Man_t * pMan, Map_Node_t * pRoot ); +static void Map_CutPrint_( Map_Man_t * pMan, Map_Cut_t * pCut, Map_Node_t * pRoot ); + +static Map_CutTable_t * Map_CutTableStart( Map_Man_t * pMan ); +static void Map_CutTableStop( Map_CutTable_t * p ); +static unsigned Map_CutTableHash( Map_Node_t * ppNodes[], int nNodes ); +static int Map_CutTableLookup( Map_CutTable_t * p, Map_Node_t * ppNodes[], int nNodes ); +static Map_Cut_t * Map_CutTableConsider( Map_Man_t * pMan, Map_CutTable_t * p, Map_Node_t * ppNodes[], int nNodes ); +static void Map_CutTableRestart( Map_CutTable_t * p ); + +static Map_Cut_t * Map_CutSortCuts( Map_Man_t * pMan, Map_CutTable_t * p, Map_Cut_t * pList ); +static int Map_CutList2Array( Map_Cut_t ** pArray, Map_Cut_t * pList ); +static Map_Cut_t * Map_CutArray2List( Map_Cut_t ** pArray, int nCuts ); + +static unsigned Map_CutComputeTruth( Map_Man_t * p, Map_Cut_t * pCut, Map_Cut_t * pTemp0, Map_Cut_t * pTemp1, int fComp0, int fComp1 ); + +// iterator through all the cuts of the list +#define Map_ListForEachCut( pList, pCut ) \ + for ( pCut = pList; \ + pCut; \ + pCut = pCut->pNext ) +#define Map_ListForEachCutSafe( pList, pCut, pCut2 ) \ + for ( pCut = pList, \ + pCut2 = pCut? pCut->pNext: NULL; \ + pCut; \ + pCut = pCut2, \ + pCut2 = pCut? pCut->pNext: NULL ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes the cuts for each node in the object graph.] + + Description [The cuts are computed in one sweep over the mapping graph. + First, the elementary cuts, which include the node itself, are assigned + to the PI nodes. The internal nodes are considered in the DFS order. + Each node is two-input AND-gate. So to compute the cuts at a node, we + need to merge the sets of cuts of its two predecessors. The merged set + contains only unique cuts with the number of inputs equal to k or less. + Finally, the elementary cut, composed of the node itself, is added to + the set of cuts for the node. + + This procedure is pretty fast for 5-feasible cuts, but it dramatically + slows down on some "dense" networks when computing 6-feasible cuts. + The problem is that there are too many cuts in this case. We should + think how to heuristically trim the number of cuts in such cases, + to have reasonable runtime.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingCuts( Map_Man_t * p ) +{ + ProgressBar * pProgress; + Map_CutTable_t * pTable; + Map_Node_t * pNode; + Map_Cut_t * pCut; + int nCuts, nNodes, i; + int clk = clock(); + // set the elementary cuts for the PI variables + assert( p->nVarsMax > 1 && p->nVarsMax < 7 ); + for ( i = 0; i < p->nInputs; i++ ) + { + pCut = Map_CutAlloc( p ); + pCut->nLeaves = 1; + pCut->ppLeaves[0] = p->pInputs[i]; + p->pInputs[i]->pCuts = pCut; + p->pInputs[i]->pCutBest[0] = NULL; // negative polarity is not mapped + p->pInputs[i]->pCutBest[1] = pCut; // positive polarity is a trivial cut + pCut->uTruth = 0xAAAAAAAA; // the first variable "10101010" + pCut->M[0].AreaFlow = 0.0; + pCut->M[1].AreaFlow = 0.0; + } + + // compute the cuts for the internal nodes + nNodes = p->vAnds->nSize; + pProgress = Extra_ProgressBarStart( stdout, nNodes ); + pTable = Map_CutTableStart( p ); + for ( i = 0; i < nNodes; i++ ) + { + pNode = p->vAnds->pArray[i]; + if ( !Map_NodeIsAnd( pNode ) ) + continue; + Map_CutCompute( p, pTable, pNode ); + Extra_ProgressBarUpdate( pProgress, i, "Cuts ..." ); + } + Extra_ProgressBarStop( pProgress ); + Map_CutTableStop( pTable ); + + // report the stats + if ( p->fVerbose ) + { + nCuts = Map_MappingCountAllCuts(p); + printf( "Nodes = %6d. Total %d-feasible cuts = %10d. Per node = %.1f. ", + p->nNodes, p->nVarsMax, nCuts, ((float)nCuts)/p->nNodes ); + PRT( "Time", clock() - clk ); + } + + // print the cuts for the first primary output +// Map_CutListPrint( p, Map_Regular(p->pOutputs[0]) ); +} + +/**Function************************************************************* + + Synopsis [Computes the cuts for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Cut_t * Map_CutCompute( Map_Man_t * p, Map_CutTable_t * pTable, Map_Node_t * pNode ) +{ + Map_Node_t * pTemp; + Map_Cut_t * pList, * pList1, * pList2; + Map_Cut_t * pCut; + + // if the cuts are computed return them + if ( pNode->pCuts ) + return pNode->pCuts; + + // compute the cuts for the children + pList1 = Map_Regular(pNode->p1)->pCuts; + pList2 = Map_Regular(pNode->p2)->pCuts; + // merge the lists + pList = Map_CutMergeLists( p, pTable, pList1, pList2, + Map_IsComplement(pNode->p1), Map_IsComplement(pNode->p2) ); + // if there are functionally equivalent nodes, union them with this list + assert( pList ); + // only add to the list of cuts if the node is a representative one + if ( pNode->pRepr == NULL ) + { + for ( pTemp = pNode->pNextE; pTemp; pTemp = pTemp->pNextE ) + { + assert( pTemp->pCuts ); + pList = Map_CutUnionLists( pList, pTemp->pCuts ); + assert( pTemp->pCuts ); + pList = Map_CutSortCuts( p, pTable, pList ); + } + } + // add the new cut + pCut = Map_CutAlloc( p ); + pCut->nLeaves = 1; + pCut->ppLeaves[0] = pNode; + pCut->uTruth = 0xAAAAAAAA; + // append (it is important that the elementary cut is appended first) + pCut->pNext = pList; + // set at the node + pNode->pCuts = pCut; + // remove the dominated cuts + Map_CutFilter( p, pNode ); + // set the phase correctly + if ( pNode->pRepr && Map_NodeComparePhase(pNode, pNode->pRepr) ) + { + Map_ListForEachCut( pNode->pCuts, pCut ) + pCut->Phase = 1; + } +/* + { + int i, Counter = 0;; + for ( pCut = pNode->pCuts->pNext; pCut; pCut = pCut->pNext ) + for ( i = 0; i < pCut->nLeaves; i++ ) + Counter += (pCut->ppLeaves[i]->Level >= pNode->Level); +// if ( Counter ) +// printf( " %d", Counter ); + } +*/ + return pCut; +} + +/**Function************************************************************* + + Synopsis [Filter the cuts using dominance.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_CutFilter( Map_Man_t * p, Map_Node_t * pNode ) +{ + Map_Cut_t * pTemp, * pPrev, * pCut, * pCut2; + int i, k, Counter; + + Counter = 0; + pPrev = pNode->pCuts; + Map_ListForEachCutSafe( pNode->pCuts->pNext, pCut, pCut2 ) + { + // go through all the previous cuts up to pCut + for ( pTemp = pNode->pCuts->pNext; pTemp != pCut; pTemp = pTemp->pNext ) + { + // check if every node in pTemp is contained in pCut + for ( i = 0; i < pTemp->nLeaves; i++ ) + { + for ( k = 0; k < pCut->nLeaves; k++ ) + if ( pTemp->ppLeaves[i] == pCut->ppLeaves[k] ) + break; + if ( k == pCut->nLeaves ) // node i in pTemp is not contained in pCut + break; + } + if ( i == pTemp->nLeaves ) // every node in pTemp is contained in pCut + { + Counter++; + break; + } + } + if ( pTemp != pCut ) // pTemp contain pCut + { + pPrev->pNext = pCut->pNext; // skip pCut + // recycle pCut + Map_CutFree( p, pCut ); + } + else + pPrev = pCut; + } +// printf( "Dominated = %3d. \n", Counter ); +} + +/**Function************************************************************* + + Synopsis [Merges two lists of cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Cut_t * Map_CutMergeLists( Map_Man_t * p, Map_CutTable_t * pTable, + Map_Cut_t * pList1, Map_Cut_t * pList2, int fComp1, int fComp2 ) +{ + Map_Node_t * ppNodes[6]; + Map_Cut_t * pListNew, ** ppListNew, * pLists[7] = { NULL }; + Map_Cut_t * pCut, * pPrev, * pTemp1, * pTemp2; + int nNodes, Counter, i; + Map_Cut_t ** ppArray1, ** ppArray2, ** ppArray3; + int nCuts1, nCuts2, nCuts3, k, fComp3; + + ppArray1 = pTable->pCuts1; + ppArray2 = pTable->pCuts2; + nCuts1 = Map_CutList2Array( ppArray1, pList1 ); + nCuts2 = Map_CutList2Array( ppArray2, pList2 ); + // swap the lists based on their length + if ( nCuts1 > nCuts2 ) + { + ppArray3 = ppArray1; + ppArray1 = ppArray2; + ppArray2 = ppArray3; + + nCuts3 = nCuts1; + nCuts1 = nCuts2; + nCuts2 = nCuts3; + + fComp3 = fComp1; + fComp1 = fComp2; + fComp2 = fComp3; + } + // pList1 is shorter or equal length compared to pList2 + + // prepare the manager for the cut computation + Map_CutTableRestart( pTable ); + // go through the cut pairs + Counter = 0; +// for ( pTemp1 = pList1; pTemp1; pTemp1 = fPivot1? NULL: pTemp1->pNext ) +// for ( pTemp2 = pList2; pTemp2; pTemp2 = fPivot2? NULL: pTemp2->pNext ) + for ( i = 0; i < nCuts1; i++ ) + { + for ( k = 0; k <= i; k++ ) + { + pTemp1 = ppArray1[i]; + pTemp2 = ppArray2[k]; + + if ( pTemp1->nLeaves == p->nVarsMax && pTemp2->nLeaves == p->nVarsMax ) + { + if ( pTemp1->ppLeaves[0] != pTemp2->ppLeaves[0] ) + continue; + if ( pTemp1->ppLeaves[1] != pTemp2->ppLeaves[1] ) + continue; + } + + // check if k-feasible cut exists + nNodes = Map_CutMergeTwo( pTemp1, pTemp2, ppNodes, p->nVarsMax ); + if ( nNodes == 0 ) + continue; + // consider the cut for possible addition to the set of new cuts + pCut = Map_CutTableConsider( p, pTable, ppNodes, nNodes ); + if ( pCut == NULL ) + continue; + // add data to the cut + pCut->pOne = Map_CutNotCond( pTemp1, fComp1 ); + pCut->pTwo = Map_CutNotCond( pTemp2, fComp2 ); +// if ( p->nVarsMax == 5 ) +// pCut->uTruth = Map_CutComputeTruth( p, pCut, pTemp1, pTemp2, fComp1, fComp2 ); + // add it to the corresponding list + pCut->pNext = pLists[pCut->nLeaves]; + pLists[pCut->nLeaves] = pCut; + // count this cut and quit if limit is reached + Counter++; + if ( Counter == MAP_CUTS_MAX_COMPUTE ) + goto QUITS; + } + for ( k = 0; k < i; k++ ) + { + pTemp1 = ppArray1[k]; + pTemp2 = ppArray2[i]; + + if ( pTemp1->nLeaves == p->nVarsMax && pTemp2->nLeaves == p->nVarsMax ) + { + if ( pTemp1->ppLeaves[0] != pTemp2->ppLeaves[0] ) + continue; + if ( pTemp1->ppLeaves[1] != pTemp2->ppLeaves[1] ) + continue; + } + + // check if k-feasible cut exists + nNodes = Map_CutMergeTwo( pTemp1, pTemp2, ppNodes, p->nVarsMax ); + if ( nNodes == 0 ) + continue; + // consider the cut for possible addition to the set of new cuts + pCut = Map_CutTableConsider( p, pTable, ppNodes, nNodes ); + if ( pCut == NULL ) + continue; + // add data to the cut + pCut->pOne = Map_CutNotCond( pTemp1, fComp1 ); + pCut->pTwo = Map_CutNotCond( pTemp2, fComp2 ); +// if ( p->nVarsMax == 5 ) +// pCut->uTruth = Map_CutComputeTruth( p, pCut, pTemp1, pTemp2, fComp1, fComp2 ); + // add it to the corresponding list + pCut->pNext = pLists[pCut->nLeaves]; + pLists[pCut->nLeaves] = pCut; + // count this cut and quit if limit is reached + Counter++; + if ( Counter == MAP_CUTS_MAX_COMPUTE ) + goto QUITS; + } + } + // consider the rest of them + for ( i = nCuts1; i < nCuts2; i++ ) + for ( k = 0; k < nCuts1; k++ ) + { + pTemp1 = ppArray1[k]; + pTemp2 = ppArray2[i]; + + if ( pTemp1->nLeaves == p->nVarsMax && pTemp2->nLeaves == p->nVarsMax ) + { + if ( pTemp1->ppLeaves[0] != pTemp2->ppLeaves[0] ) + continue; + if ( pTemp1->ppLeaves[1] != pTemp2->ppLeaves[1] ) + continue; + } + + // check if k-feasible cut exists + nNodes = Map_CutMergeTwo( pTemp1, pTemp2, ppNodes, p->nVarsMax ); + if ( nNodes == 0 ) + continue; + // consider the cut for possible addition to the set of new cuts + pCut = Map_CutTableConsider( p, pTable, ppNodes, nNodes ); + if ( pCut == NULL ) + continue; + // add data to the cut + pCut->pOne = Map_CutNotCond( pTemp1, fComp1 ); + pCut->pTwo = Map_CutNotCond( pTemp2, fComp2 ); +// if ( p->nVarsMax == 5 ) +// pCut->uTruth = Map_CutComputeTruth( p, pCut, pTemp1, pTemp2, fComp1, fComp2 ); + // add it to the corresponding list + pCut->pNext = pLists[pCut->nLeaves]; + pLists[pCut->nLeaves] = pCut; + // count this cut and quit if limit is reached + Counter++; + if ( Counter == MAP_CUTS_MAX_COMPUTE ) + goto QUITS; + } +QUITS : + // combine all the lists into one + pListNew = NULL; + ppListNew = &pListNew; + for ( i = 1; i <= p->nVarsMax; i++ ) + { + if ( pLists[i] == NULL ) + continue; + // find the last entry + for ( pPrev = pLists[i], pCut = pPrev->pNext; pCut; + pPrev = pCut, pCut = pCut->pNext ); + // connect these lists + *ppListNew = pLists[i]; + ppListNew = &pPrev->pNext; + } + *ppListNew = NULL; + // soft the cuts by arrival times and use only the first MAP_CUTS_MAX_USE + pListNew = Map_CutSortCuts( p, pTable, pListNew ); + return pListNew; +} + + +/**Function************************************************************* + + Synopsis [Merges two lists of cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Cut_t * Map_CutMergeLists2( Map_Man_t * p, Map_CutTable_t * pTable, + Map_Cut_t * pList1, Map_Cut_t * pList2, int fComp1, int fComp2 ) +{ + Map_Node_t * ppNodes[6]; + Map_Cut_t * pListNew, ** ppListNew, * pLists[7] = { NULL }; + Map_Cut_t * pCut, * pPrev, * pTemp1, * pTemp2; + int nNodes, Counter, i; + + // prepare the manager for the cut computation + Map_CutTableRestart( pTable ); + // go through the cut pairs + Counter = 0; + for ( pTemp1 = pList1; pTemp1; pTemp1 = pTemp1->pNext ) + for ( pTemp2 = pList2; pTemp2; pTemp2 = pTemp2->pNext ) + { + // check if k-feasible cut exists + nNodes = Map_CutMergeTwo( pTemp1, pTemp2, ppNodes, p->nVarsMax ); + if ( nNodes == 0 ) + continue; + // consider the cut for possible addition to the set of new cuts + pCut = Map_CutTableConsider( p, pTable, ppNodes, nNodes ); + if ( pCut == NULL ) + continue; + // add data to the cut + pCut->pOne = Map_CutNotCond( pTemp1, fComp1 ); + pCut->pTwo = Map_CutNotCond( pTemp2, fComp2 ); + // add it to the corresponding list + pCut->pNext = pLists[pCut->nLeaves]; + pLists[pCut->nLeaves] = pCut; + // count this cut and quit if limit is reached + Counter++; + if ( Counter == MAP_CUTS_MAX_COMPUTE ) + goto QUITS; + } +QUITS : + // combine all the lists into one + pListNew = NULL; + ppListNew = &pListNew; + for ( i = 1; i <= p->nVarsMax; i++ ) + { + if ( pLists[i] == NULL ) + continue; + // find the last entry + for ( pPrev = pLists[i], pCut = pPrev->pNext; pCut; + pPrev = pCut, pCut = pCut->pNext ); + // connect these lists + *ppListNew = pLists[i]; + ppListNew = &pPrev->pNext; + } + *ppListNew = NULL; + // soft the cuts by arrival times and use only the first MAP_CUTS_MAX_USE + pListNew = Map_CutSortCuts( p, pTable, pListNew ); + return pListNew; +} + +/**Function************************************************************* + + Synopsis [Merges two cuts.] + + Description [Returns the number of nodes in the resulting cut, or 0 if the + cut is infeasible. Returns the resulting nodes in the array ppNodes[].] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_CutMergeTwo( Map_Cut_t * pCut1, Map_Cut_t * pCut2, Map_Node_t * ppNodes[], int nNodesMax ) +{ + Map_Node_t * pNodeTemp; + int nTotal, i, k, min, fMismatch; + + // check the special case when at least of the cuts is the largest + if ( pCut1->nLeaves == nNodesMax ) + { + if ( pCut2->nLeaves == nNodesMax ) + { + // return 0 if the cuts are different + for ( i = 0; i < nNodesMax; i++ ) + if ( pCut1->ppLeaves[i] != pCut2->ppLeaves[i] ) + return 0; + // return nNodesMax if they are the same + for ( i = 0; i < nNodesMax; i++ ) + ppNodes[i] = pCut1->ppLeaves[i]; + return nNodesMax; + } + else if ( pCut2->nLeaves == nNodesMax - 1 ) + { + // return 0 if the cuts are different + fMismatch = 0; + for ( i = 0; i < nNodesMax; i++ ) + if ( pCut1->ppLeaves[i] != pCut2->ppLeaves[i - fMismatch] ) + { + if ( fMismatch == 1 ) + return 0; + fMismatch = 1; + } + // return nNodesMax if they are the same + for ( i = 0; i < nNodesMax; i++ ) + ppNodes[i] = pCut1->ppLeaves[i]; + return nNodesMax; + } + } + else if ( pCut1->nLeaves == nNodesMax - 1 && pCut2->nLeaves == nNodesMax ) + { + // return 0 if the cuts are different + fMismatch = 0; + for ( i = 0; i < nNodesMax; i++ ) + if ( pCut1->ppLeaves[i - fMismatch] != pCut2->ppLeaves[i] ) + { + if ( fMismatch == 1 ) + return 0; + fMismatch = 1; + } + // return nNodesMax if they are the same + for ( i = 0; i < nNodesMax; i++ ) + ppNodes[i] = pCut2->ppLeaves[i]; + return nNodesMax; + } + + // count the number of unique entries in pCut2 + nTotal = pCut1->nLeaves; + for ( i = 0; i < pCut2->nLeaves; i++ ) + { + // try to find this entry among the leaves of pCut1 + for ( k = 0; k < pCut1->nLeaves; k++ ) + if ( pCut2->ppLeaves[i] == pCut1->ppLeaves[k] ) + break; + if ( k < pCut1->nLeaves ) // found + continue; + // we found a new entry to add + if ( nTotal == nNodesMax ) + return 0; + ppNodes[nTotal++] = pCut2->ppLeaves[i]; + } + // we know that the feasible cut exists + + // add the starting entries + for ( k = 0; k < pCut1->nLeaves; k++ ) + ppNodes[k] = pCut1->ppLeaves[k]; + + // selection-sort the entries + for ( i = 0; i < nTotal - 1; i++ ) + { + min = i; + for ( k = i+1; k < nTotal; k++ ) +// if ( ppNodes[k] < ppNodes[min] ) // reported bug fix (non-determinism!) + if ( ppNodes[k]->Num < ppNodes[min]->Num ) + min = k; + pNodeTemp = ppNodes[i]; + ppNodes[i] = ppNodes[min]; + ppNodes[min] = pNodeTemp; + } + + return nTotal; +} + +/**Function************************************************************* + + Synopsis [Computes the union of the two lists of cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Cut_t * Map_CutUnionLists( Map_Cut_t * pList1, Map_Cut_t * pList2 ) +{ + Map_Cut_t * pTemp, * pRoot; + // find the last cut in the first list + pRoot = pList1; + Map_ListForEachCut( pList1, pTemp ) + pRoot = pTemp; + // attach the non-trival part of the second cut to the end of the first + assert( pRoot->pNext == NULL ); + pRoot->pNext = pList2->pNext; + pList2->pNext = NULL; + return pList1; +} + + +/**Function************************************************************* + + Synopsis [Checks whether the given cut belongs to the list.] + + Description [This procedure takes most of the runtime in the cut + computation.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_CutBelongsToList( Map_Cut_t * pList, Map_Node_t * ppNodes[], int nNodes ) +{ + Map_Cut_t * pTemp; + int i; + for ( pTemp = pList; pTemp; pTemp = pTemp->pNext ) + { + for ( i = 0; i < nNodes; i++ ) + if ( pTemp->ppLeaves[i] != ppNodes[i] ) + break; + if ( i == nNodes ) + return 1; + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Counts all the cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_MappingCountAllCuts( Map_Man_t * pMan ) +{ + Map_Node_t * pNode; + Map_Cut_t * pCut; + int i, nCuts; +// int nCuts55 = 0, nCuts5x = 0, nCuts4x = 0, nCuts3x = 0; +// int pCounts[7] = {0}; + nCuts = 0; + for ( i = 0; i < pMan->nBins; i++ ) + for ( pNode = pMan->pBins[i]; pNode; pNode = pNode->pNext ) + for ( pCut = pNode->pCuts; pCut; pCut = pCut->pNext ) + if ( pCut->nLeaves > 1 ) // skip the elementary cuts + { + nCuts++; +/* + if ( Map_CutRegular(pCut->pOne)->nLeaves == 5 && Map_CutRegular(pCut->pTwo)->nLeaves == 5 ) + nCuts55++; + if ( Map_CutRegular(pCut->pOne)->nLeaves == 5 || Map_CutRegular(pCut->pTwo)->nLeaves == 5 ) + nCuts5x++; + else if ( Map_CutRegular(pCut->pOne)->nLeaves == 4 || Map_CutRegular(pCut->pTwo)->nLeaves == 4 ) + nCuts4x++; + else if ( Map_CutRegular(pCut->pOne)->nLeaves == 3 || Map_CutRegular(pCut->pTwo)->nLeaves == 3 ) + nCuts3x++; +*/ +// pCounts[ Map_CutRegular(pCut->pOne)->nLeaves ]++; +// pCounts[ Map_CutRegular(pCut->pTwo)->nLeaves ]++; + } +// printf( "Total cuts = %6d. 55 = %6d. 5x = %6d. 4x = %6d. 3x = %6d.\n", nCuts, nCuts55, nCuts5x, nCuts4x, nCuts3x ); + +// printf( "Total cuts = %6d. 6= %6d. 5= %6d. 4= %6d. 3= %6d. 2= %6d. 1= %6d.\n", +// nCuts, pCounts[6], pCounts[5], pCounts[4], pCounts[3], pCounts[2], pCounts[1] ); + return nCuts; +} + + +/**Function************************************************************* + + Synopsis [Prints the cuts in the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_CutListPrint( Map_Man_t * pMan, Map_Node_t * pRoot ) +{ + Map_Cut_t * pTemp; + int Counter; + for ( Counter = 0, pTemp = pRoot->pCuts; pTemp; pTemp = pTemp->pNext, Counter++ ) + { + printf( "%2d : ", Counter + 1 ); + Map_CutPrint_( pMan, pTemp, pRoot ); + } +} + +/**Function************************************************************* + + Synopsis [Prints the cuts in the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_CutListPrint2( Map_Man_t * pMan, Map_Node_t * pRoot ) +{ + Map_Cut_t * pTemp; + int Counter; + for ( Counter = 0, pTemp = pRoot->pCuts; pTemp; pTemp = pTemp->pNext, Counter++ ) + { + printf( "%2d : ", Counter + 1 ); + Map_CutPrint_( pMan, pTemp, pRoot ); + } +} + +/**Function************************************************************* + + Synopsis [Prints the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_CutPrint_( Map_Man_t * pMan, Map_Cut_t * pCut, Map_Node_t * pRoot ) +{ + int i; + printf( "(%3d) {", pRoot->Num ); + for ( i = 0; i < pMan->nVarsMax; i++ ) + if ( pCut->ppLeaves[i] ) + printf( " %3d", pCut->ppLeaves[i]->Num ); + printf( " }\n" ); +} + + + + + + + + +/**Function************************************************************* + + Synopsis [Starts the hash table to canonicize cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_CutTable_t * Map_CutTableStart( Map_Man_t * pMan ) +{ + Map_CutTable_t * p; + // allocate the table + p = ALLOC( Map_CutTable_t, 1 ); + memset( p, 0, sizeof(Map_CutTable_t) ); + p->nBins = Cudd_Prime( 10 * MAP_CUTS_MAX_COMPUTE ); + p->pBins = ALLOC( Map_Cut_t *, p->nBins ); + memset( p->pBins, 0, sizeof(Map_Cut_t *) * p->nBins ); + p->pCuts = ALLOC( int, 2 * MAP_CUTS_MAX_COMPUTE ); + p->pArray = ALLOC( Map_Cut_t *, 2 * MAP_CUTS_MAX_COMPUTE ); + p->pCuts1 = ALLOC( Map_Cut_t *, 2 * MAP_CUTS_MAX_COMPUTE ); + p->pCuts2 = ALLOC( Map_Cut_t *, 2 * MAP_CUTS_MAX_COMPUTE ); + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_CutTableStop( Map_CutTable_t * p ) +{ + free( p->pCuts1 ); + free( p->pCuts2 ); + free( p->pArray ); + free( p->pBins ); + free( p->pCuts ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Computes the hash value of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Map_CutTableHash( Map_Node_t * ppNodes[], int nNodes ) +{ + unsigned uRes; + int i; + uRes = 0; + for ( i = 0; i < nNodes; i++ ) + uRes += s_HashPrimes[i] * ppNodes[i]->Num; + return uRes; +} + +/**Function************************************************************* + + Synopsis [Looks up the table for the available cut.] + + Description [Returns -1 if the same cut is found. Returns the index + of the cell where the cut should be added, if it does not exist.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_CutTableLookup( Map_CutTable_t * p, Map_Node_t * ppNodes[], int nNodes ) +{ + Map_Cut_t * pCut; + unsigned Key; + int b, i; + + Key = Map_CutTableHash(ppNodes, nNodes) % p->nBins; + for ( b = Key; p->pBins[b]; b = (b+1) % p->nBins ) + { + pCut = p->pBins[b]; + if ( pCut->nLeaves != nNodes ) + continue; + for ( i = 0; i < nNodes; i++ ) + if ( pCut->ppLeaves[i] != ppNodes[i] ) + break; + if ( i == nNodes ) + return -1; + } + return b; +} + + +/**Function************************************************************* + + Synopsis [Starts the hash table to canonicize cuts.] + + Description [Considers addition of the cut to the hash table.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Cut_t * Map_CutTableConsider( Map_Man_t * pMan, Map_CutTable_t * p, Map_Node_t * ppNodes[], int nNodes ) +{ + Map_Cut_t * pCut; + int Place, i; +// int clk; + // check the cut + Place = Map_CutTableLookup( p, ppNodes, nNodes ); + if ( Place == -1 ) + return NULL; + assert( nNodes > 0 ); + // create the new cut +//clk = clock(); + pCut = Map_CutAlloc( pMan ); +//pMan->time1 += clock() - clk; + pCut->nLeaves = nNodes; + for ( i = 0; i < nNodes; i++ ) + pCut->ppLeaves[i] = ppNodes[i]; + // add the cut to the table + assert( p->pBins[Place] == NULL ); + p->pBins[Place] = pCut; + // add the cut to the new list + p->pCuts[ p->nCuts++ ] = Place; + return pCut; +} + +/**Function************************************************************* + + Synopsis [Prepares the table to be used with other cuts.] + + Description [Restarts the table by cleaning the info about cuts stored + when the previous node was considered.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_CutTableRestart( Map_CutTable_t * p ) +{ + int i; + for ( i = 0; i < p->nCuts; i++ ) + { + assert( p->pBins[ p->pCuts[i] ] ); + p->pBins[ p->pCuts[i] ] = NULL; + } + p->nCuts = 0; +} + + + +/**Function************************************************************* + + Synopsis [Compares the cuts by the number of leaves and then by delay.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_CutSortCutsCompare( Map_Cut_t ** pC1, Map_Cut_t ** pC2 ) +{ + if ( (*pC1)->nLeaves < (*pC2)->nLeaves ) + return -1; + if ( (*pC1)->nLeaves > (*pC2)->nLeaves ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Sorts the cuts by average arrival time.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Cut_t * Map_CutSortCuts( Map_Man_t * pMan, Map_CutTable_t * p, Map_Cut_t * pList ) +{ + Map_Cut_t * pListNew; + int nCuts, i; +// int clk; + // move the cuts from the list into the array + nCuts = Map_CutList2Array( p->pCuts1, pList ); + assert( nCuts <= MAP_CUTS_MAX_COMPUTE ); + // sort the cuts +//clk = clock(); + qsort( (void *)p->pCuts1, nCuts, sizeof(Map_Cut_t *), + (int (*)(const void *, const void *)) Map_CutSortCutsCompare ); +//pMan->time2 += clock() - clk; + // move them back into the list + if ( nCuts > MAP_CUTS_MAX_USE - 1 ) + { + // free the remaining cuts + for ( i = MAP_CUTS_MAX_USE - 1; i < nCuts; i++ ) + Extra_MmFixedEntryRecycle( pMan->mmCuts, (char *)p->pCuts1[i] ); + // update the number of cuts + nCuts = MAP_CUTS_MAX_USE - 1; + } + pListNew = Map_CutArray2List( p->pCuts1, nCuts ); + return pListNew; +} + +/**Function************************************************************* + + Synopsis [Moves the nodes from the list into the array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_CutList2Array( Map_Cut_t ** pArray, Map_Cut_t * pList ) +{ + int i; + for ( i = 0; pList; pList = pList->pNext, i++ ) + pArray[i] = pList; + return i; +} + +/**Function************************************************************* + + Synopsis [Moves the nodes from the array into the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Cut_t * Map_CutArray2List( Map_Cut_t ** pArray, int nCuts ) +{ + Map_Cut_t * pListNew, ** ppListNew; + int i; + pListNew = NULL; + ppListNew = &pListNew; + for ( i = 0; i < nCuts; i++ ) + { + // connect these lists + *ppListNew = pArray[i]; + ppListNew = &pArray[i]->pNext; + } +//printf( "\n" ); + + *ppListNew = NULL; + return pListNew; +} + + +/**Function************************************************************* + + Synopsis [Computes the truth table of the 5-input cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Map_CutComputeTruth( Map_Man_t * p, Map_Cut_t * pCut, Map_Cut_t * pTemp0, Map_Cut_t * pTemp1, int fComp0, int fComp1 ) +{ + static unsigned ** pPerms53 = NULL; + static unsigned ** pPerms54 = NULL; + + unsigned uPhase, uTruth, uTruth0, uTruth1; + int i, k; + + if ( pPerms53 == NULL ) + { + pPerms53 = (unsigned **)Extra_TruthPerm53(); + pPerms54 = (unsigned **)Extra_TruthPerm54(); + } + + // find the mapping from the old nodes to the new + if ( pTemp0->nLeaves == pCut->nLeaves ) + uTruth0 = pTemp0->uTruth; + else + { + assert( pTemp0->nLeaves < pCut->nLeaves ); + uPhase = 0; + for ( i = 0; i < (int)pTemp0->nLeaves; i++ ) + { + for ( k = 0; k < pCut->nLeaves; k++ ) + if ( pTemp0->ppLeaves[i] == pCut->ppLeaves[k] ) + break; + uPhase |= (1 << k); + } + assert( uPhase < 32 ); + if ( pTemp0->nLeaves == 4 ) + { + if ( uPhase == 31-16 ) // 01111 + uTruth0 = pTemp0->uTruth; + else if ( uPhase == 31-8 ) // 10111 + uTruth0 = pPerms54[pTemp0->uTruth & 0xFFFF][0]; + else if ( uPhase == 31-4 ) // 11011 + uTruth0 = pPerms54[pTemp0->uTruth & 0xFFFF][1]; + else if ( uPhase == 31-2 ) // 11101 + uTruth0 = pPerms54[pTemp0->uTruth & 0xFFFF][2]; + else if ( uPhase == 31-1 ) // 11110 + uTruth0 = pPerms54[pTemp0->uTruth & 0xFFFF][3]; + else + assert( 0 ); + } + else + uTruth0 = pPerms53[pTemp0->uTruth & 0xFF][uPhase]; + } + uTruth0 = fComp0? ~uTruth0: uTruth0; + + // find the mapping from the old nodes to the new + if ( pTemp1->nLeaves == pCut->nLeaves ) + uTruth1 = pTemp1->uTruth; + else + { + assert( pTemp1->nLeaves < pCut->nLeaves ); + uPhase = 0; + for ( i = 0; i < (int)pTemp1->nLeaves; i++ ) + { + for ( k = 0; k < pCut->nLeaves; k++ ) + if ( pTemp1->ppLeaves[i] == pCut->ppLeaves[k] ) + break; + uPhase |= (1 << k); + } + assert( uPhase < 32 ); + if ( pTemp1->nLeaves == 4 ) + { + if ( uPhase == 31-16 ) // 01111 + uTruth1 = pTemp1->uTruth; + else if ( uPhase == 31-8 ) // 10111 + uTruth1 = pPerms54[pTemp1->uTruth & 0xFFFF][0]; + else if ( uPhase == 31-4 ) // 11011 + uTruth1 = pPerms54[pTemp1->uTruth & 0xFFFF][1]; + else if ( uPhase == 31-2 ) // 11101 + uTruth1 = pPerms54[pTemp1->uTruth & 0xFFFF][2]; + else if ( uPhase == 31-1 ) // 11110 + uTruth1 = pPerms54[pTemp1->uTruth & 0xFFFF][3]; + else + assert( 0 ); + } + else + uTruth1 = pPerms53[pTemp1->uTruth & 0xFF][uPhase]; + } + uTruth1 = fComp1? ~uTruth1: uTruth1; + uTruth = uTruth0 & uTruth1; + return uTruth; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/map/mapper/mapperCutUtils.c b/abc_with_bb_support/src/map/mapper/mapperCutUtils.c new file mode 100644 index 000000000..fae7ac907 --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/mapperCutUtils.c @@ -0,0 +1,273 @@ +/**CFile**************************************************************** + + FileName [mapperCutUtils.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - June 1, 2004.] + + Revision [$Id: mapperCutUtils.h,v 1.0 2003/09/08 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "mapperInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Cut_t * Map_CutAlloc( Map_Man_t * p ) +{ + Map_Cut_t * pCut; + Map_Match_t * pMatch; + pCut = (Map_Cut_t *)Extra_MmFixedEntryFetch( p->mmCuts ); + memset( pCut, 0, sizeof(Map_Cut_t) ); + + pMatch = pCut->M; + pMatch->AreaFlow = MAP_FLOAT_LARGE; // unassigned + pMatch->tArrive.Rise = MAP_FLOAT_LARGE; // unassigned + pMatch->tArrive.Fall = MAP_FLOAT_LARGE; // unassigned + pMatch->tArrive.Worst = MAP_FLOAT_LARGE; // unassigned + + pMatch = pCut->M + 1; + pMatch->AreaFlow = MAP_FLOAT_LARGE; // unassigned + pMatch->tArrive.Rise = MAP_FLOAT_LARGE; // unassigned + pMatch->tArrive.Fall = MAP_FLOAT_LARGE; // unassigned + pMatch->tArrive.Worst = MAP_FLOAT_LARGE; // unassigned + return pCut; +} + +/**Function************************************************************* + + Synopsis [Deallocates the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_CutFree( Map_Man_t * p, Map_Cut_t * pCut ) +{ + if ( pCut ) + Extra_MmFixedEntryRecycle( p->mmCuts, (char *)pCut ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_CutPrint( Map_Man_t * p, Map_Node_t * pRoot, Map_Cut_t * pCut, int fPhase ) +{ + int i; + printf( "CUT: Delay = (%4.2f, %4.2f). Area = %4.2f. Nodes = %d -> {", + pCut->M[fPhase].tArrive.Rise, pCut->M[fPhase].tArrive.Fall, pCut->M[fPhase].AreaFlow, pRoot->Num ); + for ( i = 0; i < pCut->nLeaves; i++ ) + printf( " %d", pCut->ppLeaves[i]->Num ); + printf( " } \n" ); +} + + +/**function************************************************************* + + synopsis [Computes the exact area associated with the cut.] + + description [] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Map_CutGetRootArea( Map_Cut_t * pCut, int fPhase ) +{ + assert( pCut->M[fPhase].pSuperBest ); + return pCut->M[fPhase].pSuperBest->Area; +} + +/**function************************************************************* + + synopsis [Computes the exact area associated with the cut.] + + description [] + + sideeffects [] + + seealso [] + +***********************************************************************/ +int Map_CutGetLeafPhase( Map_Cut_t * pCut, int fPhase, int iLeaf ) +{ + assert( pCut->M[fPhase].pSuperBest ); + return (( pCut->M[fPhase].uPhaseBest & (1<pCutBest[fPhase]->M[fPhase].pSuperBest ); + return (( pNode->pCutBest[fPhase]->M[fPhase].uPhaseBest & (1<pNext ) + pPrev = pTemp; + // append all the end of the current set + assert( pPrev->pNext == NULL ); + pPrev->pNext = pSetAll; + return pSets; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_CutListRecycle( Map_Man_t * p, Map_Cut_t * pSetList, Map_Cut_t * pSave ) +{ + Map_Cut_t * pNext, * pTemp; + for ( pTemp = pSetList, pNext = pTemp? pTemp->pNext : NULL; + pTemp; + pTemp = pNext, pNext = pNext? pNext->pNext : NULL ) + if ( pTemp != pSave ) + Extra_MmFixedEntryRecycle( p->mmCuts, (char *)pTemp ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_CutListCount( Map_Cut_t * pSets ) +{ + Map_Cut_t * pTemp; + int i; + for ( i = 0, pTemp = pSets; pTemp; pTemp = pTemp->pNext, i++ ); + return i; +} + +#if 0 + +/**function************************************************************* + + synopsis [Removes the fanouts of the cut.] + + description [] + + sideeffects [] + + seealso [] + +***********************************************************************/ +void Map_CutRemoveFanouts( Map_Node_t * pNode, Map_Cut_t * pCut, int fPhase ) +{ + Map_NodeVec_t * vFanouts; + int i, k; + for ( i = 0; i < pCut->nLeaves; i++ ) + { + vFanouts = pCut->ppLeaves[i]->vFanouts; + for ( k = 0; k < vFanouts->nSize; k++ ) + if ( vFanouts->pArray[k] == pNode ) + break; + assert( k != vFanouts->nSize ); + for ( k++; k < vFanouts->nSize; k++ ) + vFanouts->pArray[k-1] = vFanouts->pArray[k]; + vFanouts->nSize--; + } +} + +/**function************************************************************* + + synopsis [Removes the fanouts of the cut.] + + description [] + + sideeffects [] + + seealso [] + +***********************************************************************/ +void Map_CutInsertFanouts( Map_Node_t * pNode, Map_Cut_t * pCut, int fPhase ) +{ + int i; + for ( i = 0; i < pCut->nLeaves; i++ ) + Map_NodeVecPush( pCut->ppLeaves[i]->vFanouts, pNode ); +} + +#endif + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/mapper/mapperFanout.c b/abc_with_bb_support/src/map/mapper/mapperFanout.c new file mode 100644 index 000000000..d0ded369f --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/mapperFanout.c @@ -0,0 +1,141 @@ +/**CFile**************************************************************** + + FileName [mapperFanout.c] + + PackageName [FRAIG: Functionally reduced AND-INV graphs.] + + Synopsis [Procedures to manipulate fanouts of the FRAIG nodes.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - June 1, 2004.] + + Revision [$Id: mapperFanout.c,v 1.5 2005/01/23 06:59:43 alanmi Exp $] + +***********************************************************************/ + +#include "mapperInt.h" + +#ifdef MAP_ALLOCATE_FANOUT + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Add the fanout to the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_NodeAddFaninFanout( Map_Node_t * pFanin, Map_Node_t * pFanout ) +{ + Map_Node_t * pPivot; + + // pFanins is a fanin of pFanout + assert( !Map_IsComplement(pFanin) ); + assert( !Map_IsComplement(pFanout) ); + assert( Map_Regular(pFanout->p1) == pFanin || Map_Regular(pFanout->p2) == pFanin ); + + pPivot = pFanin->pFanPivot; + if ( pPivot == NULL ) + { + pFanin->pFanPivot = pFanout; + return; + } + + if ( Map_Regular(pPivot->p1) == pFanin ) + { + if ( Map_Regular(pFanout->p1) == pFanin ) + { + pFanout->pFanFanin1 = pPivot->pFanFanin1; + pPivot->pFanFanin1 = pFanout; + } + else // if ( Map_Regular(pFanout->p2) == pFanin ) + { + pFanout->pFanFanin2 = pPivot->pFanFanin1; + pPivot->pFanFanin1 = pFanout; + } + } + else // if ( Map_Regular(pPivot->p2) == pFanin ) + { + assert( Map_Regular(pPivot->p2) == pFanin ); + if ( Map_Regular(pFanout->p1) == pFanin ) + { + pFanout->pFanFanin1 = pPivot->pFanFanin2; + pPivot->pFanFanin2 = pFanout; + } + else // if ( Map_Regular(pFanout->p2) == pFanin ) + { + pFanout->pFanFanin2 = pPivot->pFanFanin2; + pPivot->pFanFanin2 = pFanout; + } + } +} + +/**Function************************************************************* + + Synopsis [Add the fanout to the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_NodeRemoveFaninFanout( Map_Node_t * pFanin, Map_Node_t * pFanoutToRemove ) +{ + Map_Node_t * pFanout, * pFanout2, ** ppFanList; + // start the linked list of fanouts + ppFanList = &pFanin->pFanPivot; + // go through the fanouts + Map_NodeForEachFanoutSafe( pFanin, pFanout, pFanout2 ) + { + // skip the fanout-to-remove + if ( pFanout == pFanoutToRemove ) + continue; + // add useful fanouts to the list + *ppFanList = pFanout; + ppFanList = Map_NodeReadNextFanoutPlace( pFanin, pFanout ); + } + *ppFanList = NULL; +} + +/**Function************************************************************* + + Synopsis [Returns the number of fanouts of a node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_NodeGetFanoutNum( Map_Node_t * pNode ) +{ + Map_Node_t * pFanout; + int Counter = 0; + Map_NodeForEachFanout( pNode, pFanout ) + Counter++; + return Counter; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + +#endif + diff --git a/abc_with_bb_support/src/map/mapper/mapperGENERIC.c b/abc_with_bb_support/src/map/mapper/mapperGENERIC.c new file mode 100644 index 000000000..9775ed7f3 --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/mapperGENERIC.c @@ -0,0 +1,46 @@ +/**CFile**************************************************************** + + FileName [mapper__.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 8, 2003.] + + Revision [$Id: mapper__.h,v 1.0 2003/09/08 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "mapperInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/mapper/mapperInt.h b/abc_with_bb_support/src/map/mapper/mapperInt.h new file mode 100644 index 000000000..a04357294 --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/mapperInt.h @@ -0,0 +1,477 @@ +/**CFile**************************************************************** + + FileName [mapperInt.h] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - June 1, 2004.] + + Revision [$Id: mapperInt.h,v 1.8 2004/09/30 21:18:10 satrajit Exp $] + +***********************************************************************/ + +#ifndef __MAPPER_INT_H__ +#define __MAPPER_INT_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//#include "leaks.h" +#include +#include +#include +#include +#include "cuddInt.h" +#include "main.h" +#include "mio.h" +#include "mapper.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +// uncomment to have fanouts represented in the mapping graph +//#define MAP_ALLOCATE_FANOUT 1 + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// the bit masks +#define MAP_MASK(n) ((~((unsigned)0)) >> (32-(n))) +#define MAP_FULL (~((unsigned)0)) +#define MAP_NO_VAR (-9999.0) + +// maximum/minimum operators +#define MAP_MIN(a,b) (((a) < (b))? (a) : (b)) +#define MAP_MAX(a,b) (((a) > (b))? (a) : (b)) + +// the small and large numbers (min/max float are 1.17e-38/3.40e+38) +#define MAP_FLOAT_LARGE ((float)(FLT_MAX/10)) +#define MAP_FLOAT_SMALL ((float)1.0e-03) + +// generating random unsigned (#define RAND_MAX 0x7fff) +#define MAP_RANDOM_UNSIGNED ((((unsigned)rand()) << 24) ^ (((unsigned)rand()) << 12) ^ ((unsigned)rand())) + +// internal macros to work with cuts +#define Map_CutIsComplement(p) (((int)((unsigned long) (p) & 01))) +#define Map_CutRegular(p) ((Map_Cut_t *)((unsigned long)(p) & ~01)) +#define Map_CutNot(p) ((Map_Cut_t *)((unsigned long)(p) ^ 01)) +#define Map_CutNotCond(p,c) ((Map_Cut_t *)((unsigned long)(p) ^ (c))) + +// internal macros for referencing of nodes +#define Map_NodeReadRef(p) ((Map_Regular(p))->nRefs) +#define Map_NodeRef(p) ((Map_Regular(p))->nRefs++) + +// macros to get hold of the bits in the support info +#define Map_InfoSetVar(p,i) (p[(i)>>5] |= (1<<((i) & 31))) +#define Map_InfoRemVar(p,i) (p[(i)>>5] &= ~(1<<((i) & 31))) +#define Map_InfoFlipVar(p,i) (p[(i)>>5] ^= (1<<((i) & 31))) +#define Map_InfoReadVar(p,i) ((p[(i)>>5] & (1<<((i) & 31))) > 0) + +// returns the complemented attribute of the node +#define Map_NodeIsSimComplement(p) (Map_IsComplement(p)? !(Map_Regular(p)->fInv) : (p)->fInv) + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// the mapping manager +struct Map_ManStruct_t_ +{ + // the mapping graph + Map_Node_t ** pBins; // the table of nodes hashed by their children + int nBins; // the size of the table + Map_Node_t ** pInputs; // the array of inputs + int nInputs; // the number of inputs + Map_Node_t ** pOutputs; // the array of outputs + int nOutputs; // the number of outputs + int nNodes; // the total number of nodes + Map_Node_t * pConst1; // the constant 1 node + Map_NodeVec_t * vAnds; // the array of nodes in the DFS order + Map_NodeVec_t * vNodesAll; // the array of all nodes + Map_NodeVec_t * vNodesTemp; // the array of all nodes + Map_NodeVec_t * vMapping; // the array of internal nodes used in the mapping + + // info about the original circuit + char ** ppOutputNames; // the primary output names + Map_Time_t * pInputArrivals;// the PI arrival times + + // mapping parameters + int nVarsMax; // the max number of variables + int fAreaRecovery; // the flag to enable area recovery + int fVerbose; // the verbosiness flag + int fMappingMode; // set to 1 when doing area + float fRequiredGlo; // the global required times + float fEpsilon; // the epsilon used to compare floats + float AreaBase; // the area after delay-oriented mapping + float AreaFinal; // the area after delay-oriented mapping + int nIterations; // How many matching passes to do + bool fObeyFanoutLimits;// Should mapper try to obey fanout limits or not + float DelayTarget; // the required times set by the user + int nTravIds; // the traversal counter + bool fSwitching; // Should mapper try to obey fanout limits or not + + // the supergate library + Map_SuperLib_t * pSuperLib; // the current supergate library + unsigned uTruths[6][2]; // the elementary truth tables + unsigned uTruthsLarge[10][32]; // the elementary truth tables + int nCounts[32]; // the counter of minterms + int nCountsBest[32];// the counter of minterms + Map_NodeVec_t * vVisited; // the visited cuts during cut computation + + // the memory managers + Extra_MmFixed_t * mmNodes; // the memory manager for nodes + Extra_MmFixed_t * mmCuts; // the memory manager for cuts + + // precomputed N-canonical forms + unsigned short * uCanons; // N-canonical forms + char ** uPhases; // N-canonical phases + char * pCounters; // counters of phases + + // various statistical variables + int nChoiceNodes; // the number of choice nodes + int nChoices; // the number of all choices + int nCanons; // the number of times N-canonical form was computed + int nMatches; // the number of times supergate matching was performed + int nPhases; // the number of phases considered during matching + int nFanoutViolations; // the number of nodes in mapped circuit violating fanout + + // runtime statistics + int timeToMap; // time to transfer to the mapping structure + int timeCuts; // time to compute k-feasible cuts + int timeTruth; // time to compute the truth table for each cut + int timeMatch; // time to perform matching for each node + int timeArea; // time to recover area after delay oriented mapping + int timeSweep; // time to perform technology dependent sweep + int timeToNet; // time to transfer back to the network + int timeTotal; // the total mapping time + int time1; // time to transfer to the mapping structure + int time2; // time to transfer to the mapping structure + int time3; // time to transfer to the mapping structure +}; + +// the supergate library +struct Map_SuperLibStruct_t_ +{ + // general info + char * pName; // the name of the supergate library + Mio_Library_t * pGenlib; // the generic library + + // other info + int nVarsMax; // the max number of variables + int nSupersAll; // the total number of supergates + int nSupersReal; // the total number of supergates + int nLines; // the total number of lines in the supergate file + bool fVerbose; // the verbosity flag + + // hash tables + Map_Super_t ** ppSupers; // the array of supergates + Map_HashTable_t * tTableC; // the table mapping N-canonical forms into supergates + Map_HashTable_t * tTable; // the table mapping truth tables into supergates + + // data structures for N-canonical form computation + unsigned uTruths[6][2]; // the elementary truth tables + unsigned uMask[2]; // the mask for the truth table + + // the invertor + Mio_Gate_t * pGateInv; // the pointer to the intertor gate + Map_Time_t tDelayInv; // the delay of the inverter + float AreaInv; // the area of the inverter + float AreaBuf; // the area of the buffer + Map_Super_t * pSuperInv; // the supergate representing the inverter + + // the memory manager for the internal table + Extra_MmFixed_t * mmSupers; // the mamory manager for supergates + Extra_MmFixed_t * mmEntries; // the memory manager for the entries + Extra_MmFlex_t * mmForms; // the memory manager for formulas +}; + +// the mapping node +struct Map_NodeStruct_t_ +{ + // general information about the node + Map_Man_t * p; // the mapping manager + Map_Node_t * pNext; // the next node in the hash table + int Num; // the unique number of this node + int TravId; // the traversal ID (use to avoid cleaning marks) + int nRefs; // the number of references (fanouts) of the given node + unsigned fMark0 : 1; // the mark used for traversals + unsigned fMark1 : 1; // the mark used for traversals + unsigned fUsed : 1; // the mark to mark the node or its fanins + unsigned fInv : 1; // the complemented attribute for the equivalent nodes + unsigned fInvert: 1; // the flag to denote the use of interter + unsigned Level :16; // the level of the given node + unsigned NumTemp:10; // the level of the given node + int nRefAct[3]; // estimated fanout for current covering phase, neg and pos and sum + float nRefEst[3]; // actual fanout for previous covering phase, neg and pos and sum + float Switching; // the probability of switching + + // connectivity + Map_Node_t * p1; // the first child + Map_Node_t * p2; // the second child + Map_Node_t * pNextE; // the next functionally equivalent node + Map_Node_t * pRepr; // the representative of the functionally equivalent class + +#ifdef MAP_ALLOCATE_FANOUT + // representation of node's fanouts + Map_Node_t * pFanPivot; // the first fanout of this node + Map_Node_t * pFanFanin1; // the next fanout of p1 + Map_Node_t * pFanFanin2; // the next fanout of p2 +// Map_NodeVec_t * vFanouts; // the array of fanouts of the gate +#endif + + // the delay information + Map_Time_t tArrival[2]; // the best arrival time of the neg (0) and pos (1) phases + Map_Time_t tRequired[2]; // the required time of the neg (0) and pos (1) phases + + // misc information + Map_Cut_t * pCutBest[2]; // the best mapping for neg and pos phase + Map_Cut_t * pCuts; // mapping choices for the node (elementary comes first) + char * pData0; // temporary storage for the corresponding network node + char * pData1; // temporary storage for the corresponding network node +}; + +// the match of the cut +struct Map_MatchStruct_t_ +{ + // information used for matching + Map_Super_t * pSupers; + unsigned uPhase; + // information about the best selected match + unsigned uPhaseBest; // the best phase (the EXOR of match's phase and gate's phase) + Map_Super_t * pSuperBest; // the best supergate matched + // the parameters of the match + Map_Time_t tArrive; // the arrival time of this match + float AreaFlow; // the area flow or area of this match +}; + +// the cuts used for matching +struct Map_CutStruct_t_ +{ + Map_Cut_t * pNext; // the pointer to the next cut in the list + Map_Cut_t * pOne; // the father of this cut + Map_Cut_t * pTwo; // the mother of this cut + Map_Node_t * ppLeaves[6]; // the leaves of this cut + unsigned uTruth; // truth table for five-input cuts + char nLeaves; // the number of leaves + char nVolume; // the volume of this cut + char fMark; // the mark to denote visited cut + char Phase; // the mark to denote complemented cut + Map_Match_t M[2]; // the matches for positive/negative phase +}; + +// the supergate internally represented +struct Map_SuperStruct_t_ +{ + int Num; // the ID of the supergate + unsigned fSuper : 1; // the flag to distinquish a real super from a fake one + unsigned fExclude: 1; // the flag if set causes gate to be excluded from being used for mapping + unsigned nFanins : 3; // the number of inputs + unsigned nGates : 3; // the number of gates inside this supergate + unsigned nFanLimit: 4; // the max number of fanout count + unsigned nSupers : 16; // the number of supergates in the list + unsigned nPhases : 4; // the number of phases for matching with canonical form + unsigned char uPhases[4]; // the maximum of 4 phases for matching with canonical form + int nUsed; // the number of times the supergate is used + Map_Super_t * pFanins[6]; // the fanins of the gate + Mio_Gate_t * pRoot; // the root gate + unsigned uTruth[2]; // the truth table + Map_Time_t tDelaysR[6]; // the pin-to-pin delay constraints for the rise of the output + Map_Time_t tDelaysF[6]; // the pin-to-pin delay constraints for the rise of the output + Map_Time_t tDelayMax; // the maximum delay + float Area; // the area + char * pFormula; // the symbolic formula + Map_Super_t * pNext; // the pointer to the next super in the list +}; + +// the vector of nodes +struct Map_NodeVecStruct_t_ +{ + Map_Node_t ** pArray; // the array of nodes + int nSize; // the number of entries in the array + int nCap; // the number of allocated entries +}; + +// the hash table +struct Map_HashTableStruct_t_ +{ + Map_HashEntry_t ** pBins; // the table bins + int nBins; // the size of the table + int nEntries; // the total number of entries in the table + Extra_MmFixed_t * mmMan; // the memory manager for entries +}; + +// the entry in the hash table +struct Map_HashEntryStruct_t_ +{ + unsigned uTruth[2]; // the truth table for 6-var function + unsigned uPhase; // the phase to tranform it into the canonical form + Map_Super_t * pGates; // the linked list of matching supergates + Map_HashEntry_t * pNext; // the next entry in the hash table +}; + +// getting hold of the next fanout of the node +#define Map_NodeReadNextFanout( pNode, pFanout ) \ + ( ( pFanout == NULL )? NULL : \ + ((Map_Regular((pFanout)->p1) == (pNode))? \ + (pFanout)->pFanFanin1 : (pFanout)->pFanFanin2) ) + +// getting hold of the place where the next fanout will be attached +#define Map_NodeReadNextFanoutPlace( pNode, pFanout ) \ + ( (Map_Regular((pFanout)->p1) == (pNode))? \ + &(pFanout)->pFanFanin1 : &(pFanout)->pFanFanin2 ) + +// iterator through the fanouts of the node +#define Map_NodeForEachFanout( pNode, pFanout ) \ + for ( pFanout = (pNode)->pFanPivot; pFanout; \ + pFanout = Map_NodeReadNextFanout(pNode, pFanout) ) + +// safe iterator through the fanouts of the node +#define Map_NodeForEachFanoutSafe( pNode, pFanout, pFanout2 ) \ + for ( pFanout = (pNode)->pFanPivot, \ + pFanout2 = Map_NodeReadNextFanout(pNode, pFanout); \ + pFanout; \ + pFanout = pFanout2, \ + pFanout2 = Map_NodeReadNextFanout(pNode, pFanout) ) + +//////////////////////////////////////////////////////////////////////// +/// GLOBAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== mapperCanon.c =============================================================*/ +/*=== mapperCut.c ===============================================================*/ +extern void Map_MappingCuts( Map_Man_t * p ); +extern int Map_MappingCountAllCuts( Map_Man_t * p ); +/*=== mapperCutDcs.c ===============================================================*/ +extern void Map_ComputeDcs( Map_Man_t * p ); +extern unsigned Map_ComputeIsop_rec( Map_Man_t * p, unsigned uF, unsigned uFD, int iVar, int nVars, int fDir ); +/*=== mapperCutUtils.c ===============================================================*/ +extern Map_Cut_t * Map_CutAlloc( Map_Man_t * p ); +extern void Map_CutFree( Map_Man_t * p, Map_Cut_t * pCut ); +extern void Map_CutPrint( Map_Man_t * p, Map_Node_t * pRoot, Map_Cut_t * pCut, int fPhase ); +extern float Map_CutGetRootArea( Map_Cut_t * pCut, int fPhase ); +extern int Map_CutGetLeafPhase( Map_Cut_t * pCut, int fPhase, int iLeaf ); +extern int Map_NodeGetLeafPhase( Map_Node_t * pNode, int fPhase, int iLeaf ); +extern Map_Cut_t * Map_CutListAppend( Map_Cut_t * pSetAll, Map_Cut_t * pSets ); +extern void Map_CutListRecycle( Map_Man_t * p, Map_Cut_t * pSetList, Map_Cut_t * pSave ); +extern int Map_CutListCount( Map_Cut_t * pSets ); +extern void Map_CutRemoveFanouts( Map_Node_t * pNode, Map_Cut_t * pCut, int fPhase ); +extern void Map_CutInsertFanouts( Map_Node_t * pNode, Map_Cut_t * pCut, int fPhase ); +/*=== mapperFanout.c =============================================================*/ +extern void Map_NodeAddFaninFanout( Map_Node_t * pFanin, Map_Node_t * pFanout ); +extern void Map_NodeRemoveFaninFanout( Map_Node_t * pFanin, Map_Node_t * pFanoutToRemove ); +extern int Map_NodeGetFanoutNum( Map_Node_t * pNode ); +/*=== mapperLib.c ============================================================*/ +extern Map_SuperLib_t * Map_SuperLibCreate( char * pFileName, char * pExcludeFile, bool fAlgorithm, bool fVerbose ); +extern void Map_SuperLibFree( Map_SuperLib_t * p ); +/*=== mapperMatch.c ===============================================================*/ +extern int Map_MappingMatches( Map_Man_t * p ); +extern float Map_MappingCombinePhases( Map_Man_t * p ); +extern void Map_MatchClean( Map_Match_t * pMatch ); +extern int Map_MatchCompare( Map_Man_t * pMan, Map_Match_t * pM1, Map_Match_t * pM2, int fDoingArea ); +/*=== mapperPower.c =============================================================*/ +extern float Map_SwitchCutGetDerefed( Map_Node_t * pNode, Map_Cut_t * pCut, int fPhase ); +extern float Map_SwitchCutRef( Map_Node_t * pNode, Map_Cut_t * pCut, int fPhase ); +extern float Map_SwitchCutDeref( Map_Node_t * pNode, Map_Cut_t * pCut, int fPhase ); +extern float Map_MappingGetSwitching( Map_Man_t * pMan, Map_NodeVec_t * vMapping ); +/*=== mapperRefs.c =============================================================*/ +extern int Map_NodeReadRefPhaseAct( Map_Node_t * pNode, int fPhase ); +extern float Map_NodeReadRefPhaseEst( Map_Node_t * pNode, int fPhase ); +extern void Map_MappingEstimateRefsInit( Map_Man_t * p ); +extern void Map_MappingEstimateRefs( Map_Man_t * p ); +extern float Map_CutGetAreaFlow( Map_Cut_t * pCut, int fPhase ); +extern float Map_CutGetAreaRefed( Map_Cut_t * pCut, int fPhase ); +extern float Map_CutGetAreaDerefed( Map_Cut_t * pCut, int fPhase ); +extern float Map_CutRef( Map_Cut_t * pCut, int fPhase ); +extern float Map_CutDeref( Map_Cut_t * pCut, int fPhase ); +extern void Map_MappingSetRefs( Map_Man_t * pMan ); +extern float Map_MappingGetArea( Map_Man_t * pMan, Map_NodeVec_t * vMapping ); +/*=== mapperShow.c =============================================================*/ +extern void Map_MappingShow( Map_Man_t * pMan, char * pFileName ); +/*=== mapperTree.c ===============================================================*/ +extern int Map_LibraryReadTree( Map_SuperLib_t * pLib, char * pFileName, char * pExcludeFile ); +extern void Map_LibraryPrintTree( Map_SuperLib_t * pLib ); +/*=== mapperSuper.c ===============================================================*/ +extern int Map_LibraryRead( Map_SuperLib_t * p, char * pFileName ); +extern void Map_LibraryPrintSupergate( Map_Super_t * pGate ); +/*=== mapperTable.c ============================================================*/ +extern Map_HashTable_t * Map_SuperTableCreate( Map_SuperLib_t * pLib ); +extern void Map_SuperTableFree( Map_HashTable_t * p ); +extern int Map_SuperTableInsertC( Map_HashTable_t * pLib, unsigned uTruthC[], Map_Super_t * pGate ); +extern int Map_SuperTableInsert( Map_HashTable_t * pLib, unsigned uTruth[], Map_Super_t * pGate, unsigned uPhase ); +extern Map_Super_t * Map_SuperTableLookup( Map_HashTable_t * p, unsigned uTruth[], unsigned * puPhase ); +extern void Map_SuperTableSortSupergates( Map_HashTable_t * p, int nSupersMax ); +extern void Map_SuperTableSortSupergatesByDelay( Map_HashTable_t * p, int nSupersMax ); +/*=== mapperTime.c =============================================================*/ +extern float Map_TimeCutComputeArrival( Map_Node_t * pNode, Map_Cut_t * pCut, int fPhase, float tWorstCaseLimit ); +extern void Map_TimeCutComputeArrival_rec( Map_Cut_t * pCut, int fPhase ); +extern float Map_TimeComputeArrivalMax( Map_Man_t * p ); +extern void Map_TimeComputeRequiredGlobal( Map_Man_t * p ); +extern void Map_TimeComputeRequired( Map_Man_t * p, float fRequired ); +extern float Map_TimeNodeFanoutDelay( Map_Node_t * pNode, int fPhase ); +extern float Map_TimeCutFanoutDelay( Map_Node_t * pNode, Map_Cut_t * pCut, int fPhase ); +extern float Map_TimeMatchWithInverter( Map_Man_t * p, Map_Match_t * pMatch ); +/*=== mapperTruth.c ===============================================================*/ +extern void Map_MappingTruths( Map_Man_t * pMan ); +extern int Map_TruthsCutDontCare( Map_Man_t * pMan, Map_Cut_t * pCut, unsigned * uTruthDc ); +extern int Map_TruthCountOnes( unsigned * uTruth, int nLeaves ); +extern int Map_TruthDetectTwoFirst( unsigned * uTruth, int nLeaves ); +/*=== mapperUtils.c ===============================================================*/ +extern Map_NodeVec_t * Map_MappingDfs( Map_Man_t * pMan, int fCollectEquiv ); +extern Map_NodeVec_t * Map_MappingDfsNodes( Map_Man_t * pMan, Map_Node_t ** ppNodes, int nNodes, int fEquiv ); + +extern void Map_MappingDfsMarked1_rec( Map_Node_t * pNode, Map_NodeVec_t * vNodes, int fFirst ); +extern void Map_MappingDfsMarked2_rec( Map_Node_t * pNode, Map_NodeVec_t * vNodes, Map_NodeVec_t * vBoundary, int fFirst ); + +extern int Map_MappingCountLevels( Map_Man_t * pMan ); +extern void Map_MappingUnmark( Map_Man_t * pMan ); +extern void Map_MappingMark_rec( Map_Node_t * pNode ); +extern void Map_MappingUnmark_rec( Map_Node_t * pNode ); +extern void Map_MappingPrintOutputArrivals( Map_Man_t * p ); +extern void Map_MappingSetupMask( unsigned uMask[], int nVarsMax ); +extern int Map_MappingNodeIsViolator( Map_Node_t * pNode, Map_Cut_t * pCut, int fPosPol ); +extern float Map_MappingGetAreaFlow( Map_Man_t * p ); +extern void Map_MappingSortByLevel( Map_Man_t * pMan, Map_NodeVec_t * vNodes ); +extern int Map_MappingCountDoubles( Map_Man_t * pMan, Map_NodeVec_t * vNodes ); +extern void Map_MappingExpandTruth( unsigned uTruth[2], int nVars ); +extern float Map_MappingPrintSwitching( Map_Man_t * pMan ); +extern void Map_MappingSetPlacementInfo( Map_Man_t * p ); +extern float Map_MappingPrintWirelength( Map_Man_t * p ); +extern void Map_MappingWireReport( Map_Man_t * p ); +extern float Map_MappingComputeDelayWithFanouts( Map_Man_t * p ); +extern int Map_MappingGetMaxLevel( Map_Man_t * pMan ); +extern void Map_MappingSetChoiceLevels( Map_Man_t * pMan ); +extern void Map_MappingReportChoices( Map_Man_t * pMan ); +/*=== mapperVec.c =============================================================*/ +extern Map_NodeVec_t * Map_NodeVecAlloc( int nCap ); +extern void Map_NodeVecFree( Map_NodeVec_t * p ); +extern Map_Node_t ** Map_NodeVecReadArray( Map_NodeVec_t * p ); +extern int Map_NodeVecReadSize( Map_NodeVec_t * p ); +extern void Map_NodeVecGrow( Map_NodeVec_t * p, int nCapMin ); +extern void Map_NodeVecShrink( Map_NodeVec_t * p, int nSizeNew ); +extern void Map_NodeVecClear( Map_NodeVec_t * p ); +extern void Map_NodeVecPush( Map_NodeVec_t * p, Map_Node_t * Entry ); +extern int Map_NodeVecPushUnique( Map_NodeVec_t * p, Map_Node_t * Entry ); +extern Map_Node_t * Map_NodeVecPop( Map_NodeVec_t * p ); +extern void Map_NodeVecRemove( Map_NodeVec_t * p, Map_Node_t * Entry ); +extern void Map_NodeVecWriteEntry( Map_NodeVec_t * p, int i, Map_Node_t * Entry ); +extern Map_Node_t * Map_NodeVecReadEntry( Map_NodeVec_t * p, int i ); +extern void Map_NodeVecSortByLevel( Map_NodeVec_t * p ); + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/map/mapper/mapperLib.c b/abc_with_bb_support/src/map/mapper/mapperLib.c new file mode 100644 index 000000000..36fe8f5e0 --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/mapperLib.c @@ -0,0 +1,231 @@ +/**CFile**************************************************************** + + FileName [mapperLib.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - June 1, 2004.] + + Revision [$Id: mapperLib.c,v 1.6 2005/01/23 06:59:44 alanmi Exp $] + +***********************************************************************/ + +#include "mapperInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Reads in the supergate library and prepares it for use.] + + Description [The supergates library comes in a .super file. This file + contains descriptions of supergates along with some relevant information. + This procedure reads the supergate file, canonicizes the supergates, + and constructs an additional lookup table, which can be used to map + truth tables of the cuts into the pair (phase, supergate). The phase + indicates how the current truth table should be phase assigned to + match the canonical form of the supergate. The resulting phase is the + bitwise EXOR of the phase needed to canonicize the supergate and the + phase needed to transform the truth table into its canonical form.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_SuperLib_t * Map_SuperLibCreate( char * pFileName, char * pExcludeFile, bool fAlgorithm, bool fVerbose ) +{ + Map_SuperLib_t * p; + int clk; + + // start the supergate library + p = ALLOC( Map_SuperLib_t, 1 ); + memset( p, 0, sizeof(Map_SuperLib_t) ); + p->pName = pFileName; + p->fVerbose = fVerbose; + p->mmSupers = Extra_MmFixedStart( sizeof(Map_Super_t) ); + p->mmEntries = Extra_MmFixedStart( sizeof(Map_HashEntry_t) ); + p->mmForms = Extra_MmFlexStart(); + Map_MappingSetupTruthTables( p->uTruths ); + + // start the hash table + p->tTableC = Map_SuperTableCreate( p ); + p->tTable = Map_SuperTableCreate( p ); + + // read the supergate library from file +clk = clock(); + if ( fAlgorithm ) + { + if ( !Map_LibraryReadTree( p, pFileName, pExcludeFile ) ) + { + Map_SuperLibFree( p ); + return NULL; + } + } + else + { + if ( pExcludeFile != 0 ) + { + printf ("Error: Exclude file support not present for old format. Stop.\n"); + return NULL; + } + if ( !Map_LibraryRead( p, pFileName ) ) + { + Map_SuperLibFree( p ); + return NULL; + } + } + assert( p->nVarsMax > 0 ); + + // report the stats +if ( fVerbose ) { + printf( "Loaded %d unique %d-input supergates from \"%s\". ", + p->nSupersReal, p->nVarsMax, pFileName ); + PRT( "Time", clock() - clk ); +} + + // assign the interver parameters + p->pGateInv = Mio_LibraryReadInv( p->pGenlib ); + p->tDelayInv.Rise = Mio_LibraryReadDelayInvRise( p->pGenlib ); + p->tDelayInv.Fall = Mio_LibraryReadDelayInvFall( p->pGenlib ); + p->tDelayInv.Worst = MAP_MAX( p->tDelayInv.Rise, p->tDelayInv.Fall ); + p->AreaInv = Mio_LibraryReadAreaInv( p->pGenlib ); + p->AreaBuf = Mio_LibraryReadAreaBuf( p->pGenlib ); + + // assign the interver supergate + p->pSuperInv = (Map_Super_t *)Extra_MmFixedEntryFetch( p->mmSupers ); + memset( p->pSuperInv, 0, sizeof(Map_Super_t) ); + p->pSuperInv->Num = -1; + p->pSuperInv->nGates = 1; + p->pSuperInv->nFanins = 1; + p->pSuperInv->nFanLimit = 10; + p->pSuperInv->pFanins[0] = p->ppSupers[0]; + p->pSuperInv->pRoot = p->pGateInv; + p->pSuperInv->Area = p->AreaInv; + p->pSuperInv->tDelayMax = p->tDelayInv; + p->pSuperInv->tDelaysR[0].Rise = MAP_NO_VAR; + p->pSuperInv->tDelaysR[0].Fall = p->tDelayInv.Rise; + p->pSuperInv->tDelaysF[0].Rise = p->tDelayInv.Fall; + p->pSuperInv->tDelaysF[0].Fall = MAP_NO_VAR; + return p; +} + + +/**Function************************************************************* + + Synopsis [Deallocates the supergate library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_SuperLibFree( Map_SuperLib_t * p ) +{ + if ( p == NULL ) return; + if ( p->pGenlib ) + { + assert( p->pGenlib == Abc_FrameReadLibGen() ); + Mio_LibraryDelete( p->pGenlib ); + Abc_FrameSetLibGen( NULL ); + } + if ( p->tTableC ) + Map_SuperTableFree( p->tTableC ); + if ( p->tTable ) + Map_SuperTableFree( p->tTable ); + Extra_MmFixedStop( p->mmSupers ); + Extra_MmFixedStop( p->mmEntries ); + Extra_MmFlexStop( p->mmForms ); + FREE( p->ppSupers ); + FREE( p ); +} + +/**Function************************************************************* + + Synopsis [Derives the library from the genlib library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_SuperLibDeriveFromGenlib( Mio_Library_t * pLib ) +{ + Abc_Frame_t * pAbc = Abc_FrameGetGlobalFrame(); + char * pNameGeneric; + char FileNameGenlib[100]; + char FileNameSuper[100]; + char CommandSuper[500]; + char CommandRead[500]; + FILE * pFile; + + if ( pLib == NULL ) + return 0; + + // write the current library into the file + sprintf( FileNameGenlib, "%s_temp", Mio_LibraryReadName(pLib) ); + pFile = fopen( FileNameGenlib, "w" ); + Mio_WriteLibrary( pFile, pLib, 0 ); + fclose( pFile ); + + // get the file name with the library + pNameGeneric = Extra_FileNameGeneric( Mio_LibraryReadName(pLib) ); + sprintf( FileNameSuper, "%s.super", pNameGeneric ); + free( pNameGeneric ); + + sprintf( CommandSuper, "super -l 1 -i 5 -d 10000000 -a 10000000 -t 100 %s", FileNameGenlib ); + if ( Cmd_CommandExecute( pAbc, CommandSuper ) ) + { + fprintf( stdout, "Cannot execute command \"%s\".\n", CommandSuper ); + return 0; + } +//#ifdef WIN32 +// _unlink( FileNameGenlib ); +//#else +// unlink( FileNameGenlib ); +//#endif + + sprintf( CommandRead, "read_super %s", FileNameSuper ); + if ( Cmd_CommandExecute( pAbc, CommandRead ) ) + { +#ifdef WIN32 + _unlink( FileNameSuper ); +#else + unlink( FileNameSuper ); +#endif + fprintf( stdout, "Cannot execute command \"%s\".\n", CommandRead ); + return 0; + } + +/* // don't remove the intermediate file +#ifdef WIN32 + _unlink( FileNameSuper ); +#else + unlink( FileNameSuper ); +#endif +*/ + return 1; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/mapper/mapperMatch.c b/abc_with_bb_support/src/map/mapper/mapperMatch.c new file mode 100644 index 000000000..6e019257c --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/mapperMatch.c @@ -0,0 +1,596 @@ +/**CFile**************************************************************** + + FileName [mapperMatch.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - June 1, 2004.] + + Revision [$Id: mapperMatch.c,v 1.7 2004/09/30 21:18:10 satrajit Exp $] + +***********************************************************************/ + +#include "mapperInt.h" + +/* + A potential improvement: + When an internal node is not used in the mapping, its required times + are set to be +infinity. So when we recover area, we try to find the + best match for area and completely disregard the delay for the nodes + that are not currently used in the mapping because any match whose + arrival times are less than the required times (+infinity) can be used. + It may be possible to develop a better approach to recover area for + the nodes that are not currently used in the mapping... +*/ + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Map_MatchNodePhase( Map_Man_t * p, Map_Node_t * pNode, int fPhase ); +static int Map_MatchNodeCut( Map_Man_t * p, Map_Node_t * pNode, Map_Cut_t * pCut, int fPhase, float fWorstLimit ); + +static void Map_MappingSetPiArrivalTimes( Map_Man_t * p ); +static void Map_NodeTryDroppingOnePhase( Map_Man_t * p, Map_Node_t * pNode ); +static void Map_NodeTransferArrivalTimes( Map_Man_t * p, Map_Node_t * pNode ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes the best matches of the nodes.] + + Description [Uses parameter p->fMappingMode to decide how to assign + the matches for both polarities of the node. While the matches are + being assigned, one of them may turn out to be better than the other + (in terms of delay, for example). In this case, the worse match can + be permanently dropped, and the corresponding pointer set to NULL.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_MappingMatches( Map_Man_t * p ) +{ + ProgressBar * pProgress; + Map_Node_t * pNode; + int i; + + assert( p->fMappingMode >= 0 && p->fMappingMode <= 4 ); + + // use the externally given PI arrival times + if ( p->fMappingMode == 0 ) + Map_MappingSetPiArrivalTimes( p ); + + // estimate the fanouts + if ( p->fMappingMode == 0 ) + Map_MappingEstimateRefsInit( p ); + else if ( p->fMappingMode == 1 ) + Map_MappingEstimateRefs( p ); + + // the PI cuts are matched in the cut computation package + // in the loop below we match the internal nodes + pProgress = Extra_ProgressBarStart( stdout, p->vAnds->nSize ); + for ( i = 0; i < p->vAnds->nSize; i++ ) + { + // skip primary inputs and secondary nodes if mapping with choices + pNode = p->vAnds->pArray[i]; + if ( !Map_NodeIsAnd( pNode ) || pNode->pRepr ) + continue; + + // make sure that at least one non-trival cut is present + if ( pNode->pCuts->pNext == NULL ) + { + printf( "\nError: A node in the mapping graph does not have feasible cuts.\n" ); + return 0; + } + + // match negative phase + if ( !Map_MatchNodePhase( p, pNode, 0 ) ) + return 0; + // match positive phase + if ( !Map_MatchNodePhase( p, pNode, 1 ) ) + return 0; + + // make sure that at least one phase is mapped + if ( pNode->pCutBest[0] == NULL && pNode->pCutBest[1] == NULL ) + { + printf( "\nError: Could not match both phases of AIG node %d.\n", pNode->Num ); + printf( "Please make sure that the supergate library has equivalents of AND2 or NAND2.\n" ); + printf( "If such supergates exist in the library, report a bug.\n" ); + return 0; + } + + // if both phases are assigned, check if one of them can be dropped + Map_NodeTryDroppingOnePhase( p, pNode ); + // set the arrival times of the node using the best cuts + Map_NodeTransferArrivalTimes( p, pNode ); + + // update the progress bar + Extra_ProgressBarUpdate( pProgress, i, "Matches ..." ); + } + Extra_ProgressBarStop( pProgress ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Find the matching of one polarity of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_MatchNodePhase( Map_Man_t * p, Map_Node_t * pNode, int fPhase ) +{ + Map_Match_t MatchBest, * pMatch; + Map_Cut_t * pCut, * pCutBest; + float Area1, Area2, fWorstLimit; + + // skip the cuts that have been unassigned during area recovery + pCutBest = pNode->pCutBest[fPhase]; + if ( p->fMappingMode != 0 && pCutBest == NULL ) + return 1; + + // recompute the arrival times of the current best match + // because the arrival times of the fanins may have changed + // as a result of remapping fanins in the topological order + if ( p->fMappingMode != 0 ) + { + Map_TimeCutComputeArrival( pNode, pCutBest, fPhase, MAP_FLOAT_LARGE ); + // make sure that the required times are met + assert( pCutBest->M[fPhase].tArrive.Rise < pNode->tRequired[fPhase].Rise + p->fEpsilon ); + assert( pCutBest->M[fPhase].tArrive.Fall < pNode->tRequired[fPhase].Fall + p->fEpsilon ); + } + + // recompute the exact area of the current best match + // because the exact area of the fanins may have changed + // as a result of remapping fanins in the topological order + if ( p->fMappingMode == 2 || p->fMappingMode == 3 ) + { + pMatch = pCutBest->M + fPhase; + if ( pNode->nRefAct[fPhase] > 0 || + (pNode->pCutBest[!fPhase] == NULL && pNode->nRefAct[!fPhase] > 0) ) + pMatch->AreaFlow = Area1 = Map_CutDeref( pCutBest, fPhase ); + else + pMatch->AreaFlow = Area1 = Map_CutGetAreaDerefed( pCutBest, fPhase ); + } + else if ( p->fMappingMode == 4 ) + { + pMatch = pCutBest->M + fPhase; + if ( pNode->nRefAct[fPhase] > 0 || + (pNode->pCutBest[!fPhase] == NULL && pNode->nRefAct[!fPhase] > 0) ) + pMatch->AreaFlow = Area1 = Map_SwitchCutDeref( pNode, pCutBest, fPhase ); + else + pMatch->AreaFlow = Area1 = Map_SwitchCutGetDerefed( pNode, pCutBest, fPhase ); + } + + // save the old mapping + if ( pCutBest ) + MatchBest = pCutBest->M[fPhase]; + else + Map_MatchClean( &MatchBest ); + + // select the new best cut + fWorstLimit = pNode->tRequired[fPhase].Worst; + for ( pCut = pNode->pCuts->pNext; pCut; pCut = pCut->pNext ) + { + pMatch = pCut->M + fPhase; + if ( pMatch->pSupers == NULL ) + continue; + + // find the matches for the cut + Map_MatchNodeCut( p, pNode, pCut, fPhase, fWorstLimit ); + if ( pMatch->pSuperBest == NULL || pMatch->tArrive.Worst > fWorstLimit + p->fEpsilon ) + continue; + + // if the cut can be matched compare the matchings + if ( Map_MatchCompare( p, &MatchBest, pMatch, p->fMappingMode ) ) + { + pCutBest = pCut; + MatchBest = *pMatch; + // if we are mapping for delay, the worst-case limit should be tightened + if ( p->fMappingMode == 0 ) + fWorstLimit = MatchBest.tArrive.Worst; + } + } + + if ( pCutBest == NULL ) + return 1; + + // set the new mapping + pNode->pCutBest[fPhase] = pCutBest; + pCutBest->M[fPhase] = MatchBest; + + // reference the new cut if it used + if ( p->fMappingMode >= 2 && + (pNode->nRefAct[fPhase] > 0 || + (pNode->pCutBest[!fPhase] == NULL && pNode->nRefAct[!fPhase] > 0)) ) + { + if ( p->fMappingMode == 2 || p->fMappingMode == 3 ) + Area2 = Map_CutRef( pNode->pCutBest[fPhase], fPhase ); + else if ( p->fMappingMode == 4 ) + Area2 = Map_SwitchCutRef( pNode, pNode->pCutBest[fPhase], fPhase ); + else + assert( 0 ); + assert( Area2 < Area1 + p->fEpsilon ); + } + + // make sure that the requited times are met + assert( MatchBest.tArrive.Rise < pNode->tRequired[fPhase].Rise + p->fEpsilon ); + assert( MatchBest.tArrive.Fall < pNode->tRequired[fPhase].Fall + p->fEpsilon ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Find the best matching of the cut.] + + Description [The parameters: the node (pNode), the cut (pCut), the phase to be matched + (fPhase), and the upper bound on the arrival times of the cut (fWorstLimit). This + procedure goes through the matching supergates up to the phase assignment, and selects the + best supergate, which will be used to map the cut. As a result of calling this procedure + the matching information is written into pMatch.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_MatchNodeCut( Map_Man_t * p, Map_Node_t * pNode, Map_Cut_t * pCut, int fPhase, float fWorstLimit ) +{ + Map_Match_t MatchBest, * pMatch = pCut->M + fPhase; + Map_Super_t * pSuper; + int i, Counter; + + // save the current match of the cut + MatchBest = *pMatch; + // go through the supergates + for ( pSuper = pMatch->pSupers, Counter = 0; pSuper; pSuper = pSuper->pNext, Counter++ ) + { + p->nMatches++; + // this is an attempt to reduce the runtime of matching and area + // at the cost of rare and very minor increase in delay + // (the supergates are sorted by increasing area) + if ( Counter == 30 ) + break; + + // go through different phases of the given match and supergate + pMatch->pSuperBest = pSuper; + for ( i = 0; i < (int)pSuper->nPhases; i++ ) + { + p->nPhases++; + // find the overall phase of this match + pMatch->uPhaseBest = pMatch->uPhase ^ pSuper->uPhases[i]; + if ( p->fMappingMode == 0 ) + { + // get the arrival time + Map_TimeCutComputeArrival( pNode, pCut, fPhase, fWorstLimit ); + // skip the cut if the arrival times exceed the required times + if ( pMatch->tArrive.Worst > fWorstLimit + p->fEpsilon ) + continue; + // get the area (area flow) + pMatch->AreaFlow = Map_CutGetAreaFlow( pCut, fPhase ); + } + else + { + // get the area (area flow) + if ( p->fMappingMode == 2 || p->fMappingMode == 3 ) + pMatch->AreaFlow = Map_CutGetAreaDerefed( pCut, fPhase ); + else if ( p->fMappingMode == 4 ) + pMatch->AreaFlow = Map_SwitchCutGetDerefed( pNode, pCut, fPhase ); + else + pMatch->AreaFlow = Map_CutGetAreaFlow( pCut, fPhase ); + // skip if the cut is too large + if ( pMatch->AreaFlow > MatchBest.AreaFlow + p->fEpsilon ) + continue; + // get the arrival time + Map_TimeCutComputeArrival( pNode, pCut, fPhase, fWorstLimit ); + // skip the cut if the arrival times exceed the required times + if ( pMatch->tArrive.Worst > fWorstLimit + p->fEpsilon ) + continue; + } + + // if the cut is non-trivial, compare it + if ( Map_MatchCompare( p, &MatchBest, pMatch, p->fMappingMode ) ) + { + MatchBest = *pMatch; + // if we are mapping for delay, the worst-case limit should be reduced + if ( p->fMappingMode == 0 ) + fWorstLimit = MatchBest.tArrive.Worst; + } + } + } + // set the best match + *pMatch = MatchBest; + + // recompute the arrival time and area (area flow) of this cut + if ( pMatch->pSuperBest ) + { + Map_TimeCutComputeArrival( pNode, pCut, fPhase, MAP_FLOAT_LARGE ); + if ( p->fMappingMode == 2 || p->fMappingMode == 3 ) + pMatch->AreaFlow = Map_CutGetAreaDerefed( pCut, fPhase ); + else if ( p->fMappingMode == 4 ) + pMatch->AreaFlow = Map_SwitchCutGetDerefed( pNode, pCut, fPhase ); + else + pMatch->AreaFlow = Map_CutGetAreaFlow( pCut, fPhase ); + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Cleans the match.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MatchClean( Map_Match_t * pMatch ) +{ + memset( pMatch, 0, sizeof(Map_Match_t) ); + pMatch->AreaFlow = MAP_FLOAT_LARGE; // unassigned + pMatch->tArrive.Rise = MAP_FLOAT_LARGE; // unassigned + pMatch->tArrive.Fall = MAP_FLOAT_LARGE; // unassigned + pMatch->tArrive.Worst = MAP_FLOAT_LARGE; // unassigned +} + +/**Function************************************************************* + + Synopsis [Compares two matches.] + + Description [Returns 1 if the second match is better. Otherwise returns 0.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_MatchCompare( Map_Man_t * pMan, Map_Match_t * pM1, Map_Match_t * pM2, int fDoingArea ) +{ + if ( !fDoingArea ) + { + // compare the arrival times + if ( pM1->tArrive.Worst < pM2->tArrive.Worst - pMan->fEpsilon ) + return 0; + if ( pM1->tArrive.Worst > pM2->tArrive.Worst + pMan->fEpsilon ) + return 1; + // compare the areas or area flows + if ( pM1->AreaFlow < pM2->AreaFlow - pMan->fEpsilon ) + return 0; + if ( pM1->AreaFlow > pM2->AreaFlow + pMan->fEpsilon ) + return 1; + // compare the fanout limits + if ( pM1->pSuperBest->nFanLimit > pM2->pSuperBest->nFanLimit ) + return 0; + if ( pM1->pSuperBest->nFanLimit < pM2->pSuperBest->nFanLimit ) + return 1; + // compare the number of leaves + if ( pM1->pSuperBest->nFanins < pM2->pSuperBest->nFanins ) + return 0; + if ( pM1->pSuperBest->nFanins > pM2->pSuperBest->nFanins ) + return 1; + // otherwise prefer the old cut + return 0; + } + else + { + // compare the areas or area flows + if ( pM1->AreaFlow < pM2->AreaFlow - pMan->fEpsilon ) + return 0; + if ( pM1->AreaFlow > pM2->AreaFlow + pMan->fEpsilon ) + return 1; + // compare the arrival times + if ( pM1->tArrive.Worst < pM2->tArrive.Worst - pMan->fEpsilon ) + return 0; + if ( pM1->tArrive.Worst > pM2->tArrive.Worst + pMan->fEpsilon ) + return 1; + // compare the fanout limits + if ( pM1->pSuperBest->nFanLimit > pM2->pSuperBest->nFanLimit ) + return 0; + if ( pM1->pSuperBest->nFanLimit < pM2->pSuperBest->nFanLimit ) + return 1; + // compare the number of leaves + if ( pM1->pSuperBest->nFanins < pM2->pSuperBest->nFanins ) + return 0; + if ( pM1->pSuperBest->nFanins > pM2->pSuperBest->nFanins ) + return 1; + // otherwise prefer the old cut + return 0; + } +} + +/**Function************************************************************* + + Synopsis [Sets the PI arrival times.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingSetPiArrivalTimes( Map_Man_t * p ) +{ + Map_Node_t * pNode; + int i; + for ( i = 0; i < p->nInputs; i++ ) + { + pNode = p->pInputs[i]; + // set the arrival time of the positive phase + pNode->tArrival[1] = p->pInputArrivals[i]; + // set the arrival time of the negative phase + pNode->tArrival[0].Rise = pNode->tArrival[1].Fall + p->pSuperLib->tDelayInv.Rise; + pNode->tArrival[0].Fall = pNode->tArrival[1].Rise + p->pSuperLib->tDelayInv.Fall; + pNode->tArrival[0].Worst = MAP_MAX(pNode->tArrival[0].Rise, pNode->tArrival[0].Fall); + } +} + + +/**Function************************************************************* + + Synopsis [Attempts dropping one phase of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_NodeTryDroppingOnePhase( Map_Man_t * p, Map_Node_t * pNode ) +{ + Map_Match_t * pMatchBest0, * pMatchBest1; + float tWorst0Using1, tWorst1Using0; + int fUsePhase1, fUsePhase0; + + // nothing to do if one of the phases is already dropped + if ( pNode->pCutBest[0] == NULL || pNode->pCutBest[1] == NULL ) + return; + + // do not drop while recovering area flow + if ( p->fMappingMode == 1 )//|| p->fMappingMode == 2 ) + return; + + // get the pointers to the matches of the best cuts + pMatchBest0 = pNode->pCutBest[0]->M + 0; + pMatchBest1 = pNode->pCutBest[1]->M + 1; + + // get the worst arrival times of each phase + // implemented using the other phase with inverter added + tWorst0Using1 = Map_TimeMatchWithInverter( p, pMatchBest1 ); + tWorst1Using0 = Map_TimeMatchWithInverter( p, pMatchBest0 ); + + // consider the case of mapping for delay + if ( p->fMappingMode == 0 ) + { + // if the arrival time of a phase is larger than the arrival time + // of the opposite phase plus the inverter, drop this phase + if ( pMatchBest0->tArrive.Worst > tWorst0Using1 + p->fEpsilon ) + pNode->pCutBest[0] = NULL; + else if ( pMatchBest1->tArrive.Worst > tWorst1Using0 + p->fEpsilon ) + pNode->pCutBest[1] = NULL; + return; + } + + // do not perform replacement if one of the phases is unused + if ( pNode->nRefAct[0] == 0 || pNode->nRefAct[1] == 0 ) + return; + + // check if replacement of each phase is possible using required times + fUsePhase0 = fUsePhase1 = 0; + if ( p->fMappingMode == 2 ) + { + fUsePhase0 = (pNode->tRequired[1].Worst > tWorst1Using0 + 3*p->pSuperLib->tDelayInv.Worst + p->fEpsilon); + fUsePhase1 = (pNode->tRequired[0].Worst > tWorst0Using1 + 3*p->pSuperLib->tDelayInv.Worst + p->fEpsilon); + } + else if ( p->fMappingMode == 3 || p->fMappingMode == 4 ) + { + fUsePhase0 = (pNode->tRequired[1].Worst > tWorst1Using0 + p->fEpsilon); + fUsePhase1 = (pNode->tRequired[0].Worst > tWorst0Using1 + p->fEpsilon); + } + if ( !fUsePhase0 && !fUsePhase1 ) + return; + + // if replacement is possible both ways, use the one that works better + if ( fUsePhase0 && fUsePhase1 ) + { + if ( pMatchBest0->AreaFlow < pMatchBest1->AreaFlow ) + fUsePhase1 = 0; + else + fUsePhase0 = 0; + } + // only one phase should be used + assert( fUsePhase0 ^ fUsePhase1 ); + + // set the corresponding cut to NULL + if ( fUsePhase0 ) + { + // deref phase 1 cut if necessary + if ( p->fMappingMode >= 2 && pNode->nRefAct[1] > 0 ) + Map_CutDeref( pNode->pCutBest[1], 1 ); + // get rid of the cut + pNode->pCutBest[1] = NULL; + // ref phase 0 cut if necessary + if ( p->fMappingMode >= 2 && pNode->nRefAct[0] == 0 ) + Map_CutRef( pNode->pCutBest[0], 0 ); + } + else + { + // deref phase 0 cut if necessary + if ( p->fMappingMode >= 2 && pNode->nRefAct[0] > 0 ) + Map_CutDeref( pNode->pCutBest[0], 0 ); + // get rid of the cut + pNode->pCutBest[0] = NULL; + // ref phase 1 cut if necessary + if ( p->fMappingMode >= 2 && pNode->nRefAct[1] == 0 ) + Map_CutRef( pNode->pCutBest[1], 1 ); + } +} + + +/**Function************************************************************* + + Synopsis [Transfers the arrival times from the best cuts to the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_NodeTransferArrivalTimes( Map_Man_t * p, Map_Node_t * pNode ) +{ + // if both phases are available, set their arrival times + if ( pNode->pCutBest[0] && pNode->pCutBest[1] ) + { + pNode->tArrival[0] = pNode->pCutBest[0]->M[0].tArrive; + pNode->tArrival[1] = pNode->pCutBest[1]->M[1].tArrive; + } + // if only one phase is available, compute the arrival time of other phase + else if ( pNode->pCutBest[0] ) + { + pNode->tArrival[0] = pNode->pCutBest[0]->M[0].tArrive; + pNode->tArrival[1].Rise = pNode->tArrival[0].Fall + p->pSuperLib->tDelayInv.Rise; + pNode->tArrival[1].Fall = pNode->tArrival[0].Rise + p->pSuperLib->tDelayInv.Fall; + pNode->tArrival[1].Worst = MAP_MAX(pNode->tArrival[1].Rise, pNode->tArrival[1].Fall); + } + else if ( pNode->pCutBest[1] ) + { + pNode->tArrival[1] = pNode->pCutBest[1]->M[1].tArrive; + pNode->tArrival[0].Rise = pNode->tArrival[1].Fall + p->pSuperLib->tDelayInv.Rise; + pNode->tArrival[0].Fall = pNode->tArrival[1].Rise + p->pSuperLib->tDelayInv.Fall; + pNode->tArrival[0].Worst = MAP_MAX(pNode->tArrival[0].Rise, pNode->tArrival[0].Fall); + } + else + { + assert( 0 ); + } + + assert( pNode->tArrival[0].Rise < pNode->tRequired[0].Rise + p->fEpsilon ); + assert( pNode->tArrival[0].Fall < pNode->tRequired[0].Fall + p->fEpsilon ); + + assert( pNode->tArrival[1].Rise < pNode->tRequired[1].Rise + p->fEpsilon ); + assert( pNode->tArrival[1].Fall < pNode->tRequired[1].Fall + p->fEpsilon ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/map/mapper/mapperRefs.c b/abc_with_bb_support/src/map/mapper/mapperRefs.c new file mode 100644 index 000000000..b0f2fdda3 --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/mapperRefs.c @@ -0,0 +1,557 @@ +/**CFile**************************************************************** + + FileName [mapperRefs.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - June 1, 2004.] + + Revision [$Id: mapperRefs.h,v 1.0 2003/09/08 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "mapperInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Map_NodeIncRefPhaseAct( Map_Node_t * pNode, int fPhase ); +static int Map_NodeDecRefPhaseAct( Map_Node_t * pNode, int fPhase ); +static float Map_CutRefDeref( Map_Cut_t * pCut, int fPhase, int fReference ); +static void Map_MappingSetRefs_rec( Map_Man_t * pMan, Map_Node_t * pNode, Map_Node_t ** ppStore ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Reads the actual reference counter of a phase.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_NodeReadRefPhaseAct( Map_Node_t * pNode, int fPhase ) +{ + assert( !Map_IsComplement(pNode) ); + if ( pNode->pCutBest[0] && pNode->pCutBest[1] ) // both assigned + return pNode->nRefAct[fPhase]; + assert( pNode->pCutBest[0] || pNode->pCutBest[1] ); // at least one assigned + return pNode->nRefAct[2]; +} + +/**Function************************************************************* + + Synopsis [Reads the estimated reference counter of a phase.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Map_NodeReadRefPhaseEst( Map_Node_t * pNode, int fPhase ) +{ + assert( !Map_IsComplement(pNode) ); + if ( pNode->pCutBest[0] && pNode->pCutBest[1] ) // both assigned + return pNode->nRefEst[fPhase]; + assert( pNode->pCutBest[0] || pNode->pCutBest[1] ); // at least one assigned +// return pNode->nRefEst[0] + pNode->nRefEst[1]; + return pNode->nRefEst[2]; +} + + +/**Function************************************************************* + + Synopsis [Increments the actual reference counter of a phase.] + + Description [Returns the old reference counter.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_NodeIncRefPhaseAct( Map_Node_t * pNode, int fPhase ) +{ + assert( !Map_IsComplement(pNode) ); + if ( pNode->pCutBest[0] && pNode->pCutBest[1] ) // both assigned + return pNode->nRefAct[fPhase]++; + assert( pNode->pCutBest[0] || pNode->pCutBest[1] ); // at least one assigned + return pNode->nRefAct[2]++; +} + +/**Function************************************************************* + + Synopsis [Decrements the actual reference counter of a phase.] + + Description [Returns the new reference counter.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_NodeDecRefPhaseAct( Map_Node_t * pNode, int fPhase ) +{ + assert( !Map_IsComplement(pNode) ); + if ( pNode->pCutBest[0] && pNode->pCutBest[1] ) // both assigned + return --pNode->nRefAct[fPhase]; + assert( pNode->pCutBest[0] || pNode->pCutBest[1] ); // at least one assigned + return --pNode->nRefAct[2]; +} + + +/**Function************************************************************* + + Synopsis [Sets the estimated reference counter for the PIs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingEstimateRefsInit( Map_Man_t * p ) +{ + Map_Node_t * pNode; + int i; + for ( i = 0; i < p->vAnds->nSize; i++ ) + { + pNode = p->vAnds->pArray[i]; +// pNode->nRefEst[0] = pNode->nRefEst[1] = ((float)pNode->nRefs)*(float)2.0; + pNode->nRefEst[0] = pNode->nRefEst[1] = pNode->nRefEst[2] = ((float)pNode->nRefs); + } +} + +/**Function************************************************************* + + Synopsis [Sets the estimated reference counter.] + + Description [When this procedure is called for the first time, + the reference counter is estimated from the AIG. Otherwise, it is + a linear combination of reference counters in the last two iterations.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingEstimateRefs( Map_Man_t * p ) +{ + Map_Node_t * pNode; + int i; + for ( i = 0; i < p->vAnds->nSize; i++ ) + { + pNode = p->vAnds->pArray[i]; +// pNode->nRefEst[0] = (float)((2.0 * pNode->nRefEst[0] + 1.0 * pNode->nRefAct[0]) / 3.0); +// pNode->nRefEst[1] = (float)((2.0 * pNode->nRefEst[1] + 1.0 * pNode->nRefAct[1]) / 3.0); +// pNode->nRefEst[2] = (float)((2.0 * pNode->nRefEst[2] + 1.0 * pNode->nRefAct[2]) / 3.0); + pNode->nRefEst[0] = (float)((3.0 * pNode->nRefEst[0] + 1.0 * pNode->nRefAct[0]) / 4.0); + pNode->nRefEst[1] = (float)((3.0 * pNode->nRefEst[1] + 1.0 * pNode->nRefAct[1]) / 4.0); + pNode->nRefEst[2] = (float)((3.0 * pNode->nRefEst[2] + 1.0 * pNode->nRefAct[2]) / 4.0); + } +} + + + + + +/**function************************************************************* + + synopsis [Computes the area flow of the cut.] + + description [Computes the area flow of the cut if it is implemented using + the best supergate with the best phase.] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Map_CutGetAreaFlow( Map_Cut_t * pCut, int fPhase ) +{ + Map_Match_t * pM = pCut->M + fPhase; + Map_Super_t * pSuper = pM->pSuperBest; + unsigned uPhaseTot = pM->uPhaseBest; + Map_Cut_t * pCutFanin; + float aFlowRes, aFlowFanin, nRefs; + int i, fPinPhasePos; + + // start the resulting area flow + aFlowRes = pSuper->Area; + // iterate through the leaves + for ( i = 0; i < pCut->nLeaves; i++ ) + { + // get the phase of this fanin + fPinPhasePos = ((uPhaseTot & (1 << i)) == 0); + // get the cut implementing this phase of the fanin + pCutFanin = pCut->ppLeaves[i]->pCutBest[fPinPhasePos]; + // if the cut is not available, we have to use the opposite phase + if ( pCutFanin == NULL ) + { + fPinPhasePos = !fPinPhasePos; + pCutFanin = pCut->ppLeaves[i]->pCutBest[fPinPhasePos]; + } + aFlowFanin = pCutFanin->M[fPinPhasePos].AreaFlow; // ignores the area of the interter + // get the fanout count of the cut in the given phase + nRefs = Map_NodeReadRefPhaseEst( pCut->ppLeaves[i], fPinPhasePos ); + // if the node does no fanout, assume fanout count equal to 1 + if ( nRefs == (float)0.0 ) + nRefs = (float)1.0; + // add the area flow due to the fanin + aFlowRes += aFlowFanin / nRefs; + } + pM->AreaFlow = aFlowRes; + return aFlowRes; +} + + + +/**function************************************************************* + + synopsis [Computes the exact area associated with the cut.] + + description [Assumes that the cut is referenced.] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Map_CutGetAreaRefed( Map_Cut_t * pCut, int fPhase ) +{ + float aResult, aResult2; + aResult2 = Map_CutRefDeref( pCut, fPhase, 0 ); // dereference + aResult = Map_CutRefDeref( pCut, fPhase, 1 ); // reference +// assert( aResult == aResult2 ); + return aResult; +} + +/**function************************************************************* + + synopsis [Computes the exact area associated with the cut.] + + description [] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Map_CutGetAreaDerefed( Map_Cut_t * pCut, int fPhase ) +{ + float aResult, aResult2; + aResult2 = Map_CutRefDeref( pCut, fPhase, 1 ); // reference + aResult = Map_CutRefDeref( pCut, fPhase, 0 ); // dereference +// assert( aResult == aResult2 ); + return aResult; +} + +/**function************************************************************* + + synopsis [References the cut.] + + description [] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Map_CutRef( Map_Cut_t * pCut, int fPhase ) +{ + return Map_CutRefDeref( pCut, fPhase, 1 ); // reference +} + +/**function************************************************************* + + synopsis [Dereferences the cut.] + + description [] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Map_CutDeref( Map_Cut_t * pCut, int fPhase ) +{ + return Map_CutRefDeref( pCut, fPhase, 0 ); // dereference +} + +/**function************************************************************* + + synopsis [References or dereferences the cut.] + + description [This reference part is similar to Cudd_NodeReclaim(). + The dereference part is similar to Cudd_RecursiveDeref().] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Map_CutRefDeref( Map_Cut_t * pCut, int fPhase, int fReference ) +{ + Map_Node_t * pNodeChild; + Map_Cut_t * pCutChild; + float aArea; + int i, fPhaseChild; +// int nRefs; + + // consider the elementary variable + if ( pCut->nLeaves == 1 ) + return 0; + // start the area of this cut + aArea = Map_CutGetRootArea( pCut, fPhase ); + // go through the children + for ( i = 0; i < pCut->nLeaves; i++ ) + { + pNodeChild = pCut->ppLeaves[i]; + fPhaseChild = Map_CutGetLeafPhase( pCut, fPhase, i ); + // get the reference counter of the child +/* + // this code does not take inverters into account + // the quality of area recovery seems to always be a little worse + if ( fReference ) + nRefs = Map_NodeIncRefPhaseAct( pNodeChild, fPhaseChild ); + else + nRefs = Map_NodeDecRefPhaseAct( pNodeChild, fPhaseChild ); + assert( nRefs >= 0 ); + // skip if the child was already reference before + if ( nRefs > 0 ) + continue; +*/ + + if ( fReference ) + { + if ( pNodeChild->pCutBest[0] && pNodeChild->pCutBest[1] ) // both phases are present + { + // if this phase of the node is referenced, there is no recursive call + pNodeChild->nRefAct[2]++; + if ( pNodeChild->nRefAct[fPhaseChild]++ > 0 ) + continue; + } + else // only one phase is present + { + // inverter should be added if the phase + // (a) has no reference and (b) is implemented using other phase + if ( pNodeChild->nRefAct[fPhaseChild]++ == 0 && pNodeChild->pCutBest[fPhaseChild] == NULL ) + aArea += pNodeChild->p->pSuperLib->AreaInv; + // if the node is referenced, there is no recursive call + if ( pNodeChild->nRefAct[2]++ > 0 ) + continue; + } + } + else + { + if ( pNodeChild->pCutBest[0] && pNodeChild->pCutBest[1] ) // both phases are present + { + // if this phase of the node is referenced, there is no recursive call + --pNodeChild->nRefAct[2]; + if ( --pNodeChild->nRefAct[fPhaseChild] > 0 ) + continue; + } + else // only one phase is present + { + // inverter should be added if the phase + // (a) has no reference and (b) is implemented using other phase + if ( --pNodeChild->nRefAct[fPhaseChild] == 0 && pNodeChild->pCutBest[fPhaseChild] == NULL ) + aArea += pNodeChild->p->pSuperLib->AreaInv; + // if the node is referenced, there is no recursive call + if ( --pNodeChild->nRefAct[2] > 0 ) + continue; + } + assert( pNodeChild->nRefAct[fPhaseChild] >= 0 ); + } + + // get the child cut + pCutChild = pNodeChild->pCutBest[fPhaseChild]; + // if the child does not have this phase mapped, take the opposite phase + if ( pCutChild == NULL ) + { + fPhaseChild = !fPhaseChild; + pCutChild = pNodeChild->pCutBest[fPhaseChild]; + } + // reference and compute area recursively + aArea += Map_CutRefDeref( pCutChild, fPhaseChild, fReference ); + } + return aArea; +} + + + + +/**Function************************************************************* + + Synopsis [Computes actual reference counters.] + + Description [Collects the nodes used in the mapping in array pMan->vMapping. + Nodes are collected in reverse topological order to facilitate the + computation of required times.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingSetRefs( Map_Man_t * pMan ) +{ + Map_Node_t * pNode, ** ppStore; + int i, fPhase, LevelMax; + + // clean all references + for ( i = 0; i < pMan->vNodesAll->nSize; i++ ) + { + pNode = pMan->vNodesAll->pArray[i]; + pNode->nRefAct[0] = 0; + pNode->nRefAct[1] = 0; + pNode->nRefAct[2] = 0; + } + + // find the largest level of a node + LevelMax = 0; + for ( i = 0; i < pMan->nOutputs; i++ ) + if ( LevelMax < (int)Map_Regular(pMan->pOutputs[i])->Level ) + LevelMax = Map_Regular(pMan->pOutputs[i])->Level; + + // allocate place to store the nodes + ppStore = ALLOC( Map_Node_t *, LevelMax + 1 ); + memset( ppStore, 0, sizeof(Map_Node_t *) * (LevelMax + 1) ); + + // visit nodes reachable from POs in the DFS order through the best cuts + for ( i = 0; i < pMan->nOutputs; i++ ) + { + pNode = pMan->pOutputs[i]; + fPhase = !Map_IsComplement(pNode); + if ( !Map_NodeIsConst(pNode) ) + Map_MappingSetRefs_rec( pMan, pNode, ppStore ); + } + + // reconnect the nodes in reverse topological order + pMan->vMapping->nSize = 0; + for ( i = LevelMax; i >= 0; i-- ) + for ( pNode = ppStore[i]; pNode; pNode = (Map_Node_t *)pNode->pData0 ) + Map_NodeVecPush( pMan->vMapping, pNode ); + free( ppStore ); +} + +/**Function************************************************************* + + Synopsis [Recursively computes the DFS ordering of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingSetRefs_rec( Map_Man_t * pMan, Map_Node_t * pNode, Map_Node_t ** ppStore ) +{ + Map_Cut_t * pCut; + Map_Node_t * pNodeR; + unsigned uPhase; + int i, fPhase, fInvPin; + + // get the regular node and its phase + pNodeR = Map_Regular(pNode); + fPhase = !Map_IsComplement(pNode); + + // add the node to the list of all visited nodes + if ( pNodeR->nRefAct[2]++ == 0 ) +// Map_NodeVecPush( pMan->vMapping, pNodeR ); + pNodeR->pData0 = (char *)ppStore[pNodeR->Level], ppStore[pNodeR->Level] = pNodeR; + + // quit if the node was already visited in this phase + if ( pNodeR->nRefAct[fPhase]++ ) + return; + + // quit if this is a PI node + if ( Map_NodeIsVar(pNodeR) ) + return; + + // get the cut implementing this or opposite polarity + pCut = pNodeR->pCutBest[fPhase]; + if ( pCut == NULL ) + { + fPhase = !fPhase; + pCut = pNodeR->pCutBest[fPhase]; + } + + // visit the transitive fanin + uPhase = pCut->M[fPhase].uPhaseBest; + for ( i = 0; i < pCut->nLeaves; i++ ) + { + fInvPin = ((uPhase & (1 << i)) > 0); + Map_MappingSetRefs_rec( pMan, Map_NotCond(pCut->ppLeaves[i], fInvPin), ppStore ); + } +} + + +/**Function************************************************************* + + Synopsis [Computes the array of mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Map_MappingGetArea( Map_Man_t * pMan, Map_NodeVec_t * vMapping ) +{ + Map_Node_t * pNode; + float Area; + int i; + Area = 0.0; + for ( i = 0; i < vMapping->nSize; i++ ) + { + pNode = vMapping->pArray[i]; + // at least one phase has the best cut assigned + assert( pNode->pCutBest[0] != NULL || pNode->pCutBest[1] != NULL ); + // at least one phase is used in the mapping + assert( pNode->nRefAct[0] > 0 || pNode->nRefAct[1] > 0 ); + // compute the array due to the supergate + if ( Map_NodeIsAnd(pNode) ) + { + // count area of the negative phase + if ( pNode->pCutBest[0] && (pNode->nRefAct[0] > 0 || pNode->pCutBest[1] == NULL) ) + Area += pNode->pCutBest[0]->M[0].pSuperBest->Area; + // count area of the positive phase + if ( pNode->pCutBest[1] && (pNode->nRefAct[1] > 0 || pNode->pCutBest[0] == NULL) ) + Area += pNode->pCutBest[1]->M[1].pSuperBest->Area; + } + // count area of the interver if we need to implement one phase with another phase + if ( (pNode->pCutBest[0] == NULL && pNode->nRefAct[0] > 0) || + (pNode->pCutBest[1] == NULL && pNode->nRefAct[1] > 0) ) + Area += pMan->pSuperLib->AreaInv; + } + // add buffers for each CO driven by a CI + for ( i = 0; i < pMan->nOutputs; i++ ) + if ( Map_NodeIsVar(pMan->pOutputs[i]) && !Map_IsComplement(pMan->pOutputs[i]) ) + Area += pMan->pSuperLib->AreaBuf; + return Area; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/mapper/mapperSuper.c b/abc_with_bb_support/src/map/mapper/mapperSuper.c new file mode 100644 index 000000000..03557c160 --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/mapperSuper.c @@ -0,0 +1,449 @@ +/**CFile**************************************************************** + + FileName [mapperSuper.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - June 1, 2004.] + + Revision [$Id: mapperSuper.c,v 1.6 2005/01/23 06:59:44 alanmi Exp $] + +***********************************************************************/ + +#include "mapperInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Map_LibraryReadFile( Map_SuperLib_t * pLib, FILE * pFile ); +static Map_Super_t * Map_LibraryReadGate( Map_SuperLib_t * pLib, char * pBuffer, int nVars ); +static int Map_LibraryTruthVerify( Map_SuperLib_t * pLib, Map_Super_t * pGate ); +static void Map_LibraryComputeTruth( Map_SuperLib_t * pLib, char * pFormula, unsigned uTruthRes[] ); +static void Map_LibraryComputeTruth_rec( Map_SuperLib_t * pLib, char * pFormula, unsigned uTruthsIn[][2], unsigned uTruthRes[] ); +static void Map_LibraryPrintClasses( Map_SuperLib_t * p ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Reads the supergate library from file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_LibraryRead( Map_SuperLib_t * pLib, char * pFileName ) +{ + FILE * pFile; + int Status; + // read the beginning of the file + assert( pLib->pGenlib == NULL ); + pFile = fopen( pFileName, "r" ); + if ( pFile == NULL ) + { + printf( "Cannot open input file \"%s\".\n", pFileName ); + return 0; + } + Status = Map_LibraryReadFile( pLib, pFile ); + fclose( pFile ); +// Map_LibraryPrintClasses( pLib ); + return Status; +} + + +/**Function************************************************************* + + Synopsis [Reads the library file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_LibraryReadFile( Map_SuperLib_t * pLib, FILE * pFile ) +{ + ProgressBar * pProgress; + char pBuffer[2000]; + FILE * pFileGen; + Map_Super_t * pGate; + char * pTemp, * pLibName; + int nCounter, nGatesTotal; + unsigned uCanon[2]; + + // skip empty and comment lines + while ( fgets( pBuffer, 5000, pFile ) != NULL ) + { + // skip leading spaces + for ( pTemp = pBuffer; *pTemp == ' ' || *pTemp == '\r' || *pTemp == '\n'; pTemp++ ); + // skip comment lines and empty lines + if ( *pTemp != 0 && *pTemp != '#' ) + break; + } + + // get the genlib file name + pLibName = strtok( pTemp, " \t\r\n" ); + if ( strcmp( pLibName, "GATE" ) == 0 ) + { + printf( "The input file \"%s\" looks like a GENLIB file and not a supergate library file.\n", pLib->pName ); + return 0; + } + pFileGen = fopen( pLibName, "r" ); + if ( pFileGen == NULL ) + { + printf( "Cannot open the GENLIB file \"%s\".\n", pLibName ); + return 0; + } + fclose( pFileGen ); + + // read the genlib library + pLib->pGenlib = Mio_LibraryRead( Abc_FrameGetGlobalFrame(), pLibName, 0, 0 ); + if ( pLib->pGenlib == NULL ) + { + printf( "Cannot read GENLIB file \"%s\".\n", pLibName ); + return 0; + } + + // read the number of variables + fscanf( pFile, "%d\n", &pLib->nVarsMax ); + if ( pLib->nVarsMax < 2 || pLib->nVarsMax > 10 ) + { + printf( "Suspicious number of variables (%d).\n", pLib->nVarsMax ); + return 0; + } + + // read the number of gates + fscanf( pFile, "%d\n", &nGatesTotal ); + if ( nGatesTotal < 1 || nGatesTotal > 10000000 ) + { + printf( "Suspicious number of gates (%d).\n", nGatesTotal ); + return 0; + } + + // read the lines + nCounter = 0; + pProgress = Extra_ProgressBarStart( stdout, nGatesTotal ); + while ( fgets( pBuffer, 5000, pFile ) != NULL ) + { + for ( pTemp = pBuffer; *pTemp == ' ' || *pTemp == '\r' || *pTemp == '\n'; pTemp++ ); + if ( pTemp[0] == '\0' ) + continue; + // get the gate + pGate = Map_LibraryReadGate( pLib, pTemp, pLib->nVarsMax ); + assert( pGate->Num == nCounter + 1 ); + // count the number of parantheses in the formula - this is the number of gates + for ( pTemp = pGate->pFormula; *pTemp; pTemp++ ) + pGate->nGates += (*pTemp == '('); + // verify the truth table + assert( Map_LibraryTruthVerify(pLib, pGate) ); + + // find the N-canonical form of this supergate + pGate->nPhases = Map_CanonComputeSlow( pLib->uTruths, pLib->nVarsMax, pLib->nVarsMax, pGate->uTruth, pGate->uPhases, uCanon ); + // add the supergate into the table by its N-canonical table + Map_SuperTableInsertC( pLib->tTableC, uCanon, pGate ); + // update the progress bar + Extra_ProgressBarUpdate( pProgress, ++nCounter, NULL ); + } + Extra_ProgressBarStop( pProgress ); + pLib->nSupersAll = nCounter; + if ( nCounter != nGatesTotal ) + printf( "The number of gates read (%d) is different what the file says (%d).\n", nGatesTotal, nCounter ); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Super_t * Map_LibraryReadGate( Map_SuperLib_t * pLib, char * pBuffer, int nVars ) +{ + Map_Super_t * pGate; + char * pTemp; + int i; + + // start and clean the gate + pGate = (Map_Super_t *)Extra_MmFixedEntryFetch( pLib->mmSupers ); + memset( pGate, 0, sizeof(Map_Super_t) ); + + // read the number + pTemp = strtok( pBuffer, " " ); + pGate->Num = atoi(pTemp); + + // read the signature + pTemp = strtok( NULL, " " ); + if ( pLib->nVarsMax < 6 ) + { + pGate->uTruth[0] = Extra_ReadBinary(pTemp); + pGate->uTruth[1] = 0; + } + else + { + pGate->uTruth[0] = Extra_ReadBinary(pTemp+32); + pTemp[32] = 0; + pGate->uTruth[1] = Extra_ReadBinary(pTemp); + } + + // read the max delay + pTemp = strtok( NULL, " " ); + pGate->tDelayMax.Rise = (float)atof(pTemp); + pGate->tDelayMax.Fall = pGate->tDelayMax.Rise; + + // read the pin-to-pin delay + for ( i = 0; i < nVars; i++ ) + { + pTemp = strtok( NULL, " " ); + pGate->tDelaysR[i].Rise = (float)atof(pTemp); + pGate->tDelaysF[i].Fall = pGate->tDelaysR[i].Rise; + } + + // read the area + pTemp = strtok( NULL, " " ); + pGate->Area = (float)atof(pTemp); + + // the rest is the gate name + pTemp = strtok( NULL, " \r\n" ); + if ( strlen(pTemp) == 0 ) + printf( "A gate name is empty.\n" ); + + // save the gate name + pGate->pFormula = Extra_MmFlexEntryFetch( pLib->mmForms, strlen(pTemp) + 1 ); + strcpy( pGate->pFormula, pTemp ); + + // the rest is the gate name + pTemp = strtok( NULL, " \n\0" ); + if ( pTemp != NULL ) + printf( "The following trailing symbols found \"%s\".\n", pTemp ); + return pGate; +} + +/**Function************************************************************* + + Synopsis [Performs one step of parsing the formula into parts.] + + Description [This function will eventually be replaced when the + tree-supergate library representation will become standard.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Map_LibraryReadFormulaStep( char * pFormula, char * pStrings[], int * pnStrings ) +{ + char * pName, * pPar1, * pPar2, * pCur; + int nStrings, CountPars; + + // skip leading spaces + for ( pName = pFormula; *pName && *pName == ' '; pName++ ); + assert( *pName ); + // find the first opening paranthesis + for ( pPar1 = pName; *pPar1 && *pPar1 != '('; pPar1++ ); + if ( *pPar1 == 0 ) + { + *pnStrings = 0; + return pName; + } + // overwrite it with space + assert( *pPar1 == '(' ); + *pPar1 = 0; + // find the corresponding closing paranthesis + for ( CountPars = 1, pPar2 = pPar1 + 1; *pPar2 && CountPars; pPar2++ ) + if ( *pPar2 == '(' ) + CountPars++; + else if ( *pPar2 == ')' ) + CountPars--; + pPar2--; + assert( CountPars == 0 ); + // overwrite it with space + assert( *pPar2 == ')' ); + *pPar2 = 0; + // save the intervals between the commas + nStrings = 0; + pCur = pPar1 + 1; + while ( 1 ) + { + // save the current string + pStrings[ nStrings++ ] = pCur; + // find the beginning of the next string + for ( CountPars = 0; *pCur && (CountPars || *pCur != ','); pCur++ ) + if ( *pCur == '(' ) + CountPars++; + else if ( *pCur == ')' ) + CountPars--; + if ( *pCur == 0 ) + break; + assert( *pCur == ',' ); + *pCur = 0; + pCur++; + } + // save the results and return + *pnStrings = nStrings; + return pName; +} + + +/**Function************************************************************* + + Synopsis [Verifies the truth table of the supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_LibraryTruthVerify( Map_SuperLib_t * pLib, Map_Super_t * pGate ) +{ + unsigned uTruthRes[2]; + Map_LibraryComputeTruth( pLib, pGate->pFormula, uTruthRes ); + if ( uTruthRes[0] != pGate->uTruth[0] || uTruthRes[1] != pGate->uTruth[1] ) + return 0; + return 1; +} + + +/**Function************************************************************* + + Synopsis [Derives the functionality of the supergate.] + + Description [This procedure is useful for verification the supergate + library. The truth table derived by this procedure should be the same + as the one contained in the original supergate file.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_LibraryComputeTruth( Map_SuperLib_t * pLib, char * pFormula, unsigned uTruthRes[] ) +{ + char Buffer[1000]; + strcpy( Buffer, pFormula ); + Map_LibraryComputeTruth_rec( pLib, Buffer, pLib->uTruths, uTruthRes ); +} + +/**Function************************************************************* + + Synopsis [Derives the functionality of the supergate.] + + Description [This procedure is useful for verification the supergate + library. The truth table derived by this procedure should be the same + as the one contained in the original supergate file.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_LibraryComputeTruth_rec( Map_SuperLib_t * pLib, char * pFormula, unsigned uTruthsIn[][2], unsigned uTruthRes[] ) +{ + Mio_Gate_t * pMioGate; + char * pGateName, * pStrings[6]; + unsigned uTruthsFanins[6][2]; + int nStrings, i; + + // perform one step parsing of the formula + // detect the root gate name, the next-step strings, and their number + pGateName = Map_LibraryReadFormulaStep( pFormula, pStrings, &nStrings ); + if ( nStrings == 0 ) // elementary variable + { + assert( pGateName[0] - 'a' < pLib->nVarsMax ); + uTruthRes[0] = uTruthsIn[pGateName[0] - 'a'][0]; + uTruthRes[1] = uTruthsIn[pGateName[0] - 'a'][1]; + return; + } + // derive the functionality of the fanins + for ( i = 0; i < nStrings; i++ ) + Map_LibraryComputeTruth_rec( pLib, pStrings[i], uTruthsIn, uTruthsFanins[i] ); + // get the root supergate + pMioGate = Mio_LibraryReadGateByName( pLib->pGenlib, pGateName ); + if ( pMioGate == NULL ) + printf( "A supergate contains gate \"%s\" that is not in \"%s\".\n", pGateName, Mio_LibraryReadName(pLib->pGenlib) ); + // derive the functionality of the output of the supergate + Mio_DeriveTruthTable( pMioGate, uTruthsFanins, nStrings, pLib->nVarsMax, uTruthRes ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_LibraryPrintSupergate( Map_Super_t * pGate ) +{ + printf( "%5d : ", pGate->nUsed ); + printf( "%5d ", pGate->Num ); + printf( "A = %5.2f ", pGate->Area ); + printf( "D = %5.2f ", pGate->tDelayMax ); + printf( "%s", pGate->pFormula ); + printf( "\n" ); +} + + +/**Function************************************************************* + + Synopsis [Prints N-classes of supergates.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_LibraryPrintClasses( Map_SuperLib_t * p ) +{ +/* + st_generator * gen; + Map_Super_t * pSuper, * pSuper2; + unsigned Key, uTruth; + int Counter = 0; + // copy all the supergates into one array + st_foreach_item( p->tSuplib, gen, (char **)&Key, (char **)&pSuper ) + { + for ( pSuper2 = pSuper; pSuper2; pSuper2 = pSuper2->pNext ) + { + uTruth = pSuper2->Phase; + Extra_PrintBinary( stdout, &uTruth, 5 ); + printf( " %5d ", pSuper2->Num ); + printf( "%s", pSuper2->pFormula ); + printf( "\n" ); + } + printf( "\n" ); + if ( ++ Counter == 100 ) + break; + } +*/ +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/mapper/mapperSwitch.c b/abc_with_bb_support/src/map/mapper/mapperSwitch.c new file mode 100644 index 000000000..8eadf0799 --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/mapperSwitch.c @@ -0,0 +1,223 @@ +/**CFile**************************************************************** + + FileName [mapperSwitch.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 8, 2003.] + + Revision [$Id: mapperSwitch.h,v 1.0 2003/09/08 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "mapperInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static float Map_SwitchCutRefDeref( Map_Node_t * pNode, Map_Cut_t * pCut, int fPhase, int fReference ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**function************************************************************* + + synopsis [Computes the exact area associated with the cut.] + + description [] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Map_SwitchCutGetDerefed( Map_Node_t * pNode, Map_Cut_t * pCut, int fPhase ) +{ + float aResult, aResult2; +// assert( pNode->Switching > 0 ); + aResult2 = Map_SwitchCutRefDeref( pNode, pCut, fPhase, 1 ); // reference + aResult = Map_SwitchCutRefDeref( pNode, pCut, fPhase, 0 ); // dereference +// assert( aResult == aResult2 ); + return aResult; +} + +/**function************************************************************* + + synopsis [References the cut.] + + description [] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Map_SwitchCutRef( Map_Node_t * pNode, Map_Cut_t * pCut, int fPhase ) +{ + return Map_SwitchCutRefDeref( pNode, pCut, fPhase, 1 ); // reference +} + +/**function************************************************************* + + synopsis [References the cut.] + + description [] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Map_SwitchCutDeref( Map_Node_t * pNode, Map_Cut_t * pCut, int fPhase ) +{ + return Map_SwitchCutRefDeref( pNode, pCut, fPhase, 0 ); // dereference +} + +/**function************************************************************* + + synopsis [References or dereferences the cut.] + + description [This reference part is similar to Cudd_NodeReclaim(). + The dereference part is similar to Cudd_RecursiveDeref().] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Map_SwitchCutRefDeref( Map_Node_t * pNode, Map_Cut_t * pCut, int fPhase, int fReference ) +{ + Map_Node_t * pNodeChild; + Map_Cut_t * pCutChild; + float aSwitchActivity; + int i, fPhaseChild; + + // start switching activity for the node + aSwitchActivity = pNode->Switching; + // consider the elementary variable + if ( pCut->nLeaves == 1 ) + return aSwitchActivity; + + // go through the children + assert( pCut->M[fPhase].pSuperBest ); + for ( i = 0; i < pCut->nLeaves; i++ ) + { + pNodeChild = pCut->ppLeaves[i]; + fPhaseChild = Map_CutGetLeafPhase( pCut, fPhase, i ); + // get the reference counter of the child + + if ( fReference ) + { + if ( pNodeChild->pCutBest[0] && pNodeChild->pCutBest[1] ) // both phases are present + { + // if this phase of the node is referenced, there is no recursive call + pNodeChild->nRefAct[2]++; + if ( pNodeChild->nRefAct[fPhaseChild]++ > 0 ) + continue; + } + else // only one phase is present + { + // inverter should be added if the phase + // (a) has no reference and (b) is implemented using other phase + if ( pNodeChild->nRefAct[fPhaseChild]++ == 0 && pNodeChild->pCutBest[fPhaseChild] == NULL ) + aSwitchActivity += pNodeChild->Switching; // inverter switches the same as the node + // if the node is referenced, there is no recursive call + if ( pNodeChild->nRefAct[2]++ > 0 ) + continue; + } + } + else + { + if ( pNodeChild->pCutBest[0] && pNodeChild->pCutBest[1] ) // both phases are present + { + // if this phase of the node is referenced, there is no recursive call + --pNodeChild->nRefAct[2]; + if ( --pNodeChild->nRefAct[fPhaseChild] > 0 ) + continue; + } + else // only one phase is present + { + // inverter should be added if the phase + // (a) has no reference and (b) is implemented using other phase + if ( --pNodeChild->nRefAct[fPhaseChild] == 0 && pNodeChild->pCutBest[fPhaseChild] == NULL ) + aSwitchActivity += pNodeChild->Switching; // inverter switches the same as the node + // if the node is referenced, there is no recursive call + if ( --pNodeChild->nRefAct[2] > 0 ) + continue; + } + assert( pNodeChild->nRefAct[fPhaseChild] >= 0 ); + } + + // get the child cut + pCutChild = pNodeChild->pCutBest[fPhaseChild]; + // if the child does not have this phase mapped, take the opposite phase + if ( pCutChild == NULL ) + { + fPhaseChild = !fPhaseChild; + pCutChild = pNodeChild->pCutBest[fPhaseChild]; + } + // reference and compute area recursively + aSwitchActivity += Map_SwitchCutRefDeref( pNodeChild, pCutChild, fPhaseChild, fReference ); + } + return aSwitchActivity; +} + +/**Function************************************************************* + + Synopsis [Computes the array of mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Map_MappingGetSwitching( Map_Man_t * pMan, Map_NodeVec_t * vMapping ) +{ + Map_Node_t * pNode; + float Switch; + int i; + Switch = 0.0; + for ( i = 0; i < vMapping->nSize; i++ ) + { + pNode = vMapping->pArray[i]; + // at least one phase has the best cut assigned + assert( pNode->pCutBest[0] != NULL || pNode->pCutBest[1] != NULL ); + // at least one phase is used in the mapping + assert( pNode->nRefAct[0] > 0 || pNode->nRefAct[1] > 0 ); + // compute the array due to the supergate + if ( Map_NodeIsAnd(pNode) ) + { + // count switching of the negative phase + if ( pNode->pCutBest[0] && (pNode->nRefAct[0] > 0 || pNode->pCutBest[1] == NULL) ) + Switch += pNode->Switching; + // count switching of the positive phase + if ( pNode->pCutBest[1] && (pNode->nRefAct[1] > 0 || pNode->pCutBest[0] == NULL) ) + Switch += pNode->Switching; + } + // count switching of the interver if we need to implement one phase with another phase + if ( (pNode->pCutBest[0] == NULL && pNode->nRefAct[0] > 0) || + (pNode->pCutBest[1] == NULL && pNode->nRefAct[1] > 0) ) + Switch += pNode->Switching; // inverter switches the same as the node + } + // add buffers for each CO driven by a CI + for ( i = 0; i < pMan->nOutputs; i++ ) + if ( Map_NodeIsVar(pMan->pOutputs[i]) && !Map_IsComplement(pMan->pOutputs[i]) ) + Switch += pMan->pOutputs[i]->Switching; + return Switch; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/mapper/mapperTable.c b/abc_with_bb_support/src/map/mapper/mapperTable.c new file mode 100644 index 000000000..d1bac36de --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/mapperTable.c @@ -0,0 +1,402 @@ +/**CFile**************************************************************** + + FileName [mapperTable.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - June 1, 2004.] + + Revision [$Id: mapperTable.c,v 1.6 2005/01/23 06:59:44 alanmi Exp $] + +***********************************************************************/ + +#include "mapperInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// the table function for the tables +#define MAP_TABLE_HASH(u1,u2,nSize) (((u1) + 2003 * (u2)) % nSize) + +static void Map_SuperTableResize( Map_HashTable_t * pLib ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Creates the hash table for supergates.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_HashTable_t * Map_SuperTableCreate( Map_SuperLib_t * pLib ) +{ + Map_HashTable_t * p; + // allocate the table + p = ALLOC( Map_HashTable_t, 1 ); + memset( p, 0, sizeof(Map_HashTable_t) ); + p->mmMan = pLib->mmEntries; + // allocate and clean the bins + p->nBins = Cudd_Prime(20000); + p->pBins = ALLOC( Map_HashEntry_t *, p->nBins ); + memset( p->pBins, 0, sizeof(Map_HashEntry_t *) * p->nBins ); + return p; +} + + +/**Function************************************************************* + + Synopsis [Deallocates the supergate hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_SuperTableFree( Map_HashTable_t * p ) +{ + FREE( p->pBins ); + FREE( p ); +} + +/**Function************************************************************* + + Synopsis [Inserts a new entry into the hash table.] + + Description [This function inserts the new gate (pGate), which will be + accessible through its canonical form (uTruthC).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_SuperTableInsertC( Map_HashTable_t * p, unsigned uTruthC[], Map_Super_t * pGate ) +{ + Map_HashEntry_t * pEnt; + unsigned Key; + // resize the table + if ( p->nEntries >= 2 * p->nBins ) + Map_SuperTableResize( p ); + // check if another supergate with the same canonical form exists + Key = MAP_TABLE_HASH( uTruthC[0], uTruthC[1], p->nBins ); + for ( pEnt = p->pBins[Key]; pEnt; pEnt = pEnt->pNext ) + if ( pEnt->uTruth[0] == uTruthC[0] && pEnt->uTruth[1] == uTruthC[1] ) + break; + // create a new entry if it does not exist + if ( pEnt == NULL ) + { + // add the new entry to the table + pEnt = (Map_HashEntry_t *)Extra_MmFixedEntryFetch( p->mmMan ); + memset( pEnt, 0, sizeof(Map_HashEntry_t) ); + pEnt->uTruth[0] = uTruthC[0]; + pEnt->uTruth[1] = uTruthC[1]; + // add the hash table entry to the corresponding linked list in the table + pEnt->pNext = p->pBins[Key]; + p->pBins[Key] = pEnt; + p->nEntries++; + } + // add the supergate to the entry + pGate->pNext = pEnt->pGates; + pEnt->pGates = pGate; + return 0; +} + + + +/**Function************************************************************* + + Synopsis [Inserts a new entry into the library.] + + Description [This function inserts the new gate (pGate), which will be + accessible through its unfolded function (uTruth).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_SuperTableInsert( Map_HashTable_t * p, unsigned uTruth[], Map_Super_t * pGate, unsigned uPhase ) +{ + Map_HashEntry_t * pEnt; + unsigned Key; + // resize the table + if ( p->nEntries >= 2 * p->nBins ) + Map_SuperTableResize( p ); + // check if this entry already exists + Key = MAP_TABLE_HASH( uTruth[0], uTruth[1], p->nBins ); + for ( pEnt = p->pBins[Key]; pEnt; pEnt = pEnt->pNext ) + if ( pEnt->uTruth[0] == uTruth[0] && pEnt->uTruth[1] == uTruth[1] ) + return 1; + // add the new hash table entry to the table + pEnt = (Map_HashEntry_t *)Extra_MmFixedEntryFetch( p->mmMan ); + memset( pEnt, 0, sizeof(Map_HashEntry_t) ); + pEnt->uTruth[0] = uTruth[0]; + pEnt->uTruth[1] = uTruth[1]; + pEnt->pGates = pGate; + pEnt->uPhase = uPhase; + // add the hash table to the corresponding linked list in the table + pEnt->pNext = p->pBins[Key]; + p->pBins[Key] = pEnt; + p->nEntries++; +/* +printf( "Adding gate: %10u ", Key ); +Map_LibraryPrintSupergate( pGate ); +Extra_PrintBinary( stdout, uTruth, 32 ); +printf( "\n" ); +*/ + return 0; +} + +/**Function************************************************************* + + Synopsis [Looks up an entry in the library.] + + Description [This function looks up the function, given by its truth table, + and return two things: (1) the linked list of supergates, which can implement + the functions of this N-class; (2) the phase, which should be applied to the + given function, in order to derive the canonical form of this N-class.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Super_t * Map_SuperTableLookupC( Map_SuperLib_t * p, unsigned uTruth[] ) +{ + Map_HashEntry_t * pEnt; + unsigned Key; + Key = MAP_TABLE_HASH( uTruth[0], uTruth[1], p->tTableC->nBins ); + for ( pEnt = p->tTableC->pBins[Key]; pEnt; pEnt = pEnt->pNext ) + if ( pEnt->uTruth[0] == uTruth[0] && pEnt->uTruth[1] == uTruth[1] ) + return pEnt->pGates; + return NULL; +} + +/**Function************************************************************* + + Synopsis [Looks up an entry in the library.] + + Description [This function looks up the function, given by its truth table, + and return two things: (1) the linked list of supergates, which can implement + the functions of this N-class; (2) the phase, which should be applied to the + given function, in order to derive the canonical form of this N-class.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Super_t * Map_SuperTableLookup( Map_HashTable_t * p, unsigned uTruth[], unsigned * puPhase ) +{ + Map_HashEntry_t * pEnt; + unsigned Key; + Key = MAP_TABLE_HASH( uTruth[0], uTruth[1], p->nBins ); + for ( pEnt = p->pBins[Key]; pEnt; pEnt = pEnt->pNext ) + if ( pEnt->uTruth[0] == uTruth[0] && pEnt->uTruth[1] == uTruth[1] ) + { + *puPhase = pEnt->uPhase; + return pEnt->pGates; + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [Resizes the table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_SuperTableResize( Map_HashTable_t * p ) +{ + Map_HashEntry_t ** pBinsNew; + Map_HashEntry_t * pEnt, * pEnt2; + int nBinsNew, Counter, i, clk = clock(); + unsigned Key; + // get the new table size + nBinsNew = Cudd_Prime(2 * p->nBins); + // allocate a new array + pBinsNew = ALLOC( Map_HashEntry_t *, nBinsNew ); + memset( pBinsNew, 0, sizeof(Map_HashEntry_t *) * nBinsNew ); + // rehash the entries from the old table + Counter = 0; + for ( i = 0; i < p->nBins; i++ ) + for ( pEnt = p->pBins[i], pEnt2 = pEnt? pEnt->pNext: NULL; pEnt; + pEnt = pEnt2, pEnt2 = pEnt? pEnt->pNext: NULL ) + { + Key = MAP_TABLE_HASH( pEnt->uTruth[0], pEnt->uTruth[1], nBinsNew ); + pEnt->pNext = pBinsNew[Key]; + pBinsNew[Key] = pEnt; + Counter++; + } + assert( Counter == p->nEntries ); + // replace the table and the parameters + free( p->pBins ); + p->pBins = pBinsNew; + p->nBins = nBinsNew; +} + +/**Function************************************************************* + + Synopsis [Compares the supergates by the number of times they are used.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_SuperTableCompareSupergates( Map_Super_t ** ppS1, Map_Super_t ** ppS2 ) +{ + if ( (*ppS1)->nUsed > (*ppS2)->nUsed ) + return -1; + if ( (*ppS1)->nUsed < (*ppS2)->nUsed ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Compares the supergates by the number of times they are used.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_SuperTableCompareGatesInList( Map_Super_t ** ppS1, Map_Super_t ** ppS2 ) +{ +// if ( (*ppS1)->tDelayMax.Rise > (*ppS2)->tDelayMax.Rise ) + if ( (*ppS1)->Area > (*ppS2)->Area ) + return -1; +// if ( (*ppS1)->tDelayMax.Rise < (*ppS2)->tDelayMax.Rise ) + if ( (*ppS1)->Area < (*ppS2)->Area ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Sorts supergates by usefulness and prints out most useful.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_SuperTableSortSupergates( Map_HashTable_t * p, int nSupersMax ) +{ + Map_HashEntry_t * pEnt; + Map_Super_t ** ppSupers; + Map_Super_t * pSuper; + int nSupers, i; + + // copy all the supergates into one array + ppSupers = ALLOC( Map_Super_t *, nSupersMax ); + nSupers = 0; + for ( i = 0; i < p->nBins; i++ ) + for ( pEnt = p->pBins[i]; pEnt; pEnt = pEnt->pNext ) + for ( pSuper = pEnt->pGates; pSuper; pSuper = pSuper->pNext ) + ppSupers[nSupers++] = pSuper; + + // sort by usage + qsort( (void *)ppSupers, nSupers, sizeof(Map_Super_t *), + (int (*)(const void *, const void *)) Map_SuperTableCompareSupergates ); + assert( Map_SuperTableCompareSupergates( ppSupers, ppSupers + nSupers - 1 ) <= 0 ); + + // print out the "top ten" +// for ( i = 0; i < nSupers; i++ ) + for ( i = 0; i < 10; i++ ) + { + if ( ppSupers[i]->nUsed == 0 ) + break; + printf( "%5d : ", ppSupers[i]->nUsed ); + printf( "%5d ", ppSupers[i]->Num ); + printf( "A = %5.2f ", ppSupers[i]->Area ); + printf( "D = %5.2f ", ppSupers[i]->tDelayMax.Rise ); + printf( "%s", ppSupers[i]->pFormula ); + printf( "\n" ); + } + free( ppSupers ); +} + +/**Function************************************************************* + + Synopsis [Sorts supergates by max delay for each truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_SuperTableSortSupergatesByDelay( Map_HashTable_t * p, int nSupersMax ) +{ + Map_HashEntry_t * pEnt; + Map_Super_t ** ppSupers; + Map_Super_t * pSuper; + int nSupers, i, k; + + ppSupers = ALLOC( Map_Super_t *, nSupersMax ); + for ( i = 0; i < p->nBins; i++ ) + for ( pEnt = p->pBins[i]; pEnt; pEnt = pEnt->pNext ) + { + // collect the gates in this entry + nSupers = 0; + for ( pSuper = pEnt->pGates; pSuper; pSuper = pSuper->pNext ) + { + // skip supergates, whose root is the AND gate +// if ( strcmp( Mio_GateReadName(pSuper->pRoot), "and" ) == 0 ) +// continue; + ppSupers[nSupers++] = pSuper; + } + pEnt->pGates = NULL; + if ( nSupers == 0 ) + continue; + // sort the gates by delay + qsort( (void *)ppSupers, nSupers, sizeof(Map_Super_t *), + (int (*)(const void *, const void *)) Map_SuperTableCompareGatesInList ); + assert( Map_SuperTableCompareGatesInList( ppSupers, ppSupers + nSupers - 1 ) <= 0 ); + // link them in the reverse order + for ( k = 0; k < nSupers; k++ ) + { + ppSupers[k]->pNext = pEnt->pGates; + pEnt->pGates = ppSupers[k]; + } + // save the number of supergates in the list + pEnt->pGates->nSupers = nSupers; + } + FREE( ppSupers ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/mapper/mapperTime.c b/abc_with_bb_support/src/map/mapper/mapperTime.c new file mode 100644 index 000000000..619549769 --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/mapperTime.c @@ -0,0 +1,510 @@ +/**CFile**************************************************************** + + FileName [mapperTime.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - June 1, 2004.] + + Revision [$Id: mapperTime.c,v 1.3 2005/03/02 02:35:54 alanmi Exp $] + +***********************************************************************/ + +#include "mapperInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Map_TimePropagateRequired( Map_Man_t * p, Map_NodeVec_t * vNodes ); +static void Map_TimePropagateRequiredPhase( Map_Man_t * p, Map_Node_t * pNode, int fPhase ); +static float Map_MatchComputeReqTimes( Map_Cut_t * pCut, int fPhase, Map_Time_t * ptArrRes ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**function************************************************************* + + synopsis [Computes the exact area associated with the cut.] + + description [] + + sideeffects [] + + seealso [] + +***********************************************************************/ +float Map_TimeMatchWithInverter( Map_Man_t * p, Map_Match_t * pMatch ) +{ + Map_Time_t tArrInv; + tArrInv.Fall = pMatch->tArrive.Rise + p->pSuperLib->tDelayInv.Fall; + tArrInv.Rise = pMatch->tArrive.Fall + p->pSuperLib->tDelayInv.Rise; + tArrInv.Worst = MAP_MAX( tArrInv.Rise, tArrInv.Fall ); + return tArrInv.Worst; +} + +/**Function************************************************************* + + Synopsis [Computes the arrival times of the cut recursively.] + + Description [When computing the arrival time for the previously unused + cuts, their arrival time may be incorrect because their fanins have + incorrect arrival time. This procedure is called to fix this problem.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_TimeCutComputeArrival_rec( Map_Cut_t * pCut, int fPhase ) +{ + int i, fPhaseLeaf; + for ( i = 0; i < pCut->nLeaves; i++ ) + { + fPhaseLeaf = Map_CutGetLeafPhase( pCut, fPhase, i ); + if ( pCut->ppLeaves[i]->nRefAct[fPhaseLeaf] > 0 ) + continue; + Map_TimeCutComputeArrival_rec( pCut->ppLeaves[i]->pCutBest[fPhaseLeaf], fPhaseLeaf ); + } + Map_TimeCutComputeArrival( NULL, pCut, fPhase, MAP_FLOAT_LARGE ); +} + +/**Function************************************************************* + + Synopsis [Computes the arrival times of the cut.] + + Description [Computes the arrival times of the cut if it is implemented using + the given supergate with the given phase. Uses the constraint-type specification + of rise/fall arrival times.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Map_TimeCutComputeArrival( Map_Node_t * pNode, Map_Cut_t * pCut, int fPhase, float tWorstLimit ) +{ + Map_Match_t * pM = pCut->M + fPhase; + Map_Super_t * pSuper = pM->pSuperBest; + unsigned uPhaseTot = pM->uPhaseBest; + Map_Time_t * ptArrRes = &pM->tArrive; + Map_Time_t * ptArrIn; + bool fPinPhase; + float tDelay; + int i; + + ptArrRes->Rise = ptArrRes->Fall = 0.0; + ptArrRes->Worst = MAP_FLOAT_LARGE; + for ( i = pCut->nLeaves - 1; i >= 0; i-- ) + { + // get the phase of the given pin + fPinPhase = ((uPhaseTot & (1 << i)) == 0); + ptArrIn = pCut->ppLeaves[i]->tArrival + fPinPhase; + + // get the rise of the output due to rise of the inputs + if ( pSuper->tDelaysR[i].Rise > 0 ) + { + tDelay = ptArrIn->Rise + pSuper->tDelaysR[i].Rise; + if ( tDelay > tWorstLimit ) + return MAP_FLOAT_LARGE; + if ( ptArrRes->Rise < tDelay ) + ptArrRes->Rise = tDelay; + } + + // get the rise of the output due to fall of the inputs + if ( pSuper->tDelaysR[i].Fall > 0 ) + { + tDelay = ptArrIn->Fall + pSuper->tDelaysR[i].Fall; + if ( tDelay > tWorstLimit ) + return MAP_FLOAT_LARGE; + if ( ptArrRes->Rise < tDelay ) + ptArrRes->Rise = tDelay; + } + + // get the fall of the output due to rise of the inputs + if ( pSuper->tDelaysF[i].Rise > 0 ) + { + tDelay = ptArrIn->Rise + pSuper->tDelaysF[i].Rise; + if ( tDelay > tWorstLimit ) + return MAP_FLOAT_LARGE; + if ( ptArrRes->Fall < tDelay ) + ptArrRes->Fall = tDelay; + } + + // get the fall of the output due to fall of the inputs + if ( pSuper->tDelaysF[i].Fall > 0 ) + { + tDelay = ptArrIn->Fall + pSuper->tDelaysF[i].Fall; + if ( tDelay > tWorstLimit ) + return MAP_FLOAT_LARGE; + if ( ptArrRes->Fall < tDelay ) + ptArrRes->Fall = tDelay; + } + } + // return the worst-case of rise/fall arrival times + ptArrRes->Worst = MAP_MAX(ptArrRes->Rise, ptArrRes->Fall); + return ptArrRes->Worst; +} + + +/**Function************************************************************* + + Synopsis [Computes the maximum arrival times.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Map_TimeComputeArrivalMax( Map_Man_t * p ) +{ + float tReqMax, tReq; + int i, fPhase; + // get the critical PO arrival time + tReqMax = -MAP_FLOAT_LARGE; + for ( i = 0; i < p->nOutputs; i++ ) + { + if ( Map_NodeIsConst(p->pOutputs[i]) ) + continue; + fPhase = !Map_IsComplement(p->pOutputs[i]); + tReq = Map_Regular(p->pOutputs[i])->tArrival[fPhase].Worst; + tReqMax = MAP_MAX( tReqMax, tReq ); + } + return tReqMax; +} + +/**Function************************************************************* + + Synopsis [Computes the required times of all nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_TimeComputeRequiredGlobal( Map_Man_t * p ) +{ + p->fRequiredGlo = Map_TimeComputeArrivalMax( p ); + // update the required times according to the target + if ( p->DelayTarget != -1 ) + { + if ( p->fRequiredGlo > p->DelayTarget + p->fEpsilon ) + { + if ( p->fMappingMode == 1 ) + printf( "Cannot meet the target required times (%4.2f). Continue anyway.\n", p->DelayTarget ); + } + else if ( p->fRequiredGlo < p->DelayTarget - p->fEpsilon ) + { + if ( p->fMappingMode == 1 ) + printf( "Relaxing the required times from (%4.2f) to the target (%4.2f).\n", p->fRequiredGlo, p->DelayTarget ); + p->fRequiredGlo = p->DelayTarget; + } + } + Map_TimeComputeRequired( p, p->fRequiredGlo ); +} + +/**Function************************************************************* + + Synopsis [Computes the required times of all nodes.] + + Description [This procedure assumes that the nodes used in the mapping + are collected in p->vMapping.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_TimeComputeRequired( Map_Man_t * p, float fRequired ) +{ + Map_Time_t * ptTime; + int fPhase, i; + + // clean the required times + for ( i = 0; i < p->vAnds->nSize; i++ ) + { + p->vAnds->pArray[i]->tRequired[0].Rise = MAP_FLOAT_LARGE; + p->vAnds->pArray[i]->tRequired[0].Fall = MAP_FLOAT_LARGE; + p->vAnds->pArray[i]->tRequired[0].Worst = MAP_FLOAT_LARGE; + p->vAnds->pArray[i]->tRequired[1].Rise = MAP_FLOAT_LARGE; + p->vAnds->pArray[i]->tRequired[1].Fall = MAP_FLOAT_LARGE; + p->vAnds->pArray[i]->tRequired[1].Worst = MAP_FLOAT_LARGE; + } + + // set the required times for the POs + for ( i = 0; i < p->nOutputs; i++ ) + { + fPhase = !Map_IsComplement(p->pOutputs[i]); + ptTime = Map_Regular(p->pOutputs[i])->tRequired + fPhase; + ptTime->Rise = ptTime->Fall = ptTime->Worst = fRequired; + } + + // sorts the nodes in the decreasing order of levels + // this puts the nodes in reverse topological order +// Map_MappingSortByLevel( p, p->vMapping ); + // the array is already sorted by construction in Map_MappingSetRefs() + + Map_TimePropagateRequired( p, p->vMapping ); +} + +/**Function************************************************************* + + Synopsis [Computes the required times of the given nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_TimePropagateRequired( Map_Man_t * p, Map_NodeVec_t * vNodes ) +{ + Map_Node_t * pNode; + Map_Time_t tReqOutTest, * ptReqOutTest = &tReqOutTest; + Map_Time_t * ptReqIn, * ptReqOut; + int fPhase, k; + + // go through the nodes in the reverse topological order + for ( k = 0; k < vNodes->nSize; k++ ) + { + pNode = vNodes->pArray[k]; + + // this computation works for regular nodes only + assert( !Map_IsComplement(pNode) ); + // at least one phase should be mapped + assert( pNode->pCutBest[0] != NULL || pNode->pCutBest[1] != NULL ); + // the node should be used in the currently assigned mapping + assert( pNode->nRefAct[0] > 0 || pNode->nRefAct[1] > 0 ); + + // if one of the cuts is not given, project the required times from the other cut + if ( pNode->pCutBest[0] == NULL || pNode->pCutBest[1] == NULL ) + { +// assert( 0 ); + // get the missing phase + fPhase = (pNode->pCutBest[1] == NULL); + // check if the missing phase is needed in the mapping + if ( pNode->nRefAct[fPhase] > 0 ) + { + // get the pointers to the required times of the missing phase + ptReqOut = pNode->tRequired + fPhase; +// assert( ptReqOut->Fall < MAP_FLOAT_LARGE ); + // get the pointers to the required times of the present phase + ptReqIn = pNode->tRequired + !fPhase; + // propagate the required times from the missing phase to the present phase + // tArrInv.Fall = pMatch->tArrive.Rise + p->pSuperLib->tDelayInv.Fall; + // tArrInv.Rise = pMatch->tArrive.Fall + p->pSuperLib->tDelayInv.Rise; + ptReqIn->Fall = MAP_MIN( ptReqIn->Fall, ptReqOut->Rise - p->pSuperLib->tDelayInv.Rise ); + ptReqIn->Rise = MAP_MIN( ptReqIn->Rise, ptReqOut->Fall - p->pSuperLib->tDelayInv.Fall ); + } + } + + // finalize the worst case computation + pNode->tRequired[0].Worst = MAP_MIN( pNode->tRequired[0].Fall, pNode->tRequired[0].Rise ); + pNode->tRequired[1].Worst = MAP_MIN( pNode->tRequired[1].Fall, pNode->tRequired[1].Rise ); + + // skip the PIs + if ( !Map_NodeIsAnd(pNode) ) + continue; + + // propagate required times of different phases of the node + // the ordering of phases does not matter since they are mapped independently + if ( pNode->pCutBest[0] && pNode->tRequired[0].Worst < MAP_FLOAT_LARGE ) + Map_TimePropagateRequiredPhase( p, pNode, 0 ); + if ( pNode->pCutBest[1] && pNode->tRequired[1].Worst < MAP_FLOAT_LARGE ) + Map_TimePropagateRequiredPhase( p, pNode, 1 ); + } + + // in the end, we verify the required times + // for this, we compute the arrival times of the outputs of each phase + // of the supergates using the fanins' required times as the fanins' arrival times + // the resulting arrival time of the supergate should be less than the actual required time + for ( k = 0; k < vNodes->nSize; k++ ) + { + pNode = vNodes->pArray[k]; + if ( !Map_NodeIsAnd(pNode) ) + continue; + // verify that the required times are propagated correctly +// if ( pNode->pCutBest[0] && (pNode->nRefAct[0] > 0 || pNode->pCutBest[1] == NULL) ) + if ( pNode->pCutBest[0] && pNode->tRequired[0].Worst < MAP_FLOAT_LARGE ) + { + Map_MatchComputeReqTimes( pNode->pCutBest[0], 0, ptReqOutTest ); + assert( ptReqOutTest->Rise < pNode->tRequired[0].Rise + p->fEpsilon ); + assert( ptReqOutTest->Fall < pNode->tRequired[0].Fall + p->fEpsilon ); + } +// if ( pNode->pCutBest[1] && (pNode->nRefAct[1] > 0 || pNode->pCutBest[0] == NULL) ) + if ( pNode->pCutBest[1] && pNode->tRequired[1].Worst < MAP_FLOAT_LARGE ) + { + Map_MatchComputeReqTimes( pNode->pCutBest[1], 1, ptReqOutTest ); + assert( ptReqOutTest->Rise < pNode->tRequired[1].Rise + p->fEpsilon ); + assert( ptReqOutTest->Fall < pNode->tRequired[1].Fall + p->fEpsilon ); + } + } + +} + +/**Function************************************************************* + + Synopsis [Computes the required times of the given nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_TimePropagateRequiredPhase( Map_Man_t * p, Map_Node_t * pNode, int fPhase ) +{ + Map_Time_t * ptReqIn, * ptReqOut; + Map_Cut_t * pCut; + Map_Super_t * pSuper; + float tNewReqTime; + unsigned uPhase; + int fPinPhase, i; + + // get the cut to be propagated + pCut = pNode->pCutBest[fPhase]; + assert( pCut != NULL ); + // get the supergate and its polarity + pSuper = pCut->M[fPhase].pSuperBest; + uPhase = pCut->M[fPhase].uPhaseBest; + // get the required time of the output of the supergate + ptReqOut = pNode->tRequired + fPhase; + // set the required time of the children + for ( i = 0; i < pCut->nLeaves; i++ ) + { + // get the phase of the given pin of the supergate + fPinPhase = ((uPhase & (1 << i)) == 0); + ptReqIn = pCut->ppLeaves[i]->tRequired + fPinPhase; + assert( pCut->ppLeaves[i]->nRefAct[2] > 0 ); + + // get the rise of the output due to rise of the inputs +// if ( ptArrOut->Rise < ptArrIn->Rise + pSuper->tDelaysR[i].Rise ) +// ptArrOut->Rise = ptArrIn->Rise + pSuper->tDelaysR[i].Rise; + if ( pSuper->tDelaysR[i].Rise > 0 ) + { + tNewReqTime = ptReqOut->Rise - pSuper->tDelaysR[i].Rise; + ptReqIn->Rise = MAP_MIN( ptReqIn->Rise, tNewReqTime ); + } + + // get the rise of the output due to fall of the inputs +// if ( ptArrOut->Rise < ptArrIn->Fall + pSuper->tDelaysR[i].Fall ) +// ptArrOut->Rise = ptArrIn->Fall + pSuper->tDelaysR[i].Fall; + if ( pSuper->tDelaysR[i].Fall > 0 ) + { + tNewReqTime = ptReqOut->Rise - pSuper->tDelaysR[i].Fall; + ptReqIn->Fall = MAP_MIN( ptReqIn->Fall, tNewReqTime ); + } + + // get the fall of the output due to rise of the inputs +// if ( ptArrOut->Fall < ptArrIn->Rise + pSuper->tDelaysF[i].Rise ) +// ptArrOut->Fall = ptArrIn->Rise + pSuper->tDelaysF[i].Rise; + if ( pSuper->tDelaysF[i].Rise > 0 ) + { + tNewReqTime = ptReqOut->Fall - pSuper->tDelaysF[i].Rise; + ptReqIn->Rise = MAP_MIN( ptReqIn->Rise, tNewReqTime ); + } + + // get the fall of the output due to fall of the inputs +// if ( ptArrOut->Fall < ptArrIn->Fall + pSuper->tDelaysF[i].Fall ) +// ptArrOut->Fall = ptArrIn->Fall + pSuper->tDelaysF[i].Fall; + if ( pSuper->tDelaysF[i].Fall > 0 ) + { + tNewReqTime = ptReqOut->Fall - pSuper->tDelaysF[i].Fall; + ptReqIn->Fall = MAP_MIN( ptReqIn->Fall, tNewReqTime ); + } + } + + // compare the required times with the arrival times + assert( pNode->tArrival[fPhase].Rise < ptReqOut->Rise + p->fEpsilon ); + assert( pNode->tArrival[fPhase].Fall < ptReqOut->Fall + p->fEpsilon ); +} + +/**Function************************************************************* + + Synopsis [Computes the arrival times of the cut.] + + Description [Computes the arrival times of the cut if it is implemented using + the given supergate with the given phase. Uses the constraint-type specification + of rise/fall arrival times.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Map_MatchComputeReqTimes( Map_Cut_t * pCut, int fPhase, Map_Time_t * ptArrRes ) +{ + Map_Time_t * ptArrIn; + Map_Super_t * pSuper; + unsigned uPhaseTot; + int fPinPhase, i; + float tDelay; + + // get the supergate and the phase + pSuper = pCut->M[fPhase].pSuperBest; + uPhaseTot = pCut->M[fPhase].uPhaseBest; + + // propagate the arrival times + ptArrRes->Rise = ptArrRes->Fall = -MAP_FLOAT_LARGE; + for ( i = 0; i < pCut->nLeaves; i++ ) + { + // get the phase of the given pin + fPinPhase = ((uPhaseTot & (1 << i)) == 0); + ptArrIn = pCut->ppLeaves[i]->tRequired + fPinPhase; +// assert( ptArrIn->Worst < MAP_FLOAT_LARGE ); + + // get the rise of the output due to rise of the inputs + if ( pSuper->tDelaysR[i].Rise > 0 ) + { + tDelay = ptArrIn->Rise + pSuper->tDelaysR[i].Rise; + if ( ptArrRes->Rise < tDelay ) + ptArrRes->Rise = tDelay; + } + + // get the rise of the output due to fall of the inputs + if ( pSuper->tDelaysR[i].Fall > 0 ) + { + tDelay = ptArrIn->Fall + pSuper->tDelaysR[i].Fall; + if ( ptArrRes->Rise < tDelay ) + ptArrRes->Rise = tDelay; + } + + // get the fall of the output due to rise of the inputs + if ( pSuper->tDelaysF[i].Rise > 0 ) + { + tDelay = ptArrIn->Rise + pSuper->tDelaysF[i].Rise; + if ( ptArrRes->Fall < tDelay ) + ptArrRes->Fall = tDelay; + } + + // get the fall of the output due to fall of the inputs + if ( pSuper->tDelaysF[i].Fall > 0 ) + { + tDelay = ptArrIn->Fall + pSuper->tDelaysF[i].Fall; + if ( ptArrRes->Fall < tDelay ) + ptArrRes->Fall = tDelay; + } + } + // return the worst-case of rise/fall arrival times + return MAP_MAX(ptArrRes->Rise, ptArrRes->Fall); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/mapper/mapperTree.c b/abc_with_bb_support/src/map/mapper/mapperTree.c new file mode 100644 index 000000000..3710a560d --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/mapperTree.c @@ -0,0 +1,818 @@ +/**CFile**************************************************************** + + FileName [mapperTree.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - June 1, 2004.] + + Revision [$Id: mapperTree.c,v 1.9 2005/01/23 06:59:45 alanmi Exp $] + +***********************************************************************/ + +#ifdef __linux__ +#include +#endif + +#include "mapperInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Map_LibraryReadFileTree( Map_SuperLib_t * pLib, FILE * pFile, char *pFileName ); +static Map_Super_t * Map_LibraryReadGateTree( Map_SuperLib_t * pLib, char * pBuffer, int Number, int nVars ); +static int Map_LibraryDeriveGateInfo( Map_SuperLib_t * pLib, st_table * tExcludeGate ); +static void Map_LibraryAddFaninDelays( Map_SuperLib_t * pLib, Map_Super_t * pGate, Map_Super_t * pFanin, Mio_Pin_t * pPin ); +static int Map_LibraryGetMaxSuperPi_rec( Map_Super_t * pGate ); +static unsigned Map_LibraryGetGateSupp_rec( Map_Super_t * pGate ); + +// fanout limits +extern const int s_MapFanoutLimits[10] = { 1/*0*/, 10/*1*/, 5/*2*/, 2/*3*/, 1/*4*/, 1/*5*/, 1/*6*/ }; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Reads the supergate library from file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_LibraryReadTree( Map_SuperLib_t * pLib, char * pFileName, char * pExcludeFile ) +{ + FILE * pFile; + int Status, num; + Abc_Frame_t * pAbc; + st_table * tExcludeGate = 0; + + // read the beginning of the file + assert( pLib->pGenlib == NULL ); + pFile = Io_FileOpen( pFileName, "open_path", "r", 1 ); +// pFile = fopen( pFileName, "r" ); + if ( pFile == NULL ) + { + printf( "Cannot open input file \"%s\".\n", pFileName ); + return 0; + } + + if ( pExcludeFile ) + { + pAbc = Abc_FrameGetGlobalFrame(); + + tExcludeGate = st_init_table(strcmp, st_strhash); + if ( (num = Mio_LibraryReadExclude( pAbc, pExcludeFile, tExcludeGate )) == -1 ) + { + st_free_table( tExcludeGate ); + tExcludeGate = 0; + return 0; + } + + fprintf ( Abc_FrameReadOut( pAbc ), "Read %d gates from exclude file\n", num ); + } + + Status = Map_LibraryReadFileTree( pLib, pFile, pFileName ); + fclose( pFile ); + if ( Status == 0 ) + return 0; + // prepare the info about the library + return Map_LibraryDeriveGateInfo( pLib, tExcludeGate ); +} + + +/**Function************************************************************* + + Synopsis [Reads the library file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_LibraryReadFileTree( Map_SuperLib_t * pLib, FILE * pFile, char *pFileName ) +{ + ProgressBar * pProgress; + char pBuffer[5000], pLibFile[5000]; + FILE * pFileGen; + Map_Super_t * pGate; + char * pTemp = 0, * pLibName; + int nCounter, k, i; + + // skip empty and comment lines + while ( fgets( pBuffer, 5000, pFile ) != NULL ) + { + // skip leading spaces + for ( pTemp = pBuffer; *pTemp == ' ' || *pTemp == '\r' || *pTemp == '\n'; pTemp++ ); + // skip comment lines and empty lines + if ( *pTemp != 0 && *pTemp != '#' ) + break; + } + + // get the genlib file name (base) + pLibName = strtok( pTemp, " \t\r\n" ); + + if ( strcmp( pLibName, "GATE" ) == 0 ) + { + printf( "The input file \"%s\" looks like a GENLIB file and not a supergate library file.\n", pLib->pName ); + return 0; + } + + + // now figure out the directory if any in the pFileName +#ifdef __linux__ + snprintf( pLibFile, 5000, "%s/%s", dirname(strdup(pFileName)), pLibName ); +#else + { + char * pStr; + strcpy( pLibFile, pFileName ); + pStr = pLibFile + strlen(pBuffer) - 1; + while ( pStr > pLibFile && *pStr != '\\' && *pStr != '/' ) + pStr--; + if ( pStr == pLibFile ) + strcpy( pLibFile, pLibName ); + else + sprintf( pStr, "/%s", pLibName ); + } +#endif + + pFileGen = Io_FileOpen( pLibFile, "open_path", "r", 1 ); +// pFileGen = fopen( pLibFile, "r" ); + if ( pFileGen == NULL ) + { + printf( "Cannot open the GENLIB file \"%s\".\n", pLibFile ); + return 0; + } + fclose( pFileGen ); + + // read the genlib library + pLib->pGenlib = Mio_LibraryRead( Abc_FrameGetGlobalFrame(), pLibFile, 0, 0 ); + if ( pLib->pGenlib == NULL ) + { + printf( "Cannot read GENLIB file \"%s\".\n", pLibFile ); + return 0; + } + + // read the number of variables + fscanf( pFile, "%d\n", &pLib->nVarsMax ); + if ( pLib->nVarsMax < 2 || pLib->nVarsMax > 10 ) + { + printf( "Suspicious number of variables (%d).\n", pLib->nVarsMax ); + return 0; + } + + // read the number of gates + fscanf( pFile, "%d\n", &pLib->nSupersReal ); + if ( pLib->nSupersReal < 1 || pLib->nSupersReal > 10000000 ) + { + printf( "Suspicious number of gates (%d).\n", pLib->nSupersReal ); + return 0; + } + + // read the number of lines + fscanf( pFile, "%d\n", &pLib->nLines ); + if ( pLib->nLines < 1 || pLib->nLines > 10000000 ) + { + printf( "Suspicious number of lines (%d).\n", pLib->nLines ); + return 0; + } + + // allocate room for supergate pointers + pLib->ppSupers = ALLOC( Map_Super_t *, pLib->nLines + 10000 ); + + // create the elementary supergates + for ( i = 0; i < pLib->nVarsMax; i++ ) + { + // get a new gate + pGate = (Map_Super_t *)Extra_MmFixedEntryFetch( pLib->mmSupers ); + memset( pGate, 0, sizeof(Map_Super_t) ); + // assign the elementary variable, the truth table, and the delays + pGate->Num = i; + // set the truth table + pGate->uTruth[0] = pLib->uTruths[i][0]; + pGate->uTruth[1] = pLib->uTruths[i][1]; + // set the arrival times of all input to non-existent delay + for ( k = 0; k < pLib->nVarsMax; k++ ) + { + pGate->tDelaysR[k].Rise = pGate->tDelaysR[k].Fall = MAP_NO_VAR; + pGate->tDelaysF[k].Rise = pGate->tDelaysF[k].Fall = MAP_NO_VAR; + } + // set an existent arrival time for rise and fall + pGate->tDelaysR[i].Rise = 0.0; + pGate->tDelaysF[i].Fall = 0.0; + // set the gate + pLib->ppSupers[i] = pGate; + } + + // read the lines + nCounter = pLib->nVarsMax; + pProgress = Extra_ProgressBarStart( stdout, pLib->nLines ); + while ( fgets( pBuffer, 5000, pFile ) != NULL ) + { + for ( pTemp = pBuffer; *pTemp == ' ' || *pTemp == '\r' || *pTemp == '\n'; pTemp++ ); + if ( pTemp[0] == '\0' ) + continue; +// if ( pTemp[0] == 'a' || pTemp[2] == 'a' ) +// { +// pLib->nLines--; +// continue; +// } + + // get the gate + pGate = Map_LibraryReadGateTree( pLib, pTemp, nCounter, pLib->nVarsMax ); + if ( pGate == NULL ) + { + Extra_ProgressBarStop( pProgress ); + return 0; + } + pLib->ppSupers[nCounter++] = pGate; + // later we will derive: truth table, delays, area, number of component gates, etc + + // update the progress bar + Extra_ProgressBarUpdate( pProgress, nCounter, NULL ); + } + Extra_ProgressBarStop( pProgress ); + if ( nCounter != pLib->nLines ) + printf( "The number of lines read (%d) is different what the file says (%d).\n", nCounter, pLib->nLines ); + pLib->nSupersAll = nCounter; + // count the number of real supergates + nCounter = 0; + for ( k = 0; k < pLib->nLines; k++ ) + nCounter += pLib->ppSupers[k]->fSuper; + if ( nCounter != pLib->nSupersReal ) + printf( "The number of gates read (%d) is different what the file says (%d).\n", nCounter, pLib->nSupersReal ); + pLib->nSupersReal = nCounter; + return 1; +} + +/**Function************************************************************* + + Synopsis [Reads one gate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Super_t * Map_LibraryReadGateTree( Map_SuperLib_t * pLib, char * pBuffer, int Number, int nVarsMax ) +{ + Map_Super_t * pGate; + char * pTemp; + int i, Num; + + // start and clean the gate + pGate = (Map_Super_t *)Extra_MmFixedEntryFetch( pLib->mmSupers ); + memset( pGate, 0, sizeof(Map_Super_t) ); + + // set the gate number + pGate->Num = Number; + + // read the mark + pTemp = strtok( pBuffer, " " ); + if ( pTemp[0] == '*' ) + { + pGate->fSuper = 1; + pTemp = strtok( NULL, " " ); + } + + // read the root gate + pGate->pRoot = Mio_LibraryReadGateByName( pLib->pGenlib, pTemp ); + if ( pGate->pRoot == NULL ) + { + printf( "Cannot read the root gate names %s.\n", pTemp ); + return NULL; + } + // set the max number of fanouts + pGate->nFanLimit = s_MapFanoutLimits[ Mio_GateReadInputs(pGate->pRoot) ]; + + // read the pin-to-pin delay + for ( i = 0; ( pTemp = strtok( NULL, " \n\0" ) ); i++ ) + { + if ( pTemp[0] == '#' ) + break; + if ( i == nVarsMax ) + { + printf( "There are too many entries on the line.\n" ); + return NULL; + } + Num = atoi(pTemp); + if ( Num < 0 ) + { + printf( "The number of a child supergate is negative.\n" ); + return NULL; + } + if ( Num > pLib->nLines ) + { + printf( "The number of a child supergate (%d) exceeded the number of lines (%d).\n", + Num, pLib->nLines ); + return NULL; + } + pGate->pFanins[i] = pLib->ppSupers[Num]; + } + pGate->nFanins = i; + if ( pGate->nFanins != (unsigned)Mio_GateReadInputs(pGate->pRoot) ) + { + printf( "The number of fanins of a root gate is wrong.\n" ); + return NULL; + } + + // save the gate name, just in case + if ( pTemp && pTemp[0] == '#' ) + { + if ( pTemp[1] == 0 ) + pTemp = strtok( NULL, " \n\0" ); + else // skip spaces + for ( pTemp++; *pTemp == ' '; pTemp++ ); + // save the formula + pGate->pFormula = Extra_MmFlexEntryFetch( pLib->mmForms, strlen(pTemp)+1 ); + strcpy( pGate->pFormula, pTemp ); + } + // check the rest of the string + pTemp = strtok( NULL, " \n\0" ); + if ( pTemp != NULL ) + printf( "The following trailing symbols found \"%s\".\n", pTemp ); + return pGate; +} + + +/**Function************************************************************* + + Synopsis [Derives information about the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_LibraryDeriveGateInfo( Map_SuperLib_t * pLib, st_table * tExcludeGate ) +{ + Map_Super_t * pGate, * pFanin; + Mio_Pin_t * pPin; + unsigned uCanon[2]; + unsigned uTruths[6][2]; + int i, k, nRealVars; + + // set all the derivable info related to the supergates + for ( i = pLib->nVarsMax; i < (int)pLib->nLines; i++ ) + { + pGate = pLib->ppSupers[i]; + + if ( tExcludeGate ) + { + if ( st_is_member( tExcludeGate, Mio_GateReadName( pGate->pRoot ) ) ) + pGate->fExclude = 1; + for ( k = 0; k < (int)pGate->nFanins; k++ ) + { + pFanin = pGate->pFanins[k]; + if ( pFanin->fExclude ) + { + pGate->fExclude = 1; + continue; + } + } + } + + // collect the truth tables of the fanins + for ( k = 0; k < (int)pGate->nFanins; k++ ) + { + pFanin = pGate->pFanins[k]; + uTruths[k][0] = pFanin->uTruth[0]; + uTruths[k][1] = pFanin->uTruth[1]; + } + // derive the new truth table + Mio_DeriveTruthTable( pGate->pRoot, uTruths, pGate->nFanins, 6, pGate->uTruth ); + + // set the initial delays of the supergate + for ( k = 0; k < pLib->nVarsMax; k++ ) + { + pGate->tDelaysR[k].Rise = pGate->tDelaysR[k].Fall = MAP_NO_VAR; + pGate->tDelaysF[k].Rise = pGate->tDelaysF[k].Fall = MAP_NO_VAR; + } + // get the linked list of pins for the given root gate + pPin = Mio_GateReadPins( pGate->pRoot ); + // update the initial delay of the supergate using info from the corresponding pin + for ( k = 0; k < (int)pGate->nFanins; k++, pPin = Mio_PinReadNext(pPin) ) + { + // if there is no corresponding pin, this is a bug, return fail + if ( pPin == NULL ) + { + printf( "There are less pins than gate inputs.\n" ); + return 0; + } + // update the delay information of k-th fanins info from the corresponding pin + Map_LibraryAddFaninDelays( pLib, pGate, pGate->pFanins[k], pPin ); + } + // if there are some pins left, this is a bug, return fail + if ( pPin != NULL ) + { + printf( "There are more pins than gate inputs.\n" ); + return 0; + } + // find the max delay + pGate->tDelayMax.Rise = pGate->tDelayMax.Fall = MAP_NO_VAR; + for ( k = 0; k < pLib->nVarsMax; k++ ) + { + // the rise of the output depends on the rise and fall of the output + if ( pGate->tDelayMax.Rise < pGate->tDelaysR[k].Rise ) + pGate->tDelayMax.Rise = pGate->tDelaysR[k].Rise; + if ( pGate->tDelayMax.Rise < pGate->tDelaysR[k].Fall ) + pGate->tDelayMax.Rise = pGate->tDelaysR[k].Fall; + // the fall of the output depends on the rise and fall of the output + if ( pGate->tDelayMax.Fall < pGate->tDelaysF[k].Rise ) + pGate->tDelayMax.Fall = pGate->tDelaysF[k].Rise; + if ( pGate->tDelayMax.Fall < pGate->tDelaysF[k].Fall ) + pGate->tDelayMax.Fall = pGate->tDelaysF[k].Fall; + + pGate->tDelaysF[k].Worst = MAP_MAX( pGate->tDelaysF[k].Fall, pGate->tDelaysF[k].Rise ); + pGate->tDelaysR[k].Worst = MAP_MAX( pGate->tDelaysR[k].Fall, pGate->tDelaysR[k].Rise ); + } + + // count gates and area of the supergate + pGate->nGates = 1; + pGate->Area = (float)Mio_GateReadArea(pGate->pRoot); + for ( k = 0; k < (int)pGate->nFanins; k++ ) + { + pGate->nGates += pGate->pFanins[k]->nGates; + pGate->Area += pGate->pFanins[k]->Area; + } + // do not add the gate to the table, if this gate is an internal gate + // of some supegate and does not correspond to a supergate output + if ( ( !pGate->fSuper ) || pGate->fExclude ) + continue; + + // find the maximum index of a variable in the support of the supergates + // this is important for two reasons: + // (1) to limit the number of permutations considered for canonicization + // (2) to get rid of equivalence phases to speed-up matching + nRealVars = Map_LibraryGetMaxSuperPi_rec( pGate ) + 1; + assert( nRealVars > 0 && nRealVars <= pLib->nVarsMax ); + // if there are some problems with this code, try this instead +// nRealVars = pLib->nVarsMax; + + // find the N-canonical form of this supergate + pGate->nPhases = Map_CanonComputeSlow( pLib->uTruths, pLib->nVarsMax, nRealVars, pGate->uTruth, pGate->uPhases, uCanon ); + // add the supergate into the table by its N-canonical table + Map_SuperTableInsertC( pLib->tTableC, uCanon, pGate ); +/* + { + int uCanon1, uCanon2; + uCanon1 = uCanon[0]; + pGate->uTruth[0] = ~pGate->uTruth[0]; + pGate->uTruth[1] = ~pGate->uTruth[1]; + Map_CanonComputeSlow( pLib->uTruths, pLib->nVarsMax, nRealVars, pGate->uTruth, pGate->uPhases, uCanon ); + uCanon2 = uCanon[0]; +Rwt_Man5ExploreCount( uCanon1 < uCanon2 ? uCanon1 : uCanon2 ); + } +*/ + } + // sort the gates in each line + Map_SuperTableSortSupergatesByDelay( pLib->tTableC, pLib->nSupersAll ); + + // let the glory be manifest +// Map_LibraryPrintTree( pLib ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Finds the largest PI number in the support of the supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_LibraryGetMaxSuperPi_rec( Map_Super_t * pGate ) +{ + int i, VarCur, VarMax = 0; + if ( pGate->pRoot == NULL ) + return pGate->Num; + for ( i = 0; i < (int)pGate->nFanins; i++ ) + { + VarCur = Map_LibraryGetMaxSuperPi_rec( pGate->pFanins[i] ); + if ( VarMax < VarCur ) + VarMax = VarCur; + } + return VarMax; +} + +/**Function************************************************************* + + Synopsis [Finds the largest PI number in the support of the supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Map_LibraryGetGateSupp_rec( Map_Super_t * pGate ) +{ + unsigned uSupport; + int i; + if ( pGate->pRoot == NULL ) + return (unsigned)(1 << (pGate->Num)); + uSupport = 0; + for ( i = 0; i < (int)pGate->nFanins; i++ ) + uSupport |= Map_LibraryGetGateSupp_rec( pGate->pFanins[i] ); + return uSupport; +} + +/**Function************************************************************* + + Synopsis [Derives the pin-to-pin delay constraints for the supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_LibraryAddFaninDelays( Map_SuperLib_t * pLib, Map_Super_t * pGate, Map_Super_t * pFanin, Mio_Pin_t * pPin ) +{ + Mio_PinPhase_t PinPhase; + float tDelayBlockRise, tDelayBlockFall, tDelayPin; + bool fMaxDelay = 0; + int i; + + // use this node to enable max-delay model + if ( fMaxDelay ) + { + float tDelayBlockMax; + // get the maximum delay + tDelayBlockMax = (float)Mio_PinReadDelayBlockMax(pPin); + // go through the supergate inputs + for ( i = 0; i < pLib->nVarsMax; i++ ) + { + if ( pFanin->tDelaysR[i].Rise < 0 ) + continue; + tDelayPin = pFanin->tDelaysR[i].Rise + tDelayBlockMax; + if ( pGate->tDelaysR[i].Rise < tDelayPin ) + pGate->tDelaysR[i].Rise = tDelayPin; + } + // go through the supergate inputs + for ( i = 0; i < pLib->nVarsMax; i++ ) + { + if ( pFanin->tDelaysF[i].Fall < 0 ) + continue; + tDelayPin = pFanin->tDelaysF[i].Fall + tDelayBlockMax; + if ( pGate->tDelaysF[i].Fall < tDelayPin ) + pGate->tDelaysF[i].Fall = tDelayPin; + } + return; + } + + // get the interesting parameters of this pin + PinPhase = Mio_PinReadPhase(pPin); + tDelayBlockRise = (float)Mio_PinReadDelayBlockRise( pPin ); + tDelayBlockFall = (float)Mio_PinReadDelayBlockFall( pPin ); + + // update the rise and fall of the output depending on the phase of the pin + if ( PinPhase != MIO_PHASE_INV ) // NONINV phase is present + { + // the rise of the gate is determined by the rise of the fanin + // the fall of the gate is determined by the fall of the fanin + for ( i = 0; i < pLib->nVarsMax; i++ ) + { + //////////////////////////////////////////////////////// + // consider the rise of the gate + //////////////////////////////////////////////////////// + // check two types of constraints on the rise of the fanin: + // (1) the constraints related to the rise of the PIs + // (2) the constraints related to the fall of the PIs + if ( pFanin->tDelaysR[i].Rise >= 0 ) // case (1) + { // fanin's rise depends on the rise of i-th PI + // update the rise of the gate's output + if ( pGate->tDelaysR[i].Rise < pFanin->tDelaysR[i].Rise + tDelayBlockRise ) + pGate->tDelaysR[i].Rise = pFanin->tDelaysR[i].Rise + tDelayBlockRise; + } + if ( pFanin->tDelaysR[i].Fall >= 0 ) // case (2) + { // fanin's rise depends on the fall of i-th PI + // update the rise of the gate's output + if ( pGate->tDelaysR[i].Fall < pFanin->tDelaysR[i].Fall + tDelayBlockRise ) + pGate->tDelaysR[i].Fall = pFanin->tDelaysR[i].Fall + tDelayBlockRise; + } + //////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////// + // consider the fall of the gate (similar) + //////////////////////////////////////////////////////// + // check two types of constraints on the fall of the fanin: + // (1) the constraints related to the rise of the PIs + // (2) the constraints related to the fall of the PIs + if ( pFanin->tDelaysF[i].Rise >= 0 ) // case (1) + { + if ( pGate->tDelaysF[i].Rise < pFanin->tDelaysF[i].Rise + tDelayBlockFall ) + pGate->tDelaysF[i].Rise = pFanin->tDelaysF[i].Rise + tDelayBlockFall; + } + if ( pFanin->tDelaysF[i].Fall >= 0 ) // case (2) + { + if ( pGate->tDelaysF[i].Fall < pFanin->tDelaysF[i].Fall + tDelayBlockFall ) + pGate->tDelaysF[i].Fall = pFanin->tDelaysF[i].Fall + tDelayBlockFall; + } + //////////////////////////////////////////////////////// + } + } + if ( PinPhase != MIO_PHASE_NONINV ) // INV phase is present + { + // the rise of the gate is determined by the fall of the fanin + // the fall of the gate is determined by the rise of the fanin + for ( i = 0; i < pLib->nVarsMax; i++ ) + { + //////////////////////////////////////////////////////// + // consider the rise of the gate's output + //////////////////////////////////////////////////////// + // check two types of constraints on the fall of the fanin: + // (1) the constraints related to the rise of the PIs + // (2) the constraints related to the fall of the PIs + if ( pFanin->tDelaysF[i].Rise >= 0 ) // case (1) + { // fanin's rise depends on the rise of i-th PI + // update the rise of the gate + if ( pGate->tDelaysR[i].Rise < pFanin->tDelaysF[i].Rise + tDelayBlockRise ) + pGate->tDelaysR[i].Rise = pFanin->tDelaysF[i].Rise + tDelayBlockRise; + } + if ( pFanin->tDelaysF[i].Fall >= 0 ) // case (2) + { // fanin's rise depends on the fall of i-th PI + // update the rise of the gate + if ( pGate->tDelaysR[i].Fall < pFanin->tDelaysF[i].Fall + tDelayBlockRise ) + pGate->tDelaysR[i].Fall = pFanin->tDelaysF[i].Fall + tDelayBlockRise; + } + //////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////// + // consider the fall of the gate (similar) + //////////////////////////////////////////////////////// + // check two types of constraints on the rise of the fanin: + // (1) the constraints related to the rise of the PIs + // (2) the constraints related to the fall of the PIs + if ( pFanin->tDelaysR[i].Rise >= 0 ) // case (1) + { + if ( pGate->tDelaysF[i].Rise < pFanin->tDelaysR[i].Rise + tDelayBlockFall ) + pGate->tDelaysF[i].Rise = pFanin->tDelaysR[i].Rise + tDelayBlockFall; + } + if ( pFanin->tDelaysR[i].Fall >= 0 ) // case (2) + { + if ( pGate->tDelaysF[i].Fall < pFanin->tDelaysR[i].Fall + tDelayBlockFall ) + pGate->tDelaysF[i].Fall = pFanin->tDelaysR[i].Fall + tDelayBlockFall; + } + //////////////////////////////////////////////////////// + } + } +} + + +/**Function************************************************************* + + Synopsis [Performs phase transformation for one function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Map_CalculatePhase( unsigned uTruths[][2], int nVars, unsigned uTruth, unsigned uPhase ) +{ + int v, Shift; + for ( v = 0, Shift = 1; v < nVars; v++, Shift <<= 1 ) + if ( uPhase & Shift ) + uTruth = (((uTruth & ~uTruths[v][0]) << Shift) | ((uTruth & uTruths[v][0]) >> Shift)); + return uTruth; +} + +/**Function************************************************************* + + Synopsis [Performs phase transformation for one function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_CalculatePhase6( unsigned uTruths[][2], int nVars, unsigned uTruth[], unsigned uPhase, unsigned uTruthRes[] ) +{ + unsigned uTemp; + int v, Shift; + + // initialize the result + uTruthRes[0] = uTruth[0]; + uTruthRes[1] = uTruth[1]; + if ( uPhase == 0 ) + return; + // compute the phase + for ( v = 0, Shift = 1; v < nVars; v++, Shift <<= 1 ) + if ( uPhase & Shift ) + { + if ( Shift < 32 ) + { + uTruthRes[0] = (((uTruthRes[0] & ~uTruths[v][0]) << Shift) | ((uTruthRes[0] & uTruths[v][0]) >> Shift)); + uTruthRes[1] = (((uTruthRes[1] & ~uTruths[v][1]) << Shift) | ((uTruthRes[1] & uTruths[v][1]) >> Shift)); + } + else + { + uTemp = uTruthRes[0]; + uTruthRes[0] = uTruthRes[1]; + uTruthRes[1] = uTemp; + } + } +} + +/**Function************************************************************* + + Synopsis [Prints the supergate library after deriving parameters.] + + Description [This procedure is very useful to see the library after + it has been read into the mapper by "read_super" and all the information + about the supergates derived.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_LibraryPrintTree( Map_SuperLib_t * pLib ) +{ + Map_Super_t * pGate; + int i, k; + + // print all the info related to the supergates +// for ( i = pLib->nVarsMax; i < (int)pLib->nLines; i++ ) + for ( i = pLib->nVarsMax; i < 20; i++ ) + { + pGate = pLib->ppSupers[i]; + + // write the gate's fanin info and formula + printf( "%6d ", pGate->Num ); + printf( "%c ", pGate->fSuper? '*' : ' ' ); + printf( "%6s", Mio_GateReadName(pGate->pRoot) ); + for ( k = 0; k < (int)pGate->nFanins; k++ ) + printf( " %6d", pGate->pFanins[k]->Num ); + printf( " %s", pGate->pFormula ); + printf( "\n" ); + + // write the gate's derived info + Extra_PrintBinary( stdout, pGate->uTruth, 64 ); + printf( " %3d", pGate->nGates ); + printf( " %6.2f", pGate->Area ); + printf( " (%4.2f, %4.2f)", pGate->tDelayMax.Rise, pGate->tDelayMax.Fall ); + printf( "\n" ); + for ( k = 0; k < pLib->nVarsMax; k++ ) + { + // print the constraint on the rise of the gate in the form (D1, D2), + // where D1 is the constraint related to the rise of the k-th PI + // where D2 is the constraint related to the fall of the k-th PI + if ( pGate->tDelaysR[k].Rise < 0 && pGate->tDelaysR[k].Fall < 0 ) + printf( " (----, ----)" ); + else if ( pGate->tDelaysR[k].Fall < 0 ) + printf( " (%4.2f, ----)", pGate->tDelaysR[k].Rise ); + else if ( pGate->tDelaysR[k].Rise < 0 ) + printf( " (----, %4.2f)", pGate->tDelaysR[k].Fall ); + else + printf( " (%4.2f, %4.2f)", pGate->tDelaysR[k].Rise, pGate->tDelaysR[k].Fall ); + + // print the constraint on the fall of the gate in the form (D1, D2), + // where D1 is the constraint related to the rise of the k-th PI + // where D2 is the constraint related to the fall of the k-th PI + if ( pGate->tDelaysF[k].Rise < 0 && pGate->tDelaysF[k].Fall < 0 ) + printf( " (----, ----)" ); + else if ( pGate->tDelaysF[k].Fall < 0 ) + printf( " (%4.2f, ----)", pGate->tDelaysF[k].Rise ); + else if ( pGate->tDelaysF[k].Rise < 0 ) + printf( " (----, %4.2f)", pGate->tDelaysF[k].Fall ); + else + printf( " (%4.2f, %4.2f)", pGate->tDelaysF[k].Rise, pGate->tDelaysF[k].Fall ); + printf( "\n" ); + } + printf( "\n" ); + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/mapper/mapperTruth.c b/abc_with_bb_support/src/map/mapper/mapperTruth.c new file mode 100644 index 000000000..2dc138a66 --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/mapperTruth.c @@ -0,0 +1,310 @@ +/**CFile**************************************************************** + + FileName [mapperTruth.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - June 1, 2004.] + + Revision [$Id: mapperTruth.c,v 1.8 2005/01/23 06:59:45 alanmi Exp $] + +***********************************************************************/ + +#include "mapperInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Map_TruthsCut( Map_Man_t * pMan, Map_Cut_t * pCut ); +extern void Map_TruthsCutOne( Map_Man_t * p, Map_Cut_t * pCut, unsigned uTruth[] ); +static void Map_CutsCollect_rec( Map_Cut_t * pCut, Map_NodeVec_t * vVisited ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Derives truth tables for each cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingTruths( Map_Man_t * pMan ) +{ + ProgressBar * pProgress; + Map_Node_t * pNode; + Map_Cut_t * pCut; + int nNodes, i; + // compute the cuts for the POs + nNodes = pMan->vAnds->nSize; + pProgress = Extra_ProgressBarStart( stdout, nNodes ); + for ( i = 0; i < nNodes; i++ ) + { + pNode = pMan->vAnds->pArray[i]; + if ( !Map_NodeIsAnd( pNode ) ) + continue; + assert( pNode->pCuts ); + assert( pNode->pCuts->nLeaves == 1 ); + + // match the simple cut + pNode->pCuts->M[0].uPhase = 0; + pNode->pCuts->M[0].pSupers = pMan->pSuperLib->pSuperInv; + pNode->pCuts->M[0].uPhaseBest = 0; + pNode->pCuts->M[0].pSuperBest = pMan->pSuperLib->pSuperInv; + + pNode->pCuts->M[1].uPhase = 0; + pNode->pCuts->M[1].pSupers = pMan->pSuperLib->pSuperInv; + pNode->pCuts->M[1].uPhaseBest = 1; + pNode->pCuts->M[1].pSuperBest = pMan->pSuperLib->pSuperInv; + + // match the rest of the cuts + for ( pCut = pNode->pCuts->pNext; pCut; pCut = pCut->pNext ) + Map_TruthsCut( pMan, pCut ); + Extra_ProgressBarUpdate( pProgress, i, "Tables ..." ); + } + Extra_ProgressBarStop( pProgress ); +} + +/**Function************************************************************* + + Synopsis [Derives the truth table for one cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_TruthsCut( Map_Man_t * p, Map_Cut_t * pCut ) +{ +// unsigned uCanon1, uCanon2; + unsigned uTruth[2], uCanon[2]; + unsigned char uPhases[16]; + unsigned * uCanon2; + char * pPhases2; + int fUseFast = 1; + int fUseSlow = 0; + int fUseRec = 0; // this does not work for Solaris + + extern int Map_CanonCompute( int nVarsMax, int nVarsReal, unsigned * pt, unsigned ** pptRes, char ** ppfRes ); + + // generally speaking, 1-input cut can be matched into a wire! + if ( pCut->nLeaves == 1 ) + return; +/* + if ( p->nVarsMax == 5 ) + { + uTruth[0] = pCut->uTruth; + uTruth[1] = pCut->uTruth; + } + else +*/ + Map_TruthsCutOne( p, pCut, uTruth ); + + + // compute the canonical form for the positive phase + if ( fUseFast ) + Map_CanonComputeFast( p, p->nVarsMax, pCut->nLeaves, uTruth, uPhases, uCanon ); + else if ( fUseSlow ) + Map_CanonComputeSlow( p->uTruths, p->nVarsMax, pCut->nLeaves, uTruth, uPhases, uCanon ); + else if ( fUseRec ) + { +// Map_CanonComputeSlow( p->uTruths, p->nVarsMax, pCut->nLeaves, uTruth, uPhases, uCanon ); + Extra_TruthCanonFastN( p->nVarsMax, pCut->nLeaves, uTruth, &uCanon2, &pPhases2 ); +/* + if ( uCanon[0] != uCanon2[0] || uPhases[0] != pPhases2[0] ) + { + int k = 0; + Map_CanonCompute( p->nVarsMax, pCut->nLeaves, uTruth, &uCanon2, &pPhases2 ); + } +*/ + uCanon[0] = uCanon2[0]; + uCanon[1] = (p->nVarsMax == 6)? uCanon2[1] : uCanon2[0]; + uPhases[0] = pPhases2[0]; + } + else + Map_CanonComputeSlow( p->uTruths, p->nVarsMax, pCut->nLeaves, uTruth, uPhases, uCanon ); + pCut->M[1].pSupers = Map_SuperTableLookupC( p->pSuperLib, uCanon ); + pCut->M[1].uPhase = uPhases[0]; + p->nCanons++; + +//uCanon1 = uCanon[0] & 0xFFFF; + + // compute the canonical form for the negative phase + uTruth[0] = ~uTruth[0]; + uTruth[1] = ~uTruth[1]; + if ( fUseFast ) + Map_CanonComputeFast( p, p->nVarsMax, pCut->nLeaves, uTruth, uPhases, uCanon ); + else if ( fUseSlow ) + Map_CanonComputeSlow( p->uTruths, p->nVarsMax, pCut->nLeaves, uTruth, uPhases, uCanon ); + else if ( fUseRec ) + { +// Map_CanonComputeSlow( p->uTruths, p->nVarsMax, pCut->nLeaves, uTruth, uPhases, uCanon ); + Extra_TruthCanonFastN( p->nVarsMax, pCut->nLeaves, uTruth, &uCanon2, &pPhases2 ); +/* + if ( uCanon[0] != uCanon2[0] || uPhases[0] != pPhases2[0] ) + { + int k = 0; + Map_CanonCompute( p->nVarsMax, pCut->nLeaves, uTruth, &uCanon2, &pPhases2 ); + } +*/ + uCanon[0] = uCanon2[0]; + uCanon[1] = (p->nVarsMax == 6)? uCanon2[1] : uCanon2[0]; + uPhases[0] = pPhases2[0]; + } + else + Map_CanonComputeSlow( p->uTruths, p->nVarsMax, pCut->nLeaves, uTruth, uPhases, uCanon ); + pCut->M[0].pSupers = Map_SuperTableLookupC( p->pSuperLib, uCanon ); + pCut->M[0].uPhase = uPhases[0]; + p->nCanons++; + +//uCanon2 = uCanon[0] & 0xFFFF; +//assert( p->nVarsMax == 4 ); +//Rwt_Man4ExploreCount( uCanon1 < uCanon2 ? uCanon1 : uCanon2 ); + + // restore the truth table + uTruth[0] = ~uTruth[0]; + uTruth[1] = ~uTruth[1]; +} + +/**Function************************************************************* + + Synopsis [Computes the truth table of one cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_TruthsCutOne( Map_Man_t * p, Map_Cut_t * pCut, unsigned uTruth[] ) +{ + unsigned uTruth1[2], uTruth2[2]; + Map_Cut_t * pTemp; + int i; + // mark the cut leaves + for ( i = 0; i < pCut->nLeaves; i++ ) + { + pTemp = pCut->ppLeaves[i]->pCuts; + pTemp->fMark = 1; + pTemp->M[0].uPhaseBest = p->uTruths[i][0]; + pTemp->M[1].uPhaseBest = p->uTruths[i][1]; + } + assert( pCut->fMark == 0 ); + + // collect the cuts in the cut cone + p->vVisited->nSize = 0; + Map_CutsCollect_rec( pCut, p->vVisited ); + assert( p->vVisited->nSize > 0 ); + pCut->nVolume = p->vVisited->nSize; + + // compute the tables and unmark + for ( i = 0; i < pCut->nLeaves; i++ ) + { + pTemp = pCut->ppLeaves[i]->pCuts; + pTemp->fMark = 0; + } + for ( i = 0; i < p->vVisited->nSize; i++ ) + { + // get the cut + pTemp = (Map_Cut_t *)p->vVisited->pArray[i]; + pTemp->fMark = 0; + // get truth table of the first branch + if ( Map_CutIsComplement(pTemp->pOne) ) + { + uTruth1[0] = ~Map_CutRegular(pTemp->pOne)->M[0].uPhaseBest; + uTruth1[1] = ~Map_CutRegular(pTemp->pOne)->M[1].uPhaseBest; + } + else + { + uTruth1[0] = Map_CutRegular(pTemp->pOne)->M[0].uPhaseBest; + uTruth1[1] = Map_CutRegular(pTemp->pOne)->M[1].uPhaseBest; + } + // get truth table of the second branch + if ( Map_CutIsComplement(pTemp->pTwo) ) + { + uTruth2[0] = ~Map_CutRegular(pTemp->pTwo)->M[0].uPhaseBest; + uTruth2[1] = ~Map_CutRegular(pTemp->pTwo)->M[1].uPhaseBest; + } + else + { + uTruth2[0] = Map_CutRegular(pTemp->pTwo)->M[0].uPhaseBest; + uTruth2[1] = Map_CutRegular(pTemp->pTwo)->M[1].uPhaseBest; + } + // get the truth table of the output + if ( !pTemp->Phase ) + { + pTemp->M[0].uPhaseBest = uTruth1[0] & uTruth2[0]; + pTemp->M[1].uPhaseBest = uTruth1[1] & uTruth2[1]; + } + else + { + pTemp->M[0].uPhaseBest = ~(uTruth1[0] & uTruth2[0]); + pTemp->M[1].uPhaseBest = ~(uTruth1[1] & uTruth2[1]); + } + } + uTruth[0] = pTemp->M[0].uPhaseBest; + uTruth[1] = pTemp->M[1].uPhaseBest; +} + +/**Function************************************************************* + + Synopsis [Recursively collect the cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_CutsCollect_rec( Map_Cut_t * pCut, Map_NodeVec_t * vVisited ) +{ + if ( pCut->fMark ) + return; + Map_CutsCollect_rec( Map_CutRegular(pCut->pOne), vVisited ); + Map_CutsCollect_rec( Map_CutRegular(pCut->pTwo), vVisited ); + assert( pCut->fMark == 0 ); + pCut->fMark = 1; + Map_NodeVecPush( vVisited, (Map_Node_t *)pCut ); +} + +/* + { + unsigned * uCanon2; + char * pPhases2; + + Map_CanonComputeSlow( p->uTruths, p->nVarsMax, pCut->nLeaves, uTruth, uPhases, uCanon ); + Map_CanonCompute( p->nVarsMax, pCut->nLeaves, uTruth, &uCanon2, &pPhases2 ); + if ( uCanon2[0] != uCanon[0] ) + { + int v = 0; + Map_CanonCompute( p->nVarsMax, pCut->nLeaves, uTruth, &uCanon2, &pPhases2 ); + Map_CanonComputeFast( p, p->nVarsMax, pCut->nLeaves, uTruth, uPhases, uCanon ); + } +// else +// { +// printf( "Correct.\n" ); +// } + } +*/ + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/mapper/mapperUtils.c b/abc_with_bb_support/src/map/mapper/mapperUtils.c new file mode 100644 index 000000000..23104b8f6 --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/mapperUtils.c @@ -0,0 +1,1154 @@ +/**CFile**************************************************************** + + FileName [mapperUtils.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - June 1, 2004.] + + Revision [$Id: mapperUtils.c,v 1.8 2004/11/03 22:41:45 satrajit Exp $] + +***********************************************************************/ + +#include "mapperInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define MAP_CO_LIST_SIZE 5 + +static void Map_MappingDfs_rec( Map_Node_t * pNode, Map_NodeVec_t * vNodes, int fCollectEquiv ); +static int Map_MappingCountLevels_rec( Map_Node_t * pNode ); +static float Map_MappingSetRefsAndArea_rec( Map_Man_t * pMan, Map_Node_t * pNode ); +static float Map_MappingSetRefsAndSwitch_rec( Map_Man_t * pMan, Map_Node_t * pNode ); +static float Map_MappingSetRefsAndWire_rec( Map_Man_t * pMan, Map_Node_t * pNode ); +static void Map_MappingDfsCuts_rec( Map_Node_t * pNode, Map_NodeVec_t * vNodes ); +static float Map_MappingArea_rec( Map_Man_t * pMan, Map_Node_t * pNode, Map_NodeVec_t * vNodes ); +static int Map_MappingCompareOutputDelay( Map_Node_t ** ppNode1, Map_Node_t ** ppNode2 ); +static void Map_MappingFindLatest( Map_Man_t * p, int * pNodes, int nNodesMax ); +static unsigned Map_MappingExpandTruth_rec( unsigned uTruth, int nVars ); +static void Map_MappingGetChoiceLevels( Map_Man_t * pMan, Map_Node_t * p1, Map_Node_t * p2, int * pMin, int * pMax ); +static float Map_MappingGetChoiceVolumes( Map_Man_t * pMan, Map_Node_t * p1, Map_Node_t * p2 ); +static int Map_MappingCountUsedNodes( Map_Man_t * pMan, int fChoices ); +static Map_Man_t * s_pMan = NULL; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + + +/**Function************************************************************* + + Synopsis [Computes the DFS ordering of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_NodeVec_t * Map_MappingDfs( Map_Man_t * pMan, int fCollectEquiv ) +{ + Map_NodeVec_t * vNodes; + int i; + // perform the traversal + vNodes = Map_NodeVecAlloc( 100 ); + for ( i = 0; i < pMan->nOutputs; i++ ) + Map_MappingDfs_rec( Map_Regular(pMan->pOutputs[i]), vNodes, fCollectEquiv ); + for ( i = 0; i < vNodes->nSize; i++ ) + vNodes->pArray[i]->fMark0 = 0; +// for ( i = 0; i < pMan->nOutputs; i++ ) +// Map_MappingUnmark_rec( Map_Regular(pMan->pOutputs[i]) ); + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Computes the DFS ordering of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_NodeVec_t * Map_MappingDfsNodes( Map_Man_t * pMan, Map_Node_t ** ppCuts, int nNodes, int fEquiv ) +{ + Map_NodeVec_t * vNodes; + int i; + // perform the traversal + vNodes = Map_NodeVecAlloc( 200 ); + for ( i = 0; i < nNodes; i++ ) + Map_MappingDfs_rec( ppCuts[i], vNodes, fEquiv ); + for ( i = 0; i < vNodes->nSize; i++ ) + vNodes->pArray[i]->fMark0 = 0; + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Recursively computes the DFS ordering of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingDfs_rec( Map_Node_t * pNode, Map_NodeVec_t * vNodes, int fCollectEquiv ) +{ + assert( !Map_IsComplement(pNode) ); + if ( pNode->fMark0 ) + return; + // visit the transitive fanin + if ( Map_NodeIsAnd(pNode) ) + { + Map_MappingDfs_rec( Map_Regular(pNode->p1), vNodes, fCollectEquiv ); + Map_MappingDfs_rec( Map_Regular(pNode->p2), vNodes, fCollectEquiv ); + } + // visit the equivalent nodes + if ( fCollectEquiv && pNode->pNextE ) + Map_MappingDfs_rec( pNode->pNextE, vNodes, fCollectEquiv ); + // make sure the node is not visited through the equivalent nodes + assert( pNode->fMark0 == 0 ); + // mark the node as visited + pNode->fMark0 = 1; + // add the node to the list + Map_NodeVecPush( vNodes, pNode ); +} + + + +/**Function************************************************************* + + Synopsis [Recursively computes the DFS ordering of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingDfsMarked1_rec( Map_Node_t * pNode, Map_NodeVec_t * vNodes, int fFirst ) +{ + assert( !Map_IsComplement(pNode) ); + if ( pNode->fMark0 ) + return; + // visit the transitive fanin + if ( Map_NodeIsAnd(pNode) ) + { + Map_MappingDfsMarked1_rec( Map_Regular(pNode->p1), vNodes, 0 ); + Map_MappingDfsMarked1_rec( Map_Regular(pNode->p2), vNodes, 0 ); + } + // visit the equivalent nodes + if ( !fFirst && pNode->pNextE ) + Map_MappingDfsMarked1_rec( pNode->pNextE, vNodes, 0 ); + // make sure the node is not visited through the equivalent nodes + assert( pNode->fMark0 == 0 ); + // mark the node as visited + pNode->fMark0 = 1; + // add the node to the list + Map_NodeVecPush( vNodes, pNode ); +} + +/**Function************************************************************* + + Synopsis [Recursively computes the DFS ordering of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingDfsMarked2_rec( Map_Node_t * pNode, Map_NodeVec_t * vNodes, Map_NodeVec_t * vBoundary, int fFirst ) +{ + assert( !Map_IsComplement(pNode) ); + if ( pNode->fMark1 ) + return; + if ( pNode->fMark0 || Map_NodeIsVar(pNode) ) + { + pNode->fMark1 = 1; + Map_NodeVecPush(vBoundary, pNode); + return; + } + // visit the transitive fanin + if ( Map_NodeIsAnd(pNode) ) + { + Map_MappingDfsMarked2_rec( Map_Regular(pNode->p1), vNodes, vBoundary, 0 ); + Map_MappingDfsMarked2_rec( Map_Regular(pNode->p2), vNodes, vBoundary, 0 ); + } + // visit the equivalent nodes + if ( !fFirst && pNode->pNextE ) + Map_MappingDfsMarked2_rec( pNode->pNextE, vNodes, vBoundary, 0 ); + // make sure the node is not visited through the equivalent nodes + assert( pNode->fMark1 == 0 ); + // mark the node as visited + pNode->fMark1 = 1; + // add the node to the list + Map_NodeVecPush( vNodes, pNode ); +} + + +/**Function************************************************************* + + Synopsis [Recursively computes the DFS ordering of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingDfsMarked3_rec( Map_Node_t * pNode, Map_NodeVec_t * vNodes ) +{ + assert( !Map_IsComplement(pNode) ); + if ( pNode->fMark0 ) + return; + // visit the transitive fanin + if ( Map_NodeIsAnd(pNode) ) + { + Map_MappingDfsMarked3_rec( Map_Regular(pNode->p1), vNodes ); + Map_MappingDfsMarked3_rec( Map_Regular(pNode->p2), vNodes ); + } + // make sure the node is not visited through the equivalent nodes + assert( pNode->fMark0 == 0 ); + // mark the node as visited + pNode->fMark0 = 1; + // add the node to the list + Map_NodeVecPush( vNodes, pNode ); +} + +/**Function************************************************************* + + Synopsis [Recursively computes the DFS ordering of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingDfsMarked4_rec( Map_Node_t * pNode, Map_NodeVec_t * vNodes ) +{ + assert( !Map_IsComplement(pNode) ); + if ( pNode->fMark1 ) + return; + // visit the transitive fanin + if ( Map_NodeIsAnd(pNode) ) + { + Map_MappingDfsMarked4_rec( Map_Regular(pNode->p1), vNodes ); + Map_MappingDfsMarked4_rec( Map_Regular(pNode->p2), vNodes ); + } + // make sure the node is not visited through the equivalent nodes + assert( pNode->fMark1 == 0 ); + // mark the node as visited + pNode->fMark1 = 1; + // add the node to the list + Map_NodeVecPush( vNodes, pNode ); +} + + + +/**Function************************************************************* + + Synopsis [Computes the number of logic levels not counting PIs/POs.] + + Description [] + + SideEffects [Note that this procedure will reassign the levels assigned + originally by NodeCreate() because it counts the number of levels with + choices differently!] + + SeeAlso [] + +***********************************************************************/ +int Map_MappingCountLevels( Map_Man_t * pMan ) +{ + int i, LevelsMax, LevelsCur; + // perform the traversal + LevelsMax = -1; + for ( i = 0; i < pMan->nOutputs; i++ ) + { + LevelsCur = Map_MappingCountLevels_rec( Map_Regular(pMan->pOutputs[i]) ); + if ( LevelsMax < LevelsCur ) + LevelsMax = LevelsCur; + } + for ( i = 0; i < pMan->nOutputs; i++ ) + Map_MappingUnmark_rec( Map_Regular(pMan->pOutputs[i]) ); + return LevelsMax; +} + +/**Function************************************************************* + + Synopsis [Recursively computes the number of logic levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_MappingCountLevels_rec( Map_Node_t * pNode ) +{ + int Level1, Level2; + assert( !Map_IsComplement(pNode) ); + if ( !Map_NodeIsAnd(pNode) ) + { + pNode->Level = 0; + return 0; + } + if ( pNode->fMark0 ) + return pNode->Level; + pNode->fMark0 = 1; + // visit the transitive fanin + Level1 = Map_MappingCountLevels_rec( Map_Regular(pNode->p1) ); + Level2 = Map_MappingCountLevels_rec( Map_Regular(pNode->p2) ); + // set the number of levels + pNode->Level = 1 + ((Level1>Level2)? Level1: Level2); + return pNode->Level; +} + +/**Function************************************************************* + + Synopsis [Unmarks the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingUnmark( Map_Man_t * pMan ) +{ + int i; + for ( i = 0; i < pMan->nOutputs; i++ ) + Map_MappingUnmark_rec( Map_Regular(pMan->pOutputs[i]) ); +} + +/**Function************************************************************* + + Synopsis [Recursively unmarks the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingUnmark_rec( Map_Node_t * pNode ) +{ + assert( !Map_IsComplement(pNode) ); + if ( pNode->fMark0 == 0 ) + return; + pNode->fMark0 = 0; + if ( !Map_NodeIsAnd(pNode) ) + return; + Map_MappingUnmark_rec( Map_Regular(pNode->p1) ); + Map_MappingUnmark_rec( Map_Regular(pNode->p2) ); + // visit the equivalent nodes + if ( pNode->pNextE ) + Map_MappingUnmark_rec( pNode->pNextE ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingMark_rec( Map_Node_t * pNode ) +{ + assert( !Map_IsComplement(pNode) ); + if ( pNode->fMark0 == 1 ) + return; + pNode->fMark0 = 1; + if ( !Map_NodeIsAnd(pNode) ) + return; + // visit the transitive fanin of the selected cut + Map_MappingMark_rec( Map_Regular(pNode->p1) ); + Map_MappingMark_rec( Map_Regular(pNode->p2) ); +} + +/**Function************************************************************* + + Synopsis [Compares the outputs by their arrival times.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_MappingCompareOutputDelay( Map_Node_t ** ppNode1, Map_Node_t ** ppNode2 ) +{ + Map_Node_t * pNode1 = Map_Regular(*ppNode1); + Map_Node_t * pNode2 = Map_Regular(*ppNode2); + int fPhase1 = !Map_IsComplement(*ppNode1); + int fPhase2 = !Map_IsComplement(*ppNode2); + float Arrival1 = pNode1->tArrival[fPhase1].Worst; + float Arrival2 = pNode2->tArrival[fPhase2].Worst; + if ( Arrival1 < Arrival2 ) + return -1; + if ( Arrival1 > Arrival2 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Finds given number of latest arriving COs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingFindLatest( Map_Man_t * p, int * pNodes, int nNodesMax ) +{ + int nNodes, i, k, v; + assert( p->nOutputs >= nNodesMax ); + pNodes[0] = 0; + nNodes = 1; + for ( i = 1; i < p->nOutputs; i++ ) + { + for ( k = nNodes - 1; k >= 0; k-- ) + if ( Map_MappingCompareOutputDelay( &p->pOutputs[pNodes[k]], &p->pOutputs[i] ) >= 0 ) + break; + if ( k == nNodesMax - 1 ) + continue; + if ( nNodes < nNodesMax ) + nNodes++; + for ( v = nNodes - 1; v > k+1; v-- ) + pNodes[v] = pNodes[v-1]; + pNodes[k+1] = i; + } +} + +/**Function************************************************************* + + Synopsis [Prints a bunch of latest arriving outputs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingPrintOutputArrivals( Map_Man_t * p ) +{ + int pSorted[MAP_CO_LIST_SIZE]; + Map_Time_t * pTimes; + Map_Node_t * pNode; + int fPhase, Limit, i; + int MaxNameSize; + + // determine the number of nodes to print + Limit = (p->nOutputs > MAP_CO_LIST_SIZE)? MAP_CO_LIST_SIZE : p->nOutputs; + + // determine the order + Map_MappingFindLatest( p, pSorted, Limit ); + + // determine max size of the node's name + MaxNameSize = 0; + for ( i = 0; i < Limit; i++ ) + if ( MaxNameSize < (int)strlen(p->ppOutputNames[pSorted[i]]) ) + MaxNameSize = strlen(p->ppOutputNames[pSorted[i]]); + + // print the latest outputs + for ( i = 0; i < Limit; i++ ) + { + // get the i-th latest output + pNode = Map_Regular(p->pOutputs[pSorted[i]]); + fPhase =!Map_IsComplement(p->pOutputs[pSorted[i]]); + pTimes = pNode->tArrival + fPhase; + // print out the best arrival time + printf( "Output %-*s : ", MaxNameSize + 3, p->ppOutputNames[pSorted[i]] ); + printf( "Delay = (%5.2f, %5.2f) ", (double)pTimes->Rise, (double)pTimes->Fall ); + printf( "%s", fPhase? "POS" : "NEG" ); + printf( "\n" ); + } +} + +/**Function************************************************************* + + Synopsis [Sets up the truth tables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingSetupTruthTables( unsigned uTruths[][2] ) +{ + int m, v; + // set up the truth tables + for ( m = 0; m < 32; m++ ) + for ( v = 0; v < 5; v++ ) + if ( m & (1 << v) ) + uTruths[v][0] |= (1 << m); + // make adjustments for the case of 6 variables + for ( v = 0; v < 5; v++ ) + uTruths[v][1] = uTruths[v][0]; + uTruths[5][0] = 0; + uTruths[5][1] = MAP_FULL; +} + +/**Function************************************************************* + + Synopsis [Sets up the truth tables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingSetupTruthTablesLarge( unsigned uTruths[][32] ) +{ + int m, v; + // clean everything + for ( m = 0; m < 32; m++ ) + for ( v = 0; v < 10; v++ ) + uTruths[v][m] = 0; + // set up the truth tables + for ( m = 0; m < 32; m++ ) + for ( v = 0; v < 5; v++ ) + if ( m & (1 << v) ) + { + uTruths[v][0] |= (1 << m); + uTruths[v+5][m] = MAP_FULL; + } + // extend this info for the rest of the first 5 variables + for ( m = 0; m < 32; m++ ) + for ( v = 0; v < 5; v++ ) + uTruths[v][m] = uTruths[v][0]; +/* + // verify + for ( m = 0; m < 1024; m++, printf("\n") ) + for ( v = 0; v < 10; v++ ) + if ( Map_InfoReadVar( uTruths[v], m ) ) + printf( "1" ); + else + printf( "0" ); +*/ +} + +/**Function************************************************************* + + Synopsis [Sets up the mask.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingSetupMask( unsigned uMask[], int nVarsMax ) +{ + if ( nVarsMax == 6 ) + uMask[0] = uMask[1] = MAP_FULL; + else + { + uMask[0] = MAP_MASK(1 << nVarsMax); + uMask[1] = 0; + } +} + +/**Function************************************************************* + + Synopsis [Verify one useful property.] + + Description [This procedure verifies one useful property. After + the FRAIG construction with choice nodes is over, each primary node + should have fanins that are primary nodes. The primary nodes is the + one that does not have pNode->pRepr set to point to another node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_ManCheckConsistency( Map_Man_t * p ) +{ + Map_Node_t * pNode; + Map_NodeVec_t * pVec; + int i; + pVec = Map_MappingDfs( p, 0 ); + for ( i = 0; i < pVec->nSize; i++ ) + { + pNode = pVec->pArray[i]; + if ( Map_NodeIsVar(pNode) ) + { + if ( pNode->pRepr ) + printf( "Primary input %d is a secondary node.\n", pNode->Num ); + } + else if ( Map_NodeIsConst(pNode) ) + { + if ( pNode->pRepr ) + printf( "Constant 1 %d is a secondary node.\n", pNode->Num ); + } + else + { + if ( pNode->pRepr ) + printf( "Internal node %d is a secondary node.\n", pNode->Num ); + if ( Map_Regular(pNode->p1)->pRepr ) + printf( "Internal node %d has first fanin that is a secondary node.\n", pNode->Num ); + if ( Map_Regular(pNode->p2)->pRepr ) + printf( "Internal node %d has second fanin that is a secondary node.\n", pNode->Num ); + } + } + Map_NodeVecFree( pVec ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if current mapping of the node violates fanout limits.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_MappingNodeIsViolator( Map_Node_t * pNode, Map_Cut_t * pCut, int fPosPol ) +{ + return pNode->nRefAct[fPosPol] > (int)pCut->M[fPosPol].pSuperBest->nFanLimit; +} + +/**Function************************************************************* + + Synopsis [Computes the total are flow of the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Map_MappingGetAreaFlow( Map_Man_t * p ) +{ + Map_Node_t * pNode; + Map_Cut_t * pCut; + float aFlowFlowTotal = 0; + int fPosPol, i; + for ( i = 0; i < p->nOutputs; i++ ) + { + pNode = Map_Regular(p->pOutputs[i]); + if ( !Map_NodeIsAnd(pNode) ) + continue; + fPosPol = !Map_IsComplement(p->pOutputs[i]); + pCut = pNode->pCutBest[fPosPol]; + if ( pCut == NULL ) + { + fPosPol = !fPosPol; + pCut = pNode->pCutBest[fPosPol]; + } + aFlowFlowTotal += pNode->pCutBest[fPosPol]->M[fPosPol].AreaFlow; + } + return aFlowFlowTotal; +} + + +/**Function************************************************************* + + Synopsis [Compares the supergates by their level.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_CompareNodesByLevel( Map_Node_t ** ppS1, Map_Node_t ** ppS2 ) +{ + Map_Node_t * pN1 = Map_Regular(*ppS1); + Map_Node_t * pN2 = Map_Regular(*ppS2); + if ( pN1->Level > pN2->Level ) + return -1; + if ( pN1->Level < pN2->Level ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Orders the nodes in the decreasing order of levels.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingSortByLevel( Map_Man_t * pMan, Map_NodeVec_t * vNodes ) +{ + qsort( (void *)vNodes->pArray, vNodes->nSize, sizeof(Map_Node_t *), + (int (*)(const void *, const void *)) Map_CompareNodesByLevel ); +// assert( Map_CompareNodesByLevel( vNodes->pArray, vNodes->pArray + vNodes->nSize - 1 ) <= 0 ); +} + + +/**Function************************************************************* + + Synopsis [Compares the supergates by their pointer.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_CompareNodesByPointer( Map_Node_t ** ppS1, Map_Node_t ** ppS2 ) +{ + if ( *ppS1 < *ppS2 ) + return -1; + if ( *ppS1 > *ppS2 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Counts how many AIG nodes are mapped in both polarities.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_MappingCountDoubles( Map_Man_t * pMan, Map_NodeVec_t * vNodes ) +{ + Map_Node_t * pNode; + int Counter, i; + // count the number of equal adjacent nodes + Counter = 0; + for ( i = 0; i < vNodes->nSize; i++ ) + { + pNode = vNodes->pArray[i]; + if ( !Map_NodeIsAnd(pNode) ) + continue; + if ( (pNode->nRefAct[0] && pNode->pCutBest[0]) && + (pNode->nRefAct[1] && pNode->pCutBest[1]) ) + Counter++; + } + return Counter; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +st_table * Map_CreateTableGate2Super( Map_Man_t * pMan ) +{ + Map_Super_t * pSuper; + st_table * tTable; + int i, nInputs, v; + tTable = st_init_table(strcmp, st_strhash); + for ( i = 0; i < pMan->pSuperLib->nSupersAll; i++ ) + { + pSuper = pMan->pSuperLib->ppSupers[i]; + if ( pSuper->nGates == 1 ) + { + // skip different versions of the same root gate + nInputs = Mio_GateReadInputs(pSuper->pRoot); + for ( v = 0; v < nInputs; v++ ) + if ( pSuper->pFanins[v]->Num != nInputs - 1 - v ) + break; + if ( v != nInputs ) + continue; +// printf( "%s\n", Mio_GateReadName(pSuper->pRoot) ); + if ( st_insert( tTable, (char *)pSuper->pRoot, (char *)pSuper ) ) + { + assert( 0 ); + } + } + } + return tTable; +} + +/**Function************************************************************* + + Synopsis [Get the FRAIG node with phase.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_ManCleanData( Map_Man_t * p ) +{ + int i; + for ( i = 0; i < p->vNodesAll->nSize; i++ ) + p->vNodesAll->pArray[i]->pData0 = p->vNodesAll->pArray[i]->pData1 = 0; +} + +/**Function************************************************************* + + Synopsis [Expand the truth table] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingExpandTruth( unsigned uTruth[2], int nVars ) +{ + assert( nVars < 7 ); + if ( nVars == 6 ) + return; + if ( nVars < 5 ) + { + uTruth[0] &= MAP_MASK( (1<vAnds->nSize; i++ ) + { + // skip primary inputs + pNode = p->vAnds->pArray[i]; + if ( !Map_NodeIsAnd( pNode ) ) + continue; + // skip a secondary node + if ( pNode->pRepr ) + continue; + // count the switching nodes + if ( pNode->nRefAct[0] > 0 ) + Map_TimeCutComputeArrival( pNode, pNode->pCutBest[0], 0, MAP_FLOAT_LARGE ); + if ( pNode->nRefAct[1] > 0 ) + Map_TimeCutComputeArrival( pNode, pNode->pCutBest[1], 1, MAP_FLOAT_LARGE ); + } + Result = Map_TimeComputeArrivalMax(p); + printf( "Max arrival times with fanouts = %10.2f.\n", Result ); + return Result; +} + + +/**Function************************************************************* + + Synopsis [Sets up the mask.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_MappingGetMaxLevel( Map_Man_t * pMan ) +{ + int nLevelMax, i; + nLevelMax = 0; + for ( i = 0; i < pMan->nOutputs; i++ ) + nLevelMax = ((unsigned)nLevelMax) > Map_Regular(pMan->pOutputs[i])->Level? + nLevelMax : Map_Regular(pMan->pOutputs[i])->Level; + return nLevelMax; +} + +/**Function************************************************************* + + Synopsis [Analyses choice nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_MappingUpdateLevel_rec( Map_Man_t * pMan, Map_Node_t * pNode, int fMaximum ) +{ + Map_Node_t * pTemp; + int Level1, Level2, LevelE; + assert( !Map_IsComplement(pNode) ); + if ( !Map_NodeIsAnd(pNode) ) + return pNode->Level; + // skip the visited node + if ( pNode->TravId == pMan->nTravIds ) + return pNode->Level; + pNode->TravId = pMan->nTravIds; + // compute levels of the children nodes + Level1 = Map_MappingUpdateLevel_rec( pMan, Map_Regular(pNode->p1), fMaximum ); + Level2 = Map_MappingUpdateLevel_rec( pMan, Map_Regular(pNode->p2), fMaximum ); + pNode->Level = 1 + MAP_MAX( Level1, Level2 ); + if ( pNode->pNextE ) + { + LevelE = Map_MappingUpdateLevel_rec( pMan, pNode->pNextE, fMaximum ); + if ( fMaximum ) + { + if ( pNode->Level < (unsigned)LevelE ) + pNode->Level = LevelE; + } + else + { + if ( pNode->Level > (unsigned)LevelE ) + pNode->Level = LevelE; + } + // set the level of all equivalent nodes to be the same minimum + if ( pNode->pRepr == NULL ) // the primary node + for ( pTemp = pNode->pNextE; pTemp; pTemp = pTemp->pNextE ) + pTemp->Level = pNode->Level; + } + return pNode->Level; +} + +/**Function************************************************************* + + Synopsis [Resets the levels of the nodes in the choice graph.] + + Description [Makes the level of the choice nodes to be equal to the + maximum of the level of the nodes in the equivalence class. This way + sorting by level leads to the reverse topological order, which is + needed for the required time computation.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingSetChoiceLevels( Map_Man_t * pMan ) +{ + int i; + pMan->nTravIds++; + for ( i = 0; i < pMan->nOutputs; i++ ) + Map_MappingUpdateLevel_rec( pMan, Map_Regular(pMan->pOutputs[i]), 1 ); +} + +/**Function************************************************************* + + Synopsis [Reports statistics on choice nodes.] + + Description [The number of choice nodes is the number of primary nodes, + which has pNextE set to a pointer. The number of choices is the number + of entries in the equivalent-node lists of the primary nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingReportChoices( Map_Man_t * pMan ) +{ + Map_Node_t * pNode, * pTemp; + int nChoiceNodes, nChoices; + int i, LevelMax1, LevelMax2; + + // report the number of levels + LevelMax1 = Map_MappingGetMaxLevel( pMan ); + pMan->nTravIds++; + for ( i = 0; i < pMan->nOutputs; i++ ) + Map_MappingUpdateLevel_rec( pMan, Map_Regular(pMan->pOutputs[i]), 0 ); + LevelMax2 = Map_MappingGetMaxLevel( pMan ); + + // report statistics about choices + nChoiceNodes = nChoices = 0; + for ( i = 0; i < pMan->vAnds->nSize; i++ ) + { + pNode = pMan->vAnds->pArray[i]; + if ( pNode->pRepr == NULL && pNode->pNextE != NULL ) + { // this is a choice node = the primary node that has equivalent nodes + nChoiceNodes++; + for ( pTemp = pNode; pTemp; pTemp = pTemp->pNextE ) + nChoices++; + } + } + printf( "Maximum level: Original = %d. Reduced due to choices = %d.\n", LevelMax1, LevelMax2 ); + printf( "Choice stats: Choice nodes = %d. Total choices = %d.\n", nChoiceNodes, nChoices ); +} + +/**Function************************************************************* + + Synopsis [Computes the maximum and minimum levels of the choice nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_MappingGetChoiceLevels( Map_Man_t * pMan, Map_Node_t * p1, Map_Node_t * p2, int * pMin, int * pMax ) +{ + Map_NodeVec_t * vNodes; + Map_NodeVec_t * vBoundary; + Map_Node_t * pNode; + int i, Min, Max; + + vNodes = Map_NodeVecAlloc( 100 ); + vBoundary = Map_NodeVecAlloc( 100 ); + Map_MappingDfsMarked1_rec( p1, vNodes, 1 ); + Map_MappingDfsMarked2_rec( p2, vNodes, vBoundary, 1 ); + // clean the marks + Min = 100000; + Max = -100000; + for ( i = 0; i < vBoundary->nSize; i++ ) + { + pNode = vBoundary->pArray[i]; + if ( Min > (int)pNode->Level ) + Min = pNode->Level; + if ( Max < (int)pNode->Level ) + Max = pNode->Level; + } + Map_NodeVecFree( vBoundary ); + for ( i = 0; i < vNodes->nSize; i++ ) + { + pNode = vNodes->pArray[i]; + pNode->fMark0 = pNode->fMark1 = 0; + } + Map_NodeVecFree( vNodes ); + *pMin = Min; + *pMax = Max; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Map_MappingGetChoiceVolumes( Map_Man_t * pMan, Map_Node_t * p1, Map_Node_t * p2 ) +{ + Map_NodeVec_t * vNodes; + Map_Node_t * pNode; + int i, nVolumeTotal, nVolumeUnique; + + vNodes = Map_NodeVecAlloc( 100 ); + Map_MappingDfsMarked3_rec( p1, vNodes ); + Map_MappingDfsMarked4_rec( p2, vNodes ); + // clean the marks + nVolumeTotal = nVolumeUnique = 0; + for ( i = 0; i < vNodes->nSize; i++ ) + { + pNode = vNodes->pArray[i]; + if ( !Map_NodeIsAnd(pNode) ) + continue; + nVolumeTotal++; + if ( pNode->fMark0 ^ pNode->fMark1 ) + nVolumeUnique++; + pNode->fMark0 = pNode->fMark1 = 0; + } + Map_NodeVecFree( vNodes ); +// return ((float)nVolumeUnique)/nVolumeTotal; + return (float)nVolumeUnique; +} + + +/**Function************************************************************* + + Synopsis [Computes the maximum and minimum levels of the choice nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_MappingCountUsedNodes( Map_Man_t * pMan, int fChoices ) +{ + Map_NodeVec_t * vNodes; + int Result; + vNodes = Map_MappingDfs( pMan, fChoices ); + Result = vNodes->nSize; + Map_NodeVecFree( vNodes ); + return Result; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/mapper/mapperVec.c b/abc_with_bb_support/src/map/mapper/mapperVec.c new file mode 100644 index 000000000..aad9137c8 --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/mapperVec.c @@ -0,0 +1,318 @@ +/**CFile**************************************************************** + + FileName [mapperVec.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Generic technology mapping engine.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - June 1, 2004.] + + Revision [$Id: mapperVec.c,v 1.3 2005/01/23 06:59:45 alanmi Exp $] + +***********************************************************************/ + +#include "mapperInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Map_NodeVecCompareLevels( Map_Node_t ** pp1, Map_Node_t ** pp2 ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_NodeVec_t * Map_NodeVecAlloc( int nCap ) +{ + Map_NodeVec_t * p; + p = ALLOC( Map_NodeVec_t, 1 ); + if ( nCap > 0 && nCap < 16 ) + nCap = 16; + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ALLOC( Map_Node_t *, p->nCap ) : NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_NodeVecFree( Map_NodeVec_t * p ) +{ + FREE( p->pArray ); + FREE( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Node_t ** Map_NodeVecReadArray( Map_NodeVec_t * p ) +{ + return p->pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_NodeVecReadSize( Map_NodeVec_t * p ) +{ + return p->nSize; +} + +/**Function************************************************************* + + Synopsis [Resizes the vector to the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_NodeVecGrow( Map_NodeVec_t * p, int nCapMin ) +{ + if ( p->nCap >= nCapMin ) + return; + p->pArray = REALLOC( Map_Node_t *, p->pArray, nCapMin ); + p->nCap = nCapMin; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_NodeVecShrink( Map_NodeVec_t * p, int nSizeNew ) +{ + assert( p->nSize >= nSizeNew ); + p->nSize = nSizeNew; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_NodeVecClear( Map_NodeVec_t * p ) +{ + p->nSize = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_NodeVecPush( Map_NodeVec_t * p, Map_Node_t * Entry ) +{ + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Map_NodeVecGrow( p, 16 ); + else + Map_NodeVecGrow( p, 2 * p->nCap ); + } + p->pArray[p->nSize++] = Entry; +} + +/**Function************************************************************* + + Synopsis [Add the element while ensuring uniqueness.] + + Description [Returns 1 if the element was found, and 0 if it was new. ] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_NodeVecPushUnique( Map_NodeVec_t * p, Map_Node_t * Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return 1; + Map_NodeVecPush( p, Entry ); + return 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Node_t * Map_NodeVecPop( Map_NodeVec_t * p ) +{ + return p->pArray[--p->nSize]; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_NodeVecRemove( Map_NodeVec_t * p, Map_Node_t * Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + break; + assert( i < p->nSize ); + for ( i++; i < p->nSize; i++ ) + p->pArray[i-1] = p->pArray[i]; + p->nSize--; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_NodeVecWriteEntry( Map_NodeVec_t * p, int i, Map_Node_t * Entry ) +{ + assert( i >= 0 && i < p->nSize ); + p->pArray[i] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Map_Node_t * Map_NodeVecReadEntry( Map_NodeVec_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray[i]; +} + +/**Function************************************************************* + + Synopsis [Sorting the entries by their integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Map_NodeVecSortByLevel( Map_NodeVec_t * p ) +{ + qsort( (void *)p->pArray, p->nSize, sizeof(Map_Node_t *), + (int (*)(const void *, const void *)) Map_NodeVecCompareLevels ); +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two clauses.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Map_NodeVecCompareLevels( Map_Node_t ** pp1, Map_Node_t ** pp2 ) +{ + int Level1 = Map_Regular(*pp1)->Level; + int Level2 = Map_Regular(*pp2)->Level; + if ( Level1 < Level2 ) + return -1; + if ( Level1 > Level2 ) + return 1; + if ( Map_Regular(*pp1)->Num < Map_Regular(*pp2)->Num ) + return -1; + if ( Map_Regular(*pp1)->Num > Map_Regular(*pp2)->Num ) + return 1; + return 0; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/map/mapper/module.make b/abc_with_bb_support/src/map/mapper/module.make new file mode 100644 index 000000000..2960db20a --- /dev/null +++ b/abc_with_bb_support/src/map/mapper/module.make @@ -0,0 +1,18 @@ +SRC += src/map/mapper/mapper.c \ + src/map/mapper/mapperCanon.c \ + src/map/mapper/mapperCore.c \ + src/map/mapper/mapperCreate.c \ + src/map/mapper/mapperCut.c \ + src/map/mapper/mapperCutUtils.c \ + src/map/mapper/mapperFanout.c \ + src/map/mapper/mapperLib.c \ + src/map/mapper/mapperMatch.c \ + src/map/mapper/mapperRefs.c \ + src/map/mapper/mapperSuper.c \ + src/map/mapper/mapperSwitch.c \ + src/map/mapper/mapperTable.c \ + src/map/mapper/mapperTime.c \ + src/map/mapper/mapperTree.c \ + src/map/mapper/mapperTruth.c \ + src/map/mapper/mapperUtils.c \ + src/map/mapper/mapperVec.c diff --git a/abc_with_bb_support/src/map/mio/mio.c b/abc_with_bb_support/src/map/mio/mio.c new file mode 100644 index 000000000..7a2840f5d --- /dev/null +++ b/abc_with_bb_support/src/map/mio/mio.c @@ -0,0 +1,269 @@ +/**CFile**************************************************************** + + FileName [mio.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [File reading/writing for technology mapping.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - August 18, 2003.] + + Revision [$Id: mio.c,v 1.4 2004/08/05 18:34:51 satrajit Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "mvc.h" +#include "mainInt.h" +#include "mioInt.h" +#include "mapper.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Mio_CommandReadLibrary( Abc_Frame_t * pAbc, int argc, char **argv ); +static int Mio_CommandPrintLibrary( Abc_Frame_t * pAbc, int argc, char **argv ); + +// internal version of GENLIB library +static char * pMcncGenlib[25] = { + "GATE inv1 1 O=!a; PIN * INV 1 999 0.9 0.0 0.9 0.0\n", + "GATE inv2 2 O=!a; PIN * INV 2 999 1.0 0.0 1.0 0.0\n", + "GATE inv3 3 O=!a; PIN * INV 3 999 1.1 0.0 1.1 0.0\n", + "GATE inv4 4 O=!a; PIN * INV 4 999 1.2 0.0 1.2 0.0\n", + "GATE nand2 2 O=!(a*b); PIN * INV 1 999 1.0 0.0 1.0 0.0\n", + "GATE nand3 3 O=!(a*b*c); PIN * INV 1 999 1.1 0.0 1.1 0.0\n", + "GATE nand4 4 O=!(a*b*c*d); PIN * INV 1 999 1.4 0.0 1.4 0.0\n", + "GATE nor2 2 O=!(a+b); PIN * INV 1 999 1.4 0.0 1.4 0.0\n", + "GATE nor3 3 O=!(a+b+c); PIN * INV 1 999 2.4 0.0 2.4 0.0\n", + "GATE nor4 4 O=!(a+b+c+d); PIN * INV 1 999 3.8 0.0 3.8 0.0\n", + "GATE xora 5 O=a*!b+!a*b; PIN * UNKNOWN 2 999 1.9 0.0 1.9 0.0\n", + "GATE xorb 5 O=!(a*b+!a*!b); PIN * UNKNOWN 2 999 1.9 0.0 1.9 0.0\n", + "GATE xnora 5 O=a*b+!a*!b; PIN * UNKNOWN 2 999 2.1 0.0 2.1 0.0\n", + "GATE xnorb 5 O=!(!a*b+a*!b); PIN * UNKNOWN 2 999 2.1 0.0 2.1 0.0\n", + "GATE aoi21 3 O=!(a*b+c); PIN * INV 1 999 1.6 0.0 1.6 0.0\n", + "GATE aoi22 4 O=!(a*b+c*d); PIN * INV 1 999 2.0 0.0 2.0 0.0\n", + "GATE oai21 3 O=!((a+b)*c); PIN * INV 1 999 1.6 0.0 1.6 0.0\n", + "GATE oai22 4 O=!((a+b)*(c+d)); PIN * INV 1 999 2.0 0.0 2.0 0.0\n", + "GATE buf 1 O=a; PIN * NONINV 1 999 1.0 0.0 1.0 0.0\n", + "GATE zero 0 O=CONST0;\n", + "GATE one 0 O=CONST1;\n" +}; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mio_Init( Abc_Frame_t * pAbc ) +{ + char * pFileTemp = "mcnc_temp.genlib"; + Mio_Library_t * pLibGen; + FILE * pFile; + int i; + + // write genlib into file + pFile = fopen( pFileTemp, "w" ); + for ( i = 0; pMcncGenlib[i]; i++ ) + fputs( pMcncGenlib[i], pFile ); + fclose( pFile ); + // read genlib from file + pLibGen = Mio_LibraryRead( pAbc, pFileTemp, NULL, 0 ); + Abc_FrameSetLibGen( pLibGen ); +#ifdef WIN32 + _unlink( pFileTemp ); +#else + unlink( pFileTemp ); +#endif + + Cmd_CommandAdd( pAbc, "SC mapping", "read_library", Mio_CommandReadLibrary, 0 ); + Cmd_CommandAdd( pAbc, "SC mapping", "print_library", Mio_CommandPrintLibrary, 0 ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mio_End() +{ +// Mio_LibraryDelete( s_pLib ); + Mio_LibraryDelete( Abc_FrameReadLibGen() ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mio_CommandReadLibrary( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + FILE * pFile; + FILE * pOut, * pErr; + Mio_Library_t * pLib; + Abc_Ntk_t * pNet; + char * FileName; + int fVerbose; + int c; + + pNet = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set the defaults + fVerbose = 1; + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "vh")) != EOF ) + { + switch (c) + { + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + break; + default: + goto usage; + } + } + + + if ( argc != globalUtilOptind + 1 ) + { + goto usage; + } + + // get the input file name + FileName = argv[globalUtilOptind]; + if ( (pFile = Io_FileOpen( FileName, "open_path", "r", 0 )) == NULL ) + { + fprintf( pErr, "Cannot open input file \"%s\". ", FileName ); + if ( (FileName = Extra_FileGetSimilarName( FileName, ".genlib", ".lib", ".gen", ".g", NULL )) ) + fprintf( pErr, "Did you mean \"%s\"?", FileName ); + fprintf( pErr, "\n" ); + return 1; + } + fclose( pFile ); + + // set the new network + pLib = Mio_LibraryRead( pAbc, FileName, 0, fVerbose ); + if ( pLib == NULL ) + { + fprintf( pErr, "Reading GENLIB library has failed.\n" ); + return 1; + } + // free the current superlib because it depends on the old Mio library + if ( Abc_FrameReadLibSuper() ) + { + extern void Map_SuperLibFree( Map_SuperLib_t * p ); +// Map_SuperLibFree( s_pSuperLib ); +// s_pSuperLib = NULL; + Map_SuperLibFree( Abc_FrameReadLibSuper() ); + Abc_FrameSetLibSuper( NULL ); + } + + // replace the current library +// Mio_LibraryDelete( s_pLib ); +// s_pLib = pLib; + Mio_LibraryDelete( Abc_FrameReadLibGen() ); + Abc_FrameSetLibGen( pLib ); + return 0; + +usage: + fprintf( pErr, "usage: read_library [-vh]\n"); + fprintf( pErr, "\t read the library from a genlib file\n" ); + fprintf( pErr, "\t-h : enable verbose output\n"); + return 1; /* error exit */ +} + + +/**Function************************************************************* + + Synopsis [Command procedure to read LUT libraries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mio_CommandPrintLibrary( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + FILE * pOut, * pErr; + Abc_Ntk_t * pNet; + int fVerbose; + int c; + + pNet = Abc_FrameReadNtk(pAbc); + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set the defaults + fVerbose = 1; + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "vh")) != EOF ) + { + switch (c) + { + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + break; + default: + goto usage; + } + } + + + if ( argc != globalUtilOptind ) + { + goto usage; + } + + // set the new network + Mio_WriteLibrary( stdout, Abc_FrameReadLibGen(), 0 ); + return 0; + +usage: + fprintf( pErr, "\nusage: print_library [-vh]\n"); + fprintf( pErr, "\t print the current genlib library\n" ); + fprintf( pErr, "\t-v : toggles enabling of verbose output [default = %s]\n", (fVerbose? "yes" : "no") ); + fprintf( pErr, "\t-h : print the command usage\n"); + return 1; /* error exit */ +} +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/mio/mio.h b/abc_with_bb_support/src/map/mio/mio.h new file mode 100644 index 000000000..94d629621 --- /dev/null +++ b/abc_with_bb_support/src/map/mio/mio.h @@ -0,0 +1,150 @@ +/**CFile**************************************************************** + + FileName [mio.h] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [File reading/writing for technology mapping.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 8, 2003.] + + Revision [$Id: mio.h,v 1.6 2004/08/09 22:16:31 satrajit Exp $] + +***********************************************************************/ + +#ifndef __MIO_H__ +#define __MIO_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef enum { MIO_PHASE_UNKNOWN, MIO_PHASE_INV, MIO_PHASE_NONINV } Mio_PinPhase_t; + +typedef struct Mio_LibraryStruct_t_ Mio_Library_t; +typedef struct Mio_GateStruct_t_ Mio_Gate_t; +typedef struct Mio_PinStruct_t_ Mio_Pin_t; + +//////////////////////////////////////////////////////////////////////// +/// GLOBAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define Mio_LibraryForEachGate( Lib, Gate ) \ + for ( Gate = Mio_LibraryReadGates(Lib); \ + Gate; \ + Gate = Mio_GateReadNext(Gate) ) +#define Mio_LibraryForEachGateSafe( Lib, Gate, Gate2 ) \ + for ( Gate = Mio_LibraryReadGates(Lib), \ + Gate2 = (Gate? Mio_GateReadNext(Gate): NULL); \ + Gate; \ + Gate = Gate2, \ + Gate2 = (Gate? Mio_GateReadNext(Gate): NULL) ) + +#define Mio_GateForEachPin( Gate, Pin ) \ + for ( Pin = Mio_GateReadPins(Gate); \ + Pin; \ + Pin = Mio_PinReadNext(Pin) ) +#define Mio_GateForEachPinSafe( Gate, Pin, Pin2 ) \ + for ( Pin = Mio_GateReadPins(Gate), \ + Pin2 = (Pin? Mio_PinReadNext(Pin): NULL); \ + Pin; \ + Pin = Pin2, \ + Pin2 = (Pin? Mio_PinReadNext(Pin): NULL) ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== mioApi.c =============================================================*/ +extern char * Mio_LibraryReadName ( Mio_Library_t * pLib ); +extern int Mio_LibraryReadGateNum ( Mio_Library_t * pLib ); +extern Mio_Gate_t * Mio_LibraryReadGates ( Mio_Library_t * pLib ); +extern DdManager * Mio_LibraryReadDd ( Mio_Library_t * pLib ); +extern Mio_Gate_t * Mio_LibraryReadGateByName ( Mio_Library_t * pLib, char * pName ); +extern char * Mio_LibraryReadSopByName ( Mio_Library_t * pLib, char * pName ); +extern Mio_Gate_t * Mio_LibraryReadConst0 ( Mio_Library_t * pLib ); +extern Mio_Gate_t * Mio_LibraryReadConst1 ( Mio_Library_t * pLib ); +extern Mio_Gate_t * Mio_LibraryReadNand2 ( Mio_Library_t * pLib ); +extern Mio_Gate_t * Mio_LibraryReadAnd2 ( Mio_Library_t * pLib ); +extern Mio_Gate_t * Mio_LibraryReadBuf ( Mio_Library_t * pLib ); +extern Mio_Gate_t * Mio_LibraryReadInv ( Mio_Library_t * pLib ); +extern float Mio_LibraryReadDelayInvRise( Mio_Library_t * pLib ); +extern float Mio_LibraryReadDelayInvFall( Mio_Library_t * pLib ); +extern float Mio_LibraryReadDelayInvMax( Mio_Library_t * pLib ); +extern float Mio_LibraryReadDelayNand2Rise( Mio_Library_t * pLib ); +extern float Mio_LibraryReadDelayNand2Fall( Mio_Library_t * pLib ); +extern float Mio_LibraryReadDelayNand2Max( Mio_Library_t * pLib ); +extern float Mio_LibraryReadDelayAnd2Max( Mio_Library_t * pLib ); +extern float Mio_LibraryReadAreaInv ( Mio_Library_t * pLib ); +extern float Mio_LibraryReadAreaBuf ( Mio_Library_t * pLib ); +extern float Mio_LibraryReadAreaNand2 ( Mio_Library_t * pLib ); +extern int Mio_LibraryReadGateNameMax( Mio_Library_t * pLib ); +extern char * Mio_GateReadName ( Mio_Gate_t * pGate ); +extern char * Mio_GateReadOutName ( Mio_Gate_t * pGate ); +extern double Mio_GateReadArea ( Mio_Gate_t * pGate ); +extern char * Mio_GateReadForm ( Mio_Gate_t * pGate ); +extern Mio_Pin_t * Mio_GateReadPins ( Mio_Gate_t * pGate ); +extern Mio_Library_t * Mio_GateReadLib ( Mio_Gate_t * pGate ); +extern Mio_Gate_t * Mio_GateReadNext ( Mio_Gate_t * pGate ); +extern int Mio_GateReadInputs ( Mio_Gate_t * pGate ); +extern double Mio_GateReadDelayMax ( Mio_Gate_t * pGate ); +extern char * Mio_GateReadSop ( Mio_Gate_t * pGate ); +extern DdNode * Mio_GateReadFunc ( Mio_Gate_t * pGate ); +extern char * Mio_PinReadName ( Mio_Pin_t * pPin ); +extern Mio_PinPhase_t Mio_PinReadPhase ( Mio_Pin_t * pPin ); +extern double Mio_PinReadInputLoad ( Mio_Pin_t * pPin ); +extern double Mio_PinReadMaxLoad ( Mio_Pin_t * pPin ); +extern double Mio_PinReadDelayBlockRise ( Mio_Pin_t * pPin ); +extern double Mio_PinReadDelayFanoutRise( Mio_Pin_t * pPin ); +extern double Mio_PinReadDelayBlockFall ( Mio_Pin_t * pPin ); +extern double Mio_PinReadDelayFanoutFall( Mio_Pin_t * pPin ); +extern double Mio_PinReadDelayBlockMax ( Mio_Pin_t * pPin ); +extern Mio_Pin_t * Mio_PinReadNext ( Mio_Pin_t * pPin ); +/*=== mioRead.c =============================================================*/ +extern Mio_Library_t * Mio_LibraryRead( void * pAbc, char * FileName, char * ExcludeFile, int fVerbose ); +extern int Mio_LibraryReadExclude( void * pAbc, char * ExcludeFile, st_table * tExcludeGate ); +/*=== mioFunc.c =============================================================*/ +extern int Mio_LibraryParseFormulas( Mio_Library_t * pLib ); +/*=== mioUtils.c =============================================================*/ +extern void Mio_LibraryDelete( Mio_Library_t * pLib ); +extern void Mio_GateDelete( Mio_Gate_t * pGate ); +extern void Mio_PinDelete( Mio_Pin_t * pPin ); +extern Mio_Pin_t * Mio_PinDup( Mio_Pin_t * pPin ); +extern void Mio_WriteLibrary( FILE * pFile, Mio_Library_t * pLib, int fPrintSops ); +extern Mio_Gate_t ** Mio_CollectRoots( Mio_Library_t * pLib, int nInputs, float tDelay, bool fSkipInv, int * pnGates ); +extern void Mio_DeriveTruthTable( Mio_Gate_t * pGate, unsigned uTruthsIn[][2], int nSigns, int nInputs, unsigned uTruthRes[] ); +extern void Mio_DeriveGateDelays( Mio_Gate_t * pGate, + float ** ptPinDelays, int nPins, int nInputs, float tDelayZero, + float * ptDelaysRes, float * ptPinDelayMax ); +extern Mio_Gate_t * Mio_GateCreatePseudo( int nInputs ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/map/mio/mioApi.c b/abc_with_bb_support/src/map/mio/mioApi.c new file mode 100644 index 000000000..b3ccf04f4 --- /dev/null +++ b/abc_with_bb_support/src/map/mio/mioApi.c @@ -0,0 +1,172 @@ +/**CFile**************************************************************** + + FileName [mioApi.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [File reading/writing for technology mapping.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 8, 2003.] + + Revision [$Id: mioApi.c,v 1.4 2004/06/28 14:20:25 alanmi Exp $] + +***********************************************************************/ + +#include "mioInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Mio_LibraryReadName ( Mio_Library_t * pLib ) { return pLib->pName; } +int Mio_LibraryReadGateNum ( Mio_Library_t * pLib ) { return pLib->nGates; } +Mio_Gate_t * Mio_LibraryReadGates ( Mio_Library_t * pLib ) { return pLib->pGates; } +DdManager * Mio_LibraryReadDd ( Mio_Library_t * pLib ) { return pLib->dd; } +Mio_Gate_t * Mio_LibraryReadBuf ( Mio_Library_t * pLib ) { return pLib->pGateBuf; } +Mio_Gate_t * Mio_LibraryReadInv ( Mio_Library_t * pLib ) { return pLib->pGateInv; } +Mio_Gate_t * Mio_LibraryReadConst0 ( Mio_Library_t * pLib ) { return pLib->pGate0; } +Mio_Gate_t * Mio_LibraryReadConst1 ( Mio_Library_t * pLib ) { return pLib->pGate1; } +Mio_Gate_t * Mio_LibraryReadNand2 ( Mio_Library_t * pLib ) { return pLib->pGateNand2; } +Mio_Gate_t * Mio_LibraryReadAnd2 ( Mio_Library_t * pLib ) { return pLib->pGateAnd2; } +float Mio_LibraryReadDelayInvRise ( Mio_Library_t * pLib ) { return (float)(pLib->pGateInv? pLib->pGateInv->pPins->dDelayBlockRise : 0.0); } +float Mio_LibraryReadDelayInvFall ( Mio_Library_t * pLib ) { return (float)(pLib->pGateInv? pLib->pGateInv->pPins->dDelayBlockFall : 0.0); } +float Mio_LibraryReadDelayInvMax ( Mio_Library_t * pLib ) { return (float)(pLib->pGateInv? pLib->pGateInv->pPins->dDelayBlockMax : 0.0); } +float Mio_LibraryReadDelayNand2Rise( Mio_Library_t * pLib ) { return (float)(pLib->pGateNand2? pLib->pGateNand2->pPins->dDelayBlockRise : 0.0); } +float Mio_LibraryReadDelayNand2Fall( Mio_Library_t * pLib ) { return (float)(pLib->pGateNand2? pLib->pGateNand2->pPins->dDelayBlockFall : 0.0); } +float Mio_LibraryReadDelayNand2Max ( Mio_Library_t * pLib ) { return (float)(pLib->pGateNand2? pLib->pGateNand2->pPins->dDelayBlockMax : 0.0); } +float Mio_LibraryReadDelayAnd2Max ( Mio_Library_t * pLib ) { return (float)(pLib->pGateAnd2? pLib->pGateAnd2->pPins->dDelayBlockMax : 0.0); } +float Mio_LibraryReadAreaInv ( Mio_Library_t * pLib ) { return (float)(pLib->pGateInv? pLib->pGateInv->dArea : 0.0); } +float Mio_LibraryReadAreaBuf ( Mio_Library_t * pLib ) { return (float)(pLib->pGateBuf? pLib->pGateBuf->dArea : 0.0); } +float Mio_LibraryReadAreaNand2 ( Mio_Library_t * pLib ) { return (float)(pLib->pGateNand2? pLib->pGateNand2->dArea : 0.0); } + +/**Function************************************************************* + + Synopsis [Returns the longest gate name.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mio_LibraryReadGateNameMax( Mio_Library_t * pLib ) +{ + Mio_Gate_t * pGate; + int LenMax = 0, LenCur; + Mio_LibraryForEachGate( pLib, pGate ) + { + LenCur = strlen( Mio_GateReadName(pGate) ); + if ( LenMax < LenCur ) + LenMax = LenCur; + } + return LenMax; +} + +/**Function************************************************************* + + Synopsis [Read Mvc of the gate by name.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mio_Gate_t * Mio_LibraryReadGateByName( Mio_Library_t * pLib, char * pName ) +{ + Mio_Gate_t * pGate; + if ( st_lookup( pLib->tName2Gate, pName, (char **)&pGate ) ) + return pGate; + return NULL; +} + +/**Function************************************************************* + + Synopsis [Read Mvc of the gate by name.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Mio_LibraryReadSopByName( Mio_Library_t * pLib, char * pName ) +{ + Mio_Gate_t * pGate; + if ( st_lookup( pLib->tName2Gate, pName, (char **)&pGate ) ) + return pGate->pSop; + return NULL; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Mio_GateReadName ( Mio_Gate_t * pGate ) { return pGate->pName; } +char * Mio_GateReadOutName ( Mio_Gate_t * pGate ) { return pGate->pOutName; } +double Mio_GateReadArea ( Mio_Gate_t * pGate ) { return pGate->dArea; } +char * Mio_GateReadForm ( Mio_Gate_t * pGate ) { return pGate->pForm; } +Mio_Pin_t * Mio_GateReadPins ( Mio_Gate_t * pGate ) { return pGate->pPins; } +Mio_Library_t * Mio_GateReadLib ( Mio_Gate_t * pGate ) { return pGate->pLib; } +Mio_Gate_t * Mio_GateReadNext ( Mio_Gate_t * pGate ) { return pGate->pNext; } +int Mio_GateReadInputs ( Mio_Gate_t * pGate ) { return pGate->nInputs; } +double Mio_GateReadDelayMax( Mio_Gate_t * pGate ) { return pGate->dDelayMax; } +char * Mio_GateReadSop ( Mio_Gate_t * pGate ) { return pGate->pSop; } +DdNode * Mio_GateReadFunc ( Mio_Gate_t * pGate ) { return pGate->bFunc; } + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Mio_PinReadName ( Mio_Pin_t * pPin ) { return pPin->pName; } +Mio_PinPhase_t Mio_PinReadPhase ( Mio_Pin_t * pPin ) { return pPin->Phase; } +double Mio_PinReadInputLoad ( Mio_Pin_t * pPin ) { return pPin->dLoadInput; } +double Mio_PinReadMaxLoad ( Mio_Pin_t * pPin ) { return pPin->dLoadMax; } +double Mio_PinReadDelayBlockRise ( Mio_Pin_t * pPin ) { return pPin->dDelayBlockRise; } +double Mio_PinReadDelayFanoutRise( Mio_Pin_t * pPin ) { return pPin->dDelayFanoutRise;} +double Mio_PinReadDelayBlockFall ( Mio_Pin_t * pPin ) { return pPin->dDelayBlockFall; } +double Mio_PinReadDelayFanoutFall( Mio_Pin_t * pPin ) { return pPin->dDelayFanoutFall;} +double Mio_PinReadDelayBlockMax ( Mio_Pin_t * pPin ) { return pPin->dDelayBlockMax; } +Mio_Pin_t * Mio_PinReadNext ( Mio_Pin_t * pPin ) { return pPin->pNext; } + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/mio/mioFunc.c b/abc_with_bb_support/src/map/mio/mioFunc.c new file mode 100644 index 000000000..78b24b830 --- /dev/null +++ b/abc_with_bb_support/src/map/mio/mioFunc.c @@ -0,0 +1,268 @@ +/**CFile**************************************************************** + + FileName [mioFunc.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [File reading/writing for technology mapping.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 8, 2003.] + + Revision [$Id: mioFunc.c,v 1.4 2004/06/28 14:20:25 alanmi Exp $] + +***********************************************************************/ + +#include "mioInt.h" +#include "parse.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// these symbols (and no other) can appear in the formulas +#define MIO_SYMB_AND '*' +#define MIO_SYMB_OR '+' +#define MIO_SYMB_NOT '!' +#define MIO_SYMB_AFTNOT '\'' +#define MIO_SYMB_OPEN '(' +#define MIO_SYMB_CLOSE ')' + +static int Mio_GateParseFormula( Mio_Gate_t * pGate ); +static int Mio_GateCollectNames( char * pFormula, char * pPinNames[] ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Deriving the functionality of the gates.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mio_LibraryParseFormulas( Mio_Library_t * pLib ) +{ + Mio_Gate_t * pGate; + + // count the gates + pLib->nGates = 0; + Mio_LibraryForEachGate( pLib, pGate ) + pLib->nGates++; + + // start a temporary BDD manager + pLib->dd = Cudd_Init( 20, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 ); + // introduce ZDD variables + Cudd_zddVarsFromBddVars( pLib->dd, 2 ); + + // for each gate, derive its function + Mio_LibraryForEachGate( pLib, pGate ) + if ( Mio_GateParseFormula( pGate ) ) + return 1; + return 0; +} + + +/**Function************************************************************* + + Synopsis [Deriving the functionality of the gates.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mio_GateParseFormula( Mio_Gate_t * pGate ) +{ + DdManager * dd = pGate->pLib->dd; + char * pPinNames[100]; + char * pPinNamesCopy[100]; + Mio_Pin_t * pPin, ** ppPin; + int nPins, iPin, i; + + // set the maximum delay of the gate; count pins + pGate->dDelayMax = 0.0; + nPins = 0; + Mio_GateForEachPin( pGate, pPin ) + { + // set the maximum delay of the gate + if ( pGate->dDelayMax < pPin->dDelayBlockMax ) + pGate->dDelayMax = pPin->dDelayBlockMax; + // count the pin + nPins++; + } + + // check for the gate with const function + if ( nPins == 0 ) + { + if ( strcmp( pGate->pForm, MIO_STRING_CONST0 ) == 0 ) + { + pGate->bFunc = b0; + pGate->pSop = Abc_SopRegister( pGate->pLib->pMmFlex, " 0\n" ); + pGate->pLib->pGate0 = pGate; + } + else if ( strcmp( pGate->pForm, MIO_STRING_CONST1 ) == 0 ) + { + pGate->bFunc = b1; + pGate->pSop = Abc_SopRegister( pGate->pLib->pMmFlex, " 1\n" ); + pGate->pLib->pGate1 = pGate; + } + else + { + printf( "Cannot parse formula \"%s\" of gate \"%s\".\n", pGate->pForm, pGate->pName ); + return 1; + } + Cudd_Ref( pGate->bFunc ); + return 0; + } + + // collect the names as they appear in the formula + nPins = Mio_GateCollectNames( pGate->pForm, pPinNames ); + if ( nPins == 0 ) + { + printf( "Cannot read formula \"%s\" of gate \"%s\".\n", pGate->pForm, pGate->pName ); + return 1; + } + + // set the number of inputs + pGate->nInputs = nPins; + + // consider the case when all the pins have identical pin info + if ( strcmp( pGate->pPins->pName, "*" ) == 0 ) + { + // get the topmost (generic) pin + pPin = pGate->pPins; + FREE( pPin->pName ); + + // create individual pins from the generic pin + ppPin = &pPin->pNext; + for ( i = 1; i < nPins; i++ ) + { + // get the new pin + *ppPin = Mio_PinDup( pPin ); + // set its name + (*ppPin)->pName = pPinNames[i]; + // prepare the next place in the list + ppPin = &((*ppPin)->pNext); + } + *ppPin = NULL; + + // set the name of the topmost pin + pPin->pName = pPinNames[0]; + } + else + { + // reorder the variable names to appear the save way as the pins + iPin = 0; + Mio_GateForEachPin( pGate, pPin ) + { + // find the pin with the name pPin->pName + for ( i = 0; i < nPins; i++ ) + { + if ( pPinNames[i] && strcmp( pPinNames[i], pPin->pName ) == 0 ) + { + // free pPinNames[i] because it is already available as pPin->pName + // setting pPinNames[i] to NULL is useful to make sure that + // this name is not assigned to two pins in the list + FREE( pPinNames[i] ); + pPinNamesCopy[iPin++] = pPin->pName; + break; + } + if ( i == nPins ) + { + printf( "Cannot find pin name \"%s\" in the formula \"%s\" of gate \"%s\".\n", + pPin->pName, pGate->pForm, pGate->pName ); + return 1; + } + } + } + + // check for the remaining names + for ( i = 0; i < nPins; i++ ) + if ( pPinNames[i] ) + { + printf( "Name \"%s\" appears in the formula \"%s\" of gate \"%s\" but there is no such pin.\n", + pPinNames[i], pGate->pForm, pGate->pName ); + return 1; + } + + // copy the names back + memcpy( pPinNames, pPinNamesCopy, nPins * sizeof(char *) ); + } + + // expand the manager if necessary + if ( dd->size < nPins ) + { + Cudd_Quit( dd ); + dd = Cudd_Init( nPins + 10, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 ); + Cudd_zddVarsFromBddVars( dd, 2 ); + } + + // derive the formula as the BDD + pGate->bFunc = Parse_FormulaParser( stdout, pGate->pForm, nPins, 0, pPinNames, dd, dd->vars ); + Cudd_Ref( pGate->bFunc ); + + // derive the cover (SOP) + pGate->pSop = Abc_ConvertBddToSop( pGate->pLib->pMmFlex, dd, pGate->bFunc, pGate->bFunc, nPins, 0, pGate->pLib->vCube, -1 ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Collect the pin names in the formula.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mio_GateCollectNames( char * pFormula, char * pPinNames[] ) +{ + char Buffer[1000]; + char * pTemp; + int nPins, i; + + // save the formula as it was + strcpy( Buffer, pFormula ); + + // remove the non-name symbols + for ( pTemp = Buffer; *pTemp; pTemp++ ) + if ( *pTemp == MIO_SYMB_AND || *pTemp == MIO_SYMB_OR || *pTemp == MIO_SYMB_NOT + || *pTemp == MIO_SYMB_OPEN || *pTemp == MIO_SYMB_CLOSE || *pTemp == MIO_SYMB_AFTNOT ) + *pTemp = ' '; + + // save the names + nPins = 0; + pTemp = strtok( Buffer, " " ); + while ( pTemp ) + { + for ( i = 0; i < nPins; i++ ) + if ( strcmp( pTemp, pPinNames[i] ) == 0 ) + break; + if ( i == nPins ) + { // cannot find this name; save it + pPinNames[nPins++] = Extra_UtilStrsav(pTemp); + } + // get the next name + pTemp = strtok( NULL, " " ); + } + return nPins; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/mio/mioGENERIC.c b/abc_with_bb_support/src/map/mio/mioGENERIC.c new file mode 100644 index 000000000..415c81b4c --- /dev/null +++ b/abc_with_bb_support/src/map/mio/mioGENERIC.c @@ -0,0 +1,46 @@ +/**CFile**************************************************************** + + FileName [mio___.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [File reading/writing for technology mapping.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 8, 2003.] + + Revision [$Id: mio___.h,v 1.0 2003/09/08 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "mioInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/mio/mioInt.h b/abc_with_bb_support/src/map/mio/mioInt.h new file mode 100644 index 000000000..a518e2287 --- /dev/null +++ b/abc_with_bb_support/src/map/mio/mioInt.h @@ -0,0 +1,125 @@ +/**CFile**************************************************************** + + FileName [mioInt.h] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [File reading/writing for technology mapping.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 8, 2003.] + + Revision [$Id: mioInt.h,v 1.4 2004/06/28 14:20:25 alanmi Exp $] + +***********************************************************************/ + +#ifndef __MIO_INT_H__ +#define __MIO_INT_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include "abc.h" +#include "mvc.h" +#include "main.h" +#include "mio.h" +#include "extra.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +#define MIO_STRING_GATE "GATE" +#define MIO_STRING_PIN "PIN" +#define MIO_STRING_NONINV "NONINV" +#define MIO_STRING_INV "INV" +#define MIO_STRING_UNKNOWN "UNKNOWN" + +#define MIO_STRING_CONST0 "CONST0" +#define MIO_STRING_CONST1 "CONST1" + +// the bit masks +#define MIO_MASK(n) ((~((unsigned)0)) >> (32-(n))) +#define MIO_FULL (~((unsigned)0)) + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +struct Mio_LibraryStruct_t_ +{ + char * pName; // the name of the library + int nGates; // the number of the gates + Mio_Gate_t * pGates; // the linked list of all gates in no particular order + Mio_Gate_t * pGate0; // the constant zero gate + Mio_Gate_t * pGate1; // the constant one gate + Mio_Gate_t * pGateBuf; // the buffer + Mio_Gate_t * pGateInv; // the inverter + Mio_Gate_t * pGateNand2; // the NAND2 gate + Mio_Gate_t * pGateAnd2; // the AND2 gate + st_table * tName2Gate; // the mapping of gate names into their pointer + DdManager * dd; // the nanager storing functions of gates + Extra_MmFlex_t * pMmFlex; // the memory manaqer for SOPs + Vec_Str_t * vCube; // temporary cube +}; + +struct Mio_GateStruct_t_ +{ + // information derived from the genlib file + char * pName; // the name of the gate + double dArea; // the area of the gate + char * pForm; // the formula describing functionality of the gate + Mio_Pin_t * pPins; // the linked list of all pins (one pin if info is the same) + char * pOutName; // the name of the output pin + // the library to which this gate belongs + Mio_Library_t * pLib; + // the next gate in the list + Mio_Gate_t * pNext; + + // the derived information + int nInputs; // the number of inputs + double dDelayMax; // the maximum delay + DdNode * bFunc; // the functionality + char * pSop; +}; + +struct Mio_PinStruct_t_ +{ + char * pName; + Mio_PinPhase_t Phase; + double dLoadInput; + double dLoadMax; + double dDelayBlockRise; + double dDelayFanoutRise; + double dDelayBlockFall; + double dDelayFanoutFall; + double dDelayBlockMax; + Mio_Pin_t * pNext; +}; + + +//////////////////////////////////////////////////////////////////////// +/// GLOBAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== mio.c =============================================================*/ +/*=== mioRead.c =============================================================*/ +/*=== mioUtils.c =============================================================*/ + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/map/mio/mioRead.c b/abc_with_bb_support/src/map/mio/mioRead.c new file mode 100644 index 000000000..0c001a5b8 --- /dev/null +++ b/abc_with_bb_support/src/map/mio/mioRead.c @@ -0,0 +1,582 @@ +/**CFile**************************************************************** + + FileName [mioRead.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [File reading/writing for technology mapping.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 8, 2003.] + + Revision [$Id: mioRead.c,v 1.9 2004/10/19 06:40:16 satrajit Exp $] + +***********************************************************************/ + +#include "mioInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +static Mio_Library_t * Mio_LibraryReadOne( Abc_Frame_t * pAbc, char * FileName, bool fExtendedFormat, st_table * tExcludeGate, int fVerbose ); +static int Mio_LibraryReadInternal( Mio_Library_t * pLib, char * pBuffer, bool fExtendedFormat, st_table * tExcludeGate, int fVerbose ); +static Mio_Gate_t * Mio_LibraryReadGate( char ** ppToken, bool fExtendedFormat ); +static Mio_Pin_t * Mio_LibraryReadPin( char ** ppToken, bool fExtendedFormat ); +static char * chomp( char *s ); +static void Mio_LibraryDetectSpecialGates( Mio_Library_t * pLib ); +static void Io_ReadFileRemoveComments( char * pBuffer, int * pnDots, int * pnLines ); + +#ifdef WIN32 +extern int isspace( int c ); // to silence the warning in VS +#endif + +/**Function************************************************************* + + Synopsis [Read the genlib type of library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mio_Library_t * Mio_LibraryRead( void * pAbc, char * FileName, char * ExcludeFile, int fVerbose ) +{ + Mio_Library_t * pLib; + int num; + + st_table * tExcludeGate = 0; + + if ( ExcludeFile ) + { + tExcludeGate = st_init_table(strcmp, st_strhash); + if ( (num = Mio_LibraryReadExclude( pAbc, ExcludeFile, tExcludeGate )) == -1 ) + { + st_free_table( tExcludeGate ); + tExcludeGate = 0; + return 0; + } + + fprintf ( Abc_FrameReadOut( pAbc ), "Read %d gates from exclude file\n", num ); + } + + pLib = Mio_LibraryReadOne( pAbc, FileName, 0, tExcludeGate, fVerbose ); // try normal format first .. + if ( pLib == NULL ) + { + pLib = Mio_LibraryReadOne( pAbc, FileName, 1, tExcludeGate, fVerbose ); // .. otherwise try extended format + if ( pLib != NULL ) + printf ( "Warning: Read extended GENLIB format but ignoring extensions\n" ); + } + + return pLib; +} + +/**Function************************************************************* + + Synopsis [Read the genlib type of library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mio_Library_t * Mio_LibraryReadOne( Abc_Frame_t * pAbc, char * FileName, bool fExtendedFormat, st_table * tExcludeGate, int fVerbose ) +{ + Mio_Library_t * pLib; + char * pBuffer = 0; + + // allocate the genlib structure + pLib = ALLOC( Mio_Library_t, 1 ); + memset( pLib, 0, sizeof(Mio_Library_t) ); + pLib->pName = Extra_UtilStrsav( FileName ); + pLib->tName2Gate = st_init_table(strcmp, st_strhash); + pLib->pMmFlex = Extra_MmFlexStart(); + pLib->vCube = Vec_StrAlloc( 100 ); + + // read the file and clean comments + // pBuffer = Io_ReadFileFileContents( FileName, NULL ); + // we don't use above function but actually do the same thing explicitly + // to handle open_path expansion correctly + + { + FILE * pFile; + int nFileSize; + + // open the BLIF file for binary reading + pFile = Io_FileOpen( FileName, "open_path", "rb", 1 ); +// pFile = fopen( FileName, "rb" ); + // if we got this far, file should be okay otherwise would + // have been detected by caller + assert ( pFile != NULL ); + // get the file size, in bytes + fseek( pFile, 0, SEEK_END ); + nFileSize = ftell( pFile ); + // move the file current reading position to the beginning + rewind( pFile ); + // load the contents of the file into memory + pBuffer = ALLOC( char, nFileSize + 10 ); + fread( pBuffer, nFileSize, 1, pFile ); + // terminate the string with '\0' + pBuffer[ nFileSize ] = '\0'; + strcat( pBuffer, "\n.end\n" ); + // close file + fclose( pFile ); + } + + Io_ReadFileRemoveComments( pBuffer, NULL, NULL ); + + // parse the contents of the file + if ( Mio_LibraryReadInternal( pLib, pBuffer, fExtendedFormat, tExcludeGate, fVerbose ) ) + { + Mio_LibraryDelete( pLib ); + free( pBuffer ); + return NULL; + } + free( pBuffer ); + + // derive the functinality of gates + if ( Mio_LibraryParseFormulas( pLib ) ) + { + printf( "Mio_LibraryRead: Had problems parsing formulas.\n" ); + Mio_LibraryDelete( pLib ); + return NULL; + } + + // detect INV and NAND2 + Mio_LibraryDetectSpecialGates( pLib ); +//Mio_WriteLibrary( stdout, pLib ); + return pLib; +} + +/**Function************************************************************* + + Synopsis [Read the genlib type of library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mio_LibraryReadInternal( Mio_Library_t * pLib, char * pBuffer, bool fExtendedFormat, st_table * tExcludeGate, int fVerbose ) +{ + Mio_Gate_t * pGate, ** ppGate; + char * pToken; + int nGates = 0; + int nDel = 0; + + // start the linked list of gates + pLib->pGates = NULL; + ppGate = &pLib->pGates; + + // read gates one by one + pToken = strtok( pBuffer, " \t\r\n" ); + while ( pToken && strcmp( pToken, MIO_STRING_GATE ) == 0 ) + { + // derive the next gate + pGate = Mio_LibraryReadGate( &pToken, fExtendedFormat ); + if ( pGate == NULL ) + return 1; + + // set the library + pGate->pLib = pLib; + + // printf ("Processing: '%s'\n", pGate->pName); + + if ( tExcludeGate && st_is_member( tExcludeGate, pGate->pName ) ) + { + //printf ("Excluding: '%s'\n", pGate->pName); + Mio_GateDelete( pGate ); + nDel++; + } + else + { + // add this gate to the list + *ppGate = pGate; + ppGate = &pGate->pNext; + nGates++; + + // remember this gate by name + if ( !st_is_member( pLib->tName2Gate, pGate->pName ) ) + st_insert( pLib->tName2Gate, pGate->pName, (char *)pGate ); + else + printf( "The gate with name \"%s\" appears more than once.\n", pGate->pName ); + } + } + if ( fVerbose ) + printf( "The number of gates read = %d.\n", nGates ); + + // check what is the last word read + if ( pToken && strcmp( pToken, ".end" ) != 0 ) + return 1; + + if ( nDel != 0 ) + printf( "Actually excluded %d cells\n", nDel ); + + return 0; +} + +/**Function************************************************************* + + Synopsis [Read the genlib type of gate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mio_Gate_t * Mio_LibraryReadGate( char ** ppToken, bool fExtendedFormat ) +{ + Mio_Gate_t * pGate; + Mio_Pin_t * pPin, ** ppPin; + char * pToken = *ppToken; + + // allocate the gate structure + pGate = ALLOC( Mio_Gate_t, 1 ); + memset( pGate, 0, sizeof(Mio_Gate_t) ); + + // read the name + pToken = strtok( NULL, " \t\r\n" ); + pGate->pName = Extra_UtilStrsav( pToken ); + + // read the area + pToken = strtok( NULL, " \t\r\n" ); + pGate->dArea = atof( pToken ); + + // read the formula + + // first the output name + pToken = strtok( NULL, "=" ); + pGate->pOutName = chomp( pToken ); + + // then rest of the expression + pToken = strtok( NULL, ";" ); + pGate->pForm = Extra_UtilStrsav( pToken ); + + // read the pin info + // start the linked list of pins + pGate->pPins = NULL; + ppPin = &pGate->pPins; + + // read gates one by one + pToken = strtok( NULL, " \t\r\n" ); + while ( pToken && strcmp( pToken, MIO_STRING_PIN ) == 0 ) + { + // derive the next gate + pPin = Mio_LibraryReadPin( &pToken, fExtendedFormat ); + if ( pPin == NULL ) + { + Mio_GateDelete( pGate ); + *ppToken = pToken; + return NULL; + } + // add this pin to the list + *ppPin = pPin; + ppPin = &pPin->pNext; + // get the next token + pToken = strtok( NULL, " \t\r\n" ); + } + + *ppToken = pToken; + return pGate; +} + + + +/**Function************************************************************* + + Synopsis [Read the genlib type of pin.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mio_Pin_t * Mio_LibraryReadPin( char ** ppToken, bool fExtendedFormat ) +{ + Mio_Pin_t * pPin; + char * pToken = *ppToken; + + // allocate the gate structure + pPin = ALLOC( Mio_Pin_t, 1 ); + memset( pPin, 0, sizeof(Mio_Pin_t) ); + + // read the name + pToken = strtok( NULL, " \t\r\n" ); + pPin->pName = Extra_UtilStrsav( pToken ); + + // read the pin phase + pToken = strtok( NULL, " \t\r\n" ); + if ( strcmp( pToken, MIO_STRING_UNKNOWN ) == 0 ) + pPin->Phase = MIO_PHASE_UNKNOWN; + else if ( strcmp( pToken, MIO_STRING_INV ) == 0 ) + pPin->Phase = MIO_PHASE_INV; + else if ( strcmp( pToken, MIO_STRING_NONINV ) == 0 ) + pPin->Phase = MIO_PHASE_NONINV; + else + { + printf( "Cannot read pin phase specification\n" ); + Mio_PinDelete( pPin ); + *ppToken = pToken; + return NULL; + } + + pToken = strtok( NULL, " \t\r\n" ); + pPin->dLoadInput = atof( pToken ); + + pToken = strtok( NULL, " \t\r\n" ); + pPin->dLoadMax = atof( pToken ); + + pToken = strtok( NULL, " \t\r\n" ); + pPin->dDelayBlockRise = atof( pToken ); + + pToken = strtok( NULL, " \t\r\n" ); + pPin->dDelayFanoutRise = atof( pToken ); + + pToken = strtok( NULL, " \t\r\n" ); + pPin->dDelayBlockFall = atof( pToken ); + + pToken = strtok( NULL, " \t\r\n" ); + pPin->dDelayFanoutFall = atof( pToken ); + + if ( fExtendedFormat ) + { + /* In extended format, the field after dDelayFanoutRise + * is to be ignored + **/ + + pPin->dDelayBlockFall = pPin->dDelayFanoutFall; + + pToken = strtok( NULL, " \t" ); + pPin->dDelayFanoutFall = atof( pToken ); + + /* last field is ignored */ + pToken = strtok( NULL, " \t\r\n" ); + } + + if ( pPin->dDelayBlockRise > pPin->dDelayBlockFall ) + pPin->dDelayBlockMax = pPin->dDelayBlockRise; + else + pPin->dDelayBlockMax = pPin->dDelayBlockFall; + + *ppToken = pToken; + return pPin; +} + + +/**Function************************************************************* + + Synopsis [Duplicates string and returns it with leading and + trailing spaces removed.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char *chomp( char *s ) +{ + char *b = ALLOC(char, strlen(s)+1), *c = b; + while (*s && isspace(*s)) + ++s; + while (*s && !isspace(*s)) + *c++ = *s++; + *c = 0; + return b; +} + +/**Function************************************************************* + + Synopsis [Duplicates string and returns it with leading and + trailing spaces removed.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mio_LibraryDetectSpecialGates( Mio_Library_t * pLib ) +{ + Mio_Gate_t * pGate; + DdNode * bFuncBuf, * bFuncInv, * bFuncNand2, * bFuncAnd2; + + bFuncBuf = pLib->dd->vars[0]; Cudd_Ref( bFuncBuf ); + bFuncInv = Cudd_Not( pLib->dd->vars[0] ); Cudd_Ref( bFuncInv ); + bFuncNand2 = Cudd_bddNand( pLib->dd, pLib->dd->vars[0], pLib->dd->vars[1] ); Cudd_Ref( bFuncNand2 ); + bFuncAnd2 = Cudd_bddAnd( pLib->dd, pLib->dd->vars[0], pLib->dd->vars[1] ); Cudd_Ref( bFuncAnd2 ); + + // get buffer + Mio_LibraryForEachGate( pLib, pGate ) + if ( pLib->pGateBuf == NULL && pGate->bFunc == bFuncBuf ) + { + pLib->pGateBuf = pGate; + break; + } + if ( pLib->pGateBuf == NULL ) + { + printf( "Warnings: GENLIB library reader cannot detect the buffer gate.\n" ); + printf( "Some parts of the supergate-based technology mapper may not work correctly.\n" ); + } + + // get inverter + Mio_LibraryForEachGate( pLib, pGate ) + if ( pLib->pGateInv == NULL && pGate->bFunc == bFuncInv ) + { + pLib->pGateInv = pGate; + break; + } + if ( pLib->pGateInv == NULL ) + { + printf( "Warnings: GENLIB library reader cannot detect the invertor gate.\n" ); + printf( "Some parts of the supergate-based technology mapper may not work correctly.\n" ); + } + + // get the NAND2 and AND2 gates + Mio_LibraryForEachGate( pLib, pGate ) + if ( pLib->pGateNand2 == NULL && pGate->bFunc == bFuncNand2 ) + { + pLib->pGateNand2 = pGate; + break; + } + Mio_LibraryForEachGate( pLib, pGate ) + if ( pLib->pGateAnd2 == NULL && pGate->bFunc == bFuncAnd2 ) + { + pLib->pGateAnd2 = pGate; + break; + } + if ( pLib->pGateAnd2 == NULL && pLib->pGateNand2 == NULL ) + { + printf( "Warnings: GENLIB library reader cannot detect the AND2 or NAND2 gate.\n" ); + printf( "Some parts of the supergate-based technology mapper may not work correctly.\n" ); + } + + Cudd_RecursiveDeref( pLib->dd, bFuncInv ); + Cudd_RecursiveDeref( pLib->dd, bFuncNand2 ); + Cudd_RecursiveDeref( pLib->dd, bFuncAnd2 ); +} + +/**Function************************************************************* + + Synopsis [populate hash table of gates to be exlcuded from genlib] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mio_LibraryReadExclude( void * pAbc, char * ExcludeFile, st_table * tExcludeGate ) +{ + int nDel = 0; + FILE *pEx; + char buffer[128]; + + assert ( tExcludeGate ); + + if ( ExcludeFile ) + { + pEx = fopen( ExcludeFile, "r" ); + + if ( pEx == NULL ) + { + fprintf ( Abc_FrameReadErr( pAbc ), "Error: Could not open exclude file %s. Stop.\n", ExcludeFile ); + return -1; + } + + while (1 == fscanf( pEx, "%127s", buffer )) + { + //printf ("Read: '%s'\n", buffer ); + st_insert( tExcludeGate, Extra_UtilStrsav( buffer ), (char *)0 ); + nDel++; + } + + fclose( pEx ); + } + + return nDel; +} + +/**Function************************************************************* + + Synopsis [Eliminates comments from the input file.] + + Description [As a byproduct, this procedure also counts the number + lines and dot-statements in the input file. This also joins non-comment + lines that are joined with a backspace '\'] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Io_ReadFileRemoveComments( char * pBuffer, int * pnDots, int * pnLines ) +{ + char * pCur; + int nDots, nLines; + // scan through the buffer and eliminate comments + // (in the BLIF file, comments are lines starting with "#") + nDots = nLines = 0; + for ( pCur = pBuffer; *pCur; pCur++ ) + { + // if this is the beginning of comment + // clean it with spaces until the new line statement + if ( *pCur == '#' ) + while ( *pCur != '\n' ) + *pCur++ = ' '; + + // count the number of new lines and dots + if ( *pCur == '\n' ) { + if (*(pCur-1)=='\r') { + // DOS(R) file support + if (*(pCur-2)!='\\') nLines++; + else { + // rewind to backslash and overwrite with a space + *(pCur-2) = ' '; + *(pCur-1) = ' '; + *pCur = ' '; + } + } else { + // UNIX(TM) file support + if (*(pCur-1)!='\\') nLines++; + else { + // rewind to backslash and overwrite with a space + *(pCur-1) = ' '; + *pCur = ' '; + } + } + } + else if ( *pCur == '.' ) + nDots++; + } + if ( pnDots ) + *pnDots = nDots; + if ( pnLines ) + *pnLines = nLines; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/mio/mioUtils.c b/abc_with_bb_support/src/map/mio/mioUtils.c new file mode 100644 index 000000000..e53a90182 --- /dev/null +++ b/abc_with_bb_support/src/map/mio/mioUtils.c @@ -0,0 +1,531 @@ +/**CFile**************************************************************** + + FileName [mioUtils.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [File reading/writing for technology mapping.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 8, 2003.] + + Revision [$Id: mioUtils.c,v 1.6 2004/09/03 18:02:20 satrajit Exp $] + +***********************************************************************/ + +#include "mioInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Mio_WriteGate( FILE * pFile, Mio_Gate_t * pGate, int fPrintSops ); +static void Mio_WritePin( FILE * pFile, Mio_Pin_t * pPin ); +static int Mio_DelayCompare( Mio_Gate_t ** ppG1, Mio_Gate_t ** ppG2 ); +static void Mio_DeriveTruthTable_rec( DdNode * bFunc, unsigned uTruthsIn[][2], unsigned uTruthRes[] ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mio_LibraryDelete( Mio_Library_t * pLib ) +{ + Mio_Gate_t * pGate, * pGate2; + if ( pLib == NULL ) + return; + // free the bindings of nodes to gates from this library for all networks + Abc_FrameUnmapAllNetworks( Abc_FrameGetGlobalFrame() ); + // free the library + FREE( pLib->pName ); + Mio_LibraryForEachGateSafe( pLib, pGate, pGate2 ) + Mio_GateDelete( pGate ); + Extra_MmFlexStop( pLib->pMmFlex ); + Vec_StrFree( pLib->vCube ); + if ( pLib->tName2Gate ) + st_free_table( pLib->tName2Gate ); + if ( pLib->dd ) + Cudd_Quit( pLib->dd ); + free( pLib ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mio_GateDelete( Mio_Gate_t * pGate ) +{ + Mio_Pin_t * pPin, * pPin2; + FREE( pGate->pOutName ); + FREE( pGate->pName ); + FREE( pGate->pForm ); + if ( pGate->bFunc ) + Cudd_RecursiveDeref( pGate->pLib->dd, pGate->bFunc ); + Mio_GateForEachPinSafe( pGate, pPin, pPin2 ) + Mio_PinDelete( pPin ); + free( pGate ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mio_PinDelete( Mio_Pin_t * pPin ) +{ + FREE( pPin->pName ); + free( pPin ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mio_Pin_t * Mio_PinDup( Mio_Pin_t * pPin ) +{ + Mio_Pin_t * pPinNew; + + pPinNew = ALLOC( Mio_Pin_t, 1 ); + *pPinNew = *pPin; + pPinNew->pName = (pPinNew->pName ? Extra_UtilStrsav(pPinNew->pName) : NULL); + pPinNew->pNext = NULL; + + return pPinNew; +} + + + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mio_WriteLibrary( FILE * pFile, Mio_Library_t * pLib, int fPrintSops ) +{ + Mio_Gate_t * pGate; + + fprintf( pFile, "# The genlib library \"%s\".\n", pLib->pName ); + Mio_LibraryForEachGate( pLib, pGate ) + Mio_WriteGate( pFile, pGate, fPrintSops ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mio_WriteGate( FILE * pFile, Mio_Gate_t * pGate, int fPrintSops ) +{ + Mio_Pin_t * pPin; + + fprintf( pFile, "GATE " ); + fprintf( pFile, "%12s ", pGate->pName ); + fprintf( pFile, "%10.2f ", pGate->dArea ); + fprintf( pFile, "%s=%s;\n", pGate->pOutName, pGate->pForm ); + // print the pins + if ( fPrintSops ) + fprintf( pFile, "%s", pGate->pSop? pGate->pSop : "unspecified\n" ); +// Extra_bddPrint( pGate->pLib->dd, pGate->bFunc ); +// fprintf( pFile, "\n" ); + Mio_GateForEachPin( pGate, pPin ) + Mio_WritePin( pFile, pPin ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mio_WritePin( FILE * pFile, Mio_Pin_t * pPin ) +{ + char * pPhaseNames[10] = { "UNKNOWN", "INV", "NONINV" }; + fprintf( pFile, " PIN " ); + fprintf( pFile, "%9s ", pPin->pName ); + fprintf( pFile, "%10s ", pPhaseNames[pPin->Phase] ); + fprintf( pFile, "%6d ", (int)pPin->dLoadInput ); + fprintf( pFile, "%6d ", (int)pPin->dLoadMax ); + fprintf( pFile, "%6.2f ", pPin->dDelayBlockRise ); + fprintf( pFile, "%6.2f ", pPin->dDelayFanoutRise ); + fprintf( pFile, "%6.2f ", pPin->dDelayBlockFall ); + fprintf( pFile, "%6.2f", pPin->dDelayFanoutFall ); + fprintf( pFile, "\n" ); +} + +/**Function************************************************************* + + Synopsis [Collects the set of root gates.] + + Description [Only collects the gates with unique functionality, + which have fewer inputs and shorter delay than the given limits.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mio_Gate_t ** Mio_CollectRoots( Mio_Library_t * pLib, int nInputs, float tDelay, bool fSkipInv, int * pnGates ) +{ + Mio_Gate_t * pGate; + Mio_Gate_t ** ppGates; + /* st_table * tFuncs; */ + /* st_generator * gen; */ + DdNode * bFunc; + DdManager * dd; + int nGates, iGate; + + dd = Mio_LibraryReadDd( pLib ); + nGates = Mio_LibraryReadGateNum( pLib ); + + /* + + // for each functionality select one gate; skip constants and buffers + tFuncs = st_init_table( st_ptrcmp, st_ptrhash ); + Mio_LibraryForEachGate( pLib, pGate ) + { + bFunc = Mio_GateReadFunc(pGate); + if ( pGate->nInputs > nInputs ) + continue; + if ( pGate->dDelayMax > (double)tDelay ) + continue; + if ( bFunc == b0 || bFunc == b1 ) + continue; + if ( bFunc == dd->vars[0] ) + continue; + if ( bFunc == Cudd_Not(dd->vars[0]) && fSkipInv ) + continue; + if ( st_is_member( tFuncs, (char *)bFunc ) ) + continue; + st_insert( tFuncs, (char *)bFunc, (char *)pGate ); + } + + // collect the gates into the array + ppGates = ALLOC( Mio_Gate_t *, nGates ); + iGate = 0; + st_foreach_item( tFuncs, gen, (char **)&bFunc, (char **)&pGate ) + ppGates[ iGate++ ] = pGate; + assert( iGate <= nGates ); + st_free_table( tFuncs ); + + */ + + ppGates = ALLOC( Mio_Gate_t *, nGates ); + iGate = 0; + Mio_LibraryForEachGate( pLib, pGate ) + { + bFunc = Mio_GateReadFunc(pGate); + if ( pGate->nInputs > nInputs ) + continue; + if ( pGate->dDelayMax > (double)tDelay ) + continue; + if ( bFunc == b0 || bFunc == b1 ) + continue; + if ( bFunc == dd->vars[0] ) + continue; + if ( bFunc == Cudd_Not(dd->vars[0]) && fSkipInv ) + continue; + + assert( iGate < nGates ); + ppGates[ iGate++ ] = pGate; + } + + if ( iGate > 0 ) + { + // sort the gates by delay + qsort( (void *)ppGates, iGate, sizeof(Mio_Gate_t *), + (int (*)(const void *, const void *)) Mio_DelayCompare ); + assert( Mio_DelayCompare( ppGates, ppGates + iGate - 1 ) <= 0 ); + } + + if ( pnGates ) + *pnGates = iGate; + return ppGates; +} + +/**Function************************************************************* + + Synopsis [Compares the max delay of two gates.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mio_DelayCompare( Mio_Gate_t ** ppG1, Mio_Gate_t ** ppG2 ) +{ + if ( (*ppG1)->dDelayMax < (*ppG2)->dDelayMax ) + return -1; + if ( (*ppG1)->dDelayMax > (*ppG2)->dDelayMax ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Derives the truth table of the gate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mio_DeriveTruthTable( Mio_Gate_t * pGate, unsigned uTruthsIn[][2], int nSigns, int nInputs, unsigned uTruthRes[] ) +{ + Mio_DeriveTruthTable_rec( pGate->bFunc, uTruthsIn, uTruthRes ); +} + +/**Function************************************************************* + + Synopsis [Recursively derives the truth table of the gate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mio_DeriveTruthTable_rec( DdNode * bFunc, unsigned uTruthsIn[][2], unsigned uTruthRes[] ) +{ + unsigned uTruthsCof0[2]; + unsigned uTruthsCof1[2]; + + // complement the resulting truth table, if the function is complemented + if ( Cudd_IsComplement(bFunc) ) + { + Mio_DeriveTruthTable_rec( Cudd_Not(bFunc), uTruthsIn, uTruthRes ); + uTruthRes[0] = ~uTruthRes[0]; + uTruthRes[1] = ~uTruthRes[1]; + return; + } + + // if the function is constant 1, return the constant 1 truth table + if ( bFunc->index == CUDD_CONST_INDEX ) + { + uTruthRes[0] = MIO_FULL; + uTruthRes[1] = MIO_FULL; + return; + } + + // solve the problem for both cofactors + Mio_DeriveTruthTable_rec( cuddE(bFunc), uTruthsIn, uTruthsCof0 ); + Mio_DeriveTruthTable_rec( cuddT(bFunc), uTruthsIn, uTruthsCof1 ); + + // derive the resulting truth table using the input truth tables + uTruthRes[0] = (uTruthsCof0[0] & ~uTruthsIn[bFunc->index][0]) | + (uTruthsCof1[0] & uTruthsIn[bFunc->index][0]); + uTruthRes[1] = (uTruthsCof0[1] & ~uTruthsIn[bFunc->index][1]) | + (uTruthsCof1[1] & uTruthsIn[bFunc->index][1]); +} + +/**Function************************************************************* + + Synopsis [Derives the truth table of the root of the gate.] + + Description [Given the truth tables of the leaves of the gate, + this procedure derives the truth table of the root.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mio_DeriveTruthTable2( Mio_Gate_t * pGate, unsigned uTruthsIn[][2], int nTruths, int nInputs, unsigned uTruthRes[] ) +{ + unsigned uSignCube[2]; + int i, nFanins; + char * pCube; + + // make sure that the number of input truth tables in equal to the number of gate inputs + assert( pGate->nInputs == nTruths ); + assert( nInputs < 7 ); + + nFanins = Abc_SopGetVarNum( pGate->pSop ); + assert( nFanins == nInputs ); + + // clean the resulting truth table + uTruthRes[0] = 0; + uTruthRes[1] = 0; + if ( nInputs < 6 ) + { +// for ( c = 0; *(pCube = pGate->pSop + c * (nFanins + 3)); c++ ) + Abc_SopForEachCube( pGate->pSop, nFanins, pCube ) + { + // add the clause + uSignCube[0] = MIO_FULL; + for ( i = 0; i < nFanins; i++ ) + { + if ( pCube[i] == '0' ) + uSignCube[0] &= ~uTruthsIn[i][0]; + else if ( pCube[i] == '1' ) + uSignCube[0] &= uTruthsIn[i][0]; + } + } + if ( nInputs < 5 ) + uTruthRes[0] &= MIO_MASK(1<pSop + c * (nFanins + 3)); c++ ) + Abc_SopForEachCube( pGate->pSop, nFanins, pCube ) + { + uSignCube[0] = MIO_FULL; + uSignCube[1] = MIO_FULL; + for ( i = 0; i < nFanins; i++ ) + { + if ( pCube[i] == '0' ) + { + uSignCube[0] &= ~uTruthsIn[i][0]; + uSignCube[1] &= ~uTruthsIn[i][1]; + } + else if ( pCube[i] == '1' ) + { + uSignCube[0] &= uTruthsIn[i][0]; + uSignCube[1] &= uTruthsIn[i][1]; + } + } + uTruthRes[0] |= uSignCube[0]; + uTruthRes[1] |= uSignCube[1]; + } + } +} + +/**Function************************************************************* + + Synopsis [Derives the area and delay of the root of the gate.] + + Description [Array of the resulting delays should be initialized + to the (negative) SUPER_NO_VAR value.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mio_DeriveGateDelays( Mio_Gate_t * pGate, + float ** ptPinDelays, int nPins, int nInputs, float tDelayZero, + float * ptDelaysRes, float * ptPinDelayMax ) +{ + Mio_Pin_t * pPin; + float Delay, DelayMax; + int i, k; + assert( pGate->nInputs == nPins ); + // set all the delays to the unused delay + for ( i = 0; i < nInputs; i++ ) + ptDelaysRes[i] = tDelayZero; + // compute the delays for each input and the max delay at the same time + DelayMax = 0; + for ( i = 0; i < nInputs; i++ ) + { + for ( k = 0, pPin = pGate->pPins; pPin; pPin = pPin->pNext, k++ ) + { + if ( ptPinDelays[k][i] < 0 ) + continue; + Delay = ptPinDelays[k][i] + (float)pPin->dDelayBlockMax; + if ( ptDelaysRes[i] < Delay ) + ptDelaysRes[i] = Delay; + } + if ( k != nPins ) + { + printf ("DEBUG: problem gate is %s\n", Mio_GateReadName( pGate )); + } + assert( k == nPins ); + if ( DelayMax < ptDelaysRes[i] ) + DelayMax = ptDelaysRes[i]; + } + *ptPinDelayMax = DelayMax; +} + + +/**Function************************************************************* + + Synopsis [Creates a pseudo-gate.] + + Description [The pseudo-gate is a N-input gate with all info set to 0.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mio_Gate_t * Mio_GateCreatePseudo( int nInputs ) +{ + Mio_Gate_t * pGate; + Mio_Pin_t * pPin; + int i; + // allocate the gate structure + pGate = ALLOC( Mio_Gate_t, 1 ); + memset( pGate, 0, sizeof(Mio_Gate_t) ); + pGate->nInputs = nInputs; + // create pins + for ( i = 0; i < nInputs; i++ ) + { + pPin = ALLOC( Mio_Pin_t, 1 ); + memset( pPin, 0, sizeof(Mio_Pin_t) ); + pPin->pNext = pGate->pPins; + pGate->pPins = pPin; + } + return pGate; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/mio/module.make b/abc_with_bb_support/src/map/mio/module.make new file mode 100644 index 000000000..a4a6f06f1 --- /dev/null +++ b/abc_with_bb_support/src/map/mio/module.make @@ -0,0 +1,5 @@ +SRC += src/map/mio/mio.c \ + src/map/mio/mioApi.c \ + src/map/mio/mioFunc.c \ + src/map/mio/mioRead.c \ + src/map/mio/mioUtils.c diff --git a/abc_with_bb_support/src/map/super/module.make b/abc_with_bb_support/src/map/super/module.make new file mode 100644 index 000000000..39a0da2a0 --- /dev/null +++ b/abc_with_bb_support/src/map/super/module.make @@ -0,0 +1,4 @@ +SRC += src/map/super/super.c \ + src/map/super/superAnd.c \ + src/map/super/superGate.c \ + src/map/super/superWrite.c diff --git a/abc_with_bb_support/src/map/super/super.c b/abc_with_bb_support/src/map/super/super.c new file mode 100644 index 000000000..ac21910fb --- /dev/null +++ b/abc_with_bb_support/src/map/super/super.c @@ -0,0 +1,319 @@ +/**CFile**************************************************************** + + FileName [super.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Pre-computation of supergates.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - August 18, 2003.] + + Revision [$Id: super.c,v 1.6 2004/10/30 20:51:11 satrajit Exp $] + +***********************************************************************/ + +#include "superInt.h" +#include "mainInt.h" +#include "mio.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Super_CommandSupergates ( Abc_Frame_t * pAbc, int argc, char **argv ); +static int Super_CommandSupergatesAnd( Abc_Frame_t * pAbc, int argc, char **argv ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Super_Init( Abc_Frame_t * pAbc ) +{ + Cmd_CommandAdd( pAbc, "SC mapping", "super", Super_CommandSupergates, 0 ); + Cmd_CommandAdd( pAbc, "SC mapping", "super2", Super_CommandSupergatesAnd, 0 ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Super_End() +{ +} + + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Super_CommandSupergatesAnd( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + FILE * pOut, * pErr; + int nVarsMax, nLevels; + int fVerbose; + int c; + + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set the defaults + nVarsMax = 4; + nLevels = 3; + fVerbose = 0; + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "ilvh")) != EOF ) + { + switch (c) + { + case 'i': + nVarsMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nVarsMax < 0 ) + goto usage; + break; + case 'l': + nLevels = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nLevels < 0 ) + goto usage; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + break; + default: + goto usage; + } + } + + Super2_Precompute( nVarsMax, nLevels, fVerbose ); + + return 0; + +usage: + fprintf( pErr, "usage: super2 [-i num] [-l num] [-vh]\n"); + fprintf( pErr, "\t precomputes the supergates composed of AND2s and INVs\n" ); + fprintf( pErr, "\t-i num : the max number of inputs to the supergate [default = %d]\n", nVarsMax ); + fprintf( pErr, "\t-l num : the max number of logic levels of gates [default = %d]\n", nLevels ); + fprintf( pErr, "\t-v : enable verbose output\n"); + fprintf( pErr, "\t-h : print the help message\n"); + return 1; /* error exit */ +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Super_CommandSupergates( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + FILE * pFile; + FILE * pOut, * pErr; + Mio_Library_t * pLib; + char * FileName, * ExcludeFile; + float DelayLimit; + float AreaLimit; + bool fSkipInvs; + bool fWriteOldFormat; + int nVarsMax, nLevels, TimeLimit; + int fVerbose; + int c; + + pOut = Abc_FrameReadOut(pAbc); + pErr = Abc_FrameReadErr(pAbc); + + // set the defaults + nVarsMax = 5; + nLevels = 3; + DelayLimit = 3.5; + AreaLimit = 9; + TimeLimit = 10; + fSkipInvs = 1; + fVerbose = 0; + fWriteOldFormat = 0; + ExcludeFile = 0; + + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "eiltdasovh")) != EOF ) + { + switch (c) + { + case 'e': + ExcludeFile = argv[globalUtilOptind]; + if ( ExcludeFile == 0 ) + goto usage; + globalUtilOptind++; + break; + case 'i': + nVarsMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nVarsMax < 0 ) + goto usage; + break; + case 'l': + nLevels = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nLevels < 0 ) + goto usage; + break; + case 't': + TimeLimit = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( TimeLimit < 0 ) + goto usage; + break; + case 'd': + DelayLimit = (float)atof(argv[globalUtilOptind]); + globalUtilOptind++; + if ( DelayLimit <= 0.0 ) + goto usage; + break; + case 'a': + AreaLimit = (float)atof(argv[globalUtilOptind]); + globalUtilOptind++; + if ( AreaLimit <= 0.0 ) + goto usage; + break; + case 's': + fSkipInvs ^= 1; + break; + case 'o': + fWriteOldFormat ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + break; + default: + goto usage; + } + } + + + if ( argc != globalUtilOptind + 1 ) + { + fprintf( pErr, "The GENLIB library file should be given on the command line.\n" ); + goto usage; + } + + if ( nVarsMax < 2 || nVarsMax > 6 ) + { + fprintf( pErr, "The max number of variables (%d) should be more than 1 and less than 7.\n", nVarsMax ); + goto usage; + } + + // get the input file name + FileName = argv[globalUtilOptind]; + if ( (pFile = Io_FileOpen( FileName, "open_path", "r", 0 )) == NULL ) +// if ( (pFile = fopen( FileName, "r" )) == NULL ) + { + fprintf( pErr, "Cannot open input file \"%s\". ", FileName ); + if (( FileName = Extra_FileGetSimilarName( FileName, ".genlib", ".lib", ".gen", ".g", NULL ) )) + fprintf( pErr, "Did you mean \"%s\"?", FileName ); + fprintf( pErr, "\n" ); + return 1; + } + fclose( pFile ); + + // set the new network + pLib = Mio_LibraryRead( pAbc, FileName, ExcludeFile, fVerbose ); + if ( pLib == NULL ) + { + fprintf( pErr, "Reading library has failed.\n" ); + goto usage; + } + + // compute the gates + Super_Precompute( pLib, nVarsMax, nLevels, DelayLimit, AreaLimit, TimeLimit, fSkipInvs, fWriteOldFormat, fVerbose ); + + // delete the library + Mio_LibraryDelete( pLib ); + return 0; + +usage: + fprintf( pErr, "usage: super [-i num] [-l num] [-d float] [-a float] [-t num] [-sovh] \n"); + fprintf( pErr, "\t precomputes the supergates for the given GENLIB library\n" ); + fprintf( pErr, "\t-i num : the max number of supergate inputs [default = %d]\n", nVarsMax ); + fprintf( pErr, "\t-l num : the max number of levels of gates [default = %d]\n", nLevels ); + fprintf( pErr, "\t-d float : the max delay of the supergates [default = %.2f]\n", DelayLimit ); + fprintf( pErr, "\t-a float : the max area of the supergates [default = %.2f]\n", AreaLimit ); + fprintf( pErr, "\t-t num : the approximate runtime limit in seconds [default = %d]\n", TimeLimit ); + fprintf( pErr, "\t-s : toggle the use of inverters at the inputs [default = %s]\n", (fSkipInvs? "no": "yes") ); + fprintf( pErr, "\t-o : toggle dumping the supergate library in old format [default = %s]\n", (fWriteOldFormat? "yes": "no") ); + fprintf( pErr, "\t-e file : file contains list of genlib gates to exclude\n" ); + fprintf( pErr, "\t-v : enable verbose output [default = %s]\n", (fVerbose? "yes" : "no") ); + fprintf( pErr, "\t-h : print the help message\n"); + fprintf( pErr, "\n"); + fprintf( pErr, "\tHere is a piece of advice on precomputing supergate libraries:\n"); + fprintf( pErr, "\t\n"); + fprintf( pErr, "\tStart with the number of inputs equal to 5 (-i 5), the number of \n"); + fprintf( pErr, "\tlevels equal to 3 (-l 3), the delay equal to 2-3 delays of inverter, \n"); + fprintf( pErr, "\tthe area equal to 3-4 areas of two input NAND, and runtime limit equal \n"); + fprintf( pErr, "\tto 10 seconds (-t 10). Run precomputation and learn from the result.\n"); + fprintf( pErr, "\tDetermine what parameter is most constraining and try to increase \n"); + fprintf( pErr, "\tthe value of that parameter. The goal is to have a well-balanced\n"); + fprintf( pErr, "\tset of constraints and the resulting supergate library containing\n"); + fprintf( pErr, "\tapproximately 100K-200K supergates. Typically, it is better to increase\n"); + fprintf( pErr, "\tdelay limit rather than area limit, because having large-area supergates\n"); + fprintf( pErr, "\tmay result in a considerable increase in area.\n"); + fprintf( pErr, "\t\n"); + fprintf( pErr, "\tNote that a good supergate library for experiments typically can be \n"); + fprintf( pErr, "\tprecomputed in 30 sec. Increasing the runtime limit makes sense when\n"); + fprintf( pErr, "\tother parameters are well-balanced and it is needed to enumerate more\n"); + fprintf( pErr, "\tchoices to have a good result. In the end, to compute the final library\n"); + fprintf( pErr, "\tthe runtime can be set to 300 sec to ensure the ultimate quality.\n"); + fprintf( pErr, "\tIn some cases, the runtime has to be reduced if the supergate library\n"); + fprintf( pErr, "\tcontains too many supergates (> 500K).\n"); + fprintf( pErr, "\t\n"); + fprintf( pErr, "\tWhen precomputing libraries of 6 inputs (-i 6), start with even more \n"); + fprintf( pErr, "\trestricted parameters and gradually increase them until the goal is met.\n"); + fprintf( pErr, "\t\n"); + return 1; /* error exit */ +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/super/super.h b/abc_with_bb_support/src/map/super/super.h new file mode 100644 index 000000000..1ee9bf2d1 --- /dev/null +++ b/abc_with_bb_support/src/map/super/super.h @@ -0,0 +1,60 @@ +/**CFile**************************************************************** + + FileName [super.h] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Pre-computation of supergates (delay-limited gate combinations).] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 8, 2003.] + + Revision [$Id: super.h,v 1.3 2004/06/28 14:20:25 alanmi Exp $] + +***********************************************************************/ + +#ifndef __SUPER_H__ +#define __SUPER_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// GLOBAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== superCore.c =============================================================*/ + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/map/super/superAnd.c b/abc_with_bb_support/src/map/super/superAnd.c new file mode 100644 index 000000000..6b7417aef --- /dev/null +++ b/abc_with_bb_support/src/map/super/superAnd.c @@ -0,0 +1,696 @@ +/**CFile**************************************************************** + + FileName [superAnd.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Pre-computation of supergates.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 8, 2003.] + + Revision [$Id: superAnd.c,v 1.3 2004/06/28 14:20:25 alanmi Exp $] + +***********************************************************************/ + +#include "superInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// the bit masks +#define SUPER_MASK(n) ((~((unsigned)0)) >> (32-n)) +#define SUPER_FULL (~((unsigned)0)) + +// data structure for AND2 subgraph precomputation +typedef struct Super2_ManStruct_t_ Super2_Man_t; // manager +typedef struct Super2_LibStruct_t_ Super2_Lib_t; // library +typedef struct Super2_GateStruct_t_ Super2_Gate_t; // supergate + +struct Super2_ManStruct_t_ +{ + Extra_MmFixed_t * pMem; // memory manager for all supergates + stmm_table * tTable; // mapping of truth tables into gates + int nTried; // the total number of tried +}; + +struct Super2_LibStruct_t_ +{ + int i; // used to iterate through the table + int k; // used to iterate through the table + int nInputs; // the number of inputs + int nMints; // the number of minterms + int nLevels; // the number of logic levels + int nGates; // the number of gates in the library + int nGatesAlloc; // the number of allocated places + Super2_Gate_t ** pGates; // the gates themselves + unsigned uMaskBit; // the mask used to determine the compl bit +}; + +struct Super2_GateStruct_t_ +{ + unsigned uTruth; // the truth table of this supergate + Super2_Gate_t * pOne; // the left wing + Super2_Gate_t * pTwo; // the right wing + Super2_Gate_t * pNext; // the next gate in the table +}; + + +// manipulation of complemented attributes +#define Super2_IsComplement(p) (((int)((unsigned long) (p) & 01))) +#define Super2_Regular(p) ((Super2_Gate_t *)((unsigned long)(p) & ~01)) +#define Super2_Not(p) ((Super2_Gate_t *)((unsigned long)(p) ^ 01)) +#define Super2_NotCond(p,c) ((Super2_Gate_t *)((unsigned long)(p) ^ (c))) + +// iterating through the gates in the library +#define Super2_LibForEachGate( Lib, Gate ) \ + for ( Lib->i = 0; \ + Lib->i < Lib->nGates && (Gate = Lib->pGates[Lib->i]); \ + Lib->i++ ) +#define Super2_LibForEachGate2( Lib, Gate2 ) \ + for ( Lib->k = 0; \ + Lib->k < Lib->i && (Gate2 = Lib->pGates[Lib->k]); \ + Lib->k++ ) + +// static functions +static Super2_Man_t * Super2_ManStart(); +static void Super2_ManStop( Super2_Man_t * pMan ); +static Super2_Lib_t * Super2_LibStart(); +static Super2_Lib_t * Super2_LibDup( Super2_Lib_t * pLib ); +static void Super2_LibStop( Super2_Lib_t * pLib ); +static void Super2_LibAddGate( Super2_Lib_t * pLib, Super2_Gate_t * pGate ); +static Super2_Lib_t * Super2_LibFirst( Super2_Man_t * pMan, int nInputs ); +static Super2_Lib_t * Super2_LibCompute( Super2_Man_t * pMan, Super2_Lib_t * pLib ); + +static void Super2_LibWrite( Super2_Lib_t * pLib ); +static void Super2_LibWriteGate( FILE * pFile, Super2_Lib_t * pLib, Super2_Gate_t * pGate ); +static char * Super2_LibWriteGate_rec( Super2_Gate_t * pGate, int fInv, int Level ); +static int Super2_LibWriteCompare( char * pStr1, char * pStr2 ); +static int Super2_LibCompareGates( Super2_Gate_t ** ppG1, Super2_Gate_t ** ppG2 ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Precomputes the library of AND2 gates.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Super2_Precompute( int nInputs, int nLevels, int fVerbose ) +{ + Super2_Man_t * pMan; + Super2_Lib_t * pLibCur, * pLibNext; + int Level; + int clk; + + assert( nInputs < 6 ); + + // start the manager + pMan = Super2_ManStart(); + + // get the starting supergates + pLibCur = Super2_LibFirst( pMan, nInputs ); + + // perform the computation of supergates +printf( "Computing supergates for %d inputs and %d levels:\n", nInputs, nLevels ); + for ( Level = 1; Level <= nLevels; Level++ ) + { +clk = clock(); + pLibNext = Super2_LibCompute( pMan, pLibCur ); + pLibNext->nLevels = Level; + Super2_LibStop( pLibCur ); + pLibCur = pLibNext; +printf( "Level %d: Tried = %7d. Computed = %7d. ", Level, pMan->nTried, pLibCur->nGates ); +PRT( "Runtime", clock() - clk ); +fflush( stdout ); + } + +printf( "Writing the output file...\n" ); +fflush( stdout ); + // write them into a file + Super2_LibWrite( pLibCur ); + Super2_LibStop( pLibCur ); + + // stop the manager + Super2_ManStop( pMan ); +} + + + + +/**Function************************************************************* + + Synopsis [Starts the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Super2_Man_t * Super2_ManStart() +{ + Super2_Man_t * pMan; + pMan = ALLOC( Super2_Man_t, 1 ); + memset( pMan, 0, sizeof(Super2_Man_t) ); + pMan->pMem = Extra_MmFixedStart( sizeof(Super2_Gate_t) ); + pMan->tTable = stmm_init_table( st_ptrcmp, st_ptrhash ); + return pMan; +} + +/**Function************************************************************* + + Synopsis [Stops the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Super2_ManStop( Super2_Man_t * pMan ) +{ + Extra_MmFixedStop( pMan->pMem ); + stmm_free_table( pMan->tTable ); + free( pMan ); +} + +/**Function************************************************************* + + Synopsis [Starts the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Super2_Lib_t * Super2_LibStart() +{ + Super2_Lib_t * pLib; + pLib = ALLOC( Super2_Lib_t, 1 ); + memset( pLib, 0, sizeof(Super2_Lib_t) ); + return pLib; +} + +/**Function************************************************************* + + Synopsis [Duplicates the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Super2_Lib_t * Super2_LibDup( Super2_Lib_t * pLib ) +{ + Super2_Lib_t * pLibNew; + pLibNew = Super2_LibStart(); + pLibNew->nInputs = pLib->nInputs; + pLibNew->nMints = pLib->nMints; + pLibNew->nLevels = pLib->nLevels; + pLibNew->nGates = pLib->nGates; + pLibNew->uMaskBit = pLib->uMaskBit; + pLibNew->nGatesAlloc = 1000 + pLib->nGatesAlloc; + pLibNew->pGates = ALLOC( Super2_Gate_t *, pLibNew->nGatesAlloc ); + memcpy( pLibNew->pGates, pLib->pGates, pLibNew->nGates * sizeof(Super2_Gate_t *) ); + return pLibNew; +} + +/**Function************************************************************* + + Synopsis [Add gate to the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Super2_LibAddGate( Super2_Lib_t * pLib, Super2_Gate_t * pGate ) +{ + if ( pLib->nGates == pLib->nGatesAlloc ) + { + pLib->pGates = REALLOC( Super2_Gate_t *, pLib->pGates, 3 * pLib->nGatesAlloc ); + pLib->nGatesAlloc *= 3; + } + pLib->pGates[ pLib->nGates++ ] = pGate; +} + +/**Function************************************************************* + + Synopsis [Stops the library.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Super2_LibStop( Super2_Lib_t * pLib ) +{ + free( pLib->pGates ); + free( pLib ); +} + +/**Function************************************************************* + + Synopsis [Derives the starting supergates.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Super2_Lib_t * Super2_LibFirst( Super2_Man_t * pMan, int nInputs ) +{ + Super2_Lib_t * pLib; + int v, m; + + // start the library + pLib = Super2_LibStart(); + + // create the starting supergates + pLib->nInputs = nInputs; + pLib->nMints = (1 << nInputs); + pLib->nLevels = 0; + pLib->nGates = nInputs + 1; + pLib->nGatesAlloc = nInputs + 1; + pLib->uMaskBit = (1 << (pLib->nMints-1)); + pLib->pGates = ALLOC( Super2_Gate_t *, nInputs + 1 ); + // add the constant 0 + pLib->pGates[0] = (Super2_Gate_t *)Extra_MmFixedEntryFetch( pMan->pMem ); + memset( pLib->pGates[0], 0, sizeof(Super2_Gate_t) ); + // add the elementary gates + for ( v = 0; v < nInputs; v++ ) + { + pLib->pGates[v+1] = (Super2_Gate_t *)Extra_MmFixedEntryFetch( pMan->pMem ); + memset( pLib->pGates[v+1], 0, sizeof(Super2_Gate_t) ); + pLib->pGates[v+1]->pTwo = (Super2_Gate_t *)v; + } + + // set up their truth tables + for ( m = 0; m < pLib->nMints; m++ ) + for ( v = 0; v < nInputs; v++ ) + if ( m & (1 << v) ) + pLib->pGates[v+1]->uTruth |= (1 << m); + return pLib; +} + +/**Function************************************************************* + + Synopsis [Precomputes one level of supergates.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Super2_Lib_t * Super2_LibCompute( Super2_Man_t * pMan, Super2_Lib_t * pLib ) +{ + Super2_Lib_t * pLibNew; + Super2_Gate_t * pGate1, * pGate2, * pGateNew; + Super2_Gate_t ** ppGate; + unsigned Mask = SUPER_MASK(pLib->nMints); + unsigned uTruth, uTruthR, uTruth1, uTruth2, uTruth1c, uTruth2c; + + // start the new library + pLibNew = Super2_LibDup( pLib ); + + // reset the hash table + stmm_free_table( pMan->tTable ); + pMan->tTable = stmm_init_table( st_ptrcmp, st_ptrhash ); + // set the starting things into the hash table + Super2_LibForEachGate( pLibNew, pGate1 ) + { + uTruthR = ((pGate1->uTruth & pLibNew->uMaskBit)? Mask & ~pGate1->uTruth : pGate1->uTruth); + + if ( stmm_lookup( pMan->tTable, (char *)uTruthR, (char **)&pGate2 ) ) + { + printf( "New gate:\n" ); + Super2_LibWriteGate( stdout, pLibNew, pGate1 ); + printf( "Gate in the table:\n" ); + Super2_LibWriteGate( stdout, pLibNew, pGate2 ); + assert( 0 ); + } + stmm_insert( pMan->tTable, (char *)uTruthR, (char *)pGate1 ); + } + + + // set the number of gates tried + pMan->nTried = pLibNew->nGates; + + // go through the gate pairs + Super2_LibForEachGate( pLib, pGate1 ) + { + if ( pLib->i && pLib->i % 300 == 0 ) + { + printf( "Tried %5d first gates...\n", pLib->i ); + fflush( stdout ); + } + + Super2_LibForEachGate2( pLib, pGate2 ) + { + uTruth1 = pGate1->uTruth; + uTruth2 = pGate2->uTruth; + uTruth1c = Mask & ~uTruth1; + uTruth2c = Mask & ~uTruth2; + + // none complemented + uTruth = uTruth1 & uTruth2; + uTruthR = ((uTruth & pLibNew->uMaskBit)? Mask & ~uTruth : uTruth); + + if ( !stmm_find_or_add( pMan->tTable, (char *)uTruthR, (char ***)&ppGate ) ) + { + pGateNew = (Super2_Gate_t *)Extra_MmFixedEntryFetch( pMan->pMem ); + pGateNew->pOne = pGate1; + pGateNew->pTwo = pGate2; + pGateNew->uTruth = uTruth; + *ppGate = pGateNew; + Super2_LibAddGate( pLibNew, pGateNew ); + } + + // one complemented + uTruth = uTruth1c & uTruth2; + uTruthR = ((uTruth & pLibNew->uMaskBit)? Mask & ~uTruth : uTruth); + + if ( !stmm_find_or_add( pMan->tTable, (char *)uTruthR, (char ***)&ppGate ) ) + { + pGateNew = (Super2_Gate_t *)Extra_MmFixedEntryFetch( pMan->pMem ); + pGateNew->pOne = Super2_Not(pGate1); + pGateNew->pTwo = pGate2; + pGateNew->uTruth = uTruth; + *ppGate = pGateNew; + Super2_LibAddGate( pLibNew, pGateNew ); + } + + // another complemented + uTruth = uTruth1 & uTruth2c; + uTruthR = ((uTruth & pLibNew->uMaskBit)? Mask & ~uTruth : uTruth); + + if ( !stmm_find_or_add( pMan->tTable, (char *)uTruthR, (char ***)&ppGate ) ) + { + pGateNew = (Super2_Gate_t *)Extra_MmFixedEntryFetch( pMan->pMem ); + pGateNew->pOne = pGate1; + pGateNew->pTwo = Super2_Not(pGate2); + pGateNew->uTruth = uTruth; + *ppGate = pGateNew; + Super2_LibAddGate( pLibNew, pGateNew ); + } + + // both complemented + uTruth = uTruth1c & uTruth2c; + uTruthR = ((uTruth & pLibNew->uMaskBit)? Mask & ~uTruth : uTruth); + + if ( !stmm_find_or_add( pMan->tTable, (char *)uTruthR, (char ***)&ppGate ) ) + { + pGateNew = (Super2_Gate_t *)Extra_MmFixedEntryFetch( pMan->pMem ); + pGateNew->pOne = Super2_Not(pGate1); + pGateNew->pTwo = Super2_Not(pGate2); + pGateNew->uTruth = uTruth; + *ppGate = pGateNew; + Super2_LibAddGate( pLibNew, pGateNew ); + } + + pMan->nTried += 4; + } + } + return pLibNew; +} + + +static unsigned s_uMaskBit; +static unsigned s_uMaskAll; + +/**Function************************************************************* + + Synopsis [Writes the library into the file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Super2_LibWrite( Super2_Lib_t * pLib ) +{ + Super2_Gate_t * pGate; + FILE * pFile; + char FileName[100]; + int clk; + + if ( pLib->nLevels > 5 ) + { + printf( "Cannot write file for %d levels.\n", pLib->nLevels ); + return; + } + +clk = clock(); + // sort the supergates by truth table + s_uMaskBit = pLib->uMaskBit; + s_uMaskAll = SUPER_MASK(pLib->nMints); + qsort( (void *)pLib->pGates, pLib->nGates, sizeof(Super2_Gate_t *), + (int (*)(const void *, const void *)) Super2_LibCompareGates ); + assert( Super2_LibCompareGates( pLib->pGates, pLib->pGates + pLib->nGates - 1 ) < 0 ); +PRT( "Sorting", clock() - clk ); + + + // start the file + sprintf( FileName, "superI%dL%d", pLib->nInputs, pLib->nLevels ); + pFile = fopen( FileName, "w" ); + fprintf( pFile, "# AND2/INV supergates derived on %s.\n", Extra_TimeStamp() ); + fprintf( pFile, "# Command line: \"super2 -i %d -l %d\".\n", pLib->nInputs, pLib->nLevels ); + fprintf( pFile, "# The number of inputs = %6d.\n", pLib->nInputs ); + fprintf( pFile, "# The number of levels = %6d.\n", pLib->nLevels ); + fprintf( pFile, "# The number of supergates = %6d.\n", pLib->nGates ); + fprintf( pFile, "# The total functions = %6d.\n", (1<<(pLib->nMints-1)) ); + fprintf( pFile, "\n" ); + fprintf( pFile, "%6d\n", pLib->nGates ); + + // print the gates + Super2_LibForEachGate( pLib, pGate ) + Super2_LibWriteGate( pFile, pLib, pGate ); + fclose( pFile ); + + printf( "The supergates are written into file \"%s\" ", FileName ); + printf( "(%0.2f Mb).\n", ((double)Extra_FileSize(FileName))/(1<<20) ); +} + +/**Function************************************************************* + + Synopsis [Writes the gate into the file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Super2_LibCompareGates( Super2_Gate_t ** ppG1, Super2_Gate_t ** ppG2 ) +{ + Super2_Gate_t * pG1 = *ppG1; + Super2_Gate_t * pG2 = *ppG2; + unsigned uTruth1, uTruth2; + + uTruth1 = (pG1->uTruth & s_uMaskBit)? s_uMaskAll & ~pG1->uTruth : pG1->uTruth; + uTruth2 = (pG2->uTruth & s_uMaskBit)? s_uMaskAll & ~pG2->uTruth : pG2->uTruth; + + if ( uTruth1 < uTruth2 ) + return -1; + return 1; +} + +/**Function************************************************************* + + Synopsis [Writes the gate into the file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Super2_LibWriteGate( FILE * pFile, Super2_Lib_t * pLib, Super2_Gate_t * pGate ) +{ +// unsigned uTruthR; + unsigned uTruth; + int fInv; + + // check whether the gate need complementation + fInv = (int)(pGate->uTruth & pLib->uMaskBit); + uTruth = (fInv? ~pGate->uTruth : pGate->uTruth); +/* + // reverse the truth table + uTruthR = 0; + for ( m = 0; m < pLib->nMints; m++ ) + if ( uTruth & (1 << m) ) + uTruthR |= (1 << (pLib->nMints-1-m)); +*/ + // write the truth table + Extra_PrintBinary( pFile, &uTruth, pLib->nMints ); + fprintf( pFile, " " ); + // write the symbolic expression + fprintf( pFile, "%s", Super2_LibWriteGate_rec( pGate, fInv, pLib->nLevels ) ); + fprintf( pFile, "\n" ); +} + + +/**Function************************************************************* + + Synopsis [Recursively writes the gate into the file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Super2_LibWriteGate_rec( Super2_Gate_t * pGate, int fInv, int Level ) +{ + static char Buff01[ 3], Buff02[ 3]; // Max0 = 1 + static char Buff11[ 6], Buff12[ 6]; // Max1 = 2*Max0 + 2 = 4 + static char Buff21[ 12], Buff22[ 12]; // Max2 = 2*Max1 + 2 = 10 + static char Buff31[ 25], Buff32[ 25]; // Max3 = 2*Max2 + 2 = 22 + static char Buff41[ 50], Buff42[ 50]; // Max4 = 2*Max3 + 2 = 46 + static char Buff51[100], Buff52[100]; // Max5 = 2*Max4 + 2 = 94 + static char * pBuffs1[6] = { Buff01, Buff11, Buff21, Buff31, Buff41, Buff51 }; + static char * pBuffs2[6] = { Buff02, Buff12, Buff22, Buff32, Buff42, Buff52 }; + char * pBranch; + char * pBuffer1 = pBuffs1[Level]; + char * pBuffer2 = pBuffs2[Level]; + Super2_Gate_t * pGateNext1, * pGateNext2; + int fInvNext1, fInvNext2; + int RetValue; + + // consider the last level + assert( Level >= 0 ); + if ( pGate->pOne == NULL ) + { + if ( pGate->uTruth == 0 ) + { + pBuffer1[0] = (fInv? '1': '0'); + pBuffer1[1] = '$'; + pBuffer1[2] = 0; + } + else + { + pBuffer1[0] = (fInv? 'A' + ((int)pGate->pTwo): 'a' + ((int)pGate->pTwo)); + pBuffer1[1] = 0; + } + return pBuffer1; + } + assert( Level > 0 ); + + + // get the left branch + pGateNext1 = Super2_Regular(pGate->pOne); + fInvNext1 = Super2_IsComplement(pGate->pOne); + pBranch = Super2_LibWriteGate_rec(pGateNext1, fInvNext1, Level - 1); + // copy into Buffer1 + strcpy( pBuffer1, pBranch ); + + // get the right branch + pGateNext2 = Super2_Regular(pGate->pTwo); + fInvNext2 = Super2_IsComplement(pGate->pTwo); + pBranch = Super2_LibWriteGate_rec(pGateNext2, fInvNext2, Level - 1); + + // consider the case when comparison is not necessary + if ( fInvNext1 ^ fInvNext2 ) + { + if ( fInvNext1 > fInvNext2 ) + sprintf( pBuffer2, "%c%s%s%c", (fInv? '<': '('), pBuffer1, pBranch, (fInv? '>': ')') ); + else + sprintf( pBuffer2, "%c%s%s%c", (fInv? '<': '('), pBranch, pBuffer1, (fInv? '>': ')') ); + } + else + { + // compare the two branches + RetValue = Super2_LibWriteCompare( pBuffer1, pBranch ); + if ( RetValue == 1 ) + sprintf( pBuffer2, "%c%s%s%c", (fInv? '<': '('), pBuffer1, pBranch, (fInv? '>': ')') ); + else // if ( RetValue == -1 ) + { + sprintf( pBuffer2, "%c%s%s%c", (fInv? '<': '('), pBranch, pBuffer1, (fInv? '>': ')') ); + if ( RetValue == 0 ) + printf( "Strange!\n" ); + } + } + return pBuffer2; +} + +/**Function************************************************************* + + Synopsis [Compares the two branches of the tree.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Super2_LibWriteCompare( char * pStr1, char * pStr2 ) +{ + while ( 1 ) + { + // skip extra symbols + while ( *pStr1 && *pStr1 < 'A' ) + pStr1++; + while ( *pStr2 && *pStr2 < 'A' ) + pStr2++; + + // check if any one is finished + if ( *pStr1 == 0 || *pStr2 == 0 ) + { + if ( *pStr2 ) + return 1; + return -1; + } + + // compare + if ( *pStr1 == *pStr2 ) + { + pStr1++; + pStr2++; + } + else + { + if ( *pStr1 < *pStr2 ) + return 1; + return -1; + } + } + return 0; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/super/superGENERIC.c b/abc_with_bb_support/src/map/super/superGENERIC.c new file mode 100644 index 000000000..15175efcf --- /dev/null +++ b/abc_with_bb_support/src/map/super/superGENERIC.c @@ -0,0 +1,46 @@ +/**CFile**************************************************************** + + FileName [super__.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Pre-computation of supergates.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 8, 2003.] + + Revision [$Id: super__.h,v 1.0 2003/09/08 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "superInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/map/super/superGate.c b/abc_with_bb_support/src/map/super/superGate.c new file mode 100644 index 000000000..b35d1b94a --- /dev/null +++ b/abc_with_bb_support/src/map/super/superGate.c @@ -0,0 +1,1324 @@ +/**CFile**************************************************************** + + FileName [superGate.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Pre-computation of supergates.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 8, 2003.] + + Revision [$Id: superGate.c,v 1.7 2004/08/03 00:11:40 satrajit Exp $] + +***********************************************************************/ + +#include "superInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// the bit masks +#define SUPER_MASK(n) ((~((unsigned)0)) >> (32-(n))) +#define SUPER_FULL (~((unsigned)0)) +#define SUPER_NO_VAR (-9999.0) +#define SUPER_EPSILON (0.001) + +// data structure for supergate precomputation +typedef struct Super_ManStruct_t_ Super_Man_t; // manager +typedef struct Super_GateStruct_t_ Super_Gate_t; // supergate + +struct Super_ManStruct_t_ +{ + // parameters + char * pName; // the original genlib file name + int nVarsMax; // the number of inputs + int nMints; // the number of minterms + int nLevels; // the number of logic levels + float tDelayMax; // the max delay of the supergates in the library + float tAreaMax; // the max area of the supergates in the library + int fSkipInv; // the flag says about skipping inverters + int fWriteOldFormat; // in addition, writes the file in the old format + int fVerbose; + + // supergates + Super_Gate_t * pInputs[10]; // the input supergates + int nGates; // the number of gates in the library + Super_Gate_t ** pGates; // the gates themselves + stmm_table * tTable; // mapping of truth tables into gates + + // memory managers + Extra_MmFixed_t * pMem; // memory manager for the supergates + Extra_MmFlex_t * pMemFlex; // memory manager for the fanin arrays + + // statistics + int nTried; // the total number of tried + int nAdded; // the number of entries added + int nRemoved; // the number of entries removed + int nUnique; // the number of unique gates + int nLookups; // the number of hash table lookups + int nAliases; // the number of hash table lookups thrown away due to aliasing + + // runtime + int Time; // the runtime of the generation procedure + int TimeLimit; // the runtime limit (in seconds) + int TimeSec; // the time passed (in seconds) + int TimeStop; // the time to stop computation (in miliseconds) + int TimePrint; // the time to print message +}; + +struct Super_GateStruct_t_ +{ + Mio_Gate_t * pRoot; // the root gate for this supergate + unsigned fVar : 1; // the flag signaling the elementary variable + unsigned fSuper : 1; // the flag signaling the elementary variable + unsigned nFanins : 6; // the number of fanin gates + unsigned Number : 24; // the number assigned in the process + unsigned uTruth[2]; // the truth table of this supergate + Super_Gate_t * pFanins[6]; // the fanins of the gate + float Area; // the area of this gate + float ptDelays[6]; // the pin-to-pin delays for all inputs + float tDelayMax; // the maximum delay + Super_Gate_t * pNext; // the next gate in the table +}; + + +// iterating through the gates in the library +#define Super_ManForEachGate( GateArray, Limit, Index, Gate ) \ + for ( Index = 0; \ + Index < Limit && (Gate = GateArray[Index]); \ + Index++ ) + +// static functions +static Super_Man_t * Super_ManStart(); +static void Super_ManStop( Super_Man_t * pMan ); + +static void Super_AddGateToTable( Super_Man_t * pMan, Super_Gate_t * pGate ); +static void Super_First( Super_Man_t * pMan, int nVarsMax ); +static Super_Man_t * Super_Compute( Super_Man_t * pMan, Mio_Gate_t ** ppGates, int nGates, bool fSkipInv ); +static Super_Gate_t * Super_CreateGateNew( Super_Man_t * pMan, Mio_Gate_t * pRoot, Super_Gate_t ** pSupers, int nSupers, unsigned uTruth[], float Area, float tPinDelaysRes[], float tDelayMax, int nPins ); +static bool Super_CompareGates( Super_Man_t * pMan, unsigned uTruth[], float Area, float tPinDelaysRes[], int nPins ); +static int Super_DelayCompare( Super_Gate_t ** ppG1, Super_Gate_t ** ppG2 ); +static int Super_AreaCompare( Super_Gate_t ** ppG1, Super_Gate_t ** ppG2 ); +static void Super_TranferGatesToArray( Super_Man_t * pMan ); +static int Super_CheckTimeout( ProgressBar * pPro, Super_Man_t * pMan ); + +static void Super_Write( Super_Man_t * pMan ); +static int Super_WriteCompare( Super_Gate_t ** ppG1, Super_Gate_t ** ppG2 ); +static void Super_WriteFileHeader( Super_Man_t * pMan, FILE * pFile ); + +static void Super_WriteLibrary( Super_Man_t * pMan ); +static void Super_WriteLibraryGate( FILE * pFile, Super_Man_t * pMan, Super_Gate_t * pGate, int Num ); +static char * Super_WriteLibraryGateName( Super_Gate_t * pGate ); +static void Super_WriteLibraryGateName_rec( Super_Gate_t * pGate, char * pBuffer ); + +static void Super_WriteLibraryTree( Super_Man_t * pMan ); +static void Super_WriteLibraryTree_rec( FILE * pFile, Super_Man_t * pMan, Super_Gate_t * pSuper, int * pCounter ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Precomputes the library of supergates.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Super_Precompute( Mio_Library_t * pLibGen, int nVarsMax, int nLevels, float tDelayMax, float tAreaMax, int TimeLimit, bool fSkipInv, bool fWriteOldFormat, int fVerbose ) +{ + Super_Man_t * pMan; + Mio_Gate_t ** ppGates; + int nGates, Level, clk, clockStart; + + assert( nVarsMax < 7 ); + + // get the root gates + ppGates = Mio_CollectRoots( pLibGen, nVarsMax, tDelayMax, 0, &nGates ); + + // start the manager + pMan = Super_ManStart(); + pMan->pName = Mio_LibraryReadName(pLibGen); + pMan->fSkipInv = fSkipInv; + pMan->tDelayMax = tDelayMax; + pMan->tAreaMax = tAreaMax; + pMan->TimeLimit = TimeLimit; // in seconds + pMan->TimeStop = TimeLimit * CLOCKS_PER_SEC + clock(); // in CPU ticks + pMan->fWriteOldFormat = fWriteOldFormat; + pMan->fVerbose = fVerbose; + + if ( nGates == 0 ) + { + fprintf( stderr, "Error: No genlib gates satisfy the limits criteria. Stop.\n"); + fprintf( stderr, "Limits: max delay = %.2f, max area = %.2f, time limit = %d sec.\n", + pMan->tDelayMax, pMan->tAreaMax, pMan->TimeLimit ); + + // stop the manager + Super_ManStop( pMan ); + free( ppGates ); + + return; + } + + // get the starting supergates + Super_First( pMan, nVarsMax ); + + // perform the computation of supergates + clockStart = clock(); +if ( fVerbose ) +{ + printf( "Computing supergates with %d inputs and %d levels.\n", + pMan->nVarsMax, nLevels ); + printf( "Limits: max delay = %.2f, max area = %.2f, time limit = %d sec.\n", + pMan->tDelayMax, pMan->tAreaMax, pMan->TimeLimit ); +} + + for ( Level = 1; Level <= nLevels; Level++ ) + { + if ( clock() > pMan->TimeStop ) + break; +clk = clock(); + Super_Compute( pMan, ppGates, nGates, fSkipInv ); + pMan->nLevels = Level; +if ( fVerbose ) +{ + printf( "Lev %d: Try =%12d. Add =%6d. Rem =%5d. Save =%6d. Lookups =%12d. Aliases =%12d. ", + Level, pMan->nTried, pMan->nAdded, pMan->nRemoved, pMan->nAdded - pMan->nRemoved, pMan->nLookups, pMan->nAliases ); +PRT( "Time", clock() - clk ); +fflush( stdout ); +} + } + pMan->Time = clock() - clockStart; + +if ( fVerbose ) +{ +printf( "Writing the output file...\n" ); +fflush( stdout ); +} + // write them into a file + Super_Write( pMan ); + + // stop the manager + Super_ManStop( pMan ); + free( ppGates ); +} + + +/**Function************************************************************* + + Synopsis [Derives the starting supergates.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Super_First( Super_Man_t * pMan, int nVarsMax ) +{ + Super_Gate_t * pSuper; + int nMintLimit, nVarLimit; + int v, m; + // set the parameters + pMan->nVarsMax = nVarsMax; + pMan->nMints = (1 << nVarsMax); + pMan->nLevels = 0; + // allocate room for the gates + pMan->nGates = nVarsMax; + pMan->pGates = ALLOC( Super_Gate_t *, nVarsMax + 2 ); + // create the gates corresponding to the elementary variables + for ( v = 0; v < nVarsMax; v++ ) + { + // get a new gate + pSuper = (Super_Gate_t *)Extra_MmFixedEntryFetch( pMan->pMem ); + memset( pSuper, 0, sizeof(Super_Gate_t) ); + // assign the elementary variable, the truth table, and the delays + pSuper->fVar = 1; + pSuper->Number = v; + for ( m = 0; m < nVarsMax; m++ ) + pSuper->ptDelays[m] = SUPER_NO_VAR; + pSuper->ptDelays[v] = 0.0; + // set the gate + pMan->pGates[v] = pSuper; + Super_AddGateToTable( pMan, pSuper ); + pMan->pInputs[v] = pSuper; + } + // set up their truth tables + nVarLimit = (nVarsMax >= 5)? 5 : nVarsMax; + nMintLimit = (1 << nVarLimit); + for ( m = 0; m < nMintLimit; m++ ) + for ( v = 0; v < nVarLimit; v++ ) + if ( m & (1 << v) ) + pMan->pGates[v]->uTruth[0] |= (1 << m); + // make adjustments for the case of 6 variables + if ( nVarsMax == 6 ) + { + for ( v = 0; v < 5; v++ ) + pMan->pGates[v]->uTruth[1] = pMan->pGates[v]->uTruth[0]; + pMan->pGates[5]->uTruth[0] = 0; + pMan->pGates[5]->uTruth[1] = ~((unsigned)0); + } + else + { + for ( v = 0; v < nVarsMax; v++ ) + pMan->pGates[v]->uTruth[1] = 0; + } +} + +/**Function************************************************************* + + Synopsis [Precomputes one level of supergates.] + + Description [This procedure computes the set of supergates that can be + derived from the given set of root gates (from GENLIB library) by composing + the root gates with the currently available supergates. This procedure is + smart in the sense that it tries to avoid useless emuration by imposing + tight bounds by area and delay. Only the supergates and are guaranteed to + have smaller area and delay are enumereated. See comments below for details.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Super_Man_t * Super_Compute( Super_Man_t * pMan, Mio_Gate_t ** ppGates, int nGates, bool fSkipInv ) +{ + Super_Gate_t * pSupers[6], * pGate0, * pGate1, * pGate2, * pGate3, * pGate4, * pGate5, * pGateNew; + float tPinDelaysRes[6], * ptPinDelays[6], tPinDelayMax, tDelayMio; + float Area, Area0, Area1, Area2, Area3, Area4, AreaMio; + unsigned uTruth[2], uTruths[6][2]; + int i0, i1, i2, i3, i4, i5; + Super_Gate_t ** ppGatesLimit; + int nFanins, nGatesLimit, k, s, t; + ProgressBar * pProgress; + int fTimeOut; + int fPrune = 1; // Shall we prune? + int iPruneLimit = 3; // Each of the gates plugged into the root gate will have + // less than these many fanins + int iPruneLimitRoot = 4; // The root gate may have only less than these many fanins + + // put the gates from the unique table into the array + // the gates from the array will be used to compose other gates + // the gates in tbe table are used to check uniqueness of collected gates + Super_TranferGatesToArray( pMan ); + + // sort the gates in the increasing order of maximum delay + if ( pMan->nGates > 10000 ) + { + printf( "Sorting array of %d supergates...\r", pMan->nGates ); + fflush( stdout ); + } + qsort( (void *)pMan->pGates, pMan->nGates, sizeof(Super_Gate_t *), + (int (*)(const void *, const void *)) Super_DelayCompare ); + assert( Super_DelayCompare( pMan->pGates, pMan->pGates + pMan->nGates - 1 ) <= 0 ); + if ( pMan->nGates > 10000 ) + { + printf( " \r" ); + } + + pProgress = Extra_ProgressBarStart( stdout, pMan->TimeLimit ); + pMan->TimePrint = clock() + CLOCKS_PER_SEC; + ppGatesLimit = ALLOC( Super_Gate_t *, pMan->nGates ); + // go through the root gates + // the root gates are sorted in the increasing gelay + fTimeOut = 0; + for ( k = 0; k < nGates; k++ ) + { + if ( fTimeOut ) break; + + if ( fPrune ) + { + if ( pMan->nLevels >= 1 ) // First level gates have been computed + { + if ( Mio_GateReadInputs(ppGates[k]) >= iPruneLimitRoot ) + continue; + } + } + + // select the subset of gates to be considered with this root gate + // all the gates past this point will lead to delay larger than the limit + tDelayMio = (float)Mio_GateReadDelayMax(ppGates[k]); + for ( s = 0, t = 0; s < pMan->nGates; s++ ) + { + if ( fPrune && ( pMan->nLevels >= 1 ) && ( ((int)pMan->pGates[s]->nFanins) >= iPruneLimit )) + continue; + + ppGatesLimit[t] = pMan->pGates[s]; + if ( ppGatesLimit[t++]->tDelayMax + tDelayMio > pMan->tDelayMax ) + break; + } + nGatesLimit = t; + + if ( pMan->fVerbose ) + { + printf ("Trying %d choices for %d inputs\n", t, Mio_GateReadInputs(ppGates[k]) ); + } + + // resort part of this range by area + // now we can prune the search by going up in the list until we reach the limit on area + // all the gates beyond this point can be skipped because their area can be only larger + if ( nGatesLimit > 10000 ) + printf( "Sorting array of %d supergates...\r", nGatesLimit ); + qsort( (void *)ppGatesLimit, nGatesLimit, sizeof(Super_Gate_t *), + (int (*)(const void *, const void *)) Super_AreaCompare ); + assert( Super_AreaCompare( ppGatesLimit, ppGatesLimit + nGatesLimit - 1 ) <= 0 ); + if ( nGatesLimit > 10000 ) + printf( " \r" ); + + // consider the combinations of gates with the root gate on top + AreaMio = (float)Mio_GateReadArea(ppGates[k]); + nFanins = Mio_GateReadInputs(ppGates[k]); + switch ( nFanins ) + { + case 0: // should not happen + assert( 0 ); + break; + case 1: // interter root + Super_ManForEachGate( ppGatesLimit, nGatesLimit, i0, pGate0 ) + { + if ( fTimeOut ) break; + fTimeOut = Super_CheckTimeout( pProgress, pMan ); + // skip the inverter as the root gate before the elementary variable + // as a result, the supergates will not have inverters on the input side + // but inverters still may occur at the output of or inside complex supergates + if ( fSkipInv && pGate0->tDelayMax == 0 ) + continue; + // compute area + Area = AreaMio + pGate0->Area; + if ( Area > pMan->tAreaMax ) + break; + + pSupers[0] = pGate0; uTruths[0][0] = pGate0->uTruth[0]; uTruths[0][1] = pGate0->uTruth[1]; ptPinDelays[0] = pGate0->ptDelays; + Mio_DeriveGateDelays( ppGates[k], ptPinDelays, nFanins, pMan->nVarsMax, SUPER_NO_VAR, tPinDelaysRes, &tPinDelayMax ); + Mio_DeriveTruthTable( ppGates[k], uTruths, nFanins, pMan->nVarsMax, uTruth ); + if ( !Super_CompareGates( pMan, uTruth, Area, tPinDelaysRes, pMan->nVarsMax ) ) + continue; + // create a new gate + pGateNew = Super_CreateGateNew( pMan, ppGates[k], pSupers, nFanins, uTruth, Area, tPinDelaysRes, tPinDelayMax, pMan->nVarsMax ); + Super_AddGateToTable( pMan, pGateNew ); + } + break; + case 2: // two-input root gate + Super_ManForEachGate( ppGatesLimit, nGatesLimit, i0, pGate0 ) + { + Area0 = AreaMio + pGate0->Area; + if ( Area0 > pMan->tAreaMax ) + break; + pSupers[0] = pGate0; uTruths[0][0] = pGate0->uTruth[0]; uTruths[0][1] = pGate0->uTruth[1]; ptPinDelays[0] = pGate0->ptDelays; + Super_ManForEachGate( ppGatesLimit, nGatesLimit, i1, pGate1 ) + if ( i1 != i0 ) + { + if ( fTimeOut ) goto done; + fTimeOut = Super_CheckTimeout( pProgress, pMan ); + // compute area + Area = Area0 + pGate1->Area; + if ( Area > pMan->tAreaMax ) + break; + + pSupers[1] = pGate1; uTruths[1][0] = pGate1->uTruth[0]; uTruths[1][1] = pGate1->uTruth[1]; ptPinDelays[1] = pGate1->ptDelays; + Mio_DeriveGateDelays( ppGates[k], ptPinDelays, nFanins, pMan->nVarsMax, SUPER_NO_VAR, tPinDelaysRes, &tPinDelayMax ); + Mio_DeriveTruthTable( ppGates[k], uTruths, nFanins, pMan->nVarsMax, uTruth ); + if ( !Super_CompareGates( pMan, uTruth, Area, tPinDelaysRes, pMan->nVarsMax ) ) + continue; + // create a new gate + pGateNew = Super_CreateGateNew( pMan, ppGates[k], pSupers, nFanins, uTruth, Area, tPinDelaysRes, tPinDelayMax, pMan->nVarsMax ); + Super_AddGateToTable( pMan, pGateNew ); + } + } + break; + case 3: // three-input root gate + Super_ManForEachGate( ppGatesLimit, nGatesLimit, i0, pGate0 ) + { + Area0 = AreaMio + pGate0->Area; + if ( Area0 > pMan->tAreaMax ) + break; + pSupers[0] = pGate0; uTruths[0][0] = pGate0->uTruth[0]; uTruths[0][1] = pGate0->uTruth[1]; ptPinDelays[0] = pGate0->ptDelays; + + Super_ManForEachGate( ppGatesLimit, nGatesLimit, i1, pGate1 ) + if ( i1 != i0 ) + { + Area1 = Area0 + pGate1->Area; + if ( Area1 > pMan->tAreaMax ) + break; + pSupers[1] = pGate1; uTruths[1][0] = pGate1->uTruth[0]; uTruths[1][1] = pGate1->uTruth[1]; ptPinDelays[1] = pGate1->ptDelays; + + Super_ManForEachGate( ppGatesLimit, nGatesLimit, i2, pGate2 ) + if ( i2 != i0 && i2 != i1 ) + { + if ( fTimeOut ) goto done; + fTimeOut = Super_CheckTimeout( pProgress, pMan ); + // compute area + Area = Area1 + pGate2->Area; + if ( Area > pMan->tAreaMax ) + break; + pSupers[2] = pGate2; uTruths[2][0] = pGate2->uTruth[0]; uTruths[2][1] = pGate2->uTruth[1]; ptPinDelays[2] = pGate2->ptDelays; + + Mio_DeriveGateDelays( ppGates[k], ptPinDelays, nFanins, pMan->nVarsMax, SUPER_NO_VAR, tPinDelaysRes, &tPinDelayMax ); + Mio_DeriveTruthTable( ppGates[k], uTruths, nFanins, pMan->nVarsMax, uTruth ); + if ( !Super_CompareGates( pMan, uTruth, Area, tPinDelaysRes, pMan->nVarsMax ) ) + continue; + // create a new gate + pGateNew = Super_CreateGateNew( pMan, ppGates[k], pSupers, nFanins, uTruth, Area, tPinDelaysRes, tPinDelayMax, pMan->nVarsMax ); + Super_AddGateToTable( pMan, pGateNew ); + } + } + } + break; + case 4: // four-input root gate + Super_ManForEachGate( ppGatesLimit, nGatesLimit, i0, pGate0 ) + { + Area0 = AreaMio + pGate0->Area; + if ( Area0 > pMan->tAreaMax ) + break; + pSupers[0] = pGate0; uTruths[0][0] = pGate0->uTruth[0]; uTruths[0][1] = pGate0->uTruth[1]; ptPinDelays[0] = pGate0->ptDelays; + + Super_ManForEachGate( ppGatesLimit, nGatesLimit, i1, pGate1 ) + if ( i1 != i0 ) + { + Area1 = Area0 + pGate1->Area; + if ( Area1 > pMan->tAreaMax ) + break; + pSupers[1] = pGate1; uTruths[1][0] = pGate1->uTruth[0]; uTruths[1][1] = pGate1->uTruth[1]; ptPinDelays[1] = pGate1->ptDelays; + + Super_ManForEachGate( ppGatesLimit, nGatesLimit, i2, pGate2 ) + if ( i2 != i0 && i2 != i1 ) + { + Area2 = Area1 + pGate2->Area; + if ( Area2 > pMan->tAreaMax ) + break; + pSupers[2] = pGate2; uTruths[2][0] = pGate2->uTruth[0]; uTruths[2][1] = pGate2->uTruth[1]; ptPinDelays[2] = pGate2->ptDelays; + + Super_ManForEachGate( ppGatesLimit, nGatesLimit, i3, pGate3 ) + if ( i3 != i0 && i3 != i1 && i3 != i2 ) + { + if ( fTimeOut ) goto done; + fTimeOut = Super_CheckTimeout( pProgress, pMan ); + // compute area + Area = Area2 + pGate3->Area; + if ( Area > pMan->tAreaMax ) + break; + pSupers[3] = pGate3; uTruths[3][0] = pGate3->uTruth[0]; uTruths[3][1] = pGate3->uTruth[1]; ptPinDelays[3] = pGate3->ptDelays; + + Mio_DeriveGateDelays( ppGates[k], ptPinDelays, nFanins, pMan->nVarsMax, SUPER_NO_VAR, tPinDelaysRes, &tPinDelayMax ); + Mio_DeriveTruthTable( ppGates[k], uTruths, nFanins, pMan->nVarsMax, uTruth ); + if ( !Super_CompareGates( pMan, uTruth, Area, tPinDelaysRes, pMan->nVarsMax ) ) + continue; + // create a new gate + pGateNew = Super_CreateGateNew( pMan, ppGates[k], pSupers, nFanins, uTruth, Area, tPinDelaysRes, tPinDelayMax, pMan->nVarsMax ); + Super_AddGateToTable( pMan, pGateNew ); + } + } + } + } + break; + case 5: // five-input root gate + Super_ManForEachGate( ppGatesLimit, nGatesLimit, i0, pGate0 ) + { + Area0 = AreaMio + pGate0->Area; + if ( Area0 > pMan->tAreaMax ) + break; + pSupers[0] = pGate0; uTruths[0][0] = pGate0->uTruth[0]; uTruths[0][1] = pGate0->uTruth[1]; ptPinDelays[0] = pGate0->ptDelays; + + Super_ManForEachGate( ppGatesLimit, nGatesLimit, i1, pGate1 ) + if ( i1 != i0 ) + { + Area1 = Area0 + pGate1->Area; + if ( Area1 > pMan->tAreaMax ) + break; + pSupers[1] = pGate1; uTruths[1][0] = pGate1->uTruth[0]; uTruths[1][1] = pGate1->uTruth[1]; ptPinDelays[1] = pGate1->ptDelays; + + Super_ManForEachGate( ppGatesLimit, nGatesLimit, i2, pGate2 ) + if ( i2 != i0 && i2 != i1 ) + { + Area2 = Area1 + pGate2->Area; + if ( Area2 > pMan->tAreaMax ) + break; + pSupers[2] = pGate2; uTruths[2][0] = pGate2->uTruth[0]; uTruths[2][1] = pGate2->uTruth[1]; ptPinDelays[2] = pGate2->ptDelays; + + Super_ManForEachGate( ppGatesLimit, nGatesLimit, i3, pGate3 ) + if ( i3 != i0 && i3 != i1 && i3 != i2 ) + { + Area3 = Area2 + pGate3->Area; + if ( Area3 > pMan->tAreaMax ) + break; + pSupers[3] = pGate3; uTruths[3][0] = pGate3->uTruth[0]; uTruths[3][1] = pGate3->uTruth[1]; ptPinDelays[3] = pGate3->ptDelays; + + Super_ManForEachGate( ppGatesLimit, nGatesLimit, i4, pGate4 ) + if ( i4 != i0 && i4 != i1 && i4 != i2 && i4 != i3 ) + { + if ( fTimeOut ) goto done; + fTimeOut = Super_CheckTimeout( pProgress, pMan ); + // compute area + Area = Area3 + pGate4->Area; + if ( Area > pMan->tAreaMax ) + break; + pSupers[4] = pGate4; uTruths[4][0] = pGate4->uTruth[0]; uTruths[4][1] = pGate4->uTruth[1]; ptPinDelays[4] = pGate4->ptDelays; + + Mio_DeriveGateDelays( ppGates[k], ptPinDelays, nFanins, pMan->nVarsMax, SUPER_NO_VAR, tPinDelaysRes, &tPinDelayMax ); + Mio_DeriveTruthTable( ppGates[k], uTruths, nFanins, pMan->nVarsMax, uTruth ); + if ( !Super_CompareGates( pMan, uTruth, Area, tPinDelaysRes, pMan->nVarsMax ) ) + continue; + // create a new gate + pGateNew = Super_CreateGateNew( pMan, ppGates[k], pSupers, nFanins, uTruth, Area, tPinDelaysRes, tPinDelayMax, pMan->nVarsMax ); + Super_AddGateToTable( pMan, pGateNew ); + } + } + } + } + } + break; + case 6: // six-input root gate + Super_ManForEachGate( ppGatesLimit, nGatesLimit, i0, pGate0 ) + { + Area0 = AreaMio + pGate0->Area; + if ( Area0 > pMan->tAreaMax ) + break; + pSupers[0] = pGate0; uTruths[0][0] = pGate0->uTruth[0]; uTruths[0][1] = pGate0->uTruth[1]; ptPinDelays[0] = pGate0->ptDelays; + + Super_ManForEachGate( ppGatesLimit, nGatesLimit, i1, pGate1 ) + if ( i1 != i0 ) + { + Area1 = Area0 + pGate1->Area; + if ( Area1 > pMan->tAreaMax ) + break; + pSupers[1] = pGate1; uTruths[1][0] = pGate1->uTruth[0]; uTruths[1][1] = pGate1->uTruth[1]; ptPinDelays[1] = pGate1->ptDelays; + + Super_ManForEachGate( ppGatesLimit, nGatesLimit, i2, pGate2 ) + if ( i2 != i0 && i2 != i1 ) + { + Area2 = Area1 + pGate2->Area; + if ( Area2 > pMan->tAreaMax ) + break; + pSupers[2] = pGate2; uTruths[2][0] = pGate2->uTruth[0]; uTruths[2][1] = pGate2->uTruth[1]; ptPinDelays[2] = pGate2->ptDelays; + + Super_ManForEachGate( ppGatesLimit, nGatesLimit, i3, pGate3 ) + if ( i3 != i0 && i3 != i1 && i3 != i2 ) + { + Area3 = Area2 + pGate3->Area; + if ( Area3 > pMan->tAreaMax ) + break; + pSupers[3] = pGate3; uTruths[3][0] = pGate3->uTruth[0]; uTruths[3][1] = pGate3->uTruth[1]; ptPinDelays[3] = pGate3->ptDelays; + + Super_ManForEachGate( ppGatesLimit, nGatesLimit, i4, pGate4 ) + if ( i4 != i0 && i4 != i1 && i4 != i2 && i4 != i3 ) + { + if ( fTimeOut ) break; + fTimeOut = Super_CheckTimeout( pProgress, pMan ); + // compute area + Area4 = Area3 + pGate4->Area; + if ( Area > pMan->tAreaMax ) + break; + pSupers[4] = pGate4; uTruths[4][0] = pGate4->uTruth[0]; uTruths[4][1] = pGate4->uTruth[1]; ptPinDelays[4] = pGate4->ptDelays; + + Super_ManForEachGate( ppGatesLimit, nGatesLimit, i5, pGate5 ) + if ( i5 != i0 && i5 != i1 && i5 != i2 && i5 != i3 && i5 != i4 ) + { + if ( fTimeOut ) goto done; + fTimeOut = Super_CheckTimeout( pProgress, pMan ); + // compute area + Area = Area4 + pGate5->Area; + if ( Area > pMan->tAreaMax ) + break; + pSupers[5] = pGate5; uTruths[5][0] = pGate5->uTruth[0]; uTruths[5][1] = pGate5->uTruth[1]; ptPinDelays[5] = pGate5->ptDelays; + + Mio_DeriveGateDelays( ppGates[k], ptPinDelays, nFanins, pMan->nVarsMax, SUPER_NO_VAR, tPinDelaysRes, &tPinDelayMax ); + Mio_DeriveTruthTable( ppGates[k], uTruths, nFanins, pMan->nVarsMax, uTruth ); + if ( !Super_CompareGates( pMan, uTruth, Area, tPinDelaysRes, pMan->nVarsMax ) ) + continue; + // create a new gate + pGateNew = Super_CreateGateNew( pMan, ppGates[k], pSupers, nFanins, uTruth, Area, tPinDelaysRes, tPinDelayMax, pMan->nVarsMax ); + Super_AddGateToTable( pMan, pGateNew ); + } + } + } + } + } + } + break; + default : + assert( 0 ); + break; + } + } +done: + Extra_ProgressBarStop( pProgress ); + free( ppGatesLimit ); + return pMan; +} + +/**Function************************************************************* + + Synopsis [Transfers gates from table into the array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Super_CheckTimeout( ProgressBar * pPro, Super_Man_t * pMan ) +{ + int TimeNow = clock(); + if ( TimeNow > pMan->TimePrint ) + { + Extra_ProgressBarUpdate( pPro, ++pMan->TimeSec, NULL ); + pMan->TimePrint = clock() + CLOCKS_PER_SEC; + } + if ( TimeNow > pMan->TimeStop ) + { + printf ("Timeout!\n"); + return 1; + } + pMan->nTried++; + return 0; +} + + +/**Function************************************************************* + + Synopsis [Transfers gates from table into the array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Super_TranferGatesToArray( Super_Man_t * pMan ) +{ + stmm_generator * gen; + Super_Gate_t * pGate, * pList; + unsigned Key; + + // put the gates fron the table into the array + free( pMan->pGates ); + pMan->pGates = ALLOC( Super_Gate_t *, pMan->nAdded ); + pMan->nGates = 0; + stmm_foreach_item( pMan->tTable, gen, (char **)&Key, (char **)&pList ) + { + for ( pGate = pList; pGate; pGate = pGate->pNext ) + pMan->pGates[ pMan->nGates++ ] = pGate; + } +// assert( pMan->nGates == pMan->nAdded - pMan->nRemoved ); +} + +/**Function************************************************************* + + Synopsis [Adds one supergate into the unique table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Super_AddGateToTable( Super_Man_t * pMan, Super_Gate_t * pGate ) +{ + Super_Gate_t ** ppList; + unsigned Key; +// Key = pGate->uTruth[0] + 2003 * pGate->uTruth[1]; + Key = pGate->uTruth[0] ^ pGate->uTruth[1]; + if ( !stmm_find_or_add( pMan->tTable, (char *)Key, (char ***)&ppList ) ) + *ppList = NULL; + pGate->pNext = *ppList; + *ppList = pGate; + pMan->nAdded++; +} + +/**Function************************************************************* + + Synopsis [Check the manager's unique table for comparable gates.] + + Description [Returns 0 if the gate is dominated by others. Returns 1 + if the gate is new or is better than the available ones. In this case, + cleans the table by removing the gates that are worse than the given one.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Super_CompareGates( Super_Man_t * pMan, unsigned uTruth[], float Area, float tPinDelaysRes[], int nPins ) +{ + Super_Gate_t ** ppList, * pPrev, * pGate, * pGate2; + int i, fNewIsBetter, fGateIsBetter; + unsigned Key; + + // skip constant functions + if ( pMan->nVarsMax < 6 ) + { + if ( uTruth[0] == 0 || ~uTruth[0] == 0 ) + return 0; + } + else + { + if ( ( uTruth[0] == 0 && uTruth[1] == 0 ) || ( ~uTruth[0] == 0 && ~uTruth[1] == 0 ) ) + return 0; + } + + // get hold of the place where the entry is stored +// Key = uTruth[0] + 2003 * uTruth[1]; + Key = uTruth[0] ^ uTruth[1]; + if ( !stmm_find( pMan->tTable, (char *)Key, (char ***)&ppList ) ) + return 1; + // the entry with this truth table is found + pPrev = NULL; + for ( pGate = *ppList, pGate2 = pGate? pGate->pNext: NULL; pGate; + pGate = pGate2, pGate2 = pGate? pGate->pNext: NULL ) + { + pMan->nLookups++; + if ( pGate->uTruth[0] != uTruth[0] || pGate->uTruth[1] != uTruth[1] ) + { + pMan->nAliases++; + continue; + } + fGateIsBetter = 0; + fNewIsBetter = 0; + if ( pGate->Area + SUPER_EPSILON < Area ) + fGateIsBetter = 1; + else if ( pGate->Area > Area + SUPER_EPSILON ) + fNewIsBetter = 1; + for ( i = 0; i < nPins; i++ ) + { + if ( pGate->ptDelays[i] == SUPER_NO_VAR || tPinDelaysRes[i] == SUPER_NO_VAR ) + continue; + if ( pGate->ptDelays[i] + SUPER_EPSILON < tPinDelaysRes[i] ) + fGateIsBetter = 1; + else if ( pGate->ptDelays[i] > tPinDelaysRes[i] + SUPER_EPSILON ) + fNewIsBetter = 1; + if ( fGateIsBetter && fNewIsBetter ) + break; + } + // consider 4 cases + if ( fGateIsBetter && fNewIsBetter ) // Pareto points; save both + pPrev = pGate; + else if ( fNewIsBetter ) // gate is worse; remove the gate + { + if ( pPrev == NULL ) + *ppList = pGate->pNext; + else + pPrev->pNext = pGate->pNext; + Extra_MmFixedEntryRecycle( pMan->pMem, (char *)pGate ); + pMan->nRemoved++; + } + else if ( fGateIsBetter ) // new is worse, already dominated no need to see others + return 0; + else // if ( !fGateIsBetter && !fNewIsBetter ) // they are identical, no need to see others + return 0; + } + return 1; +} + + +/**Function************************************************************* + + Synopsis [Create a new supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Super_Gate_t * Super_CreateGateNew( Super_Man_t * pMan, Mio_Gate_t * pRoot, Super_Gate_t ** pSupers, int nSupers, + unsigned uTruth[], float Area, float tPinDelaysRes[], float tDelayMax, int nPins ) +{ + Super_Gate_t * pSuper; + pSuper = (Super_Gate_t *)Extra_MmFixedEntryFetch( pMan->pMem ); + memset( pSuper, 0, sizeof(Super_Gate_t) ); + pSuper->pRoot = pRoot; + pSuper->uTruth[0] = uTruth[0]; + pSuper->uTruth[1] = uTruth[1]; + memcpy( pSuper->ptDelays, tPinDelaysRes, sizeof(float) * nPins ); + pSuper->Area = Area; + pSuper->nFanins = nSupers; + memcpy( pSuper->pFanins, pSupers, sizeof(Super_Gate_t *) * nSupers ); + pSuper->pNext = NULL; + pSuper->tDelayMax = tDelayMax; + return pSuper; +} + +/**Function************************************************************* + + Synopsis [Starts the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Super_Man_t * Super_ManStart() +{ + Super_Man_t * pMan; + pMan = ALLOC( Super_Man_t, 1 ); + memset( pMan, 0, sizeof(Super_Man_t) ); + pMan->pMem = Extra_MmFixedStart( sizeof(Super_Gate_t) ); + pMan->tTable = stmm_init_table( st_ptrcmp, st_ptrhash ); + return pMan; +} + +/**Function************************************************************* + + Synopsis [Stops the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Super_ManStop( Super_Man_t * pMan ) +{ + Extra_MmFixedStop( pMan->pMem ); + if ( pMan->tTable ) stmm_free_table( pMan->tTable ); + FREE( pMan->pGates ); + free( pMan ); +} + + + + + +/**Function************************************************************* + + Synopsis [Writes the supergate library into the file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Super_Write( Super_Man_t * pMan ) +{ + Super_Gate_t * pGateRoot, * pGate; + stmm_generator * gen; + int fZeroFound, clk, v; + unsigned Key; + + if ( pMan->nGates < 1 ) + { + printf( "The generated library is empty. No output file written.\n" ); + return; + } + + // Filters the supergates by removing those that have fewer inputs than + // the given limit, provided that the inputs are not consequtive. + // For example, NAND2(a,c) is removed, but NAND2(a,b) is left, + // because a and b are consequtive. + FREE( pMan->pGates ); + pMan->pGates = ALLOC( Super_Gate_t *, pMan->nAdded ); + pMan->nGates = 0; + stmm_foreach_item( pMan->tTable, gen, (char **)&Key, (char **)&pGateRoot ) + { + for ( pGate = pGateRoot; pGate; pGate = pGate->pNext ) + { + // skip the elementary variables + if ( pGate->pRoot == NULL ) + continue; + // skip the non-consequtive gates + fZeroFound = 0; + for ( v = 0; v < pMan->nVarsMax; v++ ) + if ( pGate->ptDelays[v] < SUPER_NO_VAR + SUPER_EPSILON ) + fZeroFound = 1; + else if ( fZeroFound ) + break; + if ( v < pMan->nVarsMax ) + continue; + // save the unique gate + pMan->pGates[ pMan->nGates++ ] = pGate; + } + } + +clk = clock(); + // sort the supergates by truth table + qsort( (void *)pMan->pGates, pMan->nGates, sizeof(Super_Gate_t *), + (int (*)(const void *, const void *)) Super_WriteCompare ); + assert( Super_WriteCompare( pMan->pGates, pMan->pGates + pMan->nGates - 1 ) <= 0 ); +if ( pMan->fVerbose ) +{ +PRT( "Sorting", clock() - clk ); +} + + + // write library in the old format +clk = clock(); + if ( pMan->fWriteOldFormat ) + Super_WriteLibrary( pMan ); +if ( pMan->fVerbose ) +{ +PRT( "Writing old format", clock() - clk ); +} + + // write the tree-like structure of supergates +clk = clock(); + Super_WriteLibraryTree( pMan ); +if ( pMan->fVerbose ) +{ +PRT( "Writing new format", clock() - clk ); +} +} + + +/**Function************************************************************* + + Synopsis [Writes the file header.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Super_WriteFileHeader( Super_Man_t * pMan, FILE * pFile ) +{ + fprintf( pFile, "#\n" ); + fprintf( pFile, "# Supergate library derived for \"%s\" on %s.\n", pMan->pName, Extra_TimeStamp() ); + fprintf( pFile, "#\n" ); + fprintf( pFile, "# Command line: \"super -i %d -l %d -d %.2f -a %.2f -t %d %s %s\".\n", + pMan->nVarsMax, pMan->nLevels, pMan->tDelayMax, pMan->tAreaMax, pMan->TimeLimit, (pMan->fSkipInv? "" : "-s"), pMan->pName ); + fprintf( pFile, "#\n" ); + fprintf( pFile, "# The number of inputs = %10d.\n", pMan->nVarsMax ); + fprintf( pFile, "# The number of levels = %10d.\n", pMan->nLevels ); + fprintf( pFile, "# The maximum delay = %10.2f.\n", pMan->tDelayMax ); + fprintf( pFile, "# The maximum area = %10.2f.\n", pMan->tAreaMax ); + fprintf( pFile, "# The maximum runtime (sec) = %10d.\n", pMan->TimeLimit ); + fprintf( pFile, "#\n" ); + fprintf( pFile, "# The number of attempts = %10d.\n", pMan->nTried ); + fprintf( pFile, "# The number of supergates = %10d.\n", pMan->nGates ); + fprintf( pFile, "# The number of functions = %10d.\n", pMan->nUnique ); + fprintf( pFile, "# The total functions = %.0f (2^%d).\n", pow(2,pMan->nMints), pMan->nMints ); + fprintf( pFile, "#\n" ); + fprintf( pFile, "# Generation time (sec) = %10.2f.\n", (float)(pMan->Time)/(float)(CLOCKS_PER_SEC) ); + fprintf( pFile, "#\n" ); + fprintf( pFile, "%s\n", pMan->pName ); + fprintf( pFile, "%d\n", pMan->nVarsMax ); + fprintf( pFile, "%d\n", pMan->nGates ); +} + +/**Function************************************************************* + + Synopsis [Compares the truth tables of two gates.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Super_WriteCompare( Super_Gate_t ** ppG1, Super_Gate_t ** ppG2 ) +{ + unsigned * pTruth1 = (*ppG1)->uTruth; + unsigned * pTruth2 = (*ppG2)->uTruth; + if ( pTruth1[1] < pTruth2[1] ) + return -1; + if ( pTruth1[1] > pTruth2[1] ) + return 1; + if ( pTruth1[0] < pTruth2[0] ) + return -1; + if ( pTruth1[0] > pTruth2[0] ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Compares the max delay of two gates.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Super_DelayCompare( Super_Gate_t ** ppG1, Super_Gate_t ** ppG2 ) +{ + if ( (*ppG1)->tDelayMax < (*ppG2)->tDelayMax ) + return -1; + if ( (*ppG1)->tDelayMax > (*ppG2)->tDelayMax ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Compares the area of two gates.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Super_AreaCompare( Super_Gate_t ** ppG1, Super_Gate_t ** ppG2 ) +{ + if ( (*ppG1)->Area < (*ppG2)->Area ) + return -1; + if ( (*ppG1)->Area > (*ppG2)->Area ) + return 1; + return 0; +} + + + + + + +/**Function************************************************************* + + Synopsis [Writes the gates into the file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Super_WriteLibrary( Super_Man_t * pMan ) +{ + Super_Gate_t * pGate, * pGateNext; + FILE * pFile; + char FileName[100]; + char * pNameGeneric; + int i, Counter; + + // get the file name + pNameGeneric = Extra_FileNameGeneric( pMan->pName ); + sprintf( FileName, "%s.super_old", pNameGeneric ); + free( pNameGeneric ); + + // count the number of unique functions + pMan->nUnique = 1; + Super_ManForEachGate( pMan->pGates, pMan->nGates, i, pGate ) + { + if ( i == pMan->nGates - 1 ) + break; + // print the newline if this gate is different from the following one + pGateNext = pMan->pGates[i+1]; + if ( pGateNext->uTruth[0] != pGate->uTruth[0] || pGateNext->uTruth[1] != pGate->uTruth[1] ) + pMan->nUnique++; + } + + // start the file + pFile = fopen( FileName, "w" ); + Super_WriteFileHeader( pMan, pFile ); + + // print the gates + Counter = 0; + Super_ManForEachGate( pMan->pGates, pMan->nGates, i, pGate ) + { + Super_WriteLibraryGate( pFile, pMan, pGate, ++Counter ); + if ( i == pMan->nGates - 1 ) + break; + // print the newline if this gate is different from the following one + pGateNext = pMan->pGates[i+1]; + if ( pGateNext->uTruth[0] != pGate->uTruth[0] || pGateNext->uTruth[1] != pGate->uTruth[1] ) + fprintf( pFile, "\n" ); + } + assert( Counter == pMan->nGates ); + fclose( pFile ); + +if ( pMan->fVerbose ) +{ + printf( "The supergates are written using old format \"%s\" ", FileName ); + printf( "(%0.3f Mb).\n", ((double)Extra_FileSize(FileName))/(1<<20) ); +} +} + +/**Function************************************************************* + + Synopsis [Writes the supergate into the file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Super_WriteLibraryGate( FILE * pFile, Super_Man_t * pMan, Super_Gate_t * pGate, int Num ) +{ + int i; + fprintf( pFile, "%04d ", Num ); // the number + Extra_PrintBinary( pFile, pGate->uTruth, pMan->nMints ); // the truth table + fprintf( pFile, " %5.2f", pGate->tDelayMax ); // the max delay + fprintf( pFile, " " ); + for ( i = 0; i < pMan->nVarsMax; i++ ) // the pin-to-pin delays + fprintf( pFile, " %5.2f", pGate->ptDelays[i]==SUPER_NO_VAR? 0.0 : pGate->ptDelays[i] ); + fprintf( pFile, " %5.2f", pGate->Area ); // the area + fprintf( pFile, " " ); + fprintf( pFile, "%s", Super_WriteLibraryGateName(pGate) ); // the symbolic expression + fprintf( pFile, "\n" ); +} + +/**Function************************************************************* + + Synopsis [Recursively generates symbolic name of the supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Super_WriteLibraryGateName( Super_Gate_t * pGate ) +{ + static char Buffer[2000]; + Buffer[0] = 0; + Super_WriteLibraryGateName_rec( pGate, Buffer ); + return Buffer; +} + +/**Function************************************************************* + + Synopsis [Recursively generates symbolic name of the supergate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Super_WriteLibraryGateName_rec( Super_Gate_t * pGate, char * pBuffer ) +{ + char Buffer[10]; + int i; + + if ( pGate->pRoot == NULL ) + { + sprintf( Buffer, "%c", 'a' + pGate->Number ); + strcat( pBuffer, Buffer ); + return; + } + strcat( pBuffer, Mio_GateReadName(pGate->pRoot) ); + strcat( pBuffer, "(" ); + for ( i = 0; i < (int)pGate->nFanins; i++ ) + { + if ( i ) + strcat( pBuffer, "," ); + Super_WriteLibraryGateName_rec( pGate->pFanins[i], pBuffer ); + } + strcat( pBuffer, ")" ); +} + + + + + +/**Function************************************************************* + + Synopsis [Recursively writes the gates.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Super_WriteLibraryTree( Super_Man_t * pMan ) +{ + Super_Gate_t * pSuper; + FILE * pFile; + char FileName[100]; + char * pNameGeneric; + int i, Counter; + int posStart; + + // get the file name + pNameGeneric = Extra_FileNameGeneric( pMan->pName ); + sprintf( FileName, "%s.super", pNameGeneric ); + free( pNameGeneric ); + + // write the elementary variables + pFile = fopen( FileName, "w" ); + Super_WriteFileHeader( pMan, pFile ); + // write the place holder for the number of lines + posStart = ftell( pFile ); + fprintf( pFile, " \n" ); + // mark the real supergates + Super_ManForEachGate( pMan->pGates, pMan->nGates, i, pSuper ) + pSuper->fSuper = 1; + // write the supergates + Counter = pMan->nVarsMax; + Super_ManForEachGate( pMan->pGates, pMan->nGates, i, pSuper ) + Super_WriteLibraryTree_rec( pFile, pMan, pSuper, &Counter ); + fclose( pFile ); + // write the number of lines + pFile = fopen( FileName, "rb+" ); + fseek( pFile, posStart, SEEK_SET ); + fprintf( pFile, "%d", Counter ); + fclose( pFile ); + +if ( pMan->fVerbose ) +{ + printf( "The supergates are written using new format \"%s\" ", FileName ); + printf( "(%0.3f Mb).\n", ((double)Extra_FileSize(FileName))/(1<<20) ); +} +} + +/**Function************************************************************* + + Synopsis [Recursively writes the gate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Super_WriteLibraryTree_rec( FILE * pFile, Super_Man_t * pMan, Super_Gate_t * pSuper, int * pCounter ) +{ + int nFanins, i; + // skip an elementary variable and a gate that was already written + if ( pSuper->fVar || pSuper->Number > 0 ) + return; + // write the fanins + nFanins = Mio_GateReadInputs(pSuper->pRoot); + for ( i = 0; i < nFanins; i++ ) + Super_WriteLibraryTree_rec( pFile, pMan, pSuper->pFanins[i], pCounter ); + // finally write the gate + pSuper->Number = (*pCounter)++; + fprintf( pFile, "%s", pSuper->fSuper? "* " : "" ); + fprintf( pFile, "%s", Mio_GateReadName(pSuper->pRoot) ); + for ( i = 0; i < nFanins; i++ ) + fprintf( pFile, " %d", pSuper->pFanins[i]->Number ); + // write the formula + // this step is optional, the resulting library will work in any case + // however, it may be helpful to for debugging to compare the same library + // written in the old format and written in the new format with formulas +// fprintf( pFile, " # %s", Super_WriteLibraryGateName( pSuper ) ); + fprintf( pFile, "\n" ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/map/super/superInt.h b/abc_with_bb_support/src/map/super/superInt.h new file mode 100644 index 000000000..198ea8568 --- /dev/null +++ b/abc_with_bb_support/src/map/super/superInt.h @@ -0,0 +1,62 @@ +/**CFile**************************************************************** + + FileName [superInt.h] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Pre-computation of supergates.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 8, 2003.] + + Revision [$Id: superInt.h,v 1.4 2004/07/06 04:55:59 alanmi Exp $] + +***********************************************************************/ + +#ifndef __super_INT_H__ +#define __super_INT_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include "abc.h" +#include "mainInt.h" +#include "mvc.h" +#include "mio.h" +#include "stmm.h" +#include "super.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// GLOBAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== superAnd.c =============================================================*/ +extern void Super2_Precompute( int nInputs, int nLevels, int fVerbose ); +/*=== superGate.c =============================================================*/ +extern void Super_Precompute( Mio_Library_t * pLibGen, int nInputs, int nLevels, float tDelayMax, float tAreaMax, int TimeLimit, bool fSkipInv, bool fWriteOldFormat, int fVerbose ); + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/map/super/superWrite.c b/abc_with_bb_support/src/map/super/superWrite.c new file mode 100644 index 000000000..b5b9dff08 --- /dev/null +++ b/abc_with_bb_support/src/map/super/superWrite.c @@ -0,0 +1,76 @@ +/**CFile**************************************************************** + + FileName [superWrite.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Pre-computation of supergates.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 8, 2003.] + + Revision [$Id: superWrite.c,v 1.1 2004/04/03 01:36:45 alanmi Exp $] + +***********************************************************************/ + +#include "superInt.h" + +/* + One record in the supergate library file consists of: + + + + is a zero-based integer + is a string of 2^n bits representing the value of the function for each minterm + is the maximum delay of the gate + is the array of n double values + is a floating point value + is the string representing the gate in the following format: + GATENAME1( GATENAME2( a, c ), GATENAME3( a, d ), ... ) + The gate names (GATENAME1, etc) are the names as they appear in the .genlib library. + The primary inputs of the gates are denoted by lowercase chars 'a', 'b', etc. + The parantheses are mandatory for each gate, except for the wire. + The wire name can be omitted, so that "a" can be used instead of "**wire**( a )". + The spaces are optional in any position of this string. + + + The supergates are generated exhaustively from all gate combinations that + have the max delay lower than the delay given by the user, or until the specified time + limit is reached. + + The supergates are stored in supergate classes by their functionality. + Among the gates with the equivalent functionaly only those are dropped, which are + dominated by at least one other gate in the class in terms of both delay and area. + For the definition of gate dominance see pliGenCheckDominance(). +*/ + + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/espresso/cofactor.c b/abc_with_bb_support/src/misc/espresso/cofactor.c new file mode 100644 index 000000000..306de662c --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/cofactor.c @@ -0,0 +1,382 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +#include "espresso.h" + +/* + The cofactor of a cover against a cube "c" is a cover formed by the + cofactor of each cube in the cover against c. The cofactor of two + cubes is null if they are distance 1 or more apart. If they are + distance zero apart, the cofactor is the restriction of the cube + to the minterms of c. + + The cube list contains the following information: + + T[0] = pointer to a cube identifying the variables that have + been cofactored against + T[1] = pointer to just beyond the sentinel (i.e., T[n] in this case) + T[2] + . + . = pointers to cubes + . + T[n-2] + T[n-1] = NULL pointer (sentinel) + + + Cofactoring involves repeated application of "cdist0" to check if a + cube of the cover intersects the cofactored cube. This can be + slow, especially for the recursive descent of the espresso + routines. Therefore, a special cofactor routine "scofactor" is + provided which assumes the cofactor is only in a single variable. +*/ + + +/* cofactor -- compute the cofactor of a cover with respect to a cube */ +pcube *cofactor(T, c) +IN pcube *T; +IN register pcube c; +{ + pcube temp = cube.temp[0], *Tc_save, *Tc, *T1; + register pcube p; + int listlen; + + listlen = CUBELISTSIZE(T) + 5; + + /* Allocate a new list of cube pointers (max size is previous size) */ + Tc_save = Tc = ALLOC(pcube, listlen); + + /* pass on which variables have been cofactored against */ + *Tc++ = set_or(new_cube(), T[0], set_diff(temp, cube.fullset, c)); + Tc++; + + /* Loop for each cube in the list, determine suitability, and save */ + for(T1 = T+2; (p = *T1++) != NULL; ) { + if (p != c) { + +#ifdef NO_INLINE + if (! cdist0(p, c)) goto false; +#else + {register int w,last;register unsigned int x;if((last=cube.inword)!=-1) + {x=p[last]&c[last];if(~(x|x>>1)&cube.inmask)goto false;for(w=1;w>1)&DISJOINT)goto false;}}}{register int w,var,last; + register pcube mask;for(var=cube.num_binary_vars;var= 0; i--) + count[i] = 0; + } + + /* Count the number of zeros in each column */ + { register int i, *cnt; + register unsigned int val; + register pcube p, cof = T[0], full = cube.fullset; + for(T1 = T+2; (p = *T1++) != NULL; ) + for(i = LOOP(p); i > 0; i--) + if (val = full[i] & ~ (p[i] | cof[i])) { + cnt = count + ((i-1) << LOGBPI); +#if BPI == 32 + if (val & 0xFF000000) { + if (val & 0x80000000) cnt[31]++; + if (val & 0x40000000) cnt[30]++; + if (val & 0x20000000) cnt[29]++; + if (val & 0x10000000) cnt[28]++; + if (val & 0x08000000) cnt[27]++; + if (val & 0x04000000) cnt[26]++; + if (val & 0x02000000) cnt[25]++; + if (val & 0x01000000) cnt[24]++; + } + if (val & 0x00FF0000) { + if (val & 0x00800000) cnt[23]++; + if (val & 0x00400000) cnt[22]++; + if (val & 0x00200000) cnt[21]++; + if (val & 0x00100000) cnt[20]++; + if (val & 0x00080000) cnt[19]++; + if (val & 0x00040000) cnt[18]++; + if (val & 0x00020000) cnt[17]++; + if (val & 0x00010000) cnt[16]++; + } +#endif + if (val & 0xFF00) { + if (val & 0x8000) cnt[15]++; + if (val & 0x4000) cnt[14]++; + if (val & 0x2000) cnt[13]++; + if (val & 0x1000) cnt[12]++; + if (val & 0x0800) cnt[11]++; + if (val & 0x0400) cnt[10]++; + if (val & 0x0200) cnt[ 9]++; + if (val & 0x0100) cnt[ 8]++; + } + if (val & 0x00FF) { + if (val & 0x0080) cnt[ 7]++; + if (val & 0x0040) cnt[ 6]++; + if (val & 0x0020) cnt[ 5]++; + if (val & 0x0010) cnt[ 4]++; + if (val & 0x0008) cnt[ 3]++; + if (val & 0x0004) cnt[ 2]++; + if (val & 0x0002) cnt[ 1]++; + if (val & 0x0001) cnt[ 0]++; + } + } + } + + /* + * Perform counts for each variable: + * cdata.var_zeros[var] = number of zeros in the variable + * cdata.parts_active[var] = number of active parts for each variable + * cdata.vars_active = number of variables which are active + * cdata.vars_unate = number of variables which are active and unate + * + * best -- the variable which is best for splitting based on: + * mostactive -- most # active parts in any variable + * mostzero -- most # zeros in any variable + * mostbalanced -- minimum over the maximum # zeros / part / variable + */ + + { register int var, i, lastbit, active, maxactive; + int best = -1, mostactive = 0, mostzero = 0, mostbalanced = 32000; + cdata.vars_unate = cdata.vars_active = 0; + + for(var = 0; var < cube.num_vars; var++) { + if (var < cube.num_binary_vars) { /* special hack for binary vars */ + i = count[var*2]; + lastbit = count[var*2 + 1]; + active = (i > 0) + (lastbit > 0); + cdata.var_zeros[var] = i + lastbit; + maxactive = MAX(i, lastbit); + } else { + maxactive = active = cdata.var_zeros[var] = 0; + lastbit = cube.last_part[var]; + for(i = cube.first_part[var]; i <= lastbit; i++) { + cdata.var_zeros[var] += count[i]; + active += (count[i] > 0); + if (active > maxactive) maxactive = active; + } + } + + /* first priority is to maximize the number of active parts */ + /* for binary case, this will usually select the output first */ + if (active > mostactive) + best = var, mostactive = active, mostzero = cdata.var_zeros[best], + mostbalanced = maxactive; + else if (active == mostactive) + /* secondary condition is to maximize the number zeros */ + /* for binary variables, this is the same as minimum # of 2's */ + if (cdata.var_zeros[var] > mostzero) + best = var, mostzero = cdata.var_zeros[best], + mostbalanced = maxactive; + else if (cdata.var_zeros[var] == mostzero) + /* third condition is to pick a balanced variable */ + /* for binary vars, this means roughly equal # 0's and 1's */ + if (maxactive < mostbalanced) + best = var, mostbalanced = maxactive; + + cdata.parts_active[var] = active; + cdata.is_unate[var] = (active == 1); + cdata.vars_active += (active > 0); + cdata.vars_unate += (active == 1); + } + cdata.best = best; + } +} + +int binate_split_select(T, cleft, cright, debug_flag) +IN pcube *T; +IN register pcube cleft, cright; +IN int debug_flag; +{ + int best = cdata.best; + register int i, lastbit = cube.last_part[best], halfbit = 0; + register pcube cof=T[0]; + + /* Create the cubes to cofactor against */ + (void) set_diff(cleft, cube.fullset, cube.var_mask[best]); + (void) set_diff(cright, cube.fullset, cube.var_mask[best]); + for(i = cube.first_part[best]; i <= lastbit; i++) + if (! is_in_set(cof,i)) + halfbit++; + for(i = cube.first_part[best], halfbit = halfbit/2; halfbit > 0; i++) + if (! is_in_set(cof,i)) + halfbit--, set_insert(cleft, i); + for(; i <= lastbit; i++) + if (! is_in_set(cof,i)) + set_insert(cright, i); + + if (debug & debug_flag) { + (void) printf("BINATE_SPLIT_SELECT: split against %d\n", best); + if (verbose_debug) + (void) printf("cl=%s\ncr=%s\n", pc1(cleft), pc2(cright)); + } + return best; +} + + +pcube *cube1list(A) +pcover A; +{ + register pcube last, p, *plist, *list; + + list = plist = ALLOC(pcube, A->count + 3); + *plist++ = new_cube(); + plist++; + foreach_set(A, last, p) { + *plist++ = p; + } + *plist++ = NULL; /* sentinel */ + list[1] = (pcube) plist; + return list; +} + + +pcube *cube2list(A, B) +pcover A, B; +{ + register pcube last, p, *plist, *list; + + list = plist = ALLOC(pcube, A->count + B->count + 3); + *plist++ = new_cube(); + plist++; + foreach_set(A, last, p) { + *plist++ = p; + } + foreach_set(B, last, p) { + *plist++ = p; + } + *plist++ = NULL; + list[1] = (pcube) plist; + return list; +} + + +pcube *cube3list(A, B, C) +pcover A, B, C; +{ + register pcube last, p, *plist, *list; + + plist = ALLOC(pcube, A->count + B->count + C->count + 3); + list = plist; + *plist++ = new_cube(); + plist++; + foreach_set(A, last, p) { + *plist++ = p; + } + foreach_set(B, last, p) { + *plist++ = p; + } + foreach_set(C, last, p) { + *plist++ = p; + } + *plist++ = NULL; + list[1] = (pcube) plist; + return list; +} + + +pcover cubeunlist(A1) +pcube *A1; +{ + register int i; + register pcube p, pdest, cof = A1[0]; + register pcover A; + + A = new_cover(CUBELISTSIZE(A1)); + for(i = 2; (p = A1[i]) != NULL; i++) { + pdest = GETSET(A, i-2); + INLINEset_or(pdest, p, cof); + } + A->count = CUBELISTSIZE(A1); + return A; +} + +simplify_cubelist(T) +pcube *T; +{ + register pcube *Tdest; + register int i, ncubes; + + (void) set_copy(cube.temp[0], T[0]); /* retrieve cofactor */ + + ncubes = CUBELISTSIZE(T); + qsort((char *) (T+2), ncubes, sizeof(pset), (int (*)()) d1_order); + + Tdest = T+2; + /* *Tdest++ = T[2]; */ + for(i = 3; i < ncubes; i++) { + if (d1_order(&T[i-1], &T[i]) != 0) { + *Tdest++ = T[i]; + } + } + + *Tdest++ = NULL; /* sentinel */ + Tdest[1] = (pcube) Tdest; /* save pointer to last */ +} diff --git a/abc_with_bb_support/src/misc/espresso/cols.c b/abc_with_bb_support/src/misc/espresso/cols.c new file mode 100644 index 000000000..4cdf7c38e --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/cols.c @@ -0,0 +1,314 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +//#include "port.h" +#include "sparse_int.h" + + +/* + * allocate a new col vector + */ +sm_col * +sm_col_alloc() +{ + register sm_col *pcol; + +#ifdef FAST_AND_LOOSE + if (sm_col_freelist == NIL(sm_col)) { + pcol = ALLOC(sm_col, 1); + } else { + pcol = sm_col_freelist; + sm_col_freelist = pcol->next_col; + } +#else + pcol = ALLOC(sm_col, 1); +#endif + + pcol->col_num = 0; + pcol->length = 0; + pcol->first_row = pcol->last_row = NIL(sm_element); + pcol->next_col = pcol->prev_col = NIL(sm_col); + pcol->flag = 0; + pcol->user_word = NIL(char); /* for our user ... */ + return pcol; +} + + +/* + * free a col vector -- for FAST_AND_LOOSE, this is real cheap for cols; + * however, freeing a rowumn must still walk down the rowumn discarding + * the elements one-by-one; that is the only use for the extra '-DCOLS' + * compile flag ... + */ +void +sm_col_free(pcol) +register sm_col *pcol; +{ +#if defined(FAST_AND_LOOSE) && ! defined(COLS) + if (pcol->first_row != NIL(sm_element)) { + /* Add the linked list of col items to the free list */ + pcol->last_row->next_row = sm_element_freelist; + sm_element_freelist = pcol->first_row; + } + + /* Add the col to the free list of cols */ + pcol->next_col = sm_col_freelist; + sm_col_freelist = pcol; +#else + register sm_element *p, *pnext; + + for(p = pcol->first_row; p != 0; p = pnext) { + pnext = p->next_row; + sm_element_free(p); + } + FREE(pcol); +#endif +} + + +/* + * duplicate an existing col + */ +sm_col * +sm_col_dup(pcol) +register sm_col *pcol; +{ + register sm_col *pnew; + register sm_element *p; + + pnew = sm_col_alloc(); + for(p = pcol->first_row; p != 0; p = p->next_row) { + (void) sm_col_insert(pnew, p->row_num); + } + return pnew; +} + + +/* + * insert an element into a col vector + */ +sm_element * +sm_col_insert(pcol, row) +register sm_col *pcol; +register int row; +{ + register sm_element *test, *element; + + /* get a new item, save its address */ + sm_element_alloc(element); + test = element; + sorted_insert(sm_element, pcol->first_row, pcol->last_row, pcol->length, + next_row, prev_row, row_num, row, test); + + /* if item was not used, free it */ + if (element != test) { + sm_element_free(element); + } + + /* either way, return the current new value */ + return test; +} + + +/* + * remove an element from a col vector + */ +void +sm_col_remove(pcol, row) +register sm_col *pcol; +register int row; +{ + register sm_element *p; + + for(p = pcol->first_row; p != 0 && p->row_num < row; p = p->next_row) + ; + if (p != 0 && p->row_num == row) { + dll_unlink(p, pcol->first_row, pcol->last_row, + next_row, prev_row, pcol->length); + sm_element_free(p); + } +} + + +/* + * find an element (if it is in the col vector) + */ +sm_element * +sm_col_find(pcol, row) +sm_col *pcol; +int row; +{ + register sm_element *p; + + for(p = pcol->first_row; p != 0 && p->row_num < row; p = p->next_row) + ; + if (p != 0 && p->row_num == row) { + return p; + } else { + return NIL(sm_element); + } +} + +/* + * return 1 if col p2 contains col p1; 0 otherwise + */ +int +sm_col_contains(p1, p2) +sm_col *p1, *p2; +{ + register sm_element *q1, *q2; + + q1 = p1->first_row; + q2 = p2->first_row; + while (q1 != 0) { + if (q2 == 0 || q1->row_num < q2->row_num) { + return 0; + } else if (q1->row_num == q2->row_num) { + q1 = q1->next_row; + q2 = q2->next_row; + } else { + q2 = q2->next_row; + } + } + return 1; +} + + +/* + * return 1 if col p1 and col p2 share an element in common + */ +int +sm_col_intersects(p1, p2) +sm_col *p1, *p2; +{ + register sm_element *q1, *q2; + + q1 = p1->first_row; + q2 = p2->first_row; + if (q1 == 0 || q2 == 0) return 0; + for(;;) { + if (q1->row_num < q2->row_num) { + if ((q1 = q1->next_row) == 0) { + return 0; + } + } else if (q1->row_num > q2->row_num) { + if ((q2 = q2->next_row) == 0) { + return 0; + } + } else { + return 1; + } + } +} + + +/* + * compare two cols, lexical ordering + */ +int +sm_col_compare(p1, p2) +sm_col *p1, *p2; +{ + register sm_element *q1, *q2; + + q1 = p1->first_row; + q2 = p2->first_row; + while(q1 != 0 && q2 != 0) { + if (q1->row_num != q2->row_num) { + return q1->row_num - q2->row_num; + } + q1 = q1->next_row; + q2 = q2->next_row; + } + + if (q1 != 0) { + return 1; + } else if (q2 != 0) { + return -1; + } else { + return 0; + } +} + + +/* + * return the intersection + */ +sm_col * +sm_col_and(p1, p2) +sm_col *p1, *p2; +{ + register sm_element *q1, *q2; + register sm_col *result; + + result = sm_col_alloc(); + q1 = p1->first_row; + q2 = p2->first_row; + if (q1 == 0 || q2 == 0) return result; + for(;;) { + if (q1->row_num < q2->row_num) { + if ((q1 = q1->next_row) == 0) { + return result; + } + } else if (q1->row_num > q2->row_num) { + if ((q2 = q2->next_row) == 0) { + return result; + } + } else { + (void) sm_col_insert(result, q1->row_num); + if ((q1 = q1->next_row) == 0) { + return result; + } + if ((q2 = q2->next_row) == 0) { + return result; + } + } + } +} + +int +sm_col_hash(pcol, modulus) +sm_col *pcol; +int modulus; +{ + register int sum; + register sm_element *p; + + sum = 0; + for(p = pcol->first_row; p != 0; p = p->next_row) { + sum = (sum*17 + p->row_num) % modulus; + } + return sum; +} + +/* + * remove an element from a col vector (given a pointer to the element) + */ +void +sm_col_remove_element(pcol, p) +register sm_col *pcol; +register sm_element *p; +{ + dll_unlink(p, pcol->first_row, pcol->last_row, + next_row, prev_row, pcol->length); + sm_element_free(p); +} + + +void +sm_col_print(fp, pcol) +FILE *fp; +sm_col *pcol; +{ + sm_element *p; + + for(p = pcol->first_row; p != 0; p = p->next_row) { + (void) fprintf(fp, " %d", p->row_num); + } +} diff --git a/abc_with_bb_support/src/misc/espresso/compl.c b/abc_with_bb_support/src/misc/espresso/compl.c new file mode 100644 index 000000000..d2fa3e1b7 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/compl.c @@ -0,0 +1,680 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +/* + * module: compl.c + * purpose: compute the complement of a multiple-valued function + * + * The "unate recursive paradigm" is used. After a set of special + * cases are examined, the function is split on the "most active + * variable". These two halves are complemented recursively, and then + * the results are merged. + * + * Changes (from Version 2.1 to Version 2.2) + * 1. Minor bug in compl_lifting -- cubes in the left half were + * not marked as active, so that when merging a leaf from the left + * hand side, the active flags were essentially random. This led + * to minor impredictability problem, but never affected the + * accuracy of the results. + */ + +#include "espresso.h" + +#define USE_COMPL_LIFT 0 +#define USE_COMPL_LIFT_ONSET 1 +#define USE_COMPL_LIFT_ONSET_COMPLEX 2 +#define NO_LIFTING 3 + +static bool compl_special_cases(); +static pcover compl_merge(); +static void compl_d1merge(); +static pcover compl_cube(); +static void compl_lift(); +static void compl_lift_onset(); +static void compl_lift_onset_complex(); +static bool simp_comp_special_cases(); +static bool simplify_special_cases(); + + +/* complement -- compute the complement of T */ +pcover complement(T) +pcube *T; /* T will be disposed of */ +{ + register pcube cl, cr; + register int best; + pcover Tbar, Tl, Tr; + int lifting; + static int compl_level = 0; + + if (debug & COMPL) + debug_print(T, "COMPLEMENT", compl_level++); + + if (compl_special_cases(T, &Tbar) == MAYBE) { + + /* Allocate space for the partition cubes */ + cl = new_cube(); + cr = new_cube(); + best = binate_split_select(T, cl, cr, COMPL); + + /* Complement the left and right halves */ + Tl = complement(scofactor(T, cl, best)); + Tr = complement(scofactor(T, cr, best)); + + if (Tr->count*Tl->count > (Tr->count+Tl->count)*CUBELISTSIZE(T)) { + lifting = USE_COMPL_LIFT_ONSET; + } else { + lifting = USE_COMPL_LIFT; + } + Tbar = compl_merge(T, Tl, Tr, cl, cr, best, lifting); + + free_cube(cl); + free_cube(cr); + free_cubelist(T); + } + + if (debug & COMPL) + debug1_print(Tbar, "exit COMPLEMENT", --compl_level); + return Tbar; +} + +static bool compl_special_cases(T, Tbar) +pcube *T; /* will be disposed if answer is determined */ +pcover *Tbar; /* returned only if answer determined */ +{ + register pcube *T1, p, ceil, cof=T[0]; + pcover A, ceil_compl; + + /* Check for no cubes in the cover */ + if (T[2] == NULL) { + *Tbar = sf_addset(new_cover(1), cube.fullset); + free_cubelist(T); + return TRUE; + } + + /* Check for only a single cube in the cover */ + if (T[3] == NULL) { + *Tbar = compl_cube(set_or(cof, cof, T[2])); + free_cubelist(T); + return TRUE; + } + + /* Check for a row of all 1's (implies complement is null) */ + for(T1 = T+2; (p = *T1++) != NULL; ) { + if (full_row(p, cof)) { + *Tbar = new_cover(0); + free_cubelist(T); + return TRUE; + } + } + + /* Check for a column of all 0's which can be factored out */ + ceil = set_save(cof); + for(T1 = T+2; (p = *T1++) != NULL; ) { + INLINEset_or(ceil, ceil, p); + } + if (! setp_equal(ceil, cube.fullset)) { + ceil_compl = compl_cube(ceil); + (void) set_or(cof, cof, set_diff(ceil, cube.fullset, ceil)); + set_free(ceil); + *Tbar = sf_append(complement(T), ceil_compl); + return TRUE; + } + set_free(ceil); + + /* Collect column counts, determine unate variables, etc. */ + massive_count(T); + + /* If single active variable not factored out above, then tautology ! */ + if (cdata.vars_active == 1) { + *Tbar = new_cover(0); + free_cubelist(T); + return TRUE; + + /* Check for unate cover */ + } else if (cdata.vars_unate == cdata.vars_active) { + A = map_cover_to_unate(T); + free_cubelist(T); + A = unate_compl(A); + *Tbar = map_unate_to_cover(A); + sf_free(A); + return TRUE; + + /* Not much we can do about it */ + } else { + return MAYBE; + } +} + +/* + * compl_merge -- merge the two cofactors around the splitting + * variable + * + * The merge operation involves intersecting each cube of the left + * cofactor with cl, and intersecting each cube of the right cofactor + * with cr. The union of these two covers is the merged result. + * + * In order to reduce the number of cubes, a distance-1 merge is + * performed (note that two cubes can only combine distance-1 in the + * splitting variable). Also, a simple expand is performed in the + * splitting variable (simple implies the covering check for the + * expansion is not full containment, but single-cube containment). + */ + +static pcover compl_merge(T1, L, R, cl, cr, var, lifting) +pcube *T1; /* Original ON-set */ +pcover L, R; /* Complement from each recursion branch */ +register pcube cl, cr; /* cubes used for cofactoring */ +int var; /* splitting variable */ +int lifting; /* whether to perform lifting or not */ +{ + register pcube p, last, pt; + pcover T, Tbar; + pcube *L1, *R1; + + if (debug & COMPL) { + (void) printf("compl_merge: left %d, right %d\n", L->count, R->count); + (void) printf("%s (cl)\n%s (cr)\nLeft is\n", pc1(cl), pc2(cr)); + cprint(L); + (void) printf("Right is\n"); + cprint(R); + } + + /* Intersect each cube with the cofactored cube */ + foreach_set(L, last, p) { + INLINEset_and(p, p, cl); + SET(p, ACTIVE); + } + foreach_set(R, last, p) { + INLINEset_and(p, p, cr); + SET(p, ACTIVE); + } + + /* Sort the arrays for a distance-1 merge */ + (void) set_copy(cube.temp[0], cube.var_mask[var]); + qsort((char *) (L1 = sf_list(L)), L->count, sizeof(pset), (int (*)()) d1_order); + qsort((char *) (R1 = sf_list(R)), R->count, sizeof(pset), (int (*)()) d1_order); + + /* Perform distance-1 merge */ + compl_d1merge(L1, R1); + + /* Perform lifting */ + switch(lifting) { + case USE_COMPL_LIFT_ONSET: + T = cubeunlist(T1); + compl_lift_onset(L1, T, cr, var); + compl_lift_onset(R1, T, cl, var); + free_cover(T); + break; + case USE_COMPL_LIFT_ONSET_COMPLEX: + T = cubeunlist(T1); + compl_lift_onset_complex(L1, T, var); + compl_lift_onset_complex(R1, T, var); + free_cover(T); + break; + case USE_COMPL_LIFT: + compl_lift(L1, R1, cr, var); + compl_lift(R1, L1, cl, var); + break; + case NO_LIFTING: + break; + default: + ; + } + FREE(L1); + FREE(R1); + + /* Re-create the merged cover */ + Tbar = new_cover(L->count + R->count); + pt = Tbar->data; + foreach_set(L, last, p) { + INLINEset_copy(pt, p); + Tbar->count++; + pt += Tbar->wsize; + } + foreach_active_set(R, last, p) { + INLINEset_copy(pt, p); + Tbar->count++; + pt += Tbar->wsize; + } + + if (debug & COMPL) { + (void) printf("Result %d\n", Tbar->count); + if (verbose_debug) + cprint(Tbar); + } + + free_cover(L); + free_cover(R); + return Tbar; +} + +/* + * compl_lift_simple -- expand in the splitting variable using single + * cube containment against the other recursion branch to check + * validity of the expansion, and expanding all (or none) of the + * splitting variable. + */ +static void compl_lift(A1, B1, bcube, var) +pcube *A1, *B1, bcube; +int var; +{ + register pcube a, b, *B2, lift=cube.temp[4], liftor=cube.temp[5]; + pcube mask = cube.var_mask[var]; + + (void) set_and(liftor, bcube, mask); + + /* for each cube in the first array ... */ + for(; (a = *A1++) != NULL; ) { + if (TESTP(a, ACTIVE)) { + + /* create a lift of this cube in the merging coord */ + (void) set_merge(lift, bcube, a, mask); + + /* for each cube in the second array */ + for(B2 = B1; (b = *B2++) != NULL; ) { + INLINEsetp_implies(lift, b, /* when_false => */ continue); + /* when_true => fall through to next statement */ + + /* cube of A1 was contained by some cube of B1, so raise */ + INLINEset_or(a, a, liftor); + break; + } + } + } +} + + + +/* + * compl_lift_onset -- expand in the splitting variable using a + * distance-1 check against the original on-set; expand all (or + * none) of the splitting variable. Each cube of A1 is expanded + * against the original on-set T. + */ +static void compl_lift_onset(A1, T, bcube, var) +pcube *A1; +pcover T; +pcube bcube; +int var; +{ + register pcube a, last, p, lift=cube.temp[4], mask=cube.var_mask[var]; + + /* for each active cube from one branch of the complement */ + for(; (a = *A1++) != NULL; ) { + if (TESTP(a, ACTIVE)) { + + /* create a lift of this cube in the merging coord */ + INLINEset_and(lift, bcube, mask); /* isolate parts to raise */ + INLINEset_or(lift, a, lift); /* raise these parts in a */ + + /* for each cube in the ON-set, check for intersection */ + foreach_set(T, last, p) { + if (cdist0(p, lift)) { + goto nolift; + } + } + INLINEset_copy(a, lift); /* save the raising */ + SET(a, ACTIVE); +nolift : ; + } + } +} + +/* + * compl_lift_complex -- expand in the splitting variable, but expand all + * parts which can possibly expand. + * T is the original ON-set + * A1 is either the left or right cofactor + */ +static void compl_lift_onset_complex(A1, T, var) +pcube *A1; /* array of pointers to new result */ +pcover T; /* original ON-set */ +int var; /* which variable we split on */ +{ + register int dist; + register pcube last, p, a, xlower; + + /* for each cube in the complement */ + xlower = new_cube(); + for(; (a = *A1++) != NULL; ) { + + if (TESTP(a, ACTIVE)) { + + /* Find which parts of the splitting variable are forced low */ + INLINEset_clear(xlower, cube.size); + foreach_set(T, last, p) { + if ((dist = cdist01(p, a)) < 2) { + if (dist == 0) { + fatal("compl: ON-set and OFF-set are not orthogonal"); + } else { + (void) force_lower(xlower, p, a); + } + } + } + + (void) set_diff(xlower, cube.var_mask[var], xlower); + (void) set_or(a, a, xlower); + free_cube(xlower); + } + } +} + + + +/* + * compl_d1merge -- distance-1 merge in the splitting variable + */ +static void compl_d1merge(L1, R1) +register pcube *L1, *R1; +{ + register pcube pl, pr; + + /* Find equal cubes between the two cofactors */ + for(pl = *L1, pr = *R1; (pl != NULL) && (pr != NULL); ) + switch (d1_order(L1, R1)) { + case 1: + pr = *(++R1); break; /* advance right pointer */ + case -1: + pl = *(++L1); break; /* advance left pointer */ + case 0: + RESET(pr, ACTIVE); + INLINEset_or(pl, pl, pr); + pr = *(++R1); + default: + ; + } +} + + + +/* compl_cube -- return the complement of a single cube (De Morgan's law) */ +static pcover compl_cube(p) +register pcube p; +{ + register pcube diff=cube.temp[7], pdest, mask, full=cube.fullset; + int var; + pcover R; + + /* Allocate worst-case size cover (to avoid checking overflow) */ + R = new_cover(cube.num_vars); + + /* Compute bit-wise complement of the cube */ + INLINEset_diff(diff, full, p); + + for(var = 0; var < cube.num_vars; var++) { + mask = cube.var_mask[var]; + /* If the bit-wise complement is not empty in var ... */ + if (! setp_disjoint(diff, mask)) { + pdest = GETSET(R, R->count++); + INLINEset_merge(pdest, diff, full, mask); + } + } + return R; +} + +/* simp_comp -- quick simplification of T */ +void simp_comp(T, Tnew, Tbar) +pcube *T; /* T will be disposed of */ +pcover *Tnew; +pcover *Tbar; +{ + register pcube cl, cr; + register int best; + pcover Tl, Tr, Tlbar, Trbar; + int lifting; + static int simplify_level = 0; + + if (debug & COMPL) + debug_print(T, "SIMPCOMP", simplify_level++); + + if (simp_comp_special_cases(T, Tnew, Tbar) == MAYBE) { + + /* Allocate space for the partition cubes */ + cl = new_cube(); + cr = new_cube(); + best = binate_split_select(T, cl, cr, COMPL); + + /* Complement the left and right halves */ + simp_comp(scofactor(T, cl, best), &Tl, &Tlbar); + simp_comp(scofactor(T, cr, best), &Tr, &Trbar); + + lifting = USE_COMPL_LIFT; + *Tnew = compl_merge(T, Tl, Tr, cl, cr, best, lifting); + + lifting = USE_COMPL_LIFT; + *Tbar = compl_merge(T, Tlbar, Trbar, cl, cr, best, lifting); + + /* All of this work for nothing ? Let's hope not ... */ + if ((*Tnew)->count > CUBELISTSIZE(T)) { + sf_free(*Tnew); + *Tnew = cubeunlist(T); + } + + free_cube(cl); + free_cube(cr); + free_cubelist(T); + } + + if (debug & COMPL) { + debug1_print(*Tnew, "exit SIMPCOMP (new)", simplify_level); + debug1_print(*Tbar, "exit SIMPCOMP (compl)", simplify_level); + simplify_level--; + } +} + +static bool simp_comp_special_cases(T, Tnew, Tbar) +pcube *T; /* will be disposed if answer is determined */ +pcover *Tnew; /* returned only if answer determined */ +pcover *Tbar; /* returned only if answer determined */ +{ + register pcube *T1, p, ceil, cof=T[0]; + pcube last; + pcover A; + + /* Check for no cubes in the cover (function is empty) */ + if (T[2] == NULL) { + *Tnew = new_cover(1); + *Tbar = sf_addset(new_cover(1), cube.fullset); + free_cubelist(T); + return TRUE; + } + + /* Check for only a single cube in the cover */ + if (T[3] == NULL) { + (void) set_or(cof, cof, T[2]); + *Tnew = sf_addset(new_cover(1), cof); + *Tbar = compl_cube(cof); + free_cubelist(T); + return TRUE; + } + + /* Check for a row of all 1's (function is a tautology) */ + for(T1 = T+2; (p = *T1++) != NULL; ) { + if (full_row(p, cof)) { + *Tnew = sf_addset(new_cover(1), cube.fullset); + *Tbar = new_cover(1); + free_cubelist(T); + return TRUE; + } + } + + /* Check for a column of all 0's which can be factored out */ + ceil = set_save(cof); + for(T1 = T+2; (p = *T1++) != NULL; ) { + INLINEset_or(ceil, ceil, p); + } + if (! setp_equal(ceil, cube.fullset)) { + p = new_cube(); + (void) set_diff(p, cube.fullset, ceil); + (void) set_or(cof, cof, p); + set_free(p); + simp_comp(T, Tnew, Tbar); + + /* Adjust the ON-set */ + A = *Tnew; + foreach_set(A, last, p) { + INLINEset_and(p, p, ceil); + } + + /* Compute the new complement */ + *Tbar = sf_append(*Tbar, compl_cube(ceil)); + set_free(ceil); + return TRUE; + } + set_free(ceil); + + /* Collect column counts, determine unate variables, etc. */ + massive_count(T); + + /* If single active variable not factored out above, then tautology ! */ + if (cdata.vars_active == 1) { + *Tnew = sf_addset(new_cover(1), cube.fullset); + *Tbar = new_cover(1); + free_cubelist(T); + return TRUE; + + /* Check for unate cover */ + } else if (cdata.vars_unate == cdata.vars_active) { + /* Make the cover minimum by single-cube containment */ + A = cubeunlist(T); + *Tnew = sf_contain(A); + + /* Now form a minimum representation of the complement */ + A = map_cover_to_unate(T); + A = unate_compl(A); + *Tbar = map_unate_to_cover(A); + sf_free(A); + free_cubelist(T); + return TRUE; + + /* Not much we can do about it */ + } else { + return MAYBE; + } +} + +/* simplify -- quick simplification of T */ +pcover simplify(T) +pcube *T; /* T will be disposed of */ +{ + register pcube cl, cr; + register int best; + pcover Tbar, Tl, Tr; + int lifting; + static int simplify_level = 0; + + if (debug & COMPL) { + debug_print(T, "SIMPLIFY", simplify_level++); + } + + if (simplify_special_cases(T, &Tbar) == MAYBE) { + + /* Allocate space for the partition cubes */ + cl = new_cube(); + cr = new_cube(); + + best = binate_split_select(T, cl, cr, COMPL); + + /* Complement the left and right halves */ + Tl = simplify(scofactor(T, cl, best)); + Tr = simplify(scofactor(T, cr, best)); + + lifting = USE_COMPL_LIFT; + Tbar = compl_merge(T, Tl, Tr, cl, cr, best, lifting); + + /* All of this work for nothing ? Let's hope not ... */ + if (Tbar->count > CUBELISTSIZE(T)) { + sf_free(Tbar); + Tbar = cubeunlist(T); + } + + free_cube(cl); + free_cube(cr); + free_cubelist(T); + } + + if (debug & COMPL) { + debug1_print(Tbar, "exit SIMPLIFY", --simplify_level); + } + return Tbar; +} + +static bool simplify_special_cases(T, Tnew) +pcube *T; /* will be disposed if answer is determined */ +pcover *Tnew; /* returned only if answer determined */ +{ + register pcube *T1, p, ceil, cof=T[0]; + pcube last; + pcover A; + + /* Check for no cubes in the cover */ + if (T[2] == NULL) { + *Tnew = new_cover(0); + free_cubelist(T); + return TRUE; + } + + /* Check for only a single cube in the cover */ + if (T[3] == NULL) { + *Tnew = sf_addset(new_cover(1), set_or(cof, cof, T[2])); + free_cubelist(T); + return TRUE; + } + + /* Check for a row of all 1's (implies function is a tautology) */ + for(T1 = T+2; (p = *T1++) != NULL; ) { + if (full_row(p, cof)) { + *Tnew = sf_addset(new_cover(1), cube.fullset); + free_cubelist(T); + return TRUE; + } + } + + /* Check for a column of all 0's which can be factored out */ + ceil = set_save(cof); + for(T1 = T+2; (p = *T1++) != NULL; ) { + INLINEset_or(ceil, ceil, p); + } + if (! setp_equal(ceil, cube.fullset)) { + p = new_cube(); + (void) set_diff(p, cube.fullset, ceil); + (void) set_or(cof, cof, p); + free_cube(p); + + A = simplify(T); + foreach_set(A, last, p) { + INLINEset_and(p, p, ceil); + } + *Tnew = A; + set_free(ceil); + return TRUE; + } + set_free(ceil); + + /* Collect column counts, determine unate variables, etc. */ + massive_count(T); + + /* If single active variable not factored out above, then tautology ! */ + if (cdata.vars_active == 1) { + *Tnew = sf_addset(new_cover(1), cube.fullset); + free_cubelist(T); + return TRUE; + + /* Check for unate cover */ + } else if (cdata.vars_unate == cdata.vars_active) { + A = cubeunlist(T); + *Tnew = sf_contain(A); + free_cubelist(T); + return TRUE; + + /* Not much we can do about it */ + } else { + return MAYBE; + } +} diff --git a/abc_with_bb_support/src/misc/espresso/contain.c b/abc_with_bb_support/src/misc/espresso/contain.c new file mode 100644 index 000000000..a8307f5c3 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/contain.c @@ -0,0 +1,441 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +/* + contain.c -- set containment routines + + These are complex routines for performing containment over a + family of sets, but they have the advantage of being much faster + than a straightforward n*n routine. + + First the cubes are sorted by size, and as a secondary key they are + sorted so that if two cubes are equal they end up adjacent. We can + than quickly remove equal cubes from further consideration by + comparing each cube to its neighbor. Finally, because the cubes + are sorted by size, we need only check cubes which are larger (or + smaller) than a given cube for containment. +*/ + +#include "espresso.h" + + +/* + sf_contain -- perform containment on a set family (delete sets which + are contained by some larger set in the family). No assumptions are + made about A, and the result will be returned in decreasing order of + set size. +*/ +pset_family sf_contain(A) +INOUT pset_family A; /* disposes of A */ +{ + int cnt; + pset *A1; + pset_family R; + + A1 = sf_sort(A, descend); /* sort into descending order */ + cnt = rm_equal(A1, descend); /* remove duplicates */ + cnt = rm_contain(A1); /* remove contained sets */ + R = sf_unlist(A1, cnt, A->sf_size); /* recreate the set family */ + sf_free(A); + return R; +} + + +/* + sf_rev_contain -- perform containment on a set family (delete sets which + contain some smaller set in the family). No assumptions are made about + A, and the result will be returned in increasing order of set size +*/ +pset_family sf_rev_contain(A) +INOUT pset_family A; /* disposes of A */ +{ + int cnt; + pset *A1; + pset_family R; + + A1 = sf_sort(A, ascend); /* sort into ascending order */ + cnt = rm_equal(A1, ascend); /* remove duplicates */ + cnt = rm_rev_contain(A1); /* remove containing sets */ + R = sf_unlist(A1, cnt, A->sf_size); /* recreate the set family */ + sf_free(A); + return R; +} + + +/* + sf_ind_contain -- perform containment on a set family (delete sets which + are contained by some larger set in the family). No assumptions are + made about A, and the result will be returned in decreasing order of + set size. Also maintains a set of row_indices to track which rows + disappear and how the rows end up permuted. +*/ +pset_family sf_ind_contain(A, row_indices) +INOUT pset_family A; /* disposes of A */ +INOUT int *row_indices; /* updated with the new values */ +{ + int cnt; + pset *A1; + pset_family R; + + A1 = sf_sort(A, descend); /* sort into descending order */ + cnt = rm_equal(A1, descend); /* remove duplicates */ + cnt = rm_contain(A1); /* remove contained sets */ + R = sf_ind_unlist(A1, cnt, A->sf_size, row_indices, A->data); + sf_free(A); + return R; +} + + +/* sf_dupl -- delete duplicate sets in a set family */ +pset_family sf_dupl(A) +INOUT pset_family A; /* disposes of A */ +{ + register int cnt; + register pset *A1; + pset_family R; + + A1 = sf_sort(A, descend); /* sort the set family */ + cnt = rm_equal(A1, descend); /* remove duplicates */ + R = sf_unlist(A1, cnt, A->sf_size); /* recreate the set family */ + sf_free(A); + return R; +} + + +/* + sf_union -- form the contained union of two set families (delete + sets which are contained by some larger set in the family). A and + B are assumed already sorted in decreasing order of set size (and + the SIZE field is assumed to contain the set size), and the result + will be returned sorted likewise. +*/ +pset_family sf_union(A, B) +INOUT pset_family A, B; /* disposes of A and B */ +{ + int cnt; + pset_family R; + pset *A1 = sf_list(A), *B1 = sf_list(B), *E1; + + E1 = ALLOC(pset, MAX(A->count, B->count) + 1); + cnt = rm2_equal(A1, B1, E1, descend); + cnt += rm2_contain(A1, B1) + rm2_contain(B1, A1); + R = sf_merge(A1, B1, E1, cnt, A->sf_size); + sf_free(A); sf_free(B); + return R; +} + + +/* + dist_merge -- consider all sets to be "or"-ed with "mask" and then + delete duplicates from the set family. +*/ +pset_family dist_merge(A, mask) +INOUT pset_family A; /* disposes of A */ +IN pset mask; /* defines variables to mask out */ +{ + pset *A1; + int cnt; + pset_family R; + + (void) set_copy(cube.temp[0], mask); + A1 = sf_sort(A, d1_order); + cnt = d1_rm_equal(A1, d1_order); + R = sf_unlist(A1, cnt, A->sf_size); + sf_free(A); + return R; +} + + +/* + d1merge -- perform an efficient distance-1 merge of cubes of A +*/ +pset_family d1merge(A, var) +INOUT pset_family A; /* disposes of A */ +IN int var; +{ + return dist_merge(A, cube.var_mask[var]); +} + + + +/* d1_rm_equal -- distance-1 merge (merge cubes which are equal under a mask) */ +int d1_rm_equal(A1, compare) +register pset *A1; /* array of set pointers */ +int (*compare)(); /* comparison function */ +{ + register int i, j, dest; + + dest = 0; + if (A1[0] != (pcube) NULL) { + for(i = 0, j = 1; A1[j] != (pcube) NULL; j++) + if ( (*compare)(&A1[i], &A1[j]) == 0) { + /* if sets are equal (under the mask) merge them */ + (void) set_or(A1[i], A1[i], A1[j]); + } else { + /* sets are unequal, so save the set i */ + A1[dest++] = A1[i]; + i = j; + } + A1[dest++] = A1[i]; + } + A1[dest] = (pcube) NULL; + return dest; +} + + +/* rm_equal -- scan a sorted array of set pointers for duplicate sets */ +int rm_equal(A1, compare) +INOUT pset *A1; /* updated in place */ +IN int (*compare)(); +{ + register pset *p, *pdest = A1; + + if (*A1 != NULL) { /* If more than one set */ + for(p = A1+1; *p != NULL; p++) + if ((*compare)(p, p-1) != 0) + *pdest++ = *(p-1); + *pdest++ = *(p-1); + *pdest = NULL; + } + return pdest - A1; +} + + +/* rm_contain -- perform containment over a sorted array of set pointers */ +int rm_contain(A1) +INOUT pset *A1; /* updated in place */ +{ + register pset *pa, *pb, *pcheck, a, b; + pset *pdest = A1; + int last_size = -1; + + /* Loop for all cubes of A1 */ + for(pa = A1; (a = *pa++) != NULL; ) { + /* Update the check pointer if the size has changed */ + if (SIZE(a) != last_size) + last_size = SIZE(a), pcheck = pdest; + for(pb = A1; pb != pcheck; ) { + b = *pb++; + INLINEsetp_implies(a, b, /* when_false => */ continue); + goto lnext1; + } + /* set a was not contained by some larger set, so save it */ + *pdest++ = a; + lnext1: ; + } + + *pdest = NULL; + return pdest - A1; +} + + +/* rm_rev_contain -- perform rcontainment over a sorted array of set pointers */ +int rm_rev_contain(A1) +INOUT pset *A1; /* updated in place */ +{ + register pset *pa, *pb, *pcheck, a, b; + pset *pdest = A1; + int last_size = -1; + + /* Loop for all cubes of A1 */ + for(pa = A1; (a = *pa++) != NULL; ) { + /* Update the check pointer if the size has changed */ + if (SIZE(a) != last_size) + last_size = SIZE(a), pcheck = pdest; + for(pb = A1; pb != pcheck; ) { + b = *pb++; + INLINEsetp_implies(b, a, /* when_false => */ continue); + goto lnext1; + } + /* the set a did not contain some smaller set, so save it */ + *pdest++ = a; + lnext1: ; + } + + *pdest = NULL; + return pdest - A1; +} + + +/* rm2_equal -- check two sorted arrays of set pointers for equal cubes */ +int rm2_equal(A1, B1, E1, compare) +INOUT register pset *A1, *B1; /* updated in place */ +OUT pset *E1; +IN int (*compare)(); +{ + register pset *pda = A1, *pdb = B1, *pde = E1; + + /* Walk through the arrays advancing pointer to larger cube */ + for(; *A1 != NULL && *B1 != NULL; ) + switch((*compare)(A1, B1)) { + case -1: /* "a" comes before "b" */ + *pda++ = *A1++; break; + case 0: /* equal cubes */ + *pde++ = *A1++; B1++; break; + case 1: /* "a" is to follow "b" */ + *pdb++ = *B1++; break; + } + + /* Finish moving down the pointers of A and B */ + while (*A1 != NULL) + *pda++ = *A1++; + while (*B1 != NULL) + *pdb++ = *B1++; + *pda = *pdb = *pde = NULL; + + return pde - E1; +} + + +/* rm2_contain -- perform containment between two arrays of set pointers */ +int rm2_contain(A1, B1) +INOUT pset *A1; /* updated in place */ +IN pset *B1; /* unchanged */ +{ + register pset *pa, *pb, a, b, *pdest = A1; + + /* for each set in the first array ... */ + for(pa = A1; (a = *pa++) != NULL; ) { + /* for each set in the second array which is larger ... */ + for(pb = B1; (b = *pb++) != NULL && SIZE(b) > SIZE(a); ) { + INLINEsetp_implies(a, b, /* when_false => */ continue); + /* set was contained in some set of B, so don't save pointer */ + goto lnext1; + } + /* set wasn't contained in any set of B, so save the pointer */ + *pdest++ = a; + lnext1: ; + } + + *pdest = NULL; /* sentinel */ + return pdest - A1; /* # elements in A1 */ +} + + + +/* sf_sort -- sort the sets of A */ +pset *sf_sort(A, compare) +IN pset_family A; +IN int (*compare)(); +{ + register pset p, last, *pdest, *A1; + + /* Create a single array pointing to each cube of A */ + pdest = A1 = ALLOC(pset, A->count + 1); + foreach_set(A, last, p) { + PUTSIZE(p, set_ord(p)); /* compute the set size */ + *pdest++ = p; /* save the pointer */ + } + *pdest = NULL; /* Sentinel -- never seen by sort */ + + /* Sort cubes by size */ + qsort((char *) A1, A->count, sizeof(pset), compare); + return A1; +} + + +/* sf_list -- make a list of pointers to the sets in a set family */ +pset *sf_list(A) +IN register pset_family A; +{ + register pset p, last, *pdest, *A1; + + /* Create a single array pointing to each cube of A */ + pdest = A1 = ALLOC(pset, A->count + 1); + foreach_set(A, last, p) + *pdest++ = p; /* save the pointer */ + *pdest = NULL; /* Sentinel */ + return A1; +} + + +/* sf_unlist -- make a set family out of a list of pointers to sets */ +pset_family sf_unlist(A1, totcnt, size) +IN pset *A1; +IN int totcnt, size; +{ + register pset pr, p, *pa; + pset_family R = sf_new(totcnt, size); + + R->count = totcnt; + for(pr = R->data, pa = A1; (p = *pa++) != NULL; pr += R->wsize) + INLINEset_copy(pr, p); + FREE(A1); + return R; +} + + +/* sf_ind_unlist -- make a set family out of a list of pointers to sets */ +pset_family sf_ind_unlist(A1, totcnt, size, row_indices, pfirst) +IN pset *A1; +IN int totcnt, size; +INOUT int *row_indices; +IN register pset pfirst; +{ + register pset pr, p, *pa; + register int i, *new_row_indices; + pset_family R = sf_new(totcnt, size); + + R->count = totcnt; + new_row_indices = ALLOC(int, totcnt); + for(pr = R->data, pa = A1, i=0; (p = *pa++) != NULL; pr += R->wsize, i++) { + INLINEset_copy(pr, p); + new_row_indices[i] = row_indices[(p - pfirst)/R->wsize]; + } + for(i = 0; i < totcnt; i++) + row_indices[i] = new_row_indices[i]; + FREE(new_row_indices); + FREE(A1); + return R; +} + + +/* sf_merge -- merge three sorted lists of set pointers */ +pset_family sf_merge(A1, B1, E1, totcnt, size) +INOUT pset *A1, *B1, *E1; /* will be disposed of */ +IN int totcnt, size; +{ + register pset pr, ps, *pmin, *pmid, *pmax; + pset_family R; + pset *temp[3], *swap; + int i, j, n; + + /* Allocate the result set_family */ + R = sf_new(totcnt, size); + R->count = totcnt; + pr = R->data; + + /* Quick bubble sort to order the top member of the three arrays */ + n = 3; temp[0] = A1; temp[1] = B1; temp[2] = E1; + for(i = 0; i < n-1; i++) + for(j = i+1; j < n; j++) + if (desc1(*temp[i], *temp[j]) > 0) { + swap = temp[j]; + temp[j] = temp[i]; + temp[i] = swap; + } + pmin = temp[0]; pmid = temp[1]; pmax = temp[2]; + + /* Save the minimum element, then update pmin, pmid, pmax */ + while (*pmin != (pset) NULL) { + ps = *pmin++; + INLINEset_copy(pr, ps); + pr += R->wsize; + if (desc1(*pmin, *pmax) > 0) { + swap = pmax; pmax = pmin; pmin = pmid; pmid = swap; + } else if (desc1(*pmin, *pmid) > 0) { + swap = pmin; pmin = pmid; pmid = swap; + } + } + + FREE(A1); + FREE(B1); + FREE(E1); + return R; +} diff --git a/abc_with_bb_support/src/misc/espresso/cubehack.c b/abc_with_bb_support/src/misc/espresso/cubehack.c new file mode 100644 index 000000000..92068c014 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/cubehack.c @@ -0,0 +1,138 @@ +/* + * Revision Control Information + * + * $Source: /vol/opua/opua2/sis/sis-1.1/common/src/sis/node/RCS/cubehack.c,v $ + * $Author: sis $ + * $Revision: 1.2 $ + * $Date: 1992/05/06 18:57:41 $ + * + */ +/* +#include "sis.h" +#include "node_int.h" + +#ifdef lint +struct cube_struct cube; +bool summary; +bool trace; +bool remove_essential; +bool force_irredundant; +bool unwrap_onset; +bool single_expand; +bool pos; +bool recompute_onset; +bool use_super_gasp; +bool use_random_order; +#endif +*/ +#include "espresso.h" + + +void +cautious_define_cube_size(n) +int n; +{ + if (cube.fullset != 0 && cube.num_binary_vars == n) + return; + if (cube.fullset != 0) { + setdown_cube(); + FREE(cube.part_size); + } + cube.num_binary_vars = cube.num_vars = n; + cube.part_size = ALLOC(int, n); + cube_setup(); +} + + +void +define_cube_size(n) +int n; +{ + register int q, i; + static int called_before = 0; + + /* check if the cube is already just the right size */ + if (cube.fullset != 0 && cube.num_binary_vars == n && cube.num_vars == n) + return; + + /* We can't handle more than 100 inputs */ + if (n > 100) { + cautious_define_cube_size(n); + called_before = 0; + return; + } + + if (cube.fullset == 0 || ! called_before) { + cautious_define_cube_size(100); + called_before = 1; + } + + cube.num_vars = n; + cube.num_binary_vars = n; + cube.num_mv_vars = 0; + cube.output = -1; + cube.size = n * 2; + + /* first_part, last_part, first_word, last_word, part_size OKAY */ + /* cube.sparse is OKAY */ + + /* need to completely re-make cube.fullset and cube.binary_mask */ + (void) set_fill(cube.fullset, n*2); + (void) set_fill(cube.binary_mask, n*2); + + /* need to resize each set in cube.var_mask and cube.temp */ + q = cube.fullset[0]; + for(i = 0; i < cube.num_vars; i++) + cube.var_mask[i][0] = q; + for(i = 0; i < CUBE_TEMP; i++) + cube.temp[i][0] = q; + + /* need to resize cube.emptyset and cube.mv_mask */ + cube.emptyset[0] = q; + cube.mv_mask[0] = q; + + /* need to reset the inword and inmask */ + if (cube.num_binary_vars != 0) { + cube.inword = cube.last_word[cube.num_binary_vars - 1]; + cube.inmask = cube.binary_mask[cube.inword] & DISJOINT; + } else { + cube.inword = -1; + cube.inmask = 0; + } + + /* cdata (entire structure) is OKAY */ +} + + +void +undefine_cube_size() +{ + if (cube.num_binary_vars > 100) { + if (cube.fullset != 0) { + setdown_cube(); + FREE(cube.part_size); + } + } else { + cube.num_vars = cube.num_binary_vars = 100; + if (cube.fullset != 0) { + setdown_cube(); + FREE(cube.part_size); + } + } +} + + +void +set_espresso_flags() +{ + summary = FALSE; + trace = FALSE; + remove_essential = TRUE; + force_irredundant = TRUE; + unwrap_onset = TRUE; + single_expand = FALSE; + pos = FALSE; + recompute_onset = FALSE; + use_super_gasp = FALSE; + use_random_order = FALSE; +} diff --git a/abc_with_bb_support/src/misc/espresso/cubestr.c b/abc_with_bb_support/src/misc/espresso/cubestr.c new file mode 100644 index 000000000..cebd370dd --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/cubestr.c @@ -0,0 +1,152 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +/* + Module: cubestr.c -- routines for managing the global cube structure +*/ + +#include "espresso.h" + +/* + cube_setup -- assume that the fields "num_vars", "num_binary_vars", and + part_size[num_binary_vars .. num_vars-1] are setup, and initialize the + rest of cube and cdata. + + If a part_size is < 0, then the field size is abs(part_size) and the + field read from the input is symbolic. +*/ +void cube_setup() +{ + register int i, var; + register pcube p; + + if (cube.num_binary_vars < 0 || cube.num_vars < cube.num_binary_vars) + fatal("cube size is silly, error in .i/.o or .mv"); + + cube.num_mv_vars = cube.num_vars - cube.num_binary_vars; + cube.output = cube.num_mv_vars > 0 ? cube.num_vars - 1 : -1; + + cube.size = 0; + cube.first_part = ALLOC(int, cube.num_vars); + cube.last_part = ALLOC(int, cube.num_vars); + cube.first_word = ALLOC(int, cube.num_vars); + cube.last_word = ALLOC(int, cube.num_vars); + for(var = 0; var < cube.num_vars; var++) { + if (var < cube.num_binary_vars) + cube.part_size[var] = 2; + cube.first_part[var] = cube.size; + cube.first_word[var] = WHICH_WORD(cube.size); + cube.size += ABS(cube.part_size[var]); + cube.last_part[var] = cube.size - 1; + cube.last_word[var] = WHICH_WORD(cube.size - 1); + } + + cube.var_mask = ALLOC(pset, cube.num_vars); + cube.sparse = ALLOC(int, cube.num_vars); + cube.binary_mask = new_cube(); + cube.mv_mask = new_cube(); + for(var = 0; var < cube.num_vars; var++) { + p = cube.var_mask[var] = new_cube(); + for(i = cube.first_part[var]; i <= cube.last_part[var]; i++) + set_insert(p, i); + if (var < cube.num_binary_vars) { + INLINEset_or(cube.binary_mask, cube.binary_mask, p); + cube.sparse[var] = 0; + } else { + INLINEset_or(cube.mv_mask, cube.mv_mask, p); + cube.sparse[var] = 1; + } + } + if (cube.num_binary_vars == 0) + cube.inword = -1; + else { + cube.inword = cube.last_word[cube.num_binary_vars - 1]; + cube.inmask = cube.binary_mask[cube.inword] & DISJOINT; + } + + cube.temp = ALLOC(pset, CUBE_TEMP); + for(i = 0; i < CUBE_TEMP; i++) + cube.temp[i] = new_cube(); + cube.fullset = set_fill(new_cube(), cube.size); + cube.emptyset = new_cube(); + + cdata.part_zeros = ALLOC(int, cube.size); + cdata.var_zeros = ALLOC(int, cube.num_vars); + cdata.parts_active = ALLOC(int, cube.num_vars); + cdata.is_unate = ALLOC(int, cube.num_vars); +} + +/* + setdown_cube -- free memory allocated for the cube/cdata structs + (free's all but the part_size array) + + (I wanted to call this cube_setdown, but that violates the 8-character + external routine limit on the IBM !) +*/ +void setdown_cube() +{ + register int i, var; + + FREE(cube.first_part); + FREE(cube.last_part); + FREE(cube.first_word); + FREE(cube.last_word); + FREE(cube.sparse); + + free_cube(cube.binary_mask); + free_cube(cube.mv_mask); + free_cube(cube.fullset); + free_cube(cube.emptyset); + for(var = 0; var < cube.num_vars; var++) + free_cube(cube.var_mask[var]); + FREE(cube.var_mask); + + for(i = 0; i < CUBE_TEMP; i++) + free_cube(cube.temp[i]); + FREE(cube.temp); + + FREE(cdata.part_zeros); + FREE(cdata.var_zeros); + FREE(cdata.parts_active); + FREE(cdata.is_unate); + + cube.first_part = cube.last_part = (int *) NULL; + cube.first_word = cube.last_word = (int *) NULL; + cube.sparse = (int *) NULL; + cube.binary_mask = cube.mv_mask = (pcube) NULL; + cube.fullset = cube.emptyset = (pcube) NULL; + cube.var_mask = cube.temp = (pcube *) NULL; + + cdata.part_zeros = cdata.var_zeros = cdata.parts_active = (int *) NULL; + cdata.is_unate = (bool *) NULL; +} + + +void save_cube_struct() +{ + temp_cube_save = cube; /* structure copy ! */ + temp_cdata_save = cdata; /* "" */ + + cube.first_part = cube.last_part = (int *) NULL; + cube.first_word = cube.last_word = (int *) NULL; + cube.part_size = (int *) NULL; + cube.binary_mask = cube.mv_mask = (pcube) NULL; + cube.fullset = cube.emptyset = (pcube) NULL; + cube.var_mask = cube.temp = (pcube *) NULL; + + cdata.part_zeros = cdata.var_zeros = cdata.parts_active = (int *) NULL; + cdata.is_unate = (bool *) NULL; +} + + +void restore_cube_struct() +{ + cube = temp_cube_save; /* structure copy ! */ + cdata = temp_cdata_save; /* "" */ +} diff --git a/abc_with_bb_support/src/misc/espresso/cvrin.c b/abc_with_bb_support/src/misc/espresso/cvrin.c new file mode 100644 index 000000000..333210a30 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/cvrin.c @@ -0,0 +1,810 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +/* + module: cvrin.c + purpose: cube and cover input routines +*/ + +#include "espresso.h" + +static bool line_length_error; +static int lineno; + +void skip_line(fpin, fpout, echo) +register FILE *fpin, *fpout; +register bool echo; +{ + register int ch; + while ((ch=getc(fpin)) != EOF && ch != '\n') + if (echo) + putc(ch, fpout); + if (echo) + putc('\n', fpout); + lineno++; +} + +char *get_word(fp, word) +register FILE *fp; +register char *word; +{ + register int ch, i = 0; + while ((ch = getc(fp)) != EOF && isspace(ch)) + ; + word[i++] = ch; + while ((ch = getc(fp)) != EOF && ! isspace(ch)) + word[i++] = ch; + word[i++] = '\0'; + return word; +} + +/* + * Yes, I know this routine is a mess + */ +void read_cube(fp, PLA) +register FILE *fp; +pPLA PLA; +{ + register int var, i; + pcube cf = cube.temp[0], cr = cube.temp[1], cd = cube.temp[2]; + bool savef = FALSE, saved = FALSE, saver = FALSE; + char token[256]; /* for kiss read hack */ + int varx, first, last, offset; /* for kiss read hack */ + + set_clear(cf, cube.size); + + /* Loop and read binary variables */ + for(var = 0; var < cube.num_binary_vars; var++) + switch(getc(fp)) { + case EOF: + goto bad_char; + case '\n': + if (! line_length_error) + (void) fprintf(stderr, "product term(s) %s\n", + "span more than one line (warning only)"); + line_length_error = TRUE; + lineno++; + var--; + break; + case ' ': case '|': case '\t': + var--; + break; + case '2': case '-': + set_insert(cf, var*2+1); + case '0': + set_insert(cf, var*2); + break; + case '1': + set_insert(cf, var*2+1); + break; + case '?': + break; + default: + goto bad_char; + } + + + /* Loop for the all but one of the multiple-valued variables */ + for(var = cube.num_binary_vars; var < cube.num_vars-1; var++) + + /* Read a symbolic multiple-valued variable */ + if (cube.part_size[var] < 0) { + (void) fscanf(fp, "%s", token); + if (equal(token, "-") || equal(token, "ANY")) { + if (kiss && var == cube.num_vars - 2) { + /* leave it empty */ + } else { + /* make it full */ + set_or(cf, cf, cube.var_mask[var]); + } + } else if (equal(token, "~")) { + ; + /* leave it empty ... (?) */ + } else { + if (kiss && var == cube.num_vars - 2) + varx = var - 1, offset = ABS(cube.part_size[var-1]); + else + varx = var, offset = 0; + /* Find the symbolic label in the label table */ + first = cube.first_part[varx]; + last = cube.last_part[varx]; + for(i = first; i <= last; i++) + if (PLA->label[i] == (char *) NULL) { + PLA->label[i] = util_strsav(token); /* add new label */ + set_insert(cf, i+offset); + break; + } else if (equal(PLA->label[i], token)) { + set_insert(cf, i+offset); /* use column i */ + break; + } + if (i > last) { + (void) fprintf(stderr, +"declared size of variable %d (counting from variable 0) is too small\n", var); + exit(-1); + } + } + + } else for(i = cube.first_part[var]; i <= cube.last_part[var]; i++) + switch (getc(fp)) { + case EOF: + goto bad_char; + case '\n': + if (! line_length_error) + (void) fprintf(stderr, "product term(s) %s\n", + "span more than one line (warning only)"); + line_length_error = TRUE; + lineno++; + i--; + break; + case ' ': case '|': case '\t': + i--; + break; + case '1': + set_insert(cf, i); + case '0': + break; + default: + goto bad_char; + } + + /* Loop for last multiple-valued variable */ + if (kiss) { + saver = savef = TRUE; + (void) set_xor(cr, cf, cube.var_mask[cube.num_vars - 2]); + } else + set_copy(cr, cf); + set_copy(cd, cf); + for(i = cube.first_part[var]; i <= cube.last_part[var]; i++) + switch (getc(fp)) { + case EOF: + goto bad_char; + case '\n': + if (! line_length_error) + (void) fprintf(stderr, "product term(s) %s\n", + "span more than one line (warning only)"); + line_length_error = TRUE; + lineno++; + i--; + break; + case ' ': case '|': case '\t': + i--; + break; + case '4': case '1': + if (PLA->pla_type & F_type) + set_insert(cf, i), savef = TRUE; + break; + case '3': case '0': + if (PLA->pla_type & R_type) + set_insert(cr, i), saver = TRUE; + break; + case '2': case '-': + if (PLA->pla_type & D_type) + set_insert(cd, i), saved = TRUE; + case '~': + break; + default: + goto bad_char; + } + if (savef) PLA->F = sf_addset(PLA->F, cf); + if (saved) PLA->D = sf_addset(PLA->D, cd); + if (saver) PLA->R = sf_addset(PLA->R, cr); + return; + +bad_char: + (void) fprintf(stderr, "(warning): input line #%d ignored\n", lineno); + skip_line(fp, stdout, TRUE); + return; +} +void parse_pla(fp, PLA) +IN FILE *fp; +INOUT pPLA PLA; +{ + int i, var, ch, np, last; + char word[256]; + + lineno = 1; + line_length_error = FALSE; + +loop: + switch(ch = getc(fp)) { + case EOF: + return; + + case '\n': + lineno++; + + case ' ': case '\t': case '\f': case '\r': + break; + + case '#': + (void) ungetc(ch, fp); + skip_line(fp, stdout, echo_comments); + break; + + case '.': + /* .i gives the cube input size (binary-functions only) */ + if (equal(get_word(fp, word), "i")) { + if (cube.fullset != NULL) { + (void) fprintf(stderr, "extra .i ignored\n"); + skip_line(fp, stdout, /* echo */ FALSE); + } else { + if (fscanf(fp, "%d", &cube.num_binary_vars) != 1) + fatal("error reading .i"); + cube.num_vars = cube.num_binary_vars + 1; + cube.part_size = ALLOC(int, cube.num_vars); + } + + /* .o gives the cube output size (binary-functions only) */ + } else if (equal(word, "o")) { + if (cube.fullset != NULL) { + (void) fprintf(stderr, "extra .o ignored\n"); + skip_line(fp, stdout, /* echo */ FALSE); + } else { + if (cube.part_size == NULL) + fatal(".o cannot appear before .i"); + if (fscanf(fp, "%d", &(cube.part_size[cube.num_vars-1]))!=1) + fatal("error reading .o"); + cube_setup(); + PLA_labels(PLA); + } + + /* .mv gives the cube size for a multiple-valued function */ + } else if (equal(word, "mv")) { + if (cube.fullset != NULL) { + (void) fprintf(stderr, "extra .mv ignored\n"); + skip_line(fp, stdout, /* echo */ FALSE); + } else { + if (cube.part_size != NULL) + fatal("cannot mix .i and .mv"); + if (fscanf(fp,"%d %d", + &cube.num_vars,&cube.num_binary_vars) != 2) + fatal("error reading .mv"); + if (cube.num_binary_vars < 0) +fatal("num_binary_vars (second field of .mv) cannot be negative"); + if (cube.num_vars < cube.num_binary_vars) + fatal( +"num_vars (1st field of .mv) must exceed num_binary_vars (2nd field of .mv)"); + cube.part_size = ALLOC(int, cube.num_vars); + for(var=cube.num_binary_vars; var < cube.num_vars; var++) + if (fscanf(fp, "%d", &(cube.part_size[var])) != 1) + fatal("error reading .mv"); + cube_setup(); + PLA_labels(PLA); + } + + /* .p gives the number of product terms -- we ignore it */ + } else if (equal(word, "p")) + (void) fscanf(fp, "%d", &np); + /* .e and .end specify the end of the file */ + else if (equal(word, "e") || equal(word,"end")) { + if (cube.fullset == NULL) { + /* fatal("unknown PLA size, need .i/.o or .mv");*/ + } else if (PLA->F == NULL) { + PLA->F = new_cover(10); + PLA->D = new_cover(10); + PLA->R = new_cover(10); + } + return; + } + /* .kiss turns on the kiss-hack option */ + else if (equal(word, "kiss")) + kiss = TRUE; + + /* .type specifies a logical type for the PLA */ + else if (equal(word, "type")) { + (void) get_word(fp, word); + for(i = 0; pla_types[i].key != 0; i++) + if (equal(pla_types[i].key + 1, word)) { + PLA->pla_type = pla_types[i].value; + break; + } + if (pla_types[i].key == 0) + fatal("unknown type in .type command"); + + /* parse the labels */ + } else if (equal(word, "ilb")) { + if (cube.fullset == NULL) + fatal("PLA size must be declared before .ilb or .ob"); + if (PLA->label == NULL) + PLA_labels(PLA); + for(var = 0; var < cube.num_binary_vars; var++) { + (void) get_word(fp, word); + i = cube.first_part[var]; + PLA->label[i+1] = util_strsav(word); + PLA->label[i] = ALLOC(char, strlen(word) + 6); + (void) sprintf(PLA->label[i], "%s.bar", word); + } + } else if (equal(word, "ob")) { + if (cube.fullset == NULL) + fatal("PLA size must be declared before .ilb or .ob"); + if (PLA->label == NULL) + PLA_labels(PLA); + var = cube.num_vars - 1; + for(i = cube.first_part[var]; i <= cube.last_part[var]; i++) { + (void) get_word(fp, word); + PLA->label[i] = util_strsav(word); + } + /* .label assigns labels to multiple-valued variables */ + } else if (equal(word, "label")) { + if (cube.fullset == NULL) + fatal("PLA size must be declared before .label"); + if (PLA->label == NULL) + PLA_labels(PLA); + if (fscanf(fp, "var=%d", &var) != 1) + fatal("Error reading labels"); + for(i = cube.first_part[var]; i <= cube.last_part[var]; i++) { + (void) get_word(fp, word); + PLA->label[i] = util_strsav(word); + } + + } else if (equal(word, "symbolic")) { + symbolic_t *newlist, *p1; + if (read_symbolic(fp, PLA, word, &newlist)) { + if (PLA->symbolic == NIL(symbolic_t)) { + PLA->symbolic = newlist; + } else { + for(p1=PLA->symbolic;p1->next!=NIL(symbolic_t); + p1=p1->next){ + } + p1->next = newlist; + } + } else { + fatal("error reading .symbolic"); + } + + } else if (equal(word, "symbolic-output")) { + symbolic_t *newlist, *p1; + if (read_symbolic(fp, PLA, word, &newlist)) { + if (PLA->symbolic_output == NIL(symbolic_t)) { + PLA->symbolic_output = newlist; + } else { + for(p1=PLA->symbolic_output;p1->next!=NIL(symbolic_t); + p1=p1->next){ + } + p1->next = newlist; + } + } else { + fatal("error reading .symbolic-output"); + } + + /* .phase allows a choice of output phases */ + } else if (equal(word, "phase")) { + if (cube.fullset == NULL) + fatal("PLA size must be declared before .phase"); + if (PLA->phase != NULL) { + (void) fprintf(stderr, "extra .phase ignored\n"); + skip_line(fp, stdout, /* echo */ FALSE); + } else { + do ch = getc(fp); while (ch == ' ' || ch == '\t'); + (void) ungetc(ch, fp); + PLA->phase = set_save(cube.fullset); + last = cube.last_part[cube.num_vars - 1]; + for(i=cube.first_part[cube.num_vars - 1]; i <= last; i++) + if ((ch = getc(fp)) == '0') + set_remove(PLA->phase, i); + else if (ch != '1') + fatal("only 0 or 1 allowed in phase description"); + } + + /* .pair allows for bit-pairing input variables */ + } else if (equal(word, "pair")) { + int j; + if (PLA->pair != NULL) { + (void) fprintf(stderr, "extra .pair ignored\n"); + } else { + ppair pair; + PLA->pair = pair = ALLOC(pair_t, 1); + if (fscanf(fp, "%d", &(pair->cnt)) != 1) + fatal("syntax error in .pair"); + pair->var1 = ALLOC(int, pair->cnt); + pair->var2 = ALLOC(int, pair->cnt); + for(i = 0; i < pair->cnt; i++) { + (void) get_word(fp, word); + if (word[0] == '(') (void) strcpy(word, word+1); + if (label_index(PLA, word, &var, &j)) { + pair->var1[i] = var+1; + } else { + fatal("syntax error in .pair"); + } + + (void) get_word(fp, word); + if (word[strlen(word)-1] == ')') { + word[strlen(word)-1]='\0'; + } + if (label_index(PLA, word, &var, &j)) { + pair->var2[i] = var+1; + } else { + fatal("syntax error in .pair"); + } + } + } + + } else { + if (echo_unknown_commands) + printf("%c%s ", ch, word); + skip_line(fp, stdout, echo_unknown_commands); + } + break; + default: + (void) ungetc(ch, fp); + if (cube.fullset == NULL) { +/* fatal("unknown PLA size, need .i/.o or .mv");*/ + if (echo_comments) + putchar('#'); + skip_line(fp, stdout, echo_comments); + break; + } + if (PLA->F == NULL) { + PLA->F = new_cover(10); + PLA->D = new_cover(10); + PLA->R = new_cover(10); + } + read_cube(fp, PLA); + } + goto loop; +} +/* + read_pla -- read a PLA from a file + + Input stops when ".e" is encountered in the input file, or upon reaching + end of file. + + Returns the PLA in the variable PLA after massaging the "symbolic" + representation into a positional cube notation of the ON-set, OFF-set, + and the DC-set. + + needs_dcset and needs_offset control the computation of the OFF-set + and DC-set (i.e., if either needs to be computed, then it will be + computed via complement only if the corresponding option is TRUE.) + pla_type specifies the interpretation to be used when reading the + PLA. + + The phase of the output functions is adjusted according to the + global option "pos" or according to an imbedded .phase option in + the input file. Note that either phase option implies that the + OFF-set be computed regardless of whether the caller needs it + explicitly or not. + + Bit pairing of the binary variables is performed according to an + imbedded .pair option in the input file. + + The global cube structure also reflects the sizes of the PLA which + was just read. If these fields have already been set, then any + subsequent PLA must conform to these sizes. + + The global flags trace and summary control the output produced + during the read. + + Returns a status code as a result: + EOF (-1) : End of file reached before any data was read + > 0 : Operation successful +*/ + +int read_pla(fp, needs_dcset, needs_offset, pla_type, PLA_return) +IN FILE *fp; +IN bool needs_dcset, needs_offset; +IN int pla_type; +OUT pPLA *PLA_return; +{ + pPLA PLA; + int i, second, third; + long time; + cost_t cost; + + /* Allocate and initialize the PLA structure */ + PLA = *PLA_return = new_PLA(); + PLA->pla_type = pla_type; + + /* Read the pla */ + time = ptime(); + parse_pla(fp, PLA); + + /* Check for nothing on the file -- implies reached EOF */ + if (PLA->F == NULL) { + return EOF; + } + + /* This hack merges the next-state field with the outputs */ + for(i = 0; i < cube.num_vars; i++) { + cube.part_size[i] = ABS(cube.part_size[i]); + } + if (kiss) { + third = cube.num_vars - 3; + second = cube.num_vars - 2; + if (cube.part_size[third] != cube.part_size[second]) { + (void) fprintf(stderr," with .kiss option, third to last and second\n"); + (void) fprintf(stderr, "to last variables must be the same size.\n"); + return EOF; + } + for(i = 0; i < cube.part_size[second]; i++) { + PLA->label[i + cube.first_part[second]] = + util_strsav(PLA->label[i + cube.first_part[third]]); + } + cube.part_size[second] += cube.part_size[cube.num_vars-1]; + cube.num_vars--; + setdown_cube(); + cube_setup(); + } + + if (trace) { + totals(time, READ_TIME, PLA->F, &cost); + } + + /* Decide how to break PLA into ON-set, OFF-set and DC-set */ + time = ptime(); + if (pos || PLA->phase != NULL || PLA->symbolic_output != NIL(symbolic_t)) { + needs_offset = TRUE; + } + if (needs_offset && (PLA->pla_type==F_type || PLA->pla_type==FD_type)) { + free_cover(PLA->R); + PLA->R = complement(cube2list(PLA->F, PLA->D)); + } else if (needs_dcset && PLA->pla_type == FR_type) { + pcover X; + free_cover(PLA->D); + /* hack, why not? */ + X = d1merge(sf_join(PLA->F, PLA->R), cube.num_vars - 1); + PLA->D = complement(cube1list(X)); + free_cover(X); + } else if (PLA->pla_type == R_type || PLA->pla_type == DR_type) { + free_cover(PLA->F); + PLA->F = complement(cube2list(PLA->D, PLA->R)); + } + + if (trace) { + totals(time, COMPL_TIME, PLA->R, &cost); + } + + /* Check for phase rearrangement of the functions */ + if (pos) { + pcover onset = PLA->F; + PLA->F = PLA->R; + PLA->R = onset; + PLA->phase = new_cube(); + set_diff(PLA->phase, cube.fullset, cube.var_mask[cube.num_vars-1]); + } else if (PLA->phase != NULL) { + (void) set_phase(PLA); + } + + /* Setup minimization for two-bit decoders */ + if (PLA->pair != (ppair) NULL) { + set_pair(PLA); + } + + if (PLA->symbolic != NIL(symbolic_t)) { + EXEC(map_symbolic(PLA), "MAP-INPUT ", PLA->F); + } + if (PLA->symbolic_output != NIL(symbolic_t)) { + EXEC(map_output_symbolic(PLA), "MAP-OUTPUT ", PLA->F); + if (needs_offset) { + free_cover(PLA->R); +EXECUTE(PLA->R=complement(cube2list(PLA->F,PLA->D)), COMPL_TIME, PLA->R, cost); + } + } + + return 1; +} + +void PLA_summary(PLA) +pPLA PLA; +{ + int var, i; + symbolic_list_t *p2; + symbolic_t *p1; + + printf("# PLA is %s", PLA->filename); + if (cube.num_binary_vars == cube.num_vars - 1) + printf(" with %d inputs and %d outputs\n", + cube.num_binary_vars, cube.part_size[cube.num_vars - 1]); + else { + printf(" with %d variables (%d binary, mv sizes", + cube.num_vars, cube.num_binary_vars); + for(var = cube.num_binary_vars; var < cube.num_vars; var++) + printf(" %d", cube.part_size[var]); + printf(")\n"); + } + printf("# ON-set cost is %s\n", print_cost(PLA->F)); + printf("# OFF-set cost is %s\n", print_cost(PLA->R)); + printf("# DC-set cost is %s\n", print_cost(PLA->D)); + if (PLA->phase != NULL) + printf("# phase is %s\n", pc1(PLA->phase)); + if (PLA->pair != NULL) { + printf("# two-bit decoders:"); + for(i = 0; i < PLA->pair->cnt; i++) + printf(" (%d %d)", PLA->pair->var1[i], PLA->pair->var2[i]); + printf("\n"); + } + if (PLA->symbolic != NIL(symbolic_t)) { + for(p1 = PLA->symbolic; p1 != NIL(symbolic_t); p1 = p1->next) { + printf("# symbolic: "); + for(p2=p1->symbolic_list; p2!=NIL(symbolic_list_t); p2=p2->next) { + printf(" %d", p2->variable); + } + printf("\n"); + } + } + if (PLA->symbolic_output != NIL(symbolic_t)) { + for(p1 = PLA->symbolic_output; p1 != NIL(symbolic_t); p1 = p1->next) { + printf("# output symbolic: "); + for(p2=p1->symbolic_list; p2!=NIL(symbolic_list_t); p2=p2->next) { + printf(" %d", p2->pos); + } + printf("\n"); + } + } + (void) fflush(stdout); +} + + +pPLA new_PLA() +{ + pPLA PLA; + + PLA = ALLOC(PLA_t, 1); + PLA->F = PLA->D = PLA->R = (pcover) NULL; + PLA->phase = (pcube) NULL; + PLA->pair = (ppair) NULL; + PLA->label = (char **) NULL; + PLA->filename = (char *) NULL; + PLA->pla_type = 0; + PLA->symbolic = NIL(symbolic_t); + PLA->symbolic_output = NIL(symbolic_t); + return PLA; +} + + +PLA_labels(PLA) +pPLA PLA; +{ + int i; + + PLA->label = ALLOC(char *, cube.size); + for(i = 0; i < cube.size; i++) + PLA->label[i] = (char *) NULL; +} + + +void free_PLA(PLA) +pPLA PLA; +{ + symbolic_list_t *p2, *p2next; + symbolic_t *p1, *p1next; + int i; + + if (PLA->F != (pcover) NULL) + free_cover(PLA->F); + if (PLA->R != (pcover) NULL) + free_cover(PLA->R); + if (PLA->D != (pcover) NULL) + free_cover(PLA->D); + if (PLA->phase != (pcube) NULL) + free_cube(PLA->phase); + if (PLA->pair != (ppair) NULL) { + FREE(PLA->pair->var1); + FREE(PLA->pair->var2); + FREE(PLA->pair); + } + if (PLA->label != NULL) { + for(i = 0; i < cube.size; i++) + if (PLA->label[i] != NULL) + FREE(PLA->label[i]); + FREE(PLA->label); + } + if (PLA->filename != NULL) { + FREE(PLA->filename); + } + for(p1 = PLA->symbolic; p1 != NIL(symbolic_t); p1 = p1next) { + for(p2 = p1->symbolic_list; p2 != NIL(symbolic_list_t); p2 = p2next) { + p2next = p2->next; + FREE(p2); + } + p1next = p1->next; + FREE(p1); + } + PLA->symbolic = NIL(symbolic_t); + for(p1 = PLA->symbolic_output; p1 != NIL(symbolic_t); p1 = p1next) { + for(p2 = p1->symbolic_list; p2 != NIL(symbolic_list_t); p2 = p2next) { + p2next = p2->next; + FREE(p2); + } + p1next = p1->next; + FREE(p1); + } + PLA->symbolic_output = NIL(symbolic_t); + FREE(PLA); +} + + +int read_symbolic(fp, PLA, word, retval) +FILE *fp; +pPLA PLA; +char *word; /* scratch string for words */ +symbolic_t **retval; +{ + symbolic_list_t *listp, *prev_listp; + symbolic_label_t *labelp, *prev_labelp; + symbolic_t *newlist; + int i, var; + + newlist = ALLOC(symbolic_t, 1); + newlist->next = NIL(symbolic_t); + newlist->symbolic_list = NIL(symbolic_list_t); + newlist->symbolic_list_length = 0; + newlist->symbolic_label = NIL(symbolic_label_t); + newlist->symbolic_label_length = 0; + prev_listp = NIL(symbolic_list_t); + prev_labelp = NIL(symbolic_label_t); + + for(;;) { + (void) get_word(fp, word); + if (equal(word, ";")) + break; + if (label_index(PLA, word, &var, &i)) { + listp = ALLOC(symbolic_list_t, 1); + listp->variable = var; + listp->pos = i; + listp->next = NIL(symbolic_list_t); + if (prev_listp == NIL(symbolic_list_t)) { + newlist->symbolic_list = listp; + } else { + prev_listp->next = listp; + } + prev_listp = listp; + newlist->symbolic_list_length++; + } else { + return FALSE; + } + } + + for(;;) { + (void) get_word(fp, word); + if (equal(word, ";")) + break; + labelp = ALLOC(symbolic_label_t, 1); + labelp->label = util_strsav(word); + labelp->next = NIL(symbolic_label_t); + if (prev_labelp == NIL(symbolic_label_t)) { + newlist->symbolic_label = labelp; + } else { + prev_labelp->next = labelp; + } + prev_labelp = labelp; + newlist->symbolic_label_length++; + } + + *retval = newlist; + return TRUE; +} + + +int label_index(PLA, word, varp, ip) +pPLA PLA; +char *word; +int *varp; +int *ip; +{ + int var, i; + + if (PLA->label == NIL(char *) || PLA->label[0] == NIL(char)) { + if (sscanf(word, "%d", varp) == 1) { + *ip = *varp; + return TRUE; + } + } else { + for(var = 0; var < cube.num_vars; var++) { + for(i = 0; i < cube.part_size[var]; i++) { + if (equal(PLA->label[cube.first_part[var]+i], word)) { + *varp = var; + *ip = i; + return TRUE; + } + } + } + } + return FALSE; +} diff --git a/abc_with_bb_support/src/misc/espresso/cvrm.c b/abc_with_bb_support/src/misc/espresso/cvrm.c new file mode 100644 index 000000000..66b00d70e --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/cvrm.c @@ -0,0 +1,539 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +/* + module: cvrm.c + Purpose: miscellaneous cover manipulation + a) verify two covers are equal, check consistency of a cover + b) unravel a multiple-valued cover into minterms + c) sort covers +*/ + +#include "espresso.h" + + +static void cb_unravel(c, start, end, startbase, B1) +IN register pcube c; +IN int start, end; +IN pcube startbase; +INOUT pcover B1; +{ + pcube base = cube.temp[0], p, last; + int expansion, place, skip, var, size, offset; + register int i, j, k, n; + + /* Determine how many cubes it will blow up into, and create a mask + for those parts that have only a single coordinate + */ + expansion = 1; + (void) set_copy(base, startbase); + for(var = start; var <= end; var++) { + if ((size = set_dist(c, cube.var_mask[var])) < 2) { + (void) set_or(base, base, cube.var_mask[var]); + } else { + expansion *= size; + } + } + (void) set_and(base, c, base); + + /* Add the unravelled sets starting at the last element of B1 */ + offset = B1->count; + B1->count += expansion; + foreach_remaining_set(B1, last, GETSET(B1, offset-1), p) { + INLINEset_copy(p, base); + } + + place = expansion; + for(var = start; var <= end; var++) { + if ((size = set_dist(c, cube.var_mask[var])) > 1) { + skip = place; + place = place / size; + n = 0; + for(i = cube.first_part[var]; i <= cube.last_part[var]; i++) { + if (is_in_set(c, i)) { + for(j = n; j < expansion; j += skip) { + for(k = 0; k < place; k++) { + p = GETSET(B1, j+k+offset); + (void) set_insert(p, i); + } + } + n += place; + } + } + } + } +} + + +pcover unravel_range(B, start, end) +IN pcover B; +IN int start, end; +{ + pcover B1; + int var, total_size, expansion, size; + register pcube p, last, startbase = cube.temp[1]; + + /* Create the starting base for those variables not being unravelled */ + (void) set_copy(startbase, cube.emptyset); + for(var = 0; var < start; var++) + (void) set_or(startbase, startbase, cube.var_mask[var]); + for(var = end+1; var < cube.num_vars; var++) + (void) set_or(startbase, startbase, cube.var_mask[var]); + + /* Determine how many cubes it will blow up into */ + total_size = 0; + foreach_set(B, last, p) { + expansion = 1; + for(var = start; var <= end; var++) + if ((size = set_dist(p, cube.var_mask[var])) >= 2) + if ((expansion *= size) > 1000000) + fatal("unreasonable expansion in unravel"); + total_size += expansion; + } + + /* We can now allocate a cover of exactly the correct size */ + B1 = new_cover(total_size); + foreach_set(B, last, p) { + cb_unravel(p, start, end, startbase, B1); + } + free_cover(B); + return B1; +} + + +pcover unravel(B, start) +IN pcover B; +IN int start; +{ + return unravel_range(B, start, cube.num_vars-1); +} + +/* lex_sort -- sort cubes in a standard lexical fashion */ +pcover lex_sort(T) +pcover T; +{ + pcover T1 = sf_unlist(sf_sort(T, lex_order), T->count, T->sf_size); + free_cover(T); + return T1; +} + + +/* size_sort -- sort cubes by their size */ +pcover size_sort(T) +pcover T; +{ + pcover T1 = sf_unlist(sf_sort(T, descend), T->count, T->sf_size); + free_cover(T); + return T1; +} + + +/* mini_sort -- sort cubes according to the heuristics of mini */ +pcover mini_sort(F, compare) +pcover F; +int (*compare)(); +{ + register int *count, cnt, n = cube.size, i; + register pcube p, last; + pcover F_sorted; + pcube *F1; + + /* Perform a column sum over the set family */ + count = sf_count(F); + + /* weight is "inner product of the cube and the column sums" */ + foreach_set(F, last, p) { + cnt = 0; + for(i = 0; i < n; i++) + if (is_in_set(p, i)) + cnt += count[i]; + PUTSIZE(p, cnt); + } + FREE(count); + + /* use qsort to sort the array */ + qsort((char *) (F1 = sf_list(F)), F->count, sizeof(pcube), compare); + F_sorted = sf_unlist(F1, F->count, F->sf_size); + free_cover(F); + + return F_sorted; +} + + +/* sort_reduce -- Espresso strategy for ordering the cubes before reduction */ +pcover sort_reduce(T) +IN pcover T; +{ + register pcube p, last, largest = NULL; + register int bestsize = -1, size, n = cube.num_vars; + pcover T_sorted; + pcube *T1; + + if (T->count == 0) + return T; + + /* find largest cube */ + foreach_set(T, last, p) + if ((size = set_ord(p)) > bestsize) + largest = p, bestsize = size; + + foreach_set(T, last, p) + PUTSIZE(p, ((n - cdist(largest,p)) << 7) + MIN(set_ord(p),127)); + + qsort((char *) (T1 = sf_list(T)), T->count, sizeof(pcube), (int (*)()) descend); + T_sorted = sf_unlist(T1, T->count, T->sf_size); + free_cover(T); + + return T_sorted; +} + +pcover random_order(F) +register pcover F; +{ + pset temp; + register int i, k; +#ifdef RANDOM + long random(); +#endif + + temp = set_new(F->sf_size); + for(i = F->count - 1; i > 0; i--) { + /* Choose a random number between 0 and i */ +#ifdef RANDOM + k = random() % i; +#else + /* this is not meant to be really used; just provides an easy + "out" if random() and srandom() aren't around + */ + k = (i*23 + 997) % i; +#endif + /* swap sets i and k */ + (void) set_copy(temp, GETSET(F, k)); + (void) set_copy(GETSET(F, k), GETSET(F, i)); + (void) set_copy(GETSET(F, i), temp); + } + set_free(temp); + return F; +} + +/* + * cubelist_partition -- take a cubelist T and see if it has any components; + * if so, return cubelist's of the two partitions A and B; the return value + * is the size of the partition; if not, A and B + * are undefined and the return value is 0 + */ +int cubelist_partition(T, A, B, comp_debug) +pcube *T; /* a list of cubes */ +pcube **A, **B; /* cubelist of partition and remainder */ +unsigned int comp_debug; +{ + register pcube *T1, p, seed, cof; + pcube *A1, *B1; + bool change; + int count, numcube; + + numcube = CUBELISTSIZE(T); + + /* Mark all cubes -- covered cubes belong to the partition */ + for(T1 = T+2; (p = *T1++) != NULL; ) { + RESET(p, COVERED); + } + + /* + * Extract a partition from the cubelist T; start with the first cube as a + * seed, and then pull in all cubes which share a variable with the seed; + * iterate until no new cubes are brought into the partition. + */ + seed = set_save(T[2]); + cof = T[0]; + SET(T[2], COVERED); + count = 1; + + do { + change = FALSE; + for(T1 = T+2; (p = *T1++) != NULL; ) { + if (! TESTP(p, COVERED) && ccommon(p, seed, cof)) { + INLINEset_and(seed, seed, p); + SET(p, COVERED); + change = TRUE; + count++; + } + + } + } while (change); + + set_free(seed); + + if (comp_debug) { + (void) printf("COMPONENT_REDUCTION: split into %d %d\n", + count, numcube - count); + } + + if (count != numcube) { + /* Allocate and setup the cubelist's for the two partitions */ + *A = A1 = ALLOC(pcube, numcube+3); + *B = B1 = ALLOC(pcube, numcube+3); + (*A)[0] = set_save(T[0]); + (*B)[0] = set_save(T[0]); + A1 = *A + 2; + B1 = *B + 2; + + /* Loop over the cubes in T and distribute to A and B */ + for(T1 = T+2; (p = *T1++) != NULL; ) { + if (TESTP(p, COVERED)) { + *A1++ = p; + } else { + *B1++ = p; + } + } + + /* Stuff needed at the end of the cubelist's */ + *A1++ = NULL; + (*A)[1] = (pcube) A1; + *B1++ = NULL; + (*B)[1] = (pcube) B1; + } + + return numcube - count; +} + +/* + * quick cofactor against a single output function + */ +pcover cof_output(T, i) +pcover T; +register int i; +{ + pcover T1; + register pcube p, last, pdest, mask; + + mask = cube.var_mask[cube.output]; + T1 = new_cover(T->count); + foreach_set(T, last, p) { + if (is_in_set(p, i)) { + pdest = GETSET(T1, T1->count++); + INLINEset_or(pdest, p, mask); + RESET(pdest, PRIME); + } + } + return T1; +} + + +/* + * quick intersection against a single output function + */ +pcover uncof_output(T, i) +pcover T; +int i; +{ + register pcube p, last, mask; + + if (T == NULL) { + return T; + } + + mask = cube.var_mask[cube.output]; + foreach_set(T, last, p) { + INLINEset_diff(p, p, mask); + set_insert(p, i); + } + return T; +} + + +/* + * A generic routine to perform an operation for each output function + * + * func() is called with a PLA for each output function (with the output + * part effectively removed). + * func1() is called after reforming the equivalent output function + * + * Each function returns TRUE if process is to continue + */ +foreach_output_function(PLA, func, func1) +pPLA PLA; +int (*func)(); +int (*func1)(); +{ + pPLA PLA1; + int i; + + /* Loop for each output function */ + for(i = 0; i < cube.part_size[cube.output]; i++) { + + /* cofactor on the output part */ + PLA1 = new_PLA(); + PLA1->F = cof_output(PLA->F, i + cube.first_part[cube.output]); + PLA1->R = cof_output(PLA->R, i + cube.first_part[cube.output]); + PLA1->D = cof_output(PLA->D, i + cube.first_part[cube.output]); + + /* Call a routine to do something with the cover */ + if ((*func)(PLA1, i) == 0) { + free_PLA(PLA1); + return; + } + + /* intersect with the particular output part again */ + PLA1->F = uncof_output(PLA1->F, i + cube.first_part[cube.output]); + PLA1->R = uncof_output(PLA1->R, i + cube.first_part[cube.output]); + PLA1->D = uncof_output(PLA1->D, i + cube.first_part[cube.output]); + + /* Call a routine to do something with the final result */ + if ((*func1)(PLA1, i) == 0) { + free_PLA(PLA1); + return; + } + + /* Cleanup for next go-around */ + free_PLA(PLA1); + + + } +} + +static pcover Fmin; +static pcube phase; + +/* + * minimize each output function individually + */ +void so_espresso(PLA, strategy) +pPLA PLA; +int strategy; +{ + Fmin = new_cover(PLA->F->count); + if (strategy == 0) { + foreach_output_function(PLA, so_do_espresso, so_save); + } else { + foreach_output_function(PLA, so_do_exact, so_save); + } + sf_free(PLA->F); + PLA->F = Fmin; +} + + +/* + * minimize each output function, choose function or complement based on the + * one with the fewer number of terms + */ +void so_both_espresso(PLA, strategy) +pPLA PLA; +int strategy; +{ + phase = set_save(cube.fullset); + Fmin = new_cover(PLA->F->count); + if (strategy == 0) { + foreach_output_function(PLA, so_both_do_espresso, so_both_save); + } else { + foreach_output_function(PLA, so_both_do_exact, so_both_save); + } + sf_free(PLA->F); + PLA->F = Fmin; + PLA->phase = phase; +} + + +int so_do_espresso(PLA, i) +pPLA PLA; +int i; +{ + char word[32]; + + /* minimize the single-output function (on-set) */ + skip_make_sparse = 1; + (void) sprintf(word, "ESPRESSO-POS(%d)", i); + EXEC_S(PLA->F = espresso(PLA->F, PLA->D, PLA->R), word, PLA->F); + return 1; +} + + +int so_do_exact(PLA, i) +pPLA PLA; +int i; +{ + char word[32]; + + /* minimize the single-output function (on-set) */ + skip_make_sparse = 1; + (void) sprintf(word, "EXACT-POS(%d)", i); + EXEC_S(PLA->F = minimize_exact(PLA->F, PLA->D, PLA->R, 1), word, PLA->F); + return 1; +} + + +/*ARGSUSED*/ +int so_save(PLA, i) +pPLA PLA; +int i; +{ + Fmin = sf_append(Fmin, PLA->F); /* disposes of PLA->F */ + PLA->F = NULL; + return 1; +} + + +int so_both_do_espresso(PLA, i) +pPLA PLA; +int i; +{ + char word[32]; + + /* minimize the single-output function (on-set) */ + (void) sprintf(word, "ESPRESSO-POS(%d)", i); + skip_make_sparse = 1; + EXEC_S(PLA->F = espresso(PLA->F, PLA->D, PLA->R), word, PLA->F); + + /* minimize the single-output function (off-set) */ + (void) sprintf(word, "ESPRESSO-NEG(%d)", i); + skip_make_sparse = 1; + EXEC_S(PLA->R = espresso(PLA->R, PLA->D, PLA->F), word, PLA->R); + + return 1; +} + + +int so_both_do_exact(PLA, i) +pPLA PLA; +int i; +{ + char word[32]; + + /* minimize the single-output function (on-set) */ + (void) sprintf(word, "EXACT-POS(%d)", i); + skip_make_sparse = 1; + EXEC_S(PLA->F = minimize_exact(PLA->F, PLA->D, PLA->R, 1), word, PLA->F); + + /* minimize the single-output function (off-set) */ + (void) sprintf(word, "EXACT-NEG(%d)", i); + skip_make_sparse = 1; + EXEC_S(PLA->R = minimize_exact(PLA->R, PLA->D, PLA->F, 1), word, PLA->R); + + return 1; +} + + +int so_both_save(PLA, i) +pPLA PLA; +int i; +{ + if (PLA->F->count > PLA->R->count) { + sf_free(PLA->F); + PLA->F = PLA->R; + PLA->R = NULL; + i += cube.first_part[cube.output]; + set_remove(phase, i); + } else { + sf_free(PLA->R); + PLA->R = NULL; + } + Fmin = sf_append(Fmin, PLA->F); + PLA->F = NULL; + return 1; +} diff --git a/abc_with_bb_support/src/misc/espresso/cvrmisc.c b/abc_with_bb_support/src/misc/espresso/cvrmisc.c new file mode 100644 index 000000000..85ec6bb1d --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/cvrmisc.c @@ -0,0 +1,142 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +#include "espresso.h" + + +/* cost -- compute the cost of a cover */ +void cover_cost(F, cost) +IN pcover F; +INOUT pcost cost; +{ + register pcube p, last; + pcube *T; + int var; + + /* use the routine used by cofactor to decide splitting variables */ + massive_count(T = cube1list(F)); + free_cubelist(T); + + cost->cubes = F->count; + cost->total = cost->in = cost->out = cost->mv = cost->primes = 0; + + /* Count transistors (zeros) for each binary variable (inputs) */ + for(var = 0; var < cube.num_binary_vars; var++) + cost->in += cdata.var_zeros[var]; + + /* Count transistors for each mv variable based on sparse/dense */ + for(var = cube.num_binary_vars; var < cube.num_vars - 1; var++) + if (cube.sparse[var]) + cost->mv += F->count * cube.part_size[var] - cdata.var_zeros[var]; + else + cost->mv += cdata.var_zeros[var]; + + /* Count the transistors (ones) for the output variable */ + if (cube.num_binary_vars != cube.num_vars) { + var = cube.num_vars - 1; + cost->out = F->count * cube.part_size[var] - cdata.var_zeros[var]; + } + + /* Count the number of nonprime cubes */ + foreach_set(F, last, p) + cost->primes += TESTP(p, PRIME) != 0; + + /* Count the total number of literals */ + cost->total = cost->in + cost->out + cost->mv; +} + + +/* fmt_cost -- return a string which reports the "cost" of a cover */ +char *fmt_cost(cost) +IN pcost cost; +{ + static char s[200]; + + if (cube.num_binary_vars == cube.num_vars - 1) + (void) sprintf(s, "c=%d(%d) in=%d out=%d tot=%d", + cost->cubes, cost->cubes - cost->primes, cost->in, + cost->out, cost->total); + else + (void) sprintf(s, "c=%d(%d) in=%d mv=%d out=%d", + cost->cubes, cost->cubes - cost->primes, cost->in, + cost->mv, cost->out); + return s; +} + + +char *print_cost(F) +IN pcover F; +{ + cost_t cost; + cover_cost(F, &cost); + return fmt_cost(&cost); +} + + +/* copy_cost -- copy a cost function from s to d */ +void copy_cost(s, d) +pcost s, d; +{ + d->cubes = s->cubes; + d->in = s->in; + d->out = s->out; + d->mv = s->mv; + d->total = s->total; + d->primes = s->primes; +} + + +/* size_stamp -- print single line giving the size of a cover */ +void size_stamp(T, name) +IN pcover T; +IN char *name; +{ + (void) printf("# %s\tCost is %s\n", name, print_cost(T)); + (void) fflush(stdout); +} + + +/* print_trace -- print a line reporting size and time after a function */ +void print_trace(T, name, time) +pcover T; +char *name; +long time; +{ + (void) printf("# %s\tTime was %s, cost is %s\n", + name, print_time(time), print_cost(T)); + (void) fflush(stdout); +} + + +/* totals -- add time spent in the function into the totals */ +void totals(time, i, T, cost) +long time; +int i; +pcover T; +pcost cost; +{ + time = ptime() - time; + total_time[i] += time; + total_calls[i]++; + cover_cost(T, cost); + if (trace) { + (void) printf("# %s\tTime was %s, cost is %s\n", + total_name[i], print_time(time), fmt_cost(cost)); + (void) fflush(stdout); + } +} + + +/* fatal -- report fatal error message and take a dive */ +void fatal(s) +char *s; +{ + (void) fprintf(stderr, "espresso: %s\n", s); + exit(1); +} diff --git a/abc_with_bb_support/src/misc/espresso/cvrout.c b/abc_with_bb_support/src/misc/espresso/cvrout.c new file mode 100644 index 000000000..4850fa915 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/cvrout.c @@ -0,0 +1,609 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +/* + module: cvrout.c + purpose: cube and cover output routines +*/ + +#include "espresso.h" + +void fprint_pla(fp, PLA, output_type) +INOUT FILE *fp; +IN pPLA PLA; +IN int output_type; +{ + int num; + register pcube last, p; + + if ((output_type & CONSTRAINTS_type) != 0) { + output_symbolic_constraints(fp, PLA, 0); + output_type &= ~ CONSTRAINTS_type; + if (output_type == 0) { + return; + } + } + + if ((output_type & SYMBOLIC_CONSTRAINTS_type) != 0) { + output_symbolic_constraints(fp, PLA, 1); + output_type &= ~ SYMBOLIC_CONSTRAINTS_type; + if (output_type == 0) { + return; + } + } + + if (output_type == PLEASURE_type) { + pls_output(PLA); + } else if (output_type == EQNTOTT_type) { + eqn_output(PLA); + } else if (output_type == KISS_type) { + kiss_output(fp, PLA); + } else { + fpr_header(fp, PLA, output_type); + + num = 0; + if (output_type & F_type) num += (PLA->F)->count; + if (output_type & D_type) num += (PLA->D)->count; + if (output_type & R_type) num += (PLA->R)->count; + (void) fprintf(fp, ".p %d\n", num); + + /* quick patch 01/17/85 to support TPLA ! */ + if (output_type == F_type) { + foreach_set(PLA->F, last, p) { + print_cube(fp, p, "01"); + } + (void) fprintf(fp, ".e\n"); + } else { + if (output_type & F_type) { + foreach_set(PLA->F, last, p) { + print_cube(fp, p, "~1"); + } + } + if (output_type & D_type) { + foreach_set(PLA->D, last, p) { + print_cube(fp, p, "~2"); + } + } + if (output_type & R_type) { + foreach_set(PLA->R, last, p) { + print_cube(fp, p, "~0"); + } + } + (void) fprintf(fp, ".end\n"); + } + } +} + +void fpr_header(fp, PLA, output_type) +FILE *fp; +pPLA PLA; +int output_type; +{ + register int i, var; + int first, last; + + /* .type keyword gives logical type */ + if (output_type != F_type) { + (void) fprintf(fp, ".type "); + if (output_type & F_type) putc('f', fp); + if (output_type & D_type) putc('d', fp); + if (output_type & R_type) putc('r', fp); + putc('\n', fp); + } + + /* Check for binary or multiple-valued labels */ + if (cube.num_mv_vars <= 1) { + (void) fprintf(fp, ".i %d\n", cube.num_binary_vars); + if (cube.output != -1) + (void) fprintf(fp, ".o %d\n", cube.part_size[cube.output]); + } else { + (void) fprintf(fp, ".mv %d %d", cube.num_vars, cube.num_binary_vars); + for(var = cube.num_binary_vars; var < cube.num_vars; var++) + (void) fprintf(fp, " %d", cube.part_size[var]); + (void) fprintf(fp, "\n"); + } + + /* binary valued labels */ + if (PLA->label != NIL(char *) && PLA->label[1] != NIL(char) + && cube.num_binary_vars > 0) { + (void) fprintf(fp, ".ilb"); + for(var = 0; var < cube.num_binary_vars; var++) + /* see (NIL) OUTLABELS comment below */ + if(INLABEL(var) == NIL(char)){ + (void) fprintf(fp, " (null)"); + } + else{ + (void) fprintf(fp, " %s", INLABEL(var)); + } + putc('\n', fp); + } + + /* output-part (last multiple-valued variable) labels */ + if (PLA->label != NIL(char *) && + PLA->label[cube.first_part[cube.output]] != NIL(char) + && cube.output != -1) { + (void) fprintf(fp, ".ob"); + for(i = 0; i < cube.part_size[cube.output]; i++) + /* (NIL) OUTLABELS caused espresso to segfault under solaris */ + if(OUTLABEL(i) == NIL(char)){ + (void) fprintf(fp, " (null)"); + } + else{ + (void) fprintf(fp, " %s", OUTLABEL(i)); + } + putc('\n', fp); + } + + /* multiple-valued labels */ + for(var = cube.num_binary_vars; var < cube.num_vars-1; var++) { + first = cube.first_part[var]; + last = cube.last_part[var]; + if (PLA->label != NULL && PLA->label[first] != NULL) { + (void) fprintf(fp, ".label var=%d", var); + for(i = first; i <= last; i++) { + (void) fprintf(fp, " %s", PLA->label[i]); + } + putc('\n', fp); + } + } + + if (PLA->phase != (pcube) NULL) { + first = cube.first_part[cube.output]; + last = cube.last_part[cube.output]; + (void) fprintf(fp, "#.phase "); + for(i = first; i <= last; i++) + putc(is_in_set(PLA->phase,i) ? '1' : '0', fp); + (void) fprintf(fp, "\n"); + } +} + +void pls_output(PLA) +IN pPLA PLA; +{ + register pcube last, p; + + (void) printf(".option unmerged\n"); + makeup_labels(PLA); + pls_label(PLA, stdout); + pls_group(PLA, stdout); + (void) printf(".p %d\n", PLA->F->count); + foreach_set(PLA->F, last, p) { + print_expanded_cube(stdout, p, PLA->phase); + } + (void) printf(".end\n"); +} + + +void pls_group(PLA, fp) +pPLA PLA; +FILE *fp; +{ + int var, i, col, len; + + (void) fprintf(fp, "\n.group"); + col = 6; + for(var = 0; var < cube.num_vars-1; var++) { + (void) fprintf(fp, " ("), col += 2; + for(i = cube.first_part[var]; i <= cube.last_part[var]; i++) { + len = strlen(PLA->label[i]); + if (col + len > 75) + (void) fprintf(fp, " \\\n"), col = 0; + else if (i != 0) + putc(' ', fp), col += 1; + (void) fprintf(fp, "%s", PLA->label[i]), col += len; + } + (void) fprintf(fp, ")"), col += 1; + } + (void) fprintf(fp, "\n"); +} + + +void pls_label(PLA, fp) +pPLA PLA; +FILE *fp; +{ + int var, i, col, len; + + (void) fprintf(fp, ".label"); + col = 6; + for(var = 0; var < cube.num_vars; var++) + for(i = cube.first_part[var]; i <= cube.last_part[var]; i++) { + len = strlen(PLA->label[i]); + if (col + len > 75) + (void) fprintf(fp, " \\\n"), col = 0; + else + putc(' ', fp), col += 1; + (void) fprintf(fp, "%s", PLA->label[i]), col += len; + } +} + + + +/* + eqntott output mode -- output algebraic equations +*/ +void eqn_output(PLA) +pPLA PLA; +{ + register pcube p, last; + register int i, var, col, len; + int x; + bool firstand, firstor; + + if (cube.output == -1) + fatal("Cannot have no-output function for EQNTOTT output mode"); + if (cube.num_mv_vars != 1) + fatal("Must have binary-valued function for EQNTOTT output mode"); + makeup_labels(PLA); + + /* Write a single equation for each output */ + for(i = 0; i < cube.part_size[cube.output]; i++) { + (void) printf("%s = ", OUTLABEL(i)); + col = strlen(OUTLABEL(i)) + 3; + firstor = TRUE; + + /* Write product terms for each cube in this output */ + foreach_set(PLA->F, last, p) + if (is_in_set(p, i + cube.first_part[cube.output])) { + if (firstor) + (void) printf("("), col += 1; + else + (void) printf(" | ("), col += 4; + firstor = FALSE; + firstand = TRUE; + + /* print out a product term */ + for(var = 0; var < cube.num_binary_vars; var++) + if ((x=GETINPUT(p, var)) != DASH) { + len = strlen(INLABEL(var)); + if (col+len > 72) + (void) printf("\n "), col = 4; + if (! firstand) + (void) printf("&"), col += 1; + firstand = FALSE; + if (x == ZERO) + (void) printf("!"), col += 1; + (void) printf("%s", INLABEL(var)), col += len; + } + (void) printf(")"), col += 1; + } + (void) printf(";\n\n"); + } +} + + +char *fmt_cube(c, out_map, s) +register pcube c; +register char *out_map, *s; +{ + register int i, var, last, len = 0; + + for(var = 0; var < cube.num_binary_vars; var++) { + s[len++] = "?01-" [GETINPUT(c, var)]; + } + for(var = cube.num_binary_vars; var < cube.num_vars - 1; var++) { + s[len++] = ' '; + for(i = cube.first_part[var]; i <= cube.last_part[var]; i++) { + s[len++] = "01" [is_in_set(c, i) != 0]; + } + } + if (cube.output != -1) { + last = cube.last_part[cube.output]; + s[len++] = ' '; + for(i = cube.first_part[cube.output]; i <= last; i++) { + s[len++] = out_map [is_in_set(c, i) != 0]; + } + } + s[len] = '\0'; + return s; +} + + +void print_cube(fp, c, out_map) +register FILE *fp; +register pcube c; +register char *out_map; +{ + register int i, var, ch; + int last; + + for(var = 0; var < cube.num_binary_vars; var++) { + ch = "?01-" [GETINPUT(c, var)]; + putc(ch, fp); + } + for(var = cube.num_binary_vars; var < cube.num_vars - 1; var++) { + putc(' ', fp); + for(i = cube.first_part[var]; i <= cube.last_part[var]; i++) { + ch = "01" [is_in_set(c, i) != 0]; + putc(ch, fp); + } + } + if (cube.output != -1) { + last = cube.last_part[cube.output]; + putc(' ', fp); + for(i = cube.first_part[cube.output]; i <= last; i++) { + ch = out_map [is_in_set(c, i) != 0]; + putc(ch, fp); + } + } + putc('\n', fp); +} + + +void print_expanded_cube(fp, c, phase) +register FILE *fp; +register pcube c; +pcube phase; +{ + register int i, var, ch; + char *out_map; + + for(var = 0; var < cube.num_binary_vars; var++) { + for(i = cube.first_part[var]; i <= cube.last_part[var]; i++) { + ch = "~1" [is_in_set(c, i) != 0]; + putc(ch, fp); + } + } + for(var = cube.num_binary_vars; var < cube.num_vars - 1; var++) { + for(i = cube.first_part[var]; i <= cube.last_part[var]; i++) { + ch = "1~" [is_in_set(c, i) != 0]; + putc(ch, fp); + } + } + if (cube.output != -1) { + var = cube.num_vars - 1; + putc(' ', fp); + for(i = cube.first_part[var]; i <= cube.last_part[var]; i++) { + if (phase == (pcube) NULL || is_in_set(phase, i)) { + out_map = "~1"; + } else { + out_map = "~0"; + } + ch = out_map[is_in_set(c, i) != 0]; + putc(ch, fp); + } + } + putc('\n', fp); +} + + +char *pc1(c) pcube c; +{static char s1[256];return fmt_cube(c, "01", s1);} +char *pc2(c) pcube c; +{static char s2[256];return fmt_cube(c, "01", s2);} + + +void debug_print(T, name, level) +pcube *T; +char *name; +int level; +{ + register pcube *T1, p, temp; + register int cnt; + + cnt = CUBELISTSIZE(T); + temp = new_cube(); + if (verbose_debug && level == 0) + (void) printf("\n"); + (void) printf("%s[%d]: ord(T)=%d\n", name, level, cnt); + if (verbose_debug) { + (void) printf("cofactor=%s\n", pc1(T[0])); + for(T1 = T+2, cnt = 1; (p = *T1++) != (pcube) NULL; cnt++) + (void) printf("%4d. %s\n", cnt, pc1(set_or(temp, p, T[0]))); + } + free_cube(temp); +} + + +void debug1_print(T, name, num) +pcover T; +char *name; +int num; +{ + register int cnt = 1; + register pcube p, last; + + if (verbose_debug && num == 0) + (void) printf("\n"); + (void) printf("%s[%d]: ord(T)=%d\n", name, num, T->count); + if (verbose_debug) + foreach_set(T, last, p) + (void) printf("%4d. %s\n", cnt++, pc1(p)); +} + + +void cprint(T) +pcover T; +{ + register pcube p, last; + + foreach_set(T, last, p) + (void) printf("%s\n", pc1(p)); +} + + +int makeup_labels(PLA) +pPLA PLA; +{ + int var, i, ind; + + if (PLA->label == (char **) NULL) + PLA_labels(PLA); + + for(var = 0; var < cube.num_vars; var++) + for(i = 0; i < cube.part_size[var]; i++) { + ind = cube.first_part[var] + i; + if (PLA->label[ind] == (char *) NULL) { + PLA->label[ind] = ALLOC(char, 15); + if (var < cube.num_binary_vars) + if ((i % 2) == 0) + (void) sprintf(PLA->label[ind], "v%d.bar", var); + else + (void) sprintf(PLA->label[ind], "v%d", var); + else + (void) sprintf(PLA->label[ind], "v%d.%d", var, i); + } + } +} + + +kiss_output(fp, PLA) +FILE *fp; +pPLA PLA; +{ + register pset last, p; + + foreach_set(PLA->F, last, p) { + kiss_print_cube(fp, PLA, p, "~1"); + } + foreach_set(PLA->D, last, p) { + kiss_print_cube(fp, PLA, p, "~2"); + } +} + + +kiss_print_cube(fp, PLA, p, out_string) +FILE *fp; +pPLA PLA; +pcube p; +char *out_string; +{ + register int i, var; + int part, x; + + for(var = 0; var < cube.num_binary_vars; var++) { + x = "?01-" [GETINPUT(p, var)]; + putc(x, fp); + } + + for(var = cube.num_binary_vars; var < cube.num_vars - 1; var++) { + putc(' ', fp); + if (setp_implies(cube.var_mask[var], p)) { + putc('-', fp); + } else { + part = -1; + for(i = cube.first_part[var]; i <= cube.last_part[var]; i++) { + if (is_in_set(p, i)) { + if (part != -1) { + fatal("more than 1 part in a symbolic variable\n"); + } + part = i; + } + } + if (part == -1) { + putc('~', fp); /* no parts, hope its an output ... */ + } else { + (void) fputs(PLA->label[part], fp); + } + } + } + + if ((var = cube.output) != -1) { + putc(' ', fp); + for(i = cube.first_part[var]; i <= cube.last_part[var]; i++) { + x = out_string [is_in_set(p, i) != 0]; + putc(x, fp); + } + } + + putc('\n', fp); +} + +output_symbolic_constraints(fp, PLA, output_symbolic) +FILE *fp; +pPLA PLA; +int output_symbolic; +{ + pset_family A; + register int i, j; + int size, var, npermute, *permute, *weight, noweight; + + if ((cube.num_vars - cube.num_binary_vars) <= 1) { + return; + } + makeup_labels(PLA); + + for(var=cube.num_binary_vars; var < cube.num_vars-1; var++) { + + /* pull out the columns for variable "var" */ + npermute = cube.part_size[var]; + permute = ALLOC(int, npermute); + for(i=0; i < npermute; i++) { + permute[i] = cube.first_part[var] + i; + } + A = sf_permute(sf_save(PLA->F), permute, npermute); + FREE(permute); + + + /* Delete the singletons and the full sets */ + noweight = 0; + for(i = 0; i < A->count; i++) { + size = set_ord(GETSET(A,i)); + if (size == 1 || size == A->sf_size) { + sf_delset(A, i--); + noweight++; + } + } + + + /* Count how many times each is duplicated */ + weight = ALLOC(int, A->count); + for(i = 0; i < A->count; i++) { + RESET(GETSET(A, i), COVERED); + } + for(i = 0; i < A->count; i++) { + weight[i] = 0; + if (! TESTP(GETSET(A,i), COVERED)) { + weight[i] = 1; + for(j = i+1; j < A->count; j++) { + if (setp_equal(GETSET(A,i), GETSET(A,j))) { + weight[i]++; + SET(GETSET(A,j), COVERED); + } + } + } + } + + + /* Print out the contraints */ + if (! output_symbolic) { + (void) fprintf(fp, + "# Symbolic constraints for variable %d (Numeric form)\n", var); + (void) fprintf(fp, "# unconstrained weight = %d\n", noweight); + (void) fprintf(fp, "num_codes=%d\n", cube.part_size[var]); + for(i = 0; i < A->count; i++) { + if (weight[i] > 0) { + (void) fprintf(fp, "weight=%d: ", weight[i]); + for(j = 0; j < A->sf_size; j++) { + if (is_in_set(GETSET(A,i), j)) { + (void) fprintf(fp, " %d", j); + } + } + (void) fprintf(fp, "\n"); + } + } + } else { + (void) fprintf(fp, + "# Symbolic constraints for variable %d (Symbolic form)\n", var); + for(i = 0; i < A->count; i++) { + if (weight[i] > 0) { + (void) fprintf(fp, "# w=%d: (", weight[i]); + for(j = 0; j < A->sf_size; j++) { + if (is_in_set(GETSET(A,i), j)) { + (void) fprintf(fp, " %s", + PLA->label[cube.first_part[var]+j]); + } + } + (void) fprintf(fp, " )\n"); + } + } + FREE(weight); + } + } +} diff --git a/abc_with_bb_support/src/misc/espresso/dominate.c b/abc_with_bb_support/src/misc/espresso/dominate.c new file mode 100644 index 000000000..b045c6f4e --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/dominate.c @@ -0,0 +1,98 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +#include "mincov_int.h" + + +int +sm_row_dominance(A) +sm_matrix *A; +{ + register sm_row *prow, *prow1; + register sm_col *pcol, *least_col; + register sm_element *p, *pnext; + int rowcnt; + + rowcnt = A->nrows; + + /* Check each row against all other rows */ + for(prow = A->first_row; prow != 0; prow = prow->next_row) { + + /* Among all columns with a 1 in this row, choose smallest */ + least_col = sm_get_col(A, prow->first_col->col_num); + for(p = prow->first_col->next_col; p != 0; p = p->next_col) { + pcol = sm_get_col(A, p->col_num); + if (pcol->length < least_col->length) { + least_col = pcol; + } + } + + /* Only check for containment against rows in this column */ + for(p = least_col->first_row; p != 0; p = pnext) { + pnext = p->next_row; + + prow1 = sm_get_row(A, p->row_num); + if ((prow1->length > prow->length) || + (prow1->length == prow->length && + prow1->row_num > prow->row_num)) { + if (sm_row_contains(prow, prow1)) { + sm_delrow(A, prow1->row_num); + } + } + } + } + + return rowcnt - A->nrows; +} + +int +sm_col_dominance(A, weight) +sm_matrix *A; +int *weight; +{ + register sm_row *prow; + register sm_col *pcol, *pcol1; + register sm_element *p; + sm_row *least_row; + sm_col *next_col; + int colcnt; + + colcnt = A->ncols; + + /* Check each column against all other columns */ + for(pcol = A->first_col; pcol != 0; pcol = next_col) { + next_col = pcol->next_col; + + /* Check all rows to find the one with fewest elements */ + least_row = sm_get_row(A, pcol->first_row->row_num); + for(p = pcol->first_row->next_row; p != 0; p = p->next_row) { + prow = sm_get_row(A, p->row_num); + if (prow->length < least_row->length) { + least_row = prow; + } + } + + /* Only check for containment against columns in this row */ + for(p = least_row->first_col; p != 0; p = p->next_col) { + pcol1 = sm_get_col(A, p->col_num); + if (weight != 0 && weight[pcol1->col_num] > weight[pcol->col_num]) + continue; + if ((pcol1->length > pcol->length) || + (pcol1->length == pcol->length && + pcol1->col_num > pcol->col_num)) { + if (sm_col_contains(pcol, pcol1)) { + sm_delcol(A, pcol->col_num); + break; + } + } + } + } + + return colcnt - A->ncols; +} diff --git a/abc_with_bb_support/src/misc/espresso/equiv.c b/abc_with_bb_support/src/misc/espresso/equiv.c new file mode 100644 index 000000000..11a7b60b0 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/equiv.c @@ -0,0 +1,94 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +#include "espresso.h" + + +find_equiv_outputs(PLA) +pPLA PLA; +{ + int i, j, ipart, jpart, some_equiv; + pcover *R, *F; + + some_equiv = FALSE; + + makeup_labels(PLA); + + F = ALLOC(pcover, cube.part_size[cube.output]); + R = ALLOC(pcover, cube.part_size[cube.output]); + + for(i = 0; i < cube.part_size[cube.output]; i++) { + ipart = cube.first_part[cube.output] + i; + R[i] = cof_output(PLA->R, ipart); + F[i] = complement(cube1list(R[i])); + } + + for(i = 0; i < cube.part_size[cube.output]-1; i++) { + for(j = i+1; j < cube.part_size[cube.output]; j++) { + ipart = cube.first_part[cube.output] + i; + jpart = cube.first_part[cube.output] + j; + + if (check_equiv(F[i], F[j])) { + (void) printf("# Outputs %d and %d (%s and %s) are equivalent\n", + i, j, PLA->label[ipart], PLA->label[jpart]); + some_equiv = TRUE; + } else if (check_equiv(F[i], R[j])) { + (void) printf("# Outputs %d and NOT %d (%s and %s) are equivalent\n", + i, j, PLA->label[ipart], PLA->label[jpart]); + some_equiv = TRUE; + } else if (check_equiv(R[i], F[j])) { + (void) printf("# Outputs NOT %d and %d (%s and %s) are equivalent\n", + i, j, PLA->label[ipart], PLA->label[jpart]); + some_equiv = TRUE; + } else if (check_equiv(R[i], R[j])) { + (void) printf("# Outputs NOT %d and NOT %d (%s and %s) are equivalent\n", + i, j, PLA->label[ipart], PLA->label[jpart]); + some_equiv = TRUE; + } + } + } + + if (! some_equiv) { + (void) printf("# No outputs are equivalent\n"); + } + + for(i = 0; i < cube.part_size[cube.output]; i++) { + free_cover(F[i]); + free_cover(R[i]); + } + FREE(F); + FREE(R); +} + + + +int check_equiv(f1, f2) +pcover f1, f2; +{ + register pcube *f1list, *f2list; + register pcube p, last; + + f1list = cube1list(f1); + foreach_set(f2, last, p) { + if (! cube_is_covered(f1list, p)) { + return FALSE; + } + } + free_cubelist(f1list); + + f2list = cube1list(f2); + foreach_set(f1, last, p) { + if (! cube_is_covered(f2list, p)) { + return FALSE; + } + } + free_cubelist(f2list); + + return TRUE; +} diff --git a/abc_with_bb_support/src/misc/espresso/espresso.c b/abc_with_bb_support/src/misc/espresso/espresso.c new file mode 100644 index 000000000..c63f3bfa4 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/espresso.c @@ -0,0 +1,139 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +/* + * Module: espresso.c + * Purpose: The main espresso algorithm + * + * Returns a minimized version of the ON-set of a function + * + * The following global variables affect the operation of Espresso: + * + * MISCELLANEOUS: + * trace + * print trace information as the minimization progresses + * + * remove_essential + * remove essential primes + * + * single_expand + * if true, stop after first expand/irredundant + * + * LAST_GASP or SUPER_GASP strategy: + * use_super_gasp + * uses the super_gasp strategy rather than last_gasp + * + * SETUP strategy: + * recompute_onset + * recompute onset using the complement before starting + * + * unwrap_onset + * unwrap the function output part before first expand + * + * MAKE_SPARSE strategy: + * force_irredundant + * iterates make_sparse to force a minimal solution (used + * indirectly by make_sparse) + * + * skip_make_sparse + * skip the make_sparse step (used by opo only) + */ + +#include "espresso.h" + +pcover espresso(F, D1, R) +pcover F, D1, R; +{ + pcover E, D, Fsave; + pset last, p; + cost_t cost, best_cost; + +begin: + Fsave = sf_save(F); /* save original function */ + D = sf_save(D1); /* make a scratch copy of D */ + + /* Setup has always been a problem */ + if (recompute_onset) { + EXEC(E = simplify(cube1list(F)), "SIMPLIFY ", E); + free_cover(F); + F = E; + } + cover_cost(F, &cost); + if (unwrap_onset && (cube.part_size[cube.num_vars - 1] > 1) + && (cost.out != cost.cubes*cube.part_size[cube.num_vars-1]) + && (cost.out < 5000)) + EXEC(F = sf_contain(unravel(F, cube.num_vars - 1)), "SETUP ", F); + + /* Initial expand and irredundant */ + foreach_set(F, last, p) { + RESET(p, PRIME); + } + EXECUTE(F = expand(F, R, FALSE), EXPAND_TIME, F, cost); + EXECUTE(F = irredundant(F, D), IRRED_TIME, F, cost); + + if (! single_expand) { + if (remove_essential) { + EXECUTE(E = essential(&F, &D), ESSEN_TIME, E, cost); + } else { + E = new_cover(0); + } + + cover_cost(F, &cost); + do { + + /* Repeat inner loop until solution becomes "stable" */ + do { + copy_cost(&cost, &best_cost); + EXECUTE(F = reduce(F, D), REDUCE_TIME, F, cost); + EXECUTE(F = expand(F, R, FALSE), EXPAND_TIME, F, cost); + EXECUTE(F = irredundant(F, D), IRRED_TIME, F, cost); + } while (cost.cubes < best_cost.cubes); + + /* Perturb solution to see if we can continue to iterate */ + copy_cost(&cost, &best_cost); + if (use_super_gasp) { + F = super_gasp(F, D, R, &cost); + if (cost.cubes >= best_cost.cubes) + break; + } else { + F = last_gasp(F, D, R, &cost); + } + + } while (cost.cubes < best_cost.cubes || + (cost.cubes == best_cost.cubes && cost.total < best_cost.total)); + + /* Append the essential cubes to F */ + F = sf_append(F, E); /* disposes of E */ + if (trace) size_stamp(F, "ADJUST "); + } + + /* Free the D which we used */ + free_cover(D); + + /* Attempt to make the PLA matrix sparse */ + if (! skip_make_sparse) { + F = make_sparse(F, D1, R); + } + + /* + * Check to make sure function is actually smaller !! + * This can only happen because of the initial unravel. If we fail, + * then run the whole thing again without the unravel. + */ + if (Fsave->count < F->count) { + free_cover(F); + F = Fsave; + unwrap_onset = FALSE; + goto begin; + } else { + free_cover(Fsave); + } + + return F; +} diff --git a/abc_with_bb_support/src/misc/espresso/espresso.h b/abc_with_bb_support/src/misc/espresso/espresso.h new file mode 100644 index 000000000..11e935ee2 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/espresso.h @@ -0,0 +1,782 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +/* + * espresso.h -- header file for Espresso-mv + */ + +//#include "port.h" +//#include "utility.h" +#include "sparse.h" +#include "mincov.h" + +#include "util_hack.h" // added + +#define ptime() util_cpu_time() +#define print_time(t) util_print_time(t) + +#ifdef IBM_WATC +#define void int +#include "short.h" +#endif + +#ifdef IBMPC /* set default options for IBM/PC */ +#define NO_INLINE +#define BPI 16 +#endif + +/*-----THIS USED TO BE set.h----- */ + +/* + * set.h -- definitions for packed arrays of bits + * + * This header file describes the data structures which comprise a + * facility for efficiently implementing packed arrays of bits + * (otherwise known as sets, cf. Pascal). + * + * A set is a vector of bits and is implemented here as an array of + * unsigned integers. The low order bits of set[0] give the index of + * the last word of set data. The higher order bits of set[0] are + * used to store data associated with the set. The set data is + * contained in elements set[1] ... set[LOOP(set)] as a packed bit + * array. + * + * A family of sets is a two-dimensional matrix of bits and is + * implemented with the data type "set_family". + * + * BPI == 32 and BPI == 16 have been tested and work. + */ + + +/* Define host machine characteristics of "unsigned int" */ +#ifndef BPI +#define BPI 32 /* # bits per integer */ +#endif + +#if BPI == 32 +#define LOGBPI 5 /* log(BPI)/log(2) */ +#else +#define LOGBPI 4 /* log(BPI)/log(2) */ +#endif + +/* Define the set type */ +typedef unsigned int *pset; + +/* Define the set family type -- an array of sets */ +typedef struct set_family { + int wsize; /* Size of each set in 'ints' */ + int sf_size; /* User declared set size */ + int capacity; /* Number of sets allocated */ + int count; /* The number of sets in the family */ + int active_count; /* Number of "active" sets */ + pset data; /* Pointer to the set data */ + struct set_family *next; /* For garbage collection */ +} set_family_t, *pset_family; + +/* Macros to set and test single elements */ +#define WHICH_WORD(element) (((element) >> LOGBPI) + 1) +#define WHICH_BIT(element) ((element) & (BPI-1)) + +/* # of ints needed to allocate a set with "size" elements */ +#if BPI == 32 +#define SET_SIZE(size) ((size) <= BPI ? 2 : (WHICH_WORD((size)-1) + 1)) +#else +#define SET_SIZE(size) ((size) <= BPI ? 3 : (WHICH_WORD((size)-1) + 2)) +#endif + +/* + * Three fields are maintained in the first word of the set + * LOOP is the index of the last word used for set data + * LOOPCOPY is the index of the last word in the set + * SIZE is available for general use (e.g., recording # elements in set) + * NELEM retrieves the number of elements in the set + */ +#define LOOP(set) (set[0] & 0x03ff) +#define PUTLOOP(set, i) (set[0] &= ~0x03ff, set[0] |= (i)) +#if BPI == 32 +#define LOOPCOPY(set) LOOP(set) +#define SIZE(set) (set[0] >> 16) +#define PUTSIZE(set, size) (set[0] &= 0xffff, set[0] |= ((size) << 16)) +#else +#define LOOPCOPY(set) (LOOP(set) + 1) +#define SIZE(set) (set[LOOP(set)+1]) +#define PUTSIZE(set, size) ((set[LOOP(set)+1]) = (size)) +#endif + +#define NELEM(set) (BPI * LOOP(set)) +#define LOOPINIT(size) ((size <= BPI) ? 1 : WHICH_WORD((size)-1)) + +/* + * FLAGS store general information about the set + */ +#define SET(set, flag) (set[0] |= (flag)) +#define RESET(set, flag) (set[0] &= ~ (flag)) +#define TESTP(set, flag) (set[0] & (flag)) + +/* Flag definitions are ... */ +#define PRIME 0x8000 /* cube is prime */ +#define NONESSEN 0x4000 /* cube cannot be essential prime */ +#define ACTIVE 0x2000 /* cube is still active */ +#define REDUND 0x1000 /* cube is redundant(at this point) */ +#define COVERED 0x0800 /* cube has been covered */ +#define RELESSEN 0x0400 /* cube is relatively essential */ + +/* Most efficient way to look at all members of a set family */ +#define foreach_set(R, last, p)\ + for(p=R->data,last=p+R->count*R->wsize;pwsize) +#define foreach_remaining_set(R, last, pfirst, p)\ + for(p=pfirst+R->wsize,last=R->data+R->count*R->wsize;pwsize) +#define foreach_active_set(R, last, p)\ + foreach_set(R,last,p) if (TESTP(p, ACTIVE)) + +/* Another way that also keeps the index of the current set member in i */ +#define foreachi_set(R, i, p)\ + for(p=R->data,i=0;icount;p+=R->wsize,i++) +#define foreachi_active_set(R, i, p)\ + foreachi_set(R,i,p) if (TESTP(p, ACTIVE)) + +/* Looping over all elements in a set: + * foreach_set_element(pset p, int i, unsigned val, int base) { + * . + * . + * . + * } + */ +#define foreach_set_element(p, i, val, base) \ + for(i = LOOP(p); i > 0; ) \ + for(val = p[i], base = --i << LOGBPI; val != 0; base++, val >>= 1) \ + if (val & 1) + +/* Return a pointer to a given member of a set family */ +#define GETSET(family, index) ((family)->data + (family)->wsize * (index)) + +/* Allocate and deallocate sets */ +#define set_new(size) set_clear(ALLOC(unsigned int, SET_SIZE(size)), size) +#define set_full(size) set_fill(ALLOC(unsigned int, SET_SIZE(size)), size) +#define set_save(r) set_copy(ALLOC(unsigned int, SET_SIZE(NELEM(r))), r) +#define set_free(r) FREE(r) + +/* Check for set membership, remove set element and insert set element */ +#define is_in_set(set, e) (set[WHICH_WORD(e)] & (1 << WHICH_BIT(e))) +#define set_remove(set, e) (set[WHICH_WORD(e)] &= ~ (1 << WHICH_BIT(e))) +#define set_insert(set, e) (set[WHICH_WORD(e)] |= 1 << WHICH_BIT(e)) + +/* Inline code substitution for those places that REALLY need it on a VAX */ +#ifdef NO_INLINE +#define INLINEset_copy(r, a) (void) set_copy(r,a) +#define INLINEset_clear(r, size) (void) set_clear(r, size) +#define INLINEset_fill(r, size) (void) set_fill(r, size) +#define INLINEset_and(r, a, b) (void) set_and(r, a, b) +#define INLINEset_or(r, a, b) (void) set_or(r, a, b) +#define INLINEset_diff(r, a, b) (void) set_diff(r, a, b) +#define INLINEset_ndiff(r, a, b, f) (void) set_ndiff(r, a, b, f) +#define INLINEset_xor(r, a, b) (void) set_xor(r, a, b) +#define INLINEset_xnor(r, a, b, f) (void) set_xnor(r, a, b, f) +#define INLINEset_merge(r, a, b, mask) (void) set_merge(r, a, b, mask) +#define INLINEsetp_implies(a, b, when_false) \ + if (! setp_implies(a,b)) when_false +#define INLINEsetp_disjoint(a, b, when_false) \ + if (! setp_disjoint(a,b)) when_false +#define INLINEsetp_equal(a, b, when_false) \ + if (! setp_equal(a,b)) when_false + +#else + +#define INLINEset_copy(r, a)\ + {register int i_=LOOPCOPY(a); do r[i_]=a[i_]; while (--i_>=0);} +#define INLINEset_clear(r, size)\ + {register int i_=LOOPINIT(size); *r=i_; do r[i_] = 0; while (--i_ > 0);} +#define INLINEset_fill(r, size)\ + {register int i_=LOOPINIT(size); *r=i_; \ + r[i_]=((unsigned int)(~0))>>(i_*BPI-size); while(--i_>0) r[i_]=~0;} +#define INLINEset_and(r, a, b)\ + {register int i_=LOOP(a); PUTLOOP(r,i_);\ + do r[i_] = a[i_] & b[i_]; while (--i_>0);} +#define INLINEset_or(r, a, b)\ + {register int i_=LOOP(a); PUTLOOP(r,i_);\ + do r[i_] = a[i_] | b[i_]; while (--i_>0);} +#define INLINEset_diff(r, a, b)\ + {register int i_=LOOP(a); PUTLOOP(r,i_);\ + do r[i_] = a[i_] & ~ b[i_]; while (--i_>0);} +#define INLINEset_ndiff(r, a, b, fullset)\ + {register int i_=LOOP(a); PUTLOOP(r,i_);\ + do r[i_] = fullset[i_] & (a[i_] | ~ b[i_]); while (--i_>0);} +#ifdef IBM_WATC +#define INLINEset_xor(r, a, b) (void) set_xor(r, a, b) +#define INLINEset_xnor(r, a, b, f) (void) set_xnor(r, a, b, f) +#else +#define INLINEset_xor(r, a, b)\ + {register int i_=LOOP(a); PUTLOOP(r,i_);\ + do r[i_] = a[i_] ^ b[i_]; while (--i_>0);} +#define INLINEset_xnor(r, a, b, fullset)\ + {register int i_=LOOP(a); PUTLOOP(r,i_);\ + do r[i_] = fullset[i_] & ~ (a[i_] ^ b[i_]); while (--i_>0);} +#endif +#define INLINEset_merge(r, a, b, mask)\ + {register int i_=LOOP(a); PUTLOOP(r,i_);\ + do r[i_] = (a[i_]&mask[i_]) | (b[i_]&~mask[i_]); while (--i_>0);} +#define INLINEsetp_implies(a, b, when_false)\ + {register int i_=LOOP(a); do if (a[i_]&~b[i_]) break; while (--i_>0);\ + if (i_ != 0) when_false;} +#define INLINEsetp_disjoint(a, b, when_false)\ + {register int i_=LOOP(a); do if (a[i_]&b[i_]) break; while (--i_>0);\ + if (i_ != 0) when_false;} +#define INLINEsetp_equal(a, b, when_false)\ + {register int i_=LOOP(a); do if (a[i_]!=b[i_]) break; while (--i_>0);\ + if (i_ != 0) when_false;} + +#endif + +#if BPI == 32 +#define count_ones(v)\ + (bit_count[v & 255] + bit_count[(v >> 8) & 255]\ + + bit_count[(v >> 16) & 255] + bit_count[(v >> 24) & 255]) +#else +#define count_ones(v) (bit_count[v & 255] + bit_count[(v >> 8) & 255]) +#endif + +/* Table for efficient bit counting */ +extern int bit_count[256]; +/*----- END OF set.h ----- */ + + +/* Define a boolean type */ +#define bool int +#define FALSE 0 +#define TRUE 1 +#define MAYBE 2 +#define print_bool(x) ((x) == 0 ? "FALSE" : ((x) == 1 ? "TRUE" : "MAYBE")) + +/* Map many cube/cover types/routines into equivalent set types/routines */ +#define pcube pset +#define new_cube() set_new(cube.size) +#define free_cube(r) set_free(r) +#define pcover pset_family +#define new_cover(i) sf_new(i, cube.size) +#define free_cover(r) sf_free(r) +#define free_cubelist(T) FREE(T[0]); FREE(T); + + +/* cost_t describes the cost of a cover */ +typedef struct cost_struct { + int cubes; /* number of cubes in the cover */ + int in; /* transistor count, binary-valued variables */ + int out; /* transistor count, output part */ + int mv; /* transistor count, multiple-valued vars */ + int total; /* total number of transistors */ + int primes; /* number of prime cubes */ +} cost_t, *pcost; + + +/* pair_t describes bit-paired variables */ +typedef struct pair_struct { + int cnt; + int *var1; + int *var2; +} pair_t, *ppair; + + +/* symbolic_list_t describes a single ".symbolic" line */ +typedef struct symbolic_list_struct { + int variable; + int pos; + struct symbolic_list_struct *next; +} symbolic_list_t; + + +/* symbolic_list_t describes a single ".symbolic" line */ +typedef struct symbolic_label_struct { + char *label; + struct symbolic_label_struct *next; +} symbolic_label_t; + + +/* symbolic_t describes a linked list of ".symbolic" lines */ +typedef struct symbolic_struct { + symbolic_list_t *symbolic_list; /* linked list of items */ + int symbolic_list_length; /* length of symbolic_list list */ + symbolic_label_t *symbolic_label; /* linked list of new names */ + int symbolic_label_length; /* length of symbolic_label list */ + struct symbolic_struct *next; +} symbolic_t; + + +/* PLA_t stores the logical representation of a PLA */ +typedef struct { + pcover F, D, R; /* on-set, off-set and dc-set */ + char *filename; /* filename */ + int pla_type; /* logical PLA format */ + pcube phase; /* phase to split into on-set and off-set */ + ppair pair; /* how to pair variables */ + char **label; /* labels for the columns */ + symbolic_t *symbolic; /* allow binary->symbolic mapping */ + symbolic_t *symbolic_output;/* allow symbolic output mapping */ +} PLA_t, *pPLA; + +#define equal(a,b) (strcmp(a,b) == 0) + +/* This is a hack which I wish I hadn't done, but too painful to change */ +#define CUBELISTSIZE(T) (((pcube *) T[1] - T) - 3) + +/* For documentation purposes */ +#define IN +#define OUT +#define INOUT + +/* The pla_type field describes the input and output format of the PLA */ +#define F_type 1 +#define D_type 2 +#define R_type 4 +#define PLEASURE_type 8 /* output format */ +#define EQNTOTT_type 16 /* output format algebraic eqns */ +#define KISS_type 128 /* output format kiss */ +#define CONSTRAINTS_type 256 /* output the constraints (numeric) */ +#define SYMBOLIC_CONSTRAINTS_type 512 /* output the constraints (symbolic) */ +#define FD_type (F_type | D_type) +#define FR_type (F_type | R_type) +#define DR_type (D_type | R_type) +#define FDR_type (F_type | D_type | R_type) + +/* Definitions for the debug variable */ +#define COMPL 0x0001 +#define ESSEN 0x0002 +#define EXPAND 0x0004 +#define EXPAND1 0x0008 +#define GASP 0x0010 +#define IRRED 0x0020 +#define REDUCE 0x0040 +#define REDUCE1 0x0080 +#define SPARSE 0x0100 +#define TAUT 0x0200 +#define EXACT 0x0400 +#define MINCOV 0x0800 +#define MINCOV1 0x1000 +#define SHARP 0x2000 +#define IRRED1 0x4000 + +#define VERSION\ + "UC Berkeley, Espresso Version #2.3, Release date 01/31/88" + +/* Define constants used for recording program statistics */ +#define TIME_COUNT 16 +#define READ_TIME 0 +#define COMPL_TIME 1 +#define ONSET_TIME 2 +#define ESSEN_TIME 3 +#define EXPAND_TIME 4 +#define IRRED_TIME 5 +#define REDUCE_TIME 6 +#define GEXPAND_TIME 7 +#define GIRRED_TIME 8 +#define GREDUCE_TIME 9 +#define PRIMES_TIME 10 +#define MINCOV_TIME 11 +#define MV_REDUCE_TIME 12 +#define RAISE_IN_TIME 13 +#define VERIFY_TIME 14 +#define WRITE_TIME 15 + + +/* For those who like to think about PLAs, macros to get at inputs/outputs */ +#define NUMINPUTS cube.num_binary_vars +#define NUMOUTPUTS cube.part_size[cube.num_vars - 1] + +#define POSITIVE_PHASE(pos)\ + (is_in_set(PLA->phase, cube.first_part[cube.output]+pos) != 0) + +#define INLABEL(var) PLA->label[cube.first_part[var] + 1] +#define OUTLABEL(pos) PLA->label[cube.first_part[cube.output] + pos] + +#define GETINPUT(c, pos)\ + ((c[WHICH_WORD(2*pos)] >> WHICH_BIT(2*pos)) & 3) +#define GETOUTPUT(c, pos)\ + (is_in_set(c, cube.first_part[cube.output] + pos) != 0) + +#define PUTINPUT(c, pos, value)\ + c[WHICH_WORD(2*pos)] = (c[WHICH_WORD(2*pos)] & ~(3 << WHICH_BIT(2*pos)))\ + | (value << WHICH_BIT(2*pos)) +#define PUTOUTPUT(c, pos, value)\ + c[WHICH_WORD(pos)] = (c[WHICH_WORD(pos)] & ~(1 << WHICH_BIT(pos)))\ + | (value << WHICH_BIT(pos)) + +#define TWO 3 +#define DASH 3 +#define ONE 2 +#define ZERO 1 + + +#define EXEC(fct, name, S)\ + {long t=ptime();fct;if(trace)print_trace(S,name,ptime()-t);} +#define EXEC_S(fct, name, S)\ + {long t=ptime();fct;if(summary)print_trace(S,name,ptime()-t);} +#define EXECUTE(fct,i,S,cost)\ + {long t=ptime();fct;totals(t,i,S,&(cost));} + +/* + * Global Variable Declarations + */ + +extern unsigned int debug; /* debug parameter */ +extern bool verbose_debug; /* -v: whether to print a lot */ +extern char *total_name[TIME_COUNT]; /* basic function names */ +extern long total_time[TIME_COUNT]; /* time spent in basic fcts */ +extern int total_calls[TIME_COUNT]; /* # calls to each fct */ + +extern bool echo_comments; /* turned off by -eat option */ +extern bool echo_unknown_commands; /* always true ?? */ +extern bool force_irredundant; /* -nirr command line option */ +extern bool skip_make_sparse; +extern bool kiss; /* -kiss command line option */ +extern bool pos; /* -pos command line option */ +extern bool print_solution; /* -x command line option */ +extern bool recompute_onset; /* -onset command line option */ +extern bool remove_essential; /* -ness command line option */ +extern bool single_expand; /* -fast command line option */ +extern bool summary; /* -s command line option */ +extern bool trace; /* -t command line option */ +extern bool unwrap_onset; /* -nunwrap command line option */ +extern bool use_random_order; /* -random command line option */ +extern bool use_super_gasp; /* -strong command line option */ +extern char *filename; /* filename PLA was read from */ +extern bool debug_exact_minimization; /* dumps info for -do exact */ + + +/* + * pla_types are the input and output types for reading/writing a PLA + */ +struct pla_types_struct { + char *key; + int value; +}; + + +/* + * The cube structure is a global structure which contains information + * on how a set maps into a cube -- i.e., number of parts per variable, + * number of variables, etc. Also, many fields are pre-computed to + * speed up various primitive operations. + */ +#define CUBE_TEMP 10 + +struct cube_struct { + int size; /* set size of a cube */ + int num_vars; /* number of variables in a cube */ + int num_binary_vars; /* number of binary variables */ + int *first_part; /* first element of each variable */ + int *last_part; /* first element of each variable */ + int *part_size; /* number of elements in each variable */ + int *first_word; /* first word for each variable */ + int *last_word; /* last word for each variable */ + pset binary_mask; /* Mask to extract binary variables */ + pset mv_mask; /* mask to get mv parts */ + pset *var_mask; /* mask to extract a variable */ + pset *temp; /* an array of temporary sets */ + pset fullset; /* a full cube */ + pset emptyset; /* an empty cube */ + unsigned int inmask; /* mask to get odd word of binary part */ + int inword; /* which word number for above */ + int *sparse; /* should this variable be sparse? */ + int num_mv_vars; /* number of multiple-valued variables */ + int output; /* which variable is "output" (-1 if none) */ +}; + +struct cdata_struct { + int *part_zeros; /* count of zeros for each element */ + int *var_zeros; /* count of zeros for each variable */ + int *parts_active; /* number of "active" parts for each var */ + bool *is_unate; /* indicates given var is unate */ + int vars_active; /* number of "active" variables */ + int vars_unate; /* number of unate variables */ + int best; /* best "binate" variable */ +}; + + +extern struct pla_types_struct pla_types[]; +extern struct cube_struct cube, temp_cube_save; +extern struct cdata_struct cdata, temp_cdata_save; + +#ifdef lint +#define DISJOINT 0x5555 +#else +#if BPI == 32 +#define DISJOINT 0x55555555 +#else +#define DISJOINT 0x5555 +#endif +#endif + +/* function declarations */ + +/* cofactor.c */ extern int binate_split_select(); +/* cofactor.c */ extern pcover cubeunlist(); +/* cofactor.c */ extern pcube *cofactor(); +/* cofactor.c */ extern pcube *cube1list(); +/* cofactor.c */ extern pcube *cube2list(); +/* cofactor.c */ extern pcube *cube3list(); +/* cofactor.c */ extern pcube *scofactor(); +/* cofactor.c */ extern void massive_count(); +/* compl.c */ extern pcover complement(); +/* compl.c */ extern pcover simplify(); +/* compl.c */ extern void simp_comp(); +/* contain.c */ extern int d1_rm_equal(); +/* contain.c */ extern int rm2_contain(); +/* contain.c */ extern int rm2_equal(); +/* contain.c */ extern int rm_contain(); +/* contain.c */ extern int rm_equal(); +/* contain.c */ extern int rm_rev_contain(); +/* contain.c */ extern pset *sf_list(); +/* contain.c */ extern pset *sf_sort(); +/* contain.c */ extern pset_family d1merge(); +/* contain.c */ extern pset_family dist_merge(); +/* contain.c */ extern pset_family sf_contain(); +/* contain.c */ extern pset_family sf_dupl(); +/* contain.c */ extern pset_family sf_ind_contain(); +/* contain.c */ extern pset_family sf_ind_unlist(); +/* contain.c */ extern pset_family sf_merge(); +/* contain.c */ extern pset_family sf_rev_contain(); +/* contain.c */ extern pset_family sf_union(); +/* contain.c */ extern pset_family sf_unlist(); +/* cubestr.c */ extern void cube_setup(); +/* cubestr.c */ extern void restore_cube_struct(); +/* cubestr.c */ extern void save_cube_struct(); +/* cubestr.c */ extern void setdown_cube(); +/* cvrin.c */ extern PLA_labels(); +/* cvrin.c */ extern char *get_word(); +/* cvrin.c */ extern int label_index(); +/* cvrin.c */ extern int read_pla(); +/* cvrin.c */ extern int read_symbolic(); +/* cvrin.c */ extern pPLA new_PLA(); +/* cvrin.c */ extern void PLA_summary(); +/* cvrin.c */ extern void free_PLA(); +/* cvrin.c */ extern void parse_pla(); +/* cvrin.c */ extern void read_cube(); +/* cvrin.c */ extern void skip_line(); +/* cvrm.c */ extern foreach_output_function(); +/* cvrm.c */ extern int cubelist_partition(); +/* cvrm.c */ extern int so_both_do_espresso(); +/* cvrm.c */ extern int so_both_do_exact(); +/* cvrm.c */ extern int so_both_save(); +/* cvrm.c */ extern int so_do_espresso(); +/* cvrm.c */ extern int so_do_exact(); +/* cvrm.c */ extern int so_save(); +/* cvrm.c */ extern pcover cof_output(); +/* cvrm.c */ extern pcover lex_sort(); +/* cvrm.c */ extern pcover mini_sort(); +/* cvrm.c */ extern pcover random_order(); +/* cvrm.c */ extern pcover size_sort(); +/* cvrm.c */ extern pcover sort_reduce(); +/* cvrm.c */ extern pcover uncof_output(); +/* cvrm.c */ extern pcover unravel(); +/* cvrm.c */ extern pcover unravel_range(); +/* cvrm.c */ extern void so_both_espresso(); +/* cvrm.c */ extern void so_espresso(); +/* cvrmisc.c */ extern char *fmt_cost(); +/* cvrmisc.c */ extern char *print_cost(); +/* cvrmisc.c */ extern char *strsav(); +/* cvrmisc.c */ extern void copy_cost(); +/* cvrmisc.c */ extern void cover_cost(); +/* cvrmisc.c */ extern void fatal(); +/* cvrmisc.c */ extern void print_trace(); +/* cvrmisc.c */ extern void size_stamp(); +/* cvrmisc.c */ extern void totals(); +/* cvrout.c */ extern char *fmt_cube(); +/* cvrout.c */ extern char *fmt_expanded_cube(); +/* cvrout.c */ extern char *pc1(); +/* cvrout.c */ extern char *pc2(); +/* cvrout.c */ extern char *pc3(); +/* cvrout.c */ extern int makeup_labels(); +/* cvrout.c */ extern kiss_output(); +/* cvrout.c */ extern kiss_print_cube(); +/* cvrout.c */ extern output_symbolic_constraints(); +/* cvrout.c */ extern void cprint(); +/* cvrout.c */ extern void debug1_print(); +/* cvrout.c */ extern void debug_print(); +/* cvrout.c */ extern void eqn_output(); +/* cvrout.c */ extern void fpr_header(); +/* cvrout.c */ extern void fprint_pla(); +/* cvrout.c */ extern void pls_group(); +/* cvrout.c */ extern void pls_label(); +/* cvrout.c */ extern void pls_output(); +/* cvrout.c */ extern void print_cube(); +/* cvrout.c */ extern void print_expanded_cube(); +/* cvrout.c */ extern void sf_debug_print(); +/* equiv.c */ extern find_equiv_outputs(); +/* equiv.c */ extern int check_equiv(); +/* espresso.c */ extern pcover espresso(); +/* essen.c */ extern bool essen_cube(); +/* essen.c */ extern pcover cb_consensus(); +/* essen.c */ extern pcover cb_consensus_dist0(); +/* essen.c */ extern pcover essential(); +/* exact.c */ extern pcover minimize_exact(); +/* exact.c */ extern pcover minimize_exact_literals(); +/* expand.c */ extern bool feasibly_covered(); +/* expand.c */ extern int most_frequent(); +/* expand.c */ extern pcover all_primes(); +/* expand.c */ extern pcover expand(); +/* expand.c */ extern pcover find_all_primes(); +/* expand.c */ extern void elim_lowering(); +/* expand.c */ extern void essen_parts(); +/* expand.c */ extern void essen_raising(); +/* expand.c */ extern void expand1(); +/* expand.c */ extern void mincov(); +/* expand.c */ extern void select_feasible(); +/* expand.c */ extern void setup_BB_CC(); +/* gasp.c */ extern pcover expand_gasp(); +/* gasp.c */ extern pcover irred_gasp(); +/* gasp.c */ extern pcover last_gasp(); +/* gasp.c */ extern pcover super_gasp(); +/* gasp.c */ extern void expand1_gasp(); +/* getopt.c */ extern int util_getopt(); +/* hack.c */ extern find_dc_inputs(); +/* hack.c */ extern find_inputs(); +/* hack.c */ extern form_bitvector(); +/* hack.c */ extern map_dcset(); +/* hack.c */ extern map_output_symbolic(); +/* hack.c */ extern map_symbolic(); +/* hack.c */ extern pcover map_symbolic_cover(); +/* hack.c */ extern symbolic_hack_labels(); +/* irred.c */ extern bool cube_is_covered(); +/* irred.c */ extern bool taut_special_cases(); +/* irred.c */ extern bool tautology(); +/* irred.c */ extern pcover irredundant(); +/* irred.c */ extern void mark_irredundant(); +/* irred.c */ extern void irred_split_cover(); +/* irred.c */ extern sm_matrix *irred_derive_table(); +/* map.c */ extern pset minterms(); +/* map.c */ extern void explode(); +/* map.c */ extern void map(); +/* opo.c */ extern output_phase_setup(); +/* opo.c */ extern pPLA set_phase(); +/* opo.c */ extern pcover opo(); +/* opo.c */ extern pcube find_phase(); +/* opo.c */ extern pset_family find_covers(); +/* opo.c */ extern pset_family form_cover_table(); +/* opo.c */ extern pset_family opo_leaf(); +/* opo.c */ extern pset_family opo_recur(); +/* opo.c */ extern void opoall(); +/* opo.c */ extern void phase_assignment(); +/* opo.c */ extern void repeated_phase_assignment(); +/* pair.c */ extern generate_all_pairs(); +/* pair.c */ extern int **find_pairing_cost(); +/* pair.c */ extern int find_best_cost(); +/* pair.c */ extern int greedy_best_cost(); +/* pair.c */ extern int minimize_pair(); +/* pair.c */ extern int pair_free(); +/* pair.c */ extern pair_all(); +/* pair.c */ extern pcover delvar(); +/* pair.c */ extern pcover pairvar(); +/* pair.c */ extern ppair pair_best_cost(); +/* pair.c */ extern ppair pair_new(); +/* pair.c */ extern ppair pair_save(); +/* pair.c */ extern print_pair(); +/* pair.c */ extern void find_optimal_pairing(); +/* pair.c */ extern void set_pair(); +/* pair.c */ extern void set_pair1(); +/* primes.c */ extern pcover primes_consensus(); +/* reduce.c */ extern bool sccc_special_cases(); +/* reduce.c */ extern pcover reduce(); +/* reduce.c */ extern pcube reduce_cube(); +/* reduce.c */ extern pcube sccc(); +/* reduce.c */ extern pcube sccc_cube(); +/* reduce.c */ extern pcube sccc_merge(); +/* set.c */ extern bool set_andp(); +/* set.c */ extern bool set_orp(); +/* set.c */ extern bool setp_disjoint(); +/* set.c */ extern bool setp_empty(); +/* set.c */ extern bool setp_equal(); +/* set.c */ extern bool setp_full(); +/* set.c */ extern bool setp_implies(); +/* set.c */ extern char *pbv1(); +/* set.c */ extern char *ps1(); +/* set.c */ extern int *sf_count(); +/* set.c */ extern int *sf_count_restricted(); +/* set.c */ extern int bit_index(); +/* set.c */ extern int set_dist(); +/* set.c */ extern int set_ord(); +/* set.c */ extern void set_adjcnt(); +/* set.c */ extern pset set_and(); +/* set.c */ extern pset set_clear(); +/* set.c */ extern pset set_copy(); +/* set.c */ extern pset set_diff(); +/* set.c */ extern pset set_fill(); +/* set.c */ extern pset set_merge(); +/* set.c */ extern pset set_or(); +/* set.c */ extern pset set_xor(); +/* set.c */ extern pset sf_and(); +/* set.c */ extern pset sf_or(); +/* set.c */ extern pset_family sf_active(); +/* set.c */ extern pset_family sf_addcol(); +/* set.c */ extern pset_family sf_addset(); +/* set.c */ extern pset_family sf_append(); +/* set.c */ extern pset_family sf_bm_read(); +/* set.c */ extern pset_family sf_compress(); +/* set.c */ extern pset_family sf_copy(); +/* set.c */ extern pset_family sf_copy_col(); +/* set.c */ extern pset_family sf_delc(); +/* set.c */ extern pset_family sf_delcol(); +/* set.c */ extern pset_family sf_inactive(); +/* set.c */ extern pset_family sf_join(); +/* set.c */ extern pset_family sf_new(); +/* set.c */ extern pset_family sf_permute(); +/* set.c */ extern pset_family sf_read(); +/* set.c */ extern pset_family sf_save(); +/* set.c */ extern pset_family sf_transpose(); +/* set.c */ extern void set_write(); +/* set.c */ extern void sf_bm_print(); +/* set.c */ extern void sf_cleanup(); +/* set.c */ extern void sf_delset(); +/* set.c */ extern void sf_free(); +/* set.c */ extern void sf_print(); +/* set.c */ extern void sf_write(); +/* setc.c */ extern bool ccommon(); +/* setc.c */ extern bool cdist0(); +/* setc.c */ extern bool full_row(); +/* setc.c */ extern int ascend(); +/* setc.c */ extern int cactive(); +/* setc.c */ extern int cdist(); +/* setc.c */ extern int cdist01(); +/* setc.c */ extern int cvolume(); +/* setc.c */ extern int d1_order(); +/* setc.c */ extern int d1_order_size(); +/* setc.c */ extern int desc1(); +/* setc.c */ extern int descend(); +/* setc.c */ extern int lex_order(); +/* setc.c */ extern int lex_order1(); +/* setc.c */ extern pset force_lower(); +/* setc.c */ extern void consensus(); +/* sharp.c */ extern pcover cb1_dsharp(); +/* sharp.c */ extern pcover cb_dsharp(); +/* sharp.c */ extern pcover cb_recur_dsharp(); +/* sharp.c */ extern pcover cb_recur_sharp(); +/* sharp.c */ extern pcover cb_sharp(); +/* sharp.c */ extern pcover cv_dsharp(); +/* sharp.c */ extern pcover cv_intersect(); +/* sharp.c */ extern pcover cv_sharp(); +/* sharp.c */ extern pcover dsharp(); +/* sharp.c */ extern pcover make_disjoint(); +/* sharp.c */ extern pcover sharp(); +/* sminterf.c */pset do_sm_minimum_cover(); +/* sparse.c */ extern pcover make_sparse(); +/* sparse.c */ extern pcover mv_reduce(); +#if !defined(__osf__) && !defined(__STDC__) && !defined(__hpux) +/* ucbqsort.c */ extern qsort(); +#endif +/* ucbqsort.c */ extern qst(); +/* unate.c */ extern pcover find_all_minimal_covers_petrick(); +/* unate.c */ extern pcover map_cover_to_unate(); +/* unate.c */ extern pcover map_unate_to_cover(); +/* unate.c */ extern pset_family exact_minimum_cover(); +/* unate.c */ extern pset_family gen_primes(); +/* unate.c */ extern pset_family unate_compl(); +/* unate.c */ extern pset_family unate_complement(); +/* unate.c */ extern pset_family unate_intersect(); +/* verify.c */ extern PLA_permute(); +/* verify.c */ extern bool PLA_verify(); +/* verify.c */ extern bool check_consistency(); +/* verify.c */ extern bool verify(); diff --git a/abc_with_bb_support/src/misc/espresso/essen.c b/abc_with_bb_support/src/misc/espresso/essen.c new file mode 100644 index 000000000..2c368c92d --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/essen.c @@ -0,0 +1,179 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +/* + module: essen.c + purpose: Find essential primes in a multiple-valued function +*/ + +#include "espresso.h" + +/* + essential -- return a cover consisting of the cubes of F which are + essential prime implicants (with respect to F u D); Further, remove + these cubes from the ON-set F, and add them to the OFF-set D. + + Sometimes EXPAND can determine that a cube is not an essential prime. + If so, it will set the "NONESSEN" flag in the cube. + + We count on IRREDUNDANT to have set the flag RELESSEN to indicate + that a prime was relatively essential (i.e., covers some minterm + not contained in any other prime in the current cover), or to have + reset the flag to indicate that a prime was relatively redundant + (i.e., all minterms covered by other primes in the current cover). + Of course, after executing irredundant, all of the primes in the + cover are relatively essential, but we can mark the primes which + were redundant at the start of irredundant and avoid an extra check + on these primes for essentiality. +*/ + +pcover essential(Fp, Dp) +IN pcover *Fp, *Dp; +{ + register pcube last, p; + pcover E, F = *Fp, D = *Dp; + + /* set all cubes in F active */ + (void) sf_active(F); + + /* Might as well start out with some cubes in E */ + E = new_cover(10); + + foreach_set(F, last, p) { + /* don't test a prime which EXPAND says is nonessential */ + if (! TESTP(p, NONESSEN)) { + /* only test a prime which was relatively essential */ + if (TESTP(p, RELESSEN)) { + /* Check essentiality */ + if (essen_cube(F, D, p)) { + if (debug & ESSEN) + printf("ESSENTIAL: %s\n", pc1(p)); + E = sf_addset(E, p); + RESET(p, ACTIVE); + F->active_count--; + } + } + } + } + + *Fp = sf_inactive(F); /* delete the inactive cubes from F */ + *Dp = sf_join(D, E); /* add the essentials to D */ + sf_free(D); + return E; +} + +/* + essen_cube -- check if a single cube is essential or not + + The prime c is essential iff + + consensus((F u D) # c, c) u D + + does not contain c. +*/ +bool essen_cube(F, D, c) +IN pcover F, D; +IN pcube c; +{ + pcover H, FD; + pcube *H1; + bool essen; + + /* Append F and D together, and take the sharp-consensus with c */ + FD = sf_join(F, D); + H = cb_consensus(FD, c); + free_cover(FD); + + /* Add the don't care set, and see if this covers c */ + H1 = cube2list(H, D); + essen = ! cube_is_covered(H1, c); + free_cubelist(H1); + + free_cover(H); + return essen; +} + + +/* + * cb_consensus -- compute consensus(T # c, c) + */ +pcover cb_consensus(T, c) +register pcover T; +register pcube c; +{ + register pcube temp, last, p; + register pcover R; + + R = new_cover(T->count*2); + temp = new_cube(); + foreach_set(T, last, p) { + if (p != c) { + switch (cdist01(p, c)) { + case 0: + /* distance-0 needs special care */ + R = cb_consensus_dist0(R, p, c); + break; + + case 1: + /* distance-1 is easy because no sharping required */ + consensus(temp, p, c); + R = sf_addset(R, temp); + break; + } + } + } + set_free(temp); + return R; +} + + +/* + * form the sharp-consensus for p and c when they intersect + * What we are forming is consensus(p # c, c). + */ +pcover cb_consensus_dist0(R, p, c) +pcover R; +register pcube p, c; +{ + int var; + bool got_one; + register pcube temp, mask; + register pcube p_diff_c=cube.temp[0], p_and_c=cube.temp[1]; + + /* If c contains p, then this gives us no information for essential test */ + if (setp_implies(p, c)) { + return R; + } + + /* For the multiple-valued variables */ + temp = new_cube(); + got_one = FALSE; + INLINEset_diff(p_diff_c, p, c); + INLINEset_and(p_and_c, p, c); + + for(var = cube.num_binary_vars; var < cube.num_vars; var++) { + /* Check if c(var) is contained in p(var) -- if so, no news */ + mask = cube.var_mask[var]; + if (! setp_disjoint(p_diff_c, mask)) { + INLINEset_merge(temp, c, p_and_c, mask); + R = sf_addset(R, temp); + got_one = TRUE; + } + } + + /* if no cube so far, add one for the intersection */ + if (! got_one && cube.num_binary_vars > 0) { + /* Add a single cube for the intersection of p and c */ + INLINEset_and(temp, p, c); + R = sf_addset(R, temp); + } + + set_free(temp); + return R; +} diff --git a/abc_with_bb_support/src/misc/espresso/exact.c b/abc_with_bb_support/src/misc/espresso/exact.c new file mode 100644 index 000000000..a9420ce63 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/exact.c @@ -0,0 +1,181 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +#include "espresso.h" + + +static void dump_irredundant(); +static pcover do_minimize(); + + +/* + * minimize_exact -- main entry point for exact minimization + * + * Global flags which affect this routine are: + * + * debug + * skip_make_sparse + */ + +pcover +minimize_exact(F, D, R, exact_cover) +pcover F, D, R; +int exact_cover; +{ + return do_minimize(F, D, R, exact_cover, /*weighted*/ 0); +} + + +pcover +minimize_exact_literals(F, D, R, exact_cover) +pcover F, D, R; +int exact_cover; +{ + return do_minimize(F, D, R, exact_cover, /*weighted*/ 1); +} + + + +static pcover +do_minimize(F, D, R, exact_cover, weighted) +pcover F, D, R; +int exact_cover; +int weighted; +{ + pcover newF, E, Rt, Rp; + pset p, last; + int heur, level, *weights, i; + sm_matrix *table; + sm_row *cover; + sm_element *pe; + int debug_save = debug; + + if (debug & EXACT) { + debug |= (IRRED | MINCOV); + } +#if defined(sun) || defined(bsd4_2) /* hack ... */ + if (debug & MINCOV) { + setlinebuf(stdout); + } +#endif + level = (debug & MINCOV) ? 4 : 0; + heur = ! exact_cover; + + /* Generate all prime implicants */ + EXEC(F = primes_consensus(cube2list(F, D)), "PRIMES ", F); + + /* Setup the prime implicant table */ + EXEC(irred_split_cover(F, D, &E, &Rt, &Rp), "ESSENTIALS ", E); + EXEC(table = irred_derive_table(D, E, Rp), "PI-TABLE ", Rp); + + /* Solve either a weighted or nonweighted covering problem */ + if (weighted) { + /* correct only for all 2-valued variables */ + weights = ALLOC(int, F->count); + foreach_set(Rp, last, p) { + weights[SIZE(p)] = cube.size - set_ord(p); + /* We have added the 0's in the output part instead of the 1's. + This loop corrects the literal count. */ + for (i = cube.first_part[cube.output]; + i <= cube.last_part[cube.output]; i++) { + is_in_set(p, i) ? weights[SIZE(p)]++ : weights[SIZE(p)]--; + } + } + } else { + weights = NIL(int); + } + EXEC(cover=sm_minimum_cover(table,weights,heur,level), "MINCOV ", F); + if (weights != 0) { + FREE(weights); + } + + if (debug & EXACT) { + dump_irredundant(E, Rt, Rp, table); + } + + /* Form the result cover */ + newF = new_cover(100); + foreach_set(E, last, p) { + newF = sf_addset(newF, p); + } + sm_foreach_row_element(cover, pe) { + newF = sf_addset(newF, GETSET(F, pe->col_num)); + } + + free_cover(E); + free_cover(Rt); + free_cover(Rp); + sm_free(table); + sm_row_free(cover); + free_cover(F); + + /* Attempt to make the results more sparse */ + debug &= ~ (IRRED | SHARP | MINCOV); + if (! skip_make_sparse && R != 0) { + newF = make_sparse(newF, D, R); + } + + debug = debug_save; + return newF; +} + +static void +dump_irredundant(E, Rt, Rp, table) +pcover E, Rt, Rp; +sm_matrix *table; +{ + FILE *fp_pi_table, *fp_primes; + pPLA PLA; + pset last, p; + char *file; + + if (filename == 0 || strcmp(filename, "(stdin)") == 0) { + fp_pi_table = fp_primes = stdout; + } else { + file = ALLOC(char, strlen(filename)+20); + (void) sprintf(file, "%s.primes", filename); + if ((fp_primes = fopen(file, "w")) == NULL) { + (void) fprintf(stderr, "espresso: Unable to open %s\n", file); + fp_primes = stdout; + } + (void) sprintf(file, "%s.pi", filename); + if ((fp_pi_table = fopen(file, "w")) == NULL) { + (void) fprintf(stderr, "espresso: Unable to open %s\n", file); + fp_pi_table = stdout; + } + FREE(file); + } + + PLA = new_PLA(); + PLA_labels(PLA); + + fpr_header(fp_primes, PLA, F_type); + free_PLA(PLA); + + (void) fprintf(fp_primes, "# Essential primes are\n"); + foreach_set(E, last, p) { + (void) fprintf(fp_primes, "%s\n", pc1(p)); + } + (void) fprintf(fp_primes, "# Totally redundant primes are\n"); + foreach_set(Rt, last, p) { + (void) fprintf(fp_primes, "%s\n", pc1(p)); + } + (void) fprintf(fp_primes, "# Partially redundant primes are\n"); + foreach_set(Rp, last, p) { + (void) fprintf(fp_primes, "%s\n", pc1(p)); + } + if (fp_primes != stdout) { + (void) fclose(fp_primes); + } + + sm_write(fp_pi_table, table); + if (fp_pi_table != stdout) { + (void) fclose(fp_pi_table); + } +} diff --git a/abc_with_bb_support/src/misc/espresso/expand.c b/abc_with_bb_support/src/misc/espresso/expand.c new file mode 100644 index 000000000..b71fcabc5 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/expand.c @@ -0,0 +1,693 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +/* + module: expand.c + purpose: Perform the Espresso-II Expansion Step + + The idea is to take each nonprime cube of the on-set and expand it + into a prime implicant such that we can cover as many other cubes + of the on-set. If no cube of the on-set can be covered, then we + expand each cube into a large prime implicant by transforming the + problem into a minimum covering problem which is solved by the + heuristics of minimum_cover. + + These routines revolve around having a representation of the + OFF-set. (In contrast to the Espresso-II manuscript, we do NOT + require an "unwrapped" version of the OFF-set). + + Some conventions on variable names: + + SUPER_CUBE is the supercube of all cubes which can be covered + by an expansion of the cube being expanded + + OVEREXPANDED_CUBE is the cube which would result from expanding + all parts which can expand individually of the cube being expanded + + RAISE is the current expansion of the current cube + + FREESET is the set of parts which haven't been raised or lowered yet. + + INIT_LOWER is a set of parts to be removed from the free parts before + starting the expansion +*/ + +#include "espresso.h" + +/* + expand -- expand each nonprime cube of F into a prime implicant + + If nonsparse is true, only the non-sparse variables will be expanded; + this is done by forcing all of the sparse variables out of the free set. +*/ + +pcover expand(F, R, nonsparse) +INOUT pcover F; +IN pcover R; +IN bool nonsparse; /* expand non-sparse variables only */ +{ + register pcube last, p; + pcube RAISE, FREESET, INIT_LOWER, SUPER_CUBE, OVEREXPANDED_CUBE; + int var, num_covered; + bool change; + + /* Order the cubes according to "chewing-away from the edges" of mini */ + if (use_random_order) + F = random_order(F); + else + F = mini_sort(F, ascend); + + /* Allocate memory for variables needed by expand1() */ + RAISE = new_cube(); + FREESET = new_cube(); + INIT_LOWER = new_cube(); + SUPER_CUBE = new_cube(); + OVEREXPANDED_CUBE = new_cube(); + + /* Setup the initial lowering set (differs only for nonsparse) */ + if (nonsparse) + for(var = 0; var < cube.num_vars; var++) + if (cube.sparse[var]) + (void) set_or(INIT_LOWER, INIT_LOWER, cube.var_mask[var]); + + /* Mark all cubes as not covered, and maybe essential */ + foreach_set(F, last, p) { + RESET(p, COVERED); + RESET(p, NONESSEN); + } + + /* Try to expand each nonprime and noncovered cube */ + foreach_set(F, last, p) { + /* do not expand if PRIME or if covered by previous expansion */ + if (! TESTP(p, PRIME) && ! TESTP(p, COVERED)) { + + /* expand the cube p, result is RAISE */ + expand1(R, F, RAISE, FREESET, OVEREXPANDED_CUBE, SUPER_CUBE, + INIT_LOWER, &num_covered, p); + if (debug & EXPAND) + printf("EXPAND: %s (covered %d)\n", pc1(p), num_covered); + (void) set_copy(p, RAISE); + SET(p, PRIME); + RESET(p, COVERED); /* not really necessary */ + + /* See if we generated an inessential prime */ + if (num_covered == 0 && ! setp_equal(p, OVEREXPANDED_CUBE)) { + SET(p, NONESSEN); + } + } + } + + /* Delete any cubes of F which became covered during the expansion */ + F->active_count = 0; + change = FALSE; + foreach_set(F, last, p) { + if (TESTP(p, COVERED)) { + RESET(p, ACTIVE); + change = TRUE; + } else { + SET(p, ACTIVE); + F->active_count++; + } + } + if (change) + F = sf_inactive(F); + + free_cube(RAISE); + free_cube(FREESET); + free_cube(INIT_LOWER); + free_cube(SUPER_CUBE); + free_cube(OVEREXPANDED_CUBE); + return F; +} + +/* + expand1 -- Expand a single cube against the OFF-set +*/ +void expand1(BB, CC, RAISE, FREESET, OVEREXPANDED_CUBE, SUPER_CUBE, + INIT_LOWER, num_covered, c) +pcover BB; /* Blocking matrix (OFF-set) */ +pcover CC; /* Covering matrix (ON-set) */ +pcube RAISE; /* The current parts which have been raised */ +pcube FREESET; /* The current parts which are free */ +pcube OVEREXPANDED_CUBE; /* Overexpanded cube of c */ +pcube SUPER_CUBE; /* Supercube of all cubes of CC we cover */ +pcube INIT_LOWER; /* Parts to initially remove from FREESET */ +int *num_covered; /* Number of cubes of CC which are covered */ +pcube c; /* The cube to be expanded */ +{ + int bestindex; + + if (debug & EXPAND1) + printf("\nEXPAND1: \t%s\n", pc1(c)); + + /* initialize BB and CC */ + SET(c, PRIME); /* don't try to cover ourself */ + setup_BB_CC(BB, CC); + + /* initialize count of # cubes covered, and the supercube of them */ + *num_covered = 0; + (void) set_copy(SUPER_CUBE, c); + + /* Initialize the lowering, raising and unassigned sets */ + (void) set_copy(RAISE, c); + (void) set_diff(FREESET, cube.fullset, RAISE); + + /* If some parts are forced into lowering set, remove them */ + if (! setp_empty(INIT_LOWER)) { + (void) set_diff(FREESET, FREESET, INIT_LOWER); + elim_lowering(BB, CC, RAISE, FREESET); + } + + /* Determine what can be raised, and return the over-expanded cube */ + essen_parts(BB, CC, RAISE, FREESET); + (void) set_or(OVEREXPANDED_CUBE, RAISE, FREESET); + + /* While there are still cubes which can be covered, cover them ! */ + if (CC->active_count > 0) { + select_feasible(BB, CC, RAISE, FREESET, SUPER_CUBE, num_covered); + } + + /* While there are still cubes covered by the overexpanded cube ... */ + while (CC->active_count > 0) { + bestindex = most_frequent(CC, FREESET); + set_insert(RAISE, bestindex); + set_remove(FREESET, bestindex); + essen_parts(BB, CC, RAISE, FREESET); + } + + /* Finally, when all else fails, choose the largest possible prime */ + /* We will loop only if we decide unravelling OFF-set is too expensive */ + while (BB->active_count > 0) { + mincov(BB, RAISE, FREESET); + } + + /* Raise any remaining free coordinates */ + (void) set_or(RAISE, RAISE, FREESET); +} + +/* + essen_parts -- determine which parts are forced into the lowering + set to insure that the cube be orthognal to the OFF-set. + + If any cube of the OFF-set is distance 1 from the raising cube, + then we must lower all parts of the conflicting variable. (If the + cube is distance 0, we detect this error here.) + + If there are essentially lowered parts, we can remove from consideration + any cubes of the OFF-set which are more than distance 1 from the + overexpanded cube of RAISE. +*/ + +void essen_parts(BB, CC, RAISE, FREESET) +pcover BB, CC; +pcube RAISE, FREESET; +{ + register pcube p, r = RAISE; + pcube lastp, xlower = cube.temp[0]; + int dist; + + (void) set_copy(xlower, cube.emptyset); + + foreach_active_set(BB, lastp, p) { +#ifdef NO_INLINE + if ((dist = cdist01(p, r)) > 1) goto exit_if; +#else + {register int w,last;register unsigned int x;dist=0;if((last=cube.inword)!=-1) +{x=p[last]&r[last];if(x=~(x|x>>1)&cube.inmask)if((dist=count_ones(x))>1)goto +exit_if;for(w=1;w>1)&DISJOINT)if(dist==1||( +dist+=count_ones(x))>1)goto exit_if;}}}{register int w,var,last;register pcube +mask;for(var=cube.num_binary_vars;var1)goto exit_if;nextvar:;}} +#endif + if (dist == 0) { + fatal("ON-set and OFF-set are not orthogonal"); + } else { + (void) force_lower(xlower, p, r); + BB->active_count--; + RESET(p, ACTIVE); + } +exit_if: ; + } + + if (! setp_empty(xlower)) { + (void) set_diff(FREESET, FREESET, xlower);/* remove from free set */ + elim_lowering(BB, CC, RAISE, FREESET); + } + + if (debug & EXPAND1) + printf("ESSEN_PARTS:\tRAISE=%s FREESET=%s\n", pc1(RAISE), pc2(FREESET)); +} + +/* + essen_raising -- determine which parts may always be added to + the raising set without restricting further expansions + + General rule: if some part is not blocked by any cube of BB, then + this part can always be raised. +*/ + +void essen_raising(BB, RAISE, FREESET) +register pcover BB; +pcube RAISE, FREESET; +{ + register pcube last, p, xraise = cube.temp[0]; + + /* Form union of all cubes of BB, and then take complement wrt FREESET */ + (void) set_copy(xraise, cube.emptyset); + foreach_active_set(BB, last, p) + INLINEset_or(xraise, xraise, p); + (void) set_diff(xraise, FREESET, xraise); + + (void) set_or(RAISE, RAISE, xraise); /* add to raising set */ + (void) set_diff(FREESET, FREESET, xraise); /* remove from free set */ + + if (debug & EXPAND1) + printf("ESSEN_RAISING:\tRAISE=%s FREESET=%s\n", + pc1(RAISE), pc2(FREESET)); +} + +/* + elim_lowering -- after removing parts from FREESET, we can reduce the + size of both BB and CC. + + We mark as inactive any cube of BB which does not intersect the + overexpanded cube (i.e., RAISE + FREESET). Likewise, we remove + from CC any cube which is not covered by the overexpanded cube. +*/ + +void elim_lowering(BB, CC, RAISE, FREESET) +pcover BB, CC; +pcube RAISE, FREESET; +{ + register pcube p, r = set_or(cube.temp[0], RAISE, FREESET); + pcube last; + + /* + * Remove sets of BB which are orthogonal to future expansions + */ + foreach_active_set(BB, last, p) { +#ifdef NO_INLINE + if (! cdist0(p, r)) +#else + {register int w,lastw;register unsigned int x;if((lastw=cube.inword)!=-1){x=p[ +lastw]&r[lastw];if(~(x|x>>1)&cube.inmask)goto false;for(w=1;w>1)&DISJOINT)goto false;}}}{register int w,var,lastw;register +pcube mask;for(var=cube.num_binary_vars;varactive_count--, RESET(p, ACTIVE); + } + + + /* + * Remove sets of CC which cannot be covered by future expansions + */ + if (CC != (pcover) NULL) { + foreach_active_set(CC, last, p) { +#ifdef NO_INLINE + if (! setp_implies(p, r)) +#else + INLINEsetp_implies(p, r, /* when false => */ goto false1); + /* when true => go to end of loop */ continue; + false1: +#endif + CC->active_count--, RESET(p, ACTIVE); + } + } +} + +/* + most_frequent -- When all else fails, select a reasonable part to raise + The active cubes of CC are the cubes which are covered by the + overexpanded cube of the original cube (however, we know that none + of them can actually be covered by a feasible expansion of the + original cube). We resort to the MINI strategy of selecting to + raise the part which will cover the same part in the most cubes of CC. +*/ +int most_frequent(CC, FREESET) +pcover CC; +pcube FREESET; +{ + register int i, best_part, best_count, *count; + register pset p, last; + + /* Count occurences of each variable */ + count = ALLOC(int, cube.size); + for(i = 0; i < cube.size; i++) + count[i] = 0; + if (CC != (pcover) NULL) + foreach_active_set(CC, last, p) + set_adjcnt(p, count, 1); + + /* Now find which free part occurs most often */ + best_count = best_part = -1; + for(i = 0; i < cube.size; i++) + if (is_in_set(FREESET,i) && count[i] > best_count) { + best_part = i; + best_count = count[i]; + } + FREE(count); + + if (debug & EXPAND1) + printf("MOST_FREQUENT:\tbest=%d FREESET=%s\n", best_part, pc2(FREESET)); + return best_part; +} + +/* + setup_BB_CC -- set up the blocking and covering set families; + + Note that the blocking family is merely the set of cubes of R, and + that CC is the set of cubes of F which might possibly be covered + (i.e., nonprime cubes, and cubes not already covered) +*/ + +void setup_BB_CC(BB, CC) +register pcover BB, CC; +{ + register pcube p, last; + + /* Create the block and cover set families */ + BB->active_count = BB->count; + foreach_set(BB, last, p) + SET(p, ACTIVE); + + if (CC != (pcover) NULL) { + CC->active_count = CC->count; + foreach_set(CC, last, p) + if (TESTP(p, COVERED) || TESTP(p, PRIME)) + CC->active_count--, RESET(p, ACTIVE); + else + SET(p, ACTIVE); + } +} + +/* + select_feasible -- Determine if there are cubes which can be covered, + and if so, raise those parts necessary to cover as many as possible. + + We really don't check to maximize the number that can be covered; + instead, we check, for each fcc, how many other fcc remain fcc + after expanding to cover the fcc. (Essentially one-level lookahead). +*/ + +void select_feasible(BB, CC, RAISE, FREESET, SUPER_CUBE, num_covered) +pcover BB, CC; +pcube RAISE, FREESET, SUPER_CUBE; +int *num_covered; +{ + register pcube p, last, bestfeas, *feas; + register int i, j; + pcube *feas_new_lower; + int bestcount, bestsize, count, size, numfeas, lastfeas; + pcover new_lower; + + /* Start out with all cubes covered by the over-expanded cube as + * the "possibly" feasibly-covered cubes (pfcc) + */ + feas = ALLOC(pcube, CC->active_count); + numfeas = 0; + foreach_active_set(CC, last, p) + feas[numfeas++] = p; + + /* Setup extra cubes to record parts forced low after a covering */ + feas_new_lower = ALLOC(pcube, CC->active_count); + new_lower = new_cover(numfeas); + for(i = 0; i < numfeas; i++) + feas_new_lower[i] = GETSET(new_lower, i); + + +loop: + /* Find the essentially raised parts -- this might cover some cubes + for us, without having to find out if they are fcc or not + */ + essen_raising(BB, RAISE, FREESET); + + /* Now check all "possibly" feasibly covered cubes to check feasibility */ + lastfeas = numfeas; + numfeas = 0; + for(i = 0; i < lastfeas; i++) { + p = feas[i]; + + /* Check active because essen_parts might have removed it */ + if (TESTP(p, ACTIVE)) { + + /* See if the cube is already covered by RAISE -- + * this can happen because of essen_raising() or because of + * the previous "loop" + */ + if (setp_implies(p, RAISE)) { + (*num_covered) += 1; + (void) set_or(SUPER_CUBE, SUPER_CUBE, p); + CC->active_count--; + RESET(p, ACTIVE); + SET(p, COVERED); + /* otherwise, test if it is feasibly covered */ + } else if (feasibly_covered(BB,p,RAISE,feas_new_lower[numfeas])) { + feas[numfeas] = p; /* save the fcc */ + numfeas++; + } + } + } + if (debug & EXPAND1) + printf("SELECT_FEASIBLE: started with %d pfcc, ended with %d fcc\n", + lastfeas, numfeas); + + /* Exit here if there are no feasibly covered cubes */ + if (numfeas == 0) { + FREE(feas); + FREE(feas_new_lower); + free_cover(new_lower); + return; + } + + /* Now find which is the best feasibly covered cube */ + bestcount = 0; + bestsize = 9999; + for(i = 0; i < numfeas; i++) { + size = set_dist(feas[i], FREESET); /* # of newly raised parts */ + count = 0; /* # of other cubes which remain fcc after raising */ + +#define NEW +#ifdef NEW + for(j = 0; j < numfeas; j++) + if (setp_disjoint(feas_new_lower[i], feas[j])) + count++; +#else + for(j = 0; j < numfeas; j++) + if (setp_implies(feas[j], feas[i])) + count++; +#endif + if (count > bestcount) { + bestcount = count; + bestfeas = feas[i]; + bestsize = size; + } else if (count == bestcount && size < bestsize) { + bestfeas = feas[i]; + bestsize = size; + } + } + + /* Add the necessary parts to the raising set */ + (void) set_or(RAISE, RAISE, bestfeas); + (void) set_diff(FREESET, FREESET, RAISE); + if (debug & EXPAND1) + printf("FEASIBLE: \tRAISE=%s FREESET=%s\n", pc1(RAISE), pc2(FREESET)); + essen_parts(BB, CC, RAISE, FREESET); + goto loop; +/* NOTREACHED */ +} + +/* + feasibly_covered -- determine if the cube c is feasibly covered + (i.e., if it is possible to raise all of the necessary variables + while still insuring orthogonality with R). Also, if c is feasibly + covered, then compute the new set of parts which are forced into + the lowering set. +*/ + +bool feasibly_covered(BB, c, RAISE, new_lower) +pcover BB; +pcube c, RAISE, new_lower; +{ + register pcube p, r = set_or(cube.temp[0], RAISE, c); + int dist; + pcube lastp; + + set_copy(new_lower, cube.emptyset); + foreach_active_set(BB, lastp, p) { +#ifdef NO_INLINE + if ((dist = cdist01(p, r)) > 1) goto exit_if; +#else + {register int w,last;register unsigned int x;dist=0;if((last=cube.inword)!=-1) +{x=p[last]&r[last];if(x=~(x|x>>1)&cube.inmask)if((dist=count_ones(x))>1)goto +exit_if;for(w=1;w>1)&DISJOINT)if(dist==1||( +dist+=count_ones(x))>1)goto exit_if;}}}{register int w,var,last;register pcube +mask;for(var=cube.num_binary_vars;var1)goto exit_if;nextvar:;}} +#endif + if (dist == 0) + return FALSE; + else + (void) force_lower(new_lower, p, r); + exit_if: ; + } + return TRUE; +} + +/* + mincov -- transform the problem of expanding a cube to a maximally- + large prime implicant into the problem of selecting a minimum + cardinality cover over a family of sets. + + When we get to this point, we must unravel the remaining off-set. + This may be painful. +*/ + +void mincov(BB, RAISE, FREESET) +pcover BB; +pcube RAISE, FREESET; +{ + int expansion, nset, var, dist; + pset_family B; + register pcube xraise=cube.temp[0], xlower, p, last, plower; + +#ifdef RANDOM_MINCOV +#if defined(_POSIX_SOURCE) || defined(__SVR4) + dist = rand() % set_ord(FREESET); +#else + dist = random() % set_ord(FREESET); +#endif + for(var = 0; var < cube.size && dist >= 0; var++) { + if (is_in_set(FREESET, var)) { + dist--; + } + } + + set_insert(RAISE, var); + set_remove(FREESET, var); + (void) essen_parts(BB, /*CC*/ (pcover) NULL, RAISE, FREESET); +#else + + /* Create B which are those cubes which we must avoid intersecting */ + B = new_cover(BB->active_count); + foreach_active_set(BB, last, p) { + plower = set_copy(GETSET(B, B->count++), cube.emptyset); + (void) force_lower(plower, p, RAISE); + } + + /* Determine how many sets it will blow up into after the unravel */ + nset = 0; + foreach_set(B, last, p) { + expansion = 1; + for(var = cube.num_binary_vars; var < cube.num_vars; var++) { + if ((dist=set_dist(p, cube.var_mask[var])) > 1) { + expansion *= dist; + if (expansion > 500) goto heuristic_mincov; + } + } + nset += expansion; + if (nset > 500) goto heuristic_mincov; + } + + B = unravel(B, cube.num_binary_vars); + xlower = do_sm_minimum_cover(B); + + /* Add any remaining free parts to the raising set */ + (void) set_or(RAISE, RAISE, set_diff(xraise, FREESET, xlower)); + (void) set_copy(FREESET, cube.emptyset); /* free set is empty */ + BB->active_count = 0; /* BB satisfied */ + if (debug & EXPAND1) { + printf("MINCOV: \tRAISE=%s FREESET=%s\n", pc1(RAISE), pc2(FREESET)); + } + sf_free(B); + set_free(xlower); + return; + +heuristic_mincov: + sf_free(B); + /* most_frequent will pick first free part */ + set_insert(RAISE, most_frequent(/*CC*/ (pcover) NULL, FREESET)); + (void) set_diff(FREESET, FREESET, RAISE); + essen_parts(BB, /*CC*/ (pcover) NULL, RAISE, FREESET); + return; +#endif +} + +/* + find_all_primes -- find all of the primes which cover the + currently reduced BB +*/ +pcover find_all_primes(BB, RAISE, FREESET) +pcover BB; +register pcube RAISE, FREESET; +{ + register pset last, p, plower; + pset_family B, B1; + + if (BB->active_count == 0) { + B1 = new_cover(1); + p = GETSET(B1, B1->count++); + (void) set_copy(p, RAISE); + SET(p, PRIME); + } else { + B = new_cover(BB->active_count); + foreach_active_set(BB, last, p) { + plower = set_copy(GETSET(B, B->count++), cube.emptyset); + (void) force_lower(plower, p, RAISE); + } + B = sf_rev_contain(unravel(B, cube.num_binary_vars)); + B1 = exact_minimum_cover(B); + foreach_set(B1, last, p) { + INLINEset_diff(p, FREESET, p); + INLINEset_or(p, p, RAISE); + SET(p, PRIME); + } + free_cover(B); + } + return B1; +} + +/* + all_primes -- foreach cube in F, generate all of the primes + which cover the cube. +*/ + +pcover all_primes(F, R) +pcover F, R; +{ + register pcube last, p, RAISE, FREESET; + pcover Fall_primes, B1; + + FREESET = new_cube(); + RAISE = new_cube(); + Fall_primes = new_cover(F->count); + + foreach_set(F, last, p) { + if (TESTP(p, PRIME)) { + Fall_primes = sf_addset(Fall_primes, p); + } else { + /* Setup for call to essential parts */ + (void) set_copy(RAISE, p); + (void) set_diff(FREESET, cube.fullset, RAISE); + setup_BB_CC(R, /* CC */ (pcover) NULL); + essen_parts(R, /* CC */ (pcover) NULL, RAISE, FREESET); + + /* Find all of the primes, and add them to the prime set */ + B1 = find_all_primes(R, RAISE, FREESET); + Fall_primes = sf_append(Fall_primes, B1); + } + } + + set_free(RAISE); + set_free(FREESET); + return Fall_primes; +} diff --git a/abc_with_bb_support/src/misc/espresso/gasp.c b/abc_with_bb_support/src/misc/espresso/gasp.c new file mode 100644 index 000000000..c0ef67ecb --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/gasp.c @@ -0,0 +1,228 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +/* + module: gasp.c + + The "last_gasp" heuristic computes the reduction of each cube in + the cover (without replacement) and then performs an expansion of + these cubes. The cubes which expand to cover some other cube are + added to the original cover and irredundant finds a minimal subset. + + If one of the reduced cubes expands to cover some other reduced + cube, then the new prime thus generated is a candidate for reducing + the size of the cover. + + super_gasp is a variation on this strategy which extracts a minimal + subset from the set of all prime implicants which cover all + maximally reduced cubes. +*/ + +#include "espresso.h" + + +/* + * reduce_gasp -- compute the maximal reduction of each cube of F + * + * If a cube does not reduce, it remains prime; otherwise, it is marked + * as nonprime. If the cube is redundant (should NEVER happen here) we + * just crap out ... + * + * A cover with all of the cubes of F is returned. Those that did + * reduce are marked "NONPRIME"; those that reduced are marked "PRIME". + * The cubes are in the same order as in F. + */ +static pcover reduce_gasp(F, D) +pcover F, D; +{ + pcube p, last, cunder, *FD; + pcover G; + + G = new_cover(F->count); + FD = cube2list(F, D); + + /* Reduce cubes of F without replacement */ + foreach_set(F, last, p) { + cunder = reduce_cube(FD, p); + if (setp_empty(cunder)) { + fatal("empty reduction in reduce_gasp, shouldn't happen"); + } else if (setp_equal(cunder, p)) { + SET(cunder, PRIME); /* just to make sure */ + G = sf_addset(G, p); /* it did not reduce ... */ + } else { + RESET(cunder, PRIME); /* it reduced ... */ + G = sf_addset(G, cunder); + } + if (debug & GASP) { + printf("REDUCE_GASP: %s reduced to %s\n", pc1(p), pc2(cunder)); + } + free_cube(cunder); + } + + free_cubelist(FD); + return G; +} + +/* + * expand_gasp -- expand each nonprime cube of F into a prime implicant + * + * The gasp strategy differs in that only those cubes which expand to + * cover some other cube are saved; also, all cubes are expanded + * regardless of whether they become covered or not. + */ + +pcover expand_gasp(F, D, R, Foriginal) +INOUT pcover F; +IN pcover D; +IN pcover R; +IN pcover Foriginal; +{ + int c1index; + pcover G; + + /* Try to expand each nonprime and noncovered cube */ + G = new_cover(10); + for(c1index = 0; c1index < F->count; c1index++) { + expand1_gasp(F, D, R, Foriginal, c1index, &G); + } + G = sf_dupl(G); + G = expand(G, R, /*nonsparse*/ FALSE); /* Make them prime ! */ + return G; +} + + + +/* + * expand1 -- Expand a single cube against the OFF-set, using the gasp strategy + */ +void expand1_gasp(F, D, R, Foriginal, c1index, G) +pcover F; /* reduced cubes of ON-set */ +pcover D; /* DC-set */ +pcover R; /* OFF-set */ +pcover Foriginal; /* ON-set before reduction (same order as F) */ +int c1index; /* which index of F (or Freduced) to be checked */ +pcover *G; +{ + register int c2index; + register pcube p, last, c2under; + pcube RAISE, FREESET, temp, *FD, c2essential; + pcover F1; + + if (debug & EXPAND1) { + printf("\nEXPAND1_GASP: \t%s\n", pc1(GETSET(F, c1index))); + } + + RAISE = new_cube(); + FREESET = new_cube(); + temp = new_cube(); + + /* Initialize the OFF-set */ + R->active_count = R->count; + foreach_set(R, last, p) { + SET(p, ACTIVE); + } + /* Initialize the reduced ON-set, all nonprime cubes become active */ + F->active_count = F->count; + foreachi_set(F, c2index, c2under) { + if (c1index == c2index || TESTP(c2under, PRIME)) { + F->active_count--; + RESET(c2under, ACTIVE); + } else { + SET(c2under, ACTIVE); + } + } + + /* Initialize the raising and unassigned sets */ + (void) set_copy(RAISE, GETSET(F, c1index)); + (void) set_diff(FREESET, cube.fullset, RAISE); + + /* Determine parts which must be lowered */ + essen_parts(R, F, RAISE, FREESET); + + /* Determine parts which can always be raised */ + essen_raising(R, RAISE, FREESET); + + /* See which, if any, of the reduced cubes we can cover */ + foreachi_set(F, c2index, c2under) { + if (TESTP(c2under, ACTIVE)) { + /* See if this cube can be covered by an expansion */ + if (setp_implies(c2under, RAISE) || + feasibly_covered(R, c2under, RAISE, temp)) { + + /* See if c1under can expanded to cover c2 reduced against + * (F - c1) u c1under; if so, c2 can definitely be removed ! + */ + + /* Copy F and replace c1 with c1under */ + F1 = sf_save(Foriginal); + (void) set_copy(GETSET(F1, c1index), GETSET(F, c1index)); + + /* Reduce c2 against ((F - c1) u c1under) */ + FD = cube2list(F1, D); + c2essential = reduce_cube(FD, GETSET(F1, c2index)); + free_cubelist(FD); + sf_free(F1); + + /* See if c2essential is covered by an expansion of c1under */ + if (feasibly_covered(R, c2essential, RAISE, temp)) { + (void) set_or(temp, RAISE, c2essential); + RESET(temp, PRIME); /* cube not prime */ + *G = sf_addset(*G, temp); + } + set_free(c2essential); + } + } + } + + free_cube(RAISE); + free_cube(FREESET); + free_cube(temp); +} + +/* irred_gasp -- Add new primes to F and find an irredundant subset */ +pcover irred_gasp(F, D, G) +pcover F, D, G; /* G is disposed of */ +{ + if (G->count != 0) + F = irredundant(sf_append(F, G), D); + else + free_cover(G); + return F; +} + + +/* last_gasp */ +pcover last_gasp(F, D, R, cost) +pcover F, D, R; +cost_t *cost; +{ + pcover G, G1; + + EXECUTE(G = reduce_gasp(F, D), GREDUCE_TIME, G, *cost); + EXECUTE(G1 = expand_gasp(G, D, R, F), GEXPAND_TIME, G1, *cost); + free_cover(G); + EXECUTE(F = irred_gasp(F, D, G1), GIRRED_TIME, F, *cost); + return F; +} + + +/* super_gasp */ +pcover super_gasp(F, D, R, cost) +pcover F, D, R; +cost_t *cost; +{ + pcover G, G1; + + EXECUTE(G = reduce_gasp(F, D), GREDUCE_TIME, G, *cost); + EXECUTE(G1 = all_primes(G, R), GEXPAND_TIME, G1, *cost); + free_cover(G); + EXEC(G = sf_dupl(sf_append(F, G1)), "NEWPRIMES", G); + EXECUTE(F = irredundant(G, D), IRRED_TIME, F, *cost); + return F; +} diff --git a/abc_with_bb_support/src/misc/espresso/gimpel.c b/abc_with_bb_support/src/misc/espresso/gimpel.c new file mode 100644 index 000000000..ea8bc18e8 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/gimpel.c @@ -0,0 +1,106 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +#include "mincov_int.h" + + +/* + * check for: + * + * c1 c2 rest + * -- -- --- + * 1 1 0 0 0 0 <-- primary row + * 1 0 S1 <-- secondary row + * 0 1 T1 + * 0 1 T2 + * 0 1 Tn + * 0 0 R + */ + +int +gimpel_reduce(A, select, weight, lb, bound, depth, stats, best) +sm_matrix *A; +solution_t *select; +int *weight; +int lb; +int bound; +int depth; +stats_t *stats; +solution_t **best; +{ + register sm_row *prow, *save_sec; + register sm_col *c1, *c2; + register sm_element *p, *p1; + int c1_col_num, c2_col_num, primary_row_num, secondary_row_num; + int reduce_it; + + reduce_it = 0; + for(prow = A->first_row; prow != 0; prow = prow->next_row) { + if (prow->length == 2) { + c1 = sm_get_col(A, prow->first_col->col_num); + c2 = sm_get_col(A, prow->last_col->col_num); + if (c1->length == 2) { + reduce_it = 1; + } else if (c2->length == 2) { + c1 = sm_get_col(A, prow->last_col->col_num); + c2 = sm_get_col(A, prow->first_col->col_num); + reduce_it = 1; + } + if (reduce_it) { + primary_row_num = prow->row_num; + secondary_row_num = c1->first_row->row_num; + if (secondary_row_num == primary_row_num) { + secondary_row_num = c1->last_row->row_num; + } + break; + } + } + } + + if (reduce_it) { + c1_col_num = c1->col_num; + c2_col_num = c2->col_num; + save_sec = sm_row_dup(sm_get_row(A, secondary_row_num)); + sm_row_remove(save_sec, c1_col_num); + + for(p = c2->first_row; p != 0; p = p->next_row) { + if (p->row_num != primary_row_num) { + /* merge rows S1 and T */ + for(p1 = save_sec->first_col; p1 != 0; p1 = p1->next_col) { + (void) sm_insert(A, p->row_num, p1->col_num); + } + } + } + + sm_delcol(A, c1_col_num); + sm_delcol(A, c2_col_num); + sm_delrow(A, primary_row_num); + sm_delrow(A, secondary_row_num); + + stats->gimpel_count++; + stats->gimpel++; + *best = sm_mincov(A, select, weight, lb-1, bound-1, depth, stats); + stats->gimpel--; + + if (*best != NIL(solution_t)) { + /* is secondary row covered ? */ + if (sm_row_intersects(save_sec, (*best)->row)) { + /* yes, actually select c2 */ + solution_add(*best, weight, c2_col_num); + } else { + solution_add(*best, weight, c1_col_num); + } + } + + sm_row_free(save_sec); + return 1; + } else { + return 0; + } +} diff --git a/abc_with_bb_support/src/misc/espresso/globals.c b/abc_with_bb_support/src/misc/espresso/globals.c new file mode 100644 index 000000000..724b408a1 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/globals.c @@ -0,0 +1,76 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +#include "espresso.h" + +/* + * Global Variable Declarations + */ + +unsigned int debug; /* debug parameter */ +bool verbose_debug; /* -v: whether to print a lot */ +char *total_name[TIME_COUNT]; /* basic function names */ +long total_time[TIME_COUNT]; /* time spent in basic fcts */ +int total_calls[TIME_COUNT]; /* # calls to each fct */ + +bool echo_comments; /* turned off by -eat option */ +bool echo_unknown_commands; /* always true ?? */ +bool force_irredundant; /* -nirr command line option */ +bool skip_make_sparse; +bool kiss; /* -kiss command line option */ +bool pos; /* -pos command line option */ +bool print_solution; /* -x command line option */ +bool recompute_onset; /* -onset command line option */ +bool remove_essential; /* -ness command line option */ +bool single_expand; /* -fast command line option */ +bool summary; /* -s command line option */ +bool trace; /* -t command line option */ +bool unwrap_onset; /* -nunwrap command line option */ +bool use_random_order; /* -random command line option */ +bool use_super_gasp; /* -strong command line option */ +char *filename; /* filename PLA was read from */ + +struct pla_types_struct pla_types[] = { + "-f", F_type, + "-r", R_type, + "-d", D_type, + "-fd", FD_type, + "-fr", FR_type, + "-dr", DR_type, + "-fdr", FDR_type, + "-fc", F_type | CONSTRAINTS_type, + "-rc", R_type | CONSTRAINTS_type, + "-dc", D_type | CONSTRAINTS_type, + "-fdc", FD_type | CONSTRAINTS_type, + "-frc", FR_type | CONSTRAINTS_type, + "-drc", DR_type | CONSTRAINTS_type, + "-fdrc", FDR_type | CONSTRAINTS_type, + "-pleasure", PLEASURE_type, + "-eqn", EQNTOTT_type, + "-eqntott", EQNTOTT_type, + "-kiss", KISS_type, + "-cons", CONSTRAINTS_type, + "-scons", SYMBOLIC_CONSTRAINTS_type, + 0, 0 +}; + + +struct cube_struct cube, temp_cube_save; +struct cdata_struct cdata, temp_cdata_save; + +int bit_count[256] = { + 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 +}; diff --git a/abc_with_bb_support/src/misc/espresso/hack.c b/abc_with_bb_support/src/misc/espresso/hack.c new file mode 100644 index 000000000..105aebac7 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/hack.c @@ -0,0 +1,641 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +#include "espresso.h" + +map_dcset(PLA) +pPLA PLA; +{ + int var, i; + pcover Tplus, Tminus, Tplusbar, Tminusbar; + pcover newf, term1, term2, dcset, dcsetbar; + pcube cplus, cminus, last, p; + + if (PLA->label == NIL(char *) || PLA->label[0] == NIL(char)) + return; + + /* try to find a binary variable named "DONT_CARE" */ + var = -1; + for(i = 0; i < cube.num_binary_vars * 2; i++) { + if (strncmp(PLA->label[i], "DONT_CARE", 9) == 0 || + strncmp(PLA->label[i], "DONTCARE", 8) == 0 || + strncmp(PLA->label[i], "dont_care", 9) == 0 || + strncmp(PLA->label[i], "dontcare", 8) == 0) { + var = i/2; + break; + } + } + if (var == -1) { + return; + } + + /* form the cofactor cubes for the don't-care variable */ + cplus = set_save(cube.fullset); + cminus = set_save(cube.fullset); + set_remove(cplus, var*2); + set_remove(cminus, var*2 + 1); + + /* form the don't-care set */ + EXEC(simp_comp(cofactor(cube1list(PLA->F), cplus), &Tplus, &Tplusbar), + "simpcomp+", Tplus); + EXEC(simp_comp(cofactor(cube1list(PLA->F), cminus), &Tminus, &Tminusbar), + "simpcomp-", Tminus); + EXEC(term1 = cv_intersect(Tplus, Tminusbar), "term1 ", term1); + EXEC(term2 = cv_intersect(Tminus, Tplusbar), "term2 ", term2); + EXEC(dcset = sf_union(term1, term2), "union ", dcset); + EXEC(simp_comp(cube1list(dcset), &PLA->D, &dcsetbar), "simplify", PLA->D); + EXEC(newf = cv_intersect(PLA->F, dcsetbar), "separate ", PLA->F); + free_cover(PLA->F); + PLA->F = newf; + free_cover(Tplus); + free_cover(Tminus); + free_cover(Tplusbar); + free_cover(Tminusbar); + free_cover(dcsetbar); + + /* remove any cubes dependent on the DONT_CARE variable */ + (void) sf_active(PLA->F); + foreach_set(PLA->F, last, p) { + if (! is_in_set(p, var*2) || ! is_in_set(p, var*2+1)) { + RESET(p, ACTIVE); + } + } + PLA->F = sf_inactive(PLA->F); + + /* resize the cube and delete the don't-care variable */ + setdown_cube(); + for(i = 2*var+2; i < cube.size; i++) { + PLA->label[i-2] = PLA->label[i]; + } + for(i = var+1; i < cube.num_vars; i++) { + cube.part_size[i-1] = cube.part_size[i]; + } + cube.num_binary_vars--; + cube.num_vars--; + cube_setup(); + PLA->F = sf_delc(PLA->F, 2*var, 2*var+1); + PLA->D = sf_delc(PLA->D, 2*var, 2*var+1); +} + +map_output_symbolic(PLA) +pPLA PLA; +{ + pset_family newF, newD; + pset compress; + symbolic_t *p1; + symbolic_list_t *p2; + int i, bit, tot_size, base, old_size; + + /* Remove the DC-set from the ON-set (is this necessary ??) */ + if (PLA->D->count > 0) { + sf_free(PLA->F); + PLA->F = complement(cube2list(PLA->D, PLA->R)); + } + + /* tot_size = width added for all symbolic variables */ + tot_size = 0; + for(p1=PLA->symbolic_output; p1!=NIL(symbolic_t); p1=p1->next) { + for(p2=p1->symbolic_list; p2!=NIL(symbolic_list_t); p2=p2->next) { + if (p2->pos<0 || p2->pos>=cube.part_size[cube.output]) { + fatal("symbolic-output index out of range"); +/* } else if (p2->variable != cube.output) { + fatal("symbolic-output label must be an output");*/ + } + } + tot_size += 1 << p1->symbolic_list_length; + } + + /* adjust the indices to skip over new outputs */ + for(p1=PLA->symbolic_output; p1!=NIL(symbolic_t); p1=p1->next) { + for(p2=p1->symbolic_list; p2!=NIL(symbolic_list_t); p2=p2->next) { + p2->pos += tot_size; + } + } + + /* resize the cube structure -- add enough for the one-hot outputs */ + old_size = cube.size; + cube.part_size[cube.output] += tot_size; + setdown_cube(); + cube_setup(); + + /* insert space in the output part for the one-hot output */ + base = cube.first_part[cube.output]; + PLA->F = sf_addcol(PLA->F, base, tot_size); + PLA->D = sf_addcol(PLA->D, base, tot_size); + PLA->R = sf_addcol(PLA->R, base, tot_size); + + /* do the real work */ + for(p1=PLA->symbolic_output; p1!=NIL(symbolic_t); p1=p1->next) { + newF = new_cover(100); + newD = new_cover(100); + find_inputs(NIL(set_family_t), PLA, p1->symbolic_list, base, 0, + &newF, &newD); +/* + * Not sure what this means + find_dc_inputs(PLA, p1->symbolic_list, + base, 1 << p1->symbolic_list_length, &newF, &newD); + */ + free_cover(PLA->F); + PLA->F = newF; +/* + * retain OLD DC-set -- but we've lost the don't-care arc information + * (it defaults to branch to the zero state) + free_cover(PLA->D); + PLA->D = newD; + */ + free_cover(newD); + base += 1 << p1->symbolic_list_length; + } + + /* delete the old outputs, and resize the cube */ + compress = set_full(newF->sf_size); + for(p1=PLA->symbolic_output; p1!=NIL(symbolic_t); p1=p1->next) { + for(p2=p1->symbolic_list; p2!=NIL(symbolic_list_t); p2=p2->next) { + bit = cube.first_part[cube.output] + p2->pos; + set_remove(compress, bit); + } + } + cube.part_size[cube.output] -= newF->sf_size - set_ord(compress); + setdown_cube(); + cube_setup(); + PLA->F = sf_compress(PLA->F, compress); + PLA->D = sf_compress(PLA->D, compress); + if (cube.size != PLA->F->sf_size) fatal("error"); + + /* Quick minimization */ + PLA->F = sf_contain(PLA->F); + PLA->D = sf_contain(PLA->D); + for(i = 0; i < cube.num_vars; i++) { + PLA->F = d1merge(PLA->F, i); + PLA->D = d1merge(PLA->D, i); + } + PLA->F = sf_contain(PLA->F); + PLA->D = sf_contain(PLA->D); + + free_cover(PLA->R); + PLA->R = new_cover(0); + + symbolic_hack_labels(PLA, PLA->symbolic_output, + compress, cube.size, old_size, tot_size); + set_free(compress); +} + + +find_inputs(A, PLA, list, base, value, newF, newD) +pcover A; +pPLA PLA; +symbolic_list_t *list; +int base, value; +pcover *newF, *newD; +{ + pcover S, S1; + register pset last, p; + + /* + * A represents th 'input' values for which the outputs assume + * the integer value 'value + */ + if (list == NIL(symbolic_list_t)) { + /* + * Simulate these inputs against the on-set; then, insert into the + * new on-set a 1 in the proper position + */ + S = cv_intersect(A, PLA->F); + foreach_set(S, last, p) { + set_insert(p, base + value); + } + *newF = sf_append(*newF, S); + + /* + * 'simulate' these inputs against the don't-care set + S = cv_intersect(A, PLA->D); + *newD = sf_append(*newD, S); + */ + + } else { + /* intersect and recur with the OFF-set */ + S = cof_output(PLA->R, cube.first_part[cube.output] + list->pos); + if (A != NIL(set_family_t)) { + S1 = cv_intersect(A, S); + free_cover(S); + S = S1; + } + find_inputs(S, PLA, list->next, base, value*2, newF, newD); + free_cover(S); + + /* intersect and recur with the ON-set */ + S = cof_output(PLA->F, cube.first_part[cube.output] + list->pos); + if (A != NIL(set_family_t)) { + S1 = cv_intersect(A, S); + free_cover(S); + S = S1; + } + find_inputs(S, PLA, list->next, base, value*2 + 1, newF, newD); + free_cover(S); + } +} + + +#if 0 +find_dc_inputs(PLA, list, base, maxval, newF, newD) +pPLA PLA; +symbolic_list_t *list; +int base, maxval; +pcover *newF, *newD; +{ + pcover A, S, S1; + symbolic_list_t *p2; + register pset p, last; + register int i; + + /* painfully find the points for which the symbolic output is dc */ + A = NIL(set_family_t); + for(p2=list; p2!=NIL(symbolic_list_t); p2=p2->next) { + S = cof_output(PLA->D, cube.first_part[cube.output] + p2->pos); + if (A == NIL(set_family_t)) { + A = S; + } else { + S1 = cv_intersect(A, S); + free_cover(S); + free_cover(A); + A = S1; + } + } + + S = cv_intersect(A, PLA->F); + *newF = sf_append(*newF, S); + + S = cv_intersect(A, PLA->D); + foreach_set(S, last, p) { + for(i = base; i < base + maxval; i++) { + set_insert(p, i); + } + } + *newD = sf_append(*newD, S); + free_cover(A); +} +#endif + +map_symbolic(PLA) +pPLA PLA; +{ + symbolic_t *p1; + symbolic_list_t *p2; + int var, base, num_vars, num_binary_vars, *new_part_size; + int new_size, size_added, num_deleted_vars, num_added_vars, newvar; + pset compress; + + /* Verify legal values are in the symbolic lists */ + for(p1 = PLA->symbolic; p1 != NIL(symbolic_t); p1 = p1->next) { + for(p2=p1->symbolic_list; p2!=NIL(symbolic_list_t); p2=p2->next) { + if (p2->variable < 0 || p2->variable >= cube.num_binary_vars) { + fatal(".symbolic requires binary variables"); + } + } + } + + /* + * size_added = width added for all symbolic variables + * num_deleted_vars = # binary variables to be deleted + * num_added_vars = # new mv variables + * compress = a cube which will be used to compress the set families + */ + size_added = 0; + num_added_vars = 0; + for(p1 = PLA->symbolic; p1 != NIL(symbolic_t); p1 = p1->next) { + size_added += 1 << p1->symbolic_list_length; + num_added_vars++; + } + compress = set_full(PLA->F->sf_size + size_added); + for(p1 = PLA->symbolic; p1 != NIL(symbolic_t); p1 = p1->next) { + for(p2=p1->symbolic_list; p2!=NIL(symbolic_list_t); p2=p2->next) { + set_remove(compress, p2->variable*2); + set_remove(compress, p2->variable*2+1); + } + } + num_deleted_vars = ((PLA->F->sf_size + size_added) - set_ord(compress))/2; + + /* compute the new cube constants */ + num_vars = cube.num_vars - num_deleted_vars + num_added_vars; + num_binary_vars = cube.num_binary_vars - num_deleted_vars; + new_size = cube.size - num_deleted_vars*2 + size_added; + new_part_size = ALLOC(int, num_vars); + new_part_size[num_vars-1] = cube.part_size[cube.num_vars-1]; + for(var = cube.num_binary_vars; var < cube.num_vars-1; var++) { + new_part_size[var-num_deleted_vars] = cube.part_size[var]; + } + + /* re-size the covers, opening room for the new mv variables */ + base = cube.first_part[cube.output]; + PLA->F = sf_addcol(PLA->F, base, size_added); + PLA->D = sf_addcol(PLA->D, base, size_added); + PLA->R = sf_addcol(PLA->R, base, size_added); + + /* compute the values for the new mv variables */ + newvar = (cube.num_vars - 1) - num_deleted_vars; + for(p1 = PLA->symbolic; p1 != NIL(symbolic_t); p1 = p1->next) { + PLA->F = map_symbolic_cover(PLA->F, p1->symbolic_list, base); + PLA->D = map_symbolic_cover(PLA->D, p1->symbolic_list, base); + PLA->R = map_symbolic_cover(PLA->R, p1->symbolic_list, base); + base += 1 << p1->symbolic_list_length; + new_part_size[newvar++] = 1 << p1->symbolic_list_length; + } + + /* delete the binary variables which disappear */ + PLA->F = sf_compress(PLA->F, compress); + PLA->D = sf_compress(PLA->D, compress); + PLA->R = sf_compress(PLA->R, compress); + + symbolic_hack_labels(PLA, PLA->symbolic, compress, + new_size, cube.size, size_added); + setdown_cube(); + FREE(cube.part_size); + cube.num_vars = num_vars; + cube.num_binary_vars = num_binary_vars; + cube.part_size = new_part_size; + cube_setup(); + set_free(compress); +} + + +pcover map_symbolic_cover(T, list, base) +pcover T; +symbolic_list_t *list; +int base; +{ + pset last, p; + foreach_set(T, last, p) { + form_bitvector(p, base, 0, list); + } + return T; +} + + +form_bitvector(p, base, value, list) +pset p; /* old cube, looking at binary variables */ +int base; /* where in mv cube the new variable starts */ +int value; /* current value for this recursion */ +symbolic_list_t *list; /* current place in the symbolic list */ +{ + if (list == NIL(symbolic_list_t)) { + set_insert(p, base + value); + } else { + switch(GETINPUT(p, list->variable)) { + case ZERO: + form_bitvector(p, base, value*2, list->next); + break; + case ONE: + form_bitvector(p, base, value*2+1, list->next); + break; + case TWO: + form_bitvector(p, base, value*2, list->next); + form_bitvector(p, base, value*2+1, list->next); + break; + default: + fatal("bad cube in form_bitvector"); + } + } +} + + +symbolic_hack_labels(PLA, list, compress, new_size, old_size, size_added) +pPLA PLA; +symbolic_t *list; +pset compress; +int new_size, old_size, size_added; +{ + int i, base; + char **oldlabel; + symbolic_t *p1; + symbolic_label_t *p3; + + /* hack with the labels */ + if ((oldlabel = PLA->label) == NIL(char *)) + return; + PLA->label = ALLOC(char *, new_size); + for(i = 0; i < new_size; i++) { + PLA->label[i] = NIL(char); + } + + /* copy the binary variable labels and unchanged mv variable labels */ + base = 0; + for(i = 0; i < cube.first_part[cube.output]; i++) { + if (is_in_set(compress, i)) { + PLA->label[base++] = oldlabel[i]; + } else { + if (oldlabel[i] != NIL(char)) { + FREE(oldlabel[i]); + } + } + } + + /* add the user-defined labels for the symbolic outputs */ + for(p1 = list; p1 != NIL(symbolic_t); p1 = p1->next) { + p3 = p1->symbolic_label; + for(i = 0; i < (1 << p1->symbolic_list_length); i++) { + if (p3 == NIL(symbolic_label_t)) { + PLA->label[base+i] = ALLOC(char, 10); + (void) sprintf(PLA->label[base+i], "X%d", i); + } else { + PLA->label[base+i] = p3->label; + p3 = p3->next; + } + } + base += 1 << p1->symbolic_list_length; + } + + /* copy the labels for the binary outputs which remain */ + for(i = cube.first_part[cube.output]; i < old_size; i++) { + if (is_in_set(compress, i + size_added)) { + PLA->label[base++] = oldlabel[i]; + } else { + if (oldlabel[i] != NIL(char)) { + FREE(oldlabel[i]); + } + } + } + FREE(oldlabel); +} + +static pcover fsm_simplify(F) +pcover F; +{ + pcover D, R; + D = new_cover(0); + R = complement(cube1list(F)); + F = espresso(F, D, R); + free_cover(D); + free_cover(R); + return F; +} + + +disassemble_fsm(PLA, verbose_mode) +pPLA PLA; +int verbose_mode; +{ + int nin, nstates, nout; + int before, after, present_state, next_state, i, j; + pcube next_state_mask, present_state_mask, state_mask, p, p1, last; + pcover go_nowhere, F, tF; + + /* We make the DISGUSTING assumption that the first 'n' outputs have + * been created by .symbolic-output, and represent a one-hot encoding + * of the next state. 'n' is the size of the second-to-last multiple- + * valued variable (i.e., before the outputs + */ + + if (cube.num_vars - cube.num_binary_vars != 2) { + (void) fprintf(stderr, + "use .symbolic and .symbolic-output to specify\n"); + (void) fprintf(stderr, + "the present state and next state field information\n"); + fatal("disassemble_pla: need two multiple-valued variables\n"); + } + + nin = cube.num_binary_vars; + nstates = cube.part_size[cube.num_binary_vars]; + nout = cube.part_size[cube.num_vars - 1]; + if (nout < nstates) { + (void) fprintf(stderr, + "use .symbolic and .symbolic-output to specify\n"); + (void) fprintf(stderr, + "the present state and next state field information\n"); + fatal("disassemble_pla: # outputs < # states\n"); + } + + + present_state = cube.first_part[cube.num_binary_vars]; + present_state_mask = new_cube(); + for(i = 0; i < nstates; i++) { + set_insert(present_state_mask, i + present_state); + } + + next_state = cube.first_part[cube.num_binary_vars+1]; + next_state_mask = new_cube(); + for(i = 0; i < nstates; i++) { + set_insert(next_state_mask, i + next_state); + } + + state_mask = set_or(new_cube(), next_state_mask, present_state_mask); + + F = new_cover(10); + + + /* + * check for arcs which go from ANY state to state #i + */ + for(i = 0; i < nstates; i++) { + tF = new_cover(10); + foreach_set(PLA->F, last, p) { + if (setp_implies(present_state_mask, p)) { /* from any state ! */ + if (is_in_set(p, next_state + i)) { + tF = sf_addset(tF, p); + } + } + } + before = tF->count; + if (before > 0) { + tF = fsm_simplify(tF); + /* don't allow the next state to disappear ... */ + foreach_set(tF, last, p) { + set_insert(p, next_state + i); + } + after = tF->count; + F = sf_append(F, tF); + if (verbose_mode) { + printf("# state EVERY to %d, before=%d after=%d\n", + i, before, after); + } + } + } + + + /* + * some 'arcs' may NOT have a next state -- handle these + * we must unravel the present state part + */ + go_nowhere = new_cover(10); + foreach_set(PLA->F, last, p) { + if (setp_disjoint(p, next_state_mask)) { /* no next state !! */ + go_nowhere = sf_addset(go_nowhere, p); + } + } + before = go_nowhere->count; + go_nowhere = unravel_range(go_nowhere, + cube.num_binary_vars, cube.num_binary_vars); + after = go_nowhere->count; + F = sf_append(F, go_nowhere); + if (verbose_mode) { + printf("# state ANY to NOWHERE, before=%d after=%d\n", before, after); + } + + + /* + * minimize cover for all arcs from state #i to state #j + */ + for(i = 0; i < nstates; i++) { + for(j = 0; j < nstates; j++) { + tF = new_cover(10); + foreach_set(PLA->F, last, p) { + /* not EVERY state */ + if (! setp_implies(present_state_mask, p)) { + if (is_in_set(p, present_state + i)) { + if (is_in_set(p, next_state + j)) { + p1 = set_save(p); + set_diff(p1, p1, state_mask); + set_insert(p1, present_state + i); + set_insert(p1, next_state + j); + tF = sf_addset(tF, p1); + set_free(p1); + } + } + } + } + before = tF->count; + if (before > 0) { + tF = fsm_simplify(tF); + /* don't allow the next state to disappear ... */ + foreach_set(tF, last, p) { + set_insert(p, next_state + j); + } + after = tF->count; + F = sf_append(F, tF); + if (verbose_mode) { + printf("# state %d to %d, before=%d after=%d\n", + i, j, before, after); + } + } + } + } + + + free_cube(state_mask); + free_cube(present_state_mask); + free_cube(next_state_mask); + + free_cover(PLA->F); + PLA->F = F; + free_cover(PLA->D); + PLA->D = new_cover(0); + + setdown_cube(); + FREE(cube.part_size); + cube.num_binary_vars = nin; + cube.num_vars = nin + 3; + cube.part_size = ALLOC(int, cube.num_vars); + cube.part_size[cube.num_binary_vars] = nstates; + cube.part_size[cube.num_binary_vars+1] = nstates; + cube.part_size[cube.num_binary_vars+2] = nout - nstates; + cube_setup(); + + foreach_set(PLA->F, last, p) { + kiss_print_cube(stdout, PLA, p, "~1"); + } +} diff --git a/abc_with_bb_support/src/misc/espresso/indep.c b/abc_with_bb_support/src/misc/espresso/indep.c new file mode 100644 index 000000000..5cb1f2f5f --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/indep.c @@ -0,0 +1,134 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +#include "mincov_int.h" + +static sm_matrix *build_intersection_matrix(); + + +#if 0 +/* + * verify that all rows in 'indep' are actually independent ! + */ +static int +verify_indep_set(A, indep) +sm_matrix *A; +sm_row *indep; +{ + register sm_row *prow, *prow1; + register sm_element *p, *p1; + + for(p = indep->first_col; p != 0; p = p->next_col) { + prow = sm_get_row(A, p->col_num); + for(p1 = p->next_col; p1 != 0; p1 = p1->next_col) { + prow1 = sm_get_row(A, p1->col_num); + if (sm_row_intersects(prow, prow1)) { + return 0; + } + } + } + return 1; +} +#endif + +solution_t * +sm_maximal_independent_set(A, weight) +sm_matrix *A; +int *weight; +{ + register sm_row *best_row, *prow; + register sm_element *p; + int least_weight; + sm_row *save; + sm_matrix *B; + solution_t *indep; + + indep = solution_alloc(); + B = build_intersection_matrix(A); + + while (B->nrows > 0) { + /* Find the row which is disjoint from a maximum number of rows */ + best_row = B->first_row; + for(prow = B->first_row->next_row; prow != 0; prow = prow->next_row) { + if (prow->length < best_row->length) { + best_row = prow; + } + } + + /* Find which element in this row has least weight */ + if (weight == NIL(int)) { + least_weight = 1; + } else { + prow = sm_get_row(A, best_row->row_num); + least_weight = weight[prow->first_col->col_num]; + for(p = prow->first_col->next_col; p != 0; p = p->next_col) { + if (weight[p->col_num] < least_weight) { + least_weight = weight[p->col_num]; + } + } + } + indep->cost += least_weight; + (void) sm_row_insert(indep->row, best_row->row_num); + + /* Discard the rows which intersect this row */ + save = sm_row_dup(best_row); + for(p = save->first_col; p != 0; p = p->next_col) { + sm_delrow(B, p->col_num); + sm_delcol(B, p->col_num); + } + sm_row_free(save); + } + + sm_free(B); + +/* + if (! verify_indep_set(A, indep->row)) { + fail("sm_maximal_independent_set: row set is not independent"); + } +*/ + return indep; +} + +static sm_matrix * +build_intersection_matrix(A) +sm_matrix *A; +{ + register sm_row *prow, *prow1; + register sm_element *p, *p1; + register sm_col *pcol; + sm_matrix *B; + + /* Build row-intersection matrix */ + B = sm_alloc(); + for(prow = A->first_row; prow != 0; prow = prow->next_row) { + + /* Clear flags on all rows we can reach from row 'prow' */ + for(p = prow->first_col; p != 0; p = p->next_col) { + pcol = sm_get_col(A, p->col_num); + for(p1 = pcol->first_row; p1 != 0; p1 = p1->next_row) { + prow1 = sm_get_row(A, p1->row_num); + prow1->flag = 0; + } + } + + /* Now record which rows can be reached */ + for(p = prow->first_col; p != 0; p = p->next_col) { + pcol = sm_get_col(A, p->col_num); + for(p1 = pcol->first_row; p1 != 0; p1 = p1->next_row) { + prow1 = sm_get_row(A, p1->row_num); + if (! prow1->flag) { + prow1->flag = 1; + (void) sm_insert(B, prow->row_num, prow1->row_num); + } + } + } + } + + return B; +} diff --git a/abc_with_bb_support/src/misc/espresso/irred.c b/abc_with_bb_support/src/misc/espresso/irred.c new file mode 100644 index 000000000..7e16ae547 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/irred.c @@ -0,0 +1,440 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +#include "espresso.h" + +static void fcube_is_covered(); +static void ftautology(); +static bool ftaut_special_cases(); + + +static int Rp_current; + +/* + * irredundant -- Return a minimal subset of F + */ + +pcover +irredundant(F, D) +pcover F, D; +{ + mark_irredundant(F, D); + return sf_inactive(F); +} + + +/* + * mark_irredundant -- find redundant cubes, and mark them "INACTIVE" + */ + +void +mark_irredundant(F, D) +pcover F, D; +{ + pcover E, Rt, Rp; + pset p, p1, last; + sm_matrix *table; + sm_row *cover; + sm_element *pe; + + /* extract a minimum cover */ + irred_split_cover(F, D, &E, &Rt, &Rp); + table = irred_derive_table(D, E, Rp); + cover = sm_minimum_cover(table, NIL(int), /* heuristic */ 1, /* debug */ 0); + + /* mark the cubes for the result */ + foreach_set(F, last, p) { + RESET(p, ACTIVE); + RESET(p, RELESSEN); + } + foreach_set(E, last, p) { + p1 = GETSET(F, SIZE(p)); + assert(setp_equal(p1, p)); + SET(p1, ACTIVE); + SET(p1, RELESSEN); /* for essen(), mark as rel. ess. */ + } + sm_foreach_row_element(cover, pe) { + p1 = GETSET(F, pe->col_num); + SET(p1, ACTIVE); + } + + if (debug & IRRED) { + printf("# IRRED: F=%d E=%d R=%d Rt=%d Rp=%d Rc=%d Final=%d Bound=%d\n", + F->count, E->count, Rt->count+Rp->count, Rt->count, Rp->count, + cover->length, E->count + cover->length, 0); + } + + free_cover(E); + free_cover(Rt); + free_cover(Rp); + sm_free(table); + sm_row_free(cover); +} + +/* + * irred_split_cover -- find E, Rt, and Rp from the cover F, D + * + * E -- relatively essential cubes + * Rt -- totally redundant cubes + * Rp -- partially redundant cubes + */ + +void +irred_split_cover(F, D, E, Rt, Rp) +pcover F, D; +pcover *E, *Rt, *Rp; +{ + register pcube p, last; + register int index; + pcover R; + pcube *FD, *ED; + + /* number the cubes of F -- these numbers track into E, Rp, Rt, etc. */ + index = 0; + foreach_set(F, last, p) { + PUTSIZE(p, index); + index++; + } + + *E = new_cover(10); + *Rt = new_cover(10); + *Rp = new_cover(10); + R = new_cover(10); + + /* Split F into E and R */ + FD = cube2list(F, D); + foreach_set(F, last, p) { + if (cube_is_covered(FD, p)) { + R = sf_addset(R, p); + } else { + *E = sf_addset(*E, p); + } + if (debug & IRRED1) { + (void) printf("IRRED1: zr=%d ze=%d to-go=%d time=%s\n", + R->count, (*E)->count, F->count - (R->count + (*E)->count), + print_time(ptime())); + } + } + free_cubelist(FD); + + /* Split R into Rt and Rp */ + ED = cube2list(*E, D); + foreach_set(R, last, p) { + if (cube_is_covered(ED, p)) { + *Rt = sf_addset(*Rt, p); + } else { + *Rp = sf_addset(*Rp, p); + } + if (debug & IRRED1) { + (void) printf("IRRED1: zr=%d zrt=%d to-go=%d time=%s\n", + (*Rp)->count, (*Rt)->count, + R->count - ((*Rp)->count +(*Rt)->count), print_time(ptime())); + } + } + free_cubelist(ED); + + free_cover(R); +} + +/* + * irred_derive_table -- given the covers D, E and the set of + * partially redundant primes Rp, build a covering table showing + * possible selections of primes to cover Rp. + */ + +sm_matrix * +irred_derive_table(D, E, Rp) +pcover D, E, Rp; +{ + register pcube last, p, *list; + sm_matrix *table; + int size_last_dominance, i; + + /* Mark each cube in DE as not part of the redundant set */ + foreach_set(D, last, p) { + RESET(p, REDUND); + } + foreach_set(E, last, p) { + RESET(p, REDUND); + } + + /* Mark each cube in Rp as partially redundant */ + foreach_set(Rp, last, p) { + SET(p, REDUND); /* belongs to redundant set */ + } + + /* For each cube in Rp, find ways to cover its minterms */ + list = cube3list(D, E, Rp); + table = sm_alloc(); + size_last_dominance = 0; + i = 0; + foreach_set(Rp, last, p) { + Rp_current = SIZE(p); + fcube_is_covered(list, p, table); + RESET(p, REDUND); /* can now consider this cube redundant */ + if (debug & IRRED1) { + (void) printf("IRRED1: %d of %d to-go=%d, table=%dx%d time=%s\n", + i, Rp->count, Rp->count - i, + table->nrows, table->ncols, print_time(ptime())); + } + /* try to keep memory limits down by reducing table as we go along */ + if (table->nrows - size_last_dominance > 1000) { + (void) sm_row_dominance(table); + size_last_dominance = table->nrows; + if (debug & IRRED1) { + (void) printf("IRRED1: delete redundant rows, now %dx%d\n", + table->nrows, table->ncols); + } + } + i++; + } + free_cubelist(list); + + return table; +} + +/* cube_is_covered -- determine if a cubelist "covers" a single cube */ +bool +cube_is_covered(T, c) +pcube *T, c; +{ + return tautology(cofactor(T,c)); +} + + + +/* tautology -- answer the tautology question for T */ +bool +tautology(T) +pcube *T; /* T will be disposed of */ +{ + register pcube cl, cr; + register int best, result; + static int taut_level = 0; + + if (debug & TAUT) { + debug_print(T, "TAUTOLOGY", taut_level++); + } + + if ((result = taut_special_cases(T)) == MAYBE) { + cl = new_cube(); + cr = new_cube(); + best = binate_split_select(T, cl, cr, TAUT); + result = tautology(scofactor(T, cl, best)) && + tautology(scofactor(T, cr, best)); + free_cubelist(T); + free_cube(cl); + free_cube(cr); + } + + if (debug & TAUT) { + printf("exit TAUTOLOGY[%d]: %s\n", --taut_level, print_bool(result)); + } + return result; +} + +/* + * taut_special_cases -- check special cases for tautology + */ + +bool +taut_special_cases(T) +pcube *T; /* will be disposed if answer is determined */ +{ + register pcube *T1, *Tsave, p, ceil=cube.temp[0], temp=cube.temp[1]; + pcube *A, *B; + int var; + + /* Check for a row of all 1's which implies tautology */ + for(T1 = T+2; (p = *T1++) != NULL; ) { + if (full_row(p, T[0])) { + free_cubelist(T); + return TRUE; + } + } + + /* Check for a column of all 0's which implies no tautology */ +start: + INLINEset_copy(ceil, T[0]); + for(T1 = T+2; (p = *T1++) != NULL; ) { + INLINEset_or(ceil, ceil, p); + } + if (! setp_equal(ceil, cube.fullset)) { + free_cubelist(T); + return FALSE; + } + + /* Collect column counts, determine unate variables, etc. */ + massive_count(T); + + /* If function is unate (and no row of all 1's), then no tautology */ + if (cdata.vars_unate == cdata.vars_active) { + free_cubelist(T); + return FALSE; + + /* If active in a single variable (and no column of 0's) then tautology */ + } else if (cdata.vars_active == 1) { + free_cubelist(T); + return TRUE; + + /* Check for unate variables, and reduce cover if there are any */ + } else if (cdata.vars_unate != 0) { + /* Form a cube "ceil" with full variables in the unate variables */ + (void) set_copy(ceil, cube.emptyset); + for(var = 0; var < cube.num_vars; var++) { + if (cdata.is_unate[var]) { + INLINEset_or(ceil, ceil, cube.var_mask[var]); + } + } + + /* Save only those cubes that are "full" in all unate variables */ + for(Tsave = T1 = T+2; (p = *T1++) != 0; ) { + if (setp_implies(ceil, set_or(temp, p, T[0]))) { + *Tsave++ = p; + } + } + *Tsave++ = NULL; + T[1] = (pcube) Tsave; + + if (debug & TAUT) { + printf("UNATE_REDUCTION: %d unate variables, reduced to %d\n", + cdata.vars_unate, CUBELISTSIZE(T)); + } + goto start; + + /* Check for component reduction */ + } else if (cdata.var_zeros[cdata.best] < CUBELISTSIZE(T) / 2) { + if (cubelist_partition(T, &A, &B, debug & TAUT) == 0) { + return MAYBE; + } else { + free_cubelist(T); + if (tautology(A)) { + free_cubelist(B); + return TRUE; + } else { + return tautology(B); + } + } + } + + /* We tried as hard as we could, but must recurse from here on */ + return MAYBE; +} + +/* fcube_is_covered -- determine exactly how a cubelist "covers" a cube */ +static void +fcube_is_covered(T, c, table) +pcube *T, c; +sm_matrix *table; +{ + ftautology(cofactor(T,c), table); +} + + +/* ftautology -- find ways to make a tautology */ +static void +ftautology(T, table) +pcube *T; /* T will be disposed of */ +sm_matrix *table; +{ + register pcube cl, cr; + register int best; + static int ftaut_level = 0; + + if (debug & TAUT) { + debug_print(T, "FIND_TAUTOLOGY", ftaut_level++); + } + + if (ftaut_special_cases(T, table) == MAYBE) { + cl = new_cube(); + cr = new_cube(); + best = binate_split_select(T, cl, cr, TAUT); + + ftautology(scofactor(T, cl, best), table); + ftautology(scofactor(T, cr, best), table); + + free_cubelist(T); + free_cube(cl); + free_cube(cr); + } + + if (debug & TAUT) { + (void) printf("exit FIND_TAUTOLOGY[%d]: table is %d by %d\n", + --ftaut_level, table->nrows, table->ncols); + } +} + +static bool +ftaut_special_cases(T, table) +pcube *T; /* will be disposed if answer is determined */ +sm_matrix *table; +{ + register pcube *T1, *Tsave, p, temp = cube.temp[0], ceil = cube.temp[1]; + int var, rownum; + + /* Check for a row of all 1's in the essential cubes */ + for(T1 = T+2; (p = *T1++) != 0; ) { + if (! TESTP(p, REDUND)) { + if (full_row(p, T[0])) { + /* subspace is covered by essentials -- no new rows for table */ + free_cubelist(T); + return TRUE; + } + } + } + + /* Collect column counts, determine unate variables, etc. */ +start: + massive_count(T); + + /* If function is unate, find the rows of all 1's */ + if (cdata.vars_unate == cdata.vars_active) { + /* find which nonessentials cover this subspace */ + rownum = table->last_row ? table->last_row->row_num+1 : 0; + (void) sm_insert(table, rownum, Rp_current); + for(T1 = T+2; (p = *T1++) != 0; ) { + if (TESTP(p, REDUND)) { + /* See if a redundant cube covers this leaf */ + if (full_row(p, T[0])) { + (void) sm_insert(table, rownum, (int) SIZE(p)); + } + } + } + free_cubelist(T); + return TRUE; + + /* Perform unate reduction if there are any unate variables */ + } else if (cdata.vars_unate != 0) { + /* Form a cube "ceil" with full variables in the unate variables */ + (void) set_copy(ceil, cube.emptyset); + for(var = 0; var < cube.num_vars; var++) { + if (cdata.is_unate[var]) { + INLINEset_or(ceil, ceil, cube.var_mask[var]); + } + } + + /* Save only those cubes that are "full" in all unate variables */ + for(Tsave = T1 = T+2; (p = *T1++) != 0; ) { + if (setp_implies(ceil, set_or(temp, p, T[0]))) { + *Tsave++ = p; + } + } + *Tsave++ = 0; + T[1] = (pcube) Tsave; + + if (debug & TAUT) { + printf("UNATE_REDUCTION: %d unate variables, reduced to %d\n", + cdata.vars_unate, CUBELISTSIZE(T)); + } + goto start; + } + + /* Not much we can do about it */ + return MAYBE; +} diff --git a/abc_with_bb_support/src/misc/espresso/main.c b/abc_with_bb_support/src/misc/espresso/main.c new file mode 100644 index 000000000..905a03270 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/main.c @@ -0,0 +1,746 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +/* + * Main driver for espresso + * + * Old style -do xxx, -out xxx, etc. are still supported. + */ + +#include "espresso.h" +#include "main.h" /* table definitions for options */ + +static FILE *last_fp; +static int input_type = FD_type; + + +main(argc, argv) +int argc; +char *argv[]; +{ + int i, j, first, last, strategy, out_type, option; + pPLA PLA, PLA1; + pcover F, Fold, Dold; + pset last1, p; + cost_t cost; + bool error, exact_cover; + long start; + extern char *util_optarg; + extern int util_optind; + + start = ptime(); + + error = FALSE; + init_runtime(); +#ifdef RANDOM + srandom(314973); +#endif + + option = 0; /* default -D: ESPRESSO */ + out_type = F_type; /* default -o: default is ON-set only */ + debug = 0; /* default -d: no debugging info */ + verbose_debug = FALSE; /* default -v: not verbose */ + print_solution = TRUE; /* default -x: print the solution (!) */ + summary = FALSE; /* default -s: no summary */ + trace = FALSE; /* default -t: no trace information */ + strategy = 0; /* default -S: strategy number */ + first = -1; /* default -R: select range */ + last = -1; + remove_essential = TRUE; /* default -e: */ + force_irredundant = TRUE; + unwrap_onset = TRUE; + single_expand = FALSE; + pos = FALSE; + recompute_onset = FALSE; + use_super_gasp = FALSE; + use_random_order = FALSE; + kiss = FALSE; + echo_comments = TRUE; + echo_unknown_commands = TRUE; + exact_cover = FALSE; /* for -qm option, the default */ + + backward_compatibility_hack(&argc, argv, &option, &out_type); + + + /* parse command line options*/ + while ((i = util_getopt(argc, argv, "D:S:de:o:r:stv:x")) != EOF) { + switch(i) { + case 'D': /* -Dcommand invokes a subcommand */ + for(j = 0; option_table[j].name != 0; j++) { + if (strcmp(util_optarg, option_table[j].name) == 0) { + option = j; + break; + } + } + if (option_table[j].name == 0) { + (void) fprintf(stderr, "%s: bad subcommand \"%s\"\n", + argv[0], util_optarg); + exit(1); + } + break; + + case 'o': /* -ooutput selects and output option */ + for(j = 0; pla_types[j].key != 0; j++) { + if (strcmp(util_optarg, pla_types[j].key+1) == 0) { + out_type = pla_types[j].value; + break; + } + } + if (pla_types[j].key == 0) { + (void) fprintf(stderr, "%s: bad output type \"%s\"\n", + argv[0], util_optarg); + exit(1); + } + break; + + case 'e': /* -eespresso selects an option for espresso */ + for(j = 0; esp_opt_table[j].name != 0; j++) { + if (strcmp(util_optarg, esp_opt_table[j].name) == 0) { + *(esp_opt_table[j].variable) = esp_opt_table[j].value; + break; + } + } + if (esp_opt_table[j].name == 0) { + (void) fprintf(stderr, "%s: bad espresso option \"%s\"\n", + argv[0], util_optarg); + exit(1); + } + break; + + case 'd': /* -d turns on (softly) all debug switches */ + debug = debug_table[0].value; + trace = TRUE; + summary = TRUE; + break; + + case 'v': /* -vdebug invokes a debug option */ + verbose_debug = TRUE; + for(j = 0; debug_table[j].name != 0; j++) { + if (strcmp(util_optarg, debug_table[j].name) == 0) { + debug |= debug_table[j].value; + break; + } + } + if (debug_table[j].name == 0) { + (void) fprintf(stderr, "%s: bad debug type \"%s\"\n", + argv[0], util_optarg); + exit(1); + } + break; + + case 't': + trace = TRUE; + break; + + case 's': + summary = TRUE; + break; + + case 'x': /* -x suppress printing of results */ + print_solution = FALSE; + break; + + case 'S': /* -S sets a strategy for several cmds */ + strategy = atoi(util_optarg); + break; + + case 'r': /* -r selects range (outputs or vars) */ + if (sscanf(util_optarg, "%d-%d", &first, &last) < 2) { + (void) fprintf(stderr, "%s: bad output range \"%s\"\n", + argv[0], util_optarg); + exit(1); + } + break; + + default: + usage(); + exit(1); + } + } + + /* provide version information and summaries */ + if (summary || trace) { + /* echo command line and arguments */ + printf("#"); + for(i = 0; i < argc; i++) { + printf(" %s", argv[i]); + } + printf("\n"); + printf("# %s\n", VERSION); + } + + /* the remaining arguments are argv[util_optind ... argc-1] */ + PLA = PLA1 = NIL(PLA_t); + switch(option_table[option].num_plas) { + case 2: + if (util_optind+2 < argc) fatal("trailing arguments on command line"); + getPLA(util_optind++, argc, argv, option, &PLA, out_type); + getPLA(util_optind++, argc, argv, option, &PLA1, out_type); + break; + case 1: + if (util_optind+1 < argc) fatal("trailing arguments on command line"); + getPLA(util_optind++, argc, argv, option, &PLA, out_type); + break; + } + if (util_optind < argc) fatal("trailing arguments on command line"); + + if (summary || trace) { + if (PLA != NIL(PLA_t)) PLA_summary(PLA); + if (PLA1 != NIL(PLA_t)) PLA_summary(PLA1); + } + +/* + * Now a case-statement to decide what to do + */ + + switch(option_table[option].key) { + + +/******************** Espresso operations ********************/ + + case KEY_ESPRESSO: + Fold = sf_save(PLA->F); + PLA->F = espresso(PLA->F, PLA->D, PLA->R); + EXECUTE(error=verify(PLA->F,Fold,PLA->D), VERIFY_TIME, PLA->F, cost); + if (error) { + print_solution = FALSE; + PLA->F = Fold; + (void) check_consistency(PLA); + } else { + free_cover(Fold); + } + break; + + case KEY_MANY_ESPRESSO: { + int pla_type; + do { + EXEC(PLA->F=espresso(PLA->F,PLA->D,PLA->R),"ESPRESSO ",PLA->F); + if (print_solution) { + fprint_pla(stdout, PLA, out_type); + (void) fflush(stdout); + } + pla_type = PLA->pla_type; + free_PLA(PLA); + setdown_cube(); + FREE(cube.part_size); + } while (read_pla(last_fp, TRUE, TRUE, pla_type, &PLA) != EOF); + exit(0); + } + + case KEY_simplify: + EXEC(PLA->F = simplify(cube1list(PLA->F)), "SIMPLIFY ", PLA->F); + break; + + case KEY_so: /* minimize all functions as single-output */ + if (strategy < 0 || strategy > 1) { + strategy = 0; + } + so_espresso(PLA, strategy); + break; + + case KEY_so_both: /* minimize all functions as single-output */ + if (strategy < 0 || strategy > 1) { + strategy = 0; + } + so_both_espresso(PLA, strategy); + break; + + case KEY_expand: /* execute expand */ + EXECUTE(PLA->F=expand(PLA->F,PLA->R,FALSE),EXPAND_TIME, PLA->F, cost); + break; + + case KEY_irred: /* extract minimal irredundant subset */ + EXECUTE(PLA->F = irredundant(PLA->F, PLA->D), IRRED_TIME, PLA->F, cost); + break; + + case KEY_reduce: /* perform reduction */ + EXECUTE(PLA->F = reduce(PLA->F, PLA->D), REDUCE_TIME, PLA->F, cost); + break; + + case KEY_essen: /* check for essential primes */ + foreach_set(PLA->F, last1, p) { + SET(p, RELESSEN); + RESET(p, NONESSEN); + } + EXECUTE(F = essential(&(PLA->F), &(PLA->D)), ESSEN_TIME, PLA->F, cost); + free_cover(F); + break; + + case KEY_super_gasp: + PLA->F = super_gasp(PLA->F, PLA->D, PLA->R, &cost); + break; + + case KEY_gasp: + PLA->F = last_gasp(PLA->F, PLA->D, PLA->R, &cost); + break; + + case KEY_make_sparse: /* make_sparse step of Espresso */ + PLA->F = make_sparse(PLA->F, PLA->D, PLA->R); + break; + + case KEY_exact: + exact_cover = TRUE; + + case KEY_qm: + Fold = sf_save(PLA->F); + PLA->F = minimize_exact(PLA->F, PLA->D, PLA->R, exact_cover); + EXECUTE(error=verify(PLA->F,Fold,PLA->D), VERIFY_TIME, PLA->F, cost); + if (error) { + print_solution = FALSE; + PLA->F = Fold; + (void) check_consistency(PLA); + } + free_cover(Fold); + break; + + case KEY_primes: /* generate all prime implicants */ + EXEC(PLA->F = primes_consensus(cube2list(PLA->F, PLA->D)), + "PRIMES ", PLA->F); + break; + + case KEY_map: /* print out a Karnaugh map of function */ + map(PLA->F); + print_solution = FALSE; + break; + + + +/******************** Output phase and bit pairing ********************/ + + case KEY_opo: /* sasao output phase assignment */ + phase_assignment(PLA, strategy); + break; + + case KEY_opoall: /* try all phase assignments (!) */ + if (first < 0 || first >= cube.part_size[cube.output]) { + first = 0; + } + if (last < 0 || last >= cube.part_size[cube.output]) { + last = cube.part_size[cube.output] - 1; + } + opoall(PLA, first, last, strategy); + break; + + case KEY_pair: /* find an optimal pairing */ + find_optimal_pairing(PLA, strategy); + break; + + case KEY_pairall: /* try all pairings !! */ + pair_all(PLA, strategy); + break; + + + +/******************** Simple cover operations ********************/ + + case KEY_echo: /* echo the PLA */ + break; + + case KEY_taut: /* tautology check */ + printf("ON-set is%sa tautology\n", + tautology(cube1list(PLA->F)) ? " " : " not "); + print_solution = FALSE; + break; + + case KEY_contain: /* single cube containment */ + PLA->F = sf_contain(PLA->F); + break; + + case KEY_intersect: /* cover intersection */ + PLA->F = cv_intersect(PLA->F, PLA1->F); + break; + + case KEY_union: /* cover union */ + PLA->F = sf_union(PLA->F, PLA1->F); + break; + + case KEY_disjoint: /* make cover disjoint */ + PLA->F = make_disjoint(PLA->F); + break; + + case KEY_dsharp: /* cover disjoint-sharp */ + PLA->F = cv_dsharp(PLA->F, PLA1->F); + break; + + case KEY_sharp: /* cover sharp */ + PLA->F = cv_sharp(PLA->F, PLA1->F); + break; + + case KEY_lexsort: /* lexical sort order */ + PLA->F = lex_sort(PLA->F); + break; + + case KEY_stats: /* print info on size */ + if (! summary) PLA_summary(PLA); + print_solution = FALSE; + break; + + case KEY_minterms: /* explode into minterms */ + if (first < 0 || first >= cube.num_vars) { + first = 0; + } + if (last < 0 || last >= cube.num_vars) { + last = cube.num_vars - 1; + } + PLA->F = sf_dupl(unravel_range(PLA->F, first, last)); + break; + + case KEY_d1merge: /* distance-1 merge */ + if (first < 0 || first >= cube.num_vars) { + first = 0; + } + if (last < 0 || last >= cube.num_vars) { + last = cube.num_vars - 1; + } + for(i = first; i <= last; i++) { + PLA->F = d1merge(PLA->F, i); + } + break; + + case KEY_d1merge_in: /* distance-1 merge inputs only */ + for(i = 0; i < cube.num_binary_vars; i++) { + PLA->F = d1merge(PLA->F, i); + } + break; + + case KEY_PLA_verify: /* check two PLAs for equivalence */ + EXECUTE(error = PLA_verify(PLA, PLA1), VERIFY_TIME, PLA->F, cost); + if (error) { + printf("PLA comparison failed; the PLA's are not equivalent\n"); + exit(1); + } else { + printf("PLA's compared equal\n"); + exit(0); + } + break; /* silly */ + + case KEY_verify: /* check two covers for equivalence */ + Fold = PLA->F; Dold = PLA->D; F = PLA1->F; + EXECUTE(error=verify(F, Fold, Dold), VERIFY_TIME, PLA->F, cost); + if (error) { + printf("PLA comparison failed; the PLA's are not equivalent\n"); + exit(1); + } else { + printf("PLA's compared equal\n"); + exit(0); + } + break; /* silly */ + + case KEY_check: /* check consistency */ + (void) check_consistency(PLA); + print_solution = FALSE; + break; + + case KEY_mapdc: /* compute don't care set */ + map_dcset(PLA); + out_type = FD_type; + break; + + case KEY_equiv: + find_equiv_outputs(PLA); + print_solution = FALSE; + break; + + case KEY_separate: /* remove PLA->D from PLA->F */ + PLA->F = complement(cube2list(PLA->D, PLA->R)); + break; + + case KEY_xor: { + pcover T1 = cv_intersect(PLA->F, PLA1->R); + pcover T2 = cv_intersect(PLA1->F, PLA->R); + free_cover(PLA->F); + PLA->F = sf_contain(sf_join(T1, T2)); + free_cover(T1); + free_cover(T2); + break; + } + + case KEY_fsm: { + disassemble_fsm(PLA, summary); + print_solution = FALSE; + break; + } + + case KEY_test: { + pcover T, E; + T = sf_join(PLA->D, PLA->R); + E = new_cover(10); + sf_free(PLA->F); + EXECUTE(PLA->F = complement(cube1list(T)), COMPL_TIME, PLA->F, cost); + EXECUTE(PLA->F = expand(PLA->F, T, FALSE), EXPAND_TIME, PLA->F, cost); + EXECUTE(PLA->F = irredundant(PLA->F, E), IRRED_TIME, PLA->F, cost); + sf_free(T); + T = sf_join(PLA->F, PLA->R); + EXECUTE(PLA->D = expand(PLA->D, T, FALSE), EXPAND_TIME, PLA->D, cost); + EXECUTE(PLA->D = irredundant(PLA->D, E), IRRED_TIME, PLA->D, cost); + sf_free(T); + sf_free(E); + break; + } + + + } + + /* Print a runtime summary if trace mode enabled */ + if (trace) { + runtime(); + } + + /* Print total runtime */ + if (summary || trace) { + print_trace(PLA->F, option_table[option].name, ptime()-start); + } + + /* Output the solution */ + if (print_solution) { + EXECUTE(fprint_pla(stdout, PLA, out_type), WRITE_TIME, PLA->F, cost); + } + + /* Crash and burn if there was a verify error */ + if (error) { + fatal("cover verification failed"); + } + + /* cleanup all used memory */ + free_PLA(PLA); + FREE(cube.part_size); + setdown_cube(); /* free the cube/cdata structure data */ + sf_cleanup(); /* free unused set structures */ + sm_cleanup(); /* sparse matrix cleanup */ + + exit(0); +} + + +getPLA(opt, argc, argv, option, PLA, out_type) +int opt; +int argc; +char *argv[]; +int option; +pPLA *PLA; +int out_type; +{ + FILE *fp; + int needs_dcset, needs_offset; + char *fname; + + if (opt >= argc) { + fp = stdin; + fname = "(stdin)"; + } else { + fname = argv[opt]; + if (strcmp(fname, "-") == 0) { + fp = stdin; + } else if ((fp = fopen(argv[opt], "r")) == NULL) { + (void) fprintf(stderr, "%s: Unable to open %s\n", argv[0], fname); + exit(1); + } + } + if (option_table[option].key == KEY_echo) { + needs_dcset = (out_type & D_type) != 0; + needs_offset = (out_type & R_type) != 0; + } else { + needs_dcset = option_table[option].needs_dcset; + needs_offset = option_table[option].needs_offset; + } + + if (read_pla(fp, needs_dcset, needs_offset, input_type, PLA) == EOF) { + (void) fprintf(stderr, "%s: Unable to find PLA on file %s\n", argv[0], fname); + exit(1); + } + (*PLA)->filename = util_strsav(fname); + filename = (*PLA)->filename; +/* (void) fclose(fp);*/ +/* hackto support -Dmany */ + last_fp = fp; +} + + +runtime() +{ + int i; + long total = 1, temp; + + for(i = 0; i < TIME_COUNT; i++) { + total += total_time[i]; + } + for(i = 0; i < TIME_COUNT; i++) { + if (total_calls[i] != 0) { + temp = 100 * total_time[i]; + printf("# %s\t%2d call(s) for %s (%2ld.%01ld%%)\n", + total_name[i], total_calls[i], print_time(total_time[i]), + temp/total, (10 * (temp%total)) / total); + } + } +} + + +init_runtime() +{ + total_name[READ_TIME] = "READ "; + total_name[WRITE_TIME] = "WRITE "; + total_name[COMPL_TIME] = "COMPL "; + total_name[REDUCE_TIME] = "REDUCE "; + total_name[EXPAND_TIME] = "EXPAND "; + total_name[ESSEN_TIME] = "ESSEN "; + total_name[IRRED_TIME] = "IRRED "; + total_name[GREDUCE_TIME] = "REDUCE_GASP"; + total_name[GEXPAND_TIME] = "EXPAND_GASP"; + total_name[GIRRED_TIME] = "IRRED_GASP "; + total_name[MV_REDUCE_TIME] ="MV_REDUCE "; + total_name[RAISE_IN_TIME] = "RAISE_IN "; + total_name[VERIFY_TIME] = "VERIFY "; + total_name[PRIMES_TIME] = "PRIMES "; + total_name[MINCOV_TIME] = "MINCOV "; +} + + +subcommands() +{ + int i, col; + printf(" "); + col = 16; + for(i = 0; option_table[i].name != 0; i++) { + if ((col + strlen(option_table[i].name) + 1) > 76) { + printf(",\n "); + col = 16; + } else if (i != 0) { + printf(", "); + } + printf("%s", option_table[i].name); + col += strlen(option_table[i].name) + 2; + } + printf("\n"); +} + + +usage() +{ + printf("%s\n\n", VERSION); + printf("SYNOPSIS: espresso [options] [file]\n\n"); + printf(" -d Enable debugging\n"); + printf(" -e[opt] Select espresso option:\n"); + printf(" fast, ness, nirr, nunwrap, onset, pos, strong,\n"); + printf(" eat, eatdots, kiss, random\n"); + printf(" -o[type] Select output format:\n"); + printf(" f, fd, fr, fdr, pleasure, eqntott, kiss, cons\n"); + printf(" -rn-m Select range for subcommands:\n"); + printf(" d1merge: first and last variables (0 ... m-1)\n"); + printf(" minterms: first and last variables (0 ... m-1)\n"); + printf(" opoall: first and last outputs (0 ... m-1)\n"); + printf(" -s Provide short execution summary\n"); + printf(" -t Provide longer execution trace\n"); + printf(" -x Suppress printing of solution\n"); + printf(" -v[type] Verbose debugging detail (-v '' for all)\n"); + printf(" -D[cmd] Execute subcommand 'cmd':\n"); + subcommands(); + printf(" -Sn Select strategy for subcommands:\n"); + printf(" opo: bit2=exact bit1=repeated bit0=skip sparse\n"); + printf(" opoall: 0=minimize, 1=exact\n"); + printf(" pair: 0=algebraic, 1=strongd, 2=espresso, 3=exact\n"); + printf(" pairall: 0=minimize, 1=exact, 2=opo\n"); + printf(" so_espresso: 0=minimize, 1=exact\n"); + printf(" so_both: 0=minimize, 1=exact\n"); +} + +/* + * Hack for backward compatibility (ACK! ) + */ + +backward_compatibility_hack(argc, argv, option, out_type) +int *argc; +char **argv; +int *option; +int *out_type; +{ + int i, j; + + /* Scan the argument list for something to do (default is ESPRESSO) */ + *option = 0; + for(i = 1; i < (*argc)-1; i++) { + if (strcmp(argv[i], "-do") == 0) { + for(j = 0; option_table[j].name != 0; j++) + if (strcmp(argv[i+1], option_table[j].name) == 0) { + *option = j; + delete_arg(argc, argv, i+1); + delete_arg(argc, argv, i); + break; + } + if (option_table[j].name == 0) { + (void) fprintf(stderr, + "espresso: bad keyword \"%s\" following -do\n",argv[i+1]); + exit(1); + } + break; + } + } + + for(i = 1; i < (*argc)-1; i++) { + if (strcmp(argv[i], "-out") == 0) { + for(j = 0; pla_types[j].key != 0; j++) + if (strcmp(pla_types[j].key+1, argv[i+1]) == 0) { + *out_type = pla_types[j].value; + delete_arg(argc, argv, i+1); + delete_arg(argc, argv, i); + break; + } + if (pla_types[j].key == 0) { + (void) fprintf(stderr, + "espresso: bad keyword \"%s\" following -out\n",argv[i+1]); + exit(1); + } + break; + } + } + + for(i = 1; i < (*argc); i++) { + if (argv[i][0] == '-') { + for(j = 0; esp_opt_table[j].name != 0; j++) { + if (strcmp(argv[i]+1, esp_opt_table[j].name) == 0) { + delete_arg(argc, argv, i); + *(esp_opt_table[j].variable) = esp_opt_table[j].value; + break; + } + } + } + } + + if (check_arg(argc, argv, "-fdr")) input_type = FDR_type; + if (check_arg(argc, argv, "-fr")) input_type = FR_type; + if (check_arg(argc, argv, "-f")) input_type = F_type; +} + + +/* delete_arg -- delete an argument from the argument list */ +delete_arg(argc, argv, num) +int *argc, num; +register char *argv[]; +{ + register int i; + (*argc)--; + for(i = num; i < *argc; i++) { + argv[i] = argv[i+1]; + } +} + + +/* check_arg -- scan argv for an argument, and return TRUE if found */ +bool check_arg(argc, argv, s) +int *argc; +register char *argv[], *s; +{ + register int i; + for(i = 1; i < *argc; i++) { + if (strcmp(argv[i], s) == 0) { + delete_arg(argc, argv, i); + return TRUE; + } + } + return FALSE; +} diff --git a/abc_with_bb_support/src/misc/espresso/main.h b/abc_with_bb_support/src/misc/espresso/main.h new file mode 100644 index 000000000..94ea674d4 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/main.h @@ -0,0 +1,122 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +enum keys { + KEY_ESPRESSO, KEY_PLA_verify, KEY_check, KEY_contain, KEY_d1merge, + KEY_disjoint, KEY_dsharp, KEY_echo, KEY_essen, KEY_exact, KEY_expand, + KEY_gasp, KEY_intersect, KEY_irred, KEY_lexsort, KEY_make_sparse, + KEY_map, KEY_mapdc, KEY_minterms, KEY_opo, KEY_opoall, + KEY_pair, KEY_pairall, KEY_primes, KEY_qm, KEY_reduce, KEY_sharp, + KEY_simplify, KEY_so, KEY_so_both, KEY_stats, KEY_super_gasp, KEY_taut, + KEY_test, KEY_equiv, KEY_union, KEY_verify, KEY_MANY_ESPRESSO, + KEY_separate, KEY_xor, KEY_d1merge_in, KEY_fsm, + KEY_unknown +}; + +/* Lookup table for program options */ +struct { + char *name; + enum keys key; + int num_plas; + bool needs_offset; + bool needs_dcset; +} option_table [] = { + /* ways to minimize functions */ + "ESPRESSO", KEY_ESPRESSO, 1, TRUE, TRUE, /* must be first */ + "many", KEY_MANY_ESPRESSO, 1, TRUE, TRUE, + "exact", KEY_exact, 1, TRUE, TRUE, + "qm", KEY_qm, 1, TRUE, TRUE, + "single_output", KEY_so, 1, TRUE, TRUE, + "so", KEY_so, 1, TRUE, TRUE, + "so_both", KEY_so_both, 1, TRUE, TRUE, + "simplify", KEY_simplify, 1, FALSE, FALSE, + "echo", KEY_echo, 1, FALSE, FALSE, + + /* output phase assignment and assignment of inputs to two-bit decoders */ + "opo", KEY_opo, 1, TRUE, TRUE, + "opoall", KEY_opoall, 1, TRUE, TRUE, + "pair", KEY_pair, 1, TRUE, TRUE, + "pairall", KEY_pairall, 1, TRUE, TRUE, + + /* Ways to check covers */ + "check", KEY_check, 1, TRUE, TRUE, + "stats", KEY_stats, 1, FALSE, FALSE, + "verify", KEY_verify, 2, FALSE, TRUE, + "PLAverify", KEY_PLA_verify, 2, FALSE, TRUE, + + /* hacks */ + "equiv", KEY_equiv, 1, TRUE, TRUE, + "map", KEY_map, 1, FALSE, FALSE, + "mapdc", KEY_mapdc, 1, FALSE, FALSE, + "fsm", KEY_fsm, 1, FALSE, TRUE, + + /* the basic boolean operations on covers */ + "contain", KEY_contain, 1, FALSE, FALSE, + "d1merge", KEY_d1merge, 1, FALSE, FALSE, + "d1merge_in", KEY_d1merge_in, 1, FALSE, FALSE, + "disjoint", KEY_disjoint, 1, TRUE, FALSE, + "dsharp", KEY_dsharp, 2, FALSE, FALSE, + "intersect", KEY_intersect, 2, FALSE, FALSE, + "minterms", KEY_minterms, 1, FALSE, FALSE, + "primes", KEY_primes, 1, FALSE, TRUE, + "separate", KEY_separate, 1, TRUE, TRUE, + "sharp", KEY_sharp, 2, FALSE, FALSE, + "union", KEY_union, 2, FALSE, FALSE, + "xor", KEY_xor, 2, TRUE, TRUE, + + /* debugging only -- call each step of the espresso algorithm */ + "essen", KEY_essen, 1, FALSE, TRUE, + "expand", KEY_expand, 1, TRUE, FALSE, + "gasp", KEY_gasp, 1, TRUE, TRUE, + "irred", KEY_irred, 1, FALSE, TRUE, + "make_sparse", KEY_make_sparse, 1, TRUE, TRUE, + "reduce", KEY_reduce, 1, FALSE, TRUE, + "taut", KEY_taut, 1, FALSE, FALSE, + "super_gasp", KEY_super_gasp, 1, TRUE, TRUE, + "lexsort", KEY_lexsort, 1, FALSE, FALSE, + "test", KEY_test, 1, TRUE, TRUE, + 0, KEY_unknown, 0, FALSE, FALSE /* must be last */ +}; + + +struct { + char *name; + int value; +} debug_table[] = { + "", EXPAND + ESSEN + IRRED + REDUCE + SPARSE + GASP + SHARP + MINCOV, + "compl", COMPL, "essen", ESSEN, + "expand", EXPAND, "expand1", EXPAND1|EXPAND, + "irred", IRRED, "irred1", IRRED1|IRRED, + "reduce", REDUCE, "reduce1", REDUCE1|REDUCE, + "mincov", MINCOV, "mincov1", MINCOV1|MINCOV, + "sparse", SPARSE, "sharp", SHARP, + "taut", TAUT, "gasp", GASP, + "exact", EXACT, + 0, +}; + + +struct { + char *name; + int *variable; + int value; +} esp_opt_table[] = { + "eat", &echo_comments, FALSE, + "eatdots", &echo_unknown_commands, FALSE, + "fast", &single_expand, TRUE, + "kiss", &kiss, TRUE, + "ness", &remove_essential, FALSE, + "nirr", &force_irredundant, FALSE, + "nunwrap", &unwrap_onset, FALSE, + "onset", &recompute_onset, TRUE, + "pos", &pos, TRUE, + "random", &use_random_order, TRUE, + "strong", &use_super_gasp, TRUE, + 0, +}; diff --git a/abc_with_bb_support/src/misc/espresso/map.c b/abc_with_bb_support/src/misc/espresso/map.c new file mode 100644 index 000000000..4e2abc9cd --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/map.c @@ -0,0 +1,115 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +#include "espresso.h" + +static pcube Gcube; +static pset Gminterm; + +pset minterms(T) +pcover T; +{ + int size, var; + register pcube last; + + size = 1; + for(var = 0; var < cube.num_vars; var++) + size *= cube.part_size[var]; + Gminterm = set_new(size); + + foreach_set(T, last, Gcube) + explode(cube.num_vars-1, 0); + + return Gminterm; +} + + +void explode(var, z) +int var, z; +{ + int i, last = cube.last_part[var]; + for(i=cube.first_part[var], z *= cube.part_size[var]; i<=last; i++, z++) + if (is_in_set(Gcube, i)) + if (var == 0) + set_insert(Gminterm, z); + else + explode(var-1, z); +} + + +static int mapindex[16][16] = { + 0, 1, 3, 2, 16, 17, 19, 18, 80, 81, 83, 82, 64, 65, 67, 66, + 4, 5, 7, 6, 20, 21, 23, 22, 84, 85, 87, 86, 68, 69, 71, 70, + 12, 13, 15, 14, 28, 29, 31, 30, 92, 93, 95, 94, 76, 77, 79, 78, + 8, 9, 11, 10, 24, 25, 27, 26, 88, 89, 91, 90, 72, 73, 75, 74, + + 32, 33, 35, 34, 48, 49, 51, 50, 112,113,115,114, 96, 97, 99, 98, + 36, 37, 39, 38, 52, 53, 55, 54, 116,117,119,118, 100,101,103,102, + 44, 45, 47, 46, 60, 61, 63, 62, 124,125,127,126, 108,109,111,110, + 40, 41, 43, 42, 56, 57, 59, 58, 120,121,123,122, 104,105,107,106, + + + 160,161,163,162, 176,177,179,178, 240,241,243,242, 224,225,227,226, + 164,165,167,166, 180,181,183,182, 244,245,247,246, 228,229,231,230, + 172,173,175,174, 188,189,191,190, 252,253,255,254, 236,237,239,238, + 168,169,171,170, 184,185,187,186, 248,249,251,250, 232,233,235,234, + + 128,129,131,130, 144,145,147,146, 208,209,211,210, 192,193,195,194, + 132,133,135,134, 148,149,151,150, 212,213,215,214, 196,197,199,198, + 140,141,143,142, 156,157,159,158, 220,221,223,222, 204,205,207,206, + 136,137,139,138, 152,153,155,154, 216,217,219,218, 200,201,203,202 +}; + +#define POWER2(n) (1 << n) +void map(T) +pcover T; +{ + int j, k, l, other_input_offset, output_offset, outnum, ind; + int largest_input_ind, numout; + char c; + pset m; + bool some_output; + + m = minterms(T); + largest_input_ind = POWER2(cube.num_binary_vars); + numout = cube.part_size[cube.num_vars-1]; + + for(outnum = 0; outnum < numout; outnum++) { + output_offset = outnum * largest_input_ind; + printf("\n\nOutput space # %d\n", outnum); + for(l = 0; l <= MAX(cube.num_binary_vars - 8, 0); l++) { + other_input_offset = l * 256; + for(k = 0; k < 16; k++) { + some_output = FALSE; + for(j = 0; j < 16; j++) { + ind = mapindex[k][j] + other_input_offset; + if (ind < largest_input_ind) { + c = is_in_set(m, ind+output_offset) ? '1' : '.'; + putchar(c); + some_output = TRUE; + } + if ((j+1)%4 == 0) + putchar(' '); + if ((j+1)%8 == 0) + printf(" "); + } + if (some_output) + putchar('\n'); + if ((k+1)%4 == 0) { + if (k != 15 && mapindex[k+1][0] >= largest_input_ind) + break; + putchar('\n'); + } + if ((k+1)%8 == 0) + putchar('\n'); + } + } + } + set_free(m); +} diff --git a/abc_with_bb_support/src/misc/espresso/matrix.c b/abc_with_bb_support/src/misc/espresso/matrix.c new file mode 100644 index 000000000..4fb3f1c03 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/matrix.c @@ -0,0 +1,574 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +//#include "port.h" +#include "sparse_int.h" + +/* + * free-lists are only used if 'FAST_AND_LOOSE' is set; this is because + * we lose the debugging capability of libmm_t which trashes objects when + * they are free'd. However, FAST_AND_LOOSE is much faster if matrices + * are created and freed frequently. + */ + +#ifdef FAST_AND_LOOSE +sm_element *sm_element_freelist; +sm_row *sm_row_freelist; +sm_col *sm_col_freelist; +#endif + + +sm_matrix * +sm_alloc() +{ + register sm_matrix *A; + + A = ALLOC(sm_matrix, 1); + A->rows = NIL(sm_row *); + A->cols = NIL(sm_col *); + A->nrows = A->ncols = 0; + A->rows_size = A->cols_size = 0; + A->first_row = A->last_row = NIL(sm_row); + A->first_col = A->last_col = NIL(sm_col); + A->user_word = NIL(char); /* for our user ... */ + return A; +} + + +sm_matrix * +sm_alloc_size(row, col) +int row, col; +{ + register sm_matrix *A; + + A = sm_alloc(); + sm_resize(A, row, col); + return A; +} + + +void +sm_free(A) +sm_matrix *A; +{ +#ifdef FAST_AND_LOOSE + register sm_row *prow; + + if (A->first_row != 0) { + for(prow = A->first_row; prow != 0; prow = prow->next_row) { + /* add the elements to the free list of elements */ + prow->last_col->next_col = sm_element_freelist; + sm_element_freelist = prow->first_col; + } + + /* Add the linked list of rows to the row-free-list */ + A->last_row->next_row = sm_row_freelist; + sm_row_freelist = A->first_row; + + /* Add the linked list of cols to the col-free-list */ + A->last_col->next_col = sm_col_freelist; + sm_col_freelist = A->first_col; + } +#else + register sm_row *prow, *pnext_row; + register sm_col *pcol, *pnext_col; + + for(prow = A->first_row; prow != 0; prow = pnext_row) { + pnext_row = prow->next_row; + sm_row_free(prow); + } + for(pcol = A->first_col; pcol != 0; pcol = pnext_col) { + pnext_col = pcol->next_col; + pcol->first_row = pcol->last_row = NIL(sm_element); + sm_col_free(pcol); + } +#endif + + /* Free the arrays to map row/col numbers into pointers */ + FREE(A->rows); + FREE(A->cols); + FREE(A); +} + + +sm_matrix * +sm_dup(A) +sm_matrix *A; +{ + register sm_row *prow; + register sm_element *p; + register sm_matrix *B; + + B = sm_alloc(); + if (A->last_row != 0) { + sm_resize(B, A->last_row->row_num, A->last_col->col_num); + for(prow = A->first_row; prow != 0; prow = prow->next_row) { + for(p = prow->first_col; p != 0; p = p->next_col) { + (void) sm_insert(B, p->row_num, p->col_num); + } + } + } + return B; +} + + +void +sm_resize(A, row, col) +register sm_matrix *A; +int row, col; +{ + register int i, new_size; + + if (row >= A->rows_size) { + new_size = MAX(A->rows_size*2, row+1); + A->rows = REALLOC(sm_row *, A->rows, new_size); + for(i = A->rows_size; i < new_size; i++) { + A->rows[i] = NIL(sm_row); + } + A->rows_size = new_size; + } + + if (col >= A->cols_size) { + new_size = MAX(A->cols_size*2, col+1); + A->cols = REALLOC(sm_col *, A->cols, new_size); + for(i = A->cols_size; i < new_size; i++) { + A->cols[i] = NIL(sm_col); + } + A->cols_size = new_size; + } +} + + +/* + * insert -- insert a value into the matrix + */ +sm_element * +sm_insert(A, row, col) +register sm_matrix *A; +register int row, col; +{ + register sm_row *prow; + register sm_col *pcol; + register sm_element *element; + sm_element *save_element; + + if (row >= A->rows_size || col >= A->cols_size) { + sm_resize(A, row, col); + } + + prow = A->rows[row]; + if (prow == NIL(sm_row)) { + prow = A->rows[row] = sm_row_alloc(); + prow->row_num = row; + sorted_insert(sm_row, A->first_row, A->last_row, A->nrows, + next_row, prev_row, row_num, row, prow); + } + + pcol = A->cols[col]; + if (pcol == NIL(sm_col)) { + pcol = A->cols[col] = sm_col_alloc(); + pcol->col_num = col; + sorted_insert(sm_col, A->first_col, A->last_col, A->ncols, + next_col, prev_col, col_num, col, pcol); + } + + /* get a new item, save its address */ + sm_element_alloc(element); + save_element = element; + + /* insert it into the row list */ + sorted_insert(sm_element, prow->first_col, prow->last_col, + prow->length, next_col, prev_col, col_num, col, element); + + /* if it was used, also insert it into the column list */ + if (element == save_element) { + sorted_insert(sm_element, pcol->first_row, pcol->last_row, + pcol->length, next_row, prev_row, row_num, row, element); + } else { + /* otherwise, it was already in matrix -- free element we allocated */ + sm_element_free(save_element); + } + return element; +} + + +sm_element * +sm_find(A, rownum, colnum) +sm_matrix *A; +int rownum, colnum; +{ + sm_row *prow; + sm_col *pcol; + + prow = sm_get_row(A, rownum); + if (prow == NIL(sm_row)) { + return NIL(sm_element); + } else { + pcol = sm_get_col(A, colnum); + if (pcol == NIL(sm_col)) { + return NIL(sm_element); + } + if (prow->length < pcol->length) { + return sm_row_find(prow, colnum); + } else { + return sm_col_find(pcol, rownum); + } + } +} + + +void +sm_remove(A, rownum, colnum) +sm_matrix *A; +int rownum, colnum; +{ + sm_remove_element(A, sm_find(A, rownum, colnum)); +} + + + +void +sm_remove_element(A, p) +register sm_matrix *A; +register sm_element *p; +{ + register sm_row *prow; + register sm_col *pcol; + + if (p == 0) return; + + /* Unlink the element from its row */ + prow = sm_get_row(A, p->row_num); + dll_unlink(p, prow->first_col, prow->last_col, + next_col, prev_col, prow->length); + + /* if no more elements in the row, discard the row header */ + if (prow->first_col == NIL(sm_element)) { + sm_delrow(A, p->row_num); + } + + /* Unlink the element from its column */ + pcol = sm_get_col(A, p->col_num); + dll_unlink(p, pcol->first_row, pcol->last_row, + next_row, prev_row, pcol->length); + + /* if no more elements in the column, discard the column header */ + if (pcol->first_row == NIL(sm_element)) { + sm_delcol(A, p->col_num); + } + + sm_element_free(p); +} + +void +sm_delrow(A, i) +sm_matrix *A; +int i; +{ + register sm_element *p, *pnext; + sm_col *pcol; + sm_row *prow; + + prow = sm_get_row(A, i); + if (prow != NIL(sm_row)) { + /* walk across the row */ + for(p = prow->first_col; p != 0; p = pnext) { + pnext = p->next_col; + + /* unlink the item from the column (and delete it) */ + pcol = sm_get_col(A, p->col_num); + sm_col_remove_element(pcol, p); + + /* discard the column if it is now empty */ + if (pcol->first_row == NIL(sm_element)) { + sm_delcol(A, pcol->col_num); + } + } + + /* discard the row -- we already threw away the elements */ + A->rows[i] = NIL(sm_row); + dll_unlink(prow, A->first_row, A->last_row, + next_row, prev_row, A->nrows); + prow->first_col = prow->last_col = NIL(sm_element); + sm_row_free(prow); + } +} + +void +sm_delcol(A, i) +sm_matrix *A; +int i; +{ + register sm_element *p, *pnext; + sm_row *prow; + sm_col *pcol; + + pcol = sm_get_col(A, i); + if (pcol != NIL(sm_col)) { + /* walk down the column */ + for(p = pcol->first_row; p != 0; p = pnext) { + pnext = p->next_row; + + /* unlink the element from the row (and delete it) */ + prow = sm_get_row(A, p->row_num); + sm_row_remove_element(prow, p); + + /* discard the row if it is now empty */ + if (prow->first_col == NIL(sm_element)) { + sm_delrow(A, prow->row_num); + } + } + + /* discard the column -- we already threw away the elements */ + A->cols[i] = NIL(sm_col); + dll_unlink(pcol, A->first_col, A->last_col, + next_col, prev_col, A->ncols); + pcol->first_row = pcol->last_row = NIL(sm_element); + sm_col_free(pcol); + } +} + +void +sm_copy_row(dest, dest_row, prow) +register sm_matrix *dest; +int dest_row; +sm_row *prow; +{ + register sm_element *p; + + for(p = prow->first_col; p != 0; p = p->next_col) { + (void) sm_insert(dest, dest_row, p->col_num); + } +} + + +void +sm_copy_col(dest, dest_col, pcol) +register sm_matrix *dest; +int dest_col; +sm_col *pcol; +{ + register sm_element *p; + + for(p = pcol->first_row; p != 0; p = p->next_row) { + (void) sm_insert(dest, dest_col, p->row_num); + } +} + + +sm_row * +sm_longest_row(A) +sm_matrix *A; +{ + register sm_row *large_row, *prow; + register int max_length; + + max_length = 0; + large_row = NIL(sm_row); + for(prow = A->first_row; prow != 0; prow = prow->next_row) { + if (prow->length > max_length) { + max_length = prow->length; + large_row = prow; + } + } + return large_row; +} + + +sm_col * +sm_longest_col(A) +sm_matrix *A; +{ + register sm_col *large_col, *pcol; + register int max_length; + + max_length = 0; + large_col = NIL(sm_col); + for(pcol = A->first_col; pcol != 0; pcol = pcol->next_col) { + if (pcol->length > max_length) { + max_length = pcol->length; + large_col = pcol; + } + } + return large_col; +} + +int +sm_num_elements(A) +sm_matrix *A; +{ + register sm_row *prow; + register int num; + + num = 0; + sm_foreach_row(A, prow) { + num += prow->length; + } + return num; +} + +int +sm_read(fp, A) +FILE *fp; +sm_matrix **A; +{ + int i, j, err; + + *A = sm_alloc(); + while (! feof(fp)) { + err = fscanf(fp, "%d %d", &i, &j); + if (err == EOF) { + return 1; + } else if (err != 2) { + return 0; + } + (void) sm_insert(*A, i, j); + } + return 1; +} + + +int +sm_read_compressed(fp, A) +FILE *fp; +sm_matrix **A; +{ + int i, j, k, nrows, ncols; + unsigned long x; + + *A = sm_alloc(); + if (fscanf(fp, "%d %d", &nrows, &ncols) != 2) { + return 0; + } + sm_resize(*A, nrows, ncols); + + for(i = 0; i < nrows; i++) { + if (fscanf(fp, "%lx", &x) != 1) { + return 0; + } + for(j = 0; j < ncols; j += 32) { + if (fscanf(fp, "%lx", &x) != 1) { + return 0; + } + for(k = j; x != 0; x >>= 1, k++) { + if (x & 1) { + (void) sm_insert(*A, i, k); + } + } + } + } + return 1; +} + + +void +sm_write(fp, A) +FILE *fp; +sm_matrix *A; +{ + register sm_row *prow; + register sm_element *p; + + for(prow = A->first_row; prow != 0; prow = prow->next_row) { + for(p = prow->first_col; p != 0; p = p->next_col) { + (void) fprintf(fp, "%d %d\n", p->row_num, p->col_num); + } + } +} + +void +sm_print(fp, A) +FILE *fp; +sm_matrix *A; +{ + register sm_row *prow; + register sm_col *pcol; + int c; + + if (A->last_col->col_num >= 100) { + (void) fprintf(fp, " "); + for(pcol = A->first_col; pcol != 0; pcol = pcol->next_col) { + (void) fprintf(fp, "%d", (pcol->col_num / 100)%10); + } + putc('\n', fp); + } + + if (A->last_col->col_num >= 10) { + (void) fprintf(fp, " "); + for(pcol = A->first_col; pcol != 0; pcol = pcol->next_col) { + (void) fprintf(fp, "%d", (pcol->col_num / 10)%10); + } + putc('\n', fp); + } + + (void) fprintf(fp, " "); + for(pcol = A->first_col; pcol != 0; pcol = pcol->next_col) { + (void) fprintf(fp, "%d", pcol->col_num % 10); + } + putc('\n', fp); + + (void) fprintf(fp, " "); + for(pcol = A->first_col; pcol != 0; pcol = pcol->next_col) { + (void) fprintf(fp, "-"); + } + putc('\n', fp); + + for(prow = A->first_row; prow != 0; prow = prow->next_row) { + (void) fprintf(fp, "%3d:", prow->row_num); + + for(pcol = A->first_col; pcol != 0; pcol = pcol->next_col) { + c = sm_row_find(prow, pcol->col_num) ? '1' : '.'; + putc(c, fp); + } + putc('\n', fp); + } +} + + +void +sm_dump(A, s, max) +sm_matrix *A; +char *s; +int max; +{ + FILE *fp = stdout; + + (void) fprintf(fp, "%s %d rows by %d cols\n", s, A->nrows, A->ncols); + if (A->nrows < max) { + sm_print(fp, A); + } +} + +void +sm_cleanup() +{ +#ifdef FAST_AND_LOOSE + register sm_element *p, *pnext; + register sm_row *prow, *pnextrow; + register sm_col *pcol, *pnextcol; + + for(p = sm_element_freelist; p != 0; p = pnext) { + pnext = p->next_col; + FREE(p); + } + sm_element_freelist = 0; + + for(prow = sm_row_freelist; prow != 0; prow = pnextrow) { + pnextrow = prow->next_row; + FREE(prow); + } + sm_row_freelist = 0; + + for(pcol = sm_col_freelist; pcol != 0; pcol = pnextcol) { + pnextcol = pcol->next_col; + FREE(pcol); + } + sm_col_freelist = 0; +#endif +} diff --git a/abc_with_bb_support/src/misc/espresso/mincov.c b/abc_with_bb_support/src/misc/espresso/mincov.c new file mode 100644 index 000000000..6786f3aa2 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/mincov.c @@ -0,0 +1,378 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +#include "mincov_int.h" + +/* + * mincov.c + */ + +#define USE_GIMPEL +#define USE_INDEP_SET + +static int select_column(); +static void select_essential(); +static int verify_cover(); + +#define fail(why) {\ + (void) fprintf(stderr, "Fatal error: file %s, line %d\n%s\n",\ + __FILE__, __LINE__, why);\ + (void) fflush(stdout);\ + abort();\ +} + +sm_row * +sm_minimum_cover(A, weight, heuristic, debug_level) +sm_matrix *A; +int *weight; +int heuristic; /* set to 1 for a heuristic covering */ +int debug_level; /* how deep in the recursion to provide info */ +{ + stats_t stats; + solution_t *best, *select; + sm_row *prow, *sol; + sm_col *pcol; + sm_matrix *dup_A; + int nelem, bound; + double sparsity; + + /* Avoid sillyness */ + if (A->nrows <= 0) { + return sm_row_alloc(); /* easy to cover */ + } + + /* Initialize debugging structure */ + stats.start_time = util_cpu_time(); + stats.debug = debug_level > 0; + stats.max_print_depth = debug_level; + stats.max_depth = -1; + stats.nodes = 0; + stats.component = stats.comp_count = 0; + stats.gimpel = stats.gimpel_count = 0; + stats.no_branching = heuristic != 0; + stats.lower_bound = -1; + + /* Check the matrix sparsity */ + nelem = 0; + sm_foreach_row(A, prow) { + nelem += prow->length; + } + sparsity = (double) nelem / (double) (A->nrows * A->ncols); + + /* Determine an upper bound on the solution */ + bound = 1; + sm_foreach_col(A, pcol) { + bound += WEIGHT(weight, pcol->col_num); + } + + /* Perform the covering */ + select = solution_alloc(); + dup_A = sm_dup(A); + best = sm_mincov(dup_A, select, weight, 0, bound, 0, &stats); + sm_free(dup_A); + solution_free(select); + + if (stats.debug) { + if (stats.no_branching) { + (void) printf("**** heuristic covering ...\n"); + (void) printf("lower bound = %d\n", stats.lower_bound); + } + (void) printf("matrix = %d by %d with %d elements (%4.3f%%)\n", + A->nrows, A->ncols, nelem, sparsity * 100.0); + (void) printf("cover size = %d elements\n", best->row->length); + (void) printf("cover cost = %d\n", best->cost); + (void) printf("time = %s\n", + util_print_time(util_cpu_time() - stats.start_time)); + (void) printf("components = %d\n", stats.comp_count); + (void) printf("gimpel = %d\n", stats.gimpel_count); + (void) printf("nodes = %d\n", stats.nodes); + (void) printf("max_depth = %d\n", stats.max_depth); + } + + sol = sm_row_dup(best->row); + if (! verify_cover(A, sol)) { + fail("mincov: internal error -- cover verification failed\n"); + } + solution_free(best); + return sol; +} + +/* + * Find the best cover for 'A' (given that 'select' already selected); + * + * - abort search if a solution cannot be found which beats 'bound' + * + * - if any solution meets 'lower_bound', then it is the optimum solution + * and can be returned without further work. + */ + +solution_t * +sm_mincov(A, select, weight, lb, bound, depth, stats) +sm_matrix *A; +solution_t *select; +int *weight; +int lb; +int bound; +int depth; +stats_t *stats; +{ + sm_matrix *A1, *A2, *L, *R; + sm_element *p; + solution_t *select1, *select2, *best, *best1, *best2, *indep; + int pick, lb_new, debug; + + /* Start out with some debugging information */ + stats->nodes++; + if (depth > stats->max_depth) stats->max_depth = depth; + debug = stats->debug && (depth <= stats->max_print_depth); + + /* Apply row dominance, column dominance, and select essentials */ + select_essential(A, select, weight, bound); + if (select->cost >= bound) { + return NIL(solution_t); + } + + /* See if gimpel's reduction technique applies ... */ +#ifdef USE_GIMPEL + if ( weight == NIL(int)) { /* hack until we fix it */ + if (gimpel_reduce(A, select, weight, lb, bound, depth, stats, &best)) { + return best; + } + } +#endif + +#ifdef USE_INDEP_SET + /* Determine bound from here to final solution using independent-set */ + indep = sm_maximal_independent_set(A, weight); + + /* make sure the lower bound is monotonically increasing */ + lb_new = MAX(select->cost + indep->cost, lb); + pick = select_column(A, weight, indep); + solution_free(indep); +#else + lb_new = select->cost + (A->nrows > 0); + pick = select_column(A, weight, NIL(solution_t)); +#endif + + if (depth == 0) { + stats->lower_bound = lb_new + stats->gimpel; + } + + if (debug) { + (void) printf("ABSMIN[%2d]%s", depth, stats->component ? "*" : " "); + (void) printf(" %3dx%3d sel=%3d bnd=%3d lb=%3d %12s ", + A->nrows, A->ncols, select->cost + stats->gimpel, + bound + stats->gimpel, lb_new + stats->gimpel, + util_print_time(util_cpu_time()-stats->start_time)); + } + + /* Check for bounding based on no better solution possible */ + if (lb_new >= bound) { + if (debug) (void) printf("bounded\n"); + best = NIL(solution_t); + + + /* Check for new best solution */ + } else if (A->nrows == 0) { + best = solution_dup(select); + if (debug) (void) printf("BEST\n"); + if (stats->debug && stats->component == 0) { + (void) printf("new 'best' solution %d at level %d (time is %s)\n", + best->cost + stats->gimpel, depth, + util_print_time(util_cpu_time() - stats->start_time)); + } + + + /* Check for a partition of the problem */ + } else if (sm_block_partition(A, &L, &R)) { + /* Make L the smaller problem */ + if (L->ncols > R->ncols) { + A1 = L; + L = R; + R = A1; + } + if (debug) (void) printf("comp %d %d\n", L->nrows, R->nrows); + stats->comp_count++; + + /* Solve problem for L */ + select1 = solution_alloc(); + stats->component++; + best1 = sm_mincov(L, select1, weight, 0, + bound-select->cost, depth+1, stats); + stats->component--; + solution_free(select1); + sm_free(L); + + /* Add best solution to the selected set */ + if (best1 == NIL(solution_t)) { + best = NIL(solution_t); + } else { + for(p = best1->row->first_col; p != 0; p = p->next_col) { + solution_add(select, weight, p->col_num); + } + solution_free(best1); + + /* recur for the remaining block */ + best = sm_mincov(R, select, weight, lb_new, bound, depth+1, stats); + } + sm_free(R); + + /* We've tried as hard as possible, but now we must split and recur */ + } else { + if (debug) (void) printf("pick=%d\n", pick); + + /* Assume we choose this column to be in the covering set */ + A1 = sm_dup(A); + select1 = solution_dup(select); + solution_accept(select1, A1, weight, pick); + best1 = sm_mincov(A1, select1, weight, lb_new, bound, depth+1, stats); + solution_free(select1); + sm_free(A1); + + /* Update the upper bound if we found a better solution */ + if (best1 != NIL(solution_t) && bound > best1->cost) { + bound = best1->cost; + } + + /* See if this is a heuristic covering (no branching) */ + if (stats->no_branching) { + return best1; + } + + /* Check for reaching lower bound -- if so, don't actually branch */ + if (best1 != NIL(solution_t) && best1->cost == lb_new) { + return best1; + } + + /* Now assume we cannot have that column */ + A2 = sm_dup(A); + select2 = solution_dup(select); + solution_reject(select2, A2, weight, pick); + best2 = sm_mincov(A2, select2, weight, lb_new, bound, depth+1, stats); + solution_free(select2); + sm_free(A2); + + best = solution_choose_best(best1, best2); + } + + return best; +} + +static int +select_column(A, weight, indep) +sm_matrix *A; +int *weight; +solution_t *indep; +{ + register sm_col *pcol; + register sm_row *prow, *indep_cols; + register sm_element *p, *p1; + double w, best; + int best_col; + + indep_cols = sm_row_alloc(); + if (indep != NIL(solution_t)) { + /* Find which columns are in the independent sets */ + for(p = indep->row->first_col; p != 0; p = p->next_col) { + prow = sm_get_row(A, p->col_num); + for(p1 = prow->first_col; p1 != 0; p1 = p1->next_col) { + (void) sm_row_insert(indep_cols, p1->col_num); + } + } + } else { + /* select out of all columns */ + sm_foreach_col(A, pcol) { + (void) sm_row_insert(indep_cols, pcol->col_num); + } + } + + /* Find the best column */ + best_col = -1; + best = -1; + + /* Consider only columns which are in some independent row */ + sm_foreach_row_element(indep_cols, p1) { + pcol = sm_get_col(A, p1->col_num); + + /* Compute the total 'value' of all things covered by the column */ + w = 0.0; + for(p = pcol->first_row; p != 0; p = p->next_row) { + prow = sm_get_row(A, p->row_num); + w += 1.0 / ((double) prow->length - 1.0); + } + + /* divide this by the relative cost of choosing this column */ + w = w / (double) WEIGHT(weight, pcol->col_num); + + /* maximize this ratio */ + if (w > best) { + best_col = pcol->col_num; + best = w; + } + } + + sm_row_free(indep_cols); + return best_col; +} + +static void +select_essential(A, select, weight, bound) +sm_matrix *A; +solution_t *select; +int *weight; +int bound; /* must beat this solution */ +{ + register sm_element *p; + register sm_row *prow, *essen; + int delcols, delrows, essen_count; + + do { + /* Check for dominated columns */ + delcols = sm_col_dominance(A, weight); + + /* Find the rows with only 1 element (the essentials) */ + essen = sm_row_alloc(); + sm_foreach_row(A, prow) { + if (prow->length == 1) { + (void) sm_row_insert(essen, prow->first_col->col_num); + } + } + + /* Select all of the elements */ + sm_foreach_row_element(essen, p) { + solution_accept(select, A, weight, p->col_num); + /* Make sure solution still looks good */ + if (select->cost >= bound) { + sm_row_free(essen); + return; + } + } + essen_count = essen->length; + sm_row_free(essen); + + /* Check for dominated rows */ + delrows = sm_row_dominance(A); + + } while (delcols > 0 || delrows > 0 || essen_count > 0); +} + +static int +verify_cover(A, cover) +sm_matrix *A; +sm_row *cover; +{ + sm_row *prow; + + sm_foreach_row(A, prow) { + if (! sm_row_intersects(prow, cover)) { + return 0; + } + } + return 1; +} diff --git a/abc_with_bb_support/src/misc/espresso/mincov.h b/abc_with_bb_support/src/misc/espresso/mincov.h new file mode 100644 index 000000000..873a353de --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/mincov.h @@ -0,0 +1,11 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +/* exported */ +extern sm_row *sm_minimum_cover(); diff --git a/abc_with_bb_support/src/misc/espresso/mincov_int.h b/abc_with_bb_support/src/misc/espresso/mincov_int.h new file mode 100644 index 000000000..d69e5fd8b --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/mincov_int.h @@ -0,0 +1,55 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +//#include "port.h" +//#include "utility.h" +#include "sparse.h" +#include "mincov.h" + +#include "util_hack.h" // added + + +typedef struct stats_struct stats_t; +struct stats_struct { + int debug; /* 1 if debugging is enabled */ + int max_print_depth; /* dump stats for levels up to this level */ + int max_depth; /* deepest the recursion has gone */ + int nodes; /* total nodes visited */ + int component; /* currently solving a component */ + int comp_count; /* number of components detected */ + int gimpel_count; /* number of times Gimpel reduction applied */ + int gimpel; /* currently inside Gimpel reduction */ + long start_time; /* cpu time when the covering started */ + int no_branching; + int lower_bound; +}; + + + +typedef struct solution_struct solution_t; +struct solution_struct { + sm_row *row; + int cost; +}; + + +extern solution_t *solution_alloc(); +extern void solution_free(); +extern solution_t *solution_dup(); +extern void solution_accept(); +extern void solution_reject(); +extern void solution_add(); +extern solution_t *solution_choose_best(); + +extern solution_t *sm_maximal_independent_set(); +extern solution_t *sm_mincov(); +extern int gimpel_reduce(); + + +#define WEIGHT(weight, col) (weight == NIL(int) ? 1 : weight[col]) diff --git a/abc_with_bb_support/src/misc/espresso/module.make b/abc_with_bb_support/src/misc/espresso/module.make new file mode 100644 index 000000000..02067519b --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/module.make @@ -0,0 +1,39 @@ +SRC += src/misc/espresso/cofactor.c \ + src/misc/espresso/cols.c \ + src/misc/espresso/compl.c \ + src/misc/espresso/contain.c \ + src/misc/espresso/cubehack.c \ + src/misc/espresso/cubestr.c \ + src/misc/espresso/cvrin.c \ + src/misc/espresso/cvrm.c \ + src/misc/espresso/cvrmisc.c \ + src/misc/espresso/cvrout.c \ + src/misc/espresso/dominate.c \ + src/misc/espresso/equiv.c \ + src/misc/espresso/espresso.c \ + src/misc/espresso/essen.c \ + src/misc/espresso/exact.c \ + src/misc/espresso/expand.c \ + src/misc/espresso/gasp.c \ + src/misc/espresso/gimpel.c \ + src/misc/espresso/globals.c \ + src/misc/espresso/hack.c \ + src/misc/espresso/indep.c \ + src/misc/espresso/irred.c \ + src/misc/espresso/map.c \ + src/misc/espresso/matrix.c \ + src/misc/espresso/mincov.c \ + src/misc/espresso/opo.c \ + src/misc/espresso/pair.c \ + src/misc/espresso/part.c \ + src/misc/espresso/primes.c \ + src/misc/espresso/reduce.c \ + src/misc/espresso/rows.c \ + src/misc/espresso/set.c \ + src/misc/espresso/setc.c \ + src/misc/espresso/sharp.c \ + src/misc/espresso/sminterf.c \ + src/misc/espresso/solution.c \ + src/misc/espresso/sparse.c \ + src/misc/espresso/unate.c \ + src/misc/espresso/verify.c diff --git a/abc_with_bb_support/src/misc/espresso/opo.c b/abc_with_bb_support/src/misc/espresso/opo.c new file mode 100644 index 000000000..316218468 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/opo.c @@ -0,0 +1,624 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +#include "espresso.h" + +/* + * Phase assignment technique (T. Sasao): + * + * 1. create a function with 2*m outputs which implements the + * original function and its complement for each output + * + * 2. minimize this function + * + * 3. choose the minimum number of prime implicants from the + * result of step 2 which are needed to realize either a function + * or its complement for each output + * + * Step 3 is performed in a rather crude way -- by simply multiplying + * out a large expression of the form: + * + * I = (ab + cdef)(acd + bgh) ... + * + * which is a product of m expressions where each expression has two + * product terms -- one representing which primes are needed for the + * function, and one representing which primes are needed for the + * complement. The largest product term resulting shows which primes + * to keep to implement one function or the other for each output. + * For problems with many outputs, this may grind to a + * halt. + * + * Untried: form complement of I and use unate_complement ... + * + * I have unsuccessfully tried several modifications to the basic + * algorithm. The first is quite simple: use Sasao's technique, but + * only commit to a single output at a time (rather than all + * outputs). The goal would be that the later minimizations can "take + * into account" the partial assignment at each step. This is + * expensive (m+1 minimizations rather than 2), and the results are + * discouraging. + * + * The second modification is rather complicated. The result from the + * minimization in step 2 is guaranteed to be minimal. Hence, for + * each output, the set of primes with a 1 in that output are both + * necessary and sufficient to implement the function. Espresso + * achieves the minimality using the routine MAKE_SPARSE. The + * modification is to prevent MAKE_SPARSE from running. Hence, there + * are potentially many subsets of the set of primes with a 1 in a + * column which can be used to implement that output. We use + * IRREDUNDANT to enumerate all possible subsets and then proceed as + * before. + */ + +static int opo_no_make_sparse; +static int opo_repeated; +static int opo_exact; +static void minimize(); + +void phase_assignment(PLA, opo_strategy) +pPLA PLA; +int opo_strategy; +{ + opo_no_make_sparse = opo_strategy % 2; + skip_make_sparse = opo_no_make_sparse; + opo_repeated = (opo_strategy / 2) % 2; + opo_exact = (opo_strategy / 4) % 2; + + /* Determine a phase assignment */ + if (PLA->phase != NULL) { + FREE(PLA->phase); + } + + if (opo_repeated) { + PLA->phase = set_save(cube.fullset); + repeated_phase_assignment(PLA); + } else { + PLA->phase = find_phase(PLA, 0, (pcube) NULL); + } + + /* Now minimize with this assignment */ + skip_make_sparse = FALSE; + (void) set_phase(PLA); + minimize(PLA); +} + +/* + * repeated_phase_assignment -- an alternate strategy which commits + * to a single phase assignment a step at a time. Performs m + 1 + * minimizations ! + */ +void repeated_phase_assignment(PLA) +pPLA PLA; +{ + int i; + pcube phase; + + for(i = 0; i < cube.part_size[cube.output]; i++) { + + /* Find best assignment for all undecided outputs */ + phase = find_phase(PLA, i, PLA->phase); + + /* Commit for only a single output ... */ + if (! is_in_set(phase, cube.first_part[cube.output] + i)) { + set_remove(PLA->phase, cube.first_part[cube.output] + i); + } + + if (trace || summary) { + printf("\nOPO loop for output #%d\n", i); + printf("PLA->phase is %s\n", pc1(PLA->phase)); + printf("phase is %s\n", pc1(phase)); + } + set_free(phase); + } +} + + +/* + * find_phase -- find a phase assignment for the PLA for all outputs starting + * with output number first_output. + */ +pcube find_phase(PLA, first_output, phase1) +pPLA PLA; +int first_output; +pcube phase1; +{ + pcube phase; + pPLA PLA1; + + phase = set_save(cube.fullset); + + /* setup the double-phase characteristic function, resize the cube */ + PLA1 = new_PLA(); + PLA1->F = sf_save(PLA->F); + PLA1->R = sf_save(PLA->R); + PLA1->D = sf_save(PLA->D); + if (phase1 != NULL) { + PLA1->phase = set_save(phase1); + (void) set_phase(PLA1); + } + EXEC_S(output_phase_setup(PLA1, first_output), "OPO-SETUP ", PLA1->F); + + /* minimize the double-phase function */ + minimize(PLA1); + + /* set the proper phases according to what gives a minimum solution */ + EXEC_S(PLA1->F = opo(phase, PLA1->F, PLA1->D, PLA1->R, first_output), + "OPO ", PLA1->F); + free_PLA(PLA1); + + /* set the cube structure to reflect the old size */ + setdown_cube(); + cube.part_size[cube.output] -= + (cube.part_size[cube.output] - first_output) / 2; + cube_setup(); + + return phase; +} + +/* + * opo -- multiply the expression out to determine a minimum subset of + * primes. + */ + +/*ARGSUSED*/ +pcover opo(phase, T, D, R, first_output) +pcube phase; +pcover T, D, R; +int first_output; +{ + int offset, output, i, last_output, ind; + pset pdest, select, p, p1, last, last1, not_covered, tmp; + pset_family temp, T1, T2; + + /* must select all primes for outputs [0 .. first_output-1] */ + select = set_full(T->count); + for(output = 0; output < first_output; output++) { + ind = cube.first_part[cube.output] + output; + foreachi_set(T, i, p) { + if (is_in_set(p, ind)) { + set_remove(select, i); + } + } + } + + /* Recursively perform the intersections */ + offset = (cube.part_size[cube.output] - first_output) / 2; + last_output = first_output + offset - 1; + temp = opo_recur(T, D, select, offset, first_output, last_output); + + /* largest set is on top -- select primes which are inferred from it */ + pdest = temp->data; + T1 = new_cover(T->count); + foreachi_set(T, i, p) { + if (! is_in_set(pdest, i)) { + T1 = sf_addset(T1, p); + } + } + + set_free(select); + sf_free(temp); + + /* finding phases is difficult -- see which functions are not covered */ + T2 = complement(cube1list(T1)); + not_covered = new_cube(); + tmp = new_cube(); + foreach_set(T, last, p) { + foreach_set(T2, last1, p1) { + if (cdist0(p, p1)) { + (void) set_or(not_covered, not_covered, set_and(tmp, p, p1)); + } + } + } + free_cover(T); + free_cover(T2); + set_free(tmp); + + /* Now reflect the phase choice in a single cube */ + for(output = first_output; output <= last_output; output++) { + ind = cube.first_part[cube.output] + output; + if (is_in_set(not_covered, ind)) { + if (is_in_set(not_covered, ind + offset)) { + fatal("error in output phase assignment"); + } else { + set_remove(phase, ind); + } + } + } + set_free(not_covered); + return T1; +} + +pset_family opo_recur(T, D, select, offset, first, last) +pcover T, D; +pcube select; +int offset, first, last; +{ + static int level = 0; + int middle; + pset_family sl, sr, temp; + + level++; + if (first == last) { +#if 0 + if (opo_no_make_sparse) { + temp = form_cover_table(T, D, select, first, first + offset); + } else { + temp = opo_leaf(T, select, first, first + offset); + } +#else + temp = opo_leaf(T, select, first, first + offset); +#endif + } else { + middle = (first + last) / 2; + sl = opo_recur(T, D, select, offset, first, middle); + sr = opo_recur(T, D, select, offset, middle+1, last); + temp = unate_intersect(sl, sr, level == 1); + if (trace) { + printf("# OPO[%d]: %4d = %4d x %4d, time = %s\n", level - 1, + temp->count, sl->count, sr->count, print_time(ptime())); + (void) fflush(stdout); + } + free_cover(sl); + free_cover(sr); + } + level--; + return temp; +} + + +pset_family opo_leaf(T, select, out1, out2) +register pcover T; +pset select; +int out1, out2; +{ + register pset_family temp; + register pset p, pdest; + register int i; + + out1 += cube.first_part[cube.output]; + out2 += cube.first_part[cube.output]; + + /* Allocate space for the result */ + temp = sf_new(2, T->count); + + /* Find which primes are needed for the ON-set of this fct */ + pdest = GETSET(temp, temp->count++); + (void) set_copy(pdest, select); + foreachi_set(T, i, p) { + if (is_in_set(p, out1)) { + set_remove(pdest, i); + } + } + + /* Find which primes are needed for the OFF-set of this fct */ + pdest = GETSET(temp, temp->count++); + (void) set_copy(pdest, select); + foreachi_set(T, i, p) { + if (is_in_set(p, out2)) { + set_remove(pdest, i); + } + } + + return temp; +} + +#if 0 +pset_family form_cover_table(F, D, select, f, fbar) +pcover F, D; +pset select; +int f, fbar; /* indices of f and fbar in the output part */ +{ + register int i; + register pcube p; + pset_family f_table, fbar_table; + + /* setup required for fcube_is_covered */ + Rp_size = F->count; + Rp_start = set_new(Rp_size); + foreachi_set(F, i, p) { + PUTSIZE(p, i); + } + foreachi_set(D, i, p) { + RESET(p, REDUND); + } + + f_table = find_covers(F, D, select, f); + fbar_table = find_covers(F, D, select, fbar); + f_table = sf_append(f_table, fbar_table); + + set_free(Rp_start); + return f_table; +} + + +pset_family find_covers(F, D, select, n) +pcover F, D; +register pset select; +int n; +{ + register pset p, last, new; + pcover F1; + pcube *Flist; + pset_family f_table, table; + int i; + + n += cube.first_part[cube.output]; + + /* save cubes in this output, and remove the output variable */ + F1 = new_cover(F->count); + foreach_set(F, last, p) + if (is_in_set(p, n)) { + new = GETSET(F1, F1->count++); + set_or(new, p, cube.var_mask[cube.output]); + PUTSIZE(new, SIZE(p)); + SET(new, REDUND); + } + + /* Find ways (sop form) to fail to cover output indexed by n */ + Flist = cube2list(F1, D); + table = sf_new(10, Rp_size); + foreach_set(F1, last, p) { + set_fill(Rp_start, Rp_size); + set_remove(Rp_start, SIZE(p)); + table = sf_append(table, fcube_is_covered(Flist, p)); + RESET(p, REDUND); + } + set_fill(Rp_start, Rp_size); + foreach_set(table, last, p) { + set_diff(p, Rp_start, p); + } + + /* complement this to get possible ways to cover the function */ + for(i = 0; i < Rp_size; i++) { + if (! is_in_set(select, i)) { + p = set_new(Rp_size); + set_insert(p, i); + table = sf_addset(table, p); + set_free(p); + } + } + f_table = unate_compl(table); + + /* what a pain, but we need bitwise complement of this */ + set_fill(Rp_start, Rp_size); + foreach_set(f_table, last, p) { + set_diff(p, Rp_start, p); + } + + free_cubelist(Flist); + sf_free(F1); + return f_table; +} +#endif + +/* + * Take a PLA (ON-set, OFF-set and DC-set) and create the + * "double-phase characteristic function" which is merely a new + * function which has twice as many outputs and realizes both the + * function and the complement. + * + * The cube structure is assumed to represent the PLA upon entering. + * It will be modified to represent the double-phase function upon + * exit. + * + * Only the outputs numbered starting with "first_output" are + * duplicated in the output part + */ + +output_phase_setup(PLA, first_output) +INOUT pPLA PLA; +int first_output; +{ + pcover F, R, D; + pcube mask, mask1, last; + int first_part, offset; + bool save; + register pcube p, pr, pf; + register int i, last_part; + + if (cube.output == -1) + fatal("output_phase_setup: must have an output"); + + F = PLA->F; + D = PLA->D; + R = PLA->R; + first_part = cube.first_part[cube.output] + first_output; + last_part = cube.last_part[cube.output]; + offset = cube.part_size[cube.output] - first_output; + + /* Change the output size, setup the cube structure */ + setdown_cube(); + cube.part_size[cube.output] += offset; + cube_setup(); + + /* Create a mask to select that part of the cube which isn't changing */ + mask = set_save(cube.fullset); + for(i = first_part; i < cube.size; i++) + set_remove(mask, i); + mask1 = set_save(mask); + for(i = cube.first_part[cube.output]; i < first_part; i++) { + set_remove(mask1, i); + } + + PLA->F = new_cover(F->count + R->count); + PLA->R = new_cover(F->count + R->count); + PLA->D = new_cover(D->count); + + foreach_set(F, last, p) { + pf = GETSET(PLA->F, (PLA->F)->count++); + pr = GETSET(PLA->R, (PLA->R)->count++); + INLINEset_and(pf, mask, p); + INLINEset_and(pr, mask1, p); + for(i = first_part; i <= last_part; i++) + if (is_in_set(p, i)) + set_insert(pf, i); + save = FALSE; + for(i = first_part; i <= last_part; i++) + if (is_in_set(p, i)) + save = TRUE, set_insert(pr, i+offset); + if (! save) PLA->R->count--; + } + + foreach_set(R, last, p) { + pf = GETSET(PLA->F, (PLA->F)->count++); + pr = GETSET(PLA->R, (PLA->R)->count++); + INLINEset_and(pf, mask1, p); + INLINEset_and(pr, mask, p); + save = FALSE; + for(i = first_part; i <= last_part; i++) + if (is_in_set(p, i)) + save = TRUE, set_insert(pf, i+offset); + if (! save) PLA->F->count--; + for(i = first_part; i <= last_part; i++) + if (is_in_set(p, i)) + set_insert(pr, i); + } + + foreach_set(D, last, p) { + pf = GETSET(PLA->D, (PLA->D)->count++); + INLINEset_and(pf, mask, p); + for(i = first_part; i <= last_part; i++) + if (is_in_set(p, i)) { + set_insert(pf, i); + set_insert(pf, i+offset); + } + } + + free_cover(F); + free_cover(D); + free_cover(R); + set_free(mask); + set_free(mask1); +} + +/* + * set_phase -- given a "cube" which describes which phases of the output + * are to be implemented, compute the appropriate on-set and off-set + */ +pPLA set_phase(PLA) +INOUT pPLA PLA; +{ + pcover F1, R1; + register pcube last, p, outmask; + register pcube temp=cube.temp[0], phase=PLA->phase, phase1=cube.temp[1]; + + outmask = cube.var_mask[cube.num_vars - 1]; + set_diff(phase1, outmask, phase); + set_or(phase1, set_diff(temp, cube.fullset, outmask), phase1); + F1 = new_cover((PLA->F)->count + (PLA->R)->count); + R1 = new_cover((PLA->F)->count + (PLA->R)->count); + + foreach_set(PLA->F, last, p) { + if (! setp_disjoint(set_and(temp, p, phase), outmask)) + set_copy(GETSET(F1, F1->count++), temp); + if (! setp_disjoint(set_and(temp, p, phase1), outmask)) + set_copy(GETSET(R1, R1->count++), temp); + } + foreach_set(PLA->R, last, p) { + if (! setp_disjoint(set_and(temp, p, phase), outmask)) + set_copy(GETSET(R1, R1->count++), temp); + if (! setp_disjoint(set_and(temp, p, phase1), outmask)) + set_copy(GETSET(F1, F1->count++), temp); + } + free_cover(PLA->F); + free_cover(PLA->R); + PLA->F = F1; + PLA->R = R1; + return PLA; +} + +#define POW2(x) (1 << (x)) + +void opoall(PLA, first_output, last_output, opo_strategy) +pPLA PLA; +int first_output, last_output; +int opo_strategy; +{ + pcover F, D, R, best_F, best_D, best_R; + int i, j, ind, num; + pcube bestphase; + + opo_exact = opo_strategy; + + if (PLA->phase != NULL) { + set_free(PLA->phase); + } + + bestphase = set_save(cube.fullset); + best_F = sf_save(PLA->F); + best_D = sf_save(PLA->D); + best_R = sf_save(PLA->R); + + for(i = 0; i < POW2(last_output - first_output + 1); i++) { + + /* save the initial PLA covers */ + F = sf_save(PLA->F); + D = sf_save(PLA->D); + R = sf_save(PLA->R); + + /* compute the phase cube for this iteration */ + PLA->phase = set_save(cube.fullset); + num = i; + for(j = last_output; j >= first_output; j--) { + if (num % 2 == 0) { + ind = cube.first_part[cube.output] + j; + set_remove(PLA->phase, ind); + } + num /= 2; + } + + /* set the phase and minimize */ + (void) set_phase(PLA); + printf("# phase is %s\n", pc1(PLA->phase)); + summary = TRUE; + minimize(PLA); + + /* see if this is the best so far */ + if (PLA->F->count < best_F->count) { + /* save new best solution */ + set_copy(bestphase, PLA->phase); + sf_free(best_F); + sf_free(best_D); + sf_free(best_R); + best_F = PLA->F; + best_D = PLA->D; + best_R = PLA->R; + } else { + /* throw away the solution */ + free_cover(PLA->F); + free_cover(PLA->D); + free_cover(PLA->R); + } + set_free(PLA->phase); + + /* restore the initial PLA covers */ + PLA->F = F; + PLA->D = D; + PLA->R = R; + } + + /* one more minimization to restore the best answer */ + PLA->phase = bestphase; + sf_free(PLA->F); + sf_free(PLA->D); + sf_free(PLA->R); + PLA->F = best_F; + PLA->D = best_D; + PLA->R = best_R; +} + +static void minimize(PLA) +pPLA PLA; +{ + if (opo_exact) { + EXEC_S(PLA->F = minimize_exact(PLA->F,PLA->D,PLA->R,1), "EXACT", PLA->F); + } else { + EXEC_S(PLA->F = espresso(PLA->F, PLA->D, PLA->R), "ESPRESSO ",PLA->F); + } +} diff --git a/abc_with_bb_support/src/misc/espresso/pair.c b/abc_with_bb_support/src/misc/espresso/pair.c new file mode 100644 index 000000000..8195ccf0b --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/pair.c @@ -0,0 +1,675 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +#include "espresso.h" + +void set_pair(PLA) +pPLA PLA; +{ + set_pair1(PLA, TRUE); +} + +void set_pair1(PLA, adjust_labels) +pPLA PLA; +bool adjust_labels; +{ + int i, var, *paired, newvar; + int old_num_vars, old_num_binary_vars, old_size, old_mv_start; + int *new_part_size, new_num_vars, new_num_binary_vars, new_mv_start; + ppair pair = PLA->pair; + char scratch[1000], **oldlabel, *var1, *var1bar, *var2, *var2bar; + + if (adjust_labels) + makeup_labels(PLA); + + /* Check the pair structure for valid entries and see which binary + variables are left unpaired + */ + paired = ALLOC(bool, cube.num_binary_vars); + for(var = 0; var < cube.num_binary_vars; var++) + paired[var] = FALSE; + for(i = 0; i < pair->cnt; i++) + if ((pair->var1[i] > 0 && pair->var1[i] <= cube.num_binary_vars) && + (pair->var2[i] > 0 && pair->var2[i] <= cube.num_binary_vars)) { + paired[pair->var1[i]-1] = TRUE; + paired[pair->var2[i]-1] = TRUE; + } else + fatal("can only pair binary-valued variables"); + + PLA->F = delvar(pairvar(PLA->F, pair), paired); + PLA->R = delvar(pairvar(PLA->R, pair), paired); + PLA->D = delvar(pairvar(PLA->D, pair), paired); + + /* Now painfully adjust the cube size */ + old_size = cube.size; + old_num_vars = cube.num_vars; + old_num_binary_vars = cube.num_binary_vars; + old_mv_start = cube.first_part[cube.num_binary_vars]; + /* Create the new cube.part_size vector and setup the cube structure */ + new_num_binary_vars = 0; + for(var = 0; var < old_num_binary_vars; var++) + new_num_binary_vars += (paired[var] == FALSE); + new_num_vars = new_num_binary_vars + pair->cnt; + new_num_vars += old_num_vars - old_num_binary_vars; + new_part_size = ALLOC(int, new_num_vars); + for(var = 0; var < pair->cnt; var++) + new_part_size[new_num_binary_vars + var] = 4; + for(var = 0; var < old_num_vars - old_num_binary_vars; var++) + new_part_size[new_num_binary_vars + pair->cnt + var] = + cube.part_size[old_num_binary_vars + var]; + setdown_cube(); + FREE(cube.part_size); + cube.num_vars = new_num_vars; + cube.num_binary_vars = new_num_binary_vars; + cube.part_size = new_part_size; + cube_setup(); + + /* hack with the labels to get them correct */ + if (adjust_labels) { + oldlabel = PLA->label; + PLA->label = ALLOC(char *, cube.size); + for(var = 0; var < pair->cnt; var++) { + newvar = cube.num_binary_vars*2 + var*4; + var1 = oldlabel[ (pair->var1[var]-1) * 2 + 1]; + var2 = oldlabel[ (pair->var2[var]-1) * 2 + 1]; + var1bar = oldlabel[ (pair->var1[var]-1) * 2]; + var2bar = oldlabel[ (pair->var2[var]-1) * 2]; + (void) sprintf(scratch, "%s+%s", var1bar, var2bar); + PLA->label[newvar] = util_strsav(scratch); + (void) sprintf(scratch, "%s+%s", var1bar, var2); + PLA->label[newvar+1] = util_strsav(scratch); + (void) sprintf(scratch, "%s+%s", var1, var2bar); + PLA->label[newvar+2] = util_strsav(scratch); + (void) sprintf(scratch, "%s+%s", var1, var2); + PLA->label[newvar+3] = util_strsav(scratch); + } + /* Copy the old labels for the unpaired binary vars */ + i = 0; + for(var = 0; var < old_num_binary_vars; var++) { + if (paired[var] == FALSE) { + PLA->label[2*i] = oldlabel[2*var]; + PLA->label[2*i+1] = oldlabel[2*var+1]; + oldlabel[2*var] = oldlabel[2*var+1] = (char *) NULL; + i++; + } + } + /* Copy the old labels for the remaining unpaired vars */ + new_mv_start = cube.num_binary_vars*2 + pair->cnt*4; + for(i = old_mv_start; i < old_size; i++) { + PLA->label[new_mv_start + i - old_mv_start] = oldlabel[i]; + oldlabel[i] = (char *) NULL; + } + /* free remaining entries in oldlabel */ + for(i = 0; i < old_size; i++) + if (oldlabel[i] != (char *) NULL) + FREE(oldlabel[i]); + FREE(oldlabel); + } + + /* the paired variables should not be sparse (cf. mv_reduce/raise_in)*/ + for(var = 0; var < pair->cnt; var++) + cube.sparse[cube.num_binary_vars + var] = 0; + FREE(paired); +} + +pcover pairvar(A, pair) +pcover A; +ppair pair; +{ + register pcube last, p; + register int val, p1, p2, b1, b0; + int insert_col, pairnum; + + insert_col = cube.first_part[cube.num_vars - 1]; + + /* stretch the cover matrix to make room for the paired variables */ + A = sf_delcol(A, insert_col, -4*pair->cnt); + + /* compute the paired values */ + foreach_set(A, last, p) { + for(pairnum = 0; pairnum < pair->cnt; pairnum++) { + p1 = cube.first_part[pair->var1[pairnum] - 1]; + p2 = cube.first_part[pair->var2[pairnum] - 1]; + b1 = is_in_set(p, p2+1); + b0 = is_in_set(p, p2); + val = insert_col + pairnum * 4; + if (/* a0 */ is_in_set(p, p1)) { + if (b0) + set_insert(p, val + 3); + if (b1) + set_insert(p, val + 2); + } + if (/* a1 */ is_in_set(p, p1+1)) { + if (b0) + set_insert(p, val + 1); + if (b1) + set_insert(p, val); + } + } + } + return A; +} + + +/* delvar -- delete variables from A, minimize the number of column shifts */ +pcover delvar(A, paired) +pcover A; +bool paired[]; +{ + bool run; + int first_run, run_length, var, offset = 0; + + run = FALSE; run_length = 0; + for(var = 0; var < cube.num_binary_vars; var++) + if (paired[var]) + if (run) + run_length += cube.part_size[var]; + else { + run = TRUE; + first_run = cube.first_part[var]; + run_length = cube.part_size[var]; + } + else + if (run) { + A = sf_delcol(A, first_run-offset, run_length); + run = FALSE; + offset += run_length; + } + if (run) + A = sf_delcol(A, first_run-offset, run_length); + return A; +} + +/* + find_optimal_pairing -- find which binary variables should be paired + to maximally reduce the number of terms + + This is essentially the technique outlined by T. Sasao in the + Trans. on Comp., Oct 1984. We estimate the cost of pairing each + pair individually using 1 of 4 strategies: (1) algebraic division + of F by the pair (exactly T. Sasao technique); (2) strong division + of F by the paired variables (using REDUCE/EXPAND/ IRREDUNDANT from + espresso); (3) full minimization using espresso; (4) exact + minimization. These are in order of both increasing accuracy and + increasing difficulty (!) + + Once the n squared pairs have been evaluated, T. Sasao proposes a + graph covering of nodes by disjoint edges. For now, I solve this + problem exhaustively (complexity = (n-1)*(n-3)*...*3*1 for n + variables when n is even). Note that solving this problem exactly + is the same as evaluating the cost function for all possible + pairings. + + n pairs + + 1, 2 1 + 3, 4 3 + 5, 6 15 + 7, 8 105 + 9,10 945 + 11,12 10,395 + 13,14 135,135 + 15,16 2,027,025 + 17,18 34,459,425 + 19,20 654,729,075 +*/ +void find_optimal_pairing(PLA, strategy) +pPLA PLA; +int strategy; +{ + int i, j, **cost_array; + + cost_array = find_pairing_cost(PLA, strategy); + + if (summary) { + printf(" "); + for(i = 0; i < cube.num_binary_vars; i++) + printf("%3d ", i+1); + printf("\n"); + for(i = 0; i < cube.num_binary_vars; i++) { + printf("%3d ", i+1); + for(j = 0; j < cube.num_binary_vars; j++) + printf("%3d ", cost_array[i][j]); + printf("\n"); + } + } + + if (cube.num_binary_vars <= 14) { + PLA->pair = pair_best_cost(cost_array); + } else { + (void) greedy_best_cost(cost_array, &(PLA->pair)); + } + printf("# "); + print_pair(PLA->pair); + + for(i = 0; i < cube.num_binary_vars; i++) + FREE(cost_array[i]); + FREE(cost_array); + + set_pair(PLA); + EXEC_S(PLA->F=espresso(PLA->F,PLA->D,PLA->R),"ESPRESSO ",PLA->F); +} + +int **find_pairing_cost(PLA, strategy) +pPLA PLA; +int strategy; +{ + int var1, var2, **cost_array; + int i, j, xnum_binary_vars, xnum_vars, *xpart_size, cost; + pcover T, Fsave, Dsave, Rsave; + pset mask; +/* char *s;*/ + + /* data is returned in the cost array */ + cost_array = ALLOC(int *, cube.num_binary_vars); + for(i = 0; i < cube.num_binary_vars; i++) + cost_array[i] = ALLOC(int, cube.num_binary_vars); + for(i = 0; i < cube.num_binary_vars; i++) + for(j = 0; j < cube.num_binary_vars; j++) + cost_array[i][j] = 0; + + /* Setup the pair structure for pairing variables together */ + PLA->pair = pair_new(1); + PLA->pair->cnt = 1; + + for(var1 = 0; var1 < cube.num_binary_vars-1; var1++) { + for(var2 = var1+1; var2 < cube.num_binary_vars; var2++) { + /* if anything but simple strategy, perform setup */ + if (strategy > 0) { + /* save the original covers */ + Fsave = sf_save(PLA->F); + Dsave = sf_save(PLA->D); + Rsave = sf_save(PLA->R); + + /* save the original cube structure */ + xnum_binary_vars = cube.num_binary_vars; + xnum_vars = cube.num_vars; + xpart_size = ALLOC(int, cube.num_vars); + for(i = 0; i < cube.num_vars; i++) + xpart_size[i] = cube.part_size[i]; + + /* pair two variables together */ + PLA->pair->var1[0] = var1 + 1; + PLA->pair->var2[0] = var2 + 1; + set_pair1(PLA, /* adjust_labels */ FALSE); + } + + + /* decide how to best estimate worth of this pairing */ + switch(strategy) { + case 3: + /*s = "exact minimization";*/ + PLA->F = minimize_exact(PLA->F, PLA->D, PLA->R, 1); + cost = Fsave->count - PLA->F->count; + break; + case 2: + /*s = "full minimization";*/ + PLA->F = espresso(PLA->F, PLA->D, PLA->R); + cost = Fsave->count - PLA->F->count; + break; + case 1: + /*s = "strong division";*/ + PLA->F = reduce(PLA->F, PLA->D); + PLA->F = expand(PLA->F, PLA->R, FALSE); + PLA->F = irredundant(PLA->F, PLA->D); + cost = Fsave->count - PLA->F->count; + break; + case 0: + /*s = "weak division";*/ + mask = new_cube(); + set_or(mask, cube.var_mask[var1], cube.var_mask[var2]); + T = dist_merge(sf_save(PLA->F), mask); + cost = PLA->F->count - T->count; + sf_free(T); + set_free(mask); + } + + cost_array[var1][var2] = cost; + + if (strategy > 0) { + /* restore the original cube structure -- free the new ones */ + setdown_cube(); + FREE(cube.part_size); + cube.num_binary_vars = xnum_binary_vars; + cube.num_vars = xnum_vars; + cube.part_size = xpart_size; + cube_setup(); + + /* restore the original cover(s) -- free the new ones */ + sf_free(PLA->F); + sf_free(PLA->D); + sf_free(PLA->R); + PLA->F = Fsave; + PLA->D = Dsave; + PLA->R = Rsave; + } + } + } + + pair_free(PLA->pair); + PLA->pair = NULL; + return cost_array; +} + +static int best_cost; +static int **cost_array; +static ppair best_pair; +static pset best_phase; +static pPLA global_PLA; +static pcover best_F, best_D, best_R; +static int pair_minim_strategy; + + +print_pair(pair) +ppair pair; +{ + int i; + + printf("pair is"); + for(i = 0; i < pair->cnt; i++) + printf (" (%d %d)", pair->var1[i], pair->var2[i]); + printf("\n"); +} + + +int greedy_best_cost(cost_array_local, pair_p) +int **cost_array_local; +ppair *pair_p; +{ + int i, j, besti, bestj, maxcost, total_cost; + pset cand; + ppair pair; + + pair = pair_new(cube.num_binary_vars); + cand = set_full(cube.num_binary_vars); + total_cost = 0; + + while (set_ord(cand) >= 2) { + maxcost = -1; + for(i = 0; i < cube.num_binary_vars; i++) { + if (is_in_set(cand, i)) { + for(j = i+1; j < cube.num_binary_vars; j++) { + if (is_in_set(cand, j)) { + if (cost_array_local[i][j] > maxcost) { + maxcost = cost_array_local[i][j]; + besti = i; + bestj = j; + } + } + } + } + } + pair->var1[pair->cnt] = besti+1; + pair->var2[pair->cnt] = bestj+1; + pair->cnt++; + set_remove(cand, besti); + set_remove(cand, bestj); + total_cost += maxcost; + } + set_free(cand); + *pair_p = pair; + return total_cost; +} + + +ppair pair_best_cost(cost_array_local) +int **cost_array_local; +{ + ppair pair; + pset candidate; + + best_cost = -1; + best_pair = NULL; + cost_array = cost_array_local; + + pair = pair_new(cube.num_binary_vars); + candidate = set_full(cube.num_binary_vars); + generate_all_pairs(pair, cube.num_binary_vars, candidate, find_best_cost); + pair_free(pair); + set_free(candidate); + return best_pair; +} + + +int find_best_cost(pair) +register ppair pair; +{ + register int i, cost; + + cost = 0; + for(i = 0; i < pair->cnt; i++) + cost += cost_array[pair->var1[i]-1][pair->var2[i]-1]; + if (cost > best_cost) { + best_cost = cost; + best_pair = pair_save(pair, pair->cnt); + } + if ((debug & MINCOV) && trace) { + printf("cost is %d ", cost); + print_pair(pair); + } +} + +/* + pair_all: brute-force approach to try all possible pairings + + pair_strategy is: + 2) for espresso + 3) for minimize_exact + 4) for phase assignment +*/ + +pair_all(PLA, pair_strategy) +pPLA PLA; +int pair_strategy; +{ + ppair pair; + pset candidate; + + global_PLA = PLA; + pair_minim_strategy = pair_strategy; + best_cost = PLA->F->count + 1; + best_pair = NULL; + best_phase = NULL; + best_F = best_D = best_R = NULL; + pair = pair_new(cube.num_binary_vars); + candidate = set_fill(set_new(cube.num_binary_vars), cube.num_binary_vars); + + generate_all_pairs(pair, cube.num_binary_vars, candidate, minimize_pair); + + pair_free(pair); + set_free(candidate); + + PLA->pair = best_pair; + PLA->phase = best_phase; +/* not really necessary + if (phase != NULL) + (void) set_phase(PLA->phase); +*/ + set_pair(PLA); + printf("# "); + print_pair(PLA->pair); + + sf_free(PLA->F); + sf_free(PLA->D); + sf_free(PLA->R); + PLA->F = best_F; + PLA->D = best_D; + PLA->R = best_R; +} + + +/* + * minimize_pair -- called as each pair is generated + */ +int minimize_pair(pair) +ppair pair; +{ + pcover Fsave, Dsave, Rsave; + int i, xnum_binary_vars, xnum_vars, *xpart_size; + + /* save the original covers */ + Fsave = sf_save(global_PLA->F); + Dsave = sf_save(global_PLA->D); + Rsave = sf_save(global_PLA->R); + + /* save the original cube structure */ + xnum_binary_vars = cube.num_binary_vars; + xnum_vars = cube.num_vars; + xpart_size = ALLOC(int, cube.num_vars); + for(i = 0; i < cube.num_vars; i++) + xpart_size[i] = cube.part_size[i]; + + /* setup the paired variables */ + global_PLA->pair = pair; + set_pair1(global_PLA, /* adjust_labels */ FALSE); + + /* call the minimizer */ + if (summary) + print_pair(pair); + switch(pair_minim_strategy) { + case 2: + EXEC_S(phase_assignment(global_PLA,0), "OPO ", global_PLA->F); + if (summary) + printf("# phase is %s\n", pc1(global_PLA->phase)); + break; + case 1: + EXEC_S(global_PLA->F = minimize_exact(global_PLA->F, global_PLA->D, + global_PLA->R, 1), "EXACT ", global_PLA->F); + break; + case 0: + EXEC_S(global_PLA->F = espresso(global_PLA->F, global_PLA->D, + global_PLA->R), "ESPRESSO ", global_PLA->F); + break; + default: + break; + } + + /* see if we have a new best solution */ + if (global_PLA->F->count < best_cost) { + best_cost = global_PLA->F->count; + best_pair = pair_save(pair, pair->cnt); + best_phase = global_PLA->phase!=NULL?set_save(global_PLA->phase):NULL; + if (best_F != NULL) sf_free(best_F); + if (best_D != NULL) sf_free(best_D); + if (best_R != NULL) sf_free(best_R); + best_F = sf_save(global_PLA->F); + best_D = sf_save(global_PLA->D); + best_R = sf_save(global_PLA->R); + } + + /* restore the original cube structure -- free the new ones */ + setdown_cube(); + FREE(cube.part_size); + cube.num_binary_vars = xnum_binary_vars; + cube.num_vars = xnum_vars; + cube.part_size = xpart_size; + cube_setup(); + + /* restore the original cover(s) -- free the new ones */ + sf_free(global_PLA->F); + sf_free(global_PLA->D); + sf_free(global_PLA->R); + global_PLA->F = Fsave; + global_PLA->D = Dsave; + global_PLA->R = Rsave; + global_PLA->pair = NULL; + global_PLA->phase = NULL; +} + +generate_all_pairs(pair, n, candidate, action) +ppair pair; +int n; +pset candidate; +int (*action)(); +{ + int i, j; + pset recur_candidate; + ppair recur_pair; + + if (set_ord(candidate) < 2) { + (*action)(pair); + return; + } + + recur_pair = pair_save(pair, n); + recur_candidate = set_save(candidate); + + /* Find first variable still in the candidate set */ + for(i = 0; i < n; i++) + if (is_in_set(candidate, i)) + break; + + /* Try all pairs of i with other variables */ + for(j = i+1; j < n; j++) + if (is_in_set(candidate, j)) { + /* pair (i j) -- remove from candidate set for future pairings */ + set_remove(recur_candidate, i); + set_remove(recur_candidate, j); + + /* add to the pair array */ + recur_pair->var1[recur_pair->cnt] = i+1; + recur_pair->var2[recur_pair->cnt] = j+1; + recur_pair->cnt++; + + /* recur looking for the end ... */ + generate_all_pairs(recur_pair, n, recur_candidate, action); + + /* now break this pair, and restore candidate set */ + recur_pair->cnt--; + set_insert(recur_candidate, i); + set_insert(recur_candidate, j); + } + + /* if odd, generate all pairs which do NOT include i */ + if ((set_ord(candidate) % 2) == 1) { + set_remove(recur_candidate, i); + generate_all_pairs(recur_pair, n, recur_candidate, action); + } + + pair_free(recur_pair); + set_free(recur_candidate); +} + +ppair pair_new(n) +register int n; +{ + register ppair pair1; + + pair1 = ALLOC(pair_t, 1); + pair1->cnt = 0; + pair1->var1 = ALLOC(int, n); + pair1->var2 = ALLOC(int, n); + return pair1; +} + + +ppair pair_save(pair, n) +register ppair pair; +register int n; +{ + register int k; + register ppair pair1; + + pair1 = pair_new(n); + pair1->cnt = pair->cnt; + for(k = 0; k < pair->cnt; k++) { + pair1->var1[k] = pair->var1[k]; + pair1->var2[k] = pair->var2[k]; + } + return pair1; +} + + +int pair_free(pair) +register ppair pair; +{ + FREE(pair->var1); + FREE(pair->var2); + FREE(pair); +} diff --git a/abc_with_bb_support/src/misc/espresso/part.c b/abc_with_bb_support/src/misc/espresso/part.c new file mode 100644 index 000000000..03fedf7d9 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/part.c @@ -0,0 +1,122 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +#include "mincov_int.h" + +static int visit_col(); + +static void +copy_row(A, prow) +register sm_matrix *A; +register sm_row *prow; +{ + register sm_element *p; + + for(p = prow->first_col; p != 0; p = p->next_col) { + (void) sm_insert(A, p->row_num, p->col_num); + } +} + + +static int +visit_row(A, prow, rows_visited, cols_visited) +sm_matrix *A; +sm_row *prow; +int *rows_visited; +int *cols_visited; +{ + sm_element *p; + sm_col *pcol; + + if (! prow->flag) { + prow->flag = 1; + (*rows_visited)++; + if (*rows_visited == A->nrows) { + return 1; + } + for(p = prow->first_col; p != 0; p = p->next_col) { + pcol = sm_get_col(A, p->col_num); + if (! pcol->flag) { + if (visit_col(A, pcol, rows_visited, cols_visited)) { + return 1; + } + } + } + } + return 0; +} + + +static int +visit_col(A, pcol, rows_visited, cols_visited) +sm_matrix *A; +sm_col *pcol; +int *rows_visited; +int *cols_visited; +{ + sm_element *p; + sm_row *prow; + + if (! pcol->flag) { + pcol->flag = 1; + (*cols_visited)++; + if (*cols_visited == A->ncols) { + return 1; + } + for(p = pcol->first_row; p != 0; p = p->next_row) { + prow = sm_get_row(A, p->row_num); + if (! prow->flag) { + if (visit_row(A, prow, rows_visited, cols_visited)) { + return 1; + } + } + } + } + return 0; +} + +int +sm_block_partition(A, L, R) +sm_matrix *A; +sm_matrix **L, **R; +{ + int cols_visited, rows_visited; + register sm_row *prow; + register sm_col *pcol; + + /* Avoid the trivial case */ + if (A->nrows == 0) { + return 0; + } + + /* Reset the visited flags for each row and column */ + for(prow = A->first_row; prow != 0; prow = prow->next_row) { + prow->flag = 0; + } + for(pcol = A->first_col; pcol != 0; pcol = pcol->next_col) { + pcol->flag = 0; + } + + cols_visited = rows_visited = 0; + if (visit_row(A, A->first_row, &rows_visited, &cols_visited)) { + /* we found all of the rows */ + return 0; + } else { + *L = sm_alloc(); + *R = sm_alloc(); + for(prow = A->first_row; prow != 0; prow = prow->next_row) { + if (prow->flag) { + copy_row(*L, prow); + } else { + copy_row(*R, prow); + } + } + return 1; + } +} diff --git a/abc_with_bb_support/src/misc/espresso/primes.c b/abc_with_bb_support/src/misc/espresso/primes.c new file mode 100644 index 000000000..4698844f3 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/primes.c @@ -0,0 +1,170 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +#include "espresso.h" + +static bool primes_consensus_special_cases(); +static pcover primes_consensus_merge(); +static pcover and_with_cofactor(); + + +/* primes_consensus -- generate primes using consensus */ +pcover primes_consensus(T) +pcube *T; /* T will be disposed of */ +{ + register pcube cl, cr; + register int best; + pcover Tnew, Tl, Tr; + + if (primes_consensus_special_cases(T, &Tnew) == MAYBE) { + cl = new_cube(); + cr = new_cube(); + best = binate_split_select(T, cl, cr, COMPL); + + Tl = primes_consensus(scofactor(T, cl, best)); + Tr = primes_consensus(scofactor(T, cr, best)); + Tnew = primes_consensus_merge(Tl, Tr, cl, cr); + + free_cube(cl); + free_cube(cr); + free_cubelist(T); + } + + return Tnew; +} + +static bool +primes_consensus_special_cases(T, Tnew) +pcube *T; /* will be disposed if answer is determined */ +pcover *Tnew; /* returned only if answer determined */ +{ + register pcube *T1, p, ceil, cof=T[0]; + pcube last; + pcover A; + + /* Check for no cubes in the cover */ + if (T[2] == NULL) { + *Tnew = new_cover(0); + free_cubelist(T); + return TRUE; + } + + /* Check for only a single cube in the cover */ + if (T[3] == NULL) { + *Tnew = sf_addset(new_cover(1), set_or(cof, cof, T[2])); + free_cubelist(T); + return TRUE; + } + + /* Check for a row of all 1's (implies function is a tautology) */ + for(T1 = T+2; (p = *T1++) != NULL; ) { + if (full_row(p, cof)) { + *Tnew = sf_addset(new_cover(1), cube.fullset); + free_cubelist(T); + return TRUE; + } + } + + /* Check for a column of all 0's which can be factored out */ + ceil = set_save(cof); + for(T1 = T+2; (p = *T1++) != NULL; ) { + INLINEset_or(ceil, ceil, p); + } + if (! setp_equal(ceil, cube.fullset)) { + p = new_cube(); + (void) set_diff(p, cube.fullset, ceil); + (void) set_or(cof, cof, p); + free_cube(p); + + A = primes_consensus(T); + foreach_set(A, last, p) { + INLINEset_and(p, p, ceil); + } + *Tnew = A; + set_free(ceil); + return TRUE; + } + set_free(ceil); + + /* Collect column counts, determine unate variables, etc. */ + massive_count(T); + + /* If single active variable not factored out above, then tautology ! */ + if (cdata.vars_active == 1) { + *Tnew = sf_addset(new_cover(1), cube.fullset); + free_cubelist(T); + return TRUE; + + /* Check for unate cover */ + } else if (cdata.vars_unate == cdata.vars_active) { + A = cubeunlist(T); + *Tnew = sf_contain(A); + free_cubelist(T); + return TRUE; + + /* Not much we can do about it */ + } else { + return MAYBE; + } +} + +static pcover +primes_consensus_merge(Tl, Tr, cl, cr) +pcover Tl, Tr; +pcube cl, cr; +{ + register pcube pl, pr, lastl, lastr, pt; + pcover T, Tsave; + + Tl = and_with_cofactor(Tl, cl); + Tr = and_with_cofactor(Tr, cr); + + T = sf_new(500, Tl->sf_size); + pt = T->data; + Tsave = sf_contain(sf_join(Tl, Tr)); + + foreach_set(Tl, lastl, pl) { + foreach_set(Tr, lastr, pr) { + if (cdist01(pl, pr) == 1) { + consensus(pt, pl, pr); + if (++T->count >= T->capacity) { + Tsave = sf_union(Tsave, sf_contain(T)); + T = sf_new(500, Tl->sf_size); + pt = T->data; + } else { + pt += T->wsize; + } + } + } + } + free_cover(Tl); + free_cover(Tr); + + Tsave = sf_union(Tsave, sf_contain(T)); + return Tsave; +} + + +static pcover +and_with_cofactor(A, cof) +pset_family A; +register pset cof; +{ + register pset last, p; + + foreach_set(A, last, p) { + INLINEset_and(p, p, cof); + if (cdist(p, cube.fullset) > 0) { + RESET(p, ACTIVE); + } else { + SET(p, ACTIVE); + } + } + return sf_inactive(A); +} diff --git a/abc_with_bb_support/src/misc/espresso/reduce.c b/abc_with_bb_support/src/misc/espresso/reduce.c new file mode 100644 index 000000000..4501efde5 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/reduce.c @@ -0,0 +1,258 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +/* + module: reduce.c + purpose: Perform the Espresso-II reduction step + + Reduction is a technique used to explore larger regions of the + optimization space. We replace each cube of F with a smaller + cube while still maintaining a cover of the same logic function. +*/ + +#include "espresso.h" + +static bool toggle = TRUE; + + +/* + reduce -- replace each cube in F with its reduction + + The reduction of a cube is the smallest cube contained in the cube + which can replace the cube in the original cover without changing + the cover. This is equivalent to the super cube of all of the + essential points in the cube. This can be computed directly. + + The problem is that the order in which the cubes are reduced can + greatly affect the final result. We alternate between two ordering + strategies: + + (1) Order the cubes in ascending order of distance from the + largest cube breaking ties by ordering cubes of equal distance + in descending order of size (sort_reduce) + + (2) Order the cubes in descending order of the inner-product of + the cube and the column sums (mini_sort) + + The real workhorse of this section is the routine SCCC which is + used to find the Smallest Cube Containing the Complement of a cover. + Reduction as proposed by Espresso-II takes a cube and computes its + maximal reduction as the intersection between the cube and the + smallest cube containing the complement of (F u D - {c}) cofactored + against c. + + As usual, the unate-recursive paradigm is used to compute SCCC. + The SCCC of a unate cover is trivial to compute, and thus we perform + Shannon Cofactor expansion attempting to drive the cover to be unate + as fast as possible. +*/ + +pcover reduce(F, D) +INOUT pcover F; +IN pcover D; +{ + register pcube last, p, cunder, *FD; + + /* Order the cubes */ + if (use_random_order) + F = random_order(F); + else { + F = toggle ? sort_reduce(F) : mini_sort(F, descend); + toggle = ! toggle; + } + + /* Try to reduce each cube */ + FD = cube2list(F, D); + foreach_set(F, last, p) { + cunder = reduce_cube(FD, p); /* reduce the cube */ + if (setp_equal(cunder, p)) { /* see if it actually did */ + SET(p, ACTIVE); /* cube remains active */ + SET(p, PRIME); /* cube remains prime ? */ + } else { + if (debug & REDUCE) { + printf("REDUCE: %s to %s %s\n", + pc1(p), pc2(cunder), print_time(ptime())); + } + set_copy(p, cunder); /* save reduced version */ + RESET(p, PRIME); /* cube is no longer prime */ + if (setp_empty(cunder)) + RESET(p, ACTIVE); /* if null, kill the cube */ + else + SET(p, ACTIVE); /* cube is active */ + } + free_cube(cunder); + } + free_cubelist(FD); + + /* Delete any cubes of F which reduced to the empty cube */ + return sf_inactive(F); +} + +/* reduce_cube -- find the maximal reduction of a cube */ +pcube reduce_cube(FD, p) +IN pcube *FD, p; +{ + pcube cunder; + + cunder = sccc(cofactor(FD, p)); + return set_and(cunder, cunder, p); +} + + +/* sccc -- find Smallest Cube Containing the Complement of a cover */ +pcube sccc(T) +INOUT pcube *T; /* T will be disposed of */ +{ + pcube r; + register pcube cl, cr; + register int best; + static int sccc_level = 0; + + if (debug & REDUCE1) { + debug_print(T, "SCCC", sccc_level++); + } + + if (sccc_special_cases(T, &r) == MAYBE) { + cl = new_cube(); + cr = new_cube(); + best = binate_split_select(T, cl, cr, REDUCE1); + r = sccc_merge(sccc(scofactor(T, cl, best)), + sccc(scofactor(T, cr, best)), cl, cr); + free_cubelist(T); + } + + if (debug & REDUCE1) + printf("SCCC[%d]: result is %s\n", --sccc_level, pc1(r)); + return r; +} + + +pcube sccc_merge(left, right, cl, cr) +INOUT register pcube left, right; /* will be disposed of ... */ +INOUT register pcube cl, cr; /* will be disposed of ... */ +{ + INLINEset_and(left, left, cl); + INLINEset_and(right, right, cr); + INLINEset_or(left, left, right); + free_cube(right); + free_cube(cl); + free_cube(cr); + return left; +} + + +/* + sccc_cube -- find the smallest cube containing the complement of a cube + + By DeMorgan's law and the fact that the smallest cube containing a + cover is the "or" of the positional cubes, it is simple to see that + the SCCC is the universe if the cube has more than two active + variables. If there is only a single active variable, then the + SCCC is merely the bitwise complement of the cube in that + variable. A last special case is no active variables, in which + case the SCCC is empty. + + This is "anded" with the incoming cube result. +*/ +pcube sccc_cube(result, p) +register pcube result, p; +{ + register pcube temp=cube.temp[0], mask; + int var; + + if ((var = cactive(p)) >= 0) { + mask = cube.var_mask[var]; + INLINEset_xor(temp, p, mask); + INLINEset_and(result, result, temp); + } + return result; +} + +/* + * sccc_special_cases -- check the special cases for sccc + */ + +bool sccc_special_cases(T, result) +INOUT pcube *T; /* will be disposed if answer is determined */ +OUT pcube *result; /* returned only if answer determined */ +{ + register pcube *T1, p, temp = cube.temp[1], ceil, cof = T[0]; + pcube *A, *B; + + /* empty cover => complement is universe => SCCC is universe */ + if (T[2] == NULL) { + *result = set_save(cube.fullset); + free_cubelist(T); + return TRUE; + } + + /* row of 1's => complement is empty => SCCC is empty */ + for(T1 = T+2; (p = *T1++) != NULL; ) { + if (full_row(p, cof)) { + *result = new_cube(); + free_cubelist(T); + return TRUE; + } + } + + /* Collect column counts, determine unate variables, etc. */ + massive_count(T); + + /* If cover is unate (or single cube), apply simple rules to find SCCCU */ + if (cdata.vars_unate == cdata.vars_active || T[3] == NULL) { + *result = set_save(cube.fullset); + for(T1 = T+2; (p = *T1++) != NULL; ) { + (void) sccc_cube(*result, set_or(temp, p, cof)); + } + free_cubelist(T); + return TRUE; + } + + /* Check for column of 0's (which can be easily factored( */ + ceil = set_save(cof); + for(T1 = T+2; (p = *T1++) != NULL; ) { + INLINEset_or(ceil, ceil, p); + } + if (! setp_equal(ceil, cube.fullset)) { + *result = sccc_cube(set_save(cube.fullset), ceil); + if (setp_equal(*result, cube.fullset)) { + free_cube(ceil); + } else { + *result = sccc_merge(sccc(cofactor(T,ceil)), + set_save(cube.fullset), ceil, *result); + } + free_cubelist(T); + return TRUE; + } + free_cube(ceil); + + /* Single active column at this point => tautology => SCCC is empty */ + if (cdata.vars_active == 1) { + *result = new_cube(); + free_cubelist(T); + return TRUE; + } + + /* Check for components */ + if (cdata.var_zeros[cdata.best] < CUBELISTSIZE(T)/2) { + if (cubelist_partition(T, &A, &B, debug & REDUCE1) == 0) { + return MAYBE; + } else { + free_cubelist(T); + *result = sccc(A); + ceil = sccc(B); + (void) set_and(*result, *result, ceil); + set_free(ceil); + return TRUE; + } + } + + /* Not much we can do about it */ + return MAYBE; +} diff --git a/abc_with_bb_support/src/misc/espresso/rows.c b/abc_with_bb_support/src/misc/espresso/rows.c new file mode 100644 index 000000000..5fa0026e3 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/rows.c @@ -0,0 +1,314 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +//#include "port.h" +#include "sparse_int.h" + + +/* + * allocate a new row vector + */ +sm_row * +sm_row_alloc() +{ + register sm_row *prow; + +#ifdef FAST_AND_LOOSE + if (sm_row_freelist == NIL(sm_row)) { + prow = ALLOC(sm_row, 1); + } else { + prow = sm_row_freelist; + sm_row_freelist = prow->next_row; + } +#else + prow = ALLOC(sm_row, 1); +#endif + + prow->row_num = 0; + prow->length = 0; + prow->first_col = prow->last_col = NIL(sm_element); + prow->next_row = prow->prev_row = NIL(sm_row); + prow->flag = 0; + prow->user_word = NIL(char); /* for our user ... */ + return prow; +} + + +/* + * free a row vector -- for FAST_AND_LOOSE, this is real cheap for rows; + * however, freeing a column must still walk down the column discarding + * the elements one-by-one; that is the only use for the extra '-DCOLS' + * compile flag ... + */ +void +sm_row_free(prow) +register sm_row *prow; +{ +#if defined(FAST_AND_LOOSE) && ! defined(COLS) + if (prow->first_col != NIL(sm_element)) { + /* Add the linked list of row items to the free list */ + prow->last_col->next_col = sm_element_freelist; + sm_element_freelist = prow->first_col; + } + + /* Add the row to the free list of rows */ + prow->next_row = sm_row_freelist; + sm_row_freelist = prow; +#else + register sm_element *p, *pnext; + + for(p = prow->first_col; p != 0; p = pnext) { + pnext = p->next_col; + sm_element_free(p); + } + FREE(prow); +#endif +} + + +/* + * duplicate an existing row + */ +sm_row * +sm_row_dup(prow) +register sm_row *prow; +{ + register sm_row *pnew; + register sm_element *p; + + pnew = sm_row_alloc(); + for(p = prow->first_col; p != 0; p = p->next_col) { + (void) sm_row_insert(pnew, p->col_num); + } + return pnew; +} + + +/* + * insert an element into a row vector + */ +sm_element * +sm_row_insert(prow, col) +register sm_row *prow; +register int col; +{ + register sm_element *test, *element; + + /* get a new item, save its address */ + sm_element_alloc(element); + test = element; + sorted_insert(sm_element, prow->first_col, prow->last_col, prow->length, + next_col, prev_col, col_num, col, test); + + /* if item was not used, free it */ + if (element != test) { + sm_element_free(element); + } + + /* either way, return the current new value */ + return test; +} + + +/* + * remove an element from a row vector + */ +void +sm_row_remove(prow, col) +register sm_row *prow; +register int col; +{ + register sm_element *p; + + for(p = prow->first_col; p != 0 && p->col_num < col; p = p->next_col) + ; + if (p != 0 && p->col_num == col) { + dll_unlink(p, prow->first_col, prow->last_col, + next_col, prev_col, prow->length); + sm_element_free(p); + } +} + + +/* + * find an element (if it is in the row vector) + */ +sm_element * +sm_row_find(prow, col) +sm_row *prow; +int col; +{ + register sm_element *p; + + for(p = prow->first_col; p != 0 && p->col_num < col; p = p->next_col) + ; + if (p != 0 && p->col_num == col) { + return p; + } else { + return NIL(sm_element); + } +} + +/* + * return 1 if row p2 contains row p1; 0 otherwise + */ +int +sm_row_contains(p1, p2) +sm_row *p1, *p2; +{ + register sm_element *q1, *q2; + + q1 = p1->first_col; + q2 = p2->first_col; + while (q1 != 0) { + if (q2 == 0 || q1->col_num < q2->col_num) { + return 0; + } else if (q1->col_num == q2->col_num) { + q1 = q1->next_col; + q2 = q2->next_col; + } else { + q2 = q2->next_col; + } + } + return 1; +} + + +/* + * return 1 if row p1 and row p2 share an element in common + */ +int +sm_row_intersects(p1, p2) +sm_row *p1, *p2; +{ + register sm_element *q1, *q2; + + q1 = p1->first_col; + q2 = p2->first_col; + if (q1 == 0 || q2 == 0) return 0; + for(;;) { + if (q1->col_num < q2->col_num) { + if ((q1 = q1->next_col) == 0) { + return 0; + } + } else if (q1->col_num > q2->col_num) { + if ((q2 = q2->next_col) == 0) { + return 0; + } + } else { + return 1; + } + } +} + + +/* + * compare two rows, lexical ordering + */ +int +sm_row_compare(p1, p2) +sm_row *p1, *p2; +{ + register sm_element *q1, *q2; + + q1 = p1->first_col; + q2 = p2->first_col; + while(q1 != 0 && q2 != 0) { + if (q1->col_num != q2->col_num) { + return q1->col_num - q2->col_num; + } + q1 = q1->next_col; + q2 = q2->next_col; + } + + if (q1 != 0) { + return 1; + } else if (q2 != 0) { + return -1; + } else { + return 0; + } +} + + +/* + * return the intersection + */ +sm_row * +sm_row_and(p1, p2) +sm_row *p1, *p2; +{ + register sm_element *q1, *q2; + register sm_row *result; + + result = sm_row_alloc(); + q1 = p1->first_col; + q2 = p2->first_col; + if (q1 == 0 || q2 == 0) return result; + for(;;) { + if (q1->col_num < q2->col_num) { + if ((q1 = q1->next_col) == 0) { + return result; + } + } else if (q1->col_num > q2->col_num) { + if ((q2 = q2->next_col) == 0) { + return result; + } + } else { + (void) sm_row_insert(result, q1->col_num); + if ((q1 = q1->next_col) == 0) { + return result; + } + if ((q2 = q2->next_col) == 0) { + return result; + } + } + } +} + +int +sm_row_hash(prow, modulus) +sm_row *prow; +int modulus; +{ + register int sum; + register sm_element *p; + + sum = 0; + for(p = prow->first_col; p != 0; p = p->next_col) { + sum = (sum*17 + p->col_num) % modulus; + } + return sum; +} + +/* + * remove an element from a row vector (given a pointer to the element) + */ +void +sm_row_remove_element(prow, p) +register sm_row *prow; +register sm_element *p; +{ + dll_unlink(p, prow->first_col, prow->last_col, + next_col, prev_col, prow->length); + sm_element_free(p); +} + + +void +sm_row_print(fp, prow) +FILE *fp; +sm_row *prow; +{ + sm_element *p; + + for(p = prow->first_col; p != 0; p = p->next_col) { + (void) fprintf(fp, " %d", p->col_num); + } +} diff --git a/abc_with_bb_support/src/misc/espresso/set.c b/abc_with_bb_support/src/misc/espresso/set.c new file mode 100644 index 000000000..619ee7497 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/set.c @@ -0,0 +1,820 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +/* + * set.c -- routines for maniuplating sets and set families + */ + +/* LINTLIBRARY */ + +#include "espresso.h" +static pset_family set_family_garbage = NULL; + +static int intcpy(d, s, n) +register unsigned int *d, *s; +register long n; +{ + register int i; + for(i = 0; i < n; i++) { + *d++ = *s++; + } +} + + +/* bit_index -- find first bit (from LSB) in a word (MSB=bit n, LSB=bit 0) */ +int bit_index(a) +register unsigned int a; +{ + register int i; + if (a == 0) + return -1; + for(i = 0; (a & 1) == 0; a >>= 1, i++) + ; + return i; +} + + +/* set_ord -- count number of elements in a set */ +int set_ord(a) +register pset a; +{ + register int i, sum = 0; + register unsigned int val; + for(i = LOOP(a); i > 0; i--) + if ((val = a[i]) != 0) + sum += count_ones(val); + return sum; +} + +/* set_dist -- distance between two sets (# elements in common) */ +int set_dist(a, b) +register pset a, b; +{ + register int i, sum = 0; + register unsigned int val; + for(i = LOOP(a); i > 0; i--) + if ((val = a[i] & b[i]) != 0) + sum += count_ones(val); + return sum; +} + +/* set_clear -- make "r" the empty set of "size" elements */ +pset set_clear(r, size) +register pset r; +int size; +{ + register int i = LOOPINIT(size); + *r = i; do r[i] = 0; while (--i > 0); + return r; +} + +/* set_fill -- make "r" the universal set of "size" elements */ +pset set_fill(r, size) +register pset r; +register int size; +{ + register int i = LOOPINIT(size); + *r = i; + r[i] = ~ (unsigned) 0; + r[i] >>= i * BPI - size; + while (--i > 0) + r[i] = ~ (unsigned) 0; + return r; +} + +/* set_copy -- copy set a into set r */ +pset set_copy(r, a) +register pset r, a; +{ + register int i = LOOPCOPY(a); + do r[i] = a[i]; while (--i >= 0); + return r; +} + +/* set_and -- compute intersection of sets "a" and "b" */ +pset set_and(r, a, b) +register pset r, a, b; +{ + register int i = LOOP(a); + PUTLOOP(r,i); do r[i] = a[i] & b[i]; while (--i > 0); + return r; +} + +/* set_or -- compute union of sets "a" and "b" */ +pset set_or(r, a, b) +register pset r, a, b; +{ + register int i = LOOP(a); + PUTLOOP(r,i); do r[i] = a[i] | b[i]; while (--i > 0); + return r; +} + +/* set_diff -- compute difference of sets "a" and "b" */ +pset set_diff(r, a, b) +register pset r, a, b; +{ + register int i = LOOP(a); + PUTLOOP(r,i); do r[i] = a[i] & ~b[i]; while (--i > 0); + return r; +} + +/* set_xor -- compute exclusive-or of sets "a" and "b" */ +pset set_xor(r, a, b) +register pset r, a, b; +{ + register int i = LOOP(a); +#ifdef IBM_WATC + PUTLOOP(r,i); do r[i] = (a[i]&~b[i]) | (~a[i]&b[i]); while (--i > 0); +#else + PUTLOOP(r,i); do r[i] = a[i] ^ b[i]; while (--i > 0); +#endif + return r; +} + +/* set_merge -- compute "a" & "mask" | "b" & ~ "mask" */ +pset set_merge(r, a, b, mask) +register pset r, a, b, mask; +{ + register int i = LOOP(a); + PUTLOOP(r,i); do r[i] = (a[i]&mask[i]) | (b[i]&~mask[i]); while (--i > 0); + return r; +} + +/* set_andp -- compute intersection of sets "a" and "b" , TRUE if nonempty */ +bool set_andp(r, a, b) +register pset r, a, b; +{ + register int i = LOOP(a); + register unsigned int x = 0; + PUTLOOP(r,i); do {r[i] = a[i] & b[i]; x |= r[i];} while (--i > 0); + return x != 0; +} + +/* set_orp -- compute union of sets "a" and "b" , TRUE if nonempty */ +bool set_orp(r, a, b) +register pset r, a, b; +{ + register int i = LOOP(a); + register unsigned int x = 0; + PUTLOOP(r,i); do {r[i] = a[i] | b[i]; x |= r[i];} while (--i > 0); + return x != 0; +} + +/* setp_empty -- check if the set "a" is empty */ +bool setp_empty(a) +register pset a; +{ + register int i = LOOP(a); + do if (a[i]) return FALSE; while (--i > 0); + return TRUE; +} + +/* setp_full -- check if the set "a" is the full set of "size" elements */ +bool setp_full(a, size) +register pset a; +register int size; +{ + register int i = LOOP(a); + register unsigned int test; + test = ~ (unsigned) 0; + test >>= i * BPI - size; + if (a[i] != test) + return FALSE; + while (--i > 0) + if (a[i] != (~(unsigned) 0)) + return FALSE; + return TRUE; +} + +/* setp_equal -- check if the set "a" equals set "b" */ +bool setp_equal(a, b) +register pset a, b; +{ + register int i = LOOP(a); + do if (a[i] != b[i]) return FALSE; while (--i > 0); + return TRUE; +} + +/* setp_disjoint -- check if intersection of "a" and "b" is empty */ +bool setp_disjoint(a, b) +register pset a, b; +{ + register int i = LOOP(a); + do if (a[i] & b[i]) return FALSE; while (--i > 0); + return TRUE; +} + +/* setp_implies -- check if "a" implies "b" ("b" contains "a") */ +bool setp_implies(a, b) +register pset a, b; +{ + register int i = LOOP(a); + do if (a[i] & ~b[i]) return FALSE; while (--i > 0); + return TRUE; +} + +/* sf_or -- form the "or" of all sets in a set family */ +pset sf_or(A) +pset_family A; +{ + register pset or, last, p; + + or = set_new(A->sf_size); + foreach_set(A, last, p) + INLINEset_or(or, or, p); + return or; +} + +/* sf_and -- form the "and" of all sets in a set family */ +pset sf_and(A) +pset_family A; +{ + register pset and, last, p; + + and = set_fill(set_new(A->sf_size), A->sf_size); + foreach_set(A, last, p) + INLINEset_and(and, and, p); + return and; +} + +/* sf_active -- make all members of the set family active */ +pset_family sf_active(A) +pset_family A; +{ + register pset p, last; + foreach_set(A, last, p) { + SET(p, ACTIVE); + } + A->active_count = A->count; + return A; +} + + +/* sf_inactive -- remove all inactive cubes in a set family */ +pset_family sf_inactive(A) +pset_family A; +{ + register pset p, last, pdest; + + pdest = A->data; + foreach_set(A, last, p) { + if (TESTP(p, ACTIVE)) { + if (pdest != p) { + INLINEset_copy(pdest, p); + } + pdest += A->wsize; + } else { + A->count--; + } + } + return A; +} + + +/* sf_copy -- copy a set family */ +pset_family sf_copy(R, A) +pset_family R, A; +{ + R->sf_size = A->sf_size; + R->wsize = A->wsize; +/*R->capacity = A->count;*/ +/*R->data = REALLOC(unsigned int, R->data, (long) R->capacity * R->wsize);*/ + R->count = A->count; + R->active_count = A->active_count; + intcpy(R->data, A->data, (long) A->wsize * A->count); + return R; +} + + +/* sf_join -- join A and B into a single set_family */ +pset_family sf_join(A, B) +pset_family A, B; +{ + pset_family R; + long asize = A->count * A->wsize; + long bsize = B->count * B->wsize; + + if (A->sf_size != B->sf_size) fatal("sf_join: sf_size mismatch"); + R = sf_new(A->count + B->count, A->sf_size); + R->count = A->count + B->count; + R->active_count = A->active_count + B->active_count; + intcpy(R->data, A->data, asize); + intcpy(R->data + asize, B->data, bsize); + return R; +} + + +/* sf_append -- append the sets of B to the end of A, and dispose of B */ +pset_family sf_append(A, B) +pset_family A, B; +{ + long asize = A->count * A->wsize; + long bsize = B->count * B->wsize; + + if (A->sf_size != B->sf_size) fatal("sf_append: sf_size mismatch"); + A->capacity = A->count + B->count; + A->data = REALLOC(unsigned int, A->data, (long) A->capacity * A->wsize); + intcpy(A->data + asize, B->data, bsize); + A->count += B->count; + A->active_count += B->active_count; + sf_free(B); + return A; +} + + +/* sf_new -- allocate "num" sets of "size" elements each */ +pset_family sf_new(num, size) +int num, size; +{ + pset_family A; + if (set_family_garbage == NULL) { + A = ALLOC(set_family_t, 1); + } else { + A = set_family_garbage; + set_family_garbage = A->next; + } + A->sf_size = size; + A->wsize = SET_SIZE(size); + A->capacity = num; + A->data = ALLOC(unsigned int, (long) A->capacity * A->wsize); + A->count = 0; + A->active_count = 0; + return A; +} + + +/* sf_save -- create a duplicate copy of a set family */ +pset_family sf_save(A) +register pset_family A; +{ + return sf_copy(sf_new(A->count, A->sf_size), A); +} + + +/* sf_free -- free the storage allocated for a set family */ +void sf_free(A) +pset_family A; +{ + FREE(A->data); + A->next = set_family_garbage; + set_family_garbage = A; +} + + +/* sf_cleanup -- free all of the set families from the garbage list */ +void sf_cleanup() +{ + register pset_family p, pnext; + for(p = set_family_garbage; p != (pset_family) NULL; p = pnext) { + pnext = p->next; + FREE(p); + } + set_family_garbage = (pset_family) NULL; +} + + +/* sf_addset -- add a set to the end of a set family */ +pset_family sf_addset(A, s) +pset_family A; +pset s; +{ + register pset p; + + if (A->count >= A->capacity) { + A->capacity = A->capacity + A->capacity/2 + 1; + A->data = REALLOC(unsigned int, A->data, (long) A->capacity * A->wsize); + } + p = GETSET(A, A->count++); + INLINEset_copy(p, s); + return A; +} + +/* sf_delset -- delete a set from a set family */ +void sf_delset(A, i) +pset_family A; +int i; +{ (void) set_copy(GETSET(A,i), GETSET(A, --A->count));} + +/* sf_print -- print a set_family as a set (list the element numbers) */ +void sf_print(A) +pset_family A; +{ + char *ps1(); + register pset p; + register int i; + foreachi_set(A, i, p) + printf("A[%d] = %s\n", i, ps1(p)); +} + +/* sf_bm_print -- print a set_family as a bit-matrix */ +void sf_bm_print(A) +pset_family A; +{ + char *pbv1(); + register pset p; + register int i; + foreachi_set(A, i, p) + printf("[%4d] %s\n", i, pbv1(p, A->sf_size)); +} + + +/* sf_write -- output a set family in an unintelligable manner */ +void sf_write(fp, A) +FILE *fp; +pset_family A; +{ + register pset p, last; + (void) fprintf(fp, "%d %d\n", A->count, A->sf_size); + foreach_set(A, last, p) + set_write(fp, p); + (void) fflush(fp); +} + + +/* sf_read -- read a set family written by sf_write */ +pset_family sf_read(fp) +FILE *fp; +{ + int i, j; + register pset p, last; + pset_family A; + + (void) fscanf(fp, "%d %d\n", &i, &j); + A = sf_new(i, j); + A->count = i; + foreach_set(A, last, p) { + (void) fscanf(fp, "%x", p); + for(j = 1; j <= LOOP(p); j++) + (void) fscanf(fp, "%x", p+j); + } + return A; +} + + +/* set_write -- output a set in an unintelligable manner */ +void set_write(fp, a) +register FILE *fp; +register pset a; +{ + register int n = LOOP(a), j; + + for(j = 0; j <= n; j++) { + (void) fprintf(fp, "%x ", a[j]); + if ((j+1) % 8 == 0 && j != n) + (void) fprintf(fp, "\n\t"); + } + (void) fprintf(fp, "\n"); +} + + +/* sf_bm_read -- read a set family written by sf_bm_print (almost) */ +pset_family sf_bm_read(fp) +FILE *fp; +{ + int i, j, rows, cols; + register pset pdest; + pset_family A; + + (void) fscanf(fp, "%d %d\n", &rows, &cols); + A = sf_new(rows, cols); + for(i = 0; i < rows; i++) { + pdest = GETSET(A, A->count++); + (void) set_clear(pdest, A->sf_size); + for(j = 0; j < cols; j++) { + switch(getc(fp)) { + case '0': + break; + case '1': + set_insert(pdest, j); + break; + default: + fatal("Error reading set family"); + } + } + if (getc(fp) != '\n') { + fatal("Error reading set family (at end of line)"); + } + } + return A; +} + + + +/* ps1 -- convert a set into a printable string */ +#define largest_string 120 +static char s1[largest_string]; +char *ps1(a) +register pset a; +{ + register int i, num, l, len = 0, n = NELEM(a); + char temp[20]; + bool first = TRUE; + + s1[len++] = '['; + for(i = 0; i < n; i++) + if (is_in_set(a,i)) { + if (! first) + s1[len++] = ','; + first = FALSE; num = i; + /* Generate digits (reverse order) */ + l = 0; do temp[l++] = num % 10 + '0'; while ((num /= 10) > 0); + /* Copy them back in correct order */ + do s1[len++] = temp[--l]; while (l > 0); + if (len > largest_string-15) { + s1[len++] = '.'; s1[len++] = '.'; s1[len++] = '.'; + break; + } + } + + s1[len++] = ']'; + s1[len++] = '\0'; + return s1; +} + +/* pbv1 -- print bit-vector */ +char *pbv1(s, n) +pset s; +int n; +{ + register int i; + for(i = 0; i < n; i++) + s1[i] = is_in_set(s,i) ? '1' : '0'; + s1[n] = '\0'; + return s1; +} + + +/* set_adjcnt -- adjust the counts for a set by "weight" */ +void +set_adjcnt(a, count, weight) +register pset a; +register int *count, weight; +{ + register int i, base; + register unsigned int val; + + for(i = LOOP(a); i > 0; ) { + for(val = a[i], base = --i << LOGBPI; val != 0; base++, val >>= 1) { + if (val & 1) { + count[base] += weight; + } + } + } +} + + + +/* sf_count -- perform a column sum over a set family */ +int *sf_count(A) +pset_family A; +{ + register pset p, last; + register int i, base, *count; + register unsigned int val; + + count = ALLOC(int, A->sf_size); + for(i = A->sf_size - 1; i >= 0; i--) { + count[i] = 0; + } + + foreach_set(A, last, p) { + for(i = LOOP(p); i > 0; ) { + for(val = p[i], base = --i << LOGBPI; val != 0; base++, val >>= 1) { + if (val & 1) { + count[base]++; + } + } + } + } + return count; +} + + +/* sf_count_restricted -- perform a column sum over a set family, restricting + * to only the columns which are in r; also, the columns are weighted by the + * number of elements which are in each row + */ +int *sf_count_restricted(A, r) +pset_family A; +register pset r; +{ + register pset p; + register int i, base, *count; + register unsigned int val; + int weight; + pset last; + + count = ALLOC(int, A->sf_size); + for(i = A->sf_size - 1; i >= 0; i--) { + count[i] = 0; + } + + /* Loop for each set */ + foreach_set(A, last, p) { + weight = 1024 / (set_ord(p) - 1); + for(i = LOOP(p); i > 0; ) { + for(val=p[i]&r[i], base= --i<>= 1) { + if (val & 1) { + count[base] += weight; + } + } + } + } + return count; +} + + +/* + * sf_delc -- delete columns first ... last of A + */ +pset_family sf_delc(A, first, last) +pset_family A; +int first, last; +{ + return sf_delcol(A, first, last-first + 1); +} + + +/* + * sf_addcol -- add columns to a set family; includes a quick check to see + * if there is already enough room (and hence, can avoid copying) + */ +pset_family sf_addcol(A, firstcol, n) +pset_family A; +int firstcol, n; +{ + int maxsize; + + /* Check if adding columns at the end ... */ + if (firstcol == A->sf_size) { + /* If so, check if there is already enough room */ + maxsize = BPI * LOOPINIT(A->sf_size); + if ((A->sf_size + n) <= maxsize) { + A->sf_size += n; + return A; + } + } + return sf_delcol(A, firstcol, -n); +} + +/* + * sf_delcol -- add/delete columns to/from a set family + * + * if n > 0 then n columns starting from firstcol are deleted if n < 0 + * then n blank columns are inserted starting at firstcol + * (i.e., the first new column number is firstcol) + * + * This is done by copying columns in the array which is a relatively + * slow operation. + */ +pset_family sf_delcol(A, firstcol, n) +pset_family A; +register int firstcol, n; +{ + register pset p, last, pdest; + register int i; + pset_family B; + + B = sf_new(A->count, A->sf_size - n); + foreach_set(A, last, p) { + pdest = GETSET(B, B->count++); + INLINEset_clear(pdest, B->sf_size); + for(i = 0; i < firstcol; i++) + if (is_in_set(p, i)) + set_insert(pdest, i); + for(i = n > 0 ? firstcol + n : firstcol; i < A->sf_size; i++) + if (is_in_set(p, i)) + set_insert(pdest, i - n); + } + sf_free(A); + return B; +} + + +/* + * sf_copy_col -- copy column "srccol" from "src" to column "dstcol" of "dst" + */ +pset_family sf_copy_col(dst, dstcol, src, srccol) +pset_family dst, src; +int dstcol, srccol; +{ + register pset last, p, pdest; + register int word_test, word_set; + unsigned int bit_set, bit_test; + + /* CHEAT! form these constants outside the loop */ + word_test = WHICH_WORD(srccol); + bit_test = 1 << WHICH_BIT(srccol); + word_set = WHICH_WORD(dstcol); + bit_set = 1 << WHICH_BIT(dstcol); + + pdest = dst->data; + foreach_set(src, last, p) { + if ((p[word_test] & bit_test) != 0) + pdest[word_set] |= bit_set; +/* + * equivalent code for this is ... + * if (is_in_set(p, srccol)) set_insert(pdest, destcol); + */ + pdest += dst->wsize; + } + return dst; +} + + + +/* + * sf_compress -- delete columns from a matrix + */ +pset_family sf_compress(A, c) +pset_family A; /* will be freed */ +register pset c; +{ + register pset p; + register int i, bcol; + pset_family B; + + /* create a clean set family for the result */ + B = sf_new(A->count, set_ord(c)); + for(i = 0; i < A->count; i++) { + p = GETSET(B, B->count++); + INLINEset_clear(p, B->sf_size); + } + + /* copy each column of A which has a 1 in c */ + bcol = 0; + for(i = 0; i < A->sf_size; i++) { + if (is_in_set(c, i)) { + (void) sf_copy_col(B, bcol++, A, i); + } + } + sf_free(A); + return B; +} + + + +/* + * sf_transpose -- transpose a bit matrix + * + * There are trickier ways of doing this, but this works. + */ +pset_family sf_transpose(A) +pset_family A; +{ + pset_family B; + register pset p; + register int i, j; + + B = sf_new(A->sf_size, A->count); + B->count = A->sf_size; + foreachi_set(B, i, p) { + INLINEset_clear(p, B->sf_size); + } + foreachi_set(A, i, p) { + for(j = 0; j < A->sf_size; j++) { + if (is_in_set(p, j)) { + set_insert(GETSET(B, j), i); + } + } + } + sf_free(A); + return B; +} + + +/* + * sf_permute -- permute the columns of a set_family + * + * permute is an array of integers containing column numbers of A which + * are to be retained. + */ +pset_family sf_permute(A, permute, npermute) +pset_family A; +register int *permute, npermute; +{ + pset_family B; + register pset p, last, pdest; + register int j; + + B = sf_new(A->count, npermute); + B->count = A->count; + foreach_set(B, last, p) + INLINEset_clear(p, npermute); + + pdest = B->data; + foreach_set(A, last, p) { + for(j = 0; j < npermute; j++) + if (is_in_set(p, permute[j])) + set_insert(pdest, j); + pdest += B->wsize; + } + sf_free(A); + return B; +} diff --git a/abc_with_bb_support/src/misc/espresso/setc.c b/abc_with_bb_support/src/misc/espresso/setc.c new file mode 100644 index 000000000..b4d494b81 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/setc.c @@ -0,0 +1,483 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +/* + setc.c -- massive bit-hacking for performing special "cube"-type + operations on a set + + The basic trick used for binary valued variables is the following: + + If a[w] and b[w] contain a full word of binary variables, then: + + 1) to get the full word of their intersection, we use + + x = a[w] & b[w]; + + + 2) to see if the intersection is null in any variables, we examine + + x = ~(x | x >> 1) & DISJOINT; + + this will have a single 1 in each binary variable for which + the intersection is null. In particular, if this is zero, + then there are no disjoint variables; or, if this is nonzero, + then there is at least one disjoint variable. A "count_ones" + over x will tell in how many variables they have an null + intersection. + + + 3) to get a mask which selects the disjoint variables, we use + + (x | x << 1) + + this provides a selector which can be used to see where + they have an null intersection + + + cdist return distance between two cubes + cdist0 return true if two cubes are distance 0 apart + cdist01 return distance, or 2 if distance exceeds 1 + consensus compute consensus of two cubes distance 1 apart + force_lower expand hack (for now), related to consensus +*/ + +#include "espresso.h" + +/* see if the cube has a full row of 1's (with respect to cof) */ +bool full_row(p, cof) +IN register pcube p, cof; +{ + register int i = LOOP(p); + do if ((p[i] | cof[i]) != cube.fullset[i]) return FALSE; while (--i > 0); + return TRUE; +} + +/* + cdist0 -- return TRUE if a and b are distance 0 apart +*/ + +bool cdist0(a, b) +register pcube a, b; +{ + { /* Check binary variables */ + register int w, last; register unsigned int x; + if ((last = cube.inword) != -1) { + + /* Check the partial word of binary variables */ + x = a[last] & b[last]; + if (~(x | x >> 1) & cube.inmask) + return FALSE; /* disjoint in some variable */ + + /* Check the full words of binary variables */ + for(w = 1; w < last; w++) { + x = a[w] & b[w]; + if (~(x | x >> 1) & DISJOINT) + return FALSE; /* disjoint in some variable */ + } + } + } + + { /* Check the multiple-valued variables */ + register int w, var, last; register pcube mask; + for(var = cube.num_binary_vars; var < cube.num_vars; var++) { + mask = cube.var_mask[var]; last = cube.last_word[var]; + for(w = cube.first_word[var]; w <= last; w++) + if (a[w] & b[w] & mask[w]) + goto nextvar; + return FALSE; /* disjoint in this variable */ + nextvar: ; + } + } + return TRUE; +} + +/* + cdist01 -- return the "distance" between two cubes (defined as the + number of null variables in their intersection). If the distance + exceeds 1, the value 2 is returned. +*/ + +int cdist01(a, b) +register pset a, b; +{ + int dist = 0; + + { /* Check binary variables */ + register int w, last; register unsigned int x; + if ((last = cube.inword) != -1) { + + /* Check the partial word of binary variables */ + x = a[last] & b[last]; + if (x = ~ (x | x >> 1) & cube.inmask) + if ((dist = count_ones(x)) > 1) + return 2; + + /* Check the full words of binary variables */ + for(w = 1; w < last; w++) { + x = a[w] & b[w]; + if (x = ~ (x | x >> 1) & DISJOINT) + if (dist == 1 || (dist += count_ones(x)) > 1) + return 2; + } + } + } + + { /* Check the multiple-valued variables */ + register int w, var, last; register pcube mask; + for(var = cube.num_binary_vars; var < cube.num_vars; var++) { + mask = cube.var_mask[var]; last = cube.last_word[var]; + for(w = cube.first_word[var]; w <= last; w++) + if (a[w] & b[w] & mask[w]) + goto nextvar; + if (++dist > 1) + return 2; + nextvar: ; + } + } + return dist; +} + +/* + cdist -- return the "distance" between two cubes (defined as the + number of null variables in their intersection). +*/ + +int cdist(a, b) +register pset a, b; +{ + int dist = 0; + + { /* Check binary variables */ + register int w, last; register unsigned int x; + if ((last = cube.inword) != -1) { + + /* Check the partial word of binary variables */ + x = a[last] & b[last]; + if (x = ~ (x | x >> 1) & cube.inmask) + dist = count_ones(x); + + /* Check the full words of binary variables */ + for(w = 1; w < last; w++) { + x = a[w] & b[w]; + if (x = ~ (x | x >> 1) & DISJOINT) + dist += count_ones(x); + } + } + } + + { /* Check the multiple-valued variables */ + register int w, var, last; register pcube mask; + for(var = cube.num_binary_vars; var < cube.num_vars; var++) { + mask = cube.var_mask[var]; last = cube.last_word[var]; + for(w = cube.first_word[var]; w <= last; w++) + if (a[w] & b[w] & mask[w]) + goto nextvar; + dist++; + nextvar: ; + } + } + return dist; +} + +/* + force_lower -- Determine which variables of a do not intersect b. +*/ + +pset force_lower(xlower, a, b) +INOUT pset xlower; +IN register pset a, b; +{ + + { /* Check binary variables (if any) */ + register int w, last; register unsigned int x; + if ((last = cube.inword) != -1) { + + /* Check the partial word of binary variables */ + x = a[last] & b[last]; + if (x = ~(x | x >> 1) & cube.inmask) + xlower[last] |= (x | (x << 1)) & a[last]; + + /* Check the full words of binary variables */ + for(w = 1; w < last; w++) { + x = a[w] & b[w]; + if (x = ~(x | x >> 1) & DISJOINT) + xlower[w] |= (x | (x << 1)) & a[w]; + } + } + } + + { /* Check the multiple-valued variables */ + register int w, var, last; register pcube mask; + for(var = cube.num_binary_vars; var < cube.num_vars; var++) { + mask = cube.var_mask[var]; last = cube.last_word[var]; + for(w = cube.first_word[var]; w <= last; w++) + if (a[w] & b[w] & mask[w]) + goto nextvar; + for(w = cube.first_word[var]; w <= last; w++) + xlower[w] |= a[w] & mask[w]; + nextvar: ; + } + } + return xlower; +} + +/* + consensus -- multiple-valued consensus + + Although this looks very messy, the idea is to compute for r the + "and" of the cubes a and b for each variable, unless the "and" is + null in a variable, in which case the "or" of a and b is computed + for this variable. + + Because we don't check how many variables are null in the + intersection of a and b, the returned value for r really only + represents the consensus when a and b are distance 1 apart. +*/ + +void consensus(r, a, b) +INOUT pcube r; +IN register pcube a, b; +{ + INLINEset_clear(r, cube.size); + + { /* Check binary variables (if any) */ + register int w, last; register unsigned int x; + if ((last = cube.inword) != -1) { + + /* Check the partial word of binary variables */ + r[last] = x = a[last] & b[last]; + if (x = ~(x | x >> 1) & cube.inmask) + r[last] |= (x | (x << 1)) & (a[last] | b[last]); + + /* Check the full words of binary variables */ + for(w = 1; w < last; w++) { + r[w] = x = a[w] & b[w]; + if (x = ~(x | x >> 1) & DISJOINT) + r[w] |= (x | (x << 1)) & (a[w] | b[w]); + } + } + } + + + { /* Check the multiple-valued variables */ + bool empty; int var; unsigned int x; + register int w, last; register pcube mask; + for(var = cube.num_binary_vars; var < cube.num_vars; var++) { + mask = cube.var_mask[var]; + last = cube.last_word[var]; + empty = TRUE; + for(w = cube.first_word[var]; w <= last; w++) + if (x = a[w] & b[w] & mask[w]) + empty = FALSE, r[w] |= x; + if (empty) + for(w = cube.first_word[var]; w <= last; w++) + r[w] |= mask[w] & (a[w] | b[w]); + } + } +} + +/* + cactive -- return the index of the single active variable in + the cube, or return -1 if there are none or more than 2. +*/ + +int cactive(a) +register pcube a; +{ + int active = -1, dist = 0, bit_index(); + + { /* Check binary variables */ + register int w, last; + register unsigned int x; + if ((last = cube.inword) != -1) { + + /* Check the partial word of binary variables */ + x = a[last]; + if (x = ~ (x & x >> 1) & cube.inmask) { + if ((dist = count_ones(x)) > 1) + return -1; /* more than 2 active variables */ + active = (last-1)*(BPI/2) + bit_index(x) / 2; + } + + /* Check the full words of binary variables */ + for(w = 1; w < last; w++) { + x = a[w]; + if (x = ~ (x & x >> 1) & DISJOINT) { + if ((dist += count_ones(x)) > 1) + return -1; /* more than 2 active variables */ + active = (w-1)*(BPI/2) + bit_index(x) / 2; + } + } + } + } + + { /* Check the multiple-valued variables */ + register int w, var, last; + register pcube mask; + for(var = cube.num_binary_vars; var < cube.num_vars; var++) { + mask = cube.var_mask[var]; + last = cube.last_word[var]; + for(w = cube.first_word[var]; w <= last; w++) + if (mask[w] & ~ a[w]) { + if (++dist > 1) + return -1; + active = var; + break; + } + } + } + return active; +} + +/* + ccommon -- return TRUE if a and b are share "active" variables + active variables include variables that are empty; +*/ + +bool ccommon(a, b, cof) +register pcube a, b, cof; +{ + { /* Check binary variables */ + int last; + register int w; + register unsigned int x, y; + if ((last = cube.inword) != -1) { + + /* Check the partial word of binary variables */ + x = a[last] | cof[last]; + y = b[last] | cof[last]; + if (~(x & x>>1) & ~(y & y>>1) & cube.inmask) + return TRUE; + + /* Check the full words of binary variables */ + for(w = 1; w < last; w++) { + x = a[w] | cof[w]; + y = b[w] | cof[w]; + if (~(x & x>>1) & ~(y & y>>1) & DISJOINT) + return TRUE; + } + } + } + + { /* Check the multiple-valued variables */ + int var; + register int w, last; + register pcube mask; + for(var = cube.num_binary_vars; var < cube.num_vars; var++) { + mask = cube.var_mask[var]; last = cube.last_word[var]; + /* Check for some part missing from a */ + for(w = cube.first_word[var]; w <= last; w++) + if (mask[w] & ~a[w] & ~cof[w]) { + + /* If so, check for some part missing from b */ + for(w = cube.first_word[var]; w <= last; w++) + if (mask[w] & ~b[w] & ~cof[w]) + return TRUE; /* both active */ + break; + } + } + } + return FALSE; +} + +/* + These routines compare two sets (cubes) for the qsort() routine and + return: + + -1 if set a is to precede set b + 0 if set a and set b are equal + 1 if set a is to follow set b + + Usually the SIZE field of the set is assumed to contain the size + of the set (which will save recomputing the set size during the + sort). For distance-1 merging, the global variable cube.temp[0] is + a mask which mask's-out the merging variable. +*/ + +/* descend -- comparison for descending sort on set size */ +int descend(a, b) +pset *a, *b; +{ + register pset a1 = *a, b1 = *b; + if (SIZE(a1) > SIZE(b1)) return -1; + else if (SIZE(a1) < SIZE(b1)) return 1; + else { + register int i = LOOP(a1); + do + if (a1[i] > b1[i]) return -1; else if (a1[i] < b1[i]) return 1; + while (--i > 0); + } + return 0; +} + +/* ascend -- comparison for ascending sort on set size */ +int ascend(a, b) +pset *a, *b; +{ + register pset a1 = *a, b1 = *b; + if (SIZE(a1) > SIZE(b1)) return 1; + else if (SIZE(a1) < SIZE(b1)) return -1; + else { + register int i = LOOP(a1); + do + if (a1[i] > b1[i]) return 1; else if (a1[i] < b1[i]) return -1; + while (--i > 0); + } + return 0; +} + + +/* lex_order -- comparison for "lexical" ordering of cubes */ +int lex_order(a, b) +pset *a, *b; +{ + register pset a1 = *a, b1 = *b; + register int i = LOOP(a1); + do + if (a1[i] > b1[i]) return -1; else if (a1[i] < b1[i]) return 1; + while (--i > 0); + return 0; +} + + +/* d1_order -- comparison for distance-1 merge routine */ +int d1_order(a, b) +pset *a, *b; +{ + register pset a1 = *a, b1 = *b, c1 = cube.temp[0]; + register int i = LOOP(a1); + register unsigned int x1, x2; + do + if ((x1 = a1[i] | c1[i]) > (x2 = b1[i] | c1[i])) return -1; + else if (x1 < x2) return 1; + while (--i > 0); + return 0; +} + + +/* desc1 -- comparison (without indirection) for descending sort */ +/* also has effect of handling NULL pointers,and a NULL pointer has smallest +order */ +int desc1(a, b) +register pset a, b; +{ + if (a == (pset) NULL) + return (b == (pset) NULL) ? 0 : 1; + else if (b == (pset) NULL) + return -1; + if (SIZE(a) > SIZE(b)) return -1; + else if (SIZE(a) < SIZE(b)) return 1; + else { + register int i = LOOP(a); + do + if (a[i] > b[i]) return -1; else if (a[i] < b[i]) return 1; + while (--i > 0); + } + return 0; +} diff --git a/abc_with_bb_support/src/misc/espresso/sharp.c b/abc_with_bb_support/src/misc/espresso/sharp.c new file mode 100644 index 000000000..61dc58de6 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/sharp.c @@ -0,0 +1,247 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +/* + sharp.c -- perform sharp, disjoint sharp, and intersection +*/ + +#include "espresso.h" + +long start_time; + + +/* cv_sharp -- form the sharp product between two covers */ +pcover cv_sharp(A, B) +pcover A, B; +{ + pcube last, p; + pcover T; + + T = new_cover(0); + foreach_set(A, last, p) + T = sf_union(T, cb_sharp(p, B)); + return T; +} + + +/* cb_sharp -- form the sharp product between a cube and a cover */ +pcover cb_sharp(c, T) +pcube c; +pcover T; +{ + if (T->count == 0) { + T = sf_addset(new_cover(1), c); + } else { + start_time = ptime(); + T = cb_recur_sharp(c, T, 0, T->count-1, 0); + } + return T; +} + + +/* recursive formulation to provide balanced merging */ +pcover cb_recur_sharp(c, T, first, last, level) +pcube c; +pcover T; +int first, last, level; +{ + pcover temp, left, right; + int middle; + + if (first == last) { + temp = sharp(c, GETSET(T, first)); + } else { + middle = (first + last) / 2; + left = cb_recur_sharp(c, T, first, middle, level+1); + right = cb_recur_sharp(c, T, middle+1, last, level+1); + temp = cv_intersect(left, right); + if ((debug & SHARP) && level < 4) { + printf("# SHARP[%d]: %4d = %4d x %4d, time = %s\n", + level, temp->count, left->count, right->count, + print_time(ptime() - start_time)); + (void) fflush(stdout); + } + free_cover(left); + free_cover(right); + } + return temp; +} + + +/* sharp -- form the sharp product between two cubes */ +pcover sharp(a, b) +pcube a, b; +{ + register int var; + register pcube d=cube.temp[0], temp=cube.temp[1], temp1=cube.temp[2]; + pcover r = new_cover(cube.num_vars); + + if (cdist0(a, b)) { + set_diff(d, a, b); + for(var = 0; var < cube.num_vars; var++) { + if (! setp_empty(set_and(temp, d, cube.var_mask[var]))) { + set_diff(temp1, a, cube.var_mask[var]); + set_or(GETSET(r, r->count++), temp, temp1); + } + } + } else { + r = sf_addset(r, a); + } + return r; +} + +pcover make_disjoint(A) +pcover A; +{ + pcover R, new; + register pset last, p; + + R = new_cover(0); + foreach_set(A, last, p) { + new = cb_dsharp(p, R); + R = sf_append(R, new); + } + return R; +} + + +/* cv_dsharp -- disjoint-sharp product between two covers */ +pcover cv_dsharp(A, B) +pcover A, B; +{ + register pcube last, p; + pcover T; + + T = new_cover(0); + foreach_set(A, last, p) { + T = sf_union(T, cb_dsharp(p, B)); + } + return T; +} + + +/* cb1_dsharp -- disjoint-sharp product between a cover and a cube */ +pcover cb1_dsharp(T, c) +pcover T; +pcube c; +{ + pcube last, p; + pcover R; + + R = new_cover(T->count); + foreach_set(T, last, p) { + R = sf_union(R, dsharp(p, c)); + } + return R; +} + + +/* cb_dsharp -- disjoint-sharp product between a cube and a cover */ +pcover cb_dsharp(c, T) +pcube c; +pcover T; +{ + pcube last, p; + pcover Y, Y1; + + if (T->count == 0) { + Y = sf_addset(new_cover(1), c); + } else { + Y = new_cover(T->count); + set_copy(GETSET(Y,Y->count++), c); + foreach_set(T, last, p) { + Y1 = cb1_dsharp(Y, p); + free_cover(Y); + Y = Y1; + } + } + return Y; +} + + +/* dsharp -- form the disjoint-sharp product between two cubes */ +pcover dsharp(a, b) +pcube a, b; +{ + register pcube mask, diff, and, temp, temp1 = cube.temp[0]; + int var; + pcover r; + + r = new_cover(cube.num_vars); + + if (cdist0(a, b)) { + diff = set_diff(new_cube(), a, b); + and = set_and(new_cube(), a, b); + mask = new_cube(); + for(var = 0; var < cube.num_vars; var++) { + /* check if position var of "a and not b" is not empty */ + if (! setp_disjoint(diff, cube.var_mask[var])) { + + /* coordinate var equals the difference between a and b */ + temp = GETSET(r, r->count++); + (void) set_and(temp, diff, cube.var_mask[var]); + + /* coordinates 0 ... var-1 equal the intersection */ + INLINEset_and(temp1, and, mask); + INLINEset_or(temp, temp, temp1); + + /* coordinates var+1 .. cube.num_vars equal a */ + set_or(mask, mask, cube.var_mask[var]); + INLINEset_diff(temp1, a, mask); + INLINEset_or(temp, temp, temp1); + } + } + free_cube(diff); + free_cube(and); + free_cube(mask); + } else { + r = sf_addset(r, a); + } + return r; +} + +/* cv_intersect -- form the intersection of two covers */ + +#define MAGIC 500 /* save 500 cubes before containment */ + +pcover cv_intersect(A, B) +pcover A, B; +{ + register pcube pi, pj, lasti, lastj, pt; + pcover T, Tsave = NULL; + + /* How large should each temporary result cover be ? */ + T = new_cover(MAGIC); + pt = T->data; + + /* Form pairwise intersection of each cube of A with each cube of B */ + foreach_set(A, lasti, pi) { + foreach_set(B, lastj, pj) { + if (cdist0(pi, pj)) { + (void) set_and(pt, pi, pj); + if (++T->count >= T->capacity) { + if (Tsave == NULL) + Tsave = sf_contain(T); + else + Tsave = sf_union(Tsave, sf_contain(T)); + T = new_cover(MAGIC); + pt = T->data; + } else + pt += T->wsize; + } + } + } + + + if (Tsave == NULL) + Tsave = sf_contain(T); + else + Tsave = sf_union(Tsave, sf_contain(T)); + return Tsave; +} diff --git a/abc_with_bb_support/src/misc/espresso/sminterf.c b/abc_with_bb_support/src/misc/espresso/sminterf.c new file mode 100644 index 000000000..96a27491c --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/sminterf.c @@ -0,0 +1,44 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +#include "espresso.h" + + +pset +do_sm_minimum_cover(A) +pset_family A; +{ + sm_matrix *M; + sm_row *sparse_cover; + sm_element *pe; + pset cover; + register int i, base, rownum; + register unsigned val; + register pset last, p; + + M = sm_alloc(); + rownum = 0; + foreach_set(A, last, p) { + foreach_set_element(p, i, val, base) { + (void) sm_insert(M, rownum, base); + } + rownum++; + } + + sparse_cover = sm_minimum_cover(M, NIL(int), 1, 0); + sm_free(M); + + cover = set_new(A->sf_size); + sm_foreach_row_element(sparse_cover, pe) { + set_insert(cover, pe->col_num); + } + sm_row_free(sparse_cover); + + return cover; +} diff --git a/abc_with_bb_support/src/misc/espresso/solution.c b/abc_with_bb_support/src/misc/espresso/solution.c new file mode 100644 index 000000000..660d37885 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/solution.c @@ -0,0 +1,114 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +#include "mincov_int.h" + + +solution_t * +solution_alloc() +{ + solution_t *sol; + + sol = ALLOC(solution_t, 1); + sol->cost = 0; + sol->row = sm_row_alloc(); + return sol; +} + + +void +solution_free(sol) +solution_t *sol; +{ + sm_row_free(sol->row); + FREE(sol); +} + + +solution_t * +solution_dup(sol) +solution_t *sol; +{ + solution_t *new_sol; + + new_sol = ALLOC(solution_t, 1); + new_sol->cost = sol->cost; + new_sol->row = sm_row_dup(sol->row); + return new_sol; +} + + +void +solution_add(sol, weight, col) +solution_t *sol; +int *weight; +int col; +{ + (void) sm_row_insert(sol->row, col); + sol->cost += WEIGHT(weight, col); +} + + +void +solution_accept(sol, A, weight, col) +solution_t *sol; +sm_matrix *A; +int *weight; +int col; +{ + register sm_element *p, *pnext; + sm_col *pcol; + + solution_add(sol, weight, col); + + /* delete rows covered by this column */ + pcol = sm_get_col(A, col); + for(p = pcol->first_row; p != 0; p = pnext) { + pnext = p->next_row; /* grab it before it disappears */ + sm_delrow(A, p->row_num); + } +} + + +/* ARGSUSED */ +void +solution_reject(sol, A, weight, col) +solution_t *sol; +sm_matrix *A; +int *weight; +int col; +{ + sm_delcol(A, col); +} + + +solution_t * +solution_choose_best(best1, best2) +solution_t *best1, *best2; +{ + if (best1 != NIL(solution_t)) { + if (best2 != NIL(solution_t)) { + if (best1->cost <= best2->cost) { + solution_free(best2); + return best1; + } else { + solution_free(best1); + return best2; + } + } else { + return best1; + } + } else { + if (best2 != NIL(solution_t)) { + return best2; + } else { + return NIL(solution_t); + } + } +} diff --git a/abc_with_bb_support/src/misc/espresso/sparse.c b/abc_with_bb_support/src/misc/espresso/sparse.c new file mode 100644 index 000000000..1f047b557 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/sparse.c @@ -0,0 +1,146 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +/* + module: sparse.c + + make_sparse is a last-step cleanup to reduce the total number + of literals in the cover. + + This is done by reducing the "sparse" variables (using a modified + version of irredundant rather than reduce), followed by expanding + the "dense" variables (using modified version of expand). +*/ + +#include "espresso.h" + +pcover make_sparse(F, D, R) +pcover F, D, R; +{ + cost_t cost, best_cost; + + cover_cost(F, &best_cost); + + do { + EXECUTE(F = mv_reduce(F, D), MV_REDUCE_TIME, F, cost); + if (cost.total == best_cost.total) + break; + copy_cost(&cost, &best_cost); + + EXECUTE(F = expand(F, R, TRUE), RAISE_IN_TIME, F, cost); + if (cost.total == best_cost.total) + break; + copy_cost(&cost, &best_cost); + } while (force_irredundant); + + return F; +} + +/* + mv_reduce -- perform an "optimal" reduction of the variables which + we desire to be sparse + + This could be done using "reduce" and then saving just the desired + part of the reduction. Instead, this version uses IRRED to find + which cubes of an output are redundant. Note that this gets around + the cube-ordering problem. + + In normal use, it is expected that the cover is irredundant and + hence no cubes will be reduced to the empty cube (however, this is + checked for and such cubes will be deleted) +*/ + +pcover +mv_reduce(F, D) +pcover F, D; +{ + register int i, var; + register pcube p, p1, last; + int index; + pcover F1, D1; + pcube *F_cube_table; + + /* loop for each multiple-valued variable */ + for(var = 0; var < cube.num_vars; var++) { + + if (cube.sparse[var]) { + + /* loop for each part of the variable */ + for(i = cube.first_part[var]; i <= cube.last_part[var]; i++) { + + /* remember mapping of F1 cubes back to F cubes */ + F_cube_table = ALLOC(pcube, F->count); + + /* 'cofactor' against part #i of variable #var */ + F1 = new_cover(F->count); + foreach_set(F, last, p) { + if (is_in_set(p, i)) { + F_cube_table[F1->count] = p; + p1 = GETSET(F1, F1->count++); + (void) set_diff(p1, p, cube.var_mask[var]); + set_insert(p1, i); + } + } + + /* 'cofactor' against part #i of variable #var */ + /* not really necessary -- just more efficient ? */ + D1 = new_cover(D->count); + foreach_set(D, last, p) { + if (is_in_set(p, i)) { + p1 = GETSET(D1, D1->count++); + (void) set_diff(p1, p, cube.var_mask[var]); + set_insert(p1, i); + } + } + + mark_irredundant(F1, D1); + + /* now remove part i from cubes which are redundant */ + index = 0; + foreach_set(F1, last, p1) { + if (! TESTP(p1, ACTIVE)) { + p = F_cube_table[index]; + + /* don't reduce a variable which is full + * (unless it is the output variable) + */ + if (var == cube.num_vars-1 || + ! setp_implies(cube.var_mask[var], p)) { + set_remove(p, i); + } + RESET(p, PRIME); + } + index++; + } + + free_cover(F1); + free_cover(D1); + FREE(F_cube_table); + } + } + } + + /* Check if any cubes disappeared */ + (void) sf_active(F); + for(var = 0; var < cube.num_vars; var++) { + if (cube.sparse[var]) { + foreach_active_set(F, last, p) { + if (setp_disjoint(p, cube.var_mask[var])) { + RESET(p, ACTIVE); + F->active_count--; + } + } + } + } + + if (F->count != F->active_count) { + F = sf_inactive(F); + } + return F; +} diff --git a/abc_with_bb_support/src/misc/espresso/sparse.h b/abc_with_bb_support/src/misc/espresso/sparse.h new file mode 100644 index 000000000..fcc33d3bf --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/sparse.h @@ -0,0 +1,135 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +#ifndef SPARSE_H +#define SPARSE_H + +/* + * sparse.h -- sparse matrix package header file + */ + +typedef struct sm_element_struct sm_element; +typedef struct sm_row_struct sm_row; +typedef struct sm_col_struct sm_col; +typedef struct sm_matrix_struct sm_matrix; + + +/* + * sparse matrix element + */ +struct sm_element_struct { + int row_num; /* row number of this element */ + int col_num; /* column number of this element */ + sm_element *next_row; /* next row in this column */ + sm_element *prev_row; /* previous row in this column */ + sm_element *next_col; /* next column in this row */ + sm_element *prev_col; /* previous column in this row */ + char *user_word; /* user-defined word */ +}; + + +/* + * row header + */ +struct sm_row_struct { + int row_num; /* the row number */ + int length; /* number of elements in this row */ + int flag; /* user-defined word */ + sm_element *first_col; /* first element in this row */ + sm_element *last_col; /* last element in this row */ + sm_row *next_row; /* next row (in sm_matrix linked list) */ + sm_row *prev_row; /* previous row (in sm_matrix linked list) */ + char *user_word; /* user-defined word */ +}; + + +/* + * column header + */ +struct sm_col_struct { + int col_num; /* the column number */ + int length; /* number of elements in this column */ + int flag; /* user-defined word */ + sm_element *first_row; /* first element in this column */ + sm_element *last_row; /* last element in this column */ + sm_col *next_col; /* next column (in sm_matrix linked list) */ + sm_col *prev_col; /* prev column (in sm_matrix linked list) */ + char *user_word; /* user-defined word */ +}; + + +/* + * A sparse matrix + */ +struct sm_matrix_struct { + sm_row **rows; /* pointer to row headers (by row #) */ + int rows_size; /* alloc'ed size of above array */ + sm_col **cols; /* pointer to column headers (by col #) */ + int cols_size; /* alloc'ed size of above array */ + sm_row *first_row; /* first row (linked list of all rows) */ + sm_row *last_row; /* last row (linked list of all rows) */ + int nrows; /* number of rows */ + sm_col *first_col; /* first column (linked list of columns) */ + sm_col *last_col; /* last column (linked list of columns) */ + int ncols; /* number of columns */ + char *user_word; /* user-defined word */ +}; + + +#define sm_get_col(A, colnum) \ + (((colnum) >= 0 && (colnum) < (A)->cols_size) ? \ + (A)->cols[colnum] : (sm_col *) 0) + +#define sm_get_row(A, rownum) \ + (((rownum) >= 0 && (rownum) < (A)->rows_size) ? \ + (A)->rows[rownum] : (sm_row *) 0) + +#define sm_foreach_row(A, prow) \ + for(prow = A->first_row; prow != 0; prow = prow->next_row) + +#define sm_foreach_col(A, pcol) \ + for(pcol = A->first_col; pcol != 0; pcol = pcol->next_col) + +#define sm_foreach_row_element(prow, p) \ + for(p = prow->first_col; p != 0; p = p->next_col) + +#define sm_foreach_col_element(pcol, p) \ + for(p = pcol->first_row; p != 0; p = p->next_row) + +#define sm_put(x, val) \ + (x->user_word = (char *) val) + +#define sm_get(type, x) \ + ((type) (x->user_word)) + +extern sm_matrix *sm_alloc(), *sm_alloc_size(), *sm_dup(); +extern void sm_free(), sm_delrow(), sm_delcol(), sm_resize(); +extern void sm_write(), sm_print(), sm_dump(), sm_cleanup(); +extern void sm_copy_row(), sm_copy_col(); +extern void sm_remove(), sm_remove_element(); +extern sm_element *sm_insert(), *sm_find(); +extern sm_row *sm_longest_row(); +extern sm_col *sm_longest_col(); +extern int sm_read(), sm_read_compressed(); + +extern sm_row *sm_row_alloc(), *sm_row_dup(), *sm_row_and(); +extern void sm_row_free(), sm_row_remove(), sm_row_print(); +extern sm_element *sm_row_insert(), *sm_row_find(); +extern int sm_row_contains(), sm_row_intersects(); +extern int sm_row_compare(), sm_row_hash(); + +extern sm_col *sm_col_alloc(), *sm_col_dup(), *sm_col_and(); +extern void sm_col_free(), sm_col_remove(), sm_col_print(); +extern sm_element *sm_col_insert(), *sm_col_find(); +extern int sm_col_contains(), sm_col_intersects(); +extern int sm_col_compare(), sm_col_hash(); + +extern int sm_row_dominance(), sm_col_dominance(), sm_block_partition(); + +#endif diff --git a/abc_with_bb_support/src/misc/espresso/sparse_int.h b/abc_with_bb_support/src/misc/espresso/sparse_int.h new file mode 100644 index 000000000..d671bd24d --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/sparse_int.h @@ -0,0 +1,121 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +//#include "port.h" +//#include "utility.h" +#include "sparse.h" + +#include "util_hack.h" // added + + + +/* + * sorted, double-linked list insertion + * + * type: object type + * + * first, last: fields (in header) to head and tail of the list + * count: field (in header) of length of the list + * + * next, prev: fields (in object) to link next and previous objects + * value: field (in object) which controls the order + * + * newval: value field for new object + * e: an object to use if insertion needed (set to actual value used) + */ + +#define sorted_insert(type, first, last, count, next, prev, value, newval, e) \ + if (last == 0) { \ + e->value = newval; \ + first = e; \ + last = e; \ + e->next = 0; \ + e->prev = 0; \ + count++; \ + } else if (last->value < newval) { \ + e->value = newval; \ + last->next = e; \ + e->prev = last; \ + last = e; \ + e->next = 0; \ + count++; \ + } else if (first->value > newval) { \ + e->value = newval; \ + first->prev = e; \ + e->next = first; \ + first = e; \ + e->prev = 0; \ + count++; \ + } else { \ + type *p; \ + for(p = first; p->value < newval; p = p->next) \ + ; \ + if (p->value > newval) { \ + e->value = newval; \ + p = p->prev; \ + p->next->prev = e; \ + e->next = p->next; \ + p->next = e; \ + e->prev = p; \ + count++; \ + } else { \ + e = p; \ + } \ + } + + +/* + * double linked-list deletion + */ +#define dll_unlink(p, first, last, next, prev, count) { \ + if (p->prev == 0) { \ + first = p->next; \ + } else { \ + p->prev->next = p->next; \ + } \ + if (p->next == 0) { \ + last = p->prev; \ + } else { \ + p->next->prev = p->prev; \ + } \ + count--; \ +} + + +#ifdef FAST_AND_LOOSE +extern sm_element *sm_element_freelist; +extern sm_row *sm_row_freelist; +extern sm_col *sm_col_freelist; + +#define sm_element_alloc(newobj) \ + if (sm_element_freelist == NIL(sm_element)) { \ + newobj = ALLOC(sm_element, 1); \ + } else { \ + newobj = sm_element_freelist; \ + sm_element_freelist = sm_element_freelist->next_col; \ + } \ + newobj->user_word = NIL(char); \ + +#define sm_element_free(e) \ + (e->next_col = sm_element_freelist, sm_element_freelist = e) + +#else + +#define sm_element_alloc(newobj) \ + newobj = ALLOC(sm_element, 1); \ + newobj->user_word = NIL(char); +#define sm_element_free(e) \ + FREE(e) +#endif + + +extern void sm_row_remove_element(); +extern void sm_col_remove_element(); + +/* LINTLIBRARY */ diff --git a/abc_with_bb_support/src/misc/espresso/unate.c b/abc_with_bb_support/src/misc/espresso/unate.c new file mode 100644 index 000000000..2ebb7d218 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/unate.c @@ -0,0 +1,441 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +/* + * unate.c -- routines for dealing with unate functions + */ + +#include "espresso.h" + +static pset_family abs_covered(); +static pset_family abs_covered_many(); +static int abs_select_restricted(); + +pcover map_cover_to_unate(T) +pcube *T; +{ + register unsigned int word_test, word_set, bit_test, bit_set; + register pcube p, pA; + pset_family A; + pcube *T1; + int ncol, i; + + A = sf_new(CUBELISTSIZE(T), cdata.vars_unate); + A->count = CUBELISTSIZE(T); + foreachi_set(A, i, p) { + (void) set_clear(p, A->sf_size); + } + ncol = 0; + + for(i = 0; i < cube.size; i++) { + if (cdata.part_zeros[i] > 0) { + assert(ncol <= cdata.vars_unate); + + /* Copy a column from T to A */ + word_test = WHICH_WORD(i); + bit_test = 1 << WHICH_BIT(i); + word_set = WHICH_WORD(ncol); + bit_set = 1 << WHICH_BIT(ncol); + + pA = A->data; + for(T1 = T+2; (p = *T1++) != 0; ) { + if ((p[word_test] & bit_test) == 0) { + pA[word_set] |= bit_set; + } + pA += A->wsize; + } + + ncol++; + } + } + + return A; +} + +pcover map_unate_to_cover(A) +pset_family A; +{ + register int i, ncol, lp; + register pcube p, pB; + int var, nunate, *unate; + pcube last; + pset_family B; + + B = sf_new(A->count, cube.size); + B->count = A->count; + + /* Find the unate variables */ + unate = ALLOC(int, cube.num_vars); + nunate = 0; + for(var = 0; var < cube.num_vars; var++) { + if (cdata.is_unate[var]) { + unate[nunate++] = var; + } + } + + /* Loop for each set of A */ + pB = B->data; + foreach_set(A, last, p) { + + /* Initialize this set of B */ + INLINEset_fill(pB, cube.size); + + /* Now loop for the unate variables; if the part is in A, + * then this variable of B should be a single 1 in the unate + * part. + */ + for(ncol = 0; ncol < nunate; ncol++) { + if (is_in_set(p, ncol)) { + lp = cube.last_part[unate[ncol]]; + for(i = cube.first_part[unate[ncol]]; i <= lp; i++) { + if (cdata.part_zeros[i] == 0) { + set_remove(pB, i); + } + } + } + } + pB += B->wsize; + } + + FREE(unate); + return B; +} + +/* + * unate_compl + */ + +pset_family unate_compl(A) +pset_family A; +{ + register pset p, last; + + /* Make sure A is single-cube containment minimal */ +/* A = sf_rev_contain(A);*/ + + foreach_set(A, last, p) { + PUTSIZE(p, set_ord(p)); + } + + /* Recursively find the complement */ + A = unate_complement(A); + + /* Now, we can guarantee a minimal result by containing the result */ + A = sf_rev_contain(A); + return A; +} + + +/* + * Assume SIZE(p) records the size of each set + */ +pset_family unate_complement(A) +pset_family A; /* disposes of A */ +{ + pset_family Abar; + register pset p, p1, restrict; + register int i; + int max_i, min_set_ord, j; + + /* Check for no sets in the matrix -- complement is the universe */ + if (A->count == 0) { + sf_free(A); + Abar = sf_new(1, A->sf_size); + (void) set_clear(GETSET(Abar, Abar->count++), A->sf_size); + + /* Check for a single set in the maxtrix -- compute de Morgan complement */ + } else if (A->count == 1) { + p = A->data; + Abar = sf_new(A->sf_size, A->sf_size); + for(i = 0; i < A->sf_size; i++) { + if (is_in_set(p, i)) { + p1 = set_clear(GETSET(Abar, Abar->count++), A->sf_size); + set_insert(p1, i); + } + } + sf_free(A); + + } else { + + /* Select splitting variable as the variable which belongs to a set + * of the smallest size, and which has greatest column count + */ + restrict = set_new(A->sf_size); + min_set_ord = A->sf_size + 1; + foreachi_set(A, i, p) { + if (SIZE(p) < min_set_ord) { + set_copy(restrict, p); + min_set_ord = SIZE(p); + } else if (SIZE(p) == min_set_ord) { + set_or(restrict, restrict, p); + } + } + + /* Check for no data (shouldn't happen ?) */ + if (min_set_ord == 0) { + A->count = 0; + Abar = A; + + /* Check for "essential" columns */ + } else if (min_set_ord == 1) { + Abar = unate_complement(abs_covered_many(A, restrict)); + sf_free(A); + foreachi_set(Abar, i, p) { + set_or(p, p, restrict); + } + + /* else, recur as usual */ + } else { + max_i = abs_select_restricted(A, restrict); + + /* Select those rows of A which are not covered by max_i, + * recursively find all minimal covers of these rows, and + * then add back in max_i + */ + Abar = unate_complement(abs_covered(A, max_i)); + foreachi_set(Abar, i, p) { + set_insert(p, max_i); + } + + /* Now recur on A with all zero's on column max_i */ + foreachi_set(A, i, p) { + if (is_in_set(p, max_i)) { + set_remove(p, max_i); + j = SIZE(p) - 1; + PUTSIZE(p, j); + } + } + + Abar = sf_append(Abar, unate_complement(A)); + } + set_free(restrict); + } + + return Abar; +} + +pset_family exact_minimum_cover(T) +IN pset_family T; +{ + register pset p, last, p1; + register int i, n; + int lev, lvl; + pset nlast; + pset_family temp; + long start = ptime(); + struct { + pset_family sf; + int level; + } stack[32]; /* 32 suffices for 2 ** 32 cubes ! */ + + if (T->count <= 0) + return sf_new(1, T->sf_size); + for(n = T->count, lev = 0; n != 0; n >>= 1, lev++) ; + + /* A simple heuristic ordering */ + T = lex_sort(sf_save(T)); + + /* Push a full set on the stack to get things started */ + n = 1; + stack[0].sf = sf_new(1, T->sf_size); + stack[0].level = lev; + set_fill(GETSET(stack[0].sf, stack[0].sf->count++), T->sf_size); + + nlast = GETSET(T, T->count - 1); + foreach_set(T, last, p) { + + /* "unstack" the set into a family */ + temp = sf_new(set_ord(p), T->sf_size); + for(i = 0; i < T->sf_size; i++) + if (is_in_set(p, i)) { + p1 = set_fill(GETSET(temp, temp->count++), T->sf_size); + set_remove(p1, i); + } + stack[n].sf = temp; + stack[n++].level = lev; + + /* Pop the stack and perform (leveled) intersections */ + while (n > 1 && (stack[n-1].level==stack[n-2].level || p == nlast)) { + temp = unate_intersect(stack[n-1].sf, stack[n-2].sf, FALSE); + lvl = MIN(stack[n-1].level, stack[n-2].level) - 1; + if (debug & MINCOV && lvl < 10) { + printf("# EXACT_MINCOV[%d]: %4d = %4d x %4d, time = %s\n", + lvl, temp->count, stack[n-1].sf->count, + stack[n-2].sf->count, print_time(ptime() - start)); + (void) fflush(stdout); + } + sf_free(stack[n-2].sf); + sf_free(stack[n-1].sf); + stack[n-2].sf = temp; + stack[n-2].level = lvl; + n--; + } + } + + temp = stack[0].sf; + p1 = set_fill(set_new(T->sf_size), T->sf_size); + foreach_set(temp, last, p) + INLINEset_diff(p, p1, p); + set_free(p1); + if (debug & MINCOV1) { + printf("MINCOV: family of all minimal coverings is\n"); + sf_print(temp); + } + sf_free(T); /* this is the copy of T we made ... */ + return temp; +} + +/* + * unate_intersect -- intersect two unate covers + * + * If largest_only is TRUE, then only the largest cube(s) are returned + */ + +#define MAGIC 500 /* save 500 cubes before containment */ + +pset_family unate_intersect(A, B, largest_only) +pset_family A, B; +bool largest_only; +{ + register pset pi, pj, lasti, lastj, pt; + pset_family T, Tsave; + bool save; + int maxord, ord; + + /* How large should each temporary result cover be ? */ + T = sf_new(MAGIC, A->sf_size); + Tsave = NULL; + maxord = 0; + pt = T->data; + + /* Form pairwise intersection of each set of A with each cube of B */ + foreach_set(A, lasti, pi) { + + foreach_set(B, lastj, pj) { + + save = set_andp(pt, pi, pj); + + /* Check if we want the largest only */ + if (save && largest_only) { + if ((ord = set_ord(pt)) > maxord) { + /* discard Tsave and T */ + if (Tsave != NULL) { + sf_free(Tsave); + Tsave = NULL; + } + pt = T->data; + T->count = 0; + /* Re-create pt (which was just thrown away) */ + (void) set_and(pt, pi, pj); + maxord = ord; + } else if (ord < maxord) { + save = FALSE; + } + } + + if (save) { + if (++T->count >= T->capacity) { + T = sf_contain(T); + Tsave = (Tsave == NULL) ? T : sf_union(Tsave, T); + T = sf_new(MAGIC, A->sf_size); + pt = T->data; + } else { + pt += T->wsize; + } + } + } + } + + + /* Contain the final result and merge it into Tsave */ + T = sf_contain(T); + Tsave = (Tsave == NULL) ? T : sf_union(Tsave, T); + + return Tsave; +} + +/* + * abs_covered -- after selecting a new column for the selected set, + * create a new matrix which is only those rows which are still uncovered + */ +static pset_family +abs_covered(A, pick) +pset_family A; +register int pick; +{ + register pset last, p, pdest; + register pset_family Aprime; + + Aprime = sf_new(A->count, A->sf_size); + pdest = Aprime->data; + foreach_set(A, last, p) + if (! is_in_set(p, pick)) { + INLINEset_copy(pdest, p); + Aprime->count++; + pdest += Aprime->wsize; + } + return Aprime; +} + + +/* + * abs_covered_many -- after selecting many columns for ther selected set, + * create a new matrix which is only those rows which are still uncovered + */ +static pset_family +abs_covered_many(A, pick_set) +pset_family A; +register pset pick_set; +{ + register pset last, p, pdest; + register pset_family Aprime; + + Aprime = sf_new(A->count, A->sf_size); + pdest = Aprime->data; + foreach_set(A, last, p) + if (setp_disjoint(p, pick_set)) { + INLINEset_copy(pdest, p); + Aprime->count++; + pdest += Aprime->wsize; + } + return Aprime; +} + + +/* + * abs_select_restricted -- select the column of maximum column count which + * also belongs to the set "restrict"; weight each column of a set as + * 1 / (set_ord(p) - 1). + */ +static int +abs_select_restricted(A, restrict) +pset_family A; +pset restrict; +{ + register int i, best_var, best_count, *count; + + /* Sum the elements in these columns */ + count = sf_count_restricted(A, restrict); + + /* Find which variable has maximum weight */ + best_var = -1; + best_count = 0; + for(i = 0; i < A->sf_size; i++) { + if (count[i] > best_count) { + best_var = i; + best_count = count[i]; + } + } + FREE(count); + + if (best_var == -1) + fatal("abs_select_restricted: should not have best_var == -1"); + + return best_var; +} diff --git a/abc_with_bb_support/src/misc/espresso/util_old.h b/abc_with_bb_support/src/misc/espresso/util_old.h new file mode 100644 index 000000000..5fb44cf32 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/util_old.h @@ -0,0 +1,301 @@ +/* + * Revision Control Information + * + * $Source: /vol/opua/opua2/sis/sis-1.2/common/src/sis/util/RCS/util.h,v $ + * $Author: sis $ + * $Revision: 1.9 $ + * $Date: 1993/06/07 21:04:07 $ + * + */ +#ifndef UTIL_H +#define UTIL_H + +#if defined(_IBMR2) +#ifndef _POSIX_SOURCE +#define _POSIX_SOURCE /* Argh! IBM strikes again */ +#endif +#ifndef _ALL_SOURCE +#define _ALL_SOURCE /* Argh! IBM strikes again */ +#endif +#ifndef _ANSI_C_SOURCE +#define _ANSI_C_SOURCE /* Argh! IBM strikes again */ +#endif +#endif + +#if defined(__STDC__) || defined(sprite) || defined(_IBMR2) || defined(__osf__) +#include +#endif + +#if defined(_IBMR2) && !defined(__STDC__) +#define _BSD +#endif + +#include "ansi.h" /* since some files don't include sis.h */ + +/* This was taken out and defined at compile time in the SIS Makefile + that uses the OctTools. When the OctTools are used, USE_MM is defined, + because the OctTools contain libmm.a. Otherwise, USE_MM is not defined, + since the mm package is not distributed with SIS, only with Oct. */ + +/* #define USE_MM */ /* choose libmm.a as the memory allocator */ + +#define NIL(type) ((type *) 0) + +#ifdef USE_MM +/* + * assumes the memory manager is libmm.a + * - allows malloc(0) or realloc(obj, 0) + * - catches out of memory (and calls MMout_of_memory()) + * - catch free(0) and realloc(0, size) in the macros + */ +#define ALLOC(type, num) \ + ((type *) malloc(sizeof(type) * (num))) +#define REALLOC(type, obj, num) \ + (obj) ? ((type *) realloc((char *) obj, sizeof(type) * (num))) : \ + ((type *) malloc(sizeof(type) * (num))) +#define FREE(obj) \ + ((obj) ? (free((char *) (obj)), (obj) = 0) : 0) +#else +/* + * enforce strict semantics on the memory allocator + * - when in doubt, delete the '#define USE_MM' above + */ +#define ALLOC(type, num) \ + ((type *) MMalloc((long) sizeof(type) * (long) (num))) +#define REALLOC(type, obj, num) \ + ((type *) MMrealloc((char *) (obj), (long) sizeof(type) * (long) (num))) +#define FREE(obj) \ + ((obj) ? (free((void *) (obj)), (obj) = 0) : 0) +#endif + + +/* Ultrix (and SABER) have 'fixed' certain functions which used to be int */ +#if defined(ultrix) || defined(SABER) || defined(aiws) || defined(__hpux) || defined(__STDC__) || defined(apollo) +#define VOID_HACK void +#else +#define VOID_HACK int +#endif + + +/* No machines seem to have much of a problem with these */ +#include +#include + + +/* Some machines fail to define some functions in stdio.h */ +#if !defined(__STDC__) && !defined(sprite) && !defined(_IBMR2) && !defined(__osf__) +extern FILE *popen(), *tmpfile(); +extern int pclose(); +#ifndef clearerr /* is a macro on many machines, but not all */ +extern VOID_HACK clearerr(); +#endif +#ifndef rewind +extern VOID_HACK rewind(); +#endif +#endif + +#ifndef PORT_H +#include +#include +#if defined(ultrix) +#if defined(_SIZE_T_) +#define ultrix4 +#else +#if defined(SIGLOST) +#define ultrix3 +#else +#define ultrix2 +#endif +#endif +#endif +#endif + +/* most machines don't give us a header file for these */ +#if defined(__STDC__) || defined(sprite) || defined(_IBMR2) || defined(__osf__) || defined(sunos4) || defined(__hpux) +#include +#if defined(__hpux) +#include /* For perror() defininition */ +#endif /* __hpux */ +#else +extern VOID_HACK abort(), free(), exit(), perror(); +extern char *getenv(); +#ifdef ultrix4 +extern void *malloc(), *realloc(), *calloc(); +#else +extern char *malloc(), *realloc(), *calloc(); +#endif +#if defined(aiws) +extern int sprintf(); +#else +#ifndef _IBMR2 +extern char *sprintf(); +#endif +#endif +extern int system(); +extern double atof(); +#endif + +#ifndef PORT_H +#if defined(ultrix3) || defined(sunos4) || defined(_IBMR2) || defined(__STDC__) +#define SIGNAL_FN void +#else +/* sequent, ultrix2, 4.3BSD (vax, hp), sunos3 */ +#define SIGNAL_FN int +#endif +#endif + +/* some call it strings.h, some call it string.h; others, also have memory.h */ +#if defined(__STDC__) || defined(sprite) +#include +#else +#if defined(ultrix4) || defined(__hpux) +#include +#else +#if defined(_IBMR2) || defined(__osf__) +#include +#include +#else +/* ANSI C string.h -- 1/11/88 Draft Standard */ +/* ugly, awful hack */ +#ifndef PORT_H +extern char *strcpy(), *strncpy(), *strcat(), *strncat(), *strerror(); +extern char *strpbrk(), *strtok(), *strchr(), *strrchr(), *strstr(); +extern int strcoll(), strxfrm(), strncmp(), strlen(), strspn(), strcspn(); +extern char *memmove(), *memccpy(), *memchr(), *memcpy(), *memset(); +extern int memcmp(), strcmp(); +#endif +#endif +#endif +#endif + +/* a few extras */ +#if defined(__hpux) +#define random() lrand48() +#define srandom(a) srand48(a) +#define bzero(a,b) memset(a, 0, b) +#else +#if !defined(__osf__) && !defined(linux) +/* these are defined as macros in stdlib.h */ +extern VOID_HACK srandom(); +extern long random(); +#endif +#endif + +/* code from sis-1.3 commented out below +#if defined(__STDC__) || defined(sprite) +#include +#else +#ifndef NDEBUG +#define assert(ex) {\ + if (! (ex)) {\ + (void) fprintf(stderr,\ + "Assertion failed: file %s, line %d\n\"%s\"\n",\ + __FILE__, __LINE__, "ex");\ + (void) fflush(stdout);\ + abort();\ + }\ +} +#else +#define assert(ex) {ex;} +#endif +#endif +*/ + + /* Sunil 5/3/97: + sis-1.4: dont let the assert call go to the OS, since + much of the code in SIS has actual computation done in + the assert function. %$#$@@#! The OS version of assert + will do nothing if NDEBUG is set. We cant let that happen... + */ +# ifdef NDEBUG +# define assert(ex) {ex;} +# else +# define assert(ex) {\ + if (! (ex)) {\ + (void) fprintf(stderr,\ + "Assertion failed: file %s, line %d\n\"%s\"\n",\ + __FILE__, __LINE__, "ex");\ + (void) fflush(stdout);\ + abort();\ + }\ +} +# endif + + +#define fail(why) {\ + (void) fprintf(stderr, "Fatal error: file %s, line %d\n%s\n",\ + __FILE__, __LINE__, why);\ + (void) fflush(stdout);\ + abort();\ +} + + +#ifdef lint +#undef putc /* correct lint '_flsbuf' bug */ +#undef ALLOC /* allow for lint -h flag */ +#undef REALLOC +#define ALLOC(type, num) (((type *) 0) + (num)) +#define REALLOC(type, obj, num) ((obj) + (num)) +#endif + +/* +#if !defined(__osf__) +#define MAXPATHLEN 1024 +#endif +*/ + +/* These arguably do NOT belong in util.h */ +#ifndef ABS +#define ABS(a) ((a) < 0 ? -(a) : (a)) +#endif +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + + +#ifndef USE_MM +EXTERN void MMout_of_memory ARGS((long)); +EXTERN char *MMalloc ARGS((long)); +EXTERN char *MMrealloc ARGS((char *, long)); +EXTERN void MMfree ARGS((char *)); +#endif + +EXTERN void util_print_cpu_stats ARGS((FILE *)); +EXTERN long util_cpu_time ARGS((void)); +EXTERN void util_getopt_reset ARGS((void)); +EXTERN int util_getopt ARGS((int, char **, char *)); +EXTERN char *util_path_search ARGS((char *)); +EXTERN char *util_file_search ARGS((char *, char *, char *)); +EXTERN int util_pipefork ARGS((char **, FILE **, FILE **, int *)); +EXTERN char *util_print_time ARGS((long)); +EXTERN int util_save_image ARGS((char *, char *)); +EXTERN char *util_strsav ARGS((char *)); +EXTERN int util_do_nothing ARGS((void)); +EXTERN char *util_tilde_expand ARGS((char *)); +EXTERN char *util_tempnam ARGS((char *, char *)); +EXTERN FILE *util_tmpfile ARGS((void)); +EXTERN long getSoftDataLimit(); + +#define ptime() util_cpu_time() +#define print_time(t) util_print_time(t) + +/* util_getopt() global variables (ack !) */ +extern int util_optind; +extern char *util_optarg; + +#include +#ifndef HUGE_VAL +#ifndef HUGE +#define HUGE 8.9884656743115790e+307 +#endif +#define HUGE_VAL HUGE +#endif +#ifndef MAXINT +#define MAXINT (1 << 30) +#endif + +#include +#endif diff --git a/abc_with_bb_support/src/misc/espresso/verify.c b/abc_with_bb_support/src/misc/espresso/verify.c new file mode 100644 index 000000000..f92319aa1 --- /dev/null +++ b/abc_with_bb_support/src/misc/espresso/verify.c @@ -0,0 +1,193 @@ +/* + * Revision Control Information + * + * $Source$ + * $Author$ + * $Revision$ + * $Date$ + * + */ +/* + */ + +#include "espresso.h" + +/* + * verify -- check that all minterms of F are contained in (Fold u Dold) + * and that all minterms of Fold are contained in (F u Dold). + */ +bool verify(F, Fold, Dold) +pcover F, Fold, Dold; +{ + pcube p, last, *FD; + bool verify_error = FALSE; + + /* Make sure the function didn't grow too large */ + FD = cube2list(Fold, Dold); + foreach_set(F, last, p) + if (! cube_is_covered(FD, p)) { + printf("some minterm in F is not covered by Fold u Dold\n"); + verify_error = TRUE; + if (verbose_debug) printf("%s\n", pc1(p)); else break; + } + free_cubelist(FD); + + /* Make sure minimized function covers the original function */ + FD = cube2list(F, Dold); + foreach_set(Fold, last, p) + if (! cube_is_covered(FD, p)) { + printf("some minterm in Fold is not covered by F u Dold\n"); + verify_error = TRUE; + if (verbose_debug) printf("%s\n", pc1(p)); else break; + } + free_cubelist(FD); + + return verify_error; +} + + + +/* + * PLA_verify -- verify that two PLA's are identical + * + * If names are given, row and column permutations are done to make + * the comparison meaningful. + * + */ +bool PLA_verify(PLA1, PLA2) +pPLA PLA1, PLA2; +{ + /* Check if both have names given; if so, attempt to permute to + * match the names + */ + if (PLA1->label != NULL && PLA1->label[0] != NULL && + PLA2->label != NULL && PLA2->label[0] != NULL) { + PLA_permute(PLA1, PLA2); + } else { + (void) fprintf(stderr, "Warning: cannot permute columns without names\n"); + return TRUE; + } + + if (PLA1->F->sf_size != PLA2->F->sf_size) { + (void) fprintf(stderr, "PLA_verify: PLA's are not the same size\n"); + return TRUE; + } + + return verify(PLA2->F, PLA1->F, PLA1->D); +} + + + +/* + * Permute the columns of PLA1 so that they match the order of PLA2 + * Discard any columns of PLA1 which are not in PLA2 + * Association is strictly by the names of the columns of the cover. + */ +PLA_permute(PLA1, PLA2) +pPLA PLA1, PLA2; +{ + register int i, j, *permute, npermute; + register char *labi; + char **label; + + /* determine which columns of PLA1 to save, and place these in the list + * "permute"; the order in this list is the final output order + */ + npermute = 0; + permute = ALLOC(int, PLA2->F->sf_size); + for(i = 0; i < PLA2->F->sf_size; i++) { + labi = PLA2->label[i]; + for(j = 0; j < PLA1->F->sf_size; j++) { + if (strcmp(labi, PLA1->label[j]) == 0) { + permute[npermute++] = j; + break; + } + } + } + + /* permute columns */ + if (PLA1->F != NULL) { + PLA1->F = sf_permute(PLA1->F, permute, npermute); + } + if (PLA1->R != NULL) { + PLA1->R = sf_permute(PLA1->R, permute, npermute); + } + if (PLA1->D != NULL) { + PLA1->D = sf_permute(PLA1->D, permute, npermute); + } + + /* permute the labels */ + label = ALLOC(char *, cube.size); + for(i = 0; i < npermute; i++) { + label[i] = PLA1->label[permute[i]]; + } + for(i = npermute; i < cube.size; i++) { + label[i] = NULL; + } + FREE(PLA1->label); + PLA1->label = label; + + FREE(permute); +} + + + +/* + * check_consistency -- test that the ON-set, OFF-set and DC-set form + * a partition of the boolean space. + */ +bool check_consistency(PLA) +pPLA PLA; +{ + bool verify_error = FALSE; + pcover T; + + T = cv_intersect(PLA->F, PLA->D); + if (T->count == 0) + printf("ON-SET and DC-SET are disjoint\n"); + else { + printf("Some minterm(s) belong to both the ON-SET and DC-SET !\n"); + if (verbose_debug) + cprint(T); + verify_error = TRUE; + } + (void) fflush(stdout); + free_cover(T); + + T = cv_intersect(PLA->F, PLA->R); + if (T->count == 0) + printf("ON-SET and OFF-SET are disjoint\n"); + else { + printf("Some minterm(s) belong to both the ON-SET and OFF-SET !\n"); + if (verbose_debug) + cprint(T); + verify_error = TRUE; + } + (void) fflush(stdout); + free_cover(T); + + T = cv_intersect(PLA->D, PLA->R); + if (T->count == 0) + printf("DC-SET and OFF-SET are disjoint\n"); + else { + printf("Some minterm(s) belong to both the OFF-SET and DC-SET !\n"); + if (verbose_debug) + cprint(T); + verify_error = TRUE; + } + (void) fflush(stdout); + free_cover(T); + + if (tautology(cube3list(PLA->F, PLA->D, PLA->R))) + printf("Union of ON-SET, OFF-SET and DC-SET is the universe\n"); + else { + T = complement(cube3list(PLA->F, PLA->D, PLA->R)); + printf("There are minterms left unspecified !\n"); + if (verbose_debug) + cprint(T); + verify_error = TRUE; + free_cover(T); + } + (void) fflush(stdout); + return verify_error; +} diff --git a/abc_with_bb_support/src/misc/extra/extra.h b/abc_with_bb_support/src/misc/extra/extra.h new file mode 100644 index 000000000..b1e061e45 --- /dev/null +++ b/abc_with_bb_support/src/misc/extra/extra.h @@ -0,0 +1,626 @@ +/**CFile**************************************************************** + + FileName [extra.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [extra] + + Synopsis [Various reusable software utilities.] + + Description [This library contains a number of operators and + traversal routines developed to extend the functionality of + CUDD v.2.3.x, by Fabio Somenzi (http://vlsi.colorado.edu/~fabio/) + To compile your code with the library, #include "extra.h" + in your source files and link your project to CUDD and this + library. Use the library at your own risk and with caution. + Note that debugging of some operators still continues.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: extra.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __EXTRA_H__ +#define __EXTRA_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +#define inline __inline // compatible with MS VS 6.0 +#endif + +/*---------------------------------------------------------------------------*/ +/* Nested includes */ +/*---------------------------------------------------------------------------*/ + +// this include should be the first one in the list +// it is used to catch memory leaks on Windows +#include "leaks.h" + +#include +#include +#include +#include +#include +#include "st.h" +#include "cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +#ifdef WIN32 +typedef unsigned __int64 uint64; +#else +typedef unsigned long long uint64; +#endif + +/* constants of the manager */ +#define b0 Cudd_Not((dd)->one) +#define b1 (dd)->one +#define z0 (dd)->zero +#define z1 (dd)->one +#define a0 (dd)->zero +#define a1 (dd)->one + +// hash key macros +#define hashKey1(a,TSIZE) \ +((unsigned)(a) % TSIZE) + +#define hashKey2(a,b,TSIZE) \ +(((unsigned)(a) + (unsigned)(b) * DD_P1) % TSIZE) + +#define hashKey3(a,b,c,TSIZE) \ +(((((unsigned)(a) + (unsigned)(b)) * DD_P1 + (unsigned)(c)) * DD_P2 ) % TSIZE) + +#define hashKey4(a,b,c,d,TSIZE) \ +((((((unsigned)(a) + (unsigned)(b)) * DD_P1 + (unsigned)(c)) * DD_P2 + \ + (unsigned)(d)) * DD_P3) % TSIZE) + +#define hashKey5(a,b,c,d,e,TSIZE) \ +(((((((unsigned)(a) + (unsigned)(b)) * DD_P1 + (unsigned)(c)) * DD_P2 + \ + (unsigned)(d)) * DD_P3 + (unsigned)(e)) * DD_P1) % TSIZE) + +#ifndef PRT +#define PRT(a,t) printf("%s = ", (a)); printf("%6.2f sec\n", (float)(t)/(float)(CLOCKS_PER_SEC)) +#define PRTn(a,t) printf("%s = ", (a)); printf("%6.2f sec ", (float)(t)/(float)(CLOCKS_PER_SEC)) +#endif + +#ifndef PRTP +#define PRTP(a,t,T) printf("%s = ", (a)); printf("%6.2f sec (%6.2f %%)\n", (float)(t)/(float)(CLOCKS_PER_SEC), (T)? 100.0*(t)/(T) : 0.0) +#endif + +/*===========================================================================*/ +/* Various Utilities */ +/*===========================================================================*/ + +/*=== extraBddAuto.c ========================================================*/ + +extern DdNode * Extra_bddSpaceFromFunctionFast( DdManager * dd, DdNode * bFunc ); +extern DdNode * Extra_bddSpaceFromFunction( DdManager * dd, DdNode * bF, DdNode * bG ); +extern DdNode * extraBddSpaceFromFunction( DdManager * dd, DdNode * bF, DdNode * bG ); +extern DdNode * Extra_bddSpaceFromFunctionPos( DdManager * dd, DdNode * bFunc ); +extern DdNode * extraBddSpaceFromFunctionPos( DdManager * dd, DdNode * bFunc ); +extern DdNode * Extra_bddSpaceFromFunctionNeg( DdManager * dd, DdNode * bFunc ); +extern DdNode * extraBddSpaceFromFunctionNeg( DdManager * dd, DdNode * bFunc ); + +extern DdNode * Extra_bddSpaceCanonVars( DdManager * dd, DdNode * bSpace ); +extern DdNode * extraBddSpaceCanonVars( DdManager * dd, DdNode * bSpace ); + +extern DdNode * Extra_bddSpaceEquations( DdManager * dd, DdNode * bSpace ); +extern DdNode * Extra_bddSpaceEquationsNeg( DdManager * dd, DdNode * bSpace ); +extern DdNode * extraBddSpaceEquationsNeg( DdManager * dd, DdNode * bSpace ); +extern DdNode * Extra_bddSpaceEquationsPos( DdManager * dd, DdNode * bSpace ); +extern DdNode * extraBddSpaceEquationsPos( DdManager * dd, DdNode * bSpace ); + +extern DdNode * Extra_bddSpaceFromMatrixPos( DdManager * dd, DdNode * zA ); +extern DdNode * extraBddSpaceFromMatrixPos( DdManager * dd, DdNode * zA ); +extern DdNode * Extra_bddSpaceFromMatrixNeg( DdManager * dd, DdNode * zA ); +extern DdNode * extraBddSpaceFromMatrixNeg( DdManager * dd, DdNode * zA ); + +extern DdNode * Extra_bddSpaceReduce( DdManager * dd, DdNode * bFunc, DdNode * bCanonVars ); +extern DdNode ** Extra_bddSpaceExorGates( DdManager * dd, DdNode * bFuncRed, DdNode * zEquations ); + +/*=== extraBddCas.c =============================================================*/ + +/* performs the binary encoding of the set of function using the given vars */ +extern DdNode * Extra_bddEncodingBinary( DdManager * dd, DdNode ** pbFuncs, int nFuncs, DdNode ** pbVars, int nVars ); +/* solves the column encoding problem using a sophisticated method */ +extern DdNode * Extra_bddEncodingNonStrict( DdManager * dd, DdNode ** pbColumns, int nColumns, DdNode * bVarsCol, DdNode ** pCVars, int nMulti, int * pSimple ); +/* collects the nodes under the cut and, for each node, computes the sum of paths leading to it from the root */ +extern st_table * Extra_bddNodePathsUnderCut( DdManager * dd, DdNode * bFunc, int CutLevel ); +/* collects the nodes under the cut starting from the given set of ADD nodes */ +extern int Extra_bddNodePathsUnderCutArray( DdManager * dd, DdNode ** paNodes, DdNode ** pbCubes, int nNodes, DdNode ** paNodesRes, DdNode ** pbCubesRes, int CutLevel ); +/* find the profile of a DD (the number of edges crossing each level) */ +extern int Extra_ProfileWidth( DdManager * dd, DdNode * F, int * Profile, int CutLevel ); + +/*=== extraBddMisc.c ========================================================*/ + +extern DdNode * Extra_TransferPermute( DdManager * ddSource, DdManager * ddDestination, DdNode * f, int * Permute ); +extern DdNode * Extra_TransferLevelByLevel( DdManager * ddSource, DdManager * ddDestination, DdNode * f ); +extern DdNode * Extra_bddRemapUp( DdManager * dd, DdNode * bF ); +extern DdNode * Extra_bddMove( DdManager * dd, DdNode * bF, int fShiftUp ); +extern DdNode * extraBddMove( DdManager * dd, DdNode * bF, DdNode * bFlag ); +extern void Extra_StopManager( DdManager * dd ); +extern void Extra_bddPrint( DdManager * dd, DdNode * F ); +extern void extraDecomposeCover( DdManager* dd, DdNode* zC, DdNode** zC0, DdNode** zC1, DdNode** zC2 ); +extern int Extra_bddSuppSize( DdManager * dd, DdNode * bSupp ); +extern int Extra_bddSuppContainVar( DdManager * dd, DdNode * bS, DdNode * bVar ); +extern int Extra_bddSuppOverlapping( DdManager * dd, DdNode * S1, DdNode * S2 ); +extern int Extra_bddSuppDifferentVars( DdManager * dd, DdNode * S1, DdNode * S2, int DiffMax ); +extern int Extra_bddSuppCheckContainment( DdManager * dd, DdNode * bL, DdNode * bH, DdNode ** bLarge, DdNode ** bSmall ); +extern int * Extra_SupportArray( DdManager * dd, DdNode * F, int * support ); +extern int * Extra_VectorSupportArray( DdManager * dd, DdNode ** F, int n, int * support ); +extern DdNode * Extra_bddFindOneCube( DdManager * dd, DdNode * bF ); +extern DdNode * Extra_bddGetOneCube( DdManager * dd, DdNode * bFunc ); +extern DdNode * Extra_bddComputeRangeCube( DdManager * dd, int iStart, int iStop ); +extern DdNode * Extra_bddBitsToCube( DdManager * dd, int Code, int CodeWidth, DdNode ** pbVars, int fMsbFirst ); +extern DdNode * Extra_bddSupportNegativeCube( DdManager * dd, DdNode * f ); +extern int Extra_bddIsVar( DdNode * bFunc ); +extern DdNode * Extra_bddCreateAnd( DdManager * dd, int nVars ); +extern DdNode * Extra_bddCreateOr( DdManager * dd, int nVars ); +extern DdNode * Extra_bddCreateExor( DdManager * dd, int nVars ); +extern DdNode * Extra_zddPrimes( DdManager * dd, DdNode * F ); +extern void Extra_bddPermuteArray( DdManager * dd, DdNode ** bNodesIn, DdNode ** bNodesOut, int nNodes, int *permut ); + +/*=== extraBddKmap.c ================================================================*/ + +/* displays the Karnaugh Map of a function */ +extern void Extra_PrintKMap( FILE * pFile, DdManager * dd, DdNode * OnSet, DdNode * OffSet, int nVars, DdNode ** XVars, int fSuppType, char ** pVarNames ); +/* displays the Karnaugh Map of a relation */ +extern void Extra_PrintKMapRelation( FILE * pFile, DdManager * dd, DdNode * OnSet, DdNode * OffSet, int nXVars, int nYVars, DdNode ** XVars, DdNode ** YVars ); + +/*=== extraBddSymm.c =================================================================*/ + +typedef struct Extra_SymmInfo_t_ Extra_SymmInfo_t; +struct Extra_SymmInfo_t_ { + int nVars; // the number of variables in the support + int nVarsMax; // the number of variables in the DD manager + int nSymms; // the number of pair-wise symmetries + int nNodes; // the number of nodes in a ZDD (if applicable) + int * pVars; // the list of all variables present in the support + char ** pSymms; // the symmetry information +}; + +/* computes the classical symmetry information for the function - recursive */ +extern Extra_SymmInfo_t * Extra_SymmPairsCompute( DdManager * dd, DdNode * bFunc ); +/* computes the classical symmetry information for the function - using naive approach */ +extern Extra_SymmInfo_t * Extra_SymmPairsComputeNaive( DdManager * dd, DdNode * bFunc ); +extern int Extra_bddCheckVarsSymmetricNaive( DdManager * dd, DdNode * bF, int iVar1, int iVar2 ); + +/* allocates the data structure */ +extern Extra_SymmInfo_t * Extra_SymmPairsAllocate( int nVars ); +/* deallocates the data structure */ +extern void Extra_SymmPairsDissolve( Extra_SymmInfo_t * ); +/* print the contents the data structure */ +extern void Extra_SymmPairsPrint( Extra_SymmInfo_t * ); +/* converts the ZDD into the Extra_SymmInfo_t structure */ +extern Extra_SymmInfo_t * Extra_SymmPairsCreateFromZdd( DdManager * dd, DdNode * zPairs, DdNode * bVars ); + +/* computes the classical symmetry information as a ZDD */ +extern DdNode * Extra_zddSymmPairsCompute( DdManager * dd, DdNode * bF, DdNode * bVars ); +extern DdNode * extraZddSymmPairsCompute( DdManager * dd, DdNode * bF, DdNode * bVars ); +/* returns a singleton-set ZDD containing all variables that are symmetric with the given one */ +extern DdNode * Extra_zddGetSymmetricVars( DdManager * dd, DdNode * bF, DdNode * bG, DdNode * bVars ); +extern DdNode * extraZddGetSymmetricVars( DdManager * dd, DdNode * bF, DdNode * bG, DdNode * bVars ); +/* converts a set of variables into a set of singleton subsets */ +extern DdNode * Extra_zddGetSingletons( DdManager * dd, DdNode * bVars ); +extern DdNode * extraZddGetSingletons( DdManager * dd, DdNode * bVars ); +/* filters the set of variables using the support of the function */ +extern DdNode * Extra_bddReduceVarSet( DdManager * dd, DdNode * bVars, DdNode * bF ); +extern DdNode * extraBddReduceVarSet( DdManager * dd, DdNode * bVars, DdNode * bF ); + +/* checks the possibility that the two vars are symmetric */ +extern int Extra_bddCheckVarsSymmetric( DdManager * dd, DdNode * bF, int iVar1, int iVar2 ); +extern DdNode * extraBddCheckVarsSymmetric( DdManager * dd, DdNode * bF, DdNode * bVars ); + +/* build the set of all tuples of K variables out of N from the BDD cube */ +extern DdNode * Extra_zddTuplesFromBdd( DdManager * dd, int K, DdNode * bVarsN ); +extern DdNode * extraZddTuplesFromBdd( DdManager * dd, DdNode * bVarsK, DdNode * bVarsN ); +/* selects one subset from a ZDD representing the set of subsets */ +extern DdNode * Extra_zddSelectOneSubset( DdManager * dd, DdNode * zS ); +extern DdNode * extraZddSelectOneSubset( DdManager * dd, DdNode * zS ); + +/*=== extraBddUnate.c =================================================================*/ + +typedef struct Extra_UnateVar_t_ Extra_UnateVar_t; +struct Extra_UnateVar_t_ { + unsigned iVar : 30; // index of the variable + unsigned Pos : 1; // 1 if positive unate + unsigned Neg : 1; // 1 if negative unate +}; + +typedef struct Extra_UnateInfo_t_ Extra_UnateInfo_t; +struct Extra_UnateInfo_t_ { + int nVars; // the number of variables in the support + int nVarsMax; // the number of variables in the DD manager + int nUnate; // the number of unate variables + Extra_UnateVar_t * pVars; // the array of variables present in the support +}; + +/* allocates the data structure */ +extern Extra_UnateInfo_t * Extra_UnateInfoAllocate( int nVars ); +/* deallocates the data structure */ +extern void Extra_UnateInfoDissolve( Extra_UnateInfo_t * ); +/* print the contents the data structure */ +extern void Extra_UnateInfoPrint( Extra_UnateInfo_t * ); +/* converts the ZDD into the Extra_SymmInfo_t structure */ +extern Extra_UnateInfo_t * Extra_UnateInfoCreateFromZdd( DdManager * dd, DdNode * zUnate, DdNode * bVars ); +/* naive check of unateness of one variable */ +extern int Extra_bddCheckUnateNaive( DdManager * dd, DdNode * bF, int iVar ); + +/* computes the unateness information for the function */ +extern Extra_UnateInfo_t * Extra_UnateComputeFast( DdManager * dd, DdNode * bFunc ); +extern Extra_UnateInfo_t * Extra_UnateComputeSlow( DdManager * dd, DdNode * bFunc ); + +/* computes the classical symmetry information as a ZDD */ +extern DdNode * Extra_zddUnateInfoCompute( DdManager * dd, DdNode * bF, DdNode * bVars ); +extern DdNode * extraZddUnateInfoCompute( DdManager * dd, DdNode * bF, DdNode * bVars ); + +/* converts a set of variables into a set of singleton subsets */ +extern DdNode * Extra_zddGetSingletonsBoth( DdManager * dd, DdNode * bVars ); +extern DdNode * extraZddGetSingletonsBoth( DdManager * dd, DdNode * bVars ); + +/*=== extraUtilBitMatrix.c ================================================================*/ + +typedef struct Extra_BitMat_t_ Extra_BitMat_t; +extern Extra_BitMat_t * Extra_BitMatrixStart( int nSize ); +extern void Extra_BitMatrixClean( Extra_BitMat_t * p ); +extern void Extra_BitMatrixStop( Extra_BitMat_t * p ); +extern void Extra_BitMatrixPrint( Extra_BitMat_t * p ); +extern int Extra_BitMatrixReadSize( Extra_BitMat_t * p ); +extern void Extra_BitMatrixInsert1( Extra_BitMat_t * p, int i, int k ); +extern int Extra_BitMatrixLookup1( Extra_BitMat_t * p, int i, int k ); +extern void Extra_BitMatrixDelete1( Extra_BitMat_t * p, int i, int k ); +extern void Extra_BitMatrixInsert2( Extra_BitMat_t * p, int i, int k ); +extern int Extra_BitMatrixLookup2( Extra_BitMat_t * p, int i, int k ); +extern void Extra_BitMatrixDelete2( Extra_BitMat_t * p, int i, int k ); +extern void Extra_BitMatrixOr( Extra_BitMat_t * p, int i, unsigned * pInfo ); +extern void Extra_BitMatrixOrTwo( Extra_BitMat_t * p, int i, int j ); +extern int Extra_BitMatrixCountOnesUpper( Extra_BitMat_t * p ); +extern int Extra_BitMatrixIsDisjoint( Extra_BitMat_t * p1, Extra_BitMat_t * p2 ); +extern int Extra_BitMatrixIsClique( Extra_BitMat_t * p ); + +/*=== extraUtilFile.c ========================================================*/ + +extern char * Extra_FileGetSimilarName( char * pFileNameWrong, char * pS1, char * pS2, char * pS3, char * pS4, char * pS5 ); +extern char * Extra_FileNameExtension( char * FileName ); +extern char * Extra_FileNameAppend( char * pBase, char * pSuffix ); +extern char * Extra_FileNameGeneric( char * FileName ); +extern int Extra_FileSize( char * pFileName ); +extern char * Extra_FileRead( FILE * pFile ); +extern char * Extra_TimeStamp(); +extern char * Extra_StringAppend( char * pStrGiven, char * pStrAdd ); +extern unsigned Extra_ReadBinary( char * Buffer ); +extern void Extra_PrintBinary( FILE * pFile, unsigned Sign[], int nBits ); +extern int Extra_ReadHexadecimal( unsigned Sign[], char * pString, int nVars ); +extern void Extra_PrintHexadecimal( FILE * pFile, unsigned Sign[], int nVars ); +extern void Extra_PrintHexadecimalString( char * pString, unsigned Sign[], int nVars ); +extern void Extra_PrintHex( FILE * pFile, unsigned uTruth, int nVars ); +extern void Extra_PrintSymbols( FILE * pFile, char Char, int nTimes, int fPrintNewLine ); + +/*=== extraUtilReader.c ========================================================*/ + +typedef struct Extra_FileReader_t_ Extra_FileReader_t; +extern Extra_FileReader_t * Extra_FileReaderAlloc( char * pFileName, + char * pCharsComment, char * pCharsStop, char * pCharsClean ); +extern void Extra_FileReaderFree( Extra_FileReader_t * p ); +extern char * Extra_FileReaderGetFileName( Extra_FileReader_t * p ); +extern int Extra_FileReaderGetFileSize( Extra_FileReader_t * p ); +extern int Extra_FileReaderGetCurPosition( Extra_FileReader_t * p ); +extern void * Extra_FileReaderGetTokens( Extra_FileReader_t * p ); +extern int Extra_FileReaderGetLineNumber( Extra_FileReader_t * p, int iToken ); + +/*=== extraUtilMemory.c ========================================================*/ + +typedef struct Extra_MmFixed_t_ Extra_MmFixed_t; +typedef struct Extra_MmFlex_t_ Extra_MmFlex_t; +typedef struct Extra_MmStep_t_ Extra_MmStep_t; + +// fixed-size-block memory manager +extern Extra_MmFixed_t * Extra_MmFixedStart( int nEntrySize ); +extern void Extra_MmFixedStop( Extra_MmFixed_t * p ); +extern char * Extra_MmFixedEntryFetch( Extra_MmFixed_t * p ); +extern void Extra_MmFixedEntryRecycle( Extra_MmFixed_t * p, char * pEntry ); +extern void Extra_MmFixedRestart( Extra_MmFixed_t * p ); +extern int Extra_MmFixedReadMemUsage( Extra_MmFixed_t * p ); +extern int Extra_MmFixedReadMaxEntriesUsed( Extra_MmFixed_t * p ); +// flexible-size-block memory manager +extern Extra_MmFlex_t * Extra_MmFlexStart(); +extern void Extra_MmFlexStop( Extra_MmFlex_t * p ); +extern void Extra_MmFlexPrint( Extra_MmFlex_t * p ); +extern char * Extra_MmFlexEntryFetch( Extra_MmFlex_t * p, int nBytes ); +extern int Extra_MmFlexReadMemUsage( Extra_MmFlex_t * p ); +// hierarchical memory manager +extern Extra_MmStep_t * Extra_MmStepStart( int nSteps ); +extern void Extra_MmStepStop( Extra_MmStep_t * p ); +extern char * Extra_MmStepEntryFetch( Extra_MmStep_t * p, int nBytes ); +extern void Extra_MmStepEntryRecycle( Extra_MmStep_t * p, char * pEntry, int nBytes ); +extern int Extra_MmStepReadMemUsage( Extra_MmStep_t * p ); + +/*=== extraUtilMisc.c ========================================================*/ + +/* finds the smallest integer larger or equal than the logarithm */ +extern int Extra_Base2Log( unsigned Num ); +extern int Extra_Base2LogDouble( double Num ); +extern int Extra_Base10Log( unsigned Num ); +/* returns the power of two as a double */ +extern double Extra_Power2( int Num ); +extern int Extra_Power3( int Num ); +/* the number of combinations of k elements out of n */ +extern int Extra_NumCombinations( int k, int n ); +extern int * Extra_DeriveRadixCode( int Number, int Radix, int nDigits ); +/* counts the number of 1s in the bitstring */ +extern int Extra_CountOnes( unsigned char * pBytes, int nBytes ); +/* the factorial of number */ +extern int Extra_Factorial( int n ); +/* the permutation of the given number of elements */ +extern char ** Extra_Permutations( int n ); +/* permutation and complementation of a truth table */ +unsigned Extra_TruthPermute( unsigned Truth, char * pPerms, int nVars, int fReverse ); +unsigned Extra_TruthPolarize( unsigned uTruth, int Polarity, int nVars ); +/* canonical forms of a truth table */ +extern unsigned Extra_TruthCanonN( unsigned uTruth, int nVars ); +extern unsigned Extra_TruthCanonNN( unsigned uTruth, int nVars ); +extern unsigned Extra_TruthCanonP( unsigned uTruth, int nVars ); +extern unsigned Extra_TruthCanonNP( unsigned uTruth, int nVars ); +extern unsigned Extra_TruthCanonNPN( unsigned uTruth, int nVars ); +/* canonical forms of 4-variable functions */ +extern void Extra_Truth4VarNPN( unsigned short ** puCanons, char ** puPhases, char ** puPerms, unsigned char ** puMap ); +extern void Extra_Truth4VarN( unsigned short ** puCanons, char *** puPhases, char ** ppCounters, int nPhasesMax ); +/* permutation mapping */ +extern unsigned short Extra_TruthPerm4One( unsigned uTruth, int Phase ); +extern unsigned Extra_TruthPerm5One( unsigned uTruth, int Phase ); +extern void Extra_TruthPerm6One( unsigned * uTruth, int Phase, unsigned * uTruthRes ); +extern void Extra_TruthExpand( int nVars, int nWords, unsigned * puTruth, unsigned uPhase, unsigned * puTruthR ); +/* precomputing tables for permutation mapping */ +extern void ** Extra_ArrayAlloc( int nCols, int nRows, int Size ); +extern unsigned short ** Extra_TruthPerm43(); +extern unsigned ** Extra_TruthPerm53(); +extern unsigned ** Extra_TruthPerm54(); +/* bubble sort for small number of entries */ +extern void Extra_BubbleSort( int Order[], int Costs[], int nSize, int fIncreasing ); +/* for independence from CUDD */ +extern unsigned int Cudd_PrimeCopy( unsigned int p ); + +/*=== extraUtilCanon.c ========================================================*/ + +/* fast computation of N-canoninical form up to 6 inputs */ +extern int Extra_TruthCanonFastN( int nVarsMax, int nVarsReal, unsigned * pt, unsigned ** pptRes, char ** ppfRes ); + +/*=== extraUtilProgress.c ================================================================*/ + +typedef struct ProgressBarStruct ProgressBar; + +extern ProgressBar * Extra_ProgressBarStart( FILE * pFile, int nItemsTotal ); +extern void Extra_ProgressBarStop( ProgressBar * p ); +extern void Extra_ProgressBarUpdate_int( ProgressBar * p, int nItemsCur, char * pString ); + +static inline void Extra_ProgressBarUpdate( ProgressBar * p, int nItemsCur, char * pString ) +{ if ( p && nItemsCur < *((int*)p) ) return; Extra_ProgressBarUpdate_int(p, nItemsCur, pString); } + +/*=== extraUtilTruth.c ================================================================*/ + +static inline int Extra_Float2Int( float Val ) { return *((int *)&Val); } +static inline float Extra_Int2Float( int Num ) { return *((float *)&Num); } +static inline int Extra_BitWordNum( int nBits ) { return nBits/(8*sizeof(unsigned)) + ((nBits%(8*sizeof(unsigned))) > 0); } +static inline int Extra_TruthWordNum( int nVars ) { return nVars <= 5 ? 1 : (1 << (nVars - 5)); } + +static inline void Extra_TruthSetBit( unsigned * p, int Bit ) { p[Bit>>5] |= (1<<(Bit & 31)); } +static inline void Extra_TruthXorBit( unsigned * p, int Bit ) { p[Bit>>5] ^= (1<<(Bit & 31)); } +static inline int Extra_TruthHasBit( unsigned * p, int Bit ) { return (p[Bit>>5] & (1<<(Bit & 31))) > 0; } + +static inline int Extra_WordCountOnes( unsigned uWord ) +{ + uWord = (uWord & 0x55555555) + ((uWord>>1) & 0x55555555); + uWord = (uWord & 0x33333333) + ((uWord>>2) & 0x33333333); + uWord = (uWord & 0x0F0F0F0F) + ((uWord>>4) & 0x0F0F0F0F); + uWord = (uWord & 0x00FF00FF) + ((uWord>>8) & 0x00FF00FF); + return (uWord & 0x0000FFFF) + (uWord>>16); +} +static inline int Extra_TruthCountOnes( unsigned * pIn, int nVars ) +{ + int w, Counter = 0; + for ( w = Extra_TruthWordNum(nVars)-1; w >= 0; w-- ) + Counter += Extra_WordCountOnes(pIn[w]); + return Counter; +} +static inline int Extra_TruthIsEqual( unsigned * pIn0, unsigned * pIn1, int nVars ) +{ + int w; + for ( w = Extra_TruthWordNum(nVars)-1; w >= 0; w-- ) + if ( pIn0[w] != pIn1[w] ) + return 0; + return 1; +} +static inline int Extra_TruthIsConst0( unsigned * pIn, int nVars ) +{ + int w; + for ( w = Extra_TruthWordNum(nVars)-1; w >= 0; w-- ) + if ( pIn[w] ) + return 0; + return 1; +} +static inline int Extra_TruthIsConst1( unsigned * pIn, int nVars ) +{ + int w; + for ( w = Extra_TruthWordNum(nVars)-1; w >= 0; w-- ) + if ( pIn[w] != ~(unsigned)0 ) + return 0; + return 1; +} +static inline int Extra_TruthIsImply( unsigned * pIn1, unsigned * pIn2, int nVars ) +{ + int w; + for ( w = Extra_TruthWordNum(nVars)-1; w >= 0; w-- ) + if ( pIn1[w] & ~pIn2[w] ) + return 0; + return 1; +} +static inline void Extra_TruthCopy( unsigned * pOut, unsigned * pIn, int nVars ) +{ + int w; + for ( w = Extra_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = pIn[w]; +} +static inline void Extra_TruthClear( unsigned * pOut, int nVars ) +{ + int w; + for ( w = Extra_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = 0; +} +static inline void Extra_TruthFill( unsigned * pOut, int nVars ) +{ + int w; + for ( w = Extra_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = ~(unsigned)0; +} +static inline void Extra_TruthNot( unsigned * pOut, unsigned * pIn, int nVars ) +{ + int w; + for ( w = Extra_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = ~pIn[w]; +} +static inline void Extra_TruthAnd( unsigned * pOut, unsigned * pIn0, unsigned * pIn1, int nVars ) +{ + int w; + for ( w = Extra_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = pIn0[w] & pIn1[w]; +} +static inline void Extra_TruthOr( unsigned * pOut, unsigned * pIn0, unsigned * pIn1, int nVars ) +{ + int w; + for ( w = Extra_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = pIn0[w] | pIn1[w]; +} +static inline void Extra_TruthSharp( unsigned * pOut, unsigned * pIn0, unsigned * pIn1, int nVars ) +{ + int w; + for ( w = Extra_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = pIn0[w] & ~pIn1[w]; +} +static inline void Extra_TruthNand( unsigned * pOut, unsigned * pIn0, unsigned * pIn1, int nVars ) +{ + int w; + for ( w = Extra_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = ~(pIn0[w] & pIn1[w]); +} +static inline void Extra_TruthAndPhase( unsigned * pOut, unsigned * pIn0, unsigned * pIn1, int nVars, int fCompl0, int fCompl1 ) +{ + int w; + if ( fCompl0 && fCompl1 ) + { + for ( w = Extra_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = ~(pIn0[w] | pIn1[w]); + } + else if ( fCompl0 && !fCompl1 ) + { + for ( w = Extra_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = ~pIn0[w] & pIn1[w]; + } + else if ( !fCompl0 && fCompl1 ) + { + for ( w = Extra_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = pIn0[w] & ~pIn1[w]; + } + else // if ( !fCompl0 && !fCompl1 ) + { + for ( w = Extra_TruthWordNum(nVars)-1; w >= 0; w-- ) + pOut[w] = pIn0[w] & pIn1[w]; + } +} + +extern unsigned ** Extra_TruthElementary( int nVars ); +extern void Extra_TruthSwapAdjacentVars( unsigned * pOut, unsigned * pIn, int nVars, int Start ); +extern void Extra_TruthStretch( unsigned * pOut, unsigned * pIn, int nVars, int nVarsAll, unsigned Phase ); +extern void Extra_TruthShrink( unsigned * pOut, unsigned * pIn, int nVars, int nVarsAll, unsigned Phase ); +extern int Extra_TruthVarInSupport( unsigned * pTruth, int nVars, int iVar ); +extern int Extra_TruthSupportSize( unsigned * pTruth, int nVars ); +extern int Extra_TruthSupport( unsigned * pTruth, int nVars ); +extern void Extra_TruthCofactor0( unsigned * pTruth, int nVars, int iVar ); +extern void Extra_TruthCofactor1( unsigned * pTruth, int nVars, int iVar ); +extern void Extra_TruthExist( unsigned * pTruth, int nVars, int iVar ); +extern void Extra_TruthForall( unsigned * pTruth, int nVars, int iVar ); +extern void Extra_TruthMux( unsigned * pOut, unsigned * pCof0, unsigned * pCof1, int nVars, int iVar ); +extern void Extra_TruthChangePhase( unsigned * pTruth, int nVars, int iVar ); +extern int Extra_TruthMinCofSuppOverlap( unsigned * pTruth, int nVars, int * pVarMin ); +extern void Extra_TruthCountOnesInCofs( unsigned * pTruth, int nVars, short * pStore ); +extern unsigned Extra_TruthHash( unsigned * pIn, int nWords ); +extern unsigned Extra_TruthSemiCanonicize( unsigned * pInOut, unsigned * pAux, int nVars, char * pCanonPerm, short * pStore ); + +/*=== extraUtilUtil.c ================================================================*/ + +#ifndef ABS +#define ABS(a) ((a) < 0 ? -(a) : (a)) +#endif + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef ALLOC +#define ALLOC(type, num) ((type *) malloc(sizeof(type) * (num))) +#endif + +#ifndef FREE +#define FREE(obj) ((obj) ? (free((char *) (obj)), (obj) = 0) : 0) +#endif + +#ifndef REALLOC +#define REALLOC(type, obj, num) \ + ((obj) ? ((type *) realloc((char *)(obj), sizeof(type) * (num))) : \ + ((type *) malloc(sizeof(type) * (num)))) +#endif + + +extern long Extra_CpuTime(); +extern int Extra_GetSoftDataLimit(); +extern void Extra_UtilGetoptReset(); +extern int Extra_UtilGetopt( int argc, char *argv[], char *optstring ); +extern char * Extra_UtilPrintTime( long t ); +extern char * Extra_UtilStrsav( char *s ); +extern char * Extra_UtilTildeExpand( char *fname ); +extern char * Extra_UtilFileSearch( char *file, char *path, char *mode ); +extern void (*Extra_UtilMMoutOfMemory)(); + +extern char * globalUtilOptarg; +extern int globalUtilOptind; + +/**AutomaticEnd***************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* __EXTRA_H__ */ diff --git a/abc_with_bb_support/src/misc/extra/extraBddAuto.c b/abc_with_bb_support/src/misc/extra/extraBddAuto.c new file mode 100644 index 000000000..bb0d2da15 --- /dev/null +++ b/abc_with_bb_support/src/misc/extra/extraBddAuto.c @@ -0,0 +1,1558 @@ +/**CFile**************************************************************** + + FileName [extraBddAuto.c] + + PackageName [extra] + + Synopsis [Computation of autosymmetries.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - September 1, 2003.] + + Revision [$Id: extraBddAuto.c,v 1.0 2003/05/21 18:03:50 alanmi Exp $] + +***********************************************************************/ + +#include "extra.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticEnd***************************************************************/ + + +/* + LinearSpace(f) = Space(f,f) + + Space(f,g) + { + if ( f = const ) + { + if ( f = g ) return 1; + else return 0; + } + if ( g = const ) return 0; + return x' * Space(fx',gx') * Space(fx,gx) + x * Space(fx',gx) * Space(fx,gx'); + } + + Equations(s) = Pos(s) + Neg(s); + + Pos(s) + { + if ( s = 0 ) return 1; + if ( s = 1 ) return 0; + if ( sx'= 0 ) return Pos(sx) + x; + if ( sx = 0 ) return Pos(sx'); + return 1 * [Pos(sx') & Pos(sx)] + x * [Pos(sx') & Neg(sx)]; + } + + Neg(s) + { + if ( s = 0 ) return 1; + if ( s = 1 ) return 0; + if ( sx'= 0 ) return Neg(sx); + if ( sx = 0 ) return Neg(sx') + x; + return 1 * [Neg(sx') & Neg(sx)] + x * [Neg(sx') & Pos(sx)]; + } + + + SpaceP(A) + { + if ( A = 0 ) return 1; + if ( A = 1 ) return 1; + return x' * SpaceP(Ax') * SpaceP(Ax) + x * SpaceP(Ax') * SpaceN(Ax); + } + + SpaceN(A) + { + if ( A = 0 ) return 1; + if ( A = 1 ) return 0; + return x' * SpaceN(Ax') * SpaceN(Ax) + x * SpaceN(Ax') * SpaceP(Ax); + } + + + LinInd(A) + { + if ( A = const ) return 1; + if ( !LinInd(Ax') ) return 0; + if ( !LinInd(Ax) ) return 0; + if ( LinSumOdd(Ax') & LinSumEven(Ax) != 0 ) return 0; + if ( LinSumEven(Ax') & LinSumEven(Ax) != 0 ) return 0; + return 1; + } + + LinSumOdd(A) + { + if ( A = 0 ) return 0; // Odd0 ---e-- Odd1 + if ( A = 1 ) return 1; // \ o + Odd0 = LinSumOdd(Ax'); // x is absent // \ + Even0 = LinSumEven(Ax'); // x is absent // / o + Odd1 = LinSumOdd(Ax); // x is present // Even0 ---e-- Even1 + Even1 = LinSumEven(Ax); // x is absent + return 1 * [Odd0 + ExorP(Odd0, Even1)] + x * [Odd1 + ExorP(Odd1, Even0)]; + } + + LinSumEven(A) + { + if ( A = 0 ) return 0; + if ( A = 1 ) return 0; + Odd0 = LinSumOdd(Ax'); // x is absent + Even0 = LinSumEven(Ax'); // x is absent + Odd1 = LinSumOdd(Ax); // x is present + Even1 = LinSumEven(Ax); // x is absent + return 1 * [Even0 + Even1 + ExorP(Even0, Even1)] + x * [ExorP(Odd0, Odd1)]; + } + +*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Extra_bddSpaceFromFunctionFast( DdManager * dd, DdNode * bFunc ) +{ + int * pSupport; + int * pPermute; + int * pPermuteBack; + DdNode ** pCompose; + DdNode * bCube, * bTemp; + DdNode * bSpace, * bFunc1, * bFunc2, * bSpaceShift; + int nSupp, Counter; + int i, lev; + + // get the support + pSupport = ALLOC( int, ddMax(dd->size,dd->sizeZ) ); + Extra_SupportArray( dd, bFunc, pSupport ); + nSupp = 0; + for ( i = 0; i < dd->size; i++ ) + if ( pSupport[i] ) + nSupp++; + + // make sure the manager has enough variables + if ( 2*nSupp > dd->size ) + { + printf( "Cannot derive linear space, because DD manager does not have enough variables.\n" ); + fflush( stdout ); + free( pSupport ); + return NULL; + } + + // create the permutation arrays + pPermute = ALLOC( int, dd->size ); + pPermuteBack = ALLOC( int, dd->size ); + pCompose = ALLOC( DdNode *, dd->size ); + for ( i = 0; i < dd->size; i++ ) + { + pPermute[i] = i; + pPermuteBack[i] = i; + pCompose[i] = dd->vars[i]; Cudd_Ref( pCompose[i] ); + } + + // remap the function in such a way that the variables are interleaved + Counter = 0; + bCube = b1; Cudd_Ref( bCube ); + for ( lev = 0; lev < dd->size; lev++ ) + if ( pSupport[ dd->invperm[lev] ] ) + { // var "dd->invperm[lev]" on level "lev" should go to level 2*Counter; + pPermute[ dd->invperm[lev] ] = dd->invperm[2*Counter]; + // var from level 2*Counter+1 should go back to the place of this var + pPermuteBack[ dd->invperm[2*Counter+1] ] = dd->invperm[lev]; + // the permutation should be defined in such a way that variable + // on level 2*Counter is replaced by an EXOR of itself and var on the next level + Cudd_Deref( pCompose[ dd->invperm[2*Counter] ] ); + pCompose[ dd->invperm[2*Counter] ] = + Cudd_bddXor( dd, dd->vars[ dd->invperm[2*Counter] ], dd->vars[ dd->invperm[2*Counter+1] ] ); + Cudd_Ref( pCompose[ dd->invperm[2*Counter] ] ); + // add this variable to the cube + bCube = Cudd_bddAnd( dd, bTemp = bCube, dd->vars[ dd->invperm[2*Counter] ] ); Cudd_Ref( bCube ); + Cudd_RecursiveDeref( dd, bTemp ); + // increment the counter + Counter ++; + } + + // permute the functions + bFunc1 = Cudd_bddPermute( dd, bFunc, pPermute ); Cudd_Ref( bFunc1 ); + // compose to gate the function depending on both vars + bFunc2 = Cudd_bddVectorCompose( dd, bFunc1, pCompose ); Cudd_Ref( bFunc2 ); + // gate the vector space + // L(a) = ForAll x [ F(x) = F(x+a) ] = Not( Exist x [ F(x) (+) F(x+a) ] ) + bSpaceShift = Cudd_bddXorExistAbstract( dd, bFunc1, bFunc2, bCube ); Cudd_Ref( bSpaceShift ); + bSpaceShift = Cudd_Not( bSpaceShift ); + // permute the space back into the original mapping + bSpace = Cudd_bddPermute( dd, bSpaceShift, pPermuteBack ); Cudd_Ref( bSpace ); + Cudd_RecursiveDeref( dd, bFunc1 ); + Cudd_RecursiveDeref( dd, bFunc2 ); + Cudd_RecursiveDeref( dd, bSpaceShift ); + Cudd_RecursiveDeref( dd, bCube ); + + for ( i = 0; i < dd->size; i++ ) + Cudd_RecursiveDeref( dd, pCompose[i] ); + free( pPermute ); + free( pPermuteBack ); + free( pCompose ); + free( pSupport ); + + Cudd_Deref( bSpace ); + return bSpace; +} + + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Extra_bddSpaceFromFunction( DdManager * dd, DdNode * bF, DdNode * bG ) +{ + DdNode * bRes; + do { + dd->reordered = 0; + bRes = extraBddSpaceFromFunction( dd, bF, bG ); + } while (dd->reordered == 1); + return bRes; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Extra_bddSpaceFromFunctionPos( DdManager * dd, DdNode * bFunc ) +{ + DdNode * bRes; + do { + dd->reordered = 0; + bRes = extraBddSpaceFromFunctionPos( dd, bFunc ); + } while (dd->reordered == 1); + return bRes; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Extra_bddSpaceFromFunctionNeg( DdManager * dd, DdNode * bFunc ) +{ + DdNode * bRes; + do { + dd->reordered = 0; + bRes = extraBddSpaceFromFunctionNeg( dd, bFunc ); + } while (dd->reordered == 1); + return bRes; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Extra_bddSpaceCanonVars( DdManager * dd, DdNode * bSpace ) +{ + DdNode * bRes; + do { + dd->reordered = 0; + bRes = extraBddSpaceCanonVars( dd, bSpace ); + } while (dd->reordered == 1); + return bRes; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Extra_bddSpaceReduce( DdManager * dd, DdNode * bFunc, DdNode * bCanonVars ) +{ + DdNode * bNegCube; + DdNode * bResult; + bNegCube = Extra_bddSupportNegativeCube( dd, bCanonVars ); Cudd_Ref( bNegCube ); + bResult = Cudd_Cofactor( dd, bFunc, bNegCube ); Cudd_Ref( bResult ); + Cudd_RecursiveDeref( dd, bNegCube ); + Cudd_Deref( bResult ); + return bResult; +} + + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Extra_bddSpaceEquations( DdManager * dd, DdNode * bSpace ) +{ + DdNode * zRes; + DdNode * zEquPos; + DdNode * zEquNeg; + zEquPos = Extra_bddSpaceEquationsPos( dd, bSpace ); Cudd_Ref( zEquPos ); + zEquNeg = Extra_bddSpaceEquationsNeg( dd, bSpace ); Cudd_Ref( zEquNeg ); + zRes = Cudd_zddUnion( dd, zEquPos, zEquNeg ); Cudd_Ref( zRes ); + Cudd_RecursiveDerefZdd( dd, zEquPos ); + Cudd_RecursiveDerefZdd( dd, zEquNeg ); + Cudd_Deref( zRes ); + return zRes; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Extra_bddSpaceEquationsPos( DdManager * dd, DdNode * bSpace ) +{ + DdNode * zRes; + do { + dd->reordered = 0; + zRes = extraBddSpaceEquationsPos( dd, bSpace ); + } while (dd->reordered == 1); + return zRes; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Extra_bddSpaceEquationsNeg( DdManager * dd, DdNode * bSpace ) +{ + DdNode * zRes; + do { + dd->reordered = 0; + zRes = extraBddSpaceEquationsNeg( dd, bSpace ); + } while (dd->reordered == 1); + return zRes; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Extra_bddSpaceFromMatrixPos( DdManager * dd, DdNode * zA ) +{ + DdNode * bRes; + do { + dd->reordered = 0; + bRes = extraBddSpaceFromMatrixPos( dd, zA ); + } while (dd->reordered == 1); + return bRes; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Extra_bddSpaceFromMatrixNeg( DdManager * dd, DdNode * zA ) +{ + DdNode * bRes; + do { + dd->reordered = 0; + bRes = extraBddSpaceFromMatrixNeg( dd, zA ); + } while (dd->reordered == 1); + return bRes; +} + +/**Function************************************************************* + + Synopsis [Counts the number of literals in one combination.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_zddLitCountComb( DdManager * dd, DdNode * zComb ) +{ + int Counter; + if ( zComb == z0 ) + return 0; + Counter = 0; + for ( ; zComb != z1; zComb = cuddT(zComb) ) + Counter++; + return Counter; +} + +/**Function************************************************************* + + Synopsis [] + + Description [Returns the array of ZDDs with the number equal to the number of + vars in the DD manager. If the given var is non-canonical, this array contains + the referenced ZDD representing literals in the corresponding EXOR equation.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode ** Extra_bddSpaceExorGates( DdManager * dd, DdNode * bFuncRed, DdNode * zEquations ) +{ + DdNode ** pzRes; + int * pVarsNonCan; + DdNode * zEquRem; + int iVarNonCan; + DdNode * zExor, * zTemp; + + // get the set of non-canonical variables + pVarsNonCan = ALLOC( int, ddMax(dd->size,dd->sizeZ) ); + Extra_SupportArray( dd, bFuncRed, pVarsNonCan ); + + // allocate storage for the EXOR sets + pzRes = ALLOC( DdNode *, dd->size ); + memset( pzRes, 0, sizeof(DdNode *) * dd->size ); + + // go through all the equations + zEquRem = zEquations; Cudd_Ref( zEquRem ); + while ( zEquRem != z0 ) + { + // extract one product + zExor = Extra_zddSelectOneSubset( dd, zEquRem ); Cudd_Ref( zExor ); + // remove it from the set + zEquRem = Cudd_zddDiff( dd, zTemp = zEquRem, zExor ); Cudd_Ref( zEquRem ); + Cudd_RecursiveDerefZdd( dd, zTemp ); + + // locate the non-canonical variable + iVarNonCan = -1; + for ( zTemp = zExor; zTemp != z1; zTemp = cuddT(zTemp) ) + { + if ( pVarsNonCan[zTemp->index/2] == 1 ) + { + assert( iVarNonCan == -1 ); + iVarNonCan = zTemp->index/2; + } + } + assert( iVarNonCan != -1 ); + + if ( Extra_zddLitCountComb( dd, zExor ) > 1 ) + pzRes[ iVarNonCan ] = zExor; // takes ref + else + Cudd_RecursiveDerefZdd( dd, zExor ); + } + Cudd_RecursiveDerefZdd( dd, zEquRem ); + + free( pVarsNonCan ); + return pzRes; +} + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Performs the recursive steps of Extra_bddSpaceFromFunction.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * extraBddSpaceFromFunction( DdManager * dd, DdNode * bF, DdNode * bG ) +{ + DdNode * bRes; + DdNode * bFR, * bGR; + + bFR = Cudd_Regular( bF ); + bGR = Cudd_Regular( bG ); + if ( cuddIsConstant(bFR) ) + { + if ( bF == bG ) + return b1; + else + return b0; + } + if ( cuddIsConstant(bGR) ) + return b0; + // both bFunc and bCore are not constants + + // the operation is commutative - normalize the problem + if ( (unsigned)bF > (unsigned)bG ) + return extraBddSpaceFromFunction(dd, bG, bF); + + + if ( bRes = cuddCacheLookup2(dd, extraBddSpaceFromFunction, bF, bG) ) + return bRes; + else + { + DdNode * bF0, * bF1; + DdNode * bG0, * bG1; + DdNode * bTemp1, * bTemp2; + DdNode * bRes0, * bRes1; + int LevelF, LevelG; + int index; + + LevelF = dd->perm[bFR->index]; + LevelG = dd->perm[bGR->index]; + if ( LevelF <= LevelG ) + { + index = dd->invperm[LevelF]; + if ( bFR != bF ) + { + bF0 = Cudd_Not( cuddE(bFR) ); + bF1 = Cudd_Not( cuddT(bFR) ); + } + else + { + bF0 = cuddE(bFR); + bF1 = cuddT(bFR); + } + } + else + { + index = dd->invperm[LevelG]; + bF0 = bF1 = bF; + } + + if ( LevelG <= LevelF ) + { + if ( bGR != bG ) + { + bG0 = Cudd_Not( cuddE(bGR) ); + bG1 = Cudd_Not( cuddT(bGR) ); + } + else + { + bG0 = cuddE(bGR); + bG1 = cuddT(bGR); + } + } + else + bG0 = bG1 = bG; + + bTemp1 = extraBddSpaceFromFunction( dd, bF0, bG0 ); + if ( bTemp1 == NULL ) + return NULL; + cuddRef( bTemp1 ); + + bTemp2 = extraBddSpaceFromFunction( dd, bF1, bG1 ); + if ( bTemp2 == NULL ) + { + Cudd_RecursiveDeref( dd, bTemp1 ); + return NULL; + } + cuddRef( bTemp2 ); + + + bRes0 = cuddBddAndRecur( dd, bTemp1, bTemp2 ); + if ( bRes0 == NULL ) + { + Cudd_RecursiveDeref( dd, bTemp1 ); + Cudd_RecursiveDeref( dd, bTemp2 ); + return NULL; + } + cuddRef( bRes0 ); + Cudd_RecursiveDeref( dd, bTemp1 ); + Cudd_RecursiveDeref( dd, bTemp2 ); + + + bTemp1 = extraBddSpaceFromFunction( dd, bF0, bG1 ); + if ( bTemp1 == NULL ) + { + Cudd_RecursiveDeref( dd, bRes0 ); + return NULL; + } + cuddRef( bTemp1 ); + + bTemp2 = extraBddSpaceFromFunction( dd, bF1, bG0 ); + if ( bTemp2 == NULL ) + { + Cudd_RecursiveDeref( dd, bRes0 ); + Cudd_RecursiveDeref( dd, bTemp1 ); + return NULL; + } + cuddRef( bTemp2 ); + + bRes1 = cuddBddAndRecur( dd, bTemp1, bTemp2 ); + if ( bRes1 == NULL ) + { + Cudd_RecursiveDeref( dd, bRes0 ); + Cudd_RecursiveDeref( dd, bTemp1 ); + Cudd_RecursiveDeref( dd, bTemp2 ); + return NULL; + } + cuddRef( bRes1 ); + Cudd_RecursiveDeref( dd, bTemp1 ); + Cudd_RecursiveDeref( dd, bTemp2 ); + + + + // consider the case when Res0 and Res1 are the same node + if ( bRes0 == bRes1 ) + bRes = bRes1; + // consider the case when Res1 is complemented + else if ( Cudd_IsComplement(bRes1) ) + { + bRes = cuddUniqueInter(dd, index, Cudd_Not(bRes1), Cudd_Not(bRes0)); + if ( bRes == NULL ) + { + Cudd_RecursiveDeref(dd,bRes0); + Cudd_RecursiveDeref(dd,bRes1); + return NULL; + } + bRes = Cudd_Not(bRes); + } + else + { + bRes = cuddUniqueInter( dd, index, bRes1, bRes0 ); + if ( bRes == NULL ) + { + Cudd_RecursiveDeref(dd,bRes0); + Cudd_RecursiveDeref(dd,bRes1); + return NULL; + } + } + cuddDeref( bRes0 ); + cuddDeref( bRes1 ); + + // insert the result into cache + cuddCacheInsert2(dd, extraBddSpaceFromFunction, bF, bG, bRes); + return bRes; + } +} /* end of extraBddSpaceFromFunction */ + + + +/**Function************************************************************* + + Synopsis [Performs the recursive step of Extra_bddSpaceFromFunctionPos().] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * extraBddSpaceFromFunctionPos( DdManager * dd, DdNode * bF ) +{ + DdNode * bRes, * bFR; + statLine( dd ); + + bFR = Cudd_Regular(bF); + if ( cuddIsConstant(bFR) ) + return b1; + + if ( bRes = cuddCacheLookup1(dd, extraBddSpaceFromFunctionPos, bF) ) + return bRes; + else + { + DdNode * bF0, * bF1; + DdNode * bPos0, * bPos1; + DdNode * bNeg0, * bNeg1; + DdNode * bRes0, * bRes1; + + if ( bFR != bF ) // bF is complemented + { + bF0 = Cudd_Not( cuddE(bFR) ); + bF1 = Cudd_Not( cuddT(bFR) ); + } + else + { + bF0 = cuddE(bFR); + bF1 = cuddT(bFR); + } + + + bPos0 = extraBddSpaceFromFunctionPos( dd, bF0 ); + if ( bPos0 == NULL ) + return NULL; + cuddRef( bPos0 ); + + bPos1 = extraBddSpaceFromFunctionPos( dd, bF1 ); + if ( bPos1 == NULL ) + { + Cudd_RecursiveDeref( dd, bPos0 ); + return NULL; + } + cuddRef( bPos1 ); + + bRes0 = cuddBddAndRecur( dd, bPos0, bPos1 ); + if ( bRes0 == NULL ) + { + Cudd_RecursiveDeref( dd, bPos0 ); + Cudd_RecursiveDeref( dd, bPos1 ); + return NULL; + } + cuddRef( bRes0 ); + Cudd_RecursiveDeref( dd, bPos0 ); + Cudd_RecursiveDeref( dd, bPos1 ); + + + bNeg0 = extraBddSpaceFromFunctionNeg( dd, bF0 ); + if ( bNeg0 == NULL ) + { + Cudd_RecursiveDeref( dd, bRes0 ); + return NULL; + } + cuddRef( bNeg0 ); + + bNeg1 = extraBddSpaceFromFunctionNeg( dd, bF1 ); + if ( bNeg1 == NULL ) + { + Cudd_RecursiveDeref( dd, bRes0 ); + Cudd_RecursiveDeref( dd, bNeg0 ); + return NULL; + } + cuddRef( bNeg1 ); + + bRes1 = cuddBddAndRecur( dd, bNeg0, bNeg1 ); + if ( bRes1 == NULL ) + { + Cudd_RecursiveDeref( dd, bRes0 ); + Cudd_RecursiveDeref( dd, bNeg0 ); + Cudd_RecursiveDeref( dd, bNeg1 ); + return NULL; + } + cuddRef( bRes1 ); + Cudd_RecursiveDeref( dd, bNeg0 ); + Cudd_RecursiveDeref( dd, bNeg1 ); + + + // consider the case when Res0 and Res1 are the same node + if ( bRes0 == bRes1 ) + bRes = bRes1; + // consider the case when Res1 is complemented + else if ( Cudd_IsComplement(bRes1) ) + { + bRes = cuddUniqueInter( dd, bFR->index, Cudd_Not(bRes1), Cudd_Not(bRes0) ); + if ( bRes == NULL ) + { + Cudd_RecursiveDeref(dd,bRes0); + Cudd_RecursiveDeref(dd,bRes1); + return NULL; + } + bRes = Cudd_Not(bRes); + } + else + { + bRes = cuddUniqueInter( dd, bFR->index, bRes1, bRes0 ); + if ( bRes == NULL ) + { + Cudd_RecursiveDeref(dd,bRes0); + Cudd_RecursiveDeref(dd,bRes1); + return NULL; + } + } + cuddDeref( bRes0 ); + cuddDeref( bRes1 ); + + cuddCacheInsert1( dd, extraBddSpaceFromFunctionPos, bF, bRes ); + return bRes; + } +} + + + +/**Function************************************************************* + + Synopsis [Performs the recursive step of Extra_bddSpaceFromFunctionPos().] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * extraBddSpaceFromFunctionNeg( DdManager * dd, DdNode * bF ) +{ + DdNode * bRes, * bFR; + statLine( dd ); + + bFR = Cudd_Regular(bF); + if ( cuddIsConstant(bFR) ) + return b0; + + if ( bRes = cuddCacheLookup1(dd, extraBddSpaceFromFunctionNeg, bF) ) + return bRes; + else + { + DdNode * bF0, * bF1; + DdNode * bPos0, * bPos1; + DdNode * bNeg0, * bNeg1; + DdNode * bRes0, * bRes1; + + if ( bFR != bF ) // bF is complemented + { + bF0 = Cudd_Not( cuddE(bFR) ); + bF1 = Cudd_Not( cuddT(bFR) ); + } + else + { + bF0 = cuddE(bFR); + bF1 = cuddT(bFR); + } + + + bPos0 = extraBddSpaceFromFunctionNeg( dd, bF0 ); + if ( bPos0 == NULL ) + return NULL; + cuddRef( bPos0 ); + + bPos1 = extraBddSpaceFromFunctionNeg( dd, bF1 ); + if ( bPos1 == NULL ) + { + Cudd_RecursiveDeref( dd, bPos0 ); + return NULL; + } + cuddRef( bPos1 ); + + bRes0 = cuddBddAndRecur( dd, bPos0, bPos1 ); + if ( bRes0 == NULL ) + { + Cudd_RecursiveDeref( dd, bPos0 ); + Cudd_RecursiveDeref( dd, bPos1 ); + return NULL; + } + cuddRef( bRes0 ); + Cudd_RecursiveDeref( dd, bPos0 ); + Cudd_RecursiveDeref( dd, bPos1 ); + + + bNeg0 = extraBddSpaceFromFunctionPos( dd, bF0 ); + if ( bNeg0 == NULL ) + { + Cudd_RecursiveDeref( dd, bRes0 ); + return NULL; + } + cuddRef( bNeg0 ); + + bNeg1 = extraBddSpaceFromFunctionPos( dd, bF1 ); + if ( bNeg1 == NULL ) + { + Cudd_RecursiveDeref( dd, bRes0 ); + Cudd_RecursiveDeref( dd, bNeg0 ); + return NULL; + } + cuddRef( bNeg1 ); + + bRes1 = cuddBddAndRecur( dd, bNeg0, bNeg1 ); + if ( bRes1 == NULL ) + { + Cudd_RecursiveDeref( dd, bRes0 ); + Cudd_RecursiveDeref( dd, bNeg0 ); + Cudd_RecursiveDeref( dd, bNeg1 ); + return NULL; + } + cuddRef( bRes1 ); + Cudd_RecursiveDeref( dd, bNeg0 ); + Cudd_RecursiveDeref( dd, bNeg1 ); + + + // consider the case when Res0 and Res1 are the same node + if ( bRes0 == bRes1 ) + bRes = bRes1; + // consider the case when Res1 is complemented + else if ( Cudd_IsComplement(bRes1) ) + { + bRes = cuddUniqueInter( dd, bFR->index, Cudd_Not(bRes1), Cudd_Not(bRes0) ); + if ( bRes == NULL ) + { + Cudd_RecursiveDeref(dd,bRes0); + Cudd_RecursiveDeref(dd,bRes1); + return NULL; + } + bRes = Cudd_Not(bRes); + } + else + { + bRes = cuddUniqueInter( dd, bFR->index, bRes1, bRes0 ); + if ( bRes == NULL ) + { + Cudd_RecursiveDeref(dd,bRes0); + Cudd_RecursiveDeref(dd,bRes1); + return NULL; + } + } + cuddDeref( bRes0 ); + cuddDeref( bRes1 ); + + cuddCacheInsert1( dd, extraBddSpaceFromFunctionNeg, bF, bRes ); + return bRes; + } +} + + + +/**Function************************************************************* + + Synopsis [Performs the recursive step of Extra_bddSpaceCanonVars().] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * extraBddSpaceCanonVars( DdManager * dd, DdNode * bF ) +{ + DdNode * bRes, * bFR; + statLine( dd ); + + bFR = Cudd_Regular(bF); + if ( cuddIsConstant(bFR) ) + return bF; + + if ( bRes = cuddCacheLookup1(dd, extraBddSpaceCanonVars, bF) ) + return bRes; + else + { + DdNode * bF0, * bF1; + DdNode * bRes, * bRes0; + + if ( bFR != bF ) // bF is complemented + { + bF0 = Cudd_Not( cuddE(bFR) ); + bF1 = Cudd_Not( cuddT(bFR) ); + } + else + { + bF0 = cuddE(bFR); + bF1 = cuddT(bFR); + } + + if ( bF0 == b0 ) + { + bRes = extraBddSpaceCanonVars( dd, bF1 ); + if ( bRes == NULL ) + return NULL; + } + else if ( bF1 == b0 ) + { + bRes = extraBddSpaceCanonVars( dd, bF0 ); + if ( bRes == NULL ) + return NULL; + } + else + { + bRes0 = extraBddSpaceCanonVars( dd, bF0 ); + if ( bRes0 == NULL ) + return NULL; + cuddRef( bRes0 ); + + bRes = cuddUniqueInter( dd, bFR->index, bRes0, b0 ); + if ( bRes == NULL ) + { + Cudd_RecursiveDeref( dd,bRes0 ); + return NULL; + } + cuddDeref( bRes0 ); + } + + cuddCacheInsert1( dd, extraBddSpaceCanonVars, bF, bRes ); + return bRes; + } +} + +/**Function************************************************************* + + Synopsis [Performs the recursive step of Extra_bddSpaceEquationsPos().] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * extraBddSpaceEquationsPos( DdManager * dd, DdNode * bF ) +{ + DdNode * zRes; + statLine( dd ); + + if ( bF == b0 ) + return z1; + if ( bF == b1 ) + return z0; + + if ( zRes = cuddCacheLookup1Zdd(dd, extraBddSpaceEquationsPos, bF) ) + return zRes; + else + { + DdNode * bFR, * bF0, * bF1; + DdNode * zPos0, * zPos1, * zNeg1; + DdNode * zRes, * zRes0, * zRes1; + + bFR = Cudd_Regular(bF); + if ( bFR != bF ) // bF is complemented + { + bF0 = Cudd_Not( cuddE(bFR) ); + bF1 = Cudd_Not( cuddT(bFR) ); + } + else + { + bF0 = cuddE(bFR); + bF1 = cuddT(bFR); + } + + if ( bF0 == b0 ) + { + zRes1 = extraBddSpaceEquationsPos( dd, bF1 ); + if ( zRes1 == NULL ) + return NULL; + cuddRef( zRes1 ); + + // add the current element to the set + zRes = cuddZddGetNode( dd, 2*bFR->index, z1, zRes1 ); + if ( zRes == NULL ) + { + Cudd_RecursiveDerefZdd(dd, zRes1); + return NULL; + } + cuddDeref( zRes1 ); + } + else if ( bF1 == b0 ) + { + zRes = extraBddSpaceEquationsPos( dd, bF0 ); + if ( zRes == NULL ) + return NULL; + } + else + { + zPos0 = extraBddSpaceEquationsPos( dd, bF0 ); + if ( zPos0 == NULL ) + return NULL; + cuddRef( zPos0 ); + + zPos1 = extraBddSpaceEquationsPos( dd, bF1 ); + if ( zPos1 == NULL ) + { + Cudd_RecursiveDerefZdd(dd, zPos0); + return NULL; + } + cuddRef( zPos1 ); + + zNeg1 = extraBddSpaceEquationsNeg( dd, bF1 ); + if ( zNeg1 == NULL ) + { + Cudd_RecursiveDerefZdd(dd, zPos0); + Cudd_RecursiveDerefZdd(dd, zPos1); + return NULL; + } + cuddRef( zNeg1 ); + + + zRes0 = cuddZddIntersect( dd, zPos0, zPos1 ); + if ( zRes0 == NULL ) + { + Cudd_RecursiveDerefZdd(dd, zNeg1); + Cudd_RecursiveDerefZdd(dd, zPos0); + Cudd_RecursiveDerefZdd(dd, zPos1); + return NULL; + } + cuddRef( zRes0 ); + + zRes1 = cuddZddIntersect( dd, zPos0, zNeg1 ); + if ( zRes1 == NULL ) + { + Cudd_RecursiveDerefZdd(dd, zRes0); + Cudd_RecursiveDerefZdd(dd, zNeg1); + Cudd_RecursiveDerefZdd(dd, zPos0); + Cudd_RecursiveDerefZdd(dd, zPos1); + return NULL; + } + cuddRef( zRes1 ); + Cudd_RecursiveDerefZdd(dd, zNeg1); + Cudd_RecursiveDerefZdd(dd, zPos0); + Cudd_RecursiveDerefZdd(dd, zPos1); + // only zRes0 and zRes1 are refed at this point + + zRes = cuddZddGetNode( dd, 2*bFR->index, zRes1, zRes0 ); + if ( zRes == NULL ) + { + Cudd_RecursiveDerefZdd(dd, zRes0); + Cudd_RecursiveDerefZdd(dd, zRes1); + return NULL; + } + cuddDeref( zRes0 ); + cuddDeref( zRes1 ); + } + + cuddCacheInsert1( dd, extraBddSpaceEquationsPos, bF, zRes ); + return zRes; + } +} + + +/**Function************************************************************* + + Synopsis [Performs the recursive step of Extra_bddSpaceEquationsNev().] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * extraBddSpaceEquationsNeg( DdManager * dd, DdNode * bF ) +{ + DdNode * zRes; + statLine( dd ); + + if ( bF == b0 ) + return z1; + if ( bF == b1 ) + return z0; + + if ( zRes = cuddCacheLookup1Zdd(dd, extraBddSpaceEquationsNeg, bF) ) + return zRes; + else + { + DdNode * bFR, * bF0, * bF1; + DdNode * zPos0, * zPos1, * zNeg1; + DdNode * zRes, * zRes0, * zRes1; + + bFR = Cudd_Regular(bF); + if ( bFR != bF ) // bF is complemented + { + bF0 = Cudd_Not( cuddE(bFR) ); + bF1 = Cudd_Not( cuddT(bFR) ); + } + else + { + bF0 = cuddE(bFR); + bF1 = cuddT(bFR); + } + + if ( bF0 == b0 ) + { + zRes = extraBddSpaceEquationsNeg( dd, bF1 ); + if ( zRes == NULL ) + return NULL; + } + else if ( bF1 == b0 ) + { + zRes0 = extraBddSpaceEquationsNeg( dd, bF0 ); + if ( zRes0 == NULL ) + return NULL; + cuddRef( zRes0 ); + + // add the current element to the set + zRes = cuddZddGetNode( dd, 2*bFR->index, z1, zRes0 ); + if ( zRes == NULL ) + { + Cudd_RecursiveDerefZdd(dd, zRes0); + return NULL; + } + cuddDeref( zRes0 ); + } + else + { + zPos0 = extraBddSpaceEquationsNeg( dd, bF0 ); + if ( zPos0 == NULL ) + return NULL; + cuddRef( zPos0 ); + + zPos1 = extraBddSpaceEquationsNeg( dd, bF1 ); + if ( zPos1 == NULL ) + { + Cudd_RecursiveDerefZdd(dd, zPos0); + return NULL; + } + cuddRef( zPos1 ); + + zNeg1 = extraBddSpaceEquationsPos( dd, bF1 ); + if ( zNeg1 == NULL ) + { + Cudd_RecursiveDerefZdd(dd, zPos0); + Cudd_RecursiveDerefZdd(dd, zPos1); + return NULL; + } + cuddRef( zNeg1 ); + + + zRes0 = cuddZddIntersect( dd, zPos0, zPos1 ); + if ( zRes0 == NULL ) + { + Cudd_RecursiveDerefZdd(dd, zNeg1); + Cudd_RecursiveDerefZdd(dd, zPos0); + Cudd_RecursiveDerefZdd(dd, zPos1); + return NULL; + } + cuddRef( zRes0 ); + + zRes1 = cuddZddIntersect( dd, zPos0, zNeg1 ); + if ( zRes1 == NULL ) + { + Cudd_RecursiveDerefZdd(dd, zRes0); + Cudd_RecursiveDerefZdd(dd, zNeg1); + Cudd_RecursiveDerefZdd(dd, zPos0); + Cudd_RecursiveDerefZdd(dd, zPos1); + return NULL; + } + cuddRef( zRes1 ); + Cudd_RecursiveDerefZdd(dd, zNeg1); + Cudd_RecursiveDerefZdd(dd, zPos0); + Cudd_RecursiveDerefZdd(dd, zPos1); + // only zRes0 and zRes1 are refed at this point + + zRes = cuddZddGetNode( dd, 2*bFR->index, zRes1, zRes0 ); + if ( zRes == NULL ) + { + Cudd_RecursiveDerefZdd(dd, zRes0); + Cudd_RecursiveDerefZdd(dd, zRes1); + return NULL; + } + cuddDeref( zRes0 ); + cuddDeref( zRes1 ); + } + + cuddCacheInsert1( dd, extraBddSpaceEquationsNeg, bF, zRes ); + return zRes; + } +} + + + + +/**Function************************************************************* + + Synopsis [Performs the recursive step of Extra_bddSpaceFromFunctionPos().] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * extraBddSpaceFromMatrixPos( DdManager * dd, DdNode * zA ) +{ + DdNode * bRes; + statLine( dd ); + + if ( zA == z0 ) + return b1; + if ( zA == z1 ) + return b1; + + if ( bRes = cuddCacheLookup1(dd, extraBddSpaceFromMatrixPos, zA) ) + return bRes; + else + { + DdNode * bP0, * bP1; + DdNode * bN0, * bN1; + DdNode * bRes0, * bRes1; + + bP0 = extraBddSpaceFromMatrixPos( dd, cuddE(zA) ); + if ( bP0 == NULL ) + return NULL; + cuddRef( bP0 ); + + bP1 = extraBddSpaceFromMatrixPos( dd, cuddT(zA) ); + if ( bP1 == NULL ) + { + Cudd_RecursiveDeref( dd, bP0 ); + return NULL; + } + cuddRef( bP1 ); + + bRes0 = cuddBddAndRecur( dd, bP0, bP1 ); + if ( bRes0 == NULL ) + { + Cudd_RecursiveDeref( dd, bP0 ); + Cudd_RecursiveDeref( dd, bP1 ); + return NULL; + } + cuddRef( bRes0 ); + Cudd_RecursiveDeref( dd, bP0 ); + Cudd_RecursiveDeref( dd, bP1 ); + + + bN0 = extraBddSpaceFromMatrixPos( dd, cuddE(zA) ); + if ( bN0 == NULL ) + { + Cudd_RecursiveDeref( dd, bRes0 ); + return NULL; + } + cuddRef( bN0 ); + + bN1 = extraBddSpaceFromMatrixNeg( dd, cuddT(zA) ); + if ( bN1 == NULL ) + { + Cudd_RecursiveDeref( dd, bRes0 ); + Cudd_RecursiveDeref( dd, bN0 ); + return NULL; + } + cuddRef( bN1 ); + + bRes1 = cuddBddAndRecur( dd, bN0, bN1 ); + if ( bRes1 == NULL ) + { + Cudd_RecursiveDeref( dd, bRes0 ); + Cudd_RecursiveDeref( dd, bN0 ); + Cudd_RecursiveDeref( dd, bN1 ); + return NULL; + } + cuddRef( bRes1 ); + Cudd_RecursiveDeref( dd, bN0 ); + Cudd_RecursiveDeref( dd, bN1 ); + + + // consider the case when Res0 and Res1 are the same node + if ( bRes0 == bRes1 ) + bRes = bRes1; + // consider the case when Res1 is complemented + else if ( Cudd_IsComplement(bRes1) ) + { + bRes = cuddUniqueInter( dd, zA->index/2, Cudd_Not(bRes1), Cudd_Not(bRes0) ); + if ( bRes == NULL ) + { + Cudd_RecursiveDeref(dd,bRes0); + Cudd_RecursiveDeref(dd,bRes1); + return NULL; + } + bRes = Cudd_Not(bRes); + } + else + { + bRes = cuddUniqueInter( dd, zA->index/2, bRes1, bRes0 ); + if ( bRes == NULL ) + { + Cudd_RecursiveDeref(dd,bRes0); + Cudd_RecursiveDeref(dd,bRes1); + return NULL; + } + } + cuddDeref( bRes0 ); + cuddDeref( bRes1 ); + + cuddCacheInsert1( dd, extraBddSpaceFromMatrixPos, zA, bRes ); + return bRes; + } +} + + +/**Function************************************************************* + + Synopsis [Performs the recursive step of Extra_bddSpaceFromFunctionPos().] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * extraBddSpaceFromMatrixNeg( DdManager * dd, DdNode * zA ) +{ + DdNode * bRes; + statLine( dd ); + + if ( zA == z0 ) + return b1; + if ( zA == z1 ) + return b0; + + if ( bRes = cuddCacheLookup1(dd, extraBddSpaceFromMatrixNeg, zA) ) + return bRes; + else + { + DdNode * bP0, * bP1; + DdNode * bN0, * bN1; + DdNode * bRes0, * bRes1; + + bP0 = extraBddSpaceFromMatrixNeg( dd, cuddE(zA) ); + if ( bP0 == NULL ) + return NULL; + cuddRef( bP0 ); + + bP1 = extraBddSpaceFromMatrixNeg( dd, cuddT(zA) ); + if ( bP1 == NULL ) + { + Cudd_RecursiveDeref( dd, bP0 ); + return NULL; + } + cuddRef( bP1 ); + + bRes0 = cuddBddAndRecur( dd, bP0, bP1 ); + if ( bRes0 == NULL ) + { + Cudd_RecursiveDeref( dd, bP0 ); + Cudd_RecursiveDeref( dd, bP1 ); + return NULL; + } + cuddRef( bRes0 ); + Cudd_RecursiveDeref( dd, bP0 ); + Cudd_RecursiveDeref( dd, bP1 ); + + + bN0 = extraBddSpaceFromMatrixNeg( dd, cuddE(zA) ); + if ( bN0 == NULL ) + { + Cudd_RecursiveDeref( dd, bRes0 ); + return NULL; + } + cuddRef( bN0 ); + + bN1 = extraBddSpaceFromMatrixPos( dd, cuddT(zA) ); + if ( bN1 == NULL ) + { + Cudd_RecursiveDeref( dd, bRes0 ); + Cudd_RecursiveDeref( dd, bN0 ); + return NULL; + } + cuddRef( bN1 ); + + bRes1 = cuddBddAndRecur( dd, bN0, bN1 ); + if ( bRes1 == NULL ) + { + Cudd_RecursiveDeref( dd, bRes0 ); + Cudd_RecursiveDeref( dd, bN0 ); + Cudd_RecursiveDeref( dd, bN1 ); + return NULL; + } + cuddRef( bRes1 ); + Cudd_RecursiveDeref( dd, bN0 ); + Cudd_RecursiveDeref( dd, bN1 ); + + + // consider the case when Res0 and Res1 are the same node + if ( bRes0 == bRes1 ) + bRes = bRes1; + // consider the case when Res1 is complemented + else if ( Cudd_IsComplement(bRes1) ) + { + bRes = cuddUniqueInter( dd, zA->index/2, Cudd_Not(bRes1), Cudd_Not(bRes0) ); + if ( bRes == NULL ) + { + Cudd_RecursiveDeref(dd,bRes0); + Cudd_RecursiveDeref(dd,bRes1); + return NULL; + } + bRes = Cudd_Not(bRes); + } + else + { + bRes = cuddUniqueInter( dd, zA->index/2, bRes1, bRes0 ); + if ( bRes == NULL ) + { + Cudd_RecursiveDeref(dd,bRes0); + Cudd_RecursiveDeref(dd,bRes1); + return NULL; + } + } + cuddDeref( bRes0 ); + cuddDeref( bRes1 ); + + cuddCacheInsert1( dd, extraBddSpaceFromMatrixNeg, zA, bRes ); + return bRes; + } +} + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + diff --git a/abc_with_bb_support/src/misc/extra/extraBddCas.c b/abc_with_bb_support/src/misc/extra/extraBddCas.c new file mode 100644 index 000000000..6a484c650 --- /dev/null +++ b/abc_with_bb_support/src/misc/extra/extraBddCas.c @@ -0,0 +1,1230 @@ +/**CFile**************************************************************** + + FileName [extraBddCas.c] + + PackageName [extra] + + Synopsis [Procedures related to LUT cascade synthesis.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - September 1, 2003.] + + Revision [$Id: extraBddCas.c,v 1.0 2003/05/21 18:03:50 alanmi Exp $] + +***********************************************************************/ + +#include "extra.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +// the table to store cofactor operations +#define _TABLESIZE_COF 51113 +typedef struct +{ + unsigned Sign; + DdNode * Arg1; +} _HashEntry_cof; +_HashEntry_cof HHTable1[_TABLESIZE_COF]; + +// the table to store the result of computation of the number of minterms +#define _TABLESIZE_MINT 15113 +typedef struct +{ + DdNode * Arg1; + unsigned Arg2; + unsigned Res; +} _HashEntry_mint; +_HashEntry_mint HHTable2[_TABLESIZE_MINT]; + +typedef struct +{ + int nEdges; // the number of in-coming edges of the node + DdNode * bSum; // the sum of paths of the incoming edges +} traventry; + +// the signature used for hashing +static unsigned s_Signature = 1; + +static int s_CutLevel = 0; + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +// because the proposed solution to the optimal encoding problem has exponential complexity +// we limit the depth of the branch and bound procedure to 5 levels +static int s_MaxDepth = 5; + +static int s_nVarsBest; // the number of vars in the best ordering +static int s_VarOrderBest[32]; // storing the best ordering of vars in the "simple encoding" +static int s_VarOrderCur[32]; // storing the current ordering of vars + +// the place to store the supports of the encoded function +static DdNode * s_Field[8][256]; // the size should be K, 2^K, where K is no less than MaxDepth +static DdNode * s_Encoded; // this is the original function +static DdNode * s_VarAll; // the set of all column variables +static int s_MultiStart; // the total number of encoding variables used +// the array field now stores the supports + +static DdNode ** s_pbTemp; // the temporary storage for the columns + +static int s_BackTracks; +static int s_BackTrackLimit = 100; + +static DdNode * s_Terminal; // the terminal value for counting minterms + + +static int s_EncodingVarsLevel; + + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static DdNode * CreateTheCodes_rec( DdManager * dd, DdNode * bEncoded, int Level, DdNode ** pCVars ); +static void EvaluateEncodings_rec( DdManager * dd, DdNode * bVarsCol, int nVarsCol, int nMulti, int Level ); +// functions called from EvaluateEncodings_rec() +static DdNode * ComputeVarSetAndCountMinterms( DdManager * dd, DdNode * bVars, DdNode * bVarTop, unsigned * Cost ); +static DdNode * ComputeVarSetAndCountMinterms2( DdManager * dd, DdNode * bVars, DdNode * bVarTop, unsigned * Cost ); +unsigned Extra_CountCofactorMinterms( DdManager * dd, DdNode * bFunc, DdNode * bVarsCof, DdNode * bVarsAll ); +static unsigned Extra_CountMintermsSimple( DdNode * bFunc, unsigned max ); + +static void CountNodeVisits_rec( DdManager * dd, DdNode * aFunc, st_table * Visited ); +static void CollectNodesAndComputePaths_rec( DdManager * dd, DdNode * aFunc, DdNode * bCube, st_table * Visited, st_table * CutNodes ); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Performs the binary encoding of the set of function using the given vars.] + + Description [Performs a straight binary encoding of the set of functions using + the variable cubes formed from the given set of variables. ] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * +Extra_bddEncodingBinary( + DdManager * dd, + DdNode ** pbFuncs, // pbFuncs is the array of columns to be encoded + int nFuncs, // nFuncs is the number of columns in the array + DdNode ** pbVars, // pbVars is the array of variables to use for the codes + int nVars ) // nVars is the column multiplicity, [log2(nFuncs)] +{ + int i; + DdNode * bResult; + DdNode * bCube, * bTemp, * bProd; + + assert( nVars >= Extra_Base2Log(nFuncs) ); + + bResult = b0; Cudd_Ref( bResult ); + for ( i = 0; i < nFuncs; i++ ) + { + bCube = Extra_bddBitsToCube( dd, i, nVars, pbVars, 1 ); Cudd_Ref( bCube ); + bProd = Cudd_bddAnd( dd, bCube, pbFuncs[i] ); Cudd_Ref( bProd ); + Cudd_RecursiveDeref( dd, bCube ); + + bResult = Cudd_bddOr( dd, bProd, bTemp = bResult ); Cudd_Ref( bResult ); + Cudd_RecursiveDeref( dd, bTemp ); + Cudd_RecursiveDeref( dd, bProd ); + } + + Cudd_Deref( bResult ); + return bResult; +} /* end of Extra_bddEncodingBinary */ + + +/**Function******************************************************************** + + Synopsis [Solves the column encoding problem using a sophisticated method.] + + Description [The encoding is based on the idea of deriving functions which + depend on only one variable, which corresponds to the case of non-disjoint + decompostion. It is assumed that the variables pCVars are ordered below the variables + representing the solumns, and the first variable pCVars[0] is the topmost one.] + + SideEffects [] + + SeeAlso [Extra_bddEncodingBinary] + +******************************************************************************/ + +DdNode * +Extra_bddEncodingNonStrict( + DdManager * dd, + DdNode ** pbColumns, // pbColumns is the array of columns to be encoded; + int nColumns, // nColumns is the number of columns in the array + DdNode * bVarsCol, // bVarsCol is the cube of variables on which the columns depend + DdNode ** pCVars, // pCVars is the array of variables to use for the codes + int nMulti, // nMulti is the column multiplicity, [log2(nColumns)] + int * pSimple ) // pSimple gets the number of code variables taken from the input varibles without change +{ + DdNode * bEncoded, * bResult; + int nVarsCol = Cudd_SupportSize(dd,bVarsCol); + long clk; + + // cannot work with more that 32-bit codes + assert( nMulti < 32 ); + + // perform the preliminary encoding using the straight binary code + bEncoded = Extra_bddEncodingBinary( dd, pbColumns, nColumns, pCVars, nMulti ); Cudd_Ref( bEncoded ); + //printf( "Node count = %d", Cudd_DagSize(bEncoded) ); + + // set the backgroup value for counting minterms + s_Terminal = b0; + // set the level of the encoding variables + s_EncodingVarsLevel = dd->invperm[pCVars[0]->index]; + + // the current number of backtracks + s_BackTracks = 0; + // the variables that are cofactored on the topmost level where everything starts (no vars) + s_Field[0][0] = b1; + // the size of the best set of "simple" encoding variables found so far + s_nVarsBest = 0; + + // set the relation to be accessible to traversal procedures + s_Encoded = bEncoded; + // the set of all vars to be accessible to traversal procedures + s_VarAll = bVarsCol; + // the column multiplicity + s_MultiStart = nMulti; + + + clk = clock(); + // find the simplest encoding + if ( nColumns > 2 ) + EvaluateEncodings_rec( dd, bVarsCol, nVarsCol, nMulti, 1 ); +// printf( "The number of backtracks = %d\n", s_BackTracks ); +// s_EncSearchTime += clock() - clk; + + // allocate the temporary storage for the columns + s_pbTemp = (DdNode **) malloc( nColumns * sizeof(DdNode *) ); + +// clk = clock(); + bResult = CreateTheCodes_rec( dd, bEncoded, 0, pCVars ); Cudd_Ref( bResult ); +// s_EncComputeTime += clock() - clk; + + // delocate the preliminarily encoded set + Cudd_RecursiveDeref( dd, bEncoded ); +// Cudd_RecursiveDeref( dd, aEncoded ); + + free( s_pbTemp ); + + *pSimple = s_nVarsBest; + Cudd_Deref( bResult ); + return bResult; +} + +/**Function******************************************************************** + + Synopsis [Collects the nodes under the cut and, for each node, computes the sum of paths leading to it from the root.] + + Description [The table returned contains the set of BDD nodes pointed to under the cut + and, for each node, the BDD of the sum of paths leading to this node from the root + The sums of paths in the table are referenced. CutLevel is the first DD level + considered to be under the cut.] + + SideEffects [] + + SeeAlso [Extra_bddNodePaths] + +******************************************************************************/ +st_table * Extra_bddNodePathsUnderCut( DdManager * dd, DdNode * bFunc, int CutLevel ) +{ + st_table * Visited; // temporary table to remember the visited nodes + st_table * CutNodes; // the result goes here + st_table * Result; // the result goes here + DdNode * aFunc; + + s_CutLevel = CutLevel; + + Result = st_init_table(st_ptrcmp,st_ptrhash); + // the terminal cases + if ( Cudd_IsConstant( bFunc ) ) + { + if ( bFunc == b1 ) + { + st_insert( Result, (char*)b1, (char*)b1 ); + Cudd_Ref( b1 ); + Cudd_Ref( b1 ); + } + else + { + st_insert( Result, (char*)b0, (char*)b0 ); + Cudd_Ref( b0 ); + Cudd_Ref( b0 ); + } + return Result; + } + + // create the ADD to simplify processing (no complemented edges) + aFunc = Cudd_BddToAdd( dd, bFunc ); Cudd_Ref( aFunc ); + + // Step 1: Start the tables and collect information about the nodes above the cut + // this information tells how many edges point to each node + Visited = st_init_table(st_ptrcmp,st_ptrhash); + CutNodes = st_init_table(st_ptrcmp,st_ptrhash); + + CountNodeVisits_rec( dd, aFunc, Visited ); + + // Step 2: Traverse the BDD using the visited table and compute the sum of paths + CollectNodesAndComputePaths_rec( dd, aFunc, b1, Visited, CutNodes ); + + // at this point the table of cut nodes is ready and the table of visited is useless + { + st_generator * gen; + DdNode * aNode; + traventry * p; + st_foreach_item( Visited, gen, (char**)&aNode, (char**)&p ) + { + Cudd_RecursiveDeref( dd, p->bSum ); + free( p ); + } + st_free_table( Visited ); + } + + // go through the table CutNodes and create the BDD and the path to be returned + { + st_generator * gen; + DdNode * aNode, * bNode, * bSum; + st_foreach_item( CutNodes, gen, (char**)&aNode, (char**)&bSum) + { + // aNode is not referenced, because aFunc is holding it + bNode = Cudd_addBddPattern( dd, aNode ); Cudd_Ref( bNode ); + st_insert( Result, (char*)bNode, (char*)bSum ); + // the new table takes both refs + } + st_free_table( CutNodes ); + } + + // dereference the ADD + Cudd_RecursiveDeref( dd, aFunc ); + + // return the table + return Result; + +} /* end of Extra_bddNodePathsUnderCut */ + +/**Function******************************************************************** + + Synopsis [Collects the nodes under the cut in the ADD starting from the given set of ADD nodes.] + + Description [Takes the array, paNodes, of ADD nodes to start the traversal, + the array, pbCubes, of BDD cubes to start the traversal with in each node, + and the number, nNodes, of ADD nodes and BDD cubes in paNodes and pbCubes. + Returns the number of columns found. Fills in paNodesRes (pbCubesRes) + with the set of ADD columns (BDD paths). These arrays should be allocated + by the user.] + + SideEffects [] + + SeeAlso [Extra_bddNodePaths] + +******************************************************************************/ +int Extra_bddNodePathsUnderCutArray( DdManager * dd, DdNode ** paNodes, DdNode ** pbCubes, int nNodes, DdNode ** paNodesRes, DdNode ** pbCubesRes, int CutLevel ) +{ + st_table * Visited; // temporary table to remember the visited nodes + st_table * CutNodes; // the nodes under the cut go here + int i, Counter; + + s_CutLevel = CutLevel; + + // there should be some nodes + assert( nNodes > 0 ); + if ( nNodes == 1 && Cudd_IsConstant( paNodes[0] ) ) + { + if ( paNodes[0] == a1 ) + { + paNodesRes[0] = a1; Cudd_Ref( a1 ); + pbCubesRes[0] = pbCubes[0]; Cudd_Ref( pbCubes[0] ); + } + else + { + paNodesRes[0] = a0; Cudd_Ref( a0 ); + pbCubesRes[0] = pbCubes[0]; Cudd_Ref( pbCubes[0] ); + } + return 1; + } + + // Step 1: Start the table and collect information about the nodes above the cut + // this information tells how many edges point to each node + CutNodes = st_init_table(st_ptrcmp,st_ptrhash); + Visited = st_init_table(st_ptrcmp,st_ptrhash); + + for ( i = 0; i < nNodes; i++ ) + CountNodeVisits_rec( dd, paNodes[i], Visited ); + + // Step 2: Traverse the BDD using the visited table and compute the sum of paths + for ( i = 0; i < nNodes; i++ ) + CollectNodesAndComputePaths_rec( dd, paNodes[i], pbCubes[i], Visited, CutNodes ); + + // at this point, the table of cut nodes is ready and the table of visited is useless + { + st_generator * gen; + DdNode * aNode; + traventry * p; + st_foreach_item( Visited, gen, (char**)&aNode, (char**)&p ) + { + Cudd_RecursiveDeref( dd, p->bSum ); + free( p ); + } + st_free_table( Visited ); + } + + // go through the table CutNodes and create the BDD and the path to be returned + { + st_generator * gen; + DdNode * aNode, * bSum; + Counter = 0; + st_foreach_item( CutNodes, gen, (char**)&aNode, (char**)&bSum) + { + paNodesRes[Counter] = aNode; Cudd_Ref( aNode ); + pbCubesRes[Counter] = bSum; + Counter++; + } + st_free_table( CutNodes ); + } + + // return the number of cofactors found + return Counter; + +} /* end of Extra_bddNodePathsUnderCutArray */ + +/**Function************************************************************* + + Synopsis [Collects all the BDD nodes into the table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void extraCollectNodes( DdNode * Func, st_table * tNodes ) +{ + DdNode * FuncR; + FuncR = Cudd_Regular(Func); + if ( st_find_or_add( tNodes, (char*)FuncR, NULL ) ) + return; + if ( cuddIsConstant(FuncR) ) + return; + extraCollectNodes( cuddE(FuncR), tNodes ); + extraCollectNodes( cuddT(FuncR), tNodes ); +} + +/**Function************************************************************* + + Synopsis [Collects all the nodes of one DD into the table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +st_table * Extra_CollectNodes( DdNode * Func ) +{ + st_table * tNodes; + tNodes = st_init_table( st_ptrcmp, st_ptrhash ); + extraCollectNodes( Func, tNodes ); + return tNodes; +} + +/**Function************************************************************* + + Synopsis [Updates the topmost level from which the given node is referenced.] + + Description [Takes the table which maps each BDD nodes (including the constants) + into the topmost level on which this node counts as a cofactor. Takes the topmost + level, on which this node counts as a cofactor (see Extra_ProfileWidthFast(). + Takes the node, for which the table entry should be updated.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void extraProfileUpdateTopLevel( st_table * tNodeTopRef, int TopLevelNew, DdNode * node ) +{ + int * pTopLevel; + + if ( st_find_or_add( tNodeTopRef, (char*)node, (char***)&pTopLevel ) ) + { // the node is already referenced + // the current top level should be updated if it is larger than the new level + if ( *pTopLevel > TopLevelNew ) + *pTopLevel = TopLevelNew; + } + else + { // the node is not referenced + // its level should be set to the current new level + *pTopLevel = TopLevelNew; + } +} +/**Function************************************************************* + + Synopsis [Fast computation of the BDD profile.] + + Description [The array to store the profile is given by the user and should + contain at least as many entries as there is the maximum of the BDD/ZDD + size of the manager PLUS ONE. + When we say that the widths of the DD on level L is W, we mean the following. + Let us create the cut between the level L-1 and the level L and count the number + of different DD nodes pointed to across the cut. This number is the width W. + From this it follows the on level 0, the width is equal to the number of external + pointers to the considered DDs. If there is only one DD, then the profile on + level 0 is always 1. If this DD is rooted in the topmost variable, then the width + on level 1 is always 2, etc. The width at the level equal to dd->size is the + number of terminal nodes in the DD. (Because we consider the first level #0 + and the last level #dd->size, the profile array should contain dd->size+1 entries.) + ] + + SideEffects [This procedure will not work for BDDs w/ complement edges, only for ADDs and ZDDs] + + SeeAlso [] + +***********************************************************************/ +int Extra_ProfileWidth( DdManager * dd, DdNode * Func, int * pProfile, int CutLevel ) +{ + st_generator * gen; + st_table * tNodeTopRef; // this table stores the top level from which this node is pointed to + st_table * tNodes; + DdNode * node; + DdNode * nodeR; + int LevelStart, Limit; + int i, size; + int WidthMax; + + // start the mapping table + tNodeTopRef = st_init_table(st_ptrcmp,st_ptrhash); + // add the topmost node to the profile + extraProfileUpdateTopLevel( tNodeTopRef, 0, Func ); + + // collect all nodes + tNodes = Extra_CollectNodes( Func ); + // go though all the nodes and set the top level the cofactors are pointed from +// Cudd_ForeachNode( dd, Func, genDD, node ) + st_foreach_item( tNodes, gen, (char**)&node, NULL ) + { +// assert( Cudd_Regular(node) ); // this procedure works only with ADD/ZDD (not BDD w/ compl.edges) + nodeR = Cudd_Regular(node); + if ( cuddIsConstant(nodeR) ) + continue; + // this node is not a constant - consider its cofactors + extraProfileUpdateTopLevel( tNodeTopRef, dd->perm[node->index]+1, cuddE(nodeR) ); + extraProfileUpdateTopLevel( tNodeTopRef, dd->perm[node->index]+1, cuddT(nodeR) ); + } + st_free_table( tNodes ); + + // clean the profile + size = ddMax(dd->size, dd->sizeZ) + 1; + for ( i = 0; i < size; i++ ) + pProfile[i] = 0; + + // create the profile + st_foreach_item( tNodeTopRef, gen, (char**)&node, (char**)&LevelStart ) + { + nodeR = Cudd_Regular(node); + Limit = (cuddIsConstant(nodeR))? dd->size: dd->perm[nodeR->index]; + for ( i = LevelStart; i <= Limit; i++ ) + pProfile[i]++; + } + + if ( CutLevel != -1 && CutLevel != 0 ) + size = CutLevel; + + // get the max width + WidthMax = 0; + for ( i = 0; i < size; i++ ) + if ( WidthMax < pProfile[i] ) + WidthMax = pProfile[i]; + + // deref the table + st_free_table( tNodeTopRef ); + + return WidthMax; +} /* end of Extra_ProfileWidth */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Computes the non-strict codes when evaluation is finished.] + + Description [The information about the best code is stored in s_VarOrderBest, + which has s_nVarsBest entries.] + + SideEffects [None] + +******************************************************************************/ +DdNode * CreateTheCodes_rec( DdManager * dd, DdNode * bEncoded, int Level, DdNode ** pCVars ) +// bEncoded is the preliminarily encoded set of columns +// Level is the current level in the recursion +// pCVars are the variables to be used for encoding +{ + DdNode * bRes; + if ( Level == s_nVarsBest ) + { // the terminal case, when we need to remap the encoded function + // from the preliminary encoded variables to the new ones + st_table * CutNodes; + int nCols; +// double nMints; +/* +#ifdef _DEBUG + + { + DdNode * bTemp; + // make sure that the given number of variables is enough + bTemp = Cudd_bddExistAbstract( dd, bEncoded, s_VarAll ); Cudd_Ref( bTemp ); +// nMints = Cudd_CountMinterm( dd, bTemp, s_MultiStart ); + nMints = Extra_CountMintermsSimple( bTemp, (1< Extra_Power2( s_MultiStart-Level ) ) + { // the number of minterms is too large to encode the columns + // using the given minimum number of encoding variables + assert( 0 ); + } + Cudd_RecursiveDeref( dd, bTemp ); + } +#endif +*/ + // get the columns to be re-encoded + CutNodes = Extra_bddNodePathsUnderCut( dd, bEncoded, s_EncodingVarsLevel ); + // LUT size is the cut level because because the temporary encoding variables + // are above the functional variables - this is not true!!! + // the temporary variables are below! + + // put the entries from the table into the temporary array + { + st_generator * gen; + DdNode * bColumn, * bCode; + nCols = 0; + st_foreach_item( CutNodes, gen, (char**)&bCode, (char**)&bColumn ) + { + if ( bCode == b0 ) + { // the unused part of the columns + Cudd_RecursiveDeref( dd, bColumn ); + Cudd_RecursiveDeref( dd, bCode ); + continue; + } + else + { + s_pbTemp[ nCols ] = bColumn; // takes ref + Cudd_RecursiveDeref( dd, bCode ); + nCols++; + } + } + st_free_table( CutNodes ); +// assert( nCols == (int)nMints ); + } + + // encode the columns + if ( s_MultiStart-Level == 0 ) // we reached the bottom level of recursion + { + assert( nCols == 1 ); +// assert( (int)nMints == 1 ); + bRes = s_pbTemp[0]; Cudd_Ref( bRes ); + } + else + { + bRes = Extra_bddEncodingBinary( dd, s_pbTemp, nCols, pCVars+Level, s_MultiStart-Level ); Cudd_Ref( bRes ); + } + + // deref the columns + { + int i; + for ( i = 0; i < nCols; i++ ) + Cudd_RecursiveDeref( dd, s_pbTemp[i] ); + } + } + else + { + // cofactor the problem as specified in the best solution + DdNode * bCof0, * bCof1; + DdNode * bRes0, * bRes1; + DdNode * bProd0, * bProd1; + DdNode * bTemp; + DdNode * bVarNext = dd->vars[ s_VarOrderBest[Level] ]; + + bCof0 = Cudd_Cofactor( dd, bEncoded, Cudd_Not( bVarNext ) ); Cudd_Ref( bCof0 ); + bCof1 = Cudd_Cofactor( dd, bEncoded, bVarNext ); Cudd_Ref( bCof1 ); + + // call recursively + bRes0 = CreateTheCodes_rec( dd, bCof0, Level+1, pCVars ); Cudd_Ref( bRes0 ); + bRes1 = CreateTheCodes_rec( dd, bCof1, Level+1, pCVars ); Cudd_Ref( bRes1 ); + + Cudd_RecursiveDeref( dd, bCof0 ); + Cudd_RecursiveDeref( dd, bCof1 ); + + // compose the result using the identity (bVarNext <=> pCVars[Level]) - this is wrong! + // compose the result as follows: x'y'F0 + xyF1 + bProd0 = Cudd_bddAnd( dd, Cudd_Not(bVarNext), Cudd_Not(pCVars[Level]) ); Cudd_Ref( bProd0 ); + bProd1 = Cudd_bddAnd( dd, bVarNext , pCVars[Level] ); Cudd_Ref( bProd1 ); + + bProd0 = Cudd_bddAnd( dd, bTemp = bProd0, bRes0 ); Cudd_Ref( bProd0 ); + Cudd_RecursiveDeref( dd, bTemp ); + Cudd_RecursiveDeref( dd, bRes0 ); + + bProd1 = Cudd_bddAnd( dd, bTemp = bProd1, bRes1 ); Cudd_Ref( bProd1 ); + Cudd_RecursiveDeref( dd, bTemp ); + Cudd_RecursiveDeref( dd, bRes1 ); + + bRes = Cudd_bddOr( dd, bProd0, bProd1 ); Cudd_Ref( bRes ); + + Cudd_RecursiveDeref( dd, bProd0 ); + Cudd_RecursiveDeref( dd, bProd1 ); + } + Cudd_Deref( bRes ); + return bRes; +} + +/**Function******************************************************************** + + Synopsis [Computes the current set of variables and counts the number of minterms.] + + Description [Old implementation.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void EvaluateEncodings_rec( DdManager * dd, DdNode * bVarsCol, int nVarsCol, int nMulti, int Level ) +// bVarsCol is the set of remaining variables +// nVarsCol is the number of remaining variables +// nMulti is the number of encoding variables to be used +// Level is the level of recursion, from which this function is called +// if we successfully finish this procedure, Level also stands for how many encoding variabled we saved +{ + int i, k; + int nEntries = (1<<(Level-1)); // the number of entries in the field of the previous level + DdNode * bVars0, * bVars1; // the cofactors + unsigned nMint0, nMint1; // the number of minterms + DdNode * bTempV; + DdNode * bVarTop; + int fBreak; + + + // there is no need to search above this level + if ( Level > s_MaxDepth ) + return; + + // if there are no variables left, quit the research + if ( bVarsCol == b1 ) + return; + + if ( s_BackTracks > s_BackTrackLimit ) + return; + + s_BackTracks++; + + // otherwise, go through the remaining variables + for ( bTempV = bVarsCol; bTempV != b1; bTempV = cuddT(bTempV) ) + { + // the currently tested variable + bVarTop = dd->vars[bTempV->index]; + + // put it into the array + s_VarOrderCur[Level-1] = bTempV->index; + + // go through the entries and fill them out by cofactoring + fBreak = 0; + for ( i = 0; i < nEntries; i++ ) + { + bVars0 = ComputeVarSetAndCountMinterms( dd, s_Field[Level-1][i], Cudd_Not(bVarTop), &nMint0 ); + Cudd_Ref( bVars0 ); + + if ( nMint0 > Extra_Power2( nMulti-1 ) ) + { + // there is no way to encode - dereference and return + Cudd_RecursiveDeref( dd, bVars0 ); + fBreak = 1; + break; + } + + bVars1 = ComputeVarSetAndCountMinterms( dd, s_Field[Level-1][i], bVarTop, &nMint1 ); + Cudd_Ref( bVars1 ); + + if ( nMint1 > Extra_Power2( nMulti-1 ) ) + { + // there is no way to encode - dereference and return + Cudd_RecursiveDeref( dd, bVars0 ); + Cudd_RecursiveDeref( dd, bVars1 ); + fBreak = 1; + break; + } + + // otherwise, add these two cofactors + s_Field[Level][2*i + 0] = bVars0; // takes ref + s_Field[Level][2*i + 1] = bVars1; // takes ref + } + + if ( !fBreak ) + { + DdNode * bVarsRem; + // if we ended up here, it means that the cofactors w.r.t. variable bVarTop satisfy the condition + // save this situation + if ( s_nVarsBest < Level ) + { + s_nVarsBest = Level; + // copy the variable assignment + for ( k = 0; k < Level; k++ ) + s_VarOrderBest[k] = s_VarOrderCur[k]; + } + + // call recursively + // get the new variable set + if ( nMulti-1 > 0 ) + { + bVarsRem = Cudd_bddExistAbstract( dd, bVarsCol, bVarTop ); Cudd_Ref( bVarsRem ); + EvaluateEncodings_rec( dd, bVarsRem, nVarsCol-1, nMulti-1, Level+1 ); + Cudd_RecursiveDeref( dd, bVarsRem ); + } + } + + // deref the contents of the array + for ( k = 0; k < i; k++ ) + { + Cudd_RecursiveDeref( dd, s_Field[Level][2*k + 0] ); + Cudd_RecursiveDeref( dd, s_Field[Level][2*k + 1] ); + } + + // if the solution is found, there is no need to continue + if ( s_nVarsBest == s_MaxDepth ) + return; + + // if the solution is found, there is no need to continue + if ( s_nVarsBest == s_MultiStart ) + return; + } + // at this point, we have tried all possible directions in the space of variables +} + +/**Function******************************************************************** + + Synopsis [Computes the current set of variables and counts the number of minterms.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * ComputeVarSetAndCountMinterms( DdManager * dd, DdNode * bVars, DdNode * bVarTop, unsigned * Cost ) +// takes bVars - the variables cofactored so far (some of them may be in negative polarity) +// bVarTop - the topmost variable w.r.t. which to cofactor (may be in negative polarity) +// returns the cost and the new set of variables (bVars & bVarTop) +{ + DdNode * bVarsRes; + + // get the resulting set of variables + bVarsRes = Cudd_bddAnd( dd, bVars, bVarTop ); Cudd_Ref( bVarsRes ); + + // increment signature before calling Cudd_CountCofactorMinterms() + s_Signature++; + *Cost = Extra_CountCofactorMinterms( dd, s_Encoded, bVarsRes, s_VarAll ); + + Cudd_Deref( bVarsRes ); +// s_CountCalls++; + return bVarsRes; +} + +/**Function******************************************************************** + + Synopsis [Computes the current set of variables and counts the number of minterms.] + + Description [The old implementation, which is approximately 4 times slower.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * ComputeVarSetAndCountMinterms2( DdManager * dd, DdNode * bVars, DdNode * bVarTop, unsigned * Cost ) +{ + DdNode * bVarsRes; + DdNode * bCof, * bFun; + + bVarsRes = Cudd_bddAnd( dd, bVars, bVarTop ); Cudd_Ref( bVarsRes ); + + bCof = Cudd_Cofactor( dd, s_Encoded, bVarsRes ); Cudd_Ref( bCof ); + bFun = Cudd_bddExistAbstract( dd, bCof, s_VarAll ); Cudd_Ref( bFun ); + *Cost = (unsigned)Cudd_CountMinterm( dd, bFun, s_MultiStart ); + Cudd_RecursiveDeref( dd, bFun ); + Cudd_RecursiveDeref( dd, bCof ); + + Cudd_Deref( bVarsRes ); +// s_CountCalls++; + return bVarsRes; +} + + +/**Function******************************************************************** + + Synopsis [Counts the number of encoding minterms pointed to by the cofactor of the function.] + + Description [] + + SideEffects [None] + +******************************************************************************/ +unsigned Extra_CountCofactorMinterms( DdManager * dd, DdNode * bFunc, DdNode * bVarsCof, DdNode * bVarsAll ) +// this function computes how many minterms depending on the encoding variables +// are there in the cofactor of bFunc w.r.t. variables bVarsCof +// bFunc is assumed to depend on variables s_VarsAll +// the variables s_VarsAll should be ordered above the encoding variables +{ + unsigned HKey; + DdNode * bFuncR; + + // if the function is zero, there are no minterms +// if ( bFunc == b0 ) +// return 0; + +// if ( st_lookup(Visited, (char*)bFunc, NULL) ) +// return 0; + +// HKey = hashKey2c( s_Signature, bFuncR ); +// if ( HHTable1[HKey].Sign == s_Signature && HHTable1[HKey].Arg1 == bFuncR ) // this node is visited +// return 0; + + + // check the hash-table + bFuncR = Cudd_Regular(bFunc); +// HKey = hashKey2( s_Signature, bFuncR, _TABLESIZE_COF ); + HKey = hashKey2( s_Signature, bFunc, _TABLESIZE_COF ); + for ( ; HHTable1[HKey].Sign == s_Signature; HKey = (HKey+1) % _TABLESIZE_COF ) +// if ( HHTable1[HKey].Arg1 == bFuncR ) // this node is visited + if ( HHTable1[HKey].Arg1 == bFunc ) // this node is visited + return 0; + + + // if the function is already the code + if ( dd->perm[bFuncR->index] >= s_EncodingVarsLevel ) + { +// st_insert(Visited, (char*)bFunc, NULL); + +// HHTable1[HKey].Sign = s_Signature; +// HHTable1[HKey].Arg1 = bFuncR; + + assert( HHTable1[HKey].Sign != s_Signature ); + HHTable1[HKey].Sign = s_Signature; +// HHTable1[HKey].Arg1 = bFuncR; + HHTable1[HKey].Arg1 = bFunc; + + return Extra_CountMintermsSimple( bFunc, (1<perm[bFuncR->index]; + int LevelC = cuddI(dd,bVarsCofR->index); + int LevelA = dd->perm[bVarsAll->index]; + + int LevelTop = LevelF; + + if ( LevelTop > LevelC ) + LevelTop = LevelC; + + if ( LevelTop > LevelA ) + LevelTop = LevelA; + + // the top var in the function or in cofactoring vars always belongs to the set of all vars + assert( !( LevelTop == LevelF || LevelTop == LevelC ) || LevelTop == LevelA ); + + // cofactor the function + if ( LevelTop == LevelF ) + { + if ( bFuncR != bFunc ) // bFunc is complemented + { + bFunc0 = Cudd_Not( cuddE(bFuncR) ); + bFunc1 = Cudd_Not( cuddT(bFuncR) ); + } + else + { + bFunc0 = cuddE(bFuncR); + bFunc1 = cuddT(bFuncR); + } + } + else // bVars is higher in the variable order + bFunc0 = bFunc1 = bFunc; + + // cofactor the cube + if ( LevelTop == LevelC ) + { + if ( bVarsCofR != bVarsCof ) // bFunc is complemented + { + bVarsCof0 = Cudd_Not( cuddE(bVarsCofR) ); + bVarsCof1 = Cudd_Not( cuddT(bVarsCofR) ); + } + else + { + bVarsCof0 = cuddE(bVarsCofR); + bVarsCof1 = cuddT(bVarsCofR); + } + } + else // bVars is higher in the variable order + bVarsCof0 = bVarsCof1 = bVarsCof; + + // there are two cases: + // (1) the top variable belongs to the cofactoring variables + // (2) the top variable does not belong to the cofactoring variables + + // (1) the top variable belongs to the cofactoring variables + Res = 0; + if ( LevelTop == LevelC ) + { + if ( bVarsCof1 == b0 ) // this is a negative cofactor + { + if ( bFunc0 != b0 ) + Res = Extra_CountCofactorMinterms( dd, bFunc0, bVarsCof0, cuddT(bVarsAll) ); + } + else // this is a positive cofactor + { + if ( bFunc1 != b0 ) + Res = Extra_CountCofactorMinterms( dd, bFunc1, bVarsCof1, cuddT(bVarsAll) ); + } + } + else + { + if ( bFunc0 != b0 ) + Res += Extra_CountCofactorMinterms( dd, bFunc0, bVarsCof0, cuddT(bVarsAll) ); + + if ( bFunc1 != b0 ) + Res += Extra_CountCofactorMinterms( dd, bFunc1, bVarsCof1, cuddT(bVarsAll) ); + } + +// st_insert(Visited, (char*)bFunc, NULL); + +// HHTable1[HKey].Sign = s_Signature; +// HHTable1[HKey].Arg1 = bFuncR; + + // skip through the entries with the same signatures + // (these might have been created at the time of recursive calls) + for ( ; HHTable1[HKey].Sign == s_Signature; HKey = (HKey+1) % _TABLESIZE_COF ); + assert( HHTable1[HKey].Sign != s_Signature ); + HHTable1[HKey].Sign = s_Signature; +// HHTable1[HKey].Arg1 = bFuncR; + HHTable1[HKey].Arg1 = bFunc; + + return Res; + } +} + +/**Function******************************************************************** + + Synopsis [Counts the number of minterms.] + + Description [This function counts minterms for functions up to 32 variables + using a local cache. The terminal value (s_Termina) should be adjusted for + BDDs and ADDs.] + + SideEffects [None] + +******************************************************************************/ +unsigned Extra_CountMintermsSimple( DdNode * bFunc, unsigned max ) +{ + unsigned HKey; + + // normalize + if ( Cudd_IsComplement(bFunc) ) + return max - Extra_CountMintermsSimple( Cudd_Not(bFunc), max ); + + // now it is known that the function is not complemented + if ( cuddIsConstant(bFunc) ) + return ((bFunc==s_Terminal)? 0: max); + + // check cache + HKey = hashKey2( bFunc, max, _TABLESIZE_MINT ); + if ( HHTable2[HKey].Arg1 == bFunc && HHTable2[HKey].Arg2 == max ) + return HHTable2[HKey].Res; + else + { + // min = min0/2 + min1/2; + unsigned min = (Extra_CountMintermsSimple( cuddE(bFunc), max ) >> 1) + + (Extra_CountMintermsSimple( cuddT(bFunc), max ) >> 1); + + HHTable2[HKey].Arg1 = bFunc; + HHTable2[HKey].Arg2 = max; + HHTable2[HKey].Res = min; + + return min; + } +} /* end of Extra_CountMintermsSimple */ + + +/**Function******************************************************************** + + Synopsis [Visits the nodes.] + + Description [Visits the nodes above the cut and the nodes pointed to below the cut; + collects the visited nodes, counts how many times each node is visited, and sets + the path-sum to be the constant zero BDD.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void CountNodeVisits_rec( DdManager * dd, DdNode * aFunc, st_table * Visited ) + +{ + traventry * p; + char **slot; + if ( st_find_or_add(Visited, (char*)aFunc, &slot) ) + { // the entry already exists + p = (traventry*) *slot; + // increment the counter of incoming edges + p->nEdges++; + return; + } + // this node has not been visited + assert( !Cudd_IsComplement(aFunc) ); + + // create the new traversal entry + p = (traventry *) malloc( sizeof(traventry) ); + // set the initial sum of edges to zero BDD + p->bSum = b0; Cudd_Ref( b0 ); + // set the starting number of incoming edges + p->nEdges = 1; + // set this entry into the slot + *slot = (char*)p; + + // recur if the node is above the cut + if ( cuddI(dd,aFunc->index) < s_CutLevel ) + { + CountNodeVisits_rec( dd, cuddE(aFunc), Visited ); + CountNodeVisits_rec( dd, cuddT(aFunc), Visited ); + } +} /* end of CountNodeVisits_rec */ + + +/**Function******************************************************************** + + Synopsis [Revisits the nodes and computes the paths.] + + Description [This function visits the nodes above the cut having the goal of + summing all the incomming BDD edges; when this function comes across the node + below the cut, it saves this node in the CutNode table.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void CollectNodesAndComputePaths_rec( DdManager * dd, DdNode * aFunc, DdNode * bCube, st_table * Visited, st_table * CutNodes ) +{ + // find the node in the visited table + DdNode * bTemp; + traventry * p; + char **slot; + if ( st_find_or_add(Visited, (char*)aFunc, &slot) ) + { // the node is found + // get the pointer to the traversal entry + p = (traventry*) *slot; + + // make sure that the counter of incoming edges is positive + assert( p->nEdges > 0 ); + + // add the cube to the currently accumulated cubes + p->bSum = Cudd_bddOr( dd, bTemp = p->bSum, bCube ); Cudd_Ref( p->bSum ); + Cudd_RecursiveDeref( dd, bTemp ); + + // decrement the number of visits + p->nEdges--; + + // if more visits to this node are expected, return + if ( p->nEdges ) + return; + else // if ( p->nEdges == 0 ) + { // this is the last visit - propagate the cube + + // check where this node is + if ( cuddI(dd,aFunc->index) < s_CutLevel ) + { // the node is above the cut + DdNode * bCube0, * bCube1; + + // get the top-most variable + DdNode * bVarTop = dd->vars[aFunc->index]; + + // compute the propagated cubes + bCube0 = Cudd_bddAnd( dd, p->bSum, Cudd_Not( bVarTop ) ); Cudd_Ref( bCube0 ); + bCube1 = Cudd_bddAnd( dd, p->bSum, bVarTop ); Cudd_Ref( bCube1 ); + + // call recursively + CollectNodesAndComputePaths_rec( dd, cuddE(aFunc), bCube0, Visited, CutNodes ); + CollectNodesAndComputePaths_rec( dd, cuddT(aFunc), bCube1, Visited, CutNodes ); + + // dereference the cubes + Cudd_RecursiveDeref( dd, bCube0 ); + Cudd_RecursiveDeref( dd, bCube1 ); + return; + } + else + { // the node is below the cut + // add this node to the cut node table, if it is not yet there + +// DdNode * bNode; +// bNode = Cudd_addBddPattern( dd, aFunc ); Cudd_Ref( bNode ); + if ( st_find_or_add(CutNodes, (char*)aFunc, &slot) ) + { // the node exists - should never happen + assert( 0 ); + } + *slot = (char*) p->bSum; Cudd_Ref( p->bSum ); + // the table takes the reference of bNode + return; + } + } + } + + // the node does not exist in the visited table - should never happen + assert(0); + +} /* end of CollectNodesAndComputePaths_rec */ + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/misc/extra/extraBddKmap.c b/abc_with_bb_support/src/misc/extra/extraBddKmap.c new file mode 100644 index 000000000..9e97f7368 --- /dev/null +++ b/abc_with_bb_support/src/misc/extra/extraBddKmap.c @@ -0,0 +1,783 @@ +/**CFile**************************************************************** + + FileName [extraBddKmap.c] + + PackageName [extra] + + Synopsis [Visualizing the K-map.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - September 1, 2003.] + + Revision [$Id: extraBddKmap.c,v 1.0 2003/05/21 18:03:50 alanmi Exp $] + +***********************************************************************/ + +/// K-map visualization using pseudo graphics /// +/// Version 1.0. Started - August 20, 2000 /// +/// Version 2.0. Added to EXTRA - July 17, 2001 /// + +#include "extra.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +// the maximum number of variables in the Karnaugh Map +#define MAXVARS 20 + +/* +// single line +#define SINGLE_VERTICAL (char)179 +#define SINGLE_HORIZONTAL (char)196 +#define SINGLE_TOP_LEFT (char)218 +#define SINGLE_TOP_RIGHT (char)191 +#define SINGLE_BOT_LEFT (char)192 +#define SINGLE_BOT_RIGHT (char)217 + +// double line +#define DOUBLE_VERTICAL (char)186 +#define DOUBLE_HORIZONTAL (char)205 +#define DOUBLE_TOP_LEFT (char)201 +#define DOUBLE_TOP_RIGHT (char)187 +#define DOUBLE_BOT_LEFT (char)200 +#define DOUBLE_BOT_RIGHT (char)188 + +// line intersections +#define SINGLES_CROSS (char)197 +#define DOUBLES_CROSS (char)206 +#define S_HOR_CROSS_D_VER (char)215 +#define S_VER_CROSS_D_HOR (char)216 + +// single line joining +#define S_JOINS_S_VER_LEFT (char)180 +#define S_JOINS_S_VER_RIGHT (char)195 +#define S_JOINS_S_HOR_TOP (char)193 +#define S_JOINS_S_HOR_BOT (char)194 + +// double line joining +#define D_JOINS_D_VER_LEFT (char)185 +#define D_JOINS_D_VER_RIGHT (char)204 +#define D_JOINS_D_HOR_TOP (char)202 +#define D_JOINS_D_HOR_BOT (char)203 + +// single line joining double line +#define S_JOINS_D_VER_LEFT (char)182 +#define S_JOINS_D_VER_RIGHT (char)199 +#define S_JOINS_D_HOR_TOP (char)207 +#define S_JOINS_D_HOR_BOT (char)209 +*/ + +// single line +#define SINGLE_VERTICAL (char)'|' +#define SINGLE_HORIZONTAL (char)'-' +#define SINGLE_TOP_LEFT (char)'+' +#define SINGLE_TOP_RIGHT (char)'+' +#define SINGLE_BOT_LEFT (char)'+' +#define SINGLE_BOT_RIGHT (char)'+' + +// double line +#define DOUBLE_VERTICAL (char)'|' +#define DOUBLE_HORIZONTAL (char)'-' +#define DOUBLE_TOP_LEFT (char)'+' +#define DOUBLE_TOP_RIGHT (char)'+' +#define DOUBLE_BOT_LEFT (char)'+' +#define DOUBLE_BOT_RIGHT (char)'+' + +// line intersections +#define SINGLES_CROSS (char)'+' +#define DOUBLES_CROSS (char)'+' +#define S_HOR_CROSS_D_VER (char)'+' +#define S_VER_CROSS_D_HOR (char)'+' + +// single line joining +#define S_JOINS_S_VER_LEFT (char)'+' +#define S_JOINS_S_VER_RIGHT (char)'+' +#define S_JOINS_S_HOR_TOP (char)'+' +#define S_JOINS_S_HOR_BOT (char)'+' + +// double line joining +#define D_JOINS_D_VER_LEFT (char)'+' +#define D_JOINS_D_VER_RIGHT (char)'+' +#define D_JOINS_D_HOR_TOP (char)'+' +#define D_JOINS_D_HOR_BOT (char)'+' + +// single line joining double line +#define S_JOINS_D_VER_LEFT (char)'+' +#define S_JOINS_D_VER_RIGHT (char)'+' +#define S_JOINS_D_HOR_TOP (char)'+' +#define S_JOINS_D_HOR_BOT (char)'+' + + +// other symbols +#define UNDERSCORE (char)95 +//#define SYMBOL_ZERO (char)248 // degree sign +//#define SYMBOL_ZERO (char)'o' +#define SYMBOL_ZERO (char)' ' +#define SYMBOL_ONE (char)'1' +#define SYMBOL_DC (char)'-' +#define SYMBOL_OVERLAP (char)'?' + +// full cells and half cells +#define CELL_FREE (char)32 +#define CELL_FULL (char)219 +#define HALF_UPPER (char)223 +#define HALF_LOWER (char)220 +#define HALF_LEFT (char)221 +#define HALF_RIGHT (char)222 + + +/*---------------------------------------------------------------------------*/ +/* Structure declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +// the array of BDD variables used internally +static DdNode * s_XVars[MAXVARS]; + +// flag which determines where the horizontal variable names are printed +static int fHorizontalVarNamesPrintedAbove = 1; + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +// Oleg's way of generating the gray code +static int GrayCode( int BinCode ); +static int BinCode ( int GrayCode ); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Prints the K-map of the function.] + + Description [If the pointer to the array of variables XVars is NULL, + fSuppType determines how the support will be determined. + fSuppType == 0 -- takes the first nVars of the manager + fSuppType == 1 -- takes the topmost nVars of the manager + fSuppType == 2 -- determines support from the on-set and the offset + ] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void Extra_PrintKMap( + FILE * Output, /* the output stream */ + DdManager * dd, + DdNode * OnSet, + DdNode * OffSet, + int nVars, + DdNode ** XVars, + int fSuppType, /* the flag which determines how support is computed */ + char ** pVarNames ) +{ + int d, p, n, s, v, h, w; + int nVarsVer; + int nVarsHor; + int nCellsVer; + int nCellsHor; + int nSkipSpaces; + + // make sure that on-set and off-set do not overlap + if ( !Cudd_bddLeq( dd, OnSet, Cudd_Not(OffSet) ) ) + { + fprintf( Output, "PrintKMap(): The on-set and the off-set overlap\n" ); + return; + } +/* + if ( OnSet == b1 ) + { + fprintf( Output, "PrintKMap(): Constant 1\n" ); + return; + } + if ( OffSet == b1 ) + { + fprintf( Output, "PrintKMap(): Constant 0\n" ); + return; + } +*/ + if ( nVars < 0 || nVars > MAXVARS ) + { + fprintf( Output, "PrintKMap(): The number of variables is less than zero or more than %d\n", MAXVARS ); + return; + } + + // determine the support if it is not given + if ( XVars == NULL ) + { + if ( fSuppType == 0 ) + { // assume that the support includes the first nVars of the manager + assert( nVars ); + for ( v = 0; v < nVars; v++ ) + s_XVars[v] = Cudd_bddIthVar( dd, v ); + } + else if ( fSuppType == 1 ) + { // assume that the support includes the topmost nVars of the manager + assert( nVars ); + for ( v = 0; v < nVars; v++ ) + s_XVars[v] = Cudd_bddIthVar( dd, dd->invperm[v] ); + } + else // determine the support + { + DdNode * SuppOn, * SuppOff, * Supp; + int cVars = 0; + DdNode * TempSupp; + + // determine support + SuppOn = Cudd_Support( dd, OnSet ); Cudd_Ref( SuppOn ); + SuppOff = Cudd_Support( dd, OffSet ); Cudd_Ref( SuppOff ); + Supp = Cudd_bddAnd( dd, SuppOn, SuppOff ); Cudd_Ref( Supp ); + Cudd_RecursiveDeref( dd, SuppOn ); + Cudd_RecursiveDeref( dd, SuppOff ); + + nVars = Cudd_SupportSize( dd, Supp ); + if ( nVars > MAXVARS ) + { + fprintf( Output, "PrintKMap(): The number of variables is more than %d\n", MAXVARS ); + Cudd_RecursiveDeref( dd, Supp ); + return; + } + + // assign variables + for ( TempSupp = Supp; TempSupp != dd->one; TempSupp = Cudd_T(TempSupp), cVars++ ) + s_XVars[cVars] = Cudd_bddIthVar( dd, TempSupp->index ); + + Cudd_RecursiveDeref( dd, TempSupp ); + } + } + else + { + // copy variables + assert( XVars ); + for ( v = 0; v < nVars; v++ ) + s_XVars[v] = XVars[v]; + } + + //////////////////////////////////////////////////////////////////// + // determine the Karnaugh map parameters + nVarsVer = nVars/2; + nVarsHor = nVars - nVarsVer; + nCellsVer = (1< MAXVARS ) + { + fprintf( Output, "PrintKMap(): The number of variables is less than zero or more than %d\n", MAXVARS ); + return; + } + + + //////////////////////////////////////////////////////////////////// + // determine the Karnaugh map parameters + nVarsVer = nXVars; + nVarsHor = nYVars; + nCellsVer = (1<> 1 ); +} + +/**Function******************************************************************** + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int BinCode ( int GrayCode ) +{ + int bc = GrayCode; + while( GrayCode >>= 1 ) bc ^= GrayCode; + return bc; +} + + diff --git a/abc_with_bb_support/src/misc/extra/extraBddMisc.c b/abc_with_bb_support/src/misc/extra/extraBddMisc.c new file mode 100644 index 000000000..a6600c18c --- /dev/null +++ b/abc_with_bb_support/src/misc/extra/extraBddMisc.c @@ -0,0 +1,1614 @@ +/**CFile**************************************************************** + + FileName [extraBddMisc.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [extra] + + Synopsis [DD-based utilities.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: extraBddMisc.c,v 1.4 2005/10/04 00:19:54 alanmi Exp $] + +***********************************************************************/ + +#include "extra.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +// file "extraDdTransfer.c" +static DdNode * extraTransferPermuteRecur( DdManager * ddS, DdManager * ddD, DdNode * f, st_table * table, int * Permute ); +static DdNode * extraTransferPermute( DdManager * ddS, DdManager * ddD, DdNode * f, int * Permute ); +static DdNode * cuddBddPermuteRecur ARGS( ( DdManager * manager, DdHashTable * table, DdNode * node, int *permut ) ); + +// file "cuddUtils.c" +static void ddSupportStep(DdNode *f, int *support); +static void ddClearFlag(DdNode *f); + +static DdNode* extraZddPrimes( DdManager *dd, DdNode* F ); + +/**AutomaticEnd***************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Convert a {A,B}DD from a manager to another with variable remapping.] + + Description [Convert a {A,B}DD from a manager to another one. The orders of the + variables in the two managers may be different. Returns a + pointer to the {A,B}DD in the destination manager if successful; NULL + otherwise. The i-th entry in the array Permute tells what is the index + of the i-th variable from the old manager in the new manager.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * Extra_TransferPermute( DdManager * ddSource, DdManager * ddDestination, DdNode * f, int * Permute ) +{ + DdNode * bRes; + do + { + ddDestination->reordered = 0; + bRes = extraTransferPermute( ddSource, ddDestination, f, Permute ); + } + while ( ddDestination->reordered == 1 ); + return ( bRes ); + +} /* end of Extra_TransferPermute */ + +/**Function******************************************************************** + + Synopsis [Transfers the BDD from one manager into another level by level.] + + Description [Transfers the BDD from one manager into another while + preserving the correspondence between variables level by level.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * Extra_TransferLevelByLevel( DdManager * ddSource, DdManager * ddDestination, DdNode * f ) +{ + DdNode * bRes; + int * pPermute; + int nMin, nMax, i; + + nMin = ddMin(ddSource->size, ddDestination->size); + nMax = ddMax(ddSource->size, ddDestination->size); + pPermute = ALLOC( int, nMax ); + // set up the variable permutation + for ( i = 0; i < nMin; i++ ) + pPermute[ ddSource->invperm[i] ] = ddDestination->invperm[i]; + if ( ddSource->size > ddDestination->size ) + { + for ( ; i < nMax; i++ ) + pPermute[ ddSource->invperm[i] ] = -1; + } + bRes = Extra_TransferPermute( ddSource, ddDestination, f, pPermute ); + FREE( pPermute ); + return bRes; +} + +/**Function******************************************************************** + + Synopsis [Remaps the function to depend on the topmost variables on the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * Extra_bddRemapUp( + DdManager * dd, + DdNode * bF ) +{ + int * pPermute; + DdNode * bSupp, * bTemp, * bRes; + int Counter; + + pPermute = ALLOC( int, dd->size ); + + // get support + bSupp = Cudd_Support( dd, bF ); Cudd_Ref( bSupp ); + + // create the variable map + // to remap the DD into the upper part of the manager + Counter = 0; + for ( bTemp = bSupp; bTemp != dd->one; bTemp = cuddT(bTemp) ) + pPermute[bTemp->index] = dd->invperm[Counter++]; + + // transfer the BDD and remap it + bRes = Cudd_bddPermute( dd, bF, pPermute ); Cudd_Ref( bRes ); + + // remove support + Cudd_RecursiveDeref( dd, bSupp ); + + // return + Cudd_Deref( bRes ); + free( pPermute ); + return bRes; +} + +/**Function******************************************************************** + + Synopsis [Moves the BDD by the given number of variables up or down.] + + Description [] + + SideEffects [] + + SeeAlso [Extra_bddShift] + +******************************************************************************/ +DdNode * Extra_bddMove( + DdManager * dd, /* the DD manager */ + DdNode * bF, + int nVars) +{ + DdNode * res; + DdNode * bVars; + if ( nVars == 0 ) + return bF; + if ( Cudd_IsConstant(bF) ) + return bF; + assert( nVars <= dd->size ); + if ( nVars > 0 ) + bVars = dd->vars[nVars]; + else + bVars = Cudd_Not(dd->vars[-nVars]); + + do { + dd->reordered = 0; + res = extraBddMove( dd, bF, bVars ); + } while (dd->reordered == 1); + return(res); + +} /* end of Extra_bddMove */ + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_StopManager( DdManager * dd ) +{ + int RetValue; + // check for remaining references in the package + RetValue = Cudd_CheckZeroRef( dd ); + if ( RetValue > 0 ) + printf( "\nThe number of referenced nodes = %d\n\n", RetValue ); +// Cudd_PrintInfo( dd, stdout ); + Cudd_Quit( dd ); +} + +/**Function******************************************************************** + + Synopsis [Outputs the BDD in a readable format.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void Extra_bddPrint( DdManager * dd, DdNode * F ) +{ + DdGen * Gen; + int * Cube; + CUDD_VALUE_TYPE Value; + int nVars = dd->size; + int fFirstCube = 1; + int i; + + if ( F == NULL ) + { + printf("NULL"); + return; + } + if ( F == b0 ) + { + printf("Constant 0"); + return; + } + if ( F == b1 ) + { + printf("Constant 1"); + return; + } + + Cudd_ForeachCube( dd, F, Gen, Cube, Value ) + { + if ( fFirstCube ) + fFirstCube = 0; + else +// Output << " + "; + printf( " + " ); + + for ( i = 0; i < nVars; i++ ) + if ( Cube[i] == 0 ) + printf( "[%d]'", i ); +// printf( "%c'", (char)('a'+i) ); + else if ( Cube[i] == 1 ) + printf( "[%d]", i ); +// printf( "%c", (char)('a'+i) ); + } + +// printf("\n"); +} +/**Function******************************************************************** + + Synopsis [Returns the size of the support.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int Extra_bddSuppSize( DdManager * dd, DdNode * bSupp ) +{ + int Counter = 0; + while ( bSupp != b1 ) + { + assert( !Cudd_IsComplement(bSupp) ); + assert( cuddE(bSupp) == b0 ); + + bSupp = cuddT(bSupp); + Counter++; + } + return Counter; +} + +/**Function******************************************************************** + + Synopsis [Returns 1 if the support contains the given BDD variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int Extra_bddSuppContainVar( DdManager * dd, DdNode * bS, DdNode * bVar ) +{ + for( ; bS != b1; bS = cuddT(bS) ) + if ( bS->index == bVar->index ) + return 1; + return 0; +} + +/**Function******************************************************************** + + Synopsis [Returns 1 if two supports represented as BDD cubes are overlapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int Extra_bddSuppOverlapping( DdManager * dd, DdNode * S1, DdNode * S2 ) +{ + while ( S1->index != CUDD_CONST_INDEX && S2->index != CUDD_CONST_INDEX ) + { + // if the top vars are the same, they intersect + if ( S1->index == S2->index ) + return 1; + // if the top vars are different, skip the one, which is higher + if ( dd->perm[S1->index] < dd->perm[S2->index] ) + S1 = cuddT(S1); + else + S2 = cuddT(S2); + } + return 0; +} + +/**Function******************************************************************** + + Synopsis [Returns the number of different vars in two supports.] + + Description [Counts the number of variables that appear in one support and + does not appear in other support. If the number exceeds DiffMax, returns DiffMax.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int Extra_bddSuppDifferentVars( DdManager * dd, DdNode * S1, DdNode * S2, int DiffMax ) +{ + int Result = 0; + while ( S1->index != CUDD_CONST_INDEX && S2->index != CUDD_CONST_INDEX ) + { + // if the top vars are the same, this var is the same + if ( S1->index == S2->index ) + { + S1 = cuddT(S1); + S2 = cuddT(S2); + continue; + } + // the top var is different + Result++; + + if ( Result >= DiffMax ) + return DiffMax; + + // if the top vars are different, skip the one, which is higher + if ( dd->perm[S1->index] < dd->perm[S2->index] ) + S1 = cuddT(S1); + else + S2 = cuddT(S2); + } + + // consider the remaining variables + if ( S1->index != CUDD_CONST_INDEX ) + Result += Extra_bddSuppSize(dd,S1); + else if ( S2->index != CUDD_CONST_INDEX ) + Result += Extra_bddSuppSize(dd,S2); + + if ( Result >= DiffMax ) + return DiffMax; + return Result; +} + + +/**Function******************************************************************** + + Synopsis [Checks the support containment.] + + Description [This function returns 1 if one support is contained in another. + In this case, bLarge (bSmall) is assigned to point to the larger (smaller) support. + If the supports are identical, return 0 and does not assign the supports!] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int Extra_bddSuppCheckContainment( DdManager * dd, DdNode * bL, DdNode * bH, DdNode ** bLarge, DdNode ** bSmall ) +{ + DdNode * bSL = bL; + DdNode * bSH = bH; + int fLcontainsH = 1; + int fHcontainsL = 1; + int TopVar; + + if ( bSL == bSH ) + return 0; + + while ( bSL != b1 || bSH != b1 ) + { + if ( bSL == b1 ) + { // Low component has no vars; High components has some vars + fLcontainsH = 0; + if ( fHcontainsL == 0 ) + return 0; + else + break; + } + + if ( bSH == b1 ) + { // similarly + fHcontainsL = 0; + if ( fLcontainsH == 0 ) + return 0; + else + break; + } + + // determine the topmost var of the supports by comparing their levels + if ( dd->perm[bSL->index] < dd->perm[bSH->index] ) + TopVar = bSL->index; + else + TopVar = bSH->index; + + if ( TopVar == bSL->index && TopVar == bSH->index ) + { // they are on the same level + // it does not tell us anything about their containment + // skip this var + bSL = cuddT(bSL); + bSH = cuddT(bSH); + } + else if ( TopVar == bSL->index ) // and TopVar != bSH->index + { // Low components is higher and contains more vars + // it is not possible that High component contains Low + fHcontainsL = 0; + // skip this var + bSL = cuddT(bSL); + } + else // if ( TopVar == bSH->index ) // and TopVar != bSL->index + { // similarly + fLcontainsH = 0; + // skip this var + bSH = cuddT(bSH); + } + + // check the stopping condition + if ( !fHcontainsL && !fLcontainsH ) + return 0; + } + // only one of them can be true at the same time + assert( !fHcontainsL || !fLcontainsH ); + if ( fHcontainsL ) + { + *bLarge = bH; + *bSmall = bL; + } + else // fLcontainsH + { + *bLarge = bL; + *bSmall = bH; + } + return 1; +} + + +/**Function******************************************************************** + + Synopsis [Finds variables on which the DD depends and returns them as am array.] + + Description [Finds the variables on which the DD depends. Returns an array + with entries set to 1 for those variables that belong to the support; + NULL otherwise. The array is allocated by the user and should have at least + as many entries as the maximum number of variables in BDD and ZDD parts of + the manager.] + + SideEffects [None] + + SeeAlso [Cudd_Support Cudd_VectorSupport Cudd_ClassifySupport] + +******************************************************************************/ +int * +Extra_SupportArray( + DdManager * dd, /* manager */ + DdNode * f, /* DD whose support is sought */ + int * support ) /* array allocated by the user */ +{ + int i, size; + + /* Initialize support array for ddSupportStep. */ + size = ddMax(dd->size, dd->sizeZ); + for (i = 0; i < size; i++) + support[i] = 0; + + /* Compute support and clean up markers. */ + ddSupportStep(Cudd_Regular(f),support); + ddClearFlag(Cudd_Regular(f)); + + return(support); + +} /* end of Extra_SupportArray */ + +/**Function******************************************************************** + + Synopsis [Finds the variables on which a set of DDs depends.] + + Description [Finds the variables on which a set of DDs depends. + The set must contain either BDDs and ADDs, or ZDDs. + Returns a BDD consisting of the product of the variables if + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_Support Cudd_ClassifySupport] + +******************************************************************************/ +int * +Extra_VectorSupportArray( + DdManager * dd, /* manager */ + DdNode ** F, /* array of DDs whose support is sought */ + int n, /* size of the array */ + int * support ) /* array allocated by the user */ +{ + int i, size; + + /* Allocate and initialize support array for ddSupportStep. */ + size = ddMax( dd->size, dd->sizeZ ); + for ( i = 0; i < size; i++ ) + support[i] = 0; + + /* Compute support and clean up markers. */ + for ( i = 0; i < n; i++ ) + ddSupportStep( Cudd_Regular(F[i]), support ); + for ( i = 0; i < n; i++ ) + ddClearFlag( Cudd_Regular(F[i]) ); + + return support; +} + +/**Function******************************************************************** + + Synopsis [Find any cube belonging to the on-set of the function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * Extra_bddFindOneCube( DdManager * dd, DdNode * bF ) +{ + char * s_Temp; + DdNode * bCube, * bTemp; + int v; + + // get the vector of variables in the cube + s_Temp = ALLOC( char, dd->size ); + Cudd_bddPickOneCube( dd, bF, s_Temp ); + + // start the cube + bCube = b1; Cudd_Ref( bCube ); + for ( v = 0; v < dd->size; v++ ) + if ( s_Temp[v] == 0 ) + { +// Cube &= !s_XVars[v]; + bCube = Cudd_bddAnd( dd, bTemp = bCube, Cudd_Not(dd->vars[v]) ); Cudd_Ref( bCube ); + Cudd_RecursiveDeref( dd, bTemp ); + } + else if ( s_Temp[v] == 1 ) + { +// Cube &= s_XVars[v]; + bCube = Cudd_bddAnd( dd, bTemp = bCube, dd->vars[v] ); Cudd_Ref( bCube ); + Cudd_RecursiveDeref( dd, bTemp ); + } + Cudd_Deref(bCube); + free( s_Temp ); + return bCube; +} + +/**Function******************************************************************** + + Synopsis [Returns one cube contained in the given BDD.] + + Description [This function returns the cube with the smallest + bits-to-integer value.] + + SideEffects [] + +******************************************************************************/ +DdNode * Extra_bddGetOneCube( DdManager * dd, DdNode * bFunc ) +{ + DdNode * bFuncR, * bFunc0, * bFunc1; + DdNode * bRes0, * bRes1, * bRes; + + bFuncR = Cudd_Regular(bFunc); + if ( cuddIsConstant(bFuncR) ) + return bFunc; + + // cofactor + if ( Cudd_IsComplement(bFunc) ) + { + bFunc0 = Cudd_Not( cuddE(bFuncR) ); + bFunc1 = Cudd_Not( cuddT(bFuncR) ); + } + else + { + bFunc0 = cuddE(bFuncR); + bFunc1 = cuddT(bFuncR); + } + + // try to find the cube with the negative literal + bRes0 = Extra_bddGetOneCube( dd, bFunc0 ); Cudd_Ref( bRes0 ); + + if ( bRes0 != b0 ) + { + bRes = Cudd_bddAnd( dd, bRes0, Cudd_Not(dd->vars[bFuncR->index]) ); Cudd_Ref( bRes ); + Cudd_RecursiveDeref( dd, bRes0 ); + } + else + { + Cudd_RecursiveDeref( dd, bRes0 ); + // try to find the cube with the positive literal + bRes1 = Extra_bddGetOneCube( dd, bFunc1 ); Cudd_Ref( bRes1 ); + assert( bRes1 != b0 ); + bRes = Cudd_bddAnd( dd, bRes1, dd->vars[bFuncR->index] ); Cudd_Ref( bRes ); + Cudd_RecursiveDeref( dd, bRes1 ); + } + + Cudd_Deref( bRes ); + return bRes; +} + +/**Function******************************************************************** + + Synopsis [Performs the reordering-sensitive step of Extra_bddMove().] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * Extra_bddComputeRangeCube( DdManager * dd, int iStart, int iStop ) +{ + DdNode * bTemp, * bProd; + int i; + assert( iStart <= iStop ); + assert( iStart >= 0 && iStart <= dd->size ); + assert( iStop >= 0 && iStop <= dd->size ); + bProd = b1; Cudd_Ref( bProd ); + for ( i = iStart; i < iStop; i++ ) + { + bProd = Cudd_bddAnd( dd, bTemp = bProd, dd->vars[i] ); Cudd_Ref( bProd ); + Cudd_RecursiveDeref( dd, bTemp ); + } + Cudd_Deref( bProd ); + return bProd; +} + +/**Function******************************************************************** + + Synopsis [Computes the cube of BDD variables corresponding to bits it the bit-code] + + Description [Returns a bdd composed of elementary bdds found in array BddVars[] such + that the bdd vars encode the number Value of bit length CodeWidth (if fMsbFirst is 1, + the most significant bit is encoded with the first bdd variable). If the variables + BddVars are not specified, takes the first CodeWidth variables of the manager] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * Extra_bddBitsToCube( DdManager * dd, int Code, int CodeWidth, DdNode ** pbVars, int fMsbFirst ) +{ + int z; + DdNode * bTemp, * bVar, * bVarBdd, * bResult; + + bResult = b1; Cudd_Ref( bResult ); + for ( z = 0; z < CodeWidth; z++ ) + { + bVarBdd = (pbVars)? pbVars[z]: dd->vars[z]; + if ( fMsbFirst ) + bVar = Cudd_NotCond( bVarBdd, (Code & (1 << (CodeWidth-1-z)))==0 ); + else + bVar = Cudd_NotCond( bVarBdd, (Code & (1 << (z)))==0 ); + bResult = Cudd_bddAnd( dd, bTemp = bResult, bVar ); Cudd_Ref( bResult ); + Cudd_RecursiveDeref( dd, bTemp ); + } + Cudd_Deref( bResult ); + + return bResult; +} /* end of Extra_bddBitsToCube */ + +/**Function******************************************************************** + + Synopsis [Finds the support as a negative polarity cube.] + + Description [Finds the variables on which a DD depends. Returns a BDD + consisting of the product of the variables in the negative polarity + if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_VectorSupport Cudd_Support] + +******************************************************************************/ +DdNode * Extra_bddSupportNegativeCube( DdManager * dd, DdNode * f ) +{ + int *support; + DdNode *res, *tmp, *var; + int i, j; + int size; + + /* Allocate and initialize support array for ddSupportStep. */ + size = ddMax( dd->size, dd->sizeZ ); + support = ALLOC( int, size ); + if ( support == NULL ) + { + dd->errorCode = CUDD_MEMORY_OUT; + return ( NULL ); + } + for ( i = 0; i < size; i++ ) + { + support[i] = 0; + } + + /* Compute support and clean up markers. */ + ddSupportStep( Cudd_Regular( f ), support ); + ddClearFlag( Cudd_Regular( f ) ); + + /* Transform support from array to cube. */ + do + { + dd->reordered = 0; + res = DD_ONE( dd ); + cuddRef( res ); + for ( j = size - 1; j >= 0; j-- ) + { /* for each level bottom-up */ + i = ( j >= dd->size ) ? j : dd->invperm[j]; + if ( support[i] == 1 ) + { + var = cuddUniqueInter( dd, i, dd->one, Cudd_Not( dd->one ) ); + ////////////////////////////////////////////////////////////////// + var = Cudd_Not(var); + ////////////////////////////////////////////////////////////////// + cuddRef( var ); + tmp = cuddBddAndRecur( dd, res, var ); + if ( tmp == NULL ) + { + Cudd_RecursiveDeref( dd, res ); + Cudd_RecursiveDeref( dd, var ); + res = NULL; + break; + } + cuddRef( tmp ); + Cudd_RecursiveDeref( dd, res ); + Cudd_RecursiveDeref( dd, var ); + res = tmp; + } + } + } + while ( dd->reordered == 1 ); + + FREE( support ); + if ( res != NULL ) + cuddDeref( res ); + return ( res ); + +} /* end of Extra_SupportNeg */ + +/**Function******************************************************************** + + Synopsis [Returns 1 if the BDD is the BDD of elementary variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int Extra_bddIsVar( DdNode * bFunc ) +{ + bFunc = Cudd_Regular( bFunc ); + if ( cuddIsConstant(bFunc) ) + return 0; + return cuddIsConstant( cuddT(bFunc) ) && cuddIsConstant( Cudd_Regular(cuddE(bFunc)) ); +} + +/**Function******************************************************************** + + Synopsis [Creates AND composed of the first nVars of the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * Extra_bddCreateAnd( DdManager * dd, int nVars ) +{ + DdNode * bFunc, * bTemp; + int i; + bFunc = Cudd_ReadOne(dd); Cudd_Ref( bFunc ); + for ( i = 0; i < nVars; i++ ) + { + bFunc = Cudd_bddAnd( dd, bTemp = bFunc, Cudd_bddIthVar(dd,i) ); Cudd_Ref( bFunc ); + Cudd_RecursiveDeref( dd, bTemp ); + } + Cudd_Deref( bFunc ); + return bFunc; +} + +/**Function******************************************************************** + + Synopsis [Creates OR composed of the first nVars of the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * Extra_bddCreateOr( DdManager * dd, int nVars ) +{ + DdNode * bFunc, * bTemp; + int i; + bFunc = Cudd_ReadLogicZero(dd); Cudd_Ref( bFunc ); + for ( i = 0; i < nVars; i++ ) + { + bFunc = Cudd_bddOr( dd, bTemp = bFunc, Cudd_bddIthVar(dd,i) ); Cudd_Ref( bFunc ); + Cudd_RecursiveDeref( dd, bTemp ); + } + Cudd_Deref( bFunc ); + return bFunc; +} + +/**Function******************************************************************** + + Synopsis [Creates EXOR composed of the first nVars of the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * Extra_bddCreateExor( DdManager * dd, int nVars ) +{ + DdNode * bFunc, * bTemp; + int i; + bFunc = Cudd_ReadLogicZero(dd); Cudd_Ref( bFunc ); + for ( i = 0; i < nVars; i++ ) + { + bFunc = Cudd_bddXor( dd, bTemp = bFunc, Cudd_bddIthVar(dd,i) ); Cudd_Ref( bFunc ); + Cudd_RecursiveDeref( dd, bTemp ); + } + Cudd_Deref( bFunc ); + return bFunc; +} + +/**Function******************************************************************** + + Synopsis [Computes the set of primes as a ZDD.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * Extra_zddPrimes( DdManager * dd, DdNode * F ) +{ + DdNode *res; + do { + dd->reordered = 0; + res = extraZddPrimes(dd, F); + if ( dd->reordered == 1 ) + printf("\nReordering in Extra_zddPrimes()\n"); + } while (dd->reordered == 1); + return(res); + +} /* end of Extra_zddPrimes */ + +/**Function******************************************************************** + + Synopsis [Permutes the variables of the array of BDDs.] + + Description [Given a permutation in array permut, creates a new BDD + with permuted variables. There should be an entry in array permut + for each variable in the manager. The i-th entry of permut holds the + index of the variable that is to substitute the i-th variable. + The DDs in the resulting array are already referenced.] + + SideEffects [None] + + SeeAlso [Cudd_addPermute Cudd_bddSwapVariables] + +******************************************************************************/ +void Extra_bddPermuteArray( DdManager * manager, DdNode ** bNodesIn, DdNode ** bNodesOut, int nNodes, int *permut ) +{ + DdHashTable *table; + int i, k; + do + { + manager->reordered = 0; + table = cuddHashTableInit( manager, 1, 2 ); + + /* permute the output functions one-by-one */ + for ( i = 0; i < nNodes; i++ ) + { + bNodesOut[i] = cuddBddPermuteRecur( manager, table, bNodesIn[i], permut ); + if ( bNodesOut[i] == NULL ) + { + /* deref the array of the already computed outputs */ + for ( k = 0; k < i; k++ ) + Cudd_RecursiveDeref( manager, bNodesOut[k] ); + break; + } + cuddRef( bNodesOut[i] ); + } + /* Dispose of local cache. */ + cuddHashTableQuit( table ); + } + while ( manager->reordered == 1 ); +} /* end of Extra_bddPermuteArray */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Performs the reordering-sensitive step of Extra_bddMove().] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * extraBddMove( + DdManager * dd, /* the DD manager */ + DdNode * bF, + DdNode * bDist) +{ + DdNode * bRes; + + if ( Cudd_IsConstant(bF) ) + return bF; + + if ( bRes = cuddCacheLookup2(dd, extraBddMove, bF, bDist) ) + return bRes; + else + { + DdNode * bRes0, * bRes1; + DdNode * bF0, * bF1; + DdNode * bFR = Cudd_Regular(bF); + int VarNew; + + if ( Cudd_IsComplement(bDist) ) + VarNew = bFR->index - Cudd_Not(bDist)->index; + else + VarNew = bFR->index + bDist->index; + assert( VarNew < dd->size ); + + // cofactor the functions + if ( bFR != bF ) // bFunc is complemented + { + bF0 = Cudd_Not( cuddE(bFR) ); + bF1 = Cudd_Not( cuddT(bFR) ); + } + else + { + bF0 = cuddE(bFR); + bF1 = cuddT(bFR); + } + + bRes0 = extraBddMove( dd, bF0, bDist ); + if ( bRes0 == NULL ) + return NULL; + cuddRef( bRes0 ); + + bRes1 = extraBddMove( dd, bF1, bDist ); + if ( bRes1 == NULL ) + { + Cudd_RecursiveDeref( dd, bRes0 ); + return NULL; + } + cuddRef( bRes1 ); + + /* only bRes0 and bRes1 are referenced at this point */ + bRes = cuddBddIteRecur( dd, dd->vars[VarNew], bRes1, bRes0 ); + if ( bRes == NULL ) + { + Cudd_RecursiveDeref( dd, bRes0 ); + Cudd_RecursiveDeref( dd, bRes1 ); + return NULL; + } + cuddRef( bRes ); + Cudd_RecursiveDeref( dd, bRes0 ); + Cudd_RecursiveDeref( dd, bRes1 ); + + /* insert the result into cache */ + cuddCacheInsert2( dd, extraBddMove, bF, bDist, bRes ); + cuddDeref( bRes ); + return bRes; + } +} /* end of extraBddMove */ + + +/**Function******************************************************************** + + Synopsis [Finds three cofactors of the cover w.r.t. to the topmost variable.] + + Description [Finds three cofactors of the cover w.r.t. to the topmost variable. + Does not check the cover for being a constant. Assumes that ZDD variables encoding + positive and negative polarities are adjacent in the variable order. Is different + from cuddZddGetCofactors3() in that it does not compute the cofactors w.r.t. the + given variable but takes the cofactors with respent to the topmost variable. + This function is more efficient when used in recursive procedures because it does + not require referencing of the resulting cofactors (compare cuddZddProduct() + and extraZddPrimeProduct()).] + + SideEffects [None] + + SeeAlso [cuddZddGetCofactors3] + +******************************************************************************/ +void +extraDecomposeCover( + DdManager* dd, /* the manager */ + DdNode* zC, /* the cover */ + DdNode** zC0, /* the pointer to the negative var cofactor */ + DdNode** zC1, /* the pointer to the positive var cofactor */ + DdNode** zC2 ) /* the pointer to the cofactor without var */ +{ + if ( (zC->index & 1) == 0 ) + { /* the top variable is present in positive polarity and maybe in negative */ + + DdNode *Temp = cuddE( zC ); + *zC1 = cuddT( zC ); + if ( cuddIZ(dd,Temp->index) == cuddIZ(dd,zC->index) + 1 ) + { /* Temp is not a terminal node + * top var is present in negative polarity */ + *zC2 = cuddE( Temp ); + *zC0 = cuddT( Temp ); + } + else + { /* top var is not present in negative polarity */ + *zC2 = Temp; + *zC0 = dd->zero; + } + } + else + { /* the top variable is present only in negative */ + *zC1 = dd->zero; + *zC2 = cuddE( zC ); + *zC0 = cuddT( zC ); + } +} /* extraDecomposeCover */ + +/*---------------------------------------------------------------------------*/ +/* Definition of static Functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Convert a BDD from a manager to another one.] + + Description [Convert a BDD from a manager to another one. Returns a + pointer to the BDD in the destination manager if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Extra_TransferPermute] + +******************************************************************************/ +DdNode * extraTransferPermute( DdManager * ddS, DdManager * ddD, DdNode * f, int * Permute ) +{ + DdNode *res; + st_table *table = NULL; + st_generator *gen = NULL; + DdNode *key, *value; + + table = st_init_table( st_ptrcmp, st_ptrhash ); + if ( table == NULL ) + goto failure; + res = extraTransferPermuteRecur( ddS, ddD, f, table, Permute ); + if ( res != NULL ) + cuddRef( res ); + + /* Dereference all elements in the table and dispose of the table. + ** This must be done also if res is NULL to avoid leaks in case of + ** reordering. */ + gen = st_init_gen( table ); + if ( gen == NULL ) + goto failure; + while ( st_gen( gen, ( char ** ) &key, ( char ** ) &value ) ) + { + Cudd_RecursiveDeref( ddD, value ); + } + st_free_gen( gen ); + gen = NULL; + st_free_table( table ); + table = NULL; + + if ( res != NULL ) + cuddDeref( res ); + return ( res ); + + failure: + if ( table != NULL ) + st_free_table( table ); + if ( gen != NULL ) + st_free_gen( gen ); + return ( NULL ); + +} /* end of extraTransferPermute */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Extra_TransferPermute.] + + Description [Performs the recursive step of Extra_TransferPermute. + Returns a pointer to the result if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [extraTransferPermute] + +******************************************************************************/ +static DdNode * +extraTransferPermuteRecur( + DdManager * ddS, + DdManager * ddD, + DdNode * f, + st_table * table, + int * Permute ) +{ + DdNode *ft, *fe, *t, *e, *var, *res; + DdNode *one, *zero; + int index; + int comple = 0; + + statLine( ddD ); + one = DD_ONE( ddD ); + comple = Cudd_IsComplement( f ); + + /* Trivial cases. */ + if ( Cudd_IsConstant( f ) ) + return ( Cudd_NotCond( one, comple ) ); + + + /* Make canonical to increase the utilization of the cache. */ + f = Cudd_NotCond( f, comple ); + /* Now f is a regular pointer to a non-constant node. */ + + /* Check the cache. */ + if ( st_lookup( table, ( char * ) f, ( char ** ) &res ) ) + return ( Cudd_NotCond( res, comple ) ); + + /* Recursive step. */ + if ( Permute ) + index = Permute[f->index]; + else + index = f->index; + + ft = cuddT( f ); + fe = cuddE( f ); + + t = extraTransferPermuteRecur( ddS, ddD, ft, table, Permute ); + if ( t == NULL ) + { + return ( NULL ); + } + cuddRef( t ); + + e = extraTransferPermuteRecur( ddS, ddD, fe, table, Permute ); + if ( e == NULL ) + { + Cudd_RecursiveDeref( ddD, t ); + return ( NULL ); + } + cuddRef( e ); + + zero = Cudd_Not(ddD->one); + var = cuddUniqueInter( ddD, index, one, zero ); + if ( var == NULL ) + { + Cudd_RecursiveDeref( ddD, t ); + Cudd_RecursiveDeref( ddD, e ); + return ( NULL ); + } + res = cuddBddIteRecur( ddD, var, t, e ); + + if ( res == NULL ) + { + Cudd_RecursiveDeref( ddD, t ); + Cudd_RecursiveDeref( ddD, e ); + return ( NULL ); + } + cuddRef( res ); + Cudd_RecursiveDeref( ddD, t ); + Cudd_RecursiveDeref( ddD, e ); + + if ( st_add_direct( table, ( char * ) f, ( char * ) res ) == + ST_OUT_OF_MEM ) + { + Cudd_RecursiveDeref( ddD, res ); + return ( NULL ); + } + return ( Cudd_NotCond( res, comple ) ); + +} /* end of extraTransferPermuteRecur */ + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_Support.] + + Description [Performs the recursive step of Cudd_Support. Performs a + DFS from f. The support is accumulated in supp as a side effect. Uses + the LSB of the then pointer as visited flag.] + + SideEffects [None] + + SeeAlso [ddClearFlag] + +******************************************************************************/ +static void +ddSupportStep( + DdNode * f, + int * support) +{ + if (cuddIsConstant(f) || Cudd_IsComplement(f->next)) { + return; + } + + support[f->index] = 1; + ddSupportStep(cuddT(f),support); + ddSupportStep(Cudd_Regular(cuddE(f)),support); + /* Mark as visited. */ + f->next = Cudd_Not(f->next); + return; + +} /* end of ddSupportStep */ + + +/**Function******************************************************************** + + Synopsis [Performs a DFS from f, clearing the LSB of the next + pointers.] + + Description [] + + SideEffects [None] + + SeeAlso [ddSupportStep ddDagInt] + +******************************************************************************/ +static void +ddClearFlag( + DdNode * f) +{ + if (!Cudd_IsComplement(f->next)) { + return; + } + /* Clear visited flag. */ + f->next = Cudd_Regular(f->next); + if (cuddIsConstant(f)) { + return; + } + ddClearFlag(cuddT(f)); + ddClearFlag(Cudd_Regular(cuddE(f))); + return; + +} /* end of ddClearFlag */ + + +/**Function******************************************************************** + + Synopsis [Composed three subcovers into one ZDD.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +extraComposeCover( + DdManager* dd, /* the manager */ + DdNode* zC0, /* the pointer to the negative var cofactor */ + DdNode* zC1, /* the pointer to the positive var cofactor */ + DdNode* zC2, /* the pointer to the cofactor without var */ + int TopVar) /* the index of the positive ZDD var */ +{ + DdNode * zRes, * zTemp; + /* compose with-neg-var and without-var using the neg ZDD var */ + zTemp = cuddZddGetNode( dd, 2*TopVar + 1, zC0, zC2 ); + if ( zTemp == NULL ) + { + Cudd_RecursiveDerefZdd(dd, zC0); + Cudd_RecursiveDerefZdd(dd, zC1); + Cudd_RecursiveDerefZdd(dd, zC2); + return NULL; + } + cuddRef( zTemp ); + cuddDeref( zC0 ); + cuddDeref( zC2 ); + + /* compose with-pos-var and previous result using the pos ZDD var */ + zRes = cuddZddGetNode( dd, 2*TopVar, zC1, zTemp ); + if ( zRes == NULL ) + { + Cudd_RecursiveDerefZdd(dd, zC1); + Cudd_RecursiveDerefZdd(dd, zTemp); + return NULL; + } + cuddDeref( zC1 ); + cuddDeref( zTemp ); + return zRes; +} /* extraComposeCover */ + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of prime computation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode* extraZddPrimes( DdManager *dd, DdNode* F ) +{ + DdNode *zRes; + + if ( F == Cudd_Not( dd->one ) ) + return dd->zero; + if ( F == dd->one ) + return dd->one; + + /* check cache */ + zRes = cuddCacheLookup1Zdd(dd, extraZddPrimes, F); + if (zRes) + return(zRes); + { + /* temporary variables */ + DdNode *bF01, *zP0, *zP1; + /* three components of the prime set */ + DdNode *zResE, *zResP, *zResN; + int fIsComp = Cudd_IsComplement( F ); + + /* find cofactors of F */ + DdNode * bF0 = Cudd_NotCond( Cudd_E( F ), fIsComp ); + DdNode * bF1 = Cudd_NotCond( Cudd_T( F ), fIsComp ); + + /* find the intersection of cofactors */ + bF01 = cuddBddAndRecur( dd, bF0, bF1 ); + if ( bF01 == NULL ) return NULL; + cuddRef( bF01 ); + + /* solve the problems for cofactors */ + zP0 = extraZddPrimes( dd, bF0 ); + if ( zP0 == NULL ) + { + Cudd_RecursiveDeref( dd, bF01 ); + return NULL; + } + cuddRef( zP0 ); + + zP1 = extraZddPrimes( dd, bF1 ); + if ( zP1 == NULL ) + { + Cudd_RecursiveDeref( dd, bF01 ); + Cudd_RecursiveDerefZdd( dd, zP0 ); + return NULL; + } + cuddRef( zP1 ); + + /* check for local unateness */ + if ( bF01 == bF0 ) /* unate increasing */ + { + /* intersection is useless */ + cuddDeref( bF01 ); + /* the primes of intersection are the primes of F0 */ + zResE = zP0; + /* there are no primes with negative var */ + zResN = dd->zero; + cuddRef( zResN ); + /* primes with positive var are primes of F1 that are not primes of F01 */ + zResP = cuddZddDiff( dd, zP1, zP0 ); + if ( zResP == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zResE ); + Cudd_RecursiveDerefZdd( dd, zResN ); + Cudd_RecursiveDerefZdd( dd, zP1 ); + return NULL; + } + cuddRef( zResP ); + Cudd_RecursiveDerefZdd( dd, zP1 ); + } + else if ( bF01 == bF1 ) /* unate decreasing */ + { + /* intersection is useless */ + cuddDeref( bF01 ); + /* the primes of intersection are the primes of F1 */ + zResE = zP1; + /* there are no primes with positive var */ + zResP = dd->zero; + cuddRef( zResP ); + /* primes with negative var are primes of F0 that are not primes of F01 */ + zResN = cuddZddDiff( dd, zP0, zP1 ); + if ( zResN == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zResE ); + Cudd_RecursiveDerefZdd( dd, zResP ); + Cudd_RecursiveDerefZdd( dd, zP0 ); + return NULL; + } + cuddRef( zResN ); + Cudd_RecursiveDerefZdd( dd, zP0 ); + } + else /* not unate */ + { + /* primes without the top var are primes of F10 */ + zResE = extraZddPrimes( dd, bF01 ); + if ( zResE == NULL ) + { + Cudd_RecursiveDerefZdd( dd, bF01 ); + Cudd_RecursiveDerefZdd( dd, zP0 ); + Cudd_RecursiveDerefZdd( dd, zP1 ); + return NULL; + } + cuddRef( zResE ); + Cudd_RecursiveDeref( dd, bF01 ); + + /* primes with the negative top var are those of P0 that are not in F10 */ + zResN = cuddZddDiff( dd, zP0, zResE ); + if ( zResN == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zResE ); + Cudd_RecursiveDerefZdd( dd, zP0 ); + Cudd_RecursiveDerefZdd( dd, zP1 ); + return NULL; + } + cuddRef( zResN ); + Cudd_RecursiveDerefZdd( dd, zP0 ); + + /* primes with the positive top var are those of P1 that are not in F10 */ + zResP = cuddZddDiff( dd, zP1, zResE ); + if ( zResP == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zResE ); + Cudd_RecursiveDerefZdd( dd, zResN ); + Cudd_RecursiveDerefZdd( dd, zP1 ); + return NULL; + } + cuddRef( zResP ); + Cudd_RecursiveDerefZdd( dd, zP1 ); + } + + zRes = extraComposeCover( dd, zResN, zResP, zResE, Cudd_Regular(F)->index ); + if ( zRes == NULL ) return NULL; + + /* insert the result into cache */ + cuddCacheInsert1(dd, extraZddPrimes, F, zRes); + return zRes; + } +} /* end of extraZddPrimes */ + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_bddPermute.] + + Description [ Recursively puts the BDD in the order given in the array permut. + Checks for trivial cases to terminate recursion, then splits on the + children of this node. Once the solutions for the children are + obtained, it puts into the current position the node from the rest of + the BDD that should be here. Then returns this BDD. + The key here is that the node being visited is NOT put in its proper + place by this instance, but rather is switched when its proper position + is reached in the recursion tree.

        + The DdNode * that is returned is the same BDD as passed in as node, + but in the new order.] + + SideEffects [None] + + SeeAlso [Cudd_bddPermute cuddAddPermuteRecur] + +******************************************************************************/ +static DdNode * +cuddBddPermuteRecur( DdManager * manager /* DD manager */ , + DdHashTable * table /* computed table */ , + DdNode * node /* BDD to be reordered */ , + int *permut /* permutation array */ ) +{ + DdNode *N, *T, *E; + DdNode *res; + int index; + + statLine( manager ); + N = Cudd_Regular( node ); + + /* Check for terminal case of constant node. */ + if ( cuddIsConstant( N ) ) + { + return ( node ); + } + + /* If problem already solved, look up answer and return. */ + if ( N->ref != 1 && ( res = cuddHashTableLookup1( table, N ) ) != NULL ) + { +#ifdef DD_DEBUG + bddPermuteRecurHits++; +#endif + return ( Cudd_NotCond( res, N != node ) ); + } + + /* Split and recur on children of this node. */ + T = cuddBddPermuteRecur( manager, table, cuddT( N ), permut ); + if ( T == NULL ) + return ( NULL ); + cuddRef( T ); + E = cuddBddPermuteRecur( manager, table, cuddE( N ), permut ); + if ( E == NULL ) + { + Cudd_IterDerefBdd( manager, T ); + return ( NULL ); + } + cuddRef( E ); + + /* Move variable that should be in this position to this position + ** by retrieving the single var BDD for that variable, and calling + ** cuddBddIteRecur with the T and E we just created. + */ + index = permut[N->index]; + res = cuddBddIteRecur( manager, manager->vars[index], T, E ); + if ( res == NULL ) + { + Cudd_IterDerefBdd( manager, T ); + Cudd_IterDerefBdd( manager, E ); + return ( NULL ); + } + cuddRef( res ); + Cudd_IterDerefBdd( manager, T ); + Cudd_IterDerefBdd( manager, E ); + + /* Do not keep the result if the reference count is only 1, since + ** it will not be visited again. + */ + if ( N->ref != 1 ) + { + ptrint fanout = ( ptrint ) N->ref; + cuddSatDec( fanout ); + if ( !cuddHashTableInsert1( table, N, res, fanout ) ) + { + Cudd_IterDerefBdd( manager, res ); + return ( NULL ); + } + } + cuddDeref( res ); + return ( Cudd_NotCond( res, N != node ) ); + +} /* end of cuddBddPermuteRecur */ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/extra/extraBddSymm.c b/abc_with_bb_support/src/misc/extra/extraBddSymm.c new file mode 100644 index 000000000..94565dc3b --- /dev/null +++ b/abc_with_bb_support/src/misc/extra/extraBddSymm.c @@ -0,0 +1,1469 @@ +/**CFile**************************************************************** + + FileName [extraBddSymm.c] + + PackageName [extra] + + Synopsis [Efficient methods to compute the information about + symmetric variables using the algorithm presented in the paper: + A. Mishchenko. Fast Computation of Symmetries in Boolean Functions. + Transactions on CAD, Nov. 2003.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - September 1, 2003.] + + Revision [$Id: extraBddSymm.c,v 1.0 2003/09/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "extra.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +#define DD_GET_SYMM_VARS_TAG 0x0a /* former DD_BDD_XOR_EXIST_ABSTRACT_TAG */ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticEnd***************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Computes the classical symmetry information for the function.] + + Description [Returns the symmetry information in the form of Extra_SymmInfo_t structure.] + + SideEffects [If the ZDD variables are not derived from BDD variables with + multiplicity 2, this function may derive them in a wrong way.] + + SeeAlso [] + +******************************************************************************/ +Extra_SymmInfo_t * Extra_SymmPairsCompute( + DdManager * dd, /* the manager */ + DdNode * bFunc) /* the function whose symmetries are computed */ +{ + DdNode * bSupp; + DdNode * zRes; + Extra_SymmInfo_t * p; + + bSupp = Cudd_Support( dd, bFunc ); Cudd_Ref( bSupp ); + zRes = Extra_zddSymmPairsCompute( dd, bFunc, bSupp ); Cudd_Ref( zRes ); + + p = Extra_SymmPairsCreateFromZdd( dd, zRes, bSupp ); + + Cudd_RecursiveDeref( dd, bSupp ); + Cudd_RecursiveDerefZdd( dd, zRes ); + + return p; + +} /* end of Extra_SymmPairsCompute */ + + +/**Function******************************************************************** + + Synopsis [Computes the classical symmetry information as a ZDD.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * Extra_zddSymmPairsCompute( + DdManager * dd, /* the DD manager */ + DdNode * bF, + DdNode * bVars) +{ + DdNode * res; + do { + dd->reordered = 0; + res = extraZddSymmPairsCompute( dd, bF, bVars ); + } while (dd->reordered == 1); + return(res); + +} /* end of Extra_zddSymmPairsCompute */ + +/**Function******************************************************************** + + Synopsis [Returns a singleton-set ZDD containing all variables that are symmetric with the given one.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * Extra_zddGetSymmetricVars( + DdManager * dd, /* the DD manager */ + DdNode * bF, /* the first function - originally, the positive cofactor */ + DdNode * bG, /* the second fucntion - originally, the negative cofactor */ + DdNode * bVars) /* the set of variables, on which F and G depend */ +{ + DdNode * res; + do { + dd->reordered = 0; + res = extraZddGetSymmetricVars( dd, bF, bG, bVars ); + } while (dd->reordered == 1); + return(res); + +} /* end of Extra_zddGetSymmetricVars */ + + +/**Function******************************************************************** + + Synopsis [Converts a set of variables into a set of singleton subsets.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * Extra_zddGetSingletons( + DdManager * dd, /* the DD manager */ + DdNode * bVars) /* the set of variables */ +{ + DdNode * res; + do { + dd->reordered = 0; + res = extraZddGetSingletons( dd, bVars ); + } while (dd->reordered == 1); + return(res); + +} /* end of Extra_zddGetSingletons */ + +/**Function******************************************************************** + + Synopsis [Filters the set of variables using the support of the function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * Extra_bddReduceVarSet( + DdManager * dd, /* the DD manager */ + DdNode * bVars, /* the set of variables to be reduced */ + DdNode * bF) /* the function whose support is used for reduction */ +{ + DdNode * res; + do { + dd->reordered = 0; + res = extraBddReduceVarSet( dd, bVars, bF ); + } while (dd->reordered == 1); + return(res); + +} /* end of Extra_bddReduceVarSet */ + + +/**Function******************************************************************** + + Synopsis [Allocates symmetry information structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +Extra_SymmInfo_t * Extra_SymmPairsAllocate( int nVars ) +{ + int i; + Extra_SymmInfo_t * p; + + // allocate and clean the storage for symmetry info + p = ALLOC( Extra_SymmInfo_t, 1 ); + memset( p, 0, sizeof(Extra_SymmInfo_t) ); + p->nVars = nVars; + p->pVars = ALLOC( int, nVars ); + p->pSymms = ALLOC( char *, nVars ); + p->pSymms[0] = ALLOC( char , nVars * nVars ); + memset( p->pSymms[0], 0, nVars * nVars * sizeof(char) ); + + for ( i = 1; i < nVars; i++ ) + p->pSymms[i] = p->pSymms[i-1] + nVars; + + return p; +} /* end of Extra_SymmPairsAllocate */ + +/**Function******************************************************************** + + Synopsis [Deallocates symmetry information structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void Extra_SymmPairsDissolve( Extra_SymmInfo_t * p ) +{ + free( p->pVars ); + free( p->pSymms[0] ); + free( p->pSymms ); + free( p ); +} /* end of Extra_SymmPairsDissolve */ + +/**Function******************************************************************** + + Synopsis [Allocates symmetry information structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void Extra_SymmPairsPrint( Extra_SymmInfo_t * p ) +{ + int i, k; + printf( "\n" ); + for ( i = 0; i < p->nVars; i++ ) + { + for ( k = 0; k <= i; k++ ) + printf( " " ); + for ( k = i+1; k < p->nVars; k++ ) + if ( p->pSymms[i][k] ) + printf( "1" ); + else + printf( "." ); + printf( "\n" ); + } +} /* end of Extra_SymmPairsPrint */ + + +/**Function******************************************************************** + + Synopsis [Creates the symmetry information structure from ZDD.] + + Description [ZDD representation of symmetries is the set of cubes, each + of which has two variables in the positive polarity. These variables correspond + to the symmetric variable pair.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +Extra_SymmInfo_t * Extra_SymmPairsCreateFromZdd( DdManager * dd, DdNode * zPairs, DdNode * bSupp ) +{ + int i; + int nSuppSize; + Extra_SymmInfo_t * p; + int * pMapVars2Nums; + DdNode * bTemp; + DdNode * zSet, * zCube, * zTemp; + int iVar1, iVar2; + + nSuppSize = Extra_bddSuppSize( dd, bSupp ); + + // allocate and clean the storage for symmetry info + p = Extra_SymmPairsAllocate( nSuppSize ); + + // allocate the storage for the temporary map + pMapVars2Nums = ALLOC( int, dd->size ); + memset( pMapVars2Nums, 0, dd->size * sizeof(int) ); + + // assign the variables + p->nVarsMax = dd->size; +// p->nNodes = Cudd_DagSize( zPairs ); + p->nNodes = 0; + for ( i = 0, bTemp = bSupp; bTemp != b1; bTemp = cuddT(bTemp), i++ ) + { + p->pVars[i] = bTemp->index; + pMapVars2Nums[bTemp->index] = i; + } + + // write the symmetry info into the structure + zSet = zPairs; Cudd_Ref( zSet ); + while ( zSet != z0 ) + { + // get the next cube + zCube = Extra_zddSelectOneSubset( dd, zSet ); Cudd_Ref( zCube ); + + // add these two variables to the data structure + assert( cuddT( cuddT(zCube) ) == z1 ); + iVar1 = zCube->index/2; + iVar2 = cuddT(zCube)->index/2; + if ( pMapVars2Nums[iVar1] < pMapVars2Nums[iVar2] ) + p->pSymms[ pMapVars2Nums[iVar1] ][ pMapVars2Nums[iVar2] ] = 1; + else + p->pSymms[ pMapVars2Nums[iVar2] ][ pMapVars2Nums[iVar1] ] = 1; + // count the symmetric pairs + p->nSymms ++; + + // update the cuver and deref the cube + zSet = Cudd_zddDiff( dd, zTemp = zSet, zCube ); Cudd_Ref( zSet ); + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zCube ); + + } // for each cube + Cudd_RecursiveDerefZdd( dd, zSet ); + + FREE( pMapVars2Nums ); + return p; + +} /* end of Extra_SymmPairsCreateFromZdd */ + + +/**Function******************************************************************** + + Synopsis [Checks the possibility of two variables being symmetric.] + + Description [Returns 0 if vars are not symmetric. Return 1 if vars can be symmetric.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int Extra_bddCheckVarsSymmetric( + DdManager * dd, /* the DD manager */ + DdNode * bF, + int iVar1, + int iVar2) +{ + DdNode * bVars; + int Res; + +// return 1; + + assert( iVar1 != iVar2 ); + assert( iVar1 < dd->size ); + assert( iVar2 < dd->size ); + + bVars = Cudd_bddAnd( dd, dd->vars[iVar1], dd->vars[iVar2] ); Cudd_Ref( bVars ); + + Res = (int)( extraBddCheckVarsSymmetric( dd, bF, bVars ) == b1 ); + + Cudd_RecursiveDeref( dd, bVars ); + + return Res; +} /* end of Extra_bddCheckVarsSymmetric */ + + +/**Function******************************************************************** + + Synopsis [Computes the classical symmetry information for the function.] + + Description [Uses the naive way of comparing cofactors.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +Extra_SymmInfo_t * Extra_SymmPairsComputeNaive( DdManager * dd, DdNode * bFunc ) +{ + DdNode * bSupp, * bTemp; + int nSuppSize; + Extra_SymmInfo_t * p; + int i, k; + + // compute the support + bSupp = Cudd_Support( dd, bFunc ); Cudd_Ref( bSupp ); + nSuppSize = Extra_bddSuppSize( dd, bSupp ); +//printf( "Support = %d. ", nSuppSize ); +//Extra_bddPrint( dd, bSupp ); +//printf( "%d ", nSuppSize ); + + // allocate the storage for symmetry info + p = Extra_SymmPairsAllocate( nSuppSize ); + + // assign the variables + p->nVarsMax = dd->size; + for ( i = 0, bTemp = bSupp; bTemp != b1; bTemp = cuddT(bTemp), i++ ) + p->pVars[i] = bTemp->index; + + // go through the candidate pairs and check using Idea1 + for ( i = 0; i < nSuppSize; i++ ) + for ( k = i+1; k < nSuppSize; k++ ) + { + p->pSymms[k][i] = p->pSymms[i][k] = Extra_bddCheckVarsSymmetricNaive( dd, bFunc, p->pVars[i], p->pVars[k] ); + if ( p->pSymms[i][k] ) + p->nSymms++; + } + + Cudd_RecursiveDeref( dd, bSupp ); + return p; + +} /* end of Extra_SymmPairsComputeNaive */ + +/**Function******************************************************************** + + Synopsis [Checks if the two variables are symmetric.] + + Description [Returns 0 if vars are not symmetric. Return 1 if vars are symmetric.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int Extra_bddCheckVarsSymmetricNaive( + DdManager * dd, /* the DD manager */ + DdNode * bF, + int iVar1, + int iVar2) +{ + DdNode * bCube1, * bCube2; + DdNode * bCof01, * bCof10; + int Res; + + assert( iVar1 != iVar2 ); + assert( iVar1 < dd->size ); + assert( iVar2 < dd->size ); + + bCube1 = Cudd_bddAnd( dd, Cudd_Not( dd->vars[iVar1] ), dd->vars[iVar2] ); Cudd_Ref( bCube1 ); + bCube2 = Cudd_bddAnd( dd, Cudd_Not( dd->vars[iVar2] ), dd->vars[iVar1] ); Cudd_Ref( bCube2 ); + + bCof01 = Cudd_Cofactor( dd, bF, bCube1 ); Cudd_Ref( bCof01 ); + bCof10 = Cudd_Cofactor( dd, bF, bCube2 ); Cudd_Ref( bCof10 ); + + Res = (int)( bCof10 == bCof01 ); + + Cudd_RecursiveDeref( dd, bCof01 ); + Cudd_RecursiveDeref( dd, bCof10 ); + Cudd_RecursiveDeref( dd, bCube1 ); + Cudd_RecursiveDeref( dd, bCube2 ); + + return Res; +} /* end of Extra_bddCheckVarsSymmetricNaive */ + + +/**Function******************************************************************** + + Synopsis [Builds ZDD representing the set of fixed-size variable tuples.] + + Description [Creates ZDD of all combinations of variables in Support that + is represented by a BDD.] + + SideEffects [New ZDD variables are created if indices of the variables + present in the combination are larger than the currently + allocated number of ZDD variables.] + + SeeAlso [] + +******************************************************************************/ +DdNode* Extra_zddTuplesFromBdd( + DdManager * dd, /* the DD manager */ + int K, /* the number of variables in tuples */ + DdNode * bVarsN) /* the set of all variables represented as a BDD */ +{ + DdNode *zRes; + int autoDynZ; + + autoDynZ = dd->autoDynZ; + dd->autoDynZ = 0; + + do { + /* transform the numeric arguments (K) into a DdNode* argument; + * this allows us to use the standard internal CUDD cache */ + DdNode *bVarSet = bVarsN, *bVarsK = bVarsN; + int nVars = 0, i; + + /* determine the number of variables in VarSet */ + while ( bVarSet != b1 ) + { + nVars++; + /* make sure that the VarSet is a cube */ + if ( cuddE( bVarSet ) != b0 ) + return NULL; + bVarSet = cuddT( bVarSet ); + } + /* make sure that the number of variables in VarSet is less or equal + that the number of variables that should be present in the tuples + */ + if ( K > nVars ) + return NULL; + + /* the second argument in the recursive call stannds for ; + /* reate the first argument, which stands for + * as when we are talking about the tuple of out of */ + for ( i = 0; i < nVars-K; i++ ) + bVarsK = cuddT( bVarsK ); + + dd->reordered = 0; + zRes = extraZddTuplesFromBdd(dd, bVarsK, bVarsN ); + + } while (dd->reordered == 1); + dd->autoDynZ = autoDynZ; + return zRes; + +} /* end of Extra_zddTuplesFromBdd */ + +/**Function******************************************************************** + + Synopsis [Selects one subset from the set of subsets represented by a ZDD.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode* Extra_zddSelectOneSubset( + DdManager * dd, /* the DD manager */ + DdNode * zS) /* the ZDD */ +{ + DdNode *res; + do { + dd->reordered = 0; + res = extraZddSelectOneSubset(dd, zS); + } while (dd->reordered == 1); + return(res); + +} /* end of Extra_zddSelectOneSubset */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Performs a recursive step of Extra_SymmPairsCompute.] + + Description [Returns the set of symmetric variable pairs represented as a set + of two-literal ZDD cubes. Both variables always appear in the positive polarity + in the cubes. This function works without building new BDD nodes. Some relatively + small number of ZDD nodes may be built to ensure proper bookkeeping of the + symmetry information.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * +extraZddSymmPairsCompute( + DdManager * dd, /* the manager */ + DdNode * bFunc, /* the function whose symmetries are computed */ + DdNode * bVars ) /* the set of variables on which this function depends */ +{ + DdNode * zRes; + DdNode * bFR = Cudd_Regular(bFunc); + + if ( cuddIsConstant(bFR) ) + { + int nVars, i; + + // determine how many vars are in the bVars + nVars = Extra_bddSuppSize( dd, bVars ); + if ( nVars < 2 ) + return z0; + else + { + DdNode * bVarsK; + + // create the BDD bVarsK corresponding to K = 2; + bVarsK = bVars; + for ( i = 0; i < nVars-2; i++ ) + bVarsK = cuddT( bVarsK ); + return extraZddTuplesFromBdd( dd, bVarsK, bVars ); + } + } + assert( bVars != b1 ); + + if ( zRes = cuddCacheLookup2Zdd(dd, extraZddSymmPairsCompute, bFunc, bVars) ) + return zRes; + else + { + DdNode * zRes0, * zRes1; + DdNode * zTemp, * zPlus, * zSymmVars; + DdNode * bF0, * bF1; + DdNode * bVarsNew; + int nVarsExtra; + int LevelF; + + // every variable in bF should be also in bVars, therefore LevelF cannot be above LevelV + // if LevelF is below LevelV, scroll through the vars in bVars to the same level as F + // count how many extra vars are there in bVars + nVarsExtra = 0; + LevelF = dd->perm[bFR->index]; + for ( bVarsNew = bVars; LevelF > dd->perm[bVarsNew->index]; bVarsNew = cuddT(bVarsNew) ) + nVarsExtra++; + // the indexes (level) of variables should be synchronized now + assert( bFR->index == bVarsNew->index ); + + // cofactor the function + if ( bFR != bFunc ) // bFunc is complemented + { + bF0 = Cudd_Not( cuddE(bFR) ); + bF1 = Cudd_Not( cuddT(bFR) ); + } + else + { + bF0 = cuddE(bFR); + bF1 = cuddT(bFR); + } + + // solve subproblems + zRes0 = extraZddSymmPairsCompute( dd, bF0, cuddT(bVarsNew) ); + if ( zRes0 == NULL ) + return NULL; + cuddRef( zRes0 ); + + // if there is no symmetries in the negative cofactor + // there is no need to test the positive cofactor + if ( zRes0 == z0 ) + zRes = zRes0; // zRes takes reference + else + { + zRes1 = extraZddSymmPairsCompute( dd, bF1, cuddT(bVarsNew) ); + if ( zRes1 == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zRes0 ); + return NULL; + } + cuddRef( zRes1 ); + + // only those variables are pair-wise symmetric + // that are pair-wise symmetric in both cofactors + // therefore, intersect the solutions + zRes = cuddZddIntersect( dd, zRes0, zRes1 ); + if ( zRes == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zRes0 ); + Cudd_RecursiveDerefZdd( dd, zRes1 ); + return NULL; + } + cuddRef( zRes ); + Cudd_RecursiveDerefZdd( dd, zRes0 ); + Cudd_RecursiveDerefZdd( dd, zRes1 ); + } + + // consider the current top-most variable and find all the vars + // that are pairwise symmetric with it + // these variables are returned as a set of ZDD singletons + zSymmVars = extraZddGetSymmetricVars( dd, bF1, bF0, cuddT(bVarsNew) ); + if ( zSymmVars == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zRes ); + return NULL; + } + cuddRef( zSymmVars ); + + // attach the topmost variable to the set, to get the variable pairs + // use the positive polarity ZDD variable for the purpose + + // there is no need to do so, if zSymmVars is empty + if ( zSymmVars == z0 ) + Cudd_RecursiveDerefZdd( dd, zSymmVars ); + else + { + zPlus = cuddZddGetNode( dd, 2*bFR->index, zSymmVars, z0 ); + if ( zPlus == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zRes ); + Cudd_RecursiveDerefZdd( dd, zSymmVars ); + return NULL; + } + cuddRef( zPlus ); + cuddDeref( zSymmVars ); + + // add these variable pairs to the result + zRes = cuddZddUnion( dd, zTemp = zRes, zPlus ); + if ( zRes == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zPlus ); + return NULL; + } + cuddRef( zRes ); + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zPlus ); + } + + // only zRes is referenced at this point + + // if we skipped some variables, these variables cannot be symmetric with + // any variables that are currently in the support of bF, but they can be + // symmetric with the variables that are in bVars but not in the support of bF + if ( nVarsExtra ) + { + // it is possible to improve this step: + // (1) there is no need to enter here, if nVarsExtra < 2 + + // create the set of topmost nVarsExtra in bVars + DdNode * bVarsExtra; + int nVars; + + // remove from bVars all the variable that are in the support of bFunc + bVarsExtra = extraBddReduceVarSet( dd, bVars, bFunc ); + if ( bVarsExtra == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zRes ); + return NULL; + } + cuddRef( bVarsExtra ); + + // determine how many vars are in the bVarsExtra + nVars = Extra_bddSuppSize( dd, bVarsExtra ); + if ( nVars < 2 ) + { + Cudd_RecursiveDeref( dd, bVarsExtra ); + } + else + { + int i; + DdNode * bVarsK; + + // create the BDD bVarsK corresponding to K = 2; + bVarsK = bVarsExtra; + for ( i = 0; i < nVars-2; i++ ) + bVarsK = cuddT( bVarsK ); + + // create the 2 variable tuples + zPlus = extraZddTuplesFromBdd( dd, bVarsK, bVarsExtra ); + if ( zPlus == NULL ) + { + Cudd_RecursiveDeref( dd, bVarsExtra ); + Cudd_RecursiveDerefZdd( dd, zRes ); + return NULL; + } + cuddRef( zPlus ); + Cudd_RecursiveDeref( dd, bVarsExtra ); + + // add these to the result + zRes = cuddZddUnion( dd, zTemp = zRes, zPlus ); + if ( zRes == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zPlus ); + return NULL; + } + cuddRef( zRes ); + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zPlus ); + } + } + cuddDeref( zRes ); + + + /* insert the result into cache */ + cuddCacheInsert2(dd, extraZddSymmPairsCompute, bFunc, bVars, zRes); + return zRes; + } +} /* end of extraZddSymmPairsCompute */ + +/**Function******************************************************************** + + Synopsis [Performs a recursive step of Extra_zddGetSymmetricVars.] + + Description [Returns the set of ZDD singletons, containing those positive + ZDD variables that correspond to BDD variables x, for which it is true + that bF(x=0) == bG(x=1).] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * extraZddGetSymmetricVars( + DdManager * dd, /* the DD manager */ + DdNode * bF, /* the first function - originally, the positive cofactor */ + DdNode * bG, /* the second function - originally, the negative cofactor */ + DdNode * bVars) /* the set of variables, on which F and G depend */ +{ + DdNode * zRes; + DdNode * bFR = Cudd_Regular(bF); + DdNode * bGR = Cudd_Regular(bG); + + if ( cuddIsConstant(bFR) && cuddIsConstant(bGR) ) + { + if ( bF == bG ) + return extraZddGetSingletons( dd, bVars ); + else + return z0; + } + assert( bVars != b1 ); + + if ( zRes = cuddCacheLookupZdd(dd, DD_GET_SYMM_VARS_TAG, bF, bG, bVars) ) + return zRes; + else + { + DdNode * zRes0, * zRes1; + DdNode * zPlus, * zTemp; + DdNode * bF0, * bF1; + DdNode * bG0, * bG1; + DdNode * bVarsNew; + + int LevelF = cuddI(dd,bFR->index); + int LevelG = cuddI(dd,bGR->index); + int LevelFG; + + if ( LevelF < LevelG ) + LevelFG = LevelF; + else + LevelFG = LevelG; + + // at least one of the arguments is not a constant + assert( LevelFG < dd->size ); + + // every variable in bF and bG should be also in bVars, therefore LevelFG cannot be above LevelV + // if LevelFG is below LevelV, scroll through the vars in bVars to the same level as LevelFG + for ( bVarsNew = bVars; LevelFG > dd->perm[bVarsNew->index]; bVarsNew = cuddT(bVarsNew) ); + assert( LevelFG == dd->perm[bVarsNew->index] ); + + // cofactor the functions + if ( LevelF == LevelFG ) + { + if ( bFR != bF ) // bF is complemented + { + bF0 = Cudd_Not( cuddE(bFR) ); + bF1 = Cudd_Not( cuddT(bFR) ); + } + else + { + bF0 = cuddE(bFR); + bF1 = cuddT(bFR); + } + } + else + bF0 = bF1 = bF; + + if ( LevelG == LevelFG ) + { + if ( bGR != bG ) // bG is complemented + { + bG0 = Cudd_Not( cuddE(bGR) ); + bG1 = Cudd_Not( cuddT(bGR) ); + } + else + { + bG0 = cuddE(bGR); + bG1 = cuddT(bGR); + } + } + else + bG0 = bG1 = bG; + + // solve subproblems + zRes0 = extraZddGetSymmetricVars( dd, bF0, bG0, cuddT(bVarsNew) ); + if ( zRes0 == NULL ) + return NULL; + cuddRef( zRes0 ); + + // if there is not symmetries in the negative cofactor + // there is no need to test the positive cofactor + if ( zRes0 == z0 ) + zRes = zRes0; // zRes takes reference + else + { + zRes1 = extraZddGetSymmetricVars( dd, bF1, bG1, cuddT(bVarsNew) ); + if ( zRes1 == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zRes0 ); + return NULL; + } + cuddRef( zRes1 ); + + // only those variables should belong to the resulting set + // for which the property is true for both cofactors + zRes = cuddZddIntersect( dd, zRes0, zRes1 ); + if ( zRes == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zRes0 ); + Cudd_RecursiveDerefZdd( dd, zRes1 ); + return NULL; + } + cuddRef( zRes ); + Cudd_RecursiveDerefZdd( dd, zRes0 ); + Cudd_RecursiveDerefZdd( dd, zRes1 ); + } + + // add one more singleton if the property is true for this variable + if ( bF0 == bG1 ) + { + zPlus = cuddZddGetNode( dd, 2*bVarsNew->index, z1, z0 ); + if ( zPlus == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zRes ); + return NULL; + } + cuddRef( zPlus ); + + // add these variable pairs to the result + zRes = cuddZddUnion( dd, zTemp = zRes, zPlus ); + if ( zRes == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zPlus ); + return NULL; + } + cuddRef( zRes ); + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zPlus ); + } + + if ( bF == bG && bVars != bVarsNew ) + { + // if the functions are equal, so are their cofactors + // add those variables from V that are above F and G + + DdNode * bVarsExtra; + + assert( LevelFG > dd->perm[bVars->index] ); + + // create the BDD of the extra variables + bVarsExtra = cuddBddExistAbstractRecur( dd, bVars, bVarsNew ); + if ( bVarsExtra == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zRes ); + return NULL; + } + cuddRef( bVarsExtra ); + + zPlus = extraZddGetSingletons( dd, bVarsExtra ); + if ( zPlus == NULL ) + { + Cudd_RecursiveDeref( dd, bVarsExtra ); + Cudd_RecursiveDerefZdd( dd, zRes ); + return NULL; + } + cuddRef( zPlus ); + Cudd_RecursiveDeref( dd, bVarsExtra ); + + // add these to the result + zRes = cuddZddUnion( dd, zTemp = zRes, zPlus ); + if ( zRes == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zPlus ); + return NULL; + } + cuddRef( zRes ); + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zPlus ); + } + cuddDeref( zRes ); + + cuddCacheInsert( dd, DD_GET_SYMM_VARS_TAG, bF, bG, bVars, zRes ); + return zRes; + } +} /* end of extraZddGetSymmetricVars */ + + +/**Function******************************************************************** + + Synopsis [Performs a recursive step of Extra_zddGetSingletons.] + + Description [Returns the set of ZDD singletons, containing those positive + polarity ZDD variables that correspond to the BDD variables in bVars.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * extraZddGetSingletons( + DdManager * dd, /* the DD manager */ + DdNode * bVars) /* the set of variables */ +{ + DdNode * zRes; + + if ( bVars == b1 ) +// if ( bVars == b0 ) // bug fixed by Jin Zhang, Jan 23, 2004 + return z1; + + if ( zRes = cuddCacheLookup1Zdd(dd, extraZddGetSingletons, bVars) ) + return zRes; + else + { + DdNode * zTemp, * zPlus; + + // solve subproblem + zRes = extraZddGetSingletons( dd, cuddT(bVars) ); + if ( zRes == NULL ) + return NULL; + cuddRef( zRes ); + + zPlus = cuddZddGetNode( dd, 2*bVars->index, z1, z0 ); + if ( zPlus == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zRes ); + return NULL; + } + cuddRef( zPlus ); + + // add these to the result + zRes = cuddZddUnion( dd, zTemp = zRes, zPlus ); + if ( zRes == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zPlus ); + return NULL; + } + cuddRef( zRes ); + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zPlus ); + cuddDeref( zRes ); + + cuddCacheInsert1( dd, extraZddGetSingletons, bVars, zRes ); + return zRes; + } +} /* end of extraZddGetSingletons */ + + +/**Function******************************************************************** + + Synopsis [Performs a recursive step of Extra_bddReduceVarSet.] + + Description [Returns the set of all variables in the given set that are not in the + support of the given function.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * extraBddReduceVarSet( + DdManager * dd, /* the DD manager */ + DdNode * bVars, /* the set of variables to be reduced */ + DdNode * bF) /* the function whose support is used for reduction */ +{ + DdNode * bRes; + DdNode * bFR = Cudd_Regular(bF); + + if ( cuddIsConstant(bFR) || bVars == b1 ) + return bVars; + + if ( bRes = cuddCacheLookup2(dd, extraBddReduceVarSet, bVars, bF) ) + return bRes; + else + { + DdNode * bF0, * bF1; + DdNode * bVarsThis, * bVarsLower, * bTemp; + int LevelF; + + // if LevelF is below LevelV, scroll through the vars in bVars + LevelF = dd->perm[bFR->index]; + for ( bVarsThis = bVars; LevelF > cuddI(dd,bVarsThis->index); bVarsThis = cuddT(bVarsThis) ); + // scroll also through the current var, because it should be not be added + if ( LevelF == cuddI(dd,bVarsThis->index) ) + bVarsLower = cuddT(bVarsThis); + else + bVarsLower = bVarsThis; + + // cofactor the function + if ( bFR != bF ) // bFunc is complemented + { + bF0 = Cudd_Not( cuddE(bFR) ); + bF1 = Cudd_Not( cuddT(bFR) ); + } + else + { + bF0 = cuddE(bFR); + bF1 = cuddT(bFR); + } + + // solve subproblems + bRes = extraBddReduceVarSet( dd, bVarsLower, bF0 ); + if ( bRes == NULL ) + return NULL; + cuddRef( bRes ); + + bRes = extraBddReduceVarSet( dd, bTemp = bRes, bF1 ); + if ( bRes == NULL ) + { + Cudd_RecursiveDeref( dd, bTemp ); + return NULL; + } + cuddRef( bRes ); + Cudd_RecursiveDeref( dd, bTemp ); + + // the current var should not be added + // add the skipped vars + if ( bVarsThis != bVars ) + { + DdNode * bVarsExtra; + + // extract the skipped variables + bVarsExtra = cuddBddExistAbstractRecur( dd, bVars, bVarsThis ); + if ( bVarsExtra == NULL ) + { + Cudd_RecursiveDeref( dd, bRes ); + return NULL; + } + cuddRef( bVarsExtra ); + + // add these variables + bRes = cuddBddAndRecur( dd, bTemp = bRes, bVarsExtra ); + if ( bRes == NULL ) + { + Cudd_RecursiveDeref( dd, bTemp ); + Cudd_RecursiveDeref( dd, bVarsExtra ); + return NULL; + } + cuddRef( bRes ); + Cudd_RecursiveDeref( dd, bTemp ); + Cudd_RecursiveDeref( dd, bVarsExtra ); + } + cuddDeref( bRes ); + + cuddCacheInsert2( dd, extraBddReduceVarSet, bVars, bF, bRes ); + return bRes; + } +} /* end of extraBddReduceVarSet */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Extra_bddCheckVarsSymmetric().] + + Description [Returns b0 if the variables are not symmetric. Returns b1 if the + variables can be symmetric. The variables are represented in the form of a + two-variable cube. In case the cube contains one variable (below Var1 level), + the cube's pointer is complemented if the variable Var1 occurred on the + current path; otherwise, the cube's pointer is regular. Uses additional + complemented bit (Hash_Not) to mark the result if in the BDD rooted that this + node there is a branch passing though the node labeled with Var2.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * extraBddCheckVarsSymmetric( + DdManager * dd, /* the DD manager */ + DdNode * bF, + DdNode * bVars) +{ + DdNode * bRes; + + if ( bF == b0 ) + return b1; + + assert( bVars != b1 ); + + if ( bRes = cuddCacheLookup2(dd, extraBddCheckVarsSymmetric, bF, bVars) ) + return bRes; + else + { + DdNode * bRes0, * bRes1; + DdNode * bF0, * bF1; + DdNode * bFR = Cudd_Regular(bF); + int LevelF = cuddI(dd,bFR->index); + + DdNode * bVarsR = Cudd_Regular(bVars); + int fVar1Pres; + int iLev1; + int iLev2; + + if ( bVarsR != bVars ) // cube's pointer is complemented + { + assert( cuddT(bVarsR) == b1 ); + fVar1Pres = 1; // the first var is present on the path + iLev1 = -1; // we are already below the first var level + iLev2 = dd->perm[bVarsR->index]; // the level of the second var + } + else // cube's pointer is NOT complemented + { + fVar1Pres = 0; // the first var is absent on the path + if ( cuddT(bVars) == b1 ) + { + iLev1 = -1; // we are already below the first var level + iLev2 = dd->perm[bVars->index]; // the level of the second var + } + else + { + assert( cuddT(cuddT(bVars)) == b1 ); + iLev1 = dd->perm[bVars->index]; // the level of the first var + iLev2 = dd->perm[cuddT(bVars)->index]; // the level of the second var + } + } + + // cofactor the function + // the cofactors are needed only if we are above the second level + if ( LevelF < iLev2 ) + { + if ( bFR != bF ) // bFunc is complemented + { + bF0 = Cudd_Not( cuddE(bFR) ); + bF1 = Cudd_Not( cuddT(bFR) ); + } + else + { + bF0 = cuddE(bFR); + bF1 = cuddT(bFR); + } + } + else + bF0 = bF1 = NULL; + + // consider five cases: + // (1) F is above iLev1 + // (2) F is on the level iLev1 + // (3) F is between iLev1 and iLev2 + // (4) F is on the level iLev2 + // (5) F is below iLev2 + + // (1) F is above iLev1 + if ( LevelF < iLev1 ) + { + // the returned result cannot have the hash attribute + // because we still did not reach the level of Var1; + // the attribute never travels above the level of Var1 + bRes0 = extraBddCheckVarsSymmetric( dd, bF0, bVars ); +// assert( !Hash_IsComplement( bRes0 ) ); + assert( bRes0 != z0 ); + if ( bRes0 == b0 ) + bRes = b0; + else + bRes = extraBddCheckVarsSymmetric( dd, bF1, bVars ); +// assert( !Hash_IsComplement( bRes ) ); + assert( bRes != z0 ); + } + // (2) F is on the level iLev1 + else if ( LevelF == iLev1 ) + { + bRes0 = extraBddCheckVarsSymmetric( dd, bF0, Cudd_Not( cuddT(bVars) ) ); + if ( bRes0 == b0 ) + bRes = b0; + else + { + bRes1 = extraBddCheckVarsSymmetric( dd, bF1, Cudd_Not( cuddT(bVars) ) ); + if ( bRes1 == b0 ) + bRes = b0; + else + { +// if ( Hash_IsComplement( bRes0 ) || Hash_IsComplement( bRes1 ) ) + if ( bRes0 == z0 || bRes1 == z0 ) + bRes = b1; + else + bRes = b0; + } + } + } + // (3) F is between iLev1 and iLev2 + else if ( LevelF < iLev2 ) + { + bRes0 = extraBddCheckVarsSymmetric( dd, bF0, bVars ); + if ( bRes0 == b0 ) + bRes = b0; + else + { + bRes1 = extraBddCheckVarsSymmetric( dd, bF1, bVars ); + if ( bRes1 == b0 ) + bRes = b0; + else + { +// if ( Hash_IsComplement( bRes0 ) || Hash_IsComplement( bRes1 ) ) +// bRes = Hash_Not( b1 ); + if ( bRes0 == z0 || bRes1 == z0 ) + bRes = z0; + else + bRes = b1; + } + } + } + // (4) F is on the level iLev2 + else if ( LevelF == iLev2 ) + { + // this is the only place where the hash attribute (Hash_Not) can be added + // to the result; it can be added only if the path came through the node + // lebeled with Var1; therefore, the hash attribute cannot be returned + // to the caller function + if ( fVar1Pres ) +// bRes = Hash_Not( b1 ); + bRes = z0; + else + bRes = b0; + } + // (5) F is below iLev2 + else // if ( LevelF > iLev2 ) + { + // it is possible that the path goes through the node labeled by Var1 + // and still everything is okay; we do not label with Hash_Not here + // because the path does not go through node labeled by Var2 + bRes = b1; + } + + cuddCacheInsert2(dd, extraBddCheckVarsSymmetric, bF, bVars, bRes); + return bRes; + } +} /* end of extraBddCheckVarsSymmetric */ + +/**Function******************************************************************** + + Synopsis [Performs the reordering-sensitive step of Extra_zddTupleFromBdd().] + + Description [Generates in a bottom-up fashion ZDD for all combinations + composed of k variables out of variables belonging to Support.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode* extraZddTuplesFromBdd( + DdManager * dd, /* the DD manager */ + DdNode * bVarsK, /* the number of variables in tuples */ + DdNode * bVarsN) /* the set of all variables */ +{ + DdNode *zRes, *zRes0, *zRes1; + statLine(dd); + + /* terminal cases */ +/* if ( k < 0 || k > n ) + * return dd->zero; + * if ( n == 0 ) + * return dd->one; + */ + if ( cuddI( dd, bVarsK->index ) < cuddI( dd, bVarsN->index ) ) + return z0; + if ( bVarsN == b1 ) + return z1; + + /* check cache */ + zRes = cuddCacheLookup2Zdd(dd, extraZddTuplesFromBdd, bVarsK, bVarsN); + if (zRes) + return(zRes); + + /* ZDD in which this variable is 0 */ +/* zRes0 = extraZddTuplesFromBdd( dd, k, n-1 ); */ + zRes0 = extraZddTuplesFromBdd( dd, bVarsK, cuddT(bVarsN) ); + if ( zRes0 == NULL ) + return NULL; + cuddRef( zRes0 ); + + /* ZDD in which this variable is 1 */ +/* zRes1 = extraZddTuplesFromBdd( dd, k-1, n-1 ); */ + if ( bVarsK == b1 ) + { + zRes1 = z0; + cuddRef( zRes1 ); + } + else + { + zRes1 = extraZddTuplesFromBdd( dd, cuddT(bVarsK), cuddT(bVarsN) ); + if ( zRes1 == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zRes0 ); + return NULL; + } + cuddRef( zRes1 ); + } + + /* compose Res0 and Res1 with the given ZDD variable */ + zRes = cuddZddGetNode( dd, 2*bVarsN->index, zRes1, zRes0 ); + if ( zRes == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zRes0 ); + Cudd_RecursiveDerefZdd( dd, zRes1 ); + return NULL; + } + cuddDeref( zRes0 ); + cuddDeref( zRes1 ); + + /* insert the result into cache */ + cuddCacheInsert2(dd, extraZddTuplesFromBdd, bVarsK, bVarsN, zRes); + return zRes; + +} /* end of extraZddTuplesFromBdd */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Extra_zddSelectOneSubset.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * extraZddSelectOneSubset( + DdManager * dd, + DdNode * zS ) +// selects one subset from the ZDD zS +// returns z0 if and only if zS is an empty set of cubes +{ + DdNode * zRes; + + if ( zS == z0 ) return z0; + if ( zS == z1 ) return z1; + + // check cache + if ( zRes = cuddCacheLookup1Zdd( dd, extraZddSelectOneSubset, zS ) ) + return zRes; + else + { + DdNode * zS0, * zS1, * zTemp; + + zS0 = cuddE(zS); + zS1 = cuddT(zS); + + if ( zS0 != z0 ) + { + zRes = extraZddSelectOneSubset( dd, zS0 ); + if ( zRes == NULL ) + return NULL; + } + else // if ( zS0 == z0 ) + { + assert( zS1 != z0 ); + zRes = extraZddSelectOneSubset( dd, zS1 ); + if ( zRes == NULL ) + return NULL; + cuddRef( zRes ); + + zRes = cuddZddGetNode( dd, zS->index, zTemp = zRes, z0 ); + if ( zRes == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zTemp ); + return NULL; + } + cuddDeref( zTemp ); + } + + // insert the result into cache + cuddCacheInsert1( dd, extraZddSelectOneSubset, zS, zRes ); + return zRes; + } +} /* end of extraZddSelectOneSubset */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static Functions */ +/*---------------------------------------------------------------------------*/ diff --git a/abc_with_bb_support/src/misc/extra/extraBddUnate.c b/abc_with_bb_support/src/misc/extra/extraBddUnate.c new file mode 100644 index 000000000..ec9be2aa6 --- /dev/null +++ b/abc_with_bb_support/src/misc/extra/extraBddUnate.c @@ -0,0 +1,641 @@ +/**CFile**************************************************************** + + FileName [extraBddUnate.c] + + PackageName [extra] + + Synopsis [Efficient methods to compute the information about + unate variables using an algorithm that is conceptually similar to + the algorithm for two-variable symmetry computation presented in: + A. Mishchenko. Fast Computation of Symmetries in Boolean Functions. + Transactions on CAD, Nov. 2003.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - September 1, 2003.] + + Revision [$Id: extraBddUnate.c,v 1.0 2003/09/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "extra.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticEnd***************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Computes the classical symmetry information for the function.] + + Description [Returns the symmetry information in the form of Extra_UnateInfo_t structure.] + + SideEffects [If the ZDD variables are not derived from BDD variables with + multiplicity 2, this function may derive them in a wrong way.] + + SeeAlso [] + +******************************************************************************/ +Extra_UnateInfo_t * Extra_UnateComputeFast( + DdManager * dd, /* the manager */ + DdNode * bFunc) /* the function whose symmetries are computed */ +{ + DdNode * bSupp; + DdNode * zRes; + Extra_UnateInfo_t * p; + + bSupp = Cudd_Support( dd, bFunc ); Cudd_Ref( bSupp ); + zRes = Extra_zddUnateInfoCompute( dd, bFunc, bSupp ); Cudd_Ref( zRes ); + + p = Extra_UnateInfoCreateFromZdd( dd, zRes, bSupp ); + + Cudd_RecursiveDeref( dd, bSupp ); + Cudd_RecursiveDerefZdd( dd, zRes ); + + return p; + +} /* end of Extra_UnateInfoCompute */ + + +/**Function******************************************************************** + + Synopsis [Computes the classical symmetry information as a ZDD.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * Extra_zddUnateInfoCompute( + DdManager * dd, /* the DD manager */ + DdNode * bF, + DdNode * bVars) +{ + DdNode * res; + do { + dd->reordered = 0; + res = extraZddUnateInfoCompute( dd, bF, bVars ); + } while (dd->reordered == 1); + return(res); + +} /* end of Extra_zddUnateInfoCompute */ + + +/**Function******************************************************************** + + Synopsis [Converts a set of variables into a set of singleton subsets.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * Extra_zddGetSingletonsBoth( + DdManager * dd, /* the DD manager */ + DdNode * bVars) /* the set of variables */ +{ + DdNode * res; + do { + dd->reordered = 0; + res = extraZddGetSingletonsBoth( dd, bVars ); + } while (dd->reordered == 1); + return(res); + +} /* end of Extra_zddGetSingletonsBoth */ + +/**Function******************************************************************** + + Synopsis [Allocates unateness information structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +Extra_UnateInfo_t * Extra_UnateInfoAllocate( int nVars ) +{ + Extra_UnateInfo_t * p; + // allocate and clean the storage for unateness info + p = ALLOC( Extra_UnateInfo_t, 1 ); + memset( p, 0, sizeof(Extra_UnateInfo_t) ); + p->nVars = nVars; + p->pVars = ALLOC( Extra_UnateVar_t, nVars ); + memset( p->pVars, 0, nVars * sizeof(Extra_UnateVar_t) ); + return p; +} /* end of Extra_UnateInfoAllocate */ + +/**Function******************************************************************** + + Synopsis [Deallocates symmetry information structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void Extra_UnateInfoDissolve( Extra_UnateInfo_t * p ) +{ + free( p->pVars ); + free( p ); +} /* end of Extra_UnateInfoDissolve */ + +/**Function******************************************************************** + + Synopsis [Allocates symmetry information structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void Extra_UnateInfoPrint( Extra_UnateInfo_t * p ) +{ + char * pBuffer; + int i; + pBuffer = ALLOC( char, p->nVarsMax+1 ); + memset( pBuffer, ' ', p->nVarsMax ); + pBuffer[p->nVarsMax] = 0; + for ( i = 0; i < p->nVars; i++ ) + if ( p->pVars[i].Neg ) + pBuffer[ p->pVars[i].iVar ] = 'n'; + else if ( p->pVars[i].Pos ) + pBuffer[ p->pVars[i].iVar ] = 'p'; + else + pBuffer[ p->pVars[i].iVar ] = '.'; + printf( "%s\n", pBuffer ); + free( pBuffer ); +} /* end of Extra_UnateInfoPrint */ + + +/**Function******************************************************************** + + Synopsis [Creates the symmetry information structure from ZDD.] + + Description [ZDD representation of symmetries is the set of cubes, each + of which has two variables in the positive polarity. These variables correspond + to the symmetric variable pair.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +Extra_UnateInfo_t * Extra_UnateInfoCreateFromZdd( DdManager * dd, DdNode * zPairs, DdNode * bSupp ) +{ + Extra_UnateInfo_t * p; + DdNode * bTemp, * zSet, * zCube, * zTemp; + int * pMapVars2Nums; + int i, nSuppSize; + + nSuppSize = Extra_bddSuppSize( dd, bSupp ); + + // allocate and clean the storage for symmetry info + p = Extra_UnateInfoAllocate( nSuppSize ); + + // allocate the storage for the temporary map + pMapVars2Nums = ALLOC( int, dd->size ); + memset( pMapVars2Nums, 0, dd->size * sizeof(int) ); + + // assign the variables + p->nVarsMax = dd->size; + for ( i = 0, bTemp = bSupp; bTemp != b1; bTemp = cuddT(bTemp), i++ ) + { + p->pVars[i].iVar = bTemp->index; + pMapVars2Nums[bTemp->index] = i; + } + + // write the symmetry info into the structure + zSet = zPairs; Cudd_Ref( zSet ); +// Cudd_zddPrintCover( dd, zPairs ); printf( "\n" ); + while ( zSet != z0 ) + { + // get the next cube + zCube = Extra_zddSelectOneSubset( dd, zSet ); Cudd_Ref( zCube ); + + // add this var to the data structure + assert( cuddT(zCube) == z1 && cuddE(zCube) == z0 ); + if ( zCube->index & 1 ) // neg + p->pVars[ pMapVars2Nums[zCube->index/2] ].Neg = 1; + else + p->pVars[ pMapVars2Nums[zCube->index/2] ].Pos = 1; + // count the unate vars + p->nUnate++; + + // update the cuver and deref the cube + zSet = Cudd_zddDiff( dd, zTemp = zSet, zCube ); Cudd_Ref( zSet ); + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zCube ); + + } // for each cube + Cudd_RecursiveDerefZdd( dd, zSet ); + FREE( pMapVars2Nums ); + return p; + +} /* end of Extra_UnateInfoCreateFromZdd */ + + + +/**Function******************************************************************** + + Synopsis [Computes the classical unateness information for the function.] + + Description [Uses the naive way of comparing cofactors.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +Extra_UnateInfo_t * Extra_UnateComputeSlow( DdManager * dd, DdNode * bFunc ) +{ + int nSuppSize; + DdNode * bSupp, * bTemp; + Extra_UnateInfo_t * p; + int i, Res; + + // compute the support + bSupp = Cudd_Support( dd, bFunc ); Cudd_Ref( bSupp ); + nSuppSize = Extra_bddSuppSize( dd, bSupp ); +//printf( "Support = %d. ", nSuppSize ); +//Extra_bddPrint( dd, bSupp ); +//printf( "%d ", nSuppSize ); + + // allocate the storage for symmetry info + p = Extra_UnateInfoAllocate( nSuppSize ); + + // assign the variables + p->nVarsMax = dd->size; + for ( i = 0, bTemp = bSupp; bTemp != b1; bTemp = cuddT(bTemp), i++ ) + { + Res = Extra_bddCheckUnateNaive( dd, bFunc, bTemp->index ); + p->pVars[i].iVar = bTemp->index; + if ( Res == -1 ) + p->pVars[i].Neg = 1; + else if ( Res == 1 ) + p->pVars[i].Pos = 1; + p->nUnate += (Res != 0); + } + Cudd_RecursiveDeref( dd, bSupp ); + return p; + +} /* end of Extra_UnateComputeSlow */ + +/**Function******************************************************************** + + Synopsis [Checks if the two variables are symmetric.] + + Description [Returns 0 if vars are not unate. Return -1/+1 if the var is neg/pos unate.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int Extra_bddCheckUnateNaive( + DdManager * dd, /* the DD manager */ + DdNode * bF, + int iVar) +{ + DdNode * bCof0, * bCof1; + int Res; + + assert( iVar < dd->size ); + + bCof0 = Cudd_Cofactor( dd, bF, Cudd_Not(Cudd_bddIthVar(dd,iVar)) ); Cudd_Ref( bCof0 ); + bCof1 = Cudd_Cofactor( dd, bF, Cudd_bddIthVar(dd,iVar) ); Cudd_Ref( bCof1 ); + + if ( Cudd_bddLeq( dd, bCof0, bCof1 ) ) + Res = 1; + else if ( Cudd_bddLeq( dd, bCof1, bCof0 ) ) + Res =-1; + else + Res = 0; + + Cudd_RecursiveDeref( dd, bCof0 ); + Cudd_RecursiveDeref( dd, bCof1 ); + return Res; +} /* end of Extra_bddCheckUnateNaive */ + + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Performs a recursive step of Extra_UnateInfoCompute.] + + Description [Returns the set of symmetric variable pairs represented as a set + of two-literal ZDD cubes. Both variables always appear in the positive polarity + in the cubes. This function works without building new BDD nodes. Some relatively + small number of ZDD nodes may be built to ensure proper bookkeeping of the + symmetry information.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * +extraZddUnateInfoCompute( + DdManager * dd, /* the manager */ + DdNode * bFunc, /* the function whose symmetries are computed */ + DdNode * bVars ) /* the set of variables on which this function depends */ +{ + DdNode * zRes; + DdNode * bFR = Cudd_Regular(bFunc); + + if ( cuddIsConstant(bFR) ) + { + if ( cuddIsConstant(bVars) ) + return z0; + return extraZddGetSingletonsBoth( dd, bVars ); + } + assert( bVars != b1 ); + + if ( zRes = cuddCacheLookup2Zdd(dd, extraZddUnateInfoCompute, bFunc, bVars) ) + return zRes; + else + { + DdNode * zRes0, * zRes1; + DdNode * zTemp, * zPlus; + DdNode * bF0, * bF1; + DdNode * bVarsNew; + int nVarsExtra; + int LevelF; + int AddVar; + + // every variable in bF should be also in bVars, therefore LevelF cannot be above LevelV + // if LevelF is below LevelV, scroll through the vars in bVars to the same level as F + // count how many extra vars are there in bVars + nVarsExtra = 0; + LevelF = dd->perm[bFR->index]; + for ( bVarsNew = bVars; LevelF > dd->perm[bVarsNew->index]; bVarsNew = cuddT(bVarsNew) ) + nVarsExtra++; + // the indexes (level) of variables should be synchronized now + assert( bFR->index == bVarsNew->index ); + + // cofactor the function + if ( bFR != bFunc ) // bFunc is complemented + { + bF0 = Cudd_Not( cuddE(bFR) ); + bF1 = Cudd_Not( cuddT(bFR) ); + } + else + { + bF0 = cuddE(bFR); + bF1 = cuddT(bFR); + } + + // solve subproblems + zRes0 = extraZddUnateInfoCompute( dd, bF0, cuddT(bVarsNew) ); + if ( zRes0 == NULL ) + return NULL; + cuddRef( zRes0 ); + + // if there is no symmetries in the negative cofactor + // there is no need to test the positive cofactor + if ( zRes0 == z0 ) + zRes = zRes0; // zRes takes reference + else + { + zRes1 = extraZddUnateInfoCompute( dd, bF1, cuddT(bVarsNew) ); + if ( zRes1 == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zRes0 ); + return NULL; + } + cuddRef( zRes1 ); + + // only those variables are pair-wise symmetric + // that are pair-wise symmetric in both cofactors + // therefore, intersect the solutions + zRes = cuddZddIntersect( dd, zRes0, zRes1 ); + if ( zRes == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zRes0 ); + Cudd_RecursiveDerefZdd( dd, zRes1 ); + return NULL; + } + cuddRef( zRes ); + Cudd_RecursiveDerefZdd( dd, zRes0 ); + Cudd_RecursiveDerefZdd( dd, zRes1 ); + } + + // consider the current top-most variable + AddVar = -1; + if ( Cudd_bddLeq( dd, bF0, bF1 ) ) // pos + AddVar = 0; + else if ( Cudd_bddLeq( dd, bF1, bF0 ) ) // neg + AddVar = 1; + if ( AddVar >= 0 ) + { + // create the singleton + zPlus = cuddZddGetNode( dd, 2*bFR->index + AddVar, z1, z0 ); + if ( zPlus == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zRes ); + return NULL; + } + cuddRef( zPlus ); + + // add these to the result + zRes = cuddZddUnion( dd, zTemp = zRes, zPlus ); + if ( zRes == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zPlus ); + return NULL; + } + cuddRef( zRes ); + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zPlus ); + } + // only zRes is referenced at this point + + // if we skipped some variables, these variables cannot be symmetric with + // any variables that are currently in the support of bF, but they can be + // symmetric with the variables that are in bVars but not in the support of bF + for ( bVarsNew = bVars; LevelF > dd->perm[bVarsNew->index]; bVarsNew = cuddT(bVarsNew) ) + { + // create the negative singleton + zPlus = cuddZddGetNode( dd, 2*bVarsNew->index+1, z1, z0 ); + if ( zPlus == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zRes ); + return NULL; + } + cuddRef( zPlus ); + + // add these to the result + zRes = cuddZddUnion( dd, zTemp = zRes, zPlus ); + if ( zRes == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zPlus ); + return NULL; + } + cuddRef( zRes ); + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zPlus ); + + + // create the positive singleton + zPlus = cuddZddGetNode( dd, 2*bVarsNew->index, z1, z0 ); + if ( zPlus == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zRes ); + return NULL; + } + cuddRef( zPlus ); + + // add these to the result + zRes = cuddZddUnion( dd, zTemp = zRes, zPlus ); + if ( zRes == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zPlus ); + return NULL; + } + cuddRef( zRes ); + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zPlus ); + } + cuddDeref( zRes ); + + /* insert the result into cache */ + cuddCacheInsert2(dd, extraZddUnateInfoCompute, bFunc, bVars, zRes); + return zRes; + } +} /* end of extraZddUnateInfoCompute */ + + +/**Function******************************************************************** + + Synopsis [Performs a recursive step of Extra_zddGetSingletons.] + + Description [Returns the set of ZDD singletons, containing those pos/neg + polarity ZDD variables that correspond to the BDD variables in bVars.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +DdNode * extraZddGetSingletonsBoth( + DdManager * dd, /* the DD manager */ + DdNode * bVars) /* the set of variables */ +{ + DdNode * zRes; + + if ( bVars == b1 ) + return z1; + + if ( zRes = cuddCacheLookup1Zdd(dd, extraZddGetSingletonsBoth, bVars) ) + return zRes; + else + { + DdNode * zTemp, * zPlus; + + // solve subproblem + zRes = extraZddGetSingletonsBoth( dd, cuddT(bVars) ); + if ( zRes == NULL ) + return NULL; + cuddRef( zRes ); + + + // create the negative singleton + zPlus = cuddZddGetNode( dd, 2*bVars->index+1, z1, z0 ); + if ( zPlus == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zRes ); + return NULL; + } + cuddRef( zPlus ); + + // add these to the result + zRes = cuddZddUnion( dd, zTemp = zRes, zPlus ); + if ( zRes == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zPlus ); + return NULL; + } + cuddRef( zRes ); + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zPlus ); + + + // create the positive singleton + zPlus = cuddZddGetNode( dd, 2*bVars->index, z1, z0 ); + if ( zPlus == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zRes ); + return NULL; + } + cuddRef( zPlus ); + + // add these to the result + zRes = cuddZddUnion( dd, zTemp = zRes, zPlus ); + if ( zRes == NULL ) + { + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zPlus ); + return NULL; + } + cuddRef( zRes ); + Cudd_RecursiveDerefZdd( dd, zTemp ); + Cudd_RecursiveDerefZdd( dd, zPlus ); + + cuddDeref( zRes ); + cuddCacheInsert1( dd, extraZddGetSingletonsBoth, bVars, zRes ); + return zRes; + } +} /* end of extraZddGetSingletonsBoth */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static Functions */ +/*---------------------------------------------------------------------------*/ diff --git a/abc_with_bb_support/src/misc/extra/extraUtilBitMatrix.c b/abc_with_bb_support/src/misc/extra/extraUtilBitMatrix.c new file mode 100644 index 000000000..34cd11722 --- /dev/null +++ b/abc_with_bb_support/src/misc/extra/extraUtilBitMatrix.c @@ -0,0 +1,415 @@ +/**CFile**************************************************************** + + FileName [extraUtilBitMatrix.c] + + PackageName [extra] + + Synopsis [Various reusable software utilities.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 1, 2003.] + + Revision [$Id: extraUtilBitMatrix.c,v 1.0 2003/09/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "extra.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +struct Extra_BitMat_t_ +{ + unsigned ** ppData; // bit data + int nSize; // the number of bits in one dimension + int nWords; // the number of words in one dimension + int nBitShift; // the number of bits to shift to get words + unsigned uMask; // the mask to get the number of bits in the word + int nLookups; // the number of lookups + int nInserts; // the number of inserts + int nDeletes; // the number of deletions +}; + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function************************************************************* + + Synopsis [Starts the bit matrix.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Extra_BitMat_t * Extra_BitMatrixStart( int nSize ) +{ + Extra_BitMat_t * p; + int i; + p = ALLOC( Extra_BitMat_t, 1 ); + memset( p, 0, sizeof(Extra_BitMat_t) ); + p->nSize = nSize; + p->nBitShift = (sizeof(unsigned) == 4) ? 5: 6; + p->uMask = (sizeof(unsigned) == 4) ? 31: 63; + p->nWords = nSize / (8 * sizeof(unsigned)) + ((nSize % (8 * sizeof(unsigned))) > 0); + p->ppData = ALLOC( unsigned *, nSize ); + p->ppData[0] = ALLOC( unsigned, nSize * p->nWords ); + memset( p->ppData[0], 0, sizeof(unsigned) * nSize * p->nWords ); + for ( i = 1; i < nSize; i++ ) + p->ppData[i] = p->ppData[i-1] + p->nWords; + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the bit matrix.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_BitMatrixClean( Extra_BitMat_t * p ) +{ + memset( p->ppData[0], 0, sizeof(unsigned) * p->nSize * p->nWords ); +} + +/**Function************************************************************* + + Synopsis [Stops the bit matrix.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_BitMatrixStop( Extra_BitMat_t * p ) +{ + FREE( p->ppData[0] ); + FREE( p->ppData ); + FREE( p ); +} + +/**Function************************************************************* + + Synopsis [Prints the bit-matrix.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_BitMatrixPrint( Extra_BitMat_t * pMat ) +{ + int i, k, nVars; + printf( "\n" ); + nVars = Extra_BitMatrixReadSize( pMat ); + for ( i = 0; i < nVars; i++ ) + { + for ( k = 0; k <= i; k++ ) + printf( " " ); + for ( k = i+1; k < nVars; k++ ) + if ( Extra_BitMatrixLookup1( pMat, i, k ) ) + printf( "1" ); + else + printf( "." ); + printf( "\n" ); + } +} + + +/**Function************************************************************* + + Synopsis [Reads the matrix size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_BitMatrixReadSize( Extra_BitMat_t * p ) +{ + return p->nSize; +} + +/**Function************************************************************* + + Synopsis [Inserts the element into the upper part.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_BitMatrixInsert1( Extra_BitMat_t * p, int i, int k ) +{ + p->nInserts++; + if ( i < k ) + p->ppData[i][k>>p->nBitShift] |= (1<<(k & p->uMask)); + else + p->ppData[k][i>>p->nBitShift] |= (1<<(i & p->uMask)); +} + +/**Function************************************************************* + + Synopsis [Inserts the element into the upper part.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_BitMatrixLookup1( Extra_BitMat_t * p, int i, int k ) +{ + p->nLookups++; + if ( i < k ) + return ((p->ppData[i][k>>p->nBitShift] & (1<<(k & p->uMask))) > 0); + else + return ((p->ppData[k][i>>p->nBitShift] & (1<<(i & p->uMask))) > 0); +} + +/**Function************************************************************* + + Synopsis [Inserts the element into the upper part.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_BitMatrixDelete1( Extra_BitMat_t * p, int i, int k ) +{ + p->nDeletes++; + if ( i < k ) + p->ppData[i][k>>p->nBitShift] &= ~(1<<(k & p->uMask)); + else + p->ppData[k][i>>p->nBitShift] &= ~(1<<(i & p->uMask)); +} + + + +/**Function************************************************************* + + Synopsis [Inserts the element into the upper part.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_BitMatrixInsert2( Extra_BitMat_t * p, int i, int k ) +{ + p->nInserts++; + if ( i > k ) + p->ppData[i][k>>p->nBitShift] |= (1<<(k & p->uMask)); + else + p->ppData[k][i>>p->nBitShift] |= (1<<(i & p->uMask)); +} + +/**Function************************************************************* + + Synopsis [Inserts the element into the upper part.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_BitMatrixLookup2( Extra_BitMat_t * p, int i, int k ) +{ + p->nLookups++; + if ( i > k ) + return ((p->ppData[i][k>>p->nBitShift] & (1<<(k & p->uMask))) > 0); + else + return ((p->ppData[k][i>>p->nBitShift] & (1<<(i & p->uMask))) > 0); +} + +/**Function************************************************************* + + Synopsis [Inserts the element into the upper part.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_BitMatrixDelete2( Extra_BitMat_t * p, int i, int k ) +{ + p->nDeletes++; + if ( i > k ) + p->ppData[i][k>>p->nBitShift] &= ~(1<<(k & p->uMask)); + else + p->ppData[k][i>>p->nBitShift] &= ~(1<<(i & p->uMask)); +} + + +/**Function************************************************************* + + Synopsis [Inserts the element into the upper part.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_BitMatrixOr( Extra_BitMat_t * p, int i, unsigned * pInfo ) +{ + int w; + for ( w = 0; w < p->nWords; w++ ) + p->ppData[i][w] |= pInfo[w]; +} + +/**Function************************************************************* + + Synopsis [Inserts the element into the upper part.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_BitMatrixOrTwo( Extra_BitMat_t * p, int i, int j ) +{ + int w; + for ( w = 0; w < p->nWords; w++ ) + p->ppData[i][w] = p->ppData[j][w] = (p->ppData[i][w] | p->ppData[j][w]); +} + +/**Function************************************************************* + + Synopsis [Counts the number of 1's in the upper rectangle.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_BitMatrixCountOnesUpper( Extra_BitMat_t * p ) +{ + int i, k, nTotal = 0; + for ( i = 0; i < p->nSize; i++ ) + for ( k = i + 1; k < p->nSize; k++ ) + nTotal += ( (p->ppData[i][k>>5] & (1 << (k&31))) > 0 ); + return nTotal; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the matrices have no entries in common.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_BitMatrixIsDisjoint( Extra_BitMat_t * p1, Extra_BitMat_t * p2 ) +{ + int i, w; + assert( p1->nSize == p2->nSize ); + for ( i = 0; i < p1->nSize; i++ ) + for ( w = 0; w < p1->nWords; w++ ) + if ( p1->ppData[i][w] & p2->ppData[i][w] ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the matrix is a set of cliques.] + + Description [For example pairwise symmetry info should satisfy this property.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_BitMatrixIsClique( Extra_BitMat_t * pMat ) +{ + int v, u, i; + for ( v = 0; v < pMat->nSize; v++ ) + for ( u = v+1; u < pMat->nSize; u++ ) + { + if ( !Extra_BitMatrixLookup1( pMat, v, u ) ) + continue; + // v and u are symmetric + for ( i = 0; i < pMat->nSize; i++ ) + { + if ( i == v || i == u ) + continue; + // i is neither v nor u + // the symmetry status of i is the same w.r.t. to v and u + if ( Extra_BitMatrixLookup1( pMat, i, v ) != Extra_BitMatrixLookup1( pMat, i, u ) ) + return 0; + } + } + return 1; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/extra/extraUtilCanon.c b/abc_with_bb_support/src/misc/extra/extraUtilCanon.c new file mode 100644 index 000000000..3ec2e2c8b --- /dev/null +++ b/abc_with_bb_support/src/misc/extra/extraUtilCanon.c @@ -0,0 +1,701 @@ +/**CFile**************************************************************** + + FileName [extraUtilMisc.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [extra] + + Synopsis [Computing canonical forms of Boolean functions using truth tables.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: extraUtilMisc.c,v 1.0 2003/09/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "extra.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +static unsigned s_Truths3[256]; +static char s_Phases3[256][9]; + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int Extra_TruthCanonN_rec( int nVars, unsigned char * pt, unsigned ** pptRes, char ** ppfRes, int Flag ); + +/**AutomaticEnd***************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Computes the N-canonical form of the Boolean function up to 6 inputs.] + + Description [The N-canonical form is defined as the truth table with + the minimum integer value. This function exhaustively enumerates + through the complete set of 2^N phase assignments. + Returns pointers to the static storage to the truth table and phases. + This data should be used before the function is called again.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int Extra_TruthCanonFastN( int nVarsMax, int nVarsReal, unsigned * pt, unsigned ** pptRes, char ** ppfRes ) +{ + static unsigned uTruthStore6[2]; + int RetValue; + assert( nVarsMax <= 6 ); + assert( nVarsReal <= nVarsMax ); + RetValue = Extra_TruthCanonN_rec( nVarsReal <= 3? 3: nVarsReal, (unsigned char *)pt, pptRes, ppfRes, 0 ); + if ( nVarsMax == 6 && nVarsReal < nVarsMax ) + { + uTruthStore6[0] = **pptRes; + uTruthStore6[1] = **pptRes; + *pptRes = uTruthStore6; + } + return RetValue; +} + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + +/**Function************************************************************* + + Synopsis [Recursive implementation of the above.] + + Description [] + + SideEffects [This procedure has a bug, which shows on Solaris. + Most likely has something to do with the casts, i.g *((unsigned *)pt0)] + + SeeAlso [] + +***********************************************************************/ +int Extra_TruthCanonN_rec( int nVars, unsigned char * pt, unsigned ** pptRes, char ** ppfRes, int Flag ) +{ + static unsigned uTruthStore[7][2][2]; + static char uPhaseStore[7][2][64]; + + unsigned char * pt0, * pt1; + unsigned * ptRes0, * ptRes1, * ptRes; + unsigned uInit0, uInit1, uTruth0, uTruth1, uTemp; + char * pfRes0, * pfRes1, * pfRes; + int nf0, nf1, nfRes, i, nVarsN; + + // table lookup for three vars + if ( nVars == 3 ) + { + *pptRes = &s_Truths3[*pt]; + *ppfRes = s_Phases3[*pt]+1; + return s_Phases3[*pt][0]; + } + + // number of vars for the next call + nVarsN = nVars-1; + // truth table for the next call + pt0 = pt; + pt1 = pt + (1 << nVarsN) / 8; + // 5-var truth tables for this call +// uInit0 = *((unsigned *)pt0); +// uInit1 = *((unsigned *)pt1); + if ( nVarsN == 3 ) + { + uInit0 = (pt0[0] << 24) | (pt0[0] << 16) | (pt0[0] << 8) | pt0[0]; + uInit1 = (pt1[0] << 24) | (pt1[0] << 16) | (pt1[0] << 8) | pt1[0]; + } + else if ( nVarsN == 4 ) + { + uInit0 = (pt0[1] << 24) | (pt0[0] << 16) | (pt0[1] << 8) | pt0[0]; + uInit1 = (pt1[1] << 24) | (pt1[0] << 16) | (pt1[1] << 8) | pt1[0]; + } + else + { + uInit0 = (pt0[3] << 24) | (pt0[2] << 16) | (pt0[1] << 8) | pt0[0]; + uInit1 = (pt1[3] << 24) | (pt1[2] << 16) | (pt1[1] << 8) | pt1[0]; + } + + // storage for truth tables and phases + ptRes = uTruthStore[nVars][Flag]; + pfRes = uPhaseStore[nVars][Flag]; + + // solve trivial cases + if ( uInit1 == 0 ) + { + nf0 = Extra_TruthCanonN_rec( nVarsN, pt0, &ptRes0, &pfRes0, 0 ); + uTruth1 = uInit1; + uTruth0 = *ptRes0; + nfRes = 0; + for ( i = 0; i < nf0; i++ ) + pfRes[nfRes++] = pfRes0[i]; + goto finish; + } + if ( uInit0 == 0 ) + { + nf1 = Extra_TruthCanonN_rec( nVarsN, pt1, &ptRes1, &pfRes1, 1 ); + uTruth1 = uInit0; + uTruth0 = *ptRes1; + nfRes = 0; + for ( i = 0; i < nf1; i++ ) + pfRes[nfRes++] = pfRes1[i] | (1< uTemp ) + { + nfRes = 0; + uTruth0 = uTemp; + pfRes[nfRes++] = pfRes1[i]; + } + else if ( uTruth0 == uTemp ) + pfRes[nfRes++] = pfRes1[i]; + } + uTruth1 = *ptRes1; + } + else if ( *ptRes1 > *ptRes0 ) + { + uTruth0 = 0xFFFFFFFF; + nfRes = 0; + for ( i = 0; i < nf0; i++ ) + { + uTemp = Extra_TruthPolarize( uInit1, pfRes0[i], nVarsN ); + if ( uTruth0 > uTemp ) + { + nfRes = 0; + uTruth0 = uTemp; + pfRes[nfRes++] = pfRes0[i] | (1<= FileName; pDot-- ) + if ( *pDot == '.' ) + return pDot + 1; + return NULL; +} + +/**Function************************************************************* + + Synopsis [Returns the composite name of the file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Extra_FileNameAppend( char * pBase, char * pSuffix ) +{ + static char Buffer[500]; + sprintf( Buffer, "%s%s", pBase, pSuffix ); + return Buffer; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Extra_FileNameGeneric( char * FileName ) +{ + char * pDot; + char * pUnd; + char * pRes; + + // find the generic name of the file + pRes = Extra_UtilStrsav( FileName ); + // find the pointer to the "." symbol in the file name +// pUnd = strstr( FileName, "_" ); + pUnd = NULL; + pDot = strstr( FileName, "." ); + if ( pUnd ) + pRes[pUnd - FileName] = 0; + else if ( pDot ) + pRes[pDot - FileName] = 0; + return pRes; +} + +/**Function************************************************************* + + Synopsis [Returns the file size.] + + Description [The file should be closed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_FileSize( char * pFileName ) +{ + FILE * pFile; + int nFileSize; + pFile = fopen( pFileName, "r" ); + if ( pFile == NULL ) + { + printf( "Extra_FileSize(): The file is unavailable (absent or open).\n" ); + return 0; + } + fseek( pFile, 0, SEEK_END ); + nFileSize = ftell( pFile ); + fclose( pFile ); + return nFileSize; +} + + +/**Function************************************************************* + + Synopsis [Read the file into the internal buffer.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Extra_FileRead( FILE * pFile ) +{ + int nFileSize; + char * pBuffer; + // get the file size, in bytes + fseek( pFile, 0, SEEK_END ); + nFileSize = ftell( pFile ); + // move the file current reading position to the beginning + rewind( pFile ); + // load the contents of the file into memory + pBuffer = ALLOC( char, nFileSize + 3 ); + fread( pBuffer, nFileSize, 1, pFile ); + // terminate the string with '\0' + pBuffer[ nFileSize + 0] = '\n'; + pBuffer[ nFileSize + 1] = '\0'; + return pBuffer; +} + +/**Function************************************************************* + + Synopsis [Returns the time stamp.] + + Description [The file should be closed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Extra_TimeStamp() +{ + static char Buffer[100]; + char * TimeStamp; + time_t ltime; + // get the current time + time( <ime ); + TimeStamp = asctime( localtime( <ime ) ); + TimeStamp[ strlen(TimeStamp) - 1 ] = 0; + strcpy( Buffer, TimeStamp ); + return Buffer; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Extra_ReadBinary( char * Buffer ) +{ + unsigned Result; + int i; + + Result = 0; + for ( i = 0; Buffer[i]; i++ ) + if ( Buffer[i] == '0' || Buffer[i] == '1' ) + Result = Result * 2 + Buffer[i] - '0'; + else + { + assert( 0 ); + } + return Result; +} + +/**Function************************************************************* + + Synopsis [Prints the bit string.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_PrintBinary( FILE * pFile, unsigned Sign[], int nBits ) +{ + int Remainder, nWords; + int w, i; + + Remainder = (nBits%(sizeof(unsigned)*8)); + nWords = (nBits/(sizeof(unsigned)*8)) + (Remainder>0); + + for ( w = nWords-1; w >= 0; w-- ) + for ( i = ((w == nWords-1 && Remainder)? Remainder-1: 31); i >= 0; i-- ) + fprintf( pFile, "%c", '0' + (int)((Sign[w] & (1< 0) ); + +// fprintf( pFile, "\n" ); +} + +/**Function************************************************************* + + Synopsis [Reads the hex unsigned into the bit-string.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_ReadHexadecimal( unsigned Sign[], char * pString, int nVars ) +{ + int nWords, nDigits, Digit, k, c; + nWords = Extra_TruthWordNum( nVars ); + for ( k = 0; k < nWords; k++ ) + Sign[k] = 0; + // read the number from the string + nDigits = (1 << nVars) / 4; + if ( nDigits == 0 ) + nDigits = 1; + for ( k = 0; k < nDigits; k++ ) + { + c = nDigits-1-k; + if ( pString[c] >= '0' && pString[c] <= '9' ) + Digit = pString[c] - '0'; + else if ( pString[c] >= 'A' && pString[c] <= 'F' ) + Digit = pString[c] - 'A' + 10; + else if ( pString[c] >= 'a' && pString[c] <= 'f' ) + Digit = pString[c] - 'a' + 10; + else { assert( 0 ); return 0; } + Sign[k/8] |= ( (Digit & 15) << ((k%8) * 4) ); + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Prints the hex unsigned into a file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_PrintHexadecimal( FILE * pFile, unsigned Sign[], int nVars ) +{ + int nDigits, Digit, k; + // write the number into the file + nDigits = (1 << nVars) / 4; + for ( k = nDigits - 1; k >= 0; k-- ) + { + Digit = ((Sign[k/8] >> ((k%8) * 4)) & 15); + if ( Digit < 10 ) + fprintf( pFile, "%d", Digit ); + else + fprintf( pFile, "%c", 'a' + Digit-10 ); + } +// fprintf( pFile, "\n" ); +} + +/**Function************************************************************* + + Synopsis [Prints the hex unsigned into a file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_PrintHexadecimalString( char * pString, unsigned Sign[], int nVars ) +{ + int nDigits, Digit, k; + // write the number into the file + nDigits = (1 << nVars) / 4; + for ( k = nDigits - 1; k >= 0; k-- ) + { + Digit = ((Sign[k/8] >> ((k%8) * 4)) & 15); + if ( Digit < 10 ) + *pString++ = '0' + Digit; + else + *pString++ = 'a' + Digit-10; + } +// fprintf( pFile, "\n" ); + *pString = 0; +} + +/**Function************************************************************* + + Synopsis [Prints the hex unsigned into a file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_PrintHex( FILE * pFile, unsigned uTruth, int nVars ) +{ + int nMints, nDigits, Digit, k; + + // write the number into the file + fprintf( pFile, "0x" ); + nMints = (1 << nVars); + nDigits = nMints / 4; + for ( k = nDigits - 1; k >= 0; k-- ) + { + Digit = ((uTruth >> (k * 4)) & 15); + if ( Digit < 10 ) + fprintf( pFile, "%d", Digit ); + else + fprintf( pFile, "%c", 'a' + Digit-10 ); + } +// fprintf( pFile, "\n" ); +} + +/**Function************************************************************* + + Synopsis [Returns the composite name of the file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_PrintSymbols( FILE * pFile, char Char, int nTimes, int fPrintNewLine ) +{ + int i; + for ( i = 0; i < nTimes; i++ ) + printf( "%c", Char ); + if ( fPrintNewLine ) + printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [Appends the string.] + + Description [Assumes that the given string (pStrGiven) has been allocated + before using malloc(). The additional string has not been allocated. + Allocs more root, appends the additional part, frees the old given string.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Extra_StringAppend( char * pStrGiven, char * pStrAdd ) +{ + char * pTemp; + if ( pStrGiven ) + { + pTemp = ALLOC( char, strlen(pStrGiven) + strlen(pStrAdd) + 2 ); + sprintf( pTemp, "%s%s", pStrGiven, pStrAdd ); + free( pStrGiven ); + } + else + pTemp = Extra_UtilStrsav( pStrAdd ); + return pTemp; +} + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of static Functions */ +/*---------------------------------------------------------------------------*/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/extra/extraUtilMemory.c b/abc_with_bb_support/src/misc/extra/extraUtilMemory.c new file mode 100644 index 000000000..284cd7150 --- /dev/null +++ b/abc_with_bb_support/src/misc/extra/extraUtilMemory.c @@ -0,0 +1,625 @@ +/**CFile**************************************************************** + + FileName [extraUtilMemory.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [extra] + + Synopsis [Memory managers.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: extraUtilMemory.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "extra.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +struct Extra_MmFixed_t_ +{ + // information about individual entries + int nEntrySize; // the size of one entry + int nEntriesAlloc; // the total number of entries allocated + int nEntriesUsed; // the number of entries in use + int nEntriesMax; // the max number of entries in use + char * pEntriesFree; // the linked list of free entries + + // this is where the memory is stored + int nChunkSize; // the size of one chunk + int nChunksAlloc; // the maximum number of memory chunks + int nChunks; // the current number of memory chunks + char ** pChunks; // the allocated memory + + // statistics + int nMemoryUsed; // memory used in the allocated entries + int nMemoryAlloc; // memory allocated +}; + +struct Extra_MmFlex_t_ +{ + // information about individual entries + int nEntriesUsed; // the number of entries allocated + char * pCurrent; // the current pointer to free memory + char * pEnd; // the first entry outside the free memory + + // this is where the memory is stored + int nChunkSize; // the size of one chunk + int nChunksAlloc; // the maximum number of memory chunks + int nChunks; // the current number of memory chunks + char ** pChunks; // the allocated memory + + // statistics + int nMemoryUsed; // memory used in the allocated entries + int nMemoryAlloc; // memory allocated +}; + + +struct Extra_MmStep_t_ +{ + int nMems; // the number of fixed memory managers employed + Extra_MmFixed_t ** pMems; // memory managers: 2^1 words, 2^2 words, etc + int nMapSize; // the size of the memory array + Extra_MmFixed_t ** pMap; // maps the number of bytes into its memory manager + int nLargeChunksAlloc; // the maximum number of large memory chunks + int nLargeChunks; // the current number of large memory chunks + void ** pLargeChunks; // the allocated large memory chunks +}; + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function************************************************************* + + Synopsis [Allocates memory pieces of fixed size.] + + Description [The size of the chunk is computed as the minimum of + 1024 entries and 64K. Can only work with entry size at least 4 byte long.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Extra_MmFixed_t * Extra_MmFixedStart( int nEntrySize ) +{ + Extra_MmFixed_t * p; + + p = ALLOC( Extra_MmFixed_t, 1 ); + memset( p, 0, sizeof(Extra_MmFixed_t) ); + + p->nEntrySize = nEntrySize; + p->nEntriesAlloc = 0; + p->nEntriesUsed = 0; + p->pEntriesFree = NULL; + + if ( nEntrySize * (1 << 10) < (1<<16) ) + p->nChunkSize = (1 << 10); + else + p->nChunkSize = (1<<16) / nEntrySize; + if ( p->nChunkSize < 8 ) + p->nChunkSize = 8; + + p->nChunksAlloc = 64; + p->nChunks = 0; + p->pChunks = ALLOC( char *, p->nChunksAlloc ); + + p->nMemoryUsed = 0; + p->nMemoryAlloc = 0; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_MmFixedPrint( Extra_MmFixed_t * p ) +{ + printf( "Fixed memory manager: Entry = %5d. Chunk = %5d. Chunks used = %5d.\n", + p->nEntrySize, p->nChunkSize, p->nChunks ); + printf( " Entries used = %8d. Entries peak = %8d. Memory used = %8d. Memory alloc = %8d.\n", + p->nEntriesUsed, p->nEntriesMax, p->nEntrySize * p->nEntriesUsed, p->nMemoryAlloc ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_MmFixedStop( Extra_MmFixed_t * p ) +{ + int i; + if ( p == NULL ) + return; + for ( i = 0; i < p->nChunks; i++ ) + free( p->pChunks[i] ); + free( p->pChunks ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Extra_MmFixedEntryFetch( Extra_MmFixed_t * p ) +{ + char * pTemp; + int i; + + // check if there are still free entries + if ( p->nEntriesUsed == p->nEntriesAlloc ) + { // need to allocate more entries + assert( p->pEntriesFree == NULL ); + if ( p->nChunks == p->nChunksAlloc ) + { + p->nChunksAlloc *= 2; + p->pChunks = REALLOC( char *, p->pChunks, p->nChunksAlloc ); + } + p->pEntriesFree = ALLOC( char, p->nEntrySize * p->nChunkSize ); + p->nMemoryAlloc += p->nEntrySize * p->nChunkSize; + // transform these entries into a linked list + pTemp = p->pEntriesFree; + for ( i = 1; i < p->nChunkSize; i++ ) + { + *((char **)pTemp) = pTemp + p->nEntrySize; + pTemp += p->nEntrySize; + } + // set the last link + *((char **)pTemp) = NULL; + // add the chunk to the chunk storage + p->pChunks[ p->nChunks++ ] = p->pEntriesFree; + // add to the number of entries allocated + p->nEntriesAlloc += p->nChunkSize; + } + // incrememt the counter of used entries + p->nEntriesUsed++; + if ( p->nEntriesMax < p->nEntriesUsed ) + p->nEntriesMax = p->nEntriesUsed; + // return the first entry in the free entry list + pTemp = p->pEntriesFree; + p->pEntriesFree = *((char **)pTemp); + return pTemp; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_MmFixedEntryRecycle( Extra_MmFixed_t * p, char * pEntry ) +{ + // decrement the counter of used entries + p->nEntriesUsed--; + // add the entry to the linked list of free entries + *((char **)pEntry) = p->pEntriesFree; + p->pEntriesFree = pEntry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [Relocates all the memory except the first chunk.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_MmFixedRestart( Extra_MmFixed_t * p ) +{ + int i; + char * pTemp; + + // deallocate all chunks except the first one + for ( i = 1; i < p->nChunks; i++ ) + free( p->pChunks[i] ); + p->nChunks = 1; + // transform these entries into a linked list + pTemp = p->pChunks[0]; + for ( i = 1; i < p->nChunkSize; i++ ) + { + *((char **)pTemp) = pTemp + p->nEntrySize; + pTemp += p->nEntrySize; + } + // set the last link + *((char **)pTemp) = NULL; + // set the free entry list + p->pEntriesFree = p->pChunks[0]; + // set the correct statistics + p->nMemoryAlloc = p->nEntrySize * p->nChunkSize; + p->nMemoryUsed = 0; + p->nEntriesAlloc = p->nChunkSize; + p->nEntriesUsed = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_MmFixedReadMemUsage( Extra_MmFixed_t * p ) +{ + return p->nMemoryAlloc; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_MmFixedReadMaxEntriesUsed( Extra_MmFixed_t * p ) +{ + return p->nEntriesMax; +} + + +/**Function************************************************************* + + Synopsis [Allocates entries of flexible size.] + + Description [Can only work with entry size at least 4 byte long.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Extra_MmFlex_t * Extra_MmFlexStart() +{ + Extra_MmFlex_t * p; +//printf( "allocing flex\n" ); + p = ALLOC( Extra_MmFlex_t, 1 ); + memset( p, 0, sizeof(Extra_MmFlex_t) ); + + p->nEntriesUsed = 0; + p->pCurrent = NULL; + p->pEnd = NULL; + + p->nChunkSize = (1 << 10); + p->nChunksAlloc = 64; + p->nChunks = 0; + p->pChunks = ALLOC( char *, p->nChunksAlloc ); + + p->nMemoryUsed = 0; + p->nMemoryAlloc = 0; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_MmFlexPrint( Extra_MmFlex_t * p ) +{ + printf( "Flexible memory manager: Chunk size = %d. Chunks used = %d.\n", + p->nChunkSize, p->nChunks ); + printf( " Entries used = %d. Memory used = %d. Memory alloc = %d.\n", + p->nEntriesUsed, p->nMemoryUsed, p->nMemoryAlloc ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_MmFlexStop( Extra_MmFlex_t * p ) +{ + int i; + if ( p == NULL ) + return; +//printf( "deleting flex\n" ); + for ( i = 0; i < p->nChunks; i++ ) + free( p->pChunks[i] ); + free( p->pChunks ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Extra_MmFlexEntryFetch( Extra_MmFlex_t * p, int nBytes ) +{ + char * pTemp; + // check if there are still free entries + if ( p->pCurrent == NULL || p->pCurrent + nBytes > p->pEnd ) + { // need to allocate more entries + if ( p->nChunks == p->nChunksAlloc ) + { + p->nChunksAlloc *= 2; + p->pChunks = REALLOC( char *, p->pChunks, p->nChunksAlloc ); + } + if ( nBytes > p->nChunkSize ) + { + // resize the chunk size if more memory is requested than it can give + // (ideally, this should never happen) + p->nChunkSize = 2 * nBytes; + } + p->pCurrent = ALLOC( char, p->nChunkSize ); + p->pEnd = p->pCurrent + p->nChunkSize; + p->nMemoryAlloc += p->nChunkSize; + // add the chunk to the chunk storage + p->pChunks[ p->nChunks++ ] = p->pCurrent; + } + assert( p->pCurrent + nBytes <= p->pEnd ); + // increment the counter of used entries + p->nEntriesUsed++; + // keep track of the memory used + p->nMemoryUsed += nBytes; + // return the next entry + pTemp = p->pCurrent; + p->pCurrent += nBytes; + return pTemp; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_MmFlexReadMemUsage( Extra_MmFlex_t * p ) +{ + return p->nMemoryAlloc; +} + + + + + +/**Function************************************************************* + + Synopsis [Starts the hierarchical memory manager.] + + Description [This manager can allocate entries of any size. + Iternally they are mapped into the entries with the number of bytes + equal to the power of 2. The smallest entry size is 8 bytes. The + next one is 16 bytes etc. So, if the user requests 6 bytes, he gets + 8 byte entry. If we asks for 25 bytes, he gets 32 byte entry etc. + The input parameters "nSteps" says how many fixed memory managers + are employed internally. Calling this procedure with nSteps equal + to 10 results in 10 hierarchically arranged internal memory managers, + which can allocate up to 4096 (1Kb) entries. Requests for larger + entries are handed over to malloc() and then free()ed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Extra_MmStep_t * Extra_MmStepStart( int nSteps ) +{ + Extra_MmStep_t * p; + int i, k; + p = ALLOC( Extra_MmStep_t, 1 ); + memset( p, 0, sizeof(Extra_MmStep_t) ); + p->nMems = nSteps; + // start the fixed memory managers + p->pMems = ALLOC( Extra_MmFixed_t *, p->nMems ); + for ( i = 0; i < p->nMems; i++ ) + p->pMems[i] = Extra_MmFixedStart( (8<nMapSize = (4<nMems); + p->pMap = ALLOC( Extra_MmFixed_t *, p->nMapSize+1 ); + p->pMap[0] = NULL; + for ( k = 1; k <= 4; k++ ) + p->pMap[k] = p->pMems[0]; + for ( i = 0; i < p->nMems; i++ ) + for ( k = (4<pMap[k] = p->pMems[i]; +//for ( i = 1; i < 100; i ++ ) +//printf( "%10d: size = %10d\n", i, p->pMap[i]->nEntrySize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the memory manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_MmStepStop( Extra_MmStep_t * p ) +{ + int i; + for ( i = 0; i < p->nMems; i++ ) + Extra_MmFixedStop( p->pMems[i] ); +// if ( p->pLargeChunks ) +// { +// for ( i = 0; i < p->nLargeChunks; i++ ) +// free( p->pLargeChunks[i] ); +// free( p->pLargeChunks ); +// } + free( p->pMems ); + free( p->pMap ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Creates the entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Extra_MmStepEntryFetch( Extra_MmStep_t * p, int nBytes ) +{ + if ( nBytes == 0 ) + return NULL; + if ( nBytes > p->nMapSize ) + { +// printf( "Allocating %d bytes.\n", nBytes ); +/* + if ( p->nLargeChunks == p->nLargeChunksAlloc ) + { + if ( p->nLargeChunksAlloc == 0 ) + p->nLargeChunksAlloc = 5; + p->nLargeChunksAlloc *= 2; + p->pLargeChunks = REALLOC( char *, p->pLargeChunks, p->nLargeChunksAlloc ); + } + p->pLargeChunks[ p->nLargeChunks++ ] = ALLOC( char, nBytes ); + return p->pLargeChunks[ p->nLargeChunks - 1 ]; +*/ + return ALLOC( char, nBytes ); + } + return Extra_MmFixedEntryFetch( p->pMap[nBytes] ); +} + + +/**Function************************************************************* + + Synopsis [Recycles the entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_MmStepEntryRecycle( Extra_MmStep_t * p, char * pEntry, int nBytes ) +{ + if ( nBytes == 0 ) + return; + if ( nBytes > p->nMapSize ) + { + free( pEntry ); + return; + } + Extra_MmFixedEntryRecycle( p->pMap[nBytes], pEntry ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_MmStepReadMemUsage( Extra_MmStep_t * p ) +{ + int i, nMemTotal = 0; + for ( i = 0; i < p->nMems; i++ ) + nMemTotal += p->pMems[i]->nMemoryAlloc; + return nMemTotal; +} + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + diff --git a/abc_with_bb_support/src/misc/extra/extraUtilMisc.c b/abc_with_bb_support/src/misc/extra/extraUtilMisc.c new file mode 100644 index 000000000..c3c2f60e0 --- /dev/null +++ b/abc_with_bb_support/src/misc/extra/extraUtilMisc.c @@ -0,0 +1,2235 @@ +/**CFile**************************************************************** + + FileName [extraUtilMisc.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [extra] + + Synopsis [Misc procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: extraUtilMisc.c,v 1.0 2003/09/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "extra.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticEnd***************************************************************/ + +static void Extra_Permutations_rec( char ** pRes, int nFact, int n, char Array[] ); + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Finds the smallest integer larger of equal than the logarithm.] + + Description [Returns [Log2(Num)].] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int Extra_Base2Log( unsigned Num ) +{ + int Res; + assert( Num >= 0 ); + if ( Num == 0 ) return 0; + if ( Num == 1 ) return 1; + for ( Res = 0, Num--; Num; Num >>= 1, Res++ ); + return Res; +} /* end of Extra_Base2Log */ + +/**Function******************************************************************** + + Synopsis [Finds the smallest integer larger of equal than the logarithm.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int Extra_Base2LogDouble( double Num ) +{ + double Res; + int ResInt; + + Res = log(Num)/log(2.0); + ResInt = (int)Res; + if ( ResInt == Res ) + return ResInt; + else + return ResInt+1; +} + +/**Function******************************************************************** + + Synopsis [Finds the smallest integer larger of equal than the logarithm.] + + Description [Returns [Log10(Num)].] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int Extra_Base10Log( unsigned Num ) +{ + int Res; + assert( Num >= 0 ); + if ( Num == 0 ) return 0; + if ( Num == 1 ) return 1; + for ( Res = 0, Num--; Num; Num /= 10, Res++ ); + return Res; +} /* end of Extra_Base2Log */ + +/**Function******************************************************************** + + Synopsis [Returns the power of two as a double.] + + Description [] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +double Extra_Power2( int Degree ) +{ + double Res; + assert( Degree >= 0 ); + if ( Degree < 32 ) + return (double)(01<>= Shift; + uTruth = uCof0 | uCof1; + } + return uTruth; +} + +/**Function************************************************************* + + Synopsis [Computes N-canonical form using brute-force methods.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Extra_TruthCanonN( unsigned uTruth, int nVars ) +{ + unsigned uTruthMin, uPhase; + int nMints, i; + nMints = (1 << nVars); + uTruthMin = 0xFFFFFFFF; + for ( i = 0; i < nMints; i++ ) + { + uPhase = Extra_TruthPolarize( uTruth, i, nVars ); + if ( uTruthMin > uPhase ) + uTruthMin = uPhase; + } + return uTruthMin; +} + +/**Function************************************************************* + + Synopsis [Computes NN-canonical form using brute-force methods.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Extra_TruthCanonNN( unsigned uTruth, int nVars ) +{ + unsigned uTruthMin, uTruthC, uPhase; + int nMints, i; + nMints = (1 << nVars); + uTruthC = (unsigned)( (~uTruth) & ((~((unsigned)0)) >> (32-nMints)) ); + uTruthMin = 0xFFFFFFFF; + for ( i = 0; i < nMints; i++ ) + { + uPhase = Extra_TruthPolarize( uTruth, i, nVars ); + if ( uTruthMin > uPhase ) + uTruthMin = uPhase; + uPhase = Extra_TruthPolarize( uTruthC, i, nVars ); + if ( uTruthMin > uPhase ) + uTruthMin = uPhase; + } + return uTruthMin; +} + +/**Function************************************************************* + + Synopsis [Computes P-canonical form using brute-force methods.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Extra_TruthCanonP( unsigned uTruth, int nVars ) +{ + static int nVarsOld, nPerms; + static char ** pPerms = NULL; + + unsigned uTruthMin, uPerm; + int k; + + if ( pPerms == NULL ) + { + nPerms = Extra_Factorial( nVars ); + pPerms = Extra_Permutations( nVars ); + nVarsOld = nVars; + } + else if ( nVarsOld != nVars ) + { + free( pPerms ); + nPerms = Extra_Factorial( nVars ); + pPerms = Extra_Permutations( nVars ); + nVarsOld = nVars; + } + + uTruthMin = 0xFFFFFFFF; + for ( k = 0; k < nPerms; k++ ) + { + uPerm = Extra_TruthPermute( uTruth, pPerms[k], nVars, 0 ); + if ( uTruthMin > uPerm ) + uTruthMin = uPerm; + } + return uTruthMin; +} + +/**Function************************************************************* + + Synopsis [Computes NP-canonical form using brute-force methods.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Extra_TruthCanonNP( unsigned uTruth, int nVars ) +{ + static int nVarsOld, nPerms; + static char ** pPerms = NULL; + + unsigned uTruthMin, uPhase, uPerm; + int nMints, k, i; + + if ( pPerms == NULL ) + { + nPerms = Extra_Factorial( nVars ); + pPerms = Extra_Permutations( nVars ); + nVarsOld = nVars; + } + else if ( nVarsOld != nVars ) + { + free( pPerms ); + nPerms = Extra_Factorial( nVars ); + pPerms = Extra_Permutations( nVars ); + nVarsOld = nVars; + } + + nMints = (1 << nVars); + uTruthMin = 0xFFFFFFFF; + for ( i = 0; i < nMints; i++ ) + { + uPhase = Extra_TruthPolarize( uTruth, i, nVars ); + for ( k = 0; k < nPerms; k++ ) + { + uPerm = Extra_TruthPermute( uPhase, pPerms[k], nVars, 0 ); + if ( uTruthMin > uPerm ) + uTruthMin = uPerm; + } + } + return uTruthMin; +} + +/**Function************************************************************* + + Synopsis [Computes NPN-canonical form using brute-force methods.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Extra_TruthCanonNPN( unsigned uTruth, int nVars ) +{ + static int nVarsOld, nPerms; + static char ** pPerms = NULL; + + unsigned uTruthMin, uTruthC, uPhase, uPerm; + int nMints, k, i; + + if ( pPerms == NULL ) + { + nPerms = Extra_Factorial( nVars ); + pPerms = Extra_Permutations( nVars ); + nVarsOld = nVars; + } + else if ( nVarsOld != nVars ) + { + free( pPerms ); + nPerms = Extra_Factorial( nVars ); + pPerms = Extra_Permutations( nVars ); + nVarsOld = nVars; + } + + nMints = (1 << nVars); + uTruthC = (unsigned)( (~uTruth) & ((~((unsigned)0)) >> (32-nMints)) ); + uTruthMin = 0xFFFFFFFF; + for ( i = 0; i < nMints; i++ ) + { + uPhase = Extra_TruthPolarize( uTruth, i, nVars ); + for ( k = 0; k < nPerms; k++ ) + { + uPerm = Extra_TruthPermute( uPhase, pPerms[k], nVars, 0 ); + if ( uTruthMin > uPerm ) + uTruthMin = uPerm; + } + uPhase = Extra_TruthPolarize( uTruthC, i, nVars ); + for ( k = 0; k < nPerms; k++ ) + { + uPerm = Extra_TruthPermute( uPhase, pPerms[k], nVars, 0 ); + if ( uTruthMin > uPerm ) + uTruthMin = uPerm; + } + } + return uTruthMin; +} + +/**Function************************************************************* + + Synopsis [Computes NPN canonical forms for 4-variable functions.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_Truth4VarNPN( unsigned short ** puCanons, char ** puPhases, char ** puPerms, unsigned char ** puMap ) +{ + unsigned short * uCanons; + unsigned char * uMap; + unsigned uTruth, uPhase, uPerm; + char ** pPerms4, * uPhases, * uPerms; + int nFuncs, nClasses; + int i, k; + + nFuncs = (1 << 16); + uCanons = ALLOC( unsigned short, nFuncs ); + uPhases = ALLOC( char, nFuncs ); + uPerms = ALLOC( char, nFuncs ); + uMap = ALLOC( unsigned char, nFuncs ); + memset( uCanons, 0, sizeof(unsigned short) * nFuncs ); + memset( uPhases, 0, sizeof(char) * nFuncs ); + memset( uPerms, 0, sizeof(char) * nFuncs ); + memset( uMap, 0, sizeof(unsigned char) * nFuncs ); + pPerms4 = Extra_Permutations( 4 ); + + nClasses = 1; + nFuncs = (1 << 15); + for ( uTruth = 1; uTruth < (unsigned)nFuncs; uTruth++ ) + { + // skip already assigned + if ( uCanons[uTruth] ) + { + assert( uTruth > uCanons[uTruth] ); + uMap[~uTruth & 0xFFFF] = uMap[uTruth] = uMap[uCanons[uTruth]]; + continue; + } + uMap[uTruth] = nClasses++; + for ( i = 0; i < 16; i++ ) + { + uPhase = Extra_TruthPolarize( uTruth, i, 4 ); + for ( k = 0; k < 24; k++ ) + { + uPerm = Extra_TruthPermute( uPhase, pPerms4[k], 4, 0 ); + if ( uCanons[uPerm] == 0 ) + { + uCanons[uPerm] = uTruth; + uPhases[uPerm] = i; + uPerms[uPerm] = k; + + uPerm = ~uPerm & 0xFFFF; + uCanons[uPerm] = uTruth; + uPhases[uPerm] = i | 16; + uPerms[uPerm] = k; + } + else + assert( uCanons[uPerm] == uTruth ); + } + uPhase = Extra_TruthPolarize( ~uTruth & 0xFFFF, i, 4 ); + for ( k = 0; k < 24; k++ ) + { + uPerm = Extra_TruthPermute( uPhase, pPerms4[k], 4, 0 ); + if ( uCanons[uPerm] == 0 ) + { + uCanons[uPerm] = uTruth; + uPhases[uPerm] = i; + uPerms[uPerm] = k; + + uPerm = ~uPerm & 0xFFFF; + uCanons[uPerm] = uTruth; + uPhases[uPerm] = i | 16; + uPerms[uPerm] = k; + } + else + assert( uCanons[uPerm] == uTruth ); + } + } + } + uPhases[(1<<16)-1] = 16; + assert( nClasses == 222 ); + free( pPerms4 ); + if ( puCanons ) + *puCanons = uCanons; + else + free( uCanons ); + if ( puPhases ) + *puPhases = uPhases; + else + free( uPhases ); + if ( puPerms ) + *puPerms = uPerms; + else + free( uPerms ); + if ( puMap ) + *puMap = uMap; + else + free( uMap ); +} + +/**Function************************************************************* + + Synopsis [Computes NPN canonical forms for 4-variable functions.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_Truth3VarN( unsigned ** puCanons, char *** puPhases, char ** ppCounters ) +{ + int nPhasesMax = 8; + unsigned * uCanons; + unsigned uTruth, uPhase, uTruth32; + char ** uPhases, * pCounters; + int nFuncs, nClasses, i; + + nFuncs = (1 << 8); + uCanons = ALLOC( unsigned, nFuncs ); + memset( uCanons, 0, sizeof(unsigned) * nFuncs ); + pCounters = ALLOC( char, nFuncs ); + memset( pCounters, 0, sizeof(char) * nFuncs ); + uPhases = (char **)Extra_ArrayAlloc( nFuncs, nPhasesMax, sizeof(char) ); + nClasses = 0; + for ( uTruth = 0; uTruth < (unsigned)nFuncs; uTruth++ ) + { + // skip already assigned + uTruth32 = ((uTruth << 24) | (uTruth << 16) | (uTruth << 8) | uTruth); + if ( uCanons[uTruth] ) + { + assert( uTruth32 > uCanons[uTruth] ); + continue; + } + nClasses++; + for ( i = 0; i < 8; i++ ) + { + uPhase = Extra_TruthPolarize( uTruth, i, 3 ); + if ( uCanons[uPhase] == 0 && (uTruth || i==0) ) + { + uCanons[uPhase] = uTruth32; + uPhases[uPhase][0] = i; + pCounters[uPhase] = 1; + } + else + { + assert( uCanons[uPhase] == uTruth32 ); + if ( pCounters[uPhase] < nPhasesMax ) + uPhases[uPhase][ pCounters[uPhase]++ ] = i; + } + } + } + if ( puCanons ) + *puCanons = uCanons; + else + free( uCanons ); + if ( puPhases ) + *puPhases = uPhases; + else + free( uPhases ); + if ( ppCounters ) + *ppCounters = pCounters; + else + free( pCounters ); +// printf( "The number of 3N-classes = %d.\n", nClasses ); +} + +/**Function************************************************************* + + Synopsis [Computes NPN canonical forms for 4-variable functions.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_Truth4VarN( unsigned short ** puCanons, char *** puPhases, char ** ppCounters, int nPhasesMax ) +{ + unsigned short * uCanons; + unsigned uTruth, uPhase; + char ** uPhases, * pCounters; + int nFuncs, nClasses, i; + + nFuncs = (1 << 16); + uCanons = ALLOC( unsigned short, nFuncs ); + memset( uCanons, 0, sizeof(unsigned short) * nFuncs ); + pCounters = ALLOC( char, nFuncs ); + memset( pCounters, 0, sizeof(char) * nFuncs ); + uPhases = (char **)Extra_ArrayAlloc( nFuncs, nPhasesMax, sizeof(char) ); + nClasses = 0; + for ( uTruth = 0; uTruth < (unsigned)nFuncs; uTruth++ ) + { + // skip already assigned + if ( uCanons[uTruth] ) + { + assert( uTruth > uCanons[uTruth] ); + continue; + } + nClasses++; + for ( i = 0; i < 16; i++ ) + { + uPhase = Extra_TruthPolarize( uTruth, i, 4 ); + if ( uCanons[uPhase] == 0 && (uTruth || i==0) ) + { + uCanons[uPhase] = uTruth; + uPhases[uPhase][0] = i; + pCounters[uPhase] = 1; + } + else + { + assert( uCanons[uPhase] == uTruth ); + if ( pCounters[uPhase] < nPhasesMax ) + uPhases[uPhase][ pCounters[uPhase]++ ] = i; + } + } + } + if ( puCanons ) + *puCanons = uCanons; + else + free( uCanons ); + if ( puPhases ) + *puPhases = uPhases; + else + free( uPhases ); + if ( ppCounters ) + *ppCounters = pCounters; + else + free( pCounters ); +// printf( "The number of 4N-classes = %d.\n", nClasses ); +} + +/**Function************************************************************* + + Synopsis [Allocated one-memory-chunk array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void ** Extra_ArrayAlloc( int nCols, int nRows, int Size ) +{ + char ** pRes; + char * pBuffer; + int i; + assert( nCols > 0 && nRows > 0 && Size > 0 ); + pBuffer = ALLOC( char, nCols * (sizeof(void *) + nRows * Size) ); + pRes = (char **)pBuffer; + pRes[0] = pBuffer + nCols * sizeof(void *); + for ( i = 1; i < nCols; i++ ) + pRes[i] = pRes[0] + i * nRows * Size; + return pRes; +} + +/**Function************************************************************* + + Synopsis [Computes a phase of the 3-var function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned short Extra_TruthPerm4One( unsigned uTruth, int Phase ) +{ + // cases + static unsigned short Cases[16] = { + 0, // 0000 - skip + 0, // 0001 - skip + 0xCCCC, // 0010 - single var + 0, // 0011 - skip + 0xF0F0, // 0100 - single var + 1, // 0101 + 1, // 0110 + 0, // 0111 - skip + 0xFF00, // 1000 - single var + 1, // 1001 + 1, // 1010 + 1, // 1011 + 1, // 1100 + 1, // 1101 + 1, // 1110 + 0 // 1111 - skip + }; + // permutations + static int Perms[16][4] = { + { 0, 0, 0, 0 }, // 0000 - skip + { 0, 0, 0, 0 }, // 0001 - skip + { 0, 0, 0, 0 }, // 0010 - single var + { 0, 0, 0, 0 }, // 0011 - skip + { 0, 0, 0, 0 }, // 0100 - single var + { 0, 2, 1, 3 }, // 0101 + { 2, 0, 1, 3 }, // 0110 + { 0, 0, 0, 0 }, // 0111 - skip + { 0, 0, 0, 0 }, // 1000 - single var + { 0, 2, 3, 1 }, // 1001 + { 2, 0, 3, 1 }, // 1010 + { 0, 1, 3, 2 }, // 1011 + { 2, 3, 0, 1 }, // 1100 + { 0, 3, 1, 2 }, // 1101 + { 3, 0, 1, 2 }, // 1110 + { 0, 0, 0, 0 } // 1111 - skip + }; + int i, k, iRes; + unsigned uTruthRes; + assert( Phase >= 0 && Phase < 16 ); + if ( Cases[Phase] == 0 ) + return uTruth; + if ( Cases[Phase] > 1 ) + return Cases[Phase]; + uTruthRes = 0; + for ( i = 0; i < 16; i++ ) + if ( uTruth & (1 << i) ) + { + for ( iRes = 0, k = 0; k < 4; k++ ) + if ( i & (1 << Perms[Phase][k]) ) + iRes |= (1 << k); + uTruthRes |= (1 << iRes); + } + return uTruthRes; +} + +/**Function************************************************************* + + Synopsis [Computes a phase of the 3-var function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Extra_TruthPerm5One( unsigned uTruth, int Phase ) +{ + // cases + static unsigned Cases[32] = { + 0, // 00000 - skip + 0, // 00001 - skip + 0xCCCCCCCC, // 00010 - single var + 0, // 00011 - skip + 0xF0F0F0F0, // 00100 - single var + 1, // 00101 + 1, // 00110 + 0, // 00111 - skip + 0xFF00FF00, // 01000 - single var + 1, // 01001 + 1, // 01010 + 1, // 01011 + 1, // 01100 + 1, // 01101 + 1, // 01110 + 0, // 01111 - skip + 0xFFFF0000, // 10000 - skip + 1, // 10001 + 1, // 10010 + 1, // 10011 + 1, // 10100 + 1, // 10101 + 1, // 10110 + 1, // 10111 - four var + 1, // 11000 + 1, // 11001 + 1, // 11010 + 1, // 11011 - four var + 1, // 11100 + 1, // 11101 - four var + 1, // 11110 - four var + 0 // 11111 - skip + }; + // permutations + static int Perms[32][5] = { + { 0, 0, 0, 0, 0 }, // 00000 - skip + { 0, 0, 0, 0, 0 }, // 00001 - skip + { 0, 0, 0, 0, 0 }, // 00010 - single var + { 0, 0, 0, 0, 0 }, // 00011 - skip + { 0, 0, 0, 0, 0 }, // 00100 - single var + { 0, 2, 1, 3, 4 }, // 00101 + { 2, 0, 1, 3, 4 }, // 00110 + { 0, 0, 0, 0, 0 }, // 00111 - skip + { 0, 0, 0, 0, 0 }, // 01000 - single var + { 0, 2, 3, 1, 4 }, // 01001 + { 2, 0, 3, 1, 4 }, // 01010 + { 0, 1, 3, 2, 4 }, // 01011 + { 2, 3, 0, 1, 4 }, // 01100 + { 0, 3, 1, 2, 4 }, // 01101 + { 3, 0, 1, 2, 4 }, // 01110 + { 0, 0, 0, 0, 0 }, // 01111 - skip + { 0, 0, 0, 0, 0 }, // 10000 - single var + { 0, 4, 2, 3, 1 }, // 10001 + { 4, 0, 2, 3, 1 }, // 10010 + { 0, 1, 3, 4, 2 }, // 10011 + { 2, 3, 0, 4, 1 }, // 10100 + { 0, 3, 1, 4, 2 }, // 10101 + { 3, 0, 1, 4, 2 }, // 10110 + { 0, 1, 2, 4, 3 }, // 10111 - four var + { 2, 3, 4, 0, 1 }, // 11000 + { 0, 3, 4, 1, 2 }, // 11001 + { 3, 0, 4, 1, 2 }, // 11010 + { 0, 1, 4, 2, 3 }, // 11011 - four var + { 3, 4, 0, 1, 2 }, // 11100 + { 0, 4, 1, 2, 3 }, // 11101 - four var + { 4, 0, 1, 2, 3 }, // 11110 - four var + { 0, 0, 0, 0, 0 } // 11111 - skip + }; + int i, k, iRes; + unsigned uTruthRes; + assert( Phase >= 0 && Phase < 32 ); + if ( Cases[Phase] == 0 ) + return uTruth; + if ( Cases[Phase] > 1 ) + return Cases[Phase]; + uTruthRes = 0; + for ( i = 0; i < 32; i++ ) + if ( uTruth & (1 << i) ) + { + for ( iRes = 0, k = 0; k < 5; k++ ) + if ( i & (1 << Perms[Phase][k]) ) + iRes |= (1 << k); + uTruthRes |= (1 << iRes); + } + return uTruthRes; +} + +/**Function************************************************************* + + Synopsis [Computes a phase of the 3-var function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_TruthPerm6One( unsigned * uTruth, int Phase, unsigned * uTruthRes ) +{ + // cases + static unsigned Cases[64] = { + 0, // 000000 - skip + 0, // 000001 - skip + 0xCCCCCCCC, // 000010 - single var + 0, // 000011 - skip + 0xF0F0F0F0, // 000100 - single var + 1, // 000101 + 1, // 000110 + 0, // 000111 - skip + 0xFF00FF00, // 001000 - single var + 1, // 001001 + 1, // 001010 + 1, // 001011 + 1, // 001100 + 1, // 001101 + 1, // 001110 + 0, // 001111 - skip + 0xFFFF0000, // 010000 - skip + 1, // 010001 + 1, // 010010 + 1, // 010011 + 1, // 010100 + 1, // 010101 + 1, // 010110 + 1, // 010111 - four var + 1, // 011000 + 1, // 011001 + 1, // 011010 + 1, // 011011 - four var + 1, // 011100 + 1, // 011101 - four var + 1, // 011110 - four var + 0, // 011111 - skip + 0xFFFFFFFF, // 100000 - single var + 1, // 100001 + 1, // 100010 + 1, // 100011 + 1, // 100100 + 1, // 100101 + 1, // 100110 + 1, // 100111 + 1, // 101000 + 1, // 101001 + 1, // 101010 + 1, // 101011 + 1, // 101100 + 1, // 101101 + 1, // 101110 + 1, // 101111 + 1, // 110000 + 1, // 110001 + 1, // 110010 + 1, // 110011 + 1, // 110100 + 1, // 110101 + 1, // 110110 + 1, // 110111 + 1, // 111000 + 1, // 111001 + 1, // 111010 + 1, // 111011 + 1, // 111100 + 1, // 111101 + 1, // 111110 + 0 // 111111 - skip + }; + // permutations + static int Perms[64][6] = { + { 0, 0, 0, 0, 0, 0 }, // 000000 - skip + { 0, 0, 0, 0, 0, 0 }, // 000001 - skip + { 0, 0, 0, 0, 0, 0 }, // 000010 - single var + { 0, 0, 0, 0, 0, 0 }, // 000011 - skip + { 0, 0, 0, 0, 0, 0 }, // 000100 - single var + { 0, 2, 1, 3, 4, 5 }, // 000101 + { 2, 0, 1, 3, 4, 5 }, // 000110 + { 0, 0, 0, 0, 0, 0 }, // 000111 - skip + { 0, 0, 0, 0, 0, 0 }, // 001000 - single var + { 0, 2, 3, 1, 4, 5 }, // 001001 + { 2, 0, 3, 1, 4, 5 }, // 001010 + { 0, 1, 3, 2, 4, 5 }, // 001011 + { 2, 3, 0, 1, 4, 5 }, // 001100 + { 0, 3, 1, 2, 4, 5 }, // 001101 + { 3, 0, 1, 2, 4, 5 }, // 001110 + { 0, 0, 0, 0, 0, 0 }, // 001111 - skip + { 0, 0, 0, 0, 0, 0 }, // 010000 - skip + { 0, 4, 2, 3, 1, 5 }, // 010001 + { 4, 0, 2, 3, 1, 5 }, // 010010 + { 0, 1, 3, 4, 2, 5 }, // 010011 + { 2, 3, 0, 4, 1, 5 }, // 010100 + { 0, 3, 1, 4, 2, 5 }, // 010101 + { 3, 0, 1, 4, 2, 5 }, // 010110 + { 0, 1, 2, 4, 3, 5 }, // 010111 - four var + { 2, 3, 4, 0, 1, 5 }, // 011000 + { 0, 3, 4, 1, 2, 5 }, // 011001 + { 3, 0, 4, 1, 2, 5 }, // 011010 + { 0, 1, 4, 2, 3, 5 }, // 011011 - four var + { 3, 4, 0, 1, 2, 5 }, // 011100 + { 0, 4, 1, 2, 3, 5 }, // 011101 - four var + { 4, 0, 1, 2, 3, 5 }, // 011110 - four var + { 0, 0, 0, 0, 0, 0 }, // 011111 - skip + { 0, 0, 0, 0, 0, 0 }, // 100000 - single var + { 0, 2, 3, 4, 5, 1 }, // 100001 + { 2, 0, 3, 4, 5, 1 }, // 100010 + { 0, 1, 3, 4, 5, 2 }, // 100011 + { 2, 3, 0, 4, 5, 1 }, // 100100 + { 0, 3, 1, 4, 5, 2 }, // 100101 + { 3, 0, 1, 4, 5, 2 }, // 100110 + { 0, 1, 2, 4, 5, 3 }, // 100111 + { 2, 3, 4, 0, 5, 1 }, // 101000 + { 0, 3, 4, 1, 5, 2 }, // 101001 + { 3, 0, 4, 1, 5, 2 }, // 101010 + { 0, 1, 4, 2, 5, 3 }, // 101011 + { 3, 4, 0, 1, 5, 2 }, // 101100 + { 0, 4, 1, 2, 5, 3 }, // 101101 + { 4, 0, 1, 2, 5, 3 }, // 101110 + { 0, 1, 2, 3, 5, 4 }, // 101111 + { 2, 3, 4, 5, 0, 1 }, // 110000 + { 0, 3, 4, 5, 1, 2 }, // 110001 + { 3, 0, 4, 5, 1, 2 }, // 110010 + { 0, 1, 4, 5, 2, 3 }, // 110011 + { 3, 4, 0, 5, 1, 2 }, // 110100 + { 0, 4, 1, 5, 2, 3 }, // 110101 + { 4, 0, 1, 5, 2, 3 }, // 110110 + { 0, 1, 2, 5, 3, 4 }, // 110111 + { 3, 4, 5, 0, 1, 2 }, // 111000 + { 0, 4, 5, 1, 2, 3 }, // 111001 + { 4, 0, 5, 1, 2, 3 }, // 111010 + { 0, 1, 5, 2, 3, 4 }, // 111011 + { 4, 5, 0, 1, 2, 3 }, // 111100 + { 0, 5, 1, 2, 3, 4 }, // 111101 + { 5, 0, 1, 2, 3, 4 }, // 111110 + { 0, 0, 0, 0, 0, 0 } // 111111 - skip + }; + int i, k, iRes; + assert( Phase >= 0 && Phase < 64 ); + if ( Cases[Phase] == 0 ) + { + uTruthRes[0] = uTruth[0]; + uTruthRes[1] = uTruth[1]; + return; + } + if ( Cases[Phase] > 1 ) + { + if ( Phase == 32 ) + { + uTruthRes[0] = 0x00000000; + uTruthRes[1] = 0xFFFFFFFF; + } + else + { + uTruthRes[0] = Cases[Phase]; + uTruthRes[1] = Cases[Phase]; + } + return; + } + uTruthRes[0] = 0; + uTruthRes[1] = 0; + for ( i = 0; i < 64; i++ ) + { + if ( i < 32 ) + { + if ( uTruth[0] & (1 << i) ) + { + for ( iRes = 0, k = 0; k < 6; k++ ) + if ( i & (1 << Perms[Phase][k]) ) + iRes |= (1 << k); + if ( iRes < 32 ) + uTruthRes[0] |= (1 << iRes); + else + uTruthRes[1] |= (1 << (iRes-32)); + } + } + else + { + if ( uTruth[1] & (1 << (i-32)) ) + { + for ( iRes = 0, k = 0; k < 6; k++ ) + if ( i & (1 << Perms[Phase][k]) ) + iRes |= (1 << k); + if ( iRes < 32 ) + uTruthRes[0] |= (1 << iRes); + else + uTruthRes[1] |= (1 << (iRes-32)); + } + } + } +} + +/**Function************************************************************* + + Synopsis [Computes a phase of the 8-var function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_TruthExpand( int nVars, int nWords, unsigned * puTruth, unsigned uPhase, unsigned * puTruthR ) +{ + // elementary truth tables + static unsigned uTruths[8][8] = { + { 0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA }, + { 0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC }, + { 0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0 }, + { 0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00 }, + { 0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000 }, + { 0x00000000,0xFFFFFFFF,0x00000000,0xFFFFFFFF,0x00000000,0xFFFFFFFF,0x00000000,0xFFFFFFFF }, + { 0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF,0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF }, + { 0x00000000,0x00000000,0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF } + }; + static char Cases[256] = { + 0, // 00000000 + 0, // 00000001 + 1, // 00000010 + 0, // 00000011 + 2, // 00000100 + -1, // 00000101 + -1, // 00000110 + 0, // 00000111 + 3, // 00001000 + -1, // 00001001 + -1, // 00001010 + -1, // 00001011 + -1, // 00001100 + -1, // 00001101 + -1, // 00001110 + 0, // 00001111 + 4, // 00010000 + -1, // 00010001 + -1, // 00010010 + -1, // 00010011 + -1, // 00010100 + -1, // 00010101 + -1, // 00010110 + -1, // 00010111 + -1, // 00011000 + -1, // 00011001 + -1, // 00011010 + -1, // 00011011 + -1, // 00011100 + -1, // 00011101 + -1, // 00011110 + 0, // 00011111 + 5, // 00100000 + -1, // 00100001 + -1, // 00100010 + -1, // 00100011 + -1, // 00100100 + -1, // 00100101 + -1, // 00100110 + -1, // 00100111 + -1, // 00101000 + -1, // 00101001 + -1, // 00101010 + -1, // 00101011 + -1, // 00101100 + -1, // 00101101 + -1, // 00101110 + -1, // 00101111 + -1, // 00110000 + -1, // 00110001 + -1, // 00110010 + -1, // 00110011 + -1, // 00110100 + -1, // 00110101 + -1, // 00110110 + -1, // 00110111 + -1, // 00111000 + -1, // 00111001 + -1, // 00111010 + -1, // 00111011 + -1, // 00111100 + -1, // 00111101 + -1, // 00111110 + 0, // 00111111 + 6, // 01000000 + -1, // 01000001 + -1, // 01000010 + -1, // 01000011 + -1, // 01000100 + -1, // 01000101 + -1, // 01000110 + -1, // 01000111 + -1, // 01001000 + -1, // 01001001 + -1, // 01001010 + -1, // 01001011 + -1, // 01001100 + -1, // 01001101 + -1, // 01001110 + -1, // 01001111 + -1, // 01010000 + -1, // 01010001 + -1, // 01010010 + -1, // 01010011 + -1, // 01010100 + -1, // 01010101 + -1, // 01010110 + -1, // 01010111 + -1, // 01011000 + -1, // 01011001 + -1, // 01011010 + -1, // 01011011 + -1, // 01011100 + -1, // 01011101 + -1, // 01011110 + -1, // 01011111 + -1, // 01100000 + -1, // 01100001 + -1, // 01100010 + -1, // 01100011 + -1, // 01100100 + -1, // 01100101 + -1, // 01100110 + -1, // 01100111 + -1, // 01101000 + -1, // 01101001 + -1, // 01101010 + -1, // 01101011 + -1, // 01101100 + -1, // 01101101 + -1, // 01101110 + -1, // 01101111 + -1, // 01110000 + -1, // 01110001 + -1, // 01110010 + -1, // 01110011 + -1, // 01110100 + -1, // 01110101 + -1, // 01110110 + -1, // 01110111 + -1, // 01111000 + -1, // 01111001 + -1, // 01111010 + -1, // 01111011 + -1, // 01111100 + -1, // 01111101 + -1, // 01111110 + 0, // 01111111 + 7, // 10000000 + -1, // 10000001 + -1, // 10000010 + -1, // 10000011 + -1, // 10000100 + -1, // 10000101 + -1, // 10000110 + -1, // 10000111 + -1, // 10001000 + -1, // 10001001 + -1, // 10001010 + -1, // 10001011 + -1, // 10001100 + -1, // 10001101 + -1, // 10001110 + -1, // 10001111 + -1, // 10010000 + -1, // 10010001 + -1, // 10010010 + -1, // 10010011 + -1, // 10010100 + -1, // 10010101 + -1, // 10010110 + -1, // 10010111 + -1, // 10011000 + -1, // 10011001 + -1, // 10011010 + -1, // 10011011 + -1, // 10011100 + -1, // 10011101 + -1, // 10011110 + -1, // 10011111 + -1, // 10100000 + -1, // 10100001 + -1, // 10100010 + -1, // 10100011 + -1, // 10100100 + -1, // 10100101 + -1, // 10100110 + -1, // 10100111 + -1, // 10101000 + -1, // 10101001 + -1, // 10101010 + -1, // 10101011 + -1, // 10101100 + -1, // 10101101 + -1, // 10101110 + -1, // 10101111 + -1, // 10110000 + -1, // 10110001 + -1, // 10110010 + -1, // 10110011 + -1, // 10110100 + -1, // 10110101 + -1, // 10110110 + -1, // 10110111 + -1, // 10111000 + -1, // 10111001 + -1, // 10111010 + -1, // 10111011 + -1, // 10111100 + -1, // 10111101 + -1, // 10111110 + -1, // 10111111 + -1, // 11000000 + -1, // 11000001 + -1, // 11000010 + -1, // 11000011 + -1, // 11000100 + -1, // 11000101 + -1, // 11000110 + -1, // 11000111 + -1, // 11001000 + -1, // 11001001 + -1, // 11001010 + -1, // 11001011 + -1, // 11001100 + -1, // 11001101 + -1, // 11001110 + -1, // 11001111 + -1, // 11010000 + -1, // 11010001 + -1, // 11010010 + -1, // 11010011 + -1, // 11010100 + -1, // 11010101 + -1, // 11010110 + -1, // 11010111 + -1, // 11011000 + -1, // 11011001 + -1, // 11011010 + -1, // 11011011 + -1, // 11011100 + -1, // 11011101 + -1, // 11011110 + -1, // 11011111 + -1, // 11100000 + -1, // 11100001 + -1, // 11100010 + -1, // 11100011 + -1, // 11100100 + -1, // 11100101 + -1, // 11100110 + -1, // 11100111 + -1, // 11101000 + -1, // 11101001 + -1, // 11101010 + -1, // 11101011 + -1, // 11101100 + -1, // 11101101 + -1, // 11101110 + -1, // 11101111 + -1, // 11110000 + -1, // 11110001 + -1, // 11110010 + -1, // 11110011 + -1, // 11110100 + -1, // 11110101 + -1, // 11110110 + -1, // 11110111 + -1, // 11111000 + -1, // 11111001 + -1, // 11111010 + -1, // 11111011 + -1, // 11111100 + -1, // 11111101 + -1, // 11111110 + 0 // 11111111 + }; + static char Perms[256][8] = { + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 00000000 + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 00000001 + { 1, 0, 2, 3, 4, 5, 6, 7 }, // 00000010 + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 00000011 + { 1, 2, 0, 3, 4, 5, 6, 7 }, // 00000100 + { 0, 2, 1, 3, 4, 5, 6, 7 }, // 00000101 + { 2, 0, 1, 3, 4, 5, 6, 7 }, // 00000110 + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 00000111 + { 1, 2, 3, 0, 4, 5, 6, 7 }, // 00001000 + { 0, 2, 3, 1, 4, 5, 6, 7 }, // 00001001 + { 2, 0, 3, 1, 4, 5, 6, 7 }, // 00001010 + { 0, 1, 3, 2, 4, 5, 6, 7 }, // 00001011 + { 2, 3, 0, 1, 4, 5, 6, 7 }, // 00001100 + { 0, 3, 1, 2, 4, 5, 6, 7 }, // 00001101 + { 3, 0, 1, 2, 4, 5, 6, 7 }, // 00001110 + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 00001111 + { 1, 2, 3, 4, 0, 5, 6, 7 }, // 00010000 + { 0, 2, 3, 4, 1, 5, 6, 7 }, // 00010001 + { 2, 0, 3, 4, 1, 5, 6, 7 }, // 00010010 + { 0, 1, 3, 4, 2, 5, 6, 7 }, // 00010011 + { 2, 3, 0, 4, 1, 5, 6, 7 }, // 00010100 + { 0, 3, 1, 4, 2, 5, 6, 7 }, // 00010101 + { 3, 0, 1, 4, 2, 5, 6, 7 }, // 00010110 + { 0, 1, 2, 4, 3, 5, 6, 7 }, // 00010111 + { 2, 3, 4, 0, 1, 5, 6, 7 }, // 00011000 + { 0, 3, 4, 1, 2, 5, 6, 7 }, // 00011001 + { 3, 0, 4, 1, 2, 5, 6, 7 }, // 00011010 + { 0, 1, 4, 2, 3, 5, 6, 7 }, // 00011011 + { 3, 4, 0, 1, 2, 5, 6, 7 }, // 00011100 + { 0, 4, 1, 2, 3, 5, 6, 7 }, // 00011101 + { 4, 0, 1, 2, 3, 5, 6, 7 }, // 00011110 + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 00011111 + { 1, 2, 3, 4, 5, 0, 6, 7 }, // 00100000 + { 0, 2, 3, 4, 5, 1, 6, 7 }, // 00100001 + { 2, 0, 3, 4, 5, 1, 6, 7 }, // 00100010 + { 0, 1, 3, 4, 5, 2, 6, 7 }, // 00100011 + { 2, 3, 0, 4, 5, 1, 6, 7 }, // 00100100 + { 0, 3, 1, 4, 5, 2, 6, 7 }, // 00100101 + { 3, 0, 1, 4, 5, 2, 6, 7 }, // 00100110 + { 0, 1, 2, 4, 5, 3, 6, 7 }, // 00100111 + { 2, 3, 4, 0, 5, 1, 6, 7 }, // 00101000 + { 0, 3, 4, 1, 5, 2, 6, 7 }, // 00101001 + { 3, 0, 4, 1, 5, 2, 6, 7 }, // 00101010 + { 0, 1, 4, 2, 5, 3, 6, 7 }, // 00101011 + { 3, 4, 0, 1, 5, 2, 6, 7 }, // 00101100 + { 0, 4, 1, 2, 5, 3, 6, 7 }, // 00101101 + { 4, 0, 1, 2, 5, 3, 6, 7 }, // 00101110 + { 0, 1, 2, 3, 5, 4, 6, 7 }, // 00101111 + { 2, 3, 4, 5, 0, 1, 6, 7 }, // 00110000 + { 0, 3, 4, 5, 1, 2, 6, 7 }, // 00110001 + { 3, 0, 4, 5, 1, 2, 6, 7 }, // 00110010 + { 0, 1, 4, 5, 2, 3, 6, 7 }, // 00110011 + { 3, 4, 0, 5, 1, 2, 6, 7 }, // 00110100 + { 0, 4, 1, 5, 2, 3, 6, 7 }, // 00110101 + { 4, 0, 1, 5, 2, 3, 6, 7 }, // 00110110 + { 0, 1, 2, 5, 3, 4, 6, 7 }, // 00110111 + { 3, 4, 5, 0, 1, 2, 6, 7 }, // 00111000 + { 0, 4, 5, 1, 2, 3, 6, 7 }, // 00111001 + { 4, 0, 5, 1, 2, 3, 6, 7 }, // 00111010 + { 0, 1, 5, 2, 3, 4, 6, 7 }, // 00111011 + { 4, 5, 0, 1, 2, 3, 6, 7 }, // 00111100 + { 0, 5, 1, 2, 3, 4, 6, 7 }, // 00111101 + { 5, 0, 1, 2, 3, 4, 6, 7 }, // 00111110 + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 00111111 + { 1, 2, 3, 4, 5, 6, 0, 7 }, // 01000000 + { 0, 2, 3, 4, 5, 6, 1, 7 }, // 01000001 + { 2, 0, 3, 4, 5, 6, 1, 7 }, // 01000010 + { 0, 1, 3, 4, 5, 6, 2, 7 }, // 01000011 + { 2, 3, 0, 4, 5, 6, 1, 7 }, // 01000100 + { 0, 3, 1, 4, 5, 6, 2, 7 }, // 01000101 + { 3, 0, 1, 4, 5, 6, 2, 7 }, // 01000110 + { 0, 1, 2, 4, 5, 6, 3, 7 }, // 01000111 + { 2, 3, 4, 0, 5, 6, 1, 7 }, // 01001000 + { 0, 3, 4, 1, 5, 6, 2, 7 }, // 01001001 + { 3, 0, 4, 1, 5, 6, 2, 7 }, // 01001010 + { 0, 1, 4, 2, 5, 6, 3, 7 }, // 01001011 + { 3, 4, 0, 1, 5, 6, 2, 7 }, // 01001100 + { 0, 4, 1, 2, 5, 6, 3, 7 }, // 01001101 + { 4, 0, 1, 2, 5, 6, 3, 7 }, // 01001110 + { 0, 1, 2, 3, 5, 6, 4, 7 }, // 01001111 + { 2, 3, 4, 5, 0, 6, 1, 7 }, // 01010000 + { 0, 3, 4, 5, 1, 6, 2, 7 }, // 01010001 + { 3, 0, 4, 5, 1, 6, 2, 7 }, // 01010010 + { 0, 1, 4, 5, 2, 6, 3, 7 }, // 01010011 + { 3, 4, 0, 5, 1, 6, 2, 7 }, // 01010100 + { 0, 4, 1, 5, 2, 6, 3, 7 }, // 01010101 + { 4, 0, 1, 5, 2, 6, 3, 7 }, // 01010110 + { 0, 1, 2, 5, 3, 6, 4, 7 }, // 01010111 + { 3, 4, 5, 0, 1, 6, 2, 7 }, // 01011000 + { 0, 4, 5, 1, 2, 6, 3, 7 }, // 01011001 + { 4, 0, 5, 1, 2, 6, 3, 7 }, // 01011010 + { 0, 1, 5, 2, 3, 6, 4, 7 }, // 01011011 + { 4, 5, 0, 1, 2, 6, 3, 7 }, // 01011100 + { 0, 5, 1, 2, 3, 6, 4, 7 }, // 01011101 + { 5, 0, 1, 2, 3, 6, 4, 7 }, // 01011110 + { 0, 1, 2, 3, 4, 6, 5, 7 }, // 01011111 + { 2, 3, 4, 5, 6, 0, 1, 7 }, // 01100000 + { 0, 3, 4, 5, 6, 1, 2, 7 }, // 01100001 + { 3, 0, 4, 5, 6, 1, 2, 7 }, // 01100010 + { 0, 1, 4, 5, 6, 2, 3, 7 }, // 01100011 + { 3, 4, 0, 5, 6, 1, 2, 7 }, // 01100100 + { 0, 4, 1, 5, 6, 2, 3, 7 }, // 01100101 + { 4, 0, 1, 5, 6, 2, 3, 7 }, // 01100110 + { 0, 1, 2, 5, 6, 3, 4, 7 }, // 01100111 + { 3, 4, 5, 0, 6, 1, 2, 7 }, // 01101000 + { 0, 4, 5, 1, 6, 2, 3, 7 }, // 01101001 + { 4, 0, 5, 1, 6, 2, 3, 7 }, // 01101010 + { 0, 1, 5, 2, 6, 3, 4, 7 }, // 01101011 + { 4, 5, 0, 1, 6, 2, 3, 7 }, // 01101100 + { 0, 5, 1, 2, 6, 3, 4, 7 }, // 01101101 + { 5, 0, 1, 2, 6, 3, 4, 7 }, // 01101110 + { 0, 1, 2, 3, 6, 4, 5, 7 }, // 01101111 + { 3, 4, 5, 6, 0, 1, 2, 7 }, // 01110000 + { 0, 4, 5, 6, 1, 2, 3, 7 }, // 01110001 + { 4, 0, 5, 6, 1, 2, 3, 7 }, // 01110010 + { 0, 1, 5, 6, 2, 3, 4, 7 }, // 01110011 + { 4, 5, 0, 6, 1, 2, 3, 7 }, // 01110100 + { 0, 5, 1, 6, 2, 3, 4, 7 }, // 01110101 + { 5, 0, 1, 6, 2, 3, 4, 7 }, // 01110110 + { 0, 1, 2, 6, 3, 4, 5, 7 }, // 01110111 + { 4, 5, 6, 0, 1, 2, 3, 7 }, // 01111000 + { 0, 5, 6, 1, 2, 3, 4, 7 }, // 01111001 + { 5, 0, 6, 1, 2, 3, 4, 7 }, // 01111010 + { 0, 1, 6, 2, 3, 4, 5, 7 }, // 01111011 + { 5, 6, 0, 1, 2, 3, 4, 7 }, // 01111100 + { 0, 6, 1, 2, 3, 4, 5, 7 }, // 01111101 + { 6, 0, 1, 2, 3, 4, 5, 7 }, // 01111110 + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 01111111 + { 1, 2, 3, 4, 5, 6, 7, 0 }, // 10000000 + { 0, 2, 3, 4, 5, 6, 7, 1 }, // 10000001 + { 2, 0, 3, 4, 5, 6, 7, 1 }, // 10000010 + { 0, 1, 3, 4, 5, 6, 7, 2 }, // 10000011 + { 2, 3, 0, 4, 5, 6, 7, 1 }, // 10000100 + { 0, 3, 1, 4, 5, 6, 7, 2 }, // 10000101 + { 3, 0, 1, 4, 5, 6, 7, 2 }, // 10000110 + { 0, 1, 2, 4, 5, 6, 7, 3 }, // 10000111 + { 2, 3, 4, 0, 5, 6, 7, 1 }, // 10001000 + { 0, 3, 4, 1, 5, 6, 7, 2 }, // 10001001 + { 3, 0, 4, 1, 5, 6, 7, 2 }, // 10001010 + { 0, 1, 4, 2, 5, 6, 7, 3 }, // 10001011 + { 3, 4, 0, 1, 5, 6, 7, 2 }, // 10001100 + { 0, 4, 1, 2, 5, 6, 7, 3 }, // 10001101 + { 4, 0, 1, 2, 5, 6, 7, 3 }, // 10001110 + { 0, 1, 2, 3, 5, 6, 7, 4 }, // 10001111 + { 2, 3, 4, 5, 0, 6, 7, 1 }, // 10010000 + { 0, 3, 4, 5, 1, 6, 7, 2 }, // 10010001 + { 3, 0, 4, 5, 1, 6, 7, 2 }, // 10010010 + { 0, 1, 4, 5, 2, 6, 7, 3 }, // 10010011 + { 3, 4, 0, 5, 1, 6, 7, 2 }, // 10010100 + { 0, 4, 1, 5, 2, 6, 7, 3 }, // 10010101 + { 4, 0, 1, 5, 2, 6, 7, 3 }, // 10010110 + { 0, 1, 2, 5, 3, 6, 7, 4 }, // 10010111 + { 3, 4, 5, 0, 1, 6, 7, 2 }, // 10011000 + { 0, 4, 5, 1, 2, 6, 7, 3 }, // 10011001 + { 4, 0, 5, 1, 2, 6, 7, 3 }, // 10011010 + { 0, 1, 5, 2, 3, 6, 7, 4 }, // 10011011 + { 4, 5, 0, 1, 2, 6, 7, 3 }, // 10011100 + { 0, 5, 1, 2, 3, 6, 7, 4 }, // 10011101 + { 5, 0, 1, 2, 3, 6, 7, 4 }, // 10011110 + { 0, 1, 2, 3, 4, 6, 7, 5 }, // 10011111 + { 2, 3, 4, 5, 6, 0, 7, 1 }, // 10100000 + { 0, 3, 4, 5, 6, 1, 7, 2 }, // 10100001 + { 3, 0, 4, 5, 6, 1, 7, 2 }, // 10100010 + { 0, 1, 4, 5, 6, 2, 7, 3 }, // 10100011 + { 3, 4, 0, 5, 6, 1, 7, 2 }, // 10100100 + { 0, 4, 1, 5, 6, 2, 7, 3 }, // 10100101 + { 4, 0, 1, 5, 6, 2, 7, 3 }, // 10100110 + { 0, 1, 2, 5, 6, 3, 7, 4 }, // 10100111 + { 3, 4, 5, 0, 6, 1, 7, 2 }, // 10101000 + { 0, 4, 5, 1, 6, 2, 7, 3 }, // 10101001 + { 4, 0, 5, 1, 6, 2, 7, 3 }, // 10101010 + { 0, 1, 5, 2, 6, 3, 7, 4 }, // 10101011 + { 4, 5, 0, 1, 6, 2, 7, 3 }, // 10101100 + { 0, 5, 1, 2, 6, 3, 7, 4 }, // 10101101 + { 5, 0, 1, 2, 6, 3, 7, 4 }, // 10101110 + { 0, 1, 2, 3, 6, 4, 7, 5 }, // 10101111 + { 3, 4, 5, 6, 0, 1, 7, 2 }, // 10110000 + { 0, 4, 5, 6, 1, 2, 7, 3 }, // 10110001 + { 4, 0, 5, 6, 1, 2, 7, 3 }, // 10110010 + { 0, 1, 5, 6, 2, 3, 7, 4 }, // 10110011 + { 4, 5, 0, 6, 1, 2, 7, 3 }, // 10110100 + { 0, 5, 1, 6, 2, 3, 7, 4 }, // 10110101 + { 5, 0, 1, 6, 2, 3, 7, 4 }, // 10110110 + { 0, 1, 2, 6, 3, 4, 7, 5 }, // 10110111 + { 4, 5, 6, 0, 1, 2, 7, 3 }, // 10111000 + { 0, 5, 6, 1, 2, 3, 7, 4 }, // 10111001 + { 5, 0, 6, 1, 2, 3, 7, 4 }, // 10111010 + { 0, 1, 6, 2, 3, 4, 7, 5 }, // 10111011 + { 5, 6, 0, 1, 2, 3, 7, 4 }, // 10111100 + { 0, 6, 1, 2, 3, 4, 7, 5 }, // 10111101 + { 6, 0, 1, 2, 3, 4, 7, 5 }, // 10111110 + { 0, 1, 2, 3, 4, 5, 7, 6 }, // 10111111 + { 2, 3, 4, 5, 6, 7, 0, 1 }, // 11000000 + { 0, 3, 4, 5, 6, 7, 1, 2 }, // 11000001 + { 3, 0, 4, 5, 6, 7, 1, 2 }, // 11000010 + { 0, 1, 4, 5, 6, 7, 2, 3 }, // 11000011 + { 3, 4, 0, 5, 6, 7, 1, 2 }, // 11000100 + { 0, 4, 1, 5, 6, 7, 2, 3 }, // 11000101 + { 4, 0, 1, 5, 6, 7, 2, 3 }, // 11000110 + { 0, 1, 2, 5, 6, 7, 3, 4 }, // 11000111 + { 3, 4, 5, 0, 6, 7, 1, 2 }, // 11001000 + { 0, 4, 5, 1, 6, 7, 2, 3 }, // 11001001 + { 4, 0, 5, 1, 6, 7, 2, 3 }, // 11001010 + { 0, 1, 5, 2, 6, 7, 3, 4 }, // 11001011 + { 4, 5, 0, 1, 6, 7, 2, 3 }, // 11001100 + { 0, 5, 1, 2, 6, 7, 3, 4 }, // 11001101 + { 5, 0, 1, 2, 6, 7, 3, 4 }, // 11001110 + { 0, 1, 2, 3, 6, 7, 4, 5 }, // 11001111 + { 3, 4, 5, 6, 0, 7, 1, 2 }, // 11010000 + { 0, 4, 5, 6, 1, 7, 2, 3 }, // 11010001 + { 4, 0, 5, 6, 1, 7, 2, 3 }, // 11010010 + { 0, 1, 5, 6, 2, 7, 3, 4 }, // 11010011 + { 4, 5, 0, 6, 1, 7, 2, 3 }, // 11010100 + { 0, 5, 1, 6, 2, 7, 3, 4 }, // 11010101 + { 5, 0, 1, 6, 2, 7, 3, 4 }, // 11010110 + { 0, 1, 2, 6, 3, 7, 4, 5 }, // 11010111 + { 4, 5, 6, 0, 1, 7, 2, 3 }, // 11011000 + { 0, 5, 6, 1, 2, 7, 3, 4 }, // 11011001 + { 5, 0, 6, 1, 2, 7, 3, 4 }, // 11011010 + { 0, 1, 6, 2, 3, 7, 4, 5 }, // 11011011 + { 5, 6, 0, 1, 2, 7, 3, 4 }, // 11011100 + { 0, 6, 1, 2, 3, 7, 4, 5 }, // 11011101 + { 6, 0, 1, 2, 3, 7, 4, 5 }, // 11011110 + { 0, 1, 2, 3, 4, 7, 5, 6 }, // 11011111 + { 3, 4, 5, 6, 7, 0, 1, 2 }, // 11100000 + { 0, 4, 5, 6, 7, 1, 2, 3 }, // 11100001 + { 4, 0, 5, 6, 7, 1, 2, 3 }, // 11100010 + { 0, 1, 5, 6, 7, 2, 3, 4 }, // 11100011 + { 4, 5, 0, 6, 7, 1, 2, 3 }, // 11100100 + { 0, 5, 1, 6, 7, 2, 3, 4 }, // 11100101 + { 5, 0, 1, 6, 7, 2, 3, 4 }, // 11100110 + { 0, 1, 2, 6, 7, 3, 4, 5 }, // 11100111 + { 4, 5, 6, 0, 7, 1, 2, 3 }, // 11101000 + { 0, 5, 6, 1, 7, 2, 3, 4 }, // 11101001 + { 5, 0, 6, 1, 7, 2, 3, 4 }, // 11101010 + { 0, 1, 6, 2, 7, 3, 4, 5 }, // 11101011 + { 5, 6, 0, 1, 7, 2, 3, 4 }, // 11101100 + { 0, 6, 1, 2, 7, 3, 4, 5 }, // 11101101 + { 6, 0, 1, 2, 7, 3, 4, 5 }, // 11101110 + { 0, 1, 2, 3, 7, 4, 5, 6 }, // 11101111 + { 4, 5, 6, 7, 0, 1, 2, 3 }, // 11110000 + { 0, 5, 6, 7, 1, 2, 3, 4 }, // 11110001 + { 5, 0, 6, 7, 1, 2, 3, 4 }, // 11110010 + { 0, 1, 6, 7, 2, 3, 4, 5 }, // 11110011 + { 5, 6, 0, 7, 1, 2, 3, 4 }, // 11110100 + { 0, 6, 1, 7, 2, 3, 4, 5 }, // 11110101 + { 6, 0, 1, 7, 2, 3, 4, 5 }, // 11110110 + { 0, 1, 2, 7, 3, 4, 5, 6 }, // 11110111 + { 5, 6, 7, 0, 1, 2, 3, 4 }, // 11111000 + { 0, 6, 7, 1, 2, 3, 4, 5 }, // 11111001 + { 6, 0, 7, 1, 2, 3, 4, 5 }, // 11111010 + { 0, 1, 7, 2, 3, 4, 5, 6 }, // 11111011 + { 6, 7, 0, 1, 2, 3, 4, 5 }, // 11111100 + { 0, 7, 1, 2, 3, 4, 5, 6 }, // 11111101 + { 7, 0, 1, 2, 3, 4, 5, 6 }, // 11111110 + { 0, 1, 2, 3, 4, 5, 6, 7 } // 11111111 + }; + + assert( uPhase > 0 && uPhase < (unsigned)(1 << nVars) ); + + // the same function + if ( Cases[uPhase] == 0 ) + { + int i; + for ( i = 0; i < nWords; i++ ) + puTruthR[i] = puTruth[i]; + return; + } + + // an elementary variable + if ( Cases[uPhase] > 0 ) + { + int i; + for ( i = 0; i < nWords; i++ ) + puTruthR[i] = uTruths[Cases[uPhase]][i]; + return; + } + + // truth table takes one word + if ( nWords == 1 ) + { + int i, k, nMints, iRes; + char * pPerm = Perms[uPhase]; + puTruthR[0] = 0; + nMints = (1 << nVars); + for ( i = 0; i < nMints; i++ ) + if ( puTruth[0] & (1 << i) ) + { + for ( iRes = 0, k = 0; k < nVars; k++ ) + if ( i & (1 << pPerm[k]) ) + iRes |= (1 << k); + puTruthR[0] |= (1 << iRes); + } + return; + } + else if ( nWords == 2 ) + { + int i, k, iRes; + char * pPerm = Perms[uPhase]; + puTruthR[0] = puTruthR[1] = 0; + for ( i = 0; i < 32; i++ ) + { + if ( puTruth[0] & (1 << i) ) + { + for ( iRes = 0, k = 0; k < 6; k++ ) + if ( i & (1 << pPerm[k]) ) + iRes |= (1 << k); + if ( iRes < 32 ) + puTruthR[0] |= (1 << iRes); + else + puTruthR[1] |= (1 << (iRes-32)); + } + } + for ( ; i < 64; i++ ) + { + if ( puTruth[1] & (1 << (i-32)) ) + { + for ( iRes = 0, k = 0; k < 6; k++ ) + if ( i & (1 << pPerm[k]) ) + iRes |= (1 << k); + if ( iRes < 32 ) + puTruthR[0] |= (1 << iRes); + else + puTruthR[1] |= (1 << (iRes-32)); + } + } + } + // truth table takes more than one word + else + { + int i, k, nMints, iRes; + char * pPerm = Perms[uPhase]; + for ( i = 0; i < nWords; i++ ) + puTruthR[i] = 0; + nMints = (1 << nVars); + for ( i = 0; i < nMints; i++ ) + if ( puTruth[i>>5] & (1 << (i&31)) ) + { + for ( iRes = 0, k = 0; k < 5; k++ ) + if ( i & (1 << pPerm[k]) ) + iRes |= (1 << k); + puTruthR[iRes>>5] |= (1 << (iRes&31)); + } + } +} + +/**Function************************************************************* + + Synopsis [Allocated lookup table for truth table permutation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned short ** Extra_TruthPerm43() +{ + unsigned short ** pTable; + unsigned uTruth; + int i, k; + pTable = (unsigned short **)Extra_ArrayAlloc( 256, 16, 2 ); + for ( i = 0; i < 256; i++ ) + { + uTruth = (i << 8) | i; + for ( k = 0; k < 16; k++ ) + pTable[i][k] = Extra_TruthPerm4One( uTruth, k ); + } + return pTable; +} + +/**Function************************************************************* + + Synopsis [Allocated lookup table for truth table permutation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned ** Extra_TruthPerm53() +{ + unsigned ** pTable; + unsigned uTruth; + int i, k; + pTable = (unsigned **)Extra_ArrayAlloc( 256, 32, 4 ); + for ( i = 0; i < 256; i++ ) + { + uTruth = (i << 24) | (i << 16) | (i << 8) | i; + for ( k = 0; k < 32; k++ ) + pTable[i][k] = Extra_TruthPerm5One( uTruth, k ); + } + return pTable; +} + +/**Function************************************************************* + + Synopsis [Allocated lookup table for truth table permutation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned ** Extra_TruthPerm54() +{ + unsigned ** pTable; + unsigned uTruth; + int i; + pTable = (unsigned **)Extra_ArrayAlloc( 256*256, 4, 4 ); + for ( i = 0; i < 256*256; i++ ) + { + uTruth = (i << 16) | i; + pTable[i][0] = Extra_TruthPerm5One( uTruth, 31-8 ); + pTable[i][1] = Extra_TruthPerm5One( uTruth, 31-4 ); + pTable[i][2] = Extra_TruthPerm5One( uTruth, 31-2 ); + pTable[i][3] = Extra_TruthPerm5One( uTruth, 31-1 ); + } + return pTable; +} + +/**Function************************************************************* + + Synopsis [Allocated lookup table for truth table permutation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned ** Extra_TruthPerm63() +{ + unsigned ** pTable; + unsigned uTruth[2]; + int i, k; + pTable = (unsigned **)Extra_ArrayAlloc( 256, 64, 8 ); + for ( i = 0; i < 256; i++ ) + { + uTruth[0] = (i << 24) | (i << 16) | (i << 8) | i; + uTruth[1] = uTruth[0]; + for ( k = 0; k < 64; k++ ) + Extra_TruthPerm6One( uTruth, k, &pTable[i][k] ); + } + return pTable; +} + +/**Function************************************************************* + + Synopsis [Returns the pointer to the elementary truth tables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned ** Extra_Truths8() +{ + static unsigned uTruths[8][8] = { + { 0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA }, + { 0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC }, + { 0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0 }, + { 0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00 }, + { 0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000 }, + { 0x00000000,0xFFFFFFFF,0x00000000,0xFFFFFFFF,0x00000000,0xFFFFFFFF,0x00000000,0xFFFFFFFF }, + { 0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF,0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF }, + { 0x00000000,0x00000000,0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF } + }; + static unsigned * puResult[8] = { + uTruths[0], uTruths[1], uTruths[2], uTruths[3], uTruths[4], uTruths[5], uTruths[6], uTruths[7] + }; + return (unsigned **)puResult; +} + +/**Function************************************************************* + + Synopsis [Bubble-sorts components by scores in increasing order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_BubbleSort( int Order[], int Costs[], int nSize, int fIncreasing ) +{ + int i, Temp, fChanges; + assert( nSize < 1000 ); + for ( i = 0; i < nSize; i++ ) + Order[i] = i; + if ( fIncreasing ) + { + do { + fChanges = 0; + for ( i = 0; i < nSize - 1; i++ ) + { + if ( Costs[Order[i]] <= Costs[Order[i+1]] ) + continue; + Temp = Order[i]; + Order[i] = Order[i+1]; + Order[i+1] = Temp; + fChanges = 1; + } + } while ( fChanges ); + } + else + { + do { + fChanges = 0; + for ( i = 0; i < nSize - 1; i++ ) + { + if ( Costs[Order[i]] >= Costs[Order[i+1]] ) + continue; + Temp = Order[i]; + Order[i] = Order[i+1]; + Order[i+1] = Temp; + fChanges = 1; + } + } while ( fChanges ); + } +} + +/**Function************************************************************* + + Synopsis [Returns the smallest prime larger than the number.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned int Cudd_PrimeCopy( unsigned int p) +{ + int i,pn; + + p--; + do { + p++; + if (p&1) { + pn = 1; + i = 3; + while ((unsigned) (i * i) <= p) { + if (p % i == 0) { + pn = 0; + break; + } + i += 2; + } + } else { + pn = 0; + } + } while (!pn); + return(p); + +} /* end of Cudd_Prime */ + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of static Functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function************************************************************* + + Synopsis [Computes the permutation table for 8 variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_TruthExpandGeneratePermTable() +{ + int i, k, nOnes, Last1, First0; + int iOne, iZero; + + printf( "\nstatic char Cases[256] = {\n" ); + for ( i = 0; i < 256; i++ ) + { + nOnes = 0; + Last1 = First0 = -1; + for ( k = 0; k < 8; k++ ) + { + if ( i & (1 << k) ) + { + nOnes++; + Last1 = k; + } + else if ( First0 == -1 ) + First0 = k; + } + if ( Last1 + 1 == First0 || i == 255 ) + printf( " %d%s", 0, (i==255? " ":",") ); + else if ( nOnes == 1 ) + printf( " %d,", Last1 ); + else + printf( " -%d,", 1 ); + printf( " // " ); + Extra_PrintBinary( stdout, (unsigned*)&i, 8 ); + printf( "\n" ); + } + printf( "};\n" ); + + printf( "\nstatic char Perms[256][8] = {\n" ); + for ( i = 0; i < 256; i++ ) + { + printf( " {" ); + nOnes = 0; + for ( k = 0; k < 8; k++ ) + if ( i & (1 << k) ) + nOnes++; + iOne = 0; + iZero = nOnes; + for ( k = 0; k < 8; k++ ) + if ( i & (1 << k) ) + printf( "%s %d", (k==0? "":","), iOne++ ); + else + printf( "%s %d", (k==0? "":","), iZero++ ); + assert( iOne + iZero == 8 ); + printf( " }%s // ", (i==255? " ":",") ); + Extra_PrintBinary( stdout, (unsigned*)&i, 8 ); + printf( "\n" ); + } + printf( "};\n" ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/extra/extraUtilProgress.c b/abc_with_bb_support/src/misc/extra/extraUtilProgress.c new file mode 100644 index 000000000..d74e050f5 --- /dev/null +++ b/abc_with_bb_support/src/misc/extra/extraUtilProgress.c @@ -0,0 +1,176 @@ +/**CFile**************************************************************** + + FileName [extraUtilProgress.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [extra] + + Synopsis [Progress bar.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: extraUtilProgress.c,v 1.0 2003/02/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include +#include "extra.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +struct ProgressBarStruct +{ + int nItemsNext; // the number of items for the next update of the progress bar + int nItemsTotal; // the total number of items + int posTotal; // the total number of positions + int posCur; // the current position + FILE * pFile; // the output stream +}; + +static void Extra_ProgressBarShow( ProgressBar * p, char * pString ); +static void Extra_ProgressBarClean( ProgressBar * p ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the progress bar.] + + Description [The first parameter is the output stream (pFile), where + the progress is printed. The current printing position should be the + first one on the given line. The second parameters is the total + number of items that correspond to 100% position of the progress bar.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +ProgressBar * Extra_ProgressBarStart( FILE * pFile, int nItemsTotal ) +{ + ProgressBar * p; + extern int Abc_FrameShowProgress( void * p ); + extern void * Abc_FrameGetGlobalFrame(); + + if ( !Abc_FrameShowProgress(Abc_FrameGetGlobalFrame()) ) return NULL; + p = ALLOC( ProgressBar, 1 ); + memset( p, 0, sizeof(ProgressBar) ); + p->pFile = pFile; + p->nItemsTotal = nItemsTotal; + p->posTotal = 78; + p->posCur = 1; + p->nItemsNext = (int)((7.0+p->posCur)*p->nItemsTotal/p->posTotal); + Extra_ProgressBarShow( p, NULL ); + return p; +} + +/**Function************************************************************* + + Synopsis [Updates the progress bar.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_ProgressBarUpdate_int( ProgressBar * p, int nItemsCur, char * pString ) +{ + if ( p == NULL ) return; + if ( nItemsCur < p->nItemsNext ) + return; + if ( nItemsCur >= p->nItemsTotal ) + { + p->posCur = 78; + p->nItemsNext = 0x7FFFFFFF; + } + else + { + p->posCur += 7; + p->nItemsNext = (int)((7.0+p->posCur)*p->nItemsTotal/p->posTotal); + } + Extra_ProgressBarShow( p, pString ); +} + + +/**Function************************************************************* + + Synopsis [Stops the progress bar.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_ProgressBarStop( ProgressBar * p ) +{ + if ( p == NULL ) return; + Extra_ProgressBarClean( p ); + FREE( p ); +} + +/**Function************************************************************* + + Synopsis [Prints the progress bar of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_ProgressBarShow( ProgressBar * p, char * pString ) +{ + int i; + if ( p == NULL ) return; + if ( pString ) + fprintf( p->pFile, "%s ", pString ); + for ( i = (pString? strlen(pString) + 1 : 0); i < p->posCur; i++ ) + fprintf( p->pFile, "-" ); + if ( i == p->posCur ) + fprintf( p->pFile, ">" ); + for ( i++ ; i <= p->posTotal; i++ ) + fprintf( p->pFile, " " ); + fprintf( p->pFile, "\r" ); + fflush( stdout ); +} + +/**Function************************************************************* + + Synopsis [Cleans the progress bar before quitting.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_ProgressBarClean( ProgressBar * p ) +{ + int i; + if ( p == NULL ) return; + for ( i = 0; i <= p->posTotal; i++ ) + fprintf( p->pFile, " " ); + fprintf( p->pFile, "\r" ); + fflush( stdout ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/extra/extraUtilReader.c b/abc_with_bb_support/src/misc/extra/extraUtilReader.c new file mode 100644 index 000000000..4baffb983 --- /dev/null +++ b/abc_with_bb_support/src/misc/extra/extraUtilReader.c @@ -0,0 +1,383 @@ +/**CFile**************************************************************** + + FileName [extraUtilReader.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [extra] + + Synopsis [File reading utilities.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: extraUtilReader.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include +#include "extra.h" +#include "vec.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define EXTRA_BUFFER_SIZE 4*1048576 // 1M - size of the data chunk stored in memory +#define EXTRA_OFFSET_SIZE 4096 // 4K - load new data when less than this is left + +#define EXTRA_MINIMUM(a,b) (((a) < (b))? (a) : (b)) + +struct Extra_FileReader_t_ +{ + // the input file + char * pFileName; // the input file name + FILE * pFile; // the input file pointer + int nFileSize; // the total number of bytes in the file + int nFileRead; // the number of bytes currently read from file + // info about processing different types of input chars + char pCharMap[256]; // the character map + // temporary storage for data + char * pBuffer; // the buffer + int nBufferSize; // the size of the buffer + char * pBufferCur; // the current reading position + char * pBufferEnd; // the first position not used by currently loaded data + char * pBufferStop; // the position where loading new data will be done + // tokens given to the user + Vec_Ptr_t * vTokens; // the vector of tokens returned to the user + Vec_Int_t * vLines; // the vector of line numbers for each token + int nLineCounter; // the counter of lines processed + // status of the parser + int fStop; // this flag goes high when the end of file is reached +}; + +// character types +typedef enum { + EXTRA_CHAR_COMMENT, // a character that begins the comment + EXTRA_CHAR_NORMAL, // a regular character + EXTRA_CHAR_STOP, // a character that delimits a series of tokens + EXTRA_CHAR_CLEAN // a character that should be cleaned +} Extra_CharType_t; + +// the static functions +static void * Extra_FileReaderGetTokens_int( Extra_FileReader_t * p ); +static void Extra_FileReaderReload( Extra_FileReader_t * p ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the file reader.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Extra_FileReader_t * Extra_FileReaderAlloc( char * pFileName, + char * pCharsComment, char * pCharsStop, char * pCharsClean ) +{ + Extra_FileReader_t * p; + FILE * pFile; + char * pChar; + int nCharsToRead; + // check if the file can be opened + pFile = fopen( pFileName, "rb" ); + if ( pFile == NULL ) + { + printf( "Extra_FileReaderAlloc(): Cannot open input file \"%s\".\n", pFileName ); + return NULL; + } + // start the file reader + p = ALLOC( Extra_FileReader_t, 1 ); + memset( p, 0, sizeof(Extra_FileReader_t) ); + p->pFileName = pFileName; + p->pFile = pFile; + // set the character map + memset( p->pCharMap, EXTRA_CHAR_NORMAL, 256 ); + for ( pChar = pCharsComment; *pChar; pChar++ ) + p->pCharMap[(unsigned char)*pChar] = EXTRA_CHAR_COMMENT; + for ( pChar = pCharsStop; *pChar; pChar++ ) + p->pCharMap[(unsigned char)*pChar] = EXTRA_CHAR_STOP; + for ( pChar = pCharsClean; *pChar; pChar++ ) + p->pCharMap[(unsigned char)*pChar] = EXTRA_CHAR_CLEAN; + // get the file size, in bytes + fseek( pFile, 0, SEEK_END ); + p->nFileSize = ftell( pFile ); + rewind( pFile ); + // allocate the buffer + p->pBuffer = ALLOC( char, EXTRA_BUFFER_SIZE+1 ); + p->nBufferSize = EXTRA_BUFFER_SIZE; + p->pBufferCur = p->pBuffer; + // determine how many chars to read + nCharsToRead = EXTRA_MINIMUM(p->nFileSize, EXTRA_BUFFER_SIZE); + // load the first part into the buffer + fread( p->pBuffer, nCharsToRead, 1, p->pFile ); + p->nFileRead = nCharsToRead; + // set the ponters to the end and the stopping point + p->pBufferEnd = p->pBuffer + nCharsToRead; + p->pBufferStop = (p->nFileRead == p->nFileSize)? p->pBufferEnd : p->pBuffer + EXTRA_BUFFER_SIZE - EXTRA_OFFSET_SIZE; + // start the arrays + p->vTokens = Vec_PtrAlloc( 100 ); + p->vLines = Vec_IntAlloc( 100 ); + p->nLineCounter = 1; // 1-based line counting + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the file reader.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_FileReaderFree( Extra_FileReader_t * p ) +{ + if ( p->pFile ) + fclose( p->pFile ); + FREE( p->pBuffer ); + Vec_PtrFree( p->vTokens ); + Vec_IntFree( p->vLines ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Returns the file size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Extra_FileReaderGetFileName( Extra_FileReader_t * p ) +{ + return p->pFileName; +} + +/**Function************************************************************* + + Synopsis [Returns the file size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_FileReaderGetFileSize( Extra_FileReader_t * p ) +{ + return p->nFileSize; +} + +/**Function************************************************************* + + Synopsis [Returns the current reading position.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_FileReaderGetCurPosition( Extra_FileReader_t * p ) +{ + return p->nFileRead - (p->pBufferEnd - p->pBufferCur); +} + +/**Function************************************************************* + + Synopsis [Returns the line number for the given token.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_FileReaderGetLineNumber( Extra_FileReader_t * p, int iToken ) +{ + assert( iToken >= 0 && iToken < p->vTokens->nSize ); + return p->vLines->pArray[iToken]; +} + + +/**Function************************************************************* + + Synopsis [Returns the next set of tokens.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void * Extra_FileReaderGetTokens( Extra_FileReader_t * p ) +{ + Vec_Ptr_t * vTokens; + while ( vTokens = Extra_FileReaderGetTokens_int( p ) ) + if ( vTokens->nSize > 0 ) + break; + return vTokens; +} + +/**Function************************************************************* + + Synopsis [Returns the next set of tokens.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void * Extra_FileReaderGetTokens_int( Extra_FileReader_t * p ) +{ + char * pChar; + int fTokenStarted, MapValue; + if ( p->fStop ) + return NULL; + // reset the token info + p->vTokens->nSize = 0; + p->vLines->nSize = 0; + fTokenStarted = 0; + // check if the new data should to be loaded + if ( p->pBufferCur > p->pBufferStop ) + Extra_FileReaderReload( p ); + +// printf( "%d\n", p->pBufferEnd - p->pBufferCur ); + + // process the string starting from the current position + for ( pChar = p->pBufferCur; pChar < p->pBufferEnd; pChar++ ) + { + // count the lines + if ( *pChar == '\n' ) + p->nLineCounter++; + // switch depending on the character + MapValue = p->pCharMap[*pChar]; + +// printf( "Char value = %d. Map value = %d.\n", *pChar, MapValue ); + + + switch ( MapValue ) + { + case EXTRA_CHAR_COMMENT: + if ( *pChar != '/' || *(pChar+1) == '/' ) + { // dealing with the need to have // as a comment + // if the token was being written, stop it + if ( fTokenStarted ) + fTokenStarted = 0; + // eraze the comment till the end of line + while ( *pChar != '\n' ) + { + *pChar++ = 0; + if ( pChar == p->pBufferEnd ) + { // this failure is due to the fact the comment continued + // through EXTRA_OFFSET_SIZE chars till the end of the buffer + printf( "Extra_FileReader failed to parse the file \"%s\".\n", p->pFileName ); + return NULL; + } + } + pChar--; + break; + } + // otherwise it is a normal character + case EXTRA_CHAR_NORMAL: + if ( !fTokenStarted ) + { + Vec_PtrPush( p->vTokens, pChar ); + Vec_IntPush( p->vLines, p->nLineCounter ); + fTokenStarted = 1; + } + break; + case EXTRA_CHAR_STOP: + if ( fTokenStarted ) + fTokenStarted = 0; + *pChar = 0; + // prepare before leaving + p->pBufferCur = pChar + 1; + return p->vTokens; + case EXTRA_CHAR_CLEAN: + if ( fTokenStarted ) + fTokenStarted = 0; + *pChar = 0; + break; + default: + assert( 0 ); + } + } + // the file is finished or the last part continued + // through EXTRA_OFFSET_SIZE chars till the end of the buffer + if ( p->pBufferStop == p->pBufferEnd ) // end of file + { + *pChar = 0; + p->fStop = 1; + return p->vTokens; + } + printf( "Extra_FileReader failed to parse the file \"%s\".\n", p->pFileName ); +/* + { + int i; + for ( i = 0; i < p->vTokens->nSize; i++ ) + printf( "%s ", p->vTokens->pArray[i] ); + printf( "\n" ); + } +*/ + return NULL; +} + +/**Function************************************************************* + + Synopsis [Loads new data into the file reader.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_FileReaderReload( Extra_FileReader_t * p ) +{ + int nCharsUsed, nCharsToRead; + assert( !p->fStop ); + assert( p->pBufferCur > p->pBufferStop ); + assert( p->pBufferCur < p->pBufferEnd ); + // figure out how many chars are still not processed + nCharsUsed = p->pBufferEnd - p->pBufferCur; + // move the remaining data to the beginning of the buffer + memmove( p->pBuffer, p->pBufferCur, nCharsUsed ); + p->pBufferCur = p->pBuffer; + // determine how many chars we will read + nCharsToRead = EXTRA_MINIMUM( p->nBufferSize - nCharsUsed, p->nFileSize - p->nFileRead ); + // read the chars + fread( p->pBuffer + nCharsUsed, nCharsToRead, 1, p->pFile ); + p->nFileRead += nCharsToRead; + // set the ponters to the end and the stopping point + p->pBufferEnd = p->pBuffer + nCharsUsed + nCharsToRead; + p->pBufferStop = (p->nFileRead == p->nFileSize)? p->pBufferEnd : p->pBuffer + EXTRA_BUFFER_SIZE - EXTRA_OFFSET_SIZE; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/extra/extraUtilTruth.c b/abc_with_bb_support/src/misc/extra/extraUtilTruth.c new file mode 100644 index 000000000..e94dd7936 --- /dev/null +++ b/abc_with_bb_support/src/misc/extra/extraUtilTruth.c @@ -0,0 +1,1148 @@ +/**CFile**************************************************************** + + FileName [extraUtilMisc.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [extra] + + Synopsis [Various procedures for truth table manipulation.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: extraUtilMisc.c,v 1.0 2003/09/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "extra.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +static unsigned s_VarMasks[5][2] = { + { 0x33333333, 0xAAAAAAAA }, + { 0x55555555, 0xCCCCCCCC }, + { 0x0F0F0F0F, 0xF0F0F0F0 }, + { 0x00FF00FF, 0xFF00FF00 }, + { 0x0000FFFF, 0xFFFF0000 } +}; + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticEnd***************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function************************************************************* + + Synopsis [Derive elementary truth tables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned ** Extra_TruthElementary( int nVars ) +{ + unsigned ** pRes; + int i, k, nWords; + nWords = Extra_TruthWordNum(nVars); + pRes = (unsigned **)Extra_ArrayAlloc( nVars, nWords, 4 ); + for ( i = 0; i < nVars; i++ ) + { + if ( i < 5 ) + { + for ( k = 0; k < nWords; k++ ) + pRes[i][k] = s_VarMasks[i][1]; + } + else + { + for ( k = 0; k < nWords; k++ ) + if ( k & (1 << (i-5)) ) + pRes[i][k] = ~(unsigned)0; + else + pRes[i][k] = 0; + } + } + return pRes; +} + +/**Function************************************************************* + + Synopsis [Swaps two adjacent variables in the truth table.] + + Description [Swaps var number Start and var number Start+1 (0-based numbers). + The input truth table is pIn. The output truth table is pOut.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_TruthSwapAdjacentVars( unsigned * pOut, unsigned * pIn, int nVars, int iVar ) +{ + static unsigned PMasks[4][3] = { + { 0x99999999, 0x22222222, 0x44444444 }, + { 0xC3C3C3C3, 0x0C0C0C0C, 0x30303030 }, + { 0xF00FF00F, 0x00F000F0, 0x0F000F00 }, + { 0xFF0000FF, 0x0000FF00, 0x00FF0000 } + }; + int nWords = Extra_TruthWordNum( nVars ); + int i, k, Step, Shift; + + assert( iVar < nVars - 1 ); + if ( iVar < 4 ) + { + Shift = (1 << iVar); + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pIn[i] & PMasks[iVar][0]) | ((pIn[i] & PMasks[iVar][1]) << Shift) | ((pIn[i] & PMasks[iVar][2]) >> Shift); + } + else if ( iVar > 4 ) + { + Step = (1 << (iVar - 5)); + for ( k = 0; k < nWords; k += 4*Step ) + { + for ( i = 0; i < Step; i++ ) + pOut[i] = pIn[i]; + for ( i = 0; i < Step; i++ ) + pOut[Step+i] = pIn[2*Step+i]; + for ( i = 0; i < Step; i++ ) + pOut[2*Step+i] = pIn[Step+i]; + for ( i = 0; i < Step; i++ ) + pOut[3*Step+i] = pIn[3*Step+i]; + pIn += 4*Step; + pOut += 4*Step; + } + } + else // if ( iVar == 4 ) + { + for ( i = 0; i < nWords; i += 2 ) + { + pOut[i] = (pIn[i] & 0x0000FFFF) | ((pIn[i+1] & 0x0000FFFF) << 16); + pOut[i+1] = (pIn[i+1] & 0xFFFF0000) | ((pIn[i] & 0xFFFF0000) >> 16); + } + } +} + +/**Function************************************************************* + + Synopsis [Swaps two adjacent variables in the truth table.] + + Description [Swaps var number Start and var number Start+1 (0-based numbers). + The input truth table is pIn. The output truth table is pOut.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_TruthSwapAdjacentVars2( unsigned * pIn, unsigned * pOut, int nVars, int Start ) +{ + int nWords = (nVars <= 5)? 1 : (1 << (nVars-5)); + int i, k, Step; + + assert( Start < nVars - 1 ); + switch ( Start ) + { + case 0: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pIn[i] & 0x99999999) | ((pIn[i] & 0x22222222) << 1) | ((pIn[i] & 0x44444444) >> 1); + return; + case 1: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pIn[i] & 0xC3C3C3C3) | ((pIn[i] & 0x0C0C0C0C) << 2) | ((pIn[i] & 0x30303030) >> 2); + return; + case 2: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pIn[i] & 0xF00FF00F) | ((pIn[i] & 0x00F000F0) << 4) | ((pIn[i] & 0x0F000F00) >> 4); + return; + case 3: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pIn[i] & 0xFF0000FF) | ((pIn[i] & 0x0000FF00) << 8) | ((pIn[i] & 0x00FF0000) >> 8); + return; + case 4: + for ( i = 0; i < nWords; i += 2 ) + { + pOut[i] = (pIn[i] & 0x0000FFFF) | ((pIn[i+1] & 0x0000FFFF) << 16); + pOut[i+1] = (pIn[i+1] & 0xFFFF0000) | ((pIn[i] & 0xFFFF0000) >> 16); + } + return; + default: + Step = (1 << (Start - 5)); + for ( k = 0; k < nWords; k += 4*Step ) + { + for ( i = 0; i < Step; i++ ) + pOut[i] = pIn[i]; + for ( i = 0; i < Step; i++ ) + pOut[Step+i] = pIn[2*Step+i]; + for ( i = 0; i < Step; i++ ) + pOut[2*Step+i] = pIn[Step+i]; + for ( i = 0; i < Step; i++ ) + pOut[3*Step+i] = pIn[3*Step+i]; + pIn += 4*Step; + pOut += 4*Step; + } + return; + } +} + +/**Function************************************************************* + + Synopsis [Expands the truth table according to the phase.] + + Description [The input and output truth tables are in pIn/pOut. The current number + of variables is nVars. The total number of variables in nVarsAll. The last argument + (Phase) contains shows where the variables should go.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_TruthStretch( unsigned * pOut, unsigned * pIn, int nVars, int nVarsAll, unsigned Phase ) +{ + unsigned * pTemp; + int i, k, Var = nVars - 1, Counter = 0; + for ( i = nVarsAll - 1; i >= 0; i-- ) + if ( Phase & (1 << i) ) + { + for ( k = Var; k < i; k++ ) + { + Extra_TruthSwapAdjacentVars( pOut, pIn, nVarsAll, k ); + pTemp = pIn; pIn = pOut; pOut = pTemp; + Counter++; + } + Var--; + } + assert( Var == -1 ); + // swap if it was moved an even number of times + if ( !(Counter & 1) ) + Extra_TruthCopy( pOut, pIn, nVarsAll ); +} + +/**Function************************************************************* + + Synopsis [Shrinks the truth table according to the phase.] + + Description [The input and output truth tables are in pIn/pOut. The current number + of variables is nVars. The total number of variables in nVarsAll. The last argument + (Phase) contains shows what variables should remain.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_TruthShrink( unsigned * pOut, unsigned * pIn, int nVars, int nVarsAll, unsigned Phase ) +{ + unsigned * pTemp; + int i, k, Var = 0, Counter = 0; + for ( i = 0; i < nVarsAll; i++ ) + if ( Phase & (1 << i) ) + { + for ( k = i-1; k >= Var; k-- ) + { + Extra_TruthSwapAdjacentVars( pOut, pIn, nVarsAll, k ); + pTemp = pIn; pIn = pOut; pOut = pTemp; + Counter++; + } + Var++; + } + assert( Var == nVars ); + // swap if it was moved an even number of times + if ( !(Counter & 1) ) + Extra_TruthCopy( pOut, pIn, nVarsAll ); +} + + +/**Function************************************************************* + + Synopsis [Returns 1 if TT depends on the given variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_TruthVarInSupport( unsigned * pTruth, int nVars, int iVar ) +{ + int nWords = Extra_TruthWordNum( nVars ); + int i, k, Step; + + assert( iVar < nVars ); + switch ( iVar ) + { + case 0: + for ( i = 0; i < nWords; i++ ) + if ( (pTruth[i] & 0x55555555) != ((pTruth[i] & 0xAAAAAAAA) >> 1) ) + return 1; + return 0; + case 1: + for ( i = 0; i < nWords; i++ ) + if ( (pTruth[i] & 0x33333333) != ((pTruth[i] & 0xCCCCCCCC) >> 2) ) + return 1; + return 0; + case 2: + for ( i = 0; i < nWords; i++ ) + if ( (pTruth[i] & 0x0F0F0F0F) != ((pTruth[i] & 0xF0F0F0F0) >> 4) ) + return 1; + return 0; + case 3: + for ( i = 0; i < nWords; i++ ) + if ( (pTruth[i] & 0x00FF00FF) != ((pTruth[i] & 0xFF00FF00) >> 8) ) + return 1; + return 0; + case 4: + for ( i = 0; i < nWords; i++ ) + if ( (pTruth[i] & 0x0000FFFF) != ((pTruth[i] & 0xFFFF0000) >> 16) ) + return 1; + return 0; + default: + Step = (1 << (iVar - 5)); + for ( k = 0; k < nWords; k += 2*Step ) + { + for ( i = 0; i < Step; i++ ) + if ( pTruth[i] != pTruth[Step+i] ) + return 1; + pTruth += 2*Step; + } + return 0; + } +} + +/**Function************************************************************* + + Synopsis [Returns the number of support vars.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_TruthSupportSize( unsigned * pTruth, int nVars ) +{ + int i, Counter = 0; + for ( i = 0; i < nVars; i++ ) + Counter += Extra_TruthVarInSupport( pTruth, nVars, i ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Returns support of the function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_TruthSupport( unsigned * pTruth, int nVars ) +{ + int i, Support = 0; + for ( i = 0; i < nVars; i++ ) + if ( Extra_TruthVarInSupport( pTruth, nVars, i ) ) + Support |= (1 << i); + return Support; +} + + + +/**Function************************************************************* + + Synopsis [Computes positive cofactor of the function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_TruthCofactor1( unsigned * pTruth, int nVars, int iVar ) +{ + int nWords = Extra_TruthWordNum( nVars ); + int i, k, Step; + + assert( iVar < nVars ); + switch ( iVar ) + { + case 0: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = (pTruth[i] & 0xAAAAAAAA) | ((pTruth[i] & 0xAAAAAAAA) >> 1); + return; + case 1: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = (pTruth[i] & 0xCCCCCCCC) | ((pTruth[i] & 0xCCCCCCCC) >> 2); + return; + case 2: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = (pTruth[i] & 0xF0F0F0F0) | ((pTruth[i] & 0xF0F0F0F0) >> 4); + return; + case 3: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = (pTruth[i] & 0xFF00FF00) | ((pTruth[i] & 0xFF00FF00) >> 8); + return; + case 4: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = (pTruth[i] & 0xFFFF0000) | ((pTruth[i] & 0xFFFF0000) >> 16); + return; + default: + Step = (1 << (iVar - 5)); + for ( k = 0; k < nWords; k += 2*Step ) + { + for ( i = 0; i < Step; i++ ) + pTruth[i] = pTruth[Step+i]; + pTruth += 2*Step; + } + return; + } +} + +/**Function************************************************************* + + Synopsis [Computes negative cofactor of the function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_TruthCofactor0( unsigned * pTruth, int nVars, int iVar ) +{ + int nWords = Extra_TruthWordNum( nVars ); + int i, k, Step; + + assert( iVar < nVars ); + switch ( iVar ) + { + case 0: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = (pTruth[i] & 0x55555555) | ((pTruth[i] & 0x55555555) << 1); + return; + case 1: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = (pTruth[i] & 0x33333333) | ((pTruth[i] & 0x33333333) << 2); + return; + case 2: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = (pTruth[i] & 0x0F0F0F0F) | ((pTruth[i] & 0x0F0F0F0F) << 4); + return; + case 3: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = (pTruth[i] & 0x00FF00FF) | ((pTruth[i] & 0x00FF00FF) << 8); + return; + case 4: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = (pTruth[i] & 0x0000FFFF) | ((pTruth[i] & 0x0000FFFF) << 16); + return; + default: + Step = (1 << (iVar - 5)); + for ( k = 0; k < nWords; k += 2*Step ) + { + for ( i = 0; i < Step; i++ ) + pTruth[Step+i] = pTruth[i]; + pTruth += 2*Step; + } + return; + } +} + + +/**Function************************************************************* + + Synopsis [Existentially quantifies the variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_TruthExist( unsigned * pTruth, int nVars, int iVar ) +{ + int nWords = Extra_TruthWordNum( nVars ); + int i, k, Step; + + assert( iVar < nVars ); + switch ( iVar ) + { + case 0: + for ( i = 0; i < nWords; i++ ) + pTruth[i] |= ((pTruth[i] & 0xAAAAAAAA) >> 1) | ((pTruth[i] & 0x55555555) << 1); + return; + case 1: + for ( i = 0; i < nWords; i++ ) + pTruth[i] |= ((pTruth[i] & 0xCCCCCCCC) >> 2) | ((pTruth[i] & 0x33333333) << 2); + return; + case 2: + for ( i = 0; i < nWords; i++ ) + pTruth[i] |= ((pTruth[i] & 0xF0F0F0F0) >> 4) | ((pTruth[i] & 0x0F0F0F0F) << 4); + return; + case 3: + for ( i = 0; i < nWords; i++ ) + pTruth[i] |= ((pTruth[i] & 0xFF00FF00) >> 8) | ((pTruth[i] & 0x00FF00FF) << 8); + return; + case 4: + for ( i = 0; i < nWords; i++ ) + pTruth[i] |= ((pTruth[i] & 0xFFFF0000) >> 16) | ((pTruth[i] & 0x0000FFFF) << 16); + return; + default: + Step = (1 << (iVar - 5)); + for ( k = 0; k < nWords; k += 2*Step ) + { + for ( i = 0; i < Step; i++ ) + { + pTruth[i] |= pTruth[Step+i]; + pTruth[Step+i] = pTruth[i]; + } + pTruth += 2*Step; + } + return; + } +} + +/**Function************************************************************* + + Synopsis [Existentially quantifies the variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_TruthForall( unsigned * pTruth, int nVars, int iVar ) +{ + int nWords = Extra_TruthWordNum( nVars ); + int i, k, Step; + + assert( iVar < nVars ); + switch ( iVar ) + { + case 0: + for ( i = 0; i < nWords; i++ ) + pTruth[i] &= ((pTruth[i] & 0xAAAAAAAA) >> 1) | ((pTruth[i] & 0x55555555) << 1); + return; + case 1: + for ( i = 0; i < nWords; i++ ) + pTruth[i] &= ((pTruth[i] & 0xCCCCCCCC) >> 2) | ((pTruth[i] & 0x33333333) << 2); + return; + case 2: + for ( i = 0; i < nWords; i++ ) + pTruth[i] &= ((pTruth[i] & 0xF0F0F0F0) >> 4) | ((pTruth[i] & 0x0F0F0F0F) << 4); + return; + case 3: + for ( i = 0; i < nWords; i++ ) + pTruth[i] &= ((pTruth[i] & 0xFF00FF00) >> 8) | ((pTruth[i] & 0x00FF00FF) << 8); + return; + case 4: + for ( i = 0; i < nWords; i++ ) + pTruth[i] &= ((pTruth[i] & 0xFFFF0000) >> 16) | ((pTruth[i] & 0x0000FFFF) << 16); + return; + default: + Step = (1 << (iVar - 5)); + for ( k = 0; k < nWords; k += 2*Step ) + { + for ( i = 0; i < Step; i++ ) + { + pTruth[i] &= pTruth[Step+i]; + pTruth[Step+i] = pTruth[i]; + } + pTruth += 2*Step; + } + return; + } +} + + +/**Function************************************************************* + + Synopsis [Computes negative cofactor of the function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_TruthMux( unsigned * pOut, unsigned * pCof0, unsigned * pCof1, int nVars, int iVar ) +{ + int nWords = Extra_TruthWordNum( nVars ); + int i, k, Step; + + assert( iVar < nVars ); + switch ( iVar ) + { + case 0: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pCof0[i] & 0x55555555) | (pCof1[i] & 0xAAAAAAAA); + return; + case 1: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pCof0[i] & 0x33333333) | (pCof1[i] & 0xCCCCCCCC); + return; + case 2: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pCof0[i] & 0x0F0F0F0F) | (pCof1[i] & 0xF0F0F0F0); + return; + case 3: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pCof0[i] & 0x00FF00FF) | (pCof1[i] & 0xFF00FF00); + return; + case 4: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (pCof0[i] & 0x0000FFFF) | (pCof1[i] & 0xFFFF0000); + return; + default: + Step = (1 << (iVar - 5)); + for ( k = 0; k < nWords; k += 2*Step ) + { + for ( i = 0; i < Step; i++ ) + { + pOut[i] = pCof0[i]; + pOut[Step+i] = pCof1[Step+i]; + } + pOut += 2*Step; + } + return; + } +} + +/**Function************************************************************* + + Synopsis [Checks symmetry of two variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_TruthVarsSymm( unsigned * pTruth, int nVars, int iVar0, int iVar1 ) +{ + static unsigned uTemp0[16], uTemp1[16]; + assert( nVars <= 9 ); + // compute Cof01 + Extra_TruthCopy( uTemp0, pTruth, nVars ); + Extra_TruthCofactor0( uTemp0, nVars, iVar0 ); + Extra_TruthCofactor1( uTemp0, nVars, iVar1 ); + // compute Cof10 + Extra_TruthCopy( uTemp1, pTruth, nVars ); + Extra_TruthCofactor1( uTemp1, nVars, iVar0 ); + Extra_TruthCofactor0( uTemp1, nVars, iVar1 ); + // compare + return Extra_TruthIsEqual( uTemp0, uTemp1, nVars ); +} + +/**Function************************************************************* + + Synopsis [Checks antisymmetry of two variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_TruthVarsAntiSymm( unsigned * pTruth, int nVars, int iVar0, int iVar1 ) +{ + static unsigned uTemp0[16], uTemp1[16]; + assert( nVars <= 9 ); + // compute Cof00 + Extra_TruthCopy( uTemp0, pTruth, nVars ); + Extra_TruthCofactor0( uTemp0, nVars, iVar0 ); + Extra_TruthCofactor0( uTemp0, nVars, iVar1 ); + // compute Cof11 + Extra_TruthCopy( uTemp1, pTruth, nVars ); + Extra_TruthCofactor1( uTemp1, nVars, iVar0 ); + Extra_TruthCofactor1( uTemp1, nVars, iVar1 ); + // compare + return Extra_TruthIsEqual( uTemp0, uTemp1, nVars ); +} + +/**Function************************************************************* + + Synopsis [Changes phase of the function w.r.t. one variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_TruthChangePhase( unsigned * pTruth, int nVars, int iVar ) +{ + int nWords = Extra_TruthWordNum( nVars ); + int i, k, Step; + unsigned Temp; + + assert( iVar < nVars ); + switch ( iVar ) + { + case 0: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = ((pTruth[i] & 0x55555555) << 1) | ((pTruth[i] & 0xAAAAAAAA) >> 1); + return; + case 1: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = ((pTruth[i] & 0x33333333) << 2) | ((pTruth[i] & 0xCCCCCCCC) >> 2); + return; + case 2: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = ((pTruth[i] & 0x0F0F0F0F) << 4) | ((pTruth[i] & 0xF0F0F0F0) >> 4); + return; + case 3: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = ((pTruth[i] & 0x00FF00FF) << 8) | ((pTruth[i] & 0xFF00FF00) >> 8); + return; + case 4: + for ( i = 0; i < nWords; i++ ) + pTruth[i] = ((pTruth[i] & 0x0000FFFF) << 16) | ((pTruth[i] & 0xFFFF0000) >> 16); + return; + default: + Step = (1 << (iVar - 5)); + for ( k = 0; k < nWords; k += 2*Step ) + { + for ( i = 0; i < Step; i++ ) + { + Temp = pTruth[i]; + pTruth[i] = pTruth[Step+i]; + pTruth[Step+i] = Temp; + } + pTruth += 2*Step; + } + return; + } +} + +/**Function************************************************************* + + Synopsis [Computes minimum overlap in supports of cofactors.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_TruthMinCofSuppOverlap( unsigned * pTruth, int nVars, int * pVarMin ) +{ + static unsigned uCofactor[16]; + int i, ValueCur, ValueMin, VarMin; + unsigned uSupp0, uSupp1; + int nVars0, nVars1; + assert( nVars <= 9 ); + ValueMin = 32; + VarMin = -1; + for ( i = 0; i < nVars; i++ ) + { + // get negative cofactor + Extra_TruthCopy( uCofactor, pTruth, nVars ); + Extra_TruthCofactor0( uCofactor, nVars, i ); + uSupp0 = Extra_TruthSupport( uCofactor, nVars ); + nVars0 = Extra_WordCountOnes( uSupp0 ); +//Extra_PrintBinary( stdout, &uSupp0, 8 ); printf( "\n" ); + // get positive cofactor + Extra_TruthCopy( uCofactor, pTruth, nVars ); + Extra_TruthCofactor1( uCofactor, nVars, i ); + uSupp1 = Extra_TruthSupport( uCofactor, nVars ); + nVars1 = Extra_WordCountOnes( uSupp1 ); +//Extra_PrintBinary( stdout, &uSupp1, 8 ); printf( "\n" ); + // get the number of common vars + ValueCur = Extra_WordCountOnes( uSupp0 & uSupp1 ); + if ( ValueMin > ValueCur && nVars0 <= 5 && nVars1 <= 5 ) + { + ValueMin = ValueCur; + VarMin = i; + } + if ( ValueMin == 0 ) + break; + } + if ( pVarMin ) + *pVarMin = VarMin; + return ValueMin; +} + + +/**Function************************************************************* + + Synopsis [Counts the number of 1's in each cofactor.] + + Description [The resulting numbers are stored in the array of shorts, + whose length is 2*nVars. The number of 1's is counted in a different + space than the original function. For example, if the function depends + on k variables, the cofactors are assumed to depend on k-1 variables.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_TruthCountOnesInCofs( unsigned * pTruth, int nVars, short * pStore ) +{ + int nWords = Extra_TruthWordNum( nVars ); + int i, k, Counter; + memset( pStore, 0, sizeof(short) * 2 * nVars ); + if ( nVars <= 5 ) + { + if ( nVars > 0 ) + { + pStore[2*0+0] = Extra_WordCountOnes( pTruth[0] & 0x55555555 ); + pStore[2*0+1] = Extra_WordCountOnes( pTruth[0] & 0xAAAAAAAA ); + } + if ( nVars > 1 ) + { + pStore[2*1+0] = Extra_WordCountOnes( pTruth[0] & 0x33333333 ); + pStore[2*1+1] = Extra_WordCountOnes( pTruth[0] & 0xCCCCCCCC ); + } + if ( nVars > 2 ) + { + pStore[2*2+0] = Extra_WordCountOnes( pTruth[0] & 0x0F0F0F0F ); + pStore[2*2+1] = Extra_WordCountOnes( pTruth[0] & 0xF0F0F0F0 ); + } + if ( nVars > 3 ) + { + pStore[2*3+0] = Extra_WordCountOnes( pTruth[0] & 0x00FF00FF ); + pStore[2*3+1] = Extra_WordCountOnes( pTruth[0] & 0xFF00FF00 ); + } + if ( nVars > 4 ) + { + pStore[2*4+0] = Extra_WordCountOnes( pTruth[0] & 0x0000FFFF ); + pStore[2*4+1] = Extra_WordCountOnes( pTruth[0] & 0xFFFF0000 ); + } + return; + } + // nVars >= 6 + // count 1's for all other variables + for ( k = 0; k < nWords; k++ ) + { + Counter = Extra_WordCountOnes( pTruth[k] ); + for ( i = 5; i < nVars; i++ ) + if ( k & (1 << (i-5)) ) + pStore[2*i+1] += Counter; + else + pStore[2*i+0] += Counter; + } + // count 1's for the first five variables + for ( k = 0; k < nWords/2; k++ ) + { + pStore[2*0+0] += Extra_WordCountOnes( (pTruth[0] & 0x55555555) | ((pTruth[1] & 0x55555555) << 1) ); + pStore[2*0+1] += Extra_WordCountOnes( (pTruth[0] & 0xAAAAAAAA) | ((pTruth[1] & 0xAAAAAAAA) >> 1) ); + pStore[2*1+0] += Extra_WordCountOnes( (pTruth[0] & 0x33333333) | ((pTruth[1] & 0x33333333) << 2) ); + pStore[2*1+1] += Extra_WordCountOnes( (pTruth[0] & 0xCCCCCCCC) | ((pTruth[1] & 0xCCCCCCCC) >> 2) ); + pStore[2*2+0] += Extra_WordCountOnes( (pTruth[0] & 0x0F0F0F0F) | ((pTruth[1] & 0x0F0F0F0F) << 4) ); + pStore[2*2+1] += Extra_WordCountOnes( (pTruth[0] & 0xF0F0F0F0) | ((pTruth[1] & 0xF0F0F0F0) >> 4) ); + pStore[2*3+0] += Extra_WordCountOnes( (pTruth[0] & 0x00FF00FF) | ((pTruth[1] & 0x00FF00FF) << 8) ); + pStore[2*3+1] += Extra_WordCountOnes( (pTruth[0] & 0xFF00FF00) | ((pTruth[1] & 0xFF00FF00) >> 8) ); + pStore[2*4+0] += Extra_WordCountOnes( (pTruth[0] & 0x0000FFFF) | ((pTruth[1] & 0x0000FFFF) << 16) ); + pStore[2*4+1] += Extra_WordCountOnes( (pTruth[0] & 0xFFFF0000) | ((pTruth[1] & 0xFFFF0000) >> 16) ); + pTruth += 2; + } +} + + +/**Function************************************************************* + + Synopsis [Canonicize the truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Extra_TruthHash( unsigned * pIn, int nWords ) +{ + // The 1,024 smallest prime numbers used to compute the hash value + // http://www.math.utah.edu/~alfeld/math/primelist.html + static int HashPrimes[1024] = { 2, 3, 5, + 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, + 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, + 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, + 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, + 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, + 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, + 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, + 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, + 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, + 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, + 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, + 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, + 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, + 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, + 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, + 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, + 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, + 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, + 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, + 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 2131, 2137, + 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, + 2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, + 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, + 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, + 2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, + 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791, + 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, + 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011, 3019, 3023, + 3037, 3041, 3049, 3061, 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, + 3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271, + 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359, 3361, 3371, 3373, + 3389, 3391, 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, + 3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, + 3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, + 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823, 3833, + 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923, 3929, 3931, + 3943, 3947, 3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, + 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177, + 4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, + 4289, 4297, 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 4421, 4423, + 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519, 4523, 4547, + 4549, 4561, 4567, 4583, 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657, + 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, 4759, 4783, 4787, 4789, + 4793, 4799, 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, + 4933, 4937, 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, 5009, 5011, + 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, 5099, 5101, 5107, 5113, 5119, 5147, + 5153, 5167, 5171, 5179, 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, + 5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 5407, 5413, + 5417, 5419, 5431, 5437, 5441, 5443, 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, + 5519, 5521, 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639, 5641, 5647, + 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741, 5743, + 5749, 5779, 5783, 5791, 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857, + 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, 5953, 5981, 5987, 6007, + 6011, 6029, 6037, 6043, 6047, 6053, 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, + 6131, 6133, 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, 6229, 6247, + 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301, 6311, 6317, 6323, 6329, 6337, 6343, + 6353, 6359, 6361, 6367, 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473, + 6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571, 6577, 6581, 6599, 6607, + 6619, 6637, 6653, 6659, 6661, 6673, 6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, + 6737, 6761, 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, 6841, 6857, + 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917, 6947, 6949, 6959, 6961, 6967, 6971, + 6977, 6983, 6991, 6997, 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103, + 7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, 7211, 7213, 7219, 7229, + 7237, 7243, 7247, 7253, 7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, + 7393, 7411, 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, 7507, 7517, + 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561, 7573, 7577, 7583, 7589, 7591, 7603, + 7607, 7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723, + 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829, 7841, 7853, 7867, 7873, + 7877, 7879, 7883, 7901, 7907, 7919, 7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009, + 8011, 8017, 8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111, 8117, 8123, + 8147, 8161 }; + int i; + unsigned uHashKey; + assert( nWords <= 1024 ); + uHashKey = 0; + for ( i = 0; i < nWords; i++ ) + uHashKey ^= HashPrimes[i] * pIn[i]; + return uHashKey; +} + + +/**Function************************************************************* + + Synopsis [Canonicize the truth table.] + + Description [Returns the phase. ] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Extra_TruthSemiCanonicize( unsigned * pInOut, unsigned * pAux, int nVars, char * pCanonPerm, short * pStore ) +{ + unsigned * pIn = pInOut, * pOut = pAux, * pTemp; + int nWords = Extra_TruthWordNum( nVars ); + int i, Temp, fChange, Counter, nOnes;//, k, j, w, Limit; + unsigned uCanonPhase; + + // canonicize output + uCanonPhase = 0; + nOnes = Extra_TruthCountOnes(pIn, nVars); + if ( (nOnes > nWords * 16) || ((nOnes == nWords * 16) && (pIn[0] & 1)) ) + { + uCanonPhase |= (1 << nVars); + Extra_TruthNot( pIn, pIn, nVars ); + } + + // collect the minterm counts + Extra_TruthCountOnesInCofs( pIn, nVars, pStore ); + + // canonicize phase + for ( i = 0; i < nVars; i++ ) + { + if ( pStore[2*i+0] <= pStore[2*i+1] ) + continue; + uCanonPhase |= (1 << i); + Temp = pStore[2*i+0]; + pStore[2*i+0] = pStore[2*i+1]; + pStore[2*i+1] = Temp; + Extra_TruthChangePhase( pIn, nVars, i ); + } + +// Extra_PrintHexadecimal( stdout, pIn, nVars ); +// printf( "\n" ); + + // permute + Counter = 0; + do { + fChange = 0; + for ( i = 0; i < nVars-1; i++ ) + { + if ( pStore[2*i] <= pStore[2*(i+1)] ) + continue; + Counter++; + fChange = 1; + + Temp = pCanonPerm[i]; + pCanonPerm[i] = pCanonPerm[i+1]; + pCanonPerm[i+1] = Temp; + + Temp = pStore[2*i]; + pStore[2*i] = pStore[2*(i+1)]; + pStore[2*(i+1)] = Temp; + + Temp = pStore[2*i+1]; + pStore[2*i+1] = pStore[2*(i+1)+1]; + pStore[2*(i+1)+1] = Temp; + + Extra_TruthSwapAdjacentVars( pOut, pIn, nVars, i ); + pTemp = pIn; pIn = pOut; pOut = pTemp; + } + } while ( fChange ); + +/* + Extra_PrintBinary( stdout, &uCanonPhase, nVars+1 ); printf( " : " ); + for ( i = 0; i < nVars; i++ ) + printf( "%d=%d/%d ", pCanonPerm[i], pStore[2*i], pStore[2*i+1] ); + printf( " C = %d\n", Counter ); + Extra_PrintHexadecimal( stdout, pIn, nVars ); + printf( "\n" ); +*/ + +/* + // process symmetric variable groups + uSymms = 0; + for ( i = 0; i < nVars-1; i++ ) + { + if ( pStore[2*i] != pStore[2*(i+1)] ) // i and i+1 cannot be symmetric + continue; + if ( pStore[2*i] != pStore[2*i+1] ) + continue; + if ( Extra_TruthVarsSymm( pIn, nVars, i, i+1 ) ) + continue; + if ( Extra_TruthVarsAntiSymm( pIn, nVars, i, i+1 ) ) + Extra_TruthChangePhase( pIn, nVars, i+1 ); + } +*/ + +/* + // process symmetric variable groups + uSymms = 0; + for ( i = 0; i < nVars-1; i++ ) + { + if ( pStore[2*i] != pStore[2*(i+1)] ) // i and i+1 cannot be symmetric + continue; + // i and i+1 can be symmetric + // find the end of this group + for ( k = i+1; k < nVars; k++ ) + if ( pStore[2*i] != pStore[2*k] ) + break; + Limit = k; + assert( i < Limit-1 ); + // go through the variables in this group + for ( j = i + 1; j < Limit; j++ ) + { + // check symmetry + if ( Extra_TruthVarsSymm( pIn, nVars, i, j ) ) + { + uSymms |= (1 << j); + continue; + } + // they are phase-unknown + if ( pStore[2*i] == pStore[2*i+1] ) + { + if ( Extra_TruthVarsAntiSymm( pIn, nVars, i, j ) ) + { + Extra_TruthChangePhase( pIn, nVars, j ); + uCanonPhase ^= (1 << j); + uSymms |= (1 << j); + continue; + } + } + + // they are not symmetric - move j as far as it goes in the group + for ( k = j; k < Limit-1; k++ ) + { + Counter++; + + Temp = pCanonPerm[k]; + pCanonPerm[k] = pCanonPerm[k+1]; + pCanonPerm[k+1] = Temp; + + assert( pStore[2*k] == pStore[2*(k+1)] ); + Extra_TruthSwapAdjacentVars( pOut, pIn, nVars, k ); + pTemp = pIn; pIn = pOut; pOut = pTemp; + } + Limit--; + j--; + } + i = Limit - 1; + } +*/ + + // swap if it was moved an even number of times + if ( Counter & 1 ) + Extra_TruthCopy( pOut, pIn, nVars ); + return uCanonPhase; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/extra/extraUtilUtil.c b/abc_with_bb_support/src/misc/extra/extraUtilUtil.c new file mode 100644 index 000000000..5cd1a8002 --- /dev/null +++ b/abc_with_bb_support/src/misc/extra/extraUtilUtil.c @@ -0,0 +1,330 @@ +/**CFile**************************************************************** + + FileName [extraUtilUtil.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [extra] + + Synopsis [Old SIS utilities.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: extraUtilUtil.c,v 1.0 2003/02/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include +#include "extra.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define EXTRA_RLIMIT_DATA_DEFAULT 67108864 // assume 64MB by default + +/* File : getopt.c + * Author : Henry Spencer, University of Toronto + * Updated: 28 April 1984 + * + * Changes: (R Rudell) + * changed index() to strchr(); + * added getopt_reset() to reset the getopt argument parsing + * + * Purpose: get option letter from argv. + */ + +char * globalUtilOptarg; // Global argument pointer (util_optarg) +int globalUtilOptind = 0; // Global argv index (util_optind) + +static char *pScanStr; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [util_cpu_time()] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +long Extra_CpuTime() +{ + return clock(); +} + +/**Function************************************************************* + + Synopsis [getSoftDataLimit()] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_GetSoftDataLimit() +{ + return EXTRA_RLIMIT_DATA_DEFAULT; +} + +/**Function************************************************************* + + Synopsis [util_getopt_reset()] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_UtilGetoptReset() +{ + globalUtilOptarg = 0; + globalUtilOptind = 0; + pScanStr = 0; +} + +/**Function************************************************************* + + Synopsis [util_getopt()] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_UtilGetopt( int argc, char *argv[], char *optstring ) +{ + register int c; + register char *place; + + globalUtilOptarg = NULL; + + if (pScanStr == NULL || *pScanStr == '\0') { + if (globalUtilOptind == 0) globalUtilOptind++; + if (globalUtilOptind >= argc) return EOF; + place = argv[globalUtilOptind]; + if (place[0] != '-' || place[1] == '\0') return EOF; + globalUtilOptind++; + if (place[1] == '-' && place[2] == '\0') return EOF; + pScanStr = place+1; + } + + c = *pScanStr++; + place = strchr(optstring, c); + if (place == NULL || c == ':') { + (void) fprintf(stderr, "%s: unknown option %c\n", argv[0], c); + return '?'; + } + if (*++place == ':') { + if (*pScanStr != '\0') { + globalUtilOptarg = pScanStr; + pScanStr = NULL; + } else { + if (globalUtilOptind >= argc) { + (void) fprintf(stderr, "%s: %c requires an argument\n", + argv[0], c); + return '?'; + } + globalUtilOptarg = argv[globalUtilOptind]; + globalUtilOptind++; + } + } + return c; +} + +/**Function************************************************************* + + Synopsis [util_print_time()] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Extra_UtilPrintTime( long t ) +{ + static char s[40]; + + (void) sprintf(s, "%ld.%02ld sec", t/1000, (t%1000)/10); + return s; +} + + +/**Function************************************************************* + + Synopsis [Extra_UtilStrsav()] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Extra_UtilStrsav( char *s ) +{ + if(s == NULL) { /* added 7/95, for robustness */ + return s; + } + else { + return strcpy(ALLOC(char, strlen(s)+1), s); + } +} + +/**Function************************************************************* + + Synopsis [util_tilde_expand()] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Extra_UtilTildeExpand( char *fname ) +{ + return Extra_UtilStrsav( fname ); +} + +/**Function************************************************************* + + Synopsis [check_file()] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Extra_UtilCheckFile(char *filename, char *mode) +{ + FILE *fp; + int got_file; + + if (strcmp(mode, "x") == 0) { + mode = "r"; + } + fp = fopen(filename, mode); + got_file = (fp != 0); + if (fp != 0) { + (void) fclose(fp); + } + return got_file; +} + +/**Function************************************************************* + + Synopsis [util_file_search()] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Extra_UtilFileSearch(char *file, char *path, char *mode) +//char *file; // file we're looking for +//char *path; // search path, colon separated +//char *mode; // "r", "w", or "x" +{ + int quit; + char *buffer, *filename, *save_path, *cp; + + if (path == 0 || strcmp(path, "") == 0) { + path = "."; /* just look in the current directory */ + } + + save_path = path = Extra_UtilStrsav(path); + quit = 0; + do { + cp = strchr(path, ':'); + if (cp != 0) { + *cp = '\0'; + } else { + quit = 1; + } + + /* cons up the filename out of the path and file name */ + if (strcmp(path, ".") == 0) { + buffer = Extra_UtilStrsav(file); + } else { + buffer = ALLOC(char, strlen(path) + strlen(file) + 4); + (void) sprintf(buffer, "%s/%s", path, file); + } + filename = Extra_UtilTildeExpand(buffer); + FREE(buffer); + + /* see if we can access it */ + if (Extra_UtilCheckFile(filename, mode)) { + FREE(save_path); + return filename; + } + FREE(filename); + path = ++cp; + } while (! quit); + + FREE(save_path); + return 0; +} + +/**Function************************************************************* + + Synopsis [MMout_of_memory()] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +/* MMout_of_memory -- out of memory for lazy people, flush and exit */ +void Extra_UtilMMout_Of_Memory( long size ) +{ + (void) fflush(stdout); + (void) fprintf(stderr, "\nout of memory allocating %u bytes\n", + (unsigned) size); + assert( 0 ); + exit(1); +} + +/**Function************************************************************* + + Synopsis [MMoutOfMemory()] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void (*Extra_UtilMMoutOfMemory)() = Extra_UtilMMout_Of_Memory; + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/extra/module.make b/abc_with_bb_support/src/misc/extra/module.make new file mode 100644 index 000000000..b2c709505 --- /dev/null +++ b/abc_with_bb_support/src/misc/extra/module.make @@ -0,0 +1,15 @@ +SRC += src/misc/extra/extraBddAuto.c \ + src/misc/extra/extraBddCas.c \ + src/misc/extra/extraBddKmap.c \ + src/misc/extra/extraBddMisc.c \ + src/misc/extra/extraBddSymm.c \ + src/misc/extra/extraBddUnate.c \ + src/misc/extra/extraUtilBitMatrix.c \ + src/misc/extra/extraUtilCanon.c \ + src/misc/extra/extraUtilFile.c \ + src/misc/extra/extraUtilMemory.c \ + src/misc/extra/extraUtilMisc.c \ + src/misc/extra/extraUtilProgress.c \ + src/misc/extra/extraUtilReader.c \ + src/misc/extra/extraUtilTruth.c \ + src/misc/extra/extraUtilUtil.c diff --git a/abc_with_bb_support/src/misc/hash/hash.h b/abc_with_bb_support/src/misc/hash/hash.h new file mode 100644 index 000000000..286ef69c4 --- /dev/null +++ b/abc_with_bb_support/src/misc/hash/hash.h @@ -0,0 +1,65 @@ +/**CFile**************************************************************** + + FileName [hash.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Hash map.] + + Synopsis [External declarations.] + + Author [Aaron P. Hurst] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 16, 2005.] + + Revision [$Id: vec.h,v 1.00 2005/06/20 00:00:00 ahurst Exp $] + +***********************************************************************/ + +#ifndef __HASH_H__ +#define __HASH_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#ifdef _WIN32 +#define inline __inline // compatible with MS VS 6.0 +#endif + +#include "hashInt.h" +#include "hashFlt.h" +#include "hashPtr.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#ifndef ABS +#define ABS(a) ((a) < 0 ? -(a) : (a)) +#endif + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +int Hash_DefaultHashFunc(int key, int nBins) { + return ABS( ( (key+11)*(key)*7+3 ) % nBins ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + +#endif + diff --git a/abc_with_bb_support/src/misc/hash/hashFlt.h b/abc_with_bb_support/src/misc/hash/hashFlt.h new file mode 100644 index 000000000..b8fb31033 --- /dev/null +++ b/abc_with_bb_support/src/misc/hash/hashFlt.h @@ -0,0 +1,330 @@ +/**CFile**************************************************************** + + FileName [hashFlt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Hash maps.] + + Synopsis [Hash maps.] + + Author [Aaron P. Hurst] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 16, 2006.] + + Revision [$Id: vecInt.h,v 1.00 2005/06/20 00:00:00 ahurst Exp $] + +***********************************************************************/ + +#ifndef __HASH_FLT_H__ +#define __HASH_FLT_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include +#include "extra.h" + +extern int Hash_DefaultHashFunc(int key, int nBins); + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Hash_Flt_t_ Hash_Flt_t; +typedef struct Hash_Flt_Entry_t_ Hash_Flt_Entry_t; + +struct Hash_Flt_Entry_t_ +{ + int key; + float data; + struct Hash_Flt_Entry_t_ * pNext; +}; + +struct Hash_Flt_t_ +{ + int nSize; + int nBins; + int (* fHash)(int key, int nBins); + Hash_Flt_Entry_t ** pArray; +}; + + + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define Hash_FltForEachEntry( pHash, pEntry, bin) \ + for(bin=-1, pEntry=NULL; bin < pHash->nBins; (!pEntry)?(pEntry=pHash->pArray[++bin]):(pEntry=pEntry->pNext)) \ + if (pEntry) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates a hash map with the given number of bins.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Hash_Flt_t * Hash_FltAlloc( int nBins ) +{ + Hash_Flt_t * p; + int i; + assert(nBins > 0); + p = ALLOC( Hash_Flt_t, 1); + p->nBins = nBins; + p->fHash = Hash_DefaultHashFunc; + p->nSize = 0; + p->pArray = ALLOC( Hash_Flt_Entry_t *, nBins ); + for(i=0; ipArray[i] = NULL; + + return p; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if a key already exists.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Hash_FltExists( Hash_Flt_t *p, int key ) +{ + int bin; + Hash_Flt_Entry_t *pEntry, **pLast; + + // find the bin where this key would live + bin = (*(p->fHash))(key, p->nBins); + + // search for key + pLast = &(p->pArray[bin]); + pEntry = p->pArray[bin]; + while(pEntry) { + if (pEntry->key == key) { + return 1; + } + pLast = &(pEntry->pNext); + pEntry = pEntry->pNext; + } + + return 0; +} + +/**Function************************************************************* + + Synopsis [Finds or creates an entry with a key and writes value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Hash_FltWriteEntry( Hash_Flt_t *p, int key, float data ) +{ + int bin; + Hash_Flt_Entry_t *pEntry, **pLast; + + // find the bin where this key would live + bin = (*(p->fHash))(key, p->nBins); + + // search for key + pLast = &(p->pArray[bin]); + pEntry = p->pArray[bin]; + while(pEntry) { + if (pEntry->key == key) { + pEntry->data = data; + return; + } + pLast = &(pEntry->pNext); + pEntry = pEntry->pNext; + } + + // this key does not currently exist + // create a new entry and add to bin + p->nSize++; + (*pLast) = pEntry = ALLOC( Hash_Flt_Entry_t, 1 ); + pEntry->pNext = NULL; + pEntry->key = key; + pEntry->data = data; + + return; +} + + +/**Function************************************************************* + + Synopsis [Finds or creates an entry with a key.] + + Description [fCreate specifies whether new entries should be created.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline float Hash_FltEntry( Hash_Flt_t *p, int key, int fCreate ) +{ + int bin; + Hash_Flt_Entry_t *pEntry, **pLast; + + // find the bin where this key would live + bin = (*(p->fHash))(key, p->nBins); + + // search for key + pLast = &(p->pArray[bin]); + pEntry = p->pArray[bin]; + while(pEntry) { + if (pEntry->key == key) + return pEntry->data; + pLast = &(pEntry->pNext); + pEntry = pEntry->pNext; + } + + // this key does not currently exist + if (fCreate) { + // create a new entry and add to bin + p->nSize++; + (*pLast) = pEntry = ALLOC( Hash_Flt_Entry_t, 1 ); + pEntry->pNext = NULL; + pEntry->key = key; + pEntry->data = 0.0; + return pEntry->data; + } + + return 0.0; +} + + +/**Function************************************************************* + + Synopsis [Finds or creates an entry with a key and returns the pointer to it.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline float* Hash_FltEntryPtr( Hash_Flt_t *p, int key ) +{ + int bin; + Hash_Flt_Entry_t *pEntry, **pLast; + + // find the bin where this key would live + bin = (*(p->fHash))(key, p->nBins); + + // search for key + pLast = &(p->pArray[bin]); + pEntry = p->pArray[bin]; + while(pEntry) { + if (pEntry->key == key) + return &(pEntry->data); + pLast = &(pEntry->pNext); + pEntry = pEntry->pNext; + } + + // this key does not currently exist + // create a new entry and add to bin + p->nSize++; + (*pLast) = pEntry = ALLOC( Hash_Flt_Entry_t, 1 ); + pEntry->pNext = NULL; + pEntry->key = key; + pEntry->data = 0.0; + + return &(pEntry->data); +} + +/**Function************************************************************* + + Synopsis [Deletes an entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Hash_FltRemove( Hash_Flt_t *p, int key ) +{ + int bin; + Hash_Flt_Entry_t *pEntry, **pLast; + + // find the bin where this key would live + bin = (*(p->fHash))(key, p->nBins); + + // search for key + pLast = &(p->pArray[bin]); + pEntry = p->pArray[bin]; + while(pEntry) { + if (pEntry->key == key) { + p->nSize--; + *pLast = pEntry->pNext; + FREE( pEntry ); + return; + } + pLast = &(pEntry->pNext); + pEntry = pEntry->pNext; + } + + // could not find key + return; +} + +/**Function************************************************************* + + Synopsis [Frees the hash.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Hash_FltFree( Hash_Flt_t *p ) { + int bin; + Hash_Flt_Entry_t *pEntry; + + // free bins + for(bin = 0; bin < p->nBins; bin++) { + pEntry = p->pArray[bin]; + while(pEntry) { + pEntry = pEntry->pNext; + FREE( pEntry ); + } + } + + // free hash + FREE( p->pArray ); + FREE( p ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + +#endif diff --git a/abc_with_bb_support/src/misc/hash/hashInt.h b/abc_with_bb_support/src/misc/hash/hashInt.h new file mode 100644 index 000000000..0ba1cb32d --- /dev/null +++ b/abc_with_bb_support/src/misc/hash/hashInt.h @@ -0,0 +1,293 @@ +/**CFile**************************************************************** + + FileName [hashInt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Hash maps.] + + Synopsis [Hash maps.] + + Author [Aaron P. Hurst] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 16, 2006.] + + Revision [$Id: vecInt.h,v 1.00 2005/06/20 00:00:00 ahurst Exp $] + +***********************************************************************/ + +#ifndef __HASH_INT_H__ +#define __HASH_INT_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include +#include "extra.h" + +extern int Hash_DefaultHashFunc(int key, int nBins); + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Hash_Int_t_ Hash_Int_t; +typedef struct Hash_Int_Entry_t_ Hash_Int_Entry_t; + +struct Hash_Int_Entry_t_ +{ + int key; + int data; + struct Hash_Int_Entry_t_ * pNext; +}; + +struct Hash_Int_t_ +{ + int nSize; + int nBins; + int (* fHash)(int key, int nBins); + Hash_Int_Entry_t ** pArray; +}; + + + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define Hash_IntForEachEntry( pHash, pEntry, bin) \ + for(bin=-1, pEntry=NULL; bin < pHash->nBins; (!pEntry)?(pEntry=pHash->pArray[++bin]):(pEntry=pEntry->pNext)) \ + if (pEntry) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates a hash map with the given number of bins.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Hash_Int_t * Hash_IntAlloc( int nBins ) +{ + Hash_Int_t * p; + int i; + assert(nBins > 0); + p = ALLOC( Hash_Int_t, 1); + p->nBins = nBins; + p->fHash = Hash_DefaultHashFunc; + p->nSize = 0; + p->pArray = ALLOC( Hash_Int_Entry_t *, nBins ); + for(i=0; ipArray[i] = NULL; + + return p; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if a key already exists.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Hash_IntExists( Hash_Int_t *p, int key) +{ + int bin; + Hash_Int_Entry_t *pEntry, **pLast; + + // find the bin where this key would live + bin = (*(p->fHash))(key, p->nBins); + + // search for key + pLast = &(p->pArray[bin]); + pEntry = p->pArray[bin]; + while(pEntry) { + if (pEntry->key == key) { + return 1; + } + pLast = &(pEntry->pNext); + pEntry = pEntry->pNext; + } + + return 0; +} + +/**Function************************************************************* + + Synopsis [Finds or creates an entry with a key and writes value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Hash_IntWriteEntry( Hash_Int_t *p, int key, int data ) +{ + int bin; + Hash_Int_Entry_t *pEntry, **pLast; + + // find the bin where this key would live + bin = (*(p->fHash))(key, p->nBins); + + // search for key + pLast = &(p->pArray[bin]); + pEntry = p->pArray[bin]; + while(pEntry) { + if (pEntry->key == key) { + pEntry->data = data; + return; + } + pLast = &(pEntry->pNext); + pEntry = pEntry->pNext; + } + + // this key does not currently exist + // create a new entry and add to bin + p->nSize++; + (*pLast) = pEntry = ALLOC( Hash_Int_Entry_t, 1 ); + pEntry->pNext = NULL; + pEntry->key = key; + pEntry->data = data; + + return; +} + + +/**Function************************************************************* + + Synopsis [Finds or creates an entry with a key.] + + Description [fCreate specifies whether new entries will be created.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Hash_IntEntry( Hash_Int_t *p, int key, int fCreate ) +{ + int bin; + Hash_Int_Entry_t *pEntry, **pLast; + + // find the bin where this key would live + bin = (*(p->fHash))(key, p->nBins); + + // search for key + pLast = &(p->pArray[bin]); + pEntry = p->pArray[bin]; + while(pEntry) { + if (pEntry->key == key) + return pEntry->data; + pLast = &(pEntry->pNext); + pEntry = pEntry->pNext; + } + + // this key does not currently exist + if (fCreate) { + // create a new entry and add to bin + p->nSize++; + (*pLast) = pEntry = ALLOC( Hash_Int_Entry_t, 1 ); + pEntry->pNext = NULL; + pEntry->key = key; + pEntry->data = 0; + return pEntry->data; + } + + return 0; +} + + +/**Function************************************************************* + + Synopsis [Finds or creates an entry with a key and returns the pointer to it.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int* Hash_IntEntryPtr( Hash_Int_t *p, int key ) +{ + int bin; + Hash_Int_Entry_t *pEntry, **pLast; + + // find the bin where this key would live + bin = (*(p->fHash))(key, p->nBins); + + // search for key + pLast = &(p->pArray[bin]); + pEntry = p->pArray[bin]; + while(pEntry) { + if (pEntry->key == key) + return &(pEntry->data); + pLast = &(pEntry->pNext); + pEntry = pEntry->pNext; + } + + // this key does not currently exist + // create a new entry and add to bin + p->nSize++; + (*pLast) = pEntry = ALLOC( Hash_Int_Entry_t, 1 ); + pEntry->pNext = NULL; + pEntry->key = key; + pEntry->data = 0; + + return &(pEntry->data); +} + +/**Function************************************************************* + + Synopsis [Frees the hash.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Hash_IntFree( Hash_Int_t *p ) { + int bin; + Hash_Int_Entry_t *pEntry; + + // free bins + for(bin = 0; bin < p->nBins; bin++) { + pEntry = p->pArray[bin]; + while(pEntry) { + pEntry = pEntry->pNext; + FREE( pEntry ); + } + } + + // free hash + FREE( p->pArray ); + FREE( p ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + +#endif diff --git a/abc_with_bb_support/src/misc/hash/hashPtr.h b/abc_with_bb_support/src/misc/hash/hashPtr.h new file mode 100644 index 000000000..c81900ac9 --- /dev/null +++ b/abc_with_bb_support/src/misc/hash/hashPtr.h @@ -0,0 +1,331 @@ +/**CFile**************************************************************** + + FileName [hashFlt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Hash maps.] + + Synopsis [Hash maps.] + + Author [Aaron P. Hurst] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - May 16, 2006.] + + Revision [$Id: vecInt.h,v 1.00 2005/06/20 00:00:00 ahurst Exp $] + +***********************************************************************/ + +#ifndef __HASH_PTR_H__ +#define __HASH_PTR_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include +#include "extra.h" + +extern int Hash_DefaultHashFunc(int key, int nBins); + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Hash_Ptr_t_ Hash_Ptr_t; +typedef struct Hash_Ptr_Entry_t_ Hash_Ptr_Entry_t; + +struct Hash_Ptr_Entry_t_ +{ + int key; + void * data; + struct Hash_Ptr_Entry_t_ * pNext; +}; + +struct Hash_Ptr_t_ +{ + int nSize; + int nBins; + int (* fHash)(int key, int nBins); + Hash_Ptr_Entry_t ** pArray; +}; + + + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define Hash_PtrForEachEntry( pHash, pEntry, bin ) \ + for(bin=-1, pEntry=NULL; bin < pHash->nBins; (!pEntry)?(pEntry=pHash->pArray[++bin]):(pEntry=pEntry->pNext)) \ + if (pEntry) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates a hash map with the given number of bins.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Hash_Ptr_t * Hash_PtrAlloc( int nBins ) +{ + Hash_Ptr_t * p; + int i; + assert(nBins > 0); + p = ALLOC( Hash_Ptr_t, 1); + p->nBins = nBins; + p->fHash = Hash_DefaultHashFunc; + p->nSize = 0; + p->pArray = ALLOC( Hash_Ptr_Entry_t *, nBins ); + for(i=0; ipArray[i] = NULL; + + return p; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if a key already exists.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Hash_PtrExists( Hash_Ptr_t *p, int key ) +{ + int bin; + Hash_Ptr_Entry_t *pEntry, **pLast; + + // find the bin where this key would live + bin = (*(p->fHash))(key, p->nBins); + + // search for key + pLast = &(p->pArray[bin]); + pEntry = p->pArray[bin]; + while(pEntry) { + if (pEntry->key == key) { + return 1; + } + pLast = &(pEntry->pNext); + pEntry = pEntry->pNext; + } + + return 0; +} + +/**Function************************************************************* + + Synopsis [Finds or creates an entry with a key and writes value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Hash_PtrWriteEntry( Hash_Ptr_t *p, int key, void * data ) +{ + int bin; + Hash_Ptr_Entry_t *pEntry, **pLast; + + // find the bin where this key would live + bin = (*(p->fHash))(key, p->nBins); + + // search for key + pLast = &(p->pArray[bin]); + pEntry = p->pArray[bin]; + while(pEntry) { + if (pEntry->key == key) { + pEntry->data = data; + return; + } + pLast = &(pEntry->pNext); + pEntry = pEntry->pNext; + } + + // this key does not currently exist + // create a new entry and add to bin + p->nSize++; + (*pLast) = pEntry = ALLOC( Hash_Ptr_Entry_t, 1 ); + pEntry->pNext = NULL; + pEntry->key = key; + pEntry->data = data; + + return; +} + + +/**Function************************************************************* + + Synopsis [Finds or creates an entry with a key.] + + Description [fCreate specifies whether a new entry should be created.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void * Hash_PtrEntry( Hash_Ptr_t *p, int key, int fCreate ) +{ + int bin; + Hash_Ptr_Entry_t *pEntry, **pLast; + + // find the bin where this key would live + bin = (*(p->fHash))(key, p->nBins); + + // search for key + pLast = &(p->pArray[bin]); + pEntry = p->pArray[bin]; + while(pEntry) { + if (pEntry->key == key) + return pEntry->data; + pLast = &(pEntry->pNext); + pEntry = pEntry->pNext; + } + + // this key does not currently exist + if (fCreate) { + // create a new entry and add to bin + p->nSize++; + (*pLast) = pEntry = ALLOC( Hash_Ptr_Entry_t, 1 ); + pEntry->pNext = NULL; + pEntry->key = key; + pEntry->data = NULL; + return pEntry->data; + } + + return NULL; +} + + +/**Function************************************************************* + + Synopsis [Finds or creates an entry with a key and returns the pointer to it.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void** Hash_PtrEntryPtr( Hash_Ptr_t *p, int key ) +{ + int bin; + Hash_Ptr_Entry_t *pEntry, **pLast; + + // find the bin where this key would live + bin = (*(p->fHash))(key, p->nBins); + + // search for key + pLast = &(p->pArray[bin]); + pEntry = p->pArray[bin]; + while(pEntry) { + if (pEntry->key == key) + return &(pEntry->data); + pLast = &(pEntry->pNext); + pEntry = pEntry->pNext; + } + + // this key does not currently exist + // create a new entry and add to bin + p->nSize++; + (*pLast) = pEntry = ALLOC( Hash_Ptr_Entry_t, 1 ); + pEntry->pNext = NULL; + pEntry->key = key; + pEntry->data = NULL; + + return &(pEntry->data); +} + +/**Function************************************************************* + + Synopsis [Deletes an entry.] + + Description [Returns data, if there was any.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void* Hash_PtrRemove( Hash_Ptr_t *p, int key ) +{ + int bin; + void * data; + Hash_Ptr_Entry_t *pEntry, **pLast; + + // find the bin where this key would live + bin = (*(p->fHash))(key, p->nBins); + + // search for key + pLast = &(p->pArray[bin]); + pEntry = p->pArray[bin]; + while(pEntry) { + if (pEntry->key == key) { + p->nSize--; + data = pEntry->data; + *pLast = pEntry->pNext; + return data; + } + pLast = &(pEntry->pNext); + pEntry = pEntry->pNext; + } + + // could not find key + return NULL; +} + +/**Function************************************************************* + + Synopsis [Frees the hash.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Hash_PtrFree( Hash_Ptr_t *p ) { + int bin; + Hash_Ptr_Entry_t *pEntry; + + // free bins + for(bin = 0; bin < p->nBins; bin++) { + pEntry = p->pArray[bin]; + while(pEntry) { + pEntry = pEntry->pNext; + FREE( pEntry ); + } + } + + // free hash + FREE( p->pArray ); + FREE( p ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + +#endif diff --git a/abc_with_bb_support/src/misc/hash/module.make b/abc_with_bb_support/src/misc/hash/module.make new file mode 100644 index 000000000..d6d908e73 --- /dev/null +++ b/abc_with_bb_support/src/misc/hash/module.make @@ -0,0 +1 @@ +SRC += diff --git a/abc_with_bb_support/src/misc/mvc/module.make b/abc_with_bb_support/src/misc/mvc/module.make new file mode 100644 index 000000000..cb19d24ca --- /dev/null +++ b/abc_with_bb_support/src/misc/mvc/module.make @@ -0,0 +1,16 @@ +SRC += src/misc/mvc/mvc.c \ + src/misc/mvc/mvcApi.c \ + src/misc/mvc/mvcCompare.c \ + src/misc/mvc/mvcContain.c \ + src/misc/mvc/mvcCover.c \ + src/misc/mvc/mvcCube.c \ + src/misc/mvc/mvcDivide.c \ + src/misc/mvc/mvcDivisor.c \ + src/misc/mvc/mvcList.c \ + src/misc/mvc/mvcLits.c \ + src/misc/mvc/mvcMan.c \ + src/misc/mvc/mvcOpAlg.c \ + src/misc/mvc/mvcOpBool.c \ + src/misc/mvc/mvcPrint.c \ + src/misc/mvc/mvcSort.c \ + src/misc/mvc/mvcUtils.c diff --git a/abc_with_bb_support/src/misc/mvc/mvc.c b/abc_with_bb_support/src/misc/mvc/mvc.c new file mode 100644 index 000000000..1f692ebe5 --- /dev/null +++ b/abc_with_bb_support/src/misc/mvc/mvc.c @@ -0,0 +1,46 @@ +/**CFile**************************************************************** + + FileName [mvc.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: mvc.c,v 1.3 2003/03/19 19:50:26 alanmi Exp $] + +***********************************************************************/ + +#include "mvc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/mvc/mvc.h b/abc_with_bb_support/src/misc/mvc/mvc.h new file mode 100644 index 000000000..ae931aa1d --- /dev/null +++ b/abc_with_bb_support/src/misc/mvc/mvc.h @@ -0,0 +1,732 @@ +/**CFile**************************************************************** + + FileName [mvc.h] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Data structure for MV cube/cover manipulation.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: mvc.h,v 1.10 2003/05/02 23:23:59 wjiang Exp $] + +***********************************************************************/ + +#ifndef __MVC_H__ +#define __MVC_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include +#include "extra.h" +#include "extra.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +// this is the only part of Mvc package, which should be modified +// when compiling the package for other platforms + +// these parameters can be computed but setting them manually makes it faster +#define BITS_PER_WORD 32 // sizeof(Mvc_CubeWord_t) * 8 +#define BITS_PER_WORD_MINUS 31 // the same minus 1 +#define BITS_PER_WORD_LOG 5 // log2(sizeof(Mvc_CubeWord_t) * 8) +#define BITS_DISJOINT ((Mvc_CubeWord_t)0x55555555) // the mask of the type "01010101" +#define BITS_FULL ((Mvc_CubeWord_t)0xffffffff) // the mask of the type "11111111" + +// uncomment this macro to switch to standard memory management +//#define USE_SYSTEM_MEMORY_MANAGEMENT + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// cube/list/cover/data +typedef unsigned int Mvc_CubeWord_t; +typedef struct MvcCubeStruct Mvc_Cube_t; +typedef struct MvcListStruct Mvc_List_t; +typedef struct MvcCoverStruct Mvc_Cover_t; +typedef struct MvcDataStruct Mvc_Data_t; +typedef struct MvcManagerStruct Mvc_Manager_t; + +// the cube data structure +struct MvcCubeStruct +{ + Mvc_Cube_t * pNext; // the next cube in the linked list + unsigned iLast : 8; // the index of the last word + unsigned nUnused : 6; // the number of unused bits in the last word + unsigned fPrime : 1; // marks the prime cube + unsigned fEssen : 1; // marks the essential cube + unsigned nOnes : 16; // the number of 1's in the bit data + Mvc_CubeWord_t pData[1]; // the first Mvc_CubeWord_t filled with bit data +}; + +// the single-linked list of cubes in the cover +struct MvcListStruct +{ + Mvc_Cube_t * pHead; // the first cube in the list + Mvc_Cube_t * pTail; // the last cube in the list + int nItems; // the number of cubes in the list +}; + +// the cover data structure +struct MvcCoverStruct +{ + char nWords; // the number of machine words + char nUnused; // the number of unused bits in the last word + short nBits; // the number of used data bits in the cube + Mvc_List_t lCubes; // the single-linked list of cubes + Mvc_Cube_t ** pCubes; // the array of cubes (for sorting) + int nCubesAlloc; // the size of allocated storage + int * pLits; // the counter of lit occurrances in cubes + Mvc_Cube_t * pMask; // the multipurpose mask + Mvc_Manager_t * pMem; // the memory manager +}; + +// data structure to store information about MV variables +struct MvcDataStruct +{ + Mvc_Manager_t * pMan; // the memory manager +// Vm_VarMap_t * pVm; // the MV variable data + int nBinVars; // the number of binary variables + Mvc_Cube_t * pMaskBin; // the mask to select the binary bits only + Mvc_Cube_t ** ppMasks; // the mask to select each MV variable + Mvc_Cube_t * ppTemp[3]; // the temporary cubes +}; + +// the manager of covers and cubes (as of today, only managing memory) +struct MvcManagerStruct +{ + Extra_MmFixed_t * pManC; // the manager for covers + Extra_MmFixed_t * pMan1; // the manager for 1-word cubes + Extra_MmFixed_t * pMan2; // the manager for 2-word cubes + Extra_MmFixed_t * pMan4; // the manager for 3-word cubes +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// reading data from the header of the cube +#define Mvc_CubeReadNext(Cube) ((Cube)->pNext) +#define Mvc_CubeReadNextP(Cube) (&(Cube)->pNext) +#define Mvc_CubeReadLast(Cube) ((Cube)->iLast) +#define Mvc_CubeReadSize(Cube) ((Cube)->nOnes) +// setting data to the header of the cube +#define Mvc_CubeSetNext(Cube,Next) ((Cube)->pNext = (Next)) +#define Mvc_CubeSetLast(Cube,Last) ((Cube)->iLast = (Last)) +#define Mvc_CubeSetSize(Cube,Size) ((Cube)->nOnes = (Size)) +// checking the number of words + +#define Mvc_Cube1Words(Cube) ((Cube)->iLast == 0) +#define Mvc_Cube2Words(Cube) ((Cube)->iLast == 1) +#define Mvc_CubeNWords(Cube) ((Cube)->iLast > 1) +// getting one data bit +#define Mvc_CubeWhichWord(Bit) ((Bit) >> BITS_PER_WORD_LOG) +#define Mvc_CubeWhichBit(Bit) ((Bit) & BITS_PER_WORD_MINUS) +// accessing individual bits +#define Mvc_CubeBitValue(Cube, Bit) (((Cube)->pData[Mvc_CubeWhichWord(Bit)] & (((Mvc_CubeWord_t)1)<<(Mvc_CubeWhichBit(Bit)))) > 0) +#define Mvc_CubeBitInsert(Cube, Bit) ((Cube)->pData[Mvc_CubeWhichWord(Bit)] |= (((Mvc_CubeWord_t)1)<<(Mvc_CubeWhichBit(Bit)))) +#define Mvc_CubeBitRemove(Cube, Bit) ((Cube)->pData[Mvc_CubeWhichWord(Bit)] &= ~(((Mvc_CubeWord_t)1)<<(Mvc_CubeWhichBit(Bit)))) +// accessing values of the binary variables +#define Mvc_CubeVarValue(Cube, Var) (((Cube)->pData[Mvc_CubeWhichWord(2*(Var))] >> (Mvc_CubeWhichBit(2*(Var)))) & ((Mvc_CubeWord_t)3)) + +// various macros + +// cleaning the data bits of the cube +#define Mvc_Cube1BitClean( Cube )\ + ((Cube)->pData[0] = 0) +#define Mvc_Cube2BitClean( Cube )\ + (((Cube)->pData[0] = 0),\ + ((Cube)->pData[1] = 0)) +#define Mvc_CubeNBitClean( Cube )\ +{\ + int _i_;\ + for( _i_ = (Cube)->iLast; _i_ >= 0; _i_--)\ + (Cube)->pData[_i_] = 0;\ +} + +// cleaning the unused part of the lat word +#define Mvc_CubeBitCleanUnused( Cube )\ + ((Cube)->pData[(Cube)->iLast] &= (BITS_FULL >> (Cube)->nUnused)) + +// filling the used data bits with 1's +#define Mvc_Cube1BitFill( Cube )\ + (Cube)->pData[0] = (BITS_FULL >> (Cube)->nUnused); +#define Mvc_Cube2BitFill( Cube )\ + (((Cube)->pData[0] = BITS_FULL),\ + ((Cube)->pData[1] = (BITS_FULL >> (Cube)->nUnused))) +#define Mvc_CubeNBitFill( Cube )\ +{\ + int _i_;\ + (Cube)->pData[(Cube)->iLast] = (BITS_FULL >> (Cube)->nUnused);\ + for( _i_ = (Cube)->iLast - 1; _i_ >= 0; _i_-- )\ + (Cube)->pData[_i_] = BITS_FULL;\ +} + +// complementing the data bits +#define Mvc_Cube1BitNot( Cube )\ + ((Cube)->pData[0] ^= (BITS_FULL >> (Cube)->nUnused)) +#define Mvc_Cube2BitNot( Cube )\ + (((Cube)->pData[0] ^= BITS_FULL),\ + ((Cube)->pData[1] ^= (BITS_FULL >> (Cube)->nUnused))) +#define Mvc_CubeNBitNot( Cube )\ +{\ + int _i_;\ + (Cube)->pData[(Cube)->iLast] ^= (BITS_FULL >> (Cube)->nUnused);\ + for( _i_ = (Cube)->iLast - 1; _i_ >= 0; _i_-- )\ + (Cube)->pData[_i_] ^= BITS_FULL;\ +} + +#define Mvc_Cube1BitCopy( Cube1, Cube2 )\ + (((Cube1)->pData[0]) = ((Cube2)->pData[0])) +#define Mvc_Cube2BitCopy( Cube1, Cube2 )\ + ((((Cube1)->pData[0]) = ((Cube2)->pData[0])),\ + (((Cube1)->pData[1])= ((Cube2)->pData[1]))) +#define Mvc_CubeNBitCopy( Cube1, Cube2 )\ +{\ + int _i_;\ + for (_i_ = (Cube1)->iLast; _i_ >= 0; _i_--)\ + ((Cube1)->pData[_i_]) = ((Cube2)->pData[_i_]);\ +} + +#define Mvc_Cube1BitOr( CubeR, Cube1, Cube2 )\ + (((CubeR)->pData[0]) = ((Cube1)->pData[0] | (Cube2)->pData[0])) +#define Mvc_Cube2BitOr( CubeR, Cube1, Cube2 )\ + ((((CubeR)->pData[0]) = ((Cube1)->pData[0] | (Cube2)->pData[0])),\ + (((CubeR)->pData[1]) = ((Cube1)->pData[1] | (Cube2)->pData[1]))) +#define Mvc_CubeNBitOr( CubeR, Cube1, Cube2 )\ +{\ + int _i_;\ + for (_i_ = (Cube1)->iLast; _i_ >= 0; _i_--)\ + (((CubeR)->pData[_i_]) = ((Cube1)->pData[_i_] | (Cube2)->pData[_i_]));\ +} + +#define Mvc_Cube1BitExor( CubeR, Cube1, Cube2 )\ + (((CubeR)->pData[0]) = ((Cube1)->pData[0] ^ (Cube2)->pData[0])) +#define Mvc_Cube2BitExor( CubeR, Cube1, Cube2 )\ + ((((CubeR)->pData[0]) = ((Cube1)->pData[0] ^ (Cube2)->pData[0])),\ + (((CubeR)->pData[1]) = ((Cube1)->pData[1] ^ (Cube2)->pData[1]))) +#define Mvc_CubeNBitExor( CubeR, Cube1, Cube2 )\ +{\ + int _i_;\ + for (_i_ = (Cube1)->iLast; _i_ >= 0; _i_--)\ + (((CubeR)->pData[_i_]) = ((Cube1)->pData[_i_] ^ (Cube2)->pData[_i_]));\ +} + +#define Mvc_Cube1BitAnd( CubeR, Cube1, Cube2 )\ + (((CubeR)->pData[0]) = ((Cube1)->pData[0] & (Cube2)->pData[0])) +#define Mvc_Cube2BitAnd( CubeR, Cube1, Cube2 )\ + ((((CubeR)->pData[0]) = ((Cube1)->pData[0] & (Cube2)->pData[0])),\ + (((CubeR)->pData[1]) = ((Cube1)->pData[1] & (Cube2)->pData[1]))) +#define Mvc_CubeNBitAnd( CubeR, Cube1, Cube2 )\ +{\ + int _i_;\ + for (_i_ = (Cube1)->iLast; _i_ >= 0; _i_--)\ + (((CubeR)->pData[_i_]) = ((Cube1)->pData[_i_] & (Cube2)->pData[_i_]));\ +} + +#define Mvc_Cube1BitSharp( CubeR, Cube1, Cube2 )\ + (((CubeR)->pData[0]) = ((Cube1)->pData[0] & ~((Cube2)->pData[0]))) +#define Mvc_Cube2BitSharp( CubeR, Cube1, Cube2 )\ + ((((CubeR)->pData[0]) = ((Cube1)->pData[0] & ~((Cube2)->pData[0]))),\ + (((CubeR)->pData[1]) = ((Cube1)->pData[1] & ~((Cube2)->pData[1])))) +#define Mvc_CubeNBitSharp( CubeR, Cube1, Cube2 )\ +{\ + int _i_;\ + for (_i_ = (Cube1)->iLast; _i_ >= 0; _i_--)\ + (((CubeR)->pData[_i_]) = ((Cube1)->pData[_i_] & ~(Cube2)->pData[_i_]));\ +} + +#define Mvc_Cube1BitEmpty( Res, Cube )\ + (Res = ((Cube)->pData[0] == 0)) +#define Mvc_Cube2BitEmpty( Res, Cube )\ + (Res = ((Cube)->pData[0] == 0 && (Cube)->pData[1] == 0)) +#define Mvc_CubeNBitEmpty( Res, Cube )\ +{\ + int _i_; Res = 1;\ + for (_i_ = (Cube)->iLast; _i_ >= 0; _i_--)\ + if ( (Cube)->pData[_i_] )\ + { Res = 0; break; }\ +} + +#define Mvc_Cube1BitEqual( Res, Cube1, Cube2 )\ + (Res = (((Cube1)->pData[0]) == ((Cube2)->pData[0]))) +#define Mvc_Cube2BitEqual( Res, Cube1, Cube2 )\ + (Res = ((((Cube1)->pData[0]) == ((Cube2)->pData[0])) &&\ + (((Cube1)->pData[1]) == ((Cube2)->pData[1])))) +#define Mvc_CubeNBitEqual( Res, Cube1, Cube2 )\ +{\ + int _i_; Res = 1;\ + for (_i_ = (Cube1)->iLast; _i_ >= 0; _i_--)\ + if (((Cube1)->pData[_i_]) != ((Cube2)->pData[_i_]))\ + { Res = 0; break; }\ +} + +#define Mvc_Cube1BitLess( Res, Cube1, Cube2 )\ + (Res = (((Cube1)->pData[0]) < ((Cube2)->pData[0]))) +#define Mvc_Cube2BitLess( Res, Cube1, Cube2 )\ + (Res = ((((Cube1)->pData[0]) < ((Cube2)->pData[0])) ||\ + ((((Cube1)->pData[0]) == ((Cube2)->pData[0])) && (((Cube1)->pData[1]) < ((Cube2)->pData[1]))))) +#define Mvc_CubeNBitLess( Res, Cube1, Cube2 )\ +{\ + int _i_; Res = 1;\ + for (_i_ = (Cube1)->iLast; _i_ >= 0; _i_--)\ + if (((Cube1)->pData[_i_]) >= ((Cube2)->pData[_i_]))\ + { Res = 0; break; }\ +} + +#define Mvc_Cube1BitMore( Res, Cube1, Cube2 )\ + (Res = (((Cube1)->pData[0]) > ((Cube2)->pData[0]))) +#define Mvc_Cube2BitMore( Res, Cube1, Cube2 )\ + (Res = ((((Cube1)->pData[0]) > ((Cube2)->pData[0])) ||\ + ((((Cube1)->pData[0]) == ((Cube2)->pData[0])) && (((Cube1)->pData[1]) > ((Cube2)->pData[1]))))) +#define Mvc_CubeNBitMore( Res, Cube1, Cube2 )\ +{\ + int _i_; Res = 1;\ + for (_i_ = (Cube1)->iLast; _i_ >= 0; _i_--)\ + if (((Cube1)->pData[_i_]) <= ((Cube2)->pData[_i_]))\ + { Res = 0; break; }\ +} + +#define Mvc_Cube1BitNotImpl( Res, Cube1, Cube2 )\ + (Res = (((Cube1)->pData[0]) & ~((Cube2)->pData[0]))) +#define Mvc_Cube2BitNotImpl( Res, Cube1, Cube2 )\ + (Res = ((((Cube1)->pData[0]) & ~((Cube2)->pData[0])) ||\ + (((Cube1)->pData[1]) & ~((Cube2)->pData[1])))) +#define Mvc_CubeNBitNotImpl( Res, Cube1, Cube2 )\ +{\ + int _i_; Res = 0;\ + for (_i_ = (Cube1)->iLast; _i_ >= 0; _i_--)\ + if (((Cube1)->pData[_i_]) & ~((Cube2)->pData[_i_]))\ + { Res = 1; break; }\ +} + +#define Mvc_Cube1BitDisjoint( Res, Cube1, Cube2 )\ + (Res = ((((Cube1)->pData[0]) & ((Cube2)->pData[0])) == 0 )) +#define Mvc_Cube2BitDisjoint( Res, Cube1, Cube2 )\ + (Res = (((((Cube1)->pData[0]) & ((Cube2)->pData[0])) == 0 ) &&\ + ((((Cube1)->pData[1]) & ((Cube2)->pData[1])) == 0 ))) +#define Mvc_CubeNBitDisjoint( Res, Cube1, Cube2 )\ +{\ + int _i_; Res = 1;\ + for (_i_ = (Cube1)->iLast; _i_ >= 0; _i_--)\ + if (((Cube1)->pData[_i_]) & ((Cube2)->pData[_i_]))\ + { Res = 0; break; }\ +} + +#define Mvc_Cube1BitEqualUnderMask( Res, Cube1, Cube2, Mask )\ + (Res = ((((Cube1)->pData[0]) & ((Mask)->pData[0])) == (((Cube2)->pData[0]) & ((Mask)->pData[0])))) +#define Mvc_Cube2BitEqualUnderMask( Res, Cube1, Cube2, Mask )\ + (Res = (((((Cube1)->pData[0]) & ((Mask)->pData[0])) == (((Cube2)->pData[0]) & ((Mask)->pData[0]))) &&\ + ((((Cube1)->pData[1]) & ((Mask)->pData[1])) == (((Cube2)->pData[1]) & ((Mask)->pData[1]))))) +#define Mvc_CubeNBitEqualUnderMask( Res, Cube1, Cube2, Mask )\ +{\ + int _i_; Res = 1;\ + for (_i_ = (Cube1)->iLast; _i_ >= 0; _i_--)\ + if ((((Cube1)->pData[_i_]) & ((Mask)->pData[_i_])) != (((Cube2)->pData[_i_]) & ((Mask)->pData[_i_])))\ + { Res = 0; break; }\ +} + +#define Mvc_Cube1BitEqualOutsideMask( Res, Cube1, Cube2, Mask )\ + (Res = ((((Cube1)->pData[0]) | ((Mask)->pData[0])) == (((Cube2)->pData[0]) | ((Mask)->pData[0])))) +#define Mvc_Cube2BitEqualOutsideMask( Res, Cube1, Cube2, Mask )\ + (Res = (((((Cube1)->pData[0]) | ((Mask)->pData[0])) == (((Cube2)->pData[0]) | ((Mask)->pData[0]))) &&\ + ((((Cube1)->pData[1]) | ((Mask)->pData[1])) == (((Cube2)->pData[1]) | ((Mask)->pData[1]))))) +#define Mvc_CubeNBitEqualOutsideMask( Res, Cube1, Cube2, Mask )\ +{\ + int _i_; Res = 1;\ + for (_i_ = (Cube1)->iLast; _i_ >= 0; _i_--)\ + if ((((Cube1)->pData[_i_]) | ((Mask)->pData[_i_])) != (((Cube2)->pData[_i_]) | ((Mask)->pData[_i_])))\ + { Res = 0; break; }\ +} + +#define Mvc_Cube1BitIntersectUnderMask( Res, Cube1, Cube2, Mask)\ + (Res = ((((Cube1)->pData[0]) & ((Cube2)->pData[0]) & ((Mask)->pData[0])) > 0)) +#define Mvc_Cube2BitIntersectUnderMask( Res, Cube1, Cube2, Mask)\ + (Res = (((((Cube1)->pData[0]) & ((Cube2)->pData[0]) & ((Mask)->pData[0])) > 0) ||\ + ((((Cube1)->pData[1]) & ((Cube2)->pData[1]) & ((Mask)->pData[1])) > 0))) +#define Mvc_CubeNBitIntersectUnderMask( Res, Cube1, Cube2, Mask)\ +{\ + int _i_; Res = 0;\ + for (_i_ = (Cube1)->iLast; _i_ >= 0; _i_--)\ + if (((Cube1)->pData[_i_]) & ((Cube2)->pData[_i_]) & ((Mask)->pData[_i_]))\ + { Res = 1; break; }\ +} + +#define Mvc_Cube1BitNotImplUnderMask( Res, Cube1, Cube2, Mask )\ + (Res = (((Mask)->pData[0]) & ((Cube1)->pData[0]) & ~((Cube2)->pData[0]))) +#define Mvc_Cube2BitNotImplUnderMask( Res, Cube1, Cube2, Mask )\ + (Res = ((((Mask)->pData[0]) & ((Cube1)->pData[0]) & ~((Cube2)->pData[0])) ||\ + (((Mask)->pData[1]) & ((Cube1)->pData[1]) & ~((Cube2)->pData[1])))) +#define Mvc_CubeNBitNotImplUnderMask( Res, Cube1, Cube2, Mask )\ +{\ + int _i_; Res = 0;\ + for (_i_ = (Cube1)->iLast; _i_ >= 0; _i_--)\ + if (((Mask)->pData[_i_]) & ((Cube1)->pData[_i_]) & ~((Cube2)->pData[_i_]))\ + { Res = 1; break; }\ +} + +// the following macros make no assumption about the cube's bitset size +#define Mvc_CubeBitClean( Cube )\ + if ( Mvc_Cube1Words(Cube) ) { Mvc_Cube1BitClean( Cube ); }\ + else if ( Mvc_Cube2Words(Cube) ) { Mvc_Cube2BitClean( Cube ); }\ + else { Mvc_CubeNBitClean( Cube ); } +#define Mvc_CubeBitFill( Cube )\ + if ( Mvc_Cube1Words(Cube) ) { Mvc_Cube1BitFill( Cube ); }\ + else if ( Mvc_Cube2Words(Cube) ) { Mvc_Cube2BitFill( Cube ); }\ + else { Mvc_CubeNBitFill( Cube ); } +#define Mvc_CubeBitNot( Cube )\ + if ( Mvc_Cube1Words(Cube) ) { Mvc_Cube1BitNot( Cube ); }\ + else if ( Mvc_Cube2Words(Cube) ) { Mvc_Cube2BitNot( Cube ); }\ + else { Mvc_CubeNBitNot( Cube ); } +#define Mvc_CubeBitCopy( Cube1, Cube2 )\ + if ( Mvc_Cube1Words(Cube1) ) { Mvc_Cube1BitCopy( Cube1, Cube2 ); }\ + else if ( Mvc_Cube2Words(Cube1) ){ Mvc_Cube2BitCopy( Cube1, Cube2 ); }\ + else { Mvc_CubeNBitCopy( Cube1, Cube2 ); } +#define Mvc_CubeBitOr( CubeR, Cube1, Cube2 )\ + if ( Mvc_Cube1Words(Cube1) ) { Mvc_Cube1BitOr( CubeR, Cube1, Cube2 ); }\ + else if ( Mvc_Cube2Words(Cube1) ){ Mvc_Cube2BitOr( CubeR, Cube1, Cube2 ); }\ + else { Mvc_CubeNBitOr( CubeR, Cube1, Cube2 ); } +#define Mvc_CubeBitExor( CubeR, Cube1, Cube2 )\ + if ( Mvc_Cube1Words(Cube1) ) { Mvc_Cube1BitExor( CubeR, Cube1, Cube2 ); }\ + else if ( Mvc_Cube2Words(Cube1) ){ Mvc_Cube2BitExor( CubeR, Cube1, Cube2 ); }\ + else { Mvc_CubeNBitExor( CubeR, Cube1, Cube2 ); } +#define Mvc_CubeBitAnd( CubeR, Cube1, Cube2 )\ + if ( Mvc_Cube1Words(Cube1) ) { Mvc_Cube1BitAnd( CubeR, Cube1, Cube2 ); }\ + else if ( Mvc_Cube2Words(Cube1) ){ Mvc_Cube2BitAnd( CubeR, Cube1, Cube2 ); }\ + else { Mvc_CubeNBitAnd( CubeR, Cube1, Cube2 ); } +#define Mvc_CubeBitSharp( CubeR, Cube1, Cube2 )\ + if ( Mvc_Cube1Words(Cube1) ) { Mvc_Cube1BitSharp( CubeR, Cube1, Cube2 ); }\ + else if ( Mvc_Cube2Words(Cube1) ){ Mvc_Cube2BitSharp( CubeR, Cube1, Cube2 ); }\ + else { Mvc_CubeNBitSharp( CubeR, Cube1, Cube2 ); } +#define Mvc_CubeBitEmpty( Res, Cube )\ + if ( Mvc_Cube1Words(Cube) ) { Mvc_Cube1BitEmpty( Res, Cube ); }\ + else if ( Mvc_Cube2Words(Cube) ) { Mvc_Cube2BitEmpty( Res, Cube ); }\ + else { Mvc_CubeNBitEmpty( Res, Cube ); } +#define Mvc_CubeBitEqual( Res, Cube1, Cube2 )\ + if ( Mvc_Cube1Words(Cube1) ) { Mvc_Cube1BitEqual( Res, Cube1, Cube2 ); }\ + else if ( Mvc_Cube2Words(Cube1) ){ Mvc_Cube2BitEqual( Res, Cube1, Cube2 ); }\ + else { Mvc_CubeNBitEqual( Res, Cube1, Cube2 ); } +#define Mvc_CubeBitLess( Res, Cube1, Cube2 )\ + if ( Mvc_Cube1Words(Cube1) ) { Mvc_Cube1BitLess( Res, Cube1, Cube2 ); }\ + else if ( Mvc_Cube2Words(Cube1) ){ Mvc_Cube2BitLess( Res, Cube1, Cube2 ); }\ + else { Mvc_CubeNBitLess( Res, Cube1, Cube2 ); } +#define Mvc_CubeBitMore( Res, Cube1, Cube2 )\ + if ( Mvc_Cube1Words(Cube1) ) { Mvc_Cube1BitMore( Res, Cube1, Cube2 ); }\ + else if ( Mvc_Cube2Words(Cube1) ){ Mvc_Cube2BitMore( Res, Cube1, Cube2 ); }\ + else { Mvc_CubeNBitMore( Res, Cube1, Cube2 ); } +#define Mvc_CubeBitNotImpl( Res, Cube1, Cube2 )\ + if ( Mvc_Cube1Words(Cube1) ) { Mvc_Cube1BitNotImpl( Res, Cube1, Cube2 ); }\ + else if ( Mvc_Cube2Words(Cube1) ){ Mvc_Cube2BitNotImpl( Res, Cube1, Cube2 ); }\ + else { Mvc_CubeNBitNotImpl( Res, Cube1, Cube2 ); } +#define Mvc_CubeBitDisjoint( Res, Cube1, Cube2 )\ + if ( Mvc_Cube1Words(Cube1) ) { Mvc_Cube1BitDisjoint( Res, Cube1, Cube2 ); }\ + else if ( Mvc_Cube2Words(Cube1) ){ Mvc_Cube2BitDisjoint( Res, Cube1, Cube2 ); }\ + else { Mvc_CubeNBitDisjoint( Res, Cube1, Cube2 ); } +#define Mvc_CubeBitEqualUnderMask( Res, Cube1, Cube2, Mask )\ + if ( Mvc_Cube1Words(Cube1) ) { Mvc_Cube1BitEqualUnderMask( Res, Cube1, Cube2, Mask ); }\ + else if ( Mvc_Cube2Words(Cube1) ){ Mvc_Cube2BitEqualUnderMask( Res, Cube1, Cube2, Mask ); }\ + else { Mvc_CubeNBitEqualUnderMask( Res, Cube1, Cube2, Mask ); } +#define Mvc_CubeBitEqualOutsideMask( Res, Cube1, Cube2, Mask )\ + if ( Mvc_Cube1Words(Cube1) ) { Mvc_Cube1BitEqualOutsideMask( Res, Cube1, Cube2, Mask ); }\ + else if ( Mvc_Cube2Words(Cube1) ){ Mvc_Cube2BitEqualOutsideMask( Res, Cube1, Cube2, Mask ); }\ + else { Mvc_CubeNBitEqualOutsideMask( Res, Cube1, Cube2, Mask ); } +#define Mvc_CubeBitIntersectUnderMask( Res, Cube1, Cube2, Mask )\ + if ( Mvc_Cube1Words(Cube1) ) { Mvc_Cube1BitIntersectUnderMask( Res, Cube1, Cube2, Mask ); }\ + else if ( Mvc_Cube2Words(Cube1) ){ Mvc_Cube2BitIntersectUnderMask( Res, Cube1, Cube2, Mask ); }\ + else { Mvc_CubeNBitIntersectUnderMask( Res, Cube1, Cube2, Mask ); } +#define Mvc_CubeBitNotImplUnderMask( Res, Cube1, Cube2, Mask )\ + if ( Mvc_Cube1Words(Cube1) ) { Mvc_Cube1BitNotImplUnderMask( Res, Cube1, Cube2, Mask ); }\ + else if ( Mvc_Cube2Words(Cube1) ){ Mvc_Cube2BitNotImplUnderMask( Res, Cube1, Cube2, Mask ); }\ + else { Mvc_CubeNBitNotImplUnderMask( Res, Cube1, Cube2, Mask ); } + + +// managing linked lists +#define Mvc_ListAddCubeHead( pList, pCube )\ + {\ + if ( pList->pHead == NULL )\ + {\ + Mvc_CubeSetNext( pCube, NULL );\ + pList->pHead = pCube;\ + pList->pTail = pCube;\ + }\ + else\ + {\ + Mvc_CubeSetNext( pCube, pList->pHead );\ + pList->pHead = pCube;\ + }\ + pList->nItems++;\ + } +#define Mvc_ListAddCubeTail( pList, pCube )\ + {\ + if ( pList->pHead == NULL )\ + pList->pHead = pCube;\ + else\ + Mvc_CubeSetNext( pList->pTail, pCube );\ + pList->pTail = pCube;\ + Mvc_CubeSetNext( pCube, NULL );\ + pList->nItems++;\ + } +#define Mvc_ListDeleteCube( pList, pPrev, pCube )\ +{\ + if ( pPrev == NULL )\ + pList->pHead = pCube->pNext;\ + else\ + pPrev->pNext = pCube->pNext;\ + if ( pList->pTail == pCube )\ + {\ + assert( pCube->pNext == NULL );\ + pList->pTail = pPrev;\ + }\ + pList->nItems--;\ +} + +// managing linked lists inside the cover +#define Mvc_CoverAddCubeHead( pCover, pCube )\ +{\ + Mvc_List_t * pList = &pCover->lCubes;\ + Mvc_ListAddCubeHead( pList, pCube );\ +} +#define Mvc_CoverAddCubeTail( pCover, pCube )\ +{\ + Mvc_List_t * pList = &pCover->lCubes;\ + Mvc_ListAddCubeTail( pList, pCube );\ +} +#define Mvc_CoverDeleteCube( pCover, pPrev, pCube )\ +{\ + Mvc_List_t * pList = &pCover->lCubes;\ + Mvc_ListDeleteCube( pList, pPrev, pCube );\ +} + + + + + + +// iterator through the cubes in the cube list +#define Mvc_ListForEachCube( List, Cube )\ + for ( Cube = List->pHead;\ + Cube;\ + Cube = Cube->pNext ) +#define Mvc_ListForEachCubeSafe( List, Cube, Cube2 )\ + for ( Cube = List->pHead, Cube2 = (Cube? Cube->pNext: NULL);\ + Cube;\ + Cube = Cube2, Cube2 = (Cube? Cube->pNext: NULL) ) + +// iterator through cubes in the cover +#define Mvc_CoverForEachCube( Cover, Cube )\ + for ( Cube = (Cover)->lCubes.pHead;\ + Cube;\ + Cube = Cube->pNext ) +#define Mvc_CoverForEachCubeWithIndex( Cover, Cube, Index )\ + for ( Index = 0, Cube = (Cover)->lCubes.pHead;\ + Cube;\ + Index++, Cube = Cube->pNext ) +#define Mvc_CoverForEachCubeSafe( Cover, Cube, Cube2 )\ + for ( Cube = (Cover)->lCubes.pHead, Cube2 = (Cube? Cube->pNext: NULL);\ + Cube;\ + Cube = Cube2, Cube2 = (Cube? Cube->pNext: NULL) ) + +// iterator which starts from the given cube +#define Mvc_CoverForEachCubeStart( Start, Cube )\ + for ( Cube = Start;\ + Cube;\ + Cube = Cube->pNext ) +#define Mvc_CoverForEachCubeStartSafe( Start, Cube, Cube2 )\ + for ( Cube = Start, Cube2 = (Cube? Cube->pNext: NULL);\ + Cube;\ + Cube = Cube2, Cube2 = (Cube? Cube->pNext: NULL) ) + + +// iterator through literals of the cube +#define Mvc_CubeForEachBit( Cover, Cube, iBit, Value )\ + for ( iBit = 0;\ + iBit < Cover->nBits && ((Value = Mvc_CubeBitValue(Cube,iBit))>=0);\ + iBit++ ) +// iterator through values of binary variables +#define Mvc_CubeForEachVarValue( Cover, Cube, iVar, Value )\ + for ( iVar = 0;\ + iVar < Cover->nBits/2 && (Value = Mvc_CubeVarValue(Cube,iVar));\ + iVar++ ) + + +// macros which work with memory +// MEM_ALLOC: allocate the given number (Size) of items of type (Type) +// MEM_FREE: deallocate the pointer (Pointer) to the given number (Size) of items of type (Type) +#define MEM_ALLOC( Manager, Type, Size ) ((Type *)malloc( (Size) * sizeof(Type) )) +#define MEM_FREE( Manager, Type, Size, Pointer ) if ( Pointer ) { free(Pointer); Pointer = NULL; } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== mvcApi.c ====================================================*/ +extern int Mvc_CoverReadWordNum( Mvc_Cover_t * pCover ); +extern int Mvc_CoverReadBitNum( Mvc_Cover_t * pCover ); +extern int Mvc_CoverReadCubeNum( Mvc_Cover_t * pCover ); +extern Mvc_Cube_t * Mvc_CoverReadCubeHead( Mvc_Cover_t * pCover ); +extern Mvc_Cube_t * Mvc_CoverReadCubeTail( Mvc_Cover_t * pCover ); +extern Mvc_List_t * Mvc_CoverReadCubeList( Mvc_Cover_t * pCover ); +extern int Mvc_ListReadCubeNum( Mvc_List_t * pList ); +extern Mvc_Cube_t * Mvc_ListReadCubeHead( Mvc_List_t * pList ); +extern Mvc_Cube_t * Mvc_ListReadCubeTail( Mvc_List_t * pList ); +extern void Mvc_CoverSetCubeNum( Mvc_Cover_t * pCover,int nItems ); +extern void Mvc_CoverSetCubeHead( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ); +extern void Mvc_CoverSetCubeTail( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ); +extern void Mvc_CoverSetCubeList( Mvc_Cover_t * pCover, Mvc_List_t * pList ); +extern int Mvc_CoverIsEmpty( Mvc_Cover_t * pCover ); +extern int Mvc_CoverIsTautology( Mvc_Cover_t * pCover ); +extern int Mvc_CoverIsBinaryBuffer( Mvc_Cover_t * pCover ); +extern void Mvc_CoverMakeEmpty( Mvc_Cover_t * pCover ); +extern void Mvc_CoverMakeTautology( Mvc_Cover_t * pCover ); +extern Mvc_Cover_t * Mvc_CoverCreateEmpty( Mvc_Cover_t * pCover ); +extern Mvc_Cover_t * Mvc_CoverCreateTautology( Mvc_Cover_t * pCover ); +/*=== mvcCover.c ====================================================*/ +extern Mvc_Cover_t * Mvc_CoverAlloc( Mvc_Manager_t * pMem, int nBits ); +extern Mvc_Cover_t * Mvc_CoverCreateConst( Mvc_Manager_t * pMem, int nBits, int Phase ); +extern Mvc_Cover_t * Mvc_CoverClone( Mvc_Cover_t * pCover ); +extern Mvc_Cover_t * Mvc_CoverDup( Mvc_Cover_t * pCover ); +extern void Mvc_CoverFree( Mvc_Cover_t * pCover ); +extern void Mvc_CoverAllocateMask( Mvc_Cover_t * pCover ); +extern void Mvc_CoverAllocateArrayLits( Mvc_Cover_t * pCover ); +extern void Mvc_CoverAllocateArrayCubes( Mvc_Cover_t * pCover ); +extern void Mvc_CoverDeallocateMask( Mvc_Cover_t * pCover ); +extern void Mvc_CoverDeallocateArrayLits( Mvc_Cover_t * pCover ); +/*=== mvcCube.c ====================================================*/ +extern Mvc_Cube_t * Mvc_CubeAlloc( Mvc_Cover_t * pCover ); +extern Mvc_Cube_t * Mvc_CubeDup( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ); +extern void Mvc_CubeFree( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ); +extern void Mvc_CubeBitRemoveDcs( Mvc_Cube_t * pCube ); +/*=== mvcCompare.c ====================================================*/ +extern int Mvc_CubeCompareInt( Mvc_Cube_t * pC1, Mvc_Cube_t * pC2, Mvc_Cube_t * pMask ); +extern int Mvc_CubeCompareSizeAndInt( Mvc_Cube_t * pC1, Mvc_Cube_t * pC2, Mvc_Cube_t * pMask ); +extern int Mvc_CubeCompareIntUnderMask( Mvc_Cube_t * pC1, Mvc_Cube_t * pC2, Mvc_Cube_t * pMask ); +extern int Mvc_CubeCompareIntOutsideMask( Mvc_Cube_t * pC1, Mvc_Cube_t * pC2, Mvc_Cube_t * pMask ); +extern int Mvc_CubeCompareIntOutsideAndUnderMask( Mvc_Cube_t * pC1, Mvc_Cube_t * pC2, Mvc_Cube_t * pMask ); +/*=== mvcDd.c ====================================================*/ +/* +extern DdNode * Mvc_CoverConvertToBdd( DdManager * dd, Mvc_Cover_t * pCover ); +extern DdNode * Mvc_CoverConvertToZdd( DdManager * dd, Mvc_Cover_t * pCover ); +extern DdNode * Mvc_CoverConvertToZdd2( DdManager * dd, Mvc_Cover_t * pCover ); +extern DdNode * Mvc_CubeConvertToBdd( DdManager * dd, Mvc_Cube_t * pCube ); +extern DdNode * Mvc_CubeConvertToZdd( DdManager * dd, Mvc_Cube_t * pCube ); +extern DdNode * Mvc_CubeConvertToZdd2( DdManager * dd, Mvc_Cube_t * pCube ); +*/ +/*=== mvcDivisor.c ====================================================*/ +extern Mvc_Cover_t * Mvc_CoverDivisor( Mvc_Cover_t * pCover ); +/*=== mvcDivide.c ====================================================*/ +extern void Mvc_CoverDivide( Mvc_Cover_t * pCover, Mvc_Cover_t * pDiv, Mvc_Cover_t ** ppQuo, Mvc_Cover_t ** ppRem ); +extern void Mvc_CoverDivideInternal( Mvc_Cover_t * pCover, Mvc_Cover_t * pDiv, Mvc_Cover_t ** ppQuo, Mvc_Cover_t ** ppRem ); +extern void Mvc_CoverDivideByLiteral( Mvc_Cover_t * pCover, Mvc_Cover_t * pDiv, Mvc_Cover_t ** ppQuo, Mvc_Cover_t ** ppRem ); +extern void Mvc_CoverDivideByCube( Mvc_Cover_t * pCover, Mvc_Cover_t * pDiv, Mvc_Cover_t ** ppQuo, Mvc_Cover_t ** ppRem ); +extern void Mvc_CoverDivideByLiteralQuo( Mvc_Cover_t * pCover, int iLit ); +/*=== mvcList.c ====================================================*/ +// these functions are available as macros +extern void Mvc_ListAddCubeHead_( Mvc_List_t * pList, Mvc_Cube_t * pCube ); +extern void Mvc_ListAddCubeTail_( Mvc_List_t * pList, Mvc_Cube_t * pCube ); +extern void Mvc_ListDeleteCube_( Mvc_List_t * pList, Mvc_Cube_t * pPrev, Mvc_Cube_t * pCube ); +extern void Mvc_CoverAddCubeHead_( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ); +extern void Mvc_CoverAddCubeTail_( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ); +extern void Mvc_CoverDeleteCube_( Mvc_Cover_t * pCover, Mvc_Cube_t * pPrev, Mvc_Cube_t * pCube ); +extern void Mvc_CoverAddDupCubeHead( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ); +extern void Mvc_CoverAddDupCubeTail( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ); +// other functions +extern void Mvc_CoverAddLiteralsOfCube( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ); +extern void Mvc_CoverDeleteLiteralsOfCube( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ); +extern void Mvc_CoverList2Array( Mvc_Cover_t * pCover ); +extern void Mvc_CoverArray2List( Mvc_Cover_t * pCover ); +extern Mvc_Cube_t * Mvc_ListGetTailFromHead( Mvc_Cube_t * pHead ); +/*=== mvcPrint.c ====================================================*/ +extern void Mvc_CoverPrint( Mvc_Cover_t * pCover ); +extern void Mvc_CubePrint( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ); +extern void Mvc_CoverPrintMv( Mvc_Data_t * pData, Mvc_Cover_t * pCover ); +extern void Mvc_CubePrintMv( Mvc_Data_t * pData, Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ); +/*=== mvcSort.c ====================================================*/ +extern void Mvc_CoverSort( Mvc_Cover_t * pCover, Mvc_Cube_t * pMask, int (* pCompareFunc)(Mvc_Cube_t *, Mvc_Cube_t *, Mvc_Cube_t *) ); +/*=== mvcUtils.c ====================================================*/ +extern void Mvc_CoverSupport( Mvc_Cover_t * pCover, Mvc_Cube_t * pSupp ); +extern int Mvc_CoverSupportSizeBinary( Mvc_Cover_t * pCover ); +extern int Mvc_CoverSupportVarBelongs( Mvc_Cover_t * pCover, int iVar ); +extern void Mvc_CoverCommonCube( Mvc_Cover_t * pCover, Mvc_Cube_t * pComCube ); +extern int Mvc_CoverIsCubeFree( Mvc_Cover_t * pCover ); +extern void Mvc_CoverMakeCubeFree( Mvc_Cover_t * pCover ); +extern Mvc_Cover_t * Mvc_CoverCommonCubeCover( Mvc_Cover_t * pCover ); +extern int Mvc_CoverCheckSuppContainment( Mvc_Cover_t * pCover1, Mvc_Cover_t * pCover2 ); +extern int Mvc_CoverSetCubeSizes( Mvc_Cover_t * pCover ); +extern int Mvc_CoverGetCubeSize( Mvc_Cube_t * pCube ); +extern int Mvc_CoverCountCubePairDiffs( Mvc_Cover_t * pCover, unsigned char pDiffs[] ); +extern Mvc_Cover_t * Mvc_CoverRemap( Mvc_Cover_t * pCover, int * pVarsRem, int nVarsRem ); +extern void Mvc_CoverInverse( Mvc_Cover_t * pCover ); +extern Mvc_Cover_t * Mvc_CoverRemoveDontCareLits( Mvc_Cover_t * pCover ); +extern Mvc_Cover_t * Mvc_CoverCofactor( Mvc_Cover_t * pCover, int iValue, int iValueOther ); +extern Mvc_Cover_t * Mvc_CoverFlipVar( Mvc_Cover_t * pCover, int iValue0, int iValue1 ); +extern Mvc_Cover_t * Mvc_CoverUnivQuantify( Mvc_Cover_t * p, int iValueA0, int iValueA1, int iValueB0, int iValueB1 ); +extern Mvc_Cover_t ** Mvc_CoverCofactors( Mvc_Data_t * pData, Mvc_Cover_t * pCover, int iVar ); +extern int Mvr_CoverCountLitsWithValue( Mvc_Data_t * pData, Mvc_Cover_t * pCover, int iVar, int iValue ); +//extern Mvc_Cover_t * Mvc_CoverCreateExpanded( Mvc_Cover_t * pCover, Vm_VarMap_t * pVmNew ); +extern Mvc_Cover_t * Mvc_CoverTranspose( Mvc_Cover_t * pCover ); +extern int Mvc_UtilsCheckUnusedZeros( Mvc_Cover_t * pCover ); +/*=== mvcLits.c ====================================================*/ +extern int Mvc_CoverAnyLiteral( Mvc_Cover_t * pCover, Mvc_Cube_t * pMask ); +extern int Mvc_CoverBestLiteral( Mvc_Cover_t * pCover, Mvc_Cube_t * pMask ); +extern int Mvc_CoverWorstLiteral( Mvc_Cover_t * pCover, Mvc_Cube_t * pMask ); +extern Mvc_Cover_t * Mvc_CoverBestLiteralCover( Mvc_Cover_t * pCover, Mvc_Cover_t * pSimple ); +extern int Mvc_CoverFirstCubeFirstLit( Mvc_Cover_t * pCover ); +extern int Mvc_CoverCountLiterals( Mvc_Cover_t * pCover ); +extern int Mvc_CoverIsOneLiteral( Mvc_Cover_t * pCover ); +/*=== mvcOpAlg.c ====================================================*/ +extern Mvc_Cover_t * Mvc_CoverAlgebraicMultiply( Mvc_Cover_t * pCover1, Mvc_Cover_t * pCover2 ); +extern Mvc_Cover_t * Mvc_CoverAlgebraicSubtract( Mvc_Cover_t * pCover1, Mvc_Cover_t * pCover2 ); +extern int Mvc_CoverAlgebraicEqual( Mvc_Cover_t * pCover1, Mvc_Cover_t * pCover2 ); +/*=== mvcOpBool.c ====================================================*/ +extern Mvc_Cover_t * Mvc_CoverBooleanOr( Mvc_Cover_t * pCover1, Mvc_Cover_t * pCover2 ); +extern Mvc_Cover_t * Mvc_CoverBooleanAnd( Mvc_Data_t * p, Mvc_Cover_t * pCover1, Mvc_Cover_t * pCover2 ); +extern int Mvc_CoverBooleanEqual( Mvc_Data_t * p, Mvc_Cover_t * pCover1, Mvc_Cover_t * pCover2 ); + +/*=== mvcContain.c ====================================================*/ +extern int Mvc_CoverContain( Mvc_Cover_t * pCover ); +/*=== mvcTau.c ====================================================*/ +extern int Mvc_CoverTautology( Mvc_Data_t * p, Mvc_Cover_t * pCover ); +/*=== mvcCompl.c ====================================================*/ +extern Mvc_Cover_t * Mvc_CoverComplement( Mvc_Data_t * p, Mvc_Cover_t * pCover ); +/*=== mvcSharp.c ====================================================*/ +extern Mvc_Cover_t * Mvc_CoverSharp( Mvc_Data_t * p, Mvc_Cover_t * pA, Mvc_Cover_t * pB ); +extern int Mvc_CoverDist0Cubes( Mvc_Data_t * pData, Mvc_Cube_t * pA, Mvc_Cube_t * pB ); +extern void Mvc_CoverIntersectCubes( Mvc_Data_t * pData, Mvc_Cover_t * pC1, Mvc_Cover_t * pC2 ); +extern int Mvc_CoverIsIntersecting( Mvc_Data_t * pData, Mvc_Cover_t * pC1, Mvc_Cover_t * pC2 ); +extern void Mvc_CoverAppendCubes( Mvc_Cover_t * pC1, Mvc_Cover_t * pC2 ); +extern void Mvc_CoverCopyAndAppendCubes( Mvc_Cover_t * pC1, Mvc_Cover_t * pC2 ); +extern void Mvc_CoverRemoveCubes( Mvc_Cover_t * pC ); + +/*=== mvcReshape.c ====================================================*/ +extern void Mvc_CoverMinimizeByReshape( Mvc_Data_t * pData, Mvc_Cover_t * pCover ); + +/*=== mvcMerge.c ====================================================*/ +extern void Mvc_CoverDist1Merge( Mvc_Data_t * p, Mvc_Cover_t * pCover ); + +/*=== mvcData.c ====================================================*/ +//extern Mvc_Data_t * Mvc_CoverDataAlloc( Vm_VarMap_t * pVm, Mvc_Cover_t * pCover ); +//extern void Mvc_CoverDataFree( Mvc_Data_t * p, Mvc_Cover_t * pCover ); + +/*=== mvcMan.c ====================================================*/ +extern void Mvc_ManagerFree( Mvc_Manager_t * p ); +extern Mvc_Manager_t * Mvc_ManagerStart(); +extern Mvc_Manager_t * Mvc_ManagerAllocCover(); +extern Mvc_Manager_t * Mvc_ManagerAllocCube( int nWords ); +extern Mvc_Manager_t * Mvc_ManagerFreeCover( Mvc_Cover_t * pCover ); +extern Mvc_Manager_t * Mvc_ManagerFreeCube( Mvc_Cover_t * pCube, int nWords ); + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/misc/mvc/mvcApi.c b/abc_with_bb_support/src/misc/mvc/mvcApi.c new file mode 100644 index 000000000..6452b72a0 --- /dev/null +++ b/abc_with_bb_support/src/misc/mvc/mvcApi.c @@ -0,0 +1,233 @@ +/**CFile**************************************************************** + + FileName [mvcApi.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: mvcApi.c,v 1.4 2003/04/03 06:31:48 alanmi Exp $] + +***********************************************************************/ + +#include "mvc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CoverReadWordNum( Mvc_Cover_t * pCover ) { return pCover->nWords; } +int Mvc_CoverReadBitNum( Mvc_Cover_t * pCover ) { return pCover->nBits; } +int Mvc_CoverReadCubeNum( Mvc_Cover_t * pCover ) { return pCover->lCubes.nItems; } +Mvc_Cube_t * Mvc_CoverReadCubeHead( Mvc_Cover_t * pCover ) { return pCover->lCubes.pHead; } +Mvc_Cube_t * Mvc_CoverReadCubeTail( Mvc_Cover_t * pCover ) { return pCover->lCubes.pTail; } +Mvc_List_t * Mvc_CoverReadCubeList( Mvc_Cover_t * pCover ) { return &pCover->lCubes; } + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_ListReadCubeNum( Mvc_List_t * pList ) { return pList->nItems; } +Mvc_Cube_t * Mvc_ListReadCubeHead( Mvc_List_t * pList ) { return pList->pHead; } +Mvc_Cube_t * Mvc_ListReadCubeTail( Mvc_List_t * pList ) { return pList->pTail; } + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverSetCubeNum( Mvc_Cover_t * pCover,int nItems ) { pCover->lCubes.nItems = nItems; } +void Mvc_CoverSetCubeHead( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ) { pCover->lCubes.pHead = pCube; } +void Mvc_CoverSetCubeTail( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ) { pCover->lCubes.pTail = pCube; } +void Mvc_CoverSetCubeList( Mvc_Cover_t * pCover, Mvc_List_t * pList ) { pCover->lCubes = *pList; } + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CoverIsEmpty( Mvc_Cover_t * pCover ) +{ + return Mvc_CoverReadCubeNum(pCover) == 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CoverIsTautology( Mvc_Cover_t * pCover ) +{ + Mvc_Cube_t * pCube; + int iBit, Value; + + if ( Mvc_CoverReadCubeNum(pCover) != 1 ) + return 0; + + pCube = Mvc_CoverReadCubeHead( pCover ); + Mvc_CubeForEachBit( pCover, pCube, iBit, Value ) + if ( Value == 0 ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the cover is a binary buffer.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CoverIsBinaryBuffer( Mvc_Cover_t * pCover ) +{ + Mvc_Cube_t * pCube; + if ( pCover->nBits != 2 ) + return 0; + if ( Mvc_CoverReadCubeNum(pCover) != 1 ) + return 0; + pCube = pCover->lCubes.pHead; + if ( Mvc_CubeBitValue(pCube, 0) == 0 && Mvc_CubeBitValue(pCube, 1) == 1 ) + return 1; + return 0; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverMakeEmpty( Mvc_Cover_t * pCover ) +{ + Mvc_Cube_t * pCube, * pCube2; + Mvc_CoverForEachCubeSafe( pCover, pCube, pCube2 ) + Mvc_CubeFree( pCover, pCube ); + pCover->lCubes.nItems = 0; + pCover->lCubes.pHead = NULL; + pCover->lCubes.pTail = NULL; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverMakeTautology( Mvc_Cover_t * pCover ) +{ + Mvc_Cube_t * pCubeNew; + Mvc_CoverMakeEmpty( pCover ); + pCubeNew = Mvc_CubeAlloc( pCover ); + Mvc_CubeBitFill( pCubeNew ); + Mvc_CoverAddCubeTail( pCover, pCubeNew ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Cover_t * Mvc_CoverCreateEmpty( Mvc_Cover_t * pCover ) +{ + Mvc_Cover_t * pCoverNew; + pCoverNew = Mvc_CoverAlloc( pCover->pMem, pCover->nBits ); + return pCoverNew; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Cover_t * Mvc_CoverCreateTautology( Mvc_Cover_t * pCover ) +{ + Mvc_Cube_t * pCubeNew; + Mvc_Cover_t * pCoverNew; + pCoverNew = Mvc_CoverAlloc( pCover->pMem, pCover->nBits ); + pCubeNew = Mvc_CubeAlloc( pCoverNew ); + Mvc_CubeBitFill( pCubeNew ); + Mvc_CoverAddCubeTail( pCoverNew, pCubeNew ); + return pCoverNew; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/mvc/mvcCompare.c b/abc_with_bb_support/src/misc/mvc/mvcCompare.c new file mode 100644 index 000000000..ab521822d --- /dev/null +++ b/abc_with_bb_support/src/misc/mvc/mvcCompare.c @@ -0,0 +1,369 @@ +/**CFile**************************************************************** + + FileName [mvcCompare.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Various cube comparison functions.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: mvcCompare.c,v 1.5 2003/04/03 23:25:41 alanmi Exp $] + +***********************************************************************/ + +#include "mvc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Compares two cubes according to their integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CubeCompareInt( Mvc_Cube_t * pC1, Mvc_Cube_t * pC2, Mvc_Cube_t * pMask ) +{ + if ( Mvc_Cube1Words(pC1) ) + { + if ( pC1->pData[0] < pC2->pData[0] ) + return -1; + if ( pC1->pData[0] > pC2->pData[0] ) + return 1; + return 0; + } + else if ( Mvc_Cube2Words(pC1) ) + { + if ( pC1->pData[1] < pC2->pData[1] ) + return -1; + if ( pC1->pData[1] > pC2->pData[1] ) + return 1; + if ( pC1->pData[0] < pC2->pData[0] ) + return -1; + if ( pC1->pData[0] > pC2->pData[0] ) + return 1; + return 0; + } + else + { + int i = Mvc_CubeReadLast(pC1); + for(; i >= 0; i--) + { + if ( pC1->pData[i] < pC2->pData[i] ) + return -1; + if ( pC1->pData[i] > pC2->pData[i] ) + return 1; + } + return 0; + } +} + + +/**Function************************************************************* + + Synopsis [Compares the cubes (1) by size, (2) by integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CubeCompareSizeAndInt( Mvc_Cube_t * pC1, Mvc_Cube_t * pC2, Mvc_Cube_t * pMask ) +{ + // compare the cubes by size + if ( Mvc_CubeReadSize( pC1 ) < Mvc_CubeReadSize( pC2 ) ) + return 1; + if ( Mvc_CubeReadSize( pC1 ) > Mvc_CubeReadSize( pC2 ) ) + return -1; + // the cubes have the same size + + // compare the cubes as integers + if ( Mvc_Cube1Words( pC1 ) ) + { + if ( pC1->pData[0] < pC2->pData[0] ) + return -1; + if ( pC1->pData[0] > pC2->pData[0] ) + return 1; + return 0; + } + else if ( Mvc_Cube2Words( pC1 ) ) + { + if ( pC1->pData[1] < pC2->pData[1] ) + return -1; + if ( pC1->pData[1] > pC2->pData[1] ) + return 1; + if ( pC1->pData[0] < pC2->pData[0] ) + return -1; + if ( pC1->pData[0] > pC2->pData[0] ) + return 1; + return 0; + } + else + { + int i = Mvc_CubeReadLast( pC1 ); + for(; i >= 0; i--) + { + if ( pC1->pData[i] < pC2->pData[i] ) + return -1; + if ( pC1->pData[i] > pC2->pData[i] ) + return 1; + } + return 0; + } +} + +/**Function************************************************************* + + Synopsis [Compares two cubes under the mask.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CubeCompareIntUnderMask( Mvc_Cube_t * pC1, Mvc_Cube_t * pC2, Mvc_Cube_t * pMask ) +{ + unsigned uBits1, uBits2; + + // compare the cubes under the mask + if ( Mvc_Cube1Words(pC1) ) + { + uBits1 = pC1->pData[0] & pMask->pData[0]; + uBits2 = pC2->pData[0] & pMask->pData[0]; + if ( uBits1 < uBits2 ) + return -1; + if ( uBits1 > uBits2 ) + return 1; + // cubes are equal + return 0; + } + else if ( Mvc_Cube2Words(pC1) ) + { + uBits1 = pC1->pData[1] & pMask->pData[1]; + uBits2 = pC2->pData[1] & pMask->pData[1]; + if ( uBits1 < uBits2 ) + return -1; + if ( uBits1 > uBits2 ) + return 1; + uBits1 = pC1->pData[0] & pMask->pData[0]; + uBits2 = pC2->pData[0] & pMask->pData[0]; + if ( uBits1 < uBits2 ) + return -1; + if ( uBits1 > uBits2 ) + return 1; + return 0; + } + else + { + int i = Mvc_CubeReadLast(pC1); + for(; i >= 0; i--) + { + uBits1 = pC1->pData[i] & pMask->pData[i]; + uBits2 = pC2->pData[i] & pMask->pData[i]; + if ( uBits1 < uBits2 ) + return -1; + if ( uBits1 > uBits2 ) + return 1; + } + return 0; + } +} + +/**Function************************************************************* + + Synopsis [Compares two cubes under the mask.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CubeCompareIntOutsideMask( Mvc_Cube_t * pC1, Mvc_Cube_t * pC2, Mvc_Cube_t * pMask ) +{ + unsigned uBits1, uBits2; + + // compare the cubes under the mask + if ( Mvc_Cube1Words(pC1) ) + { + uBits1 = pC1->pData[0] | pMask->pData[0]; + uBits2 = pC2->pData[0] | pMask->pData[0]; + if ( uBits1 < uBits2 ) + return -1; + if ( uBits1 > uBits2 ) + return 1; + // cubes are equal + return 0; + } + else if ( Mvc_Cube2Words(pC1) ) + { + uBits1 = pC1->pData[1] | pMask->pData[1]; + uBits2 = pC2->pData[1] | pMask->pData[1]; + if ( uBits1 < uBits2 ) + return -1; + if ( uBits1 > uBits2 ) + return 1; + uBits1 = pC1->pData[0] | pMask->pData[0]; + uBits2 = pC2->pData[0] | pMask->pData[0]; + if ( uBits1 < uBits2 ) + return -1; + if ( uBits1 > uBits2 ) + return 1; + return 0; + } + else + { + int i = Mvc_CubeReadLast(pC1); + for(; i >= 0; i--) + { + uBits1 = pC1->pData[i] | pMask->pData[i]; + uBits2 = pC2->pData[i] | pMask->pData[i]; + if ( uBits1 < uBits2 ) + return -1; + if ( uBits1 > uBits2 ) + return 1; + } + return 0; + } +} + + +/**Function************************************************************* + + Synopsis [Compares the cubes (1) outside the mask, (2) under the mask.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CubeCompareIntOutsideAndUnderMask( Mvc_Cube_t * pC1, Mvc_Cube_t * pC2, Mvc_Cube_t * pMask ) +{ + unsigned uBits1, uBits2; + + if ( Mvc_Cube1Words(pC1) ) + { + // compare the cubes outside the mask + uBits1 = pC1->pData[0] & ~(pMask->pData[0]); + uBits2 = pC2->pData[0] & ~(pMask->pData[0]); + if ( uBits1 < uBits2 ) + return -1; + if ( uBits1 > uBits2 ) + return 1; + + // compare the cubes under the mask + uBits1 = pC1->pData[0] & pMask->pData[0]; + uBits2 = pC2->pData[0] & pMask->pData[0]; + if ( uBits1 < uBits2 ) + return -1; + if ( uBits1 > uBits2 ) + return 1; + // cubes are equal + // should never happen + assert( 0 ); + return 0; + } + else if ( Mvc_Cube2Words(pC1) ) + { + // compare the cubes outside the mask + uBits1 = pC1->pData[1] & ~(pMask->pData[1]); + uBits2 = pC2->pData[1] & ~(pMask->pData[1]); + if ( uBits1 < uBits2 ) + return -1; + if ( uBits1 > uBits2 ) + return 1; + + uBits1 = pC1->pData[0] & ~(pMask->pData[0]); + uBits2 = pC2->pData[0] & ~(pMask->pData[0]); + if ( uBits1 < uBits2 ) + return -1; + if ( uBits1 > uBits2 ) + return 1; + + // compare the cubes under the mask + uBits1 = pC1->pData[1] & pMask->pData[1]; + uBits2 = pC2->pData[1] & pMask->pData[1]; + if ( uBits1 < uBits2 ) + return -1; + if ( uBits1 > uBits2 ) + return 1; + + uBits1 = pC1->pData[0] & pMask->pData[0]; + uBits2 = pC2->pData[0] & pMask->pData[0]; + if ( uBits1 < uBits2 ) + return -1; + if ( uBits1 > uBits2 ) + return 1; + + // cubes are equal + // should never happen + assert( 0 ); + return 0; + } + else + { + int i; + + // compare the cubes outside the mask + for( i = Mvc_CubeReadLast(pC1); i >= 0; i-- ) + { + uBits1 = pC1->pData[i] & ~(pMask->pData[i]); + uBits2 = pC2->pData[i] & ~(pMask->pData[i]); + if ( uBits1 < uBits2 ) + return -1; + if ( uBits1 > uBits2 ) + return 1; + } + // compare the cubes under the mask + for( i = Mvc_CubeReadLast(pC1); i >= 0; i-- ) + { + uBits1 = pC1->pData[i] & pMask->pData[i]; + uBits2 = pC2->pData[i] & pMask->pData[i]; + if ( uBits1 < uBits2 ) + return -1; + if ( uBits1 > uBits2 ) + return 1; + } +/* + { + Mvc_Cover_t * pCover; + pCover = Mvc_CoverAlloc( NULL, 96 ); + Mvc_CubePrint( pCover, pC1 ); + Mvc_CubePrint( pCover, pC2 ); + Mvc_CubePrint( pCover, pMask ); + } +*/ + // cubes are equal + // should never happen + assert( 0 ); + return 0; + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/mvc/mvcContain.c b/abc_with_bb_support/src/misc/mvc/mvcContain.c new file mode 100644 index 000000000..940667b4e --- /dev/null +++ b/abc_with_bb_support/src/misc/mvc/mvcContain.c @@ -0,0 +1,173 @@ +/**CFile**************************************************************** + + FileName [mvcContain.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Making the cover single-cube containment free.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: mvcContain.c,v 1.4 2003/04/03 23:25:42 alanmi Exp $] + +***********************************************************************/ + +#include "mvc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Mvc_CoverRemoveDuplicates( Mvc_Cover_t * pCover ); +static void Mvc_CoverRemoveContained( Mvc_Cover_t * pCover ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + + +/**Function************************************************************* + + Synopsis [Removes the contained cubes.] + + Description [Returns 1 if the cover has been changed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CoverContain( Mvc_Cover_t * pCover ) +{ + int nCubes; + nCubes = Mvc_CoverReadCubeNum( pCover ); + if ( nCubes < 2 ) + return 0; + Mvc_CoverSetCubeSizes(pCover); + Mvc_CoverSort( pCover, NULL, Mvc_CubeCompareSizeAndInt ); + Mvc_CoverRemoveDuplicates( pCover ); + if ( nCubes > 1 ) + Mvc_CoverRemoveContained( pCover ); + return (nCubes != Mvc_CoverReadCubeNum(pCover)); +} + +/**Function************************************************************* + + Synopsis [Removes adjacent duplicated cubes from the cube list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverRemoveDuplicates( Mvc_Cover_t * pCover ) +{ + Mvc_Cube_t * pPrev, * pCube, * pCube2; + int fEqual; + + // set the first cube of the cover + pPrev = Mvc_CoverReadCubeHead(pCover); + // go through all the cubes after this one + Mvc_CoverForEachCubeStartSafe( Mvc_CubeReadNext(pPrev), pCube, pCube2 ) + { + // compare the current cube with the prev cube + Mvc_CubeBitEqual( fEqual, pPrev, pCube ); + if ( fEqual ) + { // they are equal - remove the current cube + Mvc_CoverDeleteCube( pCover, pPrev, pCube ); + Mvc_CubeFree( pCover, pCube ); + // don't change the previous cube cube + } + else + { // they are not equal - update the previous cube + pPrev = pCube; + } + } +} + +/**Function************************************************************* + + Synopsis [Removes contained cubes from the sorted cube list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverRemoveContained( Mvc_Cover_t * pCover ) +{ + Mvc_Cube_t * pCubeBeg, * pCubeEnd, * pCubeLarge; + Mvc_Cube_t * pCube, * pCube2, * pPrev; + unsigned sizeCur; + int Result; + + // since the cubes are sorted by size, it is sufficient + // to compare each cube with other cubes that have larger sizes + // if the given cube implies a larger cube, the larger cube is removed + pCubeBeg = Mvc_CoverReadCubeHead(pCover); + do + { + // get the current cube size + sizeCur = Mvc_CubeReadSize(pCubeBeg); + + // initialize the end of the given size group + pCubeEnd = pCubeBeg; + // find the beginning of the next size group + Mvc_CoverForEachCubeStart( Mvc_CubeReadNext(pCubeBeg), pCube ) + { + if ( sizeCur == Mvc_CubeReadSize(pCube) ) + pCubeEnd = pCube; + else // pCube is the first cube in the new size group + break; + } + // if we could not find the next size group + // the containment check is finished + if ( pCube == NULL ) + break; + // otherwise, pCubeBeg/pCubeEnd are the first/last cubes of the group + + // go through all the cubes between pCubeBeg and pCubeEnd, inclusive, + // and for each of them, try removing cubes after pCubeEnd + Mvc_CoverForEachCubeStart( pCubeBeg, pCubeLarge ) + { + pPrev = pCubeEnd; + Mvc_CoverForEachCubeStartSafe( Mvc_CubeReadNext(pCubeEnd), pCube, pCube2 ) + { + // check containment + Mvc_CubeBitNotImpl( Result, pCube, pCubeLarge ); + if ( !Result ) + { // pCubeLarge implies pCube - remove pCube + Mvc_CoverDeleteCube( pCover, pPrev, pCube ); + Mvc_CubeFree( pCover, pCube ); + // don't update the previous cube + } + else + { // update the previous cube + pPrev = pCube; + } + } + // quit, if the main cube was the last one of this size + if ( pCubeLarge == pCubeEnd ) + break; + } + + // set the beginning of the next group + pCubeBeg = Mvc_CubeReadNext(pCubeEnd); + } + while ( pCubeBeg ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/mvc/mvcCover.c b/abc_with_bb_support/src/misc/mvc/mvcCover.c new file mode 100644 index 000000000..8a1098c97 --- /dev/null +++ b/abc_with_bb_support/src/misc/mvc/mvcCover.c @@ -0,0 +1,251 @@ +/**CFile**************************************************************** + + FileName [mvcCover.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Basic procedures to manipulate unate cube covers.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: mvcCover.c,v 1.5 2003/04/09 18:02:05 alanmi Exp $] + +***********************************************************************/ + +#include "mvc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Cover_t * Mvc_CoverAlloc( Mvc_Manager_t * pMem, int nBits ) +{ + Mvc_Cover_t * p; + int nBitsInUnsigned; + + nBitsInUnsigned = 8 * sizeof(Mvc_CubeWord_t); +#ifdef USE_SYSTEM_MEMORY_MANAGEMENT + p = (Mvc_Cover_t *)malloc( sizeof(Mvc_Cover_t) ); +#else + p = (Mvc_Cover_t *)Extra_MmFixedEntryFetch( pMem->pManC ); +#endif + p->pMem = pMem; + p->nBits = nBits; + p->nWords = nBits / nBitsInUnsigned + (int)(nBits % nBitsInUnsigned > 0); + p->nUnused = p->nWords * nBitsInUnsigned - p->nBits; + p->lCubes.nItems = 0; + p->lCubes.pHead = NULL; + p->lCubes.pTail = NULL; + p->nCubesAlloc = 0; + p->pCubes = NULL; + p->pMask = NULL; + p->pLits = NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Cover_t * Mvc_CoverClone( Mvc_Cover_t * p ) +{ + Mvc_Cover_t * pCover; +#ifdef USE_SYSTEM_MEMORY_MANAGEMENT + pCover = (Mvc_Cover_t *)malloc( sizeof(Mvc_Cover_t) ); +#else + pCover = (Mvc_Cover_t *)Extra_MmFixedEntryFetch( p->pMem->pManC ); +#endif + pCover->pMem = p->pMem; + pCover->nBits = p->nBits; + pCover->nWords = p->nWords; + pCover->nUnused = p->nUnused; + pCover->lCubes.nItems = 0; + pCover->lCubes.pHead = NULL; + pCover->lCubes.pTail = NULL; + pCover->nCubesAlloc = 0; + pCover->pCubes = NULL; + pCover->pMask = NULL; + pCover->pLits = NULL; + return pCover; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Cover_t * Mvc_CoverDup( Mvc_Cover_t * p ) +{ + Mvc_Cover_t * pCover; + Mvc_Cube_t * pCube, * pCubeCopy; + // clone the cover + pCover = Mvc_CoverClone( p ); + // copy the cube list + Mvc_CoverForEachCube( p, pCube ) + { + pCubeCopy = Mvc_CubeDup( p, pCube ); + Mvc_CoverAddCubeTail( pCover, pCubeCopy ); + } + return pCover; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverFree( Mvc_Cover_t * p ) +{ + Mvc_Cube_t * pCube, * pCube2; + // recycle cube list + Mvc_CoverForEachCubeSafe( p, pCube, pCube2 ) + Mvc_CubeFree( p, pCube ); + // recycle other pointers + Mvc_CubeFree( p, p->pMask ); + MEM_FREE( p->pMem, Mvc_Cube_t *, p->nCubesAlloc, p->pCubes ); + MEM_FREE( p->pMem, int, p->nBits, p->pLits ); + +#ifdef USE_SYSTEM_MEMORY_MANAGEMENT + free( p ); +#else + Extra_MmFixedEntryRecycle( p->pMem->pManC, (char *)p ); +#endif +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverAllocateMask( Mvc_Cover_t * pCover ) +{ + if ( pCover->pMask == NULL ) + pCover->pMask = Mvc_CubeAlloc( pCover ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverAllocateArrayLits( Mvc_Cover_t * pCover ) +{ + if ( pCover->pLits == NULL ) + pCover->pLits = MEM_ALLOC( pCover->pMem, int, pCover->nBits ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverAllocateArrayCubes( Mvc_Cover_t * pCover ) +{ + if ( pCover->nCubesAlloc < pCover->lCubes.nItems ) + { + if ( pCover->nCubesAlloc > 0 ) + MEM_FREE( pCover->pMem, Mvc_Cube_t *, pCover->nCubesAlloc, pCover->pCubes ); + pCover->nCubesAlloc = pCover->lCubes.nItems; + pCover->pCubes = MEM_ALLOC( pCover->pMem, Mvc_Cube_t *, pCover->nCubesAlloc ); + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverDeallocateMask( Mvc_Cover_t * pCover ) +{ + Mvc_CubeFree( pCover, pCover->pMask ); + pCover->pMask = NULL; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverDeallocateArrayLits( Mvc_Cover_t * pCover ) +{ + if ( pCover->pLits ) + { + MEM_FREE( pCover->pMem, int, pCover->nBits, pCover->pLits ); + pCover->pLits = NULL; + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/mvc/mvcCube.c b/abc_with_bb_support/src/misc/mvc/mvcCube.c new file mode 100644 index 000000000..1886acc74 --- /dev/null +++ b/abc_with_bb_support/src/misc/mvc/mvcCube.c @@ -0,0 +1,175 @@ +/**CFile**************************************************************** + + FileName [mvcCube.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Manipulating unate cubes.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: mvcCube.c,v 1.4 2003/04/03 06:31:49 alanmi Exp $] + +***********************************************************************/ + +#include "mvc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Cube_t * Mvc_CubeAlloc( Mvc_Cover_t * pCover ) +{ + Mvc_Cube_t * pCube; + + assert( pCover->nWords >= 0 ); + // allocate the cube +#ifdef USE_SYSTEM_MEMORY_MANAGEMENT + if ( pCover->nWords == 0 ) + pCube = (Mvc_Cube_t *)malloc( sizeof(Mvc_Cube_t) ); + else + pCube = (Mvc_Cube_t *)malloc( sizeof(Mvc_Cube_t) + sizeof(Mvc_CubeWord_t) * (pCover->nWords - 1) ); +#else + switch( pCover->nWords ) + { + case 0: + case 1: + pCube = (Mvc_Cube_t *)Extra_MmFixedEntryFetch( pCover->pMem->pMan1 ); + break; + case 2: + pCube = (Mvc_Cube_t *)Extra_MmFixedEntryFetch( pCover->pMem->pMan2 ); + break; + case 3: + case 4: + pCube = (Mvc_Cube_t *)Extra_MmFixedEntryFetch( pCover->pMem->pMan4 ); + break; + default: + pCube = (Mvc_Cube_t *)malloc( sizeof(Mvc_Cube_t) + sizeof(Mvc_CubeWord_t) * (pCover->nWords - 1) ); + break; + } +#endif + // set the parameters charactering this cube + if ( pCover->nWords == 0 ) + pCube->iLast = pCover->nWords; + else + pCube->iLast = pCover->nWords - 1; + pCube->nUnused = pCover->nUnused; + return pCube; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Cube_t * Mvc_CubeDup( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ) +{ + Mvc_Cube_t * pCubeCopy; + pCubeCopy = Mvc_CubeAlloc( pCover ); + Mvc_CubeBitCopy( pCubeCopy, pCube ); + return pCubeCopy; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CubeFree( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ) +{ + if ( pCube == NULL ) + return; + + // verify the parameters charactering this cube + assert( pCube->iLast == 0 || ((int)pCube->iLast) == pCover->nWords - 1 ); + assert( ((int)pCube->nUnused) == pCover->nUnused ); + + // deallocate the cube +#ifdef USE_SYSTEM_MEMORY_MANAGEMENT + free( pCube ); +#else + switch( pCover->nWords ) + { + case 0: + case 1: + Extra_MmFixedEntryRecycle( pCover->pMem->pMan1, (char *)pCube ); + break; + case 2: + Extra_MmFixedEntryRecycle( pCover->pMem->pMan2, (char *)pCube ); + break; + case 3: + case 4: + Extra_MmFixedEntryRecycle( pCover->pMem->pMan4, (char *)pCube ); + break; + default: + free( pCube ); + break; + } +#endif +} + + +/**Function************************************************************* + + Synopsis [Removes the don't-care variable from the cube.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CubeBitRemoveDcs( Mvc_Cube_t * pCube ) +{ + unsigned Mask; + int i; + for ( i = Mvc_CubeReadLast(pCube); i >= 0; i-- ) + { + // detect those variables that are different (not DCs) + Mask = (pCube->pData[i] ^ (pCube->pData[i] >> 1)) & BITS_DISJOINT; + // create the mask of all that are different + Mask |= (Mask << 1); + // remove other bits from the set + pCube->pData[i] &= Mask; + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/mvc/mvcDivide.c b/abc_with_bb_support/src/misc/mvc/mvcDivide.c new file mode 100644 index 000000000..ea8b4beda --- /dev/null +++ b/abc_with_bb_support/src/misc/mvc/mvcDivide.c @@ -0,0 +1,436 @@ +/**CFile**************************************************************** + + FileName [mvcDivide.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Procedures for algebraic division.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: mvcDivide.c,v 1.5 2003/04/26 20:41:36 alanmi Exp $] + +***********************************************************************/ + +#include "mvc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Mvc_CoverVerifyDivision( Mvc_Cover_t * pCover, Mvc_Cover_t * pDiv, Mvc_Cover_t * pQuo, Mvc_Cover_t * pRem ); + +int s_fVerbose = 0; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverDivide( Mvc_Cover_t * pCover, Mvc_Cover_t * pDiv, Mvc_Cover_t ** ppQuo, Mvc_Cover_t ** ppRem ) +{ + // check the number of cubes + if ( Mvc_CoverReadCubeNum( pCover ) < Mvc_CoverReadCubeNum( pDiv ) ) + { + *ppQuo = NULL; + *ppRem = NULL; + return; + } + + // make sure that support of pCover contains that of pDiv + if ( !Mvc_CoverCheckSuppContainment( pCover, pDiv ) ) + { + *ppQuo = NULL; + *ppRem = NULL; + return; + } + + // perform the general division + Mvc_CoverDivideInternal( pCover, pDiv, ppQuo, ppRem ); +} + + +/**Function************************************************************* + + Synopsis [Merge the cubes inside the groups.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverDivideInternal( Mvc_Cover_t * pCover, Mvc_Cover_t * pDiv, Mvc_Cover_t ** ppQuo, Mvc_Cover_t ** ppRem ) +{ + Mvc_Cover_t * pQuo, * pRem; + Mvc_Cube_t * pCubeC, * pCubeD, * pCubeCopy; + Mvc_Cube_t * pCube1, * pCube2; + int * pGroups, nGroups; // the cube groups + int nCubesC, nCubesD, nMerges, iCubeC, iCubeD, iMerge; + int fSkipG, GroupSize, g, c, RetValue; + int nCubes; + + // get cover sizes + nCubesD = Mvc_CoverReadCubeNum( pDiv ); + nCubesC = Mvc_CoverReadCubeNum( pCover ); + + // check trivial cases + if ( nCubesD == 1 ) + { + if ( Mvc_CoverIsOneLiteral( pDiv ) ) + Mvc_CoverDivideByLiteral( pCover, pDiv, ppQuo, ppRem ); + else + Mvc_CoverDivideByCube( pCover, pDiv, ppQuo, ppRem ); + return; + } + + // create the divisor and the remainder + pQuo = Mvc_CoverAlloc( pCover->pMem, pCover->nBits ); + pRem = Mvc_CoverAlloc( pCover->pMem, pCover->nBits ); + + // get the support of the divisor + Mvc_CoverAllocateMask( pDiv ); + Mvc_CoverSupport( pDiv, pDiv->pMask ); + + // sort the cubes of the divisor + Mvc_CoverSort( pDiv, NULL, Mvc_CubeCompareInt ); + // sort the cubes of the cover + Mvc_CoverSort( pCover, pDiv->pMask, Mvc_CubeCompareIntOutsideAndUnderMask ); + + // allocate storage for cube groups + pGroups = MEM_ALLOC( pCover->pMem, int, nCubesC + 1 ); + + // mask contains variables in the support of Div + // split the cubes into groups using the mask + Mvc_CoverList2Array( pCover ); + Mvc_CoverList2Array( pDiv ); + pGroups[0] = 0; + nGroups = 1; + for ( c = 1; c < nCubesC; c++ ) + { + // get the cubes + pCube1 = pCover->pCubes[c-1]; + pCube2 = pCover->pCubes[c ]; + // compare the cubes + Mvc_CubeBitEqualOutsideMask( RetValue, pCube1, pCube2, pDiv->pMask ); + if ( !RetValue ) + pGroups[nGroups++] = c; + } + // finish off the last group + pGroups[nGroups] = nCubesC; + + // consider each group separately and decide + // whether it can produce a quotient cube + nCubes = 0; + for ( g = 0; g < nGroups; g++ ) + { + // if the group has less than nCubesD cubes, + // there is no way it can produce the quotient cube + // copy the cubes to the remainder + GroupSize = pGroups[g+1] - pGroups[g]; + if ( GroupSize < nCubesD ) + { + for ( c = pGroups[g]; c < pGroups[g+1]; c++ ) + { + pCubeCopy = Mvc_CubeDup( pRem, pCover->pCubes[c] ); + Mvc_CoverAddCubeTail( pRem, pCubeCopy ); + nCubes++; + } + continue; + } + + // mark the cubes as those that should be added to the remainder + for ( c = pGroups[g]; c < pGroups[g+1]; c++ ) + Mvc_CubeSetSize( pCover->pCubes[c], 1 ); + + // go through the cubes in the group and at the same time + // go through the cubes in the divisor + iCubeD = 0; + iCubeC = 0; + pCubeD = pDiv->pCubes[iCubeD++]; + pCubeC = pCover->pCubes[pGroups[g]+iCubeC++]; + fSkipG = 0; + nMerges = 0; + + while ( 1 ) + { + // compare the topmost cubes in F and in D + RetValue = Mvc_CubeCompareIntUnderMask( pCubeC, pCubeD, pDiv->pMask ); + // cube are ordered in increasing order of their int value + if ( RetValue == -1 ) // pCubeC is above pCubeD + { // cube in C should be added to the remainder + // check that there is enough cubes in the group + if ( GroupSize - iCubeC < nCubesD - nMerges ) + { + fSkipG = 1; + break; + } + // get the next cube in the cover + pCubeC = pCover->pCubes[pGroups[g]+iCubeC++]; + continue; + } + if ( RetValue == 1 ) // pCubeD is above pCubeC + { // given cube in D does not have a corresponding cube in the cover + fSkipG = 1; + break; + } + // mark the cube as the one that should NOT be added to the remainder + Mvc_CubeSetSize( pCubeC, 0 ); + // remember this merged cube + iMerge = iCubeC-1; + nMerges++; + + // stop if we considered the last cube of the group + if ( iCubeD == nCubesD ) + break; + + // advance the cube of the divisor + assert( iCubeD < nCubesD ); + pCubeD = pDiv->pCubes[iCubeD++]; + + // advance the cube of the group + assert( pGroups[g]+iCubeC < nCubesC ); + pCubeC = pCover->pCubes[pGroups[g]+iCubeC++]; + } + + if ( fSkipG ) + { + // the group has failed, add all the cubes to the remainder + for ( c = pGroups[g]; c < pGroups[g+1]; c++ ) + { + pCubeCopy = Mvc_CubeDup( pRem, pCover->pCubes[c] ); + Mvc_CoverAddCubeTail( pRem, pCubeCopy ); + nCubes++; + } + continue; + } + + // the group has worked, add left-over cubes to the remainder + for ( c = pGroups[g]; c < pGroups[g+1]; c++ ) + { + pCubeC = pCover->pCubes[c]; + if ( Mvc_CubeReadSize(pCubeC) ) + { + pCubeCopy = Mvc_CubeDup( pRem, pCubeC ); + Mvc_CoverAddCubeTail( pRem, pCubeCopy ); + nCubes++; + } + } + + // create the quotient cube + pCube1 = Mvc_CubeAlloc( pQuo ); + Mvc_CubeBitSharp( pCube1, pCover->pCubes[pGroups[g]+iMerge], pDiv->pMask ); + // add the cube to the quotient + Mvc_CoverAddCubeTail( pQuo, pCube1 ); + nCubes += nCubesD; + } + assert( nCubes == nCubesC ); + + // deallocate the memory + MEM_FREE( pCover->pMem, int, nCubesC + 1, pGroups ); + + // return the results + *ppRem = pRem; + *ppQuo = pQuo; +// Mvc_CoverVerifyDivision( pCover, pDiv, pQuo, pRem ); +} + + +/**Function************************************************************* + + Synopsis [Divides the cover by a cube.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverDivideByCube( Mvc_Cover_t * pCover, Mvc_Cover_t * pDiv, Mvc_Cover_t ** ppQuo, Mvc_Cover_t ** ppRem ) +{ + Mvc_Cover_t * pQuo, * pRem; + Mvc_Cube_t * pCubeC, * pCubeD, * pCubeCopy; + int CompResult; + + // get the only cube of D + assert( Mvc_CoverReadCubeNum(pDiv) == 1 ); + + // start the quotient and the remainder + pQuo = Mvc_CoverAlloc( pCover->pMem, pCover->nBits ); + pRem = Mvc_CoverAlloc( pCover->pMem, pCover->nBits ); + + // get the first and only cube of the divisor + pCubeD = Mvc_CoverReadCubeHead( pDiv ); + + // iterate through the cubes in the cover + Mvc_CoverForEachCube( pCover, pCubeC ) + { + // check the containment of literals from pCubeD in pCube + Mvc_Cube2BitNotImpl( CompResult, pCubeD, pCubeC ); + if ( !CompResult ) + { // this cube belongs to the quotient + // alloc the cube + pCubeCopy = Mvc_CubeAlloc( pQuo ); + // clean the support of D + Mvc_CubeBitSharp( pCubeCopy, pCubeC, pCubeD ); + // add the cube to the quotient + Mvc_CoverAddCubeTail( pQuo, pCubeCopy ); + } + else + { + // copy the cube + pCubeCopy = Mvc_CubeDup( pRem, pCubeC ); + // add the cube to the remainder + Mvc_CoverAddCubeTail( pRem, pCubeCopy ); + } + } + // return the results + *ppRem = pRem; + *ppQuo = pQuo; +} + +/**Function************************************************************* + + Synopsis [Divides the cover by a literal.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverDivideByLiteral( Mvc_Cover_t * pCover, Mvc_Cover_t * pDiv, Mvc_Cover_t ** ppQuo, Mvc_Cover_t ** ppRem ) +{ + Mvc_Cover_t * pQuo, * pRem; + Mvc_Cube_t * pCubeC, * pCubeCopy; + int iLit; + + // get the only cube of D + assert( Mvc_CoverReadCubeNum(pDiv) == 1 ); + + // start the quotient and the remainder + pQuo = Mvc_CoverAlloc( pCover->pMem, pCover->nBits ); + pRem = Mvc_CoverAlloc( pCover->pMem, pCover->nBits ); + + // get the first and only literal in the divisor cube + iLit = Mvc_CoverFirstCubeFirstLit( pDiv ); + + // iterate through the cubes in the cover + Mvc_CoverForEachCube( pCover, pCubeC ) + { + // copy the cube + pCubeCopy = Mvc_CubeDup( pCover, pCubeC ); + // add the cube to the quotient or to the remainder depending on the literal + if ( Mvc_CubeBitValue( pCubeCopy, iLit ) ) + { // remove the literal + Mvc_CubeBitRemove( pCubeCopy, iLit ); + // add the cube ot the quotient + Mvc_CoverAddCubeTail( pQuo, pCubeCopy ); + } + else + { // add the cube ot the remainder + Mvc_CoverAddCubeTail( pRem, pCubeCopy ); + } + } + // return the results + *ppRem = pRem; + *ppQuo = pQuo; +} + + +/**Function************************************************************* + + Synopsis [Derives the quotient of division by literal.] + + Description [Reduces the cover to be the equal to the result of + division of the given cover by the literal.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverDivideByLiteralQuo( Mvc_Cover_t * pCover, int iLit ) +{ + Mvc_Cube_t * pCube, * pCube2, * pPrev; + // delete those cubes that do not have this literal + // remove this literal from other cubes + pPrev = NULL; + Mvc_CoverForEachCubeSafe( pCover, pCube, pCube2 ) + { + if ( Mvc_CubeBitValue( pCube, iLit ) == 0 ) + { // delete the cube from the cover + Mvc_CoverDeleteCube( pCover, pPrev, pCube ); + Mvc_CubeFree( pCover, pCube ); + // don't update the previous cube + } + else + { // delete this literal from the cube + Mvc_CubeBitRemove( pCube, iLit ); + // update the previous cube + pPrev = pCube; + } + } +} + + +/**Function************************************************************* + + Synopsis [Verifies that the result of algebraic division is correct.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverVerifyDivision( Mvc_Cover_t * pCover, Mvc_Cover_t * pDiv, Mvc_Cover_t * pQuo, Mvc_Cover_t * pRem ) +{ + Mvc_Cover_t * pProd; + Mvc_Cover_t * pDiff; + + pProd = Mvc_CoverAlgebraicMultiply( pDiv, pQuo ); + pDiff = Mvc_CoverAlgebraicSubtract( pCover, pProd ); + + if ( Mvc_CoverAlgebraicEqual( pDiff, pRem ) ) + printf( "Verification OKAY!\n" ); + else + { + printf( "Verification FAILED!\n" ); + printf( "pCover:\n" ); + Mvc_CoverPrint( pCover ); + printf( "pDiv:\n" ); + Mvc_CoverPrint( pDiv ); + printf( "pRem:\n" ); + Mvc_CoverPrint( pRem ); + printf( "pQuo:\n" ); + Mvc_CoverPrint( pQuo ); + } + + Mvc_CoverFree( pProd ); + Mvc_CoverFree( pDiff ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/mvc/mvcDivisor.c b/abc_with_bb_support/src/misc/mvc/mvcDivisor.c new file mode 100644 index 000000000..576af5d55 --- /dev/null +++ b/abc_with_bb_support/src/misc/mvc/mvcDivisor.c @@ -0,0 +1,90 @@ +/**CFile**************************************************************** + + FileName [mvcDivisor.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Procedures for compute the quick divisor.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: mvcDivisor.c,v 1.1 2003/04/03 15:34:08 alanmi Exp $] + +***********************************************************************/ + +#include "mvc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Mvc_CoverDivisorZeroKernel( Mvc_Cover_t * pCover ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Returns the quick divisor of the cover.] + + Description [Returns NULL, if there is not divisor other than + trivial.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Cover_t * Mvc_CoverDivisor( Mvc_Cover_t * pCover ) +{ + Mvc_Cover_t * pKernel; + if ( Mvc_CoverReadCubeNum(pCover) <= 1 ) + return NULL; + // allocate the literal array and count literals + if ( Mvc_CoverAnyLiteral( pCover, NULL ) == -1 ) + return NULL; + // duplicate the cover + pKernel = Mvc_CoverDup(pCover); + // perform the kerneling + Mvc_CoverDivisorZeroKernel( pKernel ); + assert( Mvc_CoverReadCubeNum(pKernel) ); + return pKernel; +} + +/**Function************************************************************* + + Synopsis [Computes a level-zero kernel.] + + Description [Modifies the cover to contain one level-zero kernel.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverDivisorZeroKernel( Mvc_Cover_t * pCover ) +{ + int iLit; + // find any literal that occurs at least two times +// iLit = Mvc_CoverAnyLiteral( pCover, NULL ); + iLit = Mvc_CoverWorstLiteral( pCover, NULL ); +// iLit = Mvc_CoverBestLiteral( pCover, NULL ); + if ( iLit == -1 ) + return; + // derive the cube-free quotient + Mvc_CoverDivideByLiteralQuo( pCover, iLit ); // the same cover + Mvc_CoverMakeCubeFree( pCover ); // the same cover + // call recursively + Mvc_CoverDivisorZeroKernel( pCover ); // the same cover +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/mvc/mvcList.c b/abc_with_bb_support/src/misc/mvc/mvcList.c new file mode 100644 index 000000000..c673482c6 --- /dev/null +++ b/abc_with_bb_support/src/misc/mvc/mvcList.c @@ -0,0 +1,362 @@ +/**CFile**************************************************************** + + FileName [mvcList.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Manipulating list of cubes in the cover.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: mvcList.c,v 1.4 2003/04/03 06:31:50 alanmi Exp $] + +***********************************************************************/ + +#include "mvc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_ListAddCubeHead_( Mvc_List_t * pList, Mvc_Cube_t * pCube ) +{ + if ( pList->pHead == NULL ) + { + Mvc_CubeSetNext( pCube, NULL ); + pList->pHead = pCube; + pList->pTail = pCube; + } + else + { + Mvc_CubeSetNext( pCube, pList->pHead ); + pList->pHead = pCube; + } + pList->nItems++; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_ListAddCubeTail_( Mvc_List_t * pList, Mvc_Cube_t * pCube ) +{ + if ( pList->pHead == NULL ) + pList->pHead = pCube; + else + Mvc_CubeSetNext( pList->pTail, pCube ); + pList->pTail = pCube; + Mvc_CubeSetNext( pCube, NULL ); + pList->nItems++; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_ListDeleteCube_( Mvc_List_t * pList, Mvc_Cube_t * pPrev, Mvc_Cube_t * pCube ) +{ + if ( pPrev == NULL ) // deleting the head cube + pList->pHead = Mvc_CubeReadNext(pCube); + else + pPrev->pNext = pCube->pNext; + if ( pList->pTail == pCube ) // deleting the tail cube + { + assert( Mvc_CubeReadNext(pCube) == NULL ); + pList->pTail = pPrev; + } + pList->nItems--; +} + + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverAddCubeHead_( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ) +{ + Mvc_List_t * pList = &pCover->lCubes; + if ( pList->pHead == NULL ) + { + Mvc_CubeSetNext( pCube, NULL ); + pList->pHead = pCube; + pList->pTail = pCube; + } + else + { + Mvc_CubeSetNext( pCube, pList->pHead ); + pList->pHead = pCube; + } + pList->nItems++; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverAddCubeTail_( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ) +{ + Mvc_List_t * pList = &pCover->lCubes; + + if ( pList->pHead == NULL ) + pList->pHead = pCube; + else + Mvc_CubeSetNext( pList->pTail, pCube ); + pList->pTail = pCube; + Mvc_CubeSetNext( pCube, NULL ); + pList->nItems++; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverDeleteCube_( Mvc_Cover_t * pCover, Mvc_Cube_t * pPrev, Mvc_Cube_t * pCube ) +{ + Mvc_List_t * pList = &pCover->lCubes; + + if ( pPrev == NULL ) // deleting the head cube + pList->pHead = Mvc_CubeReadNext(pCube); + else + pPrev->pNext = pCube->pNext; + if ( pList->pTail == pCube ) // deleting the tail cube + { + assert( Mvc_CubeReadNext(pCube) == NULL ); + pList->pTail = pPrev; + } + pList->nItems--; +} + + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverAddDupCubeHead( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ) +{ + Mvc_Cube_t * pCubeNew; + pCubeNew = Mvc_CubeAlloc( pCover ); + Mvc_CubeBitCopy( pCubeNew, pCube ); + Mvc_CoverAddCubeHead( pCover, pCubeNew ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverAddDupCubeTail( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ) +{ + Mvc_Cube_t * pCubeNew; + // copy the cube as part of this cover + pCubeNew = Mvc_CubeAlloc( pCover ); + Mvc_CubeBitCopy( pCubeNew, pCube ); + // clean the last bits of the new cube +// pCubeNew->pData[pCubeNew->iLast] &= (BITS_FULL >> pCubeNew->nUnused); + // add the cube at the end + Mvc_CoverAddCubeTail( pCover, pCubeNew ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverAddLiteralsOfCube( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ) +{ +// int iBit, Value; +// assert( pCover->pLits ); +// Mvc_CubeForEachBit( pCover, pCube, iBit, Value ) +// if ( Value ) +// pCover->pLits[iBit] += Value; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverDeleteLiteralsOfCube( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ) +{ +// int iBit, Value; +// assert( pCover->pLits ); +// Mvc_CubeForEachBit( pCover, pCube, iBit, Value ) +// if ( Value ) +// pCover->pLits[iBit] -= Value; +} + + +/**Function************************************************************* + + Synopsis [Transfers the cubes from the list into the array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverList2Array( Mvc_Cover_t * pCover ) +{ + Mvc_Cube_t * pCube; + int Counter; + // resize storage if necessary + Mvc_CoverAllocateArrayCubes( pCover ); + // iterate through the cubes + Counter = 0; + Mvc_CoverForEachCube( pCover, pCube ) + pCover->pCubes[ Counter++ ] = pCube; + assert( Counter == Mvc_CoverReadCubeNum(pCover) ); +} + +/**Function************************************************************* + + Synopsis [Transfers the cubes from the array into list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverArray2List( Mvc_Cover_t * pCover ) +{ + Mvc_Cube_t * pCube; + int nCubes, i; + + assert( pCover->pCubes ); + + nCubes = Mvc_CoverReadCubeNum(pCover); + if ( nCubes == 0 ) + return; + if ( nCubes == 1 ) + { + pCube = pCover->pCubes[0]; + pCube->pNext = NULL; + pCover->lCubes.pHead = pCover->lCubes.pTail = pCube; + return; + } + // set up the first cube + pCube = pCover->pCubes[0]; + pCover->lCubes.pHead = pCube; + // set up the last cube + pCube = pCover->pCubes[nCubes-1]; + pCube->pNext = NULL; + pCover->lCubes.pTail = pCube; + + // link all cubes starting from the first one + for ( i = 0; i < nCubes - 1; i++ ) + pCover->pCubes[i]->pNext = pCover->pCubes[i+1]; +} + +/**Function************************************************************* + + Synopsis [Returns the tail of the linked list given by the head.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Cube_t * Mvc_ListGetTailFromHead( Mvc_Cube_t * pHead ) +{ + Mvc_Cube_t * pCube, * pTail; + for ( pTail = pCube = pHead; + pCube; + pTail = pCube, pCube = Mvc_CubeReadNext(pCube) ); + return pTail; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/mvc/mvcLits.c b/abc_with_bb_support/src/misc/mvc/mvcLits.c new file mode 100644 index 000000000..5d28d8719 --- /dev/null +++ b/abc_with_bb_support/src/misc/mvc/mvcLits.c @@ -0,0 +1,345 @@ +/**CFile**************************************************************** + + FileName [mvcLits.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Literal counting/updating procedures.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: mvcLits.c,v 1.4 2003/04/03 06:31:50 alanmi Exp $] + +***********************************************************************/ + +#include "mvc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Find the any literal that occurs more than once.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CoverAnyLiteral( Mvc_Cover_t * pCover, Mvc_Cube_t * pMask ) +{ + Mvc_Cube_t * pCube; + int nWord, nBit, i; + int nLitsCur; + int fUseFirst = 0; + + // go through each literal + if ( fUseFirst ) + { + for ( i = 0; i < pCover->nBits; i++ ) + if ( !pMask || Mvc_CubeBitValue(pMask,i) ) + { + // get the word and bit of this literal + nWord = Mvc_CubeWhichWord(i); + nBit = Mvc_CubeWhichBit(i); + // go through all the cubes + nLitsCur = 0; + Mvc_CoverForEachCube( pCover, pCube ) + if ( pCube->pData[nWord] & (1< 1 ) + return i; + } + } + } + else + { + for ( i = pCover->nBits - 1; i >=0; i-- ) + if ( !pMask || Mvc_CubeBitValue(pMask,i) ) + { + // get the word and bit of this literal + nWord = Mvc_CubeWhichWord(i); + nBit = Mvc_CubeWhichBit(i); + // go through all the cubes + nLitsCur = 0; + Mvc_CoverForEachCube( pCover, pCube ) + if ( pCube->pData[nWord] & (1< 1 ) + return i; + } + } + } + return -1; +} + +/**Function************************************************************* + + Synopsis [Find the most often occurring literal.] + + Description [Find the most often occurring literal among those + that occur more than once.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CoverBestLiteral( Mvc_Cover_t * pCover, Mvc_Cube_t * pMask ) +{ + Mvc_Cube_t * pCube; + int nWord, nBit; + int i, iMax, nLitsMax, nLitsCur; + int fUseFirst = 1; + + // go through each literal + iMax = -1; + nLitsMax = -1; + for ( i = 0; i < pCover->nBits; i++ ) + if ( !pMask || Mvc_CubeBitValue(pMask,i) ) + { + // get the word and bit of this literal + nWord = Mvc_CubeWhichWord(i); + nBit = Mvc_CubeWhichBit(i); + // go through all the cubes + nLitsCur = 0; + Mvc_CoverForEachCube( pCover, pCube ) + if ( pCube->pData[nWord] & (1< 1 ) + return iMax; + return -1; +} + +/**Function************************************************************* + + Synopsis [Find the most often occurring literal.] + + Description [Find the most often occurring literal among those + that occur more than once.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CoverWorstLiteral( Mvc_Cover_t * pCover, Mvc_Cube_t * pMask ) +{ + Mvc_Cube_t * pCube; + int nWord, nBit; + int i, iMin, nLitsMin, nLitsCur; + int fUseFirst = 1; + + // go through each literal + iMin = -1; + nLitsMin = 1000000; + for ( i = 0; i < pCover->nBits; i++ ) + if ( !pMask || Mvc_CubeBitValue(pMask,i) ) + { + // get the word and bit of this literal + nWord = Mvc_CubeWhichWord(i); + nBit = Mvc_CubeWhichBit(i); + // go through all the cubes + nLitsCur = 0; + Mvc_CoverForEachCube( pCover, pCube ) + if ( pCube->pData[nWord] & (1< nLitsCur ) + { + nLitsMin = nLitsCur; + iMin = i; + } + } + else + { + if ( nLitsMin >= nLitsCur ) + { + nLitsMin = nLitsCur; + iMin = i; + } + } + } + + if ( nLitsMin < 1000000 ) + return iMin; + return -1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Cover_t * Mvc_CoverBestLiteralCover( Mvc_Cover_t * pCover, Mvc_Cover_t * pSimple ) +{ + Mvc_Cover_t * pCoverNew; + Mvc_Cube_t * pCubeNew; + Mvc_Cube_t * pCubeS; + int iLitBest; + + // create the new cover + pCoverNew = Mvc_CoverClone( pCover ); + // get the new cube + pCubeNew = Mvc_CubeAlloc( pCoverNew ); + // clean the cube + Mvc_CubeBitClean( pCubeNew ); + + // get the first cube of pSimple + assert( Mvc_CoverReadCubeNum(pSimple) == 1 ); + pCubeS = Mvc_CoverReadCubeHead( pSimple ); + // find the best literal among those of pCubeS + iLitBest = Mvc_CoverBestLiteral( pCover, pCubeS ); + + // insert this literal into the cube + Mvc_CubeBitInsert( pCubeNew, iLitBest ); + // add the cube to the cover + Mvc_CoverAddCubeTail( pCoverNew, pCubeNew ); + return pCoverNew; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CoverFirstCubeFirstLit( Mvc_Cover_t * pCover ) +{ + Mvc_Cube_t * pCube; + int iBit, Value; + + // get the first cube + pCube = Mvc_CoverReadCubeHead( pCover ); + // get the first literal + Mvc_CubeForEachBit( pCover, pCube, iBit, Value ) + if ( Value ) + return iBit; + return -1; +} + +/**Function************************************************************* + + Synopsis [Returns the number of literals in the cover.] + + Description [Allocates storage for literal counters and fills it up + using the current information.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CoverCountLiterals( Mvc_Cover_t * pCover ) +{ + Mvc_Cube_t * pCube; + int nWord, nBit; + int i, CounterTot, CounterCur; + + // allocate/clean the storage for literals +// Mvc_CoverAllocateArrayLits( pCover ); +// memset( pCover->pLits, 0, pCover->nBits * sizeof(int) ); + // go through each literal + CounterTot = 0; + for ( i = 0; i < pCover->nBits; i++ ) + { + // get the word and bit of this literal + nWord = Mvc_CubeWhichWord(i); + nBit = Mvc_CubeWhichBit(i); + // go through all the cubes + CounterCur = 0; + Mvc_CoverForEachCube( pCover, pCube ) + if ( pCube->pData[nWord] & (1< +#include "mvc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Manager_t * Mvc_ManagerStart() +{ + Mvc_Manager_t * p; + p = ALLOC( Mvc_Manager_t, 1 ); + memset( p, 0, sizeof(Mvc_Manager_t) ); + p->pMan1 = Extra_MmFixedStart( sizeof(Mvc_Cube_t) ); + p->pMan2 = Extra_MmFixedStart( sizeof(Mvc_Cube_t) + sizeof(Mvc_CubeWord_t) ); + p->pMan4 = Extra_MmFixedStart( sizeof(Mvc_Cube_t) + 3 * sizeof(Mvc_CubeWord_t) ); + p->pManC = Extra_MmFixedStart( sizeof(Mvc_Cover_t) ); + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_ManagerFree( Mvc_Manager_t * p ) +{ + Extra_MmFixedStop( p->pMan1 ); + Extra_MmFixedStop( p->pMan2 ); + Extra_MmFixedStop( p->pMan4 ); + Extra_MmFixedStop( p->pManC ); + free( p ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/mvc/mvcOpAlg.c b/abc_with_bb_support/src/misc/mvc/mvcOpAlg.c new file mode 100644 index 000000000..73692c6cf --- /dev/null +++ b/abc_with_bb_support/src/misc/mvc/mvcOpAlg.c @@ -0,0 +1,163 @@ +/**CFile**************************************************************** + + FileName [mvcOperAlg.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Miscellaneous operations on covers.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: mvcOpAlg.c,v 1.4 2003/04/26 20:41:36 alanmi Exp $] + +***********************************************************************/ + +#include "mvc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Multiplies two disjoint-support covers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Cover_t * Mvc_CoverAlgebraicMultiply( Mvc_Cover_t * pCover1, Mvc_Cover_t * pCover2 ) +{ + Mvc_Cover_t * pCover; + Mvc_Cube_t * pCube1, * pCube2, * pCube; + int CompResult; + + // covers should be the same base + assert( pCover1->nBits == pCover2->nBits ); + // make sure that supports do not overlap + Mvc_CoverAllocateMask( pCover1 ); + Mvc_CoverAllocateMask( pCover2 ); + Mvc_CoverSupport( pCover1, pCover1->pMask ); + Mvc_CoverSupport( pCover2, pCover2->pMask ); + // check if the cubes are bit-wise disjoint + Mvc_CubeBitDisjoint( CompResult, pCover1->pMask, pCover2->pMask ); + if ( !CompResult ) + printf( "Mvc_CoverMultiply(): Cover supports are not disjoint!\n" ); + + // iterate through the cubes + pCover = Mvc_CoverClone( pCover1 ); + Mvc_CoverForEachCube( pCover1, pCube1 ) + Mvc_CoverForEachCube( pCover2, pCube2 ) + { + // create the product cube + pCube = Mvc_CubeAlloc( pCover ); + // set the product cube equal to the product of the two cubes + Mvc_CubeBitOr( pCube, pCube1, pCube2 ); + // add the cube to the cover + Mvc_CoverAddCubeTail( pCover, pCube ); + } + return pCover; +} + + +/**Function************************************************************* + + Synopsis [Subtracts the second cover from the first.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Cover_t * Mvc_CoverAlgebraicSubtract( Mvc_Cover_t * pCover1, Mvc_Cover_t * pCover2 ) +{ + Mvc_Cover_t * pCover; + Mvc_Cube_t * pCube1, * pCube2, * pCube; + int fFound; + int CompResult; + + // covers should be the same base + assert( pCover1->nBits == pCover2->nBits ); + + // iterate through the cubes + pCover = Mvc_CoverClone( pCover1 ); + Mvc_CoverForEachCube( pCover1, pCube1 ) + { + fFound = 0; + Mvc_CoverForEachCube( pCover2, pCube2 ) + { + Mvc_CubeBitEqual( CompResult, pCube1, pCube2 ); + if ( CompResult ) + { + fFound = 1; + break; + } + } + if ( !fFound ) + { + // create the copy of the cube + pCube = Mvc_CubeDup( pCover, pCube1 ); + // add the cube copy to the cover + Mvc_CoverAddCubeTail( pCover, pCube ); + } + } + return pCover; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CoverAlgebraicEqual( Mvc_Cover_t * pCover1, Mvc_Cover_t * pCover2 ) +{ + Mvc_Cube_t * pCube1, * pCube2; + int fFound; + int CompResult; + + // covers should be the same base + assert( pCover1->nBits == pCover2->nBits ); + // iterate through the cubes + Mvc_CoverForEachCube( pCover1, pCube1 ) + { + fFound = 0; + Mvc_CoverForEachCube( pCover2, pCube2 ) + { + Mvc_CubeBitEqual( CompResult, pCube1, pCube2 ); + if ( CompResult ) + { + fFound = 1; + break; + } + } + if ( !fFound ) + return 0; + } + return 1; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/mvc/mvcOpBool.c b/abc_with_bb_support/src/misc/mvc/mvcOpBool.c new file mode 100644 index 000000000..03ee4daac --- /dev/null +++ b/abc_with_bb_support/src/misc/mvc/mvcOpBool.c @@ -0,0 +1,151 @@ +/**CFile**************************************************************** + + FileName [mvcProc.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Various boolean procedures working with covers.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: mvcOpBool.c,v 1.4 2003/04/16 01:55:37 alanmi Exp $] + +***********************************************************************/ + +#include "mvc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Cover_t * Mvc_CoverBooleanOr( Mvc_Cover_t * pCover1, Mvc_Cover_t * pCover2 ) +{ + Mvc_Cover_t * pCover; + Mvc_Cube_t * pCube, * pCubeCopy; + // make sure the covers are compatible + assert( pCover1->nBits == pCover2->nBits ); + // clone the cover + pCover = Mvc_CoverClone( pCover1 ); + // create the cubes by making pair-wise products + // of cubes in pCover1 and pCover2 + Mvc_CoverForEachCube( pCover1, pCube ) + { + pCubeCopy = Mvc_CubeDup( pCover, pCube ); + Mvc_CoverAddCubeTail( pCover, pCubeCopy ); + } + Mvc_CoverForEachCube( pCover2, pCube ) + { + pCubeCopy = Mvc_CubeDup( pCover, pCube ); + Mvc_CoverAddCubeTail( pCover, pCubeCopy ); + } + return pCover; +} + +#if 0 + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Cover_t * Mvc_CoverBooleanAnd( Mvc_Data_t * p, Mvc_Cover_t * pCover1, Mvc_Cover_t * pCover2 ) +{ + Mvc_Cover_t * pCover; + Mvc_Cube_t * pCube1, * pCube2, * pCubeCopy; + // make sure the covers are compatible + assert( pCover1->nBits == pCover2->nBits ); + // clone the cover + pCover = Mvc_CoverClone( pCover1 ); + // create the cubes by making pair-wise products + // of cubes in pCover1 and pCover2 + Mvc_CoverForEachCube( pCover1, pCube1 ) + { + Mvc_CoverForEachCube( pCover2, pCube2 ) + { + if ( Mvc_CoverDist0Cubes( p, pCube1, pCube2 ) ) + { + pCubeCopy = Mvc_CubeAlloc( pCover ); + Mvc_CubeBitAnd( pCubeCopy, pCube1, pCube2 ); + Mvc_CoverAddCubeTail( pCover, pCubeCopy ); + } + } + // if the number of cubes in the new cover is too large + // try compressing them + if ( Mvc_CoverReadCubeNum( pCover ) > 500 ) + Mvc_CoverContain( pCover ); + } + return pCover; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the two covers are equal.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CoverBooleanEqual( Mvc_Data_t * p, Mvc_Cover_t * pCover1, Mvc_Cover_t * pCover2 ) +{ + Mvc_Cover_t * pSharp; + + pSharp = Mvc_CoverSharp( p, pCover1, pCover2 ); + if ( Mvc_CoverReadCubeNum( pSharp ) ) + { +Mvc_CoverContain( pSharp ); +printf( "Sharp \n" ); +Mvc_CoverPrint( pSharp ); + Mvc_CoverFree( pSharp ); + return 0; + } + Mvc_CoverFree( pSharp ); + + pSharp = Mvc_CoverSharp( p, pCover2, pCover1 ); + if ( Mvc_CoverReadCubeNum( pSharp ) ) + { +Mvc_CoverContain( pSharp ); +printf( "Sharp \n" ); +Mvc_CoverPrint( pSharp ); + Mvc_CoverFree( pSharp ); + return 0; + } + Mvc_CoverFree( pSharp ); + + return 1; +} + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/mvc/mvcPrint.c b/abc_with_bb_support/src/misc/mvc/mvcPrint.c new file mode 100644 index 000000000..54fa2e53f --- /dev/null +++ b/abc_with_bb_support/src/misc/mvc/mvcPrint.c @@ -0,0 +1,220 @@ +/**CFile**************************************************************** + + FileName [mvcPrint.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Printing cubes and covers.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: mvcPrint.c,v 1.6 2003/04/09 18:02:06 alanmi Exp $] + +***********************************************************************/ + +#include "mvc.h" +//#include "vm.h" +//#include "vmInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Mvc_CubePrintBinary( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverPrint( Mvc_Cover_t * pCover ) +{ + Mvc_Cube_t * pCube; + int i; + // print general statistics + printf( "The cover contains %d cubes (%d bits and %d words)\n", + pCover->lCubes.nItems, pCover->nBits, pCover->nWords ); + // iterate through the cubes + Mvc_CoverForEachCube( pCover, pCube ) + Mvc_CubePrint( pCover, pCube ); + + if ( pCover->pLits ) + { + for ( i = 0; i < pCover->nBits; i++ ) + printf( " %d", pCover->pLits[i] ); + printf( "\n" ); + } + printf( "End of cover printout\n" ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CubePrint( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ) +{ + int iBit, Value; + // iterate through the literals +// printf( "Size = %2d ", Mvc_CubeReadSize(pCube) ); + Mvc_CubeForEachBit( pCover, pCube, iBit, Value ) + printf( "%c", '0' + Value ); + printf( "\n" ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverPrintBinary( Mvc_Cover_t * pCover ) +{ + Mvc_Cube_t * pCube; + int i; + // print general statistics + printf( "The cover contains %d cubes (%d bits and %d words)\n", + pCover->lCubes.nItems, pCover->nBits, pCover->nWords ); + // iterate through the cubes + Mvc_CoverForEachCube( pCover, pCube ) + Mvc_CubePrintBinary( pCover, pCube ); + + if ( pCover->pLits ) + { + for ( i = 0; i < pCover->nBits; i++ ) + printf( " %d", pCover->pLits[i] ); + printf( "\n" ); + } + printf( "End of cover printout\n" ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CubePrintBinary( Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ) +{ + int iVar, Value; + // iterate through the literals +// printf( "Size = %2d ", Mvc_CubeReadSize(pCube) ); + Mvc_CubeForEachVarValue( pCover, pCube, iVar, Value ) + { + assert( Value != 0 ); + if ( Value == 3 ) + printf( "-" ); + else if ( Value == 1 ) + printf( "0" ); + else + printf( "1" ); + } + printf( "\n" ); +} + +#if 0 + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverPrintMv( Mvc_Data_t * pData, Mvc_Cover_t * pCover ) +{ + Mvc_Cube_t * pCube; + int i; + // print general statistics + printf( "The cover contains %d cubes (%d bits and %d words)\n", + pCover->lCubes.nItems, pCover->nBits, pCover->nWords ); + // iterate through the cubes + Mvc_CoverForEachCube( pCover, pCube ) + Mvc_CubePrintMv( pData, pCover, pCube ); + + if ( pCover->pLits ) + { + for ( i = 0; i < pCover->nBits; i++ ) + printf( " %d", pCover->pLits[i] ); + printf( "\n" ); + } + printf( "End of cover printout\n" ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CubePrintMv( Mvc_Data_t * pData, Mvc_Cover_t * pCover, Mvc_Cube_t * pCube ) +{ + int iLit, iVar; + // iterate through the literals + printf( "Size = %2d ", Mvc_CubeReadSize(pCube) ); + iVar = 0; + for ( iLit = 0; iLit < pData->pVm->nValuesIn; iLit++ ) + { + if ( iLit == pData->pVm->pValuesFirst[iVar+1] ) + { + printf( " " ); + iVar++; + } + if ( Mvc_CubeBitValue( pCube, iLit ) ) + printf( "%c", '0' + iLit - pData->pVm->pValuesFirst[iVar] ); + else + printf( "-" ); + } + printf( "\n" ); +} + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/mvc/mvcSort.c b/abc_with_bb_support/src/misc/mvc/mvcSort.c new file mode 100644 index 000000000..9794c8478 --- /dev/null +++ b/abc_with_bb_support/src/misc/mvc/mvcSort.c @@ -0,0 +1,141 @@ +/**CFile**************************************************************** + + FileName [mvcSort.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Sorting cubes in the cover with the mask.] + + Author [MVSIS Group] + + Affiliation [uC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: mvcSort.c,v 1.5 2003/04/27 01:03:45 wjiang Exp $] + +***********************************************************************/ + +#include "mvc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + + +Mvc_Cube_t * Mvc_CoverSort_rec( Mvc_Cube_t * pList, int nItems, Mvc_Cube_t * pMask, int (* pCompareFunc)(Mvc_Cube_t *, Mvc_Cube_t *, Mvc_Cube_t *) ); +Mvc_Cube_t * Mvc_CoverSortMerge( Mvc_Cube_t * pList1, Mvc_Cube_t * pList2, Mvc_Cube_t * pMask, int (* pCompareFunc)(Mvc_Cube_t *, Mvc_Cube_t *, Mvc_Cube_t *) ); + +//////////////////////////////////////////////////////////////////////// +/// FuNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Sorts cubes using the given cost function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverSort( Mvc_Cover_t * pCover, Mvc_Cube_t * pMask, int (* pCompareFunc)(Mvc_Cube_t *, Mvc_Cube_t *, Mvc_Cube_t *) ) +{ + Mvc_Cube_t * pHead; + int nCubes; + // one cube does not need sorting + nCubes = Mvc_CoverReadCubeNum(pCover); + if ( nCubes <= 1 ) + return; + // sort the cubes + pHead = Mvc_CoverSort_rec( Mvc_CoverReadCubeHead(pCover), nCubes, pMask, pCompareFunc ); + // insert the sorted list into the cover + Mvc_CoverSetCubeHead( pCover, pHead ); + Mvc_CoverSetCubeTail( pCover, Mvc_ListGetTailFromHead(pHead) ); + // make sure that the list is sorted in the increasing order + assert( pCompareFunc( Mvc_CoverReadCubeHead(pCover), Mvc_CoverReadCubeTail(pCover), pMask ) <= 0 ); +} + +/**Function************************************************************* + + Synopsis [Recursive part of Mvc_CoverSort()] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Cube_t * Mvc_CoverSort_rec( Mvc_Cube_t * pList, int nItems, Mvc_Cube_t * pMask, int (* pCompareFunc)(Mvc_Cube_t *, Mvc_Cube_t *, Mvc_Cube_t *) ) +{ + Mvc_Cube_t * pList1, * pList2; + int nItems1, nItems2, i; + + // trivial case + if ( nItems == 1 ) + { + Mvc_CubeSetNext( pList, NULL ); + return pList; + } + + // select the new sizes + nItems1 = nItems/2; + nItems2 = nItems - nItems1; + + // set the new beginnings + pList1 = pList2 = pList; + for ( i = 0; i < nItems1; i++ ) + pList2 = Mvc_CubeReadNext( pList2 ); + + // solve recursively + pList1 = Mvc_CoverSort_rec( pList1, nItems1, pMask, pCompareFunc ); + pList2 = Mvc_CoverSort_rec( pList2, nItems2, pMask, pCompareFunc ); + + // merge + return Mvc_CoverSortMerge( pList1, pList2, pMask, pCompareFunc ); +} + + +/**Function************************************************************* + + Synopsis [Merges two NULL-terminated linked lists.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Cube_t * Mvc_CoverSortMerge( Mvc_Cube_t * pList1, Mvc_Cube_t * pList2, Mvc_Cube_t * pMask, int (* pCompareFunc)(Mvc_Cube_t *, Mvc_Cube_t *, Mvc_Cube_t *) ) +{ + Mvc_Cube_t * pList = NULL, ** ppTail = &pList; + Mvc_Cube_t * pCube; + while ( pList1 && pList2 ) + { + if ( pCompareFunc( pList1, pList2, pMask ) < 0 ) + { + pCube = pList1; + pList1 = Mvc_CubeReadNext(pList1); + } + else + { + pCube = pList2; + pList2 = Mvc_CubeReadNext(pList2); + } + *ppTail = pCube; + ppTail = Mvc_CubeReadNextP(pCube); + } + *ppTail = pList1? pList1: pList2; + return pList; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/mvc/mvcUtils.c b/abc_with_bb_support/src/misc/mvc/mvcUtils.c new file mode 100644 index 000000000..5b833fc93 --- /dev/null +++ b/abc_with_bb_support/src/misc/mvc/mvcUtils.c @@ -0,0 +1,868 @@ +/**CFile**************************************************************** + + FileName [mvcUtils.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Various cover handling utilities.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: mvcUtils.c,v 1.7 2003/04/26 20:41:36 alanmi Exp $] + +***********************************************************************/ + +#include "mvc.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int bit_count[256] = { + 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 +}; + + +static void Mvc_CoverCopyColumn( Mvc_Cover_t * pCoverOld, Mvc_Cover_t * pCoverNew, int iColOld, int iColNew ); + + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverSupport( Mvc_Cover_t * pCover, Mvc_Cube_t * pSupp ) +{ + Mvc_Cube_t * pCube; + // clean the support + Mvc_CubeBitClean( pSupp ); + // collect the support + Mvc_CoverForEachCube( pCover, pCube ) + Mvc_CubeBitOr( pSupp, pSupp, pCube ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverSupportAnd( Mvc_Cover_t * pCover, Mvc_Cube_t * pSupp ) +{ + Mvc_Cube_t * pCube; + // clean the support + Mvc_CubeBitFill( pSupp ); + // collect the support + Mvc_CoverForEachCube( pCover, pCube ) + Mvc_CubeBitAnd( pSupp, pSupp, pCube ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CoverSupportSizeBinary( Mvc_Cover_t * pCover ) +{ + Mvc_Cube_t * pSupp; + int Counter, i, v0, v1; + // compute the support + pSupp = Mvc_CubeAlloc( pCover ); + Mvc_CoverSupportAnd( pCover, pSupp ); + Counter = pCover->nBits/2; + for ( i = 0; i < pCover->nBits/2; i++ ) + { + v0 = Mvc_CubeBitValue( pSupp, 2*i ); + v1 = Mvc_CubeBitValue( pSupp, 2*i+1 ); + if ( v0 && v1 ) + Counter--; + } + Mvc_CubeFree( pCover, pSupp ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CoverSupportVarBelongs( Mvc_Cover_t * pCover, int iVar ) +{ + Mvc_Cube_t * pSupp; + int RetValue, v0, v1; + // compute the support + pSupp = Mvc_CubeAlloc( pCover ); + Mvc_CoverSupportAnd( pCover, pSupp ); + v0 = Mvc_CubeBitValue( pSupp, 2*iVar ); + v1 = Mvc_CubeBitValue( pSupp, 2*iVar+1 ); + RetValue = (int)( !v0 || !v1 ); + Mvc_CubeFree( pCover, pSupp ); + return RetValue; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverCommonCube( Mvc_Cover_t * pCover, Mvc_Cube_t * pComCube ) +{ + Mvc_Cube_t * pCube; + // clean the support + Mvc_CubeBitFill( pComCube ); + // collect the support + Mvc_CoverForEachCube( pCover, pCube ) + Mvc_CubeBitAnd( pComCube, pComCube, pCube ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CoverIsCubeFree( Mvc_Cover_t * pCover ) +{ + int Result; + // get the common cube + Mvc_CoverAllocateMask( pCover ); + Mvc_CoverCommonCube( pCover, pCover->pMask ); + // check whether the common cube is empty + Mvc_CubeBitEmpty( Result, pCover->pMask ); + return Result; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverMakeCubeFree( Mvc_Cover_t * pCover ) +{ + Mvc_Cube_t * pCube; + // get the common cube + Mvc_CoverAllocateMask( pCover ); + Mvc_CoverCommonCube( pCover, pCover->pMask ); + // remove this cube from the cubes in the cover + Mvc_CoverForEachCube( pCover, pCube ) + Mvc_CubeBitSharp( pCube, pCube, pCover->pMask ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Cover_t * Mvc_CoverCommonCubeCover( Mvc_Cover_t * pCover ) +{ + Mvc_Cover_t * pRes; + Mvc_Cube_t * pCube; + // create the new cover + pRes = Mvc_CoverClone( pCover ); + // get the new cube + pCube = Mvc_CubeAlloc( pRes ); + // get the common cube + Mvc_CoverCommonCube( pCover, pCube ); + // add the cube to the cover + Mvc_CoverAddCubeTail( pRes, pCube ); + return pRes; +} + + +/**Function************************************************************* + + Synopsis [Returns 1 if the support of cover2 is contained in the support of cover1.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CoverCheckSuppContainment( Mvc_Cover_t * pCover1, Mvc_Cover_t * pCover2 ) +{ + int Result; + assert( pCover1->nBits == pCover2->nBits ); + // set the supports + Mvc_CoverAllocateMask( pCover1 ); + Mvc_CoverSupport( pCover1, pCover1->pMask ); + Mvc_CoverAllocateMask( pCover2 ); + Mvc_CoverSupport( pCover2, pCover2->pMask ); + // check the containment + Mvc_CubeBitNotImpl( Result, pCover2->pMask, pCover1->pMask ); + return !Result; +} + +/**Function************************************************************* + + Synopsis [Counts the cube sizes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CoverSetCubeSizes( Mvc_Cover_t * pCover ) +{ + Mvc_Cube_t * pCube; + unsigned char * pByte, * pByteStart, * pByteStop; + int nBytes, nOnes; + + // get the number of unsigned chars in the cube's bit strings + nBytes = pCover->nBits / (8 * sizeof(unsigned char)) + (int)(pCover->nBits % (8 * sizeof(unsigned char)) > 0); + // iterate through the cubes + Mvc_CoverForEachCube( pCover, pCube ) + { + // clean the counter of ones + nOnes = 0; + // set the starting and stopping positions + pByteStart = (unsigned char *)pCube->pData; + pByteStop = pByteStart + nBytes; + // iterate through the positions + for ( pByte = pByteStart; pByte < pByteStop; pByte++ ) + nOnes += bit_count[*pByte]; + // set the nOnes + Mvc_CubeSetSize( pCube, nOnes ); + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Counts the cube sizes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CoverGetCubeSize( Mvc_Cube_t * pCube ) +{ + unsigned char * pByte, * pByteStart, * pByteStop; + int nOnes, nBytes, nBits; + // get the number of unsigned chars in the cube's bit strings + nBits = (pCube->iLast + 1) * sizeof(Mvc_CubeWord_t) * 8 - pCube->nUnused; + nBytes = nBits / (8 * sizeof(unsigned char)) + (int)(nBits % (8 * sizeof(unsigned char)) > 0); + // clean the counter of ones + nOnes = 0; + // set the starting and stopping positions + pByteStart = (unsigned char *)pCube->pData; + pByteStop = pByteStart + nBytes; + // iterate through the positions + for ( pByte = pByteStart; pByte < pByteStop; pByte++ ) + nOnes += bit_count[*pByte]; + return nOnes; +} + +/**Function************************************************************* + + Synopsis [Counts the differences in each cube pair in the cover.] + + Description [Takes the cover (pCover) and the array where the + diff counters go (pDiffs). The array pDiffs should have as many + entries as there are different pairs of cubes in the cover: n(n-1)/2. + Fills out the array pDiffs with the following info: For each cube + pair, included in the array is the number of literals in both cubes + after they are made cube free.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvc_CoverCountCubePairDiffs( Mvc_Cover_t * pCover, unsigned char pDiffs[] ) +{ + Mvc_Cube_t * pCube1; + Mvc_Cube_t * pCube2; + Mvc_Cube_t * pMask; + unsigned char * pByte, * pByteStart, * pByteStop; + int nBytes, nOnes; + int nCubePairs; + + // allocate a temporary mask + pMask = Mvc_CubeAlloc( pCover ); + // get the number of unsigned chars in the cube's bit strings + nBytes = pCover->nBits / (8 * sizeof(unsigned char)) + (int)(pCover->nBits % (8 * sizeof(unsigned char)) > 0); + // iterate through the cubes + nCubePairs = 0; + Mvc_CoverForEachCube( pCover, pCube1 ) + { + Mvc_CoverForEachCubeStart( Mvc_CubeReadNext(pCube1), pCube2 ) + { + // find the bit-wise exor of cubes + Mvc_CubeBitExor( pMask, pCube1, pCube2 ); + // set the starting and stopping positions + pByteStart = (unsigned char *)pMask->pData; + pByteStop = pByteStart + nBytes; + // clean the counter of ones + nOnes = 0; + // iterate through the positions + for ( pByte = pByteStart; pByte < pByteStop; pByte++ ) + nOnes += bit_count[*pByte]; + // set the nOnes + pDiffs[nCubePairs++] = nOnes; + } + } + // deallocate the mask + Mvc_CubeFree( pCover, pMask ); + return 1; +} + + +/**Function************************************************************* + + Synopsis [Creates a new cover containing some literals of the old cover.] + + Description [Creates the new cover containing the given number (nVarsRem) + literals of the old cover. All the bits of the new cover are initialized + to "1". The selected bits from the old cover are copied on top. The numbers + of the selected bits to copy are given in the array pVarsRem. The i-set + entry in this array is the index of the bit in the old cover which goes + to the i-th place in the new cover. If the i-th entry in pVarsRem is -1, + it means that the i-th bit does not change (remains composed of all 1's). + This is a useful feature to speed up remapping covers, which are known + to depend only on a subset of input variables.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Cover_t * Mvc_CoverRemap( Mvc_Cover_t * p, int * pVarsRem, int nVarsRem ) +{ + Mvc_Cover_t * pCover; + Mvc_Cube_t * pCube, * pCubeCopy; + int i; + // clone the cover + pCover = Mvc_CoverAlloc( p->pMem, nVarsRem ); + // copy the cube list + Mvc_CoverForEachCube( p, pCube ) + { + pCubeCopy = Mvc_CubeAlloc( pCover ); + //Mvc_CubeBitClean( pCubeCopy ); //changed by wjiang + Mvc_CubeBitFill( pCubeCopy ); //changed by wjiang + Mvc_CoverAddCubeTail( pCover, pCubeCopy ); + } + // copy the corresponding columns + for ( i = 0; i < nVarsRem; i++ ) + { + if (pVarsRem[i] < 0) + continue; //added by wjiang + assert( pVarsRem[i] >= 0 && pVarsRem[i] < p->nBits ); + Mvc_CoverCopyColumn( p, pCover, pVarsRem[i], i ); + } + return pCover; +} + +/**Function************************************************************* + + Synopsis [Copies a column from the old cover to the new cover.] + + Description [Copies the column (iColOld) of the old cover (pCoverOld) + into the column (iColNew) of the new cover (pCoverNew). Assumes that + the number of cubes is the same in both covers. Makes no assuptions + about the current contents of the column in the new cover.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Mvc_CoverCopyColumn( Mvc_Cover_t * pCoverOld, Mvc_Cover_t * pCoverNew, + int iColOld, int iColNew ) +{ + Mvc_Cube_t * pCubeOld, * pCubeNew; + int iWordOld, iWordNew, iBitOld, iBitNew; + + assert( Mvc_CoverReadCubeNum(pCoverOld) == Mvc_CoverReadCubeNum(pCoverNew) ); + + // get the place of the old and new columns + iWordOld = Mvc_CubeWhichWord(iColOld); + iBitOld = Mvc_CubeWhichBit(iColOld); + iWordNew = Mvc_CubeWhichWord(iColNew); + iBitNew = Mvc_CubeWhichBit(iColNew); + + // go through the cubes of both covers + pCubeNew = Mvc_CoverReadCubeHead(pCoverNew); + Mvc_CoverForEachCube( pCoverOld, pCubeOld ) + { + if ( pCubeOld->pData[iWordOld] & (1<pData[iWordNew] |= (1<pData[iWordNew] &= ~(1<pVm, iVar); + nValues = Vm_VarMapReadValues(pData->pVm, iVar); + ppCofs = ALLOC( Mvc_Cover_t *, nValues + 1 ); + for ( i = 0; i <= nValues; i++ ) + ppCofs[i] = Mvc_CoverClone( pCover ); + + // go through the cubes + Mvc_CoverForEachCube( pCover, pCube ) + { + // if the literal if a full literal, add it to last "cofactor" + Mvc_CubeBitEqualUnderMask( Res, pCube, pData->ppMasks[iVar], pData->ppMasks[iVar] ); + if ( Res ) + { + pCubeNew = Mvc_CubeDup(pCover, pCube); + Mvc_CoverAddCubeTail( ppCofs[nValues], pCubeNew ); + continue; + } + + // otherwise, add it to separate values + for ( i = 0; i < nValues; i++ ) + if ( Mvc_CubeBitValue( pCube, iValueFirst + i ) ) + { + pCubeNew = Mvc_CubeDup(pCover, pCube); + Mvc_CubeBitOr( pCubeNew, pCubeNew, pData->ppMasks[iVar] ); + Mvc_CoverAddCubeTail( ppCofs[i], pCubeNew ); + } + } + return ppCofs; +} + +/**Function************************************************************* + + Synopsis [Count the cubes with non-trivial literals with the given value.] + + Description [The data and the cover are given (pData, pCover). Also given + are the variable number and the number of a value of this variable. + This procedure returns the number of cubes having a non-trivial literal + of this variable that have the given value present.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Mvr_CoverCountLitsWithValue( Mvc_Data_t * pData, Mvc_Cover_t * pCover, int iVar, int iValue ) +{ + Mvc_Cube_t * pCube; + int iValueFirst, Res, Counter; + + Counter = 0; + iValueFirst = Vm_VarMapReadValuesFirst( pData->pVm, iVar ); + Mvc_CoverForEachCube( pCover, pCube ) + { + // check if the given literal is the full literal + Mvc_CubeBitEqualUnderMask( Res, pCube, pData->ppMasks[iVar], pData->ppMasks[iVar] ); + if ( Res ) + continue; + // this literal is not a full literal; check if it has this value + Counter += Mvc_CubeBitValue( pCube, iValueFirst + iValue ); + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Creates the expanded cover.] + + Description [The original cover is expanded by adding some variables. + These variables are the additional variables in pVmNew, compared to + pCvr->pVm. The resulting cover is the same as the original one, except + that it contains the additional variables present as full literals + in every cube.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Cover_t * Mvc_CoverCreateExpanded( Mvc_Cover_t * pCover, Vm_VarMap_t * pVmNew ) +{ + Mvc_Cover_t * pCoverNew; + Mvc_Cube_t * pCube, * pCubeNew; + int i, iLast, iLastNew; + + // create the new cover + pCoverNew = Mvc_CoverAlloc( pCover->pMem, Vm_VarMapReadValuesInNum(pVmNew) ); + + // get the cube composed of extra bits + Mvc_CoverAllocateMask( pCoverNew ); + Mvc_CubeBitClean( pCoverNew->pMask ); + for ( i = pCover->nBits; i < pCoverNew->nBits; i++ ) + Mvc_CubeBitInsert( pCoverNew->pMask, i ); + + // get the indexes of the last words in both covers + iLast = pCover->nWords? pCover->nWords - 1: 0; + iLastNew = pCoverNew->nWords? pCoverNew->nWords - 1: 0; + + // create the cubes of the new cover + Mvc_CoverForEachCube( pCover, pCube ) + { + pCubeNew = Mvc_CubeAlloc( pCoverNew ); + Mvc_CubeBitClean( pCubeNew ); + // copy the bits (cannot immediately use Mvc_CubeBitCopy, + // because covers have different numbers of bits) + Mvc_CubeSetLast( pCubeNew, iLast ); + Mvc_CubeBitCopy( pCubeNew, pCube ); + Mvc_CubeSetLast( pCubeNew, iLastNew ); + // add the extra bits + Mvc_CubeBitOr( pCubeNew, pCubeNew, pCoverNew->pMask ); + // add the cube to the new cover + Mvc_CoverAddCubeTail( pCoverNew, pCubeNew ); + } + return pCoverNew; +} + +#endif + +/**Function************************************************************* + + Synopsis [Transposes the cube cover.] + + Description [Returns the cube cover that looks like a transposed + matrix, compared to the matrix derived from the original cover.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Cover_t * Mvc_CoverTranspose( Mvc_Cover_t * pCover ) +{ + Mvc_Cover_t * pRes; + Mvc_Cube_t * pCubeRes, * pCube; + int nWord, nBit, i, iCube; + + pRes = Mvc_CoverAlloc( pCover->pMem, Mvc_CoverReadCubeNum(pCover) ); + for ( i = 0; i < pCover->nBits; i++ ) + { + // get the word and bit of this literal + nWord = Mvc_CubeWhichWord(i); + nBit = Mvc_CubeWhichBit(i); + // get the transposed cube + pCubeRes = Mvc_CubeAlloc( pRes ); + Mvc_CubeBitClean( pCubeRes ); + iCube = 0; + Mvc_CoverForEachCube( pCover, pCube ) + { + if ( pCube->pData[nWord] & (1<nUnused == 0 ) + continue; + + Unsigned = ( pCube->pData[pCube->iLast] & + (BITS_FULL << (32-pCube->nUnused)) ); + if( Unsigned ) + { + printf( "Cube %2d out of %2d contains dirty bits.\n", nCubes, + Mvc_CoverReadCubeNum(pCover) ); + } + nCubes++; + } + return 1; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/nm/module.make b/abc_with_bb_support/src/misc/nm/module.make new file mode 100644 index 000000000..8c4d2b232 --- /dev/null +++ b/abc_with_bb_support/src/misc/nm/module.make @@ -0,0 +1,2 @@ +SRC += src/misc/nm/nmApi.c \ + src/misc/nm/nmTable.c diff --git a/abc_with_bb_support/src/misc/nm/nm.h b/abc_with_bb_support/src/misc/nm/nm.h new file mode 100644 index 000000000..10af0187e --- /dev/null +++ b/abc_with_bb_support/src/misc/nm/nm.h @@ -0,0 +1,92 @@ +/**CFilextern e**************************************************************** + + FileName [nm.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Name manager.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: nm.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __NM_H__ +#define __NM_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + This manager is designed to store ID-to-name and name-to-ID mapping + for Boolean networks and And-Inverter Graphs. + + In a netlist, net names are unique. In this case, there is a one-to-one + mapping between IDs and names. + + In a logic network, which do not have nets, several objects may have + the same name. For example, a latch output and a primary output. + Another example, a primary input and an input to a black box. + In this case, for each ID on an object there is only one name, + but for each name may be several IDs of objects having this name. + + The name manager maps ID-to-name uniquely but it allows one name to + be mapped into several IDs. When a query to find an ID of the object + by its name is submitted, it is possible to specify the object type, + which will help select one of several IDs. If the type is -1, and + there is more than one object with the given name, any object with + the given name is returned. +*/ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Nm_Man_t_ Nm_Man_t; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== nmApi.c ==========================================================*/ +extern Nm_Man_t * Nm_ManCreate( int nSize ); +extern void Nm_ManFree( Nm_Man_t * p ); +extern int Nm_ManNumEntries( Nm_Man_t * p ); +extern char * Nm_ManStoreIdName( Nm_Man_t * p, int ObjId, int Type, char * pName, char * pSuffix ); +extern void Nm_ManDeleteIdName( Nm_Man_t * p, int ObjId ); +extern char * Nm_ManCreateUniqueName( Nm_Man_t * p, int ObjId ); +extern char * Nm_ManFindNameById( Nm_Man_t * p, int ObjId ); +extern int Nm_ManFindIdByName( Nm_Man_t * p, char * pName, int Type ); +extern int Nm_ManFindIdByNameTwoTypes( Nm_Man_t * p, char * pName, int Type1, int Type2 ); +extern Vec_Int_t * Nm_ManReturnNameIds( Nm_Man_t * p ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/misc/nm/nmApi.c b/abc_with_bb_support/src/misc/nm/nmApi.c new file mode 100644 index 000000000..0fd682282 --- /dev/null +++ b/abc_with_bb_support/src/misc/nm/nmApi.c @@ -0,0 +1,272 @@ +/**CFile**************************************************************** + + FileName [nmApi.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Name manager.] + + Synopsis [APIs of the name manager.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: nmApi.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "nmInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates the name manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Nm_Man_t * Nm_ManCreate( int nSize ) +{ + Nm_Man_t * p; + // allocate the table + p = ALLOC( Nm_Man_t, 1 ); + memset( p, 0, sizeof(Nm_Man_t) ); + // set the parameters + p->nSizeFactor = 2; // determined the limit on the grow of data before the table resizes + p->nGrowthFactor = 3; // determined how much the table grows after resizing + // allocate and clean the bins + p->nBins = Cudd_PrimeNm(nSize); + p->pBinsI2N = ALLOC( Nm_Entry_t *, p->nBins ); + p->pBinsN2I = ALLOC( Nm_Entry_t *, p->nBins ); + memset( p->pBinsI2N, 0, sizeof(Nm_Entry_t *) * p->nBins ); + memset( p->pBinsN2I, 0, sizeof(Nm_Entry_t *) * p->nBins ); + // start the memory manager + p->pMem = Extra_MmFlexStart(); + return p; +} + +/**Function************************************************************* + + Synopsis [Deallocates the name manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Nm_ManFree( Nm_Man_t * p ) +{ + Extra_MmFlexStop( p->pMem ); + FREE( p->pBinsI2N ); + FREE( p->pBinsN2I ); + FREE( p ); +} + +/**Function************************************************************* + + Synopsis [Returns the number of objects with names.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Nm_ManNumEntries( Nm_Man_t * p ) +{ + return p->nEntries; +} + +/**Function************************************************************* + + Synopsis [Creates a new entry in the name manager.] + + Description [Returns 1 if the entry with the given object ID + already exists in the name manager.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Nm_ManStoreIdName( Nm_Man_t * p, int ObjId, int Type, char * pName, char * pSuffix ) +{ + Nm_Entry_t * pEntry; + int RetValue, nEntrySize; + // check if the object with this ID is already stored + if ( pEntry = Nm_ManTableLookupId(p, ObjId) ) + { + printf( "Nm_ManStoreIdName(): Entry with the same ID already exists.\n" ); + return NULL; + } + // create a new entry + nEntrySize = sizeof(Nm_Entry_t) + strlen(pName) + (pSuffix?strlen(pSuffix):0) + 1; + nEntrySize = (nEntrySize / 4 + ((nEntrySize % 4) > 0)) * 4; + pEntry = (Nm_Entry_t *)Extra_MmFlexEntryFetch( p->pMem, nEntrySize ); + pEntry->pNextI2N = pEntry->pNextN2I = pEntry->pNameSake = NULL; + pEntry->ObjId = ObjId; + pEntry->Type = Type; + sprintf( pEntry->Name, "%s%s", pName, pSuffix? pSuffix : "" ); + // add the entry to the hash table + RetValue = Nm_ManTableAdd( p, pEntry ); + assert( RetValue == 1 ); + return pEntry->Name; +} + +/**Function************************************************************* + + Synopsis [Creates a new entry in the name manager.] + + Description [Returns 1 if the entry with the given object ID + already exists in the name manager.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Nm_ManDeleteIdName( Nm_Man_t * p, int ObjId ) +{ + Nm_Entry_t * pEntry; + pEntry = Nm_ManTableLookupId(p, ObjId); + if ( pEntry == NULL ) + { + printf( "Nm_ManDeleteIdName(): This entry is not in the table.\n" ); + return; + } + // remove entry from the table + Nm_ManTableDelete( p, ObjId ); +} + + +/**Function************************************************************* + + Synopsis [Finds a unique name for the node.] + + Description [If the name exists, tries appending numbers to it until + it becomes unique. The name is not added to the table.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Nm_ManCreateUniqueName( Nm_Man_t * p, int ObjId ) +{ + static char NameStr[1000]; + Nm_Entry_t * pEntry; + int i; + if ( pEntry = Nm_ManTableLookupId(p, ObjId) ) + return pEntry->Name; + sprintf( NameStr, "n%d", ObjId ); + for ( i = 1; Nm_ManTableLookupName(p, NameStr, -1); i++ ) + sprintf( NameStr, "n%d_%d", ObjId, i ); + return NameStr; +} + +/**Function************************************************************* + + Synopsis [Returns name of the object if the ID is known.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Nm_ManFindNameById( Nm_Man_t * p, int ObjId ) +{ + Nm_Entry_t * pEntry; + if ( pEntry = Nm_ManTableLookupId(p, ObjId) ) + return pEntry->Name; + return NULL; +} + +/**Function************************************************************* + + Synopsis [Returns ID of the object if its name is known.] + + Description [This procedure may return two IDs because POs and latches + may have the same name (the only allowed case of name duplication).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Nm_ManFindIdByName( Nm_Man_t * p, char * pName, int Type ) +{ + Nm_Entry_t * pEntry; + if ( pEntry = Nm_ManTableLookupName(p, pName, Type) ) + return pEntry->ObjId; + return -1; +} + +/**Function************************************************************* + + Synopsis [Returns ID of the object if its name is known.] + + Description [This procedure may return two IDs because POs and latches + may have the same name (the only allowed case of name duplication).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Nm_ManFindIdByNameTwoTypes( Nm_Man_t * p, char * pName, int Type1, int Type2 ) +{ + int iNodeId; + iNodeId = Nm_ManFindIdByName( p, pName, Type1 ); + if ( iNodeId == -1 ) + iNodeId = Nm_ManFindIdByName( p, pName, Type2 ); + if ( iNodeId == -1 ) + return -1; + return iNodeId; +} + +/**Function************************************************************* + + Synopsis [Return the IDs of objects with names.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Nm_ManReturnNameIds( Nm_Man_t * p ) +{ + Vec_Int_t * vNameIds; + int i; + vNameIds = Vec_IntAlloc( p->nEntries ); + for ( i = 0; i < p->nBins; i++ ) + if ( p->pBinsI2N[i] ) + Vec_IntPush( vNameIds, p->pBinsI2N[i]->ObjId ); + return vNameIds; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/nm/nmInt.h b/abc_with_bb_support/src/misc/nm/nmInt.h new file mode 100644 index 000000000..67c80d1e7 --- /dev/null +++ b/abc_with_bb_support/src/misc/nm/nmInt.h @@ -0,0 +1,91 @@ +/**CFile**************************************************************** + + FileName [nmInt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Name manager.] + + Synopsis [Internal declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: nmInt.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __NM_INT_H__ +#define __NM_INT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include "extra.h" +#include "vec.h" +#include "nm.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Nm_Entry_t_ Nm_Entry_t; +struct Nm_Entry_t_ +{ + unsigned Type : 4; // object type + unsigned ObjId : 28; // object ID + Nm_Entry_t * pNextI2N; // the next entry in the ID hash table + Nm_Entry_t * pNextN2I; // the next entry in the name hash table + Nm_Entry_t * pNameSake; // the next entry with the same name + char Name[0]; // name of the object +}; + +struct Nm_Man_t_ +{ + Nm_Entry_t ** pBinsI2N; // mapping IDs into names + Nm_Entry_t ** pBinsN2I; // mapping names into IDs + int nBins; // the number of bins in tables + int nEntries; // the number of entries + int nSizeFactor; // determined how much larger the table should be + int nGrowthFactor; // determined how much the table grows after resizing + Extra_MmFlex_t * pMem; // memory manager for entries (and names) +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== nmTable.c ==========================================================*/ +extern int Nm_ManTableAdd( Nm_Man_t * p, Nm_Entry_t * pEntry ); +extern int Nm_ManTableDelete( Nm_Man_t * p, int ObjId ); +extern Nm_Entry_t * Nm_ManTableLookupId( Nm_Man_t * p, int ObjId ); +extern Nm_Entry_t * Nm_ManTableLookupName( Nm_Man_t * p, char * pName, int Type ); +extern unsigned int Cudd_PrimeNm( unsigned int p ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/nm/nmTable.c b/abc_with_bb_support/src/misc/nm/nmTable.c new file mode 100644 index 000000000..ffedd7bbe --- /dev/null +++ b/abc_with_bb_support/src/misc/nm/nmTable.c @@ -0,0 +1,340 @@ +/**CFile**************************************************************** + + FileName [nmTable.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Name manager.] + + Synopsis [Hash table for the name manager.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: nmTable.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "nmInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// hashing for integers +static unsigned Nm_HashNumber( int Num, int TableSize ) +{ + unsigned Key = 0; + Key ^= ( Num & 0xFF) * 7937; + Key ^= ((Num >> 8) & 0xFF) * 2971; + Key ^= ((Num >> 16) & 0xFF) * 911; + Key ^= ((Num >> 24) & 0xFF) * 353; + return Key % TableSize; +} + +// hashing for strings +static unsigned Nm_HashString( char * pName, int TableSize ) +{ + static int s_Primes[10] = { + 1291, 1699, 2357, 4177, 5147, + 5647, 6343, 7103, 7873, 8147 + }; + unsigned i, Key = 0; + for ( i = 0; pName[i] != '\0'; i++ ) + Key ^= s_Primes[i%10]*pName[i]*pName[i]; + return Key % TableSize; +} + +static void Nm_ManResize( Nm_Man_t * p ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Adds an entry to two hash tables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Nm_ManTableAdd( Nm_Man_t * p, Nm_Entry_t * pEntry ) +{ + Nm_Entry_t ** ppSpot, * pOther; + // resize the tables if needed + if ( p->nEntries > p->nBins * p->nSizeFactor ) + Nm_ManResize( p ); + // add the entry to the table Id->Name + assert( Nm_ManTableLookupId(p, pEntry->ObjId) == NULL ); + ppSpot = p->pBinsI2N + Nm_HashNumber(pEntry->ObjId, p->nBins); + pEntry->pNextI2N = *ppSpot; + *ppSpot = pEntry; + // check if an entry with the same name already exists + if ( pOther = Nm_ManTableLookupName(p, pEntry->Name, -1) ) + { + // entry with the same name already exists - add it to the ring + pEntry->pNameSake = pOther->pNameSake? pOther->pNameSake : pOther; + pOther->pNameSake = pEntry; + } + else + { + // entry with the same name does not exist - add it to the table + ppSpot = p->pBinsN2I + Nm_HashString(pEntry->Name, p->nBins); + pEntry->pNextN2I = *ppSpot; + *ppSpot = pEntry; + } + // report successfully added entry + p->nEntries++; + return 1; +} + +/**Function************************************************************* + + Synopsis [Deletes the entry from two hash tables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Nm_ManTableDelete( Nm_Man_t * p, int ObjId ) +{ + Nm_Entry_t ** ppSpot, * pEntry, * pPrev; + int fRemoved; + p->nEntries--; + // remove the entry from the table Id->Name + assert( Nm_ManTableLookupId(p, ObjId) != NULL ); + ppSpot = p->pBinsI2N + Nm_HashNumber(ObjId, p->nBins); + while ( (*ppSpot)->ObjId != (unsigned)ObjId ) + ppSpot = &(*ppSpot)->pNextI2N; + pEntry = *ppSpot; + *ppSpot = (*ppSpot)->pNextI2N; + // remove the entry from the table Name->Id + ppSpot = p->pBinsN2I + Nm_HashString(pEntry->Name, p->nBins); + while ( *ppSpot && *ppSpot != pEntry ) + ppSpot = &(*ppSpot)->pNextN2I; + // remember if we found this one in the list + fRemoved = (*ppSpot != NULL); + if ( *ppSpot ) + { + assert( *ppSpot == pEntry ); + *ppSpot = (*ppSpot)->pNextN2I; + } + // quit if this entry has no namesakes + if ( pEntry->pNameSake == NULL ) + { + assert( fRemoved ); + return 1; + } + // remove entry from the ring of namesakes + assert( pEntry->pNameSake != pEntry ); + for ( pPrev = pEntry; pPrev->pNameSake != pEntry; pPrev = pPrev->pNameSake ); + assert( !strcmp(pPrev->Name, pEntry->Name) ); + assert( pPrev->pNameSake == pEntry ); + if ( pEntry->pNameSake == pPrev ) // two entries in the ring + pPrev->pNameSake = NULL; + else + pPrev->pNameSake = pEntry->pNameSake; + // reinsert the ring back if we removed its connection with the list in the table + if ( fRemoved ) + { + assert( pPrev->pNextN2I == NULL ); + pPrev->pNextN2I = *ppSpot; + *ppSpot = pPrev; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Looks up the entry by ID.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Nm_Entry_t * Nm_ManTableLookupId( Nm_Man_t * p, int ObjId ) +{ + Nm_Entry_t * pEntry; + for ( pEntry = p->pBinsI2N[ Nm_HashNumber(ObjId, p->nBins) ]; pEntry; pEntry = pEntry->pNextI2N ) + if ( pEntry->ObjId == (unsigned)ObjId ) + return pEntry; + return NULL; +} + +/**Function************************************************************* + + Synopsis [Looks up the entry by name and type.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Nm_Entry_t * Nm_ManTableLookupName( Nm_Man_t * p, char * pName, int Type ) +{ + Nm_Entry_t * pEntry, * pTemp; + int Counter = 0; + for ( pEntry = p->pBinsN2I[ Nm_HashString(pName, p->nBins) ]; pEntry; pEntry = pEntry->pNextN2I ) + { + // check the entry itself + if ( !strcmp(pEntry->Name, pName) && (Type == -1 || pEntry->Type == (unsigned)Type) ) + return pEntry; + // if there is no namesakes, continue + if ( pEntry->pNameSake == NULL ) + continue; + // check the list of namesakes + for ( pTemp = pEntry->pNameSake; pTemp != pEntry; pTemp = pTemp->pNameSake ) + if ( !strcmp(pTemp->Name, pName) && (Type == -1 || pTemp->Type == (unsigned)Type) ) + return pTemp; + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [Profiles hash tables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Nm_ManProfile( Nm_Man_t * p ) +{ + Nm_Entry_t * pEntry; + int Counter, e; + printf( "I2N table: " ); + for ( e = 0; e < p->nBins; e++ ) + { + Counter = 0; + for ( pEntry = p->pBinsI2N[e]; pEntry; pEntry = pEntry->pNextI2N ) + Counter++; + printf( "%d ", Counter ); + } + printf( "\n" ); + printf( "N2I table: " ); + for ( e = 0; e < p->nBins; e++ ) + { + Counter = 0; + for ( pEntry = p->pBinsN2I[e]; pEntry; pEntry = pEntry->pNextN2I ) + Counter++; + printf( "%d ", Counter ); + } + printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [Resizes the table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Nm_ManResize( Nm_Man_t * p ) +{ + Nm_Entry_t ** pBinsNewI2N, ** pBinsNewN2I, * pEntry, * pEntry2, ** ppSpot; + int nBinsNew, Counter, e, clk; + +clk = clock(); + // get the new table size + nBinsNew = Cudd_PrimeCopy( p->nGrowthFactor * p->nBins ); + // allocate a new array + pBinsNewI2N = ALLOC( Nm_Entry_t *, nBinsNew ); + pBinsNewN2I = ALLOC( Nm_Entry_t *, nBinsNew ); + memset( pBinsNewI2N, 0, sizeof(Nm_Entry_t *) * nBinsNew ); + memset( pBinsNewN2I, 0, sizeof(Nm_Entry_t *) * nBinsNew ); + // rehash entries in Id->Name table + Counter = 0; + for ( e = 0; e < p->nBins; e++ ) + for ( pEntry = p->pBinsI2N[e], pEntry2 = pEntry? pEntry->pNextI2N : NULL; + pEntry; pEntry = pEntry2, pEntry2 = pEntry? pEntry->pNextI2N : NULL ) + { + ppSpot = pBinsNewI2N + Nm_HashNumber(pEntry->ObjId, nBinsNew); + pEntry->pNextI2N = *ppSpot; + *ppSpot = pEntry; + Counter++; + } + // rehash entries in Name->Id table + for ( e = 0; e < p->nBins; e++ ) + for ( pEntry = p->pBinsN2I[e], pEntry2 = pEntry? pEntry->pNextN2I : NULL; + pEntry; pEntry = pEntry2, pEntry2 = pEntry? pEntry->pNextN2I : NULL ) + { + ppSpot = pBinsNewN2I + Nm_HashString(pEntry->Name, nBinsNew); + pEntry->pNextN2I = *ppSpot; + *ppSpot = pEntry; + } + assert( Counter == p->nEntries ); +// printf( "Increasing the structural table size from %6d to %6d. ", p->nBins, nBinsNew ); +// PRT( "Time", clock() - clk ); + // replace the table and the parameters + free( p->pBinsI2N ); + free( p->pBinsN2I ); + p->pBinsI2N = pBinsNewI2N; + p->pBinsN2I = pBinsNewN2I; + p->nBins = nBinsNew; +// Nm_ManProfile( p ); +} + + +/**Function************************************************************* + + Synopsis [Returns the smallest prime larger than the number.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned int Cudd_PrimeNm( unsigned int p) +{ + int i,pn; + + p--; + do { + p++; + if (p&1) { + pn = 1; + i = 3; + while ((unsigned) (i * i) <= p) { + if (p % i == 0) { + pn = 0; + break; + } + i += 2; + } + } else { + pn = 0; + } + } while (!pn); + return(p); + +} /* end of Cudd_Prime */ + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/st/module.make b/abc_with_bb_support/src/misc/st/module.make new file mode 100644 index 000000000..f2b188582 --- /dev/null +++ b/abc_with_bb_support/src/misc/st/module.make @@ -0,0 +1,2 @@ +SRC += src/misc/st/st.c \ + src/misc/st/stmm.c diff --git a/abc_with_bb_support/src/misc/st/st.c b/abc_with_bb_support/src/misc/st/st.c new file mode 100644 index 000000000..4c601248a --- /dev/null +++ b/abc_with_bb_support/src/misc/st/st.c @@ -0,0 +1,624 @@ +/* + * Revision Control Information + * + * /projects/hsis/CVS/utilities/st/st.c,v + * serdar + * 1.1 + * 1993/07/29 01:00:13 + * + */ +#include +#include +#include "st.h" + +#ifndef ABS +# define ABS(a) ((a) < 0 ? -(a) : (a)) +#endif + +#ifndef ALLOC +#define ALLOC(type, num) ((type *) malloc(sizeof(type) * (num))) +#endif + +#ifndef FREE +#define FREE(obj) ((obj) ? (free((char *) (obj)), (obj) = 0) : 0) +#endif + +#ifndef REALLOC +#define REALLOC(type, obj, num) \ + ((obj) ? ((type *) realloc((char *)(obj), sizeof(type) * (num))) : \ + ((type *) malloc(sizeof(type) * (num)))) +#endif + +#define ST_NUMCMP(x,y) ((x) != (y)) +#define ST_NUMHASH(x,size) (ABS((long)x)%(size)) +#define ST_PTRHASH(x,size) ((int)((unsigned long)(x)>>2)%size) +#define EQUAL(func, x, y) \ + ((((func) == st_numcmp) || ((func) == st_ptrcmp)) ?\ + (ST_NUMCMP((x),(y)) == 0) : ((*func)((x), (y)) == 0)) + + +#define do_hash(key, table)\ + ((table->hash == st_ptrhash) ? ST_PTRHASH((key),(table)->num_bins) :\ + (table->hash == st_numhash) ? ST_NUMHASH((key), (table)->num_bins) :\ + (*table->hash)((key), (table)->num_bins)) + +static int rehash(); +int st_numhash(), st_ptrhash(), st_numcmp(), st_ptrcmp(); + +st_table * +st_init_table_with_params(compare, hash, size, density, grow_factor, + reorder_flag) +int (*compare)(); +int (*hash)(); +int size; +int density; +double grow_factor; +int reorder_flag; +{ + int i; + st_table *new; + + new = ALLOC(st_table, 1); + if (new == NULL) { + return NULL; + } + new->compare = compare; + new->hash = hash; + new->num_entries = 0; + new->max_density = density; + new->grow_factor = grow_factor; + new->reorder_flag = reorder_flag; + if (size <= 0) { + size = 1; + } + new->num_bins = size; + new->bins = ALLOC(st_table_entry *, size); + if (new->bins == NULL) { + FREE(new); + return NULL; + } + for(i = 0; i < size; i++) { + new->bins[i] = 0; + } + return new; +} + +st_table * +st_init_table(compare, hash) +int (*compare)(); +int (*hash)(); +{ + return st_init_table_with_params(compare, hash, ST_DEFAULT_INIT_TABLE_SIZE, + ST_DEFAULT_MAX_DENSITY, + ST_DEFAULT_GROW_FACTOR, + ST_DEFAULT_REORDER_FLAG); +} + +void +st_free_table(table) +st_table *table; +{ + register st_table_entry *ptr, *next; + int i; + + for(i = 0; i < table->num_bins ; i++) { + ptr = table->bins[i]; + while (ptr != NULL) { + next = ptr->next; + FREE(ptr); + ptr = next; + } + } + FREE(table->bins); + FREE(table); +} + +#define PTR_NOT_EQUAL(table, ptr, user_key)\ +(ptr != NULL && !EQUAL(table->compare, user_key, (ptr)->key)) + +#define FIND_ENTRY(table, hash_val, key, ptr, last) \ + (last) = &(table)->bins[hash_val];\ + (ptr) = *(last);\ + while (PTR_NOT_EQUAL((table), (ptr), (key))) {\ + (last) = &(ptr)->next; (ptr) = *(last);\ + }\ + if ((ptr) != NULL && (table)->reorder_flag) {\ + *(last) = (ptr)->next;\ + (ptr)->next = (table)->bins[hash_val];\ + (table)->bins[hash_val] = (ptr);\ + } + +int +st_lookup(table, key, value) +st_table *table; +register char *key; +char **value; +{ + int hash_val; + register st_table_entry *ptr, **last; + + hash_val = do_hash(key, table); + + FIND_ENTRY(table, hash_val, key, ptr, last); + + if (ptr == NULL) { + return 0; + } else { + if (value != NULL) { + *value = ptr->record; + } + return 1; + } +} + +int +st_lookup_int(table, key, value) +st_table *table; +register char *key; +int *value; +{ + int hash_val; + register st_table_entry *ptr, **last; + + hash_val = do_hash(key, table); + + FIND_ENTRY(table, hash_val, key, ptr, last); + + if (ptr == NULL) { + return 0; + } else { + if (value != 0) { + *value = (long) ptr->record; + } + return 1; + } +} + +/* This macro does not check if memory allocation fails. Use at you own risk */ +#define ADD_DIRECT(table, key, value, hash_val, new)\ +{\ + if (table->num_entries/table->num_bins >= table->max_density) {\ + rehash(table);\ + hash_val = do_hash(key,table);\ + }\ + \ + new = ALLOC(st_table_entry, 1);\ + \ + new->key = key;\ + new->record = value;\ + new->next = table->bins[hash_val];\ + table->bins[hash_val] = new;\ + table->num_entries++;\ +} + +int +st_insert(table, key, value) +register st_table *table; +register char *key; +char *value; +{ + int hash_val; + st_table_entry *new; + register st_table_entry *ptr, **last; + + hash_val = do_hash(key, table); + + FIND_ENTRY(table, hash_val, key, ptr, last); + + if (ptr == NULL) { + if (table->num_entries/table->num_bins >= table->max_density) { + if (rehash(table) == ST_OUT_OF_MEM) { + return ST_OUT_OF_MEM; + } + hash_val = do_hash(key, table); + } + new = ALLOC(st_table_entry, 1); + if (new == NULL) { + return ST_OUT_OF_MEM; + } + new->key = key; + new->record = value; + new->next = table->bins[hash_val]; + table->bins[hash_val] = new; + table->num_entries++; + return 0; + } else { + ptr->record = value; + return 1; + } +} + +int +st_add_direct(table, key, value) +st_table *table; +char *key; +char *value; +{ + int hash_val; + st_table_entry *new; + + hash_val = do_hash(key, table); + if (table->num_entries / table->num_bins >= table->max_density) { + if (rehash(table) == ST_OUT_OF_MEM) { + return ST_OUT_OF_MEM; + } + } + hash_val = do_hash(key, table); + new = ALLOC(st_table_entry, 1); + if (new == NULL) { + return ST_OUT_OF_MEM; + } + new->key = key; + new->record = value; + new->next = table->bins[hash_val]; + table->bins[hash_val] = new; + table->num_entries++; + return 1; +} + +int +st_find_or_add(table, key, slot) +st_table *table; +char *key; +char ***slot; +{ + int hash_val; + st_table_entry *new, *ptr, **last; + + hash_val = do_hash(key, table); + + FIND_ENTRY(table, hash_val, key, ptr, last); + + if (ptr == NULL) { + if (table->num_entries / table->num_bins >= table->max_density) { + if (rehash(table) == ST_OUT_OF_MEM) { + return ST_OUT_OF_MEM; + } + hash_val = do_hash(key, table); + } + new = ALLOC(st_table_entry, 1); + if (new == NULL) { + return ST_OUT_OF_MEM; + } + new->key = key; + new->record = (char *) 0; + new->next = table->bins[hash_val]; + table->bins[hash_val] = new; + table->num_entries++; + if (slot != NULL) *slot = &new->record; + return 0; + } else { + if (slot != NULL) *slot = &ptr->record; + return 1; + } +} + +int +st_find(table, key, slot) +st_table *table; +char *key; +char ***slot; +{ + int hash_val; + st_table_entry *ptr, **last; + + hash_val = do_hash(key, table); + + FIND_ENTRY(table, hash_val, key, ptr, last); + + if (ptr == NULL) { + return 0; + } else { + if (slot != NULL) { + *slot = &ptr->record; + } + return 1; + } +} + +static int +rehash(table) +register st_table *table; +{ + register st_table_entry *ptr, *next, **old_bins; + int i, old_num_bins, hash_val, old_num_entries; + + /* save old values */ + old_bins = table->bins; + old_num_bins = table->num_bins; + old_num_entries = table->num_entries; + + /* rehash */ + table->num_bins = (int)(table->grow_factor * old_num_bins); + if (table->num_bins % 2 == 0) { + table->num_bins += 1; + } + table->num_entries = 0; + table->bins = ALLOC(st_table_entry *, table->num_bins); + if (table->bins == NULL) { + table->bins = old_bins; + table->num_bins = old_num_bins; + table->num_entries = old_num_entries; + return ST_OUT_OF_MEM; + } + /* initialize */ + for (i = 0; i < table->num_bins; i++) { + table->bins[i] = 0; + } + + /* copy data over */ + for (i = 0; i < old_num_bins; i++) { + ptr = old_bins[i]; + while (ptr != NULL) { + next = ptr->next; + hash_val = do_hash(ptr->key, table); + ptr->next = table->bins[hash_val]; + table->bins[hash_val] = ptr; + table->num_entries++; + ptr = next; + } + } + FREE(old_bins); + + return 1; +} + +st_table * +st_copy(old_table) +st_table *old_table; +{ + st_table *new_table; + st_table_entry *ptr, *newptr, *next, *new; + int i, j, num_bins = old_table->num_bins; + + new_table = ALLOC(st_table, 1); + if (new_table == NULL) { + return NULL; + } + + *new_table = *old_table; + new_table->bins = ALLOC(st_table_entry *, num_bins); + if (new_table->bins == NULL) { + FREE(new_table); + return NULL; + } + for(i = 0; i < num_bins ; i++) { + new_table->bins[i] = NULL; + ptr = old_table->bins[i]; + while (ptr != NULL) { + new = ALLOC(st_table_entry, 1); + if (new == NULL) { + for (j = 0; j <= i; j++) { + newptr = new_table->bins[j]; + while (newptr != NULL) { + next = newptr->next; + FREE(newptr); + newptr = next; + } + } + FREE(new_table->bins); + FREE(new_table); + return NULL; + } + *new = *ptr; + new->next = new_table->bins[i]; + new_table->bins[i] = new; + ptr = ptr->next; + } + } + return new_table; +} + +int +st_delete(table, keyp, value) +register st_table *table; +register char **keyp; +char **value; +{ + int hash_val; + char *key = *keyp; + register st_table_entry *ptr, **last; + + hash_val = do_hash(key, table); + + FIND_ENTRY(table, hash_val, key, ptr ,last); + + if (ptr == NULL) { + return 0; + } + + *last = ptr->next; + if (value != NULL) *value = ptr->record; + *keyp = ptr->key; + FREE(ptr); + table->num_entries--; + return 1; +} + +int +st_delete_int(table, keyp, value) +register st_table *table; +register long *keyp; +char **value; +{ + int hash_val; + char *key = (char *) *keyp; + register st_table_entry *ptr, **last; + + hash_val = do_hash(key, table); + + FIND_ENTRY(table, hash_val, key, ptr ,last); + + if (ptr == NULL) { + return 0; + } + + *last = ptr->next; + if (value != NULL) *value = ptr->record; + *keyp = (long) ptr->key; + FREE(ptr); + table->num_entries--; + return 1; +} + +int +st_foreach(table, func, arg) +st_table *table; +enum st_retval (*func)(); +char *arg; +{ + st_table_entry *ptr, **last; + enum st_retval retval; + int i; + + for(i = 0; i < table->num_bins; i++) { + last = &table->bins[i]; ptr = *last; + while (ptr != NULL) { + retval = (*func)(ptr->key, ptr->record, arg); + switch (retval) { + case ST_CONTINUE: + last = &ptr->next; ptr = *last; + break; + case ST_STOP: + return 0; + case ST_DELETE: + *last = ptr->next; + table->num_entries--; /* cstevens@ic */ + FREE(ptr); + ptr = *last; + } + } + } + return 1; +} + +int +st_strhash(string, modulus) +register char *string; +int modulus; +{ + register int val = 0; + register int c; + + while ((c = *string++) != '\0') { + val = val*997 + c; + } + + return ((val < 0) ? -val : val)%modulus; +} + +int +st_numhash(x, size) +char *x; +int size; +{ + return ST_NUMHASH(x, size); +} + +int +st_ptrhash(x, size) +char *x; +int size; +{ + return ST_PTRHASH(x, size); +} + +int +st_numcmp(x, y) +char *x; +char *y; +{ + return ST_NUMCMP(x, y); +} + +int +st_ptrcmp(x, y) +char *x; +char *y; +{ + return ST_NUMCMP(x, y); +} + +st_generator * +st_init_gen(table) +st_table *table; +{ + st_generator *gen; + + gen = ALLOC(st_generator, 1); + if (gen == NULL) { + return NULL; + } + gen->table = table; + gen->entry = NULL; + gen->index = 0; + return gen; +} + + +int +st_gen(gen, key_p, value_p) +st_generator *gen; +char **key_p; +char **value_p; +{ + register int i; + + if (gen->entry == NULL) { + /* try to find next entry */ + for(i = gen->index; i < gen->table->num_bins; i++) { + if (gen->table->bins[i] != NULL) { + gen->index = i+1; + gen->entry = gen->table->bins[i]; + break; + } + } + if (gen->entry == NULL) { + return 0; /* that's all folks ! */ + } + } + *key_p = gen->entry->key; + if (value_p != 0) { + *value_p = gen->entry->record; + } + gen->entry = gen->entry->next; + return 1; +} + + +int +st_gen_int(gen, key_p, value_p) +st_generator *gen; +char **key_p; +long *value_p; +{ + register int i; + + if (gen->entry == NULL) { + /* try to find next entry */ + for(i = gen->index; i < gen->table->num_bins; i++) { + if (gen->table->bins[i] != NULL) { + gen->index = i+1; + gen->entry = gen->table->bins[i]; + break; + } + } + if (gen->entry == NULL) { + return 0; /* that's all folks ! */ + } + } + *key_p = gen->entry->key; + if (value_p != 0) { + *value_p = (long) gen->entry->record; + } + gen->entry = gen->entry->next; + return 1; +} + + +void +st_free_gen(gen) +st_generator *gen; +{ + FREE(gen); +} diff --git a/abc_with_bb_support/src/misc/st/st.h b/abc_with_bb_support/src/misc/st/st.h new file mode 100644 index 000000000..eba452bf3 --- /dev/null +++ b/abc_with_bb_support/src/misc/st/st.h @@ -0,0 +1,96 @@ +/* + * Revision Control Information + * + * /projects/hsis/CVS/utilities/st/st.h,v + * serdar + * 1.1 + * 1993/07/29 01:00:21 + * + */ +/* LINTLIBRARY */ + +/* /projects/hsis/CVS/utilities/st/st.h,v 1.1 1993/07/29 01:00:21 serdar Exp */ + +#ifndef ST_INCLUDED +#define ST_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct st_table_entry st_table_entry; +struct st_table_entry { + char *key; + char *record; + st_table_entry *next; +}; + +typedef struct st_table st_table; +struct st_table { + int (*compare)(); + int (*hash)(); + int num_bins; + int num_entries; + int max_density; + int reorder_flag; + double grow_factor; + st_table_entry **bins; +}; + +typedef struct st_generator st_generator; +struct st_generator { + st_table *table; + st_table_entry *entry; + int index; +}; + +#define st_is_member(table,key) st_lookup(table,key,(char **) 0) +#define st_count(table) ((table)->num_entries) + +enum st_retval {ST_CONTINUE, ST_STOP, ST_DELETE}; + +typedef enum st_retval (*ST_PFSR)(); +typedef int (*ST_PFI)(); + +extern st_table *st_init_table_with_params (ST_PFI, ST_PFI, int, int, double, int); +extern st_table *st_init_table (ST_PFI, ST_PFI); +extern void st_free_table (st_table *); +extern int st_lookup (st_table *, char *, char **); +extern int st_lookup_int (st_table *, char *, int *); +extern int st_insert (st_table *, char *, char *); +extern int st_add_direct (st_table *, char *, char *); +extern int st_find_or_add (st_table *, char *, char ***); +extern int st_find (st_table *, char *, char ***); +extern st_table *st_copy (st_table *); +extern int st_delete (st_table *, char **, char **); +extern int st_delete_int (st_table *, long *, char **); +extern int st_foreach (st_table *, ST_PFSR, char *); +extern int st_strhash (char *, int); +extern int st_numhash (char *, int); +extern int st_ptrhash (char *, int); +extern int st_numcmp (char *, char *); +extern int st_ptrcmp (char *, char *); +extern st_generator *st_init_gen (st_table *); +extern int st_gen (st_generator *, char **, char **); +extern int st_gen_int (st_generator *, char **, long *); +extern void st_free_gen (st_generator *); + + +#define ST_DEFAULT_MAX_DENSITY 5 +#define ST_DEFAULT_INIT_TABLE_SIZE 11 +#define ST_DEFAULT_GROW_FACTOR 2.0 +#define ST_DEFAULT_REORDER_FLAG 0 + +#define st_foreach_item(table, gen, key, value) \ + for(gen=st_init_gen(table); st_gen(gen,key,value) || (st_free_gen(gen),0);) + +#define st_foreach_item_int(table, gen, key, value) \ + for(gen=st_init_gen(table); st_gen_int(gen,key,value) || (st_free_gen(gen),0);) + +#define ST_OUT_OF_MEM -10000 + +#ifdef __cplusplus +} +#endif + +#endif /* ST_INCLUDED */ diff --git a/abc_with_bb_support/src/misc/st/stmm.c b/abc_with_bb_support/src/misc/st/stmm.c new file mode 100644 index 000000000..f1909a971 --- /dev/null +++ b/abc_with_bb_support/src/misc/st/stmm.c @@ -0,0 +1,687 @@ +/* + * Revision Control Information + * + * /projects/hsis/CVS/utilities/st/st.c,v + * serdar + * 1.1 + * 1993/07/29 01:00:13 + * + */ +#include +#include "extra.h" +#include "stmm.h" + +#ifndef ABS +# define ABS(a) ((a) < 0 ? -(a) : (a)) +#endif + +#define STMM_NUMCMP(x,y) ((x) != (y)) +#define STMM_NUMHASH(x,size) (ABS((long)x)%(size)) +#define STMM_PTRHASH(x,size) ((int)((unsigned long)(x)>>2)%size) +#define EQUAL(func, x, y) \ + ((((func) == stmm_numcmp) || ((func) == stmm_ptrcmp)) ?\ + (STMM_NUMCMP((x),(y)) == 0) : ((*func)((x), (y)) == 0)) + + +#define do_hash(key, table)\ + ((table->hash == stmm_ptrhash) ? STMM_PTRHASH((key),(table)->num_bins) :\ + (table->hash == stmm_numhash) ? STMM_NUMHASH((key), (table)->num_bins) :\ + (*table->hash)((key), (table)->num_bins)) + +static int rehash (); +int stmm_numhash (), stmm_ptrhash (), stmm_numcmp (), stmm_ptrcmp (); + +stmm_table * +stmm_init_table_with_params (compare, hash, size, density, grow_factor, + reorder_flag) + int (*compare) (); + int (*hash) (); + int size; + int density; + double grow_factor; + int reorder_flag; +{ + int i; + stmm_table *new; + + new = ALLOC (stmm_table, 1); + if (new == NULL) { + return NULL; + } + new->compare = compare; + new->hash = hash; + new->num_entries = 0; + new->max_density = density; + new->grow_factor = grow_factor; + new->reorder_flag = reorder_flag; + if (size <= 0) { + size = 1; + } + new->num_bins = size; + new->bins = ALLOC (stmm_table_entry *, size); + if (new->bins == NULL) { + FREE (new); + return NULL; + } + for (i = 0; i < size; i++) { + new->bins[i] = 0; + } + + // added by alanmi + new->pMemMan = Extra_MmFixedStart(sizeof (stmm_table_entry)); + return new; +} + +stmm_table * +stmm_init_table (compare, hash) + int (*compare) (); + int (*hash) (); +{ + return stmm_init_table_with_params (compare, hash, + STMM_DEFAULT_INIT_TABLE_SIZE, + STMM_DEFAULT_MAX_DENSITY, + STMM_DEFAULT_GROW_FACTOR, + STMM_DEFAULT_REORDER_FLAG); +} + +void +stmm_free_table (table) + stmm_table *table; +{ +/* + register stmm_table_entry *ptr, *next; + int i; + for ( i = 0; i < table->num_bins; i++ ) + { + ptr = table->bins[i]; + while ( ptr != NULL ) + { + next = ptr->next; + FREE( ptr ); + ptr = next; + } + } +*/ + // no need to deallocate entries because they are in the memory manager now + // added by alanmi + if ( table->pMemMan ) + Extra_MmFixedStop (table->pMemMan); + FREE (table->bins); + FREE (table); +} + +// this function recycles all the bins +void +stmm_clean (table) + stmm_table *table; +{ + int i; + // clean the bins + for (i = 0; i < table->num_bins; i++) + table->bins[i] = NULL; + // reset the parameters + table->num_entries = 0; + // restart the memory manager + Extra_MmFixedRestart (table->pMemMan); +} + + +#define PTR_NOT_EQUAL(table, ptr, user_key)\ +(ptr != NULL && !EQUAL(table->compare, user_key, (ptr)->key)) + +#define FIND_ENTRY(table, hash_val, key, ptr, last) \ + (last) = &(table)->bins[hash_val];\ + (ptr) = *(last);\ + while (PTR_NOT_EQUAL((table), (ptr), (key))) {\ + (last) = &(ptr)->next; (ptr) = *(last);\ + }\ + if ((ptr) != NULL && (table)->reorder_flag) {\ + *(last) = (ptr)->next;\ + (ptr)->next = (table)->bins[hash_val];\ + (table)->bins[hash_val] = (ptr);\ + } + +int +stmm_lookup (table, key, value) + stmm_table *table; + register char *key; + char **value; +{ + int hash_val; + register stmm_table_entry *ptr, **last; + + hash_val = do_hash (key, table); + + FIND_ENTRY (table, hash_val, key, ptr, last); + + if (ptr == NULL) { + return 0; + } + else { + if (value != NULL) + { + *value = ptr->record; + } + return 1; + } +} + +int +stmm_lookup_int (table, key, value) + stmm_table *table; + register char *key; + int *value; +{ + int hash_val; + register stmm_table_entry *ptr, **last; + + hash_val = do_hash (key, table); + + FIND_ENTRY (table, hash_val, key, ptr, last); + + if (ptr == NULL) { + return 0; + } + else { + if (value != 0) + { + *value = (long) ptr->record; + } + return 1; + } +} + +// This macro contained a line +// new = ALLOC(stmm_table_entry, 1); +// which was modified by alanmi + + +/* This macro does not check if memory allocation fails. Use at you own risk */ +#define ADD_DIRECT(table, key, value, hash_val, new)\ +{\ + if (table->num_entries/table->num_bins >= table->max_density) {\ + rehash(table);\ + hash_val = do_hash(key,table);\ + }\ + \ + new = (stmm_table_entry *)Extra_MmFixedEntryFetch( table->pMemMan );\ + \ + new->key = key;\ + new->record = value;\ + new->next = table->bins[hash_val];\ + table->bins[hash_val] = new;\ + table->num_entries++;\ +} + +int +stmm_insert (table, key, value) + register stmm_table *table; + register char *key; + char *value; +{ + int hash_val; + stmm_table_entry *new; + register stmm_table_entry *ptr, **last; + + hash_val = do_hash (key, table); + + FIND_ENTRY (table, hash_val, key, ptr, last); + + if (ptr == NULL) { + if (table->num_entries / table->num_bins >= table->max_density) { + if (rehash (table) == STMM_OUT_OF_MEM) { + return STMM_OUT_OF_MEM; + } + hash_val = do_hash (key, table); + } + +// new = ALLOC( stmm_table_entry, 1 ); + new = (stmm_table_entry *) Extra_MmFixedEntryFetch (table->pMemMan); + if (new == NULL) { + return STMM_OUT_OF_MEM; + } + + new->key = key; + new->record = value; + new->next = table->bins[hash_val]; + table->bins[hash_val] = new; + table->num_entries++; + return 0; + } + else { + ptr->record = value; + return 1; + } +} + +int +stmm_add_direct (table, key, value) + stmm_table *table; + char *key; + char *value; +{ + int hash_val; + stmm_table_entry *new; + + hash_val = do_hash (key, table); + if (table->num_entries / table->num_bins >= table->max_density) { + if (rehash (table) == STMM_OUT_OF_MEM) { + return STMM_OUT_OF_MEM; + } + } + hash_val = do_hash (key, table); + +// new = ALLOC( stmm_table_entry, 1 ); + new = (stmm_table_entry *) Extra_MmFixedEntryFetch (table->pMemMan); + if (new == NULL) { + return STMM_OUT_OF_MEM; + } + + new->key = key; + new->record = value; + new->next = table->bins[hash_val]; + table->bins[hash_val] = new; + table->num_entries++; + return 1; +} + +int +stmm_find_or_add (table, key, slot) + stmm_table *table; + char *key; + char ***slot; +{ + int hash_val; + stmm_table_entry *new, *ptr, **last; + + hash_val = do_hash (key, table); + + FIND_ENTRY (table, hash_val, key, ptr, last); + + if (ptr == NULL) { + if (table->num_entries / table->num_bins >= table->max_density) { + if (rehash (table) == STMM_OUT_OF_MEM) { + return STMM_OUT_OF_MEM; + } + hash_val = do_hash (key, table); + } + + // new = ALLOC( stmm_table_entry, 1 ); + new = (stmm_table_entry *) Extra_MmFixedEntryFetch (table->pMemMan); + if (new == NULL) { + return STMM_OUT_OF_MEM; + } + + new->key = key; + new->record = (char *) 0; + new->next = table->bins[hash_val]; + table->bins[hash_val] = new; + table->num_entries++; + if (slot != NULL) + *slot = &new->record; + return 0; + } + else { + if (slot != NULL) + *slot = &ptr->record; + return 1; + } +} + +int +stmm_find (table, key, slot) + stmm_table *table; + char *key; + char ***slot; +{ + int hash_val; + stmm_table_entry *ptr, **last; + + hash_val = do_hash (key, table); + + FIND_ENTRY (table, hash_val, key, ptr, last); + + if (ptr == NULL) { + return 0; + } + else { + if (slot != NULL) + { + *slot = &ptr->record; + } + return 1; + } +} + +static int +rehash (table) + register stmm_table *table; +{ + register stmm_table_entry *ptr, *next, **old_bins; + int i, old_num_bins, hash_val, old_num_entries; + + /* save old values */ + old_bins = table->bins; + old_num_bins = table->num_bins; + old_num_entries = table->num_entries; + + /* rehash */ + table->num_bins = (int) (table->grow_factor * old_num_bins); + if (table->num_bins % 2 == 0) { + table->num_bins += 1; + } + table->num_entries = 0; + table->bins = ALLOC (stmm_table_entry *, table->num_bins); + if (table->bins == NULL) { + table->bins = old_bins; + table->num_bins = old_num_bins; + table->num_entries = old_num_entries; + return STMM_OUT_OF_MEM; + } + /* initialize */ + for (i = 0; i < table->num_bins; i++) { + table->bins[i] = 0; + } + + /* copy data over */ + for (i = 0; i < old_num_bins; i++) { + ptr = old_bins[i]; + while (ptr != NULL) { + next = ptr->next; + hash_val = do_hash (ptr->key, table); + ptr->next = table->bins[hash_val]; + table->bins[hash_val] = ptr; + table->num_entries++; + ptr = next; + } + } + FREE (old_bins); + + return 1; +} + +stmm_table * +stmm_copy (old_table) + stmm_table *old_table; +{ + stmm_table *new_table; + stmm_table_entry *ptr, /* *newptr, *next, */ *new; + int i, /*j, */ num_bins = old_table->num_bins; + + new_table = ALLOC (stmm_table, 1); + if (new_table == NULL) { + return NULL; + } + + *new_table = *old_table; + new_table->bins = ALLOC (stmm_table_entry *, num_bins); + if (new_table->bins == NULL) { + FREE (new_table); + return NULL; + } + + // allocate the memory manager for the new table + new_table->pMemMan = + Extra_MmFixedStart (sizeof (stmm_table_entry)); + + for (i = 0; i < num_bins; i++) { + new_table->bins[i] = NULL; + ptr = old_table->bins[i]; + while (ptr != NULL) { +// new = ALLOC( stmm_table_entry, 1 ); + new = + (stmm_table_entry *) Extra_MmFixedEntryFetch (new_table-> + pMemMan); + + if (new == NULL) { +/* + for ( j = 0; j <= i; j++ ) + { + newptr = new_table->bins[j]; + while ( newptr != NULL ) + { + next = newptr->next; + FREE( newptr ); + newptr = next; + } + } +*/ + Extra_MmFixedStop (new_table->pMemMan); + + FREE (new_table->bins); + FREE (new_table); + return NULL; + } + *new = *ptr; + new->next = new_table->bins[i]; + new_table->bins[i] = new; + ptr = ptr->next; + } + } + return new_table; +} + +int +stmm_delete (table, keyp, value) + register stmm_table *table; + register char **keyp; + char **value; +{ + int hash_val; + char *key = *keyp; + register stmm_table_entry *ptr, **last; + + hash_val = do_hash (key, table); + + FIND_ENTRY (table, hash_val, key, ptr, last); + + if (ptr == NULL) { + return 0; + } + + *last = ptr->next; + if (value != NULL) + *value = ptr->record; + *keyp = ptr->key; +// FREE( ptr ); + Extra_MmFixedEntryRecycle (table->pMemMan, (char *) ptr); + + table->num_entries--; + return 1; +} + +int +stmm_delete_int (table, keyp, value) + register stmm_table *table; + register long *keyp; + char **value; +{ + int hash_val; + char *key = (char *) *keyp; + register stmm_table_entry *ptr, **last; + + hash_val = do_hash (key, table); + + FIND_ENTRY (table, hash_val, key, ptr, last); + + if (ptr == NULL) { + return 0; + } + + *last = ptr->next; + if (value != NULL) + *value = ptr->record; + *keyp = (long) ptr->key; +// FREE( ptr ); + Extra_MmFixedEntryRecycle (table->pMemMan, (char *) ptr); + + table->num_entries--; + return 1; +} + +int +stmm_foreach (table, func, arg) + stmm_table *table; + enum stmm_retval (*func) (); + char *arg; +{ + stmm_table_entry *ptr, **last; + enum stmm_retval retval; + int i; + + for (i = 0; i < table->num_bins; i++) { + last = &table->bins[i]; + ptr = *last; + while (ptr != NULL) { + retval = (*func) (ptr->key, ptr->record, arg); + switch (retval) { + case STMM_CONTINUE: + last = &ptr->next; + ptr = *last; + break; + case STMM_STOP: + return 0; + case STMM_DELETE: + *last = ptr->next; + table->num_entries--; /* cstevens@ic */ +// FREE( ptr ); + Extra_MmFixedEntryRecycle (table->pMemMan, (char *) ptr); + + ptr = *last; + } + } + } + return 1; +} + +int +stmm_strhash (string, modulus) + register char *string; + int modulus; +{ + register int val = 0; + register int c; + + while ((c = *string++) != '\0') { + val = val * 997 + c; + } + + return ((val < 0) ? -val : val) % modulus; +} + +int +stmm_numhash (x, size) + char *x; + int size; +{ + return STMM_NUMHASH (x, size); +} + +int +stmm_ptrhash (x, size) + char *x; + int size; +{ + return STMM_PTRHASH (x, size); +} + +int +stmm_numcmp (x, y) + char *x; + char *y; +{ + return STMM_NUMCMP (x, y); +} + +int +stmm_ptrcmp (x, y) + char *x; + char *y; +{ + return STMM_NUMCMP (x, y); +} + +stmm_generator * +stmm_init_gen (table) + stmm_table *table; +{ + stmm_generator *gen; + + gen = ALLOC (stmm_generator, 1); + if (gen == NULL) { + return NULL; + } + gen->table = table; + gen->entry = NULL; + gen->index = 0; + return gen; +} + + +int +stmm_gen (gen, key_p, value_p) + stmm_generator *gen; + char **key_p; + char **value_p; +{ + register int i; + + if (gen->entry == NULL) { + /* try to find next entry */ + for (i = gen->index; i < gen->table->num_bins; i++) { + if (gen->table->bins[i] != NULL) { + gen->index = i + 1; + gen->entry = gen->table->bins[i]; + break; + } + } + if (gen->entry == NULL) { + return 0; /* that's all folks ! */ + } + } + *key_p = gen->entry->key; + if (value_p != 0) { + *value_p = gen->entry->record; + } + gen->entry = gen->entry->next; + return 1; +} + + +int +stmm_gen_int (gen, key_p, value_p) + stmm_generator *gen; + char **key_p; + long *value_p; +{ + register int i; + + if (gen->entry == NULL) { + /* try to find next entry */ + for (i = gen->index; i < gen->table->num_bins; i++) { + if (gen->table->bins[i] != NULL) { + gen->index = i + 1; + gen->entry = gen->table->bins[i]; + break; + } + } + if (gen->entry == NULL) { + return 0; /* that's all folks ! */ + } + } + *key_p = gen->entry->key; + if (value_p != 0) + { + *value_p = (long) gen->entry->record; + } + gen->entry = gen->entry->next; + return 1; +} + + +void +stmm_free_gen (gen) + stmm_generator *gen; +{ + FREE (gen); +} diff --git a/abc_with_bb_support/src/misc/st/stmm.h b/abc_with_bb_support/src/misc/st/stmm.h new file mode 100644 index 000000000..6b047c498 --- /dev/null +++ b/abc_with_bb_support/src/misc/st/stmm.h @@ -0,0 +1,127 @@ +/* + * Revision Control Information + * + * /projects/hsis/CVS/utilities/st/st.h,v + * serdar + * 1.1 + * 1993/07/29 01:00:21 + * + */ +/* LINTLIBRARY */ + +/* /projects/hsis/CVS/utilities/st/st.h,v 1.1 1993/07/29 01:00:21 serdar Exp */ + +#ifndef STMM_INCLUDED +#define STMM_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#include "extra.h" + +typedef struct stmm_table_entry stmm_table_entry; +typedef struct stmm_table stmm_table; +typedef struct stmm_generator stmm_generator; + +struct stmm_table_entry +{ + char *key; + char *record; + stmm_table_entry *next; +}; + +struct stmm_table +{ + int (*compare) (); + int (*hash) (); + int num_bins; + int num_entries; + int max_density; + int reorder_flag; + double grow_factor; + stmm_table_entry **bins; + // memory manager to improve runtime and prevent memory fragmentation + // added by alanmi - January 16, 2003 + Extra_MmFixed_t *pMemMan; +}; + +struct stmm_generator +{ + stmm_table *table; + stmm_table_entry *entry; + int index; +}; + +#define stmm_is_member(table,key) stmm_lookup(table,key,(char **) 0) +#define stmm_count(table) ((table)->num_entries) + +enum stmm_retval +{ STMM_CONTINUE, STMM_STOP, STMM_DELETE }; + +typedef enum stmm_retval (*STMM_PFSR) (); +typedef int (*STMM_PFI) (); + +EXTERN stmm_table *stmm_init_table_with_params +ARGS ((STMM_PFI, STMM_PFI, int, int, double, int)); +EXTERN stmm_table *stmm_init_table ARGS ((STMM_PFI, STMM_PFI)); +EXTERN void stmm_free_table ARGS ((stmm_table *)); +EXTERN int stmm_lookup ARGS ((stmm_table *, char *, char **)); +EXTERN int stmm_lookup_int ARGS ((stmm_table *, char *, int *)); +EXTERN int stmm_insert ARGS ((stmm_table *, char *, char *)); +EXTERN int stmm_add_direct ARGS ((stmm_table *, char *, char *)); +EXTERN int stmm_find_or_add ARGS ((stmm_table *, char *, char ***)); +EXTERN int stmm_find ARGS ((stmm_table *, char *, char ***)); +EXTERN stmm_table *stmm_copy ARGS ((stmm_table *)); +EXTERN int stmm_delete ARGS ((stmm_table *, char **, char **)); +EXTERN int stmm_delete_int ARGS ((stmm_table *, long *, char **)); +EXTERN int stmm_foreach ARGS ((stmm_table *, STMM_PFSR, char *)); +EXTERN int stmm_strhash ARGS ((char *, int)); +EXTERN int stmm_numhash ARGS ((char *, int)); +EXTERN int stmm_ptrhash ARGS ((char *, int)); +EXTERN int stmm_numcmp ARGS ((char *, char *)); +EXTERN int stmm_ptrcmp ARGS ((char *, char *)); +EXTERN stmm_generator *stmm_init_gen ARGS ((stmm_table *)); +EXTERN int stmm_gen ARGS ((stmm_generator *, char **, char **)); +EXTERN int stmm_gen_int ARGS ((stmm_generator *, char **, long *)); +EXTERN void stmm_free_gen ARGS ((stmm_generator *)); +// additional functions +EXTERN void stmm_clean ARGS ((stmm_table *)); + + + +#define STMM_DEFAULT_MAX_DENSITY 5 +#define STMM_DEFAULT_INIT_TABLE_SIZE 11 +#define STMM_DEFAULT_GROW_FACTOR 2.0 +#define STMM_DEFAULT_REORDER_FLAG 0 + +// added by Zhihong: no need for memory allocation +#define stmm_foreach_item2(tb, /* stmm_generator */gen, key, value) \ + for(gen.table=(tb), gen.entry=NULL, gen.index=0; \ + stmm_gen(&(gen),key,value);) + +#define stmm_foreach_item(table, gen, key, value) \ + for(gen=stmm_init_gen(table); stmm_gen(gen,key,value) || (stmm_free_gen(gen),0);) + +#define stmm_foreach_item_int(table, gen, key, value) \ + for(gen=stmm_init_gen(table); stmm_gen_int(gen,key,value) || (stmm_free_gen(gen),0);) + +#define STMM_OUT_OF_MEM -10000 + +/* + +// consider adding these other other similar definitions +#define st_table stmm_table +#define st_insert stmm_insert +#define st_delete stmm_delete +#define st_lookup stmm_lookup +#define st_init_table stmm_init_table +#define st_free_table stmm_free_table + +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* STMM_INCLUDED */ diff --git a/abc_with_bb_support/src/misc/util/leaks.h b/abc_with_bb_support/src/misc/util/leaks.h new file mode 100644 index 000000000..56f5199a1 --- /dev/null +++ b/abc_with_bb_support/src/misc/util/leaks.h @@ -0,0 +1,30 @@ +////////////////////////////////////////////////////////////////////////// +// This file is used to detect memory leaks using Visual Studio 6.0 +// The idea comes from this page: http://www.michaelmoser.org/memory.htm +// In addition to this file, it required the presence of "stdlib_hack.h" +////////////////////////////////////////////////////////////////////////// + +#ifndef __LEAKS_H__ +#define __LEAKS_H__ + +#ifdef _DEBUG +#define _CRTDBG_MAP_ALLOC // include Microsoft memory leak detection procedures +//#define _INC_MALLOC // exclude standard memory alloc procedures + +#define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define calloc(c, s) _calloc_dbg(c, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define realloc(p, s) _realloc_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__) +//#define _expand(p, s) _expand_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__) +//#define free(p) _free_dbg(p, _NORMAL_BLOCK) +//#define _msize(p) _msize_dbg(p, _NORMAL_BLOCK) + +//#include +#include +#include +#endif + +#endif + +////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/misc/util/module.make b/abc_with_bb_support/src/misc/util/module.make new file mode 100644 index 000000000..d6d908e73 --- /dev/null +++ b/abc_with_bb_support/src/misc/util/module.make @@ -0,0 +1 @@ +SRC += diff --git a/abc_with_bb_support/src/misc/util/stdlib_hack.h b/abc_with_bb_support/src/misc/util/stdlib_hack.h new file mode 100644 index 000000000..8e6c0e66b --- /dev/null +++ b/abc_with_bb_support/src/misc/util/stdlib_hack.h @@ -0,0 +1,471 @@ +/*** +*stdlib.h - declarations/definitions for commonly used library functions +* +* Copyright (c) 1985-1997, Microsoft Corporation. All rights reserved. +* +*Purpose: +* This include file contains the function declarations for commonly +* used library functions which either don't fit somewhere else, or, +* cannot be declared in the normal place for other reasons. +* +* [ANSI] +* +* [Public] +* +****/ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#ifndef _INC_STDLIB +#define _INC_STDLIB + +#if !defined(_WIN32) && !defined(_MAC) +#error ERROR: Only Mac or Win32 targets supported! +#endif + + +#ifdef _MSC_VER +/* + * Currently, all MS C compilers for Win32 platforms default to 8 byte + * alignment. + */ +#pragma pack(push,8) +#endif /* _MSC_VER */ + +#ifdef __cplusplus +extern "C" { +#endif + + + +/* Define _CRTIMP */ + +#ifndef _CRTIMP +#ifdef _DLL +#define _CRTIMP __declspec(dllimport) +#else /* ndef _DLL */ +#define _CRTIMP +#endif /* _DLL */ +#endif /* _CRTIMP */ + + +/* Define __cdecl for non-Microsoft compilers */ + +#if ( !defined(_MSC_VER) && !defined(__cdecl) ) +#define __cdecl +#endif + +/* Define _CRTAPI1 (for compatibility with the NT SDK) */ + +#ifndef _CRTAPI1 +#if _MSC_VER >= 800 && _M_IX86 >= 300 +#define _CRTAPI1 __cdecl +#else +#define _CRTAPI1 +#endif +#endif + + +#ifndef _SIZE_T_DEFINED +typedef unsigned int size_t; +#define _SIZE_T_DEFINED +#endif + + +#ifndef _WCHAR_T_DEFINED +typedef unsigned short wchar_t; +#define _WCHAR_T_DEFINED +#endif + + +/* Define NULL pointer value */ + +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *)0) +#endif +#endif + + +/* Definition of the argument values for the exit() function */ + +#define EXIT_SUCCESS 0 +#define EXIT_FAILURE 1 + + +#ifndef _ONEXIT_T_DEFINED +typedef int (__cdecl * _onexit_t)(void); +#if !__STDC__ +/* Non-ANSI name for compatibility */ +#define onexit_t _onexit_t +#endif +#define _ONEXIT_T_DEFINED +#endif + + +/* Data structure definitions for div and ldiv runtimes. */ + +#ifndef _DIV_T_DEFINED + +typedef struct _div_t { + int quot; + int rem; +} div_t; + +typedef struct _ldiv_t { + long quot; + long rem; +} ldiv_t; + +#define _DIV_T_DEFINED +#endif + + +/* Maximum value that can be returned by the rand function. */ + +#define RAND_MAX 0x7fff + +/* + * Maximum number of bytes in multi-byte character in the current locale + * (also defined in ctype.h). + */ +#ifndef MB_CUR_MAX +#define MB_CUR_MAX __mb_cur_max +_CRTIMP extern int __mb_cur_max; +#endif /* MB_CUR_MAX */ + + +/* Minimum and maximum macros */ + +#define __max(a,b) (((a) > (b)) ? (a) : (b)) +#define __min(a,b) (((a) < (b)) ? (a) : (b)) + +/* + * Sizes for buffers used by the _makepath() and _splitpath() functions. + * note that the sizes include space for 0-terminator + */ +#ifndef _MAC +#define _MAX_PATH 260 /* max. length of full pathname */ +#define _MAX_DRIVE 3 /* max. length of drive component */ +#define _MAX_DIR 256 /* max. length of path component */ +#define _MAX_FNAME 256 /* max. length of file name component */ +#define _MAX_EXT 256 /* max. length of extension component */ +#else /* def _MAC */ +#define _MAX_PATH 256 /* max. length of full pathname */ +#define _MAX_DIR 32 /* max. length of path component */ +#define _MAX_FNAME 64 /* max. length of file name component */ +#endif /* _MAC */ + +/* + * Argument values for _set_error_mode(). + */ +#define _OUT_TO_DEFAULT 0 +#define _OUT_TO_STDERR 1 +#define _OUT_TO_MSGBOX 2 +#define _REPORT_ERRMODE 3 + + +/* External variable declarations */ + +#if (defined(_MT) || defined(_DLL)) && !defined(_MAC) +_CRTIMP int * __cdecl _errno(void); +_CRTIMP unsigned long * __cdecl __doserrno(void); +#define errno (*_errno()) +#define _doserrno (*__doserrno()) +#else /* ndef _MT && ndef _DLL */ +_CRTIMP extern int errno; /* XENIX style error number */ +_CRTIMP extern unsigned long _doserrno; /* OS system error value */ +#endif /* _MT || _DLL */ + + +#ifdef _MAC +_CRTIMP extern int _macerrno; /* OS system error value */ +#endif + + +_CRTIMP extern char * _sys_errlist[]; /* perror error message table */ +_CRTIMP extern int _sys_nerr; /* # of entries in sys_errlist table */ + + +#if defined(_DLL) && defined(_M_IX86) + +#define __argc (*__p___argc()) /* count of cmd line args */ +#define __argv (*__p___argv()) /* pointer to table of cmd line args */ +#define __wargv (*__p___wargv()) /* pointer to table of wide cmd line args */ +#define _environ (*__p__environ()) /* pointer to environment table */ +#ifdef _POSIX_ +extern char ** environ; /* pointer to environment table */ +#else +#ifndef _MAC +#define _wenviron (*__p__wenviron()) /* pointer to wide environment table */ +#endif /* ndef _MAC */ +#endif /* _POSIX_ */ +#define _pgmptr (*__p__pgmptr()) /* points to the module (EXE) name */ +#ifndef _MAC +#define _wpgmptr (*__p__wpgmptr()) /* points to the module (EXE) wide name */ +#endif /* ndef _MAC */ + +_CRTIMP int * __cdecl __p___argc(void); +_CRTIMP char *** __cdecl __p___argv(void); +_CRTIMP wchar_t *** __cdecl __p___wargv(void); +_CRTIMP char *** __cdecl __p__environ(void); +_CRTIMP wchar_t *** __cdecl __p__wenviron(void); +_CRTIMP char ** __cdecl __p__pgmptr(void); +_CRTIMP wchar_t ** __cdecl __p__wpgmptr(void); + + +#else + +_CRTIMP extern int __argc; /* count of cmd line args */ +_CRTIMP extern char ** __argv; /* pointer to table of cmd line args */ +#ifndef _MAC +_CRTIMP extern wchar_t ** __wargv; /* pointer to table of wide cmd line args */ +#endif /* ndef _MAC */ + +#ifdef _POSIX_ +extern char ** environ; /* pointer to environment table */ +#else +_CRTIMP extern char ** _environ; /* pointer to environment table */ +#ifndef _MAC +_CRTIMP extern wchar_t ** _wenviron; /* pointer to wide environment table */ +#endif /* ndef _MAC */ +#endif /* _POSIX_ */ + +_CRTIMP extern char * _pgmptr; /* points to the module (EXE) name */ +#ifndef _MAC +_CRTIMP extern wchar_t * _wpgmptr; /* points to the module (EXE) wide name */ +#endif /* ndef _MAC */ + +#endif + + +_CRTIMP extern int _fmode; /* default file translation mode */ +_CRTIMP extern int _fileinfo; /* open file info mode (for spawn) */ + + +/* Windows major/minor and O.S. version numbers */ + +_CRTIMP extern unsigned int _osver; +_CRTIMP extern unsigned int _winver; +_CRTIMP extern unsigned int _winmajor; +_CRTIMP extern unsigned int _winminor; + + +/* function prototypes */ + +#if _MSC_VER >= 1200 +_CRTIMP __declspec(noreturn) void __cdecl abort(void); +_CRTIMP __declspec(noreturn) void __cdecl exit(int); +#else +_CRTIMP void __cdecl abort(void); +_CRTIMP void __cdecl exit(int); +#endif + +#if defined(_M_MRX000) +_CRTIMP int __cdecl abs(int); +#else + int __cdecl abs(int); +#endif + int __cdecl atexit(void (__cdecl *)(void)); +_CRTIMP double __cdecl atof(const char *); +_CRTIMP int __cdecl atoi(const char *); +_CRTIMP long __cdecl atol(const char *); +#ifdef _M_M68K +_CRTIMP long double __cdecl _atold(const char *); +#endif +_CRTIMP void * __cdecl bsearch(const void *, const void *, size_t, size_t, + int (__cdecl *)(const void *, const void *)); + +#ifndef _CRTDBG_MAP_ALLOC +_CRTIMP void * __cdecl calloc(size_t, size_t); +#endif + +_CRTIMP div_t __cdecl div(int, int); +_CRTIMP void __cdecl free(void *); +_CRTIMP char * __cdecl getenv(const char *); +_CRTIMP char * __cdecl _itoa(int, char *, int); +#if _INTEGRAL_MAX_BITS >= 64 +_CRTIMP char * __cdecl _i64toa(__int64, char *, int); +_CRTIMP char * __cdecl _ui64toa(unsigned __int64, char *, int); +_CRTIMP __int64 __cdecl _atoi64(const char *); +#endif +#if defined(_M_MRX000) +_CRTIMP long __cdecl labs(long); +#else + long __cdecl labs(long); +#endif +_CRTIMP ldiv_t __cdecl ldiv(long, long); +_CRTIMP char * __cdecl _ltoa(long, char *, int); + +#ifndef _CRTDBG_MAP_ALLOC +_CRTIMP void * __cdecl malloc(size_t); +#endif + +_CRTIMP int __cdecl mblen(const char *, size_t); +_CRTIMP size_t __cdecl _mbstrlen(const char *s); +_CRTIMP int __cdecl mbtowc(wchar_t *, const char *, size_t); +_CRTIMP size_t __cdecl mbstowcs(wchar_t *, const char *, size_t); +_CRTIMP void __cdecl qsort(void *, size_t, size_t, int (__cdecl *) + (const void *, const void *)); +_CRTIMP int __cdecl rand(void); + +#ifndef _CRTDBG_MAP_ALLOC +_CRTIMP void * __cdecl realloc(void *, size_t); +#endif + +_CRTIMP int __cdecl _set_error_mode(int); +_CRTIMP void __cdecl srand(unsigned int); +_CRTIMP double __cdecl strtod(const char *, char **); +_CRTIMP long __cdecl strtol(const char *, char **, int); +#ifdef _M_M68K +_CRTIMP long double __cdecl _strtold(const char *, char **); +#endif +_CRTIMP unsigned long __cdecl strtoul(const char *, char **, int); +#ifndef _MAC +_CRTIMP int __cdecl system(const char *); +#endif +_CRTIMP char * __cdecl _ultoa(unsigned long, char *, int); +_CRTIMP int __cdecl wctomb(char *, wchar_t); +_CRTIMP size_t __cdecl wcstombs(char *, const wchar_t *, size_t); + + +#ifndef _MAC +#ifndef _WSTDLIB_DEFINED + +/* wide function prototypes, also declared in wchar.h */ + +_CRTIMP wchar_t * __cdecl _itow (int, wchar_t *, int); +_CRTIMP wchar_t * __cdecl _ltow (long, wchar_t *, int); +_CRTIMP wchar_t * __cdecl _ultow (unsigned long, wchar_t *, int); +_CRTIMP double __cdecl wcstod(const wchar_t *, wchar_t **); +_CRTIMP long __cdecl wcstol(const wchar_t *, wchar_t **, int); +_CRTIMP unsigned long __cdecl wcstoul(const wchar_t *, wchar_t **, int); +_CRTIMP wchar_t * __cdecl _wgetenv(const wchar_t *); +_CRTIMP int __cdecl _wsystem(const wchar_t *); +_CRTIMP int __cdecl _wtoi(const wchar_t *); +_CRTIMP long __cdecl _wtol(const wchar_t *); +#if _INTEGRAL_MAX_BITS >= 64 +_CRTIMP wchar_t * __cdecl _i64tow(__int64, wchar_t *, int); +_CRTIMP wchar_t * __cdecl _ui64tow(unsigned __int64, wchar_t *, int); +_CRTIMP __int64 __cdecl _wtoi64(const wchar_t *); +#endif + +#define _WSTDLIB_DEFINED +#endif +#endif /* ndef _MAC */ + + +#ifndef _POSIX_ + +_CRTIMP char * __cdecl _ecvt(double, int, int *, int *); +#if _MSC_VER >= 1200 +_CRTIMP __declspec(noreturn) void __cdecl _exit(int); +#else +_CRTIMP void __cdecl _exit(int); +#endif +_CRTIMP char * __cdecl _fcvt(double, int, int *, int *); +_CRTIMP char * __cdecl _fullpath(char *, const char *, size_t); +_CRTIMP char * __cdecl _gcvt(double, int, char *); + unsigned long __cdecl _lrotl(unsigned long, int); + unsigned long __cdecl _lrotr(unsigned long, int); +#ifndef _MAC +_CRTIMP void __cdecl _makepath(char *, const char *, const char *, const char *, + const char *); +#endif + _onexit_t __cdecl _onexit(_onexit_t); +_CRTIMP void __cdecl perror(const char *); +_CRTIMP int __cdecl _putenv(const char *); + unsigned int __cdecl _rotl(unsigned int, int); + unsigned int __cdecl _rotr(unsigned int, int); +_CRTIMP void __cdecl _searchenv(const char *, const char *, char *); +#ifndef _MAC +_CRTIMP void __cdecl _splitpath(const char *, char *, char *, char *, char *); +#endif +_CRTIMP void __cdecl _swab(char *, char *, int); + +#ifndef _MAC +#ifndef _WSTDLIBP_DEFINED + +/* wide function prototypes, also declared in wchar.h */ + +_CRTIMP wchar_t * __cdecl _wfullpath(wchar_t *, const wchar_t *, size_t); +_CRTIMP void __cdecl _wmakepath(wchar_t *, const wchar_t *, const wchar_t *, const wchar_t *, + const wchar_t *); +_CRTIMP void __cdecl _wperror(const wchar_t *); +_CRTIMP int __cdecl _wputenv(const wchar_t *); +_CRTIMP void __cdecl _wsearchenv(const wchar_t *, const wchar_t *, wchar_t *); +_CRTIMP void __cdecl _wsplitpath(const wchar_t *, wchar_t *, wchar_t *, wchar_t *, wchar_t *); + +#define _WSTDLIBP_DEFINED +#endif +#endif /* ndef _MAC */ + +/* --------- The following functions are OBSOLETE --------- */ +/* The Win32 API SetErrorMode, Beep and Sleep should be used instead. */ +#ifndef _MAC +_CRTIMP void __cdecl _seterrormode(int); +_CRTIMP void __cdecl _beep(unsigned, unsigned); +_CRTIMP void __cdecl _sleep(unsigned long); +#endif /* ndef _MAC */ +/* --------- The preceding functions are OBSOLETE --------- */ + +#endif /* _POSIX_ */ + + +#if !__STDC__ +/* --------- The declarations below should not be in stdlib.h --------- */ +/* --------- and will be removed in a future release. Include --------- */ +/* --------- ctype.h to obtain these declarations. --------- */ +#ifndef tolower /* tolower has been undefined - use function */ +_CRTIMP int __cdecl tolower(int); +#endif /* tolower */ +#ifndef toupper /* toupper has been undefined - use function */ +_CRTIMP int __cdecl toupper(int); +#endif /* toupper */ +/* --------- The declarations above will be removed. --------- */ +#endif + + +#if !__STDC__ + +#ifndef _POSIX_ + +/* Non-ANSI names for compatibility */ + +#ifndef __cplusplus +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#define sys_errlist _sys_errlist +#define sys_nerr _sys_nerr +#define environ _environ + +_CRTIMP char * __cdecl ecvt(double, int, int *, int *); +_CRTIMP char * __cdecl fcvt(double, int, int *, int *); +_CRTIMP char * __cdecl gcvt(double, int, char *); +_CRTIMP char * __cdecl itoa(int, char *, int); +_CRTIMP char * __cdecl ltoa(long, char *, int); + onexit_t __cdecl onexit(onexit_t); +_CRTIMP int __cdecl putenv(const char *); +_CRTIMP void __cdecl swab(char *, char *, int); +_CRTIMP char * __cdecl ultoa(unsigned long, char *, int); + +#endif /* _POSIX_ */ + +#endif /* __STDC__ */ + +#ifdef __cplusplus +} + +#endif + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +#endif /* _INC_STDLIB */ diff --git a/abc_with_bb_support/src/misc/util/util_hack.h b/abc_with_bb_support/src/misc/util/util_hack.h new file mode 100644 index 000000000..1b1e6dfbc --- /dev/null +++ b/abc_with_bb_support/src/misc/util/util_hack.h @@ -0,0 +1,95 @@ +/**CFile**************************************************************** + + FileName [util_hack.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [This file is used to simulate the presence of "util.h".] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: util_hack.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __UTIL_HACK_H__ +#define __UTIL_HACK_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include + +#define EXTERN extern +#define NIL(type) ((type *) 0) +#define random rand +#define srandom srand + +#define util_cpu_time Extra_CpuTime +#define getSoftDataLimit Extra_GetSoftDataLimit +#define util_getopt_reset Extra_UtilGetoptReset +#define util_getopt Extra_UtilGetopt +#define util_print_time Extra_UtilPrintTime +#define util_strsav Extra_UtilStrsav +#define util_tilde_expand Extra_UtilTildeExpand +#define util_file_search Extra_UtilFileSearch +#define MMoutOfMemory Extra_UtilMMoutOfMemory + +#define util_optarg globalUtilOptarg +#define util_optind globalUtilOptind + +#ifndef ARGS +# ifdef __STDC__ +# define ARGS(args) args +# else +# define ARGS(args) () +# endif +#endif + +#ifndef ABS +# define ABS(a) ((a) < 0 ? -(a) : (a)) +#endif + +#ifndef MAX +# define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef MIN +# define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#define ALLOC(type, num) ((type *) malloc(sizeof(type) * (num))) +#define FREE(obj) ((obj) ? (free((char *) (obj)), (obj) = 0) : 0) +#define REALLOC(type, obj, num) \ + ((obj) ? ((type *) realloc((char *)(obj), sizeof(type) * (num))) : \ + ((type *) malloc(sizeof(type) * (num)))) + +extern long Extra_CpuTime(); +extern int Extra_GetSoftDataLimit(); +extern void Extra_UtilGetoptReset(); +extern int Extra_UtilGetopt( int argc, char *argv[], char *optstring ); +extern char * Extra_UtilPrintTime( long t ); +extern char * Extra_UtilStrsav( char *s ); +extern char * Extra_UtilTildeExpand( char *fname ); +extern char * Extra_UtilFileSearch( char *file, char *path, char *mode ); + +extern char * globalUtilOptarg; +extern int globalUtilOptind; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/abc_with_bb_support/src/misc/vec/0005-Removed-an-unnecessary-dereference.patch b/abc_with_bb_support/src/misc/vec/0005-Removed-an-unnecessary-dereference.patch new file mode 100644 index 000000000..8cbd6e264 --- /dev/null +++ b/abc_with_bb_support/src/misc/vec/0005-Removed-an-unnecessary-dereference.patch @@ -0,0 +1,34 @@ +From d5cd41ce889aa61de6873a0aa44343e36739ede3 Mon Sep 17 00:00:00 2001 +From: Rafi Rubin +Date: Fri, 25 Mar 2011 01:07:43 -0400 +Subject: [PATCH 5/8] Removed an unnecessary dereference + +In this case the dereference was harmless, but it did make these lines +harder to read and throws a bunch of warnings which I wanted to get rid +of. +--- + abc_with_bb_support/src/misc/vec/vecInt.h | 6 +++--- + 1 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/abc_with_bb_support/src/misc/vec/vecInt.h b/abc_with_bb_support/src/misc/vec/vecInt.h +index ad9b7d3..3ff764a 100755 +--- a/abc_with_bb_support/src/misc/vec/vecInt.h ++++ b/abc_with_bb_support/src/misc/vec/vecInt.h +@@ -796,11 +796,11 @@ static inline int Vec_IntTwoCountCommon( Vec_Int_t * vArr1, Vec_Int_t * vArr2 ) + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) +- *pBeg1++, pBeg2++, Counter++; ++ pBeg1++, pBeg2++, Counter++; + else if ( *pBeg1 < *pBeg2 ) +- *pBeg1++; ++ pBeg1++; + else +- *pBeg2++; ++ pBeg2++; + } + return Counter; + } +-- +1.7.4.1 + diff --git a/abc_with_bb_support/src/misc/vec/module.make b/abc_with_bb_support/src/misc/vec/module.make new file mode 100644 index 000000000..d6d908e73 --- /dev/null +++ b/abc_with_bb_support/src/misc/vec/module.make @@ -0,0 +1 @@ +SRC += diff --git a/abc_with_bb_support/src/misc/vec/vec.h b/abc_with_bb_support/src/misc/vec/vec.h new file mode 100644 index 000000000..f92062641 --- /dev/null +++ b/abc_with_bb_support/src/misc/vec/vec.h @@ -0,0 +1,68 @@ +/**CFile**************************************************************** + + FileName [vec.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resizable arrays.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: vec.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __VEC_H__ +#define __VEC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#ifdef _WIN32 +#define inline __inline // compatible with MS VS 6.0 +#endif + +#include "vecInt.h" +#include "vecFlt.h" +#include "vecStr.h" +#include "vecPtr.h" +#include "vecVec.h" +#include "vecAtt.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/misc/vec/vecAtt.h b/abc_with_bb_support/src/misc/vec/vecAtt.h new file mode 100644 index 000000000..bef4f9f13 --- /dev/null +++ b/abc_with_bb_support/src/misc/vec/vecAtt.h @@ -0,0 +1,391 @@ +/**CFile**************************************************************** + + FileName [vecAtt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resizable arrays.] + + Synopsis [Array of user-specified attiributes.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: vecAtt.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __Vec_Att_H__ +#define __Vec_Att_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +// various attributes +typedef enum { + VEC_ATTR_NONE = 0, // 0 + VEC_ATTR_COPY, // 1 + VEC_ATTR_LOCAL_AIG, // 2 + VEC_ATTR_LOCAL_SOP, // 3 + VEC_ATTR_LOCAL_BDD, // 4 + VEC_ATTR_GLOBAL_AIG, // 5 + VEC_ATTR_GLOBAL_SOP, // 6 + VEC_ATTR_GLOBAL_BDD, // 7 + VEC_ATTR_LEVEL, // 8 + VEC_ATTR_LEVEL_REV, // 9 + VEC_ATTR_RETIME_LAG, // 10 + VEC_ATTR_FRAIG, // 11 + VEC_ATTR_MVVAR, // 12 + VEC_ATTR_DATA1, // 13 + VEC_ATTR_DATA2, // 14 + VEC_ATTR_TOTAL_NUM // 15 +} Vec_AttrType_t; + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Vec_Att_t_ Vec_Att_t; +struct Vec_Att_t_ +{ + // storage for attributes + int nCap; // the size of array allocated + int * pArrayInt; // the integer attribute array + void ** pArrayPtr; // the pointer attribute array + // attribute specific info + void * pMan; // the manager for this attribute + void (*pFuncFreeMan) (void *); // the procedure to free the manager + void*(*pFuncStartObj)(void *); // the procedure to start one attribute + void (*pFuncFreeObj) (void *, void *); // the procedure to free one attribute +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Att_t * Vec_AttAlloc( + int fInteger, int nSize, void * pMan, + void (*pFuncFreeMan) (void *), + void*(*pFuncStartObj)(void *), + void (*pFuncFreeObj) (void *, void *) ) +{ + Vec_Att_t * p; + p = ALLOC( Vec_Att_t, 1 ); + memset( p, 0, sizeof(Vec_Att_t) ); + p->pMan = pMan; + p->pFuncFreeMan = pFuncFreeMan; + p->pFuncStartObj = pFuncStartObj; + p->pFuncFreeObj = pFuncFreeObj; + p->nCap = nSize? nSize : 16; + if ( fInteger ) + { + p->pArrayInt = ALLOC( int, p->nCap ); + memset( p->pArrayInt, 0xff, sizeof(int) * p->nCap ); + } + else + { + p->pArrayPtr = ALLOC( void *, p->nCap ); + memset( p->pArrayPtr, 0, sizeof(void *) * p->nCap ); + } + return p; +} + +/**Function************************************************************* + + Synopsis [Frees the vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void * Vec_AttFree( Vec_Att_t * p, int fFreeMan ) +{ + void * pMan; + if ( p == NULL ) + return NULL; + // free the attributes of objects + if ( p->pFuncFreeObj ) + { + int i; + if ( p->pArrayInt ) + { + for ( i = 0; i < p->nCap; i++ ) + if ( p->pArrayInt[i] ) + p->pFuncFreeObj( p->pMan, (void *)p->pArrayInt[i] ); + } + else + { + for ( i = 0; i < p->nCap; i++ ) + if ( p->pArrayPtr[i] ) + p->pFuncFreeObj( p->pMan, p->pArrayPtr[i] ); + } + } + // free the memory manager + pMan = fFreeMan? NULL : p->pMan; + if ( p->pMan && fFreeMan ) + p->pFuncFreeMan( p->pMan ); + FREE( p->pArrayInt ); + FREE( p->pArrayPtr ); + FREE( p ); + return pMan; +} + +/**Function************************************************************* + + Synopsis [Clears the vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_AttClear( Vec_Att_t * p ) +{ + // free the attributes of objects + if ( p->pFuncFreeObj ) + { + int i; + if ( p->pArrayInt ) + { + if ( p->pFuncFreeObj ) + for ( i = 0; i < p->nCap; i++ ) + if ( p->pArrayInt[i] ) + p->pFuncFreeObj( p->pMan, (void *)p->pArrayInt[i] ); + } + else + { + if ( p->pFuncFreeObj ) + for ( i = 0; i < p->nCap; i++ ) + if ( p->pArrayPtr[i] ) + p->pFuncFreeObj( p->pMan, p->pArrayPtr[i] ); + } + } + if ( p->pArrayInt ) + memset( p->pArrayInt, 0xff, sizeof(int) * p->nCap ); + else + memset( p->pArrayPtr, 0, sizeof(void *) * p->nCap ); + +} + +/**Function************************************************************* + + Synopsis [Deletes one entry from the attribute manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_AttFreeEntry( Vec_Att_t * p, int i ) +{ + if ( i >= p->nCap ) + return; + if ( p->pMan ) + { + if ( p->pArrayInt[i] && p->pFuncFreeObj ) + p->pFuncFreeObj( p->pMan, (void *)p->pArrayInt[i] ); + if ( p->pArrayPtr[i] && p->pFuncFreeObj ) + p->pFuncFreeObj( p->pMan, (void *)p->pArrayPtr[i] ); + } + if ( p->pArrayInt ) + p->pArrayInt[i] = ~(unsigned)0; + else + p->pArrayPtr[i] = NULL; +} + +/**Function************************************************************* + + Synopsis [Resizes the vector to the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_AttGrow( Vec_Att_t * p, int nCapMin ) +{ + if ( p->nCap >= nCapMin ) + return; + if ( p->pArrayInt ) + { + p->pArrayInt = REALLOC( int, p->pArrayInt, nCapMin ); + memset( p->pArrayInt + p->nCap, 0xff, sizeof(int) * (nCapMin - p->nCap) ); + } + else + { + p->pArrayPtr = REALLOC( void *, p->pArrayPtr, nCapMin ); + memset( p->pArrayPtr + p->nCap, 0, sizeof(void *) * (nCapMin - p->nCap) ); + } + p->nCap = nCapMin; +} + +/**Function************************************************************* + + Synopsis [Writes the entry into its place.] + + Description [Only works if the manager is not defined.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_AttWriteEntry( Vec_Att_t * p, int i, void * pEntry ) +{ + assert( p->pArrayPtr ); + assert( p->pFuncStartObj == NULL ); + if ( i >= p->nCap ) + Vec_AttGrow( p, (2 * p->nCap > i)? 2 * p->nCap : i + 10 ); + p->pArrayPtr[i] = pEntry; +} + +/**Function************************************************************* + + Synopsis [Writes the entry into its place.] + + Description [Only works if the manager is not defined.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_AttWriteEntryInt( Vec_Att_t * p, int i, int Entry ) +{ + assert( p->pArrayInt ); + assert( p->pFuncStartObj == NULL ); + if ( i >= p->nCap ) + Vec_AttGrow( p, (2 * p->nCap > i)? 2 * p->nCap : i + 10 ); + p->pArrayInt[i] = Entry; +} + +/**Function************************************************************* + + Synopsis [Returns the entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void * Vec_AttEntry( Vec_Att_t * p, int i ) +{ + assert( p->pArrayPtr ); + if ( i >= p->nCap ) + Vec_AttGrow( p, (2 * p->nCap > i)? 2 * p->nCap : i + 10 ); + if ( p->pArrayPtr[i] == NULL && p->pFuncStartObj ) + p->pArrayPtr[i] = p->pFuncStartObj( p->pMan ); + return p->pArrayPtr[i]; +} + +/**Function************************************************************* + + Synopsis [Returns the entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_AttEntryInt( Vec_Att_t * p, int i ) +{ + assert( p->pArrayInt ); + assert( p->pMan == NULL ); + if ( i >= p->nCap ) + Vec_AttGrow( p, (2 * p->nCap > i)? 2 * p->nCap : i + 10 ); + return p->pArrayInt[i]; +} + +/**Function************************************************************* + + Synopsis [Returns the entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void * Vec_AttMan( Vec_Att_t * p ) +{ + return p->pMan; +} + +/**Function************************************************************* + + Synopsis [Returns the array of attributes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void ** Vec_AttArray( Vec_Att_t * p ) +{ + return p->pArrayPtr; +} + +/**Function************************************************************* + + Synopsis [Returns the array of attributes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int * Vec_AttArrayInt( Vec_Att_t * p ) +{ + return p->pArrayInt; +} + +#endif + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/misc/vec/vecFlt.h b/abc_with_bb_support/src/misc/vec/vecFlt.h new file mode 100644 index 000000000..9b8905a09 --- /dev/null +++ b/abc_with_bb_support/src/misc/vec/vecFlt.h @@ -0,0 +1,665 @@ +/**CFile**************************************************************** + + FileName [vecFlt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resizable arrays.] + + Synopsis [Resizable arrays of floats.] + + Author [Aaron P. Hurst] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: vecInt.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __VEC_FLT_H__ +#define __VEC_FLT_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Vec_Flt_t_ Vec_Flt_t; +struct Vec_Flt_t_ +{ + int nCap; + int nSize; + float * pArray; +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define Vec_FltForEachEntry( vVec, Entry, i ) \ + for ( i = 0; (i < Vec_FltSize(vVec)) && (((Entry) = Vec_FltEntry(vVec, i)), 1); i++ ) +#define Vec_FltForEachEntryStart( vVec, Entry, i, Start ) \ + for ( i = Start; (i < Vec_FltSize(vVec)) && (((Entry) = Vec_FltEntry(vVec, i)), 1); i++ ) +#define Vec_FltForEachEntryStartStop( vVec, Entry, i, Start, Stop ) \ + for ( i = Start; (i < Stop) && (((Entry) = Vec_FltEntry(vVec, i)), 1); i++ ) +#define Vec_FltForEachEntryReverse( vVec, pEntry, i ) \ + for ( i = Vec_FltSize(vVec) - 1; (i >= 0) && (((pEntry) = Vec_FltEntry(vVec, i)), 1); i-- ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Flt_t * Vec_FltAlloc( int nCap ) +{ + Vec_Flt_t * p; + p = ALLOC( Vec_Flt_t, 1 ); + if ( nCap > 0 && nCap < 16 ) + nCap = 16; + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ALLOC( float, p->nCap ) : NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given size and cleans it.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Flt_t * Vec_FltStart( int nSize ) +{ + Vec_Flt_t * p; + p = Vec_FltAlloc( nSize ); + p->nSize = nSize; + memset( p->pArray, 0, sizeof(float) * nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Creates the vector from a float array of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Flt_t * Vec_FltAllocArray( float * pArray, int nSize ) +{ + Vec_Flt_t * p; + p = ALLOC( Vec_Flt_t, 1 ); + p->nSize = nSize; + p->nCap = nSize; + p->pArray = pArray; + return p; +} + +/**Function************************************************************* + + Synopsis [Creates the vector from a float array of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Flt_t * Vec_FltAllocArrayCopy( float * pArray, int nSize ) +{ + Vec_Flt_t * p; + p = ALLOC( Vec_Flt_t, 1 ); + p->nSize = nSize; + p->nCap = nSize; + p->pArray = ALLOC( float, nSize ); + memcpy( p->pArray, pArray, sizeof(float) * nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Duplicates the float array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Flt_t * Vec_FltDup( Vec_Flt_t * pVec ) +{ + Vec_Flt_t * p; + p = ALLOC( Vec_Flt_t, 1 ); + p->nSize = pVec->nSize; + p->nCap = pVec->nCap; + p->pArray = p->nCap? ALLOC( float, p->nCap ) : NULL; + memcpy( p->pArray, pVec->pArray, sizeof(float) * pVec->nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Transfers the array into another vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Flt_t * Vec_FltDupArray( Vec_Flt_t * pVec ) +{ + Vec_Flt_t * p; + p = ALLOC( Vec_Flt_t, 1 ); + p->nSize = pVec->nSize; + p->nCap = pVec->nCap; + p->pArray = pVec->pArray; + pVec->nSize = 0; + pVec->nCap = 0; + pVec->pArray = NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_FltFree( Vec_Flt_t * p ) +{ + FREE( p->pArray ); + FREE( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline float * Vec_FltReleaseArray( Vec_Flt_t * p ) +{ + float * pArray = p->pArray; + p->nCap = 0; + p->nSize = 0; + p->pArray = NULL; + return pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline float * Vec_FltArray( Vec_Flt_t * p ) +{ + return p->pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_FltSize( Vec_Flt_t * p ) +{ + return p->nSize; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline float Vec_FltEntry( Vec_Flt_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray[i]; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_FltWriteEntry( Vec_Flt_t * p, int i, float Entry ) +{ + assert( i >= 0 && i < p->nSize ); + p->pArray[i] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_FltAddToEntry( Vec_Flt_t * p, int i, float Addition ) +{ + assert( i >= 0 && i < p->nSize ); + p->pArray[i] += Addition; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline float Vec_FltEntryLast( Vec_Flt_t * p ) +{ + return p->pArray[p->nSize-1]; +} + +/**Function************************************************************* + + Synopsis [Resizes the vector to the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_FltGrow( Vec_Flt_t * p, int nCapMin ) +{ + if ( p->nCap >= nCapMin ) + return; + p->pArray = REALLOC( float, p->pArray, nCapMin ); + p->nCap = nCapMin; +} + +/**Function************************************************************* + + Synopsis [Fills the vector with given number of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_FltFill( Vec_Flt_t * p, int nSize, float Entry ) +{ + int i; + Vec_FltGrow( p, nSize ); + for ( i = 0; i < nSize; i++ ) + p->pArray[i] = Entry; + p->nSize = nSize; +} + +/**Function************************************************************* + + Synopsis [Fills the vector with given number of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_FltFillExtra( Vec_Flt_t * p, int nSize, float Entry ) +{ + int i; + if ( p->nSize >= nSize ) + return; + Vec_FltGrow( p, nSize ); + for ( i = p->nSize; i < nSize; i++ ) + p->pArray[i] = Entry; + p->nSize = nSize; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_FltShrink( Vec_Flt_t * p, int nSizeNew ) +{ + assert( p->nSize >= nSizeNew ); + p->nSize = nSizeNew; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_FltClear( Vec_Flt_t * p ) +{ + p->nSize = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_FltPush( Vec_Flt_t * p, float Entry ) +{ + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_FltGrow( p, 16 ); + else + Vec_FltGrow( p, 2 * p->nCap ); + } + p->pArray[p->nSize++] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_FltPushMem( Extra_MmStep_t * pMemMan, Vec_Flt_t * p, float Entry ) +{ + if ( p->nSize == p->nCap ) + { + float * pArray; + int i; + + if ( p->nSize == 0 ) + p->nCap = 1; + pArray = (float *)Extra_MmStepEntryFetch( pMemMan, p->nCap * 8 ); +// pArray = ALLOC( float, p->nCap * 2 ); + if ( p->pArray ) + { + for ( i = 0; i < p->nSize; i++ ) + pArray[i] = p->pArray[i]; + Extra_MmStepEntryRecycle( pMemMan, (char *)p->pArray, p->nCap * 4 ); +// free( p->pArray ); + } + p->nCap *= 2; + p->pArray = pArray; + } + p->pArray[p->nSize++] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_FltPushOrder( Vec_Flt_t * p, float Entry ) +{ + int i; + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_FltGrow( p, 16 ); + else + Vec_FltGrow( p, 2 * p->nCap ); + } + p->nSize++; + for ( i = p->nSize-2; i >= 0; i-- ) + if ( p->pArray[i] > Entry ) + p->pArray[i+1] = p->pArray[i]; + else + break; + p->pArray[i+1] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_FltPushUnique( Vec_Flt_t * p, float Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return 1; + Vec_FltPush( p, Entry ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Returns the last entry and removes it from the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline float Vec_FltPop( Vec_Flt_t * p ) +{ + assert( p->nSize > 0 ); + return p->pArray[--p->nSize]; +} + +/**Function************************************************************* + + Synopsis [Find entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_FltFind( Vec_Flt_t * p, float Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return i; + return -1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_FltRemove( Vec_Flt_t * p, float Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + break; + if ( i == p->nSize ) + return 0; + assert( i < p->nSize ); + for ( i++; i < p->nSize; i++ ) + p->pArray[i-1] = p->pArray[i]; + p->nSize--; + return 1; +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two floats.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_FltSortCompare1( float * pp1, float * pp2 ) +{ + // for some reason commenting out lines (as shown) led to crashing of the release version + if ( *pp1 < *pp2 ) + return -1; + if ( *pp1 > *pp2 ) // + return 1; + return 0; // +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two floats.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_FltSortCompare2( float * pp1, float * pp2 ) +{ + // for some reason commenting out lines (as shown) led to crashing of the release version + if ( *pp1 > *pp2 ) + return -1; + if ( *pp1 < *pp2 ) // + return 1; + return 0; // +} + +/**Function************************************************************* + + Synopsis [Sorting the entries by their value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_FltSort( Vec_Flt_t * p, int fReverse ) +{ + if ( fReverse ) + qsort( (void *)p->pArray, p->nSize, sizeof(float), + (int (*)(const void *, const void *)) Vec_FltSortCompare2 ); + else + qsort( (void *)p->pArray, p->nSize, sizeof(float), + (int (*)(const void *, const void *)) Vec_FltSortCompare1 ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + +#endif + diff --git a/abc_with_bb_support/src/misc/vec/vecInt.h b/abc_with_bb_support/src/misc/vec/vecInt.h new file mode 100644 index 000000000..3ff764a1e --- /dev/null +++ b/abc_with_bb_support/src/misc/vec/vecInt.h @@ -0,0 +1,852 @@ +/**CFile**************************************************************** + + FileName [vecInt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resizable arrays.] + + Synopsis [Resizable arrays of integers.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: vecInt.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __VEC_INT_H__ +#define __VEC_INT_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include +#include "extra.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Vec_Int_t_ Vec_Int_t; +struct Vec_Int_t_ +{ + int nCap; + int nSize; + int * pArray; +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define Vec_IntForEachEntry( vVec, Entry, i ) \ + for ( i = 0; (i < Vec_IntSize(vVec)) && (((Entry) = Vec_IntEntry(vVec, i)), 1); i++ ) +#define Vec_IntForEachEntryStart( vVec, Entry, i, Start ) \ + for ( i = Start; (i < Vec_IntSize(vVec)) && (((Entry) = Vec_IntEntry(vVec, i)), 1); i++ ) +#define Vec_IntForEachEntryStartStop( vVec, Entry, i, Start, Stop ) \ + for ( i = Start; (i < Stop) && (((Entry) = Vec_IntEntry(vVec, i)), 1); i++ ) +#define Vec_IntForEachEntryReverse( vVec, pEntry, i ) \ + for ( i = Vec_IntSize(vVec) - 1; (i >= 0) && (((pEntry) = Vec_IntEntry(vVec, i)), 1); i-- ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntAlloc( int nCap ) +{ + Vec_Int_t * p; + p = ALLOC( Vec_Int_t, 1 ); + if ( nCap > 0 && nCap < 16 ) + nCap = 16; + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ALLOC( int, p->nCap ) : NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given size and cleans it.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntStart( int nSize ) +{ + Vec_Int_t * p; + p = Vec_IntAlloc( nSize ); + p->nSize = nSize; + memset( p->pArray, 0, sizeof(int) * nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Creates the vector from an integer array of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntAllocArray( int * pArray, int nSize ) +{ + Vec_Int_t * p; + p = ALLOC( Vec_Int_t, 1 ); + p->nSize = nSize; + p->nCap = nSize; + p->pArray = pArray; + return p; +} + +/**Function************************************************************* + + Synopsis [Creates the vector from an integer array of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntAllocArrayCopy( int * pArray, int nSize ) +{ + Vec_Int_t * p; + p = ALLOC( Vec_Int_t, 1 ); + p->nSize = nSize; + p->nCap = nSize; + p->pArray = ALLOC( int, nSize ); + memcpy( p->pArray, pArray, sizeof(int) * nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Duplicates the integer array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntDup( Vec_Int_t * pVec ) +{ + Vec_Int_t * p; + p = ALLOC( Vec_Int_t, 1 ); + p->nSize = pVec->nSize; + p->nCap = pVec->nSize; + p->pArray = p->nCap? ALLOC( int, p->nCap ) : NULL; + memcpy( p->pArray, pVec->pArray, sizeof(int) * pVec->nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Transfers the array into another vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntDupArray( Vec_Int_t * pVec ) +{ + Vec_Int_t * p; + p = ALLOC( Vec_Int_t, 1 ); + p->nSize = pVec->nSize; + p->nCap = pVec->nCap; + p->pArray = pVec->pArray; + pVec->nSize = 0; + pVec->nCap = 0; + pVec->pArray = NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntFree( Vec_Int_t * p ) +{ + FREE( p->pArray ); + FREE( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int * Vec_IntReleaseArray( Vec_Int_t * p ) +{ + int * pArray = p->pArray; + p->nCap = 0; + p->nSize = 0; + p->pArray = NULL; + return pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int * Vec_IntArray( Vec_Int_t * p ) +{ + return p->pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntSize( Vec_Int_t * p ) +{ + return p->nSize; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntEntry( Vec_Int_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray[i]; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntWriteEntry( Vec_Int_t * p, int i, int Entry ) +{ + assert( i >= 0 && i < p->nSize ); + p->pArray[i] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntAddToEntry( Vec_Int_t * p, int i, int Addition ) +{ + assert( i >= 0 && i < p->nSize ); + p->pArray[i] += Addition; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntEntryLast( Vec_Int_t * p ) +{ + assert( p->nSize > 0 ); + return p->pArray[p->nSize-1]; +} + +/**Function************************************************************* + + Synopsis [Resizes the vector to the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntGrow( Vec_Int_t * p, int nCapMin ) +{ + if ( p->nCap >= nCapMin ) + return; + p->pArray = REALLOC( int, p->pArray, nCapMin ); + assert( p->pArray ); + p->nCap = nCapMin; +} + +/**Function************************************************************* + + Synopsis [Fills the vector with given number of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntFill( Vec_Int_t * p, int nSize, int Entry ) +{ + int i; + Vec_IntGrow( p, nSize ); + for ( i = 0; i < nSize; i++ ) + p->pArray[i] = Entry; + p->nSize = nSize; +} + +/**Function************************************************************* + + Synopsis [Fills the vector with given number of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntFillExtra( Vec_Int_t * p, int nSize, int Entry ) +{ + int i; + if ( p->nSize >= nSize ) + return; + Vec_IntGrow( p, nSize ); + for ( i = p->nSize; i < nSize; i++ ) + p->pArray[i] = Entry; + p->nSize = nSize; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntShrink( Vec_Int_t * p, int nSizeNew ) +{ + assert( p->nSize >= nSizeNew ); + p->nSize = nSizeNew; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntClear( Vec_Int_t * p ) +{ + p->nSize = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntPush( Vec_Int_t * p, int Entry ) +{ + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_IntGrow( p, 16 ); + else + Vec_IntGrow( p, 2 * p->nCap ); + } + p->pArray[p->nSize++] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntPushFirst( Vec_Int_t * p, int Entry ) +{ + int i; + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_IntGrow( p, 16 ); + else + Vec_IntGrow( p, 2 * p->nCap ); + } + p->nSize++; + for ( i = p->nSize - 1; i >= 1; i-- ) + p->pArray[i] = p->pArray[i-1]; + p->pArray[0] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntPushMem( Extra_MmStep_t * pMemMan, Vec_Int_t * p, int Entry ) +{ + if ( p->nSize == p->nCap ) + { + int * pArray; + int i; + + if ( p->nSize == 0 ) + p->nCap = 1; + if ( pMemMan ) + pArray = (int *)Extra_MmStepEntryFetch( pMemMan, p->nCap * 8 ); + else + pArray = ALLOC( int, p->nCap * 2 ); + if ( p->pArray ) + { + for ( i = 0; i < p->nSize; i++ ) + pArray[i] = p->pArray[i]; + if ( pMemMan ) + Extra_MmStepEntryRecycle( pMemMan, (char *)p->pArray, p->nCap * 4 ); + else + free( p->pArray ); + } + p->nCap *= 2; + p->pArray = pArray; + } + p->pArray[p->nSize++] = Entry; +} + +/**Function************************************************************* + + Synopsis [Inserts the entry while preserving the increasing order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntPushOrder( Vec_Int_t * p, int Entry ) +{ + int i; + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_IntGrow( p, 16 ); + else + Vec_IntGrow( p, 2 * p->nCap ); + } + p->nSize++; + for ( i = p->nSize-2; i >= 0; i-- ) + if ( p->pArray[i] > Entry ) + p->pArray[i+1] = p->pArray[i]; + else + break; + p->pArray[i+1] = Entry; +} + +/**Function************************************************************* + + Synopsis [Inserts the entry while preserving the increasing order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntPushUniqueOrder( Vec_Int_t * p, int Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return 1; + Vec_IntPushOrder( p, Entry ); + return 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntPushUnique( Vec_Int_t * p, int Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return 1; + Vec_IntPush( p, Entry ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Returns the pointer to the next nWords entries in the vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned * Vec_IntFetch( Vec_Int_t * p, int nWords ) +{ + if ( nWords == 0 ) + return NULL; + assert( nWords > 0 ); + p->nSize += nWords; + if ( p->nSize > p->nCap ) + { +// Vec_IntGrow( p, 2 * p->nSize ); + return NULL; + } + return ((unsigned *)p->pArray) + p->nSize - nWords; +} + +/**Function************************************************************* + + Synopsis [Returns the last entry and removes it from the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntPop( Vec_Int_t * p ) +{ + assert( p->nSize > 0 ); + return p->pArray[--p->nSize]; +} + +/**Function************************************************************* + + Synopsis [Find entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntFind( Vec_Int_t * p, int Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return i; + return -1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntRemove( Vec_Int_t * p, int Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + break; + if ( i == p->nSize ) + return 0; + assert( i < p->nSize ); + for ( i++; i < p->nSize; i++ ) + p->pArray[i-1] = p->pArray[i]; + p->nSize--; + return 1; +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two integers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntSortCompare1( int * pp1, int * pp2 ) +{ + // for some reason commenting out lines (as shown) led to crashing of the release version + if ( *pp1 < *pp2 ) + return -1; + if ( *pp1 > *pp2 ) // + return 1; + return 0; // +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two integers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntSortCompare2( int * pp1, int * pp2 ) +{ + // for some reason commenting out lines (as shown) led to crashing of the release version + if ( *pp1 > *pp2 ) + return -1; + if ( *pp1 < *pp2 ) // + return 1; + return 0; // +} + +/**Function************************************************************* + + Synopsis [Sorting the entries by their integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntSort( Vec_Int_t * p, int fReverse ) +{ + if ( fReverse ) + qsort( (void *)p->pArray, p->nSize, sizeof(int), + (int (*)(const void *, const void *)) Vec_IntSortCompare2 ); + else + qsort( (void *)p->pArray, p->nSize, sizeof(int), + (int (*)(const void *, const void *)) Vec_IntSortCompare1 ); +} + + +/**Function************************************************************* + + Synopsis [Comparison procedure for two integers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntSortCompareUnsigned( unsigned * pp1, unsigned * pp2 ) +{ + if ( *pp1 < *pp2 ) + return -1; + if ( *pp1 > *pp2 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Sorting the entries by their integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntSortUnsigned( Vec_Int_t * p ) +{ + qsort( (void *)p->pArray, p->nSize, sizeof(int), + (int (*)(const void *, const void *)) Vec_IntSortCompareUnsigned ); +} + +/**Function************************************************************* + + Synopsis [Returns the number of common entries.] + + Description [Assumes that the vectors are sorted in the increasing order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntTwoCountCommon( Vec_Int_t * vArr1, Vec_Int_t * vArr2 ) +{ + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + int Counter = 0; + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + pBeg1++, pBeg2++, Counter++; + else if ( *pBeg1 < *pBeg2 ) + pBeg1++; + else + pBeg2++; + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Returns the result of merging the two vectors.] + + Description [Assumes that the vectors are sorted in the increasing order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntTwoMerge( Vec_Int_t * vArr1, Vec_Int_t * vArr2 ) +{ + Vec_Int_t * vArr = Vec_IntAlloc( vArr1->nSize + vArr2->nSize ); + int * pBeg = vArr->pArray; + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + *pBeg++ = *pBeg1++, pBeg2++; + else if ( *pBeg1 < *pBeg2 ) + *pBeg++ = *pBeg1++; + else + *pBeg++ = *pBeg2++; + } + while ( pBeg1 < pEnd1 ) + *pBeg++ = *pBeg1++; + while ( pBeg2 < pEnd2 ) + *pBeg++ = *pBeg2++; + vArr->nSize = pBeg - vArr->pArray; + assert( vArr->nSize <= vArr->nCap ); + assert( vArr->nSize >= vArr1->nSize ); + assert( vArr->nSize >= vArr2->nSize ); + return vArr; +} + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/misc/vec/vecPtr.h b/abc_with_bb_support/src/misc/vec/vecPtr.h new file mode 100644 index 000000000..56d11eca5 --- /dev/null +++ b/abc_with_bb_support/src/misc/vec/vecPtr.h @@ -0,0 +1,762 @@ +/**CFile**************************************************************** + + FileName [vecPtr.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resizable arrays.] + + Synopsis [Resizable arrays of generic pointers.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: vecPtr.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __VEC_PTR_H__ +#define __VEC_PTR_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Vec_Ptr_t_ Vec_Ptr_t; +struct Vec_Ptr_t_ +{ + int nCap; + int nSize; + void ** pArray; +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// iterators through entries +#define Vec_PtrForEachEntry( vVec, pEntry, i ) \ + for ( i = 0; (i < Vec_PtrSize(vVec)) && (((pEntry) = Vec_PtrEntry(vVec, i)), 1); i++ ) +#define Vec_PtrForEachEntryStart( vVec, pEntry, i, Start ) \ + for ( i = Start; (i < Vec_PtrSize(vVec)) && (((pEntry) = Vec_PtrEntry(vVec, i)), 1); i++ ) +#define Vec_PtrForEachEntryStop( vVec, pEntry, i, Stop ) \ + for ( i = 0; (i < Stop) && (((pEntry) = Vec_PtrEntry(vVec, i)), 1); i++ ) +#define Vec_PtrForEachEntryStartStop( vVec, pEntry, i, Start, Stop ) \ + for ( i = Start; (i < Stop) && (((pEntry) = Vec_PtrEntry(vVec, i)), 1); i++ ) +#define Vec_PtrForEachEntryReverse( vVec, pEntry, i ) \ + for ( i = Vec_PtrSize(vVec) - 1; (i >= 0) && (((pEntry) = Vec_PtrEntry(vVec, i)), 1); i-- ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Ptr_t * Vec_PtrAlloc( int nCap ) +{ + Vec_Ptr_t * p; + p = ALLOC( Vec_Ptr_t, 1 ); + if ( nCap > 0 && nCap < 8 ) + nCap = 8; + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ALLOC( void *, p->nCap ) : NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given size and cleans it.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Ptr_t * Vec_PtrStart( int nSize ) +{ + Vec_Ptr_t * p; + p = Vec_PtrAlloc( nSize ); + p->nSize = nSize; + memset( p->pArray, 0, sizeof(void *) * nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Creates the vector from an integer array of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Ptr_t * Vec_PtrAllocArray( void ** pArray, int nSize ) +{ + Vec_Ptr_t * p; + p = ALLOC( Vec_Ptr_t, 1 ); + p->nSize = nSize; + p->nCap = nSize; + p->pArray = pArray; + return p; +} + +/**Function************************************************************* + + Synopsis [Creates the vector from an integer array of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Ptr_t * Vec_PtrAllocArrayCopy( void ** pArray, int nSize ) +{ + Vec_Ptr_t * p; + p = ALLOC( Vec_Ptr_t, 1 ); + p->nSize = nSize; + p->nCap = nSize; + p->pArray = ALLOC( void *, nSize ); + memcpy( p->pArray, pArray, sizeof(void *) * nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Allocates the array of simulation info.] + + Description [Allocates the array containing given number of entries, + each of which contains given number of unsigned words of simulation data. + The resulting array can be freed using regular procedure Vec_PtrFree(). + It is the responsibility of the user to ensure this array is never grown.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Ptr_t * Vec_PtrAllocSimInfo( int nEntries, int nWords ) +{ + void ** pMemory; + unsigned * pInfo; + int i; + pMemory = (void **)ALLOC( char, (sizeof(void *) + sizeof(unsigned) * nWords) * nEntries ); + pInfo = (unsigned *)(pMemory + nEntries); + for ( i = 0; i < nEntries; i++ ) + pMemory[i] = pInfo + i * nWords; + return Vec_PtrAllocArray( pMemory, nEntries ); +} + +/**Function************************************************************* + + Synopsis [Allocates the array of truth tables for the given number of vars.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Ptr_t * Vec_PtrAllocTruthTables( int nVars ) +{ + Vec_Ptr_t * p; + unsigned Masks[5] = { 0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, 0xFF00FF00, 0xFFFF0000 }; + unsigned * pTruth; + int i, k, nWords; + nWords = (nVars <= 5 ? 1 : (1 << (nVars - 5))); + p = Vec_PtrAllocSimInfo( nVars, nWords ); + for ( i = 0; i < nVars; i++ ) + { + pTruth = (unsigned *)p->pArray[i]; + if ( i < 5 ) + { + for ( k = 0; k < nWords; k++ ) + pTruth[k] = Masks[i]; + } + else + { + for ( k = 0; k < nWords; k++ ) + if ( k & (1 << (i-5)) ) + pTruth[k] = ~(unsigned)0; + else + pTruth[k] = 0; + } + } + return p; +} + +/**Function************************************************************* + + Synopsis [Duplicates the integer array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Ptr_t * Vec_PtrDup( Vec_Ptr_t * pVec ) +{ + Vec_Ptr_t * p; + p = ALLOC( Vec_Ptr_t, 1 ); + p->nSize = pVec->nSize; + p->nCap = pVec->nCap; + p->pArray = p->nCap? ALLOC( void *, p->nCap ) : NULL; + memcpy( p->pArray, pVec->pArray, sizeof(void *) * pVec->nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Transfers the array into another vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Ptr_t * Vec_PtrDupArray( Vec_Ptr_t * pVec ) +{ + Vec_Ptr_t * p; + p = ALLOC( Vec_Ptr_t, 1 ); + p->nSize = pVec->nSize; + p->nCap = pVec->nCap; + p->pArray = pVec->pArray; + pVec->nSize = 0; + pVec->nCap = 0; + pVec->pArray = NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [Frees the vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrFree( Vec_Ptr_t * p ) +{ + FREE( p->pArray ); + FREE( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void ** Vec_PtrReleaseArray( Vec_Ptr_t * p ) +{ + void ** pArray = p->pArray; + p->nCap = 0; + p->nSize = 0; + p->pArray = NULL; + return pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void ** Vec_PtrArray( Vec_Ptr_t * p ) +{ + return p->pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_PtrSize( Vec_Ptr_t * p ) +{ + return p->nSize; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void * Vec_PtrEntry( Vec_Ptr_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray[i]; +} + +/**Function************************************************************* + + Synopsis [Resizes the array of simulation info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrDoubleSimInfo( Vec_Ptr_t * vInfo ) +{ + Vec_Ptr_t * vInfoNew; + int nWords; + assert( Vec_PtrSize(vInfo) > 2 ); + // get the new array + nWords = (unsigned *)Vec_PtrEntry(vInfo,1) - (unsigned *)Vec_PtrEntry(vInfo,0); + vInfoNew = Vec_PtrAllocSimInfo( 2*Vec_PtrSize(vInfo), nWords ); + // copy the simulation info + memcpy( Vec_PtrEntry(vInfoNew,0), Vec_PtrEntry(vInfo,0), Vec_PtrSize(vInfo) * nWords * 4 ); + // replace the array + free( vInfo->pArray ); + vInfo->pArray = vInfoNew->pArray; + vInfo->nSize *= 2; + vInfo->nCap *= 2; + // free the old array + vInfoNew->pArray = NULL; + free( vInfoNew ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void ** Vec_PtrEntryP( Vec_Ptr_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray + i; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrWriteEntry( Vec_Ptr_t * p, int i, void * Entry ) +{ + assert( i >= 0 && i < p->nSize ); + p->pArray[i] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void * Vec_PtrEntryLast( Vec_Ptr_t * p ) +{ + assert( p->nSize > 0 ); + return p->pArray[p->nSize-1]; +} + +/**Function************************************************************* + + Synopsis [Resizes the vector to the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrGrow( Vec_Ptr_t * p, int nCapMin ) +{ + if ( p->nCap >= nCapMin ) + return; + p->pArray = REALLOC( void *, p->pArray, nCapMin ); + p->nCap = nCapMin; +} + +/**Function************************************************************* + + Synopsis [Fills the vector with given number of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrFill( Vec_Ptr_t * p, int nSize, void * Entry ) +{ + int i; + Vec_PtrGrow( p, nSize ); + for ( i = 0; i < nSize; i++ ) + p->pArray[i] = Entry; + p->nSize = nSize; +} + +/**Function************************************************************* + + Synopsis [Fills the vector with given number of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrFillExtra( Vec_Ptr_t * p, int nSize, void * Entry ) +{ + int i; + if ( p->nSize >= nSize ) + return; + assert( p->nSize < nSize ); + if ( 2 * p->nSize > nSize ) + Vec_PtrGrow( p, 2 * nSize ); + else + Vec_PtrGrow( p, nSize ); + for ( i = p->nSize; i < nSize; i++ ) + p->pArray[i] = Entry; + p->nSize = nSize; +} + +/**Function************************************************************* + + Synopsis [Returns the entry even if the place not exist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void * Vec_PtrGetEntry( Vec_Ptr_t * p, int i ) +{ + Vec_PtrFillExtra( p, i + 1, NULL ); + return Vec_PtrEntry( p, i ); +} + +/**Function************************************************************* + + Synopsis [Inserts the entry even if the place does not exist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrSetEntry( Vec_Ptr_t * p, int i, void * Entry ) +{ + Vec_PtrFillExtra( p, i + 1, NULL ); + Vec_PtrWriteEntry( p, i, Entry ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrShrink( Vec_Ptr_t * p, int nSizeNew ) +{ + assert( p->nSize >= nSizeNew ); + p->nSize = nSizeNew; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrClear( Vec_Ptr_t * p ) +{ + p->nSize = 0; +} + +/**Function************************************************************* + + Synopsis [Copies the interger array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrCopy( Vec_Ptr_t * pDest, Vec_Ptr_t * pSour ) +{ + pDest->nSize = 0; + Vec_PtrGrow( pDest, pSour->nSize ); + memcpy( pDest->pArray, pSour->pArray, sizeof(void *) * pSour->nSize ); + pDest->nSize = pSour->nSize; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrPush( Vec_Ptr_t * p, void * Entry ) +{ + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_PtrGrow( p, 16 ); + else + Vec_PtrGrow( p, 2 * p->nCap ); + } + p->pArray[p->nSize++] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_PtrPushUnique( Vec_Ptr_t * p, void * Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return 1; + Vec_PtrPush( p, Entry ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Returns the last entry and removes it from the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void * Vec_PtrPop( Vec_Ptr_t * p ) +{ + assert( p->nSize > 0 ); + return p->pArray[--p->nSize]; +} + +/**Function************************************************************* + + Synopsis [Find entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_PtrFind( Vec_Ptr_t * p, void * Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return i; + return -1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrRemove( Vec_Ptr_t * p, void * Entry ) +{ + int i; + // delete assuming that it is closer to the end + for ( i = p->nSize - 1; i >= 0; i-- ) + if ( p->pArray[i] == Entry ) + break; + assert( i >= 0 ); +/* + // delete assuming that it is closer to the beginning + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + break; + assert( i < p->nSize ); +*/ + for ( i++; i < p->nSize; i++ ) + p->pArray[i-1] = p->pArray[i]; + p->nSize--; +} + +/**Function************************************************************* + + Synopsis [Moves the first nItems to the end.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrReorder( Vec_Ptr_t * p, int nItems ) +{ + assert( nItems < p->nSize ); + Vec_PtrGrow( p, nItems + p->nSize ); + memmove( (char **)p->pArray + p->nSize, p->pArray, nItems * sizeof(void*) ); + memmove( p->pArray, (char **)p->pArray + nItems, p->nSize * sizeof(void*) ); +} + +/**Function************************************************************* + + Synopsis [Sorting the entries by their integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrSort( Vec_Ptr_t * p, int (*Vec_PtrSortCompare)() ) +{ + if ( p->nSize < 2 ) + return; + qsort( (void *)p->pArray, p->nSize, sizeof(void *), + (int (*)(const void *, const void *)) Vec_PtrSortCompare ); +} + +/**Function************************************************************* + + Synopsis [Sorting the entries by their integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrUniqify( Vec_Ptr_t * p, int (*Vec_PtrSortCompare)() ) +{ + int i, k; + if ( p->nSize < 2 ) + return; + qsort( (void *)p->pArray, p->nSize, sizeof(void *), + (int (*)(const void *, const void *)) Vec_PtrSortCompare ); + for ( i = k = 1; i < p->nSize; i++ ) + if ( p->pArray[i] != p->pArray[i-1] ) + p->pArray[k++] = p->pArray[i]; + p->nSize = k; +} + +#endif + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/misc/vec/vecStr.h b/abc_with_bb_support/src/misc/vec/vecStr.h new file mode 100644 index 000000000..cdfff4c8b --- /dev/null +++ b/abc_with_bb_support/src/misc/vec/vecStr.h @@ -0,0 +1,562 @@ +/**CFile**************************************************************** + + FileName [vecStr.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resizable arrays.] + + Synopsis [Resizable arrays of characters.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: vecStr.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __VEC_STR_H__ +#define __VEC_STR_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Vec_Str_t_ Vec_Str_t; +struct Vec_Str_t_ +{ + int nCap; + int nSize; + char * pArray; +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define Vec_StrForEachEntry( vVec, Entry, i ) \ + for ( i = 0; (i < Vec_StrSize(vVec)) && (((Entry) = Vec_StrEntry(vVec, i)), 1); i++ ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Str_t * Vec_StrAlloc( int nCap ) +{ + Vec_Str_t * p; + p = ALLOC( Vec_Str_t, 1 ); + if ( nCap > 0 && nCap < 16 ) + nCap = 16; + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ALLOC( char, p->nCap ) : NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given size and cleans it.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Str_t * Vec_StrStart( int nSize ) +{ + Vec_Str_t * p; + p = Vec_StrAlloc( nSize ); + p->nSize = nSize; + memset( p->pArray, 0, sizeof(char) * nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Creates the vector from an integer array of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Str_t * Vec_StrAllocArray( char * pArray, int nSize ) +{ + Vec_Str_t * p; + p = ALLOC( Vec_Str_t, 1 ); + p->nSize = nSize; + p->nCap = nSize; + p->pArray = pArray; + return p; +} + +/**Function************************************************************* + + Synopsis [Creates the vector from an integer array of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Str_t * Vec_StrAllocArrayCopy( char * pArray, int nSize ) +{ + Vec_Str_t * p; + p = ALLOC( Vec_Str_t, 1 ); + p->nSize = nSize; + p->nCap = nSize; + p->pArray = ALLOC( char, nSize ); + memcpy( p->pArray, pArray, sizeof(char) * nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Duplicates the integer array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Str_t * Vec_StrDup( Vec_Str_t * pVec ) +{ + Vec_Str_t * p; + p = ALLOC( Vec_Str_t, 1 ); + p->nSize = pVec->nSize; + p->nCap = pVec->nCap; + p->pArray = p->nCap? ALLOC( char, p->nCap ) : NULL; + memcpy( p->pArray, pVec->pArray, sizeof(char) * pVec->nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Transfers the array into another vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Str_t * Vec_StrDupArray( Vec_Str_t * pVec ) +{ + Vec_Str_t * p; + p = ALLOC( Vec_Str_t, 1 ); + p->nSize = pVec->nSize; + p->nCap = pVec->nCap; + p->pArray = pVec->pArray; + pVec->nSize = 0; + pVec->nCap = 0; + pVec->pArray = NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_StrFree( Vec_Str_t * p ) +{ + FREE( p->pArray ); + FREE( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline char * Vec_StrReleaseArray( Vec_Str_t * p ) +{ + char * pArray = p->pArray; + p->nCap = 0; + p->nSize = 0; + p->pArray = NULL; + return pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline char * Vec_StrArray( Vec_Str_t * p ) +{ + return p->pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_StrSize( Vec_Str_t * p ) +{ + return p->nSize; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline char Vec_StrEntry( Vec_Str_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray[i]; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_StrWriteEntry( Vec_Str_t * p, int i, char Entry ) +{ + assert( i >= 0 && i < p->nSize ); + p->pArray[i] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline char Vec_StrEntryLast( Vec_Str_t * p ) +{ + assert( p->nSize > 0 ); + return p->pArray[p->nSize-1]; +} + +/**Function************************************************************* + + Synopsis [Resizes the vector to the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_StrGrow( Vec_Str_t * p, int nCapMin ) +{ + if ( p->nCap >= nCapMin ) + return; + p->pArray = REALLOC( char, p->pArray, 2 * nCapMin ); + p->nCap = 2 * nCapMin; +} + +/**Function************************************************************* + + Synopsis [Fills the vector with given number of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_StrFill( Vec_Str_t * p, int nSize, char Entry ) +{ + int i; + Vec_StrGrow( p, nSize ); + p->nSize = nSize; + for ( i = 0; i < p->nSize; i++ ) + p->pArray[i] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_StrShrink( Vec_Str_t * p, int nSizeNew ) +{ + assert( p->nSize >= nSizeNew ); + p->nSize = nSizeNew; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_StrClear( Vec_Str_t * p ) +{ + p->nSize = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_StrPush( Vec_Str_t * p, char Entry ) +{ + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_StrGrow( p, 16 ); + else + Vec_StrGrow( p, 2 * p->nCap ); + } + p->pArray[p->nSize++] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_StrPrintNum( Vec_Str_t * p, int Num ) +{ + int i, nDigits; + if ( Num < 0 ) + { + Vec_StrPush( p, '-' ); + Num = -Num; + } + if ( Num < 10 ) + { + Vec_StrPush( p, (char)('0' + Num) ); + return; + } + nDigits = Extra_Base10Log( Num ); + Vec_StrGrow( p, p->nSize + nDigits ); + for ( i = nDigits - 1; i >= 0; i-- ) + { + Vec_StrWriteEntry( p, p->nSize + i, (char)('0' + Num % 10) ); + Num /= 10; + } + assert( Num == 0 ); + p->nSize += nDigits; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_StrPrintStr( Vec_Str_t * p, char * pStr ) +{ + int i, Length = strlen(pStr); + for ( i = 0; i < Length; i++ ) + Vec_StrPush( p, pStr[i] ); +} + +/**Function************************************************************* + + Synopsis [Appends the string to the char vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_StrAppend( Vec_Str_t * p, char * pString ) +{ + int i, nLength = strlen(pString); + Vec_StrGrow( p, p->nSize + nLength ); + for ( i = 0; i < nLength; i++ ) + p->pArray[p->nSize + i] = pString[i]; + p->nSize += nLength; +} + +/**Function************************************************************* + + Synopsis [Returns the last entry and removes it from the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline char Vec_StrPop( Vec_Str_t * p ) +{ + assert( p->nSize > 0 ); + return p->pArray[--p->nSize]; +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two clauses.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_StrSortCompare1( char * pp1, char * pp2 ) +{ + // for some reason commenting out lines (as shown) led to crashing of the release version + if ( *pp1 < *pp2 ) + return -1; + if ( *pp1 > *pp2 ) // + return 1; + return 0; // +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two clauses.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_StrSortCompare2( char * pp1, char * pp2 ) +{ + // for some reason commenting out lines (as shown) led to crashing of the release version + if ( *pp1 > *pp2 ) + return -1; + if ( *pp1 < *pp2 ) // + return 1; + return 0; // +} + +/**Function************************************************************* + + Synopsis [Sorting the entries by their integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_StrSort( Vec_Str_t * p, int fReverse ) +{ + if ( fReverse ) + qsort( (void *)p->pArray, p->nSize, sizeof(char), + (int (*)(const void *, const void *)) Vec_StrSortCompare2 ); + else + qsort( (void *)p->pArray, p->nSize, sizeof(char), + (int (*)(const void *, const void *)) Vec_StrSortCompare1 ); +} + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/misc/vec/vecVec.h b/abc_with_bb_support/src/misc/vec/vecVec.h new file mode 100644 index 000000000..475cbb829 --- /dev/null +++ b/abc_with_bb_support/src/misc/vec/vecVec.h @@ -0,0 +1,356 @@ +/**CFile**************************************************************** + + FileName [vecVec.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resizable arrays.] + + Synopsis [Resizable vector of resizable vectors.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: vecVec.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __VEC_VEC_H__ +#define __VEC_VEC_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Vec_Vec_t_ Vec_Vec_t; +struct Vec_Vec_t_ +{ + int nCap; + int nSize; + void ** pArray; +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// iterators through levels +#define Vec_VecForEachLevel( vGlob, vVec, i ) \ + for ( i = 0; (i < Vec_VecSize(vGlob)) && (((vVec) = (Vec_Ptr_t*)Vec_VecEntry(vGlob, i)), 1); i++ ) +#define Vec_VecForEachLevelStart( vGlob, vVec, i, LevelStart ) \ + for ( i = LevelStart; (i < Vec_VecSize(vGlob)) && (((vVec) = (Vec_Ptr_t*)Vec_VecEntry(vGlob, i)), 1); i++ ) +#define Vec_VecForEachLevelStartStop( vGlob, vVec, i, LevelStart, LevelStop ) \ + for ( i = LevelStart; (i <= LevelStop) && (((vVec) = (Vec_Ptr_t*)Vec_VecEntry(vGlob, i)), 1); i++ ) +#define Vec_VecForEachLevelReverse( vGlob, vVec, i ) \ + for ( i = Vec_VecSize(vGlob) - 1; (i >= 0) && (((vVec) = (Vec_Ptr_t*)Vec_VecEntry(vGlob, i)), 1); i-- ) +#define Vec_VecForEachLevelReverseStartStop( vGlob, vVec, i, LevelStart, LevelStop ) \ + for ( i = LevelStart; (i >= LevelStop) && (((vVec) = (Vec_Ptr_t*)Vec_VecEntry(vGlob, i)), 1); i-- ) + +// iteratores through entries +#define Vec_VecForEachEntry( vGlob, pEntry, i, k ) \ + for ( i = 0; i < Vec_VecSize(vGlob); i++ ) \ + Vec_PtrForEachEntry( Vec_VecEntry(vGlob, i), pEntry, k ) +#define Vec_VecForEachEntryLevel( vGlob, pEntry, i, Level ) \ + Vec_PtrForEachEntry( Vec_VecEntry(vGlob, Level), pEntry, i ) +#define Vec_VecForEachEntryStart( vGlob, pEntry, i, k, LevelStart ) \ + for ( i = LevelStart; i < Vec_VecSize(vGlob); i++ ) \ + Vec_PtrForEachEntry( Vec_VecEntry(vGlob, i), pEntry, k ) +#define Vec_VecForEachEntryStartStop( vGlob, pEntry, i, k, LevelStart, LevelStop ) \ + for ( i = LevelStart; i <= LevelStop; i++ ) \ + Vec_PtrForEachEntry( Vec_VecEntry(vGlob, i), pEntry, k ) +#define Vec_VecForEachEntryReverse( vGlob, pEntry, i, k ) \ + for ( i = 0; i < Vec_VecSize(vGlob); i++ ) \ + Vec_PtrForEachEntryReverse( Vec_VecEntry(vGlob, i), pEntry, k ) +#define Vec_VecForEachEntryReverseReverse( vGlob, pEntry, i, k ) \ + for ( i = Vec_VecSize(vGlob) - 1; i >= 0; i-- ) \ + Vec_PtrForEachEntryReverse( Vec_VecEntry(vGlob, i), pEntry, k ) +#define Vec_VecForEachEntryReverseStart( vGlob, pEntry, i, k, LevelStart ) \ + for ( i = LevelStart; i >= 0; i-- ) \ + Vec_PtrForEachEntry( Vec_VecEntry(vGlob, i), pEntry, k ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Vec_t * Vec_VecAlloc( int nCap ) +{ + Vec_Vec_t * p; + p = ALLOC( Vec_Vec_t, 1 ); + if ( nCap > 0 && nCap < 8 ) + nCap = 8; + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ALLOC( void *, p->nCap ) : NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Vec_t * Vec_VecStart( int nSize ) +{ + Vec_Vec_t * p; + int i; + p = Vec_VecAlloc( nSize ); + for ( i = 0; i < nSize; i++ ) + p->pArray[i] = Vec_PtrAlloc( 0 ); + p->nSize = nSize; + return p; +} + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_VecExpand( Vec_Vec_t * p, int Level ) +{ + int i; + if ( p->nSize >= Level + 1 ) + return; + Vec_PtrGrow( (Vec_Ptr_t *)p, Level + 1 ); + for ( i = p->nSize; i <= Level; i++ ) + p->pArray[i] = Vec_PtrAlloc( 0 ); + p->nSize = Level + 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_VecSize( Vec_Vec_t * p ) +{ + return p->nSize; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void * Vec_VecEntry( Vec_Vec_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray[i]; +} + +/**Function************************************************************* + + Synopsis [Frees the vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_VecFree( Vec_Vec_t * p ) +{ + Vec_Ptr_t * vVec; + int i; + Vec_VecForEachLevel( p, vVec, i ) + Vec_PtrFree( vVec ); + Vec_PtrFree( (Vec_Ptr_t *)p ); +} + +/**Function************************************************************* + + Synopsis [Frees the vector of vectors.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_VecSizeSize( Vec_Vec_t * p ) +{ + Vec_Ptr_t * vVec; + int i, Counter = 0; + Vec_VecForEachLevel( p, vVec, i ) + Counter += vVec->nSize; + return Counter; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_VecClear( Vec_Vec_t * p ) +{ + Vec_Ptr_t * vVec; + int i; + Vec_VecForEachLevel( p, vVec, i ) + Vec_PtrClear( vVec ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_VecPush( Vec_Vec_t * p, int Level, void * Entry ) +{ + if ( p->nSize < Level + 1 ) + { + int i; + Vec_PtrGrow( (Vec_Ptr_t *)p, Level + 1 ); + for ( i = p->nSize; i < Level + 1; i++ ) + p->pArray[i] = Vec_PtrAlloc( 0 ); + p->nSize = Level + 1; + } + Vec_PtrPush( (Vec_Ptr_t*)p->pArray[Level], Entry ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_VecPushUnique( Vec_Vec_t * p, int Level, void * Entry ) +{ + if ( p->nSize < Level + 1 ) + Vec_VecPush( p, Level, Entry ); + else + Vec_PtrPushUnique( (Vec_Ptr_t*)p->pArray[Level], Entry ); +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two arrays.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_VecSortCompare1( Vec_Ptr_t ** pp1, Vec_Ptr_t ** pp2 ) +{ + if ( Vec_PtrSize(*pp1) < Vec_PtrSize(*pp2) ) + return -1; + if ( Vec_PtrSize(*pp1) > Vec_PtrSize(*pp2) ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two integers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_VecSortCompare2( Vec_Ptr_t ** pp1, Vec_Ptr_t ** pp2 ) +{ + if ( Vec_PtrSize(*pp1) > Vec_PtrSize(*pp2) ) + return -1; + if ( Vec_PtrSize(*pp1) < Vec_PtrSize(*pp2) ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Sorting the entries by their integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_VecSort( Vec_Vec_t * p, int fReverse ) +{ + if ( fReverse ) + qsort( (void *)p->pArray, p->nSize, sizeof(void *), + (int (*)(const void *, const void *)) Vec_VecSortCompare2 ); + else + qsort( (void *)p->pArray, p->nSize, sizeof(void *), + (int (*)(const void *, const void *)) Vec_VecSortCompare1 ); +} + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/opt/cut/abcCut.c b/abc_with_bb_support/src/opt/cut/abcCut.c new file mode 100644 index 000000000..98159e324 --- /dev/null +++ b/abc_with_bb_support/src/opt/cut/abcCut.c @@ -0,0 +1,492 @@ +/**CFile**************************************************************** + + FileName [abcCut.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Interface to cut computation.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: abcCut.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "cut.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Abc_NtkPrintCuts( void * p, Abc_Ntk_t * pNtk, int fSeq ); +static void Abc_NtkPrintCuts_( void * p, Abc_Ntk_t * pNtk, int fSeq ); + + +extern int nTotal, nGood, nEqual; + +// temporary +//Vec_Int_t * Abc_NtkGetNodeAttributes( Abc_Ntk_t * pNtk ) { return NULL; } +Vec_Int_t * Abc_NtkGetNodeAttributes( Abc_Ntk_t * pNtk ) +{ + Vec_Int_t * vAttrs = Vec_IntStart( Abc_NtkObjNumMax(pNtk) + 1 ); + int i; + Abc_Obj_t * pObj; + +// Abc_NtkForEachCi( pNtk, pObj, i ) +// Vec_IntWriteEntry( vAttrs, pObj->Id, 1 ); + + Abc_NtkForEachObj( pNtk, pObj, i ) + { +// if ( Abc_ObjIsNode(pObj) && (rand() % 4 == 0) ) + if ( Abc_ObjIsNode(pObj) && Abc_ObjFanoutNum(pObj) > 1 && !Abc_NodeIsMuxControlType(pObj) && (rand() % 3 == 0) ) + Vec_IntWriteEntry( vAttrs, pObj->Id, 1 ); + } + return vAttrs; +} + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes the cuts for the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Man_t * Abc_NtkCuts( Abc_Ntk_t * pNtk, Cut_Params_t * pParams ) +{ + ProgressBar * pProgress; + Cut_Man_t * p; + Abc_Obj_t * pObj, * pNode; + Vec_Ptr_t * vNodes; + Vec_Int_t * vChoices; + int i; + int clk = clock(); + + extern void Abc_NtkBalanceAttach( Abc_Ntk_t * pNtk ); + extern void Abc_NtkBalanceDetach( Abc_Ntk_t * pNtk ); + + nTotal = nGood = nEqual = 0; + + assert( Abc_NtkIsStrash(pNtk) ); + // start the manager + pParams->nIdsMax = Abc_NtkObjNumMax( pNtk ); + p = Cut_ManStart( pParams ); + // compute node attributes if local or global cuts are requested + if ( pParams->fGlobal || pParams->fLocal ) + { + extern Vec_Int_t * Abc_NtkGetNodeAttributes( Abc_Ntk_t * pNtk ); + Cut_ManSetNodeAttrs( p, Abc_NtkGetNodeAttributes(pNtk) ); + } + // prepare for cut dropping + if ( pParams->fDrop ) + Cut_ManSetFanoutCounts( p, Abc_NtkFanoutCounts(pNtk) ); + // set cuts for PIs + Abc_NtkForEachCi( pNtk, pObj, i ) + if ( Abc_ObjFanoutNum(pObj) > 0 ) + Cut_NodeSetTriv( p, pObj->Id ); + // compute cuts for internal nodes + vNodes = Abc_AigDfs( pNtk, 0, 1 ); // collects POs + vChoices = Vec_IntAlloc( 100 ); + pProgress = Extra_ProgressBarStart( stdout, Vec_PtrSize(vNodes) ); + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + // when we reached a CO, it is time to deallocate the cuts + if ( Abc_ObjIsCo(pObj) ) + { + if ( pParams->fDrop ) + Cut_NodeTryDroppingCuts( p, Abc_ObjFaninId0(pObj) ); + continue; + } + // skip constant node, it has no cuts +// if ( Abc_NodeIsConst(pObj) ) +// continue; + Extra_ProgressBarUpdate( pProgress, i, NULL ); + // compute the cuts to the internal node + Abc_NodeGetCuts( p, pObj, pParams->fDag, pParams->fTree ); + // consider dropping the fanins cuts + if ( pParams->fDrop ) + { + Cut_NodeTryDroppingCuts( p, Abc_ObjFaninId0(pObj) ); + Cut_NodeTryDroppingCuts( p, Abc_ObjFaninId1(pObj) ); + } + // add cuts due to choices + if ( Abc_AigNodeIsChoice(pObj) ) + { + Vec_IntClear( vChoices ); + for ( pNode = pObj; pNode; pNode = pNode->pData ) + Vec_IntPush( vChoices, pNode->Id ); + Cut_NodeUnionCuts( p, vChoices ); + } + } + Extra_ProgressBarStop( pProgress ); + Vec_PtrFree( vNodes ); + Vec_IntFree( vChoices ); +PRT( "Total", clock() - clk ); +//Abc_NtkPrintCuts( p, pNtk, 0 ); +// Cut_ManPrintStatsToFile( p, pNtk->pSpec, clock() - clk ); + + // temporary printout of stats + if ( nTotal ) + printf( "Total cuts = %d. Good cuts = %d. Ratio = %5.2f\n", nTotal, nGood, ((double)nGood)/nTotal ); + return p; +} + +/**Function************************************************************* + + Synopsis [Cut computation using the oracle.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkCutsOracle( Abc_Ntk_t * pNtk, Cut_Oracle_t * p ) +{ + Abc_Obj_t * pObj; + Vec_Ptr_t * vNodes; + int i, clk = clock(); + int fDrop = Cut_OracleReadDrop(p); + + assert( Abc_NtkIsStrash(pNtk) ); + + // prepare cut droppping + if ( fDrop ) + Cut_OracleSetFanoutCounts( p, Abc_NtkFanoutCounts(pNtk) ); + + // set cuts for PIs + Abc_NtkForEachCi( pNtk, pObj, i ) + if ( Abc_ObjFanoutNum(pObj) > 0 ) + Cut_OracleNodeSetTriv( p, pObj->Id ); + + // compute cuts for internal nodes + vNodes = Abc_AigDfs( pNtk, 0, 1 ); // collects POs + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + // when we reached a CO, it is time to deallocate the cuts + if ( Abc_ObjIsCo(pObj) ) + { + if ( fDrop ) + Cut_OracleTryDroppingCuts( p, Abc_ObjFaninId0(pObj) ); + continue; + } + // skip constant node, it has no cuts +// if ( Abc_NodeIsConst(pObj) ) +// continue; + // compute the cuts to the internal node + Cut_OracleComputeCuts( p, pObj->Id, Abc_ObjFaninId0(pObj), Abc_ObjFaninId1(pObj), + Abc_ObjFaninC0(pObj), Abc_ObjFaninC1(pObj) ); + // consider dropping the fanins cuts + if ( fDrop ) + { + Cut_OracleTryDroppingCuts( p, Abc_ObjFaninId0(pObj) ); + Cut_OracleTryDroppingCuts( p, Abc_ObjFaninId1(pObj) ); + } + } + Vec_PtrFree( vNodes ); +//PRT( "Total", clock() - clk ); +//Abc_NtkPrintCuts_( p, pNtk, 0 ); +} + + +/**Function************************************************************* + + Synopsis [Computes the cuts for the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Man_t * Abc_NtkSeqCuts( Abc_Ntk_t * pNtk, Cut_Params_t * pParams ) +{ +/* + Cut_Man_t * p; + Abc_Obj_t * pObj, * pNode; + int i, nIters, fStatus; + Vec_Int_t * vChoices; + int clk = clock(); + + assert( Abc_NtkIsSeq(pNtk) ); + assert( pParams->fSeq ); +// assert( Abc_NtkIsDfsOrdered(pNtk) ); + + // start the manager + pParams->nIdsMax = Abc_NtkObjNumMax( pNtk ); + pParams->nCutSet = Abc_NtkCutSetNodeNum( pNtk ); + p = Cut_ManStart( pParams ); + + // set cuts for the constant node and the PIs + pObj = Abc_AigConst1(pNtk); + if ( Abc_ObjFanoutNum(pObj) > 0 ) + Cut_NodeSetTriv( p, pObj->Id ); + Abc_NtkForEachPi( pNtk, pObj, i ) + { +//printf( "Setting trivial cut %d.\n", pObj->Id ); + Cut_NodeSetTriv( p, pObj->Id ); + } + // label the cutset nodes and set their number in the array + // assign the elementary cuts to the cutset nodes + Abc_SeqForEachCutsetNode( pNtk, pObj, i ) + { + assert( pObj->fMarkC == 0 ); + pObj->fMarkC = 1; + pObj->pCopy = (Abc_Obj_t *)i; + Cut_NodeSetTriv( p, pObj->Id ); +//printf( "Setting trivial cut %d.\n", pObj->Id ); + } + + // process the nodes + vChoices = Vec_IntAlloc( 100 ); + for ( nIters = 0; nIters < 10; nIters++ ) + { +//printf( "ITERATION %d:\n", nIters ); + // compute the cuts for the internal nodes + Abc_AigForEachAnd( pNtk, pObj, i ) + { + Abc_NodeGetCutsSeq( p, pObj, nIters==0 ); + // add cuts due to choices + if ( Abc_AigNodeIsChoice(pObj) ) + { + Vec_IntClear( vChoices ); + for ( pNode = pObj; pNode; pNode = pNode->pData ) + Vec_IntPush( vChoices, pNode->Id ); + Cut_NodeUnionCutsSeq( p, vChoices, (pObj->fMarkC ? (int)pObj->pCopy : -1), nIters==0 ); + } + } + // merge the new cuts with the old cuts + Abc_NtkForEachPi( pNtk, pObj, i ) + Cut_NodeNewMergeWithOld( p, pObj->Id ); + Abc_AigForEachAnd( pNtk, pObj, i ) + Cut_NodeNewMergeWithOld( p, pObj->Id ); + // for the cutset, transfer temp cuts to new cuts + fStatus = 0; + Abc_SeqForEachCutsetNode( pNtk, pObj, i ) + fStatus |= Cut_NodeTempTransferToNew( p, pObj->Id, i ); + if ( fStatus == 0 ) + break; + } + Vec_IntFree( vChoices ); + + // if the status is not finished, transfer new to old for the cutset + Abc_SeqForEachCutsetNode( pNtk, pObj, i ) + Cut_NodeNewMergeWithOld( p, pObj->Id ); + + // transfer the old cuts to the new positions + Abc_NtkForEachObj( pNtk, pObj, i ) + Cut_NodeOldTransferToNew( p, pObj->Id ); + + // unlabel the cutset nodes + Abc_SeqForEachCutsetNode( pNtk, pObj, i ) + pObj->fMarkC = 0; +if ( pParams->fVerbose ) +{ +PRT( "Total", clock() - clk ); +printf( "Converged after %d iterations.\n", nIters ); +} +//Abc_NtkPrintCuts( p, pNtk, 1 ); + return p; +*/ +} + +/**Function************************************************************* + + Synopsis [Computes the cuts for the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void * Abc_NodeGetCutsRecursive( void * p, Abc_Obj_t * pObj, int fDag, int fTree ) +{ + void * pList; + if ( pList = Abc_NodeReadCuts( p, pObj ) ) + return pList; + Abc_NodeGetCutsRecursive( p, Abc_ObjFanin0(pObj), fDag, fTree ); + Abc_NodeGetCutsRecursive( p, Abc_ObjFanin1(pObj), fDag, fTree ); + return Abc_NodeGetCuts( p, pObj, fDag, fTree ); +} + +/**Function************************************************************* + + Synopsis [Computes the cuts for the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void * Abc_NodeGetCuts( void * p, Abc_Obj_t * pObj, int fDag, int fTree ) +{ + Abc_Obj_t * pFanin; + int fDagNode, fTriv, TreeCode = 0; +// assert( Abc_NtkIsStrash(pObj->pNtk) ); + assert( Abc_ObjFaninNum(pObj) == 2 ); + + + // check if the node is a DAG node + fDagNode = (Abc_ObjFanoutNum(pObj) > 1 && !Abc_NodeIsMuxControlType(pObj)); + // increment the counter of DAG nodes + if ( fDagNode ) Cut_ManIncrementDagNodes( p ); + // add the trivial cut if the node is a DAG node, or if we compute all cuts + fTriv = fDagNode || !fDag; + // check if fanins are DAG nodes + if ( fTree ) + { + pFanin = Abc_ObjFanin0(pObj); + TreeCode |= (Abc_ObjFanoutNum(pFanin) > 1 && !Abc_NodeIsMuxControlType(pFanin)); + pFanin = Abc_ObjFanin1(pObj); + TreeCode |= ((Abc_ObjFanoutNum(pFanin) > 1 && !Abc_NodeIsMuxControlType(pFanin)) << 1); + } + + + // changes due to the global/local cut computation + { + Cut_Params_t * pParams = Cut_ManReadParams(p); + if ( pParams->fLocal ) + { + Vec_Int_t * vNodeAttrs = Cut_ManReadNodeAttrs(p); + fDagNode = Vec_IntEntry( vNodeAttrs, pObj->Id ); + if ( fDagNode ) Cut_ManIncrementDagNodes( p ); +// fTriv = fDagNode || !pParams->fGlobal; + fTriv = !Vec_IntEntry( vNodeAttrs, pObj->Id ); + TreeCode = 0; + pFanin = Abc_ObjFanin0(pObj); + TreeCode |= Vec_IntEntry( vNodeAttrs, pFanin->Id ); + pFanin = Abc_ObjFanin1(pObj); + TreeCode |= (Vec_IntEntry( vNodeAttrs, pFanin->Id ) << 1); + } + } + return Cut_NodeComputeCuts( p, pObj->Id, Abc_ObjFaninId0(pObj), Abc_ObjFaninId1(pObj), + Abc_ObjFaninC0(pObj), Abc_ObjFaninC1(pObj), fTriv, TreeCode ); +} + +/**Function************************************************************* + + Synopsis [Computes the cuts for the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeGetCutsSeq( void * p, Abc_Obj_t * pObj, int fTriv ) +{ + int CutSetNum; + assert( Abc_NtkIsSeq(pObj->pNtk) ); + assert( Abc_ObjFaninNum(pObj) == 2 ); + fTriv = pObj->fMarkC ? 0 : fTriv; + CutSetNum = pObj->fMarkC ? (int)pObj->pCopy : -1; + Cut_NodeComputeCutsSeq( p, pObj->Id, Abc_ObjFaninId0(pObj), Abc_ObjFaninId1(pObj), + Abc_ObjFaninC0(pObj), Abc_ObjFaninC1(pObj), Seq_ObjFaninL0(pObj), Seq_ObjFaninL1(pObj), fTriv, CutSetNum ); +} + +/**Function************************************************************* + + Synopsis [Computes the cuts for the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void * Abc_NodeReadCuts( void * p, Abc_Obj_t * pObj ) +{ + return Cut_NodeReadCutsNew( p, pObj->Id ); +} + +/**Function************************************************************* + + Synopsis [Computes the cuts for the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NodeFreeCuts( void * p, Abc_Obj_t * pObj ) +{ + Cut_NodeFreeCuts( p, pObj->Id ); +} + +/**Function************************************************************* + + Synopsis [Computes the cuts for the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintCuts( void * p, Abc_Ntk_t * pNtk, int fSeq ) +{ + Cut_Man_t * pMan = p; + Cut_Cut_t * pList; + Abc_Obj_t * pObj; + int i; + printf( "Cuts of the network:\n" ); + Abc_NtkForEachObj( pNtk, pObj, i ) + { + pList = Abc_NodeReadCuts( p, pObj ); + printf( "Node %s:\n", Abc_ObjName(pObj) ); + Cut_CutPrintList( pList, fSeq ); + } +} + +/**Function************************************************************* + + Synopsis [Computes the cuts for the network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkPrintCuts_( void * p, Abc_Ntk_t * pNtk, int fSeq ) +{ + Cut_Man_t * pMan = p; + Cut_Cut_t * pList; + Abc_Obj_t * pObj; + pObj = Abc_NtkObj( pNtk, 2 * Abc_NtkObjNum(pNtk) / 3 ); + pList = Abc_NodeReadCuts( p, pObj ); + printf( "Node %s:\n", Abc_ObjName(pObj) ); + Cut_CutPrintList( pList, fSeq ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/cut/cut.h b/abc_with_bb_support/src/opt/cut/cut.h new file mode 100644 index 000000000..f0a091e12 --- /dev/null +++ b/abc_with_bb_support/src/opt/cut/cut.h @@ -0,0 +1,165 @@ +/**CFile**************************************************************** + + FileName [cut.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [K-feasible cut computation package.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: .h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __CUT_H__ +#define __CUT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +#define CUT_SIZE_MIN 3 // the min K of the K-feasible cut computation +#define CUT_SIZE_MAX 12 // the max K of the K-feasible cut computation + +#define CUT_SHIFT 8 // the number of bits for storing latch number in the cut leaves +#define CUT_MASK 0xFF // the mask to get the stored latch number + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Cut_ManStruct_t_ Cut_Man_t; +typedef struct Cut_OracleStruct_t_ Cut_Oracle_t; +typedef struct Cut_CutStruct_t_ Cut_Cut_t; +typedef struct Cut_ParamsStruct_t_ Cut_Params_t; + +struct Cut_ParamsStruct_t_ +{ + int nVarsMax; // the max cut size ("k" of the k-feasible cuts) + int nKeepMax; // the max number of cuts kept at a node + int nIdsMax; // the max number of IDs of cut objects + int nBitShift; // the number of bits used for the latch counter of an edge + int nCutSet; // the number of nodes in the cut set + int fTruth; // compute truth tables + int fFilter; // filter dominated cuts + int fSeq; // compute sequential cuts + int fDrop; // drop cuts on the fly + int fDag; // compute only DAG cuts + int fTree; // compute only tree cuts + int fGlobal; // compute only global cuts + int fLocal; // compute only local cuts + int fRecord; // record the cut computation flow + int fFancy; // perform fancy computations + int fMap; // computes delay of FPGA mapping with cuts + int fVerbose; // the verbosiness flag +}; + +struct Cut_CutStruct_t_ +{ + unsigned Num0 : 11; // temporary number + unsigned Num1 : 11; // temporary number + unsigned fSimul : 1; // the value of cut's output at 000.. pattern + unsigned fCompl : 1; // the cut is complemented + unsigned nVarsMax : 4; // the max number of vars [4-6] + unsigned nLeaves : 4; // the number of leaves [4-6] + unsigned uSign; // the signature + unsigned uCanon0; // the canonical form + unsigned uCanon1; // the canonical form + Cut_Cut_t * pNext; // the next cut in the list + int pLeaves[0]; // the array of leaves +}; + +static inline int Cut_CutReadLeaveNum( Cut_Cut_t * p ) { return p->nLeaves; } +static inline int * Cut_CutReadLeaves( Cut_Cut_t * p ) { return p->pLeaves; } +static inline unsigned * Cut_CutReadTruth( Cut_Cut_t * p ) { return (unsigned *)(p->pLeaves + p->nVarsMax); } +static inline void Cut_CutWriteTruth( Cut_Cut_t * p, unsigned * puTruth ) { + int i; + for ( i = (p->nVarsMax <= 5) ? 0 : ((1 << (p->nVarsMax - 5)) - 1); i >= 0; i-- ) + p->pLeaves[p->nVarsMax + i] = (int)puTruth[i]; +} + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== cutApi.c ==========================================================*/ +extern Cut_Cut_t * Cut_NodeReadCutsNew( Cut_Man_t * p, int Node ); +extern Cut_Cut_t * Cut_NodeReadCutsOld( Cut_Man_t * p, int Node ); +extern Cut_Cut_t * Cut_NodeReadCutsTemp( Cut_Man_t * p, int Node ); +extern void Cut_NodeWriteCutsNew( Cut_Man_t * p, int Node, Cut_Cut_t * pList ); +extern void Cut_NodeWriteCutsOld( Cut_Man_t * p, int Node, Cut_Cut_t * pList ); +extern void Cut_NodeWriteCutsTemp( Cut_Man_t * p, int Node, Cut_Cut_t * pList ); +extern void Cut_NodeSetTriv( Cut_Man_t * p, int Node ); +extern void Cut_NodeTryDroppingCuts( Cut_Man_t * p, int Node ); +extern void Cut_NodeFreeCuts( Cut_Man_t * p, int Node ); +/*=== cutCut.c ==========================================================*/ +extern void Cut_CutPrint( Cut_Cut_t * pCut, int fSeq ); +extern void Cut_CutPrintList( Cut_Cut_t * pList, int fSeq ); +extern int Cut_CutCountList( Cut_Cut_t * pList ); +/*=== cutMan.c ==========================================================*/ +extern Cut_Man_t * Cut_ManStart( Cut_Params_t * pParams ); +extern void Cut_ManStop( Cut_Man_t * p ); +extern void Cut_ManPrintStats( Cut_Man_t * p ); +extern void Cut_ManPrintStatsToFile( Cut_Man_t * p, char * pFileName, int TimeTotal ); +extern void Cut_ManSetFanoutCounts( Cut_Man_t * p, Vec_Int_t * vFanCounts ); +extern void Cut_ManSetNodeAttrs( Cut_Man_t * p, Vec_Int_t * vFanCounts ); +extern int Cut_ManReadVarsMax( Cut_Man_t * p ); +extern Cut_Params_t * Cut_ManReadParams( Cut_Man_t * p ); +extern Vec_Int_t * Cut_ManReadNodeAttrs( Cut_Man_t * p ); +extern void Cut_ManIncrementDagNodes( Cut_Man_t * p ); +/*=== cutNode.c ==========================================================*/ +extern Cut_Cut_t * Cut_NodeComputeCuts( Cut_Man_t * p, int Node, int Node0, int Node1, int fCompl0, int fCompl1, int fTriv, int TreeCode ); +extern Cut_Cut_t * Cut_NodeUnionCuts( Cut_Man_t * p, Vec_Int_t * vNodes ); +extern Cut_Cut_t * Cut_NodeUnionCutsSeq( Cut_Man_t * p, Vec_Int_t * vNodes, int CutSetNum, int fFirst ); +extern int Cut_ManMappingArea_rec( Cut_Man_t * p, int Node ); +/*=== cutSeq.c ==========================================================*/ +extern void Cut_NodeComputeCutsSeq( Cut_Man_t * p, int Node, int Node0, int Node1, int fCompl0, int fCompl1, int nLat0, int nLat1, int fTriv, int CutSetNum ); +extern void Cut_NodeNewMergeWithOld( Cut_Man_t * p, int Node ); +extern int Cut_NodeTempTransferToNew( Cut_Man_t * p, int Node, int CutSetNum ); +extern void Cut_NodeOldTransferToNew( Cut_Man_t * p, int Node ); +/*=== cutOracle.c ==========================================================*/ +extern Cut_Oracle_t * Cut_OracleStart( Cut_Man_t * pMan ); +extern void Cut_OracleStop( Cut_Oracle_t * p ); +extern void Cut_OracleSetFanoutCounts( Cut_Oracle_t * p, Vec_Int_t * vFanCounts ); +extern int Cut_OracleReadDrop( Cut_Oracle_t * p ); +extern void Cut_OracleNodeSetTriv( Cut_Oracle_t * p, int Node ); +extern Cut_Cut_t * Cut_OracleComputeCuts( Cut_Oracle_t * p, int Node, int Node0, int Node1, int fCompl0, int fCompl1 ); +extern void Cut_OracleTryDroppingCuts( Cut_Oracle_t * p, int Node ); +/*=== cutTruth.c ==========================================================*/ +extern void Cut_TruthNCanonicize( Cut_Cut_t * pCut ); +/*=== cutPre22.c ==========================================================*/ +extern void Cut_CellPrecompute(); +extern void Cut_CellLoad(); +extern int Cut_CellIsRunning(); +extern void Cut_CellDumpToFile(); +extern int Cut_CellTruthLookup( unsigned * pTruth, int nVars ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/opt/cut/cutApi.c b/abc_with_bb_support/src/opt/cut/cutApi.c new file mode 100644 index 000000000..ee7878767 --- /dev/null +++ b/abc_with_bb_support/src/opt/cut/cutApi.c @@ -0,0 +1,197 @@ +/**CFile**************************************************************** + + FileName [cutNode.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [K-feasible cut computation package.] + + Synopsis [Procedures to compute cuts for a node.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cutNode.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cutInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Returns the pointer to the linked list of cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Cut_t * Cut_NodeReadCutsNew( Cut_Man_t * p, int Node ) +{ + if ( Node >= p->vCutsNew->nSize ) + return NULL; + return Vec_PtrEntry( p->vCutsNew, Node ); +} + +/**Function************************************************************* + + Synopsis [Returns the pointer to the linked list of cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Cut_t * Cut_NodeReadCutsOld( Cut_Man_t * p, int Node ) +{ + assert( Node < p->vCutsOld->nSize ); + return Vec_PtrEntry( p->vCutsOld, Node ); +} + +/**Function************************************************************* + + Synopsis [Returns the pointer to the linked list of cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Cut_t * Cut_NodeReadCutsTemp( Cut_Man_t * p, int Node ) +{ + assert( Node < p->vCutsTemp->nSize ); + return Vec_PtrEntry( p->vCutsTemp, Node ); +} + +/**Function************************************************************* + + Synopsis [Returns the pointer to the linked list of cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_NodeWriteCutsNew( Cut_Man_t * p, int Node, Cut_Cut_t * pList ) +{ + Vec_PtrWriteEntry( p->vCutsNew, Node, pList ); +} + +/**Function************************************************************* + + Synopsis [Returns the pointer to the linked list of cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_NodeWriteCutsOld( Cut_Man_t * p, int Node, Cut_Cut_t * pList ) +{ + Vec_PtrWriteEntry( p->vCutsOld, Node, pList ); +} + +/**Function************************************************************* + + Synopsis [Returns the pointer to the linked list of cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_NodeWriteCutsTemp( Cut_Man_t * p, int Node, Cut_Cut_t * pList ) +{ + Vec_PtrWriteEntry( p->vCutsTemp, Node, pList ); +} + +/**Function************************************************************* + + Synopsis [Sets the trivial cut for the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_NodeSetTriv( Cut_Man_t * p, int Node ) +{ + assert( Cut_NodeReadCutsNew(p, Node) == NULL ); + Cut_NodeWriteCutsNew( p, Node, Cut_CutCreateTriv(p, Node) ); +} + +/**Function************************************************************* + + Synopsis [Consider dropping cuts if they are useless by now.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_NodeTryDroppingCuts( Cut_Man_t * p, int Node ) +{ + int nFanouts; + assert( p->vFanCounts ); + nFanouts = Vec_IntEntry( p->vFanCounts, Node ); + assert( nFanouts > 0 ); + if ( --nFanouts == 0 ) + Cut_NodeFreeCuts( p, Node ); + Vec_IntWriteEntry( p->vFanCounts, Node, nFanouts ); +} + +/**Function************************************************************* + + Synopsis [Deallocates the cuts at the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_NodeFreeCuts( Cut_Man_t * p, int Node ) +{ + Cut_Cut_t * pList, * pCut, * pCut2; + pList = Cut_NodeReadCutsNew( p, Node ); + if ( pList == NULL ) + return; + Cut_ListForEachCutSafe( pList, pCut, pCut2 ) + Cut_CutRecycle( p, pCut ); + Cut_NodeWriteCutsNew( p, Node, NULL ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/cut/cutCut.c b/abc_with_bb_support/src/opt/cut/cutCut.c new file mode 100644 index 000000000..04e30469c --- /dev/null +++ b/abc_with_bb_support/src/opt/cut/cutCut.c @@ -0,0 +1,359 @@ +/**CFile**************************************************************** + + FileName [cutNode.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [K-feasible cut computation package.] + + Synopsis [Procedures to compute cuts for a node.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cutNode.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cutInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Cut_t * Cut_CutAlloc( Cut_Man_t * p ) +{ + Cut_Cut_t * pCut; + // cut allocation + pCut = (Cut_Cut_t *)Extra_MmFixedEntryFetch( p->pMmCuts ); + memset( pCut, 0, sizeof(Cut_Cut_t) ); + pCut->nVarsMax = p->pParams->nVarsMax; + pCut->fSimul = p->fSimul; + // statistics + p->nCutsAlloc++; + p->nCutsCur++; + if ( p->nCutsPeak < p->nCutsAlloc - p->nCutsDealloc ) + p->nCutsPeak = p->nCutsAlloc - p->nCutsDealloc; + return pCut; +} + +/**Function************************************************************* + + Synopsis [Recybles the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_CutRecycle( Cut_Man_t * p, Cut_Cut_t * pCut ) +{ + p->nCutsDealloc++; + p->nCutsCur--; + if ( pCut->nLeaves == 1 ) + p->nCutsTriv--; + Extra_MmFixedEntryRecycle( p->pMmCuts, (char *)pCut ); +} + +/**Function************************************************************* + + Synopsis [Compares two cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Cut_CutCompare( Cut_Cut_t * pCut1, Cut_Cut_t * pCut2 ) +{ + int i; + if ( pCut1->nLeaves < pCut2->nLeaves ) + return -1; + if ( pCut1->nLeaves > pCut2->nLeaves ) + return 1; + for ( i = 0; i < (int)pCut1->nLeaves; i++ ) + { + if ( pCut1->pLeaves[i] < pCut2->pLeaves[i] ) + return -1; + if ( pCut1->pLeaves[i] > pCut2->pLeaves[i] ) + return 1; + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Duplicates the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Cut_t * Cut_CutDupList( Cut_Man_t * p, Cut_Cut_t * pList ) +{ + Cut_Cut_t * pHead = NULL, ** ppTail = &pHead; + Cut_Cut_t * pTemp, * pCopy; + if ( pList == NULL ) + return NULL; + Cut_ListForEachCut( pList, pTemp ) + { + pCopy = (Cut_Cut_t *)Extra_MmFixedEntryFetch( p->pMmCuts ); + memcpy( pCopy, pTemp, p->EntrySize ); + *ppTail = pCopy; + ppTail = &pCopy->pNext; + } + *ppTail = NULL; + return pHead; +} + +/**Function************************************************************* + + Synopsis [Recycles the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_CutRecycleList( Cut_Man_t * p, Cut_Cut_t * pList ) +{ + Cut_Cut_t * pCut, * pCut2; + Cut_ListForEachCutSafe( pList, pCut, pCut2 ) + Extra_MmFixedEntryRecycle( p->pMmCuts, (char *)pCut ); +} + +/**Function************************************************************* + + Synopsis [Counts the number of cuts in the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Cut_CutCountList( Cut_Cut_t * pList ) +{ + int Counter = 0; + Cut_ListForEachCut( pList, pList ) + Counter++; + return Counter; +} + +/**Function************************************************************* + + Synopsis [Merges two NULL-terminated linked lists.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Cut_t * Cut_CutMergeLists( Cut_Cut_t * pList1, Cut_Cut_t * pList2 ) +{ + Cut_Cut_t * pList = NULL, ** ppTail = &pList; + Cut_Cut_t * pCut; + while ( pList1 && pList2 ) + { + if ( Cut_CutCompare(pList1, pList2) < 0 ) + { + pCut = pList1; + pList1 = pList1->pNext; + } + else + { + pCut = pList2; + pList2 = pList2->pNext; + } + *ppTail = pCut; + ppTail = &pCut->pNext; + } + *ppTail = pList1? pList1: pList2; + return pList; +} + +/**Function************************************************************* + + Synopsis [Sets the number of the cuts in the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_CutNumberList( Cut_Cut_t * pList ) +{ + Cut_Cut_t * pCut; + int i = 0; + Cut_ListForEachCut( pList, pCut ) + pCut->Num0 = i++; +} + +/**Function************************************************************* + + Synopsis [Creates the trivial cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Cut_t * Cut_CutCreateTriv( Cut_Man_t * p, int Node ) +{ + Cut_Cut_t * pCut; + if ( p->pParams->fSeq ) + Node <<= CUT_SHIFT; + pCut = Cut_CutAlloc( p ); + pCut->nLeaves = 1; + pCut->pLeaves[0] = Node; + pCut->uSign = Cut_NodeSign( Node ); + if ( p->pParams->fTruth ) + { +/* + if ( pCut->nVarsMax == 4 ) + Cut_CutWriteTruth( pCut, p->uTruthVars[0] ); + else + Extra_BitCopy( pCut->nLeaves, p->uTruths[0], (uint8*)Cut_CutReadTruth(pCut) ); +*/ + unsigned * pTruth = Cut_CutReadTruth(pCut); + int i; + for ( i = 0; i < p->nTruthWords; i++ ) + pTruth[i] = 0xAAAAAAAA; + } + p->nCutsTriv++; + return pCut; +} + + +/**Function************************************************************* + + Synopsis [Print the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_CutPrint( Cut_Cut_t * pCut, int fSeq ) +{ + int i; + assert( pCut->nLeaves > 0 ); + printf( "%d : {", pCut->nLeaves ); + for ( i = 0; i < (int)pCut->nLeaves; i++ ) + { + if ( fSeq ) + { + printf( " %d", pCut->pLeaves[i] >> CUT_SHIFT ); + if ( pCut->pLeaves[i] & CUT_MASK ) + printf( "(%d)", pCut->pLeaves[i] & CUT_MASK ); + } + else + printf( " %d", pCut->pLeaves[i] ); + } + printf( " }" ); +// printf( "\nSign = " ); +// Extra_PrintBinary( stdout, &pCut->uSign, 32 ); +} + +/**Function************************************************************* + + Synopsis [Print the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_CutPrintList( Cut_Cut_t * pList, int fSeq ) +{ + Cut_Cut_t * pCut; + for ( pCut = pList; pCut; pCut = pCut->pNext ) + Cut_CutPrint( pCut, fSeq ), printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [Consider dropping cuts if they are useless by now.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_CutPrintMerge( Cut_Cut_t * pCut, Cut_Cut_t * pCut0, Cut_Cut_t * pCut1 ) +{ + printf( "\n" ); + printf( "%d : %5d %5d %5d %5d %5d\n", + pCut0->nLeaves, + pCut0->nLeaves > 0 ? pCut0->pLeaves[0] : -1, + pCut0->nLeaves > 1 ? pCut0->pLeaves[1] : -1, + pCut0->nLeaves > 2 ? pCut0->pLeaves[2] : -1, + pCut0->nLeaves > 3 ? pCut0->pLeaves[3] : -1, + pCut0->nLeaves > 4 ? pCut0->pLeaves[4] : -1 + ); + printf( "%d : %5d %5d %5d %5d %5d\n", + pCut1->nLeaves, + pCut1->nLeaves > 0 ? pCut1->pLeaves[0] : -1, + pCut1->nLeaves > 1 ? pCut1->pLeaves[1] : -1, + pCut1->nLeaves > 2 ? pCut1->pLeaves[2] : -1, + pCut1->nLeaves > 3 ? pCut1->pLeaves[3] : -1, + pCut1->nLeaves > 4 ? pCut1->pLeaves[4] : -1 + ); + if ( pCut == NULL ) + printf( "Cannot merge\n" ); + else + printf( "%d : %5d %5d %5d %5d %5d\n", + pCut->nLeaves, + pCut->nLeaves > 0 ? pCut->pLeaves[0] : -1, + pCut->nLeaves > 1 ? pCut->pLeaves[1] : -1, + pCut->nLeaves > 2 ? pCut->pLeaves[2] : -1, + pCut->nLeaves > 3 ? pCut->pLeaves[3] : -1, + pCut->nLeaves > 4 ? pCut->pLeaves[4] : -1 + ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/cut/cutExpand.c b/abc_with_bb_support/src/opt/cut/cutExpand.c new file mode 100644 index 000000000..2e094b92c --- /dev/null +++ b/abc_with_bb_support/src/opt/cut/cutExpand.c @@ -0,0 +1,184 @@ +/**CFile**************************************************************** + + FileName [cutExpand.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [K-feasible cut computation package.] + + Synopsis [Computes the truth table of the cut after expansion.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cutExpand.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cutInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define CUT_CELL_MVAR 9 + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes the stretching phase of the cut w.r.t. the merged cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Cut_TruthPhase( Cut_Cut_t * pCut, Cut_Cut_t * pCut1 ) +{ + unsigned uPhase = 0; + int i, k; + for ( i = k = 0; i < (int)pCut->nLeaves; i++ ) + { + if ( k == (int)pCut1->nLeaves ) + break; + if ( pCut->pLeaves[i] < pCut1->pLeaves[k] ) + continue; + assert( pCut->pLeaves[i] == pCut1->pLeaves[k] ); + uPhase |= (1 << i); + k++; + } + return uPhase; +} + +/**Function************************************************************* + + Synopsis [Computes the truth table of the composition of cuts.] + + Description [Inputs are: + - a factor cut (truth table is stored inside) + - a node in the factor cut + - a tree cut to be substituted (truth table is stored inside) + - the resulting cut (truth table will be filled in). + Note that all cuts, including the resulting one, should be already + computed and the nodes should be stored in the ascending order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_TruthCompose( Cut_Cut_t * pCutF, int Node, Cut_Cut_t * pCutT, Cut_Cut_t * pCutRes ) +{ + static unsigned uCof0[1<<(CUT_CELL_MVAR-5)]; + static unsigned uCof1[1<<(CUT_CELL_MVAR-5)]; + static unsigned uTemp[1<<(CUT_CELL_MVAR-5)]; + unsigned * pIn, * pOut, * pTemp; + unsigned uPhase; + int NodeIndex, i, k; + + // sanity checks + assert( pCutF->nVarsMax == pCutT->nVarsMax ); + assert( pCutF->nVarsMax == pCutRes->nVarsMax ); + assert( pCutF->nVarsMax <= CUT_CELL_MVAR ); + // the factor cut (pCutF) should have its nodes sorted in the ascending order + assert( pCutF->nLeaves <= pCutF->nVarsMax ); + for ( i = 0; i < (int)pCutF->nLeaves - 1; i++ ) + assert( pCutF->pLeaves[i] < pCutF->pLeaves[i+1] ); + // the tree cut (pCutT) should have its nodes sorted in the ascending order + assert( pCutT->nLeaves <= pCutT->nVarsMax ); + for ( i = 0; i < (int)pCutT->nLeaves - 1; i++ ) + assert( pCutT->pLeaves[i] < pCutT->pLeaves[i+1] ); + // the resulting cut (pCutRes) should have its nodes sorted in the ascending order + assert( pCutRes->nLeaves <= pCutRes->nVarsMax ); + for ( i = 0; i < (int)pCutRes->nLeaves - 1; i++ ) + assert( pCutRes->pLeaves[i] < pCutRes->pLeaves[i+1] ); + // make sure that every node in pCutF (except Node) appears in pCutRes + for ( i = 0; i < (int)pCutF->nLeaves; i++ ) + { + if ( pCutF->pLeaves[i] == Node ) + continue; + for ( k = 0; k < (int)pCutRes->nLeaves; k++ ) + if ( pCutF->pLeaves[i] == pCutRes->pLeaves[k] ) + break; + assert( k < (int)pCutRes->nLeaves ); // node i from pCutF is not found in pCutRes!!! + } + // make sure that every node in pCutT appears in pCutRes + for ( i = 0; i < (int)pCutT->nLeaves; i++ ) + { + for ( k = 0; k < (int)pCutRes->nLeaves; k++ ) + if ( pCutT->pLeaves[i] == pCutRes->pLeaves[k] ) + break; + assert( k < (int)pCutRes->nLeaves ); // node i from pCutT is not found in pCutRes!!! + } + + + // find the index of the given node in the factor cut + NodeIndex = -1; + for ( NodeIndex = 0; NodeIndex < (int)pCutF->nLeaves; NodeIndex++ ) + if ( pCutF->pLeaves[NodeIndex] == Node ) + break; + assert( NodeIndex >= 0 ); // Node should be in pCutF + + // copy the truth table + Extra_TruthCopy( uTemp, Cut_CutReadTruth(pCutF), pCutF->nLeaves ); + + // bubble-move the NodeIndex variable to be the last one (the most significant one) + pIn = uTemp; pOut = uCof0; // uCof0 is used for temporary storage here + for ( i = NodeIndex; i < (int)pCutF->nLeaves - 1; i++ ) + { + Extra_TruthSwapAdjacentVars( pOut, pIn, pCutF->nLeaves, i ); + pTemp = pIn; pIn = pOut; pOut = pTemp; + } + if ( (pCutF->nLeaves - 1 - NodeIndex) & 1 ) + Extra_TruthCopy( pOut, pIn, pCutF->nLeaves ); + // the result of stretching is in uTemp + + // cofactor the factor cut with respect to the node + Extra_TruthCopy( uCof0, uTemp, pCutF->nLeaves ); + Extra_TruthCofactor0( uCof0, pCutF->nLeaves, pCutF->nLeaves-1 ); + Extra_TruthCopy( uCof1, uTemp, pCutF->nLeaves ); + Extra_TruthCofactor1( uCof1, pCutF->nLeaves, pCutF->nLeaves-1 ); + + // temporarily shrink the factor cut's variables by removing Node + for ( i = NodeIndex; i < (int)pCutF->nLeaves - 1; i++ ) + pCutF->pLeaves[i] = pCutF->pLeaves[i+1]; + pCutF->nLeaves--; + + // spread out the cofactors' truth tables to the same var order as the resulting cut + uPhase = Cut_TruthPhase(pCutRes, pCutF); + assert( Extra_WordCountOnes(uPhase) == (int)pCutF->nLeaves ); + Extra_TruthStretch( uTemp, uCof0, pCutF->nLeaves, pCutF->nVarsMax, uPhase ); + Extra_TruthCopy( uCof0, uTemp, pCutF->nVarsMax ); + Extra_TruthStretch( uTemp, uCof1, pCutF->nLeaves, pCutF->nVarsMax, uPhase ); + Extra_TruthCopy( uCof1, uTemp, pCutF->nVarsMax ); + + // spread out the tree cut's truth table to the same var order as the resulting cut + uPhase = Cut_TruthPhase(pCutRes, pCutT); + assert( Extra_WordCountOnes(uPhase) == (int)pCutT->nLeaves ); + Extra_TruthStretch( uTemp, Cut_CutReadTruth(pCutT), pCutT->nLeaves, pCutT->nVarsMax, uPhase ); + + // create the resulting truth table + pTemp = Cut_CutReadTruth(pCutRes); + for ( i = Extra_TruthWordNum(pCutRes->nLeaves)-1; i >= 0; i-- ) + pTemp[i] = (uCof0[i] & ~uTemp[i]) | (uCof1[i] & uTemp[i]); + + // undo the removal of the node from the cut + for ( i = (int)pCutF->nLeaves - 1; i >= NodeIndex; --i ) + pCutF->pLeaves[i+1] = pCutF->pLeaves[i]; + pCutF->pLeaves[NodeIndex] = Node; + pCutF->nLeaves++; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/cut/cutInt.h b/abc_with_bb_support/src/opt/cut/cutInt.h new file mode 100644 index 000000000..eabc33891 --- /dev/null +++ b/abc_with_bb_support/src/opt/cut/cutInt.h @@ -0,0 +1,157 @@ +/**CFile**************************************************************** + + FileName [cutInt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [K-feasible cut computation package.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cutInt.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __CUT_INT_H__ +#define __CUT_INT_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include +#include "extra.h" +#include "vec.h" +#include "cut.h" +#include "cutList.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Cut_HashTableStruct_t_ Cut_HashTable_t; + +struct Cut_ManStruct_t_ +{ + // user preferences + Cut_Params_t * pParams; // computation parameters + Vec_Int_t * vFanCounts; // the array of fanout counters + Vec_Int_t * vNodeAttrs; // node attributes (1 = global; 0 = local) + // storage for cuts + Vec_Ptr_t * vCutsNew; // new cuts by node ID + Vec_Ptr_t * vCutsOld; // old cuts by node ID + Vec_Ptr_t * vCutsTemp; // temp cuts for cutset nodes by cutset node number + // memory management + Extra_MmFixed_t * pMmCuts; + int EntrySize; + int nTruthWords; + // temporary variables + Cut_Cut_t * pReady; + Vec_Ptr_t * vTemp; + int fCompl0; + int fCompl1; + int fSimul; + int nNodeCuts; + Cut_Cut_t * pStore0[2]; + Cut_Cut_t * pStore1[2]; + Cut_Cut_t * pCompareOld; + Cut_Cut_t * pCompareNew; + unsigned * puTemp[4]; + // record of the cut computation + Vec_Int_t * vNodeCuts; // the number of cuts for each node + Vec_Int_t * vNodeStarts; // the number of the starting cut of each node + Vec_Int_t * vCutPairs; // the pairs of parent cuts for each cut + // minimum delay mapping with the given cuts + Vec_Ptr_t * vCutsMax; + Vec_Int_t * vDelays; + Vec_Int_t * vDelays2; + int nDelayMin; + // statistics + int nCutsCur; + int nCutsAlloc; + int nCutsDealloc; + int nCutsPeak; + int nCutsTriv; + int nCutsFilter; + int nCutsLimit; + int nNodes; + int nNodesDag; + int nNodesNoCuts; + // runtime + int timeMerge; + int timeUnion; + int timeTruth; + int timeFilter; + int timeHash; + int timeMap; +}; + +// iterator through all the cuts of the list +#define Cut_ListForEachCut( pList, pCut ) \ + for ( pCut = pList; \ + pCut; \ + pCut = pCut->pNext ) +#define Cut_ListForEachCutStop( pList, pCut, pStop ) \ + for ( pCut = pList; \ + pCut != pStop; \ + pCut = pCut->pNext ) +#define Cut_ListForEachCutSafe( pList, pCut, pCut2 ) \ + for ( pCut = pList, \ + pCut2 = pCut? pCut->pNext: NULL; \ + pCut; \ + pCut = pCut2, \ + pCut2 = pCut? pCut->pNext: NULL ) + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// computes signature of the node +static inline unsigned Cut_NodeSign( int Node ) { return (1 << (Node % 31)); } +static inline int Cut_TruthWords( int nVarsMax ) { return nVarsMax <= 5 ? 1 : (1 << (nVarsMax - 5)); } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== cutCut.c ==========================================================*/ +extern Cut_Cut_t * Cut_CutAlloc( Cut_Man_t * p ); +extern void Cut_CutRecycle( Cut_Man_t * p, Cut_Cut_t * pCut ); +extern int Cut_CutCompare( Cut_Cut_t * pCut1, Cut_Cut_t * pCut2 ); +extern Cut_Cut_t * Cut_CutDupList( Cut_Man_t * p, Cut_Cut_t * pList ); +extern void Cut_CutRecycleList( Cut_Man_t * p, Cut_Cut_t * pList ); +extern Cut_Cut_t * Cut_CutMergeLists( Cut_Cut_t * pList1, Cut_Cut_t * pList2 ); +extern void Cut_CutNumberList( Cut_Cut_t * pList ); +extern Cut_Cut_t * Cut_CutCreateTriv( Cut_Man_t * p, int Node ); +extern void Cut_CutPrintMerge( Cut_Cut_t * pCut, Cut_Cut_t * pCut0, Cut_Cut_t * pCut1 ); +/*=== cutMerge.c ==========================================================*/ +extern Cut_Cut_t * Cut_CutMergeTwo( Cut_Man_t * p, Cut_Cut_t * pCut0, Cut_Cut_t * pCut1 ); +/*=== cutNode.c ==========================================================*/ +extern void Cut_NodeDoComputeCuts( Cut_Man_t * p, Cut_List_t * pSuper, int Node, int fCompl0, int fCompl1, Cut_Cut_t * pList0, Cut_Cut_t * pList1, int fTriv, int TreeCode ); +extern int Cut_CutListVerify( Cut_Cut_t * pList ); +/*=== cutTable.c ==========================================================*/ +extern Cut_HashTable_t * Cut_TableStart( int Size ); +extern void Cut_TableStop( Cut_HashTable_t * pTable ); +extern int Cut_TableLookup( Cut_HashTable_t * pTable, Cut_Cut_t * pCut, int fStore ); +extern void Cut_TableClear( Cut_HashTable_t * pTable ); +extern int Cut_TableReadTime( Cut_HashTable_t * pTable ); +/*=== cutTruth.c ==========================================================*/ +extern void Cut_TruthComputeOld( Cut_Cut_t * pCut, Cut_Cut_t * pCut0, Cut_Cut_t * pCut1, int fCompl0, int fCompl1 ); +extern void Cut_TruthCompute( Cut_Man_t * p, Cut_Cut_t * pCut, Cut_Cut_t * pCut0, Cut_Cut_t * pCut1, int fCompl0, int fCompl1 ); + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/opt/cut/cutList.h b/abc_with_bb_support/src/opt/cut/cutList.h new file mode 100644 index 000000000..877c4e931 --- /dev/null +++ b/abc_with_bb_support/src/opt/cut/cutList.h @@ -0,0 +1,207 @@ +/**CFile**************************************************************** + + FileName [cutList.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Implementation of layered listed list of cuts.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cutList.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __CUT_LIST_H__ +#define __CUT_LIST_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Cut_ListStruct_t_ Cut_List_t; +struct Cut_ListStruct_t_ +{ + Cut_Cut_t * pHead[CUT_SIZE_MAX+1]; + Cut_Cut_t ** ppTail[CUT_SIZE_MAX+1]; +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Start the cut list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Cut_ListStart( Cut_List_t * p ) +{ + int i; + for ( i = 1; i <= CUT_SIZE_MAX; i++ ) + { + p->pHead[i] = 0; + p->ppTail[i] = &p->pHead[i]; + } +} + +/**Function************************************************************* + + Synopsis [Adds one cut to the cut list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Cut_ListAdd( Cut_List_t * p, Cut_Cut_t * pCut ) +{ + assert( pCut->nLeaves > 0 && pCut->nLeaves <= CUT_SIZE_MAX ); + *p->ppTail[pCut->nLeaves] = pCut; + p->ppTail[pCut->nLeaves] = &pCut->pNext; +} + +/**Function************************************************************* + + Synopsis [Adds one cut to the cut list while preserving order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Cut_ListAdd2( Cut_List_t * p, Cut_Cut_t * pCut ) +{ + extern int Cut_CutCompare( Cut_Cut_t * pCut1, Cut_Cut_t * pCut2 ); + Cut_Cut_t * pTemp, ** ppSpot; + assert( pCut->nLeaves > 0 && pCut->nLeaves <= CUT_SIZE_MAX ); + if ( p->pHead[pCut->nLeaves] != NULL ) + { + ppSpot = &p->pHead[pCut->nLeaves]; + for ( pTemp = p->pHead[pCut->nLeaves]; pTemp; pTemp = pTemp->pNext ) + { + if ( Cut_CutCompare(pCut, pTemp) < 0 ) + { + *ppSpot = pCut; + pCut->pNext = pTemp; + return; + } + else + ppSpot = &pTemp->pNext; + } + } + *p->ppTail[pCut->nLeaves] = pCut; + p->ppTail[pCut->nLeaves] = &pCut->pNext; +} + +/**Function************************************************************* + + Synopsis [Derive the super list from the linked list of cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Cut_ListDerive( Cut_List_t * p, Cut_Cut_t * pList ) +{ + Cut_Cut_t * pPrev; + int nLeaves; + Cut_ListStart( p ); + while ( pList != NULL ) + { + nLeaves = pList->nLeaves; + p->pHead[nLeaves] = pList; + for ( pPrev = pList, pList = pList->pNext; pList; pPrev = pList, pList = pList->pNext ) + if ( nLeaves < (int)pList->nLeaves ) + break; + p->ppTail[nLeaves] = &pPrev->pNext; + pPrev->pNext = NULL; + } +} + +/**Function************************************************************* + + Synopsis [Adds the second list to the first list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Cut_ListAddList( Cut_List_t * pOld, Cut_List_t * pNew ) +{ + int i; + for ( i = 1; i <= CUT_SIZE_MAX; i++ ) + { + if ( pNew->pHead[i] == NULL ) + continue; + *pOld->ppTail[i] = pNew->pHead[i]; + pOld->ppTail[i] = pNew->ppTail[i]; + } +} + +/**Function************************************************************* + + Synopsis [Returns the cut list linked into one sequence of cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Cut_Cut_t * Cut_ListFinish( Cut_List_t * p ) +{ + Cut_Cut_t * pHead = NULL, ** ppTail = &pHead; + int i; + for ( i = 1; i <= CUT_SIZE_MAX; i++ ) + { + if ( p->pHead[i] == NULL ) + continue; + *ppTail = p->pHead[i]; + ppTail = p->ppTail[i]; + } + *ppTail = NULL; + return pHead; +} + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/opt/cut/cutMan.c b/abc_with_bb_support/src/opt/cut/cutMan.c new file mode 100644 index 000000000..15d1f96ca --- /dev/null +++ b/abc_with_bb_support/src/opt/cut/cutMan.c @@ -0,0 +1,326 @@ +/**CFile**************************************************************** + + FileName [cutMan.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [K-feasible cut computation package.] + + Synopsis [Cut manager.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cutMan.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cutInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +extern void Npn_StartTruth8( uint8 uTruths[][32] ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the cut manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Man_t * Cut_ManStart( Cut_Params_t * pParams ) +{ + Cut_Man_t * p; + int clk = clock(); +// extern int nTruthDsd; +// nTruthDsd = 0; + assert( pParams->nVarsMax >= 3 && pParams->nVarsMax <= CUT_SIZE_MAX ); + p = ALLOC( Cut_Man_t, 1 ); + memset( p, 0, sizeof(Cut_Man_t) ); + // set and correct parameters + p->pParams = pParams; + // prepare storage for cuts + p->vCutsNew = Vec_PtrAlloc( pParams->nIdsMax ); + Vec_PtrFill( p->vCutsNew, pParams->nIdsMax, NULL ); + // prepare storage for sequential cuts + if ( pParams->fSeq ) + { + p->pParams->fFilter = 1; + p->vCutsOld = Vec_PtrAlloc( pParams->nIdsMax ); + Vec_PtrFill( p->vCutsOld, pParams->nIdsMax, NULL ); + p->vCutsTemp = Vec_PtrAlloc( pParams->nCutSet ); + Vec_PtrFill( p->vCutsTemp, pParams->nCutSet, NULL ); + if ( pParams->fTruth && pParams->nVarsMax > 5 ) + { + pParams->fTruth = 0; + printf( "Skipping computation of truth tables for sequential cuts with more than 5 inputs.\n" ); + } + } + // entry size + p->EntrySize = sizeof(Cut_Cut_t) + pParams->nVarsMax * sizeof(int); + if ( pParams->fTruth ) + { + if ( pParams->nVarsMax > 14 ) + { + pParams->fTruth = 0; + printf( "Skipping computation of truth table for more than %d inputs.\n", 14 ); + } + else + { + p->nTruthWords = Cut_TruthWords( pParams->nVarsMax ); + p->EntrySize += p->nTruthWords * sizeof(unsigned); + } + p->puTemp[0] = ALLOC( unsigned, 4 * p->nTruthWords ); + p->puTemp[1] = p->puTemp[0] + p->nTruthWords; + p->puTemp[2] = p->puTemp[1] + p->nTruthWords; + p->puTemp[3] = p->puTemp[2] + p->nTruthWords; + } + // enable cut computation recording + if ( pParams->fRecord ) + { + p->vNodeCuts = Vec_IntStart( pParams->nIdsMax ); + p->vNodeStarts = Vec_IntStart( pParams->nIdsMax ); + p->vCutPairs = Vec_IntAlloc( 0 ); + } + // allocate storage for delays + if ( pParams->fMap && !p->pParams->fSeq ) + { + p->vDelays = Vec_IntStart( pParams->nIdsMax ); + p->vDelays2 = Vec_IntStart( pParams->nIdsMax ); + p->vCutsMax = Vec_PtrStart( pParams->nIdsMax ); + } + // memory for cuts + p->pMmCuts = Extra_MmFixedStart( p->EntrySize ); + p->vTemp = Vec_PtrAlloc( 100 ); + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the cut manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_ManStop( Cut_Man_t * p ) +{ + Cut_Cut_t * pCut; + int i; +// extern int nTruthDsd; +// printf( "Decomposable cuts = %d.\n", nTruthDsd ); + + Vec_PtrForEachEntry( p->vCutsNew, pCut, i ) + if ( pCut != NULL ) + { + int k = 0; + } + if ( p->vCutsNew ) Vec_PtrFree( p->vCutsNew ); + if ( p->vCutsOld ) Vec_PtrFree( p->vCutsOld ); + if ( p->vCutsTemp ) Vec_PtrFree( p->vCutsTemp ); + if ( p->vFanCounts ) Vec_IntFree( p->vFanCounts ); + if ( p->vTemp ) Vec_PtrFree( p->vTemp ); + + if ( p->vCutsMax ) Vec_PtrFree( p->vCutsMax ); + if ( p->vDelays ) Vec_IntFree( p->vDelays ); + if ( p->vDelays2 ) Vec_IntFree( p->vDelays2 ); + if ( p->vNodeCuts ) Vec_IntFree( p->vNodeCuts ); + if ( p->vNodeStarts ) Vec_IntFree( p->vNodeStarts ); + if ( p->vCutPairs ) Vec_IntFree( p->vCutPairs ); + if ( p->puTemp[0] ) free( p->puTemp[0] ); + + Extra_MmFixedStop( p->pMmCuts ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_ManPrintStats( Cut_Man_t * p ) +{ + if ( p->pReady ) + { + Cut_CutRecycle( p, p->pReady ); + p->pReady = NULL; + } + printf( "Cut computation statistics:\n" ); + printf( "Current cuts = %8d. (Trivial = %d.)\n", p->nCutsCur-p->nCutsTriv, p->nCutsTriv ); + printf( "Peak cuts = %8d.\n", p->nCutsPeak ); + printf( "Total allocated = %8d.\n", p->nCutsAlloc ); + printf( "Total deallocated = %8d.\n", p->nCutsDealloc ); + printf( "Cuts filtered = %8d.\n", p->nCutsFilter ); + printf( "Nodes saturated = %8d. (Max cuts = %d.)\n", p->nCutsLimit, p->pParams->nKeepMax ); + printf( "Cuts per node = %8.1f\n", ((float)(p->nCutsCur-p->nCutsTriv))/p->nNodes ); + printf( "The cut size = %8d bytes.\n", p->EntrySize ); + printf( "Peak memory = %8.2f Mb.\n", (float)p->nCutsPeak * p->EntrySize / (1<<20) ); + printf( "Total nodes = %8d.\n", p->nNodes ); + if ( p->pParams->fDag || p->pParams->fTree ) + { + printf( "DAG nodes = %8d.\n", p->nNodesDag ); + printf( "Tree nodes = %8d.\n", p->nNodes - p->nNodesDag ); + } + printf( "Nodes w/o cuts = %8d.\n", p->nNodesNoCuts ); + if ( p->pParams->fMap && !p->pParams->fSeq ) + printf( "Mapping delay = %8d.\n", p->nDelayMin ); + + PRT( "Merge ", p->timeMerge ); + PRT( "Union ", p->timeUnion ); + PRT( "Filter", p->timeFilter ); + PRT( "Truth ", p->timeTruth ); + PRT( "Map ", p->timeMap ); +// printf( "Nodes = %d. Multi = %d. Cuts = %d. Multi = %d.\n", +// p->nNodes, p->nNodesMulti, p->nCutsCur-p->nCutsTriv, p->nCutsMulti ); +// printf( "Count0 = %d. Count1 = %d. Count2 = %d.\n\n", p->Count0, p->Count1, p->Count2 ); +} + + +/**Function************************************************************* + + Synopsis [Prints some interesting stats.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_ManPrintStatsToFile( Cut_Man_t * p, char * pFileName, int TimeTotal ) +{ + FILE * pTable; + pTable = fopen( "cut_stats.txt", "a+" ); + fprintf( pTable, "%-20s ", pFileName ); + fprintf( pTable, "%8d ", p->nNodes ); + fprintf( pTable, "%6.1f ", ((float)(p->nCutsCur))/p->nNodes ); + fprintf( pTable, "%6.2f ", ((float)(100.0 * p->nCutsLimit))/p->nNodes ); + fprintf( pTable, "%6.2f ", (float)p->nCutsPeak * p->EntrySize / (1<<20) ); + fprintf( pTable, "%6.2f ", (float)(TimeTotal)/(float)(CLOCKS_PER_SEC) ); + fprintf( pTable, "\n" ); + fclose( pTable ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_ManSetFanoutCounts( Cut_Man_t * p, Vec_Int_t * vFanCounts ) +{ + p->vFanCounts = vFanCounts; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_ManSetNodeAttrs( Cut_Man_t * p, Vec_Int_t * vNodeAttrs ) +{ + p->vNodeAttrs = vNodeAttrs; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Cut_ManReadVarsMax( Cut_Man_t * p ) +{ + return p->pParams->nVarsMax; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Params_t * Cut_ManReadParams( Cut_Man_t * p ) +{ + return p->pParams; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Cut_ManReadNodeAttrs( Cut_Man_t * p ) +{ + return p->vNodeAttrs; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_ManIncrementDagNodes( Cut_Man_t * p ) +{ + p->nNodesDag++; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/cut/cutMerge.c b/abc_with_bb_support/src/opt/cut/cutMerge.c new file mode 100644 index 000000000..f4b17ae01 --- /dev/null +++ b/abc_with_bb_support/src/opt/cut/cutMerge.c @@ -0,0 +1,657 @@ +/**CFile**************************************************************** + + FileName [cutMerge.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [K-feasible cut computation package.] + + Synopsis [Procedure to merge two cuts.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cutMerge.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cutInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Merges two cuts.] + + Description [This procedure works.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Cut_t * Cut_CutMergeTwo2( Cut_Man_t * p, Cut_Cut_t * pCut0, Cut_Cut_t * pCut1 ) +{ + static int M[7][3] = {{0},{0},{0},{0},{0},{0},{0}}; + Cut_Cut_t * pRes; + int * pRow; + int nLeaves0, nLeaves1, Limit; + int i, k, Count, nNodes; + + assert( pCut0->nLeaves >= pCut1->nLeaves ); + + // the case of the largest cut sizes + Limit = p->pParams->nVarsMax; + nLeaves0 = pCut0->nLeaves; + nLeaves1 = pCut1->nLeaves; + if ( nLeaves0 == Limit && nLeaves1 == Limit ) + { + for ( i = 0; i < nLeaves0; i++ ) + if ( pCut0->pLeaves[i] != pCut1->pLeaves[i] ) + return NULL; + pRes = Cut_CutAlloc( p ); + for ( i = 0; i < nLeaves0; i++ ) + pRes->pLeaves[i] = pCut0->pLeaves[i]; + pRes->nLeaves = nLeaves0; + return pRes; + } + // the case when one of the cuts is the largest + if ( nLeaves0 == Limit ) + { + for ( i = 0; i < nLeaves1; i++ ) + { + for ( k = nLeaves0 - 1; k >= 0; k-- ) + if ( pCut0->pLeaves[k] == pCut1->pLeaves[i] ) + break; + if ( k == -1 ) // did not find + return NULL; + } + pRes = Cut_CutAlloc( p ); + for ( i = 0; i < nLeaves0; i++ ) + pRes->pLeaves[i] = pCut0->pLeaves[i]; + pRes->nLeaves = nLeaves0; + return pRes; + } + // other cases + nNodes = nLeaves0; + for ( i = 0; i < nLeaves1; i++ ) + { + for ( k = nLeaves0 - 1; k >= 0; k-- ) + { + if ( pCut0->pLeaves[k] > pCut1->pLeaves[i] ) + continue; + if ( pCut0->pLeaves[k] < pCut1->pLeaves[i] ) + { + pRow = M[k+1]; + if ( pRow[0] == 0 ) + pRow[0] = pCut1->pLeaves[i], pRow[1] = 0; + else if ( pRow[1] == 0 ) + pRow[1] = pCut1->pLeaves[i], pRow[2] = 0; + else if ( pRow[2] == 0 ) + pRow[2] = pCut1->pLeaves[i]; + else + assert( 0 ); + if ( ++nNodes > Limit ) + { + for ( i = 0; i <= nLeaves0; i++ ) + M[i][0] = 0; + return NULL; + } + } + break; + } + if ( k == -1 ) + { + pRow = M[0]; + if ( pRow[0] == 0 ) + pRow[0] = pCut1->pLeaves[i], pRow[1] = 0; + else if ( pRow[1] == 0 ) + pRow[1] = pCut1->pLeaves[i], pRow[2] = 0; + else if ( pRow[2] == 0 ) + pRow[2] = pCut1->pLeaves[i]; + else + assert( 0 ); + if ( ++nNodes > Limit ) + { + for ( i = 0; i <= nLeaves0; i++ ) + M[i][0] = 0; + return NULL; + } + continue; + } + } + + pRes = Cut_CutAlloc( p ); + for ( Count = 0, i = 0; i <= nLeaves0; i++ ) + { + if ( i > 0 ) + pRes->pLeaves[Count++] = pCut0->pLeaves[i-1]; + pRow = M[i]; + if ( pRow[0] ) + { + pRes->pLeaves[Count++] = pRow[0]; + if ( pRow[1] ) + { + pRes->pLeaves[Count++] = pRow[1]; + if ( pRow[2] ) + pRes->pLeaves[Count++] = pRow[2]; + } + pRow[0] = 0; + } + } + assert( Count == nNodes ); + pRes->nLeaves = nNodes; + return pRes; +} + +/**Function************************************************************* + + Synopsis [Merges two cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Cut_t * Cut_CutMergeTwo( Cut_Man_t * p, Cut_Cut_t * pCut0, Cut_Cut_t * pCut1 ) +{ + Cut_Cut_t * pRes; + int * pLeaves; + int Limit, nLeaves0, nLeaves1; + int i, k, c; + + assert( pCut0->nLeaves >= pCut1->nLeaves ); + + // consider two cuts + nLeaves0 = pCut0->nLeaves; + nLeaves1 = pCut1->nLeaves; + + // the case of the largest cut sizes + Limit = p->pParams->nVarsMax; + if ( nLeaves0 == Limit && nLeaves1 == Limit ) + { + for ( i = 0; i < nLeaves0; i++ ) + if ( pCut0->pLeaves[i] != pCut1->pLeaves[i] ) + return NULL; + pRes = Cut_CutAlloc( p ); + for ( i = 0; i < nLeaves0; i++ ) + pRes->pLeaves[i] = pCut0->pLeaves[i]; + pRes->nLeaves = pCut0->nLeaves; + return pRes; + } + // the case when one of the cuts is the largest + if ( nLeaves0 == Limit ) + { + for ( i = 0; i < nLeaves1; i++ ) + { + for ( k = nLeaves0 - 1; k >= 0; k-- ) + if ( pCut0->pLeaves[k] == pCut1->pLeaves[i] ) + break; + if ( k == -1 ) // did not find + return NULL; + } + pRes = Cut_CutAlloc( p ); + for ( i = 0; i < nLeaves0; i++ ) + pRes->pLeaves[i] = pCut0->pLeaves[i]; + pRes->nLeaves = pCut0->nLeaves; + return pRes; + } + + // prepare the cut + if ( p->pReady == NULL ) + p->pReady = Cut_CutAlloc( p ); + pLeaves = p->pReady->pLeaves; + + // compare two cuts with different numbers + i = k = 0; + for ( c = 0; c < Limit; c++ ) + { + if ( k == nLeaves1 ) + { + if ( i == nLeaves0 ) + { + p->pReady->nLeaves = c; + pRes = p->pReady; p->pReady = NULL; + return pRes; + } + pLeaves[c] = pCut0->pLeaves[i++]; + continue; + } + if ( i == nLeaves0 ) + { + if ( k == nLeaves1 ) + { + p->pReady->nLeaves = c; + pRes = p->pReady; p->pReady = NULL; + return pRes; + } + pLeaves[c] = pCut1->pLeaves[k++]; + continue; + } + if ( pCut0->pLeaves[i] < pCut1->pLeaves[k] ) + { + pLeaves[c] = pCut0->pLeaves[i++]; + continue; + } + if ( pCut0->pLeaves[i] > pCut1->pLeaves[k] ) + { + pLeaves[c] = pCut1->pLeaves[k++]; + continue; + } + pLeaves[c] = pCut0->pLeaves[i++]; + k++; + } + if ( i < nLeaves0 || k < nLeaves1 ) + return NULL; + p->pReady->nLeaves = c; + pRes = p->pReady; p->pReady = NULL; + return pRes; +} + + +/**Function************************************************************* + + Synopsis [Merges two cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Cut_t * Cut_CutMergeTwo3( Cut_Man_t * p, Cut_Cut_t * pCut0, Cut_Cut_t * pCut1 ) +{ + Cut_Cut_t * pRes; + int * pLeaves; + int Limit, nLeaves0, nLeaves1; + int i, k, c; + + assert( pCut0->nLeaves >= pCut1->nLeaves ); + + // prepare the cut + if ( p->pReady == NULL ) + p->pReady = Cut_CutAlloc( p ); + pLeaves = p->pReady->pLeaves; + + // consider two cuts + Limit = p->pParams->nVarsMax; + nLeaves0 = pCut0->nLeaves; + nLeaves1 = pCut1->nLeaves; + if ( nLeaves0 == Limit ) + { // the case when one of the cuts is the largest + if ( nLeaves1 == Limit ) + { // the case when both cuts are the largest + for ( i = 0; i < nLeaves0; i++ ) + { + pLeaves[i] = pCut0->pLeaves[i]; + if ( pLeaves[i] != pCut1->pLeaves[i] ) + return NULL; + } + } + else + { + for ( i = k = 0; i < nLeaves0; i++ ) + { + pLeaves[i] = pCut0->pLeaves[i]; + if ( k == (int)nLeaves1 ) + continue; + if ( pLeaves[i] < pCut1->pLeaves[k] ) + continue; + if ( pLeaves[i] == pCut1->pLeaves[k++] ) + continue; + return NULL; + } + if ( k < nLeaves1 ) + return NULL; + } + p->pReady->nLeaves = nLeaves0; + pRes = p->pReady; p->pReady = NULL; + return pRes; + } + + // compare two cuts with different numbers + i = k = 0; + for ( c = 0; c < Limit; c++ ) + { + if ( k == nLeaves1 ) + { + if ( i == nLeaves0 ) + { + p->pReady->nLeaves = c; + pRes = p->pReady; p->pReady = NULL; + return pRes; + } + pLeaves[c] = pCut0->pLeaves[i++]; + continue; + } + if ( i == nLeaves0 ) + { + if ( k == nLeaves1 ) + { + p->pReady->nLeaves = c; + pRes = p->pReady; p->pReady = NULL; + return pRes; + } + pLeaves[c] = pCut1->pLeaves[k++]; + continue; + } + if ( pCut0->pLeaves[i] < pCut1->pLeaves[k] ) + { + pLeaves[c] = pCut0->pLeaves[i++]; + continue; + } + if ( pCut0->pLeaves[i] > pCut1->pLeaves[k] ) + { + pLeaves[c] = pCut1->pLeaves[k++]; + continue; + } + pLeaves[c] = pCut0->pLeaves[i++]; + k++; + } + if ( i < nLeaves0 || k < nLeaves1 ) + return NULL; + p->pReady->nLeaves = c; + pRes = p->pReady; p->pReady = NULL; + return pRes; +} + +/**Function************************************************************* + + Synopsis [Merges two cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Cut_t * Cut_CutMergeTwo4( Cut_Man_t * p, Cut_Cut_t * pCut0, Cut_Cut_t * pCut1 ) +{ + Cut_Cut_t * pRes; + int * pLeaves; + int i, k, min, NodeTemp, Limit, nTotal; + + assert( pCut0->nLeaves >= pCut1->nLeaves ); + + // prepare the cut + if ( p->pReady == NULL ) + p->pReady = Cut_CutAlloc( p ); + pLeaves = p->pReady->pLeaves; + + // consider two cuts + Limit = p->pParams->nVarsMax; + if ( pCut0->nLeaves == (unsigned)Limit ) + { // the case when one of the cuts is the largest + if ( pCut1->nLeaves == (unsigned)Limit ) + { // the case when both cuts are the largest + for ( i = 0; i < (int)pCut0->nLeaves; i++ ) + { + pLeaves[i] = pCut0->pLeaves[i]; + if ( pLeaves[i] != pCut1->pLeaves[i] ) + return NULL; + } + } + else + { + for ( i = k = 0; i < (int)pCut0->nLeaves; i++ ) + { + pLeaves[i] = pCut0->pLeaves[i]; + if ( k == (int)pCut1->nLeaves ) + continue; + if ( pLeaves[i] < pCut1->pLeaves[k] ) + continue; + if ( pLeaves[i] == pCut1->pLeaves[k++] ) + continue; + return NULL; + } + if ( k < (int)pCut1->nLeaves ) + return NULL; + } + p->pReady->nLeaves = pCut0->nLeaves; + pRes = p->pReady; p->pReady = NULL; + return pRes; + } + + // count the number of unique entries in pCut1 + nTotal = pCut0->nLeaves; + for ( i = 0; i < (int)pCut1->nLeaves; i++ ) + { + // try to find this entry among the leaves of pCut0 + for ( k = 0; k < (int)pCut0->nLeaves; k++ ) + if ( pCut1->pLeaves[i] == pCut0->pLeaves[k] ) + break; + if ( k < (int)pCut0->nLeaves ) // found + continue; + // we found a new entry to add + if ( nTotal == Limit ) + return NULL; + pLeaves[nTotal++] = pCut1->pLeaves[i]; + } + // we know that the feasible cut exists + + // add the starting entries + for ( k = 0; k < (int)pCut0->nLeaves; k++ ) + pLeaves[k] = pCut0->pLeaves[k]; + + // selection-sort the entries + for ( i = 0; i < nTotal - 1; i++ ) + { + min = i; + for ( k = i+1; k < nTotal; k++ ) + if ( pLeaves[k] < pLeaves[min] ) + min = k; + NodeTemp = pLeaves[i]; + pLeaves[i] = pLeaves[min]; + pLeaves[min] = NodeTemp; + } + p->pReady->nLeaves = nTotal; + pRes = p->pReady; p->pReady = NULL; + return pRes; +} + +/**Function************************************************************* + + Synopsis [Merges two cuts.] + + Description [This procedure works.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Cut_t * Cut_CutMergeTwo5( Cut_Man_t * p, Cut_Cut_t * pCut0, Cut_Cut_t * pCut1 ) +{ + static int M[7][3] = {{0},{0},{0},{0},{0},{0},{0}}; + Cut_Cut_t * pRes; + int * pRow; + unsigned uSign0, uSign1; + int i, k, nNodes, Count; + unsigned Limit = p->pParams->nVarsMax; + + assert( pCut0->nLeaves >= pCut1->nLeaves ); + + // the case of the largest cut sizes + if ( pCut0->nLeaves == Limit && pCut1->nLeaves == Limit ) + { + for ( i = 0; i < (int)pCut0->nLeaves; i++ ) + if ( pCut0->pLeaves[i] != pCut1->pLeaves[i] ) + return NULL; + pRes = Cut_CutAlloc( p ); + for ( i = 0; i < (int)pCut0->nLeaves; i++ ) + pRes->pLeaves[i] = pCut0->pLeaves[i]; + pRes->nLeaves = pCut0->nLeaves; + return pRes; + } + // the case when one of the cuts is the largest + if ( pCut0->nLeaves == Limit ) + { + if ( !p->pParams->fTruth ) + { + for ( i = 0; i < (int)pCut1->nLeaves; i++ ) + { + for ( k = pCut0->nLeaves - 1; k >= 0; k-- ) + if ( pCut0->pLeaves[k] == pCut1->pLeaves[i] ) + break; + if ( k == -1 ) // did not find + return NULL; + } + pRes = Cut_CutAlloc( p ); + } + else + { + uSign1 = 0; + for ( i = 0; i < (int)pCut1->nLeaves; i++ ) + { + for ( k = pCut0->nLeaves - 1; k >= 0; k-- ) + if ( pCut0->pLeaves[k] == pCut1->pLeaves[i] ) + { + uSign1 |= (1 << i); + break; + } + if ( k == -1 ) // did not find + return NULL; + } + pRes = Cut_CutAlloc( p ); + pRes->Num1 = uSign1; + } + for ( i = 0; i < (int)pCut0->nLeaves; i++ ) + pRes->pLeaves[i] = pCut0->pLeaves[i]; + pRes->nLeaves = pCut0->nLeaves; + return pRes; + } + // other cases + nNodes = pCut0->nLeaves; + for ( i = 0; i < (int)pCut1->nLeaves; i++ ) + { + for ( k = pCut0->nLeaves - 1; k >= 0; k-- ) + { + if ( pCut0->pLeaves[k] > pCut1->pLeaves[i] ) + continue; + if ( pCut0->pLeaves[k] < pCut1->pLeaves[i] ) + { + pRow = M[k+1]; + if ( pRow[0] == 0 ) + pRow[0] = pCut1->pLeaves[i], pRow[1] = 0; + else if ( pRow[1] == 0 ) + pRow[1] = pCut1->pLeaves[i], pRow[2] = 0; + else if ( pRow[2] == 0 ) + pRow[2] = pCut1->pLeaves[i]; + else + assert( 0 ); + if ( ++nNodes > (int)Limit ) + { + for ( i = 0; i <= (int)pCut0->nLeaves; i++ ) + M[i][0] = 0; + return NULL; + } + } + break; + } + if ( k == -1 ) + { + pRow = M[0]; + if ( pRow[0] == 0 ) + pRow[0] = pCut1->pLeaves[i], pRow[1] = 0; + else if ( pRow[1] == 0 ) + pRow[1] = pCut1->pLeaves[i], pRow[2] = 0; + else if ( pRow[2] == 0 ) + pRow[2] = pCut1->pLeaves[i]; + else + assert( 0 ); + if ( ++nNodes > (int)Limit ) + { + for ( i = 0; i <= (int)pCut0->nLeaves; i++ ) + M[i][0] = 0; + return NULL; + } + continue; + } + } + + pRes = Cut_CutAlloc( p ); + if ( !p->pParams->fTruth ) + { + for ( Count = 0, i = 0; i <= (int)pCut0->nLeaves; i++ ) + { + if ( i > 0 ) + pRes->pLeaves[Count++] = pCut0->pLeaves[i-1]; + pRow = M[i]; + if ( pRow[0] ) + { + pRes->pLeaves[Count++] = pRow[0]; + if ( pRow[1] ) + { + pRes->pLeaves[Count++] = pRow[1]; + if ( pRow[2] ) + pRes->pLeaves[Count++] = pRow[2]; + } + pRow[0] = 0; + } + } + assert( Count == nNodes ); + pRes->nLeaves = nNodes; +/* + // make sure that the cut is correct + { + for ( i = 1; i < (int)pRes->nLeaves; i++ ) + if ( pRes->pLeaves[i-1] >= pRes->pLeaves[i] ) + { + int v = 0; + } + } +*/ + return pRes; + } + + uSign0 = uSign1 = 0; + for ( Count = 0, i = 0; i <= (int)pCut0->nLeaves; i++ ) + { + if ( i > 0 ) + { + uSign0 |= (1 << Count); + pRes->pLeaves[Count++] = pCut1->pLeaves[i-1]; + } + pRow = M[i]; + if ( pRow[0] ) + { + uSign1 |= (1 << Count); + pRes->pLeaves[Count++] = pRow[0]; + if ( pRow[1] ) + { + uSign1 |= (1 << Count); + pRes->pLeaves[Count++] = pRow[1]; + if ( pRow[2] ) + { + uSign1 |= (1 << Count); + pRes->pLeaves[Count++] = pRow[2]; + } + } + pRow[0] = 0; + } + } + assert( Count == nNodes ); + pRes->nLeaves = nNodes; + pRes->Num1 = uSign1; + pRes->Num0 = uSign0; + return pRes; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/cut/cutNode.c b/abc_with_bb_support/src/opt/cut/cutNode.c new file mode 100644 index 000000000..cf69dde62 --- /dev/null +++ b/abc_with_bb_support/src/opt/cut/cutNode.c @@ -0,0 +1,992 @@ +/**CFile**************************************************************** + + FileName [cutNode.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [K-feasible cut computation package.] + + Synopsis [Procedures to compute cuts for a node.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cutNode.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cutInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Cut_NodeMapping( Cut_Man_t * p, Cut_Cut_t * pCuts, int Node, int Node0, int Node1 ); +static int Cut_NodeMapping2( Cut_Man_t * p, Cut_Cut_t * pCuts, int Node, int Node0, int Node1 ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Returns 1 if pDom is contained in pCut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Cut_CutCheckDominance( Cut_Cut_t * pDom, Cut_Cut_t * pCut ) +{ + int i, k; + for ( i = 0; i < (int)pDom->nLeaves; i++ ) + { + for ( k = 0; k < (int)pCut->nLeaves; k++ ) + if ( pDom->pLeaves[i] == pCut->pLeaves[k] ) + break; + if ( k == (int)pCut->nLeaves ) // node i in pDom is not contained in pCut + return 0; + } + // every node in pDom is contained in pCut + return 1; +} + +/**Function************************************************************* + + Synopsis [Filters cuts using dominance.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Cut_CutFilter( Cut_Man_t * p, Cut_Cut_t * pList ) +{ + Cut_Cut_t * pListR, ** ppListR = &pListR; + Cut_Cut_t * pCut, * pCut2, * pDom, * pPrev; + // save the first cut + *ppListR = pList, ppListR = &pList->pNext; + // try to filter out other cuts + pPrev = pList; + Cut_ListForEachCutSafe( pList->pNext, pCut, pCut2 ) + { + assert( pCut->nLeaves > 1 ); + // go through all the previous cuts up to pCut + Cut_ListForEachCutStop( pList->pNext, pDom, pCut ) + { + if ( pDom->nLeaves > pCut->nLeaves ) + continue; + if ( (pDom->uSign & pCut->uSign) != pDom->uSign ) + continue; + if ( Cut_CutCheckDominance( pDom, pCut ) ) + break; + } + if ( pDom != pCut ) // pDom is contained in pCut - recycle pCut + { + // make sure cuts are connected after removing + pPrev->pNext = pCut->pNext; + // recycle the cut + Cut_CutRecycle( p, pCut ); + } + else // pDom is NOT contained in pCut - save pCut + { + *ppListR = pCut, ppListR = &pCut->pNext; + pPrev = pCut; + } + } + *ppListR = NULL; +} + +/**Function************************************************************* + + Synopsis [Checks equality of one cut.] + + Description [Returns 1 if the cut is removed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Cut_CutFilterOneEqual( Cut_Man_t * p, Cut_List_t * pSuperList, Cut_Cut_t * pCut ) +{ + Cut_Cut_t * pTemp; + Cut_ListForEachCut( pSuperList->pHead[pCut->nLeaves], pTemp ) + { + // skip the non-contained cuts + if ( pCut->uSign != pTemp->uSign ) + continue; + // check containment seriously + if ( Cut_CutCheckDominance( pTemp, pCut ) ) + { + p->nCutsFilter++; + Cut_CutRecycle( p, pCut ); + return 1; + } + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Checks containment for one cut.] + + Description [Returns 1 if the cut is removed.] + + SideEffects [May remove other cuts in the set.] + + SeeAlso [] + +***********************************************************************/ +static inline int Cut_CutFilterOne( Cut_Man_t * p, Cut_List_t * pSuperList, Cut_Cut_t * pCut ) +{ + Cut_Cut_t * pTemp, * pTemp2, ** ppTail; + int a; + + // check if this cut is filtered out by smaller cuts + for ( a = 2; a <= (int)pCut->nLeaves; a++ ) + { + Cut_ListForEachCut( pSuperList->pHead[a], pTemp ) + { + // skip the non-contained cuts + if ( (pTemp->uSign & pCut->uSign) != pTemp->uSign ) + continue; + // check containment seriously + if ( Cut_CutCheckDominance( pTemp, pCut ) ) + { + p->nCutsFilter++; + Cut_CutRecycle( p, pCut ); + return 1; + } + } + } + + // filter out other cuts using this one + for ( a = pCut->nLeaves + 1; a <= (int)pCut->nVarsMax; a++ ) + { + ppTail = pSuperList->pHead + a; + Cut_ListForEachCutSafe( pSuperList->pHead[a], pTemp, pTemp2 ) + { + // skip the non-contained cuts + if ( (pTemp->uSign & pCut->uSign) != pCut->uSign ) + { + ppTail = &pTemp->pNext; + continue; + } + // check containment seriously + if ( Cut_CutCheckDominance( pCut, pTemp ) ) + { + p->nCutsFilter++; + p->nNodeCuts--; + // move the head + if ( pSuperList->pHead[a] == pTemp ) + pSuperList->pHead[a] = pTemp->pNext; + // move the tail + if ( pSuperList->ppTail[a] == &pTemp->pNext ) + pSuperList->ppTail[a] = ppTail; + // skip the given cut in the list + *ppTail = pTemp->pNext; + // recycle pTemp + Cut_CutRecycle( p, pTemp ); + } + else + ppTail = &pTemp->pNext; + } + assert( ppTail == pSuperList->ppTail[a] ); + assert( *ppTail == NULL ); + } + + return 0; +} + +/**Function************************************************************* + + Synopsis [Checks if the cut is local and can be removed.] + + Description [Returns 1 if the cut is removed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Cut_CutFilterGlobal( Cut_Man_t * p, Cut_Cut_t * pCut ) +{ + int a; + if ( pCut->nLeaves == 1 ) + return 0; + for ( a = 0; a < (int)pCut->nLeaves; a++ ) + if ( Vec_IntEntry( p->vNodeAttrs, pCut->pLeaves[a] ) ) // global + return 0; + // there is no global nodes, the cut should be removed + p->nCutsFilter++; + Cut_CutRecycle( p, pCut ); + return 1; +} + + +/**Function************************************************************* + + Synopsis [Checks containment for one cut.] + + Description [Returns 1 if the cut is removed.] + + SideEffects [May remove other cuts in the set.] + + SeeAlso [] + +***********************************************************************/ +static inline int Cut_CutFilterOld( Cut_Man_t * p, Cut_Cut_t * pList, Cut_Cut_t * pCut ) +{ + Cut_Cut_t * pPrev, * pTemp, * pTemp2, ** ppTail; + + // check if this cut is filtered out by smaller cuts + pPrev = NULL; + Cut_ListForEachCut( pList, pTemp ) + { + if ( pTemp->nLeaves > pCut->nLeaves ) + break; + pPrev = pTemp; + // skip the non-contained cuts + if ( (pTemp->uSign & pCut->uSign) != pTemp->uSign ) + continue; + // check containment seriously + if ( Cut_CutCheckDominance( pTemp, pCut ) ) + { + p->nCutsFilter++; + Cut_CutRecycle( p, pCut ); + return 1; + } + } + assert( pPrev->pNext == pTemp ); + + // filter out other cuts using this one + ppTail = &pPrev->pNext; + Cut_ListForEachCutSafe( pTemp, pTemp, pTemp2 ) + { + // skip the non-contained cuts + if ( (pTemp->uSign & pCut->uSign) != pCut->uSign ) + { + ppTail = &pTemp->pNext; + continue; + } + // check containment seriously + if ( Cut_CutCheckDominance( pCut, pTemp ) ) + { + p->nCutsFilter++; + p->nNodeCuts--; + // skip the given cut in the list + *ppTail = pTemp->pNext; + // recycle pTemp + Cut_CutRecycle( p, pTemp ); + } + else + ppTail = &pTemp->pNext; + } + assert( *ppTail == NULL ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Processes two cuts.] + + Description [Returns 1 if the limit has been reached.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Cut_CutProcessTwo( Cut_Man_t * p, Cut_Cut_t * pCut0, Cut_Cut_t * pCut1, Cut_List_t * pSuperList ) +{ + Cut_Cut_t * pCut; + // merge the cuts + if ( pCut0->nLeaves >= pCut1->nLeaves ) + pCut = Cut_CutMergeTwo( p, pCut0, pCut1 ); + else + pCut = Cut_CutMergeTwo( p, pCut1, pCut0 ); + if ( pCut == NULL ) + return 0; + assert( p->pParams->fSeq || pCut->nLeaves > 1 ); + // set the signature + pCut->uSign = pCut0->uSign | pCut1->uSign; + if ( p->pParams->fRecord ) + pCut->Num0 = pCut0->Num0, pCut->Num1 = pCut1->Num0; + // check containment + if ( p->pParams->fFilter ) + { + if ( Cut_CutFilterOne(p, pSuperList, pCut) ) +// if ( Cut_CutFilterOneEqual(p, pSuperList, pCut) ) + return 0; + if ( p->pParams->fSeq ) + { + if ( p->pCompareOld && Cut_CutFilterOld(p, p->pCompareOld, pCut) ) + return 0; + if ( p->pCompareNew && Cut_CutFilterOld(p, p->pCompareNew, pCut) ) + return 0; + } + } + + if ( p->pParams->fGlobal ) + { + assert( p->vNodeAttrs != NULL ); + if ( Cut_CutFilterGlobal( p, pCut ) ) + return 0; + } + + // compute the truth table + if ( p->pParams->fTruth ) + Cut_TruthCompute( p, pCut, pCut0, pCut1, p->fCompl0, p->fCompl1 ); + // add to the list + Cut_ListAdd( pSuperList, pCut ); + // return status (0 if okay; 1 if exceeded the limit) + return ++p->nNodeCuts == p->pParams->nKeepMax; +} + +/**Function************************************************************* + + Synopsis [Computes the cuts by merging cuts at two nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Cut_t * Cut_NodeComputeCuts( Cut_Man_t * p, int Node, int Node0, int Node1, int fCompl0, int fCompl1, int fTriv, int TreeCode ) +{ + Cut_List_t Super, * pSuper = &Super; + Cut_Cut_t * pList, * pCut; + int clk; + // start the number of cuts at the node + p->nNodes++; + p->nNodeCuts = 0; + // prepare information for recording + if ( p->pParams->fRecord ) + { + Cut_CutNumberList( Cut_NodeReadCutsNew(p, Node0) ); + Cut_CutNumberList( Cut_NodeReadCutsNew(p, Node1) ); + } + // compute the cuts +clk = clock(); + Cut_ListStart( pSuper ); + Cut_NodeDoComputeCuts( p, pSuper, Node, fCompl0, fCompl1, Cut_NodeReadCutsNew(p, Node0), Cut_NodeReadCutsNew(p, Node1), fTriv, TreeCode ); + pList = Cut_ListFinish( pSuper ); +p->timeMerge += clock() - clk; + // verify the result of cut computation +// Cut_CutListVerify( pList ); + // performing the recording + if ( p->pParams->fRecord ) + { + Vec_IntWriteEntry( p->vNodeStarts, Node, Vec_IntSize(p->vCutPairs) ); + Cut_ListForEachCut( pList, pCut ) + Vec_IntPush( p->vCutPairs, ((pCut->Num1 << 16) | pCut->Num0) ); + Vec_IntWriteEntry( p->vNodeCuts, Node, Vec_IntSize(p->vCutPairs) - Vec_IntEntry(p->vNodeStarts, Node) ); + } + // check if the node is over the list + if ( p->nNodeCuts == p->pParams->nKeepMax ) + p->nCutsLimit++; + // set the list at the node + Vec_PtrFillExtra( p->vCutsNew, Node + 1, NULL ); + assert( Cut_NodeReadCutsNew(p, Node) == NULL ); + ///// +// pList->pNext = NULL; + ///// + Cut_NodeWriteCutsNew( p, Node, pList ); + // filter the cuts +//clk = clock(); +// if ( p->pParams->fFilter ) +// Cut_CutFilter( p, pList0 ); +//p->timeFilter += clock() - clk; + // perform mapping of this node with these cuts +clk = clock(); + if ( p->pParams->fMap && !p->pParams->fSeq ) + { +// int Delay1, Delay2; +// Delay1 = Cut_NodeMapping( p, pList, Node, Node0, Node1 ); +// Delay2 = Cut_NodeMapping2( p, pList, Node, Node0, Node1 ); +// assert( Delay1 >= Delay2 ); + Cut_NodeMapping( p, pList, Node, Node0, Node1 ); + } +p->timeMap += clock() - clk; + return pList; +} + +/**Function************************************************************* + + Synopsis [Returns optimum delay mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Cut_NodeMapping2( Cut_Man_t * p, Cut_Cut_t * pCuts, int Node, int Node0, int Node1 ) +{ + Cut_Cut_t * pCut; + int DelayMin, DelayCur, i; + if ( pCuts == NULL ) + p->nDelayMin = -1; + if ( p->nDelayMin == -1 ) + return -1; + DelayMin = 1000000; + Cut_ListForEachCut( pCuts, pCut ) + { + if ( pCut->nLeaves == 1 ) + continue; + DelayCur = 0; + for ( i = 0; i < (int)pCut->nLeaves; i++ ) + if ( DelayCur < Vec_IntEntry(p->vDelays, pCut->pLeaves[i]) ) + DelayCur = Vec_IntEntry(p->vDelays, pCut->pLeaves[i]); + if ( DelayMin > DelayCur ) + DelayMin = DelayCur; + } + if ( DelayMin == 1000000 ) + { + p->nDelayMin = -1; + return -1; + } + DelayMin++; + Vec_IntWriteEntry( p->vDelays, Node, DelayMin ); + if ( p->nDelayMin < DelayMin ) + p->nDelayMin = DelayMin; + return DelayMin; +} + +/**Function************************************************************* + + Synopsis [Returns optimum delay mapping using the largest cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Cut_NodeMapping( Cut_Man_t * p, Cut_Cut_t * pCuts, int Node, int Node0, int Node1 ) +{ + Cut_Cut_t * pCut0, * pCut1, * pCut; + int Delay0, Delay1, Delay; + // get the fanin cuts + Delay0 = Vec_IntEntry( p->vDelays2, Node0 ); + Delay1 = Vec_IntEntry( p->vDelays2, Node1 ); + pCut0 = (Delay0 == 0) ? Vec_PtrEntry( p->vCutsNew, Node0 ) : Vec_PtrEntry( p->vCutsMax, Node0 ); + pCut1 = (Delay1 == 0) ? Vec_PtrEntry( p->vCutsNew, Node1 ) : Vec_PtrEntry( p->vCutsMax, Node1 ); + if ( Delay0 == Delay1 ) + Delay = (Delay0 == 0) ? Delay0 + 1: Delay0; + else if ( Delay0 > Delay1 ) + { + Delay = Delay0; + pCut1 = Vec_PtrEntry( p->vCutsNew, Node1 ); + assert( pCut1->nLeaves == 1 ); + } + else // if ( Delay0 < Delay1 ) + { + Delay = Delay1; + pCut0 = Vec_PtrEntry( p->vCutsNew, Node0 ); + assert( pCut0->nLeaves == 1 ); + } + // merge the cuts + if ( pCut0->nLeaves < pCut1->nLeaves ) + pCut = Cut_CutMergeTwo( p, pCut1, pCut0 ); + else + pCut = Cut_CutMergeTwo( p, pCut0, pCut1 ); + if ( pCut == NULL ) + { + Delay++; + pCut = Cut_CutAlloc( p ); + pCut->nLeaves = 2; + pCut->pLeaves[0] = Node0 < Node1 ? Node0 : Node1; + pCut->pLeaves[1] = Node0 < Node1 ? Node1 : Node0; + } + assert( Delay > 0 ); + Vec_IntWriteEntry( p->vDelays2, Node, Delay ); + Vec_PtrWriteEntry( p->vCutsMax, Node, pCut ); + if ( p->nDelayMin < Delay ) + p->nDelayMin = Delay; + return Delay; +} + +/**Function************************************************************* + + Synopsis [Computes area after mapping.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Cut_ManMappingArea_rec( Cut_Man_t * p, int Node ) +{ + Cut_Cut_t * pCut; + int i, Counter; + if ( p->vCutsMax == NULL ) + return 0; + pCut = Vec_PtrEntry( p->vCutsMax, Node ); + if ( pCut == NULL || pCut->nLeaves == 1 ) + return 0; + Counter = 0; + for ( i = 0; i < (int)pCut->nLeaves; i++ ) + Counter += Cut_ManMappingArea_rec( p, pCut->pLeaves[i] ); + Vec_PtrWriteEntry( p->vCutsMax, Node, NULL ); + return 1 + Counter; +} + + +/**Function************************************************************* + + Synopsis [Computes the cuts by merging cuts at two nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_NodeDoComputeCuts( Cut_Man_t * p, Cut_List_t * pSuper, int Node, int fCompl0, int fCompl1, Cut_Cut_t * pList0, Cut_Cut_t * pList1, int fTriv, int TreeCode ) +{ + Cut_Cut_t * pStop0, * pStop1, * pTemp0, * pTemp1, * pStore0, * pStore1; + int i, nCutsOld, Limit; + // start with the elementary cut + if ( fTriv ) + { +// printf( "Creating trivial cut %d.\n", Node ); + pTemp0 = Cut_CutCreateTriv( p, Node ); + Cut_ListAdd( pSuper, pTemp0 ); + p->nNodeCuts++; + } + // get the cut lists of children + if ( pList0 == NULL || pList1 == NULL || (p->pParams->fLocal && TreeCode) ) + return; + + // remember the old number of cuts + nCutsOld = p->nCutsCur; + Limit = p->pParams->nVarsMax; + // get the simultation bit of the node + p->fSimul = (fCompl0 ^ pList0->fSimul) & (fCompl1 ^ pList1->fSimul); + // set temporary variables + p->fCompl0 = fCompl0; + p->fCompl1 = fCompl1; + // if tree cuts are computed, make sure only the unit cuts propagate over the DAG nodes + if ( TreeCode & 1 ) + { + assert( pList0->nLeaves == 1 ); + pStore0 = pList0->pNext; + pList0->pNext = NULL; + } + if ( TreeCode & 2 ) + { + assert( pList1->nLeaves == 1 ); + pStore1 = pList1->pNext; + pList1->pNext = NULL; + } + // find the point in the list where the max-var cuts begin + Cut_ListForEachCut( pList0, pStop0 ) + if ( pStop0->nLeaves == (unsigned)Limit ) + break; + Cut_ListForEachCut( pList1, pStop1 ) + if ( pStop1->nLeaves == (unsigned)Limit ) + break; + + // small by small + Cut_ListForEachCutStop( pList0, pTemp0, pStop0 ) + Cut_ListForEachCutStop( pList1, pTemp1, pStop1 ) + { + if ( Cut_CutProcessTwo( p, pTemp0, pTemp1, pSuper ) ) + goto Quits; + } + // small by large + Cut_ListForEachCutStop( pList0, pTemp0, pStop0 ) + Cut_ListForEachCut( pStop1, pTemp1 ) + { + if ( (pTemp0->uSign & pTemp1->uSign) != pTemp0->uSign ) + continue; + if ( Cut_CutProcessTwo( p, pTemp0, pTemp1, pSuper ) ) + goto Quits; + } + // small by large + Cut_ListForEachCutStop( pList1, pTemp1, pStop1 ) + Cut_ListForEachCut( pStop0, pTemp0 ) + { + if ( (pTemp0->uSign & pTemp1->uSign) != pTemp1->uSign ) + continue; + if ( Cut_CutProcessTwo( p, pTemp0, pTemp1, pSuper ) ) + goto Quits; + } + // large by large + Cut_ListForEachCut( pStop0, pTemp0 ) + Cut_ListForEachCut( pStop1, pTemp1 ) + { + assert( pTemp0->nLeaves == (unsigned)Limit && pTemp1->nLeaves == (unsigned)Limit ); + if ( pTemp0->uSign != pTemp1->uSign ) + continue; + for ( i = 0; i < Limit; i++ ) + if ( pTemp0->pLeaves[i] != pTemp1->pLeaves[i] ) + break; + if ( i < Limit ) + continue; + if ( Cut_CutProcessTwo( p, pTemp0, pTemp1, pSuper ) ) + goto Quits; + } + if ( p->nNodeCuts == 0 ) + p->nNodesNoCuts++; +Quits: + if ( TreeCode & 1 ) + pList0->pNext = pStore0; + if ( TreeCode & 2 ) + pList1->pNext = pStore1; +} + +/**Function************************************************************* + + Synopsis [Computes the cuts by unioning cuts at a choice node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Cut_t * Cut_NodeUnionCuts( Cut_Man_t * p, Vec_Int_t * vNodes ) +{ + Cut_List_t Super, * pSuper = &Super; + Cut_Cut_t * pList, * pListStart, * pCut, * pCut2, * pTop; + int i, k, Node, Root, Limit = p->pParams->nVarsMax; + int clk = clock(); + + // start the new list + Cut_ListStart( pSuper ); + + // remember the root node to save the resulting cuts + Root = Vec_IntEntry( vNodes, 0 ); + p->nNodeCuts = 1; + + // collect small cuts first + Vec_PtrClear( p->vTemp ); + Vec_IntForEachEntry( vNodes, Node, i ) + { + // get the cuts of this node + pList = Cut_NodeReadCutsNew( p, Node ); + Cut_NodeWriteCutsNew( p, Node, NULL ); + assert( pList ); + // remember the starting point + pListStart = pList->pNext; + pList->pNext = NULL; + // save or recycle the elementary cut + if ( i == 0 ) + Cut_ListAdd( pSuper, pList ), pTop = pList; + else + Cut_CutRecycle( p, pList ); + // save all the cuts that are smaller than the limit + Cut_ListForEachCutSafe( pListStart, pCut, pCut2 ) + { + if ( pCut->nLeaves == (unsigned)Limit ) + { + Vec_PtrPush( p->vTemp, pCut ); + break; + } + // check containment + if ( p->pParams->fFilter && Cut_CutFilterOne( p, pSuper, pCut ) ) + continue; + // set the complemented bit by comparing the first cut with the current cut + pCut->fCompl = pTop->fSimul ^ pCut->fSimul; + pListStart = pCut->pNext; + pCut->pNext = NULL; + // add to the list + Cut_ListAdd( pSuper, pCut ); + if ( ++p->nNodeCuts == p->pParams->nKeepMax ) + { + // recycle the rest of the cuts of this node + Cut_ListForEachCutSafe( pListStart, pCut, pCut2 ) + Cut_CutRecycle( p, pCut ); + // recycle all cuts of other nodes + Vec_IntForEachEntryStart( vNodes, Node, k, i+1 ) + Cut_NodeFreeCuts( p, Node ); + // recycle the saved cuts of other nodes + Vec_PtrForEachEntry( p->vTemp, pList, k ) + Cut_ListForEachCutSafe( pList, pCut, pCut2 ) + Cut_CutRecycle( p, pCut ); + goto finish; + } + } + } + // collect larger cuts next + Vec_PtrForEachEntry( p->vTemp, pList, i ) + { + Cut_ListForEachCutSafe( pList, pCut, pCut2 ) + { + // check containment + if ( p->pParams->fFilter && Cut_CutFilterOne( p, pSuper, pCut ) ) + continue; + // set the complemented bit + pCut->fCompl = pTop->fSimul ^ pCut->fSimul; + pListStart = pCut->pNext; + pCut->pNext = NULL; + // add to the list + Cut_ListAdd( pSuper, pCut ); + if ( ++p->nNodeCuts == p->pParams->nKeepMax ) + { + // recycle the rest of the cuts + Cut_ListForEachCutSafe( pListStart, pCut, pCut2 ) + Cut_CutRecycle( p, pCut ); + // recycle the saved cuts of other nodes + Vec_PtrForEachEntryStart( p->vTemp, pList, k, i+1 ) + Cut_ListForEachCutSafe( pList, pCut, pCut2 ) + Cut_CutRecycle( p, pCut ); + goto finish; + } + } + } +finish : + // set the cuts at the node + assert( Cut_NodeReadCutsNew(p, Root) == NULL ); + pList = Cut_ListFinish( pSuper ); + Cut_NodeWriteCutsNew( p, Root, pList ); +p->timeUnion += clock() - clk; + // filter the cuts +//clk = clock(); +// if ( p->pParams->fFilter ) +// Cut_CutFilter( p, pList ); +//p->timeFilter += clock() - clk; + p->nNodes -= vNodes->nSize - 1; + return pList; +} + +/**Function************************************************************* + + Synopsis [Computes the cuts by unioning cuts at a choice node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Cut_t * Cut_NodeUnionCutsSeq( Cut_Man_t * p, Vec_Int_t * vNodes, int CutSetNum, int fFirst ) +{ + Cut_List_t Super, * pSuper = &Super; + Cut_Cut_t * pList, * pListStart, * pCut, * pCut2, * pTop; + int i, k, Node, Root, Limit = p->pParams->nVarsMax; + int clk = clock(); + + // start the new list + Cut_ListStart( pSuper ); + + // remember the root node to save the resulting cuts + Root = Vec_IntEntry( vNodes, 0 ); + p->nNodeCuts = 1; + + // store the original lists for comparison + p->pCompareOld = Cut_NodeReadCutsOld( p, Root ); + p->pCompareNew = (CutSetNum >= 0)? Cut_NodeReadCutsNew( p, Root ) : NULL; + + // get the topmost cut + pTop = NULL; + if ( (pTop = Cut_NodeReadCutsOld( p, Root )) == NULL ) + pTop = Cut_NodeReadCutsNew( p, Root ); + assert( pTop != NULL ); + + // collect small cuts first + Vec_PtrClear( p->vTemp ); + Vec_IntForEachEntry( vNodes, Node, i ) + { + // get the cuts of this node + if ( i == 0 && CutSetNum >= 0 ) + { + pList = Cut_NodeReadCutsTemp( p, CutSetNum ); + Cut_NodeWriteCutsTemp( p, CutSetNum, NULL ); + } + else + { + pList = Cut_NodeReadCutsNew( p, Node ); + Cut_NodeWriteCutsNew( p, Node, NULL ); + } + if ( pList == NULL ) + continue; + + // process the cuts + if ( fFirst ) + { + // remember the starting point + pListStart = pList->pNext; + pList->pNext = NULL; + // save or recycle the elementary cut + if ( i == 0 ) + Cut_ListAdd( pSuper, pList ); + else + Cut_CutRecycle( p, pList ); + } + else + pListStart = pList; + + // save all the cuts that are smaller than the limit + Cut_ListForEachCutSafe( pListStart, pCut, pCut2 ) + { + if ( pCut->nLeaves == (unsigned)Limit ) + { + Vec_PtrPush( p->vTemp, pCut ); + break; + } + // check containment +// if ( p->pParams->fFilter && Cut_CutFilterOne( p, pSuper, pCut ) ) +// continue; + if ( p->pParams->fFilter ) + { + if ( Cut_CutFilterOne(p, pSuper, pCut) ) + continue; + if ( p->pParams->fSeq ) + { + if ( p->pCompareOld && Cut_CutFilterOld(p, p->pCompareOld, pCut) ) + continue; + if ( p->pCompareNew && Cut_CutFilterOld(p, p->pCompareNew, pCut) ) + continue; + } + } + + // set the complemented bit by comparing the first cut with the current cut + pCut->fCompl = pTop->fSimul ^ pCut->fSimul; + pListStart = pCut->pNext; + pCut->pNext = NULL; + // add to the list + Cut_ListAdd( pSuper, pCut ); + if ( ++p->nNodeCuts == p->pParams->nKeepMax ) + { + // recycle the rest of the cuts of this node + Cut_ListForEachCutSafe( pListStart, pCut, pCut2 ) + Cut_CutRecycle( p, pCut ); + // recycle all cuts of other nodes + Vec_IntForEachEntryStart( vNodes, Node, k, i+1 ) + Cut_NodeFreeCuts( p, Node ); + // recycle the saved cuts of other nodes + Vec_PtrForEachEntry( p->vTemp, pList, k ) + Cut_ListForEachCutSafe( pList, pCut, pCut2 ) + Cut_CutRecycle( p, pCut ); + goto finish; + } + } + } + // collect larger cuts next + Vec_PtrForEachEntry( p->vTemp, pList, i ) + { + Cut_ListForEachCutSafe( pList, pCut, pCut2 ) + { + // check containment +// if ( p->pParams->fFilter && Cut_CutFilterOne( p, pSuper, pCut ) ) +// continue; + if ( p->pParams->fFilter ) + { + if ( Cut_CutFilterOne(p, pSuper, pCut) ) + continue; + if ( p->pParams->fSeq ) + { + if ( p->pCompareOld && Cut_CutFilterOld(p, p->pCompareOld, pCut) ) + continue; + if ( p->pCompareNew && Cut_CutFilterOld(p, p->pCompareNew, pCut) ) + continue; + } + } + + // set the complemented bit + pCut->fCompl = pTop->fSimul ^ pCut->fSimul; + pListStart = pCut->pNext; + pCut->pNext = NULL; + // add to the list + Cut_ListAdd( pSuper, pCut ); + if ( ++p->nNodeCuts == p->pParams->nKeepMax ) + { + // recycle the rest of the cuts + Cut_ListForEachCutSafe( pListStart, pCut, pCut2 ) + Cut_CutRecycle( p, pCut ); + // recycle the saved cuts of other nodes + Vec_PtrForEachEntryStart( p->vTemp, pList, k, i+1 ) + Cut_ListForEachCutSafe( pList, pCut, pCut2 ) + Cut_CutRecycle( p, pCut ); + goto finish; + } + } + } +finish : + // set the cuts at the node + pList = Cut_ListFinish( pSuper ); + + // set the lists at the node +// assert( Cut_NodeReadCutsNew(p, Root) == NULL ); +// Cut_NodeWriteCutsNew( p, Root, pList ); + if ( CutSetNum >= 0 ) + { + assert( Cut_NodeReadCutsTemp(p, CutSetNum) == NULL ); + Cut_NodeWriteCutsTemp( p, CutSetNum, pList ); + } + else + { + assert( Cut_NodeReadCutsNew(p, Root) == NULL ); + Cut_NodeWriteCutsNew( p, Root, pList ); + } + +p->timeUnion += clock() - clk; + // filter the cuts +//clk = clock(); +// if ( p->pParams->fFilter ) +// Cut_CutFilter( p, pList ); +//p->timeFilter += clock() - clk; +// if ( fFirst ) +// p->nNodes -= vNodes->nSize - 1; + return pList; +} + + +/**Function************************************************************* + + Synopsis [Verifies that the list contains only non-dominated cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Cut_CutListVerify( Cut_Cut_t * pList ) +{ + Cut_Cut_t * pCut, * pDom; + Cut_ListForEachCut( pList, pCut ) + { + Cut_ListForEachCutStop( pList, pDom, pCut ) + { + if ( Cut_CutCheckDominance( pDom, pCut ) ) + { + int x = 0; + printf( "******************* These are contained cuts:\n" ); + Cut_CutPrint( pDom, 1 ); + Cut_CutPrint( pDom, 1 ); + + return 0; + } + } + } + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/cut/cutOracle.c b/abc_with_bb_support/src/opt/cut/cutOracle.c new file mode 100644 index 000000000..6b0b20b1c --- /dev/null +++ b/abc_with_bb_support/src/opt/cut/cutOracle.c @@ -0,0 +1,428 @@ +/**CFile**************************************************************** + + FileName [cutOracle.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [K-feasible cut computation package.] + + Synopsis [Procedures to compute cuts for a node using the oracle.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cutOracle.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cutInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +struct Cut_OracleStruct_t_ +{ + // cut comptupatation parameters + Cut_Params_t * pParams; + Vec_Int_t * vFanCounts; + int fSimul; + // storage for cuts + Vec_Ptr_t * vCutsNew; + Vec_Ptr_t * vCuts0; + Vec_Ptr_t * vCuts1; + // oracle info + Vec_Int_t * vNodeCuts; + Vec_Int_t * vNodeStarts; + Vec_Int_t * vCutPairs; + // memory management + Extra_MmFixed_t * pMmCuts; + int EntrySize; + int nTruthWords; + // stats + int timeTotal; + int nCuts; + int nCutsTriv; +}; + +static Cut_Cut_t * Cut_CutStart( Cut_Oracle_t * p ); +static Cut_Cut_t * Cut_CutTriv( Cut_Oracle_t * p, int Node ); +static Cut_Cut_t * Cut_CutMerge( Cut_Oracle_t * p, Cut_Cut_t * pCut0, Cut_Cut_t * pCut1 ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the cut oracle.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Oracle_t * Cut_OracleStart( Cut_Man_t * pMan ) +{ + Cut_Oracle_t * p; + int clk = clock(); + + assert( pMan->pParams->nVarsMax >= 3 && pMan->pParams->nVarsMax <= CUT_SIZE_MAX ); + assert( pMan->pParams->fRecord ); + + p = ALLOC( Cut_Oracle_t, 1 ); + memset( p, 0, sizeof(Cut_Oracle_t) ); + + // set and correct parameters + p->pParams = pMan->pParams; + + // transfer the recording info + p->vNodeCuts = pMan->vNodeCuts; pMan->vNodeCuts = NULL; + p->vNodeStarts = pMan->vNodeStarts; pMan->vNodeStarts = NULL; + p->vCutPairs = pMan->vCutPairs; pMan->vCutPairs = NULL; + + // prepare storage for cuts + p->vCutsNew = Vec_PtrAlloc( p->pParams->nIdsMax ); + Vec_PtrFill( p->vCutsNew, p->pParams->nIdsMax, NULL ); + p->vCuts0 = Vec_PtrAlloc( 100 ); + p->vCuts1 = Vec_PtrAlloc( 100 ); + + // entry size + p->EntrySize = sizeof(Cut_Cut_t) + p->pParams->nVarsMax * sizeof(int); + if ( p->pParams->fTruth ) + { + if ( p->pParams->nVarsMax > 8 ) + { + p->pParams->fTruth = 0; + printf( "Skipping computation of truth table for more than 8 inputs.\n" ); + } + else + { + p->nTruthWords = Cut_TruthWords( p->pParams->nVarsMax ); + p->EntrySize += p->nTruthWords * sizeof(unsigned); + } + } + // memory for cuts + p->pMmCuts = Extra_MmFixedStart( p->EntrySize ); + return p; +} +/**Function************************************************************* + + Synopsis [Stop the cut oracle.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_OracleStop( Cut_Oracle_t * p ) +{ + Cut_Cut_t * pCut; + int i; + +// if ( p->pParams->fVerbose ) + { + printf( "Cut computation statistics with oracle:\n" ); + printf( "Current cuts = %8d. (Trivial = %d.)\n", p->nCuts-p->nCutsTriv, p->nCutsTriv ); + PRT( "Total time ", p->timeTotal ); + } + + Vec_PtrForEachEntry( p->vCutsNew, pCut, i ) + if ( pCut != NULL ) + { + int k = 0; + } + if ( p->vCuts0 ) Vec_PtrFree( p->vCuts0 ); + if ( p->vCuts1 ) Vec_PtrFree( p->vCuts1 ); + if ( p->vCutsNew ) Vec_PtrFree( p->vCutsNew ); + if ( p->vFanCounts ) Vec_IntFree( p->vFanCounts ); + + if ( p->vNodeCuts ) Vec_IntFree( p->vNodeCuts ); + if ( p->vNodeStarts ) Vec_IntFree( p->vNodeStarts ); + if ( p->vCutPairs ) Vec_IntFree( p->vCutPairs ); + + Extra_MmFixedStop( p->pMmCuts ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_OracleSetFanoutCounts( Cut_Oracle_t * p, Vec_Int_t * vFanCounts ) +{ + p->vFanCounts = vFanCounts; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Cut_OracleReadDrop( Cut_Oracle_t * p ) +{ + return p->pParams->fDrop; +} + +/**Function************************************************************* + + Synopsis [Sets the trivial cut for the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_OracleNodeSetTriv( Cut_Oracle_t * p, int Node ) +{ + assert( Vec_PtrEntry( p->vCutsNew, Node ) == NULL ); + Vec_PtrWriteEntry( p->vCutsNew, Node, Cut_CutTriv(p, Node) ); +} + + + +/**Function************************************************************* + + Synopsis [Allocates the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Cut_t * Cut_CutStart( Cut_Oracle_t * p ) +{ + Cut_Cut_t * pCut; + // cut allocation + pCut = (Cut_Cut_t *)Extra_MmFixedEntryFetch( p->pMmCuts ); + memset( pCut, 0, sizeof(Cut_Cut_t) ); + pCut->nVarsMax = p->pParams->nVarsMax; + pCut->fSimul = p->fSimul; + p->nCuts++; + return pCut; +} + +/**Function************************************************************* + + Synopsis [Creates the trivial cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Cut_t * Cut_CutTriv( Cut_Oracle_t * p, int Node ) +{ + Cut_Cut_t * pCut; + pCut = Cut_CutStart( p ); + pCut->nLeaves = 1; + pCut->pLeaves[0] = Node; + if ( p->pParams->fTruth ) + { + unsigned * pTruth = Cut_CutReadTruth(pCut); + int i; + for ( i = 0; i < p->nTruthWords; i++ ) + pTruth[i] = 0xAAAAAAAA; + } + p->nCutsTriv++; + return pCut; +} + +/**Function************************************************************* + + Synopsis [Merges two cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Cut_t * Cut_CutMerge( Cut_Oracle_t * p, Cut_Cut_t * pCut0, Cut_Cut_t * pCut1 ) +{ + Cut_Cut_t * pCut; + int Limit, i, k, c; + // create the leaves of the new cut + pCut = Cut_CutStart( p ); + Limit = p->pParams->nVarsMax; + for ( i = k = c = 0; c < Limit; c++ ) + { + if ( k == (int)pCut1->nLeaves ) + { + if ( i == (int)pCut0->nLeaves ) + { + pCut->nLeaves = c; + return pCut; + } + pCut->pLeaves[c] = pCut0->pLeaves[i++]; + continue; + } + if ( i == (int)pCut0->nLeaves ) + { + if ( k == (int)pCut1->nLeaves ) + { + pCut->nLeaves = c; + return pCut; + } + pCut->pLeaves[c] = pCut1->pLeaves[k++]; + continue; + } + if ( pCut0->pLeaves[i] < pCut1->pLeaves[k] ) + { + pCut->pLeaves[c] = pCut0->pLeaves[i++]; + continue; + } + if ( pCut0->pLeaves[i] > pCut1->pLeaves[k] ) + { + pCut->pLeaves[c] = pCut1->pLeaves[k++]; + continue; + } + pCut->pLeaves[c] = pCut0->pLeaves[i++]; + k++; + } + assert( i == (int)pCut0->nLeaves && k == (int)pCut1->nLeaves ); + pCut->nLeaves = c; + return pCut; +} + +/**Function************************************************************* + + Synopsis [Reconstruct the cuts of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_Cut_t * Cut_OracleComputeCuts( Cut_Oracle_t * p, int Node, int Node0, int Node1, int fCompl0, int fCompl1 ) +{ + Cut_Cut_t * pList = NULL, ** ppTail = &pList; + Cut_Cut_t * pCut, * pCut0, * pCut1, * pList0, * pList1; + int iCutStart, nCuts, i, Entry; + int clk = clock(); + + // get the cuts of the children + pList0 = Vec_PtrEntry( p->vCutsNew, Node0 ); + pList1 = Vec_PtrEntry( p->vCutsNew, Node1 ); + assert( pList0 && pList1 ); + + // get the complemented attribute of the cut + p->fSimul = (fCompl0 ^ pList0->fSimul) & (fCompl1 ^ pList1->fSimul); + + // collect the cuts + Vec_PtrClear( p->vCuts0 ); + Cut_ListForEachCut( pList0, pCut ) + Vec_PtrPush( p->vCuts0, pCut ); + Vec_PtrClear( p->vCuts1 ); + Cut_ListForEachCut( pList1, pCut ) + Vec_PtrPush( p->vCuts1, pCut ); + + // get the first and last cuts of this node + nCuts = Vec_IntEntry(p->vNodeCuts, Node); + iCutStart = Vec_IntEntry(p->vNodeStarts, Node); + + // create trivial cut + assert( Vec_IntEntry(p->vCutPairs, iCutStart) == 0 ); + pCut = Cut_CutTriv( p, Node ); + *ppTail = pCut; + ppTail = &pCut->pNext; + // create other cuts + for ( i = 1; i < nCuts; i++ ) + { + Entry = Vec_IntEntry( p->vCutPairs, iCutStart + i ); + pCut0 = Vec_PtrEntry( p->vCuts0, Entry & 0xFFFF ); + pCut1 = Vec_PtrEntry( p->vCuts1, Entry >> 16 ); + pCut = Cut_CutMerge( p, pCut0, pCut1 ); + *ppTail = pCut; + ppTail = &pCut->pNext; + // compute the truth table + if ( p->pParams->fTruth ) + Cut_TruthComputeOld( pCut, pCut0, pCut1, fCompl0, fCompl1 ); + } + *ppTail = NULL; + + // write the new cut + assert( Vec_PtrEntry( p->vCutsNew, Node ) == NULL ); + Vec_PtrWriteEntry( p->vCutsNew, Node, pList ); +p->timeTotal += clock() - clk; + return pList; +} + +/**Function************************************************************* + + Synopsis [Deallocates the cuts at the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_OracleFreeCuts( Cut_Oracle_t * p, int Node ) +{ + Cut_Cut_t * pList, * pCut, * pCut2; + pList = Vec_PtrEntry( p->vCutsNew, Node ); + if ( pList == NULL ) + return; + Cut_ListForEachCutSafe( pList, pCut, pCut2 ) + Extra_MmFixedEntryRecycle( p->pMmCuts, (char *)pCut ); + Vec_PtrWriteEntry( p->vCutsNew, Node, pList ); +} + +/**Function************************************************************* + + Synopsis [Consider dropping cuts if they are useless by now.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_OracleTryDroppingCuts( Cut_Oracle_t * p, int Node ) +{ + int nFanouts; + assert( p->vFanCounts ); + nFanouts = Vec_IntEntry( p->vFanCounts, Node ); + assert( nFanouts > 0 ); + if ( --nFanouts == 0 ) + Cut_OracleFreeCuts( p, Node ); + Vec_IntWriteEntry( p->vFanCounts, Node, nFanouts ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/cut/cutPre22.c b/abc_with_bb_support/src/opt/cut/cutPre22.c new file mode 100644 index 000000000..061714b9a --- /dev/null +++ b/abc_with_bb_support/src/opt/cut/cutPre22.c @@ -0,0 +1,988 @@ +/**CFile**************************************************************** + + FileName [cutPre22.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Precomputes truth tables for the 2x2 macro cell.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cutPre22.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cutInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define CUT_CELL_MVAR 9 + +typedef struct Cut_Cell_t_ Cut_Cell_t; +typedef struct Cut_CMan_t_ Cut_CMan_t; + +struct Cut_Cell_t_ +{ + Cut_Cell_t * pNext; // pointer to the next cell in the table + Cut_Cell_t * pNextVar; // pointer to the next cell of this support size + Cut_Cell_t * pParent; // pointer to the cell used to derive this one + int nUsed; // the number of times the cell is used + char Box[4]; // functions in the boxes + unsigned nVars : 4; // the number of variables + unsigned CrossBar0 : 4; // the variable set equal + unsigned CrossBar1 : 4; // the variable set equal + unsigned CrossBarPhase : 2; // the phase of the cross bar (0, 1, or 2) + unsigned CanonPhase : 18; // the canonical phase + char CanonPerm[CUT_CELL_MVAR+3]; // semicanonical permutation + short Store[2*CUT_CELL_MVAR]; // minterm counts in the cofactors + unsigned uTruth[1<<(CUT_CELL_MVAR-5)]; // the current truth table +}; + +struct Cut_CMan_t_ +{ + // storage for canonical cells + Extra_MmFixed_t * pMem; + st_table * tTable; + Cut_Cell_t * pSameVar[CUT_CELL_MVAR+1]; + // elementary truth tables + unsigned uInputs[CUT_CELL_MVAR][1<<(CUT_CELL_MVAR-5)]; + // temporary truth tables + unsigned uTemp1[22][1<<(CUT_CELL_MVAR-5)]; + unsigned uTemp2[22][1<<(CUT_CELL_MVAR-5)]; + unsigned uTemp3[22][1<<(CUT_CELL_MVAR-5)]; + unsigned uFinal[1<<(CUT_CELL_MVAR-5)]; + unsigned puAux[1<<(CUT_CELL_MVAR-5)]; + // statistical variables + int nTotal; + int nGood; + int nVarCounts[CUT_CELL_MVAR+1]; + int nSymGroups[CUT_CELL_MVAR+1]; + int nSymGroupsE[CUT_CELL_MVAR+1]; + int timeCanon; + int timeSupp; + int timeTable; + int nCellFound; + int nCellNotFound; +}; + +// NP-classes of functions of 3 variables (22) +static char * s_NP3[22] = { + " 0\n", // 00 const 0 // 0 vars + " 1\n", // 01 const 1 // 0 vars + "1 1\n", // 02 a // 1 vars + "11 1\n", // 03 ab // 2 vars + "11 0\n", // 04 (ab)' // 2 vars + "10 1\n01 1\n", // 05 a<+>b // 2 vars + "111 1\n", // 06 0s abc // 3 vars + "111 0\n", // 07 (abc)' // + "11- 1\n1-1 1\n", // 08 1p a(b+c) // + "11- 0\n1-1 0\n", // 09 (a(b+c))' // + "111 1\n100 1\n010 1\n001 1\n", // 10 2s a<+>b<+>c // + "10- 0\n1-0 0\n011 0\n", // 11 3p a<+>bc // + "101 1\n110 1\n", // 12 4p a(b<+>c) // + "101 0\n110 0\n", // 13 (a(b<+>c))' // + "11- 1\n1-1 1\n-11 1\n", // 14 5s ab+bc+ac // + "111 1\n000 1\n", // 15 6s abc+a'b'c' // + "111 0\n000 0\n", // 16 (abc+a'b'c')' // + "11- 1\n-11 1\n0-1 1\n", // 17 7 ab+bc+a'c // + "011 1\n101 1\n110 1\n", // 18 8s a'bc+ab'c+abc' // + "011 0\n101 0\n110 0\n", // 19 (a'bc+ab'c+abc')' // + "100 1\n-11 1\n", // 20 9p ab'c'+bc // + "100 0\n-11 0\n" // 21 (ab'c'+bc)' // +}; + +// NP-classes of functions of 3 variables (22) +static char * s_NP3Names[22] = { + " const 0 ", + " const 1 ", + " a ", + " ab ", + " (ab)' ", + " a<+>b ", + "0s abc ", + " (abc)' ", + "1p a(b+c) ", + " (a(b+c))' ", + "2s a<+>b<+>c ", + "3p a<+>bc ", + "4p a(b<+>c) ", + " (a(b<+>c))' ", + "5s ab+bc+ac ", + "6s abc+a'b'c' ", + " (abc+a'b'c')' ", + "7 ab+bc+a'c ", + "8s a'bc+ab'c+abc' ", + " (a'bc+ab'c+abc')' ", + "9p ab'c'+bc ", + " (ab'c'+bc)' " +}; + +// the number of variables in each function +static int s_NP3VarNums[22] = { 0, 0, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }; + +// NPN classes of functions of exactly 3 inputs (10) +static int s_NPNe3[10] = { 6, 8, 10, 11, 12, 14, 15, 17, 18, 20 }; + +// NPN classes of functions of exactly 3 inputs that are symmetric (5) +static int s_NPNe3s[10] = { 6, 10, 14, 15, 18 }; + +// NPN classes of functions of exactly 3 inputs (4) +static int s_NPNe3p[10] = { 8, 11, 12, 20 }; + +static Cut_CMan_t * Cut_CManStart(); +static void Cut_CManStop( Cut_CMan_t * p ); +static void Cut_CellTruthElem( unsigned * InA, unsigned * InB, unsigned * InC, unsigned * pOut, int nVars, int Type ); +static void Cut_CellCanonicize( Cut_CMan_t * p, Cut_Cell_t * pCell ); +static int Cut_CellTableLookup( Cut_CMan_t * p, Cut_Cell_t * pCell ); +static void Cut_CellSuppMin( Cut_Cell_t * pCell ); +static void Cut_CellCrossBar( Cut_Cell_t * pCell ); + + +static Cut_CMan_t * s_pCMan = NULL; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Start the precomputation manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_CellLoad() +{ + FILE * pFile; + char * pFileName = "cells22_daomap_iwls.txt"; + char pString[1000]; + Cut_CMan_t * p; + Cut_Cell_t * pCell; + int Length; //, i; + pFile = fopen( pFileName, "r" ); + if ( pFile == NULL ) + { + printf( "Cannot open file \"%s\".\n", pFileName ); + return; + } + // start the manager + p = Cut_CManStart(); + // load truth tables + while ( fgets(pString, 1000, pFile) ) + { + Length = strlen(pString); + pString[Length--] = 0; + if ( Length == 0 ) + continue; + // derive the cell + pCell = (Cut_Cell_t *)Extra_MmFixedEntryFetch( p->pMem ); + memset( pCell, 0, sizeof(Cut_Cell_t) ); + pCell->nVars = Extra_Base2Log(Length*4); + pCell->nUsed = 1; +// Extra_TruthCopy( pCell->uTruth, pTruth, nVars ); + Extra_ReadHexadecimal( pCell->uTruth, pString, pCell->nVars ); + Cut_CellSuppMin( pCell ); +/* + // set the elementary permutation + for ( i = 0; i < (int)pCell->nVars; i++ ) + pCell->CanonPerm[i] = i; + // canonicize + pCell->CanonPhase = Extra_TruthSemiCanonicize( pCell->uTruth, p->puAux, pCell->nVars, pCell->CanonPerm, pCell->Store ); +*/ + // add to the table + p->nTotal++; + +// Extra_PrintHexadecimal( stdout, pCell->uTruth, pCell->nVars ); printf( "\n" ); +// if ( p->nTotal == 500 ) +// break; + + if ( !Cut_CellTableLookup( p, pCell ) ) // new cell + p->nGood++; + } + printf( "Read %d cells from file \"%s\". Added %d cells to the table.\n", p->nTotal, pFileName, p->nGood ); + fclose( pFile ); +// return p; +} + +/**Function************************************************************* + + Synopsis [Precomputes truth tables for the 2x2 macro cell.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_CellPrecompute() +{ + Cut_CMan_t * p; + Cut_Cell_t * pCell, * pTemp; + int i1, i2, i3, i, j, k, c, clk = clock(), clk2 = clock(); + + p = Cut_CManStart(); + + // precompute truth tables + for ( i = 0; i < 22; i++ ) + Cut_CellTruthElem( p->uInputs[0], p->uInputs[1], p->uInputs[2], p->uTemp1[i], 9, i ); + for ( i = 0; i < 22; i++ ) + Cut_CellTruthElem( p->uInputs[3], p->uInputs[4], p->uInputs[5], p->uTemp2[i], 9, i ); + for ( i = 0; i < 22; i++ ) + Cut_CellTruthElem( p->uInputs[6], p->uInputs[7], p->uInputs[8], p->uTemp3[i], 9, i ); +/* + if ( k == 8 && ((i1 == 6 && i2 == 14 && i3 == 20) || (i1 == 20 && i2 == 6 && i3 == 14)) ) + { + Extra_PrintBinary( stdout, &pCell->CanonPhase, pCell->nVars+1 ); printf( " : " ); + for ( i = 0; i < pCell->nVars; i++ ) + printf( "%d=%d/%d ", pCell->CanonPerm[i], pCell->Store[2*i], pCell->Store[2*i+1] ); + Extra_PrintHexadecimal( stdout, pCell->uTruth, pCell->nVars ); + printf( "\n" ); + } +*/ +/* + // go through symmetric roots + for ( k = 0; k < 5; k++ ) + for ( i1 = 0; i1 < 22; i1++ ) + for ( i2 = i1; i2 < 22; i2++ ) + for ( i3 = i2; i3 < 22; i3++ ) + { + // derive the cell + pCell = (Cut_Cell_t *)Extra_MmFixedEntryFetch( p->pMem ); + memset( pCell, 0, sizeof(Cut_Cell_t) ); + pCell->nVars = 9; + pCell->Box[0] = s_NPNe3s[k]; + pCell->Box[1] = i1; + pCell->Box[2] = i2; + pCell->Box[3] = i3; + // fill in the truth table + Cut_CellTruthElem( p->uTemp1[i1], p->uTemp2[i2], p->uTemp3[i3], pCell->uTruth, 9, s_NPNe3s[k] ); + // canonicize + Cut_CellCanonicize( pCell ); + + // add to the table + p->nTotal++; + if ( Cut_CellTableLookup( p, pCell ) ) // already exists + Extra_MmFixedEntryRecycle( p->pMem, (char *)pCell ); + else + p->nGood++; + } + + // go through partially symmetric roots + for ( k = 0; k < 4; k++ ) + for ( i1 = 0; i1 < 22; i1++ ) + for ( i2 = 0; i2 < 22; i2++ ) + for ( i3 = i2; i3 < 22; i3++ ) + { + // derive the cell + pCell = (Cut_Cell_t *)Extra_MmFixedEntryFetch( p->pMem ); + memset( pCell, 0, sizeof(Cut_Cell_t) ); + pCell->nVars = 9; + pCell->Box[0] = s_NPNe3p[k]; + pCell->Box[1] = i1; + pCell->Box[2] = i2; + pCell->Box[3] = i3; + // fill in the truth table + Cut_CellTruthElem( p->uTemp1[i1], p->uTemp2[i2], p->uTemp3[i3], pCell->uTruth, 9, s_NPNe3p[k] ); + // canonicize + Cut_CellCanonicize( pCell ); + + // add to the table + p->nTotal++; + if ( Cut_CellTableLookup( p, pCell ) ) // already exists + Extra_MmFixedEntryRecycle( p->pMem, (char *)pCell ); + else + p->nGood++; + } + + // go through non-symmetric functions + for ( i1 = 0; i1 < 22; i1++ ) + for ( i2 = 0; i2 < 22; i2++ ) + for ( i3 = 0; i3 < 22; i3++ ) + { + // derive the cell + pCell = (Cut_Cell_t *)Extra_MmFixedEntryFetch( p->pMem ); + memset( pCell, 0, sizeof(Cut_Cell_t) ); + pCell->nVars = 9; + pCell->Box[0] = 17; + pCell->Box[1] = i1; + pCell->Box[2] = i2; + pCell->Box[3] = i3; + // fill in the truth table + Cut_CellTruthElem( p->uTemp1[i1], p->uTemp2[i2], p->uTemp3[i3], pCell->uTruth, 9, 17 ); + // canonicize + Cut_CellCanonicize( pCell ); + + // add to the table + p->nTotal++; + if ( Cut_CellTableLookup( p, pCell ) ) // already exists + Extra_MmFixedEntryRecycle( p->pMem, (char *)pCell ); + else + p->nGood++; + } +*/ + + // go through non-symmetric functions + for ( k = 0; k < 10; k++ ) + for ( i1 = 0; i1 < 22; i1++ ) + for ( i2 = 0; i2 < 22; i2++ ) + for ( i3 = 0; i3 < 22; i3++ ) + { + // derive the cell + pCell = (Cut_Cell_t *)Extra_MmFixedEntryFetch( p->pMem ); + memset( pCell, 0, sizeof(Cut_Cell_t) ); + pCell->nVars = 9; + pCell->Box[0] = s_NPNe3[k]; + pCell->Box[1] = i1; + pCell->Box[2] = i2; + pCell->Box[3] = i3; + // set the elementary permutation + for ( i = 0; i < (int)pCell->nVars; i++ ) + pCell->CanonPerm[i] = i; + // fill in the truth table + Cut_CellTruthElem( p->uTemp1[i1], p->uTemp2[i2], p->uTemp3[i3], pCell->uTruth, 9, s_NPNe3[k] ); + // minimize the support + Cut_CellSuppMin( pCell ); + + // canonicize + pCell->CanonPhase = Extra_TruthSemiCanonicize( pCell->uTruth, p->puAux, pCell->nVars, pCell->CanonPerm, pCell->Store ); + + // add to the table + p->nTotal++; + if ( Cut_CellTableLookup( p, pCell ) ) // already exists + Extra_MmFixedEntryRecycle( p->pMem, (char *)pCell ); + else + { + p->nGood++; + p->nVarCounts[pCell->nVars]++; + + if ( pCell->nVars ) + for ( i = 0; i < (int)pCell->nVars-1; i++ ) + { + if ( pCell->Store[2*i] != pCell->Store[2*(i+1)] ) // i and i+1 cannot be symmetric + continue; + // i and i+1 can be symmetric + // find the end of this group + for ( j = i+1; j < (int)pCell->nVars; j++ ) + if ( pCell->Store[2*i] != pCell->Store[2*j] ) + break; + + if ( pCell->Store[2*i] == pCell->Store[2*i+1] ) + p->nSymGroupsE[j-i]++; + else + p->nSymGroups[j-i]++; + i = j - 1; + } +/* + if ( pCell->nVars == 3 ) + { + Extra_PrintBinary( stdout, pCell->uTruth, 32 ); printf( "\n" ); + for ( i = 0; i < (int)pCell->nVars; i++ ) + printf( "%d=%d/%d ", pCell->CanonPerm[i], pCell->Store[2*i], pCell->Store[2*i+1] ); + printf( "\n" ); + } +*/ + } + } + + printf( "BASIC: Total = %d. Good = %d. Entry = %d. ", p->nTotal, p->nGood, sizeof(Cut_Cell_t) ); + PRT( "Time", clock() - clk ); + printf( "Cells: " ); + for ( i = 0; i <= 9; i++ ) + printf( "%d=%d ", i, p->nVarCounts[i] ); + printf( "\nDiffs: " ); + for ( i = 0; i <= 9; i++ ) + printf( "%d=%d ", i, p->nSymGroups[i] ); + printf( "\nEquals: " ); + for ( i = 0; i <= 9; i++ ) + printf( "%d=%d ", i, p->nSymGroupsE[i] ); + printf( "\n" ); + + // continue adding new cells using support + for ( k = CUT_CELL_MVAR; k > 3; k-- ) + { + for ( pTemp = p->pSameVar[k]; pTemp; pTemp = pTemp->pNextVar ) + for ( i1 = 0; i1 < k; i1++ ) + for ( i2 = i1+1; i2 < k; i2++ ) + for ( c = 0; c < 3; c++ ) + { + // derive the cell + pCell = (Cut_Cell_t *)Extra_MmFixedEntryFetch( p->pMem ); + memset( pCell, 0, sizeof(Cut_Cell_t) ); + pCell->nVars = pTemp->nVars; + pCell->pParent = pTemp; + // set the elementary permutation + for ( i = 0; i < (int)pCell->nVars; i++ ) + pCell->CanonPerm[i] = i; + // fill in the truth table + Extra_TruthCopy( pCell->uTruth, pTemp->uTruth, pTemp->nVars ); + // create the cross-bar + pCell->CrossBar0 = i1; + pCell->CrossBar1 = i2; + pCell->CrossBarPhase = c; + Cut_CellCrossBar( pCell ); + // minimize the support +//clk2 = clock(); + Cut_CellSuppMin( pCell ); +//p->timeSupp += clock() - clk2; + // canonicize +//clk2 = clock(); + pCell->CanonPhase = Extra_TruthSemiCanonicize( pCell->uTruth, p->puAux, pCell->nVars, pCell->CanonPerm, pCell->Store ); +//p->timeCanon += clock() - clk2; + + // add to the table +//clk2 = clock(); + p->nTotal++; + if ( Cut_CellTableLookup( p, pCell ) ) // already exists + Extra_MmFixedEntryRecycle( p->pMem, (char *)pCell ); + else + { + p->nGood++; + p->nVarCounts[pCell->nVars]++; + + for ( i = 0; i < (int)pCell->nVars-1; i++ ) + { + if ( pCell->Store[2*i] != pCell->Store[2*(i+1)] ) // i and i+1 cannot be symmetric + continue; + // i and i+1 can be symmetric + // find the end of this group + for ( j = i+1; j < (int)pCell->nVars; j++ ) + if ( pCell->Store[2*i] != pCell->Store[2*j] ) + break; + + if ( pCell->Store[2*i] == pCell->Store[2*i+1] ) + p->nSymGroupsE[j-i]++; + else + p->nSymGroups[j-i]++; + i = j - 1; + } +/* + if ( pCell->nVars == 3 ) + { + Extra_PrintBinary( stdout, pCell->uTruth, 32 ); printf( "\n" ); + for ( i = 0; i < (int)pCell->nVars; i++ ) + printf( "%d=%d/%d ", pCell->CanonPerm[i], pCell->Store[2*i], pCell->Store[2*i+1] ); + printf( "\n" ); + } +*/ + } +//p->timeTable += clock() - clk2; + } + + printf( "VAR %d: Total = %d. Good = %d. Entry = %d. ", k, p->nTotal, p->nGood, sizeof(Cut_Cell_t) ); + PRT( "Time", clock() - clk ); + printf( "Cells: " ); + for ( i = 0; i <= 9; i++ ) + printf( "%d=%d ", i, p->nVarCounts[i] ); + printf( "\nDiffs: " ); + for ( i = 0; i <= 9; i++ ) + printf( "%d=%d ", i, p->nSymGroups[i] ); + printf( "\nEquals: " ); + for ( i = 0; i <= 9; i++ ) + printf( "%d=%d ", i, p->nSymGroupsE[i] ); + printf( "\n" ); + } +// printf( "\n" ); + PRT( "Supp ", p->timeSupp ); + PRT( "Canon", p->timeCanon ); + PRT( "Table", p->timeTable ); +// Cut_CManStop( p ); +} + +/**Function************************************************************* + + Synopsis [Check the table.] + + Description [Returns 1 if such a truth table already exists.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Cut_CellTableLookup( Cut_CMan_t * p, Cut_Cell_t * pCell ) +{ + Cut_Cell_t ** pSlot, * pTemp; + unsigned Hash; + Hash = Extra_TruthHash( pCell->uTruth, Extra_TruthWordNum( pCell->nVars ) ); + if ( !st_find_or_add( p->tTable, (char *)Hash, (char ***)&pSlot ) ) + *pSlot = NULL; + for ( pTemp = *pSlot; pTemp; pTemp = pTemp->pNext ) + { + if ( pTemp->nVars != pCell->nVars ) + continue; + if ( Extra_TruthIsEqual(pTemp->uTruth, pCell->uTruth, pCell->nVars) ) + return 1; + } + // the entry is new + pCell->pNext = *pSlot; + *pSlot = pCell; + // add it to the variable support list + pCell->pNextVar = p->pSameVar[pCell->nVars]; + p->pSameVar[pCell->nVars] = pCell; + return 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_CellSuppMin( Cut_Cell_t * pCell ) +{ + static unsigned uTemp[1<<(CUT_CELL_MVAR-5)]; + unsigned * pIn, * pOut, * pTemp; + int i, k, Counter, Temp; + + // go backward through the support variables and remove redundant + for ( k = pCell->nVars - 1; k >= 0; k-- ) + if ( !Extra_TruthVarInSupport(pCell->uTruth, pCell->nVars, k) ) + { + // shift all the variables above this one + Counter = 0; + pIn = pCell->uTruth; pOut = uTemp; + for ( i = k; i < (int)pCell->nVars - 1; i++ ) + { + Extra_TruthSwapAdjacentVars( pOut, pIn, pCell->nVars, i ); + pTemp = pIn; pIn = pOut; pOut = pTemp; + // swap the support vars + Temp = pCell->CanonPerm[i]; + pCell->CanonPerm[i] = pCell->CanonPerm[i+1]; + pCell->CanonPerm[i+1] = Temp; + Counter++; + } + // return the function back into its place + if ( Counter & 1 ) + Extra_TruthCopy( pOut, pIn, pCell->nVars ); + // remove one variable + pCell->nVars--; +// Extra_PrintBinary( stdout, pCell->uTruth, (1<nVars) ); printf( "\n" ); + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_CellCrossBar( Cut_Cell_t * pCell ) +{ + static unsigned uTemp0[1<<(CUT_CELL_MVAR-5)]; + static unsigned uTemp1[1<<(CUT_CELL_MVAR-5)]; + Extra_TruthCopy( uTemp0, pCell->uTruth, pCell->nVars ); + Extra_TruthCopy( uTemp1, pCell->uTruth, pCell->nVars ); + if ( pCell->CanonPhase == 0 ) + { + Extra_TruthCofactor0( uTemp0, pCell->nVars, pCell->CrossBar0 ); + Extra_TruthCofactor0( uTemp0, pCell->nVars, pCell->CrossBar1 ); + Extra_TruthCofactor1( uTemp1, pCell->nVars, pCell->CrossBar0 ); + Extra_TruthCofactor1( uTemp1, pCell->nVars, pCell->CrossBar1 ); + } + else if ( pCell->CanonPhase == 1 ) + { + Extra_TruthCofactor1( uTemp0, pCell->nVars, pCell->CrossBar0 ); + Extra_TruthCofactor0( uTemp0, pCell->nVars, pCell->CrossBar1 ); + Extra_TruthCofactor0( uTemp1, pCell->nVars, pCell->CrossBar0 ); + Extra_TruthCofactor1( uTemp1, pCell->nVars, pCell->CrossBar1 ); + } + else if ( pCell->CanonPhase == 2 ) + { + Extra_TruthCofactor0( uTemp0, pCell->nVars, pCell->CrossBar0 ); + Extra_TruthCofactor1( uTemp0, pCell->nVars, pCell->CrossBar1 ); + Extra_TruthCofactor1( uTemp1, pCell->nVars, pCell->CrossBar0 ); + Extra_TruthCofactor0( uTemp1, pCell->nVars, pCell->CrossBar1 ); + } + else assert( 0 ); + Extra_TruthMux( pCell->uTruth, uTemp0, uTemp1, pCell->nVars, pCell->CrossBar0 ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_CellTruthElem( unsigned * InA, unsigned * InB, unsigned * InC, unsigned * pOut, int nVars, int Type ) +{ + int nWords = Extra_TruthWordNum( nVars ); + int i; + + assert( Type < 22 ); + switch ( Type ) + { + // " 0\n", // 00 const 0 + case 0: + for ( i = 0; i < nWords; i++ ) + pOut[i] = 0; + return; + // " 1\n", // 01 const 1 + case 1: + for ( i = 0; i < nWords; i++ ) + pOut[i] = 0xFFFFFFFF; + return; + // "1 1\n", // 02 a + case 2: + for ( i = 0; i < nWords; i++ ) + pOut[i] = InA[i]; + return; + // "11 1\n", // 03 ab + case 3: + for ( i = 0; i < nWords; i++ ) + pOut[i] = InA[i] & InB[i]; + return; + // "11 0\n", // 04 (ab)' + case 4: + for ( i = 0; i < nWords; i++ ) + pOut[i] = ~(InA[i] & InB[i]); + return; + // "10 1\n01 1\n", // 05 a<+>b + case 5: + for ( i = 0; i < nWords; i++ ) + pOut[i] = InA[i] ^ InB[i]; + return; + // "111 1\n", // 06 + abc + case 6: + for ( i = 0; i < nWords; i++ ) + pOut[i] = InA[i] & InB[i] & InC[i]; + return; + // "111 0\n", // 07 (abc)' + case 7: + for ( i = 0; i < nWords; i++ ) + pOut[i] = ~(InA[i] & InB[i] & InC[i]); + return; + // "11- 1\n1-1 1\n", // 08 + a(b+c) + case 8: + for ( i = 0; i < nWords; i++ ) + pOut[i] = InA[i] & (InB[i] | InC[i]); + return; + // "11- 0\n1-1 0\n", // 09 (a(b+c))' + case 9: + for ( i = 0; i < nWords; i++ ) + pOut[i] = ~(InA[i] & (InB[i] | InC[i])); + return; + // "111 1\n100 1\n010 1\n001 1\n", // 10 + a<+>b<+>c + case 10: + for ( i = 0; i < nWords; i++ ) + pOut[i] = InA[i] ^ InB[i] ^ InC[i]; + return; + // "10- 0\n1-0 0\n011 0\n", // 11 + a<+>bc + case 11: + for ( i = 0; i < nWords; i++ ) + pOut[i] = InA[i] ^ (InB[i] & InC[i]); + return; + // "101 1\n110 1\n", // 12 + a(b<+>c) + case 12: + for ( i = 0; i < nWords; i++ ) + pOut[i] = InA[i] & (InB[i] ^ InC[i]); + return; + // "101 0\n110 0\n", // 13 (a(b<+>c))' + case 13: + for ( i = 0; i < nWords; i++ ) + pOut[i] = ~(InA[i] & (InB[i] ^ InC[i])); + return; + // "11- 1\n1-1 1\n-11 1\n", // 14 + ab+bc+ac + case 14: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (InA[i] & InB[i]) | (InB[i] & InC[i]) | (InA[i] & InC[i]); + return; + // "111 1\n000 1\n", // 15 + abc+a'b'c' + case 15: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (InA[i] & InB[i] & InC[i]) | (~InA[i] & ~InB[i] & ~InC[i]); + return; + // "111 0\n000 0\n", // 16 (abc+a'b'c')' + case 16: + for ( i = 0; i < nWords; i++ ) + pOut[i] = ~((InA[i] & InB[i] & InC[i]) | (~InA[i] & ~InB[i] & ~InC[i])); + return; + // "11- 1\n-11 1\n0-1 1\n", // 17 + ab+bc+a'c + case 17: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (InA[i] & InB[i]) | (InB[i] & InC[i]) | (~InA[i] & InC[i]); + return; + // "011 1\n101 1\n110 1\n", // 18 + a'bc+ab'c+abc' + case 18: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (~InA[i] & InB[i] & InC[i]) | (InA[i] & ~InB[i] & InC[i]) | (InA[i] & InB[i] & ~InC[i]); + return; + // "011 0\n101 0\n110 0\n", // 19 (a'bc+ab'c+abc')' + case 19: + for ( i = 0; i < nWords; i++ ) + pOut[i] = ~((~InA[i] & InB[i] & InC[i]) | (InA[i] & ~InB[i] & InC[i]) | (InA[i] & InB[i] & ~InC[i])); + return; + // "100 1\n-11 1\n", // 20 + ab'c'+bc + case 20: + for ( i = 0; i < nWords; i++ ) + pOut[i] = (InA[i] & ~InB[i] & ~InC[i]) | (InB[i] & InC[i]); + return; + // "100 0\n-11 0\n" // 21 (ab'c'+bc)' + case 21: + for ( i = 0; i < nWords; i++ ) + pOut[i] = ~((InA[i] & ~InB[i] & ~InC[i]) | (InB[i] & InC[i])); + return; + } +} + + +/**Function************************************************************* + + Synopsis [Start the precomputation manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Cut_CMan_t * Cut_CManStart() +{ + Cut_CMan_t * p; + int i, k; + // start the manager + assert( sizeof(unsigned) == 4 ); + p = ALLOC( Cut_CMan_t, 1 ); + memset( p, 0, sizeof(Cut_CMan_t) ); + // start the table and the memory manager + p->tTable = st_init_table(st_ptrcmp,st_ptrhash); + p->pMem = Extra_MmFixedStart( sizeof(Cut_Cell_t) ); + // set elementary truth tables + for ( k = 0; k < CUT_CELL_MVAR; k++ ) + for ( i = 0; i < (1<uInputs[k][i>>5] |= (1 << (i&31)); + s_pCMan = p; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_CManStop( Cut_CMan_t * p ) +{ + st_free_table( p->tTable ); + Extra_MmFixedStop( p->pMem ); + free( p ); +} +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Cut_CellIsRunning() +{ + return s_pCMan != NULL; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_CellDumpToFile() +{ + FILE * pFile; + Cut_CMan_t * p = s_pCMan; + Cut_Cell_t * pTemp; + char * pFileName = "celllib22.txt"; + int NumUsed[10][5] = {{0}}; + int BoxUsed[22][5] = {{0}}; + int i, k, Counter; + int clk = clock(); + + if ( p == NULL ) + { + printf( "Cut_CellDumpToFile: Cell manager is not defined.\n" ); + return; + } + + // count the number of cells used + for ( k = CUT_CELL_MVAR; k >= 0; k-- ) + { + for ( pTemp = p->pSameVar[k]; pTemp; pTemp = pTemp->pNextVar ) + { + if ( pTemp->nUsed == 0 ) + NumUsed[k][0]++; + else if ( pTemp->nUsed < 10 ) + NumUsed[k][1]++; + else if ( pTemp->nUsed < 100 ) + NumUsed[k][2]++; + else if ( pTemp->nUsed < 1000 ) + NumUsed[k][3]++; + else + NumUsed[k][4]++; + + for ( i = 0; i < 4; i++ ) + if ( pTemp->nUsed == 0 ) + BoxUsed[ pTemp->Box[i] ][0]++; + else if ( pTemp->nUsed < 10 ) + BoxUsed[ pTemp->Box[i] ][1]++; + else if ( pTemp->nUsed < 100 ) + BoxUsed[ pTemp->Box[i] ][2]++; + else if ( pTemp->nUsed < 1000 ) + BoxUsed[ pTemp->Box[i] ][3]++; + else + BoxUsed[ pTemp->Box[i] ][4]++; + } + } + + printf( "Functions found = %10d. Functions not found = %10d.\n", p->nCellFound, p->nCellNotFound ); + for ( k = 0; k <= CUT_CELL_MVAR; k++ ) + { + printf( "%3d : ", k ); + for ( i = 0; i < 5; i++ ) + printf( "%8d ", NumUsed[k][i] ); + printf( "\n" ); + } + printf( "Box usage:\n" ); + for ( k = 0; k < 22; k++ ) + { + printf( "%3d : ", k ); + for ( i = 0; i < 5; i++ ) + printf( "%8d ", BoxUsed[k][i] ); + printf( " %s", s_NP3Names[k] ); + printf( "\n" ); + } + + pFile = fopen( pFileName, "w" ); + if ( pFile == NULL ) + { + printf( "Cut_CellDumpToFile: Cannout open output file.\n" ); + return; + } + + Counter = 0; + for ( k = 0; k <= CUT_CELL_MVAR; k++ ) + { + for ( pTemp = p->pSameVar[k]; pTemp; pTemp = pTemp->pNextVar ) + if ( pTemp->nUsed > 0 ) + { + Extra_PrintHexadecimal( pFile, pTemp->uTruth, (k <= 5? 5 : k) ); + fprintf( pFile, "\n" ); + Counter++; + } + fprintf( pFile, "\n" ); + } + fclose( pFile ); + + printf( "Library composed of %d functions is written into file \"%s\". ", Counter, pFileName ); + + PRT( "Time", clock() - clk ); +} + + +/**Function************************************************************* + + Synopsis [Looks up if the given function exists in the hash table.] + + Description [If the function exists, returns 1, meaning that it can be + implemented using two levels of 3-input LUTs. If the function does not + exist, return 0.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Cut_CellTruthLookup( unsigned * pTruth, int nVars ) +{ + Cut_CMan_t * p = s_pCMan; + Cut_Cell_t * pTemp; + Cut_Cell_t Cell, * pCell = &Cell; + unsigned Hash; + int i; + + // cell manager is not defined + if ( p == NULL ) + { + printf( "Cut_CellTruthLookup: Cell manager is not defined.\n" ); + return 0; + } + + // canonicize + memset( pCell, 0, sizeof(Cut_Cell_t) ); + pCell->nVars = nVars; + Extra_TruthCopy( pCell->uTruth, pTruth, nVars ); + Cut_CellSuppMin( pCell ); + // set the elementary permutation + for ( i = 0; i < (int)pCell->nVars; i++ ) + pCell->CanonPerm[i] = i; + // canonicize + pCell->CanonPhase = Extra_TruthSemiCanonicize( pCell->uTruth, p->puAux, pCell->nVars, pCell->CanonPerm, pCell->Store ); + + + // check if the cell exists + Hash = Extra_TruthHash( pCell->uTruth, Extra_TruthWordNum(pCell->nVars) ); + if ( st_lookup( p->tTable, (char *)Hash, (char **)&pTemp ) ) + { + for ( ; pTemp; pTemp = pTemp->pNext ) + { + if ( pTemp->nVars != pCell->nVars ) + continue; + if ( Extra_TruthIsEqual(pTemp->uTruth, pCell->uTruth, pCell->nVars) ) + { + pTemp->nUsed++; + p->nCellFound++; + return 1; + } + } + } + p->nCellNotFound++; + return 0; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/cut/cutSeq.c b/abc_with_bb_support/src/opt/cut/cutSeq.c new file mode 100644 index 000000000..bde28eb4c --- /dev/null +++ b/abc_with_bb_support/src/opt/cut/cutSeq.c @@ -0,0 +1,227 @@ +/**CFile**************************************************************** + + FileName [cutSeq.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [K-feasible cut computation package.] + + Synopsis [Sequential cut computation.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cutSeq.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cutInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Shifts all cut leaves of the node by the given number of latches.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Cut_NodeShiftCutLeaves( Cut_Cut_t * pList, int nLat ) +{ + Cut_Cut_t * pTemp; + int i; + // shift the cuts by as many latches + Cut_ListForEachCut( pList, pTemp ) + { + pTemp->uSign = 0; + for ( i = 0; i < (int)pTemp->nLeaves; i++ ) + { + pTemp->pLeaves[i] += nLat; + pTemp->uSign |= Cut_NodeSign( pTemp->pLeaves[i] ); + } + } +} + +/**Function************************************************************* + + Synopsis [Computes sequential cuts for the node from its fanins.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_NodeComputeCutsSeq( Cut_Man_t * p, int Node, int Node0, int Node1, int fCompl0, int fCompl1, int nLat0, int nLat1, int fTriv, int CutSetNum ) +{ + Cut_List_t Super, * pSuper = &Super; + Cut_Cut_t * pListNew; + int clk; + + // get the number of cuts at the node + p->nNodeCuts = Cut_CutCountList( Cut_NodeReadCutsOld(p, Node) ); + if ( p->nNodeCuts >= p->pParams->nKeepMax ) + return; + + // count only the first visit + if ( p->nNodeCuts == 0 ) + p->nNodes++; + + // store the fanin lists + p->pStore0[0] = Cut_NodeReadCutsOld( p, Node0 ); + p->pStore0[1] = Cut_NodeReadCutsNew( p, Node0 ); + p->pStore1[0] = Cut_NodeReadCutsOld( p, Node1 ); + p->pStore1[1] = Cut_NodeReadCutsNew( p, Node1 ); + + // duplicate the cut lists if fanin nodes are non-standard + if ( Node == Node0 || Node == Node1 || Node0 == Node1 ) + { + p->pStore0[0] = Cut_CutDupList( p, p->pStore0[0] ); + p->pStore0[1] = Cut_CutDupList( p, p->pStore0[1] ); + p->pStore1[0] = Cut_CutDupList( p, p->pStore1[0] ); + p->pStore1[1] = Cut_CutDupList( p, p->pStore1[1] ); + } + + // shift the cuts by as many latches and recompute signatures + if ( nLat0 ) Cut_NodeShiftCutLeaves( p->pStore0[0], nLat0 ); + if ( nLat0 ) Cut_NodeShiftCutLeaves( p->pStore0[1], nLat0 ); + if ( nLat1 ) Cut_NodeShiftCutLeaves( p->pStore1[0], nLat1 ); + if ( nLat1 ) Cut_NodeShiftCutLeaves( p->pStore1[1], nLat1 ); + + // store the original lists for comparison + p->pCompareOld = Cut_NodeReadCutsOld( p, Node ); + p->pCompareNew = Cut_NodeReadCutsNew( p, Node ); + + // merge the old and the new +clk = clock(); + Cut_ListStart( pSuper ); + Cut_NodeDoComputeCuts( p, pSuper, Node, fCompl0, fCompl1, p->pStore0[0], p->pStore1[1], 0, 0 ); + Cut_NodeDoComputeCuts( p, pSuper, Node, fCompl0, fCompl1, p->pStore0[1], p->pStore1[0], 0, 0 ); + Cut_NodeDoComputeCuts( p, pSuper, Node, fCompl0, fCompl1, p->pStore0[1], p->pStore1[1], fTriv, 0 ); + pListNew = Cut_ListFinish( pSuper ); +p->timeMerge += clock() - clk; + + // shift the cuts by as many latches and recompute signatures + if ( Node == Node0 || Node == Node1 || Node0 == Node1 ) + { + Cut_CutRecycleList( p, p->pStore0[0] ); + Cut_CutRecycleList( p, p->pStore0[1] ); + Cut_CutRecycleList( p, p->pStore1[0] ); + Cut_CutRecycleList( p, p->pStore1[1] ); + } + else + { + if ( nLat0 ) Cut_NodeShiftCutLeaves( p->pStore0[0], -nLat0 ); + if ( nLat0 ) Cut_NodeShiftCutLeaves( p->pStore0[1], -nLat0 ); + if ( nLat1 ) Cut_NodeShiftCutLeaves( p->pStore1[0], -nLat1 ); + if ( nLat1 ) Cut_NodeShiftCutLeaves( p->pStore1[1], -nLat1 ); + } + + // set the lists at the node + if ( CutSetNum >= 0 ) + { + assert( Cut_NodeReadCutsTemp(p, CutSetNum) == NULL ); + Cut_NodeWriteCutsTemp( p, CutSetNum, pListNew ); + } + else + { + assert( Cut_NodeReadCutsNew(p, Node) == NULL ); + Cut_NodeWriteCutsNew( p, Node, pListNew ); + } + + // mark the node if we exceeded the number of cuts + if ( p->nNodeCuts >= p->pParams->nKeepMax ) + p->nCutsLimit++; +} + +/**Function************************************************************* + + Synopsis [Merges the new cuts with the old cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_NodeNewMergeWithOld( Cut_Man_t * p, int Node ) +{ + Cut_Cut_t * pListOld, * pListNew, * pList; + // get the new cuts + pListNew = Cut_NodeReadCutsNew( p, Node ); + if ( pListNew == NULL ) + return; + Cut_NodeWriteCutsNew( p, Node, NULL ); + // get the old cuts + pListOld = Cut_NodeReadCutsOld( p, Node ); + if ( pListOld == NULL ) + { + Cut_NodeWriteCutsOld( p, Node, pListNew ); + return; + } + // merge the lists + pList = Cut_CutMergeLists( pListOld, pListNew ); + Cut_NodeWriteCutsOld( p, Node, pList ); +} + + +/**Function************************************************************* + + Synopsis [Transfers the temporary cuts to be the new cuts.] + + Description [Returns 1 if something was transferred.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Cut_NodeTempTransferToNew( Cut_Man_t * p, int Node, int CutSetNum ) +{ + Cut_Cut_t * pList; + pList = Cut_NodeReadCutsTemp( p, CutSetNum ); + Cut_NodeWriteCutsTemp( p, CutSetNum, NULL ); + Cut_NodeWriteCutsNew( p, Node, pList ); + return pList != NULL; +} + +/**Function************************************************************* + + Synopsis [Transfers the old cuts to be the new cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_NodeOldTransferToNew( Cut_Man_t * p, int Node ) +{ + Cut_Cut_t * pList; + pList = Cut_NodeReadCutsOld( p, Node ); + Cut_NodeWriteCutsOld( p, Node, NULL ); + Cut_NodeWriteCutsNew( p, Node, pList ); +// Cut_CutListVerify( pList ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/cut/cutTruth.c b/abc_with_bb_support/src/opt/cut/cutTruth.c new file mode 100644 index 000000000..25772ff3d --- /dev/null +++ b/abc_with_bb_support/src/opt/cut/cutTruth.c @@ -0,0 +1,226 @@ +/**CFile**************************************************************** + + FileName [cutTruth.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [K-feasible cut computation package.] + + Synopsis [Incremental truth table computation.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cutTruth.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cutInt.h" + +/* + Truth tables computed in this package are represented as bit-strings + stored in the cut data structure. Cuts of any number of inputs have + the truth table with 2^k bits, where k is the max number of cut inputs. +*/ + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +extern int nTotal = 0; +extern int nGood = 0; +extern int nEqual = 0; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes the stretching phase of the cut w.r.t. the merged cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Cut_TruthPhase( Cut_Cut_t * pCut, Cut_Cut_t * pCut1 ) +{ + unsigned uPhase = 0; + int i, k; + for ( i = k = 0; i < (int)pCut->nLeaves; i++ ) + { + if ( k == (int)pCut1->nLeaves ) + break; + if ( pCut->pLeaves[i] < pCut1->pLeaves[k] ) + continue; + assert( pCut->pLeaves[i] == pCut1->pLeaves[k] ); + uPhase |= (1 << i); + k++; + } + return uPhase; +} + +/**Function************************************************************* + + Synopsis [Performs truth table computation.] + + Description [This procedure cannot be used while recording oracle + because it will overwrite Num0 and Num1.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_TruthNCanonicize( Cut_Cut_t * pCut ) +{ + unsigned uTruth; + unsigned * uCanon2; + char * pPhases2; + assert( pCut->nVarsMax < 6 ); + + // get the direct truth table + uTruth = *Cut_CutReadTruth(pCut); + + // compute the direct truth table + Extra_TruthCanonFastN( pCut->nVarsMax, pCut->nLeaves, &uTruth, &uCanon2, &pPhases2 ); +// uCanon[0] = uCanon2[0]; +// uCanon[1] = (p->nVarsMax == 6)? uCanon2[1] : uCanon2[0]; +// uPhases[0] = pPhases2[0]; + pCut->uCanon0 = uCanon2[0]; + pCut->Num0 = pPhases2[0]; + + // get the complemented truth table + uTruth = ~*Cut_CutReadTruth(pCut); + + // compute the direct truth table + Extra_TruthCanonFastN( pCut->nVarsMax, pCut->nLeaves, &uTruth, &uCanon2, &pPhases2 ); +// uCanon[0] = uCanon2[0]; +// uCanon[1] = (p->nVarsMax == 6)? uCanon2[1] : uCanon2[0]; +// uPhases[0] = pPhases2[0]; + pCut->uCanon1 = uCanon2[0]; + pCut->Num1 = pPhases2[0]; +} + +/**Function************************************************************* + + Synopsis [Performs truth table computation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_TruthComputeOld( Cut_Cut_t * pCut, Cut_Cut_t * pCut0, Cut_Cut_t * pCut1, int fCompl0, int fCompl1 ) +{ + static unsigned uTruth0[8], uTruth1[8]; + int nTruthWords = Cut_TruthWords( pCut->nVarsMax ); + unsigned * pTruthRes; + int i, uPhase; + + // permute the first table + uPhase = Cut_TruthPhase( pCut, pCut0 ); + Extra_TruthExpand( pCut->nVarsMax, nTruthWords, Cut_CutReadTruth(pCut0), uPhase, uTruth0 ); + if ( fCompl0 ) + { + for ( i = 0; i < nTruthWords; i++ ) + uTruth0[i] = ~uTruth0[i]; + } + + // permute the second table + uPhase = Cut_TruthPhase( pCut, pCut1 ); + Extra_TruthExpand( pCut->nVarsMax, nTruthWords, Cut_CutReadTruth(pCut1), uPhase, uTruth1 ); + if ( fCompl1 ) + { + for ( i = 0; i < nTruthWords; i++ ) + uTruth1[i] = ~uTruth1[i]; + } + + // write the resulting table + pTruthRes = Cut_CutReadTruth(pCut); + + if ( pCut->fCompl ) + { + for ( i = 0; i < nTruthWords; i++ ) + pTruthRes[i] = ~(uTruth0[i] & uTruth1[i]); + } + else + { + for ( i = 0; i < nTruthWords; i++ ) + pTruthRes[i] = uTruth0[i] & uTruth1[i]; + } +} + +/**Function************************************************************* + + Synopsis [Performs truth table computation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Cut_TruthCompute( Cut_Man_t * p, Cut_Cut_t * pCut, Cut_Cut_t * pCut0, Cut_Cut_t * pCut1, int fCompl0, int fCompl1 ) +{ + // permute the first table + if ( fCompl0 ) + Extra_TruthNot( p->puTemp[0], Cut_CutReadTruth(pCut0), pCut->nVarsMax ); + else + Extra_TruthCopy( p->puTemp[0], Cut_CutReadTruth(pCut0), pCut->nVarsMax ); + Extra_TruthStretch( p->puTemp[2], p->puTemp[0], pCut0->nLeaves, pCut->nVarsMax, Cut_TruthPhase(pCut, pCut0) ); + // permute the second table + if ( fCompl1 ) + Extra_TruthNot( p->puTemp[1], Cut_CutReadTruth(pCut1), pCut->nVarsMax ); + else + Extra_TruthCopy( p->puTemp[1], Cut_CutReadTruth(pCut1), pCut->nVarsMax ); + Extra_TruthStretch( p->puTemp[3], p->puTemp[1], pCut1->nLeaves, pCut->nVarsMax, Cut_TruthPhase(pCut, pCut1) ); + // produce the resulting table + if ( pCut->fCompl ) + Extra_TruthNand( Cut_CutReadTruth(pCut), p->puTemp[2], p->puTemp[3], pCut->nVarsMax ); + else + Extra_TruthAnd( Cut_CutReadTruth(pCut), p->puTemp[2], p->puTemp[3], pCut->nVarsMax ); + +// Ivy_TruthTestOne( *Cut_CutReadTruth(pCut) ); + + // quit if no fancy computation is needed + if ( !p->pParams->fFancy ) + return; + + if ( pCut->nLeaves != 7 ) + return; + + // count the total number of truth tables computed + nTotal++; + + // MAPPING INTO ALTERA 6-2 LOGIC BLOCKS + // call this procedure to find the minimum number of common variables in the cofactors + // if this number is less or equal than 3, the cut can be implemented using the 6-2 logic block + if ( Extra_TruthMinCofSuppOverlap( Cut_CutReadTruth(pCut), pCut->nVarsMax, NULL ) <= 4 ) + nGood++; + + // MAPPING INTO ACTEL 2x2 CELLS + // call this procedure to see if a semi-canonical form can be found in the lookup table + // (if it exists, then a two-level 3-input LUT implementation of the cut exists) + // Before this procedure is called, cell manager should be defined by calling + // Cut_CellLoad (make sure file "cells22_daomap_iwls.txt" is available in the working dir) +// if ( Cut_CellIsRunning() && pCut->nVarsMax <= 9 ) +// nGood += Cut_CellTruthLookup( Cut_CutReadTruth(pCut), pCut->nVarsMax ); +} + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/cut/module.make b/abc_with_bb_support/src/opt/cut/module.make new file mode 100644 index 000000000..132e730b5 --- /dev/null +++ b/abc_with_bb_support/src/opt/cut/module.make @@ -0,0 +1,9 @@ +SRC += src/opt/cut/cutApi.c \ + src/opt/cut/cutCut.c \ + src/opt/cut/cutMan.c \ + src/opt/cut/cutMerge.c \ + src/opt/cut/cutNode.c \ + src/opt/cut/cutOracle.c \ + src/opt/cut/cutPre22.c \ + src/opt/cut/cutSeq.c \ + src/opt/cut/cutTruth.c diff --git a/abc_with_bb_support/src/opt/dec/dec.h b/abc_with_bb_support/src/opt/dec/dec.h new file mode 100644 index 000000000..a096bb419 --- /dev/null +++ b/abc_with_bb_support/src/opt/dec/dec.h @@ -0,0 +1,719 @@ +/**CFile**************************************************************** + + FileName [dec.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [A simple decomposition tree/node data structure and its APIs.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: dec.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __DEC_H__ +#define __DEC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Dec_Edge_t_ Dec_Edge_t; +struct Dec_Edge_t_ +{ + unsigned fCompl : 1; // the complemented bit + unsigned Node : 30; // the decomposition node pointed by the edge +}; + +typedef struct Dec_Node_t_ Dec_Node_t; +struct Dec_Node_t_ +{ + Dec_Edge_t eEdge0; // the left child of the node + Dec_Edge_t eEdge1; // the right child of the node + // other info + void * pFunc; // the function of the node (BDD or AIG) + unsigned Level : 14; // the level of this node in the global AIG + // printing info + unsigned fNodeOr : 1; // marks the original OR node + unsigned fCompl0 : 1; // marks the original complemented edge + unsigned fCompl1 : 1; // marks the original complemented edge + // latch info + unsigned nLat0 : 5; // the number of latches on the first edge + unsigned nLat1 : 5; // the number of latches on the second edge + unsigned nLat2 : 5; // the number of latches on the output edge +}; + +typedef struct Dec_Graph_t_ Dec_Graph_t; +struct Dec_Graph_t_ +{ + int fConst; // marks the constant 1 graph + int nLeaves; // the number of leaves + int nSize; // the number of nodes (including the leaves) + int nCap; // the number of allocated nodes + Dec_Node_t * pNodes; // the array of leaves and internal nodes + Dec_Edge_t eRoot; // the pointer to the topmost node +}; + +typedef struct Dec_Man_t_ Dec_Man_t; +struct Dec_Man_t_ +{ + void * pMvcMem; // memory manager for MVC cover (used for factoring) + Vec_Int_t * vCubes; // storage for cubes + Vec_Int_t * vLits; // storage for literals + // precomputation information about 4-variable functions + unsigned short * puCanons; // canonical forms + char * pPhases; // canonical phases + char * pPerms; // canonical permutations + unsigned char * pMap; // mapping of functions into class numbers +}; + + +//////////////////////////////////////////////////////////////////////// +/// ITERATORS /// +//////////////////////////////////////////////////////////////////////// + +// interator throught the leaves +#define Dec_GraphForEachLeaf( pGraph, pLeaf, i ) \ + for ( i = 0; (i < (pGraph)->nLeaves) && (((pLeaf) = Dec_GraphNode(pGraph, i)), 1); i++ ) +// interator throught the internal nodes +#define Dec_GraphForEachNode( pGraph, pAnd, i ) \ + for ( i = (pGraph)->nLeaves; (i < (pGraph)->nSize) && (((pAnd) = Dec_GraphNode(pGraph, i)), 1); i++ ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== decAbc.c ========================================================*/ +extern Abc_Obj_t * Dec_GraphToNetwork( Abc_Ntk_t * pNtk, Dec_Graph_t * pGraph ); +extern Abc_Obj_t * Dec_GraphToNetworkNoStrash( Abc_Ntk_t * pNtk, Dec_Graph_t * pGraph ); +extern int Dec_GraphToNetworkCount( Abc_Obj_t * pRoot, Dec_Graph_t * pGraph, int NodeMax, int LevelMax ); +extern void Dec_GraphUpdateNetwork( Abc_Obj_t * pRoot, Dec_Graph_t * pGraph, bool fUpdateLevel, int nGain ); +/*=== decFactor.c ========================================================*/ +extern Dec_Graph_t * Dec_Factor( char * pSop ); +/*=== decMan.c ========================================================*/ +extern Dec_Man_t * Dec_ManStart(); +extern void Dec_ManStop( Dec_Man_t * p ); +/*=== decPrint.c ========================================================*/ +extern void Dec_GraphPrint( FILE * pFile, Dec_Graph_t * pGraph, char * pNamesIn[], char * pNameOut ); +/*=== decUtil.c ========================================================*/ +extern DdNode * Dec_GraphDeriveBdd( DdManager * dd, Dec_Graph_t * pGraph ); +extern unsigned Dec_GraphDeriveTruth( Dec_Graph_t * pGraph ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Creates an edge pointing to the node in the given polarity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Edge_t Dec_EdgeCreate( int Node, int fCompl ) +{ + Dec_Edge_t eEdge = { fCompl, Node }; + return eEdge; +} + +/**Function************************************************************* + + Synopsis [Converts the edge into unsigned integer.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Dec_EdgeToInt( Dec_Edge_t eEdge ) +{ + return (eEdge.Node << 1) | eEdge.fCompl; +} + +/**Function************************************************************* + + Synopsis [Converts unsigned integer into the edge.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Edge_t Dec_IntToEdge( unsigned Edge ) +{ + return Dec_EdgeCreate( Edge >> 1, Edge & 1 ); +} + +/**Function************************************************************* + + Synopsis [Converts the edge into unsigned integer.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Dec_EdgeToInt_( Dec_Edge_t eEdge ) +{ + return *(unsigned *)&eEdge; +} + +/**Function************************************************************* + + Synopsis [Converts unsigned integer into the edge.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Edge_t Dec_IntToEdge_( unsigned Edge ) +{ + return *(Dec_Edge_t *)&Edge; +} + +/**Function************************************************************* + + Synopsis [Creates a graph with the given number of leaves.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Graph_t * Dec_GraphCreate( int nLeaves ) +{ + Dec_Graph_t * pGraph; + pGraph = ALLOC( Dec_Graph_t, 1 ); + memset( pGraph, 0, sizeof(Dec_Graph_t) ); + pGraph->nLeaves = nLeaves; + pGraph->nSize = nLeaves; + pGraph->nCap = 2 * nLeaves + 50; + pGraph->pNodes = ALLOC( Dec_Node_t, pGraph->nCap ); + memset( pGraph->pNodes, 0, sizeof(Dec_Node_t) * pGraph->nSize ); + return pGraph; +} + +/**Function************************************************************* + + Synopsis [Creates constant 0 graph.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Graph_t * Dec_GraphCreateConst0() +{ + Dec_Graph_t * pGraph; + pGraph = ALLOC( Dec_Graph_t, 1 ); + memset( pGraph, 0, sizeof(Dec_Graph_t) ); + pGraph->fConst = 1; + pGraph->eRoot.fCompl = 1; + return pGraph; +} + +/**Function************************************************************* + + Synopsis [Creates constant 1 graph.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Graph_t * Dec_GraphCreateConst1() +{ + Dec_Graph_t * pGraph; + pGraph = ALLOC( Dec_Graph_t, 1 ); + memset( pGraph, 0, sizeof(Dec_Graph_t) ); + pGraph->fConst = 1; + return pGraph; +} + +/**Function************************************************************* + + Synopsis [Creates the literal graph.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Graph_t * Dec_GraphCreateLeaf( int iLeaf, int nLeaves, int fCompl ) +{ + Dec_Graph_t * pGraph; + assert( 0 <= iLeaf && iLeaf < nLeaves ); + pGraph = Dec_GraphCreate( nLeaves ); + pGraph->eRoot.Node = iLeaf; + pGraph->eRoot.fCompl = fCompl; + return pGraph; +} + +/**Function************************************************************* + + Synopsis [Creates a graph with the given number of leaves.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Dec_GraphFree( Dec_Graph_t * pGraph ) +{ + FREE( pGraph->pNodes ); + free( pGraph ); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the graph is a constant.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline bool Dec_GraphIsConst( Dec_Graph_t * pGraph ) +{ + return pGraph->fConst; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the graph is constant 0.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline bool Dec_GraphIsConst0( Dec_Graph_t * pGraph ) +{ + return pGraph->fConst && pGraph->eRoot.fCompl; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the graph is constant 1.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline bool Dec_GraphIsConst1( Dec_Graph_t * pGraph ) +{ + return pGraph->fConst && !pGraph->eRoot.fCompl; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the graph is complemented.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline bool Dec_GraphIsComplement( Dec_Graph_t * pGraph ) +{ + return pGraph->eRoot.fCompl; +} + +/**Function************************************************************* + + Synopsis [Checks if the graph is complemented.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Dec_GraphComplement( Dec_Graph_t * pGraph ) +{ + pGraph->eRoot.fCompl ^= 1; +} + + +/**Function************************************************************* + + Synopsis [Returns the number of leaves.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Dec_GraphLeaveNum( Dec_Graph_t * pGraph ) +{ + return pGraph->nLeaves; +} + +/**Function************************************************************* + + Synopsis [Returns the number of internal nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Dec_GraphNodeNum( Dec_Graph_t * pGraph ) +{ + return pGraph->nSize - pGraph->nLeaves; +} + +/**Function************************************************************* + + Synopsis [Returns the pointer to the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Node_t * Dec_GraphNode( Dec_Graph_t * pGraph, int i ) +{ + return pGraph->pNodes + i; +} + +/**Function************************************************************* + + Synopsis [Returns the pointer to the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Node_t * Dec_GraphNodeLast( Dec_Graph_t * pGraph ) +{ + return pGraph->pNodes + pGraph->nSize - 1; +} + +/**Function************************************************************* + + Synopsis [Returns the number of the given node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Dec_GraphNodeInt( Dec_Graph_t * pGraph, Dec_Node_t * pNode ) +{ + return pNode - pGraph->pNodes; +} + +/**Function************************************************************* + + Synopsis [Check if the graph represents elementary variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline bool Dec_GraphIsVar( Dec_Graph_t * pGraph ) +{ + return pGraph->eRoot.Node < (unsigned)pGraph->nLeaves; +} + +/**Function************************************************************* + + Synopsis [Check if the graph represents elementary variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline bool Dec_GraphNodeIsVar( Dec_Graph_t * pGraph, Dec_Node_t * pNode ) +{ + return Dec_GraphNodeInt(pGraph,pNode) < pGraph->nLeaves; +} + +/**Function************************************************************* + + Synopsis [Returns the elementary variable elementary variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Node_t * Dec_GraphVar( Dec_Graph_t * pGraph ) +{ + assert( Dec_GraphIsVar( pGraph ) ); + return Dec_GraphNode( pGraph, pGraph->eRoot.Node ); +} + +/**Function************************************************************* + + Synopsis [Returns the number of the elementary variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Dec_GraphVarInt( Dec_Graph_t * pGraph ) +{ + assert( Dec_GraphIsVar( pGraph ) ); + return Dec_GraphNodeInt( pGraph, Dec_GraphVar(pGraph) ); +} + +/**Function************************************************************* + + Synopsis [Sets the root of the graph.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Dec_GraphSetRoot( Dec_Graph_t * pGraph, Dec_Edge_t eRoot ) +{ + pGraph->eRoot = eRoot; +} + +/**Function************************************************************* + + Synopsis [Appends a new node to the graph.] + + Description [This procedure is meant for internal use.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Node_t * Dec_GraphAppendNode( Dec_Graph_t * pGraph ) +{ + Dec_Node_t * pNode; + if ( pGraph->nSize == pGraph->nCap ) + { + pGraph->pNodes = REALLOC( Dec_Node_t, pGraph->pNodes, 2 * pGraph->nCap ); + pGraph->nCap = 2 * pGraph->nCap; + } + pNode = pGraph->pNodes + pGraph->nSize++; + memset( pNode, 0, sizeof(Dec_Node_t) ); + return pNode; +} + +/**Function************************************************************* + + Synopsis [Creates an AND node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Edge_t Dec_GraphAddNodeAnd( Dec_Graph_t * pGraph, Dec_Edge_t eEdge0, Dec_Edge_t eEdge1 ) +{ + Dec_Node_t * pNode; + // get the new node + pNode = Dec_GraphAppendNode( pGraph ); + // set the inputs and other info + pNode->eEdge0 = eEdge0; + pNode->eEdge1 = eEdge1; + pNode->fCompl0 = eEdge0.fCompl; + pNode->fCompl1 = eEdge1.fCompl; + return Dec_EdgeCreate( pGraph->nSize - 1, 0 ); +} + +/**Function************************************************************* + + Synopsis [Creates an OR node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Edge_t Dec_GraphAddNodeOr( Dec_Graph_t * pGraph, Dec_Edge_t eEdge0, Dec_Edge_t eEdge1 ) +{ + Dec_Node_t * pNode; + // get the new node + pNode = Dec_GraphAppendNode( pGraph ); + // set the inputs and other info + pNode->eEdge0 = eEdge0; + pNode->eEdge1 = eEdge1; + pNode->fCompl0 = eEdge0.fCompl; + pNode->fCompl1 = eEdge1.fCompl; + // make adjustments for the OR gate + pNode->fNodeOr = 1; + pNode->eEdge0.fCompl = !pNode->eEdge0.fCompl; + pNode->eEdge1.fCompl = !pNode->eEdge1.fCompl; + return Dec_EdgeCreate( pGraph->nSize - 1, 1 ); +} + +/**Function************************************************************* + + Synopsis [Creates an XOR node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Edge_t Dec_GraphAddNodeXor( Dec_Graph_t * pGraph, Dec_Edge_t eEdge0, Dec_Edge_t eEdge1, int Type ) +{ + Dec_Edge_t eNode0, eNode1, eNode; + if ( Type == 0 ) + { + // derive the first AND + eEdge0.fCompl ^= 1; + eNode0 = Dec_GraphAddNodeAnd( pGraph, eEdge0, eEdge1 ); + eEdge0.fCompl ^= 1; + // derive the second AND + eEdge1.fCompl ^= 1; + eNode1 = Dec_GraphAddNodeAnd( pGraph, eEdge0, eEdge1 ); + // derive the final OR + eNode = Dec_GraphAddNodeOr( pGraph, eNode0, eNode1 ); + } + else + { + // derive the first AND + eNode0 = Dec_GraphAddNodeAnd( pGraph, eEdge0, eEdge1 ); + // derive the second AND + eEdge0.fCompl ^= 1; + eEdge1.fCompl ^= 1; + eNode1 = Dec_GraphAddNodeAnd( pGraph, eEdge0, eEdge1 ); + // derive the final OR + eNode = Dec_GraphAddNodeOr( pGraph, eNode0, eNode1 ); + eNode.fCompl ^= 1; + } + return eNode; +} + +/**Function************************************************************* + + Synopsis [Creates an XOR node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Dec_Edge_t Dec_GraphAddNodeMux( Dec_Graph_t * pGraph, Dec_Edge_t eEdgeC, Dec_Edge_t eEdgeT, Dec_Edge_t eEdgeE, int Type ) +{ + Dec_Edge_t eNode0, eNode1, eNode; + if ( Type == 0 ) + { + // derive the first AND + eNode0 = Dec_GraphAddNodeAnd( pGraph, eEdgeC, eEdgeT ); + // derive the second AND + eEdgeC.fCompl ^= 1; + eNode1 = Dec_GraphAddNodeAnd( pGraph, eEdgeC, eEdgeE ); + // derive the final OR + eNode = Dec_GraphAddNodeOr( pGraph, eNode0, eNode1 ); + } + else + { + // complement the arguments + eEdgeT.fCompl ^= 1; + eEdgeE.fCompl ^= 1; + // derive the first AND + eNode0 = Dec_GraphAddNodeAnd( pGraph, eEdgeC, eEdgeT ); + // derive the second AND + eEdgeC.fCompl ^= 1; + eNode1 = Dec_GraphAddNodeAnd( pGraph, eEdgeC, eEdgeE ); + // derive the final OR + eNode = Dec_GraphAddNodeOr( pGraph, eNode0, eNode1 ); + eNode.fCompl ^= 1; + } + return eNode; +} + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/opt/dec/decAbc.c b/abc_with_bb_support/src/opt/dec/decAbc.c new file mode 100644 index 000000000..99f193584 --- /dev/null +++ b/abc_with_bb_support/src/opt/dec/decAbc.c @@ -0,0 +1,305 @@ +/**CFile**************************************************************** + + FileName [decAbc.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Interface between the decomposition package and ABC network.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: decAbc.c,v 1.1 2003/05/22 19:20:05 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "dec.h" +#include "ivy.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Transforms the decomposition graph into the AIG.] + + Description [AIG nodes for the fanins should be assigned to pNode->pFunc + of the leaves of the graph before calling this procedure.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Dec_GraphToNetwork( Abc_Ntk_t * pNtk, Dec_Graph_t * pGraph ) +{ + Abc_Obj_t * pAnd0, * pAnd1; + Dec_Node_t * pNode; + int i; + // check for constant function + if ( Dec_GraphIsConst(pGraph) ) + return Abc_ObjNotCond( Abc_AigConst1(pNtk), Dec_GraphIsComplement(pGraph) ); + // check for a literal + if ( Dec_GraphIsVar(pGraph) ) + return Abc_ObjNotCond( Dec_GraphVar(pGraph)->pFunc, Dec_GraphIsComplement(pGraph) ); + // build the AIG nodes corresponding to the AND gates of the graph + Dec_GraphForEachNode( pGraph, pNode, i ) + { + pAnd0 = Abc_ObjNotCond( Dec_GraphNode(pGraph, pNode->eEdge0.Node)->pFunc, pNode->eEdge0.fCompl ); + pAnd1 = Abc_ObjNotCond( Dec_GraphNode(pGraph, pNode->eEdge1.Node)->pFunc, pNode->eEdge1.fCompl ); + pNode->pFunc = Abc_AigAnd( pNtk->pManFunc, pAnd0, pAnd1 ); + } + // complement the result if necessary + return Abc_ObjNotCond( pNode->pFunc, Dec_GraphIsComplement(pGraph) ); +} + +/**Function************************************************************* + + Synopsis [Transforms the decomposition graph into the AIG.] + + Description [AIG nodes for the fanins should be assigned to pNode->pFunc + of the leaves of the graph before calling this procedure.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Dec_GraphToNetworkNoStrash( Abc_Ntk_t * pNtk, Dec_Graph_t * pGraph ) +{ + Abc_Obj_t * pAnd, * pAnd0, * pAnd1; + Dec_Node_t * pNode; + int i; + // check for constant function + if ( Dec_GraphIsConst(pGraph) ) + return Abc_ObjNotCond( Abc_AigConst1(pNtk), Dec_GraphIsComplement(pGraph) ); + // check for a literal + if ( Dec_GraphIsVar(pGraph) ) + return Abc_ObjNotCond( Dec_GraphVar(pGraph)->pFunc, Dec_GraphIsComplement(pGraph) ); + // build the AIG nodes corresponding to the AND gates of the graph + Dec_GraphForEachNode( pGraph, pNode, i ) + { + pAnd0 = Abc_ObjNotCond( Dec_GraphNode(pGraph, pNode->eEdge0.Node)->pFunc, pNode->eEdge0.fCompl ); + pAnd1 = Abc_ObjNotCond( Dec_GraphNode(pGraph, pNode->eEdge1.Node)->pFunc, pNode->eEdge1.fCompl ); +// pNode->pFunc = Abc_AigAnd( pNtk->pManFunc, pAnd0, pAnd1 ); + pAnd = Abc_NtkCreateNode( pNtk ); + Abc_ObjAddFanin( pAnd, pAnd0 ); + Abc_ObjAddFanin( pAnd, pAnd1 ); + pNode->pFunc = pAnd; + } + // complement the result if necessary + return Abc_ObjNotCond( pNode->pFunc, Dec_GraphIsComplement(pGraph) ); +} + +/**Function************************************************************* + + Synopsis [Counts the number of new nodes added when using this graph.] + + Description [AIG nodes for the fanins should be assigned to pNode->pFunc + of the leaves of the graph before calling this procedure. + Returns -1 if the number of nodes and levels exceeded the given limit or + the number of levels exceeded the maximum allowed level.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dec_GraphToNetworkCount( Abc_Obj_t * pRoot, Dec_Graph_t * pGraph, int NodeMax, int LevelMax ) +{ + Abc_Aig_t * pMan = pRoot->pNtk->pManFunc; + Dec_Node_t * pNode, * pNode0, * pNode1; + Abc_Obj_t * pAnd, * pAnd0, * pAnd1; + int i, Counter, LevelNew, LevelOld; + // check for constant function or a literal + if ( Dec_GraphIsConst(pGraph) || Dec_GraphIsVar(pGraph) ) + return 0; + // set the levels of the leaves + Dec_GraphForEachLeaf( pGraph, pNode, i ) + pNode->Level = Abc_ObjRegular(pNode->pFunc)->Level; + // compute the AIG size after adding the internal nodes + Counter = 0; + Dec_GraphForEachNode( pGraph, pNode, i ) + { + // get the children of this node + pNode0 = Dec_GraphNode( pGraph, pNode->eEdge0.Node ); + pNode1 = Dec_GraphNode( pGraph, pNode->eEdge1.Node ); + // get the AIG nodes corresponding to the children + pAnd0 = pNode0->pFunc; + pAnd1 = pNode1->pFunc; + if ( pAnd0 && pAnd1 ) + { + // if they are both present, find the resulting node + pAnd0 = Abc_ObjNotCond( pAnd0, pNode->eEdge0.fCompl ); + pAnd1 = Abc_ObjNotCond( pAnd1, pNode->eEdge1.fCompl ); + pAnd = Abc_AigAndLookup( pMan, pAnd0, pAnd1 ); + // return -1 if the node is the same as the original root + if ( Abc_ObjRegular(pAnd) == pRoot ) + return -1; + } + else + pAnd = NULL; + // count the number of added nodes + if ( pAnd == NULL || Abc_NodeIsTravIdCurrent(Abc_ObjRegular(pAnd)) ) + { + if ( ++Counter > NodeMax ) + return -1; + } + // count the number of new levels + LevelNew = 1 + ABC_MAX( pNode0->Level, pNode1->Level ); + if ( pAnd ) + { + if ( Abc_ObjRegular(pAnd) == Abc_AigConst1(pRoot->pNtk) ) + LevelNew = 0; + else if ( Abc_ObjRegular(pAnd) == Abc_ObjRegular(pAnd0) ) + LevelNew = (int)Abc_ObjRegular(pAnd0)->Level; + else if ( Abc_ObjRegular(pAnd) == Abc_ObjRegular(pAnd1) ) + LevelNew = (int)Abc_ObjRegular(pAnd1)->Level; + LevelOld = (int)Abc_ObjRegular(pAnd)->Level; +// assert( LevelNew == LevelOld ); + } + if ( LevelNew > LevelMax ) + return -1; + pNode->pFunc = pAnd; + pNode->Level = LevelNew; + } + return Counter; +} + + +/**Function************************************************************* + + Synopsis [Replaces MFFC of the node by the new factored form.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dec_GraphUpdateNetwork( Abc_Obj_t * pRoot, Dec_Graph_t * pGraph, bool fUpdateLevel, int nGain ) +{ + Abc_Obj_t * pRootNew; + Abc_Ntk_t * pNtk = pRoot->pNtk; + int nNodesNew, nNodesOld; + nNodesOld = Abc_NtkNodeNum(pNtk); + // create the new structure of nodes + pRootNew = Dec_GraphToNetwork( pNtk, pGraph ); + // remove the old nodes + Abc_AigReplace( pNtk->pManFunc, pRoot, pRootNew, fUpdateLevel ); + // compare the gains + nNodesNew = Abc_NtkNodeNum(pNtk); + assert( nGain <= nNodesOld - nNodesNew ); +} + + +/**Function************************************************************* + + Synopsis [Transforms the decomposition graph into the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Dec_GraphToNetworkAig( Hop_Man_t * pMan, Dec_Graph_t * pGraph ) +{ + Dec_Node_t * pNode; + Hop_Obj_t * pAnd0, * pAnd1; + int i; + // check for constant function + if ( Dec_GraphIsConst(pGraph) ) + return Hop_NotCond( Hop_ManConst1(pMan), Dec_GraphIsComplement(pGraph) ); + // check for a literal + if ( Dec_GraphIsVar(pGraph) ) + return Hop_NotCond( Dec_GraphVar(pGraph)->pFunc, Dec_GraphIsComplement(pGraph) ); + // build the AIG nodes corresponding to the AND gates of the graph + Dec_GraphForEachNode( pGraph, pNode, i ) + { + pAnd0 = Hop_NotCond( Dec_GraphNode(pGraph, pNode->eEdge0.Node)->pFunc, pNode->eEdge0.fCompl ); + pAnd1 = Hop_NotCond( Dec_GraphNode(pGraph, pNode->eEdge1.Node)->pFunc, pNode->eEdge1.fCompl ); + pNode->pFunc = Hop_And( pMan, pAnd0, pAnd1 ); + } + // complement the result if necessary + return Hop_NotCond( pNode->pFunc, Dec_GraphIsComplement(pGraph) ); +} + +/**Function************************************************************* + + Synopsis [Strashes one logic node using its SOP.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Hop_Obj_t * Dec_GraphFactorSop( Hop_Man_t * pMan, char * pSop ) +{ + Hop_Obj_t * pFunc; + Dec_Graph_t * pFForm; + Dec_Node_t * pNode; + int i; + // perform factoring + pFForm = Dec_Factor( pSop ); + // collect the fanins + Dec_GraphForEachLeaf( pFForm, pNode, i ) + pNode->pFunc = Hop_IthVar( pMan, i ); + // perform strashing + pFunc = Dec_GraphToNetworkAig( pMan, pFForm ); + Dec_GraphFree( pFForm ); + return pFunc; +} + +/**Function************************************************************* + + Synopsis [Transforms the decomposition graph into the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Ivy_Obj_t * Dec_GraphToNetworkIvy( Ivy_Man_t * pMan, Dec_Graph_t * pGraph ) +{ + Dec_Node_t * pNode; + Ivy_Obj_t * pAnd0, * pAnd1; + int i; + // check for constant function + if ( Dec_GraphIsConst(pGraph) ) + return Ivy_NotCond( Ivy_ManConst1(pMan), Dec_GraphIsComplement(pGraph) ); + // check for a literal + if ( Dec_GraphIsVar(pGraph) ) + return Ivy_NotCond( Dec_GraphVar(pGraph)->pFunc, Dec_GraphIsComplement(pGraph) ); + // build the AIG nodes corresponding to the AND gates of the graph + Dec_GraphForEachNode( pGraph, pNode, i ) + { + pAnd0 = Ivy_NotCond( Dec_GraphNode(pGraph, pNode->eEdge0.Node)->pFunc, pNode->eEdge0.fCompl ); + pAnd1 = Ivy_NotCond( Dec_GraphNode(pGraph, pNode->eEdge1.Node)->pFunc, pNode->eEdge1.fCompl ); + pNode->pFunc = Ivy_And( pMan, pAnd0, pAnd1 ); + } + // complement the result if necessary + return Ivy_NotCond( pNode->pFunc, Dec_GraphIsComplement(pGraph) ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/dec/decFactor.c b/abc_with_bb_support/src/opt/dec/decFactor.c new file mode 100644 index 000000000..b2ccf2fa7 --- /dev/null +++ b/abc_with_bb_support/src/opt/dec/decFactor.c @@ -0,0 +1,392 @@ +/**CFile**************************************************************** + + FileName [ftFactor.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Procedures for algebraic factoring.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: ftFactor.c,v 1.3 2003/09/01 04:56:43 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "main.h" +#include "mvc.h" +#include "dec.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Dec_Edge_t Dec_Factor_rec( Dec_Graph_t * pFForm, Mvc_Cover_t * pCover ); +static Dec_Edge_t Dec_FactorLF_rec( Dec_Graph_t * pFForm, Mvc_Cover_t * pCover, Mvc_Cover_t * pSimple ); +static Dec_Edge_t Dec_FactorTrivial( Dec_Graph_t * pFForm, Mvc_Cover_t * pCover ); +static Dec_Edge_t Dec_FactorTrivialCube( Dec_Graph_t * pFForm, Mvc_Cover_t * pCover, Mvc_Cube_t * pCube, Vec_Int_t * vEdgeLits ); +static Dec_Edge_t Dec_FactorTrivialTree_rec( Dec_Graph_t * pFForm, Dec_Edge_t * peNodes, int nNodes, int fNodeOr ); +static int Dec_FactorVerify( char * pSop, Dec_Graph_t * pFForm ); +static Mvc_Cover_t * Dec_ConvertSopToMvc( char * pSop ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Factors the cover.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Dec_Factor( char * pSop ) +{ + Mvc_Cover_t * pCover; + Dec_Graph_t * pFForm; + Dec_Edge_t eRoot; + + // derive the cover from the SOP representation + pCover = Dec_ConvertSopToMvc( pSop ); + + // make sure the cover is CCS free (should be done before CST) + Mvc_CoverContain( pCover ); + // check for trivial functions + if ( Mvc_CoverIsEmpty(pCover) ) + { + Mvc_CoverFree( pCover ); + return Dec_GraphCreateConst0(); + } + if ( Mvc_CoverIsTautology(pCover) ) + { + Mvc_CoverFree( pCover ); + return Dec_GraphCreateConst1(); + } + + // perform CST + Mvc_CoverInverse( pCover ); // CST + // start the factored form + pFForm = Dec_GraphCreate( Abc_SopGetVarNum(pSop) ); + // factor the cover + eRoot = Dec_Factor_rec( pFForm, pCover ); + // finalize the factored form + Dec_GraphSetRoot( pFForm, eRoot ); + // complement the factored form if SOP is complemented + if ( Abc_SopIsComplement(pSop) ) + Dec_GraphComplement( pFForm ); + // verify the factored form +// if ( !Dec_FactorVerify( pSop, pFForm ) ) +// printf( "Verification has failed.\n" ); +// Mvc_CoverInverse( pCover ); // undo CST + Mvc_CoverFree( pCover ); + return pFForm; +} + +/**Function************************************************************* + + Synopsis [Internal recursive factoring procedure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Edge_t Dec_Factor_rec( Dec_Graph_t * pFForm, Mvc_Cover_t * pCover ) +{ + Mvc_Cover_t * pDiv, * pQuo, * pRem, * pCom; + Dec_Edge_t eNodeDiv, eNodeQuo, eNodeRem; + Dec_Edge_t eNodeAnd, eNode; + + // make sure the cover contains some cubes + assert( Mvc_CoverReadCubeNum(pCover) ); + + // get the divisor + pDiv = Mvc_CoverDivisor( pCover ); + if ( pDiv == NULL ) + return Dec_FactorTrivial( pFForm, pCover ); + + // divide the cover by the divisor + Mvc_CoverDivideInternal( pCover, pDiv, &pQuo, &pRem ); + assert( Mvc_CoverReadCubeNum(pQuo) ); + + Mvc_CoverFree( pDiv ); + Mvc_CoverFree( pRem ); + + // check the trivial case + if ( Mvc_CoverReadCubeNum(pQuo) == 1 ) + { + eNode = Dec_FactorLF_rec( pFForm, pCover, pQuo ); + Mvc_CoverFree( pQuo ); + return eNode; + } + + // make the quotient cube free + Mvc_CoverMakeCubeFree( pQuo ); + + // divide the cover by the quotient + Mvc_CoverDivideInternal( pCover, pQuo, &pDiv, &pRem ); + + // check the trivial case + if ( Mvc_CoverIsCubeFree( pDiv ) ) + { + eNodeDiv = Dec_Factor_rec( pFForm, pDiv ); + eNodeQuo = Dec_Factor_rec( pFForm, pQuo ); + Mvc_CoverFree( pDiv ); + Mvc_CoverFree( pQuo ); + eNodeAnd = Dec_GraphAddNodeAnd( pFForm, eNodeDiv, eNodeQuo ); + if ( Mvc_CoverReadCubeNum(pRem) == 0 ) + { + Mvc_CoverFree( pRem ); + return eNodeAnd; + } + else + { + eNodeRem = Dec_Factor_rec( pFForm, pRem ); + Mvc_CoverFree( pRem ); + return Dec_GraphAddNodeOr( pFForm, eNodeAnd, eNodeRem ); + } + } + + // get the common cube + pCom = Mvc_CoverCommonCubeCover( pDiv ); + Mvc_CoverFree( pDiv ); + Mvc_CoverFree( pQuo ); + Mvc_CoverFree( pRem ); + + // solve the simple problem + eNode = Dec_FactorLF_rec( pFForm, pCover, pCom ); + Mvc_CoverFree( pCom ); + return eNode; +} + + +/**Function************************************************************* + + Synopsis [Internal recursive factoring procedure for the leaf case.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Edge_t Dec_FactorLF_rec( Dec_Graph_t * pFForm, Mvc_Cover_t * pCover, Mvc_Cover_t * pSimple ) +{ + Dec_Man_t * pManDec = Abc_FrameReadManDec(); + Vec_Int_t * vEdgeLits = pManDec->vLits; + Mvc_Cover_t * pDiv, * pQuo, * pRem; + Dec_Edge_t eNodeDiv, eNodeQuo, eNodeRem; + Dec_Edge_t eNodeAnd; + + // get the most often occurring literal + pDiv = Mvc_CoverBestLiteralCover( pCover, pSimple ); + // divide the cover by the literal + Mvc_CoverDivideByLiteral( pCover, pDiv, &pQuo, &pRem ); + // get the node pointer for the literal + eNodeDiv = Dec_FactorTrivialCube( pFForm, pDiv, Mvc_CoverReadCubeHead(pDiv), vEdgeLits ); + Mvc_CoverFree( pDiv ); + // factor the quotient and remainder + eNodeQuo = Dec_Factor_rec( pFForm, pQuo ); + Mvc_CoverFree( pQuo ); + eNodeAnd = Dec_GraphAddNodeAnd( pFForm, eNodeDiv, eNodeQuo ); + if ( Mvc_CoverReadCubeNum(pRem) == 0 ) + { + Mvc_CoverFree( pRem ); + return eNodeAnd; + } + else + { + eNodeRem = Dec_Factor_rec( pFForm, pRem ); + Mvc_CoverFree( pRem ); + return Dec_GraphAddNodeOr( pFForm, eNodeAnd, eNodeRem ); + } +} + + + +/**Function************************************************************* + + Synopsis [Factoring the cover, which has no algebraic divisors.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Edge_t Dec_FactorTrivial( Dec_Graph_t * pFForm, Mvc_Cover_t * pCover ) +{ + Dec_Man_t * pManDec = Abc_FrameReadManDec(); + Vec_Int_t * vEdgeCubes = pManDec->vCubes; + Vec_Int_t * vEdgeLits = pManDec->vLits; + Mvc_Manager_t * pMem = pManDec->pMvcMem; + Dec_Edge_t eNode; + Mvc_Cube_t * pCube; + // create the factored form for each cube + Vec_IntClear( vEdgeCubes ); + Mvc_CoverForEachCube( pCover, pCube ) + { + eNode = Dec_FactorTrivialCube( pFForm, pCover, pCube, vEdgeLits ); + Vec_IntPush( vEdgeCubes, Dec_EdgeToInt_(eNode) ); + } + // balance the factored forms + return Dec_FactorTrivialTree_rec( pFForm, (Dec_Edge_t *)vEdgeCubes->pArray, vEdgeCubes->nSize, 1 ); +} + +/**Function************************************************************* + + Synopsis [Factoring the cube.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Edge_t Dec_FactorTrivialCube( Dec_Graph_t * pFForm, Mvc_Cover_t * pCover, Mvc_Cube_t * pCube, Vec_Int_t * vEdgeLits ) +{ + Dec_Edge_t eNode; + int iBit, Value; + // create the factored form for each literal + Vec_IntClear( vEdgeLits ); + Mvc_CubeForEachBit( pCover, pCube, iBit, Value ) + if ( Value ) + { + eNode = Dec_EdgeCreate( iBit/2, iBit%2 ); // CST + Vec_IntPush( vEdgeLits, Dec_EdgeToInt_(eNode) ); + } + // balance the factored forms + return Dec_FactorTrivialTree_rec( pFForm, (Dec_Edge_t *)vEdgeLits->pArray, vEdgeLits->nSize, 0 ); +} + +/**Function************************************************************* + + Synopsis [Create the well-balanced tree of nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Edge_t Dec_FactorTrivialTree_rec( Dec_Graph_t * pFForm, Dec_Edge_t * peNodes, int nNodes, int fNodeOr ) +{ + Dec_Edge_t eNode1, eNode2; + int nNodes1, nNodes2; + + if ( nNodes == 1 ) + return peNodes[0]; + + // split the nodes into two parts + nNodes1 = nNodes/2; + nNodes2 = nNodes - nNodes1; +// nNodes2 = nNodes/2; +// nNodes1 = nNodes - nNodes2; + + // recursively construct the tree for the parts + eNode1 = Dec_FactorTrivialTree_rec( pFForm, peNodes, nNodes1, fNodeOr ); + eNode2 = Dec_FactorTrivialTree_rec( pFForm, peNodes + nNodes1, nNodes2, fNodeOr ); + + if ( fNodeOr ) + return Dec_GraphAddNodeOr( pFForm, eNode1, eNode2 ); + else + return Dec_GraphAddNodeAnd( pFForm, eNode1, eNode2 ); +} + + + +/**Function************************************************************* + + Synopsis [Converts SOP into MVC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Mvc_Cover_t * Dec_ConvertSopToMvc( char * pSop ) +{ + Dec_Man_t * pManDec = Abc_FrameReadManDec(); + Mvc_Manager_t * pMem = pManDec->pMvcMem; + Mvc_Cover_t * pMvc; + Mvc_Cube_t * pMvcCube; + char * pCube; + int nVars, Value, v; + + // start the cover + nVars = Abc_SopGetVarNum(pSop); + pMvc = Mvc_CoverAlloc( pMem, nVars * 2 ); + // check the logic function of the node + Abc_SopForEachCube( pSop, nVars, pCube ) + { + // create and add the cube + pMvcCube = Mvc_CubeAlloc( pMvc ); + Mvc_CoverAddCubeTail( pMvc, pMvcCube ); + // fill in the literals + Mvc_CubeBitFill( pMvcCube ); + Abc_CubeForEachVar( pCube, Value, v ) + { + if ( Value == '0' ) + Mvc_CubeBitRemove( pMvcCube, v * 2 + 1 ); + else if ( Value == '1' ) + Mvc_CubeBitRemove( pMvcCube, v * 2 ); + } + } + return pMvc; +} + +/**Function************************************************************* + + Synopsis [Verifies that the factoring is correct.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dec_FactorVerify( char * pSop, Dec_Graph_t * pFForm ) +{ + DdManager * dd = Abc_FrameReadManDd(); + DdNode * bFunc1, * bFunc2; + int RetValue; + bFunc1 = Abc_ConvertSopToBdd( dd, pSop ); Cudd_Ref( bFunc1 ); + bFunc2 = Dec_GraphDeriveBdd( dd, pFForm ); Cudd_Ref( bFunc2 ); +//Extra_bddPrint( dd, bFunc1 ); printf("\n"); +//Extra_bddPrint( dd, bFunc2 ); printf("\n"); + RetValue = (bFunc1 == bFunc2); + if ( bFunc1 != bFunc2 ) + { + int s; + Extra_bddPrint( dd, bFunc1 ); printf("\n"); + Extra_bddPrint( dd, bFunc2 ); printf("\n"); + s = 0; + } + Cudd_RecursiveDeref( dd, bFunc1 ); + Cudd_RecursiveDeref( dd, bFunc2 ); + return RetValue; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/dec/decMan.c b/abc_with_bb_support/src/opt/dec/decMan.c new file mode 100644 index 000000000..8a33fb39b --- /dev/null +++ b/abc_with_bb_support/src/opt/dec/decMan.c @@ -0,0 +1,83 @@ +/**CFile**************************************************************** + + FileName [decMan.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Decomposition manager.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: decMan.c,v 1.1 2003/05/22 19:20:05 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "mvc.h" +#include "dec.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Start the MVC manager used in the factoring package.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Man_t * Dec_ManStart() +{ + Dec_Man_t * p; + int clk = clock(); + p = ALLOC( Dec_Man_t, 1 ); + p->pMvcMem = Mvc_ManagerStart(); + p->vCubes = Vec_IntAlloc( 8 ); + p->vLits = Vec_IntAlloc( 8 ); + // canonical forms, phases, perms + Extra_Truth4VarNPN( &p->puCanons, &p->pPhases, &p->pPerms, &p->pMap ); +//PRT( "NPN classes precomputation time", clock() - clk ); + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the MVC maanager used in the factoring package.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dec_ManStop( Dec_Man_t * p ) +{ + Mvc_ManagerFree( p->pMvcMem ); + Vec_IntFree( p->vCubes ); + Vec_IntFree( p->vLits ); + free( p->puCanons ); + free( p->pPhases ); + free( p->pPerms ); + free( p->pMap ); + free( p ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/dec/decPrint.c b/abc_with_bb_support/src/opt/dec/decPrint.c new file mode 100644 index 000000000..c5f87975e --- /dev/null +++ b/abc_with_bb_support/src/opt/dec/decPrint.c @@ -0,0 +1,284 @@ +/**CFile**************************************************************** + + FileName [decPrint.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Procedures to print the decomposition graphs (factored forms).] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: decPrint.c,v 1.1 2003/05/22 19:20:05 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "dec.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Dec_GraphPrint_rec( FILE * pFile, Dec_Graph_t * pGraph, Dec_Node_t * pNode, int fCompl, char * pNamesIn[], int * pPos, int LitSizeMax ); +static int Dec_GraphPrintGetLeafName( FILE * pFile, int iLeaf, int fCompl, char * pNamesIn[] ); +static void Dec_GraphPrintUpdatePos( FILE * pFile, int * pPos, int LitSizeMax ); +static int Dec_GraphPrintOutputName( FILE * pFile, char * pNameOut, int fCompl ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Prints the decomposition graph.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dec_GraphPrint( FILE * pFile, Dec_Graph_t * pGraph, char * pNamesIn[], char * pNameOut ) +{ + Vec_Ptr_t * vNamesIn = NULL; + int LitSizeMax, LitSizeCur, Pos, i; + + // create the names if not given by the user + if ( pNamesIn == NULL ) + { + vNamesIn = Abc_NodeGetFakeNames( Dec_GraphLeaveNum(pGraph) ); + pNamesIn = (char **)vNamesIn->pArray; + } + if ( pNameOut == NULL ) + pNameOut = "F"; + + // get the size of the longest literal + LitSizeMax = 0; + for ( i = 0; i < Dec_GraphLeaveNum(pGraph); i++ ) + { + LitSizeCur = strlen(pNamesIn[i]); + if ( LitSizeMax < LitSizeCur ) + LitSizeMax = LitSizeCur; + } + if ( LitSizeMax > 50 ) + LitSizeMax = 20; + + // write the decomposition graph (factored form) + if ( Dec_GraphIsConst(pGraph) ) // constant + { + Pos = Dec_GraphPrintOutputName( pFile, pNameOut, 0 ); + fprintf( pFile, "Constant %d", !Dec_GraphIsComplement(pGraph) ); + } + else if ( Dec_GraphIsVar(pGraph) ) // literal + { + Pos = Dec_GraphPrintOutputName( pFile, pNameOut, 0 ); + Dec_GraphPrintGetLeafName( pFile, Dec_GraphVarInt(pGraph), Dec_GraphIsComplement(pGraph), pNamesIn ); + } + else + { + Pos = Dec_GraphPrintOutputName( pFile, pNameOut, Dec_GraphIsComplement(pGraph) ); + Dec_GraphPrint_rec( pFile, pGraph, Dec_GraphNodeLast(pGraph), 0, pNamesIn, &Pos, LitSizeMax ); + } + fprintf( pFile, "\n" ); + + if ( vNamesIn ) + Abc_NodeFreeNames( vNamesIn ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dec_GraphPrint2_rec( FILE * pFile, Dec_Graph_t * pGraph, Dec_Node_t * pNode, int fCompl, char * pNamesIn[], int * pPos, int LitSizeMax ) +{ + Dec_Node_t * pNode0, * pNode1; + pNode0 = Dec_GraphNode(pGraph, pNode->eEdge0.Node); + pNode1 = Dec_GraphNode(pGraph, pNode->eEdge1.Node); + if ( Dec_GraphNodeIsVar(pGraph, pNode) ) // FT_NODE_LEAF ) + { + (*pPos) += Dec_GraphPrintGetLeafName( pFile, Dec_GraphNodeInt(pGraph,pNode), fCompl, pNamesIn ); + return; + } + if ( !pNode->fNodeOr ) // FT_NODE_AND ) + { + if ( !pNode0->fNodeOr ) // != FT_NODE_OR ) + Dec_GraphPrint_rec( pFile, pGraph, pNode0, pNode->fCompl0, pNamesIn, pPos, LitSizeMax ); + else + { + fprintf( pFile, "(" ); + (*pPos)++; + Dec_GraphPrint_rec( pFile, pGraph, pNode0, pNode->fCompl0, pNamesIn, pPos, LitSizeMax ); + fprintf( pFile, ")" ); + (*pPos)++; + } + fprintf( pFile, " " ); + (*pPos)++; + + Dec_GraphPrintUpdatePos( pFile, pPos, LitSizeMax ); + + if ( !pNode1->fNodeOr ) // != FT_NODE_OR ) + Dec_GraphPrint_rec( pFile, pGraph, pNode1, pNode->fCompl1, pNamesIn, pPos, LitSizeMax ); + else + { + fprintf( pFile, "(" ); + (*pPos)++; + Dec_GraphPrint_rec( pFile, pGraph, pNode1, pNode->fCompl1, pNamesIn, pPos, LitSizeMax ); + fprintf( pFile, ")" ); + (*pPos)++; + } + return; + } + if ( pNode->fNodeOr ) // FT_NODE_OR ) + { + Dec_GraphPrint_rec( pFile, pGraph, pNode0, pNode->fCompl0, pNamesIn, pPos, LitSizeMax ); + fprintf( pFile, " + " ); + (*pPos) += 3; + + Dec_GraphPrintUpdatePos( pFile, pPos, LitSizeMax ); + + Dec_GraphPrint_rec( pFile, pGraph, pNode1, pNode->fCompl1, pNamesIn, pPos, LitSizeMax ); + return; + } + assert( 0 ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dec_GraphPrint_rec( FILE * pFile, Dec_Graph_t * pGraph, Dec_Node_t * pNode, int fCompl, char * pNamesIn[], int * pPos, int LitSizeMax ) +{ + Dec_Node_t * pNode0, * pNode1; + Dec_Node_t * pNode00, * pNode01, * pNode10, * pNode11; + pNode0 = Dec_GraphNode(pGraph, pNode->eEdge0.Node); + pNode1 = Dec_GraphNode(pGraph, pNode->eEdge1.Node); + if ( Dec_GraphNodeIsVar(pGraph, pNode) ) // FT_NODE_LEAF ) + { + (*pPos) += Dec_GraphPrintGetLeafName( pFile, Dec_GraphNodeInt(pGraph,pNode), fCompl, pNamesIn ); + return; + } + if ( !Dec_GraphNodeIsVar(pGraph, pNode0) && !Dec_GraphNodeIsVar(pGraph, pNode1) ) + { + pNode00 = Dec_GraphNode(pGraph, pNode0->eEdge0.Node); + pNode01 = Dec_GraphNode(pGraph, pNode0->eEdge1.Node); + pNode10 = Dec_GraphNode(pGraph, pNode1->eEdge0.Node); + pNode11 = Dec_GraphNode(pGraph, pNode1->eEdge1.Node); + if ( (pNode00 == pNode10 || pNode00 == pNode11) && (pNode01 == pNode10 || pNode01 == pNode11) ) + { + fprintf( pFile, "(" ); + (*pPos)++; + Dec_GraphPrint_rec( pFile, pGraph, pNode00, pNode00->fCompl0, pNamesIn, pPos, LitSizeMax ); + fprintf( pFile, " # " ); + (*pPos) += 3; + Dec_GraphPrint_rec( pFile, pGraph, pNode01, pNode01->fCompl1, pNamesIn, pPos, LitSizeMax ); + fprintf( pFile, ")" ); + (*pPos)++; + return; + } + } + if ( fCompl ) + { + fprintf( pFile, "(" ); + (*pPos)++; + Dec_GraphPrint_rec( pFile, pGraph, pNode0, !pNode->fCompl0, pNamesIn, pPos, LitSizeMax ); + fprintf( pFile, " + " ); + (*pPos) += 3; + Dec_GraphPrint_rec( pFile, pGraph, pNode1, !pNode->fCompl1, pNamesIn, pPos, LitSizeMax ); + fprintf( pFile, ")" ); + (*pPos)++; + } + else + { + fprintf( pFile, "(" ); + (*pPos)++; + Dec_GraphPrint_rec( pFile, pGraph, pNode0, pNode->fCompl0, pNamesIn, pPos, LitSizeMax ); + Dec_GraphPrint_rec( pFile, pGraph, pNode1, pNode->fCompl1, pNamesIn, pPos, LitSizeMax ); + fprintf( pFile, ")" ); + (*pPos)++; + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dec_GraphPrintGetLeafName( FILE * pFile, int iLeaf, int fCompl, char * pNamesIn[] ) +{ + static char Buffer[100]; + sprintf( Buffer, "%s%s", pNamesIn[iLeaf], fCompl? "\'" : "" ); + fprintf( pFile, "%s", Buffer ); + return strlen( Buffer ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Dec_GraphPrintUpdatePos( FILE * pFile, int * pPos, int LitSizeMax ) +{ + int i; + if ( *pPos + LitSizeMax < 77 ) + return; + fprintf( pFile, "\n" ); + for ( i = 0; i < 10; i++ ) + fprintf( pFile, " " ); + *pPos = 10; +} + +/**Function************************************************************* + + Synopsis [Starts the printout for a decomposition graph.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Dec_GraphPrintOutputName( FILE * pFile, char * pNameOut, int fCompl ) +{ + if ( pNameOut == NULL ) + return 0; + fprintf( pFile, "%6s%s = ", pNameOut, fCompl? "\'" : " " ); + return 10; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/dec/decUtil.c b/abc_with_bb_support/src/opt/dec/decUtil.c new file mode 100644 index 000000000..070d1b8bf --- /dev/null +++ b/abc_with_bb_support/src/opt/dec/decUtil.c @@ -0,0 +1,134 @@ +/**CFile**************************************************************** + + FileName [decUtil.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Decomposition unitilies.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: decUtil.c,v 1.1 2003/05/22 19:20:05 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "dec.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Converts graph to BDD.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +DdNode * Dec_GraphDeriveBdd( DdManager * dd, Dec_Graph_t * pGraph ) +{ + DdNode * bFunc, * bFunc0, * bFunc1; + Dec_Node_t * pNode; + int i; + + // sanity checks + assert( Dec_GraphLeaveNum(pGraph) >= 0 ); + assert( Dec_GraphLeaveNum(pGraph) <= pGraph->nSize ); + + // check for constant function + if ( Dec_GraphIsConst(pGraph) ) + return Cudd_NotCond( b1, Dec_GraphIsComplement(pGraph) ); + // check for a literal + if ( Dec_GraphIsVar(pGraph) ) + return Cudd_NotCond( Cudd_bddIthVar(dd, Dec_GraphVarInt(pGraph)), Dec_GraphIsComplement(pGraph) ); + + // assign the elementary variables + Dec_GraphForEachLeaf( pGraph, pNode, i ) + pNode->pFunc = Cudd_bddIthVar( dd, i ); + + // compute the function for each internal node + Dec_GraphForEachNode( pGraph, pNode, i ) + { + bFunc0 = Cudd_NotCond( Dec_GraphNode(pGraph, pNode->eEdge0.Node)->pFunc, pNode->eEdge0.fCompl ); + bFunc1 = Cudd_NotCond( Dec_GraphNode(pGraph, pNode->eEdge1.Node)->pFunc, pNode->eEdge1.fCompl ); + pNode->pFunc = Cudd_bddAnd( dd, bFunc0, bFunc1 ); Cudd_Ref( pNode->pFunc ); + } + + // deref the intermediate results + bFunc = pNode->pFunc; Cudd_Ref( bFunc ); + Dec_GraphForEachNode( pGraph, pNode, i ) + Cudd_RecursiveDeref( dd, pNode->pFunc ); + Cudd_Deref( bFunc ); + + // complement the result if necessary + return Cudd_NotCond( bFunc, Dec_GraphIsComplement(pGraph) ); +} + +/**Function************************************************************* + + Synopsis [Derives the truth table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Dec_GraphDeriveTruth( Dec_Graph_t * pGraph ) +{ + unsigned uTruths[5] = { 0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, 0xFF00FF00, 0xFFFF0000 }; + unsigned uTruth, uTruth0, uTruth1; + Dec_Node_t * pNode; + int i; + + // sanity checks + assert( Dec_GraphLeaveNum(pGraph) >= 0 ); + assert( Dec_GraphLeaveNum(pGraph) <= pGraph->nSize ); + assert( Dec_GraphLeaveNum(pGraph) <= 5 ); + + // check for constant function + if ( Dec_GraphIsConst(pGraph) ) + return Dec_GraphIsComplement(pGraph)? 0 : ~((unsigned)0); + // check for a literal + if ( Dec_GraphIsVar(pGraph) ) + return Dec_GraphIsComplement(pGraph)? ~uTruths[Dec_GraphVarInt(pGraph)] : uTruths[Dec_GraphVarInt(pGraph)]; + + // assign the elementary variables + Dec_GraphForEachLeaf( pGraph, pNode, i ) + pNode->pFunc = (void *)uTruths[i]; + + // compute the function for each internal node + Dec_GraphForEachNode( pGraph, pNode, i ) + { + uTruth0 = (unsigned)Dec_GraphNode(pGraph, pNode->eEdge0.Node)->pFunc; + uTruth1 = (unsigned)Dec_GraphNode(pGraph, pNode->eEdge1.Node)->pFunc; + uTruth0 = pNode->eEdge0.fCompl? ~uTruth0 : uTruth0; + uTruth1 = pNode->eEdge1.fCompl? ~uTruth1 : uTruth1; + uTruth = uTruth0 & uTruth1; + pNode->pFunc = (void *)uTruth; + } + + // complement the result if necessary + return Dec_GraphIsComplement(pGraph)? ~uTruth : uTruth; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/dec/module.make b/abc_with_bb_support/src/opt/dec/module.make new file mode 100644 index 000000000..1e0722d57 --- /dev/null +++ b/abc_with_bb_support/src/opt/dec/module.make @@ -0,0 +1,5 @@ +SRC += src/opt/dec/decAbc.c \ + src/opt/dec/decFactor.c \ + src/opt/dec/decMan.c \ + src/opt/dec/decPrint.c \ + src/opt/dec/decUtil.c diff --git a/abc_with_bb_support/src/opt/fxu/fxu.c b/abc_with_bb_support/src/opt/fxu/fxu.c new file mode 100644 index 000000000..053718b20 --- /dev/null +++ b/abc_with_bb_support/src/opt/fxu/fxu.c @@ -0,0 +1,252 @@ +/**CFile**************************************************************** + + FileName [fxu.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [The entrance into the fast extract module.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: fxu.c,v 1.0 2003/02/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fxuInt.h" +#include "fxu.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*===== fxuCreate.c ====================================================*/ +extern Fxu_Matrix * Fxu_CreateMatrix( Fxu_Data_t * pData ); +extern void Fxu_CreateCovers( Fxu_Matrix * p, Fxu_Data_t * pData ); + +static int s_MemoryTotal; +static int s_MemoryPeak; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs fast_extract on a set of covers.] + + Description [All the covers are given in the array p->vSops. + The resulting covers are returned in the array p->vSopsNew. + The entries in these arrays correspond to objects in the network. + The entries corresponding to the PI and objects with trivial covers are NULL. + The number of extracted covers (not exceeding p->nNodesExt) is returned. + Two other things are important for the correct operation of this procedure: + (1) The input covers do not have duplicated fanins and are SCC-free. + (2) The fanins array contains the numbers of the fanin objects.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fxu_FastExtract( Fxu_Data_t * pData ) +{ + Fxu_Matrix * p; + Fxu_Single * pSingle; + Fxu_Double * pDouble; + int Weight1, Weight2, Weight3; + + s_MemoryTotal = 0; + s_MemoryPeak = 0; + + // create the matrix + p = Fxu_CreateMatrix( pData ); + if ( p == NULL ) + return -1; +// if ( pData->fVerbose ) +// printf( "Memory usage after construction: Total = %d. Peak = %d.\n", s_MemoryTotal, s_MemoryPeak ); +//Fxu_MatrixPrint( NULL, p ); + + if ( pData->fOnlyS ) + { + pData->nNodesNew = 0; + do + { + Weight1 = Fxu_HeapSingleReadMaxWeight( p->pHeapSingle ); + if ( pData->fVerbose ) + printf( "Best single = %3d.\n", Weight1 ); + if ( Weight1 > 0 || Weight1 == 0 && pData->fUse0 ) + Fxu_UpdateSingle( p ); + else + break; + } + while ( ++pData->nNodesNew < pData->nNodesExt ); + } + else if ( pData->fOnlyD ) + { + pData->nNodesNew = 0; + do + { + Weight2 = Fxu_HeapDoubleReadMaxWeight( p->pHeapDouble ); + if ( pData->fVerbose ) + printf( "Best double = %3d.\n", Weight2 ); + if ( Weight2 > 0 || Weight2 == 0 && pData->fUse0 ) + Fxu_UpdateDouble( p ); + else + break; + } + while ( ++pData->nNodesNew < pData->nNodesExt ); + } + else if ( !pData->fUseCompl ) + { + pData->nNodesNew = 0; + do + { + Weight1 = Fxu_HeapSingleReadMaxWeight( p->pHeapSingle ); + Weight2 = Fxu_HeapDoubleReadMaxWeight( p->pHeapDouble ); + + if ( pData->fVerbose ) + printf( "Best double = %3d. Best single = %3d.\n", Weight2, Weight1 ); +//Fxu_Select( p, &pSingle, &pDouble ); + + if ( Weight1 >= Weight2 ) + { + if ( Weight1 > 0 || Weight1 == 0 && pData->fUse0 ) + Fxu_UpdateSingle( p ); + else + break; + } + else + { + if ( Weight2 > 0 || Weight2 == 0 && pData->fUse0 ) + Fxu_UpdateDouble( p ); + else + break; + } + } + while ( ++pData->nNodesNew < pData->nNodesExt ); + } + else + { // use the complement + pData->nNodesNew = 0; + do + { + Weight1 = Fxu_HeapSingleReadMaxWeight( p->pHeapSingle ); + Weight2 = Fxu_HeapDoubleReadMaxWeight( p->pHeapDouble ); + + // select the best single and double + Weight3 = Fxu_Select( p, &pSingle, &pDouble ); + if ( pData->fVerbose ) + printf( "Best double = %3d. Best single = %3d. Best complement = %3d.\n", + Weight2, Weight1, Weight3 ); + + if ( Weight3 > 0 || Weight3 == 0 && pData->fUse0 ) + Fxu_Update( p, pSingle, pDouble ); + else + break; + } + while ( ++pData->nNodesNew < pData->nNodesExt ); + } + + if ( pData->fVerbose ) + printf( "Total single = %3d. Total double = %3d. Total compl = %3d.\n", p->nDivs1, p->nDivs2, p->nDivs3 ); + + // create the new covers + if ( pData->nNodesNew ) + Fxu_CreateCovers( p, pData ); + Fxu_MatrixDelete( p ); +// printf( "Memory usage after deallocation: Total = %d. Peak = %d.\n", s_MemoryTotal, s_MemoryPeak ); + if ( pData->nNodesNew == pData->nNodesExt ) + printf( "Warning: The limit on the number of extracted divisors has been reached.\n" ); + return pData->nNodesNew; +} + + +/**Function************************************************************* + + Synopsis [Unmarks the cubes in the ring.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_MatrixRingCubesUnmark( Fxu_Matrix * p ) +{ + Fxu_Cube * pCube, * pCube2; + // unmark the cubes + Fxu_MatrixForEachCubeInRingSafe( p, pCube, pCube2 ) + pCube->pOrder = NULL; + Fxu_MatrixRingCubesReset( p ); +} + + +/**Function************************************************************* + + Synopsis [Unmarks the vars in the ring.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_MatrixRingVarsUnmark( Fxu_Matrix * p ) +{ + Fxu_Var * pVar, * pVar2; + // unmark the vars + Fxu_MatrixForEachVarInRingSafe( p, pVar, pVar2 ) + pVar->pOrder = NULL; + Fxu_MatrixRingVarsReset( p ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Fxu_MemFetch( Fxu_Matrix * p, int nBytes ) +{ + s_MemoryTotal += nBytes; + if ( s_MemoryPeak < s_MemoryTotal ) + s_MemoryPeak = s_MemoryTotal; +// return malloc( nBytes ); + return Extra_MmFixedEntryFetch( p->pMemMan ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_MemRecycle( Fxu_Matrix * p, char * pItem, int nBytes ) +{ + s_MemoryTotal -= nBytes; +// free( pItem ); + Extra_MmFixedEntryRecycle( p->pMemMan, pItem ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/fxu/fxu.h b/abc_with_bb_support/src/opt/fxu/fxu.h new file mode 100644 index 000000000..788978a13 --- /dev/null +++ b/abc_with_bb_support/src/opt/fxu/fxu.h @@ -0,0 +1,92 @@ +/**CFile**************************************************************** + + FileName [fxu.h] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [External declarations of fast extract for unate covers.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: fxu.h,v 1.0 2003/02/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __FXU_H__ +#define __FXU_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include "vec.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#ifndef __cplusplus +#ifndef bool +#define bool int +#endif +#endif + +typedef struct FxuDataStruct Fxu_Data_t; + +// structure for the FX input/output data +struct FxuDataStruct +{ + // user specified parameters + bool fOnlyS; // set to 1 to have only single-cube divs + bool fOnlyD; // set to 1 to have only double-cube divs + bool fUse0; // set to 1 to have 0-weight also extracted + bool fUseCompl; // set to 1 to have complement taken into account + bool fVerbose; // set to 1 to have verbose output + int nNodesExt; // the number of divisors to extract + int nPairsMax; // the maximum number of cube pairs to consider + // the input information + Vec_Ptr_t * vSops; // the SOPs for each node in the network + Vec_Ptr_t * vFanins; // the fanins of each node in the network + // output information + Vec_Ptr_t * vSopsNew; // the SOPs for each node in the network after extraction + Vec_Ptr_t * vFaninsNew; // the fanins of each node in the network after extraction + // the SOP manager + Extra_MmFlex_t * pManSop; + // statistics + int nNodesOld; // the old number of nodes + int nNodesNew; // the number of divisors actually extracted +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/*===== fxu.c ==========================================================*/ +extern int Fxu_FastExtract( Fxu_Data_t * pData ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/opt/fxu/fxuCreate.c b/abc_with_bb_support/src/opt/fxu/fxuCreate.c new file mode 100644 index 000000000..b9e2c673a --- /dev/null +++ b/abc_with_bb_support/src/opt/fxu/fxuCreate.c @@ -0,0 +1,418 @@ +/**CFile**************************************************************** + + FileName [fxuCreate.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Create matrix from covers and covers from matrix.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: fxuCreate.c,v 1.0 2003/02/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "fxuInt.h" +#include "fxu.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Fxu_CreateMatrixAddCube( Fxu_Matrix * p, Fxu_Cube * pCube, char * pSopCube, Vec_Int_t * vFanins, int * pOrder ); +static int Fxu_CreateMatrixLitCompare( int * ptrX, int * ptrY ); +static void Fxu_CreateCoversNode( Fxu_Matrix * p, Fxu_Data_t * pData, int iNode, Fxu_Cube * pCubeFirst, Fxu_Cube * pCubeNext ); +static Fxu_Cube * Fxu_CreateCoversFirstCube( Fxu_Matrix * p, Fxu_Data_t * pData, int iNode ); +static int * s_pLits; + +extern int Fxu_PreprocessCubePairs( Fxu_Matrix * p, Vec_Ptr_t * vCovers, int nPairsTotal, int nPairsMax ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Creates the sparse matrix from the array of SOPs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fxu_Matrix * Fxu_CreateMatrix( Fxu_Data_t * pData ) +{ + Fxu_Matrix * p; + Fxu_Var * pVar; + Fxu_Cube * pCubeFirst, * pCubeNew; + Fxu_Cube * pCube1, * pCube2; + Vec_Int_t * vFanins; + char * pSopCover; + char * pSopCube; + int * pOrder, nBitsMax; + int i, v, c; + int nCubesTotal; + int nPairsTotal; + int nPairsStore; + int nCubes; + int iCube, iPair; + int nFanins; + + // collect all sorts of statistics + nCubesTotal = 0; + nPairsTotal = 0; + nPairsStore = 0; + nBitsMax = -1; + for ( i = 0; i < pData->nNodesOld; i++ ) + if ( pSopCover = pData->vSops->pArray[i] ) + { + nCubes = Abc_SopGetCubeNum( pSopCover ); + nFanins = Abc_SopGetVarNum( pSopCover ); + assert( nFanins > 1 && nCubes > 0 ); + + nCubesTotal += nCubes; + nPairsTotal += nCubes * (nCubes - 1) / 2; + nPairsStore += nCubes * nCubes; + if ( nBitsMax < nFanins ) + nBitsMax = nFanins; + } + if ( nBitsMax <= 0 ) + { + printf( "The current network does not have SOPs to perform extraction.\n" ); + return NULL; + } +/* + if ( nPairsStore > 10000000 ) + { + printf( "The problem is too large to be solved by \"fxu\" (%d cubes and %d cube pairs)\n", nCubesTotal, nPairsStore ); + return NULL; + } +*/ + // start the matrix + p = Fxu_MatrixAllocate(); + // create the column labels + p->ppVars = ALLOC( Fxu_Var *, 2 * (pData->nNodesOld + pData->nNodesExt) ); + for ( i = 0; i < 2 * pData->nNodesOld; i++ ) + p->ppVars[i] = Fxu_MatrixAddVar( p ); + + // allocate storage for all cube pairs at once + p->pppPairs = ALLOC( Fxu_Pair **, nCubesTotal + 100 ); + p->ppPairs = ALLOC( Fxu_Pair *, nPairsStore + 100 ); + memset( p->ppPairs, 0, sizeof(Fxu_Pair *) * nPairsStore ); + iCube = 0; + iPair = 0; + for ( i = 0; i < pData->nNodesOld; i++ ) + if ( pSopCover = pData->vSops->pArray[i] ) + { + // get the number of cubes + nCubes = Abc_SopGetCubeNum( pSopCover ); + // get the new var in the matrix + pVar = p->ppVars[2*i+1]; + // assign the pair storage + pVar->nCubes = nCubes; + if ( nCubes > 0 ) + { + pVar->ppPairs = p->pppPairs + iCube; + pVar->ppPairs[0] = p->ppPairs + iPair; + for ( v = 1; v < nCubes; v++ ) + pVar->ppPairs[v] = pVar->ppPairs[v-1] + nCubes; + } + // update + iCube += nCubes; + iPair += nCubes * nCubes; + } + assert( iCube == nCubesTotal ); + assert( iPair == nPairsStore ); + + + // allocate room for the reordered literals + pOrder = ALLOC( int, nBitsMax ); + // create the rows + for ( i = 0; i < pData->nNodesOld; i++ ) + if ( pSopCover = pData->vSops->pArray[i] ) + { + // get the new var in the matrix + pVar = p->ppVars[2*i+1]; + // here we sort the literals of the cover + // in the increasing order of the numbers of the corresponding nodes + // because literals should be added to the matrix in this order + vFanins = pData->vFanins->pArray[i]; + s_pLits = vFanins->pArray; + // start the variable order + nFanins = Abc_SopGetVarNum( pSopCover ); + for ( v = 0; v < nFanins; v++ ) + pOrder[v] = v; + // reorder the fanins + qsort( (void *)pOrder, nFanins, sizeof(int),(int (*)(const void *, const void *))Fxu_CreateMatrixLitCompare); + assert( s_pLits[ pOrder[0] ] < s_pLits[ pOrder[nFanins-1] ] ); + // create the corresponding cubes in the matrix + pCubeFirst = NULL; + c = 0; + Abc_SopForEachCube( pSopCover, nFanins, pSopCube ) + { + // create the cube + pCubeNew = Fxu_MatrixAddCube( p, pVar, c++ ); + Fxu_CreateMatrixAddCube( p, pCubeNew, pSopCube, vFanins, pOrder ); + if ( pCubeFirst == NULL ) + pCubeFirst = pCubeNew; + pCubeNew->pFirst = pCubeFirst; + } + // set the first cube of this var + pVar->pFirst = pCubeFirst; + // create the divisors without preprocessing + if ( nPairsTotal <= pData->nPairsMax ) + { + for ( pCube1 = pCubeFirst; pCube1; pCube1 = pCube1->pNext ) + for ( pCube2 = pCube1? pCube1->pNext: NULL; pCube2; pCube2 = pCube2->pNext ) + Fxu_MatrixAddDivisor( p, pCube1, pCube2 ); + } + } + FREE( pOrder ); + + // consider the case when cube pairs should be preprocessed + // before adding them to the set of divisors + if ( nPairsTotal > pData->nPairsMax ) + if ( !Fxu_PreprocessCubePairs( p, pData->vSops, nPairsTotal, pData->nPairsMax ) ) + return NULL; + + // add the var pairs to the heap + Fxu_MatrixComputeSingles( p ); + + // print stats + if ( pData->fVerbose ) + { + double Density; + Density = ((double)p->nEntries) / p->lVars.nItems / p->lCubes.nItems; + fprintf( stdout, "Matrix: [vars x cubes] = [%d x %d] ", + p->lVars.nItems, p->lCubes.nItems ); + fprintf( stdout, "Lits = %d Density = %.5f%%\n", + p->nEntries, Density ); + fprintf( stdout, "1-cube divisors = %6d. ", p->lSingles.nItems ); + fprintf( stdout, "2-cube divisors = %6d. ", p->nDivsTotal ); + fprintf( stdout, "Cube pairs = %6d.", nPairsTotal ); + fprintf( stdout, "\n" ); + } +// Fxu_MatrixPrint( stdout, p ); + return p; +} + +/**Function************************************************************* + + Synopsis [Adds one cube with literals to the matrix.] + + Description [Create the cube and literals in the matrix corresponding + to the given cube in the SOP cover. Co-singleton transform is performed here.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_CreateMatrixAddCube( Fxu_Matrix * p, Fxu_Cube * pCube, char * pSopCube, Vec_Int_t * vFanins, int * pOrder ) +{ + Fxu_Var * pVar; + int Value, i; + // add literals to the matrix + Abc_CubeForEachVar( pSopCube, Value, i ) + { + Value = pSopCube[pOrder[i]]; + if ( Value == '0' ) + { + pVar = p->ppVars[ 2 * vFanins->pArray[pOrder[i]] + 1 ]; // CST + Fxu_MatrixAddLiteral( p, pCube, pVar ); + } + else if ( Value == '1' ) + { + pVar = p->ppVars[ 2 * vFanins->pArray[pOrder[i]] ]; // CST + Fxu_MatrixAddLiteral( p, pCube, pVar ); + } + } +} + + +/**Function************************************************************* + + Synopsis [Creates the new array of Sop covers from the sparse matrix.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_CreateCovers( Fxu_Matrix * p, Fxu_Data_t * pData ) +{ + Fxu_Cube * pCube, * pCubeFirst, * pCubeNext; + char * pSopCover; + int iNode, n; + + // get the first cube of the first internal node + pCubeFirst = Fxu_CreateCoversFirstCube( p, pData, 0 ); + + // go through the internal nodes + for ( n = 0; n < pData->nNodesOld; n++ ) + if ( pSopCover = pData->vSops->pArray[n] ) + { + // get the number of this node + iNode = n; + // get the next first cube + pCubeNext = Fxu_CreateCoversFirstCube( p, pData, iNode + 1 ); + // check if there any new variables in these cubes + for ( pCube = pCubeFirst; pCube != pCubeNext; pCube = pCube->pNext ) + if ( pCube->lLits.pTail && pCube->lLits.pTail->iVar >= 2 * pData->nNodesOld ) + break; + if ( pCube != pCubeNext ) + Fxu_CreateCoversNode( p, pData, iNode, pCubeFirst, pCubeNext ); + // update the first cube + pCubeFirst = pCubeNext; + } + + // add the covers for the extracted nodes + for ( n = 0; n < pData->nNodesNew; n++ ) + { + // get the number of this node + iNode = pData->nNodesOld + n; + // get the next first cube + pCubeNext = Fxu_CreateCoversFirstCube( p, pData, iNode + 1 ); + // the node should be added + Fxu_CreateCoversNode( p, pData, iNode, pCubeFirst, pCubeNext ); + // update the first cube + pCubeFirst = pCubeNext; + } +} + +/**Function************************************************************* + + Synopsis [Create Sop covers for one node that has changed.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_CreateCoversNode( Fxu_Matrix * p, Fxu_Data_t * pData, int iNode, Fxu_Cube * pCubeFirst, Fxu_Cube * pCubeNext ) +{ + Vec_Int_t * vInputsNew; + char * pSopCover, * pSopCube; + Fxu_Var * pVar; + Fxu_Cube * pCube; + Fxu_Lit * pLit; + int iNum, nCubes, v; + + // collect positive polarity variable in the cubes between pCubeFirst and pCubeNext + Fxu_MatrixRingVarsStart( p ); + for ( pCube = pCubeFirst; pCube != pCubeNext; pCube = pCube->pNext ) + for ( pLit = pCube->lLits.pHead; pLit; pLit = pLit->pHNext ) + { + pVar = p->ppVars[ 2 * (pLit->pVar->iVar/2) + 1 ]; + if ( pVar->pOrder == NULL ) + Fxu_MatrixRingVarsAdd( p, pVar ); + } + Fxu_MatrixRingVarsStop( p ); + + // collect the variable numbers + vInputsNew = Vec_IntAlloc( 4 ); + Fxu_MatrixForEachVarInRing( p, pVar ) + Vec_IntPush( vInputsNew, pVar->iVar / 2 ); + Fxu_MatrixRingVarsUnmark( p ); + + // sort the vars by their number + Vec_IntSort( vInputsNew, 0 ); + + // mark the vars with their numbers in the sorted array + for ( v = 0; v < vInputsNew->nSize; v++ ) + { + p->ppVars[ 2 * vInputsNew->pArray[v] + 0 ]->lLits.nItems = v; // hack - reuse lLits.nItems + p->ppVars[ 2 * vInputsNew->pArray[v] + 1 ]->lLits.nItems = v; // hack - reuse lLits.nItems + } + + // count the number of cubes + nCubes = 0; + for ( pCube = pCubeFirst; pCube != pCubeNext; pCube = pCube->pNext ) + if ( pCube->lLits.nItems ) + nCubes++; + + // allocate room for the new cover + pSopCover = Abc_SopStart( pData->pManSop, nCubes, vInputsNew->nSize ); + // set the correct polarity of the cover + if ( iNode < pData->nNodesOld && Abc_SopGetPhase( pData->vSops->pArray[iNode] ) == 0 ) + Abc_SopComplement( pSopCover ); + + // add the cubes + nCubes = 0; + for ( pCube = pCubeFirst; pCube != pCubeNext; pCube = pCube->pNext ) + { + if ( pCube->lLits.nItems == 0 ) + continue; + // get hold of the SOP cube + pSopCube = pSopCover + nCubes * (vInputsNew->nSize + 3); + // insert literals + for ( pLit = pCube->lLits.pHead; pLit; pLit = pLit->pHNext ) + { + iNum = pLit->pVar->lLits.nItems; // hack - reuse lLits.nItems + assert( iNum < vInputsNew->nSize ); + if ( pLit->pVar->iVar / 2 < pData->nNodesOld ) + pSopCube[iNum] = (pLit->pVar->iVar & 1)? '0' : '1'; // reverse CST + else + pSopCube[iNum] = (pLit->pVar->iVar & 1)? '1' : '0'; // no CST + } + // count the cube + nCubes++; + } + assert( nCubes == Abc_SopGetCubeNum(pSopCover) ); + + // set the new cover and the array of fanins + pData->vSopsNew->pArray[iNode] = pSopCover; + pData->vFaninsNew->pArray[iNode] = vInputsNew; +} + + +/**Function************************************************************* + + Synopsis [Adds the var to storage.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fxu_Cube * Fxu_CreateCoversFirstCube( Fxu_Matrix * p, Fxu_Data_t * pData, int iVar ) +{ + int v; + for ( v = iVar; v < pData->nNodesOld + pData->nNodesNew; v++ ) + if ( p->ppVars[ 2*v + 1 ]->pFirst ) + return p->ppVars[ 2*v + 1 ]->pFirst; + return NULL; +} + +/**Function************************************************************* + + Synopsis [Compares the vars by their number.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fxu_CreateMatrixLitCompare( int * ptrX, int * ptrY ) +{ + return s_pLits[*ptrX] - s_pLits[*ptrY]; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/opt/fxu/fxuHeapD.c b/abc_with_bb_support/src/opt/fxu/fxuHeapD.c new file mode 100644 index 000000000..32598a09e --- /dev/null +++ b/abc_with_bb_support/src/opt/fxu/fxuHeapD.c @@ -0,0 +1,445 @@ +/**CFile**************************************************************** + + FileName [fxuHeapD.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [The priority queue for double cube divisors.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: fxuHeapD.c,v 1.0 2003/02/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fxuInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define FXU_HEAP_DOUBLE_WEIGHT(pDiv) ((pDiv)->Weight) +#define FXU_HEAP_DOUBLE_CURRENT(p, pDiv) ((p)->pTree[(pDiv)->HNum]) +#define FXU_HEAP_DOUBLE_PARENT_EXISTS(p, pDiv) ((pDiv)->HNum > 1) +#define FXU_HEAP_DOUBLE_CHILD1_EXISTS(p, pDiv) (((pDiv)->HNum << 1) <= p->nItems) +#define FXU_HEAP_DOUBLE_CHILD2_EXISTS(p, pDiv) ((((pDiv)->HNum << 1)+1) <= p->nItems) +#define FXU_HEAP_DOUBLE_PARENT(p, pDiv) ((p)->pTree[(pDiv)->HNum >> 1]) +#define FXU_HEAP_DOUBLE_CHILD1(p, pDiv) ((p)->pTree[(pDiv)->HNum << 1]) +#define FXU_HEAP_DOUBLE_CHILD2(p, pDiv) ((p)->pTree[((pDiv)->HNum << 1)+1]) +#define FXU_HEAP_DOUBLE_ASSERT(p, pDiv) assert( (pDiv)->HNum >= 1 && (pDiv)->HNum <= p->nItemsAlloc ) + +static void Fxu_HeapDoubleResize( Fxu_HeapDouble * p ); +static void Fxu_HeapDoubleSwap( Fxu_Double ** pDiv1, Fxu_Double ** pDiv2 ); +static void Fxu_HeapDoubleMoveUp( Fxu_HeapDouble * p, Fxu_Double * pDiv ); +static void Fxu_HeapDoubleMoveDn( Fxu_HeapDouble * p, Fxu_Double * pDiv ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fxu_HeapDouble * Fxu_HeapDoubleStart() +{ + Fxu_HeapDouble * p; + p = ALLOC( Fxu_HeapDouble, 1 ); + memset( p, 0, sizeof(Fxu_HeapDouble) ); + p->nItems = 0; + p->nItemsAlloc = 10000; + p->pTree = ALLOC( Fxu_Double *, p->nItemsAlloc + 1 ); + p->pTree[0] = NULL; + return p; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapDoubleResize( Fxu_HeapDouble * p ) +{ + p->nItemsAlloc *= 2; + p->pTree = REALLOC( Fxu_Double *, p->pTree, p->nItemsAlloc + 1 ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapDoubleStop( Fxu_HeapDouble * p ) +{ + free( p->pTree ); + free( p ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapDoublePrint( FILE * pFile, Fxu_HeapDouble * p ) +{ + Fxu_Double * pDiv; + int Counter = 1; + int Degree = 1; + + Fxu_HeapDoubleCheck( p ); + fprintf( pFile, "The contents of the heap:\n" ); + fprintf( pFile, "Level %d: ", Degree ); + Fxu_HeapDoubleForEachItem( p, pDiv ) + { + assert( Counter == p->pTree[Counter]->HNum ); + fprintf( pFile, "%2d=%3d ", Counter, FXU_HEAP_DOUBLE_WEIGHT(p->pTree[Counter]) ); + if ( ++Counter == (1 << Degree) ) + { + fprintf( pFile, "\n" ); + Degree++; + fprintf( pFile, "Level %d: ", Degree ); + } + } + fprintf( pFile, "\n" ); + fprintf( pFile, "End of the heap printout.\n" ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapDoubleCheck( Fxu_HeapDouble * p ) +{ + Fxu_Double * pDiv; + Fxu_HeapDoubleForEachItem( p, pDiv ) + { + assert( pDiv->HNum == p->i ); + Fxu_HeapDoubleCheckOne( p, pDiv ); + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapDoubleCheckOne( Fxu_HeapDouble * p, Fxu_Double * pDiv ) +{ + int Weight1, Weight2; + if ( FXU_HEAP_DOUBLE_CHILD1_EXISTS(p,pDiv) ) + { + Weight1 = FXU_HEAP_DOUBLE_WEIGHT(pDiv); + Weight2 = FXU_HEAP_DOUBLE_WEIGHT( FXU_HEAP_DOUBLE_CHILD1(p,pDiv) ); + assert( Weight1 >= Weight2 ); + } + if ( FXU_HEAP_DOUBLE_CHILD2_EXISTS(p,pDiv) ) + { + Weight1 = FXU_HEAP_DOUBLE_WEIGHT(pDiv); + Weight2 = FXU_HEAP_DOUBLE_WEIGHT( FXU_HEAP_DOUBLE_CHILD2(p,pDiv) ); + assert( Weight1 >= Weight2 ); + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapDoubleInsert( Fxu_HeapDouble * p, Fxu_Double * pDiv ) +{ + if ( p->nItems == p->nItemsAlloc ) + Fxu_HeapDoubleResize( p ); + // put the last entry to the last place and move up + p->pTree[++p->nItems] = pDiv; + pDiv->HNum = p->nItems; + // move the last entry up if necessary + Fxu_HeapDoubleMoveUp( p, pDiv ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapDoubleUpdate( Fxu_HeapDouble * p, Fxu_Double * pDiv ) +{ +//printf( "Updating divisor %d.\n", pDiv->Num ); + + FXU_HEAP_DOUBLE_ASSERT(p,pDiv); + if ( FXU_HEAP_DOUBLE_PARENT_EXISTS(p,pDiv) && + FXU_HEAP_DOUBLE_WEIGHT(pDiv) > FXU_HEAP_DOUBLE_WEIGHT( FXU_HEAP_DOUBLE_PARENT(p,pDiv) ) ) + Fxu_HeapDoubleMoveUp( p, pDiv ); + else if ( FXU_HEAP_DOUBLE_CHILD1_EXISTS(p,pDiv) && + FXU_HEAP_DOUBLE_WEIGHT(pDiv) < FXU_HEAP_DOUBLE_WEIGHT( FXU_HEAP_DOUBLE_CHILD1(p,pDiv) ) ) + Fxu_HeapDoubleMoveDn( p, pDiv ); + else if ( FXU_HEAP_DOUBLE_CHILD2_EXISTS(p,pDiv) && + FXU_HEAP_DOUBLE_WEIGHT(pDiv) < FXU_HEAP_DOUBLE_WEIGHT( FXU_HEAP_DOUBLE_CHILD2(p,pDiv) ) ) + Fxu_HeapDoubleMoveDn( p, pDiv ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapDoubleDelete( Fxu_HeapDouble * p, Fxu_Double * pDiv ) +{ + FXU_HEAP_DOUBLE_ASSERT(p,pDiv); + // put the last entry to the deleted place + // decrement the number of entries + p->pTree[pDiv->HNum] = p->pTree[p->nItems--]; + p->pTree[pDiv->HNum]->HNum = pDiv->HNum; + // move the top entry down if necessary + Fxu_HeapDoubleUpdate( p, p->pTree[pDiv->HNum] ); + pDiv->HNum = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fxu_Double * Fxu_HeapDoubleReadMax( Fxu_HeapDouble * p ) +{ + if ( p->nItems == 0 ) + return NULL; + return p->pTree[1]; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fxu_Double * Fxu_HeapDoubleGetMax( Fxu_HeapDouble * p ) +{ + Fxu_Double * pDiv; + if ( p->nItems == 0 ) + return NULL; + // prepare the return value + pDiv = p->pTree[1]; + pDiv->HNum = 0; + // put the last entry on top + // decrement the number of entries + p->pTree[1] = p->pTree[p->nItems--]; + p->pTree[1]->HNum = 1; + // move the top entry down if necessary + Fxu_HeapDoubleMoveDn( p, p->pTree[1] ); + return pDiv; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fxu_HeapDoubleReadMaxWeight( Fxu_HeapDouble * p ) +{ + if ( p->nItems == 0 ) + return -1; + else + return FXU_HEAP_DOUBLE_WEIGHT(p->pTree[1]); +} + + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapDoubleSwap( Fxu_Double ** pDiv1, Fxu_Double ** pDiv2 ) +{ + Fxu_Double * pDiv; + int Temp; + pDiv = *pDiv1; + *pDiv1 = *pDiv2; + *pDiv2 = pDiv; + Temp = (*pDiv1)->HNum; + (*pDiv1)->HNum = (*pDiv2)->HNum; + (*pDiv2)->HNum = Temp; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapDoubleMoveUp( Fxu_HeapDouble * p, Fxu_Double * pDiv ) +{ + Fxu_Double ** ppDiv, ** ppPar; + ppDiv = &FXU_HEAP_DOUBLE_CURRENT(p, pDiv); + while ( FXU_HEAP_DOUBLE_PARENT_EXISTS(p,*ppDiv) ) + { + ppPar = &FXU_HEAP_DOUBLE_PARENT(p,*ppDiv); + if ( FXU_HEAP_DOUBLE_WEIGHT(*ppDiv) > FXU_HEAP_DOUBLE_WEIGHT(*ppPar) ) + { + Fxu_HeapDoubleSwap( ppDiv, ppPar ); + ppDiv = ppPar; + } + else + break; + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapDoubleMoveDn( Fxu_HeapDouble * p, Fxu_Double * pDiv ) +{ + Fxu_Double ** ppChild1, ** ppChild2, ** ppDiv; + ppDiv = &FXU_HEAP_DOUBLE_CURRENT(p, pDiv); + while ( FXU_HEAP_DOUBLE_CHILD1_EXISTS(p,*ppDiv) ) + { // if Child1 does not exist, Child2 also does not exists + + // get the children + ppChild1 = &FXU_HEAP_DOUBLE_CHILD1(p,*ppDiv); + if ( FXU_HEAP_DOUBLE_CHILD2_EXISTS(p,*ppDiv) ) + { + ppChild2 = &FXU_HEAP_DOUBLE_CHILD2(p,*ppDiv); + + // consider two cases + if ( FXU_HEAP_DOUBLE_WEIGHT(*ppDiv) >= FXU_HEAP_DOUBLE_WEIGHT(*ppChild1) && + FXU_HEAP_DOUBLE_WEIGHT(*ppDiv) >= FXU_HEAP_DOUBLE_WEIGHT(*ppChild2) ) + { // Div is larger than both, skip + break; + } + else + { // Div is smaller than one of them, then swap it with larger + if ( FXU_HEAP_DOUBLE_WEIGHT(*ppChild1) >= FXU_HEAP_DOUBLE_WEIGHT(*ppChild2) ) + { + Fxu_HeapDoubleSwap( ppDiv, ppChild1 ); + // update the pointer + ppDiv = ppChild1; + } + else + { + Fxu_HeapDoubleSwap( ppDiv, ppChild2 ); + // update the pointer + ppDiv = ppChild2; + } + } + } + else // Child2 does not exist + { + // consider two cases + if ( FXU_HEAP_DOUBLE_WEIGHT(*ppDiv) >= FXU_HEAP_DOUBLE_WEIGHT(*ppChild1) ) + { // Div is larger than Child1, skip + break; + } + else + { // Div is smaller than Child1, then swap them + Fxu_HeapDoubleSwap( ppDiv, ppChild1 ); + // update the pointer + ppDiv = ppChild1; + } + } + } +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/fxu/fxuHeapS.c b/abc_with_bb_support/src/opt/fxu/fxuHeapS.c new file mode 100644 index 000000000..d880ef39d --- /dev/null +++ b/abc_with_bb_support/src/opt/fxu/fxuHeapS.c @@ -0,0 +1,444 @@ +/**CFile**************************************************************** + + FileName [fxuHeapS.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [The priority queue for variables.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: fxuHeapS.c,v 1.0 2003/02/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fxuInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define FXU_HEAP_SINGLE_WEIGHT(pSingle) ((pSingle)->Weight) +#define FXU_HEAP_SINGLE_CURRENT(p, pSingle) ((p)->pTree[(pSingle)->HNum]) +#define FXU_HEAP_SINGLE_PARENT_EXISTS(p, pSingle) ((pSingle)->HNum > 1) +#define FXU_HEAP_SINGLE_CHILD1_EXISTS(p, pSingle) (((pSingle)->HNum << 1) <= p->nItems) +#define FXU_HEAP_SINGLE_CHILD2_EXISTS(p, pSingle) ((((pSingle)->HNum << 1)+1) <= p->nItems) +#define FXU_HEAP_SINGLE_PARENT(p, pSingle) ((p)->pTree[(pSingle)->HNum >> 1]) +#define FXU_HEAP_SINGLE_CHILD1(p, pSingle) ((p)->pTree[(pSingle)->HNum << 1]) +#define FXU_HEAP_SINGLE_CHILD2(p, pSingle) ((p)->pTree[((pSingle)->HNum << 1)+1]) +#define FXU_HEAP_SINGLE_ASSERT(p, pSingle) assert( (pSingle)->HNum >= 1 && (pSingle)->HNum <= p->nItemsAlloc ) + +static void Fxu_HeapSingleResize( Fxu_HeapSingle * p ); +static void Fxu_HeapSingleSwap( Fxu_Single ** pSingle1, Fxu_Single ** pSingle2 ); +static void Fxu_HeapSingleMoveUp( Fxu_HeapSingle * p, Fxu_Single * pSingle ); +static void Fxu_HeapSingleMoveDn( Fxu_HeapSingle * p, Fxu_Single * pSingle ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fxu_HeapSingle * Fxu_HeapSingleStart() +{ + Fxu_HeapSingle * p; + p = ALLOC( Fxu_HeapSingle, 1 ); + memset( p, 0, sizeof(Fxu_HeapSingle) ); + p->nItems = 0; + p->nItemsAlloc = 2000; + p->pTree = ALLOC( Fxu_Single *, p->nItemsAlloc + 10 ); + p->pTree[0] = NULL; + return p; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapSingleResize( Fxu_HeapSingle * p ) +{ + p->nItemsAlloc *= 2; + p->pTree = REALLOC( Fxu_Single *, p->pTree, p->nItemsAlloc + 10 ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapSingleStop( Fxu_HeapSingle * p ) +{ + int i; + i = 0; + free( p->pTree ); + i = 1; + free( p ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapSinglePrint( FILE * pFile, Fxu_HeapSingle * p ) +{ + Fxu_Single * pSingle; + int Counter = 1; + int Degree = 1; + + Fxu_HeapSingleCheck( p ); + fprintf( pFile, "The contents of the heap:\n" ); + fprintf( pFile, "Level %d: ", Degree ); + Fxu_HeapSingleForEachItem( p, pSingle ) + { + assert( Counter == p->pTree[Counter]->HNum ); + fprintf( pFile, "%2d=%3d ", Counter, FXU_HEAP_SINGLE_WEIGHT(p->pTree[Counter]) ); + if ( ++Counter == (1 << Degree) ) + { + fprintf( pFile, "\n" ); + Degree++; + fprintf( pFile, "Level %d: ", Degree ); + } + } + fprintf( pFile, "\n" ); + fprintf( pFile, "End of the heap printout.\n" ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapSingleCheck( Fxu_HeapSingle * p ) +{ + Fxu_Single * pSingle; + Fxu_HeapSingleForEachItem( p, pSingle ) + { + assert( pSingle->HNum == p->i ); + Fxu_HeapSingleCheckOne( p, pSingle ); + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapSingleCheckOne( Fxu_HeapSingle * p, Fxu_Single * pSingle ) +{ + int Weight1, Weight2; + if ( FXU_HEAP_SINGLE_CHILD1_EXISTS(p,pSingle) ) + { + Weight1 = FXU_HEAP_SINGLE_WEIGHT(pSingle); + Weight2 = FXU_HEAP_SINGLE_WEIGHT( FXU_HEAP_SINGLE_CHILD1(p,pSingle) ); + assert( Weight1 >= Weight2 ); + } + if ( FXU_HEAP_SINGLE_CHILD2_EXISTS(p,pSingle) ) + { + Weight1 = FXU_HEAP_SINGLE_WEIGHT(pSingle); + Weight2 = FXU_HEAP_SINGLE_WEIGHT( FXU_HEAP_SINGLE_CHILD2(p,pSingle) ); + assert( Weight1 >= Weight2 ); + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapSingleInsert( Fxu_HeapSingle * p, Fxu_Single * pSingle ) +{ + if ( p->nItems == p->nItemsAlloc ) + Fxu_HeapSingleResize( p ); + // put the last entry to the last place and move up + p->pTree[++p->nItems] = pSingle; + pSingle->HNum = p->nItems; + // move the last entry up if necessary + Fxu_HeapSingleMoveUp( p, pSingle ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapSingleUpdate( Fxu_HeapSingle * p, Fxu_Single * pSingle ) +{ + FXU_HEAP_SINGLE_ASSERT(p,pSingle); + if ( FXU_HEAP_SINGLE_PARENT_EXISTS(p,pSingle) && + FXU_HEAP_SINGLE_WEIGHT(pSingle) > FXU_HEAP_SINGLE_WEIGHT( FXU_HEAP_SINGLE_PARENT(p,pSingle) ) ) + Fxu_HeapSingleMoveUp( p, pSingle ); + else if ( FXU_HEAP_SINGLE_CHILD1_EXISTS(p,pSingle) && + FXU_HEAP_SINGLE_WEIGHT(pSingle) < FXU_HEAP_SINGLE_WEIGHT( FXU_HEAP_SINGLE_CHILD1(p,pSingle) ) ) + Fxu_HeapSingleMoveDn( p, pSingle ); + else if ( FXU_HEAP_SINGLE_CHILD2_EXISTS(p,pSingle) && + FXU_HEAP_SINGLE_WEIGHT(pSingle) < FXU_HEAP_SINGLE_WEIGHT( FXU_HEAP_SINGLE_CHILD2(p,pSingle) ) ) + Fxu_HeapSingleMoveDn( p, pSingle ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapSingleDelete( Fxu_HeapSingle * p, Fxu_Single * pSingle ) +{ + int Place = pSingle->HNum; + FXU_HEAP_SINGLE_ASSERT(p,pSingle); + // put the last entry to the deleted place + // decrement the number of entries + p->pTree[Place] = p->pTree[p->nItems--]; + p->pTree[Place]->HNum = Place; + // move the top entry down if necessary + Fxu_HeapSingleUpdate( p, p->pTree[Place] ); + pSingle->HNum = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fxu_Single * Fxu_HeapSingleReadMax( Fxu_HeapSingle * p ) +{ + if ( p->nItems == 0 ) + return NULL; + return p->pTree[1]; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fxu_Single * Fxu_HeapSingleGetMax( Fxu_HeapSingle * p ) +{ + Fxu_Single * pSingle; + if ( p->nItems == 0 ) + return NULL; + // prepare the return value + pSingle = p->pTree[1]; + pSingle->HNum = 0; + // put the last entry on top + // decrement the number of entries + p->pTree[1] = p->pTree[p->nItems--]; + p->pTree[1]->HNum = 1; + // move the top entry down if necessary + Fxu_HeapSingleMoveDn( p, p->pTree[1] ); + return pSingle; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fxu_HeapSingleReadMaxWeight( Fxu_HeapSingle * p ) +{ + if ( p->nItems == 0 ) + return -1; + return FXU_HEAP_SINGLE_WEIGHT(p->pTree[1]); +} + + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapSingleSwap( Fxu_Single ** pSingle1, Fxu_Single ** pSingle2 ) +{ + Fxu_Single * pSingle; + int Temp; + pSingle = *pSingle1; + *pSingle1 = *pSingle2; + *pSingle2 = pSingle; + Temp = (*pSingle1)->HNum; + (*pSingle1)->HNum = (*pSingle2)->HNum; + (*pSingle2)->HNum = Temp; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapSingleMoveUp( Fxu_HeapSingle * p, Fxu_Single * pSingle ) +{ + Fxu_Single ** ppSingle, ** ppPar; + ppSingle = &FXU_HEAP_SINGLE_CURRENT(p, pSingle); + while ( FXU_HEAP_SINGLE_PARENT_EXISTS(p,*ppSingle) ) + { + ppPar = &FXU_HEAP_SINGLE_PARENT(p,*ppSingle); + if ( FXU_HEAP_SINGLE_WEIGHT(*ppSingle) > FXU_HEAP_SINGLE_WEIGHT(*ppPar) ) + { + Fxu_HeapSingleSwap( ppSingle, ppPar ); + ppSingle = ppPar; + } + else + break; + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_HeapSingleMoveDn( Fxu_HeapSingle * p, Fxu_Single * pSingle ) +{ + Fxu_Single ** ppChild1, ** ppChild2, ** ppSingle; + ppSingle = &FXU_HEAP_SINGLE_CURRENT(p, pSingle); + while ( FXU_HEAP_SINGLE_CHILD1_EXISTS(p,*ppSingle) ) + { // if Child1 does not exist, Child2 also does not exists + + // get the children + ppChild1 = &FXU_HEAP_SINGLE_CHILD1(p,*ppSingle); + if ( FXU_HEAP_SINGLE_CHILD2_EXISTS(p,*ppSingle) ) + { + ppChild2 = &FXU_HEAP_SINGLE_CHILD2(p,*ppSingle); + + // consider two cases + if ( FXU_HEAP_SINGLE_WEIGHT(*ppSingle) >= FXU_HEAP_SINGLE_WEIGHT(*ppChild1) && + FXU_HEAP_SINGLE_WEIGHT(*ppSingle) >= FXU_HEAP_SINGLE_WEIGHT(*ppChild2) ) + { // Var is larger than both, skip + break; + } + else + { // Var is smaller than one of them, then swap it with larger + if ( FXU_HEAP_SINGLE_WEIGHT(*ppChild1) >= FXU_HEAP_SINGLE_WEIGHT(*ppChild2) ) + { + Fxu_HeapSingleSwap( ppSingle, ppChild1 ); + // update the pointer + ppSingle = ppChild1; + } + else + { + Fxu_HeapSingleSwap( ppSingle, ppChild2 ); + // update the pointer + ppSingle = ppChild2; + } + } + } + else // Child2 does not exist + { + // consider two cases + if ( FXU_HEAP_SINGLE_WEIGHT(*ppSingle) >= FXU_HEAP_SINGLE_WEIGHT(*ppChild1) ) + { // Var is larger than Child1, skip + break; + } + else + { // Var is smaller than Child1, then swap them + Fxu_HeapSingleSwap( ppSingle, ppChild1 ); + // update the pointer + ppSingle = ppChild1; + } + } + } +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/opt/fxu/fxuInt.h b/abc_with_bb_support/src/opt/fxu/fxuInt.h new file mode 100644 index 000000000..b41c544a2 --- /dev/null +++ b/abc_with_bb_support/src/opt/fxu/fxuInt.h @@ -0,0 +1,537 @@ +/**CFile**************************************************************** + + FileName [fxuInt.h] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Internal declarations of fast extract for unate covers.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: fxuInt.h,v 1.3 2003/04/10 05:42:44 donald Exp $] + +***********************************************************************/ + +#ifndef __FXU_INT_H__ +#define __FXU_INT_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include "extra.h" +#include "vec.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +// uncomment this macro to switch to standard memory management +//#define USE_SYSTEM_MEMORY_MANAGEMENT + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/* + Here is an informal description of the FX data structure. + (1) The sparse matrix is filled with literals, associated with + cubes (row) and variables (columns). The matrix contains + all the cubes of all the nodes in the network. + (2) A cube is associated with + (a) its literals in the matrix, + (b) the output variable of the node, to which this cube belongs, + (3) A variable is associated with + (a) its literals in the matrix and + (b) the list of cube pairs in the cover, for which it is the output + (4) A cube pair is associated with two cubes and contains the counters + of literals in the base and in the cubes without the base + (5) A double-cube divisor is associated with list of all cube pairs + that produce it and its current weight (which is updated automatically + each time a new pair is added or an old pair is removed). + (6) A single-cube divisor is associated the pair of variables. +*/ + +// sparse matrix +typedef struct FxuMatrix Fxu_Matrix; // the sparse matrix + +// sparse matrix contents: cubes (rows), vars (columns), literals (entries) +typedef struct FxuCube Fxu_Cube; // one cube in the sparse matrix +typedef struct FxuVar Fxu_Var; // one literal in the sparse matrix +typedef struct FxuLit Fxu_Lit; // one entry in the sparse matrix + +// double cube divisors +typedef struct FxuPair Fxu_Pair; // the pair of cubes +typedef struct FxuDouble Fxu_Double; // the double-cube divisor +typedef struct FxuSingle Fxu_Single; // the two-literal single-cube divisor + +// various lists +typedef struct FxuListCube Fxu_ListCube; // the list of cubes +typedef struct FxuListVar Fxu_ListVar; // the list of literals +typedef struct FxuListLit Fxu_ListLit; // the list of entries +typedef struct FxuListPair Fxu_ListPair; // the list of pairs +typedef struct FxuListDouble Fxu_ListDouble; // the list of divisors +typedef struct FxuListSingle Fxu_ListSingle; // the list of single-cube divisors + +// various heaps +typedef struct FxuHeapDouble Fxu_HeapDouble; // the heap of divisors +typedef struct FxuHeapSingle Fxu_HeapSingle; // the heap of variables + + +// various lists + +// the list of cubes in the sparse matrix +struct FxuListCube +{ + Fxu_Cube * pHead; + Fxu_Cube * pTail; + int nItems; +}; + +// the list of literals in the sparse matrix +struct FxuListVar +{ + Fxu_Var * pHead; + Fxu_Var * pTail; + int nItems; +}; + +// the list of entries in the sparse matrix +struct FxuListLit +{ + Fxu_Lit * pHead; + Fxu_Lit * pTail; + int nItems; +}; + +// the list of cube pair in the sparse matrix +struct FxuListPair +{ + Fxu_Pair * pHead; + Fxu_Pair * pTail; + int nItems; +}; + +// the list of divisors in the sparse matrix +struct FxuListDouble +{ + Fxu_Double * pHead; + Fxu_Double * pTail; + int nItems; +}; + +// the list of divisors in the sparse matrix +struct FxuListSingle +{ + Fxu_Single * pHead; + Fxu_Single * pTail; + int nItems; +}; + + +// various heaps + +// the heap of double cube divisors by weight +struct FxuHeapDouble +{ + Fxu_Double ** pTree; + int nItems; + int nItemsAlloc; + int i; +}; + +// the heap of variable by their occurrence in the cubes +struct FxuHeapSingle +{ + Fxu_Single ** pTree; + int nItems; + int nItemsAlloc; + int i; +}; + + + +// sparse matrix +struct FxuMatrix // ~ 30 words +{ + // the cubes + Fxu_ListCube lCubes; // the double linked list of cubes + // the values (binary literals) + Fxu_ListVar lVars; // the double linked list of variables + Fxu_Var ** ppVars; // the array of variables + // the double cube divisors + Fxu_ListDouble * pTable; // the hash table of divisors + int nTableSize; // the hash table size + int nDivs; // the number of divisors in the table + int nDivsTotal; // the number of divisors in the table + Fxu_HeapDouble * pHeapDouble; // the heap of divisors by weight + // the single cube divisors + Fxu_ListSingle lSingles; // the linked list of single cube divisors + Fxu_HeapSingle * pHeapSingle; // the heap of variables by the number of literals in the matrix + // storage for cube pairs + Fxu_Pair *** pppPairs; + Fxu_Pair ** ppPairs; + // temporary storage for cubes + Fxu_Cube * pOrderCubes; + Fxu_Cube ** ppTailCubes; + // temporary storage for variables + Fxu_Var * pOrderVars; + Fxu_Var ** ppTailVars; + // temporary storage for pairs + Vec_Ptr_t * vPairs; + // statistics + int nEntries; // the total number of entries in the sparse matrix + int nDivs1; // the single cube divisors taken + int nDivs2; // the double cube divisors taken + int nDivs3; // the double cube divisors with complement + // memory manager + Extra_MmFixed_t * pMemMan; // the memory manager for all small sized entries +}; + +// the cube in the sparse matrix +struct FxuCube // 9 words +{ + int iCube; // the number of this cube in the cover + Fxu_Cube * pFirst; // the pointer to the first cube of this cover + Fxu_Var * pVar; // the variable representing the output of the cover + Fxu_ListLit lLits; // the row in the table + Fxu_Cube * pPrev; // the previous cube + Fxu_Cube * pNext; // the next cube + Fxu_Cube * pOrder; // the specialized linked list of cubes +}; + +// the variable in the sparse matrix +struct FxuVar // 10 words +{ + int iVar; // the number of this variable + int nCubes; // the number of cubes assoc with this var + Fxu_Cube * pFirst; // the first cube assoc with this var + Fxu_Pair *** ppPairs; // the pairs of cubes assoc with this var + Fxu_ListLit lLits; // the column in the table + Fxu_Var * pPrev; // the previous variable + Fxu_Var * pNext; // the next variable + Fxu_Var * pOrder; // the specialized linked list of variables +}; + +// the literal entry in the sparse matrix +struct FxuLit // 8 words +{ + int iVar; // the number of this variable + int iCube; // the number of this cube + Fxu_Cube * pCube; // the cube of this literal + Fxu_Var * pVar; // the variable of this literal + Fxu_Lit * pHPrev; // prev lit in the cube + Fxu_Lit * pHNext; // next lit in the cube + Fxu_Lit * pVPrev; // prev lit of the var + Fxu_Lit * pVNext; // next lit of the var +}; + +// the cube pair +struct FxuPair // 10 words +{ + int nLits1; // the number of literals in the two cubes + int nLits2; // the number of literals in the two cubes + int nBase; // the number of literals in the base + Fxu_Double * pDiv; // the divisor of this pair + Fxu_Cube * pCube1; // the first cube of the pair + Fxu_Cube * pCube2; // the second cube of the pair + int iCube1; // the first cube of the pair + int iCube2; // the second cube of the pair + Fxu_Pair * pDPrev; // the previous pair in the divisor + Fxu_Pair * pDNext; // the next pair in the divisor +}; + +// the double cube divisor +struct FxuDouble // 10 words +{ + int Num; // the unique number of this divisor + int HNum; // the heap number of this divisor + int Weight; // the weight of this divisor + unsigned Key; // the hash key of this divisor + Fxu_ListPair lPairs; // the pairs of cubes, which produce this divisor + Fxu_Double * pPrev; // the previous divisor in the table + Fxu_Double * pNext; // the next divisor in the table + Fxu_Double * pOrder; // the specialized linked list of divisors +}; + +// the single cube divisor +struct FxuSingle // 7 words +{ + int Num; // the unique number of this divisor + int HNum; // the heap number of this divisor + int Weight; // the weight of this divisor + Fxu_Var * pVar1; // the first variable of the single-cube divisor + Fxu_Var * pVar2; // the second variable of the single-cube divisor + Fxu_Single * pPrev; // the previous divisor in the list + Fxu_Single * pNext; // the next divisor in the list +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// minimum/maximum +#define Fxu_Min( a, b ) ( ((a)<(b))? (a):(b) ) +#define Fxu_Max( a, b ) ( ((a)>(b))? (a):(b) ) + +// selection of the minimum/maximum cube in the pair +#define Fxu_PairMinCube( pPair ) (((pPair)->iCube1 < (pPair)->iCube2)? (pPair)->pCube1: (pPair)->pCube2) +#define Fxu_PairMaxCube( pPair ) (((pPair)->iCube1 > (pPair)->iCube2)? (pPair)->pCube1: (pPair)->pCube2) +#define Fxu_PairMinCubeInt( pPair ) (((pPair)->iCube1 < (pPair)->iCube2)? (pPair)->iCube1: (pPair)->iCube2) +#define Fxu_PairMaxCubeInt( pPair ) (((pPair)->iCube1 > (pPair)->iCube2)? (pPair)->iCube1: (pPair)->iCube2) + +// iterators + +#define Fxu_MatrixForEachCube( Matrix, Cube )\ + for ( Cube = (Matrix)->lCubes.pHead;\ + Cube;\ + Cube = Cube->pNext ) +#define Fxu_MatrixForEachCubeSafe( Matrix, Cube, Cube2 )\ + for ( Cube = (Matrix)->lCubes.pHead, Cube2 = (Cube? Cube->pNext: NULL);\ + Cube;\ + Cube = Cube2, Cube2 = (Cube? Cube->pNext: NULL) ) + +#define Fxu_MatrixForEachVariable( Matrix, Var )\ + for ( Var = (Matrix)->lVars.pHead;\ + Var;\ + Var = Var->pNext ) +#define Fxu_MatrixForEachVariableSafe( Matrix, Var, Var2 )\ + for ( Var = (Matrix)->lVars.pHead, Var2 = (Var? Var->pNext: NULL);\ + Var;\ + Var = Var2, Var2 = (Var? Var->pNext: NULL) ) + +#define Fxu_MatrixForEachSingle( Matrix, Single )\ + for ( Single = (Matrix)->lSingles.pHead;\ + Single;\ + Single = Single->pNext ) +#define Fxu_MatrixForEachSingleSafe( Matrix, Single, Single2 )\ + for ( Single = (Matrix)->lSingles.pHead, Single2 = (Single? Single->pNext: NULL);\ + Single;\ + Single = Single2, Single2 = (Single? Single->pNext: NULL) ) + +#define Fxu_TableForEachDouble( Matrix, Key, Div )\ + for ( Div = (Matrix)->pTable[Key].pHead;\ + Div;\ + Div = Div->pNext ) +#define Fxu_TableForEachDoubleSafe( Matrix, Key, Div, Div2 )\ + for ( Div = (Matrix)->pTable[Key].pHead, Div2 = (Div? Div->pNext: NULL);\ + Div;\ + Div = Div2, Div2 = (Div? Div->pNext: NULL) ) + +#define Fxu_MatrixForEachDouble( Matrix, Div, Index )\ + for ( Index = 0; Index < (Matrix)->nTableSize; Index++ )\ + Fxu_TableForEachDouble( Matrix, Index, Div ) +#define Fxu_MatrixForEachDoubleSafe( Matrix, Div, Div2, Index )\ + for ( Index = 0; Index < (Matrix)->nTableSize; Index++ )\ + Fxu_TableForEachDoubleSafe( Matrix, Index, Div, Div2 ) + + +#define Fxu_CubeForEachLiteral( Cube, Lit )\ + for ( Lit = (Cube)->lLits.pHead;\ + Lit;\ + Lit = Lit->pHNext ) +#define Fxu_CubeForEachLiteralSafe( Cube, Lit, Lit2 )\ + for ( Lit = (Cube)->lLits.pHead, Lit2 = (Lit? Lit->pHNext: NULL);\ + Lit;\ + Lit = Lit2, Lit2 = (Lit? Lit->pHNext: NULL) ) + +#define Fxu_VarForEachLiteral( Var, Lit )\ + for ( Lit = (Var)->lLits.pHead;\ + Lit;\ + Lit = Lit->pVNext ) + +#define Fxu_CubeForEachDivisor( Cube, Div )\ + for ( Div = (Cube)->lDivs.pHead;\ + Div;\ + Div = Div->pCNext ) + +#define Fxu_DoubleForEachPair( Div, Pair )\ + for ( Pair = (Div)->lPairs.pHead;\ + Pair;\ + Pair = Pair->pDNext ) +#define Fxu_DoubleForEachPairSafe( Div, Pair, Pair2 )\ + for ( Pair = (Div)->lPairs.pHead, Pair2 = (Pair? Pair->pDNext: NULL);\ + Pair;\ + Pair = Pair2, Pair2 = (Pair? Pair->pDNext: NULL) ) + + +// iterator through the cube pairs belonging to the given cube +#define Fxu_CubeForEachPair( pCube, pPair, i )\ + for ( i = 0;\ + i < pCube->pVar->nCubes &&\ + (((unsigned)(pPair = pCube->pVar->ppPairs[pCube->iCube][i])) >= 0);\ + i++ )\ + if ( pPair ) + +// iterator through all the items in the heap +#define Fxu_HeapDoubleForEachItem( Heap, Div )\ + for ( Heap->i = 1;\ + Heap->i <= Heap->nItems && (Div = Heap->pTree[Heap->i]);\ + Heap->i++ ) +#define Fxu_HeapSingleForEachItem( Heap, Single )\ + for ( Heap->i = 1;\ + Heap->i <= Heap->nItems && (Single = Heap->pTree[Heap->i]);\ + Heap->i++ ) + +// starting the rings +#define Fxu_MatrixRingCubesStart( Matrix ) (((Matrix)->ppTailCubes = &((Matrix)->pOrderCubes)), ((Matrix)->pOrderCubes = NULL)) +#define Fxu_MatrixRingVarsStart( Matrix ) (((Matrix)->ppTailVars = &((Matrix)->pOrderVars)), ((Matrix)->pOrderVars = NULL)) +// stopping the rings +#define Fxu_MatrixRingCubesStop( Matrix ) +#define Fxu_MatrixRingVarsStop( Matrix ) +// resetting the rings +#define Fxu_MatrixRingCubesReset( Matrix ) (((Matrix)->pOrderCubes = NULL), ((Matrix)->ppTailCubes = NULL)) +#define Fxu_MatrixRingVarsReset( Matrix ) (((Matrix)->pOrderVars = NULL), ((Matrix)->ppTailVars = NULL)) +// adding to the rings +#define Fxu_MatrixRingCubesAdd( Matrix, Cube) ((*((Matrix)->ppTailCubes) = Cube), ((Matrix)->ppTailCubes = &(Cube)->pOrder), ((Cube)->pOrder = (Fxu_Cube *)1)) +#define Fxu_MatrixRingVarsAdd( Matrix, Var ) ((*((Matrix)->ppTailVars ) = Var ), ((Matrix)->ppTailVars = &(Var)->pOrder ), ((Var)->pOrder = (Fxu_Var *)1)) +// iterating through the rings +#define Fxu_MatrixForEachCubeInRing( Matrix, Cube )\ + if ( (Matrix)->pOrderCubes )\ + for ( Cube = (Matrix)->pOrderCubes;\ + Cube != (Fxu_Cube *)1;\ + Cube = Cube->pOrder ) +#define Fxu_MatrixForEachCubeInRingSafe( Matrix, Cube, Cube2 )\ + if ( (Matrix)->pOrderCubes )\ + for ( Cube = (Matrix)->pOrderCubes, Cube2 = ((Cube != (Fxu_Cube *)1)? Cube->pOrder: (Fxu_Cube *)1);\ + Cube != (Fxu_Cube *)1;\ + Cube = Cube2, Cube2 = ((Cube != (Fxu_Cube *)1)? Cube->pOrder: (Fxu_Cube *)1) ) +#define Fxu_MatrixForEachVarInRing( Matrix, Var )\ + if ( (Matrix)->pOrderVars )\ + for ( Var = (Matrix)->pOrderVars;\ + Var != (Fxu_Var *)1;\ + Var = Var->pOrder ) +#define Fxu_MatrixForEachVarInRingSafe( Matrix, Var, Var2 )\ + if ( (Matrix)->pOrderVars )\ + for ( Var = (Matrix)->pOrderVars, Var2 = ((Var != (Fxu_Var *)1)? Var->pOrder: (Fxu_Var *)1);\ + Var != (Fxu_Var *)1;\ + Var = Var2, Var2 = ((Var != (Fxu_Var *)1)? Var->pOrder: (Fxu_Var *)1) ) +// the procedures are related to the above macros +extern void Fxu_MatrixRingCubesUnmark( Fxu_Matrix * p ); +extern void Fxu_MatrixRingVarsUnmark( Fxu_Matrix * p ); + + +// macros working with memory +// MEM_ALLOC: allocate the given number (Size) of items of type (Type) +// MEM_FREE: deallocate the pointer (Pointer) to the given number (Size) of items of type (Type) +#ifdef USE_SYSTEM_MEMORY_MANAGEMENT +#define MEM_ALLOC_FXU( Manager, Type, Size ) ((Type *)malloc( (Size) * sizeof(Type) )) +#define MEM_FREE_FXU( Manager, Type, Size, Pointer ) if ( Pointer ) { free(Pointer); Pointer = NULL; } +#else +#define MEM_ALLOC_FXU( Manager, Type, Size )\ + ((Type *)Fxu_MemFetch( Manager, (Size) * sizeof(Type) )) +#define MEM_FREE_FXU( Manager, Type, Size, Pointer )\ + if ( Pointer ) { Fxu_MemRecycle( Manager, (char *)(Pointer), (Size) * sizeof(Type) ); Pointer = NULL; } +#endif + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/*===== fxu.c ====================================================*/ +extern char * Fxu_MemFetch( Fxu_Matrix * p, int nBytes ); +extern void Fxu_MemRecycle( Fxu_Matrix * p, char * pItem, int nBytes ); +/*===== fxuCreate.c ====================================================*/ +/*===== fxuReduce.c ====================================================*/ +/*===== fxuPrint.c ====================================================*/ +extern void Fxu_MatrixPrint( FILE * pFile, Fxu_Matrix * p ); +extern void Fxu_MatrixPrintDivisorProfile( FILE * pFile, Fxu_Matrix * p ); +/*===== fxuSelect.c ====================================================*/ +extern int Fxu_Select( Fxu_Matrix * p, Fxu_Single ** ppSingle, Fxu_Double ** ppDouble ); +extern int Fxu_SelectSCD( Fxu_Matrix * p, int Weight, Fxu_Var ** ppVar1, Fxu_Var ** ppVar2 ); +/*===== fxuUpdate.c ====================================================*/ +extern void Fxu_Update( Fxu_Matrix * p, Fxu_Single * pSingle, Fxu_Double * pDouble ); +extern void Fxu_UpdateDouble( Fxu_Matrix * p ); +extern void Fxu_UpdateSingle( Fxu_Matrix * p ); +/*===== fxuPair.c ====================================================*/ +extern void Fxu_PairCanonicize( Fxu_Cube ** ppCube1, Fxu_Cube ** ppCube2 ); +extern unsigned Fxu_PairHashKeyArray( Fxu_Matrix * p, int piVarsC1[], int piVarsC2[], int nVarsC1, int nVarsC2 ); +extern unsigned Fxu_PairHashKey( Fxu_Matrix * p, Fxu_Cube * pCube1, Fxu_Cube * pCube2, int * pnBase, int * pnLits1, int * pnLits2 ); +extern unsigned Fxu_PairHashKeyMv( Fxu_Matrix * p, Fxu_Cube * pCube1, Fxu_Cube * pCube2, int * pnBase, int * pnLits1, int * pnLits2 ); +extern int Fxu_PairCompare( Fxu_Pair * pPair1, Fxu_Pair * pPair2 ); +extern void Fxu_PairAllocStorage( Fxu_Var * pVar, int nCubes ); +extern void Fxu_PairFreeStorage( Fxu_Var * pVar ); +extern void Fxu_PairClearStorage( Fxu_Cube * pCube ); +extern Fxu_Pair * Fxu_PairAlloc( Fxu_Matrix * p, Fxu_Cube * pCube1, Fxu_Cube * pCube2 ); +extern void Fxu_PairAdd( Fxu_Pair * pPair ); +/*===== fxuSingle.c ====================================================*/ +extern void Fxu_MatrixComputeSingles( Fxu_Matrix * p ); +extern void Fxu_MatrixComputeSinglesOne( Fxu_Matrix * p, Fxu_Var * pVar ); +extern int Fxu_SingleCountCoincidence( Fxu_Matrix * p, Fxu_Var * pVar1, Fxu_Var * pVar2 ); +/*===== fxuMatrix.c ====================================================*/ +// matrix +extern Fxu_Matrix * Fxu_MatrixAllocate(); +extern void Fxu_MatrixDelete( Fxu_Matrix * p ); +// double-cube divisor +extern void Fxu_MatrixAddDivisor( Fxu_Matrix * p, Fxu_Cube * pCube1, Fxu_Cube * pCube2 ); +extern void Fxu_MatrixDelDivisor( Fxu_Matrix * p, Fxu_Double * pDiv ); +// single-cube divisor +extern void Fxu_MatrixAddSingle( Fxu_Matrix * p, Fxu_Var * pVar1, Fxu_Var * pVar2, int Weight ); +// variable +extern Fxu_Var * Fxu_MatrixAddVar( Fxu_Matrix * p ); +// cube +extern Fxu_Cube * Fxu_MatrixAddCube( Fxu_Matrix * p, Fxu_Var * pVar, int iCube ); +// literal +extern void Fxu_MatrixAddLiteral( Fxu_Matrix * p, Fxu_Cube * pCube, Fxu_Var * pVar ); +extern void Fxu_MatrixDelLiteral( Fxu_Matrix * p, Fxu_Lit * pLit ); +/*===== fxuList.c ====================================================*/ +// matrix -> variable +extern void Fxu_ListMatrixAddVariable( Fxu_Matrix * p, Fxu_Var * pVar ); +extern void Fxu_ListMatrixDelVariable( Fxu_Matrix * p, Fxu_Var * pVar ); +// matrix -> cube +extern void Fxu_ListMatrixAddCube( Fxu_Matrix * p, Fxu_Cube * pCube ); +extern void Fxu_ListMatrixDelCube( Fxu_Matrix * p, Fxu_Cube * pCube ); +// matrix -> single +extern void Fxu_ListMatrixAddSingle( Fxu_Matrix * p, Fxu_Single * pSingle ); +extern void Fxu_ListMatrixDelSingle( Fxu_Matrix * p, Fxu_Single * pSingle ); +// table -> divisor +extern void Fxu_ListTableAddDivisor( Fxu_Matrix * p, Fxu_Double * pDiv ); +extern void Fxu_ListTableDelDivisor( Fxu_Matrix * p, Fxu_Double * pDiv ); +// cube -> literal +extern void Fxu_ListCubeAddLiteral( Fxu_Cube * pCube, Fxu_Lit * pLit ); +extern void Fxu_ListCubeDelLiteral( Fxu_Cube * pCube, Fxu_Lit * pLit ); +// var -> literal +extern void Fxu_ListVarAddLiteral( Fxu_Var * pVar, Fxu_Lit * pLit ); +extern void Fxu_ListVarDelLiteral( Fxu_Var * pVar, Fxu_Lit * pLit ); +// divisor -> pair +extern void Fxu_ListDoubleAddPairLast( Fxu_Double * pDiv, Fxu_Pair * pLink ); +extern void Fxu_ListDoubleAddPairFirst( Fxu_Double * pDiv, Fxu_Pair * pLink ); +extern void Fxu_ListDoubleAddPairMiddle( Fxu_Double * pDiv, Fxu_Pair * pSpot, Fxu_Pair * pLink ); +extern void Fxu_ListDoubleDelPair( Fxu_Double * pDiv, Fxu_Pair * pPair ); +/*===== fxuHeapDouble.c ====================================================*/ +extern Fxu_HeapDouble * Fxu_HeapDoubleStart(); +extern void Fxu_HeapDoubleStop( Fxu_HeapDouble * p ); +extern void Fxu_HeapDoublePrint( FILE * pFile, Fxu_HeapDouble * p ); +extern void Fxu_HeapDoubleCheck( Fxu_HeapDouble * p ); +extern void Fxu_HeapDoubleCheckOne( Fxu_HeapDouble * p, Fxu_Double * pDiv ); + +extern void Fxu_HeapDoubleInsert( Fxu_HeapDouble * p, Fxu_Double * pDiv ); +extern void Fxu_HeapDoubleUpdate( Fxu_HeapDouble * p, Fxu_Double * pDiv ); +extern void Fxu_HeapDoubleDelete( Fxu_HeapDouble * p, Fxu_Double * pDiv ); +extern int Fxu_HeapDoubleReadMaxWeight( Fxu_HeapDouble * p ); +extern Fxu_Double * Fxu_HeapDoubleReadMax( Fxu_HeapDouble * p ); +extern Fxu_Double * Fxu_HeapDoubleGetMax( Fxu_HeapDouble * p ); +/*===== fxuHeapSingle.c ====================================================*/ +extern Fxu_HeapSingle * Fxu_HeapSingleStart(); +extern void Fxu_HeapSingleStop( Fxu_HeapSingle * p ); +extern void Fxu_HeapSinglePrint( FILE * pFile, Fxu_HeapSingle * p ); +extern void Fxu_HeapSingleCheck( Fxu_HeapSingle * p ); +extern void Fxu_HeapSingleCheckOne( Fxu_HeapSingle * p, Fxu_Single * pSingle ); + +extern void Fxu_HeapSingleInsert( Fxu_HeapSingle * p, Fxu_Single * pSingle ); +extern void Fxu_HeapSingleUpdate( Fxu_HeapSingle * p, Fxu_Single * pSingle ); +extern void Fxu_HeapSingleDelete( Fxu_HeapSingle * p, Fxu_Single * pSingle ); +extern int Fxu_HeapSingleReadMaxWeight( Fxu_HeapSingle * p ); +extern Fxu_Single * Fxu_HeapSingleReadMax( Fxu_HeapSingle * p ); +extern Fxu_Single * Fxu_HeapSingleGetMax( Fxu_HeapSingle * p ); + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/opt/fxu/fxuList.c b/abc_with_bb_support/src/opt/fxu/fxuList.c new file mode 100644 index 000000000..389b04826 --- /dev/null +++ b/abc_with_bb_support/src/opt/fxu/fxuList.c @@ -0,0 +1,522 @@ +/**CFile**************************************************************** + + FileName [fxuList.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Operations on lists.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: fxuList.c,v 1.0 2003/02/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fxuInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// matrix -> var + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_ListMatrixAddVariable( Fxu_Matrix * p, Fxu_Var * pLink ) +{ + Fxu_ListVar * pList = &p->lVars; + if ( pList->pHead == NULL ) + { + pList->pHead = pLink; + pList->pTail = pLink; + pLink->pPrev = NULL; + pLink->pNext = NULL; + } + else + { + pLink->pNext = NULL; + pList->pTail->pNext = pLink; + pLink->pPrev = pList->pTail; + pList->pTail = pLink; + } + pList->nItems++; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_ListMatrixDelVariable( Fxu_Matrix * p, Fxu_Var * pLink ) +{ + Fxu_ListVar * pList = &p->lVars; + if ( pList->pHead == pLink ) + pList->pHead = pLink->pNext; + if ( pList->pTail == pLink ) + pList->pTail = pLink->pPrev; + if ( pLink->pPrev ) + pLink->pPrev->pNext = pLink->pNext; + if ( pLink->pNext ) + pLink->pNext->pPrev = pLink->pPrev; + pList->nItems--; +} + + +// matrix -> cube + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_ListMatrixAddCube( Fxu_Matrix * p, Fxu_Cube * pLink ) +{ + Fxu_ListCube * pList = &p->lCubes; + if ( pList->pHead == NULL ) + { + pList->pHead = pLink; + pList->pTail = pLink; + pLink->pPrev = NULL; + pLink->pNext = NULL; + } + else + { + pLink->pNext = NULL; + pList->pTail->pNext = pLink; + pLink->pPrev = pList->pTail; + pList->pTail = pLink; + } + pList->nItems++; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_ListMatrixDelCube( Fxu_Matrix * p, Fxu_Cube * pLink ) +{ + Fxu_ListCube * pList = &p->lCubes; + if ( pList->pHead == pLink ) + pList->pHead = pLink->pNext; + if ( pList->pTail == pLink ) + pList->pTail = pLink->pPrev; + if ( pLink->pPrev ) + pLink->pPrev->pNext = pLink->pNext; + if ( pLink->pNext ) + pLink->pNext->pPrev = pLink->pPrev; + pList->nItems--; +} + + +// matrix -> single + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_ListMatrixAddSingle( Fxu_Matrix * p, Fxu_Single * pLink ) +{ + Fxu_ListSingle * pList = &p->lSingles; + if ( pList->pHead == NULL ) + { + pList->pHead = pLink; + pList->pTail = pLink; + pLink->pPrev = NULL; + pLink->pNext = NULL; + } + else + { + pLink->pNext = NULL; + pList->pTail->pNext = pLink; + pLink->pPrev = pList->pTail; + pList->pTail = pLink; + } + pList->nItems++; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_ListMatrixDelSingle( Fxu_Matrix * p, Fxu_Single * pLink ) +{ + Fxu_ListSingle * pList = &p->lSingles; + if ( pList->pHead == pLink ) + pList->pHead = pLink->pNext; + if ( pList->pTail == pLink ) + pList->pTail = pLink->pPrev; + if ( pLink->pPrev ) + pLink->pPrev->pNext = pLink->pNext; + if ( pLink->pNext ) + pLink->pNext->pPrev = pLink->pPrev; + pList->nItems--; +} + + +// table -> divisor + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_ListTableAddDivisor( Fxu_Matrix * p, Fxu_Double * pLink ) +{ + Fxu_ListDouble * pList = &(p->pTable[pLink->Key]); + if ( pList->pHead == NULL ) + { + pList->pHead = pLink; + pList->pTail = pLink; + pLink->pPrev = NULL; + pLink->pNext = NULL; + } + else + { + pLink->pNext = NULL; + pList->pTail->pNext = pLink; + pLink->pPrev = pList->pTail; + pList->pTail = pLink; + } + pList->nItems++; + p->nDivs++; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_ListTableDelDivisor( Fxu_Matrix * p, Fxu_Double * pLink ) +{ + Fxu_ListDouble * pList = &(p->pTable[pLink->Key]); + if ( pList->pHead == pLink ) + pList->pHead = pLink->pNext; + if ( pList->pTail == pLink ) + pList->pTail = pLink->pPrev; + if ( pLink->pPrev ) + pLink->pPrev->pNext = pLink->pNext; + if ( pLink->pNext ) + pLink->pNext->pPrev = pLink->pPrev; + pList->nItems--; + p->nDivs--; +} + + +// cube -> literal + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_ListCubeAddLiteral( Fxu_Cube * pCube, Fxu_Lit * pLink ) +{ + Fxu_ListLit * pList = &(pCube->lLits); + if ( pList->pHead == NULL ) + { + pList->pHead = pLink; + pList->pTail = pLink; + pLink->pHPrev = NULL; + pLink->pHNext = NULL; + } + else + { + pLink->pHNext = NULL; + pList->pTail->pHNext = pLink; + pLink->pHPrev = pList->pTail; + pList->pTail = pLink; + } + pList->nItems++; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_ListCubeDelLiteral( Fxu_Cube * pCube, Fxu_Lit * pLink ) +{ + Fxu_ListLit * pList = &(pCube->lLits); + if ( pList->pHead == pLink ) + pList->pHead = pLink->pHNext; + if ( pList->pTail == pLink ) + pList->pTail = pLink->pHPrev; + if ( pLink->pHPrev ) + pLink->pHPrev->pHNext = pLink->pHNext; + if ( pLink->pHNext ) + pLink->pHNext->pHPrev = pLink->pHPrev; + pList->nItems--; +} + + +// var -> literal + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_ListVarAddLiteral( Fxu_Var * pVar, Fxu_Lit * pLink ) +{ + Fxu_ListLit * pList = &(pVar->lLits); + if ( pList->pHead == NULL ) + { + pList->pHead = pLink; + pList->pTail = pLink; + pLink->pVPrev = NULL; + pLink->pVNext = NULL; + } + else + { + pLink->pVNext = NULL; + pList->pTail->pVNext = pLink; + pLink->pVPrev = pList->pTail; + pList->pTail = pLink; + } + pList->nItems++; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_ListVarDelLiteral( Fxu_Var * pVar, Fxu_Lit * pLink ) +{ + Fxu_ListLit * pList = &(pVar->lLits); + if ( pList->pHead == pLink ) + pList->pHead = pLink->pVNext; + if ( pList->pTail == pLink ) + pList->pTail = pLink->pVPrev; + if ( pLink->pVPrev ) + pLink->pVPrev->pVNext = pLink->pVNext; + if ( pLink->pVNext ) + pLink->pVNext->pVPrev = pLink->pVPrev; + pList->nItems--; +} + + + +// divisor -> pair + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_ListDoubleAddPairLast( Fxu_Double * pDiv, Fxu_Pair * pLink ) +{ + Fxu_ListPair * pList = &pDiv->lPairs; + if ( pList->pHead == NULL ) + { + pList->pHead = pLink; + pList->pTail = pLink; + pLink->pDPrev = NULL; + pLink->pDNext = NULL; + } + else + { + pLink->pDNext = NULL; + pList->pTail->pDNext = pLink; + pLink->pDPrev = pList->pTail; + pList->pTail = pLink; + } + pList->nItems++; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_ListDoubleAddPairFirst( Fxu_Double * pDiv, Fxu_Pair * pLink ) +{ + Fxu_ListPair * pList = &pDiv->lPairs; + if ( pList->pHead == NULL ) + { + pList->pHead = pLink; + pList->pTail = pLink; + pLink->pDPrev = NULL; + pLink->pDNext = NULL; + } + else + { + pLink->pDPrev = NULL; + pList->pHead->pDPrev = pLink; + pLink->pDNext = pList->pHead; + pList->pHead = pLink; + } + pList->nItems++; +} + +/**Function************************************************************* + + Synopsis [Adds the entry in the middle of the list after the spot.] + + Description [Assumes that spot points to the link, after which the given + link should be added. Spot cannot be NULL or the tail of the list. + Therefore, the head and the tail of the list are not changed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_ListDoubleAddPairMiddle( Fxu_Double * pDiv, Fxu_Pair * pSpot, Fxu_Pair * pLink ) +{ + Fxu_ListPair * pList = &pDiv->lPairs; + assert( pSpot ); + assert( pSpot != pList->pTail ); + pLink->pDPrev = pSpot; + pLink->pDNext = pSpot->pDNext; + pLink->pDPrev->pDNext = pLink; + pLink->pDNext->pDPrev = pLink; + pList->nItems++; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_ListDoubleDelPair( Fxu_Double * pDiv, Fxu_Pair * pLink ) +{ + Fxu_ListPair * pList = &pDiv->lPairs; + if ( pList->pHead == pLink ) + pList->pHead = pLink->pDNext; + if ( pList->pTail == pLink ) + pList->pTail = pLink->pDPrev; + if ( pLink->pDPrev ) + pLink->pDPrev->pDNext = pLink->pDNext; + if ( pLink->pDNext ) + pLink->pDNext->pDPrev = pLink->pDPrev; + pList->nItems--; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_ListDoubleAddPairPlace( Fxu_Double * pDiv, Fxu_Pair * pPair, Fxu_Pair * pPairSpot ) +{ + printf( "Fxu_ListDoubleAddPairPlace() is called!\n" ); +} + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/fxu/fxuMatrix.c b/abc_with_bb_support/src/opt/fxu/fxuMatrix.c new file mode 100644 index 000000000..1db7eabb2 --- /dev/null +++ b/abc_with_bb_support/src/opt/fxu/fxuMatrix.c @@ -0,0 +1,374 @@ +/**CFile**************************************************************** + + FileName [fxuMatrix.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Procedures to manipulate the sparse matrix.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: fxuMatrix.c,v 1.0 2003/02/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fxuInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +extern unsigned int Cudd_Prime( unsigned int p ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fxu_Matrix * Fxu_MatrixAllocate() +{ + Fxu_Matrix * p; + p = ALLOC( Fxu_Matrix, 1 ); + memset( p, 0, sizeof(Fxu_Matrix) ); + p->nTableSize = Cudd_Prime(10000); + p->pTable = ALLOC( Fxu_ListDouble, p->nTableSize ); + memset( p->pTable, 0, sizeof(Fxu_ListDouble) * p->nTableSize ); +#ifndef USE_SYSTEM_MEMORY_MANAGEMENT + { + // get the largest size in bytes for the following structures: + // Fxu_Cube, Fxu_Var, Fxu_Lit, Fxu_Pair, Fxu_Double, Fxu_Single + // (currently, Fxu_Var, Fxu_Pair, Fxu_Double take 10 machine words) + int nSizeMax, nSizeCur; + nSizeMax = -1; + nSizeCur = sizeof(Fxu_Cube); + if ( nSizeMax < nSizeCur ) + nSizeMax = nSizeCur; + nSizeCur = sizeof(Fxu_Var); + if ( nSizeMax < nSizeCur ) + nSizeMax = nSizeCur; + nSizeCur = sizeof(Fxu_Lit); + if ( nSizeMax < nSizeCur ) + nSizeMax = nSizeCur; + nSizeCur = sizeof(Fxu_Pair); + if ( nSizeMax < nSizeCur ) + nSizeMax = nSizeCur; + nSizeCur = sizeof(Fxu_Double); + if ( nSizeMax < nSizeCur ) + nSizeMax = nSizeCur; + nSizeCur = sizeof(Fxu_Single); + if ( nSizeMax < nSizeCur ) + nSizeMax = nSizeCur; + p->pMemMan = Extra_MmFixedStart( nSizeMax ); + } +#endif + p->pHeapDouble = Fxu_HeapDoubleStart(); + p->pHeapSingle = Fxu_HeapSingleStart(); + p->vPairs = Vec_PtrAlloc( 100 ); + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_MatrixDelete( Fxu_Matrix * p ) +{ + Fxu_HeapDoubleCheck( p->pHeapDouble ); + Fxu_HeapDoubleStop( p->pHeapDouble ); + Fxu_HeapSingleStop( p->pHeapSingle ); + + // delete other things +#ifdef USE_SYSTEM_MEMORY_MANAGEMENT + // this code is not needed when the custom memory manager is used + { + Fxu_Cube * pCube, * pCube2; + Fxu_Var * pVar, * pVar2; + Fxu_Lit * pLit, * pLit2; + Fxu_Double * pDiv, * pDiv2; + Fxu_Single * pSingle, * pSingle2; + Fxu_Pair * pPair, * pPair2; + int i; + // delete the divisors + Fxu_MatrixForEachDoubleSafe( p, pDiv, pDiv2, i ) + { + Fxu_DoubleForEachPairSafe( pDiv, pPair, pPair2 ) + MEM_FREE_FXU( p, Fxu_Pair, 1, pPair ); + MEM_FREE_FXU( p, Fxu_Double, 1, pDiv ); + } + Fxu_MatrixForEachSingleSafe( p, pSingle, pSingle2 ) + MEM_FREE_FXU( p, Fxu_Single, 1, pSingle ); + // delete the entries + Fxu_MatrixForEachCube( p, pCube ) + Fxu_CubeForEachLiteralSafe( pCube, pLit, pLit2 ) + MEM_FREE_FXU( p, Fxu_Lit, 1, pLit ); + // delete the cubes + Fxu_MatrixForEachCubeSafe( p, pCube, pCube2 ) + MEM_FREE_FXU( p, Fxu_Cube, 1, pCube ); + // delete the vars + Fxu_MatrixForEachVariableSafe( p, pVar, pVar2 ) + MEM_FREE_FXU( p, Fxu_Var, 1, pVar ); + } +#else + Extra_MmFixedStop( p->pMemMan ); +#endif + + Vec_PtrFree( p->vPairs ); + FREE( p->pppPairs ); + FREE( p->ppPairs ); +// FREE( p->pPairsTemp ); + FREE( p->pTable ); + FREE( p->ppVars ); + FREE( p ); +} + + + +/**Function************************************************************* + + Synopsis [Adds a variable to the matrix.] + + Description [This procedure always adds variables at the end of the matrix. + It assigns the var's node and number. It adds the var to the linked list of + all variables and to the table of all nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fxu_Var * Fxu_MatrixAddVar( Fxu_Matrix * p ) +{ + Fxu_Var * pVar; + pVar = MEM_ALLOC_FXU( p, Fxu_Var, 1 ); + memset( pVar, 0, sizeof(Fxu_Var) ); + pVar->iVar = p->lVars.nItems; + p->ppVars[pVar->iVar] = pVar; + Fxu_ListMatrixAddVariable( p, pVar ); + return pVar; +} + +/**Function************************************************************* + + Synopsis [Adds a literal to the matrix.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fxu_Cube * Fxu_MatrixAddCube( Fxu_Matrix * p, Fxu_Var * pVar, int iCube ) +{ + Fxu_Cube * pCube; + pCube = MEM_ALLOC_FXU( p, Fxu_Cube, 1 ); + memset( pCube, 0, sizeof(Fxu_Cube) ); + pCube->pVar = pVar; + pCube->iCube = iCube; + Fxu_ListMatrixAddCube( p, pCube ); + return pCube; +} + +/**Function************************************************************* + + Synopsis [Adds a literal to the matrix.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_MatrixAddLiteral( Fxu_Matrix * p, Fxu_Cube * pCube, Fxu_Var * pVar ) +{ + Fxu_Lit * pLit; + pLit = MEM_ALLOC_FXU( p, Fxu_Lit, 1 ); + memset( pLit, 0, sizeof(Fxu_Lit) ); + // insert the literal into two linked lists + Fxu_ListCubeAddLiteral( pCube, pLit ); + Fxu_ListVarAddLiteral( pVar, pLit ); + // set the back pointers + pLit->pCube = pCube; + pLit->pVar = pVar; + pLit->iCube = pCube->iCube; + pLit->iVar = pVar->iVar; + // increment the literal counter + p->nEntries++; +} + +/**Function************************************************************* + + Synopsis [Deletes the divisor from the matrix.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_MatrixDelDivisor( Fxu_Matrix * p, Fxu_Double * pDiv ) +{ + // delete divisor from the table + Fxu_ListTableDelDivisor( p, pDiv ); + // recycle the divisor + MEM_FREE_FXU( p, Fxu_Double, 1, pDiv ); +} + +/**Function************************************************************* + + Synopsis [Deletes the literal fromthe matrix.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_MatrixDelLiteral( Fxu_Matrix * p, Fxu_Lit * pLit ) +{ + // delete the literal + Fxu_ListCubeDelLiteral( pLit->pCube, pLit ); + Fxu_ListVarDelLiteral( pLit->pVar, pLit ); + MEM_FREE_FXU( p, Fxu_Lit, 1, pLit ); + // increment the literal counter + p->nEntries--; +} + + +/**Function************************************************************* + + Synopsis [Creates and adds a single cube divisor.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_MatrixAddSingle( Fxu_Matrix * p, Fxu_Var * pVar1, Fxu_Var * pVar2, int Weight ) +{ + Fxu_Single * pSingle; + assert( pVar1->iVar < pVar2->iVar ); + pSingle = MEM_ALLOC_FXU( p, Fxu_Single, 1 ); + memset( pSingle, 0, sizeof(Fxu_Single) ); + pSingle->Num = p->lSingles.nItems; + pSingle->Weight = Weight; + pSingle->HNum = 0; + pSingle->pVar1 = pVar1; + pSingle->pVar2 = pVar2; + Fxu_ListMatrixAddSingle( p, pSingle ); + // add to the heap + Fxu_HeapSingleInsert( p->pHeapSingle, pSingle ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_MatrixAddDivisor( Fxu_Matrix * p, Fxu_Cube * pCube1, Fxu_Cube * pCube2 ) +{ + Fxu_Pair * pPair; + Fxu_Double * pDiv; + int nBase, nLits1, nLits2; + int fFound; + unsigned Key; + + // canonicize the pair + Fxu_PairCanonicize( &pCube1, &pCube2 ); + // compute the hash key + Key = Fxu_PairHashKey( p, pCube1, pCube2, &nBase, &nLits1, &nLits2 ); + + // create the cube pair + pPair = Fxu_PairAlloc( p, pCube1, pCube2 ); + pPair->nBase = nBase; + pPair->nLits1 = nLits1; + pPair->nLits2 = nLits2; + + // check if the divisor for this pair already exists + fFound = 0; + Key %= p->nTableSize; + Fxu_TableForEachDouble( p, Key, pDiv ) + { + if ( Fxu_PairCompare( pPair, pDiv->lPairs.pTail ) ) // they are equal + { + fFound = 1; + break; + } + } + + if ( !fFound ) + { // create the new divisor + pDiv = MEM_ALLOC_FXU( p, Fxu_Double, 1 ); + memset( pDiv, 0, sizeof(Fxu_Double) ); + pDiv->Key = Key; + // set the number of this divisor + pDiv->Num = p->nDivsTotal++; // p->nDivs; + // insert the divisor in the table + Fxu_ListTableAddDivisor( p, pDiv ); + // set the initial cost of the divisor + pDiv->Weight -= pPair->nLits1 + pPair->nLits2; + } + + // link the pair to the cubes + Fxu_PairAdd( pPair ); + // connect the pair and the divisor + pPair->pDiv = pDiv; + Fxu_ListDoubleAddPairLast( pDiv, pPair ); + // update the max number of pairs in a divisor +// if ( p->nPairsMax < pDiv->lPairs.nItems ) +// p->nPairsMax = pDiv->lPairs.nItems; + // update the divisor's weight + pDiv->Weight += pPair->nLits1 + pPair->nLits2 - 1 + pPair->nBase; + if ( fFound ) // update the divisor in the heap + Fxu_HeapDoubleUpdate( p->pHeapDouble, pDiv ); + else // add the new divisor to the heap + Fxu_HeapDoubleInsert( p->pHeapDouble, pDiv ); +} + +/* + { + int piVarsC1[100], piVarsC2[100], nVarsC1, nVarsC2; + Fxu_Double * pDivCur; + Fxu_MatrixGetDoubleVars( p, pDiv, piVarsC1, piVarsC2, &nVarsC1, &nVarsC2 ); + pDivCur = Fxu_MatrixFindDouble( p, piVarsC1, piVarsC2, nVarsC1, nVarsC2 ); + assert( pDivCur == pDiv ); + } +*/ + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/fxu/fxuPair.c b/abc_with_bb_support/src/opt/fxu/fxuPair.c new file mode 100644 index 000000000..0be493c45 --- /dev/null +++ b/abc_with_bb_support/src/opt/fxu/fxuPair.c @@ -0,0 +1,555 @@ +/**CFile**************************************************************** + + FileName [fxuPair.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Operations on cube pairs.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: fxuPair.c,v 1.0 2003/02/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fxuInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define MAX_PRIMES 304 + +static s_Primes[MAX_PRIMES] = +{ + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, + 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, + 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, + 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, + 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, + 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, + 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, + 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, + 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, + 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, + 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, + 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, + 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, + 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, + 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, + 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, + 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, + 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, + 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, + 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, + 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, + 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, + 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, + 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, + 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, + 1993, 1997, 1999, 2003 +}; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Find the canonical permutation of two cubes in the pair.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_PairCanonicize( Fxu_Cube ** ppCube1, Fxu_Cube ** ppCube2 ) +{ + Fxu_Lit * pLit1, * pLit2; + Fxu_Cube * pCubeTemp; + + // walk through the cubes to determine + // the one that has higher first variable + pLit1 = (*ppCube1)->lLits.pHead; + pLit2 = (*ppCube2)->lLits.pHead; + while ( 1 ) + { + if ( pLit1->iVar == pLit2->iVar ) + { + pLit1 = pLit1->pHNext; + pLit2 = pLit2->pHNext; + continue; + } + assert( pLit1 && pLit2 ); // this is true if the covers are SCC-free + if ( pLit1->iVar > pLit2->iVar ) + { // swap the cubes + pCubeTemp = *ppCube1; + *ppCube1 = *ppCube2; + *ppCube2 = pCubeTemp; + } + break; + } +} + +/**Function************************************************************* + + Synopsis [Find the canonical permutation of two cubes in the pair.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_PairCanonicize2( Fxu_Cube ** ppCube1, Fxu_Cube ** ppCube2 ) +{ + Fxu_Cube * pCubeTemp; + // canonicize the pair by ordering the cubes + if ( (*ppCube1)->iCube > (*ppCube2)->iCube ) + { // swap the cubes + pCubeTemp = *ppCube1; + *ppCube1 = *ppCube2; + *ppCube2 = pCubeTemp; + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Fxu_PairHashKeyArray( Fxu_Matrix * p, int piVarsC1[], int piVarsC2[], int nVarsC1, int nVarsC2 ) +{ + int Offset1 = 100, Offset2 = 200, i; + unsigned Key; + // compute the hash key + Key = 0; + for ( i = 0; i < nVarsC1; i++ ) + Key ^= s_Primes[Offset1+i] * piVarsC1[i]; + for ( i = 0; i < nVarsC2; i++ ) + Key ^= s_Primes[Offset2+i] * piVarsC2[i]; + return Key; +} + +/**Function************************************************************* + + Synopsis [Computes the hash key of the divisor represented by the pair of cubes.] + + Description [Goes through the variables in both cubes. Skips the identical + ones (this corresponds to making the cubes cube-free). Computes the hash + value of the cubes. Assigns the number of literals in the base and in the + cubes without base.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Fxu_PairHashKey( Fxu_Matrix * p, Fxu_Cube * pCube1, Fxu_Cube * pCube2, + int * pnBase, int * pnLits1, int * pnLits2 ) +{ + int Offset1 = 100, Offset2 = 200; + int nBase, nLits1, nLits2; + Fxu_Lit * pLit1, * pLit2; + unsigned Key; + + // compute the hash key + Key = 0; + nLits1 = 0; + nLits2 = 0; + nBase = 0; + pLit1 = pCube1->lLits.pHead; + pLit2 = pCube2->lLits.pHead; + while ( 1 ) + { + if ( pLit1 && pLit2 ) + { + if ( pLit1->iVar == pLit2->iVar ) + { // ensure cube-free + pLit1 = pLit1->pHNext; + pLit2 = pLit2->pHNext; + // add this literal to the base + nBase++; + } + else if ( pLit1->iVar < pLit2->iVar ) + { + Key ^= s_Primes[Offset1+nLits1] * pLit1->iVar; + pLit1 = pLit1->pHNext; + nLits1++; + } + else + { + Key ^= s_Primes[Offset2+nLits2] * pLit2->iVar; + pLit2 = pLit2->pHNext; + nLits2++; + } + } + else if ( pLit1 && !pLit2 ) + { + Key ^= s_Primes[Offset1+nLits1] * pLit1->iVar; + pLit1 = pLit1->pHNext; + nLits1++; + } + else if ( !pLit1 && pLit2 ) + { + Key ^= s_Primes[Offset2+nLits2] * pLit2->iVar; + pLit2 = pLit2->pHNext; + nLits2++; + } + else + break; + } + *pnBase = nBase; + *pnLits1 = nLits1; + *pnLits2 = nLits2; + return Key; +} + +/**Function************************************************************* + + Synopsis [Compares the two pairs.] + + Description [Returns 1 if the divisors represented by these pairs + are equal.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fxu_PairCompare( Fxu_Pair * pPair1, Fxu_Pair * pPair2 ) +{ + Fxu_Lit * pD1C1, * pD1C2; + Fxu_Lit * pD2C1, * pD2C2; + int TopVar1, TopVar2; + int Code; + + if ( pPair1->nLits1 != pPair2->nLits1 ) + return 0; + if ( pPair1->nLits2 != pPair2->nLits2 ) + return 0; + + pD1C1 = pPair1->pCube1->lLits.pHead; + pD1C2 = pPair1->pCube2->lLits.pHead; + + pD2C1 = pPair2->pCube1->lLits.pHead; + pD2C2 = pPair2->pCube2->lLits.pHead; + + Code = pD1C1? 8: 0; + Code |= pD1C2? 4: 0; + Code |= pD2C1? 2: 0; + Code |= pD2C2? 1: 0; + assert( Code == 15 ); + + while ( 1 ) + { + switch ( Code ) + { + case 0: // -- -- NULL NULL NULL NULL + return 1; + case 1: // -- -1 NULL NULL NULL pD2C2 + return 0; + case 2: // -- 1- NULL NULL pD2C1 NULL + return 0; + case 3: // -- 11 NULL NULL pD2C1 pD2C2 + if ( pD2C1->iVar != pD2C2->iVar ) + return 0; + pD2C1 = pD2C1->pHNext; + pD2C2 = pD2C2->pHNext; + break; + case 4: // -1 -- NULL pD1C2 NULL NULL + return 0; + case 5: // -1 -1 NULL pD1C2 NULL pD2C2 + if ( pD1C2->iVar != pD2C2->iVar ) + return 0; + pD1C2 = pD1C2->pHNext; + pD2C2 = pD2C2->pHNext; + break; + case 6: // -1 1- NULL pD1C2 pD2C1 NULL + return 0; + case 7: // -1 11 NULL pD1C2 pD2C1 pD2C2 + TopVar2 = Fxu_Min( pD2C1->iVar, pD2C2->iVar ); + if ( TopVar2 == pD1C2->iVar ) + { + if ( pD2C1->iVar <= pD2C2->iVar ) + return 0; + pD1C2 = pD1C2->pHNext; + pD2C2 = pD2C2->pHNext; + } + else if ( TopVar2 < pD1C2->iVar ) + { + if ( pD2C1->iVar != pD2C2->iVar ) + return 0; + pD2C1 = pD2C1->pHNext; + pD2C2 = pD2C2->pHNext; + } + else + return 0; + break; + case 8: // 1- -- pD1C1 NULL NULL NULL + return 0; + case 9: // 1- -1 pD1C1 NULL NULL pD2C2 + return 0; + case 10: // 1- 1- pD1C1 NULL pD2C1 NULL + if ( pD1C1->iVar != pD2C1->iVar ) + return 0; + pD1C1 = pD1C1->pHNext; + pD2C1 = pD2C1->pHNext; + break; + case 11: // 1- 11 pD1C1 NULL pD2C1 pD2C2 + TopVar2 = Fxu_Min( pD2C1->iVar, pD2C2->iVar ); + if ( TopVar2 == pD1C1->iVar ) + { + if ( pD2C1->iVar >= pD2C2->iVar ) + return 0; + pD1C1 = pD1C1->pHNext; + pD2C1 = pD2C1->pHNext; + } + else if ( TopVar2 < pD1C1->iVar ) + { + if ( pD2C1->iVar != pD2C2->iVar ) + return 0; + pD2C1 = pD2C1->pHNext; + pD2C2 = pD2C2->pHNext; + } + else + return 0; + break; + case 12: // 11 -- pD1C1 pD1C2 NULL NULL + if ( pD1C1->iVar != pD1C2->iVar ) + return 0; + pD1C1 = pD1C1->pHNext; + pD1C2 = pD1C2->pHNext; + break; + case 13: // 11 -1 pD1C1 pD1C2 NULL pD2C2 + TopVar1 = Fxu_Min( pD1C1->iVar, pD1C2->iVar ); + if ( TopVar1 == pD2C2->iVar ) + { + if ( pD1C1->iVar <= pD1C2->iVar ) + return 0; + pD1C2 = pD1C2->pHNext; + pD2C2 = pD2C2->pHNext; + } + else if ( TopVar1 < pD2C2->iVar ) + { + if ( pD1C1->iVar != pD1C2->iVar ) + return 0; + pD1C1 = pD1C1->pHNext; + pD1C2 = pD1C2->pHNext; + } + else + return 0; + break; + case 14: // 11 1- pD1C1 pD1C2 pD2C1 NULL + TopVar1 = Fxu_Min( pD1C1->iVar, pD1C2->iVar ); + if ( TopVar1 == pD2C1->iVar ) + { + if ( pD1C1->iVar >= pD1C2->iVar ) + return 0; + pD1C1 = pD1C1->pHNext; + pD2C1 = pD2C1->pHNext; + } + else if ( TopVar1 < pD2C1->iVar ) + { + if ( pD1C1->iVar != pD1C2->iVar ) + return 0; + pD1C1 = pD1C1->pHNext; + pD1C2 = pD1C2->pHNext; + } + else + return 0; + break; + case 15: // 11 11 pD1C1 pD1C2 pD2C1 pD2C2 + TopVar1 = Fxu_Min( pD1C1->iVar, pD1C2->iVar ); + TopVar2 = Fxu_Min( pD2C1->iVar, pD2C2->iVar ); + if ( TopVar1 == TopVar2 ) + { + if ( pD1C1->iVar == pD1C2->iVar ) + { + if ( pD2C1->iVar != pD2C2->iVar ) + return 0; + pD1C1 = pD1C1->pHNext; + pD1C2 = pD1C2->pHNext; + pD2C1 = pD2C1->pHNext; + pD2C2 = pD2C2->pHNext; + } + else + { + if ( pD2C1->iVar == pD2C2->iVar ) + return 0; + if ( pD1C1->iVar < pD1C2->iVar ) + { + if ( pD2C1->iVar > pD2C2->iVar ) + return 0; + pD1C1 = pD1C1->pHNext; + pD2C1 = pD2C1->pHNext; + } + else + { + if ( pD2C1->iVar < pD2C2->iVar ) + return 0; + pD1C2 = pD1C2->pHNext; + pD2C2 = pD2C2->pHNext; + } + } + } + else if ( TopVar1 < TopVar2 ) + { + if ( pD1C1->iVar != pD1C2->iVar ) + return 0; + pD1C1 = pD1C1->pHNext; + pD1C2 = pD1C2->pHNext; + } + else + { + if ( pD2C1->iVar != pD2C2->iVar ) + return 0; + pD2C1 = pD2C1->pHNext; + pD2C2 = pD2C2->pHNext; + } + break; + default: + assert( 0 ); + break; + } + + Code = pD1C1? 8: 0; + Code |= pD1C2? 4: 0; + Code |= pD2C1? 2: 0; + Code |= pD2C2? 1: 0; + } + return 1; +} + + +/**Function************************************************************* + + Synopsis [Allocates the storage for cubes pairs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_PairAllocStorage( Fxu_Var * pVar, int nCubes ) +{ + int k; +// assert( pVar->nCubes == 0 ); + pVar->nCubes = nCubes; + // allocate memory for all the pairs + pVar->ppPairs = ALLOC( Fxu_Pair **, nCubes ); + pVar->ppPairs[0] = ALLOC( Fxu_Pair *, nCubes * nCubes ); + memset( pVar->ppPairs[0], 0, sizeof(Fxu_Pair *) * nCubes * nCubes ); + for ( k = 1; k < nCubes; k++ ) + pVar->ppPairs[k] = pVar->ppPairs[k-1] + nCubes; +} + +/**Function************************************************************* + + Synopsis [Clears all pairs associated with this cube.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_PairClearStorage( Fxu_Cube * pCube ) +{ + Fxu_Var * pVar; + int i; + pVar = pCube->pVar; + for ( i = 0; i < pVar->nCubes; i++ ) + { + pVar->ppPairs[pCube->iCube][i] = NULL; + pVar->ppPairs[i][pCube->iCube] = NULL; + } +} + +/**Function************************************************************* + + Synopsis [Clears all pairs associated with this cube.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_PairFreeStorage( Fxu_Var * pVar ) +{ + if ( pVar->ppPairs ) + { + FREE( pVar->ppPairs[0] ); + FREE( pVar->ppPairs ); + } +} + +/**Function************************************************************* + + Synopsis [Adds the pair to storage.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fxu_Pair * Fxu_PairAlloc( Fxu_Matrix * p, Fxu_Cube * pCube1, Fxu_Cube * pCube2 ) +{ + Fxu_Pair * pPair; + assert( pCube1->pVar == pCube2->pVar ); + pPair = MEM_ALLOC_FXU( p, Fxu_Pair, 1 ); + memset( pPair, 0, sizeof(Fxu_Pair) ); + pPair->pCube1 = pCube1; + pPair->pCube2 = pCube2; + pPair->iCube1 = pCube1->iCube; + pPair->iCube2 = pCube2->iCube; + return pPair; +} + +/**Function************************************************************* + + Synopsis [Adds the pair to storage.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_PairAdd( Fxu_Pair * pPair ) +{ + Fxu_Var * pVar; + + pVar = pPair->pCube1->pVar; + assert( pVar == pPair->pCube2->pVar ); + + pVar->ppPairs[pPair->iCube1][pPair->iCube2] = pPair; + pVar->ppPairs[pPair->iCube2][pPair->iCube1] = pPair; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/fxu/fxuPrint.c b/abc_with_bb_support/src/opt/fxu/fxuPrint.c new file mode 100644 index 000000000..dc6402747 --- /dev/null +++ b/abc_with_bb_support/src/opt/fxu/fxuPrint.c @@ -0,0 +1,195 @@ +/**CFile**************************************************************** + + FileName [fxuPrint.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Various printing procedures.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: fxuPrint.c,v 1.0 2003/02/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fxuInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_MatrixPrint( FILE * pFile, Fxu_Matrix * p ) +{ + Fxu_Var * pVar; + Fxu_Cube * pCube; + Fxu_Double * pDiv; + Fxu_Single * pSingle; + Fxu_Lit * pLit; + Fxu_Pair * pPair; + int i, LastNum; + int fStdout; + + fStdout = 1; + if ( pFile == NULL ) + { + pFile = fopen( "matrix.txt", "w" ); + fStdout = 0; + } + + fprintf( pFile, "Matrix has %d vars, %d cubes, %d literals, %d divisors.\n", + p->lVars.nItems, p->lCubes.nItems, p->nEntries, p->nDivs ); + fprintf( pFile, "Divisors selected so far: single = %d, double = %d.\n", + p->nDivs1, p->nDivs2 ); + fprintf( pFile, "\n" ); + + // print the numbers on top of the matrix + for ( i = 0; i < 12; i++ ) + fprintf( pFile, " " ); + Fxu_MatrixForEachVariable( p, pVar ) + fprintf( pFile, "%d", pVar->iVar % 10 ); + fprintf( pFile, "\n" ); + + // print the rows + Fxu_MatrixForEachCube( p, pCube ) + { + fprintf( pFile, "%4d", pCube->iCube ); + fprintf( pFile, " " ); + fprintf( pFile, "%4d", pCube->pVar->iVar ); + fprintf( pFile, " " ); + + // print the literals + LastNum = -1; + Fxu_CubeForEachLiteral( pCube, pLit ) + { + for ( i = LastNum + 1; i < pLit->pVar->iVar; i++ ) + fprintf( pFile, "." ); + fprintf( pFile, "1" ); + LastNum = i; + } + for ( i = LastNum + 1; i < p->lVars.nItems; i++ ) + fprintf( pFile, "." ); + fprintf( pFile, "\n" ); + } + fprintf( pFile, "\n" ); + + // print the double-cube divisors + fprintf( pFile, "The double divisors are:\n" ); + Fxu_MatrixForEachDouble( p, pDiv, i ) + { + fprintf( pFile, "Divisor #%3d (lit=%d,%d) (w=%2d): ", + pDiv->Num, pDiv->lPairs.pHead->nLits1, + pDiv->lPairs.pHead->nLits2, pDiv->Weight ); + Fxu_DoubleForEachPair( pDiv, pPair ) + fprintf( pFile, " <%d, %d> (b=%d)", + pPair->pCube1->iCube, pPair->pCube2->iCube, pPair->nBase ); + fprintf( pFile, "\n" ); + } + fprintf( pFile, "\n" ); + + // print the divisors associated with each cube + fprintf( pFile, "The cubes are:\n" ); + Fxu_MatrixForEachCube( p, pCube ) + { + fprintf( pFile, "Cube #%3d: ", pCube->iCube ); + if ( pCube->pVar->ppPairs ) + Fxu_CubeForEachPair( pCube, pPair, i ) + fprintf( pFile, " <%d %d> (d=%d) (b=%d)", + pPair->iCube1, pPair->iCube2, pPair->pDiv->Num, pPair->nBase ); + fprintf( pFile, "\n" ); + } + fprintf( pFile, "\n" ); + + // print the single-cube divisors + fprintf( pFile, "The single divisors are:\n" ); + Fxu_MatrixForEachSingle( p, pSingle ) + { + fprintf( pFile, "Single-cube divisor #%5d: Var1 = %4d. Var2 = %4d. Weight = %2d\n", + pSingle->Num, pSingle->pVar1->iVar, pSingle->pVar2->iVar, pSingle->Weight ); + } + fprintf( pFile, "\n" ); + +/* + { + int Index; + fprintf( pFile, "Distribution of divisors in the hash table:\n" ); + for ( Index = 0; Index < p->nTableSize; Index++ ) + fprintf( pFile, " %d", p->pTable[Index].nItems ); + fprintf( pFile, "\n" ); + } +*/ + if ( !fStdout ) + fclose( pFile ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_MatrixPrintDivisorProfile( FILE * pFile, Fxu_Matrix * p ) +{ + Fxu_Double * pDiv; + int WeightMax; + int * pProfile; + int Counter1; // the number of -1 weight + int CounterL; // the number of less than -1 weight + int i; + + WeightMax = Fxu_HeapDoubleReadMaxWeight( p->pHeapDouble ); + pProfile = ALLOC( int, (WeightMax + 1) ); + memset( pProfile, 0, sizeof(int) * (WeightMax + 1) ); + + Counter1 = 0; + CounterL = 0; + Fxu_MatrixForEachDouble( p, pDiv, i ) + { + assert( pDiv->Weight <= WeightMax ); + if ( pDiv->Weight == -1 ) + Counter1++; + else if ( pDiv->Weight < 0 ) + CounterL++; + else + pProfile[ pDiv->Weight ]++; + } + + fprintf( pFile, "The double divisors profile:\n" ); + fprintf( pFile, "Weight < -1 divisors = %6d\n", CounterL ); + fprintf( pFile, "Weight -1 divisors = %6d\n", Counter1 ); + for ( i = 0; i <= WeightMax; i++ ) + if ( pProfile[i] ) + fprintf( pFile, "Weight %3d divisors = %6d\n", i, pProfile[i] ); + fprintf( pFile, "End of divisor profile printout\n" ); + FREE( pProfile ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/fxu/fxuReduce.c b/abc_with_bb_support/src/opt/fxu/fxuReduce.c new file mode 100644 index 000000000..6752adec7 --- /dev/null +++ b/abc_with_bb_support/src/opt/fxu/fxuReduce.c @@ -0,0 +1,204 @@ +/**CFile**************************************************************** + + FileName [fxuReduce.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Procedures to reduce the number of considered cube pairs.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: fxuReduce.c,v 1.0 2003/02/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "fxuInt.h" +#include "fxu.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Fxu_CountPairDiffs( char * pCover, unsigned char pDiffs[] ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Precomputes the pairs to use for creating two-cube divisors.] + + Description [This procedure takes the matrix with variables and cubes + allocated (p), the original covers of the nodes (i-sets) and their number + (ppCovers,nCovers). The maximum number of pairs to compute and the total + number of pairs in existence. This procedure adds to the storage of + divisors exactly the given number of pairs (nPairsMax) while taking + first those pairs that have the smallest number of literals in their + cube-free form.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fxu_PreprocessCubePairs( Fxu_Matrix * p, Vec_Ptr_t * vCovers, int nPairsTotal, int nPairsMax ) +{ + unsigned char * pnLitsDiff; // storage for the counters of diff literals + int * pnPairCounters; // the counters of pairs for each number of diff literals + Fxu_Cube * pCubeFirst, * pCubeLast; + Fxu_Cube * pCube1, * pCube2; + Fxu_Var * pVar; + int nCubes, nBitsMax, nSum; + int CutOffNum, CutOffQuant; + int iPair, iQuant, k, c; + int clk = clock(); + char * pSopCover; + int nFanins; + + assert( nPairsMax < nPairsTotal ); + + // allocate storage for counter of diffs + pnLitsDiff = ALLOC( unsigned char, nPairsTotal ); + // go through the covers and precompute the distances between the pairs + iPair = 0; + nBitsMax = -1; + for ( c = 0; c < vCovers->nSize; c++ ) + if ( pSopCover = vCovers->pArray[c] ) + { + nFanins = Abc_SopGetVarNum(pSopCover); + // precompute the differences + Fxu_CountPairDiffs( pSopCover, pnLitsDiff + iPair ); + // update the counter + nCubes = Abc_SopGetCubeNum( pSopCover ); + iPair += nCubes * (nCubes - 1) / 2; + if ( nBitsMax < nFanins ) + nBitsMax = nFanins; + } + assert( iPair == nPairsTotal ); + + // allocate storage for counters of cube pairs by difference + pnPairCounters = ALLOC( int, 2 * nBitsMax ); + memset( pnPairCounters, 0, sizeof(int) * 2 * nBitsMax ); + // count the number of different pairs + for ( k = 0; k < nPairsTotal; k++ ) + pnPairCounters[ pnLitsDiff[k] ]++; + // determine what pairs to take starting from the lower + // so that there would be exactly pPairsMax pairs + if ( pnPairCounters[0] != 0 ) + { + printf( "The SOPs of the nodes are not cube-free. Run \"bdd; sop\" before \"fx\".\n" ); + return 0; + } + if ( pnPairCounters[1] != 0 ) + { + printf( "The SOPs of the nodes are not SCC-free. Run \"bdd; sop\" before \"fx\".\n" ); + return 0; + } + assert( pnPairCounters[0] == 0 ); // otherwise, covers are not dup-free + assert( pnPairCounters[1] == 0 ); // otherwise, covers are not SCC-free + nSum = 0; + for ( k = 0; k < 2 * nBitsMax; k++ ) + { + nSum += pnPairCounters[k]; + if ( nSum >= nPairsMax ) + { + CutOffNum = k; + CutOffQuant = pnPairCounters[k] - (nSum - nPairsMax); + break; + } + } + FREE( pnPairCounters ); + + // set to 0 all the pairs above the cut-off number and quantity + iQuant = 0; + iPair = 0; + for ( k = 0; k < nPairsTotal; k++ ) + if ( pnLitsDiff[k] > CutOffNum ) + pnLitsDiff[k] = 0; + else if ( pnLitsDiff[k] == CutOffNum ) + { + if ( iQuant++ >= CutOffQuant ) + pnLitsDiff[k] = 0; + else + iPair++; + } + else + iPair++; + assert( iPair == nPairsMax ); + + // collect the corresponding pairs and add the divisors + iPair = 0; + for ( c = 0; c < vCovers->nSize; c++ ) + if ( pSopCover = vCovers->pArray[c] ) + { + // get the var + pVar = p->ppVars[2*c+1]; + // get the first cube + pCubeFirst = pVar->pFirst; + // get the last cube + pCubeLast = pCubeFirst; + for ( k = 0; k < pVar->nCubes; k++ ) + pCubeLast = pCubeLast->pNext; + assert( pCubeLast == NULL || pCubeLast->pVar != pVar ); + + // go through the cube pairs + for ( pCube1 = pCubeFirst; pCube1 != pCubeLast; pCube1 = pCube1->pNext ) + for ( pCube2 = pCube1->pNext; pCube2 != pCubeLast; pCube2 = pCube2->pNext ) + if ( pnLitsDiff[iPair++] ) + { // create the divisors for this pair + Fxu_MatrixAddDivisor( p, pCube1, pCube2 ); + } + } + assert( iPair == nPairsTotal ); + FREE( pnLitsDiff ); +//PRT( "Preprocess", clock() - clk ); + return 1; +} + + +/**Function************************************************************* + + Synopsis [Counts the differences in each cube pair in the cover.] + + Description [Takes the cover (pCover) and the array where the + diff counters go (pDiffs). The array pDiffs should have as many + entries as there are different pairs of cubes in the cover: n(n-1)/2. + Fills out the array pDiffs with the following info: For each cube + pair, included in the array is the number of literals in both cubes + after they are made cube free.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fxu_CountPairDiffs( char * pCover, unsigned char pDiffs[] ) +{ + char * pCube1, * pCube2; + int nOnes, nCubePairs, nFanins, v; + nCubePairs = 0; + nFanins = Abc_SopGetVarNum(pCover); + Abc_SopForEachCube( pCover, nFanins, pCube1 ) + Abc_SopForEachCube( pCube1, nFanins, pCube2 ) + { + if ( pCube1 == pCube2 ) + continue; + nOnes = 0; + for ( v = 0; v < nFanins; v++ ) + nOnes += (pCube1[v] != pCube2[v]); + pDiffs[nCubePairs++] = nOnes; + } + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/fxu/fxuSelect.c b/abc_with_bb_support/src/opt/fxu/fxuSelect.c new file mode 100644 index 000000000..d62ee992f --- /dev/null +++ b/abc_with_bb_support/src/opt/fxu/fxuSelect.c @@ -0,0 +1,603 @@ +/**CFile**************************************************************** + + FileName [fxuSelect.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Procedures to select the best divisor/complement pair.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: fxuSelect.c,v 1.0 2003/02/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fxuInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define MAX_SIZE_LOOKAHEAD 20 + +static int Fxu_MatrixFindComplement( Fxu_Matrix * p, int iVar ); + +static Fxu_Double * Fxu_MatrixFindComplementSingle( Fxu_Matrix * p, Fxu_Single * pSingle ); +static Fxu_Single * Fxu_MatrixFindComplementDouble2( Fxu_Matrix * p, Fxu_Double * pDouble ); +static Fxu_Double * Fxu_MatrixFindComplementDouble4( Fxu_Matrix * p, Fxu_Double * pDouble ); + +Fxu_Double * Fxu_MatrixFindDouble( Fxu_Matrix * p, + int piVarsC1[], int piVarsC2[], int nVarsC1, int nVarsC2 ); +void Fxu_MatrixGetDoubleVars( Fxu_Matrix * p, Fxu_Double * pDouble, + int piVarsC1[], int piVarsC2[], int * pnVarsC1, int * pnVarsC2 ); + + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Selects the best pair (Single,Double) and returns their weight.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fxu_Select( Fxu_Matrix * p, Fxu_Single ** ppSingle, Fxu_Double ** ppDouble ) +{ + // the top entries + Fxu_Single * pSingles[MAX_SIZE_LOOKAHEAD]; + Fxu_Double * pDoubles[MAX_SIZE_LOOKAHEAD]; + // the complements + Fxu_Double * pSCompl[MAX_SIZE_LOOKAHEAD]; + Fxu_Single * pDComplS[MAX_SIZE_LOOKAHEAD]; + Fxu_Double * pDComplD[MAX_SIZE_LOOKAHEAD]; + Fxu_Pair * pPair; + int nSingles; + int nDoubles; + int i; + int WeightBest; + int WeightCur; + int iNum, fBestS; + + // collect the top entries from the queues + for ( nSingles = 0; nSingles < MAX_SIZE_LOOKAHEAD; nSingles++ ) + { + pSingles[nSingles] = Fxu_HeapSingleGetMax( p->pHeapSingle ); + if ( pSingles[nSingles] == NULL ) + break; + } + // put them back into the queue + for ( i = 0; i < nSingles; i++ ) + if ( pSingles[i] ) + Fxu_HeapSingleInsert( p->pHeapSingle, pSingles[i] ); + + // the same for doubles + // collect the top entries from the queues + for ( nDoubles = 0; nDoubles < MAX_SIZE_LOOKAHEAD; nDoubles++ ) + { + pDoubles[nDoubles] = Fxu_HeapDoubleGetMax( p->pHeapDouble ); + if ( pDoubles[nDoubles] == NULL ) + break; + } + // put them back into the queue + for ( i = 0; i < nDoubles; i++ ) + if ( pDoubles[i] ) + Fxu_HeapDoubleInsert( p->pHeapDouble, pDoubles[i] ); + + // for each single, find the complement double (if any) + for ( i = 0; i < nSingles; i++ ) + if ( pSingles[i] ) + pSCompl[i] = Fxu_MatrixFindComplementSingle( p, pSingles[i] ); + + // for each double, find the complement single or double (if any) + for ( i = 0; i < nDoubles; i++ ) + if ( pDoubles[i] ) + { + pPair = pDoubles[i]->lPairs.pHead; + if ( pPair->nLits1 == 1 && pPair->nLits2 == 1 ) + { + pDComplS[i] = Fxu_MatrixFindComplementDouble2( p, pDoubles[i] ); + pDComplD[i] = NULL; + } +// else if ( pPair->nLits1 == 2 && pPair->nLits2 == 2 ) +// { +// pDComplS[i] = NULL; +// pDComplD[i] = Fxu_MatrixFindComplementDouble4( p, pDoubles[i] ); +// } + else + { + pDComplS[i] = NULL; + pDComplD[i] = NULL; + } + } + + // select the best pair + WeightBest = -1; + for ( i = 0; i < nSingles; i++ ) + { + WeightCur = pSingles[i]->Weight; + if ( pSCompl[i] ) + { + // add the weight of the double + WeightCur += pSCompl[i]->Weight; + // there is no need to implement this double, so... + pPair = pSCompl[i]->lPairs.pHead; + WeightCur += pPair->nLits1 + pPair->nLits2; + } + if ( WeightBest < WeightCur ) + { + WeightBest = WeightCur; + *ppSingle = pSingles[i]; + *ppDouble = pSCompl[i]; + fBestS = 1; + iNum = i; + } + } + for ( i = 0; i < nDoubles; i++ ) + { + WeightCur = pDoubles[i]->Weight; + if ( pDComplS[i] ) + { + // add the weight of the single + WeightCur += pDComplS[i]->Weight; + // there is no need to implement this double, so... + pPair = pDoubles[i]->lPairs.pHead; + WeightCur += pPair->nLits1 + pPair->nLits2; + } + if ( WeightBest < WeightCur ) + { + WeightBest = WeightCur; + *ppSingle = pDComplS[i]; + *ppDouble = pDoubles[i]; + fBestS = 0; + iNum = i; + } + } +/* + // print the statistics + printf( "\n" ); + for ( i = 0; i < nSingles; i++ ) + { + printf( "Single #%d: Weight = %3d. ", i, pSingles[i]->Weight ); + printf( "Compl: " ); + if ( pSCompl[i] == NULL ) + printf( "None." ); + else + printf( "D Weight = %3d Sum = %3d", + pSCompl[i]->Weight, pSCompl[i]->Weight + pSingles[i]->Weight ); + printf( "\n" ); + } + printf( "\n" ); + for ( i = 0; i < nDoubles; i++ ) + { + printf( "Double #%d: Weight = %3d. ", i, pDoubles[i]->Weight ); + printf( "Compl: " ); + if ( pDComplS[i] == NULL && pDComplD[i] == NULL ) + printf( "None." ); + else if ( pDComplS[i] ) + printf( "S Weight = %3d Sum = %3d", + pDComplS[i]->Weight, pDComplS[i]->Weight + pDoubles[i]->Weight ); + else if ( pDComplD[i] ) + printf( "D Weight = %3d Sum = %3d", + pDComplD[i]->Weight, pDComplD[i]->Weight + pDoubles[i]->Weight ); + printf( "\n" ); + } + if ( WeightBest == -1 ) + printf( "Selected NONE\n" ); + else + { + printf( "Selected = %s. ", fBestS? "S": "D" ); + printf( "Number = %d. ", iNum ); + printf( "Weight = %d.\n", WeightBest ); + } + printf( "\n" ); +*/ + return WeightBest; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fxu_Double * Fxu_MatrixFindComplementSingle( Fxu_Matrix * p, Fxu_Single * pSingle ) +{ +// int * pValue2Node = p->pValue2Node; + int iVar1, iVar2; + int iVar1C, iVar2C; + // get the variables of this single div + iVar1 = pSingle->pVar1->iVar; + iVar2 = pSingle->pVar2->iVar; + iVar1C = Fxu_MatrixFindComplement( p, iVar1 ); + iVar2C = Fxu_MatrixFindComplement( p, iVar2 ); + if ( iVar1C == -1 || iVar2C == -1 ) + return NULL; + assert( iVar1C < iVar2C ); + return Fxu_MatrixFindDouble( p, &iVar1C, &iVar2C, 1, 1 ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fxu_Single * Fxu_MatrixFindComplementDouble2( Fxu_Matrix * p, Fxu_Double * pDouble ) +{ +// int * pValue2Node = p->pValue2Node; + int piVarsC1[10], piVarsC2[10]; + int nVarsC1, nVarsC2; + int iVar1, iVar2, iVarTemp; + int iVar1C, iVar2C; + Fxu_Single * pSingle; + + // get the variables of this double div + Fxu_MatrixGetDoubleVars( p, pDouble, piVarsC1, piVarsC2, &nVarsC1, &nVarsC2 ); + assert( nVarsC1 == 1 ); + assert( nVarsC2 == 1 ); + iVar1 = piVarsC1[0]; + iVar2 = piVarsC2[0]; + assert( iVar1 < iVar2 ); + + iVar1C = Fxu_MatrixFindComplement( p, iVar1 ); + iVar2C = Fxu_MatrixFindComplement( p, iVar2 ); + if ( iVar1C == -1 || iVar2C == -1 ) + return NULL; + + // go through the queque and find this one +// assert( iVar1C < iVar2C ); + if ( iVar1C > iVar2C ) + { + iVarTemp = iVar1C; + iVar1C = iVar2C; + iVar2C = iVarTemp; + } + + Fxu_MatrixForEachSingle( p, pSingle ) + if ( pSingle->pVar1->iVar == iVar1C && pSingle->pVar2->iVar == iVar2C ) + return pSingle; + return NULL; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fxu_Double * Fxu_MatrixFindComplementDouble4( Fxu_Matrix * p, Fxu_Double * pDouble ) +{ +// int * pValue2Node = p->pValue2Node; + int piVarsC1[10], piVarsC2[10]; + int nVarsC1, nVarsC2; + int iVar11, iVar12, iVar21, iVar22; + int iVar11C, iVar12C, iVar21C, iVar22C; + int RetValue; + + // get the variables of this double div + Fxu_MatrixGetDoubleVars( p, pDouble, piVarsC1, piVarsC2, &nVarsC1, &nVarsC2 ); + assert( nVarsC1 == 2 && nVarsC2 == 2 ); + + iVar11 = piVarsC1[0]; + iVar12 = piVarsC1[1]; + iVar21 = piVarsC2[0]; + iVar22 = piVarsC2[1]; + assert( iVar11 < iVar21 ); + + iVar11C = Fxu_MatrixFindComplement( p, iVar11 ); + iVar12C = Fxu_MatrixFindComplement( p, iVar12 ); + iVar21C = Fxu_MatrixFindComplement( p, iVar21 ); + iVar22C = Fxu_MatrixFindComplement( p, iVar22 ); + if ( iVar11C == -1 || iVar12C == -1 || iVar21C == -1 || iVar22C == -1 ) + return NULL; + if ( iVar11C != iVar21 || iVar12C != iVar22 || + iVar21C != iVar11 || iVar22C != iVar12 ) + return NULL; + + // a'b' + ab => a'b + ab' + // a'b + ab' => a'b' + ab + // swap the second pair in each cube + RetValue = piVarsC1[1]; + piVarsC1[1] = piVarsC2[1]; + piVarsC2[1] = RetValue; + + return Fxu_MatrixFindDouble( p, piVarsC1, piVarsC2, 2, 2 ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fxu_MatrixFindComplement( Fxu_Matrix * p, int iVar ) +{ + return iVar ^ 1; +/* +// int * pValue2Node = p->pValue2Node; + int iVarC; + int iNode; + int Beg, End; + + // get the nodes + iNode = pValue2Node[iVar]; + // get the first node with the same var + for ( Beg = iVar; Beg >= 0; Beg-- ) + if ( pValue2Node[Beg] != iNode ) + { + Beg++; + break; + } + // get the last node with the same var + for ( End = iVar; ; End++ ) + if ( pValue2Node[End] != iNode ) + { + End--; + break; + } + + // if one of the vars is not binary, quit + if ( End - Beg > 1 ) + return -1; + + // get the complements + if ( iVar == Beg ) + iVarC = End; + else if ( iVar == End ) + iVarC = Beg; + else + { + assert( 0 ); + } + return iVarC; +*/ +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_MatrixGetDoubleVars( Fxu_Matrix * p, Fxu_Double * pDouble, + int piVarsC1[], int piVarsC2[], int * pnVarsC1, int * pnVarsC2 ) +{ + Fxu_Pair * pPair; + Fxu_Lit * pLit1, * pLit2; + int nLits1, nLits2; + + // get the first pair + pPair = pDouble->lPairs.pHead; + // init the parameters + nLits1 = 0; + nLits2 = 0; + pLit1 = pPair->pCube1->lLits.pHead; + pLit2 = pPair->pCube2->lLits.pHead; + while ( 1 ) + { + if ( pLit1 && pLit2 ) + { + if ( pLit1->iVar == pLit2->iVar ) + { // ensure cube-free + pLit1 = pLit1->pHNext; + pLit2 = pLit2->pHNext; + } + else if ( pLit1->iVar < pLit2->iVar ) + { + piVarsC1[nLits1++] = pLit1->iVar; + pLit1 = pLit1->pHNext; + } + else + { + piVarsC2[nLits2++] = pLit2->iVar; + pLit2 = pLit2->pHNext; + } + } + else if ( pLit1 && !pLit2 ) + { + piVarsC1[nLits1++] = pLit1->iVar; + pLit1 = pLit1->pHNext; + } + else if ( !pLit1 && pLit2 ) + { + piVarsC2[nLits2++] = pLit2->iVar; + pLit2 = pLit2->pHNext; + } + else + break; + } + *pnVarsC1 = nLits1; + *pnVarsC2 = nLits2; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fxu_Double * Fxu_MatrixFindDouble( Fxu_Matrix * p, + int piVarsC1[], int piVarsC2[], int nVarsC1, int nVarsC2 ) +{ + int piVarsC1_[100], piVarsC2_[100]; + int nVarsC1_, nVarsC2_, i; + Fxu_Double * pDouble; + Fxu_Pair * pPair; + unsigned Key; + + assert( nVarsC1 > 0 ); + assert( nVarsC2 > 0 ); + assert( piVarsC1[0] < piVarsC2[0] ); + + // get the hash key + Key = Fxu_PairHashKeyArray( p, piVarsC1, piVarsC2, nVarsC1, nVarsC2 ); + + // check if the divisor for this pair already exists + Key %= p->nTableSize; + Fxu_TableForEachDouble( p, Key, pDouble ) + { + pPair = pDouble->lPairs.pHead; + if ( pPair->nLits1 != nVarsC1 ) + continue; + if ( pPair->nLits2 != nVarsC2 ) + continue; + // get the cubes of this divisor + Fxu_MatrixGetDoubleVars( p, pDouble, piVarsC1_, piVarsC2_, &nVarsC1_, &nVarsC2_ ); + // compare lits of the first cube + for ( i = 0; i < nVarsC1; i++ ) + if ( piVarsC1[i] != piVarsC1_[i] ) + break; + if ( i != nVarsC1 ) + continue; + // compare lits of the second cube + for ( i = 0; i < nVarsC2; i++ ) + if ( piVarsC2[i] != piVarsC2_[i] ) + break; + if ( i != nVarsC2 ) + continue; + return pDouble; + } + return NULL; +} + + + + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fxu_SelectSCD( Fxu_Matrix * p, int WeightLimit, Fxu_Var ** ppVar1, Fxu_Var ** ppVar2 ) +{ +// int * pValue2Node = p->pValue2Node; + Fxu_Var * pVar1; + Fxu_Var * pVar2, * pVarTemp; + Fxu_Lit * pLitV, * pLitH; + int Coin; + int CounterAll; + int CounterTest; + int WeightCur; + int WeightBest; + + CounterAll = 0; + CounterTest = 0; + + WeightBest = -10; + + // iterate through the columns in the matrix + Fxu_MatrixForEachVariable( p, pVar1 ) + { + // start collecting the affected vars + Fxu_MatrixRingVarsStart( p ); + + // go through all the literals of this variable + for ( pLitV = pVar1->lLits.pHead; pLitV; pLitV = pLitV->pVNext ) + { + // for this literal, go through all the horizontal literals + for ( pLitH = pLitV->pHNext; pLitH; pLitH = pLitH->pHNext ) + { + // get another variable + pVar2 = pLitH->pVar; + CounterAll++; + // skip the var if it is already used + if ( pVar2->pOrder ) + continue; + // skip the var if it belongs to the same node +// if ( pValue2Node[pVar1->iVar] == pValue2Node[pVar2->iVar] ) +// continue; + // collect the var + Fxu_MatrixRingVarsAdd( p, pVar2 ); + } + } + // stop collecting the selected vars + Fxu_MatrixRingVarsStop( p ); + + // iterate through the selected vars + Fxu_MatrixForEachVarInRing( p, pVar2 ) + { + CounterTest++; + + // count the coincidence + Coin = Fxu_SingleCountCoincidence( p, pVar1, pVar2 ); + assert( Coin > 0 ); + + // get the new weight + WeightCur = Coin - 2; + + // compare the weights + if ( WeightBest < WeightCur ) + { + WeightBest = WeightCur; + *ppVar1 = pVar1; + *ppVar2 = pVar2; + } + } + // unmark the vars + Fxu_MatrixForEachVarInRingSafe( p, pVar2, pVarTemp ) + pVar2->pOrder = NULL; + Fxu_MatrixRingVarsReset( p ); + } + +// if ( WeightBest == WeightLimit ) +// return -1; + return WeightBest; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/fxu/fxuSingle.c b/abc_with_bb_support/src/opt/fxu/fxuSingle.c new file mode 100644 index 000000000..2bc098261 --- /dev/null +++ b/abc_with_bb_support/src/opt/fxu/fxuSingle.c @@ -0,0 +1,166 @@ +/**CFile**************************************************************** + + FileName [fxuSingle.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Procedures to compute the set of single-cube divisors.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: fxuSingle.c,v 1.0 2003/02/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fxuInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes and adds all single-cube divisors to storage.] + + Description [This procedure should be called once when the matrix is + already contructed before the process of logic extraction begins..] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_MatrixComputeSingles( Fxu_Matrix * p ) +{ + Fxu_Var * pVar; + // iterate through the columns in the matrix + Fxu_MatrixForEachVariable( p, pVar ) + Fxu_MatrixComputeSinglesOne( p, pVar ); +} + +/**Function************************************************************* + + Synopsis [Adds the single-cube divisors associated with a new column.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_MatrixComputeSinglesOne( Fxu_Matrix * p, Fxu_Var * pVar ) +{ +// int * pValue2Node = p->pValue2Node; + Fxu_Lit * pLitV, * pLitH; + Fxu_Var * pVar2; + int Coin; +// int CounterAll; +// int CounterTest; + int WeightCur; + + // start collecting the affected vars + Fxu_MatrixRingVarsStart( p ); + // go through all the literals of this variable + for ( pLitV = pVar->lLits.pHead; pLitV; pLitV = pLitV->pVNext ) + // for this literal, go through all the horizontal literals + for ( pLitH = pLitV->pHPrev; pLitH; pLitH = pLitH->pHPrev ) + { + // get another variable + pVar2 = pLitH->pVar; +// CounterAll++; + // skip the var if it is already used + if ( pVar2->pOrder ) + continue; + // skip the var if it belongs to the same node +// if ( pValue2Node[pVar->iVar] == pValue2Node[pVar2->iVar] ) +// continue; + // collect the var + Fxu_MatrixRingVarsAdd( p, pVar2 ); + } + // stop collecting the selected vars + Fxu_MatrixRingVarsStop( p ); + + // iterate through the selected vars + Fxu_MatrixForEachVarInRing( p, pVar2 ) + { +// CounterTest++; + // count the coincidence + Coin = Fxu_SingleCountCoincidence( p, pVar2, pVar ); + assert( Coin > 0 ); + // get the new weight + WeightCur = Coin - 2; + if ( WeightCur >= 0 ) + Fxu_MatrixAddSingle( p, pVar2, pVar, WeightCur ); + } + + // unmark the vars + Fxu_MatrixRingVarsUnmark( p ); +} + +/**Function************************************************************* + + Synopsis [Computes the coincidence count of two columns.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fxu_SingleCountCoincidence( Fxu_Matrix * p, Fxu_Var * pVar1, Fxu_Var * pVar2 ) +{ + Fxu_Lit * pLit1, * pLit2; + int Result; + + // compute the coincidence count + Result = 0; + pLit1 = pVar1->lLits.pHead; + pLit2 = pVar2->lLits.pHead; + while ( 1 ) + { + if ( pLit1 && pLit2 ) + { + if ( pLit1->pCube->pVar->iVar == pLit2->pCube->pVar->iVar ) + { // the variables are the same + if ( pLit1->iCube == pLit2->iCube ) + { // the literals are the same + pLit1 = pLit1->pVNext; + pLit2 = pLit2->pVNext; + // add this literal to the coincidence + Result++; + } + else if ( pLit1->iCube < pLit2->iCube ) + pLit1 = pLit1->pVNext; + else + pLit2 = pLit2->pVNext; + } + else if ( pLit1->pCube->pVar->iVar < pLit2->pCube->pVar->iVar ) + pLit1 = pLit1->pVNext; + else + pLit2 = pLit2->pVNext; + } + else if ( pLit1 && !pLit2 ) + pLit1 = pLit1->pVNext; + else if ( !pLit1 && pLit2 ) + pLit2 = pLit2->pVNext; + else + break; + } + return Result; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/fxu/fxuUpdate.c b/abc_with_bb_support/src/opt/fxu/fxuUpdate.c new file mode 100644 index 000000000..9f00efbcb --- /dev/null +++ b/abc_with_bb_support/src/opt/fxu/fxuUpdate.c @@ -0,0 +1,803 @@ +/**CFile**************************************************************** + + FileName [fxuUpdate.c] + + PackageName [MVSIS 2.0: Multi-valued logic synthesis system.] + + Synopsis [Updating the sparse matrix when divisors are accepted.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: fxuUpdate.c,v 1.0 2003/02/01 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "fxuInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Fxu_UpdateDoublePairs( Fxu_Matrix * p, Fxu_Double * pDouble, Fxu_Var * pVar ); +static void Fxu_UpdateMatrixDoubleCreateCubes( Fxu_Matrix * p, Fxu_Cube * pCube1, Fxu_Cube * pCube2, Fxu_Double * pDiv ); +static void Fxu_UpdateMatrixDoubleClean( Fxu_Matrix * p, Fxu_Cube * pCubeUse, Fxu_Cube * pCubeRem ); +static void Fxu_UpdateMatrixSingleClean( Fxu_Matrix * p, Fxu_Var * pVar1, Fxu_Var * pVar2, Fxu_Var * pVarNew ); + +static void Fxu_UpdateCreateNewVars( Fxu_Matrix * p, Fxu_Var ** ppVarC, Fxu_Var ** ppVarD, int nCubes ); +static int Fxu_UpdatePairCompare( Fxu_Pair ** ppP1, Fxu_Pair ** ppP2 ); +static void Fxu_UpdatePairsSort( Fxu_Matrix * p, Fxu_Double * pDouble ); + +static void Fxu_UpdateCleanOldDoubles( Fxu_Matrix * p, Fxu_Double * pDiv, Fxu_Cube * pCube ); +static void Fxu_UpdateAddNewDoubles( Fxu_Matrix * p, Fxu_Cube * pCube ); +static void Fxu_UpdateCleanOldSingles( Fxu_Matrix * p ); +static void Fxu_UpdateAddNewSingles( Fxu_Matrix * p, Fxu_Var * pVar ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Updates the matrix after selecting two divisors.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_Update( Fxu_Matrix * p, Fxu_Single * pSingle, Fxu_Double * pDouble ) +{ + Fxu_Cube * pCube, * pCubeNew; + Fxu_Var * pVarC, * pVarD; + Fxu_Var * pVar1, * pVar2; + + // consider trivial cases + if ( pSingle == NULL ) + { + assert( pDouble->Weight == Fxu_HeapDoubleReadMaxWeight( p->pHeapDouble ) ); + Fxu_UpdateDouble( p ); + return; + } + if ( pDouble == NULL ) + { + assert( pSingle->Weight == Fxu_HeapSingleReadMaxWeight( p->pHeapSingle ) ); + Fxu_UpdateSingle( p ); + return; + } + + // get the variables of the single + pVar1 = pSingle->pVar1; + pVar2 = pSingle->pVar2; + + // remove the best double from the heap + Fxu_HeapDoubleDelete( p->pHeapDouble, pDouble ); + // remove the best divisor from the table + Fxu_ListTableDelDivisor( p, pDouble ); + + // create two new columns (vars) + Fxu_UpdateCreateNewVars( p, &pVarC, &pVarD, 1 ); + // create one new row (cube) + pCubeNew = Fxu_MatrixAddCube( p, pVarD, 0 ); + pCubeNew->pFirst = pCubeNew; + // set the first cube of the positive var + pVarD->pFirst = pCubeNew; + + // start collecting the affected vars and cubes + Fxu_MatrixRingCubesStart( p ); + Fxu_MatrixRingVarsStart( p ); + // add the vars + Fxu_MatrixRingVarsAdd( p, pVar1 ); + Fxu_MatrixRingVarsAdd( p, pVar2 ); + // remove the literals and collect the affected cubes + // remove the divisors associated with this cube + // add to the affected cube the literal corresponding to the new column + Fxu_UpdateMatrixSingleClean( p, pVar1, pVar2, pVarD ); + // replace each two cubes of the pair by one new cube + // the new cube contains the base and the new literal + Fxu_UpdateDoublePairs( p, pDouble, pVarC ); + // stop collecting the affected vars and cubes + Fxu_MatrixRingCubesStop( p ); + Fxu_MatrixRingVarsStop( p ); + + // add the literals to the new cube + assert( pVar1->iVar < pVar2->iVar ); + assert( Fxu_SingleCountCoincidence( p, pVar1, pVar2 ) == 0 ); + Fxu_MatrixAddLiteral( p, pCubeNew, pVar1 ); + Fxu_MatrixAddLiteral( p, pCubeNew, pVar2 ); + + // create new doubles; we cannot add them in the same loop + // because we first have to create *all* new cubes for each node + Fxu_MatrixForEachCubeInRing( p, pCube ) + Fxu_UpdateAddNewDoubles( p, pCube ); + // update the singles after removing some literals + Fxu_UpdateCleanOldSingles( p ); + + // undo the temporary rings with cubes and vars + Fxu_MatrixRingCubesUnmark( p ); + Fxu_MatrixRingVarsUnmark( p ); + // we should undo the rings before creating new singles + + // create new singles + Fxu_UpdateAddNewSingles( p, pVarC ); + Fxu_UpdateAddNewSingles( p, pVarD ); + + // recycle the divisor + MEM_FREE_FXU( p, Fxu_Double, 1, pDouble ); + p->nDivs3++; +} + +/**Function************************************************************* + + Synopsis [Updates after accepting single cube divisor.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_UpdateSingle( Fxu_Matrix * p ) +{ + Fxu_Single * pSingle; + Fxu_Cube * pCube, * pCubeNew; + Fxu_Var * pVarC, * pVarD; + Fxu_Var * pVar1, * pVar2; + + // read the best divisor from the heap + pSingle = Fxu_HeapSingleReadMax( p->pHeapSingle ); + // get the variables of this single-cube divisor + pVar1 = pSingle->pVar1; + pVar2 = pSingle->pVar2; + + // create two new columns (vars) + Fxu_UpdateCreateNewVars( p, &pVarC, &pVarD, 1 ); + // create one new row (cube) + pCubeNew = Fxu_MatrixAddCube( p, pVarD, 0 ); + pCubeNew->pFirst = pCubeNew; + // set the first cube + pVarD->pFirst = pCubeNew; + + // start collecting the affected vars and cubes + Fxu_MatrixRingCubesStart( p ); + Fxu_MatrixRingVarsStart( p ); + // add the vars + Fxu_MatrixRingVarsAdd( p, pVar1 ); + Fxu_MatrixRingVarsAdd( p, pVar2 ); + // remove the literals and collect the affected cubes + // remove the divisors associated with this cube + // add to the affected cube the literal corresponding to the new column + Fxu_UpdateMatrixSingleClean( p, pVar1, pVar2, pVarD ); + // stop collecting the affected vars and cubes + Fxu_MatrixRingCubesStop( p ); + Fxu_MatrixRingVarsStop( p ); + + // add the literals to the new cube + assert( pVar1->iVar < pVar2->iVar ); + assert( Fxu_SingleCountCoincidence( p, pVar1, pVar2 ) == 0 ); + Fxu_MatrixAddLiteral( p, pCubeNew, pVar1 ); + Fxu_MatrixAddLiteral( p, pCubeNew, pVar2 ); + + // create new doubles; we cannot add them in the same loop + // because we first have to create *all* new cubes for each node + Fxu_MatrixForEachCubeInRing( p, pCube ) + Fxu_UpdateAddNewDoubles( p, pCube ); + // update the singles after removing some literals + Fxu_UpdateCleanOldSingles( p ); + // we should undo the rings before creating new singles + + // unmark the cubes + Fxu_MatrixRingCubesUnmark( p ); + Fxu_MatrixRingVarsUnmark( p ); + + // create new singles + Fxu_UpdateAddNewSingles( p, pVarC ); + Fxu_UpdateAddNewSingles( p, pVarD ); + p->nDivs1++; +} + +/**Function************************************************************* + + Synopsis [Updates the matrix after accepting a double cube divisor.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_UpdateDouble( Fxu_Matrix * p ) +{ + Fxu_Double * pDiv; + Fxu_Cube * pCube, * pCubeNew1, * pCubeNew2; + Fxu_Var * pVarC, * pVarD; + + // remove the best divisor from the heap + pDiv = Fxu_HeapDoubleGetMax( p->pHeapDouble ); + // remove the best divisor from the table + Fxu_ListTableDelDivisor( p, pDiv ); + + // create two new columns (vars) + Fxu_UpdateCreateNewVars( p, &pVarC, &pVarD, 2 ); + // create two new rows (cubes) + pCubeNew1 = Fxu_MatrixAddCube( p, pVarD, 0 ); + pCubeNew1->pFirst = pCubeNew1; + pCubeNew2 = Fxu_MatrixAddCube( p, pVarD, 1 ); + pCubeNew2->pFirst = pCubeNew1; + // set the first cube + pVarD->pFirst = pCubeNew1; + + // add the literals to the new cubes + Fxu_UpdateMatrixDoubleCreateCubes( p, pCubeNew1, pCubeNew2, pDiv ); + + // start collecting the affected cubes and vars + Fxu_MatrixRingCubesStart( p ); + Fxu_MatrixRingVarsStart( p ); + // replace each two cubes of the pair by one new cube + // the new cube contains the base and the new literal + Fxu_UpdateDoublePairs( p, pDiv, pVarD ); + // stop collecting the affected cubes and vars + Fxu_MatrixRingCubesStop( p ); + Fxu_MatrixRingVarsStop( p ); + + // create new doubles; we cannot add them in the same loop + // because we first have to create *all* new cubes for each node + Fxu_MatrixForEachCubeInRing( p, pCube ) + Fxu_UpdateAddNewDoubles( p, pCube ); + // update the singles after removing some literals + Fxu_UpdateCleanOldSingles( p ); + + // undo the temporary rings with cubes and vars + Fxu_MatrixRingCubesUnmark( p ); + Fxu_MatrixRingVarsUnmark( p ); + // we should undo the rings before creating new singles + + // create new singles + Fxu_UpdateAddNewSingles( p, pVarC ); + Fxu_UpdateAddNewSingles( p, pVarD ); + + // recycle the divisor + MEM_FREE_FXU( p, Fxu_Double, 1, pDiv ); + p->nDivs2++; +} + +/**Function************************************************************* + + Synopsis [Update the pairs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_UpdateDoublePairs( Fxu_Matrix * p, Fxu_Double * pDouble, Fxu_Var * pVar ) +{ + Fxu_Pair * pPair; + Fxu_Cube * pCubeUse, * pCubeRem; + int i; + + // collect and sort the pairs + Fxu_UpdatePairsSort( p, pDouble ); +// for ( i = 0; i < p->nPairsTemp; i++ ) + for ( i = 0; i < p->vPairs->nSize; i++ ) + { + // get the pair +// pPair = p->pPairsTemp[i]; + pPair = p->vPairs->pArray[i]; + // out of the two cubes, select the one which comes earlier + pCubeUse = Fxu_PairMinCube( pPair ); + pCubeRem = Fxu_PairMaxCube( pPair ); + // collect the affected cube + assert( pCubeUse->pOrder == NULL ); + Fxu_MatrixRingCubesAdd( p, pCubeUse ); + + // remove some literals from pCubeUse and all literals from pCubeRem + Fxu_UpdateMatrixDoubleClean( p, pCubeUse, pCubeRem ); + // add a literal that depends on the new variable + Fxu_MatrixAddLiteral( p, pCubeUse, pVar ); + // check the literal count + assert( pCubeUse->lLits.nItems == pPair->nBase + 1 ); + assert( pCubeRem->lLits.nItems == 0 ); + + // update the divisors by removing useless pairs + Fxu_UpdateCleanOldDoubles( p, pDouble, pCubeUse ); + Fxu_UpdateCleanOldDoubles( p, pDouble, pCubeRem ); + // remove the pair + MEM_FREE_FXU( p, Fxu_Pair, 1, pPair ); + } + p->vPairs->nSize = 0; +} + +/**Function************************************************************* + + Synopsis [Add two cubes corresponding to the given double-cube divisor.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_UpdateMatrixDoubleCreateCubes( Fxu_Matrix * p, Fxu_Cube * pCube1, Fxu_Cube * pCube2, Fxu_Double * pDiv ) +{ + Fxu_Lit * pLit1, * pLit2; + Fxu_Pair * pPair; + int nBase, nLits1, nLits2; + + // fill in the SOP and copy the fanins + nBase = nLits1 = nLits2 = 0; + pPair = pDiv->lPairs.pHead; + pLit1 = pPair->pCube1->lLits.pHead; + pLit2 = pPair->pCube2->lLits.pHead; + while ( 1 ) + { + if ( pLit1 && pLit2 ) + { + if ( pLit1->iVar == pLit2->iVar ) + { // skip the cube free part + pLit1 = pLit1->pHNext; + pLit2 = pLit2->pHNext; + nBase++; + } + else if ( pLit1->iVar < pLit2->iVar ) + { // add literal to the first cube + Fxu_MatrixAddLiteral( p, pCube1, pLit1->pVar ); + // move to the next literal in this cube + pLit1 = pLit1->pHNext; + nLits1++; + } + else + { // add literal to the second cube + Fxu_MatrixAddLiteral( p, pCube2, pLit2->pVar ); + // move to the next literal in this cube + pLit2 = pLit2->pHNext; + nLits2++; + } + } + else if ( pLit1 && !pLit2 ) + { // add literal to the first cube + Fxu_MatrixAddLiteral( p, pCube1, pLit1->pVar ); + // move to the next literal in this cube + pLit1 = pLit1->pHNext; + nLits1++; + } + else if ( !pLit1 && pLit2 ) + { // add literal to the second cube + Fxu_MatrixAddLiteral( p, pCube2, pLit2->pVar ); + // move to the next literal in this cube + pLit2 = pLit2->pHNext; + nLits2++; + } + else + break; + } + assert( pPair->nLits1 == nLits1 ); + assert( pPair->nLits2 == nLits2 ); + assert( pPair->nBase == nBase ); +} + + +/**Function************************************************************* + + Synopsis [Create the node equal to double-cube divisor.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_UpdateMatrixDoubleClean( Fxu_Matrix * p, Fxu_Cube * pCubeUse, Fxu_Cube * pCubeRem ) +{ + Fxu_Lit * pLit1, * bLit1Next; + Fxu_Lit * pLit2, * bLit2Next; + + // initialize the starting literals + pLit1 = pCubeUse->lLits.pHead; + pLit2 = pCubeRem->lLits.pHead; + bLit1Next = pLit1? pLit1->pHNext: NULL; + bLit2Next = pLit2? pLit2->pHNext: NULL; + // go through the pair and remove the literals in the base + // from the first cube and all literals from the second cube + while ( 1 ) + { + if ( pLit1 && pLit2 ) + { + if ( pLit1->iVar == pLit2->iVar ) + { // this literal is present in both cubes - it belongs to the base + // mark the affected var + if ( pLit1->pVar->pOrder == NULL ) + Fxu_MatrixRingVarsAdd( p, pLit1->pVar ); + // leave the base in pCubeUse; delete it from pCubeRem + Fxu_MatrixDelLiteral( p, pLit2 ); + // step to the next literals + pLit1 = bLit1Next; + pLit2 = bLit2Next; + bLit1Next = pLit1? pLit1->pHNext: NULL; + bLit2Next = pLit2? pLit2->pHNext: NULL; + } + else if ( pLit1->iVar < pLit2->iVar ) + { // this literal is present in pCubeUse - remove it + // mark the affected var + if ( pLit1->pVar->pOrder == NULL ) + Fxu_MatrixRingVarsAdd( p, pLit1->pVar ); + // delete this literal + Fxu_MatrixDelLiteral( p, pLit1 ); + // step to the next literals + pLit1 = bLit1Next; + bLit1Next = pLit1? pLit1->pHNext: NULL; + } + else + { // this literal is present in pCubeRem - remove it + // mark the affected var + if ( pLit2->pVar->pOrder == NULL ) + Fxu_MatrixRingVarsAdd( p, pLit2->pVar ); + // delete this literal + Fxu_MatrixDelLiteral( p, pLit2 ); + // step to the next literals + pLit2 = bLit2Next; + bLit2Next = pLit2? pLit2->pHNext: NULL; + } + } + else if ( pLit1 && !pLit2 ) + { // this literal is present in pCubeUse - leave it + // mark the affected var + if ( pLit1->pVar->pOrder == NULL ) + Fxu_MatrixRingVarsAdd( p, pLit1->pVar ); + // delete this literal + Fxu_MatrixDelLiteral( p, pLit1 ); + // step to the next literals + pLit1 = bLit1Next; + bLit1Next = pLit1? pLit1->pHNext: NULL; + } + else if ( !pLit1 && pLit2 ) + { // this literal is present in pCubeRem - remove it + // mark the affected var + if ( pLit2->pVar->pOrder == NULL ) + Fxu_MatrixRingVarsAdd( p, pLit2->pVar ); + // delete this literal + Fxu_MatrixDelLiteral( p, pLit2 ); + // step to the next literals + pLit2 = bLit2Next; + bLit2Next = pLit2? pLit2->pHNext: NULL; + } + else + break; + } +} + +/**Function************************************************************* + + Synopsis [Updates the matrix after selecting a single cube divisor.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_UpdateMatrixSingleClean( Fxu_Matrix * p, Fxu_Var * pVar1, Fxu_Var * pVar2, Fxu_Var * pVarNew ) +{ + Fxu_Lit * pLit1, * bLit1Next; + Fxu_Lit * pLit2, * bLit2Next; + + // initialize the starting literals + pLit1 = pVar1->lLits.pHead; + pLit2 = pVar2->lLits.pHead; + bLit1Next = pLit1? pLit1->pVNext: NULL; + bLit2Next = pLit2? pLit2->pVNext: NULL; + while ( 1 ) + { + if ( pLit1 && pLit2 ) + { + if ( pLit1->pCube->pVar->iVar == pLit2->pCube->pVar->iVar ) + { // these literals coincide + if ( pLit1->iCube == pLit2->iCube ) + { // these literals coincide + + // collect the affected cube + assert( pLit1->pCube->pOrder == NULL ); + Fxu_MatrixRingCubesAdd( p, pLit1->pCube ); + + // add the literal to this cube corresponding to the new column + Fxu_MatrixAddLiteral( p, pLit1->pCube, pVarNew ); + // clean the old cubes + Fxu_UpdateCleanOldDoubles( p, NULL, pLit1->pCube ); + + // remove the literals + Fxu_MatrixDelLiteral( p, pLit1 ); + Fxu_MatrixDelLiteral( p, pLit2 ); + + // go to the next literals + pLit1 = bLit1Next; + pLit2 = bLit2Next; + bLit1Next = pLit1? pLit1->pVNext: NULL; + bLit2Next = pLit2? pLit2->pVNext: NULL; + } + else if ( pLit1->iCube < pLit2->iCube ) + { + pLit1 = bLit1Next; + bLit1Next = pLit1? pLit1->pVNext: NULL; + } + else + { + pLit2 = bLit2Next; + bLit2Next = pLit2? pLit2->pVNext: NULL; + } + } + else if ( pLit1->pCube->pVar->iVar < pLit2->pCube->pVar->iVar ) + { + pLit1 = bLit1Next; + bLit1Next = pLit1? pLit1->pVNext: NULL; + } + else + { + pLit2 = bLit2Next; + bLit2Next = pLit2? pLit2->pVNext: NULL; + } + } + else if ( pLit1 && !pLit2 ) + { + pLit1 = bLit1Next; + bLit1Next = pLit1? pLit1->pVNext: NULL; + } + else if ( !pLit1 && pLit2 ) + { + pLit2 = bLit2Next; + bLit2Next = pLit2? pLit2->pVNext: NULL; + } + else + break; + } +} + +/**Function************************************************************* + + Synopsis [Sort the pairs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_UpdatePairsSort( Fxu_Matrix * p, Fxu_Double * pDouble ) +{ + Fxu_Pair * pPair; + // order the pairs by the first cube to ensure that new literals are added + // to the matrix from top to bottom - collect pairs into the array + p->vPairs->nSize = 0; + Fxu_DoubleForEachPair( pDouble, pPair ) + Vec_PtrPush( p->vPairs, pPair ); + if ( p->vPairs->nSize < 2 ) + return; + // sort + qsort( (void *)p->vPairs->pArray, p->vPairs->nSize, sizeof(Fxu_Pair *), + (int (*)(const void *, const void *)) Fxu_UpdatePairCompare ); + assert( Fxu_UpdatePairCompare( (Fxu_Pair**)p->vPairs->pArray, (Fxu_Pair**)p->vPairs->pArray + p->vPairs->nSize - 1 ) < 0 ); +} + +/**Function************************************************************* + + Synopsis [Compares the vars by their number.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fxu_UpdatePairCompare( Fxu_Pair ** ppP1, Fxu_Pair ** ppP2 ) +{ + Fxu_Cube * pC1 = (*ppP1)->pCube1; + Fxu_Cube * pC2 = (*ppP2)->pCube1; + int iP1CubeMin, iP2CubeMin; + if ( pC1->pVar->iVar < pC2->pVar->iVar ) + return -1; + if ( pC1->pVar->iVar > pC2->pVar->iVar ) + return 1; + iP1CubeMin = Fxu_PairMinCubeInt( *ppP1 ); + iP2CubeMin = Fxu_PairMinCubeInt( *ppP2 ); + if ( iP1CubeMin < iP2CubeMin ) + return -1; + if ( iP1CubeMin > iP2CubeMin ) + return 1; + assert( 0 ); + return 0; +} + + +/**Function************************************************************* + + Synopsis [Create new variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_UpdateCreateNewVars( Fxu_Matrix * p, Fxu_Var ** ppVarC, Fxu_Var ** ppVarD, int nCubes ) +{ + Fxu_Var * pVarC, * pVarD; + + // add a new column for the complement + pVarC = Fxu_MatrixAddVar( p ); + pVarC->nCubes = 0; + // add a new column for the divisor + pVarD = Fxu_MatrixAddVar( p ); + pVarD->nCubes = nCubes; + + // mark this entry in the Value2Node array +// assert( p->pValue2Node[pVarC->iVar] > 0 ); +// p->pValue2Node[pVarD->iVar ] = p->pValue2Node[pVarC->iVar]; +// p->pValue2Node[pVarD->iVar+1] = p->pValue2Node[pVarC->iVar]+1; + + *ppVarC = pVarC; + *ppVarD = pVarD; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_UpdateCleanOldDoubles( Fxu_Matrix * p, Fxu_Double * pDiv, Fxu_Cube * pCube ) +{ + Fxu_Double * pDivCur; + Fxu_Pair * pPair; + int i; + + // if the cube is a recently introduced one + // it does not have pairs allocated + // in this case, there is nothing to update + if ( pCube->pVar->ppPairs == NULL ) + return; + + // go through all the pairs of this cube + Fxu_CubeForEachPair( pCube, pPair, i ) + { + // get the divisor of this pair + pDivCur = pPair->pDiv; + // skip the current divisor + if ( pDivCur == pDiv ) + continue; + // remove this pair + Fxu_ListDoubleDelPair( pDivCur, pPair ); + // the divisor may have become useless by now + if ( pDivCur->lPairs.nItems == 0 ) + { + assert( pDivCur->Weight == pPair->nBase - 1 ); + Fxu_HeapDoubleDelete( p->pHeapDouble, pDivCur ); + Fxu_MatrixDelDivisor( p, pDivCur ); + } + else + { + // update the divisor's weight + pDivCur->Weight -= pPair->nLits1 + pPair->nLits2 - 1 + pPair->nBase; + Fxu_HeapDoubleUpdate( p->pHeapDouble, pDivCur ); + } + MEM_FREE_FXU( p, Fxu_Pair, 1, pPair ); + } + // finally erase all the pair info associated with this cube + Fxu_PairClearStorage( pCube ); +} + +/**Function************************************************************* + + Synopsis [Adds the new divisors that depend on the cube.] + + Description [Go through all the non-empty cubes of this cover + (except the given cube) and, for each of them, add the new divisor + with the given cube.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_UpdateAddNewDoubles( Fxu_Matrix * p, Fxu_Cube * pCube ) +{ + Fxu_Cube * pTemp; + assert( pCube->pOrder ); + + // if the cube is a recently introduced one + // it does not have pairs allocated + // in this case, there is nothing to update + if ( pCube->pVar->ppPairs == NULL ) + return; + + for ( pTemp = pCube->pFirst; pTemp->pVar == pCube->pVar; pTemp = pTemp->pNext ) + { + // do not add pairs with the empty cubes + if ( pTemp->lLits.nItems == 0 ) + continue; + // to prevent adding duplicated pairs of the new cubes + // do not add the pair, if the current cube is marked + if ( pTemp->pOrder && pTemp >= pCube ) + continue; + Fxu_MatrixAddDivisor( p, pTemp, pCube ); + } +} + +/**Function************************************************************* + + Synopsis [Removes old single cube divisors.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_UpdateCleanOldSingles( Fxu_Matrix * p ) +{ + Fxu_Single * pSingle, * pSingle2; + int WeightNew; + + Fxu_MatrixForEachSingleSafe( p, pSingle, pSingle2 ) + { + // if at least one of the variables is marked, recalculate + if ( pSingle->pVar1->pOrder || pSingle->pVar2->pOrder ) + { + // get the new weight + WeightNew = -2 + Fxu_SingleCountCoincidence( p, pSingle->pVar1, pSingle->pVar2 ); + if ( WeightNew >= 0 ) + { + pSingle->Weight = WeightNew; + Fxu_HeapSingleUpdate( p->pHeapSingle, pSingle ); + } + else + { + Fxu_HeapSingleDelete( p->pHeapSingle, pSingle ); + Fxu_ListMatrixDelSingle( p, pSingle ); + MEM_FREE_FXU( p, Fxu_Single, 1, pSingle ); + } + } + } +} + +/**Function************************************************************* + + Synopsis [Updates the single cube divisors.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fxu_UpdateAddNewSingles( Fxu_Matrix * p, Fxu_Var * pVar ) +{ + Fxu_MatrixComputeSinglesOne( p, pVar ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/fxu/module.make b/abc_with_bb_support/src/opt/fxu/module.make new file mode 100644 index 000000000..78081e9ca --- /dev/null +++ b/abc_with_bb_support/src/opt/fxu/module.make @@ -0,0 +1,12 @@ +SRC += src/opt/fxu/fxu.c \ + src/opt/fxu/fxuCreate.c \ + src/opt/fxu/fxuHeapD.c \ + src/opt/fxu/fxuHeapS.c \ + src/opt/fxu/fxuList.c \ + src/opt/fxu/fxuMatrix.c \ + src/opt/fxu/fxuPair.c \ + src/opt/fxu/fxuPrint.c \ + src/opt/fxu/fxuReduce.c \ + src/opt/fxu/fxuSelect.c \ + src/opt/fxu/fxuSingle.c \ + src/opt/fxu/fxuUpdate.c diff --git a/abc_with_bb_support/src/opt/lpk/lpk.h b/abc_with_bb_support/src/opt/lpk/lpk.h new file mode 100644 index 000000000..f5c0ed609 --- /dev/null +++ b/abc_with_bb_support/src/opt/lpk/lpk.h @@ -0,0 +1,83 @@ +/**CFile**************************************************************** + + FileName [lpk.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Fast Boolean matching for LUT structures.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: lpk.h,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __LPK_H__ +#define __LPK_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Lpk_Par_t_ Lpk_Par_t; +struct Lpk_Par_t_ +{ + // user-controlled parameters + int nLutsMax; // (N) the maximum number of LUTs in the structure + int nLutsOver; // (Q) the maximum number of LUTs not in the MFFC + int nVarsShared; // (S) the maximum number of shared variables (crossbars) + int nGrowthLevel; // (L) the maximum increase in the node level after resynthesis + int fSatur; // iterate till saturation + int fZeroCost; // accept zero-cost replacements + int fFirst; // use root node and first cut only + int fVerbose; // the verbosiness flag + int fVeryVerbose; // additional verbose info printout + // internal parameters + int nLutSize; // (K) the LUT size (determined by the input network) + int nVarsMax; // (V) the largest number of variables: V = N * (K-1) + 1 +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// ITERATORS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== lpkCore.c ========================================================*/ +extern int Lpk_Resynthesize( Abc_Ntk_t * pNtk, Lpk_Par_t * pPars ); + + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/opt/lpk/lpkCore.c b/abc_with_bb_support/src/opt/lpk/lpkCore.c new file mode 100644 index 000000000..ece8032db --- /dev/null +++ b/abc_with_bb_support/src/opt/lpk/lpkCore.c @@ -0,0 +1,468 @@ +/**CFile**************************************************************** + + FileName [lpkCore.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Fast Boolean matching for LUT structures.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: lpkCore.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "lpkInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Prepares the mapping manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Lpk_IfManStart( Lpk_Man_t * p ) +{ + If_Par_t * pPars; + assert( p->pIfMan == NULL ); + // set defaults + pPars = ALLOC( If_Par_t, 1 ); + memset( pPars, 0, sizeof(If_Par_t) ); + // user-controlable paramters + pPars->nLutSize = p->pPars->nLutSize; + pPars->nCutsMax = 16; + pPars->nFlowIters = 0; // 1 + pPars->nAreaIters = 0; // 1 + pPars->DelayTarget = -1; + pPars->fPreprocess = 0; + pPars->fArea = 1; + pPars->fFancy = 0; + pPars->fExpRed = 0; // + pPars->fLatchPaths = 0; + pPars->fSeqMap = 0; + pPars->fVerbose = 0; + // internal parameters + pPars->fTruth = 1; + pPars->fUsePerm = 0; + pPars->nLatches = 0; + pPars->pLutLib = NULL; // Abc_FrameReadLibLut(); + pPars->pTimesArr = NULL; + pPars->pTimesArr = NULL; + pPars->fUseBdds = 0; + pPars->fUseSops = 0; + pPars->fUseCnfs = 0; + pPars->fUseMv = 0; + // start the mapping manager and set its parameters + p->pIfMan = If_ManStart( pPars ); + If_ManSetupSetAll( p->pIfMan, 1000 ); + p->pIfMan->pPars->pTimesArr = ALLOC( float, 32 ); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if at least one entry has changed.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Lpk_NodeHasChanged( Lpk_Man_t * p, int iNode ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pTemp; + int i; + vNodes = Vec_VecEntry( p->vVisited, iNode ); + if ( Vec_PtrSize(vNodes) == 0 ) + return 1; + Vec_PtrForEachEntry( vNodes, pTemp, i ) + { + // check if the node has changed + pTemp = Abc_NtkObj( p->pNtk, (int)pTemp ); + if ( pTemp == NULL ) + return 1; + // check if the number of fanouts has changed +// if ( Abc_ObjFanoutNum(pTemp) != (int)Vec_PtrEntry(vNodes, i+1) ) +// return 1; + i++; + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Prepares the mapping manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Lpk_ExploreCut( Lpk_Man_t * p, Lpk_Cut_t * pCut, Kit_DsdNtk_t * pNtk ) +{ + extern Abc_Obj_t * Abc_NodeFromIf_rec( Abc_Ntk_t * pNtkNew, If_Man_t * pIfMan, If_Obj_t * pIfObj, Vec_Int_t * vCover ); + Kit_DsdObj_t * pRoot; + If_Obj_t * pDriver, * ppLeaves[16]; + Abc_Obj_t * pLeaf, * pObjNew; + int nGain, i, clk; +// int nOldShared; + + // check special cases + pRoot = Kit_DsdNtkRoot( pNtk ); + if ( pRoot->Type == KIT_DSD_CONST1 ) + { + if ( Kit_DsdLitIsCompl(pNtk->Root) ) + pObjNew = Abc_NtkCreateNodeConst0( p->pNtk ); + else + pObjNew = Abc_NtkCreateNodeConst1( p->pNtk ); + Abc_NtkUpdate( p->pObj, pObjNew, p->vLevels ); + p->nGainTotal += pCut->nNodes - pCut->nNodesDup; + return 1; + } + if ( pRoot->Type == KIT_DSD_VAR ) + { + pObjNew = Abc_NtkObj( p->pNtk, pCut->pLeaves[ Kit_DsdLit2Var(pRoot->pFans[0]) ] ); + if ( Kit_DsdLitIsCompl(pNtk->Root) ^ Kit_DsdLitIsCompl(pRoot->pFans[0]) ) + pObjNew = Abc_NtkCreateNodeInv( p->pNtk, pObjNew ); + Abc_NtkUpdate( p->pObj, pObjNew, p->vLevels ); + p->nGainTotal += pCut->nNodes - pCut->nNodesDup; + return 1; + } + assert( pRoot->Type == KIT_DSD_AND || pRoot->Type == KIT_DSD_XOR || pRoot->Type == KIT_DSD_PRIME ); + + // start the mapping manager + if ( p->pIfMan == NULL ) + Lpk_IfManStart( p ); + + // prepare the mapping manager + If_ManRestart( p->pIfMan ); + // create the PI variables + for ( i = 0; i < p->pPars->nVarsMax; i++ ) + ppLeaves[i] = If_ManCreateCi( p->pIfMan ); + // set the arrival times + Lpk_CutForEachLeaf( p->pNtk, pCut, pLeaf, i ) + p->pIfMan->pPars->pTimesArr[i] = (float)pLeaf->Level; + // prepare the PI cuts + If_ManSetupCiCutSets( p->pIfMan ); + // create the internal nodes + p->fCalledOnce = 0; + p->nCalledSRed = 0; + pDriver = Lpk_MapTree_rec( p, pNtk, ppLeaves, pNtk->Root, NULL ); + if ( pDriver == NULL ) + return 0; + // create the PO node + If_ManCreateCo( p->pIfMan, If_Regular(pDriver) ); + + // perform mapping + p->pIfMan->pPars->fAreaOnly = 1; +clk = clock(); + If_ManPerformMappingComb( p->pIfMan ); +p->timeMap += clock() - clk; + + // compute the gain in area + nGain = pCut->nNodes - pCut->nNodesDup - (int)p->pIfMan->AreaGlo; + if ( p->pPars->fVeryVerbose ) + printf( " Mffc = %2d. Mapped = %2d. Gain = %3d. Depth increase = %d. SReds = %d.\n", + pCut->nNodes - pCut->nNodesDup, (int)p->pIfMan->AreaGlo, nGain, (int)p->pIfMan->RequiredGlo - (int)p->pObj->Level, p->nCalledSRed ); + + // quit if there is no gain + if ( !(nGain > 0 || (p->pPars->fZeroCost && nGain == 0)) ) + return 0; + + // quit if depth increases too much + if ( (int)p->pIfMan->RequiredGlo > Abc_ObjRequiredLevel(p->pObj) ) + return 0; + + // perform replacement + p->nGainTotal += nGain; + p->nChanges++; + if ( p->nCalledSRed ) + p->nBenefited++; + + // prepare the mapping manager + If_ManCleanNodeCopy( p->pIfMan ); + If_ManCleanCutData( p->pIfMan ); + // set the PIs of the cut + Lpk_CutForEachLeaf( p->pNtk, pCut, pLeaf, i ) + If_ObjSetCopy( If_ManCi(p->pIfMan, i), pLeaf ); + // get the area of mapping + pObjNew = Abc_NodeFromIf_rec( p->pNtk, p->pIfMan, If_Regular(pDriver), p->vCover ); + pObjNew->pData = Hop_NotCond( pObjNew->pData, If_IsComplement(pDriver) ); + // perform replacement + Abc_NtkUpdate( p->pObj, pObjNew, p->vLevels ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Performs resynthesis for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Lpk_ResynthesizeNode( Lpk_Man_t * p ) +{ + static int Count = 0; + Kit_DsdNtk_t * pDsdNtk; + Lpk_Cut_t * pCut; + unsigned * pTruth; + void * pDsd = NULL; + int i, nSuppSize, RetValue, clk; + + // compute the cuts +clk = clock(); + if ( !Lpk_NodeCuts( p ) ) + { +p->timeCuts += clock() - clk; + return 0; + } +p->timeCuts += clock() - clk; + +//return 0; + + if ( p->pPars->fVeryVerbose ) + printf( "Node %5d : Mffc size = %5d. Cuts = %5d.\n", p->pObj->Id, p->nMffc, p->nEvals ); + // try the good cuts + p->nCutsTotal += p->nCuts; + p->nCutsUseful += p->nEvals; + for ( i = 0; i < p->nEvals; i++ ) + { + // get the cut + pCut = p->pCuts + p->pEvals[i]; + if ( p->pPars->fFirst && i == 1 ) + break; + + if ( p->pObj->Id == 8835 ) + { + int x = 0; + } + + // compute the truth table +clk = clock(); + pTruth = Lpk_CutTruth( p, pCut ); + nSuppSize = Extra_TruthSupportSize(pTruth, pCut->nLeaves); +p->timeTruth += clock() - clk; + + pDsdNtk = Kit_DsdDecompose( pTruth, pCut->nLeaves ); +// Kit_DsdVerify( pDsdNtk, pTruth, pCut->nLeaves ); + // skip 16-input non-DSD because ISOP will not work + if ( Kit_DsdNtkRoot(pDsdNtk)->nFans == 16 ) + { + Kit_DsdNtkFree( pDsdNtk ); + continue; + } + + // if DSD has nodes that require splitting to fit them into LUTs + // we can skip those cuts that cannot lead to improvement + // (a full DSD network requires V = Nmin * (K-1) + 1 for improvement) + if ( Kit_DsdNonDsdSizeMax(pDsdNtk) > p->pPars->nLutSize && + nSuppSize >= ((int)pCut->nNodes - (int)pCut->nNodesDup - 1) * (p->pPars->nLutSize - 1) + 1 ) + { + Kit_DsdNtkFree( pDsdNtk ); + continue; + } + + if ( p->pPars->fVeryVerbose ) + { +// char * pFileName; + printf( " C%02d: L= %2d/%2d V= %2d/%d N= %d W= %4.2f ", + i, pCut->nLeaves, nSuppSize, pCut->nNodes, pCut->nNodesDup, pCut->nLuts, pCut->Weight ); + Kit_DsdPrint( stdout, pDsdNtk ); +// Kit_DsdPrintFromTruth( pTruth, pCut->nLeaves ); +// pFileName = Kit_TruthDumpToFile( pTruth, pCut->nLeaves, Count++ ); +// printf( "Saved truth table in file \"%s\".\n", pFileName ); + } + + // update the network +clk = clock(); + RetValue = Lpk_ExploreCut( p, pCut, pDsdNtk ); +p->timeEval += clock() - clk; + Kit_DsdNtkFree( pDsdNtk ); + if ( RetValue ) + break; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Performs resynthesis for one network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Lpk_Resynthesize( Abc_Ntk_t * pNtk, Lpk_Par_t * pPars ) +{ + ProgressBar * pProgress; + Lpk_Man_t * p; + Abc_Obj_t * pObj; + double Delta; + int i, Iter, nNodes, nNodesPrev, clk = clock(); + assert( Abc_NtkIsLogic(pNtk) ); + + // get the number of inputs + pPars->nLutSize = Abc_NtkGetFaninMax( pNtk ); + // adjust the number of crossbars based on LUT size + if ( pPars->nVarsShared > pPars->nLutSize - 2 ) + pPars->nVarsShared = pPars->nLutSize - 2; + // get the max number of LUTs tried + pPars->nVarsMax = pPars->nLutsMax * (pPars->nLutSize - 1) + 1; // V = N * (K-1) + 1 + while ( pPars->nVarsMax > 16 ) + { + pPars->nLutsMax--; + pPars->nVarsMax = pPars->nLutsMax * (pPars->nLutSize - 1) + 1; + + } + if ( pPars->fVerbose ) + { + printf( "Resynthesis for %d %d-LUTs with %d non-MFFC LUTs, %d crossbars, and %d-input cuts.\n", + pPars->nLutsMax, pPars->nLutSize, pPars->nLutsOver, pPars->nVarsShared, pPars->nVarsMax ); + } + + + // convert into the AIG + if ( !Abc_NtkToAig(pNtk) ) + { + fprintf( stdout, "Converting to BDD has failed.\n" ); + return 0; + } + assert( Abc_NtkHasAig(pNtk) ); + + // set the number of levels + Abc_NtkLevel( pNtk ); + Abc_NtkStartReverseLevels( pNtk, pPars->nGrowthLevel ); + + // start the manager + p = Lpk_ManStart( pPars ); + p->pNtk = pNtk; + p->nNodesTotal = Abc_NtkNodeNum(pNtk); + p->vLevels = Vec_VecStart( pNtk->LevelMax ); + if ( p->pPars->fSatur ) + p->vVisited = Vec_VecStart( 0 ); + if ( pPars->fVerbose ) + p->nTotalNets = Abc_NtkGetTotalFanins(pNtk); + + // iterate over the network + nNodesPrev = p->nNodesTotal; + for ( Iter = 1; ; Iter++ ) + { + // expand storage for changed nodes + if ( p->pPars->fSatur ) + Vec_VecExpand( p->vVisited, Abc_NtkObjNumMax(pNtk) + 1 ); + + // consider all nodes + nNodes = Abc_NtkObjNumMax(pNtk); + if ( !pPars->fVeryVerbose ) + pProgress = Extra_ProgressBarStart( stdout, nNodes ); + Abc_NtkForEachNode( pNtk, pObj, i ) + { + // skip all except the final node + if ( pPars->fFirst ) + { + if ( !Abc_ObjIsCo(Abc_ObjFanout0(pObj)) ) + continue; + } + + if ( i >= nNodes ) + break; + if ( !pPars->fVeryVerbose ) + Extra_ProgressBarUpdate( pProgress, i, NULL ); + // skip the nodes that did not change + if ( p->pPars->fSatur && !Lpk_NodeHasChanged(p, pObj->Id) ) + continue; + // resynthesize + p->pObj = pObj; + Lpk_ResynthesizeNode( p ); + +// if ( p->nChanges == 3 ) +// break; + } + if ( !pPars->fVeryVerbose ) + Extra_ProgressBarStop( pProgress ); + + // check the increase + Delta = 100.00 * (nNodesPrev - Abc_NtkNodeNum(pNtk)) / p->nNodesTotal; + if ( Delta < 0.05 ) + break; + nNodesPrev = Abc_NtkNodeNum(pNtk); + if ( !p->pPars->fSatur ) + break; + + if ( pPars->fFirst ) + break; + } + Abc_NtkStopReverseLevels( pNtk ); + + if ( pPars->fVerbose ) + { + p->nTotalNets2 = Abc_NtkGetTotalFanins(pNtk); + printf( "Reduction in nodes = %5d. (%.2f %%) ", + p->nGainTotal, 100.0 * p->nGainTotal / p->nNodesTotal ); + printf( "Reduction in edges = %5d. (%.2f %%) ", + p->nTotalNets-p->nTotalNets2, 100.0*(p->nTotalNets-p->nTotalNets2)/p->nTotalNets ); + printf( "\n" ); + + printf( "Nodes = %5d (%3d) Cuts = %5d (%4d) Changes = %5d Iter = %2d Benefit = %d.\n", + p->nNodesTotal, p->nNodesOver, p->nCutsTotal, p->nCutsUseful, p->nChanges, Iter, p->nBenefited ); + printf( "Non-DSD:" ); + for ( i = 3; i <= pPars->nVarsMax; i++ ) + if ( p->nBlocks[i] ) + printf( " %d=%d", i, p->nBlocks[i] ); + printf( "\n" ); + + p->timeTotal = clock() - clk; + p->timeEval = p->timeEval - p->timeMap; + p->timeOther = p->timeTotal - p->timeCuts - p->timeTruth - p->timeEval - p->timeMap; + PRTP( "Cuts ", p->timeCuts, p->timeTotal ); + PRTP( "Truth ", p->timeTruth, p->timeTotal ); + PRTP( "Eval ", p->timeEval, p->timeTotal ); + PRTP( "Map ", p->timeMap, p->timeTotal ); + PRTP( "Other ", p->timeOther, p->timeTotal ); + PRTP( "TOTAL ", p->timeTotal, p->timeTotal ); + } + + Lpk_ManStop( p ); + // check the resulting network + if ( !Abc_NtkCheck( pNtk ) ) + { + printf( "Lpk_Resynthesize: The network check has failed.\n" ); + return 0; + } + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/lpk/lpkCut.c b/abc_with_bb_support/src/opt/lpk/lpkCut.c new file mode 100644 index 000000000..677cdd156 --- /dev/null +++ b/abc_with_bb_support/src/opt/lpk/lpkCut.c @@ -0,0 +1,604 @@ +/**CFile**************************************************************** + + FileName [lpkCut.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Fast Boolean matching for LUT structures.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: lpkCut.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "lpkInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes the truth able of one cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned * Lpk_CutTruth_rec( Hop_Man_t * pMan, Hop_Obj_t * pObj, int nVars, Vec_Ptr_t * vTtNodes, int * iCount ) +{ + unsigned * pTruth, * pTruth0, * pTruth1; + assert( !Hop_IsComplement(pObj) ); + if ( pObj->pData ) + { + assert( ((unsigned)pObj->pData) & 0xffff0000 ); + return pObj->pData; + } + // get the plan for a new truth table + pTruth = Vec_PtrEntry( vTtNodes, (*iCount)++ ); + if ( Hop_ObjIsConst1(pObj) ) + Extra_TruthFill( pTruth, nVars ); + else + { + assert( Hop_ObjIsAnd(pObj) ); + // compute the truth tables of the fanins + pTruth0 = Lpk_CutTruth_rec( pMan, Hop_ObjFanin0(pObj), nVars, vTtNodes, iCount ); + pTruth1 = Lpk_CutTruth_rec( pMan, Hop_ObjFanin1(pObj), nVars, vTtNodes, iCount ); + // creat the truth table of the node + Extra_TruthAndPhase( pTruth, pTruth0, pTruth1, nVars, Hop_ObjFaninC0(pObj), Hop_ObjFaninC1(pObj) ); + } + pObj->pData = pTruth; + return pTruth; +} + +/**Function************************************************************* + + Synopsis [Computes the truth able of one cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned * Lpk_CutTruth( Lpk_Man_t * p, Lpk_Cut_t * pCut ) +{ + Hop_Man_t * pManHop = p->pNtk->pManFunc; + Hop_Obj_t * pObjHop; + Abc_Obj_t * pObj, * pFanin; + unsigned * pTruth; + int i, k, iCount = 0; +// Lpk_NodePrintCut( p, pCut ); + + // initialize the leaves + Lpk_CutForEachLeaf( p->pNtk, pCut, pObj, i ) + pObj->pCopy = Vec_PtrEntry( p->vTtElems, i ); + + // construct truth table in the topological order + Lpk_CutForEachNodeReverse( p->pNtk, pCut, pObj, i ) + { + // get the local AIG + pObjHop = Hop_Regular(pObj->pData); + // clean the data field of the nodes in the AIG subgraph + Hop_ObjCleanData_rec( pObjHop ); + // set the initial truth tables at the fanins + Abc_ObjForEachFanin( pObj, pFanin, k ) + { + assert( ((unsigned)pFanin->pCopy) & 0xffff0000 ); + Hop_ManPi( pManHop, k )->pData = pFanin->pCopy; + } + // compute the truth table of internal nodes + pTruth = Lpk_CutTruth_rec( pManHop, pObjHop, pCut->nLeaves, p->vTtNodes, &iCount ); + if ( Hop_IsComplement(pObj->pData) ) + Extra_TruthNot( pTruth, pTruth, pCut->nLeaves ); + // set the truth table at the node + pObj->pCopy = (Abc_Obj_t *)pTruth; + } + + return pTruth; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if at least one entry has changed.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Lpk_NodeRecordImpact( Lpk_Man_t * p ) +{ + Lpk_Cut_t * pCut; + Vec_Ptr_t * vNodes = Vec_VecEntry( p->vVisited, p->pObj->Id ); + Abc_Obj_t * pNode; + int i, k; + // collect the nodes that impact the given node + Vec_PtrClear( vNodes ); + for ( i = 0; i < p->nCuts; i++ ) + { + pCut = p->pCuts + i; + for ( k = 0; k < (int)pCut->nLeaves; k++ ) + { + pNode = Abc_NtkObj( p->pNtk, pCut->pLeaves[k] ); + if ( pNode->fMarkC ) + continue; + pNode->fMarkC = 1; + Vec_PtrPush( vNodes, (void *)pNode->Id ); + Vec_PtrPush( vNodes, (void *)Abc_ObjFanoutNum(pNode) ); + } + } + // clear the marks + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + pNode = Abc_NtkObj( p->pNtk, (int)pNode ); + pNode->fMarkC = 0; + i++; + } +//printf( "%d ", Vec_PtrSize(vNodes) ); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the cut has structural DSD.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Lpk_NodeCutsCheckDsd( Lpk_Man_t * p, Lpk_Cut_t * pCut ) +{ + Abc_Obj_t * pObj, * pFanin; + int i, k, nCands, fLeavesOnly, RetValue; + assert( pCut->nLeaves > 0 ); + // clear ref counters + memset( p->pRefs, 0, sizeof(int) * pCut->nLeaves ); + // mark cut leaves + Lpk_CutForEachLeaf( p->pNtk, pCut, pObj, i ) + { + assert( pObj->fMarkA == 0 ); + pObj->fMarkA = 1; + pObj->pCopy = (void *)i; + } + // ref leaves pointed from the internal nodes + nCands = 0; + Lpk_CutForEachNode( p->pNtk, pCut, pObj, i ) + { + fLeavesOnly = 1; + Abc_ObjForEachFanin( pObj, pFanin, k ) + if ( pFanin->fMarkA ) + p->pRefs[(int)pFanin->pCopy]++; + else + fLeavesOnly = 0; + if ( fLeavesOnly ) + p->pCands[nCands++] = pObj->Id; + } + // look at the nodes that only point to the leaves + RetValue = 0; + for ( i = 0; i < nCands; i++ ) + { + pObj = Abc_NtkObj( p->pNtk, p->pCands[i] ); + Abc_ObjForEachFanin( pObj, pFanin, k ) + { + assert( pFanin->fMarkA == 1 ); + if ( p->pRefs[(int)pFanin->pCopy] > 1 ) + break; + } + if ( k == Abc_ObjFaninNum(pObj) ) + { + RetValue = 1; + break; + } + } + // unmark cut leaves + Lpk_CutForEachLeaf( p->pNtk, pCut, pObj, i ) + pObj->fMarkA = 0; + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if pDom is contained in pCut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Lpk_NodeCutsOneDominance( Lpk_Cut_t * pDom, Lpk_Cut_t * pCut ) +{ + int i, k; + for ( i = 0; i < (int)pDom->nLeaves; i++ ) + { + for ( k = 0; k < (int)pCut->nLeaves; k++ ) + if ( pDom->pLeaves[i] == pCut->pLeaves[k] ) + break; + if ( k == (int)pCut->nLeaves ) // node i in pDom is not contained in pCut + return 0; + } + // every node in pDom is contained in pCut + return 1; +} + +/**Function************************************************************* + + Synopsis [Check if the cut exists.] + + Description [Returns 1 if the cut exists.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Lpk_NodeCutsOneFilter( Lpk_Cut_t * pCuts, int nCuts, Lpk_Cut_t * pCutNew ) +{ + Lpk_Cut_t * pCut; + int i, k; + assert( pCutNew->uSign[0] || pCutNew->uSign[1] ); + // try to find the cut + for ( i = 0; i < nCuts; i++ ) + { + pCut = pCuts + i; + if ( pCut->nLeaves == 0 ) + continue; + if ( pCut->nLeaves == pCutNew->nLeaves ) + { + if ( pCut->uSign[0] == pCutNew->uSign[0] && pCut->uSign[1] == pCutNew->uSign[1] ) + { + for ( k = 0; k < (int)pCutNew->nLeaves; k++ ) + if ( pCut->pLeaves[k] != pCutNew->pLeaves[k] ) + break; + if ( k == (int)pCutNew->nLeaves ) + return 1; + } + continue; + } + if ( pCut->nLeaves < pCutNew->nLeaves ) + { + // skip the non-contained cuts + if ( (pCut->uSign[0] & pCutNew->uSign[0]) != pCut->uSign[0] ) + continue; + if ( (pCut->uSign[1] & pCutNew->uSign[1]) != pCut->uSign[1] ) + continue; + // check containment seriously + if ( Lpk_NodeCutsOneDominance( pCut, pCutNew ) ) + return 1; + continue; + } + // check potential containment of other cut + + // skip the non-contained cuts + if ( (pCut->uSign[0] & pCutNew->uSign[0]) != pCutNew->uSign[0] ) + continue; + if ( (pCut->uSign[1] & pCutNew->uSign[1]) != pCutNew->uSign[1] ) + continue; + // check containment seriously + if ( Lpk_NodeCutsOneDominance( pCutNew, pCut ) ) + pCut->nLeaves = 0; // removed + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Prints the given cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Lpk_NodePrintCut( Lpk_Man_t * p, Lpk_Cut_t * pCut, int fLeavesOnly ) +{ + Abc_Obj_t * pObj; + int i; + if ( !fLeavesOnly ) + printf( "LEAVES:\n" ); + Lpk_CutForEachLeaf( p->pNtk, pCut, pObj, i ) + printf( "%d,", pObj->Id ); + if ( !fLeavesOnly ) + { + printf( "\nNODES:\n" ); + Lpk_CutForEachNode( p->pNtk, pCut, pObj, i ) + { + printf( "%d,", pObj->Id ); + assert( Abc_ObjIsNode(pObj) ); + } + printf( "\n" ); + } +} + +/**Function************************************************************* + + Synopsis [Set the cut signature.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Lpk_NodeCutSignature( Lpk_Cut_t * pCut ) +{ + unsigned i; + pCut->uSign[0] = pCut->uSign[1] = 0; + for ( i = 0; i < pCut->nLeaves; i++ ) + { + pCut->uSign[(pCut->pLeaves[i] & 32) > 0] |= (1 << (pCut->pLeaves[i] & 31)); + if ( i != pCut->nLeaves - 1 ) + assert( pCut->pLeaves[i] < pCut->pLeaves[i+1] ); + } +} + + +/**Function************************************************************* + + Synopsis [Computes the set of all cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Lpk_NodeCutsOne( Lpk_Man_t * p, Lpk_Cut_t * pCut, int Node ) +{ + Lpk_Cut_t * pCutNew; + Abc_Obj_t * pObj, * pFanin; + int i, k, j, nLeavesNew; +/* + printf( "Exploring cut " ); + Lpk_NodePrintCut( p, pCut, 1 ); + printf( "with node %d\n", Node ); +*/ + // check if the cut can stand adding one more internal node + if ( pCut->nNodes == LPK_SIZE_MAX ) + return; + + // if the node is a PI, quit + pObj = Abc_NtkObj( p->pNtk, Node ); + if ( Abc_ObjIsCi(pObj) ) + return; + assert( Abc_ObjIsNode(pObj) ); + assert( Abc_ObjFaninNum(pObj) <= p->pPars->nLutSize ); + + // if the node is not in the MFFC, check the limit + if ( !Abc_NodeIsTravIdCurrent(pObj) ) + { + if ( (int)pCut->nNodesDup == p->pPars->nLutsOver ) + return; + assert( (int)pCut->nNodesDup < p->pPars->nLutsOver ); + } + + // check the possibility of adding this node using the signature + nLeavesNew = pCut->nLeaves - 1; + Abc_ObjForEachFanin( pObj, pFanin, i ) + { + if ( (pCut->uSign[(pFanin->Id & 32) > 0] & (1 << (pFanin->Id & 31))) ) + continue; + if ( ++nLeavesNew > p->pPars->nVarsMax ) + return; + } + + // initialize the set of leaves to the nodes in the cut + assert( p->nCuts < LPK_CUTS_MAX ); + pCutNew = p->pCuts + p->nCuts; +/* +if ( p->pObj->Id == 31 && Node == 38 && pCut->pNodes[0] == 31 && pCut->pNodes[1] == 34 && pCut->pNodes[2] == 35 )//p->nCuts == 48 ) +{ + int x = 0; + printf( "Start:\n" ); + Lpk_NodePrintCut( p, pCut ); +} +*/ + pCutNew->nLeaves = 0; + for ( i = 0; i < (int)pCut->nLeaves; i++ ) + if ( pCut->pLeaves[i] != Node ) + pCutNew->pLeaves[pCutNew->nLeaves++] = pCut->pLeaves[i]; + + // add new nodes + Abc_ObjForEachFanin( pObj, pFanin, i ) + { + // find the place where this node belongs + for ( k = 0; k < (int)pCutNew->nLeaves; k++ ) + if ( pCutNew->pLeaves[k] >= pFanin->Id ) + break; + if ( k < (int)pCutNew->nLeaves && pCutNew->pLeaves[k] == pFanin->Id ) + continue; + // check if there is room + if ( (int)pCutNew->nLeaves == p->pPars->nVarsMax ) + return; + // move all the nodes + for ( j = pCutNew->nLeaves; j > k; j-- ) + pCutNew->pLeaves[j] = pCutNew->pLeaves[j-1]; + pCutNew->pLeaves[k] = pFanin->Id; + pCutNew->nLeaves++; + assert( pCutNew->nLeaves <= LPK_SIZE_MAX ); + + } +/* + printf( " Trying cut: " ); + Lpk_NodePrintCut( p, pCutNew, 1 ); + printf( "\n" ); +*/ + // skip the contained cuts + Lpk_NodeCutSignature( pCutNew ); + if ( Lpk_NodeCutsOneFilter( p->pCuts, p->nCuts, pCutNew ) ) + return; + + + // update the set of internal nodes + assert( pCut->nNodes < LPK_SIZE_MAX ); + memcpy( pCutNew->pNodes, pCut->pNodes, pCut->nNodes * sizeof(int) ); + pCutNew->nNodes = pCut->nNodes; + pCutNew->pNodes[ pCutNew->nNodes++ ] = Node; + + // add the marked node + pCutNew->nNodesDup = pCut->nNodesDup + !Abc_NodeIsTravIdCurrent(pObj); +/* +if ( p->pObj->Id == 31 && Node == 38 )//p->nCuts == 48 ) +{ + int x = 0; + printf( "Finish:\n" ); + Lpk_NodePrintCut( p, pCutNew ); +} +*/ + // add the cut to storage + assert( p->nCuts < LPK_CUTS_MAX ); + p->nCuts++; + +// assert( pCut->nNodes <= p->nMffc + pCutNew->nNodesDup ); + +/* + printf( " Creating cut: " ); + Lpk_NodePrintCut( p, pCutNew, 1 ); + printf( "\n" ); +*/ + +// if ( !(pCut->nNodes <= p->nMffc + pCutNew->nNodesDup) ) +// printf( "Assertion in line 477 failed.\n" ); +} + +/**Function************************************************************* + + Synopsis [Computes the set of all cuts.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Lpk_NodeCuts( Lpk_Man_t * p ) +{ + Lpk_Cut_t * pCut, * pCut2; + int i, k, Temp, nMffc, fChanges; + + // mark the MFFC of the node with the current trav ID + nMffc = p->nMffc = Abc_NodeMffcLabel( p->pObj ); + assert( nMffc > 0 ); + if ( nMffc == 1 ) + return 0; + + // initialize the first cut + pCut = p->pCuts; p->nCuts = 1; + pCut->nNodes = 0; + pCut->nNodesDup = 0; + pCut->nLeaves = 1; + pCut->pLeaves[0] = p->pObj->Id; + // assign the signature + Lpk_NodeCutSignature( pCut ); + + // perform the cut computation + for ( i = 0; i < p->nCuts; i++ ) + { + pCut = p->pCuts + i; + if ( pCut->nLeaves == 0 ) + continue; + + // try to expand the fanins of this cut + for ( k = 0; k < (int)pCut->nLeaves; k++ ) + { + + if ( p->pObj->Id == 28 && i == 273 && k == 13 ) + { + Abc_Obj_t * pFanin = Abc_NtkObj(p->pNtk, pCut->pLeaves[k]); + int s = 0; + } + + // create a new cut + Lpk_NodeCutsOne( p, pCut, pCut->pLeaves[k] ); + // quit if the number of cuts has exceeded the limit + if ( p->nCuts == LPK_CUTS_MAX ) + break; + } + if ( p->nCuts == LPK_CUTS_MAX ) + break; + } + if ( p->nCuts == LPK_CUTS_MAX ) + p->nNodesOver++; + + // record the impact of this node + if ( p->pPars->fSatur ) + Lpk_NodeRecordImpact( p ); + + // compress the cuts by removing empty ones, those with negative Weight, and decomposable ones + p->nEvals = 0; + for ( i = 0; i < p->nCuts; i++ ) + { + pCut = p->pCuts + i; + if ( pCut->nLeaves < 2 ) + continue; + // compute the minimum number of LUTs needed to implement this cut + // V = N * (K-1) + 1 ~~~~~ N = Ceiling[(V-1)/(K-1)] = (V-1)/(K-1) + [(V-1)%(K-1) > 0] + pCut->nLuts = (pCut->nLeaves-1)/(p->pPars->nLutSize-1) + ( (pCut->nLeaves-1)%(p->pPars->nLutSize-1) > 0 ); + pCut->Weight = (float)1.0 * (pCut->nNodes - pCut->nNodesDup) / pCut->nLuts; //p->pPars->nLutsMax; + if ( pCut->Weight <= 1.001 ) + continue; + pCut->fHasDsd = Lpk_NodeCutsCheckDsd( p, pCut ); + if ( pCut->fHasDsd ) + continue; + p->pEvals[p->nEvals++] = i; + } + if ( p->nEvals == 0 ) + return 0; + + // sort the cuts by Weight + do { + fChanges = 0; + for ( i = 0; i < p->nEvals - 1; i++ ) + { + pCut = p->pCuts + p->pEvals[i]; + pCut2 = p->pCuts + p->pEvals[i+1]; + if ( pCut->Weight >= pCut2->Weight - 0.001 ) + continue; + Temp = p->pEvals[i]; + p->pEvals[i] = p->pEvals[i+1]; + p->pEvals[i+1] = Temp; + fChanges = 1; + } + } while ( fChanges ); +/* + for ( i = 0; i < p->nEvals; i++ ) + { + pCut = p->pCuts + p->pEvals[i]; + printf( "Cut %3d : W = %5.2f.\n", i, pCut->Weight ); + } + printf( "\n" ); +*/ + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/lpk/lpkInt.h b/abc_with_bb_support/src/opt/lpk/lpkInt.h new file mode 100644 index 000000000..9e665f16f --- /dev/null +++ b/abc_with_bb_support/src/opt/lpk/lpkInt.h @@ -0,0 +1,165 @@ +/**CFile**************************************************************** + + FileName [lpkInt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Fast Boolean matching for LUT structures.] + + Synopsis [Internal declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: lpkInt.h,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __LPK_INT_H__ +#define __LPK_INT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include "abc.h" +#include "kit.h" +#include "if.h" +#include "lpk.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +#define LPK_SIZE_MAX 24 // the largest size of the function +#define LPK_CUTS_MAX 512 // the largest number of cuts considered + +typedef struct Lpk_Man_t_ Lpk_Man_t; +typedef struct Lpk_Cut_t_ Lpk_Cut_t; + +struct Lpk_Cut_t_ +{ + unsigned nLeaves : 6; // (L) the number of leaves + unsigned nNodes : 6; // (M) the number of nodes + unsigned nNodesDup : 6; // (Q) nodes outside of MFFC + unsigned nLuts : 6; // (N) the number of LUTs to try + unsigned unused : 6; // unused + unsigned fHasDsd : 1; // set to 1 if the cut has structural DSD (and so cannot be used) + unsigned fMark : 1; // multipurpose mark + unsigned uSign[2]; // the signature + float Weight; // the weight of the cut: (M - Q)/N(V) (the larger the better) + int Gain; // the gain achieved using this cut + int pLeaves[LPK_SIZE_MAX]; // the leaves of the cut + int pNodes[LPK_SIZE_MAX]; // the nodes of the cut +}; + +struct Lpk_Man_t_ +{ + // parameters + Lpk_Par_t * pPars; // the set of parameters + // current representation + Abc_Ntk_t * pNtk; // the network + Abc_Obj_t * pObj; // the node to resynthesize + // cut representation + int nMffc; // the size of MFFC of the node + int nCuts; // the total number of cuts + int nCutsMax; // the largest possible number of cuts + int nEvals; // the number of good cuts + Lpk_Cut_t pCuts[LPK_CUTS_MAX]; // the storage for cuts + int pEvals[LPK_CUTS_MAX]; // the good cuts + // visited nodes + Vec_Vec_t * vVisited; + // mapping manager + If_Man_t * pIfMan; + Vec_Int_t * vCover; + Vec_Vec_t * vLevels; + // temporary variables + int fCofactoring; // working in the cofactoring mode + int fCalledOnce; // limits the depth of MUX cofactoring + int nCalledSRed; // the number of called to SRed + int pRefs[LPK_SIZE_MAX]; // fanin reference counters + int pCands[LPK_SIZE_MAX]; // internal nodes pointing only to the leaves + // truth table representation + Vec_Ptr_t * vTtElems; // elementary truth tables + Vec_Ptr_t * vTtNodes; // storage for temporary truth tables of the nodes + // variable sets + Vec_Int_t * vSets[8]; + Kit_DsdMan_t * pDsdMan; + // statistics + int nNodesTotal; // total number of nodes + int nNodesOver; // nodes with cuts over the limit + int nCutsTotal; // total number of cuts + int nCutsUseful; // useful cuts + int nGainTotal; // the gain in LUTs + int nChanges; // the number of changed nodes + int nBenefited; // the number of gainful that benefited from decomposition + int nTotalNets; + int nTotalNets2; + // counter of non-DSD blocks + int nBlocks[17]; + // rutime + int timeCuts; + int timeTruth; + int timeEval; + int timeMap; + int timeOther; + int timeTotal; +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// ITERATORS /// +//////////////////////////////////////////////////////////////////////// + +#define Lpk_CutForEachLeaf( pNtk, pCut, pObj, i ) \ + for ( i = 0; (i < (int)(pCut)->nLeaves) && (((pObj) = Abc_NtkObj(pNtk, (pCut)->pLeaves[i])), 1); i++ ) +#define Lpk_CutForEachNode( pNtk, pCut, pObj, i ) \ + for ( i = 0; (i < (int)(pCut)->nNodes) && (((pObj) = Abc_NtkObj(pNtk, (pCut)->pNodes[i])), 1); i++ ) +#define Lpk_CutForEachNodeReverse( pNtk, pCut, pObj, i ) \ + for ( i = (int)(pCut)->nNodes - 1; (i >= 0) && (((pObj) = Abc_NtkObj(pNtk, (pCut)->pNodes[i])), 1); i-- ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== lpkCut.c =========================================================*/ +extern unsigned * Lpk_CutTruth( Lpk_Man_t * p, Lpk_Cut_t * pCut ); +extern int Lpk_NodeCuts( Lpk_Man_t * p ); +/*=== lpkMap.c =========================================================*/ +extern Lpk_Man_t * Lpk_ManStart( Lpk_Par_t * pPars ); +extern void Lpk_ManStop( Lpk_Man_t * p ); +/*=== lpkMap.c =========================================================*/ +extern If_Obj_t * Lpk_MapPrime( Lpk_Man_t * p, unsigned * pTruth, int nVars, If_Obj_t ** ppLeaves ); +extern If_Obj_t * Lpk_MapTree_rec( Lpk_Man_t * p, Kit_DsdNtk_t * pNtk, If_Obj_t ** ppLeaves, int iLit, If_Obj_t * pResult ); +/*=== lpkMulti.c =======================================================*/ +extern If_Obj_t * Lpk_MapTreeMulti( Lpk_Man_t * p, unsigned * pTruth, int nLeaves, If_Obj_t ** ppLeaves ); +/*=== lpkMux.c =========================================================*/ +extern If_Obj_t * Lpk_MapTreeMux_rec( Lpk_Man_t * p, unsigned * pTruth, int nVars, If_Obj_t ** ppLeaves ); +extern If_Obj_t * Lpk_MapSuppRedDec_rec( Lpk_Man_t * p, unsigned * pTruth, int nVars, If_Obj_t ** ppLeaves ); +/*=== lpkSets.c =========================================================*/ +extern unsigned Lpk_MapSuppRedDecSelect( Lpk_Man_t * p, unsigned * pTruth, int nVars, int * piVar, int * piVarReused ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/opt/lpk/lpkMan.c b/abc_with_bb_support/src/opt/lpk/lpkMan.c new file mode 100644 index 000000000..9a80a4ab1 --- /dev/null +++ b/abc_with_bb_support/src/opt/lpk/lpkMan.c @@ -0,0 +1,98 @@ +/**CFile**************************************************************** + + FileName [lpkMan.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Fast Boolean matching for LUT structures.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: lpkMan.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "lpkInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Lpk_Man_t * Lpk_ManStart( Lpk_Par_t * pPars ) +{ + Lpk_Man_t * p; + int i; + assert( pPars->nLutsMax <= 16 ); + assert( pPars->nVarsMax > 0 ); + p = ALLOC( Lpk_Man_t, 1 ); + memset( p, 0, sizeof(Lpk_Man_t) ); + p->pPars = pPars; + p->nCutsMax = LPK_CUTS_MAX; + p->vTtElems = Vec_PtrAllocTruthTables( pPars->nVarsMax ); + p->vTtNodes = Vec_PtrAllocSimInfo( 1024, Abc_TruthWordNum(pPars->nVarsMax) ); + p->vCover = Vec_IntAlloc( 1 << 12 ); + for ( i = 0; i < 8; i++ ) + p->vSets[i] = Vec_IntAlloc(100); + p->pDsdMan = Kit_DsdManAlloc( pPars->nVarsMax ); + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Lpk_ManStop( Lpk_Man_t * p ) +{ + int i; + Kit_DsdManFree( p->pDsdMan ); + for ( i = 0; i < 8; i++ ) + Vec_IntFree(p->vSets[i]); + if ( p->pIfMan ) + { + void * pPars = p->pIfMan->pPars; + If_ManStop( p->pIfMan ); + free( pPars ); + } + if ( p->vLevels ) + Vec_VecFree( p->vLevels ); + if ( p->vVisited ) + Vec_VecFree( p->vVisited ); + Vec_IntFree( p->vCover ); + Vec_PtrFree( p->vTtElems ); + Vec_PtrFree( p->vTtNodes ); + free( p ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/lpk/lpkMap.c b/abc_with_bb_support/src/opt/lpk/lpkMap.c new file mode 100644 index 000000000..501497048 --- /dev/null +++ b/abc_with_bb_support/src/opt/lpk/lpkMap.c @@ -0,0 +1,205 @@ +/**CFile**************************************************************** + + FileName [lpkMap.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Fast Boolean matching for LUT structures.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: lpkMap.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "lpkInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Transforms the decomposition graph into the AIG.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +If_Obj_t * Lpk_MapPrimeInternal( If_Man_t * pIfMan, Kit_Graph_t * pGraph ) +{ + Kit_Node_t * pNode; + If_Obj_t * pAnd0, * pAnd1; + int i; + // check for constant function + if ( Kit_GraphIsConst(pGraph) ) + return If_ManConst1(pIfMan); + // check for a literal + if ( Kit_GraphIsVar(pGraph) ) + return Kit_GraphVar(pGraph)->pFunc; + // build the AIG nodes corresponding to the AND gates of the graph + Kit_GraphForEachNode( pGraph, pNode, i ) + { + pAnd0 = Kit_GraphNode(pGraph, pNode->eEdge0.Node)->pFunc; + pAnd1 = Kit_GraphNode(pGraph, pNode->eEdge1.Node)->pFunc; + pNode->pFunc = If_ManCreateAnd( pIfMan, + If_NotCond( If_Regular(pAnd0), If_IsComplement(pAnd0) ^ pNode->eEdge0.fCompl ), + If_NotCond( If_Regular(pAnd1), If_IsComplement(pAnd1) ^ pNode->eEdge1.fCompl ) ); + } + return pNode->pFunc; +} + +/**Function************************************************************* + + Synopsis [Strashes one logic node using its SOP.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +If_Obj_t * Lpk_MapPrime( Lpk_Man_t * p, unsigned * pTruth, int nVars, If_Obj_t ** ppLeaves ) +{ + Kit_Graph_t * pGraph; + Kit_Node_t * pNode; + If_Obj_t * pRes; + int i; + // derive the factored form + pGraph = Kit_TruthToGraph( pTruth, nVars, p->vCover ); + if ( pGraph == NULL ) + return NULL; + // collect the fanins + Kit_GraphForEachLeaf( pGraph, pNode, i ) + pNode->pFunc = ppLeaves[i]; + // perform strashing + pRes = Lpk_MapPrimeInternal( p->pIfMan, pGraph ); + pRes = If_NotCond( pRes, Kit_GraphIsComplement(pGraph) ); + Kit_GraphFree( pGraph ); + return pRes; +} + +/**Function************************************************************* + + Synopsis [Prepares the mapping manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +If_Obj_t * Lpk_MapTree_rec( Lpk_Man_t * p, Kit_DsdNtk_t * pNtk, If_Obj_t ** ppLeaves, int iLit, If_Obj_t * pResult ) +{ + Kit_DsdObj_t * pObj; + If_Obj_t * pObjNew = NULL, * pObjNew2 = NULL, * pFansNew[16]; + unsigned i, iLitFanin; + + assert( iLit >= 0 ); + + // consider the case of a gate + pObj = Kit_DsdNtkObj( pNtk, Kit_DsdLit2Var(iLit) ); + if ( pObj == NULL ) + { + pObjNew = ppLeaves[Kit_DsdLit2Var(iLit)]; + return If_NotCond( pObjNew, Kit_DsdLitIsCompl(iLit) ); + } + if ( pObj->Type == KIT_DSD_CONST1 ) + { + return If_NotCond( If_ManConst1(p->pIfMan), Kit_DsdLitIsCompl(iLit) ); + } + if ( pObj->Type == KIT_DSD_VAR ) + { + pObjNew = ppLeaves[Kit_DsdLit2Var(pObj->pFans[0])]; + return If_NotCond( pObjNew, Kit_DsdLitIsCompl(iLit) ^ Kit_DsdLitIsCompl(pObj->pFans[0]) ); + } + if ( pObj->Type == KIT_DSD_AND ) + { + assert( pObj->nFans == 2 ); + pFansNew[0] = Lpk_MapTree_rec( p, pNtk, ppLeaves, pObj->pFans[0], NULL ); + pFansNew[1] = pResult? pResult : Lpk_MapTree_rec( p, pNtk, ppLeaves, pObj->pFans[1], NULL ); + if ( pFansNew[0] == NULL || pFansNew[1] == NULL ) + return NULL; + pObjNew = If_ManCreateAnd( p->pIfMan, pFansNew[0], pFansNew[1] ); + return If_NotCond( pObjNew, Kit_DsdLitIsCompl(iLit) ); + } + if ( pObj->Type == KIT_DSD_XOR ) + { + int fCompl = Kit_DsdLitIsCompl(iLit); + assert( pObj->nFans == 2 ); + pFansNew[0] = Lpk_MapTree_rec( p, pNtk, ppLeaves, pObj->pFans[0], NULL ); + pFansNew[1] = pResult? pResult : Lpk_MapTree_rec( p, pNtk, ppLeaves, pObj->pFans[1], NULL ); + if ( pFansNew[0] == NULL || pFansNew[1] == NULL ) + return NULL; + fCompl ^= If_IsComplement(pFansNew[0]) ^ If_IsComplement(pFansNew[1]); + pObjNew = If_ManCreateXor( p->pIfMan, If_Regular(pFansNew[0]), If_Regular(pFansNew[1]) ); + return If_NotCond( pObjNew, fCompl ); + } + assert( pObj->Type == KIT_DSD_PRIME ); + p->nBlocks[pObj->nFans]++; + + // solve for the inputs + Kit_DsdObjForEachFanin( pNtk, pObj, iLitFanin, i ) + { + if ( i == 0 ) + pFansNew[i] = pResult? pResult : Lpk_MapTree_rec( p, pNtk, ppLeaves, iLitFanin, NULL ); + else + pFansNew[i] = Lpk_MapTree_rec( p, pNtk, ppLeaves, iLitFanin, NULL ); + if ( pFansNew[i] == NULL ) + return NULL; + } +/* + if ( !p->fCofactoring && p->pPars->nVarsShared > 0 && (int)pObj->nFans > p->pPars->nLutSize ) + { + pObjNew = Lpk_MapTreeMulti( p, Kit_DsdObjTruth(pObj), pObj->nFans, pFansNew ); + return If_NotCond( pObjNew, Kit_DsdLitIsCompl(iLit) ); + } +*/ +/* + if ( (int)pObj->nFans > p->pPars->nLutSize ) + { + pObjNew2 = Lpk_MapTreeMux_rec( p, Kit_DsdObjTruth(pObj), pObj->nFans, pFansNew ); +// if ( pObjNew2 ) +// return If_NotCond( pObjNew2, Kit_DsdLitIsCompl(iLit) ); + } +*/ + + // find best cofactoring variable + if ( p->pPars->nVarsShared > 0 && (int)pObj->nFans > p->pPars->nLutSize ) + { + pObjNew2 = Lpk_MapSuppRedDec_rec( p, Kit_DsdObjTruth(pObj), pObj->nFans, pFansNew ); + if ( pObjNew2 ) + return If_NotCond( pObjNew2, Kit_DsdLitIsCompl(iLit) ); + } + + pObjNew = Lpk_MapPrime( p, Kit_DsdObjTruth(pObj), pObj->nFans, pFansNew ); + + // add choice + if ( pObjNew && pObjNew2 ) + { + If_ObjSetChoice( If_Regular(pObjNew), If_Regular(pObjNew2) ); + If_ManCreateChoice( p->pIfMan, If_Regular(pObjNew) ); + } + return If_NotCond( pObjNew, Kit_DsdLitIsCompl(iLit) ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/lpk/lpkMulti.c b/abc_with_bb_support/src/opt/lpk/lpkMulti.c new file mode 100644 index 000000000..3f0995fd0 --- /dev/null +++ b/abc_with_bb_support/src/opt/lpk/lpkMulti.c @@ -0,0 +1,495 @@ +/**CFile**************************************************************** + + FileName [lpkMulti.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Fast Boolean matching for LUT structures.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: lpkMulti.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "lpkInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Records variable order.] + + Description [Increaments Order[x][y] by 1 if x should be above y in the DSD.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Lpk_CreateVarOrder( Kit_DsdNtk_t * pNtk, char pTable[][16] ) +{ + Kit_DsdObj_t * pObj; + unsigned uSuppFanins, k; + int Above[16], Below[16]; + int nAbove, nBelow, iFaninLit, i, x, y; + // iterate through the nodes + Kit_DsdNtkForEachObj( pNtk, pObj, i ) + { + // collect fanin support of this node + nAbove = 0; + uSuppFanins = 0; + Kit_DsdObjForEachFanin( pNtk, pObj, iFaninLit, k ) + { + if ( Kit_DsdLitIsLeaf( pNtk, iFaninLit ) ) + Above[nAbove++] = Kit_DsdLit2Var(iFaninLit); + else + uSuppFanins |= Kit_DsdLitSupport( pNtk, iFaninLit ); + } + // find the below variables + nBelow = 0; + for ( y = 0; y < 16; y++ ) + if ( uSuppFanins & (1 << y) ) + Below[nBelow++] = y; + // create all pairs + for ( x = 0; x < nAbove; x++ ) + for ( y = 0; y < nBelow; y++ ) + pTable[Above[x]][Below[y]]++; + } +} + +/**Function************************************************************* + + Synopsis [Creates commmon variable order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Lpk_CreateCommonOrder( char pTable[][16], int piCofVar[], int nCBars, int pPrios[], int nVars, int fVerbose ) +{ + int Score[16] = {0}, pPres[16]; + int i, y, x, iVarBest, ScoreMax, PrioCount; + + // mark the present variables + for ( i = 0; i < nVars; i++ ) + pPres[i] = 1; + // remove cofactored variables + for ( i = 0; i < nCBars; i++ ) + pPres[piCofVar[i]] = 0; + + // compute scores for each leaf + for ( i = 0; i < nVars; i++ ) + { + if ( pPres[i] == 0 ) + continue; + for ( y = 0; y < nVars; y++ ) + Score[i] += pTable[i][y]; + for ( x = 0; x < nVars; x++ ) + Score[i] -= pTable[x][i]; + } + + // print the scores + if ( fVerbose ) + { + printf( "Scores: " ); + for ( i = 0; i < nVars; i++ ) + printf( "%c=%d ", 'a'+i, Score[i] ); + printf( " " ); + printf( "Prios: " ); + } + + // derive variable priority + // variables with equal score receive the same priority + for ( i = 0; i < nVars; i++ ) + pPrios[i] = 16; + + // iterate until variables remain + for ( PrioCount = 1; ; PrioCount++ ) + { + // find the present variable with the highest score + iVarBest = -1; + ScoreMax = -100000; + for ( i = 0; i < nVars; i++ ) + { + if ( pPres[i] == 0 ) + continue; + if ( ScoreMax < Score[i] ) + { + ScoreMax = Score[i]; + iVarBest = i; + } + } + if ( iVarBest == -1 ) + break; + // give the next priority to all vars having this score + if ( fVerbose ) + printf( "%d=", PrioCount ); + for ( i = 0; i < nVars; i++ ) + { + if ( pPres[i] == 0 ) + continue; + if ( Score[i] == ScoreMax ) + { + pPrios[i] = PrioCount; + pPres[i] = 0; + if ( fVerbose ) + printf( "%c", 'a'+i ); + } + } + if ( fVerbose ) + printf( " " ); + } + if ( fVerbose ) + printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [Finds components with the highest priority.] + + Description [Returns the number of components selected.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Lpk_FindHighest( Kit_DsdNtk_t ** ppNtks, int * piLits, int nSize, int * pPrio, int * pDecision ) +{ + Kit_DsdObj_t * pObj; + unsigned uSupps[8], uSuppFanin, uSuppTotal, uSuppLarge; + int i, pTriv[8], PrioMin, iVarMax, nComps, fOneNonTriv; + + // find individual support and total support + uSuppTotal = 0; + for ( i = 0; i < nSize; i++ ) + { + pTriv[i] = 1; + if ( piLits[i] < 0 ) + uSupps[i] = 0; + else if ( Kit_DsdLitIsLeaf(ppNtks[i], piLits[i]) ) + uSupps[i] = Kit_DsdLitSupport( ppNtks[i], piLits[i] ); + else + { + pObj = Kit_DsdNtkObj( ppNtks[i], Kit_DsdLit2Var(piLits[i]) ); + if ( pObj->Type == KIT_DSD_PRIME ) + { + pTriv[i] = 0; + uSuppFanin = Kit_DsdLitSupport( ppNtks[i], pObj->pFans[0] ); + } + else + { + assert( pObj->nFans == 2 ); + if ( !Kit_DsdLitIsLeaf(ppNtks[i], pObj->pFans[0]) ) + pTriv[i] = 0; + uSuppFanin = Kit_DsdLitSupport( ppNtks[i], pObj->pFans[1] ); + } + uSupps[i] = Kit_DsdLitSupport( ppNtks[i], piLits[i] ) & ~uSuppFanin; + } + assert( uSupps[i] <= 0xFFFF ); + uSuppTotal |= uSupps[i]; + } + if ( uSuppTotal == 0 ) + return 0; + + // find one support variable with the highest priority + PrioMin = ABC_INFINITY; + iVarMax = -1; + for ( i = 0; i < 16; i++ ) + if ( uSuppTotal & (1 << i) ) + if ( PrioMin > pPrio[i] ) + { + PrioMin = pPrio[i]; + iVarMax = i; + } + assert( iVarMax != -1 ); + + // select components, which have this variable + nComps = 0; + fOneNonTriv = 0; + uSuppLarge = 0; + for ( i = 0; i < nSize; i++ ) + if ( uSupps[i] & (1<pIfMan) ); + + // iterate over the nodes + if ( p->pPars->fVeryVerbose ) + printf( "Decision: " ); + for ( i = 0; i < nSize; i++ ) + { + if ( pDecision[i] ) + { + if ( p->pPars->fVeryVerbose ) + printf( "%d ", i ); + assert( piLits[i] >= 0 ); + pObj = Kit_DsdNtkObj( ppNtks[i], Kit_DsdLit2Var(piLits[i]) ); + if ( pObj == NULL ) + piLitsNew[i] = -2; + else if ( pObj->Type == KIT_DSD_PRIME ) + piLitsNew[i] = pObj->pFans[0]; + else + piLitsNew[i] = pObj->pFans[1]; + } + else + piLitsNew[i] = piLits[i]; + } + if ( p->pPars->fVeryVerbose ) + printf( "\n" ); + + // call again + pResPrev = Lpk_MapTreeMulti_rec( p, ppNtks, piLitsNew, piCofVar, nCBars, ppLeaves, nLeaves, pPrio ); + + // create new set of nodes + for ( i = 0; i < nSize; i++ ) + { + if ( pDecision[i] ) + pObjsNew[nCBars][i] = Lpk_MapTree_rec( p, ppNtks[i], ppLeaves, piLits[i], pResPrev ); + else if ( piLits[i] == -1 ) + pObjsNew[nCBars][i] = If_ManConst1(p->pIfMan); + else if ( piLits[i] == -2 ) + pObjsNew[nCBars][i] = If_Not( If_ManConst1(p->pIfMan) ); + else + pObjsNew[nCBars][i] = pResPrev; + } + + // create MUX using these outputs + for ( k = nCBars; k > 0; k-- ) + { + nSize /= 2; + for ( i = 0; i < nSize; i++ ) + pObjsNew[k-1][i] = If_ManCreateMux( p->pIfMan, pObjsNew[k][2*i+0], pObjsNew[k][2*i+1], ppLeaves[piCofVar[k-1]] ); + } + assert( nSize == 1 ); + return pObjsNew[0][0]; +} + +/**Function************************************************************* + + Synopsis [Prepares the mapping manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +If_Obj_t * Lpk_MapTreeMulti( Lpk_Man_t * p, unsigned * pTruth, int nVars, If_Obj_t ** ppLeaves ) +{ + static Counter = 0; + If_Obj_t * pResult; + Kit_DsdNtk_t * ppNtks[8] = {0}, * pTemp; + Kit_DsdObj_t * pRoot; + int piCofVar[4], pPrios[16], pFreqs[16] = {0}, piLits[16]; + int i, k, nCBars, nSize, nMemSize; + unsigned * ppCofs[4][8], uSupport; + char pTable[16][16] = {0}; + int fVerbose = p->pPars->fVeryVerbose; + + Counter++; +// printf( "Run %d.\n", Counter ); + + // allocate storage for cofactors + nMemSize = Kit_TruthWordNum(nVars); + ppCofs[0][0] = ALLOC( unsigned, 32 * nMemSize ); + nSize = 0; + for ( i = 0; i < 4; i++ ) + for ( k = 0; k < 8; k++ ) + ppCofs[i][k] = ppCofs[0][0] + nMemSize * nSize++; + assert( nSize == 32 ); + + // find the best cofactoring variables + nCBars = Kit_DsdCofactoring( pTruth, nVars, piCofVar, p->pPars->nVarsShared, 0 ); +// nCBars = 2; +// piCofVar[0] = 0; +// piCofVar[1] = 1; + + + // copy the function + Kit_TruthCopy( ppCofs[0][0], pTruth, nVars ); + + // decompose w.r.t. these variables + for ( k = 0; k < nCBars; k++ ) + { + nSize = (1 << k); + for ( i = 0; i < nSize; i++ ) + { + Kit_TruthCofactor0New( ppCofs[k+1][2*i+0], ppCofs[k][i], nVars, piCofVar[k] ); + Kit_TruthCofactor1New( ppCofs[k+1][2*i+1], ppCofs[k][i], nVars, piCofVar[k] ); + } + } + nSize = (1 << nCBars); + // compute DSD networks + for ( i = 0; i < nSize; i++ ) + { + ppNtks[i] = Kit_DsdDecompose( ppCofs[nCBars][i], nVars ); + ppNtks[i] = Kit_DsdExpand( pTemp = ppNtks[i] ); + Kit_DsdNtkFree( pTemp ); + if ( fVerbose ) + { + printf( "Cof%d%d: ", nCBars, i ); + Kit_DsdPrint( stdout, ppNtks[i] ); + } + } + + // compute variable frequences + for ( i = 0; i < nSize; i++ ) + { + uSupport = Kit_TruthSupport( ppCofs[nCBars][i], nVars ); + for ( k = 0; k < nVars; k++ ) + if ( uSupport & (1<pSupps[0] <= 0xFFFF ); + // undec nodes should be rotated in such a way that the first input has as many shared inputs as possible + Kit_DsdRotate( ppNtks[i], pFreqs ); + // print the resulting networks + if ( fVerbose ) + { + printf( "Cof%d%d: ", nCBars, i ); + Kit_DsdPrint( stdout, ppNtks[i] ); + } + } + + for ( i = 0; i < nSize; i++ ) + { + // collect the roots + pRoot = Kit_DsdNtkRoot(ppNtks[i]); + if ( pRoot->Type == KIT_DSD_CONST1 ) + piLits[i] = Kit_DsdLitIsCompl(ppNtks[i]->Root)? -2: -1; + else if ( pRoot->Type == KIT_DSD_VAR ) + piLits[i] = Kit_DsdLitNotCond( pRoot->pFans[0], Kit_DsdLitIsCompl(ppNtks[i]->Root) ); + else + piLits[i] = ppNtks[i]->Root; + } + + + // recursively construct AIG for mapping + p->fCofactoring = 1; + pResult = Lpk_MapTreeMulti_rec( p, ppNtks, piLits, piCofVar, nCBars, ppLeaves, nVars, pPrios ); + p->fCofactoring = 0; + + if ( fVerbose ) + printf( "\n" ); + + // verify the transformations + nSize = (1 << nCBars); + for ( i = 0; i < nSize; i++ ) + Kit_DsdTruth( ppNtks[i], ppCofs[nCBars][i] ); + // mux the truth tables + for ( k = nCBars-1; k >= 0; k-- ) + { + nSize = (1 << k); + for ( i = 0; i < nSize; i++ ) + Kit_TruthMuxVar( ppCofs[k][i], ppCofs[k+1][2*i+0], ppCofs[k+1][2*i+1], nVars, piCofVar[k] ); + } + if ( !Extra_TruthIsEqual( pTruth, ppCofs[0][0], nVars ) ) + printf( "Verification failed.\n" ); + + + // free the networks + for ( i = 0; i < 8; i++ ) + if ( ppNtks[i] ) + Kit_DsdNtkFree( ppNtks[i] ); + free( ppCofs[0][0] ); + + return pResult; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/lpk/lpkMux.c b/abc_with_bb_support/src/opt/lpk/lpkMux.c new file mode 100644 index 000000000..a762a3073 --- /dev/null +++ b/abc_with_bb_support/src/opt/lpk/lpkMux.c @@ -0,0 +1,244 @@ +/**CFile**************************************************************** + + FileName [lpkMux.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Fast Boolean matching for LUT structures.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: lpkMux.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "lpkInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Find the best cofactoring variable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Lpk_MapTreeBestCofVar( Lpk_Man_t * p, unsigned * pTruth, int nVars, unsigned * pCof0, unsigned * pCof1 ) +{ + int i, iBestVar, nSuppSizeCur0, nSuppSizeCur1, nSuppSizeCur, nSuppSizeMin; + // iterate through variables + iBestVar = -1; + nSuppSizeMin = KIT_INFINITY; + for ( i = 0; i < nVars; i++ ) + { + // cofactor the functiona and get support sizes + Kit_TruthCofactor0New( pCof0, pTruth, nVars, i ); + Kit_TruthCofactor1New( pCof1, pTruth, nVars, i ); + nSuppSizeCur0 = Kit_TruthSupportSize( pCof0, nVars ); + nSuppSizeCur1 = Kit_TruthSupportSize( pCof1, nVars ); + nSuppSizeCur = nSuppSizeCur0 + nSuppSizeCur1; + // skip cofactoring that goes above the limit + if ( nSuppSizeCur0 > p->pPars->nLutSize || nSuppSizeCur1 > p->pPars->nLutSize ) + continue; + // compare this variable with other variables + if ( nSuppSizeMin > nSuppSizeCur ) + { + nSuppSizeMin = nSuppSizeCur; + iBestVar = i; + } + } + // cofactor w.r.t. this variable + if ( iBestVar != -1 ) + { + Kit_TruthCofactor0New( pCof0, pTruth, nVars, iBestVar ); + Kit_TruthCofactor1New( pCof1, pTruth, nVars, iBestVar ); + } + return iBestVar; +} + +/**Function************************************************************* + + Synopsis [Maps the function by the best cofactoring.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +If_Obj_t * Lpk_MapTreeMux_rec( Lpk_Man_t * p, unsigned * pTruth, int nVars, If_Obj_t ** ppLeaves ) +{ + unsigned * pCof0 = Vec_PtrEntry( p->vTtNodes, 0 ); + unsigned * pCof1 = Vec_PtrEntry( p->vTtNodes, 1 ); + If_Obj_t * pObj0, * pObj1; + Kit_DsdNtk_t * ppNtks[2]; + int iBestVar; + assert( nVars > 3 ); + p->fCalledOnce = 1; + // cofactor w.r.t. the best variable + iBestVar = Lpk_MapTreeBestCofVar( p, pTruth, nVars, pCof0, pCof1 ); + if ( iBestVar == -1 ) + return NULL; + // decompose the functions + ppNtks[0] = Kit_DsdDecompose( pCof0, nVars ); + ppNtks[1] = Kit_DsdDecompose( pCof1, nVars ); + if ( p->pPars->fVeryVerbose ) + { + printf( "Cofactoring w.r.t. var %c (%d -> %d+%d supp vars):\n", + 'a'+iBestVar, nVars, Kit_TruthSupportSize(pCof0, nVars), Kit_TruthSupportSize(pCof1, nVars) ); + Kit_DsdPrintExpanded( ppNtks[0] ); + Kit_DsdPrintExpanded( ppNtks[1] ); + } + // map the DSD structures + pObj0 = Lpk_MapTree_rec( p, ppNtks[0], ppLeaves, ppNtks[0]->Root, NULL ); + pObj1 = Lpk_MapTree_rec( p, ppNtks[1], ppLeaves, ppNtks[1]->Root, NULL ); + Kit_DsdNtkFree( ppNtks[0] ); + Kit_DsdNtkFree( ppNtks[1] ); + return If_ManCreateMux( p->pIfMan, pObj0, pObj1, ppLeaves[iBestVar] ); +} + + + +/**Function************************************************************* + + Synopsis [Implements support-reducing decomposition.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +If_Obj_t * Lpk_MapSuppRedDec_rec( Lpk_Man_t * p, unsigned * pTruth, int nVars, If_Obj_t ** ppLeaves ) +{ + Kit_DsdNtk_t * pNtkDec, * pNtkComp, * ppNtks[2], * pTemp; + If_Obj_t * pObjNew; + unsigned * pCof0 = Vec_PtrEntry( p->vTtNodes, 0 ); + unsigned * pCof1 = Vec_PtrEntry( p->vTtNodes, 1 ); + unsigned * pDec0 = Vec_PtrEntry( p->vTtNodes, 2 ); + unsigned * pDec1 = Vec_PtrEntry( p->vTtNodes, 3 ); + unsigned * pDec = Vec_PtrEntry( p->vTtNodes, 4 ); + unsigned * pCo00 = Vec_PtrEntry( p->vTtNodes, 5 ); + unsigned * pCo01 = Vec_PtrEntry( p->vTtNodes, 6 ); + unsigned * pCo10 = Vec_PtrEntry( p->vTtNodes, 7 ); + unsigned * pCo11 = Vec_PtrEntry( p->vTtNodes, 8 ); + unsigned * pCo0 = Vec_PtrEntry( p->vTtNodes, 9 ); + unsigned * pCo1 = Vec_PtrEntry( p->vTtNodes, 10 ); + unsigned * pCo = Vec_PtrEntry( p->vTtNodes, 11 ); + int TrueMint0, TrueMint1, FalseMint0, FalseMint1; + int uSubsets, uSubset0, uSubset1, iVar, iVarReused, i; + + // determine if supp-red decomposition exists + uSubsets = Lpk_MapSuppRedDecSelect( p, pTruth, nVars, &iVar, &iVarReused ); + if ( uSubsets == 0 ) + return NULL; + p->nCalledSRed++; + + // get the cofactors + Kit_TruthCofactor0New( pCof0, pTruth, nVars, iVar ); + Kit_TruthCofactor1New( pCof1, pTruth, nVars, iVar ); + + // get the bound sets + uSubset0 = uSubsets & 0xFFFF; + uSubset1 = uSubsets >> 16; + + // compute the decomposed functions + ppNtks[0] = Kit_DsdDecompose( pCof0, nVars ); + ppNtks[1] = Kit_DsdDecompose( pCof1, nVars ); + ppNtks[0] = Kit_DsdExpand( pTemp = ppNtks[0] ); Kit_DsdNtkFree( pTemp ); + ppNtks[1] = Kit_DsdExpand( pTemp = ppNtks[1] ); Kit_DsdNtkFree( pTemp ); + Kit_DsdTruthPartial( p->pDsdMan, ppNtks[0], pDec0, uSubset0 ); + Kit_DsdTruthPartial( p->pDsdMan, ppNtks[1], pDec1, uSubset1 ); + Kit_DsdNtkFree( ppNtks[0] ); + Kit_DsdNtkFree( ppNtks[1] ); +//Kit_DsdPrintFromTruth( pDec0, nVars ); +//Kit_DsdPrintFromTruth( pDec1, nVars ); + // get the decomposed function + Kit_TruthMuxVar( pDec, pDec0, pDec1, nVars, iVar ); + + // find any true assignments of the decomposed functions + TrueMint0 = Kit_TruthFindFirstBit( pDec0, nVars ); + TrueMint1 = Kit_TruthFindFirstBit( pDec1, nVars ); + assert( TrueMint0 >= 0 && TrueMint1 >= 0 ); + // find any false assignments of the decomposed functions + FalseMint0 = Kit_TruthFindFirstZero( pDec0, nVars ); + FalseMint1 = Kit_TruthFindFirstZero( pDec1, nVars ); + assert( FalseMint0 >= 0 && FalseMint1 >= 0 ); + + // cofactor the cofactors according to these minterms + Kit_TruthCopy( pCo00, pCof0, nVars ); + Kit_TruthCopy( pCo01, pCof0, nVars ); + for ( i = 0; i < nVars; i++ ) + if ( uSubset0 & (1 << i) ) + { + if ( FalseMint0 & (1 << i) ) + Kit_TruthCofactor1( pCo00, nVars, i ); + else + Kit_TruthCofactor0( pCo00, nVars, i ); + if ( TrueMint0 & (1 << i) ) + Kit_TruthCofactor1( pCo01, nVars, i ); + else + Kit_TruthCofactor0( pCo01, nVars, i ); + } + Kit_TruthCopy( pCo10, pCof1, nVars ); + Kit_TruthCopy( pCo11, pCof1, nVars ); + for ( i = 0; i < nVars; i++ ) + if ( uSubset1 & (1 << i) ) + { + if ( FalseMint1 & (1 << i) ) + Kit_TruthCofactor1( pCo10, nVars, i ); + else + Kit_TruthCofactor0( pCo10, nVars, i ); + if ( TrueMint1 & (1 << i) ) + Kit_TruthCofactor1( pCo11, nVars, i ); + else + Kit_TruthCofactor0( pCo11, nVars, i ); + } + + // derive the functions by composing them with the new variable (iVarReused) + Kit_TruthMuxVar( pCo0, pCo00, pCo01, nVars, iVarReused ); + Kit_TruthMuxVar( pCo1, pCo10, pCo11, nVars, iVarReused ); +//Kit_DsdPrintFromTruth( pCo0, nVars ); +//Kit_DsdPrintFromTruth( pCo1, nVars ); + // derive the composition function + Kit_TruthMuxVar( pCo , pCo0 , pCo1 , nVars, iVar ); + + // process the decomposed function + pNtkDec = Kit_DsdDecompose( pDec, nVars ); + pNtkComp = Kit_DsdDecompose( pCo, nVars ); +//Kit_DsdPrint( stdout, pNtkDec ); +//Kit_DsdPrint( stdout, pNtkComp ); +//printf( "cofactored variable %c\n", 'a' + iVar ); +//printf( "reused variable %c\n", 'a' + iVarReused ); + + ppLeaves[iVarReused] = Lpk_MapTree_rec( p, pNtkDec, ppLeaves, pNtkDec->Root, NULL ); + pObjNew = Lpk_MapTree_rec( p, pNtkComp, ppLeaves, pNtkComp->Root, NULL ); + + Kit_DsdNtkFree( pNtkDec ); + Kit_DsdNtkFree( pNtkComp ); + return pObjNew; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/lpk/lpkSets.c b/abc_with_bb_support/src/opt/lpk/lpkSets.c new file mode 100644 index 000000000..a30a86c38 --- /dev/null +++ b/abc_with_bb_support/src/opt/lpk/lpkSets.c @@ -0,0 +1,440 @@ +/**CFile**************************************************************** + + FileName [lpkSets.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Fast Boolean matching for LUT structures.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: lpkSets.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "lpkInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Lpk_Set_t_ Lpk_Set_t; +struct Lpk_Set_t_ +{ + char iVar; // the cofactoring variable + char Over; // the overlap in supports + char SRed; // the support reduction + char Size; // the size of the boundset + unsigned uSubset0; // the first subset (with removed) + unsigned uSubset1; // the second subset (with removed) +}; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Recursively computes decomposable subsets.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Lpk_ComputeSets_rec( Kit_DsdNtk_t * p, int iLit, Vec_Int_t * vSets ) +{ + unsigned i, iLitFanin, uSupport, uSuppCur; + Kit_DsdObj_t * pObj; + // consider the case of simple gate + pObj = Kit_DsdNtkObj( p, Kit_DsdLit2Var(iLit) ); + if ( pObj == NULL ) + return (1 << Kit_DsdLit2Var(iLit)); + if ( pObj->Type == KIT_DSD_AND || pObj->Type == KIT_DSD_XOR ) + { + unsigned uSupps[16], Limit, s; + uSupport = 0; + Kit_DsdObjForEachFanin( p, pObj, iLitFanin, i ) + { + uSupps[i] = Lpk_ComputeSets_rec( p, iLitFanin, vSets ); + uSupport |= uSupps[i]; + } + // create all subsets, except empty and full + Limit = (1 << pObj->nFans) - 1; + for ( s = 1; s < Limit; s++ ) + { + uSuppCur = 0; + for ( i = 0; i < pObj->nFans; i++ ) + if ( s & (1 << i) ) + uSuppCur |= uSupps[i]; + Vec_IntPush( vSets, uSuppCur ); + } + return uSupport; + } + assert( pObj->Type == KIT_DSD_PRIME ); + // get the cumulative support of all fanins + uSupport = 0; + Kit_DsdObjForEachFanin( p, pObj, iLitFanin, i ) + { + uSuppCur = Lpk_ComputeSets_rec( p, iLitFanin, vSets ); + uSupport |= uSuppCur; + Vec_IntPush( vSets, uSuppCur ); + } + return uSupport; +} + +/**Function************************************************************* + + Synopsis [Computes the set of subsets of decomposable variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Lpk_ComputeSets( Kit_DsdNtk_t * p, Vec_Int_t * vSets ) +{ + unsigned uSupport, Entry; + int Number, i; + assert( p->nVars <= 16 ); + Vec_IntClear( vSets ); + Vec_IntPush( vSets, 0 ); + if ( Kit_DsdNtkRoot(p)->Type == KIT_DSD_CONST1 ) + return 0; + if ( Kit_DsdNtkRoot(p)->Type == KIT_DSD_VAR ) + { + uSupport = ( 1 << Kit_DsdLit2Var(Kit_DsdNtkRoot(p)->pFans[0]) ); + Vec_IntPush( vSets, uSupport ); + return uSupport; + } + uSupport = Lpk_ComputeSets_rec( p, p->Root, vSets ); + assert( (uSupport & 0xFFFF0000) == 0 ); + Vec_IntPush( vSets, uSupport ); + // set the remaining variables + Vec_IntForEachEntry( vSets, Number, i ) + { + Entry = Number; + Vec_IntWriteEntry( vSets, i, Entry | ((uSupport & ~Entry) << 16) ); + } + return uSupport; +} + +/**Function************************************************************* + + Synopsis [Prints the sets of subsets.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Lpk_PrintSetOne( int uSupport ) +{ + unsigned k; + for ( k = 0; k < 16; k++ ) + if ( uSupport & (1<> 16) ); + // get the support reduction + nSuppRed = nSuppSize - 1 - nSuppOver; + // only consider support-reducing subsets + if ( nSuppRed <= 0 ) + continue; + // check if this support is already used + if ( TravId[uSupp] < nTravId ) + { + Used[nUsed++] = uSupp; + + TravId[uSupp] = nTravId; + SRed[uSupp] = nSuppRed; + Over[uSupp] = nSuppOver; + Parents[uSupp] = (k << 16) | i; + } + else if ( TravId[uSupp] == nTravId && SRed[uSupp] < nSuppRed ) + { + TravId[uSupp] = nTravId; + SRed[uSupp] = nSuppRed; + Over[uSupp] = nSuppOver; + Parents[uSupp] = (k << 16) | i; + } + } + + // find the minimum overlap + nMinOver = 1000; + for ( s = 0; s < nUsed; s++ ) + if ( nMinOver > Over[Used[s]] ) + nMinOver = Over[Used[s]]; + + + // collect the accumulated ones + for ( s = 0; s < nUsed; s++ ) + if ( Over[Used[s]] == nMinOver ) + { + // save the entry + if ( *pSize == nSizeLimit ) + return; + pEntry = pStore + (*pSize)++; + + i = Parents[Used[s]] & 0xFFFF; + k = Parents[Used[s]] >> 16; + + pEntry->uSubset0 = Vec_IntEntry(vSets0, i); + pEntry->uSubset1 = Vec_IntEntry(vSets1, k); + Entry = pEntry->uSubset0 | pEntry->uSubset1; + + // record the cofactoring variable + pEntry->iVar = iCofVar; + // set the bound set size + pEntry->Size = Kit_WordCountOnes( Entry & 0xFFFF ); + // get the number of overlapping vars + pEntry->Over = Kit_WordCountOnes( Entry & (Entry >> 16) ); + // get the support reduction + pEntry->SRed = pEntry->Size - 1 - pEntry->Over; + assert( pEntry->SRed > 0 ); + } +} + +/**Function************************************************************* + + Synopsis [Prints one set.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Lpk_MapSuppPrintSet( Lpk_Set_t * pSet, int i ) +{ + unsigned Entry; + Entry = pSet->uSubset0 | pSet->uSubset1; + printf( "%2d : ", i ); + printf( "Var = %c ", 'a' + pSet->iVar ); + printf( "Size = %2d ", pSet->Size ); + printf( "Over = %2d ", pSet->Over ); + printf( "SRed = %2d ", pSet->SRed ); + Lpk_PrintSetOne( Entry ); + printf( " " ); + Lpk_PrintSetOne( Entry >> 16 ); + printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [Evaluates the cofactors.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned Lpk_MapSuppRedDecSelect( Lpk_Man_t * p, unsigned * pTruth, int nVars, int * piVar, int * piVarReused ) +{ + static int nStoreSize = 256; + static Lpk_Set_t pStore[256], * pSet, * pSetBest; + Kit_DsdNtk_t * ppNtks[2], * pTemp; + Vec_Int_t * vSets0 = p->vSets[0]; + Vec_Int_t * vSets1 = p->vSets[1]; + unsigned * pCof0 = Vec_PtrEntry( p->vTtNodes, 0 ); + unsigned * pCof1 = Vec_PtrEntry( p->vTtNodes, 1 ); + int nSets, i, SizeMax;//, SRedMax; + unsigned Entry; + int fVerbose = p->pPars->fVeryVerbose; +// int fVerbose = 0; + + // collect decomposable subsets for each pair of cofactors + if ( fVerbose ) + { + printf( "\nExploring support-reducing bound-sets of function:\n" ); + Kit_DsdPrintFromTruth( pTruth, nVars ); + } + nSets = 0; + for ( i = 0; i < nVars; i++ ) + { + if ( fVerbose ) + printf( "Evaluating variable %c:\n", 'a'+i ); + // evaluate the cofactor pair + Kit_TruthCofactor0New( pCof0, pTruth, nVars, i ); + Kit_TruthCofactor1New( pCof1, pTruth, nVars, i ); + // decompose and expand + ppNtks[0] = Kit_DsdDecompose( pCof0, nVars ); + ppNtks[1] = Kit_DsdDecompose( pCof1, nVars ); + ppNtks[0] = Kit_DsdExpand( pTemp = ppNtks[0] ); Kit_DsdNtkFree( pTemp ); + ppNtks[1] = Kit_DsdExpand( pTemp = ppNtks[1] ); Kit_DsdNtkFree( pTemp ); + if ( fVerbose ) + Kit_DsdPrint( stdout, ppNtks[0] ); + if ( fVerbose ) + Kit_DsdPrint( stdout, ppNtks[1] ); + // compute subsets + Lpk_ComputeSets( ppNtks[0], vSets0 ); + Lpk_ComputeSets( ppNtks[1], vSets1 ); + // print subsets + if ( fVerbose ) + Lpk_PrintSets( vSets0 ); + if ( fVerbose ) + Lpk_PrintSets( vSets1 ); + // free the networks + Kit_DsdNtkFree( ppNtks[0] ); + Kit_DsdNtkFree( ppNtks[1] ); + // evaluate the pair + Lpk_ComposeSets( vSets0, vSets1, nVars, i, pStore, &nSets, nStoreSize ); + } + + // print the results + if ( fVerbose ) + printf( "\n" ); + if ( fVerbose ) + for ( i = 0; i < nSets; i++ ) + Lpk_MapSuppPrintSet( pStore + i, i ); + + // choose the best subset + SizeMax = 0; + pSetBest = NULL; + for ( i = 0; i < nSets; i++ ) + { + pSet = pStore + i; + if ( pSet->Size > p->pPars->nLutSize - 1 ) + continue; + if ( SizeMax < pSet->Size ) + { + pSetBest = pSet; + SizeMax = pSet->Size; + } + } +/* + // if the best is not choosen, select the one with largest reduction + SRedMax = 0; + if ( pSetBest == NULL ) + { + for ( i = 0; i < nSets; i++ ) + { + pSet = pStore + i; + if ( SRedMax < pSet->SRed ) + { + pSetBest = pSet; + SRedMax = pSet->SRed; + } + } + } +*/ + if ( pSetBest == NULL ) + { + if ( fVerbose ) + printf( "Could not select a subset.\n" ); + return 0; + } + else + { + if ( fVerbose ) + printf( "Selected the following subset:\n" ); + if ( fVerbose ) + Lpk_MapSuppPrintSet( pSetBest, pSetBest - pStore ); + } + + // prepare the return result + // get the remaining variables + Entry = ((pSetBest->uSubset0 >> 16) | (pSetBest->uSubset1 >> 16)); + // get the variables to be removed + Entry = Kit_BitMask(nVars) & ~(1<iVar) & ~Entry; + // make sure there are some - otherwise it is not supp-red + assert( Entry ); + // remember the first such variable + *piVarReused = Kit_WordFindFirstBit( Entry ); + *piVar = pSetBest->iVar; + return (pSetBest->uSubset1 << 16) | (pSetBest->uSubset0 & 0xFFFF); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/lpk/lpk_.c b/abc_with_bb_support/src/opt/lpk/lpk_.c new file mode 100644 index 000000000..14f6c6065 --- /dev/null +++ b/abc_with_bb_support/src/opt/lpk/lpk_.c @@ -0,0 +1,48 @@ +/**CFile**************************************************************** + + FileName [lpk_.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Fast Boolean matching for LUT structures.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - April 28, 2007.] + + Revision [$Id: lpk_.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "lpkInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/lpk/module.make b/abc_with_bb_support/src/opt/lpk/module.make new file mode 100644 index 000000000..55e3541d9 --- /dev/null +++ b/abc_with_bb_support/src/opt/lpk/module.make @@ -0,0 +1,7 @@ +SRC += src/opt/lpk/lpkCore.c \ + src/opt/lpk/lpkCut.c \ + src/opt/lpk/lpkMan.c \ + src/opt/lpk/lpkMap.c \ + src/opt/lpk/lpkMulti.c \ + src/opt/lpk/lpkMux.c \ + src/opt/lpk/lpkSets.c diff --git a/abc_with_bb_support/src/opt/res/module.make b/abc_with_bb_support/src/opt/res/module.make new file mode 100644 index 000000000..45efb5f57 --- /dev/null +++ b/abc_with_bb_support/src/opt/res/module.make @@ -0,0 +1,7 @@ +SRC += src/opt/res/resCore.c \ + src/opt/res/resDivs.c \ + src/opt/res/resFilter.c \ + src/opt/res/resSat.c \ + src/opt/res/resSim.c \ + src/opt/res/resStrash.c \ + src/opt/res/resWin.c diff --git a/abc_with_bb_support/src/opt/res/res.h b/abc_with_bb_support/src/opt/res/res.h new file mode 100644 index 000000000..6d4b34d16 --- /dev/null +++ b/abc_with_bb_support/src/opt/res/res.h @@ -0,0 +1,75 @@ +/**CFile**************************************************************** + + FileName [res.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resynthesis package.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 15, 2007.] + + Revision [$Id: res.h,v 1.00 2007/01/15 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __RES_H__ +#define __RES_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Res_Par_t_ Res_Par_t; +struct Res_Par_t_ +{ + // general parameters + int nWindow; // window size + int nGrowthLevel; // the maximum allowed growth in level after one iteration of resynthesis + int nSimWords; // the number of simulation words + int nCands; // the number of candidates to try + int fArea; // performs optimization for area + int fDelay; // performs optimization for delay + int fVerbose; // enable basic stats + int fVeryVerbose; // enable detailed stats +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== resCore.c ==========================================================*/ +extern int Abc_NtkResynthesize( Abc_Ntk_t * pNtk, Res_Par_t * pPars ); + + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/opt/res/resCore.c b/abc_with_bb_support/src/opt/res/resCore.c new file mode 100644 index 000000000..d9b4dbdf4 --- /dev/null +++ b/abc_with_bb_support/src/opt/res/resCore.c @@ -0,0 +1,415 @@ +/**CFile**************************************************************** + + FileName [resCore.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resynthesis package.] + + Synopsis [Top-level resynthesis procedure.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 15, 2007.] + + Revision [$Id: resCore.c,v 1.00 2007/01/15 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "resInt.h" +#include "kit.h" +#include "satStore.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Res_Man_t_ Res_Man_t; +struct Res_Man_t_ +{ + // general parameters + Res_Par_t * pPars; + // specialized manager + Res_Win_t * pWin; // windowing manager + Abc_Ntk_t * pAig; // the strashed window + Res_Sim_t * pSim; // simulation manager + Sto_Man_t * pCnf; // the CNF of the SAT problem + Int_Man_t * pMan; // interpolation manager; + Vec_Int_t * vMem; // memory for intermediate SOPs + Vec_Vec_t * vResubs; // resubstitution candidates of the AIG + Vec_Vec_t * vResubsW; // resubstitution candidates of the window + Vec_Vec_t * vLevels; // levelized structure for updating + // statistics + int nWins; // the number of windows tried + int nWinNodes; // the total number of window nodes + int nDivNodes; // the total number of divisors + int nWinsTriv; // the total number of trivial windows + int nWinsUsed; // the total number of useful windows (with at least one candidate) + int nConstsUsed; // the total number of constant nodes under ODC + int nCandSets; // the total number of candidates + int nProvedSets; // the total number of proved groups + int nSimEmpty; // the empty simulation info + int nTotalNets; // the total number of nets + int nTotalNodes; // the total number of nodess + int nTotalNets2; // the total number of nets + int nTotalNodes2; // the total number of nodess + // runtime + int timeWin; // windowing + int timeDiv; // divisors + int timeAig; // strashing + int timeSim; // simulation + int timeCand; // resubstitution candidates + int timeSatTotal; // SAT solving total + int timeSatSat; // SAT solving (sat calls) + int timeSatUnsat; // SAT solving (unsat calls) + int timeSatSim; // SAT solving (simulation) + int timeInt; // interpolation + int timeUpd; // updating + int timeTotal; // total runtime +}; + +extern Hop_Obj_t * Kit_GraphToHop( Hop_Man_t * pMan, Kit_Graph_t * pGraph ); + +extern int s_ResynTime; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocate resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Res_Man_t * Res_ManAlloc( Res_Par_t * pPars ) +{ + Res_Man_t * p; + p = ALLOC( Res_Man_t, 1 ); + memset( p, 0, sizeof(Res_Man_t) ); + assert( pPars->nWindow > 0 && pPars->nWindow < 100 ); + assert( pPars->nCands > 0 && pPars->nCands < 100 ); + p->pPars = pPars; + p->pWin = Res_WinAlloc(); + p->pSim = Res_SimAlloc( pPars->nSimWords ); + p->pMan = Int_ManAlloc( 512 ); + p->vMem = Vec_IntAlloc( 0 ); + p->vResubs = Vec_VecStart( pPars->nCands ); + p->vResubsW = Vec_VecStart( pPars->nCands ); + p->vLevels = Vec_VecStart( 32 ); + return p; +} + +/**Function************************************************************* + + Synopsis [Deallocate resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_ManFree( Res_Man_t * p ) +{ + if ( p->pPars->fVerbose ) + { + printf( "Reduction in nodes = %5d. (%.2f %%) ", + p->nTotalNodes-p->nTotalNodes2, + 100.0*(p->nTotalNodes-p->nTotalNodes2)/p->nTotalNodes ); + printf( "Reduction in edges = %5d. (%.2f %%) ", + p->nTotalNets-p->nTotalNets2, + 100.0*(p->nTotalNets-p->nTotalNets2)/p->nTotalNets ); + printf( "\n" ); + + printf( "Winds = %d. ", p->nWins ); + printf( "Nodes = %d. (Ave = %5.1f) ", p->nWinNodes, 1.0*p->nWinNodes/p->nWins ); + printf( "Divs = %d. (Ave = %5.1f) ", p->nDivNodes, 1.0*p->nDivNodes/p->nWins ); + printf( "\n" ); + printf( "WinsTriv = %d. ", p->nWinsTriv ); + printf( "SimsEmpt = %d. ", p->nSimEmpty ); + printf( "Const = %d. ", p->nConstsUsed ); + printf( "WindUsed = %d. ", p->nWinsUsed ); + printf( "Cands = %d. ", p->nCandSets ); + printf( "Proved = %d.", p->nProvedSets ); + printf( "\n" ); + + PRTP( "Windowing ", p->timeWin, p->timeTotal ); + PRTP( "Divisors ", p->timeDiv, p->timeTotal ); + PRTP( "Strashing ", p->timeAig, p->timeTotal ); + PRTP( "Simulation ", p->timeSim, p->timeTotal ); + PRTP( "Candidates ", p->timeCand, p->timeTotal ); + PRTP( "SAT solver ", p->timeSatTotal, p->timeTotal ); + PRTP( " sat ", p->timeSatSat, p->timeTotal ); + PRTP( " unsat ", p->timeSatUnsat, p->timeTotal ); + PRTP( " simul ", p->timeSatSim, p->timeTotal ); + PRTP( "Interpol ", p->timeInt, p->timeTotal ); + PRTP( "Undating ", p->timeUpd, p->timeTotal ); + PRTP( "TOTAL ", p->timeTotal, p->timeTotal ); + } + Res_WinFree( p->pWin ); + if ( p->pAig ) Abc_NtkDelete( p->pAig ); + Res_SimFree( p->pSim ); + if ( p->pCnf ) Sto_ManFree( p->pCnf ); + Int_ManFree( p->pMan ); + Vec_IntFree( p->vMem ); + Vec_VecFree( p->vResubs ); + Vec_VecFree( p->vResubsW ); + Vec_VecFree( p->vLevels ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Incrementally updates level of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_UpdateNetwork( Abc_Obj_t * pObj, Vec_Ptr_t * vFanins, Hop_Obj_t * pFunc, Vec_Vec_t * vLevels ) +{ + Abc_Obj_t * pObjNew, * pFanin; + int k; + // create the new node + pObjNew = Abc_NtkCreateNode( pObj->pNtk ); + pObjNew->pData = pFunc; + Vec_PtrForEachEntry( vFanins, pFanin, k ) + Abc_ObjAddFanin( pObjNew, pFanin ); + // replace the old node by the new node + // update the level of the node + Abc_NtkUpdate( pObj, pObjNew, vLevels ); +} + +/**Function************************************************************* + + Synopsis [Entrace into the resynthesis package.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkResynthesize( Abc_Ntk_t * pNtk, Res_Par_t * pPars ) +{ + ProgressBar * pProgress; + Res_Man_t * p; + Abc_Obj_t * pObj; + Hop_Obj_t * pFunc; + Kit_Graph_t * pGraph; + Vec_Ptr_t * vFanins; + unsigned * puTruth; + int i, k, RetValue, nNodesOld, nFanins, nFaninsMax; + int clk, clkTotal = clock(); + + // start the manager + p = Res_ManAlloc( pPars ); + p->nTotalNets = Abc_NtkGetTotalFanins(pNtk); + p->nTotalNodes = Abc_NtkNodeNum(pNtk); + nFaninsMax = Abc_NtkGetFaninMax(pNtk); + + // perform the network sweep + Abc_NtkSweep( pNtk, 0 ); + + // convert into the AIG + if ( !Abc_NtkToAig(pNtk) ) + { + fprintf( stdout, "Converting to BDD has failed.\n" ); + Res_ManFree( p ); + return 0; + } + assert( Abc_NtkHasAig(pNtk) ); + + // set the number of levels + Abc_NtkLevel( pNtk ); + Abc_NtkStartReverseLevels( pNtk, pPars->nGrowthLevel ); + + // try resynthesizing nodes in the topological order + nNodesOld = Abc_NtkObjNumMax(pNtk); + pProgress = Extra_ProgressBarStart( stdout, nNodesOld ); + Abc_NtkForEachObj( pNtk, pObj, i ) + { + Extra_ProgressBarUpdate( pProgress, i, NULL ); + if ( !Abc_ObjIsNode(pObj) ) + continue; + if ( pObj->Id > nNodesOld ) + break; + + // create the window for this node +clk = clock(); + RetValue = Res_WinCompute( pObj, p->pPars->nWindow/10, p->pPars->nWindow%10, p->pWin ); +p->timeWin += clock() - clk; + if ( !RetValue ) + continue; + p->nWinsTriv += Res_WinIsTrivial( p->pWin ); + + if ( p->pPars->fVeryVerbose ) + { + printf( "%5d (lev=%2d) : ", pObj->Id, pObj->Level ); + printf( "Win = %3d/%3d/%4d/%3d ", + Vec_PtrSize(p->pWin->vLeaves), + Vec_PtrSize(p->pWin->vBranches), + Vec_PtrSize(p->pWin->vNodes), + Vec_PtrSize(p->pWin->vRoots) ); + } + + // collect the divisors +clk = clock(); + Res_WinDivisors( p->pWin, Abc_ObjRequiredLevel(pObj) - 1 ); +p->timeDiv += clock() - clk; + + p->nWins++; + p->nWinNodes += Vec_PtrSize(p->pWin->vNodes); + p->nDivNodes += Vec_PtrSize( p->pWin->vDivs); + + if ( p->pPars->fVeryVerbose ) + { + printf( "D = %3d ", Vec_PtrSize(p->pWin->vDivs) ); + printf( "D+ = %3d ", p->pWin->nDivsPlus ); + } + + // create the AIG for the window +clk = clock(); + if ( p->pAig ) Abc_NtkDelete( p->pAig ); + p->pAig = Res_WndStrash( p->pWin ); +p->timeAig += clock() - clk; + + if ( p->pPars->fVeryVerbose ) + { + printf( "AIG = %4d ", Abc_NtkNodeNum(p->pAig) ); + printf( "\n" ); + } + + // prepare simulation info +clk = clock(); + RetValue = Res_SimPrepare( p->pSim, p->pAig, Vec_PtrSize(p->pWin->vLeaves), 0 ); //p->pPars->fVerbose ); +p->timeSim += clock() - clk; + if ( !RetValue ) + { + p->nSimEmpty++; + continue; + } + + // consider the case of constant node + if ( p->pSim->fConst0 || p->pSim->fConst1 ) + { + p->nConstsUsed++; + + pFunc = p->pSim->fConst1? Hop_ManConst1(pNtk->pManFunc) : Hop_ManConst0(pNtk->pManFunc); + vFanins = Vec_VecEntry( p->vResubsW, 0 ); + Vec_PtrClear( vFanins ); + Res_UpdateNetwork( pObj, vFanins, pFunc, p->vLevels ); + continue; + } + +// printf( " " ); + + // find resub candidates for the node +clk = clock(); + if ( p->pPars->fArea ) + RetValue = Res_FilterCandidates( p->pWin, p->pAig, p->pSim, p->vResubs, p->vResubsW, nFaninsMax, 1 ); + else + RetValue = Res_FilterCandidates( p->pWin, p->pAig, p->pSim, p->vResubs, p->vResubsW, nFaninsMax, 0 ); +p->timeCand += clock() - clk; + p->nCandSets += RetValue; + if ( RetValue == 0 ) + continue; + +// printf( "%d(%d) ", Vec_PtrSize(p->pWin->vDivs), RetValue ); + + p->nWinsUsed++; + + // iterate through candidate resubstitutions + Vec_VecForEachLevel( p->vResubs, vFanins, k ) + { + if ( Vec_PtrSize(vFanins) == 0 ) + break; + + // solve the SAT problem and get clauses +clk = clock(); + if ( p->pCnf ) Sto_ManFree( p->pCnf ); + p->pCnf = Res_SatProveUnsat( p->pAig, vFanins ); + if ( p->pCnf == NULL ) + { +p->timeSatSat += clock() - clk; +// printf( " Sat\n" ); +// printf( "-" ); + continue; + } +p->timeSatUnsat += clock() - clk; +// printf( "+" ); + + p->nProvedSets++; +// printf( " Unsat\n" ); +// continue; +// printf( "Proved %d.\n", k ); + + // write it into a file +// Sto_ManDumpClauses( p->pCnf, "trace.cnf" ); + + // interpolate the problem if it was UNSAT +clk = clock(); + nFanins = Int_ManInterpolate( p->pMan, p->pCnf, 0, &puTruth ); +p->timeInt += clock() - clk; + if ( nFanins != Vec_PtrSize(vFanins) - 2 ) + continue; + assert( puTruth ); +// Extra_PrintBinary( stdout, puTruth, 1 << nFanins ); printf( "\n" ); + + // transform interpolant into the AIG + pGraph = Kit_TruthToGraph( puTruth, nFanins, p->vMem ); + + // derive the AIG for the decomposition tree + pFunc = Kit_GraphToHop( pNtk->pManFunc, pGraph ); + Kit_GraphFree( pGraph ); + + // update the network +clk = clock(); + Res_UpdateNetwork( pObj, Vec_VecEntry(p->vResubsW, k), pFunc, p->vLevels ); +p->timeUpd += clock() - clk; + break; + } +// printf( "\n" ); + } + Extra_ProgressBarStop( pProgress ); + Abc_NtkStopReverseLevels( pNtk ); + +p->timeSatSim += p->pSim->timeSat; +p->timeSatTotal = p->timeSatSat + p->timeSatUnsat + p->timeSatSim; + + p->nTotalNets2 = Abc_NtkGetTotalFanins(pNtk); + p->nTotalNodes2 = Abc_NtkNodeNum(pNtk); + + // quit resubstitution manager +p->timeTotal = clock() - clkTotal; + Res_ManFree( p ); + +s_ResynTime += clock() - clkTotal; + // check the resulting network + if ( !Abc_NtkCheck( pNtk ) ) + { + fprintf( stdout, "Abc_NtkResynthesize(): Network check has failed.\n" ); + return 0; + } + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/res/resDivs.c b/abc_with_bb_support/src/opt/res/resDivs.c new file mode 100644 index 000000000..3a075db77 --- /dev/null +++ b/abc_with_bb_support/src/opt/res/resDivs.c @@ -0,0 +1,285 @@ +/**CFile**************************************************************** + + FileName [resDivs.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resynthesis package.] + + Synopsis [Collect divisors for the given window.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 15, 2007.] + + Revision [$Id: resDivs.c,v 1.00 2007/01/15 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "resInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Res_WinMarkTfi( Res_Win_t * p ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Adds candidate divisors of the node to its window.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_WinDivisors( Res_Win_t * p, int nLevDivMax ) +{ + Abc_Obj_t * pObj, * pFanout, * pFanin; + int k, f, m; + + // set the maximum level of the divisors + p->nLevDivMax = nLevDivMax; + + // mark the TFI with the current trav ID + Abc_NtkIncrementTravId( p->pNode->pNtk ); + Res_WinMarkTfi( p ); + + // mark with the current trav ID those nodes that should not be divisors: + // (1) the node and its TFO + // (2) the MFFC of the node + // (3) the node's fanins (these are treated as a special case) + Abc_NtkIncrementTravId( p->pNode->pNtk ); + Res_WinSweepLeafTfo_rec( p->pNode, p->nLevDivMax ); + Res_WinVisitMffc( p->pNode ); + Abc_ObjForEachFanin( p->pNode, pObj, k ) + Abc_NodeSetTravIdCurrent( pObj ); + + // at this point the nodes are marked with two trav IDs: + // nodes to be collected as divisors are marked with previous trav ID + // nodes to be avoided as divisors are marked with current trav ID + + // start collecting the divisors + Vec_PtrClear( p->vDivs ); + Vec_PtrForEachEntry( p->vLeaves, pObj, k ) + { + assert( (int)pObj->Level >= p->nLevLeafMin ); + if ( !Abc_NodeIsTravIdPrevious(pObj) ) + continue; + if ( (int)pObj->Level > p->nLevDivMax ) + continue; + Vec_PtrPush( p->vDivs, pObj ); + } + // add the internal nodes to the data structure + Vec_PtrForEachEntry( p->vNodes, pObj, k ) + { + if ( !Abc_NodeIsTravIdPrevious(pObj) ) + continue; + if ( (int)pObj->Level > p->nLevDivMax ) + continue; + Vec_PtrPush( p->vDivs, pObj ); + } + + // explore the fanouts of already collected divisors + p->nDivsPlus = 0; + Vec_PtrForEachEntry( p->vDivs, pObj, k ) + { + // consider fanouts of this node + Abc_ObjForEachFanout( pObj, pFanout, f ) + { + // stop if there are too many fanouts + if ( f > 20 ) + break; + // skip nodes that are already added + if ( Abc_NodeIsTravIdPrevious(pFanout) ) + continue; + // skip nodes in the TFO or in the MFFC of node + if ( Abc_NodeIsTravIdCurrent(pFanout) ) + continue; + // skip COs + if ( !Abc_ObjIsNode(pFanout) ) + continue; + // skip nodes with large level + if ( (int)pFanout->Level >= p->nLevDivMax ) + continue; + // skip nodes whose fanins are not divisors + Abc_ObjForEachFanin( pFanout, pFanin, m ) + if ( !Abc_NodeIsTravIdPrevious(pFanin) ) + break; + if ( m < Abc_ObjFaninNum(pFanout) ) + continue; + // add the node to the divisors + Vec_PtrPush( p->vDivs, pFanout ); + Vec_PtrPush( p->vNodes, pFanout ); + Abc_NodeSetTravIdPrevious( pFanout ); + p->nDivsPlus++; + } + } +/* + printf( "Node level = %d. ", Abc_ObjLevel(p->pNode) ); + Vec_PtrForEachEntryStart( p->vDivs, pObj, k, Vec_PtrSize(p->vDivs)-p->nDivsPlus ) + printf( "%d ", Abc_ObjLevel(pObj) ); + printf( "\n" ); +*/ +//printf( "%d ", p->nDivsPlus ); +} + +/**Function************************************************************* + + Synopsis [Marks the TFI cone of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_WinMarkTfi_rec( Res_Win_t * p, Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanin; + int i; + if ( Abc_NodeIsTravIdCurrent(pObj) ) + return; + Abc_NodeSetTravIdCurrent( pObj ); + assert( Abc_ObjIsNode(pObj) ); + // visit the fanins of the node + Abc_ObjForEachFanin( pObj, pFanin, i ) + Res_WinMarkTfi_rec( p, pFanin ); +} + +/**Function************************************************************* + + Synopsis [Marks the TFI cone of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_WinMarkTfi( Res_Win_t * p ) +{ + Abc_Obj_t * pObj; + int i; + // mark the leaves + Vec_PtrForEachEntry( p->vLeaves, pObj, i ) + Abc_NodeSetTravIdCurrent( pObj ); + // start from the node + Res_WinMarkTfi_rec( p, p->pNode ); +} + +/**Function************************************************************* + + Synopsis [Marks the TFO of the collected nodes up to the given level.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_WinSweepLeafTfo_rec( Abc_Obj_t * pObj, int nLevelLimit ) +{ + Abc_Obj_t * pFanout; + int i; + if ( Abc_ObjIsCo(pObj) || (int)pObj->Level > nLevelLimit ) + return; + if ( Abc_NodeIsTravIdCurrent(pObj) ) + return; + Abc_NodeSetTravIdCurrent( pObj ); + Abc_ObjForEachFanout( pObj, pFanout, i ) + Res_WinSweepLeafTfo_rec( pFanout, nLevelLimit ); +} + +/**Function************************************************************* + + Synopsis [Dereferences the node's MFFC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Res_NodeDeref_rec( Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pFanin; + int i, Counter = 1; + if ( Abc_ObjIsCi(pNode) ) + return 0; + Abc_NodeSetTravIdCurrent( pNode ); + Abc_ObjForEachFanin( pNode, pFanin, i ) + { + assert( pFanin->vFanouts.nSize > 0 ); + if ( --pFanin->vFanouts.nSize == 0 ) + Counter += Res_NodeDeref_rec( pFanin ); + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [References the node's MFFC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Res_NodeRef_rec( Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pFanin; + int i, Counter = 1; + if ( Abc_ObjIsCi(pNode) ) + return 0; + Abc_ObjForEachFanin( pNode, pFanin, i ) + { + if ( pFanin->vFanouts.nSize++ == 0 ) + Counter += Res_NodeRef_rec( pFanin ); + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Labels MFFC of the node with the current trav ID.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Res_WinVisitMffc( Abc_Obj_t * pNode ) +{ + int Count1, Count2; + assert( Abc_ObjIsNode(pNode) ); + // dereference the node (mark with the current trav ID) + Count1 = Res_NodeDeref_rec( pNode ); + // reference it back + Count2 = Res_NodeRef_rec( pNode ); + assert( Count1 == Count2 ); + return Count1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/res/resFilter.c b/abc_with_bb_support/src/opt/res/resFilter.c new file mode 100644 index 000000000..45c55e9d8 --- /dev/null +++ b/abc_with_bb_support/src/opt/res/resFilter.c @@ -0,0 +1,434 @@ +/**CFile**************************************************************** + + FileName [resFilter.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resynthesis package.] + + Synopsis [Filtering resubstitution candidates.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 15, 2007.] + + Revision [$Id: resFilter.c,v 1.00 2007/01/15 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "resInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static unsigned * Res_FilterCollectFaninInfo( Res_Win_t * pWin, Res_Sim_t * pSim, unsigned uMask ); +static int Res_FilterCriticalFanin( Abc_Obj_t * pNode ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Finds sets of feasible candidates.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Res_FilterCandidates( Res_Win_t * pWin, Abc_Ntk_t * pAig, Res_Sim_t * pSim, Vec_Vec_t * vResubs, Vec_Vec_t * vResubsW, int nFaninsMax, int fArea ) +{ + Abc_Obj_t * pFanin, * pFanin2, * pFaninTemp; + unsigned * pInfo, * pInfoDiv, * pInfoDiv2; + int Counter, RetValue, i, i2, d, d2, iDiv, iDiv2, k; + + // check that the info the node is one + pInfo = Vec_PtrEntry( pSim->vOuts, 1 ); + RetValue = Abc_InfoIsOne( pInfo, pSim->nWordsOut ); + if ( RetValue == 0 ) + { +// printf( "Failed 1!\n" ); + return 0; + } + + // collect the fanin info + pInfo = Res_FilterCollectFaninInfo( pWin, pSim, ~0 ); + RetValue = Abc_InfoIsOne( pInfo, pSim->nWordsOut ); + if ( RetValue == 0 ) + { +// printf( "Failed 2!\n" ); + return 0; + } + + // try removing each fanin +// printf( "Fanins: " ); + Counter = 0; + Vec_VecClear( vResubs ); + Vec_VecClear( vResubsW ); + Abc_ObjForEachFanin( pWin->pNode, pFanin, i ) + { + if ( fArea && Abc_ObjFanoutNum(pFanin) > 1 ) + continue; + // get simulation info without this fanin + pInfo = Res_FilterCollectFaninInfo( pWin, pSim, ~(1 << i) ); + RetValue = Abc_InfoIsOne( pInfo, pSim->nWordsOut ); + if ( RetValue ) + { +// printf( "Node %4d. Candidate fanin %4d.\n", pWin->pNode->Id, pFanin->Id ); + // collect the nodes + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,0) ); + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,1) ); + Abc_ObjForEachFanin( pWin->pNode, pFaninTemp, k ) + { + if ( k != i ) + { + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,2+k) ); + Vec_VecPush( vResubsW, Counter, pFaninTemp ); + } + } + Counter++; + if ( Counter == Vec_VecSize(vResubs) ) + return Counter; + } + } + + // try replacing each critical fanin by a non-critical fanin + Abc_ObjForEachFanin( pWin->pNode, pFanin, i ) + { + if ( Abc_ObjFanoutNum(pFanin) > 1 ) + continue; + // get simulation info without this fanin + pInfo = Res_FilterCollectFaninInfo( pWin, pSim, ~(1 << i) ); + // go over the set of divisors + for ( d = Abc_ObjFaninNum(pWin->pNode) + 2; d < Abc_NtkPoNum(pAig); d++ ) + { + pInfoDiv = Vec_PtrEntry( pSim->vOuts, d ); + iDiv = d - (Abc_ObjFaninNum(pWin->pNode) + 2); + if ( !Abc_InfoIsOrOne( pInfo, pInfoDiv, pSim->nWordsOut ) ) + continue; + // collect the nodes + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,0) ); + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,1) ); + // collect the remaning fanins and the divisor + Abc_ObjForEachFanin( pWin->pNode, pFaninTemp, k ) + { + if ( k != i ) + { + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,2+k) ); + Vec_VecPush( vResubsW, Counter, pFaninTemp ); + } + } + // collect the divisor + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,d) ); + Vec_VecPush( vResubsW, Counter, Vec_PtrEntry(pWin->vDivs, iDiv) ); + Counter++; + if ( Counter == Vec_VecSize(vResubs) ) + return Counter; + } + } + + // consider the case when two fanins can be added instead of one + if ( Abc_ObjFaninNum(pWin->pNode) < nFaninsMax ) + { + // try to replace each critical fanin by two non-critical fanins + Abc_ObjForEachFanin( pWin->pNode, pFanin, i ) + { + if ( Abc_ObjFanoutNum(pFanin) > 1 ) + continue; + // get simulation info without this fanin + pInfo = Res_FilterCollectFaninInfo( pWin, pSim, ~(1 << i) ); + // go over the set of divisors + for ( d = Abc_ObjFaninNum(pWin->pNode) + 2; d < Abc_NtkPoNum(pAig); d++ ) + { + pInfoDiv = Vec_PtrEntry( pSim->vOuts, d ); + iDiv = d - (Abc_ObjFaninNum(pWin->pNode) + 2); + // go through the second divisor + for ( d2 = d + 1; d2 < Abc_NtkPoNum(pAig); d2++ ) + { + pInfoDiv2 = Vec_PtrEntry( pSim->vOuts, d2 ); + iDiv2 = d2 - (Abc_ObjFaninNum(pWin->pNode) + 2); + if ( !Abc_InfoIsOrOne3( pInfo, pInfoDiv, pInfoDiv2, pSim->nWordsOut ) ) + continue; + // collect the nodes + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,0) ); + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,1) ); + // collect the remaning fanins and the divisor + Abc_ObjForEachFanin( pWin->pNode, pFaninTemp, k ) + { + if ( k != i ) + { + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,2+k) ); + Vec_VecPush( vResubsW, Counter, pFaninTemp ); + } + } + // collect the divisor + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,d) ); + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,d2) ); + Vec_VecPush( vResubsW, Counter, Vec_PtrEntry(pWin->vDivs, iDiv) ); + Vec_VecPush( vResubsW, Counter, Vec_PtrEntry(pWin->vDivs, iDiv2) ); + Counter++; + if ( Counter == Vec_VecSize(vResubs) ) + return Counter; + } + } + } + } + + // try to replace two nets by one + if ( !fArea ) + { + Abc_ObjForEachFanin( pWin->pNode, pFanin, i ) + { + for ( i2 = i + 1; i2 < Abc_ObjFaninNum(pWin->pNode); i2++ ) + { + pFanin2 = Abc_ObjFanin(pWin->pNode, i2); + // get simulation info without these fanins + pInfo = Res_FilterCollectFaninInfo( pWin, pSim, (~(1 << i)) & (~(1 << i2)) ); + // go over the set of divisors + for ( d = Abc_ObjFaninNum(pWin->pNode) + 2; d < Abc_NtkPoNum(pAig); d++ ) + { + pInfoDiv = Vec_PtrEntry( pSim->vOuts, d ); + iDiv = d - (Abc_ObjFaninNum(pWin->pNode) + 2); + if ( !Abc_InfoIsOrOne( pInfo, pInfoDiv, pSim->nWordsOut ) ) + continue; + // collect the nodes + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,0) ); + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,1) ); + // collect the remaning fanins and the divisor + Abc_ObjForEachFanin( pWin->pNode, pFaninTemp, k ) + { + if ( k != i && k != i2 ) + { + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,2+k) ); + Vec_VecPush( vResubsW, Counter, pFaninTemp ); + } + } + // collect the divisor + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,d) ); + Vec_VecPush( vResubsW, Counter, Vec_PtrEntry(pWin->vDivs, iDiv) ); + Counter++; + if ( Counter == Vec_VecSize(vResubs) ) + return Counter; + } + } + } + } + return Counter; +} + + +/**Function************************************************************* + + Synopsis [Finds sets of feasible candidates.] + + Description [This procedure is a special case of the above.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Res_FilterCandidatesArea( Res_Win_t * pWin, Abc_Ntk_t * pAig, Res_Sim_t * pSim, Vec_Vec_t * vResubs, Vec_Vec_t * vResubsW, int nFaninsMax ) +{ + Abc_Obj_t * pFanin; + unsigned * pInfo, * pInfoDiv, * pInfoDiv2; + int Counter, RetValue, d, d2, k, iDiv, iDiv2, iBest; + + // check that the info the node is one + pInfo = Vec_PtrEntry( pSim->vOuts, 1 ); + RetValue = Abc_InfoIsOne( pInfo, pSim->nWordsOut ); + if ( RetValue == 0 ) + { +// printf( "Failed 1!\n" ); + return 0; + } + + // collect the fanin info + pInfo = Res_FilterCollectFaninInfo( pWin, pSim, ~0 ); + RetValue = Abc_InfoIsOne( pInfo, pSim->nWordsOut ); + if ( RetValue == 0 ) + { +// printf( "Failed 2!\n" ); + return 0; + } + + // try removing fanins +// printf( "Fanins: " ); + Counter = 0; + Vec_VecClear( vResubs ); + Vec_VecClear( vResubsW ); + // get the best fanins + iBest = Res_FilterCriticalFanin( pWin->pNode ); + if ( iBest == -1 ) + return 0; + + // get the info without the critical fanin + pInfo = Res_FilterCollectFaninInfo( pWin, pSim, ~(1 << iBest) ); + RetValue = Abc_InfoIsOne( pInfo, pSim->nWordsOut ); + if ( RetValue ) + { +// printf( "Can be done without one!\n" ); + // collect the nodes + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,0) ); + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,1) ); + Abc_ObjForEachFanin( pWin->pNode, pFanin, k ) + { + if ( k != iBest ) + { + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,2+k) ); + Vec_VecPush( vResubsW, Counter, pFanin ); + } + } + Counter++; +// printf( "*" ); + return Counter; + } + + // go through the divisors + for ( d = Abc_ObjFaninNum(pWin->pNode) + 2; d < Abc_NtkPoNum(pAig); d++ ) + { + pInfoDiv = Vec_PtrEntry( pSim->vOuts, d ); + iDiv = d - (Abc_ObjFaninNum(pWin->pNode) + 2); + if ( !Abc_InfoIsOrOne( pInfo, pInfoDiv, pSim->nWordsOut ) ) + continue; +//if ( Abc_ObjLevel(pWin->pNode) <= Abc_ObjLevel( Vec_PtrEntry(pWin->vDivs, iDiv) ) ) +// printf( "Node level = %d. Divisor level = %d.\n", Abc_ObjLevel(pWin->pNode), Abc_ObjLevel( Vec_PtrEntry(pWin->vDivs, iDiv) ) ); + // collect the nodes + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,0) ); + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,1) ); + // collect the remaning fanins and the divisor + Abc_ObjForEachFanin( pWin->pNode, pFanin, k ) + { + if ( k != iBest ) + { + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,2+k) ); + Vec_VecPush( vResubsW, Counter, pFanin ); + } + } + // collect the divisor + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,d) ); + Vec_VecPush( vResubsW, Counter, Vec_PtrEntry(pWin->vDivs, iDiv) ); + Counter++; + + if ( Counter == Vec_VecSize(vResubs) ) + break; + } + + if ( Counter > 0 || Abc_ObjFaninNum(pWin->pNode) >= nFaninsMax ) + return Counter; + + // try to find the node pairs + for ( d = Abc_ObjFaninNum(pWin->pNode) + 2; d < Abc_NtkPoNum(pAig); d++ ) + { + pInfoDiv = Vec_PtrEntry( pSim->vOuts, d ); + iDiv = d - (Abc_ObjFaninNum(pWin->pNode) + 2); + // go through the second divisor + for ( d2 = d + 1; d2 < Abc_NtkPoNum(pAig); d2++ ) + { + pInfoDiv2 = Vec_PtrEntry( pSim->vOuts, d2 ); + iDiv2 = d2 - (Abc_ObjFaninNum(pWin->pNode) + 2); + + if ( !Abc_InfoIsOrOne3( pInfo, pInfoDiv, pInfoDiv2, pSim->nWordsOut ) ) + continue; + // collect the nodes + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,0) ); + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,1) ); + // collect the remaning fanins and the divisor + Abc_ObjForEachFanin( pWin->pNode, pFanin, k ) + { + if ( k != iBest ) + { + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,2+k) ); + Vec_VecPush( vResubsW, Counter, pFanin ); + } + } + // collect the divisor + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,d) ); + Vec_VecPush( vResubs, Counter, Abc_NtkPo(pAig,d2) ); + Vec_VecPush( vResubsW, Counter, Vec_PtrEntry(pWin->vDivs, iDiv) ); + Vec_VecPush( vResubsW, Counter, Vec_PtrEntry(pWin->vDivs, iDiv2) ); + Counter++; + + if ( Counter == Vec_VecSize(vResubs) ) + break; + } + if ( Counter == Vec_VecSize(vResubs) ) + break; + } + return Counter; +} + + +/**Function************************************************************* + + Synopsis [Finds sets of feasible candidates.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +unsigned * Res_FilterCollectFaninInfo( Res_Win_t * pWin, Res_Sim_t * pSim, unsigned uMask ) +{ + Abc_Obj_t * pFanin; + unsigned * pInfo; + int i; + pInfo = Vec_PtrEntry( pSim->vOuts, 0 ); + Abc_InfoClear( pInfo, pSim->nWordsOut ); + Abc_ObjForEachFanin( pWin->pNode, pFanin, i ) + { + if ( uMask & (1 << i) ) + Abc_InfoOr( pInfo, Vec_PtrEntry(pSim->vOuts, 2+i), pSim->nWordsOut ); + } + return pInfo; +} + + +/**Function************************************************************* + + Synopsis [Returns the index of the most critical fanin.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Res_FilterCriticalFanin( Abc_Obj_t * pNode ) +{ + Abc_Obj_t * pFanin; + int i, iBest = -1, CostMax = 0, CostCur; + Abc_ObjForEachFanin( pNode, pFanin, i ) + { + if ( !Abc_ObjIsNode(pFanin) ) + continue; + if ( Abc_ObjFanoutNum(pFanin) > 1 ) + continue; + CostCur = Res_WinVisitMffc( pFanin ); + if ( CostMax < CostCur ) + { + CostMax = CostCur; + iBest = i; + } + } +// if ( CostMax > 0 ) +// printf( "<%d>", CostMax ); + return iBest; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/res/resInt.h b/abc_with_bb_support/src/opt/res/resInt.h new file mode 100644 index 000000000..b56783dd7 --- /dev/null +++ b/abc_with_bb_support/src/opt/res/resInt.h @@ -0,0 +1,137 @@ +/**CFile**************************************************************** + + FileName [resInt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resynthesis package.] + + Synopsis [Internal declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 15, 2007.] + + Revision [$Id: resInt.h,v 1.00 2007/01/15 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __RES_INT_H__ +#define __RES_INT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include "res.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Res_Win_t_ Res_Win_t; +struct Res_Win_t_ +{ + // windowing parameters + Abc_Obj_t * pNode; // the node in the center + int nWinTfiMax; // the fanin levels + int nWinTfoMax; // the fanout levels + int nLevDivMax; // the maximum divisor level + // internal windowing parameters + int nFanoutLimit; // the limit on the fanout count of a TFO node (if more, the node is treated as a root) + int nLevTfiMinus; // the number of additional levels to search from TFO below the level of leaves + // derived windowing parameters + int nLevLeafMin; // the minimum level of a leaf + int nLevTravMin; // the minimum level to search from TFO + int nDivsPlus; // the number of additional divisors + // the window data + Vec_Ptr_t * vRoots; // outputs of the window + Vec_Ptr_t * vLeaves; // inputs of the window + Vec_Ptr_t * vBranches; // side nodes of the window + Vec_Ptr_t * vNodes; // internal nodes of the window + Vec_Ptr_t * vDivs; // candidate divisors of the node + // temporary data + Vec_Vec_t * vMatrix; // TFI nodes below the given node +}; + +typedef struct Res_Sim_t_ Res_Sim_t; +struct Res_Sim_t_ +{ + Abc_Ntk_t * pAig; // AIG for simulation + int nTruePis; // the number of true PIs of the window + int fConst0; // the node can be replaced by constant 0 + int fConst1; // the node can be replaced by constant 0 + // simulation parameters + int nWords; // the number of simulation words + int nPats; // the number of patterns + int nWordsIn; // the number of simulation words in the input patterns + int nPatsIn; // the number of patterns in the input patterns + int nBytesIn; // the number of bytes in the input patterns + int nWordsOut; // the number of simulation words in the output patterns + int nPatsOut; // the number of patterns in the output patterns + // simulation info + Vec_Ptr_t * vPats; // input simulation patterns + Vec_Ptr_t * vPats0; // input simulation patterns + Vec_Ptr_t * vPats1; // input simulation patterns + Vec_Ptr_t * vOuts; // output simulation info + int nPats0; // the number of 0-patterns accumulated + int nPats1; // the number of 1-patterns accumulated + // resub candidates + Vec_Vec_t * vCands; // resubstitution candidates + // statistics + int timeSat; +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== resDivs.c ==========================================================*/ +extern void Res_WinDivisors( Res_Win_t * p, int nLevDivMax ); +extern void Res_WinSweepLeafTfo_rec( Abc_Obj_t * pObj, int nLevelLimit ); +extern int Res_WinVisitMffc( Abc_Obj_t * pNode ); +/*=== resFilter.c ==========================================================*/ +extern int Res_FilterCandidates( Res_Win_t * pWin, Abc_Ntk_t * pAig, Res_Sim_t * pSim, Vec_Vec_t * vResubs, Vec_Vec_t * vResubsW, int nFaninsMax, int fArea ); +extern int Res_FilterCandidatesArea( Res_Win_t * pWin, Abc_Ntk_t * pAig, Res_Sim_t * pSim, Vec_Vec_t * vResubs, Vec_Vec_t * vResubsW, int nFaninsMax ); +/*=== resSat.c ==========================================================*/ +extern void * Res_SatProveUnsat( Abc_Ntk_t * pAig, Vec_Ptr_t * vFanins ); +extern int Res_SatSimulate( Res_Sim_t * p, int nPats, int fOnSet ); +/*=== resSim.c ==========================================================*/ +extern Res_Sim_t * Res_SimAlloc( int nWords ); +extern void Res_SimFree( Res_Sim_t * p ); +extern int Res_SimPrepare( Res_Sim_t * p, Abc_Ntk_t * pAig, int nTruePis, int fVerbose ); +/*=== resStrash.c ==========================================================*/ +extern Abc_Ntk_t * Res_WndStrash( Res_Win_t * p ); +/*=== resWnd.c ==========================================================*/ +extern void Res_UpdateNetwork( Abc_Obj_t * pObj, Vec_Ptr_t * vFanins, Hop_Obj_t * pFunc, Vec_Vec_t * vLevels ); +/*=== resWnd.c ==========================================================*/ +extern Res_Win_t * Res_WinAlloc(); +extern void Res_WinFree( Res_Win_t * p ); +extern int Res_WinIsTrivial( Res_Win_t * p ); +extern int Res_WinCompute( Abc_Obj_t * pNode, int nWinTfiMax, int nWinTfoMax, Res_Win_t * p ); + + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/opt/res/resSat.c b/abc_with_bb_support/src/opt/res/resSat.c new file mode 100644 index 000000000..baf521cf9 --- /dev/null +++ b/abc_with_bb_support/src/opt/res/resSat.c @@ -0,0 +1,407 @@ +/**CFile**************************************************************** + + FileName [resSat.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resynthesis package.] + + Synopsis [Interface with the SAT solver.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 15, 2007.] + + Revision [$Id: resSat.c,v 1.00 2007/01/15 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "resInt.h" +#include "hop.h" +#include "satSolver.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +extern int Res_SatAddConst1( sat_solver * pSat, int iVar, int fCompl ); +extern int Res_SatAddEqual( sat_solver * pSat, int iVar0, int iVar1, int fCompl ); +extern int Res_SatAddAnd( sat_solver * pSat, int iVar, int iVar0, int iVar1, int fCompl0, int fCompl1 ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Loads AIG into the SAT solver for checking resubstitution.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void * Res_SatProveUnsat( Abc_Ntk_t * pAig, Vec_Ptr_t * vFanins ) +{ + void * pCnf = NULL; + sat_solver * pSat; + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj; + int i, nNodes, status; + + // make sure fanins contain POs of the AIG + pObj = Vec_PtrEntry( vFanins, 0 ); + assert( pObj->pNtk == pAig && Abc_ObjIsPo(pObj) ); + + // collect reachable nodes + vNodes = Abc_NtkDfsNodes( pAig, (Abc_Obj_t **)vFanins->pArray, vFanins->nSize ); + + // assign unique numbers to each node + nNodes = 0; + Abc_AigConst1(pAig)->pCopy = (void *)nNodes++; + Abc_NtkForEachPi( pAig, pObj, i ) + pObj->pCopy = (void *)nNodes++; + Vec_PtrForEachEntry( vNodes, pObj, i ) + pObj->pCopy = (void *)nNodes++; + Vec_PtrForEachEntry( vFanins, pObj, i ) // useful POs + pObj->pCopy = (void *)nNodes++; + + // start the solver + pSat = sat_solver_new(); + sat_solver_store_alloc( pSat ); + + // add clause for the constant node + Res_SatAddConst1( pSat, (int)Abc_AigConst1(pAig)->pCopy, 0 ); + // add clauses for AND gates + Vec_PtrForEachEntry( vNodes, pObj, i ) + Res_SatAddAnd( pSat, (int)pObj->pCopy, + (int)Abc_ObjFanin0(pObj)->pCopy, (int)Abc_ObjFanin1(pObj)->pCopy, Abc_ObjFaninC0(pObj), Abc_ObjFaninC1(pObj) ); + Vec_PtrFree( vNodes ); + // add clauses for POs + Vec_PtrForEachEntry( vFanins, pObj, i ) + Res_SatAddEqual( pSat, (int)pObj->pCopy, + (int)Abc_ObjFanin0(pObj)->pCopy, Abc_ObjFaninC0(pObj) ); + // add trivial clauses + pObj = Vec_PtrEntry(vFanins, 0); + Res_SatAddConst1( pSat, (int)pObj->pCopy, 0 ); // care-set + pObj = Vec_PtrEntry(vFanins, 1); + Res_SatAddConst1( pSat, (int)pObj->pCopy, 0 ); // on-set + + // bookmark the clauses of A + sat_solver_store_mark_clauses_a( pSat ); + + // duplicate the clauses + pObj = Vec_PtrEntry(vFanins, 1); + Sat_SolverDoubleClauses( pSat, (int)pObj->pCopy ); + // add the equality constraints + Vec_PtrForEachEntryStart( vFanins, pObj, i, 2 ) + Res_SatAddEqual( pSat, (int)pObj->pCopy, ((int)pObj->pCopy) + nNodes, 0 ); + + // bookmark the roots + sat_solver_store_mark_roots( pSat ); + + // solve the problem + status = sat_solver_solve( pSat, NULL, NULL, (sint64)10000, (sint64)0, (sint64)0, (sint64)0 ); + if ( status == l_False ) + { + pCnf = sat_solver_store_release( pSat ); +// printf( "unsat\n" ); + } + else if ( status == l_True ) + { +// printf( "sat\n" ); + } + else + { +// printf( "undef\n" ); + } + sat_solver_delete( pSat ); + return pCnf; +} + +/**Function************************************************************* + + Synopsis [Loads AIG into the SAT solver for constrained simulation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void * Res_SatSimulateConstr( Abc_Ntk_t * pAig, int fOnSet ) +{ + sat_solver * pSat; + Vec_Ptr_t * vFanins; + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj; + int i, nNodes; + + // start the array + vFanins = Vec_PtrAlloc( 2 ); + pObj = Abc_NtkPo( pAig, 0 ); + Vec_PtrPush( vFanins, pObj ); + pObj = Abc_NtkPo( pAig, 1 ); + Vec_PtrPush( vFanins, pObj ); + + // collect reachable nodes + vNodes = Abc_NtkDfsNodes( pAig, (Abc_Obj_t **)vFanins->pArray, vFanins->nSize ); + + // assign unique numbers to each node + nNodes = 0; + Abc_AigConst1(pAig)->pCopy = (void *)nNodes++; + Abc_NtkForEachPi( pAig, pObj, i ) + pObj->pCopy = (void *)nNodes++; + Vec_PtrForEachEntry( vNodes, pObj, i ) + pObj->pCopy = (void *)nNodes++; + Vec_PtrForEachEntry( vFanins, pObj, i ) // useful POs + pObj->pCopy = (void *)nNodes++; + + // start the solver + pSat = sat_solver_new(); + + // add clause for the constant node + Res_SatAddConst1( pSat, (int)Abc_AigConst1(pAig)->pCopy, 0 ); + // add clauses for AND gates + Vec_PtrForEachEntry( vNodes, pObj, i ) + Res_SatAddAnd( pSat, (int)pObj->pCopy, + (int)Abc_ObjFanin0(pObj)->pCopy, (int)Abc_ObjFanin1(pObj)->pCopy, Abc_ObjFaninC0(pObj), Abc_ObjFaninC1(pObj) ); + Vec_PtrFree( vNodes ); + // add clauses for the first PO + pObj = Abc_NtkPo( pAig, 0 ); + Res_SatAddEqual( pSat, (int)pObj->pCopy, + (int)Abc_ObjFanin0(pObj)->pCopy, Abc_ObjFaninC0(pObj) ); + // add clauses for the second PO + pObj = Abc_NtkPo( pAig, 1 ); + Res_SatAddEqual( pSat, (int)pObj->pCopy, + (int)Abc_ObjFanin0(pObj)->pCopy, Abc_ObjFaninC0(pObj) ); + + // add trivial clauses + pObj = Abc_NtkPo( pAig, 0 ); + Res_SatAddConst1( pSat, (int)pObj->pCopy, 0 ); // care-set + + pObj = Abc_NtkPo( pAig, 1 ); + Res_SatAddConst1( pSat, (int)pObj->pCopy, !fOnSet ); // on-set + + Vec_PtrFree( vFanins ); + return pSat; +} + +/**Function************************************************************* + + Synopsis [Loads AIG into the SAT solver for constrained simulation.] + + Description [Returns 1 if the required number of patterns are found. + Returns 0 if the solver ran out of time or proved a constant. + In the latter, case one of the flags, fConst0 or fConst1, are set to 1.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Res_SatSimulate( Res_Sim_t * p, int nPatsLimit, int fOnSet ) +{ + Vec_Int_t * vLits; + Vec_Ptr_t * vPats; + sat_solver * pSat; + int RetValue, i, k, value, status, Lit, Var, iPat; + int clk = clock(); + +//printf( "Looking for %s: ", fOnSet? "onset " : "offset" ); + + // decide what problem should be solved + Lit = toLitCond( (int)Abc_NtkPo(p->pAig,1)->pCopy, !fOnSet ); + if ( fOnSet ) + { + iPat = p->nPats1; + vPats = p->vPats1; + } + else + { + iPat = p->nPats0; + vPats = p->vPats0; + } + assert( iPat < nPatsLimit ); + + // derive the SAT solver + pSat = Res_SatSimulateConstr( p->pAig, fOnSet ); + pSat->fSkipSimplify = 1; + status = sat_solver_simplify( pSat ); + if ( status == 0 ) + { + if ( iPat == 0 ) + { +// if ( fOnSet ) +// p->fConst0 = 1; +// else +// p->fConst1 = 1; + RetValue = 0; + } + goto finish; + } + + // enumerate through the SAT assignments + RetValue = 1; + vLits = Vec_IntAlloc( 32 ); + for ( k = iPat; k < nPatsLimit; k++ ) + { + // solve with the assumption +// status = sat_solver_solve( pSat, &Lit, &Lit + 1, (sint64)10000, (sint64)0, (sint64)0, (sint64)0 ); + status = sat_solver_solve( pSat, NULL, NULL, (sint64)10000, (sint64)0, (sint64)0, (sint64)0 ); + if ( status == l_False ) + { +//printf( "Const %d\n", !fOnSet ); + if ( k == 0 ) + { + if ( fOnSet ) + p->fConst0 = 1; + else + p->fConst1 = 1; + RetValue = 0; + } + break; + } + else if ( status == l_True ) + { + // save the pattern + Vec_IntClear( vLits ); + for ( i = 0; i < p->nTruePis; i++ ) + { + Var = (int)Abc_NtkPi(p->pAig,i)->pCopy; + value = (int)(pSat->model.ptr[Var] == l_True); + if ( value ) + Abc_InfoSetBit( Vec_PtrEntry(vPats, i), k ); + Lit = toLitCond( Var, value ); + Vec_IntPush( vLits, Lit ); +// printf( "%d", value ); + } +// printf( "\n" ); + + status = sat_solver_addclause( pSat, Vec_IntArray(vLits), Vec_IntArray(vLits) + Vec_IntSize(vLits) ); + if ( status == 0 ) + { + k++; + RetValue = 1; + break; + } + } + else + { +//printf( "Undecided\n" ); + if ( k == 0 ) + RetValue = 0; + else + RetValue = 1; + break; + } + } + Vec_IntFree( vLits ); +//printf( "Found %d patterns\n", k - iPat ); + + // set the new number of patterns + if ( fOnSet ) + p->nPats1 = k; + else + p->nPats0 = k; + +finish: + + sat_solver_delete( pSat ); +p->timeSat += clock() - clk; + return RetValue; +} + + +/**Function************************************************************* + + Synopsis [Asserts equality of the variable to a constant.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Res_SatAddConst1( sat_solver * pSat, int iVar, int fCompl ) +{ + lit Lit = toLitCond( iVar, fCompl ); + if ( !sat_solver_addclause( pSat, &Lit, &Lit + 1 ) ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Asserts equality of two variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Res_SatAddEqual( sat_solver * pSat, int iVar0, int iVar1, int fCompl ) +{ + lit Lits[2]; + + Lits[0] = toLitCond( iVar0, 0 ); + Lits[1] = toLitCond( iVar1, !fCompl ); + if ( !sat_solver_addclause( pSat, Lits, Lits + 2 ) ) + return 0; + + Lits[0] = toLitCond( iVar0, 1 ); + Lits[1] = toLitCond( iVar1, fCompl ); + if ( !sat_solver_addclause( pSat, Lits, Lits + 2 ) ) + return 0; + + return 1; +} + +/**Function************************************************************* + + Synopsis [Adds constraints for the two-input AND-gate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Res_SatAddAnd( sat_solver * pSat, int iVar, int iVar0, int iVar1, int fCompl0, int fCompl1 ) +{ + lit Lits[3]; + + Lits[0] = toLitCond( iVar, 1 ); + Lits[1] = toLitCond( iVar0, fCompl0 ); + if ( !sat_solver_addclause( pSat, Lits, Lits + 2 ) ) + return 0; + + Lits[0] = toLitCond( iVar, 1 ); + Lits[1] = toLitCond( iVar1, fCompl1 ); + if ( !sat_solver_addclause( pSat, Lits, Lits + 2 ) ) + return 0; + + Lits[0] = toLitCond( iVar, 0 ); + Lits[1] = toLitCond( iVar0, !fCompl0 ); + Lits[2] = toLitCond( iVar1, !fCompl1 ); + if ( !sat_solver_addclause( pSat, Lits, Lits + 3 ) ) + return 0; + + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/res/resSim.c b/abc_with_bb_support/src/opt/res/resSim.c new file mode 100644 index 000000000..a546d1089 --- /dev/null +++ b/abc_with_bb_support/src/opt/res/resSim.c @@ -0,0 +1,790 @@ +/**CFile**************************************************************** + + FileName [resSim.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resynthesis package.] + + Synopsis [Simulation engine.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 15, 2007.] + + Revision [$Id: resSim.c,v 1.00 2007/01/15 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "resInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocate simulation engine.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Res_Sim_t * Res_SimAlloc( int nWords ) +{ + Res_Sim_t * p; + p = ALLOC( Res_Sim_t, 1 ); + memset( p, 0, sizeof(Res_Sim_t) ); + // simulation parameters + p->nWords = nWords; + p->nPats = p->nWords * 8 * sizeof(unsigned); + p->nWordsIn = p->nPats; + p->nBytesIn = p->nPats * sizeof(unsigned); + p->nPatsIn = p->nPats * 8 * sizeof(unsigned); + p->nWordsOut = p->nPats * p->nWords; + p->nPatsOut = p->nPats * p->nPats; + // simulation info + p->vPats = Vec_PtrAllocSimInfo( 1024, p->nWordsIn ); + p->vPats0 = Vec_PtrAllocSimInfo( 128, p->nWords ); + p->vPats1 = Vec_PtrAllocSimInfo( 128, p->nWords ); + p->vOuts = Vec_PtrAllocSimInfo( 128, p->nWordsOut ); + // resub candidates + p->vCands = Vec_VecStart( 16 ); + return p; +} + +/**Function************************************************************* + + Synopsis [Allocate simulation engine.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimAdjust( Res_Sim_t * p, Abc_Ntk_t * pAig, int nTruePis ) +{ + srand( 0xABC ); + + assert( Abc_NtkIsStrash(pAig) ); + p->pAig = pAig; + p->nTruePis = nTruePis; + if ( Vec_PtrSize(p->vPats) < Abc_NtkObjNumMax(pAig)+1 ) + { + Vec_PtrFree( p->vPats ); + p->vPats = Vec_PtrAllocSimInfo( Abc_NtkObjNumMax(pAig)+1, p->nWordsIn ); + } + if ( Vec_PtrSize(p->vPats0) < nTruePis ) + { + Vec_PtrFree( p->vPats0 ); + p->vPats0 = Vec_PtrAllocSimInfo( nTruePis, p->nWords ); + } + if ( Vec_PtrSize(p->vPats1) < nTruePis ) + { + Vec_PtrFree( p->vPats1 ); + p->vPats1 = Vec_PtrAllocSimInfo( nTruePis, p->nWords ); + } + if ( Vec_PtrSize(p->vOuts) < Abc_NtkPoNum(pAig) ) + { + Vec_PtrFree( p->vOuts ); + p->vOuts = Vec_PtrAllocSimInfo( Abc_NtkPoNum(pAig), p->nWordsOut ); + } + // clean storage info for patterns + Abc_InfoClear( Vec_PtrEntry(p->vPats0,0), p->nWords * nTruePis ); + Abc_InfoClear( Vec_PtrEntry(p->vPats1,0), p->nWords * nTruePis ); + p->nPats0 = 0; + p->nPats1 = 0; + p->fConst0 = 0; + p->fConst1 = 0; +} + +/**Function************************************************************* + + Synopsis [Free simulation engine.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimFree( Res_Sim_t * p ) +{ + Vec_PtrFree( p->vPats ); + Vec_PtrFree( p->vPats0 ); + Vec_PtrFree( p->vPats1 ); + Vec_PtrFree( p->vOuts ); + Vec_VecFree( p->vCands ); + free( p ); +} + + +/**Function************************************************************* + + Synopsis [Sets random PI simulation info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_InfoRandomBytes( unsigned * p, int nWords ) +{ + int i, Num; + for ( i = nWords - 1; i >= 0; i-- ) + { + Num = rand(); + p[i] = (Num & 1)? 0xff : 0; + p[i] = (p[i] << 8) | ((Num & 2)? 0xff : 0); + p[i] = (p[i] << 8) | ((Num & 4)? 0xff : 0); + p[i] = (p[i] << 8) | ((Num & 8)? 0xff : 0); + } +// Extra_PrintBinary( stdout, p, 32 ); printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [Sets random PI simulation info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimSetRandomBytes( Res_Sim_t * p ) +{ + Abc_Obj_t * pObj; + unsigned * pInfo; + int i; + Abc_NtkForEachPi( p->pAig, pObj, i ) + { + pInfo = Vec_PtrEntry( p->vPats, pObj->Id ); + if ( i < p->nTruePis ) + Abc_InfoRandomBytes( pInfo, p->nWordsIn ); + else + Abc_InfoRandom( pInfo, p->nWordsIn ); + } +/* + // double-check that all are byte-patterns + Abc_NtkForEachPi( p->pAig, pObj, i ) + { + if ( i == p->nTruePis ) + break; + pInfoC = (unsigned char *)Vec_PtrEntry( p->vPats, pObj->Id ); + for ( k = 0; k < p->nBytesIn; k++ ) + assert( pInfoC[k] == 0 || pInfoC[k] == 0xff ); + } +*/ +} + +/**Function************************************************************* + + Synopsis [Sets random PI simulation info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimSetDerivedBytes( Res_Sim_t * p, int fUseWalk ) +{ + Vec_Ptr_t * vPatsSource[2]; + int nPatsSource[2]; + Abc_Obj_t * pObj; + unsigned char * pInfo; + int i, k, z, s, nPats; + + // set several random patterns + assert( p->nBytesIn % 32 == 0 ); + nPats = p->nBytesIn/8; + Abc_NtkForEachPi( p->pAig, pObj, i ) + { + if ( i == p->nTruePis ) + break; + Abc_InfoRandomBytes( Vec_PtrEntry(p->vPats, pObj->Id), nPats/4 ); + } + + // set special patterns + if ( fUseWalk ) + { + for ( z = 0; z < 2; z++ ) + { + // set the zero pattern + Abc_NtkForEachPi( p->pAig, pObj, i ) + { + if ( i == p->nTruePis ) + break; + pInfo = (unsigned char *)Vec_PtrEntry( p->vPats, pObj->Id ); + pInfo[nPats] = z ? 0xff : 0; + } + if ( ++nPats == p->nBytesIn ) + return; + // set the walking zero pattern + for ( k = 0; k < p->nTruePis; k++ ) + { + Abc_NtkForEachPi( p->pAig, pObj, i ) + { + if ( i == p->nTruePis ) + break; + pInfo = (unsigned char *)Vec_PtrEntry( p->vPats, pObj->Id ); + pInfo[nPats] = ((i == k) ^ z) ? 0xff : 0; + } + if ( ++nPats == p->nBytesIn ) + return; + } + } + } + + // decide what patterns to set first + if ( p->nPats0 < p->nPats1 ) + { + nPatsSource[0] = p->nPats0; + vPatsSource[0] = p->vPats0; + nPatsSource[1] = p->nPats1; + vPatsSource[1] = p->vPats1; + } + else + { + nPatsSource[0] = p->nPats1; + vPatsSource[0] = p->vPats1; + nPatsSource[1] = p->nPats0; + vPatsSource[1] = p->vPats0; + } + for ( z = 0; z < 2; z++ ) + { + for ( s = nPatsSource[z] - 1; s >= 0; s-- ) + { +// if ( s == 0 ) +// printf( "Patterns:\n" ); + // set the given source pattern + for ( k = 0; k < p->nTruePis; k++ ) + { + Abc_NtkForEachPi( p->pAig, pObj, i ) + { + if ( i == p->nTruePis ) + break; + pInfo = (unsigned char *)Vec_PtrEntry( p->vPats, pObj->Id ); + if ( (i == k) ^ Abc_InfoHasBit( Vec_PtrEntry(vPatsSource[z], i), s ) ) + { + pInfo[nPats] = 0xff; +// if ( s == 0 ) +// printf( "1" ); + } + else + { + pInfo[nPats] = 0; +// if ( s == 0 ) +// printf( "0" ); + } + } +// if ( s == 0 ) +// printf( "\n" ); + if ( ++nPats == p->nBytesIn ) + return; + } + } + } + // clean the rest + for ( z = nPats; z < p->nBytesIn; z++ ) + { + Abc_NtkForEachPi( p->pAig, pObj, i ) + { + if ( i == p->nTruePis ) + break; + pInfo = (unsigned char *)Vec_PtrEntry( p->vPats, pObj->Id ); + memset( pInfo + nPats, 0, p->nBytesIn - nPats ); + } + } +/* + // double-check that all are byte-patterns + Abc_NtkForEachPi( p->pAig, pObj, i ) + { + if ( i == p->nTruePis ) + break; + pInfo = (unsigned char *)Vec_PtrEntry( p->vPats, pObj->Id ); + for ( k = 0; k < p->nBytesIn; k++ ) + assert( pInfo[k] == 0 || pInfo[k] == 0xff ); + } +*/ +} + +/**Function************************************************************* + + Synopsis [Sets given PI simulation info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimSetGiven( Res_Sim_t * p, Vec_Ptr_t * vInfo ) +{ + Abc_Obj_t * pObj; + unsigned * pInfo, * pInfo2; + int i, w; + Abc_NtkForEachPi( p->pAig, pObj, i ) + { + if ( i == p->nTruePis ) + break; + pInfo = Vec_PtrEntry( p->vPats, pObj->Id ); + pInfo2 = Vec_PtrEntry( vInfo, i ); + for ( w = 0; w < p->nWords; w++ ) + pInfo[w] = pInfo2[w]; + } +} + +/**Function************************************************************* + + Synopsis [Simulates one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimPerformOne( Abc_Obj_t * pNode, Vec_Ptr_t * vSimInfo, int nSimWords ) +{ + unsigned * pInfo, * pInfo1, * pInfo2; + int k, fComp1, fComp2; + // simulate the internal nodes + assert( Abc_ObjIsNode(pNode) ); + pInfo = Vec_PtrEntry(vSimInfo, pNode->Id); + pInfo1 = Vec_PtrEntry(vSimInfo, Abc_ObjFaninId0(pNode)); + pInfo2 = Vec_PtrEntry(vSimInfo, Abc_ObjFaninId1(pNode)); + fComp1 = Abc_ObjFaninC0(pNode); + fComp2 = Abc_ObjFaninC1(pNode); + if ( fComp1 && fComp2 ) + for ( k = 0; k < nSimWords; k++ ) + pInfo[k] = ~pInfo1[k] & ~pInfo2[k]; + else if ( fComp1 && !fComp2 ) + for ( k = 0; k < nSimWords; k++ ) + pInfo[k] = ~pInfo1[k] & pInfo2[k]; + else if ( !fComp1 && fComp2 ) + for ( k = 0; k < nSimWords; k++ ) + pInfo[k] = pInfo1[k] & ~pInfo2[k]; + else // if ( fComp1 && fComp2 ) + for ( k = 0; k < nSimWords; k++ ) + pInfo[k] = pInfo1[k] & pInfo2[k]; +} + +/**Function************************************************************* + + Synopsis [Simulates one CO node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimTransferOne( Abc_Obj_t * pNode, Vec_Ptr_t * vSimInfo, int nSimWords ) +{ + unsigned * pInfo, * pInfo1; + int k, fComp1; + // simulate the internal nodes + assert( Abc_ObjIsCo(pNode) ); + pInfo = Vec_PtrEntry(vSimInfo, pNode->Id); + pInfo1 = Vec_PtrEntry(vSimInfo, Abc_ObjFaninId0(pNode)); + fComp1 = Abc_ObjFaninC0(pNode); + if ( fComp1 ) + for ( k = 0; k < nSimWords; k++ ) + pInfo[k] = ~pInfo1[k]; + else + for ( k = 0; k < nSimWords; k++ ) + pInfo[k] = pInfo1[k]; +} + +/**Function************************************************************* + + Synopsis [Performs one round of simulation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimPerformRound( Res_Sim_t * p, int nWords ) +{ + Abc_Obj_t * pObj; + int i; + Abc_InfoFill( Vec_PtrEntry(p->vPats,0), nWords ); + Abc_AigForEachAnd( p->pAig, pObj, i ) + Res_SimPerformOne( pObj, p->vPats, nWords ); + Abc_NtkForEachPo( p->pAig, pObj, i ) + Res_SimTransferOne( pObj, p->vPats, nWords ); +} + + +/**Function************************************************************* + + Synopsis [Pads the extra space with duplicated simulation info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimPadSimInfo( Vec_Ptr_t * vPats, int nPats, int nWords ) +{ + unsigned * pInfo; + int i, w, iWords; + assert( nPats > 0 && nPats < nWords * 8 * (int) sizeof(unsigned) ); + // pad the first word + if ( nPats < 8 * sizeof(unsigned) ) + { + Vec_PtrForEachEntry( vPats, pInfo, i ) + if ( pInfo[0] & 1 ) + pInfo[0] |= ((~0) << nPats); + nPats = 8 * sizeof(unsigned); + } + // pad the empty words + iWords = nPats / (8 * sizeof(unsigned)); + Vec_PtrForEachEntry( vPats, pInfo, i ) + { + for ( w = iWords; w < nWords; w++ ) + pInfo[w] = pInfo[0]; + } +} + +/**Function************************************************************* + + Synopsis [Duplicates the simulation info to fill the space.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimDeriveInfoReplicate( Res_Sim_t * p ) +{ + unsigned * pInfo, * pInfo2; + Abc_Obj_t * pObj; + int i, j, w; + Abc_NtkForEachPo( p->pAig, pObj, i ) + { + pInfo = Vec_PtrEntry( p->vPats, pObj->Id ); + pInfo2 = Vec_PtrEntry( p->vOuts, i ); + for ( j = 0; j < p->nPats; j++ ) + for ( w = 0; w < p->nWords; w++ ) + *pInfo2++ = pInfo[w]; + } +} + +/**Function************************************************************* + + Synopsis [Complement the simulation info if necessary.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimDeriveInfoComplement( Res_Sim_t * p ) +{ + unsigned * pInfo, * pInfo2; + Abc_Obj_t * pObj; + int i, j, w; + Abc_NtkForEachPo( p->pAig, pObj, i ) + { + pInfo = Vec_PtrEntry( p->vPats, pObj->Id ); + pInfo2 = Vec_PtrEntry( p->vOuts, i ); + for ( j = 0; j < p->nPats; j++, pInfo2 += p->nWords ) + if ( Abc_InfoHasBit( pInfo, j ) ) + for ( w = 0; w < p->nWords; w++ ) + pInfo2[w] = ~pInfo2[w]; + } +} + +/**Function************************************************************* + + Synopsis [Prints output patterns.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimPrintOutPatterns( Res_Sim_t * p, Abc_Ntk_t * pAig ) +{ + Abc_Obj_t * pObj; + unsigned * pInfo2; + int i; + Abc_NtkForEachPo( pAig, pObj, i ) + { + pInfo2 = Vec_PtrEntry( p->vOuts, i ); + Extra_PrintBinary( stdout, pInfo2, p->nPatsOut ); + printf( "\n" ); + } +} + +/**Function************************************************************* + + Synopsis [Prints output patterns.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimPrintNodePatterns( Res_Sim_t * p, Abc_Ntk_t * pAig ) +{ + unsigned * pInfo; + pInfo = Vec_PtrEntry( p->vPats, Abc_NtkPo(p->pAig, 1)->Id ); + Extra_PrintBinary( stdout, pInfo, p->nPats ); + printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [Counts the number of patters of different type.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimCountResults( Res_Sim_t * p, int * pnDcs, int * pnOnes, int * pnZeros, int fVerbose ) +{ + unsigned char * pInfoCare, * pInfoNode; + int i, nTotal = 0; + pInfoCare = (unsigned char *)Vec_PtrEntry( p->vPats, Abc_NtkPo(p->pAig, 0)->Id ); + pInfoNode = (unsigned char *)Vec_PtrEntry( p->vPats, Abc_NtkPo(p->pAig, 1)->Id ); + for ( i = 0; i < p->nBytesIn; i++ ) + { + if ( !pInfoCare[i] ) + (*pnDcs)++; + else if ( !pInfoNode[i] ) + (*pnZeros)++; + else + (*pnOnes)++; + } + nTotal += *pnDcs; + nTotal += *pnZeros; + nTotal += *pnOnes; + if ( fVerbose ) + { + printf( "Dc = %7.2f %% ", 100.0*(*pnDcs) /nTotal ); + printf( "On = %7.2f %% ", 100.0*(*pnOnes) /nTotal ); + printf( "Off = %7.2f %% ", 100.0*(*pnZeros)/nTotal ); + } +} + +/**Function************************************************************* + + Synopsis [Counts the number of patters of different type.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimCollectPatterns( Res_Sim_t * p, int fVerbose ) +{ + Abc_Obj_t * pObj; + unsigned char * pInfoCare, * pInfoNode, * pInfo; + int i, j; + pInfoCare = (unsigned char *)Vec_PtrEntry( p->vPats, Abc_NtkPo(p->pAig, 0)->Id ); + pInfoNode = (unsigned char *)Vec_PtrEntry( p->vPats, Abc_NtkPo(p->pAig, 1)->Id ); + for ( i = 0; i < p->nBytesIn; i++ ) + { + // skip don't-care patterns + if ( !pInfoCare[i] ) + continue; + // separate offset and onset patterns + assert( pInfoNode[i] == 0 || pInfoNode[i] == 0xff ); + if ( !pInfoNode[i] ) + { + if ( p->nPats0 >= p->nPats ) + continue; + Abc_NtkForEachPi( p->pAig, pObj, j ) + { + if ( j == p->nTruePis ) + break; + pInfo = (unsigned char *)Vec_PtrEntry( p->vPats, pObj->Id ); + assert( pInfo[i] == 0 || pInfo[i] == 0xff ); + if ( pInfo[i] ) + Abc_InfoSetBit( Vec_PtrEntry(p->vPats0, j), p->nPats0 ); + } + p->nPats0++; + } + else + { + if ( p->nPats1 >= p->nPats ) + continue; + Abc_NtkForEachPi( p->pAig, pObj, j ) + { + if ( j == p->nTruePis ) + break; + pInfo = (unsigned char *)Vec_PtrEntry( p->vPats, pObj->Id ); + assert( pInfo[i] == 0 || pInfo[i] == 0xff ); + if ( pInfo[i] ) + Abc_InfoSetBit( Vec_PtrEntry(p->vPats1, j), p->nPats1 ); + } + p->nPats1++; + } + if ( p->nPats0 >= p->nPats && p->nPats1 >= p->nPats ) + break; + } + if ( fVerbose ) + { + printf( "| " ); + printf( "On = %3d ", p->nPats1 ); + printf( "Off = %3d ", p->nPats0 ); + printf( "\n" ); + } +} + +/**Function************************************************************* + + Synopsis [Verifies the last pattern.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Res_SimVerifyValue( Res_Sim_t * p, int fOnSet ) +{ + Abc_Obj_t * pObj; + unsigned * pInfo, * pInfo2; + int i, value; + Abc_NtkForEachPi( p->pAig, pObj, i ) + { + if ( i == p->nTruePis ) + break; + if ( fOnSet ) + { + pInfo2 = Vec_PtrEntry( p->vPats1, i ); + value = Abc_InfoHasBit( pInfo2, p->nPats1 - 1 ); + } + else + { + pInfo2 = Vec_PtrEntry( p->vPats0, i ); + value = Abc_InfoHasBit( pInfo2, p->nPats0 - 1 ); + } + pInfo = Vec_PtrEntry( p->vPats, pObj->Id ); + pInfo[0] = value ? ~0 : 0; + } + Res_SimPerformRound( p, 1 ); + pObj = Abc_NtkPo( p->pAig, 1 ); + pInfo = Vec_PtrEntry( p->vPats, pObj->Id ); + assert( pInfo[0] == 0 || pInfo[0] == ~0 ); + return pInfo[0] > 0; +} + +/**Function************************************************************* + + Synopsis [Prepares simulation info for candidate filtering.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Res_SimPrepare( Res_Sim_t * p, Abc_Ntk_t * pAig, int nTruePis, int fVerbose ) +{ + int i, nOnes = 0, nZeros = 0, nDcs = 0; + if ( fVerbose ) + printf( "\n" ); + // prepare the manager + Res_SimAdjust( p, pAig, nTruePis ); + // estimate the number of patterns + Res_SimSetRandomBytes( p ); + Res_SimPerformRound( p, p->nWordsIn ); + Res_SimCountResults( p, &nDcs, &nOnes, &nZeros, fVerbose ); + // collect the patterns + Res_SimCollectPatterns( p, fVerbose ); + // add more patterns using constraint simulation + if ( p->nPats0 < 8 ) + { + if ( !Res_SatSimulate( p, 16, 0 ) ) + return p->fConst0 || p->fConst1; +// return 0; +// printf( "Value0 = %d\n", Res_SimVerifyValue( p, 0 ) ); + } + if ( p->nPats1 < 8 ) + { + if ( !Res_SatSimulate( p, 16, 1 ) ) + return p->fConst0 || p->fConst1; +// return 0; +// printf( "Value1 = %d\n", Res_SimVerifyValue( p, 1 ) ); + } + // generate additional patterns + for ( i = 0; i < 2; i++ ) + { + if ( p->nPats0 > p->nPats*7/8 && p->nPats1 > p->nPats*7/8 ) + break; + Res_SimSetDerivedBytes( p, i==0 ); + Res_SimPerformRound( p, p->nWordsIn ); + Res_SimCountResults( p, &nDcs, &nOnes, &nZeros, fVerbose ); + Res_SimCollectPatterns( p, fVerbose ); + } + // create bit-matrix info + if ( p->nPats0 < p->nPats ) + Res_SimPadSimInfo( p->vPats0, p->nPats0, p->nWords ); + if ( p->nPats1 < p->nPats ) + Res_SimPadSimInfo( p->vPats1, p->nPats1, p->nWords ); + // resimulate 0-patterns + Res_SimSetGiven( p, p->vPats0 ); + Res_SimPerformRound( p, p->nWords ); +//Res_SimPrintNodePatterns( p, pAig ); + Res_SimDeriveInfoReplicate( p ); + // resimulate 1-patterns + Res_SimSetGiven( p, p->vPats1 ); + Res_SimPerformRound( p, p->nWords ); +//Res_SimPrintNodePatterns( p, pAig ); + Res_SimDeriveInfoComplement( p ); + // print output patterns +// Res_SimPrintOutPatterns( p, pAig ); + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/res/resSim_old.c b/abc_with_bb_support/src/opt/res/resSim_old.c new file mode 100644 index 000000000..47c506f93 --- /dev/null +++ b/abc_with_bb_support/src/opt/res/resSim_old.c @@ -0,0 +1,521 @@ +/**CFile**************************************************************** + + FileName [resSim.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resynthesis package.] + + Synopsis [Simulation engine.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 15, 2007.] + + Revision [$Id: resSim.c,v 1.00 2007/01/15 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "resInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocate simulation engine.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Res_Sim_t * Res_SimAlloc( int nWords ) +{ + Res_Sim_t * p; + p = ALLOC( Res_Sim_t, 1 ); + memset( p, 0, sizeof(Res_Sim_t) ); + // simulation parameters + p->nWords = nWords; + p->nPats = 8 * sizeof(unsigned) * p->nWords; + p->nWordsOut = p->nPats * p->nWords; + p->nPatsOut = p->nPats * p->nPats; + // simulation info + p->vPats = Vec_PtrAllocSimInfo( 1024, p->nWords ); + p->vPats0 = Vec_PtrAllocSimInfo( 128, p->nWords ); + p->vPats1 = Vec_PtrAllocSimInfo( 128, p->nWords ); + p->vOuts = Vec_PtrAllocSimInfo( 128, p->nWordsOut ); + // resub candidates + p->vCands = Vec_VecStart( 16 ); + return p; +} + +/**Function************************************************************* + + Synopsis [Allocate simulation engine.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimAdjust( Res_Sim_t * p, Abc_Ntk_t * pAig ) +{ + srand( 0xABC ); + + assert( Abc_NtkIsStrash(pAig) ); + p->pAig = pAig; + if ( Vec_PtrSize(p->vPats) < Abc_NtkObjNumMax(pAig)+1 ) + { + Vec_PtrFree( p->vPats ); + p->vPats = Vec_PtrAllocSimInfo( Abc_NtkObjNumMax(pAig)+1, p->nWords ); + } + if ( Vec_PtrSize(p->vPats0) < Abc_NtkPiNum(pAig) ) + { + Vec_PtrFree( p->vPats0 ); + p->vPats0 = Vec_PtrAllocSimInfo( Abc_NtkPiNum(pAig), p->nWords ); + } + if ( Vec_PtrSize(p->vPats1) < Abc_NtkPiNum(pAig) ) + { + Vec_PtrFree( p->vPats1 ); + p->vPats1 = Vec_PtrAllocSimInfo( Abc_NtkPiNum(pAig), p->nWords ); + } + if ( Vec_PtrSize(p->vOuts) < Abc_NtkPoNum(pAig) ) + { + Vec_PtrFree( p->vOuts ); + p->vOuts = Vec_PtrAllocSimInfo( Abc_NtkPoNum(pAig), p->nWordsOut ); + } + // clean storage info for patterns + Abc_InfoClear( Vec_PtrEntry(p->vPats0,0), p->nWords * Abc_NtkPiNum(pAig) ); + Abc_InfoClear( Vec_PtrEntry(p->vPats1,0), p->nWords * Abc_NtkPiNum(pAig) ); + p->nPats0 = 0; + p->nPats1 = 0; +} + +/**Function************************************************************* + + Synopsis [Free simulation engine.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimFree( Res_Sim_t * p ) +{ + Vec_PtrFree( p->vPats ); + Vec_PtrFree( p->vPats0 ); + Vec_PtrFree( p->vPats1 ); + Vec_PtrFree( p->vOuts ); + Vec_VecFree( p->vCands ); + free( p ); +} + + +/**Function************************************************************* + + Synopsis [Sets random PI simulation info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimSetRandom( Res_Sim_t * p ) +{ + Abc_Obj_t * pObj; + unsigned * pInfo; + int i; + Abc_NtkForEachPi( p->pAig, pObj, i ) + { + pInfo = Vec_PtrEntry( p->vPats, pObj->Id ); + Abc_InfoRandom( pInfo, p->nWords ); + } +} + +/**Function************************************************************* + + Synopsis [Sets given PI simulation info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimSetGiven( Res_Sim_t * p, Vec_Ptr_t * vInfo ) +{ + Abc_Obj_t * pObj; + unsigned * pInfo, * pInfo2; + int i, w; + Abc_NtkForEachPi( p->pAig, pObj, i ) + { + pInfo = Vec_PtrEntry( p->vPats, pObj->Id ); + pInfo2 = Vec_PtrEntry( vInfo, i ); + for ( w = 0; w < p->nWords; w++ ) + pInfo[w] = pInfo2[w]; + } +} + +/**Function************************************************************* + + Synopsis [Simulates one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimPerformOne( Abc_Obj_t * pNode, Vec_Ptr_t * vSimInfo, int nSimWords ) +{ + unsigned * pInfo, * pInfo1, * pInfo2; + int k, fComp1, fComp2; + // simulate the internal nodes + assert( Abc_ObjIsNode(pNode) ); + pInfo = Vec_PtrEntry(vSimInfo, pNode->Id); + pInfo1 = Vec_PtrEntry(vSimInfo, Abc_ObjFaninId0(pNode)); + pInfo2 = Vec_PtrEntry(vSimInfo, Abc_ObjFaninId1(pNode)); + fComp1 = Abc_ObjFaninC0(pNode); + fComp2 = Abc_ObjFaninC1(pNode); + if ( fComp1 && fComp2 ) + for ( k = 0; k < nSimWords; k++ ) + pInfo[k] = ~pInfo1[k] & ~pInfo2[k]; + else if ( fComp1 && !fComp2 ) + for ( k = 0; k < nSimWords; k++ ) + pInfo[k] = ~pInfo1[k] & pInfo2[k]; + else if ( !fComp1 && fComp2 ) + for ( k = 0; k < nSimWords; k++ ) + pInfo[k] = pInfo1[k] & ~pInfo2[k]; + else // if ( fComp1 && fComp2 ) + for ( k = 0; k < nSimWords; k++ ) + pInfo[k] = pInfo1[k] & pInfo2[k]; +} + +/**Function************************************************************* + + Synopsis [Simulates one CO node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimTransferOne( Abc_Obj_t * pNode, Vec_Ptr_t * vSimInfo, int nSimWords ) +{ + unsigned * pInfo, * pInfo1; + int k, fComp1; + // simulate the internal nodes + assert( Abc_ObjIsCo(pNode) ); + pInfo = Vec_PtrEntry(vSimInfo, pNode->Id); + pInfo1 = Vec_PtrEntry(vSimInfo, Abc_ObjFaninId0(pNode)); + fComp1 = Abc_ObjFaninC0(pNode); + if ( fComp1 ) + for ( k = 0; k < nSimWords; k++ ) + pInfo[k] = ~pInfo1[k]; + else + for ( k = 0; k < nSimWords; k++ ) + pInfo[k] = pInfo1[k]; +} + +/**Function************************************************************* + + Synopsis [Performs one round of simulation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimPerformRound( Res_Sim_t * p ) +{ + Abc_Obj_t * pObj; + int i; + Abc_InfoFill( Vec_PtrEntry(p->vPats,0), p->nWords ); + Abc_AigForEachAnd( p->pAig, pObj, i ) + Res_SimPerformOne( pObj, p->vPats, p->nWords ); + Abc_NtkForEachPo( p->pAig, pObj, i ) + Res_SimTransferOne( pObj, p->vPats, p->nWords ); +} + +/**Function************************************************************* + + Synopsis [Processes simulation patterns.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimProcessPats( Res_Sim_t * p ) +{ + Abc_Obj_t * pObj; + unsigned * pInfoCare, * pInfoNode; + int i, j, nDcs = 0; + pInfoCare = Vec_PtrEntry( p->vPats, Abc_NtkPo(p->pAig, 0)->Id ); + pInfoNode = Vec_PtrEntry( p->vPats, Abc_NtkPo(p->pAig, 1)->Id ); + for ( i = 0; i < p->nPats; i++ ) + { + // skip don't-care patterns + if ( !Abc_InfoHasBit(pInfoCare, i) ) + { + nDcs++; + continue; + } + // separate offset and onset patterns + if ( !Abc_InfoHasBit(pInfoNode, i) ) + { + if ( p->nPats0 >= p->nPats ) + continue; + Abc_NtkForEachPi( p->pAig, pObj, j ) + if ( Abc_InfoHasBit( Vec_PtrEntry(p->vPats, pObj->Id), i ) ) + Abc_InfoSetBit( Vec_PtrEntry(p->vPats0, j), p->nPats0 ); + p->nPats0++; + } + else + { + if ( p->nPats1 >= p->nPats ) + continue; + Abc_NtkForEachPi( p->pAig, pObj, j ) + if ( Abc_InfoHasBit( Vec_PtrEntry(p->vPats, pObj->Id), i ) ) + Abc_InfoSetBit( Vec_PtrEntry(p->vPats1, j), p->nPats1 ); + p->nPats1++; + } + } +} + +/**Function************************************************************* + + Synopsis [Pads the extra space with duplicated simulation info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimPadSimInfo( Vec_Ptr_t * vPats, int nPats, int nWords ) +{ + unsigned * pInfo; + int i, w, iWords; + assert( nPats > 0 && nPats < nWords * 8 * (int) sizeof(unsigned) ); + // pad the first word + if ( nPats < 8 * sizeof(unsigned) ) + { + Vec_PtrForEachEntry( vPats, pInfo, i ) + if ( pInfo[0] & 1 ) + pInfo[0] |= ((~0) << nPats); + nPats = 8 * sizeof(unsigned); + } + // pad the empty words + iWords = nPats / (8 * sizeof(unsigned)); + Vec_PtrForEachEntry( vPats, pInfo, i ) + { + for ( w = iWords; w < nWords; w++ ) + pInfo[w] = pInfo[0]; + } +} + +/**Function************************************************************* + + Synopsis [Duplicates the simulation info to fill the space.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimDeriveInfoReplicate( Res_Sim_t * p ) +{ + unsigned * pInfo, * pInfo2; + Abc_Obj_t * pObj; + int i, j, w; + Abc_NtkForEachPo( p->pAig, pObj, i ) + { + pInfo = Vec_PtrEntry( p->vPats, pObj->Id ); + pInfo2 = Vec_PtrEntry( p->vOuts, i ); + for ( j = 0; j < p->nPats; j++ ) + for ( w = 0; w < p->nWords; w++ ) + *pInfo2++ = pInfo[w]; + } +} + +/**Function************************************************************* + + Synopsis [Complement the simulation info if necessary.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimDeriveInfoComplement( Res_Sim_t * p ) +{ + unsigned * pInfo, * pInfo2; + Abc_Obj_t * pObj; + int i, j, w; + Abc_NtkForEachPo( p->pAig, pObj, i ) + { + pInfo = Vec_PtrEntry( p->vPats, pObj->Id ); + pInfo2 = Vec_PtrEntry( p->vOuts, i ); + for ( j = 0; j < p->nPats; j++, pInfo2 += p->nWords ) + if ( Abc_InfoHasBit( pInfo, j ) ) + for ( w = 0; w < p->nWords; w++ ) + pInfo2[w] = ~pInfo2[w]; + } +} + +/**Function************************************************************* + + Synopsis [Free simulation engine.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimReportOne( Res_Sim_t * p ) +{ + unsigned * pInfoCare, * pInfoNode; + int i, nDcs, nOnes, nZeros; + pInfoCare = Vec_PtrEntry( p->vPats, Abc_NtkPo(p->pAig, 0)->Id ); + pInfoNode = Vec_PtrEntry( p->vPats, Abc_NtkPo(p->pAig, 1)->Id ); + nDcs = nOnes = nZeros = 0; + for ( i = 0; i < p->nPats; i++ ) + { + // skip don't-care patterns + if ( !Abc_InfoHasBit(pInfoCare, i) ) + { + nDcs++; + continue; + } + // separate offset and onset patterns + if ( !Abc_InfoHasBit(pInfoNode, i) ) + nZeros++; + else + nOnes++; + } + printf( "On = %3d (%7.2f %%) ", nOnes, 100.0*nOnes/p->nPats ); + printf( "Off = %3d (%7.2f %%) ", nZeros, 100.0*nZeros/p->nPats ); + printf( "Dc = %3d (%7.2f %%) ", nDcs, 100.0*nDcs/p->nPats ); + printf( "P0 = %3d ", p->nPats0 ); + printf( "P1 = %3d ", p->nPats1 ); + if ( p->nPats0 < 4 || p->nPats1 < 4 ) + printf( "*" ); + printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [Prints output patterns.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_SimPrintOutPatterns( Res_Sim_t * p, Abc_Ntk_t * pAig ) +{ + Abc_Obj_t * pObj; + unsigned * pInfo2; + int i; + Abc_NtkForEachPo( pAig, pObj, i ) + { + pInfo2 = Vec_PtrEntry( p->vOuts, i ); + Extra_PrintBinary( stdout, pInfo2, p->nPatsOut ); + printf( "\n" ); + } +} + +/**Function************************************************************* + + Synopsis [Prepares simulation info for candidate filtering.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Res_SimPrepare( Res_Sim_t * p, Abc_Ntk_t * pAig, int nTruePis, int fVerbose ) +{ + int Limit; + // prepare the manager + Res_SimAdjust( p, pAig ); + // collect 0/1 simulation info + for ( Limit = 0; Limit < 10; Limit++ ) + { + Res_SimSetRandom( p ); + Res_SimPerformRound( p ); + Res_SimProcessPats( p ); + if ( !(p->nPats0 < p->nPats || p->nPats1 < p->nPats) ) + break; + } +// printf( "%d ", Limit ); + // report the last set of patterns +// Res_SimReportOne( p ); +// printf( "\n" ); + // quit if there is not enough +// if ( p->nPats0 < 4 || p->nPats1 < 4 ) + if ( p->nPats0 < 4 || p->nPats1 < 4 ) + { +// Res_SimReportOne( p ); + return 0; + } + // create bit-matrix info + if ( p->nPats0 < p->nPats ) + Res_SimPadSimInfo( p->vPats0, p->nPats0, p->nWords ); + if ( p->nPats1 < p->nPats ) + Res_SimPadSimInfo( p->vPats1, p->nPats1, p->nWords ); + // resimulate 0-patterns + Res_SimSetGiven( p, p->vPats0 ); + Res_SimPerformRound( p ); + Res_SimDeriveInfoReplicate( p ); + // resimulate 1-patterns + Res_SimSetGiven( p, p->vPats1 ); + Res_SimPerformRound( p ); + Res_SimDeriveInfoComplement( p ); + // print output patterns +// Res_SimPrintOutPatterns( p, pAig ); + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/res/resStrash.c b/abc_with_bb_support/src/opt/res/resStrash.c new file mode 100644 index 000000000..71af59179 --- /dev/null +++ b/abc_with_bb_support/src/opt/res/resStrash.c @@ -0,0 +1,117 @@ +/**CFile**************************************************************** + + FileName [resStrash.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resynthesis package.] + + Synopsis [Structural hashing of the nodes in the window.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 15, 2007.] + + Revision [$Id: resStrash.c,v 1.00 2007/01/15 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "resInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +extern Abc_Obj_t * Abc_ConvertAigToAig( Abc_Ntk_t * pAig, Abc_Obj_t * pObjOld ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Structurally hashes the given window.] + + Description [The first PO is the observability condition. The second + is the node's function. The remaining POs are the candidate divisors.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Res_WndStrash( Res_Win_t * p ) +{ + Vec_Ptr_t * vPairs; + Abc_Ntk_t * pAig; + Abc_Obj_t * pObj, * pMiter; + int i; + assert( Abc_NtkHasAig(p->pNode->pNtk) ); +// Abc_NtkCleanCopy( p->pNode->pNtk ); + // create the network + pAig = Abc_NtkAlloc( ABC_NTK_STRASH, ABC_FUNC_AIG, 1 ); + pAig->pName = Extra_UtilStrsav( "window" ); + // create the inputs + Vec_PtrForEachEntry( p->vLeaves, pObj, i ) + pObj->pCopy = Abc_NtkCreatePi( pAig ); + Vec_PtrForEachEntry( p->vBranches, pObj, i ) + pObj->pCopy = Abc_NtkCreatePi( pAig ); + // go through the nodes in the topological order + Vec_PtrForEachEntry( p->vNodes, pObj, i ) + { + pObj->pCopy = Abc_ConvertAigToAig( pAig, pObj ); + if ( pObj == p->pNode ) + pObj->pCopy = Abc_ObjNot( pObj->pCopy ); + } + // collect the POs + vPairs = Vec_PtrAlloc( 2 * Vec_PtrSize(p->vRoots) ); + Vec_PtrForEachEntry( p->vRoots, pObj, i ) + { + Vec_PtrPush( vPairs, pObj->pCopy ); + Vec_PtrPush( vPairs, NULL ); + } + // mark the TFO of the node + Abc_NtkIncrementTravId( p->pNode->pNtk ); + Res_WinSweepLeafTfo_rec( p->pNode, (int)p->pNode->Level + p->nWinTfoMax ); + // update strashing of the node + p->pNode->pCopy = Abc_ObjNot( p->pNode->pCopy ); + Abc_NodeSetTravIdPrevious( p->pNode ); + // redo strashing in the TFO + Vec_PtrForEachEntry( p->vNodes, pObj, i ) + { + if ( Abc_NodeIsTravIdCurrent(pObj) ) + pObj->pCopy = Abc_ConvertAigToAig( pAig, pObj ); + } + // collect the POs + Vec_PtrForEachEntry( p->vRoots, pObj, i ) + Vec_PtrWriteEntry( vPairs, 2 * i + 1, pObj->pCopy ); + // add the miter + pMiter = Abc_AigMiter( pAig->pManFunc, vPairs ); + Abc_ObjAddFanin( Abc_NtkCreatePo(pAig), pMiter ); + Vec_PtrFree( vPairs ); + // add the node + Abc_ObjAddFanin( Abc_NtkCreatePo(pAig), p->pNode->pCopy ); + // add the fanins + Abc_ObjForEachFanin( p->pNode, pObj, i ) + Abc_ObjAddFanin( Abc_NtkCreatePo(pAig), pObj->pCopy ); + // add the divisors + Vec_PtrForEachEntry( p->vDivs, pObj, i ) + Abc_ObjAddFanin( Abc_NtkCreatePo(pAig), pObj->pCopy ); + // add the names + Abc_NtkAddDummyPiNames( pAig ); + Abc_NtkAddDummyPoNames( pAig ); + // check the resulting network + if ( !Abc_NtkCheck( pAig ) ) + fprintf( stdout, "Res_WndStrash(): Network check has failed.\n" ); + return pAig; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/res/resWin.c b/abc_with_bb_support/src/opt/res/resWin.c new file mode 100644 index 000000000..06d6afb25 --- /dev/null +++ b/abc_with_bb_support/src/opt/res/resWin.c @@ -0,0 +1,485 @@ +/**CFile**************************************************************** + + FileName [resWin.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resynthesis package.] + + Synopsis [Windowing algorithm.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 15, 2007.] + + Revision [$Id: resWin.c,v 1.00 2007/01/15 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "resInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates the window.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Res_Win_t * Res_WinAlloc() +{ + Res_Win_t * p; + // start the manager + p = ALLOC( Res_Win_t, 1 ); + memset( p, 0, sizeof(Res_Win_t) ); + // set internal parameters + p->nFanoutLimit = 10; + p->nLevTfiMinus = 3; + // allocate storage + p->vRoots = Vec_PtrAlloc( 256 ); + p->vLeaves = Vec_PtrAlloc( 256 ); + p->vBranches = Vec_PtrAlloc( 256 ); + p->vNodes = Vec_PtrAlloc( 256 ); + p->vDivs = Vec_PtrAlloc( 256 ); + p->vMatrix = Vec_VecStart( 128 ); + return p; +} + +/**Function************************************************************* + + Synopsis [Frees the window.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_WinFree( Res_Win_t * p ) +{ + Vec_PtrFree( p->vRoots ); + Vec_PtrFree( p->vLeaves ); + Vec_PtrFree( p->vBranches ); + Vec_PtrFree( p->vNodes ); + Vec_PtrFree( p->vDivs ); + Vec_VecFree( p->vMatrix ); + free( p ); +} + + + +/**Function************************************************************* + + Synopsis [Collect the limited TFI cone of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Res_WinCollectLeavesAndNodes( Res_Win_t * p ) +{ + Vec_Ptr_t * vFront; + Abc_Obj_t * pObj, * pTemp; + int i, k, m; + + assert( p->nWinTfiMax > 0 ); + assert( Vec_VecSize(p->vMatrix) > p->nWinTfiMax ); + + // start matrix with the node + Vec_VecClear( p->vMatrix ); + Vec_VecPush( p->vMatrix, 0, p->pNode ); + Abc_NtkIncrementTravId( p->pNode->pNtk ); + Abc_NodeSetTravIdCurrent( p->pNode ); + + // collect the leaves (nodes pTemp such that "p->pNode->Level - pTemp->Level > p->nWinTfiMax") + Vec_PtrClear( p->vLeaves ); + Vec_VecForEachLevelStartStop( p->vMatrix, vFront, i, 0, p->nWinTfiMax ) + { + Vec_PtrForEachEntry( vFront, pObj, k ) + { + Abc_ObjForEachFanin( pObj, pTemp, m ) + { + if ( Abc_NodeIsTravIdCurrent( pTemp ) ) + continue; + Abc_NodeSetTravIdCurrent( pTemp ); + if ( Abc_ObjIsCi(pTemp) || (int)(p->pNode->Level - pTemp->Level) > p->nWinTfiMax ) + Vec_PtrPush( p->vLeaves, pTemp ); + else + Vec_VecPush( p->vMatrix, p->pNode->Level - pTemp->Level, pTemp ); + } + } + } + if ( Vec_PtrSize(p->vLeaves) == 0 ) + return 0; + + // collect the nodes in the reverse order + Vec_PtrClear( p->vNodes ); + Vec_VecForEachLevelReverseStartStop( p->vMatrix, vFront, i, p->nWinTfiMax, 0 ) + { + Vec_PtrForEachEntry( vFront, pObj, k ) + Vec_PtrPush( p->vNodes, pObj ); + Vec_PtrClear( vFront ); + } + + // get the lowest leaf level + p->nLevLeafMin = ABC_INFINITY; + Vec_PtrForEachEntry( p->vLeaves, pObj, k ) + p->nLevLeafMin = ABC_MIN( p->nLevLeafMin, (int)pObj->Level ); + + // set minimum traversal level + p->nLevTravMin = ABC_MAX( ((int)p->pNode->Level) - p->nWinTfiMax - p->nLevTfiMinus, p->nLevLeafMin ); + assert( p->nLevTravMin >= 0 ); + return 1; +} + + + +/**Function************************************************************* + + Synopsis [Returns 1 if the node should be a root.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Res_WinComputeRootsCheck( Abc_Obj_t * pNode, int nLevelMax, int nFanoutLimit ) +{ + Abc_Obj_t * pFanout; + int i; + // the node is the root if one of the following is true: + // (1) the node has more than fanouts than the limit + if ( Abc_ObjFanoutNum(pNode) > nFanoutLimit ) + return 1; + // (2) the node has CO fanouts + // (3) the node has fanouts above the cutoff level + Abc_ObjForEachFanout( pNode, pFanout, i ) + if ( Abc_ObjIsCo(pFanout) || (int)pFanout->Level > nLevelMax ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Recursively collects the root candidates.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_WinComputeRoots_rec( Abc_Obj_t * pNode, int nLevelMax, int nFanoutLimit, Vec_Ptr_t * vRoots ) +{ + Abc_Obj_t * pFanout; + int i; + assert( Abc_ObjIsNode(pNode) ); + if ( Abc_NodeIsTravIdCurrent(pNode) ) + return; + Abc_NodeSetTravIdCurrent( pNode ); + // check if the node should be the root + if ( Res_WinComputeRootsCheck( pNode, nLevelMax, nFanoutLimit ) ) + Vec_PtrPush( vRoots, pNode ); + else // if not, explore its fanouts + Abc_ObjForEachFanout( pNode, pFanout, i ) + Res_WinComputeRoots_rec( pFanout, nLevelMax, nFanoutLimit, vRoots ); +} + +/**Function************************************************************* + + Synopsis [Recursively collects the root candidates.] + + Description [Returns 1 if the only root is this node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Res_WinComputeRoots( Res_Win_t * p ) +{ + Vec_PtrClear( p->vRoots ); + Abc_NtkIncrementTravId( p->pNode->pNtk ); + Res_WinComputeRoots_rec( p->pNode, p->pNode->Level + p->nWinTfoMax, p->nFanoutLimit, p->vRoots ); + assert( Vec_PtrSize(p->vRoots) > 0 ); + if ( Vec_PtrSize(p->vRoots) == 1 && Vec_PtrEntry(p->vRoots, 0) == p->pNode ) + return 0; + return 1; +} + + + +/**Function************************************************************* + + Synopsis [Marks the paths from the roots to the leaves.] + + Description [Returns 1 if the the node can reach a leaf.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Res_WinMarkPaths_rec( Abc_Obj_t * pNode, Abc_Obj_t * pPivot, int nLevelMin ) +{ + Abc_Obj_t * pFanin; + int i, RetValue; + // skip visited nodes + if ( Abc_NodeIsTravIdCurrent(pNode) ) + return 1; + if ( Abc_NodeIsTravIdPrevious(pNode) ) + return 0; + // assume that the node does not have access to the leaves + Abc_NodeSetTravIdPrevious( pNode ); + // skip nodes below the given level + if ( pNode == pPivot || (int)pNode->Level <= nLevelMin ) + return 0; + assert( Abc_ObjIsNode(pNode) ); + // check if the fanins have access to the leaves + RetValue = 0; + Abc_ObjForEachFanin( pNode, pFanin, i ) + RetValue |= Res_WinMarkPaths_rec( pFanin, pPivot, nLevelMin ); + // relabel the node if it has access to the leaves + if ( RetValue ) + Abc_NodeSetTravIdCurrent( pNode ); + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Marks the paths from the roots to the leaves.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_WinMarkPaths( Res_Win_t * p ) +{ + Abc_Obj_t * pObj; + int i; + // mark the leaves + Abc_NtkIncrementTravId( p->pNode->pNtk ); + Abc_NtkIncrementTravId( p->pNode->pNtk ); + Vec_PtrForEachEntry( p->vLeaves, pObj, i ) + Abc_NodeSetTravIdCurrent( pObj ); + // traverse from the roots and mark the nodes that can reach leaves + // the nodes that do not reach leaves have previous trav ID + // the nodes that reach leaves have current trav ID + Vec_PtrForEachEntry( p->vRoots, pObj, i ) + Res_WinMarkPaths_rec( pObj, p->pNode, p->nLevTravMin ); +} + + + + +/**Function************************************************************* + + Synopsis [Recursively collects the roots.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_WinFinalizeRoots_rec( Abc_Obj_t * pObj, Vec_Ptr_t * vRoots ) +{ + Abc_Obj_t * pFanout; + int i; + assert( Abc_ObjIsNode(pObj) ); + assert( Abc_NodeIsTravIdCurrent(pObj) ); + // check if the node has all fanouts marked + Abc_ObjForEachFanout( pObj, pFanout, i ) + if ( !Abc_NodeIsTravIdCurrent(pFanout) ) + break; + // if some of the fanouts are unmarked, add the node to the roots + if ( i < Abc_ObjFanoutNum(pObj) ) + Vec_PtrPushUnique( vRoots, pObj ); + else // otherwise, call recursively + Abc_ObjForEachFanout( pObj, pFanout, i ) + Res_WinFinalizeRoots_rec( pFanout, vRoots ); +} + +/**Function************************************************************* + + Synopsis [Finalizes the roots of the window.] + + Description [Roots of the window are the nodes that have at least + one fanout that it not in the TFO of the leaves.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Res_WinFinalizeRoots( Res_Win_t * p ) +{ + assert( !Abc_NodeIsTravIdCurrent(p->pNode) ); + // mark the node with the old traversal ID + Abc_NodeSetTravIdCurrent( p->pNode ); + // recollect the roots + Vec_PtrClear( p->vRoots ); + Res_WinFinalizeRoots_rec( p->pNode, p->vRoots ); + assert( Vec_PtrSize(p->vRoots) > 0 ); + if ( Vec_PtrSize(p->vRoots) == 1 && Vec_PtrEntry(p->vRoots, 0) == p->pNode ) + return 0; + return 1; +} + + +/**Function************************************************************* + + Synopsis [Recursively adds missing nodes and leaves.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_WinAddMissing_rec( Res_Win_t * p, Abc_Obj_t * pObj, int nLevTravMin ) +{ + Abc_Obj_t * pFanin; + int i; + // skip the already collected leaves, nodes, and branches + if ( Abc_NodeIsTravIdCurrent(pObj) ) + return; + // if this is not an internal node - make it a new branch + if ( !Abc_NodeIsTravIdPrevious(pObj) ) + { + assert( Vec_PtrFind(p->vLeaves, pObj) == -1 ); + Abc_NodeSetTravIdCurrent( pObj ); + Vec_PtrPush( p->vBranches, pObj ); + return; + } + assert( Abc_ObjIsNode(pObj) ); // if this is a CI, then the window is incorrect! + Abc_NodeSetTravIdCurrent( pObj ); + // visit the fanins of the node + Abc_ObjForEachFanin( pObj, pFanin, i ) + Res_WinAddMissing_rec( p, pFanin, nLevTravMin ); + // collect the node + Vec_PtrPush( p->vNodes, pObj ); +} + +/**Function************************************************************* + + Synopsis [Adds to the window nodes and leaves in the TFI of the roots.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Res_WinAddMissing( Res_Win_t * p ) +{ + Abc_Obj_t * pObj; + int i; + // mark the leaves + Abc_NtkIncrementTravId( p->pNode->pNtk ); + Vec_PtrForEachEntry( p->vLeaves, pObj, i ) + Abc_NodeSetTravIdCurrent( pObj ); + // mark the already collected nodes + Vec_PtrForEachEntry( p->vNodes, pObj, i ) + Abc_NodeSetTravIdCurrent( pObj ); + // explore from the roots + Vec_PtrClear( p->vBranches ); + Vec_PtrForEachEntry( p->vRoots, pObj, i ) + Res_WinAddMissing_rec( p, pObj, p->nLevTravMin ); +} + + + + +/**Function************************************************************* + + Synopsis [Returns 1 if the window is trivial (without TFO).] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Res_WinIsTrivial( Res_Win_t * p ) +{ + return Vec_PtrSize(p->vRoots) == 1 && Vec_PtrEntry(p->vRoots, 0) == p->pNode; +} + +/**Function************************************************************* + + Synopsis [Computes the window.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Res_WinCompute( Abc_Obj_t * pNode, int nWinTfiMax, int nWinTfoMax, Res_Win_t * p ) +{ + assert( Abc_ObjIsNode(pNode) ); + assert( nWinTfiMax > 0 && nWinTfiMax < 10 ); + assert( nWinTfoMax >= 0 && nWinTfoMax < 10 ); + + // initialize the window + p->pNode = pNode; + p->nWinTfiMax = nWinTfiMax; + p->nWinTfoMax = nWinTfoMax; + + Vec_PtrClear( p->vBranches ); + Vec_PtrClear( p->vDivs ); + Vec_PtrClear( p->vRoots ); + Vec_PtrPush( p->vRoots, pNode ); + + // compute the leaves + if ( !Res_WinCollectLeavesAndNodes( p ) ) + return 0; + + // compute the candidate roots + if ( p->nWinTfoMax > 0 && Res_WinComputeRoots(p) ) + { + // mark the paths from the roots to the leaves + Res_WinMarkPaths( p ); + // refine the roots and add branches and missing nodes + if ( Res_WinFinalizeRoots( p ) ) + Res_WinAddMissing( p ); + } + + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/res/res_.c b/abc_with_bb_support/src/opt/res/res_.c new file mode 100644 index 000000000..9c49508a6 --- /dev/null +++ b/abc_with_bb_support/src/opt/res/res_.c @@ -0,0 +1,50 @@ +/**CFile**************************************************************** + + FileName [res_.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resynthesis package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 15, 2007.] + + Revision [$Id: res_.c,v 1.00 2007/01/15 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "res.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/ret/module.make b/abc_with_bb_support/src/opt/ret/module.make new file mode 100644 index 000000000..921abcfb9 --- /dev/null +++ b/abc_with_bb_support/src/opt/ret/module.make @@ -0,0 +1,8 @@ +SRC += src/opt/ret/retArea.c \ + src/opt/ret/retCore.c \ + src/opt/ret/retDelay.c \ + src/opt/ret/retFlow.c \ + src/opt/ret/retIncrem.c \ + src/opt/ret/retInit.c \ + src/opt/ret/retLvalue.c + diff --git a/abc_with_bb_support/src/opt/ret/retArea.c b/abc_with_bb_support/src/opt/ret/retArea.c new file mode 100644 index 000000000..f8249c14e --- /dev/null +++ b/abc_with_bb_support/src/opt/ret/retArea.c @@ -0,0 +1,540 @@ +/**CFile**************************************************************** + + FileName [retArea.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Retiming package.] + + Synopsis [Min-area retiming.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Oct 31, 2006.] + + Revision [$Id: retArea.c,v 1.00 2006/10/31 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "retInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Abc_Ntk_t * Abc_NtkRetimeMinAreaOne( Abc_Ntk_t * pNtk, int fForward, int fVerbose ); +static void Abc_NtkRetimeMinAreaPrepare( Abc_Ntk_t * pNtk, int fForward ); +static void Abc_NtkRetimeMinAreaInitValues( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMinCut ); +static Abc_Ntk_t * Abc_NtkRetimeMinAreaConstructNtk( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMinCut ); +static void Abc_NtkRetimeMinAreaUpdateLatches( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMinCut, int fForward ); + +extern Abc_Ntk_t * Abc_NtkAttachBottom( Abc_Ntk_t * pNtkTop, Abc_Ntk_t * pNtkBottom ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs min-area retiming.] + + Description [Returns the number of latches reduced.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRetimeMinArea( Abc_Ntk_t * pNtk, int fForwardOnly, int fBackwardOnly, int fVerbose ) +{ + Abc_Ntk_t * pNtkTotal = NULL, * pNtkBottom; + Vec_Int_t * vValuesNew = NULL, * vValues; + int nLatches = Abc_NtkLatchNum(pNtk); + int fOneFrame = 0; + assert( !fForwardOnly || !fBackwardOnly ); + // there should not be black boxes + assert( Abc_NtkIsSopLogic(pNtk) ); + assert( Abc_NtkLatchNum(pNtk) == Vec_PtrSize(pNtk->vBoxes) ); + // reorder CI/CO/latch inputs + Abc_NtkOrderCisCos( pNtk ); + // perform forward retiming + if ( !fBackwardOnly ) + { + if ( fOneFrame ) + Abc_NtkRetimeMinAreaOne( pNtk, 1, fVerbose ); + else + while ( Abc_NtkRetimeMinAreaOne( pNtk, 1, fVerbose ) ); + } + // remember initial values + vValues = Abc_NtkCollectLatchValues( pNtk ); + // perform backward retiming + if ( !fForwardOnly ) + { + if ( fOneFrame ) + pNtkTotal = Abc_NtkRetimeMinAreaOne( pNtk, 0, fVerbose ); + else + while ( pNtkBottom = Abc_NtkRetimeMinAreaOne( pNtk, 0, fVerbose ) ) + pNtkTotal = Abc_NtkAttachBottom( pNtkTotal, pNtkBottom ); + } + // compute initial values + vValuesNew = Abc_NtkRetimeInitialValues( pNtkTotal, vValues, fVerbose ); + if ( pNtkTotal ) Abc_NtkDelete( pNtkTotal ); + // insert new initial values + Abc_NtkInsertLatchValues( pNtk, vValuesNew ); + if ( vValuesNew ) Vec_IntFree( vValuesNew ); + if ( vValues ) Vec_IntFree( vValues ); + // fix the COs (this changes the circuit structure) +// Abc_NtkLogicMakeSimpleCos( pNtk, 0 ); + // check for correctness + if ( !Abc_NtkCheck( pNtk ) ) + fprintf( stdout, "Abc_NtkRetimeMinArea(): Network check has failed.\n" ); + // return the number of latches saved + return nLatches - Abc_NtkLatchNum(pNtk); +} + +/**Function************************************************************* + + Synopsis [Performs min-area retiming backward.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkRetimeMinAreaOne( Abc_Ntk_t * pNtk, int fForward, int fVerbose ) +{ + Abc_Ntk_t * pNtkNew = NULL; + Vec_Ptr_t * vMinCut; + int nLatches = Abc_NtkLatchNum(pNtk); + // mark current latches and TFI(POs) + Abc_NtkRetimeMinAreaPrepare( pNtk, fForward ); + // run the maximum forward flow + vMinCut = Abc_NtkMaxFlow( pNtk, fForward, fVerbose ); +// assert( Vec_PtrSize(vMinCut) <= Abc_NtkLatchNum(pNtk) ); + // create new latch boundary if there is improvement + if ( Vec_PtrSize(vMinCut) < Abc_NtkLatchNum(pNtk) ) + { + pNtkNew = (Abc_Ntk_t *)1; + if ( fForward ) + Abc_NtkRetimeMinAreaInitValues( pNtk, vMinCut ); + else + pNtkNew = Abc_NtkRetimeMinAreaConstructNtk( pNtk, vMinCut ); + Abc_NtkRetimeMinAreaUpdateLatches( pNtk, vMinCut, fForward ); + } + // clean up + Vec_PtrFree( vMinCut ); + Abc_NtkCleanMarkA( pNtk ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Marks the cone with MarkA.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMarkCone_rec( Abc_Obj_t * pObj, int fForward ) +{ + Abc_Obj_t * pNext; + int i; + if ( pObj->fMarkA ) + return; + pObj->fMarkA = 1; + if ( fForward ) + { + Abc_ObjForEachFanout( pObj, pNext, i ) + Abc_NtkMarkCone_rec( pNext, fForward ); + } + else + { + Abc_ObjForEachFanin( pObj, pNext, i ) + Abc_NtkMarkCone_rec( pNext, fForward ); + } +} + +/**Function************************************************************* + + Synopsis [Marks the cone with MarkA.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkUnmarkCone_rec( Abc_Obj_t * pObj, int fForward ) +{ + Abc_Obj_t * pNext; + int i; + if ( !pObj->fMarkA || Abc_ObjIsLatch(pObj) ) + return; + pObj->fMarkA = 0; + if ( fForward ) + { + Abc_ObjForEachFanout( pObj, pNext, i ) + Abc_NtkUnmarkCone_rec( pNext, fForward ); + } + else + { + Abc_ObjForEachFanin( pObj, pNext, i ) + Abc_NtkUnmarkCone_rec( pNext, fForward ); + } +} + +/**Function************************************************************* + + Synopsis [Prepares the network for running MaxFlow.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkRetimeMinAreaPrepare( Abc_Ntk_t * pNtk, int fForward ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj, * pFanin; + int i, k; + if ( fForward ) + { + // mark the frontier + Abc_NtkForEachPo( pNtk, pObj, i ) + pObj->fMarkA = 1; + Abc_NtkForEachLatch( pNtk, pObj, i ) + { + pObj->fMarkA = 1; + Abc_ObjFanin0(pObj)->fMarkA = 1; + } + // mark the nodes reachable from the PIs + Abc_NtkForEachPi( pNtk, pObj, i ) + Abc_NtkMarkCone_rec( pObj, fForward ); + // collect the unmarked fanins of the marked nodes + vNodes = Vec_PtrAlloc( 100 ); + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( pObj->fMarkA ) + Abc_ObjForEachFanin( pObj, pFanin, k ) + if ( !pFanin->fMarkA ) + Vec_PtrPush( vNodes, pFanin ); + // mark these nodes + Vec_PtrForEachEntry( vNodes, pObj, i ) + pObj->fMarkA = 1; + Vec_PtrFree( vNodes ); + } + else + { + // mark the frontier + Abc_NtkForEachPi( pNtk, pObj, i ) + pObj->fMarkA = 1; + Abc_NtkForEachLatch( pNtk, pObj, i ) + { + pObj->fMarkA = 1; + Abc_ObjFanout0(pObj)->fMarkA = 1; + } + // mark the nodes reachable from the POs + Abc_NtkForEachPo( pNtk, pObj, i ) + Abc_NtkMarkCone_rec( pObj, fForward ); + } +} + +/**Function************************************************************* + + Synopsis [Compute initial values.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRetimeMinAreaInitValues_rec( Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanin; + int i; + // skip visited nodes + if ( Abc_NodeIsTravIdCurrent(pObj) ) + return (int)pObj->pCopy; + Abc_NodeSetTravIdCurrent(pObj); + // consider the case of a latch output + if ( Abc_ObjIsBo(pObj) ) + { + assert( Abc_ObjIsLatch(Abc_ObjFanin0(pObj)) ); + pObj->pCopy = (void *)Abc_NtkRetimeMinAreaInitValues_rec( Abc_ObjFanin0(pObj) ); + return (int)pObj->pCopy; + } + assert( Abc_ObjIsNode(pObj) ); + // visit the fanins + Abc_ObjForEachFanin( pObj, pFanin, i ) + Abc_NtkRetimeMinAreaInitValues_rec( pFanin ); + // compute the value of the node + pObj->pCopy = (void *)Abc_ObjSopSimulate(pObj); + return (int)pObj->pCopy; +} + +/**Function************************************************************* + + Synopsis [Compute initial values.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkRetimeMinAreaInitValues( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMinCut ) +{ + Abc_Obj_t * pObj; + int i; + // transfer initial values to pCopy and mark the latches + Abc_NtkIncrementTravId(pNtk); + Abc_NtkForEachLatch( pNtk, pObj, i ) + { + pObj->pCopy = (void *)Abc_LatchIsInit1(pObj); + Abc_NodeSetTravIdCurrent( pObj ); + } + // propagate initial values + Vec_PtrForEachEntry( vMinCut, pObj, i ) + Abc_NtkRetimeMinAreaInitValues_rec( pObj ); + // unmark the latches + Abc_NtkForEachLatch( pNtk, pObj, i ) + Abc_NodeSetTravIdPrevious( pObj ); +} + +/**Function************************************************************* + + Synopsis [Performs min-area retiming backward.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Obj_t * Abc_NtkRetimeMinAreaConstructNtk_rec( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanin; + int i; + // skip visited nodes + if ( Abc_NodeIsTravIdCurrent(pObj) ) + return pObj->pCopy; + Abc_NodeSetTravIdCurrent(pObj); + // consider the case of a latch output + if ( Abc_ObjIsBi(pObj) ) + { + pObj->pCopy = Abc_NtkRetimeMinAreaConstructNtk_rec( pNtkNew, Abc_ObjFanin0(pObj) ); + return pObj->pCopy; + } + assert( Abc_ObjIsNode(pObj) ); + // visit the fanins + Abc_ObjForEachFanin( pObj, pFanin, i ) + Abc_NtkRetimeMinAreaConstructNtk_rec( pNtkNew, pFanin ); + // compute the value of the node + Abc_NtkDupObj( pNtkNew, pObj, 0 ); + Abc_ObjForEachFanin( pObj, pFanin, i ) + Abc_ObjAddFanin( pObj->pCopy, pFanin->pCopy ); + return pObj->pCopy; +} + +/**Function************************************************************* + + Synopsis [Creates the network from computing initial state.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkRetimeMinAreaConstructNtk( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMinCut ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj, * pObjNew; + int i; + // create new network + pNtkNew = Abc_NtkAlloc( ABC_NTK_LOGIC, ABC_FUNC_SOP, 1 ); + // map new latches into PIs of the new network + Abc_NtkIncrementTravId(pNtk); + Vec_PtrForEachEntry( vMinCut, pObj, i ) + { + pObj->pCopy = Abc_NtkCreatePi(pNtkNew); + Abc_NodeSetTravIdCurrent( pObj ); + } + // construct the network recursively + Abc_NtkForEachLatch( pNtk, pObj, i ) + { + pObjNew = Abc_NtkRetimeMinAreaConstructNtk_rec( pNtkNew, Abc_ObjFanin0(pObj) ); + Abc_ObjAddFanin( Abc_NtkCreatePo(pNtkNew), pObjNew ); + } + // unmark the nodes in the cut + Vec_PtrForEachEntry( vMinCut, pObj, i ) + Abc_NodeSetTravIdPrevious( pObj ); + // unmark the latches + Abc_NtkForEachLatch( pNtk, pObj, i ) + Abc_NodeSetTravIdPrevious( pObj ); + // assign dummy node names + Abc_NtkAddDummyPiNames( pNtkNew ); + Abc_NtkAddDummyPoNames( pNtkNew ); + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkRetimeMinAreaConstructNtk(): Network check has failed.\n" ); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Updates the network after backward retiming.] + + Description [Assumes that fMarkA denotes all nodes reachabe from + the latches toward the cut.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkRetimeMinAreaUpdateLatches( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMinCut, int fForward ) +{ + Vec_Ptr_t * vCis, * vCos, * vBoxes, * vBoxesNew, * vNodes, * vBuffers; + Abc_Obj_t * pObj, * pLatch, * pLatchIn, * pLatchOut, * pNext, * pBuffer; + int i, k; + // create new latches + Vec_PtrShrink( pNtk->vCis, Abc_NtkCiNum(pNtk) - Abc_NtkLatchNum(pNtk) ); + Vec_PtrShrink( pNtk->vCos, Abc_NtkCoNum(pNtk) - Abc_NtkLatchNum(pNtk) ); + vCis = pNtk->vCis; pNtk->vCis = NULL; + vCos = pNtk->vCos; pNtk->vCos = NULL; + vBoxes = pNtk->vBoxes; pNtk->vBoxes = NULL; + // transfer boxes + vBoxesNew = Vec_PtrAlloc(100); + Vec_PtrForEachEntry( vBoxes, pObj, i ) + if ( !Abc_ObjIsLatch(pObj) ) + Vec_PtrPush( vBoxesNew, pObj ); + // create or reuse latches + vNodes = Vec_PtrAlloc( 100 ); + vBuffers = Vec_PtrAlloc( 100 ); + Vec_PtrForEachEntry( vMinCut, pObj, i ) + { + if ( Abc_ObjIsCi(pObj) && fForward ) + { + pLatchOut = pObj; + pLatch = Abc_ObjFanin0(pLatchOut); + pLatchIn = Abc_ObjFanin0(pLatch); + assert( Abc_ObjIsBo(pLatchOut) && Abc_ObjIsLatch(pLatch) && Abc_ObjIsBi(pLatchIn) ); + // mark the latch as reused + Abc_NodeSetTravIdCurrent( pLatch ); + + // check if there are marked fanouts + // (these are fanouts to be connected to the latch input) + Abc_ObjForEachFanout( pObj, pNext, k ) + if ( pNext->fMarkA ) + break; + if ( k < Abc_ObjFanoutNum(pObj) ) + { + // add the buffer + pBuffer = Abc_NtkCreateNodeBuf( pNtk, Abc_ObjFanin0(pLatchIn) ); + Abc_ObjPatchFanin( pLatchIn, Abc_ObjFanin0(pLatchIn), pBuffer ); + Vec_PtrPush( vBuffers, pBuffer ); + // redirect edges to the unvisited fanouts of the node + Abc_NodeCollectFanouts( pObj, vNodes ); + Vec_PtrForEachEntry( vNodes, pNext, k ) + if ( pNext->fMarkA ) + Abc_ObjPatchFanin( pNext, pObj, pBuffer ); + } + assert( Abc_ObjFanoutNum(pObj) > 0 ); +// if ( Abc_ObjFanoutNum(pObj) == 0 ) +// Abc_NtkDeleteObj_rec( pObj, 0 ); + } + else if ( Abc_ObjIsCo(pObj) && !fForward ) + { + pLatchIn = pObj; + pLatch = Abc_ObjFanout0(pLatchIn); + pLatchOut = Abc_ObjFanout0(pLatch); + assert( Abc_ObjIsBo(pLatchOut) && Abc_ObjIsLatch(pLatch) && Abc_ObjIsBi(pLatchIn) ); + // mark the latch as reused + Abc_NodeSetTravIdCurrent( pLatch ); + assert( !Abc_ObjFanin0(pLatchIn)->fMarkA ); + } + else + { + pLatchOut = Abc_NtkCreateBo(pNtk); + pLatch = Abc_NtkCreateLatch(pNtk); + pLatchIn = Abc_NtkCreateBi(pNtk); + Abc_ObjAssignName( pLatchOut, Abc_ObjName(pLatch), "_out" ); + Abc_ObjAssignName( pLatchIn, Abc_ObjName(pLatch), "_in" ); + // connect + Abc_ObjAddFanin( pLatchOut, pLatch ); + Abc_ObjAddFanin( pLatch, pLatchIn ); + if ( fForward ) + { + pLatch->pData = (void *)(pObj->pCopy? ABC_INIT_ONE : ABC_INIT_ZERO); + // redirect edges to the unvisited fanouts of the node + Abc_NodeCollectFanouts( pObj, vNodes ); + Vec_PtrForEachEntry( vNodes, pNext, k ) + if ( !pNext->fMarkA ) + Abc_ObjPatchFanin( pNext, pObj, pLatchOut ); + } + else + { + // redirect edges to the visited fanouts of the node + Abc_NodeCollectFanouts( pObj, vNodes ); + Vec_PtrForEachEntry( vNodes, pNext, k ) + if ( pNext->fMarkA ) + Abc_ObjPatchFanin( pNext, pObj, pLatchOut ); + } + // connect latch to the node + Abc_ObjAddFanin( pLatchIn, pObj ); + } + Vec_PtrPush( vCis, pLatchOut ); + Vec_PtrPush( vBoxesNew, pLatch ); + Vec_PtrPush( vCos, pLatchIn ); + } + Vec_PtrFree( vNodes ); + // remove buffers + Vec_PtrForEachEntry( vBuffers, pObj, i ) + { + Abc_ObjTransferFanout( pObj, Abc_ObjFanin0(pObj) ); + Abc_NtkDeleteObj( pObj ); + } + Vec_PtrFree( vBuffers ); + // remove useless latches + Vec_PtrForEachEntry( vBoxes, pObj, i ) + { + if ( !Abc_ObjIsLatch(pObj) ) + continue; + if ( Abc_NodeIsTravIdCurrent(pObj) ) + continue; + pLatchOut = Abc_ObjFanout0(pObj); + pLatch = pObj; + pLatchIn = Abc_ObjFanin0(pObj); + if ( Abc_ObjFanoutNum(pLatchOut) > 0 ) + Abc_ObjTransferFanout( pLatchOut, Abc_ObjFanin0(pLatchIn) ); + Abc_NtkDeleteObj( pLatchOut ); + Abc_NtkDeleteObj( pObj ); + Abc_NtkDeleteObj( pLatchIn ); + } + // set the arrays + pNtk->vCis = vCis; + pNtk->vCos = vCos; + pNtk->vBoxes = vBoxesNew; + Vec_PtrFree( vBoxes ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/ret/retCore.c b/abc_with_bb_support/src/opt/ret/retCore.c new file mode 100644 index 000000000..02b2a2d9c --- /dev/null +++ b/abc_with_bb_support/src/opt/ret/retCore.c @@ -0,0 +1,132 @@ +/**CFile**************************************************************** + + FileName [retCore.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Retiming package.] + + Synopsis [The core retiming procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Oct 31, 2006.] + + Revision [$Id: retCore.c,v 1.00 2006/10/31 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "retInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +int timeRetime = 0; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Implementation of retiming.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRetime( Abc_Ntk_t * pNtk, int Mode, int fForwardOnly, int fBackwardOnly, int fVerbose ) +{ + int nLatches = Abc_NtkLatchNum(pNtk); + int nLevels = Abc_NtkLevel(pNtk); + int RetValue = 0, clkTotal = clock(); + int nNodesOld, nLatchesOld; + assert( Mode > 0 && Mode < 7 ); + assert( !fForwardOnly || !fBackwardOnly ); + + // cleanup the network + nNodesOld = Abc_NtkNodeNum(pNtk); + nLatchesOld = Abc_NtkLatchNum(pNtk); + Abc_NtkCleanupSeq(pNtk, 0, 0, 0); + if ( nNodesOld > Abc_NtkNodeNum(pNtk) || nLatchesOld > Abc_NtkLatchNum(pNtk) ) + printf( "Cleanup before retiming removed %d dangling nodes and %d dangling latches.\n", + nNodesOld - Abc_NtkNodeNum(pNtk), nLatchesOld - Abc_NtkLatchNum(pNtk) ); + + // perform retiming + switch ( Mode ) + { + case 1: // forward + RetValue = Abc_NtkRetimeIncremental( pNtk, 1, 0, fVerbose ); + break; + case 2: // backward + RetValue = Abc_NtkRetimeIncremental( pNtk, 0, 0, fVerbose ); + break; + case 3: // min-area + RetValue = Abc_NtkRetimeMinArea( pNtk, fForwardOnly, fBackwardOnly, fVerbose ); + break; + case 4: // min-delay + if ( !fBackwardOnly ) + RetValue += Abc_NtkRetimeIncremental( pNtk, 1, 1, fVerbose ); + if ( !fForwardOnly ) + RetValue += Abc_NtkRetimeIncremental( pNtk, 0, 1, fVerbose ); + break; + case 5: // min-area + min-delay + RetValue = Abc_NtkRetimeMinArea( pNtk, fForwardOnly, fBackwardOnly, fVerbose ); + if ( !fBackwardOnly ) + RetValue += Abc_NtkRetimeIncremental( pNtk, 1, 1, fVerbose ); + if ( !fForwardOnly ) + RetValue += Abc_NtkRetimeIncremental( pNtk, 0, 1, fVerbose ); + break; + case 6: // Pan's algorithm + RetValue = Abc_NtkRetimeLValue( pNtk, 500, fVerbose ); + break; + default: + printf( "Unknown retiming option.\n" ); + break; + } + if ( fVerbose ) + { + printf( "Reduction in area = %3d. Reduction in delay = %3d. ", + nLatches - Abc_NtkLatchNum(pNtk), nLevels - Abc_NtkLevel(pNtk) ); + PRT( "Total runtime", clock() - clkTotal ); + } + timeRetime = clock() - clkTotal; + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Used for automated debugging.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRetimeDebug( Abc_Ntk_t * pNtk ) +{ + extern int Abc_NtkSecFraig( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int nSeconds, int nFrames, int fVerbose ); + Abc_Ntk_t * pNtkRet; + assert( Abc_NtkIsLogic(pNtk) ); + Abc_NtkToSop( pNtk, 0 ); +// if ( !Abc_NtkCheck( pNtk ) ) +// fprintf( stdout, "Abc_NtkRetimeDebug(): Network check has failed.\n" ); +// Io_WriteBlifLogic( pNtk, "debug_temp.blif", 1 ); + pNtkRet = Abc_NtkDup( pNtk ); + Abc_NtkRetime( pNtkRet, 3, 0, 1, 0 ); // debugging backward flow + return !Abc_NtkSecFraig( pNtk, pNtkRet, 10000, 3, 0 ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/ret/retDelay.c b/abc_with_bb_support/src/opt/ret/retDelay.c new file mode 100644 index 000000000..a2512413e --- /dev/null +++ b/abc_with_bb_support/src/opt/ret/retDelay.c @@ -0,0 +1,305 @@ +/**CFile**************************************************************** + + FileName [retDelay.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Retiming package.] + + Synopsis [Incremental retiming for optimum delay.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Oct 31, 2006.] + + Revision [$Id: retDelay.c,v 1.00 2006/10/31 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "retInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Abc_NtkRetimeMinDelayTry( Abc_Ntk_t * pNtk, int fForward, int fInitial, int nIterLimit, int * pIterBest, int fVerbose ); +static int Abc_NtkRetimeTiming( Abc_Ntk_t * pNtk, int fForward, Vec_Ptr_t * vCritical ); +static int Abc_NtkRetimeTiming_rec( Abc_Obj_t * pObj, int fForward ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Retimes incrementally for minimum delay.] + + Description [This procedure cannot be called in the application code + because it assumes that the network is preprocessed by removing LIs/LOs.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRetimeMinDelay( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkCopy, int nIterLimit, int fForward, int fVerbose ) +{ + int IterBest, DelayBest; + int IterBest2, DelayBest2; + // try to find the best delay iteration on a copy + DelayBest = Abc_NtkRetimeMinDelayTry( pNtkCopy, fForward, 0, nIterLimit, &IterBest, fVerbose ); + if ( IterBest == 0 ) + return 1; + // perform the given number of iterations on the original network + DelayBest2 = Abc_NtkRetimeMinDelayTry( pNtk, fForward, 1, IterBest, &IterBest2, fVerbose ); + assert( DelayBest == DelayBest2 ); + assert( IterBest == IterBest2 ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Returns the best delay and the number of best iteration.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRetimeMinDelayTry( Abc_Ntk_t * pNtk, int fForward, int fInitial, int nIterLimit, int * pIterBest, int fVerbose ) +{ + Abc_Ntk_t * pNtkNew = NULL; + Vec_Ptr_t * vCritical; + Vec_Int_t * vValues; + Abc_Obj_t * pObj; + int i, k, IterBest, DelayCur, DelayBest, DelayStart, LatchesBest; + // transfer intitial values + if ( fInitial ) + { + if ( fForward ) + Abc_NtkRetimeTranferToCopy( pNtk ); + else + { + // save initial value of the latches + vValues = Abc_NtkRetimeCollectLatchValues( pNtk ); + // start the network for initial value computation + pNtkNew = Abc_NtkRetimeBackwardInitialStart( pNtk ); + } + } + +if ( fVerbose && !fInitial ) + printf( "Performing analysis:\n" ); + // find the best iteration + DelayBest = ABC_INFINITY; IterBest = 0; LatchesBest = Abc_NtkLatchNum(pNtk); + vCritical = Vec_PtrAlloc( 100 ); + for ( i = 0; ; i++ ) + { + // perform moves for the timing-critical nodes + DelayCur = Abc_NtkRetimeTiming( pNtk, fForward, vCritical ); + if ( i == 0 ) + DelayStart = DelayCur; + // record this position if it has the best delay + if ( DelayBest > DelayCur ) + { +if ( fVerbose && !fInitial ) + printf( "%s Iter = %3d. Delay = %3d. Latches = %5d. Delta = %6.2f. Ratio = %4.2f %%\n", + fForward ? "Fwd": "Bwd", i, DelayCur, Abc_NtkLatchNum(pNtk), + 1.0*(Abc_NtkLatchNum(pNtk)-LatchesBest)/(DelayBest-DelayCur), + 100.0*(Abc_NtkLatchNum(pNtk)-LatchesBest)/Abc_NtkLatchNum(pNtk)/(DelayBest-DelayCur) ); + + DelayBest = DelayCur; + IterBest = i; + LatchesBest = Abc_NtkLatchNum(pNtk); + } + // quit after timing analysis + if ( i == nIterLimit ) + break; + // skip if 10 interations did not give improvement + if ( i - IterBest > 20 ) + break; + // try retiming to improve the delay + Vec_PtrForEachEntry( vCritical, pObj, k ) + if ( Abc_NtkRetimeNodeIsEnabled(pObj, fForward) ) + Abc_NtkRetimeNode( pObj, fForward, fInitial ); + // share latches + if ( !fForward ) + Abc_NtkRetimeShareLatches( pNtk, fInitial ); + } + Vec_PtrFree( vCritical ); + // transfer the initial state back to the latches + if ( fInitial ) + { + if ( fForward ) + Abc_NtkRetimeTranferFromCopy( pNtk ); + else + { + Abc_NtkRetimeBackwardInitialFinish( pNtk, pNtkNew, vValues, fVerbose ); + Abc_NtkDelete( pNtkNew ); + Vec_IntFree( vValues ); + } + } +if ( fVerbose && !fInitial ) + printf( "%s : Starting delay = %3d. Final delay = %3d. IterBest = %2d (out of %2d).\n", + fForward? "Forward " : "Backward", DelayStart, DelayBest, IterBest, nIterLimit ); + *pIterBest = IterBest; + return DelayBest; +} + +/**Function************************************************************* + + Synopsis [Returns the set of timing-critical nodes.] + + Description [Performs static timing analysis on the network. Uses + unit-delay model.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRetimeTiming( Abc_Ntk_t * pNtk, int fForward, Vec_Ptr_t * vCritical ) +{ + Vec_Ptr_t * vLatches; + Abc_Obj_t * pObj, * pNext; + int i, k, LevelCur, LevelMax = 0; + // mark all objects except nodes + Abc_NtkIncrementTravId(pNtk); + vLatches = Vec_PtrAlloc( Abc_NtkLatchNum(pNtk) ); + Abc_NtkForEachObj( pNtk, pObj, i ) + { + if ( Abc_ObjIsLatch(pObj) ) + Vec_PtrPush( vLatches, pObj ); + if ( Abc_ObjIsNode(pObj) ) + continue; + pObj->Level = 0; + Abc_NodeSetTravIdCurrent( pObj ); + } + // perform analysis from CIs/COs + if ( fForward ) + { + Vec_PtrForEachEntry( vLatches, pObj, i ) + { + Abc_ObjForEachFanout( pObj, pNext, k ) + { + LevelCur = Abc_NtkRetimeTiming_rec( pNext, fForward ); + if ( LevelMax < LevelCur ) + LevelMax = LevelCur; + } + } + Abc_NtkForEachPi( pNtk, pObj, i ) + { + Abc_ObjForEachFanout( pObj, pNext, k ) + { + LevelCur = Abc_NtkRetimeTiming_rec( pNext, fForward ); + if ( LevelMax < LevelCur ) + LevelMax = LevelCur; + } + } + } + else + { + Vec_PtrForEachEntry( vLatches, pObj, i ) + { + LevelCur = Abc_NtkRetimeTiming_rec( Abc_ObjFanin0(pObj), fForward ); + if ( LevelMax < LevelCur ) + LevelMax = LevelCur; + } + Abc_NtkForEachPo( pNtk, pObj, i ) + { + LevelCur = Abc_NtkRetimeTiming_rec( Abc_ObjFanin0(pObj), fForward ); + if ( LevelMax < LevelCur ) + LevelMax = LevelCur; + } + } + // collect timing critical nodes, which should be retimed forward/backward + Vec_PtrClear( vCritical ); + Abc_NtkIncrementTravId(pNtk); + if ( fForward ) + { + Vec_PtrForEachEntry( vLatches, pObj, i ) + { + Abc_ObjForEachFanout( pObj, pNext, k ) + { + if ( Abc_NodeIsTravIdCurrent(pNext) ) + continue; + if ( LevelMax != (int)pNext->Level ) + continue; + // new critical node + Vec_PtrPush( vCritical, pNext ); + Abc_NodeSetTravIdCurrent( pNext ); + } + } + } + else + { + Vec_PtrForEachEntry( vLatches, pObj, i ) + { + Abc_ObjForEachFanin( pObj, pNext, k ) + { + if ( Abc_NodeIsTravIdCurrent(pNext) ) + continue; + if ( LevelMax != (int)pNext->Level ) + continue; + // new critical node + Vec_PtrPush( vCritical, pNext ); + Abc_NodeSetTravIdCurrent( pNext ); + } + } + } + Vec_PtrFree( vLatches ); + return LevelMax; +} + +/**Function************************************************************* + + Synopsis [Recursively performs timing analysis.] + + Description [Performs static timing analysis on the network. Uses + unit-delay model.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRetimeTiming_rec( Abc_Obj_t * pObj, int fForward ) +{ + Abc_Obj_t * pNext; + int i, LevelCur, LevelMax = 0; + // skip visited nodes + if ( Abc_NodeIsTravIdCurrent(pObj) ) + return pObj->Level; + Abc_NodeSetTravIdCurrent(pObj); + // visit the next nodes + if ( fForward ) + { + Abc_ObjForEachFanout( pObj, pNext, i ) + { + LevelCur = Abc_NtkRetimeTiming_rec( pNext, fForward ); + if ( LevelMax < LevelCur ) + LevelMax = LevelCur; + } + } + else + { + Abc_ObjForEachFanin( pObj, pNext, i ) + { + LevelCur = Abc_NtkRetimeTiming_rec( pNext, fForward ); + if ( LevelMax < LevelCur ) + LevelMax = LevelCur; + } + } +// printf( "Node %3d -> Level %3d.\n", pObj->Id, LevelMax + 1 ); + pObj->Level = LevelMax + 1; + return pObj->Level; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/ret/retFlow.c b/abc_with_bb_support/src/opt/ret/retFlow.c new file mode 100644 index 000000000..866ae33dc --- /dev/null +++ b/abc_with_bb_support/src/opt/ret/retFlow.c @@ -0,0 +1,783 @@ +/**CFile**************************************************************** + + FileName [retFlow.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Implementation of maximum flow (min-area retiming).] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Oct 31, 2006.] + + Revision [$Id: retFlow.c,v 1.00 2006/10/31 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "retInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static inline int Abc_ObjSetPath( Abc_Obj_t * pObj, Abc_Obj_t * pNext ) { pObj->pCopy = pNext; return 1; } +static inline Abc_Obj_t * Abc_ObjGetPath( Abc_Obj_t * pObj ) { return pObj->pCopy; } +static inline Abc_Obj_t * Abc_ObjGetFanoutPath( Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanout; + int i; + assert( Abc_ObjGetPath(pObj) ); + Abc_ObjForEachFanout( pObj, pFanout, i ) + if ( Abc_ObjGetPath(pFanout) == pObj ) + return pFanout; + return NULL; +} +static inline Abc_Obj_t * Abc_ObjGetFaninPath( Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanin; + int i; + assert( Abc_ObjGetPath(pObj) ); + Abc_ObjForEachFanin( pObj, pFanin, i ) + if ( Abc_ObjGetPath(pFanin) == pObj ) + return pFanin; + return NULL; +} +static inline Abc_Obj_t * Abc_ObjGetPredecessorBwd( Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pNext; + int i; + Abc_ObjForEachFanout( pObj, pNext, i ) + if ( Abc_ObjGetPath(pNext) == pObj ) + return pNext; + Abc_ObjForEachFanin( pObj, pNext, i ) + if ( Abc_ObjGetPath(pNext) == pObj ) + return pNext; + return NULL; +} +static inline Abc_Obj_t * Abc_ObjGetPredecessorFwd( Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pNext; + int i; + Abc_ObjForEachFanin( pObj, pNext, i ) + if ( Abc_ObjGetPath(pNext) == pObj ) + return pNext; + Abc_ObjForEachFanout( pObj, pNext, i ) + if ( Abc_ObjGetPath(pNext) == pObj ) + return pNext; + return NULL; +} + +static int Abc_NtkMaxFlowBwdPath_rec( Abc_Obj_t * pObj ); +static int Abc_NtkMaxFlowFwdPath_rec( Abc_Obj_t * pObj ); +static int Abc_NtkMaxFlowBwdPath2_rec( Abc_Obj_t * pObj ); +static int Abc_NtkMaxFlowFwdPath2_rec( Abc_Obj_t * pObj ); +//static int Abc_NtkMaxFlowBwdPath3_rec( Abc_Obj_t * pObj ); +static int Abc_NtkMaxFlowFwdPath3_rec( Abc_Obj_t * pObj, Abc_Obj_t * pPrev, int fFanin ); +static Vec_Ptr_t * Abc_NtkMaxFlowMinCut( Abc_Ntk_t * pNtk, int fForward ); +static void Abc_NtkMaxFlowMinCutUpdate( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMinCut, int fForward ); +static int Abc_NtkMaxFlowVerifyCut( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMinCut, int fForward ); +static void Abc_NtkMaxFlowPrintCut( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMinCut ); +static void Abc_NtkMaxFlowPrintFlow( Abc_Ntk_t * pNtk, int fForward ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Test-bench for the max-flow computation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMaxFlowTest( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vMinCut; + Abc_Obj_t * pObj; + int i; + + // forward flow + Abc_NtkForEachPo( pNtk, pObj, i ) + pObj->fMarkA = 1; + Abc_NtkForEachLatch( pNtk, pObj, i ) + pObj->fMarkA = Abc_ObjFanin0(pObj)->fMarkA = 1; +// Abc_ObjFanin0(pObj)->fMarkA = 1; + vMinCut = Abc_NtkMaxFlow( pNtk, 1, 1 ); + Vec_PtrFree( vMinCut ); + Abc_NtkCleanMarkA( pNtk ); + + // backward flow + Abc_NtkForEachPi( pNtk, pObj, i ) + pObj->fMarkA = 1; + Abc_NtkForEachLatch( pNtk, pObj, i ) + pObj->fMarkA = Abc_ObjFanout0(pObj)->fMarkA = 1; +// Abc_ObjFanout0(pObj)->fMarkA = 1; + vMinCut = Abc_NtkMaxFlow( pNtk, 0, 1 ); + Vec_PtrFree( vMinCut ); + Abc_NtkCleanMarkA( pNtk ); + +} + +/**Function************************************************************* + + Synopsis [Implementation of max-flow/min-cut computation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkMaxFlow( Abc_Ntk_t * pNtk, int fForward, int fVerbose ) +{ + Vec_Ptr_t * vMinCut; + Abc_Obj_t * pLatch; + int Flow, FlowCur, RetValue, i; + int clk = clock(); + int fUseDirectedFlow = 1; + + // find the max-flow + Abc_NtkCleanCopy( pNtk ); + Flow = 0; + Abc_NtkIncrementTravId(pNtk); + Abc_NtkForEachLatch( pNtk, pLatch, i ) + { + if ( fForward ) + { +// assert( !Abc_ObjFanout0(pLatch)->fMarkA ); + FlowCur = Abc_NtkMaxFlowFwdPath2_rec( Abc_ObjFanout0(pLatch) ); +// FlowCur = Abc_NtkMaxFlowFwdPath3_rec( Abc_ObjFanout0(pLatch), pLatch, 1 ); + Flow += FlowCur; + } + else + { + assert( !Abc_ObjFanin0(pLatch)->fMarkA ); + FlowCur = Abc_NtkMaxFlowBwdPath2_rec( Abc_ObjFanin0(pLatch) ); + Flow += FlowCur; + } + if ( FlowCur ) + Abc_NtkIncrementTravId(pNtk); + } + + if ( !fUseDirectedFlow ) + { + Abc_NtkIncrementTravId(pNtk); + Abc_NtkForEachLatch( pNtk, pLatch, i ) + { + if ( fForward ) + { + // assert( !Abc_ObjFanout0(pLatch)->fMarkA ); + FlowCur = Abc_NtkMaxFlowFwdPath_rec( Abc_ObjFanout0(pLatch) ); + Flow += FlowCur; + } + else + { + assert( !Abc_ObjFanin0(pLatch)->fMarkA ); + FlowCur = Abc_NtkMaxFlowBwdPath_rec( Abc_ObjFanin0(pLatch) ); + Flow += FlowCur; + } + if ( FlowCur ) + Abc_NtkIncrementTravId(pNtk); + } + } +// Abc_NtkMaxFlowPrintFlow( pNtk, fForward ); + + // mark the nodes reachable from the latches + Abc_NtkIncrementTravId(pNtk); + Abc_NtkForEachLatch( pNtk, pLatch, i ) + { + if ( fForward ) + { +// assert( !Abc_ObjFanout0(pLatch)->fMarkA ); + if ( fUseDirectedFlow ) + RetValue = Abc_NtkMaxFlowFwdPath2_rec( Abc_ObjFanout0(pLatch) ); +// RetValue = Abc_NtkMaxFlowFwdPath3_rec( Abc_ObjFanout0(pLatch), pLatch, 1 ); + else + RetValue = Abc_NtkMaxFlowFwdPath_rec( Abc_ObjFanout0(pLatch) ); + } + else + { + assert( !Abc_ObjFanin0(pLatch)->fMarkA ); + if ( fUseDirectedFlow ) + RetValue = Abc_NtkMaxFlowBwdPath2_rec( Abc_ObjFanin0(pLatch) ); + else + RetValue = Abc_NtkMaxFlowBwdPath_rec( Abc_ObjFanin0(pLatch) ); + } + assert( RetValue == 0 ); + } + + // find the min-cut with the smallest volume + vMinCut = Abc_NtkMaxFlowMinCut( pNtk, fForward ); + // verify the cut + if ( !Abc_NtkMaxFlowVerifyCut(pNtk, vMinCut, fForward) ) + printf( "Abc_NtkMaxFlow() error! The computed min-cut is not a cut!\n" ); + // make the cut retimable + Abc_NtkMaxFlowMinCutUpdate( pNtk, vMinCut, fForward ); + + // report the results + if ( fVerbose ) + { + printf( "L = %6d. %s max-flow = %6d. Min-cut = %6d. ", + Abc_NtkLatchNum(pNtk), fForward? "Forward " : "Backward", Flow, Vec_PtrSize(vMinCut) ); +PRT( "Time", clock() - clk ); + } + +// Abc_NtkMaxFlowPrintCut( pNtk, vMinCut ); + return vMinCut; +} + +/**Function************************************************************* + + Synopsis [Tries to find an augmenting path originating in this node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkMaxFlowBwdPath_rec( Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pNext, * pPred; + int i; + // skip visited nodes + if ( Abc_NodeIsTravIdCurrent(pObj) ) + return 0; + Abc_NodeSetTravIdCurrent(pObj); + // get the predecessor + pPred = Abc_ObjGetPredecessorBwd( pObj ); + // process node without flow + if ( !Abc_ObjGetPath(pObj) ) + { + // start the path if we reached a terminal node + if ( pObj->fMarkA ) + return Abc_ObjSetPath( pObj, (void *)1 ); + // explore the fanins + Abc_ObjForEachFanin( pObj, pNext, i ) + if ( pNext != pPred && !Abc_ObjIsLatch(pNext) && Abc_NtkMaxFlowBwdPath_rec(pNext) ) + return Abc_ObjSetPath( pObj, pNext ); + Abc_ObjForEachFanout( pObj, pNext, i ) + if ( pNext != pPred && !Abc_ObjIsLatch(pNext) && Abc_NtkMaxFlowBwdPath_rec(pNext) ) + return Abc_ObjSetPath( pObj, pNext ); + return 0; + } + // pObj has flow - find the fanout with flow + if ( pPred == NULL ) + return 0; + // go through the successors of the predecessor + Abc_ObjForEachFanin( pPred, pNext, i ) + if ( !Abc_ObjIsLatch(pNext) && Abc_NtkMaxFlowBwdPath_rec( pNext ) ) + return Abc_ObjSetPath( pPred, pNext ); + Abc_ObjForEachFanout( pPred, pNext, i ) + if ( !Abc_ObjIsLatch(pNext) && Abc_NtkMaxFlowBwdPath_rec( pNext ) ) + return Abc_ObjSetPath( pPred, pNext ); + // try the fanout + if ( Abc_NtkMaxFlowBwdPath_rec( pPred ) ) + return Abc_ObjSetPath( pPred, NULL ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Tries to find an augmenting path originating in this node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkMaxFlowFwdPath_rec( Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pNext, * pPred; + int i; + // skip visited nodes + if ( Abc_NodeIsTravIdCurrent(pObj) ) + return 0; + Abc_NodeSetTravIdCurrent(pObj); + // get the predecessor + pPred = Abc_ObjGetPredecessorFwd( pObj ); + // process node without flow + if ( !Abc_ObjGetPath(pObj) ) + { + // start the path if we reached a terminal node + if ( pObj->fMarkA ) + return Abc_ObjSetPath( pObj, (void *)1 ); + // explore the fanins + Abc_ObjForEachFanout( pObj, pNext, i ) + if ( pNext != pPred && !Abc_ObjIsLatch(pNext) && Abc_NtkMaxFlowFwdPath_rec(pNext) ) + return Abc_ObjSetPath( pObj, pNext ); + Abc_ObjForEachFanin( pObj, pNext, i ) + if ( pNext != pPred && !Abc_ObjIsLatch(pNext) && Abc_NtkMaxFlowFwdPath_rec(pNext) ) + return Abc_ObjSetPath( pObj, pNext ); + return 0; + } + // pObj has flow - find the fanout with flow + if ( pPred == NULL ) + return 0; + // go through the successors of the predecessor + Abc_ObjForEachFanout( pPred, pNext, i ) + if ( !Abc_ObjIsLatch(pNext) && Abc_NtkMaxFlowFwdPath_rec( pNext ) ) + return Abc_ObjSetPath( pPred, pNext ); + Abc_ObjForEachFanin( pPred, pNext, i ) + if ( !Abc_ObjIsLatch(pNext) && Abc_NtkMaxFlowFwdPath_rec( pNext ) ) + return Abc_ObjSetPath( pPred, pNext ); + // try the fanout + if ( Abc_NtkMaxFlowFwdPath_rec( pPred ) ) + return Abc_ObjSetPath( pPred, NULL ); + return 0; +} + + +/**Function************************************************************* + + Synopsis [Tries to find an augmenting path originating in this edge.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkMaxFlowFwdPath3_rec( Abc_Obj_t * pObj, Abc_Obj_t * pPrev, int fFanin ) +{ + Abc_Obj_t * pFanin, * pFanout; + int i; + // skip visited nodes + if ( Abc_NodeIsTravIdCurrent(pObj) ) + return 0; + Abc_NodeSetTravIdCurrent(pObj); + // skip the fanin which already has flow + if ( fFanin && Abc_ObjGetPath(pPrev) ) + return 0; + // if the node has no flow, try to push through the fanouts + if ( !Abc_ObjGetPath(pObj) ) + { + // start the path if we reached a terminal node + if ( pObj->fMarkA ) + return Abc_ObjSetPath( pObj, (void *)1 ); + // try to push flow through the fanouts + Abc_ObjForEachFanout( pObj, pFanout, i ) + if ( Abc_NtkMaxFlowFwdPath3_rec(pFanout, pObj, 1) ) + return fFanin? Abc_ObjSetPath(pPrev, pObj) : 1; + } + // try to push through the fanins + Abc_ObjForEachFanin( pObj, pFanin, i ) + if ( !Abc_ObjIsLatch(pFanin) && Abc_NtkMaxFlowFwdPath3_rec(pFanin, pObj, 0) ) + return Abc_ObjSetPath( pFanin, NULL ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Tries to find an augmenting path originating in this node.] + + Description [This procedure works for directed graphs only!] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkMaxFlowBwdPath2_rec( Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanout, * pFanin; + int i; + // skip visited nodes + if ( Abc_NodeIsTravIdCurrent(pObj) ) + return 0; + Abc_NodeSetTravIdCurrent(pObj); + // process node without flow + if ( !Abc_ObjGetPath(pObj) ) + { + // start the path if we reached a terminal node + if ( pObj->fMarkA ) + return Abc_ObjSetPath( pObj, (void *)1 ); + // explore the fanins + Abc_ObjForEachFanin( pObj, pFanin, i ) + if ( Abc_NtkMaxFlowBwdPath2_rec(pFanin) ) + return Abc_ObjSetPath( pObj, pFanin ); + return 0; + } + // pObj has flow - find the fanout with flow + pFanout = Abc_ObjGetFanoutPath( pObj ); + if ( pFanout == NULL ) + return 0; + // go through the fanins of the fanout with flow + Abc_ObjForEachFanin( pFanout, pFanin, i ) + if ( Abc_NtkMaxFlowBwdPath2_rec( pFanin ) ) + return Abc_ObjSetPath( pFanout, pFanin ); + // try the fanout + if ( Abc_NtkMaxFlowBwdPath2_rec( pFanout ) ) + return Abc_ObjSetPath( pFanout, NULL ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Tries to find an augmenting path originating in this node.] + + Description [This procedure works for directed graphs only!] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkMaxFlowFwdPath2_rec( Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanout, * pFanin; + int i; + // skip visited nodes + if ( Abc_NodeIsTravIdCurrent(pObj) ) + return 0; + Abc_NodeSetTravIdCurrent(pObj); + // process node without flow + if ( !Abc_ObjGetPath(pObj) ) + { + // start the path if we reached a terminal node + if ( pObj->fMarkA ) + return Abc_ObjSetPath( pObj, (void *)1 ); + // explore the fanins + Abc_ObjForEachFanout( pObj, pFanout, i ) + if ( Abc_NtkMaxFlowFwdPath2_rec(pFanout) ) + return Abc_ObjSetPath( pObj, pFanout ); + return 0; + } + // pObj has flow - find the fanout with flow + pFanin = Abc_ObjGetFaninPath( pObj ); + if ( pFanin == NULL ) + return 0; + // go through the fanins of the fanout with flow + Abc_ObjForEachFanout( pFanin, pFanout, i ) + if ( Abc_NtkMaxFlowFwdPath2_rec( pFanout ) ) + return Abc_ObjSetPath( pFanin, pFanout ); + // try the fanout + if ( Abc_NtkMaxFlowFwdPath2_rec( pFanin ) ) + return Abc_ObjSetPath( pFanin, NULL ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Find minimum-volume minumum cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_NtkMaxFlowMinCut( Abc_Ntk_t * pNtk, int fForward ) +{ + Vec_Ptr_t * vMinCut; + Abc_Obj_t * pObj; + int i; + // collect the cut nodes + vMinCut = Vec_PtrAlloc( Abc_NtkLatchNum(pNtk) ); + Abc_NtkForEachObj( pNtk, pObj, i ) + { + // node without flow is not a cut node + if ( !Abc_ObjGetPath(pObj) ) + continue; + // unvisited node is below the cut + if ( !Abc_NodeIsTravIdCurrent(pObj) ) + continue; + // add terminal with flow or node whose path is not visited + if ( pObj->fMarkA || !Abc_NodeIsTravIdCurrent( Abc_ObjGetPath(pObj) ) ) + Vec_PtrPush( vMinCut, pObj ); + } + return vMinCut; +} + +/**Function************************************************************* + + Synopsis [Marks the TFI cone with MarkA.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMaxFlowMarkCut_rec( Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pNext; + int i; + if ( pObj->fMarkA ) + return; + pObj->fMarkA = 1; + Abc_ObjForEachFanin( pObj, pNext, i ) + Abc_NtkMaxFlowMarkCut_rec( pNext ); +} + +/**Function************************************************************* + + Synopsis [Visits the TFI up to marked nodes and collects marked nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMaxFlowCollectCut_rec( Abc_Obj_t * pObj, Vec_Ptr_t * vNodes ) +{ + Abc_Obj_t * pNext; + int i; + if ( Abc_NodeIsTravIdCurrent(pObj) ) + return; + Abc_NodeSetTravIdCurrent(pObj); + if ( pObj->fMarkA ) + { + Vec_PtrPush( vNodes, pObj ); + return; + } + Abc_ObjForEachFanin( pObj, pNext, i ) + Abc_NtkMaxFlowCollectCut_rec( pNext, vNodes ); +} + +/**Function************************************************************* + + Synopsis [Updates the minimum cut to be retimable.] + + Description [This procedure also labels the nodes reachable from + the latches to the cut with fMarkA.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMaxFlowMinCutUpdate( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMinCut, int fForward ) +{ + Abc_Obj_t * pObj, * pNext; + int i, k; + // clean marks + Abc_NtkForEachObj( pNtk, pObj, i ) + pObj->fMarkA = 0; + // set latch outputs + Abc_NtkForEachLatch( pNtk, pObj, i ) + Abc_ObjFanout0(pObj)->fMarkA = 1; + // traverse from cut nodes + Vec_PtrForEachEntry( vMinCut, pObj, i ) + Abc_NtkMaxFlowMarkCut_rec( pObj ); + if ( fForward ) + { + // change mincut to be nodes with unmarked fanouts + Vec_PtrClear( vMinCut ); + Abc_NtkForEachObj( pNtk, pObj, i ) + { + if ( !pObj->fMarkA ) + continue; + Abc_ObjForEachFanout( pObj, pNext, k ) + { + if ( pNext->fMarkA ) + continue; + Vec_PtrPush( vMinCut, pObj ); + break; + } + } + } + else + { + // change mincut to be marked fanins of the unmarked nodes + Vec_PtrClear( vMinCut ); + Abc_NtkIncrementTravId(pNtk); + Abc_NtkForEachLatch( pNtk, pObj, i ) + Abc_NtkMaxFlowCollectCut_rec( Abc_ObjFanin0(pObj), vMinCut ); + // transfer the attribute + Abc_NtkForEachObj( pNtk, pObj, i ) + pObj->fMarkA = Abc_NodeIsTravIdCurrent(pObj); + // unmark the cut nodes + Vec_PtrForEachEntry( vMinCut, pObj, i ) + pObj->fMarkA = 0; + } +} + +/**Function************************************************************* + + Synopsis [Verifies the min-cut is indeed a cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkMaxFlowVerifyCut_rec( Abc_Obj_t * pObj, int fForward ) +{ + Abc_Obj_t * pNext; + int i; + // skip visited nodes + if ( Abc_NodeIsTravIdCurrent(pObj) ) + return 1; + Abc_NodeSetTravIdCurrent(pObj); + // visit the node + if ( fForward ) + { + if ( Abc_ObjIsCo(pObj) ) + return 0; + // explore the fanouts + Abc_ObjForEachFanout( pObj, pNext, i ) + if ( !Abc_NtkMaxFlowVerifyCut_rec(pNext, fForward) ) + return 0; + } + else + { + if ( Abc_ObjIsCi(pObj) ) + return 0; + // explore the fanins + Abc_ObjForEachFanin( pObj, pNext, i ) + if ( !Abc_NtkMaxFlowVerifyCut_rec(pNext, fForward) ) + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Verifies the min-cut is indeed a cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkMaxFlowVerifyCut( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMinCut, int fForward ) +{ + Abc_Obj_t * pObj; + int i; + // mark the cut with the current traversal ID + Abc_NtkIncrementTravId(pNtk); + Vec_PtrForEachEntry( vMinCut, pObj, i ) + Abc_NodeSetTravIdCurrent( pObj ); + // search from the latches for a path to the COs/CIs + Abc_NtkForEachLatch( pNtk, pObj, i ) + { + if ( fForward ) + { + if ( !Abc_NtkMaxFlowVerifyCut_rec( Abc_ObjFanout0(pObj), fForward ) ) + return 0; + } + else + { + if ( !Abc_NtkMaxFlowVerifyCut_rec( Abc_ObjFanin0(pObj), fForward ) ) + return 0; + } + } +/* + { + // count the volume of the cut + int Counter = 0; + Abc_NtkForEachObj( pNtk, pObj, i ) + Counter += Abc_NodeIsTravIdCurrent( pObj ); + printf( "Volume = %d.\n", Counter ); + } +*/ + return 1; +} + +/**Function************************************************************* + + Synopsis [Prints the flows.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMaxFlowPrintFlow( Abc_Ntk_t * pNtk, int fForward ) +{ + Abc_Obj_t * pLatch, * pNext, * pPrev; + int i; + if ( fForward ) + { + Vec_PtrForEachEntry( pNtk->vBoxes, pLatch, i ) + { + assert( !Abc_ObjFanout0(pLatch)->fMarkA ); + if ( Abc_ObjGetPath(Abc_ObjFanout0(pLatch)) == NULL ) // no flow through this latch + continue; + printf( "Path = " ); + for ( pNext = Abc_ObjFanout0(pLatch); pNext != (void *)1; pNext = Abc_ObjGetPath(pNext) ) + { + printf( "%s(%d) ", Abc_ObjName(pNext), pNext->Id ); + pPrev = pNext; + } + if ( !Abc_ObjIsPo(pPrev) ) + printf( "%s(%d) ", Abc_ObjName(Abc_ObjFanout0(pPrev)), Abc_ObjFanout0(pPrev)->Id ); + printf( "\n" ); + } + } + else + { + Vec_PtrForEachEntry( pNtk->vBoxes, pLatch, i ) + { + assert( !Abc_ObjFanin0(pLatch)->fMarkA ); + if ( Abc_ObjGetPath(Abc_ObjFanin0(pLatch)) == NULL ) // no flow through this latch + continue; + printf( "Path = " ); + for ( pNext = Abc_ObjFanin0(pLatch); pNext != (void *)1; pNext = Abc_ObjGetPath(pNext) ) + { + printf( "%s(%d) ", Abc_ObjName(pNext), pNext->Id ); + pPrev = pNext; + } + if ( !Abc_ObjIsPi(pPrev) ) + printf( "%s(%d) ", Abc_ObjName(Abc_ObjFanin0(pPrev)), Abc_ObjFanin0(pPrev)->Id ); + printf( "\n" ); + } + } +} + +/**Function************************************************************* + + Synopsis [Prints the min-cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkMaxFlowPrintCut( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMinCut ) +{ + Abc_Obj_t * pObj; + int i; + printf( "Min-cut: " ); + Vec_PtrForEachEntry( vMinCut, pObj, i ) + printf( "%s(%d) ", Abc_ObjName(pObj), pObj->Id ); + printf( "\n" ); + printf( "Marked nodes: " ); + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( pObj->fMarkA ) + printf( "%s(%d) ", Abc_ObjName(pObj), pObj->Id ); + printf( "\n" ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/ret/retIncrem.c b/abc_with_bb_support/src/opt/ret/retIncrem.c new file mode 100644 index 000000000..11cda305f --- /dev/null +++ b/abc_with_bb_support/src/opt/ret/retIncrem.c @@ -0,0 +1,464 @@ +/**CFile**************************************************************** + + FileName [retIncrem.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Retiming package.] + + Synopsis [Incremental retiming in one direction.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Oct 31, 2006.] + + Revision [$Id: retIncrem.c,v 1.00 2006/10/31 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "retInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Abc_NtkRetimeOneWay( Abc_Ntk_t * pNtk, int fForward, int fVerbose ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs retiming in one direction.] + + Description [Currently does not retime over black boxes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRetimeIncremental( Abc_Ntk_t * pNtk, int fForward, int fMinDelay, int fVerbose ) +{ + Abc_Ntk_t * pNtkCopy = NULL; + Vec_Ptr_t * vBoxes; + st_table * tLatches; + int nLatches = Abc_NtkLatchNum(pNtk); + int nIdMaxStart = Abc_NtkObjNumMax(pNtk); + int RetValue, nIterLimit; + if ( Abc_NtkNodeNum(pNtk) == 0 ) + return 0; + // reorder CI/CO/latch inputs + Abc_NtkOrderCisCos( pNtk ); + if ( fMinDelay ) + { + nIterLimit = 2 * Abc_NtkLevel(pNtk); + pNtkCopy = Abc_NtkDup( pNtk ); + tLatches = Abc_NtkRetimePrepareLatches( pNtkCopy ); + st_free_table( tLatches ); + } + // collect latches and remove CIs/COs + tLatches = Abc_NtkRetimePrepareLatches( pNtk ); + // share the latches + Abc_NtkRetimeShareLatches( pNtk, 0 ); + // save boxes + vBoxes = pNtk->vBoxes; pNtk->vBoxes = NULL; + // perform the retiming + if ( fMinDelay ) + Abc_NtkRetimeMinDelay( pNtk, pNtkCopy, nIterLimit, fForward, fVerbose ); + else + Abc_NtkRetimeOneWay( pNtk, fForward, fVerbose ); + if ( fMinDelay ) + Abc_NtkDelete( pNtkCopy ); + // share the latches + Abc_NtkRetimeShareLatches( pNtk, 0 ); + // restore boxes + pNtk->vBoxes = vBoxes; + // finalize the latches + RetValue = Abc_NtkRetimeFinalizeLatches( pNtk, tLatches, nIdMaxStart ); + st_free_table( tLatches ); + if ( RetValue == 0 ) + return 0; + // fix the COs +// Abc_NtkLogicMakeSimpleCos( pNtk, 0 ); + // check for correctness + if ( !Abc_NtkCheck( pNtk ) ) + fprintf( stdout, "Abc_NtkRetimeForward(): Network check has failed.\n" ); + // return the number of latches saved + return nLatches - Abc_NtkLatchNum(pNtk); +} + +/**Function************************************************************* + + Synopsis [Prepares the network for retiming.] + + Description [Hash latches into their number in the original network.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +st_table * Abc_NtkRetimePrepareLatches( Abc_Ntk_t * pNtk ) +{ + st_table * tLatches; + Abc_Obj_t * pLatch, * pLatchIn, * pLatchOut, * pFanin; + int i, nOffSet = Abc_NtkBoxNum(pNtk) - Abc_NtkLatchNum(pNtk); + // collect latches and remove CIs/COs + tLatches = st_init_table( st_ptrcmp, st_ptrhash ); + Abc_NtkForEachLatch( pNtk, pLatch, i ) + { + // map latch into its true number + st_insert( tLatches, (void *)pLatch, (void *)(i-nOffSet) ); + // disconnect LI + pLatchIn = Abc_ObjFanin0(pLatch); + pFanin = Abc_ObjFanin0(pLatchIn); + Abc_ObjTransferFanout( pLatchIn, pFanin ); + Abc_ObjDeleteFanin( pLatchIn, pFanin ); + // disconnect LO + pLatchOut = Abc_ObjFanout0(pLatch); + pFanin = Abc_ObjFanin0(pLatchOut); + if ( Abc_ObjFanoutNum(pLatchOut) > 0 ) + Abc_ObjTransferFanout( pLatchOut, pFanin ); + Abc_ObjDeleteFanin( pLatchOut, pFanin ); + } + return tLatches; +} + +/**Function************************************************************* + + Synopsis [Finalizes the latches after retiming.] + + Description [Reuses the LIs/LOs for old latches.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRetimeFinalizeLatches( Abc_Ntk_t * pNtk, st_table * tLatches, int nIdMaxStart ) +{ + Vec_Ptr_t * vCisOld, * vCosOld, * vBoxesOld, * vCisNew, * vCosNew, * vBoxesNew; + Abc_Obj_t * pObj, * pLatch, * pLatchIn, * pLatchOut; + int i, Index; + // create new arrays + vCisOld = pNtk->vCis; pNtk->vCis = NULL; vCisNew = Vec_PtrAlloc( 100 ); + vCosOld = pNtk->vCos; pNtk->vCos = NULL; vCosNew = Vec_PtrAlloc( 100 ); + vBoxesOld = pNtk->vBoxes; pNtk->vBoxes = NULL; vBoxesNew = Vec_PtrAlloc( 100 ); + // copy boxes and their CIs/COs + Vec_PtrForEachEntryStop( vCisOld, pObj, i, Vec_PtrSize(vCisOld) - st_count(tLatches) ) + Vec_PtrPush( vCisNew, pObj ); + Vec_PtrForEachEntryStop( vCosOld, pObj, i, Vec_PtrSize(vCosOld) - st_count(tLatches) ) + Vec_PtrPush( vCosNew, pObj ); + Vec_PtrForEachEntryStop( vBoxesOld, pObj, i, Vec_PtrSize(vBoxesOld) - st_count(tLatches) ) + Vec_PtrPush( vBoxesNew, pObj ); + // go through the latches + Abc_NtkForEachObj( pNtk, pLatch, i ) + { + if ( !Abc_ObjIsLatch(pLatch) ) + continue; + if ( Abc_ObjId(pLatch) >= (unsigned)nIdMaxStart ) + { + // this is a new latch + pLatchIn = Abc_NtkCreateBi(pNtk); + pLatchOut = Abc_NtkCreateBo(pNtk); + Abc_ObjAssignName( pLatchOut, Abc_ObjName(pLatch), "_out" ); + Abc_ObjAssignName( pLatchIn, Abc_ObjName(pLatch), "_in" ); + } + else + { + // this is an old latch + // get its number in the original order + if ( !st_lookup( tLatches, (char *)pLatch, (char **)&Index ) ) + { + printf( "Abc_NtkRetimeFinalizeLatches(): Internal error.\n" ); + return 0; + } + assert( pLatch == Vec_PtrEntry(vBoxesOld, Vec_PtrSize(vBoxesOld) - st_count(tLatches) + Index) ); + // reconnect with the old LIs/LOs + pLatchIn = Vec_PtrEntry( vCosOld, Vec_PtrSize(vCosOld) - st_count(tLatches) + Index ); + pLatchOut = Vec_PtrEntry( vCisOld, Vec_PtrSize(vCisOld) - st_count(tLatches) + Index ); + } + // connect + Abc_ObjAddFanin( pLatchIn, Abc_ObjFanin0(pLatch) ); + Abc_ObjPatchFanin( pLatch, Abc_ObjFanin0(pLatch), pLatchIn ); + if ( Abc_ObjFanoutNum(pLatch) > 0 ) + Abc_ObjTransferFanout( pLatch, pLatchOut ); + Abc_ObjAddFanin( pLatchOut, pLatch ); + // add to the arrays + Vec_PtrPush( vCisNew, pLatchOut ); + Vec_PtrPush( vCosNew, pLatchIn ); + Vec_PtrPush( vBoxesNew, pLatch ); + } + // free useless Cis/Cos + Vec_PtrForEachEntry( vCisOld, pObj, i ) + if ( !Abc_ObjIsPi(pObj) && Abc_ObjFaninNum(pObj) == 0 && Abc_ObjFanoutNum(pObj) == 0 ) + Abc_NtkDeleteObj(pObj); + Vec_PtrForEachEntry( vCosOld, pObj, i ) + if ( !Abc_ObjIsPo(pObj) && Abc_ObjFaninNum(pObj) == 0 && Abc_ObjFanoutNum(pObj) == 0 ) + Abc_NtkDeleteObj(pObj); + // set the new arrays + pNtk->vCis = vCisNew; Vec_PtrFree( vCisOld ); + pNtk->vCos = vCosNew; Vec_PtrFree( vCosOld ); + pNtk->vBoxes = vBoxesNew; Vec_PtrFree( vBoxesOld ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Performs retiming one way, forward or backward.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRetimeOneWay( Abc_Ntk_t * pNtk, int fForward, int fVerbose ) +{ + Abc_Ntk_t * pNtkNew; + Vec_Int_t * vValues; + Abc_Obj_t * pObj; + int i, fChanges, nTotalMoves = 0, nTotalMovesLimit = 10000; + if ( fForward ) + Abc_NtkRetimeTranferToCopy( pNtk ); + else + { + // save initial values of the latches + vValues = Abc_NtkRetimeCollectLatchValues( pNtk ); + // start the network for initial value computation + pNtkNew = Abc_NtkRetimeBackwardInitialStart( pNtk ); + } + // try to move latches forward whenever possible + do { + fChanges = 0; + Abc_NtkForEachObj( pNtk, pObj, i ) + { + if ( !Abc_ObjIsNode(pObj) ) + continue; + if ( Abc_NtkRetimeNodeIsEnabled( pObj, fForward ) ) + { + Abc_NtkRetimeNode( pObj, fForward, 1 ); + fChanges = 1; + nTotalMoves++; + if ( nTotalMoves >= nTotalMovesLimit ) + { + printf( "Stopped after %d latch moves.\n", nTotalMoves ); + break; + } + } + } + } while ( fChanges && nTotalMoves < nTotalMovesLimit ); + // transfer the initial state back to the latches + if ( fForward ) + Abc_NtkRetimeTranferFromCopy( pNtk ); + else + { + Abc_NtkRetimeBackwardInitialFinish( pNtk, pNtkNew, vValues, fVerbose ); + Abc_NtkDelete( pNtkNew ); + Vec_IntFree( vValues ); + } + return 0; +} + + +/**Function************************************************************* + + Synopsis [Returns 1 if retiming forward/backward is possible.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRetimeNodeIsEnabled( Abc_Obj_t * pObj, int fForward ) +{ + Abc_Obj_t * pNext; + int i; + assert( Abc_ObjIsNode(pObj) ); + if ( fForward ) + { + Abc_ObjForEachFanin( pObj, pNext, i ) + if ( !Abc_ObjIsLatch(pNext) ) + return 0; + } + else + { + Abc_ObjForEachFanout( pObj, pNext, i ) + if ( !Abc_ObjIsLatch(pNext) ) + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Retimes the node backward or forward.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkRetimeNode( Abc_Obj_t * pObj, int fForward, int fInitial ) +{ + Abc_Ntk_t * pNtkNew = NULL; + Vec_Ptr_t * vNodes; + Abc_Obj_t * pNext, * pLatch; + int i; + vNodes = Vec_PtrAlloc( 10 ); + if ( fForward ) + { + // compute the initial value + if ( fInitial ) + pObj->pCopy = (void *)Abc_ObjSopSimulate( pObj ); + // collect fanins + Abc_NodeCollectFanins( pObj, vNodes ); + // make the node point to the fanins fanins + Vec_PtrForEachEntry( vNodes, pNext, i ) + { + assert( Abc_ObjIsLatch(pNext) ); + Abc_ObjPatchFanin( pObj, pNext, Abc_ObjFanin0(pNext) ); + if ( Abc_ObjFanoutNum(pNext) == 0 ) + Abc_NtkDeleteObj(pNext); + } + // add a new latch on top + pNext = Abc_NtkCreateLatch(pObj->pNtk); + if ( Abc_ObjFanoutNum(pObj) > 0 ) + Abc_ObjTransferFanout( pObj, pNext ); + Abc_ObjAddFanin( pNext, pObj ); + // set the initial value + if ( fInitial ) + pNext->pCopy = pObj->pCopy; + } + else + { + // compute the initial value + if ( fInitial ) + { + pNtkNew = Abc_ObjFanout0(pObj)->pCopy->pNtk; + Abc_NtkDupObj( pNtkNew, pObj, 0 ); + Abc_ObjForEachFanout( pObj, pNext, i ) + { + assert( Abc_ObjFaninNum(pNext->pCopy) == 0 ); + Abc_ObjAddFanin( pNext->pCopy, pObj->pCopy ); + } + } + // collect fanouts + Abc_NodeCollectFanouts( pObj, vNodes ); + // make the fanouts fanouts point to the node + Vec_PtrForEachEntry( vNodes, pNext, i ) + { + assert( Abc_ObjIsLatch(pNext) ); + Abc_ObjTransferFanout( pNext, pObj ); + Abc_NtkDeleteObj( pNext ); + } + // add new latches to the fanins + Abc_ObjForEachFanin( pObj, pNext, i ) + { + pLatch = Abc_NtkCreateLatch(pObj->pNtk); + Abc_ObjPatchFanin( pObj, pNext, pLatch ); + Abc_ObjAddFanin( pLatch, pNext ); + // create buffer isomorphic to this latch + if ( fInitial ) + { + pLatch->pCopy = Abc_NtkCreateNodeBuf( pNtkNew, NULL ); + Abc_ObjAddFanin( pObj->pCopy, pLatch->pCopy ); + } + } + } + Vec_PtrFree( vNodes ); +} + +/**Function************************************************************* + + Synopsis [Returns the number of compatible fanout latches.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRetimeCheckCompatibleLatchFanouts( Abc_Obj_t * pObj ) +{ + Abc_Obj_t * pFanout; + int i, nLatches = 0, Init = -1; + Abc_ObjForEachFanout( pObj, pFanout, i ) + { + if ( !Abc_ObjIsLatch(pFanout) ) + continue; + if ( Init == -1 ) + { + Init = (int)pObj->pData; + nLatches++; + } + else if ( Init == (int)pObj->pData ) + nLatches++; + } + return nLatches; +} + +/**Function************************************************************* + + Synopsis [Retimes the node backward or forward.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkRetimeShareLatches( Abc_Ntk_t * pNtk, int fInitial ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pFanin, * pLatchTop, * pLatchCur; + int i, k; + vNodes = Vec_PtrAlloc( 10 ); + // consider latch fanins + Abc_NtkForEachObj( pNtk, pFanin, i ) + { + if ( Abc_NtkRetimeCheckCompatibleLatchFanouts(pFanin) <= 1 ) + continue; + // get the first latch + pLatchTop = NULL; + Abc_ObjForEachFanout( pFanin, pLatchTop, k ) + if ( Abc_ObjIsLatch(pLatchTop) ) + break; + assert( pLatchTop && Abc_ObjIsLatch(pLatchTop) ); + // redirect compatible fanout latches to the first latch + Abc_NodeCollectFanouts( pFanin, vNodes ); + Vec_PtrForEachEntry( vNodes, pLatchCur, k ) + { + if ( !Abc_ObjIsLatch(pLatchCur) ) + continue; + if ( pLatchCur == pLatchTop ) + continue; + if ( pLatchCur->pData != pLatchTop->pData ) + continue; + // connect the initial state + if ( fInitial ) + Abc_ObjAddFanin( pLatchCur->pCopy, pLatchTop->pCopy ); + // redirect the fanouts + Abc_ObjTransferFanout( pLatchCur, pLatchTop ); + Abc_NtkDeleteObj(pLatchCur); + } + } + Vec_PtrFree( vNodes ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/ret/retInit.c b/abc_with_bb_support/src/opt/ret/retInit.c new file mode 100644 index 000000000..178f480ec --- /dev/null +++ b/abc_with_bb_support/src/opt/ret/retInit.c @@ -0,0 +1,349 @@ +/**CFile**************************************************************** + + FileName [retInit.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Retiming package.] + + Synopsis [Initial state computation for backward retiming.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Oct 31, 2006.] + + Revision [$Id: retInit.c,v 1.00 2006/10/31 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "retInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Abc_NtkRetimeVerifyModel( Abc_Ntk_t * pNtkCone, Vec_Int_t * vValues, int * pModel ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes initial values of the new latches.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Abc_NtkRetimeInitialValues( Abc_Ntk_t * pNtkCone, Vec_Int_t * vValues, int fVerbose ) +{ + Vec_Int_t * vSolution; + Abc_Ntk_t * pNtkMiter, * pNtkLogic; + int RetValue, clk; + if ( pNtkCone == NULL ) + return Vec_IntDup( vValues ); + // convert the target network to AIG + pNtkLogic = Abc_NtkDup( pNtkCone ); + Abc_NtkToAig( pNtkLogic ); + // get the miter + pNtkMiter = Abc_NtkCreateTarget( pNtkLogic, pNtkLogic->vCos, vValues ); + if ( fVerbose ) + printf( "The miter for initial state computation has %d AIG nodes. ", Abc_NtkNodeNum(pNtkMiter) ); + // solve the miter + clk = clock(); + RetValue = Abc_NtkMiterSat( pNtkMiter, (sint64)500000, (sint64)50000000, 0, NULL, NULL ); + if ( fVerbose ) + { PRT( "SAT solving time", clock() - clk ); } + // analyze the result + if ( RetValue == 1 ) + printf( "Abc_NtkRetimeInitialValues(): The problem is unsatisfiable. DC latch values are used.\n" ); + else if ( RetValue == -1 ) + printf( "Abc_NtkRetimeInitialValues(): The SAT problem timed out. DC latch values are used.\n" ); + else if ( !Abc_NtkRetimeVerifyModel( pNtkCone, vValues, pNtkMiter->pModel ) ) + printf( "Abc_NtkRetimeInitialValues(): The computed counter-example is incorrect.\n" ); + // set the values of the latches + vSolution = RetValue? NULL : Vec_IntAllocArray( pNtkMiter->pModel, Abc_NtkPiNum(pNtkLogic) ); + pNtkMiter->pModel = NULL; + Abc_NtkDelete( pNtkMiter ); + Abc_NtkDelete( pNtkLogic ); + return vSolution; +} + +/**Function************************************************************* + + Synopsis [Computes the results of simulating one node.] + + Description [Assumes that fanins have pCopy set to the input values.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_ObjSopSimulate( Abc_Obj_t * pObj ) +{ + char * pCube, * pSop = pObj->pData; + int nVars, Value, v, ResOr, ResAnd, ResVar; + assert( pSop && !Abc_SopIsExorType(pSop) ); + // simulate the SOP of the node + ResOr = 0; + nVars = Abc_SopGetVarNum(pSop); + Abc_SopForEachCube( pSop, nVars, pCube ) + { + ResAnd = 1; + Abc_CubeForEachVar( pCube, Value, v ) + { + if ( Value == '0' ) + ResVar = 1 ^ ((int)Abc_ObjFanin(pObj, v)->pCopy); + else if ( Value == '1' ) + ResVar = (int)Abc_ObjFanin(pObj, v)->pCopy; + else + continue; + ResAnd &= ResVar; + } + ResOr |= ResAnd; + } + // complement the result if necessary + if ( !Abc_SopGetPhase(pSop) ) + ResOr ^= 1; + return ResOr; +} + +/**Function************************************************************* + + Synopsis [Verifies the counter-example.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRetimeVerifyModel( Abc_Ntk_t * pNtkCone, Vec_Int_t * vValues, int * pModel ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj; + int i, Counter = 0; + assert( Abc_NtkIsSopLogic(pNtkCone) ); + // set the PIs + Abc_NtkForEachPi( pNtkCone, pObj, i ) + pObj->pCopy = (void *)pModel[i]; + // simulate the internal nodes + vNodes = Abc_NtkDfs( pNtkCone, 0 ); + Vec_PtrForEachEntry( vNodes, pObj, i ) + pObj->pCopy = (void *)Abc_ObjSopSimulate( pObj ); + Vec_PtrFree( vNodes ); + // compare the outputs + Abc_NtkForEachPo( pNtkCone, pObj, i ) + pObj->pCopy = Abc_ObjFanin0(pObj)->pCopy; + Abc_NtkForEachPo( pNtkCone, pObj, i ) + Counter += (Vec_IntEntry(vValues, i) != (int)pObj->pCopy); + if ( Counter > 0 ) + printf( "%d outputs (out of %d) have a value mismatch.\n", Counter, Abc_NtkPoNum(pNtkCone) ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Transfer latch initial values to pCopy.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkRetimeTranferToCopy( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i; + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( Abc_ObjIsLatch(pObj) ) + pObj->pCopy = (void *)Abc_LatchIsInit1(pObj); +} + +/**Function************************************************************* + + Synopsis [Transfer latch initial values from pCopy.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkRetimeTranferFromCopy( Abc_Ntk_t * pNtk ) +{ + Abc_Obj_t * pObj; + int i; + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( Abc_ObjIsLatch(pObj) ) + pObj->pData = (void *)(pObj->pCopy? ABC_INIT_ONE : ABC_INIT_ZERO); +} + +/**Function************************************************************* + + Synopsis [Transfer latch initial values to pCopy.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Abc_NtkRetimeCollectLatchValues( Abc_Ntk_t * pNtk ) +{ + Vec_Int_t * vValues; + Abc_Obj_t * pObj; + int i; + vValues = Vec_IntAlloc( Abc_NtkLatchNum(pNtk) ); + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( Abc_ObjIsLatch(pObj) ) + Vec_IntPush( vValues, Abc_LatchIsInit1(pObj) ); + return vValues; +} + +/**Function************************************************************* + + Synopsis [Transfer latch initial values from pCopy.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkRetimeInsertLatchValues( Abc_Ntk_t * pNtk, Vec_Int_t * vValues ) +{ + Abc_Obj_t * pObj; + int i, Counter = 0; + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( Abc_ObjIsLatch(pObj) ) + pObj->pCopy = (void *)Counter++; + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( Abc_ObjIsLatch(pObj) ) + pObj->pData = (void *)(vValues? (Vec_IntEntry(vValues,(int)pObj->pCopy)? ABC_INIT_ONE : ABC_INIT_ZERO) : ABC_INIT_DC); +} + +/**Function************************************************************* + + Synopsis [Transfer latch initial values to pCopy.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Abc_Ntk_t * Abc_NtkRetimeBackwardInitialStart( Abc_Ntk_t * pNtk ) +{ + Abc_Ntk_t * pNtkNew; + Abc_Obj_t * pObj; + int i; + // create the network used for the initial state computation + pNtkNew = Abc_NtkAlloc( ABC_NTK_LOGIC, ABC_FUNC_SOP, 1 ); + // create POs corresponding to the initial values + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( Abc_ObjIsLatch(pObj) ) + pObj->pCopy = Abc_NtkCreatePo(pNtkNew); + return pNtkNew; +} + +/**Function************************************************************* + + Synopsis [Transfer latch initial values to pCopy.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkRetimeBackwardInitialFinish( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkNew, Vec_Int_t * vValuesOld, int fVerbose ) +{ + Vec_Int_t * vValuesNew; + Abc_Obj_t * pObj; + int i; + // create PIs corresponding to the initial values + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( Abc_ObjIsLatch(pObj) ) + Abc_ObjAddFanin( pObj->pCopy, Abc_NtkCreatePi(pNtkNew) ); + // assign dummy node names + Abc_NtkAddDummyPiNames( pNtkNew ); + Abc_NtkAddDummyPoNames( pNtkNew ); + // check the network + if ( !Abc_NtkCheck( pNtkNew ) ) + fprintf( stdout, "Abc_NtkRetimeBackwardInitialFinish(): Network check has failed.\n" ); + // derive new initial values + vValuesNew = Abc_NtkRetimeInitialValues( pNtkNew, vValuesOld, fVerbose ); + // insert new initial values + Abc_NtkRetimeInsertLatchValues( pNtk, vValuesNew ); + if ( vValuesNew ) Vec_IntFree( vValuesNew ); +} + + +/**Function************************************************************* + + Synopsis [Cycles the circuit to create a new initial state.] + + Description [Simulates the circuit with random input for the given + number of timeframes to get a better initial state.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_NtkCycleInitStateSop( Abc_Ntk_t * pNtk, int nFrames, int fVerbose ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pObj; + int i, f; + assert( Abc_NtkIsSopLogic(pNtk) ); + srand( 0x12341234 ); + // initialize the values + Abc_NtkForEachPi( pNtk, pObj, i ) + pObj->pCopy = (void *)(rand() & 1); + Abc_NtkForEachLatch( pNtk, pObj, i ) + pObj->pCopy = (void *)Abc_LatchIsInit1(pObj); + // simulate for the given number of timeframes + vNodes = Abc_NtkDfs( pNtk, 0 ); + for ( f = 0; f < nFrames; f++ ) + { + // simulate internal nodes + Vec_PtrForEachEntry( vNodes, pObj, i ) + pObj->pCopy = (void *)Abc_ObjSopSimulate( pObj ); + // bring the results to the COs + Abc_NtkForEachCo( pNtk, pObj, i ) + pObj->pCopy = Abc_ObjFanin0(pObj)->pCopy; + // assign PI values + Abc_NtkForEachPi( pNtk, pObj, i ) + pObj->pCopy = (void *)(rand() & 1); + // transfer the latch values + Abc_NtkForEachLatch( pNtk, pObj, i ) + Abc_ObjFanout0(pObj)->pCopy = Abc_ObjFanin0(pObj)->pCopy; + } + Vec_PtrFree( vNodes ); + // set the final values + Abc_NtkForEachLatch( pNtk, pObj, i ) + pObj->pData = (void *)(Abc_ObjFanout0(pObj)->pCopy ? ABC_INIT_ONE : ABC_INIT_ZERO); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/ret/retInt.h b/abc_with_bb_support/src/opt/ret/retInt.h new file mode 100644 index 000000000..7d251ae89 --- /dev/null +++ b/abc_with_bb_support/src/opt/ret/retInt.h @@ -0,0 +1,80 @@ +/**CFile**************************************************************** + + FileName [retInt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Retiming package.] + + Synopsis [Internal declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Oct 31, 2006.] + + Revision [$Id: retInt.h,v 1.00 2006/10/31 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __RET_INT_H__ +#define __RET_INT_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include "abc.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== retArea.c ========================================================*/ +extern int Abc_NtkRetimeMinArea( Abc_Ntk_t * pNtk, int fForwardOnly, int fBackwardOnly, int fVerbose ); +/*=== retCore.c ========================================================*/ +extern int Abc_NtkRetime( Abc_Ntk_t * pNtk, int Mode, int fForwardOnly, int fBackwardOnly, int fVerbose ); +/*=== retDelay.c ========================================================*/ +extern int Abc_NtkRetimeMinDelay( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkCopy, int nIterLimit, int fForward, int fVerbose ); +/*=== retDirect.c ========================================================*/ +extern int Abc_NtkRetimeIncremental( Abc_Ntk_t * pNtk, int fForward, int fMinDelay, int fVerbose ); +extern void Abc_NtkRetimeShareLatches( Abc_Ntk_t * pNtk, int fInitial ); +extern int Abc_NtkRetimeNodeIsEnabled( Abc_Obj_t * pObj, int fForward ); +extern void Abc_NtkRetimeNode( Abc_Obj_t * pObj, int fForward, int fInitial ); +extern st_table * Abc_NtkRetimePrepareLatches( Abc_Ntk_t * pNtk ); +extern int Abc_NtkRetimeFinalizeLatches( Abc_Ntk_t * pNtk, st_table * tLatches, int nIdMaxStart ); +/*=== retFlow.c ========================================================*/ +extern void Abc_NtkMaxFlowTest( Abc_Ntk_t * pNtk ); +extern Vec_Ptr_t * Abc_NtkMaxFlow( Abc_Ntk_t * pNtk, int fForward, int fVerbose ); +/*=== retInit.c ========================================================*/ +extern Vec_Int_t * Abc_NtkRetimeInitialValues( Abc_Ntk_t * pNtkSat, Vec_Int_t * vValues, int fVerbose ); +extern int Abc_ObjSopSimulate( Abc_Obj_t * pObj ); +extern void Abc_NtkRetimeTranferToCopy( Abc_Ntk_t * pNtk ); +extern void Abc_NtkRetimeTranferFromCopy( Abc_Ntk_t * pNtk ); +extern Vec_Int_t * Abc_NtkRetimeCollectLatchValues( Abc_Ntk_t * pNtk ); +extern void Abc_NtkRetimeInsertLatchValues( Abc_Ntk_t * pNtk, Vec_Int_t * vValues ); +extern Abc_Ntk_t * Abc_NtkRetimeBackwardInitialStart( Abc_Ntk_t * pNtk ); +extern void Abc_NtkRetimeBackwardInitialFinish( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkNew, Vec_Int_t * vValuesOld, int fVerbose ); +/*=== retLvalue.c ========================================================*/ +extern int Abc_NtkRetimeLValue( Abc_Ntk_t * pNtk, int nIterLimit, int fVerbose ); + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/ret/retLvalue.c b/abc_with_bb_support/src/opt/ret/retLvalue.c new file mode 100644 index 000000000..3d25c1e9d --- /dev/null +++ b/abc_with_bb_support/src/opt/ret/retLvalue.c @@ -0,0 +1,395 @@ +/**CFile**************************************************************** + + FileName [retLvalue.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Retiming package.] + + Synopsis [Implementation of Pan's retiming algorithm.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Oct 31, 2006.] + + Revision [$Id: retLvalue.c,v 1.00 2006/10/31 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "retInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// node status after updating its arrival time +enum { ABC_RET_UPDATE_FAIL, ABC_RET_UPDATE_NO, ABC_RET_UPDATE_YES }; + +// the internal procedures +static Vec_Int_t * Abc_NtkRetimeGetLags( Abc_Ntk_t * pNtk, int nIterLimit, int fVerbose ); +static int Abc_NtkRetimeSearch_rec( Abc_Ntk_t * pNtk, Vec_Ptr_t * vNodes, Vec_Ptr_t * vLatches, int FiMin, int FiMax, int nMaxIters, int fVerbose ); +static int Abc_NtkRetimeForPeriod( Abc_Ntk_t * pNtk, Vec_Ptr_t * vNodes, Vec_Ptr_t * vLatches, int Fi, int nMaxIters, int fVerbose ); +static int Abc_NtkRetimeUpdateLValue( Abc_Ntk_t * pNtk, Vec_Ptr_t * vNodes, Vec_Ptr_t * vLatches, int Fi ); +static int Abc_NtkRetimePosOverLimit( Abc_Ntk_t * pNtk, int Fi ); +static Vec_Ptr_t * Abc_ManCollectLatches( Abc_Ntk_t * pNtk ); +static int Abc_NtkRetimeUsingLags( Abc_Ntk_t * pNtk, Vec_Int_t * vLags, int fVerbose ); + +static inline int Abc_NodeComputeLag( int LValue, int Fi ) { return (LValue + (1<<16)*Fi)/Fi - (1<<16) - (int)(LValue % Fi == 0); } +static inline int Abc_NodeGetLValue( Abc_Obj_t * pNode ) { return (int)pNode->pCopy; } +static inline void Abc_NodeSetLValue( Abc_Obj_t * pNode, int Value ) { pNode->pCopy = (void *)Value; } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Implements Pan's retiming algorithm.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRetimeLValue( Abc_Ntk_t * pNtk, int nIterLimit, int fVerbose ) +{ + Vec_Int_t * vLags; + int nLatches = Abc_NtkLatchNum(pNtk); + assert( Abc_NtkIsLogic( pNtk ) ); + // get the lags + vLags = Abc_NtkRetimeGetLags( pNtk, nIterLimit, fVerbose ); + // compute the retiming +// Abc_NtkRetimeUsingLags( pNtk, vLags, fVerbose ); + Vec_IntFree( vLags ); + // fix the COs +// Abc_NtkLogicMakeSimpleCos( pNtk, 0 ); + // check for correctness + if ( !Abc_NtkCheck( pNtk ) ) + fprintf( stdout, "Abc_NtkRetimeLValue(): Network check has failed.\n" ); + // return the number of latches saved + return nLatches - Abc_NtkLatchNum(pNtk); +} + +/**Function************************************************************* + + Synopsis [Computes the retiming lags.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Abc_NtkRetimeGetLags( Abc_Ntk_t * pNtk, int nIterLimit, int fVerbose ) +{ + Vec_Int_t * vLags; + Vec_Ptr_t * vNodes, * vLatches; + Abc_Obj_t * pNode; + int i, FiMax, FiBest, RetValue, clk, clkIter; + char NodeLag; + + // get the upper bound on the clock period + FiMax = Abc_NtkLevel(pNtk); + + // make sure this clock period is feasible + vNodes = Abc_NtkDfs( pNtk, 0 ); + vLatches = Abc_ManCollectLatches( pNtk ); + if ( !Abc_NtkRetimeForPeriod( pNtk, vNodes, vLatches, FiMax, nIterLimit, fVerbose ) ) + { + Vec_PtrFree( vNodes ); + printf( "Abc_NtkRetimeGetLags() error: The upper bound on the clock period cannot be computed.\n" ); + return Vec_IntStart( Abc_NtkObjNumMax(pNtk) + 1 ); + } + + // search for the optimal clock period between 0 and nLevelMax +clk = clock(); + FiBest = Abc_NtkRetimeSearch_rec( pNtk, vNodes, vLatches, 0, FiMax, nIterLimit, fVerbose ); +clkIter = clock() - clk; + + // recompute the best l-values + RetValue = Abc_NtkRetimeForPeriod( pNtk, vNodes, vLatches, FiBest, nIterLimit, fVerbose ); + assert( RetValue ); + + // fix the problem with non-converged delays + Abc_NtkForEachNode( pNtk, pNode, i ) + if ( Abc_NodeGetLValue(pNode) < -ABC_INFINITY/2 ) + Abc_NodeSetLValue( pNode, 0 ); + + // write the retiming lags + vLags = Vec_IntStart( Abc_NtkObjNumMax(pNtk) + 1 ); + Abc_NtkForEachNode( pNtk, pNode, i ) + { + NodeLag = Abc_NodeComputeLag( Abc_NodeGetLValue(pNode), FiBest ); + Vec_IntWriteEntry( vLags, pNode->Id, NodeLag ); + } +/* + Abc_NtkForEachPo( pNtk, pNode, i ) + printf( "%d ", Abc_NodeGetLValue(Abc_ObjFanin0(pNode)) ); + printf( "\n" ); + Abc_NtkForEachLatch( pNtk, pNode, i ) + printf( "%d/%d ", Abc_NodeGetLValue(Abc_ObjFanout0(pNode)), Abc_NodeGetLValue(Abc_ObjFanout0(pNode)) + FiBest ); + printf( "\n" ); +*/ + + // print the result +// if ( fVerbose ) + printf( "The best clock period is %3d. (Currently, network is not modified.)\n", FiBest ); +/* + { + FILE * pTable; + pTable = fopen( "iscas/seqmap__stats2.txt", "a+" ); + fprintf( pTable, "%d ", FiBest ); + fprintf( pTable, "\n" ); + fclose( pTable ); + } +*/ + Vec_PtrFree( vNodes ); + Vec_PtrFree( vLatches ); + return vLags; +} + +/**Function************************************************************* + + Synopsis [Performs binary search for the optimal clock period.] + + Description [Assumes that FiMin is infeasible while FiMax is feasible.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRetimeSearch_rec( Abc_Ntk_t * pNtk, Vec_Ptr_t * vNodes, Vec_Ptr_t * vLatches, int FiMin, int FiMax, int nMaxIters, int fVerbose ) +{ + int Median; + assert( FiMin < FiMax ); + if ( FiMin + 1 == FiMax ) + return FiMax; + Median = FiMin + (FiMax - FiMin)/2; + if ( Abc_NtkRetimeForPeriod( pNtk, vNodes, vLatches, Median, nMaxIters, fVerbose ) ) + return Abc_NtkRetimeSearch_rec( pNtk, vNodes, vLatches, FiMin, Median, nMaxIters, fVerbose ); // Median is feasible + else + return Abc_NtkRetimeSearch_rec( pNtk, vNodes, vLatches, Median, FiMax, nMaxIters, fVerbose ); // Median is infeasible +} + +/**Function************************************************************* + + Synopsis [Returns 1 if retiming with this clock period is feasible.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRetimeForPeriod( Abc_Ntk_t * pNtk, Vec_Ptr_t * vNodes, Vec_Ptr_t * vLatches, int Fi, int nMaxIters, int fVerbose ) +{ + Abc_Obj_t * pObj; + int c, i, fConverged; + // set l-values of all nodes to be minus infinity, except PIs and constants + Abc_NtkForEachObj( pNtk, pObj, i ) + if ( Abc_ObjFaninNum(pObj) == 0 ) + Abc_NodeSetLValue( pObj, 0 ); + else + Abc_NodeSetLValue( pObj, -ABC_INFINITY ); + // update all values iteratively + fConverged = 0; + for ( c = 1; c <= nMaxIters; c++ ) + { + if ( !Abc_NtkRetimeUpdateLValue( pNtk, vNodes, vLatches, Fi ) ) + { + fConverged = 1; + break; + } + if ( Abc_NtkRetimePosOverLimit(pNtk, Fi) ) + break; + } + // report the results + if ( fVerbose ) + { + if ( !fConverged ) + printf( "Period = %3d. Iterations = %3d. Infeasible %s\n", Fi, c, (c > nMaxIters)? "(timeout)" : "" ); + else + printf( "Period = %3d. Iterations = %3d. Feasible\n", Fi, c ); + } +/* + // check if any AND gates have infinite delay + Counter = 0; + Abc_NtkForEachNode( pNtk, pObj, i ) + Counter += (Abc_NodeGetLValue(pObj) < -ABC_INFINITY/2); + if ( Counter > 0 ) + printf( "Warning: %d internal nodes have wrong l-values!\n", Counter ); +*/ + return fConverged; +} + +/**Function************************************************************* + + Synopsis [Performs one iteration of l-value computation for the nodes.] + + Description [Experimentally it was found that checking POs changes + is not enough to detect the convergence of l-values in the network.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRetimeUpdateLValue( Abc_Ntk_t * pNtk, Vec_Ptr_t * vNodes, Vec_Ptr_t * vLatches, int Fi ) +{ + Abc_Obj_t * pObj, * pFanin; + int i, k, lValueNew, fChange; + // go through the nodes and detect change + fChange = 0; + Vec_PtrForEachEntry( vNodes, pObj, i ) + { + assert( Abc_ObjIsNode(pObj) ); + lValueNew = -ABC_INFINITY; + Abc_ObjForEachFanin( pObj, pFanin, k ) + { + if ( lValueNew < Abc_NodeGetLValue(pFanin) ) + lValueNew = Abc_NodeGetLValue(pFanin); + } + lValueNew++; + if ( Abc_NodeGetLValue(pObj) < lValueNew ) + { + Abc_NodeSetLValue( pObj, lValueNew ); + fChange = 1; + } + } + // propagate values through the latches + Vec_PtrForEachEntry( vLatches, pObj, i ) + Abc_NodeSetLValue( Abc_ObjFanout0(pObj), Abc_NodeGetLValue(Abc_ObjFanin0(Abc_ObjFanin0(pObj))) - Fi ); + return fChange; +} + +/**Function************************************************************* + + Synopsis [Detects the case when l-values exceeded the limit.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRetimePosOverLimit( Abc_Ntk_t * pNtk, int Fi ) +{ + Abc_Obj_t * pObj; + int i; + Abc_NtkForEachPo( pNtk, pObj, i ) + if ( Abc_NodeGetLValue(Abc_ObjFanin0(pObj)) > Fi ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Collects latches in the topological order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_ManCollectLatches_rec( Abc_Obj_t * pObj, Vec_Ptr_t * vLatches ) +{ + Abc_Obj_t * pDriver; + if ( !Abc_ObjIsLatch(pObj) ) + return; + // skip already collected latches + if ( Abc_NodeIsTravIdCurrent(pObj) ) + return; + Abc_NodeSetTravIdCurrent(pObj); + // get the driver node feeding into the latch + pDriver = Abc_ObjFanin0(Abc_ObjFanin0(pObj)); + // call recursively if the driver looks like a latch output + if ( Abc_ObjIsBo(pDriver) ) + Abc_ManCollectLatches_rec( Abc_ObjFanin0(pDriver), vLatches ); + // collect the latch + Vec_PtrPush( vLatches, pObj ); +} + +/**Function************************************************************* + + Synopsis [Collects latches in the topological order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Abc_ManCollectLatches( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vLatches; + Abc_Obj_t * pObj; + int i; + vLatches = Vec_PtrAlloc( Abc_NtkLatchNum(pNtk) ); + Abc_NtkIncrementTravId( pNtk ); + Abc_NtkForEachLatch( pNtk, pObj, i ) + Abc_ManCollectLatches_rec( pObj, vLatches ); + assert( Vec_PtrSize(vLatches) == Abc_NtkLatchNum(pNtk) ); + return vLatches; +} + +/**Function************************************************************* + + Synopsis [Implements the retiming given as the array of retiming lags.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_NtkRetimeUsingLags( Abc_Ntk_t * pNtk, Vec_Int_t * vLags, int fVerbose ) +{ + Abc_Obj_t * pObj; + int fChanges, fForward, nTotalMoves, Lag, Counter, i; + // iterate over the nodes + nTotalMoves = 0; + do { + fChanges = 0; + Abc_NtkForEachNode( pNtk, pObj, i ) + { + Lag = Vec_IntEntry( vLags, pObj->Id ); + if ( !Lag ) + continue; + fForward = (Lag < 0); + if ( Abc_NtkRetimeNodeIsEnabled( pObj, fForward ) ) + { + Abc_NtkRetimeNode( pObj, fForward, 0 ); + fChanges = 1; + nTotalMoves++; + Vec_IntAddToEntry( vLags, pObj->Id, fForward? 1 : -1 ); + } + } + } while ( fChanges ); + if ( fVerbose ) + printf( "Total latch moves = %d.\n", nTotalMoves ); + // check if there are remaining lags + Counter = 0; + Abc_NtkForEachNode( pNtk, pObj, i ) + Counter += (Vec_IntEntry( vLags, pObj->Id ) != 0); + if ( Counter ) + printf( "Warning! The number of nodes with unrealized lag = %d.\n", Counter ); + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/ret/ret_.c b/abc_with_bb_support/src/opt/ret/ret_.c new file mode 100644 index 000000000..b71eb79f3 --- /dev/null +++ b/abc_with_bb_support/src/opt/ret/ret_.c @@ -0,0 +1,48 @@ +/**CFile**************************************************************** + + FileName [ret_.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Retiming package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Oct 31, 2006.] + + Revision [$Id: ret_.c,v 1.00 2006/10/31 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "retInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/rwr/module.make b/abc_with_bb_support/src/opt/rwr/module.make new file mode 100644 index 000000000..3e0c4a07f --- /dev/null +++ b/abc_with_bb_support/src/opt/rwr/module.make @@ -0,0 +1,7 @@ +SRC += src/opt/rwr/rwrDec.c \ + src/opt/rwr/rwrEva.c \ + src/opt/rwr/rwrExp.c \ + src/opt/rwr/rwrLib.c \ + src/opt/rwr/rwrMan.c \ + src/opt/rwr/rwrPrint.c \ + src/opt/rwr/rwrUtil.c diff --git a/abc_with_bb_support/src/opt/rwr/rwr.h b/abc_with_bb_support/src/opt/rwr/rwr.h new file mode 100644 index 000000000..2fc2d8236 --- /dev/null +++ b/abc_with_bb_support/src/opt/rwr/rwr.h @@ -0,0 +1,169 @@ +/**CFile**************************************************************** + + FileName [rwr.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting package.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: rwr.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __RWR_H__ +#define __RWR_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include "abc.h" +#include "cut.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +#define RWR_LIMIT 1048576/4 // ((1 << 20) + +typedef struct Rwr_Man_t_ Rwr_Man_t; +typedef struct Rwr_Node_t_ Rwr_Node_t; + +struct Rwr_Man_t_ +{ + // internal lookups + int nFuncs; // number of four var functions + unsigned short * puCanons; // canonical forms + char * pPhases; // canonical phases + char * pPerms; // canonical permutations + unsigned char * pMap; // mapping of functions into class numbers + unsigned short * pMapInv; // mapping of classes into functions + char * pPractical; // practical NPN classes + char ** pPerms4; // four-var permutations + // node space + Vec_Ptr_t * vForest; // all the nodes + Rwr_Node_t ** pTable; // the hash table of nodes by their canonical form + Vec_Vec_t * vClasses; // the nodes of the equivalence classes + Extra_MmFixed_t * pMmNode; // memory for nodes and cuts + // statistical variables + int nTravIds; // the counter of traversal IDs + int nConsidered; // the number of nodes considered + int nAdded; // the number of nodes added to lists + int nClasses; // the number of NN classes + // the result of resynthesis + int fCompl; // indicates if the output of FF should be complemented + void * pGraph; // the decomposition tree (temporary) + Vec_Ptr_t * vFanins; // the fanins array (temporary) + Vec_Ptr_t * vFaninsCur; // the fanins array (temporary) + Vec_Int_t * vLevNums; // the array of levels (temporary) + Vec_Ptr_t * vNodesTemp; // the nodes in MFFC (temporary) + // node statistics + int nNodesConsidered; + int nNodesRewritten; + int nNodesGained; + int nNodesBeg; + int nNodesEnd; + int nScores[222]; + int nCutsGood; + int nCutsBad; + int nSubgraphs; + // runtime statistics + int timeStart; + int timeCut; + int timeRes; + int timeEval; + int timeMffc; + int timeUpdate; + int timeTotal; +}; + +struct Rwr_Node_t_ // 24 bytes +{ + int Id; // ID + int TravId; // traversal ID + short nScore; + short nGain; + short nAdded; + unsigned uTruth : 16; // truth table + unsigned Volume : 8; // volume + unsigned Level : 6; // level + unsigned fUsed : 1; // mark + unsigned fExor : 1; // mark + Rwr_Node_t * p0; // first child + Rwr_Node_t * p1; // second child + Rwr_Node_t * pNext; // next in the table +}; + +// manipulation of complemented attributes +static inline bool Rwr_IsComplement( Rwr_Node_t * p ) { return (bool)(((unsigned long)p) & 01); } +static inline Rwr_Node_t * Rwr_Regular( Rwr_Node_t * p ) { return (Rwr_Node_t *)((unsigned long)(p) & ~01); } +static inline Rwr_Node_t * Rwr_Not( Rwr_Node_t * p ) { return (Rwr_Node_t *)((unsigned long)(p) ^ 01); } +static inline Rwr_Node_t * Rwr_NotCond( Rwr_Node_t * p, int c ) { return (Rwr_Node_t *)((unsigned long)(p) ^ (c)); } + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== rwrDec.c ========================================================*/ +extern void Rwr_ManPreprocess( Rwr_Man_t * p ); +/*=== rwrEva.c ========================================================*/ +extern int Rwr_NodeRewrite( Rwr_Man_t * p, Cut_Man_t * pManCut, Abc_Obj_t * pNode, int fUpdateLevel, int fUseZeros, int fPlaceEnable ); +extern void Rwr_ScoresClean( Rwr_Man_t * p ); +extern void Rwr_ScoresReport( Rwr_Man_t * p ); +/*=== rwrLib.c ========================================================*/ +extern void Rwr_ManPrecompute( Rwr_Man_t * p ); +extern Rwr_Node_t * Rwr_ManAddVar( Rwr_Man_t * p, unsigned uTruth, int fPrecompute ); +extern Rwr_Node_t * Rwr_ManAddNode( Rwr_Man_t * p, Rwr_Node_t * p0, Rwr_Node_t * p1, int fExor, int Level, int Volume ); +extern int Rwr_ManNodeVolume( Rwr_Man_t * p, Rwr_Node_t * p0, Rwr_Node_t * p1 ); +extern void Rwr_ManIncTravId( Rwr_Man_t * p ); +/*=== rwrMan.c ========================================================*/ +extern Rwr_Man_t * Rwr_ManStart( bool fPrecompute ); +extern void Rwr_ManStop( Rwr_Man_t * p ); +extern void Rwr_ManPrintStats( Rwr_Man_t * p ); +extern void Rwr_ManPrintStatsFile( Rwr_Man_t * p ); +extern void * Rwr_ManReadDecs( Rwr_Man_t * p ); +extern Vec_Ptr_t * Rwr_ManReadLeaves( Rwr_Man_t * p ); +extern int Rwr_ManReadCompl( Rwr_Man_t * p ); +extern void Rwr_ManAddTimeCuts( Rwr_Man_t * p, int Time ); +extern void Rwr_ManAddTimeUpdate( Rwr_Man_t * p, int Time ); +extern void Rwr_ManAddTimeTotal( Rwr_Man_t * p, int Time ); +/*=== rwrPrint.c ========================================================*/ +extern void Rwr_ManPrint( Rwr_Man_t * p ); +/*=== rwrUtil.c ========================================================*/ +extern void Rwr_ManWriteToArray( Rwr_Man_t * p ); +extern void Rwr_ManLoadFromArray( Rwr_Man_t * p, int fVerbose ); +extern void Rwr_ManWriteToFile( Rwr_Man_t * p, char * pFileName ); +extern void Rwr_ManLoadFromFile( Rwr_Man_t * p, char * pFileName ); +extern void Rwr_ListAddToTail( Rwr_Node_t ** ppList, Rwr_Node_t * pNode ); +extern char * Rwr_ManGetPractical( Rwr_Man_t * p ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/opt/rwr/rwrDec.c b/abc_with_bb_support/src/opt/rwr/rwrDec.c new file mode 100644 index 000000000..ad0574a4a --- /dev/null +++ b/abc_with_bb_support/src/opt/rwr/rwrDec.c @@ -0,0 +1,150 @@ +/**CFile**************************************************************** + + FileName [rwrDec.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting package.] + + Synopsis [Evaluation and decomposition procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: rwrDec.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "rwr.h" +#include "dec.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Dec_Graph_t * Rwr_NodePreprocess( Rwr_Man_t * p, Rwr_Node_t * pNode ); +static Dec_Edge_t Rwr_TravCollect_rec( Rwr_Man_t * p, Rwr_Node_t * pNode, Dec_Graph_t * pGraph ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Preprocesses computed library of subgraphs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_ManPreprocess( Rwr_Man_t * p ) +{ + Dec_Graph_t * pGraph; + Rwr_Node_t * pNode; + int i, k; + // put the nodes into the structure + p->pMapInv = ALLOC( unsigned short, 222 ); + memset( p->pMapInv, 0, sizeof(unsigned short) * 222 ); + p->vClasses = Vec_VecStart( 222 ); + for ( i = 0; i < p->nFuncs; i++ ) + { + if ( p->pTable[i] == NULL ) + continue; + // consider all implementations of this function + for ( pNode = p->pTable[i]; pNode; pNode = pNode->pNext ) + { + assert( pNode->uTruth == p->pTable[i]->uTruth ); + assert( p->pMap[pNode->uTruth] >= 0 && p->pMap[pNode->uTruth] < 222 ); + Vec_VecPush( p->vClasses, p->pMap[pNode->uTruth], pNode ); + p->pMapInv[ p->pMap[pNode->uTruth] ] = p->puCanons[pNode->uTruth]; + } + } + // compute decomposition forms for each node and verify them + Vec_VecForEachEntry( p->vClasses, pNode, i, k ) + { + pGraph = Rwr_NodePreprocess( p, pNode ); + pNode->pNext = (Rwr_Node_t *)pGraph; + assert( pNode->uTruth == (Dec_GraphDeriveTruth(pGraph) & 0xFFFF) ); + } +} + +/**Function************************************************************* + + Synopsis [Preprocesses subgraphs rooted at this node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Rwr_NodePreprocess( Rwr_Man_t * p, Rwr_Node_t * pNode ) +{ + Dec_Graph_t * pGraph; + Dec_Edge_t eRoot; + assert( !Rwr_IsComplement(pNode) ); + // consider constant + if ( pNode->uTruth == 0 ) + return Dec_GraphCreateConst0(); + // consider the case of elementary var + if ( pNode->uTruth == 0x00FF ) + return Dec_GraphCreateLeaf( 3, 4, 1 ); + // start the subgraphs + pGraph = Dec_GraphCreate( 4 ); + // collect the nodes + Rwr_ManIncTravId( p ); + eRoot = Rwr_TravCollect_rec( p, pNode, pGraph ); + Dec_GraphSetRoot( pGraph, eRoot ); + return pGraph; +} + +/**Function************************************************************* + + Synopsis [Adds one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Edge_t Rwr_TravCollect_rec( Rwr_Man_t * p, Rwr_Node_t * pNode, Dec_Graph_t * pGraph ) +{ + Dec_Edge_t eNode0, eNode1, eNode; + // elementary variable + if ( pNode->fUsed ) + return Dec_EdgeCreate( pNode->Id - 1, 0 ); + // previously visited node + if ( pNode->TravId == p->nTravIds ) + return Dec_IntToEdge( pNode->Volume ); + pNode->TravId = p->nTravIds; + // solve for children + eNode0 = Rwr_TravCollect_rec( p, Rwr_Regular(pNode->p0), pGraph ); + if ( Rwr_IsComplement(pNode->p0) ) + eNode0.fCompl = !eNode0.fCompl; + eNode1 = Rwr_TravCollect_rec( p, Rwr_Regular(pNode->p1), pGraph ); + if ( Rwr_IsComplement(pNode->p1) ) + eNode1.fCompl = !eNode1.fCompl; + // create the decomposition node(s) + if ( pNode->fExor ) + eNode = Dec_GraphAddNodeXor( pGraph, eNode0, eNode1, 0 ); + else + eNode = Dec_GraphAddNodeAnd( pGraph, eNode0, eNode1 ); + // save the result + pNode->Volume = Dec_EdgeToInt( eNode ); + return eNode; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/rwr/rwrEva.c b/abc_with_bb_support/src/opt/rwr/rwrEva.c new file mode 100644 index 000000000..51820cc3d --- /dev/null +++ b/abc_with_bb_support/src/opt/rwr/rwrEva.c @@ -0,0 +1,587 @@ +/**CFile**************************************************************** + + FileName [rwrDec.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting package.] + + Synopsis [Evaluation and decomposition procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: rwrDec.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "rwr.h" +#include "dec.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Dec_Graph_t * Rwr_CutEvaluate( Rwr_Man_t * p, Abc_Obj_t * pRoot, Cut_Cut_t * pCut, Vec_Ptr_t * vFaninsCur, int nNodesSaved, int LevelMax, int * pGainBest, int fPlaceEnable ); +static int Rwr_CutIsBoolean( Abc_Obj_t * pObj, Vec_Ptr_t * vLeaves ); +static int Rwr_CutCountNumNodes( Abc_Obj_t * pObj, Cut_Cut_t * pCut ); +static int Rwr_NodeGetDepth_rec( Abc_Obj_t * pObj, Vec_Ptr_t * vLeaves ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs rewriting for one node.] + + Description [This procedure considers all the cuts computed for the node + and tries to rewrite each of them using the "forest" of different AIG + structures precomputed and stored in the RWR manager. + Determines the best rewriting and computes the gain in the number of AIG + nodes in the final network. In the end, p->vFanins contains information + about the best cut that can be used for rewriting, while p->pGraph gives + the decomposition dag (represented using decomposition graph data structure). + Returns gain in the number of nodes or -1 if node cannot be rewritten.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Rwr_NodeRewrite( Rwr_Man_t * p, Cut_Man_t * pManCut, Abc_Obj_t * pNode, int fUpdateLevel, int fUseZeros, int fPlaceEnable ) +{ + int fVeryVerbose = 0; + Dec_Graph_t * pGraph; + Cut_Cut_t * pCut;//, * pTemp; + Abc_Obj_t * pFanin; + unsigned uPhase, uTruthBest, uTruth; + char * pPerm; + int Required, nNodesSaved, nNodesSaveCur; + int i, GainCur, GainBest = -1; + int clk, clk2;//, Counter; + + p->nNodesConsidered++; + // get the required times + Required = fUpdateLevel? Abc_ObjRequiredLevel(pNode) : ABC_INFINITY; + + // get the node's cuts +clk = clock(); + pCut = (Cut_Cut_t *)Abc_NodeGetCutsRecursive( pManCut, pNode, 0, 0 ); + assert( pCut != NULL ); +p->timeCut += clock() - clk; + +//printf( " %d", Rwr_CutCountNumNodes(pNode, pCut) ); +/* + Counter = 0; + for ( pTemp = pCut->pNext; pTemp; pTemp = pTemp->pNext ) + Counter++; + printf( "%d ", Counter ); +*/ + // go through the cuts +clk = clock(); + for ( pCut = pCut->pNext; pCut; pCut = pCut->pNext ) + { + // consider only 4-input cuts + if ( pCut->nLeaves < 4 ) + continue; +// Cut_CutPrint( pCut, 0 ), printf( "\n" ); + + // get the fanin permutation + uTruth = 0xFFFF & *Cut_CutReadTruth(pCut); + pPerm = p->pPerms4[ p->pPerms[uTruth] ]; + uPhase = p->pPhases[uTruth]; + // collect fanins with the corresponding permutation/phase + Vec_PtrClear( p->vFaninsCur ); + Vec_PtrFill( p->vFaninsCur, (int)pCut->nLeaves, 0 ); + for ( i = 0; i < (int)pCut->nLeaves; i++ ) + { + pFanin = Abc_NtkObj( pNode->pNtk, pCut->pLeaves[pPerm[i]] ); + if ( pFanin == NULL ) + break; + pFanin = Abc_ObjNotCond(pFanin, ((uPhase & (1< 0) ); + Vec_PtrWriteEntry( p->vFaninsCur, i, pFanin ); + } + if ( i != (int)pCut->nLeaves ) + { + p->nCutsBad++; + continue; + } + p->nCutsGood++; + + { + int Counter = 0; + Vec_PtrForEachEntry( p->vFaninsCur, pFanin, i ) + if ( Abc_ObjFanoutNum(Abc_ObjRegular(pFanin)) == 1 ) + Counter++; + if ( Counter > 2 ) + continue; + } + +clk2 = clock(); +/* + printf( "Considering: (" ); + Vec_PtrForEachEntry( p->vFaninsCur, pFanin, i ) + printf( "%d ", Abc_ObjFanoutNum(Abc_ObjRegular(pFanin)) ); + printf( ")\n" ); +*/ + // mark the fanin boundary + Vec_PtrForEachEntry( p->vFaninsCur, pFanin, i ) + Abc_ObjRegular(pFanin)->vFanouts.nSize++; + + // label MFFC with current ID + Abc_NtkIncrementTravId( pNode->pNtk ); + nNodesSaved = Abc_NodeMffcLabelAig( pNode ); + // unmark the fanin boundary + Vec_PtrForEachEntry( p->vFaninsCur, pFanin, i ) + Abc_ObjRegular(pFanin)->vFanouts.nSize--; +p->timeMffc += clock() - clk2; + + // evaluate the cut +clk2 = clock(); + pGraph = Rwr_CutEvaluate( p, pNode, pCut, p->vFaninsCur, nNodesSaved, Required, &GainCur, fPlaceEnable ); +p->timeEval += clock() - clk2; + + // check if the cut is better than the current best one + if ( pGraph != NULL && GainBest < GainCur ) + { + // save this form + nNodesSaveCur = nNodesSaved; + GainBest = GainCur; + p->pGraph = pGraph; + p->fCompl = ((uPhase & (1<<4)) > 0); + uTruthBest = 0xFFFF & *Cut_CutReadTruth(pCut); + // collect fanins in the + Vec_PtrClear( p->vFanins ); + Vec_PtrForEachEntry( p->vFaninsCur, pFanin, i ) + Vec_PtrPush( p->vFanins, pFanin ); + } + } +p->timeRes += clock() - clk; + + if ( GainBest == -1 ) + return -1; +/* + if ( GainBest > 0 ) + { + printf( "Class %d ", p->pMap[uTruthBest] ); + printf( "Gain = %d. Node %d : ", GainBest, pNode->Id ); + Vec_PtrForEachEntry( p->vFanins, pFanin, i ) + printf( "%d ", Abc_ObjRegular(pFanin)->Id ); + Dec_GraphPrint( stdout, p->pGraph, NULL, NULL ); + printf( "\n" ); + } +*/ + +// printf( "%d", nNodesSaveCur - GainBest ); +/* + if ( GainBest > 0 ) + { + if ( Rwr_CutIsBoolean( pNode, p->vFanins ) ) + printf( "b" ); + else + { + printf( "Node %d : ", pNode->Id ); + Vec_PtrForEachEntry( p->vFanins, pFanin, i ) + printf( "%d ", Abc_ObjRegular(pFanin)->Id ); + printf( "a" ); + } + } +*/ +/* + if ( GainBest > 0 ) + if ( p->fCompl ) + printf( "c" ); + else + printf( "." ); +*/ + + // copy the leaves + Vec_PtrForEachEntry( p->vFanins, pFanin, i ) + Dec_GraphNode(p->pGraph, i)->pFunc = pFanin; +/* + printf( "(" ); + Vec_PtrForEachEntry( p->vFanins, pFanin, i ) + printf( " %d", Abc_ObjRegular(pFanin)->vFanouts.nSize - 1 ); + printf( " ) " ); +*/ +// printf( "%d ", Rwr_NodeGetDepth_rec( pNode, p->vFanins ) ); + + p->nScores[p->pMap[uTruthBest]]++; + p->nNodesGained += GainBest; + if ( fUseZeros || GainBest > 0 ) + { + p->nNodesRewritten++; + } + + // report the progress + if ( fVeryVerbose && GainBest > 0 ) + { + printf( "Node %6s : ", Abc_ObjName(pNode) ); + printf( "Fanins = %d. ", p->vFanins->nSize ); + printf( "Save = %d. ", nNodesSaveCur ); + printf( "Add = %d. ", nNodesSaveCur-GainBest ); + printf( "GAIN = %d. ", GainBest ); + printf( "Cone = %d. ", p->pGraph? Dec_GraphNodeNum(p->pGraph) : 0 ); + printf( "Class = %d. ", p->pMap[uTruthBest] ); + printf( "\n" ); + } + return GainBest; +} + +/**Function************************************************************* + + Synopsis [Evaluates the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Dec_Graph_t * Rwr_CutEvaluate( Rwr_Man_t * p, Abc_Obj_t * pRoot, Cut_Cut_t * pCut, Vec_Ptr_t * vFaninsCur, int nNodesSaved, int LevelMax, int * pGainBest, int fPlaceEnable ) +{ + Vec_Ptr_t * vSubgraphs; + Dec_Graph_t * pGraphBest, * pGraphCur; + Rwr_Node_t * pNode, * pFanin; + int nNodesAdded, GainBest, i, k; + unsigned uTruth; + float CostBest, CostCur; + // find the matching class of subgraphs + uTruth = 0xFFFF & *Cut_CutReadTruth(pCut); + vSubgraphs = Vec_VecEntry( p->vClasses, p->pMap[uTruth] ); + p->nSubgraphs += vSubgraphs->nSize; + // determine the best subgraph + GainBest = -1; + CostBest = ABC_INFINITY; + Vec_PtrForEachEntry( vSubgraphs, pNode, i ) + { + // get the current graph + pGraphCur = (Dec_Graph_t *)pNode->pNext; + // copy the leaves + Vec_PtrForEachEntry( vFaninsCur, pFanin, k ) + Dec_GraphNode(pGraphCur, k)->pFunc = pFanin; + // detect how many unlabeled nodes will be reused + nNodesAdded = Dec_GraphToNetworkCount( pRoot, pGraphCur, nNodesSaved, LevelMax ); + if ( nNodesAdded == -1 ) + continue; + assert( nNodesSaved >= nNodesAdded ); + + // evaluate the cut + if ( fPlaceEnable ) + { + extern float Abc_PlaceEvaluateCut( Abc_Obj_t * pRoot, Vec_Ptr_t * vFanins ); + + float Alpha = 0.5; // ??? + float PlaceCost; + + // get the placement cost of the cut + PlaceCost = Abc_PlaceEvaluateCut( pRoot, vFaninsCur ); + + // get the weigted cost of the cut + CostCur = nNodesSaved - nNodesAdded + Alpha * PlaceCost; + + // do not allow uphill moves + if ( nNodesSaved - nNodesAdded < 0 ) + continue; + + // decide what cut to use + if ( CostBest > CostCur ) + { + GainBest = nNodesSaved - nNodesAdded; // pure node cost + CostBest = CostCur; // cost with placement + pGraphBest = pGraphCur; // subgraph to be used for rewriting + + // score the graph + if ( nNodesSaved - nNodesAdded > 0 ) + { + pNode->nScore++; + pNode->nGain += GainBest; + pNode->nAdded += nNodesAdded; + } + } + } + else + { + // count the gain at this node + if ( GainBest < nNodesSaved - nNodesAdded ) + { + GainBest = nNodesSaved - nNodesAdded; + pGraphBest = pGraphCur; + + // score the graph + if ( nNodesSaved - nNodesAdded > 0 ) + { + pNode->nScore++; + pNode->nGain += GainBest; + pNode->nAdded += nNodesAdded; + } + } + } + } + if ( GainBest == -1 ) + return NULL; + *pGainBest = GainBest; + return pGraphBest; +} + +/**Function************************************************************* + + Synopsis [Checks the type of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_CutIsBoolean_rec( Abc_Obj_t * pObj, Vec_Ptr_t * vLeaves, int fMarkA ) +{ + if ( Vec_PtrFind(vLeaves, pObj) >= 0 || Vec_PtrFind(vLeaves, Abc_ObjNot(pObj)) >= 0 ) + { + if ( fMarkA ) + pObj->fMarkA = 1; + else + pObj->fMarkB = 1; + return; + } + assert( !Abc_ObjIsCi(pObj) ); + Rwr_CutIsBoolean_rec( Abc_ObjFanin0(pObj), vLeaves, fMarkA ); + Rwr_CutIsBoolean_rec( Abc_ObjFanin1(pObj), vLeaves, fMarkA ); +} + +/**Function************************************************************* + + Synopsis [Checks the type of the cut.] + + Description [Returns 1(0) if the cut is Boolean (algebraic).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Rwr_CutIsBoolean( Abc_Obj_t * pObj, Vec_Ptr_t * vLeaves ) +{ + Abc_Obj_t * pTemp; + int i, RetValue; + Vec_PtrForEachEntry( vLeaves, pTemp, i ) + { + pTemp = Abc_ObjRegular(pTemp); + assert( !pTemp->fMarkA && !pTemp->fMarkB ); + } + Rwr_CutIsBoolean_rec( Abc_ObjFanin0(pObj), vLeaves, 1 ); + Rwr_CutIsBoolean_rec( Abc_ObjFanin1(pObj), vLeaves, 0 ); + RetValue = 0; + Vec_PtrForEachEntry( vLeaves, pTemp, i ) + { + pTemp = Abc_ObjRegular(pTemp); + RetValue |= pTemp->fMarkA && pTemp->fMarkB; + pTemp->fMarkA = pTemp->fMarkB = 0; + } + return RetValue; +} + + +/**Function************************************************************* + + Synopsis [Count the nodes in the cut space of a node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_CutCountNumNodes_rec( Abc_Obj_t * pObj, Cut_Cut_t * pCut, Vec_Ptr_t * vNodes ) +{ + int i; + for ( i = 0; i < (int)pCut->nLeaves; i++ ) + if ( pCut->pLeaves[i] == pObj->Id ) + { + // check if the node is collected + if ( pObj->fMarkC == 0 ) + { + pObj->fMarkC = 1; + Vec_PtrPush( vNodes, pObj ); + } + return; + } + assert( Abc_ObjIsNode(pObj) ); + // check if the node is collected + if ( pObj->fMarkC == 0 ) + { + pObj->fMarkC = 1; + Vec_PtrPush( vNodes, pObj ); + } + // traverse the fanins + Rwr_CutCountNumNodes_rec( Abc_ObjFanin0(pObj), pCut, vNodes ); + Rwr_CutCountNumNodes_rec( Abc_ObjFanin1(pObj), pCut, vNodes ); +} + +/**Function************************************************************* + + Synopsis [Count the nodes in the cut space of a node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Rwr_CutCountNumNodes( Abc_Obj_t * pObj, Cut_Cut_t * pCut ) +{ + Vec_Ptr_t * vNodes; + int i, Counter; + // collect all nodes + vNodes = Vec_PtrAlloc( 100 ); + for ( pCut = pCut->pNext; pCut; pCut = pCut->pNext ) + Rwr_CutCountNumNodes_rec( pObj, pCut, vNodes ); + // clean all nodes + Vec_PtrForEachEntry( vNodes, pObj, i ) + pObj->fMarkC = 0; + // delete and return + Counter = Vec_PtrSize(vNodes); + Vec_PtrFree( vNodes ); + return Counter; +} + + +/**Function************************************************************* + + Synopsis [Returns depth of the cut.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Rwr_NodeGetDepth_rec( Abc_Obj_t * pObj, Vec_Ptr_t * vLeaves ) +{ + Abc_Obj_t * pLeaf; + int i, Depth0, Depth1; + if ( Abc_ObjIsCi(pObj) ) + return 0; + Vec_PtrForEachEntry( vLeaves, pLeaf, i ) + if ( pObj == Abc_ObjRegular(pLeaf) ) + return 0; + Depth0 = Rwr_NodeGetDepth_rec( Abc_ObjFanin0(pObj), vLeaves ); + Depth1 = Rwr_NodeGetDepth_rec( Abc_ObjFanin1(pObj), vLeaves ); + return 1 + ABC_MAX( Depth0, Depth1 ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_ScoresClean( Rwr_Man_t * p ) +{ + Vec_Ptr_t * vSubgraphs; + Rwr_Node_t * pNode; + int i, k; + for ( i = 0; i < p->vClasses->nSize; i++ ) + { + vSubgraphs = Vec_VecEntry( p->vClasses, i ); + Vec_PtrForEachEntry( vSubgraphs, pNode, k ) + pNode->nScore = pNode->nGain = pNode->nAdded = 0; + } +} + +static int Gains[222]; + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Rwr_ScoresCompare( int * pNum1, int * pNum2 ) +{ + if ( Gains[*pNum1] > Gains[*pNum2] ) + return -1; + if ( Gains[*pNum1] < Gains[*pNum2] ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_ScoresReport( Rwr_Man_t * p ) +{ + extern void Ivy_TruthDsdComputePrint( unsigned uTruth ); + int Perm[222]; + Vec_Ptr_t * vSubgraphs; + Rwr_Node_t * pNode; + int i, iNew, k; + unsigned uTruth; + // collect total gains + assert( p->vClasses->nSize == 222 ); + for ( i = 0; i < p->vClasses->nSize; i++ ) + { + Perm[i] = i; + Gains[i] = 0; + vSubgraphs = Vec_VecEntry( p->vClasses, i ); + Vec_PtrForEachEntry( vSubgraphs, pNode, k ) + Gains[i] += pNode->nGain; + } + // sort the gains + qsort( Perm, 222, sizeof(int), (int (*)(const void *, const void *))Rwr_ScoresCompare ); + + // print classes + for ( i = 0; i < p->vClasses->nSize; i++ ) + { + iNew = Perm[i]; + if ( Gains[iNew] == 0 ) + break; + vSubgraphs = Vec_VecEntry( p->vClasses, iNew ); + printf( "CLASS %3d: Subgr = %3d. Total gain = %6d. ", iNew, Vec_PtrSize(vSubgraphs), Gains[iNew] ); + uTruth = (unsigned)p->pMapInv[iNew]; + Extra_PrintBinary( stdout, &uTruth, 16 ); + printf( " " ); + Ivy_TruthDsdComputePrint( (unsigned)p->pMapInv[iNew] | ((unsigned)p->pMapInv[iNew] << 16) ); + Vec_PtrForEachEntry( vSubgraphs, pNode, k ) + { + if ( pNode->nScore == 0 ) + continue; + printf( " %2d: S=%5d. A=%5d. G=%6d. ", k, pNode->nScore, pNode->nAdded, pNode->nGain ); + Dec_GraphPrint( stdout, (Dec_Graph_t *)pNode->pNext, NULL, NULL ); + } + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/rwr/rwrExp.c b/abc_with_bb_support/src/opt/rwr/rwrExp.c new file mode 100644 index 000000000..36fd3657a --- /dev/null +++ b/abc_with_bb_support/src/opt/rwr/rwrExp.c @@ -0,0 +1,333 @@ +/**CFile**************************************************************** + + FileName [rwrExp.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting package.] + + Synopsis [Computation of practically used NN-classes of 4-input cuts.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: rwrExp.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "rwr.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Rwr_Man4_t_ Rwr_Man4_t; +struct Rwr_Man4_t_ +{ + // internal lookups + int nFuncs; // the number of four-var functions + unsigned short * puCanons; // canonical forms + int * pnCounts; // the counters of functions in each class + int nConsidered; // the number of nodes considered + int nClasses; // the number of NN classes +}; + +typedef struct Rwr_Man5_t_ Rwr_Man5_t; +struct Rwr_Man5_t_ +{ + // internal lookups + stmm_table * tTableNN; // the NN canonical forms + stmm_table * tTableNPN; // the NPN canonical forms +}; + +static Rwr_Man4_t * s_pManRwrExp4 = NULL; +static Rwr_Man5_t * s_pManRwrExp5 = NULL; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Collects stats about 4-var functions appearing in netlists.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwt_Man4ExploreStart() +{ + Rwr_Man4_t * p; + p = ALLOC( Rwr_Man4_t, 1 ); + memset( p, 0, sizeof(Rwr_Man4_t) ); + // canonical forms + p->nFuncs = (1<<16); + // canonical forms, phases, perms + Extra_Truth4VarNPN( &p->puCanons, NULL, NULL, NULL ); + // counters + p->pnCounts = ALLOC( int, p->nFuncs ); + memset( p->pnCounts, 0, sizeof(int) * p->nFuncs ); + s_pManRwrExp4 = p; +} + +/**Function************************************************************* + + Synopsis [Collects stats about 4-var functions appearing in netlists.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwt_Man4ExploreCount( unsigned uTruth ) +{ + assert( uTruth < (1<<16) ); + s_pManRwrExp4->pnCounts[ s_pManRwrExp4->puCanons[uTruth] ]++; +} + +/**Function************************************************************* + + Synopsis [Collects stats about 4-var functions appearing in netlists.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwt_Man4ExplorePrint() +{ + FILE * pFile; + int i, CountMax, CountWrite, nCuts, nClasses; + int * pDistrib; + int * pReprs; + // find the max number of occurences + nCuts = nClasses = 0; + CountMax = 0; + for ( i = 0; i < s_pManRwrExp4->nFuncs; i++ ) + { + if ( CountMax < s_pManRwrExp4->pnCounts[i] ) + CountMax = s_pManRwrExp4->pnCounts[i]; + nCuts += s_pManRwrExp4->pnCounts[i]; + if ( s_pManRwrExp4->pnCounts[i] > 0 ) + nClasses++; + } + printf( "Number of cuts considered = %8d.\n", nCuts ); + printf( "Classes occurring at least once = %8d.\n", nClasses ); + // print the distribution of classes + pDistrib = ALLOC( int, CountMax + 1 ); + pReprs = ALLOC( int, CountMax + 1 ); + memset( pDistrib, 0, sizeof(int)*(CountMax + 1) ); + for ( i = 0; i < s_pManRwrExp4->nFuncs; i++ ) + { + pDistrib[ s_pManRwrExp4->pnCounts[i] ]++; + pReprs[ s_pManRwrExp4->pnCounts[i] ] = i; + } + + printf( "Occurence = %6d. Num classes = %4d. \n", 0, 2288-nClasses ); + for ( i = 1; i <= CountMax; i++ ) + if ( pDistrib[i] ) + { + printf( "Occurence = %6d. Num classes = %4d. Repr = ", i, pDistrib[i] ); + Extra_PrintBinary( stdout, (unsigned*)&(pReprs[i]), 16 ); + printf( "\n" ); + } + free( pDistrib ); + free( pReprs ); + // write into a file all classes above limit (5) + CountWrite = 0; + pFile = fopen( "npnclass_stats4.txt", "w" ); + for ( i = 0; i < s_pManRwrExp4->nFuncs; i++ ) + if ( s_pManRwrExp4->pnCounts[i] > 0 ) + { + Extra_PrintHex( pFile, i, 4 ); + fprintf( pFile, " %10d\n", s_pManRwrExp4->pnCounts[i] ); +// fprintf( pFile, "%d ", i ); + CountWrite++; + } + fclose( pFile ); + printf( "%d classes written into file \"%s\".\n", CountWrite, "npnclass_stats4.txt" ); +} + + + + +/**Function************************************************************* + + Synopsis [Collects stats about 4-var functions appearing in netlists.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwt_Man5ExploreStart() +{ + Rwr_Man5_t * p; + p = ALLOC( Rwr_Man5_t, 1 ); + memset( p, 0, sizeof(Rwr_Man5_t) ); + p->tTableNN = stmm_init_table( st_numcmp, st_numhash ); + p->tTableNPN = stmm_init_table( st_numcmp, st_numhash ); + s_pManRwrExp5 = p; + +//Extra_PrintHex( stdout, Extra_TruthCanonNPN( 0x0000FFFF, 5 ), 5 ); +//printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [Collects stats about 4-var functions appearing in netlists.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwt_Man5ExploreCount( unsigned uTruth ) +{ + int * pCounter; + if ( !stmm_find_or_add( s_pManRwrExp5->tTableNN, (char *)uTruth, (char***)&pCounter ) ) + *pCounter = 0; + (*pCounter)++; +} + +/**Function************************************************************* + + Synopsis [Collects stats about 4-var functions appearing in netlists.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwt_Man5ExplorePrint() +{ + FILE * pFile; + stmm_generator * gen; + int i, CountMax, nCuts, Counter; + int * pDistrib; + unsigned * pReprs; + unsigned uTruth, uTruthC; + int clk = clock(); + Vec_Int_t * vClassesNN, * vClassesNPN; + + // find the max number of occurences + nCuts = 0; + CountMax = 0; + stmm_foreach_item( s_pManRwrExp5->tTableNN, gen, (char **)&uTruth, (char **)&Counter ) + { + nCuts += Counter; + if ( CountMax < Counter ) + CountMax = Counter; + } + printf( "Number of cuts considered = %8d.\n", nCuts ); + printf( "Classes occurring at least once = %8d.\n", stmm_count(s_pManRwrExp5->tTableNN) ); + printf( "The largest number of occurence = %8d.\n", CountMax ); + + // print the distribution of classes + pDistrib = ALLOC( int, CountMax + 1 ); + pReprs = ALLOC( unsigned, CountMax + 1 ); + memset( pDistrib, 0, sizeof(int)*(CountMax + 1) ); + stmm_foreach_item( s_pManRwrExp5->tTableNN, gen, (char **)&uTruth, (char **)&Counter ) + { + assert( Counter <= CountMax ); + pDistrib[ Counter ]++; + pReprs[ Counter ] = uTruth; + } + + for ( i = 1; i <= CountMax; i++ ) + if ( pDistrib[i] ) + { + printf( "Occurence = %6d. Num classes = %4d. Repr = ", i, pDistrib[i] ); + Extra_PrintBinary( stdout, pReprs + i, 32 ); + printf( "\n" ); + } + free( pDistrib ); + free( pReprs ); + + + // put them into an array + vClassesNN = Vec_IntAlloc( stmm_count(s_pManRwrExp5->tTableNN) ); + stmm_foreach_item( s_pManRwrExp5->tTableNN, gen, (char **)&uTruth, NULL ) + Vec_IntPush( vClassesNN, (int)uTruth ); + Vec_IntSortUnsigned( vClassesNN ); + + // write into a file all classes + pFile = fopen( "nnclass_stats5.txt", "w" ); + Vec_IntForEachEntry( vClassesNN, uTruth, i ) + { + if ( !stmm_lookup( s_pManRwrExp5->tTableNN, (char *)uTruth, (char **)&Counter ) ) + { + assert( 0 ); + } + Extra_PrintHex( pFile, uTruth, 5 ); + fprintf( pFile, " %10d\n", Counter ); + } + fclose( pFile ); + printf( "%d classes written into file \"%s\".\n", vClassesNN->nSize, "nnclass_stats5.txt" ); + + +clk = clock(); + // how many NPN classes exist? + Vec_IntForEachEntry( vClassesNN, uTruth, i ) + { + int * pCounter; + uTruthC = Extra_TruthCanonNPN( uTruth, 5 ); + if ( !stmm_find_or_add( s_pManRwrExp5->tTableNPN, (char *)uTruthC, (char***)&pCounter ) ) + *pCounter = 0; + if ( !stmm_lookup( s_pManRwrExp5->tTableNN, (char *)uTruth, (char **)&Counter ) ) + { + assert( 0 ); + } + (*pCounter) += Counter; + } + printf( "The numbe of NPN classes = %d.\n", stmm_count(s_pManRwrExp5->tTableNPN) ); +PRT( "Computing NPN classes", clock() - clk ); + + // put them into an array + vClassesNPN = Vec_IntAlloc( stmm_count(s_pManRwrExp5->tTableNPN) ); + stmm_foreach_item( s_pManRwrExp5->tTableNPN, gen, (char **)&uTruth, NULL ) + Vec_IntPush( vClassesNPN, (int)uTruth ); + Vec_IntSortUnsigned( vClassesNPN ); + + // write into a file all classes + pFile = fopen( "npnclass_stats5.txt", "w" ); + Vec_IntForEachEntry( vClassesNPN, uTruth, i ) + { + if ( !stmm_lookup( s_pManRwrExp5->tTableNPN, (char *)uTruth, (char **)&Counter ) ) + { + assert( 0 ); + } + Extra_PrintHex( pFile, uTruth, 5 ); + fprintf( pFile, " %10d\n", Counter ); + } + fclose( pFile ); + printf( "%d classes written into file \"%s\".\n", vClassesNPN->nSize, "npnclass_stats5.txt" ); + + + // can they be uniquely characterized? + +} + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/rwr/rwrLib.c b/abc_with_bb_support/src/opt/rwr/rwrLib.c new file mode 100644 index 000000000..77cf231de --- /dev/null +++ b/abc_with_bb_support/src/opt/rwr/rwrLib.c @@ -0,0 +1,362 @@ +/**CFile**************************************************************** + + FileName [rwrLib.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: rwrLib.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "rwr.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static Rwr_Node_t * Rwr_ManTryNode( Rwr_Man_t * p, Rwr_Node_t * p0, Rwr_Node_t * p1, int fExor, int Level, int Volume ); +static void Rwr_MarkUsed_rec( Rwr_Man_t * p, Rwr_Node_t * pNode ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Precomputes the forest in the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_ManPrecompute( Rwr_Man_t * p ) +{ + Rwr_Node_t * p0, * p1; + int i, k, Level, Volume; + int LevelOld = -1; + int nNodes; + + Vec_PtrForEachEntryStart( p->vForest, p0, i, 1 ) + Vec_PtrForEachEntryStart( p->vForest, p1, k, 1 ) + { + if ( LevelOld < (int)p0->Level ) + { + LevelOld = p0->Level; + printf( "Starting level %d (at %d nodes).\n", LevelOld+1, i ); + printf( "Considered = %5d M. Found = %8d. Classes = %6d. Trying %7d.\n", + p->nConsidered/1000000, p->vForest->nSize, p->nClasses, i ); + } + + if ( k == i ) + break; +// if ( p0->Level + p1->Level > 6 ) // hard +// break; + + if ( p0->Level + p1->Level > 5 ) // easy + break; + +// if ( p0->Level + p1->Level > 6 || (p0->Level == 3 && p1->Level == 3) ) +// break; + + // compute the level and volume of the new nodes + Level = 1 + ABC_MAX( p0->Level, p1->Level ); + Volume = 1 + Rwr_ManNodeVolume( p, p0, p1 ); + // try four different AND nodes + Rwr_ManTryNode( p, p0 , p1 , 0, Level, Volume ); + Rwr_ManTryNode( p, Rwr_Not(p0), p1 , 0, Level, Volume ); + Rwr_ManTryNode( p, p0 , Rwr_Not(p1), 0, Level, Volume ); + Rwr_ManTryNode( p, Rwr_Not(p0), Rwr_Not(p1), 0, Level, Volume ); + // try EXOR + Rwr_ManTryNode( p, p0 , p1 , 1, Level, Volume + 1 ); + // report the progress + if ( p->nConsidered % 50000000 == 0 ) + printf( "Considered = %5d M. Found = %8d. Classes = %6d. Trying %7d.\n", + p->nConsidered/1000000, p->vForest->nSize, p->nClasses, i ); + // quit after some time + if ( p->vForest->nSize == RWR_LIMIT + 5 ) + { + printf( "Considered = %5d M. Found = %8d. Classes = %6d. Trying %7d.\n", + p->nConsidered/1000000, p->vForest->nSize, p->nClasses, i ); + goto save; + } + } +save : + + // mark the relevant ones + Rwr_ManIncTravId( p ); + k = 5; + nNodes = 0; + Vec_PtrForEachEntryStart( p->vForest, p0, i, 5 ) + if ( p0->uTruth == p->puCanons[p0->uTruth] ) + { + Rwr_MarkUsed_rec( p, p0 ); + nNodes++; + } + + // compact the array by throwing away non-canonical + k = 5; + Vec_PtrForEachEntryStart( p->vForest, p0, i, 5 ) + if ( p0->fUsed ) + { + p->vForest->pArray[k] = p0; + p0->Id = k++; + } + p->vForest->nSize = k; + printf( "Total canonical = %4d. Total used = %5d.\n", nNodes, p->vForest->nSize ); +} + +/**Function************************************************************* + + Synopsis [Adds one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Rwr_Node_t * Rwr_ManTryNode( Rwr_Man_t * p, Rwr_Node_t * p0, Rwr_Node_t * p1, int fExor, int Level, int Volume ) +{ + Rwr_Node_t * pOld, * pNew, ** ppPlace; + unsigned uTruth; + // compute truth table, level, volume + p->nConsidered++; + if ( fExor ) + { +// printf( "Considering EXOR of %d and %d.\n", p0->Id, p1->Id ); + uTruth = (p0->uTruth ^ p1->uTruth); + } + else + uTruth = (Rwr_IsComplement(p0)? ~Rwr_Regular(p0)->uTruth : Rwr_Regular(p0)->uTruth) & + (Rwr_IsComplement(p1)? ~Rwr_Regular(p1)->uTruth : Rwr_Regular(p1)->uTruth) & 0xFFFF; + // skip non-practical classes + if ( Level > 2 && !p->pPractical[p->puCanons[uTruth]] ) + return NULL; + // enumerate through the nodes with the same canonical form + ppPlace = p->pTable + uTruth; + for ( pOld = *ppPlace; pOld; ppPlace = &pOld->pNext, pOld = pOld->pNext ) + { + if ( pOld->Level < (unsigned)Level && pOld->Volume < (unsigned)Volume ) + return NULL; + if ( pOld->Level == (unsigned)Level && pOld->Volume < (unsigned)Volume ) + return NULL; +// if ( pOld->Level < (unsigned)Level && pOld->Volume == (unsigned)Volume ) +// return NULL; + } +/* + // enumerate through the nodes with the opposite polarity + for ( pOld = p->pTable[~uTruth & 0xFFFF]; pOld; pOld = pOld->pNext ) + { + if ( pOld->Level < (unsigned)Level && pOld->Volume < (unsigned)Volume ) + return NULL; + if ( pOld->Level == (unsigned)Level && pOld->Volume < (unsigned)Volume ) + return NULL; +// if ( pOld->Level < (unsigned)Level && pOld->Volume == (unsigned)Volume ) +// return NULL; + } +*/ + // count the classes + if ( p->pTable[uTruth] == NULL && p->puCanons[uTruth] == uTruth ) + p->nClasses++; + // create the new node + pNew = (Rwr_Node_t *)Extra_MmFixedEntryFetch( p->pMmNode ); + pNew->Id = p->vForest->nSize; + pNew->TravId = 0; + pNew->uTruth = uTruth; + pNew->Level = Level; + pNew->Volume = Volume; + pNew->fUsed = 0; + pNew->fExor = fExor; + pNew->p0 = p0; + pNew->p1 = p1; + pNew->pNext = NULL; + Vec_PtrPush( p->vForest, pNew ); + *ppPlace = pNew; + return pNew; +} + +/**Function************************************************************* + + Synopsis [Adds one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Rwr_Node_t * Rwr_ManAddNode( Rwr_Man_t * p, Rwr_Node_t * p0, Rwr_Node_t * p1, int fExor, int Level, int Volume ) +{ + Rwr_Node_t * pNew; + unsigned uTruth; + // compute truth table, leve, volume + p->nConsidered++; + if ( fExor ) + uTruth = (p0->uTruth ^ p1->uTruth); + else + uTruth = (Rwr_IsComplement(p0)? ~Rwr_Regular(p0)->uTruth : Rwr_Regular(p0)->uTruth) & + (Rwr_IsComplement(p1)? ~Rwr_Regular(p1)->uTruth : Rwr_Regular(p1)->uTruth) & 0xFFFF; + // create the new node + pNew = (Rwr_Node_t *)Extra_MmFixedEntryFetch( p->pMmNode ); + pNew->Id = p->vForest->nSize; + pNew->TravId = 0; + pNew->uTruth = uTruth; + pNew->Level = Level; + pNew->Volume = Volume; + pNew->fUsed = 0; + pNew->fExor = fExor; + pNew->p0 = p0; + pNew->p1 = p1; + pNew->pNext = NULL; + Vec_PtrPush( p->vForest, pNew ); + // do not add if the node is not essential + if ( uTruth != p->puCanons[uTruth] ) + return pNew; + + // add to the list + p->nAdded++; + if ( p->pTable[uTruth] == NULL ) + p->nClasses++; + Rwr_ListAddToTail( p->pTable + uTruth, pNew ); + return pNew; +} + +/**Function************************************************************* + + Synopsis [Adds one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Rwr_Node_t * Rwr_ManAddVar( Rwr_Man_t * p, unsigned uTruth, int fPrecompute ) +{ + Rwr_Node_t * pNew; + pNew = (Rwr_Node_t *)Extra_MmFixedEntryFetch( p->pMmNode ); + pNew->Id = p->vForest->nSize; + pNew->TravId = 0; + pNew->uTruth = uTruth; + pNew->Level = 0; + pNew->Volume = 0; + pNew->fUsed = 1; + pNew->fExor = 0; + pNew->p0 = NULL; + pNew->p1 = NULL; + pNew->pNext = NULL; + Vec_PtrPush( p->vForest, pNew ); + if ( fPrecompute ) + Rwr_ListAddToTail( p->pTable + uTruth, pNew ); + return pNew; +} + + + +/**Function************************************************************* + + Synopsis [Adds one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_MarkUsed_rec( Rwr_Man_t * p, Rwr_Node_t * pNode ) +{ + if ( pNode->fUsed || pNode->TravId == p->nTravIds ) + return; + pNode->TravId = p->nTravIds; + pNode->fUsed = 1; + Rwr_MarkUsed_rec( p, Rwr_Regular(pNode->p0) ); + Rwr_MarkUsed_rec( p, Rwr_Regular(pNode->p1) ); +} + +/**Function************************************************************* + + Synopsis [Adds one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_Trav_rec( Rwr_Man_t * p, Rwr_Node_t * pNode, int * pVolume ) +{ + if ( pNode->fUsed || pNode->TravId == p->nTravIds ) + return; + pNode->TravId = p->nTravIds; + (*pVolume)++; + if ( pNode->fExor ) + (*pVolume)++; + Rwr_Trav_rec( p, Rwr_Regular(pNode->p0), pVolume ); + Rwr_Trav_rec( p, Rwr_Regular(pNode->p1), pVolume ); +} + +/**Function************************************************************* + + Synopsis [Adds one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Rwr_ManNodeVolume( Rwr_Man_t * p, Rwr_Node_t * p0, Rwr_Node_t * p1 ) +{ + int Volume = 0; + Rwr_ManIncTravId( p ); + Rwr_Trav_rec( p, p0, &Volume ); + Rwr_Trav_rec( p, p1, &Volume ); + return Volume; +} + +/**Function************************************************************* + + Synopsis [Adds one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_ManIncTravId( Rwr_Man_t * p ) +{ + Rwr_Node_t * pNode; + int i; + if ( p->nTravIds++ < 0x8FFFFFFF ) + return; + Vec_PtrForEachEntry( p->vForest, pNode, i ) + pNode->TravId = 0; + p->nTravIds = 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/rwr/rwrMan.c b/abc_with_bb_support/src/opt/rwr/rwrMan.c new file mode 100644 index 000000000..371c1dfbc --- /dev/null +++ b/abc_with_bb_support/src/opt/rwr/rwrMan.c @@ -0,0 +1,318 @@ +/**CFile**************************************************************** + + FileName [rwrMan.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting package.] + + Synopsis [Rewriting manager.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: rwrMan.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "rwr.h" +#include "main.h" +#include "dec.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts rewriting manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Rwr_Man_t * Rwr_ManStart( bool fPrecompute ) +{ + Dec_Man_t * pManDec; + Rwr_Man_t * p; + int clk = clock(); +clk = clock(); + p = ALLOC( Rwr_Man_t, 1 ); + memset( p, 0, sizeof(Rwr_Man_t) ); + p->nFuncs = (1<<16); + pManDec = Abc_FrameReadManDec(); + p->puCanons = pManDec->puCanons; + p->pPhases = pManDec->pPhases; + p->pPerms = pManDec->pPerms; + p->pMap = pManDec->pMap; + // initialize practical NPN classes + p->pPractical = Rwr_ManGetPractical( p ); + // create the table + p->pTable = ALLOC( Rwr_Node_t *, p->nFuncs ); + memset( p->pTable, 0, sizeof(Rwr_Node_t *) * p->nFuncs ); + // create the elementary nodes + p->pMmNode = Extra_MmFixedStart( sizeof(Rwr_Node_t) ); + p->vForest = Vec_PtrAlloc( 100 ); + Rwr_ManAddVar( p, 0x0000, fPrecompute ); // constant 0 + Rwr_ManAddVar( p, 0xAAAA, fPrecompute ); // var A + Rwr_ManAddVar( p, 0xCCCC, fPrecompute ); // var B + Rwr_ManAddVar( p, 0xF0F0, fPrecompute ); // var C + Rwr_ManAddVar( p, 0xFF00, fPrecompute ); // var D + p->nClasses = 5; + // other stuff + p->nTravIds = 1; + p->pPerms4 = Extra_Permutations( 4 ); + p->vLevNums = Vec_IntAlloc( 50 ); + p->vFanins = Vec_PtrAlloc( 50 ); + p->vFaninsCur = Vec_PtrAlloc( 50 ); + p->vNodesTemp = Vec_PtrAlloc( 50 ); + if ( fPrecompute ) + { // precompute subgraphs + Rwr_ManPrecompute( p ); +// Rwr_ManPrint( p ); + Rwr_ManWriteToArray( p ); + } + else + { // load saved subgraphs + Rwr_ManLoadFromArray( p, 0 ); +// Rwr_ManPrint( p ); + Rwr_ManPreprocess( p ); + } +p->timeStart = clock() - clk; + return p; +} + +/**Function************************************************************* + + Synopsis [Stops rewriting manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_ManStop( Rwr_Man_t * p ) +{ + if ( p->vClasses ) + { + Rwr_Node_t * pNode; + int i, k; + Vec_VecForEachEntry( p->vClasses, pNode, i, k ) + Dec_GraphFree( (Dec_Graph_t *)pNode->pNext ); + } + if ( p->vClasses ) Vec_VecFree( p->vClasses ); + Vec_PtrFree( p->vNodesTemp ); + Vec_PtrFree( p->vForest ); + Vec_IntFree( p->vLevNums ); + Vec_PtrFree( p->vFanins ); + Vec_PtrFree( p->vFaninsCur ); + Extra_MmFixedStop( p->pMmNode ); + FREE( p->pMapInv ); + free( p->pTable ); + free( p->pPractical ); + free( p->pPerms4 ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Stops the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_ManPrintStats( Rwr_Man_t * p ) +{ + int i, Counter = 0; + for ( i = 0; i < 222; i++ ) + Counter += (p->nScores[i] > 0); + + printf( "Rewriting statistics:\n" ); + printf( "Total cuts tries = %8d.\n", p->nCutsGood ); + printf( "Bad cuts found = %8d.\n", p->nCutsBad ); + printf( "Total subgraphs = %8d.\n", p->nSubgraphs ); + printf( "Used NPN classes = %8d.\n", Counter ); + printf( "Nodes considered = %8d.\n", p->nNodesConsidered ); + printf( "Nodes rewritten = %8d.\n", p->nNodesRewritten ); + printf( "Gain = %8d. (%6.2f %%).\n", p->nNodesBeg-p->nNodesEnd, 100.0*(p->nNodesBeg-p->nNodesEnd)/p->nNodesBeg ); + PRT( "Start ", p->timeStart ); + PRT( "Cuts ", p->timeCut ); + PRT( "Resynthesis ", p->timeRes ); + PRT( " Mffc ", p->timeMffc ); + PRT( " Eval ", p->timeEval ); + PRT( "Update ", p->timeUpdate ); + PRT( "TOTAL ", p->timeTotal ); + +/* + printf( "The scores are:\n" ); + for ( i = 0; i < 222; i++ ) + if ( p->nScores[i] > 0 ) + { + extern void Ivy_TruthDsdComputePrint( unsigned uTruth ); + printf( "%3d = %8d canon = %5d ", i, p->nScores[i], p->pMapInv[i] ); + Ivy_TruthDsdComputePrint( (unsigned)p->pMapInv[i] | ((unsigned)p->pMapInv[i] << 16) ); + } +*/ + printf( "\n" ); + +} + +/**Function************************************************************* + + Synopsis [Stops the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_ManPrintStatsFile( Rwr_Man_t * p ) +{ + FILE * pTable; + pTable = fopen( "stats.txt", "a+" ); + fprintf( pTable, "%d ", p->nCutsGood ); + fprintf( pTable, "%d ", p->nSubgraphs ); + fprintf( pTable, "%d ", p->nNodesRewritten ); + fprintf( pTable, "%d", p->nNodesGained ); + fprintf( pTable, "\n" ); + fclose( pTable ); +} + +/**Function************************************************************* + + Synopsis [Stops the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void * Rwr_ManReadDecs( Rwr_Man_t * p ) +{ + return p->pGraph; +} + +/**Function************************************************************* + + Synopsis [Stops the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Rwr_ManReadLeaves( Rwr_Man_t * p ) +{ + return p->vFanins; +} + +/**Function************************************************************* + + Synopsis [Stops the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Rwr_ManReadCompl( Rwr_Man_t * p ) +{ + return p->fCompl; +} + +/**Function************************************************************* + + Synopsis [Stops the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_ManAddTimeCuts( Rwr_Man_t * p, int Time ) +{ + p->timeCut += Time; +} + +/**Function************************************************************* + + Synopsis [Stops the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_ManAddTimeUpdate( Rwr_Man_t * p, int Time ) +{ + p->timeUpdate += Time; +} + +/**Function************************************************************* + + Synopsis [Stops the resynthesis manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_ManAddTimeTotal( Rwr_Man_t * p, int Time ) +{ + p->timeTotal += Time; +} + + +/**Function************************************************************* + + Synopsis [Precomputes AIG subgraphs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_Precompute() +{ + Rwr_Man_t * p; + p = Rwr_ManStart( 1 ); + Rwr_ManStop( p ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/rwr/rwrPrint.c b/abc_with_bb_support/src/opt/rwr/rwrPrint.c new file mode 100644 index 000000000..bbca1881c --- /dev/null +++ b/abc_with_bb_support/src/opt/rwr/rwrPrint.c @@ -0,0 +1,266 @@ +/**CFile**************************************************************** + + FileName [rwrCut.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting package.] + + Synopsis [Cut computation.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: rwrCut.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "rwr.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Adds one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_Trav2_rec( Rwr_Man_t * p, Rwr_Node_t * pNode, int * pVolume ) +{ + if ( pNode->fUsed || pNode->TravId == p->nTravIds ) + return; + pNode->TravId = p->nTravIds; + (*pVolume)++; + Rwr_Trav2_rec( p, Rwr_Regular(pNode->p0), pVolume ); + Rwr_Trav2_rec( p, Rwr_Regular(pNode->p1), pVolume ); +} + +/**Function************************************************************* + + Synopsis [Adds the node to the end of the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_GetBushVolume( Rwr_Man_t * p, int Entry, int * pVolume, int * pnFuncs ) +{ + Rwr_Node_t * pNode; + int Volume = 0; + int nFuncs = 0; + Rwr_ManIncTravId( p ); + for ( pNode = p->pTable[Entry]; pNode; pNode = pNode->pNext ) + { + if ( pNode->uTruth != p->puCanons[pNode->uTruth] ) + continue; + nFuncs++; + Rwr_Trav2_rec( p, pNode, &Volume ); + } + *pVolume = Volume; + *pnFuncs = nFuncs; +} + +/**Function************************************************************* + + Synopsis [Adds the node to the end of the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Rwr_GetBushSumOfVolumes( Rwr_Man_t * p, int Entry ) +{ + Rwr_Node_t * pNode; + int Volume, VolumeTotal = 0; + for ( pNode = p->pTable[Entry]; pNode; pNode = pNode->pNext ) + { + if ( pNode->uTruth != p->puCanons[pNode->uTruth] ) + continue; + Volume = 0; + Rwr_ManIncTravId( p ); + Rwr_Trav2_rec( p, pNode, &Volume ); + VolumeTotal += Volume; + } + return VolumeTotal; +} + +/**Function************************************************************* + + Synopsis [Prints one rwr node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_NodePrint_rec( FILE * pFile, Rwr_Node_t * pNode ) +{ + assert( !Rwr_IsComplement(pNode) ); + + if ( pNode->Id == 0 ) + { + fprintf( pFile, "Const1" ); + return; + } + + if ( pNode->Id < 5 ) + { + fprintf( pFile, "%c", 'a' + pNode->Id - 1 ); + return; + } + + if ( Rwr_IsComplement(pNode->p0) ) + { + if ( Rwr_Regular(pNode->p0)->Id < 5 ) + { + Rwr_NodePrint_rec( pFile, Rwr_Regular(pNode->p0) ); + fprintf( pFile, "\'" ); + } + else + { + fprintf( pFile, "(" ); + Rwr_NodePrint_rec( pFile, Rwr_Regular(pNode->p0) ); + fprintf( pFile, ")\'" ); + } + } + else + { + if ( Rwr_Regular(pNode->p0)->Id < 5 ) + { + Rwr_NodePrint_rec( pFile, Rwr_Regular(pNode->p0) ); + } + else + { + fprintf( pFile, "(" ); + Rwr_NodePrint_rec( pFile, Rwr_Regular(pNode->p0) ); + fprintf( pFile, ")" ); + } + } + + if ( pNode->fExor ) + fprintf( pFile, "+" ); + + if ( Rwr_IsComplement(pNode->p1) ) + { + if ( Rwr_Regular(pNode->p1)->Id < 5 ) + { + Rwr_NodePrint_rec( pFile, Rwr_Regular(pNode->p1) ); + fprintf( pFile, "\'" ); + } + else + { + fprintf( pFile, "(" ); + Rwr_NodePrint_rec( pFile, Rwr_Regular(pNode->p1) ); + fprintf( pFile, ")\'" ); + } + } + else + { + if ( Rwr_Regular(pNode->p1)->Id < 5 ) + { + Rwr_NodePrint_rec( pFile, Rwr_Regular(pNode->p1) ); + } + else + { + fprintf( pFile, "(" ); + Rwr_NodePrint_rec( pFile, Rwr_Regular(pNode->p1) ); + fprintf( pFile, ")" ); + } + } +} + +/**Function************************************************************* + + Synopsis [Prints one rwr node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_NodePrint( FILE * pFile, Rwr_Man_t * p, Rwr_Node_t * pNode ) +{ + unsigned uTruth; + fprintf( pFile, "%5d : ", pNode->Id ); + Extra_PrintHex( pFile, pNode->uTruth, 4 ); + fprintf( pFile, " tt=" ); + uTruth = pNode->uTruth; + Extra_PrintBinary( pFile, &uTruth, 16 ); +// fprintf( pFile, " cn=", pNode->Id ); +// uTruth = p->puCanons[pNode->uTruth]; +// Extra_PrintBinary( pFile, &uTruth, 16 ); + fprintf( pFile, " lev=%d", pNode->Level ); + fprintf( pFile, " vol=%d", pNode->Volume ); + fprintf( pFile, " " ); + Rwr_NodePrint_rec( pFile, pNode ); + fprintf( pFile, "\n" ); +} + +/**Function************************************************************* + + Synopsis [Prints one rwr node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_ManPrint( Rwr_Man_t * p ) +{ + FILE * pFile; + Rwr_Node_t * pNode; + unsigned uTruth; + int Limit, Counter, Volume, nFuncs, i; + pFile = fopen( "graph_lib.txt", "w" ); + Counter = 0; + Limit = (1 << 16); + for ( i = 0; i < Limit; i++ ) + { + if ( p->pTable[i] == NULL ) + continue; + if ( i != p->puCanons[i] ) + continue; + fprintf( pFile, "\nClass %3d. Func %6d. ", p->pMap[i], Counter++ ); + Rwr_GetBushVolume( p, i, &Volume, &nFuncs ); + fprintf( pFile, "Roots = %3d. Vol = %3d. Sum = %3d. ", nFuncs, Volume, Rwr_GetBushSumOfVolumes(p, i) ); + uTruth = i; + Extra_PrintBinary( pFile, &uTruth, 16 ); + fprintf( pFile, "\n" ); + for ( pNode = p->pTable[i]; pNode; pNode = pNode->pNext ) + if ( pNode->uTruth == p->puCanons[pNode->uTruth] ) + Rwr_NodePrint( pFile, p, pNode ); + } + fclose( pFile ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/rwr/rwrTemp.c b/abc_with_bb_support/src/opt/rwr/rwrTemp.c new file mode 100644 index 000000000..c57750681 --- /dev/null +++ b/abc_with_bb_support/src/opt/rwr/rwrTemp.c @@ -0,0 +1,121 @@ +/**CFile**************************************************************** + + FileName [rwrCut.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting package.] + + Synopsis [Cut computation.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: rwrCut.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "rwr.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int pTruths[13719]; +static int pFreqs[13719]; +static int pPerm[13719]; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Rwr_TempCompare( int * pNum1, int * pNum2 ) +{ + int Freq1 = pFreqs[*pNum1]; + int Freq2 = pFreqs[*pNum2]; + if ( Freq1 < Freq2 ) + return 1; + if ( Freq1 > Freq2 ) + return -1; + return 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_Temp() +{ + char Buffer[32]; + int nFuncs = 13719; + int nEntries = 100; + unsigned uTruth; + int i, k; + FILE * pFile; + + pFile = fopen( "nnclass_stats5.txt", "r" ); + for ( i = 0; i < 13719; i++ ) + { + fscanf( pFile, "%s%d", Buffer, &pFreqs[i] ); + Extra_ReadHexadecimal( &uTruth, Buffer+2, 5 ); + pTruths[i] = uTruth; + } + fclose( pFile ); + + for ( i = 0; i < 13719; i++ ) + pPerm[i] = i; + + qsort( (void *)pPerm, 13719, sizeof(int), + (int (*)(const void *, const void *)) Rwr_TempCompare ); + + + pFile = fopen( "5npn_100.blif", "w" ); + fprintf( pFile, "# Most frequent NPN classes of 5 vars.\n" ); + fprintf( pFile, ".model 5npn\n" ); + fprintf( pFile, ".inputs a b c d e\n" ); + fprintf( pFile, ".outputs" ); + for ( i = 0; i < nEntries; i++ ) + fprintf( pFile, " %02d", i ); + fprintf( pFile, "\n" ); + + for ( i = 0; i < nEntries; i++ ) + { + fprintf( pFile, ".names a b c d e %02d\n", i ); + uTruth = pTruths[pPerm[i]]; + for ( k = 0; k < 32; k++ ) + if ( uTruth & (1 << k) ) + { + Extra_PrintBinary( pFile, &k, 5 ); + fprintf( pFile, " 1\n" ); + } + } + fprintf( pFile, ".end\n" ); + fclose( pFile ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/rwr/rwrUtil.c b/abc_with_bb_support/src/opt/rwr/rwrUtil.c new file mode 100644 index 000000000..3cb65945e --- /dev/null +++ b/abc_with_bb_support/src/opt/rwr/rwrUtil.c @@ -0,0 +1,659 @@ +/**CFile**************************************************************** + + FileName [rwrUtil.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [DAG-aware AIG rewriting package.] + + Synopsis [Various utilities.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: rwrUtil.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "rwr.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// precomputed data +#ifdef _WIN32 +unsigned short s_RwrPracticalClasses[]; +unsigned short s_RwtAigSubgraphs[]; +#else +static unsigned short s_RwrPracticalClasses[]; +static unsigned short s_RwtAigSubgraphs[]; +#endif + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Writes data.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_ManWriteToArray( Rwr_Man_t * p ) +{ + FILE * pFile; + Rwr_Node_t * pNode; + unsigned Entry0, Entry1; + int i, nEntries, clk = clock(); + // prepare the buffer + nEntries = p->vForest->nSize - 5; + pFile = fopen( "npn4_aig_array.txt", "w" ); + fprintf( pFile, "static unsigned short s_RwtAigSubgraphs[] = \n{" ); + for ( i = 0; i < nEntries; i++ ) + { + if ( i % 5 == 0 ) + fprintf( pFile, "\n " ); + pNode = p->vForest->pArray[i+5]; + Entry0 = (Rwr_Regular(pNode->p0)->Id << 1) | Rwr_IsComplement(pNode->p0); + Entry1 = (Rwr_Regular(pNode->p1)->Id << 1) | Rwr_IsComplement(pNode->p1); + Entry0 = (Entry0 << 1) | pNode->fExor; + Extra_PrintHex( pFile, Entry0, 4 ); + fprintf( pFile, "," ); + Extra_PrintHex( pFile, Entry1, 4 ); + fprintf( pFile, ", " ); + } + if ( i % 5 == 0 ) + fprintf( pFile, "\n " ); + Extra_PrintHex( pFile, 0, 4 ); + fprintf( pFile, "," ); + Extra_PrintHex( pFile, 0, 4 ); + fprintf( pFile, " \n};\n" ); + fclose( pFile ); + printf( "The number of nodes saved = %d. ", nEntries ); PRT( "Saving", clock() - clk ); +} + +/**Function************************************************************* + + Synopsis [Loads data.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_ManLoadFromArray( Rwr_Man_t * p, int fVerbose ) +{ + unsigned short * pArray = s_RwtAigSubgraphs; + Rwr_Node_t * p0, * p1; + unsigned Entry0, Entry1; + int Level, Volume, nEntries, fExor; + int i, clk = clock(); + + // reconstruct the forest + for ( i = 0; ; i++ ) + { + Entry0 = pArray[2*i + 0]; + Entry1 = pArray[2*i + 1]; + if ( Entry0 == 0 && Entry1 == 0 ) + break; + // get EXOR flag + fExor = (Entry0 & 1); + Entry0 >>= 1; + // get the nodes + p0 = p->vForest->pArray[Entry0 >> 1]; + p1 = p->vForest->pArray[Entry1 >> 1]; + // compute the level and volume of the new nodes + Level = 1 + ABC_MAX( p0->Level, p1->Level ); + Volume = 1 + Rwr_ManNodeVolume( p, p0, p1 ); + // set the complemented attributes + p0 = Rwr_NotCond( p0, (Entry0 & 1) ); + p1 = Rwr_NotCond( p1, (Entry1 & 1) ); + // add the node +// Rwr_ManTryNode( p, p0, p1, Level, Volume ); + Rwr_ManAddNode( p, p0, p1, fExor, Level, Volume + fExor ); + } + nEntries = i - 1; + if ( fVerbose ) + { + printf( "The number of classes = %d. Canonical nodes = %d.\n", p->nClasses, p->nAdded ); + printf( "The number of nodes loaded = %d. ", nEntries ); PRT( "Loading", clock() - clk ); + } +} + + +/**Function************************************************************* + + Synopsis [Writes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_ManWriteToFile( Rwr_Man_t * p, char * pFileName ) +{ + FILE * pFile; + Rwr_Node_t * pNode; + unsigned * pBuffer; + int i, nEntries, clk = clock(); + // prepare the buffer + nEntries = p->vForest->nSize - 5; + pBuffer = ALLOC( unsigned, nEntries * 2 ); + for ( i = 0; i < nEntries; i++ ) + { + pNode = p->vForest->pArray[i+5]; + pBuffer[2*i + 0] = (Rwr_Regular(pNode->p0)->Id << 1) | Rwr_IsComplement(pNode->p0); + pBuffer[2*i + 1] = (Rwr_Regular(pNode->p1)->Id << 1) | Rwr_IsComplement(pNode->p1); + // save EXOR flag + pBuffer[2*i + 0] = (pBuffer[2*i + 0] << 1) | pNode->fExor; + + } + pFile = fopen( pFileName, "wb" ); + fwrite( &nEntries, sizeof(int), 1, pFile ); + fwrite( pBuffer, sizeof(unsigned), nEntries * 2, pFile ); + free( pBuffer ); + fclose( pFile ); + printf( "The number of nodes saved = %d. ", nEntries ); PRT( "Saving", clock() - clk ); +} + +/**Function************************************************************* + + Synopsis [Loads data.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_ManLoadFromFile( Rwr_Man_t * p, char * pFileName ) +{ + FILE * pFile; + Rwr_Node_t * p0, * p1; + unsigned * pBuffer; + int Level, Volume, nEntries, fExor; + int i, clk = clock(); + + // load the data + pFile = fopen( pFileName, "rb" ); + if ( pFile == NULL ) + { + printf( "Rwr_ManLoadFromFile: Cannot open file \"%s\".\n", pFileName ); + return; + } + fread( &nEntries, sizeof(int), 1, pFile ); + pBuffer = ALLOC( unsigned, nEntries * 2 ); + fread( pBuffer, sizeof(unsigned), nEntries * 2, pFile ); + fclose( pFile ); + // reconstruct the forest + for ( i = 0; i < nEntries; i++ ) + { + // get EXOR flag + fExor = (pBuffer[2*i + 0] & 1); + pBuffer[2*i + 0] = (pBuffer[2*i + 0] >> 1); + // get the nodes + p0 = p->vForest->pArray[pBuffer[2*i + 0] >> 1]; + p1 = p->vForest->pArray[pBuffer[2*i + 1] >> 1]; + // compute the level and volume of the new nodes + Level = 1 + ABC_MAX( p0->Level, p1->Level ); + Volume = 1 + Rwr_ManNodeVolume( p, p0, p1 ); + // set the complemented attributes + p0 = Rwr_NotCond( p0, (pBuffer[2*i + 0] & 1) ); + p1 = Rwr_NotCond( p1, (pBuffer[2*i + 1] & 1) ); + // add the node +// Rwr_ManTryNode( p, p0, p1, Level, Volume ); + Rwr_ManAddNode( p, p0, p1, fExor, Level, Volume + fExor ); + } + free( pBuffer ); + printf( "The number of classes = %d. Canonical nodes = %d.\n", p->nClasses, p->nAdded ); + printf( "The number of nodes loaded = %d. ", nEntries ); PRT( "Loading", clock() - clk ); +} + + +/**Function************************************************************* + + Synopsis [Adds the node to the end of the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Rwr_ListAddToTail( Rwr_Node_t ** ppList, Rwr_Node_t * pNode ) +{ + Rwr_Node_t * pTemp; + // find the last one + for ( pTemp = *ppList; pTemp; pTemp = pTemp->pNext ) + ppList = &pTemp->pNext; + // attach at the end + *ppList = pNode; +} + +/**Function************************************************************* + + Synopsis [Create practical classes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Rwr_ManGetPractical( Rwr_Man_t * p ) +{ + char * pPractical; + int i; + pPractical = ALLOC( char, p->nFuncs ); + memset( pPractical, 0, sizeof(char) * p->nFuncs ); + pPractical[0] = 1; + for ( i = 1; ; i++ ) + { + if ( s_RwrPracticalClasses[i] == 0 ) + break; + pPractical[ s_RwrPracticalClasses[i] ] = 1; + } + return pPractical; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + +// the following 135 practical NPN classes of 4-variable functions were computed +// by considering all 4-input cuts appearing in IWLS, MCNC, and ISCAS benchmarks +static unsigned short s_RwrPracticalClasses[] = +{ + 0x0000, 0x0001, 0x0003, 0x0006, 0x0007, 0x000f, 0x0016, 0x0017, 0x0018, 0x0019, 0x001b, + 0x001e, 0x001f, 0x003c, 0x003d, 0x003f, 0x0069, 0x006b, 0x006f, 0x007e, 0x007f, 0x00ff, + 0x0116, 0x0118, 0x0119, 0x011a, 0x011b, 0x011e, 0x011f, 0x012c, 0x012d, 0x012f, 0x013c, + 0x013d, 0x013e, 0x013f, 0x0168, 0x0169, 0x016f, 0x017f, 0x0180, 0x0181, 0x0182, 0x0183, + 0x0186, 0x0189, 0x018b, 0x018f, 0x0198, 0x0199, 0x019b, 0x01a8, 0x01a9, 0x01aa, 0x01ab, + 0x01ac, 0x01ad, 0x01ae, 0x01af, 0x01bf, 0x01e9, 0x01ea, 0x01eb, 0x01ee, 0x01ef, 0x01fe, + 0x033c, 0x033d, 0x033f, 0x0356, 0x0357, 0x0358, 0x0359, 0x035a, 0x035b, 0x035f, 0x0368, + 0x0369, 0x036c, 0x036e, 0x037d, 0x03c0, 0x03c1, 0x03c3, 0x03c7, 0x03cf, 0x03d4, 0x03d5, + 0x03d7, 0x03d8, 0x03d9, 0x03dc, 0x03dd, 0x03de, 0x03fc, 0x0660, 0x0661, 0x0666, 0x0669, + 0x066f, 0x0676, 0x067e, 0x0690, 0x0696, 0x0697, 0x069f, 0x06b1, 0x06b6, 0x06f0, 0x06f2, + 0x06f6, 0x06f9, 0x0776, 0x0778, 0x07b0, 0x07b1, 0x07b4, 0x07bc, 0x07f0, 0x07f2, 0x07f8, + 0x0ff0, 0x1683, 0x1696, 0x1698, 0x169e, 0x16e9, 0x178e, 0x17e8, 0x18e7, 0x19e6, 0x1be4, + 0x1ee1, 0x3cc3, 0x6996, 0x0000 +}; + +static unsigned short s_RwtAigSubgraphs[] = +{ + 0x0008,0x0002, 0x000a,0x0002, 0x0008,0x0003, 0x000a,0x0003, 0x0009,0x0002, + 0x000c,0x0002, 0x000e,0x0002, 0x000c,0x0003, 0x000e,0x0003, 0x000d,0x0002, + 0x000c,0x0004, 0x000e,0x0004, 0x000c,0x0005, 0x000e,0x0005, 0x000d,0x0004, + 0x0010,0x0002, 0x0012,0x0002, 0x0010,0x0003, 0x0012,0x0003, 0x0011,0x0002, + 0x0010,0x0004, 0x0012,0x0004, 0x0010,0x0005, 0x0012,0x0005, 0x0011,0x0004, + 0x0010,0x0006, 0x0012,0x0006, 0x0010,0x0007, 0x0012,0x0007, 0x0011,0x0006, + 0x0016,0x0005, 0x0014,0x0006, 0x0016,0x0006, 0x0014,0x0007, 0x0016,0x0007, + 0x0015,0x0006, 0x0014,0x0008, 0x0016,0x0008, 0x0014,0x0009, 0x0016,0x0009, + 0x0015,0x0008, 0x0018,0x0006, 0x001a,0x0006, 0x0018,0x0007, 0x001a,0x0007, + 0x0019,0x0006, 0x0018,0x0009, 0x001a,0x0009, 0x0019,0x0008, 0x001e,0x0005, + 0x001c,0x0006, 0x001e,0x0006, 0x001c,0x0007, 0x001e,0x0007, 0x001d,0x0006, + 0x001c,0x0008, 0x001e,0x0008, 0x001c,0x0009, 0x001e,0x0009, 0x001d,0x0008, + 0x0020,0x0006, 0x0022,0x0006, 0x0020,0x0007, 0x0022,0x0007, 0x0021,0x0006, + 0x0020,0x0008, 0x0022,0x0008, 0x0020,0x0009, 0x0022,0x0009, 0x0021,0x0008, + 0x0024,0x0006, 0x0026,0x0006, 0x0024,0x0007, 0x0026,0x0007, 0x0025,0x0006, + 0x0026,0x0008, 0x0024,0x0009, 0x0026,0x0009, 0x0025,0x0008, 0x0028,0x0004, + 0x002a,0x0004, 0x0028,0x0005, 0x002a,0x0007, 0x0028,0x0008, 0x002a,0x0009, + 0x0029,0x0008, 0x002a,0x000b, 0x0029,0x000a, 0x002a,0x000f, 0x0029,0x000e, + 0x002a,0x0011, 0x002a,0x0013, 0x002c,0x0004, 0x002e,0x0004, 0x002c,0x0005, + 0x002c,0x0009, 0x002e,0x0009, 0x002d,0x0008, 0x002d,0x000c, 0x002e,0x000f, + 0x002e,0x0011, 0x002e,0x0012, 0x0030,0x0004, 0x0032,0x0007, 0x0032,0x0009, + 0x0031,0x0008, 0x0032,0x000b, 0x0032,0x000d, 0x0032,0x000f, 0x0031,0x000e, + 0x0032,0x0013, 0x0034,0x0004, 0x0036,0x0004, 0x0034,0x0005, 0x0036,0x0005, + 0x0035,0x0004, 0x0036,0x0008, 0x0034,0x0009, 0x0036,0x0009, 0x0035,0x0008, + 0x0036,0x000b, 0x0036,0x000d, 0x0036,0x0011, 0x0035,0x0010, 0x0036,0x0013, + 0x0038,0x0004, 0x0039,0x0004, 0x0038,0x0009, 0x003a,0x0009, 0x0039,0x0008, + 0x0038,0x000b, 0x003a,0x000b, 0x003a,0x000d, 0x003a,0x0011, 0x003a,0x0012, + 0x0038,0x0013, 0x003a,0x0013, 0x003c,0x0002, 0x003e,0x0002, 0x003c,0x0003, + 0x003e,0x0005, 0x003e,0x0007, 0x003c,0x0008, 0x003e,0x0008, 0x003c,0x0009, + 0x003e,0x0009, 0x003d,0x0008, 0x003e,0x000d, 0x003e,0x0011, 0x003e,0x0013, + 0x003e,0x0017, 0x003e,0x001b, 0x003e,0x001d, 0x0040,0x0002, 0x0042,0x0002, + 0x0042,0x0005, 0x0041,0x0006, 0x0042,0x0008, 0x0041,0x0008, 0x0042,0x000d, + 0x0042,0x0011, 0x0042,0x0015, 0x0042,0x0019, 0x0042,0x001b, 0x0042,0x001c, + 0x0041,0x001c, 0x0044,0x0002, 0x0046,0x0003, 0x0045,0x0004, 0x0046,0x0007, + 0x0045,0x0008, 0x0046,0x000b, 0x0046,0x000f, 0x0046,0x0013, 0x0045,0x0012, + 0x0046,0x0017, 0x0046,0x001b, 0x0046,0x0021, 0x0048,0x0002, 0x004a,0x0002, + 0x0048,0x0003, 0x004a,0x0003, 0x0049,0x0002, 0x0048,0x0008, 0x004a,0x0008, + 0x0048,0x0009, 0x004a,0x0009, 0x0049,0x0008, 0x004a,0x000b, 0x004a,0x000f, + 0x004a,0x0011, 0x004a,0x0012, 0x004a,0x0013, 0x004a,0x0015, 0x004a,0x0019, + 0x004a,0x001b, 0x004a,0x001d, 0x004c,0x0002, 0x004c,0x0003, 0x004d,0x0002, + 0x004c,0x0008, 0x004e,0x0008, 0x004c,0x0009, 0x004e,0x0009, 0x004d,0x0008, + 0x004c,0x000b, 0x004e,0x000b, 0x004c,0x000f, 0x004e,0x000f, 0x004e,0x0011, + 0x004c,0x0012, 0x004c,0x0013, 0x004e,0x0013, 0x004e,0x0015, 0x004c,0x0017, + 0x004e,0x0019, 0x004c,0x001b, 0x004e,0x001b, 0x004c,0x001c, 0x004c,0x001d, + 0x004e,0x001d, 0x0050,0x0004, 0x0052,0x0004, 0x0050,0x0006, 0x0052,0x0009, + 0x0052,0x000d, 0x0052,0x000f, 0x0052,0x0013, 0x0052,0x0017, 0x0052,0x0019, + 0x0052,0x001d, 0x0052,0x001f, 0x0052,0x0021, 0x0052,0x0023, 0x0052,0x0024, + 0x0052,0x0025, 0x0051,0x0024, 0x0052,0x0027, 0x0054,0x0004, 0x0056,0x0004, + 0x0054,0x0005, 0x0056,0x0006, 0x0054,0x0007, 0x0056,0x0011, 0x0056,0x001b, + 0x0056,0x001e, 0x0054,0x001f, 0x0056,0x001f, 0x0056,0x0020, 0x0054,0x0021, + 0x0055,0x0020, 0x0056,0x0024, 0x0054,0x0025, 0x0056,0x0025, 0x0055,0x0024, + 0x0054,0x0027, 0x0056,0x0027, 0x0055,0x0026, 0x005a,0x0007, 0x005a,0x0009, + 0x005a,0x000b, 0x005a,0x0015, 0x005a,0x001f, 0x0059,0x0020, 0x0058,0x0024, + 0x005a,0x0024, 0x005a,0x0027, 0x0059,0x0026, 0x005c,0x0004, 0x005e,0x0004, + 0x005c,0x0005, 0x005e,0x0006, 0x005c,0x0007, 0x005d,0x0006, 0x005e,0x000d, + 0x005e,0x0013, 0x005e,0x0017, 0x005c,0x001f, 0x005d,0x001e, 0x005e,0x0020, + 0x005e,0x0021, 0x005e,0x0022, 0x005e,0x0023, 0x005c,0x0024, 0x005e,0x0024, + 0x005c,0x0025, 0x005e,0x0025, 0x005d,0x0024, 0x005e,0x0026, 0x005e,0x0027, + 0x0062,0x0004, 0x0061,0x0004, 0x0062,0x0006, 0x0061,0x0006, 0x0060,0x000f, + 0x0060,0x0013, 0x0062,0x0013, 0x0060,0x0019, 0x0062,0x001c, 0x0060,0x001d, + 0x0062,0x001d, 0x0062,0x001f, 0x0060,0x0021, 0x0060,0x0023, 0x0062,0x0024, + 0x0060,0x0027, 0x0061,0x0026, 0x0064,0x0002, 0x0066,0x0002, 0x0064,0x0006, + 0x0066,0x0007, 0x0066,0x0009, 0x0066,0x000d, 0x0066,0x0013, 0x0066,0x0015, + 0x0066,0x0017, 0x0066,0x0019, 0x0066,0x001a, 0x0065,0x001a, 0x0066,0x001f, + 0x0066,0x0023, 0x0066,0x0027, 0x0066,0x002f, 0x0066,0x0030, 0x006a,0x0002, + 0x0068,0x0003, 0x0068,0x0006, 0x006a,0x0006, 0x006a,0x0011, 0x0068,0x0016, + 0x0068,0x0017, 0x006a,0x0017, 0x006a,0x001a, 0x006a,0x001b, 0x006a,0x0025, + 0x006a,0x002d, 0x006e,0x0003, 0x006e,0x0007, 0x006e,0x0009, 0x006e,0x000b, + 0x006e,0x0015, 0x006e,0x0016, 0x006e,0x0017, 0x006c,0x001a, 0x006e,0x001a, + 0x006e,0x001f, 0x006e,0x002b, 0x006e,0x0035, 0x0070,0x0002, 0x0070,0x0003, + 0x0072,0x0006, 0x0070,0x0007, 0x0071,0x0006, 0x0072,0x000b, 0x0072,0x000f, + 0x0072,0x0013, 0x0070,0x0015, 0x0071,0x0014, 0x0072,0x0017, 0x0072,0x0018, + 0x0070,0x0019, 0x0072,0x0019, 0x0070,0x001a, 0x0070,0x001b, 0x0072,0x001b, + 0x0071,0x001a, 0x0072,0x0021, 0x0072,0x0029, 0x0076,0x0002, 0x0076,0x0003, + 0x0075,0x0002, 0x0076,0x0006, 0x0074,0x0007, 0x0076,0x0007, 0x0075,0x0006, + 0x0076,0x000d, 0x0076,0x0011, 0x0076,0x0013, 0x0075,0x0014, 0x0076,0x0019, + 0x0076,0x001a, 0x0076,0x001b, 0x0075,0x001c, 0x0074,0x0023, 0x0075,0x0022, + 0x0074,0x0026, 0x0076,0x0026, 0x0074,0x0027, 0x0076,0x002b, 0x0076,0x002f, + 0x0078,0x0002, 0x0078,0x0004, 0x007a,0x0004, 0x007a,0x0005, 0x0079,0x0004, + 0x007a,0x0009, 0x007a,0x000a, 0x007a,0x000b, 0x007a,0x000d, 0x007a,0x000f, + 0x007a,0x0010, 0x007a,0x0011, 0x007a,0x0012, 0x007a,0x0013, 0x007a,0x0017, + 0x007a,0x001b, 0x007a,0x0021, 0x007a,0x0027, 0x007a,0x002b, 0x007a,0x002f, + 0x007a,0x0030, 0x0079,0x0034, 0x007a,0x0039, 0x007a,0x003a, 0x007e,0x0002, + 0x007c,0x0004, 0x007e,0x0004, 0x007e,0x000c, 0x007c,0x000d, 0x007e,0x0011, + 0x007e,0x0013, 0x007e,0x001b, 0x007e,0x0025, 0x007e,0x002d, 0x007e,0x0037, + 0x0082,0x0003, 0x0082,0x0005, 0x0082,0x0009, 0x0082,0x000b, 0x0080,0x0010, + 0x0082,0x0010, 0x0082,0x0012, 0x0082,0x0015, 0x0082,0x001f, 0x0082,0x002b, + 0x0082,0x0035, 0x0082,0x0039, 0x0082,0x003f, 0x0084,0x0002, 0x0086,0x0002, + 0x0084,0x0003, 0x0086,0x0003, 0x0085,0x0002, 0x0086,0x0004, 0x0084,0x0005, + 0x0085,0x0004, 0x0086,0x000a, 0x0084,0x000b, 0x0085,0x000a, 0x0086,0x000d, + 0x0086,0x000e, 0x0086,0x000f, 0x0084,0x0010, 0x0084,0x0011, 0x0086,0x0011, + 0x0085,0x0010, 0x0084,0x0012, 0x0084,0x0013, 0x0086,0x0013, 0x0085,0x0012, + 0x0086,0x0019, 0x0086,0x0023, 0x0086,0x0029, 0x0086,0x0033, 0x0086,0x0039, + 0x008a,0x0003, 0x0089,0x0002, 0x0088,0x0004, 0x008a,0x0004, 0x0088,0x0005, + 0x0089,0x0004, 0x008a,0x000b, 0x008a,0x0010, 0x0088,0x0011, 0x008a,0x0011, + 0x0089,0x0010, 0x0088,0x0012, 0x008a,0x0012, 0x0089,0x0012, 0x008a,0x0017, + 0x008a,0x001b, 0x0089,0x0020, 0x008a,0x0025, 0x0088,0x0027, 0x008a,0x002b, + 0x008a,0x002f, 0x008a,0x0039, 0x0088,0x003a, 0x008d,0x0044, 0x0092,0x0009, + 0x0092,0x0025, 0x0092,0x0029, 0x0092,0x002d, 0x0092,0x0033, 0x0092,0x0037, + 0x0092,0x003d, 0x0092,0x0041, 0x0095,0x0002, 0x0095,0x0004, 0x0095,0x0010, + 0x0095,0x0012, 0x0096,0x0021, 0x0096,0x0029, 0x0095,0x002e, 0x0096,0x0030, + 0x0096,0x0033, 0x0096,0x003a, 0x0096,0x0043, 0x009a,0x0008, 0x009a,0x0009, + 0x0099,0x0008, 0x009a,0x0011, 0x009a,0x0023, 0x009a,0x0033, 0x009a,0x003d, + 0x009a,0x0044, 0x009a,0x0045, 0x0099,0x0044, 0x009d,0x0002, 0x009e,0x0008, + 0x009c,0x0009, 0x009e,0x0009, 0x009d,0x0008, 0x009e,0x0011, 0x009d,0x0010, + 0x009e,0x001f, 0x009e,0x003f, 0x00a0,0x0009, 0x00a0,0x0011, 0x00a2,0x0030, + 0x00a2,0x0033, 0x00a6,0x0006, 0x00a6,0x0007, 0x00a6,0x0011, 0x00a6,0x0044, + 0x00a6,0x004b, 0x00aa,0x0007, 0x00aa,0x0015, 0x00ae,0x0006, 0x00ae,0x0011, + 0x00ae,0x001b, 0x00ae,0x0025, 0x00ae,0x003d, 0x00ae,0x0041, 0x00ae,0x0043, + 0x00ae,0x0045, 0x00b2,0x0006, 0x00b0,0x0007, 0x00b1,0x0006, 0x00b2,0x0017, + 0x00b1,0x0016, 0x00b0,0x0019, 0x00b2,0x0021, 0x00b2,0x003d, 0x00b5,0x004a, + 0x00ba,0x0009, 0x00ba,0x000f, 0x00bc,0x0009, 0x00be,0x0009, 0x00be,0x000f, + 0x00bd,0x000e, 0x00be,0x0017, 0x00c2,0x0009, 0x00c2,0x0019, 0x00c2,0x001f, + 0x00c2,0x0033, 0x00c6,0x0009, 0x00c5,0x000e, 0x00c6,0x0015, 0x00c6,0x0023, + 0x00c4,0x002d, 0x00c6,0x002f, 0x00c5,0x002e, 0x00c6,0x0045, 0x00ce,0x0007, + 0x00ce,0x0021, 0x00ce,0x0023, 0x00ce,0x0025, 0x00ce,0x0027, 0x00ce,0x0033, + 0x00ce,0x003d, 0x00d2,0x0006, 0x00d0,0x0015, 0x00d0,0x001b, 0x00d2,0x001b, + 0x00d1,0x001a, 0x00d0,0x001f, 0x00d2,0x0025, 0x00d1,0x0024, 0x00d2,0x0037, + 0x00d2,0x0041, 0x00d2,0x0045, 0x00d9,0x0044, 0x00e1,0x0004, 0x00e2,0x000d, + 0x00e2,0x0021, 0x00e0,0x003a, 0x00e6,0x003d, 0x00e6,0x0061, 0x00e6,0x0067, + 0x00e9,0x0004, 0x00ea,0x0008, 0x00ea,0x0009, 0x00ea,0x0039, 0x00e9,0x0038, + 0x00ea,0x003f, 0x00ec,0x000d, 0x00ee,0x000d, 0x00ee,0x0037, 0x00f2,0x003d, + 0x00f2,0x0062, 0x00f5,0x0002, 0x00fa,0x0017, 0x00fa,0x003d, 0x00fe,0x0006, + 0x00fd,0x0006, 0x00fc,0x0015, 0x00fe,0x001b, 0x00fc,0x0025, 0x00fe,0x0025, + 0x00fd,0x0024, 0x00fe,0x0041, 0x00fe,0x004d, 0x00fd,0x004e, 0x0101,0x0014, + 0x0106,0x004d, 0x010a,0x0009, 0x010a,0x000b, 0x0109,0x000a, 0x010a,0x004f, + 0x010a,0x0058, 0x010e,0x0008, 0x010c,0x0009, 0x010e,0x0009, 0x010d,0x0008, + 0x010e,0x000b, 0x010e,0x002b, 0x010d,0x002a, 0x010e,0x0035, 0x010e,0x003d, + 0x010e,0x003f, 0x010e,0x0049, 0x010e,0x0057, 0x010d,0x0056, 0x010d,0x0058, + 0x0111,0x0004, 0x0111,0x0006, 0x0110,0x0009, 0x0112,0x0009, 0x0111,0x0008, + 0x0112,0x002f, 0x0110,0x0035, 0x0110,0x0037, 0x0112,0x0039, 0x0112,0x003d, + 0x0112,0x003f, 0x0112,0x0045, 0x0111,0x0044, 0x0112,0x004b, 0x0112,0x0059, + 0x0112,0x0069, 0x0112,0x007f, 0x0116,0x0009, 0x0115,0x0008, 0x0114,0x000b, + 0x0116,0x000b, 0x0116,0x0058, 0x011a,0x0015, 0x011a,0x001f, 0x011a,0x002b, + 0x011a,0x003f, 0x011a,0x0049, 0x011a,0x0085, 0x011e,0x0007, 0x011e,0x0019, + 0x011e,0x001b, 0x011e,0x0023, 0x011e,0x0027, 0x011e,0x002f, 0x011e,0x0043, + 0x011e,0x004b, 0x011e,0x004e, 0x011e,0x004f, 0x011e,0x005f, 0x011e,0x0061, + 0x011e,0x0065, 0x011e,0x0083, 0x0122,0x0006, 0x0120,0x0007, 0x0122,0x0007, + 0x0121,0x0006, 0x0122,0x0049, 0x0121,0x004e, 0x0122,0x008f, 0x0125,0x0004, + 0x0124,0x0007, 0x0125,0x0006, 0x0124,0x001b, 0x0126,0x001b, 0x0126,0x0045, + 0x0126,0x0087, 0x0128,0x0007, 0x0129,0x0006, 0x012a,0x0019, 0x012a,0x003d, + 0x012a,0x0051, 0x012a,0x0065, 0x012a,0x0083, 0x012d,0x005a, 0x0132,0x0009, + 0x0132,0x008f, 0x0134,0x0009, 0x0135,0x003e, 0x013a,0x003d, 0x013a,0x0044, + 0x0139,0x0044, 0x013e,0x0009, 0x013d,0x0008, 0x013c,0x003d, 0x013c,0x0044, + 0x013c,0x0053, 0x013e,0x008f, 0x013e,0x0095, 0x0142,0x0044, 0x0142,0x0097, + 0x0142,0x009e, 0x0144,0x0007, 0x0148,0x0015, 0x0148,0x001c, 0x0148,0x001f, + 0x0148,0x0026, 0x0149,0x0086, 0x014d,0x0006, 0x014e,0x0044, 0x014d,0x0048, + 0x014e,0x009e, 0x0152,0x0009, 0x0151,0x00a6, 0x0155,0x0030, 0x015d,0x003a, + 0x0162,0x009e, 0x0164,0x000f, 0x0164,0x0013, 0x0169,0x000e, 0x0174,0x0009, + 0x0179,0x0008, 0x0180,0x0009, 0x0181,0x0044, 0x0186,0x0044, 0x0185,0x0044, + 0x018a,0x0068, 0x0195,0x004e, 0x01a6,0x0009, 0x01a5,0x0008, 0x01b1,0x003a, + 0x01c4,0x0029, 0x01c4,0x0030, 0x01ca,0x008f, 0x01ca,0x0095, 0x01cc,0x0029, + 0x01cc,0x0033, 0x01ce,0x003d, 0x01d6,0x00b2, 0x01d8,0x0009, 0x01d9,0x002a, + 0x01d9,0x0056, 0x01d9,0x00a4, 0x01dd,0x003a, 0x01e2,0x00b2, 0x01e6,0x0013, + 0x01e6,0x009f, 0x01e6,0x00ba, 0x01e6,0x00c0, 0x01e6,0x00d3, 0x01e6,0x00d5, + 0x01e6,0x00e5, 0x01e8,0x0005, 0x01f2,0x0013, 0x01f2,0x0095, 0x01f2,0x009f, + 0x01f2,0x00ba, 0x01f2,0x00c0, 0x01f2,0x00d3, 0x0202,0x008f, 0x0202,0x0095, + 0x0202,0x00f3, 0x0202,0x00f9, 0x020a,0x0044, 0x0209,0x00b4, 0x020e,0x0009, + 0x020d,0x0008, 0x020c,0x003d, 0x020c,0x0044, 0x020c,0x0053, 0x020e,0x008f, + 0x020e,0x0095, 0x020c,0x00b1, 0x020e,0x00f3, 0x020e,0x00f9, 0x0210,0x0013, + 0x0211,0x0024, 0x0210,0x0026, 0x0219,0x0004, 0x021e,0x008f, 0x021e,0x0095, + 0x0221,0x003a, 0x0230,0x0009, 0x0236,0x0009, 0x0234,0x0029, 0x0234,0x0030, + 0x0234,0x0033, 0x0234,0x003a, 0x0234,0x003d, 0x0234,0x0044, 0x0235,0x00a6, + 0x023a,0x0009, 0x023d,0x003a, 0x0245,0x0044, 0x0249,0x003a, 0x024e,0x009e, + 0x024e,0x0106, 0x0251,0x0026, 0x0258,0x0013, 0x0259,0x0024, 0x0258,0x0061, + 0x0259,0x0086, 0x0258,0x00c7, 0x0258,0x00df, 0x0259,0x00ec, 0x0258,0x00fc, + 0x025d,0x0024, 0x025d,0x00de, 0x0260,0x00f6, 0x0268,0x0009, 0x0269,0x0044, + 0x0268,0x00f3, 0x0268,0x00f9, 0x026d,0x003a, 0x0270,0x0068, 0x0275,0x003a, + 0x027a,0x0044, 0x0279,0x0044, 0x027e,0x007e, 0x0281,0x0044, 0x0285,0x0008, + 0x028d,0x0006, 0x028d,0x00d2, 0x0295,0x00cc, 0x0296,0x00f6, 0x0295,0x00f8, + 0x0299,0x0030, 0x029e,0x007e, 0x029d,0x0080, 0x02a6,0x008f, 0x02a6,0x0095, + 0x02aa,0x0029, 0x02aa,0x0030, 0x02b5,0x0008, 0x02b9,0x003a, 0x02bd,0x0004, + 0x02bd,0x00fc, 0x02c2,0x00b2, 0x02c1,0x00b4, 0x02c4,0x0029, 0x02c8,0x0029, + 0x02c8,0x0033, 0x02ca,0x003d, 0x02ce,0x0029, 0x02ce,0x0030, 0x02d2,0x0068, + 0x02d1,0x006a, 0x02d5,0x006a, 0x02d9,0x0008, 0x02de,0x012c, 0x02e2,0x012c, + 0x02e4,0x0009, 0x02e5,0x002a, 0x02e5,0x0056, 0x02e5,0x012c, 0x02ea,0x0029, + 0x02ea,0x0030, 0x02e9,0x0030, 0x02ec,0x0029, 0x02ec,0x0030, 0x02ee,0x012c, + 0x02f1,0x0068, 0x02f1,0x00b2, 0x02f1,0x0108, 0x02f1,0x012c, 0x02f6,0x0013, + 0x02f6,0x0015, 0x02f6,0x001f, 0x02f6,0x0030, 0x02f6,0x0065, 0x02f6,0x0067, + 0x02f6,0x009f, 0x02f6,0x00b6, 0x02f6,0x00b9, 0x02f6,0x00c0, 0x02f6,0x00cf, + 0x02f6,0x0107, 0x02f6,0x010b, 0x02f6,0x010f, 0x02f6,0x0115, 0x02f6,0x012d, + 0x02f6,0x0134, 0x02f6,0x0153, 0x02f6,0x0171, 0x02f6,0x0176, 0x02f8,0x0003, + 0x02fa,0x017b, 0x02fc,0x00ba, 0x02fc,0x00d3, 0x0302,0x0013, 0x0302,0x001f, + 0x0302,0x0030, 0x0302,0x005d, 0x0302,0x0065, 0x0302,0x0067, 0x0302,0x0099, + 0x0302,0x009f, 0x0302,0x00ad, 0x0302,0x00b9, 0x0302,0x00c0, 0x0302,0x00cf, + 0x0301,0x00d2, 0x0301,0x00fe, 0x0302,0x0107, 0x0302,0x010b, 0x0302,0x010f, + 0x0302,0x0117, 0x0302,0x0134, 0x0302,0x0153, 0x0302,0x0157, 0x0302,0x0176, + 0x0306,0x0029, 0x0308,0x00b2, 0x0309,0x00dc, 0x030d,0x00f8, 0x0312,0x00f3, + 0x0318,0x007e, 0x031d,0x0080, 0x0321,0x0008, 0x0321,0x0094, 0x0326,0x017b, + 0x0326,0x0181, 0x0329,0x012e, 0x032a,0x017b, 0x032a,0x0181, 0x032e,0x008f, + 0x032e,0x0095, 0x032e,0x00f3, 0x032e,0x00f9, 0x0332,0x0009, 0x0331,0x0008, + 0x0330,0x003d, 0x0330,0x0044, 0x0330,0x0053, 0x0332,0x008f, 0x0332,0x0095, + 0x0330,0x00b1, 0x0332,0x00f3, 0x0332,0x00f9, 0x0330,0x0127, 0x0332,0x017b, + 0x0332,0x0181, 0x033c,0x0013, 0x033c,0x001c, 0x033d,0x0086, 0x033d,0x00ec, + 0x033d,0x0172, 0x033e,0x019d, 0x0345,0x0002, 0x0344,0x008f, 0x0344,0x00f3, + 0x034d,0x0030, 0x0352,0x0033, 0x0354,0x0029, 0x0354,0x0030, 0x035a,0x0009, + 0x035a,0x017b, 0x035a,0x019b, 0x035a,0x01a2, 0x035e,0x0181, 0x0360,0x0009, + 0x0366,0x0009, 0x0364,0x0029, 0x0364,0x0030, 0x0364,0x0033, 0x0364,0x003a, + 0x0364,0x003d, 0x0364,0x0044, 0x0369,0x0030, 0x0370,0x0029, 0x0370,0x0030, + 0x0376,0x0033, 0x037a,0x0009, 0x037a,0x019b, 0x037a,0x01a2, 0x037c,0x0009, + 0x0382,0x0181, 0x0386,0x0009, 0x0384,0x0029, 0x0384,0x0030, 0x0384,0x0033, + 0x0384,0x003a, 0x0384,0x003d, 0x0384,0x0044, 0x038a,0x0044, 0x038a,0x009e, + 0x038a,0x0106, 0x038a,0x0198, 0x038d,0x010e, 0x038d,0x0152, 0x038d,0x0158, + 0x0392,0x009e, 0x0392,0x0106, 0x0392,0x0198, 0x0395,0x0086, 0x0395,0x009a, + 0x0395,0x00ec, 0x0395,0x0172, 0x0398,0x014e, 0x0398,0x0175, 0x0398,0x018d, + 0x039c,0x0023, 0x039c,0x0027, 0x039c,0x00ef, 0x039c,0x0139, 0x039c,0x0168, + 0x03a0,0x0019, 0x03a0,0x001d, 0x03a0,0x0023, 0x03a0,0x0027, 0x03a1,0x004e, + 0x03a4,0x0162, 0x03a4,0x0183, 0x03a8,0x0013, 0x03a8,0x0027, 0x03a8,0x0133, + 0x03a8,0x0148, 0x03a8,0x0181, 0x03ac,0x0013, 0x03ac,0x0027, 0x03b0,0x017b, + 0x03b0,0x0181, 0x03b4,0x004b, 0x03b4,0x00e0, 0x03b4,0x00fb, 0x03b8,0x000f, + 0x03b8,0x0013, 0x03b8,0x00ab, 0x03b8,0x00bf, 0x03b8,0x00d0, 0x03bd,0x00da, + 0x03bd,0x012c, 0x03c8,0x000f, 0x03c8,0x0013, 0x03c8,0x0019, 0x03c8,0x001d, + 0x03cd,0x0086, 0x03cd,0x00ec, 0x03cd,0x0172, 0x03d2,0x00e0, 0x03d2,0x00ef, + 0x03d2,0x0112, 0x03d2,0x0139, 0x03d2,0x0168, 0x03d6,0x017b, 0x03d6,0x0181, + 0x03da,0x0133, 0x03da,0x0148, 0x03e2,0x0023, 0x03e2,0x0027, 0x03e6,0x0027, + 0x03e6,0x0181, 0x03ee,0x017b, 0x03ee,0x0181, 0x03fe,0x003d, 0x0401,0x012a, + 0x0401,0x019e, 0x0405,0x01a0, 0x040a,0x000d, 0x040a,0x011f, 0x040a,0x016f, + 0x040d,0x012a, 0x0412,0x017b, 0x041a,0x0033, 0x041a,0x003d, 0x041a,0x0181, + 0x0421,0x0086, 0x0421,0x009a, 0x0421,0x00ec, 0x0421,0x0172, 0x042e,0x0205, + 0x043a,0x0205, 0x043e,0x017b, 0x0442,0x01f5, 0x044c,0x0007, 0x0452,0x0033, + 0x0452,0x01ce, 0x0452,0x01d0, 0x0452,0x01f1, 0x0452,0x01fb, 0x0452,0x0225, + 0x0454,0x0005, 0x045a,0x0033, 0x045a,0x0181, 0x045a,0x01ce, 0x045a,0x01d0, + 0x045a,0x01f1, 0x0469,0x01de, 0x046e,0x0181, 0x047a,0x01ce, 0x047a,0x01f1, + 0x0485,0x012c, 0x0489,0x012c, 0x0490,0x01d8, 0x0496,0x0033, 0x0496,0x003d, + 0x0498,0x008f, 0x0498,0x00f3, 0x049e,0x0044, 0x049e,0x0221, 0x04a1,0x0006, + 0x04a2,0x0044, 0x04a6,0x0221, 0x04a9,0x0004, 0x04ac,0x0027, 0x04b1,0x009a, + 0x04b6,0x0097, 0x04b8,0x0027, 0x04c6,0x0219, 0x04ca,0x017b, 0x04cc,0x004b, + 0x04d0,0x00ab, 0x04d6,0x017b, 0x04d8,0x000f, 0x04d8,0x0019, 0x04d8,0x0033, + 0x04d8,0x003d, 0x04de,0x003d, 0x04de,0x0103, 0x04de,0x018b, 0x04de,0x0231, + 0x04e2,0x0044, 0x04e2,0x009e, 0x04e2,0x0106, 0x04e2,0x0198, 0x04e5,0x01a4, + 0x04e5,0x01b6, 0x04ea,0x009e, 0x04ea,0x0106, 0x04ea,0x0198, 0x04ed,0x002e, + 0x04ed,0x0038, 0x04ed,0x00a2, 0x04f1,0x0086, 0x04f1,0x009a, 0x04f1,0x00ec, + 0x04f1,0x0172, 0x04f9,0x004e, 0x04f8,0x0229, 0x04f8,0x022d, 0x0500,0x023e, + 0x0504,0x0217, 0x0510,0x00f3, 0x0514,0x0043, 0x0514,0x004d, 0x0514,0x00c3, + 0x0514,0x013d, 0x0514,0x0215, 0x0514,0x0232, 0x0515,0x0260, 0x0519,0x002a, + 0x0518,0x0030, 0x0518,0x0067, 0x0518,0x00c9, 0x0518,0x01eb, 0x0518,0x01ef, + 0x051c,0x0139, 0x051c,0x0168, 0x0520,0x0027, 0x0526,0x014e, 0x0526,0x0175, + 0x0526,0x018d, 0x052d,0x0200, 0x0532,0x0021, 0x0532,0x00bf, 0x0532,0x00d0, + 0x0532,0x0239, 0x0532,0x0266, 0x053d,0x0024, 0x053d,0x00da, 0x054a,0x000f, + 0x054a,0x00ab, 0x054a,0x023a, 0x054e,0x0043, 0x054e,0x004d, 0x054e,0x00c3, + 0x054e,0x013d, 0x054e,0x0215, 0x054e,0x0232, 0x054e,0x029d, 0x0552,0x014e, + 0x0552,0x018d, 0x0556,0x00f3, 0x0556,0x01e4, 0x055a,0x0299, 0x055d,0x0086, + 0x055d,0x009a, 0x055d,0x00ec, 0x055d,0x0172, 0x0566,0x01dc, 0x0566,0x02a5, + 0x056d,0x020a, 0x057a,0x003d, 0x057a,0x01d4, 0x057a,0x01f3, 0x0579,0x025e, + 0x057e,0x0139, 0x057e,0x0168, 0x0581,0x0006, 0x0586,0x017b, 0x0586,0x0181, + 0x0586,0x028c, 0x0588,0x0007, 0x058e,0x0033, 0x058e,0x008f, 0x058e,0x01d0, + 0x058e,0x027c, 0x0590,0x0003, 0x0596,0x0033, 0x0596,0x008f, 0x0596,0x0095, + 0x0596,0x01d0, 0x0596,0x027c, 0x05a2,0x026f, 0x05a5,0x0284, 0x05aa,0x017b, + 0x05ac,0x0205, 0x05b2,0x008f, 0x05b6,0x017b, 0x05b8,0x01da, 0x05c1,0x0276, + 0x05c6,0x0248, 0x05c8,0x0247, 0x05c8,0x027e, 0x05cc,0x003d, 0x05cc,0x01d4, + 0x05cc,0x01f3, 0x05d0,0x014e, 0x05d0,0x018d, 0x05da,0x00f9, 0x05dd,0x0006, + 0x05de,0x0044, 0x05e5,0x002e, 0x05e6,0x02f1, 0x05ea,0x01d4, 0x05ea,0x01f3, + 0x05ea,0x022d, 0x05ed,0x0002, 0x05f6,0x0027, 0x05fa,0x0097, 0x05fc,0x003d, + 0x0602,0x003d, 0x0606,0x00f3, 0x060a,0x0027, 0x060e,0x003d, 0x060e,0x0103, + 0x060e,0x018b, 0x060e,0x0231, 0x060e,0x02d1, 0x0611,0x01fc, 0x0611,0x0234, + 0x061a,0x0287, 0x061d,0x0214, 0x0621,0x01d4, 0x062a,0x0027, 0x062a,0x022d, + 0x062e,0x009e, 0x062e,0x0106, 0x062e,0x0198, 0x0632,0x009e, 0x0632,0x0106, + 0x0632,0x0198, 0x0639,0x0042, 0x0639,0x00b2, 0x0639,0x0108, 0x063d,0x01f8, + 0x0641,0x0086, 0x0641,0x009a, 0x0641,0x00ec, 0x0641,0x0172, 0x0645,0x0044, + 0x0649,0x0042, 0x0648,0x0087, 0x0648,0x00ed, 0x0648,0x0173, 0x0649,0x01a0, + 0x0648,0x0241, 0x0648,0x026f, 0x0648,0x02df, 0x0648,0x0307, 0x064c,0x023a, + 0x064c,0x02b3, 0x0651,0x0062, 0x0650,0x0217, 0x0651,0x02ac, 0x0650,0x02d6, + 0x0655,0x0042, 0x065d,0x0042, 0x0664,0x02b1, 0x0664,0x02ce, 0x0669,0x0238, + 0x066d,0x002a, 0x066c,0x0039, 0x066d,0x01f6, 0x066c,0x0213, 0x066c,0x022e, + 0x066d,0x02a2, 0x066c,0x02e1, 0x0671,0x002a, 0x0670,0x0030, 0x0670,0x0067, + 0x0670,0x00c9, 0x0670,0x01eb, 0x0670,0x01ef, 0x0670,0x02c3, 0x0675,0x0020, + 0x0678,0x0133, 0x0678,0x0148, 0x067c,0x0027, 0x0681,0x023a, 0x0684,0x0021, + 0x0684,0x00bf, 0x0684,0x00d0, 0x0689,0x01fc, 0x068e,0x0162, 0x068e,0x0183, + 0x0691,0x0200, 0x0696,0x0023, 0x0696,0x00e0, 0x0696,0x00fb, 0x0696,0x0268, + 0x069a,0x0282, 0x069d,0x007e, 0x06a2,0x004b, 0x06a2,0x023e, 0x06a2,0x02dc, + 0x06a6,0x0097, 0x06aa,0x02b1, 0x06aa,0x02ce, 0x06ae,0x0039, 0x06ae,0x0213, + 0x06ae,0x022e, 0x06ae,0x02e1, 0x06b2,0x0162, 0x06b2,0x0183, 0x06b6,0x0023, + 0x06b6,0x00e0, 0x06b6,0x00fb, 0x06ba,0x008f, 0x06ba,0x01e4, 0x06be,0x034b, + 0x06c1,0x0086, 0x06c1,0x009a, 0x06c1,0x00ec, 0x06c1,0x0172, 0x06c6,0x01da, + 0x06c6,0x0280, 0x06c6,0x0351, 0x06ce,0x008f, 0x06d2,0x01e3, 0x06d2,0x0287, + 0x06d2,0x0353, 0x06d6,0x027a, 0x06d6,0x029b, 0x06da,0x0033, 0x06da,0x01ce, + 0x06da,0x01f1, 0x06de,0x0133, 0x06de,0x0148, 0x06e2,0x0021, 0x06e2,0x00bf, + 0x06e2,0x00d0, 0x06e5,0x023a, 0x06e9,0x0004, 0x06ee,0x028c, 0x06ee,0x0338, + 0x06f2,0x0328, 0x06f2,0x0330, 0x06f4,0x0005, 0x06f9,0x01e0, 0x06fe,0x0328, + 0x06fe,0x0330, 0x0702,0x003d, 0x0702,0x00f3, 0x0702,0x0330, 0x0704,0x0003, + 0x070a,0x003d, 0x070a,0x00f3, 0x070a,0x01d4, 0x070a,0x01f3, 0x070a,0x0330, + 0x0711,0x032a, 0x0711,0x032e, 0x0716,0x003d, 0x0718,0x0205, 0x0718,0x0282, + 0x071e,0x00f3, 0x0720,0x01dc, 0x0720,0x02a5, 0x0726,0x0324, 0x072a,0x028a, + 0x072a,0x02a7, 0x0729,0x031c, 0x0729,0x032a, 0x072e,0x003d, 0x072e,0x00f9, + 0x072e,0x022d, 0x072e,0x0248, 0x072e,0x02e4, 0x0730,0x003d, 0x0730,0x0247, + 0x0730,0x02e3, 0x0730,0x0324, 0x0732,0x0324, 0x0739,0x032e, 0x073e,0x003d, + 0x0740,0x003d, 0x0744,0x027a, 0x0744,0x029b, 0x0748,0x0033, 0x0748,0x01ce, + 0x0748,0x01f1, 0x074c,0x0162, 0x074c,0x0183, 0x0750,0x0023, 0x0750,0x00e0, + 0x0750,0x00fb, 0x0755,0x0246, 0x075a,0x0095, 0x075a,0x0397, 0x075d,0x0004, + 0x076a,0x03b3, 0x076d,0x0002, 0x0772,0x02fb, 0x0772,0x0301, 0x0772,0x0315, + 0x0772,0x0397, 0x0776,0x008f, 0x077e,0x0027, 0x078a,0x00a1, 0x0792,0x009d, + 0x0792,0x00c3, 0x0792,0x02fb, 0x0792,0x0301, 0x0792,0x0315, 0x0792,0x03bd, + 0x0796,0x0027, 0x0796,0x024f, 0x079e,0x009d, 0x07a6,0x009d, 0x07a6,0x02fb, + 0x07a6,0x0301, 0x07a6,0x0315, 0x07a6,0x03bd, 0x07aa,0x0027, 0x07aa,0x024f, + 0x07ae,0x009d, 0x07b9,0x004e, 0x07b8,0x0087, 0x07b8,0x00ed, 0x07b8,0x0173, + 0x07b8,0x0197, 0x07b9,0x021a, 0x07b9,0x02b8, 0x07b9,0x0364, 0x07be,0x0029, + 0x07be,0x0030, 0x07c0,0x017b, 0x07c6,0x017b, 0x07c8,0x00f3, 0x07ce,0x00f3, + 0x07d0,0x008f, 0x07d6,0x008f, 0x07d9,0x01e8, 0x07dd,0x0292, 0x07e2,0x0053, + 0x07e6,0x008f, 0x07e6,0x00f3, 0x07e6,0x017b, 0x07e8,0x0029, 0x07e8,0x0030, + 0x07ec,0x0021, 0x07ec,0x02ad, 0x07f2,0x0181, 0x07f2,0x0315, 0x07f4,0x0021, + 0x07f8,0x020f, 0x07fd,0x002e, 0x0800,0x008f, 0x0805,0x0006, 0x0809,0x03c2, + 0x080d,0x0084, 0x0812,0x0009, 0x0811,0x0008, 0x0812,0x00f3, 0x0812,0x00f9, + 0x0812,0x017b, 0x0812,0x0181, 0x0814,0x0033, 0x0818,0x0023, 0x081c,0x0285, + 0x0826,0x03bd, 0x082c,0x008f, 0x082c,0x017b, 0x0832,0x0043, 0x0832,0x011b, + 0x0832,0x01b3, 0x0832,0x01c3, 0x0835,0x032a, 0x0838,0x0085, 0x0839,0x032a, + 0x083e,0x0049, 0x083d,0x0084, 0x083e,0x02fb, 0x083e,0x0301, 0x083e,0x0315, + 0x083e,0x0397, 0x0842,0x0009, 0x0841,0x0008, 0x0844,0x0009, 0x0846,0x008f, + 0x084a,0x0033, 0x084e,0x0285, 0x0851,0x009a, 0x0856,0x00a1, 0x0859,0x031c, + 0x085d,0x00b2, 0x0861,0x0012, 0x0861,0x02cc, 0x0865,0x0058, 0x0865,0x007e, + 0x0869,0x004a, 0x0871,0x0010, 0x0876,0x003d, 0x0879,0x032c, 0x087e,0x0089, + 0x0882,0x0229, 0x0882,0x022d, 0x0882,0x02c7, 0x0882,0x02cb, 0x0886,0x0021, + 0x0886,0x02ad, 0x0885,0x0356, 0x088a,0x0017, 0x088a,0x020f, 0x0889,0x0354, + 0x088d,0x009c, 0x0892,0x0089, 0x0895,0x0246, 0x089a,0x03bd, 0x089e,0x008f, + 0x089e,0x02f9, 0x089e,0x0313, 0x08a1,0x032a, 0x08a6,0x0053, 0x08a6,0x0095, + 0x08a6,0x0397, 0x08a8,0x017b, 0x08ad,0x031a, 0x08b2,0x017b, 0x08b4,0x00f3, + 0x08b5,0x02a0, 0x08b8,0x0089, 0x08c1,0x0024, 0x08c4,0x00f3, 0x08c9,0x007e, + 0x08cd,0x007c, 0x08cd,0x0222, 0x08cd,0x0294, 0x08d1,0x003a, 0x08d6,0x0009, + 0x08d9,0x003a, 0x08dc,0x001f, 0x08e0,0x008f, 0x08e0,0x017b, 0x08e4,0x0009, + 0x08e8,0x01ed, 0x08ed,0x031c, 0x08f2,0x003d, 0x08f6,0x008f, 0x08f6,0x017b, + 0x08fa,0x0009, 0x08fe,0x003d, 0x0902,0x01e9, 0x0904,0x01e9, 0x0904,0x0381, + 0x090a,0x03b1, 0x090d,0x031a, 0x0910,0x0299, 0x0914,0x034b, 0x0919,0x0008, + 0x091c,0x0033, 0x091c,0x003d, 0x0920,0x0027, 0x0924,0x0027, 0x0924,0x01fb, + 0x092a,0x01ce, 0x092a,0x01f1, 0x092d,0x031c, 0x0930,0x001f, 0x0936,0x00c5, + 0x0938,0x00c5, 0x0938,0x0381, 0x093c,0x001b, 0x0942,0x017d, 0x094a,0x0027, + 0x094e,0x0027, 0x094e,0x01fb, 0x0952,0x03b1, 0x095a,0x0029, 0x095a,0x0030, + 0x095d,0x0030, 0x0961,0x0030, 0x0966,0x02f9, 0x0966,0x0313, 0x0968,0x02eb, + 0x096d,0x0008, 0x0970,0x017b, 0x0974,0x0033, 0x0979,0x0150, 0x097d,0x009a, + 0x0982,0x0293, 0x0984,0x0293, 0x0984,0x0379, 0x098a,0x02eb, 0x098e,0x0009, + 0x0992,0x003d, 0x0996,0x003d, 0x0999,0x0062, 0x099e,0x003d, 0x09a0,0x0027, + 0x09a5,0x0144, 0x09a8,0x02b5, 0x09ae,0x008f, 0x09ae,0x009d, 0x09b2,0x004d, + 0x09b2,0x0053, 0x09b2,0x00c3, 0x09b2,0x013d, 0x09b2,0x01c5, 0x09b2,0x0271, + 0x09b4,0x0025, 0x09ba,0x0033, 0x09ba,0x0079, 0x09bc,0x0015, 0x09c2,0x013f, + 0x09c4,0x013f, 0x09c4,0x0379, 0x09ca,0x02b5, 0x09cd,0x0006, 0x09da,0x0009, + 0x09d9,0x0008, 0x09dc,0x000b, 0x09dc,0x004f, 0x09dd,0x0086, 0x09e0,0x0009, + 0x09e6,0x00a1, 0x09e8,0x0009, 0x09ed,0x0086, 0x09f2,0x001f, 0x09f2,0x002f, + 0x09f2,0x0049, 0x09f2,0x006f, 0x09f2,0x0085, 0x09f2,0x0091, 0x09f2,0x00a9, + 0x09f2,0x00d3, 0x09f2,0x00d7, 0x09f2,0x011d, 0x09f2,0x0121, 0x09f2,0x0235, + 0x09f2,0x0393, 0x09f6,0x0324, 0x09f8,0x0049, 0x09f8,0x00a9, 0x09f8,0x011d, + 0x09fe,0x001f, 0x09fe,0x0029, 0x09fe,0x0033, 0x09fe,0x003d, 0x09fe,0x0085, + 0x09fe,0x008f, 0x09fe,0x00d3, 0x0a00,0x003d, 0x0a06,0x012d, 0x0a0e,0x00b3, + 0x0a10,0x000b, 0x0a10,0x0387, 0x0a16,0x0059, 0x0a18,0x0009, 0x0a1e,0x0043, + 0x0a24,0x0085, 0x0a2a,0x0009, 0x0a2d,0x0008, 0x0a32,0x028a, 0x0a32,0x02a7, + 0x0a31,0x031c, 0x0a35,0x032e, 0x0a39,0x0006, 0x0a3a,0x0105, 0x0a3a,0x024f, + 0x0a3c,0x0299, 0x0a42,0x01ed, 0x0a46,0x0299, 0x0a48,0x01ed, 0x0a4c,0x0059, + 0x0a52,0x000b, 0x0a52,0x0387, 0x0a56,0x000b, 0x0a5e,0x0009, 0x0a60,0x003d, + 0x0a66,0x0105, 0x0a6a,0x0195, 0x0a6c,0x000b, 0x0a76,0x0053, 0x0a78,0x0009, + 0x0a7a,0x008f, 0x0a82,0x0299, 0x0a86,0x01ed, 0x0a8a,0x0027, 0x0a8e,0x004b, + 0x0a92,0x003d, 0x0a95,0x0322, 0x0a99,0x0038, 0x0a99,0x0090, 0x0a9c,0x0061, + 0x0a9c,0x00c7, 0x0a9c,0x012d, 0x0a9c,0x016f, 0x0a9c,0x017d, 0x0a9c,0x02c9, + 0x0a9c,0x0383, 0x0aa1,0x0010, 0x0aa4,0x00b3, 0x0aa8,0x002f, 0x0aac,0x0027, + 0x0ab0,0x004b, 0x0ab4,0x0043, 0x0ab9,0x0090, 0x0abd,0x0010, 0x0ac4,0x0019, + 0x0acc,0x00f5, 0x0acc,0x022b, 0x0acc,0x037b, 0x0ad2,0x008f, 0x0ad2,0x01f1, + 0x0ad6,0x0324, 0x0ad9,0x0330, 0x0ade,0x008f, 0x0ade,0x01f1, 0x0ae0,0x017b, + 0x0ae4,0x008f, 0x0ae9,0x004e, 0x0aee,0x0027, 0x0af2,0x028a, 0x0af2,0x02a7, + 0x0af1,0x031c, 0x0af6,0x0027, 0x0af9,0x031c, 0x0afe,0x00e9, 0x0afe,0x02bb, + 0x0b02,0x000b, 0x0b06,0x00f5, 0x0b06,0x022b, 0x0b06,0x037b, 0x0b0a,0x003d, + 0x0000,0x0000 +}; + + diff --git a/abc_with_bb_support/src/opt/sim/module.make b/abc_with_bb_support/src/opt/sim/module.make new file mode 100644 index 000000000..1b4d29574 --- /dev/null +++ b/abc_with_bb_support/src/opt/sim/module.make @@ -0,0 +1,10 @@ +SRC += src/opt/sim/simMan.c \ + src/opt/sim/simSat.c \ + src/opt/sim/simSeq.c \ + src/opt/sim/simSupp.c \ + src/opt/sim/simSwitch.c \ + src/opt/sim/simSym.c \ + src/opt/sim/simSymSat.c \ + src/opt/sim/simSymSim.c \ + src/opt/sim/simSymStr.c \ + src/opt/sim/simUtils.c diff --git a/abc_with_bb_support/src/opt/sim/sim.h b/abc_with_bb_support/src/opt/sim/sim.h new file mode 100644 index 000000000..0359b338d --- /dev/null +++ b/abc_with_bb_support/src/opt/sim/sim.h @@ -0,0 +1,233 @@ +/**CFile**************************************************************** + + FileName [sim.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Simulation package.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: sim.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __SIM_H__ +#define __SIM_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + The ideas realized in this package are described in the paper: + "Detecting Symmetries in Boolean Functions using Circuit Representation, + Simulation, and Satisfiability". +*/ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Sym_Man_t_ Sym_Man_t; +struct Sym_Man_t_ +{ + // info about the network + Abc_Ntk_t * pNtk; // the network + Vec_Ptr_t * vNodes; // internal nodes in topological order + int nInputs; + int nOutputs; + // internal simulation information + int nSimWords; // the number of bits in simulation info + Vec_Ptr_t * vSim; // simulation info + // support information + Vec_Ptr_t * vSuppFun; // bit representation + Vec_Vec_t * vSupports; // integer representation + // symmetry info for each output + Vec_Ptr_t * vMatrSymms; // symmetric pairs + Vec_Ptr_t * vMatrNonSymms; // non-symmetric pairs + Vec_Int_t * vPairsTotal; // total pairs + Vec_Int_t * vPairsSym; // symmetric pairs + Vec_Int_t * vPairsNonSym; // non-symmetric pairs + // temporary simulation info + unsigned * uPatRand; + unsigned * uPatCol; + unsigned * uPatRow; + // temporary + Vec_Int_t * vVarsU; + Vec_Int_t * vVarsV; + int iOutput; + int iVar1; + int iVar2; + int iVar1Old; + int iVar2Old; + // internal data structures + int nSatRuns; + int nSatRunsSat; + int nSatRunsUnsat; + // pairs + int nPairsSymm; + int nPairsSymmStr; + int nPairsNonSymm; + int nPairsRem; + int nPairsTotal; + // runtime statistics + int timeStruct; + int timeCount; + int timeMatr; + int timeSim; + int timeFraig; + int timeSat; + int timeTotal; +}; + +typedef struct Sim_Man_t_ Sim_Man_t; +struct Sim_Man_t_ +{ + // info about the network + Abc_Ntk_t * pNtk; + int nInputs; + int nOutputs; + int fLightweight; + // internal simulation information + int nSimBits; // the number of bits in simulation info + int nSimWords; // the number of words in simulation info + Vec_Ptr_t * vSim0; // simulation info 1 + Vec_Ptr_t * vSim1; // simulation info 2 + // support information + int nSuppBits; // the number of bits in support info + int nSuppWords; // the number of words in support info + Vec_Ptr_t * vSuppStr; // structural supports + Vec_Ptr_t * vSuppFun; // functional supports + // simulation targets + Vec_Vec_t * vSuppTargs; // support targets + int iInput; // the input current processed + // internal data structures + Extra_MmFixed_t * pMmPat; + Vec_Ptr_t * vFifo; + Vec_Int_t * vDiffs; + int nSatRuns; + int nSatRunsSat; + int nSatRunsUnsat; + // runtime statistics + int timeSim; + int timeTrav; + int timeFraig; + int timeSat; + int timeTotal; +}; + +typedef struct Sim_Pat_t_ Sim_Pat_t; +struct Sim_Pat_t_ +{ + int Input; // the input which it has detected + int Output; // the output for which it was collected + unsigned * pData; // the simulation data +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define SIM_NUM_WORDS(n) (((n)>>5) + (((n)&31) > 0)) +#define SIM_LAST_BITS(n) ((((n)&31) > 0)? (n)&31 : 32) + +#define SIM_MASK_FULL (0xFFFFFFFF) +#define SIM_MASK_BEG(n) (SIM_MASK_FULL >> (32-n)) +#define SIM_MASK_END(n) (SIM_MASK_FULL << (n)) +#define SIM_SET_0_FROM(m,n) ((m) & ~SIM_MASK_BEG(n)) +#define SIM_SET_1_FROM(m,n) ((m) | SIM_MASK_END(n)) + +// generating random unsigned (#define RAND_MAX 0x7fff) +#define SIM_RANDOM_UNSIGNED ((((unsigned)rand()) << 24) ^ (((unsigned)rand()) << 12) ^ ((unsigned)rand())) + +// macros to get hold of bits in a bit string +#define Sim_SetBit(p,i) ((p)[(i)>>5] |= (1<<((i) & 31))) +#define Sim_XorBit(p,i) ((p)[(i)>>5] ^= (1<<((i) & 31))) +#define Sim_HasBit(p,i) (((p)[(i)>>5] & (1<<((i) & 31))) > 0) + +// macros to get hold of the support info +#define Sim_SuppStrSetVar(vSupps,pNode,v) Sim_SetBit((unsigned*)(vSupps)->pArray[(pNode)->Id],(v)) +#define Sim_SuppStrHasVar(vSupps,pNode,v) Sim_HasBit((unsigned*)(vSupps)->pArray[(pNode)->Id],(v)) +#define Sim_SuppFunSetVar(vSupps,Output,v) Sim_SetBit((unsigned*)(vSupps)->pArray[Output],(v)) +#define Sim_SuppFunHasVar(vSupps,Output,v) Sim_HasBit((unsigned*)(vSupps)->pArray[Output],(v)) +#define Sim_SimInfoSetVar(vSupps,pNode,v) Sim_SetBit((unsigned*)(vSupps)->pArray[(pNode)->Id],(v)) +#define Sim_SimInfoHasVar(vSupps,pNode,v) Sim_HasBit((unsigned*)(vSupps)->pArray[(pNode)->Id],(v)) +#define Sim_SimInfoGet(vInfo,pNode) ((unsigned *)((vInfo)->pArray[(pNode)->Id])) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== simMan.c ==========================================================*/ +extern Sym_Man_t * Sym_ManStart( Abc_Ntk_t * pNtk, int fVerbose ); +extern void Sym_ManStop( Sym_Man_t * p ); +extern void Sym_ManPrintStats( Sym_Man_t * p ); +extern Sim_Man_t * Sim_ManStart( Abc_Ntk_t * pNtk, int fLightweight ); +extern void Sim_ManStop( Sim_Man_t * p ); +extern void Sim_ManPrintStats( Sim_Man_t * p ); +extern Sim_Pat_t * Sim_ManPatAlloc( Sim_Man_t * p ); +extern void Sim_ManPatFree( Sim_Man_t * p, Sim_Pat_t * pPat ); +/*=== simSeq.c ==========================================================*/ +extern Vec_Ptr_t * Sim_SimulateSeqRandom( Abc_Ntk_t * pNtk, int nFrames, int nWords ); +extern Vec_Ptr_t * Sim_SimulateSeqModel( Abc_Ntk_t * pNtk, int nFrames, int * pModel ); +/*=== simSupp.c ==========================================================*/ +extern Vec_Ptr_t * Sim_ComputeStrSupp( Abc_Ntk_t * pNtk ); +extern Vec_Ptr_t * Sim_ComputeFunSupp( Abc_Ntk_t * pNtk, int fVerbose ); +/*=== simSym.c ==========================================================*/ +extern int Sim_ComputeTwoVarSymms( Abc_Ntk_t * pNtk, int fVerbose ); +/*=== simSymSat.c ==========================================================*/ +extern int Sim_SymmsGetPatternUsingSat( Sym_Man_t * p, unsigned * pPattern ); +/*=== simSymStr.c ==========================================================*/ +extern void Sim_SymmsStructCompute( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMatrs, Vec_Ptr_t * vSuppFun ); +/*=== simSymSim.c ==========================================================*/ +extern void Sim_SymmsSimulate( Sym_Man_t * p, unsigned * pPatRand, Vec_Ptr_t * vMatrsNonSym ); +/*=== simUtil.c ==========================================================*/ +extern Vec_Ptr_t * Sim_UtilInfoAlloc( int nSize, int nWords, bool fClean ); +extern void Sim_UtilInfoFree( Vec_Ptr_t * p ); +extern void Sim_UtilInfoAdd( unsigned * pInfo1, unsigned * pInfo2, int nWords ); +extern void Sim_UtilInfoDetectDiffs( unsigned * pInfo1, unsigned * pInfo2, int nWords, Vec_Int_t * vDiffs ); +extern void Sim_UtilInfoDetectNews( unsigned * pInfo1, unsigned * pInfo2, int nWords, Vec_Int_t * vDiffs ); +extern void Sim_UtilInfoFlip( Sim_Man_t * p, Abc_Obj_t * pNode ); +extern bool Sim_UtilInfoCompare( Sim_Man_t * p, Abc_Obj_t * pNode ); +extern void Sim_UtilSimulate( Sim_Man_t * p, bool fFirst ); +extern void Sim_UtilSimulateNode( Sim_Man_t * p, Abc_Obj_t * pNode, bool fType, bool fType1, bool fType2 ); +extern void Sim_UtilSimulateNodeOne( Abc_Obj_t * pNode, Vec_Ptr_t * vSimInfo, int nSimWords, int nOffset ); +extern void Sim_UtilTransferNodeOne( Abc_Obj_t * pNode, Vec_Ptr_t * vSimInfo, int nSimWords, int nOffset, int fShift ); +extern int Sim_UtilCountSuppSizes( Sim_Man_t * p, int fStruct ); +extern int Sim_UtilCountOnes( unsigned * pSimInfo, int nSimWords ); +extern Vec_Int_t * Sim_UtilCountOnesArray( Vec_Ptr_t * vInfo, int nSimWords ); +extern void Sim_UtilSetRandom( unsigned * pPatRand, int nSimWords ); +extern void Sim_UtilSetCompl( unsigned * pPatRand, int nSimWords ); +extern void Sim_UtilSetConst( unsigned * pPatRand, int nSimWords, int fConst1 ); +extern int Sim_UtilInfoIsEqual( unsigned * pPats1, unsigned * pPats2, int nSimWords ); +extern int Sim_UtilInfoIsImp( unsigned * pPats1, unsigned * pPats2, int nSimWords ); +extern int Sim_UtilInfoIsClause( unsigned * pPats1, unsigned * pPats2, int nSimWords ); +extern int Sim_UtilCountAllPairs( Vec_Ptr_t * vSuppFun, int nSimWords, Vec_Int_t * vCounters ); +extern void Sim_UtilCountPairsAll( Sym_Man_t * p ); +extern int Sim_UtilMatrsAreDisjoint( Sym_Man_t * p ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/opt/sim/simMan.c b/abc_with_bb_support/src/opt/sim/simMan.c new file mode 100644 index 000000000..334cff29a --- /dev/null +++ b/abc_with_bb_support/src/opt/sim/simMan.c @@ -0,0 +1,288 @@ +/**CFile**************************************************************** + + FileName [simMan.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Simulation manager.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: simMan.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "sim.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the simulation manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Sym_Man_t * Sym_ManStart( Abc_Ntk_t * pNtk, int fVerbose ) +{ + Sym_Man_t * p; + int i, v; + // start the manager + p = ALLOC( Sym_Man_t, 1 ); + memset( p, 0, sizeof(Sym_Man_t) ); + p->pNtk = pNtk; + p->vNodes = Abc_NtkDfs( pNtk, 0 ); + p->nInputs = Abc_NtkCiNum(p->pNtk); + p->nOutputs = Abc_NtkCoNum(p->pNtk); + // internal simulation information + p->nSimWords = SIM_NUM_WORDS(p->nInputs); + p->vSim = Sim_UtilInfoAlloc( Abc_NtkObjNumMax(pNtk), p->nSimWords, 0 ); + // symmetry info for each output + p->vMatrSymms = Vec_PtrStart( p->nOutputs ); + p->vMatrNonSymms = Vec_PtrStart( p->nOutputs ); + p->vPairsTotal = Vec_IntStart( p->nOutputs ); + p->vPairsSym = Vec_IntStart( p->nOutputs ); + p->vPairsNonSym = Vec_IntStart( p->nOutputs ); + for ( i = 0; i < p->nOutputs; i++ ) + { + p->vMatrSymms->pArray[i] = Extra_BitMatrixStart( p->nInputs ); + p->vMatrNonSymms->pArray[i] = Extra_BitMatrixStart( p->nInputs ); + } + // temporary patterns + p->uPatRand = ALLOC( unsigned, p->nSimWords ); + p->uPatCol = ALLOC( unsigned, p->nSimWords ); + p->uPatRow = ALLOC( unsigned, p->nSimWords ); + p->vVarsU = Vec_IntStart( 100 ); + p->vVarsV = Vec_IntStart( 100 ); + // compute supports + p->vSuppFun = Sim_ComputeFunSupp( pNtk, fVerbose ); + p->vSupports = Vec_VecStart( p->nOutputs ); + for ( i = 0; i < p->nOutputs; i++ ) + for ( v = 0; v < p->nInputs; v++ ) + if ( Sim_SuppFunHasVar( p->vSuppFun, i, v ) ) + Vec_VecPush( p->vSupports, i, (void *)v ); + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the simulation manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sym_ManStop( Sym_Man_t * p ) +{ + int i; + Sym_ManPrintStats( p ); + if ( p->vSuppFun ) Sim_UtilInfoFree( p->vSuppFun ); + if ( p->vSim ) Sim_UtilInfoFree( p->vSim ); + if ( p->vNodes ) Vec_PtrFree( p->vNodes ); + if ( p->vSupports ) Vec_VecFree( p->vSupports ); + for ( i = 0; i < p->nOutputs; i++ ) + { + Extra_BitMatrixStop( p->vMatrSymms->pArray[i] ); + Extra_BitMatrixStop( p->vMatrNonSymms->pArray[i] ); + } + Vec_IntFree( p->vVarsU ); + Vec_IntFree( p->vVarsV ); + Vec_PtrFree( p->vMatrSymms ); + Vec_PtrFree( p->vMatrNonSymms ); + Vec_IntFree( p->vPairsTotal ); + Vec_IntFree( p->vPairsSym ); + Vec_IntFree( p->vPairsNonSym ); + FREE( p->uPatRand ); + FREE( p->uPatCol ); + FREE( p->uPatRow ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Prints the manager statisticis.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sym_ManPrintStats( Sym_Man_t * p ) +{ +// printf( "Inputs = %5d. Outputs = %5d. Sim words = %5d.\n", +// Abc_NtkCiNum(p->pNtk), Abc_NtkCoNum(p->pNtk), p->nSimWords ); + printf( "Total symm = %8d.\n", p->nPairsSymm ); + printf( "Structural symm = %8d.\n", p->nPairsSymmStr ); + printf( "Total non-sym = %8d.\n", p->nPairsNonSymm ); + printf( "Total var pairs = %8d.\n", p->nPairsTotal ); + printf( "Sat runs SAT = %8d.\n", p->nSatRunsSat ); + printf( "Sat runs UNSAT = %8d.\n", p->nSatRunsUnsat ); + PRT( "Structural ", p->timeStruct ); + PRT( "Simulation ", p->timeSim ); + PRT( "Matrix ", p->timeMatr ); + PRT( "Counting ", p->timeCount ); + PRT( "Fraiging ", p->timeFraig ); + PRT( "SAT ", p->timeSat ); + PRT( "TOTAL ", p->timeTotal ); +} + + +/**Function************************************************************* + + Synopsis [Starts the simulation manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Sim_Man_t * Sim_ManStart( Abc_Ntk_t * pNtk, int fLightweight ) +{ + Sim_Man_t * p; + // start the manager + p = ALLOC( Sim_Man_t, 1 ); + memset( p, 0, sizeof(Sim_Man_t) ); + p->pNtk = pNtk; + p->nInputs = Abc_NtkCiNum(p->pNtk); + p->nOutputs = Abc_NtkCoNum(p->pNtk); + // internal simulation information + p->nSimBits = 2048; + p->nSimWords = SIM_NUM_WORDS(p->nSimBits); + p->vSim0 = Sim_UtilInfoAlloc( Abc_NtkObjNumMax(pNtk), p->nSimWords, 0 ); + p->fLightweight = fLightweight; + if (!p->fLightweight) { + p->vSim1 = Sim_UtilInfoAlloc( Abc_NtkObjNumMax(pNtk), p->nSimWords, 0 ); + // support information + p->nSuppBits = Abc_NtkCiNum(pNtk); + p->nSuppWords = SIM_NUM_WORDS(p->nSuppBits); + p->vSuppStr = Sim_ComputeStrSupp( pNtk ); + p->vSuppFun = Sim_UtilInfoAlloc( Abc_NtkCoNum(p->pNtk), p->nSuppWords, 1 ); + // other data + p->pMmPat = Extra_MmFixedStart( sizeof(Sim_Pat_t) + p->nSuppWords * sizeof(unsigned) ); + p->vFifo = Vec_PtrAlloc( 100 ); + p->vDiffs = Vec_IntAlloc( 100 ); + // allocate support targets (array of unresolved outputs for each input) + p->vSuppTargs = Vec_VecStart( p->nInputs ); + } + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the simulation manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_ManStop( Sim_Man_t * p ) +{ + Sim_ManPrintStats( p ); + if ( p->vSim0 ) Sim_UtilInfoFree( p->vSim0 ); + if ( p->vSim1 ) Sim_UtilInfoFree( p->vSim1 ); + if ( p->vSuppStr ) Sim_UtilInfoFree( p->vSuppStr ); +// if ( p->vSuppFun ) Sim_UtilInfoFree( p->vSuppFun ); + if ( p->vSuppTargs ) Vec_VecFree( p->vSuppTargs ); + if ( p->pMmPat ) Extra_MmFixedStop( p->pMmPat ); + if ( p->vFifo ) Vec_PtrFree( p->vFifo ); + if ( p->vDiffs ) Vec_IntFree( p->vDiffs ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Prints the manager statisticis.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_ManPrintStats( Sim_Man_t * p ) +{ +// printf( "Inputs = %5d. Outputs = %5d. Sim words = %5d.\n", +// Abc_NtkCiNum(p->pNtk), Abc_NtkCoNum(p->pNtk), p->nSimWords ); + printf( "Total func supps = %8d.\n", Sim_UtilCountSuppSizes(p, 0) ); + printf( "Total struct supps = %8d.\n", Sim_UtilCountSuppSizes(p, 1) ); + printf( "Sat runs SAT = %8d.\n", p->nSatRunsSat ); + printf( "Sat runs UNSAT = %8d.\n", p->nSatRunsUnsat ); + PRT( "Simulation ", p->timeSim ); + PRT( "Traversal ", p->timeTrav ); + PRT( "Fraiging ", p->timeFraig ); + PRT( "SAT ", p->timeSat ); + PRT( "TOTAL ", p->timeTotal ); +} + + + +/**Function************************************************************* + + Synopsis [Returns one simulation pattern.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Sim_Pat_t * Sim_ManPatAlloc( Sim_Man_t * p ) +{ + Sim_Pat_t * pPat; + pPat = (Sim_Pat_t *)Extra_MmFixedEntryFetch( p->pMmPat ); + pPat->Output = -1; + pPat->pData = (unsigned *)((char *)pPat + sizeof(Sim_Pat_t)); + memset( pPat->pData, 0, p->nSuppWords * sizeof(unsigned) ); + return pPat; +} + +/**Function************************************************************* + + Synopsis [Returns one simulation pattern.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_ManPatFree( Sim_Man_t * p, Sim_Pat_t * pPat ) +{ + Extra_MmFixedEntryRecycle( p->pMmPat, (char *)pPat ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/sim/simSat.c b/abc_with_bb_support/src/opt/sim/simSat.c new file mode 100644 index 000000000..623de4b7c --- /dev/null +++ b/abc_with_bb_support/src/opt/sim/simSat.c @@ -0,0 +1,48 @@ +/**CFile**************************************************************** + + FileName [simSat.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Simulation to determine functional support.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: simSat.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "sim.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/sim/simSeq.c b/abc_with_bb_support/src/opt/sim/simSeq.c new file mode 100644 index 000000000..2c90169ca --- /dev/null +++ b/abc_with_bb_support/src/opt/sim/simSeq.c @@ -0,0 +1,171 @@ +/**CFile**************************************************************** + + FileName [simSeq.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Simulation for sequential circuits.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: simUtils.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "sim.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Sim_SimulateSeqFrame( Vec_Ptr_t * vInfo, Abc_Ntk_t * pNtk, int iFrames, int nWords, int fTransfer ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Simulates sequential circuit.] + + Description [Takes sequential circuit (pNtk). Simulates the given number + (nFrames) of the circuit with the given number of machine words (nWords) + of random simulation data, starting from the initial state. If the initial + state of some latches is a don't-care, uses random input for that latch.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Sim_SimulateSeqRandom( Abc_Ntk_t * pNtk, int nFrames, int nWords ) +{ + Vec_Ptr_t * vInfo; + Abc_Obj_t * pNode; + int i; + assert( Abc_NtkIsStrash(pNtk) ); + vInfo = Sim_UtilInfoAlloc( Abc_NtkObjNumMax(pNtk), nWords * nFrames, 0 ); + // set the constant data + pNode = Abc_AigConst1(pNtk); + Sim_UtilSetConst( Sim_SimInfoGet(vInfo,pNode), nWords * nFrames, 1 ); + // set the random PI data + Abc_NtkForEachPi( pNtk, pNode, i ) + Sim_UtilSetRandom( Sim_SimInfoGet(vInfo,pNode), nWords * nFrames ); + // set the initial state data + Abc_NtkForEachLatch( pNtk, pNode, i ) + if ( Abc_LatchIsInit0(pNode) ) + Sim_UtilSetConst( Sim_SimInfoGet(vInfo,pNode), nWords, 0 ); + else if ( Abc_LatchIsInit1(pNode) ) + Sim_UtilSetConst( Sim_SimInfoGet(vInfo,pNode), nWords, 1 ); + else + Sim_UtilSetRandom( Sim_SimInfoGet(vInfo,pNode), nWords ); + // simulate the nodes for the given number of timeframes + for ( i = 0; i < nFrames; i++ ) + Sim_SimulateSeqFrame( vInfo, pNtk, i, nWords, (int)(i < nFrames-1) ); + return vInfo; +} + +/**Function************************************************************* + + Synopsis [Simulates sequential circuit.] + + Description [Takes sequential circuit (pNtk). Simulates the given number + (nFrames) of the circuit with the given model. The model is assumed to + contain values of PIs for each frame. The latches are initialized to + the initial state. One word of data is simulated.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Sim_SimulateSeqModel( Abc_Ntk_t * pNtk, int nFrames, int * pModel ) +{ + Vec_Ptr_t * vInfo; + Abc_Obj_t * pNode; + unsigned * pUnsigned; + int i, k; + vInfo = Sim_UtilInfoAlloc( Abc_NtkObjNumMax(pNtk), nFrames, 0 ); + // set the constant data + pNode = Abc_AigConst1(pNtk); + Sim_UtilSetConst( Sim_SimInfoGet(vInfo,pNode), nFrames, 1 ); + // set the random PI data + Abc_NtkForEachPi( pNtk, pNode, i ) + { + pUnsigned = Sim_SimInfoGet(vInfo,pNode); + for ( k = 0; k < nFrames; k++ ) + pUnsigned[k] = pModel[k * Abc_NtkPiNum(pNtk) + i] ? ~((unsigned)0) : 0; + } + // set the initial state data + Abc_NtkForEachLatch( pNtk, pNode, i ) + { + pUnsigned = Sim_SimInfoGet(vInfo,pNode); + if ( Abc_LatchIsInit0(pNode) ) + pUnsigned[0] = 0; + else if ( Abc_LatchIsInit1(pNode) ) + pUnsigned[0] = ~((unsigned)0); + else + pUnsigned[0] = SIM_RANDOM_UNSIGNED; + } + // simulate the nodes for the given number of timeframes + for ( i = 0; i < nFrames; i++ ) + Sim_SimulateSeqFrame( vInfo, pNtk, i, 1, (int)(i < nFrames-1) ); +/* + // print the simulated values + for ( i = 0; i < nFrames; i++ ) + { + printf( "Frame %d : ", i+1 ); + Abc_NtkForEachPi( pNtk, pNode, k ) + printf( "%d", Sim_SimInfoGet(vInfo,pNode)[i] > 0 ); + printf( " " ); + Abc_NtkForEachLatch( pNtk, pNode, k ) + printf( "%d", Sim_SimInfoGet(vInfo,pNode)[i] > 0 ); + printf( " " ); + Abc_NtkForEachPo( pNtk, pNode, k ) + printf( "%d", Sim_SimInfoGet(vInfo,pNode)[i] > 0 ); + printf( "\n" ); + } + printf( "\n" ); +*/ + return vInfo; +} + +/**Function************************************************************* + + Synopsis [Simulates one frame of sequential circuit.] + + Description [Assumes that the latches and POs are already initialized. + In the end transfers the data to the latches of the next frame.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_SimulateSeqFrame( Vec_Ptr_t * vInfo, Abc_Ntk_t * pNtk, int iFrames, int nWords, int fTransfer ) +{ + Abc_Obj_t * pNode; + int i; + Abc_NtkForEachNode( pNtk, pNode, i ) + Sim_UtilSimulateNodeOne( pNode, vInfo, nWords, iFrames * nWords ); + Abc_NtkForEachPo( pNtk, pNode, i ) + Sim_UtilTransferNodeOne( pNode, vInfo, nWords, iFrames * nWords, 0 ); + if ( !fTransfer ) + return; + Abc_NtkForEachLatch( pNtk, pNode, i ) + Sim_UtilTransferNodeOne( pNode, vInfo, nWords, iFrames * nWords, 1 ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/sim/simSupp.c b/abc_with_bb_support/src/opt/sim/simSupp.c new file mode 100644 index 000000000..19aeb9e32 --- /dev/null +++ b/abc_with_bb_support/src/opt/sim/simSupp.c @@ -0,0 +1,597 @@ +/**CFile**************************************************************** + + FileName [simSupp.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Simulation to determine functional support.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: simSupp.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "fraig.h" +#include "sim.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Sim_ComputeSuppRound( Sim_Man_t * p, bool fUseTargets ); +static int Sim_ComputeSuppRoundNode( Sim_Man_t * p, int iNumCi, bool fUseTargets ); +static void Sim_ComputeSuppSetTargets( Sim_Man_t * p ); + +static void Sim_UtilAssignRandom( Sim_Man_t * p ); +static void Sim_UtilAssignFromFifo( Sim_Man_t * p ); +static void Sim_SolveTargetsUsingSat( Sim_Man_t * p, int nCounters ); +static int Sim_SolveSuppModelVerify( Abc_Ntk_t * pNtk, int * pModel, int Input, int Output ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes structural supports.] + + Description [Supports are returned as an array of bit strings, one + for each CO.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Sim_ComputeStrSupp( Abc_Ntk_t * pNtk ) +{ + Vec_Ptr_t * vSuppStr; + Abc_Obj_t * pNode; + unsigned * pSimmNode, * pSimmNode1, * pSimmNode2; + int nSuppWords, i, k; + // allocate room for structural supports + nSuppWords = SIM_NUM_WORDS( Abc_NtkCiNum(pNtk) ); + vSuppStr = Sim_UtilInfoAlloc( Abc_NtkObjNumMax(pNtk), nSuppWords, 1 ); + // assign the structural support to the PIs + Abc_NtkForEachCi( pNtk, pNode, i ) + Sim_SuppStrSetVar( vSuppStr, pNode, i ); + // derive the structural supports of the internal nodes + Abc_NtkForEachNode( pNtk, pNode, i ) + { +// if ( Abc_NodeIsConst(pNode) ) +// continue; + pSimmNode = vSuppStr->pArray[ pNode->Id ]; + pSimmNode1 = vSuppStr->pArray[ Abc_ObjFaninId0(pNode) ]; + pSimmNode2 = vSuppStr->pArray[ Abc_ObjFaninId1(pNode) ]; + for ( k = 0; k < nSuppWords; k++ ) + pSimmNode[k] = pSimmNode1[k] | pSimmNode2[k]; + } + // set the structural supports of the PO nodes + Abc_NtkForEachCo( pNtk, pNode, i ) + { + pSimmNode = vSuppStr->pArray[ pNode->Id ]; + pSimmNode1 = vSuppStr->pArray[ Abc_ObjFaninId0(pNode) ]; + for ( k = 0; k < nSuppWords; k++ ) + pSimmNode[k] = pSimmNode1[k]; + } + return vSuppStr; +} + +/**Function************************************************************* + + Synopsis [Compute functional supports.] + + Description [Supports are returned as an array of bit strings, one + for each CO.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Sim_ComputeFunSupp( Abc_Ntk_t * pNtk, int fVerbose ) +{ + Sim_Man_t * p; + Vec_Ptr_t * vResult; + int nSolved, i, clk = clock(); + + srand( 0xABC ); + + // start the simulation manager + p = Sim_ManStart( pNtk, 0 ); + + // compute functional support using one round of random simulation + Sim_UtilAssignRandom( p ); + Sim_ComputeSuppRound( p, 0 ); + + // set the support targets + Sim_ComputeSuppSetTargets( p ); +if ( fVerbose ) + printf( "Number of support targets after simulation = %5d.\n", Vec_VecSizeSize(p->vSuppTargs) ); + if ( Vec_VecSizeSize(p->vSuppTargs) == 0 ) + goto exit; + + for ( i = 0; i < 1; i++ ) + { + // compute patterns using one round of random simulation + Sim_UtilAssignRandom( p ); + nSolved = Sim_ComputeSuppRound( p, 1 ); + if ( Vec_VecSizeSize(p->vSuppTargs) == 0 ) + goto exit; + +if ( fVerbose ) + printf( "Targets = %5d. Solved = %5d. Fifo = %5d.\n", + Vec_VecSizeSize(p->vSuppTargs), nSolved, Vec_PtrSize(p->vFifo) ); + } + + // try to solve the support targets + while ( Vec_VecSizeSize(p->vSuppTargs) > 0 ) + { + // solve targets until the first disproved one (which gives counter-example) + Sim_SolveTargetsUsingSat( p, p->nSimWords/p->nSuppWords ); + // compute additional functional support + Sim_UtilAssignFromFifo( p ); + nSolved = Sim_ComputeSuppRound( p, 1 ); + +if ( fVerbose ) + printf( "Targets = %5d. Solved = %5d. Fifo = %5d. SAT runs = %3d.\n", + Vec_VecSizeSize(p->vSuppTargs), nSolved, Vec_PtrSize(p->vFifo), p->nSatRuns ); + } + +exit: +p->timeTotal = clock() - clk; + vResult = p->vSuppFun; + // p->vSuppFun = NULL; + Sim_ManStop( p ); + return vResult; +} + +/**Function************************************************************* + + Synopsis [Computes functional support using one round of simulation.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sim_ComputeSuppRound( Sim_Man_t * p, bool fUseTargets ) +{ + Vec_Int_t * vTargets; + int i, Counter = 0; + int clk; + // perform one round of random simulation +clk = clock(); + Sim_UtilSimulate( p, 0 ); +p->timeSim += clock() - clk; + // iterate through the CIs and detect COs that depend on them + for ( i = p->iInput; i < p->nInputs; i++ ) + { + vTargets = p->vSuppTargs->pArray[i]; + if ( fUseTargets && vTargets->nSize == 0 ) + continue; + Counter += Sim_ComputeSuppRoundNode( p, i, fUseTargets ); + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Computes functional support for one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sim_ComputeSuppRoundNode( Sim_Man_t * p, int iNumCi, bool fUseTargets ) +{ + int fVerbose = 0; + Sim_Pat_t * pPat; + Vec_Int_t * vTargets; + Vec_Vec_t * vNodesByLevel; + Abc_Obj_t * pNodeCi, * pNode; + int i, k, v, Output, LuckyPat, fType0, fType1; + int Counter = 0; + int fFirst = 1; + int clk; + // collect nodes by level in the TFO of the CI + // this proceduredoes not collect the CIs and COs + // but it increments TravId of the collected nodes and CIs/COs +clk = clock(); + pNodeCi = Abc_NtkCi( p->pNtk, iNumCi ); + vNodesByLevel = Abc_DfsLevelized( pNodeCi, 0 ); +p->timeTrav += clock() - clk; + // complement the simulation info of the selected CI + Sim_UtilInfoFlip( p, pNodeCi ); + // simulate the levelized structure of nodes + Vec_VecForEachEntry( vNodesByLevel, pNode, i, k ) + { + fType0 = Abc_NodeIsTravIdCurrent( Abc_ObjFanin0(pNode) ); + fType1 = Abc_NodeIsTravIdCurrent( Abc_ObjFanin1(pNode) ); +clk = clock(); + Sim_UtilSimulateNode( p, pNode, 1, fType0, fType1 ); +p->timeSim += clock() - clk; + } + // set the simulation info of the affected COs + if ( fUseTargets ) + { + vTargets = p->vSuppTargs->pArray[iNumCi]; + for ( i = vTargets->nSize - 1; i >= 0; i-- ) + { + // get the target output + Output = vTargets->pArray[i]; + // get the target node + pNode = Abc_ObjFanin0( Abc_NtkCo(p->pNtk, Output) ); + // the output should be in the cone + assert( Abc_NodeIsTravIdCurrent(pNode) ); + + // skip if the simulation info is equal + if ( Sim_UtilInfoCompare( p, pNode ) ) + continue; + + // otherwise, we solved a new target + Vec_IntRemove( vTargets, Output ); +if ( fVerbose ) + printf( "(%d,%d) ", iNumCi, Output ); + Counter++; + // make sure this variable is not yet detected + assert( !Sim_SuppFunHasVar(p->vSuppFun, Output, iNumCi) ); + // set this variable + Sim_SuppFunSetVar( p->vSuppFun, Output, iNumCi ); + + // detect the differences in the simulation info + Sim_UtilInfoDetectDiffs( p->vSim0->pArray[pNode->Id], p->vSim1->pArray[pNode->Id], p->nSimWords, p->vDiffs ); + // create new patterns + if ( !fFirst && p->vFifo->nSize > 1000 ) + continue; + + Vec_IntForEachEntry( p->vDiffs, LuckyPat, k ) + { + // set the new pattern + pPat = Sim_ManPatAlloc( p ); + pPat->Input = iNumCi; + pPat->Output = Output; + Abc_NtkForEachCi( p->pNtk, pNodeCi, v ) + if ( Sim_SimInfoHasVar( p->vSim0, pNodeCi, LuckyPat ) ) + Sim_SetBit( pPat->pData, v ); + Vec_PtrPush( p->vFifo, pPat ); + + fFirst = 0; + break; + } + } +if ( fVerbose && Counter ) +printf( "\n" ); + } + else + { + Abc_NtkForEachCo( p->pNtk, pNode, Output ) + { + if ( !Abc_NodeIsTravIdCurrent( pNode ) ) + continue; + if ( !Sim_UtilInfoCompare( p, Abc_ObjFanin0(pNode) ) ) + { + if ( !Sim_SuppFunHasVar(p->vSuppFun, Output, iNumCi) ) + { + Counter++; + Sim_SuppFunSetVar( p->vSuppFun, Output, iNumCi ); + } + } + } + } + Vec_VecFree( vNodesByLevel ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Sets the simulation targets.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_ComputeSuppSetTargets( Sim_Man_t * p ) +{ + Abc_Obj_t * pNode; + unsigned * pSuppStr, * pSuppFun; + int i, k, Num; + Abc_NtkForEachCo( p->pNtk, pNode, i ) + { + pSuppStr = p->vSuppStr->pArray[pNode->Id]; + pSuppFun = p->vSuppFun->pArray[i]; + // find vars in the structural support that are not in the functional support + Sim_UtilInfoDetectNews( pSuppFun, pSuppStr, p->nSuppWords, p->vDiffs ); + Vec_IntForEachEntry( p->vDiffs, Num, k ) + Vec_VecPush( p->vSuppTargs, Num, (void *)i ); + } +} + +/**Function************************************************************* + + Synopsis [Assigns random simulation info to the PIs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_UtilAssignRandom( Sim_Man_t * p ) +{ + Abc_Obj_t * pNode; + unsigned * pSimInfo; + int i, k; + // assign the random/systematic simulation info to the PIs + Abc_NtkForEachCi( p->pNtk, pNode, i ) + { + pSimInfo = p->vSim0->pArray[pNode->Id]; + for ( k = 0; k < p->nSimWords; k++ ) + pSimInfo[k] = SIM_RANDOM_UNSIGNED; + } +} + +/**Function************************************************************* + + Synopsis [Sets the new patterns from fifo.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_UtilAssignFromFifo( Sim_Man_t * p ) +{ + int fUseOneWord = 0; + Abc_Obj_t * pNode; + Sim_Pat_t * pPat; + unsigned * pSimInfo; + int nWordsNew, iWord, iWordLim, i, w; + int iBeg, iEnd; + int Counter = 0; + // go through the patterns and fill in the dist-1 minterms for each + for ( iWord = 0; p->vFifo->nSize > 0; iWord = iWordLim ) + { + ++Counter; + // get the pattern + pPat = Vec_PtrPop( p->vFifo ); + if ( fUseOneWord ) + { + // get the first word of the next series + iWordLim = iWord + 1; + // set the pattern for all PIs from iBit to iWord + p->nInputs + iBeg = p->iInput; + iEnd = ABC_MIN( iBeg + 32, p->nInputs ); +// for ( i = iBeg; i < iEnd; i++ ) + Abc_NtkForEachCi( p->pNtk, pNode, i ) + { + pNode = Abc_NtkCi(p->pNtk,i); + pSimInfo = p->vSim0->pArray[pNode->Id]; + if ( Sim_HasBit(pPat->pData, i) ) + pSimInfo[iWord] = SIM_MASK_FULL; + else + pSimInfo[iWord] = 0; + // flip one bit + if ( i >= iBeg && i < iEnd ) + Sim_XorBit( pSimInfo + iWord, i-iBeg ); + } + } + else + { + // get the number of words for the remaining inputs + nWordsNew = p->nSuppWords; +// nWordsNew = SIM_NUM_WORDS( p->nInputs - p->iInput ); + // get the first word of the next series + iWordLim = (iWord + nWordsNew < p->nSimWords)? iWord + nWordsNew : p->nSimWords; + // set the pattern for all CIs from iWord to iWord + nWordsNew + Abc_NtkForEachCi( p->pNtk, pNode, i ) + { + pSimInfo = p->vSim0->pArray[pNode->Id]; + if ( Sim_HasBit(pPat->pData, i) ) + { + for ( w = iWord; w < iWordLim; w++ ) + pSimInfo[w] = SIM_MASK_FULL; + } + else + { + for ( w = iWord; w < iWordLim; w++ ) + pSimInfo[w] = 0; + } + Sim_XorBit( pSimInfo + iWord, i ); + // flip one bit +// if ( i >= p->iInput ) +// Sim_XorBit( pSimInfo + iWord, i-p->iInput ); + } + } + Sim_ManPatFree( p, pPat ); + // stop if we ran out of room for patterns + if ( iWordLim == p->nSimWords ) + break; +// if ( Counter == 1 ) +// break; + } +} + +/**Function************************************************************* + + Synopsis [Get the given number of counter-examples using SAT.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_SolveTargetsUsingSat( Sim_Man_t * p, int Limit ) +{ + Fraig_Params_t Params; + Fraig_Man_t * pMan; + Abc_Obj_t * pNodeCi; + Abc_Ntk_t * pMiter; + Sim_Pat_t * pPat; + void * pEntry; + int * pModel; + int RetValue, Output, Input, k, v; + int Counter = 0; + int clk; + + p->nSatRuns = 0; + // put targets into one array + Vec_VecForEachEntryReverse( p->vSuppTargs, pEntry, Input, k ) + { + p->nSatRuns++; + Output = (int)pEntry; + + // set up the miter for the two cofactors of this output w.r.t. this input + pMiter = Abc_NtkMiterForCofactors( p->pNtk, Output, Input, -1 ); + + // transform the miter into a fraig + Fraig_ParamsSetDefault( &Params ); + Params.nSeconds = ABC_INFINITY; + Params.fInternal = 1; +clk = clock(); + pMan = Abc_NtkToFraig( pMiter, &Params, 0, 0 ); +p->timeFraig += clock() - clk; +clk = clock(); + Fraig_ManProveMiter( pMan ); +p->timeSat += clock() - clk; + + // analyze the result + RetValue = Fraig_ManCheckMiter( pMan ); + assert( RetValue >= 0 ); + if ( RetValue == 1 ) // unsat + { + p->nSatRunsUnsat++; + pModel = NULL; + Vec_PtrRemove( p->vSuppTargs->pArray[Input], pEntry ); + } + else // sat + { + p->nSatRunsSat++; + pModel = Fraig_ManReadModel( pMan ); + assert( pModel != NULL ); + assert( Sim_SolveSuppModelVerify( p->pNtk, pModel, Input, Output ) ); + +//printf( "Solved by SAT (%d,%d).\n", Input, Output ); + // set the new pattern + pPat = Sim_ManPatAlloc( p ); + pPat->Input = Input; + pPat->Output = Output; + Abc_NtkForEachCi( p->pNtk, pNodeCi, v ) + if ( pModel[v] ) + Sim_SetBit( pPat->pData, v ); + Vec_PtrPush( p->vFifo, pPat ); +/* + // set the new pattern + pPat = Sim_ManPatAlloc( p ); + pPat->Input = Input; + pPat->Output = Output; + Abc_NtkForEachCi( p->pNtk, pNodeCi, v ) + if ( pModel[v] ) + Sim_SetBit( pPat->pData, v ); + Sim_XorBit( pPat->pData, Input ); // add this bit in the opposite polarity + Vec_PtrPush( p->vFifo, pPat ); +*/ + Counter++; + } + // delete the fraig manager + Fraig_ManFree( pMan ); + // delete the miter + Abc_NtkDelete( pMiter ); + + // makr the input, which we are processing + p->iInput = Input; + + // stop when we found enough patterns +// if ( Counter == Limit ) + if ( Counter == 1 ) + return; + } +} + + +/**Function************************************************************* + + Synopsis [Saves the counter example.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sim_NtkSimTwoPats_rec( Abc_Obj_t * pNode ) +{ + int Value0, Value1; + if ( Abc_NodeIsTravIdCurrent( pNode ) ) + return (int)pNode->pCopy; + Abc_NodeSetTravIdCurrent( pNode ); + Value0 = Sim_NtkSimTwoPats_rec( Abc_ObjFanin0(pNode) ); + Value1 = Sim_NtkSimTwoPats_rec( Abc_ObjFanin1(pNode) ); + if ( Abc_ObjFaninC0(pNode) ) + Value0 = ~Value0; + if ( Abc_ObjFaninC1(pNode) ) + Value1 = ~Value1; + pNode->pCopy = (Abc_Obj_t *)(Value0 & Value1); + return Value0 & Value1; +} + +/**Function************************************************************* + + Synopsis [Verifies that pModel proves the presence of Input in the support of Output.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sim_SolveSuppModelVerify( Abc_Ntk_t * pNtk, int * pModel, int Input, int Output ) +{ + Abc_Obj_t * pNode; + int RetValue, i; + // set the PI values + Abc_NtkIncrementTravId( pNtk ); + Abc_NtkForEachCi( pNtk, pNode, i ) + { + Abc_NodeSetTravIdCurrent( pNode ); + if ( pNode == Abc_NtkCi(pNtk,Input) ) + pNode->pCopy = (Abc_Obj_t *)1; + else if ( pModel[i] == 1 ) + pNode->pCopy = (Abc_Obj_t *)3; + else + pNode->pCopy = NULL; + } + // perform the traversal + RetValue = 3 & Sim_NtkSimTwoPats_rec( Abc_ObjFanin0( Abc_NtkCo(pNtk,Output) ) ); +// assert( RetValue == 1 || RetValue == 2 ); + return RetValue == 1 || RetValue == 2; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/sim/simSwitch.c b/abc_with_bb_support/src/opt/sim/simSwitch.c new file mode 100644 index 000000000..ef6aa4de7 --- /dev/null +++ b/abc_with_bb_support/src/opt/sim/simSwitch.c @@ -0,0 +1,107 @@ +/**CFile**************************************************************** + + FileName [simSwitch.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Computes switching activity of nodes in the ABC network.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: simSwitch.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "sim.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Sim_NodeSimulate( Abc_Obj_t * pNode, Vec_Ptr_t * vSimInfo, int nSimWords ); +static float Sim_ComputeSwitching( unsigned * pSimInfo, int nSimWords ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes switching activity using simulation.] + + Description [Computes switching activity, which is understood as the + probability of switching under random simulation. Assigns the + random simulation information at the CI and propagates it through + the internal nodes of the AIG.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Sim_NtkComputeSwitching( Abc_Ntk_t * pNtk, int nPatterns ) +{ + Vec_Int_t * vSwitching; + float * pSwitching; + Vec_Ptr_t * vNodes; + Vec_Ptr_t * vSimInfo; + Abc_Obj_t * pNode; + unsigned * pSimInfo; + int nSimWords, i; + + // allocate space for simulation info of all nodes + nSimWords = SIM_NUM_WORDS(nPatterns); + vSimInfo = Sim_UtilInfoAlloc( Abc_NtkObjNumMax(pNtk), nSimWords, 0 ); + // assign the random simulation to the CIs + vSwitching = Vec_IntStart( Abc_NtkObjNumMax(pNtk) ); + pSwitching = (float *)vSwitching->pArray; + Abc_NtkForEachCi( pNtk, pNode, i ) + { + pSimInfo = Vec_PtrEntry(vSimInfo, pNode->Id); + Sim_UtilSetRandom( pSimInfo, nSimWords ); + pSwitching[pNode->Id] = Sim_ComputeSwitching( pSimInfo, nSimWords ); + } + // simulate the internal nodes + vNodes = Abc_AigDfs( pNtk, 1, 0 ); + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + pSimInfo = Vec_PtrEntry(vSimInfo, pNode->Id); + Sim_UtilSimulateNodeOne( pNode, vSimInfo, nSimWords, 0 ); + pSwitching[pNode->Id] = Sim_ComputeSwitching( pSimInfo, nSimWords ); + } + Vec_PtrFree( vNodes ); + Sim_UtilInfoFree( vSimInfo ); + return vSwitching; +} + +/**Function************************************************************* + + Synopsis [Computes switching activity of one node.] + + Description [Uses the formula: Switching = 2 * nOnes * nZeros / (nTotal ^ 2) ] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Sim_ComputeSwitching( unsigned * pSimInfo, int nSimWords ) +{ + int nOnes, nTotal; + nTotal = 32 * nSimWords; + nOnes = Sim_UtilCountOnes( pSimInfo, nSimWords ); + return (float)2.0 * nOnes * (nTotal - nOnes) / nTotal / nTotal; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/sim/simSym.c b/abc_with_bb_support/src/opt/sim/simSym.c new file mode 100644 index 000000000..88ff9d0a1 --- /dev/null +++ b/abc_with_bb_support/src/opt/sim/simSym.c @@ -0,0 +1,142 @@ +/**CFile**************************************************************** + + FileName [simSym.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Simulation to determine two-variable symmetries.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: simSym.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "sim.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes two variable symmetries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sim_ComputeTwoVarSymms( Abc_Ntk_t * pNtk, int fVerbose ) +{ + Sym_Man_t * p; + Vec_Ptr_t * vResult; + int Result; + int i, clk, clkTotal = clock(); + + srand( 0xABC ); + + // start the simulation manager + p = Sym_ManStart( pNtk, fVerbose ); + p->nPairsTotal = p->nPairsRem = Sim_UtilCountAllPairs( p->vSuppFun, p->nSimWords, p->vPairsTotal ); + if ( fVerbose ) + printf( "Total = %8d. Sym = %8d. NonSym = %8d. Remaining = %8d.\n", + p->nPairsTotal, p->nPairsSymm, p->nPairsNonSymm, p->nPairsRem ); + + // detect symmetries using circuit structure +clk = clock(); + Sim_SymmsStructCompute( pNtk, p->vMatrSymms, p->vSuppFun ); +p->timeStruct = clock() - clk; + + Sim_UtilCountPairsAll( p ); + p->nPairsSymmStr = p->nPairsSymm; + if ( fVerbose ) + printf( "Total = %8d. Sym = %8d. NonSym = %8d. Remaining = %8d.\n", + p->nPairsTotal, p->nPairsSymm, p->nPairsNonSymm, p->nPairsRem ); + + // detect symmetries using simulation + for ( i = 1; i <= 1000; i++ ) + { + // simulate this pattern + Sim_UtilSetRandom( p->uPatRand, p->nSimWords ); + Sim_SymmsSimulate( p, p->uPatRand, p->vMatrNonSymms ); + if ( i % 50 != 0 ) + continue; + // check disjointness + assert( Sim_UtilMatrsAreDisjoint( p ) ); + // count the number of pairs + Sim_UtilCountPairsAll( p ); + if ( i % 500 != 0 ) + continue; + if ( fVerbose ) + printf( "Total = %8d. Sym = %8d. NonSym = %8d. Remaining = %8d.\n", + p->nPairsTotal, p->nPairsSymm, p->nPairsNonSymm, p->nPairsRem ); + } + + // detect symmetries using SAT + for ( i = 1; Sim_SymmsGetPatternUsingSat( p, p->uPatRand ); i++ ) + { + // simulate this pattern in four polarities + Sim_SymmsSimulate( p, p->uPatRand, p->vMatrNonSymms ); + Sim_XorBit( p->uPatRand, p->iVar1 ); + Sim_SymmsSimulate( p, p->uPatRand, p->vMatrNonSymms ); + Sim_XorBit( p->uPatRand, p->iVar2 ); + Sim_SymmsSimulate( p, p->uPatRand, p->vMatrNonSymms ); + Sim_XorBit( p->uPatRand, p->iVar1 ); + Sim_SymmsSimulate( p, p->uPatRand, p->vMatrNonSymms ); + Sim_XorBit( p->uPatRand, p->iVar2 ); +/* + // try the previuos pair + Sim_XorBit( p->uPatRand, p->iVar1Old ); + Sim_SymmsSimulate( p, p->uPatRand, p->vMatrNonSymms ); + Sim_XorBit( p->uPatRand, p->iVar2Old ); + Sim_SymmsSimulate( p, p->uPatRand, p->vMatrNonSymms ); + Sim_XorBit( p->uPatRand, p->iVar1Old ); + Sim_SymmsSimulate( p, p->uPatRand, p->vMatrNonSymms ); +*/ + if ( i % 10 != 0 ) + continue; + // check disjointness + assert( Sim_UtilMatrsAreDisjoint( p ) ); + // count the number of pairs + Sim_UtilCountPairsAll( p ); + if ( i % 50 != 0 ) + continue; + if ( fVerbose ) + printf( "Total = %8d. Sym = %8d. NonSym = %8d. Remaining = %8d.\n", + p->nPairsTotal, p->nPairsSymm, p->nPairsNonSymm, p->nPairsRem ); + } + + // count the number of pairs + Sim_UtilCountPairsAll( p ); + if ( fVerbose ) + printf( "Total = %8d. Sym = %8d. NonSym = %8d. Remaining = %8d.\n", + p->nPairsTotal, p->nPairsSymm, p->nPairsNonSymm, p->nPairsRem ); +// Sim_UtilCountPairsAllPrint( p ); + + Result = p->nPairsSymm; + vResult = p->vMatrSymms; +p->timeTotal = clock() - clkTotal; + // p->vMatrSymms = NULL; + Sym_ManStop( p ); + return Result; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/sim/simSymSat.c b/abc_with_bb_support/src/opt/sim/simSymSat.c new file mode 100644 index 000000000..c2f34f298 --- /dev/null +++ b/abc_with_bb_support/src/opt/sim/simSymSat.c @@ -0,0 +1,199 @@ +/**CFile**************************************************************** + + FileName [simSymSat.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Satisfiability to determine two variable symmetries.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: simSymSat.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "sim.h" +#include "fraig.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Sim_SymmsSatProveOne( Sym_Man_t * p, int Out, int Var1, int Var2, unsigned * pPattern ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Tries to prove the remaining pairs using SAT.] + + Description [Continues to prove as long as it encounters symmetric pairs. + Returns 1 if a non-symmetric pair is found (which gives a counter-example). + Returns 0 if it finishes considering all pairs for all outputs.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sim_SymmsGetPatternUsingSat( Sym_Man_t * p, unsigned * pPattern ) +{ + Vec_Int_t * vSupport; + Extra_BitMat_t * pMatSym, * pMatNonSym; + int Index1, Index2, Index3, IndexU, IndexV; + int v, u, i, k, b, out; + + // iterate through outputs + for ( out = p->iOutput; out < p->nOutputs; out++ ) + { + pMatSym = Vec_PtrEntry( p->vMatrSymms, out ); + pMatNonSym = Vec_PtrEntry( p->vMatrNonSymms, out ); + + // go through the remaining variable pairs + vSupport = Vec_VecEntry( p->vSupports, out ); + Vec_IntForEachEntry( vSupport, v, Index1 ) + Vec_IntForEachEntryStart( vSupport, u, Index2, Index1+1 ) + { + if ( Extra_BitMatrixLookup1( pMatSym, v, u ) || Extra_BitMatrixLookup1( pMatNonSym, v, u ) ) + continue; + p->nSatRuns++; + + // collect the support variables that are symmetric with u and v + Vec_IntClear( p->vVarsU ); + Vec_IntClear( p->vVarsV ); + Vec_IntForEachEntry( vSupport, b, Index3 ) + { + if ( Extra_BitMatrixLookup1( pMatSym, u, b ) ) + Vec_IntPush( p->vVarsU, b ); + if ( Extra_BitMatrixLookup1( pMatSym, v, b ) ) + Vec_IntPush( p->vVarsV, b ); + } + + if ( Sim_SymmsSatProveOne( p, out, v, u, pPattern ) ) + { // update the symmetric variable info + p->nSatRunsUnsat++; + Vec_IntForEachEntry( p->vVarsU, i, IndexU ) + Vec_IntForEachEntry( p->vVarsV, k, IndexV ) + { + Extra_BitMatrixInsert1( pMatSym, i, k ); // Theorem 1 + Extra_BitMatrixInsert2( pMatSym, i, k ); // Theorem 1 + Extra_BitMatrixOrTwo( pMatNonSym, i, k ); // Theorem 2 + } + } + else + { // update the assymmetric variable info + p->nSatRunsSat++; + Vec_IntForEachEntry( p->vVarsU, i, IndexU ) + Vec_IntForEachEntry( p->vVarsV, k, IndexV ) + { + Extra_BitMatrixInsert1( pMatNonSym, i, k ); // Theorem 3 + Extra_BitMatrixInsert2( pMatNonSym, i, k ); // Theorem 3 + } + + // remember the out + p->iOutput = out; + p->iVar1Old = p->iVar1; + p->iVar2Old = p->iVar2; + p->iVar1 = v; + p->iVar2 = u; + return 1; + + } + } + // make sure that the symmetry matrix contains only cliques + assert( Extra_BitMatrixIsClique( pMatSym ) ); + } + + // mark that we finished all outputs + p->iOutput = p->nOutputs; + return 0; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the variables are symmetric; 0 otherwise.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sim_SymmsSatProveOne( Sym_Man_t * p, int Out, int Var1, int Var2, unsigned * pPattern ) +{ + Fraig_Params_t Params; + Fraig_Man_t * pMan; + Abc_Ntk_t * pMiter; + int RetValue, i, clk; + int * pModel; + + // get the miter for this problem + pMiter = Abc_NtkMiterForCofactors( p->pNtk, Out, Var1, Var2 ); + // transform the miter into a fraig + Fraig_ParamsSetDefault( &Params ); + Params.fInternal = 1; + Params.nPatsRand = 512; + Params.nPatsDyna = 512; + Params.nSeconds = ABC_INFINITY; + +clk = clock(); + pMan = Abc_NtkToFraig( pMiter, &Params, 0, 0 ); +p->timeFraig += clock() - clk; +clk = clock(); + Fraig_ManProveMiter( pMan ); +p->timeSat += clock() - clk; + + // analyze the result + RetValue = Fraig_ManCheckMiter( pMan ); +// assert( RetValue >= 0 ); + // save the pattern + if ( RetValue == 0 ) + { + // get the pattern + pModel = Fraig_ManReadModel( pMan ); + assert( pModel != NULL ); +//printf( "Disproved by SAT: out = %d pair = (%d, %d)\n", Out, Var1, Var2 ); + // transfer the model into the pattern + for ( i = 0; i < p->nSimWords; i++ ) + pPattern[i] = 0; + for ( i = 0; i < p->nInputs; i++ ) + if ( pModel[i] ) + Sim_SetBit( pPattern, i ); + // make sure these variables have the same value (1) + Sim_SetBit( pPattern, Var1 ); + Sim_SetBit( pPattern, Var2 ); + } + else if ( RetValue == -1 ) + { + // this should never happen; if it happens, such is life + // we are conservative and assume that there is no symmetry +//printf( "STRANGE THING: out = %d %s pair = (%d %s, %d %s)\n", +// Out, Abc_ObjName(Abc_NtkCo(p->pNtk,Out)), +// Var1, Abc_ObjName(Abc_NtkCi(p->pNtk,Var1)), +// Var2, Abc_ObjName(Abc_NtkCi(p->pNtk,Var2)) ); + memset( pPattern, 0, sizeof(unsigned) * p->nSimWords ); + RetValue = 0; + } + // delete the fraig manager + Fraig_ManFree( pMan ); + // delete the miter + Abc_NtkDelete( pMiter ); + return RetValue; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/sim/simSymSim.c b/abc_with_bb_support/src/opt/sim/simSymSim.c new file mode 100644 index 000000000..d54e18495 --- /dev/null +++ b/abc_with_bb_support/src/opt/sim/simSymSim.c @@ -0,0 +1,173 @@ +/**CFile**************************************************************** + + FileName [simSymSim.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Simulation to determine two-variable symmetries.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: simSymSim.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "sim.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Sim_SymmsCreateSquare( Sym_Man_t * p, unsigned * pPat ); +static void Sim_SymmsDeriveInfo( Sym_Man_t * p, unsigned * pPat, Abc_Obj_t * pNode, Vec_Ptr_t * vMatrsNonSym, int Output ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Detects non-symmetric pairs using one pattern.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_SymmsSimulate( Sym_Man_t * p, unsigned * pPat, Vec_Ptr_t * vMatrsNonSym ) +{ + Abc_Obj_t * pNode; + int i, nPairsTotal, nPairsSym, nPairsNonSym; + int clk; + + // create the simulation matrix + Sim_SymmsCreateSquare( p, pPat ); + // simulate each node in the DFS order +clk = clock(); + Vec_PtrForEachEntry( p->vNodes, pNode, i ) + { +// if ( Abc_NodeIsConst(pNode) ) +// continue; + Sim_UtilSimulateNodeOne( pNode, p->vSim, p->nSimWords, 0 ); + } +p->timeSim += clock() - clk; + // collect info into the CO matrices +clk = clock(); + Abc_NtkForEachCo( p->pNtk, pNode, i ) + { + pNode = Abc_ObjFanin0(pNode); +// if ( Abc_ObjIsCi(pNode) || Abc_AigNodeIsConst(pNode) ) +// continue; + nPairsTotal = Vec_IntEntry(p->vPairsTotal, i); + nPairsSym = Vec_IntEntry(p->vPairsSym, i); + nPairsNonSym = Vec_IntEntry(p->vPairsNonSym,i); + assert( nPairsTotal >= nPairsSym + nPairsNonSym ); + if ( nPairsTotal == nPairsSym + nPairsNonSym ) + continue; + Sim_SymmsDeriveInfo( p, pPat, pNode, vMatrsNonSym, i ); + } +p->timeMatr += clock() - clk; +} + +/**Function************************************************************* + + Synopsis [Creates the square matrix of simulation info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_SymmsCreateSquare( Sym_Man_t * p, unsigned * pPat ) +{ + unsigned * pSimInfo; + Abc_Obj_t * pNode; + int i, w; + // for each PI var copy the pattern + Abc_NtkForEachCi( p->pNtk, pNode, i ) + { + pSimInfo = Vec_PtrEntry( p->vSim, pNode->Id ); + if ( Sim_HasBit(pPat, i) ) + { + for ( w = 0; w < p->nSimWords; w++ ) + pSimInfo[w] = SIM_MASK_FULL; + } + else + { + for ( w = 0; w < p->nSimWords; w++ ) + pSimInfo[w] = 0; + } + // flip one bit + Sim_XorBit( pSimInfo, i ); + } +} + +/**Function************************************************************* + + Synopsis [Transfers the info to the POs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_SymmsDeriveInfo( Sym_Man_t * p, unsigned * pPat, Abc_Obj_t * pNode, Vec_Ptr_t * vMatrsNonSym, int Output ) +{ + Extra_BitMat_t * pMat; + Vec_Int_t * vSupport; + unsigned * pSupport; + unsigned * pSimInfo; + int i, w, Index; + // get the matrix, the support, and the simulation info + pMat = Vec_PtrEntry( vMatrsNonSym, Output ); + vSupport = Vec_VecEntry( p->vSupports, Output ); + pSupport = Vec_PtrEntry( p->vSuppFun, Output ); + pSimInfo = Vec_PtrEntry( p->vSim, pNode->Id ); + // generate vectors A1 and A2 + for ( w = 0; w < p->nSimWords; w++ ) + { + p->uPatCol[w] = pSupport[w] & pPat[w] & pSimInfo[w]; + p->uPatRow[w] = pSupport[w] & pPat[w] & ~pSimInfo[w]; + } + // add two dimensions + Vec_IntForEachEntry( vSupport, i, Index ) + if ( Sim_HasBit( p->uPatCol, i ) ) + Extra_BitMatrixOr( pMat, i, p->uPatRow ); + // add two dimensions + Vec_IntForEachEntry( vSupport, i, Index ) + if ( Sim_HasBit( p->uPatRow, i ) ) + Extra_BitMatrixOr( pMat, i, p->uPatCol ); + // generate vectors B1 and B2 + for ( w = 0; w < p->nSimWords; w++ ) + { + p->uPatCol[w] = pSupport[w] & ~pPat[w] & pSimInfo[w]; + p->uPatRow[w] = pSupport[w] & ~pPat[w] & ~pSimInfo[w]; + } + // add two dimensions + Vec_IntForEachEntry( vSupport, i, Index ) + if ( Sim_HasBit( p->uPatCol, i ) ) + Extra_BitMatrixOr( pMat, i, p->uPatRow ); + // add two dimensions + Vec_IntForEachEntry( vSupport, i, Index ) + if ( Sim_HasBit( p->uPatRow, i ) ) + Extra_BitMatrixOr( pMat, i, p->uPatCol ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/sim/simSymStr.c b/abc_with_bb_support/src/opt/sim/simSymStr.c new file mode 100644 index 000000000..7b41e2fe6 --- /dev/null +++ b/abc_with_bb_support/src/opt/sim/simSymStr.c @@ -0,0 +1,488 @@ +/**CFile**************************************************************** + + FileName [simSymStr.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Structural detection of symmetries.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: simSymStr.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "sim.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define SIM_READ_SYMMS(pNode) ((Vec_Int_t *)pNode->pCopy) +#define SIM_SET_SYMMS(pNode,vVect) (pNode->pCopy = (Abc_Obj_t *)(vVect)) + +static void Sim_SymmsStructComputeOne( Abc_Ntk_t * pNtk, Abc_Obj_t * pNode, int * pMap ); +static void Sim_SymmsBalanceCollect_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes ); +static void Sim_SymmsPartitionNodes( Vec_Ptr_t * vNodes, Vec_Ptr_t * vNodesPis0, Vec_Ptr_t * vNodesPis1, Vec_Ptr_t * vNodesOther ); +static void Sim_SymmsAppendFromGroup( Abc_Ntk_t * pNtk, Vec_Ptr_t * vNodesPi, Vec_Ptr_t * vNodesOther, Vec_Int_t * vSymms, int * pMap ); +static void Sim_SymmsAppendFromNode( Abc_Ntk_t * pNtk, Vec_Int_t * vSymms0, Vec_Ptr_t * vNodesOther, Vec_Ptr_t * vNodesPi0, Vec_Ptr_t * vNodesPi1, Vec_Int_t * vSymms, int * pMap ); +static int Sim_SymmsIsCompatibleWithNodes( Abc_Ntk_t * pNtk, unsigned uSymm, Vec_Ptr_t * vNodesOther, int * pMap ); +static int Sim_SymmsIsCompatibleWithGroup( unsigned uSymm, Vec_Ptr_t * vNodesPi, int * pMap ); +static void Sim_SymmsPrint( Vec_Int_t * vSymms ); +static void Sim_SymmsTrans( Vec_Int_t * vSymms ); +static void Sim_SymmsTransferToMatrix( Extra_BitMat_t * pMatSymm, Vec_Int_t * vSymms, unsigned * pSupport ); +static int * Sim_SymmsCreateMap( Abc_Ntk_t * pNtk ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes symmetries for a single output function.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_SymmsStructCompute( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMatrs, Vec_Ptr_t * vSuppFun ) +{ + Vec_Ptr_t * vNodes; + Abc_Obj_t * pTemp; + int * pMap, i; + + assert( Abc_NtkCiNum(pNtk) + 10 < (1<<16) ); + + // get the structural support + pNtk->vSupps = Sim_ComputeStrSupp( pNtk ); + // set elementary info for the CIs + Abc_NtkForEachCi( pNtk, pTemp, i ) + SIM_SET_SYMMS( pTemp, Vec_IntAlloc(0) ); + // create the map of CI ids into their numbers + pMap = Sim_SymmsCreateMap( pNtk ); + // collect the nodes in the TFI cone of this output + vNodes = Abc_NtkDfs( pNtk, 0 ); + Vec_PtrForEachEntry( vNodes, pTemp, i ) + { +// if ( Abc_NodeIsConst(pTemp) ) +// continue; + Sim_SymmsStructComputeOne( pNtk, pTemp, pMap ); + } + // collect the results for the COs; + Abc_NtkForEachCo( pNtk, pTemp, i ) + { +//printf( "Output %d:\n", i ); + pTemp = Abc_ObjFanin0(pTemp); + if ( Abc_ObjIsCi(pTemp) || Abc_AigNodeIsConst(pTemp) ) + continue; + Sim_SymmsTransferToMatrix( Vec_PtrEntry(vMatrs, i), SIM_READ_SYMMS(pTemp), Vec_PtrEntry(vSuppFun, i) ); + } + // clean the intermediate results + Sim_UtilInfoFree( pNtk->vSupps ); + pNtk->vSupps = NULL; + Abc_NtkForEachCi( pNtk, pTemp, i ) + Vec_IntFree( SIM_READ_SYMMS(pTemp) ); + Vec_PtrForEachEntry( vNodes, pTemp, i ) +// if ( !Abc_NodeIsConst(pTemp) ) + Vec_IntFree( SIM_READ_SYMMS(pTemp) ); + Vec_PtrFree( vNodes ); + free( pMap ); +} + +/**Function************************************************************* + + Synopsis [Recursively computes symmetries. ] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_SymmsStructComputeOne( Abc_Ntk_t * pNtk, Abc_Obj_t * pNode, int * pMap ) +{ + Vec_Ptr_t * vNodes, * vNodesPi0, * vNodesPi1, * vNodesOther; + Vec_Int_t * vSymms; + Abc_Obj_t * pTemp; + int i; + + // allocate the temporary arrays + vNodes = Vec_PtrAlloc( 10 ); + vNodesPi0 = Vec_PtrAlloc( 10 ); + vNodesPi1 = Vec_PtrAlloc( 10 ); + vNodesOther = Vec_PtrAlloc( 10 ); + + // collect the fanins of the implication supergate + Sim_SymmsBalanceCollect_rec( pNode, vNodes ); + + // sort the nodes in the implication supergate + Sim_SymmsPartitionNodes( vNodes, vNodesPi0, vNodesPi1, vNodesOther ); + + // start the resulting set + vSymms = Vec_IntAlloc( 10 ); + // generate symmetries from the groups + Sim_SymmsAppendFromGroup( pNtk, vNodesPi0, vNodesOther, vSymms, pMap ); + Sim_SymmsAppendFromGroup( pNtk, vNodesPi1, vNodesOther, vSymms, pMap ); + // add symmetries from other inputs + for ( i = 0; i < vNodesOther->nSize; i++ ) + { + pTemp = Abc_ObjRegular(vNodesOther->pArray[i]); + Sim_SymmsAppendFromNode( pNtk, SIM_READ_SYMMS(pTemp), vNodesOther, vNodesPi0, vNodesPi1, vSymms, pMap ); + } + Vec_PtrFree( vNodes ); + Vec_PtrFree( vNodesPi0 ); + Vec_PtrFree( vNodesPi1 ); + Vec_PtrFree( vNodesOther ); + + // set the symmetry at the node + SIM_SET_SYMMS( pNode, vSymms ); +} + + +/**Function************************************************************* + + Synopsis [Returns the array of nodes to be combined into one multi-input AND-gate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_SymmsBalanceCollect_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes ) +{ + // if the new node is complemented, another gate begins + if ( Abc_ObjIsComplement(pNode) ) + { + Vec_PtrPushUnique( vNodes, pNode ); + return; + } + // if pNew is the PI node, return + if ( Abc_ObjIsCi(pNode) ) + { + Vec_PtrPushUnique( vNodes, pNode ); + return; + } + // go through the branches + Sim_SymmsBalanceCollect_rec( Abc_ObjChild0(pNode), vNodes ); + Sim_SymmsBalanceCollect_rec( Abc_ObjChild1(pNode), vNodes ); +} + +/**Function************************************************************* + + Synopsis [Divides PI variables into groups.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_SymmsPartitionNodes( Vec_Ptr_t * vNodes, Vec_Ptr_t * vNodesPis0, + Vec_Ptr_t * vNodesPis1, Vec_Ptr_t * vNodesOther ) +{ + Abc_Obj_t * pNode; + int i; + Vec_PtrForEachEntry( vNodes, pNode, i ) + { + if ( !Abc_ObjIsCi(Abc_ObjRegular(pNode)) ) + Vec_PtrPush( vNodesOther, pNode ); + else if ( Abc_ObjIsComplement(pNode) ) + Vec_PtrPush( vNodesPis0, pNode ); + else + Vec_PtrPush( vNodesPis1, pNode ); + } +} + +/**Function************************************************************* + + Synopsis [Makes the product of two partitions.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_SymmsAppendFromGroup( Abc_Ntk_t * pNtk, Vec_Ptr_t * vNodesPi, Vec_Ptr_t * vNodesOther, Vec_Int_t * vSymms, int * pMap ) +{ + Abc_Obj_t * pNode1, * pNode2; + unsigned uSymm; + int i, k; + + if ( vNodesPi->nSize == 0 ) + return; + + // go through the pairs + for ( i = 0; i < vNodesPi->nSize; i++ ) + for ( k = i+1; k < vNodesPi->nSize; k++ ) + { + // get the two PI nodes + pNode1 = Abc_ObjRegular(vNodesPi->pArray[i]); + pNode2 = Abc_ObjRegular(vNodesPi->pArray[k]); + assert( pMap[pNode1->Id] != pMap[pNode2->Id] ); + assert( pMap[pNode1->Id] >= 0 ); + assert( pMap[pNode2->Id] >= 0 ); + // generate symmetry + if ( pMap[pNode1->Id] < pMap[pNode2->Id] ) + uSymm = ((pMap[pNode1->Id] << 16) | pMap[pNode2->Id]); + else + uSymm = ((pMap[pNode2->Id] << 16) | pMap[pNode1->Id]); + // check if symmetry belongs + if ( Sim_SymmsIsCompatibleWithNodes( pNtk, uSymm, vNodesOther, pMap ) ) + Vec_IntPushUnique( vSymms, (int)uSymm ); + } +} + +/**Function************************************************************* + + Synopsis [Add the filters symmetries from the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_SymmsAppendFromNode( Abc_Ntk_t * pNtk, Vec_Int_t * vSymms0, Vec_Ptr_t * vNodesOther, + Vec_Ptr_t * vNodesPi0, Vec_Ptr_t * vNodesPi1, Vec_Int_t * vSymms, int * pMap ) +{ + unsigned uSymm; + int i; + + if ( vSymms0->nSize == 0 ) + return; + + // go through the pairs + for ( i = 0; i < vSymms0->nSize; i++ ) + { + uSymm = (unsigned)vSymms0->pArray[i]; + // check if symmetry belongs + if ( Sim_SymmsIsCompatibleWithNodes( pNtk, uSymm, vNodesOther, pMap ) && + Sim_SymmsIsCompatibleWithGroup( uSymm, vNodesPi0, pMap ) && + Sim_SymmsIsCompatibleWithGroup( uSymm, vNodesPi1, pMap ) ) + Vec_IntPushUnique( vSymms, (int)uSymm ); + } +} + +/**Function************************************************************* + + Synopsis [Returns 1 if symmetry is compatible with the group of nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sim_SymmsIsCompatibleWithNodes( Abc_Ntk_t * pNtk, unsigned uSymm, Vec_Ptr_t * vNodesOther, int * pMap ) +{ + Vec_Int_t * vSymmsNode; + Abc_Obj_t * pNode; + int i, s, Ind1, Ind2, fIsVar1, fIsVar2; + + if ( vNodesOther->nSize == 0 ) + return 1; + + // get the indices of the PI variables + Ind1 = (uSymm & 0xffff); + Ind2 = (uSymm >> 16); + + // go through the nodes + // if they do not belong to a support, it is okay + // if one belongs, the other does not belong, quit + // if they belong, but are not part of symmetry, quit + for ( i = 0; i < vNodesOther->nSize; i++ ) + { + pNode = Abc_ObjRegular(vNodesOther->pArray[i]); + fIsVar1 = Sim_SuppStrHasVar( pNtk->vSupps, pNode, Ind1 ); + fIsVar2 = Sim_SuppStrHasVar( pNtk->vSupps, pNode, Ind2 ); + + if ( !fIsVar1 && !fIsVar2 ) + continue; + if ( fIsVar1 ^ fIsVar2 ) + return 0; + // both belong + // check if there is a symmetry + vSymmsNode = SIM_READ_SYMMS( pNode ); + for ( s = 0; s < vSymmsNode->nSize; s++ ) + if ( uSymm == (unsigned)vSymmsNode->pArray[s] ) + break; + if ( s == vSymmsNode->nSize ) + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if symmetry is compatible with the group of PIs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sim_SymmsIsCompatibleWithGroup( unsigned uSymm, Vec_Ptr_t * vNodesPi, int * pMap ) +{ + Abc_Obj_t * pNode; + int i, Ind1, Ind2, fHasVar1, fHasVar2; + + if ( vNodesPi->nSize == 0 ) + return 1; + + // get the indices of the PI variables + Ind1 = (uSymm & 0xffff); + Ind2 = (uSymm >> 16); + + // go through the PI nodes + fHasVar1 = fHasVar2 = 0; + for ( i = 0; i < vNodesPi->nSize; i++ ) + { + pNode = Abc_ObjRegular(vNodesPi->pArray[i]); + if ( pMap[pNode->Id] == Ind1 ) + fHasVar1 = 1; + else if ( pMap[pNode->Id] == Ind2 ) + fHasVar2 = 1; + } + return fHasVar1 == fHasVar2; +} + + + +/**Function************************************************************* + + Synopsis [Improvements due to transitivity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_SymmsTrans( Vec_Int_t * vSymms ) +{ + unsigned uSymm, uSymma, uSymmr; + int i, Ind1, Ind2; + int k, Ind1a, Ind2a; + int j; + int nTrans = 0; + + for ( i = 0; i < vSymms->nSize; i++ ) + { + uSymm = (unsigned)vSymms->pArray[i]; + Ind1 = (uSymm & 0xffff); + Ind2 = (uSymm >> 16); + // find other symmetries that have Ind1 + for ( k = i+1; k < vSymms->nSize; k++ ) + { + uSymma = (unsigned)vSymms->pArray[k]; + if ( uSymma == uSymm ) + continue; + Ind1a = (uSymma & 0xffff); + Ind2a = (uSymma >> 16); + if ( Ind1a == Ind1 ) + { + // find the symmetry (Ind2,Ind2a) + if ( Ind2 < Ind2a ) + uSymmr = ((Ind2 << 16) | Ind2a); + else + uSymmr = ((Ind2a << 16) | Ind2); + for ( j = 0; j < vSymms->nSize; j++ ) + if ( uSymmr == (unsigned)vSymms->pArray[j] ) + break; + if ( j == vSymms->nSize ) + nTrans++; + } + } + + } + printf( "Trans = %d.\n", nTrans ); +} + + +/**Function************************************************************* + + Synopsis [Transfers from the vector to the matrix.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_SymmsTransferToMatrix( Extra_BitMat_t * pMatSymm, Vec_Int_t * vSymms, unsigned * pSupport ) +{ + int i, Ind1, Ind2, nInputs; + unsigned uSymm; + // add diagonal elements + nInputs = Extra_BitMatrixReadSize( pMatSymm ); + for ( i = 0; i < nInputs; i++ ) + Extra_BitMatrixInsert1( pMatSymm, i, i ); + // add non-diagonal elements + for ( i = 0; i < vSymms->nSize; i++ ) + { + uSymm = (unsigned)vSymms->pArray[i]; + Ind1 = (uSymm & 0xffff); + Ind2 = (uSymm >> 16); +//printf( "%d,%d ", Ind1, Ind2 ); + // skip variables that are not in the true support + assert( Sim_HasBit(pSupport, Ind1) == Sim_HasBit(pSupport, Ind2) ); + if ( !Sim_HasBit(pSupport, Ind1) || !Sim_HasBit(pSupport, Ind2) ) + continue; + Extra_BitMatrixInsert1( pMatSymm, Ind1, Ind2 ); + Extra_BitMatrixInsert2( pMatSymm, Ind1, Ind2 ); + } +} + +/**Function************************************************************* + + Synopsis [Mapping of indices into numbers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int * Sim_SymmsCreateMap( Abc_Ntk_t * pNtk ) +{ + int * pMap; + Abc_Obj_t * pNode; + int i; + pMap = ALLOC( int, Abc_NtkObjNumMax(pNtk) ); + for ( i = 0; i < Abc_NtkObjNumMax(pNtk); i++ ) + pMap[i] = -1; + Abc_NtkForEachCi( pNtk, pNode, i ) + pMap[pNode->Id] = i; + return pMap; +} + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/opt/sim/simUtils.c b/abc_with_bb_support/src/opt/sim/simUtils.c new file mode 100644 index 000000000..13cf2408b --- /dev/null +++ b/abc_with_bb_support/src/opt/sim/simUtils.c @@ -0,0 +1,711 @@ +/**CFile**************************************************************** + + FileName [simUtils.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Network and node package.] + + Synopsis [Various simulation utilities.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: simUtils.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "sim.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int bit_count[256] = { + 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 +}; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates simulation information for all nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Ptr_t * Sim_UtilInfoAlloc( int nSize, int nWords, bool fClean ) +{ + Vec_Ptr_t * vInfo; + int i; + assert( nSize > 0 && nWords > 0 ); + vInfo = Vec_PtrAlloc( nSize ); + vInfo->pArray[0] = ALLOC( unsigned, nSize * nWords ); + if ( fClean ) + memset( vInfo->pArray[0], 0, sizeof(unsigned) * nSize * nWords ); + for ( i = 1; i < nSize; i++ ) + vInfo->pArray[i] = ((unsigned *)vInfo->pArray[i-1]) + nWords; + vInfo->nSize = nSize; + return vInfo; +} + +/**Function************************************************************* + + Synopsis [Allocates simulation information for all nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_UtilInfoFree( Vec_Ptr_t * p ) +{ + free( p->pArray[0] ); + Vec_PtrFree( p ); +} + +/**Function************************************************************* + + Synopsis [Adds the second supp-info the first.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_UtilInfoAdd( unsigned * pInfo1, unsigned * pInfo2, int nWords ) +{ + int w; + for ( w = 0; w < nWords; w++ ) + pInfo1[w] |= pInfo2[w]; +} + +/**Function************************************************************* + + Synopsis [Returns the positions where pInfo2 is 1 while pInfo1 is 0.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_UtilInfoDetectDiffs( unsigned * pInfo1, unsigned * pInfo2, int nWords, Vec_Int_t * vDiffs ) +{ + int w, b; + unsigned uMask; + vDiffs->nSize = 0; + for ( w = 0; w < nWords; w++ ) + if ( uMask = (pInfo2[w] ^ pInfo1[w]) ) + for ( b = 0; b < 32; b++ ) + if ( uMask & (1 << b) ) + Vec_IntPush( vDiffs, 32*w + b ); +} + +/**Function************************************************************* + + Synopsis [Returns the positions where pInfo2 is 1 while pInfo1 is 0.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_UtilInfoDetectNews( unsigned * pInfo1, unsigned * pInfo2, int nWords, Vec_Int_t * vDiffs ) +{ + int w, b; + unsigned uMask; + vDiffs->nSize = 0; + for ( w = 0; w < nWords; w++ ) + if ( uMask = (pInfo2[w] & ~pInfo1[w]) ) + for ( b = 0; b < 32; b++ ) + if ( uMask & (1 << b) ) + Vec_IntPush( vDiffs, 32*w + b ); +} + +/**Function************************************************************* + + Synopsis [Flips the simulation info of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_UtilInfoFlip( Sim_Man_t * p, Abc_Obj_t * pNode ) +{ + unsigned * pSimInfo1, * pSimInfo2; + int k; + pSimInfo1 = p->vSim0->pArray[pNode->Id]; + pSimInfo2 = p->vSim1->pArray[pNode->Id]; + for ( k = 0; k < p->nSimWords; k++ ) + pSimInfo2[k] = ~pSimInfo1[k]; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the simulation infos are equal.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Sim_UtilInfoCompare( Sim_Man_t * p, Abc_Obj_t * pNode ) +{ + unsigned * pSimInfo1, * pSimInfo2; + int k; + pSimInfo1 = p->vSim0->pArray[pNode->Id]; + pSimInfo2 = p->vSim1->pArray[pNode->Id]; + for ( k = 0; k < p->nSimWords; k++ ) + if ( pSimInfo2[k] != pSimInfo1[k] ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Simulates the internal nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_UtilSimulate( Sim_Man_t * p, bool fType ) +{ + Abc_Obj_t * pNode; + int i; + // simulate the internal nodes + Abc_NtkForEachNode( p->pNtk, pNode, i ) + Sim_UtilSimulateNode( p, pNode, fType, fType, fType ); + // assign simulation info of the CO nodes + Abc_NtkForEachCo( p->pNtk, pNode, i ) + Sim_UtilSimulateNode( p, pNode, fType, fType, fType ); +} + +/**Function************************************************************* + + Synopsis [Simulates one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_UtilSimulateNode( Sim_Man_t * p, Abc_Obj_t * pNode, bool fType, bool fType1, bool fType2 ) +{ + unsigned * pSimmNode, * pSimmNode1, * pSimmNode2; + int k, fComp1, fComp2; + // simulate the internal nodes + if ( Abc_ObjIsNode(pNode) ) + { + if ( fType ) + pSimmNode = p->vSim1->pArray[ pNode->Id ]; + else + pSimmNode = p->vSim0->pArray[ pNode->Id ]; + + if ( fType1 ) + pSimmNode1 = p->vSim1->pArray[ Abc_ObjFaninId0(pNode) ]; + else + pSimmNode1 = p->vSim0->pArray[ Abc_ObjFaninId0(pNode) ]; + + if ( fType2 ) + pSimmNode2 = p->vSim1->pArray[ Abc_ObjFaninId1(pNode) ]; + else + pSimmNode2 = p->vSim0->pArray[ Abc_ObjFaninId1(pNode) ]; + + fComp1 = Abc_ObjFaninC0(pNode); + fComp2 = Abc_ObjFaninC1(pNode); + if ( fComp1 && fComp2 ) + for ( k = 0; k < p->nSimWords; k++ ) + pSimmNode[k] = ~pSimmNode1[k] & ~pSimmNode2[k]; + else if ( fComp1 && !fComp2 ) + for ( k = 0; k < p->nSimWords; k++ ) + pSimmNode[k] = ~pSimmNode1[k] & pSimmNode2[k]; + else if ( !fComp1 && fComp2 ) + for ( k = 0; k < p->nSimWords; k++ ) + pSimmNode[k] = pSimmNode1[k] & ~pSimmNode2[k]; + else // if ( fComp1 && fComp2 ) + for ( k = 0; k < p->nSimWords; k++ ) + pSimmNode[k] = pSimmNode1[k] & pSimmNode2[k]; + } + else + { + assert( Abc_ObjFaninNum(pNode) == 1 ); + if ( fType ) + pSimmNode = p->vSim1->pArray[ pNode->Id ]; + else + pSimmNode = p->vSim0->pArray[ pNode->Id ]; + + if ( fType1 ) + pSimmNode1 = p->vSim1->pArray[ Abc_ObjFaninId0(pNode) ]; + else + pSimmNode1 = p->vSim0->pArray[ Abc_ObjFaninId0(pNode) ]; + + fComp1 = Abc_ObjFaninC0(pNode); + if ( fComp1 ) + for ( k = 0; k < p->nSimWords; k++ ) + pSimmNode[k] = ~pSimmNode1[k]; + else + for ( k = 0; k < p->nSimWords; k++ ) + pSimmNode[k] = pSimmNode1[k]; + } +} + +/**Function************************************************************* + + Synopsis [Simulates one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_UtilSimulateNodeOne( Abc_Obj_t * pNode, Vec_Ptr_t * vSimInfo, int nSimWords, int nOffset ) +{ + unsigned * pSimmNode, * pSimmNode1, * pSimmNode2; + int k, fComp1, fComp2; + // simulate the internal nodes + assert( Abc_ObjIsNode(pNode) ); + pSimmNode = Vec_PtrEntry(vSimInfo, pNode->Id); + pSimmNode1 = Vec_PtrEntry(vSimInfo, Abc_ObjFaninId0(pNode)); + pSimmNode2 = Vec_PtrEntry(vSimInfo, Abc_ObjFaninId1(pNode)); + pSimmNode += nOffset; + pSimmNode1 += nOffset; + pSimmNode2 += nOffset; + fComp1 = Abc_ObjFaninC0(pNode); + fComp2 = Abc_ObjFaninC1(pNode); + if ( fComp1 && fComp2 ) + for ( k = 0; k < nSimWords; k++ ) + pSimmNode[k] = ~pSimmNode1[k] & ~pSimmNode2[k]; + else if ( fComp1 && !fComp2 ) + for ( k = 0; k < nSimWords; k++ ) + pSimmNode[k] = ~pSimmNode1[k] & pSimmNode2[k]; + else if ( !fComp1 && fComp2 ) + for ( k = 0; k < nSimWords; k++ ) + pSimmNode[k] = pSimmNode1[k] & ~pSimmNode2[k]; + else // if ( fComp1 && fComp2 ) + for ( k = 0; k < nSimWords; k++ ) + pSimmNode[k] = pSimmNode1[k] & pSimmNode2[k]; +} + +/**Function************************************************************* + + Synopsis [Simulates one node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_UtilTransferNodeOne( Abc_Obj_t * pNode, Vec_Ptr_t * vSimInfo, int nSimWords, int nOffset, int fShift ) +{ + unsigned * pSimmNode, * pSimmNode1; + int k, fComp1; + // simulate the internal nodes + assert( Abc_ObjIsCo(pNode) ); + pSimmNode = Vec_PtrEntry(vSimInfo, pNode->Id); + pSimmNode1 = Vec_PtrEntry(vSimInfo, Abc_ObjFaninId0(pNode)); + pSimmNode += nOffset + (fShift > 0)*nSimWords; + pSimmNode1 += nOffset; + fComp1 = Abc_ObjFaninC0(pNode); + if ( fComp1 ) + for ( k = 0; k < nSimWords; k++ ) + pSimmNode[k] = ~pSimmNode1[k]; + else + for ( k = 0; k < nSimWords; k++ ) + pSimmNode[k] = pSimmNode1[k]; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the simulation infos are equal.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sim_UtilCountSuppSizes( Sim_Man_t * p, int fStruct ) +{ + Abc_Obj_t * pNode, * pNodeCi; + int i, v, Counter; + Counter = 0; + if ( fStruct ) + { + Abc_NtkForEachCo( p->pNtk, pNode, i ) + Abc_NtkForEachCi( p->pNtk, pNodeCi, v ) + Counter += Sim_SuppStrHasVar( p->vSuppStr, pNode, v ); + } + else + { + Abc_NtkForEachCo( p->pNtk, pNode, i ) + Abc_NtkForEachCi( p->pNtk, pNodeCi, v ) + Counter += Sim_SuppFunHasVar( p->vSuppFun, i, v ); + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Counts the number of 1's in the bitstring.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sim_UtilCountOnes( unsigned * pSimInfo, int nSimWords ) +{ + unsigned char * pBytes; + int nOnes, nBytes, i; + pBytes = (unsigned char *)pSimInfo; + nBytes = 4 * nSimWords; + nOnes = 0; + for ( i = 0; i < nBytes; i++ ) + nOnes += bit_count[ pBytes[i] ]; + return nOnes; +} + +/**Function************************************************************* + + Synopsis [Counts the number of 1's in the bitstring.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * Sim_UtilCountOnesArray( Vec_Ptr_t * vInfo, int nSimWords ) +{ + Vec_Int_t * vCounters; + unsigned * pSimInfo; + int i; + vCounters = Vec_IntStart( Vec_PtrSize(vInfo) ); + Vec_PtrForEachEntry( vInfo, pSimInfo, i ) + Vec_IntWriteEntry( vCounters, i, Sim_UtilCountOnes(pSimInfo, nSimWords) ); + return vCounters; +} + +/**Function************************************************************* + + Synopsis [Returns random patterns.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_UtilSetRandom( unsigned * pPatRand, int nSimWords ) +{ + int k; + for ( k = 0; k < nSimWords; k++ ) + pPatRand[k] = SIM_RANDOM_UNSIGNED; +} + +/**Function************************************************************* + + Synopsis [Returns complemented patterns.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_UtilSetCompl( unsigned * pPatRand, int nSimWords ) +{ + int k; + for ( k = 0; k < nSimWords; k++ ) + pPatRand[k] = ~pPatRand[k]; +} + +/**Function************************************************************* + + Synopsis [Returns constant patterns.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_UtilSetConst( unsigned * pPatRand, int nSimWords, int fConst1 ) +{ + int k; + for ( k = 0; k < nSimWords; k++ ) + pPatRand[k] = 0; + if ( fConst1 ) + Sim_UtilSetCompl( pPatRand, nSimWords ); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if equal.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sim_UtilInfoIsEqual( unsigned * pPats1, unsigned * pPats2, int nSimWords ) +{ + int k; + for ( k = 0; k < nSimWords; k++ ) + if ( pPats1[k] != pPats2[k] ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if Node1 implies Node2.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sim_UtilInfoIsImp( unsigned * pPats1, unsigned * pPats2, int nSimWords ) +{ + int k; + for ( k = 0; k < nSimWords; k++ ) + if ( pPats1[k] & ~pPats2[k] ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if Node1 v Node2 is always true.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sim_UtilInfoIsClause( unsigned * pPats1, unsigned * pPats2, int nSimWords ) +{ + int k; + for ( k = 0; k < nSimWords; k++ ) + if ( ~pPats1[k] & ~pPats2[k] ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Counts the total number of pairs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sim_UtilCountAllPairs( Vec_Ptr_t * vSuppFun, int nSimWords, Vec_Int_t * vCounters ) +{ + unsigned * pSupp; + int Counter, nOnes, nPairs, i; + Counter = 0; + Vec_PtrForEachEntry( vSuppFun, pSupp, i ) + { + nOnes = Sim_UtilCountOnes( pSupp, nSimWords ); + nPairs = nOnes * (nOnes - 1) / 2; + Vec_IntWriteEntry( vCounters, i, nPairs ); + Counter += nPairs; + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Counts the number of entries in the array of matrices.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sim_UtilCountPairsOne( Extra_BitMat_t * pMat, Vec_Int_t * vSupport ) +{ + int i, k, Index1, Index2; + int Counter = 0; +// int Counter2; + Vec_IntForEachEntry( vSupport, i, Index1 ) + Vec_IntForEachEntryStart( vSupport, k, Index2, Index1+1 ) + Counter += Extra_BitMatrixLookup1( pMat, i, k ); +// Counter2 = Extra_BitMatrixCountOnesUpper(pMat); +// assert( Counter == Counter2 ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Counts the number of entries in the array of matrices.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sim_UtilCountPairsOnePrint( Extra_BitMat_t * pMat, Vec_Int_t * vSupport ) +{ + int i, k, Index1, Index2; + Vec_IntForEachEntry( vSupport, i, Index1 ) + Vec_IntForEachEntryStart( vSupport, k, Index2, Index1+1 ) + if ( Extra_BitMatrixLookup1( pMat, i, k ) ) + printf( "(%d,%d) ", i, k ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Counts the number of entries in the array of matrices.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_UtilCountPairsAllPrint( Sym_Man_t * p ) +{ + int i, clk; +clk = clock(); + for ( i = 0; i < p->nOutputs; i++ ) + { + printf( "Output %2d :", i ); + Sim_UtilCountPairsOnePrint( Vec_PtrEntry(p->vMatrSymms, i), Vec_VecEntry(p->vSupports, i) ); + printf( "\n" ); + } +p->timeCount += clock() - clk; +} + +/**Function************************************************************* + + Synopsis [Counts the number of entries in the array of matrices.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sim_UtilCountPairsAll( Sym_Man_t * p ) +{ + int nPairsTotal, nPairsSym, nPairsNonSym, i, clk; +clk = clock(); + p->nPairsSymm = 0; + p->nPairsNonSymm = 0; + for ( i = 0; i < p->nOutputs; i++ ) + { + nPairsTotal = Vec_IntEntry(p->vPairsTotal, i); + nPairsSym = Vec_IntEntry(p->vPairsSym, i); + nPairsNonSym = Vec_IntEntry(p->vPairsNonSym,i); + assert( nPairsTotal >= nPairsSym + nPairsNonSym ); + if ( nPairsTotal == nPairsSym + nPairsNonSym ) + { + p->nPairsSymm += nPairsSym; + p->nPairsNonSymm += nPairsNonSym; + continue; + } + nPairsSym = Sim_UtilCountPairsOne( Vec_PtrEntry(p->vMatrSymms, i), Vec_VecEntry(p->vSupports, i) ); + nPairsNonSym = Sim_UtilCountPairsOne( Vec_PtrEntry(p->vMatrNonSymms,i), Vec_VecEntry(p->vSupports, i) ); + assert( nPairsTotal >= nPairsSym + nPairsNonSym ); + Vec_IntWriteEntry( p->vPairsSym, i, nPairsSym ); + Vec_IntWriteEntry( p->vPairsNonSym, i, nPairsNonSym ); + p->nPairsSymm += nPairsSym; + p->nPairsNonSymm += nPairsNonSym; +// printf( "%d ", nPairsTotal - nPairsSym - nPairsNonSym ); + } +//printf( "\n" ); + p->nPairsRem = p->nPairsTotal-p->nPairsSymm-p->nPairsNonSymm; +p->timeCount += clock() - clk; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sim_UtilMatrsAreDisjoint( Sym_Man_t * p ) +{ + int i; + for ( i = 0; i < p->nOutputs; i++ ) + if ( !Extra_BitMatrixIsDisjoint( Vec_PtrEntry(p->vMatrSymms,i), Vec_PtrEntry(p->vMatrNonSymms,i) ) ) + return 0; + return 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/phys/place/Makefile b/abc_with_bb_support/src/phys/place/Makefile new file mode 100644 index 000000000..1f700105d --- /dev/null +++ b/abc_with_bb_support/src/phys/place/Makefile @@ -0,0 +1,30 @@ +TARGETS = place_test BookshelfView.class + +CFLAGS = -g -pedantic -Wall + +STATIC_LIBS = libhmetis.a +DYNAMIC_LIBS = -lm + +OBJECTS = place_test.o place_qpsolver.o place_base.o place_pads.o place_genqp.o place_gordian.o \ + place_partition.o place_legalize.o place_bin.o + + +# For hMetis free code, uncomment the following lines +# +# CFLAGS = -g -pedantic -Wall -DNO_HMETIS +# STATIC_LIBS = + + +all: $(TARGETS) + +%.o: %.c *.h + gcc $(CFLAGS) -c -o $@ $< + +place_test: $(OBJECTS) + gcc *.o $(STATIC_LIBS) $(DYNAMIC_LIBS) -o place_test + +BookshelfView.class: BookshelfView.java + javac BookshelfView.java + +clean: + rm -rf *.o place_test *.class *~ diff --git a/abc_with_bb_support/src/phys/place/README b/abc_with_bb_support/src/phys/place/README new file mode 100644 index 000000000..d4f8ac8f4 --- /dev/null +++ b/abc_with_bb_support/src/phys/place/README @@ -0,0 +1,50 @@ +/*===================================================================*/ +// +// GORDIAN-like placement package +// +// Aaron P. Hurst (ahurst@eecs.berkeley.edu) +// Addl code from Philip Chong (pchong@cadence.com) +// hMetis partitioner (www.cs.umn.edu/~metis) +// +/*===================================================================*/ + +1. Requirements + +An i386 Linux system (though others will certainly work with some tweaks). +A standard ANSI C development platform. + +The following are optional, but useful: + +- hMetis partitioner. This can be obtained from (www.cs.umn.edu/~metis) + Place (links to) the files "libhmetis.a" and "libhtmetis.h" in this directory. + Otherwise, #define NO_HMETIS in the file "place_gordian.h" +- Java SDK, if compiling BookshelfView is desired. +- Perl, if additional script utilities are desired. + +2. Descriptions of contents: + +place_base.h contains the basic data structures and "external" API. +place_gordian.h contains the "internal" API and configuration options. + +There are also several utilities: + +i) place_test + +Reads a netlist description in GSRC Bookshelf format, performs global placement, +and rewrites the placement file. An example usage: + +./place_test ac97_emap.nodes ac97_emap.nets ac97_emap.pl + +ii) BookshelfView + +A simple Java GUI to view the resulting placements. It has been tested with +Java 5 and 6. Usage: + +java BookshelfView ac97_emap.nodes ac97_emap.pl + +iii) hpwl + +A perl script to print the half-perimeter wirelength of a placement. Usage: + +./hpwl ac97_emap.nets ac97_emal.pl + diff --git a/abc_with_bb_support/src/phys/place/hpwl b/abc_with_bb_support/src/phys/place/hpwl new file mode 100644 index 000000000..f69a1d054 --- /dev/null +++ b/abc_with_bb_support/src/phys/place/hpwl @@ -0,0 +1,57 @@ +#! /usr/bin/perl + +$netsfile = shift; +$plfile = shift; + +# ------------------------------ read placement + +open FILE, $plfile; +while () { + chop; + if (/(\w+)\s+([\-\d\.]+)\s+([\-\d\.]+)\s+\:/) { + $loc{$1} = "$2 $3"; + } +} +close FILE; + +open FILE, $netsfile; +while () { + chop; + $net = $2 if /NetDegree\s+\:\s+(\d+)\s+(\w+)/; + if (/(\w+)\s+(\w+)\s+\:/) { + $netconn{$net} .= "$1 "; + $cellconn{$1} .= "$net "; + } +} +close FILE; + +# ----------------------------- compute HPWL + +$hpwl = 0; +foreach $net (keys %netconn) { + @conns = split ' ',$netconn{$net}; + $min_x = $min_y = 1e12; + $max_x = $max_y = -1e12; + foreach $cell (@conns) { + if (!exists $loc{$cell}) { + print "WARNING: Unknown cell location: $cell\n"; + } else { + ($x, $y) = split ' ',$loc{$cell}; + $min_x = $x if $x < $min_x; + $min_y = $y if $y < $min_y; + $max_x = $x if $x > $max_x; + $max_y = $y if $y > $max_y; + } + } + + if ($min_x eq 1e12 or $min_y eq 1e12 or + $max_x eq -1e12 or $max_y eq -1e12) { + print "WARNING: Unbounded box\n"; + } else { + $hpwl = $hpwl + $max_x - $min_x + $max_y - $min_y; + } +} + +print "HPWL = "; +printf "%e",$hpwl; +print "\n"; diff --git a/abc_with_bb_support/src/phys/place/libhmetis.h b/abc_with_bb_support/src/phys/place/libhmetis.h new file mode 100644 index 000000000..2d30b38f7 --- /dev/null +++ b/abc_with_bb_support/src/phys/place/libhmetis.h @@ -0,0 +1,31 @@ +// A. Hurst ahurst@eecs.berkeley.edu + +#ifndef LIBHMETIS_H_ +#define LIBHMETIS_H_ + +static void HMETIS_PartRecursive(int nvtxs, + int nhedges, + int *vwgts, + int *eptr, + int *eind, + int *hewgts, + int nparts, + int nbfactor, + int *options, + int *part, + int *edgecnt ) {} //; + + +static void HMETIS_PartKway(int nvtxs, + int nhedges, + int *vwgts, + int *eptr, + int *eind, + int *hewgts, + int nparts, + int nbfactor, + int *options, + int *part, + int *edgecnt ) {} //; + +#endif diff --git a/abc_with_bb_support/src/phys/place/module.make b/abc_with_bb_support/src/phys/place/module.make new file mode 100644 index 000000000..908c3ada4 --- /dev/null +++ b/abc_with_bb_support/src/phys/place/module.make @@ -0,0 +1,10 @@ +SRC += src/phys/place/place_base.c \ + src/phys/place/place_bin.c \ + src/phys/place/place_genqp.c \ + src/phys/place/place_gordian.c \ + src/phys/place/place_legalize.c \ + src/phys/place/place_pads.c \ + src/phys/place/place_partition.c \ + src/phys/place/place_qpsolver.c \ + src/phys/place/place_io.c \ + src/phys/place/place_inc.c diff --git a/abc_with_bb_support/src/phys/place/place_base.c b/abc_with_bb_support/src/phys/place/place_base.c new file mode 100644 index 000000000..ff4edebd7 --- /dev/null +++ b/abc_with_bb_support/src/phys/place/place_base.c @@ -0,0 +1,345 @@ +/*===================================================================*/ +// +// place_base.c +// +// Aaron P. Hurst, 2003-2007 +// ahurst@eecs.berkeley.edu +// +/*===================================================================*/ + +#include +#include +#include +#include + +#include "place_base.h" +#include "place_gordian.h" + +// -------------------------------------------------------------------- +// Global variables +// +// -------------------------------------------------------------------- + +int g_place_numCells = 0; +int g_place_numNets = 0; +float g_place_rowHeight = 1.0; + +Rect g_place_coreBounds; +Rect g_place_padBounds; + +ConcreteCell **g_place_concreteCells = NULL; +int g_place_concreteCellsSize = 0; +ConcreteNet **g_place_concreteNets = NULL; +int g_place_concreteNetsSize = 0; + + +// -------------------------------------------------------------------- +// getNetBBox() +// +/// \brief Returns the bounding box of a net. +// +// -------------------------------------------------------------------- +Rect getNetBBox(const ConcreteNet *net) { + int t; + Rect r; + + assert(net); + + r.x = r.y = INT_MAX; + r.w = r.h = -INT_MAX; + for(t=0; tm_numTerms; t++) { + r.x = net->m_terms[t]->m_x < r.x ? net->m_terms[t]->m_x : r.x; + r.y = net->m_terms[t]->m_y < r.y ? net->m_terms[t]->m_y : r.y; + r.w = net->m_terms[t]->m_x > r.w ? net->m_terms[t]->m_x : r.w; + r.h = net->m_terms[t]->m_y > r.h ? net->m_terms[t]->m_y : r.h; + } + r.w -= r.x; r.h -= r.y; + return r; +} + + +// -------------------------------------------------------------------- +// getNetWirelength() +// +/// \brief Returns the half-perimeter wirelength of a net. +// +// -------------------------------------------------------------------- +float getNetWirelength(const ConcreteNet *net) { + Rect r; + + assert(net); + + r = getNetBBox(net); + return r.w+r.h; +} + + +// -------------------------------------------------------------------- +// getTotalWirelength() +// +/// \brief Returns the total HPWL of all nets. +// +// -------------------------------------------------------------------- +float getTotalWirelength() { + float r = 0; + int n; + for(n=0; nm_parent->m_width*cell->m_parent->m_height; +} + + +// -------------------------------------------------------------------- +// addConcreteNet() +// +/// \brief Adds a net to the placement database. +/// +/// The net object must already be allocated and the ID must be set +/// appropriately. +// +// -------------------------------------------------------------------- +void addConcreteNet(ConcreteNet *net) { + assert(net); + assert(net->m_id >= 0); + if (net->m_id >= g_place_concreteNetsSize) { + g_place_concreteNetsSize = (net->m_id > g_place_concreteNetsSize ? + net->m_id : g_place_concreteNetsSize); + g_place_concreteNetsSize *= 1.5; + g_place_concreteNetsSize += 20; + g_place_concreteNets = (ConcreteNet**)realloc(g_place_concreteNets, + sizeof(ConcreteNet*)*g_place_concreteNetsSize); + assert(g_place_concreteNets); + } + if (net->m_id >= g_place_numNets) { + memset(&(g_place_concreteNets[g_place_numNets]), 0, + sizeof(ConcreteNet*)*(net->m_id+1-g_place_numNets)); + g_place_numNets = net->m_id+1; + assert(g_place_numNets <= g_place_concreteNetsSize); + } + g_place_concreteNets[net->m_id] = net; +} + + +// -------------------------------------------------------------------- +// delConcreteNet() +// +/// Does not deallocate memory. +// -------------------------------------------------------------------- +void delConcreteNet(ConcreteNet *net) { + assert(net); + g_place_concreteNets[net->m_id] = 0; + while(!g_place_concreteNets[g_place_numNets-1]) g_place_numNets--; +} + + +// -------------------------------------------------------------------- +// addConcreteCell() +// +/// The cell object must already be allocated and the ID must be set +/// appropriately. +// +// -------------------------------------------------------------------- +void addConcreteCell(ConcreteCell *cell) { + assert(cell); + assert(cell->m_id >= 0); + if (cell->m_id >= g_place_concreteCellsSize) { + g_place_concreteCellsSize = (cell->m_id > g_place_concreteCellsSize ? + cell->m_id : g_place_concreteCellsSize); + g_place_concreteCellsSize *= 1.5; + g_place_concreteCellsSize += 20; + g_place_concreteCells = (ConcreteCell**)realloc(g_place_concreteCells, + sizeof(ConcreteCell*)*g_place_concreteCellsSize); + assert(g_place_concreteCells); + } + if (cell->m_id >= g_place_numCells) { + memset(&(g_place_concreteCells[g_place_numCells]), 0, + sizeof(ConcreteCell*)*(cell->m_id+1-g_place_numCells)); + g_place_numCells = cell->m_id+1; + } + g_place_concreteCells[cell->m_id] = cell; +} + + +// -------------------------------------------------------------------- +// delCellFromPartition() +// +// -------------------------------------------------------------------- +void delCellFromPartition(ConcreteCell *cell, Partition *p) { + int c; + bool found = false; + + assert(cell); + assert(p); + + for(c=0; cm_numMembers; c++) + if (p->m_members[c] == cell) { + p->m_members[c] = 0; + p->m_area -= getCellArea(cell); + found = true; + break; + } + + if (!found) return; + + if (!p->m_leaf) { + delCellFromPartition(cell, p->m_sub1); + delCellFromPartition(cell, p->m_sub2); + } +} + + +// -------------------------------------------------------------------- +// delConcreteCell() +// +/// \brief Removes a cell from the placement database. +/// +/// Does not deallocate memory. +/// +/// Important: does not modify nets that may point to this +/// cell. If these are connections are not removed, segmentation faults +/// and other nasty errors will occur. +// +// -------------------------------------------------------------------- +void delConcreteCell(ConcreteCell *cell) { + assert(cell); + g_place_concreteCells[cell->m_id] = 0; + while(!g_place_concreteCells[g_place_numCells-1]) g_place_numCells--; + + if (g_place_rootPartition) delCellFromPartition(cell, g_place_rootPartition); +} + + +// -------------------------------------------------------------------- +// netSortByX... +// +/// \brief Sorts nets by position of one of its corners. +// +/// These are for use with qsort(). +/// +/// Can tolerate pointers to NULL objects. +/// +// -------------------------------------------------------------------- +int netSortByL(const void *a, const void *b) { + const ConcreteNet *pa = *(const ConcreteNet **)a; + const ConcreteNet *pb = *(const ConcreteNet **)b; + Rect ba, bb; + + if (!pa && !pb) return 0; + else if (!pa) return -1; + else if (!pb) return 1; + ba = getNetBBox(pa), bb = getNetBBox(pb); + if (ba.x < bb.x) return -1; + if (ba.x > bb.x) return 1; + return 0; +} + +int netSortByR(const void *a, const void *b) { + const ConcreteNet *pa = *(const ConcreteNet **)a; + const ConcreteNet *pb = *(const ConcreteNet **)b; + Rect ba, bb; + + if (!pa && !pb) return 0; + else if (!pa) return -1; + else if (!pb) return 1; + ba = getNetBBox(pa), bb = getNetBBox(pb); + if (ba.x + ba.w < bb.x + bb.w) return -1; + if (ba.x + ba.w > bb.x + bb.w) return 1; + return 0; +} + +int netSortByB(const void *a, const void *b) { + const ConcreteNet *pa = *(const ConcreteNet **)a; + const ConcreteNet *pb = *(const ConcreteNet **)b; + Rect ba, bb; + + if (!pa && !pb) return 0; + else if (!pa) return -1; + else if (!pb) return 1; + ba = getNetBBox(pa), bb = getNetBBox(pb); + if (ba.y + ba.h < bb.y + bb.h) return -1; + if (ba.y + ba.h > bb.y + bb.h) return 1; + return 0; +} + +int netSortByT(const void *a, const void *b) { + const ConcreteNet *pa = *(const ConcreteNet **)a; + const ConcreteNet *pb = *(const ConcreteNet **)b; + Rect ba, bb; + + if (!pa && !pb) return 0; + else if (!pa) return -1; + else if (!pb) return 1; + ba = getNetBBox(pa), bb = getNetBBox(pb); + if (ba.y < bb.y) return -1; + if (ba.y > bb.y) return 1; + return 0; +} + +int netSortByID(const void *a, const void *b) { + const ConcreteNet *pa = *(const ConcreteNet **)a; + const ConcreteNet *pb = *(const ConcreteNet **)b; + + if (!pa && !pb) return 0; + else if (!pa) return -1; + else if (!pb) return 1; + if (pa->m_id < pb->m_id) return -1; + if (pa->m_id > pb->m_id) return 1; + return 0; +} + + +// -------------------------------------------------------------------- +// cellSortByX... +// +/// \brief Sorts cells by either position coordinate. +// +/// These are for use with qsort(). +/// +/// Can tolerate pointers to NULL objects. +// +// -------------------------------------------------------------------- +int cellSortByX(const void *a, const void *b) { + const ConcreteCell *pa = *(const ConcreteCell **)a; + const ConcreteCell *pb = *(const ConcreteCell **)b; + + if (!pa && !pb) return 0; + else if (!pa) return -1; + else if (!pb) return 1; + if (pa->m_x < pb->m_x) return -1; + if (pa->m_x > pb->m_x) return 1; + return 0; +} + +int cellSortByY(const void *a, const void *b) { + const ConcreteCell *pa = *(const ConcreteCell **)a; + const ConcreteCell *pb = *(const ConcreteCell **)b; + + if (!pa && !pb) return 0; + else if (!pa) return -1; + else if (!pb) return 1; + if (pa->m_y < pb->m_y) return -1; + if (pa->m_y > pb->m_y) return 1; + return 0; +} + +int cellSortByID(const void *a, const void *b) { + const ConcreteCell *pa = *(const ConcreteCell **)a; + const ConcreteCell *pb = *(const ConcreteCell **)b; + + if (!pa && !pb) return 0; + else if (!pa) return -1; + else if (!pb) return 1; + if (pa->m_id < pb->m_id) return -1; + if (pa->m_id > pb->m_id) return 1; + return 0; +} diff --git a/abc_with_bb_support/src/phys/place/place_base.h b/abc_with_bb_support/src/phys/place/place_base.h new file mode 100644 index 000000000..8486407f2 --- /dev/null +++ b/abc_with_bb_support/src/phys/place/place_base.h @@ -0,0 +1,137 @@ +/*===================================================================*/ +// +// place_base.h +// +// Aaron P. Hurst, 2003-2007 +// ahurst@eecs.berkeley.edu +// +/*===================================================================*/ + +#if !defined(PLACE_BASE_H_) +#define PLACE_BASE_H_ + +// -------------------------------------------------------------------- +// Data structures +// +// -------------------------------------------------------------------- + +// --- a C++ bool-like type +//typedef char bool; +#ifndef bool +#define bool int +#endif + +#define true 1 +#define false 0 + + +// --- Rect - rectangle + +typedef struct Rect { + float x, y; + float w, h; +} Rect; + + +// --- AbstractCell - a definition of a cell type + +typedef struct AbstractCell { + char *m_label; // string description + + float m_width, m_height; // dimensions + + bool m_pad; // a pad (external I/O) cell? +} AbstractCell; + + +// --- ConcreteCell - a design object + +typedef struct ConcreteCell { + int m_id; // a unique ID (see below) + char *m_label; // string description + + AbstractCell *m_parent; // cell type + + bool m_fixed; // position is fixed? + float m_x, m_y; // center of cell + + int m_data; +} ConcreteCell; + + +// --- ConcreteNet - a design net + +typedef struct ConcreteNet { + int m_id; // a unique ID (see below) + + int m_numTerms; // num. of connected cells + ConcreteCell **m_terms; // connected cells + + float m_weight; // relative weight + + int m_data; +} ConcreteNet; + + +// A note about IDs - the IDs are non-nonegative integers. They need not +// be contiguous, but this is certainly a good idea, as they are stored +// in a non-sparse array. +// Cells and nets have separate ID spaces. + +// -------------------------------------------------------------------- +// Global variable prototypes +// +// -------------------------------------------------------------------- + +// NOTE: None of these need to be managed externally. + +extern int g_place_numCells; // number of cells +extern int g_place_numNets; // number of nets +extern float g_place_rowHeight; // height of placement row +extern Rect g_place_coreBounds; // border of placeable area + // (x,y) = corner +extern Rect g_place_padBounds; // border of total die area + // (x,y) = corner + +extern ConcreteCell **g_place_concreteCells; // all concrete cells +extern ConcreteNet **g_place_concreteNets; // all concrete nets + + +// -------------------------------------------------------------------- +// Function prototypes +// +// -------------------------------------------------------------------- + +void addConcreteNet(ConcreteNet *net); +void addConcreteCell(ConcreteCell *cell); +void delConcreteNet(ConcreteNet *net); +void delConcreteCell(ConcreteCell *cell); + +void globalPreplace(float utilization); +void globalPlace(); +void globalIncremental(); +void globalFixDensity(int numBins, float maxMovement); + +float fastEstimate(ConcreteCell *cell, + int numNets, ConcreteNet *nets[]); +float fastTopoPlace(int numCells, ConcreteCell *cells[], + int numNets, ConcreteNet *nets[]); + +Rect getNetBBox(const ConcreteNet *net); +float getNetWirelength(const ConcreteNet *net); +float getTotalWirelength(); +float getCellArea(const ConcreteCell *cell); + +void writeBookshelf(const char *filename); + +// comparative qsort-style functions +int netSortByL(const void *a, const void *b); +int netSortByR(const void *a, const void *b); +int netSortByB(const void *a, const void *b); +int netSortByT(const void *a, const void *b); +int netSortByID(const void *a, const void *b); +int cellSortByX(const void *a, const void *b); +int cellSortByY(const void *a, const void *b); +int cellSortByID(const void *a, const void *b); + +#endif diff --git a/abc_with_bb_support/src/phys/place/place_bin.c b/abc_with_bb_support/src/phys/place/place_bin.c new file mode 100644 index 000000000..f36e2c0c3 --- /dev/null +++ b/abc_with_bb_support/src/phys/place/place_bin.c @@ -0,0 +1,277 @@ +/*===================================================================*/ +// +// place_bin.c +// +// Aaron P. Hurst, 2007 +// ahurst@eecs.berkeley.edu +// +/*===================================================================*/ + +#include +#include +#include +#include +#include + +//#define DEBUG + +#include "place_base.h" + +// -------------------------------------------------------------------- +// Global variables +// +// -------------------------------------------------------------------- + + +// -------------------------------------------------------------------- +// Function prototypes and local data structures +// +// -------------------------------------------------------------------- + +void spreadDensityX(int numBins, float maxMovement); +void spreadDensityY(int numBins, float maxMovement); + + +// -------------------------------------------------------------------- +// globalFixDensity() +// +/// Doesn't deal well with fixed cells in the core area. +// -------------------------------------------------------------------- +void globalFixDensity(int numBins, float maxMovement) { + + printf("QCLN-10 : \tbin-based density correction\n"); + + spreadDensityX(numBins, maxMovement); + // spreadDensityY(numBins, maxMovement); +} + + +// -------------------------------------------------------------------- +// spreadDensityX() +// +// -------------------------------------------------------------------- +void spreadDensityX(int numBins, float maxMovement) { + + int c, c2, c3, x, y; + float totalArea = 0; + int moveableCells = 0; + float yBinArea = 0, yCumArea = 0; + int yBinStart = 0, yBinCount = 0; + int xBinCount, xBinStart; + float xBinArea, xCumArea; + float lastOldEdge; + float lastNewEdge; + float curOldEdge, curNewEdge; + float stretch, w; + ConcreteCell *xCell, *yCell; + ConcreteCell **binCells; + ConcreteCell **allCells; + + binCells = (ConcreteCell **)malloc(sizeof(ConcreteCell*)*g_place_numCells); + allCells = (ConcreteCell **)malloc(sizeof(ConcreteCell*)*g_place_numCells); + + for(c=0; cm_fixed && !cell->m_parent->m_pad) { + allCells[moveableCells++] = cell; + totalArea += getCellArea(cell); + } + } + + // spread X + qsort(allCells, moveableCells, sizeof(ConcreteCell*), cellSortByY); + + y = 0; + + // for each y-bin... + for(c=0; c= totalArea*(y+1)/numBins && yBinArea > 0) { + memcpy(binCells, &(allCells[yBinStart]), sizeof(ConcreteCell*)*yBinCount); + qsort(binCells, yBinCount, sizeof(ConcreteCell*), cellSortByX); + +#if defined(DEBUG) + printf("y-bin %d count=%d area=%f\n",y,yBinCount, yBinArea); +#endif + + x = 0; + xBinCount = 0, xBinStart = 0; + xBinArea = 0, xCumArea = 0; + lastOldEdge = g_place_coreBounds.x; + lastNewEdge = g_place_coreBounds.x; + + // for each x-bin... + for(c2=0; c2m_x; + + printf("%.3f ", xCell->m_x); + + // have we filled up an x-bin? + if (xCumArea >= yBinArea*(x+1)/numBins && xBinArea > 0) { + curNewEdge = lastNewEdge + g_place_coreBounds.w*xBinArea/yBinArea; + + if (curNewEdge > g_place_coreBounds.x+g_place_coreBounds.w) + curNewEdge = g_place_coreBounds.x+g_place_coreBounds.w; + if ((curNewEdge-curOldEdge)>maxMovement) curNewEdge = curOldEdge + maxMovement; + if ((curOldEdge-curNewEdge)>maxMovement) curNewEdge = curOldEdge - maxMovement; + +#if defined(DEBUG) + printf("->\tx-bin %d count=%d area=%f (%f,%f)->(%f,%f)\n",x, xBinCount, xBinArea, + curOldEdge, lastOldEdge, curNewEdge, lastNewEdge); +#endif + + stretch = (curNewEdge-lastNewEdge)/(curOldEdge-lastOldEdge); + + // stretch! + for(c3=xBinStart; c3m_x = lastNewEdge+(c3-xBinStart)*(curNewEdge-lastNewEdge); + else + binCells[c3]->m_x = lastNewEdge+(binCells[c3]->m_x-lastOldEdge)*stretch; + + // force within core + w = binCells[c3]->m_parent->m_width*0.5; + if (binCells[c3]->m_x-w < g_place_coreBounds.x) + binCells[c3]->m_x = g_place_coreBounds.x+w; + if (binCells[c3]->m_x+w > g_place_coreBounds.x+g_place_coreBounds.w) + binCells[c3]->m_x = g_place_coreBounds.x+g_place_coreBounds.w-w; + } + + lastOldEdge = curOldEdge; + lastNewEdge = curNewEdge; + x++; + xBinCount = 0; + xBinArea = 0; + xBinStart = c2+1; + } + } + + y++; + yBinCount = 0; + yBinArea = 0; + yBinStart = c+1; + } + } + + free(binCells); + free(allCells); +} + + +// -------------------------------------------------------------------- +// spreadDensityY() +// +// -------------------------------------------------------------------- +void spreadDensityY(int numBins, float maxMovement) { + + int c, c2, c3, x, y; + float totalArea = 0; + int moveableCells = 0; + float xBinArea = 0, xCumArea = 0; + int xBinStart = 0, xBinCount = 0; + int yBinCount, yBinStart; + float yBinArea, yCumArea; + float lastOldEdge; + float lastNewEdge; + float curOldEdge, curNewEdge; + float stretch, h; + ConcreteCell *xCell, *yCell; + ConcreteCell **binCells; + ConcreteCell **allCells; + + binCells = (ConcreteCell **)malloc(sizeof(ConcreteCell*)*g_place_numCells); + allCells = (ConcreteCell **)malloc(sizeof(ConcreteCell*)*g_place_numCells); + + for(c=0; cm_fixed && !cell->m_parent->m_pad) { + allCells[moveableCells++] = cell; + totalArea += getCellArea(cell); + } + } + + // spread Y + qsort(allCells, moveableCells, sizeof(ConcreteCell*), cellSortByX); + + x = 0; + + // for each x-bin... + for(c=0; c= totalArea*(x+1)/numBins && xBinArea > 0) { + memcpy(binCells, &(allCells[xBinStart]), sizeof(ConcreteCell*)*xBinCount); + qsort(binCells, xBinCount, sizeof(ConcreteCell*), cellSortByY); + + // printf("x-bin %d count=%d area=%f\n",y,yBinCount, yBinArea); + + y = 0; + yBinCount = 0, yBinStart = 0; + yBinArea = 0, yCumArea = 0; + lastOldEdge = g_place_coreBounds.y; + lastNewEdge = g_place_coreBounds.y; + + // for each y-bin... + for(c2=0; c2m_y; + + // have we filled up an x-bin? + if (yCumArea >= xBinArea*(y+1)/numBins && yBinArea > 0) { + curNewEdge = lastNewEdge + g_place_coreBounds.h*yBinArea/xBinArea; + + if (curNewEdge > g_place_coreBounds.y+g_place_coreBounds.h) + curNewEdge = g_place_coreBounds.y+g_place_coreBounds.h; + if ((curNewEdge-curOldEdge)>maxMovement) curNewEdge = curOldEdge + maxMovement; + if ((curOldEdge-curNewEdge)>maxMovement) curNewEdge = curOldEdge - maxMovement; + + if (curOldEdge == lastOldEdge) continue; // hmmm + stretch = (curNewEdge-lastNewEdge)/(curOldEdge-lastOldEdge); + + // stretch! + for(c3=yBinStart; c3m_y = lastNewEdge+(binCells[c3]->m_y-lastOldEdge)*stretch; + + // force within core + h = binCells[c3]->m_parent->m_height; + if (binCells[c3]->m_y-h < g_place_coreBounds.y) + binCells[c3]->m_y = g_place_coreBounds.y+h; + if (binCells[c3]->m_y+h > g_place_coreBounds.y+g_place_coreBounds.h) + binCells[c3]->m_y = g_place_coreBounds.y+g_place_coreBounds.h-h; + } + + lastOldEdge = curOldEdge; + lastNewEdge = curNewEdge; + y++; + yBinCount = 0; + yBinArea = 0; + yBinStart = c2+1; + } + } + + x++; + xBinCount = 0; + xBinArea = 0; + xBinStart = c+1; + } + } + + free(binCells); + free(allCells); +} diff --git a/abc_with_bb_support/src/phys/place/place_genqp.c b/abc_with_bb_support/src/phys/place/place_genqp.c new file mode 100644 index 000000000..d238e396f --- /dev/null +++ b/abc_with_bb_support/src/phys/place/place_genqp.c @@ -0,0 +1,309 @@ +/*===================================================================*/ +// +// place_genqp.c +// +// Aaron P. Hurst, 2003-2007 +// ahurst@eecs.berkeley.edu +// +/*===================================================================*/ + +#include +#include +#include +#include +#include + +#include "place_base.h" +#include "place_qpsolver.h" +#include "place_gordian.h" + +// -------------------------------------------------------------------- +// Global variables +// +// -------------------------------------------------------------------- + +qps_problem_t *g_place_qpProb = NULL; + + +// -------------------------------------------------------------------- +// splitPenalty() +// +/// \brief Returns a weight for all of the edges in the clique for a multipin net. +// +// -------------------------------------------------------------------- +float splitPenalty(int pins) { + + if (pins > 1) { + return 1.0 + CLIQUE_PENALTY/(pins - 1); + // return pow(pins - 1, CLIQUE_PENALTY); + } + return 1.0 + CLIQUE_PENALTY; +} + + +// -------------------------------------------------------------------- +// constructQuadraticProblem() +// +/// \brief Constructs the matrices necessary to do analytical placement. +// +// -------------------------------------------------------------------- +void constructQuadraticProblem() { + int maxConnections = 1; + int ignoreNum = 0; + int n,t,c,c2,p; + ConcreteCell *cell; + ConcreteNet *net; + int *cell_numTerms = calloc(g_place_numCells, sizeof(int)); + ConcreteNet ***cell_terms = calloc(g_place_numCells, sizeof(ConcreteNet**)); + bool incremental = false; + int nextIndex = 1; + int *seen = calloc(g_place_numCells, sizeof(int)); + float weight; + int last_index; + + // create problem object + if (!g_place_qpProb) { + g_place_qpProb = malloc(sizeof(qps_problem_t)); + g_place_qpProb->area = NULL; + g_place_qpProb->x = NULL; + g_place_qpProb->y = NULL; + g_place_qpProb->fixed = NULL; + g_place_qpProb->connect = NULL; + g_place_qpProb->edge_weight = NULL; + } + + // count the maximum possible number of non-sparse entries + for(n=0; nm_numTerms > IGNORE_NETSIZE) { + ignoreNum++; + } + else { + maxConnections += net->m_numTerms*(net->m_numTerms-1); + for(t=0; tm_numTerms; t++) { + c = net->m_terms[t]->m_id; + p = ++cell_numTerms[c]; + cell_terms[c] = (ConcreteNet**)realloc(cell_terms[c], p*sizeof(ConcreteNet*)); + cell_terms[c][p-1] = net; + } + } + } + if(ignoreNum) { + printf("QMAN-10 : \t\t%d large nets ignored\n", ignoreNum); + } + + // initialize the data structures + g_place_qpProb->num_cells = g_place_numCells; + maxConnections += g_place_numCells + 1; + + g_place_qpProb->area = realloc(g_place_qpProb->area, + sizeof(float)*g_place_numCells);// "area" matrix + g_place_qpProb->edge_weight = realloc(g_place_qpProb->edge_weight, + sizeof(float)*maxConnections); // "weight" matrix + g_place_qpProb->connect = realloc(g_place_qpProb->connect, + sizeof(int)*maxConnections); // "connectivity" matrix + g_place_qpProb->fixed = realloc(g_place_qpProb->fixed, + sizeof(int)*g_place_numCells); // "fixed" matrix + + // initialize or keep preexisting locations + if (g_place_qpProb->x != NULL && g_place_qpProb->y != NULL) { + printf("QMAN-10 :\tperforming incremental placement\n"); + incremental = true; + } + g_place_qpProb->x = (float*)realloc(g_place_qpProb->x, sizeof(float)*g_place_numCells); + g_place_qpProb->y = (float*)realloc(g_place_qpProb->y, sizeof(float)*g_place_numCells); + + // form a row for each cell + // build data + for(c = 0; c < g_place_numCells; c++) if (g_place_concreteCells[c]) { + cell = g_place_concreteCells[c]; + + // fill in the characteristics for this cell + g_place_qpProb->area[c] = getCellArea(cell); + if (cell->m_fixed || cell->m_parent->m_pad) { + g_place_qpProb->x[c] = cell->m_x; + g_place_qpProb->y[c] = cell->m_y; + g_place_qpProb->fixed[c] = 1; + } else { + if (!incremental) { + g_place_qpProb->x[c] = g_place_coreBounds.x+g_place_coreBounds.w*0.5; + g_place_qpProb->y[c] = g_place_coreBounds.y+g_place_coreBounds.h*0.5; + } + g_place_qpProb->fixed[c] = 0; + } + + // update connectivity matrices + last_index = nextIndex; + for(n=0; nm_weight / splitPenalty(net->m_numTerms); + for(t=0; tm_numTerms; t++) { + c2 = net->m_terms[t]->m_id; + if (c2 == c) continue; + if (seen[c2] < last_index) { + // not seen + g_place_qpProb->connect[nextIndex-1] = c2; + g_place_qpProb->edge_weight[nextIndex-1] = weight; + seen[c2] = nextIndex; + nextIndex++; + } else { + // seen + g_place_qpProb->edge_weight[seen[c2]-1] += weight; + } + } + } + g_place_qpProb->connect[nextIndex-1] = -1; + g_place_qpProb->edge_weight[nextIndex-1] = -1.0; + nextIndex++; + } else { + // fill in dummy values for connectivity matrices + g_place_qpProb->connect[nextIndex-1] = -1; + g_place_qpProb->edge_weight[nextIndex-1] = -1.0; + nextIndex++; + } + + free(cell_numTerms); + free(cell_terms); + free(seen); +} + +typedef struct reverseCOG { + float x,y; + Partition *part; + float delta; +} reverseCOG; + + +// -------------------------------------------------------------------- +// generateCoGConstraints() +// +/// \brief Generates center of gravity constraints. +// +// -------------------------------------------------------------------- +int generateCoGConstraints(reverseCOG COG_rev[]) { + int numConstraints = 0; // actual num contraints + int cogRevNum = 0; + Partition **stack = malloc(sizeof(Partition*)*g_place_numPartitions*2); + int stackPtr = 0; + Partition *p; + float cgx, cgy; + int next_index = 0, last_constraint = 0; + bool isTrueConstraint = false; + int i, m; + float totarea; + ConcreteCell *cell; + + // each partition may give rise to a center-of-gravity constraint + stack[stackPtr] = g_place_rootPartition; + while(stackPtr >= 0) { + p = stack[stackPtr--]; + assert(p); + + // traverse down the partition tree to leaf nodes-only + if (!p->m_leaf) { + stack[++stackPtr] = p->m_sub1; + stack[++stackPtr] = p->m_sub2; + } else { + /* + cout << "adding a COG constraint for box " + << p->bounds.x << "," + << p->bounds.y << " of size" + << p->bounds.w << "x" + << p->bounds.h + << endl; + */ + cgx = p->m_bounds.x + p->m_bounds.w*0.5; + cgy = p->m_bounds.y + p->m_bounds.h*0.5; + COG_rev[cogRevNum].x = cgx; + COG_rev[cogRevNum].y = cgy; + COG_rev[cogRevNum].part = p; + COG_rev[cogRevNum].delta = 0; + + cogRevNum++; + } + } + + assert(cogRevNum == g_place_numPartitions); + + for (i = 0; i < g_place_numPartitions; i++) { + p = COG_rev[i].part; + assert(p); + g_place_qpProb->cog_x[numConstraints] = COG_rev[i].x; + g_place_qpProb->cog_y[numConstraints] = COG_rev[i].y; + totarea = 0.0; + for(m=0; mm_numMembers; m++) if (p->m_members[m]) { + cell = p->m_members[m]; + assert(cell); + + if (!cell->m_fixed && !cell->m_parent->m_pad) { + isTrueConstraint = true; + } + else { + continue; + } + g_place_qpProb->cog_list[next_index++] = cell->m_id; + totarea += getCellArea(cell); + } + if (totarea == 0.0) { + isTrueConstraint = false; + } + if (isTrueConstraint) { + numConstraints++; + g_place_qpProb->cog_list[next_index++] = -1; + last_constraint = next_index; + } + else { + next_index = last_constraint; + } + } + + free(stack); + + return --numConstraints; +} + + +// -------------------------------------------------------------------- +// solveQuadraticProblem() +// +/// \brief Calls quadratic solver. +// +// -------------------------------------------------------------------- +void solveQuadraticProblem(bool useCOG) { + int c; + + reverseCOG *COG_rev = malloc(sizeof(reverseCOG)*g_place_numPartitions); + + g_place_qpProb->cog_list = malloc(sizeof(int)*(g_place_numPartitions+g_place_numCells)); + g_place_qpProb->cog_x = malloc(sizeof(float)*g_place_numPartitions); + g_place_qpProb->cog_y = malloc(sizeof(float)*g_place_numPartitions); + + // memset(g_place_qpProb->x, 0, sizeof(float)*g_place_numCells); + // memset(g_place_qpProb->y, 0, sizeof(float)*g_place_numCells); + + qps_init(g_place_qpProb); + + if (useCOG) + g_place_qpProb->cog_num = generateCoGConstraints(COG_rev); + else + g_place_qpProb->cog_num = 0; + + g_place_qpProb->loop_num = 0; + + qps_solve(g_place_qpProb); + + qps_clean(g_place_qpProb); + + // set the positions + for(c = 0; c < g_place_numCells; c++) if (g_place_concreteCells[c]) { + g_place_concreteCells[c]->m_x = g_place_qpProb->x[c]; + g_place_concreteCells[c]->m_y = g_place_qpProb->y[c]; + } + + // clean up + free(g_place_qpProb->cog_list); + free(g_place_qpProb->cog_x); + free(g_place_qpProb->cog_y); + + free(COG_rev); +} diff --git a/abc_with_bb_support/src/phys/place/place_gordian.c b/abc_with_bb_support/src/phys/place/place_gordian.c new file mode 100644 index 000000000..c73bcb261 --- /dev/null +++ b/abc_with_bb_support/src/phys/place/place_gordian.c @@ -0,0 +1,160 @@ +/*===================================================================*/ +// +// place_gordian.c +// +// Aaron P. Hurst, 2003-2007 +// ahurst@eecs.berkeley.edu +// +/*===================================================================*/ + +#include +#include +#include +#include +#include + +#include "place_gordian.h" +#include "place_base.h" + + +// -------------------------------------------------------------------- +// Global variables +// +// -------------------------------------------------------------------- + +int g_place_numPartitions; + + +// -------------------------------------------------------------------- +// globalPlace() +// +/// \brief Performs analytic placement using a GORDIAN-like algorithm. +// +/// Updates the positions of all non-fixed non-pad cells. +/// +// -------------------------------------------------------------------- +void globalPlace() { + bool completionFlag = false; + int iteration = 0; + + printf("PLAC-10 : Global placement (wirelength-driven Gordian)\n"); + + initPartitioning(); + + // build matrices representing interconnections + printf("QMAN-00 : \tconstructing initial quadratic problem...\n"); + constructQuadraticProblem(); + + // iterate placement until termination condition is met + while(!completionFlag) { + printf("QMAN-01 : \titeration %d numPartitions = %d\n",iteration,g_place_numPartitions); + + // do the global optimization in each direction + printf("QMAN-01 : \t\tglobal optimization\n"); + solveQuadraticProblem(!IGNORE_COG); + + // -------- PARTITIONING BASED CELL SPREADING ------ + + // bisection + printf("QMAN-01 : \t\tpartition refinement\n"); + if (REALLOCATE_PARTITIONS) reallocPartitions(); + completionFlag |= refinePartitions(); + + printf("QMAN-01 : \t\twirelength = %e\n", getTotalWirelength()); + + iteration++; + } + + // final global optimization + printf("QMAN-02 : \t\tfinal pass\n"); + if (FINAL_REALLOCATE_PARTITIONS) reallocPartitions(); + solveQuadraticProblem(!IGNORE_COG); + printf("QMAN-01 : \t\twirelength = %e\n", getTotalWirelength()); + + // clean up + sanitizePlacement(); + printf("QMAN-01 : \t\twirelength = %e\n", getTotalWirelength()); + globalFixDensity(25, g_place_rowHeight*5); + printf("QMAN-01 : \t\twirelength = %e\n", getTotalWirelength()); +} + + +// -------------------------------------------------------------------- +// globalIncremental() +// +/// \brief Performs analytic placement using a GORDIAN-like algorithm. +// +/// Requires a valid set of partitions. +/// +// -------------------------------------------------------------------- + +void globalIncremental() { + if (!g_place_rootPartition) { + printf("WARNING: Can not perform incremental placement\n"); + globalPlace(); + return; + } + + printf("PLAC-10 : Incremental global placement\n"); + + incrementalPartition(); + + printf("QMAN-00 : \tconstructing initial quadratic problem...\n"); + constructQuadraticProblem(); + + solveQuadraticProblem(!IGNORE_COG); + printf("QMAN-01 : \t\twirelength = %e\n", getTotalWirelength()); + + // clean up + sanitizePlacement(); + printf("QMAN-01 : \t\twirelength = %e\n", getTotalWirelength()); + globalFixDensity(25, g_place_rowHeight*5); + printf("QMAN-01 : \t\twirelength = %e\n", getTotalWirelength()); +} + + +// -------------------------------------------------------------------- +// sanitizePlacement() +// +/// \brief Moves any cells that are outside of the core bounds to the nearest location within. +// +// -------------------------------------------------------------------- +void sanitizePlacement() { + int c; + float order_width = g_place_rowHeight; + float x, y, edge, w, h; + + printf("QCLN-10 : \tsanitizing placement\n"); + + for(c=0; cm_fixed || cell->m_parent->m_pad) { + continue; + } + // the new locations of the cells will be distributed within + // a small margin inside the border so that ordering is preserved + order_width = g_place_rowHeight; + + x = cell->m_x, y = cell->m_y, + w = cell->m_parent->m_width, h = cell->m_parent->m_height; + + if ((edge=x-w*0.5) < g_place_coreBounds.x) { + x = g_place_coreBounds.x+w*0.5 + + order_width/(1.0+g_place_coreBounds.x-edge); + } + else if ((edge=x+w*0.5) > g_place_coreBounds.x+g_place_coreBounds.w) { + x = g_place_coreBounds.x+g_place_coreBounds.w-w*0.5 - + order_width/(1.0+edge-g_place_coreBounds.x-g_place_coreBounds.w); + } + if ((edge=y-h*0.5) < g_place_coreBounds.y) { + y = g_place_coreBounds.y+h*0.5 + + order_width/(1.0+g_place_coreBounds.y-edge); + } + else if ((edge=y+h*0.5) > g_place_coreBounds.y+g_place_coreBounds.h) { + y = g_place_coreBounds.y+g_place_coreBounds.h-h*0.5 - + order_width/(1.0+edge-g_place_coreBounds.x-g_place_coreBounds.w); + } + cell->m_x = x; + cell->m_y = y; + } +} diff --git a/abc_with_bb_support/src/phys/place/place_gordian.h b/abc_with_bb_support/src/phys/place/place_gordian.h new file mode 100644 index 000000000..d62f5d361 --- /dev/null +++ b/abc_with_bb_support/src/phys/place/place_gordian.h @@ -0,0 +1,78 @@ +/*===================================================================*/ +// +// place_gordian.h +// +// Aaron P. Hurst, 2003-2007 +// ahurst@eecs.berkeley.edu +// +/*===================================================================*/ + +#if !defined(PLACE_GORDIAN_H_) +#define PLACE_GORDIAN_H_ + +#include "place_base.h" +#include "place_qpsolver.h" + +// Parameters for analytic placement +#define CLIQUE_PENALTY 1.0 +#define IGNORE_NETSIZE 20 + +// Parameters for partitioning +#define LARGEST_FINAL_SIZE 20 +#define PARTITION_AREA_ONLY true +#define REALLOCATE_PARTITIONS false +#define FINAL_REALLOCATE_PARTITIONS false +#define IGNORE_COG false +#define MAX_PARTITION_NONSYMMETRY 0.30 + +// Parameters for re-partitioning +#define REPARTITION_LEVEL_DEPTH 4 +#define REPARTITION_TARGET_FRACTION 0.15 +#define REPARTITION_FM false +#define REPARTITION_HMETIS true + +// Parameters for F-M re-partitioning +#define FM_MAX_BIN 10 +#define FM_MAX_PASSES 10 + +extern int g_place_numPartitions; + +extern qps_problem_t *g_place_qpProb; + +typedef struct Partition { + + int m_numMembers; + ConcreteCell **m_members; + Rect m_bounds; + bool m_done, + m_leaf, + m_vertical; + float m_area; + int m_level; + struct Partition *m_sub1, *m_sub2; +} Partition; + +extern Partition *g_place_rootPartition; + +void initPartitioning(); + +void incrementalPartition(); + +bool refinePartitions(); +void reallocPartitions(); +bool refinePartition(Partition *p); +void resizePartition(Partition *p); +void reallocPartition(Partition *p); + +void repartitionHMetis(Partition *parent); +void repartitionFM(Partition *parent); + +void partitionScanlineMincut(Partition *parent); +void partitionEqualArea(Partition *parent); + +void sanitizePlacement(); + +void constructQuadraticProblem(); +void solveQuadraticProblem(bool useCOG); + +#endif diff --git a/abc_with_bb_support/src/phys/place/place_inc.c b/abc_with_bb_support/src/phys/place/place_inc.c new file mode 100644 index 000000000..e7a4a18e5 --- /dev/null +++ b/abc_with_bb_support/src/phys/place/place_inc.c @@ -0,0 +1,106 @@ +/*===================================================================*/ +// +// place_inc.c +// +// Aaron P. Hurst, 2003-2007 +// ahurst@eecs.berkeley.edu +// +/*===================================================================*/ + +#include +#include +#include +#include + +#include "place_base.h" +#include "place_gordian.h" + +inline int sqHashId(int id, int max) { + return ((id * (id+17)) % max); +} + +#if 0 +// -------------------------------------------------------------------- +// fastPlace() +// +/// The first cell is assumed to be the "output". +// -------------------------------------------------------------------- +float fastPlace(int numCells, ConcreteCell *cells[], + int numNets, ConcreteNet *nets[]) { + + int n, t, c, i, local_id = 0, pass; + const int NUM_PASSES = 4; + int *cell_numTerms = calloc(numCells, sizeof(int)); + ConcreteNet **cell_terms; + ConcreteNet *net; + Rect outputBox; + + outputBox = getNetBBox(nets[0]); + + // assign local ids + // put cells in reasonable initial location + for(n=0; nm_numTerms; t++) + nets[n]->m_terms[t]->m_data = -1; + + for(c=0; cm_data = local_id; + cells[c]->m_x = outputBox.x + 0.5*outputBox.w; + cells[c]->m_y = outputBox.y + 0.5*outputBox.h; + } + + // build reverse map of cells to nets + for(n=0; nm_numTerms; t++) { + local_id = nets[n]->m_terms[t]->m_data; + if (local_id >= 0) + cell_numTerms[local_id]++; + } + + for(c=0; cm_numTerms; t++) { + local_id = nets[n]->m_terms[t]->m_data; + if (local_id >= 0) + cell_terms[cell_numTerms[local_id]++] = nets[n]; + } + + // topological order? + + // iterative linear + for(pass=0; passm_numTerms; t++); + } + } +} +#endif + +// -------------------------------------------------------------------- +// fastEstimate() +// +// -------------------------------------------------------------------- +float fastEstimate(ConcreteCell *cell, + int numNets, ConcreteNet *nets[]) { + float len = 0; + int n; + Rect box; + + assert(cell); + + for(n=0; nm_x < box.x) len += (box.x - cell->m_x); + if (cell->m_x > box.x+box.w) len += (cell->m_x-box.x-box.w); + if (cell->m_y < box.y) len += (box.x - cell->m_y); + if (cell->m_y > box.y+box.h) len += (cell->m_y-box.y-box.h); + } + + return len; +} diff --git a/abc_with_bb_support/src/phys/place/place_io.c b/abc_with_bb_support/src/phys/place/place_io.c new file mode 100644 index 000000000..b85f53e45 --- /dev/null +++ b/abc_with_bb_support/src/phys/place/place_io.c @@ -0,0 +1,94 @@ +/*===================================================================*/ +// +// place_io.c +// +// Aaron P. Hurst, 2003-2007 +// ahurst@eecs.berkeley.edu +// +/*===================================================================*/ + +#include +#include +#include +#include +#include + +#include "place_base.h" + + +// -------------------------------------------------------------------- +// writeBookshelfNodes() +// +// -------------------------------------------------------------------- +void writeBookshelfNodes(const char *filename) { + + int c = 0; + int numNodes, numTerms; + + FILE *nodesFile = fopen(filename, "w"); + if (!nodesFile) { + printf("ERROR: Could not open .nodes file\n"); + exit(1); + } + + numNodes = numTerms = 0; + for(c=0; cm_parent->m_pad) + numTerms++; + } + + + + fprintf(nodesFile, "UCLA nodes 1.0\n"); + fprintf(nodesFile, "NumNodes : %d\n", numNodes); + fprintf(nodesFile, "NumTerminals : %d\n", numTerms); + + for(c=0; cm_id, + g_place_concreteCells[c]->m_parent->m_width, + g_place_concreteCells[c]->m_parent->m_height, + (g_place_concreteCells[c]->m_parent->m_pad ? " terminal" : "")); + } + + fclose(nodesFile); +} + + +// -------------------------------------------------------------------- +// writeBookshelfPl() +// +// -------------------------------------------------------------------- +void writeBookshelfPl(const char *filename) { + + int c = 0; + + FILE *plFile = fopen(filename, "w"); + if (!plFile) { + printf("ERROR: Could not open .pl file\n"); + exit(1); + } + + fprintf(plFile, "UCLA pl 1.0\n"); + for(c=0; cm_id, + g_place_concreteCells[c]->m_x, + g_place_concreteCells[c]->m_y, + (g_place_concreteCells[c]->m_fixed ? "\\FIXED" : "")); + } + + fclose(plFile); + +} + + +// -------------------------------------------------------------------- +// writeBookshelf() +// +// -------------------------------------------------------------------- +void writeBookshelf(const char *filename) { + writeBookshelfNodes("out.nodes"); + writeBookshelfPl("out.pl"); +} diff --git a/abc_with_bb_support/src/phys/place/place_legalize.c b/abc_with_bb_support/src/phys/place/place_legalize.c new file mode 100644 index 000000000..02a5398dc --- /dev/null +++ b/abc_with_bb_support/src/phys/place/place_legalize.c @@ -0,0 +1,23 @@ +/*===================================================================*/ +// +// place_legalize.c +// +// Aaron P. Hurst, 2007 +// ahurst@eecs.berkeley.edu +// +/*===================================================================*/ + +#include +#include + +#include "place_base.h" + + +// -------------------------------------------------------------------- +// legalize() +// +// -------------------------------------------------------------------- +void legalize() { + // UNIMPLEMENTED +} + diff --git a/abc_with_bb_support/src/phys/place/place_pads.c b/abc_with_bb_support/src/phys/place/place_pads.c new file mode 100644 index 000000000..ca30d1986 --- /dev/null +++ b/abc_with_bb_support/src/phys/place/place_pads.c @@ -0,0 +1,141 @@ +/*===================================================================*/ +// +// place_pads.c +// +// Aaron P. Hurst, 2003-2007 +// ahurst@eecs.berkeley.edu +// +/*===================================================================*/ + +#include +#include +#include +#include + +#include "place_base.h" + +// -------------------------------------------------------------------- +// globalPreplace() +// +/// \brief Place pad ring, leaving a core area to meet a desired utilization. +// +/// Sets the position of pads that aren't already fixed. +/// +/// Computes g_place_coreBounds and g_place_padBounds. Determines +/// g_place_rowHeight. +// +// -------------------------------------------------------------------- +void globalPreplace(float utilization) { + int i, c, h, numRows; + float coreArea = 0, totalArea = 0; + int padCount = 0; + float area; + ConcreteCell **padCells = NULL; + AbstractCell *padType = NULL; + ConcreteCell *cell; + float nextPos; + int remainingPads, northPads, southPads, eastPads, westPads; + + printf("PLAC-00 : Placing IO pads\n");; + + // identify the pads and compute the total core area + g_place_coreBounds.x = g_place_coreBounds.y = 0; + g_place_coreBounds.w = g_place_coreBounds.h = -INT_MAX; + + for(c=0; cm_parent->m_pad) { + padType = cell->m_parent; + } else { + coreArea += area; + g_place_rowHeight = cell->m_parent->m_height; + } + + if (cell->m_fixed) { + g_place_coreBounds.x = g_place_coreBounds.x < cell->m_x ? g_place_coreBounds.x : cell->m_x; + g_place_coreBounds.y = g_place_coreBounds.y < cell->m_y ? g_place_coreBounds.y : cell->m_y; + g_place_coreBounds.w = g_place_coreBounds.w > cell->m_x ? g_place_coreBounds.w : cell->m_x; + g_place_coreBounds.h = g_place_coreBounds.h > cell->m_y ? g_place_coreBounds.h : cell->m_y; + } else if (cell->m_parent->m_pad) { + padCells = realloc(padCells, sizeof(ConcreteCell **)*(padCount+1)); + padCells[padCount++] = cell; + } + totalArea += area; + } + if (!padType) { + printf("ERROR: No pad cells\n"); + exit(1); + } + g_place_padBounds.w -= g_place_padBounds.x; + g_place_padBounds.h -= g_place_padBounds.y; + + coreArea /= utilization; + + // create the design boundaries + numRows = sqrt(coreArea)/g_place_rowHeight+1; + h = numRows * g_place_rowHeight; + g_place_coreBounds.h = g_place_coreBounds.h > h ? g_place_coreBounds.h : h; + g_place_coreBounds.w = g_place_coreBounds.w > coreArea/g_place_coreBounds.h ? + g_place_coreBounds.w : coreArea/g_place_coreBounds.h; + // increase the dimensions by the width of the padring + g_place_padBounds = g_place_coreBounds; + if (padCount) { + printf("PLAC-05 : \tpreplacing %d pad cells\n", padCount); + g_place_padBounds.x -= padType->m_width; + g_place_padBounds.y -= padType->m_height; + g_place_padBounds.w = g_place_coreBounds.w+2*padType->m_width; + g_place_padBounds.h = g_place_coreBounds.h+2*padType->m_height; + } + + printf("PLAC-05 : \tplaceable rows : %d\n", numRows); + printf("PLAC-05 : \tcore dimensions : %.0fx%.0f\n", + g_place_coreBounds.w, g_place_coreBounds.h); + printf("PLAC-05 : \tchip dimensions : %.0fx%.0f\n", + g_place_padBounds.w, g_place_padBounds.h); + + remainingPads = padCount; + c = 0; + + // north pads + northPads = remainingPads/4; remainingPads -= northPads; + nextPos = 0; + for(i=0; im_x = g_place_padBounds.x+cell->m_parent->m_width*0.5 + nextPos; + cell->m_y = g_place_padBounds.y+cell->m_parent->m_height*0.5; + nextPos += (g_place_padBounds.w-padType->m_width) / northPads; + } + + // south pads + southPads = remainingPads/3; remainingPads -= southPads; + nextPos = 0; + for(i=0; im_x = g_place_padBounds.w+g_place_padBounds.x-cell->m_parent->m_width*0.5 - nextPos; + cell->m_y = g_place_padBounds.h+g_place_padBounds.y-cell->m_parent->m_height*0.5; + nextPos += (g_place_padBounds.w-2*padType->m_width) / southPads; + } + + // east pads + eastPads = remainingPads/2; remainingPads -= eastPads; + nextPos = 0; + for(i=0; im_x = g_place_padBounds.w+g_place_padBounds.x-cell->m_parent->m_width*0.5; + cell->m_y = g_place_padBounds.y+cell->m_parent->m_height*0.5 + nextPos; + nextPos += (g_place_padBounds.h-padType->m_height) / eastPads; + } + + // west pads + westPads = remainingPads; + nextPos = 0; + for(i=0; im_x = g_place_padBounds.x+cell->m_parent->m_width*0.5; + cell->m_y = g_place_padBounds.h+g_place_padBounds.y-cell->m_parent->m_height*0.5 - nextPos; + nextPos += (g_place_padBounds.h-padType->m_height) / westPads; + } + +} + diff --git a/abc_with_bb_support/src/phys/place/place_partition.c b/abc_with_bb_support/src/phys/place/place_partition.c new file mode 100644 index 000000000..52d35eb71 --- /dev/null +++ b/abc_with_bb_support/src/phys/place/place_partition.c @@ -0,0 +1,1135 @@ +/*===================================================================*/ +// +// place_partition.c +// +// Aaron P. Hurst, 2003-2007 +// ahurst@eecs.berkeley.edu +// +/*===================================================================*/ + +#include +#include +#include +#include +#include +#include +//#include +//#include + +#include "place_base.h" +#include "place_gordian.h" + +#if !defined(NO_HMETIS) +#include "libhmetis.h" +#endif + +// -------------------------------------------------------------------- +// Global variables +// +// -------------------------------------------------------------------- + +Partition *g_place_rootPartition = NULL; +ConcreteNet **allNetsR2 = NULL, + **allNetsL2 = NULL, + **allNetsB2 = NULL, + **allNetsT2 = NULL; + + +// -------------------------------------------------------------------- +// Function prototypes and local data structures +// +// -------------------------------------------------------------------- + +typedef struct FM_cell { + int loc; + int gain; + ConcreteCell *cell; + struct FM_cell *next, *prev; + bool locked; +} FM_cell; + +void FM_updateGains(ConcreteNet *net, int partition, int inc, + FM_cell target [], FM_cell *bin [], + int count_1 [], int count_2 []); + + +// -------------------------------------------------------------------- +// initPartitioning() +// +/// \brief Initializes data structures necessary for partitioning. +// +/// Creates a valid g_place_rootPartition. +/// +// -------------------------------------------------------------------- +void initPartitioning() { + int i; + float area; + + // create root partition + g_place_numPartitions = 1; + if (g_place_rootPartition) free(g_place_rootPartition); + g_place_rootPartition = malloc(sizeof(Partition)); + g_place_rootPartition->m_level = 0; + g_place_rootPartition->m_area = 0; + g_place_rootPartition->m_bounds = g_place_coreBounds; + g_place_rootPartition->m_vertical = false; + g_place_rootPartition->m_done = false; + g_place_rootPartition->m_leaf = true; + + // add all of the cells to this partition + g_place_rootPartition->m_members = malloc(sizeof(ConcreteCell*)*g_place_numCells); + g_place_rootPartition->m_numMembers = 0; + for (i=0; im_fixed) { + area = getCellArea(g_place_concreteCells[i]); + g_place_rootPartition->m_members[g_place_rootPartition->m_numMembers++] = + g_place_concreteCells[i]; + g_place_rootPartition->m_area += area; + } + } +} + + +// -------------------------------------------------------------------- +// presortNets() +// +/// \brief Sorts nets by corner positions. +// +/// Allocates allNetsX2 structures. +/// +// -------------------------------------------------------------------- +void presortNets() { + allNetsL2 = (ConcreteNet**)realloc(allNetsL2, sizeof(ConcreteNet*)*g_place_numNets); + allNetsR2 = (ConcreteNet**)realloc(allNetsR2, sizeof(ConcreteNet*)*g_place_numNets); + allNetsB2 = (ConcreteNet**)realloc(allNetsB2, sizeof(ConcreteNet*)*g_place_numNets); + allNetsT2 = (ConcreteNet**)realloc(allNetsT2, sizeof(ConcreteNet*)*g_place_numNets); + memcpy(allNetsL2, g_place_concreteNets, sizeof(ConcreteNet*)*g_place_numNets); + memcpy(allNetsR2, g_place_concreteNets, sizeof(ConcreteNet*)*g_place_numNets); + memcpy(allNetsB2, g_place_concreteNets, sizeof(ConcreteNet*)*g_place_numNets); + memcpy(allNetsT2, g_place_concreteNets, sizeof(ConcreteNet*)*g_place_numNets); + qsort(allNetsL2, g_place_numNets, sizeof(ConcreteNet*), netSortByL); + qsort(allNetsR2, g_place_numNets, sizeof(ConcreteNet*), netSortByR); + qsort(allNetsB2, g_place_numNets, sizeof(ConcreteNet*), netSortByB); + qsort(allNetsT2, g_place_numNets, sizeof(ConcreteNet*), netSortByT); +} + +// -------------------------------------------------------------------- +// refinePartitions() +// +/// \brief Splits large leaf partitions. +// +// -------------------------------------------------------------------- +bool refinePartitions() { + + return refinePartition(g_place_rootPartition); +} + + +// -------------------------------------------------------------------- +// reallocPartitions() +// +/// \brief Reallocates the partitions based on placement information. +// +// -------------------------------------------------------------------- +void reallocPartitions() { + + reallocPartition(g_place_rootPartition); +} + + +// -------------------------------------------------------------------- +// refinePartition() +// +/// \brief Splits any large leaves within a partition. +// +// -------------------------------------------------------------------- +bool refinePartition(Partition *p) { + bool degenerate = false; + int nonzeroCount = 0; + int i; + + assert(p); + + // is this partition completed? + if (p->m_done) return true; + + // is this partition a non-leaf node? + if (!p->m_leaf) { + p->m_done = refinePartition(p->m_sub1); + p->m_done &= refinePartition(p->m_sub2); + return p->m_done; + } + + // leaf... + // create two new subpartitions + g_place_numPartitions++; + p->m_sub1 = malloc(sizeof(Partition)); + p->m_sub1->m_level = p->m_level+1; + p->m_sub1->m_leaf = true; + p->m_sub1->m_done = false; + p->m_sub1->m_area = 0; + p->m_sub1->m_vertical = !p->m_vertical; + p->m_sub1->m_numMembers = 0; + p->m_sub1->m_members = NULL; + p->m_sub2 = malloc(sizeof(Partition)); + p->m_sub2->m_level = p->m_level+1; + p->m_sub2->m_leaf = true; + p->m_sub2->m_done = false; + p->m_sub2->m_area = 0; + p->m_sub2->m_vertical = !p->m_vertical; + p->m_sub2->m_numMembers = 0; + p->m_sub2->m_members = NULL; + p->m_leaf = false; + + // --- INITIAL PARTITION + + if (PARTITION_AREA_ONLY) + partitionEqualArea(p); + else + partitionScanlineMincut(p); + + resizePartition(p); + + // --- PARTITION IMPROVEMENT + + if (p->m_level < REPARTITION_LEVEL_DEPTH) { + if (REPARTITION_FM) + repartitionFM(p); + else if (REPARTITION_HMETIS) + repartitionHMetis(p); + } + + resizePartition(p); + + // fix imbalances due to zero-area cells + for(i=0; im_sub1->m_numMembers; i++) + if (p->m_sub1->m_members[i]) + if (getCellArea(p->m_sub1->m_members[i]) > 0) { + nonzeroCount++; + } + + // is this leaf now done? + if (nonzeroCount <= LARGEST_FINAL_SIZE) + p->m_sub1->m_done = true; + if (nonzeroCount == 0) + degenerate = true; + + nonzeroCount = 0; + for(i=0; im_sub2->m_numMembers; i++) + if (p->m_sub2->m_members[i]) + if (getCellArea(p->m_sub2->m_members[i]) > 0) { + nonzeroCount++; + } + + // is this leaf now done? + if (nonzeroCount <= LARGEST_FINAL_SIZE) + p->m_sub2->m_done = true; + if (nonzeroCount == 0) + degenerate = true; + + // have we found a degenerate partitioning? + if (degenerate) { + printf("QPART-35 : WARNING: degenerate partition generated\n"); + partitionEqualArea(p); + resizePartition(p); + p->m_sub1->m_done = true; + p->m_sub2->m_done = true; + } + + // is this parent now finished? + if (p->m_sub1->m_done && p->m_sub2->m_done) p->m_done = true; + + return p->m_done; +} + + +// -------------------------------------------------------------------- +// repartitionHMetis() +// +/// \brief Repartitions the two subpartitions using the hMetis min-cut library. +/// +/// The number of cut nets between the two partitions will be minimized. +// +// -------------------------------------------------------------------- +void repartitionHMetis(Partition *parent) { +#if defined(NO_HMETIS) + printf("QPAR_02 : \t\tERROR: hMetis not available. Ignoring.\n"); +#else + + int n,c,t, i; + float area; + int *edgeConnections = NULL; + int *partitionAssignment = (int *)calloc(g_place_numCells, sizeof(int)); + int *vertexWeights = (int *)calloc(g_place_numCells, sizeof(int)); + int *edgeDegree = (int *)malloc(sizeof(int)*(g_place_numNets+1)); + int numConnections = 0; + int numEdges = 0; + float initial_cut; + int targets = 0; + ConcreteCell *cell = NULL; + int options[9]; + int afterCuts = 0; + + assert(parent); + assert(parent->m_sub1); + assert(parent->m_sub2); + + printf("QPAR-02 : \t\trepartitioning with hMetis\n"); + + // count edges + edgeDegree[0] = 0; + for(n=0; nm_numTerms > 1) { + numConnections += g_place_concreteNets[n]->m_numTerms; + edgeDegree[++numEdges] = numConnections; + } + + if (parent->m_vertical) { + // vertical + initial_cut = parent->m_sub2->m_bounds.x; + + // initialize all cells + for(c=0; cm_x < initial_cut) + partitionAssignment[c] = 0; + else + partitionAssignment[c] = 1; + } + + // initialize cells in partition 1 + for(t=0; tm_sub1->m_numMembers; t++) if (parent->m_sub1->m_members[t]) { + cell = parent->m_sub1->m_members[t]; + vertexWeights[cell->m_id] = getCellArea(cell); + // pay attention to cells that are close to the cut + if (abs(cell->m_x-initial_cut) < parent->m_bounds.w*REPARTITION_TARGET_FRACTION) { + targets++; + partitionAssignment[cell->m_id] = -1; + } + } + + // initialize cells in partition 2 + for(t=0; tm_sub2->m_numMembers; t++) if (parent->m_sub2->m_members[t]) { + cell = parent->m_sub2->m_members[t]; + vertexWeights[cell->m_id] = getCellArea(cell); + // pay attention to cells that are close to the cut + if (abs(cell->m_x-initial_cut) < parent->m_bounds.w*REPARTITION_TARGET_FRACTION) { + targets++; + partitionAssignment[cell->m_id] = -1; + } + } + + } else { + // horizontal + initial_cut = parent->m_sub2->m_bounds.y; + + // initialize all cells + for(c=0; cm_y < initial_cut) + partitionAssignment[c] = 0; + else + partitionAssignment[c] = 1; + } + + // initialize cells in partition 1 + for(t=0; tm_sub1->m_numMembers; t++) if (parent->m_sub1->m_members[t]) { + cell = parent->m_sub1->m_members[t]; + vertexWeights[cell->m_id] = getCellArea(cell); + // pay attention to cells that are close to the cut + if (abs(cell->m_y-initial_cut) < parent->m_bounds.h*REPARTITION_TARGET_FRACTION) { + targets++; + partitionAssignment[cell->m_id] = -1; + } + } + + // initialize cells in partition 2 + for(t=0; tm_sub2->m_numMembers; t++) if (parent->m_sub2->m_members[t]) { + cell = parent->m_sub2->m_members[t]; + vertexWeights[cell->m_id] = getCellArea(cell); + // pay attention to cells that are close to the cut + if (abs(cell->m_y-initial_cut) < parent->m_bounds.h*REPARTITION_TARGET_FRACTION) { + targets++; + partitionAssignment[cell->m_id] = -1; + } + } + } + + options[0] = 1; // any non-default values? + options[1] = 3; // num bisections + options[2] = 1; // grouping scheme + options[3] = 1; // refinement scheme + options[4] = 1; // cycle refinement scheme + options[5] = 0; // reconstruction scheme + options[6] = 0; // fixed assignments? + options[7] = 12261980; // random seed + options[8] = 0; // debugging level + + edgeConnections = (int *)malloc(sizeof(int)*numConnections); + + i = 0; + for(n=0; nm_numTerms > 1) + for(t=0; tm_numTerms; t++) + edgeConnections[i++] = g_place_concreteNets[n]->m_terms[t]->m_id; + } + + HMETIS_PartRecursive(g_place_numCells, numEdges, vertexWeights, + edgeDegree, edgeConnections, NULL, + 2, (int)(100*MAX_PARTITION_NONSYMMETRY), + options, partitionAssignment, &afterCuts); + + /* + printf("HMET-20 : \t\t\tbalance before %d / %d ... ", parent->m_sub1->m_numMembers, + parent->m_sub2->m_numMembers); + */ + + // reassign members to subpartitions + parent->m_sub1->m_numMembers = 0; + parent->m_sub1->m_area = 0; + parent->m_sub2->m_numMembers = 0; + parent->m_sub2->m_area = 0; + parent->m_sub1->m_members = (ConcreteCell**)realloc(parent->m_sub1->m_members, + sizeof(ConcreteCell*)*parent->m_numMembers); + parent->m_sub2->m_members = (ConcreteCell**)realloc(parent->m_sub2->m_members, + sizeof(ConcreteCell*)*parent->m_numMembers); + + for(t=0; tm_numMembers; t++) if (parent->m_members[t]) { + cell = parent->m_members[t]; + area = getCellArea(cell); + if (partitionAssignment[cell->m_id] == 0) { + parent->m_sub1->m_members[parent->m_sub1->m_numMembers++] = cell; + parent->m_sub1->m_area += area; + } + else { + parent->m_sub2->m_members[parent->m_sub2->m_numMembers++] = cell; + parent->m_sub2->m_area += area; + } + } + /* + printf("after %d / %d\n", parent->m_sub1->m_numMembers, + parent->m_sub2->m_numMembers); + */ + + // cout << "HMET-21 : \t\t\tloc: " << initial_cut << " targetting: " << targets*100/parent->m_members.length() << "%" << endl; + // cout << "HMET-22 : \t\t\tstarting cuts= " << beforeCuts << " final cuts= " << afterCuts << endl; + + free(edgeConnections); + free(vertexWeights); + free(edgeDegree); + free(partitionAssignment); +#endif +} + + +// -------------------------------------------------------------------- +// repartitionFM() +// +/// \brief Fiduccia-Matheyses mincut partitioning algorithm. +// +/// UNIMPLEMENTED (well, un-C-ified) +// +// -------------------------------------------------------------------- +void repartitionFM(Partition *parent) { +#if 0 + assert(!parent->leaf && parent->m_sub1->leaf && parent->m_sub2->leaf); + + // count of each net's number of cells in each bipartition + int count_1[m_design->nets.length()]; + memset(count_1, 0, sizeof(int)*m_design->nets.length()); + int count_2[m_design->nets.length()]; + memset(count_2, 0, sizeof(int)*m_design->nets.length()); + + FM_cell target[m_design->cells.length()]; + memset(target, 0, sizeof(FM_cell)*m_design->cells.length()); + FM_cell *bin[FM_MAX_BIN+1]; + FM_cell *locked = 0; + memset(bin, 0, sizeof(FM_cell *)*(FM_MAX_BIN+1)); + + int max_gain = 0; + int before_cuts = 0, current_cuts = 0; + double initial_cut; + int targets = 0; + long cell_id; + double halfArea = parent->m_area / 2.0; + double areaFlexibility = parent->m_area * MAX_PARTITION_NONSYMMETRY; + ConcreteNet *net; + + // INITIALIZATION + // select cells to partition + + if (parent->vertical) { + // vertical + + initial_cut = parent->m_sub2->m_bounds.x; + + // initialize all cells + for(h::list::iterator it = rootPartition->m_members.begin(); !it; it++) { + cell_id = (*it)->getID(); + if ((*it)->temp_x < initial_cut) + target[cell_id].loc = -1; + else + target[cell_id].loc = -2; + target[cell_id].cell = *it; + target[cell_id].gain = 0; + } + + // initialize cells in partition 1 + for(h::list::iterator it = parent->m_sub1->m_members.begin(); !it; it++) { + cell_id = (*it)->getID(); + // pay attention to cells that are close to the cut + if (abs((*it)->temp_x-initial_cut) < parent->m_bounds.w*REPARTITION_TARGET_FRACTION) { + targets++; + target[cell_id].loc = 1; + } + } + + // initialize cells in partition 2 + for(h::list::iterator it = parent->m_sub2->m_members.begin(); !it; it++) { + cell_id = (*it)->getID(); + // pay attention to cells that are close to the cut + if (abs((*it)->temp_x-initial_cut) < parent->m_bounds.w*REPARTITION_TARGET_FRACTION) { + targets++; + target[cell_id].loc = 2; + } + } + + // count the number of cells on each side of the partition for every net + for(h::hash_map::iterator n_it = m_design->nets.begin(); !n_it; n_it++) { + for(ConcretePinList::iterator p_it = (net = *n_it)->getPins().begin(); !p_it; p_it++) + if (abs(target[(*p_it)->getCell()->getID()].loc) == 1) + count_1[net->getID()]++; + else if (abs(target[(*p_it)->getCell()->getID()].loc) == 2) + count_2[net->getID()]++; + else if ((*p_it)->getCell()->temp_x < initial_cut) + count_1[net->getID()]++; + else + count_2[net->getID()]++; + if (count_1[net->getID()] > 0 && count_2[net->getID()] > 0) before_cuts++; + } + + } else { + // horizontal + + initial_cut = parent->m_sub2->m_bounds.y; + + // initialize all cells + for(h::list::iterator it = rootPartition->m_members.begin(); !it; it++) { + cell_id = (*it)->getID(); + if ((*it)->temp_y < initial_cut) + target[cell_id].loc = -1; + else + target[cell_id].loc = -2; + target[cell_id].cell = *it; + target[cell_id].gain = 0; + } + + // initialize cells in partition 1 + for(h::list::iterator it = parent->m_sub1->m_members.begin(); !it; it++) { + cell_id = (*it)->getID(); + // pay attention to cells that are close to the cut + if (abs((*it)->temp_y-initial_cut) < parent->m_bounds.h*REPARTITION_TARGET_FRACTION) { + targets++; + target[cell_id].loc = 1; + } + } + + // initialize cells in partition 2 + for(h::list::iterator it = parent->m_sub2->m_members.begin(); !it; it++) { + cell_id = (*it)->getID(); + // pay attention to cells that are close to the cut + if (abs((*it)->temp_y-initial_cut) < parent->m_bounds.h*REPARTITION_TARGET_FRACTION) { + targets++; + target[cell_id].loc = 2; + } + } + + // count the number of cells on each side of the partition for every net + for(h::hash_map::iterator n_it = m_design->nets.begin(); !n_it; n_it++) { + for(ConcretePinList::iterator p_it = (net = *n_it)->getPins().begin(); !p_it; p_it++) + if (abs(target[(*p_it)->getCell()->getID()].loc) == 1) + count_1[net->getID()]++; + else if (abs(target[(*p_it)->getCell()->getID()].loc) == 2) + count_2[net->getID()]++; + else if ((*p_it)->getCell()->temp_y < initial_cut) + count_1[net->getID()]++; + else + count_2[net->getID()]++; + if (count_1[net->getID()] > 0 && count_2[net->getID()] > 0) before_cuts++; + } + } + + // INITIAL GAIN CALCULATION + for(long id=0; id < m_design->cells.length(); id++) + if (target[id].loc > 0) { + assert(target[id].cell != 0); + assert(target[id].gain == 0); + + // examine counts for the net on each pin + for(ConcretePinMap::iterator p_it = target[id].cell->getPins().begin(); !p_it; p_it++) + if ((*p_it)->isAttached()) { + int n_id = (*p_it)->getNet()->getID(); + if (target[id].loc == 1 && count_1[n_id] == 1) target[id].gain++; + if (target[id].loc == 1 && count_2[n_id] == 0) target[id].gain--; + if (target[id].loc == 2 && count_1[n_id] == 0) target[id].gain--; + if (target[id].loc == 2 && count_2[n_id] == 1) target[id].gain++; + } + + assert(target[id].cell->getPins().length() >= abs(target[id].gain)); + + // add it to a bin + int bin_num = min(max(0, target[id].gain),FM_MAX_BIN); + max_gain = max(max_gain, bin_num); + + assert(bin_num >= 0 && bin_num <= FM_MAX_BIN); + target[id].next = bin[bin_num]; + target[id].prev = 0; + if (bin[bin_num] != 0) + bin[bin_num]->prev = &target[id]; + bin[bin_num] = &target[id]; + } + + // OUTER F-M LOOP + current_cuts = before_cuts; + int num_moves = 1; + int pass = 0; + while(num_moves > 0 && pass < FM_MAX_PASSES) { + pass++; + num_moves = 0; + + // check_list(bin, locked, targets); // DEBUG + + // move all locked cells back + int moved_back = 0; + while(locked != 0) { + FM_cell *current = locked; + current->locked = false; + + int bin_num = min(max(0, current->gain),FM_MAX_BIN); + max_gain = max(max_gain, bin_num); + + locked = current->next; + if (locked != 0) + locked->prev = 0; + + if (bin[bin_num] != 0) + bin[bin_num]->prev = current; + current->next = bin[bin_num]; + bin[bin_num] = current; + + moved_back++; + } + // cout << "\tmoved back: " << moved_back << endl; + // check_list(bin, locked, targets); // DEBUG + + max_gain = FM_MAX_BIN; + while(bin[max_gain] == 0 && max_gain > 0) max_gain--; + + // INNER F-M LOOP (single pass) + while(1) { + + int bin_num = FM_MAX_BIN; + FM_cell *current = bin[bin_num]; + + // look for next cell to move + while (bin_num > 0 && (current == 0 || + (current->loc==1 && current->cell->getArea()+parent->m_sub2->m_area > halfArea+areaFlexibility) || + (current->loc==2 && current->cell->getArea()+parent->m_sub1->m_area > halfArea+areaFlexibility))) { + + if (current == 0) current = bin[--bin_num]; else current = current->next; + } + if (bin_num == 0) + break; + + num_moves++; + current->locked = true; + // cout << "moving cell " << current->cell->getID() << " gain=" << current->gain << " pins= " << current->cell->getPins().length() << " from " << current->loc; + + // change partition marking and areas + if (current->loc == 1) { + current->loc = 2; + parent->m_sub1->m_area -= current->cell->getArea(); + parent->m_sub2->m_area += current->cell->getArea(); + + // update partition counts on all nets attached to this cell + for(ConcretePinMap::iterator p_it = current->cell->getPins().begin(); + !p_it; p_it++) { + + if (!(*p_it)->isAttached()) // ignore unattached pins + continue; + net = (*p_it)->getNet(); + + count_1[net->getID()]--; + count_2[net->getID()]++; + + // cout << "\tnet " << net->getID() << " was " << count_1[net->getID()]+1 << "/" << count_2[net->getID()]-1 << " now " << count_1[net->getID()] << "/" << count_2[net->getID()] << endl; + + // if net becomes critical, update gains on attached cells and resort bins + if (count_1[net->getID()] == 0) { current_cuts--; FM_updateGains(net, 2, -1, target, bin, count_1, count_2); } + if (count_2[net->getID()] == 1) { current_cuts++; FM_updateGains(net, 1, -1, target, bin, count_1, count_2); } + + // check_list(bin, locked, targets); // DEBUG + } + + } else { + current->loc = 1; + parent->m_sub2->m_area -= current->cell->getArea(); + parent->m_sub1->m_area += current->cell->getArea(); + + // update gains on all nets attached to this cell + for(ConcretePinMap::iterator p_it = current->cell->getPins().begin(); + !p_it; p_it++) { + + if (!(*p_it)->isAttached()) // ignore unattached pins + continue; + net = (*p_it)->getNet(); + count_2[net->getID()]--; + count_1[net->getID()]++; + + // cout << "\tnet " << net->getID() << " was " << count_1[net->getID()]-1 << "/" << count_2[net->getID()]+1 << " now " << count_1[net->getID()] << "/" << count_2[net->getID()] << endl; + + if (count_2[net->getID()] == 0) { current_cuts--; FM_updateGains(net, 2, -1, target, bin, count_1, count_2); } + if (count_1[net->getID()] == 1) { current_cuts++; FM_updateGains(net, 1, -1, target, bin, count_1, count_2); } + + // check_list(bin, locked, targets); // DEBUG + } + } + + //cout << " cuts=" << current_cuts << endl; + + // move current to locked + +/* + cout << "b=" << bin[bin_num] << " "; + cout << current->prev << "-> "; + if (current->prev == 0) + cout << "X"; + else cout << current->prev->next; + cout << "=" << current << "="; + if (current->next == 0) + cout << "X"; + else + cout << current->next->prev; + cout << " ->" << current->next << endl; +*/ + + if (bin[bin_num] == current) + bin[bin_num] = current->next; + if (current->prev != 0) + current->prev->next = current->next; + if (current->next != 0) + current->next->prev = current->prev; + +/* + cout << "b=" << bin[bin_num] << " "; + cout << current->prev << "-> "; + if (current->prev == 0) + cout << "X"; + else cout << current->prev->next; + cout << "=" << current << "="; + if (current->next == 0) + cout << "X"; + else + cout << current->next->prev; + cout << " ->" << current->next << endl; +*/ + + current->prev = 0; + current->next = locked; + if (locked != 0) + locked->prev = current; + locked = current; + + // check_list(bin, locked, targets); // DEBUG + + // update max_gain + max_gain = FM_MAX_BIN; + while(bin[max_gain] == 0 && max_gain > 0) max_gain--; + } + + // cout << "\tcurrent cuts= " << current_cuts << " moves= " << num_moves << endl; + } + + // reassign members to subpartitions + cout << "FIDM-20 : \tbalance before " << parent->m_sub1->m_members.length() << "/" + << parent->m_sub2->m_members.length() << " "; + parent->m_sub1->m_members.clear(); + parent->m_sub1->m_area = 0; + parent->m_sub2->m_members.clear(); + parent->m_sub2->m_area = 0; + for(h::list::iterator it = parent->m_members.begin(); !it; it++) { + if (target[(*it)->getID()].loc == 1 || target[(*it)->getID()].loc == -1) { + parent->m_sub1->m_members.push_back(*it); + parent->m_sub1->m_area += (*it)->getArea(); + } + else { + parent->m_sub2->m_members.push_back(*it); + parent->m_sub2->m_area += (*it)->getArea(); + } + } + cout << " after " << parent->m_sub1->m_members.length() << "/" + << parent->m_sub2->m_members.length() << endl; + + + cout << "FIDM-21 : \tloc: " << initial_cut << " targetting: " << targets*100/parent->m_members.length() << "%" << endl; + cout << "FIDM-22 : \tstarting cuts= " << before_cuts << " final cuts= " << current_cuts << endl; +#endif +} + +// ----- FM_updateGains() +// moves a cell between bins +#if 0 +void FM_updateGains(ConcreteNet *net, int partition, int inc, + FM_cell target [], FM_cell *bin [], + int count_1 [], int count_2 []) { + + for(ConcretePinList::iterator it = net->getPins().begin(); !it; it++) { + FM_cell *current = &(target[(*it)->getCell()->getID()]); + assert(current->cell != 0); + + int old_gain = current->gain; + current->gain = 0; + + // examine counts for the net on each pin + for(ConcretePinMap::iterator p_it = current->cell->getPins().begin(); !p_it; p_it++) + if ((*p_it)->isAttached()) { + int n_id = (*p_it)->getNet()->getID(); + if (current->loc == 1 && count_1[n_id] == 1) current->gain++; + if (current->loc == 1 && count_2[n_id] == 0) current->gain--; + if (current->loc == 2 && count_1[n_id] == 0) current->gain--; + if (current->loc == 2 && count_2[n_id] == 1) current->gain++; + } + + if (!current->locked) { + // remove cell from existing bin + int bin_num = min(max(0, old_gain),FM_MAX_BIN); + if (bin[bin_num] == current) + bin[bin_num] = current->next; + if (current->prev != 0) + current->prev->next = current->next; + if (current->next != 0) + current->next->prev = current->prev; + // add cell to correct bin + bin_num = min(max(0, current->gain),FM_MAX_BIN); + current->prev = 0; + current->next = bin[bin_num]; + if (bin[bin_num] != 0) + bin[bin_num]->prev = current; + bin[bin_num] = current; + } + } + +} +#endif + + +// -------------------------------------------------------------------- +// partitionEqualArea() +// +/// \brief Splits a partition into two halves of equal area. +// +// -------------------------------------------------------------------- +void partitionEqualArea(Partition *parent) { + float halfArea, area; + int i=0; + + // which way to sort? + if (parent->m_vertical) + // sort by X position + qsort(parent->m_members, parent->m_numMembers, sizeof(ConcreteCell*), cellSortByX); + else + // sort by Y position + qsort(parent->m_members, parent->m_numMembers, sizeof(ConcreteCell*), cellSortByY); + + // split the list + halfArea = parent->m_area*0.5; + parent->m_sub1->m_area = 0.0; + parent->m_sub1->m_numMembers = 0; + parent->m_sub1->m_members = (ConcreteCell**)realloc(parent->m_sub1->m_members, + sizeof(ConcreteCell*)*parent->m_numMembers); + parent->m_sub2->m_area = 0.0; + parent->m_sub2->m_numMembers = 0; + parent->m_sub2->m_members = (ConcreteCell**)realloc(parent->m_sub2->m_members, + sizeof(ConcreteCell*)*parent->m_numMembers); + + for(; parent->m_sub1->m_area < halfArea; i++) + if (parent->m_members[i]) { + area = getCellArea(parent->m_members[i]); + parent->m_sub1->m_members[parent->m_sub1->m_numMembers++] = parent->m_members[i]; + parent->m_sub1->m_area += area; + } + for(; im_numMembers; i++) + if (parent->m_members[i]) { + area = getCellArea(parent->m_members[i]); + parent->m_sub2->m_members[parent->m_sub2->m_numMembers++] = parent->m_members[i]; + parent->m_sub2->m_area += area; + } + +} + + +// -------------------------------------------------------------------- +// partitionScanlineMincut() +// +/// \brief Scans the cells within a partition from left to right and chooses the min-cut. +// +// -------------------------------------------------------------------- +void partitionScanlineMincut(Partition *parent) { +#if 0 + int current_cuts = 0; + int minimum_cuts = INT_MAX; + ConcreteCell *minimum_location = NULL; + double currentArea = 0, halfArea = parent->m_area * 0.5; + double areaFlexibility = parent->m_area * MAX_PARTITION_NONSYMMETRY; + double newLine, oldLine = -DBL_MAX; + + for(ConcreteNetList::iterator n_it = m_design->nets.begin(); !n_it; n_it++) + (*n_it)->m_mark = 0; + for(h::list::iterator i = parent->m_members.begin(); + !i.isDone(); i++) { + assert(*i); + for(ConcretePinMap::iterator j = (*i)->getPins().begin(); + !j.isDone(); j++) { + assert(*j); + if((*j)->isAttached()) { + (*j)->getNet()->m_mark = 1; + } + } + } + + if (parent->vertical) { + parent->m_members.sort(sortByX); + int all1 = 0, all2 = 0; + h::list::iterator local = parent->m_members.begin(); + for(; !local.isDone(); local++) { + currentArea += (*local)->getArea(); + if (currentArea < halfArea-areaFlexibility) + continue; + if (currentArea > halfArea+areaFlexibility) + break; + newLine = (*local)->temp_x; + while(all1 < g_place_numNets && allNetsL2[all1]->getBoundingBox().left() <= newLine) { + if(allNetsL2[all1]->m_mark) { + current_cuts++; + } + all1++; + } + while(all2 < g_place_numNets && allNetsR2[all2]->getBoundingBox().right() <= newLine) { + if(allNetsR2[all2]->m_mark) { + current_cuts--; + } + all2++; + } + if (current_cuts < minimum_cuts) { + minimum_cuts = current_cuts; + minimum_location = *local; + } + oldLine = newLine; + } + } + else { + parent->m_members.sort(sortByY); + int all1 = 0, all2 = 0; + h::list::iterator local = parent->m_members.begin(); + for(; !local.isDone(); local++) { + currentArea += (*local)->getArea(); + if (currentArea < halfArea-areaFlexibility) + continue; + if (currentArea > halfArea+areaFlexibility) + break; + newLine = (*local)->temp_y; + while(all1 < g_place_numNets && allNetsB2[all1]->getBoundingBox().top() <= newLine) { + if(allNetsB2[all1]->m_mark) { + current_cuts++; + } + all1++; + } + while(all2 < g_place_numNets && allNetsT2[all2]->getBoundingBox().bottom() <= newLine) { + if(allNetsT2[all2]->m_mark) { + current_cuts--; + } + all2++; + } + if (current_cuts < minimum_cuts) { + minimum_cuts = current_cuts; + minimum_location = *local; + } + oldLine = newLine; + } + } + if (minimum_location == NULL) { + return partitionEqualArea(parent); + } + h::list::iterator it = parent->m_members.begin(); + parent->m_sub1->m_members.clear(); + parent->m_sub1->m_area = 0; + for(; *it != minimum_location; it++) { + parent->m_sub1->m_members.push_front(*it); + parent->m_sub1->m_area += (*it)->getArea(); + } + parent->m_sub2->m_members.clear(); + parent->m_sub2->m_area = 0; + for(; !it; it++) { + parent->m_sub2->m_members.push_front(*it); + parent->m_sub2->m_area += (*it)->getArea(); + } +#endif +} + + +// -------------------------------------------------------------------- +// reallocPartition() +// +/// \brief Reallocates a partition and all of its children. +// +// -------------------------------------------------------------------- +void reallocPartition(Partition *p) { + + if (p->m_leaf) { + return; + } + + // --- INITIAL PARTITION + + if (PARTITION_AREA_ONLY) + partitionEqualArea(p); + else + partitionScanlineMincut(p); + + resizePartition(p); + + // --- PARTITION IMPROVEMENT + if (p->m_level < REPARTITION_LEVEL_DEPTH) { + if (REPARTITION_HMETIS) + repartitionHMetis(p); + + resizePartition(p); + } + + reallocPartition(p->m_sub1); + reallocPartition(p->m_sub2); +} + + +// -------------------------------------------------------------------- +// resizePartition() +// +/// \brief Recomputes the bounding boxes of the child partitions based on their relative areas. +// +// -------------------------------------------------------------------- +void resizePartition(Partition *p) { + // compute the new bounding box + p->m_sub1->m_bounds.x = p->m_bounds.x; + p->m_sub1->m_bounds.y = p->m_bounds.y; + if (p->m_vertical) { + p->m_sub1->m_bounds.w = p->m_bounds.w*(p->m_sub1->m_area/p->m_area); + p->m_sub1->m_bounds.h = p->m_bounds.h; + p->m_sub2->m_bounds.x = p->m_bounds.x + p->m_sub1->m_bounds.w; + p->m_sub2->m_bounds.w = p->m_bounds.w*(p->m_sub2->m_area/p->m_area); + p->m_sub2->m_bounds.y = p->m_bounds.y; + p->m_sub2->m_bounds.h = p->m_bounds.h; + } else { + p->m_sub1->m_bounds.h = p->m_bounds.h*(p->m_sub1->m_area/p->m_area); + p->m_sub1->m_bounds.w = p->m_bounds.w; + p->m_sub2->m_bounds.y = p->m_bounds.y + p->m_sub1->m_bounds.h; + p->m_sub2->m_bounds.h = p->m_bounds.h*(p->m_sub2->m_area/p->m_area); + p->m_sub2->m_bounds.x = p->m_bounds.x; + p->m_sub2->m_bounds.w = p->m_bounds.w; + } +} + + +// -------------------------------------------------------------------- +// incrementalSubpartition() +// +/// \brief Adds new cells to an existing partition. Partition sizes/locations are unchanged. +/// +/// The function recurses, adding new cells to appropriate subpartitions. +// +// -------------------------------------------------------------------- +void incrementalSubpartition(Partition *p, ConcreteCell *newCells [], const int numNewCells) { + int c; + ConcreteCell **newCells1 = (ConcreteCell **)malloc(sizeof(ConcreteCell*)*numNewCells), + **newCells2 = (ConcreteCell **)malloc(sizeof(ConcreteCell*)*numNewCells); + int numNewCells1 = 0, numNewCells2 = 0; + float cut_loc; + + assert(p); + + // add new cells to partition list + p->m_members = (ConcreteCell**)realloc(p->m_members, + sizeof(ConcreteCell*)*(p->m_numMembers+numNewCells)); + memcpy(&(p->m_members[p->m_numMembers]), newCells, sizeof(ConcreteCell*)*numNewCells); + p->m_numMembers += numNewCells; + + // if is a leaf partition, finished + if (p->m_leaf) return; + + // split new cells into sub-partitions based on location + if (p->m_vertical) { + cut_loc = p->m_sub2->m_bounds.x; + for(c=0; cm_x < cut_loc) + newCells1[numNewCells1++] = newCells[c]; + else + newCells2[numNewCells2++] = newCells[c]; + } else { + cut_loc = p->m_sub2->m_bounds.y; + for(c=0; cm_y < cut_loc) + newCells1[numNewCells1++] = newCells[c]; + else + newCells2[numNewCells2++] = newCells[c]; + } + + if (numNewCells1 > 0) incrementalSubpartition(p->m_sub1, newCells1, numNewCells1); + if (numNewCells2 > 0) incrementalSubpartition(p->m_sub2, newCells2, numNewCells2); + + free(newCells1); + free(newCells2); +} + + +// -------------------------------------------------------------------- +// incrementalPartition() +// +/// \brief Adds new cells to an existing partition. Partition sizes/locations are unchanged. +/// +/// The function recurses, adding new cells to appropriate subpartitions. +// +// -------------------------------------------------------------------- +void incrementalPartition() { + int c = 0, c2 = 0; + int numNewCells = 0; + ConcreteCell **allCells = (ConcreteCell **)malloc(sizeof(ConcreteCell*)*g_place_numCells), + **newCells = (ConcreteCell **)malloc(sizeof(ConcreteCell*)*g_place_numCells); + + assert(g_place_rootPartition); + + // update cell list of root partition + memcpy(allCells, g_place_concreteCells, sizeof(ConcreteCell*)*g_place_numCells); + qsort(allCells, g_place_numCells, sizeof(ConcreteCell*), cellSortByID); + qsort(g_place_rootPartition->m_members, g_place_rootPartition->m_numMembers, + sizeof(ConcreteCell*), cellSortByID); + + // scan sorted lists and collect cells not in partitions + while(!allCells[c++]); + while(!g_place_rootPartition->m_members[c2++]); + + for(; cm_numMembers && + allCells[c]->m_id > g_place_rootPartition->m_members[c2]->m_id) c2++; + while(c < g_place_numCells && + (c2 >= g_place_rootPartition->m_numMembers || + allCells[c]->m_id < g_place_rootPartition->m_members[c2]->m_id)) { + // a new cell! + newCells[numNewCells++] = allCells[c]; + c++; + } + } + + printf("QPRT-50 : \tincremental partitioning with %d new cells\n", numNewCells); + if (numNewCells>0) incrementalSubpartition(g_place_rootPartition, newCells, numNewCells); + + free(allCells); + free(newCells); +} diff --git a/abc_with_bb_support/src/phys/place/place_qpsolver.c b/abc_with_bb_support/src/phys/place/place_qpsolver.c new file mode 100644 index 000000000..4dd1a4309 --- /dev/null +++ b/abc_with_bb_support/src/phys/place/place_qpsolver.c @@ -0,0 +1,1270 @@ +/*===================================================================*/ +// +// place_qpsolver.c +// +// Philip Chong +// pchong@cadence.com +// +/*===================================================================*/ + +#include +#include +#include +#include + +#include "place_qpsolver.h" + +#undef QPS_DEBUG + +#define QPS_TOL 1.0e-3 +#define QPS_EPS (QPS_TOL * QPS_TOL) + +#define QPS_MAX_TOL 0.1 +#define QPS_LOOP_TOL 1.0e-3 + +#define QPS_RELAX_ITER 180 +#define QPS_MAX_ITER 200 +#define QPS_STEPSIZE_RETRIES 2 +#define QPS_MINSTEP 1.0e-6 +#define QPS_DEC_CHANGE 0.01 + +#define QPS_PRECON +#define QPS_PRECON_EPS 1.0e-9 + +#undef QPS_HOIST + +#if defined(QPS_DEBUG) +#define QPS_DEBUG_FILE "/tmp/qps_debug.log" +#endif + +#if 0 + /* ii is an array [0..p->num_cells-1] of indices from cells of original + problem to modified problem variables. If ii[k] >= 0, cell is an + independent cell; ii[k], ii[k]+1 are the indices of the corresponding + variables for the modified problem. If ii[k] == -1, cell is a fixed + cell. If ii[k] <= -2, cell is a dependent cell; -(ii[k]+2) is the index + of the corresponding COG constraint. */ +int *ii; + + /* gt is an array [0..p->cog_num-1] of indices from COG constraints to + locations in the gl array (in qps_problem_t). gt[k] is the offset into + gl where the kth constraint begins. */ +int *gt; + + /* n is the number of variables in the modified problem. n should be twice + the number of independent cells. */ +int n; + +qps_float_t *cp; /* current location during CG iteration */ +qps_float_t f; /* value of cost function at p */ + +#endif + +/**********************************************************************/ + +static void +qps_settp(qps_problem_t * p) +{ + /* Fill in the p->priv_tp array with the current locations of all cells + (independent, dependent and fixed). */ + + int i; + int t, u; + int pr; + qps_float_t rx, ry; + qps_float_t ta; + + int *ii = p->priv_ii; + qps_float_t *tp = p->priv_tp; + qps_float_t *cp = p->priv_cp; + + /* do independent and fixed cells first */ + for (i = p->num_cells; i--;) { + t = ii[i]; + if (t >= 0) { /* indep cell */ + tp[i * 2] = cp[t]; + tp[i * 2 + 1] = cp[t + 1]; + } + else if (t == -1) { /* fixed cell */ + tp[i * 2] = p->x[i]; + tp[i * 2 + 1] = p->y[i]; + } + } + /* now do dependent cells */ + for (i = p->num_cells; i--;) { + if (ii[i] < -1) { + t = -(ii[i] + 2); /* index of COG constraint */ + ta = 0.0; + rx = 0.0; + ry = 0.0; + pr = p->priv_gt[t]; + while ((u = p->cog_list[pr++]) >= 0) { + ta += p->area[u]; + if (u != i) { + rx -= p->area[u] * tp[u * 2]; + ry -= p->area[u] * tp[u * 2 + 1]; + } + } + rx += p->cog_x[t] * ta; + ry += p->cog_y[t] * ta; + tp[i * 2] = rx / p->area[i]; + tp[i * 2 + 1] = ry / p->area[i]; + } + } + +#if (QPS_DEBUG > 5) + fprintf(p->priv_fp, "### qps_settp()\n"); + for (i = 0; i < p->num_cells; i++) { + fprintf(p->priv_fp, "%f %f\n", tp[i * 2], tp[i * 2 + 1]); + } +#endif +} + +/**********************************************************************/ + +static qps_float_t +qps_func(qps_problem_t * p) +{ + /* Return f(p). qps_settp() should have already been called before + entering here */ + + int j, k; + int pr; + qps_float_t jx, jy, tx, ty; + qps_float_t f; + qps_float_t w; + +#if !defined(QPS_HOIST) + int i; + int st; + qps_float_t kx, ky, sx, sy; + qps_float_t t; +#endif + + qps_float_t *tp = p->priv_tp; + + f = 0.0; + pr = 0; + for (j = 0; j < p->num_cells; j++) { + jx = tp[j * 2]; + jy = tp[j * 2 + 1]; + while ((k = p->priv_cc[pr]) >= 0) { + w = p->priv_cw[pr]; + tx = tp[k * 2] - jx; + ty = tp[k * 2 + 1] - jy; + f += w * (tx * tx + ty * ty); + pr++; + } + pr++; + } + p->f = f; + +#if !defined(QPS_HOIST) + /* loop penalties */ + pr = 0; + for (i = 0; i < p->loop_num; i++) { + t = 0.0; + j = st = p->loop_list[pr++]; + jx = sx = tp[j * 2]; + jy = sy = tp[j * 2 + 1]; + while ((k = p->loop_list[pr]) >= 0) { + kx = tp[k * 2]; + ky = tp[k * 2 + 1]; + tx = jx - kx; + ty = jy - ky; + t += tx * tx + ty * ty; + j = k; + jx = kx; + jy = ky; + pr++; + } + tx = jx - sx; + ty = jy - sy; + t += tx * tx + ty * ty; + t -= p->loop_max[i]; +#if (QPS_DEBUG > 5) + fprintf(p->priv_fp, "### qps_penalty() %d %f %f\n", + i, p->loop_max[i], t); +#endif + p->priv_lt[i] = t; + f += p->loop_penalty[i] * t; + pr++; + } +#endif /* QPS_HOIST */ + + if (p->max_enable) { + for (j = p->num_cells; j--;) { + f += p->priv_mxl[j] * (-tp[j * 2]); + f += p->priv_mxh[j] * (tp[j * 2] - p->max_x); + f += p->priv_myl[j] * (-tp[j * 2 + 1]); + f += p->priv_myh[j] * (tp[j * 2 + 1] - p->max_y); + } + } + +#if (QPS_DEBUG > 5) + fprintf(p->priv_fp, "### qps_func() %f %f\n", f, p->f); +#endif + return f; +} + +/**********************************************************************/ + +static void +qps_dfunc(qps_problem_t * p, qps_float_t * d) +{ + /* Set d to grad f(p). First computes partial derivatives wrt all cells + then finds gradient wrt only the independent cells. qps_settp() should + have already been called before entering here */ + + int i, j, k; + int pr = 0; + qps_float_t jx, jy, kx, ky, tx, ty; + int ji, ki; + qps_float_t w; + +#if !defined(QPS_HOIST) + qps_float_t sx, sy; + int st; +#endif + + qps_float_t *tp = p->priv_tp; + qps_float_t *tp2 = p->priv_tp2; + + /* compute partials and store in tp2 */ + for (i = p->num_cells; i--;) { + tp2[i * 2] = 0.0; + tp2[i * 2 + 1] = 0.0; + } + for (j = 0; j < p->num_cells; j++) { + jx = tp[j * 2]; + jy = tp[j * 2 + 1]; + while ((k = p->priv_cc[pr]) >= 0) { + w = 2.0 * p->priv_cw[pr]; + kx = tp[k * 2]; + ky = tp[k * 2 + 1]; + tx = w * (jx - kx); + ty = w * (jy - ky); + tp2[j * 2] += tx; + tp2[k * 2] -= tx; + tp2[j * 2 + 1] += ty; + tp2[k * 2 + 1] -= ty; + pr++; + } + pr++; + } + +#if !defined(QPS_HOIST) + /* loop penalties */ + pr = 0; + for (i = 0; i < p->loop_num; i++) { + j = st = p->loop_list[pr++]; + jx = sx = tp[j * 2]; + jy = sy = tp[j * 2 + 1]; + w = 2.0 * p->loop_penalty[i]; + while ((k = p->loop_list[pr]) >= 0) { + kx = tp[k * 2]; + ky = tp[k * 2 + 1]; + tx = w * (jx - kx); + ty = w * (jy - ky); + tp2[j * 2] += tx; + tp2[k * 2] -= tx; + tp2[j * 2 + 1] += ty; + tp2[k * 2 + 1] -= ty; + j = k; + jx = kx; + jy = ky; + pr++; + } + tx = w * (jx - sx); + ty = w * (jy - sy); + tp2[j * 2] += tx; + tp2[st * 2] -= tx; + tp2[j * 2 + 1] += ty; + tp2[st * 2 + 1] -= ty; + pr++; + } +#endif /* QPS_HOIST */ + + if (p->max_enable) { + for (j = p->num_cells; j--;) { + tp2[j * 2] += p->priv_mxh[j] - p->priv_mxl[j]; + tp2[j * 2 + 1] += p->priv_myh[j] - p->priv_myl[j]; + } + } + +#if (QPS_DEBUG > 5) + fprintf(p->priv_fp, "### qps_dfunc() partials\n"); + for (j = 0; j < p->num_cells; j++) { + fprintf(p->priv_fp, "%f %f\n", tp2[j * 2], tp2[j * 2 + 1]); + } +#endif + + /* translate partials to independent variables */ + for (j = p->priv_n; j--;) { + d[j] = 0.0; + } + for (j = p->num_cells; j--;) { + ji = p->priv_ii[j]; + if (ji >= 0) { /* indep var */ + d[ji] += tp2[j * 2]; + d[ji + 1] += tp2[j * 2 + 1]; + } + else if (ji < -1) { /* dependent variable */ + ji = -(ji + 2); /* get COG index */ + pr = p->priv_gt[ji]; + while ((k = p->cog_list[pr]) >= 0) { + ki = p->priv_ii[k]; + if (ki >= 0) { + w = p->priv_gw[pr]; +#if (QPS_DEBUG > 0) + assert(fabs(w - p->area[k] / p->area[j]) < 1.0e-6); +#endif + d[ki] -= tp2[j * 2] * w; + d[ki + 1] -= tp2[j * 2 + 1] * w; + } + pr++; + } + } + } + +#if (QPS_DEBUG > 5) + fprintf(p->priv_fp, "### qps_dfunc() gradient\n"); + for (j = 0; j < p->priv_n; j++) { + fprintf(p->priv_fp, "%f\n", d[j]); + } +#endif +} + +/**********************************************************************/ + +static void +qps_linmin(qps_problem_t * p, qps_float_t dgg, qps_float_t * h) +{ + /* Perform line minimization. p->priv_cp is the current location, h is + direction of the gradient. Updates p->priv_cp to line minimal position + based on formulas from "Handbook of Applied Optimization", Pardalos and + Resende, eds., Oxford Univ. Press, 2002. qps_settp() should have + already been called before entering here. Since p->priv_cp is changed, + p->priv_tp array becomes invalid following this routine. */ + + int i, j, k; + int pr; + int ji, ki; + qps_float_t jx, jy, kx, ky; + qps_float_t f = 0.0; + qps_float_t w; + +#if !defined(QPS_HOIST) + int st; + qps_float_t sx, sy, tx, ty; + qps_float_t t; +#endif + + qps_float_t *tp = p->priv_tp; + + /* translate h vector to partials over all variables and store in tp */ + for (i = p->num_cells; i--;) { + tp[i * 2] = 0.0; + tp[i * 2 + 1] = 0.0; + } + for (j = p->num_cells; j--;) { + ji = p->priv_ii[j]; + if (ji >= 0) { /* indep cell */ + tp[j * 2] = h[ji]; + tp[j * 2 + 1] = h[ji + 1]; + } + else if (ji < -1) { /* dep cell */ + ji = -(ji + 2); /* get COG index */ + pr = p->priv_gt[ji]; + while ((k = p->cog_list[pr]) >= 0) { + ki = p->priv_ii[k]; + if (ki >= 0) { + w = p->priv_gw[pr]; +#if (QPS_DEBUG > 0) + assert(fabs(w - p->area[k] / p->area[j]) < 1.0e-6); +#endif + tp[j * 2] -= h[ki] * w; + tp[j * 2 + 1] -= h[ki + 1] * w; + } + pr++; + } + } + } + + /* take product x^T Z^T C Z x */ + pr = 0; + for (j = 0; j < p->num_cells; j++) { + jx = tp[j * 2]; + jy = tp[j * 2 + 1]; + while ((k = p->priv_cc[pr]) >= 0) { + w = p->priv_cw[pr]; + kx = tp[k * 2] - jx; + ky = tp[k * 2 + 1] - jy; + f += w * (kx * kx + ky * ky); + pr++; + } + pr++; + } + +#if !defined(QPS_HOIST) + /* add loop penalties */ + pr = 0; + for (i = 0; i < p->loop_num; i++) { + t = 0.0; + j = st = p->loop_list[pr++]; + jx = sx = tp[j * 2]; + jy = sy = tp[j * 2 + 1]; + while ((k = p->loop_list[pr]) >= 0) { + kx = tp[k * 2]; + ky = tp[k * 2 + 1]; + tx = jx - kx; + ty = jy - ky; + t += tx * tx + ty * ty; + j = k; + jx = kx; + jy = ky; + pr++; + } + tx = jx - sx; + ty = jy - sy; + t += tx * tx + ty * ty; + f += p->loop_penalty[i] * t; + pr++; + } +#endif /* QPS_HOIST */ + +#if (QPS_DEBUG > 0) + assert(f); +#endif + + /* compute step size */ + f = (dgg / f) / 2.0; + for (j = p->priv_n; j--;) { + p->priv_cp[j] += f * h[j]; + } +#if (QPS_DEBUG > 5) + fprintf(p->priv_fp, "### qps_linmin() step %f\n", f); + for (j = 0; j < p->priv_n; j++) { + fprintf(p->priv_fp, "%f\n", p->priv_cp[j]); + } +#endif +} + +/**********************************************************************/ + +static void +qps_cgmin(qps_problem_t * p) +{ + /* Perform CG minimization. Mostly from "Numerical Recipes", Press et al., + Cambridge Univ. Press, 1992, with some changes to help performance in + our restricted problem domain. */ + + qps_float_t fp, gg, dgg, gam; + qps_float_t t; + int i, j; + + int n = p->priv_n; + qps_float_t *g = p->priv_g; + qps_float_t *h = p->priv_h; + qps_float_t *xi = p->priv_xi; + + qps_settp(p); + fp = qps_func(p); + qps_dfunc(p, g); + + dgg = 0.0; + for (j = n; j--;) { + g[j] = -g[j]; + h[j] = g[j]; +#if defined(QPS_PRECON) + h[j] *= p->priv_pcgt[j]; +#endif + dgg += g[j] * h[j]; + } + + for (i = 0; i < 2 * n; i++) { + +#if (QPS_DEBUG > 5) + fprintf(p->priv_fp, "### qps_cgmin() top\n"); + for (j = 0; j < p->priv_n; j++) { + fprintf(p->priv_fp, "%f\n", p->priv_cp[j]); + } +#endif + + if (dgg == 0.0) { + break; + } + qps_linmin(p, dgg, h); + qps_settp(p); + p->priv_f = qps_func(p); + if (fabs((p->priv_f) - fp) <= + (fabs(p->priv_f) + fabs(fp) + QPS_EPS) * QPS_TOL / 2.0) { + break; + } + fp = p->priv_f; + qps_dfunc(p, xi); + gg = dgg; + dgg = 0.0; + for (j = n; j--;) { + t = xi[j] * xi[j]; +#if defined(QPS_PRECON) + t *= p->priv_pcgt[j]; +#endif + dgg += t; + } + gam = dgg / gg; + for (j = n; j--;) { + g[j] = -xi[j]; + t = g[j]; +#if defined(QPS_PRECON) + t *= p->priv_pcgt[j]; +#endif + h[j] = t + gam * h[j]; + } + } +#if (QPS_DEBUG > 0) + fprintf(p->priv_fp, "### CG ITERS=%d %d %d\n", i, p->cog_num, p->loop_num); +#endif + if (i == 2 * n) { + fprintf(stderr, "### Too many iterations in qps_cgmin()\n"); +#if defined(QPS_DEBUG) + fprintf(p->priv_fp, "### Too many iterations in qps_cgmin()\n"); +#endif + } +} + +/**********************************************************************/ + +void +qps_init(qps_problem_t * p) +{ + int i, j; + int pr, pw; + +#if defined(QPS_DEBUG) + p->priv_fp = fopen(QPS_DEBUG_FILE, "a"); + assert(p->priv_fp); +#endif + +#if (QPS_DEBUG > 5) + fprintf(p->priv_fp, "### n=%d gn=%d ln=%d\n", p->num_cells, p->cog_num, + p->loop_num); + pr = 0; + fprintf(p->priv_fp, "### (c w) values\n"); + for (i = 0; i < p->num_cells; i++) { + fprintf(p->priv_fp, "net %d: ", i); + while (p->connect[pr] >= 0) { + fprintf(p->priv_fp, "(%d %f) ", p->connect[pr], p->edge_weight[pr]); + pr++; + } + fprintf(p->priv_fp, "(-1 -1.0)\n"); + pr++; + } + fprintf(p->priv_fp, "### (x y f) values\n"); + for (i = 0; i < p->num_cells; i++) { + fprintf(p->priv_fp, "cell %d: (%f %f %d)\n", i, p->x[i], p->y[i], + p->fixed[i]); + } +#if 0 + if (p->cog_num) { + fprintf(p->priv_fp, "### ga values\n"); + for (i = 0; i < p->num_cells; i++) { + fprintf(p->priv_fp, "cell %d: (%f)\n", i, p->area[i]); + } + } + pr = 0; + fprintf(p->priv_fp, "### gl values\n"); + for (i = 0; i < p->cog_num; i++) { + fprintf(p->priv_fp, "cog %d: ", i); + while (p->cog_list[pr] >= 0) { + fprintf(p->priv_fp, "%d ", p->cog_list[pr]); + pr++; + } + fprintf(p->priv_fp, "-1\n"); + pr++; + } + fprintf(p->priv_fp, "### (gx gy) values\n"); + for (i = 0; i < p->cog_num; i++) { + fprintf(p->priv_fp, "cog %d: (%f %f)\n", i, p->cog_x[i], p->cog_y[i]); + } +#endif +#endif /* QPS_DEBUG */ + + p->priv_ii = (int *)malloc(p->num_cells * sizeof(int)); + assert(p->priv_ii); + + p->max_enable = 0; + + p->priv_fopt = 0.0; + + /* canonify c and w */ + pr = pw = 0; + for (i = 0; i < p->num_cells; i++) { + while ((j = p->connect[pr]) >= 0) { + if (j > i) { + pw++; + } + pr++; + } + pw++; + pr++; + } + p->priv_cc = (int *)malloc(pw * sizeof(int)); + assert(p->priv_cc); + p->priv_cr = (int *)malloc(p->num_cells * sizeof(int)); + assert(p->priv_cr); + p->priv_cw = (qps_float_t*)malloc(pw * sizeof(qps_float_t)); + assert(p->priv_cw); + p->priv_ct = (qps_float_t*)malloc(pw * sizeof(qps_float_t)); + assert(p->priv_ct); + p->priv_cm = pw; + pr = pw = 0; + for (i = 0; i < p->num_cells; i++) { + p->priv_cr[i] = pw; + while ((j = p->connect[pr]) >= 0) { + if (j > i) { + p->priv_cc[pw] = p->connect[pr]; + p->priv_ct[pw] = p->edge_weight[pr]; + pw++; + } + pr++; + } + p->priv_cc[pw] = -1; + p->priv_ct[pw] = -1.0; + pw++; + pr++; + } + assert(pw == p->priv_cm); + + /* temp arrays for function eval */ + p->priv_tp = (qps_float_t *) malloc(4 * p->num_cells * sizeof(qps_float_t)); + assert(p->priv_tp); + p->priv_tp2 = p->priv_tp + 2 * p->num_cells; +} + +/**********************************************************************/ + +static qps_float_t +qps_estopt(qps_problem_t * p) +{ + int i, j, cell; + qps_float_t r; + qps_float_t *t1, *t2; + qps_float_t t; + + if (p->max_enable) { + r = 0.0; + t1 = (qps_float_t *) malloc(2 * p->num_cells * sizeof(qps_float_t)); +#if (QPS_DEBUG > 0) + assert(t1); +#endif + for (i = 2 * p->num_cells; i--;) { + t1[i] = 0.0; + } + j = 0; + for (i = 0; i < p->cog_num; i++) { + while ((cell = p->cog_list[j]) >= 0) { + t1[cell * 2] = p->cog_x[i]; + t1[cell * 2 + 1] = p->cog_y[i]; + j++; + } + j++; + } + t2 = p->priv_tp; + p->priv_tp = t1; + r = qps_func(p); + p->priv_tp = t2; + free(t1); + t = (p->max_x * p->max_x + p->max_y * p->max_y); + t *= p->num_cells; + for (i = p->num_cells; i--;) { + if (p->fixed[i]) { + r += t; + } + } + } + else { + r = p->priv_f; + } + if (p->loop_num) { + /* FIXME hacky */ + r *= 8.0; + } + return r; +} + +/**********************************************************************/ + +static void +qps_solve_inner(qps_problem_t * p) +{ + int i; + qps_float_t t; + qps_float_t z; + qps_float_t pm1, pm2, tp; + qps_float_t *tw; +#if defined(QPS_HOIST) + int j, k; + qps_float_t jx, jy, kx, ky, sx, sy, tx, ty; + int pr, st; +#endif + + tw = p->priv_cw; +#if defined(QPS_HOIST) + if (!p->loop_num) { + p->priv_cw = p->priv_ct; + } + else { + for(i=p->priv_cm; i--;) { + p->priv_cw[i] = p->priv_ct[i]; + } + /* augment with loop penalties */ + pr = 0; + for (i = 0; i < p->loop_num; i++) { + while ((j = p->priv_la[pr++]) != -1) { + if (j >= 0) { + p->priv_cw[j] += p->loop_penalty[i]; + } + } + pr++; + } + } +#else /* !QPS_HOIST */ + p->priv_cw = p->priv_ct; +#endif /* QPS_HOIST */ + + qps_cgmin(p); + + if (p->max_enable || p->loop_num) { + if (p->max_enable == 1 || (p->loop_num && p->loop_k == 0)) { + p->priv_eps = 2.0; + p->priv_fmax = p->priv_f; + p->priv_fprev = p->priv_f; + p->priv_fopt = qps_estopt(p); + p->priv_pn = 0; + p->loop_fail = 0; + } + else { + if (p->priv_f < p->priv_fprev && + (p->priv_fprev - p->priv_f) > + QPS_DEC_CHANGE * fabs(p->priv_fprev)) { + if (p->priv_pn++ >= QPS_STEPSIZE_RETRIES) { + p->priv_eps /= 2.0; + p->priv_pn = 0; + } + } + p->priv_fprev = p->priv_f; + if (p->priv_fmax < p->priv_f) { + p->priv_fmax = p->priv_f; + } + if (p->priv_f >= p->priv_fopt) { + p->priv_fopt = p->priv_fmax * 2.0; + p->loop_fail |= 2; +#if (QPS_DEBUG > 0) + fprintf(p->priv_fp, "### warning: changing fopt\n"); +#endif + } + } +#if (QPS_DEBUG > 0) + fprintf(p->priv_fp, "### max_stat %.2e %.2e %.2e %.2e\n", + p->priv_f, p->priv_eps, p->priv_fmax, p->priv_fopt); + fflush(p->priv_fp); +#endif + } + + p->loop_done = 1; + if (p->loop_num) { +#if (QPS_DEBUG > 0) + fprintf(p->priv_fp, "### begin_update %d\n", p->loop_k); +#endif + p->loop_k++; + +#if defined(QPS_HOIST) + /* calc loop penalties */ + pr = 0; + for (i = 0; i < p->loop_num; i++) { + t = 0.0; + j = st = p->loop_list[pr++]; + jx = sx = p->priv_tp[j * 2]; + jy = sy = p->priv_tp[j * 2 + 1]; + while ((k = p->loop_list[pr]) >= 0) { + kx = p->priv_tp[k * 2]; + ky = p->priv_tp[k * 2 + 1]; + tx = jx - kx; + ty = jy - ky; + t += tx * tx + ty * ty; + j = k; + jx = kx; + jy = ky; + pr++; + } + tx = jx - sx; + ty = jy - sy; + t += tx * tx + ty * ty; + p->priv_lt[i] = t - p->loop_max[i]; + pr++; + } +#endif /* QPS_HOIST */ + + /* check KKT conditions */ +#if (QPS_DEBUG > 1) + for (i = p->loop_num; i--;) { + if (p->loop_penalty[i] != 0.0) { + fprintf(p->priv_fp, "### penalty %d %.2e\n", i, p->loop_penalty[i]); + } + } +#endif + t = 0.0; + for (i = p->loop_num; i--;) { + if (p->priv_lt[i] > 0.0 || p->loop_penalty[i] > 0.0) { + t += p->priv_lt[i] * p->priv_lt[i]; + } + if (fabs(p->priv_lt[i]) < QPS_LOOP_TOL) { +#if (QPS_DEBUG > 4) + fprintf(p->priv_fp, "### skip %d %f\n", i, p->priv_lt[i]); +#endif + continue; + } + z = QPS_LOOP_TOL * p->loop_max[i]; + if (p->priv_lt[i] > z || (p->loop_k < QPS_RELAX_ITER && + p->loop_penalty[i] * p->priv_lt[i] < -z)) { + p->loop_done = 0; +#if (QPS_DEBUG > 1) + fprintf(p->priv_fp, "### not_done %d %f %f %f %f\n", i, + p->priv_lt[i], z, p->loop_max[i], p->loop_penalty[i]); +#endif + } +#if (QPS_DEBUG > 5) + else { + fprintf(p->priv_fp, "### done %d %f %f %f %f\n", i, + p->priv_lt[i], z, p->loop_max[i], p->loop_penalty[i]); + } +#endif + } + /* update penalties */ + if (!p->loop_done) { + t = p->priv_eps * (p->priv_fopt - p->priv_f) / t; + tp = 0.0; + for (i = p->loop_num; i--;) { + pm1 = p->loop_penalty[i]; +#if (QPS_DEBUG > 5) + fprintf(p->priv_fp, "### update %d %.2e %.2e %.2e %.2e %.2e\n", i, + t, p->priv_lt[i], t * p->priv_lt[i], pm1, p->loop_max[i]); +#endif + p->loop_penalty[i] += t * p->priv_lt[i]; + if (p->loop_penalty[i] < 0.0) { + p->loop_penalty[i] = 0.0; + } + pm2 = p->loop_penalty[i]; + tp += fabs(pm1 - pm2); + } +#if (QPS_DEBUG > 4) + fprintf(p->priv_fp, "### penalty mag %f\n", tp); +#endif + } + } + + p->max_done = 1; + if (p->max_enable) { +#if (QPS_DEBUG > 4) + fprintf(p->priv_fp, "### begin_max_update %d\n", p->max_enable); +#endif + t = 0.0; + for (i = p->num_cells; i--;) { + z = -(p->x[i]); + t += z * z; + if (z > QPS_TOL || (p->max_enable < QPS_RELAX_ITER && + p->priv_mxl[i] * z < -QPS_MAX_TOL)) { + p->max_done = 0; +#if (QPS_DEBUG > 4) + fprintf(p->priv_fp, "### nxl %d %f %f\n", i, z, p->priv_mxl[i]); +#endif + } + z = (p->x[i] - p->max_x); + t += z * z; + if (z > QPS_TOL || (p->max_enable < QPS_RELAX_ITER && + p->priv_mxh[i] * z < -QPS_MAX_TOL)) { + p->max_done = 0; +#if (QPS_DEBUG > 4) + fprintf(p->priv_fp, "### nxh %d %f %f\n", i, z, p->priv_mxh[i]); +#endif + } + z = -(p->y[i]); + t += z * z; + if (z > QPS_TOL || (p->max_enable < QPS_RELAX_ITER && + p->priv_myl[i] * z < -QPS_MAX_TOL)) { + p->max_done = 0; +#if (QPS_DEBUG > 4) + fprintf(p->priv_fp, "### nyl %d %f %f\n", i, z, p->priv_myl[i]); +#endif + } + z = (p->y[i] - p->max_y); + t += z * z; + if (z > QPS_TOL || (p->max_enable < QPS_RELAX_ITER && + p->priv_myh[i] * z < -QPS_MAX_TOL)) { + p->max_done = 0; +#if (QPS_DEBUG > 4) + fprintf(p->priv_fp, "### nyh %d %f %f\n", i, z, p->priv_myh[i]); +#endif + } + } +#if (QPS_DEBUG > 4) + fprintf(p->priv_fp, "### max_done %d %f\n", p->max_done, t); +#endif + if (!p->max_done) { + t = p->priv_eps * (p->priv_fopt - p->priv_f) / t; + tp = 0.0; + for (i = p->num_cells; i--;) { + z = -(p->x[i]); + pm1 = p->priv_mxl[i]; + p->priv_mxl[i] += t * z; + if (p->priv_mxl[i] < 0.0) { + p->priv_mxl[i] = 0.0; + } + pm2 = p->priv_mxl[i]; + tp += fabs(pm1 - pm2); + + z = (p->x[i] - p->max_x); + pm1 = p->priv_mxh[i]; + p->priv_mxh[i] += t * z; + if (p->priv_mxh[i] < 0.0) { + p->priv_mxh[i] = 0.0; + } + pm2 = p->priv_mxh[i]; + tp += fabs(pm1 - pm2); + + z = -(p->y[i]); + pm1 = p->priv_myl[i]; + p->priv_myl[i] += t * z; + if (p->priv_myl[i] < 0.0) { + p->priv_myl[i] = 0.0; + } + pm2 = p->priv_myl[i]; + tp += fabs(pm1 - pm2); + + z = (p->y[i] - p->max_y); + pm1 = p->priv_myh[i]; + p->priv_myh[i] += t * z; + if (p->priv_myh[i] < 0.0) { + p->priv_myh[i] = 0.0; + } + pm2 = p->priv_myh[i]; + tp += fabs(pm1 - pm2); + } + } +#if (QPS_DEBUG > 4) + for (i = p->num_cells; i--;) { + fprintf(p->priv_fp, "### max_penalty %d %f %f %f %f\n", i, + p->priv_mxl[i], p->priv_mxh[i], p->priv_myl[i], p->priv_myh[i]); + } +#endif + p->max_enable++; + } + + if (p->loop_k >= QPS_MAX_ITER || p->priv_eps < QPS_MINSTEP) { + p->loop_fail |= 1; + } + + if (p->loop_fail) { + p->loop_done = 1; + } + + p->priv_cw = tw; +} + +/**********************************************************************/ + +void +qps_solve(qps_problem_t * p) +{ + int i, j; + int pr, pw; + qps_float_t bk; + int tk; + +#if defined(QPS_PRECON) + int c; + qps_float_t t; +#endif + +#if defined(QPS_HOIST) + int k; + int st; + int m1, m2; +#endif + + if (p->max_enable) { + p->priv_mxl = (qps_float_t *) + malloc(4 * p->num_cells * sizeof(qps_float_t)); + assert(p->priv_mxl); + p->priv_mxh = p->priv_mxl + p->num_cells; + p->priv_myl = p->priv_mxl + 2 * p->num_cells; + p->priv_myh = p->priv_mxl + 3 * p->num_cells; + for (i = 4 * p->num_cells; i--;) { + p->priv_mxl[i] = 0.0; + } + } + + /* flag fixed cells with -1 */ + for (i = p->num_cells; i--;) { + p->priv_ii[i] = (p->fixed[i]) ? (-1) : (0); + } + + /* read gl and set up dependent variables */ + if (p->cog_num) { + p->priv_gt = (int *)malloc(p->cog_num * sizeof(int)); + assert(p->priv_gt); + p->priv_gm = (qps_float_t*)malloc(p->cog_num * sizeof(qps_float_t)); + assert(p->priv_gm); + pr = 0; + for (i = 0; i < p->cog_num; i++) { + tk = -1; + bk = -1.0; + pw = pr; + while ((j = p->cog_list[pr++]) >= 0) { + if (!p->fixed[j]) { + /* use largest entry for numerical stability; see Gordian paper */ + if (p->area[j] > bk) { + tk = j; + bk = p->area[j]; + } + } + } + assert(bk > 0.0); + /* dependent variables have index=(-2-COG_constraint) */ + p->priv_ii[tk] = -2 - i; + p->priv_gt[i] = pw; + p->priv_gm[i] = bk; + } + p->priv_gw = (qps_float_t*)malloc(pr * sizeof(qps_float_t)); + assert(p->priv_gw); + pr = 0; + for (i = 0; i < p->cog_num; i++) { + while ((j = p->cog_list[pr]) >= 0) { + p->priv_gw[pr] = p->area[j] / p->priv_gm[i]; + pr++; + } + p->priv_gw[pr] = -1.0; + pr++; + } + } + + /* set up indexes from independent floating cells to variables */ + p->priv_n = 0; + for (i = p->num_cells; i--;) { + if (!p->priv_ii[i]) { + p->priv_ii[i] = 2 * (p->priv_n++); + } + } + p->priv_n *= 2; + +#if (QPS_DEBUG > 5) + for (i = 0; i < p->num_cells; i++) { + fprintf(p->priv_fp, "### ii %d %d\n", i, p->priv_ii[i]); + } +#endif + +#if defined(QPS_PRECON) + p->priv_pcg = (qps_float_t *) malloc(p->num_cells * sizeof(qps_float_t)); + assert(p->priv_pcg); + p->priv_pcgt = (qps_float_t *) malloc(p->priv_n * sizeof(qps_float_t)); + assert(p->priv_pcgt); + for (i = p->num_cells; i--;) { + p->priv_pcg[i] = 0.0; + } + pr = 0; + for (i = 0; i < p->num_cells; i++) { + while ((c = p->priv_cc[pr]) >= 0) { + t = p->priv_ct[pr]; + p->priv_pcg[i] += t; + p->priv_pcg[c] += t; + pr++; + } + pr++; + } + pr = 0; + for (i = 0; i < p->loop_num; i++) { + t = 2.0 * p->loop_penalty[i]; + while ((c = p->loop_list[pr++]) >= 0) { + p->priv_pcg[c] += t; + } + pr++; + } +#if (QPS_DEBUG > 6) + for (i = p->num_cells; i--;) { + fprintf(p->priv_fp, "### precon %d %.2e\n", i, p->priv_pcg[i]); + } +#endif + for (i = p->priv_n; i--;) { + p->priv_pcgt[i] = 0.0; + } + for (i = 0; i < p->num_cells; i++) { + c = p->priv_ii[i]; + if (c >= 0) { + t = p->priv_pcg[i]; + p->priv_pcgt[c] += t; + p->priv_pcgt[c + 1] += t; + } +#if 0 + else if (c < -1) { + pr = p->priv_gt[-(c+2)]; + while ((j = p->cog_list[pr++]) >= 0) { + ji = p->priv_ii[j]; + if (ji >= 0) { + w = p->area[j] / p->area[i]; + t = w * w * p->priv_pcg[i]; + p->priv_pcgt[ji] += t; + p->priv_pcgt[ji + 1] += t; + } + } + } +#endif + } + for (i = 0; i < p->priv_n; i++) { + t = p->priv_pcgt[i]; + if (fabs(t) < QPS_PRECON_EPS || fabs(t) > 1.0/QPS_PRECON_EPS) { + p->priv_pcgt[i] = 1.0; + } + else { + p->priv_pcgt[i] = 1.0 / p->priv_pcgt[i]; + } + } +#endif + + /* allocate variable storage */ + p->priv_cp = (qps_float_t *) malloc(4 * p->priv_n * sizeof(qps_float_t)); + assert(p->priv_cp); + + /* temp arrays for cg */ + p->priv_g = p->priv_cp + p->priv_n; + p->priv_h = p->priv_cp + 2 * p->priv_n; + p->priv_xi = p->priv_cp + 3 * p->priv_n; + + /* set values */ + for (i = p->num_cells; i--;) { + if (p->priv_ii[i] >= 0) { + p->priv_cp[p->priv_ii[i]] = p->x[i]; + p->priv_cp[p->priv_ii[i] + 1] = p->y[i]; + } + } + + if (p->loop_num) { + p->priv_lt = (qps_float_t *) malloc(p->loop_num * sizeof(qps_float_t)); + assert(p->priv_lt); +#if defined(QPS_HOIST) + pr = 0; + for (i=p->loop_num; i--;) { + while (p->loop_list[pr++] >= 0) { + } + pr++; + } + p->priv_lm = pr; + p->priv_la = (int *) malloc(pr * sizeof(int)); + assert(p->priv_la); + pr = 0; + for (i = 0; i < p->loop_num; i++) { + j = st = p->loop_list[pr++]; + while ((k = p->loop_list[pr]) >= 0) { + if (j > k) { + m1 = k; + m2 = j; + } + else { + assert(k > j); + m1 = j; + m2 = k; + } + pw = p->priv_cr[m1]; + while (p->priv_cc[pw] != m2) { +/* assert(p->priv_cc[pw] >= 0); */ + if (p->priv_cc[pw] < 0) { + pw = -2; + break; + } + pw++; + } + p->priv_la[pr-1] = pw; + j = k; + pr++; + } + if (j > st) { + m1 = st; + m2 = j; + } + else { + assert(st > j); + m1 = j; + m2 = st; + } + pw = p->priv_cr[m1]; + while (p->priv_cc[pw] != m2) { +/* assert(p->priv_cc[pw] >= 0); */ + if (p->priv_cc[pw] < 0) { + pw = -2; + break; + } + pw++; + } + p->priv_la[pr-1] = pw; + p->priv_la[pr] = -1; + pr++; + } +#endif /* QPS_HOIST */ + } + + do { + qps_solve_inner(p); + } while (!p->loop_done || !p->max_done); + + /* retrieve values */ + /* qps_settp() should have already been called at this point */ + for (i = p->num_cells; i--;) { + p->x[i] = p->priv_tp[i * 2]; + p->y[i] = p->priv_tp[i * 2 + 1]; + } +#if (QPS_DEBUG > 5) + for (i = p->num_cells; i--;) { + fprintf(p->priv_fp, "### cloc %d %f %f\n", i, p->x[i], p->y[i]); + } +#endif + + free(p->priv_cp); + if (p->max_enable) { + free(p->priv_mxl); + } + if (p->cog_num) { + free(p->priv_gt); + free(p->priv_gm); + free(p->priv_gw); + } + if(p->loop_num) { + free(p->priv_lt); +#if defined(QPS_HOIST) + free(p->priv_la); +#endif + } + +#if defined(QPS_PRECON) + free(p->priv_pcg); + free(p->priv_pcgt); +#endif +} + +/**********************************************************************/ + +void +qps_clean(qps_problem_t * p) +{ + free(p->priv_tp); + free(p->priv_ii); + free(p->priv_cc); + free(p->priv_cr); + free(p->priv_cw); + free(p->priv_ct); + +#if defined(QPS_DEBUG) + fclose(p->priv_fp); +#endif /* QPS_DEBUG */ +} + +/**********************************************************************/ diff --git a/abc_with_bb_support/src/phys/place/place_qpsolver.h b/abc_with_bb_support/src/phys/place/place_qpsolver.h new file mode 100644 index 000000000..02394d0ea --- /dev/null +++ b/abc_with_bb_support/src/phys/place/place_qpsolver.h @@ -0,0 +1,140 @@ +/*===================================================================*/ +// +// place_qpsolver.h +// +// Philip Chong +// pchong@cadence.com +// +/*===================================================================*/ + +#if !defined(_QPS_H) +#define _QPS_H + +#include + +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus */ + + typedef float qps_float_t; + + typedef struct qps_problem { + + /* Basic stuff */ + int num_cells; /* Total number of cells (both fixed and + floating) to be placed. */ + int *connect; /* Connectivity array. Must contain at least + num_cells elements with value -1. The + entries which precede the first element + with value -1 are the indices of the cells + which connect to cell 0; the entries + which lie between the first and second + elements with value -1 are the indices of + the cells which connect to cell 1; etc. + Example: cells 0 and 1 are connected + together, and 1 and 2 are connected as + well. *connect = { 1, -1, 0, 2, -1, 1, -1 + }. */ + qps_float_t *edge_weight; /* Same structure as connectivity array, but + giving the weights assigned to each edge + instead. */ + qps_float_t *x; /* num_cells element array which contains the + x-coordinates of the cells. This is used + for the initial values in the iterative + solution of floating cells, and for the + fixed location of fixed cells. */ + qps_float_t *y; /* num_cells element array of + y-coordinates. */ + int *fixed; /* num_cells element array with value 1 if + corresponding cell is fixed, 0 if + floating. */ + qps_float_t f; /* return value for sum-of-square + wirelengths. */ + + /* COG stuff */ + int cog_num; /* Number of COG constraints. */ + int *cog_list; /* Array indicating for each COG constraint + which cells belong to that constraint. + Format is similar to c array: there must + be at least cog_num elements with value + -1. The entries of cog_list preceding the + first -1 element are the indices of the + cells which belong to the first COG + constraint; etc. Example: cells 0 and 1 + belong to one COG constraint, cells 4 and + 5 belong to another. *cog_list= { 0, 1, + -1, 4, 5, -1 }. */ + qps_float_t *cog_x; /* cog_num element array whose values are the + x-coordinates for the corresponding COG + constraints. */ + qps_float_t *cog_y; /* cog_num element array whose values are the + y-coordinates for the corresponding COG + constraints. */ + qps_float_t *area; /* num_cells element array whose values are + the areas for the corresponding cells; + only useful with COG constraints. */ + + /* Loop constraint stuff */ + int loop_num; /* Number of loop constraints. */ + int *loop_list; /* Array with list of cells for each loop + constraint. Format is similar to cog_list. + */ + qps_float_t *loop_max; /* loop_num element array indicating maximum + distance for each loop. */ + qps_float_t *loop_penalty; /* loop_num element array indicating penalty + for each loop. */ + int loop_k; /* Current iteration for loop optimization. */ + int loop_done; /* Done flag for loop optimization. */ + int loop_fail; + + /* max_x/max_y stuff */ + qps_float_t max_x; /* max x location; only used in + constrained optimization. */ + qps_float_t max_y; /* max y location; only used in + constrained optimization. */ + int max_enable; /* Set to 1 after qps_init() to enable + max_x/max_y. */ + int max_done; /* Done flag for max optimization. */ + + /* Private stuff */ + int *priv_ii; + int *priv_cc, *priv_cr; + qps_float_t *priv_cw, *priv_ct; + int priv_cm; + int *priv_gt; + int *priv_la; + int priv_lm; + qps_float_t *priv_gm, *priv_gw; + qps_float_t *priv_g, *priv_h, *priv_xi; + qps_float_t *priv_tp, *priv_tp2; + int priv_n; + qps_float_t *priv_cp; + qps_float_t priv_f; + qps_float_t *priv_lt; + qps_float_t *priv_pcg, *priv_pcgt; + qps_float_t priv_fmax; + qps_float_t priv_fprev; + qps_float_t priv_fopt; + qps_float_t priv_eps; + int priv_pn; + qps_float_t *priv_mxl, *priv_mxh, *priv_myl, *priv_myh; + int priv_ik; + FILE *priv_fp; + + } qps_problem_t; + + /* call qps_init() as soon as the qps_problem_t has been set up */ + /* this initializes some private data structures */ + extern void qps_init(qps_problem_t *); + + /* call qps_solve() to solve the given qp problem */ + extern void qps_solve(qps_problem_t *); + + /* call qps_clean() when finished with the qps_problem_t */ + /* this discards the private data structures assigned by qps_init() */ + extern void qps_clean(qps_problem_t *); + +#if defined(__cplusplus) +} +#endif /* __cplusplus */ +#endif /* _QPS_H */ diff --git a/abc_with_bb_support/src/phys/place/place_test.c b/abc_with_bb_support/src/phys/place/place_test.c new file mode 100644 index 000000000..19906c831 --- /dev/null +++ b/abc_with_bb_support/src/phys/place/place_test.c @@ -0,0 +1,360 @@ +/*===================================================================*/ +// +// place_test.c +// +// Aaron P. Hurst, 2003-2007 +// ahurst@eecs.berkeley.edu +// +/*===================================================================*/ + +#include +#include +#include +#include +#include "place_base.h" + + +// -------------------------------------------------------------------- +// Hash type/functions +// +// -------------------------------------------------------------------- + +struct hash_element { + ConcreteCell *obj; + struct hash_element *next; +} hash_element; + +int hash_string(int hash_max, const char *str) { + unsigned int hash = 0; + int p; + for(p = 0; pm_label); + // printf("adding %s key = %d\n", cell->m_label, key); + struct hash_element *element = malloc(sizeof(struct hash_element)); + assert(element); + element->obj = cell; + element->next = hash[key]; + hash[key] = element; +} + +ConcreteCell *hash_find(struct hash_element **hash, int hash_max, const char *str) { + int key = hash_string(hash_max, str); + // printf("looking for %s key = %d\n", str, key); + struct hash_element *next = hash[key]; + while(next) { + if (!strcmp(str, next->obj->m_label)) + return next->obj; + next = next->next; + } + return 0; +} + +// -------------------------------------------------------------------- +// Global variables +// +// -------------------------------------------------------------------- + +struct hash_element **hash_cellname; + +int numCells = 0, numNets = 0; + +AbstractCell *abstractCells; +ConcreteCell *concreteCells; +ConcreteNet *concreteNets; + +// -------------------------------------------------------------------- +// Function implementations +// +// -------------------------------------------------------------------- + +void readBookshelfNets(char *filename) { + char *tok; + char buf[1024]; + const char *DELIMITERS = " \n\t:"; + int id = 0; + int t; + ConcreteCell *cell; + + FILE *netsFile = fopen(filename, "r"); + if (!netsFile) { + printf("ERROR: Could not open .nets file\n"); + exit(1); + } + + // line 1 : version + while (fgets(buf, 1024, netsFile) && (buf[0] == '\n' || buf[0] == '#')); + + // line 2 : number of nets + while (fgets(buf, 1024, netsFile) && (buf[0] == '\n' || buf[0] == '#')); + tok = strtok(buf, DELIMITERS); + tok = strtok(NULL, DELIMITERS); + numNets = atoi(tok); + printf("READ-20 : number of nets = %d\n", numNets); + concreteNets = malloc(sizeof(ConcreteNet)*numNets); + + // line 3 : number of pins + while (fgets(buf, 1024, netsFile) && (buf[0] == '\n' || buf[0] == '#')); + + // line XXX : net definitions + while(fgets(buf, 1024, netsFile)) { + if (buf[0] == '\n' || buf[0] == '#') continue; + + concreteNets[id].m_id = id; + concreteNets[id].m_weight = 1.0; + + tok = strtok(buf, DELIMITERS); + if (!!strcmp(tok, "NetDegree")) { + printf("%s\n",buf); + printf("ERROR: Incorrect format in .nets file\n"); + exit(1); + } + + tok = strtok(NULL, DELIMITERS); + concreteNets[id].m_numTerms = atoi(tok); + if (concreteNets[id].m_numTerms < 0 || + concreteNets[id].m_numTerms > 100000) { + printf("ERROR: Bad net degree\n"); + exit(1); + } + concreteNets[id].m_terms = malloc(sizeof(ConcreteCell*)* + concreteNets[id].m_numTerms); + + // read terms + t = 0; + while(t < concreteNets[id].m_numTerms && + fgets(buf, 1024, netsFile)) { + if (buf[0] == '\n' || buf[0] == '#') continue; + + // cell name + tok = strtok(buf, DELIMITERS); + cell = hash_find(hash_cellname, numCells, tok); + if (!cell) { + printf("ERROR: Could not find cell %s in .nodes file\n", tok); + exit(1); + } + concreteNets[id].m_terms[t] = cell; + t++; + } + + // add! + addConcreteNet(&(concreteNets[id])); + + id++; + } + + fclose(netsFile); +} + +void readBookshelfNodes(char *filename) { + char *tok; + char buf[1024]; + const char *DELIMITERS = " \n\t:"; + int id = 0; + + FILE *nodesFile = fopen(filename, "r"); + if (!nodesFile) { + printf("ERROR: Could not open .nodes file\n"); + exit(1); + } + + // line 1 : version + while (fgets(buf, 1024, nodesFile) && (buf[0] == '\n' || buf[0] == '#')); + + // line 2 : num nodes + while (fgets(buf, 1024, nodesFile) && (buf[0] == '\n' || buf[0] == '#')); + tok = strtok(buf, DELIMITERS); + tok = strtok(NULL, DELIMITERS); + numCells = atoi(tok); + printf("READ-10 : number of cells = %d\n", numCells); + concreteCells = malloc(sizeof(ConcreteCell)*numCells); + abstractCells = malloc(sizeof(AbstractCell)*numCells); + hash_cellname = calloc(numCells, sizeof(struct hash_element*)); + + // line 3 : num terminals + while (fgets(buf, 1024, nodesFile) && (buf[0] == '\n' || buf[0] == '#')); + + // line XXX : cell definitions + while(fgets(buf, 1024, nodesFile)) { + if (buf[0] == '\n' || buf[0] == '#') continue; + + tok = strtok(buf, DELIMITERS); + concreteCells[id].m_id = id;; + + // label + concreteCells[id].m_parent = &(abstractCells[id]); + concreteCells[id].m_label = malloc(sizeof(char)*strlen(tok)+1); + strcpy(concreteCells[id].m_label, tok); + abstractCells[id].m_label = concreteCells[id].m_label; + hash_add(hash_cellname, numCells, + &(concreteCells[id])); + + // dimensions + tok = strtok(NULL, DELIMITERS); + abstractCells[id].m_width = atof(tok); + tok = strtok(NULL, DELIMITERS); + abstractCells[id].m_height = atof(tok); + tok = strtok(NULL, DELIMITERS); + // terminal + abstractCells[id].m_pad = tok && !strcmp(tok, "terminal"); + + // add! + addConcreteCell(&(concreteCells[id])); + + // DEBUG + /* + printf("\"%s\" : %f x %f\n", concreteCells[id].m_label, + abstractCells[id].m_width, + abstractCells[id].m_height); + */ + id++; + } + + fclose(nodesFile); +} + +void readBookshelfPlacement(char *filename) { + char *tok; + char buf[1024]; + const char *DELIMITERS = " \n\t:"; + ConcreteCell *cell; + + FILE *plFile = fopen(filename, "r"); + FILE *netsFile = fopen(filename, "r"); + if (!plFile) { + printf("ERROR: Could not open .pl file\n"); + exit(1); + } + if (!netsFile) { + printf("ERROR: Could not open .nets file\n"); + exit(1); + } + + // line 1 : version + while (fgets(buf, 1024, plFile) && (buf[0] == '\n' || buf[0] == '#')); + + // line XXX : placement definitions + while(fgets(buf, 1024, plFile)) { + if (buf[0] == '\n' || buf[0] == '#') continue; + + tok = strtok(buf, DELIMITERS); + + // cell name + cell = hash_find(hash_cellname, numCells, tok); + if (!cell) { + printf("ERROR: Could not find cell %s in .nodes file\n",tok); + exit(1); + } + + // position + tok = strtok(NULL, DELIMITERS); + cell->m_x = atof(tok); + tok = strtok(NULL, DELIMITERS); + cell->m_y = atof(tok); + + // hfixed + cell->m_fixed = strtok(NULL, DELIMITERS) && + (tok = strtok(NULL, DELIMITERS)) && + !strcmp(tok, "\\FIXED"); + } + + fclose(plFile); +} + +void writeBookshelfPlacement(char *filename) { + int c = 0; + + FILE *plFile = fopen(filename, "w"); + if (!plFile) { + printf("ERROR: Could not open .pl file\n"); + exit(1); + } + + fprintf(plFile, "UCLA pl 1.0\n"); + for(c=0; cm_numTerms; t++) + if (net->m_terms[t] == cell) count++; + if (count) { + memcpy(old, net->m_terms, sizeof(ConcreteCell*)*net->m_numTerms); + net->m_terms = realloc(net->m_terms, + sizeof(ConcreteCell*)*(net->m_numTerms-count)); + t2 = 0; + for(t=0; tm_numTerms; t++) + if (old[t] != cell) net->m_terms[t2++] = old[t]; + net->m_numTerms -= count; + } + } + free(old); +} + +int main(int argc, char **argv) { + + if (argc != 4) { + printf("Usage: %s [nodes] [nets] [pl]\n", argv[0]); + exit(1); + } + + readBookshelfNodes(argv[1]); + readBookshelfNets(argv[2]); + readBookshelfPlacement(argv[3]); + + globalPreplace(0.8); + globalPlace(); + + // DEBUG net/cell removal/addition + /* + int i; + for(i=1000; i<2000; i++) { + delConcreteNet(g_place_concreteNets[i]); + delNetConnections(g_place_concreteCells[i]); + delConcreteCell(g_place_concreteCells[i]); + } + + ConcreteCell newCell[2]; + newCell[0].m_id = g_place_numCells+1; + newCell[0].m_x = 1000; + newCell[0].m_y = 1000; + newCell[0].m_fixed = false; + newCell[0].m_parent = &(abstractCells[1000]); + newCell[0].m_label = " "; + addConcreteCell(&newCell[0]); + newCell[1].m_id = g_place_numCells+3; + newCell[1].m_x = 1000; + newCell[1].m_y = 1000; + newCell[1].m_fixed = false; + newCell[1].m_parent = &(abstractCells[1000]); + newCell[1].m_label = " "; + addConcreteCell(&newCell[1]); + */ + + globalIncremental(); + + writeBookshelfPlacement(argv[3]); + + free(hash_cellname); + + return 0; +} diff --git a/abc_with_bb_support/src/sat/bsat/module.make b/abc_with_bb_support/src/sat/bsat/module.make new file mode 100644 index 000000000..2e018c078 --- /dev/null +++ b/abc_with_bb_support/src/sat/bsat/module.make @@ -0,0 +1,6 @@ +SRC += src/sat/bsat/satMem.c \ + src/sat/bsat/satInter.c \ + src/sat/bsat/satSolver.c \ + src/sat/bsat/satStore.c \ + src/sat/bsat/satTrace.c \ + src/sat/bsat/satUtil.c diff --git a/abc_with_bb_support/src/sat/bsat/satInter.c b/abc_with_bb_support/src/sat/bsat/satInter.c new file mode 100644 index 000000000..2d3d01bea --- /dev/null +++ b/abc_with_bb_support/src/sat/bsat/satInter.c @@ -0,0 +1,991 @@ +/**CFile**************************************************************** + + FileName [satInter.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [SAT sat_solver.] + + Synopsis [Interpolation package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: satInter.c,v 1.4 2005/09/16 22:55:03 casem Exp $] + +***********************************************************************/ + +#include +#include +#include +#include +#include +#include "satStore.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// variable assignments +static const lit LIT_UNDEF = 0xffffffff; + +// interpolation manager +struct Int_Man_t_ +{ + // clauses of the problems + Sto_Man_t * pCnf; // the set of CNF clauses for A and B + // various parameters + int fVerbose; // verbosiness flag + int fProofVerif; // verifies the proof + int fProofWrite; // writes the proof file + int nVarsAlloc; // the allocated size of var arrays + int nClosAlloc; // the allocated size of clause arrays + // internal BCP + int nRootSize; // the number of root level assignments + int nTrailSize; // the number of assignments made + lit * pTrail; // chronological order of assignments (size nVars) + lit * pAssigns; // assignments by variable (size nVars) + char * pSeens; // temporary mark (size nVars) + Sto_Cls_t ** pReasons; // reasons for each assignment (size nVars) + Sto_Cls_t ** pWatches; // watched clauses for each literal (size 2*nVars) + // interpolation data + int nVarsAB; // the number of global variables + char * pVarTypes; // variable type (size nVars) [1=A, 0=B, <0=AB] + unsigned * pInters; // storage for interpolants as truth tables (size nClauses) + int nIntersAlloc; // the allocated size of truth table array + int nWords; // the number of words in the truth table + // proof recording + int Counter; // counter of resolved clauses + int * pProofNums; // the proof numbers for each clause (size nClauses) + FILE * pFile; // the file for proof recording + // internal verification + lit * pResLits; // the literals of the resolvent + int nResLits; // the number of literals of the resolvent + int nResLitsAlloc;// the number of literals of the resolvent + // runtime stats + int timeBcp; // the runtime for BCP + int timeTrace; // the runtime of trace construction + int timeTotal; // the total runtime of interpolation +}; + +// procedure to get hold of the clauses' truth table +static inline unsigned * Int_ManTruthRead( Int_Man_t * p, Sto_Cls_t * pCls ) { return p->pInters + pCls->Id * p->nWords; } +static inline void Int_ManTruthClear( unsigned * p, int nWords ) { int i; for ( i = nWords - 1; i >= 0; i-- ) p[i] = 0; } +static inline void Int_ManTruthFill( unsigned * p, int nWords ) { int i; for ( i = nWords - 1; i >= 0; i-- ) p[i] = ~0; } +static inline void Int_ManTruthCopy( unsigned * p, unsigned * q, int nWords ) { int i; for ( i = nWords - 1; i >= 0; i-- ) p[i] = q[i]; } +static inline void Int_ManTruthAnd( unsigned * p, unsigned * q, int nWords ) { int i; for ( i = nWords - 1; i >= 0; i-- ) p[i] &= q[i]; } +static inline void Int_ManTruthOr( unsigned * p, unsigned * q, int nWords ) { int i; for ( i = nWords - 1; i >= 0; i-- ) p[i] |= q[i]; } +static inline void Int_ManTruthOrNot( unsigned * p, unsigned * q, int nWords ) { int i; for ( i = nWords - 1; i >= 0; i-- ) p[i] |= ~q[i]; } + +// reading/writing the proof for a clause +static inline int Int_ManProofGet( Int_Man_t * p, Sto_Cls_t * pCls ) { return p->pProofNums[pCls->Id]; } +static inline void Int_ManProofSet( Int_Man_t * p, Sto_Cls_t * pCls, int n ) { p->pProofNums[pCls->Id] = n; } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocate proof manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Int_Man_t * Int_ManAlloc( int nVarsAlloc ) +{ + Int_Man_t * p; + // allocate the manager + p = (Int_Man_t *)malloc( sizeof(Int_Man_t) ); + memset( p, 0, sizeof(Int_Man_t) ); + // verification + p->nResLitsAlloc = (1<<16); + p->pResLits = malloc( sizeof(lit) * p->nResLitsAlloc ); + // parameters + p->fProofWrite = 0; + p->fProofVerif = 1; + return p; +} + +/**Function************************************************************* + + Synopsis [Count common variables in the clauses of A and B.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Int_ManGlobalVars( Int_Man_t * p ) +{ + Sto_Cls_t * pClause; + int Var, nVarsAB, v; + + // mark the variable encountered in the clauses of A + Sto_ManForEachClauseRoot( p->pCnf, pClause ) + { + if ( !pClause->fA ) + break; + for ( v = 0; v < (int)pClause->nLits; v++ ) + p->pVarTypes[lit_var(pClause->pLits[v])] = 1; + } + + // check variables that appear in clauses of B + nVarsAB = 0; + Sto_ManForEachClauseRoot( p->pCnf, pClause ) + { + if ( pClause->fA ) + continue; + for ( v = 0; v < (int)pClause->nLits; v++ ) + { + Var = lit_var(pClause->pLits[v]); + if ( p->pVarTypes[Var] == 1 ) // var of A + { + // change it into a global variable + nVarsAB++; + p->pVarTypes[Var] = -1; + } + } + } + + // order global variables + nVarsAB = 0; + for ( v = 0; v < p->pCnf->nVars; v++ ) + if ( p->pVarTypes[v] == -1 ) + p->pVarTypes[v] -= nVarsAB++; +//printf( "There are %d global variables.\n", nVarsAB ); + return nVarsAB; +} + +/**Function************************************************************* + + Synopsis [Resize proof manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Int_ManResize( Int_Man_t * p ) +{ + // check if resizing is needed + if ( p->nVarsAlloc < p->pCnf->nVars ) + { + // find the new size + if ( p->nVarsAlloc == 0 ) + p->nVarsAlloc = 1; + while ( p->nVarsAlloc < p->pCnf->nVars ) + p->nVarsAlloc *= 2; + // resize the arrays + p->pTrail = (lit *) realloc( p->pTrail, sizeof(lit) * p->nVarsAlloc ); + p->pAssigns = (lit *) realloc( p->pAssigns, sizeof(lit) * p->nVarsAlloc ); + p->pSeens = (char *) realloc( p->pSeens, sizeof(char) * p->nVarsAlloc ); + p->pVarTypes = (char *) realloc( p->pVarTypes, sizeof(char) * p->nVarsAlloc ); + p->pReasons = (Sto_Cls_t **)realloc( p->pReasons, sizeof(Sto_Cls_t *) * p->nVarsAlloc ); + p->pWatches = (Sto_Cls_t **)realloc( p->pWatches, sizeof(Sto_Cls_t *) * p->nVarsAlloc*2 ); + } + + // clean the free space + memset( p->pAssigns , 0xff, sizeof(lit) * p->pCnf->nVars ); + memset( p->pSeens , 0, sizeof(char) * p->pCnf->nVars ); + memset( p->pVarTypes, 0, sizeof(char) * p->pCnf->nVars ); + memset( p->pReasons , 0, sizeof(Sto_Cls_t *) * p->pCnf->nVars ); + memset( p->pWatches , 0, sizeof(Sto_Cls_t *) * p->pCnf->nVars*2 ); + + // compute the number of common variables + p->nVarsAB = Int_ManGlobalVars( p ); + // compute the number of words in the truth table + p->nWords = (p->nVarsAB <= 5 ? 1 : (1 << (p->nVarsAB - 5))); + + // check if resizing of clauses is needed + if ( p->nClosAlloc < p->pCnf->nClauses ) + { + // find the new size + if ( p->nClosAlloc == 0 ) + p->nClosAlloc = 1; + while ( p->nClosAlloc < p->pCnf->nClauses ) + p->nClosAlloc *= 2; + // resize the arrays + p->pProofNums = (int *) realloc( p->pProofNums, sizeof(int) * p->nClosAlloc ); + } + memset( p->pProofNums, 0, sizeof(int) * p->pCnf->nClauses ); + + // check if resizing of truth tables is needed + if ( p->nIntersAlloc < p->nWords * p->pCnf->nClauses ) + { + p->nIntersAlloc = p->nWords * p->pCnf->nClauses; + p->pInters = (unsigned *) realloc( p->pInters, sizeof(unsigned) * p->nIntersAlloc ); + } +// memset( p->pInters, 0, sizeof(unsigned) * p->nWords * p->pCnf->nClauses ); +} + +/**Function************************************************************* + + Synopsis [Deallocate proof manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Int_ManFree( Int_Man_t * p ) +{ +/* + printf( "Runtime stats:\n" ); +PRT( "BCP ", p->timeBcp ); +PRT( "Trace ", p->timeTrace ); +PRT( "TOTAL ", p->timeTotal ); +*/ + free( p->pInters ); + free( p->pProofNums ); + free( p->pTrail ); + free( p->pAssigns ); + free( p->pSeens ); + free( p->pVarTypes ); + free( p->pReasons ); + free( p->pWatches ); + free( p->pResLits ); + free( p ); +} + + + + +/**Function************************************************************* + + Synopsis [Prints the clause.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Int_ManPrintClause( Int_Man_t * p, Sto_Cls_t * pClause ) +{ + int i; + printf( "Clause ID = %d. Proof = %d. {", pClause->Id, Int_ManProofGet(p, pClause) ); + for ( i = 0; i < (int)pClause->nLits; i++ ) + printf( " %d", pClause->pLits[i] ); + printf( " }\n" ); +} + +/**Function************************************************************* + + Synopsis [Prints the resolvent.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Int_ManPrintResolvent( lit * pResLits, int nResLits ) +{ + int i; + printf( "Resolvent: {" ); + for ( i = 0; i < nResLits; i++ ) + printf( " %d", pResLits[i] ); + printf( " }\n" ); +} + +/**Function************************************************************* + + Synopsis [Records the proof.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_PrintBinary__( FILE * pFile, unsigned Sign[], int nBits ) +{ + int Remainder, nWords; + int w, i; + + Remainder = (nBits%(sizeof(unsigned)*8)); + nWords = (nBits/(sizeof(unsigned)*8)) + (Remainder>0); + + for ( w = nWords-1; w >= 0; w-- ) + for ( i = ((w == nWords-1 && Remainder)? Remainder-1: 31); i >= 0; i-- ) + fprintf( pFile, "%c", '0' + (int)((Sign[w] & (1< 0) ); +} + +/**Function************************************************************* + + Synopsis [Prints the interpolant for one clause.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Int_ManPrintInterOne( Int_Man_t * p, Sto_Cls_t * pClause ) +{ + printf( "Clause %2d : ", pClause->Id ); + Extra_PrintBinary__( stdout, Int_ManTruthRead(p, pClause), (1 << p->nVarsAB) ); + printf( "\n" ); +} + + + +/**Function************************************************************* + + Synopsis [Adds one clause to the watcher list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Int_ManWatchClause( Int_Man_t * p, Sto_Cls_t * pClause, lit Lit ) +{ + assert( lit_check(Lit, p->pCnf->nVars) ); + if ( pClause->pLits[0] == Lit ) + pClause->pNext0 = p->pWatches[lit_neg(Lit)]; + else + { + assert( pClause->pLits[1] == Lit ); + pClause->pNext1 = p->pWatches[lit_neg(Lit)]; + } + p->pWatches[lit_neg(Lit)] = pClause; +} + + +/**Function************************************************************* + + Synopsis [Records implication.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Int_ManEnqueue( Int_Man_t * p, lit Lit, Sto_Cls_t * pReason ) +{ + int Var = lit_var(Lit); + if ( p->pAssigns[Var] != LIT_UNDEF ) + return p->pAssigns[Var] == Lit; + p->pAssigns[Var] = Lit; + p->pReasons[Var] = pReason; + p->pTrail[p->nTrailSize++] = Lit; +//printf( "assigning var %d value %d\n", Var, !lit_sign(Lit) ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Records implication.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Int_ManCancelUntil( Int_Man_t * p, int Level ) +{ + lit Lit; + int i, Var; + for ( i = p->nTrailSize - 1; i >= Level; i-- ) + { + Lit = p->pTrail[i]; + Var = lit_var( Lit ); + p->pReasons[Var] = NULL; + p->pAssigns[Var] = LIT_UNDEF; +//printf( "cancelling var %d\n", Var ); + } + p->nTrailSize = Level; +} + +/**Function************************************************************* + + Synopsis [Propagate one assignment.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Sto_Cls_t * Int_ManPropagateOne( Int_Man_t * p, lit Lit ) +{ + Sto_Cls_t ** ppPrev, * pCur, * pTemp; + lit LitF = lit_neg(Lit); + int i; + // iterate through the literals + ppPrev = p->pWatches + Lit; + for ( pCur = p->pWatches[Lit]; pCur; pCur = *ppPrev ) + { + // make sure the false literal is in the second literal of the clause + if ( pCur->pLits[0] == LitF ) + { + pCur->pLits[0] = pCur->pLits[1]; + pCur->pLits[1] = LitF; + pTemp = pCur->pNext0; + pCur->pNext0 = pCur->pNext1; + pCur->pNext1 = pTemp; + } + assert( pCur->pLits[1] == LitF ); + + // if the first literal is true, the clause is satisfied + if ( pCur->pLits[0] == p->pAssigns[lit_var(pCur->pLits[0])] ) + { + ppPrev = &pCur->pNext1; + continue; + } + + // look for a new literal to watch + for ( i = 2; i < (int)pCur->nLits; i++ ) + { + // skip the case when the literal is false + if ( lit_neg(pCur->pLits[i]) == p->pAssigns[lit_var(pCur->pLits[i])] ) + continue; + // the literal is either true or unassigned - watch it + pCur->pLits[1] = pCur->pLits[i]; + pCur->pLits[i] = LitF; + // remove this clause from the watch list of Lit + *ppPrev = pCur->pNext1; + // add this clause to the watch list of pCur->pLits[i] (now it is pCur->pLits[1]) + Int_ManWatchClause( p, pCur, pCur->pLits[1] ); + break; + } + if ( i < (int)pCur->nLits ) // found new watch + continue; + + // clause is unit - enqueue new implication + if ( Int_ManEnqueue(p, pCur->pLits[0], pCur) ) + { + ppPrev = &pCur->pNext1; + continue; + } + + // conflict detected - return the conflict clause + return pCur; + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [Propagate the current assignments.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Sto_Cls_t * Int_ManPropagate( Int_Man_t * p, int Start ) +{ + Sto_Cls_t * pClause; + int i; + int clk = clock(); + for ( i = Start; i < p->nTrailSize; i++ ) + { + pClause = Int_ManPropagateOne( p, p->pTrail[i] ); + if ( pClause ) + { +p->timeBcp += clock() - clk; + return pClause; + } + } +p->timeBcp += clock() - clk; + return NULL; +} + + +/**Function************************************************************* + + Synopsis [Writes one root clause into a file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Int_ManProofWriteOne( Int_Man_t * p, Sto_Cls_t * pClause ) +{ + Int_ManProofSet( p, pClause, ++p->Counter ); + + if ( p->fProofWrite ) + { + int v; + fprintf( p->pFile, "%d", Int_ManProofGet(p, pClause) ); + for ( v = 0; v < (int)pClause->nLits; v++ ) + fprintf( p->pFile, " %d", lit_print(pClause->pLits[v]) ); + fprintf( p->pFile, " 0 0\n" ); + } +} + +/**Function************************************************************* + + Synopsis [Traces the proof for one clause.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Int_ManProofTraceOne( Int_Man_t * p, Sto_Cls_t * pConflict, Sto_Cls_t * pFinal ) +{ + Sto_Cls_t * pReason; + int i, v, Var, PrevId; + int fPrint = 0; + int clk = clock(); + + // collect resolvent literals + if ( p->fProofVerif ) + { + assert( (int)pConflict->nLits <= p->nResLitsAlloc ); + memcpy( p->pResLits, pConflict->pLits, sizeof(lit) * pConflict->nLits ); + p->nResLits = pConflict->nLits; + } + + // mark all the variables in the conflict as seen + for ( v = 0; v < (int)pConflict->nLits; v++ ) + p->pSeens[lit_var(pConflict->pLits[v])] = 1; + + // start the anticedents +// pFinal->pAntis = Vec_PtrAlloc( 32 ); +// Vec_PtrPush( pFinal->pAntis, pConflict ); + + if ( p->pCnf->nClausesA ) + Int_ManTruthCopy( Int_ManTruthRead(p, pFinal), Int_ManTruthRead(p, pConflict), p->nWords ); + + // follow the trail backwards + PrevId = Int_ManProofGet(p, pConflict); + for ( i = p->nTrailSize - 1; i >= 0; i-- ) + { + // skip literals that are not involved + Var = lit_var(p->pTrail[i]); + if ( !p->pSeens[Var] ) + continue; + p->pSeens[Var] = 0; + + // skip literals of the resulting clause + pReason = p->pReasons[Var]; + if ( pReason == NULL ) + continue; + assert( p->pTrail[i] == pReason->pLits[0] ); + + // add the variables to seen + for ( v = 1; v < (int)pReason->nLits; v++ ) + p->pSeens[lit_var(pReason->pLits[v])] = 1; + + + // record the reason clause + assert( Int_ManProofGet(p, pReason) > 0 ); + p->Counter++; + if ( p->fProofWrite ) + fprintf( p->pFile, "%d * %d %d 0\n", p->Counter, PrevId, Int_ManProofGet(p, pReason) ); + PrevId = p->Counter; + + if ( p->pCnf->nClausesA ) + { + if ( p->pVarTypes[Var] == 1 ) // var of A + Int_ManTruthOr( Int_ManTruthRead(p, pFinal), Int_ManTruthRead(p, pReason), p->nWords ); + else + Int_ManTruthAnd( Int_ManTruthRead(p, pFinal), Int_ManTruthRead(p, pReason), p->nWords ); + } + + // resolve the temporary resolvent with the reason clause + if ( p->fProofVerif ) + { + int v1, v2; + if ( fPrint ) + Int_ManPrintResolvent( p->pResLits, p->nResLits ); + // check that the var is present in the resolvent + for ( v1 = 0; v1 < p->nResLits; v1++ ) + if ( lit_var(p->pResLits[v1]) == Var ) + break; + if ( v1 == p->nResLits ) + printf( "Recording clause %d: Cannot find variable %d in the temporary resolvent.\n", pFinal->Id, Var ); + if ( p->pResLits[v1] != lit_neg(pReason->pLits[0]) ) + printf( "Recording clause %d: The resolved variable %d is in the wrong polarity.\n", pFinal->Id, Var ); + // remove this variable from the resolvent + assert( lit_var(p->pResLits[v1]) == Var ); + p->nResLits--; + for ( ; v1 < p->nResLits; v1++ ) + p->pResLits[v1] = p->pResLits[v1+1]; + // add variables of the reason clause + for ( v2 = 1; v2 < (int)pReason->nLits; v2++ ) + { + for ( v1 = 0; v1 < p->nResLits; v1++ ) + if ( lit_var(p->pResLits[v1]) == lit_var(pReason->pLits[v2]) ) + break; + // if it is a new variable, add it to the resolvent + if ( v1 == p->nResLits ) + { + if ( p->nResLits == p->nResLitsAlloc ) + printf( "Recording clause %d: Ran out of space for intermediate resolvent.\n, pFinal->Id" ); + p->pResLits[ p->nResLits++ ] = pReason->pLits[v2]; + continue; + } + // if the variable is the same, the literal should be the same too + if ( p->pResLits[v1] == pReason->pLits[v2] ) + continue; + // the literal is different + printf( "Recording clause %d: Trying to resolve the clause with more than one opposite literal.\n", pFinal->Id ); + } + } + +// Vec_PtrPush( pFinal->pAntis, pReason ); + } + + // unmark all seen variables +// for ( i = p->nTrailSize - 1; i >= 0; i-- ) +// p->pSeens[lit_var(p->pTrail[i])] = 0; + // check that the literals are unmarked +// for ( i = p->nTrailSize - 1; i >= 0; i-- ) +// assert( p->pSeens[lit_var(p->pTrail[i])] == 0 ); + + // use the resulting clause to check the correctness of resolution + if ( p->fProofVerif ) + { + int v1, v2; + if ( fPrint ) + Int_ManPrintResolvent( p->pResLits, p->nResLits ); + for ( v1 = 0; v1 < p->nResLits; v1++ ) + { + for ( v2 = 0; v2 < (int)pFinal->nLits; v2++ ) + if ( pFinal->pLits[v2] == p->pResLits[v1] ) + break; + if ( v2 < (int)pFinal->nLits ) + continue; + break; + } + if ( v1 < p->nResLits ) + { + printf( "Recording clause %d: The final resolvent is wrong.\n", pFinal->Id ); + Int_ManPrintClause( p, pConflict ); + Int_ManPrintResolvent( p->pResLits, p->nResLits ); + Int_ManPrintClause( p, pFinal ); + } + } +p->timeTrace += clock() - clk; + + // return the proof pointer + if ( p->pCnf->nClausesA ) + { +// Int_ManPrintInterOne( p, pFinal ); + } + Int_ManProofSet( p, pFinal, p->Counter ); + return p->Counter; +} + +/**Function************************************************************* + + Synopsis [Records the proof for one clause.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Int_ManProofRecordOne( Int_Man_t * p, Sto_Cls_t * pClause ) +{ + Sto_Cls_t * pConflict; + int i; + + // empty clause never ends up there + assert( pClause->nLits > 0 ); + if ( pClause->nLits == 0 ) + printf( "Error: Empty clause is attempted.\n" ); + + // add assumptions to the trail + assert( !pClause->fRoot ); + assert( p->nTrailSize == p->nRootSize ); + for ( i = 0; i < (int)pClause->nLits; i++ ) + if ( !Int_ManEnqueue( p, lit_neg(pClause->pLits[i]), NULL ) ) + { + assert( 0 ); // impossible + return 0; + } + + // propagate the assumptions + pConflict = Int_ManPropagate( p, p->nRootSize ); + if ( pConflict == NULL ) + { + assert( 0 ); // cannot prove + return 0; + } + + // construct the proof + Int_ManProofTraceOne( p, pConflict, pClause ); + + // undo to the root level + Int_ManCancelUntil( p, p->nRootSize ); + + // add large clauses to the watched lists + if ( pClause->nLits > 1 ) + { + Int_ManWatchClause( p, pClause, pClause->pLits[0] ); + Int_ManWatchClause( p, pClause, pClause->pLits[1] ); + return 1; + } + assert( pClause->nLits == 1 ); + + // if the clause proved is unit, add it and propagate + if ( !Int_ManEnqueue( p, pClause->pLits[0], pClause ) ) + { + assert( 0 ); // impossible + return 0; + } + + // propagate the assumption + pConflict = Int_ManPropagate( p, p->nRootSize ); + if ( pConflict ) + { + // construct the proof + Int_ManProofTraceOne( p, pConflict, p->pCnf->pEmpty ); + if ( p->fVerbose ) + printf( "Found last conflict after adding unit clause number %d!\n", pClause->Id ); + return 0; + } + + // update the root level + p->nRootSize = p->nTrailSize; + return 1; +} + +/**Function************************************************************* + + Synopsis [Propagate the root clauses.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Int_ManProcessRoots( Int_Man_t * p ) +{ + Sto_Cls_t * pClause; + int Counter; + + // make sure the root clauses are preceeding the learnt clauses + Counter = 0; + Sto_ManForEachClause( p->pCnf, pClause ) + { + assert( (int)pClause->fA == (Counter < (int)p->pCnf->nClausesA) ); + assert( (int)pClause->fRoot == (Counter < (int)p->pCnf->nRoots) ); + Counter++; + } + assert( p->pCnf->nClauses == Counter ); + + // make sure the last clause if empty + assert( p->pCnf->pTail->nLits == 0 ); + + // go through the root unit clauses + p->nTrailSize = 0; + Sto_ManForEachClauseRoot( p->pCnf, pClause ) + { + // create watcher lists for the root clauses + if ( pClause->nLits > 1 ) + { + Int_ManWatchClause( p, pClause, pClause->pLits[0] ); + Int_ManWatchClause( p, pClause, pClause->pLits[1] ); + } + // empty clause and large clauses + if ( pClause->nLits != 1 ) + continue; + // unit clause + assert( lit_check(pClause->pLits[0], p->pCnf->nVars) ); + if ( !Int_ManEnqueue( p, pClause->pLits[0], pClause ) ) + { + // detected root level conflict + printf( "Error in Int_ManProcessRoots(): Detected a root-level conflict too early!\n" ); + assert( 0 ); + return 0; + } + } + + // propagate the root unit clauses + pClause = Int_ManPropagate( p, 0 ); + if ( pClause ) + { + // detected root level conflict + Int_ManProofTraceOne( p, pClause, p->pCnf->pEmpty ); + if ( p->fVerbose ) + printf( "Found root level conflict!\n" ); + return 0; + } + + // set the root level + p->nRootSize = p->nTrailSize; + return 1; +} + +/**Function************************************************************* + + Synopsis [Records the proof.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Int_ManPrepareInter( Int_Man_t * p ) +{ + // elementary truth tables + unsigned uTruths[8][8] = { + { 0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA,0xAAAAAAAA }, + { 0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC }, + { 0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0,0xF0F0F0F0 }, + { 0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00,0xFF00FF00 }, + { 0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000,0xFFFF0000 }, + { 0x00000000,0xFFFFFFFF,0x00000000,0xFFFFFFFF,0x00000000,0xFFFFFFFF,0x00000000,0xFFFFFFFF }, + { 0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF,0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF }, + { 0x00000000,0x00000000,0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF } + }; + Sto_Cls_t * pClause; + int Var, VarAB, v; + assert( p->nVarsAB <= 8 ); + + // set interpolants for root clauses + Sto_ManForEachClauseRoot( p->pCnf, pClause ) + { + if ( !pClause->fA ) // clause of B + { + Int_ManTruthFill( Int_ManTruthRead(p, pClause), p->nWords ); +// Int_ManPrintInterOne( p, pClause ); + continue; + } + // clause of A + Int_ManTruthClear( Int_ManTruthRead(p, pClause), p->nWords ); + for ( v = 0; v < (int)pClause->nLits; v++ ) + { + Var = lit_var(pClause->pLits[v]); + if ( p->pVarTypes[Var] < 0 ) // global var + { + VarAB = -p->pVarTypes[Var]-1; + assert( VarAB >= 0 && VarAB < p->nVarsAB ); + if ( lit_sign(pClause->pLits[v]) ) // negative var + Int_ManTruthOrNot( Int_ManTruthRead(p, pClause), uTruths[VarAB], p->nWords ); + else + Int_ManTruthOr( Int_ManTruthRead(p, pClause), uTruths[VarAB], p->nWords ); + } + } +// Int_ManPrintInterOne( p, pClause ); + } +} + +/**Function************************************************************* + + Synopsis [Computes interpolant for the given CNF.] + + Description [Returns the number of common variable found and interpolant. + Returns 0, if something did not work.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Int_ManInterpolate( Int_Man_t * p, Sto_Man_t * pCnf, int fVerbose, unsigned ** ppResult ) +{ + Sto_Cls_t * pClause; + int RetValue = 1; + int clkTotal = clock(); + + // check that the CNF makes sense + assert( pCnf->nVars > 0 && pCnf->nClauses > 0 ); + p->pCnf = pCnf; + p->fVerbose = fVerbose; + *ppResult = NULL; + + // adjust the manager + Int_ManResize( p ); + + // prepare the interpolant computation + Int_ManPrepareInter( p ); + + // construct proof for each clause + // start the proof + if ( p->fProofWrite ) + { + p->pFile = fopen( "proof.cnf_", "w" ); + p->Counter = 0; + } + + // write the root clauses + Sto_ManForEachClauseRoot( p->pCnf, pClause ) + Int_ManProofWriteOne( p, pClause ); + + // propagate root level assignments + if ( Int_ManProcessRoots( p ) ) + { + // if there is no conflict, consider learned clauses + Sto_ManForEachClause( p->pCnf, pClause ) + { + if ( pClause->fRoot ) + continue; + if ( !Int_ManProofRecordOne( p, pClause ) ) + { + RetValue = 0; + break; + } + } + } + + // stop the proof + if ( p->fProofWrite ) + { + fclose( p->pFile ); + p->pFile = NULL; + } + + if ( fVerbose ) + { + printf( "Vars = %d. Roots = %d. Learned = %d. Resol steps = %d. Ave = %.2f. Mem = %.2f Mb\n", + p->pCnf->nVars, p->pCnf->nRoots, p->pCnf->nClauses-p->pCnf->nRoots, p->Counter, + 1.0*(p->Counter-p->pCnf->nRoots)/(p->pCnf->nClauses-p->pCnf->nRoots), + 1.0*Sto_ManMemoryReport(p->pCnf)/(1<<20) ); +p->timeTotal += clock() - clkTotal; + } + + *ppResult = Int_ManTruthRead( p, p->pCnf->pTail ); + return p->nVarsAB; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/bsat/satMem.c b/abc_with_bb_support/src/sat/bsat/satMem.c new file mode 100644 index 000000000..90d700759 --- /dev/null +++ b/abc_with_bb_support/src/sat/bsat/satMem.c @@ -0,0 +1,527 @@ +/**CFile**************************************************************** + + FileName [satMem.c] + + PackageName [SAT solver.] + + Synopsis [Memory management.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 1, 2004.] + + Revision [$Id: satMem.c,v 1.0 2004/01/01 1:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "satMem.h" +#include "extra.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +struct Sat_MmFixed_t_ +{ + // information about individual entries + int nEntrySize; // the size of one entry + int nEntriesAlloc; // the total number of entries allocated + int nEntriesUsed; // the number of entries in use + int nEntriesMax; // the max number of entries in use + char * pEntriesFree; // the linked list of free entries + + // this is where the memory is stored + int nChunkSize; // the size of one chunk + int nChunksAlloc; // the maximum number of memory chunks + int nChunks; // the current number of memory chunks + char ** pChunks; // the allocated memory + + // statistics + int nMemoryUsed; // memory used in the allocated entries + int nMemoryAlloc; // memory allocated +}; + +struct Sat_MmFlex_t_ +{ + // information about individual entries + int nEntriesUsed; // the number of entries allocated + char * pCurrent; // the current pointer to free memory + char * pEnd; // the first entry outside the free memory + + // this is where the memory is stored + int nChunkSize; // the size of one chunk + int nChunksAlloc; // the maximum number of memory chunks + int nChunks; // the current number of memory chunks + char ** pChunks; // the allocated memory + + // statistics + int nMemoryUsed; // memory used in the allocated entries + int nMemoryAlloc; // memory allocated +}; + +struct Sat_MmStep_t_ +{ + int nMems; // the number of fixed memory managers employed + Sat_MmFixed_t ** pMems; // memory managers: 2^1 words, 2^2 words, etc + int nMapSize; // the size of the memory array + Sat_MmFixed_t ** pMap; // maps the number of bytes into its memory manager +}; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates memory pieces of fixed size.] + + Description [The size of the chunk is computed as the minimum of + 1024 entries and 64K. Can only work with entry size at least 4 byte long.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Sat_MmFixed_t * Sat_MmFixedStart( int nEntrySize ) +{ + Sat_MmFixed_t * p; + + p = ALLOC( Sat_MmFixed_t, 1 ); + memset( p, 0, sizeof(Sat_MmFixed_t) ); + + p->nEntrySize = nEntrySize; + p->nEntriesAlloc = 0; + p->nEntriesUsed = 0; + p->pEntriesFree = NULL; + + if ( nEntrySize * (1 << 10) < (1<<16) ) + p->nChunkSize = (1 << 10); + else + p->nChunkSize = (1<<16) / nEntrySize; + if ( p->nChunkSize < 8 ) + p->nChunkSize = 8; + + p->nChunksAlloc = 64; + p->nChunks = 0; + p->pChunks = ALLOC( char *, p->nChunksAlloc ); + + p->nMemoryUsed = 0; + p->nMemoryAlloc = 0; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sat_MmFixedStop( Sat_MmFixed_t * p, int fVerbose ) +{ + int i; + if ( p == NULL ) + return; + if ( fVerbose ) + { + printf( "Fixed memory manager: Entry = %5d. Chunk = %5d. Chunks used = %5d.\n", + p->nEntrySize, p->nChunkSize, p->nChunks ); + printf( " Entries used = %8d. Entries peak = %8d. Memory used = %8d. Memory alloc = %8d.\n", + p->nEntriesUsed, p->nEntriesMax, p->nEntrySize * p->nEntriesUsed, p->nMemoryAlloc ); + } + for ( i = 0; i < p->nChunks; i++ ) + free( p->pChunks[i] ); + free( p->pChunks ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Sat_MmFixedEntryFetch( Sat_MmFixed_t * p ) +{ + char * pTemp; + int i; + + // check if there are still free entries + if ( p->nEntriesUsed == p->nEntriesAlloc ) + { // need to allocate more entries + assert( p->pEntriesFree == NULL ); + if ( p->nChunks == p->nChunksAlloc ) + { + p->nChunksAlloc *= 2; + p->pChunks = REALLOC( char *, p->pChunks, p->nChunksAlloc ); + } + p->pEntriesFree = ALLOC( char, p->nEntrySize * p->nChunkSize ); + p->nMemoryAlloc += p->nEntrySize * p->nChunkSize; + // transform these entries into a linked list + pTemp = p->pEntriesFree; + for ( i = 1; i < p->nChunkSize; i++ ) + { + *((char **)pTemp) = pTemp + p->nEntrySize; + pTemp += p->nEntrySize; + } + // set the last link + *((char **)pTemp) = NULL; + // add the chunk to the chunk storage + p->pChunks[ p->nChunks++ ] = p->pEntriesFree; + // add to the number of entries allocated + p->nEntriesAlloc += p->nChunkSize; + } + // incrememt the counter of used entries + p->nEntriesUsed++; + if ( p->nEntriesMax < p->nEntriesUsed ) + p->nEntriesMax = p->nEntriesUsed; + // return the first entry in the free entry list + pTemp = p->pEntriesFree; + p->pEntriesFree = *((char **)pTemp); + return pTemp; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sat_MmFixedEntryRecycle( Sat_MmFixed_t * p, char * pEntry ) +{ + // decrement the counter of used entries + p->nEntriesUsed--; + // add the entry to the linked list of free entries + *((char **)pEntry) = p->pEntriesFree; + p->pEntriesFree = pEntry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [Relocates all the memory except the first chunk.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sat_MmFixedRestart( Sat_MmFixed_t * p ) +{ + int i; + char * pTemp; + + // deallocate all chunks except the first one + for ( i = 1; i < p->nChunks; i++ ) + free( p->pChunks[i] ); + p->nChunks = 1; + // transform these entries into a linked list + pTemp = p->pChunks[0]; + for ( i = 1; i < p->nChunkSize; i++ ) + { + *((char **)pTemp) = pTemp + p->nEntrySize; + pTemp += p->nEntrySize; + } + // set the last link + *((char **)pTemp) = NULL; + // set the free entry list + p->pEntriesFree = p->pChunks[0]; + // set the correct statistics + p->nMemoryAlloc = p->nEntrySize * p->nChunkSize; + p->nMemoryUsed = 0; + p->nEntriesAlloc = p->nChunkSize; + p->nEntriesUsed = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sat_MmFixedReadMemUsage( Sat_MmFixed_t * p ) +{ + return p->nMemoryAlloc; +} + + + +/**Function************************************************************* + + Synopsis [Allocates entries of flexible size.] + + Description [Can only work with entry size at least 4 byte long.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Sat_MmFlex_t * Sat_MmFlexStart() +{ + Sat_MmFlex_t * p; + + p = ALLOC( Sat_MmFlex_t, 1 ); + memset( p, 0, sizeof(Sat_MmFlex_t) ); + + p->nEntriesUsed = 0; + p->pCurrent = NULL; + p->pEnd = NULL; + + p->nChunkSize = (1 << 12); + p->nChunksAlloc = 64; + p->nChunks = 0; + p->pChunks = ALLOC( char *, p->nChunksAlloc ); + + p->nMemoryUsed = 0; + p->nMemoryAlloc = 0; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sat_MmFlexStop( Sat_MmFlex_t * p, int fVerbose ) +{ + int i; + if ( p == NULL ) + return; + if ( fVerbose ) + { + printf( "Flexible memory manager: Chunk size = %d. Chunks used = %d.\n", + p->nChunkSize, p->nChunks ); + printf( " Entries used = %d. Memory used = %d. Memory alloc = %d.\n", + p->nEntriesUsed, p->nMemoryUsed, p->nMemoryAlloc ); + } + for ( i = 0; i < p->nChunks; i++ ) + free( p->pChunks[i] ); + free( p->pChunks ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Sat_MmFlexEntryFetch( Sat_MmFlex_t * p, int nBytes ) +{ + char * pTemp; + // check if there are still free entries + if ( p->pCurrent == NULL || p->pCurrent + nBytes > p->pEnd ) + { // need to allocate more entries + if ( p->nChunks == p->nChunksAlloc ) + { + p->nChunksAlloc *= 2; + p->pChunks = REALLOC( char *, p->pChunks, p->nChunksAlloc ); + } + if ( nBytes > p->nChunkSize ) + { + // resize the chunk size if more memory is requested than it can give + // (ideally, this should never happen) + p->nChunkSize = 2 * nBytes; + } + p->pCurrent = ALLOC( char, p->nChunkSize ); + p->pEnd = p->pCurrent + p->nChunkSize; + p->nMemoryAlloc += p->nChunkSize; + // add the chunk to the chunk storage + p->pChunks[ p->nChunks++ ] = p->pCurrent; + } + assert( p->pCurrent + nBytes <= p->pEnd ); + // increment the counter of used entries + p->nEntriesUsed++; + // keep track of the memory used + p->nMemoryUsed += nBytes; + // return the next entry + pTemp = p->pCurrent; + p->pCurrent += nBytes; + return pTemp; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sat_MmFlexReadMemUsage( Sat_MmFlex_t * p ) +{ + return p->nMemoryAlloc; +} + + + + + +/**Function************************************************************* + + Synopsis [Starts the hierarchical memory manager.] + + Description [This manager can allocate entries of any size. + Iternally they are mapped into the entries with the number of bytes + equal to the power of 2. The smallest entry size is 8 bytes. The + next one is 16 bytes etc. So, if the user requests 6 bytes, he gets + 8 byte entry. If we asks for 25 bytes, he gets 32 byte entry etc. + The input parameters "nSteps" says how many fixed memory managers + are employed internally. Calling this procedure with nSteps equal + to 10 results in 10 hierarchically arranged internal memory managers, + which can allocate up to 4096 (1Kb) entries. Requests for larger + entries are handed over to malloc() and then free()ed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Sat_MmStep_t * Sat_MmStepStart( int nSteps ) +{ + Sat_MmStep_t * p; + int i, k; + p = ALLOC( Sat_MmStep_t, 1 ); + p->nMems = nSteps; + // start the fixed memory managers + p->pMems = ALLOC( Sat_MmFixed_t *, p->nMems ); + for ( i = 0; i < p->nMems; i++ ) + p->pMems[i] = Sat_MmFixedStart( (8<nMapSize = (4<nMems); + p->pMap = ALLOC( Sat_MmFixed_t *, p->nMapSize+1 ); + p->pMap[0] = NULL; + for ( k = 1; k <= 4; k++ ) + p->pMap[k] = p->pMems[0]; + for ( i = 0; i < p->nMems; i++ ) + for ( k = (4<pMap[k] = p->pMems[i]; +//for ( i = 1; i < 100; i ++ ) +//printf( "%10d: size = %10d\n", i, p->pMap[i]->nEntrySize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the memory manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sat_MmStepStop( Sat_MmStep_t * p, int fVerbose ) +{ + int i; + for ( i = 0; i < p->nMems; i++ ) + Sat_MmFixedStop( p->pMems[i], fVerbose ); + free( p->pMems ); + free( p->pMap ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Creates the entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Sat_MmStepEntryFetch( Sat_MmStep_t * p, int nBytes ) +{ + if ( nBytes == 0 ) + return NULL; + if ( nBytes > p->nMapSize ) + { +// printf( "Allocating %d bytes.\n", nBytes ); + return ALLOC( char, nBytes ); + } + return Sat_MmFixedEntryFetch( p->pMap[nBytes] ); +} + + +/**Function************************************************************* + + Synopsis [Recycles the entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sat_MmStepEntryRecycle( Sat_MmStep_t * p, char * pEntry, int nBytes ) +{ + if ( nBytes == 0 ) + return; + if ( nBytes > p->nMapSize ) + { + free( pEntry ); + return; + } + Sat_MmFixedEntryRecycle( p->pMap[nBytes], pEntry ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sat_MmStepReadMemUsage( Sat_MmStep_t * p ) +{ + int i, nMemTotal = 0; + for ( i = 0; i < p->nMems; i++ ) + nMemTotal += p->pMems[i]->nMemoryAlloc; + return nMemTotal; +} diff --git a/abc_with_bb_support/src/sat/bsat/satMem.h b/abc_with_bb_support/src/sat/bsat/satMem.h new file mode 100644 index 000000000..05a6056b2 --- /dev/null +++ b/abc_with_bb_support/src/sat/bsat/satMem.h @@ -0,0 +1,78 @@ +/**CFile**************************************************************** + + FileName [satMem.h] + + PackageName [SAT solver.] + + Synopsis [Memory management.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 1, 2004.] + + Revision [$Id: satMem.h,v 1.0 2004/01/01 1:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __SAT_MEM_H__ +#define __SAT_MEM_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//#include "leaks.h" +#include +#include + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Sat_MmFixed_t_ Sat_MmFixed_t; +typedef struct Sat_MmFlex_t_ Sat_MmFlex_t; +typedef struct Sat_MmStep_t_ Sat_MmStep_t; + +//////////////////////////////////////////////////////////////////////// +/// GLOBAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// fixed-size-block memory manager +extern Sat_MmFixed_t * Sat_MmFixedStart( int nEntrySize ); +extern void Sat_MmFixedStop( Sat_MmFixed_t * p, int fVerbose ); +extern char * Sat_MmFixedEntryFetch( Sat_MmFixed_t * p ); +extern void Sat_MmFixedEntryRecycle( Sat_MmFixed_t * p, char * pEntry ); +extern void Sat_MmFixedRestart( Sat_MmFixed_t * p ); +extern int Sat_MmFixedReadMemUsage( Sat_MmFixed_t * p ); +// flexible-size-block memory manager +extern Sat_MmFlex_t * Sat_MmFlexStart(); +extern void Sat_MmFlexStop( Sat_MmFlex_t * p, int fVerbose ); +extern char * Sat_MmFlexEntryFetch( Sat_MmFlex_t * p, int nBytes ); +extern int Sat_MmFlexReadMemUsage( Sat_MmFlex_t * p ); +// hierarchical memory manager +extern Sat_MmStep_t * Sat_MmStepStart( int nSteps ); +extern void Sat_MmStepStop( Sat_MmStep_t * p, int fVerbose ); +extern char * Sat_MmStepEntryFetch( Sat_MmStep_t * p, int nBytes ); +extern void Sat_MmStepEntryRecycle( Sat_MmStep_t * p, char * pEntry, int nBytes ); +extern int Sat_MmStepReadMemUsage( Sat_MmStep_t * p ); + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/sat/bsat/satSolver.c b/abc_with_bb_support/src/sat/bsat/satSolver.c new file mode 100644 index 000000000..e7e7af3fa --- /dev/null +++ b/abc_with_bb_support/src/sat/bsat/satSolver.c @@ -0,0 +1,1354 @@ +/************************************************************************************************** +MiniSat -- Copyright (c) 2005, Niklas Sorensson +http://www.cs.chalmers.se/Cs/Research/FormalMethods/MiniSat/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ +// Modified to compile with MS Visual Studio 6.0 by Alan Mishchenko + +#include +#include +#include +#include + +#include "satSolver.h" + +//#define SAT_USE_SYSTEM_MEMORY_MANAGEMENT + +//================================================================================================= +// Debug: + +//#define VERBOSEDEBUG + +// For derivation output (verbosity level 2) +#define L_IND "%-*d" +#define L_ind sat_solver_dlevel(s)*3+3,sat_solver_dlevel(s) +#define L_LIT "%sx%d" +#define L_lit(p) lit_sign(p)?"~":"", (lit_var(p)) + +// Just like 'assert()' but expression will be evaluated in the release version as well. +static inline void check(int expr) { assert(expr); } + +static void printlits(lit* begin, lit* end) +{ + int i; + for (i = 0; i < end - begin; i++) + printf(L_LIT" ",L_lit(begin[i])); +} + +//================================================================================================= +// Random numbers: + + +// Returns a random float 0 <= x < 1. Seed must never be 0. +static inline double drand(double* seed) { + int q; + *seed *= 1389796; + q = (int)(*seed / 2147483647); + *seed -= (double)q * 2147483647; + return *seed / 2147483647; } + + +// Returns a random integer 0 <= x < size. Seed must never be 0. +static inline int irand(double* seed, int size) { + return (int)(drand(seed) * size); } + + +//================================================================================================= +// Predeclarations: + +static void sat_solver_sort(void** array, int size, int(*comp)(const void *, const void *)); + +//================================================================================================= +// Clause datatype + minor functions: + +struct clause_t +{ + int size_learnt; + lit lits[0]; +}; + +static inline int clause_size (clause* c) { return c->size_learnt >> 1; } +static inline lit* clause_begin (clause* c) { return c->lits; } +static inline int clause_learnt (clause* c) { return c->size_learnt & 1; } +static inline float clause_activity (clause* c) { return *((float*)&c->lits[c->size_learnt>>1]); } +static inline void clause_setactivity(clause* c, float a) { *((float*)&c->lits[c->size_learnt>>1]) = a; } + +//================================================================================================= +// Encode literals in clause pointers: + +static clause* clause_from_lit (lit l) { return (clause*)((unsigned long)l + (unsigned long)l + 1); } +static bool clause_is_lit (clause* c) { return ((unsigned long)c & 1); } +static lit clause_read_lit (clause* c) { return (lit)((unsigned long)c >> 1); } + +//================================================================================================= +// Simple helpers: + +static inline int sat_solver_dlevel(sat_solver* s) { return veci_size(&s->trail_lim); } +static inline vecp* sat_solver_read_wlist(sat_solver* s, lit l) { return &s->wlists[l]; } +static inline void vecp_remove(vecp* v, void* e) +{ + void** ws = vecp_begin(v); + int j = 0; + for (; ws[j] != e ; j++); + assert(j < vecp_size(v)); + for (; j < vecp_size(v)-1; j++) ws[j] = ws[j+1]; + vecp_resize(v,vecp_size(v)-1); +} + +//================================================================================================= +// Variable order functions: + +static inline void order_update(sat_solver* s, int v) // updateorder +{ + int* orderpos = s->orderpos; + double* activity = s->activity; + int* heap = veci_begin(&s->order); + int i = orderpos[v]; + int x = heap[i]; + int parent = (i - 1) / 2; + + assert(s->orderpos[v] != -1); + + while (i != 0 && activity[x] > activity[heap[parent]]){ + heap[i] = heap[parent]; + orderpos[heap[i]] = i; + i = parent; + parent = (i - 1) / 2; + } + heap[i] = x; + orderpos[x] = i; +} + +static inline void order_assigned(sat_solver* s, int v) +{ +} + +static inline void order_unassigned(sat_solver* s, int v) // undoorder +{ + int* orderpos = s->orderpos; + if (orderpos[v] == -1){ + orderpos[v] = veci_size(&s->order); + veci_push(&s->order,v); + order_update(s,v); +//printf( "+%d ", v ); + } +} + +static int order_select(sat_solver* s, float random_var_freq) // selectvar +{ + int* heap; + double* activity; + int* orderpos; + + lbool* values = s->assigns; + + // Random decision: + if (drand(&s->random_seed) < random_var_freq){ + int next = irand(&s->random_seed,s->size); + assert(next >= 0 && next < s->size); + if (values[next] == l_Undef) + return next; + } + + // Activity based decision: + + heap = veci_begin(&s->order); + activity = s->activity; + orderpos = s->orderpos; + + + while (veci_size(&s->order) > 0){ + int next = heap[0]; + int size = veci_size(&s->order)-1; + int x = heap[size]; + + veci_resize(&s->order,size); + + orderpos[next] = -1; + + if (size > 0){ + double act = activity[x]; + + int i = 0; + int child = 1; + + + while (child < size){ + if (child+1 < size && activity[heap[child]] < activity[heap[child+1]]) + child++; + + assert(child < size); + + if (act >= activity[heap[child]]) + break; + + heap[i] = heap[child]; + orderpos[heap[i]] = i; + i = child; + child = 2 * child + 1; + } + heap[i] = x; + orderpos[heap[i]] = i; + } + +//printf( "-%d ", next ); + if (values[next] == l_Undef) + return next; + } + + return var_Undef; +} + +//================================================================================================= +// Activity functions: + +static inline void act_var_rescale(sat_solver* s) { + double* activity = s->activity; + int i; + for (i = 0; i < s->size; i++) + activity[i] *= 1e-100; + s->var_inc *= 1e-100; +} + +static inline void act_var_bump(sat_solver* s, int v) { + s->activity[v] += s->var_inc; + if (s->activity[v] > 1e100) + act_var_rescale(s); + //printf("bump %d %f\n", v-1, activity[v]); + if (s->orderpos[v] != -1) + order_update(s,v); +} + +static inline void act_var_bump_factor(sat_solver* s, int v) { + s->activity[v] += (s->var_inc * s->factors[v]); + if (s->activity[v] > 1e100) + act_var_rescale(s); + //printf("bump %d %f\n", v-1, activity[v]); + if (s->orderpos[v] != -1) + order_update(s,v); +} + +static inline void act_var_decay(sat_solver* s) { s->var_inc *= s->var_decay; } + +static inline void act_clause_rescale(sat_solver* s) { + clause** cs = (clause**)vecp_begin(&s->learnts); + int i; + for (i = 0; i < vecp_size(&s->learnts); i++){ + float a = clause_activity(cs[i]); + clause_setactivity(cs[i], a * (float)1e-20); + } + s->cla_inc *= (float)1e-20; +} + + +static inline void act_clause_bump(sat_solver* s, clause *c) { + float a = clause_activity(c) + s->cla_inc; + clause_setactivity(c,a); + if (a > 1e20) act_clause_rescale(s); +} + +static inline void act_clause_decay(sat_solver* s) { s->cla_inc *= s->cla_decay; } + +//================================================================================================= +// Clause functions: + +/* pre: size > 1 && no variable occurs twice + */ +static clause* clause_new(sat_solver* s, lit* begin, lit* end, int learnt) +{ + int size; + clause* c; + int i; + + assert(end - begin > 1); + assert(learnt >= 0 && learnt < 2); + size = end - begin; +// c = (clause*)malloc(sizeof(clause) + sizeof(lit) * size + learnt * sizeof(float)); +#ifdef SAT_USE_SYSTEM_MEMORY_MANAGEMENT + c = (clause*)malloc(sizeof(clause) + sizeof(lit) * size + learnt * sizeof(float)); +#else + c = (clause*)Sat_MmStepEntryFetch( s->pMem, sizeof(clause) + sizeof(lit) * size + learnt * sizeof(float) ); +#endif + + c->size_learnt = (size << 1) | learnt; + assert(((unsigned int)c & 1) == 0); + + for (i = 0; i < size; i++) + c->lits[i] = begin[i]; + + if (learnt) + *((float*)&c->lits[size]) = 0.0; + + assert(begin[0] >= 0); + assert(begin[0] < s->size*2); + assert(begin[1] >= 0); + assert(begin[1] < s->size*2); + + assert(lit_neg(begin[0]) < s->size*2); + assert(lit_neg(begin[1]) < s->size*2); + + //vecp_push(sat_solver_read_wlist(s,lit_neg(begin[0])),(void*)c); + //vecp_push(sat_solver_read_wlist(s,lit_neg(begin[1])),(void*)c); + + vecp_push(sat_solver_read_wlist(s,lit_neg(begin[0])),(void*)(size > 2 ? c : clause_from_lit(begin[1]))); + vecp_push(sat_solver_read_wlist(s,lit_neg(begin[1])),(void*)(size > 2 ? c : clause_from_lit(begin[0]))); + + return c; +} + + +static void clause_remove(sat_solver* s, clause* c) +{ + lit* lits = clause_begin(c); + assert(lit_neg(lits[0]) < s->size*2); + assert(lit_neg(lits[1]) < s->size*2); + + //vecp_remove(sat_solver_read_wlist(s,lit_neg(lits[0])),(void*)c); + //vecp_remove(sat_solver_read_wlist(s,lit_neg(lits[1])),(void*)c); + + assert(lits[0] < s->size*2); + vecp_remove(sat_solver_read_wlist(s,lit_neg(lits[0])),(void*)(clause_size(c) > 2 ? c : clause_from_lit(lits[1]))); + vecp_remove(sat_solver_read_wlist(s,lit_neg(lits[1])),(void*)(clause_size(c) > 2 ? c : clause_from_lit(lits[0]))); + + if (clause_learnt(c)){ + s->stats.learnts--; + s->stats.learnts_literals -= clause_size(c); + }else{ + s->stats.clauses--; + s->stats.clauses_literals -= clause_size(c); + } + +#ifdef SAT_USE_SYSTEM_MEMORY_MANAGEMENT + free(c); +#else + Sat_MmStepEntryRecycle( s->pMem, (char *)c, sizeof(clause) + sizeof(lit) * clause_size(c) + clause_learnt(c) * sizeof(float) ); +#endif +} + + +static lbool clause_simplify(sat_solver* s, clause* c) +{ + lit* lits = clause_begin(c); + lbool* values = s->assigns; + int i; + + assert(sat_solver_dlevel(s) == 0); + + for (i = 0; i < clause_size(c); i++){ + lbool sig = !lit_sign(lits[i]); sig += sig - 1; + if (values[lit_var(lits[i])] == sig) + return l_True; + } + return l_False; +} + +//================================================================================================= +// Minor (solver) functions: + +void sat_solver_setnvars(sat_solver* s,int n) +{ + int var; + + if (s->cap < n){ + + while (s->cap < n) s->cap = s->cap*2+1; + + s->wlists = (vecp*) realloc(s->wlists, sizeof(vecp)*s->cap*2); + s->activity = (double*) realloc(s->activity, sizeof(double)*s->cap); + s->factors = (double*) realloc(s->factors, sizeof(double)*s->cap); + s->assigns = (lbool*) realloc(s->assigns, sizeof(lbool)*s->cap); + s->orderpos = (int*) realloc(s->orderpos, sizeof(int)*s->cap); + s->reasons = (clause**)realloc(s->reasons, sizeof(clause*)*s->cap); + s->levels = (int*) realloc(s->levels, sizeof(int)*s->cap); + s->tags = (lbool*) realloc(s->tags, sizeof(lbool)*s->cap); + s->trail = (lit*) realloc(s->trail, sizeof(lit)*s->cap); + } + + for (var = s->size; var < n; var++){ + vecp_new(&s->wlists[2*var]); + vecp_new(&s->wlists[2*var+1]); + s->activity [var] = 0; + s->factors [var] = 0; + s->assigns [var] = l_Undef; + s->orderpos [var] = veci_size(&s->order); + s->reasons [var] = (clause*)0; + s->levels [var] = 0; + s->tags [var] = l_Undef; + + /* does not hold because variables enqueued at top level will not be reinserted in the heap + assert(veci_size(&s->order) == var); + */ + veci_push(&s->order,var); + order_update(s, var); + } + + s->size = n > s->size ? n : s->size; +} + + +static inline bool enqueue(sat_solver* s, lit l, clause* from) +{ + lbool* values = s->assigns; + int v = lit_var(l); + lbool val = values[v]; +#ifdef VERBOSEDEBUG + printf(L_IND"enqueue("L_LIT")\n", L_ind, L_lit(l)); +#endif + + lbool sig = !lit_sign(l); sig += sig - 1; + if (val != l_Undef){ + return val == sig; + }else{ + // New fact -- store it. +#ifdef VERBOSEDEBUG + printf(L_IND"bind("L_LIT")\n", L_ind, L_lit(l)); +#endif + int* levels = s->levels; + clause** reasons = s->reasons; + + values [v] = sig; + levels [v] = sat_solver_dlevel(s); + reasons[v] = from; + s->trail[s->qtail++] = l; + + order_assigned(s, v); + return true; + } +} + + +static inline void assume(sat_solver* s, lit l){ + assert(s->qtail == s->qhead); + assert(s->assigns[lit_var(l)] == l_Undef); +#ifdef VERBOSEDEBUG + printf(L_IND"assume("L_LIT")\n", L_ind, L_lit(l)); +#endif + veci_push(&s->trail_lim,s->qtail); + enqueue(s,l,(clause*)0); +} + + +static inline void sat_solver_canceluntil(sat_solver* s, int level) { + lit* trail; + lbool* values; + clause** reasons; + int bound; + int c; + + if (sat_solver_dlevel(s) <= level) + return; + + trail = s->trail; + values = s->assigns; + reasons = s->reasons; + bound = (veci_begin(&s->trail_lim))[level]; + + //////////////////////////////////////// + // added to cancel all assignments +// if ( level == -1 ) +// bound = 0; + //////////////////////////////////////// + + for (c = s->qtail-1; c >= bound; c--) { + int x = lit_var(trail[c]); + values [x] = l_Undef; + reasons[x] = (clause*)0; + } + + for (c = s->qhead-1; c >= bound; c--) + order_unassigned(s,lit_var(trail[c])); + + s->qhead = s->qtail = bound; + veci_resize(&s->trail_lim,level); +} + +static void sat_solver_record(sat_solver* s, veci* cls) +{ + lit* begin = veci_begin(cls); + lit* end = begin + veci_size(cls); + clause* c = (veci_size(cls) > 1) ? clause_new(s,begin,end,1) : (clause*)0; + enqueue(s,*begin,c); + + /////////////////////////////////// + // add clause to internal storage + if ( s->pStore ) + { + extern int Sto_ManAddClause( void * p, lit * pBeg, lit * pEnd ); + int RetValue = Sto_ManAddClause( s->pStore, begin, end ); + assert( RetValue ); + } + /////////////////////////////////// + + assert(veci_size(cls) > 0); + + if (c != 0) { + vecp_push(&s->learnts,c); + act_clause_bump(s,c); + s->stats.learnts++; + s->stats.learnts_literals += veci_size(cls); + } +} + + +static double sat_solver_progress(sat_solver* s) +{ + lbool* values = s->assigns; + int* levels = s->levels; + int i; + + double progress = 0; + double F = 1.0 / s->size; + for (i = 0; i < s->size; i++) + if (values[i] != l_Undef) + progress += pow(F, levels[i]); + return progress / s->size; +} + +//================================================================================================= +// Major methods: + +static bool sat_solver_lit_removable(sat_solver* s, lit l, int minl) +{ + lbool* tags = s->tags; + clause** reasons = s->reasons; + int* levels = s->levels; + int top = veci_size(&s->tagged); + + assert(lit_var(l) >= 0 && lit_var(l) < s->size); + assert(reasons[lit_var(l)] != 0); + veci_resize(&s->stack,0); + veci_push(&s->stack,lit_var(l)); + + while (veci_size(&s->stack) > 0){ + clause* c; + int v = veci_begin(&s->stack)[veci_size(&s->stack)-1]; + assert(v >= 0 && v < s->size); + veci_resize(&s->stack,veci_size(&s->stack)-1); + assert(reasons[v] != 0); + c = reasons[v]; + + if (clause_is_lit(c)){ + int v = lit_var(clause_read_lit(c)); + if (tags[v] == l_Undef && levels[v] != 0){ + if (reasons[v] != 0 && ((1 << (levels[v] & 31)) & minl)){ + veci_push(&s->stack,v); + tags[v] = l_True; + veci_push(&s->tagged,v); + }else{ + int* tagged = veci_begin(&s->tagged); + int j; + for (j = top; j < veci_size(&s->tagged); j++) + tags[tagged[j]] = l_Undef; + veci_resize(&s->tagged,top); + return false; + } + } + }else{ + lit* lits = clause_begin(c); + int i, j; + + for (i = 1; i < clause_size(c); i++){ + int v = lit_var(lits[i]); + if (tags[v] == l_Undef && levels[v] != 0){ + if (reasons[v] != 0 && ((1 << (levels[v] & 31)) & minl)){ + + veci_push(&s->stack,lit_var(lits[i])); + tags[v] = l_True; + veci_push(&s->tagged,v); + }else{ + int* tagged = veci_begin(&s->tagged); + for (j = top; j < veci_size(&s->tagged); j++) + tags[tagged[j]] = l_Undef; + veci_resize(&s->tagged,top); + return false; + } + } + } + } + } + + return true; +} + +static void sat_solver_analyze(sat_solver* s, clause* c, veci* learnt) +{ + lit* trail = s->trail; + lbool* tags = s->tags; + clause** reasons = s->reasons; + int* levels = s->levels; + int cnt = 0; + lit p = lit_Undef; + int ind = s->qtail-1; + lit* lits; + int i, j, minl; + int* tagged; + + veci_push(learnt,lit_Undef); + + do{ + assert(c != 0); + + if (clause_is_lit(c)){ + lit q = clause_read_lit(c); + assert(lit_var(q) >= 0 && lit_var(q) < s->size); + if (tags[lit_var(q)] == l_Undef && levels[lit_var(q)] > 0){ + tags[lit_var(q)] = l_True; + veci_push(&s->tagged,lit_var(q)); + act_var_bump(s,lit_var(q)); + if (levels[lit_var(q)] == sat_solver_dlevel(s)) + cnt++; + else + veci_push(learnt,q); + } + }else{ + + if (clause_learnt(c)) + act_clause_bump(s,c); + + lits = clause_begin(c); + //printlits(lits,lits+clause_size(c)); printf("\n"); + for (j = (p == lit_Undef ? 0 : 1); j < clause_size(c); j++){ + lit q = lits[j]; + assert(lit_var(q) >= 0 && lit_var(q) < s->size); + if (tags[lit_var(q)] == l_Undef && levels[lit_var(q)] > 0){ + tags[lit_var(q)] = l_True; + veci_push(&s->tagged,lit_var(q)); + act_var_bump(s,lit_var(q)); + if (levels[lit_var(q)] == sat_solver_dlevel(s)) + cnt++; + else + veci_push(learnt,q); + } + } + } + + while (tags[lit_var(trail[ind--])] == l_Undef); + + p = trail[ind+1]; + c = reasons[lit_var(p)]; + cnt--; + + }while (cnt > 0); + + *veci_begin(learnt) = lit_neg(p); + + lits = veci_begin(learnt); + minl = 0; + for (i = 1; i < veci_size(learnt); i++){ + int lev = levels[lit_var(lits[i])]; + minl |= 1 << (lev & 31); + } + + // simplify (full) + for (i = j = 1; i < veci_size(learnt); i++){ + if (reasons[lit_var(lits[i])] == 0 || !sat_solver_lit_removable(s,lits[i],minl)) + lits[j++] = lits[i]; + } + + // update size of learnt + statistics + s->stats.max_literals += veci_size(learnt); + veci_resize(learnt,j); + s->stats.tot_literals += j; + + // clear tags + tagged = veci_begin(&s->tagged); + for (i = 0; i < veci_size(&s->tagged); i++) + tags[tagged[i]] = l_Undef; + veci_resize(&s->tagged,0); + +#ifdef DEBUG + for (i = 0; i < s->size; i++) + assert(tags[i] == l_Undef); +#endif + +#ifdef VERBOSEDEBUG + printf(L_IND"Learnt {", L_ind); + for (i = 0; i < veci_size(learnt); i++) printf(" "L_LIT, L_lit(lits[i])); +#endif + if (veci_size(learnt) > 1){ + int max_i = 1; + int max = levels[lit_var(lits[1])]; + lit tmp; + + for (i = 2; i < veci_size(learnt); i++) + if (levels[lit_var(lits[i])] > max){ + max = levels[lit_var(lits[i])]; + max_i = i; + } + + tmp = lits[1]; + lits[1] = lits[max_i]; + lits[max_i] = tmp; + } +#ifdef VERBOSEDEBUG + { + int lev = veci_size(learnt) > 1 ? levels[lit_var(lits[1])] : 0; + printf(" } at level %d\n", lev); + } +#endif +} + + +clause* sat_solver_propagate(sat_solver* s) +{ + lbool* values = s->assigns; + clause* confl = (clause*)0; + lit* lits; + + //printf("sat_solver_propagate\n"); + while (confl == 0 && s->qtail - s->qhead > 0){ + lit p = s->trail[s->qhead++]; + vecp* ws = sat_solver_read_wlist(s,p); + clause **begin = (clause**)vecp_begin(ws); + clause **end = begin + vecp_size(ws); + clause **i, **j; + + s->stats.propagations++; + s->simpdb_props--; + + //printf("checking lit %d: "L_LIT"\n", veci_size(ws), L_lit(p)); + for (i = j = begin; i < end; ){ + if (clause_is_lit(*i)){ + *j++ = *i; + if (!enqueue(s,clause_read_lit(*i),clause_from_lit(p))){ + confl = s->binary; + (clause_begin(confl))[1] = lit_neg(p); + (clause_begin(confl))[0] = clause_read_lit(*i++); + + // Copy the remaining watches: + while (i < end) + *j++ = *i++; + } + }else{ + lit false_lit; + lbool sig; + + lits = clause_begin(*i); + + // Make sure the false literal is data[1]: + false_lit = lit_neg(p); + if (lits[0] == false_lit){ + lits[0] = lits[1]; + lits[1] = false_lit; + } + assert(lits[1] == false_lit); + //printf("checking clause: "); printlits(lits, lits+clause_size(*i)); printf("\n"); + + // If 0th watch is true, then clause is already satisfied. + sig = !lit_sign(lits[0]); sig += sig - 1; + if (values[lit_var(lits[0])] == sig){ + *j++ = *i; + }else{ + // Look for new watch: + lit* stop = lits + clause_size(*i); + lit* k; + for (k = lits + 2; k < stop; k++){ + lbool sig = lit_sign(*k); sig += sig - 1; + if (values[lit_var(*k)] != sig){ + lits[1] = *k; + *k = false_lit; + vecp_push(sat_solver_read_wlist(s,lit_neg(lits[1])),*i); + goto next; } + } + + *j++ = *i; + // Clause is unit under assignment: + if (!enqueue(s,lits[0], *i)){ + confl = *i++; + // Copy the remaining watches: + while (i < end) + *j++ = *i++; + } + } + } + next: + i++; + } + + s->stats.inspects += j - (clause**)vecp_begin(ws); + vecp_resize(ws,j - (clause**)vecp_begin(ws)); + } + + return confl; +} + +static inline int clause_cmp (const void* x, const void* y) { + return clause_size((clause*)x) > 2 && (clause_size((clause*)y) == 2 || clause_activity((clause*)x) < clause_activity((clause*)y)) ? -1 : 1; } + +void sat_solver_reducedb(sat_solver* s) +{ + int i, j; + double extra_lim = s->cla_inc / vecp_size(&s->learnts); // Remove any clause below this activity + clause** learnts = (clause**)vecp_begin(&s->learnts); + clause** reasons = s->reasons; + + sat_solver_sort(vecp_begin(&s->learnts), vecp_size(&s->learnts), &clause_cmp); + + for (i = j = 0; i < vecp_size(&s->learnts) / 2; i++){ + if (clause_size(learnts[i]) > 2 && reasons[lit_var(*clause_begin(learnts[i]))] != learnts[i]) + clause_remove(s,learnts[i]); + else + learnts[j++] = learnts[i]; + } + for (; i < vecp_size(&s->learnts); i++){ + if (clause_size(learnts[i]) > 2 && reasons[lit_var(*clause_begin(learnts[i]))] != learnts[i] && clause_activity(learnts[i]) < extra_lim) + clause_remove(s,learnts[i]); + else + learnts[j++] = learnts[i]; + } + + //printf("reducedb deleted %d\n", vecp_size(&s->learnts) - j); + + + vecp_resize(&s->learnts,j); +} + +static lbool sat_solver_search(sat_solver* s, sint64 nof_conflicts, sint64 nof_learnts) +{ + int* levels = s->levels; + double var_decay = 0.95; + double clause_decay = 0.999; + double random_var_freq = 0.02; + + sint64 conflictC = 0; + veci learnt_clause; + int i; + + assert(s->root_level == sat_solver_dlevel(s)); + + s->nRestarts++; + s->stats.starts++; + s->var_decay = (float)(1 / var_decay ); + s->cla_decay = (float)(1 / clause_decay); + veci_resize(&s->model,0); + veci_new(&learnt_clause); + + // use activity factors in every even restart + if ( (s->nRestarts & 1) && veci_size(&s->act_vars) > 0 ) + for ( i = 0; i < s->act_vars.size; i++ ) + act_var_bump_factor(s, s->act_vars.ptr[i]); + + for (;;){ + clause* confl = sat_solver_propagate(s); + if (confl != 0){ + // CONFLICT + int blevel; + +#ifdef VERBOSEDEBUG + printf(L_IND"**CONFLICT**\n", L_ind); +#endif + s->stats.conflicts++; conflictC++; + if (sat_solver_dlevel(s) == s->root_level){ + veci_delete(&learnt_clause); + return l_False; + } + + veci_resize(&learnt_clause,0); + sat_solver_analyze(s, confl, &learnt_clause); + blevel = veci_size(&learnt_clause) > 1 ? levels[lit_var(veci_begin(&learnt_clause)[1])] : s->root_level; + blevel = s->root_level > blevel ? s->root_level : blevel; + sat_solver_canceluntil(s,blevel); + sat_solver_record(s,&learnt_clause); + act_var_decay(s); + act_clause_decay(s); + + }else{ + // NO CONFLICT + int next; + + if (nof_conflicts >= 0 && conflictC >= nof_conflicts){ + // Reached bound on number of conflicts: + s->progress_estimate = sat_solver_progress(s); + sat_solver_canceluntil(s,s->root_level); + veci_delete(&learnt_clause); + return l_Undef; } + + if ( s->nConfLimit && s->stats.conflicts > s->nConfLimit || + s->nInsLimit && s->stats.inspects > s->nInsLimit ) + { + // Reached bound on number of conflicts: + s->progress_estimate = sat_solver_progress(s); + sat_solver_canceluntil(s,s->root_level); + veci_delete(&learnt_clause); + return l_Undef; + } + + if (sat_solver_dlevel(s) == 0 && !s->fSkipSimplify) + // Simplify the set of problem clauses: + sat_solver_simplify(s); + + if (nof_learnts >= 0 && vecp_size(&s->learnts) - s->qtail >= nof_learnts) + // Reduce the set of learnt clauses: + sat_solver_reducedb(s); + + // New variable decision: + s->stats.decisions++; + next = order_select(s,(float)random_var_freq); + + if (next == var_Undef){ + // Model found: + lbool* values = s->assigns; + int i; + veci_resize(&s->model, 0); + for (i = 0; i < s->size; i++) + veci_push(&s->model,(int)values[i]); + sat_solver_canceluntil(s,s->root_level); + veci_delete(&learnt_clause); + + /* + veci apa; veci_new(&apa); + for (i = 0; i < s->size; i++) + veci_push(&apa,(int)(s->model.ptr[i] == l_True ? toLit(i) : lit_neg(toLit(i)))); + printf("model: "); printlits((lit*)apa.ptr, (lit*)apa.ptr + veci_size(&apa)); printf("\n"); + veci_delete(&apa); + */ + + return l_True; + } + + assume(s,lit_neg(toLit(next))); + } + } + + return l_Undef; // cannot happen +} + +//================================================================================================= +// External solver functions: + +sat_solver* sat_solver_new(void) +{ + sat_solver* s = (sat_solver*)malloc(sizeof(sat_solver)); + memset( s, 0, sizeof(sat_solver) ); + + // initialize vectors + vecp_new(&s->clauses); + vecp_new(&s->learnts); + veci_new(&s->order); + veci_new(&s->trail_lim); + veci_new(&s->tagged); + veci_new(&s->stack); + veci_new(&s->model); + veci_new(&s->act_vars); + + // initialize arrays + s->wlists = 0; + s->activity = 0; + s->factors = 0; + s->assigns = 0; + s->orderpos = 0; + s->reasons = 0; + s->levels = 0; + s->tags = 0; + s->trail = 0; + + + // initialize other vars + s->size = 0; + s->cap = 0; + s->qhead = 0; + s->qtail = 0; + s->cla_inc = 1; + s->cla_decay = 1; + s->var_inc = 1; + s->var_decay = 1; + s->root_level = 0; + s->simpdb_assigns = 0; + s->simpdb_props = 0; + s->random_seed = 91648253; + s->progress_estimate = 0; + s->binary = (clause*)malloc(sizeof(clause) + sizeof(lit)*2); + s->binary->size_learnt = (2 << 1); + s->verbosity = 0; + + s->stats.starts = 0; + s->stats.decisions = 0; + s->stats.propagations = 0; + s->stats.inspects = 0; + s->stats.conflicts = 0; + s->stats.clauses = 0; + s->stats.clauses_literals = 0; + s->stats.learnts = 0; + s->stats.learnts_literals = 0; + s->stats.max_literals = 0; + s->stats.tot_literals = 0; + +#ifdef SAT_USE_SYSTEM_MEMORY_MANAGEMENT + s->pMem = NULL; +#else + s->pMem = Sat_MmStepStart( 10 ); +#endif + return s; +} + + +void sat_solver_delete(sat_solver* s) +{ + +#ifdef SAT_USE_SYSTEM_MEMORY_MANAGEMENT + int i; + for (i = 0; i < vecp_size(&s->clauses); i++) + free(vecp_begin(&s->clauses)[i]); + for (i = 0; i < vecp_size(&s->learnts); i++) + free(vecp_begin(&s->learnts)[i]); +#else + Sat_MmStepStop( s->pMem, 0 ); +#endif + + // delete vectors + vecp_delete(&s->clauses); + vecp_delete(&s->learnts); + veci_delete(&s->order); + veci_delete(&s->trail_lim); + veci_delete(&s->tagged); + veci_delete(&s->stack); + veci_delete(&s->model); + veci_delete(&s->act_vars); + free(s->binary); + + // delete arrays + if (s->wlists != 0){ + int i; + for (i = 0; i < s->size*2; i++) + vecp_delete(&s->wlists[i]); + + // if one is different from null, all are + free(s->wlists ); + free(s->activity ); + free(s->factors ); + free(s->assigns ); + free(s->orderpos ); + free(s->reasons ); + free(s->levels ); + free(s->trail ); + free(s->tags ); + } + + sat_solver_store_free(s); + free(s); +} + + +bool sat_solver_addclause(sat_solver* s, lit* begin, lit* end) +{ + lit *i,*j; + int maxvar; + lbool* values; + lit last; + + if (begin == end) return false; + + //printlits(begin,end); printf("\n"); + // insertion sort + maxvar = lit_var(*begin); + for (i = begin + 1; i < end; i++){ + lit l = *i; + maxvar = lit_var(l) > maxvar ? lit_var(l) : maxvar; + for (j = i; j > begin && *(j-1) > l; j--) + *j = *(j-1); + *j = l; + } + sat_solver_setnvars(s,maxvar+1); +// sat_solver_setnvars(s, lit_var(*(end-1))+1 ); + + //printlits(begin,end); printf("\n"); + values = s->assigns; + + // delete duplicates + last = lit_Undef; + for (i = j = begin; i < end; i++){ + //printf("lit: "L_LIT", value = %d\n", L_lit(*i), (lit_sign(*i) ? -values[lit_var(*i)] : values[lit_var(*i)])); + lbool sig = !lit_sign(*i); sig += sig - 1; + if (*i == lit_neg(last) || sig == values[lit_var(*i)]) + return true; // tautology + else if (*i != last && values[lit_var(*i)] == l_Undef) + last = *j++ = *i; + } + + //printf("final: "); printlits(begin,j); printf("\n"); + + if (j == begin) // empty clause + return false; + + /////////////////////////////////// + // add clause to internal storage + if ( s->pStore ) + { + extern int Sto_ManAddClause( void * p, lit * pBeg, lit * pEnd ); + int RetValue = Sto_ManAddClause( s->pStore, begin, j ); + assert( RetValue ); + } + /////////////////////////////////// + + if (j - begin == 1) // unit clause + return enqueue(s,*begin,(clause*)0); + + // create new clause + vecp_push(&s->clauses,clause_new(s,begin,j,0)); + + + s->stats.clauses++; + s->stats.clauses_literals += j - begin; + + return true; +} + + +bool sat_solver_simplify(sat_solver* s) +{ + clause** reasons; + int type; + + assert(sat_solver_dlevel(s) == 0); + + if (sat_solver_propagate(s) != 0) + return false; + + if (s->qhead == s->simpdb_assigns || s->simpdb_props > 0) + return true; + + reasons = s->reasons; + for (type = 0; type < 2; type++){ + vecp* cs = type ? &s->learnts : &s->clauses; + clause** cls = (clause**)vecp_begin(cs); + + int i, j; + for (j = i = 0; i < vecp_size(cs); i++){ + if (reasons[lit_var(*clause_begin(cls[i]))] != cls[i] && + clause_simplify(s,cls[i]) == l_True) + clause_remove(s,cls[i]); + else + cls[j++] = cls[i]; + } + vecp_resize(cs,j); + } + + s->simpdb_assigns = s->qhead; + // (shouldn't depend on 'stats' really, but it will do for now) + s->simpdb_props = (int)(s->stats.clauses_literals + s->stats.learnts_literals); + + return true; +} + + +int sat_solver_solve(sat_solver* s, lit* begin, lit* end, sint64 nConfLimit, sint64 nInsLimit, sint64 nConfLimitGlobal, sint64 nInsLimitGlobal) +{ + sint64 nof_conflicts = 100; + sint64 nof_learnts = sat_solver_nclauses(s) / 3; + lbool status = l_Undef; + lbool* values = s->assigns; + lit* i; + + // set the external limits + s->nRestarts = 0; + s->nConfLimit = 0; + s->nInsLimit = 0; + if ( nConfLimit ) + s->nConfLimit = s->stats.conflicts + nConfLimit; + if ( nInsLimit ) + s->nInsLimit = s->stats.inspects + nInsLimit; + if ( nConfLimitGlobal && (s->nConfLimit == 0 || s->nConfLimit > nConfLimitGlobal) ) + s->nConfLimit = nConfLimitGlobal; + if ( nInsLimitGlobal && (s->nInsLimit == 0 || s->nInsLimit > nInsLimitGlobal) ) + s->nInsLimit = nInsLimitGlobal; + + //printf("solve: "); printlits(begin, end); printf("\n"); + for (i = begin; i < end; i++){ + switch (lit_sign(*i) ? -values[lit_var(*i)] : values[lit_var(*i)]){ + case 1: /* l_True: */ + break; + case 0: /* l_Undef */ + assume(s, *i); + if (sat_solver_propagate(s) == NULL) + break; + // falltrough + case -1: /* l_False */ + sat_solver_canceluntil(s, 0); + return l_False; + } + } + + s->root_level = sat_solver_dlevel(s); + + if (s->verbosity >= 1){ + printf("==================================[MINISAT]===================================\n"); + printf("| Conflicts | ORIGINAL | LEARNT | Progress |\n"); + printf("| | Clauses Literals | Limit Clauses Literals Lit/Cl | |\n"); + printf("==============================================================================\n"); + } + + while (status == l_Undef){ + double Ratio = (s->stats.learnts == 0)? 0.0 : + s->stats.learnts_literals / (double)s->stats.learnts; + + if (s->verbosity >= 1) + { + printf("| %9.0f | %7.0f %8.0f | %7.0f %7.0f %8.0f %7.1f | %6.3f %% |\n", + (double)s->stats.conflicts, + (double)s->stats.clauses, + (double)s->stats.clauses_literals, + (double)nof_learnts, + (double)s->stats.learnts, + (double)s->stats.learnts_literals, + Ratio, + s->progress_estimate*100); + fflush(stdout); + } + status = sat_solver_search(s, nof_conflicts, nof_learnts); + nof_conflicts = nof_conflicts * 3 / 2; //*= 1.5; + nof_learnts = nof_learnts * 11 / 10; //*= 1.1; + + // quit the loop if reached an external limit + if ( s->nConfLimit && s->stats.conflicts > s->nConfLimit ) + { +// printf( "Reached the limit on the number of conflicts (%d).\n", s->nConfLimit ); + break; + } + if ( s->nInsLimit && s->stats.inspects > s->nInsLimit ) + { +// printf( "Reached the limit on the number of implications (%d).\n", s->nInsLimit ); + break; + } + } + if (s->verbosity >= 1) + printf("==============================================================================\n"); + + sat_solver_canceluntil(s,0); + + //////////////////////////////////////////////// + if ( status == l_False && s->pStore ) + { + extern int Sto_ManAddClause( void * p, lit * pBeg, lit * pEnd ); + int RetValue = Sto_ManAddClause( s->pStore, NULL, NULL ); + assert( RetValue ); + } + //////////////////////////////////////////////// + return status; +} + + +int sat_solver_nvars(sat_solver* s) +{ + return s->size; +} + + +int sat_solver_nclauses(sat_solver* s) +{ + return vecp_size(&s->clauses); +} + + +int sat_solver_nconflicts(sat_solver* s) +{ + return (int)s->stats.conflicts; +} + +//================================================================================================= +// Clause storage functions: + +void sat_solver_store_alloc( sat_solver * s ) +{ + extern void * Sto_ManAlloc(); + assert( s->pStore == NULL ); + s->pStore = Sto_ManAlloc(); +} + +void sat_solver_store_write( sat_solver * s, char * pFileName ) +{ + extern void Sto_ManDumpClauses( void * p, char * pFileName ); + if ( s->pStore ) Sto_ManDumpClauses( s->pStore, pFileName ); +} + +void sat_solver_store_free( sat_solver * s ) +{ + extern void Sto_ManFree( void * p ); + if ( s->pStore ) Sto_ManFree( s->pStore ); + s->pStore = NULL; +} + +void sat_solver_store_mark_roots( sat_solver * s ) +{ + extern void Sto_ManMarkRoots( void * p ); + if ( s->pStore ) Sto_ManMarkRoots( s->pStore ); +} + +void sat_solver_store_mark_clauses_a( sat_solver * s ) +{ + extern void Sto_ManMarkClausesA( void * p ); + if ( s->pStore ) Sto_ManMarkClausesA( s->pStore ); +} + +void * sat_solver_store_release( sat_solver * s ) +{ + void * pTemp; + if ( s->pStore == NULL ) + return NULL; + pTemp = s->pStore; + s->pStore = NULL; + return pTemp; +} + +//================================================================================================= +// Sorting functions (sigh): + +static inline void selectionsort(void** array, int size, int(*comp)(const void *, const void *)) +{ + int i, j, best_i; + void* tmp; + + for (i = 0; i < size-1; i++){ + best_i = i; + for (j = i+1; j < size; j++){ + if (comp(array[j], array[best_i]) < 0) + best_i = j; + } + tmp = array[i]; array[i] = array[best_i]; array[best_i] = tmp; + } +} + + +static void sortrnd(void** array, int size, int(*comp)(const void *, const void *), double* seed) +{ + if (size <= 15) + selectionsort(array, size, comp); + + else{ + void* pivot = array[irand(seed, size)]; + void* tmp; + int i = -1; + int j = size; + + for(;;){ + do i++; while(comp(array[i], pivot)<0); + do j--; while(comp(pivot, array[j])<0); + + if (i >= j) break; + + tmp = array[i]; array[i] = array[j]; array[j] = tmp; + } + + sortrnd(array , i , comp, seed); + sortrnd(&array[i], size-i, comp, seed); + } +} + +void sat_solver_sort(void** array, int size, int(*comp)(const void *, const void *)) +{ + double seed = 91648253; + sortrnd(array,size,comp,&seed); +} diff --git a/abc_with_bb_support/src/sat/bsat/satSolver.h b/abc_with_bb_support/src/sat/bsat/satSolver.h new file mode 100644 index 000000000..9023c57cd --- /dev/null +++ b/abc_with_bb_support/src/sat/bsat/satSolver.h @@ -0,0 +1,189 @@ +/************************************************************************************************** +MiniSat -- Copyright (c) 2005, Niklas Sorensson +http://www.cs.chalmers.se/Cs/Research/FormalMethods/MiniSat/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ +// Modified to compile with MS Visual Studio 6.0 by Alan Mishchenko + +#ifndef satSolver_h +#define satSolver_h + +#ifdef _WIN32 +#define inline __inline // compatible with MS VS 6.0 +#endif + +#include "satVec.h" +#include "satMem.h" + +//================================================================================================= +// Simple types: + +// does not work for c++ +//typedef int bool; +#ifndef __cplusplus +#ifndef bool +#define bool int +#endif +#endif + +static const bool true = 1; +static const bool false = 0; + +typedef int lit; +typedef char lbool; + +#ifndef SINT64 +#define SINT64 + +#ifdef _WIN32 +typedef signed __int64 sint64; // compatible with MS VS 6.0 +#else +typedef long long sint64; +#endif + +#endif + +static const int var_Undef = -1; +static const lit lit_Undef = -2; + +static const lbool l_Undef = 0; +static const lbool l_True = 1; +static const lbool l_False = -1; + +static inline lit toLit (int v) { return v + v; } +static inline lit toLitCond(int v, int c) { return v + v + (c != 0); } +static inline lit lit_neg (lit l) { return l ^ 1; } +static inline int lit_var (lit l) { return l >> 1; } +static inline int lit_sign (lit l) { return l & 1; } +static inline int lit_print(lit l) { return lit_sign(l)? -lit_var(l)-1 : lit_var(l)+1; } +static inline lit lit_read (int s) { return s > 0 ? toLit(s-1) : lit_neg(toLit(-s-1)); } + + +//================================================================================================= +// Public interface: + +struct sat_solver_t; +typedef struct sat_solver_t sat_solver; + +extern sat_solver* sat_solver_new(void); +extern void sat_solver_delete(sat_solver* s); + +extern bool sat_solver_addclause(sat_solver* s, lit* begin, lit* end); +extern bool sat_solver_simplify(sat_solver* s); +extern int sat_solver_solve(sat_solver* s, lit* begin, lit* end, sint64 nConfLimit, sint64 nInsLimit, sint64 nConfLimitGlobal, sint64 nInsLimitGlobal); + +extern int sat_solver_nvars(sat_solver* s); +extern int sat_solver_nclauses(sat_solver* s); +extern int sat_solver_nconflicts(sat_solver* s); + +extern void sat_solver_setnvars(sat_solver* s,int n); + +struct stats_t +{ + sint64 starts, decisions, propagations, inspects, conflicts; + sint64 clauses, clauses_literals, learnts, learnts_literals, max_literals, tot_literals; +}; +typedef struct stats_t stats; + +extern void Sat_SolverWriteDimacs( sat_solver * p, char * pFileName, lit* assumptionsBegin, lit* assumptionsEnd, int incrementVars ); +extern void Sat_SolverPrintStats( FILE * pFile, sat_solver * p ); +extern int * Sat_SolverGetModel( sat_solver * p, int * pVars, int nVars ); +extern void Sat_SolverDoubleClauses( sat_solver * p, int iVar ); + +// trace recording +extern void Sat_SolverTraceStart( sat_solver * pSat, char * pName ); +extern void Sat_SolverTraceStop( sat_solver * pSat ); +extern void Sat_SolverTraceWrite( sat_solver * pSat, int * pBeg, int * pEnd, int fRoot ); + +// clause storage +extern void sat_solver_store_alloc( sat_solver * s ); +extern void sat_solver_store_write( sat_solver * s, char * pFileName ); +extern void sat_solver_store_free( sat_solver * s ); +extern void sat_solver_store_mark_roots( sat_solver * s ); +extern void sat_solver_store_mark_clauses_a( sat_solver * s ); +extern void * sat_solver_store_release( sat_solver * s ); + +//================================================================================================= +// Solver representation: + +struct clause_t; +typedef struct clause_t clause; + +struct sat_solver_t +{ + int size; // nof variables + int cap; // size of varmaps + int qhead; // Head index of queue. + int qtail; // Tail index of queue. + + // clauses + vecp clauses; // List of problem constraints. (contains: clause*) + vecp learnts; // List of learnt clauses. (contains: clause*) + + // activities + double var_inc; // Amount to bump next variable with. + double var_decay; // INVERSE decay factor for variable activity: stores 1/decay. + float cla_inc; // Amount to bump next clause with. + float cla_decay; // INVERSE decay factor for clause activity: stores 1/decay. + + vecp* wlists; // + double* activity; // A heuristic measurement of the activity of a variable. + lbool* assigns; // Current values of variables. + int* orderpos; // Index in variable order. + clause** reasons; // + int* levels; // + lit* trail; + + clause* binary; // A temporary binary clause + lbool* tags; // + veci tagged; // (contains: var) + veci stack; // (contains: var) + + veci order; // Variable order. (heap) (contains: var) + veci trail_lim; // Separator indices for different decision levels in 'trail'. (contains: int) + veci model; // If problem is solved, this vector contains the model (contains: lbool). + + int root_level; // Level of first proper decision. + int simpdb_assigns;// Number of top-level assignments at last 'simplifyDB()'. + int simpdb_props; // Number of propagations before next 'simplifyDB()'. + double random_seed; + double progress_estimate; + int verbosity; // Verbosity level. 0=silent, 1=some progress report, 2=everything + + stats stats; + + sint64 nConfLimit; // external limit on the number of conflicts + sint64 nInsLimit; // external limit on the number of implications + + veci act_vars; // variables whose activity has changed + double* factors; // the activity factors + int nRestarts; // the number of local restarts + + Sat_MmStep_t * pMem; + + int fSkipSimplify; // set to one to skip simplification of the clause database + + // clause store + void * pStore; + + // trace recording + FILE * pFile; + int nClauses; + int nRoots; +}; + +#endif diff --git a/abc_with_bb_support/src/sat/bsat/satStore.c b/abc_with_bb_support/src/sat/bsat/satStore.c new file mode 100644 index 000000000..401bbe93b --- /dev/null +++ b/abc_with_bb_support/src/sat/bsat/satStore.c @@ -0,0 +1,437 @@ +/**CFile**************************************************************** + + FileName [satStore.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [SAT solver.] + + Synopsis [Records the trace of SAT solving in the CNF form.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: satStore.c,v 1.4 2005/09/16 22:55:03 casem Exp $] + +***********************************************************************/ + +#include +#include +#include +#include +#include +#include "satStore.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Fetches memory.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Sto_ManMemoryFetch( Sto_Man_t * p, int nBytes ) +{ + char * pMem; + if ( p->pChunkLast == NULL || nBytes > p->nChunkSize - p->nChunkUsed ) + { + pMem = (char *)malloc( p->nChunkSize ); + *(char **)pMem = p->pChunkLast; + p->pChunkLast = pMem; + p->nChunkUsed = sizeof(char *); + } + pMem = p->pChunkLast + p->nChunkUsed; + p->nChunkUsed += nBytes; + return pMem; +} + +/**Function************************************************************* + + Synopsis [Frees memory manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sto_ManMemoryStop( Sto_Man_t * p ) +{ + char * pMem, * pNext; + if ( p->pChunkLast == NULL ) + return; + for ( pMem = p->pChunkLast; pNext = *(char **)pMem; pMem = pNext ) + free( pMem ); + free( pMem ); +} + +/**Function************************************************************* + + Synopsis [Reports memory usage in bytes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sto_ManMemoryReport( Sto_Man_t * p ) +{ + int Total; + char * pMem, * pNext; + if ( p->pChunkLast == NULL ) + return 0; + Total = p->nChunkUsed; + for ( pMem = p->pChunkLast; pNext = *(char **)pMem; pMem = pNext ) + Total += p->nChunkSize; + return Total; +} + + +/**Function************************************************************* + + Synopsis [Allocate proof manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Sto_Man_t * Sto_ManAlloc() +{ + Sto_Man_t * p; + // allocate the manager + p = (Sto_Man_t *)malloc( sizeof(Sto_Man_t) ); + memset( p, 0, sizeof(Sto_Man_t) ); + // memory management + p->nChunkSize = (1<<16); // use 64K chunks + return p; +} + +/**Function************************************************************* + + Synopsis [Deallocate proof manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sto_ManFree( Sto_Man_t * p ) +{ + Sto_ManMemoryStop( p ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Adds one clause to the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sto_ManAddClause( Sto_Man_t * p, lit * pBeg, lit * pEnd ) +{ + Sto_Cls_t * pClause; + lit Lit, * i, * j; + int nSize; + + // process the literals + if ( pBeg < pEnd ) + { + // insertion sort + for ( i = pBeg + 1; i < pEnd; i++ ) + { + Lit = *i; + for ( j = i; j > pBeg && *(j-1) > Lit; j-- ) + *j = *(j-1); + *j = Lit; + } + // make sure there is no duplicated variables + for ( i = pBeg + 1; i < pEnd; i++ ) + if ( lit_var(*(i-1)) == lit_var(*i) ) + { + printf( "The clause contains two literals of the same variable: %d and %d.\n", *(i-1), *i ); + return 0; + } + // check the largest var size + p->nVars = STO_MAX( p->nVars, lit_var(*(pEnd-1)) + 1 ); + } + + // get memory for the clause + nSize = sizeof(Sto_Cls_t) + sizeof(lit) * (pEnd - pBeg); + pClause = (Sto_Cls_t *)Sto_ManMemoryFetch( p, nSize ); + memset( pClause, 0, sizeof(Sto_Cls_t) ); + + // assign the clause + pClause->Id = p->nClauses++; + pClause->nLits = pEnd - pBeg; + memcpy( pClause->pLits, pBeg, sizeof(lit) * (pEnd - pBeg) ); + + // add the clause to the list + if ( p->pHead == NULL ) + p->pHead = pClause; + if ( p->pTail == NULL ) + p->pTail = pClause; + else + { + p->pTail->pNext = pClause; + p->pTail = pClause; + } + + // add the empty clause + if ( pClause->nLits == 0 ) + { + if ( p->pEmpty ) + { + printf( "More than one empty clause!\n" ); + return 0; + } + p->pEmpty = pClause; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Mark all clauses added so far as root clauses.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sto_ManMarkRoots( Sto_Man_t * p ) +{ + Sto_Cls_t * pClause; + p->nRoots = 0; + Sto_ManForEachClause( p, pClause ) + { + pClause->fRoot = 1; + p->nRoots++; + } +} + +/**Function************************************************************* + + Synopsis [Mark all clauses added so far as clause of A.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sto_ManMarkClausesA( Sto_Man_t * p ) +{ + Sto_Cls_t * pClause; + p->nClausesA = 0; + Sto_ManForEachClause( p, pClause ) + { + pClause->fA = 1; + p->nClausesA++; + } +} + + +/**Function************************************************************* + + Synopsis [Writes the stored clauses into a file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sto_ManDumpClauses( Sto_Man_t * p, char * pFileName ) +{ + FILE * pFile; + Sto_Cls_t * pClause; + int i; + // start the file + pFile = fopen( pFileName, "w" ); + if ( pFile == NULL ) + { + printf( "Error: Cannot open output file (%s).\n", pFileName ); + return; + } + // write the data + fprintf( pFile, "p %d %d %d %d\n", p->nVars, p->nClauses, p->nRoots, p->nClausesA ); + Sto_ManForEachClause( p, pClause ) + { + for ( i = 0; i < (int)pClause->nLits; i++ ) + fprintf( pFile, " %d", lit_print(pClause->pLits[i]) ); + fprintf( pFile, "\n" ); + } + fprintf( pFile, " 0\n" ); + fclose( pFile ); +} + +/**Function************************************************************* + + Synopsis [Reads one literal from file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sto_ManLoadNumber( FILE * pFile, int * pNumber ) +{ + int Char, Number = 0, Sign = 0; + // skip space-like chars + do { + Char = fgetc( pFile ); + if ( Char == EOF ) + return 0; + } while ( Char == ' ' || Char == '\t' || Char == '\r' || Char == '\n' ); + // read the literal + while ( 1 ) + { + // get the next character + Char = fgetc( pFile ); + if ( Char == ' ' || Char == '\t' || Char == '\r' || Char == '\n' ) + break; + // check that the char is a digit + if ( (Char < '0' || Char > '9') && Char != '-' ) + { + printf( "Error: Wrong char (%c) in the input file.\n", Char ); + return 0; + } + // check if this is a minus + if ( Char == '-' ) + Sign = 1; + else + Number = 10 * Number + Char; + } + // return the number + *pNumber = Sign? -Number : Number; + return 1; +} + +/**Function************************************************************* + + Synopsis [Reads CNF from file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Sto_Man_t * Sto_ManLoadClauses( char * pFileName ) +{ + FILE * pFile; + Sto_Man_t * p; + Sto_Cls_t * pClause; + char pBuffer[1024]; + int nLits, nLitsAlloc, Counter, Number; + lit * pLits; + + // start the file + pFile = fopen( pFileName, "r" ); + if ( pFile == NULL ) + { + printf( "Error: Cannot open input file (%s).\n", pFileName ); + return NULL; + } + + // create the manager + p = Sto_ManAlloc(); + + // alloc the array of literals + nLitsAlloc = 1024; + pLits = (lit *)malloc( sizeof(lit) * nLitsAlloc ); + + // read file header + p->nVars = p->nClauses = p->nRoots = p->nClausesA = 0; + while ( fgets( pBuffer, 1024, pFile ) ) + { + if ( pBuffer[0] == 'c' ) + continue; + if ( pBuffer[0] == 'p' ) + { + sscanf( pBuffer + 1, "%d %d %d %d", p->nVars, p->nClauses, p->nRoots, p->nClausesA ); + break; + } + printf( "Warning: Skipping line: \"%s\"\n", pBuffer ); + } + + // read the clauses + nLits = 0; + while ( Sto_ManLoadNumber(pFile, &Number) ) + { + if ( Number == 0 ) + { + int RetValue; + RetValue = Sto_ManAddClause( p, pLits, pLits + nLits ); + assert( RetValue ); + nLits = 0; + continue; + } + if ( nLits == nLitsAlloc ) + { + nLitsAlloc *= 2; + pLits = (lit *)realloc( pLits, sizeof(lit) * nLitsAlloc ); + } + pLits[ nLits++ ] = lit_read(Number); + } + if ( nLits > 0 ) + printf( "Error: The last clause was not saved.\n" ); + + // count clauses + Counter = 0; + Sto_ManForEachClause( p, pClause ) + Counter++; + + // check the number of clauses + if ( p->nClauses != Counter ) + { + printf( "Error: The actual number of clauses (%d) is different than declared (%d).\n", Counter, p->nClauses ); + Sto_ManFree( p ); + return NULL; + } + + free( pLits ); + fclose( pFile ); + return p; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/bsat/satStore.h b/abc_with_bb_support/src/sat/bsat/satStore.h new file mode 100644 index 000000000..a473920f0 --- /dev/null +++ b/abc_with_bb_support/src/sat/bsat/satStore.h @@ -0,0 +1,137 @@ +/**CFile**************************************************************** + + FileName [pr.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Proof recording.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: pr.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __PR_H__ +#define __PR_H__ + +/* + The trace of SAT solving contains the original clause of the problem + along with the learned clauses derived during SAT solving. + The first line of the resulting file contains 3 numbers instead of 2: + c +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +#define inline __inline // compatible with MS VS 6.0 +#endif + +#ifndef PRT +#define PRT(a,t) printf("%s = ", (a)); printf("%6.2f sec\n", (float)(t)/(float)(CLOCKS_PER_SEC)) +#endif + +#define STO_MAX(a,b) ((a) > (b) ? (a) : (b)) + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef unsigned lit; + +typedef struct Sto_Cls_t_ Sto_Cls_t; +struct Sto_Cls_t_ +{ + Sto_Cls_t * pNext; // the next clause + Sto_Cls_t * pNext0; // the next 0-watch + Sto_Cls_t * pNext1; // the next 0-watch + int Id; // the clause ID + unsigned fA : 1; // belongs to A + unsigned fRoot : 1; // original clause + unsigned fVisit : 1; // visited clause + unsigned nLits : 24; // the number of literals + lit pLits[0]; // literals of this clause +}; + +typedef struct Sto_Man_t_ Sto_Man_t; +struct Sto_Man_t_ +{ + // general data + int nVars; // the number of variables + int nRoots; // the number of root clauses + int nClauses; // the number of all clauses + int nClausesA; // the number of clauses of A + Sto_Cls_t * pHead; // the head clause + Sto_Cls_t * pTail; // the tail clause + Sto_Cls_t * pEmpty; // the empty clause + // memory management + int nChunkSize; // the number of bytes in a chunk + int nChunkUsed; // the number of bytes used in the last chunk + char * pChunkLast; // the last memory chunk +}; + +// variable/literal conversions (taken from MiniSat) +static inline lit toLit (int v) { return v + v; } +static inline lit toLitCond(int v, int c) { return v + v + (c != 0); } +static inline lit lit_neg (lit l) { return l ^ 1; } +static inline int lit_var (lit l) { return l >> 1; } +static inline int lit_sign (lit l) { return l & 1; } +static inline int lit_print(lit l) { return lit_sign(l)? -lit_var(l)-1 : lit_var(l)+1; } +static inline lit lit_read (int s) { return s > 0 ? toLit(s-1) : lit_neg(toLit(-s-1)); } +static inline int lit_check(lit l, int n) { return l >= 0 && lit_var(l) < n; } + +// iterators through the clauses +#define Sto_ManForEachClause( p, pCls ) for( pCls = p->pHead; pCls; pCls = pCls->pNext ) +#define Sto_ManForEachClauseRoot( p, pCls ) for( pCls = p->pHead; pCls && pCls->fRoot; pCls = pCls->pNext ) + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== satStore.c ==========================================================*/ +extern Sto_Man_t * Sto_ManAlloc(); +extern void Sto_ManFree( Sto_Man_t * p ); +extern int Sto_ManAddClause( Sto_Man_t * p, lit * pBeg, lit * pEnd ); +extern int Sto_ManMemoryReport( Sto_Man_t * p ); +extern void Sto_ManMarkRoots( Sto_Man_t * p ); +extern void Sto_ManMarkClausesA( Sto_Man_t * p ); +extern void Sto_ManDumpClauses( Sto_Man_t * p, char * pFileName ); +extern Sto_Man_t * Sto_ManLoadClauses( char * pFileName ); + +/*=== satInter.c ==========================================================*/ +typedef struct Int_Man_t_ Int_Man_t; +extern Int_Man_t * Int_ManAlloc( int nVarsAlloc ); +extern void Int_ManFree( Int_Man_t * p ); +extern int Int_ManInterpolate( Int_Man_t * p, Sto_Man_t * pCnf, int fVerbose, unsigned ** ppResult ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/sat/bsat/satTrace.c b/abc_with_bb_support/src/sat/bsat/satTrace.c new file mode 100644 index 000000000..5e60180d3 --- /dev/null +++ b/abc_with_bb_support/src/sat/bsat/satTrace.c @@ -0,0 +1,109 @@ +/**CFile**************************************************************** + + FileName [satTrace.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [SAT sat_solver.] + + Synopsis [Records the trace of SAT solving in the CNF form.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: satTrace.c,v 1.4 2005/09/16 22:55:03 casem Exp $] + +***********************************************************************/ + +#include +#include +#include "satSolver.h" + +/* + The trace of SAT solving contains the original clause of the problem + along with the learned clauses derived during SAT solving. + The first line of the resulting file contains 3 numbers instead of 2: + c +*/ + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Start the trace recording.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sat_SolverTraceStart( sat_solver * pSat, char * pName ) +{ + assert( pSat->pFile == NULL ); + pSat->pFile = fopen( pName, "w" ); + fprintf( pSat->pFile, " \n" ); + pSat->nClauses = 0; + pSat->nRoots = 0; +} + +/**Function************************************************************* + + Synopsis [Stops the trace recording.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sat_SolverTraceStop( sat_solver * pSat ) +{ + if ( pSat->pFile == NULL ) + return; + rewind( pSat->pFile ); + fprintf( pSat->pFile, "p %d %d %d", sat_solver_nvars(pSat), pSat->nClauses, pSat->nRoots ); + fclose( pSat->pFile ); + pSat->pFile = NULL; +} + + +/**Function************************************************************* + + Synopsis [Writes one clause into the trace file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sat_SolverTraceWrite( sat_solver * pSat, int * pBeg, int * pEnd, int fRoot ) +{ + if ( pSat->pFile == NULL ) + return; + pSat->nClauses++; + pSat->nRoots += fRoot; + for ( ; pBeg < pEnd ; pBeg++ ) + fprintf( pSat->pFile, " %d", lit_print(*pBeg) ); + fprintf( pSat->pFile, " 0\n" ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/bsat/satUtil.c b/abc_with_bb_support/src/sat/bsat/satUtil.c new file mode 100644 index 000000000..8275a4605 --- /dev/null +++ b/abc_with_bb_support/src/sat/bsat/satUtil.c @@ -0,0 +1,232 @@ +/**CFile**************************************************************** + + FileName [satUtil.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [C-language MiniSat solver.] + + Synopsis [Additional SAT solver procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: satUtil.c,v 1.4 2005/09/16 22:55:03 casem Exp $] + +***********************************************************************/ + +#include +#include +#include "satSolver.h" +#include "extra.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +struct clause_t +{ + int size_learnt; + lit lits[0]; +}; + +static inline int clause_size( clause* c ) { return c->size_learnt >> 1; } +static inline lit* clause_begin( clause* c ) { return c->lits; } + +static void Sat_SolverClauseWriteDimacs( FILE * pFile, clause * pC, bool fIncrement ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Write the clauses in the solver into a file in DIMACS format.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sat_SolverWriteDimacs( sat_solver * p, char * pFileName, lit* assumptionsBegin, lit* assumptionsEnd, int incrementVars ) +{ + FILE * pFile; + void ** pClauses; + int nClauses, i; + + // count the number of clauses + nClauses = p->clauses.size + p->learnts.size; + for ( i = 0; i < p->size; i++ ) + if ( p->levels[i] == 0 && p->assigns[i] != l_Undef ) + nClauses++; + + // start the file + pFile = fopen( pFileName, "wb" ); + if ( pFile == NULL ) + { + printf( "Sat_SolverWriteDimacs(): Cannot open the ouput file.\n" ); + return; + } + fprintf( pFile, "c CNF generated by ABC on %s\n", Extra_TimeStamp() ); + fprintf( pFile, "p cnf %d %d\n", p->size, nClauses ); + + // write the original clauses + nClauses = p->clauses.size; + pClauses = p->clauses.ptr; + for ( i = 0; i < nClauses; i++ ) + Sat_SolverClauseWriteDimacs( pFile, pClauses[i], incrementVars ); + + // write the learned clauses + nClauses = p->learnts.size; + pClauses = p->learnts.ptr; + for ( i = 0; i < nClauses; i++ ) + Sat_SolverClauseWriteDimacs( pFile, pClauses[i], incrementVars ); + + // write zero-level assertions + for ( i = 0; i < p->size; i++ ) + if ( p->levels[i] == 0 && p->assigns[i] != l_Undef ) + fprintf( pFile, "%s%d%s\n", + (p->assigns[i] == l_False)? "-": "", + i + (int)(incrementVars>0), + (incrementVars) ? " 0" : ""); + + // write the assumptions + if (assumptionsBegin) { + for (; assumptionsBegin != assumptionsEnd; assumptionsBegin++) { + fprintf( pFile, "%s%d%s\n", + lit_sign(*assumptionsBegin)? "-": "", + lit_var(*assumptionsBegin) + (int)(incrementVars>0), + (incrementVars) ? " 0" : ""); + } + } + + fprintf( pFile, "\n" ); + fclose( pFile ); +} + +/**Function************************************************************* + + Synopsis [Writes the given clause in a file in DIMACS format.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sat_SolverClauseWriteDimacs( FILE * pFile, clause * pC, bool fIncrement ) +{ + lit * pLits = clause_begin(pC); + int nLits = clause_size(pC); + int i; + + for ( i = 0; i < nLits; i++ ) + fprintf( pFile, "%s%d ", (lit_sign(pLits[i])? "-": ""), lit_var(pLits[i]) + (int)(fIncrement>0) ); + if ( fIncrement ) + fprintf( pFile, "0" ); + fprintf( pFile, "\n" ); +} + +/**Function************************************************************* + + Synopsis [Writes the given clause in a file in DIMACS format.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sat_SolverPrintStats( FILE * pFile, sat_solver * p ) +{ + printf( "starts : %d\n", (int)p->stats.starts ); + printf( "conflicts : %d\n", (int)p->stats.conflicts ); + printf( "decisions : %d\n", (int)p->stats.decisions ); + printf( "propagations : %d\n", (int)p->stats.propagations ); + printf( "inspects : %d\n", (int)p->stats.inspects ); +} + +/**Function************************************************************* + + Synopsis [Returns a counter-example.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int * Sat_SolverGetModel( sat_solver * p, int * pVars, int nVars ) +{ + int * pModel; + int i; + pModel = ALLOC( int, nVars ); + for ( i = 0; i < nVars; i++ ) + { + assert( pVars[i] >= 0 && pVars[i] < p->size ); + pModel[i] = (int)(p->model.ptr[pVars[i]] == l_True); + } + return pModel; +} + +/**Function************************************************************* + + Synopsis [Duplicates all clauses, complements unit clause of the given var.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sat_SolverDoubleClauses( sat_solver * p, int iVar ) +{ + clause * pClause; + lit Lit, * pLits; + int RetValue, nClauses, nVarsOld, nLitsOld, nLits, c, v; + // get the number of variables + nVarsOld = p->size; + nLitsOld = 2 * p->size; + // extend the solver to depend on two sets of variables + sat_solver_setnvars( p, 2 * p->size ); + // duplicate implications + for ( v = 0; v < nVarsOld; v++ ) + if ( p->assigns[v] != l_Undef ) + { + Lit = nLitsOld + toLitCond( v, p->assigns[v]==l_False ); + if ( v == iVar ) + Lit = lit_neg(Lit); + RetValue = sat_solver_addclause( p, &Lit, &Lit + 1 ); + assert( RetValue ); + } + // duplicate clauses + nClauses = vecp_size(&p->clauses); + for ( c = 0; c < nClauses; c++ ) + { + pClause = p->clauses.ptr[c]; + nLits = clause_size(pClause); + pLits = clause_begin(pClause); + for ( v = 0; v < nLits; v++ ) + pLits[v] += nLitsOld; + RetValue = sat_solver_addclause( p, pLits, pLits + nLits ); + assert( RetValue ); + for ( v = 0; v < nLits; v++ ) + pLits[v] -= nLitsOld; + } +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/bsat/satVec.h b/abc_with_bb_support/src/sat/bsat/satVec.h new file mode 100644 index 000000000..7f02f9f5f --- /dev/null +++ b/abc_with_bb_support/src/sat/bsat/satVec.h @@ -0,0 +1,83 @@ +/************************************************************************************************** +MiniSat -- Copyright (c) 2005, Niklas Sorensson +http://www.cs.chalmers.se/Cs/Research/FormalMethods/MiniSat/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ +// Modified to compile with MS Visual Studio 6.0 by Alan Mishchenko + +#ifndef satVec_h +#define satVec_h + +#include + +// vector of 32-bit intergers (added for 64-bit portability) +struct veci_t { + int size; + int cap; + int* ptr; +}; +typedef struct veci_t veci; + +static inline void veci_new (veci* v) { + v->size = 0; + v->cap = 4; + v->ptr = (int*)malloc(sizeof(int)*v->cap); +} + +static inline void veci_delete (veci* v) { free(v->ptr); } +static inline int* veci_begin (veci* v) { return v->ptr; } +static inline int veci_size (veci* v) { return v->size; } +static inline void veci_resize (veci* v, int k) { v->size = k; } // only safe to shrink !! +static inline void veci_push (veci* v, int e) +{ + if (v->size == v->cap) { + int newsize = v->cap * 2;//+1; + v->ptr = (int*)realloc(v->ptr,sizeof(int)*newsize); + v->cap = newsize; } + v->ptr[v->size++] = e; +} + + +// vector of 32- or 64-bit pointers +struct vecp_t { + int size; + int cap; + void** ptr; +}; +typedef struct vecp_t vecp; + +static inline void vecp_new (vecp* v) { + v->size = 0; + v->cap = 4; + v->ptr = (void**)malloc(sizeof(void*)*v->cap); +} + +static inline void vecp_delete (vecp* v) { free(v->ptr); } +static inline void** vecp_begin (vecp* v) { return v->ptr; } +static inline int vecp_size (vecp* v) { return v->size; } +static inline void vecp_resize (vecp* v, int k) { v->size = k; } // only safe to shrink !! +static inline void vecp_push (vecp* v, void* e) +{ + if (v->size == v->cap) { + int newsize = v->cap * 2;//+1; + v->ptr = (void**)realloc(v->ptr,sizeof(void*)*newsize); + v->cap = newsize; } + v->ptr[v->size++] = e; +} + + +#endif diff --git a/abc_with_bb_support/src/sat/csat/csat_apis.c b/abc_with_bb_support/src/sat/csat/csat_apis.c new file mode 100644 index 000000000..cf872dc6e --- /dev/null +++ b/abc_with_bb_support/src/sat/csat/csat_apis.c @@ -0,0 +1,769 @@ +/**CFile**************************************************************** + + FileName [csat_apis.h] + + PackageName [Interface to CSAT.] + + Synopsis [APIs, enums, and data structures expected from the use of CSAT.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - August 28, 2005] + + Revision [$Id: csat_apis.h,v 1.00 2005/08/28 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc.h" +#include "fraig.h" +#include "csat_apis.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define ABC_DEFAULT_CONF_LIMIT 0 // limit on conflicts +#define ABC_DEFAULT_IMP_LIMIT 0 // limit on implications + + +struct ABC_ManagerStruct_t +{ + // information about the problem + stmm_table * tName2Node; // the hash table mapping names to nodes + stmm_table * tNode2Name; // the hash table mapping nodes to names + Abc_Ntk_t * pNtk; // the starting ABC network + Abc_Ntk_t * pTarget; // the AIG representing the target + char * pDumpFileName; // the name of the file to dump the target network + Extra_MmFlex_t * pMmNames; // memory manager for signal names + // solving parameters + int mode; // 0 = resource-aware integration; 1 = brute-force SAT + Prove_Params_t Params; // integrated CEC parameters + // information about the target + int nog; // the numbers of gates in the target + Vec_Ptr_t * vNodes; // the gates in the target + Vec_Int_t * vValues; // the values of gate's outputs in the target + // solution + CSAT_Target_ResultT * pResult; // the result of solving the target +}; + +static CSAT_Target_ResultT * ABC_TargetResAlloc( int nVars ); +static char * ABC_GetNodeName( ABC_Manager mng, Abc_Obj_t * pNode ); + +// procedures to start and stop the ABC framework +extern void Abc_Start(); +extern void Abc_Stop(); + +// some external procedures +extern int Io_WriteBench( Abc_Ntk_t * pNtk, char * FileName ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Creates a new manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +ABC_Manager ABC_InitManager() +{ + ABC_Manager_t * mng; + Abc_Start(); + mng = ALLOC( ABC_Manager_t, 1 ); + memset( mng, 0, sizeof(ABC_Manager_t) ); + mng->pNtk = Abc_NtkAlloc( ABC_NTK_LOGIC, ABC_FUNC_SOP, 1 ); + mng->pNtk->pName = Extra_UtilStrsav("csat_network"); + mng->tName2Node = stmm_init_table(strcmp, stmm_strhash); + mng->tNode2Name = stmm_init_table(stmm_ptrcmp, stmm_ptrhash); + mng->pMmNames = Extra_MmFlexStart(); + mng->vNodes = Vec_PtrAlloc( 100 ); + mng->vValues = Vec_IntAlloc( 100 ); + mng->mode = 0; // set "resource-aware integration" as the default mode + // set default parameters for CEC + Prove_ParamsSetDefault( &mng->Params ); + // set infinite resource limit for the final mitering +// mng->Params.nMiteringLimitLast = ABC_INFINITY; + return mng; +} + +/**Function************************************************************* + + Synopsis [Deletes the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void ABC_ReleaseManager( ABC_Manager mng ) +{ + CSAT_Target_ResultT * p_res = ABC_Get_Target_Result( mng,0 ); + ABC_TargetResFree(p_res); + if ( mng->tNode2Name ) stmm_free_table( mng->tNode2Name ); + if ( mng->tName2Node ) stmm_free_table( mng->tName2Node ); + if ( mng->pMmNames ) Extra_MmFlexStop( mng->pMmNames ); + if ( mng->pNtk ) Abc_NtkDelete( mng->pNtk ); + if ( mng->pTarget ) Abc_NtkDelete( mng->pTarget ); + if ( mng->vNodes ) Vec_PtrFree( mng->vNodes ); + if ( mng->vValues ) Vec_IntFree( mng->vValues ); + FREE( mng->pDumpFileName ); + free( mng ); + Abc_Stop(); +} + +/**Function************************************************************* + + Synopsis [Sets solver options for learning.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void ABC_SetSolveOption( ABC_Manager mng, enum CSAT_OptionT option ) +{ +} + +/**Function************************************************************* + + Synopsis [Sets solving mode by brute-force SAT.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void ABC_UseOnlyCoreSatSolver( ABC_Manager mng ) +{ + mng->mode = 1; // switch to "brute-force SAT" as the solving option +} + +/**Function************************************************************* + + Synopsis [Adds a gate to the circuit.] + + Description [The meaning of the parameters are: + type: the type of the gate to be added + name: the name of the gate to be added, name should be unique in a circuit. + nofi: number of fanins of the gate to be added; + fanins: the name array of fanins of the gate to be added.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int ABC_AddGate( ABC_Manager mng, enum GateType type, char * name, int nofi, char ** fanins, int dc_attr ) +{ + Abc_Obj_t * pObj, * pFanin; + char * pSop, * pNewName; + int i; + + // save the name in the local memory manager + pNewName = Extra_MmFlexEntryFetch( mng->pMmNames, strlen(name) + 1 ); + strcpy( pNewName, name ); + name = pNewName; + + // consider different cases, create the node, and map the node into the name + switch( type ) + { + case CSAT_BPI: + case CSAT_BPPI: + if ( nofi != 0 ) + { printf( "ABC_AddGate: The PI/PPI gate \"%s\" has fanins.\n", name ); return 0; } + // create the PI + pObj = Abc_NtkCreatePi( mng->pNtk ); + stmm_insert( mng->tNode2Name, (char *)pObj, name ); + break; + case CSAT_CONST: + case CSAT_BAND: + case CSAT_BNAND: + case CSAT_BOR: + case CSAT_BNOR: + case CSAT_BXOR: + case CSAT_BXNOR: + case CSAT_BINV: + case CSAT_BBUF: + // create the node + pObj = Abc_NtkCreateNode( mng->pNtk ); + // create the fanins + for ( i = 0; i < nofi; i++ ) + { + if ( !stmm_lookup( mng->tName2Node, fanins[i], (char **)&pFanin ) ) + { printf( "ABC_AddGate: The fanin gate \"%s\" is not in the network.\n", fanins[i] ); return 0; } + Abc_ObjAddFanin( pObj, pFanin ); + } + // create the node function + switch( type ) + { + case CSAT_CONST: + if ( nofi != 0 ) + { printf( "ABC_AddGate: The constant gate \"%s\" has fanins.\n", name ); return 0; } + pSop = Abc_SopCreateConst1( mng->pNtk->pManFunc ); + break; + case CSAT_BAND: + if ( nofi < 1 ) + { printf( "ABC_AddGate: The AND gate \"%s\" no fanins.\n", name ); return 0; } + pSop = Abc_SopCreateAnd( mng->pNtk->pManFunc, nofi, NULL ); + break; + case CSAT_BNAND: + if ( nofi < 1 ) + { printf( "ABC_AddGate: The NAND gate \"%s\" no fanins.\n", name ); return 0; } + pSop = Abc_SopCreateNand( mng->pNtk->pManFunc, nofi ); + break; + case CSAT_BOR: + if ( nofi < 1 ) + { printf( "ABC_AddGate: The OR gate \"%s\" no fanins.\n", name ); return 0; } + pSop = Abc_SopCreateOr( mng->pNtk->pManFunc, nofi, NULL ); + break; + case CSAT_BNOR: + if ( nofi < 1 ) + { printf( "ABC_AddGate: The NOR gate \"%s\" no fanins.\n", name ); return 0; } + pSop = Abc_SopCreateNor( mng->pNtk->pManFunc, nofi ); + break; + case CSAT_BXOR: + if ( nofi < 1 ) + { printf( "ABC_AddGate: The XOR gate \"%s\" no fanins.\n", name ); return 0; } + if ( nofi > 2 ) + { printf( "ABC_AddGate: The XOR gate \"%s\" has more than two fanins.\n", name ); return 0; } + pSop = Abc_SopCreateXor( mng->pNtk->pManFunc, nofi ); + break; + case CSAT_BXNOR: + if ( nofi < 1 ) + { printf( "ABC_AddGate: The XNOR gate \"%s\" no fanins.\n", name ); return 0; } + if ( nofi > 2 ) + { printf( "ABC_AddGate: The XNOR gate \"%s\" has more than two fanins.\n", name ); return 0; } + pSop = Abc_SopCreateNxor( mng->pNtk->pManFunc, nofi ); + break; + case CSAT_BINV: + if ( nofi != 1 ) + { printf( "ABC_AddGate: The inverter gate \"%s\" does not have exactly one fanin.\n", name ); return 0; } + pSop = Abc_SopCreateInv( mng->pNtk->pManFunc ); + break; + case CSAT_BBUF: + if ( nofi != 1 ) + { printf( "ABC_AddGate: The buffer gate \"%s\" does not have exactly one fanin.\n", name ); return 0; } + pSop = Abc_SopCreateBuf( mng->pNtk->pManFunc ); + break; + default : + break; + } + Abc_ObjSetData( pObj, pSop ); + break; + case CSAT_BPPO: + case CSAT_BPO: + if ( nofi != 1 ) + { printf( "ABC_AddGate: The PO/PPO gate \"%s\" does not have exactly one fanin.\n", name ); return 0; } + // create the PO + pObj = Abc_NtkCreatePo( mng->pNtk ); + stmm_insert( mng->tNode2Name, (char *)pObj, name ); + // connect to the PO fanin + if ( !stmm_lookup( mng->tName2Node, fanins[0], (char **)&pFanin ) ) + { printf( "ABC_AddGate: The fanin gate \"%s\" is not in the network.\n", fanins[0] ); return 0; } + Abc_ObjAddFanin( pObj, pFanin ); + break; + default: + printf( "ABC_AddGate: Unknown gate type.\n" ); + break; + } + + // map the name into the node + if ( stmm_insert( mng->tName2Node, name, (char *)pObj ) ) + { printf( "ABC_AddGate: The same gate \"%s\" is added twice.\n", name ); return 0; } + return 1; +} + +/**Function************************************************************* + + Synopsis [This procedure also finalizes construction of the ABC network.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void ABC_Network_Finalize( ABC_Manager mng ) +{ + Abc_Ntk_t * pNtk = mng->pNtk; + Abc_Obj_t * pObj; + int i; + Abc_NtkForEachPi( pNtk, pObj, i ) + Abc_ObjAssignName( pObj, ABC_GetNodeName(mng, pObj), NULL ); + Abc_NtkForEachPo( pNtk, pObj, i ) + Abc_ObjAssignName( pObj, ABC_GetNodeName(mng, pObj), NULL ); + assert( Abc_NtkLatchNum(pNtk) == 0 ); +} + +/**Function************************************************************* + + Synopsis [Checks integraty of the manager.] + + Description [Checks if there are gates that are not used by any primary output. + If no such gates exist, return 1 else return 0.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int ABC_Check_Integrity( ABC_Manager mng ) +{ + Abc_Ntk_t * pNtk = mng->pNtk; + Abc_Obj_t * pObj; + int i; + + // check that there are no dangling nodes + Abc_NtkForEachNode( pNtk, pObj, i ) + { + if ( i == 0 ) + continue; + if ( Abc_ObjFanoutNum(pObj) == 0 ) + { +// printf( "ABC_Check_Integrity: The network has dangling nodes.\n" ); + return 0; + } + } + + // make sure everything is okay with the network structure + if ( !Abc_NtkDoCheck( pNtk ) ) + { + printf( "ABC_Check_Integrity: The internal network check has failed.\n" ); + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Sets time limit for solving a target.] + + Description [Runtime: time limit (in second).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void ABC_SetTimeLimit( ABC_Manager mng, int runtime ) +{ +// printf( "ABC_SetTimeLimit: The resource limit is not implemented (warning).\n" ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void ABC_SetLearnLimit( ABC_Manager mng, int num ) +{ +// printf( "ABC_SetLearnLimit: The resource limit is not implemented (warning).\n" ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void ABC_SetLearnBacktrackLimit( ABC_Manager mng, int num ) +{ +// printf( "ABC_SetLearnBacktrackLimit: The resource limit is not implemented (warning).\n" ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void ABC_SetSolveBacktrackLimit( ABC_Manager mng, int num ) +{ + mng->Params.nMiteringLimitLast = num; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void ABC_SetSolveImplicationLimit( ABC_Manager mng, int num ) +{ +// printf( "ABC_SetSolveImplicationLimit: The resource limit is not implemented (warning).\n" ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void ABC_SetTotalBacktrackLimit( ABC_Manager mng, uint64 num ) +{ + mng->Params.nTotalBacktrackLimit = num; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void ABC_SetTotalInspectLimit( ABC_Manager mng, uint64 num ) +{ + mng->Params.nTotalInspectLimit = num; +} +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +uint64 ABC_GetTotalBacktracksMade( ABC_Manager mng ) +{ + return mng->Params.nTotalBacktracksMade; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +uint64 ABC_GetTotalInspectsMade( ABC_Manager mng ) +{ + return mng->Params.nTotalInspectsMade; +} + +/**Function************************************************************* + + Synopsis [Sets the file name to dump the structurally hashed network used for solving.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void ABC_EnableDump( ABC_Manager mng, char * dump_file ) +{ + FREE( mng->pDumpFileName ); + mng->pDumpFileName = Extra_UtilStrsav( dump_file ); +} + +/**Function************************************************************* + + Synopsis [Adds a new target to the manager.] + + Description [The meaning of the parameters are: + nog: number of gates that are in the targets, + names: name array of gates, + values: value array of the corresponding gates given in "names" to be solved. + The relation of them is AND.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int ABC_AddTarget( ABC_Manager mng, int nog, char ** names, int * values ) +{ + Abc_Obj_t * pObj; + int i; + if ( nog < 1 ) + { printf( "ABC_AddTarget: The target has no gates.\n" ); return 0; } + // clear storage for the target + mng->nog = 0; + Vec_PtrClear( mng->vNodes ); + Vec_IntClear( mng->vValues ); + // save the target + for ( i = 0; i < nog; i++ ) + { + if ( !stmm_lookup( mng->tName2Node, names[i], (char **)&pObj ) ) + { printf( "ABC_AddTarget: The target gate \"%s\" is not in the network.\n", names[i] ); return 0; } + Vec_PtrPush( mng->vNodes, pObj ); + if ( values[i] < 0 || values[i] > 1 ) + { printf( "ABC_AddTarget: The value of gate \"%s\" is not 0 or 1.\n", names[i] ); return 0; } + Vec_IntPush( mng->vValues, values[i] ); + } + mng->nog = nog; + return 1; +} + +/**Function************************************************************* + + Synopsis [Initialize the solver internal data structure.] + + Description [Prepares the solver to work on one specific target + set by calling ABC_AddTarget before.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void ABC_SolveInit( ABC_Manager mng ) +{ + // check if the target is available + assert( mng->nog == Vec_PtrSize(mng->vNodes) ); + if ( mng->nog == 0 ) + { printf( "ABC_SolveInit: Target is not specified by ABC_AddTarget().\n" ); return; } + + // free the previous target network if present + if ( mng->pTarget ) Abc_NtkDelete( mng->pTarget ); + + // set the new target network +// mng->pTarget = Abc_NtkCreateTarget( mng->pNtk, mng->vNodes, mng->vValues ); + mng->pTarget = Abc_NtkStrash( mng->pNtk, 0, 1, 0 ); +} + +/**Function************************************************************* + + Synopsis [Currently not implemented.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void ABC_AnalyzeTargets( ABC_Manager mng ) +{ +} + +/**Function************************************************************* + + Synopsis [Solves the targets added by ABC_AddTarget().] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +enum CSAT_StatusT ABC_Solve( ABC_Manager mng ) +{ + Prove_Params_t * pParams = &mng->Params; + int RetValue, i; + + // check if the target network is available + if ( mng->pTarget == NULL ) + { printf( "ABC_Solve: Target network is not derived by ABC_SolveInit().\n" ); return UNDETERMINED; } + + // try to prove the miter using a number of techniques + if ( mng->mode ) + RetValue = Abc_NtkMiterSat( mng->pTarget, (sint64)pParams->nMiteringLimitLast, (sint64)0, 0, NULL, NULL ); + else +// RetValue = Abc_NtkMiterProve( &mng->pTarget, pParams ); // old CEC engine + RetValue = Abc_NtkIvyProve( &mng->pTarget, pParams ); // new CEC engine + + // analyze the result + mng->pResult = ABC_TargetResAlloc( Abc_NtkCiNum(mng->pTarget) ); + if ( RetValue == -1 ) + mng->pResult->status = UNDETERMINED; + else if ( RetValue == 1 ) + mng->pResult->status = UNSATISFIABLE; + else if ( RetValue == 0 ) + { + mng->pResult->status = SATISFIABLE; + // create the array of PI names and values + for ( i = 0; i < mng->pResult->no_sig; i++ ) + { + mng->pResult->names[i] = Extra_UtilStrsav( ABC_GetNodeName(mng, Abc_NtkCi(mng->pNtk, i)) ); + mng->pResult->values[i] = mng->pTarget->pModel[i]; + } + FREE( mng->pTarget->pModel ); + } + else assert( 0 ); + + // delete the target + Abc_NtkDelete( mng->pTarget ); + mng->pTarget = NULL; + // return the status + return mng->pResult->status; +} + +/**Function************************************************************* + + Synopsis [Gets the solve status of a target.] + + Description [TargetID: the target id returned by ABC_AddTarget().] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +CSAT_Target_ResultT * ABC_Get_Target_Result( ABC_Manager mng, int TargetID ) +{ + return mng->pResult; +} + +/**Function************************************************************* + + Synopsis [Dumps the original network into the BENCH file.] + + Description [This procedure should be modified to dump the target.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void ABC_Dump_Bench_File( ABC_Manager mng ) +{ + Abc_Ntk_t * pNtkTemp, * pNtkAig; + char * pFileName; + + // derive the netlist + pNtkAig = Abc_NtkStrash( mng->pNtk, 0, 0, 0 ); + pNtkTemp = Abc_NtkToNetlistBench( pNtkAig ); + Abc_NtkDelete( pNtkAig ); + if ( pNtkTemp == NULL ) + { printf( "ABC_Dump_Bench_File: Dumping BENCH has failed.\n" ); return; } + pFileName = mng->pDumpFileName? mng->pDumpFileName: "abc_test.bench"; + Io_WriteBench( pNtkTemp, pFileName ); + Abc_NtkDelete( pNtkTemp ); +} + + + +/**Function************************************************************* + + Synopsis [Allocates the target result.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +CSAT_Target_ResultT * ABC_TargetResAlloc( int nVars ) +{ + CSAT_Target_ResultT * p; + p = ALLOC( CSAT_Target_ResultT, 1 ); + memset( p, 0, sizeof(CSAT_Target_ResultT) ); + p->no_sig = nVars; + p->names = ALLOC( char *, nVars ); + p->values = ALLOC( int, nVars ); + memset( p->names, 0, sizeof(char *) * nVars ); + memset( p->values, 0, sizeof(int) * nVars ); + return p; +} + +/**Function************************************************************* + + Synopsis [Deallocates the target result.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void ABC_TargetResFree( CSAT_Target_ResultT * p ) +{ + if ( p == NULL ) + return; + if( p->names ) + { + int i = 0; + for ( i = 0; i < p->no_sig; i++ ) + { + FREE(p->names[i]); + } + } + FREE( p->names ); + FREE( p->values ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Dumps the target AIG into the BENCH file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * ABC_GetNodeName( ABC_Manager mng, Abc_Obj_t * pNode ) +{ + char * pName = NULL; + if ( !stmm_lookup( mng->tNode2Name, (char *)pNode, (char **)&pName ) ) + { + assert( 0 ); + } + return pName; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/csat/csat_apis.h b/abc_with_bb_support/src/sat/csat/csat_apis.h new file mode 100644 index 000000000..e3f3e720f --- /dev/null +++ b/abc_with_bb_support/src/sat/csat/csat_apis.h @@ -0,0 +1,222 @@ +/**CFile**************************************************************** + + FileName [csat_apis.h] + + PackageName [Interface to CSAT.] + + Synopsis [APIs, enums, and data structures expected from the use of CSAT.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - August 28, 2005] + + Revision [$Id: csat_apis.h,v 1.5 2005/12/30 10:54:40 rmukherj Exp $] + +***********************************************************************/ + +#ifndef __ABC_APIS_H__ +#define __ABC_APIS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + + +typedef struct ABC_ManagerStruct_t ABC_Manager_t; +typedef struct ABC_ManagerStruct_t * ABC_Manager; + + +// GateType defines the gate type that can be added to circuit by +// ABC_AddGate(); +#ifndef _ABC_GATE_TYPE_ +#define _ABC_GATE_TYPE_ +enum GateType +{ + CSAT_CONST = 0, // constant gate + CSAT_BPI, // boolean PI + CSAT_BPPI, // bit level PSEUDO PRIMARY INPUT + CSAT_BAND, // bit level AND + CSAT_BNAND, // bit level NAND + CSAT_BOR, // bit level OR + CSAT_BNOR, // bit level NOR + CSAT_BXOR, // bit level XOR + CSAT_BXNOR, // bit level XNOR + CSAT_BINV, // bit level INVERTER + CSAT_BBUF, // bit level BUFFER + CSAT_BMUX, // bit level MUX --not supported + CSAT_BDFF, // bit level D-type FF + CSAT_BSDFF, // bit level scan FF --not supported + CSAT_BTRIH, // bit level TRISTATE gate with active high control --not supported + CSAT_BTRIL, // bit level TRISTATE gate with active low control --not supported + CSAT_BBUS, // bit level BUS --not supported + CSAT_BPPO, // bit level PSEUDO PRIMARY OUTPUT + CSAT_BPO, // boolean PO + CSAT_BCNF, // boolean constraint + CSAT_BDC, // boolean don't care gate (2 input) +}; +#endif + + +//CSAT_StatusT defines the return value by ABC_Solve(); +#ifndef _ABC_STATUS_ +#define _ABC_STATUS_ +enum CSAT_StatusT +{ + UNDETERMINED = 0, + UNSATISFIABLE, + SATISFIABLE, + TIME_OUT, + FRAME_OUT, + NO_TARGET, + ABORTED, + SEQ_SATISFIABLE +}; +#endif + + +// to identify who called the CSAT solver +#ifndef _ABC_CALLER_ +#define _ABC_CALLER_ +enum CSAT_CallerT +{ + BLS = 0, + SATORI, + NONE +}; +#endif + + +// CSAT_OptionT defines the solver option about learning +// which is used by ABC_SetSolveOption(); +#ifndef _ABC_OPTION_ +#define _ABC_OPTION_ +enum CSAT_OptionT +{ + BASE_LINE = 0, + IMPLICT_LEARNING, //default + EXPLICT_LEARNING +}; +#endif + + +#ifndef _ABC_Target_Result +#define _ABC_Target_Result +typedef struct _CSAT_Target_ResultT CSAT_Target_ResultT; +struct _CSAT_Target_ResultT +{ + enum CSAT_StatusT status; // solve status of the target + int num_dec; // num of decisions to solve the target + int num_imp; // num of implications to solve the target + int num_cftg; // num of conflict gates learned + int num_cfts; // num of conflict signals in conflict gates + double time; // time(in second) used to solve the target + int no_sig; // if "status" is SATISFIABLE, "no_sig" is the number of + // primary inputs, if the "status" is TIME_OUT, "no_sig" is the + // number of constant signals found. + char** names; // if the "status" is SATISFIABLE, "names" is the name array of + // primary inputs, "values" is the value array of primary + // inputs that satisfy the target. + // if the "status" is TIME_OUT, "names" is the name array of + // constant signals found (signals at the root of decision + // tree), "values" is the value array of constant signals found. + int* values; +}; +#endif + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// create a new manager +extern ABC_Manager ABC_InitManager(void); + +// release a manager +extern void ABC_ReleaseManager(ABC_Manager mng); + +// set solver options for learning +extern void ABC_SetSolveOption(ABC_Manager mng, enum CSAT_OptionT option); + +// enable checking by brute-force SAT solver (MiniSat-1.14) +extern void ABC_UseOnlyCoreSatSolver(ABC_Manager mng); + + +// add a gate to the circuit +// the meaning of the parameters are: +// type: the type of the gate to be added +// name: the name of the gate to be added, name should be unique in a circuit. +// nofi: number of fanins of the gate to be added; +// fanins: the name array of fanins of the gate to be added +extern int ABC_AddGate(ABC_Manager mng, + enum GateType type, + char* name, + int nofi, + char** fanins, + int dc_attr); + +// check if there are gates that are not used by any primary ouput. +// if no such gates exist, return 1 else return 0; +extern int ABC_Check_Integrity(ABC_Manager mng); + +// THIS PROCEDURE SHOULD BE CALLED AFTER THE NETWORK IS CONSTRUCTED!!! +extern void ABC_Network_Finalize( ABC_Manager mng ); + +// set time limit for solving a target. +// runtime: time limit (in second). +extern void ABC_SetTimeLimit(ABC_Manager mng, int runtime); +extern void ABC_SetLearnLimit(ABC_Manager mng, int num); +extern void ABC_SetSolveBacktrackLimit(ABC_Manager mng, int num); +extern void ABC_SetLearnBacktrackLimit(ABC_Manager mng, int num); +extern void ABC_EnableDump(ABC_Manager mng, char* dump_file); + +extern void ABC_SetTotalBacktrackLimit( ABC_Manager mng, uint64 num ); +extern void ABC_SetTotalInspectLimit( ABC_Manager mng, uint64 num ); +extern uint64 ABC_GetTotalBacktracksMade( ABC_Manager mng ); +extern uint64 ABC_GetTotalInspectsMade( ABC_Manager mng ); + +// the meaning of the parameters are: +// nog: number of gates that are in the targets +// names: name array of gates +// values: value array of the corresponding gates given in "names" to be +// solved. the relation of them is AND. +extern int ABC_AddTarget(ABC_Manager mng, int nog, char**names, int* values); + +// initialize the solver internal data structure. +extern void ABC_SolveInit(ABC_Manager mng); +extern void ABC_AnalyzeTargets(ABC_Manager mng); + +// solve the targets added by ABC_AddTarget() +extern enum CSAT_StatusT ABC_Solve(ABC_Manager mng); + +// get the solve status of a target +// TargetID: the target id returned by ABC_AddTarget(). +extern CSAT_Target_ResultT * ABC_Get_Target_Result(ABC_Manager mng, int TargetID); +extern void ABC_Dump_Bench_File(ABC_Manager mng); + +// ADDED PROCEDURES: +extern void ABC_TargetResFree( CSAT_Target_ResultT * p ); + +extern void CSAT_SetCaller(ABC_Manager mng, enum CSAT_CallerT caller); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/sat/csat/module.make b/abc_with_bb_support/src/sat/csat/module.make new file mode 100644 index 000000000..5b71a03c0 --- /dev/null +++ b/abc_with_bb_support/src/sat/csat/module.make @@ -0,0 +1 @@ +SRC += src/sat/csat/csat_apis.c diff --git a/abc_with_bb_support/src/sat/fraig/fraig.h b/abc_with_bb_support/src/sat/fraig/fraig.h new file mode 100644 index 000000000..9a517b9c8 --- /dev/null +++ b/abc_with_bb_support/src/sat/fraig/fraig.h @@ -0,0 +1,267 @@ +/**CFile**************************************************************** + + FileName [fraig.h] + + PackageName [FRAIG: Functionally reduced AND-INV graphs.] + + Synopsis [External declarations of the FRAIG package.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - October 1, 2004] + + Revision [$Id: fraig.h,v 1.18 2005/07/08 01:01:30 alanmi Exp $] + +***********************************************************************/ + +#ifndef __FRAIG_H__ +#define __FRAIG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#ifndef SINT64 +#define SINT64 + +#ifdef _WIN32 +typedef signed __int64 sint64; // compatible with MS VS 6.0 +#else +typedef long long sint64; +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Fraig_ManStruct_t_ Fraig_Man_t; +typedef struct Fraig_NodeStruct_t_ Fraig_Node_t; +typedef struct Fraig_NodeVecStruct_t_ Fraig_NodeVec_t; +typedef struct Fraig_HashTableStruct_t_ Fraig_HashTable_t; +typedef struct Fraig_ParamsStruct_t_ Fraig_Params_t; +typedef struct Fraig_PatternsStruct_t_ Fraig_Patterns_t; +typedef struct Prove_ParamsStruct_t_ Prove_Params_t; + +struct Fraig_ParamsStruct_t_ +{ + int nPatsRand; // the number of words of random simulation info + int nPatsDyna; // the number of words of dynamic simulation info + int nBTLimit; // the max number of backtracks to perform + int nSeconds; // the timeout for the final proof + int fFuncRed; // performs only one level hashing + int fFeedBack; // enables solver feedback + int fDist1Pats; // enables distance-1 patterns + int fDoSparse; // performs equiv tests for sparse functions + int fChoicing; // enables recording structural choices + int fTryProve; // tries to solve the final miter + int fVerbose; // the verbosiness flag + int fVerboseP; // the verbosiness flag (for proof reporting) + int fInternal; // is set to 1 for internal fraig calls + int nConfLimit; // the limit on the number of conflicts + sint64 nInspLimit; // the limit on the number of inspections +}; + +struct Prove_ParamsStruct_t_ +{ + // general parameters + int fUseFraiging; // enables fraiging + int fUseRewriting; // enables rewriting + int fUseBdds; // enables BDD construction when other methods fail + int fVerbose; // prints verbose stats + // iterations + int nItersMax; // the number of iterations + // mitering + int nMiteringLimitStart; // starting mitering limit + float nMiteringLimitMulti; // multiplicative coefficient to increase the limit in each iteration + // rewriting + int nRewritingLimitStart; // the number of rewriting iterations + float nRewritingLimitMulti; // multiplicative coefficient to increase the limit in each iteration + // fraiging + int nFraigingLimitStart; // starting backtrack(conflict) limit + float nFraigingLimitMulti; // multiplicative coefficient to increase the limit in each iteration + // last-gasp BDD construction + int nBddSizeLimit; // the number of BDD nodes when construction is aborted + int fBddReorder; // enables dynamic BDD variable reordering + // last-gasp mitering + int nMiteringLimitLast; // final mitering limit + // global SAT solver limits + sint64 nTotalBacktrackLimit; // global limit on the number of backtracks + sint64 nTotalInspectLimit; // global limit on the number of clause inspects + // global resources applied + sint64 nTotalBacktracksMade; // the total number of backtracks made + sint64 nTotalInspectsMade; // the total number of inspects made +}; + +//////////////////////////////////////////////////////////////////////// +/// GLOBAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// macros working with complemented attributes of the nodes +#define Fraig_IsComplement(p) (((int)((unsigned long) (p) & 01))) +#define Fraig_Regular(p) ((Fraig_Node_t *)((unsigned long)(p) & ~01)) +#define Fraig_Not(p) ((Fraig_Node_t *)((unsigned long)(p) ^ 01)) +#define Fraig_NotCond(p,c) ((Fraig_Node_t *)((unsigned long)(p) ^ (c))) + +// these are currently not used +#define Fraig_Ref(p) +#define Fraig_Deref(p) +#define Fraig_RecursiveDeref(p,c) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== fraigApi.c =============================================================*/ +extern Fraig_NodeVec_t * Fraig_ManReadVecInputs( Fraig_Man_t * p ); +extern Fraig_NodeVec_t * Fraig_ManReadVecOutputs( Fraig_Man_t * p ); +extern Fraig_NodeVec_t * Fraig_ManReadVecNodes( Fraig_Man_t * p ); +extern Fraig_Node_t ** Fraig_ManReadInputs ( Fraig_Man_t * p ); +extern Fraig_Node_t ** Fraig_ManReadOutputs( Fraig_Man_t * p ); +extern Fraig_Node_t ** Fraig_ManReadNodes( Fraig_Man_t * p ); +extern int Fraig_ManReadInputNum ( Fraig_Man_t * p ); +extern int Fraig_ManReadOutputNum( Fraig_Man_t * p ); +extern int Fraig_ManReadNodeNum( Fraig_Man_t * p ); +extern Fraig_Node_t * Fraig_ManReadConst1 ( Fraig_Man_t * p ); +extern Fraig_Node_t * Fraig_ManReadIthVar( Fraig_Man_t * p, int i ); +extern Fraig_Node_t * Fraig_ManReadIthNode( Fraig_Man_t * p, int i ); +extern char ** Fraig_ManReadInputNames( Fraig_Man_t * p ); +extern char ** Fraig_ManReadOutputNames( Fraig_Man_t * p ); +extern char * Fraig_ManReadVarsInt( Fraig_Man_t * p ); +extern char * Fraig_ManReadSat( Fraig_Man_t * p ); +extern int Fraig_ManReadFuncRed( Fraig_Man_t * p ); +extern int Fraig_ManReadFeedBack( Fraig_Man_t * p ); +extern int Fraig_ManReadDoSparse( Fraig_Man_t * p ); +extern int Fraig_ManReadChoicing( Fraig_Man_t * p ); +extern int Fraig_ManReadVerbose( Fraig_Man_t * p ); +extern int * Fraig_ManReadModel( Fraig_Man_t * p ); +extern int Fraig_ManReadPatternNumRandom( Fraig_Man_t * p ); +extern int Fraig_ManReadPatternNumDynamic( Fraig_Man_t * p ); +extern int Fraig_ManReadPatternNumDynamicFiltered( Fraig_Man_t * p ); +extern int Fraig_ManReadSatFails( Fraig_Man_t * p ); +extern int Fraig_ManReadConflicts( Fraig_Man_t * p ); +extern int Fraig_ManReadInspects( Fraig_Man_t * p ); + +extern void Fraig_ManSetFuncRed( Fraig_Man_t * p, int fFuncRed ); +extern void Fraig_ManSetFeedBack( Fraig_Man_t * p, int fFeedBack ); +extern void Fraig_ManSetDoSparse( Fraig_Man_t * p, int fDoSparse ); +extern void Fraig_ManSetChoicing( Fraig_Man_t * p, int fChoicing ); +extern void Fraig_ManSetTryProve( Fraig_Man_t * p, int fTryProve ); +extern void Fraig_ManSetVerbose( Fraig_Man_t * p, int fVerbose ); +extern void Fraig_ManSetTimeToGraph( Fraig_Man_t * p, int Time ); +extern void Fraig_ManSetTimeToNet( Fraig_Man_t * p, int Time ); +extern void Fraig_ManSetTimeTotal( Fraig_Man_t * p, int Time ); +extern void Fraig_ManSetOutputNames( Fraig_Man_t * p, char ** ppNames ); +extern void Fraig_ManSetInputNames( Fraig_Man_t * p, char ** ppNames ); +extern void Fraig_ManSetPo( Fraig_Man_t * p, Fraig_Node_t * pNode ); + +extern Fraig_Node_t * Fraig_NodeReadData0( Fraig_Node_t * p ); +extern Fraig_Node_t * Fraig_NodeReadData1( Fraig_Node_t * p ); +extern int Fraig_NodeReadNum( Fraig_Node_t * p ); +extern Fraig_Node_t * Fraig_NodeReadOne( Fraig_Node_t * p ); +extern Fraig_Node_t * Fraig_NodeReadTwo( Fraig_Node_t * p ); +extern Fraig_Node_t * Fraig_NodeReadNextE( Fraig_Node_t * p ); +extern Fraig_Node_t * Fraig_NodeReadRepr( Fraig_Node_t * p ); +extern int Fraig_NodeReadNumRefs( Fraig_Node_t * p ); +extern int Fraig_NodeReadNumFanouts( Fraig_Node_t * p ); +extern int Fraig_NodeReadSimInv( Fraig_Node_t * p ); +extern int Fraig_NodeReadNumOnes( Fraig_Node_t * p ); +extern unsigned * Fraig_NodeReadPatternsRandom( Fraig_Node_t * p ); +extern unsigned * Fraig_NodeReadPatternsDynamic( Fraig_Node_t * p ); + +extern void Fraig_NodeSetData0( Fraig_Node_t * p, Fraig_Node_t * pData ); +extern void Fraig_NodeSetData1( Fraig_Node_t * p, Fraig_Node_t * pData ); + +extern int Fraig_NodeIsConst( Fraig_Node_t * p ); +extern int Fraig_NodeIsVar( Fraig_Node_t * p ); +extern int Fraig_NodeIsAnd( Fraig_Node_t * p ); +extern int Fraig_NodeComparePhase( Fraig_Node_t * p1, Fraig_Node_t * p2 ); + +extern Fraig_Node_t * Fraig_NodeOr( Fraig_Man_t * p, Fraig_Node_t * p1, Fraig_Node_t * p2 ); +extern Fraig_Node_t * Fraig_NodeAnd( Fraig_Man_t * p, Fraig_Node_t * p1, Fraig_Node_t * p2 ); +extern Fraig_Node_t * Fraig_NodeOr( Fraig_Man_t * p, Fraig_Node_t * p1, Fraig_Node_t * p2 ); +extern Fraig_Node_t * Fraig_NodeExor( Fraig_Man_t * p, Fraig_Node_t * p1, Fraig_Node_t * p2 ); +extern Fraig_Node_t * Fraig_NodeMux( Fraig_Man_t * p, Fraig_Node_t * pNode, Fraig_Node_t * pNodeT, Fraig_Node_t * pNodeE ); +extern void Fraig_NodeSetChoice( Fraig_Man_t * pMan, Fraig_Node_t * pNodeOld, Fraig_Node_t * pNodeNew ); + +/*=== fraigMan.c =============================================================*/ +extern void Prove_ParamsSetDefault( Prove_Params_t * pParams ); +extern void Fraig_ParamsSetDefault( Fraig_Params_t * pParams ); +extern void Fraig_ParamsSetDefaultFull( Fraig_Params_t * pParams ); +extern Fraig_Man_t * Fraig_ManCreate( Fraig_Params_t * pParams ); +extern void Fraig_ManFree( Fraig_Man_t * pMan ); +extern void Fraig_ManPrintStats( Fraig_Man_t * p ); +extern Fraig_NodeVec_t * Fraig_ManGetSimInfo( Fraig_Man_t * p ); +extern int Fraig_ManCheckClauseUsingSimInfo( Fraig_Man_t * p, Fraig_Node_t * pNode1, Fraig_Node_t * pNode2 ); +extern void Fraig_ManAddClause( Fraig_Man_t * p, Fraig_Node_t ** ppNodes, int nNodes ); + +/*=== fraigDfs.c =============================================================*/ +extern Fraig_NodeVec_t * Fraig_Dfs( Fraig_Man_t * pMan, int fEquiv ); +extern Fraig_NodeVec_t * Fraig_DfsOne( Fraig_Man_t * pMan, Fraig_Node_t * pNode, int fEquiv ); +extern Fraig_NodeVec_t * Fraig_DfsNodes( Fraig_Man_t * pMan, Fraig_Node_t ** ppNodes, int nNodes, int fEquiv ); +extern Fraig_NodeVec_t * Fraig_DfsReverse( Fraig_Man_t * pMan ); +extern int Fraig_CountNodes( Fraig_Man_t * pMan, int fEquiv ); +extern int Fraig_CheckTfi( Fraig_Man_t * pMan, Fraig_Node_t * pOld, Fraig_Node_t * pNew ); +extern int Fraig_CountLevels( Fraig_Man_t * pMan ); + +/*=== fraigSat.c =============================================================*/ +extern int Fraig_NodesAreEqual( Fraig_Man_t * p, Fraig_Node_t * pNode1, Fraig_Node_t * pNode2, int nBTLimit, int nTimeLimit ); +extern int Fraig_NodeIsEquivalent( Fraig_Man_t * p, Fraig_Node_t * pOld, Fraig_Node_t * pNew, int nBTLimit, int nTimeLimit ); +extern void Fraig_ManProveMiter( Fraig_Man_t * p ); +extern int Fraig_ManCheckMiter( Fraig_Man_t * p ); +extern int Fraig_ManCheckClauseUsingSat( Fraig_Man_t * p, Fraig_Node_t * pNode1, Fraig_Node_t * pNode2, int nBTLimit ); + +/*=== fraigVec.c ===============================================================*/ +extern Fraig_NodeVec_t * Fraig_NodeVecAlloc( int nCap ); +extern void Fraig_NodeVecFree( Fraig_NodeVec_t * p ); +extern Fraig_NodeVec_t * Fraig_NodeVecDup( Fraig_NodeVec_t * p ); +extern Fraig_Node_t ** Fraig_NodeVecReadArray( Fraig_NodeVec_t * p ); +extern int Fraig_NodeVecReadSize( Fraig_NodeVec_t * p ); +extern void Fraig_NodeVecGrow( Fraig_NodeVec_t * p, int nCapMin ); +extern void Fraig_NodeVecShrink( Fraig_NodeVec_t * p, int nSizeNew ); +extern void Fraig_NodeVecClear( Fraig_NodeVec_t * p ); +extern void Fraig_NodeVecPush( Fraig_NodeVec_t * p, Fraig_Node_t * Entry ); +extern int Fraig_NodeVecPushUnique( Fraig_NodeVec_t * p, Fraig_Node_t * Entry ); +extern void Fraig_NodeVecPushOrder( Fraig_NodeVec_t * p, Fraig_Node_t * pNode ); +extern int Fraig_NodeVecPushUniqueOrder( Fraig_NodeVec_t * p, Fraig_Node_t * pNode ); +extern void Fraig_NodeVecPushOrderByLevel( Fraig_NodeVec_t * p, Fraig_Node_t * pNode ); +extern int Fraig_NodeVecPushUniqueOrderByLevel( Fraig_NodeVec_t * p, Fraig_Node_t * pNode ); +extern Fraig_Node_t * Fraig_NodeVecPop( Fraig_NodeVec_t * p ); +extern void Fraig_NodeVecRemove( Fraig_NodeVec_t * p, Fraig_Node_t * Entry ); +extern void Fraig_NodeVecWriteEntry( Fraig_NodeVec_t * p, int i, Fraig_Node_t * Entry ); +extern Fraig_Node_t * Fraig_NodeVecReadEntry( Fraig_NodeVec_t * p, int i ); +extern void Fraig_NodeVecSortByLevel( Fraig_NodeVec_t * p, int fIncreasing ); +extern void Fraig_NodeVecSortByNumber( Fraig_NodeVec_t * p ); + +/*=== fraigUtil.c ===============================================================*/ +extern void Fraig_ManMarkRealFanouts( Fraig_Man_t * p ); +extern int Fraig_ManCheckConsistency( Fraig_Man_t * p ); +extern int Fraig_GetMaxLevel( Fraig_Man_t * pMan ); +extern void Fraig_ManReportChoices( Fraig_Man_t * pMan ); +extern void Fraig_MappingSetChoiceLevels( Fraig_Man_t * pMan, int fMaximum ); +extern Fraig_NodeVec_t * Fraig_CollectSupergate( Fraig_Node_t * pNode, int fStopAtMux ); + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/abc_with_bb_support/src/sat/fraig/fraigApi.c b/abc_with_bb_support/src/sat/fraig/fraigApi.c new file mode 100644 index 000000000..b3e2c7ad9 --- /dev/null +++ b/abc_with_bb_support/src/sat/fraig/fraigApi.c @@ -0,0 +1,297 @@ +/**CFile**************************************************************** + + FileName [fraigApi.c] + + PackageName [FRAIG: Functionally reduced AND-INV graphs.] + + Synopsis [Access APIs for the FRAIG manager and node.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - October 1, 2004] + + Revision [$Id: fraigApi.c,v 1.2 2005/07/08 01:01:30 alanmi Exp $] + +***********************************************************************/ + +#include "fraigInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Access functions to read the data members of the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_NodeVec_t * Fraig_ManReadVecInputs( Fraig_Man_t * p ) { return p->vInputs; } +Fraig_NodeVec_t * Fraig_ManReadVecOutputs( Fraig_Man_t * p ) { return p->vOutputs; } +Fraig_NodeVec_t * Fraig_ManReadVecNodes( Fraig_Man_t * p ) { return p->vNodes; } +Fraig_Node_t ** Fraig_ManReadInputs ( Fraig_Man_t * p ) { return p->vInputs->pArray; } +Fraig_Node_t ** Fraig_ManReadOutputs( Fraig_Man_t * p ) { return p->vOutputs->pArray; } +Fraig_Node_t ** Fraig_ManReadNodes( Fraig_Man_t * p ) { return p->vNodes->pArray; } +int Fraig_ManReadInputNum ( Fraig_Man_t * p ) { return p->vInputs->nSize; } +int Fraig_ManReadOutputNum( Fraig_Man_t * p ) { return p->vOutputs->nSize; } +int Fraig_ManReadNodeNum( Fraig_Man_t * p ) { return p->vNodes->nSize; } +Fraig_Node_t * Fraig_ManReadConst1 ( Fraig_Man_t * p ) { return p->pConst1; } +Fraig_Node_t * Fraig_ManReadIthNode( Fraig_Man_t * p, int i ) { assert ( i < p->vNodes->nSize ); return p->vNodes->pArray[i]; } +char ** Fraig_ManReadInputNames( Fraig_Man_t * p ) { return p->ppInputNames; } +char ** Fraig_ManReadOutputNames( Fraig_Man_t * p ) { return p->ppOutputNames; } +char * Fraig_ManReadVarsInt( Fraig_Man_t * p ) { return (char *)p->vVarsInt; } +char * Fraig_ManReadSat( Fraig_Man_t * p ) { return (char *)p->pSat; } +int Fraig_ManReadFuncRed( Fraig_Man_t * p ) { return p->fFuncRed; } +int Fraig_ManReadFeedBack( Fraig_Man_t * p ) { return p->fFeedBack; } +int Fraig_ManReadDoSparse( Fraig_Man_t * p ) { return p->fDoSparse; } +int Fraig_ManReadChoicing( Fraig_Man_t * p ) { return p->fChoicing; } +int Fraig_ManReadVerbose( Fraig_Man_t * p ) { return p->fVerbose; } +int * Fraig_ManReadModel( Fraig_Man_t * p ) { return p->pModel; } +// returns the number of patterns used for random simulation (this number is fixed for the FRAIG run) +int Fraig_ManReadPatternNumRandom( Fraig_Man_t * p ) { return p->nWordsRand * 32; } +// returns the number of dynamic patterns accumulated at runtime (include SAT solver counter-examples and distance-1 patterns derived from them) +int Fraig_ManReadPatternNumDynamic( Fraig_Man_t * p ) { return p->iWordStart * 32; } +// returns the number of dynamic patterns proved useful to distinquish some FRAIG nodes (this number is more than 0 after the first garbage collection of patterns) +int Fraig_ManReadPatternNumDynamicFiltered( Fraig_Man_t * p ) { return p->iPatsPerm; } +// returns the number of times FRAIG package timed out +int Fraig_ManReadSatFails( Fraig_Man_t * p ) { return p->nSatFailsReal; } +// returns the number of conflicts in the SAT solver +int Fraig_ManReadConflicts( Fraig_Man_t * p ) { return p->pSat? Msat_SolverReadBackTracks(p->pSat) : 0; } +// returns the number of inspections in the SAT solver +int Fraig_ManReadInspects( Fraig_Man_t * p ) { return p->pSat? Msat_SolverReadInspects(p->pSat) : 0; } + +/**Function************************************************************* + + Synopsis [Access functions to set the data members of the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_ManSetFuncRed( Fraig_Man_t * p, int fFuncRed ) { p->fFuncRed = fFuncRed; } +void Fraig_ManSetFeedBack( Fraig_Man_t * p, int fFeedBack ) { p->fFeedBack = fFeedBack; } +void Fraig_ManSetDoSparse( Fraig_Man_t * p, int fDoSparse ) { p->fDoSparse = fDoSparse; } +void Fraig_ManSetChoicing( Fraig_Man_t * p, int fChoicing ) { p->fChoicing = fChoicing; } +void Fraig_ManSetTryProve( Fraig_Man_t * p, int fTryProve ) { p->fTryProve = fTryProve; } +void Fraig_ManSetVerbose( Fraig_Man_t * p, int fVerbose ) { p->fVerbose = fVerbose; } +void Fraig_ManSetTimeToGraph( Fraig_Man_t * p, int Time ) { p->timeToAig = Time; } +void Fraig_ManSetTimeToNet( Fraig_Man_t * p, int Time ) { p->timeToNet = Time; } +void Fraig_ManSetTimeTotal( Fraig_Man_t * p, int Time ) { p->timeTotal = Time; } +void Fraig_ManSetOutputNames( Fraig_Man_t * p, char ** ppNames ) { p->ppOutputNames = ppNames; } +void Fraig_ManSetInputNames( Fraig_Man_t * p, char ** ppNames ) { p->ppInputNames = ppNames; } + +/**Function************************************************************* + + Synopsis [Access functions to read the data members of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_Node_t * Fraig_NodeReadData0( Fraig_Node_t * p ) { return p->pData0; } +Fraig_Node_t * Fraig_NodeReadData1( Fraig_Node_t * p ) { return p->pData1; } +int Fraig_NodeReadNum( Fraig_Node_t * p ) { return p->Num; } +Fraig_Node_t * Fraig_NodeReadOne( Fraig_Node_t * p ) { assert (!Fraig_IsComplement(p)); return p->p1; } +Fraig_Node_t * Fraig_NodeReadTwo( Fraig_Node_t * p ) { assert (!Fraig_IsComplement(p)); return p->p2; } +Fraig_Node_t * Fraig_NodeReadNextE( Fraig_Node_t * p ) { return p->pNextE; } +Fraig_Node_t * Fraig_NodeReadRepr( Fraig_Node_t * p ) { return p->pRepr; } +int Fraig_NodeReadNumRefs( Fraig_Node_t * p ) { return p->nRefs; } +int Fraig_NodeReadNumFanouts( Fraig_Node_t * p ) { return p->nFanouts; } +int Fraig_NodeReadSimInv( Fraig_Node_t * p ) { return p->fInv; } +int Fraig_NodeReadNumOnes( Fraig_Node_t * p ) { return p->nOnes; } +// returns the pointer to the random simulation patterns (their number is returned by Fraig_ManReadPatternNumRandom) +// memory pointed to by this and the following procedure is maintained by the FRAIG package and exists as long as the package runs +unsigned * Fraig_NodeReadPatternsRandom( Fraig_Node_t * p ) { return p->puSimR; } +// returns the pointer to the dynamic simulation patterns (their number is returned by Fraig_ManReadPatternNumDynamic or Fraig_ManReadPatternNumDynamicFiltered) +// if the number of patterns is not evenly divisible by 32, the patterns beyond the given number contain garbage +unsigned * Fraig_NodeReadPatternsDynamic( Fraig_Node_t * p ) { return p->puSimD; } + +/**Function************************************************************* + + Synopsis [Access functions to set the data members of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_NodeSetData0( Fraig_Node_t * p, Fraig_Node_t * pData ) { p->pData0 = pData; } +void Fraig_NodeSetData1( Fraig_Node_t * p, Fraig_Node_t * pData ) { p->pData1 = pData; } + +/**Function************************************************************* + + Synopsis [Checks the type of the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_NodeIsConst( Fraig_Node_t * p ) { return (Fraig_Regular(p))->Num == 0; } +int Fraig_NodeIsVar( Fraig_Node_t * p ) { return (Fraig_Regular(p))->NumPi >= 0; } +int Fraig_NodeIsAnd( Fraig_Node_t * p ) { return (Fraig_Regular(p))->NumPi < 0 && (Fraig_Regular(p))->Num > 0; } +int Fraig_NodeComparePhase( Fraig_Node_t * p1, Fraig_Node_t * p2 ) { assert( !Fraig_IsComplement(p1) ); assert( !Fraig_IsComplement(p2) ); return p1->fInv ^ p2->fInv; } + +/**Function************************************************************* + + Synopsis [Returns a new primary input node.] + + Description [If the node with this number does not exist, + create a new PI node with this number.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_Node_t * Fraig_ManReadIthVar( Fraig_Man_t * p, int i ) +{ + int k; + if ( i < 0 ) + { + printf( "Requesting a PI with a negative number\n" ); + return NULL; + } + // create the PIs to fill in the interval + if ( i >= p->vInputs->nSize ) + for ( k = p->vInputs->nSize; k <= i; k++ ) + Fraig_NodeCreatePi( p ); + return p->vInputs->pArray[i]; +} + +/**Function************************************************************* + + Synopsis [Creates a new PO node.] + + Description [This procedure may take a complemented node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_ManSetPo( Fraig_Man_t * p, Fraig_Node_t * pNode ) +{ + // internal node may be a PO two times + Fraig_Regular(pNode)->fNodePo = 1; + Fraig_NodeVecPush( p->vOutputs, pNode ); +} + +/**Function************************************************************* + + Synopsis [Perfoms the AND operation with functional hashing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_Node_t * Fraig_NodeAnd( Fraig_Man_t * p, Fraig_Node_t * p1, Fraig_Node_t * p2 ) +{ + return Fraig_NodeAndCanon( p, p1, p2 ); +} + +/**Function************************************************************* + + Synopsis [Perfoms the OR operation with functional hashing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_Node_t * Fraig_NodeOr( Fraig_Man_t * p, Fraig_Node_t * p1, Fraig_Node_t * p2 ) +{ + return Fraig_Not( Fraig_NodeAndCanon( p, Fraig_Not(p1), Fraig_Not(p2) ) ); +} + +/**Function************************************************************* + + Synopsis [Perfoms the EXOR operation with functional hashing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_Node_t * Fraig_NodeExor( Fraig_Man_t * p, Fraig_Node_t * p1, Fraig_Node_t * p2 ) +{ + return Fraig_NodeMux( p, p1, Fraig_Not(p2), p2 ); +} + +/**Function************************************************************* + + Synopsis [Perfoms the MUX operation with functional hashing.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_Node_t * Fraig_NodeMux( Fraig_Man_t * p, Fraig_Node_t * pC, Fraig_Node_t * pT, Fraig_Node_t * pE ) +{ + Fraig_Node_t * pAnd1, * pAnd2, * pRes; + pAnd1 = Fraig_NodeAndCanon( p, pC, pT ); Fraig_Ref( pAnd1 ); + pAnd2 = Fraig_NodeAndCanon( p, Fraig_Not(pC), pE ); Fraig_Ref( pAnd2 ); + pRes = Fraig_NodeOr( p, pAnd1, pAnd2 ); + Fraig_RecursiveDeref( p, pAnd1 ); + Fraig_RecursiveDeref( p, pAnd2 ); + Fraig_Deref( pRes ); + return pRes; +} + + +/**Function************************************************************* + + Synopsis [Sets the node to be equivalent to the given one.] + + Description [This procedure is a work-around for the equivalence check. + Does not verify the equivalence. Use at the user's risk.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_NodeSetChoice( Fraig_Man_t * pMan, Fraig_Node_t * pNodeOld, Fraig_Node_t * pNodeNew ) +{ +// assert( pMan->fChoicing ); + pNodeNew->pNextE = pNodeOld->pNextE; + pNodeOld->pNextE = pNodeNew; + pNodeNew->pRepr = pNodeOld; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/fraig/fraigCanon.c b/abc_with_bb_support/src/sat/fraig/fraigCanon.c new file mode 100644 index 000000000..a35bba06f --- /dev/null +++ b/abc_with_bb_support/src/sat/fraig/fraigCanon.c @@ -0,0 +1,218 @@ +/**CFile**************************************************************** + + FileName [fraigCanon.c] + + PackageName [FRAIG: Functionally reduced AND-INV graphs.] + + Synopsis [AND-node creation and elementary AND-operation.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - October 1, 2004] + + Revision [$Id: fraigCanon.c,v 1.4 2005/07/08 01:01:31 alanmi Exp $] + +***********************************************************************/ + +#include +#include "fraigInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [The internal AND operation for the two FRAIG nodes.] + + Description [This procedure is the core of the FRAIG package, because + it performs the two-step canonicization of FRAIG nodes. The first step + involves the lookup in the structural hash table (which hashes two ANDs + into a node that has them as fanins, if such a node exists). If the node + is not found in the structural hash table, an attempt is made to find a + functionally equivalent node in another hash table (which hashes the + simulation info into the nodes, which has this simulation info). Some + tricks used on the way are described in the comments to the code and + in the paper "FRAIGs: Functionally reduced AND-INV graphs".] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_Node_t * Fraig_NodeAndCanon( Fraig_Man_t * pMan, Fraig_Node_t * p1, Fraig_Node_t * p2 ) +{ + Fraig_Node_t * pNodeNew, * pNodeOld, * pNodeRepr; + int fUseSatCheck; +// int RetValue; + + // check for trivial cases + if ( p1 == p2 ) + return p1; + if ( p1 == Fraig_Not(p2) ) + return Fraig_Not(pMan->pConst1); + if ( Fraig_NodeIsConst(p1) ) + { + if ( p1 == pMan->pConst1 ) + return p2; + return Fraig_Not(pMan->pConst1); + } + if ( Fraig_NodeIsConst(p2) ) + { + if ( p2 == pMan->pConst1 ) + return p1; + return Fraig_Not(pMan->pConst1); + } +/* + // check for less trivial cases + if ( Fraig_IsComplement(p1) ) + { + if ( RetValue = Fraig_NodeIsInSupergate( Fraig_Regular(p1), p2 ) ) + { + if ( RetValue == -1 ) + pMan->nImplies0++; + else + pMan->nImplies1++; + + if ( RetValue == -1 ) + return p2; + } + } + else + { + if ( RetValue = Fraig_NodeIsInSupergate( p1, p2 ) ) + { + if ( RetValue == 1 ) + pMan->nSimplifies1++; + else + pMan->nSimplifies0++; + + if ( RetValue == 1 ) + return p1; + return Fraig_Not(pMan->pConst1); + } + } + + if ( Fraig_IsComplement(p2) ) + { + if ( RetValue = Fraig_NodeIsInSupergate( Fraig_Regular(p2), p1 ) ) + { + if ( RetValue == -1 ) + pMan->nImplies0++; + else + pMan->nImplies1++; + + if ( RetValue == -1 ) + return p1; + } + } + else + { + if ( RetValue = Fraig_NodeIsInSupergate( p2, p1 ) ) + { + if ( RetValue == 1 ) + pMan->nSimplifies1++; + else + pMan->nSimplifies0++; + + if ( RetValue == 1 ) + return p2; + return Fraig_Not(pMan->pConst1); + } + } +*/ + // perform level-one structural hashing + if ( Fraig_HashTableLookupS( pMan, p1, p2, &pNodeNew ) ) // the node with these children is found + { + // if the existent node is part of the cone of unused logic + // (that is logic feeding the node which is equivalent to the given node) + // return the canonical representative of this node + // determine the phase of the given node, with respect to its canonical form + pNodeRepr = Fraig_Regular(pNodeNew)->pRepr; + if ( pMan->fFuncRed && pNodeRepr ) + return Fraig_NotCond( pNodeRepr, Fraig_IsComplement(pNodeNew) ^ Fraig_NodeComparePhase(Fraig_Regular(pNodeNew), pNodeRepr) ); + // otherwise, the node is itself a canonical representative, return it + return pNodeNew; + } + // the same node is not found, but the new one is created + + // if one level hashing is requested (without functionality hashing), return + if ( !pMan->fFuncRed ) + return pNodeNew; + + // check if the new node is unique using the simulation info + if ( pNodeNew->nOnes == 0 || pNodeNew->nOnes == (unsigned)pMan->nWordsRand * 32 ) + { + pMan->nSatZeros++; + if ( !pMan->fDoSparse ) // if we do not do sparse functions, skip + return pNodeNew; + // check the sparse function simulation hash table + pNodeOld = Fraig_HashTableLookupF0( pMan, pNodeNew ); + if ( pNodeOld == NULL ) // the node is unique (it is added to the table) + return pNodeNew; + } + else + { + // check the simulation hash table + pNodeOld = Fraig_HashTableLookupF( pMan, pNodeNew ); + if ( pNodeOld == NULL ) // the node is unique + return pNodeNew; + } + assert( pNodeOld->pRepr == 0 ); + // there is another node which looks the same according to simulation + + // use SAT to resolve the ambiguity + fUseSatCheck = (pMan->nInspLimit == 0 || Fraig_ManReadInspects(pMan) < pMan->nInspLimit); + if ( fUseSatCheck && Fraig_NodeIsEquivalent( pMan, pNodeOld, pNodeNew, pMan->nBTLimit, 1000000 ) ) + { + // set the node to be equivalent with this node + // to prevent loops, only set if the old node is not in the TFI of the new node + // the loop may happen in the following case: suppose + // NodeC = AND(NodeA, NodeB) and at the same time NodeA => NodeB + // in this case, NodeA and NodeC are functionally equivalent + // however, NodeA is a fanin of node NodeC (this leads to the loop) + // add the node to the list of equivalent nodes or dereference it + if ( pMan->fChoicing && !Fraig_CheckTfi( pMan, pNodeOld, pNodeNew ) ) + { + // if the old node is not in the TFI of the new node and choicing + // is enabled, add the new node to the list of equivalent ones + pNodeNew->pNextE = pNodeOld->pNextE; + pNodeOld->pNextE = pNodeNew; + } + // set the canonical representative of this node + pNodeNew->pRepr = pNodeOld; + // return the equivalent node + return Fraig_NotCond( pNodeOld, Fraig_NodeComparePhase(pNodeOld, pNodeNew) ); + } + + // now we add another member to this simulation class + if ( pNodeNew->nOnes == 0 || pNodeNew->nOnes == (unsigned)pMan->nWordsRand * 32 ) + { + Fraig_Node_t * pNodeTemp; + assert( pMan->fDoSparse ); + pNodeTemp = Fraig_HashTableLookupF0( pMan, pNodeNew ); +// assert( pNodeTemp == NULL ); +// Fraig_HashTableInsertF0( pMan, pNodeNew ); + } + else + { + pNodeNew->pNextD = pNodeOld->pNextD; + pNodeOld->pNextD = pNodeNew; + } + // return the new node + assert( pNodeNew->pRepr == 0 ); + return pNodeNew; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/fraig/fraigChoice.c b/abc_with_bb_support/src/sat/fraig/fraigChoice.c new file mode 100644 index 000000000..6a36b0a3b --- /dev/null +++ b/abc_with_bb_support/src/sat/fraig/fraigChoice.c @@ -0,0 +1,241 @@ +/**CFile**************************************************************** + + FileName [fraigTrans.c] + + PackageName [MVSIS 1.3: Multi-valued logic synthesis system.] + + Synopsis [Adds the additive and distributive choices to the AIG.] + + Author [MVSIS Group] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - February 1, 2003.] + + Revision [$Id: fraigTrans.c,v 1.1 2005/02/28 05:34:34 alanmi Exp $] + +***********************************************************************/ + +#include "fraigInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Adds choice nodes based on associativity.] + + Description [Make nLimit big AND gates and add all decompositions + to the Fraig.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_ManAddChoices( Fraig_Man_t * pMan, int fVerbose, int nLimit ) +{ +// ProgressBar * pProgress; + char Buffer[100]; + int clkTotal = clock(); + int i, nNodesBefore, nNodesAfter, nInputs, nMaxNodes; + int /*nMaxLevel,*/ nDistributive; + Fraig_Node_t *pNode, *pRepr; + Fraig_Node_t *pX, *pA, *pB, *pC, /* *pD,*/ *pN, /* *pQ, *pR,*/ *pT; + int fShortCut = 0; + + nDistributive = 0; + +// Fraig_ManSetApprox( pMan, 1 ); + + // NO functional reduction + if (fShortCut) Fraig_ManSetFuncRed( pMan, 0 ); + + // First we mark critical functions i.e. compute those + // nodes which lie on the critical path. Note that this + // doesn't update the required times on any choice nodes + // which are not the representatives +/* + nMaxLevel = Fraig_GetMaxLevel( pMan ); + for ( i = 0; i < pMan->nOutputs; i++ ) + { + Fraig_SetNodeRequired( pMan, pMan->pOutputs[i], nMaxLevel ); + } +*/ + nNodesBefore = Fraig_ManReadNodeNum( pMan ); + nInputs = Fraig_ManReadInputNum( pMan ); + nMaxNodes = nInputs + nLimit * ( nNodesBefore - nInputs ); + + printf ("Limit = %d, Before = %d\n", nMaxNodes, nNodesBefore ); + + if (0) + { + char buffer[128]; + sprintf (buffer, "test" ); +// Fraig_MappingShow( pMan, buffer ); + } + +// pProgress = Extra_ProgressBarStart( stdout, nMaxNodes ); +Fraig_ManCheckConsistency( pMan ); + + for ( i = nInputs+1; (i < Fraig_ManReadNodeNum( pMan )) + && (nMaxNodes > Fraig_ManReadNodeNum( pMan )); ++i ) + { +// if ( i == nNodesBefore ) +// break; + + pNode = Fraig_ManReadIthNode( pMan, i ); + assert ( pNode ); + + pRepr = pNode->pRepr ? pNode->pRepr : pNode; + //printf ("Slack: %d\n", Fraig_NodeReadSlack( pRepr )); + + // All the new associative choices we add will have huge slack + // since we do not redo timing, and timing doesnt handle choices + // well anyway. However every newly added node is a choice of an + // existing critical node, so they are considered critical. +// if ( (Fraig_NodeReadSlack( pRepr ) > 3) && (i < nNodesBefore) ) +// continue; + +// if ( pNode->pRepr ) +// continue; + + // Try ((ab)c), x = ab -> (a(bc)) and (b(ac)) + pX = Fraig_NodeReadOne(pNode); + pC = Fraig_NodeReadTwo(pNode); + if (Fraig_NodeIsAnd(pX) && !Fraig_IsComplement(pX)) + { + pA = Fraig_NodeReadOne(Fraig_Regular(pX)); + pB = Fraig_NodeReadTwo(Fraig_Regular(pX)); + +// pA = Fraig_NodeGetRepr( pA ); +// pB = Fraig_NodeGetRepr( pB ); +// pC = Fraig_NodeGetRepr( pC ); + + if (fShortCut) + { + pT = Fraig_NodeAnd(pMan, pB, pC); + if ( !pT->pRepr ) + { + pN = Fraig_NodeAnd(pMan, pA, pT); +// Fraig_NodeAddChoice( pMan, pNode, pN ); + } + } + else + pN = Fraig_NodeAnd(pMan, pA, Fraig_NodeAnd(pMan, pB, pC)); + // assert ( Fraig_NodesEqual(pN, pNode) ); + + + if (fShortCut) + { + pT = Fraig_NodeAnd(pMan, pA, pC); + if ( !pT->pRepr ) + { + pN = Fraig_NodeAnd(pMan, pB, pT); +// Fraig_NodeAddChoice( pMan, pNode, pN ); + } + } + else + pN = Fraig_NodeAnd(pMan, pB, Fraig_NodeAnd(pMan, pA, pC)); + // assert ( Fraig_NodesEqual(pN, pNode) ); + } + + + // Try (a(bc)), x = bc -> ((ab)c) and ((ac)b) + pA = Fraig_NodeReadOne(pNode); + pX = Fraig_NodeReadTwo(pNode); + if (Fraig_NodeIsAnd(pX) && !Fraig_IsComplement(pX)) + { + pB = Fraig_NodeReadOne(Fraig_Regular(pX)); + pC = Fraig_NodeReadTwo(Fraig_Regular(pX)); + +// pA = Fraig_NodeGetRepr( pA ); +// pB = Fraig_NodeGetRepr( pB ); +// pC = Fraig_NodeGetRepr( pC ); + + if (fShortCut) + { + pT = Fraig_NodeAnd(pMan, pA, pB); + if ( !pT->pRepr ) + { + pN = Fraig_NodeAnd(pMan, pC, pT); +// Fraig_NodeAddChoice( pMan, pNode, pN ); + } + } + else + pN = Fraig_NodeAnd(pMan, Fraig_NodeAnd(pMan, pA, pB), pC); + // assert ( Fraig_NodesEqual(pN, pNode) ); + + if (fShortCut) + { + pT = Fraig_NodeAnd(pMan, pA, pC); + if ( !pT->pRepr ) + { + pN = Fraig_NodeAnd(pMan, pB, pT); +// Fraig_NodeAddChoice( pMan, pNode, pN ); + } + } + else + pN = Fraig_NodeAnd(pMan, Fraig_NodeAnd(pMan, pA, pC), pB); + // assert ( Fraig_NodesEqual(pN, pNode) ); + } + + +/* + // Try distributive transform + pQ = Fraig_NodeReadOne(pNode); + pR = Fraig_NodeReadTwo(pNode); + if ( (Fraig_IsComplement(pQ) && Fraig_NodeIsAnd(pQ)) + && (Fraig_IsComplement(pR) && Fraig_NodeIsAnd(pR)) ) + { + pA = Fraig_NodeReadOne(Fraig_Regular(pQ)); + pB = Fraig_NodeReadTwo(Fraig_Regular(pQ)); + pC = Fraig_NodeReadOne(Fraig_Regular(pR)); + pD = Fraig_NodeReadTwo(Fraig_Regular(pR)); + + // Now detect the !(xy + xz) pattern, store + // x in pA, y in pB and z in pC and set pD = 0 to indicate + // pattern was found + assert (pD != 0); + if (pA == pC) { pC = pD; pD = 0; } + if (pA == pD) { pD = 0; } + if (pB == pC) { pB = pA; pA = pC; pC = pD; pD = 0; } + if (pB == pD) { pB = pA; pA = pD; pD = 0; } + if (pD == 0) + { + nDistributive++; + pN = Fraig_Not(Fraig_NodeAnd(pMan, pA, + Fraig_NodeOr(pMan, pB, pC))); + if (fShortCut) Fraig_NodeAddChoice( pMan, pNode, pN ); + // assert ( Fraig_NodesEqual(pN, pNode) ); + } + } +*/ + if ( i % 1000 == 0 ) + { + sprintf( Buffer, "Adding choice %6d...", i - nNodesBefore ); +// Extra_ProgressBarUpdate( pProgress, i, Buffer ); + } + } + +// Extra_ProgressBarStop( pProgress ); + +Fraig_ManCheckConsistency( pMan ); + + nNodesAfter = Fraig_ManReadNodeNum( pMan ); + printf ( "Nodes before = %6d. Nodes with associative choices = %6d. Increase = %4.2f %%.\n", + nNodesBefore, nNodesAfter, ((float)(nNodesAfter - nNodesBefore)) * 100.0/(nNodesBefore - nInputs) ); + printf ( "Distributive = %d\n", nDistributive ); + +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/fraig/fraigFanout.c b/abc_with_bb_support/src/sat/fraig/fraigFanout.c new file mode 100644 index 000000000..136e901d1 --- /dev/null +++ b/abc_with_bb_support/src/sat/fraig/fraigFanout.c @@ -0,0 +1,175 @@ +/**CFile**************************************************************** + + FileName [fraigFanout.c] + + PackageName [FRAIG: Functionally reduced AND-INV graphs.] + + Synopsis [Procedures to manipulate fanouts of the FRAIG nodes.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - October 1, 2004] + + Revision [$Id: fraigFanout.c,v 1.5 2005/07/08 01:01:31 alanmi Exp $] + +***********************************************************************/ + +#include "fraigInt.h" + +#ifdef FRAIG_ENABLE_FANOUTS + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Add the fanout to the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_NodeAddFaninFanout( Fraig_Node_t * pFanin, Fraig_Node_t * pFanout ) +{ + Fraig_Node_t * pPivot; + + // pFanins is a fanin of pFanout + assert( !Fraig_IsComplement(pFanin) ); + assert( !Fraig_IsComplement(pFanout) ); + assert( Fraig_Regular(pFanout->p1) == pFanin || Fraig_Regular(pFanout->p2) == pFanin ); + + pPivot = pFanin->pFanPivot; + if ( pPivot == NULL ) + { + pFanin->pFanPivot = pFanout; + return; + } + + if ( Fraig_Regular(pPivot->p1) == pFanin ) + { + if ( Fraig_Regular(pFanout->p1) == pFanin ) + { + pFanout->pFanFanin1 = pPivot->pFanFanin1; + pPivot->pFanFanin1 = pFanout; + } + else // if ( Fraig_Regular(pFanout->p2) == pFanin ) + { + pFanout->pFanFanin2 = pPivot->pFanFanin1; + pPivot->pFanFanin1 = pFanout; + } + } + else // if ( Fraig_Regular(pPivot->p2) == pFanin ) + { + assert( Fraig_Regular(pPivot->p2) == pFanin ); + if ( Fraig_Regular(pFanout->p1) == pFanin ) + { + pFanout->pFanFanin1 = pPivot->pFanFanin2; + pPivot->pFanFanin2 = pFanout; + } + else // if ( Fraig_Regular(pFanout->p2) == pFanin ) + { + pFanout->pFanFanin2 = pPivot->pFanFanin2; + pPivot->pFanFanin2 = pFanout; + } + } +} + +/**Function************************************************************* + + Synopsis [Add the fanout to the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_NodeRemoveFaninFanout( Fraig_Node_t * pFanin, Fraig_Node_t * pFanoutToRemove ) +{ + Fraig_Node_t * pFanout, * pFanout2, ** ppFanList; + // start the linked list of fanouts + ppFanList = &pFanin->pFanPivot; + // go through the fanouts + Fraig_NodeForEachFanoutSafe( pFanin, pFanout, pFanout2 ) + { + // skip the fanout-to-remove + if ( pFanout == pFanoutToRemove ) + continue; + // add useful fanouts to the list + *ppFanList = pFanout; + ppFanList = Fraig_NodeReadNextFanoutPlace( pFanin, pFanout ); + } + *ppFanList = NULL; +} + +/**Function************************************************************* + + Synopsis [Transfers fanout to a different node.] + + Description [Assumes that the other node currently has no fanouts.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_NodeTransferFanout( Fraig_Node_t * pNodeFrom, Fraig_Node_t * pNodeTo ) +{ + Fraig_Node_t * pFanout; + assert( pNodeTo->pFanPivot == NULL ); + assert( pNodeTo->pFanFanin1 == NULL ); + assert( pNodeTo->pFanFanin2 == NULL ); + // go through the fanouts and update their fanins + Fraig_NodeForEachFanout( pNodeFrom, pFanout ) + { + if ( Fraig_Regular(pFanout->p1) == pNodeFrom ) + pFanout->p1 = Fraig_NotCond( pNodeTo, Fraig_IsComplement(pFanout->p1) ); + else if ( Fraig_Regular(pFanout->p2) == pNodeFrom ) + pFanout->p2 = Fraig_NotCond( pNodeTo, Fraig_IsComplement(pFanout->p2) ); + } + // move the pointers + pNodeTo->pFanPivot = pNodeFrom->pFanPivot; + pNodeTo->pFanFanin1 = pNodeFrom->pFanFanin1; + pNodeTo->pFanFanin2 = pNodeFrom->pFanFanin2; + pNodeFrom->pFanPivot = NULL; + pNodeFrom->pFanFanin1 = NULL; + pNodeFrom->pFanFanin2 = NULL; +} + +/**Function************************************************************* + + Synopsis [Returns the number of fanouts of a node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_NodeGetFanoutNum( Fraig_Node_t * pNode ) +{ + Fraig_Node_t * pFanout; + int Counter = 0; + Fraig_NodeForEachFanout( pNode, pFanout ) + Counter++; + return Counter; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + +#endif + diff --git a/abc_with_bb_support/src/sat/fraig/fraigFeed.c b/abc_with_bb_support/src/sat/fraig/fraigFeed.c new file mode 100644 index 000000000..cbeb0363d --- /dev/null +++ b/abc_with_bb_support/src/sat/fraig/fraigFeed.c @@ -0,0 +1,909 @@ +/**CFile**************************************************************** + + FileName [fraigFeed.c] + + PackageName [FRAIG: Functionally reduced AND-INV graphs.] + + Synopsis [Procedures to support the solver feedback.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - October 1, 2004] + + Revision [$Id: fraigFeed.c,v 1.8 2005/07/08 01:01:31 alanmi Exp $] + +***********************************************************************/ + +#include "fraigInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Fraig_FeedBackPrepare( Fraig_Man_t * p, int * pModel, Msat_IntVec_t * vVars ); +static int Fraig_FeedBackInsert( Fraig_Man_t * p, int nVarsPi ); +static void Fraig_FeedBackVerify( Fraig_Man_t * p, Fraig_Node_t * pOld, Fraig_Node_t * pNew ); + +static void Fraig_FeedBackCovering( Fraig_Man_t * p, Msat_IntVec_t * vPats ); +static Fraig_NodeVec_t * Fraig_FeedBackCoveringStart( Fraig_Man_t * pMan ); +static int Fraig_GetSmallestColumn( int * pHits, int nHits ); +static int Fraig_GetHittingPattern( unsigned * pSims, int nWords ); +static void Fraig_CancelCoveredColumns( Fraig_NodeVec_t * vColumns, int * pHits, int iPat ); +static void Fraig_FeedBackCheckTable( Fraig_Man_t * p ); +static void Fraig_FeedBackCheckTableF0( Fraig_Man_t * p ); +static void Fraig_ReallocateSimulationInfo( Fraig_Man_t * p ); + + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Initializes the feedback information.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_FeedBackInit( Fraig_Man_t * p ) +{ + p->vCones = Fraig_NodeVecAlloc( 500 ); + p->vPatsReal = Msat_IntVecAlloc( 1000 ); + p->pSimsReal = (unsigned *)Fraig_MemFixedEntryFetch( p->mmSims ); + memset( p->pSimsReal, 0, sizeof(unsigned) * p->nWordsDyna ); + p->pSimsTemp = (unsigned *)Fraig_MemFixedEntryFetch( p->mmSims ); + p->pSimsDiff = (unsigned *)Fraig_MemFixedEntryFetch( p->mmSims ); +} + +/**Function************************************************************* + + Synopsis [Processes the feedback from teh solver.] + + Description [Array pModel gives the value of each variable in the SAT + solver. Array vVars is the array of integer numbers of variables + involves in this conflict.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_FeedBack( Fraig_Man_t * p, int * pModel, Msat_IntVec_t * vVars, Fraig_Node_t * pOld, Fraig_Node_t * pNew ) +{ + int nVarsPi, nWords; + int i, clk = clock(); + + // get the number of PI vars in the feedback (also sets the PI values) + nVarsPi = Fraig_FeedBackPrepare( p, pModel, vVars ); + + // set the PI values + nWords = Fraig_FeedBackInsert( p, nVarsPi ); + assert( p->iWordStart + nWords <= p->nWordsDyna ); + + // resimulates the words from p->iWordStart to iWordStop + for ( i = 1; i < p->vNodes->nSize; i++ ) + if ( Fraig_NodeIsAnd(p->vNodes->pArray[i]) ) + Fraig_NodeSimulate( p->vNodes->pArray[i], p->iWordStart, p->iWordStart + nWords, 0 ); + + if ( p->fDoSparse ) + Fraig_TableRehashF0( p, 0 ); + + if ( !p->fChoicing ) + Fraig_FeedBackVerify( p, pOld, pNew ); + + // if there is no room left, compress the patterns + if ( p->iWordStart + nWords == p->nWordsDyna ) + p->iWordStart = Fraig_FeedBackCompress( p ); + else // otherwise, update the starting word + p->iWordStart += nWords; + +p->timeFeed += clock() - clk; +} + +/**Function************************************************************* + + Synopsis [Get the number and values of the PI variables.] + + Description [Returns the number of PI variables involved in this feedback. + Fills in the internal presence and value data for the primary inputs.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_FeedBackPrepare( Fraig_Man_t * p, int * pModel, Msat_IntVec_t * vVars ) +{ + Fraig_Node_t * pNode; + int i, nVars, nVarsPis, * pVars; + + // clean the presence flag for all PIs + for ( i = 0; i < p->vInputs->nSize; i++ ) + { + pNode = p->vInputs->pArray[i]; + pNode->fFeedUse = 0; + } + + // get the variables involved in the feedback + nVars = Msat_IntVecReadSize(vVars); + pVars = Msat_IntVecReadArray(vVars); + + // set the values for the present variables + nVarsPis = 0; + for ( i = 0; i < nVars; i++ ) + { + pNode = p->vNodes->pArray[ pVars[i] ]; + if ( !Fraig_NodeIsVar(pNode) ) + continue; + // set its value + pNode->fFeedUse = 1; + pNode->fFeedVal = !MSAT_LITSIGN(pModel[pVars[i]]); + nVarsPis++; + } + return nVarsPis; +} + +/**Function************************************************************* + + Synopsis [Inserts the new simulation patterns.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_FeedBackInsert( Fraig_Man_t * p, int nVarsPi ) +{ + Fraig_Node_t * pNode; + int nWords, iPatFlip, nPatFlipLimit, i, w; + int fUseNoPats = 0; + int fUse2Pats = 0; + + // get the number of words + if ( fUse2Pats ) + nWords = FRAIG_NUM_WORDS( 2 * nVarsPi + 1 ); + else if ( fUseNoPats ) + nWords = 1; + else + nWords = FRAIG_NUM_WORDS( nVarsPi + 1 ); + // update the number of words if they do not fit into the simulation info + if ( nWords > p->nWordsDyna - p->iWordStart ) + nWords = p->nWordsDyna - p->iWordStart; + // determine the bound on the flipping bit + nPatFlipLimit = nWords * 32 - 2; + + // mark the real pattern + Msat_IntVecPush( p->vPatsReal, p->iWordStart * 32 ); + // record the real pattern + Fraig_BitStringSetBit( p->pSimsReal, p->iWordStart * 32 ); + + // set the values at the PIs + iPatFlip = 1; + for ( i = 0; i < p->vInputs->nSize; i++ ) + { + pNode = p->vInputs->pArray[i]; + for ( w = p->iWordStart; w < p->iWordStart + nWords; w++ ) + if ( !pNode->fFeedUse ) + pNode->puSimD[w] = FRAIG_RANDOM_UNSIGNED; + else if ( pNode->fFeedVal ) + pNode->puSimD[w] = FRAIG_FULL; + else // if ( !pNode->fFeedVal ) + pNode->puSimD[w] = 0; + + if ( fUse2Pats ) + { + // flip two patterns + if ( pNode->fFeedUse && 2 * iPatFlip < nPatFlipLimit ) + { + Fraig_BitStringXorBit( pNode->puSimD + p->iWordStart, 2 * iPatFlip - 1 ); + Fraig_BitStringXorBit( pNode->puSimD + p->iWordStart, 2 * iPatFlip ); + Fraig_BitStringXorBit( pNode->puSimD + p->iWordStart, 2 * iPatFlip + 1 ); + iPatFlip++; + } + } + else if ( fUseNoPats ) + { + } + else + { + // flip the diagonal + if ( pNode->fFeedUse && iPatFlip < nPatFlipLimit ) + { + Fraig_BitStringXorBit( pNode->puSimD + p->iWordStart, iPatFlip ); + iPatFlip++; + // Extra_PrintBinary( stdout, &pNode->puSimD, 45 ); printf( "\n" ); + } + } + // clean the use mask + pNode->fFeedUse = 0; + + // add the info to the D hash value of the PIs + for ( w = p->iWordStart; w < p->iWordStart + nWords; w++ ) + pNode->uHashD ^= pNode->puSimD[w] * s_FraigPrimes[w]; + + } + return nWords; +} + + +/**Function************************************************************* + + Synopsis [Checks that the SAT solver pattern indeed distinquishes the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_FeedBackVerify( Fraig_Man_t * p, Fraig_Node_t * pOld, Fraig_Node_t * pNew ) +{ + int fValue1, fValue2, iPat; + iPat = Msat_IntVecReadEntry( p->vPatsReal, Msat_IntVecReadSize(p->vPatsReal)-1 ); + fValue1 = (Fraig_BitStringHasBit( pOld->puSimD, iPat )); + fValue2 = (Fraig_BitStringHasBit( pNew->puSimD, iPat )); +/* +Fraig_PrintNode( p, pOld ); +printf( "\n" ); +Fraig_PrintNode( p, pNew ); +printf( "\n" ); +*/ +// assert( fValue1 != fValue2 ); +} + +/**Function************************************************************* + + Synopsis [Compress the simulation patterns.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_FeedBackCompress( Fraig_Man_t * p ) +{ + unsigned * pSims; + unsigned uHash; + int i, w, t, nPats, * pPats; + int fPerformChecks = (p->nBTLimit == -1); + + // solve the covering problem + if ( fPerformChecks ) + { + Fraig_FeedBackCheckTable( p ); + if ( p->fDoSparse ) + Fraig_FeedBackCheckTableF0( p ); + } + + // solve the covering problem + Fraig_FeedBackCovering( p, p->vPatsReal ); + + + // get the number of additional patterns + nPats = Msat_IntVecReadSize( p->vPatsReal ); + pPats = Msat_IntVecReadArray( p->vPatsReal ); + // get the new starting word + p->iWordStart = FRAIG_NUM_WORDS( p->iPatsPerm + nPats ); + + // set the simulation info for the PIs + for ( i = 0; i < p->vInputs->nSize; i++ ) + { + // get hold of the simulation info for this PI + pSims = p->vInputs->pArray[i]->puSimD; + // clean the storage for the new patterns + for ( w = p->iWordPerm; w < p->iWordStart; w++ ) + p->pSimsTemp[w] = 0; + // set the patterns + for ( t = 0; t < nPats; t++ ) + if ( Fraig_BitStringHasBit( pSims, pPats[t] ) ) + { + // check if this pattern falls into temporary storage + if ( p->iPatsPerm + t < p->iWordPerm * 32 ) + Fraig_BitStringSetBit( pSims, p->iPatsPerm + t ); + else + Fraig_BitStringSetBit( p->pSimsTemp, p->iPatsPerm + t ); + } + // copy the pattern + for ( w = p->iWordPerm; w < p->iWordStart; w++ ) + pSims[w] = p->pSimsTemp[w]; + // recompute the hashing info + uHash = 0; + for ( w = 0; w < p->iWordStart; w++ ) + uHash ^= pSims[w] * s_FraigPrimes[w]; + p->vInputs->pArray[i]->uHashD = uHash; + } + + // update info about the permanently stored patterns + p->iWordPerm = p->iWordStart; + p->iPatsPerm += nPats; + assert( p->iWordPerm == FRAIG_NUM_WORDS( p->iPatsPerm ) ); + + // resimulate and recompute the hash values + for ( i = 1; i < p->vNodes->nSize; i++ ) + if ( Fraig_NodeIsAnd(p->vNodes->pArray[i]) ) + { + p->vNodes->pArray[i]->uHashD = 0; + Fraig_NodeSimulate( p->vNodes->pArray[i], 0, p->iWordPerm, 0 ); + } + + // double-check that the nodes are still distinguished + if ( fPerformChecks ) + Fraig_FeedBackCheckTable( p ); + + // rehash the values in the F0 table + if ( p->fDoSparse ) + { + Fraig_TableRehashF0( p, 0 ); + if ( fPerformChecks ) + Fraig_FeedBackCheckTableF0( p ); + } + + // check if we need to resize the simulation info + // if less than FRAIG_WORDS_STORE words are left, reallocate simulation info + if ( p->iWordPerm + FRAIG_WORDS_STORE > p->nWordsDyna ) + Fraig_ReallocateSimulationInfo( p ); + + // set the real patterns + Msat_IntVecClear( p->vPatsReal ); + memset( p->pSimsReal, 0, sizeof(unsigned)*p->nWordsDyna ); + for ( w = 0; w < p->iWordPerm; w++ ) + p->pSimsReal[w] = FRAIG_FULL; + if ( p->iPatsPerm % 32 > 0 ) + p->pSimsReal[p->iWordPerm-1] = FRAIG_MASK( p->iPatsPerm % 32 ); +// printf( "The number of permanent words = %d.\n", p->iWordPerm ); + return p->iWordStart; +} + + + + +/**Function************************************************************* + + Synopsis [Checks the correctness of the functional simulation table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_FeedBackCovering( Fraig_Man_t * p, Msat_IntVec_t * vPats ) +{ + Fraig_NodeVec_t * vColumns; + unsigned * pSims; + int * pHits, iPat, iCol, i; + int nOnesTotal, nSolStarting; + int fVeryVerbose = 0; + + // collect the pairs to be distinguished + vColumns = Fraig_FeedBackCoveringStart( p ); + // collect the number of 1s in each simulation vector + nOnesTotal = 0; + pHits = ALLOC( int, vColumns->nSize ); + for ( i = 0; i < vColumns->nSize; i++ ) + { + pSims = (unsigned *)vColumns->pArray[i]; + pHits[i] = Fraig_BitStringCountOnes( pSims, p->iWordStart ); + nOnesTotal += pHits[i]; +// assert( pHits[i] > 0 ); + } + + // go through the patterns + nSolStarting = Msat_IntVecReadSize(vPats); + while ( (iCol = Fraig_GetSmallestColumn( pHits, vColumns->nSize )) != -1 ) + { + // find the pattern, which hits this column + iPat = Fraig_GetHittingPattern( (unsigned *)vColumns->pArray[iCol], p->iWordStart ); + // cancel the columns covered by this pattern + Fraig_CancelCoveredColumns( vColumns, pHits, iPat ); + // save the pattern + Msat_IntVecPush( vPats, iPat ); + } + + // free the set of columns + for ( i = 0; i < vColumns->nSize; i++ ) + Fraig_MemFixedEntryRecycle( p->mmSims, (char *)vColumns->pArray[i] ); + + // print stats related to the covering problem + if ( p->fVerbose && fVeryVerbose ) + { + printf( "%3d\\%3d\\%3d ", p->nWordsRand, p->nWordsDyna, p->iWordPerm ); + printf( "Col (pairs) = %5d. ", vColumns->nSize ); + printf( "Row (pats) = %5d. ", p->iWordStart * 32 ); + printf( "Dns = %6.2f %%. ", vColumns->nSize==0? 0.0 : 100.0 * nOnesTotal / vColumns->nSize / p->iWordStart / 32 ); + printf( "Sol = %3d (%3d). ", Msat_IntVecReadSize(vPats), nSolStarting ); + printf( "\n" ); + } + Fraig_NodeVecFree( vColumns ); + free( pHits ); +} + + +/**Function************************************************************* + + Synopsis [Checks the correctness of the functional simulation table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_NodeVec_t * Fraig_FeedBackCoveringStart( Fraig_Man_t * p ) +{ + Fraig_NodeVec_t * vColumns; + Fraig_HashTable_t * pT = p->pTableF; + Fraig_Node_t * pEntF, * pEntD; + unsigned * pSims; + unsigned * pUnsigned1, * pUnsigned2; + int i, k, m, w;//, nOnes; + + // start the set of columns + vColumns = Fraig_NodeVecAlloc( 100 ); + + // go through the pairs of nodes to be distinguished + for ( i = 0; i < pT->nBins; i++ ) + Fraig_TableBinForEachEntryF( pT->pBins[i], pEntF ) + { + p->vCones->nSize = 0; + Fraig_TableBinForEachEntryD( pEntF, pEntD ) + Fraig_NodeVecPush( p->vCones, pEntD ); + if ( p->vCones->nSize == 1 ) + continue; + //////////////////////////////// bug fix by alanmi, September 14, 2006 + if ( p->vCones->nSize > 20 ) + continue; + //////////////////////////////// + + for ( k = 0; k < p->vCones->nSize; k++ ) + for ( m = k+1; m < p->vCones->nSize; m++ ) + { + if ( !Fraig_CompareSimInfoUnderMask( p->vCones->pArray[k], p->vCones->pArray[m], p->iWordStart, 0, p->pSimsReal ) ) + continue; + + // primary simulation patterns (counter-examples) cannot distinguish this pair + // get memory to store the feasible simulation patterns + pSims = (unsigned *)Fraig_MemFixedEntryFetch( p->mmSims ); + // find the pattern that distinguish this column, exept the primary ones + pUnsigned1 = p->vCones->pArray[k]->puSimD; + pUnsigned2 = p->vCones->pArray[m]->puSimD; + for ( w = 0; w < p->iWordStart; w++ ) + pSims[w] = (pUnsigned1[w] ^ pUnsigned2[w]) & ~p->pSimsReal[w]; + // store the pattern + Fraig_NodeVecPush( vColumns, (Fraig_Node_t *)pSims ); +// nOnes = Fraig_BitStringCountOnes(pSims, p->iWordStart); +// assert( nOnes > 0 ); + } + } + + // if the flag is not set, do not consider sparse nodes in p->pTableF0 + if ( !p->fDoSparse ) + return vColumns; + + // recalculate their hash values based on p->pSimsReal + pT = p->pTableF0; + for ( i = 0; i < pT->nBins; i++ ) + Fraig_TableBinForEachEntryF( pT->pBins[i], pEntF ) + { + pSims = pEntF->puSimD; + pEntF->uHashD = 0; + for ( w = 0; w < p->iWordStart; w++ ) + pEntF->uHashD ^= (pSims[w] & p->pSimsReal[w]) * s_FraigPrimes[w]; + } + + // rehash the table using these values + Fraig_TableRehashF0( p, 1 ); + + // collect the classes of equivalent node pairs + for ( i = 0; i < pT->nBins; i++ ) + Fraig_TableBinForEachEntryF( pT->pBins[i], pEntF ) + { + p->vCones->nSize = 0; + Fraig_TableBinForEachEntryD( pEntF, pEntD ) + Fraig_NodeVecPush( p->vCones, pEntD ); + if ( p->vCones->nSize == 1 ) + continue; + + // primary simulation patterns (counter-examples) cannot distinguish all these pairs + for ( k = 0; k < p->vCones->nSize; k++ ) + for ( m = k+1; m < p->vCones->nSize; m++ ) + { + // get memory to store the feasible simulation patterns + pSims = (unsigned *)Fraig_MemFixedEntryFetch( p->mmSims ); + // find the patterns that are not distinquished + pUnsigned1 = p->vCones->pArray[k]->puSimD; + pUnsigned2 = p->vCones->pArray[m]->puSimD; + for ( w = 0; w < p->iWordStart; w++ ) + pSims[w] = (pUnsigned1[w] ^ pUnsigned2[w]) & ~p->pSimsReal[w]; + // store the pattern + Fraig_NodeVecPush( vColumns, (Fraig_Node_t *)pSims ); +// nOnes = Fraig_BitStringCountOnes(pSims, p->iWordStart); +// assert( nOnes > 0 ); + } + } + return vColumns; +} + +/**Function************************************************************* + + Synopsis [Selects the column, which has the smallest number of hits.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_GetSmallestColumn( int * pHits, int nHits ) +{ + int i, iColMin = -1, nHitsMin = 1000000; + for ( i = 0; i < nHits; i++ ) + { + // skip covered columns + if ( pHits[i] == 0 ) + continue; + // take the column if it can only be covered by one pattern + if ( pHits[i] == 1 ) + return i; + // find the column, which requires the smallest number of patterns + if ( nHitsMin > pHits[i] ) + { + nHitsMin = pHits[i]; + iColMin = i; + } + } + return iColMin; +} + +/**Function************************************************************* + + Synopsis [Select the pattern, which hits this column.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_GetHittingPattern( unsigned * pSims, int nWords ) +{ + int i, b; + for ( i = 0; i < nWords; i++ ) + { + if ( pSims[i] == 0 ) + continue; + for ( b = 0; b < 32; b++ ) + if ( pSims[i] & (1 << b) ) + return i * 32 + b; + } + return -1; +} + +/**Function************************************************************* + + Synopsis [Cancel covered patterns.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_CancelCoveredColumns( Fraig_NodeVec_t * vColumns, int * pHits, int iPat ) +{ + unsigned * pSims; + int i; + for ( i = 0; i < vColumns->nSize; i++ ) + { + pSims = (unsigned *)vColumns->pArray[i]; + if ( Fraig_BitStringHasBit( pSims, iPat ) ) + pHits[i] = 0; + } +} + + +/**Function************************************************************* + + Synopsis [Checks the correctness of the functional simulation table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_FeedBackCheckTable( Fraig_Man_t * p ) +{ + Fraig_HashTable_t * pT = p->pTableF; + Fraig_Node_t * pEntF, * pEntD; + int i, k, m, nPairs; + int clk = clock(); + + nPairs = 0; + for ( i = 0; i < pT->nBins; i++ ) + Fraig_TableBinForEachEntryF( pT->pBins[i], pEntF ) + { + p->vCones->nSize = 0; + Fraig_TableBinForEachEntryD( pEntF, pEntD ) + Fraig_NodeVecPush( p->vCones, pEntD ); + if ( p->vCones->nSize == 1 ) + continue; + for ( k = 0; k < p->vCones->nSize; k++ ) + for ( m = k+1; m < p->vCones->nSize; m++ ) + { + if ( Fraig_CompareSimInfo( p->vCones->pArray[k], p->vCones->pArray[m], p->iWordStart, 0 ) ) + printf( "Nodes %d and %d have the same D simulation info.\n", + p->vCones->pArray[k]->Num, p->vCones->pArray[m]->Num ); + nPairs++; + } + } +// printf( "\nThe total of %d node pairs have been verified.\n", nPairs ); +} + +/**Function************************************************************* + + Synopsis [Checks the correctness of the functional simulation table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_FeedBackCheckTableF0( Fraig_Man_t * p ) +{ + Fraig_HashTable_t * pT = p->pTableF0; + Fraig_Node_t * pEntF; + int i, k, m, nPairs; + + nPairs = 0; + for ( i = 0; i < pT->nBins; i++ ) + { + p->vCones->nSize = 0; + Fraig_TableBinForEachEntryF( pT->pBins[i], pEntF ) + Fraig_NodeVecPush( p->vCones, pEntF ); + if ( p->vCones->nSize == 1 ) + continue; + for ( k = 0; k < p->vCones->nSize; k++ ) + for ( m = k+1; m < p->vCones->nSize; m++ ) + { + if ( Fraig_CompareSimInfo( p->vCones->pArray[k], p->vCones->pArray[m], p->iWordStart, 0 ) ) + printf( "Nodes %d and %d have the same D simulation info.\n", + p->vCones->pArray[k]->Num, p->vCones->pArray[m]->Num ); + nPairs++; + } + } +// printf( "\nThe total of %d node pairs have been verified.\n", nPairs ); +} + +/**Function************************************************************* + + Synopsis [Doubles the size of simulation info.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_ReallocateSimulationInfo( Fraig_Man_t * p ) +{ + Fraig_MemFixed_t * mmSimsNew; // new memory manager for simulation info + Fraig_Node_t * pNode; + unsigned * pSimsNew; + unsigned uSignOld; + int i; + + // allocate a new memory manager + p->nWordsDyna *= 2; + mmSimsNew = Fraig_MemFixedStart( sizeof(unsigned) * (p->nWordsRand + p->nWordsDyna) ); + + // set the new data for the constant node + pNode = p->pConst1; + pNode->puSimR = (unsigned *)Fraig_MemFixedEntryFetch( mmSimsNew ); + pNode->puSimD = pNode->puSimR + p->nWordsRand; + memset( pNode->puSimR, 0, sizeof(unsigned) * p->nWordsRand ); + memset( pNode->puSimD, 0, sizeof(unsigned) * p->nWordsDyna ); + + // copy the simulation info of the PIs + for ( i = 0; i < p->vInputs->nSize; i++ ) + { + pNode = p->vInputs->pArray[i]; + // copy the simulation info + pSimsNew = (unsigned *)Fraig_MemFixedEntryFetch( mmSimsNew ); + memmove( pSimsNew, pNode->puSimR, sizeof(unsigned) * (p->nWordsRand + p->iWordStart) ); + // attach the new info + pNode->puSimR = pSimsNew; + pNode->puSimD = pNode->puSimR + p->nWordsRand; + // signatures remain without changes + } + + // replace the manager to free up some memory + Fraig_MemFixedStop( p->mmSims, 0 ); + p->mmSims = mmSimsNew; + + // resimulate the internal nodes (this should lead to the same signatures) + for ( i = 1; i < p->vNodes->nSize; i++ ) + { + pNode = p->vNodes->pArray[i]; + if ( !Fraig_NodeIsAnd(pNode) ) + continue; + // allocate memory for the simulation info + pNode->puSimR = (unsigned *)Fraig_MemFixedEntryFetch( mmSimsNew ); + pNode->puSimD = pNode->puSimR + p->nWordsRand; + // derive random simulation info + uSignOld = pNode->uHashR; + pNode->uHashR = 0; + Fraig_NodeSimulate( pNode, 0, p->nWordsRand, 1 ); + assert( uSignOld == pNode->uHashR ); + // derive dynamic simulation info + uSignOld = pNode->uHashD; + pNode->uHashD = 0; + Fraig_NodeSimulate( pNode, 0, p->iWordStart, 0 ); + assert( uSignOld == pNode->uHashD ); + } + + // realloc temporary storage + p->pSimsReal = (unsigned *)Fraig_MemFixedEntryFetch( mmSimsNew ); + memset( p->pSimsReal, 0, sizeof(unsigned) * p->nWordsDyna ); + p->pSimsTemp = (unsigned *)Fraig_MemFixedEntryFetch( mmSimsNew ); + p->pSimsDiff = (unsigned *)Fraig_MemFixedEntryFetch( mmSimsNew ); +} + + +/**Function************************************************************* + + Synopsis [Generated trivial counter example.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int * Fraig_ManAllocCounterExample( Fraig_Man_t * p ) +{ + int * pModel; + pModel = ALLOC( int, p->vInputs->nSize ); + memset( pModel, 0, sizeof(int) * p->vInputs->nSize ); + return pModel; +} + + +/**Function************************************************************* + + Synopsis [Saves the counter example.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_ManSimulateBitNode_rec( Fraig_Man_t * p, Fraig_Node_t * pNode ) +{ + int Value0, Value1; + if ( Fraig_NodeIsTravIdCurrent( p, pNode ) ) + return pNode->fMark3; + Fraig_NodeSetTravIdCurrent( p, pNode ); + Value0 = Fraig_ManSimulateBitNode_rec( p, Fraig_Regular(pNode->p1) ); + Value1 = Fraig_ManSimulateBitNode_rec( p, Fraig_Regular(pNode->p2) ); + Value0 ^= Fraig_IsComplement(pNode->p1); + Value1 ^= Fraig_IsComplement(pNode->p2); + pNode->fMark3 = Value0 & Value1; + return pNode->fMark3; +} + +/**Function************************************************************* + + Synopsis [Simulates one bit.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_ManSimulateBitNode( Fraig_Man_t * p, Fraig_Node_t * pNode, int * pModel ) +{ + int fCompl, RetValue, i; + // set the PI values + Fraig_ManIncrementTravId( p ); + for ( i = 0; i < p->vInputs->nSize; i++ ) + { + Fraig_NodeSetTravIdCurrent( p, p->vInputs->pArray[i] ); + p->vInputs->pArray[i]->fMark3 = pModel[i]; + } + // perform the traversal + fCompl = Fraig_IsComplement(pNode); + RetValue = Fraig_ManSimulateBitNode_rec( p, Fraig_Regular(pNode) ); + return fCompl ^ RetValue; +} + + +/**Function************************************************************* + + Synopsis [Saves the counter example.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int * Fraig_ManSaveCounterExample( Fraig_Man_t * p, Fraig_Node_t * pNode ) +{ + int * pModel; + int iPattern; + int i, fCompl; + + // the node can be complemented + fCompl = Fraig_IsComplement(pNode); + // because we compare with constant 0, p->pConst1 should also be complemented + fCompl = !fCompl; + + // derive the model + pModel = Fraig_ManAllocCounterExample( p ); + iPattern = Fraig_FindFirstDiff( p->pConst1, Fraig_Regular(pNode), fCompl, p->nWordsRand, 1 ); + if ( iPattern >= 0 ) + { + for ( i = 0; i < p->vInputs->nSize; i++ ) + if ( Fraig_BitStringHasBit( p->vInputs->pArray[i]->puSimR, iPattern ) ) + pModel[i] = 1; +/* +printf( "SAT solver's pattern:\n" ); +for ( i = 0; i < p->vInputs->nSize; i++ ) + printf( "%d", pModel[i] ); +printf( "\n" ); +*/ + assert( Fraig_ManSimulateBitNode( p, pNode, pModel ) ); + return pModel; + } + iPattern = Fraig_FindFirstDiff( p->pConst1, Fraig_Regular(pNode), fCompl, p->iWordStart, 0 ); + if ( iPattern >= 0 ) + { + for ( i = 0; i < p->vInputs->nSize; i++ ) + if ( Fraig_BitStringHasBit( p->vInputs->pArray[i]->puSimD, iPattern ) ) + pModel[i] = 1; +/* +printf( "SAT solver's pattern:\n" ); +for ( i = 0; i < p->vInputs->nSize; i++ ) + printf( "%d", pModel[i] ); +printf( "\n" ); +*/ + assert( Fraig_ManSimulateBitNode( p, pNode, pModel ) ); + return pModel; + } + FREE( pModel ); + return NULL; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/fraig/fraigInt.h b/abc_with_bb_support/src/sat/fraig/fraigInt.h new file mode 100644 index 000000000..f7c1beabd --- /dev/null +++ b/abc_with_bb_support/src/sat/fraig/fraigInt.h @@ -0,0 +1,451 @@ +/**CFile**************************************************************** + + FileName [fraigInt.h] + + PackageName [FRAIG: Functionally reduced AND-INV graphs.] + + Synopsis [Internal declarations of the FRAIG package.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - October 1, 2004] + + Revision [$Id: fraigInt.h,v 1.15 2005/07/08 01:01:31 alanmi Exp $] + +***********************************************************************/ + +#ifndef __FRAIG_INT_H__ +#define __FRAIG_INT_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//#include "leaks.h" +#include +#include +#include +#include +#include + +#include "fraig.h" +#include "msat.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +/* + The AIG node policy: + - Each node has its main number (pNode->Num) + This is the number of this node in the array of all nodes and its SAT variable number + - The PI nodes are stored along with other nodes + Additionally, PI nodes have a PI number, by which they are stored in the PI node array + - The constant node is has number 0 and is also stored in the array +*/ + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// enable this macro to support the fanouts +#define FRAIG_ENABLE_FANOUTS +#define FRAIG_PATTERNS_RANDOM 2048 // should not be less than 128 and more than 32768 (2^15) +#define FRAIG_PATTERNS_DYNAMIC 2048 // should not be less than 256 and more than 32768 (2^15) +#define FRAIG_MAX_PRIMES 1024 // the maximum number of primes used for hashing + +// this parameter determines when simulation info is extended +// it will be extended when the free storage in the dynamic simulation +// info is less or equal to this number of words (FRAIG_WORDS_STORE) +// this is done because if the free storage for dynamic simulation info +// is not sufficient, computation becomes inefficient +#define FRAIG_WORDS_STORE 5 + +// the bit masks +#define FRAIG_MASK(n) ((~((unsigned)0)) >> (32-(n))) +#define FRAIG_FULL (~((unsigned)0)) +#define FRAIG_NUM_WORDS(n) (((n)>>5) + (((n)&31) > 0)) + +// maximum/minimum operators +#define FRAIG_MIN(a,b) (((a) < (b))? (a) : (b)) +#define FRAIG_MAX(a,b) (((a) > (b))? (a) : (b)) + +// generating random unsigned (#define RAND_MAX 0x7fff) +#define FRAIG_RANDOM_UNSIGNED ((((unsigned)rand()) << 24) ^ (((unsigned)rand()) << 12) ^ ((unsigned)rand())) + +// macros to get hold of the bits in a bit string +#define Fraig_BitStringSetBit(p,i) ((p)[(i)>>5] |= (1<<((i) & 31))) +#define Fraig_BitStringXorBit(p,i) ((p)[(i)>>5] ^= (1<<((i) & 31))) +#define Fraig_BitStringHasBit(p,i) (((p)[(i)>>5] & (1<<((i) & 31))) > 0) + +// macros to get hold of the bits in the support info +//#define Fraig_NodeSetVarStr(p,i) (Fraig_Regular(p)->pSuppStr[((i)%FRAIG_SUPP_SIGN)>>5] |= (1<<(((i)%FRAIG_SUPP_SIGN) & 31))) +//#define Fraig_NodeHasVarStr(p,i) ((Fraig_Regular(p)->pSuppStr[((i)%FRAIG_SUPP_SIGN)>>5] & (1<<(((i)%FRAIG_SUPP_SIGN) & 31))) > 0) +#define Fraig_NodeSetVarStr(p,i) Fraig_BitStringSetBit(Fraig_Regular(p)->pSuppStr,i) +#define Fraig_NodeHasVarStr(p,i) Fraig_BitStringHasBit(Fraig_Regular(p)->pSuppStr,i) + +// copied from "util.h" for standaloneness +#ifndef ALLOC +# define ALLOC(type, num) \ + ((type *) malloc(sizeof(type) * (num))) +#endif + +#ifndef REALLOC +# define REALLOC(type, obj, num) \ + (obj) ? ((type *) realloc((char *) obj, sizeof(type) * (num))) : \ + ((type *) malloc(sizeof(type) * (num))) +#endif + +#ifndef FREE +# define FREE(obj) \ + ((obj) ? (free((char *) (obj)), (obj) = 0) : 0) +#endif + +// copied from "extra.h" for stand-aloneness +#define Fraig_PrintTime(a,t) printf( "%s = ", (a) ); printf( "%6.2f sec\n", (float)(t)/(float)(CLOCKS_PER_SEC) ) + +#define Fraig_HashKey2(a,b,TSIZE) (((unsigned)(a) + (unsigned)(b) * 12582917) % TSIZE) +//#define Fraig_HashKey2(a,b,TSIZE) (( ((unsigned)(a)->Num * 19) ^ ((unsigned)(b)->Num * 1999) ) % TSIZE) +//#define Fraig_HashKey2(a,b,TSIZE) ( ((unsigned)((a)->Num + (b)->Num) * ((a)->Num + (b)->Num + 1) / 2) % TSIZE) +// the other two hash functions give bad distribution of hash chain lengths (not clear why) + +#ifndef PRT +#define PRT(a,t) printf( "%s = ", (a) ); printf( "%6.2f sec\n", (float)(t)/(float)(CLOCKS_PER_SEC) ) +#endif + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Fraig_MemFixed_t_ Fraig_MemFixed_t; + +// the mapping manager +struct Fraig_ManStruct_t_ +{ + // the AIG nodes + Fraig_NodeVec_t * vInputs; // the array of primary inputs + Fraig_NodeVec_t * vNodes; // the array of all nodes, including primary inputs + Fraig_NodeVec_t * vOutputs; // the array of primary outputs (some internal nodes) + Fraig_Node_t * pConst1; // the pointer to the constant node (vNodes->pArray[0]) + + // info about the original circuit + char ** ppInputNames; // the primary input names + char ** ppOutputNames; // the primary output names + + // various hash-tables + Fraig_HashTable_t * pTableS; // hashing by structure + Fraig_HashTable_t * pTableF; // hashing by simulation info + Fraig_HashTable_t * pTableF0; // hashing by simulation info (sparse functions) + + // parameters + int nWordsRand; // the number of words of random simulation info + int nWordsDyna; // the number of words of dynamic simulation info + int nBTLimit; // the max number of backtracks to perform + int nSeconds; // the runtime limit for the miter proof + int fFuncRed; // performs only one level hashing + int fFeedBack; // enables solver feedback + int fDist1Pats; // enables solver feedback + int fDoSparse; // performs equiv tests for sparse functions + int fChoicing; // enables recording structural choices + int fTryProve; // tries to solve the final miter + int fVerbose; // the verbosiness flag + int fVerboseP; // the verbosiness flag + sint64 nInspLimit; // the inspection limit + + int nTravIds; // the traversal counter + int nTravIds2; // the traversal counter + + // info related to the solver feedback + int iWordStart; // the first word to use for simulation + int iWordPerm; // the number of words stored permanently + int iPatsPerm; // the number of patterns stored permanently + Fraig_NodeVec_t * vCones; // the temporary array of internal variables + Msat_IntVec_t * vPatsReal; // the array of real pattern numbers + unsigned * pSimsReal; // used for simulation patterns + unsigned * pSimsDiff; // used for simulation patterns + unsigned * pSimsTemp; // used for simulation patterns + + // the support information + int nSuppWords; + unsigned ** pSuppS; + unsigned ** pSuppF; + + // the memory managers + Fraig_MemFixed_t * mmNodes; // the memory manager for nodes + Fraig_MemFixed_t * mmSims; // the memory manager for simulation info + + // solving the SAT problem + Msat_Solver_t * pSat; // the SAT solver + Msat_IntVec_t * vProj; // the temporary array of projection vars + int nSatNums; // the counter of SAT variables + int * pModel; // the assignment, which satisfies the miter + // these arrays belong to the solver + Msat_IntVec_t * vVarsInt; // the temporary array of variables + Msat_ClauseVec_t * vAdjacents; // the temporary storage for connectivity + Msat_IntVec_t * vVarsUsed; // the array marking vars appearing in the cone + + // various statistic variables + int nSatCalls; // the number of times equivalence checking was called + int nSatProof; // the number of times a proof was found + int nSatCounter; // the number of times a counter example was found + int nSatFails; // the number of times the SAT solver failed to complete due to resource limit or prediction + int nSatFailsReal; // the number of times the SAT solver failed to complete due to resource limit + + int nSatCallsImp; // the number of times equivalence checking was called + int nSatProofImp; // the number of times a proof was found + int nSatCounterImp;// the number of times a counter example was found + int nSatFailsImp; // the number of times the SAT solver failed to complete + + int nSatZeros; // the number of times the simulation vector is zero + int nSatSupps; // the number of times the support info was useful + int nRefErrors; // the number of ref counting errors + int nImplies; // the number of implication cases + int nSatImpls; // the number of implication SAT calls + int nVarsClauses; // the number of variables with clauses + int nSimplifies0; + int nSimplifies1; + int nImplies0; + int nImplies1; + + // runtime statistics + int timeToAig; // time to transfer to the mapping structure + int timeSims; // time to compute k-feasible cuts + int timeTrav; // time to traverse the network + int timeFeed; // time for solver feedback (recording and resimulating) + int timeImply; // time to analyze implications + int timeSat; // time to compute the truth table for each cut + int timeToNet; // time to transfer back to the network + int timeTotal; // the total mapping time + int time1; // time to perform one task + int time2; // time to perform another task + int time3; // time to perform another task + int time4; // time to perform another task +}; + +// the mapping node +struct Fraig_NodeStruct_t_ +{ + // various numbers associated with the node + int Num; // the unique number (SAT var number) of this node + int NumPi; // if the node is a PI, this is its variable number + int Level; // the level of the node + int nRefs; // the number of references of the node + int TravId; // the traversal ID (use to avoid cleaning marks) + int TravId2; // the traversal ID (use to avoid cleaning marks) + + // general information about the node + unsigned fInv : 1; // the mark to show that simulation info is complemented + unsigned fNodePo : 1; // the mark used for primary outputs + unsigned fClauses : 1; // the clauses for this node are loaded + unsigned fMark0 : 1; // the mark used for traversals + unsigned fMark1 : 1; // the mark used for traversals + unsigned fMark2 : 1; // the mark used for traversals + unsigned fMark3 : 1; // the mark used for traversals + unsigned fFeedUse : 1; // the presence of the variable in the feedback + unsigned fFeedVal : 1; // the value of the variable in the feedback + unsigned fFailTfo : 1; // the node is in the TFO of the failed SAT run + unsigned nFanouts : 2; // the indicator of fanouts (none, one, or many) + unsigned nOnes : 20; // the number of 1's in the random sim info + + // the children of the node + Fraig_Node_t * p1; // the first child + Fraig_Node_t * p2; // the second child + Fraig_NodeVec_t * vFanins; // the fanins of the supergate rooted at this node +// Fraig_NodeVec_t * vFanouts; // the fanouts of the supergate rooted at this node + + // various linked lists + Fraig_Node_t * pNextS; // the next node in the structural hash table + Fraig_Node_t * pNextF; // the next node in the functional (simulation) hash table + Fraig_Node_t * pNextD; // the next node in the list of nodes based on dynamic simulation + Fraig_Node_t * pNextE; // the next structural choice (functionally-equivalent node) + Fraig_Node_t * pRepr; // the canonical functional representative of the node + + // simulation data + unsigned uHashR; // the hash value for random information + unsigned uHashD; // the hash value for dynamic information + unsigned * puSimR; // the simulation information (random) + unsigned * puSimD; // the simulation information (dynamic) + + // misc information + Fraig_Node_t * pData0; // temporary storage for the corresponding network node + Fraig_Node_t * pData1; // temporary storage for the corresponding network node + +#ifdef FRAIG_ENABLE_FANOUTS + // representation of node's fanouts + Fraig_Node_t * pFanPivot; // the first fanout of this node + Fraig_Node_t * pFanFanin1; // the next fanout of p1 + Fraig_Node_t * pFanFanin2; // the next fanout of p2 +#endif +}; + +// the vector of nodes +struct Fraig_NodeVecStruct_t_ +{ + int nCap; // the number of allocated entries + int nSize; // the number of entries in the array + Fraig_Node_t ** pArray; // the array of nodes +}; + +// the hash table +struct Fraig_HashTableStruct_t_ +{ + Fraig_Node_t ** pBins; // the table bins + int nBins; // the size of the table + int nEntries; // the total number of entries in the table +}; + +// getting hold of the next fanout of the node +#define Fraig_NodeReadNextFanout( pNode, pFanout ) \ + ( ( pFanout == NULL )? NULL : \ + ((Fraig_Regular((pFanout)->p1) == (pNode))? \ + (pFanout)->pFanFanin1 : (pFanout)->pFanFanin2) ) +// getting hold of the place where the next fanout will be attached +#define Fraig_NodeReadNextFanoutPlace( pNode, pFanout ) \ + ( (Fraig_Regular((pFanout)->p1) == (pNode))? \ + &(pFanout)->pFanFanin1 : &(pFanout)->pFanFanin2 ) +// iterator through the fanouts of the node +#define Fraig_NodeForEachFanout( pNode, pFanout ) \ + for ( pFanout = (pNode)->pFanPivot; pFanout; \ + pFanout = Fraig_NodeReadNextFanout(pNode, pFanout) ) +// safe iterator through the fanouts of the node +#define Fraig_NodeForEachFanoutSafe( pNode, pFanout, pFanout2 ) \ + for ( pFanout = (pNode)->pFanPivot, \ + pFanout2 = Fraig_NodeReadNextFanout(pNode, pFanout); \ + pFanout; \ + pFanout = pFanout2, \ + pFanout2 = Fraig_NodeReadNextFanout(pNode, pFanout) ) + +// iterators through the entries in the linked lists of nodes +// the list of nodes in the structural hash table +#define Fraig_TableBinForEachEntryS( pBin, pEnt ) \ + for ( pEnt = pBin; \ + pEnt; \ + pEnt = pEnt->pNextS ) +#define Fraig_TableBinForEachEntrySafeS( pBin, pEnt, pEnt2 ) \ + for ( pEnt = pBin, \ + pEnt2 = pEnt? pEnt->pNextS: NULL; \ + pEnt; \ + pEnt = pEnt2, \ + pEnt2 = pEnt? pEnt->pNextS: NULL ) +// the list of nodes in the functional (simulation) hash table +#define Fraig_TableBinForEachEntryF( pBin, pEnt ) \ + for ( pEnt = pBin; \ + pEnt; \ + pEnt = pEnt->pNextF ) +#define Fraig_TableBinForEachEntrySafeF( pBin, pEnt, pEnt2 ) \ + for ( pEnt = pBin, \ + pEnt2 = pEnt? pEnt->pNextF: NULL; \ + pEnt; \ + pEnt = pEnt2, \ + pEnt2 = pEnt? pEnt->pNextF: NULL ) +// the list of nodes with the same simulation and different functionality +#define Fraig_TableBinForEachEntryD( pBin, pEnt ) \ + for ( pEnt = pBin; \ + pEnt; \ + pEnt = pEnt->pNextD ) +#define Fraig_TableBinForEachEntrySafeD( pBin, pEnt, pEnt2 ) \ + for ( pEnt = pBin, \ + pEnt2 = pEnt? pEnt->pNextD: NULL; \ + pEnt; \ + pEnt = pEnt2, \ + pEnt2 = pEnt? pEnt->pNextD: NULL ) +// the list of nodes with the same functionality +#define Fraig_TableBinForEachEntryE( pBin, pEnt ) \ + for ( pEnt = pBin; \ + pEnt; \ + pEnt = pEnt->pNextE ) +#define Fraig_TableBinForEachEntrySafeE( pBin, pEnt, pEnt2 ) \ + for ( pEnt = pBin, \ + pEnt2 = pEnt? pEnt->pNextE: NULL; \ + pEnt; \ + pEnt = pEnt2, \ + pEnt2 = pEnt? pEnt->pNextE: NULL ) + +//////////////////////////////////////////////////////////////////////// +/// GLOBAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== fraigCanon.c =============================================================*/ +extern Fraig_Node_t * Fraig_NodeAndCanon( Fraig_Man_t * pMan, Fraig_Node_t * p1, Fraig_Node_t * p2 ); +/*=== fraigFanout.c =============================================================*/ +extern void Fraig_NodeAddFaninFanout( Fraig_Node_t * pFanin, Fraig_Node_t * pFanout ); +extern void Fraig_NodeRemoveFaninFanout( Fraig_Node_t * pFanin, Fraig_Node_t * pFanoutToRemove ); +extern int Fraig_NodeGetFanoutNum( Fraig_Node_t * pNode ); +/*=== fraigFeed.c =============================================================*/ +extern void Fraig_FeedBackInit( Fraig_Man_t * p ); +extern void Fraig_FeedBack( Fraig_Man_t * p, int * pModel, Msat_IntVec_t * vVars, Fraig_Node_t * pOld, Fraig_Node_t * pNew ); +extern void Fraig_FeedBackTest( Fraig_Man_t * p ); +extern int Fraig_FeedBackCompress( Fraig_Man_t * p ); +extern int * Fraig_ManAllocCounterExample( Fraig_Man_t * p ); +extern int * Fraig_ManSaveCounterExample( Fraig_Man_t * p, Fraig_Node_t * pNode ); +/*=== fraigMan.c =============================================================*/ +extern void Fraig_ManCreateSolver( Fraig_Man_t * p ); +/*=== fraigMem.c =============================================================*/ +extern Fraig_MemFixed_t * Fraig_MemFixedStart( int nEntrySize ); +extern void Fraig_MemFixedStop( Fraig_MemFixed_t * p, int fVerbose ); +extern char * Fraig_MemFixedEntryFetch( Fraig_MemFixed_t * p ); +extern void Fraig_MemFixedEntryRecycle( Fraig_MemFixed_t * p, char * pEntry ); +extern void Fraig_MemFixedRestart( Fraig_MemFixed_t * p ); +extern int Fraig_MemFixedReadMemUsage( Fraig_MemFixed_t * p ); +/*=== fraigNode.c =============================================================*/ +extern Fraig_Node_t * Fraig_NodeCreateConst( Fraig_Man_t * p ); +extern Fraig_Node_t * Fraig_NodeCreatePi( Fraig_Man_t * p ); +extern Fraig_Node_t * Fraig_NodeCreate( Fraig_Man_t * p, Fraig_Node_t * p1, Fraig_Node_t * p2 ); +extern void Fraig_NodeSimulate( Fraig_Node_t * pNode, int iWordStart, int iWordStop, int fUseRand ); +/*=== fraigPrime.c =============================================================*/ +extern int s_FraigPrimes[FRAIG_MAX_PRIMES]; +extern unsigned int Cudd_PrimeFraig( unsigned int p ); +/*=== fraigSat.c ===============================================================*/ +extern int Fraig_NodeIsImplication( Fraig_Man_t * p, Fraig_Node_t * pOld, Fraig_Node_t * pNew, int nBTLimit ); +/*=== fraigTable.c =============================================================*/ +extern Fraig_HashTable_t * Fraig_HashTableCreate( int nSize ); +extern void Fraig_HashTableFree( Fraig_HashTable_t * p ); +extern int Fraig_HashTableLookupS( Fraig_Man_t * pMan, Fraig_Node_t * p1, Fraig_Node_t * p2, Fraig_Node_t ** ppNodeRes ); +extern Fraig_Node_t * Fraig_HashTableLookupF( Fraig_Man_t * pMan, Fraig_Node_t * pNode ); +extern Fraig_Node_t * Fraig_HashTableLookupF0( Fraig_Man_t * pMan, Fraig_Node_t * pNode ); +extern void Fraig_HashTableInsertF0( Fraig_Man_t * pMan, Fraig_Node_t * pNode ); +extern int Fraig_CompareSimInfo( Fraig_Node_t * pNode1, Fraig_Node_t * pNode2, int iWordLast, int fUseRand ); +extern int Fraig_CompareSimInfoUnderMask( Fraig_Node_t * pNode1, Fraig_Node_t * pNode2, int iWordLast, int fUseRand, unsigned * puMask ); +extern int Fraig_FindFirstDiff( Fraig_Node_t * pNode1, Fraig_Node_t * pNode2, int fCompl, int iWordLast, int fUseRand ); +extern void Fraig_CollectXors( Fraig_Node_t * pNode1, Fraig_Node_t * pNode2, int iWordLast, int fUseRand, unsigned * puMask ); +extern void Fraig_TablePrintStatsS( Fraig_Man_t * pMan ); +extern void Fraig_TablePrintStatsF( Fraig_Man_t * pMan ); +extern void Fraig_TablePrintStatsF0( Fraig_Man_t * pMan ); +extern int Fraig_TableRehashF0( Fraig_Man_t * pMan, int fLinkEquiv ); +/*=== fraigUtil.c ===============================================================*/ +extern int Fraig_NodeCountPis( Msat_IntVec_t * vVars, int nVarsPi ); +extern int Fraig_NodeCountSuppVars( Fraig_Man_t * p, Fraig_Node_t * pNode, int fSuppStr ); +extern int Fraig_NodesCompareSupps( Fraig_Man_t * p, Fraig_Node_t * pOld, Fraig_Node_t * pNew ); +extern int Fraig_NodeAndSimpleCase_rec( Fraig_Node_t * pOld, Fraig_Node_t * pNew ); +extern int Fraig_NodeIsExorType( Fraig_Node_t * pNode ); +extern void Fraig_ManSelectBestChoice( Fraig_Man_t * p ); +extern int Fraig_BitStringCountOnes( unsigned * pString, int nWords ); +extern void Fraig_PrintBinary( FILE * pFile, unsigned * pSign, int nBits ); +extern int Fraig_NodeIsExorType( Fraig_Node_t * pNode ); +extern int Fraig_NodeIsExor( Fraig_Node_t * pNode ); +extern int Fraig_NodeIsMuxType( Fraig_Node_t * pNode ); +extern Fraig_Node_t * Fraig_NodeRecognizeMux( Fraig_Node_t * pNode, Fraig_Node_t ** ppNodeT, Fraig_Node_t ** ppNodeE ); +extern int Fraig_ManCountExors( Fraig_Man_t * pMan ); +extern int Fraig_ManCountMuxes( Fraig_Man_t * pMan ); +extern int Fraig_NodeSimsContained( Fraig_Man_t * pMan, Fraig_Node_t * pNode1, Fraig_Node_t * pNode2 ); +extern int Fraig_NodeIsInSupergate( Fraig_Node_t * pOld, Fraig_Node_t * pNew ); +extern Fraig_NodeVec_t * Fraig_CollectSupergate( Fraig_Node_t * pNode, int fStopAtMux ); +extern int Fraig_CountPis( Fraig_Man_t * p, Msat_IntVec_t * vVarNums ); +extern void Fraig_ManIncrementTravId( Fraig_Man_t * pMan ); +extern void Fraig_NodeSetTravIdCurrent( Fraig_Man_t * pMan, Fraig_Node_t * pNode ); +extern int Fraig_NodeIsTravIdCurrent( Fraig_Man_t * pMan, Fraig_Node_t * pNode ); +extern int Fraig_NodeIsTravIdPrevious( Fraig_Man_t * pMan, Fraig_Node_t * pNode ); +/*=== fraigVec.c ===============================================================*/ +extern void Fraig_NodeVecSortByRefCount( Fraig_NodeVec_t * p ); + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/sat/fraig/fraigMan.c b/abc_with_bb_support/src/sat/fraig/fraigMan.c new file mode 100644 index 000000000..8878bc401 --- /dev/null +++ b/abc_with_bb_support/src/sat/fraig/fraigMan.c @@ -0,0 +1,540 @@ +/**CFile**************************************************************** + + FileName [fraigMan.c] + + PackageName [FRAIG: Functionally reduced AND-INV graphs.] + + Synopsis [Implementation of the FRAIG manager.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - October 1, 2004] + + Revision [$Id: fraigMan.c,v 1.11 2005/07/08 01:01:31 alanmi Exp $] + +***********************************************************************/ + +#include "fraigInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +int timeSelect; +int timeAssign; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Sets the default parameters of the package.] + + Description [This set of parameters is tuned for equivalence checking.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Prove_ParamsSetDefault( Prove_Params_t * pParams ) +{ + // clean the parameter structure + memset( pParams, 0, sizeof(Prove_Params_t) ); + // general parameters + pParams->fUseFraiging = 1; // enables fraiging + pParams->fUseRewriting = 1; // enables rewriting + pParams->fUseBdds = 0; // enables BDD construction when other methods fail + pParams->fVerbose = 0; // prints verbose stats + // iterations + pParams->nItersMax = 6; // the number of iterations + // mitering + pParams->nMiteringLimitStart = 300; // starting mitering limit + pParams->nMiteringLimitMulti = 2.0; // multiplicative coefficient to increase the limit in each iteration + // rewriting (currently not used) + pParams->nRewritingLimitStart = 3; // the number of rewriting iterations + pParams->nRewritingLimitMulti = 1.0; // multiplicative coefficient to increase the limit in each iteration + // fraiging + pParams->nFraigingLimitStart = 2; // starting backtrack(conflict) limit + pParams->nFraigingLimitMulti = 8.0; // multiplicative coefficient to increase the limit in each iteration + // last-gasp BDD construction + pParams->nBddSizeLimit = 1000000; // the number of BDD nodes when construction is aborted + pParams->fBddReorder = 1; // enables dynamic BDD variable reordering + // last-gasp mitering +// pParams->nMiteringLimitLast = 1000000; // final mitering limit + pParams->nMiteringLimitLast = 0; // final mitering limit + // global SAT solver limits + pParams->nTotalBacktrackLimit = 0; // global limit on the number of backtracks + pParams->nTotalInspectLimit = 0; // global limit on the number of clause inspects +// pParams->nTotalInspectLimit = 100000000; // global limit on the number of clause inspects +} + +/**Function************************************************************* + + Synopsis [Prints out the current values of CEC engine parameters.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Prove_ParamsPrint( Prove_Params_t * pParams ) +{ + printf( "CEC enging parameters:\n" ); + printf( "Fraiging enabled: %s\n", pParams->fUseFraiging? "yes":"no" ); + printf( "Rewriting enabled: %s\n", pParams->fUseRewriting? "yes":"no" ); + printf( "BDD construction enabled: %s\n", pParams->fUseBdds? "yes":"no" ); + printf( "Verbose output enabled: %s\n", pParams->fVerbose? "yes":"no" ); + printf( "Solver iterations: %d\n", pParams->nItersMax ); + printf( "Starting mitering limit: %d\n", pParams->nMiteringLimitStart ); + printf( "Multiplicative coeficient for mitering: %.2f\n", pParams->nMiteringLimitMulti ); + printf( "Starting number of rewriting iterations: %d\n", pParams->nRewritingLimitStart ); + printf( "Multiplicative coeficient for rewriting: %.2f\n", pParams->nRewritingLimitMulti ); + printf( "Starting number of conflicts in fraiging: %d\n", pParams->nFraigingLimitMulti ); + printf( "Multiplicative coeficient for fraiging: %.2f\n", pParams->nRewritingLimitMulti ); + printf( "BDD size limit for bailing out: %.2f\n", pParams->nBddSizeLimit ); + printf( "BDD reordering enabled: %s\n", pParams->fBddReorder? "yes":"no" ); + printf( "Last-gasp mitering limit: %d\n", pParams->nMiteringLimitLast ); + printf( "Total conflict limit: %d\n", pParams->nTotalBacktrackLimit ); + printf( "Total inspection limit: %d\n", pParams->nTotalInspectLimit ); + printf( "Parameter dump complete.\n" ); +} + +/**Function************************************************************* + + Synopsis [Sets the default parameters of the package.] + + Description [This set of parameters is tuned for equivalence checking.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_ParamsSetDefault( Fraig_Params_t * pParams ) +{ + memset( pParams, 0, sizeof(Fraig_Params_t) ); + pParams->nPatsRand = FRAIG_PATTERNS_RANDOM; // the number of words of random simulation info + pParams->nPatsDyna = FRAIG_PATTERNS_DYNAMIC; // the number of words of dynamic simulation info + pParams->nBTLimit = 99; // the max number of backtracks to perform + pParams->nSeconds = 20; // the max number of seconds to solve the miter + pParams->fFuncRed = 1; // performs only one level hashing + pParams->fFeedBack = 1; // enables solver feedback + pParams->fDist1Pats = 1; // enables distance-1 patterns + pParams->fDoSparse = 0; // performs equiv tests for sparse functions + pParams->fChoicing = 0; // enables recording structural choices + pParams->fTryProve = 1; // tries to solve the final miter + pParams->fVerbose = 0; // the verbosiness flag + pParams->fVerboseP = 0; // the verbose flag for reporting the proof + pParams->fInternal = 0; // the flag indicates the internal run + pParams->nConfLimit = 0; // the limit on the number of conflicts + pParams->nInspLimit = 0; // the limit on the number of inspections +} + +/**Function************************************************************* + + Synopsis [Sets the default parameters of the package.] + + Description [This set of parameters is tuned for complete FRAIGing.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_ParamsSetDefaultFull( Fraig_Params_t * pParams ) +{ + memset( pParams, 0, sizeof(Fraig_Params_t) ); + pParams->nPatsRand = FRAIG_PATTERNS_RANDOM; // the number of words of random simulation info + pParams->nPatsDyna = FRAIG_PATTERNS_DYNAMIC; // the number of words of dynamic simulation info + pParams->nBTLimit = -1; // the max number of backtracks to perform + pParams->nSeconds = 20; // the max number of seconds to solve the miter + pParams->fFuncRed = 1; // performs only one level hashing + pParams->fFeedBack = 1; // enables solver feedback + pParams->fDist1Pats = 1; // enables distance-1 patterns + pParams->fDoSparse = 1; // performs equiv tests for sparse functions + pParams->fChoicing = 0; // enables recording structural choices + pParams->fTryProve = 0; // tries to solve the final miter + pParams->fVerbose = 0; // the verbosiness flag + pParams->fVerboseP = 0; // the verbose flag for reporting the proof + pParams->fInternal = 0; // the flag indicates the internal run + pParams->nConfLimit = 0; // the limit on the number of conflicts + pParams->nInspLimit = 0; // the limit on the number of inspections +} + +/**Function************************************************************* + + Synopsis [Creates the new FRAIG manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_Man_t * Fraig_ManCreate( Fraig_Params_t * pParams ) +{ + Fraig_Params_t Params; + Fraig_Man_t * p; + + // set the random seed for simulation +// srand( 0xFEEDDEAF ); + srand( 0xDEADCAFE ); + + // set parameters for equivalence checking + if ( pParams == NULL ) + Fraig_ParamsSetDefault( pParams = &Params ); + // adjust the amount of simulation info + if ( pParams->nPatsRand < 128 ) + pParams->nPatsRand = 128; + if ( pParams->nPatsRand > 32768 ) + pParams->nPatsRand = 32768; + if ( pParams->nPatsDyna < 128 ) + pParams->nPatsDyna = 128; + if ( pParams->nPatsDyna > 32768 ) + pParams->nPatsDyna = 32768; + // if reduction is not performed, allocate minimum simulation info + if ( !pParams->fFuncRed ) + pParams->nPatsRand = pParams->nPatsDyna = 128; + + // start the manager + p = ALLOC( Fraig_Man_t, 1 ); + memset( p, 0, sizeof(Fraig_Man_t) ); + + // set the default parameters + p->nWordsRand = FRAIG_NUM_WORDS( pParams->nPatsRand ); // the number of words of random simulation info + p->nWordsDyna = FRAIG_NUM_WORDS( pParams->nPatsDyna ); // the number of patterns for dynamic simulation info + p->nBTLimit = pParams->nBTLimit; // -1 means infinite backtrack limit + p->nSeconds = pParams->nSeconds; // the timeout for the final miter + p->fFuncRed = pParams->fFuncRed; // enables functional reduction (otherwise, only one-level hashing is performed) + p->fFeedBack = pParams->fFeedBack; // enables solver feedback (the use of counter-examples in simulation) + p->fDist1Pats = pParams->fDist1Pats; // enables solver feedback (the use of counter-examples in simulation) + p->fDoSparse = pParams->fDoSparse; // performs equivalence checking for sparse functions (whose sim-info is 0) + p->fChoicing = pParams->fChoicing; // disable accumulation of structural choices (keeps only the first choice) + p->fTryProve = pParams->fTryProve; // disable accumulation of structural choices (keeps only the first choice) + p->fVerbose = pParams->fVerbose; // disable verbose output + p->fVerboseP = pParams->fVerboseP; // disable verbose output + p->nInspLimit = pParams->nInspLimit; // the limit on the number of inspections + + // start memory managers + p->mmNodes = Fraig_MemFixedStart( sizeof(Fraig_Node_t) ); + p->mmSims = Fraig_MemFixedStart( sizeof(unsigned) * (p->nWordsRand + p->nWordsDyna) ); + // allocate node arrays + p->vInputs = Fraig_NodeVecAlloc( 1000 ); // the array of primary inputs + p->vOutputs = Fraig_NodeVecAlloc( 1000 ); // the array of primary outputs + p->vNodes = Fraig_NodeVecAlloc( 1000 ); // the array of internal nodes + // start the tables + p->pTableS = Fraig_HashTableCreate( 1000 ); // hashing by structure + p->pTableF = Fraig_HashTableCreate( 1000 ); // hashing by function + p->pTableF0 = Fraig_HashTableCreate( 1000 ); // hashing by function (for sparse functions) + // create the constant node + p->pConst1 = Fraig_NodeCreateConst( p ); + // initialize SAT solver feedback data structures + Fraig_FeedBackInit( p ); + // initialize other variables + p->vProj = Msat_IntVecAlloc( 10 ); + p->nTravIds = 1; + p->nTravIds2 = 1; + return p; +} + +/**Function************************************************************* + + Synopsis [Deallocates the mapping manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_ManFree( Fraig_Man_t * p ) +{ + int i; + if ( p->fVerbose ) + { + if ( p->fChoicing ) Fraig_ManReportChoices( p ); + Fraig_ManPrintStats( p ); +// Fraig_TablePrintStatsS( p ); +// Fraig_TablePrintStatsF( p ); +// Fraig_TablePrintStatsF0( p ); + } + + for ( i = 0; i < p->vNodes->nSize; i++ ) + if ( p->vNodes->pArray[i]->vFanins ) + { + Fraig_NodeVecFree( p->vNodes->pArray[i]->vFanins ); + p->vNodes->pArray[i]->vFanins = NULL; + } + + if ( p->vInputs ) Fraig_NodeVecFree( p->vInputs ); + if ( p->vNodes ) Fraig_NodeVecFree( p->vNodes ); + if ( p->vOutputs ) Fraig_NodeVecFree( p->vOutputs ); + + if ( p->pTableS ) Fraig_HashTableFree( p->pTableS ); + if ( p->pTableF ) Fraig_HashTableFree( p->pTableF ); + if ( p->pTableF0 ) Fraig_HashTableFree( p->pTableF0 ); + + if ( p->pSat ) Msat_SolverFree( p->pSat ); + if ( p->vProj ) Msat_IntVecFree( p->vProj ); + if ( p->vCones ) Fraig_NodeVecFree( p->vCones ); + if ( p->vPatsReal ) Msat_IntVecFree( p->vPatsReal ); + if ( p->pModel ) free( p->pModel ); + + Fraig_MemFixedStop( p->mmNodes, 0 ); + Fraig_MemFixedStop( p->mmSims, 0 ); + + if ( p->pSuppS ) + { + FREE( p->pSuppS[0] ); + FREE( p->pSuppS ); + } + if ( p->pSuppF ) + { + FREE( p->pSuppF[0] ); + FREE( p->pSuppF ); + } + + FREE( p->ppOutputNames ); + FREE( p->ppInputNames ); + FREE( p ); +} + +/**Function************************************************************* + + Synopsis [Prepares the SAT solver to run on the two nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_ManCreateSolver( Fraig_Man_t * p ) +{ + extern int timeSelect; + extern int timeAssign; + assert( p->pSat == NULL ); + // allocate data for SAT solving + p->pSat = Msat_SolverAlloc( 500, 1, 1, 1, 1, 0 ); + p->vVarsInt = Msat_SolverReadConeVars( p->pSat ); + p->vAdjacents = Msat_SolverReadAdjacents( p->pSat ); + p->vVarsUsed = Msat_SolverReadVarsUsed( p->pSat ); + timeSelect = 0; + timeAssign = 0; +} + + +/**Function************************************************************* + + Synopsis [Deallocates the mapping manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_ManPrintStats( Fraig_Man_t * p ) +{ + double nMemory; + int clk = clock(); + nMemory = ((double)(p->vInputs->nSize + p->vNodes->nSize) * + (sizeof(Fraig_Node_t) + sizeof(unsigned)*(p->nWordsRand + p->nWordsDyna) /*+ p->nSuppWords*sizeof(unsigned)*/))/(1<<20); + printf( "Words: Random = %d. Dynamic = %d. Used = %d. Memory = %0.2f Mb.\n", + p->nWordsRand, p->nWordsDyna, p->iWordPerm, nMemory ); + printf( "Proof = %d. Counter-example = %d. Fail = %d. FailReal = %d. Zero = %d.\n", + p->nSatProof, p->nSatCounter, p->nSatFails, p->nSatFailsReal, p->nSatZeros ); + printf( "Nodes: Final = %d. Total = %d. Mux = %d. (Exor = %d.) ClaVars = %d.\n", + Fraig_CountNodes(p,0), p->vNodes->nSize, Fraig_ManCountMuxes(p), Fraig_ManCountExors(p), p->nVarsClauses ); + if ( p->pSat ) Msat_SolverPrintStats( p->pSat ); + Fraig_PrintTime( "AIG simulation ", p->timeSims ); + Fraig_PrintTime( "AIG traversal ", p->timeTrav ); + Fraig_PrintTime( "Solver feedback ", p->timeFeed ); + Fraig_PrintTime( "SAT solving ", p->timeSat ); + Fraig_PrintTime( "Network update ", p->timeToNet ); + Fraig_PrintTime( "TOTAL RUNTIME ", p->timeTotal ); + if ( p->time1 > 0 ) { Fraig_PrintTime( "time1", p->time1 ); } + if ( p->time2 > 0 ) { Fraig_PrintTime( "time2", p->time2 ); } + if ( p->time3 > 0 ) { Fraig_PrintTime( "time3", p->time3 ); } + if ( p->time4 > 0 ) { Fraig_PrintTime( "time4", p->time4 ); } +// PRT( "Selection ", timeSelect ); +// PRT( "Assignment", timeAssign ); +} + +/**Function************************************************************* + + Synopsis [Allocates simulation information for all nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_NodeVec_t * Fraig_UtilInfoAlloc( int nSize, int nWords, bool fClean ) +{ + Fraig_NodeVec_t * vInfo; + unsigned * pUnsigned; + int i; + assert( nSize > 0 && nWords > 0 ); + vInfo = Fraig_NodeVecAlloc( nSize ); + pUnsigned = ALLOC( unsigned, nSize * nWords ); + vInfo->pArray[0] = (Fraig_Node_t *)pUnsigned; + if ( fClean ) + memset( pUnsigned, 0, sizeof(unsigned) * nSize * nWords ); + for ( i = 1; i < nSize; i++ ) + vInfo->pArray[i] = (Fraig_Node_t *)(((unsigned *)vInfo->pArray[i-1]) + nWords); + vInfo->nSize = nSize; + return vInfo; +} + +/**Function************************************************************* + + Synopsis [Returns simulation info of all nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_NodeVec_t * Fraig_ManGetSimInfo( Fraig_Man_t * p ) +{ + Fraig_NodeVec_t * vInfo; + Fraig_Node_t * pNode; + unsigned * pUnsigned; + int nRandom, nDynamic; + int i, k, nWords; + + nRandom = Fraig_ManReadPatternNumRandom( p ); + nDynamic = Fraig_ManReadPatternNumDynamic( p ); + nWords = nRandom / 32 + nDynamic / 32; + + vInfo = Fraig_UtilInfoAlloc( p->vNodes->nSize, nWords, 0 ); + for ( i = 0; i < p->vNodes->nSize; i++ ) + { + pNode = p->vNodes->pArray[i]; + assert( i == pNode->Num ); + pUnsigned = (unsigned *)vInfo->pArray[i]; + for ( k = 0; k < nRandom / 32; k++ ) + pUnsigned[k] = pNode->puSimR[k]; + for ( k = 0; k < nDynamic / 32; k++ ) + pUnsigned[nRandom / 32 + k] = pNode->puSimD[k]; + } + return vInfo; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if A v B is always true based on the siminfo.] + + Description [A v B is always true iff A' * B' is always false.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_ManCheckClauseUsingSimInfo( Fraig_Man_t * p, Fraig_Node_t * pNode1, Fraig_Node_t * pNode2 ) +{ + int fCompl1, fCompl2, i; + + fCompl1 = 1 ^ Fraig_IsComplement(pNode1) ^ Fraig_Regular(pNode1)->fInv; + fCompl2 = 1 ^ Fraig_IsComplement(pNode2) ^ Fraig_Regular(pNode2)->fInv; + + pNode1 = Fraig_Regular(pNode1); + pNode2 = Fraig_Regular(pNode2); + assert( pNode1 != pNode2 ); + + // check the simulation info + if ( fCompl1 && fCompl2 ) + { + for ( i = 0; i < p->nWordsRand; i++ ) + if ( ~pNode1->puSimR[i] & ~pNode2->puSimR[i] ) + return 0; + for ( i = 0; i < p->iWordStart; i++ ) + if ( ~pNode1->puSimD[i] & ~pNode2->puSimD[i] ) + return 0; + return 1; + } + if ( !fCompl1 && fCompl2 ) + { + for ( i = 0; i < p->nWordsRand; i++ ) + if ( pNode1->puSimR[i] & ~pNode2->puSimR[i] ) + return 0; + for ( i = 0; i < p->iWordStart; i++ ) + if ( pNode1->puSimD[i] & ~pNode2->puSimD[i] ) + return 0; + return 1; + } + if ( fCompl1 && !fCompl2 ) + { + for ( i = 0; i < p->nWordsRand; i++ ) + if ( ~pNode1->puSimR[i] & pNode2->puSimR[i] ) + return 0; + for ( i = 0; i < p->iWordStart; i++ ) + if ( ~pNode1->puSimD[i] & pNode2->puSimD[i] ) + return 0; + return 1; + } +// if ( fCompl1 && fCompl2 ) + { + for ( i = 0; i < p->nWordsRand; i++ ) + if ( pNode1->puSimR[i] & pNode2->puSimR[i] ) + return 0; + for ( i = 0; i < p->iWordStart; i++ ) + if ( pNode1->puSimD[i] & pNode2->puSimD[i] ) + return 0; + return 1; + } +} + +/**Function************************************************************* + + Synopsis [Adds clauses to the solver.] + + Description [This procedure is used to add external clauses to the solver. + The clauses are given by sets of nodes. Each node stands for one literal. + If the node is complemented, the literal is negated.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_ManAddClause( Fraig_Man_t * p, Fraig_Node_t ** ppNodes, int nNodes ) +{ + Fraig_Node_t * pNode; + int i, fComp, RetValue; + if ( p->pSat == NULL ) + Fraig_ManCreateSolver( p ); + // create four clauses + Msat_IntVecClear( p->vProj ); + for ( i = 0; i < nNodes; i++ ) + { + pNode = Fraig_Regular(ppNodes[i]); + fComp = Fraig_IsComplement(ppNodes[i]); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNode->Num, fComp) ); +// printf( "%d(%d) ", pNode->Num, fComp ); + } +// printf( "\n" ); + RetValue = Msat_SolverAddClause( p->pSat, p->vProj ); + assert( RetValue ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/sat/fraig/fraigMem.c b/abc_with_bb_support/src/sat/fraig/fraigMem.c new file mode 100644 index 000000000..137b91f44 --- /dev/null +++ b/abc_with_bb_support/src/sat/fraig/fraigMem.c @@ -0,0 +1,246 @@ +/**CFile**************************************************************** + + FileName [fraigMem.c] + + PackageName [FRAIG: Functionally reduced AND-INV graphs.] + + Synopsis [Fixed-size-entry memory manager for the FRAIG package.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - October 1, 2004] + + Revision [$Id: fraigMem.c,v 1.4 2005/07/08 01:01:31 alanmi Exp $] + +***********************************************************************/ + +#include "fraigInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +struct Fraig_MemFixed_t_ +{ + // information about individual entries + int nEntrySize; // the size of one entry + int nEntriesAlloc; // the total number of entries allocated + int nEntriesUsed; // the number of entries in use + int nEntriesMax; // the max number of entries in use + char * pEntriesFree; // the linked list of free entries + + // this is where the memory is stored + int nChunkSize; // the size of one chunk + int nChunksAlloc; // the maximum number of memory chunks + int nChunks; // the current number of memory chunks + char ** pChunks; // the allocated memory + + // statistics + int nMemoryUsed; // memory used in the allocated entries + int nMemoryAlloc; // memory allocated +}; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Starts the internal memory manager.] + + Description [Can only work with entry size at least 4 byte long.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_MemFixed_t * Fraig_MemFixedStart( int nEntrySize ) +{ + Fraig_MemFixed_t * p; + + p = ALLOC( Fraig_MemFixed_t, 1 ); + memset( p, 0, sizeof(Fraig_MemFixed_t) ); + + p->nEntrySize = nEntrySize; + p->nEntriesAlloc = 0; + p->nEntriesUsed = 0; + p->pEntriesFree = NULL; + + if ( nEntrySize * (1 << 10) < (1<<16) ) + p->nChunkSize = (1 << 10); + else + p->nChunkSize = (1<<16) / nEntrySize; + if ( p->nChunkSize < 8 ) + p->nChunkSize = 8; + + p->nChunksAlloc = 64; + p->nChunks = 0; + p->pChunks = ALLOC( char *, p->nChunksAlloc ); + + p->nMemoryUsed = 0; + p->nMemoryAlloc = 0; + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the internal memory manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_MemFixedStop( Fraig_MemFixed_t * p, int fVerbose ) +{ + int i; + if ( p == NULL ) + return; + if ( fVerbose ) + { + printf( "Fixed memory manager: Entry = %5d. Chunk = %5d. Chunks used = %5d.\n", + p->nEntrySize, p->nChunkSize, p->nChunks ); + printf( " Entries used = %8d. Entries peak = %8d. Memory used = %8d. Memory alloc = %8d.\n", + p->nEntriesUsed, p->nEntriesMax, p->nEntrySize * p->nEntriesUsed, p->nMemoryAlloc ); + } + for ( i = 0; i < p->nChunks; i++ ) + free( p->pChunks[i] ); + free( p->pChunks ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Extracts one entry from the memory manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Fraig_MemFixedEntryFetch( Fraig_MemFixed_t * p ) +{ + char * pTemp; + int i; + + // check if there are still free entries + if ( p->nEntriesUsed == p->nEntriesAlloc ) + { // need to allocate more entries + assert( p->pEntriesFree == NULL ); + if ( p->nChunks == p->nChunksAlloc ) + { + p->nChunksAlloc *= 2; + p->pChunks = REALLOC( char *, p->pChunks, p->nChunksAlloc ); + } + p->pEntriesFree = ALLOC( char, p->nEntrySize * p->nChunkSize ); + p->nMemoryAlloc += p->nEntrySize * p->nChunkSize; + // transform these entries into a linked list + pTemp = p->pEntriesFree; + for ( i = 1; i < p->nChunkSize; i++ ) + { + *((char **)pTemp) = pTemp + p->nEntrySize; + pTemp += p->nEntrySize; + } + // set the last link + *((char **)pTemp) = NULL; + // add the chunk to the chunk storage + p->pChunks[ p->nChunks++ ] = p->pEntriesFree; + // add to the number of entries allocated + p->nEntriesAlloc += p->nChunkSize; + } + // incrememt the counter of used entries + p->nEntriesUsed++; + if ( p->nEntriesMax < p->nEntriesUsed ) + p->nEntriesMax = p->nEntriesUsed; + // return the first entry in the free entry list + pTemp = p->pEntriesFree; + p->pEntriesFree = *((char **)pTemp); + return pTemp; +} + +/**Function************************************************************* + + Synopsis [Returns one entry into the memory manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_MemFixedEntryRecycle( Fraig_MemFixed_t * p, char * pEntry ) +{ + // decrement the counter of used entries + p->nEntriesUsed--; + // add the entry to the linked list of free entries + *((char **)pEntry) = p->pEntriesFree; + p->pEntriesFree = pEntry; +} + +/**Function************************************************************* + + Synopsis [Frees all associated memory and resets the manager.] + + Description [Relocates all the memory except the first chunk.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_MemFixedRestart( Fraig_MemFixed_t * p ) +{ + int i; + char * pTemp; + + // deallocate all chunks except the first one + for ( i = 1; i < p->nChunks; i++ ) + free( p->pChunks[i] ); + p->nChunks = 1; + // transform these entries into a linked list + pTemp = p->pChunks[0]; + for ( i = 1; i < p->nChunkSize; i++ ) + { + *((char **)pTemp) = pTemp + p->nEntrySize; + pTemp += p->nEntrySize; + } + // set the last link + *((char **)pTemp) = NULL; + // set the free entry list + p->pEntriesFree = p->pChunks[0]; + // set the correct statistics + p->nMemoryAlloc = p->nEntrySize * p->nChunkSize; + p->nMemoryUsed = 0; + p->nEntriesAlloc = p->nChunkSize; + p->nEntriesUsed = 0; +} + +/**Function************************************************************* + + Synopsis [Reports the memory usage.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_MemFixedReadMemUsage( Fraig_MemFixed_t * p ) +{ + return p->nMemoryAlloc; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/fraig/fraigNode.c b/abc_with_bb_support/src/sat/fraig/fraigNode.c new file mode 100644 index 000000000..cfacc658a --- /dev/null +++ b/abc_with_bb_support/src/sat/fraig/fraigNode.c @@ -0,0 +1,313 @@ +/**CFile**************************************************************** + + FileName [fraigNode.c] + + PackageName [FRAIG: Functionally reduced AND-INV graphs.] + + Synopsis [Implementation of the FRAIG node.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - October 1, 2004] + + Revision [$Id: fraigNode.c,v 1.3 2005/07/08 01:01:32 alanmi Exp $] + +***********************************************************************/ + +#include "fraigInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// returns the complemented attribute of the node +#define Fraig_NodeIsSimComplement(p) (Fraig_IsComplement(p)? !(Fraig_Regular(p)->fInv) : (p)->fInv) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Creates the constant 1 node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_Node_t * Fraig_NodeCreateConst( Fraig_Man_t * p ) +{ + Fraig_Node_t * pNode; + + // create the node + pNode = (Fraig_Node_t *)Fraig_MemFixedEntryFetch( p->mmNodes ); + memset( pNode, 0, sizeof(Fraig_Node_t) ); + + // assign the number and add to the array of nodes + pNode->Num = p->vNodes->nSize; + Fraig_NodeVecPush( p->vNodes, pNode ); + pNode->NumPi = -1; // this is not a PI, so its number is -1 + pNode->Level = 0; // just like a PI, it has 0 level + pNode->nRefs = 1; // it is a persistent node, which comes referenced + pNode->fInv = 1; // the simulation info is complemented + + // create the simulation info + pNode->puSimR = (unsigned *)Fraig_MemFixedEntryFetch( p->mmSims ); + pNode->puSimD = pNode->puSimR + p->nWordsRand; + memset( pNode->puSimR, 0, sizeof(unsigned) * p->nWordsRand ); + memset( pNode->puSimD, 0, sizeof(unsigned) * p->nWordsDyna ); + + // count the number of ones in the simulation vector + pNode->nOnes = p->nWordsRand * sizeof(unsigned) * 8; + + // insert it into the hash table + Fraig_HashTableLookupF0( p, pNode ); + return pNode; +} + +/**Function************************************************************* + + Synopsis [Creates a primary input node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_Node_t * Fraig_NodeCreatePi( Fraig_Man_t * p ) +{ + Fraig_Node_t * pNode, * pNodeRes; + int i, clk; + + // create the node + pNode = (Fraig_Node_t *)Fraig_MemFixedEntryFetch( p->mmNodes ); + memset( pNode, 0, sizeof(Fraig_Node_t) ); + pNode->puSimR = (unsigned *)Fraig_MemFixedEntryFetch( p->mmSims ); + pNode->puSimD = pNode->puSimR + p->nWordsRand; + memset( pNode->puSimD, 0, sizeof(unsigned) * p->nWordsDyna ); + + // assign the number and add to the array of nodes + pNode->Num = p->vNodes->nSize; + Fraig_NodeVecPush( p->vNodes, pNode ); + + // assign the PI number and add to the array of primary inputs + pNode->NumPi = p->vInputs->nSize; + Fraig_NodeVecPush( p->vInputs, pNode ); + + pNode->Level = 0; // PI has 0 level + pNode->nRefs = 1; // it is a persistent node, which comes referenced + pNode->fInv = 0; // the simulation info of the PI is not complemented + + // derive the simulation info for the new node +clk = clock(); + // set the random simulation info for the primary input + pNode->uHashR = 0; + for ( i = 0; i < p->nWordsRand; i++ ) + { + // generate the simulation info + pNode->puSimR[i] = FRAIG_RANDOM_UNSIGNED; + // for reasons that take very long to explain, it makes sense to have (0000000...) + // pattern in the set (this helps if we need to return the counter-examples) + if ( i == 0 ) + pNode->puSimR[i] <<= 1; + // compute the hash key + pNode->uHashR ^= pNode->puSimR[i] * s_FraigPrimes[i]; + } + // count the number of ones in the simulation vector + pNode->nOnes = Fraig_BitStringCountOnes( pNode->puSimR, p->nWordsRand ); + + // set the systematic simulation info for the primary input + pNode->uHashD = 0; + for ( i = 0; i < p->iWordStart; i++ ) + { + // generate the simulation info + pNode->puSimD[i] = FRAIG_RANDOM_UNSIGNED; + // compute the hash key + pNode->uHashD ^= pNode->puSimD[i] * s_FraigPrimes[i]; + } +p->timeSims += clock() - clk; + + // insert it into the hash table + pNodeRes = Fraig_HashTableLookupF( p, pNode ); + assert( pNodeRes == NULL ); + // add to the runtime of simulation + return pNode; +} + +/**Function************************************************************* + + Synopsis [Creates a new node.] + + Description [This procedure should be called to create the constant + node and the PI nodes first.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_Node_t * Fraig_NodeCreate( Fraig_Man_t * p, Fraig_Node_t * p1, Fraig_Node_t * p2 ) +{ + Fraig_Node_t * pNode; + int clk; + + // create the node + pNode = (Fraig_Node_t *)Fraig_MemFixedEntryFetch( p->mmNodes ); + memset( pNode, 0, sizeof(Fraig_Node_t) ); + + // assign the children + pNode->p1 = p1; Fraig_Ref(p1); Fraig_Regular(p1)->nRefs++; + pNode->p2 = p2; Fraig_Ref(p2); Fraig_Regular(p2)->nRefs++; + + // assign the number and add to the array of nodes + pNode->Num = p->vNodes->nSize; + Fraig_NodeVecPush( p->vNodes, pNode ); + + // assign the PI number + pNode->NumPi = -1; + + // compute the level of this node + pNode->Level = 1 + FRAIG_MAX(Fraig_Regular(p1)->Level, Fraig_Regular(p2)->Level); + pNode->fInv = Fraig_NodeIsSimComplement(p1) & Fraig_NodeIsSimComplement(p2); + pNode->fFailTfo = Fraig_Regular(p1)->fFailTfo | Fraig_Regular(p2)->fFailTfo; + + // derive the simulation info +clk = clock(); + // allocate memory for the simulation info + pNode->puSimR = (unsigned *)Fraig_MemFixedEntryFetch( p->mmSims ); + pNode->puSimD = pNode->puSimR + p->nWordsRand; + // derive random simulation info + pNode->uHashR = 0; + Fraig_NodeSimulate( pNode, 0, p->nWordsRand, 1 ); + // derive dynamic simulation info + pNode->uHashD = 0; + Fraig_NodeSimulate( pNode, 0, p->iWordStart, 0 ); + // count the number of ones in the random simulation info + pNode->nOnes = Fraig_BitStringCountOnes( pNode->puSimR, p->nWordsRand ); + if ( pNode->fInv ) + pNode->nOnes = p->nWordsRand * 32 - pNode->nOnes; + // add to the runtime of simulation +p->timeSims += clock() - clk; + +#ifdef FRAIG_ENABLE_FANOUTS + // create the fanout info + Fraig_NodeAddFaninFanout( Fraig_Regular(p1), pNode ); + Fraig_NodeAddFaninFanout( Fraig_Regular(p2), pNode ); +#endif + return pNode; +} + + +/**Function************************************************************* + + Synopsis [Simulates the node.] + + Description [Simulates the random or dynamic simulation info through + the node. Uses phases of the children to determine their real simulation + info. Uses phase of the node to determine the way its simulation info + is stored. The resulting info is guaranteed to be 0 for the first pattern.] + + SideEffects [This procedure modified the hash value of the simulation info.] + + SeeAlso [] + +***********************************************************************/ +void Fraig_NodeSimulate( Fraig_Node_t * pNode, int iWordStart, int iWordStop, int fUseRand ) +{ + unsigned * pSims, * pSims1, * pSims2; + unsigned uHash; + int fCompl, fCompl1, fCompl2, i; + + assert( !Fraig_IsComplement(pNode) ); + + // get hold of the simulation information + pSims = fUseRand? pNode->puSimR : pNode->puSimD; + pSims1 = fUseRand? Fraig_Regular(pNode->p1)->puSimR : Fraig_Regular(pNode->p1)->puSimD; + pSims2 = fUseRand? Fraig_Regular(pNode->p2)->puSimR : Fraig_Regular(pNode->p2)->puSimD; + + // get complemented attributes of the children using their random info + fCompl = pNode->fInv; + fCompl1 = Fraig_NodeIsSimComplement(pNode->p1); + fCompl2 = Fraig_NodeIsSimComplement(pNode->p2); + + // simulate + uHash = 0; + if ( fCompl1 && fCompl2 ) + { + if ( fCompl ) + for ( i = iWordStart; i < iWordStop; i++ ) + { + pSims[i] = (pSims1[i] | pSims2[i]); + uHash ^= pSims[i] * s_FraigPrimes[i]; + } + else + for ( i = iWordStart; i < iWordStop; i++ ) + { + pSims[i] = ~(pSims1[i] | pSims2[i]); + uHash ^= pSims[i] * s_FraigPrimes[i]; + } + } + else if ( fCompl1 && !fCompl2 ) + { + if ( fCompl ) + for ( i = iWordStart; i < iWordStop; i++ ) + { + pSims[i] = (pSims1[i] | ~pSims2[i]); + uHash ^= pSims[i] * s_FraigPrimes[i]; + } + else + for ( i = iWordStart; i < iWordStop; i++ ) + { + pSims[i] = (~pSims1[i] & pSims2[i]); + uHash ^= pSims[i] * s_FraigPrimes[i]; + } + } + else if ( !fCompl1 && fCompl2 ) + { + if ( fCompl ) + for ( i = iWordStart; i < iWordStop; i++ ) + { + pSims[i] = (~pSims1[i] | pSims2[i]); + uHash ^= pSims[i] * s_FraigPrimes[i]; + } + else + for ( i = iWordStart; i < iWordStop; i++ ) + { + pSims[i] = (pSims1[i] & ~pSims2[i]); + uHash ^= pSims[i] * s_FraigPrimes[i]; + } + } + else // if ( !fCompl1 && !fCompl2 ) + { + if ( fCompl ) + for ( i = iWordStart; i < iWordStop; i++ ) + { + pSims[i] = ~(pSims1[i] & pSims2[i]); + uHash ^= pSims[i] * s_FraigPrimes[i]; + } + else + for ( i = iWordStart; i < iWordStop; i++ ) + { + pSims[i] = (pSims1[i] & pSims2[i]); + uHash ^= pSims[i] * s_FraigPrimes[i]; + } + } + + if ( fUseRand ) + pNode->uHashR ^= uHash; + else + pNode->uHashD ^= uHash; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/sat/fraig/fraigPrime.c b/abc_with_bb_support/src/sat/fraig/fraigPrime.c new file mode 100644 index 000000000..50cf65bc2 --- /dev/null +++ b/abc_with_bb_support/src/sat/fraig/fraigPrime.c @@ -0,0 +1,144 @@ +/**CFile**************************************************************** + + FileName [fraigPrime.c] + + PackageName [FRAIG: Functionally reduced AND-INV graphs.] + + Synopsis [The table of the first 1000 primes.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - October 1, 2004] + + Revision [$Id: fraigPrime.c,v 1.4 2005/07/08 01:01:32 alanmi Exp $] + +***********************************************************************/ + +#include "fraigInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// The 1,024 smallest prime numbers used to compute the hash value +// http://www.math.utah.edu/~alfeld/math/primelist.html +int s_FraigPrimes[FRAIG_MAX_PRIMES] = { 2, 3, 5, +7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, +101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, +193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, +293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, +409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, +521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, +641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, +757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, +881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, +1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, +1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, +1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, +1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, +1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, +1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, +1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, +1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, +1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, +1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, +2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 2131, 2137, +2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, +2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, +2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, +2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, +2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, +2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791, +2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, +2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011, 3019, 3023, +3037, 3041, 3049, 3061, 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, +3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271, +3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359, 3361, 3371, 3373, +3389, 3391, 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, +3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, +3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, +3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823, 3833, +3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923, 3929, 3931, +3943, 3947, 3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, +4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177, +4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, +4289, 4297, 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 4421, 4423, +4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519, 4523, 4547, +4549, 4561, 4567, 4583, 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657, +4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, 4759, 4783, 4787, 4789, +4793, 4799, 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, +4933, 4937, 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, 5009, 5011, +5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, 5099, 5101, 5107, 5113, 5119, 5147, +5153, 5167, 5171, 5179, 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, +5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 5407, 5413, +5417, 5419, 5431, 5437, 5441, 5443, 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, +5519, 5521, 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639, 5641, 5647, +5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741, 5743, +5749, 5779, 5783, 5791, 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857, +5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, 5953, 5981, 5987, 6007, +6011, 6029, 6037, 6043, 6047, 6053, 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, +6131, 6133, 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, 6229, 6247, +6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301, 6311, 6317, 6323, 6329, 6337, 6343, +6353, 6359, 6361, 6367, 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473, +6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571, 6577, 6581, 6599, 6607, +6619, 6637, 6653, 6659, 6661, 6673, 6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, +6737, 6761, 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, 6841, 6857, +6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917, 6947, 6949, 6959, 6961, 6967, 6971, +6977, 6983, 6991, 6997, 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103, +7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, 7211, 7213, 7219, 7229, +7237, 7243, 7247, 7253, 7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, +7393, 7411, 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, 7507, 7517, +7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561, 7573, 7577, 7583, 7589, 7591, 7603, +7607, 7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723, +7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829, 7841, 7853, 7867, 7873, +7877, 7879, 7883, 7901, 7907, 7919, 7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009, +8011, 8017, 8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111, 8117, 8123, +8147, 8161 }; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function******************************************************************** + + Synopsis [Returns the next prime >= p.] + + Description [Copied from CUDD, for stand-aloneness.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +unsigned int Cudd_PrimeFraig( unsigned int p) +{ + int i,pn; + + p--; + do { + p++; + if (p&1) { + pn = 1; + i = 3; + while ((unsigned) (i * i) <= p) { + if (p % i == 0) { + pn = 0; + break; + } + i += 2; + } + } else { + pn = 0; + } + } while (!pn); + return(p); + +} /* end of Cudd_Prime */ + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/fraig/fraigSat.c b/abc_with_bb_support/src/sat/fraig/fraigSat.c new file mode 100644 index 000000000..847a05dd7 --- /dev/null +++ b/abc_with_bb_support/src/sat/fraig/fraigSat.c @@ -0,0 +1,1455 @@ +/**CFile**************************************************************** + + FileName [fraigSat.c] + + PackageName [FRAIG: Functionally reduced AND-INV graphs.] + + Synopsis [Proving functional equivalence using SAT.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - October 1, 2004] + + Revision [$Id: fraigSat.c,v 1.10 2005/07/08 01:01:32 alanmi Exp $] + +***********************************************************************/ + +#include "fraigInt.h" +#include "math.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Fraig_OrderVariables( Fraig_Man_t * pMan, Fraig_Node_t * pOld, Fraig_Node_t * pNew ); +static void Fraig_SetupAdjacent( Fraig_Man_t * pMan, Msat_IntVec_t * vConeVars ); +static void Fraig_SetupAdjacentMark( Fraig_Man_t * pMan, Msat_IntVec_t * vConeVars ); +static void Fraig_PrepareCones( Fraig_Man_t * pMan, Fraig_Node_t * pOld, Fraig_Node_t * pNew ); +static void Fraig_PrepareCones_rec( Fraig_Man_t * pMan, Fraig_Node_t * pNode ); + +static void Fraig_SupergateAddClauses( Fraig_Man_t * pMan, Fraig_Node_t * pNode, Fraig_NodeVec_t * vSuper ); +static void Fraig_SupergateAddClausesExor( Fraig_Man_t * pMan, Fraig_Node_t * pNode ); +static void Fraig_SupergateAddClausesMux( Fraig_Man_t * pMan, Fraig_Node_t * pNode ); +//static void Fraig_DetectFanoutFreeCone( Fraig_Man_t * pMan, Fraig_Node_t * pNode ); +static void Fraig_DetectFanoutFreeConeMux( Fraig_Man_t * pMan, Fraig_Node_t * pNode ); +static void Fraig_SetActivity( Fraig_Man_t * pMan, Fraig_Node_t * pOld, Fraig_Node_t * pNew ); + +extern void * Msat_ClauseVecReadEntry( void * p, int i ); + +// The lesson learned seems to be that variable should be in reverse topological order +// from the output of the miter. The ordering of adjacency lists is very important. +// The best way seems to be fanins followed by fanouts. Slight changes to this order +// leads to big degradation in quality. + +static int nMuxes; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Checks equivalence of two nodes.] + + Description [Returns 1 iff the nodes are equivalent.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_NodesAreEqual( Fraig_Man_t * p, Fraig_Node_t * pNode1, Fraig_Node_t * pNode2, int nBTLimit, int nTimeLimit ) +{ + if ( pNode1 == pNode2 ) + return 1; + if ( pNode1 == Fraig_Not(pNode2) ) + return 0; + return Fraig_NodeIsEquivalent( p, Fraig_Regular(pNode1), Fraig_Regular(pNode2), nBTLimit, nTimeLimit ); +} + +/**Function************************************************************* + + Synopsis [Tries to prove the final miter.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_ManProveMiter( Fraig_Man_t * p ) +{ + Fraig_Node_t * pNode; + int i, clk; + + if ( !p->fTryProve ) + return; + + clk = clock(); + // consider all outputs of the multi-output miter + for ( i = 0; i < p->vOutputs->nSize; i++ ) + { + pNode = Fraig_Regular(p->vOutputs->pArray[i]); + // skip already constant nodes + if ( pNode == p->pConst1 ) + continue; + // skip nodes that are different according to simulation + if ( !Fraig_CompareSimInfo( pNode, p->pConst1, p->nWordsRand, 1 ) ) + continue; + if ( Fraig_NodeIsEquivalent( p, p->pConst1, pNode, -1, p->nSeconds ) ) + { + if ( Fraig_IsComplement(p->vOutputs->pArray[i]) ^ Fraig_NodeComparePhase(p->pConst1, pNode) ) + p->vOutputs->pArray[i] = Fraig_Not(p->pConst1); + else + p->vOutputs->pArray[i] = p->pConst1; + } + } + if ( p->fVerboseP ) + { +// PRT( "Final miter proof time", clock() - clk ); + } +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the miter is unsat; 0 if sat; -1 if undecided.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_ManCheckMiter( Fraig_Man_t * p ) +{ + Fraig_Node_t * pNode; + int i; + FREE( p->pModel ); + for ( i = 0; i < p->vOutputs->nSize; i++ ) + { + // get the output node (it can be complemented!) + pNode = p->vOutputs->pArray[i]; + // if the miter is constant 0, the problem is UNSAT + if ( pNode == Fraig_Not(p->pConst1) ) + continue; + // consider the special case when the miter is constant 1 + if ( pNode == p->pConst1 ) + { + // in this case, any counter example will do to distinquish it from constant 0 + // here we pick the counter example composed of all zeros + p->pModel = Fraig_ManAllocCounterExample( p ); + return 0; + } + // save the counter example + p->pModel = Fraig_ManSaveCounterExample( p, pNode ); + // if the model is not found, return undecided + if ( p->pModel == NULL ) + return -1; + else + return 0; + } + return 1; +} + + +/**Function************************************************************* + + Synopsis [Returns 1 if pOld is in the TFI of pNew.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_MarkTfi_rec( Fraig_Man_t * pMan, Fraig_Node_t * pNode ) +{ + // skip the visited node + if ( pNode->TravId == pMan->nTravIds ) + return 0; + pNode->TravId = pMan->nTravIds; + // skip the PI node + if ( pNode->NumPi >= 0 ) + return 1; + // check the children + return Fraig_MarkTfi_rec( pMan, Fraig_Regular(pNode->p1) ) + + Fraig_MarkTfi_rec( pMan, Fraig_Regular(pNode->p2) ); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if pOld is in the TFI of pNew.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_MarkTfi2_rec( Fraig_Man_t * pMan, Fraig_Node_t * pNode ) +{ + // skip the visited node + if ( pNode->TravId == pMan->nTravIds ) + return 0; + // skip the boundary node + if ( pNode->TravId == pMan->nTravIds-1 ) + { + pNode->TravId = pMan->nTravIds; + return 1; + } + pNode->TravId = pMan->nTravIds; + // skip the PI node + if ( pNode->NumPi >= 0 ) + return 1; + // check the children + return Fraig_MarkTfi2_rec( pMan, Fraig_Regular(pNode->p1) ) + + Fraig_MarkTfi2_rec( pMan, Fraig_Regular(pNode->p2) ); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if pOld is in the TFI of pNew.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_MarkTfi3_rec( Fraig_Man_t * pMan, Fraig_Node_t * pNode ) +{ + // skip the visited node + if ( pNode->TravId == pMan->nTravIds ) + return 1; + // skip the boundary node + if ( pNode->TravId == pMan->nTravIds-1 ) + { + pNode->TravId = pMan->nTravIds; + return 1; + } + pNode->TravId = pMan->nTravIds; + // skip the PI node + if ( pNode->NumPi >= 0 ) + return 0; + // check the children + return Fraig_MarkTfi3_rec( pMan, Fraig_Regular(pNode->p1) ) * + Fraig_MarkTfi3_rec( pMan, Fraig_Regular(pNode->p2) ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_VarsStudy( Fraig_Man_t * p, Fraig_Node_t * pOld, Fraig_Node_t * pNew ) +{ + int NumPis, NumCut, fContain; + + // mark the TFI of pNew + p->nTravIds++; + NumPis = Fraig_MarkTfi_rec( p, pNew ); + printf( "(%d)(%d,%d):", NumPis, pOld->Level, pNew->Level ); + + // check if the old is in the TFI + if ( pOld->TravId == p->nTravIds ) + { + printf( "* " ); + return; + } + + // count the boundary of nodes in pOld + p->nTravIds++; + NumCut = Fraig_MarkTfi2_rec( p, pOld ); + printf( "%d", NumCut ); + + // check if the new is contained in the old's support + p->nTravIds++; + fContain = Fraig_MarkTfi3_rec( p, pNew ); + printf( "%c ", fContain? '+':'-' ); +} + + +/**Function************************************************************* + + Synopsis [Checks whether two nodes are functinally equivalent.] + + Description [The flag (fComp) tells whether the nodes to be checked + are in the opposite polarity. The second flag (fSkipZeros) tells whether + the checking should be performed if the simulation vectors are zeros. + Returns 1 if the nodes are equivalent; 0 othewise.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_NodeIsEquivalent( Fraig_Man_t * p, Fraig_Node_t * pOld, Fraig_Node_t * pNew, int nBTLimit, int nTimeLimit ) +{ + int RetValue, RetValue1, i, fComp, clk; + int fVerbose = 0; + int fSwitch = 0; + + // make sure the nodes are not complemented + assert( !Fraig_IsComplement(pNew) ); + assert( !Fraig_IsComplement(pOld) ); + assert( pNew != pOld ); + + // if at least one of the nodes is a failed node, perform adjustments: + // if the backtrack limit is small, simply skip this node + // if the backtrack limit is > 10, take the quare root of the limit + if ( nBTLimit > 0 && (pOld->fFailTfo || pNew->fFailTfo) ) + { + p->nSatFails++; +// return 0; +// if ( nBTLimit > 10 ) +// nBTLimit /= 10; + if ( nBTLimit <= 10 ) + return 0; + nBTLimit = (int)sqrt(nBTLimit); +// fSwitch = 1; + } + + p->nSatCalls++; + + // make sure the solver is allocated and has enough variables + if ( p->pSat == NULL ) + Fraig_ManCreateSolver( p ); + // make sure the SAT solver has enough variables + for ( i = Msat_SolverReadVarNum(p->pSat); i < p->vNodes->nSize; i++ ) + Msat_SolverAddVar( p->pSat, p->vNodes->pArray[i]->Level ); + + + +/* + { + Fraig_Node_t * ppNodes[2] = { pOld, pNew }; + extern void Fraig_MappingShowNodes( Fraig_Man_t * pMan, Fraig_Node_t ** ppRoots, int nRoots, char * pFileName ); + Fraig_MappingShowNodes( p, ppNodes, 2, "temp_aig" ); + } +*/ + + nMuxes = 0; + + + // get the logic cone +clk = clock(); +// Fraig_VarsStudy( p, pOld, pNew ); + Fraig_OrderVariables( p, pOld, pNew ); +// Fraig_PrepareCones( p, pOld, pNew ); +p->timeTrav += clock() - clk; + +// printf( "The number of MUXes detected = %d (%5.2f %% of logic). ", nMuxes, 300.0*nMuxes/(p->vNodes->nSize - p->vInputs->nSize) ); +// PRT( "Time", clock() - clk ); + +if ( fVerbose ) + printf( "%d(%d) - ", Fraig_CountPis(p,p->vVarsInt), Msat_IntVecReadSize(p->vVarsInt) ); + + + // prepare variable activity + Fraig_SetActivity( p, pOld, pNew ); + + // get the complemented attribute + fComp = Fraig_NodeComparePhase( pOld, pNew ); +//Msat_SolverPrintClauses( p->pSat ); + + //////////////////////////////////////////// + // prepare the solver to run incrementally on these variables +//clk = clock(); + Msat_SolverPrepare( p->pSat, p->vVarsInt ); +//p->time3 += clock() - clk; + + + // solve under assumptions + // A = 1; B = 0 OR A = 1; B = 1 + Msat_IntVecClear( p->vProj ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pOld->Num, 0) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNew->Num, !fComp) ); + +//Msat_SolverWriteDimacs( p->pSat, "temp_fraig.cnf" ); + + // run the solver +clk = clock(); + RetValue1 = Msat_SolverSolve( p->pSat, p->vProj, nBTLimit, nTimeLimit ); +p->timeSat += clock() - clk; + + if ( RetValue1 == MSAT_FALSE ) + { +//p->time1 += clock() - clk; + +if ( fVerbose ) +{ + printf( "unsat %d ", Msat_SolverReadBackTracks(p->pSat) ); +PRT( "time", clock() - clk ); +} + + // add the clause + Msat_IntVecClear( p->vProj ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pOld->Num, 1) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNew->Num, fComp) ); + RetValue = Msat_SolverAddClause( p->pSat, p->vProj ); + assert( RetValue ); + // continue solving the other implication + } + else if ( RetValue1 == MSAT_TRUE ) + { +//p->time2 += clock() - clk; + +if ( fVerbose ) +{ + printf( "sat %d ", Msat_SolverReadBackTracks(p->pSat) ); +PRT( "time", clock() - clk ); +} + + // record the counter example + Fraig_FeedBack( p, Msat_SolverReadModelArray(p->pSat), p->vVarsInt, pOld, pNew ); + +// if ( pOld->fFailTfo || pNew->fFailTfo ) +// printf( "*" ); +// printf( "s(%d)", pNew->Level ); + if ( fSwitch ) + printf( "s(%d)", pNew->Level ); + p->nSatCounter++; + return 0; + } + else // if ( RetValue1 == MSAT_UNKNOWN ) + { +p->time3 += clock() - clk; + +// if ( pOld->fFailTfo || pNew->fFailTfo ) +// printf( "*" ); +// printf( "T(%d)", pNew->Level ); + + // mark the node as the failed node + if ( pOld != p->pConst1 ) + pOld->fFailTfo = 1; + pNew->fFailTfo = 1; +// p->nSatFails++; + if ( fSwitch ) + printf( "T(%d)", pNew->Level ); + p->nSatFailsReal++; + return 0; + } + + // if the old node was constant 0, we already know the answer + if ( pOld == p->pConst1 ) + return 1; + + //////////////////////////////////////////// + // prepare the solver to run incrementally +//clk = clock(); + Msat_SolverPrepare( p->pSat, p->vVarsInt ); +//p->time3 += clock() - clk; + // solve under assumptions + // A = 0; B = 1 OR A = 0; B = 0 + Msat_IntVecClear( p->vProj ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pOld->Num, 1) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNew->Num, fComp) ); + // run the solver +clk = clock(); + RetValue1 = Msat_SolverSolve( p->pSat, p->vProj, nBTLimit, nTimeLimit ); +p->timeSat += clock() - clk; + + if ( RetValue1 == MSAT_FALSE ) + { +//p->time1 += clock() - clk; + +if ( fVerbose ) +{ + printf( "unsat %d ", Msat_SolverReadBackTracks(p->pSat) ); +PRT( "time", clock() - clk ); +} + + // add the clause + Msat_IntVecClear( p->vProj ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pOld->Num, 0) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNew->Num, !fComp) ); + RetValue = Msat_SolverAddClause( p->pSat, p->vProj ); + assert( RetValue ); + // continue solving the other implication + } + else if ( RetValue1 == MSAT_TRUE ) + { +//p->time2 += clock() - clk; + +if ( fVerbose ) +{ + printf( "sat %d ", Msat_SolverReadBackTracks(p->pSat) ); +PRT( "time", clock() - clk ); +} + + // record the counter example + Fraig_FeedBack( p, Msat_SolverReadModelArray(p->pSat), p->vVarsInt, pOld, pNew ); + p->nSatCounter++; + +// if ( pOld->fFailTfo || pNew->fFailTfo ) +// printf( "*" ); +// printf( "s(%d)", pNew->Level ); + if ( fSwitch ) + printf( "s(%d)", pNew->Level ); + return 0; + } + else // if ( RetValue1 == MSAT_UNKNOWN ) + { +p->time3 += clock() - clk; + +// if ( pOld->fFailTfo || pNew->fFailTfo ) +// printf( "*" ); +// printf( "T(%d)", pNew->Level ); + if ( fSwitch ) + printf( "T(%d)", pNew->Level ); + + // mark the node as the failed node + pOld->fFailTfo = 1; + pNew->fFailTfo = 1; +// p->nSatFails++; + p->nSatFailsReal++; + return 0; + } + + // return SAT proof + p->nSatProof++; + +// if ( pOld->fFailTfo || pNew->fFailTfo ) +// printf( "*" ); +// printf( "u(%d)", pNew->Level ); + + if ( fSwitch ) + printf( "u(%d)", pNew->Level ); + + return 1; +} + + +/**Function************************************************************* + + Synopsis [Checks whether pOld => pNew.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_NodeIsImplication( Fraig_Man_t * p, Fraig_Node_t * pOld, Fraig_Node_t * pNew, int nBTLimit ) +{ + int RetValue, RetValue1, i, fComp, clk; + int fVerbose = 0; + + // make sure the nodes are not complemented + assert( !Fraig_IsComplement(pNew) ); + assert( !Fraig_IsComplement(pOld) ); + assert( pNew != pOld ); + + p->nSatCallsImp++; + + // make sure the solver is allocated and has enough variables + if ( p->pSat == NULL ) + Fraig_ManCreateSolver( p ); + // make sure the SAT solver has enough variables + for ( i = Msat_SolverReadVarNum(p->pSat); i < p->vNodes->nSize; i++ ) + Msat_SolverAddVar( p->pSat, p->vNodes->pArray[i]->Level ); + + // get the logic cone +clk = clock(); + Fraig_OrderVariables( p, pOld, pNew ); +// Fraig_PrepareCones( p, pOld, pNew ); +p->timeTrav += clock() - clk; + +if ( fVerbose ) + printf( "%d(%d) - ", Fraig_CountPis(p,p->vVarsInt), Msat_IntVecReadSize(p->vVarsInt) ); + + + // get the complemented attribute + fComp = Fraig_NodeComparePhase( pOld, pNew ); +//Msat_SolverPrintClauses( p->pSat ); + + //////////////////////////////////////////// + // prepare the solver to run incrementally on these variables +//clk = clock(); + Msat_SolverPrepare( p->pSat, p->vVarsInt ); +//p->time3 += clock() - clk; + + // solve under assumptions + // A = 1; B = 0 OR A = 1; B = 1 + Msat_IntVecClear( p->vProj ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pOld->Num, 0) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNew->Num, !fComp) ); + // run the solver +clk = clock(); + RetValue1 = Msat_SolverSolve( p->pSat, p->vProj, nBTLimit, 1000000 ); +p->timeSat += clock() - clk; + + if ( RetValue1 == MSAT_FALSE ) + { +//p->time1 += clock() - clk; + +if ( fVerbose ) +{ + printf( "unsat %d ", Msat_SolverReadBackTracks(p->pSat) ); +PRT( "time", clock() - clk ); +} + + // add the clause + Msat_IntVecClear( p->vProj ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pOld->Num, 1) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNew->Num, fComp) ); + RetValue = Msat_SolverAddClause( p->pSat, p->vProj ); + assert( RetValue ); +// p->nSatProofImp++; + return 1; + } + else if ( RetValue1 == MSAT_TRUE ) + { +//p->time2 += clock() - clk; + +if ( fVerbose ) +{ + printf( "sat %d ", Msat_SolverReadBackTracks(p->pSat) ); +PRT( "time", clock() - clk ); +} + // record the counter example + Fraig_FeedBack( p, Msat_SolverReadModelArray(p->pSat), p->vVarsInt, pOld, pNew ); + p->nSatCounterImp++; + return 0; + } + else // if ( RetValue1 == MSAT_UNKNOWN ) + { +p->time3 += clock() - clk; + p->nSatFailsImp++; + return 0; + } +} + +/**Function************************************************************* + + Synopsis [Prepares the SAT solver to run on the two nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_ManCheckClauseUsingSat( Fraig_Man_t * p, Fraig_Node_t * pNode1, Fraig_Node_t * pNode2, int nBTLimit ) +{ + Fraig_Node_t * pNode1R, * pNode2R; + int RetValue, RetValue1, i, clk; + int fVerbose = 0; + + pNode1R = Fraig_Regular(pNode1); + pNode2R = Fraig_Regular(pNode2); + assert( pNode1R != pNode2R ); + + // make sure the solver is allocated and has enough variables + if ( p->pSat == NULL ) + Fraig_ManCreateSolver( p ); + // make sure the SAT solver has enough variables + for ( i = Msat_SolverReadVarNum(p->pSat); i < p->vNodes->nSize; i++ ) + Msat_SolverAddVar( p->pSat, p->vNodes->pArray[i]->Level ); + + // get the logic cone +clk = clock(); + Fraig_OrderVariables( p, pNode1R, pNode2R ); +// Fraig_PrepareCones( p, pNode1R, pNode2R ); +p->timeTrav += clock() - clk; + + //////////////////////////////////////////// + // prepare the solver to run incrementally on these variables +//clk = clock(); + Msat_SolverPrepare( p->pSat, p->vVarsInt ); +//p->time3 += clock() - clk; + + // solve under assumptions + // A = 1; B = 0 OR A = 1; B = 1 + Msat_IntVecClear( p->vProj ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNode1R->Num, !Fraig_IsComplement(pNode1)) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNode2R->Num, !Fraig_IsComplement(pNode2)) ); + // run the solver +clk = clock(); + RetValue1 = Msat_SolverSolve( p->pSat, p->vProj, nBTLimit, 1000000 ); +p->timeSat += clock() - clk; + + if ( RetValue1 == MSAT_FALSE ) + { +//p->time1 += clock() - clk; + +if ( fVerbose ) +{ + printf( "unsat %d ", Msat_SolverReadBackTracks(p->pSat) ); +PRT( "time", clock() - clk ); +} + + // add the clause + Msat_IntVecClear( p->vProj ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNode1R->Num, Fraig_IsComplement(pNode1)) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNode2R->Num, Fraig_IsComplement(pNode2)) ); + RetValue = Msat_SolverAddClause( p->pSat, p->vProj ); + assert( RetValue ); +// p->nSatProofImp++; + return 1; + } + else if ( RetValue1 == MSAT_TRUE ) + { +//p->time2 += clock() - clk; + +if ( fVerbose ) +{ + printf( "sat %d ", Msat_SolverReadBackTracks(p->pSat) ); +PRT( "time", clock() - clk ); +} + // record the counter example +// Fraig_FeedBack( p, Msat_SolverReadModelArray(p->pSat), p->vVarsInt, pNode1R, pNode2R ); + p->nSatCounterImp++; + return 0; + } + else // if ( RetValue1 == MSAT_UNKNOWN ) + { +p->time3 += clock() - clk; + p->nSatFailsImp++; + return 0; + } +} + + +/**Function************************************************************* + + Synopsis [Prepares the SAT solver to run on the two nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_PrepareCones( Fraig_Man_t * pMan, Fraig_Node_t * pOld, Fraig_Node_t * pNew ) +{ +// Msat_IntVec_t * vAdjs; +// int * pVars, nVars, i, k; + int nVarsAlloc; + + assert( pOld != pNew ); + assert( !Fraig_IsComplement(pOld) ); + assert( !Fraig_IsComplement(pNew) ); + // clean the variables + nVarsAlloc = Msat_IntVecReadSize(pMan->vVarsUsed); + Msat_IntVecFill( pMan->vVarsUsed, nVarsAlloc, 0 ); + Msat_IntVecClear( pMan->vVarsInt ); + + pMan->nTravIds++; + Fraig_PrepareCones_rec( pMan, pNew ); + Fraig_PrepareCones_rec( pMan, pOld ); + + +/* + nVars = Msat_IntVecReadSize( pMan->vVarsInt ); + pVars = Msat_IntVecReadArray( pMan->vVarsInt ); + for ( i = 0; i < nVars; i++ ) + { + // process its connections + vAdjs = (Msat_IntVec_t *)Msat_ClauseVecReadEntry( pMan->vAdjacents, pVars[i] ); + printf( "%d=%d { ", pVars[i], Msat_IntVecReadSize(vAdjs) ); + for ( k = 0; k < Msat_IntVecReadSize(vAdjs); k++ ) + printf( "%d ", Msat_IntVecReadEntry(vAdjs,k) ); + printf( "}\n" ); + + } + i = 0; +*/ +} + +/**Function************************************************************* + + Synopsis [Traverses the cone, collects the numbers and adds the clauses.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_PrepareCones_rec( Fraig_Man_t * pMan, Fraig_Node_t * pNode ) +{ + Fraig_Node_t * pFanin; + Msat_IntVec_t * vAdjs; + int fUseMuxes = 1, i; + int fItIsTime; + + // skip if the node is aleady visited + assert( !Fraig_IsComplement(pNode) ); + if ( pNode->TravId == pMan->nTravIds ) + return; + pNode->TravId = pMan->nTravIds; + + // collect the node's number (closer to reverse topological order) + Msat_IntVecPush( pMan->vVarsInt, pNode->Num ); + Msat_IntVecWriteEntry( pMan->vVarsUsed, pNode->Num, 1 ); + if ( !Fraig_NodeIsAnd( pNode ) ) + return; + + // if the node does not have fanins, create them + fItIsTime = 0; + if ( pNode->vFanins == NULL ) + { + fItIsTime = 1; + // create the fanins of the supergate + assert( pNode->fClauses == 0 ); + if ( fUseMuxes && Fraig_NodeIsMuxType(pNode) ) + { + pNode->vFanins = Fraig_NodeVecAlloc( 4 ); + Fraig_NodeVecPushUnique( pNode->vFanins, Fraig_Regular(Fraig_Regular(pNode->p1)->p1) ); + Fraig_NodeVecPushUnique( pNode->vFanins, Fraig_Regular(Fraig_Regular(pNode->p1)->p2) ); + Fraig_NodeVecPushUnique( pNode->vFanins, Fraig_Regular(Fraig_Regular(pNode->p2)->p1) ); + Fraig_NodeVecPushUnique( pNode->vFanins, Fraig_Regular(Fraig_Regular(pNode->p2)->p2) ); + Fraig_SupergateAddClausesMux( pMan, pNode ); + } + else + { + pNode->vFanins = Fraig_CollectSupergate( pNode, fUseMuxes ); + Fraig_SupergateAddClauses( pMan, pNode, pNode->vFanins ); + } + assert( pNode->vFanins->nSize > 1 ); + pNode->fClauses = 1; + pMan->nVarsClauses++; + + // add fanins + vAdjs = (Msat_IntVec_t *)Msat_ClauseVecReadEntry( pMan->vAdjacents, pNode->Num ); + assert( Msat_IntVecReadSize( vAdjs ) == 0 ); + for ( i = 0; i < pNode->vFanins->nSize; i++ ) + { + pFanin = Fraig_Regular(pNode->vFanins->pArray[i]); + Msat_IntVecPush( vAdjs, pFanin->Num ); + } + } + + // recursively visit the fanins + for ( i = 0; i < pNode->vFanins->nSize; i++ ) + Fraig_PrepareCones_rec( pMan, Fraig_Regular(pNode->vFanins->pArray[i]) ); + + if ( fItIsTime ) + { + // recursively visit the fanins + for ( i = 0; i < pNode->vFanins->nSize; i++ ) + { + pFanin = Fraig_Regular(pNode->vFanins->pArray[i]); + vAdjs = (Msat_IntVec_t *)Msat_ClauseVecReadEntry( pMan->vAdjacents, pFanin->Num ); + Msat_IntVecPush( vAdjs, pNode->Num ); + } + } +} + +/**Function************************************************************* + + Synopsis [Collect variables using their proximity from the nodes.] + + Description [This procedure creates a variable order based on collecting + first the nodes that are the closest to the given two target nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_OrderVariables( Fraig_Man_t * pMan, Fraig_Node_t * pOld, Fraig_Node_t * pNew ) +{ + Fraig_Node_t * pNode, * pFanin; + int i, k, Number, fUseMuxes = 1; + int nVarsAlloc; + + assert( pOld != pNew ); + assert( !Fraig_IsComplement(pOld) ); + assert( !Fraig_IsComplement(pNew) ); + + pMan->nTravIds++; + + // clean the variables + nVarsAlloc = Msat_IntVecReadSize(pMan->vVarsUsed); + Msat_IntVecFill( pMan->vVarsUsed, nVarsAlloc, 0 ); + Msat_IntVecClear( pMan->vVarsInt ); + + // add the first node + Msat_IntVecPush( pMan->vVarsInt, pOld->Num ); + Msat_IntVecWriteEntry( pMan->vVarsUsed, pOld->Num, 1 ); + pOld->TravId = pMan->nTravIds; + + // add the second node + Msat_IntVecPush( pMan->vVarsInt, pNew->Num ); + Msat_IntVecWriteEntry( pMan->vVarsUsed, pNew->Num, 1 ); + pNew->TravId = pMan->nTravIds; + + // create the variable order + for ( i = 0; i < Msat_IntVecReadSize(pMan->vVarsInt); i++ ) + { + // get the new node on the frontier + Number = Msat_IntVecReadEntry(pMan->vVarsInt, i); + pNode = pMan->vNodes->pArray[Number]; + if ( !Fraig_NodeIsAnd(pNode) ) + continue; + + // if the node does not have fanins, create them + if ( pNode->vFanins == NULL ) + { + // create the fanins of the supergate + assert( pNode->fClauses == 0 ); + // detecting a fanout-free cone (experiment only) +// Fraig_DetectFanoutFreeCone( pMan, pNode ); + + if ( fUseMuxes && Fraig_NodeIsMuxType(pNode) ) + { + pNode->vFanins = Fraig_NodeVecAlloc( 4 ); + Fraig_NodeVecPushUnique( pNode->vFanins, Fraig_Regular(Fraig_Regular(pNode->p1)->p1) ); + Fraig_NodeVecPushUnique( pNode->vFanins, Fraig_Regular(Fraig_Regular(pNode->p1)->p2) ); + Fraig_NodeVecPushUnique( pNode->vFanins, Fraig_Regular(Fraig_Regular(pNode->p2)->p1) ); + Fraig_NodeVecPushUnique( pNode->vFanins, Fraig_Regular(Fraig_Regular(pNode->p2)->p2) ); + Fraig_SupergateAddClausesMux( pMan, pNode ); +// Fraig_DetectFanoutFreeConeMux( pMan, pNode ); + + nMuxes++; + } + else + { + pNode->vFanins = Fraig_CollectSupergate( pNode, fUseMuxes ); + Fraig_SupergateAddClauses( pMan, pNode, pNode->vFanins ); + } + assert( pNode->vFanins->nSize > 1 ); + pNode->fClauses = 1; + pMan->nVarsClauses++; + + pNode->fMark2 = 1; // goes together with Fraig_SetupAdjacentMark() + } + + // explore the implication fanins of pNode + for ( k = 0; k < pNode->vFanins->nSize; k++ ) + { + pFanin = Fraig_Regular(pNode->vFanins->pArray[k]); + if ( pFanin->TravId == pMan->nTravIds ) // already collected + continue; + // collect and mark + Msat_IntVecPush( pMan->vVarsInt, pFanin->Num ); + Msat_IntVecWriteEntry( pMan->vVarsUsed, pFanin->Num, 1 ); + pFanin->TravId = pMan->nTravIds; + } + } + + // set up the adjacent variable information +// Fraig_SetupAdjacent( pMan, pMan->vVarsInt ); + Fraig_SetupAdjacentMark( pMan, pMan->vVarsInt ); +} + + + +/**Function************************************************************* + + Synopsis [Set up the adjacent variable information.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_SetupAdjacent( Fraig_Man_t * pMan, Msat_IntVec_t * vConeVars ) +{ + Fraig_Node_t * pNode, * pFanin; + Msat_IntVec_t * vAdjs; + int * pVars, nVars, i, k; + + // clean the adjacents for the variables + nVars = Msat_IntVecReadSize( vConeVars ); + pVars = Msat_IntVecReadArray( vConeVars ); + for ( i = 0; i < nVars; i++ ) + { + // process its connections + vAdjs = (Msat_IntVec_t *)Msat_ClauseVecReadEntry( pMan->vAdjacents, pVars[i] ); + Msat_IntVecClear( vAdjs ); + + pNode = pMan->vNodes->pArray[pVars[i]]; + if ( !Fraig_NodeIsAnd(pNode) ) + continue; + + // add fanins + vAdjs = (Msat_IntVec_t *)Msat_ClauseVecReadEntry( pMan->vAdjacents, pVars[i] ); + for ( k = 0; k < pNode->vFanins->nSize; k++ ) +// for ( k = pNode->vFanins->nSize - 1; k >= 0; k-- ) + { + pFanin = Fraig_Regular(pNode->vFanins->pArray[k]); + Msat_IntVecPush( vAdjs, pFanin->Num ); +// Msat_IntVecPushUniqueOrder( vAdjs, pFanin->Num ); + } + } + // add the fanouts + for ( i = 0; i < nVars; i++ ) + { + pNode = pMan->vNodes->pArray[pVars[i]]; + if ( !Fraig_NodeIsAnd(pNode) ) + continue; + + // add the edges + for ( k = 0; k < pNode->vFanins->nSize; k++ ) +// for ( k = pNode->vFanins->nSize - 1; k >= 0; k-- ) + { + pFanin = Fraig_Regular(pNode->vFanins->pArray[k]); + vAdjs = (Msat_IntVec_t *)Msat_ClauseVecReadEntry( pMan->vAdjacents, pFanin->Num ); + Msat_IntVecPush( vAdjs, pNode->Num ); +// Msat_IntVecPushUniqueOrder( vAdjs, pFanin->Num ); + } + } +} + + +/**Function************************************************************* + + Synopsis [Set up the adjacent variable information.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_SetupAdjacentMark( Fraig_Man_t * pMan, Msat_IntVec_t * vConeVars ) +{ + Fraig_Node_t * pNode, * pFanin; + Msat_IntVec_t * vAdjs; + int * pVars, nVars, i, k; + + // clean the adjacents for the variables + nVars = Msat_IntVecReadSize( vConeVars ); + pVars = Msat_IntVecReadArray( vConeVars ); + for ( i = 0; i < nVars; i++ ) + { + pNode = pMan->vNodes->pArray[pVars[i]]; + if ( pNode->fMark2 == 0 ) + continue; +// pNode->fMark2 = 0; + + // process its connections +// vAdjs = (Msat_IntVec_t *)Msat_ClauseVecReadEntry( pMan->vAdjacents, pVars[i] ); +// Msat_IntVecClear( vAdjs ); + + if ( !Fraig_NodeIsAnd(pNode) ) + continue; + + // add fanins + vAdjs = (Msat_IntVec_t *)Msat_ClauseVecReadEntry( pMan->vAdjacents, pVars[i] ); + for ( k = 0; k < pNode->vFanins->nSize; k++ ) +// for ( k = pNode->vFanins->nSize - 1; k >= 0; k-- ) + { + pFanin = Fraig_Regular(pNode->vFanins->pArray[k]); + Msat_IntVecPush( vAdjs, pFanin->Num ); +// Msat_IntVecPushUniqueOrder( vAdjs, pFanin->Num ); + } + } + // add the fanouts + for ( i = 0; i < nVars; i++ ) + { + pNode = pMan->vNodes->pArray[pVars[i]]; + if ( pNode->fMark2 == 0 ) + continue; + pNode->fMark2 = 0; + + if ( !Fraig_NodeIsAnd(pNode) ) + continue; + + // add the edges + for ( k = 0; k < pNode->vFanins->nSize; k++ ) +// for ( k = pNode->vFanins->nSize - 1; k >= 0; k-- ) + { + pFanin = Fraig_Regular(pNode->vFanins->pArray[k]); + vAdjs = (Msat_IntVec_t *)Msat_ClauseVecReadEntry( pMan->vAdjacents, pFanin->Num ); + Msat_IntVecPush( vAdjs, pNode->Num ); +// Msat_IntVecPushUniqueOrder( vAdjs, pFanin->Num ); + } + } +} + + + + +/**Function************************************************************* + + Synopsis [Adds clauses to the solver.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_SupergateAddClauses( Fraig_Man_t * p, Fraig_Node_t * pNode, Fraig_NodeVec_t * vSuper ) +{ + int fComp1, RetValue, nVars, Var, Var1, i; + + assert( Fraig_NodeIsAnd( pNode ) ); + nVars = Msat_SolverReadVarNum(p->pSat); + + Var = pNode->Num; + assert( Var < nVars ); + for ( i = 0; i < vSuper->nSize; i++ ) + { + // get the predecessor nodes + // get the complemented attributes of the nodes + fComp1 = Fraig_IsComplement(vSuper->pArray[i]); + // determine the variable numbers + Var1 = Fraig_Regular(vSuper->pArray[i])->Num; + // check that the variables are in the SAT manager + assert( Var1 < nVars ); + + // suppose the AND-gate is A * B = C + // add !A => !C or A + !C + // fprintf( pFile, "%d %d 0%c", Var1, -Var, 10 ); + Msat_IntVecClear( p->vProj ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(Var1, fComp1) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(Var, 1) ); + RetValue = Msat_SolverAddClause( p->pSat, p->vProj ); + assert( RetValue ); + } + + // add A & B => C or !A + !B + C +// fprintf( pFile, "%d %d %d 0%c", -Var1, -Var2, Var, 10 ); + Msat_IntVecClear( p->vProj ); + for ( i = 0; i < vSuper->nSize; i++ ) + { + // get the predecessor nodes + // get the complemented attributes of the nodes + fComp1 = Fraig_IsComplement(vSuper->pArray[i]); + // determine the variable numbers + Var1 = Fraig_Regular(vSuper->pArray[i])->Num; + + // add this variable to the array + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(Var1, !fComp1) ); + } + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(Var, 0) ); + RetValue = Msat_SolverAddClause( p->pSat, p->vProj ); + assert( RetValue ); +} + +/**Function************************************************************* + + Synopsis [Adds clauses to the solver.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_SupergateAddClausesExor( Fraig_Man_t * p, Fraig_Node_t * pNode ) +{ + Fraig_Node_t * pNode1, * pNode2; + int fComp, RetValue; + + assert( !Fraig_IsComplement( pNode ) ); + assert( Fraig_NodeIsExorType( pNode ) ); + // get nodes + pNode1 = Fraig_Regular(Fraig_Regular(pNode->p1)->p1); + pNode2 = Fraig_Regular(Fraig_Regular(pNode->p1)->p2); + // get the complemented attribute of the EXOR/NEXOR gate + fComp = Fraig_NodeIsExor( pNode ); // 1 if EXOR, 0 if NEXOR + + // create four clauses + Msat_IntVecClear( p->vProj ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNode->Num, fComp) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNode1->Num, fComp) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNode2->Num, fComp) ); + RetValue = Msat_SolverAddClause( p->pSat, p->vProj ); + assert( RetValue ); + Msat_IntVecClear( p->vProj ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNode->Num, fComp) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNode1->Num, !fComp) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNode2->Num, !fComp) ); + RetValue = Msat_SolverAddClause( p->pSat, p->vProj ); + assert( RetValue ); + Msat_IntVecClear( p->vProj ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNode->Num, !fComp) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNode1->Num, fComp) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNode2->Num, !fComp) ); + RetValue = Msat_SolverAddClause( p->pSat, p->vProj ); + assert( RetValue ); + Msat_IntVecClear( p->vProj ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNode->Num, !fComp) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNode1->Num, !fComp) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(pNode2->Num, fComp) ); + RetValue = Msat_SolverAddClause( p->pSat, p->vProj ); + assert( RetValue ); +} + +/**Function************************************************************* + + Synopsis [Adds clauses to the solver.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_SupergateAddClausesMux( Fraig_Man_t * p, Fraig_Node_t * pNode ) +{ + Fraig_Node_t * pNodeI, * pNodeT, * pNodeE; + int RetValue, VarF, VarI, VarT, VarE, fCompT, fCompE; + + assert( !Fraig_IsComplement( pNode ) ); + assert( Fraig_NodeIsMuxType( pNode ) ); + // get nodes (I = if, T = then, E = else) + pNodeI = Fraig_NodeRecognizeMux( pNode, &pNodeT, &pNodeE ); + // get the variable numbers + VarF = pNode->Num; + VarI = pNodeI->Num; + VarT = Fraig_Regular(pNodeT)->Num; + VarE = Fraig_Regular(pNodeE)->Num; + // get the complementation flags + fCompT = Fraig_IsComplement(pNodeT); + fCompE = Fraig_IsComplement(pNodeE); + + // f = ITE(i, t, e) + + // i' + t' + f + // i' + t + f' + // i + e' + f + // i + e + f' + + // create four clauses + Msat_IntVecClear( p->vProj ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(VarI, 1) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(VarT, 1^fCompT) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(VarF, 0) ); + RetValue = Msat_SolverAddClause( p->pSat, p->vProj ); + assert( RetValue ); + Msat_IntVecClear( p->vProj ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(VarI, 1) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(VarT, 0^fCompT) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(VarF, 1) ); + RetValue = Msat_SolverAddClause( p->pSat, p->vProj ); + assert( RetValue ); + Msat_IntVecClear( p->vProj ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(VarI, 0) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(VarE, 1^fCompE) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(VarF, 0) ); + RetValue = Msat_SolverAddClause( p->pSat, p->vProj ); + assert( RetValue ); + Msat_IntVecClear( p->vProj ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(VarI, 0) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(VarE, 0^fCompE) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(VarF, 1) ); + RetValue = Msat_SolverAddClause( p->pSat, p->vProj ); + assert( RetValue ); + + // two additional clauses + // t' & e' -> f' + // t & e -> f + + // t + e + f' + // t' + e' + f + + if ( VarT == VarE ) + { +// assert( fCompT == !fCompE ); + return; + } + + Msat_IntVecClear( p->vProj ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(VarT, 0^fCompT) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(VarE, 0^fCompE) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(VarF, 1) ); + RetValue = Msat_SolverAddClause( p->pSat, p->vProj ); + assert( RetValue ); + Msat_IntVecClear( p->vProj ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(VarT, 1^fCompT) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(VarE, 1^fCompE) ); + Msat_IntVecPush( p->vProj, MSAT_VAR2LIT(VarF, 0) ); + RetValue = Msat_SolverAddClause( p->pSat, p->vProj ); + assert( RetValue ); + +} + + + + + +/**Function************************************************************* + + Synopsis [Returns the array of nodes to be combined into one multi-input AND-gate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_DetectFanoutFreeCone_rec( Fraig_Node_t * pNode, Fraig_NodeVec_t * vSuper, Fraig_NodeVec_t * vInside, int fFirst ) +{ + // make the pointer regular + pNode = Fraig_Regular(pNode); + // if the new node is complemented or a PI, another gate begins + if ( (!fFirst && pNode->nRefs > 1) || Fraig_NodeIsVar(pNode) ) + { + Fraig_NodeVecPushUnique( vSuper, pNode ); + return; + } + // go through the branches + Fraig_DetectFanoutFreeCone_rec( pNode->p1, vSuper, vInside, 0 ); + Fraig_DetectFanoutFreeCone_rec( pNode->p2, vSuper, vInside, 0 ); + // add the node + Fraig_NodeVecPushUnique( vInside, pNode ); +} + +/**Function************************************************************* + + Synopsis [Returns the array of nodes to be combined into one multi-input AND-gate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +/* +void Fraig_DetectFanoutFreeCone( Fraig_Man_t * pMan, Fraig_Node_t * pNode ) +{ + Fraig_NodeVec_t * vFanins; + Fraig_NodeVec_t * vInside; + int nCubes; + extern int Fraig_CutSopCountCubes( Fraig_Man_t * pMan, Fraig_NodeVec_t * vFanins, Fraig_NodeVec_t * vInside ); + + vFanins = Fraig_NodeVecAlloc( 8 ); + vInside = Fraig_NodeVecAlloc( 8 ); + + Fraig_DetectFanoutFreeCone_rec( pNode, vFanins, vInside, 1 ); + assert( vInside->pArray[vInside->nSize-1] == pNode ); + + nCubes = Fraig_CutSopCountCubes( pMan, vFanins, vInside ); + +printf( "%d(%d)", vFanins->nSize, nCubes ); + Fraig_NodeVecFree( vFanins ); + Fraig_NodeVecFree( vInside ); +} +*/ + + + +/**Function************************************************************* + + Synopsis [Returns the array of nodes to be combined into one multi-input AND-gate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_DetectFanoutFreeConeMux_rec( Fraig_Node_t * pNode, Fraig_NodeVec_t * vSuper, Fraig_NodeVec_t * vInside, int fFirst ) +{ + // make the pointer regular + pNode = Fraig_Regular(pNode); + // if the new node is complemented or a PI, another gate begins + if ( (!fFirst && pNode->nRefs > 1) || Fraig_NodeIsVar(pNode) || !Fraig_NodeIsMuxType(pNode) ) + { + Fraig_NodeVecPushUnique( vSuper, pNode ); + return; + } + // go through the branches + Fraig_DetectFanoutFreeConeMux_rec( Fraig_Regular(pNode->p1)->p1, vSuper, vInside, 0 ); + Fraig_DetectFanoutFreeConeMux_rec( Fraig_Regular(pNode->p1)->p2, vSuper, vInside, 0 ); + Fraig_DetectFanoutFreeConeMux_rec( Fraig_Regular(pNode->p2)->p1, vSuper, vInside, 0 ); + Fraig_DetectFanoutFreeConeMux_rec( Fraig_Regular(pNode->p2)->p2, vSuper, vInside, 0 ); + // add the node + Fraig_NodeVecPushUnique( vInside, pNode ); +} + +/**Function************************************************************* + + Synopsis [Returns the array of nodes to be combined into one multi-input AND-gate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_DetectFanoutFreeConeMux( Fraig_Man_t * pMan, Fraig_Node_t * pNode ) +{ + Fraig_NodeVec_t * vFanins; + Fraig_NodeVec_t * vInside; + int nCubes; + extern int Fraig_CutSopCountCubes( Fraig_Man_t * pMan, Fraig_NodeVec_t * vFanins, Fraig_NodeVec_t * vInside ); + + vFanins = Fraig_NodeVecAlloc( 8 ); + vInside = Fraig_NodeVecAlloc( 8 ); + + Fraig_DetectFanoutFreeConeMux_rec( pNode, vFanins, vInside, 1 ); + assert( vInside->pArray[vInside->nSize-1] == pNode ); + +// nCubes = Fraig_CutSopCountCubes( pMan, vFanins, vInside ); + nCubes = 0; + +printf( "%d(%d)", vFanins->nSize, nCubes ); + Fraig_NodeVecFree( vFanins ); + Fraig_NodeVecFree( vInside ); +} + + + +/**Function************************************************************* + + Synopsis [Collect variables using their proximity from the nodes.] + + Description [This procedure creates a variable order based on collecting + first the nodes that are the closest to the given two target nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_SetActivity( Fraig_Man_t * pMan, Fraig_Node_t * pOld, Fraig_Node_t * pNew ) +{ + Fraig_Node_t * pNode; + int i, Number, MaxLevel; + float * pFactors = Msat_SolverReadFactors(pMan->pSat); + if ( pFactors == NULL ) + return; + MaxLevel = FRAIG_MAX( pOld->Level, pNew->Level ); + // create the variable order + for ( i = 0; i < Msat_IntVecReadSize(pMan->vVarsInt); i++ ) + { + // get the new node on the frontier + Number = Msat_IntVecReadEntry(pMan->vVarsInt, i); + pNode = pMan->vNodes->pArray[Number]; + pFactors[pNode->Num] = (float)pow( 0.97, MaxLevel - pNode->Level ); +// if ( pNode->Num % 50 == 0 ) +// printf( "(%d) %.2f ", MaxLevel - pNode->Level, pFactors[pNode->Num] ); + } +// printf( "\n" ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/fraig/fraigTable.c b/abc_with_bb_support/src/sat/fraig/fraigTable.c new file mode 100644 index 000000000..c5ba657c4 --- /dev/null +++ b/abc_with_bb_support/src/sat/fraig/fraigTable.c @@ -0,0 +1,657 @@ +/**CFile**************************************************************** + + FileName [fraigTable.c] + + PackageName [FRAIG: Functionally reduced AND-INV graphs.] + + Synopsis [Structural and functional hash tables.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - October 1, 2004] + + Revision [$Id: fraigTable.c,v 1.7 2005/07/08 01:01:34 alanmi Exp $] + +***********************************************************************/ + +#include "fraigInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Fraig_TableResizeS( Fraig_HashTable_t * p ); +static void Fraig_TableResizeF( Fraig_HashTable_t * p, int fUseSimR ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates the hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_HashTable_t * Fraig_HashTableCreate( int nSize ) +{ + Fraig_HashTable_t * p; + // allocate the table + p = ALLOC( Fraig_HashTable_t, 1 ); + memset( p, 0, sizeof(Fraig_HashTable_t) ); + // allocate and clean the bins + p->nBins = Cudd_PrimeFraig(nSize); + p->pBins = ALLOC( Fraig_Node_t *, p->nBins ); + memset( p->pBins, 0, sizeof(Fraig_Node_t *) * p->nBins ); + return p; +} + +/**Function************************************************************* + + Synopsis [Deallocates the supergate hash table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_HashTableFree( Fraig_HashTable_t * p ) +{ + FREE( p->pBins ); + FREE( p ); +} + +/**Function************************************************************* + + Synopsis [Looks up an entry in the structural hash table.] + + Description [If the entry with the same children does not exists, + creates it, inserts it into the table, and returns 0. If the entry + with the same children exists, finds it, and return 1. In both cases, + the new/old entry is returned in ppNodeRes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_HashTableLookupS( Fraig_Man_t * pMan, Fraig_Node_t * p1, Fraig_Node_t * p2, Fraig_Node_t ** ppNodeRes ) +{ + Fraig_HashTable_t * p = pMan->pTableS; + Fraig_Node_t * pEnt; + unsigned Key; + + // order the arguments + if ( Fraig_Regular(p1)->Num > Fraig_Regular(p2)->Num ) + pEnt = p1, p1 = p2, p2 = pEnt; + + Key = Fraig_HashKey2( p1, p2, p->nBins ); + Fraig_TableBinForEachEntryS( p->pBins[Key], pEnt ) + if ( pEnt->p1 == p1 && pEnt->p2 == p2 ) + { + *ppNodeRes = pEnt; + return 1; + } + // check if it is a good time for table resizing + if ( p->nEntries >= 2 * p->nBins ) + { + Fraig_TableResizeS( p ); + Key = Fraig_HashKey2( p1, p2, p->nBins ); + } + // create the new node + pEnt = Fraig_NodeCreate( pMan, p1, p2 ); + // add the node to the corresponding linked list in the table + pEnt->pNextS = p->pBins[Key]; + p->pBins[Key] = pEnt; + *ppNodeRes = pEnt; + p->nEntries++; + return 0; +} + + +/**Function************************************************************* + + Synopsis [Insert the entry in the functional hash table.] + + Description [If the entry with the same key exists, return it right away. + If the entry with the same key does not exists, inserts it and returns NULL. ] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_Node_t * Fraig_HashTableLookupF( Fraig_Man_t * pMan, Fraig_Node_t * pNode ) +{ + Fraig_HashTable_t * p = pMan->pTableF; + Fraig_Node_t * pEnt, * pEntD; + unsigned Key; + + // go through the hash table entries + Key = pNode->uHashR % p->nBins; + Fraig_TableBinForEachEntryF( p->pBins[Key], pEnt ) + { + // if their simulation info differs, skip + if ( !Fraig_CompareSimInfo( pNode, pEnt, pMan->nWordsRand, 1 ) ) + continue; + // equivalent up to the complement + Fraig_TableBinForEachEntryD( pEnt, pEntD ) + { + // if their simulation info differs, skip + if ( !Fraig_CompareSimInfo( pNode, pEntD, pMan->iWordStart, 0 ) ) + continue; + // found a simulation-equivalent node + return pEntD; + } + // did not find a simulation equivalent node + // add the node to the corresponding linked list + pNode->pNextD = pEnt->pNextD; + pEnt->pNextD = pNode; + // return NULL, because there is no functional equivalence in this case + return NULL; + } + + // check if it is a good time for table resizing + if ( p->nEntries >= 2 * p->nBins ) + { + Fraig_TableResizeF( p, 1 ); + Key = pNode->uHashR % p->nBins; + } + + // add the node to the corresponding linked list in the table + pNode->pNextF = p->pBins[Key]; + p->pBins[Key] = pNode; + p->nEntries++; + // return NULL, because there is no functional equivalence in this case + return NULL; +} + +/**Function************************************************************* + + Synopsis [Insert the entry in the functional hash table.] + + Description [If the entry with the same key exists, return it right away. + If the entry with the same key does not exists, inserts it and returns NULL. ] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_Node_t * Fraig_HashTableLookupF0( Fraig_Man_t * pMan, Fraig_Node_t * pNode ) +{ + Fraig_HashTable_t * p = pMan->pTableF0; + Fraig_Node_t * pEnt; + unsigned Key; + + // go through the hash table entries + Key = pNode->uHashD % p->nBins; + Fraig_TableBinForEachEntryF( p->pBins[Key], pEnt ) + { + // if their simulation info differs, skip + if ( !Fraig_CompareSimInfo( pNode, pEnt, pMan->iWordStart, 0 ) ) + continue; + // found a simulation-equivalent node + return pEnt; + } + + // check if it is a good time for table resizing + if ( p->nEntries >= 2 * p->nBins ) + { + Fraig_TableResizeF( p, 0 ); + Key = pNode->uHashD % p->nBins; + } + + // add the node to the corresponding linked list in the table + pNode->pNextF = p->pBins[Key]; + p->pBins[Key] = pNode; + p->nEntries++; + // return NULL, because there is no functional equivalence in this case + return NULL; +} + +/**Function************************************************************* + + Synopsis [Insert the entry in the functional hash table.] + + Description [Unconditionally add the node to the corresponding + linked list in the table.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_HashTableInsertF0( Fraig_Man_t * pMan, Fraig_Node_t * pNode ) +{ + Fraig_HashTable_t * p = pMan->pTableF0; + unsigned Key = pNode->uHashD % p->nBins; + + pNode->pNextF = p->pBins[Key]; + p->pBins[Key] = pNode; + p->nEntries++; +} + + +/**Function************************************************************* + + Synopsis [Resizes the table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_TableResizeS( Fraig_HashTable_t * p ) +{ + Fraig_Node_t ** pBinsNew; + Fraig_Node_t * pEnt, * pEnt2; + int nBinsNew, Counter, i, clk; + unsigned Key; + +clk = clock(); + // get the new table size + nBinsNew = Cudd_PrimeFraig(2 * p->nBins); + // allocate a new array + pBinsNew = ALLOC( Fraig_Node_t *, nBinsNew ); + memset( pBinsNew, 0, sizeof(Fraig_Node_t *) * nBinsNew ); + // rehash the entries from the old table + Counter = 0; + for ( i = 0; i < p->nBins; i++ ) + Fraig_TableBinForEachEntrySafeS( p->pBins[i], pEnt, pEnt2 ) + { + Key = Fraig_HashKey2( pEnt->p1, pEnt->p2, nBinsNew ); + pEnt->pNextS = pBinsNew[Key]; + pBinsNew[Key] = pEnt; + Counter++; + } + assert( Counter == p->nEntries ); +// printf( "Increasing the structural table size from %6d to %6d. ", p->nBins, nBinsNew ); +// PRT( "Time", clock() - clk ); + // replace the table and the parameters + free( p->pBins ); + p->pBins = pBinsNew; + p->nBins = nBinsNew; +} + +/**Function************************************************************* + + Synopsis [Resizes the table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_TableResizeF( Fraig_HashTable_t * p, int fUseSimR ) +{ + Fraig_Node_t ** pBinsNew; + Fraig_Node_t * pEnt, * pEnt2; + int nBinsNew, Counter, i, clk; + unsigned Key; + +clk = clock(); + // get the new table size + nBinsNew = Cudd_PrimeFraig(2 * p->nBins); + // allocate a new array + pBinsNew = ALLOC( Fraig_Node_t *, nBinsNew ); + memset( pBinsNew, 0, sizeof(Fraig_Node_t *) * nBinsNew ); + // rehash the entries from the old table + Counter = 0; + for ( i = 0; i < p->nBins; i++ ) + Fraig_TableBinForEachEntrySafeF( p->pBins[i], pEnt, pEnt2 ) + { + if ( fUseSimR ) + Key = pEnt->uHashR % nBinsNew; + else + Key = pEnt->uHashD % nBinsNew; + pEnt->pNextF = pBinsNew[Key]; + pBinsNew[Key] = pEnt; + Counter++; + } + assert( Counter == p->nEntries ); +// printf( "Increasing the functional table size from %6d to %6d. ", p->nBins, nBinsNew ); +// PRT( "Time", clock() - clk ); + // replace the table and the parameters + free( p->pBins ); + p->pBins = pBinsNew; + p->nBins = nBinsNew; +} + + +/**Function************************************************************* + + Synopsis [Compares two pieces of simulation info.] + + Description [Returns 1 if they are equal.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_CompareSimInfo( Fraig_Node_t * pNode1, Fraig_Node_t * pNode2, int iWordLast, int fUseRand ) +{ + int i; + assert( !Fraig_IsComplement(pNode1) ); + assert( !Fraig_IsComplement(pNode2) ); + if ( fUseRand ) + { + // if their signatures differ, skip + if ( pNode1->uHashR != pNode2->uHashR ) + return 0; + // check the simulation info + for ( i = 0; i < iWordLast; i++ ) + if ( pNode1->puSimR[i] != pNode2->puSimR[i] ) + return 0; + } + else + { + // if their signatures differ, skip + if ( pNode1->uHashD != pNode2->uHashD ) + return 0; + // check the simulation info + for ( i = 0; i < iWordLast; i++ ) + if ( pNode1->puSimD[i] != pNode2->puSimD[i] ) + return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Find the number of the different pattern.] + + Description [Returns -1 if there is no such pattern] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_FindFirstDiff( Fraig_Node_t * pNode1, Fraig_Node_t * pNode2, int fCompl, int iWordLast, int fUseRand ) +{ + int i, v; + assert( !Fraig_IsComplement(pNode1) ); + assert( !Fraig_IsComplement(pNode2) ); + // take into account possible internal complementation + fCompl ^= pNode1->fInv; + fCompl ^= pNode2->fInv; + // find the pattern + if ( fCompl ) + { + if ( fUseRand ) + { + for ( i = 0; i < iWordLast; i++ ) + if ( pNode1->puSimR[i] != ~pNode2->puSimR[i] ) + for ( v = 0; v < 32; v++ ) + if ( (pNode1->puSimR[i] ^ ~pNode2->puSimR[i]) & (1 << v) ) + return i * 32 + v; + } + else + { + for ( i = 0; i < iWordLast; i++ ) + if ( pNode1->puSimD[i] != ~pNode2->puSimD[i] ) + for ( v = 0; v < 32; v++ ) + if ( (pNode1->puSimD[i] ^ ~pNode2->puSimD[i]) & (1 << v) ) + return i * 32 + v; + } + } + else + { + if ( fUseRand ) + { + for ( i = 0; i < iWordLast; i++ ) + if ( pNode1->puSimR[i] != pNode2->puSimR[i] ) + for ( v = 0; v < 32; v++ ) + if ( (pNode1->puSimR[i] ^ pNode2->puSimR[i]) & (1 << v) ) + return i * 32 + v; + } + else + { + for ( i = 0; i < iWordLast; i++ ) + if ( pNode1->puSimD[i] != pNode2->puSimD[i] ) + for ( v = 0; v < 32; v++ ) + if ( (pNode1->puSimD[i] ^ pNode2->puSimD[i]) & (1 << v) ) + return i * 32 + v; + } + } + return -1; +} + +/**Function************************************************************* + + Synopsis [Compares two pieces of simulation info.] + + Description [Returns 1 if they are equal.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_CompareSimInfoUnderMask( Fraig_Node_t * pNode1, Fraig_Node_t * pNode2, int iWordLast, int fUseRand, unsigned * puMask ) +{ + unsigned * pSims1, * pSims2; + int i; + assert( !Fraig_IsComplement(pNode1) ); + assert( !Fraig_IsComplement(pNode2) ); + // get hold of simulation info + pSims1 = fUseRand? pNode1->puSimR : pNode1->puSimD; + pSims2 = fUseRand? pNode2->puSimR : pNode2->puSimD; + // check the simulation info + for ( i = 0; i < iWordLast; i++ ) + if ( (pSims1[i] & puMask[i]) != (pSims2[i] & puMask[i]) ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Compares two pieces of simulation info.] + + Description [Returns 1 if they are equal.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_CollectXors( Fraig_Node_t * pNode1, Fraig_Node_t * pNode2, int iWordLast, int fUseRand, unsigned * puMask ) +{ + unsigned * pSims1, * pSims2; + int i; + assert( !Fraig_IsComplement(pNode1) ); + assert( !Fraig_IsComplement(pNode2) ); + // get hold of simulation info + pSims1 = fUseRand? pNode1->puSimR : pNode1->puSimD; + pSims2 = fUseRand? pNode2->puSimR : pNode2->puSimD; + // check the simulation info + for ( i = 0; i < iWordLast; i++ ) + puMask[i] = ( pSims1[i] ^ pSims2[i] ); +} + + +/**Function************************************************************* + + Synopsis [Prints stats of the structural table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_TablePrintStatsS( Fraig_Man_t * pMan ) +{ + Fraig_HashTable_t * pT = pMan->pTableS; + Fraig_Node_t * pNode; + int i, Counter; + + printf( "Structural table. Table size = %d. Number of entries = %d.\n", pT->nBins, pT->nEntries ); + for ( i = 0; i < pT->nBins; i++ ) + { + Counter = 0; + Fraig_TableBinForEachEntryS( pT->pBins[i], pNode ) + Counter++; + if ( Counter > 1 ) + { + printf( "%d ", Counter ); + if ( Counter > 50 ) + printf( "{%d} ", i ); + } + } + printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [Prints stats of the structural table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_TablePrintStatsF( Fraig_Man_t * pMan ) +{ + Fraig_HashTable_t * pT = pMan->pTableF; + Fraig_Node_t * pNode; + int i, Counter; + + printf( "Functional table. Table size = %d. Number of entries = %d.\n", pT->nBins, pT->nEntries ); + for ( i = 0; i < pT->nBins; i++ ) + { + Counter = 0; + Fraig_TableBinForEachEntryF( pT->pBins[i], pNode ) + Counter++; + if ( Counter > 1 ) + printf( "{%d} ", Counter ); + } + printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [Prints stats of the structural table.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_TablePrintStatsF0( Fraig_Man_t * pMan ) +{ + Fraig_HashTable_t * pT = pMan->pTableF0; + Fraig_Node_t * pNode; + int i, Counter; + + printf( "Zero-node table. Table size = %d. Number of entries = %d.\n", pT->nBins, pT->nEntries ); + for ( i = 0; i < pT->nBins; i++ ) + { + Counter = 0; + Fraig_TableBinForEachEntryF( pT->pBins[i], pNode ) + Counter++; + if ( Counter == 0 ) + continue; +/* + printf( "\nBin = %4d : Number of entries = %4d\n", i, Counter ); + Fraig_TableBinForEachEntryF( pT->pBins[i], pNode ) + printf( "Node %5d. Hash = %10d.\n", pNode->Num, pNode->uHashD ); +*/ + } + printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [Rehashes the table after the simulation info has changed.] + + Description [Assumes that the hash values have been updated after performing + additional simulation. Rehashes the table using the new hash values. + Uses pNextF to link the entries in the bins. Uses pNextD to link the entries + with identical hash values. Returns 1 if the identical entries have been found. + Note that identical hash values may mean that the simulation data is different.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_TableRehashF0( Fraig_Man_t * pMan, int fLinkEquiv ) +{ + Fraig_HashTable_t * pT = pMan->pTableF0; + Fraig_Node_t ** pBinsNew; + Fraig_Node_t * pEntF, * pEntF2, * pEnt, * pEntD2, * pEntN; + int ReturnValue, Counter, i; + unsigned Key; + + // allocate a new array of bins + pBinsNew = ALLOC( Fraig_Node_t *, pT->nBins ); + memset( pBinsNew, 0, sizeof(Fraig_Node_t *) * pT->nBins ); + + // rehash the entries in the table + // go through all the nodes in the F-lists (and possible in D-lists, if used) + Counter = 0; + ReturnValue = 0; + for ( i = 0; i < pT->nBins; i++ ) + Fraig_TableBinForEachEntrySafeF( pT->pBins[i], pEntF, pEntF2 ) + Fraig_TableBinForEachEntrySafeD( pEntF, pEnt, pEntD2 ) + { + // decide where to put entry pEnt + Key = pEnt->uHashD % pT->nBins; + if ( fLinkEquiv ) + { + // go through the entries in the new bin + Fraig_TableBinForEachEntryF( pBinsNew[Key], pEntN ) + { + // if they have different values skip + if ( pEnt->uHashD != pEntN->uHashD ) + continue; + // they have the same hash value, add pEnt to the D-list pEnt3 + pEnt->pNextD = pEntN->pNextD; + pEntN->pNextD = pEnt; + ReturnValue = 1; + Counter++; + break; + } + if ( pEntN != NULL ) // already linked + continue; + // we did not find equal entry + } + // link the new entry + pEnt->pNextF = pBinsNew[Key]; + pBinsNew[Key] = pEnt; + pEnt->pNextD = NULL; + Counter++; + } + assert( Counter == pT->nEntries ); + // replace the table and the parameters + free( pT->pBins ); + pT->pBins = pBinsNew; + return ReturnValue; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/fraig/fraigUtil.c b/abc_with_bb_support/src/sat/fraig/fraigUtil.c new file mode 100644 index 000000000..78cc5663b --- /dev/null +++ b/abc_with_bb_support/src/sat/fraig/fraigUtil.c @@ -0,0 +1,1034 @@ +/**CFile**************************************************************** + + FileName [fraigUtil.c] + + PackageName [FRAIG: Functionally reduced AND-INV graphs.] + + Synopsis [Various utilities.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - October 1, 2004] + + Revision [$Id: fraigUtil.c,v 1.15 2005/07/08 01:01:34 alanmi Exp $] + +***********************************************************************/ + +#include "fraigInt.h" +#include + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int bit_count[256] = { + 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 +}; + +static void Fraig_Dfs_rec( Fraig_Man_t * pMan, Fraig_Node_t * pNode, Fraig_NodeVec_t * vNodes, int fEquiv ); +static int Fraig_CheckTfi_rec( Fraig_Man_t * pMan, Fraig_Node_t * pNode, Fraig_Node_t * pOld ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Computes the DFS ordering of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_NodeVec_t * Fraig_Dfs( Fraig_Man_t * pMan, int fEquiv ) +{ + Fraig_NodeVec_t * vNodes; + int i; + pMan->nTravIds++; + vNodes = Fraig_NodeVecAlloc( 100 ); + for ( i = 0; i < pMan->vOutputs->nSize; i++ ) + Fraig_Dfs_rec( pMan, Fraig_Regular(pMan->vOutputs->pArray[i]), vNodes, fEquiv ); + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Computes the DFS ordering of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_NodeVec_t * Fraig_DfsOne( Fraig_Man_t * pMan, Fraig_Node_t * pNode, int fEquiv ) +{ + Fraig_NodeVec_t * vNodes; + pMan->nTravIds++; + vNodes = Fraig_NodeVecAlloc( 100 ); + Fraig_Dfs_rec( pMan, Fraig_Regular(pNode), vNodes, fEquiv ); + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Computes the DFS ordering of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_NodeVec_t * Fraig_DfsNodes( Fraig_Man_t * pMan, Fraig_Node_t ** ppNodes, int nNodes, int fEquiv ) +{ + Fraig_NodeVec_t * vNodes; + int i; + pMan->nTravIds++; + vNodes = Fraig_NodeVecAlloc( 100 ); + for ( i = 0; i < nNodes; i++ ) + Fraig_Dfs_rec( pMan, Fraig_Regular(ppNodes[i]), vNodes, fEquiv ); + return vNodes; +} + +/**Function************************************************************* + + Synopsis [Recursively computes the DFS ordering of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_Dfs_rec( Fraig_Man_t * pMan, Fraig_Node_t * pNode, Fraig_NodeVec_t * vNodes, int fEquiv ) +{ + assert( !Fraig_IsComplement(pNode) ); + // skip the visited node + if ( pNode->TravId == pMan->nTravIds ) + return; + pNode->TravId = pMan->nTravIds; + // visit the transitive fanin + if ( Fraig_NodeIsAnd(pNode) ) + { + Fraig_Dfs_rec( pMan, Fraig_Regular(pNode->p1), vNodes, fEquiv ); + Fraig_Dfs_rec( pMan, Fraig_Regular(pNode->p2), vNodes, fEquiv ); + } + if ( fEquiv && pNode->pNextE ) + Fraig_Dfs_rec( pMan, pNode->pNextE, vNodes, fEquiv ); + // save the node + Fraig_NodeVecPush( vNodes, pNode ); +} + +/**Function************************************************************* + + Synopsis [Computes the DFS ordering of the nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_CountNodes( Fraig_Man_t * pMan, int fEquiv ) +{ + Fraig_NodeVec_t * vNodes; + int RetValue; + vNodes = Fraig_Dfs( pMan, fEquiv ); + RetValue = vNodes->nSize; + Fraig_NodeVecFree( vNodes ); + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Returns 1 if pOld is in the TFI of pNew.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_CheckTfi( Fraig_Man_t * pMan, Fraig_Node_t * pOld, Fraig_Node_t * pNew ) +{ + assert( !Fraig_IsComplement(pOld) ); + assert( !Fraig_IsComplement(pNew) ); + pMan->nTravIds++; + return Fraig_CheckTfi_rec( pMan, pNew, pOld ); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if pOld is in the TFI of pNew.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_CheckTfi_rec( Fraig_Man_t * pMan, Fraig_Node_t * pNode, Fraig_Node_t * pOld ) +{ + // check the trivial cases + if ( pNode == NULL ) + return 0; + if ( pNode->Num < pOld->Num && !pMan->fChoicing ) + return 0; + if ( pNode == pOld ) + return 1; + // skip the visited node + if ( pNode->TravId == pMan->nTravIds ) + return 0; + pNode->TravId = pMan->nTravIds; + // check the children + if ( Fraig_CheckTfi_rec( pMan, Fraig_Regular(pNode->p1), pOld ) ) + return 1; + if ( Fraig_CheckTfi_rec( pMan, Fraig_Regular(pNode->p2), pOld ) ) + return 1; + // check equivalent nodes + return Fraig_CheckTfi_rec( pMan, pNode->pNextE, pOld ); +} + + +/**Function************************************************************* + + Synopsis [Returns 1 if pOld is in the TFI of pNew.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_CheckTfi2( Fraig_Man_t * pMan, Fraig_Node_t * pOld, Fraig_Node_t * pNew ) +{ + Fraig_NodeVec_t * vNodes; + int RetValue; + vNodes = Fraig_DfsOne( pMan, pNew, 1 ); + RetValue = (pOld->TravId == pMan->nTravIds); + Fraig_NodeVecFree( vNodes ); + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Sets the number of fanouts (none, one, or many).] + + Description [This procedure collects the nodes reachable from + the POs of the AIG and sets the type of fanout counter (none, one, + or many) for each node. This procedure is useful to determine + fanout-free cones of AND-nodes, which is helpful for rebalancing + the AIG (see procedure Fraig_ManRebalance, or something like that).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_ManMarkRealFanouts( Fraig_Man_t * p ) +{ + Fraig_NodeVec_t * vNodes; + Fraig_Node_t * pNodeR; + int i; + // collect the nodes reachable + vNodes = Fraig_Dfs( p, 0 ); + // clean the fanouts field + for ( i = 0; i < vNodes->nSize; i++ ) + { + vNodes->pArray[i]->nFanouts = 0; + vNodes->pArray[i]->pData0 = NULL; + } + // mark reachable nodes by setting the two-bit counter pNode->nFans + for ( i = 0; i < vNodes->nSize; i++ ) + { + pNodeR = Fraig_Regular(vNodes->pArray[i]->p1); + if ( pNodeR && ++pNodeR->nFanouts == 3 ) + pNodeR->nFanouts = 2; + pNodeR = Fraig_Regular(vNodes->pArray[i]->p2); + if ( pNodeR && ++pNodeR->nFanouts == 3 ) + pNodeR->nFanouts = 2; + } + Fraig_NodeVecFree( vNodes ); +} + +/**Function************************************************************* + + Synopsis [Creates the constant 1 node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_BitStringCountOnes( unsigned * pString, int nWords ) +{ + unsigned char * pSuppBytes = (unsigned char *)pString; + int i, nOnes, nBytes = sizeof(unsigned) * nWords; + // count the number of ones in the simulation vector + for ( i = nOnes = 0; i < nBytes; i++ ) + nOnes += bit_count[pSuppBytes[i]]; + return nOnes; +} + +/**Function************************************************************* + + Synopsis [Verify one useful property.] + + Description [This procedure verifies one useful property. After + the FRAIG construction with choice nodes is over, each primary node + should have fanins that are primary nodes. The primary nodes is the + one that does not have pNode->pRepr set to point to another node.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_ManCheckConsistency( Fraig_Man_t * p ) +{ + Fraig_Node_t * pNode; + Fraig_NodeVec_t * pVec; + int i; + pVec = Fraig_Dfs( p, 0 ); + for ( i = 0; i < pVec->nSize; i++ ) + { + pNode = pVec->pArray[i]; + if ( Fraig_NodeIsVar(pNode) ) + { + if ( pNode->pRepr ) + printf( "Primary input %d is a secondary node.\n", pNode->Num ); + } + else if ( Fraig_NodeIsConst(pNode) ) + { + if ( pNode->pRepr ) + printf( "Constant 1 %d is a secondary node.\n", pNode->Num ); + } + else + { + if ( pNode->pRepr ) + printf( "Internal node %d is a secondary node.\n", pNode->Num ); + if ( Fraig_Regular(pNode->p1)->pRepr ) + printf( "Internal node %d has first fanin %d that is a secondary node.\n", + pNode->Num, Fraig_Regular(pNode->p1)->Num ); + if ( Fraig_Regular(pNode->p2)->pRepr ) + printf( "Internal node %d has second fanin %d that is a secondary node.\n", + pNode->Num, Fraig_Regular(pNode->p2)->Num ); + } + } + Fraig_NodeVecFree( pVec ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Prints the node.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_PrintNode( Fraig_Man_t * p, Fraig_Node_t * pNode ) +{ + Fraig_NodeVec_t * vNodes; + Fraig_Node_t * pTemp; + int fCompl1, fCompl2, i; + + vNodes = Fraig_DfsOne( p, pNode, 0 ); + for ( i = 0; i < vNodes->nSize; i++ ) + { + pTemp = vNodes->pArray[i]; + if ( Fraig_NodeIsVar(pTemp) ) + { + printf( "%3d : PI ", pTemp->Num ); + Fraig_PrintBinary( stdout, (unsigned *)&pTemp->puSimR, 20 ); + printf( " " ); + Fraig_PrintBinary( stdout, (unsigned *)&pTemp->puSimD, 20 ); + printf( " %d\n", pTemp->fInv ); + continue; + } + + fCompl1 = Fraig_IsComplement(pTemp->p1); + fCompl2 = Fraig_IsComplement(pTemp->p2); + printf( "%3d : %c%3d %c%3d ", pTemp->Num, + (fCompl1? '-':'+'), Fraig_Regular(pTemp->p1)->Num, + (fCompl2? '-':'+'), Fraig_Regular(pTemp->p2)->Num ); + Fraig_PrintBinary( stdout, (unsigned *)&pTemp->puSimR, 20 ); + printf( " " ); + Fraig_PrintBinary( stdout, (unsigned *)&pTemp->puSimD, 20 ); + printf( " %d\n", pTemp->fInv ); + } + Fraig_NodeVecFree( vNodes ); +} + +/**Function************************************************************* + + Synopsis [Prints the bit string.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_PrintBinary( FILE * pFile, unsigned * pSign, int nBits ) +{ + int Remainder, nWords; + int w, i; + + Remainder = (nBits%(sizeof(unsigned)*8)); + nWords = (nBits/(sizeof(unsigned)*8)) + (Remainder>0); + + for ( w = nWords-1; w >= 0; w-- ) + for ( i = ((w == nWords-1 && Remainder)? Remainder-1: 31); i >= 0; i-- ) + fprintf( pFile, "%c", '0' + (int)((pSign[w] & (1< 0) ); + +// fprintf( pFile, "\n" ); +} + +/**Function************************************************************* + + Synopsis [Sets up the mask.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_GetMaxLevel( Fraig_Man_t * pMan ) +{ + int nLevelMax, i; + nLevelMax = 0; + for ( i = 0; i < pMan->vOutputs->nSize; i++ ) + nLevelMax = nLevelMax > Fraig_Regular(pMan->vOutputs->pArray[i])->Level? + nLevelMax : Fraig_Regular(pMan->vOutputs->pArray[i])->Level; + return nLevelMax; +} + +/**Function************************************************************* + + Synopsis [Analyses choice nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_MappingUpdateLevel_rec( Fraig_Man_t * pMan, Fraig_Node_t * pNode, int fMaximum ) +{ + Fraig_Node_t * pTemp; + int Level1, Level2, LevelE; + assert( !Fraig_IsComplement(pNode) ); + if ( !Fraig_NodeIsAnd(pNode) ) + return pNode->Level; + // skip the visited node + if ( pNode->TravId == pMan->nTravIds ) + return pNode->Level; + pNode->TravId = pMan->nTravIds; + // compute levels of the children nodes + Level1 = Fraig_MappingUpdateLevel_rec( pMan, Fraig_Regular(pNode->p1), fMaximum ); + Level2 = Fraig_MappingUpdateLevel_rec( pMan, Fraig_Regular(pNode->p2), fMaximum ); + pNode->Level = 1 + FRAIG_MAX( Level1, Level2 ); + if ( pNode->pNextE ) + { + LevelE = Fraig_MappingUpdateLevel_rec( pMan, pNode->pNextE, fMaximum ); + if ( fMaximum ) + { + if ( pNode->Level < LevelE ) + pNode->Level = LevelE; + } + else + { + if ( pNode->Level > LevelE ) + pNode->Level = LevelE; + } + // set the level of all equivalent nodes to be the same minimum + if ( pNode->pRepr == NULL ) // the primary node + for ( pTemp = pNode->pNextE; pTemp; pTemp = pTemp->pNextE ) + pTemp->Level = pNode->Level; + } + return pNode->Level; +} + +/**Function************************************************************* + + Synopsis [Resets the levels of the nodes in the choice graph.] + + Description [Makes the level of the choice nodes to be equal to the + maximum of the level of the nodes in the equivalence class. This way + sorting by level leads to the reverse topological order, which is + needed for the required time computation.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_MappingSetChoiceLevels( Fraig_Man_t * pMan, int fMaximum ) +{ + int i; + pMan->nTravIds++; + for ( i = 0; i < pMan->vOutputs->nSize; i++ ) + Fraig_MappingUpdateLevel_rec( pMan, Fraig_Regular(pMan->vOutputs->pArray[i]), fMaximum ); +} + +/**Function************************************************************* + + Synopsis [Reports statistics on choice nodes.] + + Description [The number of choice nodes is the number of primary nodes, + which has pNextE set to a pointer. The number of choices is the number + of entries in the equivalent-node lists of the primary nodes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_ManReportChoices( Fraig_Man_t * pMan ) +{ + Fraig_Node_t * pNode, * pTemp; + int nChoiceNodes, nChoices; + int i, LevelMax1, LevelMax2; + + // report the number of levels + LevelMax1 = Fraig_GetMaxLevel( pMan ); + Fraig_MappingSetChoiceLevels( pMan, 0 ); + LevelMax2 = Fraig_GetMaxLevel( pMan ); + + // report statistics about choices + nChoiceNodes = nChoices = 0; + for ( i = 0; i < pMan->vNodes->nSize; i++ ) + { + pNode = pMan->vNodes->pArray[i]; + if ( pNode->pRepr == NULL && pNode->pNextE != NULL ) + { // this is a choice node = the primary node that has equivalent nodes + nChoiceNodes++; + for ( pTemp = pNode; pTemp; pTemp = pTemp->pNextE ) + nChoices++; + } + } + printf( "Maximum level: Original = %d. Reduced due to choices = %d.\n", LevelMax1, LevelMax2 ); + printf( "Choice stats: Choice nodes = %d. Total choices = %d.\n", nChoiceNodes, nChoices ); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the node is the root of EXOR/NEXOR gate.] + + Description [The node can be complemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_NodeIsExorType( Fraig_Node_t * pNode ) +{ + Fraig_Node_t * pNode1, * pNode2; + // make the node regular (it does not matter for EXOR/NEXOR) + pNode = Fraig_Regular(pNode); + // if the node or its children are not ANDs or not compl, this cannot be EXOR type + if ( !Fraig_NodeIsAnd(pNode) ) + return 0; + if ( !Fraig_NodeIsAnd(pNode->p1) || !Fraig_IsComplement(pNode->p1) ) + return 0; + if ( !Fraig_NodeIsAnd(pNode->p2) || !Fraig_IsComplement(pNode->p2) ) + return 0; + + // get children + pNode1 = Fraig_Regular(pNode->p1); + pNode2 = Fraig_Regular(pNode->p2); + assert( pNode1->Num < pNode2->Num ); + + // compare grandchildren + return pNode1->p1 == Fraig_Not(pNode2->p1) && pNode1->p2 == Fraig_Not(pNode2->p2); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the node is the root of MUX or EXOR/NEXOR.] + + Description [The node can be complemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_NodeIsMuxType( Fraig_Node_t * pNode ) +{ + Fraig_Node_t * pNode1, * pNode2; + + // make the node regular (it does not matter for EXOR/NEXOR) + pNode = Fraig_Regular(pNode); + // if the node or its children are not ANDs or not compl, this cannot be EXOR type + if ( !Fraig_NodeIsAnd(pNode) ) + return 0; + if ( !Fraig_NodeIsAnd(pNode->p1) || !Fraig_IsComplement(pNode->p1) ) + return 0; + if ( !Fraig_NodeIsAnd(pNode->p2) || !Fraig_IsComplement(pNode->p2) ) + return 0; + + // get children + pNode1 = Fraig_Regular(pNode->p1); + pNode2 = Fraig_Regular(pNode->p2); + assert( pNode1->Num < pNode2->Num ); + + // compare grandchildren + // node is an EXOR/NEXOR + if ( pNode1->p1 == Fraig_Not(pNode2->p1) && pNode1->p2 == Fraig_Not(pNode2->p2) ) + return 1; + + // otherwise the node is MUX iff it has a pair of equal grandchildren + return pNode1->p1 == Fraig_Not(pNode2->p1) || + pNode1->p1 == Fraig_Not(pNode2->p2) || + pNode1->p2 == Fraig_Not(pNode2->p1) || + pNode1->p2 == Fraig_Not(pNode2->p2); +} + +/**Function************************************************************* + + Synopsis [Returns 1 if the node is EXOR, 0 if it is NEXOR.] + + Description [The node should be EXOR type and not complemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_NodeIsExor( Fraig_Node_t * pNode ) +{ + Fraig_Node_t * pNode1; + assert( !Fraig_IsComplement(pNode) ); + assert( Fraig_NodeIsExorType(pNode) ); + assert( Fraig_IsComplement(pNode->p1) ); + // get children + pNode1 = Fraig_Regular(pNode->p1); + return Fraig_IsComplement(pNode1->p1) == Fraig_IsComplement(pNode1->p2); +} + +/**Function************************************************************* + + Synopsis [Recognizes what nodes are control and data inputs of a MUX.] + + Description [If the node is a MUX, returns the control variable C. + Assigns nodes T and E to be the then and else variables of the MUX. + Node C is never complemented. Nodes T and E can be complemented. + This function also recognizes EXOR/NEXOR gates as MUXes.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_Node_t * Fraig_NodeRecognizeMux( Fraig_Node_t * pNode, Fraig_Node_t ** ppNodeT, Fraig_Node_t ** ppNodeE ) +{ + Fraig_Node_t * pNode1, * pNode2; + assert( !Fraig_IsComplement(pNode) ); + assert( Fraig_NodeIsMuxType(pNode) ); + // get children + pNode1 = Fraig_Regular(pNode->p1); + pNode2 = Fraig_Regular(pNode->p2); + // find the control variable + if ( pNode1->p1 == Fraig_Not(pNode2->p1) ) + { + if ( Fraig_IsComplement(pNode1->p1) ) + { // pNode2->p1 is positive phase of C + *ppNodeT = Fraig_Not(pNode2->p2); + *ppNodeE = Fraig_Not(pNode1->p2); + return pNode2->p1; + } + else + { // pNode1->p1 is positive phase of C + *ppNodeT = Fraig_Not(pNode1->p2); + *ppNodeE = Fraig_Not(pNode2->p2); + return pNode1->p1; + } + } + else if ( pNode1->p1 == Fraig_Not(pNode2->p2) ) + { + if ( Fraig_IsComplement(pNode1->p1) ) + { // pNode2->p2 is positive phase of C + *ppNodeT = Fraig_Not(pNode2->p1); + *ppNodeE = Fraig_Not(pNode1->p2); + return pNode2->p2; + } + else + { // pNode1->p1 is positive phase of C + *ppNodeT = Fraig_Not(pNode1->p2); + *ppNodeE = Fraig_Not(pNode2->p1); + return pNode1->p1; + } + } + else if ( pNode1->p2 == Fraig_Not(pNode2->p1) ) + { + if ( Fraig_IsComplement(pNode1->p2) ) + { // pNode2->p1 is positive phase of C + *ppNodeT = Fraig_Not(pNode2->p2); + *ppNodeE = Fraig_Not(pNode1->p1); + return pNode2->p1; + } + else + { // pNode1->p2 is positive phase of C + *ppNodeT = Fraig_Not(pNode1->p1); + *ppNodeE = Fraig_Not(pNode2->p2); + return pNode1->p2; + } + } + else if ( pNode1->p2 == Fraig_Not(pNode2->p2) ) + { + if ( Fraig_IsComplement(pNode1->p2) ) + { // pNode2->p2 is positive phase of C + *ppNodeT = Fraig_Not(pNode2->p1); + *ppNodeE = Fraig_Not(pNode1->p1); + return pNode2->p2; + } + else + { // pNode1->p2 is positive phase of C + *ppNodeT = Fraig_Not(pNode1->p1); + *ppNodeE = Fraig_Not(pNode2->p1); + return pNode1->p2; + } + } + assert( 0 ); // this is not MUX + return NULL; +} + +/**Function************************************************************* + + Synopsis [Counts the number of EXOR type nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_ManCountExors( Fraig_Man_t * pMan ) +{ + int i, nExors; + nExors = 0; + for ( i = 0; i < pMan->vNodes->nSize; i++ ) + nExors += Fraig_NodeIsExorType( pMan->vNodes->pArray[i] ); + return nExors; + +} + +/**Function************************************************************* + + Synopsis [Counts the number of EXOR type nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_ManCountMuxes( Fraig_Man_t * pMan ) +{ + int i, nMuxes; + nMuxes = 0; + for ( i = 0; i < pMan->vNodes->nSize; i++ ) + nMuxes += Fraig_NodeIsMuxType( pMan->vNodes->pArray[i] ); + return nMuxes; + +} + +/**Function************************************************************* + + Synopsis [Returns 1 if siminfo of Node1 is contained in siminfo of Node2.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_NodeSimsContained( Fraig_Man_t * pMan, Fraig_Node_t * pNode1, Fraig_Node_t * pNode2 ) +{ + unsigned * pUnsigned1, * pUnsigned2; + int i; + + // compare random siminfo + pUnsigned1 = pNode1->puSimR; + pUnsigned2 = pNode2->puSimR; + for ( i = 0; i < pMan->nWordsRand; i++ ) + if ( pUnsigned1[i] & ~pUnsigned2[i] ) + return 0; + + // compare systematic siminfo + pUnsigned1 = pNode1->puSimD; + pUnsigned2 = pNode2->puSimD; + for ( i = 0; i < pMan->iWordStart; i++ ) + if ( pUnsigned1[i] & ~pUnsigned2[i] ) + return 0; + + return 1; +} + +/**Function************************************************************* + + Synopsis [Count the number of PI variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_CountPis( Fraig_Man_t * p, Msat_IntVec_t * vVarNums ) +{ + int * pVars, nVars, i, Counter; + + nVars = Msat_IntVecReadSize(vVarNums); + pVars = Msat_IntVecReadArray(vVarNums); + Counter = 0; + for ( i = 0; i < nVars; i++ ) + Counter += Fraig_NodeIsVar( p->vNodes->pArray[pVars[i]] ); + return Counter; +} + + + +/**Function************************************************************* + + Synopsis [Counts the number of EXOR type nodes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_ManPrintRefs( Fraig_Man_t * pMan ) +{ + Fraig_NodeVec_t * vPivots; + Fraig_Node_t * pNode, * pNode2; + int i, k, Counter, nProved; + int clk; + + vPivots = Fraig_NodeVecAlloc( 1000 ); + for ( i = 0; i < pMan->vNodes->nSize; i++ ) + { + pNode = pMan->vNodes->pArray[i]; + + if ( pNode->nOnes == 0 || pNode->nOnes == (unsigned)pMan->nWordsRand * 32 ) + continue; + + if ( pNode->nRefs > 5 ) + { + Fraig_NodeVecPush( vPivots, pNode ); +// printf( "Node %6d : nRefs = %2d Level = %3d.\n", pNode->Num, pNode->nRefs, pNode->Level ); + } + } + printf( "Total nodes = %d. Referenced nodes = %d.\n", pMan->vNodes->nSize, vPivots->nSize ); + +clk = clock(); + // count implications + Counter = nProved = 0; + for ( i = 0; i < vPivots->nSize; i++ ) + for ( k = i+1; k < vPivots->nSize; k++ ) + { + pNode = vPivots->pArray[i]; + pNode2 = vPivots->pArray[k]; + if ( Fraig_NodeSimsContained( pMan, pNode, pNode2 ) ) + { + if ( Fraig_NodeIsImplication( pMan, pNode, pNode2, -1 ) ) + nProved++; + Counter++; + } + else if ( Fraig_NodeSimsContained( pMan, pNode2, pNode ) ) + { + if ( Fraig_NodeIsImplication( pMan, pNode2, pNode, -1 ) ) + nProved++; + Counter++; + } + } + printf( "Number of candidate pairs = %d. Proved = %d.\n", Counter, nProved ); +PRT( "Time", clock() - clk ); + return 0; +} + + +/**Function************************************************************* + + Synopsis [Checks if pNew exists among the implication fanins of pOld.] + + Description [If pNew is an implication fanin of pOld, returns 1. + If Fraig_Not(pNew) is an implication fanin of pOld, return -1. + Otherwise returns 0.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_NodeIsInSupergate( Fraig_Node_t * pOld, Fraig_Node_t * pNew ) +{ + int RetValue1, RetValue2; + if ( Fraig_Regular(pOld) == Fraig_Regular(pNew) ) + return (pOld == pNew)? 1 : -1; + if ( Fraig_IsComplement(pOld) || Fraig_NodeIsVar(pOld) ) + return 0; + RetValue1 = Fraig_NodeIsInSupergate( pOld->p1, pNew ); + RetValue2 = Fraig_NodeIsInSupergate( pOld->p2, pNew ); + if ( RetValue1 == -1 || RetValue2 == -1 ) + return -1; + if ( RetValue1 == 1 || RetValue2 == 1 ) + return 1; + return 0; +} + + +/**Function************************************************************* + + Synopsis [Returns the array of nodes to be combined into one multi-input AND-gate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_CollectSupergate_rec( Fraig_Node_t * pNode, Fraig_NodeVec_t * vSuper, int fFirst, int fStopAtMux ) +{ + // if the new node is complemented or a PI, another gate begins +// if ( Fraig_IsComplement(pNode) || Fraig_NodeIsVar(pNode) || Fraig_NodeIsMuxType(pNode) ) + if ( (!fFirst && Fraig_Regular(pNode)->nRefs > 1) || + Fraig_IsComplement(pNode) || Fraig_NodeIsVar(pNode) || + (fStopAtMux && Fraig_NodeIsMuxType(pNode)) ) + { + Fraig_NodeVecPushUnique( vSuper, pNode ); + return; + } + // go through the branches + Fraig_CollectSupergate_rec( pNode->p1, vSuper, 0, fStopAtMux ); + Fraig_CollectSupergate_rec( pNode->p2, vSuper, 0, fStopAtMux ); +} + +/**Function************************************************************* + + Synopsis [Returns the array of nodes to be combined into one multi-input AND-gate.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_NodeVec_t * Fraig_CollectSupergate( Fraig_Node_t * pNode, int fStopAtMux ) +{ + Fraig_NodeVec_t * vSuper; + vSuper = Fraig_NodeVecAlloc( 8 ); + Fraig_CollectSupergate_rec( pNode, vSuper, 1, fStopAtMux ); + return vSuper; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_ManIncrementTravId( Fraig_Man_t * pMan ) +{ + pMan->nTravIds2++; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_NodeSetTravIdCurrent( Fraig_Man_t * pMan, Fraig_Node_t * pNode ) +{ + pNode->TravId2 = pMan->nTravIds2; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_NodeIsTravIdCurrent( Fraig_Man_t * pMan, Fraig_Node_t * pNode ) +{ + return pNode->TravId2 == pMan->nTravIds2; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_NodeIsTravIdPrevious( Fraig_Man_t * pMan, Fraig_Node_t * pNode ) +{ + return pNode->TravId2 == pMan->nTravIds2 - 1; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/fraig/fraigVec.c b/abc_with_bb_support/src/sat/fraig/fraigVec.c new file mode 100644 index 000000000..e0a989a7f --- /dev/null +++ b/abc_with_bb_support/src/sat/fraig/fraigVec.c @@ -0,0 +1,545 @@ +/**CFile**************************************************************** + + FileName [fraigVec.c] + + PackageName [FRAIG: Functionally reduced AND-INV graphs.] + + Synopsis [Vector of FRAIG nodes.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 2.0. Started - October 1, 2004] + + Revision [$Id: fraigVec.c,v 1.7 2005/07/08 01:01:34 alanmi Exp $] + +***********************************************************************/ + +#include "fraigInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_NodeVec_t * Fraig_NodeVecAlloc( int nCap ) +{ + Fraig_NodeVec_t * p; + p = ALLOC( Fraig_NodeVec_t, 1 ); + if ( nCap > 0 && nCap < 8 ) + nCap = 8; + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ALLOC( Fraig_Node_t *, p->nCap ) : NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_NodeVecFree( Fraig_NodeVec_t * p ) +{ + FREE( p->pArray ); + FREE( p ); +} + +/**Function************************************************************* + + Synopsis [Duplicates the integer array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_NodeVec_t * Fraig_NodeVecDup( Fraig_NodeVec_t * pVec ) +{ + Fraig_NodeVec_t * p; + p = ALLOC( Fraig_NodeVec_t, 1 ); + p->nSize = pVec->nSize; + p->nCap = pVec->nCap; + p->pArray = p->nCap? ALLOC( Fraig_Node_t *, p->nCap ) : NULL; + memcpy( p->pArray, pVec->pArray, sizeof(Fraig_Node_t *) * pVec->nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_Node_t ** Fraig_NodeVecReadArray( Fraig_NodeVec_t * p ) +{ + return p->pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_NodeVecReadSize( Fraig_NodeVec_t * p ) +{ + return p->nSize; +} + +/**Function************************************************************* + + Synopsis [Resizes the vector to the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_NodeVecGrow( Fraig_NodeVec_t * p, int nCapMin ) +{ + if ( p->nCap >= nCapMin ) + return; + p->pArray = REALLOC( Fraig_Node_t *, p->pArray, nCapMin ); + p->nCap = nCapMin; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_NodeVecShrink( Fraig_NodeVec_t * p, int nSizeNew ) +{ + assert( p->nSize >= nSizeNew ); + p->nSize = nSizeNew; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_NodeVecClear( Fraig_NodeVec_t * p ) +{ + p->nSize = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_NodeVecPush( Fraig_NodeVec_t * p, Fraig_Node_t * Entry ) +{ + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Fraig_NodeVecGrow( p, 16 ); + else + Fraig_NodeVecGrow( p, 2 * p->nCap ); + } + p->pArray[p->nSize++] = Entry; +} + +/**Function************************************************************* + + Synopsis [Add the element while ensuring uniqueness.] + + Description [Returns 1 if the element was found, and 0 if it was new. ] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_NodeVecPushUnique( Fraig_NodeVec_t * p, Fraig_Node_t * Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return 1; + Fraig_NodeVecPush( p, Entry ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Inserts a new node in the order by arrival times.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_NodeVecPushOrder( Fraig_NodeVec_t * p, Fraig_Node_t * pNode ) +{ + Fraig_Node_t * pNode1, * pNode2; + int i; + Fraig_NodeVecPush( p, pNode ); + // find the p of the node + for ( i = p->nSize-1; i > 0; i-- ) + { + pNode1 = p->pArray[i ]; + pNode2 = p->pArray[i-1]; + if ( pNode1 >= pNode2 ) + break; + p->pArray[i ] = pNode2; + p->pArray[i-1] = pNode1; + } +} + +/**Function************************************************************* + + Synopsis [Add the element while ensuring uniqueness in the order.] + + Description [Returns 1 if the element was found, and 0 if it was new. ] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_NodeVecPushUniqueOrder( Fraig_NodeVec_t * p, Fraig_Node_t * pNode ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == pNode ) + return 1; + Fraig_NodeVecPushOrder( p, pNode ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Inserts a new node in the order by arrival times.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_NodeVecPushOrderByLevel( Fraig_NodeVec_t * p, Fraig_Node_t * pNode ) +{ + Fraig_Node_t * pNode1, * pNode2; + int i; + Fraig_NodeVecPush( p, pNode ); + // find the p of the node + for ( i = p->nSize-1; i > 0; i-- ) + { + pNode1 = p->pArray[i ]; + pNode2 = p->pArray[i-1]; + if ( Fraig_Regular(pNode1)->Level <= Fraig_Regular(pNode2)->Level ) + break; + p->pArray[i ] = pNode2; + p->pArray[i-1] = pNode1; + } +} + +/**Function************************************************************* + + Synopsis [Add the element while ensuring uniqueness in the order.] + + Description [Returns 1 if the element was found, and 0 if it was new. ] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_NodeVecPushUniqueOrderByLevel( Fraig_NodeVec_t * p, Fraig_Node_t * pNode ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == pNode ) + return 1; + Fraig_NodeVecPushOrderByLevel( p, pNode ); + return 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_Node_t * Fraig_NodeVecPop( Fraig_NodeVec_t * p ) +{ + return p->pArray[--p->nSize]; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_NodeVecRemove( Fraig_NodeVec_t * p, Fraig_Node_t * Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + break; + assert( i < p->nSize ); + for ( i++; i < p->nSize; i++ ) + p->pArray[i-1] = p->pArray[i]; + p->nSize--; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_NodeVecWriteEntry( Fraig_NodeVec_t * p, int i, Fraig_Node_t * Entry ) +{ + assert( i >= 0 && i < p->nSize ); + p->pArray[i] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Fraig_Node_t * Fraig_NodeVecReadEntry( Fraig_NodeVec_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray[i]; +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two clauses.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_NodeVecCompareLevelsIncreasing( Fraig_Node_t ** pp1, Fraig_Node_t ** pp2 ) +{ + int Level1 = Fraig_Regular(*pp1)->Level; + int Level2 = Fraig_Regular(*pp2)->Level; + if ( Level1 < Level2 ) + return -1; + if ( Level1 > Level2 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two clauses.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_NodeVecCompareLevelsDecreasing( Fraig_Node_t ** pp1, Fraig_Node_t ** pp2 ) +{ + int Level1 = Fraig_Regular(*pp1)->Level; + int Level2 = Fraig_Regular(*pp2)->Level; + if ( Level1 > Level2 ) + return -1; + if ( Level1 < Level2 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two clauses.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_NodeVecCompareNumbers( Fraig_Node_t ** pp1, Fraig_Node_t ** pp2 ) +{ + int Num1 = Fraig_Regular(*pp1)->Num; + int Num2 = Fraig_Regular(*pp2)->Num; + if ( Num1 < Num2 ) + return -1; + if ( Num1 > Num2 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two clauses.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Fraig_NodeVecCompareRefCounts( Fraig_Node_t ** pp1, Fraig_Node_t ** pp2 ) +{ + int nRefs1 = Fraig_Regular(*pp1)->nRefs; + int nRefs2 = Fraig_Regular(*pp2)->nRefs; + + if ( nRefs1 < nRefs2 ) + return -1; + if ( nRefs1 > nRefs2 ) + return 1; + + nRefs1 = Fraig_Regular(*pp1)->Level; + nRefs2 = Fraig_Regular(*pp2)->Level; + + if ( nRefs1 < nRefs2 ) + return -1; + if ( nRefs1 > nRefs2 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Sorting the entries by their integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_NodeVecSortByLevel( Fraig_NodeVec_t * p, int fIncreasing ) +{ + if ( fIncreasing ) + qsort( (void *)p->pArray, p->nSize, sizeof(Fraig_Node_t *), + (int (*)(const void *, const void *)) Fraig_NodeVecCompareLevelsIncreasing ); + else + qsort( (void *)p->pArray, p->nSize, sizeof(Fraig_Node_t *), + (int (*)(const void *, const void *)) Fraig_NodeVecCompareLevelsDecreasing ); +} + +/**Function************************************************************* + + Synopsis [Sorting the entries by their integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_NodeVecSortByNumber( Fraig_NodeVec_t * p ) +{ + qsort( (void *)p->pArray, p->nSize, sizeof(Fraig_Node_t *), + (int (*)(const void *, const void *)) Fraig_NodeVecCompareNumbers ); +} + +/**Function************************************************************* + + Synopsis [Sorting the entries by their integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Fraig_NodeVecSortByRefCount( Fraig_NodeVec_t * p ) +{ + qsort( (void *)p->pArray, p->nSize, sizeof(Fraig_Node_t *), + (int (*)(const void *, const void *)) Fraig_NodeVecCompareRefCounts ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/sat/fraig/module.make b/abc_with_bb_support/src/sat/fraig/module.make new file mode 100644 index 000000000..8d977acb7 --- /dev/null +++ b/abc_with_bb_support/src/sat/fraig/module.make @@ -0,0 +1,12 @@ +SRC += src/sat/fraig/fraigApi.c \ + src/sat/fraig/fraigCanon.c \ + src/sat/fraig/fraigFanout.c \ + src/sat/fraig/fraigFeed.c \ + src/sat/fraig/fraigMan.c \ + src/sat/fraig/fraigMem.c \ + src/sat/fraig/fraigNode.c \ + src/sat/fraig/fraigPrime.c \ + src/sat/fraig/fraigSat.c \ + src/sat/fraig/fraigTable.c \ + src/sat/fraig/fraigUtil.c \ + src/sat/fraig/fraigVec.c diff --git a/abc_with_bb_support/src/sat/msat/module.make b/abc_with_bb_support/src/sat/msat/module.make new file mode 100644 index 000000000..2a2bc142d --- /dev/null +++ b/abc_with_bb_support/src/sat/msat/module.make @@ -0,0 +1,13 @@ +SRC += src/sat/msat/msatActivity.c \ + src/sat/msat/msatClause.c \ + src/sat/msat/msatClauseVec.c \ + src/sat/msat/msatMem.c \ + src/sat/msat/msatOrderJ.c \ + src/sat/msat/msatQueue.c \ + src/sat/msat/msatRead.c \ + src/sat/msat/msatSolverApi.c \ + src/sat/msat/msatSolverCore.c \ + src/sat/msat/msatSolverIo.c \ + src/sat/msat/msatSolverSearch.c \ + src/sat/msat/msatSort.c \ + src/sat/msat/msatVec.c diff --git a/abc_with_bb_support/src/sat/msat/msat.h b/abc_with_bb_support/src/sat/msat/msat.h new file mode 100644 index 000000000..1c4301ec5 --- /dev/null +++ b/abc_with_bb_support/src/sat/msat/msat.h @@ -0,0 +1,172 @@ +/**CFile**************************************************************** + + FileName [msat.h] + + PackageName [A C version of SAT solver MINISAT, originally developed + in C++ by Niklas Een and Niklas Sorensson, Chalmers University of + Technology, Sweden: http://www.cs.chalmers.se/~een/Satzoo.] + + Synopsis [External definitions of the solver.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 1, 2004.] + + Revision [$Id: msat.h,v 1.6 2004/05/12 06:30:20 satrajit Exp $] + +***********************************************************************/ + +#ifndef __MSAT_H__ +#define __MSAT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#ifdef bool +#undef bool +#endif + +#ifndef __MVTYPES_H__ +typedef int bool; +#endif + +typedef struct Msat_Solver_t_ Msat_Solver_t; + +// the vector of intergers and of clauses +typedef struct Msat_IntVec_t_ Msat_IntVec_t; +typedef struct Msat_ClauseVec_t_ Msat_ClauseVec_t; +typedef struct Msat_VarHeap_t_ Msat_VarHeap_t; + +// the return value of the solver +typedef enum { MSAT_FALSE = -1, MSAT_UNKNOWN = 0, MSAT_TRUE = 1 } Msat_Type_t; + +// representation of variables and literals +// the literal (l) is the variable (v) and the sign (s) +// s = 0 the variable is positive +// s = 1 the variable is negative +#define MSAT_VAR2LIT(v,s) (2*(v)+(s)) +#define MSAT_LITNOT(l) ((l)^1) +#define MSAT_LITSIGN(l) ((l)&1) +#define MSAT_LIT2VAR(l) ((l)>>1) + +//////////////////////////////////////////////////////////////////////// +/// GLOBAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== satRead.c ============================================================*/ +extern bool Msat_SolverParseDimacs( FILE * pFile, Msat_Solver_t ** p, int fVerbose ); +/*=== satSolver.c ===========================================================*/ +// adding vars, clauses, simplifying the database, and solving +extern bool Msat_SolverAddVar( Msat_Solver_t * p, int Level ); +extern bool Msat_SolverAddClause( Msat_Solver_t * p, Msat_IntVec_t * pLits ); +extern bool Msat_SolverSimplifyDB( Msat_Solver_t * p ); +extern bool Msat_SolverSolve( Msat_Solver_t * p, Msat_IntVec_t * pVecAssumps, int nBackTrackLimit, int nTimeLimit ); +// printing stats, assignments, and clauses +extern void Msat_SolverPrintStats( Msat_Solver_t * p ); +extern void Msat_SolverPrintAssignment( Msat_Solver_t * p ); +extern void Msat_SolverPrintClauses( Msat_Solver_t * p ); +extern void Msat_SolverWriteDimacs( Msat_Solver_t * p, char * pFileName ); +// access to the solver internal data +extern int Msat_SolverReadVarNum( Msat_Solver_t * p ); +extern int Msat_SolverReadClauseNum( Msat_Solver_t * p ); +extern int Msat_SolverReadVarAllocNum( Msat_Solver_t * p ); +extern int * Msat_SolverReadAssignsArray( Msat_Solver_t * p ); +extern int * Msat_SolverReadModelArray( Msat_Solver_t * p ); +extern unsigned Msat_SolverReadTruth( Msat_Solver_t * p ); +extern int Msat_SolverReadBackTracks( Msat_Solver_t * p ); +extern int Msat_SolverReadInspects( Msat_Solver_t * p ); +extern void Msat_SolverSetVerbosity( Msat_Solver_t * p, int fVerbose ); +extern void Msat_SolverSetProofWriting( Msat_Solver_t * p, int fProof ); +extern void Msat_SolverSetVarTypeA( Msat_Solver_t * p, int Var ); +extern void Msat_SolverSetVarMap( Msat_Solver_t * p, Msat_IntVec_t * vVarMap ); +extern void Msat_SolverMarkLastClauseTypeA( Msat_Solver_t * p ); +extern void Msat_SolverMarkClausesStart( Msat_Solver_t * p ); +extern float * Msat_SolverReadFactors( Msat_Solver_t * p ); +// returns the solution after incremental solving +extern int Msat_SolverReadSolutions( Msat_Solver_t * p ); +extern int * Msat_SolverReadSolutionsArray( Msat_Solver_t * p ); +extern Msat_ClauseVec_t * Msat_SolverReadAdjacents( Msat_Solver_t * p ); +extern Msat_IntVec_t * Msat_SolverReadConeVars( Msat_Solver_t * p ); +extern Msat_IntVec_t * Msat_SolverReadVarsUsed( Msat_Solver_t * p ); +/*=== satSolverSearch.c ===========================================================*/ +extern void Msat_SolverRemoveLearned( Msat_Solver_t * p ); +extern void Msat_SolverRemoveMarked( Msat_Solver_t * p ); +/*=== satSolverApi.c ===========================================================*/ +// allocation, cleaning, and freeing the solver +extern Msat_Solver_t * Msat_SolverAlloc( int nVars, double dClaInc, double dClaDecay, double dVarInc, double dVarDecay, bool fVerbose ); +extern void Msat_SolverResize( Msat_Solver_t * pMan, int nVarsAlloc ); +extern void Msat_SolverClean( Msat_Solver_t * p, int nVars ); +extern void Msat_SolverPrepare( Msat_Solver_t * pSat, Msat_IntVec_t * vVars ); +extern void Msat_SolverFree( Msat_Solver_t * p ); +/*=== satVec.c ===========================================================*/ +extern Msat_IntVec_t * Msat_IntVecAlloc( int nCap ); +extern Msat_IntVec_t * Msat_IntVecAllocArray( int * pArray, int nSize ); +extern Msat_IntVec_t * Msat_IntVecAllocArrayCopy( int * pArray, int nSize ); +extern Msat_IntVec_t * Msat_IntVecDup( Msat_IntVec_t * pVec ); +extern Msat_IntVec_t * Msat_IntVecDupArray( Msat_IntVec_t * pVec ); +extern void Msat_IntVecFree( Msat_IntVec_t * p ); +extern void Msat_IntVecFill( Msat_IntVec_t * p, int nSize, int Entry ); +extern int * Msat_IntVecReleaseArray( Msat_IntVec_t * p ); +extern int * Msat_IntVecReadArray( Msat_IntVec_t * p ); +extern int Msat_IntVecReadSize( Msat_IntVec_t * p ); +extern int Msat_IntVecReadEntry( Msat_IntVec_t * p, int i ); +extern int Msat_IntVecReadEntryLast( Msat_IntVec_t * p ); +extern void Msat_IntVecWriteEntry( Msat_IntVec_t * p, int i, int Entry ); +extern void Msat_IntVecGrow( Msat_IntVec_t * p, int nCapMin ); +extern void Msat_IntVecShrink( Msat_IntVec_t * p, int nSizeNew ); +extern void Msat_IntVecClear( Msat_IntVec_t * p ); +extern void Msat_IntVecPush( Msat_IntVec_t * p, int Entry ); +extern int Msat_IntVecPushUnique( Msat_IntVec_t * p, int Entry ); +extern void Msat_IntVecPushUniqueOrder( Msat_IntVec_t * p, int Entry, int fIncrease ); +extern int Msat_IntVecPop( Msat_IntVec_t * p ); +extern void Msat_IntVecSort( Msat_IntVec_t * p, int fReverse ); +/*=== satHeap.c ===========================================================*/ +extern Msat_VarHeap_t * Msat_VarHeapAlloc(); +extern void Msat_VarHeapSetActivity( Msat_VarHeap_t * p, double * pActivity ); +extern void Msat_VarHeapStart( Msat_VarHeap_t * p, int * pVars, int nVars, int nVarsAlloc ); +extern void Msat_VarHeapGrow( Msat_VarHeap_t * p, int nSize ); +extern void Msat_VarHeapStop( Msat_VarHeap_t * p ); +extern void Msat_VarHeapPrint( FILE * pFile, Msat_VarHeap_t * p ); +extern void Msat_VarHeapCheck( Msat_VarHeap_t * p ); +extern void Msat_VarHeapCheckOne( Msat_VarHeap_t * p, int iVar ); +extern int Msat_VarHeapContainsVar( Msat_VarHeap_t * p, int iVar ); +extern void Msat_VarHeapInsert( Msat_VarHeap_t * p, int iVar ); +extern void Msat_VarHeapUpdate( Msat_VarHeap_t * p, int iVar ); +extern void Msat_VarHeapDelete( Msat_VarHeap_t * p, int iVar ); +extern double Msat_VarHeapReadMaxWeight( Msat_VarHeap_t * p ); +extern int Msat_VarHeapCountNodes( Msat_VarHeap_t * p, double WeightLimit ); +extern int Msat_VarHeapReadMax( Msat_VarHeap_t * p ); +extern int Msat_VarHeapGetMax( Msat_VarHeap_t * p ); + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/abc_with_bb_support/src/sat/msat/msatActivity.c b/abc_with_bb_support/src/sat/msat/msatActivity.c new file mode 100644 index 000000000..471a4059b --- /dev/null +++ b/abc_with_bb_support/src/sat/msat/msatActivity.c @@ -0,0 +1,160 @@ +/**CFile**************************************************************** + + FileName [msatActivity.c] + + PackageName [A C version of SAT solver MINISAT, originally developed + in C++ by Niklas Een and Niklas Sorensson, Chalmers University of + Technology, Sweden: http://www.cs.chalmers.se/~een/Satzoo.] + + Synopsis [Procedures controlling activity of variables and clauses.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 1, 2004.] + + Revision [$Id: msatActivity.c,v 1.0 2004/01/01 1:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "msatInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverVarBumpActivity( Msat_Solver_t * p, Msat_Lit_t Lit ) +{ + Msat_Var_t Var; + if ( p->dVarDecay < 0 ) // (negative decay means static variable order -- don't bump) + return; + Var = MSAT_LIT2VAR(Lit); + p->pdActivity[Var] += p->dVarInc; +// p->pdActivity[Var] += p->dVarInc * p->pFactors[Var]; + if ( p->pdActivity[Var] > 1e100 ) + Msat_SolverVarRescaleActivity( p ); + Msat_OrderUpdate( p->pOrder, Var ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverVarDecayActivity( Msat_Solver_t * p ) +{ + if ( p->dVarDecay >= 0 ) + p->dVarInc *= p->dVarDecay; +} + +/**Function************************************************************* + + Synopsis [Divide all variable activities by 1e100.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverVarRescaleActivity( Msat_Solver_t * p ) +{ + int i; + for ( i = 0; i < p->nVars; i++ ) + p->pdActivity[i] *= 1e-100; + p->dVarInc *= 1e-100; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverClaBumpActivity( Msat_Solver_t * p, Msat_Clause_t * pC ) +{ + float Activ; + Activ = Msat_ClauseReadActivity(pC); + if ( Activ + p->dClaInc > 1e20 ) + { + Msat_SolverClaRescaleActivity( p ); + Activ = Msat_ClauseReadActivity( pC ); + } + Msat_ClauseWriteActivity( pC, Activ + (float)p->dClaInc ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverClaDecayActivity( Msat_Solver_t * p ) +{ + p->dClaInc *= p->dClaDecay; +} + +/**Function************************************************************* + + Synopsis [Divide all constraint activities by 1e20.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverClaRescaleActivity( Msat_Solver_t * p ) +{ + Msat_Clause_t ** pLearned; + int nLearned, i; + float Activ; + nLearned = Msat_ClauseVecReadSize( p->vLearned ); + pLearned = Msat_ClauseVecReadArray( p->vLearned ); + for ( i = 0; i < nLearned; i++ ) + { + Activ = Msat_ClauseReadActivity( pLearned[i] ); + Msat_ClauseWriteActivity( pLearned[i], Activ * (float)1e-20 ); + } + p->dClaInc *= 1e-20; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/msat/msatClause.c b/abc_with_bb_support/src/sat/msat/msatClause.c new file mode 100644 index 000000000..309134227 --- /dev/null +++ b/abc_with_bb_support/src/sat/msat/msatClause.c @@ -0,0 +1,529 @@ +/**CFile**************************************************************** + + FileName [msatClause.c] + + PackageName [A C version of SAT solver MINISAT, originally developed + in C++ by Niklas Een and Niklas Sorensson, Chalmers University of + Technology, Sweden: http://www.cs.chalmers.se/~een/Satzoo.] + + Synopsis [Procedures working with SAT clauses.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 1, 2004.] + + Revision [$Id: msatClause.c,v 1.0 2004/01/01 1:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "msatInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +struct Msat_Clause_t_ +{ + int Num; // unique number of the clause + unsigned fLearned : 1; // 1 if the clause is learned + unsigned fMark : 1; // used to mark visited clauses during proof recording + unsigned fTypeA : 1; // used to mark clauses belonging to A for interpolant computation + unsigned nSize : 14; // the number of literals in the clause + unsigned nSizeAlloc : 15; // the number of bytes allocated for the clause + Msat_Lit_t pData[0]; +}; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Creates a new clause.] + + Description [Returns FALSE if top-level conflict detected (must be handled); + TRUE otherwise. 'pClause_out' may be set to NULL if clause is already + satisfied by the top-level assignment.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Msat_ClauseCreate( Msat_Solver_t * p, Msat_IntVec_t * vLits, bool fLearned, Msat_Clause_t ** pClause_out ) +{ + int * pAssigns = Msat_SolverReadAssignsArray(p); + Msat_ClauseVec_t ** pvWatched; + Msat_Clause_t * pC; + int * pLits; + int nLits, i, j; + int nBytes; + Msat_Var_t Var; + bool Sign; + + *pClause_out = NULL; + + nLits = Msat_IntVecReadSize(vLits); + pLits = Msat_IntVecReadArray(vLits); + + if ( !fLearned ) + { + int * pSeen = Msat_SolverReadSeenArray( p ); + int nSeenId; + assert( Msat_SolverReadDecisionLevel(p) == 0 ); + // sorting literals makes the code trace-equivalent + // with to the original C++ solver + Msat_IntVecSort( vLits, 0 ); + // increment the counter of seen twice + nSeenId = Msat_SolverIncrementSeenId( p ); + nSeenId = Msat_SolverIncrementSeenId( p ); + // nSeenId - 1 stands for negative + // nSeenId stands for positive + // Remove false literals + + // there is a bug here!!!! + // when the same var in opposite polarities is given, it drops one polarity!!! + + for ( i = j = 0; i < nLits; i++ ) { + // get the corresponding variable + Var = MSAT_LIT2VAR(pLits[i]); + Sign = MSAT_LITSIGN(pLits[i]); // Sign=0 for positive + // check if we already saw this variable in the this clause + if ( pSeen[Var] >= nSeenId - 1 ) + { + if ( (pSeen[Var] != nSeenId) == Sign ) // the same lit + continue; + return 1; // two opposite polarity lits -- don't add the clause + } + // mark the variable as seen + pSeen[Var] = nSeenId - !Sign; + + // analize the value of this literal + if ( pAssigns[Var] != MSAT_VAR_UNASSIGNED ) + { + if ( pAssigns[Var] == pLits[i] ) + return 1; // the clause is always true -- don't add anything + // the literal has no impact - skip it + continue; + } + // otherwise, add this literal to the clause + pLits[j++] = pLits[i]; + } + Msat_IntVecShrink( vLits, j ); + nLits = j; +/* + // the problem with this code is that performance is very + // sensitive to the ordering of adjacency lits + // the best ordering requires fanins first, next fanouts + // this ordering is more convenient to make from FRAIG + + // create the adjacency information + if ( nLits > 2 ) + { + Msat_Var_t VarI, VarJ; + Msat_IntVec_t * pAdjI, * pAdjJ; + + for ( i = 0; i < nLits; i++ ) + { + VarI = MSAT_LIT2VAR(pLits[i]); + pAdjI = (Msat_IntVec_t *)p->vAdjacents->pArray[VarI]; + + for ( j = i+1; j < nLits; j++ ) + { + VarJ = MSAT_LIT2VAR(pLits[j]); + pAdjJ = (Msat_IntVec_t *)p->vAdjacents->pArray[VarJ]; + + Msat_IntVecPushUniqueOrder( pAdjI, VarJ, 1 ); + Msat_IntVecPushUniqueOrder( pAdjJ, VarI, 1 ); + } + } + } +*/ + } + // 'vLits' is now the (possibly) reduced vector of literals. + if ( nLits == 0 ) + return 0; + if ( nLits == 1 ) + return Msat_SolverEnqueue( p, pLits[0], NULL ); + + // Allocate clause: +// nBytes = sizeof(unsigned)*(nLits + 1 + (int)fLearned); + nBytes = sizeof(unsigned)*(nLits + 2 + (int)fLearned); +#ifdef USE_SYSTEM_MEMORY_MANAGEMENT + pC = (Msat_Clause_t *)ALLOC( char, nBytes ); +#else + pC = (Msat_Clause_t *)Msat_MmStepEntryFetch( Msat_SolverReadMem(p), nBytes ); +#endif + pC->Num = p->nClauses++; + pC->fTypeA = 0; + pC->fMark = 0; + pC->fLearned = fLearned; + pC->nSize = nLits; + pC->nSizeAlloc = nBytes; + memcpy( pC->pData, pLits, sizeof(int)*nLits ); + + // For learnt clauses only: + if ( fLearned ) + { + int * pLevel = Msat_SolverReadDecisionLevelArray( p ); + int iLevelMax, iLevelCur, iLitMax; + + // Put the second watch on the literal with highest decision level: + iLitMax = 1; + iLevelMax = pLevel[ MSAT_LIT2VAR(pLits[1]) ]; + for ( i = 2; i < nLits; i++ ) + { + iLevelCur = pLevel[ MSAT_LIT2VAR(pLits[i]) ]; + assert( iLevelCur != -1 ); + if ( iLevelMax < iLevelCur ) + // this is very strange - shouldn't it be??? + // if ( iLevelMax > iLevelCur ) + iLevelMax = iLevelCur, iLitMax = i; + } + pC->pData[1] = pLits[iLitMax]; + pC->pData[iLitMax] = pLits[1]; + + // Bumping: + // (newly learnt clauses should be considered active) + Msat_ClauseWriteActivity( pC, 0.0 ); + Msat_SolverClaBumpActivity( p, pC ); +// if ( nLits < 20 ) + for ( i = 0; i < nLits; i++ ) + { + Msat_SolverVarBumpActivity( p, pLits[i] ); +// Msat_SolverVarBumpActivity( p, pLits[i] ); +// p->pFreq[ MSAT_LIT2VAR(pLits[i]) ]++; + } + } + + // Store clause: + pvWatched = Msat_SolverReadWatchedArray( p ); + Msat_ClauseVecPush( pvWatched[ MSAT_LITNOT(pC->pData[0]) ], pC ); + Msat_ClauseVecPush( pvWatched[ MSAT_LITNOT(pC->pData[1]) ], pC ); + *pClause_out = pC; + return 1; +} + +/**Function************************************************************* + + Synopsis [Deallocates the clause.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_ClauseFree( Msat_Solver_t * p, Msat_Clause_t * pC, bool fRemoveWatched ) +{ + if ( fRemoveWatched ) + { + Msat_Lit_t Lit; + Msat_ClauseVec_t ** pvWatched; + pvWatched = Msat_SolverReadWatchedArray( p ); + Lit = MSAT_LITNOT( pC->pData[0] ); + Msat_ClauseRemoveWatch( pvWatched[Lit], pC ); + Lit = MSAT_LITNOT( pC->pData[1] ); + Msat_ClauseRemoveWatch( pvWatched[Lit], pC ); + } + +#ifdef USE_SYSTEM_MEMORY_MANAGEMENT + free( pC ); +#else + Msat_MmStepEntryRecycle( Msat_SolverReadMem(p), (char *)pC, pC->nSizeAlloc ); +#endif + +} + +/**Function************************************************************* + + Synopsis [Access the data field of the clause.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Msat_ClauseReadLearned( Msat_Clause_t * pC ) { return pC->fLearned; } +int Msat_ClauseReadSize( Msat_Clause_t * pC ) { return pC->nSize; } +int * Msat_ClauseReadLits( Msat_Clause_t * pC ) { return pC->pData; } +bool Msat_ClauseReadMark( Msat_Clause_t * pC ) { return pC->fMark; } +int Msat_ClauseReadNum( Msat_Clause_t * pC ) { return pC->Num; } +bool Msat_ClauseReadTypeA( Msat_Clause_t * pC ) { return pC->fTypeA; } + +void Msat_ClauseSetMark( Msat_Clause_t * pC, bool fMark ) { pC->fMark = fMark; } +void Msat_ClauseSetNum( Msat_Clause_t * pC, int Num ) { pC->Num = Num; } +void Msat_ClauseSetTypeA( Msat_Clause_t * pC, bool fTypeA ) { pC->fTypeA = fTypeA; } + +/**Function************************************************************* + + Synopsis [Checks whether the learned clause is locked.] + + Description [The clause may be locked if it is the reason of a + recent conflict. Such clause cannot be removed from the database.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Msat_ClauseIsLocked( Msat_Solver_t * p, Msat_Clause_t * pC ) +{ + Msat_Clause_t ** pReasons = Msat_SolverReadReasonArray( p ); + return (bool)(pReasons[MSAT_LIT2VAR(pC->pData[0])] == pC); +} + +/**Function************************************************************* + + Synopsis [Reads the activity of the given clause.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +float Msat_ClauseReadActivity( Msat_Clause_t * pC ) +{ + return *((float *)(pC->pData + pC->nSize)); +} + +/**Function************************************************************* + + Synopsis [Sets the activity of the clause.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_ClauseWriteActivity( Msat_Clause_t * pC, float Num ) +{ + *((float *)(pC->pData + pC->nSize)) = Num; +} + +/**Function************************************************************* + + Synopsis [Propages the assignment.] + + Description [The literal that has become true (Lit) is given to this + procedure. The array of current variable assignments is given for + efficiency. The output literal (pLit_out) can be the second watched + literal (if TRUE is returned) or the conflict literal (if FALSE is + returned). This messy interface is used to improve performance. + This procedure accounts for ~50% of the runtime of the solver.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Msat_ClausePropagate( Msat_Clause_t * pC, Msat_Lit_t Lit, int * pAssigns, Msat_Lit_t * pLit_out ) +{ + // make sure the false literal is pC->pData[1] + Msat_Lit_t LitF = MSAT_LITNOT(Lit); + if ( pC->pData[0] == LitF ) + pC->pData[0] = pC->pData[1], pC->pData[1] = LitF; + assert( pC->pData[1] == LitF ); + // if the 0-th watch is true, clause is already satisfied + if ( pAssigns[MSAT_LIT2VAR(pC->pData[0])] == pC->pData[0] ) + return 1; + // look for a new watch + if ( pC->nSize > 2 ) + { + int i; + for ( i = 2; i < (int)pC->nSize; i++ ) + if ( pAssigns[MSAT_LIT2VAR(pC->pData[i])] != MSAT_LITNOT(pC->pData[i]) ) + { + pC->pData[1] = pC->pData[i], pC->pData[i] = LitF; + *pLit_out = MSAT_LITNOT(pC->pData[1]); + return 1; + } + } + // clause is unit under assignment + *pLit_out = pC->pData[0]; + return 0; +} + +/**Function************************************************************* + + Synopsis [Simplifies the clause.] + + Description [Assumes everything has been propagated! (esp. watches + in clauses are NOT unsatisfied)] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Msat_ClauseSimplify( Msat_Clause_t * pC, int * pAssigns ) +{ + Msat_Var_t Var; + int i, j; + for ( i = j = 0; i < (int)pC->nSize; i++ ) + { + Var = MSAT_LIT2VAR(pC->pData[i]); + if ( pAssigns[Var] == MSAT_VAR_UNASSIGNED ) + { + pC->pData[j++] = pC->pData[i]; + continue; + } + if ( pAssigns[Var] == pC->pData[i] ) + return 1; + // otherwise, the value of the literal is false + // make sure, this literal is not watched + assert( i >= 2 ); + } + // if the size has changed, update it and move activity + if ( j < (int)pC->nSize ) + { + float Activ = Msat_ClauseReadActivity(pC); + pC->nSize = j; + Msat_ClauseWriteActivity(pC, Activ); + } + return 0; +} + +/**Function************************************************************* + + Synopsis [Computes reason of conflict in the given clause.] + + Description [If the literal is unassigned, finds the reason by + complementing literals in the given cluase (pC). If the literal is + assigned, makes sure that this literal is the first one in the clause + and computes the complement of all other literals in the clause. + Returns the reason in the given array (vLits_out). If the clause is + learned, bumps its activity.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_ClauseCalcReason( Msat_Solver_t * p, Msat_Clause_t * pC, Msat_Lit_t Lit, Msat_IntVec_t * vLits_out ) +{ + int i; + // clear the reason + Msat_IntVecClear( vLits_out ); + assert( Lit == MSAT_LIT_UNASSIGNED || Lit == pC->pData[0] ); + for ( i = (Lit != MSAT_LIT_UNASSIGNED); i < (int)pC->nSize; i++ ) + { + assert( Msat_SolverReadAssignsArray(p)[MSAT_LIT2VAR(pC->pData[i])] == MSAT_LITNOT(pC->pData[i]) ); + Msat_IntVecPush( vLits_out, MSAT_LITNOT(pC->pData[i]) ); + } + if ( pC->fLearned ) + Msat_SolverClaBumpActivity( p, pC ); +} + +/**Function************************************************************* + + Synopsis [Removes the given clause from the watched list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_ClauseRemoveWatch( Msat_ClauseVec_t * vClauses, Msat_Clause_t * pC ) +{ + Msat_Clause_t ** pClauses; + int nClauses, i; + nClauses = Msat_ClauseVecReadSize( vClauses ); + pClauses = Msat_ClauseVecReadArray( vClauses ); + for ( i = 0; pClauses[i] != pC; i++ ) + assert( i < nClauses ); + for ( ; i < nClauses - 1; i++ ) + pClauses[i] = pClauses[i+1]; + Msat_ClauseVecPop( vClauses ); +} + +/**Function************************************************************* + + Synopsis [Prints the given clause.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_ClausePrint( Msat_Clause_t * pC ) +{ + int i; + if ( pC == NULL ) + printf( "NULL pointer" ); + else + { + if ( pC->fLearned ) + printf( "Act = %.4f ", Msat_ClauseReadActivity(pC) ); + for ( i = 0; i < (int)pC->nSize; i++ ) + printf( " %s%d", ((pC->pData[i]&1)? "-": ""), pC->pData[i]/2 + 1 ); + } + printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [Writes the given clause in a file in DIMACS format.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_ClauseWriteDimacs( FILE * pFile, Msat_Clause_t * pC, bool fIncrement ) +{ + int i; + for ( i = 0; i < (int)pC->nSize; i++ ) + fprintf( pFile, "%s%d ", ((pC->pData[i]&1)? "-": ""), pC->pData[i]/2 + (int)(fIncrement>0) ); + if ( fIncrement ) + fprintf( pFile, "0" ); + fprintf( pFile, "\n" ); +} + +/**Function************************************************************* + + Synopsis [Prints the given clause.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_ClausePrintSymbols( Msat_Clause_t * pC ) +{ + int i; + if ( pC == NULL ) + printf( "NULL pointer" ); + else + { +// if ( pC->fLearned ) +// printf( "Act = %.4f ", Msat_ClauseReadActivity(pC) ); + for ( i = 0; i < (int)pC->nSize; i++ ) + printf(" "L_LIT, L_lit(pC->pData[i])); + } + printf( "\n" ); +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/msat/msatClauseVec.c b/abc_with_bb_support/src/sat/msat/msatClauseVec.c new file mode 100644 index 000000000..957c1ca41 --- /dev/null +++ b/abc_with_bb_support/src/sat/msat/msatClauseVec.c @@ -0,0 +1,232 @@ +/**CFile**************************************************************** + + FileName [msatClauseVec.c] + + PackageName [A C version of SAT solver MINISAT, originally developed + in C++ by Niklas Een and Niklas Sorensson, Chalmers University of + Technology, Sweden: http://www.cs.chalmers.se/~een/Satzoo.] + + Synopsis [Procedures working with arrays of SAT clauses.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 1, 2004.] + + Revision [$Id: msatClauseVec.c,v 1.0 2004/01/01 1:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "msatInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_ClauseVec_t * Msat_ClauseVecAlloc( int nCap ) +{ + Msat_ClauseVec_t * p; + p = ALLOC( Msat_ClauseVec_t, 1 ); + if ( nCap > 0 && nCap < 16 ) + nCap = 16; + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ALLOC( Msat_Clause_t *, p->nCap ) : NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_ClauseVecFree( Msat_ClauseVec_t * p ) +{ + FREE( p->pArray ); + FREE( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_Clause_t ** Msat_ClauseVecReadArray( Msat_ClauseVec_t * p ) +{ + return p->pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Msat_ClauseVecReadSize( Msat_ClauseVec_t * p ) +{ + return p->nSize; +} + +/**Function************************************************************* + + Synopsis [Resizes the vector to the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_ClauseVecGrow( Msat_ClauseVec_t * p, int nCapMin ) +{ + if ( p->nCap >= nCapMin ) + return; + p->pArray = REALLOC( Msat_Clause_t *, p->pArray, nCapMin ); + p->nCap = nCapMin; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_ClauseVecShrink( Msat_ClauseVec_t * p, int nSizeNew ) +{ + assert( p->nSize >= nSizeNew ); + p->nSize = nSizeNew; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_ClauseVecClear( Msat_ClauseVec_t * p ) +{ + p->nSize = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_ClauseVecPush( Msat_ClauseVec_t * p, Msat_Clause_t * Entry ) +{ + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Msat_ClauseVecGrow( p, 16 ); + else + Msat_ClauseVecGrow( p, 2 * p->nCap ); + } + p->pArray[p->nSize++] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_Clause_t * Msat_ClauseVecPop( Msat_ClauseVec_t * p ) +{ + return p->pArray[--p->nSize]; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_ClauseVecWriteEntry( Msat_ClauseVec_t * p, int i, Msat_Clause_t * Entry ) +{ + assert( i >= 0 && i < p->nSize ); + p->pArray[i] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_Clause_t * Msat_ClauseVecReadEntry( Msat_ClauseVec_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray[i]; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/msat/msatInt.h b/abc_with_bb_support/src/sat/msat/msatInt.h new file mode 100644 index 000000000..f14fa0223 --- /dev/null +++ b/abc_with_bb_support/src/sat/msat/msatInt.h @@ -0,0 +1,306 @@ +/**CFile**************************************************************** + + FileName [msatInt.c] + + PackageName [A C version of SAT solver MINISAT, originally developed + in C++ by Niklas Een and Niklas Sorensson, Chalmers University of + Technology, Sweden: http://www.cs.chalmers.se/~een/Satzoo.] + + Synopsis [Internal definitions of the solver.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 1, 2004.] + + Revision [$Id: msatInt.c,v 1.0 2004/01/01 1:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __MSAT_INT_H__ +#define __MSAT_INT_H__ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//#include "leaks.h" +#include +#include +#include +#include +#include +#include +#include "msat.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#ifdef _MSC_VER +typedef __int64 int64; +#else +typedef long long int64; +#endif + +// outputs the runtime in seconds +#define PRT(a,t) \ + printf( "%s = ", (a) ); printf( "%6.2f sec\n", (float)(t)/(float)(CLOCKS_PER_SEC) ) + +// memory management macros +#define ALLOC(type, num) \ + ((type *) malloc(sizeof(type) * (num))) +#define REALLOC(type, obj, num) \ + ((obj) ? ((type *) realloc((char *)(obj), sizeof(type) * (num))) : \ + ((type *) malloc(sizeof(type) * (num)))) +#define FREE(obj) \ + ((obj) ? (free((char *)(obj)), (obj) = 0) : 0) + +// By default, custom memory management is used +// which guarantees constant time allocation/deallocation +// for SAT clauses and other frequently modified objects. +// For debugging, it is possible use system memory management +// directly. In which case, uncomment the macro below. +//#define USE_SYSTEM_MEMORY_MANAGEMENT + +// internal data structures +typedef struct Msat_Clause_t_ Msat_Clause_t; +typedef struct Msat_Queue_t_ Msat_Queue_t; +typedef struct Msat_Order_t_ Msat_Order_t; +// memory managers (duplicated from Extra for stand-aloneness) +typedef struct Msat_MmFixed_t_ Msat_MmFixed_t; +typedef struct Msat_MmFlex_t_ Msat_MmFlex_t; +typedef struct Msat_MmStep_t_ Msat_MmStep_t; +// variables and literals +typedef int Msat_Lit_t; +typedef int Msat_Var_t; +// the type of return value +#define MSAT_VAR_UNASSIGNED (-1) +#define MSAT_LIT_UNASSIGNED (-2) +#define MSAT_ORDER_UNKNOWN (-3) + +// printing the search tree +#define L_IND "%-*d" +#define L_ind Msat_SolverReadDecisionLevel(p)*3+3,Msat_SolverReadDecisionLevel(p) +#define L_LIT "%s%d" +#define L_lit(Lit) MSAT_LITSIGN(Lit)?"-":"", MSAT_LIT2VAR(Lit)+1 + +typedef struct Msat_SolverStats_t_ Msat_SolverStats_t; +struct Msat_SolverStats_t_ +{ + int64 nStarts; // the number of restarts + int64 nDecisions; // the number of decisions + int64 nPropagations; // the number of implications + int64 nInspects; // the number of times clauses are vising while watching them + int64 nConflicts; // the number of conflicts + int64 nSuccesses; // the number of sat assignments found +}; + +typedef struct Msat_SearchParams_t_ Msat_SearchParams_t; +struct Msat_SearchParams_t_ +{ + double dVarDecay; + double dClaDecay; +}; + +// sat solver data structure visible through all the internal files +struct Msat_Solver_t_ +{ + int nClauses; // the total number of clauses + int nClausesStart; // the number of clauses before adding + Msat_ClauseVec_t * vClauses; // problem clauses + Msat_ClauseVec_t * vLearned; // learned clauses + double dClaInc; // Amount to bump next clause with. + double dClaDecay; // INVERSE decay factor for clause activity: stores 1/decay. + + double * pdActivity; // A heuristic measurement of the activity of a variable. + float * pFactors; // the multiplicative factors of variable activity + double dVarInc; // Amount to bump next variable with. + double dVarDecay; // INVERSE decay factor for variable activity: stores 1/decay. Use negative value for static variable order. + Msat_Order_t * pOrder; // Keeps track of the decision variable order. + + Msat_ClauseVec_t ** pvWatched; // 'pvWatched[lit]' is a list of constraints watching 'lit' (will go there if literal becomes true). + Msat_Queue_t * pQueue; // Propagation queue. + + int nVars; // the current number of variables + int nVarsAlloc; // the maximum allowed number of variables + int * pAssigns; // The current assignments (literals or MSAT_VAR_UNKOWN) + int * pModel; // The satisfying assignment + Msat_IntVec_t * vTrail; // List of assignments made. + Msat_IntVec_t * vTrailLim; // Separator indices for different decision levels in 'trail'. + Msat_Clause_t ** pReasons; // 'reason[var]' is the clause that implied the variables current value, or 'NULL' if none. + int * pLevel; // 'level[var]' is the decision level at which assignment was made. + int nLevelRoot; // Level of first proper decision. + + double dRandSeed; // For the internal random number generator (makes solver deterministic over different platforms). + + bool fVerbose; // the verbosity flag + double dProgress; // Set by 'search()'. + + // the variable cone and variable connectivity + Msat_IntVec_t * vConeVars; + Msat_IntVec_t * vVarsUsed; + Msat_ClauseVec_t * vAdjacents; + + // internal data used during conflict analysis + int * pSeen; // time when a lit was seen for the last time + int nSeenId; // the id of current seeing + Msat_IntVec_t * vReason; // the temporary array of literals + Msat_IntVec_t * vTemp; // the temporary array of literals + int * pFreq; // the number of times each var participated in conflicts + + // the memory manager + Msat_MmStep_t * pMem; + + // statistics + Msat_SolverStats_t Stats; + int nTwoLits; + int nTwoLitsL; + int nClausesInit; + int nClausesAlloc; + int nClausesAllocL; + int nBackTracks; +}; + +struct Msat_ClauseVec_t_ +{ + Msat_Clause_t ** pArray; + int nSize; + int nCap; +}; + +struct Msat_IntVec_t_ +{ + int * pArray; + int nSize; + int nCap; +}; + +//////////////////////////////////////////////////////////////////////// +/// GLOBAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== satActivity.c ===========================================================*/ +extern void Msat_SolverVarDecayActivity( Msat_Solver_t * p ); +extern void Msat_SolverVarRescaleActivity( Msat_Solver_t * p ); +extern void Msat_SolverClaDecayActivity( Msat_Solver_t * p ); +extern void Msat_SolverClaRescaleActivity( Msat_Solver_t * p ); +/*=== satSolverApi.c ===========================================================*/ +extern Msat_Clause_t * Msat_SolverReadClause( Msat_Solver_t * p, int Num ); +/*=== satSolver.c ===========================================================*/ +extern int Msat_SolverReadDecisionLevel( Msat_Solver_t * p ); +extern int * Msat_SolverReadDecisionLevelArray( Msat_Solver_t * p ); +extern Msat_Clause_t ** Msat_SolverReadReasonArray( Msat_Solver_t * p ); +extern Msat_Type_t Msat_SolverReadVarValue( Msat_Solver_t * p, Msat_Var_t Var ); +extern Msat_ClauseVec_t * Msat_SolverReadLearned( Msat_Solver_t * p ); +extern Msat_ClauseVec_t ** Msat_SolverReadWatchedArray( Msat_Solver_t * p ); +extern int * Msat_SolverReadSeenArray( Msat_Solver_t * p ); +extern int Msat_SolverIncrementSeenId( Msat_Solver_t * p ); +extern Msat_MmStep_t * Msat_SolverReadMem( Msat_Solver_t * p ); +extern void Msat_SolverClausesIncrement( Msat_Solver_t * p ); +extern void Msat_SolverClausesDecrement( Msat_Solver_t * p ); +extern void Msat_SolverClausesIncrementL( Msat_Solver_t * p ); +extern void Msat_SolverClausesDecrementL( Msat_Solver_t * p ); +extern void Msat_SolverVarBumpActivity( Msat_Solver_t * p, Msat_Lit_t Lit ); +extern void Msat_SolverClaBumpActivity( Msat_Solver_t * p, Msat_Clause_t * pC ); +extern bool Msat_SolverEnqueue( Msat_Solver_t * p, Msat_Lit_t Lit, Msat_Clause_t * pC ); +extern double Msat_SolverProgressEstimate( Msat_Solver_t * p ); +/*=== satSolverSearch.c ===========================================================*/ +extern bool Msat_SolverAssume( Msat_Solver_t * p, Msat_Lit_t Lit ); +extern Msat_Clause_t * Msat_SolverPropagate( Msat_Solver_t * p ); +extern void Msat_SolverCancelUntil( Msat_Solver_t * p, int Level ); +extern Msat_Type_t Msat_SolverSearch( Msat_Solver_t * p, int nConfLimit, int nLearnedLimit, int nBackTrackLimit, Msat_SearchParams_t * pPars ); +/*=== satQueue.c ===========================================================*/ +extern Msat_Queue_t * Msat_QueueAlloc( int nVars ); +extern void Msat_QueueFree( Msat_Queue_t * p ); +extern int Msat_QueueReadSize( Msat_Queue_t * p ); +extern void Msat_QueueInsert( Msat_Queue_t * p, int Lit ); +extern int Msat_QueueExtract( Msat_Queue_t * p ); +extern void Msat_QueueClear( Msat_Queue_t * p ); +/*=== satOrder.c ===========================================================*/ +extern Msat_Order_t * Msat_OrderAlloc( Msat_Solver_t * pSat ); +extern void Msat_OrderSetBounds( Msat_Order_t * p, int nVarsMax ); +extern void Msat_OrderClean( Msat_Order_t * p, Msat_IntVec_t * vCone ); +extern int Msat_OrderCheck( Msat_Order_t * p ); +extern void Msat_OrderFree( Msat_Order_t * p ); +extern int Msat_OrderVarSelect( Msat_Order_t * p ); +extern void Msat_OrderVarAssigned( Msat_Order_t * p, int Var ); +extern void Msat_OrderVarUnassigned( Msat_Order_t * p, int Var ); +extern void Msat_OrderUpdate( Msat_Order_t * p, int Var ); +/*=== satClause.c ===========================================================*/ +extern bool Msat_ClauseCreate( Msat_Solver_t * p, Msat_IntVec_t * vLits, bool fLearnt, Msat_Clause_t ** pClause_out ); +extern Msat_Clause_t * Msat_ClauseCreateFake( Msat_Solver_t * p, Msat_IntVec_t * vLits ); +extern Msat_Clause_t * Msat_ClauseCreateFakeLit( Msat_Solver_t * p, Msat_Lit_t Lit ); +extern bool Msat_ClauseReadLearned( Msat_Clause_t * pC ); +extern int Msat_ClauseReadSize( Msat_Clause_t * pC ); +extern int * Msat_ClauseReadLits( Msat_Clause_t * pC ); +extern bool Msat_ClauseReadMark( Msat_Clause_t * pC ); +extern void Msat_ClauseSetMark( Msat_Clause_t * pC, bool fMark ); +extern int Msat_ClauseReadNum( Msat_Clause_t * pC ); +extern void Msat_ClauseSetNum( Msat_Clause_t * pC, int Num ); +extern bool Msat_ClauseReadTypeA( Msat_Clause_t * pC ); +extern void Msat_ClauseSetTypeA( Msat_Clause_t * pC, bool fTypeA ); +extern bool Msat_ClauseIsLocked( Msat_Solver_t * p, Msat_Clause_t * pC ); +extern float Msat_ClauseReadActivity( Msat_Clause_t * pC ); +extern void Msat_ClauseWriteActivity( Msat_Clause_t * pC, float Num ); +extern void Msat_ClauseFree( Msat_Solver_t * p, Msat_Clause_t * pC, bool fRemoveWatched ); +extern bool Msat_ClausePropagate( Msat_Clause_t * pC, Msat_Lit_t Lit, int * pAssigns, Msat_Lit_t * pLit_out ); +extern bool Msat_ClauseSimplify( Msat_Clause_t * pC, int * pAssigns ); +extern void Msat_ClauseCalcReason( Msat_Solver_t * p, Msat_Clause_t * pC, Msat_Lit_t Lit, Msat_IntVec_t * vLits_out ); +extern void Msat_ClauseRemoveWatch( Msat_ClauseVec_t * vClauses, Msat_Clause_t * pC ); +extern void Msat_ClausePrint( Msat_Clause_t * pC ); +extern void Msat_ClausePrintSymbols( Msat_Clause_t * pC ); +extern void Msat_ClauseWriteDimacs( FILE * pFile, Msat_Clause_t * pC, bool fIncrement ); +extern unsigned Msat_ClauseComputeTruth( Msat_Solver_t * p, Msat_Clause_t * pC ); +/*=== satSort.c ===========================================================*/ +extern void Msat_SolverSortDB( Msat_Solver_t * p ); +/*=== satClauseVec.c ===========================================================*/ +extern Msat_ClauseVec_t * Msat_ClauseVecAlloc( int nCap ); +extern void Msat_ClauseVecFree( Msat_ClauseVec_t * p ); +extern Msat_Clause_t ** Msat_ClauseVecReadArray( Msat_ClauseVec_t * p ); +extern int Msat_ClauseVecReadSize( Msat_ClauseVec_t * p ); +extern void Msat_ClauseVecGrow( Msat_ClauseVec_t * p, int nCapMin ); +extern void Msat_ClauseVecShrink( Msat_ClauseVec_t * p, int nSizeNew ); +extern void Msat_ClauseVecClear( Msat_ClauseVec_t * p ); +extern void Msat_ClauseVecPush( Msat_ClauseVec_t * p, Msat_Clause_t * Entry ); +extern Msat_Clause_t * Msat_ClauseVecPop( Msat_ClauseVec_t * p ); +extern void Msat_ClauseVecWriteEntry( Msat_ClauseVec_t * p, int i, Msat_Clause_t * Entry ); +extern Msat_Clause_t * Msat_ClauseVecReadEntry( Msat_ClauseVec_t * p, int i ); + +/*=== satMem.c ===========================================================*/ +// fixed-size-block memory manager +extern Msat_MmFixed_t * Msat_MmFixedStart( int nEntrySize ); +extern void Msat_MmFixedStop( Msat_MmFixed_t * p, int fVerbose ); +extern char * Msat_MmFixedEntryFetch( Msat_MmFixed_t * p ); +extern void Msat_MmFixedEntryRecycle( Msat_MmFixed_t * p, char * pEntry ); +extern void Msat_MmFixedRestart( Msat_MmFixed_t * p ); +extern int Msat_MmFixedReadMemUsage( Msat_MmFixed_t * p ); +// flexible-size-block memory manager +extern Msat_MmFlex_t * Msat_MmFlexStart(); +extern void Msat_MmFlexStop( Msat_MmFlex_t * p, int fVerbose ); +extern char * Msat_MmFlexEntryFetch( Msat_MmFlex_t * p, int nBytes ); +extern int Msat_MmFlexReadMemUsage( Msat_MmFlex_t * p ); +// hierarchical memory manager +extern Msat_MmStep_t * Msat_MmStepStart( int nSteps ); +extern void Msat_MmStepStop( Msat_MmStep_t * p, int fVerbose ); +extern char * Msat_MmStepEntryFetch( Msat_MmStep_t * p, int nBytes ); +extern void Msat_MmStepEntryRecycle( Msat_MmStep_t * p, char * pEntry, int nBytes ); +extern int Msat_MmStepReadMemUsage( Msat_MmStep_t * p ); + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// +#endif diff --git a/abc_with_bb_support/src/sat/msat/msatMem.c b/abc_with_bb_support/src/sat/msat/msatMem.c new file mode 100644 index 000000000..d5f0ab0c3 --- /dev/null +++ b/abc_with_bb_support/src/sat/msat/msatMem.c @@ -0,0 +1,529 @@ +/**CFile**************************************************************** + + FileName [msatMem.c] + + PackageName [A C version of SAT solver MINISAT, originally developed + in C++ by Niklas Een and Niklas Sorensson, Chalmers University of + Technology, Sweden: http://www.cs.chalmers.se/~een/Satzoo.] + + Synopsis [Memory managers borrowed from Extra.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 1, 2004.] + + Revision [$Id: msatMem.c,v 1.0 2004/01/01 1:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "msatInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +struct Msat_MmFixed_t_ +{ + // information about individual entries + int nEntrySize; // the size of one entry + int nEntriesAlloc; // the total number of entries allocated + int nEntriesUsed; // the number of entries in use + int nEntriesMax; // the max number of entries in use + char * pEntriesFree; // the linked list of free entries + + // this is where the memory is stored + int nChunkSize; // the size of one chunk + int nChunksAlloc; // the maximum number of memory chunks + int nChunks; // the current number of memory chunks + char ** pChunks; // the allocated memory + + // statistics + int nMemoryUsed; // memory used in the allocated entries + int nMemoryAlloc; // memory allocated +}; + +struct Msat_MmFlex_t_ +{ + // information about individual entries + int nEntriesUsed; // the number of entries allocated + char * pCurrent; // the current pointer to free memory + char * pEnd; // the first entry outside the free memory + + // this is where the memory is stored + int nChunkSize; // the size of one chunk + int nChunksAlloc; // the maximum number of memory chunks + int nChunks; // the current number of memory chunks + char ** pChunks; // the allocated memory + + // statistics + int nMemoryUsed; // memory used in the allocated entries + int nMemoryAlloc; // memory allocated +}; + + +struct Msat_MmStep_t_ +{ + int nMems; // the number of fixed memory managers employed + Msat_MmFixed_t ** pMems; // memory managers: 2^1 words, 2^2 words, etc + int nMapSize; // the size of the memory array + Msat_MmFixed_t ** pMap; // maps the number of bytes into its memory manager +}; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates memory pieces of fixed size.] + + Description [The size of the chunk is computed as the minimum of + 1024 entries and 64K. Can only work with entry size at least 4 byte long.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_MmFixed_t * Msat_MmFixedStart( int nEntrySize ) +{ + Msat_MmFixed_t * p; + + p = ALLOC( Msat_MmFixed_t, 1 ); + memset( p, 0, sizeof(Msat_MmFixed_t) ); + + p->nEntrySize = nEntrySize; + p->nEntriesAlloc = 0; + p->nEntriesUsed = 0; + p->pEntriesFree = NULL; + + if ( nEntrySize * (1 << 10) < (1<<16) ) + p->nChunkSize = (1 << 10); + else + p->nChunkSize = (1<<16) / nEntrySize; + if ( p->nChunkSize < 8 ) + p->nChunkSize = 8; + + p->nChunksAlloc = 64; + p->nChunks = 0; + p->pChunks = ALLOC( char *, p->nChunksAlloc ); + + p->nMemoryUsed = 0; + p->nMemoryAlloc = 0; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_MmFixedStop( Msat_MmFixed_t * p, int fVerbose ) +{ + int i; + if ( p == NULL ) + return; + if ( fVerbose ) + { + printf( "Fixed memory manager: Entry = %5d. Chunk = %5d. Chunks used = %5d.\n", + p->nEntrySize, p->nChunkSize, p->nChunks ); + printf( " Entries used = %8d. Entries peak = %8d. Memory used = %8d. Memory alloc = %8d.\n", + p->nEntriesUsed, p->nEntriesMax, p->nEntrySize * p->nEntriesUsed, p->nMemoryAlloc ); + } + for ( i = 0; i < p->nChunks; i++ ) + free( p->pChunks[i] ); + free( p->pChunks ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Msat_MmFixedEntryFetch( Msat_MmFixed_t * p ) +{ + char * pTemp; + int i; + + // check if there are still free entries + if ( p->nEntriesUsed == p->nEntriesAlloc ) + { // need to allocate more entries + assert( p->pEntriesFree == NULL ); + if ( p->nChunks == p->nChunksAlloc ) + { + p->nChunksAlloc *= 2; + p->pChunks = REALLOC( char *, p->pChunks, p->nChunksAlloc ); + } + p->pEntriesFree = ALLOC( char, p->nEntrySize * p->nChunkSize ); + p->nMemoryAlloc += p->nEntrySize * p->nChunkSize; + // transform these entries into a linked list + pTemp = p->pEntriesFree; + for ( i = 1; i < p->nChunkSize; i++ ) + { + *((char **)pTemp) = pTemp + p->nEntrySize; + pTemp += p->nEntrySize; + } + // set the last link + *((char **)pTemp) = NULL; + // add the chunk to the chunk storage + p->pChunks[ p->nChunks++ ] = p->pEntriesFree; + // add to the number of entries allocated + p->nEntriesAlloc += p->nChunkSize; + } + // incrememt the counter of used entries + p->nEntriesUsed++; + if ( p->nEntriesMax < p->nEntriesUsed ) + p->nEntriesMax = p->nEntriesUsed; + // return the first entry in the free entry list + pTemp = p->pEntriesFree; + p->pEntriesFree = *((char **)pTemp); + return pTemp; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_MmFixedEntryRecycle( Msat_MmFixed_t * p, char * pEntry ) +{ + // decrement the counter of used entries + p->nEntriesUsed--; + // add the entry to the linked list of free entries + *((char **)pEntry) = p->pEntriesFree; + p->pEntriesFree = pEntry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [Relocates all the memory except the first chunk.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_MmFixedRestart( Msat_MmFixed_t * p ) +{ + int i; + char * pTemp; + + // deallocate all chunks except the first one + for ( i = 1; i < p->nChunks; i++ ) + free( p->pChunks[i] ); + p->nChunks = 1; + // transform these entries into a linked list + pTemp = p->pChunks[0]; + for ( i = 1; i < p->nChunkSize; i++ ) + { + *((char **)pTemp) = pTemp + p->nEntrySize; + pTemp += p->nEntrySize; + } + // set the last link + *((char **)pTemp) = NULL; + // set the free entry list + p->pEntriesFree = p->pChunks[0]; + // set the correct statistics + p->nMemoryAlloc = p->nEntrySize * p->nChunkSize; + p->nMemoryUsed = 0; + p->nEntriesAlloc = p->nChunkSize; + p->nEntriesUsed = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Msat_MmFixedReadMemUsage( Msat_MmFixed_t * p ) +{ + return p->nMemoryAlloc; +} + + + +/**Function************************************************************* + + Synopsis [Allocates entries of flexible size.] + + Description [Can only work with entry size at least 4 byte long.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_MmFlex_t * Msat_MmFlexStart() +{ + Msat_MmFlex_t * p; + + p = ALLOC( Msat_MmFlex_t, 1 ); + memset( p, 0, sizeof(Msat_MmFlex_t) ); + + p->nEntriesUsed = 0; + p->pCurrent = NULL; + p->pEnd = NULL; + + p->nChunkSize = (1 << 12); + p->nChunksAlloc = 64; + p->nChunks = 0; + p->pChunks = ALLOC( char *, p->nChunksAlloc ); + + p->nMemoryUsed = 0; + p->nMemoryAlloc = 0; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_MmFlexStop( Msat_MmFlex_t * p, int fVerbose ) +{ + int i; + if ( p == NULL ) + return; + if ( fVerbose ) + { + printf( "Flexible memory manager: Chunk size = %d. Chunks used = %d.\n", + p->nChunkSize, p->nChunks ); + printf( " Entries used = %d. Memory used = %d. Memory alloc = %d.\n", + p->nEntriesUsed, p->nMemoryUsed, p->nMemoryAlloc ); + } + for ( i = 0; i < p->nChunks; i++ ) + free( p->pChunks[i] ); + free( p->pChunks ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Msat_MmFlexEntryFetch( Msat_MmFlex_t * p, int nBytes ) +{ + char * pTemp; + // check if there are still free entries + if ( p->pCurrent == NULL || p->pCurrent + nBytes > p->pEnd ) + { // need to allocate more entries + if ( p->nChunks == p->nChunksAlloc ) + { + p->nChunksAlloc *= 2; + p->pChunks = REALLOC( char *, p->pChunks, p->nChunksAlloc ); + } + if ( nBytes > p->nChunkSize ) + { + // resize the chunk size if more memory is requested than it can give + // (ideally, this should never happen) + p->nChunkSize = 2 * nBytes; + } + p->pCurrent = ALLOC( char, p->nChunkSize ); + p->pEnd = p->pCurrent + p->nChunkSize; + p->nMemoryAlloc += p->nChunkSize; + // add the chunk to the chunk storage + p->pChunks[ p->nChunks++ ] = p->pCurrent; + } + assert( p->pCurrent + nBytes <= p->pEnd ); + // increment the counter of used entries + p->nEntriesUsed++; + // keep track of the memory used + p->nMemoryUsed += nBytes; + // return the next entry + pTemp = p->pCurrent; + p->pCurrent += nBytes; + return pTemp; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Msat_MmFlexReadMemUsage( Msat_MmFlex_t * p ) +{ + return p->nMemoryAlloc; +} + + + + + +/**Function************************************************************* + + Synopsis [Starts the hierarchical memory manager.] + + Description [This manager can allocate entries of any size. + Iternally they are mapped into the entries with the number of bytes + equal to the power of 2. The smallest entry size is 8 bytes. The + next one is 16 bytes etc. So, if the user requests 6 bytes, he gets + 8 byte entry. If we asks for 25 bytes, he gets 32 byte entry etc. + The input parameters "nSteps" says how many fixed memory managers + are employed internally. Calling this procedure with nSteps equal + to 10 results in 10 hierarchically arranged internal memory managers, + which can allocate up to 4096 (1Kb) entries. Requests for larger + entries are handed over to malloc() and then free()ed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_MmStep_t * Msat_MmStepStart( int nSteps ) +{ + Msat_MmStep_t * p; + int i, k; + p = ALLOC( Msat_MmStep_t, 1 ); + p->nMems = nSteps; + // start the fixed memory managers + p->pMems = ALLOC( Msat_MmFixed_t *, p->nMems ); + for ( i = 0; i < p->nMems; i++ ) + p->pMems[i] = Msat_MmFixedStart( (8<nMapSize = (4<nMems); + p->pMap = ALLOC( Msat_MmFixed_t *, p->nMapSize+1 ); + p->pMap[0] = NULL; + for ( k = 1; k <= 4; k++ ) + p->pMap[k] = p->pMems[0]; + for ( i = 0; i < p->nMems; i++ ) + for ( k = (4<pMap[k] = p->pMems[i]; +//for ( i = 1; i < 100; i ++ ) +//printf( "%10d: size = %10d\n", i, p->pMap[i]->nEntrySize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Stops the memory manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_MmStepStop( Msat_MmStep_t * p, int fVerbose ) +{ + int i; + for ( i = 0; i < p->nMems; i++ ) + Msat_MmFixedStop( p->pMems[i], fVerbose ); + free( p->pMems ); + free( p->pMap ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Creates the entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Msat_MmStepEntryFetch( Msat_MmStep_t * p, int nBytes ) +{ + if ( nBytes == 0 ) + return NULL; + if ( nBytes > p->nMapSize ) + { +// printf( "Allocating %d bytes.\n", nBytes ); + return ALLOC( char, nBytes ); + } + return Msat_MmFixedEntryFetch( p->pMap[nBytes] ); +} + + +/**Function************************************************************* + + Synopsis [Recycles the entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_MmStepEntryRecycle( Msat_MmStep_t * p, char * pEntry, int nBytes ) +{ + if ( nBytes == 0 ) + return; + if ( nBytes > p->nMapSize ) + { + free( pEntry ); + return; + } + Msat_MmFixedEntryRecycle( p->pMap[nBytes], pEntry ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Msat_MmStepReadMemUsage( Msat_MmStep_t * p ) +{ + int i, nMemTotal = 0; + for ( i = 0; i < p->nMems; i++ ) + nMemTotal += p->pMems[i]->nMemoryAlloc; + return nMemTotal; +} diff --git a/abc_with_bb_support/src/sat/msat/msatOrderH.c b/abc_with_bb_support/src/sat/msat/msatOrderH.c new file mode 100644 index 000000000..baacffecc --- /dev/null +++ b/abc_with_bb_support/src/sat/msat/msatOrderH.c @@ -0,0 +1,405 @@ +/**CFile**************************************************************** + + FileName [msatOrder.c] + + PackageName [A C version of SAT solver MINISAT, originally developed + in C++ by Niklas Een and Niklas Sorensson, Chalmers University of + Technology, Sweden: http://www.cs.chalmers.se/~een/Satzoo.] + + Synopsis [The manager of variable assignment.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 1, 2004.] + + Revision [$Id: msatOrder.c,v 1.0 2005/05/30 1:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "msatInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +// the variable package data structure +struct Msat_Order_t_ +{ + Msat_Solver_t * pSat; // the SAT solver + Msat_IntVec_t * vIndex; // the heap + Msat_IntVec_t * vHeap; // the mapping of var num into its heap num +}; + +//The solver can communicate to the variable order the following parts: +//- the array of current assignments (pSat->pAssigns) +//- the array of variable activities (pSat->pdActivity) +//- the array of variables currently in the cone (pSat->vConeVars) +//- the array of arrays of variables adjucent to each(pSat->vAdjacents) + +#define HLEFT(i) ((i)<<1) +#define HRIGHT(i) (((i)<<1)+1) +#define HPARENT(i) ((i)>>1) +#define HCOMPARE(p, i, j) ((p)->pSat->pdActivity[i] > (p)->pSat->pdActivity[j]) +#define HHEAP(p, i) ((p)->vHeap->pArray[i]) +#define HSIZE(p) ((p)->vHeap->nSize) +#define HOKAY(p, i) ((i) >= 0 && (i) < (p)->vIndex->nSize) +#define HINHEAP(p, i) (HOKAY(p, i) && (p)->vIndex->pArray[i] != 0) +#define HEMPTY(p) (HSIZE(p) == 1) + +static int Msat_HeapCheck_rec( Msat_Order_t * p, int i ); +static int Msat_HeapGetTop( Msat_Order_t * p ); +static void Msat_HeapInsert( Msat_Order_t * p, int n ); +static void Msat_HeapIncrease( Msat_Order_t * p, int n ); +static void Msat_HeapPercolateUp( Msat_Order_t * p, int i ); +static void Msat_HeapPercolateDown( Msat_Order_t * p, int i ); + +extern int timeSelect; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates the ordering structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_Order_t * Msat_OrderAlloc( Msat_Solver_t * pSat ) +{ + Msat_Order_t * p; + p = ALLOC( Msat_Order_t, 1 ); + memset( p, 0, sizeof(Msat_Order_t) ); + p->pSat = pSat; + p->vIndex = Msat_IntVecAlloc( 0 ); + p->vHeap = Msat_IntVecAlloc( 0 ); + Msat_OrderSetBounds( p, pSat->nVarsAlloc ); + return p; +} + +/**Function************************************************************* + + Synopsis [Sets the bound of the ordering structure.] + + Description [Should be called whenever the SAT solver is resized.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_OrderSetBounds( Msat_Order_t * p, int nVarsMax ) +{ + Msat_IntVecGrow( p->vIndex, nVarsMax ); + Msat_IntVecGrow( p->vHeap, nVarsMax + 1 ); + p->vIndex->nSize = nVarsMax; + p->vHeap->nSize = 0; +} + +/**Function************************************************************* + + Synopsis [Cleans the ordering structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_OrderClean( Msat_Order_t * p, Msat_IntVec_t * vCone ) +{ + int i; + for ( i = 0; i < p->vIndex->nSize; i++ ) + p->vIndex->pArray[i] = 0; + for ( i = 0; i < vCone->nSize; i++ ) + { + assert( i+1 < p->vHeap->nCap ); + p->vHeap->pArray[i+1] = vCone->pArray[i]; + + assert( vCone->pArray[i] < p->vIndex->nSize ); + p->vIndex->pArray[vCone->pArray[i]] = i+1; + } + p->vHeap->nSize = vCone->nSize + 1; +} + +/**Function************************************************************* + + Synopsis [Checks that the J-boundary is okay.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Msat_OrderCheck( Msat_Order_t * p ) +{ + return Msat_HeapCheck_rec( p, 1 ); +} + +/**Function************************************************************* + + Synopsis [Frees the ordering structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_OrderFree( Msat_Order_t * p ) +{ + Msat_IntVecFree( p->vHeap ); + Msat_IntVecFree( p->vIndex ); + free( p ); +} + + + +/**Function************************************************************* + + Synopsis [Selects the next variable to assign.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Msat_OrderVarSelect( Msat_Order_t * p ) +{ + // Activity based decision: +// while (!heap.empty()){ +// Var next = heap.getmin(); +// if (toLbool(assigns[next]) == l_Undef) +// return next; +// } +// return var_Undef; + + int Var; + int clk = clock(); + + while ( !HEMPTY(p) ) + { + Var = Msat_HeapGetTop(p); + if ( (p)->pSat->pAssigns[Var] == MSAT_VAR_UNASSIGNED ) + { +//assert( Msat_OrderCheck(p) ); +timeSelect += clock() - clk; + return Var; + } + } + return MSAT_ORDER_UNKNOWN; +} + +/**Function************************************************************* + + Synopsis [Updates J-boundary when the variable is assigned.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_OrderVarAssigned( Msat_Order_t * p, int Var ) +{ +} + +/**Function************************************************************* + + Synopsis [Updates the order after a variable is unassigned.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_OrderVarUnassigned( Msat_Order_t * p, int Var ) +{ +// if (!heap.inHeap(x)) +// heap.insert(x); + + int clk = clock(); + if ( !HINHEAP(p,Var) ) + Msat_HeapInsert( p, Var ); +timeSelect += clock() - clk; +} + +/**Function************************************************************* + + Synopsis [Updates the order after a variable changed weight.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_OrderUpdate( Msat_Order_t * p, int Var ) +{ +// if (heap.inHeap(x)) +// heap.increase(x); + + int clk = clock(); + if ( HINHEAP(p,Var) ) + Msat_HeapIncrease( p, Var ); +timeSelect += clock() - clk; +} + + + + +/**Function************************************************************* + + Synopsis [Checks the heap property recursively.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Msat_HeapCheck_rec( Msat_Order_t * p, int i ) +{ + return i >= HSIZE(p) || + ( HPARENT(i) == 0 || !HCOMPARE(p, HHEAP(p, i), HHEAP(p, HPARENT(i))) ) && + Msat_HeapCheck_rec( p, HLEFT(i) ) && Msat_HeapCheck_rec( p, HRIGHT(i) ); +} + +/**Function************************************************************* + + Synopsis [Retrieves the minimum element.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Msat_HeapGetTop( Msat_Order_t * p ) +{ + int Result, NewTop; + Result = HHEAP(p, 1); + NewTop = Msat_IntVecPop( p->vHeap ); + p->vHeap->pArray[1] = NewTop; + p->vIndex->pArray[NewTop] = 1; + p->vIndex->pArray[Result] = 0; + if ( p->vHeap->nSize > 1 ) + Msat_HeapPercolateDown( p, 1 ); + return Result; +} + +/**Function************************************************************* + + Synopsis [Inserts the new element.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_HeapInsert( Msat_Order_t * p, int n ) +{ + assert( HOKAY(p, n) ); + p->vIndex->pArray[n] = HSIZE(p); + Msat_IntVecPush( p->vHeap, n ); + Msat_HeapPercolateUp( p, p->vIndex->pArray[n] ); +} + +/**Function************************************************************* + + Synopsis [Inserts the new element.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_HeapIncrease( Msat_Order_t * p, int n ) +{ + Msat_HeapPercolateUp( p, p->vIndex->pArray[n] ); +} + +/**Function************************************************************* + + Synopsis [Moves the entry up.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_HeapPercolateUp( Msat_Order_t * p, int i ) +{ + int x = HHEAP(p, i); + while ( HPARENT(i) != 0 && HCOMPARE(p, x, HHEAP(p, HPARENT(i))) ) + { + p->vHeap->pArray[i] = HHEAP(p, HPARENT(i)); + p->vIndex->pArray[HHEAP(p, i)] = i; + i = HPARENT(i); + } + p->vHeap->pArray[i] = x; + p->vIndex->pArray[x] = i; +} + +/**Function************************************************************* + + Synopsis [Moves the entry down.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_HeapPercolateDown( Msat_Order_t * p, int i ) +{ + int x = HHEAP(p, i); + int Child; + while ( HLEFT(i) < HSIZE(p) ) + { + if ( HRIGHT(i) < HSIZE(p) && HCOMPARE(p, HHEAP(p, HRIGHT(i)), HHEAP(p, HLEFT(i))) ) + Child = HRIGHT(i); + else + Child = HLEFT(i); + if ( !HCOMPARE(p, HHEAP(p, Child), x) ) + break; + p->vHeap->pArray[i] = HHEAP(p, Child); + p->vIndex->pArray[HHEAP(p, i)] = i; + i = Child; + } + p->vHeap->pArray[i] = x; + p->vIndex->pArray[x] = i; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/msat/msatOrderJ.c b/abc_with_bb_support/src/sat/msat/msatOrderJ.c new file mode 100644 index 000000000..b163de495 --- /dev/null +++ b/abc_with_bb_support/src/sat/msat/msatOrderJ.c @@ -0,0 +1,472 @@ +/**CFile**************************************************************** + + FileName [msatOrder.c] + + PackageName [A C version of SAT solver MINISAT, originally developed + in C++ by Niklas Een and Niklas Sorensson, Chalmers University of + Technology, Sweden: http://www.cs.chalmers.se/~een/Satzoo.] + + Synopsis [The manager of variable assignment.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 1, 2004.] + + Revision [$Id: msatOrder.c,v 1.0 2005/05/30 1:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "msatInt.h" + +/* +The J-boundary (justification boundary) is defined as a set of unassigned +variables belonging to the cone of interest, such that for each of them, +there exist an adjacent assigned variable in the cone of interest. +*/ + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Msat_OrderVar_t_ Msat_OrderVar_t; +typedef struct Msat_OrderRing_t_ Msat_OrderRing_t; + +// the variable data structure +struct Msat_OrderVar_t_ +{ + Msat_OrderVar_t * pNext; + Msat_OrderVar_t * pPrev; + int Num; +}; + +// the ring of variables data structure (J-boundary) +struct Msat_OrderRing_t_ +{ + Msat_OrderVar_t * pRoot; + int nItems; +}; + +// the variable package data structure +struct Msat_Order_t_ +{ + Msat_Solver_t * pSat; // the SAT solver + Msat_OrderVar_t * pVars; // the storage for variables + int nVarsAlloc; // the number of variables allocated + Msat_OrderRing_t rVars; // the J-boundary as a ring of variables +}; + +//The solver can communicate to the variable order the following parts: +//- the array of current assignments (pSat->pAssigns) +//- the array of variable activities (pSat->pdActivity) +//- the array of variables currently in the cone (pSat->vConeVars) +//- the array of arrays of variables adjucent to each(pSat->vAdjacents) + +#define Msat_OrderVarIsInBoundary( p, i ) ((p)->pVars[i].pNext) +#define Msat_OrderVarIsAssigned( p, i ) ((p)->pSat->pAssigns[i] != MSAT_VAR_UNASSIGNED) +#define Msat_OrderVarIsUsedInCone( p, i ) ((p)->pSat->vVarsUsed->pArray[i]) + +// iterator through the entries in J-boundary +#define Msat_OrderRingForEachEntry( pRing, pVar, pNext ) \ + for ( pVar = pRing, \ + pNext = pVar? pVar->pNext : NULL; \ + pVar; \ + pVar = (pNext != pRing)? pNext : NULL, \ + pNext = pVar? pVar->pNext : NULL ) + +static void Msat_OrderRingAddLast( Msat_OrderRing_t * pRing, Msat_OrderVar_t * pVar ); +static void Msat_OrderRingRemove( Msat_OrderRing_t * pRing, Msat_OrderVar_t * pVar ); + +extern int timeSelect; +extern int timeAssign; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates the ordering structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_Order_t * Msat_OrderAlloc( Msat_Solver_t * pSat ) +{ + Msat_Order_t * p; + p = ALLOC( Msat_Order_t, 1 ); + memset( p, 0, sizeof(Msat_Order_t) ); + p->pSat = pSat; + Msat_OrderSetBounds( p, pSat->nVarsAlloc ); + return p; +} + +/**Function************************************************************* + + Synopsis [Sets the bound of the ordering structure.] + + Description [Should be called whenever the SAT solver is resized.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_OrderSetBounds( Msat_Order_t * p, int nVarsMax ) +{ + int i; + // add variables if they are missing + if ( p->nVarsAlloc < nVarsMax ) + { + p->pVars = REALLOC( Msat_OrderVar_t, p->pVars, nVarsMax ); + for ( i = p->nVarsAlloc; i < nVarsMax; i++ ) + { + p->pVars[i].pNext = p->pVars[i].pPrev = NULL; + p->pVars[i].Num = i; + } + p->nVarsAlloc = nVarsMax; + } +} + +/**Function************************************************************* + + Synopsis [Cleans the ordering structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_OrderClean( Msat_Order_t * p, Msat_IntVec_t * vCone ) +{ + Msat_OrderVar_t * pVar, * pNext; + // quickly undo the ring + Msat_OrderRingForEachEntry( p->rVars.pRoot, pVar, pNext ) + pVar->pNext = pVar->pPrev = NULL; + p->rVars.pRoot = NULL; + p->rVars.nItems = 0; +} + +/**Function************************************************************* + + Synopsis [Checks that the J-boundary is okay.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Msat_OrderCheck( Msat_Order_t * p ) +{ + Msat_OrderVar_t * pVar, * pNext; + Msat_IntVec_t * vRound; + int * pRound, nRound; + int * pVars, nVars, i, k; + int Counter = 0; + + // go through all the variables in the boundary + Msat_OrderRingForEachEntry( p->rVars.pRoot, pVar, pNext ) + { + assert( !Msat_OrderVarIsAssigned(p, pVar->Num) ); + // go though all the variables in the neighborhood + // and check that it is true that there is least one assigned + vRound = (Msat_IntVec_t *)Msat_ClauseVecReadEntry( p->pSat->vAdjacents, pVar->Num ); + nRound = Msat_IntVecReadSize( vRound ); + pRound = Msat_IntVecReadArray( vRound ); + for ( i = 0; i < nRound; i++ ) + { + if ( !Msat_OrderVarIsUsedInCone(p, pRound[i]) ) + continue; + if ( Msat_OrderVarIsAssigned(p, pRound[i]) ) + break; + } +// assert( i != nRound ); +// if ( i == nRound ) +// return 0; + if ( i == nRound ) + Counter++; + } + if ( Counter > 0 ) + printf( "%d(%d) ", Counter, p->rVars.nItems ); + + // we may also check other unassigned variables in the cone + // to make sure that if they are not in J-boundary, + // then they do not have an assigned neighbor + nVars = Msat_IntVecReadSize( p->pSat->vConeVars ); + pVars = Msat_IntVecReadArray( p->pSat->vConeVars ); + for ( i = 0; i < nVars; i++ ) + { + assert( Msat_OrderVarIsUsedInCone(p, pVars[i]) ); + // skip assigned vars, vars in the boundary, and vars not used in the cone + if ( Msat_OrderVarIsAssigned(p, pVars[i]) || + Msat_OrderVarIsInBoundary(p, pVars[i]) ) + continue; + // make sure, it does not have assigned neighbors + vRound = (Msat_IntVec_t *)Msat_ClauseVecReadEntry( p->pSat->vAdjacents, pVars[i] ); + nRound = Msat_IntVecReadSize( vRound ); + pRound = Msat_IntVecReadArray( vRound ); + for ( k = 0; k < nRound; k++ ) + { + if ( !Msat_OrderVarIsUsedInCone(p, pRound[k]) ) + continue; + if ( Msat_OrderVarIsAssigned(p, pRound[k]) ) + break; + } +// assert( k == nRound ); +// if ( k != nRound ) +// return 0; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Frees the ordering structure.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_OrderFree( Msat_Order_t * p ) +{ + free( p->pVars ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Selects the next variable to assign.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Msat_OrderVarSelect( Msat_Order_t * p ) +{ + Msat_OrderVar_t * pVar, * pNext, * pVarBest; + double * pdActs = p->pSat->pdActivity; + double dfActBest; +// int clk = clock(); + + pVarBest = NULL; + dfActBest = -1.0; + Msat_OrderRingForEachEntry( p->rVars.pRoot, pVar, pNext ) + { + if ( dfActBest < pdActs[pVar->Num] ) + { + dfActBest = pdActs[pVar->Num]; + pVarBest = pVar; + } + } +//timeSelect += clock() - clk; +//timeAssign += clock() - clk; + +//if ( pVarBest && pVarBest->Num % 1000 == 0 ) +//printf( "%d ", p->rVars.nItems ); + +// Msat_OrderCheck( p ); + if ( pVarBest ) + { + assert( Msat_OrderVarIsUsedInCone(p, pVarBest->Num) ); + return pVarBest->Num; + } + return MSAT_ORDER_UNKNOWN; +} + +/**Function************************************************************* + + Synopsis [Updates J-boundary when the variable is assigned.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_OrderVarAssigned( Msat_Order_t * p, int Var ) +{ + Msat_IntVec_t * vRound; + int i;//, clk = clock(); + + // make sure the variable is in the boundary + assert( Var < p->nVarsAlloc ); + // if it is not in the boundary (initial decision, random decision), do not remove + if ( Msat_OrderVarIsInBoundary( p, Var ) ) + Msat_OrderRingRemove( &p->rVars, &p->pVars[Var] ); + // add to the boundary those neighbors that are (1) unassigned, (2) not in boundary + // because for them we know that there is a variable (Var) which is assigned + vRound = (Msat_IntVec_t *)p->pSat->vAdjacents->pArray[Var]; + for ( i = 0; i < vRound->nSize; i++ ) + { + if ( !Msat_OrderVarIsUsedInCone(p, vRound->pArray[i]) ) + continue; + if ( Msat_OrderVarIsAssigned(p, vRound->pArray[i]) ) + continue; + if ( Msat_OrderVarIsInBoundary(p, vRound->pArray[i]) ) + continue; + Msat_OrderRingAddLast( &p->rVars, &p->pVars[vRound->pArray[i]] ); + } +//timeSelect += clock() - clk; +// Msat_OrderCheck( p ); +} + +/**Function************************************************************* + + Synopsis [Updates the order after a variable is unassigned.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_OrderVarUnassigned( Msat_Order_t * p, int Var ) +{ + Msat_IntVec_t * vRound, * vRound2; + int i, k;//, clk = clock(); + + // make sure the variable is not in the boundary + assert( Var < p->nVarsAlloc ); + assert( !Msat_OrderVarIsInBoundary( p, Var ) ); + // go through its neigbors - if one of them is assigned add this var + // add to the boundary those neighbors that are not there already + // this will also get rid of variable outside of the current cone + // because they are unassigned in Msat_SolverPrepare() + vRound = (Msat_IntVec_t *)p->pSat->vAdjacents->pArray[Var]; + for ( i = 0; i < vRound->nSize; i++ ) + if ( Msat_OrderVarIsAssigned(p, vRound->pArray[i]) ) + break; + if ( i != vRound->nSize ) + Msat_OrderRingAddLast( &p->rVars, &p->pVars[Var] ); + + // unassigning a variable may lead to its adjacents dropping from the boundary + for ( i = 0; i < vRound->nSize; i++ ) + if ( Msat_OrderVarIsInBoundary(p, vRound->pArray[i]) ) + { // the neighbor is in the J-boundary (and unassigned) + assert( !Msat_OrderVarIsAssigned(p, vRound->pArray[i]) ); + vRound2 = (Msat_IntVec_t *)p->pSat->vAdjacents->pArray[vRound->pArray[i]]; + // go through its neighbors and determine if there is at least one assigned + for ( k = 0; k < vRound2->nSize; k++ ) + if ( Msat_OrderVarIsAssigned(p, vRound2->pArray[k]) ) + break; + if ( k == vRound2->nSize ) // there is no assigned vars, delete this one + Msat_OrderRingRemove( &p->rVars, &p->pVars[vRound->pArray[i]] ); + } +//timeSelect += clock() - clk; +// Msat_OrderCheck( p ); +} + +/**Function************************************************************* + + Synopsis [Updates the order after a variable changed weight.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_OrderUpdate( Msat_Order_t * p, int Var ) +{ +} + + +/**Function************************************************************* + + Synopsis [Adds node to the end of the ring.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_OrderRingAddLast( Msat_OrderRing_t * pRing, Msat_OrderVar_t * pVar ) +{ +//printf( "adding %d\n", pVar->Num ); + // check that the node is not in a ring + assert( pVar->pPrev == NULL ); + assert( pVar->pNext == NULL ); + // if the ring is empty, make the node point to itself + pRing->nItems++; + if ( pRing->pRoot == NULL ) + { + pRing->pRoot = pVar; + pVar->pPrev = pVar; + pVar->pNext = pVar; + return; + } + // if the ring is not empty, add it as the last entry + pVar->pPrev = pRing->pRoot->pPrev; + pVar->pNext = pRing->pRoot; + pVar->pPrev->pNext = pVar; + pVar->pNext->pPrev = pVar; + + // move the root so that it points to the new entry +// pRing->pRoot = pRing->pRoot->pPrev; +} + +/**Function************************************************************* + + Synopsis [Removes the node from the ring.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_OrderRingRemove( Msat_OrderRing_t * pRing, Msat_OrderVar_t * pVar ) +{ +//printf( "removing %d\n", pVar->Num ); + // check that the var is in a ring + assert( pVar->pPrev ); + assert( pVar->pNext ); + pRing->nItems--; + if ( pRing->nItems == 0 ) + { + assert( pRing->pRoot == pVar ); + pVar->pPrev = NULL; + pVar->pNext = NULL; + pRing->pRoot = NULL; + return; + } + // move the root if needed + if ( pRing->pRoot == pVar ) + pRing->pRoot = pVar->pNext; + // move the root to the next entry after pVar + // this way all the additions to the list will be traversed first +// pRing->pRoot = pVar->pPrev; + // delete the node + pVar->pPrev->pNext = pVar->pNext; + pVar->pNext->pPrev = pVar->pPrev; + pVar->pPrev = NULL; + pVar->pNext = NULL; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/msat/msatQueue.c b/abc_with_bb_support/src/sat/msat/msatQueue.c new file mode 100644 index 000000000..5e9ab80ab --- /dev/null +++ b/abc_with_bb_support/src/sat/msat/msatQueue.c @@ -0,0 +1,157 @@ +/**CFile**************************************************************** + + FileName [msatQueue.c] + + PackageName [A C version of SAT solver MINISAT, originally developed + in C++ by Niklas Een and Niklas Sorensson, Chalmers University of + Technology, Sweden: http://www.cs.chalmers.se/~een/Satzoo.] + + Synopsis [The manager of the assignment propagation queue.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 1, 2004.] + + Revision [$Id: msatQueue.c,v 1.0 2004/01/01 1:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "msatInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +struct Msat_Queue_t_ +{ + int nVars; + int * pVars; + int iFirst; + int iLast; +}; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates the variable propagation queue.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_Queue_t * Msat_QueueAlloc( int nVars ) +{ + Msat_Queue_t * p; + p = ALLOC( Msat_Queue_t, 1 ); + memset( p, 0, sizeof(Msat_Queue_t) ); + p->nVars = nVars; + p->pVars = ALLOC( int, nVars ); + return p; +} + +/**Function************************************************************* + + Synopsis [Deallocate the variable propagation queue.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_QueueFree( Msat_Queue_t * p ) +{ + free( p->pVars ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Reads the queue size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Msat_QueueReadSize( Msat_Queue_t * p ) +{ + return p->iLast - p->iFirst; +} + +/**Function************************************************************* + + Synopsis [Insert an entry into the queue.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_QueueInsert( Msat_Queue_t * p, int Lit ) +{ + if ( p->iLast == p->nVars ) + { + int i; + assert( 0 ); + for ( i = 0; i < p->iLast; i++ ) + printf( "entry = %2d lit = %2d var = %2d \n", i, p->pVars[i], p->pVars[i]/2 ); + } + assert( p->iLast < p->nVars ); + p->pVars[p->iLast++] = Lit; +} + +/**Function************************************************************* + + Synopsis [Extracts an entry from the queue.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Msat_QueueExtract( Msat_Queue_t * p ) +{ + if ( p->iFirst == p->iLast ) + return -1; + return p->pVars[p->iFirst++]; +} + +/**Function************************************************************* + + Synopsis [Resets the queue.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_QueueClear( Msat_Queue_t * p ) +{ + p->iFirst = 0; + p->iLast = 0; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/msat/msatRead.c b/abc_with_bb_support/src/sat/msat/msatRead.c new file mode 100644 index 000000000..3a7468b5c --- /dev/null +++ b/abc_with_bb_support/src/sat/msat/msatRead.c @@ -0,0 +1,268 @@ +/**CFile**************************************************************** + + FileName [msatRead.c] + + PackageName [A C version of SAT solver MINISAT, originally developed + in C++ by Niklas Een and Niklas Sorensson, Chalmers University of + Technology, Sweden: http://www.cs.chalmers.se/~een/Satzoo.] + + Synopsis [The reader of the CNF formula in DIMACS format.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 1, 2004.] + + Revision [$Id: msatRead.c,v 1.0 2004/01/01 1:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "msatInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static char * Msat_FileRead( FILE * pFile ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Read the file into the internal buffer.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Msat_FileRead( FILE * pFile ) +{ + int nFileSize; + char * pBuffer; + // get the file size, in bytes + fseek( pFile, 0, SEEK_END ); + nFileSize = ftell( pFile ); + // move the file current reading position to the beginning + rewind( pFile ); + // load the contents of the file into memory + pBuffer = ALLOC( char, nFileSize + 3 ); + fread( pBuffer, nFileSize, 1, pFile ); + // terminate the string with '\0' + pBuffer[ nFileSize + 0] = '\n'; + pBuffer[ nFileSize + 1] = '\0'; + return pBuffer; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static void Msat_ReadWhitespace( char ** pIn ) +{ + while ((**pIn >= 9 && **pIn <= 13) || **pIn == 32) + (*pIn)++; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static void Msat_ReadNotWhitespace( char ** pIn ) +{ + while ( !((**pIn >= 9 && **pIn <= 13) || **pIn == 32) ) + (*pIn)++; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static void skipLine( char ** pIn ) +{ + while ( 1 ) + { + if (**pIn == 0) + return; + if (**pIn == '\n') + { + (*pIn)++; + return; + } + (*pIn)++; + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Msat_ReadInt( char ** pIn ) +{ + int val = 0; + bool neg = 0; + + Msat_ReadWhitespace( pIn ); + if ( **pIn == '-' ) + neg = 1, + (*pIn)++; + else if ( **pIn == '+' ) + (*pIn)++; + if ( **pIn < '0' || **pIn > '9' ) + fprintf(stderr, "PARSE ERROR! Unexpected char: %c\n", **pIn), + exit(1); + while ( **pIn >= '0' && **pIn <= '9' ) + val = val*10 + (**pIn - '0'), + (*pIn)++; + return neg ? -val : val; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static void Msat_ReadClause( char ** pIn, Msat_Solver_t * p, Msat_IntVec_t * pLits ) +{ + int nVars = Msat_SolverReadVarNum( p ); + int parsed_lit, var, sign; + + Msat_IntVecClear( pLits ); + while ( 1 ) + { + parsed_lit = Msat_ReadInt(pIn); + if ( parsed_lit == 0 ) + break; + var = abs(parsed_lit) - 1; + sign = (parsed_lit > 0); + if ( var >= nVars ) + { + printf( "Variable %d is larger than the number of allocated variables (%d).\n", var+1, nVars ); + exit(1); + } + Msat_IntVecPush( pLits, MSAT_VAR2LIT(var, !sign) ); + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static bool Msat_ReadDimacs( char * pText, Msat_Solver_t ** pS, bool fVerbose ) +{ + Msat_Solver_t * p; + Msat_IntVec_t * pLits; + char * pIn = pText; + int nVars, nClas; + while ( 1 ) + { + Msat_ReadWhitespace( &pIn ); + if ( *pIn == 0 ) + break; + else if ( *pIn == 'c' ) + skipLine( &pIn ); + else if ( *pIn == 'p' ) + { + pIn++; + Msat_ReadWhitespace( &pIn ); + Msat_ReadNotWhitespace( &pIn ); + + nVars = Msat_ReadInt( &pIn ); + nClas = Msat_ReadInt( &pIn ); + skipLine( &pIn ); + // start the solver + p = Msat_SolverAlloc( nVars, 1, 1, 1, 1, 0 ); + Msat_SolverClean( p, nVars ); + Msat_SolverSetVerbosity( p, fVerbose ); + // allocate the vector + pLits = Msat_IntVecAlloc( nVars ); + } + else + { + if ( p == NULL ) + { + printf( "There is no parameter line.\n" ); + exit(1); + } + Msat_ReadClause( &pIn, p, pLits ); + if ( !Msat_SolverAddClause( p, pLits ) ) + return 0; + } + } + Msat_IntVecFree( pLits ); + *pS = p; + return Msat_SolverSimplifyDB( p ); +} + +/**Function************************************************************* + + Synopsis [Starts the solver and reads the DIMAC file.] + + Description [Returns FALSE upon immediate conflict.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Msat_SolverParseDimacs( FILE * pFile, Msat_Solver_t ** p, int fVerbose ) +{ + char * pText; + bool Value; + pText = Msat_FileRead( pFile ); + Value = Msat_ReadDimacs( pText, p, fVerbose ); + free( pText ); + return Value; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/msat/msatSolverApi.c b/abc_with_bb_support/src/sat/msat/msatSolverApi.c new file mode 100644 index 000000000..84c35bfd7 --- /dev/null +++ b/abc_with_bb_support/src/sat/msat/msatSolverApi.c @@ -0,0 +1,500 @@ +/**CFile**************************************************************** + + FileName [msatSolverApi.c] + + PackageName [A C version of SAT solver MINISAT, originally developed + in C++ by Niklas Een and Niklas Sorensson, Chalmers University of + Technology, Sweden: http://www.cs.chalmers.se/~een/Satzoo.] + + Synopsis [APIs of the SAT solver.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 1, 2004.] + + Revision [$Id: msatSolverApi.c,v 1.0 2004/01/01 1:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "msatInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Msat_SolverSetupTruthTables( unsigned uTruths[][2] ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Simple SAT solver APIs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Msat_SolverReadVarNum( Msat_Solver_t * p ) { return p->nVars; } +int Msat_SolverReadClauseNum( Msat_Solver_t * p ) { return p->nClauses; } +int Msat_SolverReadVarAllocNum( Msat_Solver_t * p ) { return p->nVarsAlloc; } +int Msat_SolverReadDecisionLevel( Msat_Solver_t * p ) { return Msat_IntVecReadSize(p->vTrailLim); } +int * Msat_SolverReadDecisionLevelArray( Msat_Solver_t * p ) { return p->pLevel; } +Msat_Clause_t ** Msat_SolverReadReasonArray( Msat_Solver_t * p ) { return p->pReasons; } +Msat_Lit_t Msat_SolverReadVarValue( Msat_Solver_t * p, Msat_Var_t Var ) { return p->pAssigns[Var]; } +Msat_ClauseVec_t * Msat_SolverReadLearned( Msat_Solver_t * p ) { return p->vLearned; } +Msat_ClauseVec_t ** Msat_SolverReadWatchedArray( Msat_Solver_t * p ) { return p->pvWatched; } +int * Msat_SolverReadAssignsArray( Msat_Solver_t * p ) { return p->pAssigns; } +int * Msat_SolverReadModelArray( Msat_Solver_t * p ) { return p->pModel; } +int Msat_SolverReadBackTracks( Msat_Solver_t * p ) { return (int)p->Stats.nConflicts; } +int Msat_SolverReadInspects( Msat_Solver_t * p ) { return (int)p->Stats.nInspects; } +Msat_MmStep_t * Msat_SolverReadMem( Msat_Solver_t * p ) { return p->pMem; } +int * Msat_SolverReadSeenArray( Msat_Solver_t * p ) { return p->pSeen; } +int Msat_SolverIncrementSeenId( Msat_Solver_t * p ) { return ++p->nSeenId; } +void Msat_SolverSetVerbosity( Msat_Solver_t * p, int fVerbose ) { p->fVerbose = fVerbose; } +void Msat_SolverClausesIncrement( Msat_Solver_t * p ) { p->nClausesAlloc++; } +void Msat_SolverClausesDecrement( Msat_Solver_t * p ) { p->nClausesAlloc--; } +void Msat_SolverClausesIncrementL( Msat_Solver_t * p ) { p->nClausesAllocL++; } +void Msat_SolverClausesDecrementL( Msat_Solver_t * p ) { p->nClausesAllocL--; } +void Msat_SolverMarkLastClauseTypeA( Msat_Solver_t * p ) { Msat_ClauseSetTypeA( Msat_ClauseVecReadEntry( p->vClauses, Msat_ClauseVecReadSize(p->vClauses)-1 ), 1 ); } +void Msat_SolverMarkClausesStart( Msat_Solver_t * p ) { p->nClausesStart = Msat_ClauseVecReadSize(p->vClauses); } +float * Msat_SolverReadFactors( Msat_Solver_t * p ) { return p->pFactors; } + +/**Function************************************************************* + + Synopsis [Reads the clause with the given number.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_Clause_t * Msat_SolverReadClause( Msat_Solver_t * p, int Num ) +{ + int nClausesP; + assert( Num < p->nClauses ); + nClausesP = Msat_ClauseVecReadSize( p->vClauses ); + if ( Num < nClausesP ) + return Msat_ClauseVecReadEntry( p->vClauses, Num ); + return Msat_ClauseVecReadEntry( p->vLearned, Num - nClausesP ); +} + +/**Function************************************************************* + + Synopsis [Reads the clause with the given number.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_ClauseVec_t * Msat_SolverReadAdjacents( Msat_Solver_t * p ) +{ + return p->vAdjacents; +} + +/**Function************************************************************* + + Synopsis [Reads the clause with the given number.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_IntVec_t * Msat_SolverReadConeVars( Msat_Solver_t * p ) +{ + return p->vConeVars; +} + +/**Function************************************************************* + + Synopsis [Reads the clause with the given number.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_IntVec_t * Msat_SolverReadVarsUsed( Msat_Solver_t * p ) +{ + return p->vVarsUsed; +} + + +/**Function************************************************************* + + Synopsis [Allocates the solver.] + + Description [After the solver is allocated, the procedure + Msat_SolverClean() should be called to set the number of variables.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_Solver_t * Msat_SolverAlloc( int nVarsAlloc, + double dClaInc, double dClaDecay, + double dVarInc, double dVarDecay, + bool fVerbose ) +{ + Msat_Solver_t * p; + int i; + + assert(sizeof(Msat_Lit_t) == sizeof(unsigned)); + assert(sizeof(float) == sizeof(unsigned)); + + p = ALLOC( Msat_Solver_t, 1 ); + memset( p, 0, sizeof(Msat_Solver_t) ); + + p->nVarsAlloc = nVarsAlloc; + p->nVars = 0; + + p->nClauses = 0; + p->vClauses = Msat_ClauseVecAlloc( 512 ); + p->vLearned = Msat_ClauseVecAlloc( 512 ); + + p->dClaInc = dClaInc; + p->dClaDecay = dClaDecay; + p->dVarInc = dVarInc; + p->dVarDecay = dVarDecay; + + p->pdActivity = ALLOC( double, p->nVarsAlloc ); + p->pFactors = ALLOC( float, p->nVarsAlloc ); + for ( i = 0; i < p->nVarsAlloc; i++ ) + { + p->pdActivity[i] = 0.0; + p->pFactors[i] = 1.0; + } + + p->pAssigns = ALLOC( int, p->nVarsAlloc ); + p->pModel = ALLOC( int, p->nVarsAlloc ); + for ( i = 0; i < p->nVarsAlloc; i++ ) + p->pAssigns[i] = MSAT_VAR_UNASSIGNED; +// p->pOrder = Msat_OrderAlloc( p->pAssigns, p->pdActivity, p->nVarsAlloc ); + p->pOrder = Msat_OrderAlloc( p ); + + p->pvWatched = ALLOC( Msat_ClauseVec_t *, 2 * p->nVarsAlloc ); + for ( i = 0; i < 2 * p->nVarsAlloc; i++ ) + p->pvWatched[i] = Msat_ClauseVecAlloc( 16 ); + p->pQueue = Msat_QueueAlloc( p->nVarsAlloc ); + + p->vTrail = Msat_IntVecAlloc( p->nVarsAlloc ); + p->vTrailLim = Msat_IntVecAlloc( p->nVarsAlloc ); + p->pReasons = ALLOC( Msat_Clause_t *, p->nVarsAlloc ); + memset( p->pReasons, 0, sizeof(Msat_Clause_t *) * p->nVarsAlloc ); + p->pLevel = ALLOC( int, p->nVarsAlloc ); + for ( i = 0; i < p->nVarsAlloc; i++ ) + p->pLevel[i] = -1; + p->dRandSeed = 91648253; + p->fVerbose = fVerbose; + p->dProgress = 0.0; +// p->pModel = Msat_IntVecAlloc( p->nVarsAlloc ); + p->pMem = Msat_MmStepStart( 10 ); + + p->vConeVars = Msat_IntVecAlloc( p->nVarsAlloc ); + p->vAdjacents = Msat_ClauseVecAlloc( p->nVarsAlloc ); + for ( i = 0; i < p->nVarsAlloc; i++ ) + Msat_ClauseVecPush( p->vAdjacents, (Msat_Clause_t *)Msat_IntVecAlloc(5) ); + p->vVarsUsed = Msat_IntVecAlloc( p->nVarsAlloc ); + Msat_IntVecFill( p->vVarsUsed, p->nVarsAlloc, 1 ); + + + p->pSeen = ALLOC( int, p->nVarsAlloc ); + memset( p->pSeen, 0, sizeof(int) * p->nVarsAlloc ); + p->nSeenId = 1; + p->vReason = Msat_IntVecAlloc( p->nVarsAlloc ); + p->vTemp = Msat_IntVecAlloc( p->nVarsAlloc ); + return p; +} + +/**Function************************************************************* + + Synopsis [Resizes the solver.] + + Description [Assumes that the solver contains some clauses, and that + it is currently between the calls. Resizes the solver to accomodate + more variables.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverResize( Msat_Solver_t * p, int nVarsAlloc ) +{ + int nVarsAllocOld, i; + + nVarsAllocOld = p->nVarsAlloc; + p->nVarsAlloc = nVarsAlloc; + + p->pdActivity = REALLOC( double, p->pdActivity, p->nVarsAlloc ); + p->pFactors = REALLOC( float, p->pFactors, p->nVarsAlloc ); + for ( i = nVarsAllocOld; i < p->nVarsAlloc; i++ ) + { + p->pdActivity[i] = 0.0; + p->pFactors[i] = 1.0; + } + + p->pAssigns = REALLOC( int, p->pAssigns, p->nVarsAlloc ); + p->pModel = REALLOC( int, p->pModel, p->nVarsAlloc ); + for ( i = nVarsAllocOld; i < p->nVarsAlloc; i++ ) + p->pAssigns[i] = MSAT_VAR_UNASSIGNED; + +// Msat_OrderRealloc( p->pOrder, p->pAssigns, p->pdActivity, p->nVarsAlloc ); + Msat_OrderSetBounds( p->pOrder, p->nVarsAlloc ); + + p->pvWatched = REALLOC( Msat_ClauseVec_t *, p->pvWatched, 2 * p->nVarsAlloc ); + for ( i = 2 * nVarsAllocOld; i < 2 * p->nVarsAlloc; i++ ) + p->pvWatched[i] = Msat_ClauseVecAlloc( 16 ); + + Msat_QueueFree( p->pQueue ); + p->pQueue = Msat_QueueAlloc( p->nVarsAlloc ); + + p->pReasons = REALLOC( Msat_Clause_t *, p->pReasons, p->nVarsAlloc ); + p->pLevel = REALLOC( int, p->pLevel, p->nVarsAlloc ); + for ( i = nVarsAllocOld; i < p->nVarsAlloc; i++ ) + { + p->pReasons[i] = NULL; + p->pLevel[i] = -1; + } + + p->pSeen = REALLOC( int, p->pSeen, p->nVarsAlloc ); + for ( i = nVarsAllocOld; i < p->nVarsAlloc; i++ ) + p->pSeen[i] = 0; + + Msat_IntVecGrow( p->vTrail, p->nVarsAlloc ); + Msat_IntVecGrow( p->vTrailLim, p->nVarsAlloc ); + + // make sure the array of adjucents has room to store the variable numbers + for ( i = Msat_ClauseVecReadSize(p->vAdjacents); i < p->nVarsAlloc; i++ ) + Msat_ClauseVecPush( p->vAdjacents, (Msat_Clause_t *)Msat_IntVecAlloc(5) ); + Msat_IntVecFill( p->vVarsUsed, p->nVarsAlloc, 1 ); +} + +/**Function************************************************************* + + Synopsis [Prepares the solver.] + + Description [Cleans the solver assuming that the problem will involve + the given number of variables (nVars). This procedure is useful + for many small (incremental) SAT problems, to prevent the solver + from being reallocated each time.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverClean( Msat_Solver_t * p, int nVars ) +{ + int i; + // free the clauses + int nClauses; + Msat_Clause_t ** pClauses; + + assert( p->nVarsAlloc >= nVars ); + p->nVars = nVars; + p->nClauses = 0; + + nClauses = Msat_ClauseVecReadSize( p->vClauses ); + pClauses = Msat_ClauseVecReadArray( p->vClauses ); + for ( i = 0; i < nClauses; i++ ) + Msat_ClauseFree( p, pClauses[i], 0 ); +// Msat_ClauseVecFree( p->vClauses ); + Msat_ClauseVecClear( p->vClauses ); + + nClauses = Msat_ClauseVecReadSize( p->vLearned ); + pClauses = Msat_ClauseVecReadArray( p->vLearned ); + for ( i = 0; i < nClauses; i++ ) + Msat_ClauseFree( p, pClauses[i], 0 ); +// Msat_ClauseVecFree( p->vLearned ); + Msat_ClauseVecClear( p->vLearned ); + +// FREE( p->pdActivity ); + for ( i = 0; i < p->nVars; i++ ) + p->pdActivity[i] = 0; + +// Msat_OrderFree( p->pOrder ); +// Msat_OrderClean( p->pOrder, p->nVars, NULL ); + Msat_OrderSetBounds( p->pOrder, p->nVars ); + + for ( i = 0; i < 2 * p->nVars; i++ ) +// Msat_ClauseVecFree( p->pvWatched[i] ); + Msat_ClauseVecClear( p->pvWatched[i] ); +// FREE( p->pvWatched ); +// Msat_QueueFree( p->pQueue ); + Msat_QueueClear( p->pQueue ); + +// FREE( p->pAssigns ); + for ( i = 0; i < p->nVars; i++ ) + p->pAssigns[i] = MSAT_VAR_UNASSIGNED; +// Msat_IntVecFree( p->vTrail ); + Msat_IntVecClear( p->vTrail ); +// Msat_IntVecFree( p->vTrailLim ); + Msat_IntVecClear( p->vTrailLim ); +// FREE( p->pReasons ); + memset( p->pReasons, 0, sizeof(Msat_Clause_t *) * p->nVars ); +// FREE( p->pLevel ); + for ( i = 0; i < p->nVars; i++ ) + p->pLevel[i] = -1; +// Msat_IntVecFree( p->pModel ); +// Msat_MmStepStop( p->pMem, 0 ); + p->dRandSeed = 91648253; + p->dProgress = 0.0; + +// FREE( p->pSeen ); + memset( p->pSeen, 0, sizeof(int) * p->nVars ); + p->nSeenId = 1; +// Msat_IntVecFree( p->vReason ); + Msat_IntVecClear( p->vReason ); +// Msat_IntVecFree( p->vTemp ); + Msat_IntVecClear( p->vTemp ); +// printf(" The number of clauses remaining = %d (%d).\n", p->nClausesAlloc, p->nClausesAllocL ); +// FREE( p ); +} + +/**Function************************************************************* + + Synopsis [Frees the solver.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverFree( Msat_Solver_t * p ) +{ + int i; + + // free the clauses + int nClauses; + Msat_Clause_t ** pClauses; +//printf( "clauses = %d. learned = %d.\n", Msat_ClauseVecReadSize( p->vClauses ), +// Msat_ClauseVecReadSize( p->vLearned ) ); + + nClauses = Msat_ClauseVecReadSize( p->vClauses ); + pClauses = Msat_ClauseVecReadArray( p->vClauses ); + for ( i = 0; i < nClauses; i++ ) + Msat_ClauseFree( p, pClauses[i], 0 ); + Msat_ClauseVecFree( p->vClauses ); + + nClauses = Msat_ClauseVecReadSize( p->vLearned ); + pClauses = Msat_ClauseVecReadArray( p->vLearned ); + for ( i = 0; i < nClauses; i++ ) + Msat_ClauseFree( p, pClauses[i], 0 ); + Msat_ClauseVecFree( p->vLearned ); + + FREE( p->pdActivity ); + FREE( p->pFactors ); + Msat_OrderFree( p->pOrder ); + + for ( i = 0; i < 2 * p->nVarsAlloc; i++ ) + Msat_ClauseVecFree( p->pvWatched[i] ); + FREE( p->pvWatched ); + Msat_QueueFree( p->pQueue ); + + FREE( p->pAssigns ); + FREE( p->pModel ); + Msat_IntVecFree( p->vTrail ); + Msat_IntVecFree( p->vTrailLim ); + FREE( p->pReasons ); + FREE( p->pLevel ); + + Msat_MmStepStop( p->pMem, 0 ); + + nClauses = Msat_ClauseVecReadSize( p->vAdjacents ); + pClauses = Msat_ClauseVecReadArray( p->vAdjacents ); + for ( i = 0; i < nClauses; i++ ) + Msat_IntVecFree( (Msat_IntVec_t *)pClauses[i] ); + Msat_ClauseVecFree( p->vAdjacents ); + Msat_IntVecFree( p->vConeVars ); + Msat_IntVecFree( p->vVarsUsed ); + + FREE( p->pSeen ); + Msat_IntVecFree( p->vReason ); + Msat_IntVecFree( p->vTemp ); + FREE( p ); +} + +/**Function************************************************************* + + Synopsis [Prepares the solver to run on a subset of variables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverPrepare( Msat_Solver_t * p, Msat_IntVec_t * vVars ) +{ + + int i; + // undo the previous data + for ( i = 0; i < p->nVarsAlloc; i++ ) + { + p->pAssigns[i] = MSAT_VAR_UNASSIGNED; + p->pReasons[i] = NULL; + p->pLevel[i] = -1; + p->pdActivity[i] = 0.0; + } + + // set the new variable order + Msat_OrderClean( p->pOrder, vVars ); + + Msat_QueueClear( p->pQueue ); + Msat_IntVecClear( p->vTrail ); + Msat_IntVecClear( p->vTrailLim ); + p->dProgress = 0.0; +} + +/**Function************************************************************* + + Synopsis [Sets up the truth tables.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverSetupTruthTables( unsigned uTruths[][2] ) +{ + int m, v; + // set up the truth tables + for ( m = 0; m < 32; m++ ) + for ( v = 0; v < 5; v++ ) + if ( m & (1 << v) ) + uTruths[v][0] |= (1 << m); + // make adjustments for the case of 6 variables + for ( v = 0; v < 5; v++ ) + uTruths[v][1] = uTruths[v][0]; + uTruths[5][0] = 0; + uTruths[5][1] = ~((unsigned)0); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/msat/msatSolverCore.c b/abc_with_bb_support/src/sat/msat/msatSolverCore.c new file mode 100644 index 000000000..487faef93 --- /dev/null +++ b/abc_with_bb_support/src/sat/msat/msatSolverCore.c @@ -0,0 +1,212 @@ +/**CFile**************************************************************** + + FileName [msatSolverCore.c] + + PackageName [A C version of SAT solver MINISAT, originally developed + in C++ by Niklas Een and Niklas Sorensson, Chalmers University of + Technology, Sweden: http://www.cs.chalmers.se/~een/Satzoo.] + + Synopsis [The SAT solver core procedures.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 1, 2004.] + + Revision [$Id: msatSolverCore.c,v 1.2 2004/05/12 03:37:40 satrajit Exp $] + +***********************************************************************/ + +#include "msatInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Adds one variable to the solver.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Msat_SolverAddVar( Msat_Solver_t * p, int Level ) +{ + if ( p->nVars == p->nVarsAlloc ) + Msat_SolverResize( p, 2 * p->nVarsAlloc ); + p->pLevel[p->nVars] = Level; + p->nVars++; + return 1; +} + +/**Function************************************************************* + + Synopsis [Adds one clause to the solver's clause database.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Msat_SolverAddClause( Msat_Solver_t * p, Msat_IntVec_t * vLits ) +{ + Msat_Clause_t * pC; + bool Value; + Value = Msat_ClauseCreate( p, vLits, 0, &pC ); + if ( pC != NULL ) + Msat_ClauseVecPush( p->vClauses, pC ); +// else if ( p->fProof ) +// Msat_ClauseCreateFake( p, vLits ); + return Value; +} + +/**Function************************************************************* + + Synopsis [Returns search-space coverage. Not extremely reliable.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +double Msat_SolverProgressEstimate( Msat_Solver_t * p ) +{ + double dProgress = 0.0; + double dF = 1.0 / p->nVars; + int i; + for ( i = 0; i < p->nVars; i++ ) + if ( p->pAssigns[i] != MSAT_VAR_UNASSIGNED ) + dProgress += pow( dF, p->pLevel[i] ); + return dProgress / p->nVars; +} + +/**Function************************************************************* + + Synopsis [Prints statistics about the solver.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverPrintStats( Msat_Solver_t * p ) +{ + printf("C solver (%d vars; %d clauses; %d learned):\n", + p->nVars, Msat_ClauseVecReadSize(p->vClauses), Msat_ClauseVecReadSize(p->vLearned) ); + printf("starts : %lld\n", p->Stats.nStarts); + printf("conflicts : %lld\n", p->Stats.nConflicts); + printf("decisions : %lld\n", p->Stats.nDecisions); + printf("propagations : %lld\n", p->Stats.nPropagations); + printf("inspects : %lld\n", p->Stats.nInspects); +} + +/**Function************************************************************* + + Synopsis [Top-level solve.] + + Description [If using assumptions (non-empty 'assumps' vector), you must + call 'simplifyDB()' first to see that no top-level conflict is present + (which would put the solver in an undefined state. If the last argument + is given (vProj), the solver enumerates through the satisfying solutions, + which are projected on the variables listed in this array. Note that the + variables in the array may be complemented, in which case the derived + assignment for the variable is complemented.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Msat_SolverSolve( Msat_Solver_t * p, Msat_IntVec_t * vAssumps, int nBackTrackLimit, int nTimeLimit ) +{ + Msat_SearchParams_t Params = { 0.95, 0.999 }; + double nConflictsLimit, nLearnedLimit; + Msat_Type_t Status; + int timeStart = clock(); + int64 nConflictsOld = p->Stats.nConflicts; + int64 nDecisionsOld = p->Stats.nDecisions; + +// p->pFreq = ALLOC( int, p->nVarsAlloc ); +// memset( p->pFreq, 0, sizeof(int) * p->nVarsAlloc ); + + if ( vAssumps ) + { + int * pAssumps, nAssumps, i; + + assert( Msat_IntVecReadSize(p->vTrailLim) == 0 ); + + nAssumps = Msat_IntVecReadSize( vAssumps ); + pAssumps = Msat_IntVecReadArray( vAssumps ); + for ( i = 0; i < nAssumps; i++ ) + { + if ( !Msat_SolverAssume(p, pAssumps[i]) || Msat_SolverPropagate(p) ) + { + Msat_QueueClear( p->pQueue ); + Msat_SolverCancelUntil( p, 0 ); + return MSAT_FALSE; + } + } + } + p->nLevelRoot = Msat_SolverReadDecisionLevel(p); + p->nClausesInit = Msat_ClauseVecReadSize( p->vClauses ); + nConflictsLimit = 100; + nLearnedLimit = Msat_ClauseVecReadSize(p->vClauses) / 3; + Status = MSAT_UNKNOWN; + p->nBackTracks = (int)p->Stats.nConflicts; + while ( Status == MSAT_UNKNOWN ) + { + if ( p->fVerbose ) + printf("Solving -- conflicts=%d learnts=%d progress=%.4f %%\n", + (int)nConflictsLimit, (int)nLearnedLimit, p->dProgress*100); + Status = Msat_SolverSearch( p, (int)nConflictsLimit, (int)nLearnedLimit, nBackTrackLimit, &Params ); + nConflictsLimit *= 1.5; + nLearnedLimit *= 1.1; + // if the limit on the number of backtracks is given, quit the restart loop + if ( nBackTrackLimit > 0 && (int)p->Stats.nConflicts - p->nBackTracks > nBackTrackLimit ) + break; + // if the runtime limit is exceeded, quit the restart loop + if ( nTimeLimit > 0 && clock() - timeStart >= nTimeLimit * CLOCKS_PER_SEC ) + break; + } + Msat_SolverCancelUntil( p, 0 ); + p->nBackTracks = (int)p->Stats.nConflicts - p->nBackTracks; +/* + PRT( "True solver runtime", clock() - timeStart ); + // print the statistics + { + int i, Counter = 0; + for ( i = 0; i < p->nVars; i++ ) + if ( p->pFreq[i] > 0 ) + { + printf( "%d ", p->pFreq[i] ); + Counter++; + } + if ( Counter ) + printf( "\n" ); + printf( "Total = %d. Used = %d. Decisions = %d. Imps = %d. Conflicts = %d. ", p->nVars, Counter, (int)p->Stats.nDecisions, (int)p->Stats.nPropagations, (int)p->Stats.nConflicts ); + PRT( "Time", clock() - timeStart ); + } +*/ + return Status; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/msat/msatSolverIo.c b/abc_with_bb_support/src/sat/msat/msatSolverIo.c new file mode 100644 index 000000000..0315b1eb4 --- /dev/null +++ b/abc_with_bb_support/src/sat/msat/msatSolverIo.c @@ -0,0 +1,177 @@ +/**CFile**************************************************************** + + FileName [msatSolverIo.c] + + PackageName [A C version of SAT solver MINISAT, originally developed + in C++ by Niklas Een and Niklas Sorensson, Chalmers University of + Technology, Sweden: http://www.cs.chalmers.se/~een/Satzoo.] + + Synopsis [Input/output of CNFs.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 1, 2004.] + + Revision [$Id: msatSolverIo.c,v 1.0 2004/01/01 1:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "msatInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static char * Msat_TimeStamp(); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverPrintAssignment( Msat_Solver_t * p ) +{ + int i; + printf( "Current assignments are: \n" ); + for ( i = 0; i < p->nVars; i++ ) + printf( "%d", i % 10 ); + printf( "\n" ); + for ( i = 0; i < p->nVars; i++ ) + if ( p->pAssigns[i] == MSAT_VAR_UNASSIGNED ) + printf( "." ); + else + { + assert( i == MSAT_LIT2VAR(p->pAssigns[i]) ); + if ( MSAT_LITSIGN(p->pAssigns[i]) ) + printf( "0" ); + else + printf( "1" ); + } + printf( "\n" ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverPrintClauses( Msat_Solver_t * p ) +{ + Msat_Clause_t ** pClauses; + int nClauses, i; + + printf( "Original clauses: \n" ); + nClauses = Msat_ClauseVecReadSize( p->vClauses ); + pClauses = Msat_ClauseVecReadArray( p->vClauses ); + for ( i = 0; i < nClauses; i++ ) + { + printf( "%3d: ", i ); + Msat_ClausePrint( pClauses[i] ); + } + + printf( "Learned clauses: \n" ); + nClauses = Msat_ClauseVecReadSize( p->vLearned ); + pClauses = Msat_ClauseVecReadArray( p->vLearned ); + for ( i = 0; i < nClauses; i++ ) + { + printf( "%3d: ", i ); + Msat_ClausePrint( pClauses[i] ); + } + + printf( "Variable activity: \n" ); + for ( i = 0; i < p->nVars; i++ ) + printf( "%3d : %.4f\n", i, p->pdActivity[i] ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverWriteDimacs( Msat_Solver_t * p, char * pFileName ) +{ + FILE * pFile; + Msat_Clause_t ** pClauses; + int nClauses, i; + + nClauses = Msat_ClauseVecReadSize(p->vClauses) + Msat_ClauseVecReadSize(p->vLearned); + for ( i = 0; i < p->nVars; i++ ) + nClauses += ( p->pLevel[i] == 0 ); + + pFile = fopen( pFileName, "wb" ); + fprintf( pFile, "c Produced by Msat_SolverWriteDimacs() on %s\n", Msat_TimeStamp() ); + fprintf( pFile, "p cnf %d %d\n", p->nVars, nClauses ); + + nClauses = Msat_ClauseVecReadSize( p->vClauses ); + pClauses = Msat_ClauseVecReadArray( p->vClauses ); + for ( i = 0; i < nClauses; i++ ) + Msat_ClauseWriteDimacs( pFile, pClauses[i], 1 ); + + nClauses = Msat_ClauseVecReadSize( p->vLearned ); + pClauses = Msat_ClauseVecReadArray( p->vLearned ); + for ( i = 0; i < nClauses; i++ ) + Msat_ClauseWriteDimacs( pFile, pClauses[i], 1 ); + + // write zero-level assertions + for ( i = 0; i < p->nVars; i++ ) + if ( p->pLevel[i] == 0 ) + fprintf( pFile, "%s%d 0\n", ((p->pAssigns[i]&1)? "-": ""), i + 1 ); + + fprintf( pFile, "\n" ); + fclose( pFile ); +} + + +/**Function************************************************************* + + Synopsis [Returns the time stamp.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Msat_TimeStamp() +{ + static char Buffer[100]; + time_t ltime; + char * TimeStamp; + // get the current time + time( <ime ); + TimeStamp = asctime( localtime( <ime ) ); + TimeStamp[ strlen(TimeStamp) - 1 ] = 0; + strcpy( Buffer, TimeStamp ); + return Buffer; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/msat/msatSolverSearch.c b/abc_with_bb_support/src/sat/msat/msatSolverSearch.c new file mode 100644 index 000000000..08ccf2fd7 --- /dev/null +++ b/abc_with_bb_support/src/sat/msat/msatSolverSearch.c @@ -0,0 +1,629 @@ +/**CFile**************************************************************** + + FileName [msatSolverSearch.c] + + PackageName [A C version of SAT solver MINISAT, originally developed + in C++ by Niklas Een and Niklas Sorensson, Chalmers University of + Technology, Sweden: http://www.cs.chalmers.se/~een/Satzoo.] + + Synopsis [The search part of the solver.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 1, 2004.] + + Revision [$Id: msatSolverSearch.c,v 1.0 2004/01/01 1:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "msatInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static void Msat_SolverUndoOne( Msat_Solver_t * p ); +static void Msat_SolverCancel( Msat_Solver_t * p ); +static Msat_Clause_t * Msat_SolverRecord( Msat_Solver_t * p, Msat_IntVec_t * vLits ); +static void Msat_SolverAnalyze( Msat_Solver_t * p, Msat_Clause_t * pC, Msat_IntVec_t * vLits_out, int * pLevel_out ); +static void Msat_SolverReduceDB( Msat_Solver_t * p ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Makes the next assumption (Lit).] + + Description [Returns FALSE if immediate conflict.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Msat_SolverAssume( Msat_Solver_t * p, Msat_Lit_t Lit ) +{ + assert( Msat_QueueReadSize(p->pQueue) == 0 ); + if ( p->fVerbose ) + printf(L_IND"assume("L_LIT")\n", L_ind, L_lit(Lit)); + Msat_IntVecPush( p->vTrailLim, Msat_IntVecReadSize(p->vTrail) ); +// assert( Msat_IntVecReadSize(p->vTrailLim) <= Msat_IntVecReadSize(p->vTrail) + 1 ); +// assert( Msat_IntVecReadSize( p->vTrailLim ) < p->nVars ); + return Msat_SolverEnqueue( p, Lit, NULL ); +} + +/**Function************************************************************* + + Synopsis [Reverts one variable binding on the trail.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverUndoOne( Msat_Solver_t * p ) +{ + Msat_Lit_t Lit; + Msat_Var_t Var; + Lit = Msat_IntVecPop( p->vTrail ); + Var = MSAT_LIT2VAR(Lit); + p->pAssigns[Var] = MSAT_VAR_UNASSIGNED; + p->pReasons[Var] = NULL; + p->pLevel[Var] = -1; +// Msat_OrderUndo( p->pOrder, Var ); + Msat_OrderVarUnassigned( p->pOrder, Var ); + + if ( p->fVerbose ) + printf(L_IND"unbind("L_LIT")\n", L_ind, L_lit(Lit)); +} + +/**Function************************************************************* + + Synopsis [Reverts to the state before last Msat_SolverAssume().] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverCancel( Msat_Solver_t * p ) +{ + int c; + assert( Msat_QueueReadSize(p->pQueue) == 0 ); + if ( p->fVerbose ) + { + if ( Msat_IntVecReadSize(p->vTrail) != Msat_IntVecReadEntryLast(p->vTrailLim) ) + { + Msat_Lit_t Lit; + Lit = Msat_IntVecReadEntry( p->vTrail, Msat_IntVecReadEntryLast(p->vTrailLim) ); + printf(L_IND"cancel("L_LIT")\n", L_ind, L_lit(Lit)); + } + } + for ( c = Msat_IntVecReadSize(p->vTrail) - Msat_IntVecPop( p->vTrailLim ); c != 0; c-- ) + Msat_SolverUndoOne( p ); +} + +/**Function************************************************************* + + Synopsis [Reverts to the state at given level.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverCancelUntil( Msat_Solver_t * p, int Level ) +{ + while ( Msat_IntVecReadSize(p->vTrailLim) > Level ) + Msat_SolverCancel(p); +} + + +/**Function************************************************************* + + Synopsis [Record a clause and drive backtracking.] + + Description [vLits[0] must contain the asserting literal.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_Clause_t * Msat_SolverRecord( Msat_Solver_t * p, Msat_IntVec_t * vLits ) +{ + Msat_Clause_t * pC; + int Value; + assert( Msat_IntVecReadSize(vLits) != 0 ); + Value = Msat_ClauseCreate( p, vLits, 1, &pC ); + assert( Value ); + Value = Msat_SolverEnqueue( p, Msat_IntVecReadEntry(vLits,0), pC ); + assert( Value ); + if ( pC ) + Msat_ClauseVecPush( p->vLearned, pC ); + return pC; +} + +/**Function************************************************************* + + Synopsis [Enqueues one variable assignment.] + + Description [Puts a new fact on the propagation queue and immediately + updates the variable value. Should a conflict arise, FALSE is returned. + Otherwise returns TRUE.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Msat_SolverEnqueue( Msat_Solver_t * p, Msat_Lit_t Lit, Msat_Clause_t * pC ) +{ + Msat_Var_t Var = MSAT_LIT2VAR(Lit); + + // skip literals that are not in the current cone + if ( !Msat_IntVecReadEntry( p->vVarsUsed, Var ) ) + return 1; + +// assert( Msat_QueueReadSize(p->pQueue) == Msat_IntVecReadSize(p->vTrail) ); + // if the literal is assigned + // return 1 if the assignment is consistent + // return 0 if the assignment is inconsistent (conflict) + if ( p->pAssigns[Var] != MSAT_VAR_UNASSIGNED ) + return p->pAssigns[Var] == Lit; + // new fact - store it + if ( p->fVerbose ) + { +// printf(L_IND"bind("L_LIT")\n", L_ind, L_lit(Lit)); + printf(L_IND"bind("L_LIT") ", L_ind, L_lit(Lit)); + Msat_ClausePrintSymbols( pC ); + } + p->pAssigns[Var] = Lit; + p->pLevel[Var] = Msat_IntVecReadSize(p->vTrailLim); +// p->pReasons[Var] = p->pLevel[Var]? pC: NULL; + p->pReasons[Var] = pC; + Msat_IntVecPush( p->vTrail, Lit ); + Msat_QueueInsert( p->pQueue, Lit ); + + Msat_OrderVarAssigned( p->pOrder, Var ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Propagates the assignments in the queue.] + + Description [Propagates all enqueued facts. If a conflict arises, + the conflicting clause is returned, otherwise NULL.] + + SideEffects [The propagation queue is empty, even if there was a conflict.] + + SeeAlso [] + +***********************************************************************/ +Msat_Clause_t * Msat_SolverPropagate( Msat_Solver_t * p ) +{ + Msat_ClauseVec_t ** pvWatched = p->pvWatched; + Msat_Clause_t ** pClauses; + Msat_Clause_t * pConflict; + Msat_Lit_t Lit, Lit_out; + int i, j, nClauses; + + // propagate all the literals in the queue + while ( (Lit = Msat_QueueExtract( p->pQueue )) >= 0 ) + { + p->Stats.nPropagations++; + // get the clauses watched by this literal + nClauses = Msat_ClauseVecReadSize( pvWatched[Lit] ); + pClauses = Msat_ClauseVecReadArray( pvWatched[Lit] ); + // go through the watched clauses and decide what to do with them + for ( i = j = 0; i < nClauses; i++ ) + { + p->Stats.nInspects++; + // clear the returned literal + Lit_out = -1; + // propagate the clause + if ( !Msat_ClausePropagate( pClauses[i], Lit, p->pAssigns, &Lit_out ) ) + { // the clause is unit + // "Lit_out" contains the new assignment to be enqueued + if ( Msat_SolverEnqueue( p, Lit_out, pClauses[i] ) ) + { // consistent assignment + // no changes to the implication queue; the watch is the same too + pClauses[j++] = pClauses[i]; + continue; + } + // remember the reason of conflict (will be returned) + pConflict = pClauses[i]; + // leave the remaning clauses in the same watched list + for ( ; i < nClauses; i++ ) + pClauses[j++] = pClauses[i]; + Msat_ClauseVecShrink( pvWatched[Lit], j ); + // clear the propagation queue + Msat_QueueClear( p->pQueue ); + return pConflict; + } + // the clause is not unit + // in this case "Lit_out" contains the new watch if it has changed + if ( Lit_out >= 0 ) + Msat_ClauseVecPush( pvWatched[Lit_out], pClauses[i] ); + else // the watch did not change + pClauses[j++] = pClauses[i]; + } + Msat_ClauseVecShrink( pvWatched[Lit], j ); + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [Simplifies the data base.] + + Description [Simplify all constraints according to the current top-level + assigment (redundant constraints may be removed altogether).] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bool Msat_SolverSimplifyDB( Msat_Solver_t * p ) +{ + Msat_ClauseVec_t * vClauses; + Msat_Clause_t ** pClauses; + int nClauses, Type, i, j; + int * pAssigns; + int Counter; + + assert( Msat_SolverReadDecisionLevel(p) == 0 ); + if ( Msat_SolverPropagate(p) != NULL ) + return 0; +//Msat_SolverPrintClauses( p ); +//Msat_SolverPrintAssignment( p ); +//printf( "Simplification\n" ); + + // simplify and reassign clause numbers + Counter = 0; + pAssigns = Msat_SolverReadAssignsArray( p ); + for ( Type = 0; Type < 2; Type++ ) + { + vClauses = Type? p->vLearned : p->vClauses; + nClauses = Msat_ClauseVecReadSize( vClauses ); + pClauses = Msat_ClauseVecReadArray( vClauses ); + for ( i = j = 0; i < nClauses; i++ ) + if ( Msat_ClauseSimplify( pClauses[i], pAssigns ) ) + Msat_ClauseFree( p, pClauses[i], 1 ); + else + { + pClauses[j++] = pClauses[i]; + Msat_ClauseSetNum( pClauses[i], Counter++ ); + } + Msat_ClauseVecShrink( vClauses, j ); + } + p->nClauses = Counter; + return 1; +} + +/**Function************************************************************* + + Synopsis [Cleans the clause databased from the useless learnt clauses.] + + Description [Removes half of the learnt clauses, minus the clauses locked + by the current assignment. Locked clauses are clauses that are reason + to a some assignment.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverReduceDB( Msat_Solver_t * p ) +{ + Msat_Clause_t ** pLearned; + int nLearned, i, j; + double dExtraLim = p->dClaInc / Msat_ClauseVecReadSize(p->vLearned); + // Remove any clause below this activity + + // sort the learned clauses in the increasing order of activity + Msat_SolverSortDB( p ); + + // discard the first half the clauses (the less active ones) + nLearned = Msat_ClauseVecReadSize( p->vLearned ); + pLearned = Msat_ClauseVecReadArray( p->vLearned ); + for ( i = j = 0; i < nLearned / 2; i++ ) + if ( !Msat_ClauseIsLocked( p, pLearned[i]) ) + Msat_ClauseFree( p, pLearned[i], 1 ); + else + pLearned[j++] = pLearned[i]; + // filter the more active clauses and leave those above the limit + for ( ; i < nLearned; i++ ) + if ( !Msat_ClauseIsLocked( p, pLearned[i] ) && + Msat_ClauseReadActivity(pLearned[i]) < dExtraLim ) + Msat_ClauseFree( p, pLearned[i], 1 ); + else + pLearned[j++] = pLearned[i]; + Msat_ClauseVecShrink( p->vLearned, j ); +} + +/**Function************************************************************* + + Synopsis [Removes the learned clauses.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverRemoveLearned( Msat_Solver_t * p ) +{ + Msat_Clause_t ** pLearned; + int nLearned, i; + + // discard the learned clauses + nLearned = Msat_ClauseVecReadSize( p->vLearned ); + pLearned = Msat_ClauseVecReadArray( p->vLearned ); + for ( i = 0; i < nLearned; i++ ) + { + assert( !Msat_ClauseIsLocked( p, pLearned[i]) ); + + Msat_ClauseFree( p, pLearned[i], 1 ); + } + Msat_ClauseVecShrink( p->vLearned, 0 ); + p->nClauses = Msat_ClauseVecReadSize(p->vClauses); + + for ( i = 0; i < p->nVarsAlloc; i++ ) + p->pReasons[i] = NULL; +} + +/**Function************************************************************* + + Synopsis [Removes the recently added clauses.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverRemoveMarked( Msat_Solver_t * p ) +{ + Msat_Clause_t ** pLearned, ** pClauses; + int nLearned, nClauses, i; + + // discard the learned clauses + nClauses = Msat_ClauseVecReadSize( p->vClauses ); + pClauses = Msat_ClauseVecReadArray( p->vClauses ); + for ( i = p->nClausesStart; i < nClauses; i++ ) + { +// assert( !Msat_ClauseIsLocked( p, pClauses[i]) ); + Msat_ClauseFree( p, pClauses[i], 1 ); + } + Msat_ClauseVecShrink( p->vClauses, p->nClausesStart ); + + // discard the learned clauses + nLearned = Msat_ClauseVecReadSize( p->vLearned ); + pLearned = Msat_ClauseVecReadArray( p->vLearned ); + for ( i = 0; i < nLearned; i++ ) + { +// assert( !Msat_ClauseIsLocked( p, pLearned[i]) ); + Msat_ClauseFree( p, pLearned[i], 1 ); + } + Msat_ClauseVecShrink( p->vLearned, 0 ); + p->nClauses = Msat_ClauseVecReadSize(p->vClauses); +/* + // undo the previous data + for ( i = 0; i < p->nVarsAlloc; i++ ) + { + p->pAssigns[i] = MSAT_VAR_UNASSIGNED; + p->pReasons[i] = NULL; + p->pLevel[i] = -1; + p->pdActivity[i] = 0.0; + } + Msat_OrderClean( p->pOrder, p->nVars, NULL ); + Msat_QueueClear( p->pQueue ); +*/ +} + + + +/**Function************************************************************* + + Synopsis [Analyze conflict and produce a reason clause.] + + Description [Current decision level must be greater than root level.] + + SideEffects [vLits_out[0] is the asserting literal at level pLevel_out.] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverAnalyze( Msat_Solver_t * p, Msat_Clause_t * pC, Msat_IntVec_t * vLits_out, int * pLevel_out ) +{ + Msat_Lit_t LitQ, Lit = MSAT_LIT_UNASSIGNED; + Msat_Var_t VarQ, Var; + int * pReasonArray, nReasonSize; + int j, pathC = 0, nLevelCur = Msat_IntVecReadSize(p->vTrailLim); + int iStep = Msat_IntVecReadSize(p->vTrail) - 1; + + // increment the seen counter + p->nSeenId++; + // empty the vector array + Msat_IntVecClear( vLits_out ); + Msat_IntVecPush( vLits_out, -1 ); // (leave room for the asserting literal) + *pLevel_out = 0; + do { + assert( pC != NULL ); // (otherwise should be UIP) + // get the reason of conflict + Msat_ClauseCalcReason( p, pC, Lit, p->vReason ); + nReasonSize = Msat_IntVecReadSize( p->vReason ); + pReasonArray = Msat_IntVecReadArray( p->vReason ); + for ( j = 0; j < nReasonSize; j++ ) { + LitQ = pReasonArray[j]; + VarQ = MSAT_LIT2VAR(LitQ); + if ( p->pSeen[VarQ] != p->nSeenId ) { + p->pSeen[VarQ] = p->nSeenId; + + // added to better fine-tune the search + Msat_SolverVarBumpActivity( p, LitQ ); + + // skip all the literals on this decision level + if ( p->pLevel[VarQ] == nLevelCur ) + pathC++; + else if ( p->pLevel[VarQ] > 0 ) { + // add the literals on other decision levels but + // exclude variables from decision level 0 + Msat_IntVecPush( vLits_out, MSAT_LITNOT(LitQ) ); + if ( *pLevel_out < p->pLevel[VarQ] ) + *pLevel_out = p->pLevel[VarQ]; + } + } + } + // Select next clause to look at: + do { +// Lit = Msat_IntVecReadEntryLast(p->vTrail); + Lit = Msat_IntVecReadEntry( p->vTrail, iStep-- ); + Var = MSAT_LIT2VAR(Lit); + pC = p->pReasons[Var]; +// Msat_SolverUndoOne( p ); + } while ( p->pSeen[Var] != p->nSeenId ); + pathC--; + } while ( pathC > 0 ); + // we do not unbind the variables above + // this will be done after conflict analysis + + Msat_IntVecWriteEntry( vLits_out, 0, MSAT_LITNOT(Lit) ); + if ( p->fVerbose ) + { + printf( L_IND"Learnt {", L_ind ); + nReasonSize = Msat_IntVecReadSize( vLits_out ); + pReasonArray = Msat_IntVecReadArray( vLits_out ); + for ( j = 0; j < nReasonSize; j++ ) + printf(" "L_LIT, L_lit(pReasonArray[j])); + printf(" } at level %d\n", *pLevel_out); + } +} + +/**Function************************************************************* + + Synopsis [The search procedure called between the restarts.] + + Description [Search for a satisfying solution as long as the number of + conflicts does not exceed the limit (nConfLimit) while keeping the number + of learnt clauses below the provided limit (nLearnedLimit). NOTE! Use + negative value for nConfLimit or nLearnedLimit to indicate infinity.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_Type_t Msat_SolverSearch( Msat_Solver_t * p, int nConfLimit, int nLearnedLimit, int nBackTrackLimit, Msat_SearchParams_t * pPars ) +{ + Msat_Clause_t * pConf; + Msat_Var_t Var; + int nLevelBack, nConfs, nAssigns, Value; + int i; + + assert( Msat_SolverReadDecisionLevel(p) == p->nLevelRoot ); + p->Stats.nStarts++; + p->dVarDecay = 1 / pPars->dVarDecay; + p->dClaDecay = 1 / pPars->dClaDecay; + + // reset the activities + for ( i = 0; i < p->nVars; i++ ) + p->pdActivity[i] = (double)p->pFactors[i]; +// p->pdActivity[i] = 0.0; + + nConfs = 0; + while ( 1 ) + { + pConf = Msat_SolverPropagate( p ); + if ( pConf != NULL ){ + // CONFLICT + if ( p->fVerbose ) + { +// printf(L_IND"**CONFLICT**\n", L_ind); + printf(L_IND"**CONFLICT** ", L_ind); + Msat_ClausePrintSymbols( pConf ); + } + // count conflicts + p->Stats.nConflicts++; + nConfs++; + + // if top level, return UNSAT + if ( Msat_SolverReadDecisionLevel(p) == p->nLevelRoot ) + return MSAT_FALSE; + + // perform conflict analysis + Msat_SolverAnalyze( p, pConf, p->vTemp, &nLevelBack ); + Msat_SolverCancelUntil( p, (p->nLevelRoot > nLevelBack)? p->nLevelRoot : nLevelBack ); + Msat_SolverRecord( p, p->vTemp ); + + // it is important that recording is done after cancelling + // because canceling cleans the queue while recording adds to it + Msat_SolverVarDecayActivity( p ); + Msat_SolverClaDecayActivity( p ); + + } + else{ + // NO CONFLICT + if ( Msat_IntVecReadSize(p->vTrailLim) == 0 ) { + // Simplify the set of problem clauses: +// Value = Msat_SolverSimplifyDB(p); +// assert( Value ); + } + nAssigns = Msat_IntVecReadSize( p->vTrail ); + if ( nLearnedLimit >= 0 && Msat_ClauseVecReadSize(p->vLearned) >= nLearnedLimit + nAssigns ) { + // Reduce the set of learnt clauses: + Msat_SolverReduceDB(p); + } + + Var = Msat_OrderVarSelect( p->pOrder ); + if ( Var == MSAT_ORDER_UNKNOWN ) { + // Model found and stored in p->pAssigns + memcpy( p->pModel, p->pAssigns, sizeof(int) * p->nVars ); + Msat_QueueClear( p->pQueue ); + Msat_SolverCancelUntil( p, p->nLevelRoot ); + return MSAT_TRUE; + } + if ( nConfLimit > 0 && nConfs > nConfLimit ) { + // Reached bound on number of conflicts: + p->dProgress = Msat_SolverProgressEstimate( p ); + Msat_QueueClear( p->pQueue ); + Msat_SolverCancelUntil( p, p->nLevelRoot ); + return MSAT_UNKNOWN; + } + else if ( nBackTrackLimit > 0 && (int)p->Stats.nConflicts - p->nBackTracks > nBackTrackLimit ) { + // Reached bound on number of conflicts: + Msat_QueueClear( p->pQueue ); + Msat_SolverCancelUntil( p, p->nLevelRoot ); + return MSAT_UNKNOWN; + } + else{ + // New variable decision: + p->Stats.nDecisions++; + assert( Var != MSAT_ORDER_UNKNOWN && Var >= 0 && Var < p->nVars ); + Value = Msat_SolverAssume(p, MSAT_VAR2LIT(Var,0) ); + assert( Value ); + } + } + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/msat/msatSort.c b/abc_with_bb_support/src/sat/msat/msatSort.c new file mode 100644 index 000000000..2d5483b13 --- /dev/null +++ b/abc_with_bb_support/src/sat/msat/msatSort.c @@ -0,0 +1,173 @@ +/**CFile**************************************************************** + + FileName [msatSort.c] + + PackageName [A C version of SAT solver MINISAT, originally developed + in C++ by Niklas Een and Niklas Sorensson, Chalmers University of + Technology, Sweden: http://www.cs.chalmers.se/~een/Satzoo.] + + Synopsis [Sorting clauses.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 1, 2004.] + + Revision [$Id: msatSort.c,v 1.0 2004/01/01 1:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "msatInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Msat_SolverSortCompare( Msat_Clause_t ** ppC1, Msat_Clause_t ** ppC2 ); + +// Returns a random float 0 <= x < 1. Seed must never be 0. +static double drand(double seed) { + int q; + seed *= 1389796; + q = (int)(seed / 2147483647); + seed -= (double)q * 2147483647; + return seed / 2147483647; } + +// Returns a random integer 0 <= x < size. Seed must never be 0. +static int irand(double seed, int size) { + return (int)(drand(seed) * size); } + +static void Msat_SolverSort( Msat_Clause_t ** array, int size, double seed ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Msat_SolverSort the learned clauses in the increasing order of activity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverSortDB( Msat_Solver_t * p ) +{ + Msat_ClauseVec_t * pVecClauses; + Msat_Clause_t ** pLearned; + int nLearned; + // read the parameters + pVecClauses = Msat_SolverReadLearned( p ); + nLearned = Msat_ClauseVecReadSize( pVecClauses ); + pLearned = Msat_ClauseVecReadArray( pVecClauses ); + // Msat_SolverSort the array +// qMsat_SolverSort( (void *)pLearned, nLearned, sizeof(Msat_Clause_t *), +// (int (*)(const void *, const void *)) Msat_SolverSortCompare ); +// printf( "Msat_SolverSorting.\n" ); + Msat_SolverSort( pLearned, nLearned, 91648253 ); +/* + if ( nLearned > 2 ) + { + printf( "Clause 1: %0.20f\n", Msat_ClauseReadActivity(pLearned[0]) ); + printf( "Clause 2: %0.20f\n", Msat_ClauseReadActivity(pLearned[1]) ); + printf( "Clause 3: %0.20f\n", Msat_ClauseReadActivity(pLearned[2]) ); + } +*/ +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two clauses.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Msat_SolverSortCompare( Msat_Clause_t ** ppC1, Msat_Clause_t ** ppC2 ) +{ + float Value1 = Msat_ClauseReadActivity( *ppC1 ); + float Value2 = Msat_ClauseReadActivity( *ppC2 ); + if ( Value1 < Value2 ) + return -1; + if ( Value1 > Value2 ) + return 1; + return 0; +} + + +/**Function************************************************************* + + Synopsis [Selection sort for small array size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverSortSelection( Msat_Clause_t ** array, int size ) +{ + Msat_Clause_t * tmp; + int i, j, best_i; + for ( i = 0; i < size-1; i++ ) + { + best_i = i; + for (j = i+1; j < size; j++) + { + if ( Msat_ClauseReadActivity(array[j]) < Msat_ClauseReadActivity(array[best_i]) ) + best_i = j; + } + tmp = array[i]; array[i] = array[best_i]; array[best_i] = tmp; + } +} + +/**Function************************************************************* + + Synopsis [The original MiniSat sorting procedure.] + + Description [This procedure is used to preserve trace-equivalence + with the orignal C++ implemenation of the solver.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_SolverSort( Msat_Clause_t ** array, int size, double seed ) +{ + if (size <= 15) + Msat_SolverSortSelection( array, size ); + else + { + Msat_Clause_t * pivot = array[irand(seed, size)]; + Msat_Clause_t * tmp; + int i = -1; + int j = size; + + for(;;) + { + do i++; while( Msat_ClauseReadActivity(array[i]) < Msat_ClauseReadActivity(pivot) ); + do j--; while( Msat_ClauseReadActivity(pivot) < Msat_ClauseReadActivity(array[j]) ); + + if ( i >= j ) break; + + tmp = array[i]; array[i] = array[j]; array[j] = tmp; + } + Msat_SolverSort(array , i , seed); + Msat_SolverSort(&array[i], size-i, seed); + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/msat/msatVec.c b/abc_with_bb_support/src/sat/msat/msatVec.c new file mode 100644 index 000000000..bb4e0c76f --- /dev/null +++ b/abc_with_bb_support/src/sat/msat/msatVec.c @@ -0,0 +1,495 @@ +/**CFile**************************************************************** + + FileName [msatVec.c] + + PackageName [A C version of SAT solver MINISAT, originally developed + in C++ by Niklas Een and Niklas Sorensson, Chalmers University of + Technology, Sweden: http://www.cs.chalmers.se/~een/Satzoo.] + + Synopsis [Integer vector borrowed from Extra.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 1, 2004.] + + Revision [$Id: msatVec.c,v 1.0 2004/01/01 1:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "msatInt.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +static int Msat_IntVecSortCompare1( int * pp1, int * pp2 ); +static int Msat_IntVecSortCompare2( int * pp1, int * pp2 ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_IntVec_t * Msat_IntVecAlloc( int nCap ) +{ + Msat_IntVec_t * p; + p = ALLOC( Msat_IntVec_t, 1 ); + if ( nCap > 0 && nCap < 16 ) + nCap = 16; + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ALLOC( int, p->nCap ) : NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [Creates the vector from an integer array of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_IntVec_t * Msat_IntVecAllocArray( int * pArray, int nSize ) +{ + Msat_IntVec_t * p; + p = ALLOC( Msat_IntVec_t, 1 ); + p->nSize = nSize; + p->nCap = nSize; + p->pArray = pArray; + return p; +} + +/**Function************************************************************* + + Synopsis [Creates the vector from an integer array of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_IntVec_t * Msat_IntVecAllocArrayCopy( int * pArray, int nSize ) +{ + Msat_IntVec_t * p; + p = ALLOC( Msat_IntVec_t, 1 ); + p->nSize = nSize; + p->nCap = nSize; + p->pArray = ALLOC( int, nSize ); + memcpy( p->pArray, pArray, sizeof(int) * nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Duplicates the integer array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_IntVec_t * Msat_IntVecDup( Msat_IntVec_t * pVec ) +{ + Msat_IntVec_t * p; + p = ALLOC( Msat_IntVec_t, 1 ); + p->nSize = pVec->nSize; + p->nCap = pVec->nCap; + p->pArray = p->nCap? ALLOC( int, p->nCap ) : NULL; + memcpy( p->pArray, pVec->pArray, sizeof(int) * pVec->nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Transfers the array into another vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Msat_IntVec_t * Msat_IntVecDupArray( Msat_IntVec_t * pVec ) +{ + Msat_IntVec_t * p; + p = ALLOC( Msat_IntVec_t, 1 ); + p->nSize = pVec->nSize; + p->nCap = pVec->nCap; + p->pArray = pVec->pArray; + pVec->nSize = 0; + pVec->nCap = 0; + pVec->pArray = NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_IntVecFree( Msat_IntVec_t * p ) +{ + FREE( p->pArray ); + FREE( p ); +} + +/**Function************************************************************* + + Synopsis [Fills the vector with given number of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_IntVecFill( Msat_IntVec_t * p, int nSize, int Entry ) +{ + int i; + Msat_IntVecGrow( p, nSize ); + p->nSize = nSize; + for ( i = 0; i < p->nSize; i++ ) + p->pArray[i] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int * Msat_IntVecReleaseArray( Msat_IntVec_t * p ) +{ + int * pArray = p->pArray; + p->nCap = 0; + p->nSize = 0; + p->pArray = NULL; + return pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int * Msat_IntVecReadArray( Msat_IntVec_t * p ) +{ + return p->pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Msat_IntVecReadSize( Msat_IntVec_t * p ) +{ + return p->nSize; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Msat_IntVecReadEntry( Msat_IntVec_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray[i]; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_IntVecWriteEntry( Msat_IntVec_t * p, int i, int Entry ) +{ + assert( i >= 0 && i < p->nSize ); + p->pArray[i] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Msat_IntVecReadEntryLast( Msat_IntVec_t * p ) +{ + return p->pArray[p->nSize-1]; +} + +/**Function************************************************************* + + Synopsis [Resizes the vector to the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_IntVecGrow( Msat_IntVec_t * p, int nCapMin ) +{ + if ( p->nCap >= nCapMin ) + return; + p->pArray = REALLOC( int, p->pArray, nCapMin ); + p->nCap = nCapMin; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_IntVecShrink( Msat_IntVec_t * p, int nSizeNew ) +{ + assert( p->nSize >= nSizeNew ); + p->nSize = nSizeNew; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_IntVecClear( Msat_IntVec_t * p ) +{ + p->nSize = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_IntVecPush( Msat_IntVec_t * p, int Entry ) +{ + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Msat_IntVecGrow( p, 16 ); + else + Msat_IntVecGrow( p, 2 * p->nCap ); + } + p->pArray[p->nSize++] = Entry; +} + +/**Function************************************************************* + + Synopsis [Add the element while ensuring uniqueness.] + + Description [Returns 1 if the element was found, and 0 if it was new. ] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Msat_IntVecPushUnique( Msat_IntVec_t * p, int Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return 1; + Msat_IntVecPush( p, Entry ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Inserts the element while sorting in the increasing order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_IntVecPushUniqueOrder( Msat_IntVec_t * p, int Entry, int fIncrease ) +{ + int Entry1, Entry2; + int i; + Msat_IntVecPushUnique( p, Entry ); + // find the p of the node + for ( i = p->nSize-1; i > 0; i-- ) + { + Entry1 = p->pArray[i ]; + Entry2 = p->pArray[i-1]; + if ( fIncrease && Entry1 >= Entry2 || + !fIncrease && Entry1 <= Entry2 ) + break; + p->pArray[i ] = Entry2; + p->pArray[i-1] = Entry1; + } +} + + +/**Function************************************************************* + + Synopsis [Returns the last entry and removes it from the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Msat_IntVecPop( Msat_IntVec_t * p ) +{ + assert( p->nSize > 0 ); + return p->pArray[--p->nSize]; +} + +/**Function************************************************************* + + Synopsis [Sorting the entries by their integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Msat_IntVecSort( Msat_IntVec_t * p, int fReverse ) +{ + if ( fReverse ) + qsort( (void *)p->pArray, p->nSize, sizeof(int), + (int (*)(const void *, const void *)) Msat_IntVecSortCompare2 ); + else + qsort( (void *)p->pArray, p->nSize, sizeof(int), + (int (*)(const void *, const void *)) Msat_IntVecSortCompare1 ); +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two clauses.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Msat_IntVecSortCompare1( int * pp1, int * pp2 ) +{ + // for some reason commenting out lines (as shown) led to crashing of the release version + if ( *pp1 < *pp2 ) + return -1; + if ( *pp1 > *pp2 ) // + return 1; + return 0; // +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two clauses.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Msat_IntVecSortCompare2( int * pp1, int * pp2 ) +{ + // for some reason commenting out lines (as shown) led to crashing of the release version + if ( *pp1 > *pp2 ) + return -1; + if ( *pp1 < *pp2 ) // + return 1; + return 0; // +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/proof/pr.c b/abc_with_bb_support/src/sat/proof/pr.c new file mode 100644 index 000000000..806b7700e --- /dev/null +++ b/abc_with_bb_support/src/sat/proof/pr.c @@ -0,0 +1,1263 @@ +/**CFile**************************************************************** + + FileName [pr.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Proof recording.] + + Synopsis [Core procedures of the package.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: pr.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include +#include +#include +#include +#include +//#include "vec.h" +#include "pr.h" + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef unsigned lit; + +typedef struct Pr_Cls_t_ Pr_Cls_t; +struct Pr_Cls_t_ +{ + unsigned uTruth; // interpolant + void * pProof; // the proof node +// void * pAntis; // the anticedents + Pr_Cls_t * pNext; // the next clause + Pr_Cls_t * pNext0; // the next 0-watch + Pr_Cls_t * pNext1; // the next 0-watch + int Id; // the clause ID + unsigned fA : 1; // belongs to A + unsigned fRoot : 1; // original clause + unsigned fVisit : 1; // visited clause + unsigned nLits : 24; // the number of literals + lit pLits[0]; // literals of this clause +}; + +struct Pr_Man_t_ +{ + // general data + int fProofWrite; // writes the proof file + int fProofVerif; // verifies the proof + int nVars; // the number of variables + int nVarsAB; // the number of global variables + int nRoots; // the number of root clauses + int nClauses; // the number of all clauses + int nClausesA; // the number of clauses of A + Pr_Cls_t * pHead; // the head clause + Pr_Cls_t * pTail; // the tail clause + Pr_Cls_t * pLearnt; // the tail clause + Pr_Cls_t * pEmpty; // the empty clause + // internal BCP + int nRootSize; // the number of root level assignments + int nTrailSize; // the number of assignments made + lit * pTrail; // chronological order of assignments (size nVars) + lit * pAssigns; // assignments by variable (size nVars) + char * pSeens; // temporary mark (size nVars) + char * pVarTypes; // variable type (size nVars) [1=A, 0=B, <0=AB] + Pr_Cls_t ** pReasons; // reasons for each assignment (size nVars) + Pr_Cls_t ** pWatches; // watched clauses for each literal (size 2*nVars) + int nVarsAlloc; // the allocated size of arrays + // proof recording + void * pManProof; // proof manager + int Counter; // counter of resolved clauses + // memory management + int nChunkSize; // the number of bytes in a chunk + int nChunkUsed; // the number of bytes used in the last chunk + char * pChunkLast; // the last memory chunk + // internal verification + lit * pResLits; // the literals of the resolvent + int nResLits; // the number of literals of the resolvent + int nResLitsAlloc;// the number of literals of the resolvent + // runtime stats + int timeBcp; + int timeTrace; + int timeRead; + int timeTotal; +}; + +#ifndef PRT +#define PRT(a,t) printf("%s = ", (a)); printf("%6.2f sec\n", (float)(t)/(float)(CLOCKS_PER_SEC)) +#endif + +// variable assignments +static const lit LIT_UNDEF = 0xffffffff; + +// variable/literal conversions (taken from MiniSat) +static inline lit toLit (int v) { return v + v; } +static inline lit toLitCond(int v, int c) { return v + v + (c != 0); } +static inline lit lit_neg (lit l) { return l ^ 1; } +static inline int lit_var (lit l) { return l >> 1; } +static inline int lit_sign (lit l) { return l & 1; } +static inline int lit_print(lit l) { return lit_sign(l)? -lit_var(l)-1 : lit_var(l)+1; } +static inline lit lit_read (int s) { return s > 0 ? toLit(s-1) : lit_neg(toLit(-s-1)); } +static inline int lit_check(lit l, int n) { return l >= 0 && lit_var(l) < n; } + +// iterators through the clauses +#define Pr_ManForEachClause( p, pCls ) for( pCls = p->pHead; pCls; pCls = pCls->pNext ) +#define Pr_ManForEachClauseRoot( p, pCls ) for( pCls = p->pHead; pCls != p->pLearnt; pCls = pCls->pNext ) +#define Pr_ManForEachClauseLearnt( p, pCls ) for( pCls = p->pLearnt; pCls; pCls = pCls->pNext ) + +// static procedures +static char * Pr_ManMemoryFetch( Pr_Man_t * p, int nBytes ); +static void Pr_ManMemoryStop( Pr_Man_t * p ); +static void Pr_ManResize( Pr_Man_t * p, int nVarsNew ); + +// exported procedures +extern Pr_Man_t * Pr_ManAlloc( int nVarsAlloc ); +extern void Pr_ManFree( Pr_Man_t * p ); +extern int Pr_ManAddClause( Pr_Man_t * p, lit * pBeg, lit * pEnd, int fRoot, int fClauseA ); +extern int Pr_ManProofWrite( Pr_Man_t * p ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocate proof manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Pr_Man_t * Pr_ManAlloc( int nVarsAlloc ) +{ + Pr_Man_t * p; + // allocate the manager + p = (Pr_Man_t *)malloc( sizeof(Pr_Man_t) ); + memset( p, 0, sizeof(Pr_Man_t) ); + // allocate internal arrays + Pr_ManResize( p, nVarsAlloc? nVarsAlloc : 256 ); + // set the starting number of variables + p->nVars = 0; + // memory management + p->nChunkSize = (1<<16); // use 64K chunks + // verification + p->nResLitsAlloc = (1<<16); + p->pResLits = malloc( sizeof(lit) * p->nResLitsAlloc ); + // parameters + p->fProofWrite = 0; + p->fProofVerif = 0; + return p; +} + +/**Function************************************************************* + + Synopsis [Resize proof manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Pr_ManResize( Pr_Man_t * p, int nVarsNew ) +{ + // check if resizing is needed + if ( p->nVarsAlloc < nVarsNew ) + { + int nVarsAllocOld = p->nVarsAlloc; + // find the new size + if ( p->nVarsAlloc == 0 ) + p->nVarsAlloc = 1; + while ( p->nVarsAlloc < nVarsNew ) + p->nVarsAlloc *= 2; + // resize the arrays + p->pTrail = (lit *) realloc( p->pTrail, sizeof(lit) * p->nVarsAlloc ); + p->pAssigns = (lit *) realloc( p->pAssigns, sizeof(lit) * p->nVarsAlloc ); + p->pSeens = (char *) realloc( p->pSeens, sizeof(char) * p->nVarsAlloc ); + p->pVarTypes = (char *) realloc( p->pVarTypes, sizeof(char) * p->nVarsAlloc ); + p->pReasons = (Pr_Cls_t **)realloc( p->pReasons, sizeof(Pr_Cls_t *) * p->nVarsAlloc ); + p->pWatches = (Pr_Cls_t **)realloc( p->pWatches, sizeof(Pr_Cls_t *) * p->nVarsAlloc*2 ); + // clean the free space + memset( p->pAssigns + nVarsAllocOld, 0xff, sizeof(lit) * (p->nVarsAlloc - nVarsAllocOld) ); + memset( p->pSeens + nVarsAllocOld, 0, sizeof(char) * (p->nVarsAlloc - nVarsAllocOld) ); + memset( p->pVarTypes + nVarsAllocOld, 0, sizeof(char) * (p->nVarsAlloc - nVarsAllocOld) ); + memset( p->pReasons + nVarsAllocOld, 0, sizeof(Pr_Cls_t *) * (p->nVarsAlloc - nVarsAllocOld) ); + memset( p->pWatches + nVarsAllocOld, 0, sizeof(Pr_Cls_t *) * (p->nVarsAlloc - nVarsAllocOld)*2 ); + } + // adjust the number of variables + if ( p->nVars < nVarsNew ) + p->nVars = nVarsNew; +} + +/**Function************************************************************* + + Synopsis [Deallocate proof manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Pr_ManFree( Pr_Man_t * p ) +{ + printf( "Runtime stats:\n" ); +PRT( "Reading ", p->timeRead ); +PRT( "BCP ", p->timeBcp ); +PRT( "Trace ", p->timeTrace ); +PRT( "TOTAL ", p->timeTotal ); + + Pr_ManMemoryStop( p ); + free( p->pTrail ); + free( p->pAssigns ); + free( p->pSeens ); + free( p->pVarTypes ); + free( p->pReasons ); + free( p->pWatches ); + free( p->pResLits ); + free( p ); +} + +/**Function************************************************************* + + Synopsis [Adds one clause to the watcher list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Pr_ManWatchClause( Pr_Man_t * p, Pr_Cls_t * pClause, lit Lit ) +{ + assert( lit_check(Lit, p->nVars) ); + if ( pClause->pLits[0] == Lit ) + pClause->pNext0 = p->pWatches[lit_neg(Lit)]; + else + { + assert( pClause->pLits[1] == Lit ); + pClause->pNext1 = p->pWatches[lit_neg(Lit)]; + } + p->pWatches[lit_neg(Lit)] = pClause; +} + +/**Function************************************************************* + + Synopsis [Adds one clause to the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Pr_ManAddClause( Pr_Man_t * p, lit * pBeg, lit * pEnd, int fRoot, int fClauseA ) +{ + Pr_Cls_t * pClause; + lit Lit, * i, * j; + int nSize, VarMax; + + // process the literals + if ( pBeg < pEnd ) + { + // insertion sort + VarMax = lit_var( *pBeg ); + for ( i = pBeg + 1; i < pEnd; i++ ) + { + Lit = *i; + VarMax = lit_var(Lit) > VarMax ? lit_var(Lit) : VarMax; + for ( j = i; j > pBeg && *(j-1) > Lit; j-- ) + *j = *(j-1); + *j = Lit; + } + // make sure there is no duplicated variables + for ( i = pBeg + 1; i < pEnd; i++ ) + assert( lit_var(*(i-1)) != lit_var(*i) ); + // resize the manager + Pr_ManResize( p, VarMax+1 ); + } + + // get memory for the clause + nSize = sizeof(Pr_Cls_t) + sizeof(lit) * (pEnd - pBeg); + pClause = (Pr_Cls_t *)Pr_ManMemoryFetch( p, nSize ); + memset( pClause, 0, sizeof(Pr_Cls_t) ); + + // assign the clause + assert( !fClauseA || fRoot ); // clause of A is always a root clause + p->nRoots += fRoot; + p->nClausesA += fClauseA; + pClause->Id = p->nClauses++; + pClause->fA = fClauseA; + pClause->fRoot = fRoot; + pClause->nLits = pEnd - pBeg; + memcpy( pClause->pLits, pBeg, sizeof(lit) * (pEnd - pBeg) ); + + // add the clause to the list + if ( p->pHead == NULL ) + p->pHead = pClause; + if ( p->pTail == NULL ) + p->pTail = pClause; + else + { + p->pTail->pNext = pClause; + p->pTail = pClause; + } + + // mark the first learnt clause + if ( p->pLearnt == NULL && !pClause->fRoot ) + p->pLearnt = pClause; + + // add the empty clause + if ( pClause->nLits == 0 ) + { + if ( p->pEmpty ) + printf( "More than one empty clause!\n" ); + p->pEmpty = pClause; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Fetches memory.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Pr_ManMemoryFetch( Pr_Man_t * p, int nBytes ) +{ + char * pMem; + if ( p->pChunkLast == NULL || nBytes > p->nChunkSize - p->nChunkUsed ) + { + pMem = (char *)malloc( p->nChunkSize ); + *(char **)pMem = p->pChunkLast; + p->pChunkLast = pMem; + p->nChunkUsed = sizeof(char *); + } + pMem = p->pChunkLast + p->nChunkUsed; + p->nChunkUsed += nBytes; + return pMem; +} + +/**Function************************************************************* + + Synopsis [Frees memory manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Pr_ManMemoryStop( Pr_Man_t * p ) +{ + char * pMem, * pNext; + if ( p->pChunkLast == NULL ) + return; + for ( pMem = p->pChunkLast; pNext = *(char **)pMem; pMem = pNext ) + free( pMem ); + free( pMem ); +} + +/**Function************************************************************* + + Synopsis [Reports memory usage in bytes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Pr_ManMemoryReport( Pr_Man_t * p ) +{ + int Total; + char * pMem, * pNext; + if ( p->pChunkLast == NULL ) + return 0; + Total = p->nChunkUsed; + for ( pMem = p->pChunkLast; pNext = *(char **)pMem; pMem = pNext ) + Total += p->nChunkSize; + return Total; +} + +/**Function************************************************************* + + Synopsis [Records the proof.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Extra_PrintBinary_( FILE * pFile, unsigned Sign[], int nBits ) +{ + int Remainder, nWords; + int w, i; + + Remainder = (nBits%(sizeof(unsigned)*8)); + nWords = (nBits/(sizeof(unsigned)*8)) + (Remainder>0); + + for ( w = nWords-1; w >= 0; w-- ) + for ( i = ((w == nWords-1 && Remainder)? Remainder-1: 31); i >= 0; i-- ) + fprintf( pFile, "%c", '0' + (int)((Sign[w] & (1< 0) ); +} + +/**Function************************************************************* + + Synopsis [Prints the interpolant for one clause.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Pr_ManPrintInterOne( Pr_Man_t * p, Pr_Cls_t * pClause ) +{ + printf( "Clause %2d : ", pClause->Id ); + Extra_PrintBinary_( stdout, &pClause->uTruth, (1 << p->nVarsAB) ); + printf( "\n" ); +} + + + +/**Function************************************************************* + + Synopsis [Records implication.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Pr_ManEnqueue( Pr_Man_t * p, lit Lit, Pr_Cls_t * pReason ) +{ + int Var = lit_var(Lit); + if ( p->pAssigns[Var] != LIT_UNDEF ) + return p->pAssigns[Var] == Lit; + p->pAssigns[Var] = Lit; + p->pReasons[Var] = pReason; + p->pTrail[p->nTrailSize++] = Lit; +//printf( "assigning var %d value %d\n", Var, !lit_sign(Lit) ); + return 1; +} + +/**Function************************************************************* + + Synopsis [Records implication.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Pr_ManCancelUntil( Pr_Man_t * p, int Level ) +{ + lit Lit; + int i, Var; + for ( i = p->nTrailSize - 1; i >= Level; i-- ) + { + Lit = p->pTrail[i]; + Var = lit_var( Lit ); + p->pReasons[Var] = NULL; + p->pAssigns[Var] = LIT_UNDEF; +//printf( "cancelling var %d\n", Var ); + } + p->nTrailSize = Level; +} + +/**Function************************************************************* + + Synopsis [Propagate one assignment.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Pr_Cls_t * Pr_ManPropagateOne( Pr_Man_t * p, lit Lit ) +{ + Pr_Cls_t ** ppPrev, * pCur, * pTemp; + lit LitF = lit_neg(Lit); + int i; + // iterate through the literals + ppPrev = p->pWatches + Lit; + for ( pCur = p->pWatches[Lit]; pCur; pCur = *ppPrev ) + { + // make sure the false literal is in the second literal of the clause + if ( pCur->pLits[0] == LitF ) + { + pCur->pLits[0] = pCur->pLits[1]; + pCur->pLits[1] = LitF; + pTemp = pCur->pNext0; + pCur->pNext0 = pCur->pNext1; + pCur->pNext1 = pTemp; + } + assert( pCur->pLits[1] == LitF ); + + // if the first literal is true, the clause is satisfied + if ( pCur->pLits[0] == p->pAssigns[lit_var(pCur->pLits[0])] ) + { + ppPrev = &pCur->pNext1; + continue; + } + + // look for a new literal to watch + for ( i = 2; i < (int)pCur->nLits; i++ ) + { + // skip the case when the literal is false + if ( lit_neg(pCur->pLits[i]) == p->pAssigns[lit_var(pCur->pLits[i])] ) + continue; + // the literal is either true or unassigned - watch it + pCur->pLits[1] = pCur->pLits[i]; + pCur->pLits[i] = LitF; + // remove this clause from the watch list of Lit + *ppPrev = pCur->pNext1; + // add this clause to the watch list of pCur->pLits[i] (now it is pCur->pLits[1]) + Pr_ManWatchClause( p, pCur, pCur->pLits[1] ); + break; + } + if ( i < (int)pCur->nLits ) // found new watch + continue; + + // clause is unit - enqueue new implication + if ( Pr_ManEnqueue(p, pCur->pLits[0], pCur) ) + { + ppPrev = &pCur->pNext1; + continue; + } + + // conflict detected - return the conflict clause + return pCur; + } + return NULL; +} + +/**Function************************************************************* + + Synopsis [Propagate the current assignments.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Pr_Cls_t * Pr_ManPropagate( Pr_Man_t * p, int Start ) +{ + Pr_Cls_t * pClause; + int i; + int clk = clock(); + for ( i = Start; i < p->nTrailSize; i++ ) + { + pClause = Pr_ManPropagateOne( p, p->pTrail[i] ); + if ( pClause ) + { +p->timeBcp += clock() - clk; + return pClause; + } + } +p->timeBcp += clock() - clk; + return NULL; +} + + +/**Function************************************************************* + + Synopsis [Prints the clause.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Pr_ManPrintClause( Pr_Cls_t * pClause ) +{ + int i; + printf( "Clause ID = %d. Proof = %d. {", pClause->Id, (int)pClause->pProof ); + for ( i = 0; i < (int)pClause->nLits; i++ ) + printf( " %d", pClause->pLits[i] ); + printf( " }\n" ); +} + +/**Function************************************************************* + + Synopsis [Prints the resolvent.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Pr_ManPrintResolvent( lit * pResLits, int nResLits ) +{ + int i; + printf( "Resolvent: {" ); + for ( i = 0; i < nResLits; i++ ) + printf( " %d", pResLits[i] ); + printf( " }\n" ); +} + +/**Function************************************************************* + + Synopsis [Writes one root clause into a file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Pr_ManProofWriteOne( Pr_Man_t * p, Pr_Cls_t * pClause ) +{ + pClause->pProof = (void *)++p->Counter; + + if ( p->fProofWrite ) + { + int v; + fprintf( p->pManProof, "%d", (int)pClause->pProof ); + for ( v = 0; v < (int)pClause->nLits; v++ ) + fprintf( p->pManProof, " %d", lit_print(pClause->pLits[v]) ); + fprintf( p->pManProof, " 0 0\n" ); + } +} + +/**Function************************************************************* + + Synopsis [Traces the proof for one clause.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Pr_ManProofTraceOne( Pr_Man_t * p, Pr_Cls_t * pConflict, Pr_Cls_t * pFinal ) +{ + Pr_Cls_t * pReason; + int i, v, Var, PrevId; + int fPrint = 0; + int clk = clock(); + + // collect resolvent literals + if ( p->fProofVerif ) + { + assert( (int)pConflict->nLits <= p->nResLitsAlloc ); + memcpy( p->pResLits, pConflict->pLits, sizeof(lit) * pConflict->nLits ); + p->nResLits = pConflict->nLits; + } + + // mark all the variables in the conflict as seen + for ( v = 0; v < (int)pConflict->nLits; v++ ) + p->pSeens[lit_var(pConflict->pLits[v])] = 1; + + // start the anticedents +// pFinal->pAntis = Vec_PtrAlloc( 32 ); +// Vec_PtrPush( pFinal->pAntis, pConflict ); + + if ( p->nClausesA ) + pFinal->uTruth = pConflict->uTruth; + + // follow the trail backwards + PrevId = (int)pConflict->pProof; + for ( i = p->nTrailSize - 1; i >= 0; i-- ) + { + // skip literals that are not involved + Var = lit_var(p->pTrail[i]); + if ( !p->pSeens[Var] ) + continue; + p->pSeens[Var] = 0; + + // skip literals of the resulting clause + pReason = p->pReasons[Var]; + if ( pReason == NULL ) + continue; + assert( p->pTrail[i] == pReason->pLits[0] ); + + // add the variables to seen + for ( v = 1; v < (int)pReason->nLits; v++ ) + p->pSeens[lit_var(pReason->pLits[v])] = 1; + + + // record the reason clause + assert( pReason->pProof > 0 ); + p->Counter++; + if ( p->fProofWrite ) + fprintf( p->pManProof, "%d * %d %d 0\n", p->Counter, PrevId, (int)pReason->pProof ); + PrevId = p->Counter; + + if ( p->nClausesA ) + { + if ( p->pVarTypes[Var] == 1 ) // var of A + pFinal->uTruth |= pReason->uTruth; + else + pFinal->uTruth &= pReason->uTruth; + } + + // resolve the temporary resolvent with the reason clause + if ( p->fProofVerif ) + { + int v1, v2; + if ( fPrint ) + Pr_ManPrintResolvent( p->pResLits, p->nResLits ); + // check that the var is present in the resolvent + for ( v1 = 0; v1 < p->nResLits; v1++ ) + if ( lit_var(p->pResLits[v1]) == Var ) + break; + if ( v1 == p->nResLits ) + printf( "Recording clause %d: Cannot find variable %d in the temporary resolvent.\n", pFinal->Id, Var ); + if ( p->pResLits[v1] != lit_neg(pReason->pLits[0]) ) + printf( "Recording clause %d: The resolved variable %d is in the wrong polarity.\n", pFinal->Id, Var ); + // remove this variable from the resolvent + assert( lit_var(p->pResLits[v1]) == Var ); + p->nResLits--; + for ( ; v1 < p->nResLits; v1++ ) + p->pResLits[v1] = p->pResLits[v1+1]; + // add variables of the reason clause + for ( v2 = 1; v2 < (int)pReason->nLits; v2++ ) + { + for ( v1 = 0; v1 < p->nResLits; v1++ ) + if ( lit_var(p->pResLits[v1]) == lit_var(pReason->pLits[v2]) ) + break; + // if it is a new variable, add it to the resolvent + if ( v1 == p->nResLits ) + { + if ( p->nResLits == p->nResLitsAlloc ) + printf( "Recording clause %d: Ran out of space for intermediate resolvent.\n, pFinal->Id" ); + p->pResLits[ p->nResLits++ ] = pReason->pLits[v2]; + continue; + } + // if the variable is the same, the literal should be the same too + if ( p->pResLits[v1] == pReason->pLits[v2] ) + continue; + // the literal is different + printf( "Recording clause %d: Trying to resolve the clause with more than one opposite literal.\n", pFinal->Id ); + } + } + +// Vec_PtrPush( pFinal->pAntis, pReason ); + } + + // unmark all seen variables +// for ( i = p->nTrailSize - 1; i >= 0; i-- ) +// p->pSeens[lit_var(p->pTrail[i])] = 0; + // check that the literals are unmarked +// for ( i = p->nTrailSize - 1; i >= 0; i-- ) +// assert( p->pSeens[lit_var(p->pTrail[i])] == 0 ); + + // use the resulting clause to check the correctness of resolution + if ( p->fProofVerif ) + { + int v1, v2; + if ( fPrint ) + Pr_ManPrintResolvent( p->pResLits, p->nResLits ); + for ( v1 = 0; v1 < p->nResLits; v1++ ) + { + for ( v2 = 0; v2 < (int)pFinal->nLits; v2++ ) + if ( pFinal->pLits[v2] == p->pResLits[v1] ) + break; + if ( v2 < (int)pFinal->nLits ) + continue; + break; + } + if ( v1 < p->nResLits ) + { + printf( "Recording clause %d: The final resolvent is wrong.\n", pFinal->Id ); + Pr_ManPrintClause( pConflict ); + Pr_ManPrintResolvent( p->pResLits, p->nResLits ); + Pr_ManPrintClause( pFinal ); + } + } +p->timeTrace += clock() - clk; + + // return the proof pointer + if ( p->nClausesA ) + { + Pr_ManPrintInterOne( p, pFinal ); + } + return p->Counter; +} + +/**Function************************************************************* + + Synopsis [Records the proof for one clause.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Pr_ManProofRecordOne( Pr_Man_t * p, Pr_Cls_t * pClause ) +{ + Pr_Cls_t * pConflict; + int i; + + // empty clause never ends up there + assert( pClause->nLits > 0 ); + if ( pClause->nLits == 0 ) + printf( "Error: Empty clause is attempted.\n" ); + + // add assumptions to the trail + assert( !pClause->fRoot ); + assert( p->nTrailSize == p->nRootSize ); + for ( i = 0; i < (int)pClause->nLits; i++ ) + if ( !Pr_ManEnqueue( p, lit_neg(pClause->pLits[i]), NULL ) ) + { + assert( 0 ); // impossible + return 0; + } + + // propagate the assumptions + pConflict = Pr_ManPropagate( p, p->nRootSize ); + if ( pConflict == NULL ) + { + assert( 0 ); // cannot prove + return 0; + } + + // construct the proof + pClause->pProof = (void *)Pr_ManProofTraceOne( p, pConflict, pClause ); + + // undo to the root level + Pr_ManCancelUntil( p, p->nRootSize ); + + // add large clauses to the watched lists + if ( pClause->nLits > 1 ) + { + Pr_ManWatchClause( p, pClause, pClause->pLits[0] ); + Pr_ManWatchClause( p, pClause, pClause->pLits[1] ); + return 1; + } + assert( pClause->nLits == 1 ); + + // if the clause proved is unit, add it and propagate + if ( !Pr_ManEnqueue( p, pClause->pLits[0], pClause ) ) + { + assert( 0 ); // impossible + return 0; + } + + // propagate the assumption + pConflict = Pr_ManPropagate( p, p->nRootSize ); + if ( pConflict ) + { + // construct the proof + p->pEmpty->pProof = (void *)Pr_ManProofTraceOne( p, pConflict, p->pEmpty ); + printf( "Found last conflict after adding unit clause number %d!\n", pClause->Id ); + return 0; + } + + // update the root level + p->nRootSize = p->nTrailSize; + return 1; +} + +/**Function************************************************************* + + Synopsis [Propagate the root clauses.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Pr_ManProcessRoots( Pr_Man_t * p ) +{ + Pr_Cls_t * pClause; + int Counter; + + // make sure the root clauses are preceeding the learnt clauses + Counter = 0; + Pr_ManForEachClause( p, pClause ) + { + assert( (int)pClause->fA == (Counter < (int)p->nClausesA) ); + assert( (int)pClause->fRoot == (Counter < (int)p->nRoots) ); + Counter++; + } + assert( p->nClauses == Counter ); + + // make sure the last clause if empty + assert( p->pTail->nLits == 0 ); + + // go through the root unit clauses + p->nTrailSize = 0; + Pr_ManForEachClauseRoot( p, pClause ) + { + // create watcher lists for the root clauses + if ( pClause->nLits > 1 ) + { + Pr_ManWatchClause( p, pClause, pClause->pLits[0] ); + Pr_ManWatchClause( p, pClause, pClause->pLits[1] ); + } + // empty clause and large clauses + if ( pClause->nLits != 1 ) + continue; + // unit clause + assert( lit_check(pClause->pLits[0], p->nVars) ); + if ( !Pr_ManEnqueue( p, pClause->pLits[0], pClause ) ) + { + // detected root level conflict + printf( "Pr_ManProcessRoots(): Detected a root-level conflict\n" ); + assert( 0 ); + return 0; + } + } + + // propagate the root unit clauses + pClause = Pr_ManPropagate( p, 0 ); + if ( pClause ) + { + // detected root level conflict + printf( "Pr_ManProcessRoots(): Detected a root-level conflict\n" ); + assert( 0 ); + return 0; + } + + // set the root level + p->nRootSize = p->nTrailSize; + return 1; +} + +/**Function************************************************************* + + Synopsis [Records the proof.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Pr_ManPrepareInter( Pr_Man_t * p ) +{ + unsigned uTruths[5] = { 0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, 0xFF00FF00, 0xFFFF0000 }; + Pr_Cls_t * pClause; + int Var, v; + + // mark the variable encountered in the clauses of A + Pr_ManForEachClauseRoot( p, pClause ) + { + if ( !pClause->fA ) + break; + for ( v = 0; v < (int)pClause->nLits; v++ ) + p->pVarTypes[lit_var(pClause->pLits[v])] = 1; + } + + // check variables that appear in clauses of B + p->nVarsAB = 0; + Pr_ManForEachClauseRoot( p, pClause ) + { + if ( pClause->fA ) + continue; + for ( v = 0; v < (int)pClause->nLits; v++ ) + { + Var = lit_var(pClause->pLits[v]); + if ( p->pVarTypes[Var] == 1 ) // var of A + { + // change it into a global variable + p->nVarsAB++; + p->pVarTypes[Var] = -1; + } + } + } + + // order global variables + p->nVarsAB = 0; + for ( v = 0; v < p->nVars; v++ ) + if ( p->pVarTypes[v] == -1 ) + p->pVarTypes[v] -= p->nVarsAB++; +printf( "There are %d global variables.\n", p->nVarsAB ); + + // set interpolants for root clauses + Pr_ManForEachClauseRoot( p, pClause ) + { + if ( !pClause->fA ) // clause of B + { + pClause->uTruth = ~0; + Pr_ManPrintInterOne( p, pClause ); + continue; + } + // clause of A + pClause->uTruth = 0; + for ( v = 0; v < (int)pClause->nLits; v++ ) + { + Var = lit_var(pClause->pLits[v]); + if ( p->pVarTypes[Var] < 0 ) // global var + { + if ( lit_sign(pClause->pLits[v]) ) // negative var + pClause->uTruth |= ~uTruths[ -p->pVarTypes[Var]-1 ]; + else + pClause->uTruth |= uTruths[ -p->pVarTypes[Var]-1 ]; + } + } + Pr_ManPrintInterOne( p, pClause ); + } +} + +/**Function************************************************************* + + Synopsis [Records the proof.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Pr_ManProofWrite( Pr_Man_t * p ) +{ + Pr_Cls_t * pClause; + int RetValue = 1; + + // propagate root level assignments + Pr_ManProcessRoots( p ); + + // prepare the interpolant computation + if ( p->nClausesA ) + Pr_ManPrepareInter( p ); + + // construct proof for each clause + // start the proof + if ( p->fProofWrite ) + p->pManProof = fopen( "proof.cnf_", "w" ); + p->Counter = 0; + + // write the root clauses + Pr_ManForEachClauseRoot( p, pClause ) + Pr_ManProofWriteOne( p, pClause ); + + // consider each learned clause + Pr_ManForEachClauseLearnt( p, pClause ) + { + if ( !Pr_ManProofRecordOne( p, pClause ) ) + { + RetValue = 0; + break; + } + } + + if ( p->nClausesA ) + { + printf( "Interpolant: " ); + } + + + // stop the proof + if ( p->fProofWrite ) + { + fclose( p->pManProof ); + p->pManProof = NULL; + } + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Reads clauses from file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Pr_Man_t * Pr_ManProofRead( char * pFileName ) +{ + Pr_Man_t * p = NULL; + char * pCur, * pBuffer = NULL; + int * pArray = NULL; + FILE * pFile; + int RetValue, Counter, nNumbers, Temp; + int nClauses, nClausesA, nRoots, nVars; + + // open the file + pFile = fopen( pFileName, "r" ); + if ( pFile == NULL ) + { + printf( "Count not open input file \"%s\".\n", pFileName ); + return NULL; + } + + // read the file + pBuffer = (char *)malloc( (1<<16) ); + for ( Counter = 0; fgets( pBuffer, (1<<16), pFile ); ) + { + if ( pBuffer[0] == 'c' ) + continue; + if ( pBuffer[0] == 'p' ) + { + assert( p == NULL ); + nClausesA = 0; + RetValue = sscanf( pBuffer + 1, "%d %d %d %d", &nVars, &nClauses, &nRoots, &nClausesA ); + if ( RetValue != 3 && RetValue != 4 ) + { + printf( "Wrong input file format.\n" ); + } + p = Pr_ManAlloc( nVars ); + pArray = (int *)malloc( sizeof(int) * (nVars + 10) ); + continue; + } + // skip empty lines + for ( pCur = pBuffer; *pCur; pCur++ ) + if ( !(*pCur == ' ' || *pCur == '\t' || *pCur == '\r' || *pCur == '\n') ) + break; + if ( *pCur == 0 ) + continue; + // scan the numbers from file + nNumbers = 0; + pCur = pBuffer; + while ( *pCur ) + { + // skip spaces + for ( ; *pCur && *pCur == ' '; pCur++ ); + // read next number + Temp = 0; + sscanf( pCur, "%d", &Temp ); + if ( Temp == 0 ) + break; + pArray[ nNumbers++ ] = lit_read( Temp ); + // skip non-spaces + for ( ; *pCur && *pCur != ' '; pCur++ ); + } + // add the clause + if ( !Pr_ManAddClause( p, pArray, pArray + nNumbers, Counter < nRoots, Counter < nClausesA ) ) + { + printf( "Bad clause number %d.\n", Counter ); + return NULL; + } + // count the clauses + Counter++; + } + // check the number of clauses + if ( Counter != nClauses ) + printf( "Expected %d clauses but read %d.\n", nClauses, Counter ); + + // finish + if ( pArray ) free( pArray ); + if ( pBuffer ) free( pBuffer ); + fclose( pFile ); + return p; +} + +/**Function************************************************************* + + Synopsis [Records the proof.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +/* +int Pr_ManProofCount_rec( Pr_Cls_t * pClause ) +{ + Pr_Cls_t * pNext; + int i, Counter; + if ( pClause->fRoot ) + return 0; + if ( pClause->fVisit ) + return 0; + pClause->fVisit = 1; + // count the number of visited clauses + Counter = 1; + Vec_PtrForEachEntry( pClause->pAntis, pNext, i ) + Counter += Pr_ManProofCount_rec( pNext ); + return Counter; +} +*/ + +/**Function************************************************************* + + Synopsis [Records the proof.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Pr_ManProofTest( char * pFileName ) +{ + Pr_Man_t * p; + int clk, clkTotal = clock(); + +clk = clock(); + p = Pr_ManProofRead( pFileName ); +p->timeRead = clock() - clk; + if ( p == NULL ) + return 0; + + Pr_ManProofWrite( p ); + + // print stats +/* + nUsed = Pr_ManProofCount_rec( p->pEmpty ); + printf( "Roots = %d. Learned = %d. Total = %d. Steps = %d. Ave = %.2f. Used = %d. Ratio = %.2f. \n", + p->nRoots, p->nClauses-p->nRoots, p->nClauses, p->Counter, + 1.0*(p->Counter-p->nRoots)/(p->nClauses-p->nRoots), + nUsed, 1.0*nUsed/(p->nClauses-p->nRoots) ); +*/ + printf( "Vars = %d. Roots = %d. Learned = %d. Resol steps = %d. Ave = %.2f. Mem = %.2f Mb\n", + p->nVars, p->nRoots, p->nClauses-p->nRoots, p->Counter, + 1.0*(p->Counter-p->nRoots)/(p->nClauses-p->nRoots), + 1.0*Pr_ManMemoryReport(p)/(1<<20) ); + +p->timeTotal = clock() - clkTotal; + Pr_ManFree( p ); + return 1; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + diff --git a/abc_with_bb_support/src/sat/proof/pr.h b/abc_with_bb_support/src/sat/proof/pr.h new file mode 100644 index 000000000..a69cfbef6 --- /dev/null +++ b/abc_with_bb_support/src/sat/proof/pr.h @@ -0,0 +1,65 @@ +/**CFile**************************************************************** + + FileName [pr.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Proof recording.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: pr.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef __PR_H__ +#define __PR_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +#define inline __inline // compatible with MS VS 6.0 +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Pr_Man_t_ Pr_Man_t; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== pr.c ==========================================================*/ + +#ifdef __cplusplus +} +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/abc_with_bb_support/src/sat/proof/stats.txt b/abc_with_bb_support/src/sat/proof/stats.txt new file mode 100644 index 000000000..557c9682d --- /dev/null +++ b/abc_with_bb_support/src/sat/proof/stats.txt @@ -0,0 +1,66 @@ +UC Berkeley, ABC 1.01 (compiled Jan 20 2007 16:47:34) +abc.rc: No such file or directory +Loaded "abc.rc" from the parent directory. +abc 01> test +Found last conflict after adding unit clause number 10229! +Roots = 7184. Learned = 3047. Total = 10231. Steps = 196361. Ave = 62.09. Used = 2224. Ratio = 0.73. +Runtime stats: +Reading = 0.03 sec +BCP = 0.32 sec +Trace = 0.06 sec +TOTAL = 0.43 sec +abc 01> test +Found last conflict after adding unit clause number 7676! +Roots = 6605. Learned = 1073. Total = 7678. Steps = 52402. Ave = 42.68. Used = 1011. Ratio = 0.94. +Runtime stats: +Reading = 0.01 sec +BCP = 0.02 sec +Trace = 0.02 sec +TOTAL = 0.06 sec +abc 01> test +Found last conflict after adding unit clause number 37868! +Roots = 15443. Learned = 22427. Total = 37870. Steps = 2365472. Ave = 104.79. Used = 19763. Ratio = 0.88. +Runtime stats: +Reading = 0.20 sec +BCP = 14.67 sec +Trace = 0.56 sec +TOTAL = 15.74 sec +abc 01> + + +abc 05> wb ibm_bmc/len25u_renc.blif +abc 05> ps +(no name) : i/o = 348/ 1 lat = 0 nd = 3648 bdd = 15522 lev = 246 +abc 05> sat -v +==================================[MINISAT]=================================== +| Conflicts | ORIGINAL | LEARNT | Progress | +| | Clauses Literals | Limit Clauses Literals Lit/Cl | | +============================================================================== +| 0 | 17413 54996 | 5804 0 0 0.0 | 0.000 % | +| 100 | 17413 54996 | 6384 100 606 6.1 | 0.417 % | +| 250 | 17413 54996 | 7023 250 1586 6.3 | 0.417 % | +| 476 | 17413 54996 | 7725 476 3288 6.9 | 0.417 % | +| 813 | 17413 54996 | 8498 813 7586 9.3 | 0.417 % | +| 1319 | 17403 54970 | 9347 1318 14848 11.3 | 0.442 % | +| 2078 | 17403 54970 | 10282 2076 40186 19.4 | 0.466 % | +| 3217 | 17397 54948 | 11310 3208 99402 31.0 | 0.466 % | +| 4926 | 17392 54930 | 12441 4911 131848 26.8 | 0.491 % | +| 7489 | 17392 54930 | 13686 7474 204217 27.3 | 0.491 % | +| 11336 | 17357 54829 | 15054 11310 332863 29.4 | 0.638 % | +| 17103 | 17346 54794 | 16559 9130 203029 22.2 | 0.687 % | +| 25752 | 17288 54606 | 18215 9083 176982 19.5 | 0.834 % | +| 38727 | 17266 54536 | 20037 12674 278949 22.0 | 0.883 % | +| 58188 | 17240 54453 | 22041 11905 255255 21.4 | 0.957 % | +============================================================================== +Start = 15. Conf = 79435. Dec = 130967. Prop = 24083434. Insp = 136774586. +Total runtime = 18.66 sec. Var select = 0.00 sec. Var update = 0.00 sec. +UNSATISFIABLE Time = 18.69 sec +abc 05> +abc 05> test +Found last conflict after adding unit clause number 96902! +Roots = 17469. Learned = 79435. Total = 96904. Steps = 9700042. Ave = 121.89. Used = 57072. Ratio = 0.72. +Runtime stats: +Reading = 1.26 sec +BCP = 204.99 sec +Trace = 2.85 sec +TOTAL = 209.85 sec \ No newline at end of file diff --git a/abc_with_bb_support/todo.txt b/abc_with_bb_support/todo.txt new file mode 100644 index 000000000..846117b13 --- /dev/null +++ b/abc_with_bb_support/todo.txt @@ -0,0 +1,26 @@ +- required time support +- printing ABC version/platform in the output files +- fix gcc compiler warnings +- port "mfs" from MVSIS +- improve AIG rewriting package +- unify functional representation of local functions +- additional rewriting options for delay optimization +- experiment with yield-aware standard-cell mapping +- improving area recovery in integrated sequential synthesis +- high-effort logic synthesis for hard miters (cofactoring, Boolean division) +- mapping into MV cells +- SAT solver with linear constraints +- specialized synthesis for EXORs and large MUXes +- sequential AIG rewriting initial state computation +- placement-aware mapping +- sequential equivalence checking +- parser for Verilog netlists +- hierarchy manager (hierarchical BLIF/BLIF-MV parser) + +- required time based on all cuts +- comparing tts of differently derived the same cut +- area flow based AIG rewriting +- cut frontier adjustment + + +

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�pj}27-MtiPWC7J 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�@HJ(iCq{;gXS>9J% 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 OM5xKysOdP)tl>>PraLWL^rqA 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 )K@DJ6LU+X~J4VA_unZbl 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%0A%OTt%cgKPK-@j2HiE)2QGr zOqaK(x@(FnfTHGcwz6eVSDB3Fc_lllkNb4?G?bNZE_&dmu|(xI|M<6wHK)$B?BQ64 zFOVBo@duh>9!lU>_4d<#T5Tp`Z&qIdWW=Z1X{cY(G^KugVSFjAt0i=t{25-i4y^Nn z=!HpGEB<@j!ynS~<713O8Fav6?2?|)oS&kngySKra0&dRy@}emD=weoTok9x@9W0@ zO#E$qi*LPMVviBu3$JCHmI_K#zcuafuS;%=?-TWEiauCC``b?uw0;)S?2 z_K*YGxzDlp+|SP+MZ=%H6ZSXAhNAvYp6jo$epZ?{e9OL#6zunBc(ymk^LbRl2|O$8 z7>S?D-h`HKI!-_Odz8qH@pun+mH(6`#6Rp5tD}PRtD=Ijc!K3A4cIE2hUURx_E_3U9VHj32jh@zu5*cgh93IR9J$%su>iS+ zBWW6xV)vozjIa6vV?%oNZKsQH6656wv~x+G?em?Z7>#3y7n6^^koJ~xuL{3~1Hye` z%A$2l8mh(Q`#vgYLUW@ejth^#cSt3CgLb8-9HX>rl*2=IT^eV$cQVEnJ(VN!hKw8n-KyT?87#z%XvMmt39-P-M5^xQzK&hSP+ysZ*R};t!!gd#ZU5VL1k%Q zNvkD)Rb)SD?A&anzALxCTZl0j zj6V^TCr*$YXV{zZ20JTmu@mI-`)cK`;WSDJx5WTdMrMq|$Ecdu-!w!4WJ71n#R~LC zBOJh1jK-4t_U7m8iSbP{e>X#TjOUZHun;S;9+Skc#Xxyd*Yge@7y5Y)Omo-we*~Z) zJ3ygH_I?xn#)EzOzGqCYT7y~nnQwg`jhuKkJ?6f~eSJLlmk+#WjPJVhvb}L8*LwLU zU>0`Z09NC>z5b?!^htAF`_o>3})E(>2MZKU6AKTqLVNjArxy+gi( zs37mhSS;?6eiUV=f6ccY^oeo@luy7;t$!O+dXJUaHx=SrsplnjFLo~anYJ6CVJhF! zi|eJnxa#>&`LWs=ppMScC(yXcF4y-tXlbnJwVP3V5jqo<(G)$f26vUutn650+G9)= z)OkjDkp3jlUM=CfF?9=P5FR5uS{wNvdiToyjvaS~|35+-^AR=Lb0zV+%*Q@u{t|Z% zxx{U!zpCp`tW|y|`iftM&y2H-@FI-X_d&vID7l@<&vC7TGdPGVuN@vh}`uD_q)R3o)vIVhCUoz8V_y`At zYg$WXXtd*&(eX%Z{l&#=XyRGY=ybldYUFucL*GBq8+2_Y@9MA;b1(`yD%-0eOLgxk zFc9-=yN^MxI{scQO+kOD>Dh#|79ZFHv)^Uc6+eM?$I@!{?Kp})^6U`zGhIUWtK%XT z3D*<;6bsZ*owia(2k~>*V?XvA5B3!Nh4UDok9X;@D)#02d0+T%96&@>`($MbAQ$_x zJQvmb2nX?oxMy|wQ@s2kza@N6o+!^hOV)4?h*yyxtx*r{<(r~UgYgpkqPPPHj4K(+ z;A0F#CuBxJjQ&V|wAb!FnhBjyMBBB|1>^8cTsm5YuBP?qIo#J?as7K2RgnaD)f?Zu ze4x%0#yE(!p?j74NZ;b>R{?rRTv>XBelKo?y2jD<=*s>D&)LQ4RrNhaD&arOYdiD! z34UWwjZyRS2U42r1jsD@u)ajo6qwCkiuKrqZ}o8k{S+PX5xzoiyeEHpeJ+Ij?3?1w zpf>xuw$5Rrwu)(|0Coy*#u5FTI@)_0oF8Z}c*gN}U43b)XGyL_5AAtJMmfL5rN73X zy9T!L&BPYhzBw3&D_Dv5PkFbE!|XydBPL)CTH}zq+mv^YG~9P%$bn7Xlgy8KSHgao zmZo*TanHo=i`sZs_|ZuF1)4xOIekj6;s6R`m-Go(i}^T;OBjUl(ob}Ce~mewD-TKN zz28c|X~#fxMBN`f!&&A3>A-&ch!*1J(OM{moR}ArU;I6K39mo#cM<4HOc4JZ1y}nU zXY_B(!8PneYVrN)NcGfQBL6?$^I#8_VFQlhJZ|9`5}fdjGKR9V(o)DD6F%)Up_SEh zULQ(5(--tdQ*^>j@&C|NPyKBNb_cY;$9RNz;w#~;=bop_*NQz3wXgw~u@^n1e@W-# zCH8D&!kfZbF<#kUun-@pw`@%P?1=UDPl(#!nLK7;7}_B<@}M-<;13Mk=$N1*s$#0L z!_Wndkr)|~7sZjmJyc@kL4#A?O`DJ7=3@x`5p~_yjiEE>H)t*`&obZivj0VD_P5eY z)9Gln+_78X{Q@nE8{)Fk&*)FshYv9Tal}7V$6W1pb1cfC5!MK&p#$kS9KlU|q3zLY zy~9H@{G#2B^mlre{)Y_fT!V3^ocBA*yoYv2m| z8M3pJp%ota&1AgA_TQ+9yeNql=!&73f<^crHsUPqBkd3J<5QGG4fMol%)wgh#&N{K zbp*zg0=ZDJo9|3~D>BnPz(W+d&d*)*JeO9skB&<_^Gk=n_ZwIwo$tFO`V@&9dvCcw zAK5Rnv*R**J650>(nuemOdDDUYp_MQ4E=%Tr4hKso`kVWBir=}3G7-Zs-J1b+ixH{3Zf30q5{6g ze?LbDm+?2I$zRwwC(ZY)cY*IF7xJ_0?sN~Gk2QEEtr7|#Jw~D0BJZc<`wz+4`7w{Z z5pCGb<(W*EqZfMs9U<+N>7JLVvkZ+V{xZ9&_}an+(O`w=?>}lALzmf?VAOJbLXwr< zok)K~k4gUp_1Tlf{fLY5c3SE=4qn0AXeU1N51udM%wq4tFhe^B<=u=$_z)$L15Z$R zmG4p2UEBPW#S-}kW2yR%qldJL+U;*WteFJnu%mU_58q&)J|D$7 z{DPZUGugfc^D)O-*(BfB;y-fy3$b6rWb0!w)}y@mw9*sdhB6ToJR=l;Y>MZ5?0n9H z7AS@qXg#8xfid#Cz`7OSSKK`Npf6z^MrtnPZGo>%0?WZ~R-#J#wCsp@x z?d_Op4zU!Kv{?^*&<0~s6seFMJ(Bt^NZGc^UeU%Q^O8+C2_85;C1?hHDvIsGmv9L6 z+R-d|$H@nuBBdaDZK19Rr0M((@RDiNX(PMt|gU&oc@k#Xo)^p!L}|OMxSLzlwbgx@e#}+x z9(j)=k+^xdEAA9pT6c?#YbElrvtWTa*gwg0cAUU-jB+lm#aw(~-p5XKER1QQaSz3K zajj@~?8XtSQ_puaxwO;D-9s^Tt-@AJ#CPb5At)hV_9gClXd>%of%?~f@9$&aCGo=? z<5co}WnCoE{!N;j9--$j4jF`hcMRsTKV&bpMqjWyvy)mkMd=#;a2HmJZzB8){T=1m z7w{CB^dT>fYIB$MRY)H`!Yu4Hj%L!<7~5WB%%@M~#ka#?l+jKi{^g#uH`&+CduF;z zT~*|5iMqI~d|Kmr7d_Dw3$X={a1;adWi0&{MKDizHTGhle$?fYnj@3?dKtqpeZ7S) z`j^34$cGN@&zquyW7`cg9P5O5?pj(w*<#3!G%>2|Q)Q~@>oxih&RL%WX|h^Crn zP2+F=?HSs#OCh82mc?dv9a;eC`I~{l^=K<}LnUliuCYF>)}J3xWP5b5YQOz#b$j~u zt_>IT?TT*?@sd4DuJrzfAda4m4rZtItS_Uz6Fr4n*oU#`f?x0q12HGHy&ZbuXZ(hV zSc=uB&GRevE@zxmr`*?~1*(hNFD<9|ChV=+`iQ-iJwf`<_zQ#NX^UmzYtv?F+*{x- z24O7bAzD9*(Y%ASwGIR_aisEh4d2gU(@D6yrN8$wCd{ll9tj&A$*$7Ga-z@Ir)G6 zSecjIk4S5RiJAPp5G=w*3_yJ8*=S)@Mq|9IjgQfVeNJ7=@C*KjM~M5Xdokr|U?qM< zM-0FxD2-#th+gWsk=^_|=I7NnLEIeFH6M@ZK=sYSCF#}0y@xvbS{b?U3L-Mw6QB33 z4n1|w^hy&4~Q?R=yR| zccP4N4Kzn>;gs@bl%5|o@hMJ-kECVrivDHNr^otzL;N!366xnKdQJLb>_8Uvyea(z zyMw;f#$rrFaTL+VVaiQ6_Iu{0oAUj{Hxpk2X_Y;oY!r$Fk<2g?S z?{6>(-y;4?p21_Jw87~3ulZae?exsd-%RAUeN+Gc7<&)6Dst~} zdjXv8}qUYhM*bQBbjC$4*mGQMw9< z-1o_F@9zEjyTkvzd>)d?WHQM)Cz(tplL>~U@CU4dzo5Zb=C!6W9t8v5)2?5@rr-g` zc(&5?0 z7>H!t^d?*-pNGij>KEK!e5M?1?`K=)m@LL?tX}f089KnvR`e0za}lodh3R8KkW9J2 z&4j);jDitt2jB);pfjjxNnaXw7U5p7nSJ}@E?b4 z;um2%82?fHB^xn+5_dfDrb7q9?b%;C zXW@TOUUFeK`Mt*e94J$d>jd-xH?VT1EyQhEpKsma-iF^`1I&X1P?~)eh?j+bJMI*= zm%vH~jZi{L|6b06599hl#TZKC?ZE?d$lHp%3X7CoGxx zUYxTl`^v&ikl7aq6JbST=E4#dPu%IqFN5H~w)oBA0W!{n2=M3F_3#>hZT2nTn3|NWEwo~L z9ovOSJA&i8ussMSf*%|rUsuWVL3o9KCuOs)z_p8V9>f0u+OzEdb>R}*vv42d_QK7< z4Z^+7u_JL!C|40!#dV_*=ly~#TEHM^L>_)Y9z$_|CNCqP5zK~;?2n|5o`CWq4EH7M zh7>R-Y!z-b;?-$J9$^CdnG1iyTDSnmN&hQ#=Q(~;d#)Lr>k)i{Fv9neuVe5AzZLbf zHrT-p()A+#epmw4p(jK`GK9e|oO2AEA#Mxop(}K2fgRuu#=SRk=yTohsr zKnt#qJkRfo-@34=6lTs^XhW$xu{?j`dg|xG`!ZZxKSMGcdBU^VJzT>#yv-HJ@=o>m|zn3txzW!JrMsVWK;-uK?Y;hZP6=uw=X0; zVEb=)i+?2ypv{bN;rL|cIkn@N5YLUUjLbyIjJhOi}RyuSonAOp_9UoaFRh&LVA znz#wLecA3onm^&zBb|pFzY_mLD9!exbk_SJZb{8h;THpqSz=3qi;d&GIIB8u54_F2M;03|(0XbiW!kqghZVudo{>XYP z=NgSWryKdCKh%r9&}}$Re`p8uC}z`F+W~U%|7q$uPewbCG`G8Q0G|kMF{B zG-ycJC)~5RH=!rU(2%epFa1j2RuMt5zw+5G68=W54KPf z(%2UdXYk*Fjc@>LNJC4wMR(>HKofXN_*XbZ_yHJ5oMz}?0t|xL;10K72P}n?u$8zg z;C^Rh%ywyr$6p9H4L1b2J%mHB9DKp46LUo112TIKag-$rqT85Ck%O4;$#Hvdx8l|U ze|P3nLC8L{GffHeOI4&4gvTen_mhcaGegpdm3j=HN za9re{_!*S@H119q4Y}m81jjE&zvI9Gdpm-AihS&bRivZg*bba~7%V346vDhXZYGR? zg;1Ti4dEyJ&B3%ia|0m=_JRi8#N&3t?Ti}=6QCj6pK(h=GZ+X_&~G4fKp+ZU!zh?P zK$3>>PLMA|;QyVl>iDPQy0U!`*NyG-xLNRtuxMCMSUETZ9SCm-M+slV_Ga8}Y|J(R;JQiu7^8l_h&!nxnmU9q-dUzzd*jYZ*hyi#0H=(REWc#V$m1w zxM$2|gxhc$IukC%qbuSK!M_-~5YGde!`WBZF!83vP~Tu6+q+;h`=&!_j-4=>`vvx$ z=U89jwt^mD{~TL~bL1(PJPqa?p&T=uV@k3;4BTM`ID-j{^`Z~UvAaoY4QX=GlNv!+ z7!9T2Pxj}Lhr6WZPMqwgyl>6+1Kf9nJ>=Ne_-ApxWrPDBh5c?|K?m-q|=o2eiAYud^2tpTsK(6 zetYtAf;66y##H=C_i01#Q65~6hqO%(SeqC!@$Y~$guiEhmq*Bx@KmrTtTOu!5VjD% z9bqQK`G)@>t|#s^_Inb(8Hy9{0^8RK&td;~w(k%w6IO??X1G@f+k(HYh)!|SIQE1E<39owAPia(=7u|+{liFeBK}_Zr{jMC z7hpFm26tG1%r`=B{8r>Al(^H#+Y<6Qo$bG%9(j7o_A|n*#6GrT2rogH3)@|A;|co{ zzZ>BdNaH%XNynbv;r?}(^TB2ag_m#}(jkzzZNVAVBL_>u9^v+-9BZ)2@woeO{c%Ha zqj2BBZqn_G9khc9P?h60U^9ncI$VOsun?TleJp7nhHnrFrHDU@`YW-|rygxeH^$O> z^A7j~`sdTR_XJnQL!9>WOrk6IkiT#b0$Th~gY3WlDcN0Vs zR-En9i?DBK3O_?M?a^6p#{7ThTFAK1ZBxd?3QkP1oL9^v@hKz?yULvBjod@Sjc@dmN;5B!es2ZJc%OFfBfR;@y<0oRX5AQD zh4ePuzr#Ypg5fw}GTTML88*2hUxRl$^3|P zzkN6Eueg8Vj=>FtfACwcqt1dIH=Ephw)&b z5cVtkrdOv;gci^ZB&Z6Xs?o0FE`=NTXTeI?17~0iynqUhv>%Y_K%0b{S)O`yhH($J zPryA0hgk51KVSp2BJNS_yRIF52bcxBAP9y*575}tuH)vxN4N~93A+mQVHivS4e2>T zT^I=Czy-QM8}{#m-(ek81QWu0;68%akOAdMyER-*Wt|Ebo5pP$M% zJv#ZJXY3^u8z_q#y?}WatZ7&dyV#E{NYwMe$gO%h^F?S=w&Fj64J?N3Z<#O1^QlJc zJO49dCyNLi*qmny^Kv+U5z@WKm~I7ZBAom12i&_vaPM-OeOPZ$98HqrNn5cmsr5q2B?fy!(r;!eTM#vQepcPH>~1POmH@P*c}8ft<& z`%2@!!o3Mapg;IG=e%%a1LH-==NR_tGM_OPSVSExMjoGVul$m7y#s&tH>dpLxDN}Y zT<2|BkB~Y4b*XEsNM{c;iC}INbg9g<$k|*IYw?T^zl>fYj&Sb=_n~)P)}f%zZz8Ry z5aq!0KvTX&51wpqW}i=gG8T_sh}ew&nWA`OA~%!MHQXLw)?qc(-6R z+}T3gy%hVv{R?hvW}FRoA^Mt+JC1!}xUUG$f%{<3b~9)PtBE&=I7x*6#`aYF8Mqb@ zPFP+g)(^9wj}Ffu4|el~S=Rx3n9CT+1+Fjk?lSI$ zTo2yidVqcT!9plO{LZx7t!azKVCN06aV<9Qj?8ajZwDE_c!Um4!KKQfK0Hs%Qzy(CUfjcora(F}kSW6j+{f@MWoie0$nv&m?_(HW1{0_a72wck+D#buoxT!` z$mU)I>gCWTf;(XY;N(E&KR|;#>I2xpXO6S@LZ3*4;n!x;x5Dp8Sdy0h2n>f#P@Ay2 zxS8xPhue(p^|*835Mi%j1DLao1xUZLe>>@|fv;@G!DzxN;;zE=!tDpH;LW*j;hqqF z_65Lh{PUqId2bB!iL(_tv9Blkeay8g8N#73q_aE#*b&=v#}+AF;A8H3IX5^n+RV44~a8t4*dvN0aw|sL;TOU(QIoW z0sl#u0;kw^f(9@S+@T+sLrKtZ-U!@};E4Y)=hzK@kS|v_NZ2NrPg+A@6deAYxdymv z!G*l-!F@=aGtd>9K~Ijgzr?iyuAJjJ8mzd?a}?qyKnT=2huuJF*vtOS5KJ7uP{w*8 z2;$&2^dWp#7TykhFjxv}VM`;f`*0Z!f-iCV+~B$EHO_OLF=%-4 znCHCI(`e!+!aU+FZ%I1D{SCMHAj*n+n(Y=N>92ARx12b0DQ8Sk+9VJ11@GVzJcNHd}(!eYzaCOc5ZhesSV|r&v|Yg<^Bk2x1eodyE);dkMOJt zHGh2u1Y4JW;7?C*u&6wGfjwgR?LVmb3EaGO9K@L<0^tR&qSxI)-v?llg1 z@@$0k=7LpC?D81$=xrjkK|b5hB3tN&4&I()-ZE)+z<-|OR{Y9XJ^me(;~>1mt}?)f zupXo}k#iKI9OlTe3+LU>!{_Q0Fd>Hl* z*B{sgE5QwVfGt?U6Zixna2*;#6{yT}>E_TK3PCA&4aH6~?{GbB4&>GXNwU=8j!;1pD&eXGx$gD)qT zztDqcCVuog19-j!7Cavq$@$-#aF0s+wv6-qLc2POKIWZQvSfnW3U?S}!>??w#J0E7 zS6Mk9Tg2@~_&DeYf#AXOl}5M;FogJ4u!FqqhlyOj{0Q#{CY;BSc?PwJpF&x>Q?3K} zOAV{sNteFT;1jEph+EEskqVUQ0tS{OR;d1~S$bj@&~a zT0?9;P&JAj?f6otmfXCbAQ1f3peoZhl=<$ z@SXTcaGztpfghawK)b@Z%<(rNzrV8W44c^Y#{GkRzJw2i5rq9t*lKuAxmS_)Z1`;n z&leXn*9lhOADP6M9dw4Hgx%oSolqYCGu(8r#27kw9%MAn?PkDOaX*9G5&A%7NP?%tJ4(FU+^bvz_e$6| z)W%keaH~;{ag$a)j3LvHHN&n7G4}dy7;8xNm!!+sLnYGPGm!TJ(fz#Tzza3!%;Tr6N1?06m?iLsY(Xbp&K>$pFdgSpNdE7x7*~qIXay$->P#ao81ayL3 zGWCP}xI!lPvt4kDgDKR5r-Z$S5UvCBiN6P3Po*upLVbM1IWDZ_y1Rz8s-P+FWx5wN zlM>PIOZ58y-InA2TQY!u`tjEBx-bZeWXlGT|n; zwrtmhk1(I(N5YVj^w;1V?aeW0;K=p+Pu~4OE~VEoCdTz+6#mPnd4G~Nv^E5mm!t|E zS+fhycVKQ2ZpS-ZM{w`LX)y0ZonhZ#!aJ~C9~u>>?&A)?t-$`Ku!HRjw;3nET@7aJ zTf=d)@P8mY6MrSrC<#5F7~z%aBTd3}#vP8^m2{Gb7Y8{U<3hMOagVWm6K?h3Jv731 z;?G>nwF=5Fp>G4bU@;^?AMk?pkO!Bsg=pNX5RCsg?s42Wj=vp6dq>+GL_M7VJ}wyc6E;%~nnF2_F@-iz2exoe{^%#>GeZ`ffWw`c z2SQ)wF79f$2u+|1%!6P7@#1kEiIAlFT(ddYZAdUeJ!2XLL@*j9uM zq`!2I_TU)Yf(YTVZwhV2)>E`iaDwr*SajZz`|le<_izNNGJbZBeY^Ja?j20#xOs$+ zpbwkLwc;LQ_@miA$~cGxeW@O?%prx9^c_5p@J%D|fg$jQuz#AcUh53*&B3%eb0X+# zm@^h<3qjcB3DU4eo^F)q4(Hy&SkUbej5okVI0#RQ(Vr#mUg%1^8C<7QlemVG_ZFOc zC;pkV&5L0>ET#Ql5A%t)4sMc<+q9L<>F?#?euRGb?d-WO;+BVMFyt#XPFrO+iFU%5 zwhKA6rA}2Y&3uigw12q$p3!!}E4H0+UEn(YB*=xXY|ni{`-#6jRE0>k<2m*@ZVKeV zN;nC--~juk;ua@vFBk=dNvAv{-=_XTi749UJFKY*yCDN65_SeR0?I-aw%ful5Dr-! zH;C}6;0h0*3E`(a67Eh1Z0iQ)yGeZ9Y_P&V z95SIIGyp%sx8i!;;{9seTM&ppisLV{Zwc&%wuB!7KlaH`6da)m41rGY3;WmNe%#MH z4Dgce2-3Mn*e*B^FChy)lBP9f?gW#F=UtX}X74k<4M!9N;z(PQR3K?)RMyAFilzYo`mzs{)XTPUv4rE zin{Gn{hu9 zb_@Rl$|{kchax^_V{%oksvCG)r0?#iquNaQOEqD)~!GwJaNy7(zCjLNJ591&J z4nvy=WQh9#THj{e4>uPa2!D($L%BXo<6ff&ZG1TG@HwtmTCQ;qxDLGliEC{Uc+2y* zY)B+515_yfWYf!JTMJpolx|AykC9TxSEJH>`xNaBMF+r~ln- zF5euQ#XMfNvuNMnfCK&=zcMbz9Q_2YHAxUj{!+Vg4Ph+jA~a^qsVUDzN|ok&zrgUK zRCGJgGA&`zN|S?osW|!!0*8#^uJQWik6#CU^uzyZL|r;&N%nsgZB?mx)|))%@eo!C786b93YU)22CiH48pcGR!0Ec9LIiq^&mi zt9HTni4S5D_GoYUC)7$4{KfSco&SSQd>&x1|YhBK|*5%yz_LN;LQWSpeN4ZiJIz{LyjD~(=j=%)UiteCK(f&?dUZj<8=6mun{W*#@5Q1 zz9dPz?ckke=4Z6^XI|A_dv6hM6FL2&c4pa!m!jsqJUi=*on~gWOPN=_@7!!OWBrTg zi=9R}+BWzb)S%`sKFHbzY?PSnz$wj^M8u z-$K5AOw&e%aew{BJ#)Uf9_$ z^~#E*naj1dUB7PfJFa(Yi4EdZK z_jR;?YVKb5?;$n18ZL7yGa}hy z;jGA_j}mvb-0cGh0c-Yrv_UjHp3 z&22$gYQo*zX^s3#ZA+{V0ue^CnB40aLr^rM0Ro!eEfBjzZ zteW#iHQ3x@i|_WrvsX52dU~2i{k8KKb)NOGe#ONxg(sKnyZb@t&F$Lmc@do_joXwq zqJ&@d{&P|nyFbg^v@)=D^I65$XSX>#Vbi6VBiweTF1vQQ(d`m`B}43|4!U1s^S!Q% zJj%}0#5SB##BtfccaqvMVNb<6Oxe|TSabSC^|ujPzh@yXZqq{DTe$WMb&t02ShhKD(6lVK z!e4wZE!UcaIRBno?)~Cwrl-W2`a50;ipxyP zXw|`f=8Ni%QO6v&CzuA@IdsfKyTmuS{ccaY zFY-gX;*quPT5L-z7Vb5oR_}cyj&_v4w%wm<;hb)nF(&dMA9h*OzD26Vg=w4ihSq((R8#xLn=0?!5`tR|`qp&Lj44aawf3E}_7n-4 zpVI#3kcu%OQ>NO)hQ_abQPZtO#nZj>rXK9?S;Jv^>5w{gi+9Ui6L_qr>(4Gp8v)a@!YNJZoUAv!h?E{g~b>PYn6=pjTeAW0~7ro%*LG z{=2W@;mxj2wn^A~%db>c#1hw{xtE+TXwH0E@pn>4qp`pX5lOQ1 z+tjYHU5k{<>*%_FN1xTZcKn@|6|(G)Wj7~n@@%~SUHb6kD|5*+kbJ;SDcHHZKtax{4 zm(ZT^_8M)cj77egH6Bl?VRNM1r@u8JRPyy!UQy;h3|e z% zdiZoW(0GRZ!Q1l|`%X=`-N|jwBa4lHd0x5eefw#%#6|&GZZAsu3>xIv-61p3V)P$P z@1=U)I`FQuca102tjoH#+OuSrrmXkH`H}W_y7-OE?Nq(|)=fXV%EOa){=ToR&$Yx~ z=A4;5^-!e$wxI8q@-Bb+{xmB4_@@#xA@#M+%jq-PG zv$flo@0i%?q_ax}N81;DeLu7t-8gkp)A4r(dal@#QgP_T*ytsWTT?6K91YK!zuWP~ z^qmVgHkuk@NDLC&GA{_DTl;aE}22i+&um7p1Qln z%*O5Qhzsj3WZpKLz9{+Xp}-lr+iDek8@MHTbgPix#&@&)d}i^RDfj;g*ww>7HO+5l zg2UaqTe`TUcDv&?v8hXh#^dmS*O%STEq>eLpV+@kX7O6+(Jz}?)?U>ja>1CJ898em z{;v79r|V_cWLrPy`{zGQ++IE})9-%kNvY+vX<0!*TdPGrce|nq3fgz|@77I2Z~1@O z918GKzPwp*>5(c1RnX8Gba1=q2ST(D<{1Ru}Bd2fe| z?{l48yU!1H8u#asqjqt&K`r9GM}2k4{o`ET32)EtA3a-NnVlLKb9enPT#o|((H+7;@((Oc92u!GgqzU7JIz==gs{% zZc}5;*dxUp<k7iw8P>VCOLUfr$l_TL*Bac+IiPwR{B7#F`P zJ-)Bw%eMX|8dn?hV2fh|=LMB-eg8nuE?Z;|+f-EvE<89~X7P8RRPw%@oWRLl8E9wW-$n9`6u)WK) zcf~w<^o-tnw6ev9qGLV#+-hr|wsUIt=c~PnPERlP$#em$wp)+@CW5>6e`N4Je?lJ#_?#o=WXJ-1&uyw&D>_0qhKkc4hi#HznWX zf7p0vztFZ>zDu?}+}9y@xc}%|re5`06|vRq%Pu)Y(<*D-{GhALIzDdY_`LCsMQ-)C zcr5zbBmMY+JSL1!_pBO_F|FUWzdiPMabD5R&%x!(PmhmC*)FN0%?I6>?tA06QX$^q zfgjSwgtfHNW)6F`u|a@~#jQpoYc~GNy_)})O($Nvm#glSTgA@FCH=&xXF=sMPSp%s zdUjb>`mk(=yc)S3eG=@Qw)@6AWIXG<)#FK*J6_$>A8TfLd`a*K)uE zqzOJcKYH1Us6cI1zavqJW%6>fLcf^3iU@i-U`M&^!>fId?EidRJ0&6PUfM#Z$34%! z$XyZi%{i;Cb4-gJiEmnM_uCjNi- znvb2>((Uq`>sMRujtIN@^;mT9m$%ov%cg{sZ`L(&Li*C3wnau*hlZEQx#+1W;_tSapxV%_P&m+x!o!0(SeD+(<1sQMO4`~_tTijQlCE4jm|M@)SWW!RC z*E;uiJUr};U7a0%J+9weKHk-@(40PF()(XsxB19qZNl3wet}M>g7f-eTe?Dx%2B%6-7Iu2BS>NnJqGsaD>QlERu1Sw=xv*UI zwR5%|Ki7JG{LBv9m+maO^W4Qt7S`Rz6pt-weta1>JoiJMRGgUGA@`zXx36X$EX%pH zo!{E_>b&k7dM0S*ZXKWWu+nc=Bkf0*acT2$VW%%MQs10@9{1Z9uhVZmUgw}L204=o2SM! z&b}7sn6}&^_4~ZxJ7)U6OW*W9Dn4*~qdDOz@h>cMZ8NT(?)hrkWvA+XzE3I!Wn5gg zq}&Ai8(9mg7QMN=ZEk}$Zu4J1`_(#9V|HQ7ulB_}tU3(OG?71hZgaT&X0Y{`%aa{q zkGE~ou=Bpq!m&=p-0sGel|T1ewJE5$-RP#hx)t3VTS-$b;$hpen)i{@tO|P^Jyy)E z(jL>$;n%F(?Y~q?4_w{izUQ%d8|M!)i(gyze2=O@Er)sR?DAm2oGPD-4mFRCh{zm$ z-p$O*@sQ)@J-2(mY2;HYy>>_cZ6|-ygfF{JT@U|J3s^FJ6ucJ2R9LOu1b!5pH&qi0i z9mtAZGcEYWl#PpSI~4j_zFp?)c7X$bZ(4I&@`0ah{G|n9ZJwsPyiKskdT*KOurqBy z)mQsVr9X6v4iBFE-Tzr*|641UM-7#l`np#se{TGOoZny7x9#0*ZB$6B<+=T4CVifg ztjW$RS*FFq+gWuIn%3<9^>ZJs_Pblm?69Pis6BtYT%ZY<;^G_WI3+O9uk)OV*Wxis{QF^TYbF=bKISHGl3N!_E|c1(2-&}JV?NLm}@ z6xnJ;T2$;cOQ&wW#S&xB)wavr*66ukUi6fi-vVl$U3nxXa$9!BiCAq!WK_VQIdOA7 zhi6Q3&CN=GdADV%rdr>AmG@ejq;H;irs3I~y9u^oq5U^y#Ffsw@{d!hLv7d5^@ALJ zcV2x}^lEC$>dQ8dSo_H#Z*J?2Yo_`w4|uWHA~Ae^&zc^W0-de51P;4Zs@PMLpWj)e zkBDvF)7*YoGt=BM(tNkq7hlg$h`ZcqdBD8okBh!{DdSk^u=V6BmwxHrcR{ydcgs~g zTr48OtlHMLcB|blejGk|-n|m)BQD{zK-%&4izIaeLTxvg8@3nb4R-?UpU+Tg1L`;KR1Cq55- zxS{!u;5wP=361EzN}K{Qd8b0Y%ANV%8OI1*iy>fB{#j zDpZ5&Py=d0EvOB3p&scv<2HbX&=}f4TWAOEp(8N3D>0}m{REw%2lRwq@GA^}fiM_` zz))?5o+{gnJl{GG>^ZLvi~mL-!#~dSNWZiN-AU1hUv4C zgmSExF8$YXiY&gbrH>=dA<`F_bCed!wWSvPEtJJKO++5VPsGXp}USaYu3cp9V?=l^C$LCA|HLEIQgCYLH&=$#O*{A2D*AoIqZZ<ujSW_9j=<~|vzq<25EAy}oVHx5ZFUI7SrO3tEWc4oNx;u^T#JeIQ zeeuiJNxoiAQW|S1fgC5u;`?X%JmkwNKaK-BtEJOj5_vEt6H#^_jj_CE z>U1i;cdI|Q$fMr2ep6%_ugFl$2{e?U$eqy03`IsG<=raf525_x9WTRU_3}5@$zOa2 zPJe#GvfESkn!2)!IW?A?FaKC!8)oEyXEW%$l1}IO+Gc(1>&ETM7Q6bcB8v<~7Ut5z z0_1p5rHp4OqxfF1VZ9Lf6Y`m;^+)KS2IP_mVOjl$)Tpo!f zuGHmE$ieWZp-x%ypvst@KPzRUwIYwjI7n|rMWv$ng}o=prJ*b^qeC`_K1@=Gr9LOf zZjUr>`C8+?YLz`qmnLv+FOq$*Nk zlMzUVIlGdNi7!6!-Fh4HC*&jkH2et}*-;-FC^q*@eu?hh(;g>bd&zQ|qQPwWyDSqi zSjCP?V@C&7wVIx%JwBH!XUw_*Jj*A)?7 zp7PZHw>+;YhlxoomHJ(l`h8tRujkQJdlkDakKI<+*)5Nw(Hryn_%r-5GmXu1P#)~S z9+*7B)iwVJSKJQpGjxHj&>i~2AQ%C&kjf!k(Qo+-Iq)595Ec`&q!LgO8bD)c0!^VA zG>0BA1Xjak5UqP8T!ZUiiDeWqVHO6Wvj!UzW=FEkWK5|k@S=vI7hVcdp2W{FsjN~@ zQgrp?;V$FXwJ_u<=DHcHr{&O@v!b&E^hIy<2bQ}87bFQ$wRV|2d??(&G>m1 z%GDIT4Mc-TLB;lpVtZmvp0PB_lGl0$dA*=gFUlZm@l7aWZAy}oPu>wwvCDGUGt3SD3f zv|kJ5s=7KLzGGz=CqEwZ+ll#J7yqkDc`i|&k1Ba7Ltg4Ad5Kf(Ld*v>mS;=)>(!L9 z50^*FiKekCdbL5XPgV3~i@wA+xs2)Stx^WwUssWZEwZShlSO}(G~VdsFj__D<uRigg(+WEgYxC5$g4E+s%;=Ii`~ZSVmLA%s*=wHT|UKKlQrmn#gv`^Ee6*!aW&Jh zU7TxKeXdC1v_qA-NQi6AXq9y1b?J(Ey@s};w5Gj~+#eV&DO1PrV4d8fDAH;XQ4Rt5_ z#3H;DI;o}TBu3b-08~nBx1(#^RNsj#M&eduOYZ z=Y>)p)=;ZqyiZ;n9na%fwSIwkkH+v`&jM=`dMJq=YN7{mZyiq$^#j`Y%se&mj+C+d zno+Jw2IUIW<@ZPZH&XP^yGSZ+LutxgQP(y!SJCffa%Vds|9(uLK3P_N<**C!K9{jN z!%TeA7H$4;S*!zYtQ;jMcMXF!p|*-#OCVRVR)Mke6eEplN*c-XJ{3E&$Iil4>TLz3 z{BFuS^~P-YxuSF4mr{|>733q{Z8KIr-iVVc80bm7uW#%e&y;h_P}T-8Oh;c&HI%Wc zuAYi+4A=?7+#u{yU%tVW*7FCGxa5N&9F%O?ER_{GSarSvzg zyuKwBcQpDdfN1@%sMv-Bwh>Z*Jkv>A|I2c|*BStcs2u(Xn{9*jU<+ zb(SRF9Wl(m-u{YE&I$(Qe6Ett16?{&hx~B~C4>C4qwy*xUKn(sir%iFH*tq0^rrvmZH+hX(0P7eN4yJbSRTDTm_;r-D7qDWar!dk z*I2v~=*_`EZ}X9l;XSRm%87bsiF}-N@=2m?%9g)by;YIXBb|&&lYe7+T&3udxyvdx zVvmi8cdCu)@xCtY-)XykOk2o_J6p`9x1_~J3~gT6kSIq3slVJ1`jfZqDzd+h?8W=e zhVs(ei5X+w_KI9ZeIF-__lu0hD~!Ax4eaTQN_p-n<>@Ke>+~baAb$GxC%?^DC@)i5 zif!^LBjpygggbks+*UXKuUy3b`nob#`@s(MGPdZXe3v&)*>D*Oh z#yj*s2sPzy{W;*rx;b*_z`1Xn#TiL5gh`F4Rm# z&c%?kolefnkn?`ZU|7fXQsoM)l)_Awv@~lfajM4Sm}1I-3^9il5$I`pdu5LTr_{b9|p4Y)`LC8)R8dXM2l?*P#G;MCtCesT@;MV%_Wl&OL!s8rqo1gUH27-E~8()nH6lyrsnR z0MQQ}P0P%>CMr7RDW}Zw|LQb>vI;xW|Md5$eWYh{EKlX)<=5P+rE{-FhZv*mRY2a; zRCIS6-Az!@OHuSv_Fo%MvJ&t5>un^z?D^NO>&WhgBEN?6Llrx8!VW*_Y)oGsy^Z9T zCrQpln1c%_gLwDK*!lnB+=rC&vmSz~{>RB#Kb&9RZxQE@L82lD$+|Y_zw+f<5Uq*K zxfR{aQ<0Z7^0NI`UQ(O!1(d~dkMZ%RJLp5K`(fOU#*%)%9f>FHV&vLzJV!jY5I;*= zEDfWOO?g5xND|LMy2(wrWfEghvlzu5j2w+!zakYGdPyd{($inR`ba`bOO-Vp z^!xR467}G+ay&B%*wF76$Lp`rmgGw0VR`{_NmH?(3fRw%|3|vzRAd^3Ol=A*6Ym?V z=+pw8mibSotl_HGzKoam{6F#+@I$%vZBu`_vWZDe$Q-JxW@7XnP;i_b1jZ?4iQKH)3+VMrXm!pVjT&}ZW!mk z`Yr4>UMcU&0?JTJ#eVK$KMPgLa8b!CGG|RKaoy3&M4!)!*sWNDN3`Q&pZL@F2ke+9 zQ-=CH7lzB8^xH=%^CSLNk%t-bD5aA}4b`&#*OqOdtcLYaUyu9CSLrE_kj^4g!)v8J z@1nO_L1&Npa_Ia0qQ7CnB3hyHdHI6e-(;1FT*7s7asL02&+P^1q_v7p?xB+^FPX{6gkW($WG1(!42F5ox5fEw`D^*F0`f1`i88$B7WGNg z%ktQ5X>3+JPaBTSidj8(3$UeYDzau(YPp0?*2fFTM^6OpD7?u?NxPWCX02qjJ0RwkXtE5ZsTRK28v!UdL4>$pI7qT zP<(11#j2O#dW&q`h#tWc0=gB7-OxB?kToqM*hTF zF2>4mK`BFLX|#&o9-ud7@cd8R)$2gCJ$DKy-*DABDax0?6)js9Yn>VEKb1xPB^CLL z*+0xKz&3=Q^s*JMC}(Nus$4e3Z!-WZcth;ILcA9Sa0uk-uI&*WzX$fb|Y&N-#>)0at@B4#Y==%rxvP;AAV`Q=ZoiSb7VizS0>|&CN{WDWe z+-8xf{ILnKW`wb_o>I!{Eqy8=zw>^`?|*$2(N9a0v#{e;DmrFFTXFDpGP4Ou||fmyqr|>!u`l%<8m*9+>0B? zeP#hRyh}wEkCDYv^rb()$e+I6h;bItPKmWU^!xK|D&MY->+-jWa_akM;`k!`3ARdv4vvCR#GK&V=O;K6}>S2tCPDv zucADnEivbsl&f5u^5=Yr^=6IbqpU6;tC5SbJ}Ki_vRD(+SY0TMPKp`mM67*cEUzbY zc6LlfFVX0w1)2Uae-uOdq`M;&Yl#`l1FMb8IZ8QW#k>GzP2C^U5%RWG%Gq3AuOe@A zoxC+FW%yfHhCM2B;@(J``3cn}{`rjmf2qPXFO5x_N?b+$fi^YXYmm%GQb< z2FhZ+8)LdZpvbMRvL2kVva=$RoMoUtu?DiSe3sVbv!_lr6j%A_Wz5$ADSzQY$5r&h zeXd+tC6D`+JhI-SihfF>9~%Sx+*gs+Q)DI9aWz-=GWbI$gJ+aMUw1?q^fui`YHlLlX!;eqG_(nQ-se%uNk5d! z&|bQ#$gdc(E3T7YEa%qu*Tk{<`?j)N$G-mCmgKi01IdT6e2RBy#>?}h>jlVQv`QON z7#SB)WV}RvsA5NqRLbruGTf_^;qU@vbhm(fiFL8+B6IziMO&$@Vhbgh+glpSf*q7# zo^NR=3wFRNplDspL9c|A`btxHBg0QxC+(#EU7+5HnilnMEqhG;5_L<|r}w&=l)z)oe7y&gpB6@gOdMgQl_r{i+UsKg??<~3tBP9hV%QM zS@Np_^5>^wn-ahy_`Q& zhDy3Jq$qc*^pE!%e-`ipeR&mdj`x(`*mbEG^VlQh{*reA$Ikts-s{JO=J7t?c-dFZ zR?%N^^jA3Xzvoh-%;K4-XrD7Fo0voROnGB0QGUm}Ydo8hwyMbSog&9kk`MXRw?QJk z{68`KQp|_=NgAV~r(KGkSW6XM8#}(RqG#SPP?7ITVQ~x-PrZ3IQ7(8S5L(l zhD|Nwef*MSNqiH@n0*vg?1Q-(h{hup%GUxmPLY zTcw=50ZQ7&`U6(z-dfk*)mPEqEA(eg84c^8{yG}Tc)$2YgJD~zzYaQya_P#azt{d- zrM)nxy|B==7fV&jxKH7}izM zPH{?X;ia6c*uYEL+Z?OL1*9Rq6=5t54LU2L=q&&F(?AuS#iFyIn(RUg2Js5q6c1!$3-t+&&{d^zZT6{$x<&~UM#rTRSCpkGTxzvJaB75&92`ty<=>13}I zjDPy;Vg5726SDZum$7qHrMyXseqYJSDs3rWDWQ)o^s_>K@k2TEvJri!(JFFhCaWyI z>tIZ$z39)2H}}Lh$i#l}6KM(=2>bk?%iCNP*~SaoE+9|hyC{a&5`BNJD6%cAyB?3? z4Xg1Ch!!!Gq&A#Wyb~;bL--RuGbxTSt82^~yC$~-Tkbi^LsyWIpEZ<#e$qF20R(~= z11YJz6XUziTiJ^sTCEGA7&5^s9X7WCs^q$o{>y6ai&;0K!0`W#{iZTc3i*koUcfq8J ziMW?pCx2J518Zp|&mDvvtdt+A)WI!^=9p)PERE?qR;j|J#e zd>_PE`aN~_6r+;A%}V|jNNKvX_4cbMoPQ3qRpOgE#`0#3%~yu{h4;|D7)X z>s4$v0m~5IF)^0^9=iM|spNl?l7CtHU8SzEM7>;5MfNd@>?cTNIvH<=Ezm0~g7Y^J zpHyg#N*QZV#zb8iXHyO#2YoxJZ@aovjsesUaW8Fp%XnYc5m|p#Dd$EdMMve^M#iog zR_MZ9SARu+Wghtu^<3ZX2^TR)70E|M^5MuEQX(($#N|_KMrHp>k zCo^502~m;j8|2zXMJF#6olKL~Q$}OgU7=HrioDk=(%{=Ul+V~Tpc&aWKlTwkxm+>!CjdD>rqJ}y&k z!~Va1?sYt6?m^jwyoH5}afU2eti=!~yA^moX5OgCXsu30d}i$b)M=6L=ejoUsES^@ zqSwAUy^3$D8M9$aN?b^1!@bbGSU*JnE6aRM@e?|4$vPJuSxd@;bq+={204>a#bS&o z&XEJ;dCaGtFCUjr$S379@&J=zD*1h;U}Iy;>)4K3VjCY?Q-z;v?iXK}u86;ek0`-bya+m zyO1gK`Hq>fed7Loo=QGe z=-QAqD*CF5zKZ>juKr#^^zFnqPL0Xg1YL4g{_@)f@r`rCHcEdVB2&(%N;zZYrYidW zh`t>PD0gY{Cv012Ui?Jb!e@cKT7d;NYo)W>l`3*rq8v3+3dgRD$xx(Mt$;j;Z^#+T zgE{9f0_L2*h%OH`f3O*mUxY<|m3Bb>Lq!+W(8Wy^yT7TF-c#D9Qa-UVrMPDh-?RL& z-imV%CU4idGp1~(O zE5BM;%K?@AFIDnCRrwC5vHTBG@_$_pQITDBWGBAaY;3&Wlzc}UscVB(%8*PM5>)i? zNT-J#enttWEEz$e=bdP`)XOMD+3zuF%myrHp^cr&MILSdr07X|REQgkVLz z5XYBfO**kIop`$~Ue~ter=35ZC+wptZCV*6k4;T(smQVhvg}nrT?kjnU!cwgGmOYi zzz5}dZgHIoII+4^amD@FAEwtWA*BRu5_)XU7B{78w2C1-ycNSbMJ^ z2YBr=cpX4)qh}-L2-?7#*%&YS+PK+aOKE}EEaU%Qf~^aUX6_fbEe2aZF}BtgIQqoI z+;WF~#RTr_gZt+NZhr%})!@_rF;o5{Yd)eVv360uJra&@2`Xy`YhsMrBVhVFFuhdZu_1We4!aVlAanQ;K+h5%&~*i$<{6(hI_!NUmZ4KC76etJ`=cRR(7vDtEOW4i|CAV^8r!u%qC?e|C{#{>*d0mC5z zhK+#XAz&Dlcj^Mek6E_|_^G{}u$z3d2jzG=w(A{u7xD}A|321JLRz%*2U+kx&1jKU zk{u*qSQ{9ACt#li?6HOI|LF^{4XmR!s)xY&4TkeCjSoJq{-2a%lhjV!)85JNbp9jY zcp5kk6w04#%D+ayxiN4)V`9kGZDS|}IDceto-gPp6Zpl9eufJ+T+cR)RCh&SL^!fu>V8AJ{#EQJQY1Z_y=&gN$|S~{C*&0kFR8`IL!8>s^7p#RR1=S`530J z6#SjT{;p8z*o!_Y<`RMHhX&Vcgfgx(yxl9{UI@6S8r+|RANG9}(i}baNVvXl{IIKf z2iL&+TZnafclbF0m-o0l?5kZfdc2Qb0s7Eb{sd**E#Tf1xW``|J=USRuLmAM%t(_I z2X&BV=7GBn@8R@IC}7B6UvXwBk-xMqwGP(M|{m#>OULu4*Y8kKdFzZ zy}x4n1-xUxJJI0%p`eAv(86wk=N90(lf%BZ1l%UGwb-3W;P@;s8D;cKz7fY(e{UP^ zj^e58(?aZT0FH!;>JN4)Kgn(x%@Jujxo)>I;i*1{!CJFv+ z4fr?QVc$N1uO{GYG_E7<+rz$NPAlZ46+C_TM{I|H&qRaIF@g7%;C*NaAL57(YWpXc z__Xy~bzfNHCSLCm>b}y{J=@@OA-;VET0+xr8H^SQ`Z)*v;Pv##KHr;+|0FfaVgKZi z{dB*Sjtlk=^sWhgC+MU9bf;3k2ply9N8?d{bPoFjIqVbt32;ojEV|#B0-ePho!w+| zfW^s3w99g(>XUp^|-Kc4Hata1cAl7UB@!Q)?oZYltee;Yi?hioP8$ksR5R)4nv8Vlbq z=<9Fj>n4XEdVBP|Rx|Lv#Btpz!T)h4CT?|dNNY!B=$iyxlb#+3X@znsWwmy|<_&|* zVLW?WClosrd4$jFjE|>^3OZ;F9h4I|N&-io;pol~1~#{PxPPLuqWf!O8G|_WlEBG% zaB^$NcUvyqIeNnQ4mV6mep~rE{O$<ga!36w1x&Xv zrdU}}&`fh^<{q)!E4*p~7j3{rLxGD#aG?zsYlV0z0z9@DO%xUU9&P;IDsb`-IC@#Wcn33+UzQY^!U+Ew8&-z!)4xPmAP=Dz{-Wu@ZXJfkJ!u>&7m4wz_ zDm4T}Ad_;VEhKFj-es0?aL{4!{5o<3@iz_Y?C13Lu& zbFnAdX7>Mk{i?&qMuL4;z_#JWzU~4)?ZD4CfuGIHPjcKAN8Rs-V&3Kr!fubr8{zgf z2Yzj0zx=)4t_W~MnA!g7Dcm`3h;ufcnY>)kUQ1~2-H?4Y?|ceP?+V#xKck$~PSE>M z#ti#H!hRbs)(2dMRWRsXRID>3aH_%S$03_6E}!Zvyx9WYo7i*o?STi?TVN~fp?ZHe zc;pLQwg;C-4CeScdtuwDS7C7$aHst!4`Vv%D+ey!1YCx2iCTRg^3B>wcpnJFK@hXL zDjq#(_#oK#3hc`Z*f&bxyA}BMo{paD^nm+!4%==8?os`$jf}hPyUg+ihvJF+pm-mI zKL;}gcyd`8Fc9MAi%*BkUd@YR3g%IAV|G?lXfzIyB{t)N4zpMKA)GSK_j^t9Sp zj6Cmxt+d0BeP@gMSya}mu$A_*h~Z4fSqNtq&cZm0;LOEY9A^ofrEr$PNzd_;cU1Qr zK|7Z~J4XfVz5s8hz-urET1;w#*E)vVgd_gE0je=5`Tc}-@VC$fZq+ye-~PC2fVuLv z5WaTo{~O~w8whJvRM0^jbTC`cPiyGsH~1OlOT5~Rx!c`(H~f7#q!ruGedQvpU=2d} zOl8qQG4B81$#3{v1*<0Z!?`LH&wgAe^&R_y9f^f_>&vC3#i!MZ#=a(SuVt_=4m+Z9 z)oR#5x$16#_YUCwrBFU19{>S#>SvE?0?zTkd6|H78{nKcCHmfhW{f}5kA`ru^?u8^ ztkv@bT>3CBaq4;D`ftqj%Y^HDo9kZ>`E7Yw&wfu-HGpX_ekm3yhF%7u6_{IE1B;#u zvpaE8^wMey2lX1kwtdF7Iw+gwjp-o2?K|`@q3m)6Wz$+M$57(;`dNX$j^OWA)MfQd z<=I&In(G>gHxRUX1Uv=X6zgjOc10Vcd+G*)eqJ>CX)S0V0U9U)o&jaSLH^LO8)Ju+ z0tda=>#J20)N6Ax*<{-*y#1 zOYPAof(F*HCAeoHl)cxKUCm)vcLB3RVAjxK(;EWUmxAk#h9~QTjhRf=%t8IQfOk*E z8|{aQR|XG%*m4E|i=6=-`55}`-g@}D!T8%+$VJtWhiaHyw8d!6)w$*%^!Qr@mFzE+@+qKN?bTwSyCkgy~ z2`nh~Ek3lCJt=M-<1iBUq2^#O-y8L6y~e!#v_Ze3U&T96x9fL97~6Ol1m8asxa|aP zw+KFWXP>b)n843Hu<2g5ORMcd-GlWbdZeJ81cTXS0xupoi8H*sFWA_PZH!ZA1kBf% zc>2O&-wc6|WbpAbe7DcA5#N+AiI2PCH;oa9k=fv6tsbq0I$~?MUga@T9psG2@CmzV&ZcF`>qE2D;+lefchz3>|y6z zhoJWQs2!twPj$X{+~LQjQn#@WhzB$vQEkX-6HiM7{!+l-Tp`aFh4!B3Jiia^sORN7 z{Qm_$*gg*VVaM3yLmdq0W;pymAYj+U!SQCFQS~BdDB>4X_`#()B4Ead* z5vh6b@woyYl{zoztSfYu0uG2Ln-}SCDq|)%xYpN_`orL0bCcyMK@HJEbO$vLI7F3E z75TZE$oBYp=Hr?>fL~2Ndd=k@NYc(sMU>v)`;t~f{&fq zVyuY^AA%T@ALK)I*#C^_x1{fg<6XszU}P3NJT(+OvMX>tj`ImT)$kl@{1E3yIO%D3 zdQy{~INO1f?h4Ti^e&vA;`^WB@6U14sEkG`lo|Hw%J4V%{0r^PgAcGOER9F~>}Xpm zzn#KnR0@^mzwXs=+MkAK@VxrLl?t?b;RDcBtQI~ z#cDk`ge6%=Ts_BFrm6P@`)kAgCyf289ro1`eCx=*Y4wWZ+W!B8{apkedxFPZg6)l9 z`{Ty;_Z>Q%5W>dB!n2G`E&Z0m_Rh$Y;W#25vhdE*FaSIfzD4z&g51y>_WR%il|u*B zZ~1x5*ndReKOOwG(BC<#A*(!v4(?EodsVnVPEU1xdPA6FwfJ~XAVE7{SO)) zDPU2^V1dRJ`S?Q4C7Q7ojB8e7WO>Np-|`SIR{Kwz+;qxe*BHU4cE+b{ z0rNt@e1^bRhT-cMht0Qz^kBy%Hsh|kj?!|6pGgfwxTXRvNfQ z^-Ugz7UR^9n795>C*p+$iZy$X{d7?Oc)s9!8}=RjzyHC$2L*2Wf}0FS-J3$VT5O(V zT+`Lvz>eArYp?CUJ*ucZp*JEv1TPVwq6 zulaKTchu}U17&N=bB#qEKHVYU+1kY5+k($e7@u1STxWsn1_IX)!iSjp30(iAtHN&5 zgRfElPa4mZMI4ngvGf}73O;jS?@bY2LrgE}ChXU~1S{Fo&a8%6{D{J5B{a2) zb>*qg1dbkOj?&aNL2qOcS+WtUOTQ5E%WAtN`!&n!0$WiHAH{t?~-+Qu=kQ}4oi;h*7|%r9`qYY!q~u%MxAXlS=!yKO($Vb9}^4(S9B1ccC36K7|me)9mX9dtp)^Ql)g*fBd<$w zJ!QNh-Vf?1Yz^jYDvu6&isJKtuk>&B`sM8BN6>>(<6(=98@iTqB9*WTwiHoEkSqK* zg=tNd9u8iH37C{*Tl=UFLcCbsnxi&P-J@m+S}Y7L9ul-N09rZe(CHBDDR(pM#)I;a znt6ozQ0lt?PQG0fXG$#wPD_j?#tQzGF#i1&(#(av+z@p$GUJ|*|Kv4s*^K?4 z1nvfcyX4C+^hp2(IP#;8S{J`nxn0i^{3>qz`Z0v7)m2l*RjYdh%!&ZBX%0-X9k%)H z-7EU6pqm?j+l@vy^91{g8T-otKk9Fg-4vhnLwC;}!k;zqNo|~Uar)16DD^LsJFHIV z`=r%ObyVNWI-xtr`}K7W-+mV`Yhp0FMX)*7*jx(OU@!TCqdrzcr-RytmYC`C=YhJY zu2^??B;G5U#(jlsT}n^Gxbu2F7jr{5Vt*1^OE5gwT#;vqTJStk8?S@c(Yw^^wF8*ackKQU5AwJDXJcqiudbpM1o2M)ZPD|} zgP_ry95_BNXsal+^@tG9dEn_D!&3#|m`nzm!|oT;9J6Sbqld6jU+(qvdUFdz5d=w>a)YD+p zOM?F6fa~1_`p3My!0BLcT3EoVA>$RN-Vk`r1+Nb{^w24Uv(54MGtPM99KHto)Aku& zKi@6W%k>Jq3cHNkd3)+1Rtj)K_*lKq1TU1Ex1&e*ynu0z!FUre3%2o;XNlhxz^guR z>xXd-Wev3P-jfdhI>3IoSa4j(*K>G1P_p+;LH9|h=Ptufc>(`MjQ>KlQ^3C%@E-sJ zC}v2fbdU}7+kJu9u8VRS0JmTt&bG(?cyO<7E$Cwi^zo8_!I!A0CoqWaZ*9>37I-LZ zcxVBeg4bFfXwLH$N1b&9yy`PvT8#?n&W~a0!;#mc)lPwvGT`J+!^tMpZ`&cl)#l8a zSmBnQdOE;&=uT;W=RfRcA^4hYd~NUWb&JE-VgG~1dJEhQ1$T!8?G%T0I)`lXdstW> zA5ruUYzpdy+EqHfL}Z*pZ@i7bK?CNXv-(EJOBI3f@<3i11OI~cSY7(hkZ#n|LtNS% zvXZ$>Q@aJ+vkdMZhp@Fi5{DB}H^q)U?7MEwRTqqFHfaCuOs?t&oPyu8?b0xeCAVW$ zhEFkur19e?p}MS|DNc?U%~4(S?}R7C=^a5V1RjTh$8NwoDpn|O4GHmR>!moF;ly`? z-W#&{0zT*Tj{>%t2HV9UZfu<0%iK&=qaAkr8Nh?;^ncKw10J=Zk$yPipN*dD&VzQp z3)yXbCamYfXPe&%Z#(zVTv2O07;WdPx{ucncieuA%@Q!b0+>$@*>ABK2CnZEe7}c% zAB6=^{I~CH3Uvdf|L0|zOKjCc2m!YHcmynj8qi_J>@~@x3Ztd z^g{yvBY=NwYV@-LIcQP7#7aIJ!A}qa+mBhn{P^#H)>T{8OQNM-|EQ0bp(7efX^)kpRMo_|3M)XNf+jy?qVO)xP>hy_>j#$ z;N6=LPqw|JvF22$>D~~}R*TdJr1dve3mlCEN0$itDFyx16tIgK?3%-W%OCki8mD}h z3jc3r{|}=_jdf(pY>D6b#I~GBvbD;A(oPuBWiKb*B5k^4_zG* z?WWQCNcEnv+hWc};}2>by93jp-S#__1O5`?^Nxb}oT4T={GKJ~pbT`-*HQ294h`%R z+J!{MYOfwHV4Y~N9)fx)Z`piivA`}S@c1Not&a;>j|SG)I{ZEFu=^^(F3hMRuM|<4 z2&`aSSZ)7{u~zD5_#MR)^$!!fT&z9IA+2!#o1m=(#%m|{W$qAoDhr+#JN(TE)oV3R zeW|(dH_G04*qd~F^ql4x;9JB|<{hwwxU>EdhbT@}M2yk8lE?8sl|wk%9B@11h<}12_mnGO3 zXKZ{zz@c~n4hQr~hppuV{Ko?STCml=XGXD+&AZIdT$Ut?4W}SK&O)ke32uV?QGbc}yN$W==f3Nse%pVi`fZ#x$Nn|z z&_92|;ny023tdON`aeo7Li<&eu{o~GhOo6ZdW#4o{3rKby%IC zwATgR_Q3o%^%?TLnOL=?jcE5e?l1jE&_{Xb<8hOdEN&K;B=xoaN`H+t6aLbrfNfMf z5w1N1%`anI(^OM|?{VO}BkYcz_w9i71ZJv=Jmb3_qx1^cyRa(mb`0{?Iyk5Ue1ja@ z7}bm~t>A(6=^ehj0}g|E(8gzm;jgRV$6`g#eUOIYR5z%jE$%E|rn|Atujse&E=FxU z+uOpsT~|Y$WEYi3ht)P+VC|(oAfm*=#6@#_oVXg26O>d*W;y?0p>0ey?A+!VO7&yJ!;#t@E;Y@LHUhPUC~$RqrYeFeluM;A809TXR| zUv^ym58uc7h-WY99^pjP8|7DXRbMxS=bZ)HNyCZ8_Ui;K-NIU$u6_!%A*3@K51HzO zzKQ$$C-m=v1}Z@VyZ#IIQ{h8ND?aAJ;7j~FT~e*=Go7I#~gMhJ{LXLS{b;{6Sd(#wRH}> zT8$B}6cZFma0)f*j!Raz!MdNLIlvbtrS6_pj`Tjq(>GPmP zf7d|DE4f~tR~nkD=v9X9NOAq3uqobDC=BbNz&9J4etxDuvFdJlUY~UK?o%kfYD*)4ZCZ3gdE|%md;{ntk?E7Vzw`u4e%q+f- zepx|W(i5rlwq{e{)5@#l%@%N}!MNbP6hTi*Sx>#y5^x=C_kC=YI>#}CwMQIwR}(O= z3d|FyU3kBn$hR6w2(+0`hH$sBK=;0<>Ph-OL1Po4vBD0!DhQmE1t&K$Cr6<41A2*o zT~)>|PQ3v8f^GVRd)t1NYzBqqIqX;|V0+eJyB_5S`<4{*Om;C2$Y-C}UJK3nZv zfmV#(gSZjxLQRJss{}u)*q9J_`OEO~UZ{TCmmbFT&qM7>br&$M4vZT)Z2LmsZZUH= zMm;6iUd?b&+h9vEMfh0T;()E+hA4FzcuJ)&;2=%e=hf$eqZIWUa{XIs8&-!qj@6`o z!@gzP1%HRKza!O+@VBkQr}qRdCli+f)@KaXYXpBQvgPenFZg0((C;hxQF1+eNn&4q z)58TWYJiKqA>XW)h6J>9nZvgu;EU=HYjF|tMPp}A4T6oq97+~a+mxkF0qaxxHNnTq z>|;IE!%=?+fp4!MmayIfFpkOxc}2|-#_HH&n(&s*gZ|)v?e$m8oI848(cR@$WdwF;X2My`+1L}5sor;3Yb-5 z%&-oYfXg7pWrH5=D0^@yuUntL!d$~lLF=<&cWI-g6b$5ooRIINwVN38eD$lq=hfhI zctA_I1m#dkGz0qs&vl&D|LE(C4Su;7#wQg}|18Gul>SNJ^$)}A5`nu~;BJh=f88}& zZv&0qCOL4O?)dC0fzPtcCw5MSP5%8Le~lW-m-Jg6kSUF`Ddwl!1EzN&) zf3?WOJz+?A*|@*f;B*zr^%16TT$ocMuFv8bq8|mj%A1lh1Uv>9JW7VNVC!GNTEGqq z4y^wYI6Q4QO#D~$T=5j>Xr`mwc@7><2>zBaakX5)bEd)bK|xowp{v;jYd;!%J}55Q z;J)5I{IbMhR|()8mE-#}&eZaj)|G2~U9KpWQ#=ho4-F>+34SIuMe+ulMX6&yEZmqU1tmGK0w2(f^FjH8_^0vfRM=n5v^g{< zaEX9LZ-YfmfrpcZhqVqLb_g1(2Mx6Xeo^gR6@%R-N4Z@EZTDiEXJSQJrK$+{m16v` z{<@?5Z36$5f!j5P|CRzSzZqP<6nw7_-#eSUVCXR|y<*Mu7kuk!_@MVzM+@BF$lOm? zn+4sqG`f4&;d?y+w>-uTD-{W!7c;p2Ao$$?e)o6y{Vr^dil@4;xrJ|cd{+VU69)4! zA?)m!pa)~O7IVrQ^-hQH9R%Ns8{c;0+NgFvp4YxxqJI^9ZwTLigrIGz9odYZGY!7=3~?} z2QGgZuE}@0&hj;dbH{0Y+~L~~0&cm$tQq4LuNDjbbq)A8O7M?XHzFQRJ8Zbo;oEcp z`{M@t!p}#~wKoB$R|+;2W}B9(lY;M9{R4epLD7OoJ~``6>&(WV1h`)ol(O&kMd*f}c%% zU*i`ESpQqJmo{0pFb8W0ZhpkItVZSN2#lTDQ4L@Kiwd3 zSBSr@R5gM3rr>?01BWb!4=K~5+k~>fr!iyHQ?C#4ZR2zf^Q}}dNBK(xpJ*j-#7O)K zM}B(|_)`Ch>Z611z0z-;jgI`*2K{vS9;K5?XzeSTzD{X}jWrE7zF&SWq4g6mU6eaA_cLoCbay`W(l16Es={{!KLa zw|Ch8g@Es+Mk5=5Z&W{n;-Igo8^1Ezgf0F1m8iG6!MC%c?oS2m%K-PX2Kx^LjE)$L zh6>u7V0=F=@H30~$yfV@80l#IpMxwtQQ)uzIPB-}uZzH6Jh*GX{C%em2pE)t|D_EE z$3r&SK2--}S<_MAj@lSs(f++w(sniQmZ1K` z{MQM7M>9P&dhU28<2p@EaoB%U!2Gbm{04)K#m?rgq9(S+z(&woV55BosVZ!YXB+n$ zE43PL{2}eoL1of!$@p$M&e}LJr`I;VTXY}o5~H;X&vtIq2Lx_w8g6Gfe4h+^qVjqY z+x@35F5o(YaUHGn9no!jad4Yw_`MnU1>1j{pDF^su|{9_2pVby4NVbjOf)u53w>_2 zU74}`QlAz0{n7B74XmRuXwO)$)xSF8YGDZ50-fTS&N;9vO4l@J`=`KJHN#n!fNL%= zEoN|C7OLCollta01>UAJZ=+OM!RL1D^G5xQz(;HF5&JZHPWX_)^C9pZHSabCT-V{4 z*^g&&e$-1G_#_sMe(txb!Dp4j_ooHD)k58)jo!)w-yk=ZH)MOSHn6dJr5Lq&zAa<3 z8Y2d!?i4gvgf-VswGr^2#`x!}5(0)l7!2=5`6z6`_U#A?-OR-jw z|JKe?y%OIio2?(D14>F%SbOJBeJ=L8z-3Cn9nz`wYaaX>AZTnVYb9Up5_oI}9$yUkZFNI>+Ty^gw%~Im z_Bl?y5-Qi)OF6Hz!+IZ3Hg9H(b2$u=(?l?kSQeHbw&DY+y~#2hzSaufuj~ z2kc?n=zY8vlvU3FNEW~!wUO9iqt`>N| zhIyZ+MhjTBVk}3h)&hqW4TqmOu>2sz?*-lbse1_=9WWeyjBBZ%@Oi;eIx8c1E8P__zdod=j!VsKcv)PgH(yY4F(yt&SD= z$^uu}hOeJtqty$QPd4IGb%lxD?;JSpcG%Zl;HQGgD@z3arx;}O1s?Vr9{v!p9R|Bo z4YvCn_RS69M|_gsEew9Np7<{g-;-X5o*(T1-mVpJF9ggp4erAQe9ALE3)Lw{-5y@( zit3vTh40s@v(Od2Z~Axa82pcFPh)6zJK_8WDqRK~qUOJ77M0eCnvMCzR{_infsgMD zA7>r*#$YemL5Gb3$_YbY?+jyau7KyI!1G~fB{+VvIpu0_mV&H8PlwRkNQ(sA(}8)0 z!F`H=dpX8kt3QERupdn^LAr=zy!xq?LSAadc&*ii1KiM+_FaZS98dmEVQ2N<`fNy> zgbN+t=p-+tzC2%j-f`U$funB?M|wtdU;Z07_@WSxS8+T}QJD^#<_MUUHL+eAm_+56 z0gTC6U0vY56Sz-y*!+ONcMM#o8NQzt_-bnSD(diSsqu?=@pa&9A!f2sejOQGT|=w^ag{-xw}y8J*d>ZA{ccoqZVB19*n%nBFSzI+=M@s<^|3iVoX1 zh4LrWPjS+Oab1I%F1$}A@Ol||Z7kSd9rpJ&x`2FCDTjS)!GEy7Y-4DiYN*ROuB-0w z>t2ELROWn@s_VG+d4aQihO>8Ji!CuO7gd-jF8U5xVBc$Uj%=zcAdQZ^v z3C8^u-Na#EO$R2s1)WS{o#d;h1YA-~EW8EZY&$}>+x}@9d>D;q9p+)*Bl2pypqpP! zJkm2Cmm6&m4#cU|b}#7SR*tQ+`W(JDK+sobpw&8rgVjd7-lAU@c>3D#^m)ie+c!8~ zVB<#JF;u?glIAVe3s@zyH%c86u%5`MjZu9~ULpT2mMB*BG;w_tk-gKQrIrq!+c-Y! z;_#`1X{r2X_&5|@BhO1VqGVJ*UQ1{ zc7eOE40m4v7uzrP%k}3FlA*v9ZWFZX?C_tL~^b*bZ91 z;~b$P5q zi}GnqXMMMG-;?0`C0xf((C%3X>0E58!_Myn|LJXBS}!|07CpZ|fjOF{{t~pC2;C;J zc0Kj0z`GxIEj?3%J;nu&DuSO%hNBfiJ=M6LeL73<)wfxz)k0hrf-kKL;_|S1UBJ6L z@U96ygRy6I631N5R2>B_$1<1W)P3-Q@T7yTwe8n2pt>Kb@vyFQ1%Z<<3?~nxj9`D< zYMtWqF~?_Z1s)Cuc-R7rqS}B~hU+IC<(CEiQFf7D)(NpqvG=^A%y$HPS{i#=JL0~o z!=Bd!y_JX7Dj2;D7PuV4T-MS}LOl5qg&irGr=9vUK}S8HqmB-{*EsN~>+tDS$8|de z?N@^KTNG&jgdP~mgVv`XjYb+d>VMr)e@_!PEPVVyx~F5Fs-p9ufjxqK#bIA_wvYO> zf5)0T>fh?9ze|WK8@HdCm^vbG)e~F|c5rox!=CAaenzu?lsW*P@fFSkWDl)POR+K@ zJ~v~Zf8yS}|I`BYi?A!I!^iFTEGl+4a&Ga|K0)(kq4#n|^IaYOj4?Rad_(-vQ5`H^ zBjlDtf!y-B!}ec*MO5E#6tHN@SWvHldJ34=7r6Y?aQU~uWp8lV-{IdrgP--!YNVuT z$4-YdYRBFQu(2`Qc#tx>A;Ar6yWGgzB5=sOK3$L!*p_9zY$zeu&M}2aSgA67*9O z`q4Ax{=>}zub&uR=QwOG4Q!(FPkn<+4S~yn;4;Z!Q;OrW`Nkd_vljDA#7Po zb^wp{jJ=Hndk4YZ7lFUcS5&8cUi^E;pPmr8u6nD(bLx ztDuFErX6`m;B2Si>@ESLe!!@qfJ1NKP}kt_vY@3}&{BH=qanbkl*6Zg3;I1^@T=gs zwhe5K8eeyT&2`vj%EN_qFTu8+dDm1sFz+6=*!r4z>q#n{9__ghXSUErQ>} zV95}JJJm~;*}0b*urFJkgM9~hPP><*{$B`|M}6F0dI-g{XY^X6Jv&H@lumb@*16eWR6-kD~27huMb{`e&1)s609@?0s-2 z*3Kjz-q!!o<-Fk_aF_#+D^ag)uc=Ui)P2IlWiV)^?+VLXE^k8ow1Mj3hK8CEX!f6&#->?3m9C5t0p+=UJxqZ z`d=C4m*nyf&~tq{xyQv^*zaK$1^r0kg7#tFpnn(m-e&kNF7PqT@Uay3L}{xM>?v)u zMWZ0HX^z32u%W}wHYDg&ou;c`B|p5c9f%?Nj?I||xKF9P86j*4V>%9?j`=7*N;|oR z)08{?IYvJ!`7-{a9yDv3ez+$>Mlm zQ-RC&S3jrwbjt+$i~4PnE*Ij|=EivRRcOxue~nx0>mhJ9l+m1`mN@KvRKTIU!6B}g z&zkkoqWChr%KAJS|BsE9UKZkJ1mfm)!@Dil$BV|Me(tosZIW?HM z@l+K7&-d}Y_k@@zZDL{#>ZTlMa}|{xkN+=>x{Km0i)Ycx<2-;daSs906ku8}fayDe zzu89byFyx}y2+nXY?J>K>t@(RdbYe!Oi}J>tPWyM?jRW6ue%GFHw5P6LYUc@9K@bZ zRQrW^9*KA!1V3#b(y!OIA`yPpHGVb~F#iZv?u7qR|FVmqqYsUazA^fStL(egZAoC0 z%W+0CQRno3fKRXvixZ7L#t8N&!2UYM{trXflb>`P#9w;_{?bhR9ya{>m=Z63?b+&Y zj6tZ)`CI1;+%y6=T?FiYL^+>2?7BhVbfDq%+K@hNET_Tm+6DR?pic_+r^D_%WB*sM zBdR^F#M=8(4-$MRhPx==3)d#{wFmW;f(AY?8u%ObMYZR}**2wS3fMLVwhIIdV!)tQ z0S3onzdAH}w}A5i#(BCLEBIaGw+Zl_CitEQ-%}dOaeuSG`TK_RlE!|* z%8w^Mrm*XussbM){6LomNu%qc&XGuYUF^Yb9R=bxmL^j!5X0h^M*rVjiL_Frs%kB8qy*ze;Q zKYQLr_-@DBg#U$oGicwYe%Q;Gc5b4#D__O_ko~->uqoJPkxkTIrog6DwhQeme4;Y# znuAoXfBXQie#VK!qkhNv2hM-=8?YhWeg%JDSQELY?gcCQ6xiXf`0v}&1=c9_jG*`T zjNZ2i`s-(u)>4Rv9AH%4#KVIE&ZU6!JFq>7hi#)gXs4Rd&L%+{&7h5rfSafH~$ybTVV^sU3obHSDMw&~R6wz7nV>+tl}x;BRU8J6wo?(XjSQ*bvmRpIK-< z`Zf9|frq}#L%w=f;PF3($6W%3&4J-yfx|5LQ`&IYMZgd%w&+C0Fj2oD_+18mfA8>Z z1nQ?)ql0|5{eeu>@2T_1sYmsByhnIc&`KfTQ^jyJScrLKWW>BuJ7Hr~f8jpQ&kn}~ zoLT^<1O!>swSAa_r{boE{IEHr5d46~J$f(f2*Uk3eh#mh*&IKu^NU z;8;*zV$X~7=ml=q>PqYjc``@m0$ZC0sC?UI_GWBp?cnKZg%CSq5IaW&U+*%$-U3^r z{GfT4si8K+#@!*TU3#O#|Kz)(=XzQ~D>)&*Er*4`VMW8?4e)%q!>)gzlPFByVV(GU zG~A8rZToLIB!<#`{M8u&mt_W*We)$!2tH)MhYA55E^y#iSBRNj%xk{N{NiFC2`94u zZTLw0vwx)b>Sd1dlZ!{k^YzBRaso%Kz)@|%zjXLlKH%RY4vg+`*j5(41@i^zihSG3 z{O#3Gqg=}!{+TsTe<$lk*h59B4d6B^FQ$Rp3Q8lN?BnMLa|H}$7z{cK9Q0%ku2!uC z9MXV8IfDbOBmXFTjk1?k`la5;ivmu``0PC)cE=)i*Ep_gCh*r9{M8Y(^A_W~1N-3a z)f-T@#gMTz|3`aEZq~2sH*kMruO94G7BHMS*_CVKA+yvM+Mxd87&1zvkFuhY~U4x2Z?=BV6K1MNNKJ$lx*&;O&{Z9;t5 z9R4Qr@i}l-YEVdPwmpaeg|e(QQkqhYz=yTZ<~8akPXHfRGAGs4hYtUG3A$+m-Ms0D z*+YU($3drG8D6YC*7jU*lFIq^D92-4!S+jFcL%mzscu4U__xUopND+2ZBjB+J0XOj zZCg@-VHw77o}LdBXA1i0ZgTGl!PoKdb(;gDE`oox;9G6u-!~!q2{VpA{B>Mtvr=+hM9e$7{UHZY9JWzDhc6YWRZKjb6fPi54_ zSpz=OX#OX`#}s^gqrg!LN-rJAsegquWw|T^O(ioP`!KVuy_A=u=VK;-t2+dqyD`sL z=U>3{b;i@*Uvp&0U&4+2EeBuA8%}=$t{M0@x~EJB-CLmF(t&9l#-Z^zQ*ai-nS(PI zr@sn3tpSg{033KF{uVtBECC#n7zgbw0OwITAq&q#XX9J|TmEs>@jL8~8q=46ooTF# zUwO6qJ44*rI8Ow3rIrL$vD#e;kEj9L7%TNR@?OFC~H1^j;d=7Yg4NSL)x(MS_rDhWNkdDssNi{ z8T1|E3U^D%yFkQCS8zn{zB~#ei8V>&wTjtig@&rGvglN2+e2 z+rXfQL)x*pZe~2Nf2^Pl!n}crBP!3wz|UI=;2!ThvY(qnyjVNq!Apq(ePeGT!Jka{ zlTzT%Ke~p)o(_V}CPHT`1-y$Jyw`)%C@sf>&-q|(fqD{uYE{=!e`^7cc;EqK7>}0) zyf+!VKNEProOz$78ae9kCHOxH{?``pO$ENm1^8ke3`hA-3m6tN8159fjsw?u1zewu z^%QW515Q|TlX3dD5T_eWoKpX882pWjv2;yuL-o}kg45vVR@-SDqwQ5Ehp(+ec-eeX z1b8JeUNjE*M-PB6l$WXO;Bg+lpbjOSy^`hR(>n(<3KS^Xe0)7wJW51n-5=Q zK0nvjz*qa+4b?^O64MdGs%s7L_V>f;1+`kO)Q{_@^#=lOnFhB50`D>AU8!qPmu=gr z4r^~J<61}8h4CXC*iI2R{|B7kfU>FIV)a1tMFx^ZQ{}HNr8t{!^0AkVRJ4ng!M>^eEC$u5m~zQZI;_1ZMgt2SHmThg z_xUUj8(4!o^}`{oZ5-z@7HPm*sX3uMZncwxds^`Yc^rF6!9S}-veAAo4m+IZvUX^f z<=X{I?*5BgnPpV!d#6cYVNWr~2e{SqOeW$~QJO|Ge zy-U2u1^sB~CowO&U;MlAeT`s$F<+ay%zvr-K8M{E|I4*^0h_3HI1|{^7r6DnZP5a5 zl`0w1o7L)i)*IUEkbm~`T=oyoAO9B{A9MIuM8Ni>!L~NawrwoMksS>#Ku^AgCg!T> zw_#r$d}hbYom3~jrYP)t8>?<@QQ7)^#AJQ!n>|uB)i0ynh@o|Dtvaep)n!;`WC-3L zpQI+MtJO5z1DRoVxt*sLsU?^@%tmWZyHnGy+nse6-Cg&^9p{01q@Jv=*4OBT*tL4G zzDuvbUhgaMPQXhTartkL6~PX|Wy~JJHL&-4L$48LAX{Rm+)KU7yvx1u*s*#lthr6U z;J~PBD1K~wT*~p2t~LwW^`PDNfDIm_FE}!|m8O5Ap6L*^7~WBDhJ2f&mm}7(uj+r{ z^BF;NSNIf@GuVeQa{2T zgc}{SPsuub=W|Eh{RJ%78Z74scy(aBMyb=_EZ0_yzboU1ju`IdR8{p6@4ZzYtg=4| zD+kV2bFiEBBDD^E)4$YtcT*VcM{t@>HuEgrmm=fJ0@fc-lL`*VV3bD-4Z0?q!YrU)D-nm9b-u;EI9 zqcw)3n*Xk>xldjk93Ui~WY zn+tw(SJN2n!a*Z$I%4WN=qFysFtYKrLQ28WIC_qe=8+}~{%Oay-M=XYF77ux63lc2`|i{;csgXijYSRnv)F!m_X=y}gmAQN*2|3J zF1<+b;dSFfRRP0fU|7+%DPUV1 z*uE$5($1j#6fmPWAYIX6*Fbv-nEB5XY}Y$+clcAxEYsbje~Vnasxyt2Xx-J?4!kx9 zaT~{R8&i)8{=Ufm#(NzFo>IV5KY@!RaFJlRXei*72fSVb9yZnp57HVT^CD|#8?aF7 zeZhWOYliNt>PQd6XqlsYxMybH4x1-)e zg6%Qbo)NHpM<~{8f9D00vrY^4w_*G9)i=OU8V~f>*XlQc^HhP$Y;cJTja*tXAl1o0n68RXSs}QBN0m zh=GUifnhM_sSG-X;jhm_w)@s8b&ugUrBL)7Piw~4Q-=k9o;UpbC1^O+_&r|0wIpzz z;lSh$fwM$#6=yh0?HxT=kpkS(7&m_nt3v_?w;BwJ3mCLw4ARuC0`_UZKK1VCe0vjd zuqQevJ_qb5Ck8X(Zsf$`A%ASVC&Ql@`$O&PM!nR5+gt(fQouVqWUJK)>9a-%1FMsk zjDb?;1PpI680I<3ei2;SaTt|r+uba1=`ol7xal5;UylnMq=SPZz&SWiX>EVjXtYiU zS6hD~a7_*9q!N6HDksrs`5pn=(!jR8fL{xP-xChsa!N$cC*5qYeHZuzqw-2T{7x}>rK#X^yzzOR!|soT7)^j(+QevO!MBCRw+7(O+DJKzG*8*< z8RqUIj*&qEw$1t5Gt?S^monhxb;14^D%tAztiPa{IAE8|*sVq+uGL)}y8l$*rVzM! zA9Y7*EneZS7G@p37h|a9ymxU;2>Xp z>)`RMpwV>T_zxn8VyG7|491tWfh_4O;QKsAbM*6-!~UxU?DK$n34{Gl4%_F#f3nNk zL%7f}3cimGqK@)Rk2uEU>P_!G>PR_ig9fn1rP>k8V)fHwX%+NdP(SQtD$B7b8x0i+X?s4XddgV&J=Foa|*k*l)%LQZmGFS_McA+95 zwhEfcfu4#OO?@R`+=wwAr8Wt6J{GXEVF+WZ%XIY<)@8&R^a933fN>My+BClQOZ|m_ z;g!I!Ij)V$VHxTS>OF>4j?Ums&11SFbP{#HDP7mYJtlhRqJ?gWC-B2KuwUlYMqPkp}&pW&Qc%nM*@u(0Az|8}|zO$c8ow8+{xS@Sh0$w+cRzF0U{? z{x0aNA?pjdSI|aLXrsHs_T2(Mg~88s!_NT6^=AaXCc&>?1Y9zKNtVH7KJX86Z@Hnq z+-?CE;(D0D0E$Soeq{>$W+;qZ-cri1GCHA*%e!7~y^=zT+#o_X=S3Vnyz zZ@}1(QAdFxUN7Z;rBA3QO8GMz7*2zwem{Wb2|wXk(=YTM?uV!LiSCa~26p=&-|PngUv;a-M;|8C~V2nI|ST{0XMbx!dwTHPit$EKUwf+>P7y1 z%APzd;7lBzF*sijdnwK+zUZKG>9-`ajF|oV{-&-EC(fUwA9f#hJ z>q>?L@`?_tqJ_B2J{|M0vrtAx$!MHzH8|Z9;+yIqzCGZOt4`oK=@Y2ygnmuXj)r#r zG}>8(vVuCcoKkGo67&*-R?>`K9&%jYS8fwg7OyQ316-L?XdkrhtHz~E~bKuqu?TlvE`8R z?WZU+s5`=ubXO7#m*PCpKk-9{P2UT6HUgfV1UxGN&;A0Q>A>?UgXd`>)>B~jX%p+C z9s2pgVfPFHuW7*RN=KQC1di)4n-kO@0v?Tl$9J$ND%aP+dJ9MOK0VD*?_dGTI>2(G zfMpu6oNTZ>De#;Jj!Q7l*=n(X$#h_nu~yd5^$xp_z-Q{~*yr&`d!)An_?&_>9cK>C zk~quZtbvpEYgr}WT$^!Dj{8+8Hy+m~c&k_t>G8ceBglL6nNVzQdy=5g2_#rr}$Q}L)Ct4hO$VE!bGD1Rnm z99f*>DpTJkU|S#9HV^5F+5|dccrq;>ti9l@zqWo&z~?&P(;j7G7+7!=!R!MaRQ5zX zqjV!`NTjEKbSHk6wX5!i=UF%74gK0)1AabLsj>q1S-_{V;l8=ReKT&b_g1&$r_oWIvf34M{TZ&;kmvu>MWvQ3tHI9`XPab2H>G_h)1jOTyR*7dGu6I zATqBu+Atbnr?&ZE%cTZU`;J{|No{g!0< zrFBp)&C}WbHGN9$dJ$Y-9A_z<<#E=;N#$)9v@r+TsE@LOeF9r2UAIQSsUdKBL&#Oh z;B$iE^Ps?24Z~MUN8SAdEj5Rh+B>fMPVn`5_&O211%0)7EC$~C3cMEq=S2FMem z0k<20+ky~owj8={R>*gYDdn#RjeqoS^keHyM6{gbJiS>Lx;OfHk9foNd`J1`1ZH4wP0%3S8FtS_VcF!A7WjN!5z@QTtf zjng;&7Y*O-@Tq~|(|q{!xPVt#;8oJ#l~pQw?xGpEs3mY5XE=T)gqhV4#pPS@DXRaN z01f?C(AHxuc>%jPU^m)e_o|?Y*3iV|0ykBdn^Edv2X-9<>=ppKrybX=clcdNz_U5< zT#YiLa%&p*HxB441Yat%FMU)C0V@w!OFyaE7*nuf49aD9eB?b|6X4;#{YN5yJVwOK6X8xiMQuw>IO9r zPnA8Q9#zj{wd|O#f_LaH*FEv}9Ie?p2z{Yz@kZA5dM>{|cZkjy>CB3iZ?w(F75gx^o4 zd*gfcd;!Zvz;d{NWh-EL$l>n_fxn8(-*lA$pQCayW~cRuf?TZC9}XXXcGSN>;4&UO zCNP)7QfC1p+XuHep_x!4IA)YuA>gtYxFj}9N#w{jxinfkoLr+WmS zJoppGK24oaErcu0Jy8FwgxaEiWn7oyiNr0sG#+c9Tw~iu{L05A(A8~%c9Nl;;f&4p zg7-e25-?f^Qs5{H> z^8kDd>d)eo1Rt+3K7NAOC<+^*a`G`e5Ag!{i297)DsE$V@KL=mSBksB^u7uAlea)@ zn*{up0>4tIo7zU(4iE;!WfuG|${1kZ8Ucg0z@UbJL8iffUC4f`g+-|2*^maPN9NCg zZP&w0{-Esf1$^ivXf6+$JIfKdW8G}LHlC*F^uHGQ5Fcxnfp-gEfY#9`Z| z@QXA|`XHO>Z@RyVcfj$?%WGIe;OiIWt}1?0s*S^rZv~8Q z0Y*JwW3YcmHj<9wU|YPg?{xv2j=*M*!}e{!KdS6_;Ey-{`HhU<1#OgLZ6wEaariaB z;0ts4uyc?ZdWpVO-=`nIn)DUm%ost}9ii)QVSkj3m2q#d8vI)f-lhp06a(@$mFn3wa(>ST0fC;Ud z|B1lWrSRpKfUg7?*@sJ1S@!dB{(U}tjjETPDZ9c^?*xavheNsCYB&x$7;JQ~3(us@ z5qP=`JjL~kZV#6Mt6N}m6o+wO7Vwl+w}jFP&jq1=t;o1z%sZ|K4I%2`B01VMo~|1wJ(R7-;yo$Km@MA^&Wwd+_fk zqY-Manh6-)35?bYoOK0fodmv1o2>g>$Tu4=ls6v{>^_dq8Vb0T2VxZrZZA8^E-i3! zIXHRBQSSyvnJ+r7+ZBp^tKArI-W)2^ewKkXb3Vs)q-%Q+KXlOg99wWty&3oq#<+b> zGG6V_1Hiu>m+nB{^1|EYN?q!R@xA}0&Q~0M_7-$<7j*I_{0wTH{G@!8iU>|KbCvu4 zOWmUdJ(XfTO;Mi;TI>cb))hGK0?zlN-r)G&_Ros|<-;5Y@jS!w2DJC4!~ag;!@lQF zTBbgZ-#^=-`F#5_I$zi?y&t2fuBThlVteGS})}yijA0> z2Y#abD#hH)QmX`hOPIDl8CyqDU)D#%s!1vAS*2zO*e0ltbsxs|W2mu%&Vh~5>w{L{ zp3e2Sudx)lhT81kLReBZA|8?%%hP(fz{4u=P#pC{aa|Z%Iuy{-bOG}o!2GO(t2+cN zOB$UQ7qG&^+oMrP6ReR8p;i}ZOH>(y}&As3p3$wP@k6X6z2P+ZiG65V=4Na4r-%| zJN&94aDjPy)f8n$wRzbn^9R<+7G2I!cJYwSHdiE>cz;abIu2a-Du@fbuO)DHKRB!E zsQ-R}vtHnAo4{Fd=4_OD1)N01NO7>ylR2R=tX8cA{00KQiXn~K+(4Yyh223uEjF2m z?@Z{sAwCacPoEQ`8Th=A`A#1P=bs3eJpjyJ5^(7aTpBrS9)kL!Fv)@*4i@B%eAQFH zWDqcU1Dpr_vRe1Rc~8r^5D&$GeGkT7tIHj{9}&1M#@tR<9YQ|ZSfSj}(^2l50&abP z+X%tu!SMO8z{P{$qKm_p0fI(i%s<{=4_zD7*KCJ2z82yRcQSPk%KG1XG z*O18Z`<}YqQO7j`9z%e~Q6aW+!Eblw_g?(jTiuBAZT)1!h4BNo*?OdaPb%YcuSzN$ zJ!g{3I#6o2z-M3Z`H#TaL*Q(&+b%U(uq(yzHrn87`SP))xjgDa^;7c&?1tjA+fa8@ z3}zw*zvmdFHR+VPUa%qA*pS&jy1();{F^S=r%m4eMZm2ea7$iw@tlB-@1o$gn~B{O zf`7x{pZB%Y5B)@Uhc;L0)`Wd4(3wY-P?`n=ZS3CTuBg8;FG|<(=0KLb) zJ%qI#6Q%>}?+n(I3tED^pzjv5#n8Zu4&S>996bh(HaYCSBvhZpda-(3)f70Q6*RXC zSdIpkorS!T1O0U|d1a%+mv(z9zfZv5bHFyb z4POOBV`>C2jmmd%!1QvH@4j;Q)?dJ6FfchK_&pYW$8C_uOUE3(&J1Zg7)$>WxGl`w zPE#qUGdNFYpTnUs+Bd8-x*Oo{m2t@7^HKrtCxQ1L4%=^V*giqPaR_jnDd=8Z2>fR;|D#nSQ#aLVe@N3QsxRiz`{GBb z>ZWe`Ja}ZGe^~~&kv8pTWEXua1^4c0x0j~{?9+gKCxiVu2R2&;?4JSll^mSC<|y-N z0k127*QEj;NkAgm;L%6mJO!M8&75D4->^2Y!@d>*rrE&sosjL8>&5C}y+yz?20Sk{ zczz*Z_AD^_LC`}c>!G%40h@z4j_M{oq+@M^>Nrne=gCom|H<(BEB3z@`#(^?G66Uy z8Z2KExEKyDK6BvETfna{@Y^Y1lnRVG8jQMz{3QL7pULpC8vA)r9}#eT4mi#bFi3*u zUlw5SA9c{-!+C*=Ld?Z~Mez)c)@i8tK* z4BMl$5JSJJJ@BGA-E#s)&jX_?1&(sS(IJ7O6mZnRaI{3gI1w1{Ex=f*iSRd=gZwY9z2)62jQ#G0J=K_eGBvQ{Z`9_Mh_Jy()7+bi4inFesf9J%5v7IKJIc z|2YApWMFiO!RUSgqXb~IhcTkI6Z?Y#qo9_opC|CyY{zE{1P+UU!@FU7uwP=w;(hTH z%eN@^PXYH)z`dA&Z8ETJQ-G~j&kO#=!>irwAKgm=*8&GGf`hDm(f#uz;Mm^ac$))* zr{E{uW<253D90k)}B@$I{fVw!2$UjV-CitQUbT5;n@a36S>gDbA~$`n>LTd)NlGX zX#5@c6qV<+8iXI*Hy2{X!*i)$aI8$lZ+J&as3+Cb(?ig~%g{j`!M8E+t+R<~!h#O_ zd?`lZdWf!xU8E*@)6mFn66%lV`unL*;U{i?&~v|Bj{QTc!T9wiAh8hVBAknHF2T7J z=gl~8!FemrWjJrcc{|R3>9G-f;REzVO&IK595hov5x-Q`Ie4F=x5DORZ3MtCiRtlin_R40A|NV4vEj)YDi; z`dRfH-ZxsKHmFtDyDNd8q|erckyDE6lGy307Uz}5CbP8Ct#x~>gy64(Fi;QIH|d-8 z9eSm{Uq8&dZa$-*PDh2$ycGv;vjwcy0IRPZSoIOG z8V9Uy7W_$Ke`>3wSEM%OHN8T(Hj%GQjyvM8X{=z=MA(#tI-_#GMrQn+bL4A!9=Hl> z!?tzQHr+1Zv=%s>aM*MmxQm)6hy!=6nLBzu5YL*zZ)+dLfNj4M__spP^(;@d^yiqY zX6bu$!H<)0xK&91geU1^8??Wj z^{-U@kTz@%-^*Ctt{)S$jGg9mOM~|w)b+B!SygaW)?s5yq5eIl{v86Q9%GuU9~Q8= z3fR0WV7d{QR(IIcBY+#=;`@%IpN|={Me23@Ph;o+ujil@^83H^J^#qUr#U!_;4F$W z7iTe?#c}50EP=Bm&Qdr_<1C{u4Pih$;RCgsF~Ak<|^{9PMA zbkH8CO;L6$lrdV3#n{V#YdZrYuw1;S-COrD?`jXwgOTOt>Kn{nr}U=woo2sN>L<|~ z+s|U(Q`#f(O+CUJ<+XKi|AClacI)Ph^=^G!z|zm)qtpcW6gA$7;XT*aaS!PS0n5$s zccg&j=Zxht-PvLLK7s%0;C~8i3M!TMK=*N`KI|g&MGIqGTbcKgZNgZU^Ln8lnU93= zfxzDs@V6UySzrD9;`afY8LX~^KT)~(Goy`Vc(Z=yf5EGd!)M&a;eMm7n_`2W+D}&r zY5>~4BIs!+;O&l8h^W&7?ymv&xv(MFmfCyMMZnlc1#$mJY#RKr-y{6&{^*~=pTRg6 z;r+T>^fG;g!{(FV$ztNix}TTh@$PK`&Lt@Kuz+h*gX=c}C)a?J@u)j0mWy*N|Dbb# zS!dMYXBmvB@I(2gJ+7ryag$V8MXM)N!v3UHurp~jRbACkwF*W)jWBXIl; z_bKj#or*i-Im2Ff3aT&0r-K#VA;rjkgc`}~E{s70k5l8-1m4~BD(q!E1>+@uKjWEL z6^-7AqWw<&bs6Sk$GMyE?C>J!b_r~|`J$24J(z`CsqRzvs|VDB>LJHC>m`h|R;fA| zkB-#&dbA#+$70{~33{TQgx!&^!v4rputV}xO{)UW-ZVu&Zl`=4T zzHbXOvoI8^K9|@lkF{M$%|sg!<^eFP*Rij0HUM8CXf6iLeHhT(pF;k}8>YGm>w(th z-lIz!Ua1Z`Zov;77dR~+!D{USUjJWM_1DuF6JT{R#=!r7c)d#zqyH~_UKV0>JUG2F zg4G$|bhf_EtmAQ`o`?1Pn2Fu zFp2+vkPu=N%p=aj*{!QTN)Bb56w{I)sgcI17+%fq}6=@a{>V1J!F z^aV;{FR0SUKay7K70kc*8K{HVDZMkEtLa*hgKP#G&N(QoR(cCl(yPrv7jlrFfo@Z` zV|Hf+tJTjx7w4Yv49{luhI&iAk2%q8YA4T%?pAv+Gx{}h(l_cm^@Euq{Yf2Dzo?Vy zlsc{c#H8M}F>>NM`CWjw>wL-)F< zPc;y0;0#8b(esPL^$5%U7SBD(q@@hqn;SW+I+nL^Hu)-^GoznjJa;Xo9DNI z_kWrG+5gAbc>q{dTyK2lZUJ_e-uu!8R0I*RAu1N^T@(=P6e}q9-o=KoVeegI*VqwD zj5YR_SQBH(KN_QnE&hMslzo@yoonv%xy$apduP7$oik_7oH=v8y?|KOVs@inY%j5w z+RK8sc9lEXT4&YY!Kr5cwEBDPeVlQ|8FSPG@Mjo5iZ{2!K2A==llCe5w0+KMkCZ>5 zKs&zPaJBuv0?wMOK5LaVX6>_%S*NT^)-~&vb-?UA(UABF;11ECtnC+BJ&8BBN6Oo&VH?;>9g7(U0WwWz6L`e6}_R03m z_RIFq<`IoMkciy^XPw?NUzx+RBeEm2YpV1-`qnQwJwMrYM$e-il5WMhK^OCu9p}ad zU&Y~e#+tm(q5JSCB54oXXWUrxue?XPUVw&2 z-|M`Dedy=x!}eyml4swUz~DuP!5Owo!Y(buE)52s*f#Yi_PZ1Ktm5!_%TA{KAJbls z2>UnSn6Nnz&vFC4lYMH_5_9~el^Ps8rRXPVM3t2q3pNu>-b#5tX=Ch^_mt!@Ry?I@ zE46oGL|xWGMIULQISc!APKZDM?_Sa^oCAEDxgB5WE^{}L>3hw6=6>@#^MHBKJcO_E zhUGyID>jq*pnw3}DiEY0_1vzmhcwzuhK)=c<_t(lXwakHl3(}yH%&w1FMKVozJ z>IOIFifbK>=2$by%|#Aoru~$ifw6fyVdGv1Hf}2I?an=-O&-ijl-m0bXJhM`osD4* zra9Lx#7p+TzmYGldUw%UoMf&Dt5Mf;vYMX{&1MFq`OhIPPJBlKx7J|R+u`<#c_WM? z>W{9<{^QuEhwZir**u199#71Pb~2rbKv1hEVXr%4ug3;^{inoO)`79?_hBp>7v$nw zS;0;JexnL&VJa-2cUbe4HWrP8YTw?-LF?cCGnP@IA&jGsQ}?%#O#NM^93@NKTg zKdH$cPvBYuro9}l#%!P9T0J~#<+#?c>qk}X)_A3MKj+$glAW8teF?a4Sc1okiLtq_ zYujJ#JK&Mmr#ObrWe&3)a?=XtH^?OKhs5|vdt~yolZlU;9tD1iLq+_w?yIr3wRxO6 zy%E1*AUF{WFL3ks3+)XK$DI<~=m0lX4%~Q^z7)w$bC5R^{p{-c`FHzu6~2)d&jw!X zpO8^4@~Lw&+H=p(z$)^CZedTFoQ$>wCuNUlt+zX?z0rT~X9w6RC9?Urq(5hYWsiWR zo;8%0_|SpCA}CU(Jl6bqb9IEE<79cClOzwPY~`0kzXh5O_A4tG^8 zSH;TD#W&p_;T)gf)#LDLmy)*ci@fr(i2jv!@3g(I;xh{8$f&jI?k5yOQUgpODE?WYTJ_N}UotTWb{R_tpWI3IpXy4G2+R&Q&jY-u#k& z-VpjJIqHgIX`e8bTC%(bCgj}~c|RHC-6J8dXM()Wt->lk?-ZVS1nsMiwW&4hszF?} z$E5AT!YUrbIaE9B%ILrY)lajSs)r3w3yVLNJ^{m4uc?acQ> zd{93!^D}#@&=x#bYSF6`?b>iceT|p?PM6}JkJv`jw?t+SC1mj=vUmoJitJ7uT6bp& zhlU|T`D>9^IyX?*Z`qMN^-m9@f3l>Q)Qvsx*P>x4&B!M$ju(C1$+8~-ZH#ncL+R+RjVIL5d4 zu38^FsAYvPt|$5R!NaO_DRSirr%OG|-x9X25nH#SvvtoqTen|=Gn+WhNIQ(#E5Xa< z9WS>tA0)Uv92t$^t*aX{oZ`QA|G%ERR9%u^UV4>SW{hL5V;O__|knUTXaTJk!TV zMw?a16FAPz?ibl>Sm}A()?!6&HVu~)uR+NEoCv1T@6mnZn(K~EM|y(jFsCQ~Oz6Qd zKC_%Y$aNn5&S#u=X8(e&;(pg;wX7i-b6DnAWsErr?DIHCJ{pT_z(*L5dE)<4)xtMedt4;*M8NQtbOtkhLVRN-|lL# zku2jSUt5>Ypz)_|Ttwzp=l6>F)&{;UANV$&_Im00yY<75(}mZ->c#V{PrScAB5amgth~^j$r2Ysb4M?`9`M>STa#(LScB?5FUo zQ|8qo`x}=jr#0BiT&CQJ68-C(ko_RqFqpTlNmbm5c1wJ^i#DnK@rv>A=#!l2T68W_ zKisaK;Q6!g{N^f5g}dy__Fx)gY+4(?618q0Y78W4i=Q^12c^Bt1CId1s65sDuu*Mh8X)9ayWRFZbu{g4cJi z{g#~6je_mmB!TyH;5{NC_W@uqFbH#607`SWSj^Ydek)^5FbbH1ID(6f5vH9Y9qHwn(v!kLEyXI?33%Lm9t zHc;);OLksuGu1?1W*{*tfDhsdXL z^*++Jib?+lL%C6V` z!<$QR8dB9y;RN)w?^|N6-`cgSaLkWo4}n1uMm5eSm~Ng)aN=b+@j4jz$_^fg9&ROF zIRt-0ID-N;c_l>=p_k0<|pBQh|3=K1z6^K!P^ui>-D<(r?$nXBi! zS&#PdT^BOKl^gk#ttWH4KY7~&$=F_wTWFpQW=XyU^ zlj7wPSv^(4jUU0JXs%vIpYLMz{Uq8t2YD6!jgPx{DIHv*Gw+nN?Jp(r+B(6bA&y7i z*sl}3eidHVbSKx+N!R~xXR-)&m8}UVuX$a!BCw?!A2_qlE3}=c0}`c zU0#-}ng1)1$Hygde6fUM4ag$TpNbE<9QZDvRa=@jOFqAULhm#5ex0EA|1N3wo+Wtv zojygoD!UTL>LFpA>qOg&$5ibp91?aBjwSbfPj&)czz)94U3HY}T}_n5#_zkc<`{4< zju$k$waXYUsNc!YOT5K&qH+Iw4NiYhf>C$cIlc1d_$zm~e~Isn>=@M!B&~(T{g2xH zsC<5Leliu`x5O_H+h@S_z37SGP2~5!$@*1Jm_9D-Vmy^xyGyHTz&u3W=8LXY(qif) zbzomlGm&7eV65P*<2vlo*${2m*luDs%k6fa!oD1>Rca@rcD?x(vZyWEha-(ShFOB_ zr*_>a(kmHl^QNa~FGRd_GELFU?bAlmC+)-{0DfxqUHySIlxQGf+EW#*z=N zoiO=q@X8LDt%Ak%dtPRT9WJT{#4dQS*IIkLHlv1)t~w(4UGcp}gYjlyye+YNp*t5Z z^I+C_?_y8F1MEl84ut2}Z}6(yVelvR6{yP8=jE{=Dridf75Rn(jF3QNX>&q}!5zvTHBC441|4ZJV-N{eV?QTr7uGVd4b`{1DO zZNa6;|85s{o*q_WOL``B_jPo4Ir>zDn|$+a@GaILFLM?n#BKI2bADC(qi;ObtY|wW z+SfL;@05hCwAjjt!B&0^_CTo`nGvyWX#J31^%u>{}khj&mMz~mt5~~ zu~6khX!pCU{|e++DGy>@G9cvd$o|J{70rhCar@b2`{n0&FXt-k{Y~UodmCNxXS{vZ zQ-2EvaXjU*cqo2dPrm&T%&SKxWY-X6cTW|D74DyCdnDvKhUYwi?DCwB35--*yZUePa(^QGt1~0b8jLg(-5y)N!*)75#aXhbld;tO3d=_P+1(33KxMN=2F|KNccC5$!>gVBUYrp!XPVa7Fuczt{KGd?W^GSAeYA!>x_Kf$#}e* z6W7pQ%$@Pr!n0NW!l{g|XSv*k3&~x$6i=?QYy8fzmon}Xuii=dDmwVK zeaF5_E@veV% zXC@?UhPwkCRUA9oapgbBMm+0mhW4kdi}jw3_npt-I~OKx#9kqNj(u_^?Vp*@w|b{< zyUhD0r?cG3>5}ewlhfCeu?og~Mj!L@!2G(cW;KfCEhcZ}X!;VzB=tx8prrrG3EJEB zN7ZbGx{=)XYp0g+J72+MKYDwN8@-jm!ThrMv%{uzp_etx!F%6JdvHLcHR}T|lU3AcRTnjy7vVX}*+a#7l+AYucJWGDwMp2Qe@H@}t8(ib z!BhD<&+5k1Jk(Qrx$1 z75Cy8Jk^|RFHG>ZCh+z~`jOWy^#fY)UXw9Sec^nUs{6;jYVP|mvLMUAUHbV*=DCu^ z2i!rjXe2HvD?F@aUK(qS#7ZN+BAaY*tjD%&l@ZCR!uv`kBw6AEodz7TOa#+cV)J#_ z;u`O=yDNMhBt`VEbu&8I=UZGwk$b2c@(j`azngzh+vq!IvHd!bqIB{P6{(Yt*o~S- zJwr{SW}>`c-g%5Z#Q8ken7 zq$&rBjA}NfDzGs+s+vST5B7XlkLco-NU5*Vv1}zGRq2SHwSvF9Ys44T+IsDEAUzMnDOHCp_Vhv@5Yov?6 zswPLJB8O@bc@NZ&WE+J_M4K_9Z6E3n&2T#wRCCCWY`)r%Unj}TU3Pi+n9n(>>;aG@ zv`a{ltaGs{SCZMZMs^)Kp!i&zA9}Wm;|uxr3|M<6-=EEUk?C5ZXLsjxMfcFSDz00! zWGwIb{G&g%GWtZ)2mfOI*x5;98R*LZ_Zk12ad;!rw~mYeuk+W@epNCXe7!r(r{t+9 zIw3FQps=&(s8D%O{)h4zE?|wK60h+5uGDc~j5q8@0=0T&aLoIwasD@z+Vy9E;CbY# zYOmE*30qc!E$fRd(;1JlWfKy%te$cD`W9REZR2?f+ddu~`j_B8{(o%mBf&OvJ9kC@ z$IEDpU5on^t^3IC>|wMz?dyT%$1CYv%)9iX)fM(7WGNm-DGNU@OZGlFpGfnOoy5rsty@l z7ksI==~v!gnPeupCX1Vz=f@bM_on5o$X6-$ooB&UpMT>$XTQ3dpQD|C@2t5?wT;$T z99PJwG05EcN5zE4Gj0 z`YZMou4-PMjS@}V_W zjseZx9BvETilbJX55WJh=FWuftj0aIqwo232<2t#k{{q#)#P(&270CaQ38Rj5$CEgG-?Uvqwc z^rqdEu`dsAS*1FL$j+>d;Gr^~h@M%@YCq?6o`I`0=-|r0c>U2i*(;3l?c_)F(8vO2v zDp^azh2;__>r?D!33=9-*KDsK&)4`smmbS9}55T_kgXJD*1}rAwKg+}cv}a4`{V#jpK&JcFrsdLi*-dHqYMy%&A?&p7M;TXx>8 zluZh!>tytCF`US^FUm#v!)KQCYasn99v9Cb-%qxHuYiA65L)TaQpm{zTBSR z&!d4q%?Tb_c-W(bhh5Dh3H#Xq&Q}Hdxmtop_3-G*k~VG$UPbny6-aICbmwwV%gp#H z9ggF$MIMbUI{aGAbv!F?E4&o6hU@b-2P9-S0U6zwzwOb@GXaxr2 z!Mcc7*1T@JJFH)~&mu2j5wFTTWq^(AjRP~=^kzaH;{BCD9=n5U5&!kGO%qtQV_w+c zIIfDyjQ0utZ0q=QnjM$Ga&12QNCK;xAcyBl^mEq)j;+CQ6L6G#bxF2yEa(<6e6oaV zuky?y88k8WuIyyo%gzV49mBo#j2n2#b`2yVta*WQ(3*|xxmGTgjlB7J!YV}F6?6Ah z`17?H6yx;y34X1xDB7d?`##zk{iA$64OMDTt?TMp#C6z=JIU`@>?*a6a%#zJ|u0Waq?+?90|~s8oqo^@h9MI^C1hY*0<+9#oG^XORKAx2suqz%td8ze@194LrUg@OVaot8KxdO$%4Y znor=WWE-zI=bUN|V|RHI`E>Zk3BFF`Lq{U_;=F1}Tau2EYe{)L^7UD^eiw$X7OXQB zqD)oi(@QeHdUC3h-F2qf4sda_;mmLqMcas4f4guFg5*<)VC(#YO0E1Ob8{QbY$Tmt zt~OvZt0$_BJq9UEba<;ips!mkE&1Yqz~;3Qy*V(Uk8RM$uc~ZBw71(h8^I}oC3a{= zLYKP+UEVt(`zB=Hx<&Tn^OZc~{gQs2#pk|4_A##ZIN9sU>9^)XTg$r@?*`s&d3WI5 z%zFs$dh-!qzqZ49Z)AS3KK`pb$$X}Ow-GT{HL@qxpjPIaxN>e;u$W(soaA2c(JPT{QCJFXMpCiwO_QVmyWLa2=ZMmnF^|;U!14#R%x|q zWhwl_)tHu#_Y<`@Y)ggxRJp=ncgIv;plfdY-_i3H8*3WY1P+{6?yYD_Vcz=t0l`?&q|ejA6N7B^-m8K_Tw2y zel@7NzW&whr0`k5w-?sfua0j&LFSTbP-{K)Wnz=Qw*K3>{0SfJRuyU=>;Brsx>bkz zzYLx++TG|P-KxUxW8K|as_;ji-o+l?Ku8vg;=yk$m^O4i0-(ewP+?tKI4_P_D3{%!w_{a;jKCz>88UVF8S zMVE)M=rHV2(HL{OJAvW@Fh0YaoUoM**vhXH{zoJJ$C%)M)c#air@|BwZSt}At(`qp ze2e|b3A@;cCbtT{u>7}~jCX2poSW&Z<|4z%b{UN26M6?>gY*7(oEuy7yzPP=*81J^ z_I3Lu_~!ktXy>~*Y_~U;fUV+R@rrAr%AXp|XgP`esC~#;xC2f1=c$h8&P%AC{(_6D z`;#HwvNMVHT*bYLVuy__>z+QB(XS*clW{l3TEA!EW~}#jp{;78uGrR2fd^f{IN#?u zm*5;wYJw^brd6naG+vUBWh1gYG=c4MpgJgEds5Z-7TeL;jc>%B6P)X49<`S_&OK^t zj`zHdGSodJ!`AerUFgsG@FB`Z{f+TL#fTO$n{GdGSsBOvHo&{lfp=Qhz6;#*F$~Q= zgrF)wX^b1PcJH>dUM#F4Z5`A}pOWmtKxF)M|+ z=Wk2!9+%*A2jbzIJ3ebomG$$0b>;q^OO56~_&5LIgaTtOO7wjIy&lM0eP2I^v+fo7 z)B)aJ;;`O@T^rOos=`@VsjnR!&ad0wmGtSFL_7M^f@ar_$!1c{R;nGkzvR@OesAXb zeX@NcA%_nWa(J|)O=~A)G%CpGp+tW>g#PZ7(D(M>c5%@6EfcbAN1rxrktG_Kz^5@>irTLX)d27x#Sn?De zPPD5xZR+FNHOWj*$gM4X+$6~Dtwh^f)Arh+^NLcuRE4?cwm6{mT4VN0aJ3znUzFqO zbYvU*m*;eAY~+f;Mh@eCKZ1jwBlst2?jX($G)wJt?&rrl?LgblD2HMSGtZ@V-)wH8 z&rS4s45OWBh1_ad>^3?3J(rD{b|sp>@N8hGS67oYF3ILcm_(7`6Sa-rkOY&Twfm5@tUWYR6j<&Xpp-_ejpa46E@ zTKJZ=@NJMW9~O_bZ391gl=kOHdu~Fm4ajw)AlG3DPIm>xhrw8DXCqry0K{Iu0^l=i zFKJl=IDl*b%Rb686FjMhC(Gw}a(s|AVsTg0|L@3cYM&io2GX~pIgFQ;@8+g3)!AL9 zS)%sJ=cBxpRlG`eeEK9xo$ID({U}a>Jd|BC%DKAG)zk1*Y_2B`bQ`OhwOJh%Zmk)R zz`YU7n;h=1m>Y7qsr`{F%4^j3qZ3?i3|!tG9F(acye0dQyyQK!GEXxXYh*q%1xy-S z@R$-1&sOYm-ZsRsqmFTMxRaamYlhjK6WDbHyA4a)_fiR;zDd}d*2wJK zU~e>TeU_8G@1LI+uOIL`DyN5Z9ji0S@#?BD*YduwUr`w2vSpX81?Dr!m67PrmD(Sf z#=R3-K~&zXcKKfG_UtHw&#wzw_U6Bu;B=?J>0J`CZ4+esP6d^;|CT$3?cdGi{yDRDe$7{!QvQoEBN)BU=wyL>F4+KGu~e~ML#{_ zbP%%5>yE|@>0V}DwXL0ecebw*uRS&)tM16^%aZ=hscKuqQugq2@QryhYFkfUvZbdu ze2Z;Wv{WaUyq%Jjy43t8!L?R!EnCO?IeLz+BEEcA(vNP)smM>N!v?MwWOZT!%Nnq3 z)q>@Vc2Gj5^~m&`AX9t7vg2Q#Gv^|Ud|Nzklxw+=_3VYbh*!|xBDvOL6PF8fhu(mF zbcuW#s@f1`r!n|`WT$-3_%n?)lHDJi?Bs)L?kK-s`@H;Uvm4eaKX>;TusZi{Dj#JI zns^?j)105MLG{4~U7f&W3}~zzFu5hckJj*GB=RlB>tt%QX?;NJF#l-m2d+hV$g1bq zol(Z0o}(Qy<89ZH{;UoMik?>o2NpRF%yRSNQ%mI2gZt;lO>soJyS(|yQVAl{&X#jZ zKL0c_TrVfX!`Zzk87_}3!Sw1&5UJ^zaDuba@58uVkGoSuv* zY0Gsbeb_po7q#fc*}JwU zx}AS(eD}=vwPsX*HSXeDcGqfU7f9T*r+JaQfm3OR&$3u{|A$sBZ(|my>Q~~o-zQBQ z68y68YiO|9Be5}?(}Db$9_M_rQ43jRdo97A2Kcii!GqesgS8TvQGvnSS0%To8}g?< z1l_1a`xqFp)4)ir#Qx-hCU(JRzlixx%o4ac@rnb;s?z3rII%w=F{FP||x zB=jUhMiX1~#F#%Oc)UE^St0Ovvji_PcrhgKLhBTJp*w^1@Ni8u6Ww=>)WtaxOzt3m z=P{SLldmrl&x!xDI~o43u7=weWa<2i{ruhWqx(^_Z52kdDPVSpJ(5#fRG;nly!PD){y zmfZVq2|Z{;559q~%{)K0WfeRp@%4(r`IgujPfDv~bXMkEeixV@^Wr#nA`$bGu>q$M z6Y+De>)Cg`dzg0}z{9M0sb&v&Qq=!?c(Qij$?r@0_MSd7c+hH@hJ7K~CoVZ5QO#HK#}Y$@q}GPHLIM zjjW0|`Ap|1gA>fuUqqaabh@$F{I!H9`z5fh1?Ry5>$)q7=Sf4sXjs7Mte}51nRffU z2DN`Jhxdt8{9@iey*OsrmcKe7fl+5L8W=FjJ}SoeFfhInISuD|v7XLqMC&v!XfW^C z_Pjgu?#{cxe9ynX=ilG+@9%9RnWU?^94keokEROePo|j+-@$_a9ozjSJL3I*`0r7C>$1N`kmtQJp0jee{qFQ{>@~Di zHBw?*n@zK8Ya80vj&~>C-FUa5eeHO6;@z#Kt!n4X1#L{XO?!O4XC3{A5*{oM|MD>! z?ZK4%{^a;qyxtaf+*Beo8`g++HNmJqf2+^wrP_+J@2&C2R&Y4Q`nrCmr}*M8 z{V-LksQ9{-_k@wL%@TXMX-&@%Sr~B@X_OHb=>UcMB|0d@}$4ZXMhVV7$*1;!H z4dks^qnS>wrLWPeu}+?dkL>#sS}T?4yv|pSn_g8rE?+tBTxv1+x^Y_n(oVlSsg?7)P!0JJ z(0rV1nkVr%Um)Z8Ra>8_PKP{9dFablf>SNzSc79dS4~Q}$FcsKs&4E{HZdng+N(?W zG!Z?Nor~vC@U6Sr5Mi?*UQb1>dQM)gXHRpT*@pY=!Fx~Md-0yddp7Smyyx;(<>GyK z@5_5X-uv@DfVbMAI?M<1mW5rAZIu{HZ_D|lJ{!i@KAnlg<=yO$tdC?hP8F`N!e6<8 z-RBkmM3pD6F>56BuNmDN5cKcy+*qg%$F=LJWbekkXhTZ$XiP$Xk3fHaTY~ppCBJ(U z-OI11NjG&hqI*lwv6yozy;OVk+Yabhf2U*7Cmm*2ENS;{;GWNg>A)RyAAanzi|X(e z#u`O0wRbbFmig=rS%sGqwt06Y@F+dHoS=^YO{Jrq&*8`#Ee?KXRmkskITEyw*{5W-PoiYWFLu94w#R_RL2Oa$EGNw7>T(EjnHVM7~-L>7RfUuGWkbI zd%rIE-QVCr9<#WX!r3r(b;pI{S@kz&*M!{qAhW(fZZDOz@82bD*d!s3-bkWPkjK$r zzAc#R67HAr(rEZI$SdyK8H?ag2#EEpCKEwW7Rlajf7{_k(XG>Hsm~?#wRyZ(rFBMM zY3y28e=64J@jJZlHNR`Ab@~LgPM*Z8xp8-X^ZUcc&>PGLvnvK%-bulQ`2fpNLL+G^~%G1Q-5 zVxJ&CLEf=;(rB;w*Z9Y(Lz*$_^x~A00YsI@W}6Y=m_hvbpe%E7jt>%JemiugZy57` zKz2nOX@@;q=zNLG`Hs0{LN|{^H{VFu46<{v89&&Qk?-UnTgmujUXt&+IG~cRUrUy+ zC;6(9c7FD(9gX>_WRkh+LdL4LaaB2TAo>&4G0qedRP%PRi!yy{YHcfOk9ogB64W^~ zYZ9lMoRifdPGgU8XDFQJe1*j>XXXl|cAb;h@2pM>kj$2qn9izHG)nRsjKs!JVPvgr z64h$F++?Q?XRNQbzrt)eX588z{(gxb&QIuZFEpcf(BtnDHi2`o>}oliFg>BSJ<-`- zL2u7QuGMXoTdpiI27+3qVnb?Nv z$lhamBg>*bTV%PaljW(l-MTgOzT#KJIa?Dv?j3mSS8F4aX$O7C;}qpA*@B)_4n16Z^RvWx)-iuKu<3Em$M?~hIJqbuC#7WmOXpJX$l z->kO#tA<|HXZIwzafi7qA@?rGylasAX!xi5$1CQSG?MMQ#177WTg9))&jHMl-gnr| z&Q=W^j`xW3jz;ur3Hgw9%zqw9wsQqD5LxHv0kVd&S&hiLmy>m0d{x;iat?V`elLe2 zJNnxuo^-0y7mbSRlga4wu`hI$XRLXO=Zkakif#0HK*ta}w&u13UY)?Iv%_mEb74Ye zosn0UAhT-|GCu*Ck5AY!YW3Q2j{7ILJcUQVJz}CZ$9;Y8In38)_BtMWv<)+2V)gV* zx)QIpSbJT*ZjX-%O=Ny~0=?AOE#5y&U-P;W`L&S0c%Azd)sXX8jRD>L@E;epWQ$&c z|LO00o>>)H7TM`G$g*dPEZ=Q>F2Sqr@aoTjSC7-i{CKH0s_n~}5Ad}>(X z&L1_mJjHQAu`NX;me_ZL{G&~fZ?Aj3?&c^SlUozA?TB1EIoW<~Hbs6#xJ^ZV4<_V( z3UYtC3h&qs<@&r(^1HWlIPs^n+PC{p4x^1-q+)k)7@}W>YaO$W`%Yu@Sn3rh&MXVJ znX6(umASyaM4a<0+ZUrPmm`()cCclwD6si*s6FF=c^6yrbi2JM%oR7J&gFpuRTpIdDq7L6FK*x z_wnbk4*1o%7>r+k(oUt_+?iv3R`4o+J4e<12$I~VDlboE@_Go82A>ns}KD?aZ*QxMpX@a}$;cbUtm;O@H z#%&Yh3_F3d2HNvN;{I*9ce|h)-_VC5eSIgizoz%Hwma^_>&Z&_7d%7uB}TA)by8nX z>qzj6YXrhZ=PStOw*kMC9DXx$veJrQoVBhRDvzziEN@OWKf%QeE{@G{@&8J2+B3n^ z*6_5u^H~2zJXehy zogE~W`!j}PE%smM`8@IlBG1oAaM>b@69bojtirD{wk$FWs_=_3mR8``&EeMt^d^~) zz%OrSeeZqkm(NOm*S-q3IA*-%aGRCYU%BkK7w;=OdwT)e)x5FT6s-gz7%mTrs# z%OW4S9xS^CER{L$y(gW9NSnbY+9LHqc2>N4GvG6qjJhr*7+sdYXiUK9rjqt%!_qPx zo@mz_xps{M|Dy4}2lnH0dg1+(6+qvb#R+Wcz@|&UM$zRwHh0qYBHyJo+&;l^`(iu^ zY7r*Jpe9p?uUnT51iw6wA_qHIRrStH_k_GpN8V$raERmm>kbD=hw}}2ew3jYs{6|x zw#HVoo7wF@8c7>sUnFNCFFCL0+OWt~Y}98afoVPuxHpjx#|P&Zd23u*P28DTonlK` zwK#>khd$dPt{AGuxHT^x-L*u{cjx${cBy~5rjplT(U=>NTT$O?ksEfOEZzQgDRNTI zLc9hL5!0plhE5k%h03}3I9lUe$i60>7J4%odp=LqM;-f66@N2giWd`ul7BdW(}s4; z$nONVyuNu0V7QMlSAu&HrVU_T+4WUpCRWKX>ey>ehN}`Qn3p}27>jDnLh5@cUs-2S zkdIAYdYen>)foPG#RT8ZfNyJ-^!FzE zo5v#B9?j^shc^)mwJX~3TJWFFzw7ua9&K8MMdb6VEm*L3wB$LDCiqYTA36m-j4I*V zy;ZuRwhF(F=t`&HGfoDBB7J>BvQe77py&lh+=MrLS zJ0|-4itDqaZ_H~6`PU%-m0IL~teus^SYfVm1 zTF2f0awSHDA{K-*uRdbR)Rm+u*YyyccTCCkdH>m>tz3Gv)$twn%Se zyEZD}>c6Xa7Hix!Vw2}HD>wG}#eh}o1V>-W>E*}t zBd_CePL`p29i8qezqG%dnc%@$@Ze|qR5WJS!OLSEFRwD{k0Sc}SM?{_n}x9D)SMou zpOUHcm>sfa8vpk8rpCNi+V9%zX!@-_#Vh8?tJ>AjY&g+ejj?}i{2?K0i>z04vOWR( z{AJ^b0mpd17+2E$elp$J$1pehx(dhGb_qh_i|9-AdDKVUkB?Jz7q-EDeSdhc8&T%^JAWOP z-I}mx@BE5ATbPjdxybwKlD4bmVz%1e>ZCGTi=URjx?RBf@;ugip)0 z91%cW!f_uWSr@w4lqGLzZ99=OFLjF1rc}}0mdY5)PE>}{QrneWBke0%vE{_cZCJzE zA)A@)lI;%v=VdFy-6C6617nYN+?9v5IjfaR?86Ctu;{~1NG$q4UBRb_E7*ywRcZLC2rC$;ovG%FRAo~R$A;AiBM_#!(hTS?tDyS(G)>GtLl z{kbB+i}T>cx?t4Tq3{2vTtr=3FYv1a)luJ0P0?7J!JZQHrCm!@WqfRXwYuQ;2WK;D+@_I%c9 zR)B9sW1{r>o{~NfPRI$X%KBIId46hqF(G%gWgDk&s(oH#CMWD*2A7U z(E6+{hs!kjTO^xC)*Kd_Rp{#-<|D8xYEvCvta$odiF|h9{zW(`4l+4`Q&Yg{yh|Tv8Il_uLmI0hZ5sScXN1lIEpo^q;1b8WONZSdV~H}kJ+;I?Xmh=ncX;nJ)>7f z8(aMFPfPnZKKqvb<*|!nh<^5dq7AKG8>X9`64*8dY-d;TDEf%hd$S_~k95Azg1`?w zTd!!7x|k>Jkxn0;v|lCrF_+c#3FcYkkhi7rSv8F7U6I9MNaKj?$n0nOD?1)9tsU!9 z9-u11To7XU!n(Fk#pdg8G)C*^FF0LR?%Hmw30%nfmNoAuWO*^N{Gy7xQI8jLR^N~G zE02Ht-30PV=O*xN2>8xSZ~*PjYH}R-y6X8+#)@0Cr;mA_>G|s8ZLW{joQ7=TxW|vt z<5uqXM>x4CFUYSCJ;{gOM+W(^#miEhJv{3W^r7{Pbbo&?Y3DxlRXD}At1Y_P(9drY z*k1znr_i4we5b;}cC;mLbE0lfU=?g1@INwvf17~+7X`X6?Ap1${-d!=NnZzmdpG_z z_D69lefIxaxaH3kQOtNJB5OWMuNw5e7PvpTH?Bm*6{y|FgIt}6voV((FZV-xaiK)z=sQ`fIko zz@4S)*F4TM-M}q!NSOJT^h9=g71Q*W;`sfgaB~=V6pf2ok<}h{Wt88A9E5W*a$YFzv<~vR1-Zw*@ng3hps&)MMfNhgHn`{4JajIYKL7U?4lCv= z25(8+Zx6Tfp?%Z~+*+z%Te>#bTb&Z8l_bjw-FXQvG{S`uC4C;3;?_~phtl#mVC%5Kg*J|UaS zk$YIM%CB%?i;`#0 zuHr$AEofxi+VMcXGWo^yQ8CPTDZZ*p^Tkcr*D{s);s860`LA{ic6Bq~4e0k82_Ccx zJlL+JZ7(KdSQlitdsUlMll0fxG%MT5ZkCY!70CYY$Ugcevb! zK8LnRzoPFIeaB(s3Eu@)6L_z|{Bd#upZb8$9}{wKhulBs?)kQAPN;czCv%n^>UeWv zI8oxu#yv{fIxpZD+ZyvEhSJtMLtDFKYoJ>jx(w7gZti&0 zNHo9N@``+@LPal5rdDpdVrk2VvpE$1 z(3#Y0WfNTn>J)d%hObaD2Y=y!%x9L$uQX;#g0FRfuLo7}EOKfgWAd5EDZfS|ebqgC zvEER}Tap^hNPdXhn+!10F$aNHMc>r!v#Rt(GSJUog!2>32HD(%3~Pf7FXLH7{-N~x=P5P078#PvF-X{EkcD*B0#BIs8ohqN=u4xYy3!RQ245t@=46!IwJta&Q4(F00~8 zTqAwK;kyahh})wx%uy#5$NjUwmqUXbqD-rOxW!fdiES1i?xa7O>&0sj+M`Q3xTCRb z+hVtlBaU#BS>2s&q)7ET>~UJ(Zs5*qIRM-IET^_;kL*kCTp)kC%9~azy~Z4y;7Cp2 z$l!#m+av2iLDo$t7tgP|AkX`0e{s&Q_89rq(2KC4*Ep+L%X;^CqPW-FO|zfOxd|>b z!hkqVTas-60r}5GyOd8oWBO9*z zZ~e@`+<2><=dt>m_F(#PbyqiUQ%+ADO;wx!SonIp(HN>d=WztRjx3tRM%D7V*sjzU zVmCbF=1@G%%9yEYLu)!TeDr^Fy9G7jrZIBv?&>zF3Y=e=QBA&uL|AUxqpMX5Bbz{hwgiRw$*3GLPDdE@M3B7HD&Neu`tufCh`uS|= z=f(+a+k)+YfbE~?Q)8~b@@Hk!dM@K~$xh>h`tt{e$!e@uQJ054%SuHVuZ-sDZ*B>>qTUZy zex2-Bth2do@N?zk&LYcBr&0cL-=?zEFAlqOW480n#IEF*&S8)A;XFvsB8#}Cooa{1 zUz!H`XW8Z;n-@xWx=BI?-I2lPU=VY%xh45{zHZAi4g>9=^ZTg$9x7kAacfpv74y<= zx&2tDJSe+4+cDV2h?DN6Yhz^edV-&=;AU&bPgDPKiJX2y21PvSf-dzAx^x`4ypZ5Q zTX?V^80P16(a&uJ!~PD#6FH~J+piH$op3EtklN1SF0-?#lHMI zj-%T$x7!WRNMi;$G1>m~y{K>9sAzY)>wg#fU5VU}N$|b_ne_|2Z=7ihYZpJ&(zn&< zS!D0zmi|@td#acFJ6SEDub(71)DaGyi>!*~S@RttQ<<|KYu0e5{!Zciqeb>+w<`WP zSd27BgGHV ze!X^J&T`}Z{@Dcyxwk>?eS_TVs@fFs(YSm~LZ^D8Q}cpOosr;XE7QZ?=D69zexKk% zC%Dj1_52DZJ7qmeuv{7WL^~w;#C6JxgM2i@kiV<->eCX~wh7n{2iu~1JrS_o3RGvI z#rvbholDw#J$UEsYm8O42Jb!&Z+Ugb+?~L+Gq~;_aJ@KT?`{p4wjEyVGj#^jp#irj zOW~ud4W4VClJ;K+PI)_{&ue^a1x~FUR>q72t0Mhd>adzjHpL!p5BNUL8gLE?a$25y z=w5nl!b|sTYyQkg-W8kImCwlY0NeecyMLKR-5qaXGc0o4=bcss8yLrQm9bYqjE3 z-X^OPJmi6&;i7=5&h#L&qKEAxb})W3XQn53%^q)ii<3cjJER25Cy;UE0^Oybb@ZP4 zJ5IiukI0@`b8W&tDWCX}1fN>Lr`|1m`m%A-%wqrRaVHaTmz?*iw(8lsr01(A_|gDh z?oIIU=72$Sf`?t;;cW@MV};Gxv{%3=j;Cr*tW(qpZ1#0HjWiR=m zKK}zhx2Ii^pOTMWl23z~VX0wE?3~JJU{vG_JsL0?@2WEksjV2@HMXn5yF6lJzDV$= z7XI`M{CTwmw_|epZ2b$o(yF6cZFy@&=Zjm)Z zzGovjb4`40pVd4nf4VYG=xh>U)gG+(c37`Kq;O-qb&D-HnRUGL-9BsOPAYHmaZV9e zyzm$>E%K!vahOugE};XG$5#m+ULX3pZ^>sb0?*ZA2l+aimoA+*)s^UzALq0mYdkHP z$l8wfpl^+*GQ-4bhpNc7z$ngiGIgWAGe5Ah;CE>G6Ha=tM=$!fzG96k6^;q}&!;$# z=LmbG)hT$#Qe#N@r5A(GmG-{e824z%F@1_vmmaLRtOm;_!m-VW2FCo-ot8Pxe3xw3 zY&Y_P6-%<_J=-QLo=exjy&i#kCnR{$7G4~h(2dsUMVp`-XI05j>_kMIJ*ahpJ+hw@ z-0T21uM6_J0-22CGvc?dU3uxdp+s908|-NYBA20VpY>>B4_bX2PwjlI8!Kl<6>wA^ zUpxK$THEYUeOWmj@=;XzS2Gqx2d^RubtmhmqlouSb{W??`(c_}AB$%<&$WAp8u_Z; zb}-S2BaoAi==t*=1`y91!W!5Lid0bs3j|Y))?2mAZe*ObNKPF)>=VhNZ-X3_WztI)BbSB?loZw;um^B96 z2A@(KA6VYB&RWBt7s0FO?5R;Lc24`kyzRQfOlJK-r*+ICJ}RuVFIG5hmFl6wCIR7V8x4R9&?{rwIX=GF-tdSAf5FE`r`Op5e% zDwr(7D|n*BpLsF#FLE_>fj%uVk0h|^7O?r4w&&vnF}{0Wkm>rwtG0GhlnnRJ<|324 zJ=Rzc4UxvhneU}^>ElAqa=HO@ig zMh+#WvLZIp=l*U-cZk`m77% zPdtleKsL~=Uk)MTN2gnj#uklrKF=ibxi)1DKzm{LB)>(NAAmKT?`A)D*h6yM7WV$7 z`VG=`s#FIX?wRCuQJlI5S#My*5-sja%E#`xX=YZUevf2>hO(Xz$&$cl}&@KS*$^RnWb!OWNB+Ut`QboQZ3Xce%dG+tnJz zL~QLA?4r{O%s$znv@620`>@kop8laO=j#Ml4{H&X!LE0@Vu>g!lBo55RS(r%EY<>>M4p^H z|Gn%!EwydtqbUcIOMf^sv|H`%L}VUgt>G^EbwY|pMb;(1QzUsRL<{V!5@5njIOuvt@mph%DSLGgBbyOT-cvkaC#TWPY z@o|b;vm0amZmgqHVLsu1tZmw4Z6L!n34YhZ@7)5wR{(cRCN_%0igK5<&tkQ7T64yJ zqzozOR0qRe0*j@~SOvDM0B5ZKTJkR}o~AWd^S*}nb-ZupcXz-_#r!rw>!$HOp7)8g ze-TlVYwU?t?T>TG39kJevmW57@h92}eHF(K0?jep;T--o2u$M`rRK*u%N-8WOQ1mdOa{6X1a_A1&P@y_$|V%mL)vpd9$5`C`?eSa8U7RjRyUhe96>3zLB z6LRZ>+&Vb9dEc=)p(~v@39B>j-Rab&0Y|#$uIK}{;hcw#kZK*U$jAI*Es)BF8?a*E^FEe@B7#GSTe9Fx6Z9{DTn{Sf};?Mbxp z?cra$;F~M}lcK*7$A>#yRxvZGbSRD)vd8b1_NSA5xwMUKvYkq}KR}ty98I-!j(VMC&?yWo>i=CV^`a`6LbzRpH<-> z<5QhnETXUd=vQz#Hi7X?0b|9#{Vaq$_Mkse{;^%2L5=2LztWb&gMR9BaeR{uCI|gw zLwg|?+rihifv;cCpS(SfZE3?ubA00d8;AR!O<#*@-QDQ=T8X->$5^z9%M@1Rr7|Yv zYrJbtwgCQhf2?@CwQynQoc;Y7Z1U}nwxJUM9G>7w6C7z9c=ByYzxPRSs2v_ zIG+*gf-uf4O4!YL0k@w^o_!gZM*AVTMj!D8hpDeu*gqlHw#c<9$o1?51`YJTF<`I~ zSVO^QJ2lO{TfiYdwuqPF_VodWlM*uOgp9^MR?MLRW~rU# zIJ6GmG0!h~e*c`Dg-Mjdc}~WDu6koaKQ;<-ejw3@>s%l9%vM2;`StR4!8;P_dogpI z!)(t-*&ema5kKFV*=OI9Hs4XjyNKCn=j+#)#R<$hgW2^7zO;rbZ5&^$StG%_Ht?=7 z@a~0R)1p4cul0D*t(k*OMmBLRUB8PtjLk@7s{2=OVo$?~Hl52(fSdVgFE=(1E9v(G z2{~*Suqo9jQ6!4gtk^>;`f263P@|b zS>NK!Jd?n4gMjB_aHYuh%D>(w!I9Q*q#4Ewy z+i^f>|DKN(`)K{ZbVrBjQu~L5KGdTNt(-n^a%@7TU6JXG1P*oJ&?e%LXy^W+onIp_ zod^=gWUbXHrl5?7*+|RFOy?nP&WVS+;$PLc47CnmF(5>ebo$_QP7r(`oEI3+3RILx z88{o3V7*NV)>@kpr#`QeWyJbwCre}APxNuW(8u`+S@cB~Q-UnM2M0xbBBxa|!H*3e zvG49Q{HFrj<|FtJ7Nlv>062GbhfIpKc0x1KehWf^7d{cXYXmNGp)Yn zEGzAH`KfV>5`?OTR}x&y$PwEs z`CN_JeeHn>F4n@u)`5#3Rq1r(oMQB?`!74si~U|0`u%_OtH>TGCh%3F&-;Wv&nW5h zk8mr$1{8C(uX5a)NQ}eJXnp4Rp4q7hF7<&+I|MHMC()k0Lwo*Sg2P@VZ9O0<10UJ$ z28Km*4~-M;%u@3&JkvwChhhrxihE#X!z*i{+9`;?e{FI6c5dj$ekE-^6+RTjv@CpR z<@n%ZRsSi$qW`b>EynDc5D^a)#G_n0-HGjo68?tcHJ#< zy%CI!+p$Qj3();j6-b?QGf3W%AAA ze6+KfZ|KBAosV{hy@%R(Pczy*!^+6p_Cu`ur}lGB^!}%_@;}*D?!4TlFyq(WM_(sV z`^`-%G}c}tuYe8~#oMoNI_NXXmbJg+p;YNkMK7f}o$Oj*rk>?pYUVWH){EJEBurUz3F8Bfu;WrJ6q9?{O;mGW*Dni zK96t}v__{5t?r`ZYv(cwH^sBr!s*fWa7x*QGtnlUQ@JOV`jxk({SSv3f7-RL;c*ff zp{Kh@oOXp?7;4bnLPSpI5#7zI_G|b_AE9;Mqjmq~^i!?m*R$WDwbQ_+a4w}hZNFQ& z8>3P_pR`|Y_wsV=Umk(aqYMC5{aDFmnfep4rNLTRUDIU+#GU)=*-38qeynk-Oabk5 z-qLPG#=y4dyK*6?*d3{R>d(*K-R>E3kmkFpAqR)5Ap-A{g3_5ouB$6CvyJR z{*2L~RJ*G`Sx0VND>AbDiQb*E&g44iT<`AW*(&Fuch<+P^Y(Z7X)*I*C~MQpu@XBX z*fQln_^K!4G9QQY=PyolYePQcQmev~IrB{Q9JGsnOR~4Nc5C*k;=5xu6Km*mb``PH zKE2s)^`H{9^O+QJSGX|)s&vxNx;Vd`!DqvS-5eS0W|xt_T!WMXb~i^-fs#GmB|7`W zW5sjiUf9y@oGqQmcf-wt*k;N_$13ue=Z&BZb%;=K7O`vaiTNbgA?|y4Z zzaFS+bHLhC!*S0AhFuhTs>KB>(;k;Jw^CYxYSm8ml+f$yY%@9HJ(zo(>)qY5}B zKd61c>{Z&dZBF(mpp$OviI{;JvDO2ko}?jbMo&>`t??Vt2@Be z7pmGF$0+f|x0?O4j}`kD-NApG;9sns;8g?oGzN@zp#MeO+m-&W8!&jP1cNP0`Zy!9e z|6(a#^qd6VyU@0Yp?xz-`gSmw#<`vDD{hE=mpDwvWAXQ31-N(iNmU>6`YT_JO_H>! za>Y48qw`IU=5(A*?9S+~s%))D-ktRL-I9KPo8a>l_`G{be`fQ$A|0x6F}Pz*E83|! zSG*KY))i~E^rgLH*&XlBIkGF8gA%e|J7D{9Ngw_JW<|DAYfjS>oaq)g^Gy}r6~6lT zz$wVBDAsYYliR9fbSTy_w`K3vkqMs6q#qMPUpjzIa@B{zFUr`qDNUNw5I4P&J~t1I?Rv6tEn(=wiyka6oERw#p}(m?mbFfv^JZV1;L;ia*QOF| zZmGg1a^*sYjWNHY{eIPkhjPU=93QP|PpvHF$sL^aPGZ!{j!5uLnZ(y5c(F5WUp=({ z5!#Tqw{h>RZ$n#rmJZCFyO_sATMnUG`B@3M)CakAE5T%G;6vno#AJcfYtDsE$gK-< z+cL;)cnKc2)8~k(Bpz+gYV^0pTyGcg^?Lh(y*45HTC*3iroEWoQTL~$9}iW@R?p%i z%y8vn9gsaoKZ|0j71^!?rjl+KbALik(`oDYAg5s^ZMiBTkGdd_R|DpbWc;t9gB{%W z>n1of4GxV5>%47)Hp9chMLsIp3bZuQuQ}2@LY%nJ~~Z>P$zwE9>%#Oelx^*@PUY z(bjRHy{nb<_3@H6z6*x=+{;+w^E`*)WO61Z;$v;)=Fxt4k#-!Y!l*U7CuGzK8Eqb9 z^hFh4weLmOvUWiB=#Xq`f-lqI%j*ewtrGgbZb|>2uj+r4>0ZRvmLz0U6J+#tlH-zp zjoGB?xzYY82mH?oxlN_bV?(XCo7q(28ElOj%aL?)X# znM}q*VqLqc-4PSzNxf5b|JWySdM2FBk8S##ILrPmyG!8grwLiD9QyoH0`n|jUfW!} zJ~toM$VZz>+hZHVCD~${#{b(8Y4TacAC~mtdoVA`^ZHE-=U2-n;-Mx|+bNV{iDo1yB{_4(Ts;Z`e0kcpEjl=Bee3~ea`lmv^UB?_ti^0s57%z7nsFP zN@IRZ@L+W4bL-uTYi!BmpMd3wQutqGPOzJ{3Qv5HE1e#N?By|YCLUd@14mmyo( z?s#=_J6`2?t?W+N+R&Y?rMUCXZeFCBt&gIvk^L*dskU(Hhy+HAOBq?>0iznz9=zii z9yuUA92Kyx|FoDRHSnX>aRkqaK8Q#0im{(YBL99iN6a)K*C{+_#qg|^nv3;dW{}rO z^r=XP>gms5WE69_)DFc4)Q_`sGRn&*O6PXko*(<84b{5qMF~5}DQ@Hd`>}m>+MHka zj5YczV`CTQ72V+OcI17WLkQz>^D$aBFoDMo{LRRK$4v>|%!D^h%NN^|cF4XLGArtn zc1GV%f1|vGQ}o#yVcSx>ml*?&(RS$fF;;Vy!_l8-qP^7XH5cP1+x`g8i1!euqMuDp zf!)N}t8*QfM<%#8BJ}^#guGix-X(q9l(wmldbTdfRNw6%cr-47{q{U}czE`t1P*5| zgTth3X+lQ3AfwT=u?TC);ZfphFD7N72uPz@JAuz~p^f(^_}PX%j61_m#a_oH+c29- zLS#rb7h^R&V0C9g2IMnkLlPY91P9-yZ$%uGUhT>=i+(5WtR31LW#?^@V)R(^-nr1exL68r!x{c+bl7*^@SVL95)n`CjT(e zpB+PgHc!Z-hm!~Mngl=a^0J*1{oI;94hj7{H1Yf?;rW{+WYGm#djU?uws5c3o<;j&AGoa>i)c2HaX&}Uvvy9% zzbo?pG=X6w7af@=J0c;cF36~Bkke_2XABI_=u}r6H{Kmt{g}X@2@I}pF1A(sg#I;dv@D0Bt?G@O zb_(Oi6;=I<{XZq}_*S05e~kAU@eMY3nEP%FG2GL5XXZ!d5m>>6ZREF!-_IaJDO_`xbqp%vb+DiZQs zPs&;ykp>SKkCcfoajsc(3kbVq&FDEYX$Zi z>C&2=s@!c1H+OegOh=kyot%fcIwIrA{+UiSfjMOR9D-M;?8c`!i9$8|-XIUBKUpsm z$!FP-8V566{>pCbdp-~beA3vQkYPJy_*`?b{AY)L-<{}BztEq@k&Wi8@#+#z*H9I$ zqhYE}&{i(fiDVr0Actp!n^&%a%(Uy&nvKDuDE8k-)OlBMIMU3grxWSbOmcEmMUPVy zcxL`HaeY?Lq-Iyh7gDy+a58{=t%lo6#@W}Ji!qoLFxZ+l7T3+#oweX?$jS02QR}n` zoi(A;sPCiIs>$L{p589oFu}b&;of5jjQa+RpR3}3^utba{MX7Tb!Zd(B)^Wm1oF|n zANF7g4(}&$*el?$BRCX|^YRy-N%Wyl=)<@K2iwEJUAb>wx8gjmE%)8P*@@lx?kW3e zqJ4XY_B~b7xA(xX=o#(JY*s%MLsAax?CiBfKYE9L%uL8}7IK`N=wk=^xP3xCosrKD zIr+StuwxytVI6}VdoR((UZIWOC2-m!;4~k+W%J`Dymd924rKZ*wTG6-`I`h6Pjndi zJ(^!6+SfC*?`ZDtBm2GumAr-3$h}nc`sq|wJC&Afian#wast+xgD-L_^-QJho1(vq--kTsu5fB9|sHj*# zL_m5kiXBm77h?f?i^kY{*VwQdqe(2$7}3OTjNKUYd(PY$_VRxBjQQ%_-@Ci;?tAB) z=YOWpol$(LcfKr9bgeb z^I@^Vw+6l!!Y`d~d>vi{zj2n|1?aS&`B8~md|+LC(oZN=>}clf=!%cR!C!uSPx=uu zQ%NY83ly#9qt)q(7IkQ`UrLLfq?1*SNjkq({4K=avD^F|8#PmWY3h7guQ08JX$SO9 zkMqh{Y=mzeBPoX_*!?PGUr;%fK-Q)DlL_RE2-&@2K*0XmkM;g!TgB!&XY*z7%ZkHl z_)W3=T1F#C+m{_aKZ))8yEwdA(PshrvoIT*Vja6*l&)MEn;Zy7QT%u@Nho+qaz710DTjTD#2X>|7(fcO-jB)rY zZ4du{vTuxd-ro5uCm8yDWMv(*pM7fdnz0kuX5}jxi>)O0?iEif?2G+r?GtTf%y}Mt zlqCNipzxdG_-$6SUx@a9#0DQXn4cOCmh>l@(F&s`j?wK3`=+qJUy0i);&xvbx39VQ zlCM_*r=sUsf_plH9zsYXCvlMRo zI&SYd+l0A%`Swpt?CWoqb$7?XcL?Gz<0ONq%c7nffu{0hzQqtTY%LA1z!AI(EMi9um3 zS3;XUy48;HCWYVLj^BL64|&pE#*j`8FnCAtu{l2epy;v~T~;V*mb)~M$EPgr(ibXz zV*7mW$03sWG9hwcyk23_;Fx@=XkUT$dpqsVQ@Blb+>TRxU=60_wgQcP*^p?GC{$wg zofXyKhqOWb)tGn1hqrm6WNQ@h{PM*C{!O`uPebk_?HIYrjNHSRZxwy&(WiCrvlF5- z75_?|f7dB|_Humc$lRi#k4tM9-_~bTwnlc=n|~h#e6RV~k}s`v}M`l}FlrT#sWM|0H`h zsXQT;oC1EOxymGGJYUa=Vs|hSaWtcCr!&qkJG)3P|CZ=U&b*O2cpSyuL-?K*r&0!1 z(Sq&BD5b>RZHg8rIW77s@vxM5_?^O_$T4^_z<+1>utdr7kysNxQcC9&CnNq}VRZX1 zFp5l%oH+9Ri9F&+PDzneiywZ>a!;3^?ok*OIz~4Vhv_oY z=R29pc-(0N7JK==@klw%fHfCS^S;k(@&{r+A2V%ic{#_XQEWH%)~mJuHV6>hEIHWmHTe)~GQ4E>+SF7KD{NVpm^8N2eWZD*VJ74CaD?z<@3 z7D8uFTUW@woW{HbL*J(qhJNn9wPiTL$o|%IISY7}60b4#?)3jlydDuf9f*}r13Ldj z;ki33W%tqZm_?nb=(rpmCn-9%M#onIHceGD$TO{2(D_ZXEwA`ZDzZaIv zj;><=YXLjH$jK*?kB}ZKq>np}b1x>uZv{Rdb;yoYDNEN97wK5_^<5ruG2F(*lhLP& z{gbdfvi1|tf%Fek__u+7CAMXiKa(80V-#IipzEiBG{3{xYyPT6M^P$Fk6RE25OIoDL5}5}(MPX9_n@K4)T@*Wab9P>dkFpQ5*QOu!y2SOc{}0O}-@>?1iK~^w zRlz5EomPov60hE7ulH=54>;dW@p)J0b9vz2t#abQ*C|p;oS|si7ENmc_iV_yr?*@3 z!BEB4@y^y>Ir+qwJM}Ieq-ME9iG?C!VPYy4>H{|3M;v4^-OyAI!V17W6Li5Zy&5mZ2N%yc#P>D zu;Cf!yZ9qlV_#~fjj=IKgXCS|l$4e-wt06X3M`H9P&A0pU_wfRy|LM^dlZ{x&AZq< z4r?XvB{tv2Ih8HgiScQks(CM}WPh(06n%zTeb~=05MvYm2mANOuB@_J_zr|!R%{fq zi^lxuM6+{F+KqW8&pfBZ#cJYWGIsf~C&?31S_=!F6^qx|zJrV%OK(KZt&~wu&TCNO zp*`_110Q58Rq}|B2eG@JIoy7{2JjllYb@!U&QD=o2j{BzUNa%!!&^D*<=#?84zcXp z#!sSs5)u3-mye&MZBi4>%3RCf1ZMJ&5hVC=CN__e9Q0x}2J6Nu>%)YD_@AQDS|4`y{4e_~_`)YY&3A*@tTGC8nPJEdipI~)K;&zL| zq5u}VrdZ6&vC%V;I$~YGjzvoR?CavkJh^?j?%51>u{%qNg?yK{PgB@()`N|OcNASZ zqRWX2qdGGuKHlZ;In4k3SJB`|H2Al|FCTv69Y5)VZBXpwOb2V{1-vh9Y`KK3@8|C1 z-oJ_euW_vGAMs5zm8*=E8@ID6@=PT!*5c1`)}J}?rHUQHT{_Kj^blJl<_B3lj9DLu zgRd3VonU=+%0IE$*HMyRtK3M<@n|jOtsifcze}zKn6QY~VqQylE#EZJaV_9*)XSBQ-whcgK;SawoYF1^PBw(h2Adob~y z(LeL%p^0yM35UkDKeF$43s%!jCC819C$REIVrVx-pKj>0h?fiskDXRX$kN(>bdLr1z8%J0~?j#BKF zwW4BoU&nC}&v8z+>$BzTuw%@L^g7yeO5TyIul$6QHajtn^LHiABI@EAo2M2>QX7Ao zldq&K^!6U2#8h`;Y8_howz2$ux=quT6@n%B;NKS?e4X3J`S5QguT|N+cBc7R;Zy*p zHICB);=q?%J`J(HkT@9S;^5R@u=7mb_xJFAB&;RJUC6xFw76`}_HA;XXY;USwY6mg zD`<>4MTyyCiP;wL@cnbKRrc`L!^j@7agE1UAnx2{|i+vM_`lk=UI zM@tordZ5wKNkE}k;2HuN4 z%`WqbXb(2AvAD>_VizT5db)J3RJ7`2%a25>(-oid@p**vS=#k&6ip*EU1c?$Y&GqT zO<8_PIo4F=c9GpL^JGOE{E1uhS-FQVA4JzY{yoZUp`Emav7IgKf3k%gA)|N)g$o+< zRc@|T;_f)&?k*Sq5_59N&m``W8l^)(x62ild9d6m#j-yvWqib6@;tU&BN-_eXV$US zT+S`5u{%?CVx{?5d!mx`(D)mC6Ap4o86oXT$-|T}tn^RDt@`p)_Wf;19Hq7JWz|BW zr$@k#ixnM+-*^{=E%l~lJ5-62*c=hBv~eQOmvc6q65|muUY#m$`vvUWU9p8WmbK;S zoU+Wvj@09AvEflA4vr@dUf{FRp73!Xca%8jW%Eu)+5tz{agl>qk-wSw_k9&xXnR{* z?owhSCN_qrVq-+W-w6ua2(~LyY;RNaqwI_iadvvYd_6PM**P*t7s+Q5U-{@#wN01h z(OdAzis3SP(9+^M!_tv`q_1HlO8Pp!?R9iCG2r97+&8O!>A8*jw0Lsh^V4z|HQIkf zyxcO9@t`B(7Zg1r^caR7vRR*opC-v@q_V5 z>&N^6t2Y!4Xg9{ED)(i4+uk>RD!Y8>ZaEKP&Ck{5Aa+ca(m{AH4cPIc5+^4TCub{m zv~zYWfTzzd!d~*Tl*P)#+=j8K#%(~Ptn_{(-v42@Lo~(FY8@SiM z&@5fw_%cTJa{MmmJzp2es+)lXaxF8q#bO${$h{VBKf_mVf8Jd`PjCb!FDp-vsQq8#~hZ#^;TZ*ionMd?XUk4<6el^OeU@F)e!MN<&m%dp>+=VDGR2E5*Kx$|hh;A+_OmwD+CKp1 zKDH(A2p1{aYN-QuaCN}V0UI9(*!U`GXXRnmO*3xb^YBA?d%-C^#w8X={_12o9UHx& z_`n)->qB2fKgQbPCj<7~ujacYtWTFQz9rEsa8A$L%D#5OhyAbOh1aDb#bfM=!9;$e!@~ zR$;(1e3roqe9K}`$0&G5n@3jL@$h#^Q}VLE{JdCWnlD82l$bl2nEOmAQ`pBVo^SQr z#g-{s6?=K6)7twF>`3QlUng@WJzrDJUlmPDn1?HIn#zpaG$qd2Pc|Cl;=HI+_LyF^ zvt_!X5%UxA&x*eh{?2ppI+w_<_?KxmmC1eJXl?n^Uff4RXoc zMiVxHOpld%4d+@elov|6>@8u_E&5&dn&eS0iywqQ76ULa%XD~<^3}E{%_DVJ@4V$uE}2Qttllh;>1rm-)tH^kwfE73cDj5 zyBn1_X+oT|w{ar-5=gJcm@5_ieuaK56~-grJJK;8toT;yd~5RA_T{VRTmIVM>?raPwu8_$hr$xujRM#++cTGo3hZaDF^OVYegPhBYv!iqqn6%kEKlvyLD> z+-WL)C*^3;ek^jD?ydMf1iyzm-zO-%3*cRl;=R~0lC>e0!vjwB%GUiRIW_5*_Ar zNy^Q1yJdbn%Q6~)Dih*Ylz1zHQy<4k=Gos>V&!yVdEGqE!l;fp8h**lbZW zX027cUf~hLW9GJ4`>-r3$sU6^gBYsXoSpY8VBaQHW^GjbmB<%=PgQ*Bj~@e^FVXJV z>v2k8+Y7eIb7XCz2A(DRUh$i?Z1E<=?+CvS+vazkxlXZJMpVV-4)~sKxA`%OGJJ3C zd{5RkOp9+)?j?7Sd;Ll&NAl1+FIA3gR5##qjUBjsII^4=U;RcIn>=|8##t#-!zzN$*kfluJ$0+Rygx9w4>n`Ak;%Vq*?{ z3b|QsJEW&6wx{R~SF}AFZGWf4SWJw?sTdoOqq8qhS-TPSaAlaB7`8haWtCB-Xw=eb zv^jcOVO9#WL$_ll{rUxV=Z=RIJ$s^MFQ?~g0Y?25Mm-#(zXv`$2H&&TOW8h6@wWtj zTR4ATQ_7YS{GD$7mB=vW9|0StC>oxFhJRGpbcah1$L8a}y+f{Sxagj$+g>EjvKTI;-}MiLPceL^IX?0X!`A^jmY_p+IkY*dw>n7ckG^u?9$OTS zU18YGaeO@|zj%LR*8D~`zbuNDGm5mY!nB)XYC7xrd5NO+xoG`$4lADzi(qw#WhFUg zX?&cEOJBzL@f+#8oXD^ zE^sOIp!w^bBMVDWr%Ls*68(1|s zUJoewAP?@H9rxChDSKm6fsKgdn{Pvo#HLX&&w6hG^}@UGNRR(Du6y2t{YYlgx|FiH zEoT__wdk>n-qz0MM-+Y?9lt}Acx2sHe1PRQ$F@IqRbqv93iBgLtURyqVHI!G zDOFCqrRZ=zI(!I&ta33XChBZVBz5%>Id=QHj&)7({?_hfSJ$Qdo!<*i%1~)fOI~3; zMZBN=y9q{4Yfom1_OrXe`4f3&D|}nPwx#3yy<%^Ky|pQO|Dx#7!Rhdx!Y+ng$8E9o z*RmsX{Pt}O=I`Sv)}MvM`bTA_k?#ue)7KmFlh8Cb5n&Qz-zyqifCh6Ft!YOvCN5e> zW*v59#qmh&I7;Ex9A@>7+XdJscKEy@>)}P08!1c6Ic-ix02@yhtSvu-^s>qd=H}vk zo!>Q2C*=vZv6t^BN?9c{SMsdDf8Z=!{FN@dheRKmFBP5IJDtwU;qT*)dE9s(YlqBT zEsR@WOFEt0a`S3MpWU231$#8wnP`{j*TKdD^CG|K>nc-QnmrD4AsU~fXw(#anmLUw zQJ6D#7Vqtt-=O$$qw^y=H@pAY&hb1wr#wu&G(;KeVDVAfU0x%J5vgfwnKf_9{NDcN z8Gf6}_h&g-{@qCSUcUyLv-V+5_I*x9eB=qNc2TEj2=gei=k)FDI^Ba==Kb9K@^y;e z%m>Ajo!@sVe*ez;U21w`dsckcU~wHbi`{Rscl?+3)O%z4^Y>Ej`TXGHstJEz>;8TV z>!Huj;pP8+0ez*b6isWb!4tEUR}@L`0wM{H|&kS13M-Uh}sa(+2wje)ZWIl zjKejJZvVev-y@0!H@Mi_5&P3?58BvWIV7f9n>{6NSoxS2IURhs-9!Ck z#+hZ8<7e18y;4X#Ck9 zJ^Zt#7xIio5&y12i6;1SKOCRuX_zVGhyiAYxDR^+Cv%6b@VLSBb4%(v{<9K*N7l z;`yS!JYh6C+Bq5%jg2Nnhmjjk<2j@Aqm4XibaiwcPa55V5)VZWM~~QNu%6@ugQxAe z3xAE?vdvH~Pdn}hodW0BB_b2@r zpX9-L>@KjC{jEpyP^zDoY0q>S3x{3Z0+yA&JSVW5wp z?mTTEF;qzmRb%G^{Ot?G(7wb_e`2V2P7DpSzAQDT5G}ta7Tz|0;}cDzW=`8~s5&qj z5sgI0t9efBaeV#?EqX&g;V#rSME5FXRTIjo8dp|51*fbsgZYejPnVY#)2{!EqRaJ8 zm%Z`X_YtHlld@Ink#h4i&x4gwUnJ%62L9cf7)Yn-%VF9haT^;4W%S;w;t5I~-bfyP z#gzdhVK3hf8Hu{fv0ctAdsxvk&+6I5Jge}jf=#vKGgi@@)nCkO%AOF3=7;9^FZHST zUxvR;_+IJ!e>%D`@6BJd{c!Ukyd`G*<;Rbt&2}Co!habZ{zTE_I;Y84ILkeJ+eUN& z3*}f@+}d&8;%v$N8|F9SJ)Hi3RXA0^sM2xTA5Kyi`}_EH(afYq4RA_Y1;VN|ErW(= zwxaDOw0%|4Ew;KDvl`aEzabt;A5K`8!n&H@D=h0ld_M#0ZZ`g@il?K>XrB}lK4Rlk z+U!l>QDjH!tKy#=S9xD9->yv9|2O(VVa9lUyt`u-b;<4<{KjeXnZmRTi_0C;LtI&z zSkY*Qw{xpv2jigeB%gQDMan_1i?l@~28z+8&33x9a>}%b&V;Yu7hR%Q_~!GO68Ki} z`zh3Ic%o&tDE?N7KgK(#2d0}9Id=NCb1~Rb@b&r6$NlL6|F2Qv>k{H?qf(X^!nT!TE6;A;j(zF6Oze~L zr5NifZEi^FvZv{z_r>3IT_%4i`6~~9ceDO3pf2tiwfdJfCbcvgt#H5Eaqp&ZFMxTW z<9F8#KZvlK;I=<3A zx;DqwTz$tVOklDz%b5kNx1XXgy~;5ik;7CzE4nq%HqW%wkn z?Y{=|741^R=REwaHn;NqR++exAY>$ViVFGKG?DY2i2PW9V#;;e3kZ-rSL?_&8bjt|FHDIdL!!U-2>*KEY= zjpi7I@f9w%zs-pQpTCmw_2cNr!25ma`@Ny?D1k?1ipM|kInCYYp%|Z=yZnBX;xF37 zAe>3^VM1Y(cnAqLa%u?b`^j)bii_FC^yV#ryGl@HY$zA12#z3oS|H}Q2T4IIi-9L{sv=5p}1Uk;~q-WHqW8VRGD6-MJ7qwe^f z)t9Km&tm*8w0>vxB_h+H=zj(JpQ*%8Ozh;@_=(IVff#ANXLjDIGR>H`lYVEj=yyuq zB0l4>E^js2p!ET1sanQW@wJYxk7MB{!F;buOc*2n*iL(`($q7OqmJL%l zRlup%ae5=bd5WUXE$DM^j{ROADOaBi*xz4a&#Y3+lf9PxV}X1B75@uU>>JCsv8D@N zk5TSl&HWi)u=oEApXDCjX89?xd%4Ts2P?W=g>GAv{8J78oh|<*@e2X__f|A1M-%#d zHjn-le(5?=tdr6yW*^c#Ugz`sGDVwP(dM;4`co7>Rq!cte7;nCtis39&d0cGHp9yt z!_So%5Zm9wzAXN2d8SWxWSp-sDuYoI$7qeB(bZ`5bs+7TivJb(U+DbLXR~I>-$`6F zuH7%f|54U|$zQDfRy4Q`4SvLibe#IW)%oTEbCqdlnkoD)b^N|m7&SOX(aqaGU+CwH zlD52La%;`{}(&|TLt{@ znq#}~&&Sw4Vq3g?Say-3LkT)mIUUYYSQatPO^<`}t2}O|ux#yE4p)5H#re`H;LBje zmk3{Wayl&GWWz@kmUqLlx2nU1Hdp;t;ZO{RO2^?IMdxeL`3e|iL*_})n$y|Uj zli-jQv(knbh>d=(NAkI}?WGR*k1b^z^U$6y7fCGkP`I~p+;>#{zLK3>-j8<5;qL2v zDG?^B+?U3~E%ymV*3;gnuq%RHg=6<;MVou@aavAn_;%YS8yh#9-LTQOFL2qmZG_81 zj9Xp6yr!J5w8`}4UrVC<6&+{;Gb`Ju!z+rGzd_5f3e%R3>A`Tx%F{9@*M$`|FDX0< z;Zg2*%;X+L_~6TP@mchY%(8fxWxFgs0Ke0J(^%(TNS!M)x~vus#M7q=^FP3Rbq@2y z4nsMetoiu^9Mf^?<3i$iF}8~y{_@X}i~ol+BiS-;7x&}*a5-D_GllbYa6SaiS^m|- zxrJl>7iWjqm%eJya%Dh=ex$wtev&8s<>y!>Vm7)nxi*=nqJN@SR^BZ@uQI2XjQ6c| zetXS)JGam*k9V}14PmGJh4J$E^Rj|xv-=S*!+u3hp7Xv*Npx7`w=vujqP_)Ac*W*L-{}P5F8{zNVS@x~~9Vhgx4{B^;hA`qZM&i2*-XE7~_h z_vTLf5(-T5(DSj*^6h$Jr)aNi&GPW0#QIT3&$5j<+cEQ+$xq2=-H3tiykt+t_KH@2 zM61hkIQV)r9}Yt-hXqhztz3?7-exJ+^Uh21lK(&c%U*Rbx=qpRLZ{cc3a2_4HFcaW z!^f<)YggxACxzp0;dry+UmpJLkn*pM!aIg{@iyMZ3|Cmb0?XzKho*42(fQ!n_;ymV zL*!lN$pGJH6owZ#h7T(~QhLUNt&g*s?cM#Aph9PV;& zDW%W1+;29+6`z~q^B+lD?(Z)@79%CIjN{!!jlNl7PQR8uSJE##P+|T%nC}7Ctny